summaryrefslogtreecommitdiff
path: root/spec/ruby/core/numeric/shared/abs.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/numeric/shared/abs.rb')
0 files changed, 0 insertions, 0 deletions
de'>-rw-r--r--.gitattributes1
-rw-r--r--.gitignore55
-rw-r--r--.travis.yml362
-rw-r--r--COPYING10
-rw-r--r--LEGAL801
-rw-r--r--Makefile.in306
-rw-r--r--NEWS766
-rw-r--r--README.ja.md20
-rw-r--r--README.md53
-rw-r--r--acinclude.m446
-rw-r--r--aclocal.m437
-rw-r--r--addr2line.c1853
-rw-r--r--addr2line.h2
-rw-r--r--appveyor.yml166
-rw-r--r--array.c1654
-rw-r--r--ast.c761
-rw-r--r--benchmark/README.md72
-rw-r--r--benchmark/app_answer.rb (renamed from benchmark/bm_app_answer.rb)0
-rw-r--r--benchmark/app_aobench.rb (renamed from benchmark/bm_app_aobench.rb)0
-rw-r--r--benchmark/app_erb.yml23
-rw-r--r--benchmark/app_factorial.rb (renamed from benchmark/bm_app_factorial.rb)0
-rw-r--r--benchmark/app_fib.rb (renamed from benchmark/bm_app_fib.rb)0
-rw-r--r--benchmark/app_lc_fizzbuzz.rb (renamed from benchmark/bm_app_lc_fizzbuzz.rb)0
-rw-r--r--benchmark/app_mandelbrot.rb (renamed from benchmark/bm_app_mandelbrot.rb)0
-rw-r--r--benchmark/app_pentomino.rb130
-rw-r--r--benchmark/app_raise.rb (renamed from benchmark/bm_app_raise.rb)0
-rw-r--r--benchmark/app_strconcat.rb (renamed from benchmark/bm_app_strconcat.rb)0
-rw-r--r--benchmark/app_tak.rb (renamed from benchmark/bm_app_tak.rb)0
-rw-r--r--benchmark/app_tarai.rb (renamed from benchmark/bm_app_tarai.rb)0
-rw-r--r--benchmark/app_uri.rb (renamed from benchmark/bm_app_uri.rb)0
-rw-r--r--benchmark/array_sample_100k_10.rb2
-rw-r--r--benchmark/array_sample_100k_11.rb2
-rw-r--r--benchmark/array_sample_100k__100.rb2
-rw-r--r--benchmark/array_sample_100k__1k.rb2
-rw-r--r--benchmark/array_sample_100k__6k.rb2
-rw-r--r--benchmark/array_sample_100k___10k.rb2
-rw-r--r--benchmark/array_sample_100k___50k.rb2
-rw-r--r--benchmark/array_shift.rb (renamed from benchmark/bm_array_shift.rb)0
-rw-r--r--benchmark/array_small_and.rb17
-rw-r--r--benchmark/array_small_diff.rb17
-rw-r--r--benchmark/array_small_or.rb17
-rw-r--r--benchmark/array_sort_block.rb2
-rw-r--r--benchmark/array_sort_float.rb2
-rw-r--r--benchmark/array_values_at_int.rb2
-rw-r--r--benchmark/array_values_at_range.rb2
-rw-r--r--benchmark/bighash.rb (renamed from benchmark/bm_bighash.rb)0
-rw-r--r--benchmark/bm_app_erb.rb26
-rw-r--r--benchmark/bm_app_pentomino.rb259
-rw-r--r--benchmark/bm_require.rb7
-rw-r--r--benchmark/bm_require_thread.rb15
-rw-r--r--benchmark/bm_so_count_words.rb19
-rw-r--r--benchmark/bm_so_fasta.rb81
-rw-r--r--benchmark/bm_so_k_nucleotide.rb48
-rw-r--r--benchmark/bm_so_meteor_contest.rb563
-rw-r--r--benchmark/bm_so_pidigits.rb92
-rw-r--r--benchmark/bm_so_reverse_complement.rb30
-rw-r--r--benchmark/bm_vm1_attr_ivar.rb14
-rw-r--r--benchmark/bm_vm1_attr_ivar_set.rb14
-rw-r--r--benchmark/bm_vm1_block.rb10
-rw-r--r--benchmark/bm_vm1_const.rb8
-rw-r--r--benchmark/bm_vm1_ensure.rb11
-rw-r--r--benchmark/bm_vm1_float_simple.rb7
-rw-r--r--benchmark/bm_vm1_gc_short_lived.rb10
-rw-r--r--benchmark/bm_vm1_gc_short_with_complex_long.rb27
-rw-r--r--benchmark/bm_vm1_gc_short_with_long.rb13
-rw-r--r--benchmark/bm_vm1_gc_short_with_symbol.rb15
-rw-r--r--benchmark/bm_vm1_gc_wb_ary.rb12
-rw-r--r--benchmark/bm_vm1_gc_wb_ary_promoted.rb14
-rw-r--r--benchmark/bm_vm1_gc_wb_obj.rb15
-rw-r--r--benchmark/bm_vm1_gc_wb_obj_promoted.rb17
-rw-r--r--benchmark/bm_vm1_ivar.rb8
-rw-r--r--benchmark/bm_vm1_ivar_set.rb6
-rw-r--r--benchmark/bm_vm1_length.rb9
-rw-r--r--benchmark/bm_vm1_lvar_init.rb18
-rw-r--r--benchmark/bm_vm1_lvar_set.rb5
-rw-r--r--benchmark/bm_vm1_neq.rb8
-rw-r--r--benchmark/bm_vm1_not.rb7
-rw-r--r--benchmark/bm_vm1_rescue.rb7
-rw-r--r--benchmark/bm_vm1_simplereturn.rb9
-rw-r--r--benchmark/bm_vm1_swap.rb8
-rw-r--r--benchmark/bm_vm1_yield.rb10
-rw-r--r--benchmark/bm_vm2_array.rb5
-rw-r--r--benchmark/bm_vm2_bigarray.rb106
-rw-r--r--benchmark/bm_vm2_bighash.rb5
-rw-r--r--benchmark/bm_vm2_case.rb14
-rw-r--r--benchmark/bm_vm2_case_lit.rb19
-rw-r--r--benchmark/bm_vm2_defined_method.rb9
-rw-r--r--benchmark/bm_vm2_dstr.rb6
-rw-r--r--benchmark/bm_vm2_eval.rb6
-rw-r--r--benchmark/bm_vm2_method.rb9
-rw-r--r--benchmark/bm_vm2_method_missing.rb12
-rw-r--r--benchmark/bm_vm2_method_with_block.rb9
-rw-r--r--benchmark/bm_vm2_mutex.rb9
-rw-r--r--benchmark/bm_vm2_newlambda.rb5
-rw-r--r--benchmark/bm_vm2_poly_method.rb20
-rw-r--r--benchmark/bm_vm2_poly_method_ov.rb20
-rw-r--r--benchmark/bm_vm2_poly_singleton.rb14
-rw-r--r--benchmark/bm_vm2_proc.rb14
-rw-r--r--benchmark/bm_vm2_raise1.rb18
-rw-r--r--benchmark/bm_vm2_raise2.rb18
-rw-r--r--benchmark/bm_vm2_regexp.rb6
-rw-r--r--benchmark/bm_vm2_send.rb12
-rw-r--r--benchmark/bm_vm2_string_literal.rb5
-rw-r--r--benchmark/bm_vm2_struct_big_aref_hi.rb7
-rw-r--r--benchmark/bm_vm2_struct_big_aref_lo.rb7
-rw-r--r--benchmark/bm_vm2_struct_big_aset.rb7
-rw-r--r--benchmark/bm_vm2_struct_big_href_hi.rb7
-rw-r--r--benchmark/bm_vm2_struct_big_href_lo.rb7
-rw-r--r--benchmark/bm_vm2_struct_big_hset.rb7
-rw-r--r--benchmark/bm_vm2_struct_small_aref.rb7
-rw-r--r--benchmark/bm_vm2_struct_small_aset.rb7
-rw-r--r--benchmark/bm_vm2_struct_small_href.rb7
-rw-r--r--benchmark/bm_vm2_struct_small_hset.rb7
-rw-r--r--benchmark/bm_vm2_super.rb20
-rw-r--r--benchmark/bm_vm2_unif1.rb8
-rw-r--r--benchmark/bm_vm2_zsuper.rb20
-rw-r--r--benchmark/bm_vm_thread_pass_flood.rb8
-rw-r--r--benchmark/dir_empty_p.rb5
-rw-r--r--benchmark/driver.rb427
-rw-r--r--benchmark/enum_lazy_grep_v_100.rb4
-rw-r--r--benchmark/enum_lazy_grep_v_20.rb4
-rw-r--r--benchmark/enum_lazy_grep_v_50.rb4
-rw-r--r--benchmark/enum_lazy_uniq_100.rb4
-rw-r--r--benchmark/enum_lazy_uniq_20.rb4
-rw-r--r--benchmark/enum_lazy_uniq_50.rb4
-rw-r--r--benchmark/erb_render.yml24
-rwxr-xr-xbenchmark/fiber_chain.rb40
-rw-r--r--benchmark/file_chmod.rb9
-rw-r--r--benchmark/file_rename.rb11
-rw-r--r--benchmark/gc/aobench.rb2
-rw-r--r--benchmark/gc/binary_trees.rb2
-rw-r--r--benchmark/gc/gcbench.rb3
-rw-r--r--benchmark/gc/pentomino.rb2
-rw-r--r--benchmark/hash_aref_dsym.rb (renamed from benchmark/bm_hash_aref_dsym.rb)0
-rw-r--r--benchmark/hash_aref_dsym_long.rb (renamed from benchmark/bm_hash_aref_dsym_long.rb)0
-rw-r--r--benchmark/hash_aref_fix.rb (renamed from benchmark/bm_hash_aref_fix.rb)0
-rw-r--r--benchmark/hash_aref_flo.rb (renamed from benchmark/bm_hash_aref_flo.rb)0
-rw-r--r--benchmark/hash_aref_miss.rb (renamed from benchmark/bm_hash_aref_miss.rb)0
-rw-r--r--benchmark/hash_aref_str.rb (renamed from benchmark/bm_hash_aref_str.rb)0
-rw-r--r--benchmark/hash_aref_sym.rb (renamed from benchmark/bm_hash_aref_sym.rb)0
-rw-r--r--benchmark/hash_aref_sym_long.rb (renamed from benchmark/bm_hash_aref_sym_long.rb)0
-rw-r--r--benchmark/hash_flatten.rb (renamed from benchmark/bm_hash_flatten.rb)0
-rw-r--r--benchmark/hash_ident_flo.rb (renamed from benchmark/bm_hash_ident_flo.rb)0
-rw-r--r--benchmark/hash_ident_num.rb (renamed from benchmark/bm_hash_ident_num.rb)0
-rw-r--r--benchmark/hash_ident_obj.rb (renamed from benchmark/bm_hash_ident_obj.rb)0
-rw-r--r--benchmark/hash_ident_str.rb (renamed from benchmark/bm_hash_ident_str.rb)0
-rw-r--r--benchmark/hash_ident_sym.rb (renamed from benchmark/bm_hash_ident_sym.rb)0
-rw-r--r--benchmark/hash_keys.rb (renamed from benchmark/bm_hash_keys.rb)0
-rw-r--r--benchmark/hash_literal_small2.rb3
-rw-r--r--benchmark/hash_literal_small4.rb3
-rw-r--r--benchmark/hash_literal_small8.rb3
-rw-r--r--benchmark/hash_long.rb (renamed from benchmark/bm_hash_long.rb)0
-rw-r--r--benchmark/hash_shift.rb (renamed from benchmark/bm_hash_shift.rb)0
-rw-r--r--benchmark/hash_shift_u16.rb (renamed from benchmark/bm_hash_shift_u16.rb)0
-rw-r--r--benchmark/hash_shift_u24.rb (renamed from benchmark/bm_hash_shift_u24.rb)0
-rw-r--r--benchmark/hash_shift_u32.rb (renamed from benchmark/bm_hash_shift_u32.rb)0
-rw-r--r--benchmark/hash_small2.rb (renamed from benchmark/bm_hash_small2.rb)0
-rw-r--r--benchmark/hash_small4.rb (renamed from benchmark/bm_hash_small4.rb)0
-rw-r--r--benchmark/hash_small8.rb (renamed from benchmark/bm_hash_small8.rb)0
-rw-r--r--benchmark/hash_to_proc.rb (renamed from benchmark/bm_hash_to_proc.rb)0
-rw-r--r--benchmark/hash_values.rb (renamed from benchmark/bm_hash_values.rb)0
-rw-r--r--benchmark/int_quo.rb1
-rw-r--r--benchmark/io_copy_stream_write.rb24
-rw-r--r--benchmark/io_copy_stream_write_socket.rb35
-rw-r--r--benchmark/io_file_create.rb (renamed from benchmark/bm_io_file_create.rb)0
-rw-r--r--benchmark/io_file_read.rb (renamed from benchmark/bm_io_file_read.rb)0
-rw-r--r--benchmark/io_file_write.rb (renamed from benchmark/bm_io_file_write.rb)0
-rw-r--r--benchmark/io_nonblock_noex.rb (renamed from benchmark/bm_io_nonblock_noex.rb)0
-rw-r--r--benchmark/io_nonblock_noex2.rb (renamed from benchmark/bm_io_nonblock_noex2.rb)0
-rw-r--r--benchmark/io_pipe_rw.rb13
-rw-r--r--benchmark/io_select.rb (renamed from benchmark/bm_io_select.rb)0
-rw-r--r--benchmark/io_select2.rb (renamed from benchmark/bm_io_select2.rb)0
-rw-r--r--benchmark/io_select3.rb (renamed from benchmark/bm_io_select3.rb)0
-rw-r--r--benchmark/lib/benchmark_driver/output/driver.rb36
-rw-r--r--benchmark/lib/benchmark_driver/runner/cstime.rb22
-rw-r--r--benchmark/lib/benchmark_driver/runner/cutime.rb22
-rw-r--r--benchmark/lib/benchmark_driver/runner/peak.rb151
-rw-r--r--benchmark/lib/benchmark_driver/runner/size.rb25
-rw-r--r--benchmark/lib/benchmark_driver/runner/stime.rb22
-rw-r--r--benchmark/lib/benchmark_driver/runner/total.rb137
-rw-r--r--benchmark/lib/benchmark_driver/runner/utime.rb22
-rwxr-xr-xbenchmark/lib/load.rb2
-rw-r--r--benchmark/loop_for.rb (renamed from benchmark/bm_loop_for.rb)0
-rw-r--r--benchmark/loop_generator.rb (renamed from benchmark/bm_loop_generator.rb)0
-rw-r--r--benchmark/loop_times.rb (renamed from benchmark/bm_loop_times.rb)0
-rw-r--r--benchmark/loop_whileloop.rb (renamed from benchmark/bm_loop_whileloop.rb)0
-rw-r--r--benchmark/loop_whileloop2.rb (renamed from benchmark/bm_loop_whileloop2.rb)0
-rw-r--r--benchmark/make_fasta_output.rb19
-rw-r--r--benchmark/marshal_dump_flo.rb (renamed from benchmark/bm_marshal_dump_flo.rb)0
-rw-r--r--benchmark/marshal_dump_load_geniv.rb (renamed from benchmark/bm_marshal_dump_load_geniv.rb)0
-rw-r--r--benchmark/marshal_dump_load_time.rb (renamed from benchmark/bm_marshal_dump_load_time.rb)0
-rw-r--r--benchmark/memory_wrapper.rb16
-rw-r--r--benchmark/prepare_require.rb25
-rw-r--r--benchmark/prepare_require_thread.rb2
-rw-r--r--benchmark/prepare_so_count_words.rb15
-rw-r--r--benchmark/prepare_so_k_nucleotide.rb2
-rw-r--r--benchmark/prepare_so_reverse_complement.rb2
-rw-r--r--benchmark/report.rb79
-rw-r--r--benchmark/require.yml36
-rw-r--r--benchmark/require_thread.yml44
-rw-r--r--benchmark/run.rb127
-rw-r--r--benchmark/runc.rb27
-rw-r--r--benchmark/securerandom.rb (renamed from benchmark/bm_securerandom.rb)0
-rw-r--r--benchmark/so_ackermann.rb (renamed from benchmark/bm_so_ackermann.rb)0
-rw-r--r--benchmark/so_array.rb (renamed from benchmark/bm_so_array.rb)0
-rw-r--r--benchmark/so_binary_trees.rb (renamed from benchmark/bm_so_binary_trees.rb)0
-rw-r--r--benchmark/so_concatenate.rb (renamed from benchmark/bm_so_concatenate.rb)0
-rw-r--r--benchmark/so_count_words.yml66
-rw-r--r--benchmark/so_exception.rb (renamed from benchmark/bm_so_exception.rb)0
-rw-r--r--benchmark/so_fannkuch.rb (renamed from benchmark/bm_so_fannkuch.rb)0
-rw-r--r--benchmark/so_fasta.rb81
-rw-r--r--benchmark/so_k_nucleotide.yml155
-rw-r--r--benchmark/so_lists.rb (renamed from benchmark/bm_so_lists.rb)0
-rw-r--r--benchmark/so_mandelbrot.rb (renamed from benchmark/bm_so_mandelbrot.rb)0
-rw-r--r--benchmark/so_matrix.rb (renamed from benchmark/bm_so_matrix.rb)0
-rw-r--r--benchmark/so_meteor_contest.rb563
-rw-r--r--benchmark/so_nbody.rb (renamed from benchmark/bm_so_nbody.rb)0
-rw-r--r--benchmark/so_nested_loop.rb (renamed from benchmark/bm_so_nested_loop.rb)0
-rw-r--r--benchmark/so_nsieve.rb (renamed from benchmark/bm_so_nsieve.rb)0
-rw-r--r--benchmark/so_nsieve_bits.rb (renamed from benchmark/bm_so_nsieve_bits.rb)0
-rw-r--r--benchmark/so_object.rb (renamed from benchmark/bm_so_object.rb)0
-rw-r--r--benchmark/so_partial_sums.rb (renamed from benchmark/bm_so_partial_sums.rb)0
-rw-r--r--benchmark/so_pidigits.rb92
-rw-r--r--benchmark/so_random.rb (renamed from benchmark/bm_so_random.rb)0
-rw-r--r--benchmark/so_reverse_complement.yml137
-rw-r--r--benchmark/so_sieve.rb (renamed from benchmark/bm_so_sieve.rb)0
-rw-r--r--benchmark/so_spectralnorm.rb (renamed from benchmark/bm_so_spectralnorm.rb)0
-rw-r--r--benchmark/string_index.rb3
-rw-r--r--benchmark/string_scan_re.rb2
-rw-r--r--benchmark/string_scan_str.rb2
-rw-r--r--benchmark/time_subsec.rb2
-rw-r--r--benchmark/vm1_attr_ivar.yml14
-rw-r--r--benchmark/vm1_attr_ivar_set.yml14
-rw-r--r--benchmark/vm1_block.yml9
-rw-r--r--benchmark/vm1_blockparam.yml7
-rw-r--r--benchmark/vm1_blockparam_call.yml8
-rw-r--r--benchmark/vm1_blockparam_pass.yml12
-rw-r--r--benchmark/vm1_blockparam_yield.yml8
-rw-r--r--benchmark/vm1_const.yml7
-rw-r--r--benchmark/vm1_ensure.yml14
-rw-r--r--benchmark/vm1_float_simple.yml8
-rw-r--r--benchmark/vm1_gc_short_lived.yml9
-rw-r--r--benchmark/vm1_gc_short_with_complex_long.yml25
-rw-r--r--benchmark/vm1_gc_short_with_long.yml13
-rw-r--r--benchmark/vm1_gc_short_with_symbol.yml13
-rw-r--r--benchmark/vm1_gc_wb_ary.yml12
-rw-r--r--benchmark/vm1_gc_wb_ary_promoted.yml15
-rw-r--r--benchmark/vm1_gc_wb_obj.yml15
-rw-r--r--benchmark/vm1_gc_wb_obj_promoted.yml17
-rw-r--r--benchmark/vm1_ivar.yml6
-rw-r--r--benchmark/vm1_ivar_set.yml5
-rw-r--r--benchmark/vm1_length.yml8
-rw-r--r--benchmark/vm1_lvar_init.yml21
-rw-r--r--benchmark/vm1_lvar_set.yml4
-rw-r--r--benchmark/vm1_neq.yml7
-rw-r--r--benchmark/vm1_not.yml6
-rw-r--r--benchmark/vm1_rescue.yml6
-rw-r--r--benchmark/vm1_simplereturn.yml7
-rw-r--r--benchmark/vm1_swap.yml7
-rw-r--r--benchmark/vm1_yield.yml13
-rw-r--r--benchmark/vm2_array.yml4
-rw-r--r--benchmark/vm2_bigarray.yml105
-rw-r--r--benchmark/vm2_bighash.yml4
-rw-r--r--benchmark/vm2_case.yml13
-rw-r--r--benchmark/vm2_case_lit.yml23
-rw-r--r--benchmark/vm2_defined_method.yml8
-rw-r--r--benchmark/vm2_dstr.yml6
-rw-r--r--benchmark/vm2_eval.yml4
-rw-r--r--benchmark/vm2_fiber_switch.yml9
-rw-r--r--benchmark/vm2_freezestring.yml10
-rw-r--r--benchmark/vm2_method.yml8
-rw-r--r--benchmark/vm2_method_missing.yml11
-rw-r--r--benchmark/vm2_method_with_block.yml8
-rw-r--r--benchmark/vm2_module_ann_const_set.yml4
-rw-r--r--benchmark/vm2_module_const_set.yml8
-rw-r--r--benchmark/vm2_mutex.yml8
-rw-r--r--benchmark/vm2_newlambda.yml4
-rw-r--r--benchmark/vm2_poly_method.yml24
-rw-r--r--benchmark/vm2_poly_method_ov.yml24
-rw-r--r--benchmark/vm2_poly_singleton.yml18
-rw-r--r--benchmark/vm2_proc.yml12
-rw-r--r--benchmark/vm2_raise1.yml16
-rw-r--r--benchmark/vm2_raise2.yml16
-rw-r--r--benchmark/vm2_regexp.yml6
-rw-r--r--benchmark/vm2_send.yml11
-rw-r--r--benchmark/vm2_string_literal.yml4
-rw-r--r--benchmark/vm2_struct_big_aref_hi.yml7
-rw-r--r--benchmark/vm2_struct_big_aref_lo.yml7
-rw-r--r--benchmark/vm2_struct_big_aset.yml11
-rw-r--r--benchmark/vm2_struct_big_href_hi.yml7
-rw-r--r--benchmark/vm2_struct_big_href_lo.yml7
-rw-r--r--benchmark/vm2_struct_big_hset.yml11
-rw-r--r--benchmark/vm2_struct_small_aref.yml7
-rw-r--r--benchmark/vm2_struct_small_aset.yml11
-rw-r--r--benchmark/vm2_struct_small_href.yml7
-rw-r--r--benchmark/vm2_struct_small_hset.yml7
-rw-r--r--benchmark/vm2_super.yml17
-rw-r--r--benchmark/vm2_unif1.yml7
-rw-r--r--benchmark/vm2_zsuper.yml18
-rw-r--r--benchmark/vm3_backtrace.rb (renamed from benchmark/bm_vm3_backtrace.rb)0
-rw-r--r--benchmark/vm3_clearmethodcache.rb (renamed from benchmark/bm_vm3_clearmethodcache.rb)0
-rw-r--r--benchmark/vm3_gc.rb (renamed from benchmark/bm_vm3_gc.rb)0
-rw-r--r--benchmark/vm3_gc_old_full.rb (renamed from benchmark/bm_vm3_gc_old_full.rb)0
-rw-r--r--benchmark/vm3_gc_old_immediate.rb (renamed from benchmark/bm_vm3_gc_old_immediate.rb)0
-rw-r--r--benchmark/vm3_gc_old_lazy.rb (renamed from benchmark/bm_vm3_gc_old_lazy.rb)0
-rw-r--r--benchmark/vm_symbol_block_pass.rb (renamed from benchmark/bm_vm_symbol_block_pass.rb)0
-rw-r--r--benchmark/vm_thread_alive_check1.rb (renamed from benchmark/bm_vm_thread_alive_check1.rb)0
-rw-r--r--benchmark/vm_thread_close.rb (renamed from benchmark/bm_vm_thread_close.rb)0
-rw-r--r--benchmark/vm_thread_condvar1.rb28
-rw-r--r--benchmark/vm_thread_condvar2.rb35
-rw-r--r--benchmark/vm_thread_create_join.rb (renamed from benchmark/bm_vm_thread_create_join.rb)0
-rw-r--r--benchmark/vm_thread_mutex1.rb (renamed from benchmark/bm_vm_thread_mutex1.rb)0
-rw-r--r--benchmark/vm_thread_mutex2.rb (renamed from benchmark/bm_vm_thread_mutex2.rb)0
-rw-r--r--benchmark/vm_thread_mutex3.rb (renamed from benchmark/bm_vm_thread_mutex3.rb)0
-rw-r--r--benchmark/vm_thread_pass.rb (renamed from benchmark/bm_vm_thread_pass.rb)0
-rw-r--r--benchmark/vm_thread_pass_flood.rb10
-rw-r--r--benchmark/vm_thread_pipe.rb (renamed from benchmark/bm_vm_thread_pipe.rb)0
-rw-r--r--benchmark/vm_thread_queue.rb (renamed from benchmark/bm_vm_thread_queue.rb)0
-rw-r--r--benchmark/vm_thread_sized_queue.rb20
-rw-r--r--benchmark/vm_thread_sized_queue2.rb23
-rw-r--r--benchmark/vm_thread_sized_queue3.rb22
-rw-r--r--benchmark/vm_thread_sized_queue4.rb26
-rw-r--r--benchmark/wc.input.base25
-rw-r--r--bignum.c571
-rwxr-xr-xbin/bundle27
-rwxr-xr-xbin/bundler27
-rwxr-xr-xbin/erb15
-rwxr-xr-xbin/irb28
-rwxr-xr-xbin/rdoc53
-rwxr-xr-xbin/ri31
-rwxr-xr-xbootstraptest/runner.rb47
-rw-r--r--bootstraptest/test_env.rb12
-rw-r--r--bootstraptest/test_exception.rb2
-rw-r--r--bootstraptest/test_flow.rb10
-rw-r--r--bootstraptest/test_fork.rb4
-rw-r--r--bootstraptest/test_insns.rb420
-rw-r--r--bootstraptest/test_io.rb4
-rw-r--r--bootstraptest/test_literal.rb24
-rw-r--r--bootstraptest/test_proc.rb4
-rw-r--r--ccan/list/list.h25
-rw-r--r--class.c152
-rw-r--r--common.mk940
-rw-r--r--compile.c5532
-rw-r--r--complex.c928
-rw-r--r--configure.ac4078
-rw-r--r--configure.in4710
-rw-r--r--constant.h1
-rw-r--r--cont.c1279
-rw-r--r--coroutine/amd64/Context.S46
-rw-r--r--coroutine/amd64/Context.h59
-rw-r--r--coroutine/arm32/Context.S14
-rw-r--r--coroutine/arm32/Context.h56
-rw-r--r--coroutine/arm64/Context.S59
-rw-r--r--coroutine/arm64/Context.h57
-rw-r--r--coroutine/ppc64le/Context.S72
-rw-r--r--coroutine/ppc64le/Context.h54
-rw-r--r--coroutine/win32/Context.asm55
-rw-r--r--coroutine/win32/Context.h62
-rw-r--r--coroutine/win64/Context.S77
-rw-r--r--coroutine/win64/Context.asm79
-rw-r--r--coroutine/win64/Context.h72
-rw-r--r--coroutine/x86/Context.S43
-rw-r--r--coroutine/x86/Context.h59
-rw-r--r--cygwin/GNUmakefile.in2
-rw-r--r--debug.c109
-rw-r--r--debug_counter.c55
-rw-r--r--debug_counter.h283
-rw-r--r--defs/gmake.mk134
-rw-r--r--defs/id.def11
-rw-r--r--defs/keywords4
-rw-r--r--defs/known_errors.def251
-rw-r--r--defs/lex.c.src4
-rw-r--r--defs/universal.mk5
-rw-r--r--dir.c1079
-rw-r--r--dln.c32
-rw-r--r--dmydln.c2
-rw-r--r--doc/.document1
-rw-r--r--doc/ChangeLog-0.60_to_1.14
-rw-r--r--doc/ChangeLog-1.9.36
-rw-r--r--doc/ChangeLog-2.4.02
-rw-r--r--doc/ChangeLog-20165
-rw-r--r--doc/ChangeLog-20176
-rw-r--r--doc/ChangeLog-YARV2
-rw-r--r--doc/NEWS-1.8.74
-rw-r--r--doc/NEWS-1.9.22
-rw-r--r--doc/NEWS-2.0.05
-rw-r--r--doc/NEWS-2.4.0397
-rw-r--r--doc/NEWS-2.5.0567
-rw-r--r--doc/contributing.rdoc35
-rw-r--r--doc/contributors.rdoc19
-rw-r--r--doc/extension.ja.rdoc193
-rw-r--r--doc/extension.rdoc296
-rw-r--r--doc/globals.rdoc2
-rw-r--r--doc/irb/irb.rd.ja5
-rw-r--r--doc/maintainers.rdoc212
-rw-r--r--doc/regexp.rdoc22
-rw-r--r--doc/signals.rdoc106
-rw-r--r--doc/standard_library.rdoc93
-rw-r--r--doc/syntax/calling_methods.rdoc27
-rw-r--r--doc/syntax/literals.rdoc30
-rw-r--r--doc/syntax/methods.rdoc43
-rw-r--r--doc/syntax/refinements.rdoc63
-rw-r--r--doc/yarvarch.en (renamed from template/yarvarch.en)0
-rw-r--r--doc/yarvarch.ja (renamed from template/yarvarch.ja)0
-rw-r--r--enc/ascii.c9
-rw-r--r--enc/big5.c6
-rw-r--r--enc/cp949.c2
-rw-r--r--enc/depend74
-rw-r--r--enc/emacs_mule.c4
-rw-r--r--enc/euc_jp.c4
-rw-r--r--enc/euc_kr.c27
-rw-r--r--enc/euc_tw.c2
-rw-r--r--enc/gb18030.c3
-rw-r--r--enc/gbk.c2
-rw-r--r--enc/iso_8859_1.c33
-rw-r--r--enc/iso_8859_10.c41
-rw-r--r--enc/iso_8859_11.c2
-rw-r--r--enc/iso_8859_13.c41
-rw-r--r--enc/iso_8859_14.c50
-rw-r--r--enc/iso_8859_15.c47
-rw-r--r--enc/iso_8859_16.c46
-rw-r--r--enc/iso_8859_2.c34
-rw-r--r--enc/iso_8859_3.c43
-rw-r--r--enc/iso_8859_4.c29
-rw-r--r--enc/iso_8859_5.c22
-rw-r--r--enc/iso_8859_6.c2
-rw-r--r--enc/iso_8859_7.c48
-rw-r--r--enc/iso_8859_8.c2
-rw-r--r--enc/iso_8859_9.c47
-rw-r--r--enc/jis/props.h227
-rw-r--r--enc/jis/props.h.blt56
-rw-r--r--enc/jis/props.kwd6
-rw-r--r--enc/jis/props.src6
-rw-r--r--enc/koi8_r.c3
-rw-r--r--enc/koi8_u.c2
-rwxr-xr-xenc/make_encmake.rb12
-rw-r--r--enc/mktable.c42
-rw-r--r--enc/prelude.rb4
-rw-r--r--enc/shift_jis.c527
-rw-r--r--enc/shift_jis.h546
-rw-r--r--enc/trans/GB/GB12345%UCS.src61
-rw-r--r--enc/trans/GB/GB2312%UCS.src75
-rw-r--r--enc/trans/GB/UCS%GB12345.src61
-rw-r--r--enc/trans/GB/UCS%GB2312.src75
-rw-r--r--enc/unicode.c268
-rw-r--r--enc/unicode/12.1.0/casefold.h7428
-rw-r--r--enc/unicode/12.1.0/name2ctype.h41810
-rw-r--r--enc/unicode/9.0.0/casefold.h7068
-rw-r--r--enc/unicode/9.0.0/name2ctype.h35389
-rwxr-xr-xenc/unicode/case-folding.rb34
-rw-r--r--enc/us_ascii.c9
-rw-r--r--enc/utf_16be.c2
-rw-r--r--enc/utf_16le.c2
-rw-r--r--enc/utf_32be.c21
-rw-r--r--enc/utf_32le.c20
-rw-r--r--enc/utf_8.c16
-rw-r--r--enc/windows_1250.c35
-rw-r--r--enc/windows_1251.c35
-rw-r--r--enc/windows_1252.c29
-rw-r--r--enc/windows_1253.c43
-rw-r--r--enc/windows_1254.c47
-rw-r--r--enc/windows_1257.c50
-rw-r--r--enc/windows_31j.c8
-rw-r--r--encindex.h2
-rw-r--r--encoding.c56
-rw-r--r--enum.c606
-rw-r--r--enumerator.c1232
-rw-r--r--error.c828
-rw-r--r--eval.c685
-rw-r--r--eval_error.c333
-rw-r--r--eval_intern.h112
-rw-r--r--eval_jump.c32
-rw-r--r--ext/-test-/arith_seq/extract/extconf.rb2
-rw-r--r--ext/-test-/arith_seq/extract/extract.c27
-rw-r--r--ext/-test-/bignum/depend6
-rw-r--r--ext/-test-/bug-14834/bug-14384.c35
-rw-r--r--ext/-test-/bug-14834/depend13
-rw-r--r--ext/-test-/bug-14834/extconf.rb2
-rw-r--r--ext/-test-/exception/depend1
-rw-r--r--ext/-test-/exception/enc_raise.c2
-rw-r--r--ext/-test-/exception/ensured.c14
-rw-r--r--ext/-test-/file/depend2
-rw-r--r--ext/-test-/file/fs.c3
-rw-r--r--ext/-test-/funcall/funcall.c44
-rw-r--r--ext/-test-/funcall/passing_block.c30
-rw-r--r--ext/-test-/integer/core_ext.c7
-rw-r--r--ext/-test-/integer/depend1
-rw-r--r--ext/-test-/iter/break.c4
-rw-r--r--ext/-test-/load/protect/extconf.rb1
-rw-r--r--ext/-test-/load/protect/protect.c19
-rw-r--r--ext/-test-/memory_status/memory_status.c17
-rw-r--r--ext/-test-/notimplement/bug.c2
-rw-r--r--ext/-test-/printf/printf.c8
-rw-r--r--ext/-test-/rational/depend1
-rw-r--r--ext/-test-/regexp/extconf.rb3
-rw-r--r--ext/-test-/regexp/init.c11
-rw-r--r--ext/-test-/regexp/parse_depth_limit.c23
-rwxr-xr-xext/-test-/scan_args/extconf.rb1
-rw-r--r--ext/-test-/scan_args/scan_args.c286
-rw-r--r--ext/-test-/string/capacity.c2
-rw-r--r--ext/-test-/string/coderange.c4
-rw-r--r--ext/-test-/string/cstr.c3
-rw-r--r--ext/-test-/string/depend34
-rw-r--r--ext/-test-/string/ellipsize.c2
-rw-r--r--ext/-test-/string/enc_associate.c2
-rw-r--r--ext/-test-/string/enc_str_buf_cat.c2
-rw-r--r--ext/-test-/string/fstring.c2
-rw-r--r--ext/-test-/string/init.c2
-rw-r--r--ext/-test-/string/modify.c2
-rw-r--r--ext/-test-/string/new.c21
-rw-r--r--ext/-test-/string/nofree.c2
-rw-r--r--ext/-test-/string/normalize.c2
-rw-r--r--ext/-test-/string/qsort.c2
-rw-r--r--ext/-test-/string/rb_str_dup.c35
-rw-r--r--ext/-test-/string/set_len.c2
-rw-r--r--ext/-test-/struct/depend46
-rw-r--r--ext/-test-/struct/len.c13
-rw-r--r--ext/-test-/symbol/init.c8
-rw-r--r--ext/-test-/thread_fd_close/depend16
-rw-r--r--ext/-test-/thread_fd_close/extconf.rb2
-rw-r--r--ext/-test-/thread_fd_close/thread_fd_close.c14
-rw-r--r--ext/-test-/time/depend35
-rw-r--r--ext/-test-/time/init.c2
-rw-r--r--ext/-test-/time/leap_second.c15
-rw-r--r--ext/-test-/time/new.c2
-rw-r--r--ext/-test-/typeddata/typeddata.c24
-rw-r--r--ext/-test-/wait_for_single_fd/depend1
-rw-r--r--ext/-test-/wait_for_single_fd/extconf.rb2
-rw-r--r--ext/-test-/wait_for_single_fd/wait_for_single_fd.c64
-rw-r--r--ext/-test-/win32/console/attribute.c5
-rw-r--r--ext/.document26
-rw-r--r--ext/Setup2
-rw-r--r--ext/Setup.nacl44
-rw-r--r--ext/bigdecimal/bigdecimal.c997
-rw-r--r--ext/bigdecimal/bigdecimal.def3
-rw-r--r--ext/bigdecimal/bigdecimal.gemspec50
-rw-r--r--ext/bigdecimal/bigdecimal.h70
-rw-r--r--ext/bigdecimal/depend4
-rw-r--r--ext/bigdecimal/extconf.rb31
-rw-r--r--ext/bigdecimal/lib/bigdecimal.rb6
-rw-r--r--ext/bigdecimal/lib/bigdecimal/jacobian.rb3
-rw-r--r--ext/bigdecimal/lib/bigdecimal/math.rb18
-rw-r--r--ext/bigdecimal/lib/bigdecimal/util.rb128
-rw-r--r--ext/bigdecimal/sample/linear.rb21
-rw-r--r--ext/bigdecimal/sample/nlsolve.rb10
-rw-r--r--ext/bigdecimal/util/extconf.rb24
-rw-r--r--ext/bigdecimal/util/util.c9
-rw-r--r--ext/cgi/escape/depend1
-rw-r--r--ext/cgi/escape/escape.c5
-rw-r--r--ext/coverage/coverage.c249
-rw-r--r--ext/coverage/depend2
-rw-r--r--ext/coverage/lib/coverage.rb14
-rw-r--r--ext/date/date.gemspec28
-rw-r--r--ext/date/date_core.c1177
-rw-r--r--ext/date/date_parse.c98
-rw-r--r--ext/date/date_strptime.c77
-rw-r--r--ext/date/depend3
-rw-r--r--ext/date/extconf.rb7
-rw-r--r--ext/date/lib/date.rb17
-rw-r--r--ext/date/prereq.mk2
-rw-r--r--ext/date/zonetab.h375
-rw-r--r--ext/dbm/dbm.c75
-rw-r--r--ext/dbm/dbm.gemspec20
-rw-r--r--ext/dbm/extconf.rb8
-rw-r--r--ext/digest/bubblebabble/bubblebabble.c1
-rw-r--r--ext/digest/digest.c1
-rw-r--r--ext/digest/digest_conf.rb54
-rw-r--r--ext/etc/depend1
-rw-r--r--ext/etc/etc.c17
-rw-r--r--ext/etc/etc.gemspec40
-rw-r--r--ext/etc/extconf.rb14
-rw-r--r--ext/etc/mkconstants.rb18
-rwxr-xr-xext/extmk.rb324
-rw-r--r--ext/fcntl/extconf.rb2
-rw-r--r--ext/fcntl/fcntl.c2
-rw-r--r--ext/fcntl/fcntl.gemspec25
-rw-r--r--ext/fiddle/closure.c8
-rw-r--r--ext/fiddle/depend12
-rw-r--r--ext/fiddle/extconf.rb40
-rw-r--r--ext/fiddle/extlibs5
-rw-r--r--ext/fiddle/fiddle.gemspec23
-rw-r--r--ext/fiddle/function.c4
-rw-r--r--ext/fiddle/lib/fiddle.rb2
-rw-r--r--ext/fiddle/lib/fiddle/closure.rb2
-rw-r--r--ext/fiddle/lib/fiddle/cparser.rb5
-rw-r--r--ext/fiddle/lib/fiddle/function.rb2
-rw-r--r--ext/fiddle/lib/fiddle/import.rb14
-rw-r--r--ext/fiddle/lib/fiddle/pack.rb25
-rw-r--r--ext/fiddle/lib/fiddle/struct.rb2
-rw-r--r--ext/fiddle/lib/fiddle/types.rb2
-rw-r--r--ext/fiddle/lib/fiddle/value.rb29
-rw-r--r--ext/fiddle/pointer.c3
-rwxr-xr-xext/fiddle/win32/libffi-config.rb2
-rw-r--r--ext/gdbm/gdbm.c48
-rw-r--r--ext/gdbm/gdbm.gemspec26
-rw-r--r--ext/io/console/console.c55
-rw-r--r--ext/io/console/depend3
-rw-r--r--ext/io/console/extconf.rb5
-rw-r--r--ext/io/console/io-console.gemspec13
-rw-r--r--ext/io/console/lib/console/size.rb2
-rw-r--r--ext/io/console/win32_vk.inc361
-rw-r--r--ext/io/console/win32_vk.list2
-rw-r--r--ext/io/nonblock/depend1
-rw-r--r--ext/io/nonblock/nonblock.c2
-rw-r--r--ext/io/wait/depend1
-rw-r--r--ext/json/fbuffer/fbuffer.h3
-rw-r--r--ext/json/generator/depend1
-rw-r--r--ext/json/generator/generator.c13
-rw-r--r--ext/json/generator/generator.h1
-rw-r--r--ext/json/json.gemspecbin5473 -> 5474 bytes-rw-r--r--ext/json/lib/json/version.rb2
-rw-r--r--ext/json/parser/depend1
-rw-r--r--ext/json/parser/parser.c209
-rw-r--r--ext/json/parser/parser.h1
-rw-r--r--ext/json/parser/parser.rl63
-rw-r--r--ext/mathn/complex/complex.c7
-rw-r--r--ext/mathn/complex/extconf.rb4
-rw-r--r--ext/mathn/rational/extconf.rb4
-rw-r--r--ext/mathn/rational/rational.c7
-rw-r--r--ext/nkf/depend1
-rw-r--r--ext/nkf/nkf-utf8/nkf.c51
-rw-r--r--ext/nkf/nkf-utf8/utf8tbl.c158
-rw-r--r--ext/nkf/nkf.c9
-rw-r--r--ext/objspace/depend3
-rw-r--r--ext/objspace/extconf.rb2
-rw-r--r--ext/objspace/object_tracing.c38
-rw-r--r--ext/objspace/objspace.c60
-rw-r--r--ext/objspace/objspace_dump.c65
-rw-r--r--ext/openssl/History.md339
-rw-r--r--ext/openssl/depend163
-rw-r--r--ext/openssl/deprecation.rb3
-rw-r--r--ext/openssl/extconf.rb121
-rw-r--r--ext/openssl/lib/openssl.rb1
-rw-r--r--ext/openssl/lib/openssl/bn.rb3
-rw-r--r--ext/openssl/lib/openssl/buffering.rb74
-rw-r--r--ext/openssl/lib/openssl/config.rb23
-rw-r--r--ext/openssl/lib/openssl/digest.rb9
-rw-r--r--ext/openssl/lib/openssl/pkcs5.rb22
-rw-r--r--ext/openssl/lib/openssl/pkey.rb59
-rw-r--r--ext/openssl/lib/openssl/ssl.rb164
-rw-r--r--ext/openssl/lib/openssl/x509.rb41
-rw-r--r--ext/openssl/openssl.gemspec38
-rw-r--r--ext/openssl/openssl_missing.c81
-rw-r--r--ext/openssl/openssl_missing.h66
-rw-r--r--ext/openssl/ossl.c424
-rw-r--r--ext/openssl/ossl.h88
-rw-r--r--ext/openssl/ossl_asn1.c847
-rw-r--r--ext/openssl/ossl_asn1.h4
-rw-r--r--ext/openssl/ossl_bio.c73
-rw-r--r--ext/openssl/ossl_bio.h5
-rw-r--r--ext/openssl/ossl_bn.c170
-rw-r--r--ext/openssl/ossl_bn.h4
-rw-r--r--ext/openssl/ossl_cipher.c161
-rw-r--r--ext/openssl/ossl_cipher.h2
-rw-r--r--ext/openssl/ossl_config.c2
-rw-r--r--ext/openssl/ossl_digest.c57
-rw-r--r--ext/openssl/ossl_digest.h2
-rw-r--r--ext/openssl/ossl_engine.c169
-rw-r--r--ext/openssl/ossl_hmac.c41
-rw-r--r--ext/openssl/ossl_kdf.c319
-rw-r--r--ext/openssl/ossl_kdf.h6
-rw-r--r--ext/openssl/ossl_ns_spki.c49
-rw-r--r--ext/openssl/ossl_ocsp.c165
-rw-r--r--ext/openssl/ossl_pkcs12.c38
-rw-r--r--ext/openssl/ossl_pkcs5.c180
-rw-r--r--ext/openssl/ossl_pkcs5.h6
-rw-r--r--ext/openssl/ossl_pkcs7.c49
-rw-r--r--ext/openssl/ossl_pkey.c184
-rw-r--r--ext/openssl/ossl_pkey.h18
-rw-r--r--ext/openssl/ossl_pkey_dh.c28
-rw-r--r--ext/openssl/ossl_pkey_dsa.c46
-rw-r--r--ext/openssl/ossl_pkey_ec.c280
-rw-r--r--ext/openssl/ossl_pkey_rsa.c246
-rw-r--r--ext/openssl/ossl_rand.c24
-rw-r--r--ext/openssl/ossl_ssl.c889
-rw-r--r--ext/openssl/ossl_ssl.h5
-rw-r--r--ext/openssl/ossl_ssl_session.c76
-rw-r--r--ext/openssl/ossl_version.h2
-rw-r--r--ext/openssl/ossl_x509.c15
-rw-r--r--ext/openssl/ossl_x509.h16
-rw-r--r--ext/openssl/ossl_x509attr.c10
-rw-r--r--ext/openssl/ossl_x509cert.c100
-rw-r--r--ext/openssl/ossl_x509crl.c75
-rw-r--r--ext/openssl/ossl_x509ext.c23
-rw-r--r--ext/openssl/ossl_x509name.c157
-rw-r--r--ext/openssl/ossl_x509req.c81
-rw-r--r--ext/openssl/ossl_x509revoked.c37
-rw-r--r--ext/openssl/ossl_x509store.c177
-rw-r--r--ext/openssl/ruby_missing.h22
-rw-r--r--ext/pathname/depend1
-rw-r--r--ext/pathname/lib/pathname.rb18
-rw-r--r--ext/pathname/pathname.c371
-rw-r--r--ext/psych/.gitignore11
-rw-r--r--ext/psych/depend5
-rw-r--r--ext/psych/extconf.rb2
-rw-r--r--ext/psych/lib/psych.rb247
-rw-r--r--ext/psych/lib/psych/class_loader.rb2
-rw-r--r--ext/psych/lib/psych/coder.rb2
-rw-r--r--ext/psych/lib/psych/core_ext.rb21
-rw-r--r--ext/psych/lib/psych/deprecated.rb86
-rw-r--r--ext/psych/lib/psych/exception.rb2
-rw-r--r--ext/psych/lib/psych/handler.rb9
-rw-r--r--ext/psych/lib/psych/handlers/document_stream.rb2
-rw-r--r--ext/psych/lib/psych/handlers/recorder.rb2
-rw-r--r--ext/psych/lib/psych/json/ruby_events.rb2
-rw-r--r--ext/psych/lib/psych/json/stream.rb2
-rw-r--r--ext/psych/lib/psych/json/tree_builder.rb2
-rw-r--r--ext/psych/lib/psych/json/yaml_events.rb2
-rw-r--r--ext/psych/lib/psych/nodes.rb2
-rw-r--r--ext/psych/lib/psych/nodes/alias.rb4
-rw-r--r--ext/psych/lib/psych/nodes/document.rb4
-rw-r--r--ext/psych/lib/psych/nodes/mapping.rb4
-rw-r--r--ext/psych/lib/psych/nodes/node.rb21
-rw-r--r--ext/psych/lib/psych/nodes/scalar.rb4
-rw-r--r--ext/psych/lib/psych/nodes/sequence.rb4
-rw-r--r--ext/psych/lib/psych/nodes/stream.rb4
-rw-r--r--ext/psych/lib/psych/omap.rb2
-rw-r--r--ext/psych/lib/psych/parser.rb2
-rw-r--r--ext/psych/lib/psych/scalar_scanner.rb9
-rw-r--r--ext/psych/lib/psych/set.rb2
-rw-r--r--ext/psych/lib/psych/stream.rb2
-rw-r--r--ext/psych/lib/psych/streaming.rb2
-rw-r--r--ext/psych/lib/psych/syntax_error.rb2
-rw-r--r--ext/psych/lib/psych/tree_builder.rb50
-rw-r--r--ext/psych/lib/psych/versions.rb10
-rw-r--r--ext/psych/lib/psych/visitors.rb2
-rw-r--r--ext/psych/lib/psych/visitors/depth_first.rb2
-rw-r--r--ext/psych/lib/psych/visitors/emitter.rb2
-rw-r--r--ext/psych/lib/psych/visitors/json_tree.rb2
-rw-r--r--ext/psych/lib/psych/visitors/to_ruby.rb7
-rw-r--r--ext/psych/lib/psych/visitors/visitor.rb2
-rw-r--r--ext/psych/lib/psych/visitors/yaml_tree.rb75
-rw-r--r--ext/psych/lib/psych/y.rb2
-rw-r--r--ext/psych/psych.gemspec62
-rw-r--r--ext/psych/psych.h3
-rw-r--r--ext/psych/psych_emitter.c47
-rw-r--r--ext/psych/psych_parser.c79
-rw-r--r--ext/psych/psych_to_ruby.c4
-rw-r--r--ext/psych/psych_yaml_tree.c2
-rw-r--r--ext/psych/yaml/LICENSE19
-rw-r--r--ext/psych/yaml/api.c104
-rw-r--r--ext/psych/yaml/config.h14
-rw-r--r--ext/psych/yaml/dumper.c4
-rw-r--r--ext/psych/yaml/emitter.c27
-rw-r--r--ext/psych/yaml/loader.c29
-rw-r--r--ext/psych/yaml/parser.c10
-rw-r--r--ext/psych/yaml/reader.c6
-rw-r--r--ext/psych/yaml/scanner.c22
-rw-r--r--ext/psych/yaml/yaml_private.h62
-rw-r--r--ext/pty/depend1
-rw-r--r--ext/pty/extconf.rb4
-rw-r--r--ext/pty/lib/expect.rb11
-rw-r--r--ext/pty/pty.c113
-rw-r--r--ext/rbconfig/sizeof/depend22
-rw-r--r--ext/rbconfig/sizeof/extconf.rb2
-rw-r--r--ext/readline/.gitignore1
-rw-r--r--ext/readline/depend1
-rw-r--r--ext/readline/extconf.rb7
-rw-r--r--ext/readline/readline.c93
-rw-r--r--ext/ripper/depend12
-rw-r--r--ext/ripper/eventids2.c21
-rw-r--r--ext/ripper/extconf.rb2
-rw-r--r--ext/ripper/lib/ripper.rb10
-rw-r--r--ext/ripper/lib/ripper/core.rb2
-rw-r--r--ext/ripper/lib/ripper/filter.rb12
-rw-r--r--ext/ripper/lib/ripper/lexer.rb78
-rw-r--r--ext/ripper/lib/ripper/sexp.rb14
-rw-r--r--ext/ripper/tools/dsl.rb86
-rwxr-xr-xext/ripper/tools/generate-param-macros.rb2
-rwxr-xr-xext/ripper/tools/generate.rb15
-rwxr-xr-xext/ripper/tools/preproc.rb45
-rwxr-xr-xext/ripper/tools/strip.rb2
-rw-r--r--ext/rubyvm/extconf.rb1
-rw-r--r--ext/rubyvm/lib/forwardable/impl.rb16
-rw-r--r--ext/sdbm/_sdbm.c56
-rw-r--r--ext/sdbm/init.c22
-rw-r--r--ext/sdbm/sdbm.gemspec21
-rw-r--r--ext/socket/basicsocket.c30
-rw-r--r--ext/socket/depend19
-rw-r--r--ext/socket/extconf.rb5
-rw-r--r--ext/socket/getaddrinfo.c2
-rw-r--r--ext/socket/ifaddr.c66
-rw-r--r--ext/socket/init.c195
-rw-r--r--ext/socket/ipsocket.c38
-rw-r--r--ext/socket/lib/socket.rb128
-rw-r--r--ext/socket/mkconstants.rb30
-rw-r--r--ext/socket/option.c4
-rw-r--r--ext/socket/raddrinfo.c26
-rw-r--r--ext/socket/rubysocket.h13
-rw-r--r--ext/socket/socket.c53
-rw-r--r--ext/socket/tcpsocket.c11
-rw-r--r--ext/socket/unixsocket.c52
-rw-r--r--ext/stringio/depend1
-rw-r--r--ext/stringio/stringio.c146
-rw-r--r--ext/stringio/stringio.gemspec27
-rw-r--r--ext/strscan/depend1
-rw-r--r--ext/strscan/extconf.rb2
-rw-r--r--ext/strscan/strscan.c97
-rw-r--r--ext/strscan/strscan.gemspec19
-rw-r--r--ext/syslog/syslog.c9
-rw-r--r--ext/win32/extconf.rb2
-rw-r--r--ext/win32/lib/Win32API.rb2
-rw-r--r--ext/win32/lib/win32/registry.rb16
-rw-r--r--ext/win32/lib/win32/resolv.rb40
-rw-r--r--ext/win32/lib/win32/resolv9x.rb2
-rw-r--r--ext/win32/lib/win32/sspi.rb2
-rw-r--r--ext/win32ole/lib/win32ole.rb33
-rw-r--r--ext/win32ole/win32ole.c164
-rw-r--r--ext/win32ole/win32ole.h4
-rw-r--r--ext/win32ole/win32ole_error.c4
-rw-r--r--ext/win32ole/win32ole_error.h3
-rw-r--r--ext/win32ole/win32ole_event.c7
-rw-r--r--ext/win32ole/win32ole_method.c14
-rw-r--r--ext/win32ole/win32ole_method.h2
-rw-r--r--ext/win32ole/win32ole_record.c2
-rw-r--r--ext/win32ole/win32ole_record.h2
-rw-r--r--ext/win32ole/win32ole_type.c2
-rw-r--r--ext/win32ole/win32ole_type.h2
-rw-r--r--ext/win32ole/win32ole_typelib.c2
-rw-r--r--ext/win32ole/win32ole_typelib.h2
-rw-r--r--ext/win32ole/win32ole_variable.c2
-rw-r--r--ext/win32ole/win32ole_variable.h2
-rw-r--r--ext/win32ole/win32ole_variant.c5
-rw-r--r--ext/win32ole/win32ole_variant.h2
-rw-r--r--ext/win32ole/win32ole_variant_m.c2
-rw-r--r--ext/win32ole/win32ole_variant_m.h2
-rw-r--r--ext/zlib/.gitignore1
-rw-r--r--ext/zlib/depend1
-rw-r--r--ext/zlib/extconf.rb34
-rw-r--r--ext/zlib/zlib.c587
-rw-r--r--ext/zlib/zlib.gemspec28
-rw-r--r--file.c1057
-rw-r--r--gc.c1682
-rw-r--r--gc.h6
-rw-r--r--gems/bundled_gems14
-rw-r--r--golf_prelude.rb7
-rw-r--r--goruby.c7
-rw-r--r--hash.c2323
-rw-r--r--hrtime.h168
-rw-r--r--ia64.S (renamed from ia64.s)0
-rw-r--r--id_table.c1369
-rw-r--r--include/ruby/backward.h49
-rw-r--r--include/ruby/debug.h7
-rw-r--r--include/ruby/defines.h184
-rw-r--r--include/ruby/encoding.h6
-rw-r--r--include/ruby/intern.h93
-rw-r--r--include/ruby/io.h7
-rw-r--r--include/ruby/missing.h24
-rw-r--r--include/ruby/onigmo.h935
-rw-r--r--include/ruby/oniguruma.h880
-rw-r--r--include/ruby/ruby.h517
-rw-r--r--include/ruby/st.h13
-rw-r--r--include/ruby/util.h1
-rw-r--r--include/ruby/version.h2
-rw-r--r--include/ruby/vm.h2
-rw-r--r--include/ruby/win32.h86
-rw-r--r--inits.c6
-rw-r--r--insns.def1876
-rw-r--r--internal.h1039
-rw-r--r--io.c2086
-rw-r--r--iseq.c1853
-rw-r--r--iseq.h232
-rw-r--r--lex.c.blt121
-rw-r--r--lib/.document25
-rw-r--r--lib/English.rb6
-rw-r--r--lib/abbrev.rb2
-rw-r--r--lib/base64.rb6
-rw-r--r--lib/benchmark.rb15
-rw-r--r--lib/bundler.rb567
-rw-r--r--lib/bundler/build_metadata.rb53
-rw-r--r--lib/bundler/bundler.gemspec64
-rw-r--r--lib/bundler/capistrano.rb22
-rw-r--r--lib/bundler/cli.rb790
-rw-r--r--lib/bundler/cli/add.rb35
-rw-r--r--lib/bundler/cli/binstubs.rb49
-rw-r--r--lib/bundler/cli/cache.rb36
-rw-r--r--lib/bundler/cli/check.rb38
-rw-r--r--lib/bundler/cli/clean.rb25
-rw-r--r--lib/bundler/cli/common.rb102
-rw-r--r--lib/bundler/cli/config.rb119
-rw-r--r--lib/bundler/cli/console.rb43
-rw-r--r--lib/bundler/cli/doctor.rb140
-rw-r--r--lib/bundler/cli/exec.rb105
-rw-r--r--lib/bundler/cli/gem.rb252
-rw-r--r--lib/bundler/cli/info.rb50
-rw-r--r--lib/bundler/cli/init.rb47
-rw-r--r--lib/bundler/cli/inject.rb60
-rw-r--r--lib/bundler/cli/install.rb217
-rw-r--r--lib/bundler/cli/issue.rb40
-rw-r--r--lib/bundler/cli/list.rb58
-rw-r--r--lib/bundler/cli/lock.rb63
-rw-r--r--lib/bundler/cli/open.rb26
-rw-r--r--lib/bundler/cli/outdated.rb266
-rw-r--r--lib/bundler/cli/package.rb49
-rw-r--r--lib/bundler/cli/platform.rb46
-rw-r--r--lib/bundler/cli/plugin.rb24
-rw-r--r--lib/bundler/cli/pristine.rb47
-rw-r--r--lib/bundler/cli/remove.rb18
-rw-r--r--lib/bundler/cli/show.rb75
-rw-r--r--lib/bundler/cli/update.rb91
-rw-r--r--lib/bundler/cli/viz.rb31
-rw-r--r--lib/bundler/compact_index_client.rb109
-rw-r--r--lib/bundler/compact_index_client/cache.rb118
-rw-r--r--lib/bundler/compact_index_client/updater.rb116
-rw-r--r--lib/bundler/compatibility_guard.rb14
-rw-r--r--lib/bundler/constants.rb7
-rw-r--r--lib/bundler/current_ruby.rb93
-rw-r--r--lib/bundler/definition.rb993
-rw-r--r--lib/bundler/dep_proxy.rb48
-rw-r--r--lib/bundler/dependency.rb139
-rw-r--r--lib/bundler/deployment.rb69
-rw-r--r--lib/bundler/deprecate.rb44
-rw-r--r--lib/bundler/dsl.rb615
-rw-r--r--lib/bundler/endpoint_specification.rb141
-rw-r--r--lib/bundler/env.rb155
-rw-r--r--lib/bundler/environment_preserver.rb59
-rw-r--r--lib/bundler/errors.rb158
-rw-r--r--lib/bundler/feature_flag.rb74
-rw-r--r--lib/bundler/fetcher.rb312
-rw-r--r--lib/bundler/fetcher/base.rb52
-rw-r--r--lib/bundler/fetcher/compact_index.rb126
-rw-r--r--lib/bundler/fetcher/dependency.rb82
-rw-r--r--lib/bundler/fetcher/downloader.rb84
-rw-r--r--lib/bundler/fetcher/index.rb52
-rw-r--r--lib/bundler/friendly_errors.rb131
-rw-r--r--lib/bundler/gem_helper.rb202
-rw-r--r--lib/bundler/gem_helpers.rb101
-rw-r--r--lib/bundler/gem_remote_fetcher.rb43
-rw-r--r--lib/bundler/gem_tasks.rb7
-rw-r--r--lib/bundler/gem_version_promoter.rb190
-rw-r--r--lib/bundler/gemdeps.rb29
-rw-r--r--lib/bundler/graph.rb152
-rw-r--r--lib/bundler/index.rb213
-rw-r--r--lib/bundler/injector.rb253
-rw-r--r--lib/bundler/inline.rb74
-rw-r--r--lib/bundler/installer.rb318
-rw-r--r--lib/bundler/installer/gem_installer.rb85
-rw-r--r--lib/bundler/installer/parallel_installer.rb233
-rw-r--r--lib/bundler/installer/standalone.rb53
-rw-r--r--lib/bundler/lazy_specification.rb123
-rw-r--r--lib/bundler/lockfile_generator.rb95
-rw-r--r--lib/bundler/lockfile_parser.rb256
-rw-r--r--lib/bundler/match_platform.rb24
-rw-r--r--lib/bundler/mirror.rb223
-rw-r--r--lib/bundler/plugin.rb292
-rw-r--r--lib/bundler/plugin/api.rb81
-rw-r--r--lib/bundler/plugin/api/source.rb306
-rw-r--r--lib/bundler/plugin/dsl.rb53
-rw-r--r--lib/bundler/plugin/events.rb61
-rw-r--r--lib/bundler/plugin/index.rb162
-rw-r--r--lib/bundler/plugin/installer.rb96
-rw-r--r--lib/bundler/plugin/installer/git.rb38
-rw-r--r--lib/bundler/plugin/installer/rubygems.rb27
-rw-r--r--lib/bundler/plugin/source_list.rb27
-rw-r--r--lib/bundler/process_lock.rb24
-rw-r--r--lib/bundler/psyched_yaml.rb37
-rw-r--r--lib/bundler/remote_specification.rb114
-rw-r--r--lib/bundler/resolver.rb373
-rw-r--r--lib/bundler/resolver/spec_group.rb106
-rw-r--r--lib/bundler/retry.rb66
-rw-r--r--lib/bundler/ruby_dsl.rb18
-rw-r--r--lib/bundler/ruby_version.rb152
-rw-r--r--lib/bundler/rubygems_ext.rb210
-rw-r--r--lib/bundler/rubygems_gem_installer.rb99
-rw-r--r--lib/bundler/rubygems_integration.rb898
-rw-r--r--lib/bundler/runtime.rb322
-rw-r--r--lib/bundler/settings.rb463
-rw-r--r--lib/bundler/settings/validator.rb102
-rw-r--r--lib/bundler/setup.rb28
-rw-r--r--lib/bundler/shared_helpers.rb384
-rw-r--r--lib/bundler/similarity_detector.rb63
-rw-r--r--lib/bundler/source.rb94
-rw-r--r--lib/bundler/source/gemspec.rb18
-rw-r--r--lib/bundler/source/git.rb329
-rw-r--r--lib/bundler/source/git/git_proxy.rb262
-rw-r--r--lib/bundler/source/metadata.rb63
-rw-r--r--lib/bundler/source/path.rb249
-rw-r--r--lib/bundler/source/path/installer.rb74
-rw-r--r--lib/bundler/source/rubygems.rb539
-rw-r--r--lib/bundler/source/rubygems/remote.rb69
-rw-r--r--lib/bundler/source_list.rb186
-rw-r--r--lib/bundler/spec_set.rb192
-rw-r--r--lib/bundler/ssl_certs/.document1
-rw-r--r--lib/bundler/ssl_certs/certificate_manager.rb66
-rw-r--r--lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem (renamed from lib/rubygems/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem)0
-rw-r--r--lib/bundler/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem (renamed from lib/rubygems/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem)0
-rw-r--r--lib/bundler/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem (renamed from lib/rubygems/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem)0
-rw-r--r--lib/bundler/stub_specification.rb108
-rw-r--r--lib/bundler/templates/.document1
-rw-r--r--lib/bundler/templates/Executable29
-rw-r--r--lib/bundler/templates/Executable.bundler105
-rw-r--r--lib/bundler/templates/Executable.standalone14
-rw-r--r--lib/bundler/templates/Gemfile7
-rw-r--r--lib/bundler/templates/gems.rb8
-rw-r--r--lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt74
-rw-r--r--lib/bundler/templates/newgem/Gemfile.tt6
-rw-r--r--lib/bundler/templates/newgem/LICENSE.txt.tt21
-rw-r--r--lib/bundler/templates/newgem/README.md.tt47
-rw-r--r--lib/bundler/templates/newgem/Rakefile.tt29
-rw-r--r--lib/bundler/templates/newgem/bin/console.tt14
-rw-r--r--lib/bundler/templates/newgem/bin/setup.tt8
-rw-r--r--lib/bundler/templates/newgem/exe/newgem.tt3
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt3
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/newgem.c.tt9
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/newgem.h.tt6
-rw-r--r--lib/bundler/templates/newgem/gitignore.tt20
-rw-r--r--lib/bundler/templates/newgem/lib/newgem.rb.tt13
-rw-r--r--lib/bundler/templates/newgem/lib/newgem/version.rb.tt7
-rw-r--r--lib/bundler/templates/newgem/newgem.gemspec.tt55
-rw-r--r--lib/bundler/templates/newgem/rspec.tt3
-rw-r--r--lib/bundler/templates/newgem/spec/newgem_spec.rb.tt9
-rw-r--r--lib/bundler/templates/newgem/spec/spec_helper.rb.tt14
-rw-r--r--lib/bundler/templates/newgem/test/newgem_test.rb.tt11
-rw-r--r--lib/bundler/templates/newgem/test/test_helper.rb.tt4
-rw-r--r--lib/bundler/templates/newgem/travis.yml.tt7
-rw-r--r--lib/bundler/ui.rb9
-rw-r--r--lib/bundler/ui/rg_proxy.rb19
-rw-r--r--lib/bundler/ui/shell.rb146
-rw-r--r--lib/bundler/ui/silent.rb69
-rw-r--r--lib/bundler/uri_credentials_filter.rb37
-rw-r--r--lib/bundler/vendor/fileutils/lib/fileutils.rb1638
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo.rb12
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/compatibility.rb26
-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.rb81
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb223
-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.rb136
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/errors.rb143
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb6
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb101
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb67
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb837
-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/net-http-persistent/lib/net/http/faster.rb27
-rw-r--r--lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb1233
-rw-r--r--lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse.rb129
-rw-r--r--lib/bundler/vendor/thor/lib/thor.rb509
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions.rb321
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/create_file.rb104
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/create_link.rb60
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/directory.rb118
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb143
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb364
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb109
-rw-r--r--lib/bundler/vendor/thor/lib/thor/base.rb679
-rw-r--r--lib/bundler/vendor/thor/lib/thor/command.rb135
-rw-r--r--lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb97
-rw-r--r--lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb12
-rw-r--r--lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb129
-rw-r--r--lib/bundler/vendor/thor/lib/thor/error.rb32
-rw-r--r--lib/bundler/vendor/thor/lib/thor/group.rb281
-rw-r--r--lib/bundler/vendor/thor/lib/thor/invocation.rb177
-rw-r--r--lib/bundler/vendor/thor/lib/thor/line_editor.rb17
-rw-r--r--lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb37
-rw-r--r--lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb88
-rw-r--r--lib/bundler/vendor/thor/lib/thor/parser.rb4
-rw-r--r--lib/bundler/vendor/thor/lib/thor/parser/argument.rb70
-rw-r--r--lib/bundler/vendor/thor/lib/thor/parser/arguments.rb175
-rw-r--r--lib/bundler/vendor/thor/lib/thor/parser/option.rb146
-rw-r--r--lib/bundler/vendor/thor/lib/thor/parser/options.rb221
-rw-r--r--lib/bundler/vendor/thor/lib/thor/rake_compat.rb71
-rw-r--r--lib/bundler/vendor/thor/lib/thor/runner.rb324
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell.rb81
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell/basic.rb437
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell/color.rb149
-rw-r--r--lib/bundler/vendor/thor/lib/thor/shell/html.rb126
-rw-r--r--lib/bundler/vendor/thor/lib/thor/util.rb268
-rw-r--r--lib/bundler/vendor/thor/lib/thor/version.rb3
-rw-r--r--lib/bundler/vendored_fileutils.rb9
-rw-r--r--lib/bundler/vendored_molinillo.rb4
-rw-r--r--lib/bundler/vendored_persistent.rb52
-rw-r--r--lib/bundler/vendored_thor.rb8
-rw-r--r--lib/bundler/version.rb28
-rw-r--r--lib/bundler/version_ranges.rb76
-rw-r--r--lib/bundler/vlad.rb17
-rw-r--r--lib/bundler/worker.rb106
-rw-r--r--lib/bundler/yaml_serializer.rb90
-rw-r--r--lib/cgi.rb2
-rw-r--r--lib/cgi/cookie.rb7
-rw-r--r--lib/cgi/core.rb43
-rw-r--r--lib/cgi/html.rb6
-rw-r--r--lib/cgi/session.rb10
-rw-r--r--lib/cgi/session/pstore.rb4
-rw-r--r--lib/cgi/util.rb10
-rw-r--r--lib/cmath.gemspec23
-rw-r--r--lib/cmath.rb4
-rw-r--r--lib/csv.rb1868
-rw-r--r--lib/csv/core_ext/array.rb9
-rw-r--r--lib/csv/core_ext/string.rb9
-rw-r--r--lib/csv/csv.gemspec44
-rw-r--r--lib/csv/delete_suffix.rb18
-rw-r--r--lib/csv/fields_converter.rb78
-rw-r--r--lib/csv/match_p.rb20
-rw-r--r--lib/csv/parser.rb1092
-rw-r--r--lib/csv/row.rb388
-rw-r--r--lib/csv/table.rb402
-rw-r--r--lib/csv/version.rb6
-rw-r--r--lib/csv/writer.rb156
-rw-r--r--lib/debug.rb2
-rw-r--r--lib/delegate.rb14
-rw-r--r--lib/drb/acl.rb10
-rw-r--r--lib/drb/drb.rb75
-rw-r--r--lib/drb/extserv.rb2
-rw-r--r--lib/drb/extservm.rb8
-rw-r--r--lib/drb/gw.rb2
-rw-r--r--lib/drb/ssl.rb12
-rw-r--r--lib/drb/timeridconv.rb2
-rw-r--r--lib/drb/unix.rb7
-rw-r--r--lib/e2mmap.gemspec26
-rw-r--r--lib/e2mmap.rb2
-rw-r--r--lib/e2mmap/version.rb3
-rw-r--r--lib/erb.rb166
-rw-r--r--lib/fileutils.rb288
-rw-r--r--lib/fileutils/fileutils.gemspec32
-rw-r--r--lib/fileutils/version.rb5
-rw-r--r--lib/find.rb5
-rw-r--r--lib/forwardable.rb50
-rw-r--r--lib/forwardable/forwardable.gemspec26
-rw-r--r--lib/forwardable/impl.rb16
-rw-r--r--lib/getoptlong.rb22
-rw-r--r--lib/ipaddr.gemspec28
-rw-r--r--lib/ipaddr.rb100
-rw-r--r--lib/irb.rb173
-rw-r--r--lib/irb/cmd/chws.rb4
-rw-r--r--lib/irb/cmd/help.rb2
-rw-r--r--lib/irb/cmd/load.rb4
-rw-r--r--lib/irb/cmd/pushws.rb4
-rw-r--r--lib/irb/cmd/subirb.rb4
-rw-r--r--lib/irb/completion.rb40
-rw-r--r--lib/irb/context.rb15
-rw-r--r--lib/irb/ext/math-mode.rb48
-rw-r--r--lib/irb/ext/multi-irb.rb1
-rw-r--r--lib/irb/ext/use-loader.rb4
-rw-r--r--lib/irb/extend-command.rb3
-rw-r--r--lib/irb/help.rb2
-rw-r--r--lib/irb/init.rb37
-rw-r--r--lib/irb/input-method.rb4
-rw-r--r--lib/irb/irb.gemspec26
-rw-r--r--lib/irb/lc/help-message1
-rw-r--r--lib/irb/lc/ja/help-message1
-rw-r--r--lib/irb/locale.rb2
-rw-r--r--lib/irb/magic-file.rb2
-rw-r--r--lib/irb/notifier.rb2
-rw-r--r--lib/irb/ruby-lex.rb23
-rw-r--r--lib/irb/slex.rb4
-rw-r--r--lib/irb/src_encoding.rb6
-rw-r--r--lib/irb/version.rb5
-rw-r--r--lib/irb/workspace.rb34
-rw-r--r--lib/irb/xmp.rb2
-rw-r--r--lib/logger.gemspec27
-rw-r--r--lib/logger.rb36
-rw-r--r--lib/mathn.rb170
-rw-r--r--lib/matrix.rb542
-rw-r--r--lib/matrix/eigenvalue_decomposition.rb23
-rw-r--r--lib/matrix/lup_decomposition.rb2
-rw-r--r--lib/matrix/matrix.gemspec21
-rw-r--r--lib/mkmf.rb61
-rw-r--r--lib/monitor.rb49
-rw-r--r--lib/mutex_m.gemspec27
-rw-r--r--lib/mutex_m.rb12
-rw-r--r--lib/net/ftp.rb276
-rw-r--r--lib/net/http.rb230
-rw-r--r--lib/net/http/exceptions.rb7
-rw-r--r--lib/net/http/generic_request.rb11
-rw-r--r--lib/net/http/header.rb62
-rw-r--r--lib/net/http/response.rb12
-rw-r--r--lib/net/http/responses.rb67
-rw-r--r--lib/net/http/status.rb84
-rw-r--r--lib/net/https.rb2
-rw-r--r--lib/net/imap.rb182
-rw-r--r--lib/net/pop.rb38
-rw-r--r--lib/net/protocol.rb126
-rw-r--r--lib/net/smtp.rb20
-rw-r--r--lib/observer.rb4
-rw-r--r--lib/open-uri.rb15
-rw-r--r--lib/open3.rb135
-rw-r--r--lib/optparse.rb131
-rw-r--r--lib/ostruct.gemspec21
-rw-r--r--lib/ostruct.rb246
-rw-r--r--lib/pp.rb91
-rw-r--r--lib/prime.gemspec27
-rw-r--r--lib/prime.rb16
-rw-r--r--lib/profile.rb2
-rw-r--r--lib/profiler.rb2
-rw-r--r--lib/pstore.rb4
-rw-r--r--lib/racc/rdoc/grammar.en.rdoc2
-rw-r--r--lib/rbconfig/.document1
-rw-r--r--lib/rbconfig/datadir.rb14
-rw-r--r--lib/rdoc.rb13
-rw-r--r--lib/rdoc/alias.rb2
-rw-r--r--lib/rdoc/anon_class.rb2
-rw-r--r--lib/rdoc/any_method.rb15
-rw-r--r--lib/rdoc/attr.rb2
-rw-r--r--lib/rdoc/class_module.rb6
-rw-r--r--lib/rdoc/code_object.rb11
-rw-r--r--lib/rdoc/code_objects.rb2
-rw-r--r--lib/rdoc/comment.rb30
-rw-r--r--lib/rdoc/constant.rb6
-rw-r--r--lib/rdoc/context.rb83
-rw-r--r--lib/rdoc/context/section.rb4
-rw-r--r--lib/rdoc/cross_reference.rb42
-rw-r--r--lib/rdoc/encoding.rb88
-rw-r--r--lib/rdoc/erb_partial.rb2
-rw-r--r--lib/rdoc/erbio.rb10
-rw-r--r--lib/rdoc/extend.rb2
-rw-r--r--lib/rdoc/generator.rb2
-rw-r--r--lib/rdoc/generator/darkfish.rb75
-rw-r--r--lib/rdoc/generator/json_index.rb11
-rw-r--r--lib/rdoc/generator/markup.rb16
-rw-r--r--lib/rdoc/generator/pot.rb8
-rw-r--r--lib/rdoc/generator/pot/message_extractor.rb2
-rw-r--r--lib/rdoc/generator/pot/po.rb6
-rw-r--r--lib/rdoc/generator/pot/po_entry.rb22
-rw-r--r--lib/rdoc/generator/ri.rb2
-rw-r--r--lib/rdoc/generator/template/darkfish/_footer.rhtml4
-rw-r--r--lib/rdoc/generator/template/darkfish/_head.rhtml7
-rw-r--r--lib/rdoc/generator/template/darkfish/class.rhtml4
-rw-r--r--lib/rdoc/generator/template/darkfish/css/rdoc.css23
-rw-r--r--lib/rdoc/generator/template/darkfish/js/darkfish.js121
-rw-r--r--lib/rdoc/generator/template/darkfish/js/jquery.js4
-rw-r--r--lib/rdoc/generator/template/darkfish/js/search.js63
-rw-r--r--lib/rdoc/generator/template/json_index/js/navigation.js45
-rw-r--r--lib/rdoc/generator/template/json_index/js/searcher.js12
-rw-r--r--lib/rdoc/ghost_method.rb2
-rw-r--r--lib/rdoc/i18n.rb6
-rw-r--r--lib/rdoc/i18n/locale.rb4
-rw-r--r--lib/rdoc/i18n/text.rb10
-rw-r--r--lib/rdoc/include.rb2
-rw-r--r--lib/rdoc/known_classes.rb2
-rw-r--r--lib/rdoc/markdown.rb1319
-rw-r--r--lib/rdoc/markdown/entities.rb2
-rw-r--r--lib/rdoc/markdown/literals.rb26
-rw-r--r--lib/rdoc/markup.rb28
-rw-r--r--lib/rdoc/markup/attr_changer.rb2
-rw-r--r--lib/rdoc/markup/attr_span.rb2
-rw-r--r--lib/rdoc/markup/attribute_manager.rb48
-rw-r--r--lib/rdoc/markup/attributes.rb14
-rw-r--r--lib/rdoc/markup/blank_line.rb2
-rw-r--r--lib/rdoc/markup/block_quote.rb2
-rw-r--r--lib/rdoc/markup/document.rb2
-rw-r--r--lib/rdoc/markup/formatter.rb49
-rw-r--r--lib/rdoc/markup/formatter_test_case.rb2
-rw-r--r--lib/rdoc/markup/hard_break.rb2
-rw-r--r--lib/rdoc/markup/heading.rb8
-rw-r--r--lib/rdoc/markup/include.rb2
-rw-r--r--lib/rdoc/markup/indented_paragraph.rb2
-rw-r--r--lib/rdoc/markup/inline.rb2
-rw-r--r--lib/rdoc/markup/list.rb2
-rw-r--r--lib/rdoc/markup/list_item.rb2
-rw-r--r--lib/rdoc/markup/paragraph.rb2
-rw-r--r--lib/rdoc/markup/parser.rb28
-rw-r--r--lib/rdoc/markup/pre_process.rb15
-rw-r--r--lib/rdoc/markup/raw.rb2
-rw-r--r--lib/rdoc/markup/regexp_handling.rb41
-rw-r--r--lib/rdoc/markup/rule.rb2
-rw-r--r--lib/rdoc/markup/special.rb41
-rw-r--r--lib/rdoc/markup/text_formatter_test_case.rb2
-rw-r--r--lib/rdoc/markup/to_ansi.rb2
-rw-r--r--lib/rdoc/markup/to_bs.rb8
-rw-r--r--lib/rdoc/markup/to_html.rb53
-rw-r--r--lib/rdoc/markup/to_html_crossref.rb47
-rw-r--r--lib/rdoc/markup/to_html_snippet.rb20
-rw-r--r--lib/rdoc/markup/to_joined_paragraph.rb38
-rw-r--r--lib/rdoc/markup/to_label.rb20
-rw-r--r--lib/rdoc/markup/to_markdown.rb18
-rw-r--r--lib/rdoc/markup/to_rdoc.rb14
-rw-r--r--lib/rdoc/markup/to_table_of_contents.rb2
-rw-r--r--lib/rdoc/markup/to_test.rb2
-rw-r--r--lib/rdoc/markup/to_tt_only.rb6
-rw-r--r--lib/rdoc/markup/verbatim.rb2
-rw-r--r--lib/rdoc/meta_method.rb2
-rw-r--r--lib/rdoc/method_attr.rb4
-rw-r--r--lib/rdoc/mixin.rb2
-rw-r--r--lib/rdoc/normal_class.rb6
-rw-r--r--lib/rdoc/normal_module.rb2
-rw-r--r--lib/rdoc/options.rb38
-rw-r--r--lib/rdoc/parser.rb35
-rw-r--r--lib/rdoc/parser/c.rb21
-rw-r--r--lib/rdoc/parser/changelog.rb10
-rw-r--r--lib/rdoc/parser/markdown.rb2
-rw-r--r--lib/rdoc/parser/rd.rb2
-rw-r--r--lib/rdoc/parser/ripper_state_lex.rb589
-rw-r--r--lib/rdoc/parser/ruby.rb1081
-rw-r--r--lib/rdoc/parser/ruby_tools.rb67
-rw-r--r--lib/rdoc/parser/simple.rb6
-rw-r--r--lib/rdoc/parser/text.rb2
-rw-r--r--lib/rdoc/rd.rb2
-rw-r--r--lib/rdoc/rd/block_parser.rb93
-rw-r--r--lib/rdoc/rd/inline.rb10
-rw-r--r--lib/rdoc/rd/inline_parser.rb271
-rw-r--r--lib/rdoc/rdoc.gemspec25
-rw-r--r--lib/rdoc/rdoc.rb42
-rw-r--r--lib/rdoc/require.rb2
-rw-r--r--lib/rdoc/ri.rb2
-rw-r--r--lib/rdoc/ri/driver.rb160
-rw-r--r--lib/rdoc/ri/formatter.rb2
-rw-r--r--lib/rdoc/ri/paths.rb2
-rw-r--r--lib/rdoc/ri/store.rb2
-rw-r--r--lib/rdoc/ri/task.rb2
-rw-r--r--lib/rdoc/ruby_lex.rb1367
-rw-r--r--lib/rdoc/ruby_token.rb461
-rw-r--r--lib/rdoc/rubygems_hook.rb2
-rw-r--r--lib/rdoc/servlet.rb9
-rw-r--r--lib/rdoc/single_class.rb2
-rw-r--r--lib/rdoc/stats.rb2
-rw-r--r--lib/rdoc/stats/normal.rb42
-rw-r--r--lib/rdoc/stats/quiet.rb2
-rw-r--r--lib/rdoc/stats/verbose.rb2
-rw-r--r--lib/rdoc/store.rb47
-rw-r--r--lib/rdoc/task.rb2
-rw-r--r--lib/rdoc/test_case.rb204
-rw-r--r--lib/rdoc/text.rb27
-rw-r--r--lib/rdoc/token_stream.rb78
-rw-r--r--lib/rdoc/tom_doc.rb16
-rw-r--r--lib/rdoc/top_level.rb12
-rw-r--r--lib/rdoc/version.rb8
-rw-r--r--lib/resolv.rb167
-rw-r--r--lib/rexml/attlistdecl.rb4
-rw-r--r--lib/rexml/attribute.rb4
-rw-r--r--lib/rexml/cdata.rb4
-rw-r--r--lib/rexml/child.rb2
-rw-r--r--lib/rexml/comment.rb4
-rw-r--r--lib/rexml/doctype.rb76
-rw-r--r--lib/rexml/document.rb28
-rw-r--r--lib/rexml/dtd/attlistdecl.rb2
-rw-r--r--lib/rexml/dtd/dtd.rb12
-rw-r--r--lib/rexml/dtd/elementdecl.rb2
-rw-r--r--lib/rexml/dtd/entitydecl.rb2
-rw-r--r--lib/rexml/dtd/notationdecl.rb2
-rw-r--r--lib/rexml/element.rb42
-rw-r--r--lib/rexml/entity.rb11
-rw-r--r--lib/rexml/formatters/default.rb14
-rw-r--r--lib/rexml/formatters/pretty.rb2
-rw-r--r--lib/rexml/formatters/transitive.rb2
-rw-r--r--lib/rexml/functions.rb83
-rw-r--r--lib/rexml/instruction.rb34
-rw-r--r--lib/rexml/light/node.rb2
-rw-r--r--lib/rexml/namespace.rb25
-rw-r--r--lib/rexml/node.rb8
-rw-r--r--lib/rexml/output.rb2
-rw-r--r--lib/rexml/parent.rb2
-rw-r--r--lib/rexml/parsers/baseparser.rb351
-rw-r--r--lib/rexml/parsers/lightparser.rb6
-rw-r--r--lib/rexml/parsers/pullparser.rb6
-rw-r--r--lib/rexml/parsers/sax2parser.rb8
-rw-r--r--lib/rexml/parsers/streamparser.rb10
-rw-r--r--lib/rexml/parsers/treeparser.rb4
-rw-r--r--lib/rexml/parsers/ultralightparser.rb4
-rw-r--r--lib/rexml/parsers/xpathparser.rb78
-rw-r--r--lib/rexml/quickpath.rb4
-rw-r--r--lib/rexml/rexml.gemspec85
-rw-r--r--lib/rexml/rexml.rb6
-rw-r--r--lib/rexml/source.rb3
-rw-r--r--lib/rexml/text.rb58
-rw-r--r--lib/rexml/undefinednamespaceexception.rb2
-rw-r--r--lib/rexml/validation/relaxng.rb4
-rw-r--r--lib/rexml/validation/validation.rb2
-rw-r--r--lib/rexml/xmldecl.rb39
-rw-r--r--lib/rexml/xpath.rb16
-rw-r--r--lib/rexml/xpath_parser.rb806
-rw-r--r--lib/rinda/rinda.rb1
-rw-r--r--lib/rinda/ring.rb9
-rw-r--r--lib/rinda/tuplespace.rb3
-rw-r--r--lib/rss/0.9.rb2
-rw-r--r--lib/rss/1.0.rb2
-rw-r--r--lib/rss/atom.rb261
-rw-r--r--lib/rss/content.rb2
-rw-r--r--lib/rss/converter.rb2
-rw-r--r--lib/rss/dublincore.rb4
-rw-r--r--lib/rss/dublincore/atom.rb2
-rw-r--r--lib/rss/image.rb2
-rw-r--r--lib/rss/itunes.rb2
-rw-r--r--lib/rss/maker.rb26
-rw-r--r--lib/rss/maker/0.9.rb4
-rw-r--r--lib/rss/maker/1.0.rb4
-rw-r--r--lib/rss/maker/2.0.rb4
-rw-r--r--lib/rss/maker/atom.rb4
-rw-r--r--lib/rss/maker/base.rb2
-rw-r--r--lib/rss/maker/content.rb6
-rw-r--r--lib/rss/maker/dublincore.rb4
-rw-r--r--lib/rss/maker/entry.rb4
-rw-r--r--lib/rss/maker/feed.rb2
-rw-r--r--lib/rss/maker/image.rb6
-rw-r--r--lib/rss/maker/itunes.rb12
-rw-r--r--lib/rss/maker/slash.rb4
-rw-r--r--lib/rss/maker/syndication.rb4
-rw-r--r--lib/rss/maker/taxonomy.rb6
-rw-r--r--lib/rss/maker/trackback.rb6
-rw-r--r--lib/rss/parser.rb26
-rw-r--r--lib/rss/rss.gemspec38
-rw-r--r--lib/rss/rss.rb27
-rw-r--r--lib/rss/taxonomy.rb2
-rw-r--r--lib/rss/utils.rb6
-rw-r--r--lib/rss/xml-stylesheet.rb2
-rw-r--r--lib/rss/xml.rb2
-rw-r--r--lib/rubygems.rb300
-rw-r--r--lib/rubygems/LICENSE.txt54
-rw-r--r--lib/rubygems/available_set.rb2
-rw-r--r--lib/rubygems/basic_specification.rb32
-rw-r--r--lib/rubygems/bundler_version_finder.rb99
-rw-r--r--lib/rubygems/command.rb49
-rw-r--r--lib/rubygems/command_manager.rb33
-rw-r--r--lib/rubygems/commands/build_command.rb35
-rw-r--r--lib/rubygems/commands/cert_command.rb82
-rw-r--r--lib/rubygems/commands/check_command.rb2
-rw-r--r--lib/rubygems/commands/cleanup_command.rb34
-rw-r--r--lib/rubygems/commands/contents_command.rb29
-rw-r--r--lib/rubygems/commands/dependency_command.rb34
-rw-r--r--lib/rubygems/commands/environment_command.rb21
-rw-r--r--lib/rubygems/commands/fetch_command.rb5
-rw-r--r--lib/rubygems/commands/generate_index_command.rb7
-rw-r--r--lib/rubygems/commands/help_command.rb27
-rw-r--r--lib/rubygems/commands/info_command.rb33
-rw-r--r--lib/rubygems/commands/install_command.rb45
-rw-r--r--lib/rubygems/commands/list_command.rb1
-rw-r--r--lib/rubygems/commands/lock_command.rb7
-rw-r--r--lib/rubygems/commands/open_command.rb28
-rw-r--r--lib/rubygems/commands/owner_command.rb35
-rw-r--r--lib/rubygems/commands/pristine_command.rb54
-rw-r--r--lib/rubygems/commands/push_command.rb65
-rw-r--r--lib/rubygems/commands/query_command.rb80
-rw-r--r--lib/rubygems/commands/rdoc_command.rb7
-rw-r--r--lib/rubygems/commands/search_command.rb1
-rw-r--r--lib/rubygems/commands/server_command.rb3
-rw-r--r--lib/rubygems/commands/setup_command.rb341
-rw-r--r--lib/rubygems/commands/signin_command.rb34
-rw-r--r--lib/rubygems/commands/signout_command.rb33
-rw-r--r--lib/rubygems/commands/sources_command.rb23
-rw-r--r--lib/rubygems/commands/specification_command.rb14
-rw-r--r--lib/rubygems/commands/uninstall_command.rb67
-rw-r--r--lib/rubygems/commands/unpack_command.rb45
-rw-r--r--lib/rubygems/commands/update_command.rb53
-rw-r--r--lib/rubygems/commands/which_command.rb11
-rw-r--r--lib/rubygems/commands/yank_command.rb18
-rw-r--r--lib/rubygems/compatibility.rb22
-rw-r--r--lib/rubygems/config_file.rb111
-rwxr-xr-xlib/rubygems/core_ext/kernel_require.rb40
-rwxr-xr-xlib/rubygems/core_ext/kernel_warn.rb45
-rw-r--r--lib/rubygems/defaults.rb43
-rw-r--r--lib/rubygems/dependency.rb31
-rw-r--r--lib/rubygems/dependency_installer.rb64
-rw-r--r--lib/rubygems/dependency_list.rb19
-rw-r--r--lib/rubygems/deprecate.rb5
-rw-r--r--lib/rubygems/doctor.rb11
-rw-r--r--lib/rubygems/errors.rb9
-rw-r--r--lib/rubygems/exceptions.rb27
-rw-r--r--lib/rubygems/ext.rb1
-rw-r--r--lib/rubygems/ext/build_error.rb1
-rw-r--r--lib/rubygems/ext/builder.rb77
-rw-r--r--lib/rubygems/ext/cmake_builder.rb4
-rw-r--r--lib/rubygems/ext/configure_builder.rb5
-rw-r--r--lib/rubygems/ext/ext_conf_builder.rb27
-rw-r--r--lib/rubygems/ext/rake_builder.rb34
-rw-r--r--lib/rubygems/gem_runner.rb10
-rw-r--r--lib/rubygems/gemcutter_utilities.rb53
-rw-r--r--lib/rubygems/indexer.rb40
-rw-r--r--lib/rubygems/install_default_message.rb1
-rw-r--r--lib/rubygems/install_message.rb1
-rw-r--r--lib/rubygems/install_update_options.rb67
-rw-r--r--lib/rubygems/installer.rb208
-rw-r--r--lib/rubygems/installer_test_case.rb13
-rw-r--r--lib/rubygems/local_remote_options.rb9
-rw-r--r--lib/rubygems/mock_gem_ui.rb7
-rw-r--r--lib/rubygems/name_tuple.rb8
-rw-r--r--lib/rubygems/package.rb188
-rw-r--r--lib/rubygems/package/digest_io.rb7
-rw-r--r--lib/rubygems/package/file_source.rb11
-rw-r--r--lib/rubygems/package/io_source.rb3
-rw-r--r--lib/rubygems/package/old.rb28
-rw-r--r--lib/rubygems/package/source.rb1
-rw-r--r--lib/rubygems/package/tar_header.rb31
-rw-r--r--lib/rubygems/package/tar_reader.rb6
-rw-r--r--lib/rubygems/package/tar_reader/entry.rb24
-rw-r--r--lib/rubygems/package/tar_test_case.rb10
-rw-r--r--lib/rubygems/package/tar_writer.rb35
-rw-r--r--lib/rubygems/package_task.rb1
-rw-r--r--lib/rubygems/path_support.rb22
-rw-r--r--lib/rubygems/platform.rb11
-rw-r--r--lib/rubygems/psych_tree.rb2
-rw-r--r--lib/rubygems/rdoc.rb311
-rw-r--r--lib/rubygems/remote_fetcher.rb84
-rw-r--r--lib/rubygems/request.rb33
-rw-r--r--lib/rubygems/request/connection_pools.rb37
-rw-r--r--lib/rubygems/request/http_pool.rb7
-rw-r--r--lib/rubygems/request/https_pool.rb4
-rw-r--r--lib/rubygems/request_set.rb141
-rw-r--r--lib/rubygems/request_set/gem_dependency_api.rb82
-rw-r--r--lib/rubygems/request_set/lockfile.rb26
-rw-r--r--lib/rubygems/request_set/lockfile/parser.rb47
-rw-r--r--lib/rubygems/request_set/lockfile/tokenizer.rb20
-rw-r--r--lib/rubygems/requirement.rb66
-rw-r--r--lib/rubygems/resolver.rb89
-rw-r--r--lib/rubygems/resolver/activation_request.rb12
-rw-r--r--lib/rubygems/resolver/api_set.rb11
-rw-r--r--lib/rubygems/resolver/api_specification.rb10
-rw-r--r--lib/rubygems/resolver/best_set.rb11
-rw-r--r--lib/rubygems/resolver/composed_set.rb11
-rw-r--r--lib/rubygems/resolver/conflict.rb10
-rw-r--r--lib/rubygems/resolver/current_set.rb3
-rw-r--r--lib/rubygems/resolver/dependency_request.rb8
-rw-r--r--lib/rubygems/resolver/git_set.rb11
-rw-r--r--lib/rubygems/resolver/git_specification.rb9
-rw-r--r--lib/rubygems/resolver/index_set.rb11
-rw-r--r--lib/rubygems/resolver/index_specification.rb7
-rw-r--r--lib/rubygems/resolver/installed_specification.rb7
-rw-r--r--lib/rubygems/resolver/installer_set.rb32
-rw-r--r--lib/rubygems/resolver/local_specification.rb3
-rw-r--r--lib/rubygems/resolver/lock_set.rb11
-rw-r--r--lib/rubygems/resolver/lock_specification.rb15
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb20
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb11
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb11
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb4
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb2
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb2
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb84
-rw-r--r--lib/rubygems/resolver/requirement_list.rb2
-rw-r--r--lib/rubygems/resolver/set.rb6
-rw-r--r--lib/rubygems/resolver/source_set.rb9
-rw-r--r--lib/rubygems/resolver/spec_specification.rb3
-rw-r--r--lib/rubygems/resolver/specification.rb17
-rw-r--r--lib/rubygems/resolver/stats.rb2
-rw-r--r--lib/rubygems/resolver/vendor_set.rb9
-rw-r--r--lib/rubygems/resolver/vendor_specification.rb5
-rw-r--r--lib/rubygems/safe_yaml.rb59
-rw-r--r--lib/rubygems/security.rb61
-rw-r--r--lib/rubygems/security/policies.rb3
-rw-r--r--lib/rubygems/security/policy.rb50
-rw-r--r--lib/rubygems/security/signer.rb96
-rw-r--r--lib/rubygems/security/trust_dir.rb20
-rw-r--r--lib/rubygems/security_option.rb43
-rw-r--r--lib/rubygems/server.rb95
-rw-r--r--lib/rubygems/source.rb56
-rw-r--r--lib/rubygems/source/git.rb22
-rw-r--r--lib/rubygems/source/installed.rb7
-rw-r--r--lib/rubygems/source/local.rb83
-rw-r--r--lib/rubygems/source/lock.rb13
-rw-r--r--lib/rubygems/source/specific_file.rb10
-rw-r--r--lib/rubygems/source/vendor.rb5
-rw-r--r--lib/rubygems/source_list.rb4
-rw-r--r--lib/rubygems/source_local.rb5
-rw-r--r--lib/rubygems/source_specific_file.rb5
-rw-r--r--lib/rubygems/spec_fetcher.rb13
-rw-r--r--lib/rubygems/specification.rb1133
-rw-r--r--lib/rubygems/specification_policy.rb407
-rw-r--r--lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA.pem21
-rw-r--r--lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA_R3.pem21
-rw-r--r--lib/rubygems/stub_specification.rb40
-rw-r--r--lib/rubygems/test_case.rb268
-rw-r--r--lib/rubygems/test_utilities.rb59
-rw-r--r--lib/rubygems/text.rb27
-rw-r--r--lib/rubygems/uninstaller.rb63
-rw-r--r--lib/rubygems/uri_formatter.rb3
-rw-r--r--lib/rubygems/user_interaction.rb153
-rw-r--r--lib/rubygems/util.rb50
-rw-r--r--lib/rubygems/util/licenses.rb102
-rw-r--r--lib/rubygems/util/list.rb2
-rw-r--r--lib/rubygems/validator.rb15
-rw-r--r--lib/rubygems/version.rb64
-rw-r--r--lib/rubygems/version_option.rb12
-rw-r--r--lib/scanf.gemspec24
-rw-r--r--lib/scanf.rb20
-rw-r--r--lib/securerandom.rb237
-rw-r--r--lib/set.rb337
-rw-r--r--lib/shell.rb1
-rw-r--r--lib/shell/builtin-command.rb2
-rw-r--r--lib/shell/command-processor.rb15
-rw-r--r--lib/shell/filter.rb12
-rw-r--r--lib/shell/process-controller.rb4
-rw-r--r--lib/shell/shell.gemspec26
-rw-r--r--lib/shell/system-command.rb4
-rw-r--r--lib/shell/version.rb3
-rw-r--r--lib/singleton.rb1
-rw-r--r--lib/sync.gemspec27
-rw-r--r--lib/sync.rb7
-rw-r--r--lib/tempfile.rb57
-rw-r--r--lib/thwait.rb3
-rw-r--r--lib/thwait/thwait.gemspec26
-rw-r--r--lib/thwait/version.rb3
-rw-r--r--lib/time.rb230
-rw-r--r--lib/timeout.rb6
-rw-r--r--lib/tmpdir.rb43
-rw-r--r--lib/tracer.rb2
-rw-r--r--lib/tracer/tracer.gemspec26
-rw-r--r--lib/tracer/version.rb5
-rw-r--r--lib/ubygems.rb11
-rw-r--r--lib/un.rb19
-rw-r--r--lib/unicode_normalize.rb79
-rw-r--r--lib/unicode_normalize/normalize.rb22
-rw-r--r--lib/unicode_normalize/tables.rb10112
-rw-r--r--lib/uri.rb46
-rw-r--r--lib/uri/common.rb146
-rw-r--r--lib/uri/file.rb94
-rw-r--r--lib/uri/ftp.rb64
-rw-r--r--lib/uri/generic.rb416
-rw-r--r--lib/uri/http.rb46
-rw-r--r--lib/uri/https.rb2
-rw-r--r--lib/uri/ldap.rb81
-rw-r--r--lib/uri/ldaps.rb2
-rw-r--r--lib/uri/mailto.rb50
-rw-r--r--lib/uri/rfc2396_parser.rb50
-rw-r--r--lib/uri/rfc3986_parser.rb4
-rw-r--r--lib/weakref.rb51
-rw-r--r--lib/webrick/.document6
-rw-r--r--lib/webrick/cgi.rb10
-rw-r--r--lib/webrick/config.rb22
-rw-r--r--lib/webrick/cookie.rb2
-rw-r--r--lib/webrick/httpauth.rb10
-rw-r--r--lib/webrick/httpauth/basicauth.rb18
-rw-r--r--lib/webrick/httpauth/digestauth.rb33
-rw-r--r--lib/webrick/httpauth/htdigest.rb10
-rw-r--r--lib/webrick/httpauth/htgroup.rb15
-rw-r--r--lib/webrick/httpauth/htpasswd.rb47
-rw-r--r--lib/webrick/httpproxy.rb84
-rw-r--r--lib/webrick/httprequest.rb67
-rw-r--r--lib/webrick/httpresponse.rb146
-rw-r--r--lib/webrick/https.rb67
-rw-r--r--lib/webrick/httpserver.rb36
-rw-r--r--lib/webrick/httpservlet.rb10
-rw-r--r--lib/webrick/httpservlet/abstract.rb8
-rw-r--r--lib/webrick/httpservlet/cgi_runner.rb4
-rw-r--r--lib/webrick/httpservlet/cgihandler.rb8
-rw-r--r--lib/webrick/httpservlet/erbhandler.rb4
-rw-r--r--lib/webrick/httpservlet/filehandler.rb80
-rw-r--r--lib/webrick/httpservlet/prochandler.rb2
-rw-r--r--lib/webrick/httpstatus.rb6
-rw-r--r--lib/webrick/httputils.rb3
-rw-r--r--lib/webrick/log.rb6
-rw-r--r--lib/webrick/server.rb84
-rw-r--r--lib/webrick/ssl.rb27
-rw-r--r--lib/webrick/utils.rb3
-rw-r--r--lib/webrick/version.rb2
-rw-r--r--lib/webrick/webrick.gemspec33
-rw-r--r--lib/yaml.rb13
-rwxr-xr-xlibexec/bundle31
-rwxr-xr-xlibexec/bundle_ruby60
-rwxr-xr-xlibexec/bundler4
-rwxr-xr-xlibexec/irb11
-rwxr-xr-xlibexec/rdoc44
-rwxr-xr-xlibexec/ri12
-rw-r--r--load.c228
-rw-r--r--loadpath.c1
-rw-r--r--localeinit.c45
-rw-r--r--main.c6
-rw-r--r--man/bundle-add.158
-rw-r--r--man/bundle-add.1.txt52
-rw-r--r--man/bundle-add.ronn40
-rw-r--r--man/bundle-binstubs.140
-rw-r--r--man/bundle-binstubs.1.txt48
-rw-r--r--man/bundle-binstubs.ronn43
-rw-r--r--man/bundle-check.131
-rw-r--r--man/bundle-check.1.txt33
-rw-r--r--man/bundle-check.ronn26
-rw-r--r--man/bundle-clean.124
-rw-r--r--man/bundle-clean.1.txt26
-rw-r--r--man/bundle-clean.ronn18
-rw-r--r--man/bundle-config.1497
-rw-r--r--man/bundle-config.1.txt529
-rw-r--r--man/bundle-config.ronn397
-rw-r--r--man/bundle-doctor.144
-rw-r--r--man/bundle-doctor.1.txt44
-rw-r--r--man/bundle-doctor.ronn33
-rw-r--r--man/bundle-exec.1165
-rw-r--r--man/bundle-exec.1.txt178
-rw-r--r--man/bundle-exec.ronn152
-rw-r--r--man/bundle-gem.180
-rw-r--r--man/bundle-gem.1.txt91
-rw-r--r--man/bundle-gem.ronn78
-rw-r--r--man/bundle-info.120
-rw-r--r--man/bundle-info.1.txt21
-rw-r--r--man/bundle-info.ronn17
-rw-r--r--man/bundle-init.125
-rw-r--r--man/bundle-init.1.txt34
-rw-r--r--man/bundle-init.ronn29
-rw-r--r--man/bundle-inject.133
-rw-r--r--man/bundle-inject.1.txt32
-rw-r--r--man/bundle-inject.ronn22
-rw-r--r--man/bundle-install.1308
-rw-r--r--man/bundle-install.1.txt396
-rw-r--r--man/bundle-install.ronn378
-rw-r--r--man/bundle-list.150
-rw-r--r--man/bundle-list.1.txt43
-rw-r--r--man/bundle-list.ronn33
-rw-r--r--man/bundle-lock.184
-rw-r--r--man/bundle-lock.1.txt93
-rw-r--r--man/bundle-lock.ronn94
-rw-r--r--man/bundle-open.132
-rw-r--r--man/bundle-open.1.txt29
-rw-r--r--man/bundle-open.ronn19
-rw-r--r--man/bundle-outdated.1155
-rw-r--r--man/bundle-outdated.1.txt131
-rw-r--r--man/bundle-outdated.ronn111
-rw-r--r--man/bundle-package.155
-rw-r--r--man/bundle-package.1.txt79
-rw-r--r--man/bundle-package.ronn72
-rw-r--r--man/bundle-platform.161
-rw-r--r--man/bundle-platform.1.txt57
-rw-r--r--man/bundle-platform.ronn42
-rw-r--r--man/bundle-pristine.134
-rw-r--r--man/bundle-pristine.1.txt44
-rw-r--r--man/bundle-pristine.ronn34
-rw-r--r--man/bundle-remove.131
-rw-r--r--man/bundle-remove.1.txt34
-rw-r--r--man/bundle-remove.ronn23
-rw-r--r--man/bundle-show.123
-rw-r--r--man/bundle-show.1.txt27
-rw-r--r--man/bundle-show.ronn21
-rw-r--r--man/bundle-update.1394
-rw-r--r--man/bundle-update.1.txt391
-rw-r--r--man/bundle-update.ronn350
-rw-r--r--man/bundle-viz.139
-rw-r--r--man/bundle-viz.1.txt39
-rw-r--r--man/bundle-viz.ronn30
-rw-r--r--man/bundle.1132
-rw-r--r--man/bundle.1.txt113
-rw-r--r--man/bundle.ronn108
-rw-r--r--man/erb.121
-rw-r--r--man/gemfile.5689
-rw-r--r--man/gemfile.5.ronn521
-rw-r--r--man/gemfile.5.txt653
-rw-r--r--man/goruby.14
-rw-r--r--man/irb.117
-rw-r--r--man/ri.1240
-rw-r--r--man/ruby.154
-rw-r--r--marshal.c183
-rw-r--r--math.c151
-rw-r--r--method.h70
-rw-r--r--misc/README18
-rw-r--r--misc/inf-ruby.el418
-rwxr-xr-xmisc/lldb_cruby.py257
-rw-r--r--misc/rdoc-mode.el166
-rw-r--r--misc/ruby-additional.el181
-rw-r--r--misc/ruby-electric.el569
-rw-r--r--misc/ruby-mode.el1584
-rw-r--r--misc/ruby-style.el17
-rw-r--r--misc/rubydb2x.el104
-rw-r--r--misc/rubydb3x.el115
-rw-r--r--misc/test_lldb_cruby.rb36
-rw-r--r--missing/flock.c2
-rw-r--r--missing/nan.c28
-rw-r--r--missing/stdbool.h20
-rw-r--r--missing/strerror.c2
-rw-r--r--missing/strtol.c27
-rw-r--r--missing/tgamma.c55
-rw-r--r--mjit.c887
-rw-r--r--mjit.h148
-rw-r--r--mjit_compile.c254
-rw-r--r--mjit_worker.c1261
-rw-r--r--nacl/GNUmakefile.in100
-rw-r--r--nacl/README.nacl51
-rw-r--r--nacl/create_nmf.rb70
-rw-r--r--nacl/dirent.h15
-rw-r--r--nacl/example.html150
-rwxr-xr-xnacl/nacl-config.rb61
-rw-r--r--nacl/package.rb113
-rw-r--r--nacl/pepper_main.c732
-rw-r--r--nacl/resource.h8
-rw-r--r--nacl/select.h7
-rw-r--r--nacl/signal.h6
-rw-r--r--nacl/stat.h10
-rw-r--r--nacl/unistd.h9
-rw-r--r--nacl/utime.h11
-rw-r--r--node.c720
-rw-r--r--node.h447
-rw-r--r--numeric.c1770
-rw-r--r--object.c1331
-rw-r--r--pack.c456
-rw-r--r--parse.y9003
-rw-r--r--prelude.rb53
-rw-r--r--probes_helper.h23
-rw-r--r--proc.c979
-rw-r--r--process.c1430
-rw-r--r--random.c208
-rw-r--r--range.c588
-rw-r--r--rational.c1198
-rw-r--r--re.c351
-rw-r--r--regcomp.c698
-rw-r--r--regenc.c71
-rw-r--r--regenc.h141
-rw-r--r--regerror.c70
-rw-r--r--regexec.c1088
-rw-r--r--regint.h431
-rw-r--r--regparse.c1367
-rw-r--r--regparse.h59
-rw-r--r--regsyntax.c15
-rw-r--r--ruby-runner.c93
-rw-r--r--ruby.c618
-rw-r--r--ruby_assert.h6
-rw-r--r--ruby_atomic.h17
-rw-r--r--safe.c37
-rw-r--r--sample/cbreak.rb8
-rw-r--r--sample/dir.rb2
-rw-r--r--sample/drb/dchats.rb1
-rw-r--r--sample/drb/dhasen.rb1
-rw-r--r--sample/drb/dlogd.rb1
-rw-r--r--sample/drb/dqueue.rb1
-rw-r--r--sample/drb/http0serv.rb1
-rw-r--r--sample/drb/name.rb1
-rw-r--r--sample/drb/old_tuplespace.rb2
-rw-r--r--sample/drb/ring_echo.rb1
-rw-r--r--sample/drb/simpletuple.rb2
-rw-r--r--sample/dualstack-httpd.rb1
-rw-r--r--sample/fib.py2
-rw-r--r--sample/iseq_loader.rb6
-rw-r--r--sample/observ.rb1
-rw-r--r--sample/philos.rb1
-rw-r--r--sample/pty/expect_sample.rb14
-rw-r--r--sample/pty/script.rb2
-rw-r--r--sample/pty/shl.rb47
-rw-r--r--sample/ripper/ruby2html.rb6
-rw-r--r--sample/timeout.rb18
-rw-r--r--sample/trick2013/kinaba/remarks.markdown2
-rw-r--r--sample/trick2013/mame/music-box.mp4bin580724 -> 0 bytes-rw-r--r--sample/trick2013/yhara/entry.rb2
-rw-r--r--sample/trick2015/ksk_1/remarks.markdown2
-rw-r--r--sample/trick2018/01-kinaba/authors.markdown3
-rw-r--r--sample/trick2018/01-kinaba/entry.rb8
-rw-r--r--sample/trick2018/01-kinaba/remarks.markdown55
-rw-r--r--sample/trick2018/02-mame/authors.markdown3
-rw-r--r--sample/trick2018/02-mame/entry.rb15
-rw-r--r--sample/trick2018/02-mame/remarks.markdown16
-rw-r--r--sample/trick2018/03-tompng/Gemfile2
-rw-r--r--sample/trick2018/03-tompng/Gemfile.lock13
-rw-r--r--sample/trick2018/03-tompng/authors.markdown3
-rw-r--r--sample/trick2018/03-tompng/entry.rb31
-rw-r--r--sample/trick2018/03-tompng/output.txt44
-rw-r--r--sample/trick2018/03-tompng/remarks.markdown19
-rw-r--r--sample/trick2018/03-tompng/trick.pngbin0 -> 5661 bytes-rw-r--r--sample/trick2018/04-colin/authors.markdown3
-rw-r--r--sample/trick2018/04-colin/entry.rb2
-rw-r--r--sample/trick2018/04-colin/remarks.markdown62
-rw-r--r--sample/trick2018/05-tompng/authors.markdown3
-rw-r--r--sample/trick2018/05-tompng/entry.rb41
-rw-r--r--sample/trick2018/05-tompng/preview_of_output.pngbin0 -> 66800 bytes-rw-r--r--sample/trick2018/05-tompng/remarks.markdown31
-rw-r--r--sample/trick2018/README.md16
-rw-r--r--signal.c508
-rw-r--r--siphash.c12
-rw-r--r--siphash.h2
-rw-r--r--sparc.c2
-rw-r--r--spec/README31
-rw-r--r--spec/README.md100
-rw-r--r--spec/bundler/bundler/bundler_spec.rb490
-rw-r--r--spec/bundler/bundler/cli_spec.rb173
-rw-r--r--spec/bundler/bundler/compact_index_client/updater_spec.rb55
-rw-r--r--spec/bundler/bundler/definition_spec.rb360
-rw-r--r--spec/bundler/bundler/dep_proxy_spec.rb22
-rw-r--r--spec/bundler/bundler/dsl_spec.rb305
-rw-r--r--spec/bundler/bundler/endpoint_specification_spec.rb71
-rw-r--r--spec/bundler/bundler/env_spec.rb151
-rw-r--r--spec/bundler/bundler/environment_preserver_spec.rb79
-rw-r--r--spec/bundler/bundler/fetcher/base_spec.rb76
-rw-r--r--spec/bundler/bundler/fetcher/compact_index_spec.rb103
-rw-r--r--spec/bundler/bundler/fetcher/dependency_spec.rb287
-rw-r--r--spec/bundler/bundler/fetcher/downloader_spec.rb250
-rw-r--r--spec/bundler/bundler/fetcher/index_spec.rb99
-rw-r--r--spec/bundler/bundler/fetcher_spec.rb161
-rw-r--r--spec/bundler/bundler/friendly_errors_spec.rb270
-rw-r--r--spec/bundler/bundler/gem_helper_spec.rb351
-rw-r--r--spec/bundler/bundler/gem_version_promoter_spec.rb179
-rw-r--r--spec/bundler/bundler/index_spec.rb36
-rw-r--r--spec/bundler/bundler/installer/gem_installer_spec.rb29
-rw-r--r--spec/bundler/bundler/installer/parallel_installer_spec.rb47
-rw-r--r--spec/bundler/bundler/installer/spec_installation_spec.rb62
-rw-r--r--spec/bundler/bundler/lockfile_parser_spec.rb153
-rw-r--r--spec/bundler/bundler/mirror_spec.rb329
-rw-r--r--spec/bundler/bundler/plugin/api/source_spec.rb82
-rw-r--r--spec/bundler/bundler/plugin/api_spec.rb83
-rw-r--r--spec/bundler/bundler/plugin/dsl_spec.rb38
-rw-r--r--spec/bundler/bundler/plugin/events_spec.rb18
-rw-r--r--spec/bundler/bundler/plugin/index_spec.rb186
-rw-r--r--spec/bundler/bundler/plugin/installer_spec.rb104
-rw-r--r--spec/bundler/bundler/plugin/source_list_spec.rb25
-rw-r--r--spec/bundler/bundler/plugin_spec.rb309
-rw-r--r--spec/bundler/bundler/psyched_yaml_spec.rb9
-rw-r--r--spec/bundler/bundler/remote_specification_spec.rb187
-rw-r--r--spec/bundler/bundler/retry_spec.rb81
-rw-r--r--spec/bundler/bundler/ruby_dsl_spec.rb95
-rw-r--r--spec/bundler/bundler/ruby_version_spec.rb524
-rw-r--r--spec/bundler/bundler/rubygems_integration_spec.rb114
-rw-r--r--spec/bundler/bundler/settings/validator_spec.rb111
-rw-r--r--spec/bundler/bundler/settings_spec.rb326
-rw-r--r--spec/bundler/bundler/shared_helpers_spec.rb513
-rw-r--r--spec/bundler/bundler/source/git/git_proxy_spec.rb140
-rw-r--r--spec/bundler/bundler/source/git_spec.rb28
-rw-r--r--spec/bundler/bundler/source/path_spec.rb31
-rw-r--r--spec/bundler/bundler/source/rubygems/remote_spec.rb162
-rw-r--r--spec/bundler/bundler/source/rubygems_spec.rb33
-rw-r--r--spec/bundler/bundler/source_list_spec.rb463
-rw-r--r--spec/bundler/bundler/source_spec.rb154
-rw-r--r--spec/bundler/bundler/spec_set_spec.rb77
-rw-r--r--spec/bundler/bundler/ssl_certs/certificate_manager_spec.rb140
-rw-r--r--spec/bundler/bundler/stub_specification_spec.rb24
-rw-r--r--spec/bundler/bundler/ui/shell_spec.rb73
-rw-r--r--spec/bundler/bundler/ui_spec.rb41
-rw-r--r--spec/bundler/bundler/uri_credentials_filter_spec.rb127
-rw-r--r--spec/bundler/bundler/vendored_persistent_spec.rb78
-rw-r--r--spec/bundler/bundler/version_ranges_spec.rb37
-rw-r--r--spec/bundler/bundler/worker_spec.rb22
-rw-r--r--spec/bundler/bundler/yaml_serializer_spec.rb194
-rw-r--r--spec/bundler/cache/cache_path_spec.rb32
-rw-r--r--spec/bundler/cache/gems_spec.rb304
-rw-r--r--spec/bundler/cache/git_spec.rb214
-rw-r--r--spec/bundler/cache/path_spec.rb139
-rw-r--r--spec/bundler/cache/platform_spec.rb49
-rw-r--r--spec/bundler/commands/add_spec.rb217
-rw-r--r--spec/bundler/commands/binstubs_spec.rb453
-rw-r--r--spec/bundler/commands/check_spec.rb354
-rw-r--r--spec/bundler/commands/clean_spec.rb771
-rw-r--r--spec/bundler/commands/config_spec.rb384
-rw-r--r--spec/bundler/commands/console_spec.rb106
-rw-r--r--spec/bundler/commands/doctor_spec.rb110
-rw-r--r--spec/bundler/commands/exec_spec.rb858
-rw-r--r--spec/bundler/commands/help_spec.rb98
-rw-r--r--spec/bundler/commands/info_spec.rb57
-rw-r--r--spec/bundler/commands/init_spec.rb181
-rw-r--r--spec/bundler/commands/inject_spec.rb117
-rw-r--r--spec/bundler/commands/install_spec.rb587
-rw-r--r--spec/bundler/commands/issue_spec.rb16
-rw-r--r--spec/bundler/commands/licenses_spec.rb38
-rw-r--r--spec/bundler/commands/list_spec.rb131
-rw-r--r--spec/bundler/commands/lock_spec.rb322
-rw-r--r--spec/bundler/commands/newgem_spec.rb912
-rw-r--r--spec/bundler/commands/open_spec.rb92
-rw-r--r--spec/bundler/commands/outdated_spec.rb782
-rw-r--r--spec/bundler/commands/package_spec.rb306
-rw-r--r--spec/bundler/commands/pristine_spec.rb192
-rw-r--r--spec/bundler/commands/remove_spec.rb583
-rw-r--r--spec/bundler/commands/show_spec.rb244
-rw-r--r--spec/bundler/commands/update_spec.rb943
-rw-r--r--spec/bundler/commands/version_spec.rb39
-rw-r--r--spec/bundler/commands/viz_spec.rb149
-rw-r--r--spec/bundler/install/allow_offline_install_spec.rb92
-rw-r--r--spec/bundler/install/binstubs_spec.rb43
-rw-r--r--spec/bundler/install/bundler_spec.rb177
-rw-r--r--spec/bundler/install/deploy_spec.rb423
-rw-r--r--spec/bundler/install/failure_spec.rb125
-rw-r--r--spec/bundler/install/gemfile/eval_gemfile_spec.rb82
-rw-r--r--spec/bundler/install/gemfile/gemspec_spec.rb672
-rw-r--r--spec/bundler/install/gemfile/git_spec.rb1351
-rw-r--r--spec/bundler/install/gemfile/groups_spec.rb384
-rw-r--r--spec/bundler/install/gemfile/install_if.rb44
-rw-r--r--spec/bundler/install/gemfile/lockfile_spec.rb48
-rw-r--r--spec/bundler/install/gemfile/path_spec.rb630
-rw-r--r--spec/bundler/install/gemfile/platform_spec.rb426
-rw-r--r--spec/bundler/install/gemfile/ruby_spec.rb108
-rw-r--r--spec/bundler/install/gemfile/sources_spec.rb619
-rw-r--r--spec/bundler/install/gemfile/specific_platform_spec.rb114
-rw-r--r--spec/bundler/install/gemfile_spec.rb145
-rw-r--r--spec/bundler/install/gems/compact_index_spec.rb940
-rw-r--r--spec/bundler/install/gems/dependency_api_spec.rb760
-rw-r--r--spec/bundler/install/gems/env_spec.rb107
-rw-r--r--spec/bundler/install/gems/flex_spec.rb351
-rw-r--r--spec/bundler/install/gems/mirror_spec.rb39
-rw-r--r--spec/bundler/install/gems/native_extensions_spec.rb90
-rw-r--r--spec/bundler/install/gems/post_install_spec.rb150
-rw-r--r--spec/bundler/install/gems/resolving_spec.rb195
-rw-r--r--spec/bundler/install/gems/standalone_spec.rb337
-rw-r--r--spec/bundler/install/gems/sudo_spec.rb178
-rw-r--r--spec/bundler/install/gems/win32_spec.rb26
-rw-r--r--spec/bundler/install/gemspecs_spec.rb154
-rw-r--r--spec/bundler/install/git_spec.rb65
-rw-r--r--spec/bundler/install/global_cache_spec.rb235
-rw-r--r--spec/bundler/install/path_spec.rb256
-rw-r--r--spec/bundler/install/post_bundle_message_spec.rb206
-rw-r--r--spec/bundler/install/prereleases_spec.rb41
-rw-r--r--spec/bundler/install/process_lock_spec.rb35
-rw-r--r--spec/bundler/install/redownload_spec.rb92
-rw-r--r--spec/bundler/install/security_policy_spec.rb77
-rw-r--r--spec/bundler/install/yanked_spec.rb71
-rw-r--r--spec/bundler/lock/git_spec.rb34
-rw-r--r--spec/bundler/lock/lockfile_bundler_1_spec.rb1386
-rw-r--r--spec/bundler/lock/lockfile_spec.rb1425
-rw-r--r--spec/bundler/other/bundle_ruby_spec.rb155
-rw-r--r--spec/bundler/other/cli_dispatch_spec.rb29
-rw-r--r--spec/bundler/other/compatibility_guard_spec.rb25
-rw-r--r--spec/bundler/other/ext_spec.rb66
-rw-r--r--spec/bundler/other/major_deprecation_spec.rb282
-rw-r--r--spec/bundler/other/platform_spec.rb1312
-rw-r--r--spec/bundler/other/ssl_cert_spec.rb18
-rw-r--r--spec/bundler/plugins/command_spec.rb80
-rw-r--r--spec/bundler/plugins/hook_spec.rb109
-rw-r--r--spec/bundler/plugins/install_spec.rb257
-rw-r--r--spec/bundler/plugins/source/example_spec.rb505
-rw-r--r--spec/bundler/plugins/source_spec.rb108
-rw-r--r--spec/bundler/quality_spec.rb266
-rw-r--r--spec/bundler/realworld/dependency_api_spec.rb44
-rw-r--r--spec/bundler/realworld/double_check_spec.rb40
-rw-r--r--spec/bundler/realworld/edgecases_spec.rb382
-rw-r--r--spec/bundler/realworld/gemfile_source_header_spec.rb53
-rw-r--r--spec/bundler/realworld/mirror_probe_spec.rb144
-rw-r--r--spec/bundler/realworld/parallel_spec.rb74
-rw-r--r--spec/bundler/resolver/basic_spec.rb308
-rw-r--r--spec/bundler/resolver/platform_spec.rb100
-rw-r--r--spec/bundler/runtime/executable_spec.rb190
-rw-r--r--spec/bundler/runtime/gem_tasks_spec.rb44
-rw-r--r--spec/bundler/runtime/inline_spec.rb266
-rw-r--r--spec/bundler/runtime/load_spec.rb111
-rw-r--r--spec/bundler/runtime/platform_spec.rb150
-rw-r--r--spec/bundler/runtime/require_spec.rb452
-rw-r--r--spec/bundler/runtime/setup_spec.rb1445
-rw-r--r--spec/bundler/runtime/with_clean_env_spec.rb151
-rw-r--r--spec/bundler/spec_helper.rb170
-rw-r--r--spec/bundler/support/artifice/compact_index.rb122
-rw-r--r--spec/bundler/support/artifice/compact_index_api_missing.rb18
-rw-r--r--spec/bundler/support/artifice/compact_index_basic_authentication.rb15
-rw-r--r--spec/bundler/support/artifice/compact_index_checksum_mismatch.rb16
-rw-r--r--spec/bundler/support/artifice/compact_index_concurrent_download.rb32
-rw-r--r--spec/bundler/support/artifice/compact_index_creds_diff_host.rb39
-rw-r--r--spec/bundler/support/artifice/compact_index_extra.rb37
-rw-r--r--spec/bundler/support/artifice/compact_index_extra_api.rb52
-rw-r--r--spec/bundler/support/artifice/compact_index_extra_api_missing.rb17
-rw-r--r--spec/bundler/support/artifice/compact_index_extra_missing.rb17
-rw-r--r--spec/bundler/support/artifice/compact_index_forbidden.rb13
-rw-r--r--spec/bundler/support/artifice/compact_index_host_redirect.rb21
-rw-r--r--spec/bundler/support/artifice/compact_index_no_gem.rb13
-rw-r--r--spec/bundler/support/artifice/compact_index_partial_update.rb38
-rw-r--r--spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb34
-rw-r--r--spec/bundler/support/artifice/compact_index_redirects.rb21
-rw-r--r--spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb20
-rw-r--r--spec/bundler/support/artifice/compact_index_wrong_dependencies.rb17
-rw-r--r--spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb20
-rw-r--r--spec/bundler/support/artifice/endopint_marshal_fail_basic_authentication.rb15
-rw-r--r--spec/bundler/support/artifice/endpoint.rb100
-rw-r--r--spec/bundler/support/artifice/endpoint_500.rb19
-rw-r--r--spec/bundler/support/artifice/endpoint_api_forbidden.rb13
-rw-r--r--spec/bundler/support/artifice/endpoint_api_missing.rb18
-rw-r--r--spec/bundler/support/artifice/endpoint_basic_authentication.rb15
-rw-r--r--spec/bundler/support/artifice/endpoint_creds_diff_host.rb39
-rw-r--r--spec/bundler/support/artifice/endpoint_extra.rb33
-rw-r--r--spec/bundler/support/artifice/endpoint_extra_api.rb34
-rw-r--r--spec/bundler/support/artifice/endpoint_extra_missing.rb17
-rw-r--r--spec/bundler/support/artifice/endpoint_fallback.rb19
-rw-r--r--spec/bundler/support/artifice/endpoint_host_redirect.rb17
-rw-r--r--spec/bundler/support/artifice/endpoint_marshal_fail.rb13
-rw-r--r--spec/bundler/support/artifice/endpoint_mirror_source.rb15
-rw-r--r--spec/bundler/support/artifice/endpoint_redirect.rb17
-rw-r--r--spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb20
-rw-r--r--spec/bundler/support/artifice/endpoint_timeout.rb15
-rw-r--r--spec/bundler/support/artifice/fail.rb39
-rw-r--r--spec/bundler/support/artifice/vcr.rb158
-rw-r--r--spec/bundler/support/artifice/windows.rb49
-rw-r--r--spec/bundler/support/builders.rb819
-rw-r--r--spec/bundler/support/code_climate.rb26
-rw-r--r--spec/bundler/support/command_execution.rb57
-rw-r--r--spec/bundler/support/hax.rb67
-rw-r--r--spec/bundler/support/helpers.rb600
-rw-r--r--spec/bundler/support/indexes.rb421
-rw-r--r--spec/bundler/support/less_than_proc.rb20
-rw-r--r--spec/bundler/support/manpages.rb14
-rw-r--r--spec/bundler/support/matchers.rb246
-rw-r--r--spec/bundler/support/path.rb126
-rw-r--r--spec/bundler/support/permissions.rb12
-rw-r--r--spec/bundler/support/platforms.rb116
-rw-r--r--spec/bundler/support/rubygems_ext.rb71
-rw-r--r--spec/bundler/support/silent_logger.rb10
-rw-r--r--spec/bundler/support/sometimes.rb57
-rw-r--r--spec/bundler/support/streams.rb19
-rw-r--r--spec/bundler/support/sudo.rb18
-rw-r--r--spec/bundler/support/the_bundle.rb37
-rw-r--r--spec/bundler/update/gemfile_spec.rb66
-rw-r--r--spec/bundler/update/gems/post_install_spec.rb76
-rw-r--r--spec/bundler/update/git_spec.rb374
-rw-r--r--spec/bundler/update/path_spec.rb18
-rw-r--r--spec/bundler/update/redownload_spec.rb36
-rw-r--r--spec/default.mspec53
-rw-r--r--spec/mspec/Gemfile4
-rw-r--r--spec/mspec/Gemfile.lock24
-rw-r--r--spec/mspec/LICENSE22
-rw-r--r--spec/mspec/README.md91
-rw-r--r--spec/mspec/Rakefile6
-rwxr-xr-xspec/mspec/bin/mkspec7
-rwxr-xr-xspec/mspec/bin/mkspec.bat1
-rwxr-xr-xspec/mspec/bin/mspec7
-rwxr-xr-xspec/mspec/bin/mspec-ci7
-rwxr-xr-xspec/mspec/bin/mspec-ci.bat1
-rwxr-xr-xspec/mspec/bin/mspec-run7
-rwxr-xr-xspec/mspec/bin/mspec-run.bat1
-rwxr-xr-xspec/mspec/bin/mspec-tag7
-rwxr-xr-xspec/mspec/bin/mspec-tag.bat1
-rwxr-xr-xspec/mspec/bin/mspec.bat1
-rw-r--r--spec/mspec/lib/mspec.rb20
-rwxr-xr-xspec/mspec/lib/mspec/commands/mkspec.rb155
-rw-r--r--spec/mspec/lib/mspec/commands/mspec-ci.rb78
-rw-r--r--spec/mspec/lib/mspec/commands/mspec-run.rb86
-rw-r--r--spec/mspec/lib/mspec/commands/mspec-tag.rb132
-rwxr-xr-xspec/mspec/lib/mspec/commands/mspec.rb119
-rw-r--r--spec/mspec/lib/mspec/expectations.rb2
-rw-r--r--spec/mspec/lib/mspec/expectations/expectations.rb21
-rw-r--r--spec/mspec/lib/mspec/expectations/should.rb29
-rw-r--r--spec/mspec/lib/mspec/guards.rb11
-rw-r--r--spec/mspec/lib/mspec/guards/block_device.rb16
-rw-r--r--spec/mspec/lib/mspec/guards/bug.rb28
-rw-r--r--spec/mspec/lib/mspec/guards/conflict.rb23
-rw-r--r--spec/mspec/lib/mspec/guards/endian.rb25
-rw-r--r--spec/mspec/lib/mspec/guards/feature.rb45
-rw-r--r--spec/mspec/lib/mspec/guards/guard.rb141
-rw-r--r--spec/mspec/lib/mspec/guards/platform.rb91
-rw-r--r--spec/mspec/lib/mspec/guards/quarantine.rb11
-rw-r--r--spec/mspec/lib/mspec/guards/superuser.rb15
-rw-r--r--spec/mspec/lib/mspec/guards/support.rb14
-rw-r--r--spec/mspec/lib/mspec/guards/version.rb37
-rw-r--r--spec/mspec/lib/mspec/helpers.rb14
-rw-r--r--spec/mspec/lib/mspec/helpers/argf.rb35
-rw-r--r--spec/mspec/lib/mspec/helpers/argv.rb44
-rw-r--r--spec/mspec/lib/mspec/helpers/datetime.rb47
-rw-r--r--spec/mspec/lib/mspec/helpers/fixture.rb24
-rw-r--r--spec/mspec/lib/mspec/helpers/flunk.rb3
-rw-r--r--spec/mspec/lib/mspec/helpers/frozen_error_class.rb17
-rw-r--r--spec/mspec/lib/mspec/helpers/fs.rb64
-rw-r--r--spec/mspec/lib/mspec/helpers/io.rb111
-rw-r--r--spec/mspec/lib/mspec/helpers/mock_to_path.rb6
-rw-r--r--spec/mspec/lib/mspec/helpers/numeric.rb70
-rw-r--r--spec/mspec/lib/mspec/helpers/ruby_exe.rb186
-rw-r--r--spec/mspec/lib/mspec/helpers/scratch.rb17
-rw-r--r--spec/mspec/lib/mspec/helpers/tmp.rb43
-rw-r--r--spec/mspec/lib/mspec/helpers/warning.rb7
-rw-r--r--spec/mspec/lib/mspec/matchers.rb36
-rw-r--r--spec/mspec/lib/mspec/matchers/base.rb107
-rw-r--r--spec/mspec/lib/mspec/matchers/be_an_instance_of.rb26
-rw-r--r--spec/mspec/lib/mspec/matchers/be_ancestor_of.rb24
-rw-r--r--spec/mspec/lib/mspec/matchers/be_close.rb27
-rw-r--r--spec/mspec/lib/mspec/matchers/be_computed_by.rb37
-rw-r--r--spec/mspec/lib/mspec/matchers/be_empty.rb20
-rw-r--r--spec/mspec/lib/mspec/matchers/be_false.rb20
-rw-r--r--spec/mspec/lib/mspec/matchers/be_kind_of.rb24
-rw-r--r--spec/mspec/lib/mspec/matchers/be_nan.rb20
-rw-r--r--spec/mspec/lib/mspec/matchers/be_nil.rb20
-rw-r--r--spec/mspec/lib/mspec/matchers/be_true.rb20
-rw-r--r--spec/mspec/lib/mspec/matchers/be_true_or_false.rb20
-rw-r--r--spec/mspec/lib/mspec/matchers/block_caller.rb37
-rw-r--r--spec/mspec/lib/mspec/matchers/complain.rb60
-rw-r--r--spec/mspec/lib/mspec/matchers/eql.rb26
-rw-r--r--spec/mspec/lib/mspec/matchers/equal.rb26
-rw-r--r--spec/mspec/lib/mspec/matchers/equal_element.rb78
-rw-r--r--spec/mspec/lib/mspec/matchers/have_class_variable.rb12
-rw-r--r--spec/mspec/lib/mspec/matchers/have_constant.rb12
-rw-r--r--spec/mspec/lib/mspec/matchers/have_instance_method.rb24
-rw-r--r--spec/mspec/lib/mspec/matchers/have_instance_variable.rb12
-rw-r--r--spec/mspec/lib/mspec/matchers/have_method.rb24
-rw-r--r--spec/mspec/lib/mspec/matchers/have_private_instance_method.rb24
-rw-r--r--spec/mspec/lib/mspec/matchers/have_private_method.rb24
-rw-r--r--spec/mspec/lib/mspec/matchers/have_protected_instance_method.rb24
-rw-r--r--spec/mspec/lib/mspec/matchers/have_public_instance_method.rb24
-rw-r--r--spec/mspec/lib/mspec/matchers/have_singleton_method.rb24
-rw-r--r--spec/mspec/lib/mspec/matchers/include.rb31
-rw-r--r--spec/mspec/lib/mspec/matchers/include_any_of.rb29
-rw-r--r--spec/mspec/lib/mspec/matchers/infinity.rb28
-rw-r--r--spec/mspec/lib/mspec/matchers/match_yaml.rb46
-rw-r--r--spec/mspec/lib/mspec/matchers/method.rb10
-rw-r--r--spec/mspec/lib/mspec/matchers/output.rb67
-rw-r--r--spec/mspec/lib/mspec/matchers/output_to_fd.rb71
-rw-r--r--spec/mspec/lib/mspec/matchers/raise_error.rb85
-rw-r--r--spec/mspec/lib/mspec/matchers/respond_to.rb24
-rw-r--r--spec/mspec/lib/mspec/matchers/signed_zero.rb28
-rw-r--r--spec/mspec/lib/mspec/matchers/variable.rb24
-rw-r--r--spec/mspec/lib/mspec/mocks.rb3
-rw-r--r--spec/mspec/lib/mspec/mocks/mock.rb212
-rw-r--r--spec/mspec/lib/mspec/mocks/object.rb28
-rw-r--r--spec/mspec/lib/mspec/mocks/proxy.rb186
-rw-r--r--spec/mspec/lib/mspec/runner.rb12
-rw-r--r--spec/mspec/lib/mspec/runner/actions.rb6
-rw-r--r--spec/mspec/lib/mspec/runner/actions/filter.rb40
-rw-r--r--spec/mspec/lib/mspec/runner/actions/leakchecker.rb301
-rw-r--r--spec/mspec/lib/mspec/runner/actions/tag.rb133
-rw-r--r--spec/mspec/lib/mspec/runner/actions/taglist.rb56
-rw-r--r--spec/mspec/lib/mspec/runner/actions/tagpurge.rb56
-rw-r--r--spec/mspec/lib/mspec/runner/actions/tally.rb133
-rw-r--r--spec/mspec/lib/mspec/runner/actions/timer.rb22
-rw-r--r--spec/mspec/lib/mspec/runner/context.rb239
-rw-r--r--spec/mspec/lib/mspec/runner/evaluate.rb54
-rw-r--r--spec/mspec/lib/mspec/runner/example.rb34
-rw-r--r--spec/mspec/lib/mspec/runner/exception.rb43
-rw-r--r--spec/mspec/lib/mspec/runner/filters.rb4
-rw-r--r--spec/mspec/lib/mspec/runner/filters/match.rb18
-rw-r--r--spec/mspec/lib/mspec/runner/filters/profile.rb54
-rw-r--r--spec/mspec/lib/mspec/runner/filters/regexp.rb23
-rw-r--r--spec/mspec/lib/mspec/runner/filters/tag.rb29
-rw-r--r--spec/mspec/lib/mspec/runner/formatters.rb12
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/describe.rb24
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/dotted.rb117
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/file.rb19
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/html.rb81
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/junit.rb88
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/method.rb93
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/multi.rb40
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/profile.rb70
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/specdoc.rb41
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/spinner.rb117
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/summary.rb11
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/unit.rb21
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/yaml.rb42
-rw-r--r--spec/mspec/lib/mspec/runner/mspec.rb408
-rw-r--r--spec/mspec/lib/mspec/runner/object.rb26
-rw-r--r--spec/mspec/lib/mspec/runner/parallel.rb98
-rw-r--r--spec/mspec/lib/mspec/runner/shared.rb10
-rw-r--r--spec/mspec/lib/mspec/runner/tag.rb38
-rw-r--r--spec/mspec/lib/mspec/utils/deprecate.rb6
-rw-r--r--spec/mspec/lib/mspec/utils/name_map.rb121
-rw-r--r--spec/mspec/lib/mspec/utils/options.rb481
-rw-r--r--spec/mspec/lib/mspec/utils/script.rb278
-rw-r--r--spec/mspec/lib/mspec/utils/version.rb52
-rw-r--r--spec/mspec/lib/mspec/utils/warnings.rb61
-rw-r--r--spec/mspec/lib/mspec/version.rb5
-rw-r--r--spec/mspec/spec/commands/fixtures/four.txt0
-rw-r--r--spec/mspec/spec/commands/fixtures/level2/three_spec.rb1
-rw-r--r--spec/mspec/spec/commands/fixtures/one_spec.rb1
-rw-r--r--spec/mspec/spec/commands/fixtures/three.rb1
-rw-r--r--spec/mspec/spec/commands/fixtures/two_spec.rb1
-rw-r--r--spec/mspec/spec/commands/mkspec_spec.rb363
-rw-r--r--spec/mspec/spec/commands/mspec_ci_spec.rb150
-rw-r--r--spec/mspec/spec/commands/mspec_run_spec.rb173
-rw-r--r--spec/mspec/spec/commands/mspec_spec.rb207
-rw-r--r--spec/mspec/spec/commands/mspec_tag_spec.rb414
-rw-r--r--spec/mspec/spec/expectations/expectations_spec.rb29
-rw-r--r--spec/mspec/spec/expectations/should.rb73
-rw-r--r--spec/mspec/spec/expectations/should_spec.rb61
-rw-r--r--spec/mspec/spec/fixtures/a_spec.rb15
-rw-r--r--spec/mspec/spec/fixtures/b_spec.rb7
-rw-r--r--spec/mspec/spec/fixtures/chatty_spec.rb8
-rw-r--r--spec/mspec/spec/fixtures/config.mspec10
-rw-r--r--spec/mspec/spec/fixtures/die_spec.rb7
-rwxr-xr-xspec/mspec/spec/fixtures/my_ruby4
-rw-r--r--spec/mspec/spec/fixtures/object_methods_spec.rb8
-rw-r--r--spec/mspec/spec/fixtures/print_interpreter_spec.rb4
-rw-r--r--spec/mspec/spec/fixtures/tagging_spec.rb16
-rw-r--r--spec/mspec/spec/guards/block_device_spec.rb46
-rw-r--r--spec/mspec/spec/guards/bug_spec.rb151
-rw-r--r--spec/mspec/spec/guards/conflict_spec.rb53
-rw-r--r--spec/mspec/spec/guards/endian_spec.rb55
-rw-r--r--spec/mspec/spec/guards/feature_spec.rb120
-rw-r--r--spec/mspec/spec/guards/guard_spec.rb421
-rw-r--r--spec/mspec/spec/guards/platform_spec.rb328
-rw-r--r--spec/mspec/spec/guards/quarantine_spec.rb35
-rw-r--r--spec/mspec/spec/guards/superuser_spec.rb35
-rw-r--r--spec/mspec/spec/guards/support_spec.rb54
-rw-r--r--spec/mspec/spec/guards/user_spec.rb20
-rw-r--r--spec/mspec/spec/guards/version_spec.rb90
-rw-r--r--spec/mspec/spec/helpers/argf_spec.rb37
-rw-r--r--spec/mspec/spec/helpers/argv_spec.rb27
-rw-r--r--spec/mspec/spec/helpers/datetime_spec.rb44
-rw-r--r--spec/mspec/spec/helpers/fixture_spec.rb25
-rw-r--r--spec/mspec/spec/helpers/flunk_spec.rb20
-rw-r--r--spec/mspec/spec/helpers/fs_spec.rb195
-rw-r--r--spec/mspec/spec/helpers/io_spec.rb174
-rw-r--r--spec/mspec/spec/helpers/mock_to_path_spec.rb17
-rw-r--r--spec/mspec/spec/helpers/numeric_spec.rb25
-rw-r--r--spec/mspec/spec/helpers/ruby_exe_spec.rb197
-rw-r--r--spec/mspec/spec/helpers/scratch_spec.rb24
-rw-r--r--spec/mspec/spec/helpers/suppress_warning_spec.rb19
-rw-r--r--spec/mspec/spec/helpers/tmp_spec.rb27
-rw-r--r--spec/mspec/spec/integration/interpreter_spec.rb18
-rw-r--r--spec/mspec/spec/integration/object_methods_spec.rb18
-rw-r--r--spec/mspec/spec/integration/run_spec.rb68
-rw-r--r--spec/mspec/spec/integration/tag_spec.rb63
-rw-r--r--spec/mspec/spec/matchers/base_spec.rb225
-rw-r--r--spec/mspec/spec/matchers/be_an_instance_of_spec.rb50
-rw-r--r--spec/mspec/spec/matchers/be_ancestor_of_spec.rb28
-rw-r--r--spec/mspec/spec/matchers/be_close_spec.rb48
-rw-r--r--spec/mspec/spec/matchers/be_computed_by_spec.rb42
-rw-r--r--spec/mspec/spec/matchers/be_empty_spec.rb26
-rw-r--r--spec/mspec/spec/matchers/be_false_spec.rb28
-rw-r--r--spec/mspec/spec/matchers/be_kind_of_spec.rb31
-rw-r--r--spec/mspec/spec/matchers/be_nan_spec.rb28
-rw-r--r--spec/mspec/spec/matchers/be_nil_spec.rb27
-rw-r--r--spec/mspec/spec/matchers/be_true_or_false_spec.rb19
-rw-r--r--spec/mspec/spec/matchers/be_true_spec.rb28
-rw-r--r--spec/mspec/spec/matchers/block_caller_spec.rb13
-rw-r--r--spec/mspec/spec/matchers/complain_spec.rb52
-rw-r--r--spec/mspec/spec/matchers/eql_spec.rb33
-rw-r--r--spec/mspec/spec/matchers/equal_element_spec.rb75
-rw-r--r--spec/mspec/spec/matchers/equal_spec.rb32
-rw-r--r--spec/mspec/spec/matchers/have_class_variable_spec.rb49
-rw-r--r--spec/mspec/spec/matchers/have_constant_spec.rb37
-rw-r--r--spec/mspec/spec/matchers/have_instance_method_spec.rb53
-rw-r--r--spec/mspec/spec/matchers/have_instance_variable_spec.rb50
-rw-r--r--spec/mspec/spec/matchers/have_method_spec.rb55
-rw-r--r--spec/mspec/spec/matchers/have_private_instance_method_spec.rb57
-rw-r--r--spec/mspec/spec/matchers/have_private_method_spec.rb44
-rw-r--r--spec/mspec/spec/matchers/have_protected_instance_method_spec.rb57
-rw-r--r--spec/mspec/spec/matchers/have_public_instance_method_spec.rb53
-rw-r--r--spec/mspec/spec/matchers/have_singleton_method_spec.rb45
-rw-r--r--spec/mspec/spec/matchers/include_any_of_spec.rb42
-rw-r--r--spec/mspec/spec/matchers/include_spec.rb37
-rw-r--r--spec/mspec/spec/matchers/infinity_spec.rb34
-rw-r--r--spec/mspec/spec/matchers/match_yaml_spec.rb39
-rw-r--r--spec/mspec/spec/matchers/output_spec.rb74
-rw-r--r--spec/mspec/spec/matchers/output_to_fd_spec.rb44
-rw-r--r--spec/mspec/spec/matchers/raise_error_spec.rb120
-rw-r--r--spec/mspec/spec/matchers/respond_to_spec.rb33
-rw-r--r--spec/mspec/spec/matchers/signed_zero_spec.rb32
-rw-r--r--spec/mspec/spec/mocks/mock_spec.rb530
-rw-r--r--spec/mspec/spec/mocks/proxy_spec.rb405
-rw-r--r--spec/mspec/spec/runner/actions/filter_spec.rb84
-rw-r--r--spec/mspec/spec/runner/actions/tag_spec.rb315
-rw-r--r--spec/mspec/spec/runner/actions/taglist_spec.rb152
-rw-r--r--spec/mspec/spec/runner/actions/tagpurge_spec.rb154
-rw-r--r--spec/mspec/spec/runner/actions/tally_spec.rb352
-rw-r--r--spec/mspec/spec/runner/actions/timer_spec.rb44
-rw-r--r--spec/mspec/spec/runner/context_spec.rb1041
-rw-r--r--spec/mspec/spec/runner/example_spec.rb117
-rw-r--r--spec/mspec/spec/runner/exception_spec.rb146
-rw-r--r--spec/mspec/spec/runner/filters/a.yaml4
-rw-r--r--spec/mspec/spec/runner/filters/b.yaml11
-rw-r--r--spec/mspec/spec/runner/filters/match_spec.rb34
-rw-r--r--spec/mspec/spec/runner/filters/profile_spec.rb117
-rw-r--r--spec/mspec/spec/runner/filters/regexp_spec.rb31
-rw-r--r--spec/mspec/spec/runner/filters/tag_spec.rb92
-rw-r--r--spec/mspec/spec/runner/formatters/describe_spec.rb67
-rw-r--r--spec/mspec/spec/runner/formatters/dotted_spec.rb285
-rw-r--r--spec/mspec/spec/runner/formatters/file_spec.rb84
-rw-r--r--spec/mspec/spec/runner/formatters/html_spec.rb216
-rw-r--r--spec/mspec/spec/runner/formatters/junit_spec.rb147
-rw-r--r--spec/mspec/spec/runner/formatters/method_spec.rb178
-rw-r--r--spec/mspec/spec/runner/formatters/multi_spec.rb68
-rw-r--r--spec/mspec/spec/runner/formatters/specdoc_spec.rb106
-rw-r--r--spec/mspec/spec/runner/formatters/spinner_spec.rb83
-rw-r--r--spec/mspec/spec/runner/formatters/summary_spec.rb26
-rw-r--r--spec/mspec/spec/runner/formatters/unit_spec.rb74
-rw-r--r--spec/mspec/spec/runner/formatters/yaml_spec.rb125
-rw-r--r--spec/mspec/spec/runner/mspec_spec.rb595
-rw-r--r--spec/mspec/spec/runner/shared_spec.rb90
-rw-r--r--spec/mspec/spec/runner/tag_spec.rb123
-rw-r--r--spec/mspec/spec/runner/tags.txt4
-rw-r--r--spec/mspec/spec/spec_helper.rb55
-rw-r--r--spec/mspec/spec/utils/deprecate_spec.rb17
-rw-r--r--spec/mspec/spec/utils/name_map_spec.rb175
-rw-r--r--spec/mspec/spec/utils/options_spec.rb1285
-rw-r--r--spec/mspec/spec/utils/script_spec.rb475
-rw-r--r--spec/mspec/spec/utils/version_spec.rb45
-rwxr-xr-xspec/mspec/tool/find.rb10
-rwxr-xr-xspec/mspec/tool/pull-latest-mspec-spec19
-rw-r--r--spec/mspec/tool/remove_old_guards.rb41
-rw-r--r--spec/mspec/tool/sync/.gitignore4
-rw-r--r--spec/mspec/tool/sync/sync-rubyspec.rb229
-rw-r--r--spec/ruby/.gitignore5
-rw-r--r--spec/ruby/.rubocop.yml98
-rw-r--r--spec/ruby/.rubocop_todo.yml153
-rw-r--r--spec/ruby/.travis.yml30
-rw-r--r--spec/ruby/CHANGES.before-2008-05-1017796
-rw-r--r--spec/ruby/CONTRIBUTING.md243
-rw-r--r--spec/ruby/LICENSE22
-rw-r--r--spec/ruby/README.md108
-rw-r--r--spec/ruby/TODO8
-rw-r--r--spec/ruby/appveyor.yml34
-rw-r--r--spec/ruby/command_line/dash_a_spec.rb19
-rw-r--r--spec/ruby/command_line/dash_c_spec.rb13
-rw-r--r--spec/ruby/command_line/dash_d_spec.rb22
-rw-r--r--spec/ruby/command_line/dash_e_spec.rb41
-rw-r--r--spec/ruby/command_line/dash_encoding_spec.rb30
-rw-r--r--spec/ruby/command_line/dash_external_encoding_spec.rb15
-rw-r--r--spec/ruby/command_line/dash_internal_encoding_spec.rb15
-rw-r--r--spec/ruby/command_line/dash_n_spec.rb36
-rw-r--r--spec/ruby/command_line/dash_p_spec.rb19
-rw-r--r--spec/ruby/command_line/dash_r_spec.rb13
-rw-r--r--spec/ruby/command_line/dash_s_spec.rb52
-rw-r--r--spec/ruby/command_line/dash_upper_c_spec.rb23
-rw-r--r--spec/ruby/command_line/dash_upper_e_spec.rb36
-rw-r--r--spec/ruby/command_line/dash_upper_f_spec.rb13
-rw-r--r--spec/ruby/command_line/dash_upper_i_spec.rb51
-rw-r--r--spec/ruby/command_line/dash_upper_k_spec.rb65
-rw-r--r--spec/ruby/command_line/dash_upper_s_spec.rb29
-rw-r--r--spec/ruby/command_line/dash_upper_u_spec.rb43
-rw-r--r--spec/ruby/command_line/dash_upper_w_spec.rb20
-rw-r--r--spec/ruby/command_line/dash_v_spec.rb12
-rw-r--r--spec/ruby/command_line/dash_w_spec.rb6
-rw-r--r--spec/ruby/command_line/dash_x_spec.rb23
-rw-r--r--spec/ruby/command_line/error_message_spec.rb11
-rw-r--r--spec/ruby/command_line/fixtures/bad_syntax.rb1
-rw-r--r--spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt3
-rw-r--r--spec/ruby/command_line/fixtures/bin/dash_s_fail1
-rw-r--r--spec/ruby/command_line/fixtures/bin/embedded_ruby.txt3
-rw-r--r--spec/ruby/command_line/fixtures/bin/hybrid_launcher.sh4
-rwxr-xr-xspec/ruby/command_line/fixtures/bin/launcher.rb2
-rw-r--r--spec/ruby/command_line/fixtures/conditional_range.txt5
-rw-r--r--spec/ruby/command_line/fixtures/dash_s_script.rb12
-rw-r--r--spec/ruby/command_line/fixtures/dash_upper_c_script.rb1
-rw-r--r--spec/ruby/command_line/fixtures/debug.rb10
-rw-r--r--spec/ruby/command_line/fixtures/debug_info.rb11
-rw-r--r--spec/ruby/command_line/fixtures/freeze_flag_across_files.rb3
-rw-r--r--spec/ruby/command_line/fixtures/freeze_flag_across_files_diff_enc.rb3
-rw-r--r--spec/ruby/command_line/fixtures/freeze_flag_one_literal.rb2
-rw-r--r--spec/ruby/command_line/fixtures/freeze_flag_required.rb1
-rw-r--r--spec/ruby/command_line/fixtures/freeze_flag_required_diff_enc.rbbin0 -> 121 bytes-rw-r--r--spec/ruby/command_line/fixtures/freeze_flag_two_literals.rb1
-rw-r--r--spec/ruby/command_line/fixtures/full_names.txt3
-rw-r--r--spec/ruby/command_line/fixtures/loadpath.rb1
-rw-r--r--spec/ruby/command_line/fixtures/names.txt3
-rw-r--r--spec/ruby/command_line/fixtures/passwd_file.txt3
-rw-r--r--spec/ruby/command_line/fixtures/require.rb1
-rw-r--r--spec/ruby/command_line/fixtures/rubyopt.rb1
-rw-r--r--spec/ruby/command_line/fixtures/test_file.rb1
-rw-r--r--spec/ruby/command_line/fixtures/verbose.rb1
-rw-r--r--spec/ruby/command_line/frozen_strings_spec.rb28
-rw-r--r--spec/ruby/command_line/rubylib_spec.rb68
-rw-r--r--spec/ruby/command_line/rubyopt_spec.rb167
-rw-r--r--spec/ruby/command_line/shared/verbose.rb9
-rw-r--r--spec/ruby/command_line/syntax_error_spec.rb13
-rw-r--r--spec/ruby/core/argf/argf_spec.rb11
-rw-r--r--spec/ruby/core/argf/argv_spec.rb19
-rw-r--r--spec/ruby/core/argf/binmode_spec.rb43
-rw-r--r--spec/ruby/core/argf/bytes_spec.rb6
-rw-r--r--spec/ruby/core/argf/chars_spec.rb6
-rw-r--r--spec/ruby/core/argf/close_spec.rb35
-rw-r--r--spec/ruby/core/argf/closed_spec.rb18
-rw-r--r--spec/ruby/core/argf/codepoints_spec.rb6
-rw-r--r--spec/ruby/core/argf/each_byte_spec.rb6
-rw-r--r--spec/ruby/core/argf/each_char_spec.rb6
-rw-r--r--spec/ruby/core/argf/each_codepoint_spec.rb6
-rw-r--r--spec/ruby/core/argf/each_line_spec.rb6
-rw-r--r--spec/ruby/core/argf/each_spec.rb6
-rw-r--r--spec/ruby/core/argf/eof_spec.rb10
-rw-r--r--spec/ruby/core/argf/file_spec.rb21
-rw-r--r--spec/ruby/core/argf/filename_spec.rb6
-rw-r--r--spec/ruby/core/argf/fileno_spec.rb6
-rw-r--r--spec/ruby/core/argf/fixtures/bin_file.txt2
-rw-r--r--spec/ruby/core/argf/fixtures/file1.txt2
-rw-r--r--spec/ruby/core/argf/fixtures/file2.txt2
-rw-r--r--spec/ruby/core/argf/fixtures/filename.rb3
-rw-r--r--spec/ruby/core/argf/fixtures/lineno.rb5
-rw-r--r--spec/ruby/core/argf/fixtures/rewind.rb5
-rw-r--r--spec/ruby/core/argf/fixtures/stdin.txt2
-rw-r--r--spec/ruby/core/argf/getc_spec.rb20
-rw-r--r--spec/ruby/core/argf/gets_spec.rb51
-rw-r--r--spec/ruby/core/argf/lineno_spec.rb30
-rw-r--r--spec/ruby/core/argf/lines_spec.rb6
-rw-r--r--spec/ruby/core/argf/path_spec.rb6
-rw-r--r--spec/ruby/core/argf/pos_spec.rb38
-rw-r--r--spec/ruby/core/argf/read_nonblock_spec.rb80
-rw-r--r--spec/ruby/core/argf/read_spec.rb87
-rw-r--r--spec/ruby/core/argf/readchar_spec.rb19
-rw-r--r--spec/ruby/core/argf/readline_spec.rb23
-rw-r--r--spec/ruby/core/argf/readlines_spec.rb6
-rw-r--r--spec/ruby/core/argf/readpartial_spec.rb75
-rw-r--r--spec/ruby/core/argf/rewind_spec.rb39
-rw-r--r--spec/ruby/core/argf/seek_spec.rb63
-rw-r--r--spec/ruby/core/argf/set_encoding_spec.rb41
-rw-r--r--spec/ruby/core/argf/shared/each_byte.rb58
-rw-r--r--spec/ruby/core/argf/shared/each_char.rb58
-rw-r--r--spec/ruby/core/argf/shared/each_codepoint.rb58
-rw-r--r--spec/ruby/core/argf/shared/each_line.rb62
-rw-r--r--spec/ruby/core/argf/shared/eof.rb24
-rw-r--r--spec/ruby/core/argf/shared/filename.rb28
-rw-r--r--spec/ruby/core/argf/shared/fileno.rb24
-rw-r--r--spec/ruby/core/argf/shared/getc.rb17
-rw-r--r--spec/ruby/core/argf/shared/gets.rb99
-rw-r--r--spec/ruby/core/argf/shared/pos.rb31
-rw-r--r--spec/ruby/core/argf/shared/read.rb58
-rw-r--r--spec/ruby/core/argf/shared/readlines.rb22
-rw-r--r--spec/ruby/core/argf/skip_spec.rb42
-rw-r--r--spec/ruby/core/argf/tell_spec.rb6
-rw-r--r--spec/ruby/core/argf/to_a_spec.rb6
-rw-r--r--spec/ruby/core/argf/to_i_spec.rb6
-rw-r--r--spec/ruby/core/argf/to_io_spec.rb23
-rw-r--r--spec/ruby/core/argf/to_s_spec.rb14
-rw-r--r--spec/ruby/core/array/allocate_spec.rb19
-rw-r--r--spec/ruby/core/array/any_spec.rb37
-rw-r--r--spec/ruby/core/array/append_spec.rb42
-rw-r--r--spec/ruby/core/array/array_spec.rb7
-rw-r--r--spec/ruby/core/array/assoc_spec.rb40
-rw-r--r--spec/ruby/core/array/at_spec.rb56
-rw-r--r--spec/ruby/core/array/bsearch_index_spec.rb85
-rw-r--r--spec/ruby/core/array/bsearch_spec.rb84
-rw-r--r--spec/ruby/core/array/clear_spec.rb48
-rw-r--r--spec/ruby/core/array/clone_spec.rb31
-rw-r--r--spec/ruby/core/array/collect_spec.rb11
-rw-r--r--spec/ruby/core/array/combination_spec.rb74
-rw-r--r--spec/ruby/core/array/compact_spec.rb77
-rw-r--r--spec/ruby/core/array/comparison_spec.rb97
-rw-r--r--spec/ruby/core/array/concat_spec.rb132
-rw-r--r--spec/ruby/core/array/constructor_spec.rb24
-rw-r--r--spec/ruby/core/array/count_spec.rb15
-rw-r--r--spec/ruby/core/array/cycle_spec.rb101
-rw-r--r--spec/ruby/core/array/delete_at_spec.rb61
-rw-r--r--spec/ruby/core/array/delete_if_spec.rb66
-rw-r--r--spec/ruby/core/array/delete_spec.rb66
-rw-r--r--spec/ruby/core/array/dig_spec.rb52
-rw-r--r--spec/ruby/core/array/drop_spec.rb33
-rw-r--r--spec/ruby/core/array/drop_while_spec.rb15
-rw-r--r--spec/ruby/core/array/dup_spec.rb31
-rw-r--r--spec/ruby/core/array/each_index_spec.rb42
-rw-r--r--spec/ruby/core/array/each_spec.rb32
-rw-r--r--spec/ruby/core/array/element_reference_spec.rb50
-rw-r--r--spec/ruby/core/array/element_set_spec.rb436
-rw-r--r--spec/ruby/core/array/empty_spec.rb10
-rw-r--r--spec/ruby/core/array/eql_spec.rb19
-rw-r--r--spec/ruby/core/array/equal_value_spec.rb51
-rw-r--r--spec/ruby/core/array/fetch_spec.rb55
-rw-r--r--spec/ruby/core/array/fill_spec.rb317
-rw-r--r--spec/ruby/core/array/filter_spec.rb16
-rw-r--r--spec/ruby/core/array/find_index_spec.rb6
-rw-r--r--spec/ruby/core/array/first_spec.rb93
-rw-r--r--spec/ruby/core/array/fixtures/classes.rb522
-rw-r--r--spec/ruby/core/array/fixtures/encoded_strings.rb69
-rw-r--r--spec/ruby/core/array/flatten_spec.rb284
-rw-r--r--spec/ruby/core/array/frozen_spec.rb16
-rw-r--r--spec/ruby/core/array/hash_spec.rb83
-rw-r--r--spec/ruby/core/array/include_spec.rb33
-rw-r--r--spec/ruby/core/array/index_spec.rb6
-rw-r--r--spec/ruby/core/array/initialize_spec.rb156
-rw-r--r--spec/ruby/core/array/insert_spec.rb78
-rw-r--r--spec/ruby/core/array/inspect_spec.rb7
-rw-r--r--spec/ruby/core/array/intersection_spec.rb87
-rw-r--r--spec/ruby/core/array/join_spec.rb48
-rw-r--r--spec/ruby/core/array/keep_if_spec.rb10
-rw-r--r--spec/ruby/core/array/last_spec.rb87
-rw-r--r--spec/ruby/core/array/length_spec.rb7
-rw-r--r--spec/ruby/core/array/map_spec.rb11
-rw-r--r--spec/ruby/core/array/max_spec.rb118
-rw-r--r--spec/ruby/core/array/min_spec.rb123
-rw-r--r--spec/ruby/core/array/minus_spec.rb87
-rw-r--r--spec/ruby/core/array/multiply_spec.rb132
-rw-r--r--spec/ruby/core/array/new_spec.rb122
-rw-r--r--spec/ruby/core/array/pack/a_spec.rb62
-rw-r--r--spec/ruby/core/array/pack/at_spec.rb30
-rw-r--r--spec/ruby/core/array/pack/b_spec.rb108
-rw-r--r--spec/ruby/core/array/pack/buffer_spec.rb52
-rw-r--r--spec/ruby/core/array/pack/c_spec.rb75
-rw-r--r--spec/ruby/core/array/pack/comment_spec.rb25
-rw-r--r--spec/ruby/core/array/pack/d_spec.rb39
-rw-r--r--spec/ruby/core/array/pack/e_spec.rb25
-rw-r--r--spec/ruby/core/array/pack/empty_spec.rb11
-rw-r--r--spec/ruby/core/array/pack/f_spec.rb39
-rw-r--r--spec/ruby/core/array/pack/g_spec.rb25
-rw-r--r--spec/ruby/core/array/pack/h_spec.rb200
-rw-r--r--spec/ruby/core/array/pack/i_spec.rb133
-rw-r--r--spec/ruby/core/array/pack/j_spec.rb217
-rw-r--r--spec/ruby/core/array/pack/l_spec.rb221
-rw-r--r--spec/ruby/core/array/pack/m_spec.rb309
-rw-r--r--spec/ruby/core/array/pack/n_spec.rb25
-rw-r--r--spec/ruby/core/array/pack/p_spec.rb58
-rw-r--r--spec/ruby/core/array/pack/percent_spec.rb7
-rw-r--r--spec/ruby/core/array/pack/q_spec.rb61
-rw-r--r--spec/ruby/core/array/pack/s_spec.rb133
-rw-r--r--spec/ruby/core/array/pack/shared/basic.rb65
-rw-r--r--spec/ruby/core/array/pack/shared/encodings.rb16
-rw-r--r--spec/ruby/core/array/pack/shared/float.rb249
-rw-r--r--spec/ruby/core/array/pack/shared/integer.rb381
-rw-r--r--spec/ruby/core/array/pack/shared/numeric_basic.rb44
-rw-r--r--spec/ruby/core/array/pack/shared/string.rb48
-rw-r--r--spec/ruby/core/array/pack/shared/taint.rb33
-rw-r--r--spec/ruby/core/array/pack/shared/unicode.rb94
-rw-r--r--spec/ruby/core/array/pack/u_spec.rb130
-rw-r--r--spec/ruby/core/array/pack/v_spec.rb25
-rw-r--r--spec/ruby/core/array/pack/w_spec.rb42
-rw-r--r--spec/ruby/core/array/pack/x_spec.rb64
-rw-r--r--spec/ruby/core/array/pack/z_spec.rb34
-rw-r--r--spec/ruby/core/array/partition_spec.rb43
-rw-r--r--spec/ruby/core/array/permutation_spec.rb138
-rw-r--r--spec/ruby/core/array/plus_spec.rb57
-rw-r--r--spec/ruby/core/array/pop_spec.rb168
-rw-r--r--spec/ruby/core/array/prepend_spec.rb9
-rw-r--r--spec/ruby/core/array/product_spec.rb68
-rw-r--r--spec/ruby/core/array/push_spec.rb7
-rw-r--r--spec/ruby/core/array/rassoc_spec.rb38
-rw-r--r--spec/ruby/core/array/reject_spec.rb145
-rw-r--r--spec/ruby/core/array/repeated_combination_spec.rb84
-rw-r--r--spec/ruby/core/array/repeated_permutation_spec.rb94
-rw-r--r--spec/ruby/core/array/replace_spec.rb7
-rw-r--r--spec/ruby/core/array/reverse_each_spec.rb43
-rw-r--r--spec/ruby/core/array/reverse_spec.rb42
-rw-r--r--spec/ruby/core/array/rindex_spec.rb80
-rw-r--r--spec/ruby/core/array/rotate_spec.rb129
-rw-r--r--spec/ruby/core/array/sample_spec.rb155
-rw-r--r--spec/ruby/core/array/select_spec.rb14
-rw-r--r--spec/ruby/core/array/shared/clone.rb42
-rw-r--r--spec/ruby/core/array/shared/collect.rb136
-rw-r--r--spec/ruby/core/array/shared/delete_if.rb13
-rw-r--r--spec/ruby/core/array/shared/enumeratorize.rb5
-rw-r--r--spec/ruby/core/array/shared/eql.rb92
-rw-r--r--spec/ruby/core/array/shared/index.rb37
-rw-r--r--spec/ruby/core/array/shared/inspect.rb131
-rw-r--r--spec/ruby/core/array/shared/join.rb161
-rw-r--r--spec/ruby/core/array/shared/keep_if.rb60
-rw-r--r--spec/ruby/core/array/shared/length.rb11
-rw-r--r--spec/ruby/core/array/shared/push.rb33
-rw-r--r--spec/ruby/core/array/shared/replace.rb60
-rw-r--r--spec/ruby/core/array/shared/select.rb32
-rw-r--r--spec/ruby/core/array/shared/slice.rb459
-rw-r--r--spec/ruby/core/array/shared/unshift.rb46
-rw-r--r--spec/ruby/core/array/shift_spec.rb134
-rw-r--r--spec/ruby/core/array/shuffle_spec.rb102
-rw-r--r--spec/ruby/core/array/size_spec.rb7
-rw-r--r--spec/ruby/core/array/slice_spec.rb160
-rw-r--r--spec/ruby/core/array/sort_by_spec.rb52
-rw-r--r--spec/ruby/core/array/sort_spec.rb252
-rw-r--r--spec/ruby/core/array/sum_spec.rb44
-rw-r--r--spec/ruby/core/array/take_spec.rb27
-rw-r--r--spec/ruby/core/array/take_while_spec.rb15
-rw-r--r--spec/ruby/core/array/to_a_spec.rb24
-rw-r--r--spec/ruby/core/array/to_ary_spec.rb20
-rw-r--r--spec/ruby/core/array/to_h_spec.rb44
-rw-r--r--spec/ruby/core/array/to_s_spec.rb8
-rw-r--r--spec/ruby/core/array/transpose_spec.rb53
-rw-r--r--spec/ruby/core/array/try_convert_spec.rb50
-rw-r--r--spec/ruby/core/array/union_spec.rb82
-rw-r--r--spec/ruby/core/array/uniq_spec.rb221
-rw-r--r--spec/ruby/core/array/unshift_spec.rb7
-rw-r--r--spec/ruby/core/array/values_at_spec.rb63
-rw-r--r--spec/ruby/core/array/zip_spec.rb65
-rw-r--r--spec/ruby/core/basicobject/__id__spec.rb6
-rw-r--r--spec/ruby/core/basicobject/__send___spec.rb10
-rw-r--r--spec/ruby/core/basicobject/basicobject_spec.rb87
-rw-r--r--spec/ruby/core/basicobject/equal_spec.rb52
-rw-r--r--spec/ruby/core/basicobject/equal_value_spec.rb10
-rw-r--r--spec/ruby/core/basicobject/fixtures/classes.rb33
-rw-r--r--spec/ruby/core/basicobject/fixtures/common.rb9
-rw-r--r--spec/ruby/core/basicobject/fixtures/remove_method_missing.rb9
-rw-r--r--spec/ruby/core/basicobject/fixtures/singleton_method.rb10
-rw-r--r--spec/ruby/core/basicobject/initialize_spec.rb13
-rw-r--r--spec/ruby/core/basicobject/instance_eval_spec.rb188
-rw-r--r--spec/ruby/core/basicobject/instance_exec_spec.rb107
-rw-r--r--spec/ruby/core/basicobject/method_missing_spec.rb39
-rw-r--r--spec/ruby/core/basicobject/not_equal_spec.rb53
-rw-r--r--spec/ruby/core/basicobject/not_spec.rb11
-rw-r--r--spec/ruby/core/basicobject/singleton_method_added_spec.rb86
-rw-r--r--spec/ruby/core/basicobject/singleton_method_removed_spec.rb24
-rw-r--r--spec/ruby/core/basicobject/singleton_method_undefined_spec.rb24
-rw-r--r--spec/ruby/core/binding/clone_spec.rb7
-rw-r--r--spec/ruby/core/binding/dup_spec.rb7
-rw-r--r--spec/ruby/core/binding/eval_spec.rb95
-rw-r--r--spec/ruby/core/binding/fixtures/classes.rb52
-rw-r--r--spec/ruby/core/binding/local_variable_defined_spec.rb46
-rw-r--r--spec/ruby/core/binding/local_variable_get_spec.rb56
-rw-r--r--spec/ruby/core/binding/local_variable_set_spec.rb71
-rw-r--r--spec/ruby/core/binding/local_variables_spec.rb35
-rw-r--r--spec/ruby/core/binding/receiver_spec.rb11
-rw-r--r--spec/ruby/core/binding/shared/clone.rb34
-rw-r--r--spec/ruby/core/builtin_constants/builtin_constants_spec.rb49
-rw-r--r--spec/ruby/core/class/allocate_spec.rb41
-rw-r--r--spec/ruby/core/class/dup_spec.rb64
-rw-r--r--spec/ruby/core/class/fixtures/classes.rb47
-rw-r--r--spec/ruby/core/class/inherited_spec.rb101
-rw-r--r--spec/ruby/core/class/initialize_spec.rb34
-rw-r--r--spec/ruby/core/class/new_spec.rb154
-rw-r--r--spec/ruby/core/class/superclass_spec.rb27
-rw-r--r--spec/ruby/core/class/to_s_spec.rb23
-rw-r--r--spec/ruby/core/comparable/between_spec.rb25
-rw-r--r--spec/ruby/core/comparable/clamp_spec.rb50
-rw-r--r--spec/ruby/core/comparable/equal_value_spec.rb114
-rw-r--r--spec/ruby/core/comparable/fixtures/classes.rb36
-rw-r--r--spec/ruby/core/comparable/gt_spec.rb43
-rw-r--r--spec/ruby/core/comparable/gte_spec.rb47
-rw-r--r--spec/ruby/core/comparable/lt_spec.rb43
-rw-r--r--spec/ruby/core/comparable/lte_spec.rb46
-rw-r--r--spec/ruby/core/complex/abs2_spec.rb9
-rw-r--r--spec/ruby/core/complex/abs_spec.rb6
-rw-r--r--spec/ruby/core/complex/angle_spec.rb6
-rw-r--r--spec/ruby/core/complex/arg_spec.rb6
-rw-r--r--spec/ruby/core/complex/coerce_spec.rb70
-rw-r--r--spec/ruby/core/complex/conj_spec.rb6
-rw-r--r--spec/ruby/core/complex/conjugate_spec.rb6
-rw-r--r--spec/ruby/core/complex/constants_spec.rb7
-rw-r--r--spec/ruby/core/complex/denominator_spec.rb13
-rw-r--r--spec/ruby/core/complex/divide_spec.rb6
-rw-r--r--spec/ruby/core/complex/eql_spec.rb31
-rw-r--r--spec/ruby/core/complex/equal_value_spec.rb93
-rw-r--r--spec/ruby/core/complex/exponent_spec.rb61
-rw-r--r--spec/ruby/core/complex/fdiv_spec.rb129
-rw-r--r--spec/ruby/core/complex/finite_spec.rb36
-rw-r--r--spec/ruby/core/complex/hash_spec.rb16
-rw-r--r--spec/ruby/core/complex/imag_spec.rb6
-rw-r--r--spec/ruby/core/complex/imaginary_spec.rb6
-rw-r--r--spec/ruby/core/complex/infinite_spec.rb34
-rw-r--r--spec/ruby/core/complex/inspect_spec.rb16
-rw-r--r--spec/ruby/core/complex/integer_spec.rb11
-rw-r--r--spec/ruby/core/complex/magnitude_spec.rb6
-rw-r--r--spec/ruby/core/complex/marshal_dump_spec.rb11
-rw-r--r--spec/ruby/core/complex/minus_spec.rb45
-rw-r--r--spec/ruby/core/complex/multiply_spec.rb49
-rw-r--r--spec/ruby/core/complex/negative_spec.rb13
-rw-r--r--spec/ruby/core/complex/numerator_spec.rb19
-rw-r--r--spec/ruby/core/complex/phase_spec.rb6
-rw-r--r--spec/ruby/core/complex/plus_spec.rb45
-rw-r--r--spec/ruby/core/complex/polar_spec.rb27
-rw-r--r--spec/ruby/core/complex/positive_spec.rb13
-rw-r--r--spec/ruby/core/complex/quo_spec.rb6
-rw-r--r--spec/ruby/core/complex/rationalize_spec.rb31
-rw-r--r--spec/ruby/core/complex/real_spec.rb28
-rw-r--r--spec/ruby/core/complex/rect_spec.rb10
-rw-r--r--spec/ruby/core/complex/rectangular_spec.rb10
-rw-r--r--spec/ruby/core/complex/shared/abs.rb10
-rw-r--r--spec/ruby/core/complex/shared/arg.rb9
-rw-r--r--spec/ruby/core/complex/shared/conjugate.rb8
-rw-r--r--spec/ruby/core/complex/shared/divide.rb82
-rw-r--r--spec/ruby/core/complex/shared/image.rb8
-rw-r--r--spec/ruby/core/complex/shared/rect.rb94
-rw-r--r--spec/ruby/core/complex/to_f_spec.rb41
-rw-r--r--spec/ruby/core/complex/to_i_spec.rb41
-rw-r--r--spec/ruby/core/complex/to_r_spec.rb41
-rw-r--r--spec/ruby/core/complex/to_s_spec.rb44
-rw-r--r--spec/ruby/core/complex/uminus_spec.rb11
-rw-r--r--spec/ruby/core/dir/chdir_spec.rb124
-rw-r--r--spec/ruby/core/dir/children_spec.rb72
-rw-r--r--spec/ruby/core/dir/chroot_spec.rb47
-rw-r--r--spec/ruby/core/dir/close_spec.rb19
-rw-r--r--spec/ruby/core/dir/delete_spec.rb15
-rw-r--r--spec/ruby/core/dir/dir_spec.rb7
-rw-r--r--spec/ruby/core/dir/each_child_spec.rb53
-rw-r--r--spec/ruby/core/dir/each_spec.rb64
-rw-r--r--spec/ruby/core/dir/element_reference_spec.rb33
-rw-r--r--spec/ruby/core/dir/empty_spec.rb33
-rw-r--r--spec/ruby/core/dir/entries_spec.rb70
-rw-r--r--spec/ruby/core/dir/exist_spec.rb15
-rw-r--r--spec/ruby/core/dir/exists_spec.rb15
-rw-r--r--spec/ruby/core/dir/fileno_spec.rb37
-rw-r--r--spec/ruby/core/dir/fixtures/common.rb169
-rw-r--r--spec/ruby/core/dir/foreach_spec.rb56
-rw-r--r--spec/ruby/core/dir/getwd_spec.rb15
-rw-r--r--spec/ruby/core/dir/glob_spec.rb168
-rw-r--r--spec/ruby/core/dir/home_spec.rb32
-rw-r--r--spec/ruby/core/dir/initialize_spec.rb23
-rw-r--r--spec/ruby/core/dir/inspect_spec.rb24
-rw-r--r--spec/ruby/core/dir/mkdir_spec.rb87
-rw-r--r--spec/ruby/core/dir/open_spec.rb15
-rw-r--r--spec/ruby/core/dir/path_spec.rb15
-rw-r--r--spec/ruby/core/dir/pos_spec.rb40
-rw-r--r--spec/ruby/core/dir/pwd_spec.rb39
-rw-r--r--spec/ruby/core/dir/read_spec.rb43
-rw-r--r--spec/ruby/core/dir/rewind_spec.rb36
-rw-r--r--spec/ruby/core/dir/rmdir_spec.rb15
-rw-r--r--spec/ruby/core/dir/seek_spec.rb19
-rw-r--r--spec/ruby/core/dir/shared/chroot.rb41
-rw-r--r--spec/ruby/core/dir/shared/closed.rb9
-rw-r--r--spec/ruby/core/dir/shared/delete.rb63
-rw-r--r--spec/ruby/core/dir/shared/exist.rb56
-rw-r--r--spec/ruby/core/dir/shared/glob.rb411
-rw-r--r--spec/ruby/core/dir/shared/open.rb63
-rw-r--r--spec/ruby/core/dir/shared/path.rb32
-rw-r--r--spec/ruby/core/dir/shared/pos.rb51
-rw-r--r--spec/ruby/core/dir/shared/pwd.rb49
-rw-r--r--spec/ruby/core/dir/tell_spec.rb18
-rw-r--r--spec/ruby/core/dir/to_path_spec.rb15
-rw-r--r--spec/ruby/core/dir/unlink_spec.rb15
-rw-r--r--spec/ruby/core/encoding/_dump_spec.rb5
-rw-r--r--spec/ruby/core/encoding/_load_spec.rb5
-rw-r--r--spec/ruby/core/encoding/aliases_spec.rb45
-rw-r--r--spec/ruby/core/encoding/ascii_compatible_spec.rb13
-rw-r--r--spec/ruby/core/encoding/compatible_spec.rb381
-rw-r--r--spec/ruby/core/encoding/converter/asciicompat_encoding_spec.rb39
-rw-r--r--spec/ruby/core/encoding/converter/constants_spec.rb133
-rw-r--r--spec/ruby/core/encoding/converter/convert_spec.rb47
-rw-r--r--spec/ruby/core/encoding/converter/convpath_spec.rb26
-rw-r--r--spec/ruby/core/encoding/converter/destination_encoding_spec.rb13
-rw-r--r--spec/ruby/core/encoding/converter/finish_spec.rb38
-rw-r--r--spec/ruby/core/encoding/converter/insert_output_spec.rb5
-rw-r--r--spec/ruby/core/encoding/converter/inspect_spec.rb13
-rw-r--r--spec/ruby/core/encoding/converter/last_error_spec.rb93
-rw-r--r--spec/ruby/core/encoding/converter/new_spec.rb121
-rw-r--r--spec/ruby/core/encoding/converter/primitive_convert_spec.rb213
-rw-r--r--spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb70
-rw-r--r--spec/ruby/core/encoding/converter/putback_spec.rb49
-rw-r--r--spec/ruby/core/encoding/converter/replacement_spec.rb74
-rw-r--r--spec/ruby/core/encoding/converter/search_convpath_spec.rb34
-rw-r--r--spec/ruby/core/encoding/converter/source_encoding_spec.rb13
-rw-r--r--spec/ruby/core/encoding/default_external_spec.rb65
-rw-r--r--spec/ruby/core/encoding/default_internal_spec.rb76
-rw-r--r--spec/ruby/core/encoding/dummy_spec.rb16
-rw-r--r--spec/ruby/core/encoding/find_spec.rb84
-rw-r--r--spec/ruby/core/encoding/fixtures/classes.rb49
-rw-r--r--spec/ruby/core/encoding/inspect_spec.rb21
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_name_spec.rb20
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_spec.rb20
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/error_bytes_spec.rb32
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb31
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb32
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb30
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb35
-rw-r--r--spec/ruby/core/encoding/list_spec.rb43
-rw-r--r--spec/ruby/core/encoding/locale_charmap_spec.rb47
-rw-r--r--spec/ruby/core/encoding/name_list_spec.rb25
-rw-r--r--spec/ruby/core/encoding/name_spec.rb7
-rw-r--r--spec/ruby/core/encoding/names_spec.rb37
-rw-r--r--spec/ruby/core/encoding/replicate_spec.rb48
-rw-r--r--spec/ruby/core/encoding/shared/name.rb15
-rw-r--r--spec/ruby/core/encoding/to_s_spec.rb7
-rw-r--r--spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_name_spec.rb17
-rw-r--r--spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_spec.rb17
-rw-r--r--spec/ruby/core/encoding/undefined_conversion_error/error_char_spec.rb29
-rw-r--r--spec/ruby/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb30
-rw-r--r--spec/ruby/core/encoding/undefined_conversion_error/source_encoding_spec.rb31
-rw-r--r--spec/ruby/core/enumerable/all_spec.rb201
-rw-r--r--spec/ruby/core/enumerable/any_spec.rb214
-rw-r--r--spec/ruby/core/enumerable/chunk_spec.rb77
-rw-r--r--spec/ruby/core/enumerable/chunk_while_spec.rb42
-rw-r--r--spec/ruby/core/enumerable/collect_concat_spec.rb7
-rw-r--r--spec/ruby/core/enumerable/collect_spec.rb7
-rw-r--r--spec/ruby/core/enumerable/count_spec.rb59
-rw-r--r--spec/ruby/core/enumerable/cycle_spec.rb104
-rw-r--r--spec/ruby/core/enumerable/detect_spec.rb7
-rw-r--r--spec/ruby/core/enumerable/drop_spec.rb43
-rw-r--r--spec/ruby/core/enumerable/drop_while_spec.rb50
-rw-r--r--spec/ruby/core/enumerable/each_cons_spec.rb99
-rw-r--r--spec/ruby/core/enumerable/each_entry_spec.rb41
-rw-r--r--spec/ruby/core/enumerable/each_slice_spec.rb101
-rw-r--r--spec/ruby/core/enumerable/each_with_index_spec.rb53
-rw-r--r--spec/ruby/core/enumerable/each_with_object_spec.rb41
-rw-r--r--spec/ruby/core/enumerable/entries_spec.rb7
-rw-r--r--spec/ruby/core/enumerable/filter_spec.rb9
-rw-r--r--spec/ruby/core/enumerable/find_all_spec.rb7
-rw-r--r--spec/ruby/core/enumerable/find_index_spec.rb89
-rw-r--r--spec/ruby/core/enumerable/find_spec.rb7
-rw-r--r--spec/ruby/core/enumerable/first_spec.rb28
-rw-r--r--spec/ruby/core/enumerable/fixtures/classes.rb345
-rw-r--r--spec/ruby/core/enumerable/flat_map_spec.rb7
-rw-r--r--spec/ruby/core/enumerable/grep_spec.rb52
-rw-r--r--spec/ruby/core/enumerable/grep_v_spec.rb41
-rw-r--r--spec/ruby/core/enumerable/group_by_spec.rb45
-rw-r--r--spec/ruby/core/enumerable/include_spec.rb7
-rw-r--r--spec/ruby/core/enumerable/inject_spec.rb7
-rw-r--r--spec/ruby/core/enumerable/lazy_spec.rb10
-rw-r--r--spec/ruby/core/enumerable/map_spec.rb7
-rw-r--r--spec/ruby/core/enumerable/max_by_spec.rb81
-rw-r--r--spec/ruby/core/enumerable/max_spec.rb119
-rw-r--r--spec/ruby/core/enumerable/member_spec.rb7
-rw-r--r--spec/ruby/core/enumerable/min_by_spec.rb81
-rw-r--r--spec/ruby/core/enumerable/min_spec.rb123
-rw-r--r--spec/ruby/core/enumerable/minmax_by_spec.rb44
-rw-r--r--spec/ruby/core/enumerable/minmax_spec.rb44
-rw-r--r--spec/ruby/core/enumerable/none_spec.rb167
-rw-r--r--spec/ruby/core/enumerable/one_spec.rb169
-rw-r--r--spec/ruby/core/enumerable/partition_spec.rb20
-rw-r--r--spec/ruby/core/enumerable/reduce_spec.rb7
-rw-r--r--spec/ruby/core/enumerable/reject_spec.rb25
-rw-r--r--spec/ruby/core/enumerable/reverse_each_spec.rb26
-rw-r--r--spec/ruby/core/enumerable/select_spec.rb7
-rw-r--r--spec/ruby/core/enumerable/shared/collect.rb32
-rw-r--r--spec/ruby/core/enumerable/shared/collect_concat.rb54
-rw-r--r--spec/ruby/core/enumerable/shared/entries.rb24
-rw-r--r--spec/ruby/core/enumerable/shared/enumerable_enumeratorized.rb33
-rw-r--r--spec/ruby/core/enumerable/shared/enumeratorized.rb42
-rw-r--r--spec/ruby/core/enumerable/shared/find.rb73
-rw-r--r--spec/ruby/core/enumerable/shared/find_all.rb31
-rw-r--r--spec/ruby/core/enumerable/shared/include.rb34
-rw-r--r--spec/ruby/core/enumerable/shared/inject.rb69
-rw-r--r--spec/ruby/core/enumerable/shared/take.rb63
-rw-r--r--spec/ruby/core/enumerable/slice_after_spec.rb61
-rw-r--r--spec/ruby/core/enumerable/slice_before_spec.rb64
-rw-r--r--spec/ruby/core/enumerable/slice_when_spec.rb54
-rw-r--r--spec/ruby/core/enumerable/sort_by_spec.rb43
-rw-r--r--spec/ruby/core/enumerable/sort_spec.rb54
-rw-r--r--spec/ruby/core/enumerable/sum_spec.rb30
-rw-r--r--spec/ruby/core/enumerable/take_spec.rb13
-rw-r--r--spec/ruby/core/enumerable/take_while_spec.rb51
-rw-r--r--spec/ruby/core/enumerable/to_a_spec.rb7
-rw-r--r--spec/ruby/core/enumerable/to_h_spec.rb54
-rw-r--r--spec/ruby/core/enumerable/uniq_spec.rb94
-rw-r--r--spec/ruby/core/enumerable/zip_spec.rb41
-rw-r--r--spec/ruby/core/enumerator/each_spec.rb5
-rw-r--r--spec/ruby/core/enumerator/each_with_index_spec.rb38
-rw-r--r--spec/ruby/core/enumerator/each_with_object_spec.rb6
-rw-r--r--spec/ruby/core/enumerator/enum_for_spec.rb6
-rw-r--r--spec/ruby/core/enumerator/enumerator_spec.rb7
-rw-r--r--spec/ruby/core/enumerator/feed_spec.rb52
-rw-r--r--spec/ruby/core/enumerator/first_spec.rb7
-rw-r--r--spec/ruby/core/enumerator/fixtures/common.rb9
-rw-r--r--spec/ruby/core/enumerator/generator/each_spec.rb40
-rw-r--r--spec/ruby/core/enumerator/generator/initialize_spec.rb26
-rw-r--r--spec/ruby/core/enumerator/initialize_spec.rb61
-rw-r--r--spec/ruby/core/enumerator/inject_spec.rb15
-rw-r--r--spec/ruby/core/enumerator/inspect_spec.rb17
-rw-r--r--spec/ruby/core/enumerator/lazy/chunk_spec.rb71
-rw-r--r--spec/ruby/core/enumerator/lazy/collect_concat_spec.rb8
-rw-r--r--spec/ruby/core/enumerator/lazy/collect_spec.rb8
-rw-r--r--spec/ruby/core/enumerator/lazy/drop_spec.rb52
-rw-r--r--spec/ruby/core/enumerator/lazy/drop_while_spec.rb60
-rw-r--r--spec/ruby/core/enumerator/lazy/enum_for_spec.rb8
-rw-r--r--spec/ruby/core/enumerator/lazy/find_all_spec.rb8
-rw-r--r--spec/ruby/core/enumerator/lazy/fixtures/classes.rb54
-rw-r--r--spec/ruby/core/enumerator/lazy/flat_map_spec.rb8
-rw-r--r--spec/ruby/core/enumerator/lazy/force_spec.rb30
-rw-r--r--spec/ruby/core/enumerator/lazy/grep_spec.rb82
-rw-r--r--spec/ruby/core/enumerator/lazy/grep_v_spec.rb84
-rw-r--r--spec/ruby/core/enumerator/lazy/initialize_spec.rb63
-rw-r--r--spec/ruby/core/enumerator/lazy/lazy_spec.rb16
-rw-r--r--spec/ruby/core/enumerator/lazy/map_spec.rb12
-rw-r--r--spec/ruby/core/enumerator/lazy/reject_spec.rb60
-rw-r--r--spec/ruby/core/enumerator/lazy/select_spec.rb8
-rw-r--r--spec/ruby/core/enumerator/lazy/shared/collect.rb56
-rw-r--r--spec/ruby/core/enumerator/lazy/shared/collect_concat.rb72
-rw-r--r--spec/ruby/core/enumerator/lazy/shared/select.rb60
-rw-r--r--spec/ruby/core/enumerator/lazy/shared/to_enum.rb50
-rw-r--r--spec/ruby/core/enumerator/lazy/take_spec.rb66
-rw-r--r--spec/ruby/core/enumerator/lazy/take_while_spec.rb60
-rw-r--r--spec/ruby/core/enumerator/lazy/to_enum_spec.rb8
-rw-r--r--spec/ruby/core/enumerator/lazy/uniq_spec.rb76
-rw-r--r--spec/ruby/core/enumerator/lazy/zip_spec.rb74
-rw-r--r--spec/ruby/core/enumerator/new_spec.rb6
-rw-r--r--spec/ruby/core/enumerator/next_spec.rb6
-rw-r--r--spec/ruby/core/enumerator/next_values_spec.rb55
-rw-r--r--spec/ruby/core/enumerator/peek_spec.rb36
-rw-r--r--spec/ruby/core/enumerator/peek_values_spec.rb57
-rw-r--r--spec/ruby/core/enumerator/rewind_spec.rb38
-rw-r--r--spec/ruby/core/enumerator/size_spec.rb26
-rw-r--r--spec/ruby/core/enumerator/to_enum_spec.rb6
-rw-r--r--spec/ruby/core/enumerator/with_index_spec.rb72
-rw-r--r--spec/ruby/core/enumerator/with_object_spec.rb6
-rw-r--r--spec/ruby/core/enumerator/yielder/append_spec.rb24
-rw-r--r--spec/ruby/core/enumerator/yielder/initialize_spec.rb18
-rw-r--r--spec/ruby/core/enumerator/yielder/yield_spec.rb23
-rw-r--r--spec/ruby/core/env/assoc_spec.rb23
-rw-r--r--spec/ruby/core/env/clear_spec.rb20
-rw-r--r--spec/ruby/core/env/delete_if_spec.rb27
-rw-r--r--spec/ruby/core/env/delete_spec.rb24
-rw-r--r--spec/ruby/core/env/each_key_spec.rb32
-rw-r--r--spec/ruby/core/env/each_pair_spec.rb6
-rw-r--r--spec/ruby/core/env/each_spec.rb6
-rw-r--r--spec/ruby/core/env/each_value_spec.rb32
-rw-r--r--spec/ruby/core/env/element_reference_spec.rb66
-rw-r--r--spec/ruby/core/env/element_set_spec.rb6
-rw-r--r--spec/ruby/core/env/empty_spec.rb23
-rw-r--r--spec/ruby/core/env/fetch_spec.rb36
-rw-r--r--spec/ruby/core/env/has_key_spec.rb6
-rw-r--r--spec/ruby/core/env/has_value_spec.rb6
-rw-r--r--spec/ruby/core/env/include_spec.rb6
-rw-r--r--spec/ruby/core/env/index_spec.rb6
-rw-r--r--spec/ruby/core/env/indexes_spec.rb1
-rw-r--r--spec/ruby/core/env/indices_spec.rb1
-rw-r--r--spec/ruby/core/env/inspect_spec.rb11
-rw-r--r--spec/ruby/core/env/invert_spec.rb16
-rw-r--r--spec/ruby/core/env/keep_if_spec.rb33
-rw-r--r--spec/ruby/core/env/key_spec.rb11
-rw-r--r--spec/ruby/core/env/keys_spec.rb14
-rw-r--r--spec/ruby/core/env/length_spec.rb6
-rw-r--r--spec/ruby/core/env/member_spec.rb6
-rw-r--r--spec/ruby/core/env/rassoc_spec.rb23
-rw-r--r--spec/ruby/core/env/rehash_spec.rb1
-rw-r--r--spec/ruby/core/env/reject_spec.rb77
-rw-r--r--spec/ruby/core/env/replace_spec.rb15
-rw-r--r--spec/ruby/core/env/select_spec.rb39
-rw-r--r--spec/ruby/core/env/shared/each.rb65
-rw-r--r--spec/ruby/core/env/shared/include.rb11
-rw-r--r--spec/ruby/core/env/shared/key.rb13
-rw-r--r--spec/ruby/core/env/shared/length.rb13
-rw-r--r--spec/ruby/core/env/shared/store.rb56
-rw-r--r--spec/ruby/core/env/shared/to_hash.rb22
-rw-r--r--spec/ruby/core/env/shared/value.rb11
-rw-r--r--spec/ruby/core/env/shift_spec.rb59
-rw-r--r--spec/ruby/core/env/size_spec.rb6
-rw-r--r--spec/ruby/core/env/store_spec.rb6
-rw-r--r--spec/ruby/core/env/to_a_spec.rb19
-rw-r--r--spec/ruby/core/env/to_h_spec.rb19
-rw-r--r--spec/ruby/core/env/to_hash_spec.rb6
-rw-r--r--spec/ruby/core/env/to_s_spec.rb7
-rw-r--r--spec/ruby/core/env/update_spec.rb25
-rw-r--r--spec/ruby/core/env/value_spec.rb6
-rw-r--r--spec/ruby/core/env/values_at_spec.rb17
-rw-r--r--spec/ruby/core/env/values_spec.rb21
-rw-r--r--spec/ruby/core/exception/args_spec.rb5
-rw-r--r--spec/ruby/core/exception/arguments_spec.rb11
-rw-r--r--spec/ruby/core/exception/backtrace_locations_spec.rb39
-rw-r--r--spec/ruby/core/exception/backtrace_spec.rb68
-rw-r--r--spec/ruby/core/exception/case_compare_spec.rb5
-rw-r--r--spec/ruby/core/exception/cause_spec.rb19
-rw-r--r--spec/ruby/core/exception/destination_encoding_name_spec.rb9
-rw-r--r--spec/ruby/core/exception/destination_encoding_spec.rb9
-rw-r--r--spec/ruby/core/exception/dup_spec.rb61
-rw-r--r--spec/ruby/core/exception/equal_value_spec.rb68
-rw-r--r--spec/ruby/core/exception/errno_spec.rb48
-rw-r--r--spec/ruby/core/exception/error_bytes_spec.rb5
-rw-r--r--spec/ruby/core/exception/error_char_spec.rb5
-rw-r--r--spec/ruby/core/exception/exception_spec.rb83
-rw-r--r--spec/ruby/core/exception/exit_value_spec.rb5
-rw-r--r--spec/ruby/core/exception/fixtures/common.rb95
-rw-r--r--spec/ruby/core/exception/full_message_spec.rb38
-rw-r--r--spec/ruby/core/exception/incomplete_input_spec.rb5
-rw-r--r--spec/ruby/core/exception/initialize_spec.rb1
-rw-r--r--spec/ruby/core/exception/inspect_spec.rb20
-rw-r--r--spec/ruby/core/exception/interrupt_spec.rb41
-rw-r--r--spec/ruby/core/exception/io_error_spec.rb51
-rw-r--r--spec/ruby/core/exception/load_error_spec.rb21
-rw-r--r--spec/ruby/core/exception/message_spec.rb27
-rw-r--r--spec/ruby/core/exception/name_error_spec.rb13
-rw-r--r--spec/ruby/core/exception/name_spec.rb45
-rw-r--r--spec/ruby/core/exception/new_spec.rb7
-rw-r--r--spec/ruby/core/exception/no_method_error_spec.rb95
-rw-r--r--spec/ruby/core/exception/range_error_spec.rb7
-rw-r--r--spec/ruby/core/exception/readagain_bytes_spec.rb5
-rw-r--r--spec/ruby/core/exception/reason_spec.rb5
-rw-r--r--spec/ruby/core/exception/receiver_spec.rb60
-rw-r--r--spec/ruby/core/exception/result_spec.rb29
-rw-r--r--spec/ruby/core/exception/script_error_spec.rb15
-rw-r--r--spec/ruby/core/exception/set_backtrace_spec.rb56
-rw-r--r--spec/ruby/core/exception/shared/new.rb18
-rw-r--r--spec/ruby/core/exception/signal_exception_spec.rb74
-rw-r--r--spec/ruby/core/exception/signm_spec.rb5
-rw-r--r--spec/ruby/core/exception/signo_spec.rb5
-rw-r--r--spec/ruby/core/exception/source_encoding_name_spec.rb9
-rw-r--r--spec/ruby/core/exception/source_encoding_spec.rb9
-rw-r--r--spec/ruby/core/exception/standard_error_spec.rb56
-rw-r--r--spec/ruby/core/exception/status_spec.rb5
-rw-r--r--spec/ruby/core/exception/success_spec.rb5
-rw-r--r--spec/ruby/core/exception/system_call_error_spec.rb89
-rw-r--r--spec/ruby/core/exception/system_stack_error_spec.rb7
-rw-r--r--spec/ruby/core/exception/to_s_spec.rb23
-rw-r--r--spec/ruby/core/exception/uncaught_throw_error_spec.rb18
-rw-r--r--spec/ruby/core/false/and_spec.rb11
-rw-r--r--spec/ruby/core/false/dup_spec.rb9
-rw-r--r--spec/ruby/core/false/falseclass_spec.rb15
-rw-r--r--spec/ruby/core/false/inspect_spec.rb7
-rw-r--r--spec/ruby/core/false/or_spec.rb11
-rw-r--r--spec/ruby/core/false/to_s_spec.rb7
-rw-r--r--spec/ruby/core/false/xor_spec.rb11
-rw-r--r--spec/ruby/core/fiber/new_spec.rb41
-rw-r--r--spec/ruby/core/fiber/resume_spec.rb59
-rw-r--r--spec/ruby/core/fiber/yield_spec.rb51
-rw-r--r--spec/ruby/core/file/absolute_path_spec.rb37
-rw-r--r--spec/ruby/core/file/atime_spec.rb55
-rw-r--r--spec/ruby/core/file/basename_spec.rb170
-rw-r--r--spec/ruby/core/file/birthtime_spec.rb56
-rw-r--r--spec/ruby/core/file/blockdev_spec.rb6
-rw-r--r--spec/ruby/core/file/chardev_spec.rb6
-rw-r--r--spec/ruby/core/file/chmod_spec.rb185
-rw-r--r--spec/ruby/core/file/chown_spec.rb152
-rw-r--r--spec/ruby/core/file/constants/constants_spec.rb31
-rw-r--r--spec/ruby/core/file/constants_spec.rb141
-rw-r--r--spec/ruby/core/file/ctime_spec.rb51
-rw-r--r--spec/ruby/core/file/delete_spec.rb6
-rw-r--r--spec/ruby/core/file/directory_spec.rb10
-rw-r--r--spec/ruby/core/file/dirname_spec.rb108
-rw-r--r--spec/ruby/core/file/empty_spec.rb15
-rw-r--r--spec/ruby/core/file/executable_real_spec.rb7
-rw-r--r--spec/ruby/core/file/executable_spec.rb7
-rw-r--r--spec/ruby/core/file/exist_spec.rb6
-rw-r--r--spec/ruby/core/file/exists_spec.rb6
-rw-r--r--spec/ruby/core/file/expand_path_spec.rb261
-rw-r--r--spec/ruby/core/file/extname_spec.rb54
-rw-r--r--spec/ruby/core/file/file_spec.rb16
-rw-r--r--spec/ruby/core/file/fixtures/common.rb22
-rw-r--r--spec/ruby/core/file/fixtures/do_not_remove1
-rw-r--r--spec/ruby/core/file/fixtures/file_types.rb66
-rw-r--r--spec/ruby/core/file/flock_spec.rb106
-rw-r--r--spec/ruby/core/file/fnmatch_spec.rb10
-rw-r--r--spec/ruby/core/file/ftype_spec.rb73
-rw-r--r--spec/ruby/core/file/grpowned_spec.rb10
-rw-r--r--spec/ruby/core/file/identical_spec.rb6
-rw-r--r--spec/ruby/core/file/initialize_spec.rb23
-rw-r--r--spec/ruby/core/file/inspect_spec.rb17
-rw-r--r--spec/ruby/core/file/join_spec.rb139
-rw-r--r--spec/ruby/core/file/lchmod_spec.rb42
-rw-r--r--spec/ruby/core/file/lchown_spec.rb63
-rw-r--r--spec/ruby/core/file/link_spec.rb39
-rw-r--r--spec/ruby/core/file/lstat_spec.rb33
-rw-r--r--spec/ruby/core/file/mkfifo_spec.rb51
-rw-r--r--spec/ruby/core/file/mtime_spec.rb51
-rw-r--r--spec/ruby/core/file/new_spec.rb162
-rw-r--r--spec/ruby/core/file/null_spec.rb15
-rw-r--r--spec/ruby/core/file/open_spec.rb680
-rw-r--r--spec/ruby/core/file/owned_spec.rb35
-rw-r--r--spec/ruby/core/file/path_spec.rb40
-rw-r--r--spec/ruby/core/file/pipe_spec.rb32
-rw-r--r--spec/ruby/core/file/printf_spec.rb18
-rw-r--r--spec/ruby/core/file/read_spec.rb6
-rw-r--r--spec/ruby/core/file/readable_real_spec.rb7
-rw-r--r--spec/ruby/core/file/readable_spec.rb7
-rw-r--r--spec/ruby/core/file/readlink_spec.rb86
-rw-r--r--spec/ruby/core/file/realdirpath_spec.rb104
-rw-r--r--spec/ruby/core/file/realpath_spec.rb88
-rw-r--r--spec/ruby/core/file/rename_spec.rb37
-rw-r--r--spec/ruby/core/file/reopen_spec.rb32
-rw-r--r--spec/ruby/core/file/setgid_spec.rb36
-rw-r--r--spec/ruby/core/file/setuid_spec.rb38
-rw-r--r--spec/ruby/core/file/shared/fnmatch.rb241
-rw-r--r--spec/ruby/core/file/shared/open.rb12
-rw-r--r--spec/ruby/core/file/shared/path.rb80
-rw-r--r--spec/ruby/core/file/shared/read.rb15
-rw-r--r--spec/ruby/core/file/shared/stat.rb32
-rw-r--r--spec/ruby/core/file/shared/unlink.rb61
-rw-r--r--spec/ruby/core/file/size_spec.rb119
-rw-r--r--spec/ruby/core/file/socket_spec.rb42
-rw-r--r--spec/ruby/core/file/split_spec.rb63
-rw-r--r--spec/ruby/core/file/stat/atime_spec.rb18
-rw-r--r--spec/ruby/core/file/stat/birthtime_spec.rb27
-rw-r--r--spec/ruby/core/file/stat/blksize_spec.rb27
-rw-r--r--spec/ruby/core/file/stat/blockdev_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/blocks_spec.rb27
-rw-r--r--spec/ruby/core/file/stat/chardev_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/comparison_spec.rb66
-rw-r--r--spec/ruby/core/file/stat/ctime_spec.rb18
-rw-r--r--spec/ruby/core/file/stat/dev_major_spec.rb23
-rw-r--r--spec/ruby/core/file/stat/dev_minor_spec.rb23
-rw-r--r--spec/ruby/core/file/stat/dev_spec.rb15
-rw-r--r--spec/ruby/core/file/stat/directory_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/executable_real_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/executable_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/file_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/fixtures/classes.rb5
-rw-r--r--spec/ruby/core/file/stat/ftype_spec.rb68
-rw-r--r--spec/ruby/core/file/stat/gid_spec.rb19
-rw-r--r--spec/ruby/core/file/stat/grpowned_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/ino_spec.rb28
-rw-r--r--spec/ruby/core/file/stat/inspect_spec.rb26
-rw-r--r--spec/ruby/core/file/stat/mode_spec.rb19
-rw-r--r--spec/ruby/core/file/stat/mtime_spec.rb18
-rw-r--r--spec/ruby/core/file/stat/new_spec.rb32
-rw-r--r--spec/ruby/core/file/stat/nlink_spec.rb21
-rw-r--r--spec/ruby/core/file/stat/owned_spec.rb33
-rw-r--r--spec/ruby/core/file/stat/pipe_spec.rb32
-rw-r--r--spec/ruby/core/file/stat/rdev_major_spec.rb31
-rw-r--r--spec/ruby/core/file/stat/rdev_minor_spec.rb31
-rw-r--r--spec/ruby/core/file/stat/rdev_spec.rb15
-rw-r--r--spec/ruby/core/file/stat/readable_real_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/readable_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/setgid_spec.rb11
-rw-r--r--spec/ruby/core/file/stat/setuid_spec.rb11
-rw-r--r--spec/ruby/core/file/stat/size_spec.rb21
-rw-r--r--spec/ruby/core/file/stat/socket_spec.rb11
-rw-r--r--spec/ruby/core/file/stat/sticky_spec.rb11
-rw-r--r--spec/ruby/core/file/stat/symlink_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/uid_spec.rb18
-rw-r--r--spec/ruby/core/file/stat/world_readable_spec.rb11
-rw-r--r--spec/ruby/core/file/stat/world_writable_spec.rb11
-rw-r--r--spec/ruby/core/file/stat/writable_real_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/writable_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/zero_spec.rb7
-rw-r--r--spec/ruby/core/file/stat_spec.rb45
-rw-r--r--spec/ruby/core/file/sticky_spec.rb50
-rw-r--r--spec/ruby/core/file/symlink_spec.rb57
-rw-r--r--spec/ruby/core/file/to_path_spec.rb6
-rw-r--r--spec/ruby/core/file/truncate_spec.rb177
-rw-r--r--spec/ruby/core/file/umask_spec.rb57
-rw-r--r--spec/ruby/core/file/unlink_spec.rb6
-rw-r--r--spec/ruby/core/file/utime_spec.rb55
-rw-r--r--spec/ruby/core/file/world_readable_spec.rb12
-rw-r--r--spec/ruby/core/file/world_writable_spec.rb12
-rw-r--r--spec/ruby/core/file/writable_real_spec.rb7
-rw-r--r--spec/ruby/core/file/writable_spec.rb7
-rw-r--r--spec/ruby/core/file/zero_spec.rb13
-rw-r--r--spec/ruby/core/filetest/blockdev_spec.rb6
-rw-r--r--spec/ruby/core/filetest/chardev_spec.rb6
-rw-r--r--spec/ruby/core/filetest/directory_spec.rb10
-rw-r--r--spec/ruby/core/filetest/executable_real_spec.rb7
-rw-r--r--spec/ruby/core/filetest/executable_spec.rb7
-rw-r--r--spec/ruby/core/filetest/exist_spec.rb6
-rw-r--r--spec/ruby/core/filetest/exists_spec.rb6
-rw-r--r--spec/ruby/core/filetest/file_spec.rb10
-rw-r--r--spec/ruby/core/filetest/grpowned_spec.rb10
-rw-r--r--spec/ruby/core/filetest/identical_spec.rb6
-rw-r--r--spec/ruby/core/filetest/owned_spec.rb10
-rw-r--r--spec/ruby/core/filetest/pipe_spec.rb10
-rw-r--r--spec/ruby/core/filetest/readable_real_spec.rb7
-rw-r--r--spec/ruby/core/filetest/readable_spec.rb7
-rw-r--r--spec/ruby/core/filetest/setgid_spec.rb10
-rw-r--r--spec/ruby/core/filetest/setuid_spec.rb10
-rw-r--r--spec/ruby/core/filetest/size_spec.rb34
-rw-r--r--spec/ruby/core/filetest/socket_spec.rb10
-rw-r--r--spec/ruby/core/filetest/sticky_spec.rb7
-rw-r--r--spec/ruby/core/filetest/symlink_spec.rb10
-rw-r--r--spec/ruby/core/filetest/world_readable_spec.rb5
-rw-r--r--spec/ruby/core/filetest/world_writable_spec.rb5
-rw-r--r--spec/ruby/core/filetest/writable_real_spec.rb7
-rw-r--r--spec/ruby/core/filetest/writable_spec.rb7
-rw-r--r--spec/ruby/core/filetest/zero_spec.rb13
-rw-r--r--spec/ruby/core/float/abs_spec.rb6
-rw-r--r--spec/ruby/core/float/angle_spec.rb6
-rw-r--r--spec/ruby/core/float/arg_spec.rb6
-rw-r--r--spec/ruby/core/float/case_compare_spec.rb6
-rw-r--r--spec/ruby/core/float/ceil_spec.rb23
-rw-r--r--spec/ruby/core/float/coerce_spec.rb18
-rw-r--r--spec/ruby/core/float/comparison_spec.rb36
-rw-r--r--spec/ruby/core/float/constants_spec.rb55
-rw-r--r--spec/ruby/core/float/denominator_spec.rb29
-rw-r--r--spec/ruby/core/float/divide_spec.rb39
-rw-r--r--spec/ruby/core/float/divmod_spec.rb43
-rw-r--r--spec/ruby/core/float/dup_spec.rb10
-rw-r--r--spec/ruby/core/float/eql_spec.rb16
-rw-r--r--spec/ruby/core/float/equal_value_spec.rb6
-rw-r--r--spec/ruby/core/float/exponent_spec.rb15
-rw-r--r--spec/ruby/core/float/fdiv_spec.rb6
-rw-r--r--spec/ruby/core/float/finite_spec.rb19
-rw-r--r--spec/ruby/core/float/fixtures/classes.rb4
-rw-r--r--spec/ruby/core/float/fixtures/coerce.rb15
-rw-r--r--spec/ruby/core/float/float_spec.rb19
-rw-r--r--spec/ruby/core/float/floor_spec.rb23
-rw-r--r--spec/ruby/core/float/gt_spec.rb17
-rw-r--r--spec/ruby/core/float/gte_spec.rb17
-rw-r--r--spec/ruby/core/float/hash_spec.rb11
-rw-r--r--spec/ruby/core/float/infinite_spec.rb19
-rw-r--r--spec/ruby/core/float/lt_spec.rb17
-rw-r--r--spec/ruby/core/float/lte_spec.rb18
-rw-r--r--spec/ruby/core/float/magnitude_spec.rb5
-rw-r--r--spec/ruby/core/float/minus_spec.rb12
-rw-r--r--spec/ruby/core/float/modulo_spec.rb10
-rw-r--r--spec/ruby/core/float/multiply_spec.rb17
-rw-r--r--spec/ruby/core/float/nan_spec.rb9
-rw-r--r--spec/ruby/core/float/next_float_spec.rb49
-rw-r--r--spec/ruby/core/float/numerator_spec.rb39
-rw-r--r--spec/ruby/core/float/phase_spec.rb6
-rw-r--r--spec/ruby/core/float/plus_spec.rb12
-rw-r--r--spec/ruby/core/float/prev_float_spec.rb49
-rw-r--r--spec/ruby/core/float/quo_spec.rb6
-rw-r--r--spec/ruby/core/float/rationalize_spec.rb43
-rw-r--r--spec/ruby/core/float/round_spec.rb101
-rw-r--r--spec/ruby/core/float/shared/abs.rb21
-rw-r--r--spec/ruby/core/float/shared/arg.rb36
-rw-r--r--spec/ruby/core/float/shared/arithmetic_exception_in_coerce.rb33
-rw-r--r--spec/ruby/core/float/shared/comparison_exception_in_coerce.rb35
-rw-r--r--spec/ruby/core/float/shared/equal.rb17
-rw-r--r--spec/ruby/core/float/shared/modulo.rb48
-rw-r--r--spec/ruby/core/float/shared/quo.rb59
-rw-r--r--spec/ruby/core/float/shared/to_i.rb10
-rw-r--r--spec/ruby/core/float/to_f_spec.rb9
-rw-r--r--spec/ruby/core/float/to_i_spec.rb6
-rw-r--r--spec/ruby/core/float/to_int_spec.rb6
-rw-r--r--spec/ruby/core/float/to_r_spec.rb5
-rw-r--r--spec/ruby/core/float/to_s_spec.rb120
-rw-r--r--spec/ruby/core/float/truncate_spec.rb16
-rw-r--r--spec/ruby/core/float/uminus_spec.rb28
-rw-r--r--spec/ruby/core/float/uplus_spec.rb9
-rw-r--r--spec/ruby/core/float/zero_spec.rb9
-rw-r--r--spec/ruby/core/gc/count_spec.rb17
-rw-r--r--spec/ruby/core/gc/disable_spec.rb18
-rw-r--r--spec/ruby/core/gc/enable_spec.rb13
-rw-r--r--spec/ruby/core/gc/garbage_collect_spec.rb15
-rw-r--r--spec/ruby/core/gc/profiler/clear_spec.rb5
-rw-r--r--spec/ruby/core/gc/profiler/disable_spec.rb16
-rw-r--r--spec/ruby/core/gc/profiler/enable_spec.rb17
-rw-r--r--spec/ruby/core/gc/profiler/enabled_spec.rb21
-rw-r--r--spec/ruby/core/gc/profiler/report_spec.rb5
-rw-r--r--spec/ruby/core/gc/profiler/result_spec.rb7
-rw-r--r--spec/ruby/core/gc/profiler/total_time_spec.rb7
-rw-r--r--spec/ruby/core/gc/start_spec.rb8
-rw-r--r--spec/ruby/core/gc/stress_spec.rb27
-rw-r--r--spec/ruby/core/hash/allocate_spec.rb15
-rw-r--r--spec/ruby/core/hash/any_spec.rb30
-rw-r--r--spec/ruby/core/hash/assoc_spec.rb50
-rw-r--r--spec/ruby/core/hash/clear_spec.rb32
-rw-r--r--spec/ruby/core/hash/clone_spec.rb12
-rw-r--r--spec/ruby/core/hash/compact_spec.rb61
-rw-r--r--spec/ruby/core/hash/compare_by_identity_spec.rb140
-rw-r--r--spec/ruby/core/hash/constructor_spec.rb110
-rw-r--r--spec/ruby/core/hash/default_proc_spec.rb80
-rw-r--r--spec/ruby/core/hash/default_spec.rb46
-rw-r--r--spec/ruby/core/hash/delete_if_spec.rb44
-rw-r--r--spec/ruby/core/hash/delete_spec.rb44
-rw-r--r--spec/ruby/core/hash/dig_spec.rb66
-rw-r--r--spec/ruby/core/hash/each_key_spec.rb23
-rw-r--r--spec/ruby/core/hash/each_pair_spec.rb11
-rw-r--r--spec/ruby/core/hash/each_spec.rb11
-rw-r--r--spec/ruby/core/hash/each_value_spec.rb23
-rw-r--r--spec/ruby/core/hash/element_reference_spec.rb120
-rw-r--r--spec/ruby/core/hash/element_set_spec.rb7
-rw-r--r--spec/ruby/core/hash/empty_spec.rb15
-rw-r--r--spec/ruby/core/hash/eql_spec.rb9
-rw-r--r--spec/ruby/core/hash/equal_value_spec.rb18
-rw-r--r--spec/ruby/core/hash/fetch_spec.rb38
-rw-r--r--spec/ruby/core/hash/fetch_values_spec.rb31
-rw-r--r--spec/ruby/core/hash/filter_spec.rb12
-rw-r--r--spec/ruby/core/hash/fixtures/classes.rb75
-rw-r--r--spec/ruby/core/hash/flatten_spec.rb62
-rw-r--r--spec/ruby/core/hash/gt_spec.rb42
-rw-r--r--spec/ruby/core/hash/gte_spec.rb42
-rw-r--r--spec/ruby/core/hash/has_key_spec.rb7
-rw-r--r--spec/ruby/core/hash/has_value_spec.rb7
-rw-r--r--spec/ruby/core/hash/hash_spec.rb36
-rw-r--r--spec/ruby/core/hash/include_spec.rb7
-rw-r--r--spec/ruby/core/hash/index_spec.rb7
-rw-r--r--spec/ruby/core/hash/initialize_spec.rb61
-rw-r--r--spec/ruby/core/hash/inspect_spec.rb7
-rw-r--r--spec/ruby/core/hash/invert_spec.rb27
-rw-r--r--spec/ruby/core/hash/keep_if_spec.rb37
-rw-r--r--spec/ruby/core/hash/key_spec.rb12
-rw-r--r--spec/ruby/core/hash/keys_spec.rb23
-rw-r--r--spec/ruby/core/hash/length_spec.rb7
-rw-r--r--spec/ruby/core/hash/lt_spec.rb42
-rw-r--r--spec/ruby/core/hash/lte_spec.rb42
-rw-r--r--spec/ruby/core/hash/member_spec.rb7
-rw-r--r--spec/ruby/core/hash/merge_spec.rb77
-rw-r--r--spec/ruby/core/hash/new_spec.rb36
-rw-r--r--spec/ruby/core/hash/rassoc_spec.rb42
-rw-r--r--spec/ruby/core/hash/rehash_spec.rb57
-rw-r--r--spec/ruby/core/hash/reject_spec.rb100
-rw-r--r--spec/ruby/core/hash/replace_spec.rb7
-rw-r--r--spec/ruby/core/hash/select_spec.rb10
-rw-r--r--spec/ruby/core/hash/shared/comparison.rb15
-rw-r--r--spec/ruby/core/hash/shared/each.rb68
-rw-r--r--spec/ruby/core/hash/shared/eql.rb216
-rw-r--r--spec/ruby/core/hash/shared/equal.rb90
-rw-r--r--spec/ruby/core/hash/shared/greater_than.rb23
-rw-r--r--spec/ruby/core/hash/shared/index.rb27
-rw-r--r--spec/ruby/core/hash/shared/iteration.rb19
-rw-r--r--spec/ruby/core/hash/shared/key.rb38
-rw-r--r--spec/ruby/core/hash/shared/length.rb12
-rw-r--r--spec/ruby/core/hash/shared/less_than.rb23
-rw-r--r--spec/ruby/core/hash/shared/replace.rb51
-rw-r--r--spec/ruby/core/hash/shared/select.rb91
-rw-r--r--spec/ruby/core/hash/shared/store.rb98
-rw-r--r--spec/ruby/core/hash/shared/to_s.rb96
-rw-r--r--spec/ruby/core/hash/shared/update.rb59
-rw-r--r--spec/ruby/core/hash/shared/value.rb14
-rw-r--r--spec/ruby/core/hash/shared/values_at.rb9
-rw-r--r--spec/ruby/core/hash/shift_spec.rb79
-rw-r--r--spec/ruby/core/hash/size_spec.rb7
-rw-r--r--spec/ruby/core/hash/slice_spec.rb55
-rw-r--r--spec/ruby/core/hash/sort_spec.rb17
-rw-r--r--spec/ruby/core/hash/store_spec.rb7
-rw-r--r--spec/ruby/core/hash/to_a_spec.rb37
-rw-r--r--spec/ruby/core/hash/to_h_spec.rb40
-rw-r--r--spec/ruby/core/hash/to_hash_spec.rb14
-rw-r--r--spec/ruby/core/hash/to_proc_spec.rb87
-rw-r--r--spec/ruby/core/hash/to_s_spec.rb7
-rw-r--r--spec/ruby/core/hash/transform_keys_spec.rb132
-rw-r--r--spec/ruby/core/hash/transform_values_spec.rb99
-rw-r--r--spec/ruby/core/hash/try_convert_spec.rb50
-rw-r--r--spec/ruby/core/hash/update_spec.rb7
-rw-r--r--spec/ruby/core/hash/value_spec.rb7
-rw-r--r--spec/ruby/core/hash/values_at_spec.rb7
-rw-r--r--spec/ruby/core/hash/values_spec.rb10
-rw-r--r--spec/ruby/core/integer/abs_spec.rb6
-rw-r--r--spec/ruby/core/integer/allbits_spec.rb39
-rw-r--r--spec/ruby/core/integer/anybits_spec.rb38
-rw-r--r--spec/ruby/core/integer/bit_and_spec.rb97
-rw-r--r--spec/ruby/core/integer/bit_length_spec.rb76
-rw-r--r--spec/ruby/core/integer/bit_or_spec.rb68
-rw-r--r--spec/ruby/core/integer/bit_xor_spec.rb72
-rw-r--r--spec/ruby/core/integer/case_compare_spec.rb6
-rw-r--r--spec/ruby/core/integer/ceil_spec.rb21
-rw-r--r--spec/ruby/core/integer/chr_spec.rb243
-rw-r--r--spec/ruby/core/integer/coerce_spec.rb105
-rw-r--r--spec/ruby/core/integer/comparison_spec.rb189
-rw-r--r--spec/ruby/core/integer/complement_spec.rb20
-rw-r--r--spec/ruby/core/integer/denominator_spec.rb20
-rw-r--r--spec/ruby/core/integer/digits_spec.rb34
-rw-r--r--spec/ruby/core/integer/div_spec.rb138
-rw-r--r--spec/ruby/core/integer/divide_spec.rb95
-rw-r--r--spec/ruby/core/integer/divmod_spec.rb117
-rw-r--r--spec/ruby/core/integer/downto_spec.rb69
-rw-r--r--spec/ruby/core/integer/dup_spec.rb15
-rw-r--r--spec/ruby/core/integer/element_reference_spec.rb111
-rw-r--r--spec/ruby/core/integer/equal_value_spec.rb6
-rw-r--r--spec/ruby/core/integer/even_spec.rb40
-rw-r--r--spec/ruby/core/integer/exponent_spec.rb7
-rw-r--r--spec/ruby/core/integer/fdiv_spec.rb49
-rw-r--r--spec/ruby/core/integer/fixtures/classes.rb4
-rw-r--r--spec/ruby/core/integer/floor_spec.rb21
-rw-r--r--spec/ruby/core/integer/gcd_spec.rb69
-rw-r--r--spec/ruby/core/integer/gcdlcm_spec.rb53
-rw-r--r--spec/ruby/core/integer/gt_spec.rb49
-rw-r--r--spec/ruby/core/integer/gte_spec.rb49
-rw-r--r--spec/ruby/core/integer/integer_spec.rb22
-rw-r--r--spec/ruby/core/integer/lcm_spec.rb58
-rw-r--r--spec/ruby/core/integer/left_shift_spec.rb165
-rw-r--r--spec/ruby/core/integer/lt_spec.rb51
-rw-r--r--spec/ruby/core/integer/lte_spec.rb54
-rw-r--r--spec/ruby/core/integer/magnitude_spec.rb6
-rw-r--r--spec/ruby/core/integer/minus_spec.rb49
-rw-r--r--spec/ruby/core/integer/modulo_spec.rb10
-rw-r--r--spec/ruby/core/integer/multiply_spec.rb51
-rw-r--r--spec/ruby/core/integer/next_spec.rb6
-rw-r--r--spec/ruby/core/integer/nobits_spec.rb38
-rw-r--r--spec/ruby/core/integer/numerator_spec.rb18
-rw-r--r--spec/ruby/core/integer/odd_spec.rb38
-rw-r--r--spec/ruby/core/integer/ord_spec.rb17
-rw-r--r--spec/ruby/core/integer/plus_spec.rb49
-rw-r--r--spec/ruby/core/integer/pow_spec.rb51
-rw-r--r--spec/ruby/core/integer/pred_spec.rb11
-rw-r--r--spec/ruby/core/integer/rationalize_spec.rb39
-rw-r--r--spec/ruby/core/integer/remainder_spec.rb51
-rw-r--r--spec/ruby/core/integer/right_shift_spec.rb191
-rw-r--r--spec/ruby/core/integer/round_spec.rb95
-rw-r--r--spec/ruby/core/integer/shared/abs.rb18
-rw-r--r--spec/ruby/core/integer/shared/arithmetic_coerce.rb31
-rw-r--r--spec/ruby/core/integer/shared/comparison_coerce.rb33
-rw-r--r--spec/ruby/core/integer/shared/equal.rb58
-rw-r--r--spec/ruby/core/integer/shared/exponent.rb118
-rw-r--r--spec/ruby/core/integer/shared/integer_rounding.rb31
-rw-r--r--spec/ruby/core/integer/shared/modulo.rb74
-rw-r--r--spec/ruby/core/integer/shared/next.rb25
-rw-r--r--spec/ruby/core/integer/shared/to_i.rb8
-rw-r--r--spec/ruby/core/integer/size_spec.rb34
-rw-r--r--spec/ruby/core/integer/sqrt_spec.rb33
-rw-r--r--spec/ruby/core/integer/succ_spec.rb6
-rw-r--r--spec/ruby/core/integer/times_spec.rb79
-rw-r--r--spec/ruby/core/integer/to_f_spec.rb23
-rw-r--r--spec/ruby/core/integer/to_i_spec.rb6
-rw-r--r--spec/ruby/core/integer/to_int_spec.rb6
-rw-r--r--spec/ruby/core/integer/to_r_spec.rb26
-rw-r--r--spec/ruby/core/integer/to_s_spec.rb99
-rw-r--r--spec/ruby/core/integer/truncate_spec.rb21
-rw-r--r--spec/ruby/core/integer/uminus_spec.rb28
-rw-r--r--spec/ruby/core/integer/upto_spec.rb69
-rw-r--r--spec/ruby/core/io/advise_spec.rb97
-rw-r--r--spec/ruby/core/io/binmode_spec.rb60
-rw-r--r--spec/ruby/core/io/binread_spec.rb47
-rw-r--r--spec/ruby/core/io/binwrite_spec.rb8
-rw-r--r--spec/ruby/core/io/bytes_spec.rb43
-rw-r--r--spec/ruby/core/io/chars_spec.rb12
-rw-r--r--spec/ruby/core/io/close_on_exec_spec.rb80
-rw-r--r--spec/ruby/core/io/close_read_spec.rb60
-rw-r--r--spec/ruby/core/io/close_spec.rb97
-rw-r--r--spec/ruby/core/io/close_write_spec.rb64
-rw-r--r--spec/ruby/core/io/closed_spec.rb20
-rw-r--r--spec/ruby/core/io/codepoints_spec.rb25
-rw-r--r--spec/ruby/core/io/constants_spec.rb19
-rw-r--r--spec/ruby/core/io/copy_stream_spec.rb282
-rw-r--r--spec/ruby/core/io/dup_spec.rb69
-rw-r--r--spec/ruby/core/io/each_byte_spec.rb57
-rw-r--r--spec/ruby/core/io/each_char_spec.rb12
-rw-r--r--spec/ruby/core/io/each_codepoint_spec.rb43
-rw-r--r--spec/ruby/core/io/each_line_spec.rb11
-rw-r--r--spec/ruby/core/io/each_spec.rb11
-rw-r--r--spec/ruby/core/io/eof_spec.rb107
-rw-r--r--spec/ruby/core/io/external_encoding_spec.rb218
-rw-r--r--spec/ruby/core/io/fcntl_spec.rb8
-rw-r--r--spec/ruby/core/io/fdatasync_spec.rb5
-rw-r--r--spec/ruby/core/io/fileno_spec.rb12
-rw-r--r--spec/ruby/core/io/fixtures/bom_UTF-16BE.txtbin0 -> 20 bytes-rw-r--r--spec/ruby/core/io/fixtures/bom_UTF-16LE.txtbin0 -> 20 bytes-rw-r--r--spec/ruby/core/io/fixtures/bom_UTF-32BE.txtbin0 -> 40 bytes-rw-r--r--spec/ruby/core/io/fixtures/bom_UTF-32LE.txtbin0 -> 40 bytes-rw-r--r--spec/ruby/core/io/fixtures/bom_UTF-8.txt1
-rw-r--r--spec/ruby/core/io/fixtures/classes.rb189
-rw-r--r--spec/ruby/core/io/fixtures/copy_stream.txt6
-rw-r--r--spec/ruby/core/io/fixtures/empty.txt0
-rw-r--r--spec/ruby/core/io/fixtures/incomplete.txt1
-rw-r--r--spec/ruby/core/io/fixtures/lines.txt9
-rw-r--r--spec/ruby/core/io/fixtures/no_bom_UTF-8.txt1
-rw-r--r--spec/ruby/core/io/fixtures/numbered_lines.txt5
-rw-r--r--spec/ruby/core/io/fixtures/one_byte.txt1
-rw-r--r--spec/ruby/core/io/fixtures/read_binary.txt1
-rw-r--r--spec/ruby/core/io/fixtures/read_euc_jp.txt1
-rw-r--r--spec/ruby/core/io/fixtures/read_text.txt1
-rw-r--r--spec/ruby/core/io/fixtures/reopen_stdout.rb3
-rw-r--r--spec/ruby/core/io/flush_spec.rb8
-rw-r--r--spec/ruby/core/io/for_fd_spec.rb10
-rw-r--r--spec/ruby/core/io/foreach_spec.rb81
-rw-r--r--spec/ruby/core/io/fsync_spec.rb24
-rw-r--r--spec/ruby/core/io/getbyte_spec.rb42
-rw-r--r--spec/ruby/core/io/getc_spec.rb42
-rw-r--r--spec/ruby/core/io/gets_spec.rb321
-rw-r--r--spec/ruby/core/io/initialize_spec.rb57
-rw-r--r--spec/ruby/core/io/inspect_spec.rb23
-rw-r--r--spec/ruby/core/io/internal_encoding_spec.rb140
-rw-r--r--spec/ruby/core/io/io_spec.rb11
-rw-r--r--spec/ruby/core/io/ioctl_spec.rb32
-rw-r--r--spec/ruby/core/io/isatty_spec.rb6
-rw-r--r--spec/ruby/core/io/lineno_spec.rb95
-rw-r--r--spec/ruby/core/io/lines_spec.rb42
-rw-r--r--spec/ruby/core/io/new_spec.rb10
-rw-r--r--spec/ruby/core/io/open_spec.rb86
-rw-r--r--spec/ruby/core/io/output_spec.rb27
-rw-r--r--spec/ruby/core/io/pid_spec.rb35
-rw-r--r--spec/ruby/core/io/pipe_spec.rb214
-rw-r--r--spec/ruby/core/io/popen_spec.rb286
-rw-r--r--spec/ruby/core/io/pos_spec.rb11
-rw-r--r--spec/ruby/core/io/pread_spec.rb52
-rw-r--r--spec/ruby/core/io/print_spec.rb53
-rw-r--r--spec/ruby/core/io/printf_spec.rb32
-rw-r--r--spec/ruby/core/io/putc_spec.rb11
-rw-r--r--spec/ruby/core/io/puts_spec.rb141
-rw-r--r--spec/ruby/core/io/pwrite_spec.rb45
-rw-r--r--spec/ruby/core/io/read_nonblock_spec.rb92
-rw-r--r--spec/ruby/core/io/read_spec.rb620
-rw-r--r--spec/ruby/core/io/readbyte_spec.rb26
-rw-r--r--spec/ruby/core/io/readchar_spec.rb44
-rw-r--r--spec/ruby/core/io/readline_spec.rb53
-rw-r--r--spec/ruby/core/io/readlines_spec.rb210
-rw-r--r--spec/ruby/core/io/readpartial_spec.rb96
-rw-r--r--spec/ruby/core/io/reopen_spec.rb306
-rw-r--r--spec/ruby/core/io/rewind_spec.rb38
-rw-r--r--spec/ruby/core/io/seek_spec.rb79
-rw-r--r--spec/ruby/core/io/select_spec.rb120
-rw-r--r--spec/ruby/core/io/set_encoding_spec.rb193
-rw-r--r--spec/ruby/core/io/shared/binwrite.rb78
-rw-r--r--spec/ruby/core/io/shared/chars.rb73
-rw-r--r--spec/ruby/core/io/shared/codepoints.rb54
-rw-r--r--spec/ruby/core/io/shared/each.rb185
-rw-r--r--spec/ruby/core/io/shared/gets_ascii.rb19
-rw-r--r--spec/ruby/core/io/shared/new.rb378
-rw-r--r--spec/ruby/core/io/shared/pos.rb72
-rw-r--r--spec/ruby/core/io/shared/readlines.rb211
-rw-r--r--spec/ruby/core/io/shared/tty.rb25
-rw-r--r--spec/ruby/core/io/shared/write.rb93
-rw-r--r--spec/ruby/core/io/stat_spec.rb24
-rw-r--r--spec/ruby/core/io/sync_spec.rb64
-rw-r--r--spec/ruby/core/io/sysopen_spec.rb50
-rw-r--r--spec/ruby/core/io/sysread_spec.rb98
-rw-r--r--spec/ruby/core/io/sysseek_spec.rb44
-rw-r--r--spec/ruby/core/io/syswrite_spec.rb71
-rw-r--r--spec/ruby/core/io/tell_spec.rb7
-rw-r--r--spec/ruby/core/io/to_i_spec.rb12
-rw-r--r--spec/ruby/core/io/to_io_spec.rb21
-rw-r--r--spec/ruby/core/io/try_convert_spec.rb49
-rw-r--r--spec/ruby/core/io/tty_spec.rb6
-rw-r--r--spec/ruby/core/io/ungetbyte_spec.rb73
-rw-r--r--spec/ruby/core/io/ungetc_spec.rb135
-rw-r--r--spec/ruby/core/io/write_nonblock_spec.rb83
-rw-r--r--spec/ruby/core/io/write_spec.rb168
-rw-r--r--spec/ruby/core/kernel/Array_spec.rb97
-rw-r--r--spec/ruby/core/kernel/Complex_spec.rb141
-rw-r--r--spec/ruby/core/kernel/Float_spec.rb316
-rw-r--r--spec/ruby/core/kernel/Hash_spec.rb63
-rw-r--r--spec/ruby/core/kernel/Integer_spec.rb715
-rw-r--r--spec/ruby/core/kernel/Rational_spec.rb6
-rw-r--r--spec/ruby/core/kernel/String_spec.rb106
-rw-r--r--spec/ruby/core/kernel/__callee___spec.rb48
-rw-r--r--spec/ruby/core/kernel/__dir___spec.rb20
-rw-r--r--spec/ruby/core/kernel/__method___spec.rb40
-rw-r--r--spec/ruby/core/kernel/abort_spec.rb15
-rw-r--r--spec/ruby/core/kernel/at_exit_spec.rb44
-rw-r--r--spec/ruby/core/kernel/autoload_spec.rb160
-rw-r--r--spec/ruby/core/kernel/backtick_spec.rb80
-rw-r--r--spec/ruby/core/kernel/binding_spec.rb51
-rw-r--r--spec/ruby/core/kernel/block_given_spec.rb38
-rw-r--r--spec/ruby/core/kernel/caller_locations_spec.rb32
-rw-r--r--spec/ruby/core/kernel/caller_spec.rb46
-rw-r--r--spec/ruby/core/kernel/case_compare_spec.rb135
-rw-r--r--spec/ruby/core/kernel/catch_spec.rb127
-rw-r--r--spec/ruby/core/kernel/chomp_spec.rb67
-rw-r--r--spec/ruby/core/kernel/chop_spec.rb55
-rw-r--r--spec/ruby/core/kernel/class_spec.rb26
-rw-r--r--spec/ruby/core/kernel/clone_spec.rb118
-rw-r--r--spec/ruby/core/kernel/comparison_spec.rb31
-rw-r--r--spec/ruby/core/kernel/define_singleton_method_spec.rb99
-rw-r--r--spec/ruby/core/kernel/display_spec.rb6
-rw-r--r--spec/ruby/core/kernel/dup_spec.rb67
-rw-r--r--spec/ruby/core/kernel/enum_for_spec.rb5
-rw-r--r--spec/ruby/core/kernel/eql_spec.rb10
-rw-r--r--spec/ruby/core/kernel/equal_value_spec.rb15
-rw-r--r--spec/ruby/core/kernel/eval_spec.rb345
-rw-r--r--spec/ruby/core/kernel/exec_spec.rb18
-rw-r--r--spec/ruby/core/kernel/exit_spec.rb27
-rw-r--r--spec/ruby/core/kernel/extend_spec.rb79
-rw-r--r--spec/ruby/core/kernel/fail_spec.rb43
-rw-r--r--spec/ruby/core/kernel/fixtures/__callee__.rb34
-rw-r--r--spec/ruby/core/kernel/fixtures/__method__.rb34
-rw-r--r--spec/ruby/core/kernel/fixtures/autoload_b.rb5
-rw-r--r--spec/ruby/core/kernel/fixtures/autoload_d.rb5
-rw-r--r--spec/ruby/core/kernel/fixtures/autoload_from_included_module.rb9
-rw-r--r--spec/ruby/core/kernel/fixtures/autoload_from_included_module2.rb9
-rw-r--r--spec/ruby/core/kernel/fixtures/autoload_frozen.rb7
-rw-r--r--spec/ruby/core/kernel/fixtures/caller.rb7
-rw-r--r--spec/ruby/core/kernel/fixtures/caller_at_exit.rb7
-rw-r--r--spec/ruby/core/kernel/fixtures/caller_locations.rb7
-rw-r--r--spec/ruby/core/kernel/fixtures/chomp.rb4
-rw-r--r--spec/ruby/core/kernel/fixtures/chomp_f.rb4
-rw-r--r--spec/ruby/core/kernel/fixtures/chop.rb4
-rw-r--r--spec/ruby/core/kernel/fixtures/chop_f.rb4
-rw-r--r--spec/ruby/core/kernel/fixtures/classes.rb465
-rw-r--r--spec/ruby/core/kernel/fixtures/eval_locals.rb6
-rw-r--r--spec/ruby/core/kernel/fixtures/eval_return_with_lambda.rb12
-rw-r--r--spec/ruby/core/kernel/fixtures/eval_return_without_lambda.rb14
-rw-r--r--spec/ruby/core/kernel/fixtures/singleton_methods.rb13
-rw-r--r--spec/ruby/core/kernel/fixtures/test.rb362
-rw-r--r--spec/ruby/core/kernel/fork_spec.rb15
-rw-r--r--spec/ruby/core/kernel/format_spec.rb14
-rw-r--r--spec/ruby/core/kernel/freeze_spec.rb85
-rw-r--r--spec/ruby/core/kernel/frozen_spec.rb78
-rw-r--r--spec/ruby/core/kernel/gets_spec.rb17
-rw-r--r--spec/ruby/core/kernel/global_variables_spec.rb26
-rw-r--r--spec/ruby/core/kernel/gsub_spec.rb96
-rw-r--r--spec/ruby/core/kernel/inspect_spec.rb31
-rw-r--r--spec/ruby/core/kernel/instance_of_spec.rb40
-rw-r--r--spec/ruby/core/kernel/instance_variable_defined_spec.rb41
-rw-r--r--spec/ruby/core/kernel/instance_variable_get_spec.rb105
-rw-r--r--spec/ruby/core/kernel/instance_variable_set_spec.rb93
-rw-r--r--spec/ruby/core/kernel/instance_variables_spec.rb27
-rw-r--r--spec/ruby/core/kernel/is_a_spec.rb6
-rw-r--r--spec/ruby/core/kernel/iterator_spec.rb12
-rw-r--r--spec/ruby/core/kernel/itself_spec.rb9
-rw-r--r--spec/ruby/core/kernel/kind_of_spec.rb6
-rw-r--r--spec/ruby/core/kernel/lambda_spec.rb100
-rw-r--r--spec/ruby/core/kernel/load_spec.rb40
-rw-r--r--spec/ruby/core/kernel/local_variables_spec.rb37
-rw-r--r--spec/ruby/core/kernel/loop_spec.rb79
-rw-r--r--spec/ruby/core/kernel/match_spec.rb14
-rw-r--r--spec/ruby/core/kernel/method_spec.rb37
-rw-r--r--spec/ruby/core/kernel/methods_spec.rb101
-rw-r--r--spec/ruby/core/kernel/nil_spec.rb6
-rw-r--r--spec/ruby/core/kernel/not_match_spec.rb21
-rw-r--r--spec/ruby/core/kernel/object_id_spec.rb6
-rw-r--r--spec/ruby/core/kernel/open_spec.rb142
-rw-r--r--spec/ruby/core/kernel/p_spec.rb79
-rw-r--r--spec/ruby/core/kernel/print_spec.rb12
-rw-r--r--spec/ruby/core/kernel/printf_spec.rb60
-rw-r--r--spec/ruby/core/kernel/private_methods_spec.rb69
-rw-r--r--spec/ruby/core/kernel/proc_spec.rb50
-rw-r--r--spec/ruby/core/kernel/protected_methods_spec.rb69
-rw-r--r--spec/ruby/core/kernel/public_method_spec.rb32
-rw-r--r--spec/ruby/core/kernel/public_methods_spec.rb76
-rw-r--r--spec/ruby/core/kernel/public_send_spec.rb108
-rw-r--r--spec/ruby/core/kernel/putc_spec.rb39
-rw-r--r--spec/ruby/core/kernel/puts_spec.rb29
-rw-r--r--spec/ruby/core/kernel/raise_spec.rb17
-rw-r--r--spec/ruby/core/kernel/rand_spec.rb139
-rw-r--r--spec/ruby/core/kernel/readline_spec.rb12
-rw-r--r--spec/ruby/core/kernel/readlines_spec.rb12
-rw-r--r--spec/ruby/core/kernel/remove_instance_variable_spec.rb59
-rw-r--r--spec/ruby/core/kernel/require_relative_spec.rb431
-rw-r--r--spec/ruby/core/kernel/require_spec.rb34
-rw-r--r--spec/ruby/core/kernel/respond_to_missing_spec.rb100
-rw-r--r--spec/ruby/core/kernel/respond_to_spec.rb73
-rw-r--r--spec/ruby/core/kernel/select_spec.rb20
-rw-r--r--spec/ruby/core/kernel/send_spec.rb68
-rw-r--r--spec/ruby/core/kernel/set_trace_func_spec.rb12
-rw-r--r--spec/ruby/core/kernel/shared/dup_clone.rb149
-rw-r--r--spec/ruby/core/kernel/shared/kind_of.rb55
-rw-r--r--spec/ruby/core/kernel/shared/lambda.rb9
-rw-r--r--spec/ruby/core/kernel/shared/load.rb139
-rw-r--r--spec/ruby/core/kernel/shared/method.rb50
-rw-r--r--spec/ruby/core/kernel/shared/require.rb765
-rw-r--r--spec/ruby/core/kernel/shared/sprintf.rb877
-rw-r--r--spec/ruby/core/kernel/shared/sprintf_encoding.rb28
-rw-r--r--spec/ruby/core/kernel/shared/then.rb20
-rw-r--r--spec/ruby/core/kernel/singleton_class_spec.rb27
-rw-r--r--spec/ruby/core/kernel/singleton_method_spec.rb41
-rw-r--r--spec/ruby/core/kernel/singleton_methods_spec.rb192
-rw-r--r--spec/ruby/core/kernel/sleep_spec.rb52
-rw-r--r--spec/ruby/core/kernel/spawn_spec.rb25
-rw-r--r--spec/ruby/core/kernel/sprintf_spec.rb24
-rw-r--r--spec/ruby/core/kernel/srand_spec.rb61
-rw-r--r--spec/ruby/core/kernel/sub_spec.rb26
-rw-r--r--spec/ruby/core/kernel/syscall_spec.rb12
-rw-r--r--spec/ruby/core/kernel/system_spec.rb117
-rw-r--r--spec/ruby/core/kernel/taint_spec.rb45
-rw-r--r--spec/ruby/core/kernel/tainted_spec.rb12
-rw-r--r--spec/ruby/core/kernel/tap_spec.rb13
-rw-r--r--spec/ruby/core/kernel/test_spec.rb109
-rw-r--r--spec/ruby/core/kernel/then_spec.rb8
-rw-r--r--spec/ruby/core/kernel/throw_spec.rb80
-rw-r--r--spec/ruby/core/kernel/to_enum_spec.rb5
-rw-r--r--spec/ruby/core/kernel/to_s_spec.rb16
-rw-r--r--spec/ruby/core/kernel/trace_var_spec.rb54
-rw-r--r--spec/ruby/core/kernel/trap_spec.rb12
-rw-r--r--spec/ruby/core/kernel/trust_spec.rb25
-rw-r--r--spec/ruby/core/kernel/untaint_spec.rb25
-rw-r--r--spec/ruby/core/kernel/untrace_var_spec.rb12
-rw-r--r--spec/ruby/core/kernel/untrust_spec.rb25
-rw-r--r--spec/ruby/core/kernel/untrusted_spec.rb28
-rw-r--r--spec/ruby/core/kernel/warn_spec.rb135
-rw-r--r--spec/ruby/core/kernel/yield_self_spec.rb8
-rw-r--r--spec/ruby/core/main/define_method_spec.rb28
-rw-r--r--spec/ruby/core/main/fixtures/classes.rb18
-rw-r--r--spec/ruby/core/main/fixtures/string_refinement.rb7
-rw-r--r--spec/ruby/core/main/fixtures/string_refinement_user.rb11
-rw-r--r--spec/ruby/core/main/fixtures/wrapped_include.rb1
-rw-r--r--spec/ruby/core/main/include_spec.rb16
-rw-r--r--spec/ruby/core/main/private_spec.rb23
-rw-r--r--spec/ruby/core/main/public_spec.rb23
-rw-r--r--spec/ruby/core/main/to_s_spec.rb7
-rw-r--r--spec/ruby/core/main/using_spec.rb132
-rw-r--r--spec/ruby/core/marshal/dump_spec.rb597
-rw-r--r--spec/ruby/core/marshal/fixtures/marshal_data.rb420
-rw-r--r--spec/ruby/core/marshal/fixtures/random.dumpbin0 -> 2520 bytes-rw-r--r--spec/ruby/core/marshal/float_spec.rb77
-rw-r--r--spec/ruby/core/marshal/load_spec.rb6
-rw-r--r--spec/ruby/core/marshal/major_version_spec.rb7
-rw-r--r--spec/ruby/core/marshal/minor_version_spec.rb7
-rw-r--r--spec/ruby/core/marshal/restore_spec.rb6
-rw-r--r--spec/ruby/core/marshal/shared/load.rb830
-rw-r--r--spec/ruby/core/matchdata/begin_spec.rb30
-rw-r--r--spec/ruby/core/matchdata/captures_spec.rb7
-rw-r--r--spec/ruby/core/matchdata/element_reference_spec.rb87
-rw-r--r--spec/ruby/core/matchdata/end_spec.rb30
-rw-r--r--spec/ruby/core/matchdata/eql_spec.rb6
-rw-r--r--spec/ruby/core/matchdata/equal_value_spec.rb6
-rw-r--r--spec/ruby/core/matchdata/hash_spec.rb5
-rw-r--r--spec/ruby/core/matchdata/inspect_spec.rb23
-rw-r--r--spec/ruby/core/matchdata/length_spec.rb6
-rw-r--r--spec/ruby/core/matchdata/named_captures_spec.rb13
-rw-r--r--spec/ruby/core/matchdata/names_spec.rb33
-rw-r--r--spec/ruby/core/matchdata/offset_spec.rb30
-rw-r--r--spec/ruby/core/matchdata/post_match_spec.rb36
-rw-r--r--spec/ruby/core/matchdata/pre_match_spec.rb36
-rw-r--r--spec/ruby/core/matchdata/regexp_spec.rb13
-rw-r--r--spec/ruby/core/matchdata/shared/eql.rb26
-rw-r--r--spec/ruby/core/matchdata/shared/length.rb5
-rw-r--r--spec/ruby/core/matchdata/size_spec.rb6
-rw-r--r--spec/ruby/core/matchdata/string_spec.rb14
-rw-r--r--spec/ruby/core/matchdata/to_a_spec.rb7
-rw-r--r--spec/ruby/core/matchdata/to_s_spec.rb7
-rw-r--r--spec/ruby/core/matchdata/values_at_spec.rb23
-rw-r--r--spec/ruby/core/math/acos_spec.rb56
-rw-r--r--spec/ruby/core/math/acosh_spec.rb41
-rw-r--r--spec/ruby/core/math/asin_spec.rb48
-rw-r--r--spec/ruby/core/math/asinh_spec.rb42
-rw-r--r--spec/ruby/core/math/atan2_spec.rb54
-rw-r--r--spec/ruby/core/math/atan_spec.rb40
-rw-r--r--spec/ruby/core/math/atanh_spec.rb14
-rw-r--r--spec/ruby/core/math/cbrt_spec.rb27
-rw-r--r--spec/ruby/core/math/constants_spec.rb22
-rw-r--r--spec/ruby/core/math/cos_spec.rb42
-rw-r--r--spec/ruby/core/math/cosh_spec.rb37
-rw-r--r--spec/ruby/core/math/erf_spec.rb44
-rw-r--r--spec/ruby/core/math/erfc_spec.rb43
-rw-r--r--spec/ruby/core/math/exp_spec.rb37
-rw-r--r--spec/ruby/core/math/fixtures/classes.rb28
-rw-r--r--spec/ruby/core/math/frexp_spec.rb37
-rw-r--r--spec/ruby/core/math/gamma_spec.rb69
-rw-r--r--spec/ruby/core/math/hypot_spec.rb41
-rw-r--r--spec/ruby/core/math/ldexp_spec.rb54
-rw-r--r--spec/ruby/core/math/lgamma_spec.rb56
-rw-r--r--spec/ruby/core/math/log10_spec.rb43
-rw-r--r--spec/ruby/core/math/log2_spec.rb41
-rw-r--r--spec/ruby/core/math/log_spec.rb57
-rw-r--r--spec/ruby/core/math/sin_spec.rb39
-rw-r--r--spec/ruby/core/math/sinh_spec.rb37
-rw-r--r--spec/ruby/core/math/sqrt_spec.rb36
-rw-r--r--spec/ruby/core/math/tan_spec.rb42
-rw-r--r--spec/ruby/core/math/tanh_spec.rb39
-rw-r--r--spec/ruby/core/method/arity_spec.rb222
-rw-r--r--spec/ruby/core/method/call_spec.rb7
-rw-r--r--spec/ruby/core/method/case_compare_spec.rb9
-rw-r--r--spec/ruby/core/method/clone_spec.rb14
-rw-r--r--spec/ruby/core/method/curry_spec.rb36
-rw-r--r--spec/ruby/core/method/element_reference_spec.rb7
-rw-r--r--spec/ruby/core/method/eql_spec.rb6
-rw-r--r--spec/ruby/core/method/equal_value_spec.rb6
-rw-r--r--spec/ruby/core/method/fixtures/classes.rb184
-rw-r--r--spec/ruby/core/method/hash_spec.rb17
-rw-r--r--spec/ruby/core/method/inspect_spec.rb6
-rw-r--r--spec/ruby/core/method/name_spec.rb22
-rw-r--r--spec/ruby/core/method/owner_spec.rb26
-rw-r--r--spec/ruby/core/method/parameters_spec.rb244
-rw-r--r--spec/ruby/core/method/receiver_spec.rb22
-rw-r--r--spec/ruby/core/method/shared/call.rb51
-rw-r--r--spec/ruby/core/method/shared/eql.rb94
-rw-r--r--spec/ruby/core/method/shared/to_s.rb52
-rw-r--r--spec/ruby/core/method/source_location_spec.rb95
-rw-r--r--spec/ruby/core/method/super_method_spec.rb45
-rw-r--r--spec/ruby/core/method/to_proc_spec.rb93
-rw-r--r--spec/ruby/core/method/to_s_spec.rb6
-rw-r--r--spec/ruby/core/method/unbind_spec.rb37
-rw-r--r--spec/ruby/core/module/alias_method_spec.rb157
-rw-r--r--spec/ruby/core/module/allocate_spec.rb14
-rw-r--r--spec/ruby/core/module/ancestors_spec.rb70
-rw-r--r--spec/ruby/core/module/append_features_spec.rb73
-rw-r--r--spec/ruby/core/module/attr_accessor_spec.rb97
-rw-r--r--spec/ruby/core/module/attr_reader_spec.rb71
-rw-r--r--spec/ruby/core/module/attr_spec.rb156
-rw-r--r--spec/ruby/core/module/attr_writer_spec.rb71
-rw-r--r--spec/ruby/core/module/autoload_spec.rb830
-rw-r--r--spec/ruby/core/module/case_compare_spec.rb31
-rw-r--r--spec/ruby/core/module/class_eval_spec.rb7
-rw-r--r--spec/ruby/core/module/class_exec_spec.rb7
-rw-r--r--spec/ruby/core/module/class_variable_defined_spec.rb72
-rw-r--r--spec/ruby/core/module/class_variable_get_spec.rb76
-rw-r--r--spec/ruby/core/module/class_variable_set_spec.rb62
-rw-r--r--spec/ruby/core/module/class_variables_spec.rb26
-rw-r--r--spec/ruby/core/module/comparison_spec.rb36
-rw-r--r--spec/ruby/core/module/const_defined_spec.rb144
-rw-r--r--spec/ruby/core/module/const_get_spec.rb229
-rw-r--r--spec/ruby/core/module/const_missing_spec.rb36
-rw-r--r--spec/ruby/core/module/const_set_spec.rb86
-rw-r--r--spec/ruby/core/module/constants_spec.rb91
-rw-r--r--spec/ruby/core/module/define_method_spec.rb656
-rw-r--r--spec/ruby/core/module/define_singleton_method_spec.rb17
-rw-r--r--spec/ruby/core/module/deprecate_constant_spec.rb50
-rw-r--r--spec/ruby/core/module/eql_spec.rb7
-rw-r--r--spec/ruby/core/module/equal_spec.rb7
-rw-r--r--spec/ruby/core/module/equal_value_spec.rb7
-rw-r--r--spec/ruby/core/module/extend_object_spec.rb68
-rw-r--r--spec/ruby/core/module/extended_spec.rb44
-rw-r--r--spec/ruby/core/module/fixtures/autoload.rb1
-rw-r--r--spec/ruby/core/module/fixtures/autoload_abc.rb11
-rw-r--r--spec/ruby/core/module/fixtures/autoload_c.rb11
-rw-r--r--spec/ruby/core/module/fixtures/autoload_callback.rb2
-rw-r--r--spec/ruby/core/module/fixtures/autoload_concur.rb9
-rw-r--r--spec/ruby/core/module/fixtures/autoload_d.rb11
-rw-r--r--spec/ruby/core/module/fixtures/autoload_during_autoload.rb7
-rw-r--r--spec/ruby/core/module/fixtures/autoload_during_require.rb4
-rw-r--r--spec/ruby/core/module/fixtures/autoload_e.rb7
-rw-r--r--spec/ruby/core/module/fixtures/autoload_empty.rb1
-rw-r--r--spec/ruby/core/module/fixtures/autoload_ex1.rb16
-rw-r--r--spec/ruby/core/module/fixtures/autoload_exception.rb3
-rw-r--r--spec/ruby/core/module/fixtures/autoload_f.rb7
-rw-r--r--spec/ruby/core/module/fixtures/autoload_g.rb7
-rw-r--r--spec/ruby/core/module/fixtures/autoload_h.rb7
-rw-r--r--spec/ruby/core/module/fixtures/autoload_i.rb5
-rw-r--r--spec/ruby/core/module/fixtures/autoload_j.rb3
-rw-r--r--spec/ruby/core/module/fixtures/autoload_k.rb7
-rw-r--r--spec/ruby/core/module/fixtures/autoload_lm.rb4
-rw-r--r--spec/ruby/core/module/fixtures/autoload_nested.rb8
-rw-r--r--spec/ruby/core/module/fixtures/autoload_never_set.rb1
-rw-r--r--spec/ruby/core/module/fixtures/autoload_o.rb2
-rw-r--r--spec/ruby/core/module/fixtures/autoload_r.rb4
-rw-r--r--spec/ruby/core/module/fixtures/autoload_raise.rb2
-rw-r--r--spec/ruby/core/module/fixtures/autoload_required_directly.rb7
-rw-r--r--spec/ruby/core/module/fixtures/autoload_required_directly_nested.rb1
-rw-r--r--spec/ruby/core/module/fixtures/autoload_s.rb5
-rw-r--r--spec/ruby/core/module/fixtures/autoload_subclass.rb11
-rw-r--r--spec/ruby/core/module/fixtures/autoload_t.rb3
-rw-r--r--spec/ruby/core/module/fixtures/autoload_v.rb7
-rw-r--r--spec/ruby/core/module/fixtures/autoload_w.rb2
-rw-r--r--spec/ruby/core/module/fixtures/autoload_w2.rb1
-rw-r--r--spec/ruby/core/module/fixtures/autoload_x.rb3
-rw-r--r--spec/ruby/core/module/fixtures/autoload_z.rb5
-rw-r--r--spec/ruby/core/module/fixtures/classes.rb605
-rw-r--r--spec/ruby/core/module/fixtures/constant_unicode.rb5
-rw-r--r--spec/ruby/core/module/fixtures/constants_autoload.rb6
-rw-r--r--spec/ruby/core/module/fixtures/constants_autoload_a.rb2
-rw-r--r--spec/ruby/core/module/fixtures/constants_autoload_b.rb2
-rw-r--r--spec/ruby/core/module/fixtures/constants_autoload_c.rb3
-rw-r--r--spec/ruby/core/module/fixtures/constants_autoload_d.rb4
-rw-r--r--spec/ruby/core/module/fixtures/module.rb4
-rw-r--r--spec/ruby/core/module/fixtures/name.rb10
-rw-r--r--spec/ruby/core/module/fixtures/path1/load_path.rb9
-rw-r--r--spec/ruby/core/module/fixtures/path2/load_path.rb1
-rw-r--r--spec/ruby/core/module/fixtures/refine.rb13
-rw-r--r--spec/ruby/core/module/fixtures/repeated_concurrent_autoload.rb8
-rw-r--r--spec/ruby/core/module/freeze_spec.rb6
-rw-r--r--spec/ruby/core/module/gt_spec.rb36
-rw-r--r--spec/ruby/core/module/gte_spec.rb33
-rw-r--r--spec/ruby/core/module/include_spec.rb270
-rw-r--r--spec/ruby/core/module/included_modules_spec.rb12
-rw-r--r--spec/ruby/core/module/included_spec.rb44
-rw-r--r--spec/ruby/core/module/initialize_copy_spec.rb18
-rw-r--r--spec/ruby/core/module/initialize_spec.rb18
-rw-r--r--spec/ruby/core/module/instance_method_spec.rb85
-rw-r--r--spec/ruby/core/module/instance_methods_spec.rb61
-rw-r--r--spec/ruby/core/module/lt_spec.rb36
-rw-r--r--spec/ruby/core/module/lte_spec.rb33
-rw-r--r--spec/ruby/core/module/method_added_spec.rb62
-rw-r--r--spec/ruby/core/module/method_defined_spec.rb49
-rw-r--r--spec/ruby/core/module/method_removed_spec.rb33
-rw-r--r--spec/ruby/core/module/method_undefined_spec.rb33
-rw-r--r--spec/ruby/core/module/module_eval_spec.rb7
-rw-r--r--spec/ruby/core/module/module_exec_spec.rb7
-rw-r--r--spec/ruby/core/module/module_function_spec.rb269
-rw-r--r--spec/ruby/core/module/name_spec.rb68
-rw-r--r--spec/ruby/core/module/nesting_spec.rb31
-rw-r--r--spec/ruby/core/module/new_spec.rb31
-rw-r--r--spec/ruby/core/module/prepend_features_spec.rb76
-rw-r--r--spec/ruby/core/module/prepend_spec.rb373
-rw-r--r--spec/ruby/core/module/prepended_spec.rb25
-rw-r--r--spec/ruby/core/module/private_class_method_spec.rb81
-rw-r--r--spec/ruby/core/module/private_constant_spec.rb32
-rw-r--r--spec/ruby/core/module/private_instance_methods_spec.rb54
-rw-r--r--spec/ruby/core/module/private_method_defined_spec.rb72
-rw-r--r--spec/ruby/core/module/private_spec.rb95
-rw-r--r--spec/ruby/core/module/protected_instance_methods_spec.rb57
-rw-r--r--spec/ruby/core/module/protected_method_defined_spec.rb72
-rw-r--r--spec/ruby/core/module/protected_spec.rb55
-rw-r--r--spec/ruby/core/module/public_class_method_spec.rb80
-rw-r--r--spec/ruby/core/module/public_constant_spec.rb38
-rw-r--r--spec/ruby/core/module/public_instance_method_spec.rb65
-rw-r--r--spec/ruby/core/module/public_instance_methods_spec.rb61
-rw-r--r--spec/ruby/core/module/public_method_defined_spec.rb72
-rw-r--r--spec/ruby/core/module/public_spec.rb44
-rw-r--r--spec/ruby/core/module/refine_spec.rb735
-rw-r--r--spec/ruby/core/module/remove_class_variable_spec.rb44
-rw-r--r--spec/ruby/core/module/remove_const_spec.rb84
-rw-r--r--spec/ruby/core/module/remove_method_spec.rb116
-rw-r--r--spec/ruby/core/module/shared/class_eval.rb115
-rw-r--r--spec/ruby/core/module/shared/class_exec.rb29
-rw-r--r--spec/ruby/core/module/shared/equal_value.rb14
-rw-r--r--spec/ruby/core/module/shared/set_visibility.rb135
-rw-r--r--spec/ruby/core/module/singleton_class_spec.rb27
-rw-r--r--spec/ruby/core/module/to_s_spec.rb18
-rw-r--r--spec/ruby/core/module/undef_method_spec.rb159
-rw-r--r--spec/ruby/core/module/using_spec.rb287
-rw-r--r--spec/ruby/core/mutex/lock_spec.rb34
-rw-r--r--spec/ruby/core/mutex/locked_spec.rb36
-rw-r--r--spec/ruby/core/mutex/owned_spec.rb43
-rw-r--r--spec/ruby/core/mutex/sleep_spec.rb95
-rw-r--r--spec/ruby/core/mutex/synchronize_spec.rb66
-rw-r--r--spec/ruby/core/mutex/try_lock_spec.rb32
-rw-r--r--spec/ruby/core/mutex/unlock_spec.rb38
-rw-r--r--spec/ruby/core/nil/and_spec.rb11
-rw-r--r--spec/ruby/core/nil/dup_spec.rb9
-rw-r--r--spec/ruby/core/nil/inspect_spec.rb7
-rw-r--r--spec/ruby/core/nil/nil_spec.rb7
-rw-r--r--spec/ruby/core/nil/nilclass_spec.rb15
-rw-r--r--spec/ruby/core/nil/or_spec.rb11
-rw-r--r--spec/ruby/core/nil/rationalize_spec.rb16
-rw-r--r--spec/ruby/core/nil/to_a_spec.rb7
-rw-r--r--spec/ruby/core/nil/to_c_spec.rb7
-rw-r--r--spec/ruby/core/nil/to_f_spec.rb11
-rw-r--r--spec/ruby/core/nil/to_h_spec.rb8
-rw-r--r--spec/ruby/core/nil/to_i_spec.rb11
-rw-r--r--spec/ruby/core/nil/to_r_spec.rb7
-rw-r--r--spec/ruby/core/nil/to_s_spec.rb7
-rw-r--r--spec/ruby/core/nil/xor_spec.rb11
-rw-r--r--spec/ruby/core/numeric/abs2_spec.rb34
-rw-r--r--spec/ruby/core/numeric/abs_spec.rb6
-rw-r--r--spec/ruby/core/numeric/angle_spec.rb6
-rw-r--r--spec/ruby/core/numeric/arg_spec.rb6
-rw-r--r--spec/ruby/core/numeric/ceil_spec.rb15
-rw-r--r--spec/ruby/core/numeric/coerce_spec.rb59
-rw-r--r--spec/ruby/core/numeric/comparison_spec.rb48
-rw-r--r--spec/ruby/core/numeric/conj_spec.rb6
-rw-r--r--spec/ruby/core/numeric/conjugate_spec.rb6
-rw-r--r--spec/ruby/core/numeric/denominator_spec.rb24
-rw-r--r--spec/ruby/core/numeric/div_spec.rb22
-rw-r--r--spec/ruby/core/numeric/divmod_spec.rb15
-rw-r--r--spec/ruby/core/numeric/eql_spec.rb22
-rw-r--r--spec/ruby/core/numeric/fdiv_spec.rb32
-rw-r--r--spec/ruby/core/numeric/finite_spec.rb10
-rw-r--r--spec/ruby/core/numeric/fixtures/classes.rb17
-rw-r--r--spec/ruby/core/numeric/floor_spec.rb14
-rw-r--r--spec/ruby/core/numeric/i_spec.rb15
-rw-r--r--spec/ruby/core/numeric/imag_spec.rb6
-rw-r--r--spec/ruby/core/numeric/imaginary_spec.rb6
-rw-r--r--spec/ruby/core/numeric/infinite_spec.rb10
-rw-r--r--spec/ruby/core/numeric/integer_spec.rb8
-rw-r--r--spec/ruby/core/numeric/magnitude_spec.rb5
-rw-r--r--spec/ruby/core/numeric/modulo_spec.rb24
-rw-r--r--spec/ruby/core/numeric/negative_spec.rb41
-rw-r--r--spec/ruby/core/numeric/nonzero_spec.rb18
-rw-r--r--spec/ruby/core/numeric/numerator_spec.rb33
-rw-r--r--spec/ruby/core/numeric/numeric_spec.rb7
-rw-r--r--spec/ruby/core/numeric/phase_spec.rb6
-rw-r--r--spec/ruby/core/numeric/polar_spec.rb50
-rw-r--r--spec/ruby/core/numeric/positive_spec.rb41
-rw-r--r--spec/ruby/core/numeric/quo_spec.rb55
-rw-r--r--spec/ruby/core/numeric/real_spec.rb37
-rw-r--r--spec/ruby/core/numeric/rect_spec.rb6
-rw-r--r--spec/ruby/core/numeric/rectangular_spec.rb6
-rw-r--r--spec/ruby/core/numeric/remainder_spec.rb67
-rw-r--r--spec/ruby/core/numeric/round_spec.rb14
-rw-r--r--spec/ruby/core/numeric/shared/abs.rb19
-rw-r--r--spec/ruby/core/numeric/shared/arg.rb38
-rw-r--r--spec/ruby/core/numeric/shared/conj.rb20
-rw-r--r--spec/ruby/core/numeric/shared/imag.rb26
-rw-r--r--spec/ruby/core/numeric/shared/quo.rb7
-rw-r--r--spec/ruby/core/numeric/shared/rect.rb48
-rw-r--r--spec/ruby/core/numeric/shared/step.rb430
-rw-r--r--spec/ruby/core/numeric/singleton_method_added_spec.rb41
-rw-r--r--spec/ruby/core/numeric/step_spec.rb182
-rw-r--r--spec/ruby/core/numeric/to_c_spec.rb45
-rw-r--r--spec/ruby/core/numeric/to_int_spec.rb10
-rw-r--r--spec/ruby/core/numeric/truncate_spec.rb14
-rw-r--r--spec/ruby/core/numeric/uminus_spec.rb31
-rw-r--r--spec/ruby/core/numeric/uplus_spec.rb9
-rw-r--r--spec/ruby/core/numeric/zero_spec.rb18
-rw-r--r--spec/ruby/core/objectspace/_id2ref_spec.rb25
-rw-r--r--spec/ruby/core/objectspace/add_finalizer_spec.rb5
-rw-r--r--spec/ruby/core/objectspace/call_finalizer_spec.rb5
-rw-r--r--spec/ruby/core/objectspace/count_objects_spec.rb5
-rw-r--r--spec/ruby/core/objectspace/define_finalizer_spec.rb101
-rw-r--r--spec/ruby/core/objectspace/each_object_spec.rb214
-rw-r--r--spec/ruby/core/objectspace/finalizers_spec.rb5
-rw-r--r--spec/ruby/core/objectspace/fixtures/classes.rb64
-rw-r--r--spec/ruby/core/objectspace/garbage_collect_spec.rb22
-rw-r--r--spec/ruby/core/objectspace/remove_finalizer_spec.rb5
-rw-r--r--spec/ruby/core/objectspace/undefine_finalizer_spec.rb5
-rw-r--r--spec/ruby/core/proc/allocate_spec.rb9
-rw-r--r--spec/ruby/core/proc/arity_spec.rb640
-rw-r--r--spec/ruby/core/proc/binding_spec.rb21
-rw-r--r--spec/ruby/core/proc/block_pass_spec.rb41
-rw-r--r--spec/ruby/core/proc/call_spec.rb16
-rw-r--r--spec/ruby/core/proc/case_compare_spec.rb16
-rw-r--r--spec/ruby/core/proc/clone_spec.rb6
-rw-r--r--spec/ruby/core/proc/curry_spec.rb180
-rw-r--r--spec/ruby/core/proc/dup_spec.rb6
-rw-r--r--spec/ruby/core/proc/element_reference_spec.rb29
-rw-r--r--spec/ruby/core/proc/eql_spec.rb6
-rw-r--r--spec/ruby/core/proc/equal_value_spec.rb6
-rw-r--r--spec/ruby/core/proc/fixtures/common.rb51
-rw-r--r--spec/ruby/core/proc/fixtures/proc_aref.rb9
-rw-r--r--spec/ruby/core/proc/fixtures/proc_aref_frozen.rb10
-rw-r--r--spec/ruby/core/proc/fixtures/source_location.rb55
-rw-r--r--spec/ruby/core/proc/hash_spec.rb17
-rw-r--r--spec/ruby/core/proc/inspect_spec.rb6
-rw-r--r--spec/ruby/core/proc/lambda_spec.rb60
-rw-r--r--spec/ruby/core/proc/new_spec.rb190
-rw-r--r--spec/ruby/core/proc/parameters_spec.rb95
-rw-r--r--spec/ruby/core/proc/shared/call.rb96
-rw-r--r--spec/ruby/core/proc/shared/call_arguments.rb7
-rw-r--r--spec/ruby/core/proc/shared/dup.rb10
-rw-r--r--spec/ruby/core/proc/shared/equal.rb100
-rw-r--r--spec/ruby/core/proc/shared/to_s.rb45
-rw-r--r--spec/ruby/core/proc/source_location_spec.rb86
-rw-r--r--spec/ruby/core/proc/to_proc_spec.rb9
-rw-r--r--spec/ruby/core/proc/to_s_spec.rb6
-rw-r--r--spec/ruby/core/proc/yield_spec.rb16
-rw-r--r--spec/ruby/core/process/abort_spec.rb6
-rw-r--r--spec/ruby/core/process/clock_gettime_spec.rb35
-rw-r--r--spec/ruby/core/process/constants_spec.rb63
-rw-r--r--spec/ruby/core/process/daemon_spec.rb123
-rw-r--r--spec/ruby/core/process/detach_spec.rb46
-rw-r--r--spec/ruby/core/process/egid_spec.rb19
-rw-r--r--spec/ruby/core/process/euid_spec.rb59
-rw-r--r--spec/ruby/core/process/exec_spec.rb218
-rw-r--r--spec/ruby/core/process/exit_spec.rb10
-rw-r--r--spec/ruby/core/process/fixtures/common.rb84
-rw-r--r--spec/ruby/core/process/fixtures/daemon.rb111
-rw-r--r--spec/ruby/core/process/fixtures/kill.rb45
-rw-r--r--spec/ruby/core/process/fixtures/map_fd.rb9
-rw-r--r--spec/ruby/core/process/fixtures/setpriority.rb12
-rw-r--r--spec/ruby/core/process/fork_spec.rb6
-rw-r--r--spec/ruby/core/process/getpgid_spec.rb17
-rw-r--r--spec/ruby/core/process/getpgrp_spec.rb7
-rw-r--r--spec/ruby/core/process/getpriority_spec.rb23
-rw-r--r--spec/ruby/core/process/getrlimit_spec.rb91
-rw-r--r--spec/ruby/core/process/gid/change_privilege_spec.rb5
-rw-r--r--spec/ruby/core/process/gid/eid_spec.rb9
-rw-r--r--spec/ruby/core/process/gid/grant_privilege_spec.rb5
-rw-r--r--spec/ruby/core/process/gid/re_exchange_spec.rb5
-rw-r--r--spec/ruby/core/process/gid/re_exchangeable_spec.rb5
-rw-r--r--spec/ruby/core/process/gid/rid_spec.rb5
-rw-r--r--spec/ruby/core/process/gid/sid_available_spec.rb5
-rw-r--r--spec/ruby/core/process/gid/switch_spec.rb5
-rw-r--r--spec/ruby/core/process/gid_spec.rb22
-rw-r--r--spec/ruby/core/process/groups_spec.rb63
-rw-r--r--spec/ruby/core/process/initgroups_spec.rb22
-rw-r--r--spec/ruby/core/process/kill_spec.rb128
-rw-r--r--spec/ruby/core/process/last_status_spec.rb20
-rw-r--r--spec/ruby/core/process/maxgroups_spec.rb19
-rw-r--r--spec/ruby/core/process/pid_spec.rb9
-rw-r--r--spec/ruby/core/process/ppid_spec.rb23
-rw-r--r--spec/ruby/core/process/set_proctitle_spec.rb23
-rw-r--r--spec/ruby/core/process/setpgid_spec.rb28
-rw-r--r--spec/ruby/core/process/setpgrp_spec.rb37
-rw-r--r--spec/ruby/core/process/setpriority_spec.rb41
-rw-r--r--spec/ruby/core/process/setrlimit_spec.rb232
-rw-r--r--spec/ruby/core/process/setsid_spec.rb37
-rw-r--r--spec/ruby/core/process/spawn_spec.rb636
-rw-r--r--spec/ruby/core/process/status/bit_and_spec.rb5
-rw-r--r--spec/ruby/core/process/status/coredump_spec.rb5
-rw-r--r--spec/ruby/core/process/status/equal_value_spec.rb5
-rw-r--r--spec/ruby/core/process/status/exited_spec.rb37
-rw-r--r--spec/ruby/core/process/status/exitstatus_spec.rb25
-rw-r--r--spec/ruby/core/process/status/inspect_spec.rb5
-rw-r--r--spec/ruby/core/process/status/pid_spec.rb15
-rw-r--r--spec/ruby/core/process/status/right_shift_spec.rb5
-rw-r--r--spec/ruby/core/process/status/signaled_spec.rb35
-rw-r--r--spec/ruby/core/process/status/stopped_spec.rb5
-rw-r--r--spec/ruby/core/process/status/stopsig_spec.rb5
-rw-r--r--spec/ruby/core/process/status/success_spec.rb51
-rw-r--r--spec/ruby/core/process/status/termsig_spec.rb51
-rw-r--r--spec/ruby/core/process/status/to_i_spec.rb5
-rw-r--r--spec/ruby/core/process/status/to_int_spec.rb5
-rw-r--r--spec/ruby/core/process/status/to_s_spec.rb5
-rw-r--r--spec/ruby/core/process/sys/getegid_spec.rb5
-rw-r--r--spec/ruby/core/process/sys/geteuid_spec.rb5
-rw-r--r--spec/ruby/core/process/sys/getgid_spec.rb5
-rw-r--r--spec/ruby/core/process/sys/getuid_spec.rb5
-rw-r--r--spec/ruby/core/process/sys/issetugid_spec.rb5
-rw-r--r--spec/ruby/core/process/sys/setegid_spec.rb5
-rw-r--r--spec/ruby/core/process/sys/seteuid_spec.rb5
-rw-r--r--spec/ruby/core/process/sys/setgid_spec.rb5
-rw-r--r--spec/ruby/core/process/sys/setregid_spec.rb5
-rw-r--r--spec/ruby/core/process/sys/setresgid_spec.rb5
-rw-r--r--spec/ruby/core/process/sys/setresuid_spec.rb5
-rw-r--r--spec/ruby/core/process/sys/setreuid_spec.rb5
-rw-r--r--spec/ruby/core/process/sys/setrgid_spec.rb5
-rw-r--r--spec/ruby/core/process/sys/setruid_spec.rb5
-rw-r--r--spec/ruby/core/process/sys/setuid_spec.rb5
-rw-r--r--spec/ruby/core/process/times_spec.rb27
-rw-r--r--spec/ruby/core/process/tms/cstime_spec.rb9
-rw-r--r--spec/ruby/core/process/tms/cutime_spec.rb9
-rw-r--r--spec/ruby/core/process/tms/element_reference_spec.rb5
-rw-r--r--spec/ruby/core/process/tms/members_spec.rb5
-rw-r--r--spec/ruby/core/process/tms/new_spec.rb5
-rw-r--r--spec/ruby/core/process/tms/stime_spec.rb9
-rw-r--r--spec/ruby/core/process/tms/utime_spec.rb9
-rw-r--r--spec/ruby/core/process/uid/change_privilege_spec.rb5
-rw-r--r--spec/ruby/core/process/uid/eid_spec.rb9
-rw-r--r--spec/ruby/core/process/uid/grant_privilege_spec.rb5
-rw-r--r--spec/ruby/core/process/uid/re_exchange_spec.rb5
-rw-r--r--spec/ruby/core/process/uid/re_exchangeable_spec.rb5
-rw-r--r--spec/ruby/core/process/uid/rid_spec.rb5
-rw-r--r--spec/ruby/core/process/uid/sid_available_spec.rb5
-rw-r--r--spec/ruby/core/process/uid/switch_spec.rb5
-rw-r--r--spec/ruby/core/process/uid_spec.rb84
-rw-r--r--spec/ruby/core/process/wait2_spec.rb40
-rw-r--r--spec/ruby/core/process/wait_spec.rb94
-rw-r--r--spec/ruby/core/process/waitall_spec.rb48
-rw-r--r--spec/ruby/core/process/waitpid2_spec.rb5
-rw-r--r--spec/ruby/core/process/waitpid_spec.rb15
-rw-r--r--spec/ruby/core/queue/append_spec.rb6
-rw-r--r--spec/ruby/core/queue/clear_spec.rb6
-rw-r--r--spec/ruby/core/queue/close_spec.rb6
-rw-r--r--spec/ruby/core/queue/closed_spec.rb6
-rw-r--r--spec/ruby/core/queue/deq_spec.rb6
-rw-r--r--spec/ruby/core/queue/empty_spec.rb6
-rw-r--r--spec/ruby/core/queue/enq_spec.rb6
-rw-r--r--spec/ruby/core/queue/length_spec.rb6
-rw-r--r--spec/ruby/core/queue/num_waiting_spec.rb6
-rw-r--r--spec/ruby/core/queue/pop_spec.rb6
-rw-r--r--spec/ruby/core/queue/push_spec.rb6
-rw-r--r--spec/ruby/core/queue/shift_spec.rb6
-rw-r--r--spec/ruby/core/queue/size_spec.rb6
-rw-r--r--spec/ruby/core/random/bytes_spec.rb32
-rw-r--r--spec/ruby/core/random/default_spec.rb7
-rw-r--r--spec/ruby/core/random/equal_value_spec.rb37
-rw-r--r--spec/ruby/core/random/new_seed_spec.rb24
-rw-r--r--spec/ruby/core/random/new_spec.rb37
-rw-r--r--spec/ruby/core/random/rand_spec.rb216
-rw-r--r--spec/ruby/core/random/raw_seed_spec.rb9
-rw-r--r--spec/ruby/core/random/seed_spec.rb29
-rw-r--r--spec/ruby/core/random/shared/bytes.rb17
-rw-r--r--spec/ruby/core/random/shared/urandom.rb23
-rw-r--r--spec/ruby/core/random/srand_spec.rb39
-rw-r--r--spec/ruby/core/random/urandom_spec.rb9
-rw-r--r--spec/ruby/core/range/begin_spec.rb6
-rw-r--r--spec/ruby/core/range/bsearch_spec.rb137
-rw-r--r--spec/ruby/core/range/case_compare_spec.rb20
-rw-r--r--spec/ruby/core/range/cover_spec.rb9
-rw-r--r--spec/ruby/core/range/dup_spec.rb15
-rw-r--r--spec/ruby/core/range/each_spec.rb66
-rw-r--r--spec/ruby/core/range/end_spec.rb6
-rw-r--r--spec/ruby/core/range/eql_spec.rb10
-rw-r--r--spec/ruby/core/range/equal_value_spec.rb10
-rw-r--r--spec/ruby/core/range/exclude_end_spec.rb19
-rw-r--r--spec/ruby/core/range/first_spec.rb49
-rw-r--r--spec/ruby/core/range/fixtures/classes.rb68
-rw-r--r--spec/ruby/core/range/hash_spec.rb24
-rw-r--r--spec/ruby/core/range/include_spec.rb10
-rw-r--r--spec/ruby/core/range/initialize_spec.rb41
-rw-r--r--spec/ruby/core/range/inspect_spec.rb26
-rw-r--r--spec/ruby/core/range/last_spec.rb49
-rw-r--r--spec/ruby/core/range/max_spec.rb82
-rw-r--r--spec/ruby/core/range/member_spec.rb10
-rw-r--r--spec/ruby/core/range/min_spec.rb75
-rw-r--r--spec/ruby/core/range/new_spec.rb45
-rw-r--r--spec/ruby/core/range/range_spec.rb7
-rw-r--r--spec/ruby/core/range/shared/begin.rb10
-rw-r--r--spec/ruby/core/range/shared/cover.rb93
-rw-r--r--spec/ruby/core/range/shared/cover_and_include.rb66
-rw-r--r--spec/ruby/core/range/shared/end.rb10
-rw-r--r--spec/ruby/core/range/shared/equal_value.rb45
-rw-r--r--spec/ruby/core/range/shared/include.rb91
-rw-r--r--spec/ruby/core/range/size_spec.rb31
-rw-r--r--spec/ruby/core/range/step_spec.rb368
-rw-r--r--spec/ruby/core/range/to_a_spec.rb22
-rw-r--r--spec/ruby/core/range/to_s_spec.rb25
-rw-r--r--spec/ruby/core/rational/abs_spec.rb5
-rw-r--r--spec/ruby/core/rational/ceil_spec.rb5
-rw-r--r--spec/ruby/core/rational/coerce_spec.rb5
-rw-r--r--spec/ruby/core/rational/comparison_spec.rb22
-rw-r--r--spec/ruby/core/rational/denominator_spec.rb5
-rw-r--r--spec/ruby/core/rational/div_spec.rb17
-rw-r--r--spec/ruby/core/rational/divide_spec.rb19
-rw-r--r--spec/ruby/core/rational/divmod_spec.rb13
-rw-r--r--spec/ruby/core/rational/equal_value_spec.rb17
-rw-r--r--spec/ruby/core/rational/exponent_spec.rb5
-rw-r--r--spec/ruby/core/rational/fdiv_spec.rb5
-rw-r--r--spec/ruby/core/rational/floor_spec.rb5
-rw-r--r--spec/ruby/core/rational/hash_spec.rb5
-rw-r--r--spec/ruby/core/rational/inspect_spec.rb5
-rw-r--r--spec/ruby/core/rational/integer_spec.rb12
-rw-r--r--spec/ruby/core/rational/magnitude_spec.rb5
-rw-r--r--spec/ruby/core/rational/marshal_dump_spec.rb11
-rw-r--r--spec/ruby/core/rational/minus_spec.rb7
-rw-r--r--spec/ruby/core/rational/modulo_spec.rb5
-rw-r--r--spec/ruby/core/rational/multiply_spec.rb19
-rw-r--r--spec/ruby/core/rational/numerator_spec.rb5
-rw-r--r--spec/ruby/core/rational/plus_spec.rb18
-rw-r--r--spec/ruby/core/rational/quo_spec.rb5
-rw-r--r--spec/ruby/core/rational/rational_spec.rb7
-rw-r--r--spec/ruby/core/rational/rationalize_spec.rb36
-rw-r--r--spec/ruby/core/rational/remainder_spec.rb5
-rw-r--r--spec/ruby/core/rational/round_spec.rb5
-rw-r--r--spec/ruby/core/rational/to_f_spec.rb5
-rw-r--r--spec/ruby/core/rational/to_i_spec.rb5
-rw-r--r--spec/ruby/core/rational/to_r_spec.rb20
-rw-r--r--spec/ruby/core/rational/to_s_spec.rb5
-rw-r--r--spec/ruby/core/rational/truncate_spec.rb5
-rw-r--r--spec/ruby/core/rational/zero_spec.rb13
-rw-r--r--spec/ruby/core/regexp/case_compare_spec.rb25
-rw-r--r--spec/ruby/core/regexp/casefold_spec.rb8
-rw-r--r--spec/ruby/core/regexp/compile_spec.rb18
-rw-r--r--spec/ruby/core/regexp/encoding_spec.rb58
-rw-r--r--spec/ruby/core/regexp/eql_spec.rb6
-rw-r--r--spec/ruby/core/regexp/equal_value_spec.rb6
-rw-r--r--spec/ruby/core/regexp/escape_spec.rb6
-rw-r--r--spec/ruby/core/regexp/fixed_encoding_spec.rb36
-rw-r--r--spec/ruby/core/regexp/hash_spec.rb20
-rw-r--r--spec/ruby/core/regexp/initialize_spec.rb15
-rw-r--r--spec/ruby/core/regexp/inspect_spec.rb44
-rw-r--r--spec/ruby/core/regexp/last_match_spec.rb14
-rw-r--r--spec/ruby/core/regexp/match_spec.rb148
-rw-r--r--spec/ruby/core/regexp/named_captures_spec.rb35
-rw-r--r--spec/ruby/core/regexp/names_spec.rb29
-rw-r--r--spec/ruby/core/regexp/new_spec.rb30
-rw-r--r--spec/ruby/core/regexp/options_spec.rb54
-rw-r--r--spec/ruby/core/regexp/quote_spec.rb6
-rw-r--r--spec/ruby/core/regexp/shared/equal_value.rb31
-rw-r--r--spec/ruby/core/regexp/shared/new_ascii.rb464
-rw-r--r--spec/ruby/core/regexp/shared/new_ascii_8bit.rb553
-rw-r--r--spec/ruby/core/regexp/shared/quote.rb31
-rw-r--r--spec/ruby/core/regexp/source_spec.rb29
-rw-r--r--spec/ruby/core/regexp/to_s_spec.rb62
-rw-r--r--spec/ruby/core/regexp/try_convert_spec.rb21
-rw-r--r--spec/ruby/core/regexp/union_spec.rb149
-rw-r--r--spec/ruby/core/signal/fixtures/trap_all.rb8
-rw-r--r--spec/ruby/core/signal/list_spec.rb68
-rw-r--r--spec/ruby/core/signal/signame_spec.rb22
-rw-r--r--spec/ruby/core/signal/trap_spec.rb203
-rw-r--r--spec/ruby/core/sizedqueue/append_spec.rb11
-rw-r--r--spec/ruby/core/sizedqueue/clear_spec.rb6
-rw-r--r--spec/ruby/core/sizedqueue/close_spec.rb6
-rw-r--r--spec/ruby/core/sizedqueue/closed_spec.rb6
-rw-r--r--spec/ruby/core/sizedqueue/deq_spec.rb6
-rw-r--r--spec/ruby/core/sizedqueue/empty_spec.rb6
-rw-r--r--spec/ruby/core/sizedqueue/enq_spec.rb11
-rw-r--r--spec/ruby/core/sizedqueue/length_spec.rb6
-rw-r--r--spec/ruby/core/sizedqueue/max_spec.rb10
-rw-r--r--spec/ruby/core/sizedqueue/new_spec.rb6
-rw-r--r--spec/ruby/core/sizedqueue/num_waiting_spec.rb6
-rw-r--r--spec/ruby/core/sizedqueue/pop_spec.rb6
-rw-r--r--spec/ruby/core/sizedqueue/push_spec.rb11
-rw-r--r--spec/ruby/core/sizedqueue/shift_spec.rb6
-rw-r--r--spec/ruby/core/sizedqueue/size_spec.rb6
-rw-r--r--spec/ruby/core/string/allocate_spec.rb19
-rw-r--r--spec/ruby/core/string/append_spec.rb8
-rw-r--r--spec/ruby/core/string/ascii_only_spec.rb85
-rw-r--r--spec/ruby/core/string/b_spec.rb24
-rw-r--r--spec/ruby/core/string/bytes_spec.rb57
-rw-r--r--spec/ruby/core/string/bytesize_spec.rb37
-rw-r--r--spec/ruby/core/string/byteslice_spec.rb29
-rw-r--r--spec/ruby/core/string/capitalize_spec.rb197
-rw-r--r--spec/ruby/core/string/case_compare_spec.rb8
-rw-r--r--spec/ruby/core/string/casecmp_spec.rb216
-rw-r--r--spec/ruby/core/string/center_spec.rb133
-rw-r--r--spec/ruby/core/string/chars_spec.rb10
-rw-r--r--spec/ruby/core/string/chomp_spec.rb387
-rw-r--r--spec/ruby/core/string/chop_spec.rb128
-rw-r--r--spec/ruby/core/string/chr_spec.rb44
-rw-r--r--spec/ruby/core/string/clear_spec.rb39
-rw-r--r--spec/ruby/core/string/clone_spec.rb57
-rw-r--r--spec/ruby/core/string/codepoints_spec.rb20
-rw-r--r--spec/ruby/core/string/comparison_spec.rb108
-rw-r--r--spec/ruby/core/string/concat_spec.rb28
-rw-r--r--spec/ruby/core/string/count_spec.rb105
-rw-r--r--spec/ruby/core/string/crypt_spec.rb73
-rw-r--r--spec/ruby/core/string/delete_prefix_spec.rb81
-rw-r--r--spec/ruby/core/string/delete_spec.rb119
-rw-r--r--spec/ruby/core/string/delete_suffix_spec.rb81
-rw-r--r--spec/ruby/core/string/downcase_spec.rb200
-rw-r--r--spec/ruby/core/string/dump_spec.rb439
-rw-r--r--spec/ruby/core/string/dup_spec.rb52
-rw-r--r--spec/ruby/core/string/each_byte_spec.rb61
-rw-r--r--spec/ruby/core/string/each_char_spec.rb7
-rw-r--r--spec/ruby/core/string/each_codepoint_spec.rb10
-rw-r--r--spec/ruby/core/string/each_grapheme_cluster_spec.rb11
-rw-r--r--spec/ruby/core/string/each_line_spec.rb9
-rw-r--r--spec/ruby/core/string/element_reference_spec.rb35
-rw-r--r--spec/ruby/core/string/element_set_spec.rb612
-rw-r--r--spec/ruby/core/string/empty_spec.rb12
-rw-r--r--spec/ruby/core/string/encode_spec.rb159
-rw-r--r--spec/ruby/core/string/encoding_spec.rb189
-rw-r--r--spec/ruby/core/string/end_with_spec.rb50
-rw-r--r--spec/ruby/core/string/eql_spec.rb21
-rw-r--r--spec/ruby/core/string/equal_value_spec.rb8
-rw-r--r--spec/ruby/core/string/fixtures/classes.rb49
-rw-r--r--spec/ruby/core/string/fixtures/freeze_magic_comment.rb3
-rw-r--r--spec/ruby/core/string/fixtures/iso-8859-9-encoding.rb9
-rw-r--r--spec/ruby/core/string/fixtures/utf-8-encoding.rb7
-rw-r--r--spec/ruby/core/string/force_encoding_spec.rb73
-rw-r--r--spec/ruby/core/string/freeze_spec.rb17
-rw-r--r--spec/ruby/core/string/getbyte_spec.rb69
-rw-r--r--spec/ruby/core/string/grapheme_clusters_spec.rb15
-rw-r--r--spec/ruby/core/string/gsub_spec.rb696
-rw-r--r--spec/ruby/core/string/hash_spec.rb9
-rw-r--r--spec/ruby/core/string/hex_spec.rb49
-rw-r--r--spec/ruby/core/string/include_spec.rb35
-rw-r--r--spec/ruby/core/string/index_spec.rb316
-rw-r--r--spec/ruby/core/string/initialize_spec.rb26
-rw-r--r--spec/ruby/core/string/insert_spec.rb84
-rw-r--r--spec/ruby/core/string/inspect_spec.rb492
-rw-r--r--spec/ruby/core/string/intern_spec.rb7
-rw-r--r--spec/ruby/core/string/length_spec.rb7
-rw-r--r--spec/ruby/core/string/lines_spec.rb22
-rw-r--r--spec/ruby/core/string/ljust_spec.rb116
-rw-r--r--spec/ruby/core/string/lstrip_spec.rb50
-rw-r--r--spec/ruby/core/string/match_spec.rb175
-rw-r--r--spec/ruby/core/string/modulo_spec.rb807
-rw-r--r--spec/ruby/core/string/multiply_spec.rb7
-rw-r--r--spec/ruby/core/string/new_spec.rb63
-rw-r--r--spec/ruby/core/string/next_spec.rb11
-rw-r--r--spec/ruby/core/string/oct_spec.rb88
-rw-r--r--spec/ruby/core/string/ord_spec.rb30
-rw-r--r--spec/ruby/core/string/partition_spec.rb38
-rw-r--r--spec/ruby/core/string/percent_spec.rb13
-rw-r--r--spec/ruby/core/string/plus_spec.rb47
-rw-r--r--spec/ruby/core/string/prepend_spec.rb64
-rw-r--r--spec/ruby/core/string/replace_spec.rb7
-rw-r--r--spec/ruby/core/string/reverse_spec.rb52
-rw-r--r--spec/ruby/core/string/rindex_spec.rb368
-rw-r--r--spec/ruby/core/string/rjust_spec.rb116
-rw-r--r--spec/ruby/core/string/rpartition_spec.rb33
-rw-r--r--spec/ruby/core/string/rstrip_spec.rb52
-rw-r--r--spec/ruby/core/string/scan_spec.rb192
-rw-r--r--spec/ruby/core/string/scrub_spec.rb101
-rw-r--r--spec/ruby/core/string/setbyte_spec.rb105
-rw-r--r--spec/ruby/core/string/shared/chars.rb80
-rw-r--r--spec/ruby/core/string/shared/codepoints.rb56
-rw-r--r--spec/ruby/core/string/shared/concat.rb160
-rw-r--r--spec/ruby/core/string/shared/each_char_without_block.rb26
-rw-r--r--spec/ruby/core/string/shared/each_codepoint_without_block.rb33
-rw-r--r--spec/ruby/core/string/shared/each_line.rb165
-rw-r--r--spec/ruby/core/string/shared/each_line_without_block.rb17
-rw-r--r--spec/ruby/core/string/shared/encode.rb247
-rw-r--r--spec/ruby/core/string/shared/eql.rb34
-rw-r--r--spec/ruby/core/string/shared/equal_value.rb29
-rw-r--r--spec/ruby/core/string/shared/grapheme_clusters.rb16
-rw-r--r--spec/ruby/core/string/shared/length.rb28
-rw-r--r--spec/ruby/core/string/shared/replace.rb75
-rw-r--r--spec/ruby/core/string/shared/slice.rb557
-rw-r--r--spec/ruby/core/string/shared/succ.rb88
-rw-r--r--spec/ruby/core/string/shared/to_a.rb9
-rw-r--r--spec/ruby/core/string/shared/to_s.rb18
-rw-r--r--spec/ruby/core/string/shared/to_sym.rb63
-rw-r--r--spec/ruby/core/string/size_spec.rb7
-rw-r--r--spec/ruby/core/string/slice_spec.rb476
-rw-r--r--spec/ruby/core/string/split_spec.rb415
-rw-r--r--spec/ruby/core/string/squeeze_spec.rb113
-rw-r--r--spec/ruby/core/string/start_with_spec.rb76
-rw-r--r--spec/ruby/core/string/string_spec.rb7
-rw-r--r--spec/ruby/core/string/strip_spec.rb60
-rw-r--r--spec/ruby/core/string/sub_spec.rb571
-rw-r--r--spec/ruby/core/string/succ_spec.rb11
-rw-r--r--spec/ruby/core/string/sum_spec.rb22
-rw-r--r--spec/ruby/core/string/swapcase_spec.rb183
-rw-r--r--spec/ruby/core/string/to_c_spec.rb99
-rw-r--r--spec/ruby/core/string/to_f_spec.rb69
-rw-r--r--spec/ruby/core/string/to_i_spec.rb337
-rw-r--r--spec/ruby/core/string/to_r_spec.rb58
-rw-r--r--spec/ruby/core/string/to_s_spec.rb7
-rw-r--r--spec/ruby/core/string/to_str_spec.rb7
-rw-r--r--spec/ruby/core/string/to_sym_spec.rb7
-rw-r--r--spec/ruby/core/string/tr_s_spec.rb136
-rw-r--r--spec/ruby/core/string/tr_spec.rb131
-rw-r--r--spec/ruby/core/string/try_convert_spec.rb50
-rw-r--r--spec/ruby/core/string/uminus_spec.rb57
-rw-r--r--spec/ruby/core/string/undump_spec.rb451
-rw-r--r--spec/ruby/core/string/unicode_normalize_spec.rb115
-rw-r--r--spec/ruby/core/string/unicode_normalized_spec.rb74
-rw-r--r--spec/ruby/core/string/unpack/a_spec.rb66
-rw-r--r--spec/ruby/core/string/unpack/at_spec.rb29
-rw-r--r--spec/ruby/core/string/unpack/b_spec.rb193
-rw-r--r--spec/ruby/core/string/unpack/c_spec.rb63
-rw-r--r--spec/ruby/core/string/unpack/comment_spec.rb25
-rw-r--r--spec/ruby/core/string/unpack/d_spec.rb28
-rw-r--r--spec/ruby/core/string/unpack/e_spec.rb14
-rw-r--r--spec/ruby/core/string/unpack/f_spec.rb28
-rw-r--r--spec/ruby/core/string/unpack/g_spec.rb14
-rw-r--r--spec/ruby/core/string/unpack/h_spec.rb127
-rw-r--r--spec/ruby/core/string/unpack/i_spec.rb152
-rw-r--r--spec/ruby/core/string/unpack/j_spec.rb272
-rw-r--r--spec/ruby/core/string/unpack/l_spec.rb265
-rw-r--r--spec/ruby/core/string/unpack/m_spec.rb173
-rw-r--r--spec/ruby/core/string/unpack/n_spec.rb18
-rw-r--r--spec/ruby/core/string/unpack/p_spec.rb52
-rw-r--r--spec/ruby/core/string/unpack/percent_spec.rb7
-rw-r--r--spec/ruby/core/string/unpack/q_spec.rb64
-rw-r--r--spec/ruby/core/string/unpack/s_spec.rb152
-rw-r--r--spec/ruby/core/string/unpack/shared/basic.rb29
-rw-r--r--spec/ruby/core/string/unpack/shared/float.rb271
-rw-r--r--spec/ruby/core/string/unpack/shared/integer.rb339
-rw-r--r--spec/ruby/core/string/unpack/shared/string.rb51
-rw-r--r--spec/ruby/core/string/unpack/shared/taint.rb81
-rw-r--r--spec/ruby/core/string/unpack/shared/unicode.rb60
-rw-r--r--spec/ruby/core/string/unpack/u_spec.rb97
-rw-r--r--spec/ruby/core/string/unpack/v_spec.rb18
-rw-r--r--spec/ruby/core/string/unpack/w_spec.rb25
-rw-r--r--spec/ruby/core/string/unpack/x_spec.rb62
-rw-r--r--spec/ruby/core/string/unpack/z_spec.rb23
-rw-r--r--spec/ruby/core/string/unpack1_spec.rb12
-rw-r--r--spec/ruby/core/string/upcase_spec.rb184
-rw-r--r--spec/ruby/core/string/uplus_spec.rb22
-rw-r--r--spec/ruby/core/string/upto_spec.rb98
-rw-r--r--spec/ruby/core/string/valid_encoding_spec.rb129
-rw-r--r--spec/ruby/core/struct/dig_spec.rb42
-rw-r--r--spec/ruby/core/struct/dup_spec.rb20
-rw-r--r--spec/ruby/core/struct/each_pair_spec.rb33
-rw-r--r--spec/ruby/core/struct/each_spec.rb27
-rw-r--r--spec/ruby/core/struct/element_reference_spec.rb52
-rw-r--r--spec/ruby/core/struct/element_set_spec.rb29
-rw-r--r--spec/ruby/core/struct/eql_spec.rb13
-rw-r--r--spec/ruby/core/struct/equal_value_spec.rb7
-rw-r--r--spec/ruby/core/struct/filter_spec.rb12
-rw-r--r--spec/ruby/core/struct/fixtures/classes.rb26
-rw-r--r--spec/ruby/core/struct/hash_spec.rb46
-rw-r--r--spec/ruby/core/struct/initialize_spec.rb43
-rw-r--r--spec/ruby/core/struct/inspect_spec.rb15
-rw-r--r--spec/ruby/core/struct/instance_variables_spec.rb16
-rw-r--r--spec/ruby/core/struct/length_spec.rb12
-rw-r--r--spec/ruby/core/struct/members_spec.rb13
-rw-r--r--spec/ruby/core/struct/new_spec.rb194
-rw-r--r--spec/ruby/core/struct/select_spec.rb10
-rw-r--r--spec/ruby/core/struct/shared/accessor.rb7
-rw-r--r--spec/ruby/core/struct/shared/equal_value.rb37
-rw-r--r--spec/ruby/core/struct/shared/inspect.rb5
-rw-r--r--spec/ruby/core/struct/shared/select.rb26
-rw-r--r--spec/ruby/core/struct/size_spec.rb11
-rw-r--r--spec/ruby/core/struct/struct_spec.rb43
-rw-r--r--spec/ruby/core/struct/to_a_spec.rb12
-rw-r--r--spec/ruby/core/struct/to_h_spec.rb23
-rw-r--r--spec/ruby/core/struct/to_s_spec.rb12
-rw-r--r--spec/ruby/core/struct/values_at_spec.rb16
-rw-r--r--spec/ruby/core/struct/values_spec.rb11
-rw-r--r--spec/ruby/core/symbol/all_symbols_spec.rb14
-rw-r--r--spec/ruby/core/symbol/capitalize_spec.rb56
-rw-r--r--spec/ruby/core/symbol/case_compare_spec.rb11
-rw-r--r--spec/ruby/core/symbol/casecmp_spec.rb146
-rw-r--r--spec/ruby/core/symbol/comparison_spec.rb51
-rw-r--r--spec/ruby/core/symbol/downcase_spec.rb33
-rw-r--r--spec/ruby/core/symbol/dup_spec.rb9
-rw-r--r--spec/ruby/core/symbol/element_reference_spec.rb6
-rw-r--r--spec/ruby/core/symbol/empty_spec.rb11
-rw-r--r--spec/ruby/core/symbol/encoding_spec.rb23
-rw-r--r--spec/ruby/core/symbol/equal_value_spec.rb14
-rw-r--r--spec/ruby/core/symbol/fixtures/classes.rb3
-rw-r--r--spec/ruby/core/symbol/id2name_spec.rb6
-rw-r--r--spec/ruby/core/symbol/inspect_spec.rb105
-rw-r--r--spec/ruby/core/symbol/intern_spec.rb11
-rw-r--r--spec/ruby/core/symbol/length_spec.rb6
-rw-r--r--spec/ruby/core/symbol/match_spec.rb70
-rw-r--r--spec/ruby/core/symbol/next_spec.rb6
-rw-r--r--spec/ruby/core/symbol/shared/id2name.rb9
-rw-r--r--spec/ruby/core/symbol/shared/length.rb23
-rw-r--r--spec/ruby/core/symbol/shared/slice.rb278
-rw-r--r--spec/ruby/core/symbol/shared/succ.rb18
-rw-r--r--spec/ruby/core/symbol/size_spec.rb6
-rw-r--r--spec/ruby/core/symbol/slice_spec.rb6
-rw-r--r--spec/ruby/core/symbol/succ_spec.rb6
-rw-r--r--spec/ruby/core/symbol/swapcase_spec.rb41
-rw-r--r--spec/ruby/core/symbol/symbol_spec.rb19
-rw-r--r--spec/ruby/core/symbol/to_proc_spec.rb41
-rw-r--r--spec/ruby/core/symbol/to_s_spec.rb6
-rw-r--r--spec/ruby/core/symbol/to_sym_spec.rb9
-rw-r--r--spec/ruby/core/symbol/upcase_spec.rb29
-rw-r--r--spec/ruby/core/systemexit/initialize_spec.rb26
-rw-r--r--spec/ruby/core/systemexit/success_spec.rb13
-rw-r--r--spec/ruby/core/thread/abort_on_exception_spec.rb106
-rw-r--r--spec/ruby/core/thread/add_trace_func_spec.rb5
-rw-r--r--spec/ruby/core/thread/alive_spec.rb58
-rw-r--r--spec/ruby/core/thread/allocate_spec.rb9
-rw-r--r--spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb61
-rw-r--r--spec/ruby/core/thread/backtrace/location/base_label_spec.rb22
-rw-r--r--spec/ruby/core/thread/backtrace/location/fixtures/absolute_path.rb4
-rw-r--r--spec/ruby/core/thread/backtrace/location/fixtures/absolute_path_method_added.rb10
-rw-r--r--spec/ruby/core/thread/backtrace/location/fixtures/classes.rb17
-rw-r--r--spec/ruby/core/thread/backtrace/location/fixtures/main.rb5
-rw-r--r--spec/ruby/core/thread/backtrace/location/inspect_spec.rb13
-rw-r--r--spec/ruby/core/thread/backtrace/location/label_spec.rb20
-rw-r--r--spec/ruby/core/thread/backtrace/location/lineno_spec.rb13
-rw-r--r--spec/ruby/core/thread/backtrace/location/path_spec.rb89
-rw-r--r--spec/ruby/core/thread/backtrace/location/to_s_spec.rb13
-rw-r--r--spec/ruby/core/thread/backtrace_spec.rb35
-rw-r--r--spec/ruby/core/thread/current_spec.rb31
-rw-r--r--spec/ruby/core/thread/element_reference_spec.rb44
-rw-r--r--spec/ruby/core/thread/element_set_spec.rb51
-rw-r--r--spec/ruby/core/thread/exclusive_spec.rb42
-rw-r--r--spec/ruby/core/thread/exit_spec.rb15
-rw-r--r--spec/ruby/core/thread/fetch_spec.rb38
-rw-r--r--spec/ruby/core/thread/fixtures/classes.rb303
-rw-r--r--spec/ruby/core/thread/fork_spec.rb9
-rw-r--r--spec/ruby/core/thread/group_spec.rb5
-rw-r--r--spec/ruby/core/thread/initialize_spec.rb27
-rw-r--r--spec/ruby/core/thread/inspect_spec.rb44
-rw-r--r--spec/ruby/core/thread/join_spec.rb65
-rw-r--r--spec/ruby/core/thread/key_spec.rb53
-rw-r--r--spec/ruby/core/thread/keys_spec.rb44
-rw-r--r--spec/ruby/core/thread/kill_spec.rb25
-rw-r--r--spec/ruby/core/thread/list_spec.rb42
-rw-r--r--spec/ruby/core/thread/main_spec.rb10
-rw-r--r--spec/ruby/core/thread/name_spec.rb54
-rw-r--r--spec/ruby/core/thread/new_spec.rb56
-rw-r--r--spec/ruby/core/thread/pass_spec.rb8
-rw-r--r--spec/ruby/core/thread/priority_spec.rb72
-rw-r--r--spec/ruby/core/thread/raise_spec.rb205
-rw-r--r--spec/ruby/core/thread/report_on_exception_spec.rb120
-rw-r--r--spec/ruby/core/thread/run_spec.rb8
-rw-r--r--spec/ruby/core/thread/set_trace_func_spec.rb5
-rw-r--r--spec/ruby/core/thread/shared/exit.rb182
-rw-r--r--spec/ruby/core/thread/shared/start.rb41
-rw-r--r--spec/ruby/core/thread/shared/wakeup.rb61
-rw-r--r--spec/ruby/core/thread/start_spec.rb9
-rw-r--r--spec/ruby/core/thread/status_spec.rb60
-rw-r--r--spec/ruby/core/thread/stop_spec.rb54
-rw-r--r--spec/ruby/core/thread/terminate_spec.rb7
-rw-r--r--spec/ruby/core/thread/thread_variable_get_spec.rb25
-rw-r--r--spec/ruby/core/thread/thread_variable_set_spec.rb26
-rw-r--r--spec/ruby/core/thread/thread_variable_spec.rb21
-rw-r--r--spec/ruby/core/thread/thread_variables_spec.rb29
-rw-r--r--spec/ruby/core/thread/value_spec.rb21
-rw-r--r--spec/ruby/core/thread/wakeup_spec.rb7
-rw-r--r--spec/ruby/core/threadgroup/add_spec.rb40
-rw-r--r--spec/ruby/core/threadgroup/default_spec.rb11
-rw-r--r--spec/ruby/core/threadgroup/enclose_spec.rb25
-rw-r--r--spec/ruby/core/threadgroup/enclosed_spec.rb14
-rw-r--r--spec/ruby/core/threadgroup/fixtures/classes.rb6
-rw-r--r--spec/ruby/core/threadgroup/list_spec.rb24
-rw-r--r--spec/ruby/core/time/_dump_spec.rb55
-rw-r--r--spec/ruby/core/time/_load_spec.rb54
-rw-r--r--spec/ruby/core/time/asctime_spec.rb6
-rw-r--r--spec/ruby/core/time/at_spec.rb201
-rw-r--r--spec/ruby/core/time/comparison_spec.rb104
-rw-r--r--spec/ruby/core/time/ctime_spec.rb6
-rw-r--r--spec/ruby/core/time/day_spec.rb6
-rw-r--r--spec/ruby/core/time/dst_spec.rb6
-rw-r--r--spec/ruby/core/time/dup_spec.rb46
-rw-r--r--spec/ruby/core/time/eql_spec.rb29
-rw-r--r--spec/ruby/core/time/fixtures/classes.rb26
-rw-r--r--spec/ruby/core/time/friday_spec.rb11
-rw-r--r--spec/ruby/core/time/getgm_spec.rb6
-rw-r--r--spec/ruby/core/time/getlocal_spec.rb114
-rw-r--r--spec/ruby/core/time/getutc_spec.rb6
-rw-r--r--spec/ruby/core/time/gm_spec.rb10
-rw-r--r--spec/ruby/core/time/gmt_offset_spec.rb6
-rw-r--r--spec/ruby/core/time/gmt_spec.rb8
-rw-r--r--spec/ruby/core/time/gmtime_spec.rb6
-rw-r--r--spec/ruby/core/time/gmtoff_spec.rb6
-rw-r--r--spec/ruby/core/time/hash_spec.rb11
-rw-r--r--spec/ruby/core/time/hour_spec.rb17
-rw-r--r--spec/ruby/core/time/inspect_spec.rb6
-rw-r--r--spec/ruby/core/time/isdst_spec.rb6
-rw-r--r--spec/ruby/core/time/local_spec.rb11
-rw-r--r--spec/ruby/core/time/localtime_spec.rb140
-rw-r--r--spec/ruby/core/time/mday_spec.rb6
-rw-r--r--spec/ruby/core/time/min_spec.rb17
-rw-r--r--spec/ruby/core/time/minus_spec.rb103
-rw-r--r--spec/ruby/core/time/mktime_spec.rb11
-rw-r--r--spec/ruby/core/time/mon_spec.rb6
-rw-r--r--spec/ruby/core/time/monday_spec.rb11
-rw-r--r--spec/ruby/core/time/month_spec.rb6
-rw-r--r--spec/ruby/core/time/new_spec.rb127
-rw-r--r--spec/ruby/core/time/now_spec.rb6
-rw-r--r--spec/ruby/core/time/nsec_spec.rb31
-rw-r--r--spec/ruby/core/time/plus_spec.rb113
-rw-r--r--spec/ruby/core/time/round_spec.rb35
-rw-r--r--spec/ruby/core/time/saturday_spec.rb11
-rw-r--r--spec/ruby/core/time/sec_spec.rb7
-rw-r--r--spec/ruby/core/time/shared/asctime.rb6
-rw-r--r--spec/ruby/core/time/shared/day.rb15
-rw-r--r--spec/ruby/core/time/shared/getgm.rb9
-rw-r--r--spec/ruby/core/time/shared/gm.rb70
-rw-r--r--spec/ruby/core/time/shared/gmt_offset.rb53
-rw-r--r--spec/ruby/core/time/shared/gmtime.rb33
-rw-r--r--spec/ruby/core/time/shared/inspect.rb23
-rw-r--r--spec/ruby/core/time/shared/isdst.rb8
-rw-r--r--spec/ruby/core/time/shared/local.rb45
-rw-r--r--spec/ruby/core/time/shared/month.rb15
-rw-r--r--spec/ruby/core/time/shared/now.rb20
-rw-r--r--spec/ruby/core/time/shared/time_params.rb262
-rw-r--r--spec/ruby/core/time/shared/to_i.rb9
-rw-r--r--spec/ruby/core/time/strftime_spec.rb52
-rw-r--r--spec/ruby/core/time/subsec_spec.rb27
-rw-r--r--spec/ruby/core/time/succ_spec.rb19
-rw-r--r--spec/ruby/core/time/sunday_spec.rb11
-rw-r--r--spec/ruby/core/time/thursday_spec.rb11
-rw-r--r--spec/ruby/core/time/time_spec.rb7
-rw-r--r--spec/ruby/core/time/to_a_spec.rb12
-rw-r--r--spec/ruby/core/time/to_f_spec.rb7
-rw-r--r--spec/ruby/core/time/to_i_spec.rb6
-rw-r--r--spec/ruby/core/time/to_r_spec.rb11
-rw-r--r--spec/ruby/core/time/to_s_spec.rb6
-rw-r--r--spec/ruby/core/time/tuesday_spec.rb11
-rw-r--r--spec/ruby/core/time/tv_nsec_spec.rb5
-rw-r--r--spec/ruby/core/time/tv_sec_spec.rb6
-rw-r--r--spec/ruby/core/time/tv_usec_spec.rb5
-rw-r--r--spec/ruby/core/time/usec_spec.rb43
-rw-r--r--spec/ruby/core/time/utc_offset_spec.rb6
-rw-r--r--spec/ruby/core/time/utc_spec.rb21
-rw-r--r--spec/ruby/core/time/wday_spec.rb9
-rw-r--r--spec/ruby/core/time/wednesday_spec.rb11
-rw-r--r--spec/ruby/core/time/yday_spec.rb21
-rw-r--r--spec/ruby/core/time/year_spec.rb17
-rw-r--r--spec/ruby/core/time/zone_spec.rb90
-rw-r--r--spec/ruby/core/tracepoint/binding_spec.rb19
-rw-r--r--spec/ruby/core/tracepoint/callee_id_spec.rb19
-rw-r--r--spec/ruby/core/tracepoint/defined_class_spec.rb26
-rw-r--r--spec/ruby/core/tracepoint/disable_spec.rb73
-rw-r--r--spec/ruby/core/tracepoint/enable_spec.rb102
-rw-r--r--spec/ruby/core/tracepoint/enabled_spec.rb14
-rw-r--r--spec/ruby/core/tracepoint/event_spec.rb21
-rw-r--r--spec/ruby/core/tracepoint/fixtures/classes.rb34
-rw-r--r--spec/ruby/core/tracepoint/inspect_spec.rb8
-rw-r--r--spec/ruby/core/tracepoint/lineno_spec.rb10
-rw-r--r--spec/ruby/core/tracepoint/method_id_spec.rb13
-rw-r--r--spec/ruby/core/tracepoint/new_spec.rb68
-rw-r--r--spec/ruby/core/tracepoint/parameters_spec.rb23
-rw-r--r--spec/ruby/core/tracepoint/path_spec.rb18
-rw-r--r--spec/ruby/core/tracepoint/raised_exception_spec.rb16
-rw-r--r--spec/ruby/core/tracepoint/return_value_spec.rb13
-rw-r--r--spec/ruby/core/tracepoint/self_spec.rb10
-rw-r--r--spec/ruby/core/tracepoint/trace_spec.rb9
-rw-r--r--spec/ruby/core/true/and_spec.rb11
-rw-r--r--spec/ruby/core/true/dup_spec.rb9
-rw-r--r--spec/ruby/core/true/inspect_spec.rb7
-rw-r--r--spec/ruby/core/true/or_spec.rb11
-rw-r--r--spec/ruby/core/true/to_s_spec.rb7
-rw-r--r--spec/ruby/core/true/trueclass_spec.rb15
-rw-r--r--spec/ruby/core/true/xor_spec.rb11
-rw-r--r--spec/ruby/core/unboundmethod/arity_spec.rb207
-rw-r--r--spec/ruby/core/unboundmethod/bind_spec.rb51
-rw-r--r--spec/ruby/core/unboundmethod/clone_spec.rb12
-rw-r--r--spec/ruby/core/unboundmethod/eql_spec.rb5
-rw-r--r--spec/ruby/core/unboundmethod/equal_value_spec.rb101
-rw-r--r--spec/ruby/core/unboundmethod/fixtures/classes.rb86
-rw-r--r--spec/ruby/core/unboundmethod/hash_spec.rb17
-rw-r--r--spec/ruby/core/unboundmethod/inspect_spec.rb7
-rw-r--r--spec/ruby/core/unboundmethod/name_spec.rb15
-rw-r--r--spec/ruby/core/unboundmethod/owner_spec.rb26
-rw-r--r--spec/ruby/core/unboundmethod/parameters_spec.rb5
-rw-r--r--spec/ruby/core/unboundmethod/shared/to_s.rb25
-rw-r--r--spec/ruby/core/unboundmethod/source_location_spec.rb52
-rw-r--r--spec/ruby/core/unboundmethod/super_method_spec.rb28
-rw-r--r--spec/ruby/core/unboundmethod/to_s_spec.rb7
-rw-r--r--spec/ruby/core/warning/warn_spec.rb68
-rw-r--r--spec/ruby/default.mspec58
-rw-r--r--spec/ruby/fixtures/basicobject/method_missing.rb55
-rw-r--r--spec/ruby/fixtures/class.rb136
-rw-r--r--spec/ruby/fixtures/class_variables.rb58
-rw-r--r--spec/ruby/fixtures/code/a/load_fixture.bundle1
-rw-r--r--spec/ruby/fixtures/code/a/load_fixture.dll1
-rw-r--r--spec/ruby/fixtures/code/a/load_fixture.so1
-rw-r--r--spec/ruby/fixtures/code/b/load_fixture.rb1
-rw-r--r--spec/ruby/fixtures/code/concurrent.rb12
-rw-r--r--spec/ruby/fixtures/code/concurrent2.rb8
-rw-r--r--spec/ruby/fixtures/code/concurrent3.rb2
-rw-r--r--spec/ruby/fixtures/code/file_fixture.rb1
-rw-r--r--spec/ruby/fixtures/code/gem/load_fixture.rb1
-rw-r--r--spec/ruby/fixtures/code/line_fixture.rb5
-rw-r--r--spec/ruby/fixtures/code/load_ext_fixture.rb1
-rw-r--r--spec/ruby/fixtures/code/load_fixture1
-rw-r--r--spec/ruby/fixtures/code/load_fixture.bundle1
-rw-r--r--spec/ruby/fixtures/code/load_fixture.dll1
-rw-r--r--spec/ruby/fixtures/code/load_fixture.ext1
-rw-r--r--spec/ruby/fixtures/code/load_fixture.ext.bundle1
-rw-r--r--spec/ruby/fixtures/code/load_fixture.ext.dll1
-rw-r--r--spec/ruby/fixtures/code/load_fixture.ext.rb1
-rw-r--r--spec/ruby/fixtures/code/load_fixture.ext.so1
-rw-r--r--spec/ruby/fixtures/code/load_fixture.rb1
-rw-r--r--spec/ruby/fixtures/code/load_fixture.so1
-rw-r--r--spec/ruby/fixtures/code/load_fixture_and__FILE__.rb1
-rw-r--r--spec/ruby/fixtures/code/load_wrap_method_fixture.rb9
-rw-r--r--spec/ruby/fixtures/code/methods_fixture.rb364
-rw-r--r--spec/ruby/fixtures/code/raise_fixture.rb1
-rw-r--r--spec/ruby/fixtures/code/recursive_load_fixture.rb5
-rw-r--r--spec/ruby/fixtures/code/recursive_require_fixture.rb3
-rw-r--r--spec/ruby/fixtures/code/symlink/symlink1.rb1
-rw-r--r--spec/ruby/fixtures/code/symlink/symlink2/symlink2.rb1
-rw-r--r--spec/ruby/fixtures/code/wrap_fixture.rb3
-rw-r--r--spec/ruby/fixtures/code_loading.rb26
-rw-r--r--spec/ruby/fixtures/constants.rb288
-rw-r--r--spec/ruby/fixtures/enumerator/classes.rb15
-rw-r--r--spec/ruby/fixtures/math/common.rb3
-rw-r--r--spec/ruby/fixtures/rational.rb14
-rw-r--r--spec/ruby/fixtures/reflection.rb352
-rw-r--r--spec/ruby/language/BEGIN_spec.rb36
-rw-r--r--spec/ruby/language/README30
-rw-r--r--spec/ruby/language/alias_spec.rb246
-rw-r--r--spec/ruby/language/and_spec.rb80
-rw-r--r--spec/ruby/language/array_spec.rb162
-rw-r--r--spec/ruby/language/block_spec.rb865
-rw-r--r--spec/ruby/language/break_spec.rb365
-rw-r--r--spec/ruby/language/case_spec.rb427
-rw-r--r--spec/ruby/language/class_spec.rb328
-rw-r--r--spec/ruby/language/class_variable_spec.rb84
-rw-r--r--spec/ruby/language/constants_spec.rb715
-rw-r--r--spec/ruby/language/def_spec.rb761
-rw-r--r--spec/ruby/language/defined_spec.rb1132
-rw-r--r--spec/ruby/language/encoding_spec.rb36
-rw-r--r--spec/ruby/language/ensure_spec.rb333
-rw-r--r--spec/ruby/language/execution_spec.rb15
-rw-r--r--spec/ruby/language/file_spec.rb29
-rw-r--r--spec/ruby/language/fixtures/argv_encoding.rb1
-rw-r--r--spec/ruby/language/fixtures/array.rb32
-rw-r--r--spec/ruby/language/fixtures/begin_file.rb3
-rw-r--r--spec/ruby/language/fixtures/binary_symbol.rb4
-rw-r--r--spec/ruby/language/fixtures/block.rb57
-rw-r--r--spec/ruby/language/fixtures/break.rb291
-rw-r--r--spec/ruby/language/fixtures/break_lambda_toplevel.rb9
-rw-r--r--spec/ruby/language/fixtures/break_lambda_toplevel_block.rb23
-rw-r--r--spec/ruby/language/fixtures/break_lambda_toplevel_method.rb17
-rw-r--r--spec/ruby/language/fixtures/bytes_magic_comment.rb2
-rw-r--r--spec/ruby/language/fixtures/case_magic_comment.rb2
-rw-r--r--spec/ruby/language/fixtures/classes.rb31
-rw-r--r--spec/ruby/language/fixtures/coding_us_ascii.rb11
-rw-r--r--spec/ruby/language/fixtures/coding_utf_8.rb11
-rw-r--r--spec/ruby/language/fixtures/constant_visibility.rb98
-rw-r--r--spec/ruby/language/fixtures/constants_sclass.rb54
-rw-r--r--spec/ruby/language/fixtures/def.rb14
-rw-r--r--spec/ruby/language/fixtures/defined.rb303
-rw-r--r--spec/ruby/language/fixtures/dollar_zero.rb6
-rw-r--r--spec/ruby/language/fixtures/emacs_magic_comment.rb2
-rw-r--r--spec/ruby/language/fixtures/ensure.rb121
-rw-r--r--spec/ruby/language/fixtures/file.rb1
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_across_files.rb5
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_across_files_diff_enc.rb5
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_across_files_no_comment.rb5
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_one_literal.rb4
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_required.rb3
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_required_diff_enc.rbbin0 -> 181 bytes-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_required_no_comment.rb1
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_two_literals.rb3
-rw-r--r--spec/ruby/language/fixtures/hash_strings_ascii8bit.rb7
-rw-r--r--spec/ruby/language/fixtures/hash_strings_usascii.rb7
-rw-r--r--spec/ruby/language/fixtures/hash_strings_utf8.rb7
-rw-r--r--spec/ruby/language/fixtures/magic_comment.rb2
-rw-r--r--spec/ruby/language/fixtures/match_operators.rb9
-rw-r--r--spec/ruby/language/fixtures/metaclass.rb33
-rw-r--r--spec/ruby/language/fixtures/module.rb24
-rw-r--r--spec/ruby/language/fixtures/next.rb134
-rw-r--r--spec/ruby/language/fixtures/no_magic_comment.rb1
-rw-r--r--spec/ruby/language/fixtures/precedence.rb16
-rw-r--r--spec/ruby/language/fixtures/print_magic_comment_result_at_exit.rb3
-rw-r--r--spec/ruby/language/fixtures/private.rb59
-rw-r--r--spec/ruby/language/fixtures/rescue.rb67
-rw-r--r--spec/ruby/language/fixtures/return.rb139
-rw-r--r--spec/ruby/language/fixtures/second_line_magic_comment.rb3
-rw-r--r--spec/ruby/language/fixtures/second_token_magic_comment.rb2
-rw-r--r--spec/ruby/language/fixtures/send.rb140
-rwxr-xr-xspec/ruby/language/fixtures/shebang_magic_comment.rb3
-rw-r--r--spec/ruby/language/fixtures/squiggly_heredoc.rb39
-rw-r--r--spec/ruby/language/fixtures/super.rb696
-rw-r--r--spec/ruby/language/fixtures/utf16-be-nobom.rbbin0 -> 68 bytes-rw-r--r--spec/ruby/language/fixtures/utf16-le-nobom.rbbin0 -> 69 bytes-rw-r--r--spec/ruby/language/fixtures/utf8-bom.rb2
-rw-r--r--spec/ruby/language/fixtures/utf8-nobom.rb2
-rw-r--r--spec/ruby/language/fixtures/variables.rb85
-rw-r--r--spec/ruby/language/fixtures/vim_magic_comment.rb2
-rw-r--r--spec/ruby/language/fixtures/yield.rb37
-rw-r--r--spec/ruby/language/for_spec.rb177
-rw-r--r--spec/ruby/language/hash_spec.rb154
-rw-r--r--spec/ruby/language/heredoc_spec.rb85
-rw-r--r--spec/ruby/language/if_spec.rb376
-rw-r--r--spec/ruby/language/lambda_spec.rb573
-rw-r--r--spec/ruby/language/line_spec.rb45
-rw-r--r--spec/ruby/language/loop_spec.rb67
-rw-r--r--spec/ruby/language/magic_comment_spec.rb92
-rw-r--r--spec/ruby/language/match_spec.rb74
-rw-r--r--spec/ruby/language/metaclass_spec.rb143
-rw-r--r--spec/ruby/language/method_spec.rb1322
-rw-r--r--spec/ruby/language/module_spec.rb91
-rw-r--r--spec/ruby/language/next_spec.rb410
-rw-r--r--spec/ruby/language/not_spec.rb51
-rw-r--r--spec/ruby/language/numbers_spec.rb97
-rw-r--r--spec/ruby/language/optional_assignments_spec.rb298
-rw-r--r--spec/ruby/language/or_spec.rb90
-rw-r--r--spec/ruby/language/order_spec.rb75
-rw-r--r--spec/ruby/language/precedence_spec.rb449
-rw-r--r--spec/ruby/language/predefined/data_spec.rb48
-rw-r--r--spec/ruby/language/predefined/fixtures/data1.rb4
-rw-r--r--spec/ruby/language/predefined/fixtures/data2.rb3
-rw-r--r--spec/ruby/language/predefined/fixtures/data3.rb6
-rw-r--r--spec/ruby/language/predefined/fixtures/data4.rb4
-rw-r--r--spec/ruby/language/predefined/fixtures/data5.rb5
-rw-r--r--spec/ruby/language/predefined/fixtures/data_offset.rb12
-rw-r--r--spec/ruby/language/predefined/fixtures/data_only.rb2
-rw-r--r--spec/ruby/language/predefined/fixtures/empty_data.rb3
-rw-r--r--spec/ruby/language/predefined/fixtures/print_data.rb3
-rw-r--r--spec/ruby/language/predefined/fixtures/toplevel_binding_dynamic.rb4
-rw-r--r--spec/ruby/language/predefined/fixtures/toplevel_binding_dynamic_required.rb2
-rw-r--r--spec/ruby/language/predefined/fixtures/toplevel_binding_id.rb4
-rw-r--r--spec/ruby/language/predefined/fixtures/toplevel_binding_id_required.rb1
-rw-r--r--spec/ruby/language/predefined/fixtures/toplevel_binding_required_before.rb2
-rw-r--r--spec/ruby/language/predefined/fixtures/toplevel_binding_values.rb9
-rw-r--r--spec/ruby/language/predefined/fixtures/toplevel_binding_variables.rb4
-rw-r--r--spec/ruby/language/predefined/fixtures/toplevel_binding_variables_required.rb2
-rw-r--r--spec/ruby/language/predefined/toplevel_binding_spec.rb34
-rw-r--r--spec/ruby/language/predefined_spec.rb1267
-rw-r--r--spec/ruby/language/private_spec.rb67
-rw-r--r--spec/ruby/language/proc_spec.rb220
-rw-r--r--spec/ruby/language/redo_spec.rb66
-rw-r--r--spec/ruby/language/regexp/anchors_spec.rb179
-rw-r--r--spec/ruby/language/regexp/back-references_spec.rb53
-rw-r--r--spec/ruby/language/regexp/character_classes_spec.rb633
-rw-r--r--spec/ruby/language/regexp/encoding_spec.rb103
-rw-r--r--spec/ruby/language/regexp/escapes_spec.rb81
-rw-r--r--spec/ruby/language/regexp/grouping_spec.rb23
-rw-r--r--spec/ruby/language/regexp/interpolation_spec.rb58
-rw-r--r--spec/ruby/language/regexp/modifiers_spec.rb117
-rw-r--r--spec/ruby/language/regexp/repetition_spec.rb57
-rw-r--r--spec/ruby/language/regexp_spec.rb197
-rw-r--r--spec/ruby/language/rescue_spec.rb493
-rw-r--r--spec/ruby/language/retry_spec.rb52
-rw-r--r--spec/ruby/language/return_spec.rb483
-rw-r--r--spec/ruby/language/safe_navigator_spec.rb99
-rw-r--r--spec/ruby/language/safe_spec.rb97
-rw-r--r--spec/ruby/language/send_spec.rb552
-rw-r--r--spec/ruby/language/shared/__FILE__.rb23
-rw-r--r--spec/ruby/language/shared/__LINE__.rb15
-rw-r--r--spec/ruby/language/singleton_class_spec.rb293
-rw-r--r--spec/ruby/language/source_encoding_spec.rb61
-rw-r--r--spec/ruby/language/string_spec.rb289
-rw-r--r--spec/ruby/language/super_spec.rb369
-rw-r--r--spec/ruby/language/symbol_spec.rb106
-rw-r--r--spec/ruby/language/throw_spec.rb81
-rw-r--r--spec/ruby/language/undef_spec.rb72
-rw-r--r--spec/ruby/language/unless_spec.rb43
-rw-r--r--spec/ruby/language/until_spec.rb234
-rw-r--r--spec/ruby/language/variables_spec.rb760
-rw-r--r--spec/ruby/language/while_spec.rb344
-rw-r--r--spec/ruby/language/yield_spec.rb179
-rw-r--r--spec/ruby/library/English/English_spec.rb171
-rw-r--r--spec/ruby/library/abbrev/abbrev_spec.rb31
-rw-r--r--spec/ruby/library/base64/decode64_spec.rb9
-rw-r--r--spec/ruby/library/base64/encode64_spec.rb14
-rw-r--r--spec/ruby/library/base64/urlsafe_decode64_spec.rb19
-rw-r--r--spec/ruby/library/base64/urlsafe_encode64_spec.rb20
-rw-r--r--spec/ruby/library/bigdecimal/BigDecimal_spec.rb132
-rw-r--r--spec/ruby/library/bigdecimal/abs_spec.rb50
-rw-r--r--spec/ruby/library/bigdecimal/add_spec.rb179
-rw-r--r--spec/ruby/library/bigdecimal/case_compare_spec.rb7
-rw-r--r--spec/ruby/library/bigdecimal/ceil_spec.rb104
-rw-r--r--spec/ruby/library/bigdecimal/coerce_spec.rb26
-rw-r--r--spec/ruby/library/bigdecimal/comparison_spec.rb81
-rw-r--r--spec/ruby/library/bigdecimal/div_spec.rb102
-rw-r--r--spec/ruby/library/bigdecimal/divide_spec.rb7
-rw-r--r--spec/ruby/library/bigdecimal/divmod_spec.rb180
-rw-r--r--spec/ruby/library/bigdecimal/double_fig_spec.rb9
-rw-r--r--spec/ruby/library/bigdecimal/eql_spec.rb6
-rw-r--r--spec/ruby/library/bigdecimal/equal_value_spec.rb7
-rw-r--r--spec/ruby/library/bigdecimal/exponent_spec.rb38
-rw-r--r--spec/ruby/library/bigdecimal/finite_spec.rb34
-rw-r--r--spec/ruby/library/bigdecimal/fix_spec.rb57
-rw-r--r--spec/ruby/library/bigdecimal/fixtures/classes.rb17
-rw-r--r--spec/ruby/library/bigdecimal/floor_spec.rb100
-rw-r--r--spec/ruby/library/bigdecimal/frac_spec.rb48
-rw-r--r--spec/ruby/library/bigdecimal/gt_spec.rb98
-rw-r--r--spec/ruby/library/bigdecimal/gte_spec.rb102
-rw-r--r--spec/ruby/library/bigdecimal/infinite_spec.rb32
-rw-r--r--spec/ruby/library/bigdecimal/inspect_spec.rb47
-rw-r--r--spec/ruby/library/bigdecimal/limit_spec.rb55
-rw-r--r--spec/ruby/library/bigdecimal/lt_spec.rb96
-rw-r--r--spec/ruby/library/bigdecimal/lte_spec.rb102
-rw-r--r--spec/ruby/library/bigdecimal/minus_spec.rb58
-rw-r--r--spec/ruby/library/bigdecimal/mode_spec.rb36
-rw-r--r--spec/ruby/library/bigdecimal/modulo_spec.rb12
-rw-r--r--spec/ruby/library/bigdecimal/mult_spec.rb24
-rw-r--r--spec/ruby/library/bigdecimal/multiply_spec.rb26
-rw-r--r--spec/ruby/library/bigdecimal/nan_spec.rb23
-rw-r--r--spec/ruby/library/bigdecimal/nonzero_spec.rb29
-rw-r--r--spec/ruby/library/bigdecimal/plus_spec.rb47
-rw-r--r--spec/ruby/library/bigdecimal/power_spec.rb6
-rw-r--r--spec/ruby/library/bigdecimal/precs_spec.rb48
-rw-r--r--spec/ruby/library/bigdecimal/quo_spec.rb12
-rw-r--r--spec/ruby/library/bigdecimal/remainder_spec.rb84
-rw-r--r--spec/ruby/library/bigdecimal/round_spec.rb202
-rw-r--r--spec/ruby/library/bigdecimal/shared/eql.rb61
-rw-r--r--spec/ruby/library/bigdecimal/shared/modulo.rb116
-rw-r--r--spec/ruby/library/bigdecimal/shared/mult.rb97
-rw-r--r--spec/ruby/library/bigdecimal/shared/power.rb72
-rw-r--r--spec/ruby/library/bigdecimal/shared/quo.rb59
-rw-r--r--spec/ruby/library/bigdecimal/shared/to_int.rb16
-rw-r--r--spec/ruby/library/bigdecimal/sign_spec.rb46
-rw-r--r--spec/ruby/library/bigdecimal/split_spec.rb86
-rw-r--r--spec/ruby/library/bigdecimal/sqrt_spec.rb112
-rw-r--r--spec/ruby/library/bigdecimal/sub_spec.rb53
-rw-r--r--spec/ruby/library/bigdecimal/to_f_spec.rb54
-rw-r--r--spec/ruby/library/bigdecimal/to_i_spec.rb7
-rw-r--r--spec/ruby/library/bigdecimal/to_int_spec.rb8
-rw-r--r--spec/ruby/library/bigdecimal/to_r_spec.rb16
-rw-r--r--spec/ruby/library/bigdecimal/to_s_spec.rb82
-rw-r--r--spec/ruby/library/bigdecimal/truncate_spec.rb81
-rw-r--r--spec/ruby/library/bigdecimal/uminus_spec.rb58
-rw-r--r--spec/ruby/library/bigdecimal/uplus_spec.rb17
-rw-r--r--spec/ruby/library/bigdecimal/zero_spec.rb27
-rw-r--r--spec/ruby/library/bigmath/log_spec.rb10
-rw-r--r--spec/ruby/library/cgi/cookie/domain_spec.rb23
-rw-r--r--spec/ruby/library/cgi/cookie/expires_spec.rb23
-rw-r--r--spec/ruby/library/cgi/cookie/initialize_spec.rb147
-rw-r--r--spec/ruby/library/cgi/cookie/name_spec.rb23
-rw-r--r--spec/ruby/library/cgi/cookie/parse_spec.rb39
-rw-r--r--spec/ruby/library/cgi/cookie/path_spec.rb23
-rw-r--r--spec/ruby/library/cgi/cookie/secure_spec.rb70
-rw-r--r--spec/ruby/library/cgi/cookie/to_s_spec.rb42
-rw-r--r--spec/ruby/library/cgi/cookie/value_spec.rb76
-rw-r--r--spec/ruby/library/cgi/escapeElement_spec.rb20
-rw-r--r--spec/ruby/library/cgi/escapeHTML_spec.rb13
-rw-r--r--spec/ruby/library/cgi/escape_spec.rb26
-rw-r--r--spec/ruby/library/cgi/htmlextension/a_spec.rb49
-rw-r--r--spec/ruby/library/cgi/htmlextension/base_spec.rb33
-rw-r--r--spec/ruby/library/cgi/htmlextension/blockquote_spec.rb33
-rw-r--r--spec/ruby/library/cgi/htmlextension/br_spec.rb22
-rw-r--r--spec/ruby/library/cgi/htmlextension/caption_spec.rb33
-rw-r--r--spec/ruby/library/cgi/htmlextension/checkbox_group_spec.rb76
-rw-r--r--spec/ruby/library/cgi/htmlextension/checkbox_spec.rb77
-rw-r--r--spec/ruby/library/cgi/htmlextension/doctype_spec.rb27
-rw-r--r--spec/ruby/library/cgi/htmlextension/file_field_spec.rb72
-rw-r--r--spec/ruby/library/cgi/htmlextension/fixtures/common.rb15
-rw-r--r--spec/ruby/library/cgi/htmlextension/form_spec.rb58
-rw-r--r--spec/ruby/library/cgi/htmlextension/frame_spec.rb14
-rw-r--r--spec/ruby/library/cgi/htmlextension/frameset_spec.rb14
-rw-r--r--spec/ruby/library/cgi/htmlextension/hidden_spec.rb59
-rw-r--r--spec/ruby/library/cgi/htmlextension/html_spec.rb66
-rw-r--r--spec/ruby/library/cgi/htmlextension/image_button_spec.rb69
-rw-r--r--spec/ruby/library/cgi/htmlextension/img_spec.rb83
-rw-r--r--spec/ruby/library/cgi/htmlextension/multipart_form_spec.rb64
-rw-r--r--spec/ruby/library/cgi/htmlextension/password_field_spec.rb84
-rw-r--r--spec/ruby/library/cgi/htmlextension/popup_menu_spec.rb8
-rw-r--r--spec/ruby/library/cgi/htmlextension/radio_button_spec.rb77
-rw-r--r--spec/ruby/library/cgi/htmlextension/radio_group_spec.rb77
-rw-r--r--spec/ruby/library/cgi/htmlextension/reset_spec.rb57
-rw-r--r--spec/ruby/library/cgi/htmlextension/scrolling_list_spec.rb8
-rw-r--r--spec/ruby/library/cgi/htmlextension/shared/popup_menu.rb94
-rw-r--r--spec/ruby/library/cgi/htmlextension/submit_spec.rb57
-rw-r--r--spec/ruby/library/cgi/htmlextension/text_field_spec.rb84
-rw-r--r--spec/ruby/library/cgi/htmlextension/textarea_spec.rb73
-rw-r--r--spec/ruby/library/cgi/http_header_spec.rb8
-rw-r--r--spec/ruby/library/cgi/initialize_spec.rb133
-rw-r--r--spec/ruby/library/cgi/out_spec.rb51
-rw-r--r--spec/ruby/library/cgi/parse_spec.rb24
-rw-r--r--spec/ruby/library/cgi/pretty_spec.rb24
-rw-r--r--spec/ruby/library/cgi/print_spec.rb26
-rw-r--r--spec/ruby/library/cgi/queryextension/accept_charset_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/accept_encoding_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/accept_language_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/accept_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/auth_type_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/cache_control_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/content_length_spec.rb26
-rw-r--r--spec/ruby/library/cgi/queryextension/content_type_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/cookies_spec.rb10
-rw-r--r--spec/ruby/library/cgi/queryextension/element_reference_spec.rb27
-rw-r--r--spec/ruby/library/cgi/queryextension/from_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/gateway_interface_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/has_key_spec.rb7
-rw-r--r--spec/ruby/library/cgi/queryextension/host_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/include_spec.rb7
-rw-r--r--spec/ruby/library/cgi/queryextension/key_spec.rb7
-rw-r--r--spec/ruby/library/cgi/queryextension/keys_spec.rb20
-rw-r--r--spec/ruby/library/cgi/queryextension/multipart_spec.rb40
-rw-r--r--spec/ruby/library/cgi/queryextension/negotiate_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/params_spec.rb37
-rw-r--r--spec/ruby/library/cgi/queryextension/path_info_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/path_translated_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/pragma_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/query_string_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/raw_cookie2_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/raw_cookie_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/referer_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/remote_addr_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/remote_host_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/remote_ident_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/remote_user_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/request_method_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/script_name_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/server_name_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/server_port_spec.rb26
-rw-r--r--spec/ruby/library/cgi/queryextension/server_protocol_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/server_software_spec.rb22
-rw-r--r--spec/ruby/library/cgi/queryextension/shared/has_key.rb19
-rw-r--r--spec/ruby/library/cgi/queryextension/user_agent_spec.rb22
-rw-r--r--spec/ruby/library/cgi/rfc1123_date_spec.rb10
-rw-r--r--spec/ruby/library/cgi/shared/http_header.rb112
-rw-r--r--spec/ruby/library/cgi/unescapeElement_spec.rb20
-rw-r--r--spec/ruby/library/cgi/unescapeHTML_spec.rb39
-rw-r--r--spec/ruby/library/cgi/unescape_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/acos_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/acosh_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/asin_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/asinh_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/atan2_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/atan_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/atanh_spec.rb17
-rw-r--r--spec/ruby/library/complex/math/cos_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/cosh_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/exp_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/fixtures/classes.rb4
-rw-r--r--spec/ruby/library/complex/math/log10_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/log_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/shared/acos.rb41
-rw-r--r--spec/ruby/library/complex/math/shared/acosh.rb37
-rw-r--r--spec/ruby/library/complex/math/shared/asin.rb47
-rw-r--r--spec/ruby/library/complex/math/shared/asinh.rb32
-rw-r--r--spec/ruby/library/complex/math/shared/atan.rb32
-rw-r--r--spec/ruby/library/complex/math/shared/atan2.rb34
-rw-r--r--spec/ruby/library/complex/math/shared/atanh.rb30
-rw-r--r--spec/ruby/library/complex/math/shared/cos.rb30
-rw-r--r--spec/ruby/library/complex/math/shared/cosh.rb28
-rw-r--r--spec/ruby/library/complex/math/shared/exp.rb28
-rw-r--r--spec/ruby/library/complex/math/shared/log.rb39
-rw-r--r--spec/ruby/library/complex/math/shared/log10.rb41
-rw-r--r--spec/ruby/library/complex/math/shared/sin.rb30
-rw-r--r--spec/ruby/library/complex/math/shared/sinh.rb28
-rw-r--r--spec/ruby/library/complex/math/shared/sqrt.rb34
-rw-r--r--spec/ruby/library/complex/math/shared/tan.rb28
-rw-r--r--spec/ruby/library/complex/math/shared/tanh.rb32
-rw-r--r--spec/ruby/library/complex/math/sin_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/sinh_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/sqrt_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/tan_spec.rb15
-rw-r--r--spec/ruby/library/complex/math/tanh_spec.rb15
-rw-r--r--spec/ruby/library/complex/numeric/im_spec.rb3
-rw-r--r--spec/ruby/library/conditionvariable/broadcast_spec.rb67
-rw-r--r--spec/ruby/library/conditionvariable/marshal_dump_spec.rb9
-rw-r--r--spec/ruby/library/conditionvariable/signal_spec.rb69
-rw-r--r--spec/ruby/library/conditionvariable/wait_spec.rb123
-rw-r--r--spec/ruby/library/coverage/fixtures/second_class.rb5
-rw-r--r--spec/ruby/library/coverage/fixtures/some_class.rb16
-rw-r--r--spec/ruby/library/coverage/fixtures/spec_helper.rb11
-rw-r--r--spec/ruby/library/coverage/fixtures/start_coverage.rb3
-rw-r--r--spec/ruby/library/coverage/peek_result_spec.rb65
-rw-r--r--spec/ruby/library/coverage/result_spec.rb78
-rw-r--r--spec/ruby/library/coverage/start_spec.rb6
-rw-r--r--spec/ruby/library/csv/basicwriter/close_on_terminate_spec.rb6
-rw-r--r--spec/ruby/library/csv/basicwriter/initialize_spec.rb6
-rw-r--r--spec/ruby/library/csv/basicwriter/terminate_spec.rb6
-rw-r--r--spec/ruby/library/csv/cell/data_spec.rb6
-rw-r--r--spec/ruby/library/csv/cell/initialize_spec.rb6
-rw-r--r--spec/ruby/library/csv/fixtures/one_line.csv1
-rw-r--r--spec/ruby/library/csv/foreach_spec.rb6
-rw-r--r--spec/ruby/library/csv/generate_line_spec.rb30
-rw-r--r--spec/ruby/library/csv/generate_row_spec.rb6
-rw-r--r--spec/ruby/library/csv/generate_spec.rb32
-rw-r--r--spec/ruby/library/csv/iobuf/close_spec.rb6
-rw-r--r--spec/ruby/library/csv/iobuf/initialize_spec.rb6
-rw-r--r--spec/ruby/library/csv/iobuf/read_spec.rb6
-rw-r--r--spec/ruby/library/csv/iobuf/terminate_spec.rb6
-rw-r--r--spec/ruby/library/csv/ioreader/close_on_terminate_spec.rb6
-rw-r--r--spec/ruby/library/csv/ioreader/get_row_spec.rb6
-rw-r--r--spec/ruby/library/csv/ioreader/initialize_spec.rb6
-rw-r--r--spec/ruby/library/csv/ioreader/terminate_spec.rb6
-rw-r--r--spec/ruby/library/csv/liberal_parsing_spec.rb21
-rw-r--r--spec/ruby/library/csv/open_spec.rb6
-rw-r--r--spec/ruby/library/csv/parse_spec.rb95
-rw-r--r--spec/ruby/library/csv/read_spec.rb6
-rw-r--r--spec/ruby/library/csv/readlines_spec.rb37
-rw-r--r--spec/ruby/library/csv/streambuf/add_buf_spec.rb6
-rw-r--r--spec/ruby/library/csv/streambuf/buf_size_spec.rb6
-rw-r--r--spec/ruby/library/csv/streambuf/drop_spec.rb6
-rw-r--r--spec/ruby/library/csv/streambuf/element_reference_spec.rb6
-rw-r--r--spec/ruby/library/csv/streambuf/get_spec.rb6
-rw-r--r--spec/ruby/library/csv/streambuf/idx_is_eos_spec.rb6
-rw-r--r--spec/ruby/library/csv/streambuf/initialize_spec.rb6
-rw-r--r--spec/ruby/library/csv/streambuf/is_eos_spec.rb6
-rw-r--r--spec/ruby/library/csv/streambuf/read_spec.rb6
-rw-r--r--spec/ruby/library/csv/streambuf/rel_buf_spec.rb6
-rw-r--r--spec/ruby/library/csv/streambuf/terminate_spec.rb6
-rw-r--r--spec/ruby/library/csv/stringreader/get_row_spec.rb6
-rw-r--r--spec/ruby/library/csv/stringreader/initialize_spec.rb6
-rw-r--r--spec/ruby/library/csv/writer/add_row_spec.rb6
-rw-r--r--spec/ruby/library/csv/writer/append_spec.rb6
-rw-r--r--spec/ruby/library/csv/writer/close_spec.rb6
-rw-r--r--spec/ruby/library/csv/writer/create_spec.rb6
-rw-r--r--spec/ruby/library/csv/writer/generate_spec.rb6
-rw-r--r--spec/ruby/library/csv/writer/initialize_spec.rb6
-rw-r--r--spec/ruby/library/csv/writer/terminate_spec.rb6
-rw-r--r--spec/ruby/library/date/accessor_spec.rb91
-rw-r--r--spec/ruby/library/date/add_month_spec.rb38
-rw-r--r--spec/ruby/library/date/add_spec.rb30
-rw-r--r--spec/ruby/library/date/ajd_spec.rb6
-rw-r--r--spec/ruby/library/date/ajd_to_amjd_spec.rb6
-rw-r--r--spec/ruby/library/date/ajd_to_jd_spec.rb6
-rw-r--r--spec/ruby/library/date/amjd_spec.rb6
-rw-r--r--spec/ruby/library/date/amjd_to_ajd_spec.rb6
-rw-r--r--spec/ruby/library/date/append_spec.rb6
-rw-r--r--spec/ruby/library/date/asctime_spec.rb6
-rw-r--r--spec/ruby/library/date/boat_spec.rb24
-rw-r--r--spec/ruby/library/date/case_compare_spec.rb6
-rw-r--r--spec/ruby/library/date/civil_spec.rb12
-rw-r--r--spec/ruby/library/date/commercial_spec.rb17
-rw-r--r--spec/ruby/library/date/commercial_to_jd_spec.rb6
-rw-r--r--spec/ruby/library/date/comparison_spec.rb6
-rw-r--r--spec/ruby/library/date/constants_spec.rb48
-rw-r--r--spec/ruby/library/date/conversions_spec.rb43
-rw-r--r--spec/ruby/library/date/ctime_spec.rb6
-rw-r--r--spec/ruby/library/date/cwday_spec.rb6
-rw-r--r--spec/ruby/library/date/cweek_spec.rb6
-rw-r--r--spec/ruby/library/date/cwyear_spec.rb6
-rw-r--r--spec/ruby/library/date/day_fraction_spec.rb6
-rw-r--r--spec/ruby/library/date/day_fraction_to_time_spec.rb6
-rw-r--r--spec/ruby/library/date/day_spec.rb9
-rw-r--r--spec/ruby/library/date/downto_spec.rb18
-rw-r--r--spec/ruby/library/date/england_spec.rb6
-rw-r--r--spec/ruby/library/date/eql_spec.rb12
-rw-r--r--spec/ruby/library/date/format/bag/method_missing_spec.rb6
-rw-r--r--spec/ruby/library/date/format/bag/to_hash_spec.rb6
-rw-r--r--spec/ruby/library/date/friday_spec.rb12
-rw-r--r--spec/ruby/library/date/gregorian_leap_spec.rb15
-rw-r--r--spec/ruby/library/date/gregorian_spec.rb16
-rw-r--r--spec/ruby/library/date/hash_spec.rb8
-rw-r--r--spec/ruby/library/date/infinity/abs_spec.rb6
-rw-r--r--spec/ruby/library/date/infinity/coerce_spec.rb6
-rw-r--r--spec/ruby/library/date/infinity/comparison_spec.rb6
-rw-r--r--spec/ruby/library/date/infinity/d_spec.rb6
-rw-r--r--spec/ruby/library/date/infinity/finite_spec.rb6
-rw-r--r--spec/ruby/library/date/infinity/infinite_spec.rb6
-rw-r--r--spec/ruby/library/date/infinity/nan_spec.rb6
-rw-r--r--spec/ruby/library/date/infinity/uminus_spec.rb6
-rw-r--r--spec/ruby/library/date/infinity/uplus_spec.rb6
-rw-r--r--spec/ruby/library/date/infinity/zero_spec.rb6
-rw-r--r--spec/ruby/library/date/infinity_spec.rb67
-rw-r--r--spec/ruby/library/date/inspect_spec.rb6
-rw-r--r--spec/ruby/library/date/iso8601_spec.rb37
-rw-r--r--spec/ruby/library/date/italy_spec.rb6
-rw-r--r--spec/ruby/library/date/jd_spec.rb15
-rw-r--r--spec/ruby/library/date/jd_to_ajd_spec.rb6
-rw-r--r--spec/ruby/library/date/jd_to_civil_spec.rb6
-rw-r--r--spec/ruby/library/date/jd_to_commercial_spec.rb6
-rw-r--r--spec/ruby/library/date/jd_to_ld_spec.rb6
-rw-r--r--spec/ruby/library/date/jd_to_mjd_spec.rb6
-rw-r--r--spec/ruby/library/date/jd_to_ordinal_spec.rb6
-rw-r--r--spec/ruby/library/date/jd_to_wday_spec.rb6
-rw-r--r--spec/ruby/library/date/julian_leap_spec.rb15
-rw-r--r--spec/ruby/library/date/julian_spec.rb16
-rw-r--r--spec/ruby/library/date/ld_spec.rb6
-rw-r--r--spec/ruby/library/date/ld_to_jd_spec.rb6
-rw-r--r--spec/ruby/library/date/leap_spec.rb10
-rw-r--r--spec/ruby/library/date/mday_spec.rb6
-rw-r--r--spec/ruby/library/date/minus_month_spec.rb23
-rw-r--r--spec/ruby/library/date/minus_spec.rb30
-rw-r--r--spec/ruby/library/date/mjd_spec.rb6
-rw-r--r--spec/ruby/library/date/mjd_to_jd_spec.rb6
-rw-r--r--spec/ruby/library/date/mon_spec.rb6
-rw-r--r--spec/ruby/library/date/monday_spec.rb8
-rw-r--r--spec/ruby/library/date/month_spec.rb9
-rw-r--r--spec/ruby/library/date/new_spec.rb8
-rw-r--r--spec/ruby/library/date/new_start_spec.rb6
-rw-r--r--spec/ruby/library/date/next_day_spec.rb14
-rw-r--r--spec/ruby/library/date/next_month_spec.rb29
-rw-r--r--spec/ruby/library/date/next_spec.rb6
-rw-r--r--spec/ruby/library/date/next_year_spec.rb12
-rw-r--r--spec/ruby/library/date/ordinal_spec.rb7
-rw-r--r--spec/ruby/library/date/ordinal_to_jd_spec.rb6
-rw-r--r--spec/ruby/library/date/parse_spec.rb142
-rw-r--r--spec/ruby/library/date/plus_spec.rb20
-rw-r--r--spec/ruby/library/date/prev_day_spec.rb14
-rw-r--r--spec/ruby/library/date/prev_month_spec.rb29
-rw-r--r--spec/ruby/library/date/prev_year_spec.rb12
-rw-r--r--spec/ruby/library/date/relationship_spec.rb20
-rw-r--r--spec/ruby/library/date/right_shift_spec.rb6
-rw-r--r--spec/ruby/library/date/saturday_spec.rb8
-rw-r--r--spec/ruby/library/date/shared/civil.rb57
-rw-r--r--spec/ruby/library/date/shared/commercial.rb39
-rw-r--r--spec/ruby/library/date/shared/jd.rb14
-rw-r--r--spec/ruby/library/date/shared/new_bang.rb14
-rw-r--r--spec/ruby/library/date/shared/ordinal.rb22
-rw-r--r--spec/ruby/library/date/shared/parse.rb54
-rw-r--r--spec/ruby/library/date/shared/parse_eu.rb37
-rw-r--r--spec/ruby/library/date/shared/parse_us.rb36
-rw-r--r--spec/ruby/library/date/shared/valid_civil.rb36
-rw-r--r--spec/ruby/library/date/shared/valid_commercial.rb34
-rw-r--r--spec/ruby/library/date/shared/valid_jd.rb15
-rw-r--r--spec/ruby/library/date/shared/valid_ordinal.rb26
-rw-r--r--spec/ruby/library/date/start_spec.rb6
-rw-r--r--spec/ruby/library/date/step_spec.rb56
-rw-r--r--spec/ruby/library/date/strftime_spec.rb40
-rw-r--r--spec/ruby/library/date/strptime_spec.rb149
-rw-r--r--spec/ruby/library/date/succ_spec.rb6
-rw-r--r--spec/ruby/library/date/sunday_spec.rb8
-rw-r--r--spec/ruby/library/date/thursday_spec.rb8
-rw-r--r--spec/ruby/library/date/time_to_day_fraction_spec.rb6
-rw-r--r--spec/ruby/library/date/to_s_spec.rb6
-rw-r--r--spec/ruby/library/date/today_spec.rb14
-rw-r--r--spec/ruby/library/date/tuesday_spec.rb8
-rw-r--r--spec/ruby/library/date/upto_spec.rb16
-rw-r--r--spec/ruby/library/date/valid_civil_spec.rb9
-rw-r--r--spec/ruby/library/date/valid_commercial_spec.rb8
-rw-r--r--spec/ruby/library/date/valid_date_spec.rb7
-rw-r--r--spec/ruby/library/date/valid_jd_spec.rb9
-rw-r--r--spec/ruby/library/date/valid_ordinal_spec.rb9
-rw-r--r--spec/ruby/library/date/valid_time_spec.rb6
-rw-r--r--spec/ruby/library/date/wday_spec.rb9
-rw-r--r--spec/ruby/library/date/wednesday_spec.rb8
-rw-r--r--spec/ruby/library/date/yday_spec.rb6
-rw-r--r--spec/ruby/library/date/year_spec.rb9
-rw-r--r--spec/ruby/library/date/zone_to_diff_spec.rb6
-rw-r--r--spec/ruby/library/datetime/_strptime_spec.rb6
-rw-r--r--spec/ruby/library/datetime/add_spec.rb9
-rw-r--r--spec/ruby/library/datetime/civil_spec.rb6
-rw-r--r--spec/ruby/library/datetime/commercial_spec.rb6
-rw-r--r--spec/ruby/library/datetime/hour_spec.rb47
-rw-r--r--spec/ruby/library/datetime/httpdate_spec.rb6
-rw-r--r--spec/ruby/library/datetime/iso8601_spec.rb10
-rw-r--r--spec/ruby/library/datetime/jd_spec.rb6
-rw-r--r--spec/ruby/library/datetime/jisx0301_spec.rb10
-rw-r--r--spec/ruby/library/datetime/min_spec.rb6
-rw-r--r--spec/ruby/library/datetime/minute_spec.rb6
-rw-r--r--spec/ruby/library/datetime/new_offset_spec.rb6
-rw-r--r--spec/ruby/library/datetime/new_spec.rb52
-rw-r--r--spec/ruby/library/datetime/now_spec.rb25
-rw-r--r--spec/ruby/library/datetime/offset_spec.rb6
-rw-r--r--spec/ruby/library/datetime/ordinal_spec.rb6
-rw-r--r--spec/ruby/library/datetime/parse_spec.rb127
-rw-r--r--spec/ruby/library/datetime/rfc2822_spec.rb6
-rw-r--r--spec/ruby/library/datetime/rfc3339_spec.rb10
-rw-r--r--spec/ruby/library/datetime/rfc822_spec.rb6
-rw-r--r--spec/ruby/library/datetime/sec_fraction_spec.rb6
-rw-r--r--spec/ruby/library/datetime/sec_spec.rb6
-rw-r--r--spec/ruby/library/datetime/second_fraction_spec.rb6
-rw-r--r--spec/ruby/library/datetime/second_spec.rb6
-rw-r--r--spec/ruby/library/datetime/shared/min.rb40
-rw-r--r--spec/ruby/library/datetime/shared/sec.rb45
-rw-r--r--spec/ruby/library/datetime/strftime_spec.rb51
-rw-r--r--spec/ruby/library/datetime/strptime_spec.rb6
-rw-r--r--spec/ruby/library/datetime/subtract_spec.rb9
-rw-r--r--spec/ruby/library/datetime/to_date_spec.rb37
-rw-r--r--spec/ruby/library/datetime/to_datetime_spec.rb9
-rw-r--r--spec/ruby/library/datetime/to_s_spec.rb17
-rw-r--r--spec/ruby/library/datetime/to_time_spec.rb38
-rw-r--r--spec/ruby/library/datetime/xmlschema_spec.rb10
-rw-r--r--spec/ruby/library/datetime/zone_spec.rb6
-rw-r--r--spec/ruby/library/delegate/delegate_class/instance_method_spec.rb52
-rw-r--r--spec/ruby/library/delegate/delegate_class/instance_methods_spec.rb26
-rw-r--r--spec/ruby/library/delegate/delegate_class/private_instance_methods_spec.rb23
-rw-r--r--spec/ruby/library/delegate/delegate_class/protected_instance_methods_spec.rb29
-rw-r--r--spec/ruby/library/delegate/delegate_class/public_instance_methods_spec.rb25
-rw-r--r--spec/ruby/library/delegate/delegate_class/respond_to_missing_spec.rb23
-rw-r--r--spec/ruby/library/delegate/delegator/case_compare_spec.rb11
-rw-r--r--spec/ruby/library/delegate/delegator/compare_spec.rb11
-rw-r--r--spec/ruby/library/delegate/delegator/complement_spec.rb11
-rw-r--r--spec/ruby/library/delegate/delegator/eql_spec.rb46
-rw-r--r--spec/ruby/library/delegate/delegator/equal_spec.rb13
-rw-r--r--spec/ruby/library/delegate/delegator/equal_value_spec.rb24
-rw-r--r--spec/ruby/library/delegate/delegator/frozen_spec.rb39
-rw-r--r--spec/ruby/library/delegate/delegator/hash_spec.rb11
-rw-r--r--spec/ruby/library/delegate/delegator/marshal_spec.rb21
-rw-r--r--spec/ruby/library/delegate/delegator/method_spec.rb69
-rw-r--r--spec/ruby/library/delegate/delegator/methods_spec.rb37
-rw-r--r--spec/ruby/library/delegate/delegator/not_equal_spec.rb24
-rw-r--r--spec/ruby/library/delegate/delegator/not_spec.rb11
-rw-r--r--spec/ruby/library/delegate/delegator/private_methods_spec.rb20
-rw-r--r--spec/ruby/library/delegate/delegator/protected_methods_spec.rb18
-rw-r--r--spec/ruby/library/delegate/delegator/public_methods_spec.rb18
-rw-r--r--spec/ruby/library/delegate/delegator/send_spec.rb26
-rw-r--r--spec/ruby/library/delegate/delegator/taint_spec.rb23
-rw-r--r--spec/ruby/library/delegate/delegator/tap_spec.rb16
-rw-r--r--spec/ruby/library/delegate/delegator/trust_spec.rb22
-rw-r--r--spec/ruby/library/delegate/delegator/untaint_spec.rb24
-rw-r--r--spec/ruby/library/delegate/delegator/untrust_spec.rb23
-rw-r--r--spec/ruby/library/delegate/fixtures/classes.rb60
-rw-r--r--spec/ruby/library/digest/bubblebabble_spec.rb29
-rw-r--r--spec/ruby/library/digest/hexencode_spec.rb31
-rw-r--r--spec/ruby/library/digest/md5/append_spec.rb7
-rw-r--r--spec/ruby/library/digest/md5/block_length_spec.rb11
-rw-r--r--spec/ruby/library/digest/md5/digest_bang_spec.rb13
-rw-r--r--spec/ruby/library/digest/md5/digest_length_spec.rb11
-rw-r--r--spec/ruby/library/digest/md5/digest_spec.rb32
-rw-r--r--spec/ruby/library/digest/md5/equal_spec.rb37
-rw-r--r--spec/ruby/library/digest/md5/file_spec.rb43
-rw-r--r--spec/ruby/library/digest/md5/hexdigest_bang_spec.rb14
-rw-r--r--spec/ruby/library/digest/md5/hexdigest_spec.rb32
-rw-r--r--spec/ruby/library/digest/md5/inspect_spec.rb11
-rw-r--r--spec/ruby/library/digest/md5/length_spec.rb7
-rw-r--r--spec/ruby/library/digest/md5/reset_spec.rb14
-rw-r--r--spec/ruby/library/digest/md5/shared/constants.rb16
-rw-r--r--spec/ruby/library/digest/md5/shared/length.rb8
-rw-r--r--spec/ruby/library/digest/md5/shared/sample.rb17
-rw-r--r--spec/ruby/library/digest/md5/shared/update.rb7
-rw-r--r--spec/ruby/library/digest/md5/size_spec.rb7
-rw-r--r--spec/ruby/library/digest/md5/to_s_spec.rb24
-rw-r--r--spec/ruby/library/digest/md5/update_spec.rb7
-rw-r--r--spec/ruby/library/digest/sha1/digest_spec.rb20
-rw-r--r--spec/ruby/library/digest/sha1/file_spec.rb43
-rw-r--r--spec/ruby/library/digest/sha1/shared/constants.rb17
-rw-r--r--spec/ruby/library/digest/sha256/append_spec.rb7
-rw-r--r--spec/ruby/library/digest/sha256/block_length_spec.rb11
-rw-r--r--spec/ruby/library/digest/sha256/digest_bang_spec.rb13
-rw-r--r--spec/ruby/library/digest/sha256/digest_length_spec.rb11
-rw-r--r--spec/ruby/library/digest/sha256/digest_spec.rb32
-rw-r--r--spec/ruby/library/digest/sha256/equal_spec.rb36
-rw-r--r--spec/ruby/library/digest/sha256/file_spec.rb43
-rw-r--r--spec/ruby/library/digest/sha256/hexdigest_bang_spec.rb14
-rw-r--r--spec/ruby/library/digest/sha256/hexdigest_spec.rb32
-rw-r--r--spec/ruby/library/digest/sha256/inspect_spec.rb11
-rw-r--r--spec/ruby/library/digest/sha256/length_spec.rb7
-rw-r--r--spec/ruby/library/digest/sha256/reset_spec.rb14
-rw-r--r--spec/ruby/library/digest/sha256/shared/constants.rb17
-rw-r--r--spec/ruby/library/digest/sha256/shared/length.rb8
-rw-r--r--spec/ruby/library/digest/sha256/shared/update.rb7
-rw-r--r--spec/ruby/library/digest/sha256/size_spec.rb7
-rw-r--r--spec/ruby/library/digest/sha256/to_s_spec.rb21
-rw-r--r--spec/ruby/library/digest/sha256/update_spec.rb7
-rw-r--r--spec/ruby/library/digest/sha384/append_spec.rb7
-rw-r--r--spec/ruby/library/digest/sha384/block_length_spec.rb11
-rw-r--r--spec/ruby/library/digest/sha384/digest_bang_spec.rb13
-rw-r--r--spec/ruby/library/digest/sha384/digest_length_spec.rb11
-rw-r--r--spec/ruby/library/digest/sha384/digest_spec.rb32
-rw-r--r--spec/ruby/library/digest/sha384/equal_spec.rb36
-rw-r--r--spec/ruby/library/digest/sha384/file_spec.rb43
-rw-r--r--spec/ruby/library/digest/sha384/hexdigest_bang_spec.rb14
-rw-r--r--spec/ruby/library/digest/sha384/hexdigest_spec.rb32
-rw-r--r--spec/ruby/library/digest/sha384/inspect_spec.rb11
-rw-r--r--spec/ruby/library/digest/sha384/length_spec.rb7
-rw-r--r--spec/ruby/library/digest/sha384/reset_spec.rb14
-rw-r--r--spec/ruby/library/digest/sha384/shared/constants.rb18
-rw-r--r--spec/ruby/library/digest/sha384/shared/length.rb8
-rw-r--r--spec/ruby/library/digest/sha384/shared/update.rb7
-rw-r--r--spec/ruby/library/digest/sha384/size_spec.rb7
-rw-r--r--spec/ruby/library/digest/sha384/to_s_spec.rb21
-rw-r--r--spec/ruby/library/digest/sha384/update_spec.rb7
-rw-r--r--spec/ruby/library/digest/sha512/append_spec.rb7
-rw-r--r--spec/ruby/library/digest/sha512/block_length_spec.rb11
-rw-r--r--spec/ruby/library/digest/sha512/digest_bang_spec.rb13
-rw-r--r--spec/ruby/library/digest/sha512/digest_length_spec.rb11
-rw-r--r--spec/ruby/library/digest/sha512/digest_spec.rb32
-rw-r--r--spec/ruby/library/digest/sha512/equal_spec.rb36
-rw-r--r--spec/ruby/library/digest/sha512/file_spec.rb43
-rw-r--r--spec/ruby/library/digest/sha512/hexdigest_bang_spec.rb14
-rw-r--r--spec/ruby/library/digest/sha512/hexdigest_spec.rb32
-rw-r--r--spec/ruby/library/digest/sha512/inspect_spec.rb11
-rw-r--r--spec/ruby/library/digest/sha512/length_spec.rb7
-rw-r--r--spec/ruby/library/digest/sha512/reset_spec.rb14
-rw-r--r--spec/ruby/library/digest/sha512/shared/constants.rb17
-rw-r--r--spec/ruby/library/digest/sha512/shared/length.rb8
-rw-r--r--spec/ruby/library/digest/sha512/shared/update.rb7
-rw-r--r--spec/ruby/library/digest/sha512/size_spec.rb7
-rw-r--r--spec/ruby/library/digest/sha512/to_s_spec.rb21
-rw-r--r--spec/ruby/library/digest/sha512/update_spec.rb7
-rw-r--r--spec/ruby/library/drb/fixtures/test_server.rb8
-rw-r--r--spec/ruby/library/drb/start_service_spec.rb28
-rw-r--r--spec/ruby/library/erb/def_class_spec.rb29
-rw-r--r--spec/ruby/library/erb/def_method_spec.rb26
-rw-r--r--spec/ruby/library/erb/def_module_spec.rb27
-rw-r--r--spec/ruby/library/erb/defmethod/def_erb_method_spec.rb64
-rw-r--r--spec/ruby/library/erb/filename_spec.rb40
-rw-r--r--spec/ruby/library/erb/fixtures/classes.rb9
-rw-r--r--spec/ruby/library/erb/new_spec.rb148
-rw-r--r--spec/ruby/library/erb/result_spec.rb86
-rw-r--r--spec/ruby/library/erb/run_spec.rb96
-rw-r--r--spec/ruby/library/erb/src_spec.rb33
-rw-r--r--spec/ruby/library/erb/util/h_spec.rb7
-rw-r--r--spec/ruby/library/erb/util/html_escape_spec.rb7
-rw-r--r--spec/ruby/library/erb/util/shared/html_escape.rb42
-rw-r--r--spec/ruby/library/erb/util/shared/url_encode.rb50
-rw-r--r--spec/ruby/library/erb/util/u_spec.rb7
-rw-r--r--spec/ruby/library/erb/util/url_encode_spec.rb7
-rw-r--r--spec/ruby/library/etc/confstr_spec.rb14
-rw-r--r--spec/ruby/library/etc/endgrent_spec.rb7
-rw-r--r--spec/ruby/library/etc/endpwent_spec.rb7
-rw-r--r--spec/ruby/library/etc/getgrent_spec.rb7
-rw-r--r--spec/ruby/library/etc/getgrgid_spec.rb76
-rw-r--r--spec/ruby/library/etc/getgrnam_spec.rb30
-rw-r--r--spec/ruby/library/etc/getlogin_spec.rb43
-rw-r--r--spec/ruby/library/etc/getpwent_spec.rb7
-rw-r--r--spec/ruby/library/etc/getpwnam_spec.rb28
-rw-r--r--spec/ruby/library/etc/getpwuid_spec.rb36
-rw-r--r--spec/ruby/library/etc/group_spec.rb27
-rw-r--r--spec/ruby/library/etc/nprocessors_spec.rb9
-rw-r--r--spec/ruby/library/etc/passwd_spec.rb15
-rw-r--r--spec/ruby/library/etc/shared/windows.rb7
-rw-r--r--spec/ruby/library/etc/struct_group_spec.rb35
-rw-r--r--spec/ruby/library/etc/struct_passwd_spec.rb43
-rw-r--r--spec/ruby/library/etc/sysconf_spec.rb22
-rw-r--r--spec/ruby/library/etc/sysconfdir_spec.rb8
-rw-r--r--spec/ruby/library/etc/systmpdir_spec.rb8
-rw-r--r--spec/ruby/library/expect/expect_spec.rb62
-rw-r--r--spec/ruby/library/fiber/alive_spec.rb48
-rw-r--r--spec/ruby/library/fiber/current_spec.rb53
-rw-r--r--spec/ruby/library/fiber/resume_spec.rb14
-rw-r--r--spec/ruby/library/fiber/transfer_spec.rb88
-rw-r--r--spec/ruby/library/find/find_spec.rb30
-rw-r--r--spec/ruby/library/find/fixtures/common.rb174
-rw-r--r--spec/ruby/library/find/prune_spec.rb12
-rw-r--r--spec/ruby/library/getoptlong/each_option_spec.rb7
-rw-r--r--spec/ruby/library/getoptlong/each_spec.rb7
-rw-r--r--spec/ruby/library/getoptlong/error_message_spec.rb23
-rw-r--r--spec/ruby/library/getoptlong/get_option_spec.rb7
-rw-r--r--spec/ruby/library/getoptlong/get_spec.rb7
-rw-r--r--spec/ruby/library/getoptlong/initialize_spec.rb28
-rw-r--r--spec/ruby/library/getoptlong/ordering_spec.rb38
-rw-r--r--spec/ruby/library/getoptlong/set_options_spec.rb98
-rw-r--r--spec/ruby/library/getoptlong/shared/each.rb18
-rw-r--r--spec/ruby/library/getoptlong/shared/get.rb64
-rw-r--r--spec/ruby/library/getoptlong/terminate_spec.rb30
-rw-r--r--spec/ruby/library/getoptlong/terminated_spec.rb17
-rw-r--r--spec/ruby/library/ipaddr/hton_spec.rb30
-rw-r--r--spec/ruby/library/ipaddr/ipv4_conversion_spec.rb44
-rw-r--r--spec/ruby/library/ipaddr/new_spec.rb93
-rw-r--r--spec/ruby/library/ipaddr/operator_spec.rb87
-rw-r--r--spec/ruby/library/ipaddr/reverse_spec.rb27
-rw-r--r--spec/ruby/library/ipaddr/to_s_spec.rb20
-rw-r--r--spec/ruby/library/logger/device/close_spec.rb22
-rw-r--r--spec/ruby/library/logger/device/new_spec.rb47
-rw-r--r--spec/ruby/library/logger/device/write_spec.rb42
-rw-r--r--spec/ruby/library/logger/fixtures/common.rb9
-rw-r--r--spec/ruby/library/logger/logger/add_spec.rb81
-rw-r--r--spec/ruby/library/logger/logger/close_spec.rb20
-rw-r--r--spec/ruby/library/logger/logger/datetime_format_spec.rb60
-rw-r--r--spec/ruby/library/logger/logger/debug_spec.rb52
-rw-r--r--spec/ruby/library/logger/logger/error_spec.rb53
-rw-r--r--spec/ruby/library/logger/logger/fatal_spec.rb53
-rw-r--r--spec/ruby/library/logger/logger/info_spec.rb53
-rw-r--r--spec/ruby/library/logger/logger/new_spec.rb120
-rw-r--r--spec/ruby/library/logger/logger/unknown_spec.rb36
-rw-r--r--spec/ruby/library/logger/logger/warn_spec.rb53
-rw-r--r--spec/ruby/library/logger/severity_spec.rb13
-rw-r--r--spec/ruby/library/mathn/bignum/exponent_spec.rb21
-rw-r--r--spec/ruby/library/mathn/complex/Complex_spec.rb14
-rw-r--r--spec/ruby/library/mathn/fixnum/exponent_spec.rb17
-rw-r--r--spec/ruby/library/mathn/float/exponent_spec.rb17
-rw-r--r--spec/ruby/library/mathn/integer/from_prime_division_spec.rb11
-rw-r--r--spec/ruby/library/mathn/integer/prime_division_spec.rb21
-rw-r--r--spec/ruby/library/mathn/math/fixtures/classes.rb3
-rw-r--r--spec/ruby/library/mathn/math/rsqrt_spec.rb17
-rw-r--r--spec/ruby/library/mathn/math/shared/rsqrt.rb21
-rw-r--r--spec/ruby/library/mathn/math/shared/sqrt.rb25
-rw-r--r--spec/ruby/library/mathn/math/sqrt_spec.rb17
-rw-r--r--spec/ruby/library/mathn/mathn_spec.rb9
-rw-r--r--spec/ruby/library/mathn/rational/Rational_spec.rb14
-rw-r--r--spec/ruby/library/mathn/rational/inspect_spec.rb15
-rw-r--r--spec/ruby/library/matrix/I_spec.rb6
-rw-r--r--spec/ruby/library/matrix/antisymmetric_spec.rb37
-rw-r--r--spec/ruby/library/matrix/build_spec.rb73
-rw-r--r--spec/ruby/library/matrix/clone_spec.rb25
-rw-r--r--spec/ruby/library/matrix/coerce_spec.rb10
-rw-r--r--spec/ruby/library/matrix/collect_spec.rb6
-rw-r--r--spec/ruby/library/matrix/column_size_spec.rb13
-rw-r--r--spec/ruby/library/matrix/column_spec.rb35
-rw-r--r--spec/ruby/library/matrix/column_vector_spec.rb25
-rw-r--r--spec/ruby/library/matrix/column_vectors_spec.rb26
-rw-r--r--spec/ruby/library/matrix/columns_spec.rb42
-rw-r--r--spec/ruby/library/matrix/conj_spec.rb6
-rw-r--r--spec/ruby/library/matrix/conjugate_spec.rb6
-rw-r--r--spec/ruby/library/matrix/constructor_spec.rb65
-rw-r--r--spec/ruby/library/matrix/det_spec.rb7
-rw-r--r--spec/ruby/library/matrix/determinant_spec.rb7
-rw-r--r--spec/ruby/library/matrix/diagonal_spec.rb72
-rw-r--r--spec/ruby/library/matrix/divide_spec.rb54
-rw-r--r--spec/ruby/library/matrix/each_spec.rb74
-rw-r--r--spec/ruby/library/matrix/each_with_index_spec.rb81
-rw-r--r--spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalue_matrix_spec.rb9
-rw-r--r--spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalues_spec.rb22
-rw-r--r--spec/ruby/library/matrix/eigenvalue_decomposition/eigenvector_matrix_spec.rb20
-rw-r--r--spec/ruby/library/matrix/eigenvalue_decomposition/eigenvectors_spec.rb22
-rw-r--r--spec/ruby/library/matrix/eigenvalue_decomposition/initialize_spec.rb24
-rw-r--r--spec/ruby/library/matrix/eigenvalue_decomposition/to_a_spec.rb18
-rw-r--r--spec/ruby/library/matrix/element_reference_spec.rb23
-rw-r--r--spec/ruby/library/matrix/empty_spec.rb68
-rw-r--r--spec/ruby/library/matrix/eql_spec.rb11
-rw-r--r--spec/ruby/library/matrix/equal_value_spec.rb11
-rw-r--r--spec/ruby/library/matrix/exponent_spec.rb51
-rw-r--r--spec/ruby/library/matrix/find_index_spec.rb146
-rw-r--r--spec/ruby/library/matrix/fixtures/classes.rb7
-rw-r--r--spec/ruby/library/matrix/hash_spec.rb15
-rw-r--r--spec/ruby/library/matrix/hermitian_spec.rb34
-rw-r--r--spec/ruby/library/matrix/identity_spec.rb6
-rw-r--r--spec/ruby/library/matrix/imag_spec.rb6
-rw-r--r--spec/ruby/library/matrix/imaginary_spec.rb6
-rw-r--r--spec/ruby/library/matrix/inspect_spec.rb27
-rw-r--r--spec/ruby/library/matrix/inv_spec.rb7
-rw-r--r--spec/ruby/library/matrix/inverse_from_spec.rb6
-rw-r--r--spec/ruby/library/matrix/inverse_spec.rb7
-rw-r--r--spec/ruby/library/matrix/lower_triangular_spec.rb24
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/determinant_spec.rb21
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/initialize_spec.rb13
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/l_spec.rb18
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/p_spec.rb18
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/solve_spec.rb53
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/to_a_spec.rb33
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/u_spec.rb18
-rw-r--r--spec/ruby/library/matrix/map_spec.rb6
-rw-r--r--spec/ruby/library/matrix/minor_spec.rb85
-rw-r--r--spec/ruby/library/matrix/minus_spec.rb42
-rw-r--r--spec/ruby/library/matrix/multiply_spec.rb68
-rw-r--r--spec/ruby/library/matrix/new_spec.rb8
-rw-r--r--spec/ruby/library/matrix/normal_spec.rb26
-rw-r--r--spec/ruby/library/matrix/orthogonal_spec.rb26
-rw-r--r--spec/ruby/library/matrix/permutation_spec.rb32
-rw-r--r--spec/ruby/library/matrix/plus_spec.rb42
-rw-r--r--spec/ruby/library/matrix/rank_spec.rb19
-rw-r--r--spec/ruby/library/matrix/real_spec.rb43
-rw-r--r--spec/ruby/library/matrix/rect_spec.rb6
-rw-r--r--spec/ruby/library/matrix/rectangular_spec.rb6
-rw-r--r--spec/ruby/library/matrix/regular_spec.rb31
-rw-r--r--spec/ruby/library/matrix/round_spec.rb21
-rw-r--r--spec/ruby/library/matrix/row_size_spec.rb13
-rw-r--r--spec/ruby/library/matrix/row_spec.rb36
-rw-r--r--spec/ruby/library/matrix/row_vector_spec.rb24
-rw-r--r--spec/ruby/library/matrix/row_vectors_spec.rb26
-rw-r--r--spec/ruby/library/matrix/rows_spec.rb41
-rw-r--r--spec/ruby/library/matrix/scalar/Fail_spec.rb6
-rw-r--r--spec/ruby/library/matrix/scalar/Raise_spec.rb6
-rw-r--r--spec/ruby/library/matrix/scalar/divide_spec.rb6
-rw-r--r--spec/ruby/library/matrix/scalar/exponent_spec.rb6
-rw-r--r--spec/ruby/library/matrix/scalar/included_spec.rb6
-rw-r--r--spec/ruby/library/matrix/scalar/initialize_spec.rb6
-rw-r--r--spec/ruby/library/matrix/scalar/minus_spec.rb6
-rw-r--r--spec/ruby/library/matrix/scalar/multiply_spec.rb6
-rw-r--r--spec/ruby/library/matrix/scalar/plus_spec.rb6
-rw-r--r--spec/ruby/library/matrix/scalar_spec.rb67
-rw-r--r--spec/ruby/library/matrix/shared/collect.rb26
-rw-r--r--spec/ruby/library/matrix/shared/conjugate.rb20
-rw-r--r--spec/ruby/library/matrix/shared/determinant.rb38
-rw-r--r--spec/ruby/library/matrix/shared/equal_value.rb33
-rw-r--r--spec/ruby/library/matrix/shared/identity.rb19
-rw-r--r--spec/ruby/library/matrix/shared/imaginary.rb20
-rw-r--r--spec/ruby/library/matrix/shared/inverse.rb38
-rw-r--r--spec/ruby/library/matrix/shared/rectangular.rb18
-rw-r--r--spec/ruby/library/matrix/shared/trace.rb12
-rw-r--r--spec/ruby/library/matrix/shared/transpose.rb19
-rw-r--r--spec/ruby/library/matrix/singular_spec.rb31
-rw-r--r--spec/ruby/library/matrix/spec_helper.rb35
-rw-r--r--spec/ruby/library/matrix/square_spec.rb28
-rw-r--r--spec/ruby/library/matrix/symmetric_spec.rb29
-rw-r--r--spec/ruby/library/matrix/t_spec.rb6
-rw-r--r--spec/ruby/library/matrix/to_a_spec.rb11
-rw-r--r--spec/ruby/library/matrix/to_s_spec.rb6
-rw-r--r--spec/ruby/library/matrix/tr_spec.rb7
-rw-r--r--spec/ruby/library/matrix/trace_spec.rb7
-rw-r--r--spec/ruby/library/matrix/transpose_spec.rb6
-rw-r--r--spec/ruby/library/matrix/unit_spec.rb6
-rw-r--r--spec/ruby/library/matrix/unitary_spec.rb28
-rw-r--r--spec/ruby/library/matrix/upper_triangular_spec.rb24
-rw-r--r--spec/ruby/library/matrix/vector/cross_product_spec.rb14
-rw-r--r--spec/ruby/library/matrix/vector/each2_spec.rb49
-rw-r--r--spec/ruby/library/matrix/vector/eql_spec.rb16
-rw-r--r--spec/ruby/library/matrix/vector/inner_product_spec.rb22
-rw-r--r--spec/ruby/library/matrix/vector/normalize_spec.rb18
-rw-r--r--spec/ruby/library/matrix/zero_spec.rb52
-rw-r--r--spec/ruby/library/monitor/mon_initialize_spec.rb31
-rw-r--r--spec/ruby/library/net/FTPError_spec.rb8
-rw-r--r--spec/ruby/library/net/FTPPermError_spec.rb12
-rw-r--r--spec/ruby/library/net/FTPProtoError_spec.rb12
-rw-r--r--spec/ruby/library/net/FTPReplyError_spec.rb12
-rw-r--r--spec/ruby/library/net/FTPTempError_spec.rb12
-rw-r--r--spec/ruby/library/net/ftp/abort_spec.rb62
-rw-r--r--spec/ruby/library/net/ftp/acct_spec.rb58
-rw-r--r--spec/ruby/library/net/ftp/binary_spec.rb24
-rw-r--r--spec/ruby/library/net/ftp/chdir_spec.rb99
-rw-r--r--spec/ruby/library/net/ftp/close_spec.rb30
-rw-r--r--spec/ruby/library/net/ftp/closed_spec.rb21
-rw-r--r--spec/ruby/library/net/ftp/connect_spec.rb49
-rw-r--r--spec/ruby/library/net/ftp/debug_mode_spec.rb23
-rw-r--r--spec/ruby/library/net/ftp/default_passive_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/delete_spec.rb59
-rw-r--r--spec/ruby/library/net/ftp/dir_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/fixtures/default_passive.rb3
-rw-r--r--spec/ruby/library/net/ftp/fixtures/passive.rb2
-rw-r--r--spec/ruby/library/net/ftp/fixtures/putbinaryfile3
-rw-r--r--spec/ruby/library/net/ftp/fixtures/puttextfile3
-rw-r--r--spec/ruby/library/net/ftp/fixtures/server.rb277
-rw-r--r--spec/ruby/library/net/ftp/get_spec.rb21
-rw-r--r--spec/ruby/library/net/ftp/getbinaryfile_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/getdir_spec.rb7
-rw-r--r--spec/ruby/library/net/ftp/gettextfile_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/help_spec.rb66
-rw-r--r--spec/ruby/library/net/ftp/initialize_spec.rb409
-rw-r--r--spec/ruby/library/net/ftp/last_response_code_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/last_response_spec.rb25
-rw-r--r--spec/ruby/library/net/ftp/lastresp_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/list_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/login_spec.rb195
-rw-r--r--spec/ruby/library/net/ftp/ls_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/mdtm_spec.rb38
-rw-r--r--spec/ruby/library/net/ftp/mkdir_spec.rb61
-rw-r--r--spec/ruby/library/net/ftp/mtime_spec.rb50
-rw-r--r--spec/ruby/library/net/ftp/nlst_spec.rb92
-rw-r--r--spec/ruby/library/net/ftp/noop_spec.rb38
-rw-r--r--spec/ruby/library/net/ftp/open_spec.rb55
-rw-r--r--spec/ruby/library/net/ftp/passive_spec.rb28
-rw-r--r--spec/ruby/library/net/ftp/put_spec.rb21
-rw-r--r--spec/ruby/library/net/ftp/putbinaryfile_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/puttextfile_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/pwd_spec.rb53
-rw-r--r--spec/ruby/library/net/ftp/quit_spec.rb33
-rw-r--r--spec/ruby/library/net/ftp/rename_spec.rb94
-rw-r--r--spec/ruby/library/net/ftp/resume_spec.rb23
-rw-r--r--spec/ruby/library/net/ftp/retrbinary_spec.rb30
-rw-r--r--spec/ruby/library/net/ftp/retrlines_spec.rb34
-rw-r--r--spec/ruby/library/net/ftp/return_code_spec.rb24
-rw-r--r--spec/ruby/library/net/ftp/rmdir_spec.rb58
-rw-r--r--spec/ruby/library/net/ftp/sendcmd_spec.rb54
-rw-r--r--spec/ruby/library/net/ftp/set_socket_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/shared/getbinaryfile.rb150
-rw-r--r--spec/ruby/library/net/ftp/shared/gettextfile.rb100
-rw-r--r--spec/ruby/library/net/ftp/shared/last_response_code.rb25
-rw-r--r--spec/ruby/library/net/ftp/shared/list.rb104
-rw-r--r--spec/ruby/library/net/ftp/shared/putbinaryfile.rb167
-rw-r--r--spec/ruby/library/net/ftp/shared/puttextfile.rb120
-rw-r--r--spec/ruby/library/net/ftp/shared/pwd.rb3
-rw-r--r--spec/ruby/library/net/ftp/site_spec.rb53
-rw-r--r--spec/ruby/library/net/ftp/size_spec.rb48
-rw-r--r--spec/ruby/library/net/ftp/spec_helper.rb5
-rw-r--r--spec/ruby/library/net/ftp/status_spec.rb69
-rw-r--r--spec/ruby/library/net/ftp/storbinary_spec.rb48
-rw-r--r--spec/ruby/library/net/ftp/storlines_spec.rb43
-rw-r--r--spec/ruby/library/net/ftp/system_spec.rb48
-rw-r--r--spec/ruby/library/net/ftp/voidcmd_spec.rb54
-rw-r--r--spec/ruby/library/net/ftp/welcome_spec.rb25
-rw-r--r--spec/ruby/library/net/http/HTTPBadResponse_spec.rb8
-rw-r--r--spec/ruby/library/net/http/HTTPClientExcepton_spec.rb14
-rw-r--r--spec/ruby/library/net/http/HTTPError_spec.rb12
-rw-r--r--spec/ruby/library/net/http/HTTPFatalError_spec.rb12
-rw-r--r--spec/ruby/library/net/http/HTTPHeaderSyntaxError_spec.rb8
-rw-r--r--spec/ruby/library/net/http/HTTPRetriableError_spec.rb12
-rw-r--r--spec/ruby/library/net/http/HTTPServerException_spec.rb26
-rw-r--r--spec/ruby/library/net/http/http/Proxy_spec.rb35
-rw-r--r--spec/ruby/library/net/http/http/active_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/address_spec.rb9
-rw-r--r--spec/ruby/library/net/http/http/close_on_empty_response_spec.rb10
-rw-r--r--spec/ruby/library/net/http/http/copy_spec.rb21
-rw-r--r--spec/ruby/library/net/http/http/default_port_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/delete_spec.rb21
-rw-r--r--spec/ruby/library/net/http/http/finish_spec.rb29
-rw-r--r--spec/ruby/library/net/http/http/fixtures/http_server.rb105
-rw-r--r--spec/ruby/library/net/http/http/get2_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/get_print_spec.rb30
-rw-r--r--spec/ruby/library/net/http/http/get_response_spec.rb30
-rw-r--r--spec/ruby/library/net/http/http/get_spec.rb95
-rw-r--r--spec/ruby/library/net/http/http/head2_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/head_spec.rb25
-rw-r--r--spec/ruby/library/net/http/http/http_default_port_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/https_default_port_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/initialize_spec.rb46
-rw-r--r--spec/ruby/library/net/http/http/inspect_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/is_version_1_1_spec.rb7
-rw-r--r--spec/ruby/library/net/http/http/is_version_1_2_spec.rb7
-rw-r--r--spec/ruby/library/net/http/http/lock_spec.rb21
-rw-r--r--spec/ruby/library/net/http/http/mkcol_spec.rb21
-rw-r--r--spec/ruby/library/net/http/http/move_spec.rb25
-rw-r--r--spec/ruby/library/net/http/http/new_spec.rb86
-rw-r--r--spec/ruby/library/net/http/http/newobj_spec.rb48
-rw-r--r--spec/ruby/library/net/http/http/open_timeout_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/options_spec.rb25
-rw-r--r--spec/ruby/library/net/http/http/port_spec.rb9
-rw-r--r--spec/ruby/library/net/http/http/post2_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/post_form_spec.rb22
-rw-r--r--spec/ruby/library/net/http/http/post_spec.rb76
-rw-r--r--spec/ruby/library/net/http/http/propfind_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/proppatch_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/proxy_address_spec.rb31
-rw-r--r--spec/ruby/library/net/http/http/proxy_class_spec.rb9
-rw-r--r--spec/ruby/library/net/http/http/proxy_pass_spec.rb39
-rw-r--r--spec/ruby/library/net/http/http/proxy_port_spec.rb39
-rw-r--r--spec/ruby/library/net/http/http/proxy_user_spec.rb39
-rw-r--r--spec/ruby/library/net/http/http/put2_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/put_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/read_timeout_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/request_get_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/request_head_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/request_post_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/request_put_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/request_spec.rb109
-rw-r--r--spec/ruby/library/net/http/http/request_types_spec.rb254
-rw-r--r--spec/ruby/library/net/http/http/send_request_spec.rb61
-rw-r--r--spec/ruby/library/net/http/http/set_debug_output_spec.rb33
-rw-r--r--spec/ruby/library/net/http/http/shared/request_get.rb41
-rw-r--r--spec/ruby/library/net/http/http/shared/request_head.rb41
-rw-r--r--spec/ruby/library/net/http/http/shared/request_post.rb41
-rw-r--r--spec/ruby/library/net/http/http/shared/request_put.rb41
-rw-r--r--spec/ruby/library/net/http/http/shared/started.rb26
-rw-r--r--spec/ruby/library/net/http/http/shared/version_1_1.rb6
-rw-r--r--spec/ruby/library/net/http/http/shared/version_1_2.rb6
-rw-r--r--spec/ruby/library/net/http/http/socket_type_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/start_spec.rb111
-rw-r--r--spec/ruby/library/net/http/http/started_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/trace_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/unlock_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/use_ssl_spec.rb9
-rw-r--r--spec/ruby/library/net/http/http/version_1_1_spec.rb7
-rw-r--r--spec/ruby/library/net/http/http/version_1_2_spec.rb20
-rw-r--r--spec/ruby/library/net/http/httpexceptions/fixtures/classes.rb5
-rw-r--r--spec/ruby/library/net/http/httpexceptions/initialize_spec.rb17
-rw-r--r--spec/ruby/library/net/http/httpexceptions/response_spec.rb10
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/body_exist_spec.rb22
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/body_spec.rb30
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/body_stream_spec.rb32
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/exec_spec.rb131
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/inspect_spec.rb25
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/method_spec.rb15
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/path_spec.rb12
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/request_body_permitted_spec.rb12
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/response_body_permitted_spec.rb12
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/set_body_internal_spec.rb21
-rw-r--r--spec/ruby/library/net/http/httpheader/add_field_spec.rb31
-rw-r--r--spec/ruby/library/net/http/httpheader/basic_auth_spec.rb14
-rw-r--r--spec/ruby/library/net/http/httpheader/canonical_each_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/chunked_spec.rb22
-rw-r--r--spec/ruby/library/net/http/httpheader/content_length_spec.rb54
-rw-r--r--spec/ruby/library/net/http/httpheader/content_range_spec.rb32
-rw-r--r--spec/ruby/library/net/http/httpheader/content_type_spec.rb26
-rw-r--r--spec/ruby/library/net/http/httpheader/delete_spec.rb30
-rw-r--r--spec/ruby/library/net/http/httpheader/each_capitalized_name_spec.rb35
-rw-r--r--spec/ruby/library/net/http/httpheader/each_capitalized_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/each_header_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/each_key_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/each_name_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/each_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/each_value_spec.rb35
-rw-r--r--spec/ruby/library/net/http/httpheader/element_reference_spec.rb39
-rw-r--r--spec/ruby/library/net/http/httpheader/element_set_spec.rb41
-rw-r--r--spec/ruby/library/net/http/httpheader/fetch_spec.rb68
-rw-r--r--spec/ruby/library/net/http/httpheader/fixtures/classes.rb11
-rw-r--r--spec/ruby/library/net/http/httpheader/form_data_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/get_fields_spec.rb39
-rw-r--r--spec/ruby/library/net/http/httpheader/initialize_http_header_spec.rb22
-rw-r--r--spec/ruby/library/net/http/httpheader/key_spec.rb21
-rw-r--r--spec/ruby/library/net/http/httpheader/length_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/main_type_spec.rb24
-rw-r--r--spec/ruby/library/net/http/httpheader/proxy_basic_auth_spec.rb14
-rw-r--r--spec/ruby/library/net/http/httpheader/range_length_spec.rb32
-rw-r--r--spec/ruby/library/net/http/httpheader/range_spec.rb48
-rw-r--r--spec/ruby/library/net/http/httpheader/set_content_type_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/set_form_data_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/set_range_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/shared/each_capitalized.rb31
-rw-r--r--spec/ruby/library/net/http/httpheader/shared/each_header.rb31
-rw-r--r--spec/ruby/library/net/http/httpheader/shared/each_name.rb31
-rw-r--r--spec/ruby/library/net/http/httpheader/shared/set_content_type.rb18
-rw-r--r--spec/ruby/library/net/http/httpheader/shared/set_form_data.rb27
-rw-r--r--spec/ruby/library/net/http/httpheader/shared/set_range.rb89
-rw-r--r--spec/ruby/library/net/http/httpheader/shared/size.rb18
-rw-r--r--spec/ruby/library/net/http/httpheader/size_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/sub_type_spec.rb32
-rw-r--r--spec/ruby/library/net/http/httpheader/to_hash_spec.rb25
-rw-r--r--spec/ruby/library/net/http/httpheader/type_params_spec.rb24
-rw-r--r--spec/ruby/library/net/http/httprequest/initialize_spec.rb45
-rw-r--r--spec/ruby/library/net/http/httpresponse/body_permitted_spec.rb13
-rw-r--r--spec/ruby/library/net/http/httpresponse/body_spec.rb7
-rw-r--r--spec/ruby/library/net/http/httpresponse/code_spec.rb24
-rw-r--r--spec/ruby/library/net/http/httpresponse/code_type_spec.rb24
-rw-r--r--spec/ruby/library/net/http/httpresponse/entity_spec.rb7
-rw-r--r--spec/ruby/library/net/http/httpresponse/error_spec.rb29
-rw-r--r--spec/ruby/library/net/http/httpresponse/error_type_spec.rb29
-rw-r--r--spec/ruby/library/net/http/httpresponse/exception_type_spec.rb18
-rw-r--r--spec/ruby/library/net/http/httpresponse/header_spec.rb9
-rw-r--r--spec/ruby/library/net/http/httpresponse/http_version_spec.rb12
-rw-r--r--spec/ruby/library/net/http/httpresponse/initialize_spec.rb11
-rw-r--r--spec/ruby/library/net/http/httpresponse/inspect_spec.rb15
-rw-r--r--spec/ruby/library/net/http/httpresponse/message_spec.rb9
-rw-r--r--spec/ruby/library/net/http/httpresponse/msg_spec.rb9
-rw-r--r--spec/ruby/library/net/http/httpresponse/read_body_spec.rb85
-rw-r--r--spec/ruby/library/net/http/httpresponse/read_header_spec.rb9
-rw-r--r--spec/ruby/library/net/http/httpresponse/read_new_spec.rb22
-rw-r--r--spec/ruby/library/net/http/httpresponse/reading_body_spec.rb58
-rw-r--r--spec/ruby/library/net/http/httpresponse/response_spec.rb9
-rw-r--r--spec/ruby/library/net/http/httpresponse/shared/body.rb18
-rw-r--r--spec/ruby/library/net/http/httpresponse/value_spec.rb29
-rw-r--r--spec/ruby/library/observer/add_observer_spec.rb23
-rw-r--r--spec/ruby/library/observer/count_observers_spec.rb23
-rw-r--r--spec/ruby/library/observer/delete_observer_spec.rb19
-rw-r--r--spec/ruby/library/observer/delete_observers_spec.rb19
-rw-r--r--spec/ruby/library/observer/fixtures/classes.rb17
-rw-r--r--spec/ruby/library/observer/notify_observers_spec.rb31
-rw-r--r--spec/ruby/library/open3/capture2_spec.rb6
-rw-r--r--spec/ruby/library/open3/capture2e_spec.rb6
-rw-r--r--spec/ruby/library/open3/capture3_spec.rb6
-rw-r--r--spec/ruby/library/open3/pipeline_r_spec.rb6
-rw-r--r--spec/ruby/library/open3/pipeline_rw_spec.rb6
-rw-r--r--spec/ruby/library/open3/pipeline_spec.rb6
-rw-r--r--spec/ruby/library/open3/pipeline_start_spec.rb6
-rw-r--r--spec/ruby/library/open3/pipeline_w_spec.rb6
-rw-r--r--spec/ruby/library/open3/popen2_spec.rb6
-rw-r--r--spec/ruby/library/open3/popen2e_spec.rb6
-rw-r--r--spec/ruby/library/open3/popen3_spec.rb43
-rw-r--r--spec/ruby/library/openssl/cipher_spec.rb9
-rw-r--r--spec/ruby/library/openssl/config/freeze_spec.rb20
-rw-r--r--spec/ruby/library/openssl/hmac/digest_spec.rb16
-rw-r--r--spec/ruby/library/openssl/hmac/hexdigest_spec.rb16
-rw-r--r--spec/ruby/library/openssl/random/pseudo_bytes_spec.rb8
-rw-r--r--spec/ruby/library/openssl/random/random_bytes_spec.rb6
-rw-r--r--spec/ruby/library/openssl/random/shared/random_bytes.rb29
-rw-r--r--spec/ruby/library/openssl/shared/constants.rb11
-rw-r--r--spec/ruby/library/openssl/x509/name/parse_spec.rb48
-rw-r--r--spec/ruby/library/openstruct/delete_field_spec.rb19
-rw-r--r--spec/ruby/library/openstruct/element_reference_spec.rb13
-rw-r--r--spec/ruby/library/openstruct/element_set_spec.rb13
-rw-r--r--spec/ruby/library/openstruct/equal_value_spec.rb28
-rw-r--r--spec/ruby/library/openstruct/fixtures/classes.rb4
-rw-r--r--spec/ruby/library/openstruct/frozen_spec.rb38
-rw-r--r--spec/ruby/library/openstruct/initialize_spec.rb8
-rw-r--r--spec/ruby/library/openstruct/inspect_spec.rb8
-rw-r--r--spec/ruby/library/openstruct/marshal_dump_spec.rb9
-rw-r--r--spec/ruby/library/openstruct/marshal_load_spec.rb12
-rw-r--r--spec/ruby/library/openstruct/method_missing_spec.rb47
-rw-r--r--spec/ruby/library/openstruct/new_spec.rb20
-rw-r--r--spec/ruby/library/openstruct/shared/inspect.rb20
-rw-r--r--spec/ruby/library/openstruct/to_h_spec.rb36
-rw-r--r--spec/ruby/library/openstruct/to_s_spec.rb8
-rw-r--r--spec/ruby/library/optionparser/order_spec.rb32
-rw-r--r--spec/ruby/library/optionparser/parse_spec.rb32
-rw-r--r--spec/ruby/library/pathname/absolute_spec.rb22
-rw-r--r--spec/ruby/library/pathname/empty_spec.rb34
-rw-r--r--spec/ruby/library/pathname/equal_value_spec.rb14
-rw-r--r--spec/ruby/library/pathname/hash_spec.rb14
-rw-r--r--spec/ruby/library/pathname/join_spec.rb40
-rw-r--r--spec/ruby/library/pathname/new_spec.rb28
-rw-r--r--spec/ruby/library/pathname/parent_spec.rb18
-rw-r--r--spec/ruby/library/pathname/realdirpath_spec.rb10
-rw-r--r--spec/ruby/library/pathname/realpath_spec.rb10
-rw-r--r--spec/ruby/library/pathname/relative_path_from_spec.rb51
-rw-r--r--spec/ruby/library/pathname/relative_spec.rb22
-rw-r--r--spec/ruby/library/pathname/root_spec.rb26
-rw-r--r--spec/ruby/library/pathname/sub_spec.rb15
-rw-r--r--spec/ruby/library/pp/pp_spec.rb25
-rw-r--r--spec/ruby/library/prime/each_spec.rb167
-rw-r--r--spec/ruby/library/prime/instance_spec.rb21
-rw-r--r--spec/ruby/library/prime/int_from_prime_division_spec.rb13
-rw-r--r--spec/ruby/library/prime/integer/each_prime_spec.rb13
-rw-r--r--spec/ruby/library/prime/integer/from_prime_division_spec.rb13
-rw-r--r--spec/ruby/library/prime/integer/prime_division_spec.rb19
-rw-r--r--spec/ruby/library/prime/integer/prime_spec.rb17
-rw-r--r--spec/ruby/library/prime/next_spec.rb7
-rw-r--r--spec/ruby/library/prime/prime_division_spec.rb25
-rw-r--r--spec/ruby/library/prime/prime_spec.rb17
-rw-r--r--spec/ruby/library/prime/shared/next.rb8
-rw-r--r--spec/ruby/library/prime/succ_spec.rb7
-rw-r--r--spec/ruby/library/rbconfig/rbconfig_spec.rb11
-rw-r--r--spec/ruby/library/rbconfig/sizeof/sizeof_spec.rb30
-rw-r--r--spec/ruby/library/readline/basic_quote_characters_spec.rb18
-rw-r--r--spec/ruby/library/readline/basic_word_break_characters_spec.rb16
-rw-r--r--spec/ruby/library/readline/completer_quote_characters_spec.rb16
-rw-r--r--spec/ruby/library/readline/completer_word_break_characters_spec.rb16
-rw-r--r--spec/ruby/library/readline/completion_append_character_spec.rb16
-rw-r--r--spec/ruby/library/readline/completion_case_fold_spec.rb18
-rw-r--r--spec/ruby/library/readline/completion_proc_spec.rb22
-rw-r--r--spec/ruby/library/readline/constants_spec.rb18
-rw-r--r--spec/ruby/library/readline/emacs_editing_mode_spec.rb11
-rw-r--r--spec/ruby/library/readline/filename_quote_characters_spec.rb18
-rw-r--r--spec/ruby/library/readline/history/append_spec.rb28
-rw-r--r--spec/ruby/library/readline/history/delete_at_spec.rb45
-rw-r--r--spec/ruby/library/readline/history/each_spec.rb29
-rw-r--r--spec/ruby/library/readline/history/element_reference_spec.rb40
-rw-r--r--spec/ruby/library/readline/history/element_set_spec.rb35
-rw-r--r--spec/ruby/library/readline/history/empty_spec.rb13
-rw-r--r--spec/ruby/library/readline/history/history_spec.rb9
-rw-r--r--spec/ruby/library/readline/history/length_spec.rb9
-rw-r--r--spec/ruby/library/readline/history/pop_spec.rb30
-rw-r--r--spec/ruby/library/readline/history/push_spec.rb26
-rw-r--r--spec/ruby/library/readline/history/shared/size.rb14
-rw-r--r--spec/ruby/library/readline/history/shift_spec.rb30
-rw-r--r--spec/ruby/library/readline/history/size_spec.rb9
-rw-r--r--spec/ruby/library/readline/history/to_s_spec.rb9
-rw-r--r--spec/ruby/library/readline/readline_spec.rb31
-rw-r--r--spec/ruby/library/readline/spec_helper.rb11
-rw-r--r--spec/ruby/library/readline/vi_editing_mode_spec.rb11
-rw-r--r--spec/ruby/library/resolv/get_address_spec.rb21
-rw-r--r--spec/ruby/library/resolv/get_addresses_spec.rb14
-rw-r--r--spec/ruby/library/resolv/get_name_spec.rb19
-rw-r--r--spec/ruby/library/resolv/get_names_spec.rb14
-rw-r--r--spec/ruby/library/rexml/attribute/clone_spec.rb11
-rw-r--r--spec/ruby/library/rexml/attribute/element_spec.rb23
-rw-r--r--spec/ruby/library/rexml/attribute/equal_value_spec.rb18
-rw-r--r--spec/ruby/library/rexml/attribute/hash_spec.rb13
-rw-r--r--spec/ruby/library/rexml/attribute/initialize_spec.rb29
-rw-r--r--spec/ruby/library/rexml/attribute/inspect_spec.rb19
-rw-r--r--spec/ruby/library/rexml/attribute/namespace_spec.rb24
-rw-r--r--spec/ruby/library/rexml/attribute/node_type_spec.rb10
-rw-r--r--spec/ruby/library/rexml/attribute/prefix_spec.rb18
-rw-r--r--spec/ruby/library/rexml/attribute/remove_spec.rb20
-rw-r--r--spec/ruby/library/rexml/attribute/to_s_spec.rb14
-rw-r--r--spec/ruby/library/rexml/attribute/to_string_spec.rb14
-rw-r--r--spec/ruby/library/rexml/attribute/value_spec.rb14
-rw-r--r--spec/ruby/library/rexml/attribute/write_spec.rb23
-rw-r--r--spec/ruby/library/rexml/attribute/xpath_spec.rb19
-rw-r--r--spec/ruby/library/rexml/attributes/add_spec.rb7
-rw-r--r--spec/ruby/library/rexml/attributes/append_spec.rb7
-rw-r--r--spec/ruby/library/rexml/attributes/delete_all_spec.rb31
-rw-r--r--spec/ruby/library/rexml/attributes/delete_spec.rb27
-rw-r--r--spec/ruby/library/rexml/attributes/each_attribute_spec.rb22
-rw-r--r--spec/ruby/library/rexml/attributes/each_spec.rb23
-rw-r--r--spec/ruby/library/rexml/attributes/element_reference_spec.rb18
-rw-r--r--spec/ruby/library/rexml/attributes/element_set_spec.rb25
-rw-r--r--spec/ruby/library/rexml/attributes/get_attribute_ns_spec.rb14
-rw-r--r--spec/ruby/library/rexml/attributes/get_attribute_spec.rb29
-rw-r--r--spec/ruby/library/rexml/attributes/initialize_spec.rb18
-rw-r--r--spec/ruby/library/rexml/attributes/length_spec.rb7
-rw-r--r--spec/ruby/library/rexml/attributes/namespaces_spec.rb6
-rw-r--r--spec/ruby/library/rexml/attributes/prefixes_spec.rb24
-rw-r--r--spec/ruby/library/rexml/attributes/shared/add.rb17
-rw-r--r--spec/ruby/library/rexml/attributes/shared/length.rb13
-rw-r--r--spec/ruby/library/rexml/attributes/size_spec.rb7
-rw-r--r--spec/ruby/library/rexml/attributes/to_a_spec.rb19
-rw-r--r--spec/ruby/library/rexml/cdata/clone_spec.rb10
-rw-r--r--spec/ruby/library/rexml/cdata/initialize_spec.rb24
-rw-r--r--spec/ruby/library/rexml/cdata/shared/to_s.rb11
-rw-r--r--spec/ruby/library/rexml/cdata/to_s_spec.rb7
-rw-r--r--spec/ruby/library/rexml/cdata/value_spec.rb7
-rw-r--r--spec/ruby/library/rexml/document/add_element_spec.rb31
-rw-r--r--spec/ruby/library/rexml/document/add_spec.rb57
-rw-r--r--spec/ruby/library/rexml/document/clone_spec.rb20
-rw-r--r--spec/ruby/library/rexml/document/doctype_spec.rb15
-rw-r--r--spec/ruby/library/rexml/document/encoding_spec.rb22
-rw-r--r--spec/ruby/library/rexml/document/expanded_name_spec.rb16
-rw-r--r--spec/ruby/library/rexml/document/new_spec.rb36
-rw-r--r--spec/ruby/library/rexml/document/node_type_spec.rb8
-rw-r--r--spec/ruby/library/rexml/document/root_spec.rb12
-rw-r--r--spec/ruby/library/rexml/document/stand_alone_spec.rb19
-rw-r--r--spec/ruby/library/rexml/document/version_spec.rb14
-rw-r--r--spec/ruby/library/rexml/document/write_spec.rb35
-rw-r--r--spec/ruby/library/rexml/document/xml_decl_spec.rb15
-rw-r--r--spec/ruby/library/rexml/element/add_attribute_spec.rb41
-rw-r--r--spec/ruby/library/rexml/element/add_attributes_spec.rb22
-rw-r--r--spec/ruby/library/rexml/element/add_element_spec.rb39
-rw-r--r--spec/ruby/library/rexml/element/add_namespace_spec.rb23
-rw-r--r--spec/ruby/library/rexml/element/add_text_spec.rb24
-rw-r--r--spec/ruby/library/rexml/element/attribute_spec.rb17
-rw-r--r--spec/ruby/library/rexml/element/attributes_spec.rb19
-rw-r--r--spec/ruby/library/rexml/element/cdatas_spec.rb24
-rw-r--r--spec/ruby/library/rexml/element/clone_spec.rb29
-rw-r--r--spec/ruby/library/rexml/element/comments_spec.rb20
-rw-r--r--spec/ruby/library/rexml/element/delete_attribute_spec.rb39
-rw-r--r--spec/ruby/library/rexml/element/delete_element_spec.rb49
-rw-r--r--spec/ruby/library/rexml/element/delete_namespace_spec.rb25
-rw-r--r--spec/ruby/library/rexml/element/document_spec.rb16
-rw-r--r--spec/ruby/library/rexml/element/each_element_with_attribute_spec.rb35
-rw-r--r--spec/ruby/library/rexml/element/each_element_with_text_spec.rb31
-rw-r--r--spec/ruby/library/rexml/element/element_reference_spec.rb22
-rw-r--r--spec/ruby/library/rexml/element/get_text_spec.rb18
-rw-r--r--spec/ruby/library/rexml/element/has_attributes_spec.rb17
-rw-r--r--spec/ruby/library/rexml/element/has_elements_spec.rb18
-rw-r--r--spec/ruby/library/rexml/element/has_text_spec.rb16
-rw-r--r--spec/ruby/library/rexml/element/inspect_spec.rb27
-rw-r--r--spec/ruby/library/rexml/element/instructions_spec.rb21
-rw-r--r--spec/ruby/library/rexml/element/namespace_spec.rb27
-rw-r--r--spec/ruby/library/rexml/element/namespaces_spec.rb32
-rw-r--r--spec/ruby/library/rexml/element/new_spec.rb35
-rw-r--r--spec/ruby/library/rexml/element/next_element_spec.rb19
-rw-r--r--spec/ruby/library/rexml/element/node_type_spec.rb8
-rw-r--r--spec/ruby/library/rexml/element/prefixes_spec.rb23
-rw-r--r--spec/ruby/library/rexml/element/previous_element_spec.rb20
-rw-r--r--spec/ruby/library/rexml/element/raw_spec.rb24
-rw-r--r--spec/ruby/library/rexml/element/root_spec.rb28
-rw-r--r--spec/ruby/library/rexml/element/text_spec.rb46
-rw-r--r--spec/ruby/library/rexml/element/texts_spec.rb16
-rw-r--r--spec/ruby/library/rexml/element/whitespace_spec.rb23
-rw-r--r--spec/ruby/library/rexml/node/each_recursive_spec.rb21
-rw-r--r--spec/ruby/library/rexml/node/find_first_recursive_spec.rb25
-rw-r--r--spec/ruby/library/rexml/node/index_in_parent_spec.rb15
-rw-r--r--spec/ruby/library/rexml/node/next_sibling_node_spec.rb21
-rw-r--r--spec/ruby/library/rexml/node/parent_spec.rb20
-rw-r--r--spec/ruby/library/rexml/node/previous_sibling_node_spec.rb21
-rw-r--r--spec/ruby/library/rexml/shared/each_element.rb36
-rw-r--r--spec/ruby/library/rexml/shared/elements_to_a.rb34
-rw-r--r--spec/ruby/library/rexml/text/append_spec.rb10
-rw-r--r--spec/ruby/library/rexml/text/clone_spec.rb10
-rw-r--r--spec/ruby/library/rexml/text/comparison_spec.rb25
-rw-r--r--spec/ruby/library/rexml/text/empty_spec.rb12
-rw-r--r--spec/ruby/library/rexml/text/indent_text_spec.rb23
-rw-r--r--spec/ruby/library/rexml/text/inspect_spec.rb8
-rw-r--r--spec/ruby/library/rexml/text/new_spec.rb48
-rw-r--r--spec/ruby/library/rexml/text/node_type_spec.rb8
-rw-r--r--spec/ruby/library/rexml/text/normalize_spec.rb8
-rw-r--r--spec/ruby/library/rexml/text/read_with_substitution_spec.rb12
-rw-r--r--spec/ruby/library/rexml/text/to_s_spec.rb17
-rw-r--r--spec/ruby/library/rexml/text/unnormalize_spec.rb8
-rw-r--r--spec/ruby/library/rexml/text/value_spec.rb37
-rw-r--r--spec/ruby/library/rexml/text/wrap_spec.rb20
-rw-r--r--spec/ruby/library/rexml/text/write_with_substitution_spec.rb33
-rw-r--r--spec/ruby/library/scanf/io/block_scanf_spec.rb7
-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.rb35
-rw-r--r--spec/ruby/library/scanf/io/shared/block_scanf.rb28
-rw-r--r--spec/ruby/library/scanf/string/block_scanf_spec.rb7
-rw-r--r--spec/ruby/library/scanf/string/scanf_spec.rb53
-rw-r--r--spec/ruby/library/scanf/string/shared/block_scanf.rb25
-rw-r--r--spec/ruby/library/securerandom/base64_spec.rb55
-rw-r--r--spec/ruby/library/securerandom/hex_spec.rb54
-rw-r--r--spec/ruby/library/securerandom/random_bytes_spec.rb50
-rw-r--r--spec/ruby/library/securerandom/random_number_spec.rb93
-rw-r--r--spec/ruby/library/set/add_spec.rb27
-rw-r--r--spec/ruby/library/set/append_spec.rb7
-rw-r--r--spec/ruby/library/set/case_compare_spec.rb14
-rw-r--r--spec/ruby/library/set/case_equality_spec.rb9
-rw-r--r--spec/ruby/library/set/classify_spec.rb27
-rw-r--r--spec/ruby/library/set/clear_spec.rb17
-rw-r--r--spec/ruby/library/set/collect_spec.rb7
-rw-r--r--spec/ruby/library/set/compare_by_identity_spec.rb147
-rw-r--r--spec/ruby/library/set/constructor_spec.rb15
-rw-r--r--spec/ruby/library/set/delete_if_spec.rb38
-rw-r--r--spec/ruby/library/set/delete_spec.rb37
-rw-r--r--spec/ruby/library/set/difference_spec.rb7
-rw-r--r--spec/ruby/library/set/divide_spec.rb34
-rw-r--r--spec/ruby/library/set/each_spec.rb26
-rw-r--r--spec/ruby/library/set/empty_spec.rb10
-rw-r--r--spec/ruby/library/set/enumerable/to_set_spec.rb19
-rw-r--r--spec/ruby/library/set/eql_spec.rb15
-rw-r--r--spec/ruby/library/set/equal_value_spec.rb26
-rw-r--r--spec/ruby/library/set/exclusion_spec.rb18
-rw-r--r--spec/ruby/library/set/filter_spec.rb8
-rw-r--r--spec/ruby/library/set/flatten_merge_spec.rb23
-rw-r--r--spec/ruby/library/set/flatten_spec.rb40
-rw-r--r--spec/ruby/library/set/hash_spec.rb13
-rw-r--r--spec/ruby/library/set/include_spec.rb7
-rw-r--r--spec/ruby/library/set/initialize_spec.rb48
-rw-r--r--spec/ruby/library/set/inspect_spec.rb7
-rw-r--r--spec/ruby/library/set/intersection_spec.rb11
-rw-r--r--spec/ruby/library/set/keep_if_spec.rb38
-rw-r--r--spec/ruby/library/set/length_spec.rb7
-rw-r--r--spec/ruby/library/set/map_spec.rb7
-rw-r--r--spec/ruby/library/set/member_spec.rb7
-rw-r--r--spec/ruby/library/set/merge_spec.rb19
-rw-r--r--spec/ruby/library/set/minus_spec.rb7
-rw-r--r--spec/ruby/library/set/plus_spec.rb7
-rw-r--r--spec/ruby/library/set/pretty_print_cycle_spec.rb10
-rw-r--r--spec/ruby/library/set/pretty_print_spec.rb17
-rw-r--r--spec/ruby/library/set/proper_subset_spec.rb34
-rw-r--r--spec/ruby/library/set/proper_superset_spec.rb34
-rw-r--r--spec/ruby/library/set/reject_spec.rb42
-rw-r--r--spec/ruby/library/set/replace_spec.rb17
-rw-r--r--spec/ruby/library/set/select_spec.rb6
-rw-r--r--spec/ruby/library/set/shared/add.rb14
-rw-r--r--spec/ruby/library/set/shared/collect.rb20
-rw-r--r--spec/ruby/library/set/shared/difference.rb15
-rw-r--r--spec/ruby/library/set/shared/include.rb29
-rw-r--r--spec/ruby/library/set/shared/inspect.rb15
-rw-r--r--spec/ruby/library/set/shared/intersection.rb15
-rw-r--r--spec/ruby/library/set/shared/length.rb6
-rw-r--r--spec/ruby/library/set/shared/select.rb42
-rw-r--r--spec/ruby/library/set/shared/union.rb15
-rw-r--r--spec/ruby/library/set/size_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/add_spec.rb39
-rw-r--r--spec/ruby/library/set/sortedset/append_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/case_equality_spec.rb9
-rw-r--r--spec/ruby/library/set/sortedset/classify_spec.rb27
-rw-r--r--spec/ruby/library/set/sortedset/clear_spec.rb17
-rw-r--r--spec/ruby/library/set/sortedset/collect_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/constructor_spec.rb15
-rw-r--r--spec/ruby/library/set/sortedset/delete_if_spec.rb38
-rw-r--r--spec/ruby/library/set/sortedset/delete_spec.rb37
-rw-r--r--spec/ruby/library/set/sortedset/difference_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/divide_spec.rb34
-rw-r--r--spec/ruby/library/set/sortedset/each_spec.rb26
-rw-r--r--spec/ruby/library/set/sortedset/empty_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/eql_spec.rb16
-rw-r--r--spec/ruby/library/set/sortedset/equal_value_spec.rb13
-rw-r--r--spec/ruby/library/set/sortedset/exclusion_spec.rb18
-rw-r--r--spec/ruby/library/set/sortedset/filter_spec.rb9
-rw-r--r--spec/ruby/library/set/sortedset/flatten_merge_spec.rb8
-rw-r--r--spec/ruby/library/set/sortedset/flatten_spec.rb44
-rw-r--r--spec/ruby/library/set/sortedset/hash_spec.rb13
-rw-r--r--spec/ruby/library/set/sortedset/include_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/initialize_spec.rb30
-rw-r--r--spec/ruby/library/set/sortedset/inspect_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/intersection_spec.rb11
-rw-r--r--spec/ruby/library/set/sortedset/keep_if_spec.rb31
-rw-r--r--spec/ruby/library/set/sortedset/length_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/map_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/member_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/merge_spec.rb19
-rw-r--r--spec/ruby/library/set/sortedset/minus_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/plus_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/pretty_print_cycle_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/pretty_print_spec.rb17
-rw-r--r--spec/ruby/library/set/sortedset/proper_subset_spec.rb33
-rw-r--r--spec/ruby/library/set/sortedset/proper_superset_spec.rb33
-rw-r--r--spec/ruby/library/set/sortedset/reject_spec.rb42
-rw-r--r--spec/ruby/library/set/sortedset/replace_spec.rb17
-rw-r--r--spec/ruby/library/set/sortedset/select_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/shared/add.rb14
-rw-r--r--spec/ruby/library/set/sortedset/shared/collect.rb20
-rw-r--r--spec/ruby/library/set/sortedset/shared/difference.rb15
-rw-r--r--spec/ruby/library/set/sortedset/shared/include.rb7
-rw-r--r--spec/ruby/library/set/sortedset/shared/intersection.rb15
-rw-r--r--spec/ruby/library/set/sortedset/shared/length.rb6
-rw-r--r--spec/ruby/library/set/sortedset/shared/select.rb35
-rw-r--r--spec/ruby/library/set/sortedset/shared/union.rb15
-rw-r--r--spec/ruby/library/set/sortedset/size_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/subset_spec.rb33
-rw-r--r--spec/ruby/library/set/sortedset/subtract_spec.rb17
-rw-r--r--spec/ruby/library/set/sortedset/superset_spec.rb33
-rw-r--r--spec/ruby/library/set/sortedset/to_a_spec.rb17
-rw-r--r--spec/ruby/library/set/sortedset/union_spec.rb11
-rw-r--r--spec/ruby/library/set/subset_spec.rb34
-rw-r--r--spec/ruby/library/set/subtract_spec.rb17
-rw-r--r--spec/ruby/library/set/superset_spec.rb34
-rw-r--r--spec/ruby/library/set/to_a_spec.rb8
-rw-r--r--spec/ruby/library/set/to_s_spec.rb13
-rw-r--r--spec/ruby/library/set/union_spec.rb11
-rw-r--r--spec/ruby/library/shellwords/shellwords_spec.rb36
-rw-r--r--spec/ruby/library/singleton/allocate_spec.rb8
-rw-r--r--spec/ruby/library/singleton/clone_spec.rb8
-rw-r--r--spec/ruby/library/singleton/dump_spec.rb14
-rw-r--r--spec/ruby/library/singleton/dup_spec.rb8
-rw-r--r--spec/ruby/library/singleton/fixtures/classes.rb18
-rw-r--r--spec/ruby/library/singleton/instance_spec.rb30
-rw-r--r--spec/ruby/library/singleton/load_spec.rb21
-rw-r--r--spec/ruby/library/singleton/new_spec.rb8
-rw-r--r--spec/ruby/library/socket/addrinfo/afamily_spec.rb37
-rw-r--r--spec/ruby/library/socket/addrinfo/bind_spec.rb28
-rw-r--r--spec/ruby/library/socket/addrinfo/canonname_spec.rb27
-rw-r--r--spec/ruby/library/socket/addrinfo/connect_from_spec.rb75
-rw-r--r--spec/ruby/library/socket/addrinfo/connect_spec.rb35
-rw-r--r--spec/ruby/library/socket/addrinfo/connect_to_spec.rb75
-rw-r--r--spec/ruby/library/socket/addrinfo/family_addrinfo_spec.rb115
-rw-r--r--spec/ruby/library/socket/addrinfo/foreach_spec.rb9
-rw-r--r--spec/ruby/library/socket/addrinfo/getaddrinfo_spec.rb91
-rw-r--r--spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb40
-rw-r--r--spec/ruby/library/socket/addrinfo/initialize_spec.rb587
-rw-r--r--spec/ruby/library/socket/addrinfo/inspect_sockaddr_spec.rb50
-rw-r--r--spec/ruby/library/socket/addrinfo/inspect_spec.rb65
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_address_spec.rb85
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_port_spec.rb35
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_spec.rb64
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_unpack_spec.rb35
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_loopback_spec.rb43
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_multicast_spec.rb29
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_private_spec.rb47
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_spec.rb35
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_linklocal_spec.rb23
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_loopback_spec.rb45
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_mc_global_spec.rb20
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_mc_linklocal_spec.rb19
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_mc_nodelocal_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_mc_orglocal_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_mc_sitelocal_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_multicast_spec.rb48
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_sitelocal_spec.rb23
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_spec.rb35
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_to_ipv4_spec.rb71
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_unique_local_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_unspecified_spec.rb15
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_v4compat_spec.rb20
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_v4mapped_spec.rb20
-rw-r--r--spec/ruby/library/socket/addrinfo/listen_spec.rb34
-rw-r--r--spec/ruby/library/socket/addrinfo/marshal_dump_spec.rb84
-rw-r--r--spec/ruby/library/socket/addrinfo/marshal_load_spec.rb35
-rw-r--r--spec/ruby/library/socket/addrinfo/pfamily_spec.rb43
-rw-r--r--spec/ruby/library/socket/addrinfo/protocol_spec.rb24
-rw-r--r--spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb51
-rw-r--r--spec/ruby/library/socket/addrinfo/socktype_spec.rb23
-rw-r--r--spec/ruby/library/socket/addrinfo/tcp_spec.rb34
-rw-r--r--spec/ruby/library/socket/addrinfo/to_s_spec.rb6
-rw-r--r--spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb6
-rw-r--r--spec/ruby/library/socket/addrinfo/udp_spec.rb36
-rw-r--r--spec/ruby/library/socket/addrinfo/unix_path_spec.rb37
-rw-r--r--spec/ruby/library/socket/addrinfo/unix_spec.rb71
-rw-r--r--spec/ruby/library/socket/ancillarydata/cmsg_is_spec.rb33
-rw-r--r--spec/ruby/library/socket/ancillarydata/data_spec.rb9
-rw-r--r--spec/ruby/library/socket/ancillarydata/family_spec.rb9
-rw-r--r--spec/ruby/library/socket/ancillarydata/initialize_spec.rb284
-rw-r--r--spec/ruby/library/socket/ancillarydata/int_spec.rb43
-rw-r--r--spec/ruby/library/socket/ancillarydata/ip_pktinfo_spec.rb145
-rw-r--r--spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_addr_spec.rb11
-rw-r--r--spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_ifindex_spec.rb11
-rw-r--r--spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_spec.rb89
-rw-r--r--spec/ruby/library/socket/ancillarydata/level_spec.rb9
-rw-r--r--spec/ruby/library/socket/ancillarydata/type_spec.rb9
-rw-r--r--spec/ruby/library/socket/ancillarydata/unix_rights_spec.rb61
-rw-r--r--spec/ruby/library/socket/basicsocket/close_read_spec.rb43
-rw-r--r--spec/ruby/library/socket/basicsocket/close_write_spec.rb48
-rw-r--r--spec/ruby/library/socket/basicsocket/connect_address_spec.rb154
-rw-r--r--spec/ruby/library/socket/basicsocket/do_not_reverse_lookup_spec.rb39
-rw-r--r--spec/ruby/library/socket/basicsocket/for_fd_spec.rb38
-rw-r--r--spec/ruby/library/socket/basicsocket/getpeereid_spec.rb36
-rw-r--r--spec/ruby/library/socket/basicsocket/getpeername_spec.rb25
-rw-r--r--spec/ruby/library/socket/basicsocket/getsockname_spec.rb28
-rw-r--r--spec/ruby/library/socket/basicsocket/getsockopt_spec.rb188
-rw-r--r--spec/ruby/library/socket/basicsocket/ioctl_spec.rb42
-rw-r--r--spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb65
-rw-r--r--spec/ruby/library/socket/basicsocket/recv_spec.rb159
-rw-r--r--spec/ruby/library/socket/basicsocket/recvmsg_nonblock_spec.rb205
-rw-r--r--spec/ruby/library/socket/basicsocket/recvmsg_spec.rb197
-rw-r--r--spec/ruby/library/socket/basicsocket/send_spec.rb212
-rw-r--r--spec/ruby/library/socket/basicsocket/sendmsg_nonblock_spec.rb104
-rw-r--r--spec/ruby/library/socket/basicsocket/sendmsg_spec.rb111
-rw-r--r--spec/ruby/library/socket/basicsocket/setsockopt_spec.rb336
-rw-r--r--spec/ruby/library/socket/basicsocket/shutdown_spec.rb155
-rw-r--r--spec/ruby/library/socket/constants/constants_spec.rb108
-rw-r--r--spec/ruby/library/socket/fixtures/classes.rb148
-rw-r--r--spec/ruby/library/socket/fixtures/send_io.txt1
-rw-r--r--spec/ruby/library/socket/ipsocket/addr_spec.rb105
-rw-r--r--spec/ruby/library/socket/ipsocket/getaddress_spec.rb27
-rw-r--r--spec/ruby/library/socket/ipsocket/peeraddr_spec.rb117
-rw-r--r--spec/ruby/library/socket/ipsocket/recvfrom_spec.rb123
-rw-r--r--spec/ruby/library/socket/option/bool_spec.rb27
-rw-r--r--spec/ruby/library/socket/option/initialize_spec.rb83
-rw-r--r--spec/ruby/library/socket/option/inspect_spec.rb19
-rw-r--r--spec/ruby/library/socket/option/int_spec.rb43
-rw-r--r--spec/ruby/library/socket/option/linger_spec.rb76
-rw-r--r--spec/ruby/library/socket/option/new_spec.rb35
-rw-r--r--spec/ruby/library/socket/shared/pack_sockaddr.rb99
-rw-r--r--spec/ruby/library/socket/shared/partially_closable_sockets.rb13
-rw-r--r--spec/ruby/library/socket/shared/socketpair.rb138
-rw-r--r--spec/ruby/library/socket/socket/accept_loop_spec.rb84
-rw-r--r--spec/ruby/library/socket/socket/accept_nonblock_spec.rb138
-rw-r--r--spec/ruby/library/socket/socket/accept_spec.rb122
-rw-r--r--spec/ruby/library/socket/socket/bind_spec.rb144
-rw-r--r--spec/ruby/library/socket/socket/connect_nonblock_spec.rb149
-rw-r--r--spec/ruby/library/socket/socket/connect_spec.rb56
-rw-r--r--spec/ruby/library/socket/socket/for_fd_spec.rb30
-rw-r--r--spec/ruby/library/socket/socket/getaddrinfo_spec.rb373
-rw-r--r--spec/ruby/library/socket/socket/gethostbyaddr_spec.rb124
-rw-r--r--spec/ruby/library/socket/socket/gethostbyname_spec.rb135
-rw-r--r--spec/ruby/library/socket/socket/gethostname_spec.rb8
-rw-r--r--spec/ruby/library/socket/socket/getifaddrs_spec.rb117
-rw-r--r--spec/ruby/library/socket/socket/getnameinfo_spec.rb147
-rw-r--r--spec/ruby/library/socket/socket/getservbyname_spec.rb32
-rw-r--r--spec/ruby/library/socket/socket/getservbyport_spec.rb23
-rw-r--r--spec/ruby/library/socket/socket/initialize_spec.rb87
-rw-r--r--spec/ruby/library/socket/socket/ip_address_list_spec.rb50
-rw-r--r--spec/ruby/library/socket/socket/ipv6only_bang_spec.rb20
-rw-r--r--spec/ruby/library/socket/socket/listen_spec.rb64
-rw-r--r--spec/ruby/library/socket/socket/local_address_spec.rb43
-rw-r--r--spec/ruby/library/socket/socket/new_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb7
-rw-r--r--spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb7
-rw-r--r--spec/ruby/library/socket/socket/pair_spec.rb7
-rw-r--r--spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb114
-rw-r--r--spec/ruby/library/socket/socket/recvfrom_spec.rb92
-rw-r--r--spec/ruby/library/socket/socket/remote_address_spec.rb54
-rw-r--r--spec/ruby/library/socket/socket/sockaddr_in_spec.rb7
-rw-r--r--spec/ruby/library/socket/socket/sockaddr_un_spec.rb7
-rw-r--r--spec/ruby/library/socket/socket/socket_spec.rb38
-rw-r--r--spec/ruby/library/socket/socket/socketpair_spec.rb7
-rw-r--r--spec/ruby/library/socket/socket/sysaccept_spec.rb93
-rw-r--r--spec/ruby/library/socket/socket/tcp_server_loop_spec.rb47
-rw-r--r--spec/ruby/library/socket/socket/tcp_server_sockets_spec.rb39
-rw-r--r--spec/ruby/library/socket/socket/tcp_spec.rb70
-rw-r--r--spec/ruby/library/socket/socket/udp_server_loop_on_spec.rb47
-rw-r--r--spec/ruby/library/socket/socket/udp_server_loop_spec.rb47
-rw-r--r--spec/ruby/library/socket/socket/udp_server_recv_spec.rb38
-rw-r--r--spec/ruby/library/socket/socket/udp_server_sockets_spec.rb39
-rw-r--r--spec/ruby/library/socket/socket/unix_server_loop_spec.rb51
-rw-r--r--spec/ruby/library/socket/socket/unix_server_socket_spec.rb48
-rw-r--r--spec/ruby/library/socket/socket/unix_spec.rb45
-rw-r--r--spec/ruby/library/socket/socket/unpack_sockaddr_in_spec.rb46
-rw-r--r--spec/ruby/library/socket/socket/unpack_sockaddr_un_spec.rb26
-rw-r--r--spec/ruby/library/socket/spec_helper.rb17
-rw-r--r--spec/ruby/library/socket/tcpserver/accept_nonblock_spec.rb84
-rw-r--r--spec/ruby/library/socket/tcpserver/accept_spec.rb99
-rw-r--r--spec/ruby/library/socket/tcpserver/gets_spec.rb16
-rw-r--r--spec/ruby/library/socket/tcpserver/initialize_spec.rb101
-rw-r--r--spec/ruby/library/socket/tcpserver/listen_spec.rb22
-rw-r--r--spec/ruby/library/socket/tcpserver/new_spec.rb96
-rw-r--r--spec/ruby/library/socket/tcpserver/sysaccept_spec.rb66
-rw-r--r--spec/ruby/library/socket/tcpsocket/gethostbyname_spec.rb111
-rw-r--r--spec/ruby/library/socket/tcpsocket/initialize_spec.rb61
-rw-r--r--spec/ruby/library/socket/tcpsocket/local_address_spec.rb73
-rw-r--r--spec/ruby/library/socket/tcpsocket/new_spec.rb5
-rw-r--r--spec/ruby/library/socket/tcpsocket/open_spec.rb5
-rw-r--r--spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb21
-rw-r--r--spec/ruby/library/socket/tcpsocket/recv_nonblock_spec.rb34
-rw-r--r--spec/ruby/library/socket/tcpsocket/recv_spec.rb28
-rw-r--r--spec/ruby/library/socket/tcpsocket/remote_address_spec.rb72
-rw-r--r--spec/ruby/library/socket/tcpsocket/setsockopt_spec.rb45
-rw-r--r--spec/ruby/library/socket/tcpsocket/shared/new.rb79
-rw-r--r--spec/ruby/library/socket/udpsocket/bind_spec.rb83
-rw-r--r--spec/ruby/library/socket/udpsocket/connect_spec.rb35
-rw-r--r--spec/ruby/library/socket/udpsocket/initialize_spec.rb36
-rw-r--r--spec/ruby/library/socket/udpsocket/inspect_spec.rb25
-rw-r--r--spec/ruby/library/socket/udpsocket/local_address_spec.rb80
-rw-r--r--spec/ruby/library/socket/udpsocket/new_spec.rb34
-rw-r--r--spec/ruby/library/socket/udpsocket/open_spec.rb13
-rw-r--r--spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb90
-rw-r--r--spec/ruby/library/socket/udpsocket/remote_address_spec.rb79
-rw-r--r--spec/ruby/library/socket/udpsocket/send_spec.rb154
-rw-r--r--spec/ruby/library/socket/udpsocket/write_spec.rb21
-rw-r--r--spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb92
-rw-r--r--spec/ruby/library/socket/unixserver/accept_spec.rb117
-rw-r--r--spec/ruby/library/socket/unixserver/for_fd_spec.rb23
-rw-r--r--spec/ruby/library/socket/unixserver/initialize_spec.rb28
-rw-r--r--spec/ruby/library/socket/unixserver/listen_spec.rb21
-rw-r--r--spec/ruby/library/socket/unixserver/new_spec.rb6
-rw-r--r--spec/ruby/library/socket/unixserver/open_spec.rb26
-rw-r--r--spec/ruby/library/socket/unixserver/shared/new.rb22
-rw-r--r--spec/ruby/library/socket/unixserver/sysaccept_spec.rb52
-rw-r--r--spec/ruby/library/socket/unixsocket/addr_spec.rb36
-rw-r--r--spec/ruby/library/socket/unixsocket/initialize_spec.rb38
-rw-r--r--spec/ruby/library/socket/unixsocket/inspect_spec.rb17
-rw-r--r--spec/ruby/library/socket/unixsocket/local_address_spec.rb49
-rw-r--r--spec/ruby/library/socket/unixsocket/new_spec.rb6
-rw-r--r--spec/ruby/library/socket/unixsocket/open_spec.rb28
-rw-r--r--spec/ruby/library/socket/unixsocket/pair_spec.rb39
-rw-r--r--spec/ruby/library/socket/unixsocket/partially_closable_spec.rb25
-rw-r--r--spec/ruby/library/socket/unixsocket/path_spec.rb28
-rw-r--r--spec/ruby/library/socket/unixsocket/peeraddr_spec.rb30
-rw-r--r--spec/ruby/library/socket/unixsocket/recv_io_spec.rb87
-rw-r--r--spec/ruby/library/socket/unixsocket/recvfrom_spec.rb98
-rw-r--r--spec/ruby/library/socket/unixsocket/remote_address_spec.rb45
-rw-r--r--spec/ruby/library/socket/unixsocket/send_io_spec.rb58
-rw-r--r--spec/ruby/library/socket/unixsocket/shared/new.rb24
-rw-r--r--spec/ruby/library/socket/unixsocket/socketpair_spec.rb40
-rw-r--r--spec/ruby/library/stringio/append_spec.rb84
-rw-r--r--spec/ruby/library/stringio/binmode_spec.rb9
-rw-r--r--spec/ruby/library/stringio/bytes_spec.rb11
-rw-r--r--spec/ruby/library/stringio/chars_spec.rb11
-rw-r--r--spec/ruby/library/stringio/close_read_spec.rb31
-rw-r--r--spec/ruby/library/stringio/close_spec.rb23
-rw-r--r--spec/ruby/library/stringio/close_write_spec.rb31
-rw-r--r--spec/ruby/library/stringio/closed_read_spec.rb12
-rw-r--r--spec/ruby/library/stringio/closed_spec.rb16
-rw-r--r--spec/ruby/library/stringio/closed_write_spec.rb12
-rw-r--r--spec/ruby/library/stringio/codepoints_spec.rb9
-rw-r--r--spec/ruby/library/stringio/each_byte_spec.rb11
-rw-r--r--spec/ruby/library/stringio/each_char_spec.rb11
-rw-r--r--spec/ruby/library/stringio/each_codepoint_spec.rb9
-rw-r--r--spec/ruby/library/stringio/each_line_spec.rb21
-rw-r--r--spec/ruby/library/stringio/each_spec.rb21
-rw-r--r--spec/ruby/library/stringio/eof_spec.rb11
-rw-r--r--spec/ruby/library/stringio/external_encoding_spec.rb25
-rw-r--r--spec/ruby/library/stringio/fcntl_spec.rb8
-rw-r--r--spec/ruby/library/stringio/fileno_spec.rb9
-rw-r--r--spec/ruby/library/stringio/fixtures/classes.rb15
-rw-r--r--spec/ruby/library/stringio/flush_spec.rb9
-rw-r--r--spec/ruby/library/stringio/fsync_spec.rb9
-rw-r--r--spec/ruby/library/stringio/getbyte_spec.rb19
-rw-r--r--spec/ruby/library/stringio/getc_spec.rb19
-rw-r--r--spec/ruby/library/stringio/getch_spec.rb46
-rw-r--r--spec/ruby/library/stringio/gets_spec.rb247
-rw-r--r--spec/ruby/library/stringio/initialize_spec.rb185
-rw-r--r--spec/ruby/library/stringio/internal_encoding_spec.rb10
-rw-r--r--spec/ruby/library/stringio/isatty_spec.rb7
-rw-r--r--spec/ruby/library/stringio/length_spec.rb7
-rw-r--r--spec/ruby/library/stringio/lineno_spec.rb30
-rw-r--r--spec/ruby/library/stringio/lines_spec.rb21
-rw-r--r--spec/ruby/library/stringio/open_spec.rb207
-rw-r--r--spec/ruby/library/stringio/path_spec.rb8
-rw-r--r--spec/ruby/library/stringio/pid_spec.rb8
-rw-r--r--spec/ruby/library/stringio/pos_spec.rb28
-rw-r--r--spec/ruby/library/stringio/print_spec.rb100
-rw-r--r--spec/ruby/library/stringio/printf_spec.rb70
-rw-r--r--spec/ruby/library/stringio/putc_spec.rb88
-rw-r--r--spec/ruby/library/stringio/puts_spec.rb159
-rw-r--r--spec/ruby/library/stringio/read_nonblock_spec.rb29
-rw-r--r--spec/ruby/library/stringio/read_spec.rb62
-rw-r--r--spec/ruby/library/stringio/readbyte_spec.rb20
-rw-r--r--spec/ruby/library/stringio/readchar_spec.rb20
-rw-r--r--spec/ruby/library/stringio/readline_spec.rb131
-rw-r--r--spec/ruby/library/stringio/readlines_spec.rb101
-rw-r--r--spec/ruby/library/stringio/readpartial_spec.rb80
-rw-r--r--spec/ruby/library/stringio/reopen_spec.rb288
-rw-r--r--spec/ruby/library/stringio/rewind_spec.rb24
-rw-r--r--spec/ruby/library/stringio/seek_spec.rb67
-rw-r--r--spec/ruby/library/stringio/set_encoding_spec.rb10
-rw-r--r--spec/ruby/library/stringio/shared/codepoints.rb45
-rw-r--r--spec/ruby/library/stringio/shared/each.rb114
-rw-r--r--spec/ruby/library/stringio/shared/each_byte.rb48
-rw-r--r--spec/ruby/library/stringio/shared/each_char.rb36
-rw-r--r--spec/ruby/library/stringio/shared/eof.rb24
-rw-r--r--spec/ruby/library/stringio/shared/getc.rb43
-rw-r--r--spec/ruby/library/stringio/shared/isatty.rb5
-rw-r--r--spec/ruby/library/stringio/shared/length.rb5
-rw-r--r--spec/ruby/library/stringio/shared/read.rb121
-rw-r--r--spec/ruby/library/stringio/shared/readchar.rb29
-rw-r--r--spec/ruby/library/stringio/shared/sysread.rb15
-rw-r--r--spec/ruby/library/stringio/shared/tell.rb12
-rw-r--r--spec/ruby/library/stringio/shared/write.rb87
-rw-r--r--spec/ruby/library/stringio/size_spec.rb7
-rw-r--r--spec/ruby/library/stringio/string_spec.rb50
-rw-r--r--spec/ruby/library/stringio/stringio_spec.rb8
-rw-r--r--spec/ruby/library/stringio/sync_spec.rb19
-rw-r--r--spec/ruby/library/stringio/sysread_spec.rb48
-rw-r--r--spec/ruby/library/stringio/syswrite_spec.rb19
-rw-r--r--spec/ruby/library/stringio/tell_spec.rb7
-rw-r--r--spec/ruby/library/stringio/truncate_spec.rb70
-rw-r--r--spec/ruby/library/stringio/tty_spec.rb7
-rw-r--r--spec/ruby/library/stringio/ungetbyte_spec.rb6
-rw-r--r--spec/ruby/library/stringio/ungetc_spec.rb72
-rw-r--r--spec/ruby/library/stringio/write_nonblock_spec.rb19
-rw-r--r--spec/ruby/library/stringio/write_spec.rb19
-rw-r--r--spec/ruby/library/stringscanner/append_spec.rb11
-rw-r--r--spec/ruby/library/stringscanner/beginning_of_line_spec.rb7
-rw-r--r--spec/ruby/library/stringscanner/bol_spec.rb7
-rw-r--r--spec/ruby/library/stringscanner/check_spec.rb16
-rw-r--r--spec/ruby/library/stringscanner/check_until_spec.rb15
-rw-r--r--spec/ruby/library/stringscanner/clear_spec.rb20
-rw-r--r--spec/ruby/library/stringscanner/concat_spec.rb11
-rw-r--r--spec/ruby/library/stringscanner/dup_spec.rb39
-rw-r--r--spec/ruby/library/stringscanner/element_reference_spec.rb60
-rw-r--r--spec/ruby/library/stringscanner/empty_spec.rb20
-rw-r--r--spec/ruby/library/stringscanner/eos_spec.rb7
-rw-r--r--spec/ruby/library/stringscanner/exist_spec.rb24
-rw-r--r--spec/ruby/library/stringscanner/get_byte_spec.rb7
-rw-r--r--spec/ruby/library/stringscanner/getbyte_spec.rb23
-rw-r--r--spec/ruby/library/stringscanner/getch_spec.rb35
-rw-r--r--spec/ruby/library/stringscanner/initialize_spec.rb28
-rw-r--r--spec/ruby/library/stringscanner/inspect_spec.rb20
-rw-r--r--spec/ruby/library/stringscanner/match_spec.rb28
-rw-r--r--spec/ruby/library/stringscanner/matched_size_spec.rb7
-rw-r--r--spec/ruby/library/stringscanner/matched_spec.rb41
-rw-r--r--spec/ruby/library/stringscanner/must_C_version_spec.rb8
-rw-r--r--spec/ruby/library/stringscanner/peek_spec.rb7
-rw-r--r--spec/ruby/library/stringscanner/peep_spec.rb20
-rw-r--r--spec/ruby/library/stringscanner/pointer_spec.rb11
-rw-r--r--spec/ruby/library/stringscanner/pos_spec.rb11
-rw-r--r--spec/ruby/library/stringscanner/post_match_spec.rb28
-rw-r--r--spec/ruby/library/stringscanner/pre_match_spec.rb41
-rw-r--r--spec/ruby/library/stringscanner/reset_spec.rb15
-rw-r--r--spec/ruby/library/stringscanner/rest_size_spec.rb7
-rw-r--r--spec/ruby/library/stringscanner/rest_spec.rb48
-rw-r--r--spec/ruby/library/stringscanner/restsize_spec.rb20
-rw-r--r--spec/ruby/library/stringscanner/scan_full_spec.rb30
-rw-r--r--spec/ruby/library/stringscanner/scan_spec.rb43
-rw-r--r--spec/ruby/library/stringscanner/scan_until_spec.rb23
-rw-r--r--spec/ruby/library/stringscanner/search_full_spec.rb30
-rw-r--r--spec/ruby/library/stringscanner/shared/bol.rb25
-rw-r--r--spec/ruby/library/stringscanner/shared/concat.rb30
-rw-r--r--spec/ruby/library/stringscanner/shared/eos.rb17
-rw-r--r--spec/ruby/library/stringscanner/shared/extract_range.rb22
-rw-r--r--spec/ruby/library/stringscanner/shared/extract_range_matched.rb22
-rw-r--r--spec/ruby/library/stringscanner/shared/get_byte.rb29
-rw-r--r--spec/ruby/library/stringscanner/shared/matched_size.rb21
-rw-r--r--spec/ruby/library/stringscanner/shared/peek.rb47
-rw-r--r--spec/ruby/library/stringscanner/shared/pos.rb52
-rw-r--r--spec/ruby/library/stringscanner/shared/rest_size.rb18
-rw-r--r--spec/ruby/library/stringscanner/shared/terminate.rb8
-rw-r--r--spec/ruby/library/stringscanner/skip_spec.rb18
-rw-r--r--spec/ruby/library/stringscanner/skip_until_spec.rb18
-rw-r--r--spec/ruby/library/stringscanner/string_spec.rb40
-rw-r--r--spec/ruby/library/stringscanner/terminate_spec.rb7
-rw-r--r--spec/ruby/library/stringscanner/unscan_spec.rb28
-rw-r--r--spec/ruby/library/syslog/alert_spec.rb10
-rw-r--r--spec/ruby/library/syslog/close_spec.rb58
-rw-r--r--spec/ruby/library/syslog/constants_spec.rb41
-rw-r--r--spec/ruby/library/syslog/crit_spec.rb10
-rw-r--r--spec/ruby/library/syslog/debug_spec.rb10
-rw-r--r--spec/ruby/library/syslog/emerg_spec.rb16
-rw-r--r--spec/ruby/library/syslog/err_spec.rb10
-rw-r--r--spec/ruby/library/syslog/facility_spec.rb48
-rw-r--r--spec/ruby/library/syslog/ident_spec.rb35
-rw-r--r--spec/ruby/library/syslog/info_spec.rb10
-rw-r--r--spec/ruby/library/syslog/inspect_spec.rb39
-rw-r--r--spec/ruby/library/syslog/instance_spec.rb13
-rw-r--r--spec/ruby/library/syslog/log_spec.rb56
-rw-r--r--spec/ruby/library/syslog/mask_spec.rb113
-rw-r--r--spec/ruby/library/syslog/notice_spec.rb10
-rw-r--r--spec/ruby/library/syslog/open_spec.rb92
-rw-r--r--spec/ruby/library/syslog/opened_spec.rb39
-rw-r--r--spec/ruby/library/syslog/options_spec.rb48
-rw-r--r--spec/ruby/library/syslog/reopen_spec.rb10
-rw-r--r--spec/ruby/library/syslog/shared/log.rb40
-rw-r--r--spec/ruby/library/syslog/shared/reopen.rb40
-rw-r--r--spec/ruby/library/syslog/warning_spec.rb10
-rw-r--r--spec/ruby/library/tempfile/_close_spec.rb21
-rw-r--r--spec/ruby/library/tempfile/callback_spec.rb6
-rw-r--r--spec/ruby/library/tempfile/close_spec.rb57
-rw-r--r--spec/ruby/library/tempfile/delete_spec.rb7
-rw-r--r--spec/ruby/library/tempfile/initialize_spec.rb41
-rw-r--r--spec/ruby/library/tempfile/length_spec.rb7
-rw-r--r--spec/ruby/library/tempfile/open_spec.rb89
-rw-r--r--spec/ruby/library/tempfile/path_spec.rb26
-rw-r--r--spec/ruby/library/tempfile/shared/length.rb21
-rw-r--r--spec/ruby/library/tempfile/shared/unlink.rb12
-rw-r--r--spec/ruby/library/tempfile/size_spec.rb7
-rw-r--r--spec/ruby/library/tempfile/unlink_spec.rb7
-rw-r--r--spec/ruby/library/thread/queue_spec.rb8
-rw-r--r--spec/ruby/library/thread/sizedqueue_spec.rb8
-rw-r--r--spec/ruby/library/time/httpdate_spec.rb21
-rw-r--r--spec/ruby/library/time/iso8601_spec.rb7
-rw-r--r--spec/ruby/library/time/rfc2822_spec.rb7
-rw-r--r--spec/ruby/library/time/rfc822_spec.rb7
-rw-r--r--spec/ruby/library/time/shared/rfc2822.rb65
-rw-r--r--spec/ruby/library/time/shared/xmlschema.rb53
-rw-r--r--spec/ruby/library/time/to_date_spec.rb42
-rw-r--r--spec/ruby/library/time/to_datetime_spec.rb27
-rw-r--r--spec/ruby/library/time/to_time_spec.rb17
-rw-r--r--spec/ruby/library/time/xmlschema_spec.rb7
-rw-r--r--spec/ruby/library/timeout/error_spec.rb8
-rw-r--r--spec/ruby/library/timeout/timeout_spec.rb37
-rw-r--r--spec/ruby/library/tmpdir/dir/mktmpdir_spec.rb117
-rw-r--r--spec/ruby/library/tmpdir/dir/tmpdir_spec.rb10
-rw-r--r--spec/ruby/library/uri/decode_www_form_component_spec.rb6
-rw-r--r--spec/ruby/library/uri/decode_www_form_spec.rb6
-rw-r--r--spec/ruby/library/uri/encode_www_form_component_spec.rb6
-rw-r--r--spec/ruby/library/uri/encode_www_form_spec.rb6
-rw-r--r--spec/ruby/library/uri/eql_spec.rb10
-rw-r--r--spec/ruby/library/uri/equality_spec.rb46
-rw-r--r--spec/ruby/library/uri/escape/decode_spec.rb6
-rw-r--r--spec/ruby/library/uri/escape/encode_spec.rb6
-rw-r--r--spec/ruby/library/uri/escape/escape_spec.rb6
-rw-r--r--spec/ruby/library/uri/escape/unescape_spec.rb6
-rw-r--r--spec/ruby/library/uri/extract_spec.rb86
-rw-r--r--spec/ruby/library/uri/fixtures/classes.rb11
-rw-r--r--spec/ruby/library/uri/fixtures/normalization.rb54
-rw-r--r--spec/ruby/library/uri/ftp/build_spec.rb6
-rw-r--r--spec/ruby/library/uri/ftp/merge_spec.rb6
-rw-r--r--spec/ruby/library/uri/ftp/new2_spec.rb6
-rw-r--r--spec/ruby/library/uri/ftp/path_spec.rb26
-rw-r--r--spec/ruby/library/uri/ftp/set_typecode_spec.rb6
-rw-r--r--spec/ruby/library/uri/ftp/to_s_spec.rb15
-rw-r--r--spec/ruby/library/uri/ftp/typecode_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/absolute_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/build2_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/build_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/coerce_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/component_ary_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/component_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/default_port_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/eql_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/equal_value_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/fragment_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/hash_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/hierarchical_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/host_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/inspect_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/merge_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/minus_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/normalize_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/opaque_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/password_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/path_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/plus_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/port_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/query_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/registry_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/relative_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/route_from_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/route_to_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/scheme_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/select_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/set_fragment_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/set_host_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/set_opaque_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/set_password_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/set_path_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/set_port_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/set_query_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/set_registry_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/set_scheme_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/set_user_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/set_userinfo_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/to_s_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/use_registry_spec.rb6
-rw-r--r--spec/ruby/library/uri/generic/user_spec.rb10
-rw-r--r--spec/ruby/library/uri/generic/userinfo_spec.rb10
-rw-r--r--spec/ruby/library/uri/http/build_spec.rb6
-rw-r--r--spec/ruby/library/uri/http/request_uri_spec.rb16
-rw-r--r--spec/ruby/library/uri/join_spec.rb59
-rw-r--r--spec/ruby/library/uri/ldap/attributes_spec.rb10
-rw-r--r--spec/ruby/library/uri/ldap/build_spec.rb6
-rw-r--r--spec/ruby/library/uri/ldap/dn_spec.rb10
-rw-r--r--spec/ruby/library/uri/ldap/extensions_spec.rb10
-rw-r--r--spec/ruby/library/uri/ldap/filter_spec.rb10
-rw-r--r--spec/ruby/library/uri/ldap/hierarchical_spec.rb6
-rw-r--r--spec/ruby/library/uri/ldap/scope_spec.rb10
-rw-r--r--spec/ruby/library/uri/ldap/set_attributes_spec.rb6
-rw-r--r--spec/ruby/library/uri/ldap/set_dn_spec.rb6
-rw-r--r--spec/ruby/library/uri/ldap/set_extensions_spec.rb6
-rw-r--r--spec/ruby/library/uri/ldap/set_filter_spec.rb6
-rw-r--r--spec/ruby/library/uri/ldap/set_scope_spec.rb6
-rw-r--r--spec/ruby/library/uri/mailto/build_spec.rb98
-rw-r--r--spec/ruby/library/uri/mailto/headers_spec.rb10
-rw-r--r--spec/ruby/library/uri/mailto/set_headers_spec.rb6
-rw-r--r--spec/ruby/library/uri/mailto/set_to_spec.rb6
-rw-r--r--spec/ruby/library/uri/mailto/to_mailtext_spec.rb6
-rw-r--r--spec/ruby/library/uri/mailto/to_rfc822text_spec.rb6
-rw-r--r--spec/ruby/library/uri/mailto/to_s_spec.rb6
-rw-r--r--spec/ruby/library/uri/mailto/to_spec.rb10
-rw-r--r--spec/ruby/library/uri/merge_spec.rb20
-rw-r--r--spec/ruby/library/uri/normalize_spec.rb35
-rw-r--r--spec/ruby/library/uri/parse_spec.rb203
-rw-r--r--spec/ruby/library/uri/parser/escape_spec.rb6
-rw-r--r--spec/ruby/library/uri/parser/extract_spec.rb7
-rw-r--r--spec/ruby/library/uri/parser/inspect_spec.rb6
-rw-r--r--spec/ruby/library/uri/parser/join_spec.rb7
-rw-r--r--spec/ruby/library/uri/parser/make_regexp_spec.rb6
-rw-r--r--spec/ruby/library/uri/parser/parse_spec.rb7
-rw-r--r--spec/ruby/library/uri/parser/split_spec.rb6
-rw-r--r--spec/ruby/library/uri/parser/unescape_spec.rb6
-rw-r--r--spec/ruby/library/uri/plus_spec.rb459
-rw-r--r--spec/ruby/library/uri/regexp_spec.rb18
-rw-r--r--spec/ruby/library/uri/route_from_spec.rb23
-rw-r--r--spec/ruby/library/uri/route_to_spec.rb26
-rw-r--r--spec/ruby/library/uri/select_spec.rb31
-rw-r--r--spec/ruby/library/uri/set_component_spec.rb45
-rw-r--r--spec/ruby/library/uri/shared/eql.rb17
-rw-r--r--spec/ruby/library/uri/shared/extract.rb83
-rw-r--r--spec/ruby/library/uri/shared/join.rb56
-rw-r--r--spec/ruby/library/uri/shared/parse.rb199
-rw-r--r--spec/ruby/library/uri/split_spec.rb6
-rw-r--r--spec/ruby/library/uri/uri_spec.rb29
-rw-r--r--spec/ruby/library/uri/util/make_components_hash_spec.rb6
-rw-r--r--spec/ruby/library/weakref/__getobj___spec.rb17
-rw-r--r--spec/ruby/library/weakref/allocate_spec.rb8
-rw-r--r--spec/ruby/library/weakref/fixtures/classes.rb24
-rw-r--r--spec/ruby/library/weakref/new_spec.rb13
-rw-r--r--spec/ruby/library/weakref/send_spec.rb37
-rw-r--r--spec/ruby/library/weakref/weakref_alive_spec.rb15
-rw-r--r--spec/ruby/library/win32ole/fixtures/classes.rb22
-rw-r--r--spec/ruby/library/win32ole/fixtures/event.xml4
-rw-r--r--spec/ruby/library/win32ole/win32ole/_getproperty_spec.rb14
-rw-r--r--spec/ruby/library/win32ole/win32ole/_invoke_spec.rb21
-rw-r--r--spec/ruby/library/win32ole/win32ole/codepage_spec.rb13
-rw-r--r--spec/ruby/library/win32ole/win32ole/connect_spec.rb15
-rw-r--r--spec/ruby/library/win32ole/win32ole/const_load_spec.rb32
-rw-r--r--spec/ruby/library/win32ole/win32ole/constants_spec.rb42
-rw-r--r--spec/ruby/library/win32ole/win32ole/create_guid_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole/invoke_spec.rb14
-rw-r--r--spec/ruby/library/win32ole/win32ole/locale_spec.rb29
-rw-r--r--spec/ruby/library/win32ole/win32ole/new_spec.rb25
-rw-r--r--spec/ruby/library/win32ole/win32ole/ole_func_methods_spec.rb21
-rw-r--r--spec/ruby/library/win32ole/win32ole/ole_get_methods_spec.rb16
-rw-r--r--spec/ruby/library/win32ole/win32ole/ole_method_help_spec.rb10
-rw-r--r--spec/ruby/library/win32ole/win32ole/ole_method_spec.rb10
-rw-r--r--spec/ruby/library/win32ole/win32ole/ole_methods_spec.rb21
-rw-r--r--spec/ruby/library/win32ole/win32ole/ole_obj_help_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole/ole_put_methods_spec.rb21
-rw-r--r--spec/ruby/library/win32ole/win32ole/setproperty_spec.rb10
-rw-r--r--spec/ruby/library/win32ole/win32ole/shared/ole_method.rb19
-rw-r--r--spec/ruby/library/win32ole/win32ole/shared/setproperty.rb23
-rw-r--r--spec/ruby/library/win32ole/win32ole_event/new_spec.rb33
-rw-r--r--spec/ruby/library/win32ole/win32ole_event/on_event_spec.rb70
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/dispid_spec.rb20
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/event_interface_spec.rb28
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/event_spec.rb22
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/helpcontext_spec.rb26
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/helpfile_spec.rb20
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/helpstring_spec.rb20
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/invkind_spec.rb20
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/invoke_kind_spec.rb20
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/name_spec.rb11
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/new_spec.rb33
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/offset_vtbl_spec.rb21
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/params_spec.rb28
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/return_type_detail_spec.rb21
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/return_type_spec.rb20
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/return_vtype_spec.rb20
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/shared/name.rb20
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/size_opt_params_spec.rb20
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/size_params_spec.rb20
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/to_s_spec.rb11
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/visible_spec.rb20
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/default_spec.rb31
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/input_spec.rb21
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/name_spec.rb11
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/ole_type_detail_spec.rb21
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/ole_type_spec.rb21
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/optional_spec.rb21
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/retval_spec.rb21
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/shared/name.rb21
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/to_s_spec.rb11
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/guid_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/helpcontext_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/helpfile_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/helpstring_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/major_version_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/minor_version_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/name_spec.rb11
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/new_spec.rb37
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/ole_classes_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/ole_methods_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/ole_type_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/progid_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/progids_spec.rb14
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/shared/name.rb19
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/src_type_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/to_s_spec.rb11
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/typekind_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/typelibs_spec.rb22
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/variables_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/visible_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/name_spec.rb11
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/ole_type_detail_spec.rb19
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/ole_type_spec.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/shared/name.rb18
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/to_s_spec.rb11
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/value_spec.rb19
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/variable_kind_spec.rb19
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/varkind_spec.rb19
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/visible_spec.rb18
-rw-r--r--spec/ruby/library/yaml/add_builtin_type_spec.rb2
-rw-r--r--spec/ruby/library/yaml/add_domain_type_spec.rb2
-rw-r--r--spec/ruby/library/yaml/add_private_type_spec.rb2
-rw-r--r--spec/ruby/library/yaml/add_ruby_type_spec.rb2
-rw-r--r--spec/ruby/library/yaml/detect_implicit_spec.rb2
-rw-r--r--spec/ruby/library/yaml/dump_spec.rb51
-rw-r--r--spec/ruby/library/yaml/dump_stream_spec.rb8
-rw-r--r--spec/ruby/library/yaml/each_node_spec.rb2
-rw-r--r--spec/ruby/library/yaml/emitter_spec.rb2
-rw-r--r--spec/ruby/library/yaml/fixtures/common.rb10
-rw-r--r--spec/ruby/library/yaml/fixtures/example_class.rb5
-rw-r--r--spec/ruby/library/yaml/fixtures/strings.rb36
-rw-r--r--spec/ruby/library/yaml/fixtures/test_yaml.yml2
-rw-r--r--spec/ruby/library/yaml/generic_parser_spec.rb2
-rw-r--r--spec/ruby/library/yaml/load_documents_spec.rb10
-rw-r--r--spec/ruby/library/yaml/load_file_spec.rb13
-rw-r--r--spec/ruby/library/yaml/load_spec.rb137
-rw-r--r--spec/ruby/library/yaml/load_stream_spec.rb8
-rw-r--r--spec/ruby/library/yaml/object_maker_spec.rb2
-rw-r--r--spec/ruby/library/yaml/parse_documents_spec.rb2
-rw-r--r--spec/ruby/library/yaml/parse_file_spec.rb10
-rw-r--r--spec/ruby/library/yaml/parse_spec.rb22
-rw-r--r--spec/ruby/library/yaml/parser_spec.rb2
-rw-r--r--spec/ruby/library/yaml/quick_emit_spec.rb2
-rw-r--r--spec/ruby/library/yaml/read_type_class_spec.rb2
-rw-r--r--spec/ruby/library/yaml/shared/each_document.rb18
-rw-r--r--spec/ruby/library/yaml/tagurize_spec.rb11
-rw-r--r--spec/ruby/library/yaml/to_yaml_spec.rb99
-rw-r--r--spec/ruby/library/yaml/transfer_spec.rb2
-rw-r--r--spec/ruby/library/yaml/try_implicit_spec.rb2
-rw-r--r--spec/ruby/library/zlib/adler32_spec.rb46
-rw-r--r--spec/ruby/library/zlib/crc32_spec.rb54
-rw-r--r--spec/ruby/library/zlib/crc_table_spec.rb75
-rw-r--r--spec/ruby/library/zlib/deflate/append_spec.rb1
-rw-r--r--spec/ruby/library/zlib/deflate/deflate_spec.rb128
-rw-r--r--spec/ruby/library/zlib/deflate/flush_spec.rb1
-rw-r--r--spec/ruby/library/zlib/deflate/new_spec.rb1
-rw-r--r--spec/ruby/library/zlib/deflate/params_spec.rb17
-rw-r--r--spec/ruby/library/zlib/deflate/set_dictionary_spec.rb14
-rw-r--r--spec/ruby/library/zlib/deflate_spec.rb8
-rw-r--r--spec/ruby/library/zlib/gzipfile/close_spec.rb21
-rw-r--r--spec/ruby/library/zlib/gzipfile/closed_spec.rb16
-rw-r--r--spec/ruby/library/zlib/gzipfile/comment_spec.rb26
-rw-r--r--spec/ruby/library/zlib/gzipfile/crc_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipfile/finish_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipfile/level_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipfile/mtime_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipfile/orig_name_spec.rb26
-rw-r--r--spec/ruby/library/zlib/gzipfile/os_code_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipfile/sync_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipfile/to_io_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipfile/wrap_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipreader/each_byte_spec.rb51
-rw-r--r--spec/ruby/library/zlib/gzipreader/each_line_spec.rb5
-rw-r--r--spec/ruby/library/zlib/gzipreader/each_spec.rb5
-rw-r--r--spec/ruby/library/zlib/gzipreader/eof_spec.rb56
-rw-r--r--spec/ruby/library/zlib/gzipreader/getc_spec.rb41
-rw-r--r--spec/ruby/library/zlib/gzipreader/gets_spec.rb22
-rw-r--r--spec/ruby/library/zlib/gzipreader/lineno_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipreader/new_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipreader/open_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipreader/pos_spec.rb26
-rw-r--r--spec/ruby/library/zlib/gzipreader/read_spec.rb68
-rw-r--r--spec/ruby/library/zlib/gzipreader/readchar_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipreader/readline_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipreader/readlines_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipreader/readpartial_spec.rb17
-rw-r--r--spec/ruby/library/zlib/gzipreader/rewind_spec.rb48
-rw-r--r--spec/ruby/library/zlib/gzipreader/shared/each.rb51
-rw-r--r--spec/ruby/library/zlib/gzipreader/tell_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipreader/ungetbyte_spec.rb122
-rw-r--r--spec/ruby/library/zlib/gzipreader/ungetc_spec.rb292
-rw-r--r--spec/ruby/library/zlib/gzipreader/unused_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipwriter/append_spec.rb17
-rw-r--r--spec/ruby/library/zlib/gzipwriter/comment_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipwriter/flush_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipwriter/mtime_spec.rb38
-rw-r--r--spec/ruby/library/zlib/gzipwriter/new_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipwriter/open_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipwriter/orig_name_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipwriter/pos_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipwriter/print_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipwriter/printf_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipwriter/putc_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipwriter/puts_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipwriter/tell_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipwriter/write_spec.rb36
-rw-r--r--spec/ruby/library/zlib/inflate/append_spec.rb60
-rw-r--r--spec/ruby/library/zlib/inflate/finish_spec.rb28
-rw-r--r--spec/ruby/library/zlib/inflate/inflate_spec.rb152
-rw-r--r--spec/ruby/library/zlib/inflate/new_spec.rb1
-rw-r--r--spec/ruby/library/zlib/inflate/set_dictionary_spec.rb20
-rw-r--r--spec/ruby/library/zlib/inflate/sync_point_spec.rb1
-rw-r--r--spec/ruby/library/zlib/inflate/sync_spec.rb1
-rw-r--r--spec/ruby/library/zlib/inflate_spec.rb8
-rw-r--r--spec/ruby/library/zlib/zlib_version_spec.rb1
-rw-r--r--spec/ruby/library/zlib/zstream/adler_spec.rb11
-rw-r--r--spec/ruby/library/zlib/zstream/avail_in_spec.rb9
-rw-r--r--spec/ruby/library/zlib/zstream/avail_out_spec.rb9
-rw-r--r--spec/ruby/library/zlib/zstream/close_spec.rb1
-rw-r--r--spec/ruby/library/zlib/zstream/closed_spec.rb1
-rw-r--r--spec/ruby/library/zlib/zstream/data_type_spec.rb9
-rw-r--r--spec/ruby/library/zlib/zstream/end_spec.rb1
-rw-r--r--spec/ruby/library/zlib/zstream/ended_spec.rb1
-rw-r--r--spec/ruby/library/zlib/zstream/finish_spec.rb1
-rw-r--r--spec/ruby/library/zlib/zstream/finished_spec.rb1
-rw-r--r--spec/ruby/library/zlib/zstream/flush_next_in_spec.rb1
-rw-r--r--spec/ruby/library/zlib/zstream/flush_next_out_spec.rb14
-rw-r--r--spec/ruby/library/zlib/zstream/reset_spec.rb1
-rw-r--r--spec/ruby/library/zlib/zstream/stream_end_spec.rb1
-rw-r--r--spec/ruby/library/zlib/zstream/total_in_spec.rb1
-rw-r--r--spec/ruby/library/zlib/zstream/total_out_spec.rb1
-rw-r--r--spec/ruby/optional/capi/README16
-rw-r--r--spec/ruby/optional/capi/array_spec.rb463
-rw-r--r--spec/ruby/optional/capi/bignum_spec.rb214
-rw-r--r--spec/ruby/optional/capi/boolean_spec.rb33
-rw-r--r--spec/ruby/optional/capi/class_spec.rb376
-rw-r--r--spec/ruby/optional/capi/complex_spec.rb45
-rw-r--r--spec/ruby/optional/capi/constants_spec.rb270
-rw-r--r--spec/ruby/optional/capi/data_spec.rb41
-rw-r--r--spec/ruby/optional/capi/encoding_spec.rb479
-rw-r--r--spec/ruby/optional/capi/enumerator_spec.rb66
-rw-r--r--spec/ruby/optional/capi/exception_spec.rb58
-rw-r--r--spec/ruby/optional/capi/ext/.gitignore9
-rw-r--r--spec/ruby/optional/capi/ext/array_spec.c452
-rw-r--r--spec/ruby/optional/capi/ext/bignum_spec.c149
-rw-r--r--spec/ruby/optional/capi/ext/boolean_spec.c34
-rw-r--r--spec/ruby/optional/capi/ext/class_id_under_autoload_spec.c5
-rw-r--r--spec/ruby/optional/capi/ext/class_spec.c261
-rw-r--r--spec/ruby/optional/capi/ext/class_under_autoload_spec.c5
-rw-r--r--spec/ruby/optional/capi/ext/complex_spec.c76
-rw-r--r--spec/ruby/optional/capi/ext/constants_spec.c646
-rw-r--r--spec/ruby/optional/capi/ext/data_spec.c90
-rw-r--r--spec/ruby/optional/capi/ext/encoding_spec.c417
-rw-r--r--spec/ruby/optional/capi/ext/enumerator_spec.c42
-rw-r--r--spec/ruby/optional/capi/ext/exception_spec.c72
-rw-r--r--spec/ruby/optional/capi/ext/file_spec.c44
-rw-r--r--spec/ruby/optional/capi/ext/fixnum_spec.c52
-rw-r--r--spec/ruby/optional/capi/ext/float_spec.c54
-rw-r--r--spec/ruby/optional/capi/ext/gc_spec.c72
-rw-r--r--spec/ruby/optional/capi/ext/globals_spec.c199
-rw-r--r--spec/ruby/optional/capi/ext/hash_spec.c218
-rw-r--r--spec/ruby/optional/capi/ext/integer_spec.c40
-rw-r--r--spec/ruby/optional/capi/ext/io_spec.c327
-rw-r--r--spec/ruby/optional/capi/ext/kernel_spec.c461
-rw-r--r--spec/ruby/optional/capi/ext/marshal_spec.c36
-rw-r--r--spec/ruby/optional/capi/ext/module_spec.c262
-rw-r--r--spec/ruby/optional/capi/ext/module_under_autoload_spec.c7
-rw-r--r--spec/ruby/optional/capi/ext/mutex_spec.c91
-rw-r--r--spec/ruby/optional/capi/ext/numeric_spec.c217
-rw-r--r--spec/ruby/optional/capi/ext/object_spec.c646
-rw-r--r--spec/ruby/optional/capi/ext/proc_spec.c85
-rw-r--r--spec/ruby/optional/capi/ext/range_spec.c66
-rw-r--r--spec/ruby/optional/capi/ext/rational_spec.c95
-rw-r--r--spec/ruby/optional/capi/ext/regexp_spec.c84
-rw-r--r--spec/ruby/optional/capi/ext/rubyspec.h624
-rw-r--r--spec/ruby/optional/capi/ext/st_spec.c93
-rw-r--r--spec/ruby/optional/capi/ext/string_spec.c750
-rw-r--r--spec/ruby/optional/capi/ext/struct_spec.c131
-rw-r--r--spec/ruby/optional/capi/ext/symbol_spec.c138
-rw-r--r--spec/ruby/optional/capi/ext/thread_spec.c188
-rw-r--r--spec/ruby/optional/capi/ext/time_spec.c127
-rw-r--r--spec/ruby/optional/capi/ext/typed_data_spec.c170
-rw-r--r--spec/ruby/optional/capi/ext/util_spec.c95
-rw-r--r--spec/ruby/optional/capi/file_spec.rb89
-rw-r--r--spec/ruby/optional/capi/fixnum_spec.rb124
-rw-r--r--spec/ruby/optional/capi/fixtures/class.rb91
-rw-r--r--spec/ruby/optional/capi/fixtures/const_get.rb5
-rw-r--r--spec/ruby/optional/capi/fixtures/const_get_at.rb5
-rw-r--r--spec/ruby/optional/capi/fixtures/const_get_from.rb5
-rw-r--r--spec/ruby/optional/capi/fixtures/const_get_object.rb3
-rw-r--r--spec/ruby/optional/capi/fixtures/encoding.rb3
-rw-r--r--spec/ruby/optional/capi/fixtures/foo.rb1
-rw-r--r--spec/ruby/optional/capi/fixtures/module.rb35
-rw-r--r--spec/ruby/optional/capi/fixtures/module_autoload.rb4
-rw-r--r--spec/ruby/optional/capi/fixtures/path_to_class.rb6
-rw-r--r--spec/ruby/optional/capi/fixtures/proc.rb20
-rw-r--r--spec/ruby/optional/capi/float_spec.rb30
-rw-r--r--spec/ruby/optional/capi/gc_spec.rb54
-rw-r--r--spec/ruby/optional/capi/globals_spec.rb224
-rw-r--r--spec/ruby/optional/capi/hash_spec.rb252
-rw-r--r--spec/ruby/optional/capi/integer_spec.rb275
-rw-r--r--spec/ruby/optional/capi/io_spec.rb347
-rw-r--r--spec/ruby/optional/capi/kernel_spec.rb539
-rw-r--r--spec/ruby/optional/capi/marshal_spec.rb46
-rw-r--r--spec/ruby/optional/capi/module_spec.rb373
-rw-r--r--spec/ruby/optional/capi/mutex_spec.rb88
-rw-r--r--spec/ruby/optional/capi/numeric_spec.rb489
-rw-r--r--spec/ruby/optional/capi/object_spec.rb857
-rw-r--r--spec/ruby/optional/capi/proc_spec.rb112
-rw-r--r--spec/ruby/optional/capi/rake_helper.rb22
-rw-r--r--spec/ruby/optional/capi/range_spec.rb95
-rw-r--r--spec/ruby/optional/capi/rational_spec.rb57
-rw-r--r--spec/ruby/optional/capi/regexp_spec.rb71
-rw-r--r--spec/ruby/optional/capi/spec_helper.rb132
-rw-r--r--spec/ruby/optional/capi/st_spec.rb41
-rw-r--r--spec/ruby/optional/capi/string_spec.rb887
-rw-r--r--spec/ruby/optional/capi/struct_spec.rb213
-rw-r--r--spec/ruby/optional/capi/symbol_spec.rb133
-rw-r--r--spec/ruby/optional/capi/thread_spec.rb149
-rw-r--r--spec/ruby/optional/capi/time_spec.rb300
-rw-r--r--spec/ruby/optional/capi/typed_data_spec.rb51
-rw-r--r--spec/ruby/optional/capi/util_spec.rb201
-rw-r--r--spec/ruby/security/cve_2010_1330_spec.rb21
-rw-r--r--spec/ruby/security/cve_2011_4815_spec.rb49
-rw-r--r--spec/ruby/security/cve_2013_4164_spec.rb19
-rw-r--r--spec/ruby/security/cve_2014_8080_spec.rb32
-rw-r--r--spec/ruby/security/cve_2017_17742_spec.rb34
-rw-r--r--spec/ruby/security/cve_2018_16396_spec.rb21
-rw-r--r--spec/ruby/security/cve_2018_6914_spec.rb56
-rw-r--r--spec/ruby/security/cve_2018_8778_spec.rb12
-rw-r--r--spec/ruby/security/cve_2018_8779_spec.rb30
-rw-r--r--spec/ruby/security/cve_2018_8780_spec.rb47
-rw-r--r--spec/ruby/shared/basicobject/method_missing.rb124
-rw-r--r--spec/ruby/shared/basicobject/send.rb117
-rw-r--r--spec/ruby/shared/enumerator/each.rb89
-rw-r--r--spec/ruby/shared/enumerator/enum_cons.rb12
-rw-r--r--spec/ruby/shared/enumerator/enum_for.rb50
-rw-r--r--spec/ruby/shared/enumerator/new.rb42
-rw-r--r--spec/ruby/shared/enumerator/next.rb28
-rw-r--r--spec/ruby/shared/enumerator/rewind.rb39
-rw-r--r--spec/ruby/shared/enumerator/with_index.rb32
-rw-r--r--spec/ruby/shared/enumerator/with_object.rb42
-rw-r--r--spec/ruby/shared/fiber/resume.rb79
-rw-r--r--spec/ruby/shared/file/blockdev.rb9
-rw-r--r--spec/ruby/shared/file/chardev.rb9
-rw-r--r--spec/ruby/shared/file/directory.rb66
-rw-r--r--spec/ruby/shared/file/executable.rb48
-rw-r--r--spec/ruby/shared/file/executable_real.rb46
-rw-r--r--spec/ruby/shared/file/exist.rb24
-rw-r--r--spec/ruby/shared/file/file.rb45
-rw-r--r--spec/ruby/shared/file/grpowned.rb40
-rw-r--r--spec/ruby/shared/file/identical.rb47
-rw-r--r--spec/ruby/shared/file/owned.rb3
-rw-r--r--spec/ruby/shared/file/pipe.rb3
-rw-r--r--spec/ruby/shared/file/readable.rb30
-rw-r--r--spec/ruby/shared/file/readable_real.rb23
-rw-r--r--spec/ruby/shared/file/setgid.rb2
-rw-r--r--spec/ruby/shared/file/setuid.rb2
-rw-r--r--spec/ruby/shared/file/size.rb124
-rw-r--r--spec/ruby/shared/file/socket.rb3
-rw-r--r--spec/ruby/shared/file/sticky.rb29
-rw-r--r--spec/ruby/shared/file/symlink.rb46
-rw-r--r--spec/ruby/shared/file/world_readable.rb49
-rw-r--r--spec/ruby/shared/file/world_writable.rb49
-rw-r--r--spec/ruby/shared/file/writable.rb28
-rw-r--r--spec/ruby/shared/file/writable_real.rb33
-rw-r--r--spec/ruby/shared/file/zero.rb68
-rw-r--r--spec/ruby/shared/hash/key_error.rb25
-rw-r--r--spec/ruby/shared/io/putc.rb57
-rw-r--r--spec/ruby/shared/kernel/equal.rb54
-rw-r--r--spec/ruby/shared/kernel/object_id.rb80
-rw-r--r--spec/ruby/shared/kernel/raise.rb85
-rw-r--r--spec/ruby/shared/math/atanh.rb44
-rw-r--r--spec/ruby/shared/process/abort.rb36
-rw-r--r--spec/ruby/shared/process/exit.rb94
-rw-r--r--spec/ruby/shared/process/fork.rb90
-rw-r--r--spec/ruby/shared/queue/clear.rb12
-rw-r--r--spec/ruby/shared/queue/close.rb14
-rw-r--r--spec/ruby/shared/queue/closed.rb12
-rw-r--r--spec/ruby/shared/queue/deque.rb85
-rw-r--r--spec/ruby/shared/queue/empty.rb12
-rw-r--r--spec/ruby/shared/queue/enque.rb18
-rw-r--r--spec/ruby/shared/queue/length.rb9
-rw-r--r--spec/ruby/shared/queue/num_waiting.rb16
-rw-r--r--spec/ruby/shared/rational/Rational.rb103
-rw-r--r--spec/ruby/shared/rational/abs.rb11
-rw-r--r--spec/ruby/shared/rational/arithmetic_exception_in_coerce.rb33
-rw-r--r--spec/ruby/shared/rational/ceil.rb45
-rw-r--r--spec/ruby/shared/rational/coerce.rb21
-rw-r--r--spec/ruby/shared/rational/comparison.rb117
-rw-r--r--spec/ruby/shared/rational/denominator.rb14
-rw-r--r--spec/ruby/shared/rational/div.rb54
-rw-r--r--spec/ruby/shared/rational/divide.rb71
-rw-r--r--spec/ruby/shared/rational/divmod.rb42
-rw-r--r--spec/ruby/shared/rational/equal_value.rb39
-rw-r--r--spec/ruby/shared/rational/exponent.rb178
-rw-r--r--spec/ruby/shared/rational/fdiv.rb5
-rw-r--r--spec/ruby/shared/rational/floor.rb45
-rw-r--r--spec/ruby/shared/rational/hash.rb9
-rw-r--r--spec/ruby/shared/rational/inspect.rb14
-rw-r--r--spec/ruby/shared/rational/marshal_dump.rb5
-rw-r--r--spec/ruby/shared/rational/marshal_load.rb5
-rw-r--r--spec/ruby/shared/rational/minus.rb48
-rw-r--r--spec/ruby/shared/rational/modulo.rb43
-rw-r--r--spec/ruby/shared/rational/multiply.rb62
-rw-r--r--spec/ruby/shared/rational/numerator.rb10
-rw-r--r--spec/ruby/shared/rational/plus.rb48
-rw-r--r--spec/ruby/shared/rational/quo.rb5
-rw-r--r--spec/ruby/shared/rational/remainder.rb5
-rw-r--r--spec/ruby/shared/rational/round.rb99
-rw-r--r--spec/ruby/shared/rational/to_f.rb10
-rw-r--r--spec/ruby/shared/rational/to_i.rb12
-rw-r--r--spec/ruby/shared/rational/to_r.rb11
-rw-r--r--spec/ruby/shared/rational/to_s.rb14
-rw-r--r--spec/ruby/shared/rational/truncate.rb45
-rw-r--r--spec/ruby/shared/sizedqueue/enque.rb50
-rw-r--r--spec/ruby/shared/sizedqueue/max.rb47
-rw-r--r--spec/ruby/shared/sizedqueue/new.rb18
-rw-r--r--spec/ruby/shared/sizedqueue/num_waiting.rb12
-rw-r--r--spec/ruby/shared/string/times.rb64
-rw-r--r--spec/ruby/shared/time/strftime_for_date.rb275
-rw-r--r--spec/ruby/shared/time/strftime_for_time.rb173
-rw-r--r--spec/ruby/spec_helper.rb32
-rw-r--r--sprintf.c233
-rw-r--r--st.c1054
-rw-r--r--strftime.c42
-rw-r--r--string.c2291
-rw-r--r--struct.c267
-rw-r--r--symbol.c102
-rw-r--r--symbol.h6
-rw-r--r--template/Doxyfile.tmpl74
-rw-r--r--template/GNUmakefile.in8
-rw-r--r--template/configure-ext.mk.tmpl44
-rw-r--r--template/encdb.h.tmpl31
-rw-r--r--template/extinit.c.tmpl17
-rw-r--r--template/exts.mk.tmpl159
-rw-r--r--template/fake.rb.in20
-rw-r--r--template/id.h.tmpl26
-rw-r--r--template/insns.inc.tmpl20
-rw-r--r--template/insns_info.inc.tmpl83
-rw-r--r--template/limits.c.tmpl97
-rw-r--r--template/minsns.inc.tmpl14
-rw-r--r--template/opt_sc.inc.tmpl32
-rw-r--r--template/optinsn.inc.tmpl30
-rw-r--r--template/optunifs.inc.tmpl35
-rw-r--r--template/prelude.c.tmpl82
-rw-r--r--template/ruby-runner.h.in6
-rw-r--r--template/ruby.pc.in3
-rw-r--r--template/sizes.c.tmpl3
-rw-r--r--template/transdb.h.tmpl21
-rw-r--r--template/unicode_norm_gen.tmpl24
-rw-r--r--template/verconf.h.tmpl6
-rw-r--r--template/vm.inc.tmpl29
-rw-r--r--template/vmtc.inc.tmpl18
-rw-r--r--template/yasmdata.rb.tmpl20
-rw-r--r--test/-ext-/arith_seq/test_arith_seq_extract.rb40
-rw-r--r--test/-ext-/array/test_resize.rb2
-rw-r--r--test/-ext-/bignum/test_big2str.rb2
-rw-r--r--test/-ext-/bignum/test_bigzero.rb2
-rw-r--r--test/-ext-/bignum/test_div.rb2
-rw-r--r--test/-ext-/bignum/test_mul.rb2
-rw-r--r--test/-ext-/bignum/test_pack.rb2
-rw-r--r--test/-ext-/bignum/test_str2big.rb2
-rw-r--r--test/-ext-/bug_reporter/test_bug_reporter.rb4
-rw-r--r--test/-ext-/debug/test_debug.rb16
-rw-r--r--test/-ext-/debug/test_profile_frames.rb40
-rw-r--r--test/-ext-/exception/test_data_error.rb2
-rw-r--r--test/-ext-/exception/test_enc_raise.rb2
-rw-r--r--test/-ext-/exception/test_ensured.rb2
-rw-r--r--test/-ext-/exception/test_exception_at_throwing.rb18
-rw-r--r--test/-ext-/funcall/test_funcall.rb11
-rw-r--r--test/-ext-/gvl/test_last_thread.rb1
-rw-r--r--test/-ext-/hash/test_delete.rb2
-rw-r--r--test/-ext-/integer/test_integer.rb17
-rw-r--r--test/-ext-/integer/test_my_integer.rb2
-rw-r--r--test/-ext-/iseq_load/test_iseq_load.rb81
-rw-r--r--test/-ext-/iter/test_yield_block.rb12
-rw-r--r--test/-ext-/load/script.rb2
-rw-r--r--test/-ext-/load/test_protect.rb14
-rw-r--r--test/-ext-/method/test_arity.rb2
-rw-r--r--test/-ext-/num2int/test_num2int.rb38
-rw-r--r--test/-ext-/postponed_job/test_postponed_job.rb1
-rw-r--r--test/-ext-/proc/test_bmethod.rb4
-rw-r--r--test/-ext-/string/test_capacity.rb10
-rw-r--r--test/-ext-/string/test_enc_associate.rb4
-rw-r--r--test/-ext-/string/test_external_new.rb17
-rw-r--r--test/-ext-/string/test_fstring.rb9
-rw-r--r--test/-ext-/string/test_modify_expand.rb1
-rw-r--r--test/-ext-/string/test_rb_str_dup.rb16
-rw-r--r--test/-ext-/struct/test_len.rb10
-rw-r--r--test/-ext-/test_bug-14834.rb12
-rw-r--r--test/-ext-/test_notimplement.rb24
-rw-r--r--test/-ext-/test_printf.rb6
-rw-r--r--test/-ext-/test_scan_args.rb231
-rw-r--r--test/-ext-/thread_fd_close/test_thread_fd_close.rb25
-rw-r--r--test/-ext-/typeddata/test_typeddata.rb9
-rw-r--r--test/-ext-/wait_for_single_fd/test_wait_for_single_fd.rb33
-rw-r--r--test/-ext-/win32/test_console_attr.rb17
-rw-r--r--test/-ext-/win32/test_dln.rb2
-rw-r--r--test/base64/test_base64.rb2
-rw-r--r--test/benchmark/test_benchmark.rb2
-rw-r--r--test/bigdecimal/test_bigdecimal.rb848
-rw-r--r--test/bigdecimal/test_bigdecimal_util.rb42
-rw-r--r--test/cgi/test_cgi_cookie.rb11
-rw-r--r--test/cgi/test_cgi_core.rb12
-rw-r--r--test/cgi/test_cgi_header.rb12
-rw-r--r--test/cgi/test_cgi_modruby.rb2
-rw-r--r--test/cgi/test_cgi_multipart.rb12
-rw-r--r--test/cgi/test_cgi_session.rb10
-rw-r--r--test/cgi/test_cgi_tag_helper.rb2
-rw-r--r--test/cgi/test_cgi_util.rb58
-rw-r--r--test/coverage/test_coverage.rb699
-rw-r--r--test/csv/base.rb9
-rw-r--r--test/csv/helper.rb18
-rw-r--r--test/csv/interface/test_delegation.rb47
-rw-r--r--test/csv/interface/test_read.rb277
-rw-r--r--test/csv/interface/test_read_write.rb51
-rw-r--r--test/csv/interface/test_write.rb174
-rw-r--r--test/csv/parse/test_column_separator.rb40
-rw-r--r--test/csv/parse/test_convert.rb110
-rw-r--r--test/csv/parse/test_each.rb23
-rw-r--r--test/csv/parse/test_general.rb245
-rw-r--r--test/csv/parse/test_header.rb335
-rw-r--r--test/csv/parse/test_invalid.rb36
-rw-r--r--test/csv/parse/test_liberal_parsing.rb160
-rw-r--r--test/csv/parse/test_quote_char_nil.rb93
-rw-r--r--test/csv/parse/test_rewind.rb40
-rw-r--r--test/csv/parse/test_row_separator.rb16
-rw-r--r--test/csv/parse/test_skip_lines.rb105
-rw-r--r--test/csv/parse/test_strip.rb48
-rw-r--r--test/csv/parse/test_unconverted_fields.rb117
-rwxr-xr-xtest/csv/test_csv_parsing.rb222
-rwxr-xr-xtest/csv/test_csv_writing.rb98
-rwxr-xr-xtest/csv/test_data_converters.rb234
-rwxr-xr-xtest/csv/test_encodings.rb34
-rwxr-xr-xtest/csv/test_features.rb190
-rwxr-xr-xtest/csv/test_headers.rb298
-rwxr-xr-xtest/csv/test_interface.rb369
-rwxr-xr-xtest/csv/test_row.rb86
-rwxr-xr-xtest/csv/test_table.rb243
-rw-r--r--test/csv/ts_all.rb21
-rw-r--r--test/csv/write/test_converters.rb53
-rw-r--r--test/csv/write/test_general.rb228
-rw-r--r--test/csv/write/test_quote_empty.rb70
-rw-r--r--test/date/test_date.rb4
-rw-r--r--test/date/test_date_arith.rb31
-rw-r--r--test/date/test_date_attr.rb25
-rw-r--r--test/date/test_date_base.rb443
-rw-r--r--test/date/test_date_compat.rb2
-rw-r--r--test/date/test_date_conv.rb2
-rw-r--r--test/date/test_date_marshal.rb16
-rw-r--r--test/date/test_date_new.rb40
-rw-r--r--test/date/test_date_parse.rb145
-rw-r--r--test/date/test_date_strftime.rb9
-rw-r--r--test/date/test_date_strptime.rb2
-rw-r--r--test/date/test_switch_hitter.rb53
-rw-r--r--test/dbm/test_dbm.rb9
-rw-r--r--test/digest/test_digest.rb1
-rw-r--r--test/drb/drbtest.rb101
-rw-r--r--test/drb/test_acl.rb101
-rw-r--r--test/drb/test_drb.rb142
-rw-r--r--test/drb/test_drbssl.rb56
-rw-r--r--test/drb/test_drbunix.rb26
-rw-r--r--test/drb/ut_eval.rb37
-rw-r--r--test/dtrace/helper.rb87
-rw-r--r--test/dtrace/test_array_create.rb8
-rw-r--r--test/dtrace/test_function_entry.rb9
-rw-r--r--test/dtrace/test_hash_create.rb6
-rw-r--r--test/dtrace/test_method_cache.rb2
-rw-r--r--test/dtrace/test_singleton_function.rb9
-rw-r--r--test/dtrace/test_string.rb6
-rw-r--r--test/erb/test_erb.rb187
-rw-r--r--test/erb/test_erb_command.rb18
-rw-r--r--test/erb/test_erb_m17n.rb2
-rw-r--r--test/etc/test_etc.rb8
-rw-r--r--test/excludes/_appveyor/TestArray.rb7
-rw-r--r--test/excludes/_travis/osx/IMAPTest.rb3
-rw-r--r--test/excludes/_travis/osx/TestGemRemoteFetcher.rb4
-rw-r--r--test/excludes/_travis/osx/TestWEBrickUtils.rb3
-rw-r--r--test/excludes/_wercker/jit-wait/TestDelegateClass.rb2
-rw-r--r--test/excludes/_wercker/jit-wait/TestGemRemoteFetcher.rb4
-rw-r--r--test/excludes/_wercker/jit-wait/TestParallel/TestParallel.rb2
-rw-r--r--test/excludes/_wercker/jit-wait/TestThreadQueue.rb2
-rw-r--r--test/excludes/_wercker/jit/TestThreadQueue.rb2
-rw-r--r--test/fiddle/helper.rb9
-rw-r--r--test/fiddle/test_c_struct_entry.rb2
-rw-r--r--test/fiddle/test_c_union_entity.rb2
-rw-r--r--test/fiddle/test_closure.rb8
-rw-r--r--test/fiddle/test_cparser.rb16
-rw-r--r--test/fiddle/test_fiddle.rb2
-rw-r--r--test/fiddle/test_func.rb18
-rw-r--r--test/fiddle/test_function.rb31
-rw-r--r--test/fiddle/test_handle.rb26
-rw-r--r--test/fiddle/test_import.rb16
-rw-r--r--test/fiddle/test_pointer.rb8
-rw-r--r--test/fileutils/clobber.rb2
-rw-r--r--test/fileutils/fileasserts.rb2
-rw-r--r--test/fileutils/test_dryrun.rb4
-rw-r--r--test/fileutils/test_fileutils.rb248
-rw-r--r--test/fileutils/test_nowrite.rb4
-rw-r--r--test/fileutils/test_verbose.rb4
-rw-r--r--test/fileutils/visibility_tests.rb6
-rw-r--r--test/gdbm/test_gdbm.rb10
-rw-r--r--test/io/console/test_io_console.rb128
-rw-r--r--test/io/nonblock/test_flush.rb20
-rw-r--r--test/io/wait/test_io_wait_uncommon.rb78
-rw-r--r--test/irb/test_context.rb76
-rw-r--r--test/irb/test_init.rb31
-rw-r--r--test/irb/test_ruby-lex.rb108
-rw-r--r--test/irb/test_workspace.rb94
-rw-r--r--test/json/json_encoding_test.rb2
-rw-r--r--[-rwxr-xr-x]test/json/json_generator_test.rb0
-rw-r--r--test/json/json_parser_test.rb6
-rw-r--r--test/lib/envutil.rb105
-rw-r--r--test/lib/find_executable.rb2
-rw-r--r--test/lib/iseq_loader_checker.rb2
-rw-r--r--test/lib/jit_support.rb74
-rw-r--r--test/lib/leakchecker.rb49
-rw-r--r--test/lib/memory_status.rb2
-rw-r--r--test/lib/minitest/autorun.rb2
-rw-r--r--test/lib/minitest/benchmark.rb2
-rw-r--r--test/lib/minitest/mock.rb2
-rw-r--r--test/lib/minitest/unit.rb52
-rw-r--r--test/lib/profile_test_all.rb2
-rw-r--r--test/lib/test/unit.rb197
-rw-r--r--test/lib/test/unit/assertions.rb212
-rw-r--r--test/lib/test/unit/parallel.rb25
-rw-r--r--test/lib/test/unit/testcase.rb2
-rw-r--r--test/lib/tracepointchecker.rb17
-rw-r--r--test/lib/with_different_ofs.rb2
-rw-r--r--test/lib/zombie_hunter.rb3
-rw-r--r--test/logger/test_logdevice.rb200
-rw-r--r--test/logger/test_logger.rb8
-rw-r--r--test/matrix/test_matrix.rb180
-rw-r--r--test/matrix/test_vector.rb115
-rw-r--r--test/minitest/test_minitest_unit.rb9
-rw-r--r--test/mkmf/base.rb14
-rw-r--r--test/mkmf/test_framework.rb6
-rw-r--r--test/monitor/test_monitor.rb52
-rw-r--r--test/net/fixtures/cacert.pem24
-rw-r--r--test/net/fixtures/dhparams.pem29
-rw-r--r--test/net/fixtures/server.crt82
-rw-r--r--test/net/fixtures/server.key28
-rw-r--r--test/net/ftp/test_buffered_socket.rb6
-rw-r--r--test/net/ftp/test_ftp.rb1175
-rw-r--r--test/net/http/test_http.rb162
-rw-r--r--test/net/http/test_http_request.rb12
-rw-r--r--test/net/http/test_httpheader.rb34
-rw-r--r--test/net/http/test_httpresponse.rb58
-rw-r--r--test/net/http/test_https.rb104
-rw-r--r--test/net/http/utils.rb2
-rw-r--r--test/net/imap/cacert.pem66
-rw-r--r--test/net/imap/server.crt48
-rw-r--r--test/net/imap/server.key15
-rw-r--r--test/net/imap/test_imap.rb256
-rw-r--r--test/net/imap/test_imap_response_parser.rb34
-rw-r--r--test/net/pop/test_pop.rb31
-rw-r--r--test/net/protocol/test_protocol.rb103
-rw-r--r--test/net/smtp/test_response.rb2
-rw-r--r--test/net/smtp/test_smtp.rb35
-rw-r--r--test/net/smtp/test_ssl_socket.rb2
-rw-r--r--test/nkf/test_kconv.rb8
-rw-r--r--test/objspace/test_objspace.rb220
-rw-r--r--test/open-uri/test_open-uri.rb28
-rw-r--r--test/open-uri/test_ssl.rb380
-rw-r--r--test/openssl/fixtures/pkey/dh-1.pem13
-rw-r--r--test/openssl/fixtures/pkey/dh1024.pem5
-rw-r--r--test/openssl/fixtures/pkey/dsa1024.pem12
-rw-r--r--test/openssl/fixtures/pkey/dsa256.pem8
-rw-r--r--test/openssl/fixtures/pkey/dsa512.pem8
-rw-r--r--test/openssl/fixtures/pkey/p256.pem5
-rw-r--r--test/openssl/fixtures/pkey/rsa-1.pem51
-rw-r--r--test/openssl/fixtures/pkey/rsa-2.pem51
-rw-r--r--test/openssl/fixtures/pkey/rsa-3.pem51
-rw-r--r--test/openssl/fixtures/pkey/rsa1024.pem15
-rw-r--r--test/openssl/fixtures/pkey/rsa2048.pem27
-rw-r--r--test/openssl/test_asn1.rb816
-rw-r--r--test/openssl/test_bn.rb325
-rw-r--r--test/openssl/test_buffering.rb8
-rw-r--r--test/openssl/test_cipher.rb495
-rw-r--r--test/openssl/test_config.rb13
-rw-r--r--test/openssl/test_digest.rb104
-rw-r--r--test/openssl/test_engine.rb45
-rw-r--r--test/openssl/test_fips.rb19
-rw-r--r--test/openssl/test_hmac.rb50
-rw-r--r--test/openssl/test_kdf.rb183
-rw-r--r--test/openssl/test_ns_spki.rb7
-rw-r--r--test/openssl/test_ocsp.rb48
-rw-r--r--test/openssl/test_pair.rb280
-rw-r--r--test/openssl/test_pkcs12.rb54
-rw-r--r--test/openssl/test_pkcs5.rb98
-rw-r--r--test/openssl/test_pkcs7.rb25
-rw-r--r--test/openssl/test_pkey.rb49
-rw-r--r--test/openssl/test_pkey_dh.rb39
-rw-r--r--test/openssl/test_pkey_dsa.rb72
-rw-r--r--test/openssl/test_pkey_ec.rb134
-rw-r--r--test/openssl/test_pkey_rsa.rb143
-rw-r--r--test/openssl/test_random.rb6
-rw-r--r--test/openssl/test_ssl.rb954
-rw-r--r--test/openssl/test_ssl_session.rb436
-rw-r--r--test/openssl/test_x509attr.rb19
-rw-r--r--test/openssl/test_x509cert.rb147
-rw-r--r--test/openssl/test_x509crl.rb91
-rw-r--r--test/openssl/test_x509ext.rb14
-rw-r--r--test/openssl/test_x509name.rb141
-rw-r--r--test/openssl/test_x509req.rb37
-rw-r--r--test/openssl/test_x509store.rb79
-rw-r--r--test/openssl/ut_eof.rb4
-rw-r--r--test/openssl/utils.rb486
-rw-r--r--test/optparse/test_acceptable.rb15
-rw-r--r--test/optparse/test_autoconf.rb2
-rw-r--r--test/optparse/test_bash_completion.rb5
-rw-r--r--test/optparse/test_kwargs.rb2
-rw-r--r--test/optparse/test_optparse.rb2
-rw-r--r--test/optparse/test_summary.rb11
-rw-r--r--test/ostruct/test_ostruct.rb52
-rw-r--r--test/pathname/test_pathname.rb50
-rw-r--r--test/psych/handlers/test_recorder.rb2
-rw-r--r--test/psych/helper.rb20
-rw-r--r--test/psych/json/test_stream.rb2
-rw-r--r--test/psych/nodes/test_enumerable.rb2
-rw-r--r--test/psych/test_alias_and_anchor.rb2
-rw-r--r--test/psych/test_array.rb12
-rw-r--r--test/psych/test_boolean.rb2
-rw-r--r--test/psych/test_class.rb2
-rw-r--r--test/psych/test_coder.rb2
-rw-r--r--test/psych/test_date_time.rb35
-rw-r--r--test/psych/test_deprecated.rb128
-rw-r--r--test/psych/test_document.rb2
-rw-r--r--test/psych/test_emitter.rb4
-rw-r--r--test/psych/test_encoding.rb14
-rw-r--r--test/psych/test_exception.rb47
-rw-r--r--test/psych/test_hash.rb2
-rw-r--r--test/psych/test_json_tree.rb2
-rw-r--r--test/psych/test_marshalable.rb2
-rw-r--r--test/psych/test_merge_keys.rb2
-rw-r--r--test/psych/test_nil.rb6
-rw-r--r--test/psych/test_null.rb2
-rw-r--r--test/psych/test_numeric.rb2
-rw-r--r--test/psych/test_object.rb2
-rw-r--r--test/psych/test_object_references.rb2
-rw-r--r--test/psych/test_omap.rb2
-rw-r--r--test/psych/test_parser.rb64
-rw-r--r--test/psych/test_psych.rb172
-rw-r--r--test/psych/test_safe_load.rb93
-rw-r--r--test/psych/test_scalar.rb7
-rw-r--r--test/psych/test_scalar_scanner.rb14
-rw-r--r--test/psych/test_serialize_subclasses.rb2
-rw-r--r--test/psych/test_set.rb2
-rw-r--r--test/psych/test_stream.rb18
-rw-r--r--test/psych/test_string.rb34
-rw-r--r--test/psych/test_struct.rb2
-rw-r--r--test/psych/test_symbol.rb2
-rw-r--r--test/psych/test_tainted.rb4
-rw-r--r--test/psych/test_to_yaml_properties.rb64
-rw-r--r--test/psych/test_tree_builder.rb16
-rw-r--r--test/psych/test_yaml.rb30
-rw-r--r--test/psych/test_yaml_special_cases.rb130
-rw-r--r--test/psych/test_yamldbm.rb2
-rw-r--r--test/psych/test_yamlstore.rb2
-rw-r--r--test/psych/visitors/test_depth_first.rb2
-rw-r--r--test/psych/visitors/test_emitter.rb2
-rw-r--r--test/psych/visitors/test_to_ruby.rb2
-rw-r--r--test/psych/visitors/test_yaml_tree.rb29
-rw-r--r--test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Basics.text2
-rw-r--r--test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Syntax.text2
-rw-r--r--test/rdoc/minitest_helper.rb204
-rw-r--r--test/rdoc/test_rdoc_alias.rb2
-rw-r--r--test/rdoc/test_rdoc_any_method.rb66
-rw-r--r--test/rdoc/test_rdoc_attr.rb7
-rw-r--r--test/rdoc/test_rdoc_class_module.rb18
-rw-r--r--test/rdoc/test_rdoc_code_object.rb14
-rw-r--r--test/rdoc/test_rdoc_comment.rb14
-rw-r--r--test/rdoc/test_rdoc_constant.rb10
-rw-r--r--test/rdoc/test_rdoc_context.rb95
-rw-r--r--test/rdoc/test_rdoc_context_section.rb4
-rw-r--r--test/rdoc/test_rdoc_cross_reference.rb34
-rw-r--r--test/rdoc/test_rdoc_encoding.rb104
-rw-r--r--test/rdoc/test_rdoc_extend.rb2
-rw-r--r--test/rdoc/test_rdoc_generator_darkfish.rb31
-rw-r--r--test/rdoc/test_rdoc_generator_json_index.rb37
-rw-r--r--test/rdoc/test_rdoc_generator_markup.rb4
-rw-r--r--test/rdoc/test_rdoc_generator_pot.rb4
-rw-r--r--test/rdoc/test_rdoc_generator_pot_po.rb4
-rw-r--r--test/rdoc/test_rdoc_generator_pot_po_entry.rb4
-rw-r--r--test/rdoc/test_rdoc_generator_ri.rb4
-rw-r--r--test/rdoc/test_rdoc_i18n_locale.rb4
-rw-r--r--test/rdoc/test_rdoc_i18n_text.rb6
-rw-r--r--test/rdoc/test_rdoc_include.rb2
-rw-r--r--test/rdoc/test_rdoc_markdown.rb43
-rw-r--r--test/rdoc/test_rdoc_markdown_test.rb8
-rw-r--r--test/rdoc/test_rdoc_markup.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_attribute_manager.rb46
-rw-r--r--test/rdoc/test_rdoc_markup_attributes.rb12
-rw-r--r--test/rdoc/test_rdoc_markup_document.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_formatter.rb61
-rw-r--r--test/rdoc/test_rdoc_markup_hard_break.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_heading.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_include.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_indented_paragraph.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_paragraph.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_parser.rb41
-rw-r--r--test/rdoc/test_rdoc_markup_pre_process.rb20
-rw-r--r--test/rdoc/test_rdoc_markup_raw.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_to_ansi.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_to_bs.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_to_html.rb164
-rw-r--r--test/rdoc/test_rdoc_markup_to_html_crossref.rb95
-rw-r--r--test/rdoc/test_rdoc_markup_to_html_snippet.rb24
-rw-r--r--test/rdoc/test_rdoc_markup_to_joined_paragraph.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_to_label.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_to_markdown.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_to_rdoc.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_to_table_of_contents.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_to_tt_only.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_verbatim.rb4
-rw-r--r--test/rdoc/test_rdoc_method_attr.rb2
-rw-r--r--test/rdoc/test_rdoc_normal_class.rb2
-rw-r--r--test/rdoc/test_rdoc_normal_module.rb2
-rw-r--r--test/rdoc/test_rdoc_options.rb5
-rw-r--r--test/rdoc/test_rdoc_parser.rb22
-rw-r--r--test/rdoc/test_rdoc_parser_c.rb56
-rw-r--r--test/rdoc/test_rdoc_parser_changelog.rb8
-rw-r--r--test/rdoc/test_rdoc_parser_markdown.rb4
-rw-r--r--test/rdoc/test_rdoc_parser_rd.rb4
-rw-r--r--test/rdoc/test_rdoc_parser_ruby.rb1214
-rw-r--r--test/rdoc/test_rdoc_parser_simple.rb4
-rw-r--r--test/rdoc/test_rdoc_rd.rb4
-rw-r--r--test/rdoc/test_rdoc_rd_block_parser.rb4
-rw-r--r--test/rdoc/test_rdoc_rd_inline.rb4
-rw-r--r--test/rdoc/test_rdoc_rd_inline_parser.rb4
-rw-r--r--test/rdoc/test_rdoc_rdoc.rb144
-rw-r--r--test/rdoc/test_rdoc_require.rb8
-rw-r--r--test/rdoc/test_rdoc_ri_driver.rb127
-rw-r--r--test/rdoc/test_rdoc_ri_paths.rb6
-rw-r--r--test/rdoc/test_rdoc_ruby_lex.rb422
-rw-r--r--test/rdoc/test_rdoc_ruby_token.rb20
-rw-r--r--test/rdoc/test_rdoc_rubygems_hook.rb6
-rw-r--r--test/rdoc/test_rdoc_servlet.rb11
-rw-r--r--test/rdoc/test_rdoc_single_class.rb4
-rw-r--r--test/rdoc/test_rdoc_stats.rb4
-rw-r--r--test/rdoc/test_rdoc_store.rb45
-rw-r--r--test/rdoc/test_rdoc_task.rb4
-rw-r--r--test/rdoc/test_rdoc_text.rb47
-rw-r--r--test/rdoc/test_rdoc_token_stream.rb41
-rw-r--r--test/rdoc/test_rdoc_tom_doc.rb68
-rw-r--r--test/rdoc/test_rdoc_top_level.rb4
-rw-r--r--test/rdoc/xref_data.rb60
-rw-r--r--test/rdoc/xref_test_case.rb23
-rw-r--r--test/readline/test_readline.rb91
-rw-r--r--test/resolv/test_addr.rb11
-rw-r--r--test/resolv/test_dns.rb163
-rw-r--r--test/resolv/test_mdns.rb27
-rw-r--r--test/rexml/data/t75.xml2
-rw-r--r--test/rexml/formatter/test_default.rb19
-rw-r--r--test/rexml/parse/test_document_type_declaration.rb193
-rw-r--r--test/rexml/parse/test_element.rb64
-rw-r--r--test/rexml/parse/test_notation_declaration.rb181
-rw-r--r--test/rexml/parse/test_processing_instruction.rb44
-rw-r--r--test/rexml/parser/test_stream.rb32
-rw-r--r--test/rexml/parser/test_tree.rb2
-rw-r--r--test/rexml/parser/test_ultra_light.rb3
-rw-r--r--test/rexml/rexml_test_utils.rb5
-rw-r--r--test/rexml/test_attribute.rb14
-rw-r--r--test/rexml/test_core.rb37
-rw-r--r--test/rexml/test_doctype.rb124
-rw-r--r--test/rexml/test_element.rb18
-rw-r--r--test/rexml/test_functions.rb13
-rw-r--r--test/rexml/test_instruction.rb14
-rw-r--r--test/rexml/test_jaxen.rb189
-rw-r--r--test/rexml/test_martin_fowler.rb2
-rw-r--r--test/rexml/test_stream.rb4
-rw-r--r--test/rexml/test_text.rb55
-rw-r--r--test/rexml/test_xml_declaration.rb12
-rw-r--r--test/rexml/xpath/test_base.rb121
-rw-r--r--test/rinda/test_rinda.rb160
-rw-r--r--test/ripper/dummyparser.rb67
-rw-r--r--test/ripper/test_files.rb2
-rw-r--r--test/ripper/test_filter.rb13
-rw-r--r--test/ripper/test_lexer.rb98
-rw-r--r--test/ripper/test_parser_events.rb223
-rw-r--r--test/ripper/test_ripper.rb36
-rw-r--r--test/ripper/test_scanner_events.rb141
-rw-r--r--test/ripper/test_sexp.rb60
-rw-r--r--test/rss/test_itunes.rb6
-rw-r--r--test/rss/test_maker_itunes.rb14
-rw-r--r--test/rss/test_parser.rb56
-rw-r--r--test/rss/test_to_s.rb24
-rw-r--r--test/ruby/bug-13526.rb22
-rw-r--r--test/ruby/enc/test_case_comprehensive.rb25
-rw-r--r--test/ruby/enc/test_case_mapping.rb48
-rw-r--r--test/ruby/enc/test_case_options.rb12
-rw-r--r--test/ruby/enc/test_emoji_breaks.rb119
-rw-r--r--test/ruby/enc/test_grapheme_breaks.rb92
-rw-r--r--test/ruby/enc/test_regex_casefold.rb4
-rw-r--r--test/ruby/enc/test_utf16.rb112
-rw-r--r--test/ruby/enc/test_utf32.rb68
-rw-r--r--test/ruby/lbtest.rb1
-rw-r--r--test/ruby/marshaltestlib.rb4
-rw-r--r--test/ruby/test_alias.rb8
-rw-r--r--test/ruby/test_argf.rb793
-rw-r--r--test/ruby/test_arithmetic_sequence.rb464
-rw-r--r--test/ruby/test_array.rb474
-rw-r--r--test/ruby/test_assignment.rb17
-rw-r--r--test/ruby/test_ast.rb283
-rw-r--r--test/ruby/test_autoload.rb114
-rw-r--r--test/ruby/test_backtrace.rb33
-rw-r--r--test/ruby/test_basicinstructions.rb1
-rw-r--r--test/ruby/test_beginendblock.rb54
-rw-r--r--test/ruby/test_bignum.rb21
-rw-r--r--test/ruby/test_call.rb5
-rw-r--r--test/ruby/test_class.rb93
-rw-r--r--test/ruby/test_clone.rb35
-rw-r--r--test/ruby/test_complex.rb192
-rw-r--r--test/ruby/test_complexrational.rb16
-rw-r--r--test/ruby/test_const.rb4
-rw-r--r--test/ruby/test_continuation.rb26
-rw-r--r--test/ruby/test_default_gems.rb15
-rw-r--r--test/ruby/test_defined.rb54
-rw-r--r--test/ruby/test_dir.rb127
-rw-r--r--test/ruby/test_dir_m17n.rb20
-rw-r--r--test/ruby/test_encoding.rb5
-rw-r--r--test/ruby/test_enum.rb241
-rw-r--r--test/ruby/test_enumerator.rb161
-rw-r--r--test/ruby/test_env.rb70
-rw-r--r--test/ruby/test_eval.rb78
-rw-r--r--test/ruby/test_exception.rb467
-rw-r--r--test/ruby/test_extlibs.rb85
-rw-r--r--test/ruby/test_fiber.rb101
-rw-r--r--test/ruby/test_file.rb57
-rw-r--r--test/ruby/test_file_exhaustive.rb153
-rw-r--r--test/ruby/test_flip.rb35
-rw-r--r--test/ruby/test_float.rb129
-rw-r--r--test/ruby/test_fnmatch.rb37
-rw-r--r--test/ruby/test_gc.rb51
-rw-r--r--test/ruby/test_hash.rb348
-rw-r--r--test/ruby/test_ifunless.rb2
-rw-r--r--test/ruby/test_integer.rb190
-rw-r--r--test/ruby/test_integer_comb.rb24
-rw-r--r--test/ruby/test_io.rb518
-rw-r--r--test/ruby/test_io_m17n.rb72
-rw-r--r--test/ruby/test_iseq.rb270
-rw-r--r--test/ruby/test_iterator.rb14
-rw-r--r--test/ruby/test_jit.rb1047
-rw-r--r--test/ruby/test_key_error.rb42
-rw-r--r--test/ruby/test_keyword.rb162
-rw-r--r--test/ruby/test_lambda.rb52
-rw-r--r--test/ruby/test_lazy_enumerator.rb38
-rw-r--r--test/ruby/test_literal.rb109
-rw-r--r--test/ruby/test_m17n.rb44
-rw-r--r--test/ruby/test_marshal.rb70
-rw-r--r--test/ruby/test_math.rb1
-rw-r--r--test/ruby/test_method.rb233
-rw-r--r--test/ruby/test_mixed_unicode_escapes.rb8
-rw-r--r--test/ruby/test_module.rb272
-rw-r--r--test/ruby/test_not.rb2
-rw-r--r--test/ruby/test_notimp.rb21
-rw-r--r--test/ruby/test_numeric.rb66
-rw-r--r--test/ruby/test_object.rb63
-rw-r--r--test/ruby/test_optimization.rb404
-rw-r--r--test/ruby/test_pack.rb75
-rw-r--r--test/ruby/test_parse.rb298
-rw-r--r--test/ruby/test_pipe.rb19
-rw-r--r--test/ruby/test_proc.rb143
-rw-r--r--test/ruby/test_process.rb201
-rw-r--r--test/ruby/test_rand.rb41
-rw-r--r--test/ruby/test_range.rb254
-rw-r--r--test/ruby/test_rational.rb313
-rw-r--r--test/ruby/test_refinement.rb470
-rw-r--r--test/ruby/test_regexp.rb131
-rw-r--r--test/ruby/test_require.rb180
-rw-r--r--test/ruby/test_rubyoptions.rb257
-rw-r--r--test/ruby/test_rubyvm_mjit.rb75
-rw-r--r--test/ruby/test_settracefunc.rb567
-rw-r--r--test/ruby/test_signal.rb116
-rw-r--r--test/ruby/test_sprintf.rb99
-rw-r--r--test/ruby/test_string.rb859
-rw-r--r--test/ruby/test_struct.rb39
-rw-r--r--test/ruby/test_super.rb20
-rw-r--r--test/ruby/test_symbol.rb148
-rw-r--r--test/ruby/test_syntax.rb409
-rw-r--r--test/ruby/test_system.rb41
-rw-r--r--test/ruby/test_thread.rb414
-rw-r--r--test/ruby/test_thread_cv.rb245
-rw-r--r--test/ruby/test_thread_queue.rb619
-rw-r--r--test/ruby/test_threadgroup.rb1
-rw-r--r--test/ruby/test_time.rb101
-rw-r--r--test/ruby/test_time_tz.rb298
-rw-r--r--test/ruby/test_transcode.rb131
-rw-r--r--test/ruby/test_undef.rb2
-rw-r--r--test/ruby/test_unicode_escape.rb19
-rw-r--r--test/ruby/test_variable.rb8
-rw-r--r--test/ruby/test_vm_dump.rb21
-rw-r--r--test/ruby/test_weakmap.rb18
-rw-r--r--test/ruby/test_yield.rb2
-rw-r--r--test/rubygems/ca_cert.pem139
-rw-r--r--test/rubygems/client.pem148
-rw-r--r--test/rubygems/fix_openssl_warnings.rb13
-rw-r--r--test/rubygems/private3072_key.pem40
-rw-r--r--test/rubygems/public3072_cert.pem25
-rw-r--r--test/rubygems/rubygems_plugin.rb1
-rw-r--r--test/rubygems/simple_gem.rb2
-rw-r--r--test/rubygems/ssl_cert.pem95
-rw-r--r--test/rubygems/ssl_key.pem38
-rw-r--r--test/rubygems/test_bundled_ca.rb11
-rw-r--r--test/rubygems/test_config.rb11
-rw-r--r--test/rubygems/test_gem.rb499
-rw-r--r--test/rubygems/test_gem_bundler_version_finder.rb126
-rw-r--r--test/rubygems/test_gem_command.rb9
-rw-r--r--test/rubygems/test_gem_command_manager.rb15
-rw-r--r--test/rubygems/test_gem_commands_build_command.rb256
-rw-r--r--test/rubygems/test_gem_commands_cert_command.rb137
-rw-r--r--test/rubygems/test_gem_commands_check_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_cleanup_command.rb73
-rw-r--r--test/rubygems/test_gem_commands_contents_command.rb3
-rw-r--r--test/rubygems/test_gem_commands_dependency_command.rb67
-rw-r--r--test/rubygems/test_gem_commands_environment_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_fetch_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_generate_index_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_help_command.rb11
-rw-r--r--test/rubygems/test_gem_commands_info_command.rb44
-rw-r--r--test/rubygems/test_gem_commands_install_command.rb148
-rw-r--r--test/rubygems/test_gem_commands_lock_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_open_command.rb32
-rw-r--r--test/rubygems/test_gem_commands_outdated_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_owner_command.rb171
-rw-r--r--test/rubygems/test_gem_commands_pristine_command.rb97
-rw-r--r--test/rubygems/test_gem_commands_push_command.rb69
-rw-r--r--test/rubygems/test_gem_commands_query_command.rb333
-rw-r--r--test/rubygems/test_gem_commands_search_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_server_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_setup_command.rb193
-rw-r--r--test/rubygems/test_gem_commands_signin_command.rb98
-rw-r--r--test/rubygems/test_gem_commands_signout_command.rb37
-rw-r--r--test/rubygems/test_gem_commands_sources_command.rb53
-rw-r--r--test/rubygems/test_gem_commands_specification_command.rb5
-rw-r--r--test/rubygems/test_gem_commands_stale_command.rb5
-rw-r--r--test/rubygems/test_gem_commands_uninstall_command.rb106
-rw-r--r--test/rubygems/test_gem_commands_unpack_command.rb18
-rw-r--r--test/rubygems/test_gem_commands_update_command.rb29
-rw-r--r--test/rubygems/test_gem_commands_which_command.rb7
-rw-r--r--test/rubygems/test_gem_commands_yank_command.rb1
-rw-r--r--test/rubygems/test_gem_config_file.rb6
-rw-r--r--test/rubygems/test_gem_dependency.rb29
-rw-r--r--test/rubygems/test_gem_dependency_installer.rb15
-rw-r--r--test/rubygems/test_gem_dependency_list.rb13
-rw-r--r--test/rubygems/test_gem_dependency_resolution_error.rb1
-rw-r--r--test/rubygems/test_gem_doctor.rb7
-rw-r--r--test/rubygems/test_gem_ext_builder.rb49
-rw-r--r--test/rubygems/test_gem_ext_cmake_builder.rb9
-rw-r--r--test/rubygems/test_gem_ext_configure_builder.rb8
-rw-r--r--test/rubygems/test_gem_ext_ext_conf_builder.rb38
-rw-r--r--test/rubygems/test_gem_ext_rake_builder.rb66
-rw-r--r--test/rubygems/test_gem_gem_runner.rb1
-rw-r--r--test/rubygems/test_gem_gemcutter_utilities.rb46
-rw-r--r--test/rubygems/test_gem_impossible_dependencies_error.rb1
-rw-r--r--test/rubygems/test_gem_indexer.rb6
-rw-r--r--test/rubygems/test_gem_install_update_options.rb28
-rw-r--r--test/rubygems/test_gem_installer.rb363
-rw-r--r--test/rubygems/test_gem_local_remote_options.rb6
-rw-r--r--test/rubygems/test_gem_name_tuple.rb1
-rw-r--r--test/rubygems/test_gem_package.rb277
-rw-r--r--test/rubygems/test_gem_package_old.rb3
-rw-r--r--test/rubygems/test_gem_package_tar_header.rb22
-rw-r--r--test/rubygems/test_gem_package_tar_reader.rb1
-rw-r--r--test/rubygems/test_gem_package_tar_reader_entry.rb11
-rw-r--r--test/rubygems/test_gem_package_tar_writer.rb50
-rw-r--r--test/rubygems/test_gem_package_task.rb4
-rw-r--r--test/rubygems/test_gem_path_support.rb39
-rw-r--r--test/rubygems/test_gem_platform.rb9
-rw-r--r--test/rubygems/test_gem_rdoc.rb5
-rw-r--r--test/rubygems/test_gem_remote_fetcher.rb242
-rw-r--r--test/rubygems/test_gem_request.rb15
-rw-r--r--test/rubygems/test_gem_request_connection_pools.rb40
-rw-r--r--test/rubygems/test_gem_request_set.rb24
-rw-r--r--test/rubygems/test_gem_request_set_gem_dependency_api.rb15
-rw-r--r--test/rubygems/test_gem_request_set_lockfile.rb11
-rw-r--r--test/rubygems/test_gem_request_set_lockfile_parser.rb15
-rw-r--r--test/rubygems/test_gem_request_set_lockfile_tokenizer.rb4
-rw-r--r--test/rubygems/test_gem_requirement.rb40
-rw-r--r--test/rubygems/test_gem_resolver.rb52
-rw-r--r--test/rubygems/test_gem_resolver_activation_request.rb1
-rw-r--r--test/rubygems/test_gem_resolver_api_set.rb1
-rw-r--r--test/rubygems/test_gem_resolver_api_specification.rb25
-rw-r--r--test/rubygems/test_gem_resolver_best_set.rb1
-rw-r--r--test/rubygems/test_gem_resolver_composed_set.rb1
-rw-r--r--test/rubygems/test_gem_resolver_conflict.rb3
-rw-r--r--test/rubygems/test_gem_resolver_dependency_request.rb1
-rw-r--r--test/rubygems/test_gem_resolver_git_set.rb1
-rw-r--r--test/rubygems/test_gem_resolver_git_specification.rb3
-rw-r--r--test/rubygems/test_gem_resolver_index_set.rb1
-rw-r--r--test/rubygems/test_gem_resolver_index_specification.rb1
-rw-r--r--test/rubygems/test_gem_resolver_installed_specification.rb1
-rw-r--r--test/rubygems/test_gem_resolver_installer_set.rb7
-rw-r--r--test/rubygems/test_gem_resolver_local_specification.rb1
-rw-r--r--test/rubygems/test_gem_resolver_lock_set.rb1
-rw-r--r--test/rubygems/test_gem_resolver_lock_specification.rb1
-rw-r--r--test/rubygems/test_gem_resolver_requirement_list.rb1
-rw-r--r--test/rubygems/test_gem_resolver_specification.rb3
-rw-r--r--test/rubygems/test_gem_resolver_vendor_set.rb1
-rw-r--r--test/rubygems/test_gem_resolver_vendor_specification.rb1
-rw-r--r--test/rubygems/test_gem_security.rb9
-rw-r--r--test/rubygems/test_gem_security_policy.rb65
-rw-r--r--test/rubygems/test_gem_security_signer.rb19
-rw-r--r--test/rubygems/test_gem_security_trust_dir.rb7
-rw-r--r--test/rubygems/test_gem_server.rb222
-rw-r--r--test/rubygems/test_gem_source.rb28
-rw-r--r--test/rubygems/test_gem_source_fetch_problem.rb1
-rw-r--r--test/rubygems/test_gem_source_git.rb3
-rw-r--r--test/rubygems/test_gem_source_installed.rb1
-rw-r--r--test/rubygems/test_gem_source_lock.rb1
-rw-r--r--test/rubygems/test_gem_source_vendor.rb1
-rw-r--r--test/rubygems/test_gem_spec_fetcher.rb21
-rw-r--r--test/rubygems/test_gem_specification.rb755
-rw-r--r--test/rubygems/test_gem_stream_ui.rb47
-rw-r--r--test/rubygems/test_gem_stub_specification.rb91
-rw-r--r--test/rubygems/test_gem_text.rb20
-rw-r--r--test/rubygems/test_gem_uninstaller.rb22
-rw-r--r--test/rubygems/test_gem_unsatisfiable_dependency_error.rb1
-rw-r--r--test/rubygems/test_gem_uri_formatter.rb1
-rw-r--r--test/rubygems/test_gem_util.rb44
-rw-r--r--test/rubygems/test_gem_validator.rb1
-rw-r--r--test/rubygems/test_gem_version.rb103
-rw-r--r--test/rubygems/test_gem_version_option.rb16
-rw-r--r--test/rubygems/test_kernel.rb30
-rw-r--r--test/rubygems/test_remote_fetch_error.rb1
-rw-r--r--test/rubygems/test_require.rb219
-rw-r--r--test/runner.rb9
-rw-r--r--test/scanf/test_scanf.rb1
-rw-r--r--test/sdbm/test_sdbm.rb2
-rw-r--r--test/shell/test_command_processor.rb32
-rw-r--r--test/socket/test_addrinfo.rb8
-rw-r--r--test/socket/test_basicsocket.rb77
-rw-r--r--test/socket/test_socket.rb63
-rw-r--r--test/socket/test_tcp.rb9
-rw-r--r--test/socket/test_udp.rb15
-rw-r--r--test/socket/test_unix.rb11
-rw-r--r--test/stringio/test_stringio.rb87
-rw-r--r--test/strscan/test_stringscanner.rb90
-rw-r--r--test/test_abbrev.rb2
-rw-r--r--test/test_cmath.rb44
-rw-r--r--test/test_delegate.rb26
-rw-r--r--test/test_extlibs.rb83
-rw-r--r--test/test_find.rb23
-rw-r--r--test/test_forwardable.rb8
-rw-r--r--test/test_ipaddr.rb108
-rw-r--r--test/test_mathn.rb162
-rw-r--r--test/test_mutex_m.rb1
-rw-r--r--test/test_observer.rb2
-rw-r--r--test/test_open3.rb51
-rw-r--r--test/test_pp.rb9
-rw-r--r--test/test_prime.rb97
-rw-r--r--test/test_pstore.rb4
-rw-r--r--test/test_pty.rb7
-rw-r--r--test/test_rbconfig.rb9
-rw-r--r--test/test_securerandom.rb61
-rw-r--r--test/test_set.rb109
-rw-r--r--test/test_sync.rb69
-rw-r--r--test/test_syslog.rb7
-rw-r--r--test/test_tempfile.rb76
-rw-r--r--test/test_time.rb42
-rw-r--r--test/test_timeout.rb1
-rw-r--r--test/test_tmpdir.rb47
-rw-r--r--test/test_unicode_normalize.rb18
-rw-r--r--test/test_weakref.rb2
-rw-r--r--test/test_win32api.rb3
-rw-r--r--test/testunit/test_assertion.rb12
-rw-r--r--test/testunit/test_parallel.rb55
-rw-r--r--test/thread/test_cv.rb225
-rw-r--r--test/thread/test_queue.rb547
-rw-r--r--test/thread/test_sync.rb64
-rw-r--r--test/uri/test_file.rb67
-rw-r--r--test/uri/test_generic.rb78
-rw-r--r--test/uri/test_ldap.rb4
-rw-r--r--test/uri/test_mailto.rb18
-rw-r--r--test/uri/test_parser.rb8
-rw-r--r--test/webrick/test_config.rb17
-rw-r--r--test/webrick/test_filehandler.rb51
-rw-r--r--test/webrick/test_htgroup.rb19
-rw-r--r--test/webrick/test_httpauth.rb263
-rw-r--r--test/webrick/test_httpproxy.rb157
-rw-r--r--test/webrick/test_httprequest.rb17
-rw-r--r--test/webrick/test_httpresponse.rb108
-rw-r--r--test/webrick/test_https.rb112
-rw-r--r--test/webrick/test_httpserver.rb125
-rw-r--r--test/webrick/test_server.rb1
-rw-r--r--test/webrick/test_ssl_server.rb27
-rw-r--r--test/webrick/test_utils.rb8
-rw-r--r--test/webrick/utils.rb2
-rw-r--r--test/webrick/webrick.rhtml4
-rw-r--r--test/win32ole/available_ole.rb41
-rw-r--r--test/win32ole/test_ole_methods.rb2
-rw-r--r--test/win32ole/test_thread.rb2
-rw-r--r--test/win32ole/test_win32ole.rb25
-rw-r--r--test/win32ole/test_win32ole_event.rb43
-rw-r--r--test/win32ole/test_win32ole_method.rb13
-rw-r--r--test/win32ole/test_win32ole_method_event.rb36
-rw-r--r--test/win32ole/test_win32ole_param.rb53
-rw-r--r--test/win32ole/test_win32ole_param_event.rb30
-rw-r--r--test/win32ole/test_win32ole_record.rb18
-rw-r--r--test/win32ole/test_win32ole_type.rb52
-rw-r--r--test/win32ole/test_win32ole_type_event.rb44
-rw-r--r--test/win32ole/test_word.rb9
-rw-r--r--test/yaml/test_store.rb2
-rw-r--r--test/zlib/test_zlib.rb129
-rw-r--r--thread.c2247
-rw-r--r--thread_pthread.c1871
-rw-r--r--thread_pthread.h49
-rw-r--r--thread_sync.c848
-rw-r--r--thread_win32.c212
-rw-r--r--time.c1572
-rw-r--r--timev.h29
-rwxr-xr-xtool/bisect.sh29
-rwxr-xr-xtool/checksum.rb2
-rw-r--r--tool/colorize.rb41
-rwxr-xr-xtool/darwin-cc6
-rw-r--r--tool/downloader.rb222
-rw-r--r--tool/enc-emoji-citrus-gen.rb2
-rwxr-xr-xtool/enc-unicode.rb276
-rw-r--r--tool/eval.rb4
-rwxr-xr-xtool/expand-config.rb2
-rwxr-xr-xtool/extlibs.rb279
-rw-r--r--tool/fake.rb35
-rwxr-xr-xtool/fetch-bundled_gems.rb27
-rwxr-xr-xtool/file2lastrev.rb3
-rwxr-xr-xtool/gen_ruby_tapset.rb13
-rw-r--r--tool/generate-backport-changelog.rb99
-rw-r--r--tool/generic_erb.rb35
-rwxr-xr-xtool/git-refresh46
-rw-r--r--tool/gperf.sed22
-rwxr-xr-xtool/ifchange5
-rwxr-xr-xtool/insns2vm.rb17
-rw-r--r--tool/install-sh6
-rwxr-xr-xtool/instruction.rb1354
-rw-r--r--tool/m4/_colorize_result_prepare.m433
-rw-r--r--tool/m4/ac_msg_result.m45
-rw-r--r--tool/m4/colorize_result.m49
-rw-r--r--tool/m4/ruby_append_option.m45
-rw-r--r--tool/m4/ruby_append_options.m47
-rw-r--r--tool/m4/ruby_check_builtin_func.m410
-rw-r--r--tool/m4/ruby_check_builtin_setjmp.m427
-rw-r--r--tool/m4/ruby_check_printf_prefix.m430
-rw-r--r--tool/m4/ruby_check_setjmp.m417
-rw-r--r--tool/m4/ruby_check_signedness.m45
-rw-r--r--tool/m4/ruby_check_sizeof.m4108
-rw-r--r--tool/m4/ruby_check_sysconf.m413
-rw-r--r--tool/m4/ruby_cppoutfile.m418
-rw-r--r--tool/m4/ruby_decl_attribute.m445
-rw-r--r--tool/m4/ruby_default_arch.m411
-rw-r--r--tool/m4/ruby_define_if.m412
-rw-r--r--tool/m4/ruby_defint.m440
-rw-r--r--tool/m4/ruby_dtrace_available.m420
-rw-r--r--tool/m4/ruby_dtrace_postprocess.m430
-rw-r--r--tool/m4/ruby_func_attribute.m47
-rw-r--r--tool/m4/ruby_mingw32.m424
-rw-r--r--tool/m4/ruby_prepend_option.m45
-rw-r--r--tool/m4/ruby_prog_gnu_ld.m410
-rw-r--r--tool/m4/ruby_replace_type.m458
-rw-r--r--tool/m4/ruby_rm_recursive.m418
-rw-r--r--tool/m4/ruby_setjmp_type.m452
-rw-r--r--tool/m4/ruby_stack_grow_direction.m430
-rw-r--r--tool/m4/ruby_try_cflags.m412
-rw-r--r--tool/m4/ruby_try_ldflags.m415
-rw-r--r--tool/m4/ruby_type_attribute.m48
-rw-r--r--tool/m4/ruby_universal_arch.m490
-rw-r--r--tool/m4/ruby_werror_flag.m418
-rwxr-xr-xtool/make-snapshot197
-rwxr-xr-xtool/merger.rb50
-rw-r--r--tool/mjit_archflag.sh40
-rw-r--r--tool/mk_call_iseq_optimized.rb7
-rwxr-xr-xtool/mkconfig.rb45
-rwxr-xr-xtool/mkrunnable.rb6
-rwxr-xr-xtool/node_name.rb8
-rw-r--r--tool/prereq.status43
-rw-r--r--tool/pure_parser.rb20
-rwxr-xr-xtool/rbinstall.rb269
-rwxr-xr-xtool/redmine-backporter.rb16
-rw-r--r--tool/ruby_vm/controllers/application_controller.rb25
-rw-r--r--tool/ruby_vm/helpers/c_escape.rb128
-rw-r--r--tool/ruby_vm/helpers/dumper.rb112
-rw-r--r--tool/ruby_vm/helpers/scanner.rb52
-rw-r--r--tool/ruby_vm/loaders/insns_def.rb92
-rw-r--r--tool/ruby_vm/loaders/opt_insn_unif_def.rb34
-rw-r--r--tool/ruby_vm/loaders/opt_operand_def.rb56
-rw-r--r--tool/ruby_vm/loaders/vm_opts_h.rb37
-rw-r--r--tool/ruby_vm/models/attribute.rb54
-rwxr-xr-xtool/ruby_vm/models/bare_instructions.rb207
-rw-r--r--tool/ruby_vm/models/c_expr.rb41
-rw-r--r--tool/ruby_vm/models/instructions.rb22
-rw-r--r--tool/ruby_vm/models/instructions_unifications.rb43
-rw-r--r--tool/ruby_vm/models/operands_unifications.rb137
-rw-r--r--tool/ruby_vm/models/trace_instructions.rb71
-rw-r--r--tool/ruby_vm/models/typemap.rb62
-rw-r--r--tool/ruby_vm/scripts/converter.rb29
-rw-r--r--tool/ruby_vm/scripts/insns2vm.rb93
-rw-r--r--tool/ruby_vm/tests/.gitkeep0
-rw-r--r--tool/ruby_vm/views/_attributes.erb35
-rw-r--r--tool/ruby_vm/views/_c_expr.erb17
-rw-r--r--tool/ruby_vm/views/_copyright.erb31
-rw-r--r--tool/ruby_vm/views/_insn_entry.erb60
-rw-r--r--tool/ruby_vm/views/_insn_len_info.erb23
-rw-r--r--tool/ruby_vm/views/_insn_name_info.erb50
-rw-r--r--tool/ruby_vm/views/_insn_operand_info.erb59
-rw-r--r--tool/ruby_vm/views/_insn_stack_increase.erb52
-rw-r--r--tool/ruby_vm/views/_insn_type_chars.erb12
-rw-r--r--tool/ruby_vm/views/_leaf_helpers.erb105
-rw-r--r--tool/ruby_vm/views/_mjit_compile_insn.erb91
-rw-r--r--tool/ruby_vm/views/_mjit_compile_insn_body.erb105
-rw-r--r--tool/ruby_vm/views/_mjit_compile_ivar.erb54
-rw-r--r--tool/ruby_vm/views/_mjit_compile_pc_and_sp.erb36
-rw-r--r--tool/ruby_vm/views/_mjit_compile_send.erb94
-rw-r--r--tool/ruby_vm/views/_notice.erb22
-rw-r--r--tool/ruby_vm/views/_trace_instruction.erb16
-rw-r--r--tool/ruby_vm/views/insns.inc.erb26
-rw-r--r--tool/ruby_vm/views/insns_info.inc.erb20
-rw-r--r--tool/ruby_vm/views/mjit_compile.inc.erb77
-rw-r--r--tool/ruby_vm/views/opt_sc.inc.erb40
-rw-r--r--tool/ruby_vm/views/optinsn.inc.erb71
-rw-r--r--tool/ruby_vm/views/optunifs.inc.erb21
-rw-r--r--tool/ruby_vm/views/vm.inc.erb30
-rw-r--r--tool/ruby_vm/views/vmtc.inc.erb21
-rw-r--r--tool/run-gcov.rb54
-rw-r--r--tool/run-lcov.rb164
-rwxr-xr-xtool/runruby.rb100
-rw-r--r--tool/sync_default_gems.rb258
-rw-r--r--tool/test-coverage.rb118
-rw-r--r--tool/transcode-tblgen.rb24
-rw-r--r--tool/transform_mjit_header.rb298
-rwxr-xr-xtool/update-deps15
-rw-r--r--tool/vcs.rb147
-rwxr-xr-xtool/ytab.sed61
-rw-r--r--transcode.c38
-rw-r--r--transient_heap.c872
-rw-r--r--transient_heap.h60
-rw-r--r--util.c84
-rw-r--r--variable.c806
-rw-r--r--version.c40
-rw-r--r--version.h16
-rw-r--r--vm.c1467
-rw-r--r--vm_args.c325
-rw-r--r--vm_backtrace.c186
-rw-r--r--vm_core.h834
-rw-r--r--vm_dump.c289
-rw-r--r--vm_eval.c938
-rw-r--r--vm_exec.c27
-rw-r--r--vm_exec.h46
-rw-r--r--vm_insnhelper.c2227
-rw-r--r--vm_insnhelper.h169
-rw-r--r--vm_method.c427
-rw-r--r--vm_opts.h21
-rw-r--r--vm_trace.c940
-rw-r--r--vsnprintf.c14
-rw-r--r--wercker.yml282
-rw-r--r--win32/.document1
-rw-r--r--win32/Makefile.sub368
-rw-r--r--win32/README.win329
-rwxr-xr-xwin32/configure.bat40
-rw-r--r--win32/dir.h1
-rw-r--r--win32/file.c101
-rw-r--r--win32/file.h1
-rwxr-xr-xwin32/ifchange.bat13
-rwxr-xr-xwin32/mkexports.rb12
-rwxr-xr-x[-rw-r--r--]win32/rtname.cmd0
-rw-r--r--win32/setup.mak111
-rw-r--r--win32/win32.c1007
7880 files changed, 540223 insertions, 121290 deletions
diff --git a/.document b/.document
index ac7c493840..2116ca6d26 100644
--- a/.document
+++ b/.document
@@ -5,6 +5,7 @@
# Process all the C source files
*.c
+*.y
# prelude
prelude.rb
@@ -18,11 +19,18 @@ lib
ext
# rdoc files
-ChangeLog
-
NEWS
README.md
README.ja.md
+COPYING
+COPYING.ja
+CONTRIBUTING.md
+
+LEGAL
+
+# win32/README.win32 linked from README.md
+win32
+
doc
diff --git a/.editorconfig b/.editorconfig
index f404977180..cb2729fff9 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -2,19 +2,26 @@ root = true
[*]
end_of_line = lf
+indent_size = 4
+indent_style = space
insert_final_newline = true
-trim_trailing_whitespace = true
tab_width = 8
-indent_style = tab
-indent_size = 4
+trim_trailing_whitespace = true
-[**.bat]
+[*.bat]
end_of_line = crlf
-[**.rb]
-indent_style = space
+[*.gemspec]
+indent_size = 2
+
+[*.rb]
indent_size = 2
[*.yml]
indent_size = 2
-indent_style = space
+
+[{*[Mm]akefile*,*.mak,*.mk,depend}]
+indent_style = tab
+
+[reg*.[ch]]
+indent_size = 2
diff --git a/.gdbinit b/.gdbinit
index 11611e8c24..1806196a42 100644
--- a/.gdbinit
+++ b/.gdbinit
@@ -1,3 +1,5 @@
+set startup-with-shell off
+
define hook-run
set $color_type = 0
set $color_highlite = 0
@@ -68,7 +70,9 @@ define rp
print/x *((VALUE*)((struct RObject*)($arg0))->as.ary) @ (ROBJECT_EMBED_LEN_MAX+0)
else
print (((struct RObject *)($arg0))->as.heap)
- print/x *(((struct RObject*)($arg0))->as.heap.ivptr) @ (((struct RObject*)($arg0))->as.heap.numiv)
+ if (((struct RObject*)($arg0))->as.heap.numiv) > 0
+ print/x *(((struct RObject*)($arg0))->as.heap.ivptr) @ (((struct RObject*)($arg0))->as.heap.numiv)
+ end
end
else
if ($flags & RUBY_T_MASK) == RUBY_T_CLASS
@@ -95,14 +99,15 @@ define rp
set $regsrc = ((struct RRegexp*)($arg0))->src
set $rsflags = ((struct RBasic*)$regsrc)->flags
printf "%sT_REGEXP%s: ", $color_type, $color_end
+ set $len = ($rsflags & RUBY_FL_USER1) ? \
+ ((struct RString*)$regsrc)->as.heap.len : \
+ (($rsflags & (RUBY_FL_USER2|RUBY_FL_USER3|RUBY_FL_USER4|RUBY_FL_USER5|RUBY_FL_USER6)) >> RUBY_FL_USHIFT+2)
set print address off
- output (char *)(($rsflags & RUBY_FL_USER1) ? \
+ output *(char *)(($rsflags & RUBY_FL_USER1) ? \
((struct RString*)$regsrc)->as.heap.ptr : \
- ((struct RString*)$regsrc)->as.ary)
+ ((struct RString*)$regsrc)->as.ary) @ $len
set print address on
- printf " len:%ld ", ($rsflags & RUBY_FL_USER1) ? \
- ((struct RString*)$regsrc)->as.heap.len : \
- (($rsflags & (RUBY_FL_USER2|RUBY_FL_USER3|RUBY_FL_USER4|RUBY_FL_USER5|RUBY_FL_USER6)) >> RUBY_FL_USHIFT+2)
+ printf " len:%ld ", $len
if $flags & RUBY_FL_USER6
printf "(none) "
end
@@ -123,7 +128,7 @@ define rp
if ($len == 0)
printf "{(empty)} "
else
- output/x *((VALUE*)((struct RArray*)($arg0))->as.ary) @ $len
+ print/x *((VALUE*)((struct RArray*)($arg0))->as.ary) @ $len
printf " "
end
else
@@ -139,7 +144,7 @@ define rp
if ($len == 0)
printf "{(empty)} "
else
- output/x *((VALUE*)((struct RArray*)($arg0))->as.heap.ptr) @ $len
+ print/x *((VALUE*)((struct RArray*)($arg0))->as.heap.ptr) @ $len
printf " "
end
end
@@ -151,34 +156,27 @@ define rp
else
if ($flags & RUBY_T_MASK) == RUBY_T_HASH
printf "%sT_HASH%s: ", $color_type, $color_end,
- if ((struct RHash *)($arg0))->ntbl
- printf "len=%ld ", ((struct RHash *)($arg0))->ntbl->num_entries
+ if (((struct RHash *)($arg0))->basic->flags & RHASH_ST_TABLE_FLAG)
+ printf "st len=%ld ", ((struct RHash *)($arg0))->as.st->num_entries
+ else
+ printf "li len=%ld bound=%ld ", \
+ ((((struct RHash *)($arg0))->basic->flags & RHASH_ARRAY_LEN_MASK) >> RHASH_ARRAY_LEN_SHIFT), \
+ ((((struct RHash *)($arg0))->basic->flags & RHASH_ARRAY_BOUND_MASK) >> RHASH_ARRAY_BOUND_SHIFT)
end
print (struct RHash *)($arg0)
else
if ($flags & RUBY_T_MASK) == RUBY_T_STRUCT
- printf "%sT_STRUCT%s: len=%ld ", $color_type, $color_end, \
- (($flags & (RUBY_FL_USER1|RUBY_FL_USER2)) ? \
+ set $len = (($flags & (RUBY_FL_USER1|RUBY_FL_USER2)) ? \
($flags & (RUBY_FL_USER1|RUBY_FL_USER2)) >> (RUBY_FL_USHIFT+1) : \
((struct RStruct *)($arg0))->as.heap.len)
+ printf "%sT_STRUCT%s: len=%ld ", $color_type, $color_end, $len
print (struct RStruct *)($arg0)
- x/xw (($flags & (RUBY_FL_USER1|RUBY_FL_USER2)) ? \
- ((struct RStruct *)($arg0))->as.ary : \
- ((struct RStruct *)($arg0))->as.heap.ptr)
+ output/x *(($flags & (RUBY_FL_USER1|RUBY_FL_USER2)) ? \
+ ((struct RStruct *)($arg0))->as.ary : \
+ ((struct RStruct *)($arg0))->as.heap.ptr) @ $len
else
if ($flags & RUBY_T_MASK) == RUBY_T_BIGNUM
- printf "%sT_BIGNUM%s: sign=%d len=%ld ", $color_type, $color_end, \
- (($flags & RUBY_FL_USER1) != 0), \
- (($flags & RUBY_FL_USER2) ? \
- ($flags & (RUBY_FL_USER5|RUBY_FL_USER4|RUBY_FL_USER3)) >> (RUBY_FL_USHIFT+3) : \
- ((struct RBignum*)($arg0))->as.heap.len)
- if $flags & RUBY_FL_USER2
- printf "(embed) "
- end
- print (struct RBignum *)($arg0)
- x/xw (($flags & RUBY_FL_USER2) ? \
- ((struct RBignum*)($arg0))->as.ary : \
- ((struct RBignum*)($arg0))->as.heap.digits)
+ rp_bignum $arg0
else
if ($flags & RUBY_T_MASK) == RUBY_T_RATIONAL
printf "%sT_RATIONAL%s: ", $color_type, $color_end
@@ -253,7 +251,7 @@ define rp
else
if ($flags & RUBY_T_MASK) == RUBY_T_IMEMO
printf "%sT_IMEMO%s(", $color_type, $color_end
- output (enum imemo_type)(($flags>>RUBY_FL_USHIFT)&imemo_mask)
+ output (enum imemo_type)(($flags>>RUBY_FL_USHIFT)&RUBY_IMEMO_MASK)
printf "): "
rp_imemo $arg0
else
@@ -333,6 +331,9 @@ define rp_id
if $id == idLTLT
printf "(:<<)\n"
else
+ if $id == idGTGT
+ printf "(:>>)\n"
+ else
if $id == idLE
printf "(:<=)\n"
else
@@ -360,6 +361,18 @@ define rp_id
if $id == idASET
printf "(:[]=)\n"
else
+ if $id == idCOLON2
+ printf "(:'::')\n"
+ else
+ if $id == idANDOP
+ printf "(:&&)\n"
+ else
+ if $id == idOROP
+ printf "(:||)\n"
+ else
+ if $id == idANDDOT
+ printf "(:&.)\n"
+ else
if $id <= tLAST_OP_ID
printf "O"
else
@@ -391,12 +404,13 @@ define rp_id
end
end
printf "(%ld): ", $id
- set $str = lookup_id_str($id)
- if $str
- rp_string $str
- else
- echo undef\n
- end
+ print_id $id
+ echo \n
+ end
+ end
+ end
+ end
+ end
end
end
end
@@ -421,21 +435,33 @@ end
define output_string
set $flags = ((struct RBasic*)($arg0))->flags
- printf "%s", (char *)(($flags & RUBY_FL_USER1) ? \
+ set $len = ($flags & RUBY_FL_USER1) ? \
+ ((struct RString*)($arg0))->as.heap.len : \
+ (($flags & (RUBY_FL_USER2|RUBY_FL_USER3|RUBY_FL_USER4|RUBY_FL_USER5|RUBY_FL_USER6)) >> RUBY_FL_USHIFT+2)
+ if $len > 0
+ output *(char *)(($flags & RUBY_FL_USER1) ? \
((struct RString*)($arg0))->as.heap.ptr : \
- ((struct RString*)($arg0))->as.ary)
+ ((struct RString*)($arg0))->as.ary) @ $len
+ else
+ output ""
+ end
end
-define rp_string
+define print_string
set $flags = ((struct RBasic*)($arg0))->flags
- set print address off
- output (char *)(($flags & RUBY_FL_USER1) ? \
- ((struct RString*)($arg0))->as.heap.ptr : \
- ((struct RString*)($arg0))->as.ary)
- set print address on
- printf " bytesize:%ld ", ($flags & RUBY_FL_USER1) ? \
+ set $len = ($flags & RUBY_FL_USER1) ? \
((struct RString*)($arg0))->as.heap.len : \
(($flags & (RUBY_FL_USER2|RUBY_FL_USER3|RUBY_FL_USER4|RUBY_FL_USER5|RUBY_FL_USER6)) >> RUBY_FL_USHIFT+2)
+ if $len > 0
+ printf "%s", *(char *)(($flags & RUBY_FL_USER1) ? \
+ ((struct RString*)($arg0))->as.heap.ptr : \
+ ((struct RString*)($arg0))->as.ary) @ $len
+ end
+end
+
+define rp_string
+ output_string $arg0
+ printf " bytesize:%ld ", $len
if !($flags & RUBY_FL_USER1)
printf "(embed) "
else
@@ -466,6 +492,51 @@ document rp_string
Print the content of a String.
end
+define rp_bignum
+ set $flags = ((struct RBignum*)($arg0))->basic.flags
+ set $len = (($flags & RUBY_FL_USER2) ? \
+ ($flags & (RUBY_FL_USER5|RUBY_FL_USER4|RUBY_FL_USER3)) >> (RUBY_FL_USHIFT+3) : \
+ ((struct RBignum*)($arg0))->as.heap.len)
+ printf "%sT_BIGNUM%s: sign=%d len=%ld ", $color_type, $color_end, \
+ (($flags & RUBY_FL_USER1) != 0), $len
+ if $flags & RUBY_FL_USER2
+ printf "(embed) "
+ end
+ print (struct RBignum *)($arg0)
+ set $ptr = (($flags & RUBY_FL_USER2) ? \
+ ((struct RBignum*)($arg0))->as.ary : \
+ ((struct RBignum*)($arg0))->as.heap.digits)
+ set $len = $len-1
+ printf "0x%x", $ptr[$len]
+ while $len > 0
+ set $len = $len-1
+ set $val = $ptr[$len]
+ set $w = sizeof($ptr[0])
+ printf "_"
+ if $w > 8
+ printf "%.32x", $val
+ else
+ if $w > 4
+ printf "%.16x", $val
+ else
+ if $w > 2
+ printf "%.8x", $val
+ else
+ if $w > 1
+ printf "%.4x", $val
+ else
+ printf "%.2x", $val
+ end
+ end
+ end
+ end
+ end
+ printf "\n"
+end
+document rp_bignum
+ Print the content of a Bignum.
+end
+
define rp_class
printf "(struct RClass *) %p", (void*)$arg0
if ((struct RClass *)($arg0))->ptr.origin_ != $arg0
@@ -481,7 +552,7 @@ document rp_class
end
define rp_imemo
- set $flags = (enum imemo_type)((((struct RBasic *)($arg0))->flags >> RUBY_FL_USHIFT) & imemo_mask)
+ set $flags = (enum imemo_type)((((struct RBasic *)($arg0))->flags >> RUBY_FL_USHIFT) & RUBY_IMEMO_MASK)
if $flags == imemo_cref
printf "(rb_cref_t *) %p\n", (void*)$arg0
print *(rb_cref_t *)$arg0
@@ -633,12 +704,6 @@ define nd_cval
rp ($arg0).u3.value
end
-
-define nd_cnt
- printf "%su3.cnt%s: ", $color_highlite, $color_end
- p ($arg0).u3.cnt
-end
-
define nd_tbl
printf "%su1.tbl%s: ", $color_highlite, $color_end
p ($arg0).u1.tbl
@@ -677,12 +742,6 @@ define nd_lit
rp ($arg0).u1.value
end
-
-define nd_frml
- printf "%su1.node%s: ", $color_highlite, $color_end
- rp ($arg0).u1.node
-end
-
define nd_rest
printf "%su2.argc%s: ", $color_highlite, $color_end
p ($arg0).u2.argc
@@ -709,12 +768,6 @@ define nd_args
rp ($arg0).u3.node
end
-
-define nd_noex
- printf "%su1.id%s: ", $color_highlite, $color_end
- p ($arg0).u1.id
-end
-
define nd_defn
printf "%su3.node%s: ", $color_highlite, $color_end
rp ($arg0).u3.node
@@ -732,17 +785,6 @@ define nd_new
end
-define nd_cfnc
- printf "%su1.cfunc%s: ", $color_highlite, $color_end
- p ($arg0).u1.cfunc
-end
-
-define nd_argc
- printf "%su2.argc%s: ", $color_highlite, $color_end
- p ($arg0).u2.argc
-end
-
-
define nd_cname
printf "%su1.id%s: ", $color_highlite, $color_end
p ($arg0).u1.id
@@ -804,7 +846,7 @@ end
define nd_tree
set $buf = (struct RString *)rb_str_buf_new(0)
- call dump_node((VALUE)($buf), rb_str_new(0, 0), 0, ($arg0))
+ call dump_node((VALUE)($buf), rb_str_tmp_new(0), 0, ($arg0))
printf "%s\n", $buf->as.heap.ptr
end
@@ -875,8 +917,7 @@ end
define rb_classname
# up to 128bit int
- set $rb_classname_permanent = "0123456789ABCDEF"
- set $rb_classname = classname($arg0, $rb_classname_permanent)
+ set $rb_classname = rb_mod_name($arg0)
if $rb_classname != RUBY_Qnil
rp $rb_classname
else
@@ -925,7 +966,7 @@ define iseq
end
define rb_ps
- rb_ps_vm ruby_current_vm
+ rb_ps_vm ruby_current_vm_ptr
end
document rb_ps
Dump all threads and their callstacks
@@ -957,45 +998,74 @@ define print_lineno
set $pos = $pos - 1
end
- set $i = 0
- set $size = $iseq->body->line_info_size
- set $table = $iseq->body->line_info_table
+ set $index = 0
+ set $size = $iseq->body->insns_info.size
+ set $table = $iseq->body->insns_info.body
+ set $positions = $iseq->body->insns_info.positions
#printf "size: %d\n", $size
if $size == 0
else
- set $i = 1
- while $i < $size
- #printf "table[%d]: position: %d, line: %d, pos: %d\n", $i, $table[$i].position, $table[$i].line_no, $pos
- if $table[$i].position > $pos
- loop_break
+ if $size == 1
+ printf "%d", $table[0].line_no
+ else
+ if $positions
+ # get_insn_info_linear_search
+ set $index = 1
+ while $index < $size
+ #printf "table[%d]: position: %d, line: %d, pos: %d\n", $i, $positions[$i], $table[$i].line_no, $pos
+ if $positions[$index] > $pos
+ loop_break
+ end
+ set $index = $index + 1
+ if $positions[$index] == $pos
+ loop_break
+ end
end
- set $i = $i + 1
- if $table[$i].position == $pos
- loop_break
+ else
+ # get_insn_info_succinct_bitvector
+ set $sd = $iseq->body->insns_info.succ_index_table
+ set $immediate_table_size = sizeof($sd->imm_part) / sizeof(uint64_t) * 9
+ if $pos < $immediate_table_size
+ set $i = $pos / 9
+ set $j = $pos % 9
+ set $index = ((int)($sd->imm_part[$i] >> ($j * 7))) & 0x7f
+ else
+ set $block_index = ($pos - $immediate_table_size) / 512
+ set $block = &$sd->succ_part[$block_index]
+ set $block_bit_index = ($pos - $immediate_table_size) % 512
+ set $small_block_index = $block_bit_index / 64
+ set $small_block_popcount = $small_block_index == 0 ? 0 : (((int)($block->small_block_ranks >> (($small_block_index - 1) * 9))) & 0x1ff)
+ set $x = $block->bits[$small_block_index] << (63 - $block_bit_index % 64)
+ set $x = ($x & 0x5555555555555555) + ($x >> 1 & 0x5555555555555555)
+ set $x = ($x & 0x3333333333333333) + ($x >> 2 & 0x3333333333333333)
+ set $x = ($x & 0x0707070707070707) + ($x >> 4 & 0x0707070707070707)
+ set $x = ($x & 0x001f001f001f001f) + ($x >> 8 & 0x001f001f001f001f)
+ set $x = ($x & 0x0000003f0000003f) + ($x >>16 & 0x0000003f0000003f)
+ set $popcnt = ($x & 0x7f) + ($x >>32 & 0x7f)
+ set $index = $block->rank + $small_block_popcount + $popcnt
end
end
- printf "%d", $table[$i-1].line_no
+ printf "%d", $table[$index-1].line_no
+ end
end
end
define check_method_entry
- # get $immeo and $can_be_svar and return $me
set $imemo = (struct RBasic *)$arg0
- set $can_be_svar = $arg1
if $imemo != RUBY_Qfalse
set $type = ($imemo->flags >> 12) & 0x07
if $type == imemo_ment
set $me = (rb_callable_method_entry_t *)$imemo
else
if $type == imemo_svar
- set $imemo == ((struct vm_svar *)$imemo)->cref_or_me
- check_method_entry $imemo 0
+ set $imemo = ((struct vm_svar *)$imemo)->cref_or_me
+ check_method_entry $imemo
end
end
end
end
-define output_id
+define print_id
set $id = $arg0
# rb_id_to_serial
if $id > tLAST_OP_ID
@@ -1027,31 +1097,59 @@ define output_id
set $arylen = $ary->as.heap.len
end
set $result = $aryptr[($serial % ID_ENTRY_UNIT) * ID_ENTRY_SIZE + $t]
- output_string $result
+ if $result != RUBY_Qnil
+ print_string $result
+ else
+ echo undef
+ end
end
end
end
end
+define print_pathobj
+ set $flags = ((struct RBasic*)($arg0))->flags
+ if ($flags & RUBY_T_MASK) == RUBY_T_STRING
+ print_string $arg0
+ end
+ if ($flags & RUBY_T_MASK) == RUBY_T_ARRAY
+ if $flags & RUBY_FL_USER1
+ set $str = ((struct RArray*)($arg0))->as.ary[0]
+ else
+ set $str = ((struct RArray*)($arg0))->as.heap.ptr[0]
+ end
+ print_string $str
+ end
+end
+
define rb_ps_thread
set $ps_thread = (struct RTypedData*)$arg0
set $ps_thread_th = (rb_thread_t*)$ps_thread->data
printf "* #<Thread:%p rb_thread_t:%p native_thread:%p>\n", \
$ps_thread, $ps_thread_th, $ps_thread_th->thread_id
- set $cfp = $ps_thread_th->cfp
- set $cfpend = (rb_control_frame_t *)($ps_thread_th->stack + $ps_thread_th->stack_size)-1
+ set $cfp = $ps_thread_th->ec->cfp
+ set $cfpend = (rb_control_frame_t *)($ps_thread_th->ec->vm_stack + $ps_thread_th->ec->vm_stack_size)-1
while $cfp < $cfpend
if $cfp->iseq
+ if !((VALUE)$cfp->iseq & RUBY_IMMEDIATE_MASK) && (((imemo_ifunc << RUBY_FL_USHIFT) | RUBY_T_IMEMO)==$cfp->iseq->flags & ((RUBY_IMEMO_MASK << RUBY_FL_USHIFT) | RUBY_T_MASK))
+ printf "%d:ifunc ", $cfpend-$cfp
+ set print symbol-filename on
+ output/a $cfp->iseq.body
+ set print symbol-filename off
+ printf "\n"
+ else
if $cfp->pc
set $location = $cfp->iseq->body->location
- output_string $location.path
+ printf "%d:", $cfpend-$cfp
+ print_pathobj $location.pathobj
printf ":"
print_lineno $cfp
printf ":in `"
- output_string $location.label
+ print_string $location.label
printf "'\n"
else
- printf "???.rb:???:in `???'\n"
+ printf "%d: ???.rb:???:in `???'\n", $cfpend-$cfp
+ end
end
else
# if VM_FRAME_TYPE($cfp->flag) == VM_FRAME_MAGIC_CFUNC
@@ -1063,7 +1161,7 @@ define rb_ps_thread
set $env_specval = $ep[-1]
set $env_me_cref = $ep[-2]
while ($env_specval & 0x02) != 0
- check_method_entry $env_me_cref 0
+ check_method_entry $env_me_cref
if $me != 0
loop_break
end
@@ -1072,17 +1170,18 @@ define rb_ps_thread
set $env_me_cref = $ep[-2]
end
if $me == 0
- check_method_entry $env_me_cref 1
+ check_method_entry $env_me_cref
end
+ printf "%d:", $cfpend-$cfp
set print symbol-filename on
output/a $me->def->body.cfunc.func
set print symbol-filename off
set $mid = $me->def->original_id
printf ":in `"
- output_id $mid
+ print_id $mid
printf "'\n"
else
- printf "unknown_frame:???:in `???'\n"
+ printf "%d:unknown_frame:???:in `???'\n", $cfpend-$cfp
end
end
set $cfp = $cfp + 1
@@ -1090,7 +1189,7 @@ define rb_ps_thread
end
define rb_count_objects
- set $objspace = ruby_current_vm->objspace
+ set $objspace = ruby_current_vm_ptr->objspace
set $counts_00 = 0
set $counts_01 = 0
set $counts_02 = 0
@@ -1214,3 +1313,34 @@ define dump_node
((struct RString*)$str)->as.heap.ptr : \
((struct RString*)$str)->as.ary)
end
+
+define print_flags
+ printf "RUBY_FL_WB_PROTECTED: %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_WB_PROTECTED ? "1" : "0"
+ printf "RUBY_FL_PROMOTED0 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_PROMOTED0 ? "1" : "0"
+ printf "RUBY_FL_PROMOTED1 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_PROMOTED1 ? "1" : "0"
+ printf "RUBY_FL_FINALIZE : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_FINALIZE ? "1" : "0"
+ printf "RUBY_FL_TAINT : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_TAINT ? "1" : "0"
+ printf "RUBY_FL_UNTRUSTED : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_UNTRUSTED ? "1" : "0"
+ printf "RUBY_FL_EXIVAR : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_EXIVAR ? "1" : "0"
+ printf "RUBY_FL_FREEZE : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_FREEZE ? "1" : "0"
+
+ printf "RUBY_FL_USER0 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER0 ? "1" : "0"
+ printf "RUBY_FL_USER1 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER1 ? "1" : "0"
+ printf "RUBY_FL_USER2 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER2 ? "1" : "0"
+ printf "RUBY_FL_USER3 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER3 ? "1" : "0"
+ printf "RUBY_FL_USER4 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER4 ? "1" : "0"
+ printf "RUBY_FL_USER5 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER5 ? "1" : "0"
+ printf "RUBY_FL_USER6 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER6 ? "1" : "0"
+ printf "RUBY_FL_USER7 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER7 ? "1" : "0"
+ printf "RUBY_FL_USER8 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER8 ? "1" : "0"
+ printf "RUBY_FL_USER9 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER9 ? "1" : "0"
+ printf "RUBY_FL_USER10 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER10 ? "1" : "0"
+ printf "RUBY_FL_USER11 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER11 ? "1" : "0"
+ printf "RUBY_FL_USER12 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER12 ? "1" : "0"
+ printf "RUBY_FL_USER13 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER13 ? "1" : "0"
+ printf "RUBY_FL_USER14 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER14 ? "1" : "0"
+ printf "RUBY_FL_USER15 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER15 ? "1" : "0"
+ printf "RUBY_FL_USER16 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER16 ? "1" : "0"
+ printf "RUBY_FL_USER17 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER17 ? "1" : "0"
+ printf "RUBY_FL_USER18 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_USER18 ? "1" : "0"
+end
diff --git a/.gitattributes b/.gitattributes
index d9785fad00..6ca2f89462 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,5 +1,6 @@
*.gemspec diff=ruby
*.rb diff=ruby
+bin svn-properties=svn:ignore=ruby
bin/* diff=ruby
tool/update-deps diff=ruby
tool/make-snapshot diff=ruby
diff --git a/.gitignore b/.gitignore
index 165f313e84..50bf27f28a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
*.a
*.bak
*.bc
+*.bundle
*.dSYM
*.dmyh
*.dylib
@@ -13,11 +14,15 @@
*.log
*.o
*.obj
+*.old
*.orig
+*.pch
*.pdb
*.rej
*.s
*.sav
+*.sl
+*.so
*.swp
*.yarb
*~
@@ -25,17 +30,21 @@
.*.list
.*.time
.DS_Store
+.bundle
.ccmalloc
.ext
.pc
.ppack
.svn
Makefile
-Makefile.old
cygruby*.def
extconf.h
y.output
y.tab.c
+*.gcda
+*.gcno
+*.gcov
+lcov*.info
# /
/*-fake.rb
@@ -46,12 +55,8 @@ y.tab.c
/*.rc
/*_prelude.c
/COPYING.LIB
-/ChangeLog-*
-/ChangeLog.pre-alpha
-/ChangeLog.pre1_1
/Doxyfile
/GNUmakefile
-/GNUmakefile.old
/README.atheos
/README.fat-patch
/README.v6
@@ -59,6 +64,7 @@ y.tab.c
/archive
/autom4te*.cache
/automake
+/benchmark/benchmark-driver
/beos
/bmlog-*
/breakpoints.gdb
@@ -82,6 +88,9 @@ y.tab.c
/goruby
/id.[ch]
/largefile.h
+/lcov-c-out
+/lcov-rb-out
+/lcov-out
/lex.c
/libruby*.*
/miniprelude.c
@@ -97,6 +106,7 @@ y.tab.c
/ppack
/prelude.c
/preview
+/probes.dmyh
/probes.h
/rbconfig.rb
/rename2.h
@@ -108,8 +118,10 @@ y.tab.c
/ruby-runner
/ruby-runner.h
/ruby-man.rd.gz
+/run.gdb
/sizes.c
/test.rb
+/test-coverage.dat
/tmp
/transdb.h
/uncommon.mk
@@ -119,6 +131,7 @@ y.tab.c
/yasmdata.rb
# /benchmark/
+/benchmark/bm_require.data
/benchmark/bmx_*.rb
/benchmark/fasta.output.*
/benchmark/wc.input
@@ -126,7 +139,12 @@ y.tab.c
/enc/*.def
/enc/*.exp
/enc/*.lib
-/enc/unicode/data/*/*.txt
+/enc/jis/props.h
+/enc/unicode/data
+
+# /coroutine/
+!/coroutine/**/*.s
+/coroutine/**/.time
# /enc/trans/
/enc/trans/*.c
@@ -135,17 +153,21 @@ y.tab.c
/enc/trans/*.lib
/enc/trans/.time
+# /exe/
+/exe/goruby
+/exe/ruby
+/exe/.time
+
# /ext/
/ext/extinit.c
+/ext/configure-ext.mk
+/ext/*/exts.mk
# /ext/-test-/win32/dln/
+/ext/-test-/win32/dln/dlntest.dll
/ext/-test-/win32/dln/dlntest.exp
/ext/-test-/win32/dln/dlntest.lib
-# /ext/dl/callback/
-/ext/dl/callback/callback-*.c
-/ext/dl/callback/callback.c
-
# /ext/etc/
/ext/etc/constdefs.h
@@ -154,6 +176,7 @@ y.tab.c
# /ext/rbconfig/
/ext/rbconfig/sizeof/sizes.c
+/ext/rbconfig/sizeof/limits.c
# /ext/ripper/
/ext/ripper/eventids1.c
@@ -169,10 +192,11 @@ y.tab.c
# /gems
/gems/*.gem
+/gems/src
+/gems/*-*
-# /spec/
-/spec/mspec
-/spec/rubyspec
+# /spec/bundler
+/.rspec_status
# /tool/
/tool/config.guess
@@ -181,3 +205,8 @@ y.tab.c
# /win32/
/win32/*.ico
/win32/.time
+
+# MJIT
+/rb_mjit_header.h
+/mjit_config.h
+/include/ruby-*/*/rb_mjit_min_header-*.h
diff --git a/.travis.yml b/.travis.yml
index 2d94b9be03..f860bb79b4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,49 +18,341 @@
language: c
-sudo: false
+dist: xenial
-compiler:
- - gcc
+git:
+ quiet: true
-os:
- - linux
+addons:
+ apt:
+ config:
+ retries: true
+ update: true
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - gcc-8
+ - libffi-dev
+ - libgdbm-dev
+ - libgmp-dev
+ - libjemalloc-dev
+ - libncurses5-dev
+ - libncursesw5-dev
+ - libreadline6-dev
+ - libssl-dev
+ - libyaml-dev
+ - openssl
+ - valgrind
+ - zlib1g-dev
+ homebrew:
+ update: true
+ packages:
+ - gdbm
+ - gmp
+ - libffi
+ - openssl@1.1
+ - zlib
+ - ccache
-before_install:
- - "CONFIG_FLAG="
- - "JOBS='-j 4'"
+cache:
+ ccache: true
+ directories:
+ - $HOME/config_2nd
+ - $HOME/.downloaded-cache
+
+env:
+ global:
+ - CONFIGURE_TTY=no
+ - CCACHE_COMPILERCHECK=none
+ - CCACHE_NOCOMPRESS=1
+ - CCACHE_MAXSIZE=512Mi
+ - >-
+ NPROC="`case ${TRAVIS_OS_NAME} in
+ osx) sysctl -n hw.activecpu ;;
+ linux) nproc ;;
+ esac`"
+ # JOBS and SETARCH are overridden when necessary; see below.
+ - JOBS=-j$((1+${NPROC}))
+ - SETARCH=
+ - RUBY_PREFIX=/tmp/ruby-prefix
+ - GEMS_FOR_TEST='timezone tzinfo'
+
+.org.ruby-lang.ci.matrix-definitions:
+
+ - &cron-only
+ if: (type = cron) AND (branch = trunk) AND (fork = false)
+
+ - &make-test-only
+ script:
+ - $SETARCH make -s test TESTOPTS="${TESTOPTS=$JOBS -q --tty=no}"
+
+ - &linux
+ os: linux
+ compiler: gcc-8
+
+ - &osx
+ os: osx
+ compiler: clang
+ before_install:
+ - /usr/local/opt/openssl@1.1/bin/openssl version
+
+ # --------
+
+ - &x86_64-linux
+ name: x86_64-linux
+ <<: *linux
+
+ - &jemalloc
+ name: --with-jemalloc
+ <<: *linux
+ <<: *cron-only
+ env:
+ - CONFIG_FLAG='--with-gmp --with-jemalloc --with-valgrind'
+
+ - &VM_CHECK_MODE
+ name: VM_CHECK_MODE=3
+ <<: *linux
+ <<: *cron-only
+ <<: *make-test-only
+ env:
+ - cppflags=-DVM_CHECK_MODE=0x0003
+
+ - &FIBER_USE_sjlj
+ name: FIBER_USE_NATIVE=0
+ <<: *linux
+ <<: *cron-only
+ env:
+ - cppflags=-DFIBER_USE_NATIVE=0
+
+ - &TOKEN_THREADED_CODE
+ name: TOKEN_THREADED_CODE
+ <<: *linux
+ <<: *cron-only
+ <<: *make-test-only
+ env:
+ - cppflags=-DOPT_THREADED_CODE=1
+
+ - &CALL_THREADED_CODE
+ name: CALL_THREADED_CODE
+ <<: *linux
+ <<: *cron-only
+ <<: *make-test-only
+ env:
+ - cppflags=-DOPT_THREADED_CODE=2
+
+ - &NO_THREADED_CODE
+ name: NO_THREADED_CODE
+ <<: *linux
+ <<: *cron-only
+ <<: *make-test-only
+ env:
+ - cppflags=-DOPT_THREADED_CODE=3
+
+ - &ASAN
+ name: -fsanitize=address
+ <<: *linux
+ #<<: *cron-only
+ <<: *make-test-only
+ compiler: clang
+ env:
+ - ASAN_OPTIONS=detect_leaks=0
+ - cflags='-march=native -fsanitize=address -fno-omit-frame-pointer'
+ - debugflags=-ggdb3
+ - optflags=-O1
+ - LD=clang
+ - LDFLAGS=-fsanitize=address
+
+ - &MSAN
+ name: -fsanitize=memory
+ <<: *linux
+ #<<: *cron-only
+ <<: *make-test-only
+ compiler: clang
+ env:
+ - cflags='-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer'
+ - optflags=-O1
+ - LD=clang
+ - LDFLAGS=-fsanitize=memory
+ - CONFIG_FLAG=--with-out-ext=openssl
+
+ - &UBSAN
+ name: -fsanitize=undefined
+ <<: *linux
+ #<<: *cron-only
+ <<: *make-test-only
+ compiler: clang
+ env:
+ - cflags='-fsanitize=undefined,integer,nullability -fno-omit-frame-pointer'
+ - cppflags=-DUNALIGNED_WORD_ACCESS=0
+ - debugflags=-ggdb3
+ - optflags='-O1 -march=native'
+ - LD=clang
+ - LDFLAGS=-fsanitize=undefined,integer,nullability
+
+ - &i686-linux
+ name: i686-linux
+ <<: *linux
+ env:
+ - GCC_FLAGS=-m32
+ - debugflags=-g0
+ - SETARCH='setarch i686 --verbose --3gb'
+ addons:
+ apt:
+ config:
+ retries: true
+ update: true
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - gcc-8-multilib
+ - libffi-dev:i386
+ - libffi6:i386
+ - libgdbm-dev:i386
+ - libgdbm3:i386
+ - libncurses5-dev:i386
+ - libncurses5:i386
+ - libncursesw5-dev:i386
+ - libreadline6-dev:i386
+ - libreadline6:i386
+ - libssl-dev:i386
+ - libssl1.0.0:i386
+ - linux-libc-dev:i386
+ - zlib1g-dev:i386
+ - zlib1g:i386
+
+ - &rubyspec
+ name: ruby/spec on Ruby 2.3 # to ensure version guards are correctly added
+ <<: *linux
+ language: ruby
+ rvm: 2.3.8
+ addons:
+ apt:
+ packages:
+ before_install:
+ install:
+ before_script: chmod -R u+w spec/ruby
+ # -j randomly hangs. Using -fs to make sure we can know problematic spec on failure.
+ script: ruby -C spec/ruby ../mspec/bin/mspec -fs .
+
+ - &x86_64-darwin17
+ name: x86_64-darwin17
+ osx_image: xcode10.1
+ <<: *osx
+ env:
+ - CONFIG_FLAG=--with-opt-dir=/usr/local/opt/openssl@1.1:/usr/local/opt/zlib
+ - TEST_ALL_OPTS="--tty=no --excludes=\$(TESTSDIR)/excludes/_travis/osx"
+ # Disabling -j3 because it seems to cause a hang on building Ruby: https://travis-ci.org/ruby/ruby/jobs/471021727
+ - JOBS=
+
+ - &x86_64-darwin18
+ name: x86_64-darwin18
+ osx_image: xcode11
+ <<: *osx
+ env:
+ - CONFIG_FLAG=--with-opt-dir=/usr/local/opt/openssl@1.1:/usr/local/opt/zlib
+ - TEST_ALL_OPTS="--tty=no --excludes=\$(TESTSDIR)/excludes/_travis/osx"
+ # Disabling -j3 because it seems to cause a hang on building Ruby: https://travis-ci.org/ruby/ruby/jobs/471021727
+ - JOBS=
+
+ - &universal-darwin17
+ name: uinversal.x86_64h-darwin17
+ osx_image: xcode10.1
+ <<: *osx
+ <<: *cron-only
+ <<: *make-test-only
+ env:
+ - CONFIG_FLAG=--with-arch=x86_64h,x86_64,i386
+ - TEST_ALL_OPTS="$JOBS -q --tty=no --excludes=\$(TESTSDIR)/excludes/_travis/osx"
+
+matrix:
+ include:
+ # to reduce time for finishing all jobs, run the slowest osx build first.
+ - <<: *x86_64-darwin18
+ - <<: *x86_64-linux
+ - <<: *i686-linux
+ - <<: *jemalloc
+ - <<: *ASAN
+ - <<: *MSAN
+ - <<: *UBSAN
+ - <<: *VM_CHECK_MODE
+ - <<: *FIBER_USE_sjlj
+ - <<: *TOKEN_THREADED_CODE
+ - <<: *CALL_THREADED_CODE
+ - <<: *NO_THREADED_CODE
+ - <<: *rubyspec
+ allow_failures:
+ - name: uinversal.x86_64h-darwin17
+ - name: -fsanitize=address
+ - name: -fsanitize=memory
+ - name: -fsanitize=undefined
+ fast_finish: true
before_script:
- - "uname -a"
- - "uname -r"
- - "rm -fr .ext autom4te.cache"
- - "echo $TERM"
- - "make -f common.mk BASERUBY=ruby MAKEDIRS='mkdir -p' srcdir=. update-config_files"
- - "autoconf"
- - "mkdir config_1st config_2nd"
- - "./configure -C --disable-install-doc --with-gcc=$CC $CONFIG_FLAG"
- - "cp -pr config.status .ext/include config_1st"
- - "make reconfig"
- - "cp -pr config.status .ext/include config_2nd"
- - "diff -ru config_1st config_2nd"
- - "make after-update BASERUBY=ruby"
- - "make -s $JOBS"
- - "make update-rubyspec"
+ - echo JOBS=${JOBS} SETARCH=${SETARCH}
+ - $SETARCH uname -a
+ - $SETARCH uname -r
+ - rm -fr .ext autom4te.cache
+ - echo $TERM
+ - |-
+ [ -d ~/.downloaded-cache ] ||
+ mkdir ~/.downloaded-cache
+ - ln -s ~/.downloaded-cache
+ - "> config.status"
+ - sed -f tool/prereq.status Makefile.in common.mk > Makefile
+ - make -s $JOBS update-download
+ - make -s $JOBS srcs
+ - rm 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
+ - ccache --show-stats
+ - |-
+ 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 install
+ - ccache --show-stats
+ - |-
+ [ -z "${GEMS_FOR_TEST}" ] ||
+ $RUBY_PREFIX/bin/gem install --no-document $GEMS_FOR_TEST
script:
- - "make test TESTOPTS=--color=never"
- - "make test-all TESTOPTS='-q -j3 --color=never --job-status=normal'"
- - "make test-rubyspec MSPECOPT=-fm"
+ - $SETARCH make -s test TESTOPTS="${TESTOPTS=$JOBS -q --tty=no}"
+ - travis_wait 30 $SETARCH make -s test-all -o exts TESTOPTS="${TEST_ALL_OPTS=$TESTOPTS}"
+ - $SETARCH make -s test-spec MSPECOPT=-ff # not using `-j` because sometimes `mspec -j` silently dies
# Branch matrix. Not all branches are Travis-ready so we limit branches here.
branches:
only:
- trunk
- - ruby_2_1
- - ruby_2_2
- ruby_2_3
+ - ruby_2_4
+ - ruby_2_5
+ - ruby_2_6
- /^feature\//
- /^bug\//
+ - /^travis-/
# We want to be notified when something happens.
notifications:
@@ -73,16 +365,16 @@ notifications:
template:
- "%{message} by @%{author}: See %{build_url}"
- # Update ruby-head installed on Travis CI so other projects can test against it.
- webhooks:
- urls:
- - "https://rubies.travis-ci.org/rebuild/ruby-head"
- on_success: always
- on_failure: never
+ slack:
+ rooms:
+ - secure: i1GLETSKye85ea6dGNA3MxI/5myChmMFiZtBd5C69xK+s1sBFqEgOSbaSf9KHc0CYrHVyNhQMaZRruieV7xS+6Pfs0Zvxf1DO6QQTWC2KhkqwFDLvZncAzjoyASdR90hbr+iRPOngQ+HJuE94zemALAwEqNAinzA74PMiJXktqY= # ruby:<token>#commits
+ - secure: ah7UEHBvncXT7bM5mvYIQAO+tIyV/wl7nXLb7wQD16dO2v8Gragy0mWjB79Q09hrrMGmp6H9bCDpdGS80boIA5EHaHoG4QaP0i9bsSt8U2AMWgZtfyIgQKJ4H2kXkGlrjO+AXTgnIkP7LNjdgAVUUTGQPb26T3QmoN2Splt+fIQ= # ruby:<token>#alerts
+ on_pull_requests: false
+ on_success: change
+ on_failure: always
email:
- ko1c-failure@atdot.net
- - shibata.hiroshi@gmail.com
# Local Variables:
# mode: YAML
diff --git a/COPYING b/COPYING
index 426810a7fb..f06056fb45 100644
--- a/COPYING
+++ b/COPYING
@@ -11,8 +11,8 @@ You can redistribute it and/or modify it under either the terms of the
a) place your modifications in the Public Domain or otherwise
make them Freely Available, such as by posting said
- modifications to Usenet or an equivalent medium, or by allowing
- the author to include your modifications in the software.
+ modifications to Usenet or an equivalent medium, or by allowing
+ the author to include your modifications in the software.
b) use the modified software only within your corporation or
organization.
@@ -26,11 +26,11 @@ You can redistribute it and/or modify it under either the terms of the
provided that you do at least ONE of the following:
a) distribute the binaries and library files of the software,
- together with instructions (in the manual page or equivalent)
- on where to get the original distribution.
+ together with instructions (in the manual page or equivalent)
+ on where to get the original distribution.
b) accompany the distribution with the machine-readable source of
- the software.
+ the software.
c) give non-standard binaries non-standard names, with
instructions on where to get the original software distribution.
diff --git a/LEGAL b/LEGAL
index 8080cfef61..0fe24dfc3c 100644
--- a/LEGAL
+++ b/LEGAL
@@ -1,23 +1,27 @@
-LEGAL NOTICE INFORMATION
-------------------------
+# -*- rdoc -*-
+
+= LEGAL NOTICE INFORMATION
+--------------------------
All the files in this distribution are covered under either the Ruby's
license (see the file COPYING) or public-domain except some files
mentioned below.
-ccan/build_assert/build_assert.h
-ccan/check_type/check_type.h
-ccan/container_of/container_of.h
-ccan/str/str.h
+ccan/build_assert/build_assert.h::
+ccan/check_type/check_type.h::
+ccan/container_of/container_of.h::
+ccan/str/str.h::
These files are licensed under the CC0.
+ >>>
https://creativecommons.org/choose/zero/
-ccan/list/list.h
+ccan/list/list.h::
This file is licensed under the MIT License.
+ >>>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
@@ -36,149 +40,192 @@ ccan/list/list.h
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-include/ruby/oniguruma.h:
-regcomp.c:
-regenc.[ch]:
-regerror.c:
-regexec.c:
-regint.h:
-regparse.[ch]:
-enc/ascii.c
-enc/big5.c
-enc/cp949.c
-enc/emacs_mule.c
-enc/encdb.c
-enc/euc_jp.c
-enc/euc_kr.c
-enc/euc_tw.c
-enc/gb18030.c
-enc/gb2312.c
-enc/gbk.c
-enc/iso_8859_1.c
-enc/iso_8859_10.c
-enc/iso_8859_11.c
-enc/iso_8859_13.c
-enc/iso_8859_14.c
-enc/iso_8859_15.c
-enc/iso_8859_16.c
-enc/iso_8859_2.c
-enc/iso_8859_3.c
-enc/iso_8859_4.c
-enc/iso_8859_5.c
-enc/iso_8859_6.c
-enc/iso_8859_7.c
-enc/iso_8859_8.c
-enc/iso_8859_9.c
-enc/koi8_r.c
-enc/koi8_u.c
-enc/shift_jis.c
-enc/unicode.c
-enc/us_ascii.c
-enc/utf_16be.c
-enc/utf_16le.c
-enc/utf_32be.c
-enc/utf_32le.c
-enc/utf_8.c
-enc/windows_1251.c
-
-Oniguruma ---- (C) K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
-
-http://www.geocities.jp/kosako3/oniguruma/
-http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/oniguruma/
-http://www.freebsd.org/cgi/cvsweb.cgi/ports/devel/oniguruma/
-
- When this software is partly used or it is distributed with Ruby,
- this of Ruby follows the license of Ruby.
-
-enc/trans/GB/GB12345%UCS.src:
-enc/trans/GB/UCS%GB12345.src:
-
- Copyright (c) 1991-1994 Unicode, Inc. All Rights reserved.
-
- This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
- No claims are made as to fitness for any particular purpose. No
- warranties of any kind are expressed or implied. The recipient
- agrees to determine applicability of information provided. If this
- file has been provided on magnetic media by Unicode, Inc., the sole
- remedy for any claim will be exchange of defective media within 90
- days of receipt.
-
- Recipient is granted the right to make copies in any form for
- internal distribution and to freely use the information supplied
- in the creation of products supporting Unicode. Unicode, Inc.
- specifically excludes the right to re-distribute this file directly
- to third parties or other organizations whether for profit or not.
-
-
-enc/trans/GB/GB2312%UCS.src:
-enc/trans/GB/UCS%GB2312.src:
-
- Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved.
-
- This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
- No claims are made as to fitness for any particular purpose. No
- warranties of any kind are expressed or implied. The recipient
- agrees to determine applicability of information provided. If this
- file has been provided on optical media by Unicode, Inc., the sole
- remedy for any claim will be exchange of defective media within 90
- days of receipt.
-
- Unicode, Inc. hereby grants the right to freely use the information
- supplied in this file in the creation of products supporting the
- Unicode Standard, and to make copies of this file in any form for
- internal or external distribution as long as this notice remains
- attached.
-
-enc/trans/JIS/JISX0201-KANA%UCS.src:
-enc/trans/JIS/JISX0208@1990%UCS.src:
-enc/trans/JIS/JISX0212%UCS.src:
-enc/trans/JIS/UCS%JISX0201-KANA.src:
-enc/trans/JIS/UCS%JISX0208@1990.src:
-enc/trans/JIS/UCS%JISX0212.src:
+include/ruby/onigmo.h::
+include/ruby/oniguruma.h::
+regcomp.c::
+regenc.c::
+regenc.h::
+regerror.c::
+regexec.c::
+regint.h::
+regparse.c::
+regparse.h::
+enc/ascii.c::
+enc/big5.c::
+enc/cp949.c::
+enc/emacs_mule.c::
+enc/encdb.c::
+enc/euc_jp.c::
+enc/euc_kr.c::
+enc/euc_tw.c::
+enc/gb18030.c::
+enc/gb2312.c::
+enc/gbk.c::
+enc/iso_8859_1.c::
+enc/iso_8859_10.c::
+enc/iso_8859_11.c::
+enc/iso_8859_13.c::
+enc/iso_8859_14.c::
+enc/iso_8859_15.c::
+enc/iso_8859_16.c::
+enc/iso_8859_2.c::
+enc/iso_8859_3.c::
+enc/iso_8859_4.c::
+enc/iso_8859_5.c::
+enc/iso_8859_6.c::
+enc/iso_8859_7.c::
+enc/iso_8859_8.c::
+enc/iso_8859_9.c::
+enc/koi8_r.c::
+enc/koi8_u.c::
+enc/shift_jis.c::
+enc/unicode.c::
+enc/us_ascii.c::
+enc/utf_16be.c::
+enc/utf_16le.c::
+enc/utf_32be.c::
+enc/utf_32le.c::
+enc/utf_8.c::
+enc/windows_1251.c::
+
+ Onigmo (Oniguruma-mod) LICENSE
+
+ >>>
+ Copyright (c) 2002-2009 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
+ Copyright (c) 2011-2014 K.Takata <kentkt AT csc DOT jp>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ Oniguruma LICENSE
+
+ >>>
+ Copyright (c) 2002-2009 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ * https://github.com/k-takata/Onigmo/
+ * https://github.com/kkos/oniguruma
+ * https://svnweb.freebsd.org/ports/head/devel/oniguruma/
+
+ When this software is partly used or it is distributed with Ruby,
+ this of Ruby follows the license of Ruby.
+
+enc/trans/GB/GB12345%UCS.src::
+enc/trans/GB/UCS%GB12345.src::
+enc/trans/GB/GB2312%UCS.src::
+enc/trans/GB/UCS%GB2312.src::
+
+ These files have this explanatory texts.
+
+ >>>
+ This mapping data was created from files provided by Unicode, Inc.
+ (The Unicode Consortium). The files were used to create a product supporting
+ Unicode, as explicitly permitted in the files' copyright notices.
+ Please note that Unicode, Inc. never made any claims as to fitness of these
+ files for any particular purpose, and has ceased to publish the files many
+ years ago.
+
+enc/trans/JIS/JISX0201-KANA%UCS.src::
+enc/trans/JIS/JISX0208\@1990%UCS.src::
+enc/trans/JIS/JISX0212%UCS.src::
+enc/trans/JIS/UCS%JISX0201-KANA.src::
+enc/trans/JIS/UCS%JISX0208@1990.src::
+enc/trans/JIS/UCS%JISX0212.src::
+
+ These files are copyrighted as the following.
+ >>>
© 2015 Unicode®, Inc.
+
For terms of use, see http://www.unicode.org/terms_of_use.html
-enc/trans/JIS/JISX0213-1%UCS@BMP.src:
-enc/trans/JIS/JISX0213-1%UCS@SIP.src:
-enc/trans/JIS/JISX0213-2%UCS@BMP.src:
-enc/trans/JIS/JISX0213-2%UCS@SIP.src:
+enc/trans/JIS/JISX0213-1%UCS@BMP.src::
+enc/trans/JIS/JISX0213-1%UCS@SIP.src::
+enc/trans/JIS/JISX0213-2%UCS@BMP.src::
+enc/trans/JIS/JISX0213-2%UCS@SIP.src::
+
+ These files are copyrighted as the following.
+
+ >>>
+ Copyright (C) 2001 earthian@tama.or.jp, All Rights Reserved.
+ Copyright (C) 2001 I'O, All Rights Reserved.
+ Copyright (C) 2006 Project X0213, All Rights Reserved.
+ You can use, modify, distribute this table freely.
- Copyright (C) 2001 earthian@tama.or.jp, All Rights Reserved.
- Copyright (C) 2001 I'O, All Rights Reserved.
- Copyright (C) 2006 Project X0213, All Rights Reserved.
- You can use, modify, distribute this table freely.
+enc/trans/JIS/UCS@BMP%JISX0213-1.src::
+enc/trans/JIS/UCS@BMP%JISX0213-2.src::
+enc/trans/JIS/UCS@SIP%JISX0213-1.src::
+enc/trans/JIS/UCS@SIP%JISX0213-2.src::
-enc/trans/JIS/UCS@BMP%JISX0213-1.src:
-enc/trans/JIS/UCS@BMP%JISX0213-2.src:
-enc/trans/JIS/UCS@SIP%JISX0213-1.src:
-enc/trans/JIS/UCS@SIP%JISX0213-2.src:
+ These files are copyrighted as the following.
- Copyright (C) 2001 earthian@tama.or.jp, All Rights Reserved.
- Copyright (C) 2001 I'O, All Rights Reserved.
- You can use, modify, distribute this table freely.
+ >>>
+ Copyright (C) 2001 earthian@tama.or.jp, All Rights Reserved.
+ Copyright (C) 2001 I'O, All Rights Reserved.
+ You can use, modify, distribute this table freely.
-configure:
+configure::
This file is free software.
- Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+ >>>
+ Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it.
-tool/config.guess:
-tool/config.sub:
+tool/config.guess::
+tool/config.sub::
As long as you distribute these files with the file configure, they
are covered under the Ruby's license.
- Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999
- Free Software Foundation, Inc.
+ >>>
+ Copyright 1992-2018 Free Software Foundation, Inc.
This file is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
@@ -187,53 +234,55 @@ tool/config.sub:
General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ along with this program; if not, see <https://www.gnu.org/licenses/>.
As a special exception to the GNU General Public License, if you
distribute this file as part of a program that contains a
configuration script generated by Autoconf, you may include it under
- the same distribution terms that you use for the rest of that program.
+ the same distribution terms that you use for the rest of that
+ program. This Exception is an additional permission under section 7
+ of the GNU General Public License, version 3 ("GPLv3").
-parse.c:
+parse.c::
This file is licensed under the GPL, but is incorporated into Ruby and
redistributed under the terms of the Ruby license, as permitted by the
exception to the GPL below.
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
+ >>>
+ Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
- /* As a special exception, you may create a larger work that contains
- part or all of the Bison parser skeleton and distribute that work
- under terms of your choice, so long as that work isn't itself a
- parser generator using the skeleton or a modified version thereof
- as a parser skeleton. Alternatively, if you modify or redistribute
- the parser skeleton itself, you may (at your option) remove this
- special exception, which will cause the skeleton and the resulting
- Bison output files to be licensed under the GNU General Public
- License without this special exception.
+ As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
- This special exception was added by the Free Software Foundation in
- version 2.2 of Bison. */
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison.
-util.c (partly):
+util.c (partly)::
+ This file contains the source code under these licenses.
+
+ >>>
Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
Permission to use, copy, modify, and distribute this software for any
@@ -247,48 +296,49 @@ util.c (partly):
REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
-win32/win32.[ch]:
+ >>>
+ Copyright (c) 2004-2008 David Schultz <das@FreeBSD.ORG>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+win32/win32.{c,h}::
You can apply the Artistic License to these files. (or GPL,
alternatively)
+ >>>
Copyright (c) 1993, Intergraph Corporation
You may distribute under the terms of either the GNU General Public
License or the Artistic License, as specified in the perl README file.
-util.c (partly):
-
- Copyright (c) 2004-2008 David Schultz <das@FreeBSD.ORG>
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
+random.c::
-random.c
-
- This file is under the new-style BSD license.
+ This file contains the source code under the new-style BSD license.
+ >>>
A C-program for MT19937, with initialization improved 2002/2/10.
- Coded by Takuji Nishimura and Makoto Matsumoto.
+ Coded by Takuji Nishimura and Makoto Matsumoto.
This is a faster version by taking Shawn Cokus's optimization,
Matthe Bellew's simplification, Isaku Wada's real version.
@@ -302,16 +352,16 @@ random.c
modification, are permitted provided that the following conditions
are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
- 3. The names of its contributors may not be used to endorse or promote
- products derived from this software without specific prior written
- permission.
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -330,11 +380,14 @@ random.c
http://www.math.keio.ac.jp/matumoto/emt.html
email: matumoto@math.keio.ac.jp
-vm_dump.c:procstat_vm
+ The Wayback Machine url: http://web.archive.org/web/19990429082237/http://www.math.keio.ac.jp/matumoto/emt.html
+
+vm_dump.c:procstat_vm::
- This file is under the new-style BSD license.
+ This file contains the source code under the new-style BSD license.
- Copyright (c) 2007 Robert N. M. Watson
+ >>>
+ Copyright (c) 2007 Robert N. M. Watson
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -360,13 +413,14 @@ vm_dump.c:procstat_vm
$FreeBSD: head/usr.bin/procstat/procstat_vm.c 261780 2014-02-11 21:57:37Z jhb $
-vsnprintf.c:
+vsnprintf.c::
This file is under the old-style BSD license. Note that the
paragraph 3 below is now null and void.
- Copyright (c) 1990, 1993
- The Regents of the University of California. All rights reserved.
+ >>>
+ Copyright (c) 1990, 1993
+ The Regents of the University of California. All rights reserved.
This code is derived from software contributed to Berkeley by
Chris Torek.
@@ -400,39 +454,40 @@ vsnprintf.c:
From ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
paragraph 3 above is now null and void.
-st.c:
-strftime.c:
-include/ruby/st.h:
-missing/acosh.c:
-missing/alloca.c:
-missing/dup2.c:
-missing/erf.c:
-missing/finite.c:
-missing/hypot.c:
-missing/isinf.c:
-missing/isnan.c:
-missing/lgamma_r.c:
-missing/memcmp.c:
-missing/memmove.c:
-missing/strchr.c:
-missing/strerror.c:
-missing/strstr.c:
-missing/strtol.c:
-missing/tgamma.c:
-ext/date/date_strftime.c:
-ext/digest/sha1/sha1.[ch]:
-ext/sdbm/_sdbm.c:
-ext/sdbm/sdbm.h:
+st.c::
+strftime.c::
+include/ruby/st.h::
+missing/acosh.c::
+missing/alloca.c::
+missing/dup2.c::
+missing/erf.c::
+missing/finite.c::
+missing/hypot.c::
+missing/isinf.c::
+missing/isnan.c::
+missing/lgamma_r.c::
+missing/memcmp.c::
+missing/memmove.c::
+missing/strchr.c::
+missing/strerror.c::
+missing/strstr.c::
+missing/tgamma.c::
+ext/date/date_strftime.c::
+ext/digest/sha1/sha1.c::
+ext/digest/sha1/sha1.h::
+ext/sdbm/_sdbm.c::
+ext/sdbm/sdbm.h::
These files are all under public domain.
-missing/crypt.c:
+missing/crypt.c::
This file is under the old-style BSD license. Note that the
paragraph 3 below is now null and void.
- Copyright (c) 1989, 1993
- The Regents of the University of California. All rights reserved.
+ >>>
+ Copyright (c) 1989, 1993
+ The Regents of the University of California. All rights reserved.
This code is derived from software contributed to Berkeley by
Tom Truscott.
@@ -461,15 +516,16 @@ missing/crypt.c:
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
-missing/setproctitle.c
+missing/setproctitle.c::
This file is under the old-style BSD license. Note that the
paragraph 3 below is now null and void.
- Copyright 2003 Damien Miller
- Copyright (c) 1983, 1995-1997 Eric P. Allman
- Copyright (c) 1988, 1993
- The Regents of the University of California. All rights reserved.
+ >>>
+ Copyright 2003 Damien Miller
+ Copyright (c) 1983, 1995-1997 Eric P. Allman
+ Copyright (c) 1988, 1993
+ The Regents of the University of California. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -495,11 +551,12 @@ missing/setproctitle.c
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
-missing/strlcat.c
-missing/strlcpy.c
+missing/strlcat.c::
+missing/strlcpy.c::
These files are under an ISC-style license.
+ >>>
Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
Permission to use, copy, modify, and distribute this software for any
@@ -514,22 +571,25 @@ missing/strlcpy.c
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-missing/langinfo.c
+missing/langinfo.c::
This file is from http://www.cl.cam.ac.uk/~mgk25/ucs/langinfo.c.
Ruby uses a modified version. The file contains the following
author/copyright notice:
- Markus.Kuhn@cl.cam.ac.uk -- 2002-03-11
- Permission to use, copy, modify, and distribute this software
- for any purpose and without fee is hereby granted. The author
- disclaims all warranties with regard to this software.
+ >>>
+ Markus.Kuhn@cl.cam.ac.uk -- 2002-03-11
+ Permission to use, copy, modify, and distribute this software
+ for any purpose and without fee is hereby granted. The author
+ disclaims all warranties with regard to this software.
-ext/digest/md5/md5.[ch]:
+ext/digest/md5/md5.c::
+ext/digest/md5/md5.h::
These files are under the following license. Ruby uses modified
versions of them.
+ >>>
Copyright (C) 1999, 2000 Aladdin Enterprises. All rights reserved.
This software is provided 'as-is', without any express or implied
@@ -551,22 +611,26 @@ ext/digest/md5/md5.[ch]:
L. Peter Deutsch
ghost@aladdin.com
-ext/digest/rmd160/rmd160.[ch]:
+ext/digest/rmd160/rmd160.c::
+ext/digest/rmd160/rmd160.h::
These files have the following copyright information, and by the
author we are allowed to use it under the new-style BSD license.
- AUTHOR: Antoon Bosselaers, ESAT-COSIC
+ >>>
+ AUTHOR:: Antoon Bosselaers, ESAT-COSIC
(Arranged for libc by Todd C. Miller)
- DATE: 1 March 1996
+ DATE:: 1 March 1996
Copyright (c) Katholieke Universiteit Leuven
1996, All Rights Reserved
-ext/digest/sha2/sha2.[ch]:
+ext/digest/sha2/sha2.c::
+ext/digest/sha2/sha2.h::
These files are under the new-style BSD license.
+ >>>
Copyright 2000 Aaron D. Gifford. All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -593,50 +657,108 @@ ext/digest/sha2/sha2.[ch]:
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
-ext/json/generator/generator.c:
+ext/json/generator/generator.c::
+ The file contains the following copyright notice.
+
+ >>>
Copyright 2001-2004 Unicode, Inc.
- Disclaimer
+ Disclaimer::
- This source code is provided as is by Unicode, Inc. No claims are
- made as to fitness for any particular purpose. No warranties of any
- kind are expressed or implied. The recipient agrees to determine
- applicability of information provided. If this file has been
- purchased on magnetic or optical media from Unicode, Inc., the
- sole remedy for any claim will be exchange of defective media
- within 90 days of receipt.
+ This source code is provided as is by Unicode, Inc. No claims are
+ made as to fitness for any particular purpose. No warranties of any
+ kind are expressed or implied. The recipient agrees to determine
+ applicability of information provided. If this file has been
+ purchased on magnetic or optical media from Unicode, Inc., the
+ sole remedy for any claim will be exchange of defective media
+ within 90 days of receipt.
- Limitations on Rights to Redistribute This Code
+ Limitations on Rights to Redistribute This Code::
- Unicode, Inc. hereby grants the right to freely use the information
- supplied in this file in the creation of products supporting the
- Unicode Standard, and to make copies of this file in any form
- for internal or external distribution as long as this notice
- remains attached.
+ Unicode, Inc. hereby grants the right to freely use the information
+ supplied in this file in the creation of products supporting the
+ Unicode Standard, and to make copies of this file in any form
+ for internal or external distribution as long as this notice
+ remains attached.
-ext/nkf/nkf-utf8/config.h:
-ext/nkf/nkf-utf8/nkf.c:
-ext/nkf/nkf-utf8/utf8tbl.c:
+ext/nkf/nkf-utf8/config.h::
+ext/nkf/nkf-utf8/nkf.c::
+ext/nkf/nkf-utf8/utf8tbl.c::
These files are under the following license. So to speak, it is
copyrighted semi-public-domain software.
+ >>>
Copyright (C) 1987, Fujitsu LTD. (Itaru ICHIKAWA)
- Everyone is permitted to do anything on this program
- including copying, modifying, improving,
- as long as you don't try to pretend that you wrote it.
- i.e., the above copyright notice has to appear in all copies.
- Binary distribution requires original version messages.
- You don't have to ask before copying, redistribution or publishing.
- THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE.
-
-ext/socket/addrinfo.h:
-ext/socket/getaddrinfo.c:
-ext/socket/getnameinfo.c:
+
+ Everyone is permitted to do anything on this program
+ including copying, modifying, improving,
+ as long as you don't try to pretend that you wrote it.
+ i.e., the above copyright notice has to appear in all copies.
+ Binary distribution requires original version messages.
+ You don't have to ask before copying, redistribution or publishing.
+ THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE.
+
+ext/psych::
+test/psych::
+
+ The files under these directories are under the following license, except for
+ ext/psych/yaml.
+
+ >>>
+ Copyright 2009 Aaron Patterson, et al.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
+ this software and associated documentation files (the 'Software'), to deal in
+ the Software without restriction, including without limitation the rights to
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is furnished to do
+ so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ext/psych/yaml::
+
+ The files under this directory are under the following license.
+
+ >>>
+ Copyright (c) 2006 Kirill Simonov
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
+ this software and associated documentation files (the "Software"), to deal in
+ the Software without restriction, including without limitation the rights to
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is furnished to do
+ so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ext/socket/addrinfo.h::
+ext/socket/getaddrinfo.c::
+ext/socket/getnameinfo.c::
These files are under the new-style BSD license.
+ >>>
Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
All rights reserved.
@@ -664,11 +786,12 @@ ext/socket/getnameinfo.c:
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
-ext/win32ole/win32ole.c:
+ext/win32ole/win32ole.c::
You can apply the Artistic License to this file. (or GPL,
alternatively)
+ >>>
(c) 1995 Microsoft Corporation. All rights reserved.
Developed by ActiveWare Internet Corp., http://www.ActiveWare.com
@@ -679,8 +802,134 @@ ext/win32ole/win32ole.c:
License or the Artistic License, as specified in the README file
of the Perl distribution.
-lib/rdoc/generator/template/darkfish/css/fonts.css:
+ The Wayback Machine url: http://web.archive.org/web/19970607104352/http://www.activeware.com:80/
+
+lib/rdoc/generator/template/darkfish/css/fonts.css::
This file is licensed under the SIL Open Font License.
+ >>>
http://scripts.sil.org/OFL
+
+spec/mspec::
+spec/ruby::
+
+ The files under these directories are under the following license.
+
+ >>>
+ Copyright (c) 2008 Engine Yard, Inc. All rights reserved.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+
+lib/rubygems.rb::
+lib/rubygems::
+test/rubygems::
+
+ RubyGems is under the following license.
+
+ >>>
+ RubyGems is copyrighted free software by Chad Fowler, Rich Kilmer, Jim
+ Weirich and others. You can redistribute it and/or modify it under
+ either the terms of the MIT license (see the file MIT.txt), or the
+ conditions below:
+
+ 1. You may make and give away verbatim copies of the source form of the
+ software without restriction, provided that you duplicate all of the
+ original copyright notices and associated disclaimers.
+
+ 2. You may modify your copy of the software in any way, provided that
+ you do at least ONE of the following:
+
+ a. place your modifications in the Public Domain or otherwise
+ make them Freely Available, such as by posting said
+ modifications to Usenet or an equivalent medium, or by allowing
+ the author to include your modifications in the software.
+
+ b. use the modified software only within your corporation or
+ organization.
+
+ c. give non-standard executables non-standard names, with
+ instructions on where to get the original software distribution.
+
+ d. make other distribution arrangements with the author.
+
+ 3. You may distribute the software in object code or executable
+ form, provided that you do at least ONE of the following:
+
+ a. distribute the executables and library files of the software,
+ together with instructions (in the manual page or equivalent)
+ on where to get the original distribution.
+
+ b. accompany the distribution with the machine-readable source of
+ the software.
+
+ c. give non-standard executables non-standard names, with
+ instructions on where to get the original software distribution.
+
+ d. make other distribution arrangements with the author.
+
+ 4. You may modify and include the part of the software into any other
+ software (possibly commercial).
+
+ 5. The scripts and library files supplied as input to or produced as
+ output from the software do not automatically fall under the
+ copyright of the software, but belong to whomever generated them,
+ and may be sold commercially, and may be aggregated with this
+ software.
+
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+lib/bundler::
+lib/bundler.rb::
+lib/bundler.gemspec::
+spec/bundler::
+man/bundle-*,gemfile.*::
+
+ Bundler is under the following license.
+
+ >>>
+ Portions copyright (c) 2010 Andre Arko
+ Portions copyright (c) 2009 Engine Yard
+
+ MIT License::
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Makefile.in b/Makefile.in
index 9c8748aa4d..fa1e19ef37 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -16,6 +16,7 @@ top_srcdir = $(srcdir)
hdrdir = $(srcdir)/include
PLATFORM_DIR = @PLATFORM_DIR@
+CC_WRAPPER = @XCC_WRAPPER@
CC = @CC@
CPP = @CPP@
LD = @LD@
@@ -44,7 +45,17 @@ arch = @arch@
sitearch = @sitearch@
sitedir = @sitedir@
archlibdir = @archlibdir@
+includedir = @includedir@
+archincludedir = @archincludedir@
+rubylibprefix = @rubylibprefix@
+rubylibdir = @rubylibdir@
+rubyarchprefix = @rubyarchprefix@
+rubyarchdir = @rubyarchdir@
+rubyhdrdir = @rubyhdrdir@
+rubyarchhdrdir = @rubyarchhdrdir@
ruby_version = @ruby_version@
+RUBY_VERSION_NAME = @RUBY_VERSION_NAME@
+UNIVERSAL_ARCHNAMES = @UNIVERSAL_ARCHNAMES@
TESTUI = console
TESTS =
@@ -52,6 +63,7 @@ INSTALLDOC = @INSTALLDOC@
DOCTARGETS = @RDOCTARGET@ @CAPITARGET@
EXTOUT = @EXTOUT@
+TIMESTAMPDIR = $(EXTOUT)/.timestamp
arch_hdrdir = $(EXTOUT)/include/$(arch)
VPATH = $(arch_hdrdir)/ruby:$(hdrdir)/ruby:$(srcdir):$(srcdir)/missing
@@ -59,6 +71,7 @@ empty =
CC_VERSION = @CC_VERSION@
OUTFLAG = @OUTFLAG@$(empty)
COUTFLAG = @COUTFLAG@$(empty)
+CPPOUTFLAG = $(COUTFLAG)
ARCH_FLAG = @ARCH_FLAG@
CFLAGS_NO_ARCH = @CFLAGS@
CFLAGS = $(CFLAGS_NO_ARCH) $(ARCH_FLAG)
@@ -67,17 +80,38 @@ optflags = @optflags@
debugflags = @debugflags@
warnflags = @warnflags@ @strict_warnflags@
cppflags = @cppflags@
-XCFLAGS = @XCFLAGS@
-CPPFLAGS = @CPPFLAGS@ $(INCFLAGS)
+MATHN = @MATHN@
+XCFLAGS = @XCFLAGS@ $(MATHN:yes=-DCANONICALIZATION_FOR_MATHN)
+USE_RUBYGEMS = @USE_RUBYGEMS@
+USE_RUBYGEMS_ = $(USE_RUBYGEMS:yes=)
+CPPFLAGS = @CPPFLAGS@ $(INCFLAGS) $(USE_RUBYGEMS_:no=-DDISABLE_RUBYGEMS=1)
+MJIT_SUPPORT = @MJIT_SUPPORT@
+MJIT_HEADER_FLAGS = @MJIT_HEADER_FLAGS@
+MJIT_HEADER_SUFFIX =
+MJIT_HEADER_ARCH =
+MJIT_HEADER_INSTALL_DIR = @MJIT_HEADER_INSTALL_DIR@
+MJIT_CC = @MJIT_CC@
+MJIT_CFLAGS = @MJIT_CFLAGS@
+MJIT_OPTFLAGS = @MJIT_OPTFLAGS@
+MJIT_DEBUGFLAGS = @MJIT_DEBUGFLAGS@
+MJIT_LDSHARED = @MJIT_LDSHARED@
+MJIT_DLDFLAGS = @DLDFLAGS@
+MJIT_HEADER = rb_mjit_header.h
+MJIT_MIN_HEADER_NAME = rb_mjit_min_header-$(RUBY_PROGRAM_VERSION).h
+MJIT_MIN_HEADER = $(MJIT_HEADER_BUILD_DIR)/$(MJIT_MIN_HEADER_NAME)
+MJIT_HEADER_BUILD_DIR = $(EXTOUT)/include/$(arch)
LDFLAGS = @STATIC@ $(CFLAGS) @LDFLAGS@
EXTLDFLAGS = @EXTLDFLAGS@
XLDFLAGS = @XLDFLAGS@ $(EXTLDFLAGS)
EXTLIBS =
LIBS = @LIBS@ $(EXTLIBS)
MISSING = @LIBOBJS@ @ALLOCA@
+ENABLE_SHARED = @ENABLE_SHARED@
LDSHARED = @LIBRUBY_LDSHARED@
+DLDSHARED = @DLDSHARED@
DLDFLAGS = @LIBRUBY_DLDFLAGS@ $(XLDFLAGS) $(ARCH_FLAG)
SOLIBS = @SOLIBS@
+ENABLE_DEBUG_ENV = @ENABLE_DEBUG_ENV@
MAINLIBS = @MAINLIBS@
ARCHMINIOBJS = @MINIOBJS@
DLNOBJ = @DLNOBJ@
@@ -90,6 +124,7 @@ POSTLINK = @POSTLINK@
RUBY_BASE_NAME=@RUBY_BASE_NAME@
RUBY_PROGRAM_VERSION=@RUBY_PROGRAM_VERSION@
+RUBY_API_VERSION=@RUBY_API_VERSION@
RUBY_INSTALL_NAME=@RUBY_INSTALL_NAME@
RUBY_SO_NAME=@RUBY_SO_NAME@
EXEEXT = @EXEEXT@
@@ -116,6 +151,9 @@ XRUBY_RUBYLIBDIR = @XRUBY_RUBYLIBDIR@
XRUBY_RUBYHDRDIR = @XRUBY_RUBYHDRDIR@
BOOTSTRAPRUBY = @BOOTSTRAPRUBY@
+COROUTINE_H = @X_FIBER_COROUTINE_H@
+COROUTINE_OBJ = $(COROUTINE_H:.h=.@OBJEXT@)
+
#### End of system configuration section. ####
MAJOR= @MAJOR@
@@ -125,6 +163,7 @@ RUBY_PROGRAM_VERSION = @RUBY_PROGRAM_VERSION@
LIBRUBY_A = @LIBRUBY_A@
LIBRUBY_SO = @LIBRUBY_SO@
+LIBRUBY_SONAME= @LIBRUBY_SONAME@
LIBRUBY_ALIASES= @LIBRUBY_ALIASES@
LIBRUBY = @LIBRUBY@
LIBRUBYARG = @LIBRUBYARG@
@@ -164,17 +203,18 @@ RMDIRS = @RMDIRS@
RMALL = @RMALL@
NM = @NM@
AR = @AR@
-ARFLAGS = @ARFLAGS@
+ARFLAGS = @ARFLAGS@$(empty)
RANLIB = @RANLIB@
AS = @AS@
ASFLAGS = @ASFLAGS@ $(INCFLAGS)
IFCHANGE = $(srcdir)/tool/ifchange
-SET_LC_MESSAGES = env LC_MESSAGES=C
OBJDUMP = @OBJDUMP@
OBJCOPY = @OBJCOPY@
+HAVE_GIT = @HAVE_GIT@
+GIT = @GIT@
VCS = @VCS@
VCSUP = @VCSUP@
-DTRACE = @DTRACE@
+DTRACE = @DTRACE@ @DTRACE_OPT@
DTRACE_EXT = @DTRACE_EXT@
DTRACE_OBJ = @DTRACE_OBJ@
DTRACE_REBUILD= @DTRACE_REBUILD@
@@ -182,6 +222,7 @@ DTRACE_GLOMMED_OBJ = $(DTRACE_REBUILD:yes=ruby-glommed.$(OBJEXT))
OBJEXT = @OBJEXT@
ASMEXT = S
+SOEXT = @SOEXT@
DLEXT = @DLEXT@
MANTYPE = @MANTYPE@
SYMBOL_PREFIX = @SYMBOL_PREFIX@
@@ -191,8 +232,11 @@ INSTALLED_LIST= .installed.list
NEWLINE_C = enc/trans/newline.c
MINIPRELUDE_C = miniprelude.c
PRELUDE_C = prelude.c
+GOLF_PRELUDE_C= golf_prelude.c
RBCONFIG = .rbconfig.time
+MAINSRC = $(MAINOBJ:@OBJEXT@=c)
+
SRC_FILE = $<
OS_SRC_FILE = $<
DEST_FILE = $@
@@ -208,7 +252,7 @@ DESTDIR = @DESTDIR@
configure_args = @configure_args@
#### End of variables
-.SUFFIXES: .inc .h .c .y .i .$(DTRACE_EXT)
+.SUFFIXES: .inc .h .c .y .i .$(ASMEXT) .$(DTRACE_EXT)
all:
@@ -219,6 +263,7 @@ miniruby$(EXEEXT):
@-if test -f $@; then $(MV) -f $@ $@.old; $(RM) $@.old; fi
$(ECHO) linking $@
$(Q) $(PURIFY) $(CC) $(LDFLAGS) $(XLDFLAGS) $(NORMALMAINOBJ) $(MINIOBJS) $(COMMONOBJS) $(MAINLIBS) $(LIBS) $(OUTFLAG)$@
+ $(Q) $(POSTLINK)
$(PROGRAM):
@$(RM) $@
@@ -226,39 +271,45 @@ $(PROGRAM):
$(Q) $(PURIFY) $(CC) $(LDFLAGS) $(XLDFLAGS) $(MAINOBJ) $(EXTOBJS) $(LIBRUBYARG) $(MAINLIBS) $(LIBS) $(EXTLIBS) $(OUTFLAG)$@
$(Q) $(POSTLINK)
+PRE_LIBRUBY_UPDATE = [ -n "$(LIBRUBY_SO_UPDATE)" ] || $(exec) $(RM) $(LIBRUBY_EXTS)
+
# We must `rm' the library each time this rule is invoked because "updating" a
# MAB library on Apple/NeXT (see --enable-fat-binary in configure) is not
# supported.
$(LIBRUBY_A):
@$(RM) $@
+ @-[ -z "$(EXTSTATIC)" ] || $(PRE_LIBRUBY_UPDATE)
$(ECHO) linking static-library $@
$(Q) $(AR) $(ARFLAGS) $@ $(LIBRUBY_A_OBJS) $(INITOBJS)
@-$(RANLIB) $@ 2> /dev/null || true
+
+verify-static-library: $(LIBRUBY_A)
$(ECHO) verifying static-library $@
@$(PURIFY) $(CC) $(LDFLAGS) $(XLDFLAGS) $(MAINOBJ) $(LIBRUBY_A) $(MAINLIBS) $(EXTLIBS) $(LIBS) $(OUTFLAG)conftest$(EXEEXT)
- @$(RM) conftest$(EXEEXT) conftest.c
+ @$(RMALL) conftest$(EXEEXT) conftest.c conftest.dSYM
$(LIBRUBY_SO):
- @-$(PRE_LIBRUBY_UPDATE)
+ @-[ -n "$(EXTSTATIC)" ] || $(PRE_LIBRUBY_UPDATE)
$(ECHO) linking shared-library $@
$(Q) $(LDSHARED) $(DLDFLAGS) $(OBJS) $(DLDOBJS) $(SOLIBS) $(EXTSOLIBS) $(OUTFLAG)$@
-$(Q) $(OBJCOPY) -w -L '$(SYMBOL_PREFIX)Init_*' -L '$(SYMBOL_PREFIX)ruby_static_id_*' \
- -L '$(SYMBOL_PREFIX)*_threadptr_*' $@
+ -L '$(SYMBOL_PREFIX)*_threadptr_*' -L '$(SYMBOL_PREFIX)*_ec_*' $@
$(Q) $(POSTLINK)
- @-$(MINIRUBY) -e 'ARGV.each{|link| File.delete link rescue nil; \
- File.symlink "$(LIBRUBY_SO)", link}' \
- $(LIBRUBY_ALIASES) || true
+ @-$(MINIRUBY) -e 'so, *aliases = ARGV; aliases.uniq!; aliases.delete(File.basename(so)); \
+ aliases.each { |link| File.delete link rescue nil; File.symlink so, link }' \
+ $(LIBRUBY_SO) $(LIBRUBY_ALIASES) || true
+
+LIBRUBY_WITH_EXT = @LIBRUBY_WITH_EXT@
+$(LIBRUBY_$(LIBRUBY_WITH_EXT)): $(LIBRUBY_SO_UPDATE)
ruby_pc = @ruby_pc@
-$(ruby_pc):
+ruby.pc: $(ruby_pc)
+$(ruby_pc): config.status
@./config.status --file=$@:$(srcdir)/template/ruby.pc.in
-ruby-runner.h: template/ruby-runner.h.in
+ruby-runner.h: template/ruby-runner.h.in config.status
@./config.status --file=$@:$(srcdir)/template/$(@F).in
-ruby-runner$(EXEEXT): ruby-runner.c ruby-runner.h
- $(Q) $(PURIFY) $(CC) $(CFLAGS) $(CPPFLAGS) -DRUBY_INSTALL_NAME=$(RUBY_INSTALL_NAME) $(LDFLAGS) $(LIBS) $(OUTFLAG)$@ $<
-
$(RBCONFIG): $(PREP)
rbconfig.rb: $(RBCONFIG)
@@ -300,15 +351,16 @@ uncommon.mk: $(srcdir)/common.mk
.PHONY: reconfig
reconfig-args = $(srcdir)/$(CONFIGURE) $(configure_args)
config.status-args = ./config.status --recheck
-reconfig-exec-0 = test -t 1 && { CONFIGURE_TTY=yes; export CONFIGURE_TTY; }; exec 3>&1; exit `exec 4>&1; { "$$@" 3>&- 4>&-; echo $$? 1>&4; } | fgrep -v '(cached)' 1>&3 3>&- 4>&-`
+reconfig-exec-0 = test -t 1 && { : $${CONFIGURE_TTY=yes}; export CONFIGURE_TTY; }; exec 3>&1; exit `exec 4>&1; { "$$@" 3>&- 4>&-; echo $$? 1>&4; } | fgrep -v '(cached)' 1>&3 3>&- 4>&-`
reconfig-exec-1 = set -x; "$$@"
reconfig config.status: $(srcdir)/$(CONFIGURE) $(srcdir)/enc/Makefile.in \
$(srcdir)/include/ruby/version.h
@PWD= MINIRUBY="$(MINIRUBY)"; export MINIRUBY; \
+ warnflags="@warnflags@"; export warnflags; \
set $(SHELL) $($@-args); $(reconfig-exec-$(V))
-$(srcdir)/$(CONFIGURE): $(srcdir)/configure.in $(srcdir)/aclocal.m4
+$(srcdir)/$(CONFIGURE): $(srcdir)/configure.ac $(srcdir)/aclocal.m4
$(CHDIR) $(srcdir) && exec $(AUTOCONF) -o $(@F)
$(srcdir)/aclocal.m4:
@@ -316,6 +368,8 @@ $(srcdir)/aclocal.m4:
type $(ACLOCAL) >/dev/null 2>&1 && exec $(ACLOCAL); \
touch $(@F)
+prereq: $(srcdir)/$(CONFIGURE)
+
incs: id.h
all-incs: probes.h
@@ -334,7 +388,7 @@ lex.c: defs/keywords
else \
[ $(Q) ] && echo generating $@ || set -x; \
gperf -C -P -p -j1 -i 1 -g -o -t -N rb_reserved_word -k1,3,$$ $? \
- | sed 's/(long)&((\(struct stringpool_t\) *\*)0)->\(stringpool_[a-z0-9]*\)/offsetof(\1, \2)/g' \
+ | sed -f $(srcdir)/tool/gperf.sed \
> $@.tmp && \
$(MV) $@.tmp $@ && \
$(CP) $? $(srcdir)/defs/lex.c.src && \
@@ -343,7 +397,7 @@ lex.c: defs/keywords
JIS_PROPS_OPTIONS = -k1,3 -7 -c -j1 -i1 -t -C -P -t --ignore-case -H onig_jis_property_hash -Q onig_jis_property_pool -N onig_jis_property
-enc/jis/props.h: enc/jis/props.kwd
+$(srcdir)/enc/jis/props.h: enc/jis/props.kwd
$(MAKEDIRS) $(@D)
@set +e; \
if cmp -s $(?:.kwd=.src) $?; then \
@@ -352,7 +406,7 @@ enc/jis/props.h: enc/jis/props.kwd
else \
set -x; \
gperf $(JIS_PROPS_OPTIONS) $? | \
- sed 's/(int)(long)&((\([a-zA-Z_0-9 ]*[a-zA-Z_0-9]\) *\*)0)->\([a-zA-Z0-9_]*\),/(char)offsetof(\1, \2),/g' > $@ && \
+ sed -f $(srcdir)/tool/gperf.sed > $@ && \
$(CP) $? $(?:.kwd=.src) && \
$(CP) $@ $(?:.kwd=.h.blt); \
fi
@@ -361,11 +415,11 @@ enc/jis/props.h: enc/jis/props.kwd
@$(ECHO) compiling $<
$(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $<
-.s.@OBJEXT@:
+.$(ASMEXT).@OBJEXT@:
@$(ECHO) assembling $<
- $(Q) $(AS) $(ASFLAGS) -o $@ $<
+ $(Q) $(CC) $(ASFLAGS) -o $@ -c $<
-.c.S:
+.c.$(ASMEXT):
@$(ECHO) translating $<
$(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -S $<
@@ -391,11 +445,21 @@ probes.stamp: $(DTRACE_REBUILD_OBJS)
fi
$(Q) touch $@
-probes.@OBJEXT@: $(srcdir)/probes.d $(DTRACE_REBUILD:yes=probes.stamp)
+probes.$(OBJEXT): $(srcdir)/probes.d $(DTRACE_REBUILD:yes=probes.stamp)
@$(ECHO) processing probes in object files
$(Q) $(RM) $@
$(Q) $(DTRACE) -G -C $(INCFLAGS) -s $(srcdir)/probes.d -o $@ $(DTRACE_REBUILD_OBJS)
+main: mjit-headers
+yes-mjit-headers: $(MJIT_MIN_HEADER)
+clean-local::
+ $(Q)$(RM) $(MJIT_HEADER) $(MJIT_HEADER:.h=)$(MJIT_HEADER_SUFFIX:%=*).h
+ $(Q)$(RM) $(MJIT_MIN_HEADER) $(MJIT_MIN_HEADER:.h=)$(MJIT_HEADER_SUFFIX:%=*).h
+ $(Q)$(RM) $(MJIT_HEADER_INSTALL_DIR)/rb_mjit_min_header-*.h
+ $(Q)$(RM) $(TIMESTAMPDIR)/$(MJIT_HEADER:.h=)$(MJIT_HEADER_SUFFIX).time mjit_config.h
+ $(Q)$(RM) -r mjit_build_dir.*
+ -$(Q) $(RMDIRS) $(MJIT_HEADER_INSTALL_DIR) 2> $(NULL) || exit 0
+
# DTrace static library hacks described here:
# http://mail.opensolaris.org/pipermail/dtrace-discuss/2005-August/000207.html
ruby-glommed.$(OBJEXT):
@@ -406,6 +470,7 @@ clean-local::
$(Q)$(RM) ext/extinit.c ext/extinit.$(OBJEXT) ext/ripper/y.output \
enc/encinit.c enc/encinit.$(OBJEXT)
-$(Q)$(RM) $(pkgconfig_DATA)
+ -$(Q)$(RMALL) exe/ ruby-runner.$(OBJEXT) ruby-runner.h *.dSYM
distclean-local::
$(Q)$(RM) ext/config.cache $(RBCONFIG) Doxyfile
@@ -413,28 +478,43 @@ distclean-local::
-$(Q)$(RM) $(INSTALLED_LIST) $(arch_hdrdir)/ruby/config.h verconf.h
-$(Q)$(RMDIRS) $(arch_hdrdir)/ruby 2> /dev/null || true
-clean-ext distclean-ext realclean-ext::
- @cd ext 2>/dev/null || exit 0; set dummy `echo "${EXTS}" | tr , ' '`; shift; \
+ext/clean.sub gems/clean.sub:: ext/clean.mk
+ext/distclean.sub gems/distclean.sub:: ext/distclean.mk
+ext/realclean.sub gems/realclean.sub:: ext/realclean.mk
+
+ext/clean.mk ext/distclean.mk ext/realclean.mk::
+ -$(Q) if [ -f $(EXTS_MK) ]; then exec $(MAKE) -f $(EXTS_MK) $(@F:.mk=); fi
+
+ext/clean:: ext/clean.sub
+ext/distclean:: ext/distclean.sub
+ext/realclean:: ext/realclean.sub
+gems/clean:: gems/clean.sub
+gems/distclean:: gems/distclean.sub
+gems/realclean:: gems/realclean.sub
+
+ext/clean.sub ext/distclean.sub ext/realclean.sub \
+gems/clean.sub gems/distclean.sub gems/realclean.sub::
+ $(Q) set dummy `echo "${EXTS}" | tr , ' '`; shift; \
test "$$#" = 0 && set .; \
set dummy `\
- find "$$@" -name Makefile -print | sed 's:^\./::;s:/Makefile$$::' | sort; \
+ cd $(@D) 2>/dev/null && \
+ find "$$@" \( -name Makefile -o -name exts.mk \) -print | \
+ sed -n 's:^\./::;s:^:$(@D)/:;s:/[^/][^/]*$$::p' | sort -u; \
`; shift; \
- cd ..; \
for dir do \
- echo $(@:-ext=)ing "$$dir"; \
- (cd "ext/$$dir" && exec $(MAKE) $(MFLAGS) $(@:-ext=)) && \
- case "$@" in \
- *distclean-ext*|*realclean-ext*) \
- $(RMDIRS) "ext/$$dir" 2> /dev/null || true;; \
- esac; \
- done
- -$(Q)$(RM) ext/extinit.$(OBJEXT)
-
-distclean-ext realclean-ext::
- -$(Q)$(RM) ext/extinit.c
- -$(Q)$(RMDIR) ext 2> /dev/null || true
-
-clean-extout:
+ $(RM) "$$dir/exts.mk"; \
+ if [ -f "$$dir/Makefile" ]; then \
+ echo $(@F:.sub=)ing "$$dir"; \
+ (cd "$$dir" && exec $(MAKE) $(mflags) $(@F:.sub=)); \
+ fi; \
+ done || true
+
+ext/distclean ext/realclean gems/distclean gems/realclean::
+ $(Q) set dummy `echo "${EXTS}" | tr , ' '`; shift; \
+ test "$$#" = 0 && set .; \
+ cd $(@D) 2>/dev/null && \
+ find "$$@" -type d -empty -exec $(RMDIRS) {} + 2> /dev/null || true
+ $(Q) $(RMDIRS) $(@D) 2> /dev/null || true
clean-enc distclean-enc realclean-enc:
@test -f "$(ENC_MK)" || exit 0; \
@@ -447,93 +527,59 @@ ext/extinit.$(OBJEXT): ext/extinit.c $(SETUP)
enc/encinit.$(OBJEXT): enc/encinit.c $(SETUP)
+cont.$(OBJEXT): $(COROUTINE_H)
+
+test-bundled-gems-run:
+ $(Q) set -e; while read gem _; do \
+ echo testing $$gem gem && \
+ $(XRUBY) -C $(srcdir)/gems/src/$$gem -Ilib ../../../.bundle/bin/rake; \
+ done < $(srcdir)/gems/bundled_gems
+
update-src::
@$(CHDIR) "$(srcdir)" && LC_TIME=C exec $(VCSUP)
update-download:: update-config_files
-after-update:: common-srcs
+after-update:: prereq
-update-mspec:
- @$(CHDIR) $(srcdir); \
- if [ -d spec/mspec ]; then \
- echo updating mspec ...; \
- $(Q1:0=:) set -x; \
- cd spec/mspec && \
- exec git pull; \
- else \
- echo retrieving mspec ...; \
- $(Q1:0=:) set -x; \
- exec git clone $(MSPEC_GIT_URL) spec/mspec; \
- fi
- $(Q)cd $(srcdir)/spec/mspec && exec git --no-pager log -1 --oneline
-
-update-rubyspec: update-mspec
- @$(CHDIR) $(srcdir); \
- if [ -d spec/rubyspec ]; then \
- echo updating rubyspec ...; \
- $(Q1:0=:) set -x; \
- cd spec/rubyspec && \
- exec git pull; \
- else \
- echo retrieving rubyspec ...; \
- $(Q1:0=:) set -x; \
- exec git clone $(RUBYSPEC_GIT_URL) spec/rubyspec; \
- fi
- $(Q)cd $(srcdir)/spec/rubyspec && exec git --no-pager log -1 --oneline
+gcov:
+ $(Q) $(BASERUBY) $(srcdir)/tool/run-gcov.rb
+
+lcov:
+ $(Q) $(BASERUBY) $(srcdir)/tool/run-lcov.rb
-test-rubyspec-precheck:
- @if [ ! -d $(srcdir)/spec/rubyspec ]; then echo No rubyspec here. make update-rubyspec first.; exit 1; fi
+update-benchmark-driver:
+ $(Q) $(srcdir)/tool/git-refresh -C $(srcdir)/benchmark $(Q1:0=-q) \
+ --branch $(BENCHMARK_DRIVER_GIT_REF) \
+ $(BENCHMARK_DRIVER_GIT_URL) benchmark-driver $(GIT_OPTS)
update-doclie:
- @$(CHDIR) $(srcdir); \
- if [ -d coverage/doclie ]; then \
- echo updating doclie ...; \
- $(Q1:0=:) set -x; \
- cd coverage/doclie && \
- git fetch && \
- exec git checkout $(DOCLIE_GIT_REF); \
- else \
- echo retrieving doclie ...; \
- $(Q1:0=:) set -x; \
- exec git clone --branch $(DOCLIE_GIT_REF) $(DOCLIE_GIT_URL) coverage/doclie; \
- fi
+ $(Q) $(srcdir)/tool/git-refresh -C $(srcdir)/coverage $(Q1:0=-q) \
+ --branch $(DOCLIE_GIT_REF) \
+ $(DOCLIE_GIT_URL) doclie $(GIT_OPTS)
update-simplecov-html:
- @$(CHDIR) $(srcdir); \
- if [ -d coverage/simplecov-html ]; then \
- echo updating simplecov-html ...; \
- $(Q1:0=:) set -x; \
- cd coverage/simplecov-html && \
- git fetch && \
- exec git checkout $(SIMPLECOV_HTML_GIT_REF); \
- else \
- echo retrieving simplecov-html ...; \
- exec git clone --branch $(SIMPLECOV_HTML_GIT_REF) $(SIMPLECOV_HTML_GIT_URL) coverage/simplecov-html; \
- fi
+ $(Q) $(srcdir)/tool/git-refresh -C $(srcdir)/coverage $(Q1:0=-q) \
+ --branch $(SIMPLECOV_HTML_GIT_REF) \
+ $(SIMPLECOV_HTML_GIT_URL) simplecov-html $(GIT_OPTS)
update-simplecov:
- @$(CHDIR) $(srcdir); \
- if [ -d coverage/simplecov ]; then \
- echo updating simplecov ...; \
- $(Q1:0=:) set -x; \
- cd coverage/simplecov && \
- git fetch && \
- exec git checkout $(SIMPLECOV_GIT_REF); \
- else \
- echo retrieving simplecov ...; \
- $(Q1:0=:) set -x; \
- exec git clone --branch $(SIMPLECOV_GIT_REF) $(SIMPLECOV_GIT_URL) coverage/simplecov; \
- fi
+ $(Q) $(srcdir)/tool/git-refresh -C $(srcdir)/coverage $(Q1:0=-q) \
+ --branch $(SIMPLECOV_GIT_REF) \
+ $(SIMPLECOV_GIT_URL) simplecov $(GIT_OPTS)
update-coverage: update-simplecov update-simplecov-html update-doclie
+update-known-errors:
+ errno --list | cut -d' ' -f1 | sort -u - $(srcdir)/defs/known_errors.def | \
+ $(IFCHANGE) $(srcdir)/defs/known_errors.def -
+
INSNS = opt_sc.inc optinsn.inc optunifs.inc insns.inc insns_info.inc \
- vmtc.inc vm.inc
+ vmtc.inc vm.inc mjit_compile.inc
$(INSNS): $(srcdir)/insns.def vm_opts.h \
$(srcdir)/defs/opt_operand.def $(srcdir)/defs/opt_insn_unif.def \
- $(srcdir)/tool/instruction.rb $(srcdir)/tool/insns2vm.rb
+ $(srcdir)/tool/insns2vm.rb
$(ECHO) generating $@
$(Q) $(BASERUBY) -Ku $(srcdir)/tool/insns2vm.rb $(INSNS2VMOPT) $@
@@ -547,3 +593,41 @@ loadpath: verconf.h
un-runnable:
$(ECHO) cannot make runnable, configure with --enable-load-relative.
$(Q) exit 1
+
+mjit_config.h:
+ $(ECHO) making $@
+ @{ \
+ . $(srcdir)/tool/mjit_archflag.sh; \
+ parse_arch_flags "$(UNIVERSAL_ARCHNAMES)" $(ARCH_FLAG); \
+ test "$(Q)" = @ || set -x; \
+ echo '#ifndef RUBY_MJIT_CONFIG_H'; \
+ echo '#define RUBY_MJIT_CONFIG_H 1'; \
+ echo; \
+ sep=; \
+ echo '#ifdef LOAD_RELATIVE'; \
+ quote MJIT_HEADER_INSTALL_DIR "/$(MJIT_HEADER_INSTALL_DIR)"; \
+ echo '#else'; \
+ quote MJIT_HEADER_INSTALL_DIR "$(rubyarchhdrdir)"; \
+ echo '#endif'; \
+ quote MJIT_MIN_HEADER_NAME "$(MJIT_MIN_HEADER_NAME)"; \
+ sep=,; \
+ quote "MJIT_CC_COMMON " $(MJIT_CC); \
+ quote "MJIT_CFLAGS MJIT_ARCHFLAG" $(MJIT_CFLAGS); \
+ quote "MJIT_OPTFLAGS " $(MJIT_OPTFLAGS); \
+ quote "MJIT_DEBUGFLAGS " $(MJIT_DEBUGFLAGS); \
+ quote "MJIT_LDSHARED " $(MJIT_LDSHARED); \
+ quote "MJIT_DLDFLAGS MJIT_ARCHFLAG" $(MJIT_DLDFLAGS); \
+ quote "MJIT_LIBS " $(LIBRUBYARG_SHARED); \
+ quote 'PRELOADENV "@PRELOADENV@"'; \
+ indent=$${archs:+' '}; \
+ define_arch_flags; \
+ echo; \
+ echo '#endif /* RUBY_MJIT_CONFIG_H */'; \
+ } > $@
+
+yes-test-almost yes-test-all: mjit_build_dir.$(SOEXT)
+mjit_build_dir.$(SOEXT): $(MJIT_MIN_HEADER) $(srcdir)/ruby-runner.c ruby-runner.h
+ $(ECHO) making $@
+ $(Q) $(DLDSHARED) $(MJIT_DLDFLAGS) $(ARCH_FLAG) $(CFLAGS) $(CPPFLAGS) \
+ -DMAKE_MJIT_BUILD_DIR=1 -DMJIT_MIN_HEADER='"$(MJIT_MIN_HEADER)"' \
+ $(OUTFLAG)$@ $(srcdir)/ruby-runner.c
diff --git a/NEWS b/NEWS
index 1b64c5b6cc..e7684e15bb 100644
--- a/NEWS
+++ b/NEWS
@@ -1,342 +1,686 @@
# -*- rdoc -*-
-= NEWS for Ruby 2.4.0
+= NEWS for Ruby 2.6.0
This document is a list of user visible feature changes made between
releases except for bug fixes.
-Note that each entry is kept so brief that no reason behind or
-reference information is supplied with. For a full list of changes
-with all sufficient information, see the ChangeLog file or Redmine
+Note that each entry is kept so brief that no reason behind or reference
+information is supplied with. For a full list of changes with all
+sufficient information, see the ChangeLog file or Redmine
(e.g. <tt>https://bugs.ruby-lang.org/issues/$FEATURE_OR_BUG_NUMBER</tt>)
-== Changes since the 2.3.0 release
+== Changes since the 2.5.0 release
=== Language changes
-* Multiple assignment in conditional expression is now allowed.
- [Feature #10617]
+* <code>$SAFE</code> now is a process global state and can be set to 0 again. [Feature #14250]
-* Refinements is enabled at method by Symbol#to_proc. [Feature #9451]
+* Refinements take place at block passing. [Feature #14223]
-* Refinements is enabled with Kernel#send and BasicObject#__send__.
- [Feature #11476]
+* Refinements take place at Kernel#public_send. [Feature #15326]
-* Rescue modifier now applicable to method arguments.
- [Feature #12686]
+* Refinements take place at Kernel#respond_to?. [Feature #15327]
+
+* +else+ without +rescue+ now causes a syntax error. [EXPERIMENTAL] [Feature #14606]
+
+* Constant names may start with a non-ASCII capital letter. [Feature #13770]
+
+* Endless ranges are introduced. You can use a Range that has no end,
+ like <code>(0..)</code> (or similarly <code>(0...)</code>). [Feature #12912]
+
+ The following shows typical use cases:
+
+ ary[1..] # identical to ary[1..-1]
+ (1...).each {|index| block } # infinite loop from index 1
+ ary.zip(1..) {|elem, index| block } # ary.each.with_index(1) { }
+
+* The "shadowing outer local variable" warning is removed. [Feature #12490]
+
+ You can now write the following without warning:
+
+ user = users.find {|user| cond(user) }
+
+* Print +cause+ of the exception if the exception is not caught and printed
+ its backtraces and error message. [Feature #8257]
+
+* The flip-flop syntax is deprecated. [Feature #5400]
+
+* Non-Symbol keys in a keyword arguments hash was prohibited at 2.6.0, but
+ now allowed again. [Bug #15658]
=== Core classes updates (outstanding ones only)
-* Array
+[Array]
+
+ [New methods]
+
+ * Added Array#union and Array#difference instance methods. [Feature #14097]
+
+ [Modified methods]
+
+ * Array#to_h now accepts a block that maps elements to new key/value pairs. [Feature #15143]
+
+ [Aliased methods]
+
+ * Array#filter is a new alias for Array#select. [Feature #13784]
+ * Array#filter! is a new alias for Array#select!. [Feature #13784]
+
+[Binding]
+
+ [New methods]
+
+ * Added Binding#source_location. [Feature #14230]
+
+ This method returns the source location of the binding, a 2-element
+ array of <code>__FILE__</code> and <code>__LINE__</code>.
+ Traditionally, the same information could be retrieved by
+ <code>eval("[__FILE__, __LINE__]", binding)</code>, but we are
+ planning to change this behavior so that Kernel#eval ignores
+ binding's source location [Bug #4352]. So, users should use this
+ newly-introduced method instead of Kernel#eval.
+
+[Dir]
+
+ [New methods]
+
+ * Added Dir#each_child and Dir#children instance methods. [Feature #13969]
+
+[Enumerable]
+
+ [New methods]
+
+ * Enumerable#chain returns an enumerator object that iterates over the
+ elements of the receiver and then those of each argument
+ in sequence. [Feature #15144]
+
+ [Modified methods]
+
+ * Enumerable#to_h now accepts a block that maps elements to new key/value pairs. [Feature #15143]
+
+ [Aliased methods]
+
+ * Enumerable#filter is a new alias for Enumerable#select. [Feature #13784]
+
+[Enumerator::ArithmeticSequence]
+
+ * This is a new class to represent a generator of an arithmetic sequence,
+ that is a number sequence defined by a common difference. It can be used
+ for representing what is similar to Python's slice. You can get an
+ instance of this class from Numeric#step and Range#step.
+
+[Enumerator::Chain]
+
+ * This is a new class to represent a chain of enumerables that works as a
+ single enumerator, generated by such methods as Enumerable#chain and
+ Enumerator#+.
+
+[Enumerator::Lazy]
+
+ [Aliased methods]
+
+ * Enumerator::Lazy#filter is a new alias for
+ Enumerator::Lazy#select. [Feature #13784]
+
+[Enumerator]
+
+ [New methods]
+
+ * Enumerator#+ returns an enumerator object that iterates over the
+ elements of the receiver and then those of the other operand. [Feature #15144]
+
+[ENV]
+
+ [Modified methods]
+
+ * ENV.to_h now accepts a block that maps names and values to new keys and values. [Feature #15143]
+
+[Exception]
+
+ [New options]
+
+ * Exception#full_message takes +:highlight+ and +:order+
+ options. [Bug #14324]
+
+[Hash]
+
+ [Modified methods]
+
+ * Hash#merge, Hash#merge!, and Hash#update now accept multiple
+ arguments. [Feature #15111]
+
+ * Hash#to_h now accepts a block that maps keys and values to new keys and values. [Feature #15143]
+
+ [Aliased methods]
+
+ * Hash#filter is a new alias for Hash#select. [Feature #13784]
+
+ * Hash#filter! is a new alias for Hash#select!. [Feature #13784]
+
+[IO]
+
+ [New options]
+
+ * Added new mode character <code>'x'</code> to open files for exclusive
+ access. [Feature #11258]
+
+[Kernel]
+
+ [Aliased methods]
+
+ * Kernel#then is a new alias for Kernel#yield_self. [Feature #14594]
+
+ [New options]
+
+ * Kernel#Complex, Kernel#Float, Kernel#Integer, and
+ Kernel#Rational take an +:exception+ option to specify the way of
+ error handling. [Feature #12732]
+
+ * Kernel#system takes an +:exception+ option to raise an exception
+ on failure. [Feature #14386]
+
+ [Incompatible changes]
+
+ * Kernel#system and Kernel#exec do not close non-standard file descriptors
+ (the default of the +:close_others+ option is changed to +false+,
+ but we still set the +FD_CLOEXEC+ flag on descriptors we
+ create). [Misc #14907]
+
+[KeyError]
+
+ [New options]
+
+ * KeyError.new accepts +:receiver+ and +:key+ options to set receiver and
+ key in Ruby code. [Feature #14313]
+
+[Method]
+
+ [New methods]
+
+ * Added Method#<< and Method#>> for Proc composition. [Feature #6284]
+
+[Module]
+
+ [Modified methods]
+
+ * Module#method_defined?, Module#private_method_defined?, and
+ Module#protected_method_defined? now accept the second
+ parameter as optional. If it is +true+ (the default value), it checks
+ ancestor modules/classes, or checks only the class itself. [Feature #14944]
+
+[NameError]
+
+ [New options]
+
+ * NameError.new accepts a +:receiver+ option to set receiver in Ruby
+ code. [Feature #14313]
+
+[NilClass]
+
+ [New methods]
+
+ * NilClass#=~ is added for compatibility. [Feature #15231]
+
+[NoMethodError]
+
+ [New options]
- * Array#max and Array#min. [Feature #12172]
- This may cause a tiny incompatibility: if you redefine
- Enumerable#max and call max to an Array, your redefinition will be
- now ignored. You should also redefine Array#max.
+ * NoMethodError.new accepts a +:receiver+ option to set receiver in Ruby
+ code. [Feature #14313]
- * Array#sum [Feature #12217]
- This is different from Enumerable#sum in that Array#sum doesn't depend on
- the definition of each method.
+[Numeric]
- * Array#concat [Feature #12333]
- Now takes multiple arguments.
+ [Incompatible changes]
-* Comparable
+ * Numeric#step now returns an instance of the Enumerator::ArithmeticSequence
+ class rather than one of the Enumerator class.
- * Comparable#clamp. [Feature #10594]
+[OpenStruct]
-* Dir
+ [Modified methods]
- * Dir.empty?. [Feature #10121]
+ * OpenStruct#to_h now accepts a block that maps keys and values to new keys and values. [Feature #15143]
-* Enumerable
+[Proc]
- * Enumerable#sum [Feature #12217]
- * Enumerable#uniq [Feature #11090]
- * Enumerable#chunk called without a block now return an Enumerator
- [Feature #2172]
+ [New methods]
-* Enumerator::Lazy
+ * Added Proc#<< and Proc#>> for Proc composition. [Feature #6284]
- * Enumerator::Lazy#chunk_while [GH-1186]
- * Enumerator::Lazy#uniq [Feature #11090]
+ [Incompatible changes]
-* File
+ * Proc#call doesn't change <code>$SAFE</code> any more. [Feature #14250]
- * File.empty?. [Feature #9969]
+[Random]
-* Float
+ [New methods]
- * Float#ceil, Float#floor, and Float#truncate now take an optional
- digits, as well as Float#round. [Feature #12245]
+ * Added Random.bytes. [Feature #4938]
- * Float#round now takes an optional keyword argument, half option, and
- the default behavior is round-to-nearest-even now. [Bug #12548]
+[Range]
-* Hash
+ [New methods]
- * Hash#transform_values and Hash#transform_values! [Feature #12512]
- * Hash#compact and Hash#compact! [Feature #11818]
+ * Added Range#% instance method. [Feature #14697]
-* Integer
+ [Incompatible changes]
- * Integer#ceil, Integer#floor, and Integer#truncate now take an optional
- digits, as well as Integer#round. [Feature #12245]
+ * Range#=== now uses the +#cover?+ instead of the +#include?+ method. [Feature #14575]
+ * Range#cover? now accepts a Range object. [Feature #14473]
+ * Range#step now returns an instance of the Enumerator::ArithmeticSequence
+ class rather than one of the Enumerator class.
- * Fixnum and Bignum are unified into Integer [Feature #12005]
+[Regexp/String]
- * Integer#digits for extracting columns of place-value notation [Feature #12447]
+ * Update Unicode version to 12.1.0, adding support for
+ U+32FF SQUARE ERA NAME REIWA [Feature #15195]
- * Int#round now takes an optional keyword argument, half option, and the
- default behavior is round-to-nearest-even now. [Bug #12548]
+ * Update Unicode version and Emoji version from 11.0.0 to
+ 12.0.0. [Feature #15321]
-* IO
+ * Update Unicode version from 10.0.0 to 11.0.0. [Feature #14802]
- * IO#gets, IO#readline, IO#each_line, IO#readlines, IO.foreach now takes
- an optional keyword argument, chomp flag. [Feature #12553]
+ This includes a rewrite of the grapheme cluster (/\X/) algorithm
+ and special-casing for Georgian MTAVRULI on String#downcase.
-* Kernel
+ * Update Emoji version from 5.0 to 11.0.0 [Feature #14802]
- * Kernel#clone now takes an optional keyword argument, freeze flag.
- [Feature #12300]
+[RubyVM::AbstractSyntaxTree]
-* MatchData
+ [New methods]
- * MatchData#named_captures [Feature #11999]
- * MatchData#values_at supports named captures [Feature #9179]
+ * RubyVM::AbstractSyntaxTree.parse parses a given string and returns AST
+ nodes. [experimental]
-* Module
+ * RubyVM::AbstractSyntaxTree.parse_file parses a given file and returns AST
+ nodes. [experimental]
- * Module.used_modules [Feature #7418]
- * Module#refine accepts a module as the argument now. [Feature #12534]
+ * RubyVM::AbstractSyntaxTree.of returns AST nodes of the given proc or method.
+ [experimental]
-* Numeric
+[RubyVM]
- * Numeric#finite?, Numeric#infinite? [Feature #12039]
+ [New methods]
-* Process
+ * RubyVM.resolve_feature_path identifies the file that will be loaded by
+ "require(feature)". [experimental] [Feature #15230]
- * Support CLOCK_MONOTONIC_RAW_APPROX, CLOCK_UPTIME_RAW, and
- CLOCK_UPTIME_RAW_APPROX which are introduced by macOS 10.12.
+[String]
-* Rational
+ * String#crypt is now deprecated. [Feature #14915]
- * Rational#round now takes an optional keyword argument, half option, and
- the default behavior is round-to-nearest-even now. [Bug #12548]
+ [New features]
-* Regexp
+ * String#split yields each substring to the block if given. [Feature #4780]
- * Regexp#match? [Feature #8110]
- This returns bool and doesn't save backref.
+[Struct]
-* Regexp/String: Updated Unicode version from 8.0.0 to 9.0.0 [Feature #12513]
+ [Modified methods]
-* RubyVM::Env
+ * Struct#to_h now accepts a block that maps keys and values to new keys and values. [Feature #15143]
- * RubyVM::Env was removed.
+ [Aliased method]
-* String
+ * Struct#filter is a new alias for Struct#select. [Feature #13784]
- * String#upcase, String#downcase, String#capitalize, String#swapcase and
- their bang variants work for all of Unicode, and are no longer limited
- to ASCII. Supported encodings are UTF-8, UTF-16BE/LE, UTF-32BE/LE, and
- ISO-8859-1~16. Variations are available with options. See the documentation
- of String#downcase for details. [Feature #10085]
+[Time]
- * String.new(capacity: size) [Feature #12024]
+ [New features]
- * String#concat, String#prepend [Feature #12333]
- Now takes multiple arguments.
+ * Time.new and Time#getlocal accept a timezone object as well as
+ a UTC offset string. Time#+, Time#-, and Time#succ also preserve
+ the timezone. [Feature #14850]
-* Symbol
+[TracePoint]
- * Symbol#match now returns MatchData. [Bug #11991]
+ [New features]
- * Symbol#upcase, Symbol#downcase, Symbol#capitalize, and Symbol#swapcase now
- work for all of Unicode. See the documentation of String#downcase
- for details. [Feature #10085]
+ * "script_compiled" event is supported. [Feature #15287]
-* Thread
+ [New methods]
- * Thread#report_on_exception and Thread.report_on_exception
- [Feature #6647]
+ * TracePoint#parameters [Feature #14694]
-* TracePoint
+ * TracePoint#instruction_sequence [Feature #15287]
- * TracePoint#callee_id [Feature #12747]
+ * TracePoint#eval_script [Feature #15287]
-* Warning
+ [Modified methods]
- * New module named Warning is introduced. By default it has only
- one singleton method, named warn. This makes it possible for
- 3rd-party libraries to control the way warnings are handled.
- [Feature #12299]
+ * TracePoint#enable accepts new keywords "target:" and "target_line:".
+ [Feature #15289]
=== Stdlib updates (outstanding ones only)
-* CGI
+[BigDecimal]
- * Don't allow , as a separator [Bug #12791]
+ Update to version 1.4.0. This version includes several compatibility
+ issues, see Compatibility issues section below for details.
-* CSV
+ [Modified methods]
- * Add a liberal_parsing option. [Feature #11839]
+ * BigDecimal() accepts the new keyword "exception:" similar to Float().
-* IPAddr
+ [Note for the differences among recent versions]
- * IPAddr#== and IPAddr#<=> no longer raise an exception if coercion fails.
- [Bug #12799]
+ You should want to know the differences among recent versions of bigdecimal.
+ Please select the suitable version of bigdecimal according to the following
+ information.
-* IRB
+ * 1.3.5 has BigDecimal.new without "exception:" keyword. You can see the
+ deprecation warning of BigDecimal.new when you specify "-w" option.
+ BigDecimal(), BigDecimal.new, and Object#to_d methods are the same.
- * Binding#irb: Start a REPL session like `binding.pry` at r56624.
+ * 1.4.0 has BigDecimal.new with "exception:" keyword. You always see the
+ deprecation warning of BigDecimal.new. Object#to_d method is different
+ from BigDecimal() and BigDecimal.new.
-* Logger
+ * 2.0.0 will be released soon after releasing Ruby 2.6.0. This version
+ will not have the BigDecimal.new method.
- * Allow specifying logger parameters in constructor such
- as level, progname, datetime_format, formatter. [Feature #12224]
- * Add shift_period_suffix option. [Feature #10772]
+[Bundler]
-* Net::HTTP
+ * Add Bundler to Standard Library. [Feature #12733]
- * New method: Net::HTTP.post [Feature #12375]
+ * Use 1.17.2, the latest stable version.
-* OpenSSL
+[Coverage]
- * OpenSSL is extracted as a gem and the upstream has been migrated to
- https://github.com/ruby/openssl. OpenSSL still remains as a default gem.
- Refer to its History.md for the full release note. [Feature #9612]
+ A oneshot_lines mode is added. [Feature #15022]
-* optparse
+ This mode checks "whether each line was executed at least once or not",
+ instead of "how many times each line was executed".
+ A hook for each line is fired at most once, and after it is fired
+ the hook flag is removed, i.e., it runs with zero overhead.
- * Add an into option. [Feature #11191]
+ [New options]
-* pathname
+ * Add +:oneshot_lines+ keyword argument to Coverage.start.
- * New method: Pathname#empty? [Feature #12596]
+ * Add +:stop+ and +:clear+ keyword arguments to Coverage.result.
+ If +clear+ is true, it clears the counters to zero.
+ If +stop+ is true, it disables coverage measurement.
-* Readline
+ [New methods]
- * Readline.quoting_detection_proc and Readline.quoting_detection_proc=
- [Feature #12659]
+ * Coverage.line_stub, which is a simple helper function that
+ creates the "stub" of line coverage from a given source code.
-* set
+[CSV]
- * New methods: Set#compare_by_identity and Set#compare_by_identity?.
- [Feature #12210]
+ * Upgrade to 3.0.2. This includes performance improvements especially
+ for writing. Writing is about 2 times faster.
+ See https://github.com/ruby/csv/blob/master/NEWS.md.
-* WEBrick
+[ERB]
- * Don't allow , as a separator [Bug #12791]
+ [New options]
+
+ * Add +:trim_mode+ and +:eoutvar+ keyword arguments to ERB.new.
+ Now non-keyword arguments other than the first one are softly deprecated
+ and will be removed when Ruby 2.5 becomes EOL. [Feature #14256]
+
+ * erb command's <tt>-S</tt> option is deprecated, and will be removed
+ in the next version.
+
+[FileUtils]
+
+ [New methods]
+
+ * FileUtils#cp_lr. [Feature #4189]
+
+[Matrix]
+
+ [New methods]
+
+ * Matrix#antisymmetric?, Matrix#skew_symmetric?
+
+ * Matrix#map!, Matrix#collect! [Feature #14151]
+
+ * Matrix#[]=
+
+ * Vector#map!, Vector#collect!
+
+ * Vector#[]=
+
+[Net]
+
+ [New options]
+
+ * Add +:write_timeout+ keyword argument to Net::HTTP.new. [Feature #13396]
+
+ [New methods]
+
+ * Add Net::HTTP#write_timeout and Net::HTTP#write_timeout=. [Feature #13396]
+
+ [New constant]
+
+ * Add Net::HTTPClientException to deprecate Net::HTTPServerException,
+ whose name is misleading. [Bug #14688]
+
+ [Net::IMAP]
+
+ * Add Server Name Indication (SNI) support. [Feature #15594]
+
+[NKF]
+
+ * Upgrade to nkf v2.1.5
+
+[Psych]
+
+ * Upgrade to Psych 3.1.0
+
+[RDoc]
+
+ * Become about 2 times faster.
+
+ * Use SOURCE_DATE_EPOCH to generate files.
+
+ * Fix method line number that slipped off.
+
+ * Enable <code>--width</code>, <code>--exclude</code>,
+ and <code>--line-numbers</code> that were ignored.
+
+ * Add support for blockquote by ">>>" in default markup notation.
+
+ * Add support for "Raises" lines in TomDoc notation.
+
+ * Fix syntax error output.
+
+ * Fix many parsing bugs.
+
+[REXML]
+
+ * Upgrade to REXML 3.1.9.
+ See https://github.com/ruby/rexml/blob/master/NEWS.md.
+
+ [Improved some XPath implementations]
+
+ * <code>concat()</code> function: Stringify all arguments before concatenating.
+
+ * <code>string()</code> function: Support context node.
+
+ * <code>string()</code> function: Support processing instruction node.
+
+ * Support <code>"*:#{ELEMENT_NAME}"</code> syntax in XPath 2.0.
+
+ [Fixed some XPath implementations]
+
+ * <code>"//#{ELEMENT_NAME}[#{POSITION}]"</code> case
+
+ * <code>string()</code> function: Fix <code>function(document)</code>
+ returns nodes that are out of root elements.
+
+ * <code>"/ #{ELEMENT_NAME} "</code> case
+
+ * <code>"/ #{ELEMENT_NAME} [ #{PREDICATE} ]"</code> case
+
+ * <code>"/ #{AXIS}::#{ELEMENT_NAME}"</code> case
+
+ * <code>"#{N}-#{M}"</code> case: One or more white spaces were required
+ before <code>"-"</code>
+
+ * <code>"/child::node()"</code> case
+
+ * <code>"#{FUNCTION}()/#{PATH}"</code> case
+
+ * <code>"@#{ATTRIBUTE}/parent::"</code> case
+
+ * <code>"name(#{NODE_SET})"</code> case
+
+[CSV]
+
+ * Upgrade to 3.0.9.
+ See https://github.com/ruby/csv/blob/master/NEWS.md.
+
+[Date]
+
+ * Date.jisx0301, Date#jisx0301, and Date.parse provisionally support the
+ new Japanese era as an informal extension, until the new JIS X 0301 is
+ issued. [Feature #15742]
+
+[RSS]
+
+ [New options]
+
+ * RSS::Parser.parse now accepts options as Hash. +:validate+ ,
+ +:ignore_unknown_element+ , +:parser_class+ options are available.
+
+[RubyGems]
+
+ * Upgrade to RubyGems 3.0.1
+
+ * https://blog.rubygems.org/2018/12/19/3.0.0-released.html
+
+ * https://blog.rubygems.org/2018/12/23/3.0.1-released.html
+
+[Set]
+
+ [Aliased methods]
+
+ * Set#filter! is a new alias for Set#select!. [Feature #13784]
+
+[URI]
+
+ [New constant]
+
+ * Add URI::File to handle the file URI scheme. [Feature #14035]
=== Compatibility issues (excluding feature bug fixes)
-* Array#sum and Enumerable#sum are implemented. [Feature #12217]
- Ruby itself has no compatibility problem because Ruby didn't have sum method
- for arrays before Ruby 2.4.
- However many third party gems, activesupport, facets, simple_stats, etc,
- defines sum method. These implementations are mostly compatible but
- there are subtle differences.
- Ruby's sum method should be mostly compatible but it is impossible to
- be perfectly compatible with all of them.
-
-* Fixnum and Bignum are unified into Integer [Feature #12005]
- Fixnum class and Bignum class is removed.
- Integer class is changed from abstract class to concrete class.
- For example, 0 is an instance of Integer: 0.class returns Integer.
- The constants Fixnum and Bignum is bound to Integer.
- So obj.kind_of?(Fixnum) works as obj.kind_of?(Integer).
- At C-level, Fixnum object and Bignum object should be distinguished by
- FIXNUM_P(obj) and RB_TYPE_P(obj, T_BIGNUM).
- RUBY_INTEGER_UNIFICATION can be used to detect this feature at C-level.
- 0.class == Integer can be used to detect this feature at Ruby-level.
- The C-level constants, rb_cFixnum and rb_cBignum, is removed.
- They can cause compilation failure.
-
-* String/Symbol#upcase/downcase/swapcase/capitalize(!) now work for all of
- Unicode, not only for ASCII. [Feature #10085]
- No change is needed if the data is in ASCII anyway or if the limitation
- to ASCII was only tolerated while waiting for a more extensive implementation.
- A change (using the :ascii option) is needed in cases where Unicode data
- is processed, but the operation has to be limited to ASCII only.
- A good example of this are internationalized domain names.
-
-* TRUE / FALSE / NIL
- These constants are now obsoleted. [Feature #12574]
- Use true / false / nil resp. instead.
+[Dir]
-=== Stdlib compatibility issues (excluding feature bug fixes)
+ * Dir.glob with <code>'\0'</code>-separated pattern list will be deprecated,
+ and is now warned. [Feature #14643]
-* DateTime
+[File]
- * DateTime#to_time now preserves timezone. [Bug #12189]
+ * File.read, File.binread, File.write, File.binwrite, File.foreach, and
+ File.readlines do not invoke external commands even if the path starts
+ with the pipe character <code>'|'</code>. [Feature #14245]
-* RDoc
+[Object]
- * Update RDoc 5.0.0
+ * Object#=~ is deprecated. [Feature #15231]
-* RubyGems
+=== Stdlib compatibility issues (excluding feature bug fixes)
- * Update RubyGems 2.6.8
+* These standard libraries have been promoted to default gems.
-* shellwords
+ * e2mmap
+ * forwardable
+ * irb
+ * logger
+ * matrix
+ * mutex_m
+ * ostruct
+ * prime
+ * rexml
+ * rss
+ * shell
+ * sync
+ * thwait
+ * tracer
- * Shellwords.shellwords (shellsplit) treats the backslash as escape
- character only when followed by one of the following characters:
- $ ` " \ <newline>
- [Bug #10055]
+[BigDecimal]
-* Time
+ * The following methods are removed.
- * Time#to_time now preserves timezone. [Bug #12271]
+ * BigDecimal.allocate
+ * BigDecimal.ver
-* thread
+ * Every BigDecimal object is frozen. [Feature #13984]
- * the extension library is removed. Till 2.0 it was a pure ruby script
- "thread.rb", which has precedence over "thread.so", and has been provided
- in $LOADED_FEATURES since 2.1.
+ * BigDecimal() parses the given string similar to Float().
-* Tk
+ * String#to_d parses the receiver string similar to String#to_f.
- * Tk is removed from stdlib. [Feature #8539]
- https://github.com/ruby/tk is the new upstream.
+ * BigDecimal.new will be removed in version 2.0.
-* XMLRPC
+[Pathname]
- * XMLRPC is removed from stdlib. [Feature #12160][ruby-core:74239]
- https://github.com/ruby/xmlrpc is the new upstream.
+ * Pathname#read, Pathname#binread, Pathname#write, Pathname#binwrite,
+ Pathname#each_line and Pathname#readlines do not invoke external
+ commands even if the path starts with the pipe character <code>'|'</code>.
+ This follows [Feature #14245].
=== C API updates
-* ruby_show_version() will no longer exits the process, if
- RUBY_SHOW_COPYRIGHT_TO_DIE is set to 0. This will be the default in
- the future.
+=== Implementation improvements
+
+* Speedup Proc#call because we don't need to care about <code>$SAFE</code>
+ any more. [Feature #14318]
-* rb_gc_adjust_memory_usage() [Feature #12690]
+ With +lc_fizzbuzz+ benchmark which uses Proc#call many times we can
+ measure x1.4 improvements. [Bug #10212]
-=== Supported platform changes
+* Speedup block.call where +block+ is passed block parameter. [Feature #14330]
-* FreeBSD < 4 is no longer supported
+ Ruby 2.5 improves block passing performance. [Feature #14045]
-=== Implementation improvements
+ Additionally, Ruby 2.6 improves the performance of passed block calling.
+
+* Introduce an initial implementation of a JIT (Just-in-time) compiler. [Feature #14235] [experimental]
+
+ * <tt>--jit</tt> command line option is added to enable JIT. <tt>--jit-verbose=1</tt>
+ is good for inspection. See <tt>ruby --help</tt> for others.
+ * To generate machine code, this JIT compiler uses the C compiler used for building
+ the interpreter. Currently GCC, Clang, and Microsoft Visual C++ are supported for it.
+ * <tt>--disable-mjit-support</tt> option is added to configure. This is added for JIT debugging,
+ but if you get an error on building a header file for JIT, you can use this option to skip
+ building it as a workaround.
+ * rb_waitpid reimplemented on Unix-like platforms to maintain
+ compatibility with processes created for JIT [Bug #14867]
+
+* VM generator script renewal; makes the generated VM more optimized. [GH-1779]
-* In some condition, `[x, y].max` and `[x, y].min` are optimized
- so that a temporal array is not created. The concrete condition is
- an implementation detail: currently, the array literal must have no
- splat, must have at least one expression but literal, the length must
- be <= 0x100, and Array#max and min must not be redefined. It will work
- in most casual and real-life use case where it is written with intent
- to `Math.max(x, y)`.
+* Thread cache enabled for pthreads platforms (for Thread.new and
+ Thread.start). [Feature #14757]
-* Thread deadlock detection now shows their backtrace and dependency. [Feature #8214]
+* timer thread is eliminated for platforms with POSIX timers. [Misc #14937]
-* st_table (st.c) internal data structure is improved. [Feature #12142]
+* Transient Heap (theap) is supported. [Bug #14858] [Feature #14989]
+
+ theap is a managed heap for short-living memory objects. For example,
+ making a small and short-living Hash object is x2 faster. With rdoc benchmark,
+ we measured 6-7% performance improvement.
+
+* Native implementations (arm32, arm64, ppc64le, win32, win64, x86, amd64) of
+ coroutines to improve performance of Fiber significantly. [Feature #14739]
=== Miscellaneous changes
-* ChangeLog is removed from the repository.
- It is generated from commit messages in Subversion by `make dist`.
- Also note that now people should follow Git style commit message.
- The template is written at
- [Short (50 chars or less) summary of changes](https://git-scm.com/book/ch5-2.html).
- [Feature #12283]
+* On macOS, shared libraries no longer include a full version number of Ruby
+ in their names. This eliminates the burden of each teeny upgrade on the
+ platform that users need to rebuild every extension library.
+
+ [Before]
+ * libruby.2.6.0.dylib
+ * libruby.2.6.dylib -> libruby.2.6.0.dylib
+ * libruby.dylib -> libruby.2.6.0.dylib
+
+ [After]
+ * libruby.2.6.dylib
+ * libruby.dylib -> libruby.2.6.dylib
+
+* Extracted misc/*.el files to https://github.com/ruby/elisp
diff --git a/README.ja.md b/README.ja.md
index 953f949fa2..3ecdc9d7d3 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -1,3 +1,7 @@
+[![Build Status](https://travis-ci.org/ruby/ruby.svg?branch=trunk)](https://travis-ci.org/ruby/ruby)
+[![Build status](https://ci.appveyor.com/api/projects/status/0sy8rrxut4o0k960/branch/trunk?svg=true)](https://ci.appveyor.com/project/ruby/ruby/branch/trunk)
+[![wercker status](https://app.wercker.com/status/e5e7e1704f62b76525022aa424aef6ef/s/trunk "wercker status")](https://app.wercker.com/project/byKey/e5e7e1704f62b76525022aa424aef6ef)
+
# Rubyã¨ã¯
Rubyã¯ã‚·ãƒ³ãƒ—ルã‹ã¤å¼·åŠ›ãªã‚ªãƒ–ジェクト指å‘スクリプト言語ã§ã™ï¼Ž Rubyã¯ç´”粋ãªã‚ªãƒ–ジェクト指å‘言語ã¨ã—ã¦è¨­è¨ˆã•れã¦ã„ã‚‹ã®ã§ï¼Œ
@@ -16,18 +20,18 @@ Rubyã¯ãƒ†ã‚­ã‚¹ãƒˆå‡¦ç†é–¢ä¿‚ã®èƒ½åŠ›ãªã©ã«å„ªã‚Œï¼ŒPerlã¨åŒã˜ãらã„
* イテレータã¨ã‚¯ãƒ­ãƒ¼ã‚¸ãƒ£
* ガーベージコレクタ
* ダイナミックローディング (アーキテクãƒãƒ£ã«ã‚ˆã‚‹)
-* ç§»æ¤æ€§ãŒé«˜ã„.多ãã®Unix-like/POSIX互æ›ãƒ—ラットフォーム上ã§å‹•ãã ã‘ã§ãªã,Windows, Mac OS
- X,Haikuãªã©ã®ä¸Šã§ã‚‚å‹•ã cf.
- https://bugs.ruby-lang.org/projects/ruby-trunk/wiki/SupportedPlatformsJa
+* ç§»æ¤æ€§ãŒé«˜ã„.多ãã®Unix-like/POSIX互æ›ãƒ—ラットフォーム上ã§å‹•ãã ã‘ã§ãªã,Windows, macOS,
+ Haikuãªã©ã®ä¸Šã§ã‚‚å‹•ã cf.
+ https://github.com/ruby/ruby/blob/trunk/doc/contributing.rdoc#platform-maintainers
## 入手法
-### FTPã§
+サードパーティーツールを使ã£ãŸæ–¹æ³•ã‚’å«ã‚€Rubyã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«æ–¹æ³•ã®ä¸€è¦§ã¯
-以下ã®å ´æ‰€ã«ãŠã„ã¦ã‚りã¾ã™ï¼Ž
+https://www.ruby-lang.org/ja/downloads/
-ftp://ftp.ruby-lang.org/pub/ruby/
+ã‚’å‚ç…§ã—ã¦ãã ã•ã„.
### Subversionã§
@@ -43,7 +47,7 @@ ftp://ftp.ruby-lang.org/pub/ruby/
Subversionã®ãƒŸãƒ©ãƒ¼ã‚’GitHubã«å…¬é–‹ã—ã¦ã„ã¾ã™ï¼Ž 以下ã®ã‚³ãƒžãƒ³ãƒ‰ã§ãƒªãƒã‚¸ãƒˆãƒªã‚’å–å¾—ã§ãã¾ã™ï¼Ž
- $ git clone git://github.com/ruby/ruby.git
+ $ git clone https://github.com/ruby/ruby.git
## ホームページ
@@ -78,7 +82,7 @@ Ruby拡張モジュールã«ã¤ã„ã¦è©±ã—åˆã†ruby-extメーリングリスãƒ
ä»¥ä¸‹ã®æ‰‹é †ã§è¡Œã£ã¦ãã ã•ã„.
-1. ã‚‚ã— `configure` ファイルãŒè¦‹ã¤ã‹ã‚‰ãªã„,もã—ã㯠`configure.in` よりå¤ã„よã†ãªã‚‰ï¼Œ `autoconf` を実行ã—ã¦
+1. ã‚‚ã— `configure` ファイルãŒè¦‹ã¤ã‹ã‚‰ãªã„,もã—ã㯠`configure.ac` よりå¤ã„よã†ãªã‚‰ï¼Œ `autoconf` を実行ã—ã¦
æ–°ã—ã `configure` を生æˆã™ã‚‹
2. `configure` を実行ã—㦠`Makefile` ãªã©ã‚’生æˆã™ã‚‹
diff --git a/README.md b/README.md
index 283a92ce21..1e76fda4f0 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,27 @@
-[![Build Status](https://travis-ci.org/ruby/ruby.svg)](https://travis-ci.org/ruby/ruby)
+[![Build Status](https://travis-ci.org/ruby/ruby.svg?branch=trunk)](https://travis-ci.org/ruby/ruby)
[![Build status](https://ci.appveyor.com/api/projects/status/0sy8rrxut4o0k960/branch/trunk?svg=true)](https://ci.appveyor.com/project/ruby/ruby/branch/trunk)
+[![wercker status](https://app.wercker.com/status/e5e7e1704f62b76525022aa424aef6ef/s/trunk "wercker status")](https://app.wercker.com/project/byKey/e5e7e1704f62b76525022aa424aef6ef)
# What's Ruby
Ruby is the interpreted scripting language for quick and easy object-oriented
-programming. It has many features to process text files and to do system
-management tasks (as in Perl). It is simple, straight-forward, and
+programming. It has many features to process text files and to do system
+management tasks (as in Perl). It is simple, straight-forward, and
extensible.
## Features of Ruby
* Simple Syntax
* **Normal** Object-oriented Features (e.g. class, method calls)
-* **Advanced** Object-oriented Features (e.g. Mix-in, Singleton-method)
+* **Advanced** Object-oriented Features (e.g. mix-in, singleton-method)
* Operator Overloading
* Exception Handling
* Iterators and Closures
* Garbage Collection
* Dynamic Loading of Object Files (on some architectures)
* Highly Portable (works on many Unix-like/POSIX compatible platforms as
- well as Windows, Mac OS X, Haiku, etc.) cf.
- https://bugs.ruby-lang.org/projects/ruby-trunk/wiki/SupportedPlatforms
+ well as Windows, macOS, Haiku, etc.) cf.
+ https://github.com/ruby/ruby/blob/trunk/doc/contributing.rdoc#platform-maintainers
## How to get Ruby
@@ -30,10 +31,6 @@ like rvm, see:
https://www.ruby-lang.org/en/downloads/
-The Ruby distribution files can be found on the following FTP site:
-
-ftp://ftp.ruby-lang.org/pub/ruby/
-
The trunk of the Ruby source tree can be checked out with the following
command:
@@ -41,16 +38,16 @@ command:
Or if you are using git then use the following command:
- $ git clone git://github.com/ruby/ruby.git
+ $ git clone https://github.com/ruby/ruby.git
-There are some other branches under development. Try the following command
+There are some other branches under development. Try the following command
to see the list of branches:
$ svn ls https://svn.ruby-lang.org/repos/ruby/branches/
Or if you are using git then use the following command:
- $ git ls-remote git://github.com/ruby/ruby.git
+ $ git ls-remote https://github.com/ruby/ruby.git
## Ruby home page
@@ -66,22 +63,22 @@ send the following phrase:
subscribe
in the mail body (not subject) to the address
-<mailto:ruby-talk-request@ruby-lang.org>.
+<ruby-talk-request@ruby-lang.org>.
## How to compile and install
This is what you need to do to compile and install Ruby:
-1. If you want to use Microsoft Visual C++ to compile ruby, read
- win32/README.win32 instead of this document.
+1. If you want to use Microsoft Visual C++ to compile Ruby, read
+ [win32/README.win32](win32/README.win32) instead of this document.
-2. If `./configure` does not exist or is older than configure.in, run
+2. If `./configure` does not exist or is older than `configure.ac`, run
`autoconf` to (re)generate configure.
3. Run `./configure`, which will generate `config.h` and `Makefile`.
Some C compiler flags may be added by default depending on your
- environment. Specify `optflags=..` and `warnflags=..` as necessary to
+ environment. Specify `optflags=..` and `warnflags=..` as necessary to
override them.
4. Edit `defines.h` if you need. Usually this step will not be needed.
@@ -97,11 +94,19 @@ This is what you need to do to compile and install Ruby:
6. Run `make`.
+ * On Mac, set RUBY\_CODESIGN environment variable with a signing identity.
+ It uses the identity to sign `ruby` binary. See also codesign(1).
+
7. Optionally, run '`make check`' to check whether the compiled Ruby
interpreter works well. If you see the message "`check succeeded`", your
- ruby works as it should (hopefully).
+ Ruby works as it should (hopefully).
+
+8. Optionally, run `make update-gems` and `make extract-gems`.
+
+ If you want to install bundled gems, run `make update-gems` and
+ `make extract-gems` before running `make install`.
-8. Run '`make install`'
+9. Run '`make install`'.
This command will create the following directories and install files into
them.
@@ -130,10 +135,10 @@ This is what you need to do to compile and install Ruby:
**NOTE**: teeny of the API version may be different from one of Ruby's
program version
- You may have to be a super user to install ruby.
+ You may have to be a super user to install Ruby.
-If you fail to compile ruby, please send the detailed error report with the
+If you fail to compile Ruby, please send the detailed error report with the
error log and machine/OS type, to help others.
Some extension libraries may not get compiled because of lack of necessary
@@ -154,7 +159,7 @@ Bug reports should be filed at https://bugs.ruby-lang.org. Read [HowToReport] fo
[HowToReport]: https://bugs.ruby-lang.org/projects/ruby/wiki/HowToReport
-##Contributing
+## Contributing
See the file [CONTRIBUTING.md](CONTRIBUTING.md)
@@ -164,4 +169,4 @@ See the file [CONTRIBUTING.md](CONTRIBUTING.md)
Ruby was originally designed and developed by Yukihiro Matsumoto (Matz) in
1995.
-<mailto:matz@ruby-lang.org>
+<matz@ruby-lang.org>
diff --git a/acinclude.m4 b/acinclude.m4
deleted file mode 100644
index 99b24e6d93..0000000000
--- a/acinclude.m4
+++ /dev/null
@@ -1,46 +0,0 @@
-# -*- autoconf -*-
-
-AC_DEFUN([_COLORIZE_RESULT_PREPARE], [
- msg_checking= msg_result_yes= msg_result_no= msg_result_other= msg_reset=
- AS_IF([test "x${CONFIGURE_TTY}" = xyes -o -t 1], [
- msg_begin="`tput smso 2>/dev/null`"
- AS_CASE(["$msg_begin"], ['@<:@'*m],
- [msg_begin="`echo "$msg_begin" | sed ['s/[0-9]*m$//']`"
- msg_checking="${msg_begin}33m"
- AS_IF([test ${TEST_COLORS:+set}], [
- msg_result_yes=[`expr ":$TEST_COLORS:" : ".*:pass=\([^:]*\):"`]
- msg_result_no=[`expr ":$TEST_COLORS:" : ".*:fail=\([^:]*\):"`]
- msg_result_other=[`expr ":$TEST_COLORS:" : ".*:skip=\([^:]*\):"`]
- ])
- msg_result_yes="${msg_begin}${msg_result_yes:-32;1}m"
- msg_result_no="${msg_begin}${msg_result_no:-31;1}m"
- msg_result_other="${msg_begin}${msg_result_other:-33;1}m"
- msg_reset="${msg_begin}m"
- ])
- AS_UNSET(msg_begin)
- ])
- AS_REQUIRE_SHELL_FN([colorize_result],
- [AS_FUNCTION_DESCRIBE([colorize_result], [MSG], [Colorize result])],
- [AS_CASE(["$[]1"],
- [yes], [AS_ECHO(["${msg_result_yes}$[]1${msg_reset}]")],
- [no], [AS_ECHO(["${msg_result_no}$[]1${msg_reset}]")],
- [AS_ECHO(["${msg_result_other}$[]1${msg_reset}]")])])
-])
-
-AC_DEFUN([COLORIZE_RESULT], [AC_REQUIRE([_COLORIZE_RESULT_PREPARE])dnl
- AS_LITERAL_IF([$1],
- [m4_case([$1],
- [yes], [AS_ECHO(["${msg_result_yes}$1${msg_reset}"])],
- [no], [AS_ECHO(["${msg_result_no}$1${msg_reset}"])],
- [AS_ECHO(["${msg_result_other}$1${msg_reset}"])])],
- [colorize_result "$1"]) dnl
-])
-
-AC_DEFUN([AC_CHECKING],[dnl
-AC_REQUIRE([_COLORIZE_RESULT_PREPARE])dnl
-AS_MESSAGE([checking ${msg_checking}$1${msg_reset}...])])
-
-AC_DEFUN([AC_MSG_RESULT], [dnl
-{ _AS_ECHO_LOG([result: $1])
-COLORIZE_RESULT([$1]); dnl
-}])
diff --git a/aclocal.m4 b/aclocal.m4
index 56e944be8f..b0fe3eb959 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,6 +1,6 @@
-# generated automatically by aclocal 1.15 -*- Autoconf -*-
+# generated automatically by aclocal 1.16.1 -*- Autoconf -*-
-# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-2018 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -12,4 +12,35 @@
# PARTICULAR PURPOSE.
m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
-m4_include([acinclude.m4])
+m4_include([tool/m4/_colorize_result_prepare.m4])
+m4_include([tool/m4/ac_msg_result.m4])
+m4_include([tool/m4/colorize_result.m4])
+m4_include([tool/m4/ruby_append_option.m4])
+m4_include([tool/m4/ruby_append_options.m4])
+m4_include([tool/m4/ruby_check_builtin_func.m4])
+m4_include([tool/m4/ruby_check_builtin_setjmp.m4])
+m4_include([tool/m4/ruby_check_printf_prefix.m4])
+m4_include([tool/m4/ruby_check_setjmp.m4])
+m4_include([tool/m4/ruby_check_signedness.m4])
+m4_include([tool/m4/ruby_check_sizeof.m4])
+m4_include([tool/m4/ruby_check_sysconf.m4])
+m4_include([tool/m4/ruby_cppoutfile.m4])
+m4_include([tool/m4/ruby_decl_attribute.m4])
+m4_include([tool/m4/ruby_default_arch.m4])
+m4_include([tool/m4/ruby_define_if.m4])
+m4_include([tool/m4/ruby_defint.m4])
+m4_include([tool/m4/ruby_dtrace_available.m4])
+m4_include([tool/m4/ruby_dtrace_postprocess.m4])
+m4_include([tool/m4/ruby_func_attribute.m4])
+m4_include([tool/m4/ruby_mingw32.m4])
+m4_include([tool/m4/ruby_prepend_option.m4])
+m4_include([tool/m4/ruby_prog_gnu_ld.m4])
+m4_include([tool/m4/ruby_replace_type.m4])
+m4_include([tool/m4/ruby_rm_recursive.m4])
+m4_include([tool/m4/ruby_setjmp_type.m4])
+m4_include([tool/m4/ruby_stack_grow_direction.m4])
+m4_include([tool/m4/ruby_try_cflags.m4])
+m4_include([tool/m4/ruby_try_ldflags.m4])
+m4_include([tool/m4/ruby_type_attribute.m4])
+m4_include([tool/m4/ruby_universal_arch.m4])
+m4_include([tool/m4/ruby_werror_flag.m4])
diff --git a/addr2line.c b/addr2line.c
index 3cfb199d8b..b2c31b39ad 100644
--- a/addr2line.c
+++ b/addr2line.c
@@ -8,14 +8,28 @@
**********************************************************************/
+#if defined(__clang__)
+#pragma clang diagnostic ignored "-Wpedantic"
+#pragma clang diagnostic ignored "-Wgcc-compat"
+#elif defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+
#include "ruby/config.h"
+#include "ruby/defines.h"
#include "ruby/missing.h"
#include "addr2line.h"
#include <stdio.h>
#include <errno.h>
-#ifdef USE_ELF
+#ifdef HAVE_STDBOOL_H
+#include <stdbool.h>
+#else
+#include "missing/stdbool.h"
+#endif
+
+#if defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H)
#include <fcntl.h>
#include <limits.h>
@@ -28,12 +42,6 @@
#include <sys/stat.h>
#include <unistd.h>
-#ifdef __OpenBSD__
-#include <elf_abi.h>
-#else
-#include <elf.h>
-#endif
-
/* Make alloca work the best possible way. */
#ifdef __GNUC__
# ifndef alloca
@@ -57,24 +65,20 @@ void *alloca();
# include <dlfcn.h>
#endif
-#define DW_LNS_copy 0x01
-#define DW_LNS_advance_pc 0x02
-#define DW_LNS_advance_line 0x03
-#define DW_LNS_set_file 0x04
-#define DW_LNS_set_column 0x05
-#define DW_LNS_negate_stmt 0x06
-#define DW_LNS_set_basic_block 0x07
-#define DW_LNS_const_add_pc 0x08
-#define DW_LNS_fixed_advance_pc 0x09
-#define DW_LNS_set_prologue_end 0x0a /* DWARF3 */
-#define DW_LNS_set_epilogue_begin 0x0b /* DWARF3 */
-#define DW_LNS_set_isa 0x0c /* DWARF3 */
+#ifdef HAVE_MACH_O_LOADER_H
+# include <mach-o/fat.h>
+# include <mach-o/ldsyms.h>
+# include <mach-o/loader.h>
+# include <mach-o/nlist.h>
+# include <mach-o/stab.h>
+#endif
-/* Line number extended opcode name. */
-#define DW_LNE_end_sequence 0x01
-#define DW_LNE_set_address 0x02
-#define DW_LNE_define_file 0x03
-#define DW_LNE_set_discriminator 0x04 /* DWARF4 */
+#ifdef USE_ELF
+# ifdef __OpenBSD__
+# include <elf_abi.h>
+# else
+# include <elf.h>
+# endif
#ifndef ElfW
# if SIZEOF_VOIDP == 8
@@ -90,13 +94,44 @@ void *alloca();
# define ELF_ST_TYPE ELF32_ST_TYPE
# endif
#endif
+#endif
+
+#ifdef SHF_COMPRESSED
+# if defined(ELFCOMPRESS_ZLIB) && defined(HAVE_LIBZ)
+ /* FreeBSD 11.0 lacks ELFCOMPRESS_ZLIB */
+# include <zlib.h>
+# define SUPPORT_COMPRESSED_DEBUG_LINE
+# endif
+#else /* compatibility with glibc < 2.22 */
+# define SHF_COMPRESSED 0
+#endif
+
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
-int kprintf(const char *fmt, ...);
+#define DW_LNS_copy 0x01
+#define DW_LNS_advance_pc 0x02
+#define DW_LNS_advance_line 0x03
+#define DW_LNS_set_file 0x04
+#define DW_LNS_set_column 0x05
+#define DW_LNS_negate_stmt 0x06
+#define DW_LNS_set_basic_block 0x07
+#define DW_LNS_const_add_pc 0x08
+#define DW_LNS_fixed_advance_pc 0x09
+#define DW_LNS_set_prologue_end 0x0a /* DWARF3 */
+#define DW_LNS_set_epilogue_begin 0x0b /* DWARF3 */
+#define DW_LNS_set_isa 0x0c /* DWARF3 */
-typedef struct {
+/* Line number extended opcode name. */
+#define DW_LNE_end_sequence 0x01
+#define DW_LNE_set_address 0x02
+#define DW_LNE_define_file 0x03
+#define DW_LNE_set_discriminator 0x04 /* DWARF4 */
+
+PRINTF_ARGS(static int kprintf(const char *fmt, ...), 1, 2);
+
+typedef struct line_info {
const char *dirname;
const char *filename;
const char *path; /* object path */
@@ -105,15 +140,52 @@ typedef struct {
uintptr_t base_addr;
uintptr_t saddr;
const char *sname; /* function name */
+
+ struct line_info *next;
} line_info_t;
-typedef struct obj_info obj_info_t;
-struct obj_info {
+
+struct dwarf_section {
+ char *ptr;
+ size_t size;
+ uint64_t flags;
+};
+
+typedef struct obj_info {
const char *path; /* object path */
- int fd;
- void *mapped;
+ char *mapped;
size_t mapped_size;
+ void *uncompressed;
uintptr_t base_addr;
- obj_info_t *next;
+ uintptr_t vmaddr;
+ struct dwarf_section debug_abbrev;
+ struct dwarf_section debug_info;
+ struct dwarf_section debug_line;
+ struct dwarf_section debug_ranges;
+ struct dwarf_section debug_str;
+ struct obj_info *next;
+} obj_info_t;
+
+#define DWARF_SECTION_COUNT 5
+
+static struct dwarf_section *
+obj_dwarf_section_at(obj_info_t *obj, int n)
+{
+ struct dwarf_section *ary[] = {
+ &obj->debug_abbrev,
+ &obj->debug_info,
+ &obj->debug_line,
+ &obj->debug_ranges,
+ &obj->debug_str
+ };
+ if (n < 0 || DWARF_SECTION_COUNT <= n) {
+ abort();
+ }
+ return ary[n];
+}
+
+struct debug_section_definition {
+ const char *name;
+ struct dwarf_section *dwarf;
};
/* Avoid consuming stack as this module may be used from signal handler */
@@ -177,8 +249,7 @@ get_nth_dirname(unsigned long dir, char *p)
}
static void
-fill_filename(int file, char *include_directories, char *filenames,
- line_info_t *line)
+fill_filename(int file, char *include_directories, char *filenames, line_info_t *line, obj_info_t *obj)
{
int i;
char *p = filenames;
@@ -188,8 +259,8 @@ fill_filename(int file, char *include_directories, char *filenames,
filename = p;
if (!*p) {
/* Need to output binary file name? */
- kprintf("Unexpected file number %d in %s\n",
- file, binary_filename);
+ kprintf("Unexpected file number %d in %s at %tx\n",
+ file, binary_filename, filenames - obj->mapped);
return;
}
while (*p) p++;
@@ -213,104 +284,128 @@ fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line,
obj_info_t *obj, line_info_t *lines, int offset)
{
int i;
- addr += obj->base_addr;
+ addr += obj->base_addr - obj->vmaddr;
for (i = offset; i < num_traces; i++) {
uintptr_t a = (uintptr_t)traces[i];
/* 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]);
+ fill_filename(file, include_directories, filenames, &lines[i], obj);
lines[i].line = line;
}
}
}
+struct LineNumberProgramHeader {
+ uint64_t unit_length;
+ uint16_t version;
+ uint8_t format; /* 4 or 8 */
+ uint64_t header_length;
+ uint8_t minimum_instruction_length;
+ uint8_t maximum_operations_per_instruction;
+ uint8_t default_is_stmt;
+ int8_t line_base;
+ uint8_t line_range;
+ uint8_t opcode_base;
+ /* uint8_t standard_opcode_lengths[opcode_base-1]; */
+ const char *include_directories;
+ const char *filenames;
+ const char *cu_start;
+ const char *cu_end;
+};
+
static int
-parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
- obj_info_t *obj, line_info_t *lines, int offset)
+parse_debug_line_header(const char **pp, struct LineNumberProgramHeader *header)
{
- char *p, *cu_end, *cu_start, *include_directories, *filenames;
- unsigned long unit_length;
- int default_is_stmt, line_base;
- unsigned int header_length, minimum_instruction_length, line_range,
- opcode_base;
- /* unsigned char *standard_opcode_lengths; */
-
- /* The registers. */
- unsigned long addr = 0;
- unsigned int file = 1;
- unsigned int line = 1;
- /* unsigned int column = 0; */
- int is_stmt;
- /* int basic_block = 0; */
- /* int end_sequence = 0; */
- /* int prologue_end = 0; */
- /* int epilogue_begin = 0; */
- /* unsigned int isa = 0; */
-
- p = *debug_line;
-
- unit_length = *(unsigned int *)p;
- p += sizeof(unsigned int);
- if (unit_length == 0xffffffff) {
- unit_length = *(unsigned long *)p;
- p += sizeof(unsigned long);
+ const char *p = *pp;
+ 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;
}
- cu_end = p + unit_length;
+ header->cu_end = p + header->unit_length;
- /*dwarf_version = *(unsigned short *)p;*/
- p += 2;
+ header->version = *(uint16_t *)p;
+ p += sizeof(uint16_t);
+ if (header->version > 4) return -1;
- header_length = *(unsigned int *)p;
- p += sizeof(unsigned int);
+ header->header_length = header->format == 4 ? *(uint32_t *)p : *(uint64_t *)p;
+ p += header->format;
+ header->cu_start = p + header->header_length;
- cu_start = p + header_length;
+ header->minimum_instruction_length = *(uint8_t *)p++;
- minimum_instruction_length = *(unsigned char *)p;
- p++;
-
- is_stmt = default_is_stmt = *(unsigned char *)p;
- p++;
-
- line_base = *(signed char *)p;
- p++;
-
- line_range = *(unsigned char *)p;
- p++;
-
- opcode_base = *(unsigned char *)p;
- p++;
+ if (header->version >= 4) {
+ /* maximum_operations_per_instruction = *(uint8_t *)p; */
+ if (*p != 1) return -1; /* For non-VLIW architectures, this field is 1 */
+ p++;
+ }
- /* standard_opcode_lengths = (unsigned char *)p - 1; */
- p += opcode_base - 1;
+ header->default_is_stmt = *(uint8_t *)p++;
+ header->line_base = *(int8_t *)p++;
+ header->line_range = *(uint8_t *)p++;
+ header->opcode_base = *(uint8_t *)p++;
+ /* header->standard_opcode_lengths = (uint8_t *)p - 1; */
+ p += header->opcode_base - 1;
- include_directories = p;
+ header->include_directories = p;
/* temporary measure for compress-debug-sections */
- if (p >= cu_end) return -1;
+ if (p >= header->cu_end) return -1;
/* skip include directories */
while (*p) {
- p = memchr(p, '\0', cu_end - p);
+ p = memchr(p, '\0', header->cu_end - p);
if (!p) return -1;
p++;
}
p++;
- filenames = p;
+ header->filenames = p;
+
+ *pp = header->cu_start;
+
+ return 0;
+}
- p = cu_start;
+static int
+parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
+ obj_info_t *obj, line_info_t *lines, int offset)
+{
+ const char *p = (const char *)*debug_line;
+ struct LineNumberProgramHeader header;
+
+ /* The registers. */
+ unsigned long addr = 0;
+ unsigned int file = 1;
+ unsigned int line = 1;
+ /* unsigned int column = 0; */
+ int is_stmt;
+ /* int basic_block = 0; */
+ /* int end_sequence = 0; */
+ /* int prologue_end = 0; */
+ /* int epilogue_begin = 0; */
+ /* unsigned int isa = 0; */
+
+ if (parse_debug_line_header(&p, &header))
+ return -1;
+ is_stmt = header.default_is_stmt;
#define FILL_LINE() \
do { \
fill_line(num_traces, traces, addr, file, line, \
- include_directories, filenames, \
+ (char *)header.include_directories, \
+ (char *)header.filenames, \
obj, lines, offset); \
/*basic_block = prologue_end = epilogue_begin = 0;*/ \
} while (0)
- while (p < cu_end) {
+ while (p < header.cu_end) {
unsigned long a;
unsigned char op = *p++;
switch (op) {
@@ -318,19 +413,19 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
FILL_LINE();
break;
case DW_LNS_advance_pc:
- a = uleb128(&p);
+ a = uleb128((char **)&p);
addr += a;
break;
case DW_LNS_advance_line: {
- long a = sleb128(&p);
+ long a = sleb128((char **)&p);
line += a;
break;
}
case DW_LNS_set_file:
- file = (unsigned int)uleb128(&p);
+ file = (unsigned int)uleb128((char **)&p);
break;
case DW_LNS_set_column:
- /*column = (unsigned int)*/(void)uleb128(&p);
+ /*column = (unsigned int)*/(void)uleb128((char **)&p);
break;
case DW_LNS_negate_stmt:
is_stmt = !is_stmt;
@@ -339,8 +434,8 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
/*basic_block = 1; */
break;
case DW_LNS_const_add_pc:
- a = ((255 - opcode_base) / line_range) *
- minimum_instruction_length;
+ a = ((255 - header.opcode_base) / header.line_range) *
+ header.minimum_instruction_length;
addr += a;
break;
case DW_LNS_fixed_advance_pc:
@@ -354,7 +449,7 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
/* epilogue_begin = 1; */
break;
case DW_LNS_set_isa:
- /* isa = (unsigned int)*/(void)uleb128(&p);
+ /* isa = (unsigned int)*/(void)uleb128((char **)&p);
break;
case 0:
a = *(unsigned char *)p++;
@@ -367,7 +462,7 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
file = 1;
line = 1;
/* column = 0; */
- is_stmt = default_is_stmt;
+ is_stmt = header.default_is_stmt;
/* end_sequence = 0; */
/* isa = 0; */
break;
@@ -381,7 +476,7 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
break;
case DW_LNE_set_discriminator:
/* TODO:currently ignore */
- uleb128(&p);
+ uleb128((char **)&p);
break;
default:
kprintf("Unknown extended opcode: %d in %s\n",
@@ -389,18 +484,16 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
}
break;
default: {
- unsigned long addr_incr;
- unsigned long line_incr;
- a = op - opcode_base;
- addr_incr = (a / line_range) * minimum_instruction_length;
- line_incr = line_base + (a % line_range);
- addr += (unsigned int)addr_incr;
- line += (unsigned int)line_incr;
+ uint8_t adjusted_opcode = op - header.opcode_base;
+ uint8_t operation_advance = adjusted_opcode / header.line_range;
+ /* NOTE: this code doesn't support VLIW */
+ addr += operation_advance * header.minimum_instruction_length;
+ line += header.line_base + (adjusted_opcode % header.line_range);
FILL_LINE();
}
}
}
- *debug_line = p;
+ *debug_line = (char *)p;
return 0;
}
@@ -427,22 +520,26 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
obj_info_t **objp, line_info_t *lines, int offset);
static void
-append_obj(obj_info_t **objp) {
+append_obj(obj_info_t **objp)
+{
obj_info_t *newobj = calloc(1, sizeof(obj_info_t));
if (*objp) (*objp)->next = newobj;
*objp = newobj;
}
+#ifdef USE_ELF
static void
-follow_debuglink(char *debuglink, int num_traces, void **traces,
+follow_debuglink(const char *debuglink, int num_traces, void **traces,
obj_info_t **objp, line_info_t *lines, int offset)
{
/* Ideally we should check 4 paths to follow gnu_debuglink,
but we handle only one case for now as this format is used
by some linux distributions. See GDB's info for detail. */
static const char global_debug_dir[] = "/usr/lib/debug";
- char *p, *subdir;
+ const size_t global_debug_dir_len = sizeof(global_debug_dir) - 1;
+ char *p;
obj_info_t *o1 = *objp, *o2;
+ size_t len;
p = strrchr(binary_filename, '/');
if (!p) {
@@ -450,11 +547,13 @@ follow_debuglink(char *debuglink, int num_traces, void **traces,
}
p[1] = '\0';
- subdir = (char *)alloca(strlen(binary_filename) + 1);
- strcpy(subdir, binary_filename);
- strcpy(binary_filename, global_debug_dir);
- strlcat(binary_filename, subdir, PATH_MAX);
- strlcat(binary_filename, debuglink, PATH_MAX);
+ len = strlen(binary_filename);
+ if (len >= PATH_MAX - global_debug_dir_len)
+ len = PATH_MAX - global_debug_dir_len - 1;
+ memmove(binary_filename + global_debug_dir_len, binary_filename, len);
+ memcpy(binary_filename, global_debug_dir, global_debug_dir_len);
+ len += global_debug_dir_len;
+ strlcpy(binary_filename + len, debuglink, PATH_MAX - len);
append_obj(objp);
o2 = *objp;
@@ -462,6 +561,1054 @@ follow_debuglink(char *debuglink, int num_traces, void **traces,
o2->path = o1->path;
fill_lines(num_traces, traces, 0, objp, lines, offset);
}
+#endif
+
+enum
+{
+ DW_TAG_compile_unit = 0x11,
+ DW_TAG_inlined_subroutine = 0x1d,
+ DW_TAG_subprogram = 0x2e,
+};
+
+/* Attributes encodings */
+enum
+{
+ DW_AT_sibling = 0x01,
+ DW_AT_location = 0x02,
+ DW_AT_name = 0x03,
+ /* Reserved 0x04 */
+ /* Reserved 0x05 */
+ /* Reserved 0x06 */
+ /* Reserved 0x07 */
+ /* Reserved 0x08 */
+ DW_AT_ordering = 0x09,
+ /* Reserved 0x0a */
+ DW_AT_byte_size = 0x0b,
+ /* Reserved 0x0c */
+ DW_AT_bit_size = 0x0d,
+ /* Reserved 0x0e */
+ /* Reserved 0x0f */
+ DW_AT_stmt_list = 0x10,
+ DW_AT_low_pc = 0x11,
+ DW_AT_high_pc = 0x12,
+ DW_AT_language = 0x13,
+ /* Reserved 0x14 */
+ DW_AT_discr = 0x15,
+ DW_AT_discr_value = 0x16,
+ DW_AT_visibility = 0x17,
+ DW_AT_import = 0x18,
+ DW_AT_string_length = 0x19,
+ DW_AT_common_reference = 0x1a,
+ DW_AT_comp_dir = 0x1b,
+ DW_AT_const_value = 0x1c,
+ DW_AT_containing_type = 0x1d,
+ DW_AT_default_value = 0x1e,
+ /* Reserved 0x1f */
+ DW_AT_inline = 0x20,
+ DW_AT_is_optional = 0x21,
+ DW_AT_lower_bound = 0x22,
+ /* Reserved 0x23 */
+ /* Reserved 0x24 */
+ DW_AT_producer = 0x25,
+ /* Reserved 0x26 */
+ DW_AT_prototyped = 0x27,
+ /* Reserved 0x28 */
+ /* Reserved 0x29 */
+ DW_AT_return_addr = 0x2a,
+ /* Reserved 0x2b */
+ DW_AT_start_scope = 0x2c,
+ /* Reserved 0x2d */
+ DW_AT_bit_stride = 0x2e,
+ DW_AT_upper_bound = 0x2f,
+ /* Reserved 0x30 */
+ DW_AT_abstract_origin = 0x31,
+ DW_AT_accessibility = 0x32,
+ DW_AT_address_class = 0x33,
+ DW_AT_artificial = 0x34,
+ DW_AT_base_types = 0x35,
+ DW_AT_calling_convention = 0x36,
+ DW_AT_count = 0x37,
+ DW_AT_data_member_location = 0x38,
+ DW_AT_decl_column = 0x39,
+ DW_AT_decl_file = 0x3a,
+ DW_AT_decl_line = 0x3b,
+ DW_AT_declaration = 0x3c,
+ DW_AT_discr_list = 0x3d,
+ DW_AT_encoding = 0x3e,
+ DW_AT_external = 0x3f,
+ DW_AT_frame_base = 0x40,
+ DW_AT_friend = 0x41,
+ DW_AT_identifier_case = 0x42,
+ /* Reserved 0x43 */
+ DW_AT_namelist_item = 0x44,
+ DW_AT_priority = 0x45,
+ DW_AT_segment = 0x46,
+ DW_AT_specification = 0x47,
+ DW_AT_static_link = 0x48,
+ DW_AT_type = 0x49,
+ DW_AT_use_location = 0x4a,
+ DW_AT_variable_parameter = 0x4b,
+ DW_AT_virtuality = 0x4c,
+ DW_AT_vtable_elem_location = 0x4d,
+ DW_AT_allocated = 0x4e,
+ DW_AT_associated = 0x4f,
+ DW_AT_data_location = 0x50,
+ DW_AT_byte_stride = 0x51,
+ DW_AT_entry_pc = 0x52,
+ DW_AT_use_UTF8 = 0x53,
+ DW_AT_extension = 0x54,
+ DW_AT_ranges = 0x55,
+ DW_AT_trampoline = 0x56,
+ DW_AT_call_column = 0x57,
+ DW_AT_call_file = 0x58,
+ DW_AT_call_line = 0x59,
+ DW_AT_description = 0x5a,
+ DW_AT_binary_scale = 0x5b,
+ DW_AT_decimal_scale = 0x5c,
+ DW_AT_small = 0x5d,
+ DW_AT_decimal_sign = 0x5e,
+ DW_AT_digit_count = 0x5f,
+ DW_AT_picture_string = 0x60,
+ DW_AT_mutable = 0x61,
+ DW_AT_threads_scaled = 0x62,
+ DW_AT_explicit = 0x63,
+ DW_AT_object_pointer = 0x64,
+ DW_AT_endianity = 0x65,
+ DW_AT_elemental = 0x66,
+ DW_AT_pure = 0x67,
+ DW_AT_recursive = 0x68,
+ DW_AT_signature = 0x69,
+ DW_AT_main_subprogram = 0x6a,
+ DW_AT_data_bit_offset = 0x6b,
+ DW_AT_const_expr = 0x6c,
+ DW_AT_enum_class = 0x6d,
+ DW_AT_linkage_name = 0x6e,
+ DW_AT_string_length_bit_size = 0x6f,
+ DW_AT_string_length_byte_size = 0x70,
+ DW_AT_rank = 0x71,
+ DW_AT_str_offsets_base = 0x72,
+ DW_AT_addr_base = 0x73,
+ DW_AT_rnglists_base = 0x74,
+ /* Reserved 0x75 */
+ DW_AT_dwo_name = 0x76,
+ DW_AT_reference = 0x77,
+ DW_AT_rvalue_reference = 0x78,
+ DW_AT_macros = 0x79,
+ DW_AT_call_all_calls = 0x7a,
+ DW_AT_call_all_source_calls = 0x7b,
+ DW_AT_call_all_tail_calls = 0x7c,
+ DW_AT_call_return_pc = 0x7d,
+ DW_AT_call_value = 0x7e,
+ DW_AT_call_origin = 0x7f,
+ DW_AT_call_parameter = 0x80,
+ DW_AT_call_pc = 0x81,
+ DW_AT_call_tail_call = 0x82,
+ DW_AT_call_target = 0x83,
+ DW_AT_call_target_clobbered = 0x84,
+ DW_AT_call_data_location = 0x85,
+ DW_AT_call_data_value = 0x86,
+ DW_AT_noreturn = 0x87,
+ DW_AT_alignment = 0x88,
+ DW_AT_export_symbols = 0x89,
+ DW_AT_deleted = 0x8a,
+ DW_AT_defaulted = 0x8b,
+ DW_AT_loclists_base = 0x8c,
+ DW_AT_lo_user = 0x2000,
+ DW_AT_hi_user = 0x3fff
+};
+
+/* Attribute form encodings */
+enum
+{
+ DW_FORM_addr = 0x01,
+ /* Reserved 0x02 */
+ DW_FORM_block2 = 0x03,
+ DW_FORM_block4 = 0x04,
+ DW_FORM_data2 = 0x05,
+ DW_FORM_data4 = 0x06,
+ DW_FORM_data8 = 0x07,
+ DW_FORM_string = 0x08,
+ DW_FORM_block = 0x09,
+ DW_FORM_block1 = 0x0a,
+ DW_FORM_data1 = 0x0b,
+ DW_FORM_flag = 0x0c,
+ DW_FORM_sdata = 0x0d,
+ DW_FORM_strp = 0x0e,
+ DW_FORM_udata = 0x0f,
+ DW_FORM_ref_addr = 0x10,
+ DW_FORM_ref1 = 0x11,
+ DW_FORM_ref2 = 0x12,
+ DW_FORM_ref4 = 0x13,
+ DW_FORM_ref8 = 0x14,
+ DW_FORM_ref_udata = 0x15,
+ DW_FORM_indirect = 0x16,
+ DW_FORM_sec_offset = 0x17,
+ DW_FORM_exprloc = 0x18,
+ DW_FORM_flag_present = 0x19,
+ DW_FORM_strx = 0x1a,
+ DW_FORM_addrx = 0x1b,
+ DW_FORM_ref_sup4 = 0x1c,
+ DW_FORM_strp_sup = 0x1d,
+ DW_FORM_data16 = 0x1e,
+ DW_FORM_line_strp = 0x1f,
+ DW_FORM_ref_sig8 = 0x20,
+ DW_FORM_implicit_const = 0x21,
+ DW_FORM_loclistx = 0x22,
+ DW_FORM_rnglistx = 0x23,
+ DW_FORM_ref_sup8 = 0x24,
+ DW_FORM_strx1 = 0x25,
+ DW_FORM_strx2 = 0x26,
+ DW_FORM_strx3 = 0x27,
+ DW_FORM_strx4 = 0x28,
+ DW_FORM_addrx1 = 0x29,
+ DW_FORM_addrx2 = 0x2a,
+ DW_FORM_addrx3 = 0x2b,
+ DW_FORM_addrx4 = 0x2c
+};
+
+enum {
+ VAL_none = 0,
+ VAL_cstr = 1,
+ VAL_data = 2,
+ VAL_uint = 3,
+ VAL_int = 4
+};
+
+# define ABBREV_TABLE_SIZE 256
+typedef struct {
+ obj_info_t *obj;
+ char *file;
+ char *current_cu;
+ uint64_t current_low_pc;
+ char *debug_line_cu_end;
+ char *debug_line_files;
+ char *debug_line_directories;
+ char *p;
+ char *cu_end;
+ char *pend;
+ char *q0;
+ char *q;
+ int format; /* 4 or 8 */;
+ uint8_t address_size;
+ int level;
+ char *abbrev_table[ABBREV_TABLE_SIZE];
+} DebugInfoReader;
+
+typedef struct {
+ ptrdiff_t pos;
+ int tag;
+ int has_children;
+} DIE;
+
+typedef struct {
+ union {
+ char *ptr;
+ uint64_t uint64;
+ int64_t int64;
+ } as;
+ uint64_t off;
+ uint64_t at;
+ uint64_t form;
+ size_t size;
+ int type;
+} DebugInfoValue;
+
+/* TODO: Big Endian */
+#define MERGE_2INTS(a,b,sz) (((uint64_t)(b)<<sz)|(a))
+
+static uint16_t
+get_uint16(const uint8_t *p)
+{
+ return (uint16_t)MERGE_2INTS(p[0],p[1],8);
+}
+
+static uint32_t
+get_uint32(const uint8_t *p)
+{
+ return (uint32_t)MERGE_2INTS(get_uint16(p),get_uint16(p+2),16);
+}
+
+static uint64_t
+get_uint64(const uint8_t *p)
+{
+ return MERGE_2INTS(get_uint32(p),get_uint32(p+4),32);
+}
+
+static uint8_t
+read_uint8(char **ptr)
+{
+ const unsigned char *p = (const unsigned char *)*ptr;
+ *ptr = (char *)(p + 1);
+ return *p;
+}
+
+static uint16_t
+read_uint16(char **ptr)
+{
+ const unsigned char *p = (const unsigned char *)*ptr;
+ *ptr = (char *)(p + 2);
+ return get_uint16(p);
+}
+
+static uint32_t
+read_uint24(char **ptr)
+{
+ const unsigned char *p = (const unsigned char *)*ptr;
+ *ptr = (char *)(p + 3);
+ return (*p << 16) | get_uint16(p+1);
+}
+
+static uint32_t
+read_uint32(char **ptr)
+{
+ const unsigned char *p = (const unsigned char *)*ptr;
+ *ptr = (char *)(p + 4);
+ return get_uint32(p);
+}
+
+static uint64_t
+read_uint64(char **ptr)
+{
+ const unsigned char *p = (const unsigned char *)*ptr;
+ *ptr = (char *)(p + 8);
+ return get_uint64(p);
+}
+
+static uintptr_t
+read_uintptr(char **ptr)
+{
+ const unsigned char *p = (const unsigned char *)*ptr;
+ *ptr = (char *)(p + SIZEOF_VOIDP);
+#if SIZEOF_VOIDP == 8
+ return get_uint64(p);
+#else
+ return get_uint32(p);
+#endif
+}
+
+static uint64_t
+read_uint(DebugInfoReader *reader)
+{
+ if (reader->format == 4) {
+ return read_uint32(&reader->p);
+ } else { /* 64 bit */
+ return read_uint64(&reader->p);
+ }
+}
+
+static uint64_t
+read_uleb128(DebugInfoReader *reader)
+{
+ return uleb128(&reader->p);
+}
+
+static int64_t
+read_sleb128(DebugInfoReader *reader)
+{
+ return sleb128(&reader->p);
+}
+
+static void
+debug_info_reader_init(DebugInfoReader *reader, obj_info_t *obj)
+{
+ reader->file = obj->mapped;
+ reader->obj = obj;
+ reader->p = obj->debug_info.ptr;
+ reader->pend = obj->debug_info.ptr + obj->debug_info.size;
+ reader->debug_line_cu_end = obj->debug_line.ptr;
+}
+
+static void
+di_read_debug_abbrev_cu(DebugInfoReader *reader)
+{
+ uint64_t prev = 0;
+ char *p = reader->q0;
+ for (;;) {
+ uint64_t abbrev_number = uleb128(&p);
+ if (abbrev_number <= prev) break;
+ if (abbrev_number < ABBREV_TABLE_SIZE) {
+ reader->abbrev_table[abbrev_number] = p;
+ }
+ prev = abbrev_number;
+ uleb128(&p); /* tag */
+ p++; /* has_children */
+ /* skip content */
+ for (;;) {
+ uint64_t at = uleb128(&p);
+ uint64_t form = uleb128(&p);
+ if (!at && !form) break;
+ }
+ }
+}
+
+static int
+di_read_debug_line_cu(DebugInfoReader *reader)
+{
+ const char *p;
+ struct LineNumberProgramHeader header;
+
+ p = (const char *)reader->debug_line_cu_end;
+ if (parse_debug_line_header(&p, &header))
+ return -1;
+
+ reader->debug_line_cu_end = (char *)header.cu_end;
+ reader->debug_line_directories = (char *)header.include_directories;
+ reader->debug_line_files = (char *)header.filenames;
+
+ return 0;
+}
+
+static void
+set_uint_value(DebugInfoValue *v, uint64_t n)
+{
+ v->as.uint64 = n;
+ v->type = VAL_uint;
+}
+
+static void
+set_int_value(DebugInfoValue *v, int64_t n)
+{
+ v->as.int64 = n;
+ v->type = VAL_int;
+}
+
+static void
+set_cstr_value(DebugInfoValue *v, char *s)
+{
+ v->as.ptr = s;
+ v->off = 0;
+ v->type = VAL_cstr;
+}
+
+static void
+set_cstrp_value(DebugInfoValue *v, char *s, uint64_t off)
+{
+ v->as.ptr = s;
+ v->off = off;
+ v->type = VAL_cstr;
+}
+
+static void
+set_data_value(DebugInfoValue *v, char *s)
+{
+ v->as.ptr = s;
+ v->type = VAL_data;
+}
+
+static const char *
+get_cstr_value(DebugInfoValue *v)
+{
+ if (v->as.ptr) {
+ return v->as.ptr + v->off;
+ } else {
+ return NULL;
+ }
+}
+
+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();
+ }
+ break;
+ case DW_FORM_block2:
+ v->size = read_uint16(&reader->p);
+ set_data_value(v, reader->p);
+ reader->p += v->size;
+ break;
+ case DW_FORM_block4:
+ v->size = read_uint32(&reader->p);
+ set_data_value(v, reader->p);
+ reader->p += v->size;
+ break;
+ case DW_FORM_data2:
+ set_uint_value(v, read_uint16(&reader->p));
+ break;
+ case DW_FORM_data4:
+ set_uint_value(v, read_uint32(&reader->p));
+ break;
+ case DW_FORM_data8:
+ set_uint_value(v, read_uint64(&reader->p));
+ break;
+ case DW_FORM_string:
+ v->size = strlen(reader->p);
+ set_cstr_value(v, reader->p);
+ reader->p += v->size + 1;
+ break;
+ case DW_FORM_block:
+ v->size = uleb128(&reader->p);
+ set_data_value(v, reader->p);
+ reader->p += v->size;
+ break;
+ case DW_FORM_block1:
+ v->size = read_uint8(&reader->p);
+ set_data_value(v, reader->p);
+ reader->p += v->size;
+ break;
+ case DW_FORM_data1:
+ set_uint_value(v, read_uint8(&reader->p));
+ break;
+ case DW_FORM_flag:
+ set_uint_value(v, read_uint8(&reader->p));
+ break;
+ case DW_FORM_sdata:
+ set_int_value(v, read_sleb128(reader));
+ break;
+ case DW_FORM_strp:
+ set_cstrp_value(v, reader->obj->debug_str.ptr, read_uint(reader));
+ break;
+ case DW_FORM_udata:
+ set_uint_value(v, read_uleb128(reader));
+ break;
+ case DW_FORM_ref_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();
+ }
+ break;
+ case DW_FORM_ref1:
+ set_uint_value(v, read_uint8(&reader->p));
+ break;
+ case DW_FORM_ref2:
+ set_uint_value(v, read_uint16(&reader->p));
+ break;
+ case DW_FORM_ref4:
+ set_uint_value(v, read_uint32(&reader->p));
+ break;
+ case DW_FORM_ref8:
+ set_uint_value(v, read_uint64(&reader->p));
+ break;
+ case DW_FORM_ref_udata:
+ set_uint_value(v, uleb128(&reader->p));
+ break;
+ case DW_FORM_indirect:
+ /* TODO: read the refered value */
+ set_uint_value(v, uleb128(&reader->p));
+ break;
+ case DW_FORM_sec_offset:
+ set_uint_value(v, read_uint(reader)); /* offset */
+ /* addrptr: debug_addr */
+ /* lineptr: debug_line */
+ /* loclist: debug_loclists */
+ /* loclistptr: debug_loclists */
+ /* macptr: debug_macro */
+ /* rnglist: debug_rnglists */
+ /* rnglistptr: debug_rnglists */
+ /* stroffsetsptr: debug_str_offsets */
+ break;
+ case DW_FORM_exprloc:
+ v->size = (size_t)read_uleb128(reader);
+ set_data_value(v, reader->p);
+ reader->p += v->size;
+ break;
+ case DW_FORM_flag_present:
+ set_uint_value(v, 1);
+ break;
+ case DW_FORM_strx:
+ set_uint_value(v, uleb128(&reader->p));
+ break;
+ case DW_FORM_addrx:
+ /* TODO: read .debug_addr */
+ set_uint_value(v, uleb128(&reader->p));
+ break;
+ case DW_FORM_ref_sup4:
+ set_uint_value(v, read_uint32(&reader->p));
+ break;
+ case DW_FORM_strp_sup:
+ set_uint_value(v, read_uint(reader));
+ /* *p = reader->sup_file + reader->sup_str->sh_offset + ret; */
+ break;
+ case DW_FORM_data16:
+ v->size = 16;
+ set_data_value(v, reader->p);
+ 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; */
+ break;
+ case DW_FORM_ref_sig8:
+ set_uint_value(v, read_uint64(&reader->p));
+ break;
+ case DW_FORM_implicit_const:
+ set_int_value(v, sleb128(&reader->q));
+ break;
+ case DW_FORM_loclistx:
+ set_uint_value(v, read_uleb128(reader));
+ break;
+ case DW_FORM_rnglistx:
+ set_uint_value(v, read_uleb128(reader));
+ break;
+ case DW_FORM_ref_sup8:
+ set_uint_value(v, read_uint64(&reader->p));
+ break;
+ case DW_FORM_strx1:
+ set_uint_value(v, read_uint8(&reader->p));
+ break;
+ case DW_FORM_strx2:
+ set_uint_value(v, read_uint16(&reader->p));
+ break;
+ case DW_FORM_strx3:
+ set_uint_value(v, read_uint24(&reader->p));
+ break;
+ case DW_FORM_strx4:
+ set_uint_value(v, read_uint32(&reader->p));
+ break;
+ case DW_FORM_addrx1:
+ set_uint_value(v, read_uint8(&reader->p));
+ break;
+ case DW_FORM_addrx2:
+ set_uint_value(v, read_uint16(&reader->p));
+ break;
+ case DW_FORM_addrx3:
+ set_uint_value(v, read_uint24(&reader->p));
+ break;
+ case DW_FORM_addrx4:
+ set_uint_value(v, read_uint32(&reader->p));
+ break;
+ case 0:
+ goto fail;
+ break;
+ }
+ return;
+
+ fail:
+ fprintf(stderr, "%d: unsupported form: %#"PRIx64"\n", __LINE__, form);
+ exit(1);
+}
+
+/* find abbrev in current compilation unit */
+static char *
+di_find_abbrev(DebugInfoReader *reader, uint64_t abbrev_number)
+{
+ uint64_t n;
+ char *p;
+ if (abbrev_number < ABBREV_TABLE_SIZE) {
+ return reader->abbrev_table[abbrev_number];
+ }
+ p = reader->abbrev_table[ABBREV_TABLE_SIZE-1];
+ /* skip 255th record */
+ uleb128(&p); /* tag */
+ p++; /* has_children */
+ /* skip content */
+ for (;;) {
+ uint64_t at = uleb128(&p);
+ uint64_t form = uleb128(&p);
+ if (!at && !form) break;
+ }
+ for (n = uleb128(&p); abbrev_number != n; n = uleb128(&p)) {
+ if (n == 0) {
+ fprintf(stderr,"%d: Abbrev Number %"PRId64" not found\n",__LINE__, abbrev_number);
+ exit(1);
+ }
+ uleb128(&p); /* tag */
+ p++; /* has_children */
+ /* skip content */
+ for (;;) {
+ uint64_t at = uleb128(&p);
+ uint64_t form = uleb128(&p);
+ if (!at && !form) break;
+ }
+ }
+ return p;
+}
+
+#if 0
+static void
+hexdump0(const unsigned char *p, size_t n)
+{
+ size_t i;
+ fprintf(stderr, " 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
+ for (i=0; i < n; i++){
+ switch (i & 15) {
+ case 0:
+ fprintf(stderr, "%02zd: %02X ", i/16, p[i]);
+ break;
+ case 15:
+ fprintf(stderr, "%02X\n", p[i]);
+ break;
+ default:
+ fprintf(stderr, "%02X ", p[i]);
+ break;
+ }
+ }
+ if ((i & 15) != 15) {
+ fprintf(stderr, "\n");
+ }
+}
+#define hexdump(p,n) hexdump0((const unsigned char *)p, n)
+
+static void
+div_inspect(DebugInfoValue *v)
+{
+ switch (v->type) {
+ case VAL_uint:
+ fprintf(stderr,"%d: type:%d size:%zx v:%lx\n",__LINE__,v->type,v->size,v->as.uint64);
+ break;
+ case VAL_int:
+ fprintf(stderr,"%d: type:%d size:%zx v:%ld\n",__LINE__,v->type,v->size,(int64_t)v->as.uint64);
+ break;
+ case VAL_cstr:
+ fprintf(stderr,"%d: type:%d size:%zx v:'%s'\n",__LINE__,v->type,v->size,v->as.ptr);
+ break;
+ case VAL_data:
+ fprintf(stderr,"%d: type:%d size:%zx v:\n",__LINE__,v->type,v->size);
+ hexdump(v->as.ptr, 16);
+ break;
+ }
+}
+#endif
+
+static DIE *
+di_read_die(DebugInfoReader *reader, DIE *die)
+{
+ uint64_t abbrev_number = uleb128(&reader->p);
+ if (abbrev_number == 0) {
+ reader->level--;
+ return NULL;
+ }
+
+ reader->q = di_find_abbrev(reader, abbrev_number);
+
+ die->pos = reader->p - reader->obj->debug_info.ptr - 1;
+ die->tag = (int)uleb128(&reader->q); /* tag */
+ die->has_children = *reader->q++; /* has_children */
+ if (die->has_children) {
+ reader->level++;
+ }
+ return die;
+}
+
+static DebugInfoValue *
+di_read_record(DebugInfoReader *reader, DebugInfoValue *vp)
+{
+ uint64_t at = uleb128(&reader->q);
+ uint64_t form = uleb128(&reader->q);
+ if (!at || !form) return NULL;
+ vp->at = at;
+ vp->form = form;
+ debug_info_reader_read_value(reader, form, vp);
+ return vp;
+}
+
+static void
+di_skip_records(DebugInfoReader *reader)
+{
+ for (;;) {
+ DebugInfoValue v = {{}};
+ uint64_t at = uleb128(&reader->q);
+ uint64_t form = uleb128(&reader->q);
+ if (!at || !form) return;
+ debug_info_reader_read_value(reader, form, &v);
+ }
+}
+
+typedef struct {
+ uint64_t low_pc;
+ uint64_t high_pc;
+ uint64_t ranges;
+ bool low_pc_set;
+ bool high_pc_set;
+ bool ranges_set;
+} ranges_t;
+
+static void
+ranges_set(ranges_t *ptr, DebugInfoValue *v)
+{
+ switch (v->at) {
+ case DW_AT_low_pc:
+ ptr->low_pc = v->as.uint64;
+ ptr->low_pc_set = true;
+ break;
+ case DW_AT_high_pc:
+ if (v->form == DW_FORM_addr) {
+ ptr->high_pc = v->as.uint64;
+ }
+ else {
+ ptr->high_pc = ptr->low_pc + v->as.uint64;
+ }
+ ptr->high_pc_set = true;
+ break;
+ case DW_AT_ranges:
+ ptr->ranges = v->as.uint64;
+ ptr->ranges_set = true;
+ break;
+ }
+}
+
+static uintptr_t
+ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr)
+{
+ if (ptr->high_pc_set) {
+ if (ptr->ranges_set || !ptr->low_pc_set) {
+ exit(1);
+ }
+ if (ptr->low_pc <= addr && addr <= ptr->high_pc) {
+ return (uintptr_t)ptr->low_pc;
+ }
+ }
+ else if (ptr->ranges_set) {
+ /* TODO: support base address selection entry */
+ char *p = reader->obj->debug_ranges.ptr + ptr->ranges;
+ uint64_t base = ptr->low_pc_set ? ptr->low_pc : reader->current_low_pc;
+ for (;;) {
+ uintptr_t from = read_uintptr(&p);
+ uintptr_t to = read_uintptr(&p);
+ if (!from && !to) break;
+ if (from == UINTPTR_MAX) {
+ /* base address selection entry */
+ base = to;
+ }
+ else if (base + from <= addr && addr < base + to) {
+ return from;
+ }
+ }
+ }
+ else if (ptr->low_pc_set) {
+ if (ptr->low_pc == addr) {
+ return (uintptr_t)ptr->low_pc;
+ }
+ }
+ return false;
+}
+
+#if 0
+static void
+ranges_inspect(DebugInfoReader *reader, ranges_t *ptr)
+{
+ if (ptr->high_pc_set) {
+ if (ptr->ranges_set || !ptr->low_pc_set) {
+ fprintf(stderr,"low_pc_set:%d high_pc_set:%d ranges_set:%d\n",ptr->low_pc_set,ptr->high_pc_set,ptr->ranges_set);
+ exit(1);
+ }
+ fprintf(stderr,"low_pc:%"PRIx64" high_pc:%"PRIx64"\n",ptr->low_pc,ptr->high_pc);
+ }
+ else if (ptr->ranges_set) {
+ char *p = reader->obj->debug_ranges.ptr + ptr->ranges;
+ fprintf(stderr,"low_pc:%"PRIx64" ranges:%"PRIx64" %lx ",ptr->low_pc,ptr->ranges, p-reader->obj->mapped);
+ for (;;) {
+ uintptr_t from = read_uintptr(&p);
+ uintptr_t to = read_uintptr(&p);
+ if (!from && !to) break;
+ fprintf(stderr,"%"PRIx64"-%"PRIx64" ",ptr->low_pc+from,ptr->low_pc+to);
+ }
+ fprintf(stderr,"\n");
+ }
+ else if (ptr->low_pc_set) {
+ fprintf(stderr,"low_pc:%"PRIx64"\n",ptr->low_pc);
+ }
+ else {
+ fprintf(stderr,"empty\n");
+ }
+}
+#endif
+
+static int
+di_read_cu(DebugInfoReader *reader)
+{
+ uint64_t unit_length;
+ uint16_t version;
+ uint64_t debug_abbrev_offset;
+ reader->format = 4;
+ reader->current_cu = reader->p;
+ unit_length = read_uint32(&reader->p);
+ if (unit_length == 0xffffffff) {
+ unit_length = read_uint64(&reader->p);
+ reader->format = 8;
+ }
+ reader->cu_end = reader->p + unit_length;
+ version = read_uint16(&reader->p);
+ if (version > 5) {
+ return -1;
+ }
+ else if (version == 5) {
+ /* unit_type = */ read_uint8(&reader->p);
+ reader->address_size = read_uint8(&reader->p);
+ debug_abbrev_offset = read_uint(reader);
+ }
+ else {
+ debug_abbrev_offset = read_uint(reader);
+ reader->address_size = read_uint8(&reader->p);
+ }
+ reader->q0 = reader->obj->debug_abbrev.ptr + debug_abbrev_offset;
+
+ reader->level = 0;
+ di_read_debug_abbrev_cu(reader);
+ if (di_read_debug_line_cu(reader)) return -1;
+
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER_BUILD_DATE)
+ /* Though DWARF specifies "the applicable base address defaults to the base
+ address of the compilation unit", but GCC seems to use zero as default */
+#else
+ do {
+ DIE die;
+
+ if (!di_read_die(reader, &die)) continue;
+
+ if (die.tag != DW_TAG_compile_unit) {
+ di_skip_records(reader);
+ break;
+ }
+
+ /* 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;
+ break;
+ }
+ }
+ } while (0);
+#endif
+ return 0;
+}
+
+static void
+read_abstract_origin(DebugInfoReader *reader, uint64_t abstract_origin, line_info_t *line)
+{
+ char *p = reader->p;
+ char *q = reader->q;
+ int level = reader->level;
+ DIE die;
+
+ reader->p = reader->current_cu + abstract_origin;
+ if (!di_read_die(reader, &die)) goto finish;
+
+ /* enumerate abbrev */
+ for (;;) {
+ DebugInfoValue v = {{}};
+ if (!di_read_record(reader, &v)) break;
+ switch (v.at) {
+ case DW_AT_name:
+ line->sname = get_cstr_value(&v);
+ break;
+ }
+ }
+
+ finish:
+ reader->p = p;
+ reader->q = q;
+ reader->level = level;
+}
+
+static void
+debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
+ line_info_t *lines, int offset)
+{
+ int i;
+ while (reader->p < reader->cu_end) {
+ DIE die;
+ ranges_t ranges = {};
+ line_info_t line = {};
+
+ if (!di_read_die(reader, &die)) continue;
+ /* fprintf(stderr,"%d:%tx: <%d>\n",__LINE__,die.pos,reader->level,die.tag); */
+
+ if (die.tag != DW_TAG_subprogram && die.tag != DW_TAG_inlined_subroutine) {
+ skip_die:
+ di_skip_records(reader);
+ continue;
+ }
+
+ /* enumerate abbrev */
+ for (;;) {
+ DebugInfoValue v = {{}};
+ /* ptrdiff_t pos = reader->p - reader->p0; */
+ if (!di_read_record(reader, &v)) break;
+ /* fprintf(stderr,"\n%d:%tx: AT:%lx FORM:%lx\n",__LINE__,pos,v.at,v.form); */
+ /* div_inspect(&v); */
+ switch (v.at) {
+ case DW_AT_name:
+ 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);
+ break;
+ case DW_AT_call_line:
+ line.line = (int)v.as.uint64;
+ break;
+ case DW_AT_low_pc:
+ case DW_AT_high_pc:
+ case DW_AT_ranges:
+ ranges_set(&ranges, &v);
+ break;
+ case DW_AT_declaration:
+ goto skip_die;
+ case DW_AT_inline:
+ /* 1 or 3 */
+ break; /* goto skip_die; */
+ case DW_AT_abstract_origin:
+ read_abstract_origin(reader, v.as.uint64, &line);
+ break; /* goto skip_die; */
+ }
+ }
+ /* ranges_inspect(reader, &ranges); */
+ /* fprintf(stderr,"%d:%tx: %x ",__LINE__,diepos,die.tag); */
+ for (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);
+ 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); */
+ if (lines[i].sname) {
+ line_info_t *lp = malloc(sizeof(line_info_t));
+ memcpy(lp, &lines[i], sizeof(line_info_t));
+ lines[i].next = lp;
+ lp->dirname = line.dirname;
+ lp->filename = line.filename;
+ lp->line = line.line;
+ lp->saddr = 0;
+ }
+ lines[i].path = reader->obj->path;
+ lines[i].base_addr = line.base_addr;
+ lines[i].sname = line.sname;
+ lines[i].saddr = saddr + reader->obj->base_addr - reader->obj->vmaddr;
+ }
+ }
+ }
+}
+
+#ifdef USE_ELF
+static unsigned long
+uncompress_debug_section(ElfW(Shdr) *shdr, char *file, char **ptr)
+{
+#ifdef SUPPORT_COMPRESSED_DEBUG_LINE
+ ElfW(Chdr) *chdr = (ElfW(Chdr) *)(file + shdr->sh_offset);
+ unsigned long destsize = chdr->ch_size;
+ int ret = 0;
+
+ if (chdr->ch_type != ELFCOMPRESS_ZLIB) {
+ /* unsupported compression type */
+ return 0;
+ }
+
+ *ptr = malloc(destsize);
+ if (!*ptr) return 0;
+ ret = uncompress((Bytef *)*ptr, &destsize,
+ (const Bytef*)chdr + sizeof(ElfW(Chdr)),
+ shdr->sh_size - sizeof(ElfW(Chdr)));
+ if (ret != Z_OK) goto fail;
+ return destsize;
+
+fail:
+ free(*ptr);
+#endif
+ return 0;
+}
/* read file and fill lines */
static uintptr_t
@@ -470,10 +1617,9 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
{
int i, j;
char *shstr;
- char *section_name;
ElfW(Ehdr) *ehdr;
ElfW(Shdr) *shdr, *shstr_shdr;
- ElfW(Shdr) *debug_line_shdr = NULL, *gnu_debuglink_shdr = NULL;
+ ElfW(Shdr) *gnu_debuglink_shdr = NULL;
int fd;
off_t filesize;
char *file;
@@ -509,6 +1655,7 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
kprintf("mmap: %s\n", strerror(e));
goto fail;
}
+ close(fd);
ehdr = (ElfW(Ehdr) *)file;
if (memcmp(ehdr->e_ident, "\177ELF", 4) != 0) {
@@ -516,11 +1663,8 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
* Huh? Maybe filename was overridden by setproctitle() and
* it match non-elf file.
*/
- close(fd);
goto fail;
}
-
- obj->fd = fd;
obj->mapped = file;
obj->mapped_size = (size_t)filesize;
@@ -530,7 +1674,7 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
shstr = file + shstr_shdr->sh_offset;
for (i = 0; i < ehdr->e_shnum; i++) {
- section_name = shstr + shdr[i].sh_name;
+ char *section_name = shstr + shdr[i].sh_name;
switch (shdr[i].sh_type) {
case SHT_STRTAB:
if (!strcmp(section_name, ".strtab")) {
@@ -549,12 +1693,34 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
dynsym_shdr = shdr + i;
break;
case SHT_PROGBITS:
- if (!strcmp(section_name, ".debug_line")) {
- debug_line_shdr = shdr + i;
- }
- else if (!strcmp(section_name, ".gnu_debuglink")) {
+ if (!strcmp(section_name, ".gnu_debuglink")) {
gnu_debuglink_shdr = shdr + i;
}
+ else {
+ const char *debug_section_names[] = {
+ ".debug_abbrev",
+ ".debug_info",
+ ".debug_line",
+ ".debug_ranges",
+ ".debug_str"
+ };
+
+ for (j=0; j < DWARF_SECTION_COUNT; j++) {
+ struct dwarf_section *s = obj_dwarf_section_at(obj, j);
+
+ if (strcmp(section_name, debug_section_names[j]) != 0)
+ continue;
+
+ s->ptr = file + shdr[i].sh_offset;
+ s->size = shdr[i].sh_size;
+ s->flags = shdr[i].sh_flags;
+ if (s->flags & SHF_COMPRESSED) {
+ s->size = uncompress_debug_section(&shdr[i], file, &s->ptr);
+ if (!s->size) goto fail;
+ }
+ break;
+ }
+ }
break;
}
}
@@ -566,20 +1732,22 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
char *strtab = file + dynstr_shdr->sh_offset;
ElfW(Sym) *symtab = (ElfW(Sym) *)(file + dynsym_shdr->sh_offset);
int symtab_count = (int)(dynsym_shdr->sh_size / sizeof(ElfW(Sym)));
- for (j = 0; j < symtab_count; j++) {
- ElfW(Sym) *sym = &symtab[j];
- Dl_info info;
- void *h, *s;
- if (ELF_ST_TYPE(sym->st_info) != STT_FUNC || sym->st_size <= 0) continue;
- h = dlopen(NULL, RTLD_NOW|RTLD_LOCAL);
- if (!h) continue;
- s = dlsym(h, strtab + sym->st_name);
- if (!s) continue;
- if (dladdr(s, &info)) {
- dladdr_fbase = (uintptr_t)info.dli_fbase;
- break;
- }
- }
+ void *handle = dlopen(NULL, RTLD_NOW|RTLD_LOCAL);
+ if (handle) {
+ for (j = 0; j < symtab_count; j++) {
+ ElfW(Sym) *sym = &symtab[j];
+ Dl_info info;
+ void *s;
+ if (ELF_ST_TYPE(sym->st_info) != STT_FUNC || sym->st_size == 0) continue;
+ s = dlsym(handle, strtab + sym->st_name);
+ if (s && dladdr(s, &info)) {
+ obj->base_addr = dladdr_fbase;
+ dladdr_fbase = (uintptr_t)info.dli_fbase;
+ break;
+ }
+ }
+ dlclose(handle);
+ }
if (ehdr->e_type == ET_EXEC) {
obj->base_addr = 0;
}
@@ -590,33 +1758,48 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
}
}
- if (!symtab_shdr) {
- symtab_shdr = dynsym_shdr;
- strtab_shdr = dynstr_shdr;
- }
-
- if (symtab_shdr && strtab_shdr) {
- char *strtab = file + strtab_shdr->sh_offset;
- ElfW(Sym) *symtab = (ElfW(Sym) *)(file + symtab_shdr->sh_offset);
- int symtab_count = (int)(symtab_shdr->sh_size / sizeof(ElfW(Sym)));
- for (j = 0; j < symtab_count; j++) {
- ElfW(Sym) *sym = &symtab[j];
- uintptr_t saddr = (uintptr_t)sym->st_value + obj->base_addr;
- if (ELF_ST_TYPE(sym->st_info) != STT_FUNC || sym->st_size <= 0) continue;
- for (i = offset; i < num_traces; i++) {
- uintptr_t d = (uintptr_t)traces[i] - saddr;
- if (lines[i].line > 0 || d <= 0 || d > (uintptr_t)sym->st_size)
- continue;
- /* fill symbol name and addr from .symtab */
- lines[i].sname = strtab + sym->st_name;
- lines[i].saddr = saddr;
- lines[i].path = obj->path;
- lines[i].base_addr = obj->base_addr;
- }
- }
+ if (obj->debug_info.ptr && obj->debug_abbrev.ptr) {
+ DebugInfoReader reader;
+ debug_info_reader_init(&reader, obj);
+ i = 0;
+ while (reader.p < reader.pend) {
+ /* fprintf(stderr, "%d:%tx: CU[%d]\n", __LINE__, reader.p - reader.obj->debug_info.ptr, i++); */
+ if (di_read_cu(&reader)) goto use_symtab;
+ debug_info_read(&reader, num_traces, traces, lines, offset);
+ }
+ }
+ else {
+ /* This file doesn't have dwarf, use symtab or dynsym */
+use_symtab:
+ if (!symtab_shdr) {
+ /* This file doesn't have symtab, use dynsym instead */
+ symtab_shdr = dynsym_shdr;
+ strtab_shdr = dynstr_shdr;
+ }
+
+ if (symtab_shdr && strtab_shdr) {
+ char *strtab = file + strtab_shdr->sh_offset;
+ ElfW(Sym) *symtab = (ElfW(Sym) *)(file + symtab_shdr->sh_offset);
+ int symtab_count = (int)(symtab_shdr->sh_size / sizeof(ElfW(Sym)));
+ for (j = 0; j < symtab_count; j++) {
+ ElfW(Sym) *sym = &symtab[j];
+ uintptr_t saddr = (uintptr_t)sym->st_value + obj->base_addr;
+ if (ELF_ST_TYPE(sym->st_info) != STT_FUNC) continue;
+ for (i = offset; i < num_traces; i++) {
+ uintptr_t d = (uintptr_t)traces[i] - saddr;
+ if (lines[i].line > 0 || d > (uintptr_t)sym->st_size)
+ continue;
+ /* fill symbol name and addr from .symtab */
+ if (!lines[i].sname) lines[i].sname = strtab + sym->st_name;
+ lines[i].saddr = saddr;
+ lines[i].path = obj->path;
+ lines[i].base_addr = obj->base_addr;
+ }
+ }
+ }
}
- if (!debug_line_shdr) {
+ if (!obj->debug_line.ptr) {
/* This file doesn't have .debug_line section,
let's check .gnu_debuglink section instead. */
if (gnu_debuglink_shdr && check_debuglink) {
@@ -628,15 +1811,232 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
}
if (parse_debug_line(num_traces, traces,
- file + debug_line_shdr->sh_offset,
- debug_line_shdr->sh_size,
- obj, lines, offset))
- goto fail;
+ obj->debug_line.ptr,
+ obj->debug_line.size,
+ obj, lines, offset) == -1)
+ goto fail;
+
finish:
return dladdr_fbase;
fail:
return (uintptr_t)-1;
}
+#else /* Mach-O */
+/* read file and fill lines */
+static uintptr_t
+fill_lines(int num_traces, void **traces, int check_debuglink,
+ obj_info_t **objp, line_info_t *lines, int offset)
+{
+# ifdef __LP64__
+# define LP(x) x##_64
+# else
+# define LP(x) x
+# endif
+ int fd;
+ off_t filesize;
+ char *file, *p = NULL;
+ obj_info_t *obj = *objp;
+ struct LP(mach_header) *header;
+ uintptr_t dladdr_fbase = 0;
+
+ {
+ char *s = binary_filename;
+ char *base = strrchr(binary_filename, '/')+1;
+ size_t max = PATH_MAX;
+ size_t size = strlen(binary_filename);
+ size_t basesize = size - (base - binary_filename);
+ s += size;
+ max -= size;
+ p = s;
+ size = strlcpy(s, ".dSYM/Contents/Resources/DWARF/", max);
+ if (size == 0) goto fail;
+ s += size;
+ max -= size;
+ if (max <= basesize) goto fail;
+ memcpy(s, base, basesize);
+ s[basesize] = 0;
+
+ fd = open(binary_filename, O_RDONLY);
+ if (fd < 0) {
+ *p = 0; /* binary_filename becomes original file name */
+ fd = open(binary_filename, O_RDONLY);
+ if (fd < 0) {
+ goto fail;
+ }
+ }
+ }
+
+ filesize = lseek(fd, 0, SEEK_END);
+ if (filesize < 0) {
+ int e = errno;
+ close(fd);
+ kprintf("lseek: %s\n", strerror(e));
+ goto fail;
+ }
+#if SIZEOF_OFF_T > SIZEOF_SIZE_T
+ if (filesize > (off_t)SIZE_MAX) {
+ close(fd);
+ kprintf("Too large file %s\n", binary_filename);
+ goto fail;
+ }
+#endif
+ lseek(fd, 0, SEEK_SET);
+ /* async-signal unsafe */
+ file = (char *)mmap(NULL, (size_t)filesize, PROT_READ, MAP_SHARED, fd, 0);
+ if (file == MAP_FAILED) {
+ int e = errno;
+ close(fd);
+ kprintf("mmap: %s\n", strerror(e));
+ goto fail;
+ }
+ close(fd);
+
+ obj->mapped = file;
+ obj->mapped_size = (size_t)filesize;
+
+ header = (struct LP(mach_header) *)file;
+ if (header->magic == LP(MH_MAGIC)) {
+ /* non universal binary */
+ p = file;
+ }
+ else if (header->magic == FAT_CIGAM) {
+ struct fat_header *fat = (struct fat_header *)file;
+ char *q = file + sizeof(*fat);
+ uint32_t nfat_arch = __builtin_bswap32(fat->nfat_arch);
+ /* fprintf(stderr,"%d: fat:%s %d\n",__LINE__, binary_filename,nfat_arch); */
+ for (uint32_t i = 0; i < nfat_arch; i++) {
+ struct fat_arch *arch = (struct fat_arch *)q;
+ cpu_type_t cputype = __builtin_bswap32(arch->cputype);
+ cpu_subtype_t cpusubtype = __builtin_bswap32(arch->cpusubtype);
+ uint32_t offset = __builtin_bswap32(arch->offset);
+ /* fprintf(stderr,"%d: fat %d %x/%x %x/%x\n",__LINE__, i, _mh_execute_header.cputype,_mh_execute_header.cpusubtype, cputype,cpusubtype); */
+ if (_mh_execute_header.cputype == cputype &&
+ (_mh_execute_header.cpusubtype & ~CPU_SUBTYPE_MASK) == cpusubtype) {
+ p = file + offset;
+ file = p;
+ header = (struct LP(mach_header) *)p;
+ if (header->magic == LP(MH_MAGIC)) {
+ goto found_mach_header;
+ }
+ break;
+ }
+ q += sizeof(*arch);
+ }
+ kprintf("'%s' is not a Mach-O universal binary file!\n",binary_filename);
+ close(fd);
+ goto fail;
+ }
+ else {
+ kprintf("'%s' is not a "
+# ifdef __LP64__
+ "64"
+# else
+ "32"
+# endif
+ "-bit Mach-O file!\n",binary_filename);
+ close(fd);
+ goto fail;
+ }
+found_mach_header:
+ p += sizeof(*header);
+
+ for (uint32_t i = 0; i < (uint32_t)header->ncmds; i++) {
+ struct load_command *lcmd = (struct load_command *)p;
+ switch (lcmd->cmd) {
+ case LP(LC_SEGMENT):
+ {
+ static const char *debug_section_names[] = {
+ "__debug_abbrev",
+ "__debug_info",
+ "__debug_line",
+ "__debug_ranges",
+ "__debug_str"
+ };
+ struct LP(segment_command) *scmd = (struct LP(segment_command) *)lcmd;
+ if (strcmp(scmd->segname, "__TEXT") == 0) {
+ obj->vmaddr = scmd->vmaddr;
+ }
+ else if (strcmp(scmd->segname, "__DWARF") == 0) {
+ p += sizeof(struct LP(segment_command));
+ for (uint64_t i = 0; i < scmd->nsects; i++) {
+ struct LP(section) *sect = (struct LP(section) *)p;
+ p += sizeof(struct LP(section));
+ for (int j=0; j < DWARF_SECTION_COUNT; j++) {
+ struct dwarf_section *s = obj_dwarf_section_at(obj, j);
+
+ if (strcmp(sect->sectname, debug_section_names[j]) != 0)
+ continue;
+
+ s->ptr = file + sect->offset;
+ s->size = sect->size;
+ s->flags = sect->flags;
+ if (s->flags & SHF_COMPRESSED) {
+ goto fail;
+ }
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case LC_SYMTAB:
+ {
+ struct symtab_command *cmd = (struct symtab_command *)lcmd;
+ struct LP(nlist) *nl = (struct LP(nlist) *)(file + cmd->symoff);
+ char *strtab = file + cmd->stroff, *sname;
+ uint32_t j;
+ uintptr_t saddr;
+ /* kprintf("[%2d]: %x/symtab %p\n", i, cmd->cmd, p); */
+ for (j = 0; j < cmd->nsyms; j++) {
+ uintptr_t symsize, d;
+ struct LP(nlist) *e = &nl[j];
+ /* kprintf("[%2d][%4d]: %02x/%x/%x: %s %llx\n", i, j, e->n_type,e->n_sect,e->n_desc,strtab+e->n_un.n_strx,e->n_value); */
+ if (e->n_type != N_FUN) continue;
+ if (e->n_sect) {
+ saddr = (uintptr_t)e->n_value + obj->base_addr - obj->vmaddr;
+ sname = strtab + e->n_un.n_strx;
+ /* kprintf("[%2d][%4d]: %02x/%x/%x: %s %llx\n", i, j, e->n_type,e->n_sect,e->n_desc,strtab+e->n_un.n_strx,e->n_value); */
+ continue;
+ }
+ for (int k = offset; k < num_traces; k++) {
+ d = (uintptr_t)traces[k] - saddr;
+ symsize = e->n_value;
+ /* kprintf("%lx %lx %lx\n",saddr,symsize,traces[k]); */
+ if (lines[k].line > 0 || d > (uintptr_t)symsize)
+ continue;
+ /* fill symbol name and addr from .symtab */
+ if (!lines[k].sname) lines[k].sname = sname;
+ lines[k].saddr = saddr;
+ lines[k].path = obj->path;
+ lines[k].base_addr = obj->base_addr;
+ }
+ }
+ }
+ }
+ p += lcmd->cmdsize;
+ }
+
+ if (obj->debug_info.ptr && obj->debug_abbrev.ptr) {
+ DebugInfoReader reader;
+ debug_info_reader_init(&reader, obj);
+ while (reader.p < reader.pend) {
+ if (di_read_cu(&reader)) goto fail;
+ debug_info_read(&reader, num_traces, traces, lines, offset);
+ }
+ }
+
+ if (parse_debug_line(num_traces, traces,
+ obj->debug_line.ptr,
+ obj->debug_line.size,
+ obj, lines, offset) == -1)
+ goto fail;
+
+ return dladdr_fbase;
+fail:
+ return (uintptr_t)-1;
+}
+#endif
#define HAVE_MAIN_EXE_PATH
#if defined(__FreeBSD__)
@@ -649,7 +2049,7 @@ fail:
* it is NUL terminated.
*/
#if defined(__linux__)
-ssize_t
+static ssize_t
main_exe_path(void)
{
# define PROC_SELF_EXE "/proc/self/exe"
@@ -658,7 +2058,7 @@ main_exe_path(void)
return len;
}
#elif defined(__FreeBSD__)
-ssize_t
+static ssize_t
main_exe_path(void)
{
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
@@ -675,6 +2075,53 @@ main_exe_path(void)
#undef HAVE_MAIN_EXE_PATH
#endif
+static void
+print_line0(line_info_t *line, void *address)
+{
+ uintptr_t addr = (uintptr_t)address;
+ uintptr_t d = addr - line->saddr;
+ if (!address) {
+ /* inlined */
+ if (line->dirname && line->dirname[0]) {
+ kprintf("%s(%s) %s/%s:%d\n", line->path, line->sname, line->dirname, line->filename, line->line);
+ }
+ else {
+ kprintf("%s(%s) %s:%d\n", line->path, line->sname, line->filename, line->line);
+ }
+ }
+ else if (!line->path) {
+ kprintf("[0x%"PRIxPTR"]\n", addr);
+ }
+ else if (!line->saddr || !line->sname) {
+ kprintf("%s(0x%"PRIxPTR") [0x%"PRIxPTR"]\n", line->path, addr-line->base_addr, addr);
+ }
+ else if (line->line <= 0) {
+ kprintf("%s(%s+0x%"PRIxPTR") [0x%"PRIxPTR"]\n", line->path, line->sname,
+ d, addr);
+ }
+ else if (!line->filename) {
+ kprintf("%s(%s+0x%"PRIxPTR") [0x%"PRIxPTR"] ???:%d\n", line->path, line->sname,
+ d, addr, line->line);
+ }
+ else if (line->dirname && line->dirname[0]) {
+ kprintf("%s(%s+0x%"PRIxPTR") [0x%"PRIxPTR"] %s/%s:%d\n", line->path, line->sname,
+ d, addr, line->dirname, line->filename, line->line);
+ }
+ else {
+ kprintf("%s(%s+0x%"PRIxPTR") [0x%"PRIxPTR"] %s:%d\n", line->path, line->sname,
+ d, addr, line->filename, line->line);
+ }
+}
+
+static void
+print_line(line_info_t *line, void *address)
+{
+ print_line0(line, address);
+ if (line->next) {
+ print_line(line->next, NULL);
+ }
+}
+
void
rb_dump_backtrace_with_lines(int num_traces, void **traces)
{
@@ -726,7 +2173,9 @@ rb_dump_backtrace_with_lines(int num_traces, void **traces)
path = info.dli_fname;
obj->path = path;
lines[i].path = path;
- strcpy(binary_filename, path);
+ lines[i].sname = info.dli_sname;
+ lines[i].saddr = (uintptr_t)info.dli_saddr;
+ strlcpy(binary_filename, path, PATH_MAX);
if (fill_lines(num_traces, traces, 1, &obj, lines, i) == (uintptr_t)-1)
break;
}
@@ -736,46 +2185,36 @@ next_line:
/* output */
for (i = 0; i < num_traces; i++) {
- line_info_t *line = &lines[i];
- uintptr_t addr = (uintptr_t)traces[i];
- uintptr_t d = addr - line->saddr;
- if (!line->path) {
- kprintf("[0x%lx]\n", addr);
- }
- else if (!line->saddr || !line->sname) {
- kprintf("%s [0x%lx]\n", line->path, addr);
- }
- else if (line->line <= 0) {
- kprintf("%s(%s+0x%lx) [0x%lx]\n", line->path, line->sname,
- d, addr);
- }
- else if (!line->filename) {
- kprintf("%s(%s+0x%lx) [0x%lx] ???:%d\n", line->path, line->sname,
- d, addr, line->line);
- }
- else if (line->dirname && line->dirname[0]) {
- kprintf("%s(%s+0x%lx) [0x%lx] %s/%s:%d\n", line->path, line->sname,
- d, addr, line->dirname, line->filename, line->line);
- }
- else {
- kprintf("%s(%s+0x%lx) [0x%lx] %s:%d\n", line->path, line->sname,
- d, addr, line->filename, line->line);
- }
+ print_line(&lines[i], traces[i]);
+
/* FreeBSD's backtrace may show _start and so on */
- if (line->sname && strcmp("main", line->sname) == 0)
+ if (lines[i].sname && strcmp("main", lines[i].sname) == 0)
break;
}
/* free */
while (obj) {
obj_info_t *o = obj;
- obj = o->next;
- if (o->fd) {
- munmap(o->mapped, o->mapped_size);
- close(o->fd);
+ for (i=0; i < DWARF_SECTION_COUNT; i++) {
+ struct dwarf_section *s = obj_dwarf_section_at(obj, i);
+ if (s->flags & SHF_COMPRESSED) {
+ free(s->ptr);
+ }
+ }
+ if (obj->mapped_size) {
+ munmap(obj->mapped, obj->mapped_size);
}
+ obj = o->next;
free(o);
}
+ for (i = 0; i < num_traces; i++) {
+ line_info_t *line = lines[i].next;
+ while (line) {
+ line_info_t *l = line;
+ line = line->next;
+ free(l);
+ }
+ }
free(lines);
free(dladdr_fbases);
}
@@ -821,7 +2260,7 @@ next_line:
#define MAXNBUF (sizeof(intmax_t) * CHAR_BIT + 1)
static inline int toupper(int c) { return ('A' <= c && c <= 'Z') ? (c&0x5f) : c; }
#define hex2ascii(hex) (hex2ascii_data[hex])
-char const hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+static const char hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz";
static inline int imax(int a, int b) { return (a > b ? a : b); }
static int kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap);
@@ -835,7 +2274,7 @@ static void putce(int c)
(void)ret;
}
-int
+static int
kprintf(const char *fmt, ...)
{
va_list ap;
diff --git a/addr2line.h b/addr2line.h
index d99f010934..4f6cf179ef 100644
--- a/addr2line.h
+++ b/addr2line.h
@@ -11,7 +11,7 @@
#ifndef RUBY_ADDR2LINE_H
#define RUBY_ADDR2LINE_H
-#ifdef USE_ELF
+#if (defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H))
void
rb_dump_backtrace_with_lines(int num_traces, void **traces);
diff --git a/appveyor.yml b/appveyor.yml
index 1268a7f7e9..3bf1568cd9 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,27 +1,145 @@
---
+version: '{build}'
shallow_clone: true
-platform: x64
+platform:
+ - x64
environment:
- ruby_version: "23-x64"
-install:
- - SET
- - '"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" amd64'
- - SET PATH=\usr\local\bin;C:\Ruby%ruby_version%\bin;C:\Ruby%ruby_version%\Devkit\mingw\bin;%PATH%;C:\msys64\usr\bin
- - ruby --version
- - 'cl'
- - SET
- - mkdir \usr\local\bin
- - mkdir \usr\local\include
- - mkdir \usr\local\lib
- - appveyor DownloadFile http://downloads.sourceforge.net/project/libpng/zlib/1.2.8/zlib128.zip
- - 7z x -o%APPVEYOR_BUILD_FOLDER%\ext\zlib zlib128.zip
- - for %%I in (c:\OpenSSL-Win64\*.dll) do mklink /h \usr\local\bin\%%~nxI %%I
-build_script:
- - cd %APPVEYOR_BUILD_FOLDER%
- - win32\configure.bat --without-ext=+,dbm,gdbm,readline --with-opt-dir=/usr/local --with-openssl-dir=c:/OpenSSL-Win64
- - nmake -l up
- - nmake -l
- - nmake install-nodoc
-test_script:
- - nmake -l "TESTOPTS=-v -q" btest
- - nmake -l "TESTOPTS=-v -q" test-basic
+ ruby_version: "24-%Platform%"
+ zlib_version: "1.2.11"
+ matrix:
+ # to reduce time for finishing all jobs, run the slowest msys2 build first.
+ - build: msys2
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ - build: vs
+ vs: 120
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
+ - build: vs
+ vs: 140
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ GEMS_FOR_TEST: "timezone tzinfo"
+notifications:
+ - provider: Webhook
+ url:
+ secure: iMINHMS0nZabaDsxN9omRDsekxzVvAAzEq5ev7lN6vZ6r9zNhl3/F/7POIVyahAwVFpRDeQT/iUugpAGWmOt3eGL/lZIdqiJFZ9DjPSkP68= # #alerts
+ method: POST
+ # "icon_url" is the url used by `provider: Slack`
+ body: >-
+ {{^isPullRequest}}
+ {
+ "attachments": [
+ {
+ "title": "Build {{projectName}} {{buildVersion}} {{status}}",
+ "fallback": "AppVeyor Build {{projectName}} {{buildVersion}} {{status}}",
+ "title_link": "{{buildUrl}}",
+ "text": "Commit <{{commitUrl}}|{{commitId}}> by {{commitAuthor}} on {{commitDate}}: _{{commitMessage}}_",
+ {{#passed}}
+ "color": "#44ee44"
+ {{/passed}}
+ {{#failed}}
+ "color": "#ee4444"
+ {{/failed}}
+ }
+ ],
+ "icon_url": "https://slack-files2.s3-us-west-2.amazonaws.com/bot_icons/2018-02-10/314363543719_48.png",
+ "username": "AppVeyor CI"
+ }
+ {{/isPullRequest}}
+ on_build_success: false
+ on_build_failure: true
+ on_build_status_changed: true
+for:
+-
+ matrix:
+ only:
+ - build: vs
+ install:
+ - ver
+ - chcp
+ - SET BITS=%Platform:x86=32%
+ - SET BITS=%BITS:x=%
+ - SET OPENSSL_DIR=c:\OpenSSL-Win%BITS%
+ - CALL SET vcvars=%%^VS%VS%COMNTOOLS^%%..\..\VC\vcvarsall.bat
+ - SET vcvars
+ - '"%vcvars%" %Platform:x64=amd64%'
+ - SET ruby_path=C:\Ruby%ruby_version:-x86=%
+ - SET PATH=\usr\local\bin;%ruby_path%\bin;%PATH%;C:\msys64\mingw64\bin;C:\msys64\usr\bin
+ - ruby --version
+ - 'cl'
+ - echo> Makefile srcdir=.
+ - echo>> Makefile MSC_VER=0
+ - echo>> Makefile RT=none
+ - echo>> Makefile RT_VER=0
+ - echo>> Makefile BUILTIN_ENCOBJS=nul
+ - type win32\Makefile.sub >> Makefile
+ - nmake %mflags% touch-unicode-files
+ - nmake %mflags% up incs UNICODE_FILES=.
+ - del Makefile
+ - mkdir \usr\local\bin
+ - mkdir \usr\local\include
+ - mkdir \usr\local\lib
+ - appveyor DownloadFile https://zlib.net/zlib%zlib_version:.=%.zip
+ - 7z x -o%APPVEYOR_BUILD_FOLDER%\ext\zlib zlib%zlib_version:.=%.zip
+ - for %%I in (%OPENSSL_DIR%\*.dll) do mklink /h \usr\local\bin\%%~nxI %%I
+ - mkdir %Platform%-mswin_%vs%
+ - ps: Get-ChildItem "win32" -Recurse | foreach {$_.Attributes = 'Readonly'}
+ - ps: Get-Item $env:Platform"-mswin_"$env:vs | foreach {$_.Attributes = 'Normal'}
+ build_script:
+ - cd %APPVEYOR_BUILD_FOLDER%
+ - cd %Platform%-mswin_%vs%
+ - ..\win32\configure.bat --without-ext=+,dbm,gdbm,readline --with-opt-dir=/usr/local --with-openssl-dir=%OPENSSL_DIR:\=/%
+ - nmake -l
+ - nmake install-nodoc
+ - \usr\bin\ruby -v -e "p :locale => Encoding.find('locale'), :filesystem => Encoding.find('filesystem')"
+ - if not "%GEMS_FOR_TEST%" == "" \usr\bin\gem install --no-document %GEMS_FOR_TEST%
+ test_script:
+ - set /a JOBS=%NUMBER_OF_PROCESSORS%
+ - nmake -l "TESTOPTS=-v -q" btest
+ - nmake -l "TESTOPTS=-v -q" test-basic
+ - nmake -l "TESTOPTS=-q --subprocess-timeout-scale=3.0 --excludes=../test/excludes/_appveyor -j%JOBS% --exclude win32ole --exclude test_bignum --exclude test_syntax --exclude test_open-uri --exclude test_bundled_ca" test-all
+ # separately execute tests without -j which may crash worker with -j.
+ - nmake -l "TESTOPTS=-v --subprocess-timeout-scale=3.0 --excludes=../test/excludes/_appveyor" test-all TESTS="../test/win32ole ../test/ruby/test_bignum.rb ../test/ruby/test_syntax.rb ../test/open-uri/test_open-uri.rb ../test/rubygems/test_bundled_ca.rb"
+ - nmake -l test-spec MSPECOPT=-fs # not using `-j` because sometimes `mspec -j` silently dies on Windows
+-
+ matrix:
+ only:
+ - build: msys2
+ install:
+ - ver
+ - chcp
+ - set /a JOBS=%NUMBER_OF_PROCESSORS%
+ - set MSYS_NO_PATHCONV=1
+ - SET MSYSTEM=%Platform:x86=32%
+ - SET MSYSTEM=%MSYSTEM:x=MINGW%
+ - SET MSYS2_ARCH=%Platform:x86=i686%
+ - SET MSYS2_ARCH=%MSYS2_ARCH:x64=x86_64%
+ - set MSYSTEM_PREFIX=/mingw64
+ - set MINGW_CHOST=%MSYS2_ARCH%-w64-mingw32
+ - SET ruby_path=C:\Ruby%ruby_version:-x86=%
+ - cd ..
+ - mkdir build
+ - mkdir install
+ - SET PATH=%ruby_path%\bin;C:\msys64\%MSYSTEM%\bin;C:\msys64\usr\bin;%PATH%
+ - ruby --version
+ build_script:
+ # always update database
+ - pacman -Sy
+ - pacman -S --noconfirm --needed --noprogressbar mingw-w64-x86_64-toolchain
+ - pacman -S --noconfirm --needed --noprogressbar mingw-w64-x86_64-gdbm mingw-w64-x86_64-gmp mingw-w64-x86_64-libffi mingw-w64-x86_64-ncurses mingw-w64-x86_64-readline mingw-w64-x86_64-zlib
+ - cd %APPVEYOR_BUILD_FOLDER%
+ - set CFLAGS=-march=%MSYS2_ARCH:_=-% -mtune=generic -O3 -pipe
+ - set CXXFLAGS=%CFLAGS%
+ - set CPPFLAGS=-D_FORTIFY_SOURCE=2 -D__USE_MINGW_ANSI_STDIO=1 -DFD_SETSIZE=2048
+ - set LDFLAGS=-pipe
+ - sh -c "autoreconf -fi"
+ - cd ..\build
+ - sh ../ruby/configure --disable-install-doc --prefix=/. --build=%MINGW_CHOST% --host=%MINGW_CHOST% --target=%MINGW_CHOST%
+ - mingw32-make -j%JOBS% up
+ - mingw32-make -j%JOBS%
+ - mingw32-make DESTDIR=../install install-nodoc
+ - if not "%GEMS_FOR_TEST%" == "" ..\install\bin\gem install --no-document %GEMS_FOR_TEST%
+ test_script:
+ - mingw32-make test
+ - mingw32-make test-all TESTOPTS="--retry --job-status=normal --show-skip --subprocess-timeout-scale=1.5 --excludes=../ruby/test/excludes/_appveyor -j %JOBS% --exclude win32ole --exclude test_open-uri"
+ # separately execute tests without -j which may crash worker with -j.
+ - mingw32-make test-all TESTOPTS="--retry --job-status=normal --show-skip --subprocess-timeout-scale=1.5 --excludes=../ruby/test/excludes/_appveyor" TESTS="../ruby/test/win32ole ../ruby/test/open-uri/test_open-uri.rb"
+ - mingw32-make test-spec MSPECOPT=-fs # not using `-j` because sometimes `mspec -j` silently dies on Windows
diff --git a/array.c b/array.c
index b99ab450a7..ec6f232662 100644
--- a/array.c
+++ b/array.c
@@ -11,23 +11,29 @@
**********************************************************************/
-#include "internal.h"
+#include "ruby/encoding.h"
#include "ruby/util.h"
#include "ruby/st.h"
#include "probes.h"
#include "id.h"
+#include "debug_counter.h"
+#include "gc.h"
+#include "transient_heap.h"
+#include "internal.h"
-#ifndef ARRAY_DEBUG
+#if !ARRAY_DEBUG
# define NDEBUG
#endif
#include "ruby_assert.h"
VALUE rb_cArray;
-static ID id_cmp, id_div, id_power;
+/* for OPTIMIZED_CMP: */
+#define id_cmp idCmp
#define ARY_DEFAULT_SIZE 16
#define ARY_MAX_SIZE (LONG_MAX / (int)sizeof(VALUE))
+#define SMALL_ARRAY_LEN 16
# define ARY_SHARED_P(ary) \
(assert(!FL_TEST((ary), ELTS_SHARED) || !FL_TEST((ary), RARRAY_EMBED_FLAG)), \
@@ -38,17 +44,21 @@ static ID id_cmp, id_div, id_power;
#define ARY_HEAP_PTR(a) (assert(!ARY_EMBED_P(a)), RARRAY(a)->as.heap.ptr)
#define ARY_HEAP_LEN(a) (assert(!ARY_EMBED_P(a)), RARRAY(a)->as.heap.len)
+#define ARY_HEAP_CAPA(a) (assert(!ARY_EMBED_P(a)), RARRAY(a)->as.heap.aux.capa)
+
#define ARY_EMBED_PTR(a) (assert(ARY_EMBED_P(a)), RARRAY(a)->as.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)))
-#define ARY_HEAP_SIZE(a) (assert(!ARY_EMBED_P(a)), assert(ARY_OWNS_HEAP_P(a)), RARRAY(a)->as.heap.aux.capa * sizeof(VALUE))
+#define ARY_HEAP_SIZE(a) (assert(!ARY_EMBED_P(a)), assert(ARY_OWNS_HEAP_P(a)), ARY_HEAP_CAPA(a) * sizeof(VALUE))
#define ARY_OWNS_HEAP_P(a) (!FL_TEST((a), ELTS_SHARED|RARRAY_EMBED_FLAG))
#define FL_SET_EMBED(a) do { \
assert(!ARY_SHARED_P(a)); \
FL_SET((a), RARRAY_EMBED_FLAG); \
+ RARY_TRANSIENT_UNSET(a); \
+ ary_verify(a); \
} while (0)
#define FL_UNSET_EMBED(ary) FL_UNSET((ary), RARRAY_EMBED_FLAG|RARRAY_EMBED_LEN_MASK)
#define FL_SET_SHARED(ary) do { \
@@ -98,7 +108,7 @@ static ID id_cmp, id_div, id_power;
} while (0)
#define ARY_CAPA(ary) (ARY_EMBED_P(ary) ? RARRAY_EMBED_LEN_MAX : \
- ARY_SHARED_ROOT_P(ary) ? RARRAY_LEN(ary) : RARRAY(ary)->as.heap.aux.capa)
+ ARY_SHARED_ROOT_P(ary) ? RARRAY_LEN(ary) : ARY_HEAP_CAPA(ary))
#define ARY_SET_CAPA(ary, n) do { \
assert(!ARY_EMBED_P(ary)); \
assert(!ARY_SHARED_P(ary)); \
@@ -126,11 +136,82 @@ static ID id_cmp, id_div, id_power;
} while (0)
#define FL_SET_SHARED_ROOT(ary) do { \
assert(!ARY_EMBED_P(ary)); \
+ assert(!RARRAY_TRANSIENT_P(ary)); \
FL_SET((ary), RARRAY_SHARED_ROOT_FLAG); \
} while (0)
#define ARY_SET(a, i, v) RARRAY_ASET((assert(!ARY_SHARED_P(a)), (a)), (i), (v))
+
+#if ARRAY_DEBUG
+#define ary_verify(ary) ary_verify_(ary, __FILE__, __LINE__)
+
+static VALUE
+ary_verify_(VALUE ary, const char *file, int line)
+{
+ assert(RB_TYPE_P(ary, T_ARRAY));
+
+ if (FL_TEST(ary, ELTS_SHARED)) {
+ VALUE root = RARRAY(ary)->as.heap.aux.shared;
+ 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(FL_TEST(root, RARRAY_SHARED_ROOT_FLAG));
+ assert(root_ptr <= ptr && ptr + len <= root_ptr + root_len);
+ ary_verify(root);
+ }
+ else if (ARY_EMBED_P(ary)) {
+ assert(!RARRAY_TRANSIENT_P(ary));
+ assert(!ARY_SHARED_P(ary));
+ assert(RARRAY_LEN(ary) <= RARRAY_EMBED_LEN_MAX);
+ }
+ else {
+#if 1
+ const VALUE *ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
+ long i, len = RARRAY_LEN(ary);
+ volatile VALUE v;
+ if (len > 1) len = 1; /* check only HEAD */
+ for (i=0; i<len; i++) {
+ v = ptr[i]; /* access check */
+ }
+ v = v;
+#endif
+ }
+
+ if (RARRAY_TRANSIENT_P(ary)) {
+ assert(rb_transient_heap_managed_ptr_p(RARRAY_CONST_PTR_TRANSIENT(ary)));
+ }
+
+ rb_transient_heap_verify();
+
+ return ary;
+}
+
+void
+rb_ary_verify(VALUE ary){
+ ary_verify(ary);
+}
+#else
+#define ary_verify(ary) ((void)0)
+#endif
+
+VALUE *
+rb_ary_ptr_use_start(VALUE ary)
+{
+#if ARRAY_DEBUG
+ FL_SET_RAW(ary, RARRAY_PTR_IN_USE_FLAG);
+#endif
+ return (VALUE *)RARRAY_CONST_PTR_TRANSIENT(ary);
+}
+
+void
+rb_ary_ptr_use_end(VALUE ary)
+{
+#if ARRAY_DEBUG
+ FL_UNSET_RAW(ary, RARRAY_PTR_IN_USE_FLAG);
+#endif
+}
+
void
rb_mem_clear(register VALUE *mem, register long size)
{
@@ -142,7 +223,7 @@ rb_mem_clear(register VALUE *mem, register long size)
static void
ary_mem_clear(VALUE ary, long beg, long size)
{
- RARRAY_PTR_USE(ary, ptr, {
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
rb_mem_clear(ptr + beg, size);
});
}
@@ -158,7 +239,7 @@ memfill(register VALUE *mem, register long size, register VALUE val)
static void
ary_memfill(VALUE ary, long beg, long size, VALUE val)
{
- RARRAY_PTR_USE(ary, ptr, {
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
memfill(ptr + beg, size, val);
RB_OBJ_WRITTEN(ary, Qundef, val);
});
@@ -167,28 +248,22 @@ ary_memfill(VALUE ary, long beg, long size, VALUE val)
static void
ary_memcpy0(VALUE ary, long beg, long argc, const VALUE *argv, VALUE buff_owner_ary)
{
-#if 1
assert(!ARY_SHARED_P(buff_owner_ary));
if (argc > (int)(128/sizeof(VALUE)) /* is magic number (cache line size) */) {
- rb_gc_writebarrier_remember(buff_owner_ary);
- RARRAY_PTR_USE(ary, ptr, {
- MEMCPY(ptr+beg, argv, VALUE, argc);
- });
+ rb_gc_writebarrier_remember(buff_owner_ary);
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ MEMCPY(ptr+beg, argv, VALUE, argc);
+ });
}
else {
- int i;
- RARRAY_PTR_USE(ary, ptr, {
- for (i=0; i<argc; i++) {
- RB_OBJ_WRITE(buff_owner_ary, &ptr[i+beg], argv[i]);
- }
- });
+ int i;
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ for (i=0; i<argc; i++) {
+ RB_OBJ_WRITE(buff_owner_ary, &ptr[i+beg], argv[i]);
+ }
+ });
}
-#else
- /* giveup write barrier (traditional way) */
- RARRAY_PTR(buff_owner_ary);
- MEMCPY(RARRAY_PTR(ary)+beg, argv, VALUE, argc);
-#endif
}
static void
@@ -197,49 +272,175 @@ ary_memcpy(VALUE ary, long beg, long argc, const VALUE *argv)
ary_memcpy0(ary, beg, argc, argv, ary);
}
+static VALUE *
+ary_heap_alloc(VALUE ary, size_t capa)
+{
+ VALUE *ptr = rb_transient_heap_alloc(ary, sizeof(VALUE) * capa);
+
+ if (ptr != NULL) {
+ RARY_TRANSIENT_SET(ary);
+ }
+ else {
+ RARY_TRANSIENT_UNSET(ary);
+ ptr = ALLOC_N(VALUE, capa);
+ }
+
+ return ptr;
+}
+
+static void
+ary_heap_free_ptr(VALUE ary, const VALUE *ptr, long size)
+{
+ if (RARRAY_TRANSIENT_P(ary)) {
+ /* ignore it */
+ }
+ else {
+ ruby_sized_xfree((void *)ptr, size);
+ }
+}
+
+static void
+ary_heap_free(VALUE ary)
+{
+ if (RARRAY_TRANSIENT_P(ary)) {
+ RARY_TRANSIENT_UNSET(ary);
+ }
+ else {
+ ary_heap_free_ptr(ary, ARY_HEAP_PTR(ary), ARY_HEAP_SIZE(ary));
+ }
+}
+
+static void
+ary_heap_realloc(VALUE ary, size_t new_capa)
+{
+ size_t old_capa = ARY_HEAP_CAPA(ary);
+
+ if (RARRAY_TRANSIENT_P(ary)) {
+ if (new_capa <= old_capa) {
+ /* do nothing */
+ }
+ else {
+ VALUE *new_ptr = rb_transient_heap_alloc(ary, sizeof(VALUE) * new_capa);
+
+ if (new_ptr == NULL) {
+ new_ptr = ALLOC_N(VALUE, new_capa);
+ RARY_TRANSIENT_UNSET(ary);
+ }
+
+ MEMCPY(new_ptr, ARY_HEAP_PTR(ary), VALUE, old_capa);
+ ARY_SET_PTR(ary, new_ptr);
+ }
+ }
+ else {
+ SIZED_REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, new_capa, old_capa);
+ }
+ ary_verify(ary);
+}
+
+#if USE_TRANSIENT_HEAP
+static inline void
+rb_ary_transient_heap_evacuate_(VALUE ary, int transient, int promote)
+{
+ if (transient) {
+ 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));
+ assert(!ARY_PTR_USING_P(ary));
+
+ if (promote) {
+ new_ptr = ALLOC_N(VALUE, capa);
+ RARY_TRANSIENT_UNSET(ary);
+ }
+ else {
+ new_ptr = ary_heap_alloc(ary, capa);
+ }
+
+ MEMCPY(new_ptr, old_ptr, VALUE, capa);
+ /* do not use ARY_SET_PTR() because they assert !frozen */
+ RARRAY(ary)->as.heap.ptr = new_ptr;
+ }
+
+ ary_verify(ary);
+}
+
+void
+rb_ary_transient_heap_evacuate(VALUE ary, int promote)
+{
+ rb_ary_transient_heap_evacuate_(ary, RARRAY_TRANSIENT_P(ary), promote);
+}
+
+void
+rb_ary_detransient(VALUE ary)
+{
+ assert(RARRAY_TRANSIENT_P(ary));
+ rb_ary_transient_heap_evacuate_(ary, TRUE, TRUE);
+}
+#else
+void
+rb_ary_detransient(VALUE ary)
+{
+ /* do nothing */
+}
+#endif
+
static void
ary_resize_capa(VALUE ary, long capacity)
{
assert(RARRAY_LEN(ary) <= capacity);
assert(!OBJ_FROZEN(ary));
assert(!ARY_SHARED_P(ary));
+
if (capacity > RARRAY_EMBED_LEN_MAX) {
if (ARY_EMBED_P(ary)) {
long len = ARY_EMBED_LEN(ary);
- VALUE *ptr = ALLOC_N(VALUE, (capacity));
+ VALUE *ptr = ary_heap_alloc(ary, capacity);
+
MEMCPY(ptr, ARY_EMBED_PTR(ary), VALUE, len);
FL_UNSET_EMBED(ary);
ARY_SET_PTR(ary, ptr);
ARY_SET_HEAP_LEN(ary, len);
}
else {
- SIZED_REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, capacity, RARRAY(ary)->as.heap.aux.capa);
+ ary_heap_realloc(ary, capacity);
}
- ARY_SET_CAPA(ary, (capacity));
+ ARY_SET_CAPA(ary, capacity);
}
else {
if (!ARY_EMBED_P(ary)) {
- long len = RARRAY_LEN(ary);
- const VALUE *ptr = RARRAY_CONST_PTR(ary);
+ long len = ARY_HEAP_LEN(ary);
+ long old_capa = ARY_HEAP_CAPA(ary);
+ const VALUE *ptr = ARY_HEAP_PTR(ary);
- if (len > capacity) len = capacity;
+ if (len > capacity) len = capacity;
MEMCPY((VALUE *)RARRAY(ary)->as.ary, ptr, VALUE, len);
+ ary_heap_free_ptr(ary, ptr, old_capa);
+
FL_SET_EMBED(ary);
ARY_SET_LEN(ary, len);
- ruby_xfree((VALUE *)ptr);
}
}
+
+ ary_verify(ary);
}
static inline void
ary_shrink_capa(VALUE ary)
{
long capacity = ARY_HEAP_LEN(ary);
- long old_capa = RARRAY(ary)->as.heap.aux.capa;
+ long old_capa = ARY_HEAP_CAPA(ary);
assert(!ARY_SHARED_P(ary));
assert(old_capa >= capacity);
- if (old_capa > capacity)
- REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, capacity);
+ if (old_capa > capacity) ary_heap_realloc(ary, capacity);
+
+ ary_verify(ary);
}
static void
@@ -255,6 +456,8 @@ ary_double_capa(VALUE ary, long min)
}
new_capa += min;
ary_resize_capa(ary, new_capa);
+
+ ary_verify(ary);
}
static void
@@ -310,6 +513,7 @@ static inline void
rb_ary_modify_check(VALUE ary)
{
rb_check_frozen(ary);
+ ary_verify(ary);
}
void
@@ -319,6 +523,9 @@ rb_ary_modify(VALUE ary)
if (ARY_SHARED_P(ary)) {
long shared_len, len = RARRAY_LEN(ary);
VALUE shared = ARY_SHARED(ary);
+
+ ary_verify(shared);
+
if (len <= RARRAY_EMBED_LEN_MAX) {
const VALUE *ptr = ARY_HEAP_PTR(ary);
FL_UNSET_SHARED(ary);
@@ -328,19 +535,19 @@ rb_ary_modify(VALUE ary)
ARY_SET_EMBED_LEN(ary, len);
}
else if (ARY_SHARED_OCCUPIED(shared) && len > ((shared_len = RARRAY_LEN(shared))>>1)) {
- long shift = RARRAY_CONST_PTR(ary) - RARRAY_CONST_PTR(shared);
+ long shift = RARRAY_CONST_PTR_TRANSIENT(ary) - RARRAY_CONST_PTR_TRANSIENT(shared);
FL_UNSET_SHARED(ary);
- ARY_SET_PTR(ary, RARRAY_CONST_PTR(shared));
+ ARY_SET_PTR(ary, RARRAY_CONST_PTR_TRANSIENT(shared));
ARY_SET_CAPA(ary, shared_len);
- RARRAY_PTR_USE(ary, ptr, {
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
MEMMOVE(ptr, ptr+shift, VALUE, len);
});
FL_SET_EMBED(shared);
rb_ary_decrement_share(shared);
}
else {
- VALUE *ptr = ALLOC_N(VALUE, len);
- MEMCPY(ptr, RARRAY_CONST_PTR(ary), VALUE, len);
+ VALUE *ptr = ary_heap_alloc(ary, len);
+ MEMCPY(ptr, ARY_HEAP_PTR(ary), VALUE, len);
rb_ary_unshare(ary);
ARY_SET_CAPA(ary, len);
ARY_SET_PTR(ary, ptr);
@@ -348,6 +555,7 @@ rb_ary_modify(VALUE ary)
rb_gc_writebarrier_remember(ary);
}
+ ary_verify(ary);
}
static VALUE
@@ -364,9 +572,12 @@ ary_ensure_room_for_push(VALUE ary, long add_len)
if (new_len > RARRAY_EMBED_LEN_MAX) {
VALUE shared = ARY_SHARED(ary);
if (ARY_SHARED_OCCUPIED(shared)) {
- if (RARRAY_CONST_PTR(ary) - RARRAY_CONST_PTR(shared) + new_len <= RARRAY_LEN(shared)) {
+ if (ARY_HEAP_PTR(ary) - RARRAY_CONST_PTR_TRANSIENT(shared) + new_len <= RARRAY_LEN(shared)) {
rb_ary_modify_check(ary);
- return shared;
+
+ ary_verify(ary);
+ ary_verify(shared);
+ return shared;
}
else {
/* if array is shared, then it is likely it participate in push/shift pattern */
@@ -375,17 +586,23 @@ ary_ensure_room_for_push(VALUE ary, long add_len)
if (new_len > capa - (capa >> 6)) {
ary_double_capa(ary, new_len);
}
+ ary_verify(ary);
return ary;
}
}
}
+ ary_verify(ary);
+ rb_ary_modify(ary);
+ }
+ else {
+ rb_ary_modify_check(ary);
}
- rb_ary_modify(ary);
capa = ARY_CAPA(ary);
if (new_len > capa) {
ary_double_capa(ary, new_len);
}
+ ary_verify(ary);
return ary;
}
@@ -405,21 +622,6 @@ rb_ary_freeze(VALUE ary)
return rb_obj_freeze(ary);
}
-/*
- * call-seq:
- * ary.frozen? -> true or false
- *
- * Return +true+ if this array is frozen (or temporarily frozen
- * while being sorted). See also Object#frozen?
- */
-
-static VALUE
-rb_ary_frozen_p(VALUE ary)
-{
- if (OBJ_FROZEN(ary)) return Qtrue;
- return Qfalse;
-}
-
/* This can be used to take a snapshot of an array (with
e.g. rb_ary_replace) and check later whether the array has been
modified from the snapshot. The snapshot is cheap, though if
@@ -473,7 +675,7 @@ ary_new(VALUE klass, long capa)
ary = ary_alloc(klass);
if (capa > RARRAY_EMBED_LEN_MAX) {
- ptr = ALLOC_N(VALUE, capa);
+ ptr = ary_heap_alloc(ary, capa);
FL_UNSET_EMBED(ary);
ARY_SET_PTR(ary, ptr);
ARY_SET_CAPA(ary, capa);
@@ -514,12 +716,12 @@ VALUE
return ary;
}
-VALUE
-rb_ary_new_from_values(long n, const VALUE *elts)
+MJIT_FUNC_EXPORTED VALUE
+rb_ary_tmp_new_from_values(VALUE klass, long n, const VALUE *elts)
{
VALUE ary;
- ary = rb_ary_new2(n);
+ ary = ary_new(klass, n);
if (n > 0 && elts) {
ary_memcpy(ary, 0, n, elts);
ARY_SET_LEN(ary, n);
@@ -529,9 +731,17 @@ rb_ary_new_from_values(long n, const VALUE *elts)
}
VALUE
+rb_ary_new_from_values(long n, const VALUE *elts)
+{
+ return rb_ary_tmp_new_from_values(rb_cArray, n, elts);
+}
+
+VALUE
rb_ary_tmp_new(long capa)
{
- return ary_new(0, capa);
+ VALUE ary = ary_new(0, capa);
+ rb_ary_transient_heap_evacuate(ary, TRUE);
+ return ary;
}
VALUE
@@ -540,6 +750,7 @@ rb_ary_tmp_new_fill(long capa)
VALUE ary = ary_new(0, capa);
ary_memfill(ary, 0, capa, Qnil);
ARY_SET_LEN(ary, capa);
+ rb_ary_transient_heap_evacuate(ary, TRUE);
return ary;
}
@@ -547,7 +758,16 @@ void
rb_ary_free(VALUE ary)
{
if (ARY_OWNS_HEAP_P(ary)) {
- ruby_sized_xfree((void *)ARY_HEAP_PTR(ary), ARY_HEAP_SIZE(ary));
+ if (RARRAY_TRANSIENT_P(ary)) {
+ RB_DEBUG_COUNTER_INC(obj_ary_transient);
+ }
+ else {
+ RB_DEBUG_COUNTER_INC(obj_ary_ptr);
+ ary_heap_free(ary);
+ }
+ }
+ else {
+ RB_DEBUG_COUNTER_INC(obj_ary_embed);
}
}
@@ -555,7 +775,7 @@ RUBY_FUNC_EXPORTED size_t
rb_ary_memsize(VALUE ary)
{
if (ARY_OWNS_HEAP_P(ary)) {
- return RARRAY(ary)->as.heap.aux.capa * sizeof(VALUE);
+ return ARY_CAPA(ary) * sizeof(VALUE);
}
else {
return 0;
@@ -567,13 +787,15 @@ ary_discard(VALUE ary)
{
rb_ary_free(ary);
RBASIC(ary)->flags |= RARRAY_EMBED_FLAG;
- RBASIC(ary)->flags &= ~RARRAY_EMBED_LEN_MASK;
+ RBASIC(ary)->flags &= ~(RARRAY_EMBED_LEN_MASK | RARRAY_TRANSIENT_FLAG);
}
static VALUE
ary_make_shared(VALUE ary)
{
assert(!ARY_EMBED_P(ary));
+ ary_verify(ary);
+
if (ARY_SHARED_P(ary)) {
return ARY_SHARED(ary);
}
@@ -581,6 +803,7 @@ ary_make_shared(VALUE 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_NUM(ary, 1);
@@ -588,18 +811,25 @@ ary_make_shared(VALUE ary)
}
else {
long capa = ARY_CAPA(ary), len = RARRAY_LEN(ary);
+ const VALUE *ptr;
NEWOBJ_OF(shared, struct RArray, 0, T_ARRAY | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0));
- FL_UNSET_EMBED(shared);
+ rb_ary_transient_heap_evacuate(ary, TRUE);
+ ptr = ARY_HEAP_PTR(ary);
+
+ FL_UNSET_EMBED(shared);
ARY_SET_LEN((VALUE)shared, capa);
- ARY_SET_PTR((VALUE)shared, RARRAY_CONST_PTR(ary));
- ary_mem_clear((VALUE)shared, len, capa - len);
+ ARY_SET_PTR((VALUE)shared, ptr);
+ ary_mem_clear((VALUE)shared, len, capa - len);
FL_SET_SHARED_ROOT(shared);
ARY_SET_SHARED_NUM((VALUE)shared, 1);
FL_SET_SHARED(ary);
ARY_SET_SHARED(ary, (VALUE)shared);
OBJ_FREEZE(shared);
- return (VALUE)shared;
+
+ ary_verify((VALUE)shared);
+ ary_verify(ary);
+ return (VALUE)shared;
}
}
@@ -610,7 +840,7 @@ ary_make_substitution(VALUE ary)
if (len <= RARRAY_EMBED_LEN_MAX) {
VALUE subst = rb_ary_new2(len);
- ary_memcpy(subst, 0, len, RARRAY_CONST_PTR(ary));
+ ary_memcpy(subst, 0, len, RARRAY_CONST_PTR_TRANSIENT(ary));
ARY_SET_EMBED_LEN(subst, len);
return subst;
}
@@ -625,16 +855,23 @@ rb_assoc_new(VALUE car, VALUE cdr)
return rb_ary_new3(2, car, cdr);
}
-static VALUE
-to_ary(VALUE ary)
+VALUE
+rb_to_array_type(VALUE ary)
{
- return rb_convert_type(ary, T_ARRAY, "Array", "to_ary");
+ return rb_convert_type_with_id(ary, T_ARRAY, "Array", idTo_ary);
}
+#define to_ary rb_to_array_type
VALUE
rb_check_array_type(VALUE ary)
{
- return rb_check_convert_type(ary, T_ARRAY, "Array", "to_ary");
+ return rb_check_convert_type_with_id(ary, T_ARRAY, "Array", idTo_ary);
+}
+
+MJIT_FUNC_EXPORTED VALUE
+rb_check_to_array(VALUE ary)
+{
+ return rb_check_convert_type_with_id(ary, T_ARRAY, "Array", idTo_a);
}
/*
@@ -688,7 +925,7 @@ rb_ary_s_try_convert(VALUE dummy, VALUE ary)
* this array is created by passing the element's index to the given block
* and storing the return value.
*
- * Array.new(3){ |index| index ** 2 }
+ * Array.new(3) {|index| index ** 2}
* # => [0, 1, 4]
*
* == Common gotchas
@@ -712,7 +949,7 @@ rb_ary_s_try_convert(VALUE dummy, VALUE ary)
* version which uses the result of that block each time an element
* of the array needs to be initialized:
*
- * a = Array.new(2) { Hash.new }
+ * a = Array.new(2) {Hash.new}
* a[0]['cat'] = 'feline'
* a # => [{"cat"=>"feline"}, {}]
*
@@ -726,8 +963,8 @@ rb_ary_initialize(int argc, VALUE *argv, VALUE ary)
rb_ary_modify(ary);
if (argc == 0) {
- if (ARY_OWNS_HEAP_P(ary) && RARRAY_CONST_PTR(ary) != 0) {
- ruby_sized_xfree((void *)RARRAY_CONST_PTR(ary), ARY_HEAP_SIZE(ary));
+ if (ARY_OWNS_HEAP_P(ary) && ARY_HEAP_PTR(ary) != NULL) {
+ ary_heap_free(ary);
}
rb_ary_unshare_safe(ary);
FL_SET_EMBED(ary);
@@ -778,7 +1015,7 @@ rb_ary_initialize(int argc, VALUE *argv, VALUE ary)
/*
* Returns a new array populated with the given objects.
*
- * Array.[]( 1, 'a', /^A/ ) # => [1, "a", /^A/]
+ * Array.[]( 1, 'a', /^A/) # => [1, "a", /^A/]
* Array[ 1, 'a', /^A/ ] # => [1, "a", /^A/]
* [ 1, 'a', /^A/ ] # => [1, "a", /^A/]
*/
@@ -834,7 +1071,7 @@ ary_make_partial(VALUE ary, VALUE klass, long offset, long len)
if (len <= RARRAY_EMBED_LEN_MAX) {
VALUE result = ary_alloc(klass);
- ary_memcpy(result, 0, len, RARRAY_CONST_PTR(ary) + offset);
+ ary_memcpy(result, 0, len, RARRAY_CONST_PTR_TRANSIENT(ary) + offset);
ARY_SET_EMBED_LEN(result, len);
return result;
}
@@ -843,12 +1080,15 @@ ary_make_partial(VALUE ary, VALUE klass, long offset, long len)
FL_UNSET_EMBED(result);
shared = ary_make_shared(ary);
- ARY_SET_PTR(result, RARRAY_CONST_PTR(ary));
+ ARY_SET_PTR(result, RARRAY_CONST_PTR_TRANSIENT(ary));
ARY_SET_LEN(result, RARRAY_LEN(ary));
rb_ary_set_shared(result, shared);
ARY_INCREASE_PTR(result, offset);
ARY_SET_LEN(result, len);
+
+ ary_verify(shared);
+ ary_verify(result);
return result;
}
}
@@ -868,13 +1108,17 @@ enum ary_take_pos_flags
static VALUE
ary_take_first_or_last(int argc, const VALUE *argv, VALUE ary, enum ary_take_pos_flags last)
{
- VALUE nv;
long n;
long len;
long offset = 0;
- rb_scan_args(argc, argv, "1", &nv);
- n = NUM2LONG(nv);
+ argc = rb_check_arity(argc, 0, 1);
+ /* the case optional argument is ommited should be handled in
+ * callers of this function. if another arity case is added,
+ * this arity check needs to rewrite. */
+ RUBY_ASSERT_WHEN(TRUE, argc == 1);
+
+ n = NUM2LONG(argv[0]);
len = RARRAY_LEN(ary);
if (n > len) {
n = len;
@@ -896,7 +1140,10 @@ ary_take_first_or_last(int argc, const VALUE *argv, VALUE ary, enum ary_take_pos
* expression returns the array itself, so several appends
* may be chained together.
*
- * [ 1, 2 ] << "c" << "d" << [ 3, 4 ]
+ * a = [ 1, 2 ]
+ * a << "c" << "d" << [ 3, 4 ]
+ * #=> [ 1, 2, "c", "d", [ 3, 4 ] ]
+ * a
* #=> [ 1, 2, "c", "d", [ 3, 4 ] ]
*
*/
@@ -904,12 +1151,13 @@ ary_take_first_or_last(int argc, const VALUE *argv, VALUE ary, enum ary_take_pos
VALUE
rb_ary_push(VALUE ary, VALUE item)
{
- long idx = RARRAY_LEN(ary);
+ long idx = RARRAY_LEN((ary_verify(ary), ary));
VALUE target_ary = ary_ensure_room_for_push(ary, 1);
- RARRAY_PTR_USE(ary, ptr, {
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
RB_OBJ_WRITE(target_ary, &ptr[idx], item);
});
ARY_SET_LEN(ary, idx + 1);
+ ary_verify(ary);
return ary;
}
@@ -925,7 +1173,8 @@ rb_ary_cat(VALUE ary, const VALUE *argv, long len)
/*
* call-seq:
- * ary.push(obj, ... ) -> ary
+ * ary.push(obj, ...) -> ary
+ * ary.append(obj, ...) -> ary
*
* Append --- Pushes the given object(s) on to the end of this array. This
* expression returns the array itself, so several appends
@@ -960,6 +1209,7 @@ rb_ary_pop(VALUE ary)
}
--n;
ARY_SET_LEN(ary, n);
+ ary_verify(ary);
return RARRAY_AREF(ary, n);
}
@@ -993,6 +1243,7 @@ rb_ary_pop_m(int argc, VALUE *argv, VALUE ary)
rb_ary_modify_check(ary);
result = ary_take_first_or_last(argc, argv, ary, ARY_TAKE_LAST);
ARY_INCREASE_LEN(ary, -RARRAY_LEN(result));
+ ary_verify(ary);
return result;
}
@@ -1007,10 +1258,11 @@ rb_ary_shift(VALUE ary)
top = RARRAY_AREF(ary, 0);
if (!ARY_SHARED_P(ary)) {
if (len < ARY_DEFAULT_SIZE) {
- RARRAY_PTR_USE(ary, ptr, {
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
MEMMOVE(ptr, ptr+1, VALUE, len-1);
}); /* WB: no new reference */
ARY_INCREASE_LEN(ary, -1);
+ ary_verify(ary);
return top;
}
assert(!ARY_EMBED_P(ary)); /* ARY_EMBED_LEN_MAX < ARY_DEFAULT_SIZE */
@@ -1019,11 +1271,13 @@ rb_ary_shift(VALUE ary)
ary_make_shared(ary);
}
else if (ARY_SHARED_OCCUPIED(ARY_SHARED(ary))) {
- RARRAY_PTR_USE(ary, ptr, ptr[0] = Qnil);
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, ptr[0] = Qnil);
}
ARY_INCREASE_PTR(ary, 1); /* shift ptr */
ARY_INCREASE_LEN(ary, -1);
+ ary_verify(ary);
+
return top;
}
@@ -1063,6 +1317,17 @@ rb_ary_shift_m(int argc, VALUE *argv, VALUE ary)
rb_ary_modify_check(ary);
result = ary_take_first_or_last(argc, argv, ary, ARY_TAKE_FIRST);
n = RARRAY_LEN(result);
+ rb_ary_behead(ary,n);
+
+ return result;
+}
+
+MJIT_FUNC_EXPORTED VALUE
+rb_ary_behead(VALUE ary, long n)
+{
+ if(n<=0) return ary;
+
+ rb_ary_modify_check(ary);
if (ARY_SHARED_P(ary)) {
if (ARY_SHARED_OCCUPIED(ARY_SHARED(ary))) {
setup_occupied_shared:
@@ -1072,8 +1337,8 @@ rb_ary_shift_m(int argc, VALUE *argv, VALUE ary)
}
else {
if (RARRAY_LEN(ary) < ARY_DEFAULT_SIZE) {
- RARRAY_PTR_USE(ary, ptr, {
- MEMMOVE(ptr, ptr+n, VALUE, RARRAY_LEN(ary)-n);
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ MEMMOVE(ptr, ptr+n, VALUE, RARRAY_LEN(ary)-n);
}); /* WB: no new reference */
}
else {
@@ -1083,7 +1348,8 @@ rb_ary_shift_m(int argc, VALUE *argv, VALUE ary)
}
ARY_INCREASE_LEN(ary, -n);
- return result;
+ ary_verify(ary);
+ return ary;
}
static VALUE
@@ -1102,8 +1368,9 @@ ary_ensure_room_for_unshift(VALUE ary, int argc)
VALUE shared = ARY_SHARED(ary);
capa = RARRAY_LEN(shared);
if (ARY_SHARED_OCCUPIED(shared) && capa > new_len) {
- head = RARRAY_CONST_PTR(ary);
- sharedp = RARRAY_CONST_PTR(shared);
+ rb_ary_modify_check(ary);
+ head = RARRAY_CONST_PTR_TRANSIENT(ary);
+ sharedp = RARRAY_CONST_PTR_TRANSIENT(shared);
goto makeroom_if_need;
}
}
@@ -1116,11 +1383,13 @@ ary_ensure_room_for_unshift(VALUE ary, int argc)
/* use shared array for big "queues" */
if (new_len > ARY_DEFAULT_SIZE * 4) {
- /* make a room for unshifted items */
+ ary_verify(ary);
+
+ /* make a room for unshifted items */
capa = ARY_CAPA(ary);
ary_make_shared(ary);
- head = sharedp = RARRAY_CONST_PTR(ary);
+ head = sharedp = RARRAY_CONST_PTR_TRANSIENT(ary);
goto makeroom;
makeroom_if_need:
if (head - sharedp < argc) {
@@ -1133,14 +1402,17 @@ ary_ensure_room_for_unshift(VALUE ary, int argc)
}
ARY_SET_PTR(ary, head - argc);
assert(ARY_SHARED_OCCUPIED(ARY_SHARED(ary)));
+
+ ary_verify(ary);
return ARY_SHARED(ary);
}
else {
/* sliding items */
- RARRAY_PTR_USE(ary, ptr, {
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
MEMMOVE(ptr + argc, ptr, VALUE, len);
});
+ ary_verify(ary);
return ary;
}
}
@@ -1148,6 +1420,7 @@ ary_ensure_room_for_unshift(VALUE ary, int argc)
/*
* call-seq:
* ary.unshift(obj, ...) -> ary
+ * ary.prepend(obj, ...) -> ary
*
* Prepends objects to the front of +self+, moving other elements upwards.
* See also Array#shift for the opposite effect.
@@ -1195,17 +1468,7 @@ rb_ary_elt(VALUE ary, long offset)
VALUE
rb_ary_entry(VALUE ary, long offset)
{
- long len = RARRAY_LEN(ary);
- const VALUE *ptr = RARRAY_CONST_PTR(ary);
- if (len == 0) return Qnil;
- if (offset < 0) {
- offset += len;
- if (offset < 0) return Qnil;
- }
- else if (len <= offset) {
- return Qnil;
- }
- return ptr[offset];
+ return rb_ary_entry_internal(ary, offset);
}
VALUE
@@ -1265,21 +1528,29 @@ rb_ary_subseq(VALUE ary, long beg, long len)
VALUE
rb_ary_aref(int argc, const VALUE *argv, VALUE ary)
{
- VALUE arg;
- long beg, len;
-
+ rb_check_arity(argc, 1, 2);
if (argc == 2) {
- beg = NUM2LONG(argv[0]);
- len = NUM2LONG(argv[1]);
- if (beg < 0) {
- beg += RARRAY_LEN(ary);
- }
- return rb_ary_subseq(ary, beg, len);
+ return rb_ary_aref2(ary, argv[0], argv[1]);
}
- if (argc != 1) {
- rb_scan_args(argc, argv, "11", NULL, NULL);
+ return rb_ary_aref1(ary, argv[0]);
+}
+
+VALUE
+rb_ary_aref2(VALUE ary, VALUE b, VALUE e)
+{
+ long beg = NUM2LONG(b);
+ long len = NUM2LONG(e);
+ if (beg < 0) {
+ beg += RARRAY_LEN(ary);
}
- arg = argv[0];
+ return rb_ary_subseq(ary, beg, len);
+}
+
+MJIT_FUNC_EXPORTED VALUE
+rb_ary_aref1(VALUE ary, VALUE arg)
+{
+ long beg, len;
+
/* special case - speeding up */
if (FIXNUM_P(arg)) {
return rb_ary_entry(ary, FIX2LONG(arg));
@@ -1374,7 +1645,7 @@ rb_ary_last(int argc, const VALUE *argv, VALUE ary)
* call-seq:
* ary.fetch(index) -> obj
* ary.fetch(index, default) -> obj
- * ary.fetch(index) { |index| block } -> obj
+ * ary.fetch(index) {|index| block} -> obj
*
* Tries to return the element at position +index+, but throws an IndexError
* exception if the referenced +index+ lies outside of the array bounds. This
@@ -1390,7 +1661,7 @@ rb_ary_last(int argc, const VALUE *argv, VALUE ary)
* a.fetch(1) #=> 22
* a.fetch(-1) #=> 44
* a.fetch(4, 'cat') #=> "cat"
- * a.fetch(100) { |i| puts "#{i} is out of bounds" }
+ * a.fetch(100) {|i| puts "#{i} is out of bounds"}
* #=> "100 is out of bounds"
*/
@@ -1425,10 +1696,10 @@ rb_ary_fetch(int argc, VALUE *argv, VALUE ary)
/*
* call-seq:
* ary.find_index(obj) -> int or nil
- * ary.find_index { |item| block } -> int or nil
+ * ary.find_index {|item| block} -> int or nil
* ary.find_index -> Enumerator
* ary.index(obj) -> int or nil
- * ary.index { |item| block } -> int or nil
+ * ary.index {|item| block} -> int or nil
* ary.index -> Enumerator
*
* Returns the _index_ of the first object in +ary+ such that the object is
@@ -1445,15 +1716,14 @@ rb_ary_fetch(int argc, VALUE *argv, VALUE ary)
* a = [ "a", "b", "c" ]
* a.index("b") #=> 1
* a.index("z") #=> nil
- * a.index { |x| x == "b" } #=> 1
+ * a.index {|x| x == "b"} #=> 1
*/
static VALUE
rb_ary_index(int argc, VALUE *argv, VALUE ary)
{
- const VALUE *ptr;
VALUE val;
- long i, len;
+ long i;
if (argc == 0) {
RETURN_ENUMERATOR(ary, 0, 0);
@@ -1468,20 +1738,11 @@ rb_ary_index(int argc, VALUE *argv, VALUE ary)
val = argv[0];
if (rb_block_given_p())
rb_warn("given block not used");
- len = RARRAY_LEN(ary);
- ptr = RARRAY_CONST_PTR(ary);
- for (i=0; i<len; i++) {
- VALUE e = ptr[i];
- switch (rb_equal_opt(e, val)) {
- case Qundef:
- if (!rb_equal(e, val)) break;
- case Qtrue:
+ for (i=0; i<RARRAY_LEN(ary); i++) {
+ VALUE e = RARRAY_AREF(ary, i);
+ if (rb_equal(e, val)) {
return LONG2NUM(i);
- case Qfalse:
- continue;
}
- len = RARRAY_LEN(ary);
- ptr = RARRAY_CONST_PTR(ary);
}
return Qnil;
}
@@ -1489,7 +1750,7 @@ rb_ary_index(int argc, VALUE *argv, VALUE ary)
/*
* call-seq:
* ary.rindex(obj) -> int or nil
- * ary.rindex { |item| block } -> int or nil
+ * ary.rindex {|item| block} -> int or nil
* ary.rindex -> Enumerator
*
* Returns the _index_ of the last object in +self+ <code>==</code> to +obj+.
@@ -1507,13 +1768,12 @@ rb_ary_index(int argc, VALUE *argv, VALUE ary)
* a = [ "a", "b", "b", "b", "c" ]
* a.rindex("b") #=> 3
* a.rindex("z") #=> nil
- * a.rindex { |x| x == "b" } #=> 3
+ * a.rindex {|x| x == "b"} #=> 3
*/
static VALUE
rb_ary_rindex(int argc, VALUE *argv, VALUE ary)
{
- const VALUE *ptr;
VALUE val;
long i = RARRAY_LEN(ary), len;
@@ -1532,21 +1792,11 @@ rb_ary_rindex(int argc, VALUE *argv, VALUE ary)
val = argv[0];
if (rb_block_given_p())
rb_warn("given block not used");
- ptr = RARRAY_CONST_PTR(ary);
while (i--) {
- VALUE e = ptr[i];
- switch (rb_equal_opt(e, val)) {
- case Qundef:
- if (!rb_equal(e, val)) break;
- case Qtrue:
+ VALUE e = RARRAY_AREF(ary, i);
+ if (rb_equal(e, val)) {
return LONG2NUM(i);
- case Qfalse:
- continue;
}
- if (i > (len = RARRAY_LEN(ary))) {
- i = len;
- }
- ptr = RARRAY_CONST_PTR(ary);
}
return Qnil;
}
@@ -1580,7 +1830,7 @@ rb_ary_splice(VALUE ary, long beg, long len, const VALUE *rptr, long rlen)
}
{
- const VALUE *optr = RARRAY_CONST_PTR(ary);
+ const VALUE *optr = RARRAY_CONST_PTR_TRANSIENT(ary);
rofs = (rptr >= optr && rptr < optr + olen) ? rptr - optr : -1;
}
@@ -1593,7 +1843,7 @@ rb_ary_splice(VALUE ary, long beg, long len, const VALUE *rptr, long rlen)
len = beg + rlen;
ary_mem_clear(ary, olen, beg - olen);
if (rlen > 0) {
- if (rofs != -1) rptr = RARRAY_CONST_PTR(ary) + rofs;
+ if (rofs != -1) rptr = RARRAY_CONST_PTR_TRANSIENT(ary) + rofs;
ary_memcpy0(ary, beg, rlen, rptr, target_ary);
}
ARY_SET_LEN(ary, len);
@@ -1611,14 +1861,21 @@ rb_ary_splice(VALUE ary, long beg, long len, const VALUE *rptr, long rlen)
}
if (len != rlen) {
- RARRAY_PTR_USE(ary, ptr,
- MEMMOVE(ptr + beg + rlen, ptr + beg + len,
- VALUE, olen - (beg + len)));
+ 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) {
- if (rofs != -1) rptr = RARRAY_CONST_PTR(ary) + rofs;
- MEMMOVE(RARRAY_PTR(ary) + beg, rptr, VALUE, rlen);
+ if (rofs != -1) rptr = RARRAY_CONST_PTR_TRANSIENT(ary) + rofs;
+ /* give up wb-protected ary */
+ RB_OBJ_WB_UNPROTECT_FOR(ARRAY, ary);
+
+ /* do not use RARRAY_PTR() because it can causes GC.
+ * ary can contain T_NONE object because it is not cleared.
+ */
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr,
+ MEMMOVE(ptr + beg, rptr, VALUE, rlen));
}
}
}
@@ -1676,11 +1933,12 @@ rb_ary_resize(VALUE ary, long len)
}
else {
if (olen > len + ARY_DEFAULT_SIZE) {
- SIZED_REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, len, RARRAY(ary)->as.heap.aux.capa);
+ ary_heap_realloc(ary, len);
ARY_SET_CAPA(ary, len);
}
ARY_SET_HEAP_LEN(ary, len);
}
+ ary_verify(ary);
return ary;
}
@@ -1741,7 +1999,7 @@ rb_ary_aset(int argc, VALUE *argv, VALUE ary)
/* check if idx is Range */
range:
rpl = rb_ary_to_ary(argv[argc-1]);
- rb_ary_splice(ary, beg, len, RARRAY_CONST_PTR(rpl), RARRAY_LEN(rpl));
+ rb_ary_splice(ary, beg, len, RARRAY_CONST_PTR_TRANSIENT(rpl), RARRAY_LEN(rpl));
RB_GC_GUARD(rpl);
return argv[argc-1];
}
@@ -1775,12 +2033,17 @@ rb_ary_insert(int argc, VALUE *argv, VALUE ary)
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
rb_ary_modify_check(ary);
- if (argc == 1) return ary;
pos = NUM2LONG(argv[0]);
+ if (argc == 1) return ary;
if (pos == -1) {
pos = RARRAY_LEN(ary);
}
- if (pos < 0) {
+ 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++;
}
rb_ary_splice(ary, pos, 0, argv + 1, argc - 1);
@@ -1798,7 +2061,7 @@ ary_enum_length(VALUE ary, VALUE args, VALUE eobj)
/*
* call-seq:
- * ary.each { |item| block } -> ary
+ * ary.each {|item| block} -> ary
* ary.each -> Enumerator
*
* Calls the given block once for each element in +self+, passing that element
@@ -1818,7 +2081,7 @@ VALUE
rb_ary_each(VALUE ary)
{
long i;
-
+ 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));
@@ -1828,7 +2091,7 @@ rb_ary_each(VALUE ary)
/*
* call-seq:
- * ary.each_index { |index| block } -> ary
+ * ary.each_index {|index| block} -> ary
* ary.each_index -> Enumerator
*
* Same as Array#each, but passes the +index+ of the element instead of the
@@ -1858,7 +2121,7 @@ rb_ary_each_index(VALUE ary)
/*
* call-seq:
- * ary.reverse_each { |item| block } -> ary
+ * ary.reverse_each {|item| block} -> ary
* ary.reverse_each -> Enumerator
*
* Same as Array#each, but traverses +self+ in reverse order.
@@ -1928,15 +2191,18 @@ rb_ary_dup(VALUE ary)
{
long len = RARRAY_LEN(ary);
VALUE dup = rb_ary_new2(len);
- ary_memcpy(dup, 0, len, RARRAY_CONST_PTR(ary));
+ ary_memcpy(dup, 0, len, RARRAY_CONST_PTR_TRANSIENT(ary));
ARY_SET_LEN(dup, len);
+
+ ary_verify(ary);
+ ary_verify(dup);
return dup;
}
VALUE
rb_ary_resurrect(VALUE ary)
{
- return rb_ary_new4(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary));
+ return ary_make_partial(ary, rb_cArray, 0, RARRAY_LEN(ary));
}
extern VALUE rb_output_fs;
@@ -1990,7 +2256,10 @@ ary_join_1(VALUE obj, VALUE ary, VALUE sep, long i, VALUE result, int *first)
if (RB_TYPE_P(val, T_STRING)) {
str_join:
rb_str_buf_append(result, val);
- *first = FALSE;
+ if (*first) {
+ rb_enc_copy(result, val);
+ *first = FALSE;
+ }
}
else if (RB_TYPE_P(val, T_ARRAY)) {
obj = val;
@@ -2001,6 +2270,7 @@ ary_join_1(VALUE obj, VALUE ary, VALUE sep, long i, VALUE result, int *first)
else {
VALUE args[4];
+ *first = FALSE;
args[0] = val;
args[1] = sep;
args[2] = result;
@@ -2014,17 +2284,13 @@ ary_join_1(VALUE obj, VALUE ary, VALUE sep, long i, VALUE result, int *first)
val = tmp;
goto str_join;
}
- tmp = rb_check_convert_type(val, T_ARRAY, "Array", "to_ary");
+ tmp = rb_check_array_type(val);
if (!NIL_P(tmp)) {
obj = val;
val = tmp;
goto ary_join;
}
val = rb_obj_as_string(val);
- if (*first) {
- rb_enc_copy(result, val);
- *first = FALSE;
- }
goto str_join;
}
}
@@ -2075,11 +2341,16 @@ rb_ary_join(VALUE ary, VALUE sep)
*
* Returns a string created by converting each element of the array to
* a string, separated by the given +separator+.
- * If the +separator+ is +nil+, it uses current $,.
- * If both the +separator+ and $, are nil, it uses empty string.
+ * If the +separator+ is +nil+, it uses current <code>$,</code>.
+ * If both the +separator+ and <code>$,</code> are +nil+,
+ * it uses an empty string.
*
* [ "a", "b", "c" ].join #=> "abc"
* [ "a", "b", "c" ].join("-") #=> "a-b-c"
+ *
+ * For nested arrays, join is applied recursively:
+ *
+ * [ "a", [1, 2, [:x, :y]], "b" ].join("-") #=> "a-1-2-x-y-b"
*/
static VALUE
@@ -2087,8 +2358,12 @@ rb_ary_join_m(int argc, VALUE *argv, VALUE ary)
{
VALUE sep;
- rb_scan_args(argc, argv, "01", &sep);
- if (NIL_P(sep)) sep = rb_output_fs;
+ if (rb_check_arity(argc, 0, 1) == 0) {
+ sep = rb_output_fs;
+ }
+ else if (NIL_P(sep = argv[0])) {
+ sep = rb_output_fs;
+ }
return rb_ary_join(ary, sep);
}
@@ -2159,22 +2434,32 @@ rb_ary_to_a(VALUE ary)
/*
* call-seq:
- * ary.to_h -> hash
+ * ary.to_h -> hash
+ * ary.to_h { block } -> hash
*
* Returns the result of interpreting <i>ary</i> as an array of
* <tt>[key, value]</tt> pairs.
*
* [[:foo, :bar], [1, 2]].to_h
* # => {:foo => :bar, 1 => 2}
+ *
+ * If a block is given, the results of the block on each element of
+ * the array will be used as pairs.
+ *
+ * ["foo", "bar"].to_h {|s| [s.ord, s]}
+ * # => {102=>"foo", 98=>"bar"}
*/
static VALUE
rb_ary_to_h(VALUE ary)
{
long i;
- VALUE hash = rb_hash_new();
+ VALUE hash = rb_hash_new_with_size(RARRAY_LEN(ary));
+ int block_given = rb_block_given_p();
+
for (i=0; i<RARRAY_LEN(ary); i++) {
- const VALUE elt = rb_ary_elt(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)",
@@ -2220,9 +2505,9 @@ rb_ary_reverse(VALUE ary)
rb_ary_modify(ary);
if (len > 1) {
- RARRAY_PTR_USE(ary, p1, {
- p2 = p1 + len - 1; /* points last item */
- ary_reverse(p1, p2);
+ RARRAY_PTR_USE_TRANSIENT(ary, p1, {
+ p2 = p1 + len - 1; /* points last item */
+ ary_reverse(p1, p2);
}); /* WB: no new reference */
}
return ary;
@@ -2262,8 +2547,8 @@ rb_ary_reverse_m(VALUE ary)
VALUE dup = rb_ary_new2(len);
if (len > 0) {
- const VALUE *p1 = RARRAY_CONST_PTR(ary);
- VALUE *p2 = (VALUE *)RARRAY_CONST_PTR(dup) + len - 1;
+ const VALUE *p1 = RARRAY_CONST_PTR_TRANSIENT(ary);
+ VALUE *p2 = (VALUE *)RARRAY_CONST_PTR_TRANSIENT(dup) + len - 1;
do *p2-- = *p1++; while (--len > 0);
}
ARY_SET_LEN(dup, RARRAY_LEN(ary));
@@ -2276,24 +2561,27 @@ rotate_count(long cnt, long len)
return (cnt < 0) ? (len - (~cnt % len) - 1) : (cnt % len);
}
+static void
+ary_rotate_ptr(VALUE *ptr, long len, long cnt)
+{
+ --len;
+ if (cnt < len) ary_reverse(ptr + cnt, ptr + len);
+ if (--cnt > 0) ary_reverse(ptr, ptr + cnt);
+ if (len > 0) ary_reverse(ptr, ptr + len);
+}
+
VALUE
rb_ary_rotate(VALUE ary, long cnt)
{
rb_ary_modify(ary);
if (cnt != 0) {
- VALUE *ptr = RARRAY_PTR(ary);
- long len = RARRAY_LEN(ary);
-
- if (len > 0 && (cnt = rotate_count(cnt, len)) > 0) {
- --len;
- if (cnt < len) ary_reverse(ptr + cnt, ptr + len);
- if (--cnt > 0) ary_reverse(ptr, ptr + cnt);
- if (len > 0) ary_reverse(ptr, ptr + len);
- return ary;
- }
+ long len = RARRAY_LEN(ary);
+ if (len > 0 && (cnt = rotate_count(cnt, len)) > 0) {
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, ary_rotate_ptr(ptr, len, cnt));
+ return ary;
+ }
}
-
return Qnil;
}
@@ -2317,13 +2605,7 @@ rb_ary_rotate(VALUE ary, long cnt)
static VALUE
rb_ary_rotate_bang(int argc, VALUE *argv, VALUE ary)
{
- long n = 1;
-
- switch (argc) {
- case 1: n = NUM2LONG(argv[0]);
- case 0: break;
- default: rb_scan_args(argc, argv, "01", NULL);
- }
+ long n = (rb_check_arity(argc, 0, 1) ? NUM2LONG(argv[0]) : 1);
rb_ary_rotate(ary, n);
return ary;
}
@@ -2350,19 +2632,14 @@ rb_ary_rotate_m(int argc, VALUE *argv, VALUE ary)
{
VALUE rotated;
const VALUE *ptr;
- long len, cnt = 1;
-
- switch (argc) {
- case 1: cnt = NUM2LONG(argv[0]);
- case 0: break;
- default: rb_scan_args(argc, argv, "01", NULL);
- }
+ long len;
+ long cnt = (rb_check_arity(argc, 0, 1) ? NUM2LONG(argv[0]) : 1);
len = RARRAY_LEN(ary);
rotated = rb_ary_new2(len);
if (len > 0) {
cnt = rotate_count(cnt, len);
- ptr = RARRAY_CONST_PTR(ary);
+ ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
len -= cnt;
ary_memcpy(rotated, 0, len, ptr + cnt);
ary_memcpy(rotated, len, cnt, ptr);
@@ -2391,9 +2668,12 @@ sort_1(const void *ap, const void *bp, void *dummy)
struct ary_sort_data *data = dummy;
VALUE retval = sort_reentered(data->ary);
VALUE a = *(const VALUE *)ap, b = *(const VALUE *)bp;
+ VALUE args[2];
int n;
- retval = rb_yield_values(2, a, b);
+ args[0] = a;
+ args[1] = b;
+ retval = rb_yield_values2(2, args);
n = rb_cmpint(retval, a, b);
sort_reentered(data->ary);
return n;
@@ -2415,6 +2695,9 @@ sort_2(const void *ap, const void *bp, void *dummy)
if (STRING_P(a) && STRING_P(b) && CMP_OPTIMIZABLE(data->cmp_opt, 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);
+ }
retval = rb_funcallv(a, id_cmp, 1, &b);
n = rb_cmpint(retval, a, b);
@@ -2426,7 +2709,7 @@ sort_2(const void *ap, const void *bp, void *dummy)
/*
* call-seq:
* ary.sort! -> ary
- * ary.sort! { |a, b| block } -> ary
+ * ary.sort! {|a, b| block} -> ary
*
* Sorts +self+ in place.
*
@@ -2437,14 +2720,14 @@ sort_2(const void *ap, const void *bp, void *dummy)
* an integer less than 0 when +b+ follows +a+, +0+ when +a+ and +b+
* are equivalent, or an integer greater than 0 when +a+ follows +b+.
*
- * The result is not guaranteed as stable. When comparison of two
+ * The result is not guaranteed to be stable. When the comparison of two
* elements returns +0+, the order of the elements is unpredictable.
*
- * See also Enumerable#sort_by.
+ * ary = [ "d", "a", "e", "c", "b" ]
+ * ary.sort! #=> ["a", "b", "c", "d", "e"]
+ * ary.sort! {|a, b| b <=> a} #=> ["e", "d", "c", "b", "a"]
*
- * a = [ "d", "a", "e", "c", "b" ]
- * a.sort! #=> ["a", "b", "c", "d", "e"]
- * a.sort! { |x,y| y <=> x } #=> ["e", "d", "c", "b", "a"]
+ * See also Enumerable#sort_by.
*/
VALUE
@@ -2456,14 +2739,13 @@ rb_ary_sort_bang(VALUE ary)
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.cmp_opt.opt_methods = 0;
data.cmp_opt.opt_inited = 0;
RARRAY_PTR_USE(tmp, ptr, {
- ruby_qsort(ptr, len, sizeof(VALUE),
- rb_block_given_p()?sort_1:sort_2, &data);
+ ruby_qsort(ptr, len, sizeof(VALUE),
+ rb_block_given_p()?sort_1:sort_2, &data);
}); /* WB: no new reference */
rb_ary_modify(ary);
if (ARY_EMBED_P(tmp)) {
@@ -2489,28 +2771,29 @@ rb_ary_sort_bang(VALUE ary)
rb_ary_unshare(ary);
}
else {
- ruby_sized_xfree((void *)ARY_HEAP_PTR(ary), ARY_HEAP_SIZE(ary));
+ ary_heap_free(ary);
}
- ARY_SET_PTR(ary, RARRAY_CONST_PTR(tmp));
+ ARY_SET_PTR(ary, ARY_HEAP_PTR(tmp));
ARY_SET_HEAP_LEN(ary, len);
- ARY_SET_CAPA(ary, RARRAY_LEN(tmp));
+ ARY_SET_CAPA(ary, ARY_HEAP_LEN(tmp));
}
/* tmp was lost ownership for the ptr */
FL_UNSET(tmp, FL_FREEZE);
FL_SET_EMBED(tmp);
ARY_SET_EMBED_LEN(tmp, 0);
FL_SET(tmp, FL_FREEZE);
- }
+ }
/* tmp will be GC'ed. */
RBASIC_SET_CLASS_RAW(tmp, rb_cArray); /* rb_cArray must be marked */
}
+ ary_verify(ary);
return ary;
}
/*
* call-seq:
* ary.sort -> new_ary
- * ary.sort { |a, b| block } -> new_ary
+ * ary.sort {|a, b| block} -> new_ary
*
* Returns a new array created by sorting +self+.
*
@@ -2521,14 +2804,14 @@ rb_ary_sort_bang(VALUE ary)
* an integer less than 0 when +b+ follows +a+, +0+ when +a+ and +b+
* are equivalent, or an integer greater than 0 when +a+ follows +b+.
*
- * The result is not guaranteed as stable. When comparison of two
+ * The result is not guaranteed to be stable. When the comparison of two
* elements returns +0+, the order of the elements is unpredictable.
*
- * See also Enumerable#sort_by.
+ * ary = [ "d", "a", "e", "c", "b" ]
+ * ary.sort #=> ["a", "b", "c", "d", "e"]
+ * ary.sort {|a, b| b <=> a} #=> ["e", "d", "c", "b", "a"]
*
- * a = [ "d", "a", "e", "c", "b" ]
- * a.sort #=> ["a", "b", "c", "d", "e"]
- * a.sort { |x,y| y <=> x } #=> ["e", "d", "c", "b", "a"]
+ * See also Enumerable#sort_by.
*/
VALUE
@@ -2548,12 +2831,12 @@ static VALUE rb_ary_bsearch_index(VALUE ary);
* By using binary search, finds a value from this array which meets
* the given condition in O(log n) where n is the size of the array.
*
- * You can use this method in two use cases: a find-minimum mode and
+ * You can use this method in two modes: a find-minimum mode and
* a find-any mode. In either case, the elements of the array must be
* monotone (or sorted) with respect to the block.
*
- * In find-minimum mode (this is a good choice for typical use case),
- * the block must return true or false, and there must be an index i
+ * In find-minimum mode (this is a good choice for typical use cases),
+ * the block must always return true or false, and there must be an index i
* (0 <= i <= ary.size) so that:
*
* - the block returns false for any element whose index is less than
@@ -2571,7 +2854,7 @@ static VALUE rb_ary_bsearch_index(VALUE ary);
* ary.bsearch {|x| x >= 100 } #=> nil
*
* In find-any mode (this behaves like libc's bsearch(3)), the block
- * must return a number, and there must be two indices i and j
+ * must always return a number, and there must be two indices i and j
* (0 <= i <= j <= ary.size) so that:
*
* - the block returns a positive number for ary[k] if 0 <= k < i,
@@ -2612,8 +2895,8 @@ rb_ary_bsearch(VALUE ary)
* By using binary search, finds an index of a value from this array which
* meets the given condition in O(log n) where n is the size of the array.
*
- * It supports two modes, depending on the nature of the block and they are
- * exactly the same as in the case of #bsearch method with the only difference
+ * It supports two modes, depending on the nature of the block. They are
+ * exactly the same as in the case of the #bsearch method, with the only difference
* being that this method returns the index of the element instead of the
* element itself. For more details consult the documentation for #bsearch.
*/
@@ -2674,17 +2957,18 @@ sort_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, dummy))
/*
* call-seq:
- * ary.sort_by! { |obj| block } -> ary
+ * ary.sort_by! {|obj| block} -> ary
* ary.sort_by! -> Enumerator
*
* Sorts +self+ in place using a set of keys generated by mapping the
* values in +self+ through the given block.
*
- * The result is not guaranteed as stable. When two keys are equal,
+ * The result is not guaranteed to be stable. When two keys are equal,
* the order of the corresponding elements is unpredictable.
*
* If no block is given, an Enumerator is returned instead.
*
+ * See also Enumerable#sort_by.
*/
static VALUE
@@ -2702,8 +2986,8 @@ rb_ary_sort_by_bang(VALUE ary)
/*
* call-seq:
- * ary.collect { |item| block } -> new_ary
- * ary.map { |item| block } -> new_ary
+ * ary.collect {|item| block} -> new_ary
+ * ary.map {|item| block} -> new_ary
* ary.collect -> Enumerator
* ary.map -> Enumerator
*
@@ -2716,8 +3000,8 @@ rb_ary_sort_by_bang(VALUE ary)
* If no block is given, an Enumerator is returned instead.
*
* a = [ "a", "b", "c", "d" ]
- * a.collect { |x| x + "!" } #=> ["a!", "b!", "c!", "d!"]
- * a.map.with_index { |x, i| x * i } #=> ["", "b", "cc", "ddd"]
+ * a.collect {|x| x + "!"} #=> ["a!", "b!", "c!", "d!"]
+ * a.map.with_index {|x, i| x * i} #=> ["", "b", "cc", "ddd"]
* a #=> ["a", "b", "c", "d"]
*/
@@ -2730,7 +3014,7 @@ rb_ary_collect(VALUE ary)
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
collect = rb_ary_new2(RARRAY_LEN(ary));
for (i = 0; i < RARRAY_LEN(ary); i++) {
- rb_ary_push(collect, rb_yield(RARRAY_AREF(ary, i)));
+ rb_ary_push(collect, rb_yield(RARRAY_AREF(ary, i)));
}
return collect;
}
@@ -2796,6 +3080,34 @@ rb_get_values_at(VALUE obj, long olen, int argc, const VALUE *argv, VALUE (*func
return result;
}
+static VALUE
+append_values_at_single(VALUE result, VALUE ary, long olen, VALUE idx)
+{
+ long beg, len;
+ if (FIXNUM_P(idx)) {
+ beg = FIX2LONG(idx);
+ }
+ /* check if idx is Range */
+ else if (rb_range_beg_len(idx, &beg, &len, olen, 1)) {
+ 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;
+ }
+ else {
+ beg = NUM2LONG(idx);
+ }
+ return rb_ary_push(result, rb_ary_entry(ary, beg));
+}
+
/*
* call-seq:
* ary.values_at(selector, ...) -> new_ary
@@ -2817,26 +3129,36 @@ rb_get_values_at(VALUE obj, long olen, int argc, const VALUE *argv, VALUE (*func
static VALUE
rb_ary_values_at(int argc, VALUE *argv, VALUE ary)
{
- return rb_get_values_at(ary, RARRAY_LEN(ary), argc, argv, rb_ary_entry);
+ 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]);
+ }
+ RB_GC_GUARD(ary);
+ return result;
}
/*
* call-seq:
- * ary.select { |item| block } -> new_ary
+ * ary.select {|item| block} -> new_ary
* ary.select -> Enumerator
+ * ary.filter {|item| block} -> new_ary
+ * ary.filter -> Enumerator
*
* Returns a new array containing all elements of +ary+
* for which the given +block+ returns a true value.
*
* If no block is given, an Enumerator is returned instead.
*
- * [1,2,3,4,5].select { |num| num.even? } #=> [2, 4]
+ * [1,2,3,4,5].select {|num| num.even? } #=> [2, 4]
*
- * a = %w{ a b c d e f }
- * a.select { |v| v =~ /[aeiou]/ } #=> ["a", "e"]
+ * a = %w[ a b c d e f ]
+ * a.select {|v| v =~ /[aeiou]/ } #=> ["a", "e"]
*
* See also Enumerable#select.
+ *
+ * Array#filter is an alias for Array#select.
*/
static VALUE
@@ -2886,21 +3208,25 @@ select_bang_ensure(VALUE a)
long len = RARRAY_LEN(ary);
long i1 = arg->len[0], i2 = arg->len[1];
- if (i2 < i1) {
+ if (i2 < len && i2 < i1) {
+ long tail = 0;
if (i1 < len) {
- RARRAY_PTR_USE(ary, ptr, {
- MEMMOVE(ptr + i2, ptr + i1, VALUE, len - i1);
+ tail = len - i1;
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ MEMMOVE(ptr + i2, ptr + i1, VALUE, tail);
});
}
- ARY_SET_LEN(ary, len - i1 + i2);
+ ARY_SET_LEN(ary, i2 + tail);
}
return ary;
}
/*
* call-seq:
- * ary.select! {|item| block } -> ary or nil
- * ary.select! -> Enumerator
+ * ary.select! {|item| block } -> ary or nil
+ * ary.select! -> Enumerator
+ * ary.filter! {|item| block } -> ary or nil
+ * ary.filter! -> Enumerator
*
* Invokes the given block passing in successive elements from +self+,
* deleting elements for which the block returns a +false+ value.
@@ -2909,10 +3235,11 @@ select_bang_ensure(VALUE a)
*
* If changes were made, it will return +self+, otherwise it returns +nil+.
*
- * See also Array#keep_if
- *
* If no block is given, an Enumerator is returned instead.
*
+ * See also Array#keep_if.
+ *
+ * Array#filter! is an alias for Array#select!.
*/
static VALUE
@@ -2930,18 +3257,19 @@ rb_ary_select_bang(VALUE ary)
/*
* call-seq:
- * ary.keep_if { |item| block } -> ary
+ * ary.keep_if {|item| block} -> ary
* ary.keep_if -> Enumerator
*
* Deletes every element of +self+ for which the given block evaluates to
- * +false+.
- *
- * See also Array#select!
+ * +false+, and returns +self+.
*
* If no block is given, an Enumerator is returned instead.
*
- * a = %w{ a b c d e f }
- * a.keep_if { |v| v =~ /[aeiou]/ } #=> ["a", "e"]
+ * a = %w[ a b c d e f ]
+ * a.keep_if {|v| v =~ /[aeiou]/ } #=> ["a", "e"]
+ * a #=> ["a", "e"]
+ *
+ * See also Array#select!.
*/
static VALUE
@@ -2968,7 +3296,7 @@ ary_resize_smaller(VALUE ary, long len)
/*
* call-seq:
* ary.delete(obj) -> item or nil
- * ary.delete(obj) { block } -> item or result of block
+ * ary.delete(obj) {block} -> item or result of block
*
* Deletes all items from +self+ that are equal to +obj+.
*
@@ -2982,7 +3310,7 @@ ary_resize_smaller(VALUE ary, long len)
* a.delete("b") #=> "b"
* a #=> ["a", "c"]
* a.delete("z") #=> nil
- * a.delete("z") { "not found" } #=> "not found"
+ * a.delete("z") {"not found"} #=> "not found"
*/
VALUE
@@ -3012,6 +3340,7 @@ rb_ary_delete(VALUE ary, VALUE item)
ary_resize_smaller(ary, i2);
+ ary_verify(ary);
return v;
}
@@ -3052,11 +3381,11 @@ rb_ary_delete_at(VALUE ary, long pos)
rb_ary_modify(ary);
del = RARRAY_AREF(ary, pos);
- RARRAY_PTR_USE(ary, ptr, {
- MEMMOVE(ptr+pos, ptr+pos+1, VALUE, len-pos-1);
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ MEMMOVE(ptr+pos, ptr+pos+1, VALUE, len-pos-1);
});
ARY_INCREASE_LEN(ary, -1);
-
+ ary_verify(ary);
return del;
}
@@ -3124,16 +3453,13 @@ rb_ary_slice_bang(int argc, VALUE *argv, VALUE ary)
len = orig_len - pos;
}
if (len == 0) return rb_ary_new2(0);
- arg2 = rb_ary_new4(len, RARRAY_CONST_PTR(ary)+pos);
+ arg2 = rb_ary_new4(len, RARRAY_CONST_PTR_TRANSIENT(ary)+pos);
RBASIC_SET_CLASS(arg2, rb_obj_class(ary));
rb_ary_splice(ary, pos, len, 0, 0);
return arg2;
}
- if (argc != 1) {
- /* error report */
- rb_scan_args(argc, argv, "11", NULL, NULL);
- }
+ rb_check_arity(argc, 1, 2);
arg1 = argv[0];
if (!FIXNUM_P(arg1)) {
@@ -3160,7 +3486,8 @@ ary_reject(VALUE orig, VALUE result)
for (i = 0; i < RARRAY_LEN(orig); i++) {
VALUE v = RARRAY_AREF(orig, i);
- if (!RTEST(rb_yield(v))) {
+
+ if (!RTEST(rb_yield(v))) {
rb_ary_push(result, v);
}
}
@@ -3189,7 +3516,6 @@ static VALUE
ary_reject_bang(VALUE ary)
{
struct select_bang_arg args;
-
rb_ary_modify_check(ary);
args.ary = ary;
args.len[0] = args.len[1] = 0;
@@ -3198,7 +3524,7 @@ ary_reject_bang(VALUE ary)
/*
* call-seq:
- * ary.reject! { |item| block } -> ary or nil
+ * ary.reject! {|item| block} -> ary or nil
* ary.reject! -> Enumerator
*
* Deletes every element of +self+ for which the block evaluates to +true+,
@@ -3215,6 +3541,7 @@ static VALUE
rb_ary_reject_bang(VALUE ary)
{
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
+ rb_ary_modify(ary);
return ary_reject_bang(ary);
}
@@ -3244,7 +3571,7 @@ rb_ary_reject(VALUE ary)
/*
* call-seq:
- * ary.delete_if { |item| block } -> ary
+ * ary.delete_if {|item| block} -> ary
* ary.delete_if -> Enumerator
*
* Deletes every element of +self+ for which block evaluates to +true+.
@@ -3263,6 +3590,7 @@ rb_ary_reject(VALUE ary)
static VALUE
rb_ary_delete_if(VALUE ary)
{
+ ary_verify(ary);
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
ary_reject_bang(ary);
return ary;
@@ -3272,7 +3600,8 @@ static VALUE
take_i(RB_BLOCK_CALL_FUNC_ARGLIST(val, cbarg))
{
VALUE *args = (VALUE *)cbarg;
- if (args[1]-- == 0) rb_iter_break();
+ if (args[1] == 0) rb_iter_break();
+ else args[1]--;
if (argc > 1) val = rb_ary_new4(argc, argv);
rb_ary_push(args[0], val);
return Qnil;
@@ -3297,7 +3626,7 @@ take_items(VALUE obj, long n)
/*
* call-seq:
* ary.zip(arg, ...) -> new_ary
- * ary.zip(arg, ...) { |arr| block } -> nil
+ * ary.zip(arg, ...) {|arr| block} -> nil
*
* Converts any arguments to arrays, then merges elements of +self+ with
* corresponding elements from each argument.
@@ -3441,14 +3770,14 @@ rb_ary_replace(VALUE copy, VALUE orig)
VALUE shared = 0;
if (ARY_OWNS_HEAP_P(copy)) {
- RARRAY_PTR_USE(copy, ptr, ruby_sized_xfree(ptr, ARY_HEAP_SIZE(copy)));
+ ary_heap_free(copy);
}
else if (ARY_SHARED_P(copy)) {
shared = ARY_SHARED(copy);
FL_UNSET_SHARED(copy);
}
FL_SET_EMBED(copy);
- ary_memcpy(copy, 0, RARRAY_LEN(orig), RARRAY_CONST_PTR(orig));
+ ary_memcpy(copy, 0, RARRAY_LEN(orig), RARRAY_CONST_PTR_TRANSIENT(orig));
if (shared) {
rb_ary_decrement_share(shared);
}
@@ -3457,16 +3786,17 @@ rb_ary_replace(VALUE copy, VALUE orig)
else {
VALUE shared = ary_make_shared(orig);
if (ARY_OWNS_HEAP_P(copy)) {
- RARRAY_PTR_USE(copy, ptr, ruby_sized_xfree(ptr, ARY_HEAP_SIZE(copy)));
+ ary_heap_free(copy);
}
else {
rb_ary_unshare_safe(copy);
}
FL_UNSET_EMBED(copy);
- ARY_SET_PTR(copy, RARRAY_CONST_PTR(orig));
- ARY_SET_LEN(copy, RARRAY_LEN(orig));
+ ARY_SET_PTR(copy, ARY_HEAP_PTR(orig));
+ ARY_SET_LEN(copy, ARY_HEAP_LEN(orig));
rb_ary_set_shared(copy, shared);
}
+ ary_verify(copy);
return copy;
}
@@ -3484,16 +3814,20 @@ VALUE
rb_ary_clear(VALUE ary)
{
rb_ary_modify_check(ary);
- ARY_SET_LEN(ary, 0);
if (ARY_SHARED_P(ary)) {
if (!ARY_EMBED_P(ary)) {
rb_ary_unshare(ary);
FL_SET_EMBED(ary);
+ ARY_SET_EMBED_LEN(ary, 0);
}
}
- else if (ARY_DEFAULT_SIZE * 2 < ARY_CAPA(ary)) {
- ary_resize_capa(ary, ARY_DEFAULT_SIZE * 2);
+ else {
+ ARY_SET_LEN(ary, 0);
+ if (ARY_DEFAULT_SIZE * 2 < ARY_CAPA(ary)) {
+ ary_resize_capa(ary, ARY_DEFAULT_SIZE * 2);
+ }
}
+ ary_verify(ary);
return ary;
}
@@ -3501,10 +3835,10 @@ rb_ary_clear(VALUE ary)
* call-seq:
* ary.fill(obj) -> ary
* ary.fill(obj, start [, length]) -> ary
- * ary.fill(obj, range ) -> ary
- * ary.fill { |index| block } -> ary
- * ary.fill(start [, length] ) { |index| block } -> ary
- * ary.fill(range) { |index| block } -> ary
+ * ary.fill(obj, range) -> ary
+ * ary.fill {|index| block} -> ary
+ * ary.fill(start [, length]) {|index| block} -> ary
+ * ary.fill(range) {|index| block} -> ary
*
* The first three forms set the selected elements of +self+ (which
* may be the entire array) to +obj+.
@@ -3523,8 +3857,8 @@ rb_ary_clear(VALUE ary)
* a.fill("x") #=> ["x", "x", "x", "x"]
* a.fill("z", 2, 2) #=> ["x", "x", "z", "z"]
* a.fill("y", 0..1) #=> ["y", "y", "z", "z"]
- * a.fill { |i| i*i } #=> [0, 1, 4, 9]
- * a.fill(-2) { |i| i*i*i } #=> [0, 1, 8, 27]
+ * a.fill {|i| i*i} #=> [0, 1, 4, 9]
+ * a.fill(-2) {|i| i*i*i} #=> [0, 1, 8, 27]
*/
static VALUE
@@ -3626,8 +3960,8 @@ rb_ary_plus(VALUE x, VALUE y)
len = xlen + ylen;
z = rb_ary_new2(len);
- ary_memcpy(z, 0, xlen, RARRAY_CONST_PTR(x));
- ary_memcpy(z, xlen, ylen, RARRAY_CONST_PTR(y));
+ ary_memcpy(z, 0, xlen, RARRAY_CONST_PTR_TRANSIENT(x));
+ ary_memcpy(z, xlen, ylen, RARRAY_CONST_PTR_TRANSIENT(y));
ARY_SET_LEN(z, len);
return z;
}
@@ -3637,23 +3971,23 @@ ary_append(VALUE x, VALUE y)
{
long n = RARRAY_LEN(y);
if (n > 0) {
- rb_ary_splice(x, RARRAY_LEN(x), 0, RARRAY_CONST_PTR(y), n);
+ rb_ary_splice(x, RARRAY_LEN(x), 0, RARRAY_CONST_PTR_TRANSIENT(y), n);
}
return x;
}
/*
* call-seq:
- * ary.concat(other_ary1, other_ary2,...) -> ary
+ * ary.concat(other_ary1, other_ary2, ...) -> ary
*
- * Appends the elements of +other_ary+s to +self+.
+ * Appends the elements of <code>other_ary</code>s to +self+.
*
- * [ "a", "b" ].concat( ["c", "d"] ) #=> [ "a", "b", "c", "d" ]
- * [ "a" ].concat( ["b"], ["c", "d"] ) #=> [ "a", "b", "c", "d" ]
+ * [ "a", "b" ].concat( ["c", "d"]) #=> [ "a", "b", "c", "d" ]
+ * [ "a" ].concat( ["b"], ["c", "d"]) #=> [ "a", "b", "c", "d" ]
* [ "a" ].concat #=> [ "a" ]
*
* a = [ 1, 2, 3 ]
- * a.concat( [ 4, 5 ] )
+ * a.concat( [ 4, 5 ])
* a #=> [ 1, 2, 3, 4, 5 ]
*
* a = [ 1, 2 ]
@@ -3667,7 +4001,10 @@ rb_ary_concat_multi(int argc, VALUE *argv, VALUE ary)
{
rb_ary_modify_check(ary);
- if (argc > 0) {
+ if (argc == 1) {
+ 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++) {
@@ -3676,6 +4013,7 @@ rb_ary_concat_multi(int argc, VALUE *argv, VALUE ary)
ary_append(ary, args);
}
+ ary_verify(ary);
return ary;
}
@@ -3730,16 +4068,16 @@ rb_ary_times(VALUE ary, VALUE times)
ary2 = ary_new(rb_obj_class(ary), len);
ARY_SET_LEN(ary2, len);
- ptr = RARRAY_CONST_PTR(ary);
+ 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, t, t, RARRAY_CONST_PTR(ary2));
+ ary_memcpy(ary2, t, t, RARRAY_CONST_PTR_TRANSIENT(ary2));
t *= 2;
}
if (t < len) {
- ary_memcpy(ary2, t, len-t, RARRAY_CONST_PTR(ary2));
+ ary_memcpy(ary2, t, len-t, RARRAY_CONST_PTR_TRANSIENT(ary2));
}
}
out:
@@ -3825,6 +4163,7 @@ recursive_equal(VALUE ary1, VALUE ary2, int recur)
if (recur) return Qtrue; /* Subtle! */
+ /* rb_equal() can evacuate ptrs */
p1 = RARRAY_CONST_PTR(ary1);
p2 = RARRAY_CONST_PTR(ary2);
len1 = RARRAY_LEN(ary1);
@@ -3837,8 +4176,8 @@ recursive_equal(VALUE ary1, VALUE ary2, int recur)
return Qfalse;
if (len1 < i)
return Qtrue;
- p1 = RARRAY_CONST_PTR(ary1) + i;
- p2 = RARRAY_CONST_PTR(ary2) + i;
+ p1 = RARRAY_CONST_PTR(ary1) + i;
+ p2 = RARRAY_CONST_PTR(ary2) + i;
}
else {
return Qfalse;
@@ -3875,7 +4214,7 @@ rb_ary_equal(VALUE ary1, VALUE ary2)
return rb_equal(ary2, ary1);
}
if (RARRAY_LEN(ary1) != RARRAY_LEN(ary2)) return Qfalse;
- if (RARRAY_CONST_PTR(ary1) == RARRAY_CONST_PTR(ary2)) return Qtrue;
+ if (RARRAY_CONST_PTR_TRANSIENT(ary1) == RARRAY_CONST_PTR_TRANSIENT(ary2)) return Qtrue;
return rb_exec_recursive_paired(recursive_equal, ary1, ary2, ary2);
}
@@ -3906,7 +4245,7 @@ rb_ary_eql(VALUE ary1, VALUE ary2)
if (ary1 == ary2) return Qtrue;
if (!RB_TYPE_P(ary2, T_ARRAY)) return Qfalse;
if (RARRAY_LEN(ary1) != RARRAY_LEN(ary2)) return Qfalse;
- if (RARRAY_CONST_PTR(ary1) == RARRAY_CONST_PTR(ary2)) return Qtrue;
+ if (RARRAY_CONST_PTR_TRANSIENT(ary1) == RARRAY_CONST_PTR_TRANSIENT(ary2)) return Qtrue;
return rb_exec_recursive_paired(recursive_eql, ary1, ary2, ary2);
}
@@ -3959,17 +4298,27 @@ rb_ary_includes(VALUE ary, VALUE item)
for (i=0; i<RARRAY_LEN(ary); i++) {
e = RARRAY_AREF(ary, i);
- switch (rb_equal_opt(e, item)) {
- case Qundef:
- if (rb_equal(e, item)) return Qtrue;
- break;
- case Qtrue:
+ if (rb_equal(e, item)) {
return Qtrue;
}
}
return Qfalse;
}
+static VALUE
+rb_ary_includes_by_eql(VALUE ary, VALUE item)
+{
+ long i;
+ VALUE e;
+
+ for (i=0; i<RARRAY_LEN(ary); i++) {
+ e = RARRAY_AREF(ary, i);
+ if (rb_eql(item, e)) {
+ return Qtrue;
+ }
+ }
+ return Qfalse;
+}
static VALUE
recursive_cmp(VALUE ary1, VALUE ary2, int recur)
@@ -4051,9 +4400,10 @@ ary_add_hash(VALUE hash, VALUE ary)
}
static inline VALUE
-ary_tmp_hash_new(void)
+ary_tmp_hash_new(VALUE ary)
{
- VALUE hash = rb_hash_new();
+ long size = RARRAY_LEN(ary);
+ VALUE hash = rb_hash_new_with_size(size);
RBASIC_CLEAR_CLASS(hash);
return hash;
@@ -4062,7 +4412,7 @@ ary_tmp_hash_new(void)
static VALUE
ary_make_hash(VALUE ary)
{
- VALUE hash = ary_tmp_hash_new();
+ VALUE hash = ary_tmp_hash_new(ary);
return ary_add_hash(hash, ary);
}
@@ -4081,19 +4431,19 @@ ary_add_hash_by(VALUE hash, VALUE ary)
static VALUE
ary_make_hash_by(VALUE ary)
{
- VALUE hash = ary_tmp_hash_new();
+ VALUE hash = ary_tmp_hash_new(ary);
return ary_add_hash_by(hash, ary);
}
static inline void
ary_recycle_hash(VALUE hash)
{
- if (RHASH(hash)->ntbl) {
- st_table *tbl = RHASH(hash)->ntbl;
- RHASH(hash)->ntbl = 0;
+ assert(RBASIC_CLASS(hash) == 0);
+ if (RHASH_ST_TABLE_P(hash)) {
+ st_table *tbl = RHASH_ST_TABLE(hash);
st_free_table(tbl);
+ RHASH_ST_CLEAR(hash);
}
- RB_GC_GUARD(hash);
}
/*
@@ -4111,6 +4461,8 @@ ary_recycle_hash(VALUE hash)
* [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ]
*
* If you need set-like behavior, see the library class Set.
+ *
+ * See also Array#difference.
*/
static VALUE
@@ -4120,11 +4472,21 @@ rb_ary_diff(VALUE ary1, VALUE ary2)
VALUE hash;
long i;
- hash = ary_make_hash(to_ary(ary2));
+ ary2 = to_ary(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;
+ }
+
+ hash = ary_make_hash(ary2);
for (i=0; i<RARRAY_LEN(ary1); i++) {
- if (st_lookup(rb_hash_tbl_raw(hash), RARRAY_AREF(ary1, i), 0)) continue;
+ if (rb_hash_stlike_lookup(hash, RARRAY_AREF(ary1, i), NULL)) continue;
rb_ary_push(ary3, rb_ary_elt(ary1, i));
}
ary_recycle_hash(hash);
@@ -4133,6 +4495,63 @@ rb_ary_diff(VALUE ary1, VALUE ary2)
/*
* call-seq:
+ * ary.difference(other_ary1, other_ary2, ...) -> ary
+ *
+ * Array Difference
+ *
+ * Returns a new array that is a copy of the receiver, removing any items
+ * that also appear in any of the arrays given as arguments.
+ * The order is preserved from the original array.
+ *
+ * It compares elements using their #hash and #eql? methods for efficiency.
+ *
+ * [ 1, 1, 2, 2, 3, 3, 4, 5 ].difference([ 1, 2, 4 ]) #=> [ 3, 3, 5 ]
+ * [ 1, 'c', :s, 'yep' ].difference([ 1 ], [ 'a', 'c' ]) #=> [ :s, "yep" ]
+ *
+ * If you need set-like behavior, see the library class Set.
+ *
+ * See also Array#-.
+ */
+
+static VALUE
+rb_ary_difference_multi(int argc, VALUE *argv, VALUE ary)
+{
+ VALUE ary_diff;
+ long i, length;
+ volatile VALUE t0;
+ bool *is_hash = ALLOCV_N(bool, t0, argc);
+ ary_diff = rb_ary_new();
+ length = RARRAY_LEN(ary);
+
+ for (i = 0; i < argc; i++) {
+ argv[i] = to_ary(argv[i]);
+ is_hash[i] = (length > SMALL_ARRAY_LEN && RARRAY_LEN(argv[i]) > SMALL_ARRAY_LEN);
+ if (is_hash[i]) argv[i] = ary_make_hash(argv[i]);
+ }
+
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ int j;
+ VALUE elt = rb_ary_elt(ary, i);
+ for (j = 0; j < argc; j++){
+ if (is_hash[j]) {
+ if (rb_hash_stlike_lookup(argv[j], RARRAY_AREF(ary, i), NULL))
+ break;
+ }
+ else {
+ if (rb_ary_includes_by_eql(argv[j], elt)) break;
+ }
+ }
+ if (j == argc) rb_ary_push(ary_diff, elt);
+ }
+
+ ALLOCV_END(t0);
+
+ return ary_diff;
+}
+
+
+/*
+ * call-seq:
* ary & other_ary -> new_ary
*
* Set Intersection --- Returns a new array containing unique elements common to the
@@ -4151,20 +4570,29 @@ static VALUE
rb_ary_and(VALUE ary1, VALUE ary2)
{
VALUE hash, ary3, v;
- st_table *table;
st_data_t vv;
long i;
ary2 = to_ary(ary2);
ary3 = rb_ary_new();
- if (RARRAY_LEN(ary2) == 0) return ary3;
+ 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;
+ }
+
hash = ary_make_hash(ary2);
- table = rb_hash_tbl_raw(hash);
for (i=0; i<RARRAY_LEN(ary1); i++) {
v = RARRAY_AREF(ary1, i);
vv = (st_data_t)v;
- if (st_delete(table, &vv, 0)) {
+ if (rb_hash_stlike_delete(hash, &vv, 0)) {
rb_ary_push(ary3, v);
}
}
@@ -4181,6 +4609,29 @@ ary_hash_orset(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
return ST_CONTINUE;
}
+static void
+rb_ary_union(VALUE ary_union, VALUE ary)
+{
+ long i;
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ VALUE elt = rb_ary_elt(ary, i);
+ if (rb_ary_includes_by_eql(ary_union, elt)) continue;
+ rb_ary_push(ary_union, elt);
+ }
+}
+
+static void
+rb_ary_union_hash(VALUE hash, VALUE ary2)
+{
+ long i;
+ for (i = 0; i < RARRAY_LEN(ary2); i++) {
+ VALUE elt = RARRAY_AREF(ary2, i);
+ if (!rb_hash_stlike_update(hash, (st_data_t)elt, ary_hash_orset, (st_data_t)elt)) {
+ RB_OBJ_WRITTEN(hash, Qundef, elt);
+ }
+ }
+}
+
/*
* call-seq:
* ary | other_ary -> new_ary
@@ -4193,24 +4644,25 @@ ary_hash_orset(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
* [ "a", "b", "c" ] | [ "c", "d", "a" ] #=> [ "a", "b", "c", "d" ]
* [ "c", "d", "a" ] | [ "a", "b", "c" ] #=> [ "c", "d", "a", "b" ]
*
- * See also Array#uniq.
+ * See also Array#union.
*/
static VALUE
rb_ary_or(VALUE ary1, VALUE ary2)
{
VALUE hash, ary3;
- long i;
ary2 = to_ary(ary2);
+ if (RARRAY_LEN(ary1) + RARRAY_LEN(ary2) <= SMALL_ARRAY_LEN) {
+ ary3 = rb_ary_new();
+ rb_ary_union(ary3, ary1);
+ rb_ary_union(ary3, ary2);
+ return ary3;
+ }
+
hash = ary_make_hash(ary1);
+ rb_ary_union_hash(hash, ary2);
- for (i=0; i<RARRAY_LEN(ary2); i++) {
- VALUE elt = RARRAY_AREF(ary2, i);
- if (!st_update(RHASH_TBL_RAW(hash), (st_data_t)elt, ary_hash_orset, (st_data_t)elt)) {
- RB_OBJ_WRITTEN(hash, Qundef, elt);
- }
- }
ary3 = rb_hash_values(hash);
ary_recycle_hash(hash);
return ary3;
@@ -4218,25 +4670,71 @@ rb_ary_or(VALUE ary1, VALUE ary2)
/*
* call-seq:
+ * ary.union(other_ary1, other_ary2, ...) -> ary
+ *
+ * Set Union --- Returns a new array by joining <code>other_ary</code>s with +self+,
+ * excluding any duplicates and preserving the order from the given arrays.
+ *
+ * It compares elements using their #hash and #eql? methods for efficiency.
+ *
+ * [ "a", "b", "c" ].union( [ "c", "d", "a" ] ) #=> [ "a", "b", "c", "d" ]
+ * [ "a" ].union( ["e", "b"], ["a", "c", "b"] ) #=> [ "a", "e", "b", "c" ]
+ * [ "a" ].union #=> [ "a" ]
+ *
+ * See also Array#|.
+ */
+
+static VALUE
+rb_ary_union_multi(int argc, VALUE *argv, VALUE ary)
+{
+ int i;
+ long sum;
+ VALUE hash, ary_union;
+
+ sum = RARRAY_LEN(ary);
+ for (i = 0; i < argc; i++){
+ argv[i] = to_ary(argv[i]);
+ sum += RARRAY_LEN(argv[i]);
+ }
+
+ if (sum <= SMALL_ARRAY_LEN) {
+ ary_union = rb_ary_new();
+
+ rb_ary_union(ary_union, ary);
+ for (i = 0; i < argc; i++) rb_ary_union(ary_union, argv[i]);
+
+ return ary_union;
+ }
+
+ hash = ary_make_hash(ary);
+ for (i = 0; i < argc; i++) rb_ary_union_hash(hash, argv[i]);
+
+ ary_union = rb_hash_values(hash);
+ ary_recycle_hash(hash);
+ return ary_union;
+}
+
+/*
+ * call-seq:
* ary.max -> obj
- * ary.max { |a, b| block } -> obj
+ * ary.max {|a, b| block} -> obj
* ary.max(n) -> array
- * ary.max(n) { |a, b| block } -> array
+ * ary.max(n) {|a, b| block} -> array
*
* Returns the object in _ary_ with the maximum value. The
* first form assumes all objects implement <code>Comparable</code>;
* the second uses the block to return <em>a <=> b</em>.
*
- * a = %w(albatross dog horse)
- * a.max #=> "horse"
- * a.max { |a, b| a.length <=> b.length } #=> "albatross"
+ * ary = %w(albatross dog horse)
+ * ary.max #=> "horse"
+ * ary.max {|a, b| a.length <=> b.length} #=> "albatross"
*
* If the +n+ argument is given, maximum +n+ elements are returned
* as an array.
*
- * a = %w[albatross dog horse]
- * a.max(2) #=> ["horse", "dog"]
- * a.max(2) {|a, b| a.length <=> b.length } #=> ["albatross", "horse"]
+ * ary = %w[albatross dog horse]
+ * ary.max(2) #=> ["horse", "dog"]
+ * ary.max(2) {|a, b| a.length <=> b.length } #=> ["albatross", "horse"]
*/
static VALUE
rb_ary_max(int argc, VALUE *argv, VALUE ary)
@@ -4246,9 +4744,7 @@ rb_ary_max(int argc, VALUE *argv, VALUE ary)
VALUE num;
long i;
- rb_scan_args(argc, argv, "01", &num);
-
- if (!NIL_P(num))
+ if (rb_check_arity(argc, 0, 1) && !NIL_P(num = argv[0]))
return rb_nmin_run(ary, num, 0, 1, 1);
if (rb_block_given_p()) {
@@ -4282,16 +4778,16 @@ rb_ary_max(int argc, VALUE *argv, VALUE ary)
* first form assumes all objects implement <code>Comparable</code>;
* the second uses the block to return <em>a <=> b</em>.
*
- * a = %w(albatross dog horse)
- * a.min #=> "albatross"
- * a.min { |a, b| a.length <=> b.length } #=> "dog"
+ * ary = %w(albatross dog horse)
+ * ary.min #=> "albatross"
+ * ary.min {|a, b| a.length <=> b.length} #=> "dog"
*
* If the +n+ argument is given, minimum +n+ elements are returned
* as an array.
*
- * a = %w[albatross dog horse]
- * a.min(2) #=> ["albatross", "dog"]
- * a.min(2) {|a, b| a.length <=> b.length } #=> ["dog", "horse"]
+ * ary = %w[albatross dog horse]
+ * ary.min(2) #=> ["albatross", "dog"]
+ * ary.min(2) {|a, b| a.length <=> b.length } #=> ["dog", "horse"]
*/
static VALUE
rb_ary_min(int argc, VALUE *argv, VALUE ary)
@@ -4301,9 +4797,7 @@ rb_ary_min(int argc, VALUE *argv, VALUE ary)
VALUE num;
long i;
- rb_scan_args(argc, argv, "01", &num);
-
- if (!NIL_P(num))
+ if (rb_check_arity(argc, 0, 1) && !NIL_P(num = argv[0]))
return rb_nmin_run(ary, num, 0, 0, 1);
if (rb_block_given_p()) {
@@ -4336,7 +4830,7 @@ push_value(st_data_t key, st_data_t val, st_data_t ary)
/*
* call-seq:
* ary.uniq! -> ary or nil
- * ary.uniq! { |item| ... } -> ary or nil
+ * ary.uniq! {|item| ...} -> ary or nil
*
* Removes duplicate elements from +self+.
*
@@ -4356,7 +4850,7 @@ push_value(st_data_t key, st_data_t val, st_data_t ary)
* b.uniq! # => nil
*
* c = [["student","sam"], ["student","george"], ["teacher","matz"]]
- * c.uniq! { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]]
+ * c.uniq! {|s| s.first} # => [["student", "sam"], ["teacher", "matz"]]
*
*/
@@ -4385,7 +4879,7 @@ rb_ary_uniq_bang(VALUE ary)
FL_SET_EMBED(ary);
}
ary_resize_capa(ary, hash_size);
- st_foreach(rb_hash_tbl_raw(hash), push_value, ary);
+ rb_hash_foreach(hash, push_value, ary);
ary_recycle_hash(hash);
return ary;
@@ -4394,7 +4888,7 @@ rb_ary_uniq_bang(VALUE ary)
/*
* call-seq:
* ary.uniq -> new_ary
- * ary.uniq { |item| ... } -> new_ary
+ * ary.uniq {|item| ...} -> new_ary
*
* Returns a new array by removing duplicate values in +self+.
*
@@ -4408,7 +4902,7 @@ rb_ary_uniq_bang(VALUE ary)
* a.uniq # => ["a", "b", "c"]
*
* b = [["student","sam"], ["student","george"], ["teacher","matz"]]
- * b.uniq { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]]
+ * b.uniq {|s| s.first} # => [["student", "sam"], ["teacher", "matz"]]
*
*/
@@ -4452,14 +4946,14 @@ rb_ary_compact_bang(VALUE ary)
long n;
rb_ary_modify(ary);
- p = t = (VALUE *)RARRAY_CONST_PTR(ary); /* WB: no new reference */
+ p = t = (VALUE *)RARRAY_CONST_PTR_TRANSIENT(ary); /* WB: no new reference */
end = p + RARRAY_LEN(ary);
while (t < end) {
if (NIL_P(*t)) t++;
else *p++ = *t++;
}
- n = p - RARRAY_CONST_PTR(ary);
+ n = p - RARRAY_CONST_PTR_TRANSIENT(ary);
if (RARRAY_LEN(ary) == n) {
return Qnil;
}
@@ -4490,7 +4984,7 @@ rb_ary_compact(VALUE ary)
* call-seq:
* ary.count -> int
* ary.count(obj) -> int
- * ary.count { |item| block } -> int
+ * ary.count {|item| block} -> int
*
* Returns the number of elements.
*
@@ -4503,7 +4997,7 @@ rb_ary_compact(VALUE ary)
* ary = [1, 2, 4, 2]
* ary.count #=> 4
* ary.count(2) #=> 2
- * ary.count { |x| x%2 == 0 } #=> 3
+ * ary.count {|x| x%2 == 0} #=> 3
*
*/
@@ -4512,7 +5006,7 @@ rb_ary_count(int argc, VALUE *argv, VALUE ary)
{
long i, n = 0;
- if (argc == 0) {
+ if (rb_check_arity(argc, 0, 1) == 0) {
VALUE v;
if (!rb_block_given_p())
@@ -4524,9 +5018,8 @@ rb_ary_count(int argc, VALUE *argv, VALUE ary)
}
}
else {
- VALUE obj;
+ VALUE obj = argv[0];
- rb_scan_args(argc, argv, "1", &obj);
if (rb_block_given_p()) {
rb_warn("given block not used");
}
@@ -4622,7 +5115,7 @@ rb_ary_flatten_bang(int argc, VALUE *argv, VALUE ary)
int mod = 0, level = -1;
VALUE result, lv;
- rb_scan_args(argc, argv, "01", &lv);
+ lv = (rb_check_arity(argc, 0, 1) ? argv[0] : Qnil);
rb_ary_modify_check(ary);
if (!NIL_P(lv)) level = NUM2INT(lv);
if (level == 0) return Qnil;
@@ -4665,11 +5158,12 @@ static VALUE
rb_ary_flatten(int argc, VALUE *argv, VALUE ary)
{
int mod = 0, level = -1;
- VALUE result, lv;
+ VALUE result;
- rb_scan_args(argc, argv, "01", &lv);
- if (!NIL_P(lv)) level = NUM2INT(lv);
- if (level == 0) return ary_make_shared_copy(ary);
+ if (rb_check_arity(argc, 0, 1) && !NIL_P(argv[0])) {
+ level = NUM2INT(argv[0]);
+ if (level == 0) return ary_make_shared_copy(ary);
+ }
result = flatten(ary, level, &mod);
OBJ_INFECT(result, ary);
@@ -4722,8 +5216,8 @@ rb_ary_shuffle_bang(int argc, VALUE *argv, VALUE ary)
while (i) {
long j = RAND_UPTO(i);
VALUE tmp;
- if (len != RARRAY_LEN(ary) || ptr != RARRAY_CONST_PTR(ary)) {
- rb_raise(rb_eRuntimeError, "modified during shuffle");
+ 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];
@@ -4775,11 +5269,14 @@ rb_ary_shuffle(int argc, VALUE *argv, VALUE ary)
* If the array is empty the first form returns +nil+ and the second form
* returns an empty array.
*
- * The optional +rng+ argument will be used as the random number generator.
- *
* a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
* a.sample #=> 7
* a.sample(4) #=> [6, 4, 2, 5]
+ *
+ * The optional +rng+ argument will be used as the random number generator.
+ *
+ * a.sample(random: Random.new(1)) #=> 6
+ * a.sample(4, random: Random.new(1)) #=> [6, 10, 9, 2]
*/
@@ -4790,6 +5287,7 @@ rb_ary_sample(int argc, VALUE *argv, VALUE ary)
VALUE opts, randgen = rb_cRandom;
long n, len, i, j, k, idx[10];
long rnds[numberof(idx)];
+ long memo_threshold;
if (OPTHASH_GIVEN_P(opts)) {
VALUE rnd;
@@ -4802,7 +5300,7 @@ rb_ary_sample(int argc, VALUE *argv, VALUE ary)
}
}
len = RARRAY_LEN(ary);
- if (argc == 0) {
+ if (rb_check_arity(argc, 0, 1) == 0) {
if (len < 2)
i = 0;
else
@@ -4810,7 +5308,7 @@ rb_ary_sample(int argc, VALUE *argv, VALUE ary)
return rb_ary_elt(ary, i);
}
- rb_scan_args(argc, argv, "1", &nv);
+ nv = argv[0];
n = NUM2LONG(nv);
if (n < 0) rb_raise(rb_eArgError, "negative sample number");
if (n > len) n = len;
@@ -4849,6 +5347,11 @@ rb_ary_sample(int argc, VALUE *argv, VALUE ary)
}
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;
if (n <= numberof(idx)) {
long sorted[numberof(idx)];
sorted[0] = idx[0] = rnds[0];
@@ -4862,12 +5365,44 @@ rb_ary_sample(int argc, VALUE *argv, VALUE ary)
sorted[j] = idx[i] = k;
}
result = rb_ary_new_capa(n);
- RARRAY_PTR_USE(result, ptr_result, {
+ RARRAY_PTR_USE_TRANSIENT(result, ptr_result, {
for (i=0; i<n; i++) {
ptr_result[i] = RARRAY_AREF(ary, idx[i]);
}
});
}
+ else if (n <= memo_threshold / 2) {
+ 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;
+ 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);
+ }
else {
result = rb_ary_dup(ary);
RBASIC_CLEAR_CLASS(result);
@@ -4896,16 +5431,16 @@ rb_ary_cycle_size(VALUE self, VALUE args, VALUE eobj)
n = RARRAY_AREF(args, 0);
}
if (RARRAY_LEN(self) == 0) return INT2FIX(0);
- if (n == Qnil) return DBL2NUM(INFINITY);
+ if (n == Qnil) return DBL2NUM(HUGE_VAL);
mul = NUM2LONG(n);
if (mul <= 0) return INT2FIX(0);
n = LONG2FIX(mul);
- return rb_funcallv(rb_ary_length(self), '*', 1, &n);
+ return rb_fix_mul_fix(rb_ary_length(self), n);
}
/*
* call-seq:
- * ary.cycle(n=nil) { |obj| block } -> nil
+ * ary.cycle(n=nil) {|obj| block} -> nil
* ary.cycle(n=nil) -> Enumerator
*
* Calls the given block for each element +n+ times or forever if +nil+ is
@@ -4918,8 +5453,8 @@ rb_ary_cycle_size(VALUE self, VALUE args, VALUE eobj)
* If no block is given, an Enumerator is returned instead.
*
* a = ["a", "b", "c"]
- * a.cycle { |x| puts x } # print, a, b, c, a, b, c,.. forever.
- * a.cycle(2) { |x| puts x } # print, a, b, c, a, b, c.
+ * a.cycle {|x| puts x} # print, a, b, c, a, b, c,.. forever.
+ * a.cycle(2) {|x| puts x} # print, a, b, c, a, b, c.
*
*/
@@ -4927,16 +5462,15 @@ static VALUE
rb_ary_cycle(int argc, VALUE *argv, VALUE ary)
{
long n, i;
- VALUE nv = Qnil;
- rb_scan_args(argc, argv, "01", &nv);
+ rb_check_arity(argc, 0, 1);
RETURN_SIZED_ENUMERATOR(ary, argc, argv, rb_ary_cycle_size);
- if (NIL_P(nv)) {
+ if (argc == 0 || NIL_P(argv[0])) {
n = -1;
}
else {
- n = NUM2LONG(nv);
+ n = NUM2LONG(argv[0]);
if (n <= 0) return Qnil;
}
@@ -4948,8 +5482,6 @@ rb_ary_cycle(int argc, VALUE *argv, VALUE ary)
return Qnil;
}
-#define tmpbuf(n, size) rb_str_tmp_new((n)*(size))
-#define tmpbuf_discard(s) (rb_str_resize((s), 0L), RBASIC_SET_CLASS_RAW(s, rb_cString))
#define tmpary(n) rb_ary_tmp_new(n)
#define tmpary_discard(a) (ary_discard(a), RBASIC_SET_CLASS_RAW(a, rb_cArray))
@@ -4962,11 +5494,9 @@ static int
yield_indexed_values(const VALUE values, const long r, const long *const p)
{
const VALUE result = rb_ary_new2(r);
- VALUE *const result_array = RARRAY_PTR(result);
- const VALUE *const values_array = RARRAY_CONST_PTR(values);
long i;
- for (i = 0; i < r; i++) result_array[i] = values_array[p[i]];
+ for (i = 0; i < r; i++) RARRAY_ASET(result, i, RARRAY_AREF(values, p[i]));
ARY_SET_LEN(result, r);
rb_yield(result);
return !RBASIC(values)->klass;
@@ -5026,10 +5556,16 @@ permute0(const long n, const long r, long *const p, char *const used, const VALU
static VALUE
descending_factorial(long from, long how_many)
{
- VALUE cnt = LONG2FIX(how_many >= 0);
- while (how_many-- > 0) {
- VALUE v = LONG2FIX(from--);
- cnt = rb_funcallv(cnt, '*', 1, &v);
+ VALUE cnt;
+ if (how_many > 0) {
+ cnt = LONG2FIX(from);
+ while (--how_many > 0) {
+ long v = --from;
+ cnt = rb_int_mul(cnt, LONG2FIX(v));
+ }
+ }
+ else {
+ cnt = LONG2FIX(how_many == 0);
}
return cnt;
}
@@ -5037,16 +5573,23 @@ descending_factorial(long from, long how_many)
static VALUE
binomial_coefficient(long comb, long size)
{
- VALUE r, v;
+ VALUE r;
+ long i;
if (comb > size-comb) {
comb = size-comb;
}
if (comb < 0) {
return LONG2FIX(0);
}
- r = descending_factorial(size, comb);
- v = descending_factorial(comb, comb);
- return rb_funcallv(r, id_div, 1, &v);
+ else if (comb == 0) {
+ 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));
+ }
+ return r;
}
static VALUE
@@ -5060,9 +5603,9 @@ rb_ary_permutation_size(VALUE ary, VALUE args, VALUE eobj)
/*
* call-seq:
- * ary.permutation { |p| block } -> ary
+ * ary.permutation {|p| block} -> ary
* ary.permutation -> Enumerator
- * ary.permutation(n) { |p| block } -> ary
+ * ary.permutation(n) {|p| block} -> ary
* ary.permutation(n) -> Enumerator
*
* When invoked with a block, yield all permutations of length +n+ of the
@@ -5089,13 +5632,13 @@ rb_ary_permutation_size(VALUE ary, VALUE args, VALUE eobj)
static VALUE
rb_ary_permutation(int argc, VALUE *argv, VALUE ary)
{
- VALUE num;
long r, n, i;
n = RARRAY_LEN(ary); /* Array length */
RETURN_SIZED_ENUMERATOR(ary, argc, argv, rb_ary_permutation_size); /* Return enumerator if no block */
- rb_scan_args(argc, argv, "01", &num);
- r = NIL_P(num) ? n : NUM2LONG(num); /* Permutation size from argument */
+ r = n;
+ if (rb_check_arity(argc, 0, 1) && !NIL_P(argv[0]))
+ r = NUM2LONG(argv[0]); /* Permutation size from argument */
if (r < 0 || n < r) {
/* no permutations: yield nothing */
@@ -5156,7 +5699,7 @@ rb_ary_combination_size(VALUE ary, VALUE args, VALUE eobj)
/*
* call-seq:
- * ary.combination(n) { |c| block } -> ary
+ * ary.combination(n) {|c| block} -> ary
* ary.combination(n) -> Enumerator
*
* When invoked with a block, yields all combinations of length +n+ of elements
@@ -5194,7 +5737,7 @@ rb_ary_combination(VALUE ary, VALUE num)
rb_yield(rb_ary_new2(0));
}
else if (n == 1) {
- for (i = 0; i < len; i++) {
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
}
}
@@ -5251,19 +5794,19 @@ rb_ary_repeated_permutation_size(VALUE ary, VALUE args, VALUE eobj)
{
long n = RARRAY_LEN(ary);
long k = NUM2LONG(RARRAY_AREF(args, 0));
- VALUE v;
if (k < 0) {
return LONG2FIX(0);
}
-
- v = LONG2NUM(k);
- return rb_funcallv(LONG2NUM(n), id_power, 1, &v);
+ if (n <= 0) {
+ return LONG2FIX(!k);
+ }
+ return rb_int_positive_pow(n, (unsigned long)k);
}
/*
* call-seq:
- * ary.repeated_permutation(n) { |p| block } -> ary
+ * ary.repeated_permutation(n) {|p| block} -> ary
* ary.repeated_permutation(n) -> Enumerator
*
* When invoked with a block, yield all repeated permutations of length +n+ of
@@ -5353,7 +5896,7 @@ rb_ary_repeated_combination_size(VALUE ary, VALUE args, VALUE eobj)
/*
* call-seq:
- * ary.repeated_combination(n) { |c| block } -> ary
+ * ary.repeated_combination(n) {|c| block} -> ary
* ary.repeated_combination(n) -> Enumerator
*
* When invoked with a block, yields all repeated combinations of length +n+ of
@@ -5393,7 +5936,7 @@ rb_ary_repeated_combination(VALUE ary, VALUE num)
rb_yield(rb_ary_new2(0));
}
else if (n == 1) {
- for (i = 0; i < len; i++) {
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
}
}
@@ -5416,7 +5959,7 @@ rb_ary_repeated_combination(VALUE ary, VALUE num)
/*
* call-seq:
* ary.product(other_ary, ...) -> new_ary
- * ary.product(other_ary, ...) { |p| block } -> ary
+ * ary.product(other_ary, ...) {|p| block} -> ary
*
* Returns an array of all combinations of elements from all arrays.
*
@@ -5439,15 +5982,14 @@ 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 t1 = tmpbuf(n, sizeof(int));
+ volatile VALUE t1 = Qundef;
VALUE *arrays = RARRAY_PTR(t0); /* The arrays we're computing the product of */
- int *counters = (int*)RSTRING_PTR(t1); /* The current position in each one */
+ int *counters = ALLOCV_N(int, t1, n); /* The current position in each one */
VALUE result = Qnil; /* The array we'll be returning, when no block given */
long i,j;
long resultlen = 1;
RBASIC_CLEAR_CLASS(t0);
- RBASIC_CLEAR_CLASS(t1);
/* initialize the arrays of arrays */
ARY_SET_LEN(t0, n);
@@ -5518,7 +6060,7 @@ rb_ary_product(int argc, VALUE *argv, VALUE ary)
}
done:
tmpary_discard(t0);
- tmpbuf_discard(t1);
+ ALLOCV_END(t1);
return NIL_P(result) ? ary : result;
}
@@ -5550,7 +6092,7 @@ rb_ary_take(VALUE obj, VALUE n)
/*
* call-seq:
- * ary.take_while { |obj| block } -> new_ary
+ * ary.take_while {|obj| block} -> new_ary
* ary.take_while -> Enumerator
*
* Passes elements to the block until the block returns +nil+ or +false+, then
@@ -5561,7 +6103,7 @@ rb_ary_take(VALUE obj, VALUE n)
* See also Array#drop_while
*
* a = [1, 2, 3, 4, 5, 0]
- * a.take_while { |i| i < 3 } #=> [1, 2]
+ * a.take_while {|i| i < 3} #=> [1, 2]
*
*/
@@ -5609,7 +6151,7 @@ rb_ary_drop(VALUE ary, VALUE n)
/*
* call-seq:
- * ary.drop_while { |obj| block } -> new_ary
+ * ary.drop_while {|obj| block} -> new_ary
* ary.drop_while -> Enumerator
*
* Drops elements up to, but not including, the first element for which the
@@ -5639,20 +6181,31 @@ rb_ary_drop_while(VALUE ary)
/*
* call-seq:
- * ary.any? [{ |obj| block }] -> true or false
+ * ary.any? [{|obj| block} ] -> true or false
+ * ary.any?(pattern) -> true or false
*
* See also Enumerable#any?
*/
static VALUE
-rb_ary_any_p(VALUE ary)
+rb_ary_any_p(int argc, VALUE *argv, VALUE ary)
{
long i, len = RARRAY_LEN(ary);
- const VALUE *ptr = RARRAY_CONST_PTR(ary);
+ rb_check_arity(argc, 0, 1);
if (!len) return Qfalse;
- if (!rb_block_given_p()) {
- for (i = 0; i < len; ++i) if (RTEST(ptr[i])) return Qtrue;
+ if (argc) {
+ 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;
+ }
+ }
+ else if (!rb_block_given_p()) {
+ for (i = 0; i < len; ++i) {
+ if (RTEST(RARRAY_AREF(ary, i))) return Qtrue;
+ }
}
else {
for (i = 0; i < RARRAY_LEN(ary); ++i) {
@@ -5663,6 +6216,124 @@ rb_ary_any_p(VALUE ary)
}
/*
+ * call-seq:
+ * ary.all? [{|obj| block} ] -> true or false
+ * ary.all?(pattern) -> true or false
+ *
+ * See also Enumerable#all?
+ */
+
+static VALUE
+rb_ary_all_p(int argc, VALUE *argv, VALUE ary)
+{
+ long i, len = RARRAY_LEN(ary);
+
+ rb_check_arity(argc, 0, 1);
+ if (!len) return Qtrue;
+ if (argc) {
+ 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 Qfalse;
+ }
+ }
+ else if (!rb_block_given_p()) {
+ for (i = 0; i < len; ++i) {
+ if (!RTEST(RARRAY_AREF(ary, i))) return Qfalse;
+ }
+ }
+ else {
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ if (!RTEST(rb_yield(RARRAY_AREF(ary, i)))) return Qfalse;
+ }
+ }
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * ary.none? [{|obj| block} ] -> true or false
+ * ary.none?(pattern) -> true or false
+ *
+ * See also Enumerable#none?
+ */
+
+static VALUE
+rb_ary_none_p(int argc, VALUE *argv, VALUE ary)
+{
+ long i, len = RARRAY_LEN(ary);
+
+ rb_check_arity(argc, 0, 1);
+ if (!len) return Qtrue;
+ if (argc) {
+ 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 Qfalse;
+ }
+ }
+ else if (!rb_block_given_p()) {
+ for (i = 0; i < len; ++i) {
+ if (RTEST(RARRAY_AREF(ary, i))) return Qfalse;
+ }
+ }
+ else {
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) return Qfalse;
+ }
+ }
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * ary.one? [{|obj| block} ] -> true or false
+ * ary.one?(pattern) -> true or false
+ *
+ * See also Enumerable#one?
+ */
+
+static VALUE
+rb_ary_one_p(int argc, VALUE *argv, VALUE ary)
+{
+ long i, len = RARRAY_LEN(ary);
+ VALUE result = Qfalse;
+
+ rb_check_arity(argc, 0, 1);
+ if (!len) return Qfalse;
+ if (argc) {
+ 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)))) {
+ if (result) return Qfalse;
+ result = Qtrue;
+ }
+ }
+ }
+ else if (!rb_block_given_p()) {
+ for (i = 0; i < len; ++i) {
+ if (RTEST(RARRAY_AREF(ary, i))) {
+ if (result) return Qfalse;
+ result = Qtrue;
+ }
+ }
+ }
+ else {
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) {
+ if (result) return Qfalse;
+ result = Qtrue;
+ }
+ }
+ }
+ return result;
+}
+
+/*
* call-seq:
* ary.dig(idx, ...) -> object
*
@@ -5678,7 +6349,7 @@ rb_ary_any_p(VALUE ary)
* [42, {foo: :bar}].dig(1, :foo) #=> :bar
*/
-VALUE
+static VALUE
rb_ary_dig(int argc, VALUE *argv, VALUE self)
{
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
@@ -5688,6 +6359,26 @@ rb_ary_dig(int argc, VALUE *argv, VALUE self)
return rb_obj_dig(argc, argv, self, Qnil);
}
+static inline VALUE
+finish_exact_sum(long n, VALUE r, VALUE v, int z)
+{
+ if (n != 0)
+ v = rb_fix_plus(LONG2FIX(n), v);
+ if (r != Qundef) {
+ /* r can be an Integer when mathn is loaded */
+ if (FIXNUM_P(r))
+ v = rb_fix_plus(r, v);
+ else if (RB_TYPE_P(r, T_BIGNUM))
+ v = rb_big_plus(r, v);
+ else
+ v = rb_rational_plus(r, v);
+ }
+ else if (!n && z) {
+ v = rb_fix_plus(LONG2FIX(0), v);
+ }
+ return v;
+}
+
/*
* call-seq:
* ary.sum(init=0) -> number
@@ -5737,8 +6428,7 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
long i, n;
int block_given;
- if (rb_scan_args(argc, argv, "01", &v) == 0)
- v = LONG2FIX(0);
+ v = (rb_check_arity(argc, 0, 1) ? argv[0] : LONG2FIX(0));
block_given = rb_block_given_p();
@@ -5769,41 +6459,24 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
else
goto not_exact;
}
- if (n != 0)
- v = rb_fix_plus(LONG2FIX(n), v);
- if (r != Qundef) {
- /* r can be an Integer when mathn is loaded */
- if (FIXNUM_P(r))
- v = rb_fix_plus(r, v);
- else if (RB_TYPE_P(r, T_BIGNUM))
- v = rb_big_plus(r, v);
- else
- v = rb_rational_plus(r, v);
- }
+ v = finish_exact_sum(n, r, v, argc!=0);
return v;
not_exact:
- if (n != 0)
- v = rb_fix_plus(LONG2FIX(n), v);
- if (r != Qundef) {
- /* r can be an Integer when mathn is loaded */
- if (FIXNUM_P(r))
- v = rb_fix_plus(r, v);
- else if (RB_TYPE_P(r, T_BIGNUM))
- v = rb_big_plus(r, v);
- else
- v = rb_rational_plus(r, v);
- }
+ v = finish_exact_sum(n, r, v, i!=0);
if (RB_FLOAT_TYPE_P(e)) {
- /* Kahan's compensated summation algorithm */
+ /*
+ * Kahan-Babuska balancing compensated summation algorithm
+ * See http://link.springer.com/article/10.1007/s00607-005-0139-x
+ */
double f, c;
f = NUM2DBL(v);
c = 0.0;
goto has_float_value;
for (; i < RARRAY_LEN(ary); i++) {
- double x, y, t;
+ double x, t;
e = RARRAY_AREF(ary, i);
if (block_given)
e = rb_yield(e);
@@ -5819,11 +6492,28 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
else
goto not_float;
- y = x - c;
- t = f + y;
- c = (t - f) - y;
+ if (isnan(f)) continue;
+ if (isnan(x)) {
+ f = x;
+ continue;
+ }
+ if (isinf(x)) {
+ if (isinf(f) && signbit(x) != signbit(f))
+ f = NAN;
+ else
+ f = x;
+ continue;
+ }
+ if (isinf(f)) continue;
+
+ t = f + x;
+ if (fabs(f) >= fabs(x))
+ c += ((f - t) + x);
+ else
+ c += ((x - t) + f);
f = t;
}
+ f += c;
return DBL2NUM(f);
not_float:
@@ -5874,11 +6564,12 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
* This method is safe to use with mutable objects such as hashes, strings or
* other arrays:
*
- * Array.new(4) { Hash.new } #=> [{}, {}, {}, {}]
+ * Array.new(4) {Hash.new} #=> [{}, {}, {}, {}]
+ * Array.new(4) {|i| i.to_s } #=> ["0", "1", "2", "3"]
*
* This is also a quick way to build up multi-dimensional arrays:
*
- * empty_table = Array.new(3) { Array.new(3) }
+ * empty_table = Array.new(3) {Array.new(3)}
* #=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
*
* An array can also be created by using the Array() method, provided by
@@ -6025,7 +6716,7 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
* Note that this operation leaves the array unchanged.
*
* arr = [1, 2, 3, 4, 5]
- * arr.each { |a| print a -= 10, " " }
+ * arr.each {|a| print a -= 10, " "}
* # prints: -9 -8 -7 -6 -5
* #=> [1, 2, 3, 4, 5]
*
@@ -6034,15 +6725,15 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
*
* words = %w[first second third fourth fifth sixth]
* str = ""
- * words.reverse_each { |word| str += "#{word} " }
+ * words.reverse_each {|word| str += "#{word} "}
* p str #=> "sixth fifth fourth third second first "
*
* The #map method can be used to create a new array based on the original
* array, but with the values modified by the supplied block:
*
- * arr.map { |a| 2*a } #=> [2, 4, 6, 8, 10]
+ * arr.map {|a| 2*a} #=> [2, 4, 6, 8, 10]
* arr #=> [1, 2, 3, 4, 5]
- * arr.map! { |a| a**2 } #=> [1, 4, 9, 16, 25]
+ * arr.map! {|a| a**2} #=> [1, 4, 9, 16, 25]
* arr #=> [1, 4, 9, 16, 25]
*
* == Selecting Items from an Array
@@ -6056,9 +6747,9 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
* === Non-destructive Selection
*
* arr = [1, 2, 3, 4, 5, 6]
- * arr.select { |a| a > 3 } #=> [4, 5, 6]
- * arr.reject { |a| a < 3 } #=> [3, 4, 5, 6]
- * arr.drop_while { |a| a < 4 } #=> [4, 5, 6]
+ * arr.select {|a| a > 3} #=> [4, 5, 6]
+ * arr.reject {|a| a < 3} #=> [3, 4, 5, 6]
+ * arr.drop_while {|a| a < 4} #=> [4, 5, 6]
* arr #=> [1, 2, 3, 4, 5, 6]
*
* === Destructive Selection
@@ -6069,11 +6760,11 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
* Similar to #select vs. #reject, #delete_if and #keep_if have the exact
* opposite result when supplied with the same block:
*
- * arr.delete_if { |a| a < 4 } #=> [4, 5, 6]
+ * arr.delete_if {|a| a < 4} #=> [4, 5, 6]
* arr #=> [4, 5, 6]
*
* arr = [1, 2, 3, 4, 5, 6]
- * arr.keep_if { |a| a < 4 } #=> [1, 2, 3]
+ * arr.keep_if {|a| a < 4} #=> [1, 2, 3]
* arr #=> [1, 2, 3]
*
*/
@@ -6098,7 +6789,6 @@ Init_Array(void)
rb_define_method(rb_cArray, "to_a", rb_ary_to_a, 0);
rb_define_method(rb_cArray, "to_h", rb_ary_to_h, 0);
rb_define_method(rb_cArray, "to_ary", rb_ary_to_ary_m, 0);
- rb_define_method(rb_cArray, "frozen?", rb_ary_frozen_p, 0);
rb_define_method(rb_cArray, "==", rb_ary_equal, 1);
rb_define_method(rb_cArray, "eql?", rb_ary_eql, 1);
@@ -6111,11 +6801,15 @@ Init_Array(void)
rb_define_method(rb_cArray, "first", rb_ary_first, -1);
rb_define_method(rb_cArray, "last", rb_ary_last, -1);
rb_define_method(rb_cArray, "concat", rb_ary_concat_multi, -1);
+ rb_define_method(rb_cArray, "union", rb_ary_union_multi, -1);
+ rb_define_method(rb_cArray, "difference", rb_ary_difference_multi, -1);
rb_define_method(rb_cArray, "<<", rb_ary_push, 1);
rb_define_method(rb_cArray, "push", rb_ary_push_m, -1);
+ rb_define_alias(rb_cArray, "append", "push");
rb_define_method(rb_cArray, "pop", rb_ary_pop_m, -1);
rb_define_method(rb_cArray, "shift", rb_ary_shift_m, -1);
rb_define_method(rb_cArray, "unshift", rb_ary_unshift_m, -1);
+ rb_define_alias(rb_cArray, "prepend", "unshift");
rb_define_method(rb_cArray, "insert", rb_ary_insert, -1);
rb_define_method(rb_cArray, "each", rb_ary_each, 0);
rb_define_method(rb_cArray, "each_index", rb_ary_each_index, 0);
@@ -6140,6 +6834,8 @@ Init_Array(void)
rb_define_method(rb_cArray, "map!", rb_ary_collect_bang, 0);
rb_define_method(rb_cArray, "select", rb_ary_select, 0);
rb_define_method(rb_cArray, "select!", rb_ary_select_bang, 0);
+ rb_define_method(rb_cArray, "filter", rb_ary_select, 0);
+ rb_define_method(rb_cArray, "filter!", rb_ary_select_bang, 0);
rb_define_method(rb_cArray, "keep_if", rb_ary_keep_if, 0);
rb_define_method(rb_cArray, "values_at", rb_ary_values_at, -1);
rb_define_method(rb_cArray, "delete", rb_ary_delete, 1);
@@ -6194,12 +6890,12 @@ Init_Array(void)
rb_define_method(rb_cArray, "drop_while", rb_ary_drop_while, 0);
rb_define_method(rb_cArray, "bsearch", rb_ary_bsearch, 0);
rb_define_method(rb_cArray, "bsearch_index", rb_ary_bsearch_index, 0);
- rb_define_method(rb_cArray, "any?", rb_ary_any_p, 0);
+ rb_define_method(rb_cArray, "any?", rb_ary_any_p, -1);
+ rb_define_method(rb_cArray, "all?", rb_ary_all_p, -1);
+ rb_define_method(rb_cArray, "none?", rb_ary_none_p, -1);
+ rb_define_method(rb_cArray, "one?", rb_ary_one_p, -1);
rb_define_method(rb_cArray, "dig", rb_ary_dig, -1);
rb_define_method(rb_cArray, "sum", rb_ary_sum, -1);
- id_cmp = rb_intern("<=>");
id_random = rb_intern("random");
- id_div = rb_intern("div");
- id_power = rb_intern("**");
}
diff --git a/ast.c b/ast.c
new file mode 100644
index 0000000000..883a838b78
--- /dev/null
+++ b/ast.c
@@ -0,0 +1,761 @@
+/* indent-tabs-mode: nil */
+#include "ruby.h"
+#include "ruby/encoding.h"
+#include "ruby/util.h"
+#include "internal.h"
+#include "node.h"
+#include "vm_core.h"
+#include "iseq.h"
+
+static VALUE rb_mAST;
+static VALUE rb_cNode;
+
+struct ASTNodeData {
+ rb_ast_t *ast;
+ NODE *node;
+};
+
+static void
+node_gc_mark(void *ptr)
+{
+ struct ASTNodeData *data = (struct ASTNodeData *)ptr;
+ rb_gc_mark((VALUE)data->ast);
+}
+
+static const rb_data_type_t rb_node_type = {
+ "AST/node",
+ {node_gc_mark, RUBY_TYPED_DEFAULT_FREE, 0,},
+ 0, 0,
+ RUBY_TYPED_FREE_IMMEDIATELY,
+};
+
+static VALUE rb_ast_node_alloc(VALUE klass);
+
+static void
+setup_node(VALUE obj, rb_ast_t *ast, NODE *node)
+{
+ struct ASTNodeData *data;
+
+ TypedData_Get_Struct(obj, struct ASTNodeData, &rb_node_type, data);
+ data->ast = ast;
+ data->node = node;
+}
+
+static VALUE
+ast_new_internal(rb_ast_t *ast, NODE *node)
+{
+ VALUE obj;
+
+ obj = rb_ast_node_alloc(rb_cNode);
+ setup_node(obj, ast, node);
+
+ return obj;
+}
+
+static VALUE rb_ast_parse_str(VALUE str);
+static VALUE rb_ast_parse_file(VALUE path);
+static VALUE rb_ast_parse_array(VALUE array);
+
+static VALUE
+ast_parse_new(void)
+{
+ return rb_parser_set_context(rb_parser_new(), NULL, 0);
+}
+
+static VALUE
+ast_parse_done(rb_ast_t *ast)
+{
+ if (!ast->body.root) {
+ rb_ast_dispose(ast);
+ rb_exc_raise(GET_EC()->errinfo);
+ }
+
+ return ast_new_internal(ast, (NODE *)ast->body.root);
+}
+
+/*
+ * call-seq:
+ * RubyVM::AbstractSyntaxTree.parse(string) -> 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(NODE_SCOPE(0) 1:0, 1:9): >
+ */
+static VALUE
+rb_ast_s_parse(VALUE module, VALUE str)
+{
+ return rb_ast_parse_str(str);
+}
+
+static VALUE
+rb_ast_parse_str(VALUE str)
+{
+ rb_ast_t *ast = 0;
+
+ StringValue(str);
+ ast = rb_parser_compile_string_path(ast_parse_new(), Qnil, str, 1);
+ return ast_parse_done(ast);
+}
+
+/*
+ * call-seq:
+ * RubyVM::AbstractSyntaxTree.parse_file(pathname) -> RubyVM::AbstractSyntaxTree::Node
+ *
+ * Reads the file from <code>pathname</code>, then parses it like ::parse,
+ * returning the root node of the abstract syntax tree.
+ *
+ * SyntaxError is raised if <code>pathname</code>'s contents are not
+ * valid Ruby syntax.
+ *
+ * RubyVM::AbstractSyntaxTree.parse_file("my-app/app.rb")
+ * # => #<RubyVM::AbstractSyntaxTree::Node(NODE_SCOPE(0) 1:0, 31:3): >
+ */
+static VALUE
+rb_ast_s_parse_file(VALUE module, VALUE path)
+{
+ return rb_ast_parse_file(path);
+}
+
+static VALUE
+rb_ast_parse_file(VALUE path)
+{
+ VALUE f;
+ rb_ast_t *ast = 0;
+ rb_encoding *enc = rb_utf8_encoding();
+
+ FilePathValue(path);
+ f = rb_file_open_str(path, "r");
+ rb_funcall(f, rb_intern("set_encoding"), 2, rb_enc_from_encoding(enc), rb_str_new_cstr("-"));
+ ast = rb_parser_compile_file_path(ast_parse_new(), Qnil, f, 1);
+ rb_io_close(f);
+ return ast_parse_done(ast);
+}
+
+static VALUE
+lex_array(VALUE array, int index)
+{
+ VALUE str = rb_ary_entry(array, index);
+ if (!NIL_P(str)) {
+ StringValue(str);
+ if (!rb_enc_asciicompat(rb_enc_get(str))) {
+ rb_raise(rb_eArgError, "invalid source encoding");
+ }
+ }
+ return str;
+}
+
+static VALUE
+rb_ast_parse_array(VALUE array)
+{
+ rb_ast_t *ast = 0;
+
+ array = rb_check_array_type(array);
+ ast = rb_parser_compile_generic(ast_parse_new(), lex_array, Qnil, array, 1);
+ return ast_parse_done(ast);
+}
+
+static VALUE node_children(rb_ast_t*, NODE*);
+
+static VALUE
+node_find(VALUE self, const int node_id)
+{
+ VALUE ary;
+ long i;
+ struct ASTNodeData *data;
+ TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+
+ if (nd_node_id(data->node) == node_id) return self;
+
+ ary = node_children(data->ast, data->node);
+
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ VALUE child = RARRAY_AREF(ary, i);
+
+ if (CLASS_OF(child) == rb_cNode) {
+ VALUE result = node_find(child, node_id);
+ if (RTEST(result)) return result;
+ }
+ }
+
+ return Qnil;
+}
+
+extern VALUE rb_e_script;
+
+static VALUE
+script_lines(VALUE path)
+{
+ VALUE hash, lines;
+ ID script_lines;
+ CONST_ID(script_lines, "SCRIPT_LINES__");
+ if (!rb_const_defined_at(rb_cObject, script_lines)) return Qnil;
+ hash = rb_const_get_at(rb_cObject, script_lines);
+ if (!RB_TYPE_P(hash, T_HASH)) return Qnil;
+ lines = rb_hash_lookup(hash, path);
+ if (!RB_TYPE_P(lines, T_ARRAY)) return Qnil;
+ return lines;
+}
+
+/*
+ * call-seq:
+ * RubyVM::AbstractSyntaxTree.of(proc) -> RubyVM::AbstractSyntaxTree::Node
+ * RubyVM::AbstractSyntaxTree.of(method) -> RubyVM::AbstractSyntaxTree::Node
+ *
+ * Returns AST nodes of the given proc or method.
+ *
+ * RubyVM::AbstractSyntaxTree.of(proc {1 + 2})
+ * # => #<RubyVM::AbstractSyntaxTree::Node(NODE_SCOPE(0) 1:35, 1:42): >
+ *
+ * def hello
+ * puts "hello, world"
+ * end
+ *
+ * RubyVM::AbstractSyntaxTree.of(method(:hello))
+ * # => #<RubyVM::AbstractSyntaxTree::Node(NODE_SCOPE(0) 1:0, 3:3): >
+ */
+static VALUE
+rb_ast_s_of(VALUE module, VALUE body)
+{
+ VALUE path, node, lines;
+ int node_id;
+ const rb_iseq_t *iseq = NULL;
+
+ if (rb_obj_is_proc(body)) {
+ iseq = vm_proc_iseq(body);
+
+ if (!rb_obj_is_iseq((VALUE)iseq)) {
+ iseq = NULL;
+ }
+ }
+ else {
+ iseq = rb_method_iseq(body);
+ }
+
+ if (!iseq) return Qnil;
+
+ path = rb_iseq_path(iseq);
+ node_id = iseq->body->location.node_id;
+ if (!NIL_P(lines = script_lines(path))) {
+ node = rb_ast_parse_array(lines);
+ }
+ else if (RSTRING_LEN(path) == 2 && memcmp(RSTRING_PTR(path), "-e", 2) == 0) {
+ node = rb_ast_parse_str(rb_e_script);
+ }
+ else {
+ node = rb_ast_parse_file(path);
+ }
+
+ return node_find(node, node_id);
+}
+
+static VALUE
+rb_ast_node_alloc(VALUE klass)
+{
+ struct ASTNodeData *data;
+ VALUE obj = TypedData_Make_Struct(klass, struct ASTNodeData, &rb_node_type, data);
+
+ return obj;
+}
+
+static const char*
+node_type_to_str(const NODE *node)
+{
+ return (ruby_node_name(nd_type(node)) + rb_strlen_lit("NODE_"));
+}
+
+/*
+ * call-seq:
+ * node.type -> symbol
+ *
+ * Returns the type of this node as a symbol.
+ *
+ * root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2")
+ * root.type # => :SCOPE
+ * call = root.children[2]
+ * call.type # => :OPCALL
+ */
+static VALUE
+rb_ast_node_type(VALUE self)
+{
+ struct ASTNodeData *data;
+ TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+
+ return rb_sym_intern_ascii_cstr(node_type_to_str(data->node));
+}
+
+#define NEW_CHILD(ast, node) node ? ast_new_internal(ast, node) : Qnil
+
+static VALUE
+rb_ary_new_from_node_args(rb_ast_t *ast, long n, ...)
+{
+ va_list ar;
+ VALUE ary;
+ long i;
+
+ ary = rb_ary_new2(n);
+
+ va_start(ar, n);
+ for (i=0; i<n; i++) {
+ NODE *node;
+ node = va_arg(ar, NODE *);
+ rb_ary_push(ary, NEW_CHILD(ast, node));
+ }
+ va_end(ar);
+ return ary;
+}
+
+static VALUE
+dump_block(rb_ast_t *ast, NODE *node)
+{
+ VALUE ary = rb_ary_new();
+ do {
+ rb_ary_push(ary, NEW_CHILD(ast, node->nd_head));
+ } while (node->nd_next &&
+ nd_type(node->nd_next) == NODE_BLOCK &&
+ (node = node->nd_next, 1));
+ if (node->nd_next) {
+ rb_ary_push(ary, NEW_CHILD(ast, node->nd_next));
+ }
+
+ return ary;
+}
+
+static VALUE
+dump_array(rb_ast_t *ast, NODE *node)
+{
+ VALUE ary = rb_ary_new();
+ rb_ary_push(ary, NEW_CHILD(ast, node->nd_head));
+
+ while (node->nd_next && nd_type(node->nd_next) == NODE_ARRAY) {
+ node = node->nd_next;
+ rb_ary_push(ary, NEW_CHILD(ast, node->nd_head));
+ }
+ rb_ary_push(ary, NEW_CHILD(ast, node->nd_next));
+
+ return ary;
+}
+
+static VALUE
+var_name(ID id)
+{
+ if (!id) return Qnil;
+ if (!rb_id2str(id)) return Qnil;
+ return ID2SYM(id);
+}
+
+static VALUE
+node_children(rb_ast_t *ast, NODE *node)
+{
+ char name[DECIMAL_SIZE_OF_BITS(sizeof(long) * CHAR_BIT) + 2]; /* including '$' */
+
+ enum node_type type = nd_type(node);
+ switch (type) {
+ case NODE_BLOCK:
+ return dump_block(ast, node);
+ case NODE_IF:
+ return rb_ary_new_from_node_args(ast, 3, node->nd_cond, node->nd_body, node->nd_else);
+ case NODE_UNLESS:
+ return rb_ary_new_from_node_args(ast, 3, node->nd_cond, node->nd_body, node->nd_else);
+ case NODE_CASE:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_body);
+ case NODE_CASE2:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_body);
+ case NODE_WHEN:
+ return rb_ary_new_from_node_args(ast, 3, node->nd_head, node->nd_body, node->nd_next);
+ case NODE_WHILE:
+ goto loop;
+ case NODE_UNTIL:
+ loop:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_cond, node->nd_body);
+ case NODE_ITER:
+ case NODE_FOR:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_iter, node->nd_body);
+ case NODE_FOR_MASGN:
+ return rb_ary_new_from_node_args(ast, 1, node->nd_var);
+ case NODE_BREAK:
+ goto jump;
+ case NODE_NEXT:
+ goto jump;
+ case NODE_RETURN:
+ jump:
+ return rb_ary_new_from_node_args(ast, 1, node->nd_stts);
+ case NODE_REDO:
+ return rb_ary_new_from_node_args(ast, 0);
+ case NODE_RETRY:
+ return rb_ary_new_from_node_args(ast, 0);
+ case NODE_BEGIN:
+ return rb_ary_new_from_node_args(ast, 1, node->nd_body);
+ case NODE_RESCUE:
+ return rb_ary_new_from_node_args(ast, 3, node->nd_head, node->nd_resq, node->nd_else);
+ case NODE_RESBODY:
+ return rb_ary_new_from_node_args(ast, 3, node->nd_args, node->nd_body, node->nd_head);
+ case NODE_ENSURE:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_ensr);
+ case NODE_AND:
+ goto andor;
+ case NODE_OR:
+ andor:
+ {
+ VALUE ary = rb_ary_new();
+
+ while (1) {
+ rb_ary_push(ary, NEW_CHILD(ast, node->nd_1st));
+ if (!node->nd_2nd || nd_type(node->nd_2nd) != (int)type)
+ break;
+ node = node->nd_2nd;
+ }
+ rb_ary_push(ary, NEW_CHILD(ast, node->nd_2nd));
+ return ary;
+ }
+ case NODE_MASGN:
+ if (NODE_NAMED_REST_P(node->nd_args)) {
+ return rb_ary_new_from_node_args(ast, 3, node->nd_value, node->nd_head, node->nd_args);
+ }
+ return rb_ary_new_from_node_args(ast, 2, node->nd_value, node->nd_head);
+ case NODE_LASGN:
+ goto asgn;
+ case NODE_DASGN:
+ goto asgn;
+ case NODE_DASGN_CURR:
+ goto asgn;
+ case NODE_IASGN:
+ goto asgn;
+ case NODE_CVASGN:
+ asgn:
+ if (NODE_REQUIRED_KEYWORD_P(node)) {
+ return rb_ary_new_from_args(1, var_name(node->nd_vid));
+ }
+ return rb_ary_new_from_args(2, var_name(node->nd_vid), NEW_CHILD(ast, node->nd_value));
+ case NODE_GASGN:
+ goto asgn;
+ case NODE_CDECL:
+ if (node->nd_vid) {
+ return rb_ary_new_from_args(2, ID2SYM(node->nd_vid), NEW_CHILD(ast, node->nd_value));
+ }
+ return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_else), ID2SYM(node->nd_else->nd_mid), NEW_CHILD(ast, node->nd_value));
+ case NODE_OP_ASGN1:
+ return rb_ary_new_from_args(4, NEW_CHILD(ast, node->nd_recv),
+ ID2SYM(node->nd_mid),
+ NEW_CHILD(ast, node->nd_args->nd_head),
+ NEW_CHILD(ast, node->nd_args->nd_body));
+ case NODE_OP_ASGN2:
+ return rb_ary_new_from_args(5, NEW_CHILD(ast, node->nd_recv),
+ node->nd_next->nd_aid ? Qtrue : Qfalse,
+ ID2SYM(node->nd_next->nd_vid),
+ ID2SYM(node->nd_next->nd_mid),
+ NEW_CHILD(ast, node->nd_value));
+ case NODE_OP_ASGN_AND:
+ return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_head), ID2SYM(idANDOP),
+ NEW_CHILD(ast, node->nd_value));
+ case NODE_OP_ASGN_OR:
+ return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_head), ID2SYM(idOROP),
+ NEW_CHILD(ast, node->nd_value));
+ case NODE_OP_CDECL:
+ return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_head),
+ ID2SYM(node->nd_aid),
+ NEW_CHILD(ast, node->nd_value));
+ case NODE_CALL:
+ case NODE_OPCALL:
+ case NODE_QCALL:
+ return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_recv),
+ ID2SYM(node->nd_mid),
+ NEW_CHILD(ast, node->nd_args));
+ case NODE_FCALL:
+ return rb_ary_new_from_args(2, ID2SYM(node->nd_mid),
+ NEW_CHILD(ast, node->nd_args));
+ case NODE_VCALL:
+ return rb_ary_new_from_args(1, ID2SYM(node->nd_mid));
+ case NODE_SUPER:
+ return rb_ary_new_from_node_args(ast, 1, node->nd_args);
+ case NODE_ZSUPER:
+ return rb_ary_new_from_node_args(ast, 0);
+ case NODE_ARRAY:
+ goto ary;
+ case NODE_VALUES:
+ ary:
+ return dump_array(ast, node);
+ case NODE_ZARRAY:
+ return rb_ary_new_from_node_args(ast, 0);
+ case NODE_HASH:
+ return rb_ary_new_from_node_args(ast, 1, node->nd_head);
+ case NODE_YIELD:
+ return rb_ary_new_from_node_args(ast, 1, node->nd_head);
+ case NODE_LVAR:
+ case NODE_DVAR:
+ return rb_ary_new_from_args(1, var_name(node->nd_vid));
+ case NODE_IVAR:
+ case NODE_CONST:
+ case NODE_CVAR:
+ case NODE_GVAR:
+ return rb_ary_new_from_args(1, ID2SYM(node->nd_vid));
+ case NODE_NTH_REF:
+ snprintf(name, sizeof(name), "$%ld", node->nd_nth);
+ return rb_ary_new_from_args(1, ID2SYM(rb_intern(name)));
+ case NODE_BACK_REF:
+ name[0] = '$';
+ name[1] = (char)node->nd_nth;
+ name[2] = '\0';
+ return rb_ary_new_from_args(1, ID2SYM(rb_intern(name)));
+ case NODE_MATCH:
+ goto lit;
+ case NODE_MATCH2:
+ if (node->nd_args) {
+ return rb_ary_new_from_node_args(ast, 3, node->nd_recv, node->nd_value, node->nd_args);
+ }
+ return rb_ary_new_from_node_args(ast, 2, node->nd_recv, node->nd_value);
+ case NODE_MATCH3:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_recv, node->nd_value);
+ case NODE_LIT:
+ goto lit;
+ case NODE_STR:
+ goto lit;
+ case NODE_XSTR:
+ lit:
+ return rb_ary_new_from_args(1, node->nd_lit);
+ case NODE_ONCE:
+ return rb_ary_new_from_node_args(ast, 1, node->nd_body);
+ case NODE_DSTR:
+ goto dlit;
+ case NODE_DXSTR:
+ goto dlit;
+ case NODE_DREGX:
+ goto dlit;
+ case NODE_DSYM:
+ dlit:
+ return rb_ary_new_from_args(3, node->nd_lit,
+ NEW_CHILD(ast, node->nd_next->nd_head),
+ NEW_CHILD(ast, node->nd_next->nd_next));
+ case NODE_EVSTR:
+ return rb_ary_new_from_node_args(ast, 1, node->nd_body);
+ case NODE_ARGSCAT:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_body);
+ case NODE_ARGSPUSH:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_body);
+ case NODE_SPLAT:
+ return rb_ary_new_from_node_args(ast, 1, node->nd_head);
+ case NODE_BLOCK_PASS:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_body);
+ case NODE_DEFN:
+ return rb_ary_new_from_args(2, ID2SYM(node->nd_mid), NEW_CHILD(ast, node->nd_defn));
+ case NODE_DEFS:
+ return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_recv), ID2SYM(node->nd_mid), NEW_CHILD(ast, node->nd_defn));
+ case NODE_ALIAS:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_1st, node->nd_2nd);
+ case NODE_VALIAS:
+ return rb_ary_new_from_args(2, ID2SYM(node->nd_alias), ID2SYM(node->nd_orig));
+ case NODE_UNDEF:
+ return rb_ary_new_from_node_args(ast, 1, node->nd_undef);
+ case NODE_CLASS:
+ return rb_ary_new_from_node_args(ast, 3, node->nd_cpath, node->nd_super, node->nd_body);
+ case NODE_MODULE:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_cpath, node->nd_body);
+ case NODE_SCLASS:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_recv, node->nd_body);
+ case NODE_COLON2:
+ return rb_ary_new_from_args(2, NEW_CHILD(ast, node->nd_head), ID2SYM(node->nd_mid));
+ case NODE_COLON3:
+ return rb_ary_new_from_args(1, ID2SYM(node->nd_mid));
+ case NODE_DOT2:
+ goto dot;
+ case NODE_DOT3:
+ goto dot;
+ case NODE_FLIP2:
+ goto dot;
+ case NODE_FLIP3:
+ dot:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_beg, node->nd_end);
+ case NODE_SELF:
+ return rb_ary_new_from_node_args(ast, 0);
+ case NODE_NIL:
+ return rb_ary_new_from_node_args(ast, 0);
+ case NODE_TRUE:
+ return rb_ary_new_from_node_args(ast, 0);
+ case NODE_FALSE:
+ return rb_ary_new_from_node_args(ast, 0);
+ case NODE_ERRINFO:
+ return rb_ary_new_from_node_args(ast, 0);
+ case NODE_DEFINED:
+ return rb_ary_new_from_node_args(ast, 1, node->nd_head);
+ case NODE_POSTEXE:
+ return rb_ary_new_from_node_args(ast, 1, node->nd_body);
+ case NODE_ATTRASGN:
+ return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_recv), ID2SYM(node->nd_mid), NEW_CHILD(ast, node->nd_args));
+ case NODE_LAMBDA:
+ return rb_ary_new_from_node_args(ast, 1, node->nd_body);
+ case NODE_OPT_ARG:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_body, node->nd_next);
+ case NODE_KW_ARG:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_body, node->nd_next);
+ case NODE_POSTARG:
+ if (NODE_NAMED_REST_P(node->nd_1st)) {
+ return rb_ary_new_from_node_args(ast, 2, node->nd_1st, node->nd_2nd);
+ }
+ return rb_ary_new_from_node_args(ast, 1, node->nd_2nd);
+ case NODE_ARGS:
+ {
+ struct rb_args_info *ainfo = node->nd_ainfo;
+ return rb_ary_new_from_args(10,
+ INT2NUM(ainfo->pre_args_num),
+ NEW_CHILD(ast, ainfo->pre_init),
+ NEW_CHILD(ast, ainfo->opt_args),
+ var_name(ainfo->first_post_arg),
+ INT2NUM(ainfo->post_args_num),
+ NEW_CHILD(ast, ainfo->post_init),
+ var_name(ainfo->rest_arg),
+ NEW_CHILD(ast, ainfo->kw_args),
+ NEW_CHILD(ast, ainfo->kw_rest_arg),
+ var_name(ainfo->block_arg));
+ }
+ case NODE_SCOPE:
+ {
+ ID *tbl = node->nd_tbl;
+ int i, size = tbl ? (int)*tbl++ : 0;
+ VALUE locals = rb_ary_new_capa(size);
+ for (i = 0; i < size; i++) {
+ rb_ary_push(locals, var_name(tbl[i]));
+ }
+ return rb_ary_new_from_args(3, locals, NEW_CHILD(ast, node->nd_args), NEW_CHILD(ast, node->nd_body));
+ }
+ case NODE_ARGS_AUX:
+ case NODE_LAST:
+ break;
+ }
+
+ rb_bug("node_children: unknown node: %s", ruby_node_name(type));
+}
+
+/*
+ * call-seq:
+ * node.children -> array
+ *
+ * Returns AST nodes under this one. Each kind of node
+ * has different children, depending on what kind of node it is.
+ *
+ * The returned array may contain other nodes or <code>nil</code>.
+ */
+static VALUE
+rb_ast_node_children(VALUE self)
+{
+ struct ASTNodeData *data;
+ TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+
+ return node_children(data->ast, data->node);
+}
+
+/*
+ * call-seq:
+ * node.first_lineno -> integer
+ *
+ * The line number in the source code where this AST's text began.
+ */
+static VALUE
+rb_ast_node_first_lineno(VALUE self)
+{
+ struct ASTNodeData *data;
+ TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+
+ return INT2NUM(nd_first_lineno(data->node));
+}
+
+/*
+ * call-seq:
+ * node.first_column -> integer
+ *
+ * The column number in the source code where this AST's text began.
+ */
+static VALUE
+rb_ast_node_first_column(VALUE self)
+{
+ struct ASTNodeData *data;
+ TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+
+ return INT2NUM(nd_first_column(data->node));
+}
+
+/*
+ * call-seq:
+ * node.last_lineno -> integer
+ *
+ * The line number in the source code where this AST's text ended.
+ */
+static VALUE
+rb_ast_node_last_lineno(VALUE self)
+{
+ struct ASTNodeData *data;
+ TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+
+ return INT2NUM(nd_last_lineno(data->node));
+}
+
+/*
+ * call-seq:
+ * node.last_column -> integer
+ *
+ * The column number in the source code where this AST's text ended.
+ */
+static VALUE
+rb_ast_node_last_column(VALUE self)
+{
+ struct ASTNodeData *data;
+ TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+
+ return INT2NUM(nd_last_column(data->node));
+}
+
+/*
+ * call-seq:
+ * node.inspect -> string
+ *
+ * Returns debugging information about this node as a string.
+ */
+static VALUE
+rb_ast_node_inspect(VALUE self)
+{
+ VALUE str;
+ VALUE cname;
+ struct ASTNodeData *data;
+ TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+
+ cname = rb_class_path(rb_obj_class(self));
+ str = rb_str_new2("#<");
+
+ rb_str_append(str, cname);
+ rb_str_catf(str, ":%s@%d:%d-%d:%d>",
+ node_type_to_str(data->node),
+ nd_first_lineno(data->node), nd_first_column(data->node),
+ nd_last_lineno(data->node), nd_last_column(data->node));
+
+ return str;
+}
+
+void
+Init_ast(void)
+{
+ /*
+ * AbstractSyntaxTree provides methods to parse Ruby code into
+ * abstract syntax trees. The nodes in the tree
+ * are instances of RubyVM::AbstractSyntaxTree::Node.
+ */
+ rb_mAST = rb_define_module_under(rb_cRubyVM, "AbstractSyntaxTree");
+ /*
+ * RubyVM::AbstractSyntaxTree::Node instances are created by parse methods in
+ * RubyVM::AbstractSyntaxTree.
+ */
+ rb_cNode = rb_define_class_under(rb_mAST, "Node", rb_cObject);
+
+ rb_undef_alloc_func(rb_cNode);
+ rb_define_singleton_method(rb_mAST, "parse", rb_ast_s_parse, 1);
+ rb_define_singleton_method(rb_mAST, "parse_file", rb_ast_s_parse_file, 1);
+ rb_define_singleton_method(rb_mAST, "of", rb_ast_s_of, 1);
+ rb_define_method(rb_cNode, "type", rb_ast_node_type, 0);
+ rb_define_method(rb_cNode, "first_lineno", rb_ast_node_first_lineno, 0);
+ rb_define_method(rb_cNode, "first_column", rb_ast_node_first_column, 0);
+ rb_define_method(rb_cNode, "last_lineno", rb_ast_node_last_lineno, 0);
+ rb_define_method(rb_cNode, "last_column", rb_ast_node_last_column, 0);
+ rb_define_method(rb_cNode, "children", rb_ast_node_children, 0);
+ rb_define_method(rb_cNode, "inspect", rb_ast_node_inspect, 0);
+}
diff --git a/benchmark/README.md b/benchmark/README.md
new file mode 100644
index 0000000000..24a2669143
--- /dev/null
+++ b/benchmark/README.md
@@ -0,0 +1,72 @@
+# ruby/benchmark
+
+This directory has benchmark definitions to be run with
+[benchmark\_driver.gem](https://github.com/benchmark-driver/benchmark-driver).
+
+## Normal usage
+
+Execute `gem install benchmark_driver` and run a command like:
+
+```bash
+# Run a benchmark script with the ruby in the $PATH
+benchmark-driver benchmark/app_fib.rb
+
+# Run benchmark scripts with multiple Ruby executables or options
+benchmark-driver benchmark/*.rb -e /path/to/ruby -e '/path/to/ruby --jit'
+
+# Or compare Ruby versions managed by rbenv
+benchmark-driver benchmark/*.rb --rbenv '2.5.1;2.6.0-preview2 --jit'
+
+# You can collect many metrics in many ways
+benchmark-driver benchmark/*.rb --runner memory --output markdown
+
+# Some are defined with YAML for complex setup or accurate measurement
+benchmark-driver benchmark/*.yml
+```
+
+See also:
+
+```console
+Usage: benchmark-driver [options] RUBY|YAML...
+ -r, --runner TYPE Specify runner type: ips, time, memory, once (default: ips)
+ -o, --output TYPE Specify output type: compare, simple, markdown, record (default: compare)
+ -e, --executables EXECS Ruby executables (e1::path1 arg1; e2::path2 arg2;...)
+ --rbenv VERSIONS Ruby executables in rbenv (x.x.x arg1;y.y.y arg2;...)
+ --repeat-count NUM Try benchmark NUM times and use the fastest result or the worst memory usage
+ --repeat-result TYPE Yield "best", "average" or "worst" result with --repeat-count (default: best)
+ --bundler Install and use gems specified in Gemfile
+ --filter REGEXP Filter out benchmarks with given regexp
+ --run-duration SECONDS Warmup estimates loop_count to run for this duration (default: 3)
+ -v, --verbose Verbose mode. Multiple -v options increase visilibity (max: 2)
+```
+
+## make benchmark
+
+Using `make benchmark`, `make update-benchmark-driver` automatically downloads
+the supported version of benchmark\_driver, and it runs benchmarks with the downloaded
+benchmark\_driver.
+
+```bash
+# Run all benchmarks with the ruby in the $PATH and the built ruby
+make benchmark
+
+# Or compare with specific ruby binary
+make benchmark COMPARE_RUBY="/path/to/ruby --jit"
+
+# Run vm1 benchmarks
+make benchmark ITEM=vm1
+
+# Run some limited benchmarks in ITEM-matched files
+make benchmark ITEM=vm1 OPTS=--filter=block
+
+# You can specify the benchmark by an exact filename instead of using the default argument:
+# ARGS = $$(find $(srcdir)/benchmark -maxdepth 1 -name '*$(ITEM)*.yml' -o -name '*$(ITEM)*.rb')
+make benchmark ARGS=../benchmark/erb_render.yml
+
+# You can specify any option via $OPTS
+make benchmark OPTS="--help"
+
+# With `make benchmark`, some special runner plugins are available:
+# -r peak, -r size, -r total, -r utime, -r stime, -r cutime, -r cstime
+make benchmark ITEM=vm2_bigarray OPTS="-r peak"
+```
diff --git a/benchmark/bm_app_answer.rb b/benchmark/app_answer.rb
index 3cd8a8fd37..3cd8a8fd37 100644
--- a/benchmark/bm_app_answer.rb
+++ b/benchmark/app_answer.rb
diff --git a/benchmark/bm_app_aobench.rb b/benchmark/app_aobench.rb
index 2bd6acfaf8..2bd6acfaf8 100644
--- a/benchmark/bm_app_aobench.rb
+++ b/benchmark/app_aobench.rb
diff --git a/benchmark/app_erb.yml b/benchmark/app_erb.yml
new file mode 100644
index 0000000000..31e29b7644
--- /dev/null
+++ b/benchmark/app_erb.yml
@@ -0,0 +1,23 @@
+#
+# Create many HTML strings with ERB.
+#
+prelude: |
+ require 'erb'
+
+ data = <<erb
+ <html>
+ <head> <%= title %> </head>
+ <body>
+ <h1> <%= title %> </h1>
+ <p>
+ <%= content %>
+ </p>
+ </body>
+ </html>
+ erb
+
+ title = "hello world!"
+ content = "hello world!\n" * 10
+benchmark:
+ app_erb: ERB.new(data).result(binding)
+loop_count: 15000
diff --git a/benchmark/bm_app_factorial.rb b/benchmark/app_factorial.rb
index 45f471dfdb..45f471dfdb 100644
--- a/benchmark/bm_app_factorial.rb
+++ b/benchmark/app_factorial.rb
diff --git a/benchmark/bm_app_fib.rb b/benchmark/app_fib.rb
index 34a7b2e725..34a7b2e725 100644
--- a/benchmark/bm_app_fib.rb
+++ b/benchmark/app_fib.rb
diff --git a/benchmark/bm_app_lc_fizzbuzz.rb b/benchmark/app_lc_fizzbuzz.rb
index f09574bbeb..f09574bbeb 100644
--- a/benchmark/bm_app_lc_fizzbuzz.rb
+++ b/benchmark/app_lc_fizzbuzz.rb
diff --git a/benchmark/bm_app_mandelbrot.rb b/benchmark/app_mandelbrot.rb
index 801b75e8e2..801b75e8e2 100644
--- a/benchmark/bm_app_mandelbrot.rb
+++ b/benchmark/app_mandelbrot.rb
diff --git a/benchmark/app_pentomino.rb b/benchmark/app_pentomino.rb
new file mode 100644
index 0000000000..47be7b203f
--- /dev/null
+++ b/benchmark/app_pentomino.rb
@@ -0,0 +1,130 @@
+#!/usr/local/bin/ruby
+# This program is contributed by Shin Nishiyama
+
+
+# modified by K.Sasada
+
+NP = 5
+ROW = 8 + NP
+COL = 8
+
+$p = []
+$b = []
+$no = 0
+
+def piece(n, a, nb)
+ nb.each{|x|
+ a[n] = x
+ if n == NP-1
+ $p << [a.sort]
+ else
+ nbc=nb.dup
+ [-ROW, -1, 1, ROW].each{|d|
+ if x+d > 0 and not a.include?(x+d) and not nbc.include?(x+d)
+ nbc << x+d
+ end
+ }
+ nbc.delete x
+ piece(n+1,a[0..n],nbc)
+ end
+ }
+end
+
+def kikaku(a)
+ a.collect {|x| x - a[0]}
+end
+def ud(a)
+ kikaku(a.collect {|x| ((x+NP)%ROW)-ROW*((x+NP)/ROW) }.sort)
+end
+def rl(a)
+ kikaku(a.collect {|x| ROW*((x+NP)/ROW)+ROW-((x+NP)%ROW)}.sort)
+end
+def xy(a)
+ kikaku(a.collect {|x| ROW*((x+NP)%ROW) + (x+NP)/ROW }.sort)
+end
+
+def mkpieces
+ piece(0,[],[0])
+ $p.each do |a|
+ a0 = a[0]
+ a[1] = ud(a0)
+ a[2] = rl(a0)
+ a[3] = ud(rl(a0))
+ a[4] = xy(a0)
+ a[5] = ud(xy(a0))
+ a[6] = rl(xy(a0))
+ a[7] = ud(rl(xy(a0)))
+ a.sort!
+ a.uniq!
+ end
+ $p.uniq!.sort! {|x,y| x[0] <=> y[0] }
+end
+
+def mkboard
+ (0...ROW*COL).each{|i|
+ if i % ROW >= ROW-NP
+ $b[i] = -2
+ else
+ $b[i] = -1
+ end
+ $b[3*ROW+3]=$b[3*ROW+4]=$b[4*ROW+3]=$b[4*ROW+4]=-2
+ }
+end
+
+def pboard
+ return # skip print
+ print "No. #$no\n"
+ (0...COL).each{|i|
+ print "|"
+ (0...ROW-NP).each{|j|
+ x = $b[i*ROW+j]
+ if x < 0
+ print "..|"
+ else
+ printf "%2d|",x+1
+ end
+ }
+ print "\n"
+ }
+ print "\n"
+end
+
+$pnum=[]
+def setpiece(a,pos)
+ if a.length == $p.length then
+ $no += 1
+ pboard
+ return
+ end
+ while $b[pos] != -1
+ pos += 1
+ end
+ ($pnum - a).each do |i|
+ $p[i].each do |x|
+ f = 0
+ x.each{|s|
+ if $b[pos+s] != -1
+ f=1
+ break
+ end
+ }
+ if f == 0 then
+ x.each{|s|
+ $b[pos+s] = i
+ }
+ a << i
+ setpiece(a.dup, pos)
+ a.pop
+ x.each{|s|
+ $b[pos+s] = -1
+ }
+ end
+ end
+ end
+end
+
+mkpieces
+mkboard
+$p[4] = [$p[4][0]]
+$pnum = (0...$p.length).to_a
+setpiece([],0)
diff --git a/benchmark/bm_app_raise.rb b/benchmark/app_raise.rb
index 5db8f95d50..5db8f95d50 100644
--- a/benchmark/bm_app_raise.rb
+++ b/benchmark/app_raise.rb
diff --git a/benchmark/bm_app_strconcat.rb b/benchmark/app_strconcat.rb
index 7eed7c1aed..7eed7c1aed 100644
--- a/benchmark/bm_app_strconcat.rb
+++ b/benchmark/app_strconcat.rb
diff --git a/benchmark/bm_app_tak.rb b/benchmark/app_tak.rb
index efe5380f4e..efe5380f4e 100644
--- a/benchmark/bm_app_tak.rb
+++ b/benchmark/app_tak.rb
diff --git a/benchmark/bm_app_tarai.rb b/benchmark/app_tarai.rb
index 4c146f5ccf..4c146f5ccf 100644
--- a/benchmark/bm_app_tarai.rb
+++ b/benchmark/app_tarai.rb
diff --git a/benchmark/bm_app_uri.rb b/benchmark/app_uri.rb
index 586edfd5dc..586edfd5dc 100644
--- a/benchmark/bm_app_uri.rb
+++ b/benchmark/app_uri.rb
diff --git a/benchmark/array_sample_100k_10.rb b/benchmark/array_sample_100k_10.rb
new file mode 100644
index 0000000000..5f41ecc32b
--- /dev/null
+++ b/benchmark/array_sample_100k_10.rb
@@ -0,0 +1,2 @@
+arr = [*0...100000]
+10_000.times {arr.sample 10}
diff --git a/benchmark/array_sample_100k_11.rb b/benchmark/array_sample_100k_11.rb
new file mode 100644
index 0000000000..18b1715319
--- /dev/null
+++ b/benchmark/array_sample_100k_11.rb
@@ -0,0 +1,2 @@
+arr = [*0...100000]
+10_000.times {arr.sample 11}
diff --git a/benchmark/array_sample_100k__100.rb b/benchmark/array_sample_100k__100.rb
new file mode 100644
index 0000000000..22863afe89
--- /dev/null
+++ b/benchmark/array_sample_100k__100.rb
@@ -0,0 +1,2 @@
+arr = [*0...100000]
+10_000.times {arr.sample 100}
diff --git a/benchmark/array_sample_100k__1k.rb b/benchmark/array_sample_100k__1k.rb
new file mode 100644
index 0000000000..4cd79e6c67
--- /dev/null
+++ b/benchmark/array_sample_100k__1k.rb
@@ -0,0 +1,2 @@
+arr = [*0...100000]
+10_000.times {arr.sample 1000}
diff --git a/benchmark/array_sample_100k__6k.rb b/benchmark/array_sample_100k__6k.rb
new file mode 100644
index 0000000000..b3d264249e
--- /dev/null
+++ b/benchmark/array_sample_100k__6k.rb
@@ -0,0 +1,2 @@
+arr = [*0...100000]
+10_000.times {arr.sample 6000}
diff --git a/benchmark/array_sample_100k___10k.rb b/benchmark/array_sample_100k___10k.rb
new file mode 100644
index 0000000000..5dd55ec058
--- /dev/null
+++ b/benchmark/array_sample_100k___10k.rb
@@ -0,0 +1,2 @@
+arr = [*0...100000]
+10_000.times {arr.sample 10_000}
diff --git a/benchmark/array_sample_100k___50k.rb b/benchmark/array_sample_100k___50k.rb
new file mode 100644
index 0000000000..1506732c3c
--- /dev/null
+++ b/benchmark/array_sample_100k___50k.rb
@@ -0,0 +1,2 @@
+arr = [*0...100000]
+10_000.times {arr.sample 50_000}
diff --git a/benchmark/bm_array_shift.rb b/benchmark/array_shift.rb
index 798bb9e3f4..798bb9e3f4 100644
--- a/benchmark/bm_array_shift.rb
+++ b/benchmark/array_shift.rb
diff --git a/benchmark/array_small_and.rb b/benchmark/array_small_and.rb
new file mode 100644
index 0000000000..e53a6edae6
--- /dev/null
+++ b/benchmark/array_small_and.rb
@@ -0,0 +1,17 @@
+MIN_SIZE = ENV.fetch('SMALL_ARRAY_MIN', 0).to_i
+MAX_SIZE = ENV.fetch('SMALL_ARRAY_MAX', 16).to_i
+ITERATIONS = ENV.fetch('SMALL_ARRAY_ITERATIONS', 100).to_i
+
+ARRAYS = (MIN_SIZE..MAX_SIZE).map do |size1|
+ (MIN_SIZE..MAX_SIZE).map do |size2|
+ [Array.new(size1) { rand(MAX_SIZE) }, Array.new(size2) { rand(MAX_SIZE) }]
+ end
+end
+
+ITERATIONS.times do
+ ARRAYS.each do |group|
+ group.each do |arr1, arr2|
+ arr1 & arr2
+ end
+ end
+end
diff --git a/benchmark/array_small_diff.rb b/benchmark/array_small_diff.rb
new file mode 100644
index 0000000000..9661ee48db
--- /dev/null
+++ b/benchmark/array_small_diff.rb
@@ -0,0 +1,17 @@
+MIN_SIZE = ENV.fetch('SMALL_ARRAY_MIN', 0).to_i
+MAX_SIZE = ENV.fetch('SMALL_ARRAY_MAX', 16).to_i
+ITERATIONS = ENV.fetch('SMALL_ARRAY_ITERATIONS', 100).to_i
+
+ARRAYS = (MIN_SIZE..MAX_SIZE).map do |size1|
+ (MIN_SIZE..MAX_SIZE).map do |size2|
+ [Array.new(size1) { rand(MAX_SIZE) }, Array.new(size2) { rand(MAX_SIZE) }]
+ end
+end
+
+ITERATIONS.times do
+ ARRAYS.each do |group|
+ group.each do |arr1, arr2|
+ arr1 - arr2
+ end
+ end
+end
diff --git a/benchmark/array_small_or.rb b/benchmark/array_small_or.rb
new file mode 100644
index 0000000000..c58b5fd1ff
--- /dev/null
+++ b/benchmark/array_small_or.rb
@@ -0,0 +1,17 @@
+MIN_SIZE = ENV.fetch('SMALL_ARRAY_MIN', 0).to_i
+MAX_SIZE = ENV.fetch('SMALL_ARRAY_MAX', 16).to_i
+ITERATIONS = ENV.fetch('SMALL_ARRAY_ITERATIONS', 100).to_i
+
+ARRAYS = (MIN_SIZE..MAX_SIZE).map do |size1|
+ (MIN_SIZE..MAX_SIZE).map do |size2|
+ [Array.new(size1) { rand(MAX_SIZE) }, Array.new(size2) { rand(MAX_SIZE) }]
+ end
+end
+
+ITERATIONS.times do
+ ARRAYS.each do |group|
+ group.each do |arr1, arr2|
+ arr1 | arr2
+ end
+ end
+end
diff --git a/benchmark/array_sort_block.rb b/benchmark/array_sort_block.rb
new file mode 100644
index 0000000000..3579786056
--- /dev/null
+++ b/benchmark/array_sort_block.rb
@@ -0,0 +1,2 @@
+ary = Array.new(1000) { rand(1000) }
+10000.times { ary.sort { |a, b| a <=> b } }
diff --git a/benchmark/array_sort_float.rb b/benchmark/array_sort_float.rb
new file mode 100644
index 0000000000..9a6e2f8bd2
--- /dev/null
+++ b/benchmark/array_sort_float.rb
@@ -0,0 +1,2 @@
+arr = Array.new(1000) { rand }
+10000.times { arr.sort }
diff --git a/benchmark/array_values_at_int.rb b/benchmark/array_values_at_int.rb
new file mode 100644
index 0000000000..6cb394cb9f
--- /dev/null
+++ b/benchmark/array_values_at_int.rb
@@ -0,0 +1,2 @@
+ary = Array.new(10000) {|i| i}
+100000.times { ary.values_at(500) }
diff --git a/benchmark/array_values_at_range.rb b/benchmark/array_values_at_range.rb
new file mode 100644
index 0000000000..5b53806d1c
--- /dev/null
+++ b/benchmark/array_values_at_range.rb
@@ -0,0 +1,2 @@
+ary = Array.new(10000) {|i| i}
+100000.times { ary.values_at(1..2000) }
diff --git a/benchmark/bm_bighash.rb b/benchmark/bighash.rb
index e2ad5a5c94..e2ad5a5c94 100644
--- a/benchmark/bm_bighash.rb
+++ b/benchmark/bighash.rb
diff --git a/benchmark/bm_app_erb.rb b/benchmark/bm_app_erb.rb
deleted file mode 100644
index 77c66a7949..0000000000
--- a/benchmark/bm_app_erb.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# Create many HTML strings with ERB.
-#
-
-require 'erb'
-
-data = DATA.read
-max = 15_000
-title = "hello world!"
-content = "hello world!\n" * 10
-
-max.times{
- ERB.new(data).result(binding)
-}
-
-__END__
-
-<html>
- <head> <%= title %> </head>
- <body>
- <h1> <%= title %> </h1>
- <p>
- <%= content %>
- </p>
- </body>
-</html>
diff --git a/benchmark/bm_app_pentomino.rb b/benchmark/bm_app_pentomino.rb
deleted file mode 100644
index 59c63f358e..0000000000
--- a/benchmark/bm_app_pentomino.rb
+++ /dev/null
@@ -1,259 +0,0 @@
-#!/usr/local/bin/ruby
-# This program is contributed by Shin Nishiyama
-
-
-# modified by K.Sasada
-
-NP = 5
-ROW = 8 + NP
-COL = 8
-
-$p = []
-$b = []
-$no = 0
-
-def piece(n, a, nb)
- nb.each{|x|
- a[n] = x
- if n == NP-1
- $p << [a.sort]
- else
- nbc=nb.dup
- [-ROW, -1, 1, ROW].each{|d|
- if x+d > 0 and not a.include?(x+d) and not nbc.include?(x+d)
- nbc << x+d
- end
- }
- nbc.delete x
- piece(n+1,a[0..n],nbc)
- end
- }
-end
-
-def kikaku(a)
- a.collect {|x| x - a[0]}
-end
-def ud(a)
- kikaku(a.collect {|x| ((x+NP)%ROW)-ROW*((x+NP)/ROW) }.sort)
-end
-def rl(a)
- kikaku(a.collect {|x| ROW*((x+NP)/ROW)+ROW-((x+NP)%ROW)}.sort)
-end
-def xy(a)
- kikaku(a.collect {|x| ROW*((x+NP)%ROW) + (x+NP)/ROW }.sort)
-end
-
-def mkpieces
- piece(0,[],[0])
- $p.each do |a|
- a0 = a[0]
- a[1] = ud(a0)
- a[2] = rl(a0)
- a[3] = ud(rl(a0))
- a[4] = xy(a0)
- a[5] = ud(xy(a0))
- a[6] = rl(xy(a0))
- a[7] = ud(rl(xy(a0)))
- a.sort!
- a.uniq!
- end
- $p.uniq!.sort! {|x,y| x[0] <=> y[0] }
-end
-
-def mkboard
- (0...ROW*COL).each{|i|
- if i % ROW >= ROW-NP
- $b[i] = -2
- else
- $b[i] = -1
- end
- $b[3*ROW+3]=$b[3*ROW+4]=$b[4*ROW+3]=$b[4*ROW+4]=-2
- }
-end
-
-def pboard
- return # skip print
- print "No. #$no\n"
- (0...COL).each{|i|
- print "|"
- (0...ROW-NP).each{|j|
- x = $b[i*ROW+j]
- if x < 0
- print "..|"
- else
- printf "%2d|",x+1
- end
- }
- print "\n"
- }
- print "\n"
-end
-
-$pnum=[]
-def setpiece(a,pos)
- if a.length == $p.length then
- $no += 1
- pboard
- return
- end
- while $b[pos] != -1
- pos += 1
- end
- ($pnum - a).each do |i|
- $p[i].each do |x|
- f = 0
- x.each{|s|
- if $b[pos+s] != -1
- f=1
- break
- end
- }
- if f == 0 then
- x.each{|s|
- $b[pos+s] = i
- }
- a << i
- setpiece(a.dup, pos)
- a.pop
- x.each{|s|
- $b[pos+s] = -1
- }
- end
- end
- end
-end
-
-mkpieces
-mkboard
-$p[4] = [$p[4][0]]
-$pnum = (0...$p.length).to_a
-setpiece([],0)
-
-
-__END__
-
-# original
-
-NP = 5
-ROW = 8 + NP
-COL = 8
-
-$p = []
-$b = []
-$no = 0
-
-def piece(n,a,nb)
- for x in nb
- a[n] = x
- if n == NP-1
- $p << [a.sort]
- else
- nbc=nb.dup
- for d in [-ROW, -1, 1, ROW]
- if x+d > 0 and not a.include?(x+d) and not nbc.include?(x+d)
- nbc << x+d
- end
- end
- nbc.delete x
- piece(n+1,a[0..n],nbc)
- end
- end
-end
-
-def kikaku(a)
- a.collect {|x| x - a[0]}
-end
-def ud(a)
- kikaku(a.collect {|x| ((x+NP)%ROW)-ROW*((x+NP)/ROW) }.sort)
-end
-def rl(a)
- kikaku(a.collect {|x| ROW*((x+NP)/ROW)+ROW-((x+NP)%ROW)}.sort)
-end
-def xy(a)
- kikaku(a.collect {|x| ROW*((x+NP)%ROW) + (x+NP)/ROW }.sort)
-end
-
-def mkpieces
- piece(0,[],[0])
- $p.each do |a|
- a0 = a[0]
- a[1] = ud(a0)
- a[2] = rl(a0)
- a[3] = ud(rl(a0))
- a[4] = xy(a0)
- a[5] = ud(xy(a0))
- a[6] = rl(xy(a0))
- a[7] = ud(rl(xy(a0)))
- a.sort!
- a.uniq!
- end
- $p.uniq!.sort! {|x,y| x[0] <=> y[0] }
-end
-
-def mkboard
- for i in 0...ROW*COL
- if i % ROW >= ROW-NP
- $b[i] = -2
- else
- $b[i] = -1
- end
- $b[3*ROW+3]=$b[3*ROW+4]=$b[4*ROW+3]=$b[4*ROW+4]=-2
- end
-end
-
-def pboard
- print "No. #$no\n"
- for i in 0...COL
- print "|"
- for j in 0...ROW-NP
- x = $b[i*ROW+j]
- if x < 0
- print "..|"
- else
- printf "%2d|",x+1
- end
- end
- print "\n"
- end
- print "\n"
-end
-
-$pnum=[]
-def setpiece(a,pos)
- if a.length == $p.length then
- $no += 1
- pboard
- return
- end
- while $b[pos] != -1
- pos += 1
- end
- ($pnum - a).each do |i|
- $p[i].each do |x|
- f = 0
- for s in x do
- if $b[pos+s] != -1
- f=1
- break
- end
- end
- if f == 0 then
- for s in x do
- $b[pos+s] = i
- end
- a << i
- setpiece(a.dup, pos)
- a.pop
- for s in x do
- $b[pos+s] = -1
- end
- end
- end
- end
-end
-
-mkpieces
-mkboard
-$p[4] = [$p[4][0]]
-$pnum = (0...$p.length).to_a
-setpiece([],0)
diff --git a/benchmark/bm_require.rb b/benchmark/bm_require.rb
deleted file mode 100644
index b8abc88f41..0000000000
--- a/benchmark/bm_require.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-$:.push File.join(File.dirname(__FILE__), "bm_require.data")
-
-1.upto(10000) do |i|
- require "c#{i}"
-end
-
-$:.pop
diff --git a/benchmark/bm_require_thread.rb b/benchmark/bm_require_thread.rb
deleted file mode 100644
index e54db6c6e5..0000000000
--- a/benchmark/bm_require_thread.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-$:.push File.join(File.dirname(__FILE__), "bm_require.data")
-
-i=0
-t = Thread.new do
- while true
- i = i+1 # dummy loop
- end
-end
-
-1.upto(100) do |i|
- require "c#{i}"
-end
-
-$:.pop
-t.kill
diff --git a/benchmark/bm_so_count_words.rb b/benchmark/bm_so_count_words.rb
deleted file mode 100644
index 65f6337a4a..0000000000
--- a/benchmark/bm_so_count_words.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/ruby
-# -*- mode: ruby -*-
-# $Id: wc-ruby.code,v 1.4 2004/11/13 07:43:32 bfulgham Exp $
-# http://www.bagley.org/~doug/shootout/
-# with help from Paul Brannan
-
-input = open(File.join(File.dirname($0), 'wc.input'), 'rb')
-
-nl = nw = nc = 0
-while true
- tmp = input.read(4096) or break
- data = tmp << (input.gets || "")
- nc += data.length
- nl += data.count("\n")
- ((data.strip! || data).tr!("\n", " ") || data).squeeze!
- nw += data.count(" ") + 1
-end
-# STDERR.puts "#{nl} #{nw} #{nc}"
-
diff --git a/benchmark/bm_so_fasta.rb b/benchmark/bm_so_fasta.rb
deleted file mode 100644
index 3f759ba7ae..0000000000
--- a/benchmark/bm_so_fasta.rb
+++ /dev/null
@@ -1,81 +0,0 @@
-# The Computer Language Shootout
-# http://shootout.alioth.debian.org/
-# Contributed by Sokolov Yura
-
-$last = 42.0
-def gen_random (max,im=139968,ia=3877,ic=29573)
- (max * ($last = ($last * ia + ic) % im)) / im
-end
-
-alu =
- "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG"+
- "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA"+
- "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT"+
- "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA"+
- "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG"+
- "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC"+
- "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA"
-
-iub = [
- ["a", 0.27],
- ["c", 0.12],
- ["g", 0.12],
- ["t", 0.27],
-
- ["B", 0.02],
- ["D", 0.02],
- ["H", 0.02],
- ["K", 0.02],
- ["M", 0.02],
- ["N", 0.02],
- ["R", 0.02],
- ["S", 0.02],
- ["V", 0.02],
- ["W", 0.02],
- ["Y", 0.02],
-]
-homosapiens = [
- ["a", 0.3029549426680],
- ["c", 0.1979883004921],
- ["g", 0.1975473066391],
- ["t", 0.3015094502008],
-]
-
-def make_repeat_fasta(id, desc, src, n)
- puts ">#{id} #{desc}"
- v = nil
- width = 60
- l = src.length
- s = src * ((n / l) + 1)
- s.slice!(n, l)
- puts(s.scan(/.{1,#{width}}/).join("\n"))
-end
-
-def make_random_fasta(id, desc, table, n)
- puts ">#{id} #{desc}"
- rand, v = nil,nil
- width = 60
- chunk = 1 * width
- prob = 0.0
- table.each{|v| v[1]= (prob += v[1])}
- for i in 1..(n/width)
- puts((1..width).collect{
- rand = gen_random(1.0)
- table.find{|v| v[1]>rand}[0]
- }.join)
- end
- if n%width != 0
- puts((1..(n%width)).collect{
- rand = gen_random(1.0)
- table.find{|v| v[1]>rand}[0]
- }.join)
- end
-end
-
-
-n = (ARGV[0] or 250_000).to_i
-
-make_repeat_fasta('ONE', 'Homo sapiens alu', alu, n*2)
-make_random_fasta('TWO', 'IUB ambiguity codes', iub, n*3)
-make_random_fasta('THREE', 'Homo sapiens frequency', homosapiens, n*5)
-
diff --git a/benchmark/bm_so_k_nucleotide.rb b/benchmark/bm_so_k_nucleotide.rb
deleted file mode 100644
index dadab3e79c..0000000000
--- a/benchmark/bm_so_k_nucleotide.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-# The Computer Language Shootout
-# http://shootout.alioth.debian.org
-#
-# contributed by jose fco. gonzalez
-# modified by Sokolov Yura
-
-seq = String.new
-
-def frecuency( seq,length )
- n, table = seq.length - length + 1, Hash.new(0)
- f, i = nil, nil
- (0 ... length).each do |f|
- (f ... n).step(length) do |i|
- table[seq[i,length]] += 1
- end
- end
- [n,table]
-
-end
-
-def sort_by_freq( seq,length )
- n,table = frecuency( seq,length )
- a, b, v = nil, nil, nil
- table.sort{|a,b| b[1] <=> a[1]}.each do |v|
- puts "%s %.3f" % [v[0].upcase,((v[1]*100).to_f/n)]
- end
- puts
-end
-
-def find_seq( seq,s )
- n,table = frecuency( seq,s.length )
- puts "#{table[s].to_s}\t#{s.upcase}"
-end
-
-input = open(File.join(File.dirname($0), 'fasta.output.100000'), 'rb')
-
-line = input.gets while line !~ /^>THREE/
-line = input.gets
-
-while (line !~ /^>/) & line do
- seq << line.chomp
- line = input.gets
-end
-
-[1,2].each {|i| sort_by_freq( seq,i ) }
-
-%w(ggt ggta ggtatt ggtattttaatt ggtattttaatttatagt).each{|s| find_seq( seq,s) }
-
diff --git a/benchmark/bm_so_meteor_contest.rb b/benchmark/bm_so_meteor_contest.rb
deleted file mode 100644
index 64a7d46afe..0000000000
--- a/benchmark/bm_so_meteor_contest.rb
+++ /dev/null
@@ -1,563 +0,0 @@
-#!/usr/bin/env ruby
-#
-# The Computer Language Shootout
-# http://shootout.alioth.debian.org
-# contributed by Kevin Barnes (Ruby novice)
-
-# PROGRAM: the main body is at the bottom.
-# 1) read about the problem here: http://www-128.ibm.com/developerworks/java/library/j-javaopt/
-# 2) see how I represent a board as a bitmask by reading the blank_board comments
-# 3) read as your mental paths take you
-
-def print *args
-end
-
-# class to represent all information about a particular rotation of a particular piece
-class Rotation
- # an array (by location) containing a bit mask for how the piece maps at the given location.
- # if the rotation is invalid at that location the mask will contain false
- attr_reader :start_masks
-
- # maps a direction to a relative location. these differ depending on whether it is an even or
- # odd row being mapped from
- @@rotation_even_adder = { :west => -1, :east => 1, :nw => -7, :ne => -6, :sw => 5, :se => 6 }
- @@rotation_odd_adder = { :west => -1, :east => 1, :nw => -6, :ne => -5, :sw => 6, :se => 7 }
-
- def initialize( directions )
- @even_offsets, @odd_offsets = normalize_offsets( get_values( directions ))
-
- @even_mask = mask_for_offsets( @even_offsets)
- @odd_mask = mask_for_offsets( @odd_offsets)
-
- @start_masks = Array.new(60)
-
- # create the rotational masks by placing the base mask at the location and seeing if
- # 1) it overlaps the boundaries and 2) it produces a prunable board. if either of these
- # is true the piece cannot be placed
- 0.upto(59) do | offset |
- mask = is_even(offset) ? (@even_mask << offset) : (@odd_mask << offset)
- if (blank_board & mask == 0 && !prunable(blank_board | mask, 0, true)) then
- imask = compute_required( mask, offset)
- @start_masks[offset] = [ mask, imask, imask | mask ]
- else
- @start_masks[offset] = false
- end
- end
- end
-
- def compute_required( mask, offset )
- board = blank_board
- 0.upto(offset) { | i | board |= 1 << i }
- board |= mask
- return 0 if (!prunable(board | mask, offset))
- board = flood_fill(board,58)
- count = 0
- imask = 0
- 0.upto(59) do | i |
- if (board[i] == 0) then
- imask |= (1 << i)
- count += 1
- end
- end
- (count > 0 && count < 5) ? imask : 0
- end
-
- def flood_fill( board, location)
- return board if (board[location] == 1)
- board |= 1 << location
- row, col = location.divmod(6)
- board = flood_fill( board, location - 1) if (col > 0)
- board = flood_fill( board, location + 1) if (col < 4)
- if (row % 2 == 0) then
- board = flood_fill( board, location - 7) if (col > 0 && row > 0)
- board = flood_fill( board, location - 6) if (row > 0)
- board = flood_fill( board, location + 6) if (row < 9)
- board = flood_fill( board, location + 5) if (col > 0 && row < 9)
- else
- board = flood_fill( board, location - 5) if (col < 4 && row > 0)
- board = flood_fill( board, location - 6) if (row > 0)
- board = flood_fill( board, location + 6) if (row < 9)
- board = flood_fill( board, location + 7) if (col < 4 && row < 9)
- end
- board
- end
-
- # given a location, produces a list of relative locations covered by the piece at this rotation
- def offsets( location)
- if is_even( location) then
- @even_offsets.collect { | value | value + location }
- else
- @odd_offsets.collect { | value | value + location }
- end
- end
-
- # returns a set of offsets relative to the top-left most piece of the rotation (by even or odd rows)
- # this is hard to explain. imagine we have this partial board:
- # 0 0 0 0 0 x [positions 0-5]
- # 0 0 1 1 0 x [positions 6-11]
- # 0 0 1 0 0 x [positions 12-17]
- # 0 1 0 0 0 x [positions 18-23]
- # 0 1 0 0 0 x [positions 24-29]
- # 0 0 0 0 0 x [positions 30-35]
- # ...
- # The top-left of the piece is at position 8, the
- # board would be passed as a set of positions (values array) containing [8,9,14,19,25] not necessarily in that
- # sorted order. Since that array starts on an odd row, the offsets for an odd row are: [0,1,6,11,17] obtained
- # by subtracting 8 from everything. Now imagine the piece shifted up and to the right so it's on an even row:
- # 0 0 0 1 1 x [positions 0-5]
- # 0 0 1 0 0 x [positions 6-11]
- # 0 0 1 0 0 x [positions 12-17]
- # 0 1 0 0 0 x [positions 18-23]
- # 0 0 0 0 0 x [positions 24-29]
- # 0 0 0 0 0 x [positions 30-35]
- # ...
- # Now the positions are [3,4,8,14,19] which after subtracting the lowest value (3) gives [0,1,5,11,16] thus, the
- # offsets for this particular piece are (in even, odd order) [0,1,5,11,16],[0,1,6,11,17] which is what
- # this function would return
- def normalize_offsets( values)
- min = values.min
- even_min = is_even(min)
- other_min = even_min ? min + 6 : min + 7
- other_values = values.collect do | value |
- if is_even(value) then
- value + 6 - other_min
- else
- value + 7 - other_min
- end
- end
- values.collect! { | value | value - min }
-
- if even_min then
- [values, other_values]
- else
- [other_values, values]
- end
- end
-
- # produce a bitmask representation of an array of offset locations
- def mask_for_offsets( offsets )
- mask = 0
- offsets.each { | value | mask = mask + ( 1 << value ) }
- mask
- end
-
- # finds a "safe" position that a position as described by a list of directions can be placed
- # without falling off any edge of the board. the values returned a location to place the first piece
- # at so it will fit after making the described moves
- def start_adjust( directions )
- south = east = 0;
- directions.each do | direction |
- east += 1 if ( direction == :sw || direction == :nw || direction == :west )
- south += 1 if ( direction == :nw || direction == :ne )
- end
- south * 6 + east
- end
-
- # given a set of directions places the piece (as defined by a set of directions) on the board at
- # a location that will not take it off the edge
- def get_values ( directions )
- start = start_adjust(directions)
- values = [ start ]
- directions.each do | direction |
- if (start % 12 >= 6) then
- start += @@rotation_odd_adder[direction]
- else
- start += @@rotation_even_adder[direction]
- end
- values += [ start ]
- end
-
- # some moves take you back to an existing location, we'll strip duplicates
- values.uniq
- end
-end
-
-# describes a piece and caches information about its rotations to as to be efficient for iteration
-# ATTRIBUTES:
-# rotations -- all the rotations of the piece
-# type -- a numeic "name" of the piece
-# masks -- an array by location of all legal rotational masks (a n inner array) for that location
-# placed -- the mask that this piece was last placed at (not a location, but the actual mask used)
-class Piece
- attr_reader :rotations, :type, :masks
- attr_accessor :placed
-
- # transform hashes that change one direction into another when you either flip or rotate a set of directions
- @@flip_converter = { :west => :west, :east => :east, :nw => :sw, :ne => :se, :sw => :nw, :se => :ne }
- @@rotate_converter = { :west => :nw, :east => :se, :nw => :ne, :ne => :east, :sw => :west, :se => :sw }
-
- def initialize( directions, type )
- @type = type
- @rotations = Array.new();
- @map = {}
-
- generate_rotations( directions )
- directions.collect! { | value | @@flip_converter[value] }
- generate_rotations( directions )
-
- # creates the masks AND a map that returns [location, rotation] for any given mask
- # this is used when a board is found and we want to draw it, otherwise the map is unused
- @masks = Array.new();
- 0.upto(59) do | i |
- even = true
- @masks[i] = @rotations.collect do | rotation |
- mask = rotation.start_masks[i]
- @map[mask[0]] = [ i, rotation ] if (mask)
- mask || nil
- end
- @masks[i].compact!
- end
- end
-
- # rotates a set of directions through all six angles and adds a Rotation to the list for each one
- def generate_rotations( directions )
- 6.times do
- rotations.push( Rotation.new(directions))
- directions.collect! { | value | @@rotate_converter[value] }
- end
- end
-
- # given a board string, adds this piece to the board at whatever location/rotation
- # important: the outbound board string is 5 wide, the normal location notation is six wide (padded)
- def fill_string( board_string)
- location, rotation = @map[@placed]
- rotation.offsets(location).each do | offset |
- row, col = offset.divmod(6)
- board_string[ row*5 + col, 1 ] = @type.to_s
- end
- end
-end
-
-# a blank bit board having this form:
-#
-# 0 0 0 0 0 1
-# 0 0 0 0 0 1
-# 0 0 0 0 0 1
-# 0 0 0 0 0 1
-# 0 0 0 0 0 1
-# 0 0 0 0 0 1
-# 0 0 0 0 0 1
-# 0 0 0 0 0 1
-# 0 0 0 0 0 1
-# 0 0 0 0 0 1
-# 1 1 1 1 1 1
-#
-# where left lest significant bit is the top left and the most significant is the lower right
-# the actual board only consists of the 0 places, the 1 places are blockers to keep things from running
-# off the edges or bottom
-def blank_board
- 0b111111100000100000100000100000100000100000100000100000100000100000
-end
-
-def full_board
- 0b111111111111111111111111111111111111111111111111111111111111111111
-end
-
-# determines if a location (bit position) is in an even row
-def is_even( location)
- (location % 12) < 6
-end
-
-# support function that create three utility maps:
-# $converter -- for each row an array that maps a five bit row (via array mapping)
-# to the a five bit representation of the bits below it
-# $bit_count -- maps a five bit row (via array mapping) to the number of 1s in the row
-# @@new_regions -- maps a five bit row (via array mapping) to an array of "region" arrays
-# a region array has three values the first is a mask of bits in the region,
-# the second is the count of those bits and the third is identical to the first
-# examples:
-# 0b10010 => [ 0b01100, 2, 0b01100 ], [ 0b00001, 1, 0b00001]
-# 0b01010 => [ 0b10000, 1, 0b10000 ], [ 0b00100, 1, 0b00100 ], [ 0b00001, 1, 0b00001]
-# 0b10001 => [ 0b01110, 3, 0b01110 ]
-def create_collector_support
- odd_map = [0b11, 0b110, 0b1100, 0b11000, 0b10000]
- even_map = [0b1, 0b11, 0b110, 0b1100, 0b11000]
-
- all_odds = Array.new(0b100000)
- all_evens = Array.new(0b100000)
- bit_counts = Array.new(0b100000)
- new_regions = Array.new(0b100000)
- 0.upto(0b11111) do | i |
- bit_count = odd = even = 0
- 0.upto(4) do | bit |
- if (i[bit] == 1) then
- bit_count += 1
- odd |= odd_map[bit]
- even |= even_map[bit]
- end
- end
- all_odds[i] = odd
- all_evens[i] = even
- bit_counts[i] = bit_count
- new_regions[i] = create_regions( i)
- end
-
- $converter = []
- 10.times { | row | $converter.push((row % 2 == 0) ? all_evens : all_odds) }
- $bit_counts = bit_counts
- $regions = new_regions.collect { | set | set.collect { | value | [ value, bit_counts[value], value] } }
-end
-
-# determines if a board is punable, meaning that there is no possibility that it
-# can be filled up with pieces. A board is prunable if there is a grouping of unfilled spaces
-# that are not a multiple of five. The following board is an example of a prunable board:
-# 0 0 1 0 0
-# 0 1 0 0 0
-# 1 1 0 0 0
-# 0 1 0 0 0
-# 0 0 0 0 0
-# ...
-#
-# This board is prunable because the top left corner is only 3 bits in area, no piece will ever fit it
-# parameters:
-# board -- an initial bit board (6 bit padded rows, see blank_board for format)
-# location -- starting location, everything above and to the left is already full
-# slotting -- set to true only when testing initial pieces, when filling normally
-# additional assumptions are possible
-#
-# Algorithm:
-# The algorithm starts at the top row (as determined by location) and iterates a row at a time
-# maintainng counts of active open areas (kept in the collector array) each collector contains
-# three values at the start of an iteration:
-# 0: mask of bits that would be adjacent to the collector in this row
-# 1: the number of bits collected so far
-# 2: a scratch space starting as zero, but used during the computation to represent
-# the empty bits in the new row that are adjacent (position 0)
-# The exact procedure is described in-code
-def prunable( board, location, slotting = false)
- collectors = []
- # loop across the rows
- (location / 6).to_i.upto(9) do | row_on |
- # obtain a set of regions representing the bits of the current row.
- regions = $regions[(board >> (row_on * 6)) & 0b11111]
- converter = $converter[row_on]
-
- # track the number of collectors at the start of the cycle so that
- # we don't compute against newly created collectors, only existing collectors
- initial_collector_count = collectors.length
-
- # loop against the regions. For each region of the row
- # we will see if it connects to one or more existing collectors.
- # if it connects to 1 collector, the bits from the region are added to the
- # bits of the collector and the mask is placed in collector[2]
- # If the region overlaps more than one collector then all the collectors
- # it overlaps with are merged into the first one (the others are set to nil in the array)
- # if NO collectors are found then the region is copied as a new collector
- regions.each do | region |
- collector_found = nil
- region_mask = region[2]
- initial_collector_count.times do | collector_num |
- collector = collectors[collector_num]
- if (collector) then
- collector_mask = collector[0]
- if (collector_mask & region_mask != 0) then
- if (collector_found) then
- collector_found[0] |= collector_mask
- collector_found[1] += collector[1]
- collector_found[2] |= collector[2]
- collectors[collector_num] = nil
- else
- collector_found = collector
- collector[1] += region[1]
- collector[2] |= region_mask
- end
- end
- end
- end
- if (collector_found == nil) then
- collectors.push(Array.new(region))
- end
- end
-
- # check the existing collectors, if any collector overlapped no bits in the region its [2] value will
- # be zero. The size of any such reaason is tested if it is not a multiple of five true is returned since
- # the board is prunable. if it is a multiple of five it is removed.
- # Collector that are still active have a new adjacent value [0] set based n the matched bits
- # and have [2] cleared out for the next cycle.
- collectors.length.times do | collector_num |
- collector = collectors[collector_num]
- if (collector) then
- if (collector[2] == 0) then
- return true if (collector[1] % 5 != 0)
- collectors[collector_num] = nil
- else
- # if a collector matches all bits in the row then we can return unprunable early for the
- # following reasons:
- # 1) there can be no more unavailable bits bince we fill from the top left downward
- # 2) all previous regions have been closed or joined so only this region can fail
- # 3) this region must be good since there can never be only 1 region that is nuot
- # a multiple of five
- # this rule only applies when filling normally, so we ignore the rule if we are "slotting"
- # in pieces to see what configurations work for them (the only other time this algorithm is used).
- return false if (collector[2] == 0b11111 && !slotting)
- collector[0] = converter[collector[2]]
- collector[2] = 0
- end
- end
- end
-
- # get rid of all the empty converters for the next round
- collectors.compact!
- end
- return false if (collectors.length <= 1) # 1 collector or less and the region is fine
- collectors.any? { | collector | (collector[1] % 5) != 0 } # more than 1 and we test them all for bad size
-end
-
-# creates a region given a row mask. see prunable for what a "region" is
-def create_regions( value )
- regions = []
- cur_region = 0
- 5.times do | bit |
- if (value[bit] == 0) then
- cur_region |= 1 << bit
- else
- if (cur_region != 0 ) then
- regions.push( cur_region)
- cur_region = 0;
- end
- end
- end
- regions.push(cur_region) if (cur_region != 0)
- regions
-end
-
-# find up to the counted number of solutions (or all solutions) and prints the final result
-def find_all
- find_top( 1)
- find_top( 0)
- print_results
-end
-
-# show the board
-def print_results
- print "#{@boards_found} solutions found\n\n"
- print_full_board( @min_board)
- print "\n"
- print_full_board( @max_board)
- print "\n"
-end
-
-# finds solutions. This special version of the main function is only used for the top level
-# the reason for it is basically to force a particular ordering on how the rotations are tested for
-# the first piece. It is called twice, first looking for placements of the odd rotations and then
-# looking for placements of the even locations.
-#
-# WHY?
-# Since any found solution has an inverse we want to maximize finding solutions that are not already found
-# as an inverse. The inverse will ALWAYS be 3 one of the piece configurations that is exactly 3 rotations away
-# (an odd number). Checking even vs odd then produces a higher probability of finding more pieces earlier
-# in the cycle. We still need to keep checking all the permutations, but our probability of finding one will
-# diminsh over time. Since we are TOLD how many to search for this lets us exit before checking all pieces
-# this bennifit is very great when seeking small numbers of solutions and is 0 when looking for more than the
-# maximum number
-def find_top( rotation_skip)
- board = blank_board
- (@pieces.length-1).times do
- piece = @pieces.shift
- piece.masks[0].each do | mask, imask, cmask |
- if ((rotation_skip += 1) % 2 == 0) then
- piece.placed = mask
- find( 1, 1, board | mask)
- end
- end
- @pieces.push(piece)
- end
- piece = @pieces.shift
- @pieces.push(piece)
-end
-
-# the normail find routine, iterates through the available pieces, checks all rotations at the current location
-# and adds any boards found. depth is achieved via recursion. the overall approach is described
-# here: http://www-128.ibm.com/developerworks/java/library/j-javaopt/
-# parameters:
-# start_location -- where to start looking for place for the next piece at
-# placed -- number of pieces placed
-# board -- current state of the board
-#
-# see in-code comments
-def find( start_location, placed, board)
- # find the next location to place a piece by looking for an empty bit
- while board[start_location] == 1
- start_location += 1
- end
-
- @pieces.length.times do
- piece = @pieces.shift
- piece.masks[start_location].each do | mask, imask, cmask |
- if ( board & cmask == imask) then
- piece.placed = mask
- if (placed == 9) then
- add_board
- else
- find( start_location + 1, placed + 1, board | mask)
- end
- end
- end
- @pieces.push(piece)
- end
-end
-
-# print the board
-def print_full_board( board_string)
- 10.times do | row |
- print " " if (row % 2 == 1)
- 5.times do | col |
- print "#{board_string[row*5 + col,1]} "
- end
- print "\n"
- end
-end
-
-# when a board is found we "draw it" into a string and then flip that string, adding both to
-# the list (hash) of solutions if they are unique.
-def add_board
- board_string = "99999999999999999999999999999999999999999999999999"
- @all_pieces.each { | piece | piece.fill_string( board_string ) }
- save( board_string)
- save( board_string.reverse)
-end
-
-# adds a board string to the list (if new) and updates the current best/worst board
-def save( board_string)
- if (@all_boards[board_string] == nil) then
- @min_board = board_string if (board_string < @min_board)
- @max_board = board_string if (board_string > @max_board)
- @all_boards.store(board_string,true)
- @boards_found += 1
-
- # the exit motif is a time saver. Ideally the function should return, but those tests
- # take noticeable time (performance).
- if (@boards_found == @stop_count) then
- print_results
- exit(0)
- end
- end
-end
-
-
-##
-## MAIN BODY :)
-##
-create_collector_support
-@pieces = [
- Piece.new( [ :nw, :ne, :east, :east ], 2),
- Piece.new( [ :ne, :se, :east, :ne ], 7),
- Piece.new( [ :ne, :east, :ne, :nw ], 1),
- Piece.new( [ :east, :sw, :sw, :se ], 6),
- Piece.new( [ :east, :ne, :se, :ne ], 5),
- Piece.new( [ :east, :east, :east, :se ], 0),
- Piece.new( [ :ne, :nw, :se, :east, :se ], 4),
- Piece.new( [ :se, :se, :se, :west ], 9),
- Piece.new( [ :se, :se, :east, :se ], 8),
- Piece.new( [ :east, :east, :sw, :se ], 3)
- ];
-
-@all_pieces = Array.new( @pieces)
-
-@min_board = "99999999999999999999999999999999999999999999999999"
-@max_board = "00000000000000000000000000000000000000000000000000"
-@stop_count = ARGV[0].to_i || 2089
-@all_boards = {}
-@boards_found = 0
-
-find_all ######## DO IT!!!
diff --git a/benchmark/bm_so_pidigits.rb b/benchmark/bm_so_pidigits.rb
deleted file mode 100644
index c7d6fbfb4d..0000000000
--- a/benchmark/bm_so_pidigits.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-# The Great Computer Language Shootout
-# http://shootout.alioth.debian.org/
-#
-# contributed by Gabriele Renzi
-
-class PiDigitSpigot
-
- def initialize()
- @z = Transformation.new 1,0,0,1
- @x = Transformation.new 0,0,0,0
- @inverse = Transformation.new 0,0,0,0
- end
-
- def next!
- @y = @z.extract(3)
- if safe? @y
- @z = produce(@y)
- @y
- else
- @z = consume @x.next!()
- next!()
- end
- end
-
- def safe?(digit)
- digit == @z.extract(4)
- end
-
- def produce(i)
- @inverse.qrst(10,-10*i,0,1).compose(@z)
- end
-
- def consume(a)
- @z.compose(a)
- end
-end
-
-
-class Transformation
- attr_reader :q, :r, :s, :t
- def initialize (q, r, s, t)
- @q,@r,@s,@t,@k = q,r,s,t,0
- end
-
- def next!()
- @q = @k = @k + 1
- @r = 4 * @k + 2
- @s = 0
- @t = 2 * @k + 1
- self
- end
-
- def extract(j)
- (@q * j + @r) / (@s * j + @t)
- end
-
- def compose(a)
- self.class.new( @q * a.q,
- @q * a.r + r * a.t,
- @s * a.q + t * a.s,
- @s * a.r + t * a.t
- )
- end
-
- def qrst *args
- initialize *args
- self
- end
-
-
-end
-
-
-WIDTH = 10
-n = 2_500 # Integer(ARGV[0])
-j = 0
-
-digits = PiDigitSpigot.new
-
-while n > 0
- if n >= WIDTH
- WIDTH.times {print digits.next!}
- j += WIDTH
- else
- n.times {print digits.next!}
- (WIDTH-n).times {print " "}
- j += n
- end
- puts "\t:"+j.to_s
- n -= WIDTH
-end
-
diff --git a/benchmark/bm_so_reverse_complement.rb b/benchmark/bm_so_reverse_complement.rb
deleted file mode 100644
index 82ea666994..0000000000
--- a/benchmark/bm_so_reverse_complement.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/ruby
-# The Great Computer Language Shootout
-# http://shootout.alioth.debian.org/
-#
-# Contributed by Peter Bjarke Olsen
-# Modified by Doug King
-
-seq=Array.new
-
-def revcomp(seq)
- seq.reverse!.tr!('wsatugcyrkmbdhvnATUGCYRKMBDHVN','WSTAACGRYMKVHDBNTAACGRYMKVHDBN')
- stringlen=seq.length
- 0.step(stringlen-1,60) {|x| print seq.slice(x,60) , "\n"}
-end
-
-input = open(File.join(File.dirname($0), 'fasta.output.2500000'), 'rb')
-
-while input.gets
- if $_ =~ />/
- if seq.length != 0
- revcomp(seq.join)
- seq=Array.new
- end
- puts $_
- else
- $_.sub(/\n/,'')
- seq.push $_
- end
-end
-revcomp(seq.join)
diff --git a/benchmark/bm_vm1_attr_ivar.rb b/benchmark/bm_vm1_attr_ivar.rb
deleted file mode 100644
index 16906f3605..0000000000
--- a/benchmark/bm_vm1_attr_ivar.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-class C
- attr_reader :a, :b
- def initialize
- @a = nil
- @b = nil
- end
-end
-obj = C.new
-i = 0
-while i<30_000_000 # while loop 1
- i += 1
- j = obj.a
- k = obj.b
-end
diff --git a/benchmark/bm_vm1_attr_ivar_set.rb b/benchmark/bm_vm1_attr_ivar_set.rb
deleted file mode 100644
index 7e7a6b48c0..0000000000
--- a/benchmark/bm_vm1_attr_ivar_set.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-class C
- attr_accessor :a, :b
- def initialize
- @a = nil
- @b = nil
- end
-end
-obj = C.new
-i = 0
-while i<30_000_000 # while loop 1
- i += 1
- obj.a = 1
- obj.b = 2
-end
diff --git a/benchmark/bm_vm1_block.rb b/benchmark/bm_vm1_block.rb
deleted file mode 100644
index a9f56b15ea..0000000000
--- a/benchmark/bm_vm1_block.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-def m
- yield
-end
-
-i = 0
-while i<30_000_000 # while loop 1
- i += 1
- m{
- }
-end
diff --git a/benchmark/bm_vm1_const.rb b/benchmark/bm_vm1_const.rb
deleted file mode 100644
index ac59ebccf1..0000000000
--- a/benchmark/bm_vm1_const.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-Const = 1
-
-i = 0
-while i<30_000_000 # while loop 1
- i += 1
- j = Const
- k = Const
-end
diff --git a/benchmark/bm_vm1_ensure.rb b/benchmark/bm_vm1_ensure.rb
deleted file mode 100644
index a1596145f2..0000000000
--- a/benchmark/bm_vm1_ensure.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-i = 0
-while i<30_000_000 # benchmark loop 1
- i += 1
- begin
- begin
- ensure
- end
- ensure
- end
-end
-
diff --git a/benchmark/bm_vm1_float_simple.rb b/benchmark/bm_vm1_float_simple.rb
deleted file mode 100644
index d4581439ff..0000000000
--- a/benchmark/bm_vm1_float_simple.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-i = 0.0; f = 0.0
-while i<30_000_000
- i += 1
- f += 0.1; f -= 0.1
- f += 0.1; f -= 0.1
- f += 0.1; f -= 0.1
-end
diff --git a/benchmark/bm_vm1_gc_short_lived.rb b/benchmark/bm_vm1_gc_short_lived.rb
deleted file mode 100644
index e78bca5668..0000000000
--- a/benchmark/bm_vm1_gc_short_lived.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-i = 0
-while i<30_000_000 # while loop 1
- a = '' # short-lived String
- b = ''
- c = ''
- d = ''
- e = ''
- f = ''
- i+=1
-end
diff --git a/benchmark/bm_vm1_gc_short_with_complex_long.rb b/benchmark/bm_vm1_gc_short_with_complex_long.rb
deleted file mode 100644
index b66052dee0..0000000000
--- a/benchmark/bm_vm1_gc_short_with_complex_long.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-def nested_hash h, n
- if n == 0
- ''
- else
- 10.times{
- h[Object.new] = nested_hash(h, n-1)
- }
- end
-end
-
-long_lived = Hash.new
-nested_hash long_lived, 6
-
-GC.start
-GC.start
-
-i = 0
-while i<30_000_000 # while loop 1
- a = '' # short-lived String
- b = ''
- c = ''
- d = ''
- e = ''
- f = ''
- i+=1
-end
-
diff --git a/benchmark/bm_vm1_gc_short_with_long.rb b/benchmark/bm_vm1_gc_short_with_long.rb
deleted file mode 100644
index 298dbc845b..0000000000
--- a/benchmark/bm_vm1_gc_short_with_long.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-long_lived = Array.new(1_000_000){|i| "#{i}"}
-GC.start
-GC.start
-i = 0
-while i<30_000_000 # while loop 1
- a = '' # short-lived String
- b = ''
- c = ''
- d = ''
- e = ''
- f = ''
- i+=1
-end
diff --git a/benchmark/bm_vm1_gc_short_with_symbol.rb b/benchmark/bm_vm1_gc_short_with_symbol.rb
deleted file mode 100644
index 6b15c1b7bf..0000000000
--- a/benchmark/bm_vm1_gc_short_with_symbol.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# make many symbols
-50_000.times{|i| sym = "sym#{i}".to_sym}
-GC.start
-GC.start
-
-i = 0
-while i<30_000_000 # while loop 1
- a = '' # short-lived String
- b = ''
- c = ''
- d = ''
- e = ''
- f = ''
- i+=1
-end
diff --git a/benchmark/bm_vm1_gc_wb_ary.rb b/benchmark/bm_vm1_gc_wb_ary.rb
deleted file mode 100644
index 881528845b..0000000000
--- a/benchmark/bm_vm1_gc_wb_ary.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-short_lived_ary = []
-
-if RUBY_VERSION >= "2.2.0"
- GC.start(full_mark: false, immediate_mark: true, lazy_sweep: false)
-end
-
-i = 0
-short_lived = ''
-while i<30_000_000 # while loop 1
- short_lived_ary[0] = short_lived # write barrier
- i+=1
-end
diff --git a/benchmark/bm_vm1_gc_wb_ary_promoted.rb b/benchmark/bm_vm1_gc_wb_ary_promoted.rb
deleted file mode 100644
index 3c8279c956..0000000000
--- a/benchmark/bm_vm1_gc_wb_ary_promoted.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-long_lived = []
-
-if RUBY_VERSION > "2.2.0"
- 3.times{ GC.start(full_mark: false, immediate_mark: true, lazy_sweep: false) }
-elsif
- GC.start
-end
-
-i = 0
-short_lived = ''
-while i<30_000_000 # while loop 1
- long_lived[0] = short_lived # write barrier
- i+=1
-end
diff --git a/benchmark/bm_vm1_gc_wb_obj.rb b/benchmark/bm_vm1_gc_wb_obj.rb
deleted file mode 100644
index a4067af36b..0000000000
--- a/benchmark/bm_vm1_gc_wb_obj.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-class C
- attr_accessor :foo
-end
-short_lived_obj = C.new
-
-if RUBY_VERSION >= "2.2.0"
- GC.start(full_mark: false, immediate_mark: true, lazy_sweep: false)
-end
-
-i = 0
-short_lived = ''
-while i<30_000_000 # while loop 1
- short_lived_obj.foo = short_lived # write barrier
- i+=1
-end
diff --git a/benchmark/bm_vm1_gc_wb_obj_promoted.rb b/benchmark/bm_vm1_gc_wb_obj_promoted.rb
deleted file mode 100644
index eee07a0248..0000000000
--- a/benchmark/bm_vm1_gc_wb_obj_promoted.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-class C
- attr_accessor :foo
-end
-long_lived = C.new
-
-if RUBY_VERSION >= "2.2.0"
- 3.times{ GC.start(full_mark: false, immediate_mark: true, lazy_sweep: false) }
-elsif
- GC.start
-end
-
-i = 0
-short_lived = ''
-while i<30_000_000 # while loop 1
- long_lived.foo = short_lived # write barrier
- i+=1
-end
diff --git a/benchmark/bm_vm1_ivar.rb b/benchmark/bm_vm1_ivar.rb
deleted file mode 100644
index 68a73cf92f..0000000000
--- a/benchmark/bm_vm1_ivar.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-@a = 1
-
-i = 0
-while i<30_000_000 # while loop 1
- i += 1
- j = @a
- k = @a
-end
diff --git a/benchmark/bm_vm1_ivar_set.rb b/benchmark/bm_vm1_ivar_set.rb
deleted file mode 100644
index bd81b06c34..0000000000
--- a/benchmark/bm_vm1_ivar_set.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-i = 0
-while i<30_000_000 # while loop 1
- i += 1
- @a = 1
- @b = 2
-end
diff --git a/benchmark/bm_vm1_length.rb b/benchmark/bm_vm1_length.rb
deleted file mode 100644
index 353de3ab0e..0000000000
--- a/benchmark/bm_vm1_length.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-a = 'abc'
-b = [1, 2, 3]
-i = 0
-while i<30_000_000 # while loop 1
- i += 1
- a.length
- b.length
-end
-
diff --git a/benchmark/bm_vm1_lvar_init.rb b/benchmark/bm_vm1_lvar_init.rb
deleted file mode 100644
index 36f2068811..0000000000
--- a/benchmark/bm_vm1_lvar_init.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-def m v
- unless v
- # unreachable code
- v1 = v2 = v3 = v4 = v5 = v6 = v7 = v8 = v9 = v10 =
- v11 = v12 = v13 = v14 = v15 = v16 = v17 = v18 = v19 = v20 =
- v21 = v22 = v23 = v24 = v25 = v26 = v27 = v28 = v29 = v30 =
- v31 = v32 = v33 = v34 = v35 = v36 = v37 = v38 = v39 = v40 =
- v41 = v42 = v43 = v44 = v45 = v46 = v47 = v48 = v49 = v50 = 1
- end
-end
-
-i = 0
-
-while i<30_000_000 # while loop 1
- i += 1
- m i
-end
-
diff --git a/benchmark/bm_vm1_lvar_set.rb b/benchmark/bm_vm1_lvar_set.rb
deleted file mode 100644
index 222e864134..0000000000
--- a/benchmark/bm_vm1_lvar_set.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-i = 0
-while i<30_000_000 # while loop 1
- i += 1
- a = b = c = d = e = f = g = h = j = k = l = m = n = o = p = q = r = 1
-end
diff --git a/benchmark/bm_vm1_neq.rb b/benchmark/bm_vm1_neq.rb
deleted file mode 100644
index bbb4ae07a4..0000000000
--- a/benchmark/bm_vm1_neq.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-i = 0
-obj1 = Object.new
-obj2 = Object.new
-
-while i<30_000_000 # while loop 1
- i += 1
- obj1 != obj2
-end
diff --git a/benchmark/bm_vm1_not.rb b/benchmark/bm_vm1_not.rb
deleted file mode 100644
index b09ecdcc21..0000000000
--- a/benchmark/bm_vm1_not.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-i = 0
-obj = Object.new
-
-while i<30_000_000 # while loop 1
- i += 1
- !obj
-end
diff --git a/benchmark/bm_vm1_rescue.rb b/benchmark/bm_vm1_rescue.rb
deleted file mode 100644
index b0d3e2bdfa..0000000000
--- a/benchmark/bm_vm1_rescue.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-i = 0
-while i<30_000_000 # while loop 1
- i += 1
- begin
- rescue
- end
-end
diff --git a/benchmark/bm_vm1_simplereturn.rb b/benchmark/bm_vm1_simplereturn.rb
deleted file mode 100644
index 63f9f21675..0000000000
--- a/benchmark/bm_vm1_simplereturn.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-def m
- return 1
-end
-i = 0
-while i<30_000_000 # while loop 1
- i += 1
- m
-end
-
diff --git a/benchmark/bm_vm1_swap.rb b/benchmark/bm_vm1_swap.rb
deleted file mode 100644
index 918f8b2112..0000000000
--- a/benchmark/bm_vm1_swap.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-a = 1
-b = 2
-i = 0
-while i<30_000_000 # while loop 1
- i += 1
- a, b = b, a
-end
-
diff --git a/benchmark/bm_vm1_yield.rb b/benchmark/bm_vm1_yield.rb
deleted file mode 100644
index 775597cea6..0000000000
--- a/benchmark/bm_vm1_yield.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-def m
- i = 0
- while i<30_000_000 # while loop 1
- i += 1
- yield
- end
-end
-
-m{}
-
diff --git a/benchmark/bm_vm2_array.rb b/benchmark/bm_vm2_array.rb
deleted file mode 100644
index df9037c83c..0000000000
--- a/benchmark/bm_vm2_array.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- a = [1,2,3,4,5,6,7,8,9,10]
-end
diff --git a/benchmark/bm_vm2_bigarray.rb b/benchmark/bm_vm2_bigarray.rb
deleted file mode 100644
index b02509d6a2..0000000000
--- a/benchmark/bm_vm2_bigarray.rb
+++ /dev/null
@@ -1,106 +0,0 @@
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- a = [
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- ]
-end
diff --git a/benchmark/bm_vm2_bighash.rb b/benchmark/bm_vm2_bighash.rb
deleted file mode 100644
index 5e3f437bb8..0000000000
--- a/benchmark/bm_vm2_bighash.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-i = 0
-while i<60_000 # benchmark loop 2
- i += 1
- a = {0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9, 10=>10, 11=>11, 12=>12, 13=>13, 14=>14, 15=>15, 16=>16, 17=>17, 18=>18, 19=>19, 20=>20, 21=>21, 22=>22, 23=>23, 24=>24, 25=>25, 26=>26, 27=>27, 28=>28, 29=>29, 30=>30, 31=>31, 32=>32, 33=>33, 34=>34, 35=>35, 36=>36, 37=>37, 38=>38, 39=>39, 40=>40, 41=>41, 42=>42, 43=>43, 44=>44, 45=>45, 46=>46, 47=>47, 48=>48, 49=>49, 50=>50, 51=>51, 52=>52, 53=>53, 54=>54, 55=>55, 56=>56, 57=>57, 58=>58, 59=>59, 60=>60, 61=>61, 62=>62, 63=>63, 64=>64, 65=>65, 66=>66, 67=>67, 68=>68, 69=>69, 70=>70, 71=>71, 72=>72, 73=>73, 74=>74, 75=>75, 76=>76, 77=>77, 78=>78, 79=>79, 80=>80, 81=>81, 82=>82, 83=>83, 84=>84, 85=>85, 86=>86, 87=>87, 88=>88, 89=>89, 90=>90, 91=>91, 92=>92, 93=>93, 94=>94, 95=>95, 96=>96, 97=>97, 98=>98, 99=>99, 100=>100, 101=>101, 102=>102, 103=>103, 104=>104, 105=>105, 106=>106, 107=>107, 108=>108, 109=>109, 110=>110, 111=>111, 112=>112, 113=>113, 114=>114, 115=>115, 116=>116, 117=>117, 118=>118, 119=>119, 120=>120, 121=>121, 122=>122, 123=>123, 124=>124, 125=>125, 126=>126, 127=>127, 128=>128, 129=>129, 130=>130, 131=>131, 132=>132, 133=>133, 134=>134, 135=>135, 136=>136, 137=>137, 138=>138, 139=>139, 140=>140, 141=>141, 142=>142, 143=>143, 144=>144, 145=>145, 146=>146, 147=>147, 148=>148, 149=>149, 150=>150, 151=>151, 152=>152, 153=>153, 154=>154, 155=>155, 156=>156, 157=>157, 158=>158, 159=>159, 160=>160, 161=>161, 162=>162, 163=>163, 164=>164, 165=>165, 166=>166, 167=>167, 168=>168, 169=>169, 170=>170, 171=>171, 172=>172, 173=>173, 174=>174, 175=>175, 176=>176, 177=>177, 178=>178, 179=>179, 180=>180, 181=>181, 182=>182, 183=>183, 184=>184, 185=>185, 186=>186, 187=>187, 188=>188, 189=>189, 190=>190, 191=>191, 192=>192, 193=>193, 194=>194, 195=>195, 196=>196, 197=>197, 198=>198, 199=>199, 200=>200, 201=>201, 202=>202, 203=>203, 204=>204, 205=>205, 206=>206, 207=>207, 208=>208, 209=>209, 210=>210, 211=>211, 212=>212, 213=>213, 214=>214, 215=>215, 216=>216, 217=>217, 218=>218, 219=>219, 220=>220, 221=>221, 222=>222, 223=>223, 224=>224, 225=>225, 226=>226, 227=>227, 228=>228, 229=>229, 230=>230, 231=>231, 232=>232, 233=>233, 234=>234, 235=>235, 236=>236, 237=>237, 238=>238, 239=>239, 240=>240, 241=>241, 242=>242, 243=>243, 244=>244, 245=>245, 246=>246, 247=>247, 248=>248, 249=>249, 250=>250, 251=>251, 252=>252, 253=>253, 254=>254, 255=>255, 256=>256, 257=>257, 258=>258, 259=>259, 260=>260, 261=>261, 262=>262, 263=>263, 264=>264, 265=>265, 266=>266, 267=>267, 268=>268, 269=>269, 270=>270, 271=>271, 272=>272, 273=>273, 274=>274, 275=>275, 276=>276, 277=>277, 278=>278, 279=>279, 280=>280, 281=>281, 282=>282, 283=>283, 284=>284, 285=>285, 286=>286, 287=>287, 288=>288, 289=>289, 290=>290, 291=>291, 292=>292, 293=>293, 294=>294, 295=>295, 296=>296, 297=>297, 298=>298, 299=>299, 300=>300, 301=>301, 302=>302, 303=>303, 304=>304, 305=>305, 306=>306, 307=>307, 308=>308, 309=>309, 310=>310, 311=>311, 312=>312, 313=>313, 314=>314, 315=>315, 316=>316, 317=>317, 318=>318, 319=>319, 320=>320, 321=>321, 322=>322, 323=>323, 324=>324, 325=>325, 326=>326, 327=>327, 328=>328, 329=>329, 330=>330, 331=>331, 332=>332, 333=>333, 334=>334, 335=>335, 336=>336, 337=>337, 338=>338, 339=>339, 340=>340, 341=>341, 342=>342, 343=>343, 344=>344, 345=>345, 346=>346, 347=>347, 348=>348, 349=>349, 350=>350, 351=>351, 352=>352, 353=>353, 354=>354, 355=>355, 356=>356, 357=>357, 358=>358, 359=>359, 360=>360, 361=>361, 362=>362, 363=>363, 364=>364, 365=>365, 366=>366, 367=>367, 368=>368, 369=>369, 370=>370, 371=>371, 372=>372, 373=>373, 374=>374, 375=>375, 376=>376, 377=>377, 378=>378, 379=>379, 380=>380, 381=>381, 382=>382, 383=>383, 384=>384, 385=>385, 386=>386, 387=>387, 388=>388, 389=>389, 390=>390, 391=>391, 392=>392, 393=>393, 394=>394, 395=>395, 396=>396, 397=>397, 398=>398, 399=>399, 400=>400, 401=>401, 402=>402, 403=>403, 404=>404, 405=>405, 406=>406, 407=>407, 408=>408, 409=>409, 410=>410, 411=>411, 412=>412, 413=>413, 414=>414, 415=>415, 416=>416, 417=>417, 418=>418, 419=>419, 420=>420, 421=>421, 422=>422, 423=>423, 424=>424, 425=>425, 426=>426, 427=>427, 428=>428, 429=>429, 430=>430, 431=>431, 432=>432, 433=>433, 434=>434, 435=>435, 436=>436, 437=>437, 438=>438, 439=>439, 440=>440, 441=>441, 442=>442, 443=>443, 444=>444, 445=>445, 446=>446, 447=>447, 448=>448, 449=>449, 450=>450, 451=>451, 452=>452, 453=>453, 454=>454, 455=>455, 456=>456, 457=>457, 458=>458, 459=>459, 460=>460, 461=>461, 462=>462, 463=>463, 464=>464, 465=>465, 466=>466, 467=>467, 468=>468, 469=>469, 470=>470, 471=>471, 472=>472, 473=>473, 474=>474, 475=>475, 476=>476, 477=>477, 478=>478, 479=>479, 480=>480, 481=>481, 482=>482, 483=>483, 484=>484, 485=>485, 486=>486, 487=>487, 488=>488, 489=>489, 490=>490, 491=>491, 492=>492, 493=>493, 494=>494, 495=>495, 496=>496, 497=>497, 498=>498, 499=>499, 500=>500,}
-end
diff --git a/benchmark/bm_vm2_case.rb b/benchmark/bm_vm2_case.rb
deleted file mode 100644
index adc6e4df0a..0000000000
--- a/benchmark/bm_vm2_case.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-i = 0
-while i<6_000_000 # while loop 2
- case :foo
- when :bar
- raise
- when :baz
- raise
- when :boo
- raise
- when :foo
- i += 1
- end
-end
-
diff --git a/benchmark/bm_vm2_case_lit.rb b/benchmark/bm_vm2_case_lit.rb
deleted file mode 100644
index c62b294e0e..0000000000
--- a/benchmark/bm_vm2_case_lit.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-i = 0
-@ret = [ "foo", true, false, :sym, 6, nil, 0.1, 0xffffffffffffffff ]
-def foo(i)
- @ret[i % @ret.size]
-end
-
-while i<6_000_000 # while loop 2
- case foo(i)
- when "foo" then :foo
- when true then true
- when false then false
- when :sym then :sym
- when 6 then :fix
- when nil then nil
- when 0.1 then :float
- when 0xffffffffffffffff then :big
- end
- i += 1
-end
diff --git a/benchmark/bm_vm2_defined_method.rb b/benchmark/bm_vm2_defined_method.rb
deleted file mode 100644
index 053ed6c912..0000000000
--- a/benchmark/bm_vm2_defined_method.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class Object
- define_method(:m){}
-end
-
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- m; m; m; m; m; m; m; m;
-end
diff --git a/benchmark/bm_vm2_dstr.rb b/benchmark/bm_vm2_dstr.rb
deleted file mode 100644
index 58c0f7bbc3..0000000000
--- a/benchmark/bm_vm2_dstr.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-i = 0
-x = y = 'z'
-while i<6_000_000 # benchmark loop 2
- i += 1
- str = "foo#{x}bar#{y}baz"
-end
diff --git a/benchmark/bm_vm2_eval.rb b/benchmark/bm_vm2_eval.rb
deleted file mode 100644
index 307cfc28ef..0000000000
--- a/benchmark/bm_vm2_eval.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- eval("1")
-end
-
diff --git a/benchmark/bm_vm2_method.rb b/benchmark/bm_vm2_method.rb
deleted file mode 100644
index a8ccff7138..0000000000
--- a/benchmark/bm_vm2_method.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-def m
- nil
-end
-
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- m; m; m; m; m; m; m; m;
-end
diff --git a/benchmark/bm_vm2_method_missing.rb b/benchmark/bm_vm2_method_missing.rb
deleted file mode 100644
index 2badc73101..0000000000
--- a/benchmark/bm_vm2_method_missing.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class C
- def method_missing mid
- end
-end
-
-obj = C.new
-
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- obj.m; obj.m; obj.m; obj.m; obj.m; obj.m; obj.m; obj.m;
-end
diff --git a/benchmark/bm_vm2_method_with_block.rb b/benchmark/bm_vm2_method_with_block.rb
deleted file mode 100644
index b4efb4f520..0000000000
--- a/benchmark/bm_vm2_method_with_block.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-def m
- nil
-end
-
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- m{}; m{}; m{}; m{}; m{}; m{}; m{}; m{};
-end
diff --git a/benchmark/bm_vm2_mutex.rb b/benchmark/bm_vm2_mutex.rb
deleted file mode 100644
index 5d16480c6b..0000000000
--- a/benchmark/bm_vm2_mutex.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'thread'
-
-m = Thread::Mutex.new
-
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- m.synchronize{}
-end
diff --git a/benchmark/bm_vm2_newlambda.rb b/benchmark/bm_vm2_newlambda.rb
deleted file mode 100644
index 6422c9b0d0..0000000000
--- a/benchmark/bm_vm2_newlambda.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- lambda {}
-end
diff --git a/benchmark/bm_vm2_poly_method.rb b/benchmark/bm_vm2_poly_method.rb
deleted file mode 100644
index c82c0e4bce..0000000000
--- a/benchmark/bm_vm2_poly_method.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-class C1
- def m
- 1
- end
-end
-class C2
- def m
- 2
- end
-end
-
-o1 = C1.new
-o2 = C2.new
-
-i = 0
-while i<6_000_000 # benchmark loop 2
- o = (i % 2 == 0) ? o1 : o2
- o.m; o.m; o.m; o.m; o.m; o.m; o.m; o.m
- i += 1
-end
diff --git a/benchmark/bm_vm2_poly_method_ov.rb b/benchmark/bm_vm2_poly_method_ov.rb
deleted file mode 100644
index aa5fd1dd38..0000000000
--- a/benchmark/bm_vm2_poly_method_ov.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-class C1
- def m
- 1
- end
-end
-class C2
- def m
- 2
- end
-end
-
-o1 = C1.new
-o2 = C2.new
-
-i = 0
-while i<6_000_000 # benchmark loop 2
- o = (i % 2 == 0) ? o1 : o2
-# o.m; o.m; o.m; o.m; o.m; o.m; o.m; o.m
- i += 1
-end
diff --git a/benchmark/bm_vm2_poly_singleton.rb b/benchmark/bm_vm2_poly_singleton.rb
deleted file mode 100644
index 0dba4320c4..0000000000
--- a/benchmark/bm_vm2_poly_singleton.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-class C1
- def m; 1; end
-end
-
-o1 = C1.new
-o2 = C1.new
-o2.singleton_class
-
-i = 0
-while i<6_000_000 # benchmark loop 2
- o = (i % 2 == 0) ? o1 : o2
- o.m; o.m; o.m; o.m; o.m; o.m; o.m; o.m
- i += 1
-end
diff --git a/benchmark/bm_vm2_proc.rb b/benchmark/bm_vm2_proc.rb
deleted file mode 100644
index 65e5217371..0000000000
--- a/benchmark/bm_vm2_proc.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-def m &b
- b
-end
-
-pr = m{
- a = 1
-}
-
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- pr.call
-end
-
diff --git a/benchmark/bm_vm2_raise1.rb b/benchmark/bm_vm2_raise1.rb
deleted file mode 100644
index aa5387987f..0000000000
--- a/benchmark/bm_vm2_raise1.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-def rec n
- if n > 0
- rec n-1
- else
- raise
- end
-end
-
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
-
- begin
- rec 1
- rescue
- # ignore
- end
-end
diff --git a/benchmark/bm_vm2_raise2.rb b/benchmark/bm_vm2_raise2.rb
deleted file mode 100644
index 1f61c63157..0000000000
--- a/benchmark/bm_vm2_raise2.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-def rec n
- if n > 0
- rec n-1
- else
- raise
- end
-end
-
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
-
- begin
- rec 10
- rescue
- # ignore
- end
-end
diff --git a/benchmark/bm_vm2_regexp.rb b/benchmark/bm_vm2_regexp.rb
deleted file mode 100644
index 55f9e957a3..0000000000
--- a/benchmark/bm_vm2_regexp.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-i = 0
-str = 'xxxhogexxx'
-while i<6_000_000 # benchmark loop 2
- /hoge/ =~ str
- i += 1
-end
diff --git a/benchmark/bm_vm2_send.rb b/benchmark/bm_vm2_send.rb
deleted file mode 100644
index 6a3ab6fdab..0000000000
--- a/benchmark/bm_vm2_send.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class C
- def m
- end
-end
-
-o = C.new
-
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- o.__send__ :m
-end
diff --git a/benchmark/bm_vm2_string_literal.rb b/benchmark/bm_vm2_string_literal.rb
deleted file mode 100644
index 1d73036849..0000000000
--- a/benchmark/bm_vm2_string_literal.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- x = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
-end
diff --git a/benchmark/bm_vm2_struct_big_aref_hi.rb b/benchmark/bm_vm2_struct_big_aref_hi.rb
deleted file mode 100644
index 22cb26b0a5..0000000000
--- a/benchmark/bm_vm2_struct_big_aref_hi.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-s = Struct.new(*('a'..'z').map { |x| x.to_sym })
-x = s.new
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- x.z # x[25]
-end
diff --git a/benchmark/bm_vm2_struct_big_aref_lo.rb b/benchmark/bm_vm2_struct_big_aref_lo.rb
deleted file mode 100644
index 5e61a7087e..0000000000
--- a/benchmark/bm_vm2_struct_big_aref_lo.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-s = Struct.new(*('a'..'z').map { |x| x.to_sym })
-x = s.new
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- x.k # x[10]
-end
diff --git a/benchmark/bm_vm2_struct_big_aset.rb b/benchmark/bm_vm2_struct_big_aset.rb
deleted file mode 100644
index 5a1c3d16f3..0000000000
--- a/benchmark/bm_vm2_struct_big_aset.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-s = Struct.new(*('a'..'z').map { |x| x.to_sym })
-x = s.new
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- x.k = i # x[10] = i
-end
diff --git a/benchmark/bm_vm2_struct_big_href_hi.rb b/benchmark/bm_vm2_struct_big_href_hi.rb
deleted file mode 100644
index fff940a80a..0000000000
--- a/benchmark/bm_vm2_struct_big_href_hi.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-s = Struct.new(*('a'..'z').map { |x| x.to_sym })
-x = s.new
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- x[:z]
-end
diff --git a/benchmark/bm_vm2_struct_big_href_lo.rb b/benchmark/bm_vm2_struct_big_href_lo.rb
deleted file mode 100644
index 5e4085d59d..0000000000
--- a/benchmark/bm_vm2_struct_big_href_lo.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-s = Struct.new(*('a'..'z').map { |x| x.to_sym })
-x = s.new
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- x[:k]
-end
diff --git a/benchmark/bm_vm2_struct_big_hset.rb b/benchmark/bm_vm2_struct_big_hset.rb
deleted file mode 100644
index 9c0cee4141..0000000000
--- a/benchmark/bm_vm2_struct_big_hset.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-s = Struct.new(*('a'..'z').map { |x| x.to_sym })
-x = s.new
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- x[:k] = i
-end
diff --git a/benchmark/bm_vm2_struct_small_aref.rb b/benchmark/bm_vm2_struct_small_aref.rb
deleted file mode 100644
index 8eaa555b41..0000000000
--- a/benchmark/bm_vm2_struct_small_aref.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-s = Struct.new(:a, :b, :c)
-x = s.new
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- x.a
-end
diff --git a/benchmark/bm_vm2_struct_small_aset.rb b/benchmark/bm_vm2_struct_small_aset.rb
deleted file mode 100644
index ecd0f95669..0000000000
--- a/benchmark/bm_vm2_struct_small_aset.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-s = Struct.new(:a, :b, :c)
-x = s.new
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- x.a = i
-end
diff --git a/benchmark/bm_vm2_struct_small_href.rb b/benchmark/bm_vm2_struct_small_href.rb
deleted file mode 100644
index 2c88fee6bf..0000000000
--- a/benchmark/bm_vm2_struct_small_href.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-s = Struct.new(:a, :b, :c)
-x = s.new
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- x[:a]
-end
diff --git a/benchmark/bm_vm2_struct_small_hset.rb b/benchmark/bm_vm2_struct_small_hset.rb
deleted file mode 100644
index 33c36d20f1..0000000000
--- a/benchmark/bm_vm2_struct_small_hset.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-s = Struct.new(:a, :b, :c)
-x = s.new
-i = 0
-while i<6_000_000 # benchmark loop 2
- i += 1
- x[:a] = 1
-end
diff --git a/benchmark/bm_vm2_super.rb b/benchmark/bm_vm2_super.rb
deleted file mode 100644
index afd8579e7b..0000000000
--- a/benchmark/bm_vm2_super.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-
-class C
- def m
- 1
- end
-end
-
-class CC < C
- def m
- super()
- end
-end
-
-obj = CC.new
-
-i = 0
-while i<6_000_000 # benchmark loop 2
- obj.m
- i += 1
-end
diff --git a/benchmark/bm_vm2_unif1.rb b/benchmark/bm_vm2_unif1.rb
deleted file mode 100644
index 1774625942..0000000000
--- a/benchmark/bm_vm2_unif1.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-i = 0
-def m a, b
-end
-
-while i<6_000_000 # benchmark loop 2
- i += 1
- m 100, 200
-end
diff --git a/benchmark/bm_vm2_zsuper.rb b/benchmark/bm_vm2_zsuper.rb
deleted file mode 100644
index 2a43e62217..0000000000
--- a/benchmark/bm_vm2_zsuper.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-i = 0
-
-class C
- def m a
- 1
- end
-end
-
-class CC < C
- def m a
- super
- end
-end
-
-obj = CC.new
-
-while i<6_000_000 # benchmark loop 2
- obj.m 10
- i += 1
-end
diff --git a/benchmark/bm_vm_thread_pass_flood.rb b/benchmark/bm_vm_thread_pass_flood.rb
deleted file mode 100644
index 27157d1a6f..0000000000
--- a/benchmark/bm_vm_thread_pass_flood.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-1000.times{
- Thread.new{loop{Thread.pass}}
-}
-
-i = 0
-while i<10000
- i += 1
-end
diff --git a/benchmark/dir_empty_p.rb b/benchmark/dir_empty_p.rb
new file mode 100644
index 0000000000..8329c757cf
--- /dev/null
+++ b/benchmark/dir_empty_p.rb
@@ -0,0 +1,5 @@
+require 'tmpdir'
+max = 100_000
+Dir.mktmpdir('bm_dir_empty_p') do |dir|
+ max.times { Dir.empty?(dir) }
+end
diff --git a/benchmark/driver.rb b/benchmark/driver.rb
deleted file mode 100644
index eb976b4a90..0000000000
--- a/benchmark/driver.rb
+++ /dev/null
@@ -1,427 +0,0 @@
-#
-# Ruby Benchmark driver
-#
-
-first = true
-
-begin
- require 'optparse'
-rescue LoadError
- if first
- first = false
- $:.unshift File.join(File.dirname(__FILE__), '../lib')
- retry
- else
- raise
- end
-end
-
-require 'benchmark'
-require 'pp'
-require 'tempfile'
-
-class BenchmarkDriver
- def self.benchmark(opt)
- driver = self.new(opt[:execs], opt[:dir], opt)
- begin
- driver.run
- ensure
- driver.show_results
- end
- end
-
- def self.load(input, type, opt)
- case type
- when 'yaml'
- require 'yaml'
- h = YAML.load(input)
- when 'json'
- require 'json'
- h = JSON.load(input)
- else
- h = eval(input.read)
- end
- obj = allocate
- obj.instance_variable_set("@execs", h[:executables] || h["executables"])
- obj.instance_variable_set("@results", h[:results] || h["results"])
- obj.instance_variable_set("@opt", opt)
- obj
- end
-
- def output *args
- puts(*args)
- @output and @output.puts(*args)
- end
-
- def message *args
- output(*args) if @verbose
- end
-
- def message_print *args
- if @verbose
- print(*args)
- STDOUT.flush
- @output and @output.print(*args)
- end
- end
-
- def progress_message *args
- unless STDOUT.tty?
- STDERR.print(*args)
- STDERR.flush
- end
- end
-
- def initialize execs, dir, opt = {}
- @execs = execs.map{|e|
- e.strip!
- next if e.empty?
-
- if /(.+)::(.+)/ =~ e
- # ex) ruby-a::/path/to/ruby-a
- label = $1.strip
- path = $2
- version = `#{path} -v`.chomp
- else
- path = e
- version = label = `#{path} -v`.chomp
- end
- [path, label, version]
- }.compact
-
- @dir = dir
- @repeat = opt[:repeat] || 1
- @repeat = 1 if @repeat < 1
- @pattern = opt[:pattern] || nil
- @exclude = opt[:exclude] || nil
- @verbose = opt[:quiet] ? false : (opt[:verbose] || false)
- @output = opt[:output] ? open(opt[:output], 'w') : nil
- @loop_wl1 = @loop_wl2 = nil
- @ruby_arg = opt[:ruby_arg] || nil
- @measure_target = opt[:measure_target]
- @opt = opt
-
- # [[name, [[r-1-1, r-1-2, ...], [r-2-1, r-2-2, ...]]], ...]
- @results = []
-
- if @verbose
- @start_time = Time.now
- message @start_time
- @execs.each_with_index{|(path, label, version), i|
- message "target #{i}: " + (label == version ? "#{label}" : "#{label} (#{version})") + " at \"#{path}\""
- }
- message "measure target: #{@measure_target}"
- end
- end
-
- def adjusted_results name, results
- s = nil
- results.each_with_index{|e, i|
- r = e.min
- case name
- when /^vm1_/
- if @loop_wl1
- r -= @loop_wl1[i]
- r = 0 if r < 0
- s = '*'
- end
- when /^vm2_/
- if @loop_wl2
- r -= @loop_wl2[i]
- r = 0 if r < 0
- s = '*'
- end
- end
- yield r
- }
- s
- end
-
- def show_results
- case @opt[:format]
- when :tsv
- strformat = "\t%1$s"
- numformat = "\t%1$*2$.3f"
- minwidth = 0
- name_width = 0
- when :markdown
- markdown = true
- strformat = "|%1$-*2$s"
- numformat = "|%1$*2$.3f"
- when :plain
- strformat = " %1$-*2$s"
- numformat = " %1$*2$.3f"
- end
-
- name_width ||= @results.map {|v,*| v.size}.max
- minwidth ||= 7
- width = @execs.map{|(_, v)| [v.size, minwidth].max}
-
- output
-
- if @verbose
- message '-----------------------------------------------------------'
- message 'raw data:'
- message
- message PP.pp(@results, "", 79)
- message
- message "Elapsed time: #{Time.now - @start_time} (sec)"
- end
-
- if rawdata_output = @opt[:rawdata_output]
- h = {}
- h[:cpuinfo] = File.read('/proc/cpuinfo') if File.exist?('/proc/cpuinfo')
- h[:executables] = @execs
- h[:results] = @results
- if (type = File.extname(rawdata_output)).empty?
- type = rawdata_output
- rawdata_output = @output.path.sub(/\.[^.\/]+\z/, '') << '.' << rawdata_output
- end
- case type
- when 'yaml'
- require 'yaml'
- h = YAML.dump(h)
- when 'json'
- require 'json'
- h = JSON.pretty_generate(h)
- else
- require 'pp'
- h = h.pretty_inspect
- end
- open(rawdata_output, 'w') {|f| f.puts h}
- end
-
- output '-----------------------------------------------------------'
- output 'benchmark results:'
-
- if @verbose and @repeat > 1
- output "minimum results in each #{@repeat} measurements."
- end
-
- output({
- real: "Execution time (sec)",
- peak: "Memory usage (peak) (B)",
- size: "Memory usage (last size) (B)",
- }[@measure_target])
- output if markdown
- output ["name".ljust(name_width), @execs.map.with_index{|(_, v), i| sprintf(strformat, v, width[i])}].join("").rstrip
- output ["-"*name_width, width.map{|n|":".rjust(n, "-")}].join("|") if markdown
- @results.each{|v, result|
- rets = []
- s = adjusted_results(v, result){|r|
- rets << sprintf(numformat, r, width[rets.size])
- }
- v += s if s
- output [v.ljust(name_width), rets].join("")
- }
-
- if @execs.size > 1
- output
- output({
- real: "Speedup ratio: compare with the result of `#{@execs[0][1]}' (greater is better)",
- peak: "Memory consuming ratio (peak) with the result of `#{@execs[0][1]}' (greater is better)",
- size: "Memory consuming ratio (size) with the result of `#{@execs[0][1]}' (greater is better)",
- }[@measure_target])
- output if markdown
- output ["name".ljust(name_width), @execs[1..-1].map.with_index{|(_, v), i| sprintf(strformat, v, width[i])}].join("").rstrip
- output ["-"*name_width, width[1..-1].map{|n|":".rjust(n, "-")}].join("|") if markdown
- @results.each{|v, result|
- rets = []
- first_value = nil
- s = adjusted_results(v, result){|r|
- if first_value
- if r == 0
- rets << "Error"
- else
- rets << sprintf(numformat, first_value/Float(r), width[rets.size+1])
- end
- else
- first_value = r
- end
- }
- v += s if s
- output [v.ljust(name_width), rets].join("")
- }
- end
-
- if @opt[:output]
- output
- output "Log file: #{@opt[:output]}"
- end
- end
-
- def files
- flag = {}
- @files = Dir.glob(File.join(@dir, 'bm*.rb')).map{|file|
- next if @pattern && /#{@pattern}/ !~ File.basename(file)
- next if @exclude && /#{@exclude}/ =~ File.basename(file)
- case file
- when /bm_(vm[12])_/, /bm_loop_(whileloop2?).rb/
- flag[$1] = true
- end
- file
- }.compact
-
- if flag['vm1'] && !flag['whileloop']
- @files << File.join(@dir, 'bm_loop_whileloop.rb')
- elsif flag['vm2'] && !flag['whileloop2']
- @files << File.join(@dir, 'bm_loop_whileloop2.rb')
- end
-
- @files.sort!
- progress_message "total: #{@files.size * @repeat} trial(s) (#{@repeat} trial(s) for #{@files.size} benchmark(s))\n"
- @files
- end
-
- def run
- files.each_with_index{|file, i|
- @i = i
- r = measure_file(file)
-
- if /bm_loop_whileloop.rb/ =~ file
- @loop_wl1 = r[1].map{|e| e.min}
- elsif /bm_loop_whileloop2.rb/ =~ file
- @loop_wl2 = r[1].map{|e| e.min}
- end
- }
- end
-
- def measure_file file
- name = File.basename(file, '.rb').sub(/^bm_/, '')
- prepare_file = File.join(File.dirname(file), "prepare_#{name}.rb")
- load prepare_file if FileTest.exist?(prepare_file)
-
- if @verbose
- output
- output '-----------------------------------------------------------'
- output name
- output
- output File.read(file)
- output
- end
-
- result = [name]
- result << @execs.map{|(e, v)|
- (0...@repeat).map{
- message_print "#{v}\t"
- progress_message '.'
-
- m = measure(e, file)
- message "#{m}"
- m
- }
- }
- @results << result
- result
- end
-
- unless defined?(File::NULL)
- if File.exist?('/dev/null')
- File::NULL = '/dev/null'
- end
- end
-
- def measure executable, file
- case @measure_target
- when :real
- cmd = "#{executable} #{@ruby_arg} #{file}"
- m = Benchmark.measure{
- system(cmd, out: File::NULL)
- }
- result = m.real
- when :peak, :size
- tmp = Tempfile.new("benchmark-memory-wrapper-data")
- wrapper = "#{File.join(__dir__, 'memory_wrapper.rb')} #{tmp.path} #{@measure_target}"
- cmd = "#{executable} #{@ruby_arg} #{wrapper} #{file}"
- system(cmd, out: File::NULL)
- result = tmp.read.to_i
- tmp.close
- else
- raise "unknown measure target"
- end
-
- if $? != 0
- raise $?.inspect if $? && $?.signaled?
- output "\`#{cmd}\' exited with abnormal status (#{$?})"
- 0
- else
- result
- end
- end
-end
-
-if __FILE__ == $0
- opt = {
- :execs => [],
- :dir => File.dirname(__FILE__),
- :repeat => 1,
- :measure_target => :real,
- :output => nil,
- :raw_output => nil,
- :format => :tsv,
- }
- formats = {
- :tsv => ".tsv",
- :markdown => ".md",
- :plain => ".txt",
- }
-
- parser = OptionParser.new{|o|
- o.on('-e', '--executables [EXECS]',
- "Specify benchmark one or more targets (e1::path1; e2::path2; e3::path3;...)"){|e|
- e.split(/;/).each{|path|
- opt[:execs] << path
- }
- }
- o.on('-d', '--directory [DIRECTORY]', "Benchmark suites directory"){|d|
- opt[:dir] = d
- }
- o.on('-p', '--pattern [PATTERN]', "Benchmark name pattern"){|p|
- opt[:pattern] = p
- }
- o.on('-x', '--exclude [PATTERN]', "Benchmark exclude pattern"){|e|
- opt[:exclude] = e
- }
- o.on('-r', '--repeat-count [NUM]', "Repeat count"){|n|
- opt[:repeat] = n.to_i
- }
- o.on('-o', '--output-file [FILE]', "Output file"){|f|
- opt[:output] = f
- }
- o.on('--ruby-arg [ARG]', "Optional argument for ruby"){|a|
- opt[:ruby_arg] = a
- }
- o.on('--measure-target [TARGET]', 'real (execution time), peak, size (memory)'){|mt|
- opt[:measure_target] = mt.to_sym
- }
- o.on('--rawdata-output [FILE]', 'output rawdata'){|r|
- opt[:rawdata_output] = r
- }
- o.on('--load-rawdata=FILE', 'input rawdata'){|r|
- opt[:rawdata_input] = r
- }
- o.on('-f', "--format=FORMAT", "output format (#{formats.keys.join(",")})", formats.keys){|r|
- opt[:format] = r
- }
- o.on('-v', '--verbose'){|v|
- opt[:verbose] = v
- }
- o.on('-q', '--quiet', "Run without notify information except result table."){|q|
- opt[:quiet] = q
- opt[:verbose] = false
- }
- }
-
- parser.parse!(ARGV)
- opt[:output] ||= "bmlog-#{Time.now.strftime('%Y%m%d-%H%M%S')}.#{$$}#{formats[opt[:format]]}"
-
- if input = opt[:rawdata_input]
- b = open(input) {|f|
- BenchmarkDriver.load(f, File.extname(input)[1..-1], opt)
- }
- b.show_results
- else
- BenchmarkDriver.benchmark(opt)
- end
-end
-
diff --git a/benchmark/enum_lazy_grep_v_100.rb b/benchmark/enum_lazy_grep_v_100.rb
new file mode 100644
index 0000000000..8832392e65
--- /dev/null
+++ b/benchmark/enum_lazy_grep_v_100.rb
@@ -0,0 +1,4 @@
+grep_data = (1..10).to_a * 1000
+N = 100
+enum = grep_data.lazy.grep_v(->(i){i == 0}).grep_v(->(i){i == 0})
+N.times {enum.each {}}
diff --git a/benchmark/enum_lazy_grep_v_20.rb b/benchmark/enum_lazy_grep_v_20.rb
new file mode 100644
index 0000000000..329509fa8f
--- /dev/null
+++ b/benchmark/enum_lazy_grep_v_20.rb
@@ -0,0 +1,4 @@
+grep_data = (1..10).to_a * 1000
+N = 100
+enum = grep_data.lazy.grep_v(->(i){i > 2}).grep_v(->(i){i > 2})
+N.times {enum.each {}}
diff --git a/benchmark/enum_lazy_grep_v_50.rb b/benchmark/enum_lazy_grep_v_50.rb
new file mode 100644
index 0000000000..02ea4d4e71
--- /dev/null
+++ b/benchmark/enum_lazy_grep_v_50.rb
@@ -0,0 +1,4 @@
+grep_data = (1..10).to_a * 1000
+N = 100
+enum = grep_data.lazy.grep_v(->(i){i > 5}).grep_v(->(i){i > 5})
+N.times {enum.each {}}
diff --git a/benchmark/enum_lazy_uniq_100.rb b/benchmark/enum_lazy_uniq_100.rb
new file mode 100644
index 0000000000..2e6434d9c4
--- /dev/null
+++ b/benchmark/enum_lazy_uniq_100.rb
@@ -0,0 +1,4 @@
+uniq_data = (1..10_000).to_a
+N = 100
+enum = uniq_data.lazy.uniq {|i| i % 10000}.uniq {|i| i % 10000}
+N.times {enum.each {}}
diff --git a/benchmark/enum_lazy_uniq_20.rb b/benchmark/enum_lazy_uniq_20.rb
new file mode 100644
index 0000000000..75e6398fee
--- /dev/null
+++ b/benchmark/enum_lazy_uniq_20.rb
@@ -0,0 +1,4 @@
+uniq_data = (1..10_000).to_a
+N = 100
+enum = uniq_data.lazy.uniq {|i| i % 2000}.uniq {|i| i % 2000}
+N.times {enum.each {}}
diff --git a/benchmark/enum_lazy_uniq_50.rb b/benchmark/enum_lazy_uniq_50.rb
new file mode 100644
index 0000000000..59a39b78ff
--- /dev/null
+++ b/benchmark/enum_lazy_uniq_50.rb
@@ -0,0 +1,4 @@
+uniq_data = (1..10_000).to_a
+N = 100
+enum = uniq_data.lazy.uniq {|i| i % 5000}.uniq {|i| i % 5000}
+N.times {enum.each {}}
diff --git a/benchmark/erb_render.yml b/benchmark/erb_render.yml
new file mode 100644
index 0000000000..15f6c3880b
--- /dev/null
+++ b/benchmark/erb_render.yml
@@ -0,0 +1,24 @@
+prelude: |
+ require 'erb'
+
+ data = <<erb
+ <html>
+ <head> <%= title %> </head>
+ <body>
+ <h1> <%= title %> </h1>
+ <p>
+ <%= content %>
+ </p>
+ </body>
+ </html>
+ erb
+
+ title = "hello world!"
+ content = "hello world!\n" * 10
+
+ src = "def self.render(title, content); #{ERB.new(data).src}; end"
+ mod = Module.new
+ mod.instance_eval(src, "(ERB)")
+benchmark:
+ erb_render: mod.render(title, content)
+loop_count: 1500000
diff --git a/benchmark/fiber_chain.rb b/benchmark/fiber_chain.rb
new file mode 100755
index 0000000000..7e0a7f9d45
--- /dev/null
+++ b/benchmark/fiber_chain.rb
@@ -0,0 +1,40 @@
+# Check performance of fiber creation and transfer.
+
+def make_link(previous)
+ Fiber.new do
+ while message = previous.resume
+ Fiber.yield(message)
+ end
+ end
+end
+
+def make_chain(length, &block)
+ chain = Fiber.new(&block)
+
+ (length - 1).times do
+ chain = make_link(chain)
+ end
+
+ return chain
+end
+
+def run_benchmark(length, repeats, message = :hello)
+ chain = nil
+
+ chain = make_chain(length) do
+ while true
+ Fiber.yield(message)
+ end
+ end
+
+ repeats.times do
+ abort "invalid result" unless chain.resume == message
+ end
+end
+
+n = (ARGV[0] || 1000).to_i
+m = (ARGV[1] || 1000).to_i
+
+5.times do
+ run_benchmark(n, m)
+end
diff --git a/benchmark/file_chmod.rb b/benchmark/file_chmod.rb
new file mode 100644
index 0000000000..1cd4760c9d
--- /dev/null
+++ b/benchmark/file_chmod.rb
@@ -0,0 +1,9 @@
+# chmod file
+require 'tempfile'
+max = 200_000
+tmp = Tempfile.new('chmod')
+path = tmp.path
+max.times do
+ File.chmod(0777, path)
+end
+tmp.close!
diff --git a/benchmark/file_rename.rb b/benchmark/file_rename.rb
new file mode 100644
index 0000000000..bbb44aebac
--- /dev/null
+++ b/benchmark/file_rename.rb
@@ -0,0 +1,11 @@
+# rename file
+require 'tempfile'
+
+max = 100_000
+tmp = [ Tempfile.new('rename-a'), Tempfile.new('rename-b') ]
+a, b = tmp.map { |x| x.path }
+tmp.each { |t| t.close } # Windows can't rename files without closing them
+max.times do
+ File.rename(a, b)
+ File.rename(b, a)
+end
diff --git a/benchmark/gc/aobench.rb b/benchmark/gc/aobench.rb
index 2eed7abc83..275f58b924 100644
--- a/benchmark/gc/aobench.rb
+++ b/benchmark/gc/aobench.rb
@@ -1 +1 @@
-require_relative '../bm_app_aobench.rb'
+require_relative '../app_aobench'
diff --git a/benchmark/gc/binary_trees.rb b/benchmark/gc/binary_trees.rb
index af8ea722aa..83347cdd20 100644
--- a/benchmark/gc/binary_trees.rb
+++ b/benchmark/gc/binary_trees.rb
@@ -1 +1 @@
-require_relative '../bm_so_binary_trees.rb'
+require_relative '../so_binary_trees'
diff --git a/benchmark/gc/gcbench.rb b/benchmark/gc/gcbench.rb
index 09a404466a..23d0b91c6c 100644
--- a/benchmark/gc/gcbench.rb
+++ b/benchmark/gc/gcbench.rb
@@ -3,11 +3,12 @@ require 'pp'
require 'optparse'
$list = true
-$gcprof = true
+$gcprof = false
opt = OptionParser.new
opt.on('-q'){$list = false}
opt.on('-d'){$gcprof = false}
+opt.on('-p'){$gcprof = true}
opt.parse!(ARGV)
script = File.join(File.dirname(__FILE__), ARGV.shift)
diff --git a/benchmark/gc/pentomino.rb b/benchmark/gc/pentomino.rb
index 94ba74be89..8ebdff7d1d 100644
--- a/benchmark/gc/pentomino.rb
+++ b/benchmark/gc/pentomino.rb
@@ -1 +1 @@
-require_relative '../bm_app_pentomino.rb'
+require_relative '../app_pentomino'
diff --git a/benchmark/bm_hash_aref_dsym.rb b/benchmark/hash_aref_dsym.rb
index af4f8c36d4..af4f8c36d4 100644
--- a/benchmark/bm_hash_aref_dsym.rb
+++ b/benchmark/hash_aref_dsym.rb
diff --git a/benchmark/bm_hash_aref_dsym_long.rb b/benchmark/hash_aref_dsym_long.rb
index 9d7759379e..9d7759379e 100644
--- a/benchmark/bm_hash_aref_dsym_long.rb
+++ b/benchmark/hash_aref_dsym_long.rb
diff --git a/benchmark/bm_hash_aref_fix.rb b/benchmark/hash_aref_fix.rb
index 1346890582..1346890582 100644
--- a/benchmark/bm_hash_aref_fix.rb
+++ b/benchmark/hash_aref_fix.rb
diff --git a/benchmark/bm_hash_aref_flo.rb b/benchmark/hash_aref_flo.rb
index 2217274c82..2217274c82 100644
--- a/benchmark/bm_hash_aref_flo.rb
+++ b/benchmark/hash_aref_flo.rb
diff --git a/benchmark/bm_hash_aref_miss.rb b/benchmark/hash_aref_miss.rb
index b0913dd4bb..b0913dd4bb 100644
--- a/benchmark/bm_hash_aref_miss.rb
+++ b/benchmark/hash_aref_miss.rb
diff --git a/benchmark/bm_hash_aref_str.rb b/benchmark/hash_aref_str.rb
index 19439b061b..19439b061b 100644
--- a/benchmark/bm_hash_aref_str.rb
+++ b/benchmark/hash_aref_str.rb
diff --git a/benchmark/bm_hash_aref_sym.rb b/benchmark/hash_aref_sym.rb
index f75d163fe6..f75d163fe6 100644
--- a/benchmark/bm_hash_aref_sym.rb
+++ b/benchmark/hash_aref_sym.rb
diff --git a/benchmark/bm_hash_aref_sym_long.rb b/benchmark/hash_aref_sym_long.rb
index 9dab8df7be..9dab8df7be 100644
--- a/benchmark/bm_hash_aref_sym_long.rb
+++ b/benchmark/hash_aref_sym_long.rb
diff --git a/benchmark/bm_hash_flatten.rb b/benchmark/hash_flatten.rb
index e944aae9f2..e944aae9f2 100644
--- a/benchmark/bm_hash_flatten.rb
+++ b/benchmark/hash_flatten.rb
diff --git a/benchmark/bm_hash_ident_flo.rb b/benchmark/hash_ident_flo.rb
index 0c7edfed3e..0c7edfed3e 100644
--- a/benchmark/bm_hash_ident_flo.rb
+++ b/benchmark/hash_ident_flo.rb
diff --git a/benchmark/bm_hash_ident_num.rb b/benchmark/hash_ident_num.rb
index b226736c6f..b226736c6f 100644
--- a/benchmark/bm_hash_ident_num.rb
+++ b/benchmark/hash_ident_num.rb
diff --git a/benchmark/bm_hash_ident_obj.rb b/benchmark/hash_ident_obj.rb
index 4b3b58edec..4b3b58edec 100644
--- a/benchmark/bm_hash_ident_obj.rb
+++ b/benchmark/hash_ident_obj.rb
diff --git a/benchmark/bm_hash_ident_str.rb b/benchmark/hash_ident_str.rb
index 8582b38e31..8582b38e31 100644
--- a/benchmark/bm_hash_ident_str.rb
+++ b/benchmark/hash_ident_str.rb
diff --git a/benchmark/bm_hash_ident_sym.rb b/benchmark/hash_ident_sym.rb
index 4c81e3d28e..4c81e3d28e 100644
--- a/benchmark/bm_hash_ident_sym.rb
+++ b/benchmark/hash_ident_sym.rb
diff --git a/benchmark/bm_hash_keys.rb b/benchmark/hash_keys.rb
index 6863cd01f9..6863cd01f9 100644
--- a/benchmark/bm_hash_keys.rb
+++ b/benchmark/hash_keys.rb
diff --git a/benchmark/hash_literal_small2.rb b/benchmark/hash_literal_small2.rb
new file mode 100644
index 0000000000..c188529260
--- /dev/null
+++ b/benchmark/hash_literal_small2.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+1_000_000.times.map { { "foo" => "bar", "bar" => "baz" } }
diff --git a/benchmark/hash_literal_small4.rb b/benchmark/hash_literal_small4.rb
new file mode 100644
index 0000000000..739f71b5b0
--- /dev/null
+++ b/benchmark/hash_literal_small4.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+1_000_000.times.map { { "foo" => "bar", "bar" => "baz", "baz" => "lol", "lol" => "lgtm" } }
diff --git a/benchmark/hash_literal_small8.rb b/benchmark/hash_literal_small8.rb
new file mode 100644
index 0000000000..53d80af535
--- /dev/null
+++ b/benchmark/hash_literal_small8.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+1_000_000.times.map { { "foo" => "bar", "bar" => "baz", "baz" => "lol", "lol" => "lgtm", "lgtm" => "nope", "nope" => "ok", "ok" => "again", "again" => "wait" } }
diff --git a/benchmark/bm_hash_long.rb b/benchmark/hash_long.rb
index 03d9109602..03d9109602 100644
--- a/benchmark/bm_hash_long.rb
+++ b/benchmark/hash_long.rb
diff --git a/benchmark/bm_hash_shift.rb b/benchmark/hash_shift.rb
index a645671a5b..a645671a5b 100644
--- a/benchmark/bm_hash_shift.rb
+++ b/benchmark/hash_shift.rb
diff --git a/benchmark/bm_hash_shift_u16.rb b/benchmark/hash_shift_u16.rb
index ec800d0342..ec800d0342 100644
--- a/benchmark/bm_hash_shift_u16.rb
+++ b/benchmark/hash_shift_u16.rb
diff --git a/benchmark/bm_hash_shift_u24.rb b/benchmark/hash_shift_u24.rb
index de4e0fa696..de4e0fa696 100644
--- a/benchmark/bm_hash_shift_u24.rb
+++ b/benchmark/hash_shift_u24.rb
diff --git a/benchmark/bm_hash_shift_u32.rb b/benchmark/hash_shift_u32.rb
index 656aa55583..656aa55583 100644
--- a/benchmark/bm_hash_shift_u32.rb
+++ b/benchmark/hash_shift_u32.rb
diff --git a/benchmark/bm_hash_small2.rb b/benchmark/hash_small2.rb
index 45485d9c71..45485d9c71 100644
--- a/benchmark/bm_hash_small2.rb
+++ b/benchmark/hash_small2.rb
diff --git a/benchmark/bm_hash_small4.rb b/benchmark/hash_small4.rb
index acd4084334..acd4084334 100644
--- a/benchmark/bm_hash_small4.rb
+++ b/benchmark/hash_small4.rb
diff --git a/benchmark/bm_hash_small8.rb b/benchmark/hash_small8.rb
index 9cffcc91b6..9cffcc91b6 100644
--- a/benchmark/bm_hash_small8.rb
+++ b/benchmark/hash_small8.rb
diff --git a/benchmark/bm_hash_to_proc.rb b/benchmark/hash_to_proc.rb
index 2b675bf509..2b675bf509 100644
--- a/benchmark/bm_hash_to_proc.rb
+++ b/benchmark/hash_to_proc.rb
diff --git a/benchmark/bm_hash_values.rb b/benchmark/hash_values.rb
index 069441302f..069441302f 100644
--- a/benchmark/bm_hash_values.rb
+++ b/benchmark/hash_values.rb
diff --git a/benchmark/int_quo.rb b/benchmark/int_quo.rb
new file mode 100644
index 0000000000..e22a3f8c30
--- /dev/null
+++ b/benchmark/int_quo.rb
@@ -0,0 +1 @@
+5000000.times { 42.quo(3) }
diff --git a/benchmark/io_copy_stream_write.rb b/benchmark/io_copy_stream_write.rb
new file mode 100644
index 0000000000..3fd87250a4
--- /dev/null
+++ b/benchmark/io_copy_stream_write.rb
@@ -0,0 +1,24 @@
+# The goal of this is to use a synthetic (non-IO) reader
+# to trigger the read/write loop of IO.copy_stream,
+# bypassing in-kernel mechanisms like sendfile for zero copy,
+# so we wrap the /dev/zero IO object:
+
+class Zero
+ def initialize
+ @n = 100000
+ @in = File.open('/dev/zero', 'rb')
+ end
+
+ def read(len, buf)
+ return if (@n -= 1) == 0
+ @in.read(len, buf)
+ end
+end
+
+begin
+ src = Zero.new
+ dst = File.open(IO::NULL, 'wb')
+ n = IO.copy_stream(src, dst)
+rescue Errno::ENOENT
+ # not *nix
+end if IO.respond_to?(:copy_stream) && IO.const_defined?(:NULL)
diff --git a/benchmark/io_copy_stream_write_socket.rb b/benchmark/io_copy_stream_write_socket.rb
new file mode 100644
index 0000000000..11f369bd0d
--- /dev/null
+++ b/benchmark/io_copy_stream_write_socket.rb
@@ -0,0 +1,35 @@
+# The goal of this is to use a synthetic (non-IO) reader
+# to trigger the read/write loop of IO.copy_stream,
+# bypassing in-kernel mechanisms like sendfile for zero copy,
+# so we wrap the /dev/zero IO object:
+class Zero
+ def initialize
+ @n = 100000
+ @in = File.open('/dev/zero', 'rb')
+ end
+
+ def read(len, buf)
+ return if (@n -= 1) == 0
+ @in.read(len, buf)
+ end
+end
+
+begin
+ require 'socket'
+ src = Zero.new
+ rd, wr = UNIXSocket.pair
+ pid = fork do
+ wr.close
+ buf = String.new
+ while rd.read(16384, buf)
+ end
+ end
+ rd.close
+ IO.copy_stream(src, wr)
+rescue Errno::ENOENT, NotImplementedError, NameError
+ # not *nix: missing /dev/zero, fork, or UNIXSocket
+rescue LoadError # no socket?
+ensure
+ wr.close if wr
+ Process.waitpid(pid) if pid
+end if IO.respond_to?(:copy_stream)
diff --git a/benchmark/bm_io_file_create.rb b/benchmark/io_file_create.rb
index 2f205c1333..2f205c1333 100644
--- a/benchmark/bm_io_file_create.rb
+++ b/benchmark/io_file_create.rb
diff --git a/benchmark/bm_io_file_read.rb b/benchmark/io_file_read.rb
index b9e796ed30..b9e796ed30 100644
--- a/benchmark/bm_io_file_read.rb
+++ b/benchmark/io_file_read.rb
diff --git a/benchmark/bm_io_file_write.rb b/benchmark/io_file_write.rb
index aa1be0e5fe..aa1be0e5fe 100644
--- a/benchmark/bm_io_file_write.rb
+++ b/benchmark/io_file_write.rb
diff --git a/benchmark/bm_io_nonblock_noex.rb b/benchmark/io_nonblock_noex.rb
index da9357fdc6..da9357fdc6 100644
--- a/benchmark/bm_io_nonblock_noex.rb
+++ b/benchmark/io_nonblock_noex.rb
diff --git a/benchmark/bm_io_nonblock_noex2.rb b/benchmark/io_nonblock_noex2.rb
index 56819d049b..56819d049b 100644
--- a/benchmark/bm_io_nonblock_noex2.rb
+++ b/benchmark/io_nonblock_noex2.rb
diff --git a/benchmark/io_pipe_rw.rb b/benchmark/io_pipe_rw.rb
new file mode 100644
index 0000000000..6862a8ae61
--- /dev/null
+++ b/benchmark/io_pipe_rw.rb
@@ -0,0 +1,13 @@
+# Measure uncontended GVL performance via read/write with 1:1 threading
+# If we switch to M:N threading, this will benchmark something else...
+r, w = IO.pipe
+src = '0'.freeze
+dst = String.new
+i = 0
+while i < 1_000_000
+ i += 1
+ w.write(src)
+ r.read(1, dst)
+end
+w.close
+r.close
diff --git a/benchmark/bm_io_select.rb b/benchmark/io_select.rb
index 19248daeb1..19248daeb1 100644
--- a/benchmark/bm_io_select.rb
+++ b/benchmark/io_select.rb
diff --git a/benchmark/bm_io_select2.rb b/benchmark/io_select2.rb
index 10e37d71b2..10e37d71b2 100644
--- a/benchmark/bm_io_select2.rb
+++ b/benchmark/io_select2.rb
diff --git a/benchmark/bm_io_select3.rb b/benchmark/io_select3.rb
index 7d0ba1f092..7d0ba1f092 100644
--- a/benchmark/bm_io_select3.rb
+++ b/benchmark/io_select3.rb
diff --git a/benchmark/lib/benchmark_driver/output/driver.rb b/benchmark/lib/benchmark_driver/output/driver.rb
new file mode 100644
index 0000000000..d22236e9fb
--- /dev/null
+++ b/benchmark/lib/benchmark_driver/output/driver.rb
@@ -0,0 +1,36 @@
+require 'benchmark_driver/output/simple'
+
+# This replicates the legacy benchmark/driver.rb behavior.
+class BenchmarkDriver::Output::Driver < BenchmarkDriver::Output::Simple
+ def initialize(*)
+ super
+ @stdout = $stdout
+ @strio = StringIO.new
+ $stdout = IOMultiplexer.new(@stdout, @strio)
+ end
+
+ def with_benchmark(*)
+ super
+ ensure
+ logfile = "bmlog-#{Time.now.strftime('%Y%m%d-%H%M%S')}.#{$$}.log"
+ puts "\nLog file: #{logfile}"
+
+ $stdout = @stdout
+ File.write(logfile, @strio.tap(&:rewind).read)
+ end
+
+ class IOMultiplexer
+ def initialize(io1, io2)
+ @io1 = io1
+ @io2 = io2
+ end
+
+ [:write, :sync, :sync=, :puts, :print, :flush].each do |method|
+ define_method(method) do |*args|
+ @io1.send(method, *args)
+ @io2.send(method, *args)
+ end
+ end
+ end
+ private_constant :IOMultiplexer
+end
diff --git a/benchmark/lib/benchmark_driver/runner/cstime.rb b/benchmark/lib/benchmark_driver/runner/cstime.rb
new file mode 100644
index 0000000000..3c3453e527
--- /dev/null
+++ b/benchmark/lib/benchmark_driver/runner/cstime.rb
@@ -0,0 +1,22 @@
+require 'benchmark_driver/runner/total'
+
+class BenchmarkDriver::Runner::Cstime < BenchmarkDriver::Runner::Total
+ METRIC = BenchmarkDriver::Metric.new(name: 'cstime', unit: 's', larger_better: false)
+
+ # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job"
+ Job = Class.new(BenchmarkDriver::DefaultJob)
+ # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse`
+ JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC])
+
+ private
+
+ # Overriding BenchmarkDriver::Runner::Total#metric
+ def metric
+ METRIC
+ end
+
+ # Overriding BenchmarkDriver::Runner::Total#target
+ def target
+ :cstime
+ end
+end
diff --git a/benchmark/lib/benchmark_driver/runner/cutime.rb b/benchmark/lib/benchmark_driver/runner/cutime.rb
new file mode 100644
index 0000000000..e139962ef2
--- /dev/null
+++ b/benchmark/lib/benchmark_driver/runner/cutime.rb
@@ -0,0 +1,22 @@
+require 'benchmark_driver/runner/total'
+
+class BenchmarkDriver::Runner::Cutime < BenchmarkDriver::Runner::Total
+ METRIC = BenchmarkDriver::Metric.new(name: 'cutime', unit: 's', larger_better: false)
+
+ # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job"
+ Job = Class.new(BenchmarkDriver::DefaultJob)
+ # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse`
+ JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC])
+
+ private
+
+ # Overriding BenchmarkDriver::Runner::Total#metric
+ def metric
+ METRIC
+ end
+
+ # Overriding BenchmarkDriver::Runner::Total#target
+ def target
+ :cutime
+ end
+end
diff --git a/benchmark/lib/benchmark_driver/runner/peak.rb b/benchmark/lib/benchmark_driver/runner/peak.rb
new file mode 100644
index 0000000000..0ba8af42c5
--- /dev/null
+++ b/benchmark/lib/benchmark_driver/runner/peak.rb
@@ -0,0 +1,151 @@
+require 'benchmark_driver/struct'
+require 'benchmark_driver/metric'
+require 'benchmark_driver/default_job'
+require 'benchmark_driver/default_job_parser'
+require 'tempfile'
+
+class BenchmarkDriver::Runner::Peak
+ METRIC = BenchmarkDriver::Metric.new(
+ name: 'Peak memory usage', unit: 'bytes', larger_better: false, worse_word: 'larger',
+ )
+
+ # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job"
+ Job = Class.new(BenchmarkDriver::DefaultJob)
+ # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse`
+ JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC])
+
+ # @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)
+ if jobs.any? { |job| job.loop_count.nil? }
+ jobs = jobs.map do |job|
+ job.loop_count ? job : Job.new(job.to_h.merge(loop_count: 1))
+ end
+ end
+
+ @output.with_benchmark do
+ jobs.each do |job|
+ @output.with_job(name: job.name) do
+ job.runnable_contexts(@contexts).each do |context|
+ value = BenchmarkDriver::Repeater.with_repeat(config: @config, larger_better: false) do
+ run_benchmark(job, context: context)
+ end
+ @output.with_context(name: context.name, executable: context.executable, gems: context.gems, prelude: context.prelude) do
+ @output.report(values: { metric => value }, 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:)
+ benchmark = BenchmarkScript.new(
+ preludes: [context.prelude, job.prelude],
+ script: job.script,
+ teardown: job.teardown,
+ loop_count: job.loop_count,
+ )
+
+ memory_status = File.expand_path('../../../../test/lib/memory_status', __dir__)
+ Tempfile.open(['benchmark_driver-', '.rb']) do |f|
+ with_script(benchmark.render) do |path|
+ output = IO.popen([*context.executable.command, path, f.path, target, memory_status], &:read)
+ if $?.success?
+ Integer(f.read)
+ else
+ $stdout.print(output)
+ BenchmarkDriver::Result::ERROR
+ end
+ end
+ end
+ end
+
+ # Overridden by BenchmarkDriver::Runner::Size
+ def target
+ 'peak'
+ end
+
+ # Overridden by BenchmarkDriver::Runner::Size
+ def metric
+ METRIC
+ 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 [String] prelude
+ # @param [String] script
+ # @param [String] teardown
+ # @param [Integer] loop_count
+ BenchmarkScript = ::BenchmarkDriver::Struct.new(:preludes, :script, :teardown, :loop_count) do
+ def render
+ prelude = preludes.reject(&:nil?).reject(&:empty?).join("\n")
+ <<-RUBY
+#{prelude}
+#{while_loop(script, loop_count)}
+#{teardown}
+
+result_file, target, memory_status = ARGV
+require_relative memory_status
+
+ms = Memory::Status.new
+case target.to_sym
+when :peak
+ key = ms.respond_to?(:hwm) ? :hwm : :peak
+when :size
+ key = ms.respond_to?(:rss) ? :rss : :size
+else
+ raise('unexpected target: ' + target)
+end
+
+File.write(result_file, ms[key])
+ RUBY
+ end
+
+ private
+
+ def while_loop(content, times)
+ if !times.is_a?(Integer) || times <= 0
+ raise ArgumentError.new("Unexpected times: #{times.inspect}")
+ end
+
+ if times > 1
+ <<-RUBY
+__bmdv_i = 0
+while __bmdv_i < #{times}
+ #{content}
+ __bmdv_i += 1
+end
+ RUBY
+ else
+ content
+ end
+ end
+ end
+ private_constant :BenchmarkScript
+end
diff --git a/benchmark/lib/benchmark_driver/runner/size.rb b/benchmark/lib/benchmark_driver/runner/size.rb
new file mode 100644
index 0000000000..1b31f901c7
--- /dev/null
+++ b/benchmark/lib/benchmark_driver/runner/size.rb
@@ -0,0 +1,25 @@
+require 'benchmark_driver/runner/peak'
+
+# Actually the same as BenchmarkDriver::Runner::Memory
+class BenchmarkDriver::Runner::Size < BenchmarkDriver::Runner::Peak
+ METRIC = BenchmarkDriver::Metric.new(
+ name: 'Max resident set size', unit: 'bytes', larger_better: false, worse_word: 'larger',
+ )
+
+ # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job"
+ Job = Class.new(BenchmarkDriver::DefaultJob)
+ # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse`
+ JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC])
+
+ private
+
+ # Overriding BenchmarkDriver::Runner::Peak#metric
+ def metric
+ METRIC
+ end
+
+ # Overriding BenchmarkDriver::Runner::Peak#target
+ def target
+ 'size'
+ end
+end
diff --git a/benchmark/lib/benchmark_driver/runner/stime.rb b/benchmark/lib/benchmark_driver/runner/stime.rb
new file mode 100644
index 0000000000..4577fb0bf8
--- /dev/null
+++ b/benchmark/lib/benchmark_driver/runner/stime.rb
@@ -0,0 +1,22 @@
+require 'benchmark_driver/runner/total'
+
+class BenchmarkDriver::Runner::Stime < BenchmarkDriver::Runner::Total
+ METRIC = BenchmarkDriver::Metric.new(name: 'stime', unit: 's', larger_better: false)
+
+ # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job"
+ Job = Class.new(BenchmarkDriver::DefaultJob)
+ # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse`
+ JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC])
+
+ private
+
+ # Overriding BenchmarkDriver::Runner::Total#metric
+ def metric
+ METRIC
+ end
+
+ # Overriding BenchmarkDriver::Runner::Total#target
+ def target
+ :stime
+ end
+end
diff --git a/benchmark/lib/benchmark_driver/runner/total.rb b/benchmark/lib/benchmark_driver/runner/total.rb
new file mode 100644
index 0000000000..64dc14f84e
--- /dev/null
+++ b/benchmark/lib/benchmark_driver/runner/total.rb
@@ -0,0 +1,137 @@
+require 'benchmark_driver/struct'
+require 'benchmark_driver/metric'
+require 'benchmark_driver/default_job'
+require 'benchmark_driver/default_job_parser'
+require 'tempfile'
+
+class BenchmarkDriver::Runner::Total
+ METRIC = BenchmarkDriver::Metric.new(name: 'Total time', unit: 's', larger_better: false)
+
+ # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job"
+ Job = Class.new(BenchmarkDriver::DefaultJob)
+ # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse`
+ JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC])
+
+ # @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::Total::Job>] jobs
+ def run(jobs)
+ if jobs.any? { |job| job.loop_count.nil? }
+ raise 'missing loop_count is not supported in Ruby repository'
+ end
+
+ @output.with_benchmark do
+ jobs.each do |job|
+ @output.with_job(name: job.name) do
+ job.runnable_contexts(@contexts).each do |context|
+ duration = BenchmarkDriver::Repeater.with_repeat(config: @config, larger_better: false) do
+ run_benchmark(job, context: context)
+ end
+ @output.with_context(name: context.name, executable: context.executable, gems: context.gems, prelude: context.prelude) do
+ @output.report(values: { metric => duration }, 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:)
+ benchmark = BenchmarkScript.new(
+ preludes: [context.prelude, job.prelude],
+ script: job.script,
+ teardown: job.teardown,
+ loop_count: job.loop_count,
+ )
+
+ Tempfile.open(['benchmark_driver-', '.rb']) do |f|
+ with_script(benchmark.render(result: f.path, target: target)) do |path|
+ IO.popen([*context.executable.command, path], &:read) # TODO: print stdout if verbose=2
+ if $?.success?
+ Float(f.read)
+ else
+ BenchmarkDriver::Result::ERROR
+ end
+ end
+ end
+ end
+
+ # This method is overridden by some subclasses
+ def metric
+ METRIC
+ end
+
+ # This method is overridden by some subclasses
+ def target
+ :total
+ 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 [String] prelude
+ # @param [String] script
+ # @param [String] teardown
+ # @param [Integer] loop_count
+ BenchmarkScript = ::BenchmarkDriver::Struct.new(:preludes, :script, :teardown, :loop_count) do
+ # @param [String] result - A file to write result
+ def render(result:, target:)
+ prelude = preludes.reject(&:nil?).reject(&:empty?).join("\n")
+ <<-RUBY
+#{prelude}
+
+require 'benchmark'
+__bmdv_result = Benchmark.measure {
+ #{while_loop(script, loop_count)}
+}
+
+#{teardown}
+
+File.write(#{result.dump}, __bmdv_result.#{target})
+ RUBY
+ end
+
+ private
+
+ def while_loop(content, times)
+ if !times.is_a?(Integer) || times <= 0
+ raise ArgumentError.new("Unexpected times: #{times.inspect}")
+ elsif times == 1
+ return content
+ end
+
+ # TODO: execute in batch
+ <<-RUBY
+__bmdv_i = 0
+while __bmdv_i < #{times}
+ #{content}
+ __bmdv_i += 1
+end
+ RUBY
+ end
+ end
+ private_constant :BenchmarkScript
+end
diff --git a/benchmark/lib/benchmark_driver/runner/utime.rb b/benchmark/lib/benchmark_driver/runner/utime.rb
new file mode 100644
index 0000000000..b61d83a188
--- /dev/null
+++ b/benchmark/lib/benchmark_driver/runner/utime.rb
@@ -0,0 +1,22 @@
+require 'benchmark_driver/runner/total'
+
+class BenchmarkDriver::Runner::Utime < BenchmarkDriver::Runner::Total
+ METRIC = BenchmarkDriver::Metric.new(name: 'utime', unit: 's', larger_better: false)
+
+ # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job"
+ Job = Class.new(BenchmarkDriver::DefaultJob)
+ # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse`
+ JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC])
+
+ private
+
+ # Overriding BenchmarkDriver::Runner::Total#metric
+ def metric
+ METRIC
+ end
+
+ # Overriding BenchmarkDriver::Runner::Total#target
+ def target
+ :utime
+ end
+end
diff --git a/benchmark/lib/load.rb b/benchmark/lib/load.rb
new file mode 100755
index 0000000000..4d73a63323
--- /dev/null
+++ b/benchmark/lib/load.rb
@@ -0,0 +1,2 @@
+$:.unshift(File.join(__dir__, '../benchmark-driver/lib'))
+require 'benchmark_driver'
diff --git a/benchmark/bm_loop_for.rb b/benchmark/loop_for.rb
index 0fc4cc1511..0fc4cc1511 100644
--- a/benchmark/bm_loop_for.rb
+++ b/benchmark/loop_for.rb
diff --git a/benchmark/bm_loop_generator.rb b/benchmark/loop_generator.rb
index d3375c744c..d3375c744c 100644
--- a/benchmark/bm_loop_generator.rb
+++ b/benchmark/loop_generator.rb
diff --git a/benchmark/bm_loop_times.rb b/benchmark/loop_times.rb
index 521f72ad1a..521f72ad1a 100644
--- a/benchmark/bm_loop_times.rb
+++ b/benchmark/loop_times.rb
diff --git a/benchmark/bm_loop_whileloop.rb b/benchmark/loop_whileloop.rb
index 0072822c06..0072822c06 100644
--- a/benchmark/bm_loop_whileloop.rb
+++ b/benchmark/loop_whileloop.rb
diff --git a/benchmark/bm_loop_whileloop2.rb b/benchmark/loop_whileloop2.rb
index 47d02dffc4..47d02dffc4 100644
--- a/benchmark/bm_loop_whileloop2.rb
+++ b/benchmark/loop_whileloop2.rb
diff --git a/benchmark/make_fasta_output.rb b/benchmark/make_fasta_output.rb
deleted file mode 100644
index b6d787ae27..0000000000
--- a/benchmark/make_fasta_output.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# prepare 'fasta.output'
-
-def prepare_fasta_output n
- filebase = File.join(File.dirname($0), 'fasta.output')
- script = File.join(File.dirname($0), 'bm_so_fasta.rb')
- file = "#{filebase}.#{n}"
-
- unless FileTest.exist?(file)
- STDERR.puts "preparing #{file}"
-
- open(file, 'w'){|f|
- ARGV[0] = n
- $stdout = f
- load script
- $stdout = STDOUT
- }
- end
-end
-
diff --git a/benchmark/bm_marshal_dump_flo.rb b/benchmark/marshal_dump_flo.rb
index 9b8d0c6afb..9b8d0c6afb 100644
--- a/benchmark/bm_marshal_dump_flo.rb
+++ b/benchmark/marshal_dump_flo.rb
diff --git a/benchmark/bm_marshal_dump_load_geniv.rb b/benchmark/marshal_dump_load_geniv.rb
index 8252ad90fa..8252ad90fa 100644
--- a/benchmark/bm_marshal_dump_load_geniv.rb
+++ b/benchmark/marshal_dump_load_geniv.rb
diff --git a/benchmark/bm_marshal_dump_load_time.rb b/benchmark/marshal_dump_load_time.rb
index e29743b791..e29743b791 100644
--- a/benchmark/bm_marshal_dump_load_time.rb
+++ b/benchmark/marshal_dump_load_time.rb
diff --git a/benchmark/memory_wrapper.rb b/benchmark/memory_wrapper.rb
deleted file mode 100644
index 3f4451a037..0000000000
--- a/benchmark/memory_wrapper.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-
-write_file, target, script_file = ARGV
-
-load(script_file)
-require_relative '../test/lib/memory_status'
-open(write_file, 'wb'){|f|
- ms = Memory::Status.new
- case target.to_sym
- when :peak
- key = ms.respond_to?(:hwm) ? :hwm : :peak
- when :size
- key = ms.respond_to?(:rss) ? :rss : :size
- end
-
- f.puts ms[key]
-}
diff --git a/benchmark/prepare_require.rb b/benchmark/prepare_require.rb
deleted file mode 100644
index c4786f04ad..0000000000
--- a/benchmark/prepare_require.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require "fileutils"
-
-def prepare
- num_files = 10000
-
- basename = File.dirname($0)
- data_dir = File.join(basename, "bm_require.data")
-
- # skip if all of files exists
- if File.exist?(File.join(data_dir, "c#{num_files}.rb"))
- return
- end
-
- FileUtils.mkdir_p(data_dir)
-
- 1.upto(num_files) do |i|
- f = File.open("#{data_dir}/c#{i}.rb", "w")
- f.puts <<-END
- class C#{i}
- end
- END
- end
-end
-
-prepare
diff --git a/benchmark/prepare_require_thread.rb b/benchmark/prepare_require_thread.rb
deleted file mode 100644
index 339ecb8b39..0000000000
--- a/benchmark/prepare_require_thread.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-load File.join(File.dirname(__FILE__), "prepare_require.rb")
-
diff --git a/benchmark/prepare_so_count_words.rb b/benchmark/prepare_so_count_words.rb
deleted file mode 100644
index ee2138cdb2..0000000000
--- a/benchmark/prepare_so_count_words.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# prepare 'wc.input'
-
-def prepare_wc_input
- wcinput = File.join(File.dirname($0), 'wc.input')
- wcbase = File.join(File.dirname($0), 'wc.input.base')
- unless FileTest.exist?(wcinput)
- data = File.read(wcbase)
- 13.times{
- data << data
- }
- open(wcinput, 'w'){|f| f.write data}
- end
-end
-
-prepare_wc_input
diff --git a/benchmark/prepare_so_k_nucleotide.rb b/benchmark/prepare_so_k_nucleotide.rb
deleted file mode 100644
index d83aeb7a7e..0000000000
--- a/benchmark/prepare_so_k_nucleotide.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-require_relative 'make_fasta_output'
-prepare_fasta_output(100_000)
diff --git a/benchmark/prepare_so_reverse_complement.rb b/benchmark/prepare_so_reverse_complement.rb
deleted file mode 100644
index da3ec2df14..0000000000
--- a/benchmark/prepare_so_reverse_complement.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-require_relative 'make_fasta_output'
-prepare_fasta_output(2_500_000)
diff --git a/benchmark/report.rb b/benchmark/report.rb
deleted file mode 100644
index d2dc56b1e1..0000000000
--- a/benchmark/report.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-#
-# YARV benchmark driver
-#
-
-require 'yarvutil'
-require 'benchmark'
-require 'rbconfig'
-
-def exec_command type, file, w
- <<-EOP
- $DRIVER_PATH = '#{File.dirname($0)}'
- $LOAD_PATH.replace $LOAD_PATH | #{$LOAD_PATH.inspect}
- require 'benchmark'
- require 'yarvutil'
-# print '#{type}'
- begin
- puts Benchmark.measure{
- #{w}('#{file}')
- }.utime
- rescue Exception => exec_command_error_variable
- puts "\t" + exec_command_error_variable.message
- end
- EOP
-end
-
-def benchmark cmd
- rubybin = ENV['RUBY'] || RbConfig.ruby
-
- IO.popen(rubybin, 'r+'){|io|
- io.write cmd
- io.close_write
- return io.gets
- }
-end
-
-def ruby_exec file
- prog = exec_command 'ruby', file, 'load'
- benchmark prog
-end
-
-def yarv_exec file
- prog = exec_command 'yarv', file, 'YARVUtil.load_bm'
- benchmark prog
-end
-
-$wr = $wy = nil
-
-def measure bench
- file = File.dirname($0) + "/bm_#{bench}.rb"
- r = ruby_exec(file).to_f
- y = yarv_exec(file).to_f
- puts "#{bench}\t#{r}\t#{y}"
-end
-
-def measure2
- r = ruby_exec.to_f
- y = yarv_exec.to_f
- puts r/y
-end
-
-if $0 == __FILE__
- %w{
- whileloop
- whileloop2
- times
- const
- method
- poly_method
- block
- rescue
- rescue2
- }.each{|bench|
- measure bench
- }
-end
-
-
-
-
diff --git a/benchmark/require.yml b/benchmark/require.yml
new file mode 100644
index 0000000000..711d8e11e9
--- /dev/null
+++ b/benchmark/require.yml
@@ -0,0 +1,36 @@
+prelude: |
+ require "fileutils"
+
+ def prepare
+ num_files = 10000
+
+ basename = File.dirname($0)
+ data_dir = File.join(basename, "bm_require.data")
+
+ # skip if all of files exists
+ if File.exist?(File.join(data_dir, "c#{num_files}.rb"))
+ return
+ end
+
+ FileUtils.mkdir_p(data_dir)
+
+ 1.upto(num_files) do |i|
+ f = File.open("#{data_dir}/c#{i}.rb", "w")
+ f.puts <<-END
+ class C#{i}
+ end
+ END
+ end
+ end
+
+ prepare
+benchmark:
+ require: |
+ $:.push File.join(File.dirname(__FILE__), "bm_require.data")
+
+ 1.upto(10000) do |i|
+ require "c#{i}"
+ end
+
+ $:.pop
+loop_count: 1
diff --git a/benchmark/require_thread.yml b/benchmark/require_thread.yml
new file mode 100644
index 0000000000..87e0ba888b
--- /dev/null
+++ b/benchmark/require_thread.yml
@@ -0,0 +1,44 @@
+prelude: |
+ require "fileutils"
+
+ def prepare
+ num_files = 10000
+
+ basename = File.dirname($0)
+ data_dir = File.join(basename, "bm_require.data")
+
+ # skip if all of files exists
+ if File.exist?(File.join(data_dir, "c#{num_files}.rb"))
+ return
+ end
+
+ FileUtils.mkdir_p(data_dir)
+
+ 1.upto(num_files) do |i|
+ f = File.open("#{data_dir}/c#{i}.rb", "w")
+ f.puts <<-END
+ class C#{i}
+ end
+ END
+ end
+ end
+
+ prepare
+benchmark:
+ require_thread: |
+ $:.push File.join(File.dirname(__FILE__), "bm_require.data")
+
+ i=0
+ t = Thread.new do
+ while true
+ i = i+1 # dummy loop
+ end
+ end
+
+ 1.upto(100) do |i|
+ require "c#{i}"
+ end
+
+ $:.pop
+ t.kill
+loop_count: 1
diff --git a/benchmark/run.rb b/benchmark/run.rb
deleted file mode 100644
index 0cd2363849..0000000000
--- a/benchmark/run.rb
+++ /dev/null
@@ -1,127 +0,0 @@
-#
-# Ruby benchmark driver
-#
-
-require 'benchmark'
-require 'rbconfig'
-
-$matzrubyonly = false
-$rubyonly = false
-
-$results = []
-
-# prepare 'wc.input'
-def prepare_wc_input
- wcinput = File.join(File.dirname($0), 'wc.input')
- wcbase = File.join(File.dirname($0), 'wc.input.base')
- unless FileTest.exist?(wcinput)
- data = File.read(wcbase)
- 13.times{
- data << data
- }
- open(wcinput, 'w'){|f| f.write data}
- end
-end
-
-prepare_wc_input
-
-def bm file
- prog = File.readlines(file).map{|e| e.rstrip}.join("\n")
- return if prog.empty?
-
- /[a-z]+_(.+)\.rb/ =~ file
- bm_name = $1
- puts '-----------------------------------------------------------' unless $rubyonly || $matzrubyonly
- puts "#{bm_name}: "
-
-
-puts <<EOS unless $matzrubyonly || $rubyonly
-#{prog}
---
-EOS
- begin
- result = [bm_name]
- result << matzruby_exec(file) unless $rubyonly
- result << ruby_exec(file) unless $matzrubyonly
- $results << result
-
- rescue Exception => e
- puts
- puts "** benchmark failure: #{e}"
- puts e.backtrace
- end
-end
-
-def benchmark file, bin
- m = Benchmark.measure{
- `#{bin} #{$opts} #{file}`
- }
- sec = '%.3f' % m.real
- puts " #{sec}"
- sec
-end
-
-def ruby_exec file
- print 'ruby'
- benchmark file, $ruby_program
-end
-
-def matzruby_exec file
- print 'matz'
- rubylib = ENV['RUBYLIB']
- ENV['RUBYLIB'] = ''
- r = benchmark file, $matzruby_program
- ENV['RUBYLIB'] = rubylib
- r
-end
-
-if $0 == __FILE__
- ARGV.each{|arg|
- case arg
- when /\A--ruby=(.+)/
- $ruby_program = $1
- when /\A--matzruby=(.+)/
- $matzruby_program = $1
- when /\A--opts=(.+)/
- $opts = $1
- when /\A(-r|--only-ruby)\z/
- $rubyonly = true
- when /\A(-m|--only-matzruby)\z/
- $matzrubyonly = true
- end
- }
- ARGV.delete_if{|arg|
- /\A-/ =~ arg
- }
-
- puts "MatzRuby:"
- system("#{$matzruby_program} -v")
- puts "Ruby:"
- system("#{$ruby_program} -v")
- puts
-
- if ARGV.empty?
- Dir.glob(File.dirname(__FILE__) + '/bm_*.rb').sort.each{|file|
- bm file
- }
- else
- ARGV.each{|file|
- Dir.glob(File.join(File.dirname(__FILE__), file + '*')){|ef|
- # file = "#{File.dirname(__FILE__)}/#{file}.rb"
- bm ef
- }
- }
- end
-
- puts
- puts "-- benchmark summary ---------------------------"
- $results.each{|res|
- print res.shift, "\t"
- (res||[]).each{|result|
- /([\d\.]+)/ =~ result
- print $1 + "\t" if $1
- }
- puts
- }
-end
-
diff --git a/benchmark/runc.rb b/benchmark/runc.rb
deleted file mode 100644
index 97c5cef045..0000000000
--- a/benchmark/runc.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-#
-#
-
-require 'benchmark'
-require 'rbconfig'
-
-$rubybin = ENV['RUBY'] || RbConfig.ruby
-
-def runfile file
- puts file
- file = File.join(File.dirname($0), 'contrib', file)
- Benchmark.bm{|x|
- x.report('ruby'){
- system("#{$rubybin} #{file}")
- }
- x.report('yarv'){
- system("#{$rubybin} -rite -I.. #{file}")
- }
- }
-end
-
-ARGV.each{|file|
- runfile file
-}
-
-
diff --git a/benchmark/bm_securerandom.rb b/benchmark/securerandom.rb
index a082ea6d5b..a082ea6d5b 100644
--- a/benchmark/bm_securerandom.rb
+++ b/benchmark/securerandom.rb
diff --git a/benchmark/bm_so_ackermann.rb b/benchmark/so_ackermann.rb
index 7db5be9050..7db5be9050 100644
--- a/benchmark/bm_so_ackermann.rb
+++ b/benchmark/so_ackermann.rb
diff --git a/benchmark/bm_so_array.rb b/benchmark/so_array.rb
index 2b8fce8f99..2b8fce8f99 100644
--- a/benchmark/bm_so_array.rb
+++ b/benchmark/so_array.rb
diff --git a/benchmark/bm_so_binary_trees.rb b/benchmark/so_binary_trees.rb
index b1693e4109..b1693e4109 100644
--- a/benchmark/bm_so_binary_trees.rb
+++ b/benchmark/so_binary_trees.rb
diff --git a/benchmark/bm_so_concatenate.rb b/benchmark/so_concatenate.rb
index 873214de7c..873214de7c 100644
--- a/benchmark/bm_so_concatenate.rb
+++ b/benchmark/so_concatenate.rb
diff --git a/benchmark/so_count_words.yml b/benchmark/so_count_words.yml
new file mode 100644
index 0000000000..d0a6c8dd3e
--- /dev/null
+++ b/benchmark/so_count_words.yml
@@ -0,0 +1,66 @@
+prelude: |
+ #!/usr/bin/ruby
+ # -*- mode: ruby -*-
+
+ wc_input_base = <<EOS
+ Subject: Re: Who was Izchak Miller?
+ From: "Jane D. Anonymous" <nobody@yale.edu>
+ Date: 1996/04/28
+ Message-Id: <4lv7bc$oh@news.ycc.yale.edu>
+ References: <317C405E.5DFA@panix.com> <4lk6vl$gde@ns.oar.net>
+ To: 75176.2330@compuserve.com
+ Content-Type: text/plain; charset=us-ascii
+ Organization: Yale University
+ X-Url: news:4lk6vl$gde@ns.oar.net
+ Mime-Version: 1.0
+ Newsgroups: rec.games.roguelike.nethack
+ X-Mailer: Mozilla 1.1N (Macintosh; I; 68K)
+
+ Hello there, Izchak Miller was my father. When I was younger I spent
+ many a night, hunched over the keyboard with a cup of tea, playing
+ nethack with him and my brother. my dad was a philosopher with a strong
+ weakness for fantasy/sci fi. I remember when he started to get involved
+ with the Nethack team- my brother's Dungeons and Dragons monster book
+ found a regular place beside my dad's desk. it's nice to see him living
+ on in the game he loved so much :-).
+ Tamar Miller
+
+ The following is a really long word of 5000 characters:
+
+ wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
+ EOS
+
+ # prepare 'wc.input'
+
+ def prepare_wc_input(wcbase)
+ wcinput = File.join(File.dirname($0), 'wc.input')
+ unless FileTest.exist?(wcinput)
+ data = wcbase.dup
+ 13.times{
+ data << data
+ }
+ open(wcinput, 'w'){|f| f.write data}
+ end
+ end
+
+ prepare_wc_input(wc_input_base)
+
+benchmark:
+ so_count_words: |
+ # $Id: wc-ruby.code,v 1.4 2004/11/13 07:43:32 bfulgham Exp $
+ # http://www.bagley.org/~doug/shootout/
+ # with help from Paul Brannan
+ input = open(File.join(File.dirname($0), 'wc.input'), 'rb')
+
+ nl = nw = nc = 0
+ while true
+ tmp = input.read(4096) or break
+ data = tmp << (input.gets || "")
+ nc += data.length
+ nl += data.count("\n")
+ ((data.strip! || data).tr!("\n", " ") || data).squeeze!
+ nw += data.count(" ") + 1
+ end
+ # STDERR.puts "#{nl} #{nw} #{nc}"
+
+loop_count: 1
diff --git a/benchmark/bm_so_exception.rb b/benchmark/so_exception.rb
index deb003a594..deb003a594 100644
--- a/benchmark/bm_so_exception.rb
+++ b/benchmark/so_exception.rb
diff --git a/benchmark/bm_so_fannkuch.rb b/benchmark/so_fannkuch.rb
index bac5ecd44c..bac5ecd44c 100644
--- a/benchmark/bm_so_fannkuch.rb
+++ b/benchmark/so_fannkuch.rb
diff --git a/benchmark/so_fasta.rb b/benchmark/so_fasta.rb
new file mode 100644
index 0000000000..dcc6b39507
--- /dev/null
+++ b/benchmark/so_fasta.rb
@@ -0,0 +1,81 @@
+# The Computer Language Shootout
+# http://shootout.alioth.debian.org/
+# Contributed by Sokolov Yura
+
+$last = 42.0
+def gen_random(max, im=139968, ia=3877, ic=29573)
+ (max * ($last = ($last * ia + ic) % im)) / im
+end
+
+alu =
+ "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG"+
+ "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA"+
+ "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT"+
+ "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA"+
+ "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG"+
+ "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC"+
+ "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA"
+
+iub = [
+ ["a", 0.27],
+ ["c", 0.12],
+ ["g", 0.12],
+ ["t", 0.27],
+
+ ["B", 0.02],
+ ["D", 0.02],
+ ["H", 0.02],
+ ["K", 0.02],
+ ["M", 0.02],
+ ["N", 0.02],
+ ["R", 0.02],
+ ["S", 0.02],
+ ["V", 0.02],
+ ["W", 0.02],
+ ["Y", 0.02],
+]
+homosapiens = [
+ ["a", 0.3029549426680],
+ ["c", 0.1979883004921],
+ ["g", 0.1975473066391],
+ ["t", 0.3015094502008],
+]
+
+def make_repeat_fasta(id, desc, src, n)
+ puts ">#{id} #{desc}"
+ v = nil
+ width = 60
+ l = src.length
+ s = src * ((n / l) + 1)
+ s.slice!(n, l)
+ puts(s.scan(/.{1,#{width}}/).join("\n"))
+end
+
+def make_random_fasta(id, desc, table, n)
+ puts ">#{id} #{desc}"
+ rand, v = nil,nil
+ width = 60
+ chunk = 1 * width
+ prob = 0.0
+ table.each{|v| v[1]= (prob += v[1])}
+ for i in 1..(n/width)
+ puts((1..width).collect{
+ rand = gen_random(1.0)
+ table.find{|v| v[1]>rand}[0]
+ }.join)
+ end
+ if n%width != 0
+ puts((1..(n%width)).collect{
+ rand = gen_random(1.0)
+ table.find{|v| v[1]>rand}[0]
+ }.join)
+ end
+end
+
+
+n = (ARGV[0] or 250_000).to_i
+
+make_repeat_fasta('ONE', 'Homo sapiens alu', alu, n*2)
+make_random_fasta('TWO', 'IUB ambiguity codes', iub, n*3)
+make_random_fasta('THREE', 'Homo sapiens frequency', homosapiens, n*5)
+
diff --git a/benchmark/so_k_nucleotide.yml b/benchmark/so_k_nucleotide.yml
new file mode 100644
index 0000000000..d7df086c39
--- /dev/null
+++ b/benchmark/so_k_nucleotide.yml
@@ -0,0 +1,155 @@
+prelude: |
+ bm_so_fasta = <<'EOS'
+ # The Computer Language Shootout
+ # http://shootout.alioth.debian.org/
+ # Contributed by Sokolov Yura
+
+ $last = 42.0
+ def gen_random(max, im=139968, ia=3877, ic=29573)
+ (max * ($last = ($last * ia + ic) % im)) / im
+ end
+
+ alu =
+ "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG"+
+ "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA"+
+ "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT"+
+ "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA"+
+ "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG"+
+ "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC"+
+ "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA"
+
+ iub = [
+ ["a", 0.27],
+ ["c", 0.12],
+ ["g", 0.12],
+ ["t", 0.27],
+
+ ["B", 0.02],
+ ["D", 0.02],
+ ["H", 0.02],
+ ["K", 0.02],
+ ["M", 0.02],
+ ["N", 0.02],
+ ["R", 0.02],
+ ["S", 0.02],
+ ["V", 0.02],
+ ["W", 0.02],
+ ["Y", 0.02],
+ ]
+ homosapiens = [
+ ["a", 0.3029549426680],
+ ["c", 0.1979883004921],
+ ["g", 0.1975473066391],
+ ["t", 0.3015094502008],
+ ]
+
+ def make_repeat_fasta(id, desc, src, n)
+ puts ">#{id} #{desc}"
+ v = nil
+ width = 60
+ l = src.length
+ s = src * ((n / l) + 1)
+ s.slice!(n, l)
+ puts(s.scan(/.{1,#{width}}/).join("\n"))
+ end
+
+ def make_random_fasta(id, desc, table, n)
+ puts ">#{id} #{desc}"
+ rand, v = nil,nil
+ width = 60
+ chunk = 1 * width
+ prob = 0.0
+ table.each{|v| v[1]= (prob += v[1])}
+ for i in 1..(n/width)
+ puts((1..width).collect{
+ rand = gen_random(1.0)
+ table.find{|v| v[1]>rand}[0]
+ }.join)
+ end
+ if n%width != 0
+ puts((1..(n%width)).collect{
+ rand = gen_random(1.0)
+ table.find{|v| v[1]>rand}[0]
+ }.join)
+ end
+ end
+
+
+ n = (ARGV[0] or 250_000).to_i
+
+ make_repeat_fasta('ONE', 'Homo sapiens alu', alu, n*2)
+ make_random_fasta('TWO', 'IUB ambiguity codes', iub, n*3)
+ make_random_fasta('THREE', 'Homo sapiens frequency', homosapiens, n*5)
+ EOS
+benchmark:
+ - name: so_k_nucleotide
+ prelude: |
+ script = File.join(File.dirname($0), 'bm_so_fasta.rb')
+ File.write(script, bm_so_fasta)
+
+ def prepare_fasta_output n
+ filebase = File.join(File.dirname($0), 'fasta.output')
+ script = File.join(File.dirname($0), 'bm_so_fasta.rb')
+ file = "#{filebase}.#{n}"
+
+ unless FileTest.exist?(file)
+ STDERR.puts "preparing #{file}"
+
+ open(file, 'w'){|f|
+ ARGV[0] = n
+ $stdout = f
+ load script
+ $stdout = STDOUT
+ }
+ end
+ end
+ prepare_fasta_output(100_000)
+ script: |
+ # The Computer Language Shootout
+ # http://shootout.alioth.debian.org
+ #
+ # contributed by jose fco. gonzalez
+ # modified by Sokolov Yura
+
+ seq = String.new
+
+ def frecuency( seq,length )
+ n, table = seq.length - length + 1, Hash.new(0)
+ f, i = nil, nil
+ (0 ... length).each do |f|
+ (f ... n).step(length) do |i|
+ table[seq[i,length]] += 1
+ end
+ end
+ [n,table]
+
+ end
+
+ def sort_by_freq( seq,length )
+ n,table = frecuency( seq,length )
+ a, b, v = nil, nil, nil
+ table.sort{|a,b| b[1] <=> a[1]}.each do |v|
+ puts "%s %.3f" % [v[0].upcase,((v[1]*100).to_f/n)]
+ end
+ puts
+ end
+
+ def find_seq( seq,s )
+ n,table = frecuency( seq,s.length )
+ puts "#{table[s].to_s}\t#{s.upcase}"
+ end
+
+ input = open(File.join(File.dirname($0), 'fasta.output.100000'), 'rb')
+
+ line = input.gets while line !~ /^>THREE/
+ line = input.gets
+
+ while (line !~ /^>/) & line do
+ seq << line.chomp
+ line = input.gets
+ end
+
+ [1,2].each {|i| sort_by_freq( seq,i ) }
+
+ %w(ggt ggta ggtatt ggtattttaatt ggtattttaatttatagt).each{|s| find_seq( seq,s) }
+ loop_count: 1
diff --git a/benchmark/bm_so_lists.rb b/benchmark/so_lists.rb
index e8f4a2a5f7..e8f4a2a5f7 100644
--- a/benchmark/bm_so_lists.rb
+++ b/benchmark/so_lists.rb
diff --git a/benchmark/bm_so_mandelbrot.rb b/benchmark/so_mandelbrot.rb
index 76331c64b8..76331c64b8 100644
--- a/benchmark/bm_so_mandelbrot.rb
+++ b/benchmark/so_mandelbrot.rb
diff --git a/benchmark/bm_so_matrix.rb b/benchmark/so_matrix.rb
index e2c5c8e559..e2c5c8e559 100644
--- a/benchmark/bm_so_matrix.rb
+++ b/benchmark/so_matrix.rb
diff --git a/benchmark/so_meteor_contest.rb b/benchmark/so_meteor_contest.rb
new file mode 100644
index 0000000000..8c136baa6c
--- /dev/null
+++ b/benchmark/so_meteor_contest.rb
@@ -0,0 +1,563 @@
+#!/usr/bin/env ruby
+#
+# The Computer Language Shootout
+# http://shootout.alioth.debian.org
+# contributed by Kevin Barnes (Ruby novice)
+
+# PROGRAM: the main body is at the bottom.
+# 1) read about the problem here: http://www-128.ibm.com/developerworks/java/library/j-javaopt/
+# 2) see how I represent a board as a bitmask by reading the blank_board comments
+# 3) read as your mental paths take you
+
+def print *args
+end
+
+# class to represent all information about a particular rotation of a particular piece
+class Rotation
+ # an array (by location) containing a bit mask for how the piece maps at the given location.
+ # if the rotation is invalid at that location the mask will contain false
+ attr_reader :start_masks
+
+ # maps a direction to a relative location. these differ depending on whether it is an even or
+ # odd row being mapped from
+ @@rotation_even_adder = { :west => -1, :east => 1, :nw => -7, :ne => -6, :sw => 5, :se => 6 }
+ @@rotation_odd_adder = { :west => -1, :east => 1, :nw => -6, :ne => -5, :sw => 6, :se => 7 }
+
+ def initialize( directions )
+ @even_offsets, @odd_offsets = normalize_offsets( get_values( directions ))
+
+ @even_mask = mask_for_offsets( @even_offsets)
+ @odd_mask = mask_for_offsets( @odd_offsets)
+
+ @start_masks = Array.new(60)
+
+ # create the rotational masks by placing the base mask at the location and seeing if
+ # 1) it overlaps the boundaries and 2) it produces a prunable board. if either of these
+ # is true the piece cannot be placed
+ 0.upto(59) do | offset |
+ mask = is_even(offset) ? (@even_mask << offset) : (@odd_mask << offset)
+ if (blank_board & mask == 0 && !prunable(blank_board | mask, 0, true)) then
+ imask = compute_required( mask, offset)
+ @start_masks[offset] = [ mask, imask, imask | mask ]
+ else
+ @start_masks[offset] = false
+ end
+ end
+ end
+
+ def compute_required( mask, offset )
+ board = blank_board
+ 0.upto(offset) { | i | board |= 1 << i }
+ board |= mask
+ return 0 if (!prunable(board | mask, offset))
+ board = flood_fill(board,58)
+ count = 0
+ imask = 0
+ 0.upto(59) do | i |
+ if (board[i] == 0) then
+ imask |= (1 << i)
+ count += 1
+ end
+ end
+ (count > 0 && count < 5) ? imask : 0
+ end
+
+ def flood_fill( board, location)
+ return board if (board[location] == 1)
+ board |= 1 << location
+ row, col = location.divmod(6)
+ board = flood_fill( board, location - 1) if (col > 0)
+ board = flood_fill( board, location + 1) if (col < 4)
+ if (row % 2 == 0) then
+ board = flood_fill( board, location - 7) if (col > 0 && row > 0)
+ board = flood_fill( board, location - 6) if (row > 0)
+ board = flood_fill( board, location + 6) if (row < 9)
+ board = flood_fill( board, location + 5) if (col > 0 && row < 9)
+ else
+ board = flood_fill( board, location - 5) if (col < 4 && row > 0)
+ board = flood_fill( board, location - 6) if (row > 0)
+ board = flood_fill( board, location + 6) if (row < 9)
+ board = flood_fill( board, location + 7) if (col < 4 && row < 9)
+ end
+ board
+ end
+
+ # given a location, produces a list of relative locations covered by the piece at this rotation
+ def offsets( location)
+ if is_even( location) then
+ @even_offsets.collect { | value | value + location }
+ else
+ @odd_offsets.collect { | value | value + location }
+ end
+ end
+
+ # returns a set of offsets relative to the top-left most piece of the rotation (by even or odd rows)
+ # this is hard to explain. imagine we have this partial board:
+ # 0 0 0 0 0 x [positions 0-5]
+ # 0 0 1 1 0 x [positions 6-11]
+ # 0 0 1 0 0 x [positions 12-17]
+ # 0 1 0 0 0 x [positions 18-23]
+ # 0 1 0 0 0 x [positions 24-29]
+ # 0 0 0 0 0 x [positions 30-35]
+ # ...
+ # The top-left of the piece is at position 8, the
+ # board would be passed as a set of positions (values array) containing [8,9,14,19,25] not necessarily in that
+ # sorted order. Since that array starts on an odd row, the offsets for an odd row are: [0,1,6,11,17] obtained
+ # by subtracting 8 from everything. Now imagine the piece shifted up and to the right so it's on an even row:
+ # 0 0 0 1 1 x [positions 0-5]
+ # 0 0 1 0 0 x [positions 6-11]
+ # 0 0 1 0 0 x [positions 12-17]
+ # 0 1 0 0 0 x [positions 18-23]
+ # 0 0 0 0 0 x [positions 24-29]
+ # 0 0 0 0 0 x [positions 30-35]
+ # ...
+ # Now the positions are [3,4,8,14,19] which after subtracting the lowest value (3) gives [0,1,5,11,16] thus, the
+ # offsets for this particular piece are (in even, odd order) [0,1,5,11,16],[0,1,6,11,17] which is what
+ # this function would return
+ def normalize_offsets( values)
+ min = values.min
+ even_min = is_even(min)
+ other_min = even_min ? min + 6 : min + 7
+ other_values = values.collect do | value |
+ if is_even(value) then
+ value + 6 - other_min
+ else
+ value + 7 - other_min
+ end
+ end
+ values.collect! { | value | value - min }
+
+ if even_min then
+ [values, other_values]
+ else
+ [other_values, values]
+ end
+ end
+
+ # produce a bitmask representation of an array of offset locations
+ def mask_for_offsets( offsets )
+ mask = 0
+ offsets.each { | value | mask = mask + ( 1 << value ) }
+ mask
+ end
+
+ # finds a "safe" position that a position as described by a list of directions can be placed
+ # without falling off any edge of the board. the values returned a location to place the first piece
+ # at so it will fit after making the described moves
+ def start_adjust( directions )
+ south = east = 0;
+ directions.each do | direction |
+ east += 1 if ( direction == :sw || direction == :nw || direction == :west )
+ south += 1 if ( direction == :nw || direction == :ne )
+ end
+ south * 6 + east
+ end
+
+ # given a set of directions places the piece (as defined by a set of directions) on the board at
+ # a location that will not take it off the edge
+ def get_values( directions )
+ start = start_adjust(directions)
+ values = [ start ]
+ directions.each do | direction |
+ if (start % 12 >= 6) then
+ start += @@rotation_odd_adder[direction]
+ else
+ start += @@rotation_even_adder[direction]
+ end
+ values += [ start ]
+ end
+
+ # some moves take you back to an existing location, we'll strip duplicates
+ values.uniq
+ end
+end
+
+# describes a piece and caches information about its rotations to as to be efficient for iteration
+# ATTRIBUTES:
+# rotations -- all the rotations of the piece
+# type -- a numeic "name" of the piece
+# masks -- an array by location of all legal rotational masks (a n inner array) for that location
+# placed -- the mask that this piece was last placed at (not a location, but the actual mask used)
+class Piece
+ attr_reader :rotations, :type, :masks
+ attr_accessor :placed
+
+ # transform hashes that change one direction into another when you either flip or rotate a set of directions
+ @@flip_converter = { :west => :west, :east => :east, :nw => :sw, :ne => :se, :sw => :nw, :se => :ne }
+ @@rotate_converter = { :west => :nw, :east => :se, :nw => :ne, :ne => :east, :sw => :west, :se => :sw }
+
+ def initialize( directions, type )
+ @type = type
+ @rotations = Array.new();
+ @map = {}
+
+ generate_rotations( directions )
+ directions.collect! { | value | @@flip_converter[value] }
+ generate_rotations( directions )
+
+ # creates the masks AND a map that returns [location, rotation] for any given mask
+ # this is used when a board is found and we want to draw it, otherwise the map is unused
+ @masks = Array.new();
+ 0.upto(59) do | i |
+ even = true
+ @masks[i] = @rotations.collect do | rotation |
+ mask = rotation.start_masks[i]
+ @map[mask[0]] = [ i, rotation ] if (mask)
+ mask || nil
+ end
+ @masks[i].compact!
+ end
+ end
+
+ # rotates a set of directions through all six angles and adds a Rotation to the list for each one
+ def generate_rotations( directions )
+ 6.times do
+ rotations.push( Rotation.new(directions))
+ directions.collect! { | value | @@rotate_converter[value] }
+ end
+ end
+
+ # given a board string, adds this piece to the board at whatever location/rotation
+ # important: the outbound board string is 5 wide, the normal location notation is six wide (padded)
+ def fill_string( board_string)
+ location, rotation = @map[@placed]
+ rotation.offsets(location).each do | offset |
+ row, col = offset.divmod(6)
+ board_string[ row*5 + col, 1 ] = @type.to_s
+ end
+ end
+end
+
+# a blank bit board having this form:
+#
+# 0 0 0 0 0 1
+# 0 0 0 0 0 1
+# 0 0 0 0 0 1
+# 0 0 0 0 0 1
+# 0 0 0 0 0 1
+# 0 0 0 0 0 1
+# 0 0 0 0 0 1
+# 0 0 0 0 0 1
+# 0 0 0 0 0 1
+# 0 0 0 0 0 1
+# 1 1 1 1 1 1
+#
+# where left lest significant bit is the top left and the most significant is the lower right
+# the actual board only consists of the 0 places, the 1 places are blockers to keep things from running
+# off the edges or bottom
+def blank_board
+ 0b111111100000100000100000100000100000100000100000100000100000100000
+end
+
+def full_board
+ 0b111111111111111111111111111111111111111111111111111111111111111111
+end
+
+# determines if a location (bit position) is in an even row
+def is_even( location)
+ (location % 12) < 6
+end
+
+# support function that create three utility maps:
+# $converter -- for each row an array that maps a five bit row (via array mapping)
+# to the a five bit representation of the bits below it
+# $bit_count -- maps a five bit row (via array mapping) to the number of 1s in the row
+# @@new_regions -- maps a five bit row (via array mapping) to an array of "region" arrays
+# a region array has three values the first is a mask of bits in the region,
+# the second is the count of those bits and the third is identical to the first
+# examples:
+# 0b10010 => [ 0b01100, 2, 0b01100 ], [ 0b00001, 1, 0b00001]
+# 0b01010 => [ 0b10000, 1, 0b10000 ], [ 0b00100, 1, 0b00100 ], [ 0b00001, 1, 0b00001]
+# 0b10001 => [ 0b01110, 3, 0b01110 ]
+def create_collector_support
+ odd_map = [0b11, 0b110, 0b1100, 0b11000, 0b10000]
+ even_map = [0b1, 0b11, 0b110, 0b1100, 0b11000]
+
+ all_odds = Array.new(0b100000)
+ all_evens = Array.new(0b100000)
+ bit_counts = Array.new(0b100000)
+ new_regions = Array.new(0b100000)
+ 0.upto(0b11111) do | i |
+ bit_count = odd = even = 0
+ 0.upto(4) do | bit |
+ if (i[bit] == 1) then
+ bit_count += 1
+ odd |= odd_map[bit]
+ even |= even_map[bit]
+ end
+ end
+ all_odds[i] = odd
+ all_evens[i] = even
+ bit_counts[i] = bit_count
+ new_regions[i] = create_regions( i)
+ end
+
+ $converter = []
+ 10.times { | row | $converter.push((row % 2 == 0) ? all_evens : all_odds) }
+ $bit_counts = bit_counts
+ $regions = new_regions.collect { | set | set.collect { | value | [ value, bit_counts[value], value] } }
+end
+
+# determines if a board is punable, meaning that there is no possibility that it
+# can be filled up with pieces. A board is prunable if there is a grouping of unfilled spaces
+# that are not a multiple of five. The following board is an example of a prunable board:
+# 0 0 1 0 0
+# 0 1 0 0 0
+# 1 1 0 0 0
+# 0 1 0 0 0
+# 0 0 0 0 0
+# ...
+#
+# This board is prunable because the top left corner is only 3 bits in area, no piece will ever fit it
+# parameters:
+# board -- an initial bit board (6 bit padded rows, see blank_board for format)
+# location -- starting location, everything above and to the left is already full
+# slotting -- set to true only when testing initial pieces, when filling normally
+# additional assumptions are possible
+#
+# Algorithm:
+# The algorithm starts at the top row (as determined by location) and iterates a row at a time
+# maintainng counts of active open areas (kept in the collector array) each collector contains
+# three values at the start of an iteration:
+# 0: mask of bits that would be adjacent to the collector in this row
+# 1: the number of bits collected so far
+# 2: a scratch space starting as zero, but used during the computation to represent
+# the empty bits in the new row that are adjacent (position 0)
+# The exact procedure is described in-code
+def prunable( board, location, slotting = false)
+ collectors = []
+ # loop across the rows
+ (location / 6).to_i.upto(9) do | row_on |
+ # obtain a set of regions representing the bits of the current row.
+ regions = $regions[(board >> (row_on * 6)) & 0b11111]
+ converter = $converter[row_on]
+
+ # track the number of collectors at the start of the cycle so that
+ # we don't compute against newly created collectors, only existing collectors
+ initial_collector_count = collectors.length
+
+ # loop against the regions. For each region of the row
+ # we will see if it connects to one or more existing collectors.
+ # if it connects to 1 collector, the bits from the region are added to the
+ # bits of the collector and the mask is placed in collector[2]
+ # If the region overlaps more than one collector then all the collectors
+ # it overlaps with are merged into the first one (the others are set to nil in the array)
+ # if NO collectors are found then the region is copied as a new collector
+ regions.each do | region |
+ collector_found = nil
+ region_mask = region[2]
+ initial_collector_count.times do | collector_num |
+ collector = collectors[collector_num]
+ if (collector) then
+ collector_mask = collector[0]
+ if (collector_mask & region_mask != 0) then
+ if (collector_found) then
+ collector_found[0] |= collector_mask
+ collector_found[1] += collector[1]
+ collector_found[2] |= collector[2]
+ collectors[collector_num] = nil
+ else
+ collector_found = collector
+ collector[1] += region[1]
+ collector[2] |= region_mask
+ end
+ end
+ end
+ end
+ if (collector_found == nil) then
+ collectors.push(Array.new(region))
+ end
+ end
+
+ # check the existing collectors, if any collector overlapped no bits in the region its [2] value will
+ # be zero. The size of any such reaason is tested if it is not a multiple of five true is returned since
+ # the board is prunable. if it is a multiple of five it is removed.
+ # Collector that are still active have a new adjacent value [0] set based n the matched bits
+ # and have [2] cleared out for the next cycle.
+ collectors.length.times do | collector_num |
+ collector = collectors[collector_num]
+ if (collector) then
+ if (collector[2] == 0) then
+ return true if (collector[1] % 5 != 0)
+ collectors[collector_num] = nil
+ else
+ # if a collector matches all bits in the row then we can return unprunable early for the
+ # following reasons:
+ # 1) there can be no more unavailable bits bince we fill from the top left downward
+ # 2) all previous regions have been closed or joined so only this region can fail
+ # 3) this region must be good since there can never be only 1 region that is nuot
+ # a multiple of five
+ # this rule only applies when filling normally, so we ignore the rule if we are "slotting"
+ # in pieces to see what configurations work for them (the only other time this algorithm is used).
+ return false if (collector[2] == 0b11111 && !slotting)
+ collector[0] = converter[collector[2]]
+ collector[2] = 0
+ end
+ end
+ end
+
+ # get rid of all the empty converters for the next round
+ collectors.compact!
+ end
+ return false if (collectors.length <= 1) # 1 collector or less and the region is fine
+ collectors.any? { | collector | (collector[1] % 5) != 0 } # more than 1 and we test them all for bad size
+end
+
+# creates a region given a row mask. see prunable for what a "region" is
+def create_regions( value )
+ regions = []
+ cur_region = 0
+ 5.times do | bit |
+ if (value[bit] == 0) then
+ cur_region |= 1 << bit
+ else
+ if (cur_region != 0 ) then
+ regions.push( cur_region)
+ cur_region = 0;
+ end
+ end
+ end
+ regions.push(cur_region) if (cur_region != 0)
+ regions
+end
+
+# find up to the counted number of solutions (or all solutions) and prints the final result
+def find_all
+ find_top( 1)
+ find_top( 0)
+ print_results
+end
+
+# show the board
+def print_results
+ print "#{@boards_found} solutions found\n\n"
+ print_full_board( @min_board)
+ print "\n"
+ print_full_board( @max_board)
+ print "\n"
+end
+
+# finds solutions. This special version of the main function is only used for the top level
+# the reason for it is basically to force a particular ordering on how the rotations are tested for
+# the first piece. It is called twice, first looking for placements of the odd rotations and then
+# looking for placements of the even locations.
+#
+# WHY?
+# Since any found solution has an inverse we want to maximize finding solutions that are not already found
+# as an inverse. The inverse will ALWAYS be 3 one of the piece configurations that is exactly 3 rotations away
+# (an odd number). Checking even vs odd then produces a higher probability of finding more pieces earlier
+# in the cycle. We still need to keep checking all the permutations, but our probability of finding one will
+# diminsh over time. Since we are TOLD how many to search for this lets us exit before checking all pieces
+# this bennifit is very great when seeking small numbers of solutions and is 0 when looking for more than the
+# maximum number
+def find_top( rotation_skip)
+ board = blank_board
+ (@pieces.length-1).times do
+ piece = @pieces.shift
+ piece.masks[0].each do | mask, imask, cmask |
+ if ((rotation_skip += 1) % 2 == 0) then
+ piece.placed = mask
+ find( 1, 1, board | mask)
+ end
+ end
+ @pieces.push(piece)
+ end
+ piece = @pieces.shift
+ @pieces.push(piece)
+end
+
+# the normail find routine, iterates through the available pieces, checks all rotations at the current location
+# and adds any boards found. depth is achieved via recursion. the overall approach is described
+# here: http://www-128.ibm.com/developerworks/java/library/j-javaopt/
+# parameters:
+# start_location -- where to start looking for place for the next piece at
+# placed -- number of pieces placed
+# board -- current state of the board
+#
+# see in-code comments
+def find( start_location, placed, board)
+ # find the next location to place a piece by looking for an empty bit
+ while board[start_location] == 1
+ start_location += 1
+ end
+
+ @pieces.length.times do
+ piece = @pieces.shift
+ piece.masks[start_location].each do | mask, imask, cmask |
+ if ( board & cmask == imask) then
+ piece.placed = mask
+ if (placed == 9) then
+ add_board
+ else
+ find( start_location + 1, placed + 1, board | mask)
+ end
+ end
+ end
+ @pieces.push(piece)
+ end
+end
+
+# print the board
+def print_full_board( board_string)
+ 10.times do | row |
+ print " " if (row % 2 == 1)
+ 5.times do | col |
+ print "#{board_string[row*5 + col,1]} "
+ end
+ print "\n"
+ end
+end
+
+# when a board is found we "draw it" into a string and then flip that string, adding both to
+# the list (hash) of solutions if they are unique.
+def add_board
+ board_string = "99999999999999999999999999999999999999999999999999"
+ @all_pieces.each { | piece | piece.fill_string( board_string ) }
+ save( board_string)
+ save( board_string.reverse)
+end
+
+# adds a board string to the list (if new) and updates the current best/worst board
+def save( board_string)
+ if (@all_boards[board_string] == nil) then
+ @min_board = board_string if (board_string < @min_board)
+ @max_board = board_string if (board_string > @max_board)
+ @all_boards.store(board_string,true)
+ @boards_found += 1
+
+ # the exit motif is a time saver. Ideally the function should return, but those tests
+ # take noticeable time (performance).
+ if (@boards_found == @stop_count) then
+ print_results
+ exit(0)
+ end
+ end
+end
+
+
+##
+## MAIN BODY :)
+##
+create_collector_support
+@pieces = [
+ Piece.new( [ :nw, :ne, :east, :east ], 2),
+ Piece.new( [ :ne, :se, :east, :ne ], 7),
+ Piece.new( [ :ne, :east, :ne, :nw ], 1),
+ Piece.new( [ :east, :sw, :sw, :se ], 6),
+ Piece.new( [ :east, :ne, :se, :ne ], 5),
+ Piece.new( [ :east, :east, :east, :se ], 0),
+ Piece.new( [ :ne, :nw, :se, :east, :se ], 4),
+ Piece.new( [ :se, :se, :se, :west ], 9),
+ Piece.new( [ :se, :se, :east, :se ], 8),
+ Piece.new( [ :east, :east, :sw, :se ], 3)
+ ];
+
+@all_pieces = Array.new( @pieces)
+
+@min_board = "99999999999999999999999999999999999999999999999999"
+@max_board = "00000000000000000000000000000000000000000000000000"
+@stop_count = ARGV[0].to_i || 2089
+@all_boards = {}
+@boards_found = 0
+
+find_all ######## DO IT!!!
diff --git a/benchmark/bm_so_nbody.rb b/benchmark/so_nbody.rb
index d6c5bb9e61..d6c5bb9e61 100644
--- a/benchmark/bm_so_nbody.rb
+++ b/benchmark/so_nbody.rb
diff --git a/benchmark/bm_so_nested_loop.rb b/benchmark/so_nested_loop.rb
index a0513f8c47..a0513f8c47 100644
--- a/benchmark/bm_so_nested_loop.rb
+++ b/benchmark/so_nested_loop.rb
diff --git a/benchmark/bm_so_nsieve.rb b/benchmark/so_nsieve.rb
index a65cc78233..a65cc78233 100644
--- a/benchmark/bm_so_nsieve.rb
+++ b/benchmark/so_nsieve.rb
diff --git a/benchmark/bm_so_nsieve_bits.rb b/benchmark/so_nsieve_bits.rb
index 6f958ee44e..6f958ee44e 100644
--- a/benchmark/bm_so_nsieve_bits.rb
+++ b/benchmark/so_nsieve_bits.rb
diff --git a/benchmark/bm_so_object.rb b/benchmark/so_object.rb
index e8607c7199..e8607c7199 100644
--- a/benchmark/bm_so_object.rb
+++ b/benchmark/so_object.rb
diff --git a/benchmark/bm_so_partial_sums.rb b/benchmark/so_partial_sums.rb
index 630b45cb8d..630b45cb8d 100644
--- a/benchmark/bm_so_partial_sums.rb
+++ b/benchmark/so_partial_sums.rb
diff --git a/benchmark/so_pidigits.rb b/benchmark/so_pidigits.rb
new file mode 100644
index 0000000000..9a537b2d1c
--- /dev/null
+++ b/benchmark/so_pidigits.rb
@@ -0,0 +1,92 @@
+# The Great Computer Language Shootout
+# http://shootout.alioth.debian.org/
+#
+# contributed by Gabriele Renzi
+
+class PiDigitSpigot
+
+ def initialize()
+ @z = Transformation.new 1,0,0,1
+ @x = Transformation.new 0,0,0,0
+ @inverse = Transformation.new 0,0,0,0
+ end
+
+ def next!
+ @y = @z.extract(3)
+ if safe? @y
+ @z = produce(@y)
+ @y
+ else
+ @z = consume @x.next!()
+ next!()
+ end
+ end
+
+ def safe?(digit)
+ digit == @z.extract(4)
+ end
+
+ def produce(i)
+ @inverse.qrst(10,-10*i,0,1).compose(@z)
+ end
+
+ def consume(a)
+ @z.compose(a)
+ end
+end
+
+
+class Transformation
+ attr_reader :q, :r, :s, :t
+ def initialize(q, r, s, t)
+ @q,@r,@s,@t,@k = q,r,s,t,0
+ end
+
+ def next!()
+ @q = @k = @k + 1
+ @r = 4 * @k + 2
+ @s = 0
+ @t = 2 * @k + 1
+ self
+ end
+
+ def extract(j)
+ (@q * j + @r) / (@s * j + @t)
+ end
+
+ def compose(a)
+ self.class.new( @q * a.q,
+ @q * a.r + r * a.t,
+ @s * a.q + t * a.s,
+ @s * a.r + t * a.t
+ )
+ end
+
+ def qrst *args
+ initialize *args
+ self
+ end
+
+
+end
+
+
+WIDTH = 10
+n = 2_500 # Integer(ARGV[0])
+j = 0
+
+digits = PiDigitSpigot.new
+
+while n > 0
+ if n >= WIDTH
+ WIDTH.times {print digits.next!}
+ j += WIDTH
+ else
+ n.times {print digits.next!}
+ (WIDTH-n).times {print " "}
+ j += n
+ end
+ puts "\t:"+j.to_s
+ n -= WIDTH
+end
+
diff --git a/benchmark/bm_so_random.rb b/benchmark/so_random.rb
index a66b9e8e63..a66b9e8e63 100644
--- a/benchmark/bm_so_random.rb
+++ b/benchmark/so_random.rb
diff --git a/benchmark/so_reverse_complement.yml b/benchmark/so_reverse_complement.yml
new file mode 100644
index 0000000000..de05eedfc4
--- /dev/null
+++ b/benchmark/so_reverse_complement.yml
@@ -0,0 +1,137 @@
+prelude: |
+ bm_so_fasta = <<'EOS'
+ # The Computer Language Shootout
+ # http://shootout.alioth.debian.org/
+ # Contributed by Sokolov Yura
+
+ $last = 42.0
+ def gen_random(max, im=139968, ia=3877, ic=29573)
+ (max * ($last = ($last * ia + ic) % im)) / im
+ end
+
+ alu =
+ "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG"+
+ "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA"+
+ "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT"+
+ "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA"+
+ "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG"+
+ "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC"+
+ "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA"
+
+ iub = [
+ ["a", 0.27],
+ ["c", 0.12],
+ ["g", 0.12],
+ ["t", 0.27],
+
+ ["B", 0.02],
+ ["D", 0.02],
+ ["H", 0.02],
+ ["K", 0.02],
+ ["M", 0.02],
+ ["N", 0.02],
+ ["R", 0.02],
+ ["S", 0.02],
+ ["V", 0.02],
+ ["W", 0.02],
+ ["Y", 0.02],
+ ]
+ homosapiens = [
+ ["a", 0.3029549426680],
+ ["c", 0.1979883004921],
+ ["g", 0.1975473066391],
+ ["t", 0.3015094502008],
+ ]
+
+ def make_repeat_fasta(id, desc, src, n)
+ puts ">#{id} #{desc}"
+ v = nil
+ width = 60
+ l = src.length
+ s = src * ((n / l) + 1)
+ s.slice!(n, l)
+ puts(s.scan(/.{1,#{width}}/).join("\n"))
+ end
+
+ def make_random_fasta(id, desc, table, n)
+ puts ">#{id} #{desc}"
+ rand, v = nil,nil
+ width = 60
+ chunk = 1 * width
+ prob = 0.0
+ table.each{|v| v[1]= (prob += v[1])}
+ for i in 1..(n/width)
+ puts((1..width).collect{
+ rand = gen_random(1.0)
+ table.find{|v| v[1]>rand}[0]
+ }.join)
+ end
+ if n%width != 0
+ puts((1..(n%width)).collect{
+ rand = gen_random(1.0)
+ table.find{|v| v[1]>rand}[0]
+ }.join)
+ end
+ end
+
+
+ n = (ARGV[0] or 250_000).to_i
+
+ make_repeat_fasta('ONE', 'Homo sapiens alu', alu, n*2)
+ make_random_fasta('TWO', 'IUB ambiguity codes', iub, n*3)
+ make_random_fasta('THREE', 'Homo sapiens frequency', homosapiens, n*5)
+ EOS
+benchmark:
+ - name: so_reverse_complement
+ prelude: |
+ script = File.join(File.dirname($0), 'bm_so_fasta.rb')
+ File.write(script, bm_so_fasta)
+
+ def prepare_fasta_output n
+ filebase = File.join(File.dirname($0), 'fasta.output')
+ script = File.join(File.dirname($0), 'bm_so_fasta.rb')
+ file = "#{filebase}.#{n}"
+
+ unless FileTest.exist?(file)
+ STDERR.puts "preparing #{file}"
+
+ open(file, 'w'){|f|
+ ARGV[0] = n
+ $stdout = f
+ load script
+ $stdout = STDOUT
+ }
+ end
+ end
+ prepare_fasta_output(2_500_000)
+ script: |
+ # The Great Computer Language Shootout
+ # http://shootout.alioth.debian.org/
+ #
+ # Contributed by Peter Bjarke Olsen
+ # Modified by Doug King
+
+ seq=Array.new
+
+ def revcomp(seq)
+ seq.reverse!.tr!('wsatugcyrkmbdhvnATUGCYRKMBDHVN','WSTAACGRYMKVHDBNTAACGRYMKVHDBN')
+ stringlen=seq.length
+ 0.step(stringlen-1,60) {|x| print seq.slice(x,60) , "\n"}
+ end
+
+ input = open(File.join(File.dirname($0), 'fasta.output.2500000'), 'rb')
+
+ while input.gets
+ if $_ =~ />/
+ if seq.length != 0
+ revcomp(seq.join)
+ seq=Array.new
+ end
+ puts $_
+ else
+ $_.sub(/\n/,'')
+ seq.push $_
+ end
+ end
+ revcomp(seq.join)
+ loop_count: 1
diff --git a/benchmark/bm_so_sieve.rb b/benchmark/so_sieve.rb
index 43dc302648..43dc302648 100644
--- a/benchmark/bm_so_sieve.rb
+++ b/benchmark/so_sieve.rb
diff --git a/benchmark/bm_so_spectralnorm.rb b/benchmark/so_spectralnorm.rb
index 6b97206689..6b97206689 100644
--- a/benchmark/bm_so_spectralnorm.rb
+++ b/benchmark/so_spectralnorm.rb
diff --git a/benchmark/string_index.rb b/benchmark/string_index.rb
new file mode 100644
index 0000000000..7783111082
--- /dev/null
+++ b/benchmark/string_index.rb
@@ -0,0 +1,3 @@
+str1 = "ã‚" * 1024 + "ã„" # not single byte optimizable
+str2 = "ã„"
+100_000.times { str1.index(str2) }
diff --git a/benchmark/string_scan_re.rb b/benchmark/string_scan_re.rb
new file mode 100644
index 0000000000..b0d60201a9
--- /dev/null
+++ b/benchmark/string_scan_re.rb
@@ -0,0 +1,2 @@
+str = Array.new(1_000, 'abc').join(',')
+1_000.times { str.scan(/abc/) }
diff --git a/benchmark/string_scan_str.rb b/benchmark/string_scan_str.rb
new file mode 100644
index 0000000000..42440bd948
--- /dev/null
+++ b/benchmark/string_scan_str.rb
@@ -0,0 +1,2 @@
+str = Array.new(1_000, 'abc').join(',')
+1_000.times { str.scan('abc') }
diff --git a/benchmark/time_subsec.rb b/benchmark/time_subsec.rb
new file mode 100644
index 0000000000..505021c701
--- /dev/null
+++ b/benchmark/time_subsec.rb
@@ -0,0 +1,2 @@
+t = Time.now
+4000000.times { t.subsec }
diff --git a/benchmark/vm1_attr_ivar.yml b/benchmark/vm1_attr_ivar.yml
new file mode 100644
index 0000000000..f714dd9bd9
--- /dev/null
+++ b/benchmark/vm1_attr_ivar.yml
@@ -0,0 +1,14 @@
+prelude: |
+ class C
+ attr_reader :a, :b
+ def initialize
+ @a = nil
+ @b = nil
+ end
+ end
+ obj = C.new
+benchmark:
+ vm1_attr_ivar: |
+ j = obj.a
+ k = obj.b
+loop_count: 30000000
diff --git a/benchmark/vm1_attr_ivar_set.yml b/benchmark/vm1_attr_ivar_set.yml
new file mode 100644
index 0000000000..f383e59ef4
--- /dev/null
+++ b/benchmark/vm1_attr_ivar_set.yml
@@ -0,0 +1,14 @@
+prelude: |
+ class C
+ attr_accessor :a, :b
+ def initialize
+ @a = nil
+ @b = nil
+ end
+ end
+ obj = C.new
+benchmark:
+ vm1_attr_ivar_set: |
+ obj.a = 1
+ obj.b = 2
+loop_count: 30000000
diff --git a/benchmark/vm1_block.yml b/benchmark/vm1_block.yml
new file mode 100644
index 0000000000..ac7c940f93
--- /dev/null
+++ b/benchmark/vm1_block.yml
@@ -0,0 +1,9 @@
+prelude: |
+ def m
+ yield
+ end
+benchmark:
+ vm1_block: |
+ m{
+ }
+loop_count: 30000000
diff --git a/benchmark/vm1_blockparam.yml b/benchmark/vm1_blockparam.yml
new file mode 100644
index 0000000000..947b8c53d5
--- /dev/null
+++ b/benchmark/vm1_blockparam.yml
@@ -0,0 +1,7 @@
+prelude: |
+ def m &b
+ end
+benchmark:
+ vm1_blockparam: |
+ m{}
+loop_count: 30000000
diff --git a/benchmark/vm1_blockparam_call.yml b/benchmark/vm1_blockparam_call.yml
new file mode 100644
index 0000000000..e2817a3ce2
--- /dev/null
+++ b/benchmark/vm1_blockparam_call.yml
@@ -0,0 +1,8 @@
+prelude: |
+ def m &b
+ b.call
+ end
+benchmark:
+ vm1_blockparam_call: |
+ m{}
+loop_count: 30000000
diff --git a/benchmark/vm1_blockparam_pass.yml b/benchmark/vm1_blockparam_pass.yml
new file mode 100644
index 0000000000..ca1bef3369
--- /dev/null
+++ b/benchmark/vm1_blockparam_pass.yml
@@ -0,0 +1,12 @@
+prelude: |
+ def bp_yield
+ yield
+ end
+
+ def bp_pass &b
+ bp_yield &b
+ end
+benchmark:
+ vm1_blockparam_pass: |
+ bp_pass{}
+loop_count: 30000000
diff --git a/benchmark/vm1_blockparam_yield.yml b/benchmark/vm1_blockparam_yield.yml
new file mode 100644
index 0000000000..56ae617798
--- /dev/null
+++ b/benchmark/vm1_blockparam_yield.yml
@@ -0,0 +1,8 @@
+prelude: |
+ def bp_yield &b
+ yield
+ end
+benchmark:
+ vm1_blockparam_yield: |
+ bp_yield{}
+loop_count: 30000000
diff --git a/benchmark/vm1_const.yml b/benchmark/vm1_const.yml
new file mode 100644
index 0000000000..b98db1545c
--- /dev/null
+++ b/benchmark/vm1_const.yml
@@ -0,0 +1,7 @@
+prelude: |
+ Const = 1
+benchmark:
+ vm1_const: |
+ j = Const
+ k = Const
+loop_count: 30000000
diff --git a/benchmark/vm1_ensure.yml b/benchmark/vm1_ensure.yml
new file mode 100644
index 0000000000..afbbe38bec
--- /dev/null
+++ b/benchmark/vm1_ensure.yml
@@ -0,0 +1,14 @@
+# Not utilizing loop_count since using it for this is too unstable for now
+benchmark:
+ vm1_ensure: |
+ i = 0
+ while i<30_000_000
+ i += 1
+ begin
+ begin
+ ensure
+ end
+ ensure
+ end
+ end
+loop_count: 1
diff --git a/benchmark/vm1_float_simple.yml b/benchmark/vm1_float_simple.yml
new file mode 100644
index 0000000000..4e9ad1852b
--- /dev/null
+++ b/benchmark/vm1_float_simple.yml
@@ -0,0 +1,8 @@
+prelude: |
+ f = 0.0
+benchmark:
+ vm1_float_simple: |
+ f += 0.1; f -= 0.1
+ f += 0.1; f -= 0.1
+ f += 0.1; f -= 0.1
+loop_count: 30000000
diff --git a/benchmark/vm1_gc_short_lived.yml b/benchmark/vm1_gc_short_lived.yml
new file mode 100644
index 0000000000..8fdcb7371d
--- /dev/null
+++ b/benchmark/vm1_gc_short_lived.yml
@@ -0,0 +1,9 @@
+benchmark:
+ vm1_gc_short_lived: |
+ a = '' # short-lived String
+ b = ''
+ c = ''
+ d = ''
+ e = ''
+ f = ''
+loop_count: 30000000
diff --git a/benchmark/vm1_gc_short_with_complex_long.yml b/benchmark/vm1_gc_short_with_complex_long.yml
new file mode 100644
index 0000000000..c22ea74a60
--- /dev/null
+++ b/benchmark/vm1_gc_short_with_complex_long.yml
@@ -0,0 +1,25 @@
+prelude: |
+ def nested_hash h, n
+ if n == 0
+ ''
+ else
+ 10.times{
+ h[Object.new] = nested_hash(h, n-1)
+ }
+ end
+ end
+
+ long_lived = Hash.new
+ nested_hash long_lived, 6
+
+ GC.start
+ GC.start
+benchmark:
+ vm1_gc_short_with_complex_long: |
+ a = '' # short-lived String
+ b = ''
+ c = ''
+ d = ''
+ e = ''
+ f = ''
+loop_count: 30000000
diff --git a/benchmark/vm1_gc_short_with_long.yml b/benchmark/vm1_gc_short_with_long.yml
new file mode 100644
index 0000000000..c731aae548
--- /dev/null
+++ b/benchmark/vm1_gc_short_with_long.yml
@@ -0,0 +1,13 @@
+prelude: |
+ long_lived = Array.new(1_000_000){|i| "#{i}"}
+ GC.start
+ GC.start
+benchmark:
+ vm1_gc_short_with_long: |
+ a = '' # short-lived String
+ b = ''
+ c = ''
+ d = ''
+ e = ''
+ f = ''
+loop_count: 30000000
diff --git a/benchmark/vm1_gc_short_with_symbol.yml b/benchmark/vm1_gc_short_with_symbol.yml
new file mode 100644
index 0000000000..7fc1abedd8
--- /dev/null
+++ b/benchmark/vm1_gc_short_with_symbol.yml
@@ -0,0 +1,13 @@
+prelude: |
+ 50_000.times{|i| sym = "sym#{i}".to_sym}
+ GC.start
+ GC.start
+benchmark:
+ vm1_gc_short_with_symbol: |
+ a = '' # short-lived String
+ b = ''
+ c = ''
+ d = ''
+ e = ''
+ f = ''
+loop_count: 30000000
diff --git a/benchmark/vm1_gc_wb_ary.yml b/benchmark/vm1_gc_wb_ary.yml
new file mode 100644
index 0000000000..50fb4b6f84
--- /dev/null
+++ b/benchmark/vm1_gc_wb_ary.yml
@@ -0,0 +1,12 @@
+prelude: |
+ short_lived_ary = []
+
+ if RUBY_VERSION >= "2.2.0"
+ GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true)
+ end
+
+ short_lived = ''
+benchmark:
+ vm1_gc_wb_ary: |
+ short_lived_ary[0] = short_lived # write barrier
+loop_count: 30000000
diff --git a/benchmark/vm1_gc_wb_ary_promoted.yml b/benchmark/vm1_gc_wb_ary_promoted.yml
new file mode 100644
index 0000000000..cf9b5de005
--- /dev/null
+++ b/benchmark/vm1_gc_wb_ary_promoted.yml
@@ -0,0 +1,15 @@
+prelude: |
+ long_lived = []
+
+ if RUBY_VERSION > "2.2.0"
+ 3.times{ GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true) }
+ elsif
+ GC.start
+ end
+
+ short_lived = ''
+
+benchmark:
+ vm1_gc_wb_ary_promoted: |
+ long_lived[0] = short_lived # write barrier
+loop_count: 30000000
diff --git a/benchmark/vm1_gc_wb_obj.yml b/benchmark/vm1_gc_wb_obj.yml
new file mode 100644
index 0000000000..9dc08e7e1a
--- /dev/null
+++ b/benchmark/vm1_gc_wb_obj.yml
@@ -0,0 +1,15 @@
+prelude: |
+ class C
+ attr_accessor :foo
+ end
+ short_lived_obj = C.new
+
+ if RUBY_VERSION >= "2.2.0"
+ GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true)
+ end
+
+ short_lived = ''
+benchmark:
+ vm1_gc_wb_obj: |
+ short_lived_obj.foo = short_lived # write barrier
+loop_count: 30000000
diff --git a/benchmark/vm1_gc_wb_obj_promoted.yml b/benchmark/vm1_gc_wb_obj_promoted.yml
new file mode 100644
index 0000000000..26859d2a52
--- /dev/null
+++ b/benchmark/vm1_gc_wb_obj_promoted.yml
@@ -0,0 +1,17 @@
+prelude: |
+ class C
+ attr_accessor :foo
+ end
+ long_lived = C.new
+
+ if RUBY_VERSION >= "2.2.0"
+ 3.times{ GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true) }
+ elsif
+ GC.start
+ end
+
+ short_lived = ''
+benchmark:
+ vm1_gc_wb_obj_promoted: |
+ long_lived.foo = short_lived # write barrier
+loop_count: 30000000
diff --git a/benchmark/vm1_ivar.yml b/benchmark/vm1_ivar.yml
new file mode 100644
index 0000000000..7aa6fac729
--- /dev/null
+++ b/benchmark/vm1_ivar.yml
@@ -0,0 +1,6 @@
+prelude: "@a = 1\n"
+benchmark:
+ vm1_ivar: |
+ j = @a
+ k = @a
+loop_count: 30000000
diff --git a/benchmark/vm1_ivar_set.yml b/benchmark/vm1_ivar_set.yml
new file mode 100644
index 0000000000..6f19412d16
--- /dev/null
+++ b/benchmark/vm1_ivar_set.yml
@@ -0,0 +1,5 @@
+benchmark:
+ vm1_ivar_set: |
+ @a = 1
+ @b = 2
+loop_count: 30000000
diff --git a/benchmark/vm1_length.yml b/benchmark/vm1_length.yml
new file mode 100644
index 0000000000..a18e2ca2e6
--- /dev/null
+++ b/benchmark/vm1_length.yml
@@ -0,0 +1,8 @@
+prelude: |
+ a = 'abc'
+ b = [1, 2, 3]
+benchmark:
+ vm1_length: |
+ a.length
+ b.length
+loop_count: 30000000
diff --git a/benchmark/vm1_lvar_init.yml b/benchmark/vm1_lvar_init.yml
new file mode 100644
index 0000000000..10e2becef9
--- /dev/null
+++ b/benchmark/vm1_lvar_init.yml
@@ -0,0 +1,21 @@
+# while loop cost is not removed because `i` is used in the script
+benchmark:
+ vm1_lvar_init: |
+ def m v
+ unless v
+ # unreachable code
+ v1 = v2 = v3 = v4 = v5 = v6 = v7 = v8 = v9 = v10 =
+ v11 = v12 = v13 = v14 = v15 = v16 = v17 = v18 = v19 = v20 =
+ v21 = v22 = v23 = v24 = v25 = v26 = v27 = v28 = v29 = v30 =
+ v31 = v32 = v33 = v34 = v35 = v36 = v37 = v38 = v39 = v40 =
+ v41 = v42 = v43 = v44 = v45 = v46 = v47 = v48 = v49 = v50 = 1
+ end
+ end
+
+ i = 0
+
+ while i<30_000_000
+ i += 1
+ m i
+ end
+loop_count: 1
diff --git a/benchmark/vm1_lvar_set.yml b/benchmark/vm1_lvar_set.yml
new file mode 100644
index 0000000000..df8f6b6ea4
--- /dev/null
+++ b/benchmark/vm1_lvar_set.yml
@@ -0,0 +1,4 @@
+benchmark:
+ vm1_lvar_set: |
+ a = b = c = d = e = f = g = h = j = k = l = m = n = o = p = q = r = 1
+loop_count: 30000000
diff --git a/benchmark/vm1_neq.yml b/benchmark/vm1_neq.yml
new file mode 100644
index 0000000000..65a8128dda
--- /dev/null
+++ b/benchmark/vm1_neq.yml
@@ -0,0 +1,7 @@
+prelude: |
+ obj1 = Object.new
+ obj2 = Object.new
+benchmark:
+ vm1_neq: |
+ obj1 != obj2
+loop_count: 30000000
diff --git a/benchmark/vm1_not.yml b/benchmark/vm1_not.yml
new file mode 100644
index 0000000000..0fb7b282a9
--- /dev/null
+++ b/benchmark/vm1_not.yml
@@ -0,0 +1,6 @@
+prelude: |
+ obj = Object.new
+benchmark:
+ vm1_not: |
+ !obj
+loop_count: 30000000
diff --git a/benchmark/vm1_rescue.yml b/benchmark/vm1_rescue.yml
new file mode 100644
index 0000000000..a175b823af
--- /dev/null
+++ b/benchmark/vm1_rescue.yml
@@ -0,0 +1,6 @@
+benchmark:
+ vm1_rescue: |
+ begin
+ rescue
+ end
+loop_count: 30000000
diff --git a/benchmark/vm1_simplereturn.yml b/benchmark/vm1_simplereturn.yml
new file mode 100644
index 0000000000..3564aac7e2
--- /dev/null
+++ b/benchmark/vm1_simplereturn.yml
@@ -0,0 +1,7 @@
+prelude: |
+ def m
+ return 1
+ end
+benchmark:
+ vm1_simplereturn: m
+loop_count: 30000000
diff --git a/benchmark/vm1_swap.yml b/benchmark/vm1_swap.yml
new file mode 100644
index 0000000000..fed87ccd62
--- /dev/null
+++ b/benchmark/vm1_swap.yml
@@ -0,0 +1,7 @@
+prelude: |
+ a = 1
+ b = 2
+benchmark:
+ vm1_swap: |
+ a, b = b, a
+loop_count: 30000000
diff --git a/benchmark/vm1_yield.yml b/benchmark/vm1_yield.yml
new file mode 100644
index 0000000000..ae1f9316f9
--- /dev/null
+++ b/benchmark/vm1_yield.yml
@@ -0,0 +1,13 @@
+# while loop cost is not removed due to benchmark_driver.gem's limitation
+benchmark:
+ vm1_yield: |
+ def m
+ i = 0
+ while i<30_000_000
+ i += 1
+ yield
+ end
+ end
+
+ m{}
+loop_count: 1
diff --git a/benchmark/vm2_array.yml b/benchmark/vm2_array.yml
new file mode 100644
index 0000000000..7373098d5e
--- /dev/null
+++ b/benchmark/vm2_array.yml
@@ -0,0 +1,4 @@
+benchmark:
+ vm2_array: |
+ a = [1,2,3,4,5,6,7,8,9,10]
+loop_count: 6000000
diff --git a/benchmark/vm2_bigarray.yml b/benchmark/vm2_bigarray.yml
new file mode 100644
index 0000000000..2ad6da3905
--- /dev/null
+++ b/benchmark/vm2_bigarray.yml
@@ -0,0 +1,105 @@
+benchmark:
+ vm2_bigarray: |
+ a = [
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ ]
+loop_count: 6000000
diff --git a/benchmark/vm2_bighash.yml b/benchmark/vm2_bighash.yml
new file mode 100644
index 0000000000..e9154e4ba9
--- /dev/null
+++ b/benchmark/vm2_bighash.yml
@@ -0,0 +1,4 @@
+benchmark:
+ vm2_bighash: |
+ a = {0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9, 10=>10, 11=>11, 12=>12, 13=>13, 14=>14, 15=>15, 16=>16, 17=>17, 18=>18, 19=>19, 20=>20, 21=>21, 22=>22, 23=>23, 24=>24, 25=>25, 26=>26, 27=>27, 28=>28, 29=>29, 30=>30, 31=>31, 32=>32, 33=>33, 34=>34, 35=>35, 36=>36, 37=>37, 38=>38, 39=>39, 40=>40, 41=>41, 42=>42, 43=>43, 44=>44, 45=>45, 46=>46, 47=>47, 48=>48, 49=>49, 50=>50, 51=>51, 52=>52, 53=>53, 54=>54, 55=>55, 56=>56, 57=>57, 58=>58, 59=>59, 60=>60, 61=>61, 62=>62, 63=>63, 64=>64, 65=>65, 66=>66, 67=>67, 68=>68, 69=>69, 70=>70, 71=>71, 72=>72, 73=>73, 74=>74, 75=>75, 76=>76, 77=>77, 78=>78, 79=>79, 80=>80, 81=>81, 82=>82, 83=>83, 84=>84, 85=>85, 86=>86, 87=>87, 88=>88, 89=>89, 90=>90, 91=>91, 92=>92, 93=>93, 94=>94, 95=>95, 96=>96, 97=>97, 98=>98, 99=>99, 100=>100, 101=>101, 102=>102, 103=>103, 104=>104, 105=>105, 106=>106, 107=>107, 108=>108, 109=>109, 110=>110, 111=>111, 112=>112, 113=>113, 114=>114, 115=>115, 116=>116, 117=>117, 118=>118, 119=>119, 120=>120, 121=>121, 122=>122, 123=>123, 124=>124, 125=>125, 126=>126, 127=>127, 128=>128, 129=>129, 130=>130, 131=>131, 132=>132, 133=>133, 134=>134, 135=>135, 136=>136, 137=>137, 138=>138, 139=>139, 140=>140, 141=>141, 142=>142, 143=>143, 144=>144, 145=>145, 146=>146, 147=>147, 148=>148, 149=>149, 150=>150, 151=>151, 152=>152, 153=>153, 154=>154, 155=>155, 156=>156, 157=>157, 158=>158, 159=>159, 160=>160, 161=>161, 162=>162, 163=>163, 164=>164, 165=>165, 166=>166, 167=>167, 168=>168, 169=>169, 170=>170, 171=>171, 172=>172, 173=>173, 174=>174, 175=>175, 176=>176, 177=>177, 178=>178, 179=>179, 180=>180, 181=>181, 182=>182, 183=>183, 184=>184, 185=>185, 186=>186, 187=>187, 188=>188, 189=>189, 190=>190, 191=>191, 192=>192, 193=>193, 194=>194, 195=>195, 196=>196, 197=>197, 198=>198, 199=>199, 200=>200, 201=>201, 202=>202, 203=>203, 204=>204, 205=>205, 206=>206, 207=>207, 208=>208, 209=>209, 210=>210, 211=>211, 212=>212, 213=>213, 214=>214, 215=>215, 216=>216, 217=>217, 218=>218, 219=>219, 220=>220, 221=>221, 222=>222, 223=>223, 224=>224, 225=>225, 226=>226, 227=>227, 228=>228, 229=>229, 230=>230, 231=>231, 232=>232, 233=>233, 234=>234, 235=>235, 236=>236, 237=>237, 238=>238, 239=>239, 240=>240, 241=>241, 242=>242, 243=>243, 244=>244, 245=>245, 246=>246, 247=>247, 248=>248, 249=>249, 250=>250, 251=>251, 252=>252, 253=>253, 254=>254, 255=>255, 256=>256, 257=>257, 258=>258, 259=>259, 260=>260, 261=>261, 262=>262, 263=>263, 264=>264, 265=>265, 266=>266, 267=>267, 268=>268, 269=>269, 270=>270, 271=>271, 272=>272, 273=>273, 274=>274, 275=>275, 276=>276, 277=>277, 278=>278, 279=>279, 280=>280, 281=>281, 282=>282, 283=>283, 284=>284, 285=>285, 286=>286, 287=>287, 288=>288, 289=>289, 290=>290, 291=>291, 292=>292, 293=>293, 294=>294, 295=>295, 296=>296, 297=>297, 298=>298, 299=>299, 300=>300, 301=>301, 302=>302, 303=>303, 304=>304, 305=>305, 306=>306, 307=>307, 308=>308, 309=>309, 310=>310, 311=>311, 312=>312, 313=>313, 314=>314, 315=>315, 316=>316, 317=>317, 318=>318, 319=>319, 320=>320, 321=>321, 322=>322, 323=>323, 324=>324, 325=>325, 326=>326, 327=>327, 328=>328, 329=>329, 330=>330, 331=>331, 332=>332, 333=>333, 334=>334, 335=>335, 336=>336, 337=>337, 338=>338, 339=>339, 340=>340, 341=>341, 342=>342, 343=>343, 344=>344, 345=>345, 346=>346, 347=>347, 348=>348, 349=>349, 350=>350, 351=>351, 352=>352, 353=>353, 354=>354, 355=>355, 356=>356, 357=>357, 358=>358, 359=>359, 360=>360, 361=>361, 362=>362, 363=>363, 364=>364, 365=>365, 366=>366, 367=>367, 368=>368, 369=>369, 370=>370, 371=>371, 372=>372, 373=>373, 374=>374, 375=>375, 376=>376, 377=>377, 378=>378, 379=>379, 380=>380, 381=>381, 382=>382, 383=>383, 384=>384, 385=>385, 386=>386, 387=>387, 388=>388, 389=>389, 390=>390, 391=>391, 392=>392, 393=>393, 394=>394, 395=>395, 396=>396, 397=>397, 398=>398, 399=>399, 400=>400, 401=>401, 402=>402, 403=>403, 404=>404, 405=>405, 406=>406, 407=>407, 408=>408, 409=>409, 410=>410, 411=>411, 412=>412, 413=>413, 414=>414, 415=>415, 416=>416, 417=>417, 418=>418, 419=>419, 420=>420, 421=>421, 422=>422, 423=>423, 424=>424, 425=>425, 426=>426, 427=>427, 428=>428, 429=>429, 430=>430, 431=>431, 432=>432, 433=>433, 434=>434, 435=>435, 436=>436, 437=>437, 438=>438, 439=>439, 440=>440, 441=>441, 442=>442, 443=>443, 444=>444, 445=>445, 446=>446, 447=>447, 448=>448, 449=>449, 450=>450, 451=>451, 452=>452, 453=>453, 454=>454, 455=>455, 456=>456, 457=>457, 458=>458, 459=>459, 460=>460, 461=>461, 462=>462, 463=>463, 464=>464, 465=>465, 466=>466, 467=>467, 468=>468, 469=>469, 470=>470, 471=>471, 472=>472, 473=>473, 474=>474, 475=>475, 476=>476, 477=>477, 478=>478, 479=>479, 480=>480, 481=>481, 482=>482, 483=>483, 484=>484, 485=>485, 486=>486, 487=>487, 488=>488, 489=>489, 490=>490, 491=>491, 492=>492, 493=>493, 494=>494, 495=>495, 496=>496, 497=>497, 498=>498, 499=>499, 500=>500,}
+loop_count: 60000
diff --git a/benchmark/vm2_case.yml b/benchmark/vm2_case.yml
new file mode 100644
index 0000000000..7716783c09
--- /dev/null
+++ b/benchmark/vm2_case.yml
@@ -0,0 +1,13 @@
+benchmark:
+ vm2_case: |
+ case :foo
+ when :bar
+ raise
+ when :baz
+ raise
+ when :boo
+ raise
+ when :foo
+ # noop
+ end
+loop_count: 6000000
diff --git a/benchmark/vm2_case_lit.yml b/benchmark/vm2_case_lit.yml
new file mode 100644
index 0000000000..c49b8dfe5e
--- /dev/null
+++ b/benchmark/vm2_case_lit.yml
@@ -0,0 +1,23 @@
+# loop_count is not utilized since `i` is involved in the script
+benchmark:
+ vm2_case_lit: |
+ i = 0
+ @ret = [ "foo", true, false, :sym, 6, nil, 0.1, 0xffffffffffffffff ]
+ def foo(i)
+ @ret[i % @ret.size]
+ end
+
+ while i<6_000_000
+ case foo(i)
+ when "foo" then :foo
+ when true then true
+ when false then false
+ when :sym then :sym
+ when 6 then :fix
+ when nil then nil
+ when 0.1 then :float
+ when 0xffffffffffffffff then :big
+ end
+ i += 1
+ end
+loop_count: 1
diff --git a/benchmark/vm2_defined_method.yml b/benchmark/vm2_defined_method.yml
new file mode 100644
index 0000000000..e1b0d55674
--- /dev/null
+++ b/benchmark/vm2_defined_method.yml
@@ -0,0 +1,8 @@
+prelude: |
+ class Object
+ define_method(:m){}
+ end
+benchmark:
+ vm2_defined_method: |
+ m; m; m; m; m; m; m; m;
+loop_count: 6000000
diff --git a/benchmark/vm2_dstr.yml b/benchmark/vm2_dstr.yml
new file mode 100644
index 0000000000..f8bd6e0133
--- /dev/null
+++ b/benchmark/vm2_dstr.yml
@@ -0,0 +1,6 @@
+prelude: |
+ x = y = 'z'
+benchmark:
+ vm2_dstr: |
+ str = "foo#{x}bar#{y}baz"
+loop_count: 6000000
diff --git a/benchmark/vm2_eval.yml b/benchmark/vm2_eval.yml
new file mode 100644
index 0000000000..d506a9c079
--- /dev/null
+++ b/benchmark/vm2_eval.yml
@@ -0,0 +1,4 @@
+benchmark:
+ vm2_eval: |
+ eval("1")
+loop_count: 6000000
diff --git a/benchmark/vm2_fiber_switch.yml b/benchmark/vm2_fiber_switch.yml
new file mode 100644
index 0000000000..f3e4c91283
--- /dev/null
+++ b/benchmark/vm2_fiber_switch.yml
@@ -0,0 +1,9 @@
+prelude: |
+ # based on benchmark for [ruby-core:65518] [Feature #10341] by Knut Franke
+ fib = Fiber.new do
+ loop { Fiber.yield }
+ end
+benchmark:
+ vm2_fiber_switch: |
+ fib.resume
+loop_count: 6000000
diff --git a/benchmark/vm2_freezestring.yml b/benchmark/vm2_freezestring.yml
new file mode 100644
index 0000000000..b78af91a20
--- /dev/null
+++ b/benchmark/vm2_freezestring.yml
@@ -0,0 +1,10 @@
+prelude: |
+ class String
+ def freeze
+ -self
+ end
+ end
+benchmark:
+ vm2_freezestring: |
+ "tXnL1BP5T1WPXMjuFNLQtallEtRcay1t2lHtJSrlVsDgvunlbtfpr/DGdH0NGYE9".freeze
+loop_count: 6000000
diff --git a/benchmark/vm2_method.yml b/benchmark/vm2_method.yml
new file mode 100644
index 0000000000..cc7b9b28ff
--- /dev/null
+++ b/benchmark/vm2_method.yml
@@ -0,0 +1,8 @@
+prelude: |
+ def m
+ nil
+ end
+benchmark:
+ vm2_method: |
+ m; m; m; m; m; m; m; m;
+loop_count: 6000000
diff --git a/benchmark/vm2_method_missing.yml b/benchmark/vm2_method_missing.yml
new file mode 100644
index 0000000000..cbfb794b25
--- /dev/null
+++ b/benchmark/vm2_method_missing.yml
@@ -0,0 +1,11 @@
+prelude: |
+ class C
+ def method_missing mid
+ end
+ end
+
+ obj = C.new
+benchmark:
+ vm2_method_missing: |
+ obj.m; obj.m; obj.m; obj.m; obj.m; obj.m; obj.m; obj.m;
+loop_count: 6000000
diff --git a/benchmark/vm2_method_with_block.yml b/benchmark/vm2_method_with_block.yml
new file mode 100644
index 0000000000..6e522adccc
--- /dev/null
+++ b/benchmark/vm2_method_with_block.yml
@@ -0,0 +1,8 @@
+prelude: |
+ def m
+ nil
+ end
+benchmark:
+ vm2_method_with_block: |
+ m{}; m{}; m{}; m{}; m{}; m{}; m{}; m{};
+loop_count: 6000000
diff --git a/benchmark/vm2_module_ann_const_set.yml b/benchmark/vm2_module_ann_const_set.yml
new file mode 100644
index 0000000000..b0becd9d3d
--- /dev/null
+++ b/benchmark/vm2_module_ann_const_set.yml
@@ -0,0 +1,4 @@
+benchmark:
+ vm2_module_ann_const_set: |
+ Module.new.const_set(:X, Module.new)
+loop_count: 6000000
diff --git a/benchmark/vm2_module_const_set.yml b/benchmark/vm2_module_const_set.yml
new file mode 100644
index 0000000000..05a640069c
--- /dev/null
+++ b/benchmark/vm2_module_const_set.yml
@@ -0,0 +1,8 @@
+prelude: |
+ module M
+ end
+ $VERBOSE = nil
+benchmark:
+ vm2_module_const_set: |
+ M.const_set(:X, Module.new)
+loop_count: 6000000
diff --git a/benchmark/vm2_mutex.yml b/benchmark/vm2_mutex.yml
new file mode 100644
index 0000000000..c40a90444a
--- /dev/null
+++ b/benchmark/vm2_mutex.yml
@@ -0,0 +1,8 @@
+prelude: |
+ require 'thread'
+
+ m = Thread::Mutex.new
+benchmark:
+ vm2_mutex: |
+ m.synchronize{}
+loop_count: 6000000
diff --git a/benchmark/vm2_newlambda.yml b/benchmark/vm2_newlambda.yml
new file mode 100644
index 0000000000..93133f9f30
--- /dev/null
+++ b/benchmark/vm2_newlambda.yml
@@ -0,0 +1,4 @@
+benchmark:
+ vm2_newlambda: |
+ lambda {}
+loop_count: 6000000
diff --git a/benchmark/vm2_poly_method.yml b/benchmark/vm2_poly_method.yml
new file mode 100644
index 0000000000..0104bdfb66
--- /dev/null
+++ b/benchmark/vm2_poly_method.yml
@@ -0,0 +1,24 @@
+# loop_count is not utilized since `i` is involved in the script
+benchmark:
+ vm2_poly_method: |
+ class C1
+ def m
+ 1
+ end
+ end
+ class C2
+ def m
+ 2
+ end
+ end
+
+ o1 = C1.new
+ o2 = C2.new
+
+ i = 0
+ while i<6_000_000
+ o = (i % 2 == 0) ? o1 : o2
+ o.m; o.m; o.m; o.m; o.m; o.m; o.m; o.m
+ i += 1
+ end
+loop_count: 1
diff --git a/benchmark/vm2_poly_method_ov.yml b/benchmark/vm2_poly_method_ov.yml
new file mode 100644
index 0000000000..3748073ba2
--- /dev/null
+++ b/benchmark/vm2_poly_method_ov.yml
@@ -0,0 +1,24 @@
+# loop_count is not utilized since `i` is involved in the script
+benchmark:
+ vm2_poly_method_ov: |
+ class C1
+ def m
+ 1
+ end
+ end
+ class C2
+ def m
+ 2
+ end
+ end
+
+ o1 = C1.new
+ o2 = C2.new
+
+ i = 0
+ while i<6_000_000
+ o = (i % 2 == 0) ? o1 : o2
+ # o.m; o.m; o.m; o.m; o.m; o.m; o.m; o.m
+ i += 1
+ end
+loop_count: 1
diff --git a/benchmark/vm2_poly_singleton.yml b/benchmark/vm2_poly_singleton.yml
new file mode 100644
index 0000000000..e58d7bfb37
--- /dev/null
+++ b/benchmark/vm2_poly_singleton.yml
@@ -0,0 +1,18 @@
+# loop_count is not utilized since `i` is involved in the script
+benchmark:
+ vm2_poly_singleton: |
+ class C1
+ def m; 1; end
+ end
+
+ o1 = C1.new
+ o2 = C1.new
+ o2.singleton_class
+
+ i = 0
+ while i<6_000_000 # benchmark loop 2
+ o = (i % 2 == 0) ? o1 : o2
+ o.m; o.m; o.m; o.m; o.m; o.m; o.m; o.m
+ i += 1
+ end
+loop_count: 1
diff --git a/benchmark/vm2_proc.yml b/benchmark/vm2_proc.yml
new file mode 100644
index 0000000000..5c36e936d9
--- /dev/null
+++ b/benchmark/vm2_proc.yml
@@ -0,0 +1,12 @@
+prelude: |
+ def m &b
+ b
+ end
+
+ pr = m{
+ a = 1
+ }
+benchmark:
+ vm2_proc: |
+ pr.call
+loop_count: 6000000
diff --git a/benchmark/vm2_raise1.yml b/benchmark/vm2_raise1.yml
new file mode 100644
index 0000000000..f6eb308968
--- /dev/null
+++ b/benchmark/vm2_raise1.yml
@@ -0,0 +1,16 @@
+prelude: |
+ def rec n
+ if n > 0
+ rec n-1
+ else
+ raise
+ end
+ end
+benchmark:
+ vm2_raise1: |
+ begin
+ rec 1
+ rescue
+ # ignore
+ end
+loop_count: 6000000
diff --git a/benchmark/vm2_raise2.yml b/benchmark/vm2_raise2.yml
new file mode 100644
index 0000000000..7d51b1b314
--- /dev/null
+++ b/benchmark/vm2_raise2.yml
@@ -0,0 +1,16 @@
+prelude: |
+ def rec n
+ if n > 0
+ rec n-1
+ else
+ raise
+ end
+ end
+benchmark:
+ vm2_raise2: |
+ begin
+ rec 10
+ rescue
+ # ignore
+ end
+loop_count: 6000000
diff --git a/benchmark/vm2_regexp.yml b/benchmark/vm2_regexp.yml
new file mode 100644
index 0000000000..0f3968a99b
--- /dev/null
+++ b/benchmark/vm2_regexp.yml
@@ -0,0 +1,6 @@
+prelude: |
+ str = 'xxxhogexxx'
+benchmark:
+ vm2_regexp: |
+ /hoge/ =~ str
+loop_count: 6000000
diff --git a/benchmark/vm2_send.yml b/benchmark/vm2_send.yml
new file mode 100644
index 0000000000..44a12a27d9
--- /dev/null
+++ b/benchmark/vm2_send.yml
@@ -0,0 +1,11 @@
+prelude: |
+ class C
+ def m
+ end
+ end
+
+ o = C.new
+benchmark:
+ vm2_send: |
+ o.__send__ :m
+loop_count: 6000000
diff --git a/benchmark/vm2_string_literal.yml b/benchmark/vm2_string_literal.yml
new file mode 100644
index 0000000000..54b0aec1fe
--- /dev/null
+++ b/benchmark/vm2_string_literal.yml
@@ -0,0 +1,4 @@
+benchmark:
+ vm2_string_literal: |
+ x = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+loop_count: 6000000
diff --git a/benchmark/vm2_struct_big_aref_hi.yml b/benchmark/vm2_struct_big_aref_hi.yml
new file mode 100644
index 0000000000..eed1846d28
--- /dev/null
+++ b/benchmark/vm2_struct_big_aref_hi.yml
@@ -0,0 +1,7 @@
+prelude: |
+ s = Struct.new(*('a'..'z').map { |x| x.to_sym })
+ x = s.new
+benchmark:
+ vm2_struct_big_aref_hi: |
+ x.z # x[25]
+loop_count: 6000000
diff --git a/benchmark/vm2_struct_big_aref_lo.yml b/benchmark/vm2_struct_big_aref_lo.yml
new file mode 100644
index 0000000000..0915435b76
--- /dev/null
+++ b/benchmark/vm2_struct_big_aref_lo.yml
@@ -0,0 +1,7 @@
+prelude: |
+ s = Struct.new(*('a'..'z').map { |x| x.to_sym })
+ x = s.new
+benchmark:
+ vm2_struct_big_aref_lo: |
+ x.k # x[10]
+loop_count: 6000000
diff --git a/benchmark/vm2_struct_big_aset.yml b/benchmark/vm2_struct_big_aset.yml
new file mode 100644
index 0000000000..6af50103d3
--- /dev/null
+++ b/benchmark/vm2_struct_big_aset.yml
@@ -0,0 +1,11 @@
+# loop_count is not utilized since `i` is involved in the script
+benchmark:
+ vm2_struct_big_aset: |
+ s = Struct.new(*('a'..'z').map { |x| x.to_sym })
+ x = s.new
+ i = 0
+ while i<6_000_000
+ i += 1
+ x.k = i # x[10] = i
+ end
+loop_count: 1
diff --git a/benchmark/vm2_struct_big_href_hi.yml b/benchmark/vm2_struct_big_href_hi.yml
new file mode 100644
index 0000000000..60aa7fddf3
--- /dev/null
+++ b/benchmark/vm2_struct_big_href_hi.yml
@@ -0,0 +1,7 @@
+prelude: |
+ s = Struct.new(*('a'..'z').map { |x| x.to_sym })
+ x = s.new
+benchmark:
+ vm2_struct_big_href_hi: |
+ x[:z]
+loop_count: 6000000
diff --git a/benchmark/vm2_struct_big_href_lo.yml b/benchmark/vm2_struct_big_href_lo.yml
new file mode 100644
index 0000000000..c55c0bd16c
--- /dev/null
+++ b/benchmark/vm2_struct_big_href_lo.yml
@@ -0,0 +1,7 @@
+prelude: |
+ s = Struct.new(*('a'..'z').map { |x| x.to_sym })
+ x = s.new
+benchmark:
+ vm2_struct_big_href_lo: |
+ x[:k]
+loop_count: 6000000
diff --git a/benchmark/vm2_struct_big_hset.yml b/benchmark/vm2_struct_big_hset.yml
new file mode 100644
index 0000000000..d199c5bd47
--- /dev/null
+++ b/benchmark/vm2_struct_big_hset.yml
@@ -0,0 +1,11 @@
+# loop_count is not utilized since `i` is involved in the script
+benchmark:
+ vm2_struct_big_hset: |
+ s = Struct.new(*('a'..'z').map { |x| x.to_sym })
+ x = s.new
+ i = 0
+ while i<6_000_000
+ i += 1
+ x[:k] = i
+ end
+loop_count: 1
diff --git a/benchmark/vm2_struct_small_aref.yml b/benchmark/vm2_struct_small_aref.yml
new file mode 100644
index 0000000000..83381bed3a
--- /dev/null
+++ b/benchmark/vm2_struct_small_aref.yml
@@ -0,0 +1,7 @@
+prelude: |
+ s = Struct.new(:a, :b, :c)
+ x = s.new
+benchmark:
+ vm2_struct_small_aref: |
+ x.a
+loop_count: 6000000
diff --git a/benchmark/vm2_struct_small_aset.yml b/benchmark/vm2_struct_small_aset.yml
new file mode 100644
index 0000000000..3e84a61dd0
--- /dev/null
+++ b/benchmark/vm2_struct_small_aset.yml
@@ -0,0 +1,11 @@
+# loop_count is not utilized since `i` is involved in the script
+benchmark:
+ vm2_struct_small_aset: |
+ s = Struct.new(:a, :b, :c)
+ x = s.new
+ i = 0
+ while i<6_000_000
+ i += 1
+ x.a = i
+ end
+loop_count: 1
diff --git a/benchmark/vm2_struct_small_href.yml b/benchmark/vm2_struct_small_href.yml
new file mode 100644
index 0000000000..b744f070d1
--- /dev/null
+++ b/benchmark/vm2_struct_small_href.yml
@@ -0,0 +1,7 @@
+prelude: |
+ s = Struct.new(:a, :b, :c)
+ x = s.new
+benchmark:
+ vm2_struct_small_href: |
+ x[:a]
+loop_count: 6000000
diff --git a/benchmark/vm2_struct_small_hset.yml b/benchmark/vm2_struct_small_hset.yml
new file mode 100644
index 0000000000..d43845d6e0
--- /dev/null
+++ b/benchmark/vm2_struct_small_hset.yml
@@ -0,0 +1,7 @@
+prelude: |
+ s = Struct.new(:a, :b, :c)
+ x = s.new
+benchmark:
+ vm2_struct_small_hset: |
+ x[:a] = 1
+loop_count: 6000000
diff --git a/benchmark/vm2_super.yml b/benchmark/vm2_super.yml
new file mode 100644
index 0000000000..674743762a
--- /dev/null
+++ b/benchmark/vm2_super.yml
@@ -0,0 +1,17 @@
+prelude: |
+ class C
+ def m
+ 1
+ end
+ end
+
+ class CC < C
+ def m
+ super()
+ end
+ end
+
+ obj = CC.new
+benchmark:
+ vm2_super: obj.m
+loop_count: 6000000
diff --git a/benchmark/vm2_unif1.yml b/benchmark/vm2_unif1.yml
new file mode 100644
index 0000000000..caef13279f
--- /dev/null
+++ b/benchmark/vm2_unif1.yml
@@ -0,0 +1,7 @@
+prelude: |
+ def m a, b
+ end
+benchmark:
+ vm2_unif1: |
+ m 100, 200
+loop_count: 6000000
diff --git a/benchmark/vm2_zsuper.yml b/benchmark/vm2_zsuper.yml
new file mode 100644
index 0000000000..f760cfd48e
--- /dev/null
+++ b/benchmark/vm2_zsuper.yml
@@ -0,0 +1,18 @@
+prelude: |
+ class C
+ def m a
+ 1
+ end
+ end
+
+ class CC < C
+ def m a
+ super
+ end
+ end
+
+ obj = CC.new
+benchmark:
+ vm2_zsuper: |
+ obj.m 10
+loop_count: 6000000
diff --git a/benchmark/bm_vm3_backtrace.rb b/benchmark/vm3_backtrace.rb
index 0fbf73e1ca..0fbf73e1ca 100644
--- a/benchmark/bm_vm3_backtrace.rb
+++ b/benchmark/vm3_backtrace.rb
diff --git a/benchmark/bm_vm3_clearmethodcache.rb b/benchmark/vm3_clearmethodcache.rb
index 9661323cd2..9661323cd2 100644
--- a/benchmark/bm_vm3_clearmethodcache.rb
+++ b/benchmark/vm3_clearmethodcache.rb
diff --git a/benchmark/bm_vm3_gc.rb b/benchmark/vm3_gc.rb
index e668026915..e668026915 100644
--- a/benchmark/bm_vm3_gc.rb
+++ b/benchmark/vm3_gc.rb
diff --git a/benchmark/bm_vm3_gc_old_full.rb b/benchmark/vm3_gc_old_full.rb
index cfdfc8c5a5..cfdfc8c5a5 100644
--- a/benchmark/bm_vm3_gc_old_full.rb
+++ b/benchmark/vm3_gc_old_full.rb
diff --git a/benchmark/bm_vm3_gc_old_immediate.rb b/benchmark/vm3_gc_old_immediate.rb
index ad22feb655..ad22feb655 100644
--- a/benchmark/bm_vm3_gc_old_immediate.rb
+++ b/benchmark/vm3_gc_old_immediate.rb
diff --git a/benchmark/bm_vm3_gc_old_lazy.rb b/benchmark/vm3_gc_old_lazy.rb
index b74d44baf1..b74d44baf1 100644
--- a/benchmark/bm_vm3_gc_old_lazy.rb
+++ b/benchmark/vm3_gc_old_lazy.rb
diff --git a/benchmark/bm_vm_symbol_block_pass.rb b/benchmark/vm_symbol_block_pass.rb
index 1d433353e1..1d433353e1 100644
--- a/benchmark/bm_vm_symbol_block_pass.rb
+++ b/benchmark/vm_symbol_block_pass.rb
diff --git a/benchmark/bm_vm_thread_alive_check1.rb b/benchmark/vm_thread_alive_check1.rb
index c993accdda..c993accdda 100644
--- a/benchmark/bm_vm_thread_alive_check1.rb
+++ b/benchmark/vm_thread_alive_check1.rb
diff --git a/benchmark/bm_vm_thread_close.rb b/benchmark/vm_thread_close.rb
index 3e9a265ce8..3e9a265ce8 100644
--- a/benchmark/bm_vm_thread_close.rb
+++ b/benchmark/vm_thread_close.rb
diff --git a/benchmark/vm_thread_condvar1.rb b/benchmark/vm_thread_condvar1.rb
new file mode 100644
index 0000000000..cf5706b23e
--- /dev/null
+++ b/benchmark/vm_thread_condvar1.rb
@@ -0,0 +1,28 @@
+# two threads, two mutex, two condvar ping-pong
+require 'thread'
+m1 = Mutex.new
+m2 = Mutex.new
+cv1 = ConditionVariable.new
+cv2 = ConditionVariable.new
+max = 100000
+i = 0
+wait = nil
+m2.synchronize do
+ wait = Thread.new do
+ m1.synchronize do
+ m2.synchronize { cv2.signal }
+ while (i += 1) < max
+ cv1.wait(m1)
+ cv2.signal
+ end
+ end
+ end
+ cv2.wait(m2)
+end
+m1.synchronize do
+ while i < max
+ cv1.signal
+ cv2.wait(m1)
+ end
+end
+wait.join
diff --git a/benchmark/vm_thread_condvar2.rb b/benchmark/vm_thread_condvar2.rb
new file mode 100644
index 0000000000..7c8dc19481
--- /dev/null
+++ b/benchmark/vm_thread_condvar2.rb
@@ -0,0 +1,35 @@
+# many threads, one mutex, many condvars
+require 'thread'
+m = Mutex.new
+cv1 = ConditionVariable.new
+cv2 = ConditionVariable.new
+max = 1000
+n = 100
+waiting = 0
+scvs = []
+waiters = n.times.map do |i|
+ start_cv = ConditionVariable.new
+ scvs << start_cv
+ start_mtx = Mutex.new
+ start_mtx.synchronize do
+ th = Thread.new(start_mtx, start_cv) do |sm, scv|
+ m.synchronize do
+ sm.synchronize { scv.signal }
+ max.times do
+ cv2.signal if (waiting += 1) == n
+ cv1.wait(m)
+ end
+ end
+ end
+ start_cv.wait(start_mtx)
+ th
+ end
+end
+m.synchronize do
+ max.times do
+ cv2.wait(m) until waiting == n
+ waiting = 0
+ cv1.broadcast
+ end
+end
+waiters.each(&:join)
diff --git a/benchmark/bm_vm_thread_create_join.rb b/benchmark/vm_thread_create_join.rb
index 393cd45df9..393cd45df9 100644
--- a/benchmark/bm_vm_thread_create_join.rb
+++ b/benchmark/vm_thread_create_join.rb
diff --git a/benchmark/bm_vm_thread_mutex1.rb b/benchmark/vm_thread_mutex1.rb
index 66e42c85e1..66e42c85e1 100644
--- a/benchmark/bm_vm_thread_mutex1.rb
+++ b/benchmark/vm_thread_mutex1.rb
diff --git a/benchmark/bm_vm_thread_mutex2.rb b/benchmark/vm_thread_mutex2.rb
index 6e6c804c31..6e6c804c31 100644
--- a/benchmark/bm_vm_thread_mutex2.rb
+++ b/benchmark/vm_thread_mutex2.rb
diff --git a/benchmark/bm_vm_thread_mutex3.rb b/benchmark/vm_thread_mutex3.rb
index c750dc542a..c750dc542a 100644
--- a/benchmark/bm_vm_thread_mutex3.rb
+++ b/benchmark/vm_thread_mutex3.rb
diff --git a/benchmark/bm_vm_thread_pass.rb b/benchmark/vm_thread_pass.rb
index b5b3c0bc85..b5b3c0bc85 100644
--- a/benchmark/bm_vm_thread_pass.rb
+++ b/benchmark/vm_thread_pass.rb
diff --git a/benchmark/vm_thread_pass_flood.rb b/benchmark/vm_thread_pass_flood.rb
new file mode 100644
index 0000000000..a660aafc18
--- /dev/null
+++ b/benchmark/vm_thread_pass_flood.rb
@@ -0,0 +1,10 @@
+# n.b. this is a good test for GVL when pinned to a single CPU
+
+1000.times{
+ Thread.new{loop{Thread.pass}}
+}
+
+i = 0
+while i<10000
+ i += 1
+end
diff --git a/benchmark/bm_vm_thread_pipe.rb b/benchmark/vm_thread_pipe.rb
index 112a621905..112a621905 100644
--- a/benchmark/bm_vm_thread_pipe.rb
+++ b/benchmark/vm_thread_pipe.rb
diff --git a/benchmark/bm_vm_thread_queue.rb b/benchmark/vm_thread_queue.rb
index 274ceda366..274ceda366 100644
--- a/benchmark/bm_vm_thread_queue.rb
+++ b/benchmark/vm_thread_queue.rb
diff --git a/benchmark/vm_thread_sized_queue.rb b/benchmark/vm_thread_sized_queue.rb
new file mode 100644
index 0000000000..7b9af5482b
--- /dev/null
+++ b/benchmark/vm_thread_sized_queue.rb
@@ -0,0 +1,20 @@
+require 'thread'
+# on producer, one consumer
+
+n = 1_000_000
+q = Thread::SizedQueue.new(100)
+consumer = Thread.new{
+ while q.pop
+ # consuming
+ end
+}
+
+producer = Thread.new{
+ while n > 0
+ q.push true
+ n -= 1
+ end
+ q.push nil
+}
+
+consumer.join
diff --git a/benchmark/vm_thread_sized_queue2.rb b/benchmark/vm_thread_sized_queue2.rb
new file mode 100644
index 0000000000..de9f55e978
--- /dev/null
+++ b/benchmark/vm_thread_sized_queue2.rb
@@ -0,0 +1,23 @@
+require 'thread'
+# one producer, many consumers
+n = 1_000_000
+m = 10
+q = Thread::SizedQueue.new(100)
+consumers = m.times.map do
+ Thread.new do
+ while q.pop
+ # consuming
+ end
+ end
+end
+
+producer = Thread.new do
+ while n > 0
+ q.push true
+ n -= 1
+ end
+ m.times { q.push nil }
+end
+
+producer.join
+consumers.each(&:join)
diff --git a/benchmark/vm_thread_sized_queue3.rb b/benchmark/vm_thread_sized_queue3.rb
new file mode 100644
index 0000000000..ce5f1796d8
--- /dev/null
+++ b/benchmark/vm_thread_sized_queue3.rb
@@ -0,0 +1,22 @@
+require 'thread'
+# many producers, one consumer
+n = 1_000_000
+m = 10
+q = Thread::SizedQueue.new(100)
+consumer = Thread.new do
+ while q.pop
+ # consuming
+ end
+end
+
+producers = m.times.map do
+ Thread.new do
+ while n > 0
+ q.push true
+ n -= 1
+ end
+ end
+end
+producers.each(&:join)
+q.push nil
+consumer.join
diff --git a/benchmark/vm_thread_sized_queue4.rb b/benchmark/vm_thread_sized_queue4.rb
new file mode 100644
index 0000000000..a9b7d80ec0
--- /dev/null
+++ b/benchmark/vm_thread_sized_queue4.rb
@@ -0,0 +1,26 @@
+require 'thread'
+# many producers, many consumers
+nr = 1_000_000
+n = 10
+m = 10
+q = Thread::SizedQueue.new(100)
+consumers = n.times.map do
+ Thread.new do
+ while q.pop
+ # consuming
+ end
+ end
+end
+
+producers = m.times.map do
+ Thread.new do
+ while nr > 0
+ q.push true
+ nr -= 1
+ end
+ end
+end
+
+producers.each(&:join)
+n.times { q.push nil }
+consumers.each(&:join)
diff --git a/benchmark/wc.input.base b/benchmark/wc.input.base
deleted file mode 100644
index 41143fbac0..0000000000
--- a/benchmark/wc.input.base
+++ /dev/null
@@ -1,25 +0,0 @@
-Subject: Re: Who was Izchak Miller?
-From: "Jane D. Anonymous" <nobody@yale.edu>
-Date: 1996/04/28
-Message-Id: <4lv7bc$oh@news.ycc.yale.edu>
-References: <317C405E.5DFA@panix.com> <4lk6vl$gde@ns.oar.net>
-To: 75176.2330@compuserve.com
-Content-Type: text/plain; charset=us-ascii
-Organization: Yale University
-X-Url: news:4lk6vl$gde@ns.oar.net
-Mime-Version: 1.0
-Newsgroups: rec.games.roguelike.nethack
-X-Mailer: Mozilla 1.1N (Macintosh; I; 68K)
-
-Hello there, Izchak Miller was my father. When I was younger I spent
-many a night, hunched over the keyboard with a cup of tea, playing
-nethack with him and my brother. my dad was a philosopher with a strong
-weakness for fantasy/sci fi. I remember when he started to get involved
-with the Nethack team- my brother's Dungeons and Dragons monster book
-found a regular place beside my dad's desk. it's nice to see him living
-on in the game he loved so much :-).
- Tamar Miller
-
-The following is a really long word of 5000 characters:
-
-wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
diff --git a/bignum.c b/bignum.c
index c0847e620d..302774b6e2 100644
--- a/bignum.c
+++ b/bignum.c
@@ -12,6 +12,7 @@
#include "internal.h"
#include "ruby/thread.h"
#include "ruby/util.h"
+#include "id.h"
#ifdef HAVE_STRINGS_H
#include <strings.h>
@@ -64,7 +65,6 @@ STATIC_ASSERT(sizeof_long_and_sizeof_bdigit, SIZEOF_BDIGIT % SIZEOF_LONG == 0);
#else
# define HOST_BIGENDIAN_P 0
#endif
-#define ALIGNOF(type) ((int)offsetof(struct { char f1; type f2; }, f2))
/* (!LSHIFTABLE(d, n) ? 0 : (n)) is same as n but suppress a warning, C4293, by Visual Studio. */
#define LSHIFTABLE(d, n) ((n) < sizeof(d) * CHAR_BIT)
#define LSHIFTX(d, n) (!LSHIFTABLE(d, n) ? 0 : ((d) << (!LSHIFTABLE(d, n) ? 0 : (n))))
@@ -138,6 +138,11 @@ STATIC_ASSERT(sizeof_long_and_sizeof_bdigit, SIZEOF_BDIGIT % SIZEOF_LONG == 0);
#define GMP_DIV_DIGITS 20
#define GMP_BIG2STR_DIGITS 20
#define GMP_STR2BIG_DIGITS 20
+#ifdef USE_GMP
+# define NAIVE_MUL_DIGITS GMP_MUL_DIGITS
+#else
+# define NAIVE_MUL_DIGITS KARATSUBA_MUL_DIGITS
+#endif
typedef void (mulfunc_t)(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn, BDIGIT *wds, size_t wn);
@@ -380,6 +385,7 @@ bdigitdbl2bary(BDIGIT *ds, size_t n, BDIGIT_DBL num)
static int
bary_cmp(const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn)
{
+ size_t i;
BARY_TRUNC(xds, xn);
BARY_TRUNC(yds, yn);
@@ -388,11 +394,12 @@ bary_cmp(const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn)
if (xn > yn)
return 1;
- while (xn-- && xds[xn] == yds[xn])
- ;
- if (xn == (size_t)-1)
+ for (i = 0; i < xn; i++)
+ if (xds[xn - i - 1] != yds[yn - i - 1])
+ break;
+ if (i == xn)
return 0;
- return xds[xn] < yds[xn] ? -1 : 1;
+ return xds[xn - i - 1] < yds[yn - i - 1] ? -1 : 1;
}
static BDIGIT
@@ -413,22 +420,22 @@ bary_small_lshift(BDIGIT *zds, const BDIGIT *xds, size_t n, int shift)
static void
bary_small_rshift(BDIGIT *zds, const BDIGIT *xds, size_t n, int shift, BDIGIT higher_bdigit)
{
+ size_t i;
BDIGIT_DBL num = 0;
- BDIGIT x;
assert(0 <= shift && shift < BITSPERDIG);
num = BIGUP(higher_bdigit);
- while (n--) {
- num = (num | xds[n]) >> shift;
- x = xds[n];
- zds[n] = BIGLO(num);
+ for (i = 0; i < n; i++) {
+ BDIGIT x = xds[n - i - 1];
+ num = (num | x) >> shift;
+ zds[n - i - 1] = BIGLO(num);
num = BIGUP(x);
}
}
static int
-bary_zero_p(BDIGIT *xds, size_t xn)
+bary_zero_p(const BDIGIT *xds, size_t xn)
{
if (xn == 0)
return 1;
@@ -441,8 +448,9 @@ bary_zero_p(BDIGIT *xds, size_t xn)
static void
bary_neg(BDIGIT *ds, size_t n)
{
- while (n--)
- ds[n] = BIGLO(~ds[n]);
+ size_t i;
+ for (i = 0; i < n; i++)
+ ds[n - i - 1] = BIGLO(~ds[n - i - 1]);
}
static int
@@ -612,8 +620,12 @@ static int
bytes_2comp(unsigned char *buf, size_t len)
{
size_t i;
- for (i = 0; i < len; i++)
- buf[i] = ~buf[i];
+ for (i = 0; i < len; i++) {
+ signed char c = buf[i];
+ signed int d = ~c;
+ unsigned int e = d & 0xFF;
+ buf[i] = e;
+ }
for (i = 0; i < len; i++) {
buf[i]++;
if (buf[i] != 0)
@@ -663,7 +675,7 @@ bary_pack(int sign, BDIGIT *ds, size_t num_bdigits, void *words, size_t numwords
return ((1 < de - dp || CLEAR_LOWBITS(d, 8) != 0) ? 2 : 1) * sign;
}
#if defined(HAVE_UINT16_T) && 2 <= SIZEOF_BDIGIT
- if (wordsize == 2 && (uintptr_t)words % ALIGNOF(uint16_t) == 0) {
+ if (wordsize == 2 && (uintptr_t)words % RUBY_ALIGNOF(uint16_t) == 0) {
uint16_t u = (uint16_t)(d = dp[0]);
if (need_swap) u = swap16(u);
*((uint16_t *)words) = u;
@@ -671,7 +683,7 @@ bary_pack(int sign, BDIGIT *ds, size_t num_bdigits, void *words, size_t numwords
}
#endif
#if defined(HAVE_UINT32_T) && 4 <= SIZEOF_BDIGIT
- if (wordsize == 4 && (uintptr_t)words % ALIGNOF(uint32_t) == 0) {
+ if (wordsize == 4 && (uintptr_t)words % RUBY_ALIGNOF(uint32_t) == 0) {
uint32_t u = (uint32_t)(d = dp[0]);
if (need_swap) u = swap32(u);
*((uint32_t *)words) = u;
@@ -679,7 +691,7 @@ bary_pack(int sign, BDIGIT *ds, size_t num_bdigits, void *words, size_t numwords
}
#endif
#if defined(HAVE_UINT64_T) && 8 <= SIZEOF_BDIGIT
- if (wordsize == 8 && (uintptr_t)words % ALIGNOF(uint64_t) == 0) {
+ if (wordsize == 8 && (uintptr_t)words % RUBY_ALIGNOF(uint64_t) == 0) {
uint64_t u = (uint64_t)(d = dp[0]);
if (need_swap) u = swap64(u);
*((uint64_t *)words) = u;
@@ -694,7 +706,7 @@ bary_pack(int sign, BDIGIT *ds, size_t num_bdigits, void *words, size_t numwords
return (1 < de - dp || FILL_LOWBITS(d, 8) != -1) ? -2 : -1;
}
#if defined(HAVE_UINT16_T) && 2 <= SIZEOF_BDIGIT
- if (wordsize == 2 && (uintptr_t)words % ALIGNOF(uint16_t) == 0) {
+ if (wordsize == 2 && (uintptr_t)words % RUBY_ALIGNOF(uint16_t) == 0) {
uint16_t u = (uint16_t)(d = -(BDIGIT_DBL_SIGNED)dp[0]);
if (need_swap) u = swap16(u);
*((uint16_t *)words) = u;
@@ -703,7 +715,7 @@ bary_pack(int sign, BDIGIT *ds, size_t num_bdigits, void *words, size_t numwords
}
#endif
#if defined(HAVE_UINT32_T) && 4 <= SIZEOF_BDIGIT
- if (wordsize == 4 && (uintptr_t)words % ALIGNOF(uint32_t) == 0) {
+ if (wordsize == 4 && (uintptr_t)words % RUBY_ALIGNOF(uint32_t) == 0) {
uint32_t u = (uint32_t)(d = -(BDIGIT_DBL_SIGNED)dp[0]);
if (need_swap) u = swap32(u);
*((uint32_t *)words) = u;
@@ -712,7 +724,7 @@ bary_pack(int sign, BDIGIT *ds, size_t num_bdigits, void *words, size_t numwords
}
#endif
#if defined(HAVE_UINT64_T) && 8 <= SIZEOF_BDIGIT
- if (wordsize == 8 && (uintptr_t)words % ALIGNOF(uint64_t) == 0) {
+ if (wordsize == 8 && (uintptr_t)words % RUBY_ALIGNOF(uint64_t) == 0) {
uint64_t u = (uint64_t)(d = -(BDIGIT_DBL_SIGNED)dp[0]);
if (need_swap) u = swap64(u);
*((uint64_t *)words) = u;
@@ -755,7 +767,7 @@ bary_pack(int sign, BDIGIT *ds, size_t num_bdigits, void *words, size_t numwords
}
#endif
if (nails == 0 && SIZEOF_BDIGIT == sizeof(BDIGIT) &&
- wordsize % SIZEOF_BDIGIT == 0 && (uintptr_t)words % ALIGNOF(BDIGIT) == 0) {
+ wordsize % SIZEOF_BDIGIT == 0 && (uintptr_t)words % RUBY_ALIGNOF(BDIGIT) == 0) {
size_t bdigits_per_word = wordsize / SIZEOF_BDIGIT;
size_t src_num_bdigits = de - dp;
size_t dst_num_bdigits = numwords * bdigits_per_word;
@@ -898,8 +910,6 @@ bary_pack(int sign, BDIGIT *ds, size_t num_bdigits, void *words, size_t numwords
}
if ((flags & INTEGER_PACK_2COMP) && (sign < 0 && numwords != 0)) {
- unsigned char *buf;
-
int word_num_partialbits;
size_t word_num_fullbytes;
@@ -1077,6 +1087,13 @@ integer_unpack_single_bdigit(BDIGIT u, size_t size, int flags, BDIGIT *dp)
return sign;
}
+#ifdef HAVE_BUILTIN___BUILTIN_ASSUME_ALIGNED
+#define reinterpret_cast(type, value) (type) \
+ __builtin_assume_aligned((value), sizeof(*(type)NULL));
+#else
+#define reinterpret_cast(type, value) (type)value
+#endif
+
static int
bary_unpack_internal(BDIGIT *bdigits, size_t num_bdigits, const void *words, size_t numwords, size_t wordsize, size_t nails, int flags, int nlp_bits)
{
@@ -1097,23 +1114,24 @@ bary_unpack_internal(BDIGIT *bdigits, size_t num_bdigits, const void *words, siz
return integer_unpack_single_bdigit(*(uint8_t *)buf, sizeof(uint8_t), flags, dp);
}
#if defined(HAVE_UINT16_T) && 2 <= SIZEOF_BDIGIT
- if (wordsize == 2 && (uintptr_t)words % ALIGNOF(uint16_t) == 0) {
- uint16_t u = *(uint16_t *)buf;
+ if (wordsize == 2 && (uintptr_t)words % RUBY_ALIGNOF(uint16_t) == 0) {
+ uint16_t u = *reinterpret_cast(const uint16_t *, buf);
return integer_unpack_single_bdigit(need_swap ? swap16(u) : u, sizeof(uint16_t), flags, dp);
}
#endif
#if defined(HAVE_UINT32_T) && 4 <= SIZEOF_BDIGIT
- if (wordsize == 4 && (uintptr_t)words % ALIGNOF(uint32_t) == 0) {
- uint32_t u = *(uint32_t *)buf;
+ if (wordsize == 4 && (uintptr_t)words % RUBY_ALIGNOF(uint32_t) == 0) {
+ uint32_t u = *reinterpret_cast(const uint32_t *, buf);
return integer_unpack_single_bdigit(need_swap ? swap32(u) : u, sizeof(uint32_t), flags, dp);
}
#endif
#if defined(HAVE_UINT64_T) && 8 <= SIZEOF_BDIGIT
- if (wordsize == 8 && (uintptr_t)words % ALIGNOF(uint64_t) == 0) {
- uint64_t u = *(uint64_t *)buf;
+ if (wordsize == 8 && (uintptr_t)words % RUBY_ALIGNOF(uint64_t) == 0) {
+ uint64_t u = *reinterpret_cast(const uint64_t *, buf);
return integer_unpack_single_bdigit(need_swap ? swap64(u) : u, sizeof(uint64_t), flags, dp);
}
#endif
+#undef reinterpret_cast
}
#if !defined(WORDS_BIGENDIAN)
if (nails == 0 && SIZEOF_BDIGIT == sizeof(BDIGIT) &&
@@ -1429,7 +1447,9 @@ bary_add_one(BDIGIT *ds, size_t n)
{
size_t i;
for (i = 0; i < n; i++) {
- ds[i] = BIGLO(ds[i]+1);
+ BDIGIT_DBL n = ds[i];
+ n += 1;
+ ds[i] = BIGLO(n);
if (ds[i] != 0)
return 0;
}
@@ -1497,15 +1517,16 @@ bigdivrem_mulsub(BDIGIT *zds, size_t zn, BDIGIT x, const BDIGIT *yds, size_t yn)
i = 0;
do {
- BDIGIT_DBL ee;
+ BDIGIT_DBL_SIGNED ee;
t2 += (BDIGIT_DBL)yds[i] * x;
ee = num - BIGLO(t2);
- num = (BDIGIT_DBL)zds[i] + ee;
+ num = (BDIGIT_DBL_SIGNED)zds[i] + ee;
if (ee) zds[i] = BIGLO(num);
num = BIGDN(num);
t2 = BIGDN(t2);
} while (++i < yn);
- num += zds[i] - t2; /* borrow from high digit; don't update */
+ num -= (BDIGIT_DBL_SIGNED)t2;
+ num += (BDIGIT_DBL_SIGNED)zds[yn]; /* borrow from high digit; don't update */
return num;
}
@@ -2487,13 +2508,8 @@ bary_mul_toom3_start(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const
static void
bary_mul(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn)
{
-#ifdef USE_GMP
- const size_t naive_threshold = GMP_MUL_DIGITS;
-#else
- const size_t naive_threshold = KARATSUBA_MUL_DIGITS;
-#endif
if (xn <= yn) {
- if (xn < naive_threshold) {
+ if (xn < NAIVE_MUL_DIGITS) {
if (xds == yds && xn == yn)
bary_sq_fast(zds, zn, xds, xn);
else
@@ -2502,7 +2518,7 @@ bary_mul(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yds
}
}
else {
- if (yn < naive_threshold) {
+ if (yn < NAIVE_MUL_DIGITS) {
bary_short_mul(zds, zn, yds, yn, xds, xn);
return;
}
@@ -2578,10 +2594,9 @@ bigdivrem_single1(BDIGIT *qds, const BDIGIT *xds, size_t xn, BDIGIT x_higher_bdi
size_t i;
BDIGIT_DBL t2;
t2 = x_higher_bdigit;
- i = xn;
- while (i--) {
- t2 = BIGUP(t2) + xds[i];
- qds[i] = (BDIGIT)(t2 / y);
+ for (i = 0; i < xn; i++) {
+ t2 = BIGUP(t2) + xds[xn - i - 1];
+ qds[xn - i - 1] = (BDIGIT)(t2 / y);
t2 %= y;
}
return (BDIGIT)t2;
@@ -3139,7 +3154,7 @@ rb_big_norm(VALUE x)
}
VALUE
-rb_uint2big(VALUE n)
+rb_uint2big(uintptr_t n)
{
long i;
VALUE big = bignew(bdigit_roomof(SIZEOF_VALUE), 1);
@@ -3161,7 +3176,7 @@ rb_uint2big(VALUE n)
}
VALUE
-rb_int2big(SIGNED_VALUE n)
+rb_int2big(intptr_t n)
{
long neg = 0;
VALUE u;
@@ -3182,14 +3197,14 @@ rb_int2big(SIGNED_VALUE n)
}
VALUE
-rb_uint2inum(VALUE n)
+rb_uint2inum(uintptr_t n)
{
if (POSFIXABLE(n)) return LONG2FIX(n);
return rb_uint2big(n);
}
VALUE
-rb_int2inum(SIGNED_VALUE n)
+rb_int2inum(intptr_t n)
{
if (FIXABLE(n)) return LONG2FIX(n);
return rb_int2big(n);
@@ -4011,22 +4026,28 @@ rb_cstr_to_inum(const char *str, int base, int badcheck)
* be NUL-terminated.
* endp: if non-NULL, the address after parsed part is stored. if
* NULL, Qnil is returned when +str+ is not valid as an Integer.
+ * ndigits: if non-NULL, the number of parsed digits is stored.
* base: see +rb_cstr_to_inum+
+ * flags: bitwise OR of below flags:
+ * RB_INT_PARSE_SIGN: allow preceding spaces and +/- sign
+ * RB_INT_PARSE_UNDERSCORE: allow an underscore between digits
+ * RB_INT_PARSE_PREFIX: allow preceding prefix
*/
VALUE
-rb_cstr_parse_inum(const char *str, ssize_t len, char **endp, int base)
+rb_int_parse_cstr(const char *str, ssize_t len, char **endp, size_t *ndigits,
+ int base, int flags)
{
const char *const s = str;
char sign = 1;
int c;
- VALUE z;
+ VALUE z = Qnil;
unsigned long val;
int ov;
const char *digits_start, *digits_end;
- size_t num_digits;
+ size_t num_digits = 0;
size_t num_bdigits;
const ssize_t len0 = len;
const int badcheck = !endp;
@@ -4044,9 +4065,10 @@ rb_cstr_parse_inum(const char *str, ssize_t len, char **endp, int base)
if (!str) {
bad:
if (endp) *endp = (char *)str;
- return Qnil;
+ if (ndigits) *ndigits = num_digits;
+ return z;
}
- if (len) {
+ if (len && (flags & RB_INT_PARSE_SIGN)) {
while (ISSPACE(*str)) ADV(1);
if (str[0] == '+') {
@@ -4057,9 +4079,6 @@ rb_cstr_parse_inum(const char *str, ssize_t len, char **endp, int base)
sign = 0;
}
ASSERT_LEN();
- if (str[0] == '+' || str[0] == '-') {
- goto bad;
- }
}
if (base <= 0) {
if (str[0] == '0' && len > 1) {
@@ -4091,7 +4110,7 @@ rb_cstr_parse_inum(const char *str, ssize_t len, char **endp, int base)
base = 10;
}
}
- else if (len == 1) {
+ else if (len == 1 || !(flags & RB_INT_PARSE_PREFIX)) {
/* no prefix */
}
else if (base == 2) {
@@ -4118,15 +4137,19 @@ rb_cstr_parse_inum(const char *str, ssize_t len, char **endp, int base)
invalid_radix(base);
}
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;
- while ((c = *++str) == '0' || c == '_') {
+ ++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;
@@ -4138,14 +4161,18 @@ rb_cstr_parse_inum(const char *str, ssize_t len, char **endp, int base)
c = *str;
c = conv_digit(c);
if (c < 0 || c >= base) {
+ 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 == '_') goto bigparse;
+ 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) {
@@ -4172,6 +4199,8 @@ rb_cstr_parse_inum(const char *str, ssize_t len, char **endp, int base)
digits_start = str;
if (!str2big_scan_digits(s, str, base, badcheck, &num_digits, &len))
goto bad;
+ if (endp) *endp = (char *)(str + len);
+ if (ndigits) *ndigits += num_digits;
digits_end = digits_start + len;
if (POW2_P(base)) {
@@ -4204,7 +4233,14 @@ rb_cstr_parse_inum(const char *str, ssize_t len, char **endp, int base)
}
VALUE
-rb_str_to_inum(VALUE str, int base, int badcheck)
+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);
+}
+
+VALUE
+rb_str_convert_to_inum(VALUE str, int base, int badcheck, int raise_exception)
{
VALUE ret;
const char *s;
@@ -4216,13 +4252,22 @@ rb_str_to_inum(VALUE str, int base, int badcheck)
RSTRING_GETMEM(str, s, len);
ret = rb_cstr_parse_inum(s, len, (badcheck ? NULL : &end), base);
if (NIL_P(ret)) {
- if (badcheck) invalid_integer(str);
- ret = INT2FIX(0);
+ if (badcheck) {
+ if (!raise_exception) return Qnil;
+ invalid_integer(str);
+ }
+ ret = INT2FIX(0);
}
return ret;
}
VALUE
+rb_str_to_inum(VALUE str, int base, int badcheck)
+{
+ return rb_str_convert_to_inum(str, base, badcheck, TRUE);
+}
+
+VALUE
rb_str2big_poweroftwo(VALUE arg, int base, int badcheck)
{
int positive_p = 1;
@@ -4464,7 +4509,7 @@ rb_uint128t2big(uint128_t n)
return big;
}
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_int128t2big(int128_t n)
{
int neg = 0;
@@ -5048,6 +5093,9 @@ rb_big2str(VALUE x, int base)
static unsigned long
big2ulong(VALUE x, const char *type)
{
+#if SIZEOF_LONG > SIZEOF_BDIGIT
+ size_t i;
+#endif
size_t len = BIGNUM_LEN(x);
unsigned long num;
BDIGIT *ds;
@@ -5062,9 +5110,9 @@ big2ulong(VALUE x, const char *type)
num = (unsigned long)ds[0];
#else
num = 0;
- while (len--) {
+ for (i = 0; i < len; i++) {
num <<= BITSPERDIG;
- num += (unsigned long)ds[len]; /* overflow is already checked */
+ num += (unsigned long)ds[len - i - 1]; /* overflow is already checked */
}
#endif
return num;
@@ -5106,6 +5154,9 @@ rb_big2long(VALUE x)
static unsigned LONG_LONG
big2ull(VALUE x, const char *type)
{
+#if SIZEOF_LONG_LONG > SIZEOF_BDIGIT
+ size_t i;
+#endif
size_t len = BIGNUM_LEN(x);
unsigned LONG_LONG num;
BDIGIT *ds = BDIGITS(x);
@@ -5118,9 +5169,9 @@ big2ull(VALUE x, const char *type)
num = (unsigned LONG_LONG)ds[0];
#else
num = 0;
- while (len--) {
+ for (i = 0; i < len; i++) {
num = BIGUP(num);
- num += ds[len];
+ num += ds[len - i - 1];
}
#endif
return num;
@@ -5227,8 +5278,13 @@ big2dbl(VALUE x)
}
}
if (carry) {
- dl &= BDIGMAX << bits;
- dl = BIGLO(dl + ((BDIGIT)1 << bits));
+ BDIGIT mask = BDIGMAX;
+ BDIGIT bit = 1;
+ mask <<= bits;
+ bit <<= bits;
+ dl &= mask;
+ dl += bit;
+ dl = BIGLO(dl);
if (!dl) d += 1;
}
}
@@ -5367,7 +5423,7 @@ rb_big_cmp(VALUE x, VALUE y)
return rb_integer_float_cmp(x, y);
}
else {
- return rb_num_coerce_cmp(x, y, rb_intern("<=>"));
+ return rb_num_coerce_cmp(x, y, idCmp);
}
return INT2FIX(BIGNUM_SIGN(x) ? 1 : -1);
}
@@ -5395,9 +5451,9 @@ big_op(VALUE x, VALUE y, enum big_op_t op)
ID id = 0;
switch (op) {
case big_op_gt: id = '>'; break;
- case big_op_ge: id = rb_intern(">="); break;
+ case big_op_ge: id = idGE; break;
case big_op_lt: id = '<'; break;
- case big_op_le: id = rb_intern("<="); break;
+ case big_op_le: id = idLE; break;
}
return rb_num_coerce_relop(x, y, id);
}
@@ -5822,17 +5878,10 @@ bigsq(VALUE x)
xds = BDIGITS(x);
zds = BDIGITS(z);
-#ifdef USE_GMP
- if (xn < GMP_MUL_DIGITS)
+ if (xn < NAIVE_MUL_DIGITS)
bary_sq_fast(zds, zn, xds, xn);
else
bary_mul(zds, zn, xds, xn, xds, xn);
-#else
- if (xn < KARATSUBA_MUL_DIGITS)
- bary_sq_fast(zds, zn, xds, xn);
- else
- bary_mul(zds, zn, xds, xn, xds, xn);
-#endif
RB_GC_GUARD(x);
return z;
@@ -5914,7 +5963,7 @@ bigdivrem(VALUE x, VALUE y, volatile VALUE *divp, volatile VALUE *modp)
zds = BDIGITS(z);
dd = bigdivrem_single(zds, xds, xn, dd);
if (modp) {
- *modp = rb_uint2big((VALUE)dd);
+ *modp = rb_uint2big((uintptr_t)dd);
BIGNUM_SET_SIGN(*modp, BIGNUM_SIGN(x));
}
if (divp) *divp = z;
@@ -6004,12 +6053,15 @@ rb_big_divide(VALUE x, VALUE y, ID op)
}
else if (RB_FLOAT_TYPE_P(y)) {
if (op == '/') {
- return DBL2NUM(rb_big2dbl(x) / RFLOAT_VALUE(y));
+ double dx = rb_big2dbl(x);
+ return rb_flo_div_flo(DBL2NUM(dx), y);
}
else {
+ VALUE v;
double dy = RFLOAT_VALUE(y);
if (dy == 0.0) rb_num_zerodiv();
- return rb_dbl2big(rb_big2dbl(x) / dy);
+ v = rb_big_divide(x, y, '/');
+ return rb_dbl2big(RFLOAT_VALUE(v));
}
}
else {
@@ -6090,10 +6142,11 @@ big_shift(VALUE x, long n)
return x;
}
+enum {DBL_BIGDIG = ((DBL_MANT_DIG + BITSPERDIG) / BITSPERDIG)};
+
static double
big_fdiv(VALUE x, VALUE y, long ey)
{
-#define DBL_BIGDIG ((DBL_MANT_DIG + BITSPERDIG) / BITSPERDIG)
VALUE z;
long l, ex;
@@ -6101,6 +6154,8 @@ big_fdiv(VALUE x, VALUE y, long ey)
l = BIGNUM_LEN(x);
ex = l * BITSPERDIG - nlz(BDIGITS(x)[l-1]);
ex -= 2 * DBL_BIGDIG * BITSPERDIG;
+ if (ex > BITSPERDIG) ex -= BITSPERDIG;
+ else if (ex > 0) ex = 0;
if (ex) x = big_shift(x, ex);
bigdivrem(x, y, &z, 0);
@@ -6108,7 +6163,7 @@ big_fdiv(VALUE x, VALUE y, long ey)
#if SIZEOF_LONG > SIZEOF_INT
{
/* Visual C++ can't be here */
- if (l > INT_MAX) return INFINITY;
+ if (l > INT_MAX) return HUGE_VAL;
if (l < INT_MIN) return 0.0;
}
#endif
@@ -6139,6 +6194,7 @@ double
rb_big_fdiv_double(VALUE x, VALUE y)
{
double dx, dy;
+ VALUE v;
dx = big2dbl(x);
if (FIXNUM_P(y)) {
@@ -6147,9 +6203,7 @@ rb_big_fdiv_double(VALUE x, VALUE y)
return big_fdiv_int(x, rb_int2big(FIX2LONG(y)));
}
else if (RB_BIGNUM_TYPE_P(y)) {
- dy = rb_big2dbl(y);
- if (isinf(dx) || isinf(dy))
- return big_fdiv_int(x, y);
+ return big_fdiv_int(x, y);
}
else if (RB_FLOAT_TYPE_P(y)) {
dy = RFLOAT_VALUE(y);
@@ -6159,9 +6213,10 @@ rb_big_fdiv_double(VALUE x, VALUE y)
return big_fdiv_float(x, y);
}
else {
- return RFLOAT_VALUE(rb_num_coerce_bin(x, y, rb_intern("fdiv")));
+ return NUM2DBL(rb_num_coerce_bin(x, y, rb_intern("fdiv")));
}
- return dx / dy;
+ v = rb_flo_div_flo(DBL2NUM(dx), DBL2NUM(dy));
+ return NUM2DBL(v);
}
VALUE
@@ -6178,10 +6233,12 @@ rb_big_pow(VALUE x, VALUE y)
again:
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 != round(d))
- return rb_funcall(rb_complex_raw1(x), rb_intern("**"), 1, 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);
@@ -6193,8 +6250,13 @@ rb_big_pow(VALUE x, VALUE y)
else if (FIXNUM_P(y)) {
yy = FIX2LONG(y);
- if (yy < 0)
- return rb_funcall(rb_rational_raw1(x), rb_intern("**"), 1, y);
+ if (yy < 0) {
+ x = rb_big_pow(x, INT2NUM(-yy));
+ if (RB_INTEGER_TYPE_P(x))
+ return rb_rational_raw(INT2FIX(1), x);
+ else
+ return DBL2NUM(1.0 / NUM2DBL(x));
+ }
else {
VALUE z = 0;
SIGNED_VALUE mask;
@@ -6219,7 +6281,7 @@ rb_big_pow(VALUE x, VALUE y)
}
}
else {
- return rb_num_coerce_bin(x, y, rb_intern("**"));
+ return rb_num_coerce_bin(x, y, idPow);
}
return DBL2NUM(pow(rb_big2dbl(x), d));
}
@@ -6691,6 +6753,12 @@ rb_big_abs(VALUE x)
return x;
}
+int
+rb_big_sign(VALUE x)
+{
+ return BIGNUM_SIGN(x);
+}
+
size_t
rb_big_size(VALUE big)
{
@@ -6762,6 +6830,316 @@ rb_big_even_p(VALUE num)
return Qtrue;
}
+unsigned long rb_ulong_isqrt(unsigned long);
+#if SIZEOF_BDIGIT*2 > SIZEOF_LONG
+BDIGIT rb_bdigit_dbl_isqrt(BDIGIT_DBL);
+# ifdef ULL_TO_DOUBLE
+# define BDIGIT_DBL_TO_DOUBLE(n) ULL_TO_DOUBLE(n)
+# endif
+#else
+# define rb_bdigit_dbl_isqrt(x) (BDIGIT)rb_ulong_isqrt(x)
+#endif
+#ifndef BDIGIT_DBL_TO_DOUBLE
+# define BDIGIT_DBL_TO_DOUBLE(n) (double)(n)
+#endif
+
+static BDIGIT *
+estimate_initial_sqrt(VALUE *xp, const size_t xn, const BDIGIT *nds, size_t len)
+{
+ enum {dbl_per_bdig = roomof(DBL_MANT_DIG,BITSPERDIG)};
+ const int zbits = nlz(nds[len-1]);
+ VALUE x = *xp = bignew_1(0, xn, 1); /* division may release the GVL */
+ BDIGIT *xds = BDIGITS(x);
+ BDIGIT_DBL d = bary2bdigitdbl(nds+len-dbl_per_bdig, dbl_per_bdig);
+ BDIGIT lowbits = 1;
+ int rshift = (int)((BITSPERDIG*2-zbits+(len&BITSPERDIG&1) - DBL_MANT_DIG + 1) & ~1);
+ double f;
+
+ if (rshift > 0) {
+ lowbits = (BDIGIT)d & ~(~(BDIGIT)1U << rshift);
+ d >>= rshift;
+ }
+ else if (rshift < 0) {
+ 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;
+ }
+ else {
+ lowbits = 1;
+ }
+ rshift /= 2;
+ rshift += (2-(len&1))*BITSPERDIG/2;
+ if (rshift >= 0) {
+ if (nlz((BDIGIT)d) + rshift >= BITSPERDIG) {
+ /* (d << rshift) does cause overflow.
+ * example: Integer.sqrt(0xffff_ffff_ffff_ffff ** 2)
+ */
+ d = ~(BDIGIT_DBL)0;
+ }
+ else {
+ d <<= rshift;
+ }
+ }
+ BDIGITS_ZERO(xds, xn-2);
+ bdigitdbl2bary(&xds[xn-2], 2, d);
+
+ if (!lowbits) return NULL; /* special case, exact result */
+ return xds;
+}
+
+VALUE
+rb_big_isqrt(VALUE n)
+{
+ BDIGIT *nds = BDIGITS(n);
+ size_t len = BIGNUM_LEN(n);
+ size_t xn = (len+1) / 2;
+ VALUE x;
+ BDIGIT *xds;
+
+ if (len <= 2) {
+ BDIGIT sq = rb_bdigit_dbl_isqrt(bary2bdigitdbl(nds, len));
+#if SIZEOF_BDIGIT > SIZEOF_LONG
+ return ULL2NUM(sq);
+#else
+ 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);
+ }
+ rb_big_realloc(t, 0);
+ rb_gc_force_recycle(t);
+ }
+ RBASIC_SET_CLASS_RAW(x, rb_cInteger);
+ return x;
+}
+
+#ifdef USE_GMP
+static void
+bary_powm_gmp(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn, const BDIGIT *mds, size_t mn)
+{
+ const size_t nails = (sizeof(BDIGIT)-SIZEOF_BDIGIT)*CHAR_BIT;
+ mpz_t z, x, y, m;
+ size_t count;
+ mpz_init(x);
+ mpz_init(y);
+ mpz_init(m);
+ mpz_init(z);
+ mpz_import(x, xn, -1, sizeof(BDIGIT), 0, nails, xds);
+ mpz_import(y, yn, -1, sizeof(BDIGIT), 0, nails, yds);
+ mpz_import(m, mn, -1, sizeof(BDIGIT), 0, nails, mds);
+ mpz_powm(z, x, y, m);
+ mpz_export(zds, &count, -1, sizeof(BDIGIT), 0, nails, z);
+ BDIGITS_ZERO(zds+count, zn-count);
+ mpz_clear(x);
+ mpz_clear(y);
+ mpz_clear(m);
+ mpz_clear(z);
+}
+#endif
+
+static VALUE
+int_pow_tmp3(VALUE x, VALUE y, VALUE m, int nega_flg)
+{
+#ifdef USE_GMP
+ VALUE z;
+ size_t xn, yn, mn, zn;
+
+ if (FIXNUM_P(x)) {
+ x = rb_int2big(FIX2LONG(x));
+ }
+ if (FIXNUM_P(y)) {
+ y = rb_int2big(FIX2LONG(y));
+ }
+ assert(RB_BIGNUM_TYPE_P(m));
+ xn = BIGNUM_LEN(x);
+ yn = BIGNUM_LEN(y);
+ mn = BIGNUM_LEN(m);
+ zn = mn;
+ z = bignew(zn, 1);
+ bary_powm_gmp(BDIGITS(z), zn, BDIGITS(x), xn, BDIGITS(y), yn, BDIGITS(m), mn);
+ if (nega_flg & BIGNUM_POSITIVE_P(z)) {
+ z = rb_big_minus(z, m);
+ }
+ RB_GC_GUARD(x);
+ RB_GC_GUARD(y);
+ RB_GC_GUARD(m);
+ return rb_big_norm(z);
+#else
+ VALUE tmp = LONG2FIX(1L);
+ long yy;
+
+ for (/*NOP*/; ! FIXNUM_P(y); y = rb_big_rshift(y, LONG2FIX(1L))) {
+ if (RTEST(rb_int_odd_p(y))) {
+ tmp = rb_int_mul(tmp, x);
+ tmp = rb_int_modulo(tmp, m);
+ }
+ x = rb_int_mul(x, x);
+ x = rb_int_modulo(x, m);
+ }
+ for (yy = FIX2LONG(y); yy; yy >>= 1L) {
+ if (yy & 1L) {
+ tmp = rb_int_mul(tmp, x);
+ tmp = rb_int_modulo(tmp, m);
+ }
+ x = rb_int_mul(x, x);
+ x = rb_int_modulo(x, m);
+ }
+
+ if (nega_flg && rb_int_positive_p(tmp)) {
+ tmp = rb_int_minus(tmp, m);
+ }
+ return tmp;
+#endif
+}
+
+/*
+ * Integer#pow
+ */
+
+static VALUE
+int_pow_tmp1(VALUE x, VALUE y, long mm, int nega_flg)
+{
+ long xx = FIX2LONG(x);
+ long tmp = 1L;
+ long yy;
+
+ for (/*NOP*/; ! FIXNUM_P(y); y = rb_big_rshift(y, LONG2FIX(1L))) {
+ if (RTEST(rb_int_odd_p(y))) {
+ tmp = (tmp * xx) % mm;
+ }
+ xx = (xx * xx) % mm;
+ }
+ for (yy = FIX2LONG(y); yy; yy >>= 1L) {
+ if (yy & 1L) {
+ tmp = (tmp * xx) % mm;
+ }
+ xx = (xx * xx) % mm;
+ }
+
+ if (nega_flg && tmp) {
+ tmp -= mm;
+ }
+ return LONG2FIX(tmp);
+}
+
+static VALUE
+int_pow_tmp2(VALUE x, VALUE y, long mm, int nega_flg)
+{
+ long tmp = 1L;
+ long yy;
+#ifdef DLONG
+ const DLONG m = mm;
+ long tmp2 = tmp;
+ long xx = FIX2LONG(x);
+# define MUL_MODULO(a, b, c) (long)(((DLONG)(a) * (DLONG)(b)) % (c))
+#else
+ const VALUE m = LONG2FIX(mm);
+ VALUE tmp2 = LONG2FIX(tmp);
+ VALUE xx = x;
+# define MUL_MODULO(a, b, c) rb_int_modulo(rb_fix_mul_fix((a), (b)), (c))
+#endif
+
+ for (/*NOP*/; ! FIXNUM_P(y); y = rb_big_rshift(y, LONG2FIX(1L))) {
+ if (RTEST(rb_int_odd_p(y))) {
+ tmp2 = MUL_MODULO(tmp2, xx, m);
+ }
+ xx = MUL_MODULO(xx, xx, m);
+ }
+ for (yy = FIX2LONG(y); yy; yy >>= 1L) {
+ if (yy & 1L) {
+ tmp2 = MUL_MODULO(tmp2, xx, m);
+ }
+ xx = MUL_MODULO(xx, xx, m);
+ }
+
+#ifdef DLONG
+ tmp = tmp2;
+#else
+ tmp = FIX2LONG(tmp2);
+#endif
+ if (nega_flg && tmp) {
+ tmp -= mm;
+ }
+ return LONG2FIX(tmp);
+}
+
+/*
+ * Document-method: Integer#pow
+ * call-seq:
+ * integer.pow(numeric) -> numeric
+ * integer.pow(integer, integer) -> integer
+ *
+ * Returns (modular) exponentiation as:
+ *
+ * a.pow(b) #=> same as a**b
+ * a.pow(b, m) #=> same as (a**b) % m, but avoids huge temporary values
+ */
+VALUE
+rb_int_powm(int const argc, VALUE * const argv, VALUE const num)
+{
+ rb_check_arity(argc, 1, 2);
+
+ if (argc == 1) {
+ return rb_int_pow(num, argv[0]);
+ }
+ else {
+ VALUE const a = num;
+ VALUE const b = argv[0];
+ VALUE m = argv[1];
+ int nega_flg = 0;
+ if ( ! RB_INTEGER_TYPE_P(b)) {
+ rb_raise(rb_eTypeError, "Integer#pow() 2nd argument not allowed unless a 1st argument is integer");
+ }
+ if (rb_int_negative_p(b)) {
+ rb_raise(rb_eRangeError, "Integer#pow() 1st argument cannot be negative when 2nd argument specified");
+ }
+ if (!RB_INTEGER_TYPE_P(m)) {
+ rb_raise(rb_eTypeError, "Integer#pow() 2nd argument not allowed unless all arguments are integers");
+ }
+
+ if (rb_int_negative_p(m)) {
+ m = rb_int_uminus(m);
+ nega_flg = 1;
+ }
+
+ if (FIXNUM_P(m)) {
+ long const half_val = (long)HALF_LONG_MSB;
+ long const mm = FIX2LONG(m);
+ if (!mm) rb_num_zerodiv();
+ if (mm <= half_val) {
+ return int_pow_tmp1(rb_int_modulo(a, m), b, mm, nega_flg);
+ }
+ else {
+ return int_pow_tmp2(rb_int_modulo(a, m), b, mm, nega_flg);
+ }
+ }
+ else {
+ if (rb_bigzero_p(m)) rb_num_zerodiv();
+ return int_pow_tmp3(rb_int_modulo(a, m), b, m, nega_flg);
+ }
+ }
+ UNREACHABLE_RETURN(Qnil);
+}
+
/*
* Bignum objects hold integers outside the range of
* Fixnum. Bignum objects are created
@@ -6786,6 +7164,7 @@ Init_Bignum(void)
#ifndef RUBY_INTEGER_UNIFICATION
rb_cBignum = rb_cInteger;
#endif
+ /* An obsolete class, use Integer */
rb_define_const(rb_cObject, "Bignum", rb_cInteger);
rb_deprecate_constant(rb_cObject, "Bignum");
diff --git a/bin/bundle b/bin/bundle
new file mode 100755
index 0000000000..1a0b06b005
--- /dev/null
+++ b/bin/bundle
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by RubyGems.
+#
+# The application 'bundler' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require 'rubygems'
+
+version = ">= 0.a"
+
+str = ARGV.first
+if str
+ str = str.b[/\A_(.*)_\z/, 1]
+ if str and Gem::Version.correct?(str)
+ version = str
+ 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
diff --git a/bin/bundler b/bin/bundler
new file mode 100755
index 0000000000..e15eb39ed7
--- /dev/null
+++ b/bin/bundler
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by RubyGems.
+#
+# The application 'bundler' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require 'rubygems'
+
+version = ">= 0.a"
+
+str = ARGV.first
+if str
+ str = str.b[/\A_(.*)_\z/, 1]
+ if str and Gem::Version.correct?(str)
+ version = str
+ ARGV.shift
+ end
+end
+
+if Gem.respond_to?(:activate_bin_path)
+load Gem.activate_bin_path('bundler', 'bundler', version)
+else
+gem "bundler", version
+load Gem.bin_path("bundler", "bundler", version)
+end
diff --git a/bin/erb b/bin/erb
index 6a88c3b26a..d5e51ee969 100755
--- a/bin/erb
+++ b/bin/erb
@@ -75,6 +75,7 @@ class ERB
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
@@ -112,22 +113,26 @@ class ERB
-v enable verbose mode
-d set $DEBUG to true
-r library load a library
- -S safe_level set $SAFE (0..1)
-E ex[:in] set default external/internal encodings
- -U set default encoding to UTF-8.
+ -U set default encoding to UTF-8
-T trim_mode specify trim_mode (0..2, -)
- -P ignore lines which start with "%"
+ -P disable ruby code evaluation for lines beginning with %
var=value set variable
EOU
exit 1
end
- $<.set_encoding(Encoding::ASCII_8BIT, nil)
+ $<.set_encoding(Encoding::UTF_8, nil)
src = $<.read
filename = $FILENAME
exit 2 unless src
trim = trim_mode_opt(trim_mode, disable_percent)
- erb = factory.new(src.untaint, safe_level, trim)
+ if safe_level.nil?
+ erb = factory.new(src.untaint, trim_mode: trim)
+ else
+ # [deprecated] This will be removed at Ruby 2.7.
+ erb = factory.new(src.untaint, safe_level, trim_mode: trim)
+ end
erb.filename = filename
if output
if number
diff --git a/bin/irb b/bin/irb
index c64ee85fbd..ae6d358c9d 100755
--- a/bin/irb
+++ b/bin/irb
@@ -1,11 +1,27 @@
#!/usr/bin/env ruby
#
-# irb.rb - interactive ruby
-# $Release Version: 0.9.6 $
-# $Revision$
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+# This file was generated by RubyGems.
#
+# The application 'irb' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require 'rubygems'
+
+version = ">= 0.a"
-require "irb"
+str = ARGV.first
+if str
+ str = str.b[/\A_(.*)_\z/, 1]
+ if str and Gem::Version.correct?(str)
+ version = str
+ ARGV.shift
+ end
+end
-IRB.start(__FILE__)
+if Gem.respond_to?(:activate_bin_path)
+load Gem.activate_bin_path('irb', 'irb', version)
+else
+gem "irb", version
+load Gem.bin_path("irb", "irb", version)
+end
diff --git a/bin/rdoc b/bin/rdoc
index aaa23292df..8fa948cddb 100755
--- a/bin/rdoc
+++ b/bin/rdoc
@@ -1,44 +1,27 @@
#!/usr/bin/env ruby
#
-# RDoc: Documentation tool for source code
-# (see lib/rdoc/rdoc.rb for more information)
+# This file was generated by RubyGems.
+#
+# The application 'rdoc' is installed as part of a gem, and
+# this file is here to facilitate running it.
#
-# Copyright (c) 2003 Dave Thomas
-# Released under the same terms as Ruby
-begin
- gem 'rdoc'
-rescue NameError => e # --disable-gems
- raise unless e.name == :gem
-rescue Gem::LoadError
-end
+require 'rubygems'
-require 'rdoc/rdoc'
+version = ">= 0.a"
-begin
- r = RDoc::RDoc.new
- r.document ARGV
-rescue Errno::ENOSPC
- $stderr.puts 'Ran out of space creating documentation'
- $stderr.puts
- $stderr.puts 'Please free up some space and try again'
-rescue SystemExit
- raise
-rescue Exception => e
- if $DEBUG_RDOC then
- $stderr.puts e.message
- $stderr.puts "#{e.backtrace.join "\n\t"}"
- $stderr.puts
- elsif Interrupt === e then
- $stderr.puts
- $stderr.puts 'Interrupted'
- else
- $stderr.puts "uh-oh! RDoc had a problem:"
- $stderr.puts e.message
- $stderr.puts
- $stderr.puts "run with --debug for full backtrace"
+str = ARGV.first
+if str
+ str = str.b[/\A_(.*)_\z/, 1]
+ if str and Gem::Version.correct?(str)
+ version = str
+ ARGV.shift
end
-
- exit 1
end
+if Gem.respond_to?(:activate_bin_path)
+load Gem.activate_bin_path('rdoc', 'rdoc', version)
+else
+gem "rdoc", version
+load Gem.bin_path("rdoc", "rdoc", version)
+end
diff --git a/bin/ri b/bin/ri
index 7fbed0c099..0cc2f73bb6 100755
--- a/bin/ri
+++ b/bin/ri
@@ -1,12 +1,27 @@
#!/usr/bin/env ruby
+#
+# This file was generated by RubyGems.
+#
+# The application 'rdoc' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
-begin
- gem 'rdoc'
-rescue NameError => e # --disable-gems
- raise unless e.name == :gem
-rescue Gem::LoadError
-end
+require 'rubygems'
+
+version = ">= 0.a"
-require 'rdoc/ri/driver'
+str = ARGV.first
+if str
+ str = str.b[/\A_(.*)_\z/, 1]
+ if str and Gem::Version.correct?(str)
+ version = str
+ ARGV.shift
+ end
+end
-RDoc::RI::Driver.run ARGV
+if Gem.respond_to?(:activate_bin_path)
+load Gem.activate_bin_path('rdoc', 'ri', version)
+else
+gem "rdoc", version
+load Gem.bin_path("rdoc", "ri", version)
+end
diff --git a/bootstraptest/runner.rb b/bootstraptest/runner.rb
index 754c3f0f3b..58619fae7e 100755
--- a/bootstraptest/runner.rb
+++ b/bootstraptest/runner.rb
@@ -6,6 +6,7 @@
# Never use optparse in this file.
# Never use test/unit in this file.
# Never use Ruby extensions in this file.
+# Maintain Ruby 1.8 compatibility for now
begin
require 'fileutils'
@@ -254,30 +255,25 @@ def show_progress(message = '')
end
end
rescue Interrupt
- raise Interrupt
+ $stderr.puts "\##{@count} #{@location}"
+ raise
rescue Exception => err
$stderr.print 'E'
$stderr.puts if @verbose
error err.message, message
end
-# NativeClient is special. The binary is cross-compiled. But runs on the build environment.
-# So RUBY_PLATFORM in this process is not useful to detect it.
-def nacl?
- @ruby and File.basename(@ruby.split(/\s/).first)['sel_ldr']
-end
-
-def assert_check(testsrc, message = '', opt = '')
+def assert_check(testsrc, message = '', opt = '', **argh)
show_progress(message) {
- result = get_result_string(testsrc, opt)
+ result = get_result_string(testsrc, opt, **argh)
check_coredump
yield(result)
}
end
-def assert_equal(expected, testsrc, message = '')
+def assert_equal(expected, testsrc, message = '', opt = '', **argh)
newtest
- assert_check(testsrc, message) {|result|
+ assert_check(testsrc, message, opt, **argh) {|result|
if expected == result
nil
else
@@ -318,13 +314,10 @@ def assert_valid_syntax(testsrc, message = '')
}
end
-def assert_normal_exit(testsrc, *rest)
+def assert_normal_exit(testsrc, *rest, timeout: nil, **opt)
newtest
- opt = {}
- opt = rest.pop if Hash === rest.last
message, ignore_signals = rest
message ||= ''
- timeout = opt[:timeout]
show_progress(message) {
faildesc = nil
filename = make_srcfile(testsrc)
@@ -374,6 +367,7 @@ def assert_normal_exit(testsrc, *rest)
end
def assert_finish(timeout_seconds, testsrc, message = '')
+ timeout_seconds *= 3 if RubyVM::MJIT.enabled? # for --jit-wait
newtest
show_progress(message) {
faildesc = nil
@@ -382,12 +376,24 @@ def assert_finish(timeout_seconds, testsrc, message = '')
pid = io.pid
waited = false
tlimit = Time.now + timeout_seconds
- while Time.now < tlimit
+ diff = timeout_seconds
+ while diff > 0
if Process.waitpid pid, Process::WNOHANG
waited = true
break
end
- sleep 0.1
+ if io.respond_to?(:read_nonblock)
+ if IO.select([io], nil, nil, diff)
+ begin
+ io.read_nonblock(1024)
+ rescue Errno::EAGAIN, IO::WaitReadable, EOFError
+ break
+ end while true
+ end
+ else
+ sleep 0.1
+ end
+ diff = tlimit - Time.now
end
if !waited
Process.kill(:KILL, pid)
@@ -419,18 +425,19 @@ def untabify(str)
str.gsub(/^\t+/) {' ' * (8 * $&.size) }
end
-def make_srcfile(src)
+def make_srcfile(src, frozen_string_literal: nil)
filename = 'bootstraptest.tmp.rb'
File.open(filename, 'w') {|f|
+ f.puts "#frozen_string_literal:true" if frozen_string_literal
f.puts "GC.stress = true" if $stress
f.puts "print(begin; #{src}; end)"
}
filename
end
-def get_result_string(src, opt = '')
+def get_result_string(src, opt = '', **argh)
if @ruby
- filename = make_srcfile(src)
+ filename = make_srcfile(src, **argh)
begin
`#{@ruby} -W0 #{opt} #{filename}`
ensure
diff --git a/bootstraptest/test_env.rb b/bootstraptest/test_env.rb
new file mode 100644
index 0000000000..7d1b45b75e
--- /dev/null
+++ b/bootstraptest/test_env.rb
@@ -0,0 +1,12 @@
+assert_equal "true", %q{
+ ENV["ENVTEST"] = "\u{e9 3042 d76c}"
+ env = ENV["ENVTEST"]
+ env.valid_encoding?
+}
+
+# different encoding is used for PATH
+assert_equal "true", %q{
+ ENV["PATH"] = "\u{e9 3042 d76c}"
+ env = ENV["PATH"]
+ env.valid_encoding?
+}
diff --git a/bootstraptest/test_exception.rb b/bootstraptest/test_exception.rb
index 35c8d25e37..0fb6f552b8 100644
--- a/bootstraptest/test_exception.rb
+++ b/bootstraptest/test_exception.rb
@@ -402,7 +402,7 @@ assert_equal 'nil', %q{
exc.inspect
}, '[ruby-dev:32608]'
-assert_equal 'exception class/object expected', %q{
+assert_equal 'divided by 0', %q{
class ZeroDivisionError
def self.new(message)
42
diff --git a/bootstraptest/test_flow.rb b/bootstraptest/test_flow.rb
index 0390062a24..9da6d45cbd 100644
--- a/bootstraptest/test_flow.rb
+++ b/bootstraptest/test_flow.rb
@@ -589,3 +589,13 @@ assert_equal "foo", %q{
end
Bug6460.new.m1
}, '[ruby-dev:46372]'
+
+assert_equal "foo", %q{
+ obj = "foo"
+ if obj || any1
+ any2 = any2
+ else
+ raise obj.inspect
+ end
+ obj
+}, '[ruby-core:87830]'
diff --git a/bootstraptest/test_fork.rb b/bootstraptest/test_fork.rb
index 1cd9f7ac6c..83923dad97 100644
--- a/bootstraptest/test_fork.rb
+++ b/bootstraptest/test_fork.rb
@@ -21,7 +21,9 @@ assert_finish 10, %q{
end
}, '[ruby-core:22158]'
-assert_normal_exit(<<'End', '[ruby-dev:37934]')
+# temporarily stop this test to enable explicit failure when
+# timer thread couldn't be created (r61706 and r61717).
+assert_normal_exit(<<'End', '[ruby-dev:37934]') if false
main = Thread.current
Thread.new { sleep 0.01 until main.stop?; Thread.kill main }
Process.setrlimit(:NPROC, 1) if defined?(Process::RLIMIT_NPROC)
diff --git a/bootstraptest/test_insns.rb b/bootstraptest/test_insns.rb
new file mode 100644
index 0000000000..88f36bd0c2
--- /dev/null
+++ b/bootstraptest/test_insns.rb
@@ -0,0 +1,420 @@
+# C0 coverage of each instructions
+
+# :NOTE: This is for development purpose; never consider this file as
+# ISeq compilation specification.
+
+begin
+ # This library brings some additional coverage.
+ # Not mandatory.
+ require 'rbconfig/sizeof'
+rescue LoadError
+ # OK, just skip
+else
+ $FIXNUM_MAX = RbConfig::LIMITS["FIXNUM_MAX"]
+ $FIXNUM_MIN = RbConfig::LIMITS["FIXNUM_MIN"]
+end
+
+fsl = { frozen_string_literal: true } # used later
+tests = [
+ # insn , expression to generate such insn
+ [ 'nop', %q{ raise rescue true }, ],
+
+ [ 'setlocal *, 0', %q{ x = true }, ],
+ [ 'setlocal *, 1', %q{ x = nil; -> { x = true }.call }, ],
+ [ 'setlocal', %q{ x = nil; -> { -> { x = true }.() }.() }, ],
+ [ 'getlocal *, 0', %q{ x = true; x }, ],
+ [ 'getlocal *, 1', %q{ x = true; -> { x }.call }, ],
+ [ 'getlocal', %q{ x = true; -> { -> { x }.() }.() }, ],
+
+ [ 'setblockparam', <<~'},', ], # {
+ def m&b
+ b = # here
+ proc { true }
+ end
+ m { false }.call
+ },
+ [ 'getblockparam', <<~'},', ], # {
+ def m&b
+ b # here
+ end
+ m { true }.call
+ },
+ [ 'getblockparamproxy', <<~'},', ], # {
+ def m&b
+ b # here
+ .call
+ end
+ m { true }
+ },
+
+ [ 'setspecial', %q{ true if true..true }, ],
+ [ 'getspecial', %q{ $&.nil? }, ],
+ [ 'getspecial', %q{ $`.nil? }, ],
+ [ 'getspecial', %q{ $'.nil? }, ],
+ [ 'getspecial', %q{ $+.nil? }, ],
+ [ 'getspecial', %q{ $1.nil? }, ],
+ [ 'getspecial', %q{ $128.nil? }, ],
+
+ [ 'getglobal', %q{ String === $0 }, ],
+ [ 'getglobal', %q{ $_.nil? }, ],
+ [ 'setglobal', %q{ $0 = "true" }, ],
+
+ [ 'setinstancevariable', %q{ @x = true }, ],
+ [ 'getinstancevariable', %q{ @x = true; @x }, ],
+
+ [ 'setclassvariable', %q{ @@x = true }, ],
+ [ 'getclassvariable', %q{ @@x = true; @@x }, ],
+
+ [ 'setconstant', %q{ X = true }, ],
+ [ 'setconstant', %q{ Object::X = true }, ],
+ [ 'getconstant', %q{ X = true; X }, ],
+ [ 'getconstant', %q{ X = true; Object::X }, ],
+
+ [ 'getinlinecache / setinlinecache', %q{ def x; X; end; X = true; x; x; x }, ],
+
+ [ 'putnil', %q{ $~ == nil }, ],
+ [ 'putself', %q{ $~ != self }, ],
+ [ 'putobject INT2FIX(0)', %q{ $~ != 0 }, ],
+ [ 'putobject INT2FIX(1)', %q{ $~ != 1 }, ],
+ [ 'putobject', %q{ $~ != -1 }, ],
+ [ 'putobject', %q{ $~ != /x/ }, ],
+ [ 'putobject', %q{ $~ != :x }, ],
+ [ 'putobject', %q{ $~ != (1..2) }, ],
+ [ 'putobject', %q{ $~ != true }, ],
+ [ 'putobject', %q{ /(?<x>x)/ =~ "x"; x == "x" }, ],
+
+ [ 'putspecialobject', %q{ {//=>true}[//] }, ],
+ [ 'putiseq', %q{ -> { true }.() }, ],
+ [ 'putstring', %q{ "true" }, ],
+ [ 'tostring / concatstrings', %q{ "#{true}" }, ],
+ [ 'freezestring', %q{ "#{true}" }, fsl, ],
+ [ 'freezestring', %q{ "#{true}" }, '-d', fsl, ],
+ [ 'toregexp', %q{ /#{true}/ =~ "true" && $~ }, ],
+ [ 'intern', %q{ :"#{true}" }, ],
+
+ [ 'newarray', %q{ ["true"][0] }, ],
+ [ 'duparray', %q{ [ true ][0] }, ],
+ [ 'expandarray', %q{ y = [ true, false, nil ]; x, = y; x }, ],
+ [ 'expandarray', %q{ y = [ true, false, nil ]; x, *z = y; x }, ],
+ [ 'expandarray', %q{ y = [ true, false, nil ]; x, *z, w = y; x }, ],
+ [ 'splatarray', %q{ x, = *(y = true), false; x }, ],
+ [ 'concatarray', %q{ ["t", "r", *x = "u", "e"].join }, ],
+ [ 'concatarray', <<~'},', ], # {
+ class X; def to_a; ['u']; end; end
+ ['t', 'r', *X.new, 'e'].join
+ },
+ [ 'concatarray', <<~'},', ], # {
+ r = false
+ t = [true, nil]
+ q, w, e = r, *t # here
+ w
+ },
+
+ [ 'newhash', %q{ x = {}; x[x] = true }, ],
+ [ 'newhash', %q{ x = true; { x => x }[x] }, ],
+ [ 'newhashfromarray', %q{ { a: true }[:a] }, ],
+ [ 'newrange', %q{ x = 1; [*(0..x)][0] == 0 }, ],
+ [ 'newrange', %q{ x = 1; [*(0...x)][0] == 0 }, ],
+
+ [ 'pop', %q{ def x; true; end; x }, ],
+ [ 'dup', %q{ x = y = true; x }, ],
+ [ 'dupn', %q{ Object::X ||= true }, ],
+ [ 'reverse', %q{ q, (w, e), r = 1, [2, 3], 4; e == 3 }, ],
+ [ 'swap', <<~'},', ], # {
+ x = [[false, true]]
+ for i, j in x # here
+ ;
+ end
+ j
+ },
+
+ [ 'topn', %q{ x, y = [], 0; x[*y], = [true, false]; x[0] }, ],
+ [ 'setn', %q{ x, y = [], 0; x[*y] = true ; x[0] }, ],
+ [ 'adjuststack', %q{ x = [true]; x[0] ||= nil; x[0] }, ],
+
+ [ 'defined', %q{ !defined?(x) }, ],
+ [ 'checkkeyword', %q{ def x x:rand;x end; x x: true }, ],
+ [ 'checktype', %q{ x = true; "#{x}" }, ],
+ [ 'checkmatch', <<~'},', ], # {
+ x = y = true
+ case x
+ when false
+ y = false
+ when true # here
+ y = nil
+ end
+ y == nil
+ },
+ [ 'checkmatch', <<~'},', ], # {
+ x, y = true, [false]
+ case x
+ when *y # here
+ z = false
+ else
+ z = true
+ end
+ z
+ },
+ [ 'checkmatch', <<~'},', ], # {
+ x = false
+ begin
+ raise
+ rescue # here
+ x = true
+ end
+ x
+ },
+
+ [ 'defineclass', %q{ module X; true end }, ],
+ [ 'defineclass', %q{ X = Module.new; module X; true end }, ],
+ [ 'defineclass', %q{ class X; true end }, ],
+ [ 'defineclass', %q{ X = Class.new; class X; true end }, ],
+ [ 'defineclass', %q{ X = Class.new; class Y < X; true end }, ],
+ [ 'defineclass', %q{ X = Class.new; class << X; true end }, ],
+ [ 'defineclass', <<~'},', ], # {
+ X = Class.new
+ Y = Class.new(X)
+ class Y < X
+ true
+ end
+ },
+
+ [ 'opt_send_without_block', %q{ true.to_s }, ],
+ [ 'send', %q{ true.tap {|i| i.to_s } }, ],
+ [ 'leave', %q{ def x; true; end; x }, ],
+ [ 'invokesuper', <<~'},', ], # {
+ class X < String
+ def empty?
+ super # here
+ end
+ end
+ X.new.empty?
+ },
+ [ 'invokeblock', <<~'},', ], # {
+ def x
+ return yield self # here
+ end
+ x do
+ true
+ end
+ },
+
+ [ 'opt_str_freeze', %q{ 'true'.freeze }, ],
+ [ 'opt_str_uminus', %q{ -'true' }, ],
+ [ 'opt_str_freeze', <<~'},', ], # {
+ class String
+ def freeze
+ true
+ end
+ end
+ 'true'.freeze
+ },
+
+ [ 'opt_newarray_max', %q{ [ ].max.nil? }, ],
+ [ 'opt_newarray_max', %q{ [1, x = 2, 3].max == 3 }, ],
+ [ 'opt_newarray_max', <<~'},', ], # {
+ class Array
+ def max
+ true
+ end
+ end
+ [1, x = 2, 3].max
+ },
+ [ 'opt_newarray_min', %q{ [ ].min.nil? }, ],
+ [ 'opt_newarray_min', %q{ [3, x = 2, 1].min == 1 }, ],
+ [ 'opt_newarray_min', <<~'},', ], # {
+ class Array
+ def min
+ true
+ end
+ end
+ [3, x = 2, 1].min
+ },
+
+ [ 'throw', %q{ false.tap { break true } }, ],
+ [ 'branchif', %q{ x = nil; x ||= true }, ],
+ [ 'branchif', %q{ x = true; x ||= nil; x }, ],
+ [ 'branchunless', %q{ x = 1; x &&= true }, ],
+ [ 'branchunless', %q{ x = nil; x &&= true; x.nil? }, ],
+ [ 'branchnil', %q{ x = true; x&.to_s }, ],
+ [ 'branchnil', %q{ x = nil; (x&.to_s).nil? }, ],
+ [ 'jump', <<~'},', ], # {
+ y = 1
+ x = if y == 0 then nil elsif y == 1 then true else nil end
+ x
+ },
+ [ 'jump', <<~'},', ], # {
+ # ultra complicated situation: this ||= assignment only generates
+ # 15 instructions, not including the class definition.
+ class X; attr_accessor :x; end
+ x = X.new
+ x&.x ||= true # here
+ },
+
+ [ 'once', %q{ /#{true}/o =~ "true" && $~ }, ],
+ [ 'once', <<~'},', ], # {
+ def once expr
+ return /#{expr}/o # here
+ end
+ x = once(true); x = once(false); x = once(nil);
+ x =~ "true" && $~
+ },
+ [ 'once', <<~'},', ], # {
+ # recursive once
+ def once n
+ return %r/#{
+ if n == 0
+ true
+ else
+ once(n-1) # here
+ end
+ }/ox
+ end
+ x = once(128); x = once(7); x = once(16);
+ x =~ "true" && $~
+ },
+ [ 'once', <<~'},', ], # {
+ # inter-thread lockup situation
+ def once n
+ return Thread.start n do |m|
+ Thread.pass
+ next %r/#{
+ sleep m # here
+ true
+ }/ox
+ end
+ end
+ x = once(1); y = once(0.1); z = y.value
+ z =~ "true" && $~
+ },
+
+ [ 'opt_case_dispatch', %q{ case 0 when 1.1 then false else true end }, ],
+ [ 'opt_case_dispatch', %q{ case 1.0 when 1.1 then false else true end }, ],
+
+ [ 'opt_plus', %q{ 1 + 1 == 2 }, ],
+ if defined? $FIXNUM_MAX then
+ [ 'opt_plus', %Q{ #{ $FIXNUM_MAX } + 1 == #{ $FIXNUM_MAX + 1 } }, ]
+ end,
+ [ 'opt_plus', %q{ 1.0 + 1.0 == 2.0 }, ],
+ [ 'opt_plus', %q{ x = +0.0.next_float; x + x >= x }, ],
+ [ 'opt_plus', %q{ 't' + 'rue' }, ],
+ [ 'opt_plus', %q{ ( ['t'] + ['r', ['u', ['e'], ], ] ).join }, ],
+ [ 'opt_plus', %q{ Time.at(1) + 1 == Time.at(2) }, ],
+ [ 'opt_minus', %q{ 1 - 1 == 0 }, ],
+ if defined? $FIXNUM_MIN then
+ [ 'opt_minus', %Q{ #{ $FIXNUM_MIN } - 1 == #{ $FIXNUM_MIN - 1 } }, ]
+ end,
+ [ 'opt_minus', %q{ 1.0 - 1.0 == 0.0 }, ],
+ [ 'opt_minus', %q{ x = -0.0.prev_float; x - x == 0.0 }, ],
+ [ 'opt_minus', %q{ ( [false, true] - [false] )[0] }, ],
+ [ 'opt_mult', %q{ 1 * 1 == 1 }, ],
+ [ 'opt_mult', %q{ 1.0 * 1.0 == 1.0 }, ],
+ [ 'opt_mult', %q{ x = +0.0.next_float; x * x <= x }, ],
+ [ 'opt_mult', %q{ ( "ruet" * 3 )[7,4] }, ],
+ [ 'opt_div', %q{ 1 / 1 == 1 }, ],
+ [ 'opt_div', %q{ 1.0 / 1.0 == 1.0 }, ],
+ [ 'opt_div', %q{ x = +0.0.next_float; x / x >= x }, ],
+ [ 'opt_div', %q{ x = 1/2r; x / x == 1 }, ],
+ [ 'opt_mod', %q{ 1 % 1 == 0 }, ],
+ [ 'opt_mod', %q{ 1.0 % 1.0 == 0.0 }, ],
+ [ 'opt_mod', %q{ x = +0.0.next_float; x % x == 0.0 }, ],
+ [ 'opt_mod', %q{ '%s' % [ true ] }, ],
+
+ [ 'opt_eq', %q{ 1 == 1 }, ],
+ [ 'opt_eq', <<~'},', ], # {
+ class X; def == other; true; end; end
+ X.new == true
+ },
+ [ 'opt_neq', %q{ 1 != 0 }, ],
+ [ 'opt_neq', <<~'},', ], # {
+ class X; def != other; true; end; end
+ X.new != true
+ },
+
+ [ 'opt_lt', %q{ -1 < 0 }, ],
+ [ 'opt_lt', %q{ -1.0 < 0.0 }, ],
+ [ 'opt_lt', %q{ -0.0.prev_float < 0.0 }, ],
+ [ 'opt_lt', %q{ ?a < ?z }, ],
+ [ 'opt_le', %q{ -1 <= 0 }, ],
+ [ 'opt_le', %q{ -1.0 <= 0.0 }, ],
+ [ 'opt_le', %q{ -0.0.prev_float <= 0.0 }, ],
+ [ 'opt_le', %q{ ?a <= ?z }, ],
+ [ 'opt_gt', %q{ 1 > 0 }, ],
+ [ 'opt_gt', %q{ 1.0 > 0.0 }, ],
+ [ 'opt_gt', %q{ +0.0.next_float > 0.0 }, ],
+ [ 'opt_gt', %q{ ?z > ?a }, ],
+ [ 'opt_ge', %q{ 1 >= 0 }, ],
+ [ 'opt_ge', %q{ 1.0 >= 0.0 }, ],
+ [ 'opt_ge', %q{ +0.0.next_float >= 0.0 }, ],
+ [ 'opt_ge', %q{ ?z >= ?a }, ],
+
+ [ 'opt_ltlt', %q{ '' << 'true' }, ],
+ [ 'opt_ltlt', %q{ ([] << 'true').join }, ],
+ [ 'opt_ltlt', %q{ (1 << 31) == 2147483648 }, ],
+
+ [ 'opt_aref', %q{ ['true'][0] }, ],
+ [ 'opt_aref', %q{ { 0 => 'true'}[0] }, ],
+ [ 'opt_aref', %q{ 'true'[0] == ?t }, ],
+ [ 'opt_aset', %q{ [][0] = true }, ],
+ [ 'opt_aset', %q{ {}[0] = true }, ],
+ [ 'opt_aset', %q{ x = 'frue'; x[0] = 't'; x }, ],
+ [ 'opt_aset', <<~'},', ], # {
+ # opt_aref / opt_aset mixup situation
+ class X; def x; {}; end; end
+ x = X.new
+ x&.x[true] ||= true # here
+ },
+
+ [ 'opt_aref_with', %q{ { 'true' => true }['true'] }, ],
+ [ 'opt_aref_with', %q{ Struct.new(:nil).new['nil'].nil? }, ],
+ [ 'opt_aset_with', %q{ {}['true'] = true }, ],
+ [ 'opt_aset_with', %q{ Struct.new(:true).new['true'] = true }, ],
+
+ [ 'opt_length', %q{ 'true' .length == 4 }, ],
+ [ 'opt_length', %q{ :true .length == 4 }, ],
+ [ 'opt_length', %q{ [ 'true' ] .length == 1 }, ],
+ [ 'opt_length', %q{ { 'true' => 1 }.length == 1 }, ],
+ [ 'opt_size', %q{ 'true' .size == 4 }, ],
+ [ 'opt_size', %q{ 1.size >= 4 }, ],
+ [ 'opt_size', %q{ [ 'true' ] .size == 1 }, ],
+ [ 'opt_size', %q{ { 'true' => 1 }.size == 1 }, ],
+ [ 'opt_empty_p', %q{ ''.empty? }, ],
+ [ 'opt_empty_p', %q{ [].empty? }, ],
+ [ 'opt_empty_p', %q{ {}.empty? }, ],
+ [ 'opt_empty_p', %q{ Queue.new.empty? }, ],
+
+ [ 'opt_succ', %q{ 1.succ == 2 }, ],
+ if defined? $FIXNUM_MAX then
+ [ 'opt_succ',%Q{ #{ $FIXNUM_MAX }.succ == #{ $FIXNUM_MAX + 1 } }, ]
+ end,
+ [ 'opt_succ', %q{ '1'.succ == '2' }, ],
+ [ 'opt_succ', %q{ x = Time.at(0); x.succ == Time.at(1) }, ],
+
+ [ 'opt_not', %q{ ! false }, ],
+ [ 'opt_neq', <<~'},', ], # {
+ class X; def !; true; end; end
+ ! X.new
+ },
+
+ [ 'opt_regexpmatch1', %q{ /true/ =~ 'true' && $~ }, ],
+ [ 'opt_regexpmatch1', <<~'},', ], # {
+ class Regexp; def =~ other; true; end; end
+ /true/ =~ 'true'
+ },
+ [ 'opt_regexpmatch2', %q{ 'true' =~ /true/ && $~ }, ],
+ [ 'opt_regexpmatch2', <<~'},', ], # {
+ class String; def =~ other; true; end; end
+ 'true' =~ /true/
+ },
+
+ [ 'opt_call_c_function', 'Struct.new(:x).new.x = true', ],
+]
+
+# normal path
+tests.compact.each {|(insn, expr, *a)| assert_equal 'true', expr, insn, *a }
+
+# with trace
+tests.compact.each {|(insn, expr, *a)|
+ progn = "set_trace_func(proc{})\n" + expr
+ assert_equal 'true', progn, insn, *a
+}
diff --git a/bootstraptest/test_io.rb b/bootstraptest/test_io.rb
index 1d2b19368a..89c00d0b88 100644
--- a/bootstraptest/test_io.rb
+++ b/bootstraptest/test_io.rb
@@ -52,7 +52,7 @@ assert_equal 'ok', %q{
STDIN.reopen(rw)
STDIN.reopen(save)
rw.close
- File.unlink(tmpname) unless RUBY_PLATFORM['nacl']
+ File.unlink(tmpname)
:ok
}
@@ -69,7 +69,7 @@ assert_equal 'ok', %q{
STDIN.print "a"
STDIN.reopen(save)
rw.close
- File.unlink(tmpname) unless RUBY_PLATFORM['nacl']
+ File.unlink(tmpname)
:ok
}
diff --git a/bootstraptest/test_literal.rb b/bootstraptest/test_literal.rb
index e79092e411..9b3c10d519 100644
--- a/bootstraptest/test_literal.rb
+++ b/bootstraptest/test_literal.rb
@@ -65,10 +65,8 @@ assert_equal ':a3c', ':"a#{1+2}c".inspect'
assert_equal 'Symbol', ':"a#{1+2}c".class'
# xstring
-unless nacl?
- assert_equal "foo\n", %q(`echo foo`)
- assert_equal "foo\n", %q(s = "foo"; `echo #{s}`)
-end
+assert_equal "foo\n", %q(`echo foo`)
+assert_equal "foo\n", %q(s = "foo"; `echo #{s}`)
# regexp
assert_equal '', '//.source'
@@ -225,6 +223,24 @@ assert_equal 'ok', %q{ # long hash literal (optimized)
:ok
}
+assert_equal 'ok', %q{ # Bug #15536
+ eval <<-END
+ {
+ **{
+ a0: nil, a1: nil, a2: nil, a3: nil, a4: nil, a5: nil, a6: nil, a7: nil, a8: nil,
+ },
+ a0: nil, a1: nil, a2: nil, a3: nil, a4: nil, a5: nil, a6: nil, a7: nil, a8: nil,
+ **{
+ c: nil
+ },
+ b0: nil, b1: nil, b2: nil, b3: nil, b4: nil, b5: nil, b6: nil, b7: nil, b8: nil,
+ b9: nil, b10: nil, b11: nil, b12: nil, b13: nil, b14: nil, b15: nil, b16: nil,
+ b17: nil, b18: nil, b19: nil, b20: nil, b21: nil,
+ }
+ END
+ :ok
+}
+
assert_equal 'ok', %q{
[print(:ok), exit] # void literal with side-effect
:dummy
diff --git a/bootstraptest/test_proc.rb b/bootstraptest/test_proc.rb
index c23394e8d2..1e384411dc 100644
--- a/bootstraptest/test_proc.rb
+++ b/bootstraptest/test_proc.rb
@@ -224,14 +224,14 @@ assert_equal %q{[[nil, []], [1, []], [1, [2]], [1, [2, 3]]]}, %q{
Proc.new{|a, *b| [a, b]}.call(1, 2, 3),
]
}
-assert_equal %q{0}, %q{
+assert_equal %q{1}, %q{
pr = proc{
$SAFE
}
$SAFE = 1
pr.call
}
-assert_equal %q{[1, 0]}, %q{
+assert_equal %q{[1, 1]}, %q{
pr = proc{
$SAFE += 1
}
diff --git a/ccan/list/list.h b/ccan/list/list.h
index ca9f9f1f7f..59ab45ee53 100644
--- a/ccan/list/list.h
+++ b/ccan/list/list.h
@@ -57,7 +57,7 @@ struct list_head
* Example:
* static struct list_head my_list = LIST_HEAD_INIT(my_list);
*/
-#define LIST_HEAD_INIT(name) { { &name.n, &name.n } }
+#define LIST_HEAD_INIT(name) { { &(name).n, &(name).n } }
/**
* LIST_HEAD - define and initialize an empty list_head
@@ -238,6 +238,21 @@ static inline int list_empty_nodebug(const struct list_head *h)
#endif
/**
+ * list_empty_nocheck - is a list empty?
+ * @h: the list_head
+ *
+ * If the list is empty, returns true. This doesn't perform any
+ * debug check for list consistency, so it can be called without
+ * locks, racing with the list being modified. This is ok for
+ * checks where an incorrect result is not an issue (optimized
+ * bail out path for example).
+ */
+static inline bool list_empty_nocheck(const struct list_head *h)
+{
+ return h->n.next == &h->n;
+}
+
+/**
* list_del - delete an entry from an (unknown) linked list.
* @n: the list_node to delete from the list.
*
@@ -647,12 +662,12 @@ static inline void list_prepend_list_(struct list_head *to,
* so you can break and continue as normal.
*
* WARNING! Being the low-level macro that it is, this wrapper doesn't know
- * nor care about the type of @i. The only assumtion made is that @i points
+ * nor care about the type of @i. The only assumption made is that @i points
* to a chunk of memory that at some @offset, relative to @i, contains a
- * properly filled `struct node_list' which in turn contains pointers to
- * memory chunks and it's turtles all the way down. Whith all that in mind
+ * properly filled `struct list_node' which in turn contains pointers to
+ * memory chunks and it's turtles all the way down. With all that in mind
* remember that given the wrong pointer/offset couple this macro will
- * happilly churn all you memory untill SEGFAULT stops it, in other words
+ * happily churn all you memory until SEGFAULT stops it, in other words
* caveat emptor.
*
* It is worth mentioning that one of legitimate use-cases for that wrapper
diff --git a/class.c b/class.c
index 350e2cc31b..3936c6a540 100644
--- a/class.c
+++ b/class.c
@@ -32,6 +32,9 @@
#define id_attached id__attached__
+#define METACLASS_OF(k) RBASIC(k)->klass
+#define SET_METACLASS_OF(k, cls) RBASIC_SET_CLASS(k, cls)
+
void
rb_class_subclass_add(VALUE super, VALUE klass)
{
@@ -367,22 +370,35 @@ rb_singleton_class_clone(VALUE obj)
return rb_singleton_class_clone_and_attach(obj, Qundef);
}
+// Clone and return the singleton class of `obj` if it has been created and is attached to `obj`.
VALUE
rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach)
{
const VALUE klass = RBASIC(obj)->klass;
- if (!FL_TEST(klass, FL_SINGLETON))
- return klass;
+ // Note that `rb_singleton_class()` can create situations where `klass` is
+ // attached to an object other than `obj`. In which case `obj` does not have
+ // a material singleton class attached yet and there is no singleton class
+ // to clone.
+ if (!(FL_TEST(klass, FL_SINGLETON) && rb_attr_get(klass, id_attached) == obj)) {
+ // nothing to clone
+ return klass;
+ }
else {
/* copy singleton(unnamed) class */
+ bool klass_of_clone_is_new;
VALUE clone = class_alloc(RBASIC(klass)->flags, 0);
if (BUILTIN_TYPE(obj) == T_CLASS) {
+ klass_of_clone_is_new = true;
RBASIC_SET_CLASS(clone, clone);
}
else {
- RBASIC_SET_CLASS(clone, rb_singleton_class_clone(klass));
+ 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));
@@ -406,7 +422,9 @@ rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach)
arg.new_klass = clone;
rb_id_table_foreach(RCLASS_M_TBL(klass), clone_method_i, &arg);
}
- rb_singleton_class_attached(RBASIC(clone)->klass, clone);
+ if (klass_of_clone_is_new) {
+ rb_singleton_class_attached(RBASIC(clone)->klass, clone);
+ }
FL_SET(clone, FL_SINGLETON);
return clone;
@@ -428,11 +446,6 @@ rb_singleton_class_attached(VALUE klass, VALUE obj)
}
}
-
-
-#define METACLASS_OF(k) RBASIC(k)->klass
-#define SET_METACLASS_OF(k, cls) RBASIC_SET_CLASS(k, cls)
-
/*!
* whether k is a meta^(n)-class of Class class
* @retval 1 if \a k is a meta^(n)-class of Class class (n >= 0)
@@ -616,7 +629,7 @@ rb_define_class_id(ID id, VALUE super)
* \return the value \c Class#inherited's returns
* \pre Each of \a super and \a klass must be a \c Class object.
*/
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_class_inherited(VALUE super, VALUE klass)
{
ID inherited;
@@ -849,14 +862,23 @@ rb_include_class_new(VALUE module, VALUE super)
static int include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super);
+static void
+ensure_includable(VALUE klass, VALUE module)
+{
+ rb_class_modify_check(klass);
+ Check_Type(module, T_MODULE);
+ if (!NIL_P(rb_refinement_module_get_refined_class(module))) {
+ rb_raise(rb_eArgError, "refinement module is not allowed");
+ }
+ OBJ_INFECT(klass, module);
+}
+
void
rb_include_module(VALUE klass, VALUE module)
{
int changed = 0;
- rb_frozen_class_p(klass);
- Check_Type(module, T_MODULE);
- OBJ_INFECT(klass, module);
+ ensure_includable(klass, module);
changed = include_modules_at(klass, RCLASS_ORIGIN(klass), module, TRUE);
if (changed < 0)
@@ -966,9 +988,7 @@ rb_prepend_module(VALUE klass, VALUE module)
VALUE origin;
int changed = 0;
- rb_frozen_class_p(klass);
- Check_Type(module, T_MODULE);
- OBJ_INFECT(klass, module);
+ ensure_includable(klass, module);
origin = RCLASS_ORIGIN(klass);
if (origin == klass) {
@@ -1171,17 +1191,10 @@ static VALUE
class_instance_method_list(int argc, const VALUE *argv, VALUE mod, int obj, int (*func) (st_data_t, st_data_t, st_data_t))
{
VALUE ary;
- int recur, prepended = 0;
+ int recur = TRUE, prepended = 0;
struct method_entry_arg me_arg;
- if (argc == 0) {
- recur = TRUE;
- }
- else {
- VALUE r;
- rb_scan_args(argc, argv, "01", &r);
- recur = RTEST(r);
- }
+ if (rb_check_arity(argc, 0, 1)) recur = RTEST(argv[0]);
if (!recur && RCLASS_ORIGIN(mod) != mod) {
mod = RCLASS_ORIGIN(mod);
@@ -1410,25 +1423,21 @@ rb_obj_public_methods(int argc, const VALUE *argv, VALUE obj)
VALUE
rb_obj_singleton_methods(int argc, const VALUE *argv, VALUE obj)
{
- VALUE recur, ary, klass, origin;
+ VALUE ary, klass, origin;
struct method_entry_arg me_arg;
struct rb_id_table *mtbl;
+ int recur = TRUE;
- if (argc == 0) {
- recur = Qtrue;
- }
- else {
- rb_scan_args(argc, argv, "01", &recur);
- }
+ if (rb_check_arity(argc, 0, 1)) recur = RTEST(argv[0]);
klass = CLASS_OF(obj);
origin = RCLASS_ORIGIN(klass);
me_arg.list = st_init_numtable();
- me_arg.recur = RTEST(recur);
+ 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 (RTEST(recur)) {
+ 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);
@@ -1766,32 +1775,23 @@ rb_define_attr(VALUE klass, const char *name, int read, int write)
rb_attr(klass, rb_intern(name), read, write, FALSE);
}
-int
-rb_obj_basic_to_s_p(VALUE obj)
-{
- const rb_method_entry_t *me = rb_method_entry(CLASS_OF(obj), rb_intern("to_s"));
- if (me && me->def && me->def->type == VM_METHOD_TYPE_CFUNC &&
- me->def->body.cfunc.func == rb_any_to_s)
- return 1;
- return 0;
-}
-
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_keyword_error_new(const char *error, VALUE keys)
{
- const char *msg = "";
- VALUE error_message;
-
- if (RARRAY_LEN(keys) == 1) {
- keys = RARRAY_AREF(keys, 0);
- }
- else {
- keys = rb_ary_join(keys, rb_usascii_str_new2(", "));
- msg = "s";
+ long i = 0, len = RARRAY_LEN(keys);
+ VALUE error_message = rb_sprintf("%s keyword%.*s", error, len > 1, "s");
+
+ if (len > 0) {
+ rb_str_cat_cstr(error_message, ": ");
+ while (1) {
+ const VALUE k = RARRAY_AREF(keys, i);
+ Check_Type(k, T_SYMBOL); /* wrong hash is given to rb_get_kwargs */
+ rb_str_append(error_message, rb_sym2str(k));
+ if (++i >= len) break;
+ rb_str_cat_cstr(error_message, ", ");
+ }
}
- error_message = rb_sprintf("%s keyword%s: %"PRIsVALUE, error, msg, keys);
-
return rb_exc_new_str(rb_eArgError, error_message);
}
@@ -1806,23 +1806,19 @@ NORETURN(static void unknown_keyword_error(VALUE hash, const ID *table, int keyw
static void
unknown_keyword_error(VALUE hash, const ID *table, int keywords)
{
- st_table *tbl = rb_hash_tbl_raw(hash);
- VALUE keys;
int i;
for (i = 0; i < keywords; i++) {
st_data_t key = ID2SYM(table[i]);
- st_delete(tbl, &key, NULL);
+ rb_hash_stlike_delete(hash, &key, NULL);
}
- keys = rb_funcallv(hash, rb_intern("keys"), 0, 0);
- if (!RB_TYPE_P(keys, T_ARRAY)) rb_raise(rb_eArgError, "unknown keyword");
- rb_keyword_error("unknown", keys);
+ rb_keyword_error("unknown", rb_hash_keys(hash));
}
+
static int
separate_symbol(st_data_t key, st_data_t value, st_data_t arg)
{
VALUE *kwdhash = (VALUE *)arg;
-
if (!SYMBOL_P(key)) kwdhash++;
if (!*kwdhash) *kwdhash = rb_hash_new();
rb_hash_aset(*kwdhash, (VALUE)key, (VALUE)value);
@@ -1839,8 +1835,11 @@ rb_extract_keywords(VALUE *orighash)
*orighash = 0;
return hash;
}
- st_foreach(rb_hash_tbl_raw(hash), separate_symbol, (st_data_t)&parthash);
+ rb_hash_foreach(hash, separate_symbol, (st_data_t)&parthash);
*orighash = parthash[1];
+ if (parthash[1] && RBASIC_CLASS(hash) != rb_cHash) {
+ RBASIC_SET_CLASS(parthash[1], RBASIC_CLASS(hash));
+ }
return parthash[0];
}
@@ -1854,8 +1853,8 @@ rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, V
#define extract_kwarg(keyword, val) \
(key = (st_data_t)(keyword), values ? \
- st_delete(rb_hash_tbl_raw(keyword_hash), &key, (val)) : \
- st_lookup(rb_hash_tbl_raw(keyword_hash), key, (val)))
+ rb_hash_stlike_delete(keyword_hash, &key, (val)) : \
+ rb_hash_stlike_lookup(keyword_hash, key, (val)))
if (NIL_P(keyword_hash)) keyword_hash = 0;
@@ -1896,7 +1895,7 @@ rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, V
}
}
if (!rest && keyword_hash) {
- if (RHASH_SIZE(keyword_hash) > (unsigned int)j) {
+ if (RHASH_SIZE(keyword_hash) > (unsigned int)(values ? 0 : j)) {
unknown_keyword_error(keyword_hash, table, required+optional);
}
}
@@ -1914,8 +1913,8 @@ rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)
va_list vargs;
int f_var = 0, f_hash = 0, f_block = 0;
int n_lead = 0, n_opt = 0, n_trail = 0, n_mand;
- int argi = 0;
- VALUE hash = Qnil;
+ int argi = 0, last_idx = -1;
+ VALUE hash = Qnil, last_hash = 0;
if (ISDIGIT(*p)) {
n_lead = *p - '0';
@@ -1966,7 +1965,8 @@ rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)
hash = rb_check_hash_type(last);
if (!NIL_P(hash)) {
VALUE opts = rb_extract_keywords(&hash);
- if (!hash) argc--;
+ if (!(last_hash = hash)) argc--;
+ else last_idx = argc - 1;
hash = opts ? opts : Qnil;
}
}
@@ -1974,14 +1974,14 @@ rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)
/* capture leading mandatory arguments */
for (i = n_lead; i-- > 0; ) {
var = va_arg(vargs, VALUE *);
- if (var) *var = argv[argi];
+ if (var) *var = (argi == last_idx) ? last_hash : argv[argi];
argi++;
}
/* capture optional arguments */
for (i = n_opt; i-- > 0; ) {
var = va_arg(vargs, VALUE *);
if (argi < argc - n_trail) {
- if (var) *var = argv[argi];
+ if (var) *var = (argi == last_idx) ? last_hash : argv[argi];
argi++;
}
else {
@@ -1994,7 +1994,11 @@ rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)
var = va_arg(vargs, VALUE *);
if (0 < n_var) {
- if (var) *var = rb_ary_new4(n_var, &argv[argi]);
+ if (var) {
+ int f_last = (last_idx + 1 == argc - n_trail);
+ *var = rb_ary_new4(n_var-f_last, &argv[argi]);
+ if (f_last) rb_ary_push(*var, last_hash);
+ }
argi += n_var;
}
else {
@@ -2004,7 +2008,7 @@ rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)
/* capture trailing mandatory arguments */
for (i = n_trail; i-- > 0; ) {
var = va_arg(vargs, VALUE *);
- if (var) *var = argv[argi];
+ if (var) *var = (argi == last_idx) ? last_hash : argv[argi];
argi++;
}
/* capture an option hash - phase 2: assignment */
diff --git a/common.mk b/common.mk
index bd4b99294c..705a1020bf 100644
--- a/common.mk
+++ b/common.mk
@@ -2,7 +2,7 @@ bin: $(PROGRAM) $(WPROGRAM)
lib: $(LIBRUBY)
dll: $(LIBRUBY_SO)
-.SUFFIXES: .inc .h .c .y .i .$(DTRACE_EXT)
+.SUFFIXES: .inc .h .c .y .i .$(ASMEXT) .$(DTRACE_EXT)
# V=0 quiet, V=1 verbose. other values don't work.
V = 0
@@ -11,13 +11,20 @@ Q = $(Q1:0=@)
ECHO0 = $(ECHO1:0=echo)
ECHO = @$(ECHO0)
-UNICODE_VERSION = 9.0.0
+mflags = $(MFLAGS)
+gnumake_recursive =
+enable_shared = $(ENABLE_SHARED:no=)
+
+UNICODE_VERSION = 12.1.0
+UNICODE_EMOJI_VERSION = 12.0
+UNICODE_BETA = YES
### set the following environment variable or uncomment the line if
### the Unicode data files should be updated completely on every update ('make up',...).
# ALWAYS_UPDATE_UNICODE = yes
-UNICODE_DATA_DIR = enc/unicode/data/$(UNICODE_VERSION)
+UNICODE_DATA_DIR = enc/unicode/data/$(UNICODE_VERSION)/ucd
UNICODE_SRC_DATA_DIR = $(srcdir)/$(UNICODE_DATA_DIR)
+UNICODE_SRC_EMOJI_DATA_DIR = $(srcdir)/enc/unicode/data/emoji/$(UNICODE_EMOJI_VERSION)
UNICODE_HDR_DIR = $(srcdir)/enc/unicode/$(UNICODE_VERSION)
UNICODE_DATA_HEADERS = \
$(UNICODE_HDR_DIR)/casefold.h \
@@ -35,15 +42,13 @@ GEM_HOME =
GEM_PATH =
GEM_VENDOR =
-SPEC_GIT_BASE = git://github.com/ruby
-MSPEC_GIT_URL = $(SPEC_GIT_BASE)/mspec.git
-RUBYSPEC_GIT_URL = $(SPEC_GIT_BASE)/spec.git
-
-SIMPLECOV_GIT_URL = git://github.com/colszowka/simplecov.git
-SIMPLECOV_GIT_REF = v0.10.0
-SIMPLECOV_HTML_GIT_URL = git://github.com/colszowka/simplecov-html.git
-SIMPLECOV_HTML_GIT_REF = v0.10.0
-DOCLIE_GIT_URL = git://github.com/ms-ati/docile.git
+BENCHMARK_DRIVER_GIT_URL = https://github.com/benchmark-driver/benchmark-driver
+BENCHMARK_DRIVER_GIT_REF = v0.14.11
+SIMPLECOV_GIT_URL = https://github.com/colszowka/simplecov.git
+SIMPLECOV_GIT_REF = v0.15.0
+SIMPLECOV_HTML_GIT_URL = https://github.com/colszowka/simplecov-html.git
+SIMPLECOV_HTML_GIT_REF = v0.10.2
+DOCLIE_GIT_URL = https://github.com/ms-ati/docile.git
DOCLIE_GIT_REF = v1.1.5
STATIC_RUBY = static-ruby
@@ -57,6 +62,7 @@ ENC_TRANS_D = $(TIMESTAMPDIR)/.enc-trans.time
RDOCOUT = $(EXTOUT)/rdoc
HTMLOUT = $(EXTOUT)/html
CAPIOUT = doc/capi
+INSTALL_DOC_OPTS = --rdoc-output="$(RDOCOUT)" --html-output="$(HTMLOUT)"
INITOBJS = dmyext.$(OBJEXT) dmyenc.$(OBJEXT)
NORMALMAINOBJ = main.$(OBJEXT)
@@ -66,9 +72,10 @@ EXTSOLIBS =
MINIOBJS = $(ARCHMINIOBJS) miniinit.$(OBJEXT) dmyext.$(OBJEXT) miniprelude.$(OBJEXT)
ENC_MK = enc.mk
MAKE_ENC = -f $(ENC_MK) V="$(V)" UNICODE_HDR_DIR="$(UNICODE_HDR_DIR)" \
- RUBY="$(MINIRUBY)" MINIRUBY="$(MINIRUBY)" $(MFLAGS)
+ RUBY="$(MINIRUBY)" MINIRUBY="$(MINIRUBY)" $(mflags)
COMMONOBJS = array.$(OBJEXT) \
+ ast.$(OBJEXT) \
bignum.$(OBJEXT) \
class.$(OBJEXT) \
compar.$(OBJEXT) \
@@ -76,6 +83,7 @@ COMMONOBJS = array.$(OBJEXT) \
complex.$(OBJEXT) \
cont.$(OBJEXT) \
debug.$(OBJEXT) \
+ debug_counter.$(OBJEXT) \
dir.$(OBJEXT) \
dln_find.$(OBJEXT) \
encoding.$(OBJEXT) \
@@ -92,6 +100,8 @@ COMMONOBJS = array.$(OBJEXT) \
load.$(OBJEXT) \
marshal.$(OBJEXT) \
math.$(OBJEXT) \
+ mjit.$(OBJEXT) \
+ mjit_compile.$(OBJEXT) \
node.$(OBJEXT) \
numeric.$(OBJEXT) \
object.$(OBJEXT) \
@@ -121,6 +131,7 @@ COMMONOBJS = array.$(OBJEXT) \
thread.$(OBJEXT) \
time.$(OBJEXT) \
transcode.$(OBJEXT) \
+ transient_heap.$(OBJEXT) \
util.$(OBJEXT) \
variable.$(OBJEXT) \
version.$(OBJEXT) \
@@ -128,6 +139,7 @@ COMMONOBJS = array.$(OBJEXT) \
vm_backtrace.$(OBJEXT) \
vm_dump.$(OBJEXT) \
vm_trace.$(OBJEXT) \
+ $(COROUTINE_OBJ) \
$(DTRACE_OBJ) \
$(BUILTIN_ENCOBJS) \
$(BUILTIN_TRANSOBJS) \
@@ -144,7 +156,7 @@ ALLOBJS = $(NORMALMAINOBJ) $(MINIOBJS) $(COMMONOBJS) $(INITOBJS)
GOLFOBJS = goruby.$(OBJEXT) golf_prelude.$(OBJEXT)
DEFAULT_PRELUDES = $(GEM_PRELUDE)
-PRELUDE_SCRIPTS = $(srcdir)/prelude.rb $(srcdir)/enc/prelude.rb $(DEFAULT_PRELUDES)
+PRELUDE_SCRIPTS = $(srcdir)/prelude.rb $(DEFAULT_PRELUDES)
GEM_PRELUDE = $(srcdir)/gem_prelude.rb
PRELUDES = {$(srcdir)}prelude.c {$(srcdir)}miniprelude.c
GOLFPRELUDES = {$(srcdir)}golf_prelude.c
@@ -166,9 +178,6 @@ INSTRUBY_ARGS = $(SCRIPT_ARGS) \
INSTALL_PROG_MODE = 0755
INSTALL_DATA_MODE = 0644
-PRE_LIBRUBY_UPDATE = $(MINIRUBY) -e 'ARGV[1] or File.unlink(ARGV[0]) rescue nil' -- \
- $(LIBRUBY_EXTS) $(LIBRUBY_SO_UPDATE)
-
TESTSDIR = $(srcdir)/test
TEST_EXCLUDES = --excludes-dir=$(TESTSDIR)/excludes --name=!/memory_leak/
EXCLUDE_TESTFRAMEWORK = --exclude=/testunit/ --exclude=/minitest/
@@ -181,15 +190,55 @@ COMPILE_PRELUDE = $(srcdir)/tool/generic_erb.rb $(srcdir)/template/prelude.c.tmp
SHOWFLAGS = showflags
+MAKE_LINK = $(MINIRUBY) -rfileutils -e "include FileUtils::Verbose" \
+ -e "src, dest = ARGV" \
+ -e "exit if File.identical?(src, dest) or cmp(src, dest) rescue nil" \
+ -e "def noraise; yield; rescue; rescue NotImplementedError; end" \
+ -e "noraise {ln_sf('../'*dest.count('/')+src, dest)} or" \
+ -e "noraise {ln(src, dest)} or" \
+ -e "cp(src, dest)"
+
+
all: $(SHOWFLAGS) main docs
main: $(SHOWFLAGS) exts $(ENCSTATIC:static=lib)encs
@$(NULLCMD)
+mjit-headers: $(MJIT_SUPPORT)-mjit-headers
+no-mjit-headers: PHONY
+yes-mjit-headers: mjit_config.h PHONY
+
+mjit.$(OBJEXT): mjit_config.h
+mjit_config.h: Makefile
+
+
+# These rules using MJIT_HEADER_SUFFIX must be in common.mk, not
+# Makefile.in, in order to override the macro in defs/universal.mk.
+
+# Other `-Dxxx`s preceding `-DMJIT_HEADER` will be removed in transform_mjit_header.rb.
+# So `-DMJIT_HEADER` should be passed first when rb_mjit_header.h is generated.
+$(TIMESTAMPDIR)/$(MJIT_HEADER:.h=)$(MJIT_HEADER_SUFFIX).time: probes.h vm.$(OBJEXT) \
+ $(TIMESTAMPDIR)/$(arch)/.time
+ $(ECHO) building $(@F:.time=.h)
+ $(Q) $(CPP) -DMJIT_HEADER $(MJIT_HEADER_FLAGS) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(srcdir)/vm.c $(CPPOUTFLAG)$(@F:.time=.h).new
+ $(Q) $(IFCHANGE) "--timestamp=$@" $(@F:.time=.h) $(@F:.time=.h).new
+
+$(MJIT_HEADER:.h=)$(MJIT_HEADER_SUFFIX).h: $(TIMESTAMPDIR)/$(MJIT_HEADER:.h=)$(MJIT_HEADER_SUFFIX).time
+
+$(MJIT_MIN_HEADER:.h=)$(MJIT_HEADER_SUFFIX).h: \
+ $(TIMESTAMPDIR)/$(MJIT_HEADER:.h=)$(MJIT_HEADER_SUFFIX).time \
+ $(srcdir)/tool/transform_mjit_header.rb $(PREP) \
+ $(MJIT_HEADER:.h=)$(MJIT_HEADER_SUFFIX).h
+ $(ECHO) building $@
+ $(MINIRUBY) $(srcdir)/tool/transform_mjit_header.rb "$(CC) $(ARCH_FLAG)" $(MJIT_HEADER:.h=)$(MJIT_HEADER_ARCH).h $@
+ $(Q) $(MAKEDIRS) $(MJIT_HEADER_INSTALL_DIR)
+ $(Q) $(MAKE_LINK) $@ $(MJIT_HEADER_INSTALL_DIR)/$(@F)
+
.PHONY: showflags
exts enc trans: $(SHOWFLAGS)
showflags:
$(MESSAGE_BEGIN) \
+ " BASERUBY = $(BASERUBY)" \
" CC = $(CC)" \
" LD = $(LD)" \
" LDSHARED = $(LDSHARED)" \
@@ -198,6 +247,9 @@ showflags:
" CPPFLAGS = $(CPPFLAGS)" \
" DLDFLAGS = $(DLDFLAGS)" \
" SOLIBS = $(SOLIBS)" \
+ " LANG = $(LANG)" \
+ " LC_ALL = $(LC_ALL)" \
+ " LC_CTYPE = $(LC_CTYPE)" \
$(MESSAGE_END)
-@$(CC_VERSION)
@@ -207,20 +259,43 @@ showconfig:
$(configure_args) \
$(ECHO_END)
+EXTS_NOTE = -f $(EXTS_MK) $(mflags) RUBY="$(MINIRUBY)" top_srcdir="$(srcdir)" note
+
exts: build-ext
EXTS_MK = exts.mk
-$(EXTS_MK): $(MKFILES) all-incs $(PREP) $(RBCONFIG) $(LIBRUBY) $(TIMESTAMPDIR)/.$(arch).time
+$(EXTS_MK): ext/configure-ext.mk $(TIMESTAMPDIR)/$(arch)/.time $(srcdir)/template/exts.mk.tmpl
+ $(Q)$(MAKE) -f ext/configure-ext.mk $(mflags) V=$(V) EXTSTATIC=$(EXTSTATIC) \
+ gnumake=$(gnumake) MINIRUBY="$(MINIRUBY)" \
+ EXTLDFLAGS="$(EXTLDFLAGS)" srcdir="$(srcdir)"
$(ECHO) generating makefile $@
- $(Q)$(MINIRUBY) $(srcdir)/ext/extmk.rb --make="$(MAKE)" --command-output=$(EXTS_MK) $(EXTMK_ARGS) configure
+ $(Q)$(MINIRUBY) $(srcdir)/tool/generic_erb.rb -o $@ -c \
+ $(srcdir)/template/exts.mk.tmpl --gnumake=$(gnumake)
+
+ext/configure-ext.mk: $(PREP) all-incs $(MKFILES) $(RBCONFIG) $(LIBRUBY) \
+ $(srcdir)/template/configure-ext.mk.tmpl
+ $(ECHO) generating makefiles $@
+ $(Q)$(MAKEDIRS) $(@D)
+ $(Q)$(MINIRUBY) $(srcdir)/tool/generic_erb.rb -o $@ -c \
+ $(srcdir)/template/$(@F).tmpl --srcdir="$(srcdir)" \
+ --miniruby="$(MINIRUBY)" --script-args='$(SCRIPT_ARGS)'
configure-ext: $(EXTS_MK)
build-ext: $(EXTS_MK)
- $(Q)$(MAKE) -f $(EXTS_MK) $(MFLAGS) libdir="$(libdir)" LIBRUBY_EXTS=$(LIBRUBY_EXTS) \
+ $(Q)$(MAKE) -f $(EXTS_MK) $(mflags) libdir="$(libdir)" LIBRUBY_EXTS=$(LIBRUBY_EXTS) \
EXTENCS="$(ENCOBJS)" UPDATE_LIBRARIES=no $(EXTSTATIC)
+ $(Q)$(MAKE) $(EXTS_NOTE)
+
+exts-note: $(EXTS_MK)
+ $(Q)$(MAKE) $(EXTS_NOTE)
+
+ext/extinit.c: $(srcdir)/template/extinit.c.tmpl
+ $(Q)$(MINIRUBY) $(srcdir)/tool/generic_erb.rb -o $@ -c \
+ $(srcdir)/template/extinit.c.tmpl $(EXTINITS)
prog: program wprogram
+programs: $(PROGRAM) $(WPROGRAM)
$(PREP): $(MKFILES)
@@ -229,15 +304,23 @@ miniruby$(EXEEXT): config.status $(ALLOBJS) $(ARCHFILE)
objs: $(ALLOBJS)
GORUBY = go$(RUBY_INSTALL_NAME)
-golf: $(LIBRUBY) $(GOLFOBJS) PHONY
- $(Q) $(MAKE) $(MFLAGS) MAINOBJ="$(GOLFOBJS)" PROGRAM=$(GORUBY)$(EXEEXT) program
+GOLF = $(GORUBY)
+golf: $(GOLF)
+$(GOLF): $(LIBRUBY) $(GOLFOBJS) PHONY
+ $(Q) $(MAKE) $(mflags) \
+ GOLF=_dummy_golf_target_to_avoid_conflict_just_in_case_ \
+ MAINOBJ=goruby.$(OBJEXT) \
+ EXTOBJS="golf_prelude.$(OBJEXT) $(EXTOBJS)" \
+ PROGRAM=$(GORUBY)$(EXEEXT) \
+ V=$(V) \
+ program
capi: $(CAPIOUT)/.timestamp PHONY
$(CAPIOUT)/.timestamp: Doxyfile $(PREP)
$(Q) $(MAKEDIRS) "$(@D)"
$(ECHO) generating capi
-$(Q) $(DOXYGEN) -b
- $(Q) $(MINIRUBY) -e 'File.open(ARGV[0], "w"){|f| f.puts(Time.now)}' "$@"
+ $(Q) $(MINIRUBY) -e 'File.open(ARGV[0], "w"){'"|f|"' f.puts(Time.now)}' "$@"
Doxyfile: $(srcdir)/template/Doxyfile.tmpl $(PREP) $(srcdir)/tool/generic_erb.rb $(RBCONFIG)
$(ECHO) generating $@
@@ -252,18 +335,22 @@ $(PROGRAM) $(WPROGRAM): $(LIBRUBY) $(MAINOBJ) $(OBJS) $(EXTOBJS) $(SETUP) $(PREP
$(LIBRUBY_A): $(LIBRUBY_A_OBJS) $(MAINOBJ) $(INITOBJS) $(ARCHFILE)
-$(LIBRUBY_SO): $(OBJS) $(DLDOBJS) $(LIBRUBY_A) $(PREP) $(LIBRUBY_SO_UPDATE) $(BUILTIN_ENCOBJS)
+$(LIBRUBY_SO): $(OBJS) $(DLDOBJS) $(LIBRUBY_A) $(PREP) $(BUILTIN_ENCOBJS)
$(LIBRUBY_EXTS):
@exit > $@
$(STATIC_RUBY)$(EXEEXT): $(MAINOBJ) $(DLDOBJS) $(EXTOBJS) $(LIBRUBY_A)
$(Q)$(RM) $@
- $(PURIFY) $(CC) $(MAINOBJ) $(DLDOBJS) $(EXTOBJS) $(LIBRUBY_A) $(MAINLIBS) $(EXTLIBS) $(LIBS) $(OUTFLAG)$@ $(LDFLAGS) $(XLDFLAGS)
+ $(PURIFY) $(CC) $(MAINOBJ) $(DLDOBJS) $(LIBRUBY_A) $(MAINLIBS) $(EXTLIBS) $(LIBS) $(OUTFLAG)$@ $(LDFLAGS) $(XLDFLAGS)
ruby.imp: $(COMMONOBJS)
- $(Q)$(NM) -Pgp $(COMMONOBJS) | \
- awk 'BEGIN{print "#!"}; $$2~/^[BDT]$$/&&$$1!~/^(Init_|ruby_static_id_|.*_threadptr_|\.)/{print $$1}' | \
+ $(Q){ \
+ $(NM) -Pgp $(COMMONOBJS) | \
+ awk 'BEGIN{print "#!"}; $$2~/^[BDT]$$/&&$$1!~/^$(SYMBOL_PREFIX)(Init_|InitVM_|ruby_static_id_|.*_threadptr_|rb_ec_)|^\./{print $$1}'; \
+ ($(CHDIR) $(srcdir) && \
+ exec sed -n '/^MJIT_FUNC_EXPORTED/!d;N;s/.*\n\(rb_[a-zA-Z_0-9]*\).*/$(SYMBOL_PREFIX)\1/p' cont.c gc.c thread*c vm*.c) \
+ } | \
sort -u -o $@
install: install-$(INSTALLDOC)
@@ -274,7 +361,7 @@ $(ruby_pc): $(srcdir)/template/ruby.pc.in config.status
install-all: docs pre-install-all do-install-all post-install-all
pre-install-all:: all pre-install-local pre-install-ext pre-install-doc
do-install-all: pre-install-all
- $(INSTRUBY) --make="$(MAKE)" $(INSTRUBY_ARGS) --install=all --rdoc-output="$(RDOCOUT)"
+ $(INSTRUBY) --make="$(MAKE)" $(INSTRUBY_ARGS) --install=all $(INSTALL_DOC_OPTS)
post-install-all:: post-install-local post-install-ext post-install-doc
@$(NULLCMD)
@@ -356,7 +443,7 @@ what-where-all: no-install-all
no-install-all: pre-no-install-all dont-install-all post-no-install-all
pre-no-install-all:: pre-no-install-local pre-no-install-ext pre-no-install-doc
dont-install-all: $(PROGRAM)
- $(INSTRUBY) -n --make="$(MAKE)" $(INSTRUBY_ARGS) --install=all --rdoc-output="$(RDOCOUT)"
+ $(INSTRUBY) -n --make="$(MAKE)" $(INSTRUBY_ARGS) --install=all $(INSTALL_DOC_OPTS)
post-no-install-all:: post-no-install-local post-no-install-ext post-no-install-doc
@$(NULLCMD)
@@ -443,7 +530,7 @@ post-no-install-man::
install-doc: rdoc pre-install-doc do-install-doc post-install-doc
pre-install-doc:: install-prereq
do-install-doc: $(PROGRAM) pre-install-doc
- $(INSTRUBY) --make="$(MAKE)" $(INSTRUBY_ARGS) --install=rdoc --rdoc-output="$(RDOCOUT)"
+ $(INSTRUBY) --make="$(MAKE)" $(INSTRUBY_ARGS) --install=rdoc $(INSTALL_DOC_OPTS)
post-install-doc::
@$(NULLCMD)
@@ -456,15 +543,15 @@ post-install-gem::
rdoc: PHONY main
@echo Generating RDoc documentation
- $(Q) $(XRUBY) "$(srcdir)/bin/rdoc" --root "$(srcdir)" --page-dir "$(srcdir)/doc" --encoding=UTF-8 --no-force-update --all --ri --op "$(RDOCOUT)" --debug $(RDOCFLAGS) "$(srcdir)"
+ $(Q) $(XRUBY) "$(srcdir)/libexec/rdoc" --root "$(srcdir)" --page-dir "$(srcdir)/doc" --encoding=UTF-8 --no-force-update --all --ri --op "$(RDOCOUT)" $(RDOCFLAGS) "$(srcdir)"
html: PHONY main
@echo Generating RDoc HTML files
- $(Q) $(XRUBY) "$(srcdir)/bin/rdoc" --root "$(srcdir)" --page-dir "$(srcdir)/doc" --encoding=UTF-8 --no-force-update --all --op "$(HTMLOUT)" --debug $(RDOCFLAGS) "$(srcdir)"
+ $(Q) $(XRUBY) "$(srcdir)/libexec/rdoc" --root "$(srcdir)" --page-dir "$(srcdir)/doc" --encoding=UTF-8 --no-force-update --all --op "$(HTMLOUT)" $(RDOCFLAGS) "$(srcdir)"
rdoc-coverage: PHONY main
@echo Generating RDoc coverage report
- $(Q) $(XRUBY) "$(srcdir)/bin/rdoc" --root "$(srcdir)" --encoding=UTF-8 --all --quiet -C $(RDOCFLAGS) "$(srcdir)"
+ $(Q) $(XRUBY) "$(srcdir)/libexec/rdoc" --root "$(srcdir)" --encoding=UTF-8 --all --quiet -C $(RDOCFLAGS) "$(srcdir)"
RDOCBENCHOUT=/tmp/rdocbench
@@ -482,7 +569,7 @@ what-where-doc: no-install-doc
no-install-doc: pre-no-install-doc dont-install-doc post-no-install-doc
pre-no-install-doc:: install-prereq
dont-install-doc:: $(PREP)
- $(INSTRUBY) -n --make="$(MAKE)" $(INSTRUBY_ARGS) --install=rdoc --rdoc-output="$(RDOCOUT)"
+ $(INSTRUBY) -n --make="$(MAKE)" $(INSTRUBY_ARGS) --install=rdoc $(INSTALL_DOC_OPTS)
post-no-install-doc::
@$(NULLCMD)
@@ -493,11 +580,12 @@ install-prereq: $(CLEAR_INSTALLED_LIST) yes-fake sudo-precheck PHONY
clear-installed-list: PHONY
@> $(INSTALLED_LIST) set MAKE="$(MAKE)"
-clean: clean-ext clean-enc clean-golf clean-rdoc clean-capi clean-extout clean-local clean-platform
+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) $(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) probes.h probes.$(OBJEXT) probes.stamp ruby-glommed.$(OBJEXT)
+ $(Q)$(RM) y.tab.c y.output encdb.h transdb.h config.log rbconfig.rb $(ruby_pc)
+ $(Q)$(RM) probes.h probes.$(OBJEXT) probes.stamp ruby-glommed.$(OBJEXT) ruby.imp
$(Q)$(RM) GNUmakefile.old Makefile.old $(arch)-fake.rb bisect.sh $(ENC_TRANS_D)
-$(Q) $(RMDIR) enc/jis enc/trans enc 2> $(NULL) || exit 0
clean-runnable:: PHONY
@@ -514,8 +602,10 @@ clean-platform: PHONY
clean-extout: PHONY
-$(Q)$(RMDIR) $(EXTOUT)/$(arch) $(EXTOUT) 2> $(NULL) || exit 0
clean-docs: clean-rdoc clean-html clean-capi
+clean-spec: PHONY
+clean-rubyspec: clean-spec
-distclean: distclean-ext distclean-enc distclean-golf distclean-extout distclean-local distclean-platform
+distclean: distclean-ext distclean-enc distclean-golf distclean-docs distclean-extout distclean-local distclean-platform distclean-spec
distclean-local:: clean-local
$(Q)$(RM) $(MKFILES) yasmdata.rb *.inc $(PRELUDES)
$(Q)$(RM) config.cache config.status config.status.lineno
@@ -523,29 +613,71 @@ distclean-local:: clean-local
-$(Q)$(RMALL) $(srcdir)/autom4te.cache
distclean-ext:: PHONY
distclean-golf: clean-golf
-distclean-rdoc: PHONY
-distclean-html: PHONY
-distclean-capi: PHONY
+distclean-rdoc: clean-rdoc
+distclean-html: clean-html
+distclean-capi: clean-capi
+distclean-docs: clean-docs
distclean-extout: clean-extout
distclean-platform: clean-platform
+distclean-spec: clean-spec
+distclean-rubyspec: distclean-spec
realclean:: realclean-ext realclean-local realclean-enc realclean-golf realclean-extout
-realclean-local:: distclean-local
+realclean-local:: distclean-local realclean-srcs-local
+
+clean-srcs:: clean-srcs-local clean-srcs-ext
+realclean-srcs:: realclean-srcs-local realclean-srcs-ext
+
+clean-srcs-local::
$(Q)$(RM) parse.c parse.h lex.c enc/trans/newline.c revision.h
- $(Q)$(RM) id.c id.h probes.dmyh
+ $(Q)$(RM) id.c id.h probes.dmyh probes.h
+ $(Q)$(RM) encdb.h transdb.h verconf.h ruby-runner.h
+ $(Q)$(RM) mjit_config.h rb_mjit_header.h
+ $(Q)$(RM) $(MJIT_MIN_HEADER) $(MJIT_MIN_HEADER:.h=)$(MJIT_HEADER_SUFFIX:%=*).h
+
+realclean-srcs-local:: clean-srcs-local
$(Q)$(CHDIR) $(srcdir) && $(exec) $(RM) parse.c parse.h lex.c enc/trans/newline.c $(PRELUDES) revision.h
$(Q)$(CHDIR) $(srcdir) && $(exec) $(RM) id.c id.h probes.dmyh
$(Q)$(CHDIR) $(srcdir) && $(exec) $(RM) configure aclocal.m4 tool/config.guess tool/config.sub gems/*.gem
+
+clean-srcs-ext::
+realclean-srcs-ext:: clean-srcs-ext
+
realclean-ext:: PHONY
realclean-golf: distclean-golf
$(Q)$(RM) $(GOLFPRELUDES)
-realclean-capi: PHONY
+realclean-rdoc: distclean-rdoc
+realclean-html: distclean-html
+realclean-capi: distclean-capi
+realclean-docs: distclean-docs
realclean-extout: distclean-extout
+realclean-platform: distclean-platform
+realclean-spec: distclean-spec
+realclean-rubyspec: realclean-spec
-clean-ext distclean-ext realclean-ext::
- $(Q)$(RM) $(EXTS_MK)
- $(Q)$(RM) $(TIMESTAMPDIR)/.*.time $(TIMESTAMPDIR)/.$(arch).time $(TIMESTAMPDIR)/$(arch)/.time
- $(Q)$(RMDIR) $(TIMESTAMPDIR)/$(arch) $(TIMESTAMPDIR) 2> $(NULL) || exit 0
+clean-ext:: ext/clean gems/clean timestamp/clean
+distclean-ext:: ext/distclean gems/distclean timestamp/distclean
+realclean-ext:: ext/realclean gems/realclean timestamp/realclean
+
+ext/clean.mk ext/distclean.mk ext/realclean.mk::
+ext/clean gems/clean:: ext/clean.mk
+ext/distclean gems/distclean:: ext/distclean.mk
+ext/realclean gems/realclean:: ext/realclean.mk
+
+timestamp/clean:: ext/clean gems/clean
+timestamp/distclean:: ext/distclean gems/distclean
+timestamp/realclean:: ext/realclean gems/realclean
+
+timestamp/clean timestamp/distclean timestamp/realclean::
+ $(Q)$(RM) $(TIMESTAMPDIR)/.*.time $(TIMESTAMPDIR)/$(arch)/.time
+ $(Q)$(RMDIRS) $(TIMESTAMPDIR)/$(arch) 2> $(NULL) || exit 0
+
+clean-ext::
+ -$(Q)$(RM) ext/extinit.$(OBJEXT)
+
+distclean-ext realclean-ext::
+ -$(Q)$(RM) $(EXTS_MK) ext/extinit.* ext/configure-ext.mk
+ -$(Q)$(RMDIR) ext 2> $(NULL) || exit 0
clean-enc distclean-enc realclean-enc: PHONY
@@ -571,7 +703,12 @@ clean-platform:
$(Q) $(RM) $(PLATFORM_D)
-$(Q) $(RMDIR) $(PLATFORM_DIR) 2> $(NULL) || exit 0
-check: main test test-testframework test-almost
+RUBYSPEC_CAPIEXT = spec/ruby/optional/capi/ext
+clean-spec: PHONY
+ -$(Q) $(RM) $(RUBYSPEC_CAPIEXT)/*.$(OBJEXT) $(RUBYSPEC_CAPIEXT)/*.$(DLEXT)
+ -$(Q) $(RMDIRS) $(RUBYSPEC_CAPIEXT) 2> $(NULL) || exit 0
+
+check: main test test-testframework test-almost test-spec
$(ECHO) check succeeded
check-ruby: test test-ruby
@@ -590,12 +727,12 @@ $(arch)-fake.rb: $(srcdir)/template/fake.rb.in $(srcdir)/tool/generic_erb.rb ver
btest: $(TEST_RUNNABLE)-btest
no-btest: PHONY
yes-btest: fake miniruby$(EXEEXT) PHONY
- $(Q)$(exec) $(BOOTSTRAPRUBY) "$(srcdir)/bootstraptest/runner.rb" --ruby="$(BTESTRUBY) $(RUN_OPTS)" $(OPTS) $(TESTOPTS)
+ $(Q)$(exec) $(BOOTSTRAPRUBY) "$(srcdir)/bootstraptest/runner.rb" --ruby="$(BTESTRUBY) $(RUN_OPTS)" $(OPTS) $(TESTOPTS) $(BTESTS)
btest-ruby: $(TEST_RUNNABLE)-btest-ruby
no-btest-ruby: PHONY
yes-btest-ruby: prog PHONY
- $(Q)$(exec) $(RUNRUBY) "$(srcdir)/bootstraptest/runner.rb" --ruby="$(PROGRAM) -I$(srcdir)/lib $(RUN_OPTS)" -q $(OPTS) $(TESTOPTS)
+ $(Q)$(exec) $(RUNRUBY) "$(srcdir)/bootstraptest/runner.rb" --ruby="$(PROGRAM) -I$(srcdir)/lib $(RUN_OPTS)" -q $(OPTS) $(TESTOPTS) $(BTESTS)
test-basic: $(TEST_RUNNABLE)-test-basic
no-test-basic: PHONY
@@ -610,30 +747,31 @@ yes-test-knownbug: prog PHONY
test-testframework: $(TEST_RUNNABLE)-test-testframework
yes-test-testframework: prog PHONY
- $(Q)$(exec) $(RUNRUBY) "$(srcdir)/test/runner.rb" --ruby="$(RUNRUBY)" $(TESTOPTS) testunit minitest
+ $(gnumake_recursive)$(Q)$(exec) $(RUNRUBY) "$(srcdir)/test/runner.rb" --ruby="$(RUNRUBY)" $(TESTOPTS) testunit minitest
no-test-testframework: PHONY
test-sample: test-basic # backward compatibility for mswin-build
-test: btest-ruby test-knownbug test-basic
+test-short: btest-ruby test-knownbug test-basic
+test: test-short
# $ make test-all TESTOPTS="--help" displays more detail
# for example, make test-all TESTOPTS="-j2 -v -n test-name -- test-file-name"
test-all: $(TEST_RUNNABLE)-test-all
-yes-test-all: prog PHONY
- $(Q)$(exec) $(RUNRUBY) "$(srcdir)/test/runner.rb" --ruby="$(RUNRUBY)" $(TEST_EXCLUDES) $(TESTOPTS) $(TESTS)
+yes-test-all: programs PHONY
+ $(gnumake_recursive)$(Q)$(exec) $(RUNRUBY) "$(srcdir)/test/runner.rb" --ruby="$(RUNRUBY)" $(TEST_EXCLUDES) $(TESTOPTS) $(TESTS)
TESTS_BUILD = mkmf
no-test-all: PHONY
- $(MINIRUBY) -I"$(srcdir)/lib" "$(srcdir)/test/runner.rb" $(TESTOPTS) $(TESTS_BUILD)
+ $(gnumake_recursive)$(MINIRUBY) -I"$(srcdir)/lib" "$(srcdir)/test/runner.rb" $(TESTOPTS) $(TESTS_BUILD)
test-almost: $(TEST_RUNNABLE)-test-almost
yes-test-almost: prog PHONY
- $(Q)$(exec) $(RUNRUBY) "$(srcdir)/test/runner.rb" --ruby="$(RUNRUBY)" $(TEST_EXCLUDES) $(TESTOPTS) $(EXCLUDE_TESTFRAMEWORK) $(TESTS)
+ $(gnumake_recursive)$(Q)$(exec) $(RUNRUBY) "$(srcdir)/test/runner.rb" --ruby="$(RUNRUBY)" $(TEST_EXCLUDES) $(TESTOPTS) $(EXCLUDE_TESTFRAMEWORK) $(TESTS)
no-test-almost: PHONY
test-ruby: $(TEST_RUNNABLE)-test-ruby
no-test-ruby: PHONY
yes-test-ruby: prog encs PHONY
- $(RUNRUBY) "$(srcdir)/test/runner.rb" $(TEST_EXCLUDES) $(TESTOPTS) -- ruby -ext-
+ $(gnumake_recursive)$(RUNRUBY) "$(srcdir)/test/runner.rb" $(TEST_EXCLUDES) $(TESTOPTS) -- ruby -ext-
extconf: $(PREP)
$(Q) $(MAKEDIRS) "$(EXTCONFDIR)"
@@ -646,18 +784,24 @@ $(RBCONFIG): $(srcdir)/tool/mkconfig.rb config.status $(srcdir)/version.h
-e '(mis.delete(ARGF.path); ARGF.close) if /ONIG_UNICODE_VERSION_STRING +"#{Regexp.quote(version)}"/o' \
$(UNICODE_VERSION) $(UNICODE_DATA_HEADERS)
$(Q)$(BOOTSTRAPRUBY) $(srcdir)/tool/mkconfig.rb \
- -cross_compiling=$(CROSS_COMPILING) \
-arch=$(arch) -version=$(RUBY_PROGRAM_VERSION) \
-install_name=$(RUBY_INSTALL_NAME) \
-so_name=$(RUBY_SO_NAME) \
-unicode_version=$(UNICODE_VERSION) \
+ -unicode_emoji_version=$(UNICODE_EMOJI_VERSION) \
> rbconfig.tmp
$(IFCHANGE) "--timestamp=$@" rbconfig.rb rbconfig.tmp
-test-rubyspec-precheck:
+test-rubyspec: test-spec
+yes-test-rubyspec: yes-test-spec
+
+test-spec-precheck: $(arch)-fake.rb programs
-test-rubyspec: test-rubyspec-precheck $(arch)-fake.rb
- $(RUNRUBY) -r./$(arch)-fake $(srcdir)/spec/mspec/bin/mspec run -B $(srcdir)/spec/default.mspec $(MSPECOPT)
+test-spec: $(TEST_RUNNABLE)-test-spec
+yes-test-spec: test-spec-precheck
+ $(gnumake_recursive)$(Q) \
+ $(RUNRUBY) -r./$(arch)-fake $(srcdir)/spec/mspec/bin/mspec run -B $(srcdir)/spec/default.mspec $(MSPECOPT) $(SPECOPTS)
+no-test-spec:
RUNNABLE = $(LIBRUBY_RELATIVE:no=un)-runnable
runnable: $(RUNNABLE) prog $(srcdir)/tool/mkrunnable.rb PHONY
@@ -674,6 +818,7 @@ encs enc trans libencs libenc libtrans: $(SHOWFLAGS) $(ENC_MK) $(LIBRUBY) $(PREP
libenc enc: {$(VPATH)}encdb.h
libtrans trans: {$(VPATH)}transdb.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 $(srcdir)/lib/mkmf.rb $(RBCONFIG) fake
$(ECHO) generating $@
@@ -687,9 +832,10 @@ $(ENC_MK): $(srcdir)/enc/make_encmake.rb $(srcdir)/enc/Makefile.in $(srcdir)/enc
.PHONY: clean clean-ext clean-local clean-enc clean-golf clean-rdoc clean-html clean-extout
.PHONY: distclean distclean-ext distclean-local distclean-enc distclean-golf distclean-extout
.PHONY: realclean realclean-ext realclean-local realclean-enc realclean-golf realclean-extout
-.PHONY: check test test-all btest btest-ruby test-basic test-knownbug
-.PHONY: run runruby parse benchmark benchmark-each tbench gdb gdb-ruby
-.PHONY: update-mspec update-rubyspec test-rubyspec
+.PHONY: exam check test test-short test-all btest btest-ruby test-basic test-knownbug
+.PHONY: run runruby parse benchmark gdb gdb-ruby
+.PHONY: update-mspec update-rubyspec test-rubyspec test-spec
+.PHONY: touch-unicode-files
PHONY:
@@ -699,9 +845,10 @@ PHONY:
{$(srcdir)}.y.c:
$(ECHO) generating $@
$(Q)$(BASERUBY) $(srcdir)/tool/id2token.rb --path-separator=.$(PATH_SEPARATOR)./ --vpath=$(VPATH) id.h $(SRC_FILE) > parse.tmp.y
+ $(Q)$(BASERUBY) $(srcdir)/tool/pure_parser.rb parse.tmp.y $(YACC)
$(Q)$(YACC) -d $(YFLAGS) -o y.tab.c parse.tmp.y
$(Q)$(RM) parse.tmp.y
- $(Q)sed -f $(srcdir)/tool/ytab.sed -e "/^#/s!parse\.tmp\.[iy]!parse.y!" -e "/^#/s!y\.tab\.c!$@!" y.tab.c > $@.new
+ $(Q)sed -f $(srcdir)/tool/ytab.sed -e "/^#/s|parse\.tmp\.[iy]|$(SRC_FILE)|" -e "/^#/s!y\.tab\.c!$@!" y.tab.c > $@.new
$(Q)$(MV) $@.new $@
$(Q)sed -e "/^#line.*y\.tab\.h/d;/^#line.*parse.*\.y/d" y.tab.h > $(@:.c=.h)
$(Q)$(RM) y.tab.c y.tab.h
@@ -710,14 +857,30 @@ $(PLATFORM_D):
$(Q) $(MAKEDIRS) $(PLATFORM_DIR) $(@D)
@exit > $@
+exe/$(PROGRAM): ruby-runner.c ruby-runner.h exe/.time miniruby$(EXEEXT) {$(VPATH)}config.h
+ $(Q) $(CC) $(CFLAGS) $(CPPFLAGS) -DRUBY_INSTALL_NAME=$(@F) $(COUTFLAG)ruby-runner.$(OBJEXT) -c $(CSRCFLAG)$(srcdir)/ruby-runner.c
+ $(Q) $(PURIFY) $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(OUTFLAG)$@ ruby-runner.$(OBJEXT) $(LIBS)
+ $(Q) $(POSTLINK)
+ $(Q) ./miniruby$(EXEEXT) \
+ -e 'prog, dest = ARGV; dest += "/ruby"' \
+ -e 'unless prog=="ruby"' \
+ -e ' begin File.unlink(dest); rescue Errno::ENOENT; end' \
+ -e ' File.symlink(prog, dest)' \
+ -e 'end' \
+ $(@F) $(@D)
+
+exe/.time:
+ $(Q) $(MAKEDIRS) $(@D)
+ @exit > $@
+
$(BUILTIN_ENCOBJS) $(BUILTIN_TRANSOBJS): $(ENC_TRANS_D)
$(ENC_TRANS_D):
$(Q) $(MAKEDIRS) enc/trans $(@D)
@exit > $@
-$(TIMESTAMPDIR)/.$(arch).time:
- $(Q)$(MAKEDIRS) $(@D) $(TIMESTAMPDIR)/$(arch)
+$(TIMESTAMPDIR)/$(arch)/.time:
+ $(Q)$(MAKEDIRS) $(@D) $(EXTOUT)/$(arch)
@exit > $@
###
@@ -747,11 +910,15 @@ strerror.$(OBJEXT): {$(VPATH)}strerror.c
strlcat.$(OBJEXT): {$(VPATH)}strlcat.c
strlcpy.$(OBJEXT): {$(VPATH)}strlcpy.c
strstr.$(OBJEXT): {$(VPATH)}strstr.c
-strtod.$(OBJEXT): {$(VPATH)}strtod.c
-strtol.$(OBJEXT): {$(VPATH)}strtol.c
nt.$(OBJEXT): {$(VPATH)}nt.c
ia64.$(OBJEXT): {$(VPATH)}ia64.s
$(CC) $(CFLAGS) -c $<
+.coroutine_obj $(COROUTINE_OBJ): \
+ {$(VPATH)}$(COROUTINE_H:.h=).$(ASMEXT) \
+ $(COROUTINE_H:/Context.h=/.time)
+$(COROUTINE_H:/Context.h=/.time):
+ $(Q) $(MAKEDIRS) $(@D)
+ @exit > $@
###
@@ -765,7 +932,8 @@ compile.$(OBJEXT): {$(VPATH)}opt_sc.inc {$(VPATH)}optunifs.inc
win32/win32.$(OBJEXT): {$(VPATH)}win32/win32.c {$(VPATH)}win32/file.h \
{$(VPATH)}dln.h {$(VPATH)}dln_find.c {$(VPATH)}encindex.h \
- {$(VPATH)}internal.h {$(VPATH)}util.h $(RUBY_H_INCLUDES) $(PLATFORM_D)
+ {$(VPATH)}internal.h {$(VPATH)}util.h $(RUBY_H_INCLUDES) \
+ {$(VPATH)}vm.h $(PLATFORM_D)
win32/file.$(OBJEXT): {$(VPATH)}win32/file.c {$(VPATH)}win32/file.h \
$(RUBY_H_INCLUDES) $(PLATFORM_D)
@@ -784,24 +952,28 @@ $(OBJS): {$(VPATH)}config.h {$(VPATH)}missing.h
INSNS2VMOPT = --srcdir="$(srcdir)"
-{$(VPATH)}minsns.inc: $(srcdir)/template/minsns.inc.tmpl
-
-{$(VPATH)}opt_sc.inc: $(srcdir)/template/opt_sc.inc.tmpl
-
-{$(VPATH)}optinsn.inc: $(srcdir)/template/optinsn.inc.tmpl
-
-{$(VPATH)}optunifs.inc: $(srcdir)/template/optunifs.inc.tmpl
-
-{$(VPATH)}insns.inc: $(srcdir)/template/insns.inc.tmpl
-
-{$(VPATH)}insns_info.inc: $(srcdir)/template/insns_info.inc.tmpl
-
-{$(VPATH)}vmtc.inc: $(srcdir)/template/vmtc.inc.tmpl
-
-{$(VPATH)}vm.inc: $(srcdir)/template/vm.inc.tmpl
-
-common-srcs: {$(VPATH)}parse.c {$(VPATH)}lex.c {$(VPATH)}enc/trans/newline.c {$(VPATH)}id.c \
- srcs-lib srcs-ext
+srcs_vpath = {$(VPATH)}
+
+# TODO: dependencies on tool/ruby_vm scripts.
+inc_common_headers = $(srcdir)/tool/ruby_vm/views/_copyright.erb $(srcdir)/tool/ruby_vm/views/_notice.erb
+$(srcs_vpath)opt_sc.inc: $(srcdir)/tool/ruby_vm/views/opt_sc.inc.erb $(inc_common_headers)
+$(srcs_vpath)optinsn.inc: $(srcdir)/tool/ruby_vm/views/optinsn.inc.erb $(inc_common_headers)
+$(srcs_vpath)optunifs.inc: $(srcdir)/tool/ruby_vm/views/optunifs.inc.erb $(inc_common_headers)
+$(srcs_vpath)insns.inc: $(srcdir)/tool/ruby_vm/views/insns.inc.erb $(inc_common_headers)
+$(srcs_vpath)insns_info.inc: $(srcdir)/tool/ruby_vm/views/insns_info.inc.erb $(inc_common_headers) \
+ $(srcdir)/tool/ruby_vm/views/_insn_type_chars.erb $(srcdir)/tool/ruby_vm/views/_insn_name_info.erb \
+ $(srcdir)/tool/ruby_vm/views/_insn_len_info.erb $(srcdir)/tool/ruby_vm/views/_insn_operand_info.erb \
+ $(srcdir)/tool/ruby_vm/views/_attributes.erb $(srcdir)/tool/ruby_vm/views/_insn_stack_increase.erb
+$(srcs_vpath)vmtc.inc: $(srcdir)/tool/ruby_vm/views/vmtc.inc.erb $(inc_common_headers)
+$(srcs_vpath)vm.inc: $(srcdir)/tool/ruby_vm/views/vm.inc.erb $(inc_common_headers) \
+ $(srcdir)/tool/ruby_vm/views/_insn_entry.erb $(srcdir)/tool/ruby_vm/views/_trace_instruction.erb
+$(srcs_vpath)mjit_compile.inc: $(srcdir)/tool/ruby_vm/views/mjit_compile.inc.erb $(inc_common_headers) \
+ $(srcdir)/tool/ruby_vm/views/_mjit_compile_insn.erb $(srcdir)/tool/ruby_vm/views/_mjit_compile_send.erb \
+ $(srcdir)/tool/ruby_vm/views/_mjit_compile_ivar.erb \
+ $(srcdir)/tool/ruby_vm/views/_mjit_compile_insn_body.erb $(srcdir)/tool/ruby_vm/views/_mjit_compile_pc_and_sp.erb
+
+common-srcs: $(srcs_vpath)parse.c $(srcs_vpath)lex.c $(srcs_vpath)enc/trans/newline.c $(srcs_vpath)id.c \
+ srcs-lib srcs-ext incs
missing-srcs: $(srcdir)/missing/des_tables.c
@@ -809,14 +981,22 @@ srcs: common-srcs missing-srcs srcs-enc
EXT_SRCS = $(srcdir)/ext/ripper/ripper.c \
$(srcdir)/ext/rbconfig/sizeof/sizes.c \
+ $(srcdir)/ext/rbconfig/sizeof/limits.c \
$(srcdir)/ext/socket/constdefs.c \
+ $(srcdir)/ext/etc/constdefs.h \
# EXT_SRCS
srcs-ext: $(EXT_SRCS)
+realclean-srcs-ext::
+ $(Q)$(RM) $(EXT_SRCS)
-srcs-extra: $(srcdir)/ext/json/parser/parser.c \
- $(srcdir)/ext/date/zonetab.h \
- $(empty)
+EXTRA_SRCS = $(srcdir)/ext/json/parser/parser.c \
+ $(srcdir)/ext/date/zonetab.h \
+ $(empty)
+
+srcs-extra: $(EXTRA_SRCS)
+realclean-srcs-extra::
+ $(Q)$(RM) $(EXTRA_SRCS)
LIB_SRCS = $(srcdir)/lib/unicode_normalize/tables.rb
@@ -845,9 +1025,9 @@ id.c: $(srcdir)/tool/generic_erb.rb $(srcdir)/template/id.c.tmpl $(srcdir)/defs/
$(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb --output=$@ \
$(srcdir)/template/id.c.tmpl
-node_name.inc: {$(VPATH)}node.h
+node_name.inc: $(srcdir)/tool/node_name.rb $(srcdir)/node.h
$(ECHO) generating $@
- $(Q) $(BASERUBY) -n $(srcdir)/tool/node_name.rb < $? > $@
+ $(Q) $(BASERUBY) -n $(srcdir)/tool/node_name.rb < $(srcdir)/node.h > $@
encdb.h: $(PREP) $(srcdir)/tool/generic_erb.rb $(srcdir)/template/encdb.h.tmpl
$(ECHO) generating $@
@@ -878,11 +1058,17 @@ $(PRELUDE_C): $(COMPILE_PRELUDE) \
$(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb -I$(srcdir) -c -o $@ \
$(srcdir)/template/prelude.c.tmpl $(PRELUDE_SCRIPTS)
-{$(VPATH)}golf_prelude.c: $(COMPILE_PRELUDE) {$(srcdir)}golf_prelude.rb
+$(GOLF_PRELUDE_C): $(COMPILE_PRELUDE) {$(srcdir)}golf_prelude.rb
$(ECHO) generating $@
$(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb -I$(srcdir) -c -o $@ \
$(srcdir)/template/prelude.c.tmpl golf_prelude.rb
+MAINCPPFLAGS = $(ENABLE_DEBUG_ENV:yes=-DRUBY_DEBUG_ENV=1)
+
+$(MAINOBJ): $(srcdir)/$(MAINSRC)
+ $(ECHO) compiling $(srcdir)/$(MAINSRC)
+ $(Q) $(CC) $(MAINCPPFLAGS) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$(srcdir)/$(MAINSRC)
+
{$(VPATH)}probes.dmyh: {$(srcdir)}probes.d $(srcdir)/tool/gen_dummy_probes.rb
probes.dmyh:
@@ -897,42 +1083,57 @@ preludes: {$(VPATH)}miniprelude.c
preludes: {$(srcdir)}golf_prelude.c
$(srcdir)/revision.h:
- @exit > $@
+ $(Q)$(gnumake:yes=#) $(RM) $(@F)
+ $(Q)$(gnumake:yes=#) exit > $@ || exit > $(@F)
$(REVISION_H): $(srcdir)/version.h $(srcdir)/tool/file2lastrev.rb $(REVISION_FORCE)
- -$(Q) $(BASERUBY) $(srcdir)/tool/file2lastrev.rb --revision.h "$(srcdir)" > revision.tmp
+ -$(Q) $(BASERUBY) $(srcdir)/tool/file2lastrev.rb -q --revision.h "$(srcdir)" > revision.tmp
$(Q)$(IFCHANGE) "--timestamp=$@" "$(srcdir)/revision.h" revision.tmp
-$(srcdir)/ext/ripper/ripper.c: $(srcdir)/parse.y id.h
+$(srcdir)/ext/ripper/ripper.c: $(srcdir)/ext/ripper/tools/preproc.rb $(srcdir)/parse.y id.h
$(ECHO) generating $@
- $(Q) $(CHDIR) $(@D) && \
- sed /AUTOGENERATED/q depend | \
- $(exec) $(MAKE) -f - $(MFLAGS) \
- Q=$(Q) ECHO=$(ECHO) RM="$(RM)" top_srcdir=../.. srcdir=. VPATH="$(PWD)" \
+ $(Q) VPATH=$${PWD-`pwd`} && $(CHDIR) $(@D) && \
+ sed -e 's/{\$$([^(){}]*)[^{}]*}//g' -e /AUTOGENERATED/q depend | \
+ $(exec) $(MAKE) -f - $(mflags) \
+ Q=$(Q) ECHO=$(ECHO) RM="$(RM)" top_srcdir=../.. srcdir=. VPATH="$${VPATH}" \
RUBY="$(BASERUBY)" PATH_SEPARATOR="$(PATH_SEPARATOR)"
$(srcdir)/ext/json/parser/parser.c: $(srcdir)/ext/json/parser/parser.rl
$(ECHO) generating $@
- $(Q) $(CHDIR) $(@D) && $(exec) $(MAKE) -f prereq.mk $(MFLAGS) \
+ $(Q) $(CHDIR) $(@D) && $(exec) $(MAKE) -f prereq.mk $(mflags) \
Q=$(Q) ECHO=$(ECHO) top_srcdir=../../.. srcdir=. VPATH=../../.. BASERUBY="$(BASERUBY)"
$(srcdir)/ext/date/zonetab.h: $(srcdir)/ext/date/zonetab.list
$(ECHO) generating $@
- $(Q) $(CHDIR) $(@D) && $(exec) $(MAKE) -f prereq.mk $(MFLAGS) \
+ $(Q) $(CHDIR) $(@D) && $(exec) $(MAKE) -f prereq.mk $(mflags) \
Q=$(Q) ECHO=$(ECHO) top_srcdir=../.. srcdir=. VPATH=../.. BASERUBY="$(BASERUBY)"
$(srcdir)/ext/rbconfig/sizeof/sizes.c: $(srcdir)/ext/rbconfig/sizeof/depend \
- $(srcdir)/tool/generic_erb.rb $(srcdir)/template/sizes.c.tmpl $(srcdir)/configure.in
+ $(srcdir)/tool/generic_erb.rb $(srcdir)/template/sizes.c.tmpl $(srcdir)/configure.ac
$(ECHO) generating $@
$(Q) $(CHDIR) $(@D) && \
sed '/AUTOGENERATED/q' depend | \
- $(exec) $(MAKE) -f - $(MFLAGS) \
- Q=$(Q) ECHO=$(ECHO) top_srcdir=../../.. srcdir=. VPATH=../../.. RUBY="$(BASERUBY)"
+ $(exec) $(MAKE) -f - $(mflags) \
+ Q=$(Q) ECHO=$(ECHO) top_srcdir=../../.. srcdir=. VPATH=../../.. RUBY="$(BASERUBY)" $(@F)
+
+$(srcdir)/ext/rbconfig/sizeof/limits.c: $(srcdir)/ext/rbconfig/sizeof/depend \
+ $(srcdir)/tool/generic_erb.rb $(srcdir)/template/limits.c.tmpl
+ $(ECHO) generating $@
+ $(Q) $(CHDIR) $(@D) && \
+ sed '/AUTOGENERATED/q' 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 | \
- $(exec) $(MAKE) -f - $(MFLAGS) \
+ $(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 | \
+ $(exec) $(MAKE) -f - $(mflags) \
Q=$(Q) ECHO=$(ECHO) top_srcdir=../.. srcdir=. VPATH=../.. RUBY="$(BASERUBY)"
##
@@ -944,7 +1145,7 @@ runruby: $(PROGRAM) PHONY
$(RUNRUBY) $(TESTRUN_SCRIPT)
parse: fake miniruby$(EXEEXT) PHONY
- $(BTESTRUBY) $(srcdir)/tool/parse.rb $(TESTRUN_SCRIPT)
+ $(BTESTRUBY) --dump=parsetree_with_comment,insns $(TESTRUN_SCRIPT)
bisect: PHONY
$(srcdir)/tool/bisect.sh miniruby $(srcdir)
@@ -954,27 +1155,19 @@ bisect-ruby: PHONY
COMPARE_RUBY = $(BASERUBY)
ITEM =
+ARGS = $$(find $(srcdir)/benchmark -maxdepth 1 -name '*$(ITEM)*.yml' -o -name '*$(ITEM)*.rb' | sort)
OPTS =
# You can pass several options through OPTS environment variable.
-# $ make benchmark OPTS="--help" displays more detail.
+# $ make benchmark ARGS="--help" displays more detail.
# for example,
# $ make benchmark COMPARE_RUBY="ruby-trunk" OPTS="-e ruby-2.2.2"
# This command compares trunk and built-ruby and 2.2.2
-benchmark: miniruby$(EXEEXT) PHONY
- $(BASERUBY) $(srcdir)/benchmark/driver.rb -v \
- --executables="$(COMPARE_RUBY) -I$(srcdir)/lib -I. -I$(EXTOUT)/common --disable-gem; built-ruby::$(MINIRUBY) --disable-gem" \
- --pattern='bm_' --directory=$(srcdir)/benchmark $(OPTS)
-
-benchmark-each: miniruby$(EXEEXT) PHONY
- $(BASERUBY) $(srcdir)/benchmark/driver.rb -v \
- --executables="$(COMPARE_RUBY) -I$(srcdir)/lib -I. -I$(EXTOUT)/common --disable-gem; built-ruby::$(MINIRUBY) --disable-gem" \
- --pattern=$(ITEM) --directory=$(srcdir)/benchmark $(OPTS)
-
-tbench: miniruby$(EXEEXT) PHONY
- $(BASERUBY) $(srcdir)/benchmark/driver.rb -v \
- --executables="$(COMPARE_RUBY) -I$(srcdir)/lib -I. -I$(EXTOUT)/common --disable-gem; built-ruby::$(MINIRUBY) --disable-gem" \
- --pattern='bmx_' --directory=$(srcdir)/benchmark $(OPTS)
+benchmark: miniruby$(EXEEXT) update-benchmark-driver PHONY
+ $(BASERUBY) -rrubygems -I$(srcdir)/benchmark/lib $(srcdir)/benchmark/benchmark-driver/exe/benchmark-driver \
+ --executables="compare-ruby::$(COMPARE_RUBY) -I$(EXTOUT)/common --disable-gem" \
+ --executables="built-ruby::$(MINIRUBY) -r$(srcdir)/prelude --disable-gem" \
+ $(ARGS) $(OPTS)
run.gdb:
echo set breakpoint pending on > run.gdb
@@ -997,40 +1190,53 @@ gdb: miniruby$(EXEEXT) run.gdb PHONY
gdb-ruby: $(PROGRAM) run.gdb PHONY
$(Q) $(RUNRUBY_COMMAND) $(RUNRUBY_DEBUGGER) -- $(TESTRUN_SCRIPT)
+LLDB_INIT = command script import -r $(srcdir)/misc/lldb_cruby.py
+
+lldb: miniruby$(EXEEXT) PHONY
+ lldb -o '$(LLDB_INIT)' miniruby$(EXEEXT) -- $(TESTRUN_SCRIPT)
+
+lldb-ruby: $(PROGRAM) PHONY
+ lldb $(enable_shared:yes=-o 'target modules add ${LIBRUBY_SO}') -o '$(LLDB_INIT)' $(PROGRAM) -- $(TESTRUN_SCRIPT)
+
+DISTPKGS = gzip,zip,all
dist:
$(BASERUBY) $(srcdir)/tool/make-snapshot \
- -srcdir=$(srcdir) \
+ -srcdir=$(srcdir) -packages=$(DISTPKGS) \
-unicode-version=$(UNICODE_VERSION) \
tmp $(RELNAME)
up:: update-remote
up::
- -$(Q)$(MAKE) $(MFLAGS) Q=$(Q) REVISION_FORCE=PHONY "$(REVISION_H)"
+ -$(Q)$(MAKE) $(mflags) Q=$(Q) REVISION_FORCE=PHONY "$(REVISION_H)"
up::
- -$(Q)$(MAKE) $(MFLAGS) Q=$(Q) after-update
+ -$(Q)$(MAKE) $(mflags) Q=$(Q) after-update
after-update:: extract-extlibs
-update-remote:: update-src update-rubyspec update-download
+update-remote:: update-src update-download
update-download:: update-unicode update-gems download-extlibs
+update-mspec:
+update-rubyspec:
+
update-config_files: PHONY
- $(Q) $(BASERUBY) -C "$(srcdir)/tool" \
- ../tool/downloader.rb -e gnu \
+ $(Q) $(BASERUBY) -C "$(srcdir)" tool/downloader.rb -d tool --cache-dir=$(CACHE_DIR) -e gnu \
config.guess config.sub
update-gems: PHONY
$(ECHO) Downloading bundled gem files...
- $(Q) $(BASERUBY) -C "$(srcdir)/gems" \
- -I../tool -rdownloader -answ \
+ $(Q) $(BASERUBY) -C "$(srcdir)" \
+ -I./tool -rdownloader -answ \
-e 'gem, ver = *$$F' \
- -e 'old = Dir.glob("#{gem}-*.gem")' \
+ -e 'old = Dir.glob("gems/#{gem}-*.gem")' \
-e 'gem = "#{gem}-#{ver}.gem"' \
- -e 'Downloader::RubyGems.download(gem, nil, nil) and' \
- -e 'File.unlink(*(old-[gem]))' \
- bundled_gems
+ -e 'Downloader::RubyGems.download(gem, "gems", nil) and' \
+ -e '(old.delete("gems/#{gem}"); !old.empty?) and' \
+ -e 'File.unlink(*old) and' \
+ -e 'FileUtils.rm_rf(old.map{'"|n|"'n.chomp(".gem")})' \
+ gems/bundled_gems
extract-gems: PHONY
$(ECHO) Extracting bundled gem files...
@@ -1043,12 +1249,44 @@ extract-gems: PHONY
update-bundled_gems: PHONY
$(Q) $(RUNRUBY) -rrubygems \
-pla \
- -e '$$_=Gem::SpecFetcher.fetcher.detect(:latest) {|s|' \
- -e 'break "#{s.name} #{s.version}" if s.platform=="ruby"&&s.name==$$F[0]' \
+ -e '(gem,src), = Gem::SpecFetcher.fetcher.detect(:latest) {'"|s|" \
+ -e 's.platform=="ruby"&&s.name==$$F[0]' \
-e '}' \
+ -e 'gem = src.fetch_spec(gem)' \
+ -e '$$_ = [gem.name, gem.version, gem.metadata["source_code_uri"]||gem.homepage].join(" ")' \
"$(srcdir)/gems/bundled_gems" | \
"$(IFCHANGE)" "$(srcdir)/gems/bundled_gems" -
+test-bundled-gems-precheck: $(arch)-fake.rb programs
+
+test-bundled-gems-fetch: $(PREP)
+ $(Q) $(BASERUBY) -C $(srcdir)/gems ../tool/fetch-bundled_gems.rb src bundled_gems
+
+test-bundled-gems-prepare: test-bundled-gems-precheck test-bundled-gems-fetch
+ $(XRUBY) -C "$(srcdir)" bin/gem install --no-document \
+ --install-dir .bundle --conservative "bundler" "minitest:~> 5" 'test-unit' 'rake' 'hoe' 'yard' 'pry' 'packnga'
+
+PREPARE_BUNDLED_GEMS = test-bundled-gems-prepare
+test-bundled-gems: $(TEST_RUNNABLE)-test-bundled-gems
+yes-test-bundled-gems: test-bundled-gems-run
+no-test-bundled-gems:
+test-bundled-gems-run: $(PREPARE_BUNDLED_GEMS)
+
+test-bundler-precheck: $(arch)-fake.rb programs
+
+yes-test-bundler-prepare: test-bundler-precheck
+ $(XRUBY) -C "$(srcdir)" bin/gem install --no-document \
+ --install-dir .bundle --conservative "rspec:~> 3.5"
+
+RSPECOPTS = --format progress
+BUNDLER_SPECS =
+test-bundler: $(TEST_RUNNABLE)-test-bundler
+yes-test-bundler: yes-test-bundler-prepare
+ $(gnumake_recursive)$(Q) \
+ $(XRUBY) -C $(srcdir) -Ispec/bundler .bundle/bin/rspec \
+ --require spec_helper $(RSPECOPTS) spec/bundler/$(BUNDLER_SPECS)
+no-test-bundler:
+
UNICODE_FILES = $(UNICODE_SRC_DATA_DIR)/UnicodeData.txt \
$(UNICODE_SRC_DATA_DIR)/CompositionExclusions.txt \
$(UNICODE_SRC_DATA_DIR)/NormalizationTest.txt \
@@ -1066,20 +1304,57 @@ UNICODE_PROPERTY_FILES = \
$(UNICODE_SRC_DATA_DIR)/Scripts.txt \
$(empty)
-update-unicode: $(UNICODE_FILES)
+UNICODE_AUXILIARY_FILES = \
+ $(UNICODE_SRC_DATA_DIR)/auxiliary/GraphemeBreakProperty.txt \
+ $(UNICODE_SRC_DATA_DIR)/auxiliary/GraphemeBreakTest.txt \
+ $(empty)
+
+UNICODE_EMOJI_FILES = \
+ $(UNICODE_SRC_EMOJI_DATA_DIR)/emoji-data.txt \
+ $(UNICODE_SRC_EMOJI_DATA_DIR)/emoji-sequences.txt \
+ $(UNICODE_SRC_EMOJI_DATA_DIR)/emoji-test.txt \
+ $(UNICODE_SRC_EMOJI_DATA_DIR)/emoji-variation-sequences.txt \
+ $(UNICODE_SRC_EMOJI_DATA_DIR)/emoji-zwj-sequences.txt \
+ $(empty)
+
+update-unicode: $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES)
+CACHE_DIR = $(srcdir)/.downloaded-cache
UNICODE_DOWNLOAD = \
- $(BASERUBY) -C "$(srcdir)" tool/downloader.rb \
- -d $(UNICODE_DATA_DIR) \
+ $(BASERUBY) $(srcdir)/tool/downloader.rb \
+ --cache-dir=$(CACHE_DIR) \
+ --unicode-beta $(UNICODE_BETA) \
+ -d $(UNICODE_SRC_DATA_DIR) \
-p $(UNICODE_VERSION)/ucd \
-e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode
+UNICODE_AUXILIARY_DOWNLOAD = \
+ $(BASERUBY) $(srcdir)/tool/downloader.rb \
+ --cache-dir=$(CACHE_DIR) \
+ --unicode-beta $(UNICODE_BETA) \
+ -d $(UNICODE_SRC_DATA_DIR)/auxiliary \
+ -p $(UNICODE_VERSION)/ucd/auxiliary \
+ -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode
+UNICODE_EMOJI_DOWNLOAD = \
+ $(BASERUBY) $(srcdir)/tool/downloader.rb \
+ --cache-dir=$(CACHE_DIR) \
+ --unicode-beta $(UNICODE_BETA) \
+ -d $(UNICODE_SRC_EMOJI_DATA_DIR) \
+ -p emoji/$(UNICODE_EMOJI_VERSION) \
+ -e $(ALWAYS_UPDATE_UNICODE:yes=-a) unicode
-$(UNICODE_PROPERTY_FILES):
+$(UNICODE_PROPERTY_FILES): update-unicode-property-files
+update-unicode-property-files:
$(ECHO) Downloading Unicode $(UNICODE_VERSION) property files...
- $(Q) $(MAKEDIRS) "$(UNICODE_SRC_DATA_DIR)"
$(Q) $(UNICODE_DOWNLOAD) $(UNICODE_PROPERTY_FILES)
-
-$(UNICODE_FILES):
+ $(ECHO) Downloading Unicode $(UNICODE_VERSION) auxiliary files...
+ $(Q) $(MAKEDIRS) "$(UNICODE_SRC_DATA_DIR)/auxiliary"
+ $(Q) $(UNICODE_AUXILIARY_DOWNLOAD) $(UNICODE_AUXILIARY_FILES)
+ $(ECHO) Downloading Unicode emoji $(UNICODE_EMOJI_VERSION) files...
+ $(Q) $(MAKEDIRS) "$(UNICODE_SRC_EMOJI_DATA_DIR)"
+ $(Q) $(UNICODE_EMOJI_DOWNLOAD) $(UNICODE_EMOJI_FILES)
+
+$(UNICODE_FILES): update-unicode-files
+update-unicode-files:
$(ECHO) Downloading Unicode $(UNICODE_VERSION) data files...
$(Q) $(MAKEDIRS) "$(UNICODE_SRC_DATA_DIR)"
$(Q) $(UNICODE_DOWNLOAD) $(UNICODE_FILES)
@@ -1087,27 +1362,36 @@ $(UNICODE_FILES):
$(srcdir)/$(HAVE_BASERUBY:yes=lib/unicode_normalize/tables.rb): \
$(UNICODE_SRC_DATA_DIR)/.unicode-tables.time
-$(UNICODE_SRC_DATA_DIR)/$(ALWAYS_UPDATE_UNICODE:yes=.unicode-tables.time): $(UNICODE_FILES)
+$(UNICODE_SRC_DATA_DIR)/$(ALWAYS_UPDATE_UNICODE:yes=.unicode-tables.time): \
+ $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES)
+
+touch-unicode-files:
+ $(MAKEDIRS) $(UNICODE_SRC_DATA_DIR)
+ touch $(UNICODE_SRC_DATA_DIR)/.unicode-tables.time $(UNICODE_DATA_HEADERS)
$(UNICODE_SRC_DATA_DIR)/.unicode-tables.time: $(srcdir)/tool/generic_erb.rb \
- $(srcdir)/template/unicode_norm_gen.tmpl
- $(Q) $(ALWAYS_UPDATE_UNICODE:yes=exit &&) $(MAKE) $(MFLAGS) Q=$(Q) UNICODE_VERSION=$(UNICODE_VERSION) update-unicode
+ $(srcdir)/template/unicode_norm_gen.tmpl \
+ $(ALWAYS_UPDATE_UNICODE:yes=update-unicode)
+ $(Q) $(MAKE) $(@D)
$(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb \
-c -t$@ -o $(srcdir)/lib/unicode_normalize/tables.rb \
-I $(srcdir) \
$(srcdir)/template/unicode_norm_gen.tmpl \
$(UNICODE_DATA_DIR) lib/unicode_normalize
-# UPDATE_NAME2CTYPE= : toplevel
-# UPDATE_NAME2CTYPE=yes : sub-make to update name2ctype.h
-$(UNICODE_HDR_DIR)/$(UPDATE_NAME2CTYPE:yes=.ignore.)name2ctype.h:
- $(Q) $(MAKE) $(MFLAGS) Q=$(Q) UPDATE_NAME2CTYPE=yes UNICODE_VERSION=$(UNICODE_VERSION) $@
+$(UNICODE_SRC_DATA_DIR):
+ $(Q) $(exec) $(MAKEDIRS) $@ || exit && echo $(MAKE)
-$(UNICODE_HDR_DIR)/$(UPDATE_NAME2CTYPE:yes=name2ctype.h): \
+$(UNICODE_HDR_DIR)/$(ALWAYS_UPDATE_UNICODE:yes=name2ctype.h): \
+ $(srcdir)/tool/enc-unicode.rb \
$(UNICODE_SRC_DATA_DIR)/UnicodeData.txt \
$(UNICODE_PROPERTY_FILES)
+
+$(UNICODE_HDR_DIR)/name2ctype.h:
$(MAKEDIRS) $(@D)
- $(BOOTSTRAPRUBY) $(srcdir)/tool/enc-unicode.rb --header $(UNICODE_SRC_DATA_DIR) > $@
+ $(BOOTSTRAPRUBY) $(srcdir)/tool/enc-unicode.rb --header \
+ $(UNICODE_SRC_DATA_DIR) $(UNICODE_SRC_EMOJI_DATA_DIR) > $@.new
+ $(MV) $@.new $@
# the next non-comment line was:
# $(UNICODE_HDR_DIR)/casefold.h: $(srcdir)/enc/unicode/case-folding.rb \
@@ -1115,12 +1399,12 @@ $(UNICODE_HDR_DIR)/$(UPDATE_NAME2CTYPE:yes=name2ctype.h): \
unicode-up: $(UNICODE_DATA_HEADERS)
$(UNICODE_HDR_DIR)/$(ALWAYS_UPDATE_UNICODE:yes=casefold.h): \
+ $(srcdir)/enc/unicode/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: $(srcdir)/enc/unicode/case-folding.rb
- $(Q) $(ALWAYS_UPDATE_UNICODE:yes=exit &&) $(MAKE) $(MFLAGS) Q=$(Q) UNICODE_VERSION=$(UNICODE_VERSION) update-unicode
+$(UNICODE_HDR_DIR)/casefold.h:
$(MAKEDIRS) $(@D)
$(Q) $(BASERUBY) $(srcdir)/enc/unicode/case-folding.rb \
--output-file=$@ \
@@ -1153,12 +1437,14 @@ info-arch: PHONY
change: PHONY
$(BASERUBY) -C "$(srcdir)" ./tool/change_maker.rb $(CHANGES) > change.log
-exam: check test-rubyspec
+exam: check
-love: sudo-precheck up all test install check
+love: sudo-precheck up all test exam install
@echo love is all you need
-yes-test-all: sudo-precheck
+great: exam
+
+yes-test-all no-test-all: sudo-precheck
sudo-precheck: PHONY
@$(SUDO) echo > $(NULL)
@@ -1174,33 +1460,37 @@ 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" \
- " 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-all" \
- " exam: equals make check test-rubyspec" \
- " test: ruby core tests" \
- " test-all: all ruby tests [TESTOPTS=-j4 TESTS=\"<test files>\"]" \
- " test-rubyspec: run the Ruby spec suite" \
- " up: update local copy and autogenerated files" \
- " update-rubyspec: update local copy of the Ruby spec suite" \
- " benchmark: benchmark this ruby and COMPARE_RUBY." \
- " gcbench: gc benchmark [GCBENCH_ITEM=<item_name>]" \
- " gcbench-rdoc: gc benchmark with GCBENCH_ITEM=rdoc" \
- " install: install all ruby distributions" \
- " install-nodoc: install without rdoc" \
- " install-cross: install cross compiling stuff" \
- " clean: clean for tarball" \
- " distclean: clean for repository" \
- " change: make change log template" \
- " golf: 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-all test-spec" \
+ " exam: equals make check" \
+ " test: ruby core tests" \
+ " test-all: all ruby tests [TESTOPTS=-j4 TESTS=<test files>]" \
+ " test-spec: run the Ruby spec suite" \
+ " test-rubyspec: same as test-spec" \
+ " test-bundler: run the Bundler spec" \
+ " test-bundled-gems: run the test suite of bundled gems" \
+ " up: update local copy and autogenerated files" \
+ " benchmark: benchmark this ruby and COMPARE_RUBY." \
+ " gcbench: gc benchmark [GCBENCH_ITEM=<item_name>]" \
+ " gcbench-rdoc: gc benchmark with GCBENCH_ITEM=rdoc" \
+ " install: install all ruby distributions" \
+ " install-nodoc: install without rdoc" \
+ " install-cross: install cross compiling stuff" \
+ " clean: clean for tarball" \
+ " distclean: clean for repository" \
+ " change: make change log template" \
+ " golf: for golfers" \
+ " goruby: same as golf" \
"" \
"see DeveloperHowto for more detail: " \
" https://bugs.ruby-lang.org/projects/ruby/wiki/DeveloperHowto" \
@@ -1210,35 +1500,70 @@ help: PHONY
addr2line.$(OBJEXT): {$(VPATH)}addr2line.c
addr2line.$(OBJEXT): {$(VPATH)}addr2line.h
addr2line.$(OBJEXT): {$(VPATH)}config.h
+addr2line.$(OBJEXT): {$(VPATH)}defines.h
addr2line.$(OBJEXT): {$(VPATH)}missing.h
array.$(OBJEXT): $(hdrdir)/ruby/ruby.h
array.$(OBJEXT): $(top_srcdir)/include/ruby.h
array.$(OBJEXT): {$(VPATH)}array.c
array.$(OBJEXT): {$(VPATH)}config.h
+array.$(OBJEXT): {$(VPATH)}debug_counter.h
array.$(OBJEXT): {$(VPATH)}defines.h
array.$(OBJEXT): {$(VPATH)}encoding.h
+array.$(OBJEXT): {$(VPATH)}gc.h
array.$(OBJEXT): {$(VPATH)}id.h
array.$(OBJEXT): {$(VPATH)}intern.h
array.$(OBJEXT): {$(VPATH)}internal.h
array.$(OBJEXT): {$(VPATH)}io.h
array.$(OBJEXT): {$(VPATH)}missing.h
+array.$(OBJEXT): {$(VPATH)}onigmo.h
array.$(OBJEXT): {$(VPATH)}oniguruma.h
array.$(OBJEXT): {$(VPATH)}probes.dmyh
array.$(OBJEXT): {$(VPATH)}probes.h
array.$(OBJEXT): {$(VPATH)}ruby_assert.h
array.$(OBJEXT): {$(VPATH)}st.h
array.$(OBJEXT): {$(VPATH)}subst.h
+array.$(OBJEXT): {$(VPATH)}transient_heap.h
array.$(OBJEXT): {$(VPATH)}util.h
+ast.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+ast.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+ast.$(OBJEXT): $(CCAN_DIR)/list/list.h
+ast.$(OBJEXT): $(CCAN_DIR)/str/str.h
+ast.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+ast.$(OBJEXT): $(top_srcdir)/include/ruby.h
+ast.$(OBJEXT): {$(VPATH)}ast.c
+ast.$(OBJEXT): {$(VPATH)}config.h
+ast.$(OBJEXT): {$(VPATH)}defines.h
+ast.$(OBJEXT): {$(VPATH)}encoding.h
+ast.$(OBJEXT): {$(VPATH)}id.h
+ast.$(OBJEXT): {$(VPATH)}intern.h
+ast.$(OBJEXT): {$(VPATH)}internal.h
+ast.$(OBJEXT): {$(VPATH)}iseq.h
+ast.$(OBJEXT): {$(VPATH)}method.h
+ast.$(OBJEXT): {$(VPATH)}missing.h
+ast.$(OBJEXT): {$(VPATH)}node.h
+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)}st.h
+ast.$(OBJEXT): {$(VPATH)}subst.h
+ast.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+ast.$(OBJEXT): {$(VPATH)}thread_native.h
+ast.$(OBJEXT): {$(VPATH)}util.h
+ast.$(OBJEXT): {$(VPATH)}vm_core.h
+ast.$(OBJEXT): {$(VPATH)}vm_opts.h
bignum.$(OBJEXT): $(hdrdir)/ruby/ruby.h
bignum.$(OBJEXT): $(top_srcdir)/include/ruby.h
bignum.$(OBJEXT): {$(VPATH)}bignum.c
bignum.$(OBJEXT): {$(VPATH)}config.h
bignum.$(OBJEXT): {$(VPATH)}defines.h
bignum.$(OBJEXT): {$(VPATH)}encoding.h
+bignum.$(OBJEXT): {$(VPATH)}id.h
bignum.$(OBJEXT): {$(VPATH)}intern.h
bignum.$(OBJEXT): {$(VPATH)}internal.h
bignum.$(OBJEXT): {$(VPATH)}io.h
bignum.$(OBJEXT): {$(VPATH)}missing.h
+bignum.$(OBJEXT): {$(VPATH)}onigmo.h
bignum.$(OBJEXT): {$(VPATH)}oniguruma.h
bignum.$(OBJEXT): {$(VPATH)}ruby_assert.h
bignum.$(OBJEXT): {$(VPATH)}st.h
@@ -1264,6 +1589,7 @@ class.$(OBJEXT): {$(VPATH)}io.h
class.$(OBJEXT): {$(VPATH)}method.h
class.$(OBJEXT): {$(VPATH)}missing.h
class.$(OBJEXT): {$(VPATH)}node.h
+class.$(OBJEXT): {$(VPATH)}onigmo.h
class.$(OBJEXT): {$(VPATH)}oniguruma.h
class.$(OBJEXT): {$(VPATH)}ruby_assert.h
class.$(OBJEXT): {$(VPATH)}ruby_atomic.h
@@ -1297,6 +1623,7 @@ compile.$(OBJEXT): {$(VPATH)}encoding.h
compile.$(OBJEXT): {$(VPATH)}gc.h
compile.$(OBJEXT): {$(VPATH)}id.h
compile.$(OBJEXT): {$(VPATH)}id_table.h
+compile.$(OBJEXT): {$(VPATH)}insns.def
compile.$(OBJEXT): {$(VPATH)}insns.inc
compile.$(OBJEXT): {$(VPATH)}insns_info.inc
compile.$(OBJEXT): {$(VPATH)}intern.h
@@ -1306,6 +1633,7 @@ compile.$(OBJEXT): {$(VPATH)}iseq.h
compile.$(OBJEXT): {$(VPATH)}method.h
compile.$(OBJEXT): {$(VPATH)}missing.h
compile.$(OBJEXT): {$(VPATH)}node.h
+compile.$(OBJEXT): {$(VPATH)}onigmo.h
compile.$(OBJEXT): {$(VPATH)}oniguruma.h
compile.$(OBJEXT): {$(VPATH)}opt_sc.inc
compile.$(OBJEXT): {$(VPATH)}optinsn.inc
@@ -1318,6 +1646,7 @@ compile.$(OBJEXT): {$(VPATH)}st.h
compile.$(OBJEXT): {$(VPATH)}subst.h
compile.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
compile.$(OBJEXT): {$(VPATH)}thread_native.h
+compile.$(OBJEXT): {$(VPATH)}util.h
compile.$(OBJEXT): {$(VPATH)}vm_core.h
compile.$(OBJEXT): {$(VPATH)}vm_debug.h
compile.$(OBJEXT): {$(VPATH)}vm_opts.h
@@ -1327,10 +1656,12 @@ complex.$(OBJEXT): {$(VPATH)}complex.c
complex.$(OBJEXT): {$(VPATH)}config.h
complex.$(OBJEXT): {$(VPATH)}defines.h
complex.$(OBJEXT): {$(VPATH)}encoding.h
+complex.$(OBJEXT): {$(VPATH)}id.h
complex.$(OBJEXT): {$(VPATH)}intern.h
complex.$(OBJEXT): {$(VPATH)}internal.h
complex.$(OBJEXT): {$(VPATH)}io.h
complex.$(OBJEXT): {$(VPATH)}missing.h
+complex.$(OBJEXT): {$(VPATH)}onigmo.h
complex.$(OBJEXT): {$(VPATH)}oniguruma.h
complex.$(OBJEXT): {$(VPATH)}ruby_assert.h
complex.$(OBJEXT): {$(VPATH)}st.h
@@ -1341,6 +1672,7 @@ cont.$(OBJEXT): $(CCAN_DIR)/list/list.h
cont.$(OBJEXT): $(CCAN_DIR)/str/str.h
cont.$(OBJEXT): $(hdrdir)/ruby/ruby.h
cont.$(OBJEXT): $(top_srcdir)/include/ruby.h
+cont.$(OBJEXT): {$(VPATH)}$(COROUTINE_H)
cont.$(OBJEXT): {$(VPATH)}config.h
cont.$(OBJEXT): {$(VPATH)}cont.c
cont.$(OBJEXT): {$(VPATH)}defines.h
@@ -1353,7 +1685,9 @@ cont.$(OBJEXT): {$(VPATH)}internal.h
cont.$(OBJEXT): {$(VPATH)}io.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)}ruby_assert.h
cont.$(OBJEXT): {$(VPATH)}ruby_atomic.h
@@ -1375,6 +1709,7 @@ debug.$(OBJEXT): {$(VPATH)}debug.c
debug.$(OBJEXT): {$(VPATH)}defines.h
debug.$(OBJEXT): {$(VPATH)}encoding.h
debug.$(OBJEXT): {$(VPATH)}eval_intern.h
+debug.$(OBJEXT): {$(VPATH)}gc.h
debug.$(OBJEXT): {$(VPATH)}id.h
debug.$(OBJEXT): {$(VPATH)}intern.h
debug.$(OBJEXT): {$(VPATH)}internal.h
@@ -1382,17 +1717,34 @@ debug.$(OBJEXT): {$(VPATH)}io.h
debug.$(OBJEXT): {$(VPATH)}method.h
debug.$(OBJEXT): {$(VPATH)}missing.h
debug.$(OBJEXT): {$(VPATH)}node.h
+debug.$(OBJEXT): {$(VPATH)}onigmo.h
debug.$(OBJEXT): {$(VPATH)}oniguruma.h
debug.$(OBJEXT): {$(VPATH)}ruby_assert.h
debug.$(OBJEXT): {$(VPATH)}ruby_atomic.h
debug.$(OBJEXT): {$(VPATH)}st.h
debug.$(OBJEXT): {$(VPATH)}subst.h
+debug.$(OBJEXT): {$(VPATH)}symbol.h
debug.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
debug.$(OBJEXT): {$(VPATH)}thread_native.h
debug.$(OBJEXT): {$(VPATH)}util.h
debug.$(OBJEXT): {$(VPATH)}vm_core.h
debug.$(OBJEXT): {$(VPATH)}vm_debug.h
debug.$(OBJEXT): {$(VPATH)}vm_opts.h
+debug_counter.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+debug_counter.$(OBJEXT): $(top_srcdir)/include/ruby.h
+debug_counter.$(OBJEXT): {$(VPATH)}config.h
+debug_counter.$(OBJEXT): {$(VPATH)}debug_counter.c
+debug_counter.$(OBJEXT): {$(VPATH)}debug_counter.h
+debug_counter.$(OBJEXT): {$(VPATH)}defines.h
+debug_counter.$(OBJEXT): {$(VPATH)}encoding.h
+debug_counter.$(OBJEXT): {$(VPATH)}intern.h
+debug_counter.$(OBJEXT): {$(VPATH)}internal.h
+debug_counter.$(OBJEXT): {$(VPATH)}io.h
+debug_counter.$(OBJEXT): {$(VPATH)}missing.h
+debug_counter.$(OBJEXT): {$(VPATH)}onigmo.h
+debug_counter.$(OBJEXT): {$(VPATH)}oniguruma.h
+debug_counter.$(OBJEXT): {$(VPATH)}st.h
+debug_counter.$(OBJEXT): {$(VPATH)}subst.h
dir.$(OBJEXT): $(hdrdir)/ruby/ruby.h
dir.$(OBJEXT): $(top_srcdir)/include/ruby.h
dir.$(OBJEXT): {$(VPATH)}config.h
@@ -1400,20 +1752,25 @@ dir.$(OBJEXT): {$(VPATH)}defines.h
dir.$(OBJEXT): {$(VPATH)}dir.c
dir.$(OBJEXT): {$(VPATH)}encindex.h
dir.$(OBJEXT): {$(VPATH)}encoding.h
+dir.$(OBJEXT): {$(VPATH)}id.h
dir.$(OBJEXT): {$(VPATH)}intern.h
dir.$(OBJEXT): {$(VPATH)}internal.h
dir.$(OBJEXT): {$(VPATH)}io.h
dir.$(OBJEXT): {$(VPATH)}missing.h
+dir.$(OBJEXT): {$(VPATH)}onigmo.h
dir.$(OBJEXT): {$(VPATH)}oniguruma.h
dir.$(OBJEXT): {$(VPATH)}st.h
dir.$(OBJEXT): {$(VPATH)}subst.h
+dir.$(OBJEXT): {$(VPATH)}thread.h
dir.$(OBJEXT): {$(VPATH)}util.h
dln.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+dln.$(OBJEXT): $(top_srcdir)/include/ruby.h
dln.$(OBJEXT): {$(VPATH)}config.h
dln.$(OBJEXT): {$(VPATH)}defines.h
dln.$(OBJEXT): {$(VPATH)}dln.c
dln.$(OBJEXT): {$(VPATH)}dln.h
dln.$(OBJEXT): {$(VPATH)}intern.h
+dln.$(OBJEXT): {$(VPATH)}internal.h
dln.$(OBJEXT): {$(VPATH)}missing.h
dln.$(OBJEXT): {$(VPATH)}st.h
dln.$(OBJEXT): {$(VPATH)}subst.h
@@ -1452,14 +1809,15 @@ enc/trans/newline.$(OBJEXT): {$(VPATH)}missing.h
enc/trans/newline.$(OBJEXT): {$(VPATH)}st.h
enc/trans/newline.$(OBJEXT): {$(VPATH)}subst.h
enc/trans/newline.$(OBJEXT): {$(VPATH)}transcode_data.h
-enc/unicode.$(OBJEXT): $(hdrdir)/ruby/ruby.h
enc/unicode.$(OBJEXT): $(UNICODE_HDR_DIR)/casefold.h
enc/unicode.$(OBJEXT): $(UNICODE_HDR_DIR)/name2ctype.h
+enc/unicode.$(OBJEXT): $(hdrdir)/ruby/ruby.h
enc/unicode.$(OBJEXT): {$(VPATH)}config.h
enc/unicode.$(OBJEXT): {$(VPATH)}defines.h
enc/unicode.$(OBJEXT): {$(VPATH)}enc/unicode.c
enc/unicode.$(OBJEXT): {$(VPATH)}intern.h
enc/unicode.$(OBJEXT): {$(VPATH)}missing.h
+enc/unicode.$(OBJEXT): {$(VPATH)}onigmo.h
enc/unicode.$(OBJEXT): {$(VPATH)}oniguruma.h
enc/unicode.$(OBJEXT): {$(VPATH)}regenc.h
enc/unicode.$(OBJEXT): {$(VPATH)}regint.h
@@ -1490,6 +1848,7 @@ encoding.$(OBJEXT): {$(VPATH)}intern.h
encoding.$(OBJEXT): {$(VPATH)}internal.h
encoding.$(OBJEXT): {$(VPATH)}io.h
encoding.$(OBJEXT): {$(VPATH)}missing.h
+encoding.$(OBJEXT): {$(VPATH)}onigmo.h
encoding.$(OBJEXT): {$(VPATH)}oniguruma.h
encoding.$(OBJEXT): {$(VPATH)}regenc.h
encoding.$(OBJEXT): {$(VPATH)}ruby_assert.h
@@ -1507,9 +1866,12 @@ enum.$(OBJEXT): {$(VPATH)}intern.h
enum.$(OBJEXT): {$(VPATH)}internal.h
enum.$(OBJEXT): {$(VPATH)}io.h
enum.$(OBJEXT): {$(VPATH)}missing.h
+enum.$(OBJEXT): {$(VPATH)}onigmo.h
enum.$(OBJEXT): {$(VPATH)}oniguruma.h
enum.$(OBJEXT): {$(VPATH)}st.h
enum.$(OBJEXT): {$(VPATH)}subst.h
+enum.$(OBJEXT): {$(VPATH)}symbol.h
+enum.$(OBJEXT): {$(VPATH)}transient_heap.h
enum.$(OBJEXT): {$(VPATH)}util.h
enumerator.$(OBJEXT): $(hdrdir)/ruby/ruby.h
enumerator.$(OBJEXT): $(top_srcdir)/include/ruby.h
@@ -1517,10 +1879,12 @@ enumerator.$(OBJEXT): {$(VPATH)}config.h
enumerator.$(OBJEXT): {$(VPATH)}defines.h
enumerator.$(OBJEXT): {$(VPATH)}encoding.h
enumerator.$(OBJEXT): {$(VPATH)}enumerator.c
+enumerator.$(OBJEXT): {$(VPATH)}id.h
enumerator.$(OBJEXT): {$(VPATH)}intern.h
enumerator.$(OBJEXT): {$(VPATH)}internal.h
enumerator.$(OBJEXT): {$(VPATH)}io.h
enumerator.$(OBJEXT): {$(VPATH)}missing.h
+enumerator.$(OBJEXT): {$(VPATH)}onigmo.h
enumerator.$(OBJEXT): {$(VPATH)}oniguruma.h
enumerator.$(OBJEXT): {$(VPATH)}st.h
enumerator.$(OBJEXT): {$(VPATH)}subst.h
@@ -1542,6 +1906,7 @@ error.$(OBJEXT): {$(VPATH)}known_errors.inc
error.$(OBJEXT): {$(VPATH)}method.h
error.$(OBJEXT): {$(VPATH)}missing.h
error.$(OBJEXT): {$(VPATH)}node.h
+error.$(OBJEXT): {$(VPATH)}onigmo.h
error.$(OBJEXT): {$(VPATH)}oniguruma.h
error.$(OBJEXT): {$(VPATH)}ruby_assert.h
error.$(OBJEXT): {$(VPATH)}ruby_atomic.h
@@ -1573,7 +1938,9 @@ eval.$(OBJEXT): {$(VPATH)}io.h
eval.$(OBJEXT): {$(VPATH)}iseq.h
eval.$(OBJEXT): {$(VPATH)}method.h
eval.$(OBJEXT): {$(VPATH)}missing.h
+eval.$(OBJEXT): {$(VPATH)}mjit.h
eval.$(OBJEXT): {$(VPATH)}node.h
+eval.$(OBJEXT): {$(VPATH)}onigmo.h
eval.$(OBJEXT): {$(VPATH)}oniguruma.h
eval.$(OBJEXT): {$(VPATH)}probes.dmyh
eval.$(OBJEXT): {$(VPATH)}probes.h
@@ -1599,13 +1966,16 @@ file.$(OBJEXT): {$(VPATH)}dln.h
file.$(OBJEXT): {$(VPATH)}encindex.h
file.$(OBJEXT): {$(VPATH)}encoding.h
file.$(OBJEXT): {$(VPATH)}file.c
+file.$(OBJEXT): {$(VPATH)}id.h
file.$(OBJEXT): {$(VPATH)}intern.h
file.$(OBJEXT): {$(VPATH)}internal.h
file.$(OBJEXT): {$(VPATH)}io.h
file.$(OBJEXT): {$(VPATH)}missing.h
+file.$(OBJEXT): {$(VPATH)}onigmo.h
file.$(OBJEXT): {$(VPATH)}oniguruma.h
file.$(OBJEXT): {$(VPATH)}st.h
file.$(OBJEXT): {$(VPATH)}subst.h
+file.$(OBJEXT): {$(VPATH)}thread.h
file.$(OBJEXT): {$(VPATH)}util.h
gc.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
gc.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
@@ -1616,6 +1986,7 @@ gc.$(OBJEXT): $(top_srcdir)/include/ruby.h
gc.$(OBJEXT): {$(VPATH)}config.h
gc.$(OBJEXT): {$(VPATH)}constant.h
gc.$(OBJEXT): {$(VPATH)}debug.h
+gc.$(OBJEXT): {$(VPATH)}debug_counter.h
gc.$(OBJEXT): {$(VPATH)}defines.h
gc.$(OBJEXT): {$(VPATH)}encoding.h
gc.$(OBJEXT): {$(VPATH)}eval_intern.h
@@ -1628,7 +1999,9 @@ gc.$(OBJEXT): {$(VPATH)}internal.h
gc.$(OBJEXT): {$(VPATH)}io.h
gc.$(OBJEXT): {$(VPATH)}method.h
gc.$(OBJEXT): {$(VPATH)}missing.h
+gc.$(OBJEXT): {$(VPATH)}mjit.h
gc.$(OBJEXT): {$(VPATH)}node.h
+gc.$(OBJEXT): {$(VPATH)}onigmo.h
gc.$(OBJEXT): {$(VPATH)}oniguruma.h
gc.$(OBJEXT): {$(VPATH)}probes.dmyh
gc.$(OBJEXT): {$(VPATH)}probes.h
@@ -1643,6 +2016,7 @@ gc.$(OBJEXT): {$(VPATH)}subst.h
gc.$(OBJEXT): {$(VPATH)}thread.h
gc.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
gc.$(OBJEXT): {$(VPATH)}thread_native.h
+gc.$(OBJEXT): {$(VPATH)}transient_heap.h
gc.$(OBJEXT): {$(VPATH)}util.h
gc.$(OBJEXT): {$(VPATH)}vm_core.h
gc.$(OBJEXT): {$(VPATH)}vm_debug.h
@@ -1657,6 +2031,7 @@ golf_prelude.$(OBJEXT): {$(VPATH)}config.h
golf_prelude.$(OBJEXT): {$(VPATH)}defines.h
golf_prelude.$(OBJEXT): {$(VPATH)}encoding.h
golf_prelude.$(OBJEXT): {$(VPATH)}golf_prelude.c
+golf_prelude.$(OBJEXT): {$(VPATH)}golf_prelude.rb
golf_prelude.$(OBJEXT): {$(VPATH)}id.h
golf_prelude.$(OBJEXT): {$(VPATH)}intern.h
golf_prelude.$(OBJEXT): {$(VPATH)}internal.h
@@ -1665,6 +2040,7 @@ golf_prelude.$(OBJEXT): {$(VPATH)}iseq.h
golf_prelude.$(OBJEXT): {$(VPATH)}method.h
golf_prelude.$(OBJEXT): {$(VPATH)}missing.h
golf_prelude.$(OBJEXT): {$(VPATH)}node.h
+golf_prelude.$(OBJEXT): {$(VPATH)}onigmo.h
golf_prelude.$(OBJEXT): {$(VPATH)}oniguruma.h
golf_prelude.$(OBJEXT): {$(VPATH)}ruby_assert.h
golf_prelude.$(OBJEXT): {$(VPATH)}ruby_atomic.h
@@ -1691,20 +2067,25 @@ goruby.$(OBJEXT): {$(VPATH)}vm_debug.h
hash.$(OBJEXT): $(hdrdir)/ruby/ruby.h
hash.$(OBJEXT): $(top_srcdir)/include/ruby.h
hash.$(OBJEXT): {$(VPATH)}config.h
+hash.$(OBJEXT): {$(VPATH)}debug_counter.h
hash.$(OBJEXT): {$(VPATH)}defines.h
hash.$(OBJEXT): {$(VPATH)}encoding.h
+hash.$(OBJEXT): {$(VPATH)}gc.h
hash.$(OBJEXT): {$(VPATH)}hash.c
hash.$(OBJEXT): {$(VPATH)}id.h
hash.$(OBJEXT): {$(VPATH)}intern.h
hash.$(OBJEXT): {$(VPATH)}internal.h
hash.$(OBJEXT): {$(VPATH)}io.h
hash.$(OBJEXT): {$(VPATH)}missing.h
+hash.$(OBJEXT): {$(VPATH)}onigmo.h
hash.$(OBJEXT): {$(VPATH)}oniguruma.h
hash.$(OBJEXT): {$(VPATH)}probes.dmyh
hash.$(OBJEXT): {$(VPATH)}probes.h
+hash.$(OBJEXT): {$(VPATH)}ruby_assert.h
hash.$(OBJEXT): {$(VPATH)}st.h
hash.$(OBJEXT): {$(VPATH)}subst.h
hash.$(OBJEXT): {$(VPATH)}symbol.h
+hash.$(OBJEXT): {$(VPATH)}transient_heap.h
hash.$(OBJEXT): {$(VPATH)}util.h
inits.$(OBJEXT): $(hdrdir)/ruby/ruby.h
inits.$(OBJEXT): $(top_srcdir)/include/ruby.h
@@ -1716,9 +2097,14 @@ inits.$(OBJEXT): {$(VPATH)}intern.h
inits.$(OBJEXT): {$(VPATH)}internal.h
inits.$(OBJEXT): {$(VPATH)}io.h
inits.$(OBJEXT): {$(VPATH)}missing.h
+inits.$(OBJEXT): {$(VPATH)}onigmo.h
inits.$(OBJEXT): {$(VPATH)}oniguruma.h
inits.$(OBJEXT): {$(VPATH)}st.h
inits.$(OBJEXT): {$(VPATH)}subst.h
+io.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+io.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+io.$(OBJEXT): $(CCAN_DIR)/list/list.h
+io.$(OBJEXT): $(CCAN_DIR)/str/str.h
io.$(OBJEXT): $(hdrdir)/ruby/ruby.h
io.$(OBJEXT): $(top_srcdir)/include/ruby.h
io.$(OBJEXT): {$(VPATH)}config.h
@@ -1731,13 +2117,21 @@ io.$(OBJEXT): {$(VPATH)}intern.h
io.$(OBJEXT): {$(VPATH)}internal.h
io.$(OBJEXT): {$(VPATH)}io.c
io.$(OBJEXT): {$(VPATH)}io.h
+io.$(OBJEXT): {$(VPATH)}method.h
io.$(OBJEXT): {$(VPATH)}missing.h
+io.$(OBJEXT): {$(VPATH)}node.h
+io.$(OBJEXT): {$(VPATH)}onigmo.h
io.$(OBJEXT): {$(VPATH)}oniguruma.h
+io.$(OBJEXT): {$(VPATH)}ruby_assert.h
io.$(OBJEXT): {$(VPATH)}ruby_atomic.h
io.$(OBJEXT): {$(VPATH)}st.h
io.$(OBJEXT): {$(VPATH)}subst.h
io.$(OBJEXT): {$(VPATH)}thread.h
+io.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+io.$(OBJEXT): {$(VPATH)}thread_native.h
io.$(OBJEXT): {$(VPATH)}util.h
+io.$(OBJEXT): {$(VPATH)}vm_core.h
+io.$(OBJEXT): {$(VPATH)}vm_opts.h
iseq.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
iseq.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
iseq.$(OBJEXT): $(CCAN_DIR)/list/list.h
@@ -1751,6 +2145,7 @@ iseq.$(OBJEXT): {$(VPATH)}eval_intern.h
iseq.$(OBJEXT): {$(VPATH)}gc.h
iseq.$(OBJEXT): {$(VPATH)}id.h
iseq.$(OBJEXT): {$(VPATH)}id_table.h
+iseq.$(OBJEXT): {$(VPATH)}insns.def
iseq.$(OBJEXT): {$(VPATH)}insns.inc
iseq.$(OBJEXT): {$(VPATH)}insns_info.inc
iseq.$(OBJEXT): {$(VPATH)}intern.h
@@ -1760,8 +2155,10 @@ iseq.$(OBJEXT): {$(VPATH)}iseq.c
iseq.$(OBJEXT): {$(VPATH)}iseq.h
iseq.$(OBJEXT): {$(VPATH)}method.h
iseq.$(OBJEXT): {$(VPATH)}missing.h
+iseq.$(OBJEXT): {$(VPATH)}mjit.h
iseq.$(OBJEXT): {$(VPATH)}node.h
iseq.$(OBJEXT): {$(VPATH)}node_name.inc
+iseq.$(OBJEXT): {$(VPATH)}onigmo.h
iseq.$(OBJEXT): {$(VPATH)}oniguruma.h
iseq.$(OBJEXT): {$(VPATH)}ruby_assert.h
iseq.$(OBJEXT): {$(VPATH)}ruby_atomic.h
@@ -1792,6 +2189,7 @@ load.$(OBJEXT): {$(VPATH)}load.c
load.$(OBJEXT): {$(VPATH)}method.h
load.$(OBJEXT): {$(VPATH)}missing.h
load.$(OBJEXT): {$(VPATH)}node.h
+load.$(OBJEXT): {$(VPATH)}onigmo.h
load.$(OBJEXT): {$(VPATH)}oniguruma.h
load.$(OBJEXT): {$(VPATH)}probes.dmyh
load.$(OBJEXT): {$(VPATH)}probes.h
@@ -1827,6 +2225,7 @@ localeinit.$(OBJEXT): {$(VPATH)}internal.h
localeinit.$(OBJEXT): {$(VPATH)}io.h
localeinit.$(OBJEXT): {$(VPATH)}localeinit.c
localeinit.$(OBJEXT): {$(VPATH)}missing.h
+localeinit.$(OBJEXT): {$(VPATH)}onigmo.h
localeinit.$(OBJEXT): {$(VPATH)}oniguruma.h
localeinit.$(OBJEXT): {$(VPATH)}st.h
localeinit.$(OBJEXT): {$(VPATH)}subst.h
@@ -1854,6 +2253,7 @@ marshal.$(OBJEXT): {$(VPATH)}internal.h
marshal.$(OBJEXT): {$(VPATH)}io.h
marshal.$(OBJEXT): {$(VPATH)}marshal.c
marshal.$(OBJEXT): {$(VPATH)}missing.h
+marshal.$(OBJEXT): {$(VPATH)}onigmo.h
marshal.$(OBJEXT): {$(VPATH)}oniguruma.h
marshal.$(OBJEXT): {$(VPATH)}st.h
marshal.$(OBJEXT): {$(VPATH)}subst.h
@@ -1868,6 +2268,7 @@ math.$(OBJEXT): {$(VPATH)}internal.h
math.$(OBJEXT): {$(VPATH)}io.h
math.$(OBJEXT): {$(VPATH)}math.c
math.$(OBJEXT): {$(VPATH)}missing.h
+math.$(OBJEXT): {$(VPATH)}onigmo.h
math.$(OBJEXT): {$(VPATH)}oniguruma.h
math.$(OBJEXT): {$(VPATH)}st.h
math.$(OBJEXT): {$(VPATH)}subst.h
@@ -1878,11 +2279,76 @@ miniinit.$(OBJEXT): {$(VPATH)}encoding.h
miniinit.$(OBJEXT): {$(VPATH)}intern.h
miniinit.$(OBJEXT): {$(VPATH)}miniinit.c
miniinit.$(OBJEXT): {$(VPATH)}missing.h
+miniinit.$(OBJEXT): {$(VPATH)}onigmo.h
miniinit.$(OBJEXT): {$(VPATH)}oniguruma.h
miniinit.$(OBJEXT): {$(VPATH)}st.h
miniinit.$(OBJEXT): {$(VPATH)}subst.h
miniprelude.$(OBJEXT): {$(VPATH)}iseq.h
miniprelude.$(OBJEXT): {$(VPATH)}miniprelude.c
+mjit.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+mjit.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+mjit.$(OBJEXT): $(CCAN_DIR)/list/list.h
+mjit.$(OBJEXT): $(CCAN_DIR)/str/str.h
+mjit.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+mjit.$(OBJEXT): $(top_srcdir)/include/ruby.h
+mjit.$(OBJEXT): {$(VPATH)}config.h
+mjit.$(OBJEXT): {$(VPATH)}constant.h
+mjit.$(OBJEXT): {$(VPATH)}debug.h
+mjit.$(OBJEXT): {$(VPATH)}defines.h
+mjit.$(OBJEXT): {$(VPATH)}dln.h
+mjit.$(OBJEXT): {$(VPATH)}gc.h
+mjit.$(OBJEXT): {$(VPATH)}id.h
+mjit.$(OBJEXT): {$(VPATH)}id_table.h
+mjit.$(OBJEXT): {$(VPATH)}intern.h
+mjit.$(OBJEXT): {$(VPATH)}internal.h
+mjit.$(OBJEXT): {$(VPATH)}method.h
+mjit.$(OBJEXT): {$(VPATH)}missing.h
+mjit.$(OBJEXT): {$(VPATH)}mjit.c
+mjit.$(OBJEXT): {$(VPATH)}mjit.h
+mjit.$(OBJEXT): {$(VPATH)}mjit_config.h
+mjit.$(OBJEXT): {$(VPATH)}mjit_worker.c
+mjit.$(OBJEXT): {$(VPATH)}node.h
+mjit.$(OBJEXT): {$(VPATH)}ruby_assert.h
+mjit.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+mjit.$(OBJEXT): {$(VPATH)}st.h
+mjit.$(OBJEXT): {$(VPATH)}subst.h
+mjit.$(OBJEXT): {$(VPATH)}thread.h
+mjit.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+mjit.$(OBJEXT): {$(VPATH)}thread_native.h
+mjit.$(OBJEXT): {$(VPATH)}util.h
+mjit.$(OBJEXT): {$(VPATH)}vm_core.h
+mjit.$(OBJEXT): {$(VPATH)}vm_opts.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/ruby.h
+mjit_compile.$(OBJEXT): $(top_srcdir)/include/ruby.h
+mjit_compile.$(OBJEXT): {$(VPATH)}config.h
+mjit_compile.$(OBJEXT): {$(VPATH)}defines.h
+mjit_compile.$(OBJEXT): {$(VPATH)}id.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)}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)}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_core.h
+mjit_compile.$(OBJEXT): {$(VPATH)}vm_exec.h
+mjit_compile.$(OBJEXT): {$(VPATH)}vm_insnhelper.h
+mjit_compile.$(OBJEXT): {$(VPATH)}vm_opts.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
@@ -1900,6 +2366,7 @@ node.$(OBJEXT): {$(VPATH)}method.h
node.$(OBJEXT): {$(VPATH)}missing.h
node.$(OBJEXT): {$(VPATH)}node.c
node.$(OBJEXT): {$(VPATH)}node.h
+node.$(OBJEXT): {$(VPATH)}onigmo.h
node.$(OBJEXT): {$(VPATH)}oniguruma.h
node.$(OBJEXT): {$(VPATH)}ruby_assert.h
node.$(OBJEXT): {$(VPATH)}ruby_atomic.h
@@ -1921,6 +2388,7 @@ numeric.$(OBJEXT): {$(VPATH)}internal.h
numeric.$(OBJEXT): {$(VPATH)}io.h
numeric.$(OBJEXT): {$(VPATH)}missing.h
numeric.$(OBJEXT): {$(VPATH)}numeric.c
+numeric.$(OBJEXT): {$(VPATH)}onigmo.h
numeric.$(OBJEXT): {$(VPATH)}oniguruma.h
numeric.$(OBJEXT): {$(VPATH)}st.h
numeric.$(OBJEXT): {$(VPATH)}subst.h
@@ -1937,6 +2405,7 @@ object.$(OBJEXT): {$(VPATH)}internal.h
object.$(OBJEXT): {$(VPATH)}io.h
object.$(OBJEXT): {$(VPATH)}missing.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
@@ -1952,6 +2421,7 @@ pack.$(OBJEXT): {$(VPATH)}intern.h
pack.$(OBJEXT): {$(VPATH)}internal.h
pack.$(OBJEXT): {$(VPATH)}io.h
pack.$(OBJEXT): {$(VPATH)}missing.h
+pack.$(OBJEXT): {$(VPATH)}onigmo.h
pack.$(OBJEXT): {$(VPATH)}oniguruma.h
pack.$(OBJEXT): {$(VPATH)}pack.c
pack.$(OBJEXT): {$(VPATH)}st.h
@@ -1969,6 +2439,7 @@ parse.$(OBJEXT): {$(VPATH)}io.h
parse.$(OBJEXT): {$(VPATH)}lex.c
parse.$(OBJEXT): {$(VPATH)}missing.h
parse.$(OBJEXT): {$(VPATH)}node.h
+parse.$(OBJEXT): {$(VPATH)}onigmo.h
parse.$(OBJEXT): {$(VPATH)}oniguruma.h
parse.$(OBJEXT): {$(VPATH)}parse.c
parse.$(OBJEXT): {$(VPATH)}parse.h
@@ -1990,6 +2461,7 @@ prelude.$(OBJEXT): $(top_srcdir)/include/ruby.h
prelude.$(OBJEXT): {$(VPATH)}config.h
prelude.$(OBJEXT): {$(VPATH)}defines.h
prelude.$(OBJEXT): {$(VPATH)}encoding.h
+prelude.$(OBJEXT): {$(VPATH)}gem_prelude.rb
prelude.$(OBJEXT): {$(VPATH)}id.h
prelude.$(OBJEXT): {$(VPATH)}intern.h
prelude.$(OBJEXT): {$(VPATH)}internal.h
@@ -1998,8 +2470,10 @@ prelude.$(OBJEXT): {$(VPATH)}iseq.h
prelude.$(OBJEXT): {$(VPATH)}method.h
prelude.$(OBJEXT): {$(VPATH)}missing.h
prelude.$(OBJEXT): {$(VPATH)}node.h
+prelude.$(OBJEXT): {$(VPATH)}onigmo.h
prelude.$(OBJEXT): {$(VPATH)}oniguruma.h
prelude.$(OBJEXT): {$(VPATH)}prelude.c
+prelude.$(OBJEXT): {$(VPATH)}prelude.rb
prelude.$(OBJEXT): {$(VPATH)}ruby_assert.h
prelude.$(OBJEXT): {$(VPATH)}ruby_atomic.h
prelude.$(OBJEXT): {$(VPATH)}st.h
@@ -2028,6 +2502,7 @@ proc.$(OBJEXT): {$(VPATH)}iseq.h
proc.$(OBJEXT): {$(VPATH)}method.h
proc.$(OBJEXT): {$(VPATH)}missing.h
proc.$(OBJEXT): {$(VPATH)}node.h
+proc.$(OBJEXT): {$(VPATH)}onigmo.h
proc.$(OBJEXT): {$(VPATH)}oniguruma.h
proc.$(OBJEXT): {$(VPATH)}proc.c
proc.$(OBJEXT): {$(VPATH)}ruby_assert.h
@@ -2049,6 +2524,7 @@ process.$(OBJEXT): {$(VPATH)}config.h
process.$(OBJEXT): {$(VPATH)}defines.h
process.$(OBJEXT): {$(VPATH)}dln.h
process.$(OBJEXT): {$(VPATH)}encoding.h
+process.$(OBJEXT): {$(VPATH)}hrtime.h
process.$(OBJEXT): {$(VPATH)}id.h
process.$(OBJEXT): {$(VPATH)}intern.h
process.$(OBJEXT): {$(VPATH)}internal.h
@@ -2056,6 +2532,7 @@ process.$(OBJEXT): {$(VPATH)}io.h
process.$(OBJEXT): {$(VPATH)}method.h
process.$(OBJEXT): {$(VPATH)}missing.h
process.$(OBJEXT): {$(VPATH)}node.h
+process.$(OBJEXT): {$(VPATH)}onigmo.h
process.$(OBJEXT): {$(VPATH)}oniguruma.h
process.$(OBJEXT): {$(VPATH)}process.c
process.$(OBJEXT): {$(VPATH)}ruby_assert.h
@@ -2078,6 +2555,7 @@ random.$(OBJEXT): {$(VPATH)}intern.h
random.$(OBJEXT): {$(VPATH)}internal.h
random.$(OBJEXT): {$(VPATH)}io.h
random.$(OBJEXT): {$(VPATH)}missing.h
+random.$(OBJEXT): {$(VPATH)}onigmo.h
random.$(OBJEXT): {$(VPATH)}oniguruma.h
random.$(OBJEXT): {$(VPATH)}random.c
random.$(OBJEXT): {$(VPATH)}ruby_atomic.h
@@ -2095,6 +2573,7 @@ range.$(OBJEXT): {$(VPATH)}intern.h
range.$(OBJEXT): {$(VPATH)}internal.h
range.$(OBJEXT): {$(VPATH)}io.h
range.$(OBJEXT): {$(VPATH)}missing.h
+range.$(OBJEXT): {$(VPATH)}onigmo.h
range.$(OBJEXT): {$(VPATH)}oniguruma.h
range.$(OBJEXT): {$(VPATH)}range.c
range.$(OBJEXT): {$(VPATH)}st.h
@@ -2104,10 +2583,12 @@ rational.$(OBJEXT): $(top_srcdir)/include/ruby.h
rational.$(OBJEXT): {$(VPATH)}config.h
rational.$(OBJEXT): {$(VPATH)}defines.h
rational.$(OBJEXT): {$(VPATH)}encoding.h
+rational.$(OBJEXT): {$(VPATH)}id.h
rational.$(OBJEXT): {$(VPATH)}intern.h
rational.$(OBJEXT): {$(VPATH)}internal.h
rational.$(OBJEXT): {$(VPATH)}io.h
rational.$(OBJEXT): {$(VPATH)}missing.h
+rational.$(OBJEXT): {$(VPATH)}onigmo.h
rational.$(OBJEXT): {$(VPATH)}oniguruma.h
rational.$(OBJEXT): {$(VPATH)}rational.c
rational.$(OBJEXT): {$(VPATH)}ruby_assert.h
@@ -2123,6 +2604,7 @@ re.$(OBJEXT): {$(VPATH)}intern.h
re.$(OBJEXT): {$(VPATH)}internal.h
re.$(OBJEXT): {$(VPATH)}io.h
re.$(OBJEXT): {$(VPATH)}missing.h
+re.$(OBJEXT): {$(VPATH)}onigmo.h
re.$(OBJEXT): {$(VPATH)}oniguruma.h
re.$(OBJEXT): {$(VPATH)}re.c
re.$(OBJEXT): {$(VPATH)}re.h
@@ -2137,6 +2619,7 @@ regcomp.$(OBJEXT): {$(VPATH)}config.h
regcomp.$(OBJEXT): {$(VPATH)}defines.h
regcomp.$(OBJEXT): {$(VPATH)}intern.h
regcomp.$(OBJEXT): {$(VPATH)}missing.h
+regcomp.$(OBJEXT): {$(VPATH)}onigmo.h
regcomp.$(OBJEXT): {$(VPATH)}oniguruma.h
regcomp.$(OBJEXT): {$(VPATH)}regcomp.c
regcomp.$(OBJEXT): {$(VPATH)}regenc.h
@@ -2149,6 +2632,7 @@ regenc.$(OBJEXT): {$(VPATH)}config.h
regenc.$(OBJEXT): {$(VPATH)}defines.h
regenc.$(OBJEXT): {$(VPATH)}intern.h
regenc.$(OBJEXT): {$(VPATH)}missing.h
+regenc.$(OBJEXT): {$(VPATH)}onigmo.h
regenc.$(OBJEXT): {$(VPATH)}oniguruma.h
regenc.$(OBJEXT): {$(VPATH)}regenc.c
regenc.$(OBJEXT): {$(VPATH)}regenc.h
@@ -2160,6 +2644,7 @@ regerror.$(OBJEXT): {$(VPATH)}config.h
regerror.$(OBJEXT): {$(VPATH)}defines.h
regerror.$(OBJEXT): {$(VPATH)}intern.h
regerror.$(OBJEXT): {$(VPATH)}missing.h
+regerror.$(OBJEXT): {$(VPATH)}onigmo.h
regerror.$(OBJEXT): {$(VPATH)}oniguruma.h
regerror.$(OBJEXT): {$(VPATH)}regenc.h
regerror.$(OBJEXT): {$(VPATH)}regerror.c
@@ -2171,6 +2656,7 @@ regexec.$(OBJEXT): {$(VPATH)}config.h
regexec.$(OBJEXT): {$(VPATH)}defines.h
regexec.$(OBJEXT): {$(VPATH)}intern.h
regexec.$(OBJEXT): {$(VPATH)}missing.h
+regexec.$(OBJEXT): {$(VPATH)}onigmo.h
regexec.$(OBJEXT): {$(VPATH)}oniguruma.h
regexec.$(OBJEXT): {$(VPATH)}regenc.h
regexec.$(OBJEXT): {$(VPATH)}regexec.c
@@ -2182,6 +2668,7 @@ regparse.$(OBJEXT): {$(VPATH)}config.h
regparse.$(OBJEXT): {$(VPATH)}defines.h
regparse.$(OBJEXT): {$(VPATH)}intern.h
regparse.$(OBJEXT): {$(VPATH)}missing.h
+regparse.$(OBJEXT): {$(VPATH)}onigmo.h
regparse.$(OBJEXT): {$(VPATH)}oniguruma.h
regparse.$(OBJEXT): {$(VPATH)}regenc.h
regparse.$(OBJEXT): {$(VPATH)}regint.h
@@ -2194,12 +2681,14 @@ regsyntax.$(OBJEXT): {$(VPATH)}config.h
regsyntax.$(OBJEXT): {$(VPATH)}defines.h
regsyntax.$(OBJEXT): {$(VPATH)}intern.h
regsyntax.$(OBJEXT): {$(VPATH)}missing.h
+regsyntax.$(OBJEXT): {$(VPATH)}onigmo.h
regsyntax.$(OBJEXT): {$(VPATH)}oniguruma.h
regsyntax.$(OBJEXT): {$(VPATH)}regenc.h
regsyntax.$(OBJEXT): {$(VPATH)}regint.h
regsyntax.$(OBJEXT): {$(VPATH)}regsyntax.c
regsyntax.$(OBJEXT): {$(VPATH)}st.h
regsyntax.$(OBJEXT): {$(VPATH)}subst.h
+ruby-runner.$(OBJEXT): {$(VPATH)}config.h
ruby-runner.$(OBJEXT): {$(VPATH)}ruby-runner.c
ruby-runner.$(OBJEXT): {$(VPATH)}ruby-runner.h
ruby.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
@@ -2207,6 +2696,7 @@ ruby.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
ruby.$(OBJEXT): $(CCAN_DIR)/list/list.h
ruby.$(OBJEXT): $(CCAN_DIR)/str/str.h
ruby.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+ruby.$(OBJEXT): $(hdrdir)/ruby/version.h
ruby.$(OBJEXT): $(top_srcdir)/include/ruby.h
ruby.$(OBJEXT): {$(VPATH)}config.h
ruby.$(OBJEXT): {$(VPATH)}defines.h
@@ -2219,7 +2709,9 @@ ruby.$(OBJEXT): {$(VPATH)}internal.h
ruby.$(OBJEXT): {$(VPATH)}io.h
ruby.$(OBJEXT): {$(VPATH)}method.h
ruby.$(OBJEXT): {$(VPATH)}missing.h
+ruby.$(OBJEXT): {$(VPATH)}mjit.h
ruby.$(OBJEXT): {$(VPATH)}node.h
+ruby.$(OBJEXT): {$(VPATH)}onigmo.h
ruby.$(OBJEXT): {$(VPATH)}oniguruma.h
ruby.$(OBJEXT): {$(VPATH)}ruby.c
ruby.$(OBJEXT): {$(VPATH)}ruby_assert.h
@@ -2249,6 +2741,7 @@ safe.$(OBJEXT): {$(VPATH)}io.h
safe.$(OBJEXT): {$(VPATH)}method.h
safe.$(OBJEXT): {$(VPATH)}missing.h
safe.$(OBJEXT): {$(VPATH)}node.h
+safe.$(OBJEXT): {$(VPATH)}onigmo.h
safe.$(OBJEXT): {$(VPATH)}oniguruma.h
safe.$(OBJEXT): {$(VPATH)}ruby_assert.h
safe.$(OBJEXT): {$(VPATH)}ruby_atomic.h
@@ -2277,6 +2770,7 @@ signal.$(OBJEXT): $(CCAN_DIR)/str/str.h
signal.$(OBJEXT): $(hdrdir)/ruby/ruby.h
signal.$(OBJEXT): $(top_srcdir)/include/ruby.h
signal.$(OBJEXT): {$(VPATH)}config.h
+signal.$(OBJEXT): {$(VPATH)}debug_counter.h
signal.$(OBJEXT): {$(VPATH)}defines.h
signal.$(OBJEXT): {$(VPATH)}encoding.h
signal.$(OBJEXT): {$(VPATH)}eval_intern.h
@@ -2287,6 +2781,7 @@ signal.$(OBJEXT): {$(VPATH)}io.h
signal.$(OBJEXT): {$(VPATH)}method.h
signal.$(OBJEXT): {$(VPATH)}missing.h
signal.$(OBJEXT): {$(VPATH)}node.h
+signal.$(OBJEXT): {$(VPATH)}onigmo.h
signal.$(OBJEXT): {$(VPATH)}oniguruma.h
signal.$(OBJEXT): {$(VPATH)}ruby_assert.h
signal.$(OBJEXT): {$(VPATH)}ruby_atomic.h
@@ -2308,6 +2803,7 @@ sprintf.$(OBJEXT): {$(VPATH)}intern.h
sprintf.$(OBJEXT): {$(VPATH)}internal.h
sprintf.$(OBJEXT): {$(VPATH)}io.h
sprintf.$(OBJEXT): {$(VPATH)}missing.h
+sprintf.$(OBJEXT): {$(VPATH)}onigmo.h
sprintf.$(OBJEXT): {$(VPATH)}oniguruma.h
sprintf.$(OBJEXT): {$(VPATH)}re.h
sprintf.$(OBJEXT): {$(VPATH)}regex.h
@@ -2328,6 +2824,7 @@ st.$(OBJEXT): {$(VPATH)}intern.h
st.$(OBJEXT): {$(VPATH)}internal.h
st.$(OBJEXT): {$(VPATH)}io.h
st.$(OBJEXT): {$(VPATH)}missing.h
+st.$(OBJEXT): {$(VPATH)}onigmo.h
st.$(OBJEXT): {$(VPATH)}oniguruma.h
st.$(OBJEXT): {$(VPATH)}st.c
st.$(OBJEXT): {$(VPATH)}st.h
@@ -2341,6 +2838,7 @@ strftime.$(OBJEXT): {$(VPATH)}intern.h
strftime.$(OBJEXT): {$(VPATH)}internal.h
strftime.$(OBJEXT): {$(VPATH)}io.h
strftime.$(OBJEXT): {$(VPATH)}missing.h
+strftime.$(OBJEXT): {$(VPATH)}onigmo.h
strftime.$(OBJEXT): {$(VPATH)}oniguruma.h
strftime.$(OBJEXT): {$(VPATH)}st.h
strftime.$(OBJEXT): {$(VPATH)}strftime.c
@@ -2350,6 +2848,7 @@ string.$(OBJEXT): $(hdrdir)/ruby/ruby.h
string.$(OBJEXT): $(top_srcdir)/include/ruby.h
string.$(OBJEXT): {$(VPATH)}config.h
string.$(OBJEXT): {$(VPATH)}crypt.h
+string.$(OBJEXT): {$(VPATH)}debug_counter.h
string.$(OBJEXT): {$(VPATH)}defines.h
string.$(OBJEXT): {$(VPATH)}encindex.h
string.$(OBJEXT): {$(VPATH)}encoding.h
@@ -2359,6 +2858,7 @@ string.$(OBJEXT): {$(VPATH)}intern.h
string.$(OBJEXT): {$(VPATH)}internal.h
string.$(OBJEXT): {$(VPATH)}io.h
string.$(OBJEXT): {$(VPATH)}missing.h
+string.$(OBJEXT): {$(VPATH)}onigmo.h
string.$(OBJEXT): {$(VPATH)}oniguruma.h
string.$(OBJEXT): {$(VPATH)}probes.dmyh
string.$(OBJEXT): {$(VPATH)}probes.h
@@ -2368,6 +2868,7 @@ string.$(OBJEXT): {$(VPATH)}ruby_assert.h
string.$(OBJEXT): {$(VPATH)}st.h
string.$(OBJEXT): {$(VPATH)}string.c
string.$(OBJEXT): {$(VPATH)}subst.h
+string.$(OBJEXT): {$(VPATH)}util.h
strlcat.$(OBJEXT): {$(VPATH)}config.h
strlcat.$(OBJEXT): {$(VPATH)}missing.h
strlcat.$(OBJEXT): {$(VPATH)}strlcat.c
@@ -2390,6 +2891,7 @@ struct.$(OBJEXT): {$(VPATH)}io.h
struct.$(OBJEXT): {$(VPATH)}method.h
struct.$(OBJEXT): {$(VPATH)}missing.h
struct.$(OBJEXT): {$(VPATH)}node.h
+struct.$(OBJEXT): {$(VPATH)}onigmo.h
struct.$(OBJEXT): {$(VPATH)}oniguruma.h
struct.$(OBJEXT): {$(VPATH)}ruby_assert.h
struct.$(OBJEXT): {$(VPATH)}ruby_atomic.h
@@ -2398,6 +2900,7 @@ struct.$(OBJEXT): {$(VPATH)}struct.c
struct.$(OBJEXT): {$(VPATH)}subst.h
struct.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
struct.$(OBJEXT): {$(VPATH)}thread_native.h
+struct.$(OBJEXT): {$(VPATH)}transient_heap.h
struct.$(OBJEXT): {$(VPATH)}vm_core.h
struct.$(OBJEXT): {$(VPATH)}vm_debug.h
struct.$(OBJEXT): {$(VPATH)}vm_opts.h
@@ -2415,6 +2918,7 @@ symbol.$(OBJEXT): {$(VPATH)}intern.h
symbol.$(OBJEXT): {$(VPATH)}internal.h
symbol.$(OBJEXT): {$(VPATH)}io.h
symbol.$(OBJEXT): {$(VPATH)}missing.h
+symbol.$(OBJEXT): {$(VPATH)}onigmo.h
symbol.$(OBJEXT): {$(VPATH)}oniguruma.h
symbol.$(OBJEXT): {$(VPATH)}probes.dmyh
symbol.$(OBJEXT): {$(VPATH)}probes.h
@@ -2430,17 +2934,22 @@ thread.$(OBJEXT): $(CCAN_DIR)/str/str.h
thread.$(OBJEXT): $(hdrdir)/ruby/ruby.h
thread.$(OBJEXT): $(top_srcdir)/include/ruby.h
thread.$(OBJEXT): {$(VPATH)}config.h
+thread.$(OBJEXT): {$(VPATH)}debug.h
thread.$(OBJEXT): {$(VPATH)}defines.h
thread.$(OBJEXT): {$(VPATH)}encoding.h
thread.$(OBJEXT): {$(VPATH)}eval_intern.h
thread.$(OBJEXT): {$(VPATH)}gc.h
+thread.$(OBJEXT): {$(VPATH)}hrtime.h
thread.$(OBJEXT): {$(VPATH)}id.h
thread.$(OBJEXT): {$(VPATH)}intern.h
thread.$(OBJEXT): {$(VPATH)}internal.h
thread.$(OBJEXT): {$(VPATH)}io.h
+thread.$(OBJEXT): {$(VPATH)}iseq.h
thread.$(OBJEXT): {$(VPATH)}method.h
thread.$(OBJEXT): {$(VPATH)}missing.h
+thread.$(OBJEXT): {$(VPATH)}mjit.h
thread.$(OBJEXT): {$(VPATH)}node.h
+thread.$(OBJEXT): {$(VPATH)}onigmo.h
thread.$(OBJEXT): {$(VPATH)}oniguruma.h
thread.$(OBJEXT): {$(VPATH)}ruby_assert.h
thread.$(OBJEXT): {$(VPATH)}ruby_atomic.h
@@ -2461,10 +2970,12 @@ time.$(OBJEXT): $(top_srcdir)/include/ruby.h
time.$(OBJEXT): {$(VPATH)}config.h
time.$(OBJEXT): {$(VPATH)}defines.h
time.$(OBJEXT): {$(VPATH)}encoding.h
+time.$(OBJEXT): {$(VPATH)}id.h
time.$(OBJEXT): {$(VPATH)}intern.h
time.$(OBJEXT): {$(VPATH)}internal.h
time.$(OBJEXT): {$(VPATH)}io.h
time.$(OBJEXT): {$(VPATH)}missing.h
+time.$(OBJEXT): {$(VPATH)}onigmo.h
time.$(OBJEXT): {$(VPATH)}oniguruma.h
time.$(OBJEXT): {$(VPATH)}st.h
time.$(OBJEXT): {$(VPATH)}subst.h
@@ -2475,15 +2986,34 @@ transcode.$(OBJEXT): $(top_srcdir)/include/ruby.h
transcode.$(OBJEXT): {$(VPATH)}config.h
transcode.$(OBJEXT): {$(VPATH)}defines.h
transcode.$(OBJEXT): {$(VPATH)}encoding.h
+transcode.$(OBJEXT): {$(VPATH)}id.h
transcode.$(OBJEXT): {$(VPATH)}intern.h
transcode.$(OBJEXT): {$(VPATH)}internal.h
transcode.$(OBJEXT): {$(VPATH)}io.h
transcode.$(OBJEXT): {$(VPATH)}missing.h
+transcode.$(OBJEXT): {$(VPATH)}onigmo.h
transcode.$(OBJEXT): {$(VPATH)}oniguruma.h
transcode.$(OBJEXT): {$(VPATH)}st.h
transcode.$(OBJEXT): {$(VPATH)}subst.h
transcode.$(OBJEXT): {$(VPATH)}transcode.c
transcode.$(OBJEXT): {$(VPATH)}transcode_data.h
+transient_heap.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+transient_heap.$(OBJEXT): $(top_srcdir)/include/ruby.h
+transient_heap.$(OBJEXT): {$(VPATH)}config.h
+transient_heap.$(OBJEXT): {$(VPATH)}debug.h
+transient_heap.$(OBJEXT): {$(VPATH)}debug_counter.h
+transient_heap.$(OBJEXT): {$(VPATH)}defines.h
+transient_heap.$(OBJEXT): {$(VPATH)}gc.h
+transient_heap.$(OBJEXT): {$(VPATH)}intern.h
+transient_heap.$(OBJEXT): {$(VPATH)}internal.h
+transient_heap.$(OBJEXT): {$(VPATH)}missing.h
+transient_heap.$(OBJEXT): {$(VPATH)}node.h
+transient_heap.$(OBJEXT): {$(VPATH)}ruby_assert.h
+transient_heap.$(OBJEXT): {$(VPATH)}st.h
+transient_heap.$(OBJEXT): {$(VPATH)}subst.h
+transient_heap.$(OBJEXT): {$(VPATH)}transient_heap.c
+transient_heap.$(OBJEXT): {$(VPATH)}transient_heap.h
+transient_heap.$(OBJEXT): {$(VPATH)}vm_debug.h
util.$(OBJEXT): $(hdrdir)/ruby/ruby.h
util.$(OBJEXT): $(top_srcdir)/include/ruby.h
util.$(OBJEXT): {$(VPATH)}config.h
@@ -2493,6 +3023,7 @@ util.$(OBJEXT): {$(VPATH)}intern.h
util.$(OBJEXT): {$(VPATH)}internal.h
util.$(OBJEXT): {$(VPATH)}io.h
util.$(OBJEXT): {$(VPATH)}missing.h
+util.$(OBJEXT): {$(VPATH)}onigmo.h
util.$(OBJEXT): {$(VPATH)}oniguruma.h
util.$(OBJEXT): {$(VPATH)}st.h
util.$(OBJEXT): {$(VPATH)}subst.h
@@ -2506,6 +3037,7 @@ variable.$(OBJEXT): $(hdrdir)/ruby/ruby.h
variable.$(OBJEXT): $(top_srcdir)/include/ruby.h
variable.$(OBJEXT): {$(VPATH)}config.h
variable.$(OBJEXT): {$(VPATH)}constant.h
+variable.$(OBJEXT): {$(VPATH)}debug_counter.h
variable.$(OBJEXT): {$(VPATH)}defines.h
variable.$(OBJEXT): {$(VPATH)}encoding.h
variable.$(OBJEXT): {$(VPATH)}id.h
@@ -2513,23 +3045,49 @@ variable.$(OBJEXT): {$(VPATH)}id_table.h
variable.$(OBJEXT): {$(VPATH)}intern.h
variable.$(OBJEXT): {$(VPATH)}internal.h
variable.$(OBJEXT): {$(VPATH)}io.h
+variable.$(OBJEXT): {$(VPATH)}method.h
variable.$(OBJEXT): {$(VPATH)}missing.h
+variable.$(OBJEXT): {$(VPATH)}node.h
+variable.$(OBJEXT): {$(VPATH)}onigmo.h
variable.$(OBJEXT): {$(VPATH)}oniguruma.h
+variable.$(OBJEXT): {$(VPATH)}ruby_assert.h
+variable.$(OBJEXT): {$(VPATH)}ruby_atomic.h
variable.$(OBJEXT): {$(VPATH)}st.h
variable.$(OBJEXT): {$(VPATH)}subst.h
+variable.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+variable.$(OBJEXT): {$(VPATH)}thread_native.h
+variable.$(OBJEXT): {$(VPATH)}transient_heap.h
variable.$(OBJEXT): {$(VPATH)}util.h
variable.$(OBJEXT): {$(VPATH)}variable.c
+variable.$(OBJEXT): {$(VPATH)}vm_core.h
+variable.$(OBJEXT): {$(VPATH)}vm_opts.h
+version.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+version.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+version.$(OBJEXT): $(CCAN_DIR)/list/list.h
+version.$(OBJEXT): $(CCAN_DIR)/str/str.h
version.$(OBJEXT): $(hdrdir)/ruby/ruby.h
version.$(OBJEXT): $(hdrdir)/ruby/version.h
+version.$(OBJEXT): $(top_srcdir)/include/ruby.h
version.$(OBJEXT): $(top_srcdir)/revision.h
version.$(OBJEXT): $(top_srcdir)/version.h
version.$(OBJEXT): {$(VPATH)}config.h
version.$(OBJEXT): {$(VPATH)}defines.h
+version.$(OBJEXT): {$(VPATH)}id.h
version.$(OBJEXT): {$(VPATH)}intern.h
+version.$(OBJEXT): {$(VPATH)}internal.h
+version.$(OBJEXT): {$(VPATH)}method.h
version.$(OBJEXT): {$(VPATH)}missing.h
+version.$(OBJEXT): {$(VPATH)}mjit.h
+version.$(OBJEXT): {$(VPATH)}node.h
+version.$(OBJEXT): {$(VPATH)}ruby_assert.h
+version.$(OBJEXT): {$(VPATH)}ruby_atomic.h
version.$(OBJEXT): {$(VPATH)}st.h
version.$(OBJEXT): {$(VPATH)}subst.h
+version.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+version.$(OBJEXT): {$(VPATH)}thread_native.h
version.$(OBJEXT): {$(VPATH)}version.c
+version.$(OBJEXT): {$(VPATH)}vm_core.h
+version.$(OBJEXT): {$(VPATH)}vm_opts.h
vm.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
vm.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
vm.$(OBJEXT): $(CCAN_DIR)/list/list.h
@@ -2538,7 +3096,9 @@ vm.$(OBJEXT): $(hdrdir)/ruby/ruby.h
vm.$(OBJEXT): $(top_srcdir)/include/ruby.h
vm.$(OBJEXT): {$(VPATH)}config.h
vm.$(OBJEXT): {$(VPATH)}constant.h
+vm.$(OBJEXT): {$(VPATH)}debug_counter.h
vm.$(OBJEXT): {$(VPATH)}defines.h
+vm.$(OBJEXT): {$(VPATH)}defs/opt_operand.def
vm.$(OBJEXT): {$(VPATH)}encoding.h
vm.$(OBJEXT): {$(VPATH)}eval_intern.h
vm.$(OBJEXT): {$(VPATH)}gc.h
@@ -2546,13 +3106,16 @@ vm.$(OBJEXT): {$(VPATH)}id.h
vm.$(OBJEXT): {$(VPATH)}id_table.h
vm.$(OBJEXT): {$(VPATH)}insns.def
vm.$(OBJEXT): {$(VPATH)}insns.inc
+vm.$(OBJEXT): {$(VPATH)}insns_info.inc
vm.$(OBJEXT): {$(VPATH)}intern.h
vm.$(OBJEXT): {$(VPATH)}internal.h
vm.$(OBJEXT): {$(VPATH)}io.h
vm.$(OBJEXT): {$(VPATH)}iseq.h
vm.$(OBJEXT): {$(VPATH)}method.h
vm.$(OBJEXT): {$(VPATH)}missing.h
+vm.$(OBJEXT): {$(VPATH)}mjit.h
vm.$(OBJEXT): {$(VPATH)}node.h
+vm.$(OBJEXT): {$(VPATH)}onigmo.h
vm.$(OBJEXT): {$(VPATH)}oniguruma.h
vm.$(OBJEXT): {$(VPATH)}probes.dmyh
vm.$(OBJEXT): {$(VPATH)}probes.h
@@ -2597,6 +3160,7 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}iseq.h
vm_backtrace.$(OBJEXT): {$(VPATH)}method.h
vm_backtrace.$(OBJEXT): {$(VPATH)}missing.h
vm_backtrace.$(OBJEXT): {$(VPATH)}node.h
+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
@@ -2626,6 +3190,7 @@ vm_dump.$(OBJEXT): {$(VPATH)}iseq.h
vm_dump.$(OBJEXT): {$(VPATH)}method.h
vm_dump.$(OBJEXT): {$(VPATH)}missing.h
vm_dump.$(OBJEXT): {$(VPATH)}node.h
+vm_dump.$(OBJEXT): {$(VPATH)}onigmo.h
vm_dump.$(OBJEXT): {$(VPATH)}oniguruma.h
vm_dump.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm_dump.$(OBJEXT): {$(VPATH)}ruby_atomic.h
@@ -2652,9 +3217,12 @@ vm_trace.$(OBJEXT): {$(VPATH)}id.h
vm_trace.$(OBJEXT): {$(VPATH)}intern.h
vm_trace.$(OBJEXT): {$(VPATH)}internal.h
vm_trace.$(OBJEXT): {$(VPATH)}io.h
+vm_trace.$(OBJEXT): {$(VPATH)}iseq.h
vm_trace.$(OBJEXT): {$(VPATH)}method.h
vm_trace.$(OBJEXT): {$(VPATH)}missing.h
+vm_trace.$(OBJEXT): {$(VPATH)}mjit.h
vm_trace.$(OBJEXT): {$(VPATH)}node.h
+vm_trace.$(OBJEXT): {$(VPATH)}onigmo.h
vm_trace.$(OBJEXT): {$(VPATH)}oniguruma.h
vm_trace.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm_trace.$(OBJEXT): {$(VPATH)}ruby_atomic.h
diff --git a/compile.c b/compile.c
index b113b08df4..f5939efaeb 100644
--- a/compile.c
+++ b/compile.c
@@ -9,13 +9,15 @@
**********************************************************************/
-#include "internal.h"
+#include "ruby/encoding.h"
#include "ruby/re.h"
+#include "ruby/util.h"
+#include "internal.h"
#include "encindex.h"
#include <math.h>
-#define USE_INSN_STACK_INCREASE 1
#include "vm_core.h"
+#include "vm_debug.h"
#include "iseq.h"
#include "insns.inc"
#include "insns_info.inc"
@@ -34,10 +36,11 @@
typedef struct iseq_link_element {
enum {
- ISEQ_ELEMENT_NONE,
+ ISEQ_ELEMENT_ANCHOR,
ISEQ_ELEMENT_LABEL,
ISEQ_ELEMENT_INSN,
- ISEQ_ELEMENT_ADJUST
+ ISEQ_ELEMENT_ADJUST,
+ ISEQ_ELEMENT_TRACE
} type;
struct iseq_link_element *next;
struct iseq_link_element *prev;
@@ -64,15 +67,19 @@ typedef struct iseq_label_data {
int refcnt;
unsigned int set: 1;
unsigned int rescued: 2;
+ unsigned int unremovable: 1;
} LABEL;
typedef struct iseq_insn_data {
LINK_ELEMENT link;
enum ruby_vminsn_type insn_id;
- unsigned int line_no;
int operand_size;
int sc_state;
VALUE *operands;
+ struct {
+ int line_no;
+ rb_event_flag_t events;
+ } insn_info;
} INSN;
typedef struct iseq_adjust_data {
@@ -81,6 +88,12 @@ typedef struct iseq_adjust_data {
int line_no;
} ADJUST;
+typedef struct iseq_trace_data {
+ LINK_ELEMENT link;
+ rb_event_flag_t event;
+ long data;
+} TRACE;
+
struct ensure_range {
LABEL *begin;
LABEL *end;
@@ -88,7 +101,7 @@ struct ensure_range {
};
struct iseq_compile_data_ensure_node_stack {
- NODE *ensure_node;
+ const NODE *ensure_node;
struct iseq_compile_data_ensure_node_stack *prev;
struct ensure_range *erange;
};
@@ -143,30 +156,18 @@ struct iseq_compile_data_ensure_node_stack {
#define debug_node_start(node) ((void) \
(compile_debug_print_indent(1) && \
- (ruby_debug_print_node(1, CPDEBUG, "", (NODE *)(node)), gl_node_level)), \
+ (ruby_debug_print_node(1, CPDEBUG, "", (const NODE *)(node)), gl_node_level)), \
gl_node_level++)
#define debug_node_end() gl_node_level --
#else
-static inline ID
-r_id(ID id)
-{
- return id;
-}
-
-static inline VALUE
-r_value(VALUE value)
-{
- return value;
-}
-
-#define debugi(header, id) r_id(id)
-#define debugp(header, value) r_value(value)
-#define debugp_verbose(header, value) r_value(value)
-#define debugp_verbose_node(header, value) r_value(value)
-#define debugp_param(header, value) r_value(value)
+#define debugi(header, id) ((void)0)
+#define debugp(header, value) ((void)0)
+#define debugp_verbose(header, value) ((void)0)
+#define debugp_verbose_node(header, value) ((void)0)
+#define debugp_param(header, value) ((void)0)
#define debug_node_start(node) ((void)0)
#define debug_node_end() ((void)0)
#endif
@@ -184,9 +185,7 @@ r_value(VALUE value)
/* create new label */
#define NEW_LABEL(l) new_label_body(iseq, (l))
-
-#define iseq_path(iseq) ((iseq)->body->location.path)
-#define iseq_absolute_path(iseq) ((iseq)->body->location.absolute_path)
+#define LABEL_FORMAT "<L%03d>"
#define NEW_ISEQ(node, name, type, line_no) \
new_child_iseq(iseq, (node), rb_fstring(name), 0, (type), (line_no))
@@ -202,18 +201,27 @@ r_value(VALUE value)
#define ADD_INSN(seq, line, insn) \
ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (line), BIN(insn), 0))
-/* insert an instruction before prev */
-#define INSERT_BEFORE_INSN(prev, line, insn) \
- INSERT_ELEM_PREV(&(prev)->link, (LINK_ELEMENT *) new_insn_body(iseq, (line), BIN(insn), 0))
+/* insert an instruction before next */
+#define INSERT_BEFORE_INSN(next, line, insn) \
+ ELEM_INSERT_PREV(&(next)->link, (LINK_ELEMENT *) new_insn_body(iseq, (line), BIN(insn), 0))
+
+/* insert an instruction after prev */
+#define INSERT_AFTER_INSN(prev, line, insn) \
+ ELEM_INSERT_NEXT(&(prev)->link, (LINK_ELEMENT *) new_insn_body(iseq, (line), BIN(insn), 0))
/* add an instruction with some operands (1, 2, 3, 5) */
#define ADD_INSN1(seq, line, insn, op1) \
ADD_ELEM((seq), (LINK_ELEMENT *) \
new_insn_body(iseq, (line), BIN(insn), 1, (VALUE)(op1)))
-/* insert an instruction with some operands (1, 2, 3, 5) before prev */
-#define INSERT_BEFORE_INSN1(prev, line, insn, op1) \
- INSERT_ELEM_PREV(&(prev)->link, (LINK_ELEMENT *) \
+/* insert an instruction with some operands (1, 2, 3, 5) before next */
+#define INSERT_BEFORE_INSN1(next, line, insn, op1) \
+ ELEM_INSERT_PREV(&(next)->link, (LINK_ELEMENT *) \
+ new_insn_body(iseq, (line), BIN(insn), 1, (VALUE)(op1)))
+
+/* insert an instruction with some operands (1, 2, 3, 5) after prev */
+#define INSERT_AFTER_INSN1(prev, line, insn, op1) \
+ ELEM_INSERT_NEXT(&(prev)->link, (LINK_ELEMENT *) \
new_insn_body(iseq, (line), BIN(insn), 1, (VALUE)(op1)))
#define LABEL_REF(label) ((label)->refcnt++)
@@ -251,29 +259,51 @@ r_value(VALUE value)
#define ADD_SEND_R(seq, line, id, argc, block, flag, keywords) \
ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, (line), (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords)))
-#define ADD_TRACE(seq, line, event) \
+#define ADD_TRACE(seq, event) \
+ ADD_ELEM((seq), (LINK_ELEMENT *)new_trace_body(iseq, (event), 0))
+#define ADD_TRACE_WITH_DATA(seq, event, data) \
+ ADD_ELEM((seq), (LINK_ELEMENT *)new_trace_body(iseq, (event), (data)))
+
+
+#define DECL_BRANCH_BASE(branches, first_line, first_column, last_line, last_column, type) \
do { \
- if ((event) == RUBY_EVENT_LINE && ISEQ_COVERAGE(iseq) && \
- (line) > 0 && \
- (line) != ISEQ_COMPILE_DATA(iseq)->last_coverable_line) { \
- RARRAY_ASET(ISEQ_COVERAGE(iseq), (line) - 1, INT2FIX(0)); \
- ISEQ_COMPILE_DATA(iseq)->last_coverable_line = (line); \
- ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_COVERAGE)); \
- } \
- if (ISEQ_COMPILE_DATA(iseq)->option->trace_instruction) { \
- ADD_INSN1((seq), (line), trace, INT2FIX(event)); \
+ if (ISEQ_COVERAGE(iseq) && \
+ ISEQ_BRANCH_COVERAGE(iseq) && \
+ (first_line) > 0) { \
+ VALUE structure = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 0); \
+ branches = rb_ary_tmp_new(0); \
+ rb_ary_push(structure, branches); \
+ rb_ary_push(branches, ID2SYM(rb_intern(type))); \
+ rb_ary_push(branches, INT2FIX(first_line)); \
+ rb_ary_push(branches, INT2FIX(first_column)); \
+ rb_ary_push(branches, INT2FIX(last_line)); \
+ rb_ary_push(branches, INT2FIX(last_column)); \
} \
} while (0)
-
-#define ADD_GETLOCAL(seq, line, idx, level) \
+#define ADD_TRACE_BRANCH_COVERAGE(seq, first_line, first_column, last_line, last_column, type, branches) \
do { \
- ADD_INSN2((seq), (line), getlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)); \
+ if (ISEQ_COVERAGE(iseq) && \
+ ISEQ_BRANCH_COVERAGE(iseq) && \
+ (first_line) > 0) { \
+ VALUE counters = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 1); \
+ long counter_idx = RARRAY_LEN(counters); \
+ rb_ary_push(counters, INT2FIX(0)); \
+ rb_ary_push(branches, ID2SYM(rb_intern(type))); \
+ rb_ary_push(branches, INT2FIX(first_line)); \
+ rb_ary_push(branches, INT2FIX(first_column)); \
+ rb_ary_push(branches, INT2FIX(last_line)); \
+ rb_ary_push(branches, INT2FIX(last_column)); \
+ rb_ary_push(branches, INT2FIX(counter_idx)); \
+ ADD_TRACE_WITH_DATA(seq, RUBY_EVENT_COVERAGE_BRANCH, counter_idx); \
+ ADD_INSN(seq, last_line, nop); \
+ } \
} while (0)
-#define ADD_SETLOCAL(seq, line, idx, level) \
- do { \
- ADD_INSN2((seq), (line), setlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)); \
- } while (0)
+static void iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level);
+static void iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level);
+
+#define ADD_GETLOCAL(seq, line, idx, level) iseq_add_getlocal(iseq, (seq), (line), (idx), (level))
+#define ADD_SETLOCAL(seq, line, idx, level) iseq_add_setlocal(iseq, (seq), (line), (idx), (level))
/* add label */
#define ADD_LABEL(seq, label) \
@@ -288,13 +318,15 @@ r_value(VALUE value)
#define ADD_ADJUST_RESTORE(seq, label) \
ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), -1))
+#define LABEL_UNREMOVABLE(label) \
+ ((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); \
- if (ls) LABEL_REF(ls); \
- if (le) LABEL_REF(le); \
- if (lc) LABEL_REF(lc); \
+ LABEL_UNREMOVABLE(ls); \
+ LABEL_REF(le); \
+ LABEL_REF(lc); \
rb_ary_push(ISEQ_COMPILE_DATA(iseq)->catch_table_ary, freeze_hide_obj(_e)); \
} while (0)
@@ -304,14 +336,14 @@ r_value(VALUE value)
iseq_compile_each(iseq, (anchor), (node), 0)))
/* compile node, this node's value will be popped */
-#define COMPILE_POPED(anchor, desc, node) \
+#define COMPILE_POPPED(anchor, desc, node) \
(debug_compile("== " desc "\n", \
iseq_compile_each(iseq, (anchor), (node), 1)))
-/* compile node, which is popped when 'poped' is true */
-#define COMPILE_(anchor, desc, node, poped) \
+/* compile node, which is popped when 'popped' is true */
+#define COMPILE_(anchor, desc, node, popped) \
(debug_compile("== " desc "\n", \
- iseq_compile_each(iseq, (anchor), (node), (poped))))
+ iseq_compile_each(iseq, (anchor), (node), (popped))))
#define COMPILE_RECV(anchor, desc, node) \
(private_recv_p(node) ? \
@@ -327,17 +359,22 @@ r_value(VALUE value)
#define IS_INSN(link) ((link)->type == ISEQ_ELEMENT_INSN)
#define IS_LABEL(link) ((link)->type == ISEQ_ELEMENT_LABEL)
#define IS_ADJUST(link) ((link)->type == ISEQ_ELEMENT_ADJUST)
+#define IS_TRACE(link) ((link)->type == ISEQ_ELEMENT_TRACE)
#define IS_INSN_ID(iobj, insn) (INSN_OF(iobj) == BIN(insn))
+#define IS_NEXT_INSN_ID(link, insn) \
+ ((link)->next && IS_INSN((link)->next) && IS_INSN_ID((link)->next, insn))
/* error */
-typedef void (*compile_error_func)(rb_iseq_t *, int, const char *, ...);
+#if CPDEBUG > 0
+NORETURN(static void append_compile_error(rb_iseq_t *iseq, int line, const char *fmt, ...));
+#endif
static void
-append_compile_error(rb_iseq_t *iseq, int line, const char *fmt, ...)
+append_compile_error(const rb_iseq_t *iseq, int line, const char *fmt, ...)
{
VALUE err_info = ISEQ_COMPILE_DATA(iseq)->err_info;
- VALUE file = iseq->body->location.path;
- VALUE err = err_info;
+ VALUE file = rb_iseq_path(iseq);
+ VALUE err = err_info == Qtrue ? Qfalse : err_info;
va_list args;
va_start(args, fmt);
@@ -347,66 +384,68 @@ append_compile_error(rb_iseq_t *iseq, int line, const char *fmt, ...)
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);
+ }
+ if (compile_debug) rb_exc_fatal(err);
}
+#if 0
static void
compile_bug(rb_iseq_t *iseq, int line, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
- rb_report_bug_valist(iseq->body->location.path, line, fmt, args);
+ rb_report_bug_valist(rb_iseq_path(iseq), line, fmt, args);
va_end(args);
abort();
}
+#endif
-NOINLINE(static compile_error_func prepare_compile_error(rb_iseq_t *iseq));
-
-static compile_error_func
-prepare_compile_error(rb_iseq_t *iseq)
-{
- if (compile_debug) return &compile_bug;
- return &append_compile_error;
-}
-
-#define COMPILE_ERROR prepare_compile_error(iseq)
+#define COMPILE_ERROR append_compile_error
#define ERROR_ARGS_AT(n) iseq, nd_line(n),
#define ERROR_ARGS ERROR_ARGS_AT(node)
-#define EXPECT_NODE(prefix, node, ndtype) \
+#define EXPECT_NODE(prefix, node, ndtype, errval) \
do { \
- NODE *error_node = (node); \
+ const NODE *error_node = (node); \
enum node_type error_type = nd_type(error_node); \
if (error_type != (ndtype)) { \
- compile_bug(ERROR_ARGS_AT(error_node) \
- prefix ": " #ndtype " is expected, but %s", \
- ruby_node_name(error_type)); \
+ 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) \
+#define EXPECT_NODE_NONULL(prefix, parent, ndtype, errval) \
do { \
- compile_bug(ERROR_ARGS_AT(parent) \
- prefix ": must be " #ndtype ", but 0"); \
+ COMPILE_ERROR(ERROR_ARGS_AT(parent) \
+ prefix ": must be " #ndtype ", but 0"); \
+ return errval; \
} while (0)
-#define UNKNOWN_NODE(prefix, node) \
+#define UNKNOWN_NODE(prefix, node, errval) \
do { \
- NODE *error_node = (node); \
- compile_bug(ERROR_ARGS_AT(error_node) prefix ": unknown node (%s)", \
- ruby_node_name(nd_type(error_node))); \
+ const NODE *error_node = (node); \
+ COMPILE_ERROR(ERROR_ARGS_AT(error_node) prefix ": unknown node (%s)", \
+ ruby_node_name(nd_type(error_node))); \
+ return errval; \
} while (0)
#define COMPILE_OK 1
#define COMPILE_NG 0
+#define CHECK(sub) if (!(sub)) {BEFORE_RETURN;return COMPILE_NG;}
+#define BEFORE_RETURN
/* leave name uninitialized so that compiler warn if INIT_ANCHOR is
* missing */
#define DECL_ANCHOR(name) \
- LINK_ANCHOR *name, name##_body__ = {{0,},}
+ LINK_ANCHOR name[1] = {{{ISEQ_ELEMENT_ANCHOR,},}}
#define INIT_ANCHOR(name) \
- (name##_body__.last = &name##_body__.anchor, name = &name##_body__)
+ (name->last = &name->anchor)
static inline VALUE
freeze_hide_obj(VALUE obj)
@@ -434,7 +473,8 @@ freeze_hide_obj(VALUE obj)
#define gl_node_level ISEQ_COMPILE_DATA(iseq)->node_level
#endif
-static void dump_disasm_list(LINK_ELEMENT *elem);
+static void dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, const LABEL *dest);
+static void dump_disasm_list(const LINK_ELEMENT *elem);
static int insn_data_length(INSN *iobj);
static int calc_sp_depth(int depth, INSN *iobj);
@@ -442,27 +482,32 @@ static int calc_sp_depth(int depth, INSN *iobj);
static INSN *new_insn_body(rb_iseq_t *iseq, int line_no, enum ruby_vminsn_type insn_id, int argc, ...);
static LABEL *new_label_body(rb_iseq_t *iseq, long line);
static ADJUST *new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line);
+static TRACE *new_trace_body(rb_iseq_t *iseq, rb_event_flag_t event, long data);
+
-static int iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *anchor, NODE * n, int);
-static int iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
-static int iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
-static int iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
+static int iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *anchor, const NODE *n, int);
+static int iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
+static int iseq_setup_insn(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
+static int iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
+static int iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
static int iseq_set_local_table(rb_iseq_t *iseq, const ID *tbl);
static int iseq_set_exception_local_table(rb_iseq_t *iseq);
-static int iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *anchor, NODE * node);
+static int iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, const NODE *const node);
-static int iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
-static int iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
+static int iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
+static int iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
static int iseq_set_exception_table(rb_iseq_t *iseq);
static int iseq_set_optargs_table(rb_iseq_t *iseq);
+static int compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr);
+
/*
* To make Array to LinkedList, use link_anchor
*/
static void
-verify_list(ISEQ_ARG_DECLARE const char *info, LINK_ANCHOR *anchor)
+verify_list(ISEQ_ARG_DECLARE const char *info, LINK_ANCHOR *const anchor)
{
#if CPDEBUG
int flag = 0;
@@ -497,7 +542,7 @@ verify_list(ISEQ_ARG_DECLARE const char *info, LINK_ANCHOR *anchor)
* elem1, elem2 => elem1, elem2, elem
*/
static void
-ADD_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor, LINK_ELEMENT *elem)
+ADD_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor, LINK_ELEMENT *elem)
{
elem->prev = anchor->last;
anchor->last->next = elem;
@@ -509,7 +554,7 @@ ADD_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor, LINK_ELEMENT *elem)
* elem1, before, elem2 => elem1, before, elem, elem2
*/
static void
-APPEND_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor, LINK_ELEMENT *before, LINK_ELEMENT *elem)
+APPEND_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor, LINK_ELEMENT *before, LINK_ELEMENT *elem)
{
elem->prev = before;
elem->next = before->next;
@@ -523,16 +568,7 @@ APPEND_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor, LINK_ELEMENT *before, LINK_ELE
#define APPEND_ELEM(anchor, before, elem) APPEND_ELEM(iseq, (anchor), (before), (elem))
#endif
-static int
-iseq_add_mark_object(const rb_iseq_t *iseq, VALUE v)
-{
- if (!SPECIAL_CONST_P(v)) {
- rb_iseq_add_mark_object(iseq, v);
- }
- return COMPILE_OK;
-}
-
-#define ruby_sourcefile RSTRING_PTR(iseq->body->location.path)
+#define ISEQ_LAST_LINE(iseq) (ISEQ_COMPILE_DATA(iseq)->last_line)
static int
iseq_add_mark_object_compile_time(const rb_iseq_t *iseq, VALUE v)
@@ -543,6 +579,14 @@ iseq_add_mark_object_compile_time(const rb_iseq_t *iseq, VALUE v)
return COMPILE_OK;
}
+static inline VALUE
+freeze_literal(rb_iseq_t *iseq, VALUE lit)
+{
+ lit = rb_fstring(lit);
+ rb_ary_push(ISEQ_COMPILE_DATA(iseq)->mark_ary, lit);
+ return lit;
+}
+
static int
validate_label(st_data_t name, st_data_t label, st_data_t arg)
{
@@ -552,7 +596,7 @@ validate_label(st_data_t name, st_data_t label, st_data_t arg)
do {
COMPILE_ERROR(iseq, lobj->position,
"%"PRIsVALUE": undefined label",
- rb_id2str((ID)name));
+ rb_sym2str((VALUE)name));
} while (0);
}
return ST_CONTINUE;
@@ -563,21 +607,37 @@ validate_labels(rb_iseq_t *iseq, st_table *labels_table)
{
st_foreach(labels_table, validate_label, (st_data_t)iseq);
st_free_table(labels_table);
- if (!NIL_P(ISEQ_COMPILE_DATA(iseq)->err_info)) {
- rb_exc_raise(ISEQ_COMPILE_DATA(iseq)->err_info);
- }
}
VALUE
-rb_iseq_compile_node(rb_iseq_t *iseq, NODE *node)
+rb_iseq_compile_ifunc(rb_iseq_t *iseq, const struct vm_ifunc *ifunc)
+{
+ DECL_ANCHOR(ret);
+ INIT_ANCHOR(ret);
+
+ (*ifunc->func)(iseq, ret, ifunc->data);
+
+ ADD_INSN(ret, ISEQ_COMPILE_DATA(iseq)->last_line, leave);
+
+ CHECK(iseq_setup_insn(iseq, ret));
+ return iseq_setup(iseq, ret);
+}
+
+VALUE
+rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
{
DECL_ANCHOR(ret);
INIT_ANCHOR(ret);
+ if (imemo_type_p((VALUE)node, imemo_ifunc)) {
+ rb_raise(rb_eArgError, "unexpected imemo_ifunc");
+ }
+
if (node == 0) {
COMPILE(ret, "nil", node);
iseq_set_local_table(iseq, 0);
}
+ /* assume node is T_NODE */
else if (nd_type(node) == NODE_SCOPE) {
/* iseq type of top, method, class, block */
iseq_set_local_table(iseq, node->nd_tbl);
@@ -592,67 +652,70 @@ rb_iseq_compile_node(rb_iseq_t *iseq, NODE *node)
start->rescued = LABEL_RESCUE_BEG;
end->rescued = LABEL_RESCUE_END;
- ADD_TRACE(ret, FIX2INT(iseq->body->location.first_lineno), RUBY_EVENT_B_CALL);
+ ADD_TRACE(ret, RUBY_EVENT_B_CALL);
+ ADD_INSN (ret, FIX2INT(iseq->body->location.first_lineno), nop);
ADD_LABEL(ret, start);
- COMPILE(ret, "block body", node->nd_body);
+ CHECK(COMPILE(ret, "block body", node->nd_body));
ADD_LABEL(ret, end);
- ADD_TRACE(ret, nd_line(node), RUBY_EVENT_B_RETURN);
+ ADD_TRACE(ret, RUBY_EVENT_B_RETURN);
+ ISEQ_COMPILE_DATA(iseq)->last_line = iseq->body->location.code_location.end_pos.lineno;
/* wide range catch handler must put at last */
- ADD_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, 0, start);
- ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, 0, end);
+ 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, FIX2INT(iseq->body->location.first_lineno), RUBY_EVENT_CLASS);
- COMPILE(ret, "scoped node", node->nd_body);
- ADD_TRACE(ret, nd_line(node), RUBY_EVENT_END);
+ 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:
{
- ADD_TRACE(ret, FIX2INT(iseq->body->location.first_lineno), RUBY_EVENT_CALL);
- COMPILE(ret, "scoped node", node->nd_body);
- ADD_TRACE(ret, nd_line(node), RUBY_EVENT_RETURN);
+ ADD_TRACE(ret, RUBY_EVENT_CALL);
+ CHECK(COMPILE(ret, "scoped node", node->nd_body));
+ ADD_TRACE(ret, RUBY_EVENT_RETURN);
+ ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
break;
}
default: {
- COMPILE(ret, "scoped node", node->nd_body);
+ CHECK(COMPILE(ret, "scoped node", node->nd_body));
break;
}
}
}
- else if (RB_TYPE_P((VALUE)node, T_IMEMO)) {
- const struct vm_ifunc *ifunc = (struct vm_ifunc *)node;
- /* user callback */
- (*ifunc->func)(iseq, ret, ifunc->data);
- }
else {
+ const char *m;
+#define INVALID_ISEQ_TYPE(type) \
+ ISEQ_TYPE_##type: m = #type; goto invalid_iseq_type
switch (iseq->body->type) {
- case ISEQ_TYPE_METHOD:
- case ISEQ_TYPE_CLASS:
- case ISEQ_TYPE_BLOCK:
- case ISEQ_TYPE_EVAL:
- case ISEQ_TYPE_MAIN:
- case ISEQ_TYPE_TOP:
- COMPILE_ERROR(ERROR_ARGS "compile/should not be reached: %s:%d",
- __FILE__, __LINE__);
- return COMPILE_NG;
+ 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);
- COMPILE(ret, "rescue", node);
+ CHECK(COMPILE(ret, "rescue", node));
break;
case ISEQ_TYPE_ENSURE:
iseq_set_exception_local_table(iseq);
- COMPILE_POPED(ret, "ensure", node);
+ CHECK(COMPILE_POPPED(ret, "ensure", node));
break;
- case ISEQ_TYPE_DEFINED_GUARD:
- iseq_set_exception_local_table(iseq);
- COMPILE(ret, "defined guard", node);
+ case ISEQ_TYPE_PLAIN:
+ CHECK(COMPILE(ret, "ensure", node));
break;
default:
- compile_bug(ERROR_ARGS "unknown scope");
+ COMPILE_ERROR(ERROR_ARGS "unknown scope: %d", iseq->body->type);
+ return COMPILE_NG;
+ invalid_iseq_type:
+ COMPILE_ERROR(ERROR_ARGS "compile/ISEQ_TYPE_%s should not be reached", m);
+ return COMPILE_NG;
}
}
@@ -671,6 +734,7 @@ rb_iseq_compile_node(rb_iseq_t *iseq, NODE *node)
validate_labels(iseq, labels_table);
}
#endif
+ CHECK(iseq_setup_insn(iseq, ret));
return iseq_setup(iseq, ret);
}
@@ -688,26 +752,11 @@ rb_iseq_translate_threaded_code(rb_iseq_t *iseq)
encoded[i] = (VALUE)table[insn];
i += len;
}
+ FL_SET(iseq, ISEQ_TRANSLATED);
#endif
return COMPILE_OK;
}
-#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
-static int
-rb_vm_insn_addr2insn(const void *addr) /* cold path */
-{
- int insn;
- const void * const *table = rb_vm_get_insns_address_table();
-
- for (insn = 0; insn < VM_INSTRUCTION_SIZE; insn++) {
- if (table[insn] == addr) {
- return insn;
- }
- }
- rb_bug("rb_vm_insn_addr2insn: invalid insn address: %p", addr);
-}
-#endif
-
VALUE *
rb_iseq_original_iseq(const rb_iseq_t *iseq) /* cold path */
{
@@ -807,7 +856,7 @@ compile_data_alloc(rb_iseq_t *iseq, size_t size)
alloc_size *= 2;
}
storage->next = (void *)ALLOC_N(char, alloc_size +
- SIZEOF_ISEQ_COMPILE_DATA_STORAGE);
+ offsetof(struct iseq_compile_data_storage, buff));
storage = ISEQ_COMPILE_DATA(iseq)->storage_current = storage->next;
storage->next = 0;
storage->pos = 0;
@@ -844,11 +893,17 @@ compile_data_alloc_adjust(rb_iseq_t *iseq)
return (ADJUST *)compile_data_alloc(iseq, sizeof(ADJUST));
}
+static TRACE *
+compile_data_alloc_trace(rb_iseq_t *iseq)
+{
+ return (TRACE *)compile_data_alloc(iseq, sizeof(TRACE));
+}
+
/*
* elem1, elemX => elem1, elem2, elemX
*/
static void
-INSERT_ELEM_NEXT(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
+ELEM_INSERT_NEXT(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
{
elem2->next = elem1->next;
elem2->prev = elem1;
@@ -862,7 +917,7 @@ INSERT_ELEM_NEXT(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
* elem1, elemX => elemX, elem2, elem1
*/
static void
-INSERT_ELEM_PREV(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
+ELEM_INSERT_PREV(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
{
elem2->prev = elem1->prev;
elem2->next = elem1;
@@ -872,12 +927,11 @@ INSERT_ELEM_PREV(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
}
}
-#if 0
/*
* elemX, elem1, elemY => elemX, elem2, elemY
*/
static void
-REPLACE_ELEM(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
+ELEM_REPLACE(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
{
elem2->prev = elem1->prev;
elem2->next = elem1->next;
@@ -888,10 +942,9 @@ REPLACE_ELEM(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
elem1->next->prev = elem2;
}
}
-#endif
static void
-REMOVE_ELEM(LINK_ELEMENT *elem)
+ELEM_REMOVE(LINK_ELEMENT *elem)
{
elem->prev->next = elem->next;
if (elem->next) {
@@ -900,19 +953,19 @@ REMOVE_ELEM(LINK_ELEMENT *elem)
}
static LINK_ELEMENT *
-FIRST_ELEMENT(LINK_ANCHOR *anchor)
+FIRST_ELEMENT(const LINK_ANCHOR *const anchor)
{
return anchor->anchor.next;
}
static LINK_ELEMENT *
-LAST_ELEMENT(LINK_ANCHOR *anchor)
+LAST_ELEMENT(LINK_ANCHOR *const anchor)
{
return anchor->last;
}
static LINK_ELEMENT *
-POP_ELEMENT(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor)
+POP_ELEMENT(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor)
{
LINK_ELEMENT *elem = anchor->last;
anchor->last = anchor->last->prev;
@@ -924,14 +977,42 @@ POP_ELEMENT(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor)
#define POP_ELEMENT(anchor) POP_ELEMENT(iseq, (anchor))
#endif
+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;
+ }
+ }
+ return NULL;
+}
+
static int
-LIST_SIZE_ZERO(LINK_ANCHOR *anchor)
+LIST_INSN_SIZE_ONE(const LINK_ANCHOR *const anchor)
{
- if (anchor->anchor.next == 0) {
- return 1;
+ LINK_ELEMENT *first_insn = ELEM_FIRST_INSN(FIRST_ELEMENT(anchor));
+ if (first_insn != NULL &&
+ ELEM_FIRST_INSN(first_insn->next) == NULL) {
+ return TRUE;
}
else {
- return 0;
+ return FALSE;
+ }
+}
+
+static int
+LIST_INSN_SIZE_ZERO(const LINK_ANCHOR *const anchor)
+{
+ if (ELEM_FIRST_INSN(FIRST_ELEMENT(anchor)) == NULL) {
+ return TRUE;
+ }
+ else {
+ return FALSE;
}
}
@@ -943,7 +1024,7 @@ LIST_SIZE_ZERO(LINK_ANCHOR *anchor)
* anc2: e4, e5 (broken)
*/
static void
-APPEND_LIST(ISEQ_ARG_DECLARE LINK_ANCHOR *anc1, LINK_ANCHOR *anc2)
+APPEND_LIST(ISEQ_ARG_DECLARE LINK_ANCHOR *const anc1, LINK_ANCHOR *const anc2)
{
if (anc2->anchor.next) {
anc1->last->next = anc2->anchor.next;
@@ -964,7 +1045,7 @@ APPEND_LIST(ISEQ_ARG_DECLARE LINK_ANCHOR *anc1, LINK_ANCHOR *anc2)
* anc2: e4, e5 (broken)
*/
static void
-INSERT_LIST(ISEQ_ARG_DECLARE LINK_ANCHOR *anc1, LINK_ANCHOR *anc2)
+INSERT_LIST(ISEQ_ARG_DECLARE LINK_ANCHOR *const anc1, LINK_ANCHOR *const anc2)
{
if (anc2->anchor.next) {
LINK_ELEMENT *first = anc1->anchor.next;
@@ -987,7 +1068,7 @@ INSERT_LIST(ISEQ_ARG_DECLARE LINK_ANCHOR *anc1, LINK_ANCHOR *anc2)
#if CPDEBUG && 0
static void
-debug_list(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor)
+debug_list(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor)
{
LINK_ELEMENT *list = FIRST_ELEMENT(anchor);
printf("----\n");
@@ -1006,8 +1087,23 @@ debug_list(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor)
#if CPDEBUG < 0
#define debug_list(anc) debug_list(iseq, (anc))
#endif
+#else
+#define debug_list(anc) ((void)0)
#endif
+static TRACE *
+new_trace_body(rb_iseq_t *iseq, rb_event_flag_t event, long data)
+{
+ TRACE *trace = compile_data_alloc_trace(iseq);
+
+ trace->link.type = ISEQ_ELEMENT_TRACE;
+ trace->link.next = NULL;
+ trace->event = event;
+ trace->data = data;
+
+ return trace;
+}
+
static LABEL *
new_label_body(rb_iseq_t *iseq, long line)
{
@@ -1022,6 +1118,7 @@ new_label_body(rb_iseq_t *iseq, long line)
labelobj->refcnt = 0;
labelobj->set = 0;
labelobj->rescued = LABEL_RESCUE_NONE;
+ labelobj->unremovable = 0;
return labelobj;
}
@@ -1033,7 +1130,7 @@ new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line)
adjust->link.next = 0;
adjust->label = label;
adjust->line_no = line;
- if (label) LABEL_REF(label);
+ LABEL_UNREMOVABLE(label);
return adjust;
}
@@ -1042,12 +1139,14 @@ new_insn_core(rb_iseq_t *iseq, int line_no,
int insn_id, int argc, VALUE *argv)
{
INSN *iobj = compile_data_alloc_insn(iseq);
+
/* printf("insn_id: %d, line: %d\n", insn_id, line_no); */
iobj->link.type = ISEQ_ELEMENT_INSN;
iobj->link.next = 0;
iobj->insn_id = insn_id;
- iobj->line_no = line_no;
+ iobj->insn_info.line_no = line_no;
+ iobj->insn_info.events = 0;
iobj->operands = argv;
iobj->operand_size = argc;
iobj->sc_state = 0;
@@ -1093,7 +1192,7 @@ new_callinfo(rb_iseq_t *iseq, ID mid, int argc, unsigned int flag, struct rb_cal
iseq->body->ci_size++;
}
- if (!(ci->flag & (VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG)) &&
+ if (!(ci->flag & (VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG | VM_CALL_KW_SPLAT)) &&
kw_arg == NULL && !has_blockiseq) {
ci->flag |= VM_CALL_ARGS_SIMPLE;
}
@@ -1111,23 +1210,121 @@ new_insn_send(rb_iseq_t *iseq, int line_no, ID id, VALUE argc, const rb_iseq_t *
}
static rb_iseq_t *
-new_child_iseq(rb_iseq_t *iseq, NODE *node,
+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)
{
rb_iseq_t *ret_iseq;
+ rb_ast_body_t ast;
+
+ ast.root = node;
+ ast.compile_option = 0;
+ ast.line_count = -1;
debugs("[new_child_iseq]> ---------------------------------------\n");
- ret_iseq = rb_iseq_new_with_opt(node, name,
- iseq_path(iseq), iseq_absolute_path(iseq),
+ ret_iseq = rb_iseq_new_with_opt(&ast, name,
+ rb_iseq_path(iseq), rb_iseq_realpath(iseq),
INT2FIX(line_no), parent, type, ISEQ_COMPILE_DATA(iseq)->option);
debugs("[new_child_iseq]< ---------------------------------------\n");
- iseq_add_mark_object(iseq, (VALUE)ret_iseq);
+ iseq_add_mark_object_compile_time(iseq, (VALUE)ret_iseq);
return ret_iseq;
}
+static rb_iseq_t *
+new_child_iseq_ifunc(rb_iseq_t *iseq, const struct vm_ifunc *ifunc,
+ VALUE name, const rb_iseq_t *parent, enum iseq_type type, int line_no)
+{
+ rb_iseq_t *ret_iseq;
+
+ debugs("[new_child_iseq_ifunc]> ---------------------------------------\n");
+ ret_iseq = rb_iseq_new_ifunc(ifunc, name,
+ rb_iseq_path(iseq), rb_iseq_realpath(iseq),
+ INT2FIX(line_no), parent, type, ISEQ_COMPILE_DATA(iseq)->option);
+ debugs("[new_child_iseq_ifunc]< ---------------------------------------\n");
+ iseq_add_mark_object_compile_time(iseq, (VALUE)ret_iseq);
+ return ret_iseq;
+}
+
+static void
+set_catch_except_p(struct rb_iseq_constant_body *body)
+{
+ body->catch_except_p = TRUE;
+ if (body->parent_iseq != NULL) {
+ set_catch_except_p(body->parent_iseq->body);
+ }
+}
+
+/* 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
+ whose child ISeq would really raise an exception. */
+static void
+update_catch_except_flags(struct rb_iseq_constant_body *body)
+{
+ unsigned int pos;
+ size_t i;
+ int insn;
+ const struct iseq_catch_table *ct = body->catch_table;
+
+ /* This assumes that a block has parent_iseq which may catch an exception from the block, and that
+ BREAK/NEXT/REDO catch table entries are used only when `throw` insn is used in the block. */
+ pos = 0;
+ while (pos < body->iseq_size) {
+#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
+ insn = rb_vm_insn_addr2insn((void *)body->iseq_encoded[pos]);
+#else
+ insn = (int)body->iseq_encoded[pos];
+#endif
+ if (insn == BIN(throw)) {
+ set_catch_except_p(body);
+ break;
+ }
+ pos += insn_len(insn);
+ }
+
+ if (ct == NULL)
+ return;
+
+ for (i = 0; i < ct->size; i++) {
+ const struct iseq_catch_table_entry *entry = &ct->entries[i];
+ if (entry->type != CATCH_TYPE_BREAK
+ && entry->type != CATCH_TYPE_NEXT
+ && entry->type != CATCH_TYPE_REDO) {
+ body->catch_except_p = TRUE;
+ break;
+ }
+ }
+}
+
+static void
+iseq_insert_nop_between_end_and_cont(rb_iseq_t *iseq)
+{
+ VALUE catch_table_ary = ISEQ_COMPILE_DATA(iseq)->catch_table_ary;
+ unsigned int i, tlen = (unsigned int)RARRAY_LEN(catch_table_ary);
+ const VALUE *tptr = RARRAY_CONST_PTR_TRANSIENT(catch_table_ary);
+ for (i = 0; i < tlen; i++) {
+ const VALUE *ptr = RARRAY_CONST_PTR_TRANSIENT(tptr[i]);
+ LINK_ELEMENT *end = (LINK_ELEMENT *)(ptr[2] & ~1);
+ LINK_ELEMENT *cont = (LINK_ELEMENT *)(ptr[4] & ~1);
+ LINK_ELEMENT *e;
+ for (e = end; e && (IS_LABEL(e) || IS_TRACE(e)); e = e->next) {
+ if (e == cont) {
+ INSN *nop = new_insn_core(iseq, 0, BIN(nop), 0, 0);
+ ELEM_INSERT_NEXT(end, &nop->link);
+ break;
+ }
+ }
+ }
+}
+
static int
-iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
+iseq_setup_insn(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
{
+ if (RTEST(ISEQ_COMPILE_DATA(iseq)->err_info))
+ return COMPILE_NG;
+
/* debugs("[compile step 2] (iseq_array_to_linkedlist)\n"); */
if (compile_debug > 5)
@@ -1153,6 +1350,18 @@ iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
dump_disasm_list(FIRST_ELEMENT(anchor));
}
+ debugs("[compile step 3.4 (iseq_insert_nop_between_end_and_cont)]\n");
+ iseq_insert_nop_between_end_and_cont(iseq);
+
+ return COMPILE_OK;
+}
+
+static int
+iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
+{
+ if (RTEST(ISEQ_COMPILE_DATA(iseq)->err_info))
+ return COMPILE_NG;
+
debugs("[compile step 4.1 (iseq_set_sequence)]\n");
if (!iseq_set_sequence(iseq, anchor)) return COMPILE_NG;
if (compile_debug > 5)
@@ -1167,6 +1376,8 @@ iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
debugs("[compile step 5 (iseq_translate_threaded_code)] \n");
if (!rb_iseq_translate_threaded_code(iseq)) return COMPILE_NG;
+ update_catch_except_flags(iseq->body);
+
if (compile_debug > 1) {
VALUE str = rb_iseq_disasm(iseq);
printf("%s\n", StringValueCStr(str));
@@ -1222,7 +1433,8 @@ get_local_var_idx(const rb_iseq_t *iseq, ID id)
int idx = get_dyna_var_idx_at_raw(iseq->body->local_iseq, id);
if (idx < 0) {
- rb_bug("get_local_var_idx: %d", idx);
+ COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq),
+ "get_local_var_idx: %d", idx);
}
return idx;
@@ -1232,6 +1444,7 @@ static int
get_dyna_var_idx(const rb_iseq_t *iseq, ID id, int *level, int *ls)
{
int lv = 0, idx = -1;
+ const rb_iseq_t *const topmost_iseq = iseq;
while (iseq) {
idx = get_dyna_var_idx_at_raw(iseq, id);
@@ -1243,7 +1456,8 @@ get_dyna_var_idx(const rb_iseq_t *iseq, ID id, int *level, int *ls)
}
if (idx < 0) {
- rb_bug("get_dyna_var_idx: -1");
+ COMPILE_ERROR(topmost_iseq, ISEQ_LAST_LINE(topmost_iseq),
+ "get_dyna_var_idx: -1");
}
*level = lv;
@@ -1251,68 +1465,136 @@ get_dyna_var_idx(const rb_iseq_t *iseq, ID id, int *level, int *ls)
return idx;
}
+static int
+iseq_local_block_param_p(const rb_iseq_t *iseq, unsigned int idx, unsigned int level)
+{
+ const struct rb_iseq_constant_body *body;
+ while (level > 0) {
+ iseq = iseq->body->parent_iseq;
+ level--;
+ }
+ body = iseq->body;
+ if (body->local_iseq == iseq && /* local variables */
+ body->param.flags.has_block &&
+ body->local_table_size - body->param.block_start == idx) {
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+static int
+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;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+static void
+iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level)
+{
+ if (iseq_local_block_param_p(iseq, idx, level)) {
+ ADD_INSN2(seq, line, getblockparam, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
+ }
+ else {
+ ADD_INSN2(seq, line, getlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
+ }
+}
+
+static void
+iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level)
+{
+ if (iseq_local_block_param_p(iseq, idx, level)) {
+ ADD_INSN2(seq, line, setblockparam, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
+ }
+ else {
+ ADD_INSN2(seq, line, setlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
+ }
+}
+
+
+
static void
iseq_calc_param_size(rb_iseq_t *iseq)
{
- if (iseq->body->param.flags.has_opt ||
- iseq->body->param.flags.has_post ||
- iseq->body->param.flags.has_rest ||
- iseq->body->param.flags.has_block ||
- iseq->body->param.flags.has_kw ||
- iseq->body->param.flags.has_kwrest) {
+ struct rb_iseq_constant_body *const body = iseq->body;
+ 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 (iseq->body->param.flags.has_block) {
- iseq->body->param.size = iseq->body->param.block_start + 1;
+ if (body->param.flags.has_block) {
+ body->param.size = body->param.block_start + 1;
}
- else if (iseq->body->param.flags.has_kwrest) {
- iseq->body->param.size = iseq->body->param.keyword->rest_start + 1;
+ else if (body->param.flags.has_kwrest) {
+ body->param.size = body->param.keyword->rest_start + 1;
}
- else if (iseq->body->param.flags.has_kw) {
- iseq->body->param.size = iseq->body->param.keyword->bits_start + 1;
+ else if (body->param.flags.has_kw) {
+ body->param.size = body->param.keyword->bits_start + 1;
}
- else if (iseq->body->param.flags.has_post) {
- iseq->body->param.size = iseq->body->param.post_start + iseq->body->param.post_num;
+ else if (body->param.flags.has_post) {
+ body->param.size = body->param.post_start + body->param.post_num;
}
- else if (iseq->body->param.flags.has_rest) {
- iseq->body->param.size = iseq->body->param.rest_start + 1;
+ else if (body->param.flags.has_rest) {
+ body->param.size = body->param.rest_start + 1;
}
- else if (iseq->body->param.flags.has_opt) {
- iseq->body->param.size = iseq->body->param.lead_num + iseq->body->param.opt_num;
+ else if (body->param.flags.has_opt) {
+ body->param.size = body->param.lead_num + body->param.opt_num;
}
else {
- rb_bug("unreachable");
+ UNREACHABLE;
}
}
else {
- iseq->body->param.size = iseq->body->param.lead_num;
+ body->param.size = body->param.lead_num;
}
}
-static void
-iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *optargs, const struct rb_args_info *args)
+static int
+iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs,
+ const struct rb_args_info *args, int arg_size)
{
- NODE *node = args->kw_args;
+ const NODE *node = args->kw_args;
+ struct rb_iseq_constant_body *const body = iseq->body;
struct rb_iseq_param_keyword *keyword;
const VALUE default_values = rb_ary_tmp_new(1);
const VALUE complex_mark = rb_str_tmp_new(0);
int kw = 0, rkw = 0, di = 0, i;
- iseq->body->param.flags.has_kw = TRUE;
- iseq->body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
- keyword->bits_start = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_vid);
+ body->param.flags.has_kw = TRUE;
+ body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
while (node) {
- NODE *val_node = node->nd_body->nd_value;
+ 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;
- if (val_node == (NODE *)-1) {
+ if (val_node == (const NODE *)-1) {
++rkw;
}
else {
switch (nd_type(val_node)) {
case NODE_LIT:
dv = val_node->nd_lit;
- iseq_add_mark_object(iseq, dv);
+ iseq_add_mark_object_compile_time(iseq, dv);
break;
case NODE_NIL:
dv = Qnil;
@@ -1324,7 +1606,7 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *optargs, const struct
dv = Qfalse;
break;
default:
- COMPILE_POPED(optargs, "kwarg", node); /* nd_type(node) == NODE_KW_ARG */
+ COMPILE_POPPED(optargs, "kwarg", node); /* nd_type(node) == NODE_KW_ARG */
dv = complex_mark;
}
@@ -1332,18 +1614,17 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *optargs, const struct
rb_ary_push(default_values, dv);
}
- kw++;
node = node->nd_next;
}
keyword->num = kw;
- if (args->kw_rest_arg->nd_cflag != 0) {
- keyword->rest_start = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_cflag);
- iseq->body->param.flags.has_kwrest = TRUE;
+ if (args->kw_rest_arg->nd_vid != 0) {
+ keyword->rest_start = arg_size++;
+ body->param.flags.has_kwrest = TRUE;
}
keyword->required_num = rkw;
- keyword->table = &iseq->body->local_table[keyword->bits_start - keyword->num];
+ keyword->table = &body->local_table[keyword->bits_start - keyword->num];
{
VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values));
@@ -1351,29 +1632,35 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *optargs, const struct
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;
}
+ return arg_size;
}
static int
-iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *optargs, NODE *node_args)
+iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *const node_args)
{
debugs("iseq_set_arguments: %s\n", node_args ? "" : "0");
if (node_args) {
+ struct rb_iseq_constant_body *const body = iseq->body;
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);
+ EXPECT_NODE("iseq_set_arguments", node_args, NODE_ARGS, COMPILE_NG);
- iseq->body->param.lead_num = (int)args->pre_args_num;
- if (iseq->body->param.lead_num > 0) iseq->body->param.flags.has_lead = TRUE;
- debugs(" - argc: %d\n", iseq->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;
if (rest_id == 1) {
@@ -1382,14 +1669,8 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *optargs, NODE *node_args)
}
block_id = args->block_arg;
- if (args->first_post_arg) {
- iseq->body->param.post_start = get_dyna_var_idx_at_raw(iseq, args->first_post_arg);
- iseq->body->param.post_num = args->post_args_num;
- iseq->body->param.flags.has_post = TRUE;
- }
-
if (args->opt_args) {
- NODE *node = args->opt_args;
+ const NODE *node = args->opt_args;
LABEL *label;
VALUE labels = rb_ary_tmp_new(1);
VALUE *opt_table;
@@ -1399,7 +1680,7 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *optargs, NODE *node_args)
label = NEW_LABEL(nd_line(node));
rb_ary_push(labels, (VALUE)label | 1);
ADD_LABEL(optargs, label);
- COMPILE_POPED(optargs, "optarg", node->nd_body);
+ COMPILE_POPPED(optargs, "optarg", node->nd_body);
node = node->nd_next;
i += 1;
}
@@ -1411,61 +1692,70 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *optargs, NODE *node_args)
opt_table = ALLOC_N(VALUE, i+1);
- MEMCPY(opt_table, RARRAY_CONST_PTR(labels), 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);
- iseq->body->param.flags.has_opt = TRUE;
- iseq->body->param.opt_num = i;
- iseq->body->param.opt_table = opt_table;
+ 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) {
- iseq_set_arguments_keywords(iseq, optargs, 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 = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_vid);
- iseq->body->param.keyword = keyword;
- iseq->body->param.flags.has_kwrest = TRUE;
+ keyword->rest_start = arg_size++;
+ body->param.keyword = keyword;
+ body->param.flags.has_kwrest = TRUE;
}
- if (args->pre_init) { /* m_init */
- COMPILE_POPED(optargs, "init arguments (m)", args->pre_init);
- }
- if (args->post_init) { /* p_init */
- COMPILE_POPED(optargs, "init arguments (p)", args->post_init);
+ if (block_id) {
+ body->param.block_start = arg_size++;
+ body->param.flags.has_block = TRUE;
}
- if (rest_id) {
- iseq->body->param.rest_start = get_dyna_var_idx_at_raw(iseq, rest_id);
- iseq->body->param.flags.has_rest = TRUE;
- assert(iseq->body->param.rest_start != -1);
+ iseq_calc_param_size(iseq);
+ body->param.size = arg_size;
- if (iseq->body->param.post_start == 0) { /* TODO: why that? */
- iseq->body->param.post_start = iseq->body->param.rest_start + 1;
- }
+ if (args->pre_init) { /* m_init */
+ COMPILE_POPPED(optargs, "init arguments (m)", args->pre_init);
}
-
- if (block_id) {
- iseq->body->param.block_start = get_dyna_var_idx_at_raw(iseq, block_id);
- iseq->body->param.flags.has_block = TRUE;
+ if (args->post_init) { /* p_init */
+ COMPILE_POPPED(optargs, "init arguments (p)", args->post_init);
}
- iseq_calc_param_size(iseq);
-
- if (iseq->body->type == ISEQ_TYPE_BLOCK) {
- if (iseq->body->param.flags.has_opt == FALSE &&
- iseq->body->param.flags.has_post == FALSE &&
- iseq->body->param.flags.has_rest == FALSE &&
- iseq->body->param.flags.has_kw == FALSE &&
- iseq->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 (iseq->body->param.lead_num == 1 && last_comma == 0) {
+ if (body->param.lead_num == 1 && last_comma == 0) {
/* {|a|} */
- iseq->body->param.flags.ambiguous_param0 = TRUE;
+ body->param.flags.ambiguous_param0 = TRUE;
}
}
}
@@ -1501,27 +1791,57 @@ iseq_set_local_table(rb_iseq_t *iseq, const ID *tbl)
static int
cdhash_cmp(VALUE val, VALUE lit)
{
- if (val == lit) return 0;
- if (SPECIAL_CONST_P(lit)) {
- return val != lit;
+ int tval, tlit;
+
+ if (val == lit) {
+ return 0;
}
- if (SPECIAL_CONST_P(val) || BUILTIN_TYPE(val) != BUILTIN_TYPE(lit)) {
- return -1;
+ else if ((tlit = OBJ_BUILTIN_TYPE(lit)) == -1) {
+ return val != lit;
+ }
+ else if ((tval = OBJ_BUILTIN_TYPE(val)) == -1) {
+ return -1;
}
- if (BUILTIN_TYPE(lit) == T_STRING) {
- return rb_str_hash_cmp(lit, val);
+ else if (tlit != tval) {
+ return -1;
+ }
+ else if (tlit == T_SYMBOL) {
+ return val != lit;
+ }
+ else if (tlit == T_STRING) {
+ return rb_str_hash_cmp(lit, val);
+ }
+ else if (tlit == T_BIGNUM) {
+ long x = FIX2LONG(rb_big_cmp(lit, val));
+
+ /* Given lit and val are both Bignum, x must be -1, 0, 1.
+ * There is no need to call rb_fix2int here. */
+ RUBY_ASSERT((x == 1) || (x == 0) || (x == -1));
+ return (int)x;
+ }
+ else if (tlit == T_FLOAT) {
+ return rb_float_cmp(lit, val);
+ }
+ else {
+ UNREACHABLE_RETURN(-1);
}
- return !rb_eql(lit, val);
}
static st_index_t
cdhash_hash(VALUE a)
{
- if (SPECIAL_CONST_P(a)) return (st_index_t)a;
- if (RB_TYPE_P(a, T_STRING)) return rb_str_hash(a);
- {
- VALUE hval = rb_hash(a);
- return (st_index_t)FIX2LONG(hval);
+ switch (OBJ_BUILTIN_TYPE(a)) {
+ case -1:
+ case T_SYMBOL:
+ return (st_index_t)a;
+ case T_STRING:
+ return rb_str_hash(a);
+ case T_BIGNUM:
+ return FIX2LONG(rb_big_hash(a));
+ case T_FLOAT:
+ return rb_dbl_long_hash(RFLOAT_VALUE(a));
+ default:
+ UNREACHABLE_RETURN(0);
}
}
@@ -1565,18 +1885,170 @@ get_ivar_ic_value(rb_iseq_t *iseq,ID id)
return val;
}
+#define BADINSN_DUMP(anchor, list, dest) \
+ dump_disasm_list_with_cursor(FIRST_ELEMENT(anchor), list, dest)
+
+#define BADINSN_ERROR \
+ (xfree(generated_iseq), \
+ xfree(insns_info), \
+ BADINSN_DUMP(anchor, list, NULL), \
+ COMPILE_ERROR)
+
+static int
+fix_sp_depth(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
+{
+ int stack_max = 0, sp = 0, line = 0;
+ LINK_ELEMENT *list;
+
+ for (list = FIRST_ELEMENT(anchor); list; list = list->next) {
+ if (list->type == ISEQ_ELEMENT_LABEL) {
+ 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;
+ }
+ }
+ }
+ break;
+ }
+ case ISEQ_ELEMENT_LABEL:
+ {
+ LABEL *lobj = (LABEL *)list;
+ if (lobj->sp == -1) {
+ lobj->sp = sp;
+ }
+ else {
+ 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;
+}
+
+static int
+add_insn_info(struct iseq_insn_info_entry *insns_info, unsigned int *positions,
+ int insns_info_index, int code_index, const INSN *iobj)
+{
+ if (insns_info_index == 0 ||
+ insns_info[insns_info_index-1].line_no != iobj->insn_info.line_no ||
+ insns_info[insns_info_index-1].events != iobj->insn_info.events) {
+ insns_info[insns_info_index].line_no = iobj->insn_info.line_no;
+ insns_info[insns_info_index].events = iobj->insn_info.events;
+ positions[insns_info_index] = code_index;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int
+add_adjust_info(struct iseq_insn_info_entry *insns_info, unsigned int *positions,
+ int insns_info_index, int code_index, const ADJUST *adjust)
+{
+ if (insns_info_index > 0 ||
+ insns_info[insns_info_index-1].line_no != adjust->line_no) {
+ insns_info[insns_info_index].line_no = adjust->line_no;
+ insns_info[insns_info_index].events = 0;
+ positions[insns_info_index] = code_index;
+ return TRUE;
+ }
+ return FALSE;
+}
+
/**
ruby insn object list -> raw instruction sequence
*/
static int
-iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
+iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
{
- struct iseq_line_info_entry *line_info_table;
- unsigned int last_line = 0;
+ struct iseq_insn_info_entry *insns_info;
+ struct rb_iseq_constant_body *const body = iseq->body;
+ unsigned int *positions;
LINK_ELEMENT *list;
VALUE *generated_iseq;
+ rb_event_flag_t events = 0;
+ long data = 0;
+
+ int insn_num, code_index, insns_info_index, sp = 0;
+ int stack_max = fix_sp_depth(iseq, anchor);
- int insn_num, code_index, line_info_index, sp, stack_max = 0, line = 0;
+ if (stack_max < 0) return COMPILE_NG;
/* fix label position */
list = FIRST_ELEMENT(anchor);
@@ -1586,53 +2058,77 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
case ISEQ_ELEMENT_INSN:
{
INSN *iobj = (INSN *)list;
- line = iobj->line_no;
- code_index += insn_data_length(iobj);
+ /* 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));
+ }
+ }
+ if (ISEQ_BRANCH_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_BRANCH)) {
+ while (RARRAY_LEN(ISEQ_PC2BRANCHINDEX(iseq)) <= code_index) {
+ rb_ary_push(ISEQ_PC2BRANCHINDEX(iseq), Qnil);
+ }
+ RARRAY_ASET(ISEQ_PC2BRANCHINDEX(iseq), code_index, INT2FIX(data));
+ }
+ }
+ code_index += insn_data_length(iobj);
+ events = 0;
+ data = 0;
break;
}
case ISEQ_ELEMENT_LABEL:
{
LABEL *lobj = (LABEL *)list;
lobj->position = code_index;
- lobj->set = TRUE;
+ sp = lobj->sp;
break;
}
- case ISEQ_ELEMENT_NONE:
+ case ISEQ_ELEMENT_TRACE:
{
- /* ignore */
+ 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) {
- code_index += 2 /* insn + 1 operand */;
- insn_num++;
+ 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:
- dump_disasm_list(FIRST_ELEMENT(anchor));
- dump_disasm_list(list);
- COMPILE_ERROR(iseq, line, "error: set_sequence");
- return COMPILE_NG;
+ default: break;
}
list = list->next;
}
/* make instruction sequence */
generated_iseq = ALLOC_N(VALUE, code_index);
- line_info_table = ALLOC_N(struct iseq_line_info_entry, insn_num);
- iseq->body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, iseq->body->is_size);
- iseq->body->ci_entries = (struct rb_call_info *)ruby_xmalloc(sizeof(struct rb_call_info) * iseq->body->ci_size +
- sizeof(struct rb_call_info_with_kwarg) * iseq->body->ci_kw_size);
- iseq->body->cc_entries = ZALLOC_N(struct rb_call_cache, iseq->body->ci_size + iseq->body->ci_kw_size);
+ insns_info = ALLOC_N(struct iseq_insn_info_entry, insn_num);
+ positions = ALLOC_N(unsigned int, insn_num);
+ body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, body->is_size);
+ body->ci_entries = (struct rb_call_info *)ruby_xmalloc(sizeof(struct rb_call_info) * body->ci_size +
+ sizeof(struct rb_call_info_with_kwarg) * body->ci_kw_size);
+ MEMZERO(body->ci_entries + body->ci_size, struct rb_call_info_with_kwarg, body->ci_kw_size); /* need to clear ci_kw entries */
+ body->cc_entries = ZALLOC_N(struct rb_call_cache, body->ci_size + body->ci_kw_size);
ISEQ_COMPILE_DATA(iseq)->ci_index = ISEQ_COMPILE_DATA(iseq)->ci_kw_index = 0;
list = FIRST_ELEMENT(anchor);
- line_info_index = code_index = sp = 0;
+ insns_info_index = code_index = sp = 0;
while (list) {
switch (list->type) {
@@ -1645,10 +2141,6 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
/* update sp */
sp = calc_sp_depth(sp, iobj);
- if (sp > stack_max) {
- stack_max = sp;
- }
-
/* fprintf(stderr, "insn: %-16s, sp: %d\n", insn_name(iobj->insn_id), sp); */
operands = iobj->operands;
insn = iobj->insn_id;
@@ -1656,18 +2148,6 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
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); */
- dump_disasm_list(list);
- xfree(generated_iseq);
- xfree(line_info_table);
- COMPILE_ERROR(iseq, iobj->line_no,
- "operand size miss! (%d for %d)",
- iobj->operand_size, len - 1);
- return COMPILE_NG;
- }
-
for (j = 0; types[j]; j++) {
char type = types[j];
/* printf("--> [%c - (%d-%d)]\n", type, k, j); */
@@ -1676,14 +2156,6 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
{
/* label(destination position) */
LABEL *lobj = (LABEL *)operands[j];
- if (!lobj->set) {
- COMPILE_ERROR(iseq, iobj->line_no,
- "unknown label");
- return COMPILE_NG;
- }
- if (lobj->sp == -1) {
- lobj->sp = sp;
- }
generated_iseq[code_index + 1 + j] = lobj->position - (code_index + len);
break;
}
@@ -1699,32 +2171,38 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
rb_hash_rehash(map);
freeze_hide_obj(map);
generated_iseq[code_index + 1 + j] = map;
+ RB_OBJ_WRITTEN(iseq, Qundef, map);
+ FL_SET(iseq, ISEQ_MARKABLE_ISEQ);
break;
}
case TS_LINDEX:
case TS_NUM: /* ulong */
generated_iseq[code_index + 1 + j] = FIX2INT(operands[j]);
break;
- case TS_ISEQ: /* iseq */
- {
- VALUE v = operands[j];
- generated_iseq[code_index + 1 + j] = v;
- break;
- }
case TS_VALUE: /* VALUE */
+ case TS_ISEQ: /* iseq */
{
VALUE v = operands[j];
generated_iseq[code_index + 1 + j] = v;
/* to mark ruby object */
- iseq_add_mark_object(iseq, v);
+ if (!SPECIAL_CONST_P(v)) {
+ RB_OBJ_WRITTEN(iseq, Qundef, v);
+ FL_SET(iseq, ISEQ_MARKABLE_ISEQ);
+ }
break;
}
+ case TS_ISE: /* inline storage entry */
+ /* Treated as an IC, but may contain a markable VALUE */
+ FL_SET(iseq, ISEQ_MARKABLE_ISEQ);
case TS_IC: /* inline cache */
{
unsigned int ic_index = FIX2UINT(operands[j]);
- IC ic = (IC)&iseq->body->is_entries[ic_index];
- if (UNLIKELY(ic_index >= iseq->body->is_size)) {
- rb_bug("iseq_set_sequence: ic_index overflow: index: %d, size: %d", ic_index, iseq->body->is_size);
+ IC ic = (IC)&body->is_entries[ic_index];
+ if (UNLIKELY(ic_index >= body->is_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, body->is_size);
}
generated_iseq[code_index + 1 + j] = (VALUE)ic;
break;
@@ -1735,16 +2213,16 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
struct rb_call_info *ci;
if (base_ci->flag & VM_CALL_KWARG) {
- struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&iseq->body->ci_entries[iseq->body->ci_size];
+ struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&body->ci_entries[body->ci_size];
struct rb_call_info_with_kwarg *ci_kw = &ci_kw_entries[ISEQ_COMPILE_DATA(iseq)->ci_kw_index++];
*ci_kw = *((struct rb_call_info_with_kwarg *)base_ci);
ci = (struct rb_call_info *)ci_kw;
- assert(ISEQ_COMPILE_DATA(iseq)->ci_kw_index <= iseq->body->ci_kw_size);
+ assert(ISEQ_COMPILE_DATA(iseq)->ci_kw_index <= body->ci_kw_size);
}
else {
- ci = &iseq->body->ci_entries[ISEQ_COMPILE_DATA(iseq)->ci_index++];
+ ci = &body->ci_entries[ISEQ_COMPILE_DATA(iseq)->ci_index++];
*ci = *base_ci;
- assert(ISEQ_COMPILE_DATA(iseq)->ci_index <= iseq->body->ci_size);
+ assert(ISEQ_COMPILE_DATA(iseq)->ci_index <= body->ci_size);
}
generated_iseq[code_index + 1 + j] = (VALUE)ci;
@@ -1752,7 +2230,7 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
}
case TS_CALLCACHE:
{
- struct rb_call_cache *cc = &iseq->body->cc_entries[ISEQ_COMPILE_DATA(iseq)->ci_index + ISEQ_COMPILE_DATA(iseq)->ci_kw_index - 1];
+ struct rb_call_cache *cc = &body->cc_entries[ISEQ_COMPILE_DATA(iseq)->ci_index + ISEQ_COMPILE_DATA(iseq)->ci_kw_index - 1];
generated_iseq[code_index + 1 + j] = (VALUE)cc;
break;
}
@@ -1770,30 +2248,19 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
generated_iseq[code_index + 1 + j] = operands[j];
break;
default:
- xfree(generated_iseq);
- xfree(line_info_table);
- COMPILE_ERROR(iseq, iobj->line_no,
+ BADINSN_ERROR(iseq, iobj->insn_info.line_no,
"unknown operand type: %c", type);
return COMPILE_NG;
}
}
- if (last_line != iobj->line_no) {
- line_info_table[line_info_index].line_no = last_line = iobj->line_no;
- line_info_table[line_info_index].position = code_index;
- line_info_index++;
- }
+ 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 == -1) {
- lobj->sp = sp;
- }
- else {
- sp = lobj->sp;
- }
+ sp = lobj->sp;
break;
}
case ISEQ_ELEMENT_ADJUST:
@@ -1809,29 +2276,27 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
}
if (adjust->line_no != -1) {
- if (orig_sp - sp > 0) {
- if (last_line != (unsigned int)adjust->line_no) {
- line_info_table[line_info_index].line_no = last_line = adjust->line_no;
- line_info_table[line_info_index].position = code_index;
- line_info_index++;
- }
+ const int diff = orig_sp - sp;
+ if (diff > 0) {
+ 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 (orig_sp - sp == 0) {
- /* jump to next insn */
- if (last_line != (unsigned int)adjust->line_no) {
- line_info_table[line_info_index].line_no = last_line = adjust->line_no;
- line_info_table[line_info_index].position = code_index;
- line_info_index++;
- }
- generated_iseq[code_index++] = BIN(nop);
- generated_iseq[code_index++] = BIN(nop);
+ else if (diff == 1) {
+ generated_iseq[code_index++] = BIN(pop);
}
- else {
- compile_bug(iseq, adjust->line_no,
- "iseq_set_sequence: adjust bug %d < %d",
- orig_sp, sp);
+ else if (diff < 0) {
+ int label_no = adjust->label ? adjust->label->label_no : -1;
+ xfree(generated_iseq);
+ xfree(insns_info);
+ xfree(positions);
+ debug_list(anchor);
+ COMPILE_ERROR(iseq, adjust->line_no,
+ "iseq_set_sequence: adjust bug to %d %d < %d",
+ label_no, orig_sp, sp);
+ return COMPILE_NG;
}
}
break;
@@ -1843,13 +2308,19 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
list = list->next;
}
- iseq->body->iseq_encoded = (void *)generated_iseq;
- iseq->body->iseq_size = code_index;
- iseq->body->stack_max = stack_max;
+ body->iseq_encoded = (void *)generated_iseq;
+ body->iseq_size = code_index;
+ body->stack_max = stack_max;
- REALLOC_N(line_info_table, struct iseq_line_info_entry, line_info_index);
- iseq->body->line_info_table = line_info_table;
- iseq->body->line_info_size = line_info_index;
+ /* get rid of memory leak when REALLOC failed */
+ body->insns_info.body = insns_info;
+ body->insns_info.positions = positions;
+
+ REALLOC_N(insns_info, struct iseq_insn_info_entry, insns_info_index);
+ body->insns_info.body = insns_info;
+ REALLOC_N(positions, unsigned int, insns_info_index);
+ body->insns_info.positions = positions;
+ body->insns_info.size = insns_info_index;
return COMPILE_OK;
}
@@ -1874,24 +2345,20 @@ iseq_set_exception_table(rb_iseq_t *iseq)
struct iseq_catch_table_entry *entry;
tlen = (int)RARRAY_LEN(ISEQ_COMPILE_DATA(iseq)->catch_table_ary);
- tptr = RARRAY_CONST_PTR(ISEQ_COMPILE_DATA(iseq)->catch_table_ary);
+ tptr = RARRAY_CONST_PTR_TRANSIENT(ISEQ_COMPILE_DATA(iseq)->catch_table_ary);
if (tlen > 0) {
struct iseq_catch_table *table = xmalloc(iseq_catch_table_bytes(tlen));
table->size = tlen;
for (i = 0; i < table->size; i++) {
- ptr = RARRAY_CONST_PTR(tptr[i]);
+ ptr = RARRAY_CONST_PTR_TRANSIENT(tptr[i]);
entry = &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];
-
- /* register iseq as mark object */
- if (entry->iseq != 0) {
- iseq_add_mark_object(iseq, (VALUE)entry->iseq);
- }
+ RB_OBJ_WRITTEN(iseq, Qundef, entry->iseq);
/* stack depth */
if (ptr[4]) {
@@ -1948,14 +2415,32 @@ get_destination_insn(INSN *iobj)
{
LABEL *lobj = (LABEL *)OPERAND_AT(iobj, 0);
LINK_ELEMENT *list;
+ rb_event_flag_t events = 0;
list = lobj->link.next;
while (list) {
- if (IS_INSN(list) || IS_ADJUST(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;
}
+ found:
+ if (list && IS_INSN(list)) {
+ INSN *iobj = (INSN *)list;
+ iobj->insn_info.events |= events;
+ }
return list;
}
@@ -1992,7 +2477,7 @@ unref_destination(INSN *iobj, int pos)
{
LABEL *lobj = (LABEL *)OPERAND_AT(iobj, pos);
--lobj->refcnt;
- if (!lobj->refcnt) REMOVE_ELEM(&lobj->link);
+ if (!lobj->refcnt) ELEM_REMOVE(&lobj->link);
}
static void
@@ -2004,14 +2489,63 @@ replace_destination(INSN *dobj, INSN *nobj)
--dl->refcnt;
++nl->refcnt;
OPERAND_AT(dobj, 0) = n;
- if (!dl->refcnt) REMOVE_ELEM(&dl->link);
+ if (!dl->refcnt) ELEM_REMOVE(&dl->link);
+}
+
+static LABEL*
+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);
+ }
+ }
+ return 0;
}
static int
remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i)
{
- int removed = 0;
- while (i) {
+ LINK_ELEMENT *first = i, *end;
+ int *unref_counts = 0, nlabels = ISEQ_COMPILE_DATA(iseq)->label_no;
+
+ if (!i) return 0;
+ unref_counts = ALLOCA_N(int, nlabels);
+ 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;
+ } while ((i = i->next) != 0);
+ i = first;
+ do {
if (IS_INSN(i)) {
struct rb_iseq_constant_body *body = iseq->body;
VALUE insn = INSN_OF(i);
@@ -2030,34 +2564,152 @@ remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i)
}
}
}
- else if (IS_LABEL(i)) {
- if (((LABEL *)i)->refcnt > 0) break;
+ ELEM_REMOVE(i);
+ } while ((i != end) && (i = i->next) != 0);
+ return 1;
+}
+
+static int
+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;
+ case INT2FIX(1): /* single element array */
+ ELEM_REMOVE(&iobj->link);
+ return FALSE;
+ default:
+ iobj->insn_id = BIN(adjuststack);
+ return TRUE;
+ }
+}
+
+static int
+same_debug_pos_p(LINK_ELEMENT *iobj1, LINK_ELEMENT *iobj2)
+{
+ VALUE debug1 = OPERAND_AT(iobj1, 0);
+ VALUE debug2 = OPERAND_AT(iobj2, 0);
+ if (debug1 == debug2) return TRUE;
+ if (!RB_TYPE_P(debug1, T_ARRAY)) return FALSE;
+ if (!RB_TYPE_P(debug2, T_ARRAY)) return FALSE;
+ if (RARRAY_LEN(debug1) != 2) return FALSE;
+ if (RARRAY_LEN(debug2) != 2) return FALSE;
+ if (RARRAY_AREF(debug1, 0) != RARRAY_AREF(debug2, 0)) return FALSE;
+ if (RARRAY_AREF(debug1, 1) != RARRAY_AREF(debug2, 1)) return FALSE;
+ return TRUE;
+}
+
+static int
+is_frozen_putstring(INSN *insn, VALUE *op)
+{
+ if (IS_INSN_ID(insn, putstring)) {
+ *op = OPERAND_AT(insn, 0);
+ return 1;
+ }
+ else if (IS_INSN_ID(insn, putobject)) { /* frozen_string_literal */
+ *op = OPERAND_AT(insn, 0);
+ return RB_TYPE_P(*op, T_STRING);
+ }
+ return 0;
+}
+
+static int
+optimize_checktype(rb_iseq_t *iseq, INSN *iobj)
+{
+ /*
+ * putobject obj
+ * dup
+ * checktype T_XXX
+ * branchif l1
+ * l2:
+ * ...
+ * l1:
+ *
+ * => obj is a T_XXX
+ *
+ * putobject obj (T_XXX)
+ * jump L1
+ * L1:
+ *
+ * => obj is not a T_XXX
+ *
+ * putobject obj (T_XXX)
+ * jump L2
+ * L2:
+ */
+ int line;
+ INSN *niobj, *ciobj, *dup = 0;
+ LABEL *dest = 0;
+ VALUE type;
+
+ switch (INSN_OF(iobj)) {
+ case BIN(putstring):
+ type = INT2FIX(T_STRING);
+ break;
+ case BIN(putnil):
+ type = INT2FIX(T_NIL);
+ break;
+ case BIN(putobject):
+ 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));
+ }
+ if (IS_INSN_ID(ciobj, dup)) {
+ ciobj = (INSN *)get_next_insn(dup = ciobj);
+ }
+ if (!ciobj || !IS_INSN_ID(ciobj, checktype)) return FALSE;
+ niobj = (INSN *)get_next_insn(ciobj);
+ if (!niobj) {
+ no_branch:
+ /* 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;
+ case BIN(branchunless):
+ if (OPERAND_AT(ciobj, 0) != type) {
+ dest = (LABEL *)OPERAND_AT(niobj, 0);
+ }
+ break;
+ default:
+ goto no_branch;
+ }
+ line = ciobj->insn_info.line_no;
+ 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);
}
- else break;
- REMOVE_ELEM(i);
- removed = 1;
- i = i->next;
}
- return removed;
+ INSERT_AFTER_INSN1(iobj, line, jump, dest);
+ LABEL_REF(dest);
+ if (!dup) INSERT_AFTER_INSN(iobj, line, pop);
+ return TRUE;
}
static int
iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcallopt)
{
- INSN *iobj = (INSN *)list;
+ INSN *const iobj = (INSN *)list;
+
again:
+ optimize_checktype(iseq, iobj);
+
if (IS_INSN_ID(iobj, jump)) {
INSN *niobj, *diobj, *piobj;
- /*
- * useless jump elimination:
- * jump LABEL1
- * ...
- * LABEL1:
- * jump LABEL2
- *
- * => in this case, first jump instruction should jump to
- * LABEL2 directly
- */
diobj = (INSN *)get_destination_insn(iobj);
niobj = (INSN *)get_next_insn(iobj);
@@ -2069,10 +2721,22 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
* LABEL:
*/
unref_destination(iobj, 0);
- REMOVE_ELEM(&iobj->link);
+ ELEM_REMOVE(&iobj->link);
+ return COMPILE_OK;
}
- else if (iobj != diobj && IS_INSN_ID(diobj, jump) &&
+ else if (iobj != diobj && IS_INSN(&diobj->link) &&
+ IS_INSN_ID(diobj, jump) &&
OPERAND_AT(iobj, 0) != OPERAND_AT(diobj, 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;
@@ -2089,44 +2753,100 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
* LABEL:
* leave
*/
- INSN *popiobj = new_insn_core(iseq, iobj->line_no,
- BIN(pop), 0, 0);
/* replace */
unref_destination(iobj, 0);
iobj->insn_id = BIN(leave);
iobj->operand_size = 0;
- INSERT_ELEM_NEXT(&iobj->link, &popiobj->link);
+ iobj->insn_info = diobj->insn_info;
goto again;
}
- /*
- * useless jump elimination (if/unless destination):
- * if L1
- * jump L2
- * L1:
- * ...
- * L2:
- *
- * ==>
- * unless L2
- * L1:
- * ...
- * L2:
- */
- else if ((piobj = (INSN *)get_prev_insn(iobj)) != 0 &&
+ else if (IS_INSN(iobj->link.prev) &&
+ (piobj = (INSN *)iobj->link.prev) &&
(IS_INSN_ID(piobj, branchif) ||
IS_INSN_ID(piobj, branchunless))) {
- if (niobj == (INSN *)get_destination_insn(piobj)) {
+ 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);
- REMOVE_ELEM(&iobj->link);
+ 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
+ */
+ INSN *popiobj = new_insn_core(iseq, iobj->insn_info.line_no,
+ BIN(pop), 0, 0);
+ ELEM_REPLACE(&piobj->link, &popiobj->link);
}
}
- else if (remove_unreachable_chunk(iseq, iobj->link.next)) {
+ if (remove_unreachable_chunk(iseq, iobj->link.next)) {
goto again;
}
}
+ /*
+ * putstring "beg"
+ * putstring "end"
+ * newrange excl
+ *
+ * ==>
+ *
+ * putobject "beg".."end"
+ */
+ if (IS_INSN_ID(iobj, checkmatch)) {
+ INSN *range = (INSN *)get_prev_insn(iobj);
+ INSN *beg, *end;
+ VALUE str_beg, str_end;
+
+ if (range && IS_INSN_ID(range, newrange) &&
+ (end = (INSN *)get_prev_insn(range)) != 0 &&
+ 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);
+
+ iseq_add_mark_object_compile_time(iseq, lit_range);
+ ELEM_REMOVE(&beg->link);
+ ELEM_REMOVE(&end->link);
+ range->insn_id = BIN(putobject);
+ OPERAND_AT(range, 0) = lit_range;
+ }
+ }
+
if (IS_INSN_ID(iobj, leave)) {
remove_unreachable_chunk(iseq, iobj->link.next);
}
@@ -2143,97 +2863,135 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
* if L2
*/
INSN *nobj = (INSN *)get_destination_insn(iobj);
- INSN *pobj = (INSN *)iobj->link.prev;
- int prev_dup = 0;
- if (pobj) {
- if (!IS_INSN(&pobj->link))
- pobj = 0;
- else if (IS_INSN_ID(pobj, dup))
- prev_dup = 1;
- }
-
- for (;;) {
- if (IS_INSN_ID(nobj, jump)) {
- replace_destination(iobj, nobj);
- }
- else if (prev_dup && IS_INSN_ID(nobj, dup) &&
- !!(nobj = (INSN *)nobj->link.next) &&
- /* basic blocks, with no labels in the middle */
- nobj->insn_id == iobj->insn_id) {
- /*
- * dup
- * if L1
- * ...
- * L1:
- * dup
- * if L2
- * =>
- * dup
- * if L2
- * ...
- * L1:
- * dup
- * if L2
- */
- replace_destination(iobj, nobj);
- }
- else if (pobj) {
- /*
- * putnil
- * if L1
- * =>
- * # nothing
- *
- * putobject true
- * if L1
- * =>
- * jump L1
- *
- * putstring ".."
- * if L1
- * =>
- * jump L1
- *
- * putstring ".."
- * dup
- * if L1
- * =>
- * putstring ".."
- * jump L1
- *
- */
- int cond;
- if (prev_dup && IS_INSN(pobj->link.prev)) {
- pobj = (INSN *)pobj->link.prev;
- }
- if (IS_INSN_ID(pobj, putobject)) {
- cond = (IS_INSN_ID(iobj, branchif) ?
- OPERAND_AT(pobj, 0) != Qfalse :
- IS_INSN_ID(iobj, branchunless) ?
- OPERAND_AT(pobj, 0) == Qfalse :
- FALSE);
- }
- else if (IS_INSN_ID(pobj, putstring)) {
- cond = IS_INSN_ID(iobj, branchif);
- }
- else if (IS_INSN_ID(pobj, putnil)) {
- cond = !IS_INSN_ID(iobj, branchif);
- }
- else break;
- REMOVE_ELEM(iobj->link.prev);
- if (cond) {
- iobj->insn_id = BIN(jump);
- goto again;
- }
- else {
- unref_destination(iobj, 0);
- REMOVE_ELEM(&iobj->link);
- }
- break;
- }
- else break;
- nobj = (INSN *)get_destination_insn(nobj);
- }
+
+ /* This is super nasty hack!!!
+ *
+ * This jump-jump optimization may ignore event flags of the jump
+ * instruction being skipped. Actually, Line 2 TracePoint event
+ * is never fired in the following code:
+ *
+ * 1: raise if 1 == 2
+ * 2: while true
+ * 3: break
+ * 4: end
+ *
+ * This is critical for coverage measurement. [Bug #15980]
+ *
+ * This is a stopgap measure: stop the jump-jump optimization if
+ * coverage measurement is enabled and if the skipped instruction
+ * has any event flag.
+ *
+ * Note that, still, TracePoint Line event does not occur on Line 2.
+ * This should be fixed in future.
+ */
+ int stop_optimization =
+ ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq) &&
+ nobj->insn_info.events;
+ if (!stop_optimization) {
+ INSN *pobj = (INSN *)iobj->link.prev;
+ int prev_dup = 0;
+ if (pobj) {
+ if (!IS_INSN(&pobj->link))
+ pobj = 0;
+ else if (IS_INSN_ID(pobj, dup))
+ prev_dup = 1;
+ }
+
+ for (;;) {
+ if (IS_INSN(&nobj->link) && IS_INSN_ID(nobj, jump)) {
+ replace_destination(iobj, nobj);
+ }
+ else if (prev_dup && IS_INSN_ID(nobj, dup) &&
+ !!(nobj = (INSN *)nobj->link.next) &&
+ /* basic blocks, with no labels in the middle */
+ nobj->insn_id == iobj->insn_id) {
+ /*
+ * dup
+ * if L1
+ * ...
+ * L1:
+ * dup
+ * if L2
+ * =>
+ * dup
+ * if L2
+ * ...
+ * L1:
+ * dup
+ * if L2
+ */
+ replace_destination(iobj, nobj);
+ }
+ else if (pobj) {
+ /*
+ * putnil
+ * if L1
+ * =>
+ * # nothing
+ *
+ * putobject true
+ * if L1
+ * =>
+ * jump L1
+ *
+ * putstring ".."
+ * if L1
+ * =>
+ * jump L1
+ *
+ * putstring ".."
+ * dup
+ * if L1
+ * =>
+ * putstring ".."
+ * jump L1
+ *
+ */
+ int cond;
+ if (prev_dup && IS_INSN(pobj->link.prev)) {
+ pobj = (INSN *)pobj->link.prev;
+ }
+ if (IS_INSN_ID(pobj, putobject)) {
+ cond = (IS_INSN_ID(iobj, branchif) ?
+ OPERAND_AT(pobj, 0) != Qfalse :
+ IS_INSN_ID(iobj, branchunless) ?
+ OPERAND_AT(pobj, 0) == Qfalse :
+ FALSE);
+ }
+ else if (IS_INSN_ID(pobj, putstring) ||
+ IS_INSN_ID(pobj, duparray) ||
+ IS_INSN_ID(pobj, newarray)) {
+ cond = IS_INSN_ID(iobj, branchif);
+ }
+ else if (IS_INSN_ID(pobj, putnil)) {
+ cond = !IS_INSN_ID(iobj, branchif);
+ }
+ else break;
+ if (prev_dup || !IS_INSN_ID(pobj, newarray)) {
+ ELEM_REMOVE(iobj->link.prev);
+ }
+ else if (!iseq_pop_newarray(iseq, pobj)) {
+ pobj = new_insn_core(iseq, pobj->insn_info.line_no, BIN(pop), 0, NULL);
+ ELEM_INSERT_PREV(&iobj->link, &pobj->link);
+ }
+ if (cond) {
+ if (prev_dup) {
+ pobj = new_insn_core(iseq, pobj->insn_info.line_no, BIN(putnil), 0, NULL);
+ ELEM_INSERT_NEXT(&iobj->link, &pobj->link);
+ }
+ iobj->insn_id = BIN(jump);
+ goto again;
+ }
+ else {
+ unref_destination(iobj, 0);
+ ELEM_REMOVE(&iobj->link);
+ }
+ break;
+ }
+ else break;
+ nobj = (INSN *)get_destination_insn(nobj);
+ }
+ }
}
if (IS_INSN_ID(iobj, pop)) {
@@ -2247,12 +3005,150 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
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(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 */
- REMOVE_ELEM(prev);
- REMOVE_ELEM(&iobj->link);
+ 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;
+ INSERT_BEFORE_INSN1(piobj, piobj->insn_info.line_no, 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);
+ }
+ }
+
+ if (IS_INSN_ID(iobj, tostring)) {
+ LINK_ELEMENT *next = iobj->link.next;
+ /*
+ * tostring
+ * concatstrings 1
+ * =>
+ * tostring
+ */
+ 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);
+ }
+ }
+
+ if (IS_INSN_ID(iobj, concatstrings)) {
+ /*
+ * concatstrings N
+ * concatstrings M
+ * =>
+ * concatstrings N+M-1
+ */
+ LINK_ELEMENT *next = iobj->link.next, *freeze = 0;
+ INSN *jump = 0;
+ if (IS_INSN(next) && IS_INSN_ID(next, freezestring))
+ next = (freeze = next)->next;
+ 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++;
+ if (freeze && IS_NEXT_INSN_ID(next, freezestring)) {
+ if (same_debug_pos_p(freeze, next->next)) {
+ ELEM_REMOVE(freeze);
+ }
+ else {
+ next = next->next;
+ }
+ }
+ ELEM_INSERT_NEXT(next, &label->link);
+ CHECK(iseq_peephole_optimize(iseq, get_next_insn(jump), do_tailcallopt));
+ }
+ else {
+ if (freeze) ELEM_REMOVE(freeze);
+ ELEM_REMOVE(next);
+ }
+ }
+ }
+
+ if (IS_INSN_ID(iobj, freezestring) &&
+ NIL_P(OPERAND_AT(iobj, 0)) &&
+ IS_NEXT_INSN_ID(&iobj->link, send)) {
+ INSN *niobj = (INSN *)iobj->link.next;
+ struct rb_call_info *ci = (struct rb_call_info *)OPERAND_AT(niobj, 0);
+ /*
+ * freezestring nil # no debug_info
+ * send <:+@, 0, ARG_SIMPLE> # :-@, too
+ * =>
+ * send <:+@, 0, ARG_SIMPLE> # :-@, too
+ */
+ if ((ci->mid == idUPlus || ci->mid == idUMinus) &&
+ (ci->flag & VM_CALL_ARGS_SIMPLE) &&
+ ci->orig_argc == 0) {
+ ELEM_REMOVE(list);
+ return COMPILE_OK;
}
}
@@ -2278,7 +3174,6 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
}
switch (INSN_OF(next)) {
case BIN(nop):
- /*case BIN(trace):*/
next = next->next;
break;
case BIN(jump):
@@ -2310,13 +3205,41 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
}
}
- #define IS_TRACE_LINE(insn) \
- (IS_INSN_ID(insn, trace) && \
- OPERAND_AT(insn, 0) == INT2FIX(RUBY_EVENT_LINE))
- if (IS_TRACE_LINE(iobj) && iobj->link.prev && IS_INSN(iobj->link.prev)) {
- INSN *piobj = (INSN *)iobj->link.prev;
- if (IS_TRACE_LINE(piobj)) {
- REMOVE_ELEM(iobj->link.prev);
+ 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_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);
+ }
}
}
@@ -2333,9 +3256,9 @@ insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id)
VALUE *old_operands = iobj->operands;
iobj->operand_size = 4;
iobj->operands = (VALUE *)compile_data_alloc(iseq, iobj->operand_size * sizeof(VALUE));
- iobj->operands[0] = old_operands[0];
+ iobj->operands[0] = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE);
iobj->operands[1] = Qfalse; /* CALL_CACHE */
- iobj->operands[2] = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE);
+ iobj->operands[2] = old_operands[0];
iobj->operands[3] = Qfalse; /* CALL_CACHE */
}
@@ -2357,11 +3280,11 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
switch (ci->mid) {
case idMax:
iobj->insn_id = BIN(opt_newarray_max);
- REMOVE_ELEM(&niobj->link);
+ ELEM_REMOVE(&niobj->link);
return COMPILE_OK;
case idMin:
iobj->insn_id = BIN(opt_newarray_min);
- REMOVE_ELEM(&niobj->link);
+ ELEM_REMOVE(&niobj->link);
return COMPILE_OK;
}
}
@@ -2399,6 +3322,8 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
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:
@@ -2437,7 +3362,7 @@ tailcallable_p(rb_iseq_t *iseq)
}
static int
-iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
+iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
{
LINK_ELEMENT *list;
const int do_peepholeopt = ISEQ_COMPILE_DATA(iseq)->option->peephole_optimization;
@@ -2510,7 +3435,7 @@ new_unified_insn(rb_iseq_t *iseq,
list = list->next;
}
- return new_insn_core(iseq, iobj->line_no, insn_id, argc, operands);
+ return new_insn_core(iseq, iobj->insn_info.line_no, insn_id, argc, operands);
}
#endif
@@ -2520,7 +3445,7 @@ new_unified_insn(rb_iseq_t *iseq,
* It's future work (if compile time was bottle neck).
*/
static int
-iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
+iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
{
#if OPT_INSTRUCTIONS_UNIFICATION
LINK_ELEMENT *list;
@@ -2578,7 +3503,7 @@ iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
#include "opt_sc.inc"
static int
-insn_set_sc_state(rb_iseq_t *iseq, INSN *iobj, int state)
+insn_set_sc_state(rb_iseq_t *iseq, const LINK_ELEMENT *anchor, INSN *iobj, int state)
{
int nstate;
int insn_id;
@@ -2593,11 +3518,11 @@ insn_set_sc_state(rb_iseq_t *iseq, INSN *iobj, int state)
if (lobj->sc_state != 0) {
if (lobj->sc_state != nstate) {
- dump_disasm_list((LINK_ELEMENT *)iobj);
- dump_disasm_list((LINK_ELEMENT *)lobj);
- printf("\n-- %d, %d\n", lobj->sc_state, nstate);
- COMPILE_ERROR(iseq, iobj->line_no,
- "insn_set_sc_state error\n");
+ 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;
}
}
@@ -2634,7 +3559,7 @@ label_set_sc_state(LABEL *lobj, int state)
#endif
static int
-iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
+iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
{
#if OPT_STACK_CACHING
LINK_ELEMENT *list;
@@ -2665,7 +3590,7 @@ iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
new_insn_body(iseq, 0, BIN(reput), 0);
/* replace this insn */
- REPLACE_ELEM(list, (LINK_ELEMENT *)rpobj);
+ ELEM_REPLACE(list, (LINK_ELEMENT *)rpobj);
list = (LINK_ELEMENT *)rpobj;
goto redo_point;
}
@@ -2676,7 +3601,7 @@ iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
if (state == SCS_AB || state == SCS_BA) {
state = (state == SCS_AB ? SCS_BA : SCS_AB);
- REMOVE_ELEM(list);
+ ELEM_REMOVE(list);
list = list->next;
goto redo_point;
}
@@ -2698,12 +3623,12 @@ iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
case SCS_XX:
goto normal_insn;
default:
- COMPILE_ERROR(iseq, iobj->line_no,
+ COMPILE_ERROR(iseq, iobj->insn_info.line_no,
"unreachable");
return COMPILE_NG;
}
/* remove useless pop */
- REMOVE_ELEM(list);
+ ELEM_REMOVE(list);
list = list->next;
goto redo_point;
}
@@ -2711,7 +3636,7 @@ iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
/* none */
} /* end of switch */
normal_insn:
- state = insn_set_sc_state(iseq, iobj, state);
+ state = insn_set_sc_state(iseq, anchor, iobj, state);
break;
}
case ISEQ_ELEMENT_LABEL:
@@ -2731,9 +3656,32 @@ iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
}
static int
-compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node, int *cntp)
+all_string_result_p(const NODE *node)
{
- NODE *list = node->nd_next;
+ if (!node) return FALSE;
+ switch (nd_type(node)) {
+ case NODE_STR: case NODE_DSTR:
+ 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;
+ 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);
+ default:
+ return FALSE;
+ }
+}
+
+static int
+compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int *cntp)
+{
+ const NODE *list = node->nd_next;
VALUE lit = node->nd_lit;
LINK_ELEMENT *first_lit = 0;
int cnt = 0;
@@ -2742,29 +3690,30 @@ compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node, int *cntp)
if (!NIL_P(lit)) {
cnt++;
if (!RB_TYPE_P(lit, T_STRING)) {
- compile_bug(ERROR_ARGS "dstr: must be string: %s",
- rb_builtin_type_name(TYPE(lit)));
+ COMPILE_ERROR(ERROR_ARGS "dstr: must be string: %s",
+ rb_builtin_type_name(TYPE(lit)));
+ return COMPILE_NG;
}
- lit = node->nd_lit = rb_fstring(lit);
+ lit = freeze_literal(iseq, lit);
ADD_INSN1(ret, nd_line(node), putobject, lit);
if (RSTRING_LEN(lit) == 0) first_lit = LAST_ELEMENT(ret);
}
while (list) {
- node = list->nd_head;
- if (nd_type(node) == NODE_STR) {
- node->nd_lit = rb_fstring(node->nd_lit);
- ADD_INSN1(ret, nd_line(node), putobject, node->nd_lit);
+ const NODE *const head = list->nd_head;
+ if (nd_type(head) == NODE_STR) {
+ lit = freeze_literal(iseq, head->nd_lit);
+ ADD_INSN1(ret, nd_line(head), putobject, lit);
lit = Qnil;
}
else {
- COMPILE(ret, "each string", node);
+ CHECK(COMPILE(ret, "each string", head));
}
cnt++;
list = list->nd_next;
}
if (NIL_P(lit) && first_lit) {
- REMOVE_ELEM(first_lit);
+ ELEM_REMOVE(first_lit);
--cnt;
}
*cntp = cnt;
@@ -2773,25 +3722,25 @@ compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node, int *cntp)
}
static int
-compile_dstr(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node)
+compile_dstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
{
int cnt;
- compile_dstr_fragments(iseq, ret, node, &cnt);
+ CHECK(compile_dstr_fragments(iseq, ret, node, &cnt));
ADD_INSN1(ret, nd_line(node), concatstrings, INT2FIX(cnt));
return COMPILE_OK;
}
static int
-compile_dregx(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node)
+compile_dregx(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
{
int cnt;
- compile_dstr_fragments(iseq, ret, node, &cnt);
+ CHECK(compile_dstr_fragments(iseq, ret, node, &cnt));
ADD_INSN2(ret, nd_line(node), toregexp, INT2FIX(node->nd_cflag), INT2FIX(cnt));
return COMPILE_OK;
}
static int
-compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node, int again,
+compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int again,
LABEL *then_label, LABEL *else_label)
{
const int line = nd_line(node);
@@ -2804,7 +3753,7 @@ compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node, int again,
ADD_INSNL(ret, line, branchif, lend);
/* *flip == 0 */
- COMPILE(ret, "flip2 beg", node->nd_beg);
+ CHECK(COMPILE(ret, "flip2 beg", node->nd_beg));
ADD_INSNL(ret, line, branchunless, else_label);
ADD_INSN1(ret, line, putobject, Qtrue);
ADD_INSN1(ret, line, setspecial, key);
@@ -2814,7 +3763,7 @@ compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node, int again,
/* *flip == 1 */
ADD_LABEL(ret, lend);
- COMPILE(ret, "flip2 end", node->nd_end);
+ CHECK(COMPILE(ret, "flip2 end", node->nd_end));
ADD_INSNL(ret, line, branchunless, then_label);
ADD_INSN1(ret, line, putobject, Qfalse);
ADD_INSN1(ret, line, setspecial, key);
@@ -2824,45 +3773,36 @@ compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node, int again,
}
static int
-compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * cond,
+compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cond,
LABEL *then_label, LABEL *else_label)
{
+ again:
switch (nd_type(cond)) {
case NODE_AND:
{
LABEL *label = NEW_LABEL(nd_line(cond));
- compile_branch_condition(iseq, ret, cond->nd_1st, label,
- else_label);
+ CHECK(compile_branch_condition(iseq, ret, cond->nd_1st, label,
+ else_label));
+ if (!label->refcnt) break;
ADD_LABEL(ret, label);
- compile_branch_condition(iseq, ret, cond->nd_2nd, then_label,
- else_label);
- break;
+ cond = cond->nd_2nd;
+ goto again;
}
case NODE_OR:
{
LABEL *label = NEW_LABEL(nd_line(cond));
- compile_branch_condition(iseq, ret, cond->nd_1st, then_label,
- label);
+ CHECK(compile_branch_condition(iseq, ret, cond->nd_1st, then_label,
+ label));
+ if (!label->refcnt) break;
ADD_LABEL(ret, label);
- compile_branch_condition(iseq, ret, cond->nd_2nd, then_label,
- else_label);
- break;
+ cond = cond->nd_2nd;
+ goto again;
}
- case NODE_LIT: /* NODE_LIT is always not true */
+ case NODE_LIT: /* NODE_LIT is always true */
case NODE_TRUE:
case NODE_STR:
- case NODE_DSTR:
- case NODE_XSTR:
- case NODE_DXSTR:
- case NODE_DREGX:
- case NODE_DREGX_ONCE:
- case NODE_DSYM:
- case NODE_ARRAY:
case NODE_ZARRAY:
- case NODE_HASH:
case NODE_LAMBDA:
- case NODE_DEFN:
- case NODE_DEFS:
/* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
ADD_INSNL(ret, nd_line(cond), jump, then_label);
break;
@@ -2871,14 +3811,25 @@ compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * cond,
/* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
ADD_INSNL(ret, nd_line(cond), jump, else_label);
break;
+ case NODE_ARRAY:
+ case NODE_ARGSCAT:
+ case NODE_DREGX:
+ case NODE_DSTR:
+ CHECK(COMPILE_POPPED(ret, "branch condition", cond));
+ ADD_INSNL(ret, nd_line(cond), jump, then_label);
+ break;
case NODE_FLIP2:
- compile_flip_flop(iseq, ret, cond, TRUE, then_label, else_label);
+ CHECK(compile_flip_flop(iseq, ret, cond, TRUE, then_label, else_label));
break;
case NODE_FLIP3:
- compile_flip_flop(iseq, ret, cond, FALSE, then_label, else_label);
+ CHECK(compile_flip_flop(iseq, ret, cond, FALSE, then_label, else_label));
break;
+ case NODE_DEFINED:
+ CHECK(compile_defined_expr(iseq, ret, cond, Qfalse));
+ goto branch;
default:
- COMPILE(ret, "branch condition", cond);
+ CHECK(COMPILE(ret, "branch condition", cond));
+ branch:
ADD_INSNL(ret, nd_line(cond), branchunless, else_label);
ADD_INSNL(ret, nd_line(cond), jump, then_label);
break;
@@ -2887,18 +3838,25 @@ compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * cond,
}
static int
-compile_array_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE * const root_node, struct rb_call_info_kw_arg ** const kw_arg_ptr)
+compile_array_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
+ const NODE *const root_node,
+ struct rb_call_info_kw_arg **const kw_arg_ptr,
+ unsigned int *flag)
{
if (kw_arg_ptr == NULL) return FALSE;
if (nd_type(root_node) == NODE_HASH && root_node->nd_head && nd_type(root_node->nd_head) == NODE_ARRAY) {
- NODE *node = root_node->nd_head;
+ const NODE *node = root_node->nd_head;
while (node) {
- NODE *key_node = node->nd_head;
+ const NODE *key_node = node->nd_head;
assert(nd_type(node) == NODE_ARRAY);
- if (key_node && nd_type(key_node) == NODE_LIT && RB_TYPE_P(key_node->nd_lit, T_SYMBOL)) {
+ if (!key_node) {
+ if (flag && !root_node->nd_alen) *flag |= VM_CALL_KW_SPLAT;
+ return FALSE;
+ }
+ else if (nd_type(key_node) == NODE_LIT && RB_TYPE_P(key_node->nd_lit, T_SYMBOL)) {
/* can be keywords */
}
else {
@@ -2920,8 +3878,8 @@ compile_array_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE * const
*kw_arg_ptr = kw_arg;
for (i=0; node != NULL; i++, node = node->nd_next->nd_next) {
- NODE *key_node = node->nd_head;
- NODE *val_node = node->nd_next->nd_head;
+ const NODE *key_node = node->nd_head;
+ const NODE *val_node = node->nd_next->nd_head;
keywords[i] = key_node->nd_lit;
COMPILE(ret, "keyword values", val_node);
}
@@ -2938,16 +3896,65 @@ enum compile_array_type_t {
COMPILE_ARRAY_TYPE_ARGS
};
+static inline int
+static_literal_node_p(const NODE *node, const rb_iseq_t *iseq)
+{
+ node = node->nd_head;
+ switch (nd_type(node)) {
+ case NODE_LIT:
+ case NODE_NIL:
+ case NODE_TRUE:
+ case NODE_FALSE:
+ return TRUE;
+ case NODE_STR:
+ if (ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+ default:
+ return FALSE;
+ }
+}
+
+static inline VALUE
+static_literal_value(const NODE *node, rb_iseq_t *iseq)
+{
+ node = node->nd_head;
+ switch (nd_type(node)) {
+ case NODE_NIL:
+ return Qnil;
+ case NODE_TRUE:
+ return Qtrue;
+ case NODE_FALSE:
+ return Qfalse;
+ case NODE_STR:
+ if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
+ VALUE lit;
+ VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX((int)nd_line(node)));
+ lit = rb_str_dup(node->nd_lit);
+ rb_ivar_set(lit, id_debug_created_info, rb_obj_freeze(debug_info));
+ return rb_str_freeze(lit);
+ }
+ else {
+ return rb_fstring(node->nd_lit);
+ }
+ default:
+ return node->nd_lit;
+ }
+}
+
static int
-compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
- enum compile_array_type_t type, struct rb_call_info_kw_arg **keywords_ptr, int poped)
+compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node_root,
+ enum compile_array_type_t type, struct rb_call_info_kw_arg **keywords_ptr,
+ unsigned int *flag, int popped)
{
- NODE *node = node_root;
+ const NODE *node = node_root;
int line = (int)nd_line(node);
int len = 0;
if (nd_type(node) == NODE_ZARRAY) {
- if (!poped) {
+ if (!popped) {
switch (type) {
case COMPILE_ARRAY_TYPE_ARRAY: ADD_INSN1(ret, line, newarray, INT2FIX(0)); break;
case COMPILE_ARRAY_TYPE_HASH: ADD_INSN1(ret, line, newhash, INT2FIX(0)); break;
@@ -2960,15 +3967,15 @@ compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
int first = 1, i;
while (node) {
- NODE *start_node = node, *end_node;
- NODE *kw = 0;
+ const NODE *start_node = node, *end_node;
+ const NODE *kw = 0;
const int max = 0x100;
DECL_ANCHOR(anchor);
INIT_ANCHOR(anchor);
for (i=0; i<max && node; i++, len++, node = node->nd_next) {
if (CPDEBUG > 0) {
- EXPECT_NODE("compile_array", node, NODE_ARRAY);
+ EXPECT_NODE("compile_array", node, NODE_ARRAY, -1);
}
if (type != COMPILE_ARRAY_TYPE_ARRAY && !node->nd_head) {
@@ -2981,35 +3988,39 @@ compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
}
break;
}
- if (opt_p && nd_type(node->nd_head) != NODE_LIT) {
+ if (opt_p && !static_literal_node_p(node, iseq)) {
opt_p = 0;
}
- if (type == COMPILE_ARRAY_TYPE_ARGS && node->nd_next == NULL /* last node */ && compile_array_keyword_arg(iseq, anchor, node->nd_head, keywords_ptr)) {
+ if (type == COMPILE_ARRAY_TYPE_ARGS &&
+ node->nd_next == NULL /* last node */ &&
+ compile_array_keyword_arg(iseq, anchor, node->nd_head, keywords_ptr, flag)) {
len--;
}
else {
- COMPILE_(anchor, "array element", node->nd_head, poped);
+ COMPILE_(anchor, "array element", node->nd_head, popped);
}
}
if (opt_p && type != COMPILE_ARRAY_TYPE_ARGS) {
- if (!poped) {
+ if (!popped) {
VALUE ary = rb_ary_tmp_new(i);
end_node = node;
node = start_node;
while (node != end_node) {
- rb_ary_push(ary, node->nd_head->nd_lit);
+ rb_ary_push(ary, static_literal_value(node, iseq));
node = node->nd_next;
}
- while (node && nd_type(node->nd_head) == NODE_LIT &&
- node->nd_next && nd_type(node->nd_next->nd_head) == NODE_LIT) {
- rb_ary_push(ary, node->nd_head->nd_lit);
- node = node->nd_next;
- rb_ary_push(ary, node->nd_head->nd_lit);
- node = node->nd_next;
+ while (node && node->nd_next &&
+ static_literal_node_p(node, iseq) &&
+ static_literal_node_p(node->nd_next, iseq)) {
+ VALUE elem[2];
+ elem[0] = static_literal_value(node, iseq);
+ elem[1] = static_literal_value(node->nd_next, iseq);
+ rb_ary_cat(ary, elem, 2);
+ node = node->nd_next->nd_next;
len++;
}
@@ -3023,9 +4034,12 @@ compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
ADD_INSN1(ret, line, duparray, ary);
}
else { /* COMPILE_ARRAY_TYPE_HASH */
- ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
- ADD_INSN1(ret, line, putobject, ary);
- ADD_SEND(ret, line, id_core_hash_from_ary, INT2FIX(1));
+ VALUE hash;
+
+ hash = rb_hash_new_with_size(RARRAY_LEN(ary) / 2);
+ rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR_TRANSIENT(ary), hash);
+ iseq_add_mark_object_compile_time(iseq, rb_obj_hide(hash));
+ ADD_INSN1(ret, line, duphash, hash);
}
}
else {
@@ -3034,15 +4048,14 @@ compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
ADD_INSN(ret, line, concatarray);
}
else {
- ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
- ADD_INSN1(ret, line, putobject, ary);
- ADD_SEND(ret, line, id_core_hash_merge_ary, INT2FIX(1));
+ COMPILE_ERROR(ERROR_ARGS "core#hash_merge_ary");
+ return -1;
}
}
}
}
else {
- if (!poped) {
+ if (!popped || kw) {
switch (type) {
case COMPILE_ARRAY_TYPE_ARRAY:
ADD_INSN1(anchor, line, newarray, INT2FIX(i));
@@ -3059,23 +4072,35 @@ compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
case COMPILE_ARRAY_TYPE_HASH:
if (i > 0) {
if (first) {
- ADD_INSN1(anchor, line, newhash, INT2FIX(i));
+ if (!popped) {
+ ADD_INSN1(anchor, line, newhash, INT2FIX(i));
+ }
APPEND_LIST(ret, anchor);
}
else {
- ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
- ADD_INSN(ret, line, swap);
+ if (!popped) {
+ ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ ADD_INSN(ret, line, swap);
+ }
APPEND_LIST(ret, anchor);
- ADD_SEND(ret, line, id_core_hash_merge_ptr, INT2FIX(i + 1));
+ if (!popped) {
+ ADD_SEND(ret, line, id_core_hash_merge_ptr, INT2FIX(i + 1));
+ }
}
}
if (kw) {
- VALUE nhash = (i > 0 || !first) ? INT2FIX(2) : INT2FIX(1);
- ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
- if (i > 0 || !first) ADD_INSN(ret, line, swap);
+ if (!popped) {
+ ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ if (i > 0 || !first) ADD_INSN(ret, line, swap);
+ else ADD_INSN1(ret, line, newhash, INT2FIX(0));
+ }
COMPILE(ret, "keyword splat", kw);
- ADD_SEND(ret, line, id_core_hash_merge_kwd, nhash);
- if (nhash == INT2FIX(1)) ADD_SEND(ret, line, rb_intern("dup"), INT2FIX(0));
+ if (popped) {
+ ADD_INSN(ret, line, pop);
+ }
+ else {
+ ADD_SEND(ret, line, id_core_hash_merge_kwd, INT2FIX(2));
+ }
}
first = 0;
break;
@@ -3085,7 +4110,7 @@ compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
}
}
else {
- /* poped */
+ /* popped */
APPEND_LIST(ret, anchor);
}
}
@@ -3095,13 +4120,7 @@ compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
}
static VALUE
-compile_array(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root, enum compile_array_type_t type)
-{
- return compile_array_(iseq, ret, node_root, type, NULL, 0);
-}
-
-static VALUE
-case_when_optimizable_literal(NODE * node)
+case_when_optimizable_literal(const NODE *const node)
{
switch (nd_type(node)) {
case NODE_LIT: {
@@ -3123,16 +4142,17 @@ case_when_optimizable_literal(NODE * node)
case NODE_FALSE:
return Qfalse;
case NODE_STR:
- return node->nd_lit = rb_fstring(node->nd_lit);
+ return rb_fstring(node->nd_lit);
}
return Qundef;
}
static int
-when_vals(rb_iseq_t *iseq, LINK_ANCHOR *cond_seq, NODE *vals, LABEL *l1, int only_special_literals, VALUE literals)
+when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
+ LABEL *l1, int only_special_literals, VALUE literals)
{
while (vals) {
- NODE* val = vals->nd_head;
+ const NODE *val = vals->nd_head;
VALUE lit = case_when_optimizable_literal(val);
if (lit == Qundef) {
@@ -3140,7 +4160,8 @@ when_vals(rb_iseq_t *iseq, LINK_ANCHOR *cond_seq, NODE *vals, LABEL *l1, int onl
}
else {
if (rb_hash_lookup(literals, lit) != Qnil) {
- rb_compile_warning(ruby_sourcefile, nd_line(val),
+ VALUE file = rb_iseq_path(iseq);
+ rb_compile_warning(RSTRING_PTR(file), nd_line(val),
"duplicated when clause is ignored");
}
else {
@@ -3151,12 +4172,12 @@ when_vals(rb_iseq_t *iseq, LINK_ANCHOR *cond_seq, NODE *vals, LABEL *l1, int onl
ADD_INSN(cond_seq, nd_line(val), dup); /* dup target */
if (nd_type(val) == NODE_STR) {
- val->nd_lit = rb_fstring(val->nd_lit);
debugp_param("nd_lit", val->nd_lit);
- ADD_INSN1(cond_seq, nd_line(val), putobject, val->nd_lit);
+ lit = freeze_literal(iseq, val->nd_lit);
+ ADD_INSN1(cond_seq, nd_line(val), putobject, lit);
}
else {
- COMPILE(cond_seq, "when cond", val);
+ if (!COMPILE(cond_seq, "when cond", val)) return -1;
}
ADD_INSN1(cond_seq, nd_line(vals), checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
@@ -3167,7 +4188,48 @@ when_vals(rb_iseq_t *iseq, LINK_ANCHOR *cond_seq, NODE *vals, LABEL *l1, int onl
}
static int
-compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node)
+when_splat_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
+ LABEL *l1, int only_special_literals, VALUE literals)
+{
+ const int line = nd_line(vals);
+
+ switch (nd_type(vals)) {
+ case NODE_ARRAY:
+ if (when_vals(iseq, cond_seq, vals, l1, only_special_literals, literals) < 0)
+ return COMPILE_NG;
+ break;
+ case NODE_SPLAT:
+ ADD_INSN (cond_seq, line, dup);
+ CHECK(COMPILE(cond_seq, "when splat", vals->nd_head));
+ ADD_INSN1(cond_seq, line, splatarray, Qfalse);
+ ADD_INSN1(cond_seq, line, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
+ ADD_INSNL(cond_seq, line, branchif, l1);
+ break;
+ case NODE_ARGSCAT:
+ CHECK(when_splat_vals(iseq, cond_seq, vals->nd_head, l1, only_special_literals, literals));
+ CHECK(when_splat_vals(iseq, cond_seq, vals->nd_body, l1, only_special_literals, literals));
+ break;
+ case NODE_ARGSPUSH:
+ CHECK(when_splat_vals(iseq, cond_seq, vals->nd_head, l1, only_special_literals, literals));
+ ADD_INSN (cond_seq, line, dup);
+ CHECK(COMPILE(cond_seq, "when argspush body", vals->nd_body));
+ ADD_INSN1(cond_seq, line, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
+ ADD_INSNL(cond_seq, line, branchif, l1);
+ break;
+ default:
+ ADD_INSN (cond_seq, line, dup);
+ CHECK(COMPILE(cond_seq, "when val", vals));
+ ADD_INSN1(cond_seq, line, splatarray, Qfalse);
+ ADD_INSN1(cond_seq, line, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
+ ADD_INSNL(cond_seq, line, branchif, l1);
+ break;
+ }
+ return COMPILE_OK;
+}
+
+
+static int
+compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
{
switch (nd_type(node)) {
case NODE_ATTRASGN: {
@@ -3176,7 +4238,7 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node)
VALUE dupidx;
int line = nd_line(node);
- COMPILE_POPED(ret, "masgn lhs (NODE_ATTRASGN)", node);
+ CHECK(COMPILE_POPPED(ret, "masgn lhs (NODE_ATTRASGN)", node));
iobj = (INSN *)get_prev_insn((INSN *)LAST_ELEMENT(ret)); /* send insn */
ci = (struct rb_call_info *)iobj->operands[0];
@@ -3195,16 +4257,16 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node)
case NODE_MASGN: {
DECL_ANCHOR(anchor);
INIT_ANCHOR(anchor);
- COMPILE_POPED(anchor, "nest masgn lhs", node);
- REMOVE_ELEM(FIRST_ELEMENT(anchor));
+ CHECK(COMPILE_POPPED(anchor, "nest masgn lhs", node));
+ ELEM_REMOVE(FIRST_ELEMENT(anchor));
ADD_SEQ(ret, anchor);
break;
}
default: {
DECL_ANCHOR(anchor);
INIT_ANCHOR(anchor);
- COMPILE_POPED(anchor, "masgn lhs", node);
- REMOVE_ELEM(FIRST_ELEMENT(anchor));
+ CHECK(COMPILE_POPPED(anchor, "masgn lhs", node));
+ ELEM_REMOVE(FIRST_ELEMENT(anchor));
ADD_SEQ(ret, anchor);
}
}
@@ -3212,25 +4274,26 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node)
return COMPILE_OK;
}
-static void
-compile_massign_opt_lhs(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *lhsn)
+static int
+compile_massign_opt_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *lhsn)
{
if (lhsn) {
- compile_massign_opt_lhs(iseq, ret, lhsn->nd_next);
- compile_massign_lhs(iseq, ret, lhsn->nd_head);
+ CHECK(compile_massign_opt_lhs(iseq, ret, lhsn->nd_next));
+ CHECK(compile_massign_lhs(iseq, ret, lhsn->nd_head));
}
+ return COMPILE_OK;
}
static int
-compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *ret,
- NODE *rhsn, NODE *orig_lhsn)
+compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
+ const NODE *rhsn, const NODE *orig_lhsn)
{
VALUE mem[64];
const int memsize = numberof(mem);
int memindex = 0;
int llen = 0, rlen = 0;
int i;
- NODE *lhsn = orig_lhsn;
+ const NODE *lhsn = orig_lhsn;
#define MEMORY(v) { \
int i; \
@@ -3246,7 +4309,7 @@ compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *ret,
}
while (lhsn) {
- NODE *ln = lhsn->nd_head;
+ const NODE *ln = lhsn->nd_head;
switch (nd_type(ln)) {
case NODE_LASGN:
MEMORY(ln->nd_vid);
@@ -3254,7 +4317,6 @@ compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *ret,
case NODE_DASGN:
case NODE_DASGN_CURR:
case NODE_IASGN:
- case NODE_IASGN2:
case NODE_CVASGN:
MEMORY(ln->nd_vid);
break;
@@ -3267,7 +4329,7 @@ compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *ret,
while (rhsn) {
if (llen <= rlen) {
- COMPILE_POPED(ret, "masgn val (popped)", rhsn->nd_head);
+ COMPILE_POPPED(ret, "masgn val (popped)", rhsn->nd_head);
}
else {
COMPILE(ret, "masgn val", rhsn->nd_head);
@@ -3287,7 +4349,7 @@ compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *ret,
}
static void
-adjust_stack(rb_iseq_t *iseq, LINK_ANCHOR *ret, int line, int rlen, int llen)
+adjust_stack(rb_iseq_t *iseq, LINK_ANCHOR *const ret, int line, int rlen, int llen)
{
if (rlen < llen) {
do {ADD_INSN(ret, line, putnil);} while (++rlen < llen);
@@ -3298,14 +4360,14 @@ adjust_stack(rb_iseq_t *iseq, LINK_ANCHOR *ret, int line, int rlen, int llen)
}
static int
-compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node, int poped)
+compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
{
- NODE *rhsn = node->nd_value;
- NODE *splatn = node->nd_args;
- NODE *lhsn = node->nd_head;
- int lhs_splat = (splatn && (VALUE)splatn != (VALUE)-1) ? 1 : 0;
+ const NODE *rhsn = node->nd_value;
+ const NODE *splatn = node->nd_args;
+ const NODE *lhsn = node->nd_head;
+ int lhs_splat = (splatn && NODE_NAMED_REST_P(splatn)) ? 1 : 0;
- if (!poped || splatn || !compile_massign_opt(iseq, ret, rhsn, lhsn)) {
+ if (!popped || splatn || !compile_massign_opt(iseq, ret, rhsn, lhsn)) {
int llen = 0;
int expand = 1;
DECL_ANCHOR(lhsseq);
@@ -3313,14 +4375,14 @@ compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node, int poped)
INIT_ANCHOR(lhsseq);
while (lhsn) {
- compile_massign_lhs(iseq, lhsseq, lhsn->nd_head);
+ CHECK(compile_massign_lhs(iseq, lhsseq, lhsn->nd_head));
llen += 1;
lhsn = lhsn->nd_next;
}
COMPILE(ret, "normal masgn rhs", rhsn);
- if (!poped) {
+ if (!popped) {
ADD_INSN(ret, nd_line(node), dup);
}
else if (!lhs_splat) {
@@ -3357,25 +4419,25 @@ compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node, int poped)
if (lhs_splat) {
if (nd_type(splatn) == NODE_POSTARG) {
/*a, b, *r, p1, p2 */
- NODE *postn = splatn->nd_2nd;
- NODE *restn = splatn->nd_1st;
+ const NODE *postn = splatn->nd_2nd;
+ const NODE *restn = splatn->nd_1st;
int num = (int)postn->nd_alen;
- int flag = 0x02 | (((VALUE)restn == (VALUE)-1) ? 0x00 : 0x01);
+ int flag = 0x02 | (NODE_NAMED_REST_P(restn) ? 0x01 : 0x00);
ADD_INSN2(ret, nd_line(splatn), expandarray,
INT2FIX(num), INT2FIX(flag));
- if ((VALUE)restn != (VALUE)-1) {
- compile_massign_lhs(iseq, ret, restn);
+ if (NODE_NAMED_REST_P(restn)) {
+ CHECK(compile_massign_lhs(iseq, ret, restn));
}
while (postn) {
- compile_massign_lhs(iseq, ret, postn->nd_head);
+ CHECK(compile_massign_lhs(iseq, ret, postn->nd_head));
postn = postn->nd_next;
}
}
else {
/* a, b, *r */
- compile_massign_lhs(iseq, ret, splatn);
+ CHECK(compile_massign_lhs(iseq, ret, splatn));
}
}
}
@@ -3383,61 +4445,63 @@ compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node, int poped)
}
static int
-compile_colon2(rb_iseq_t *iseq, NODE * node,
- LINK_ANCHOR *pref, LINK_ANCHOR *body)
+compile_const_prefix(rb_iseq_t *iseq, const NODE *const node,
+ LINK_ANCHOR *const pref, LINK_ANCHOR *const body)
{
switch (nd_type(node)) {
case NODE_CONST:
- debugi("compile_colon2 - colon", node->nd_vid);
+ debugi("compile_const_prefix - colon", node->nd_vid);
ADD_INSN1(body, nd_line(node), getconstant, ID2SYM(node->nd_vid));
break;
case NODE_COLON3:
- debugi("compile_colon2 - colon3", node->nd_mid);
+ debugi("compile_const_prefix - colon3", node->nd_mid);
ADD_INSN(body, nd_line(node), pop);
ADD_INSN1(body, nd_line(node), putobject, rb_cObject);
ADD_INSN1(body, nd_line(node), getconstant, ID2SYM(node->nd_mid));
break;
case NODE_COLON2:
- compile_colon2(iseq, node->nd_head, pref, body);
- debugi("compile_colon2 - 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, nd_line(node), getconstant, ID2SYM(node->nd_mid));
break;
default:
- COMPILE(pref, "const colon2 prefix", node);
+ CHECK(COMPILE(pref, "const colon2 prefix", node));
break;
}
return COMPILE_OK;
}
-static VALUE
-compile_cpath(LINK_ANCHOR *ret, rb_iseq_t *iseq, NODE *cpath)
+static int
+compile_cpath(LINK_ANCHOR *const ret, rb_iseq_t *iseq, const NODE *cpath)
{
if (nd_type(cpath) == NODE_COLON3) {
/* toplevel class ::Foo */
ADD_INSN1(ret, nd_line(cpath), putobject, rb_cObject);
- return Qfalse;
+ return VM_DEFINECLASS_FLAG_SCOPED;
}
else if (cpath->nd_head) {
/* Bar::Foo */
COMPILE(ret, "nd_else->nd_head", cpath->nd_head);
- return Qfalse;
+ return VM_DEFINECLASS_FLAG_SCOPED;
}
else {
/* class at cbase Foo */
ADD_INSN1(ret, nd_line(cpath), putspecialobject,
INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
- return Qtrue;
+ return 0;
}
}
#define private_recv_p(node) (nd_type((node)->nd_recv) == NODE_SELF)
+static int
+defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
+ const NODE *const node, LABEL **lfinish, VALUE needstr);
-#define defined_expr defined_expr0
static int
-defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret,
- NODE *node, LABEL **lfinish, VALUE needstr)
+defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
+ const NODE *const node, LABEL **lfinish, VALUE needstr)
{
- enum defined_type expr_type = 0;
+ enum defined_type expr_type = DEFINED_NOT_DEFINED;
enum node_type type;
switch (type = nd_type(node)) {
@@ -3457,10 +4521,10 @@ defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret,
break;
case NODE_ARRAY:{
- NODE *vals = node;
+ const NODE *vals = node;
do {
- defined_expr(iseq, ret, vals->nd_head, lfinish, Qfalse);
+ defined_expr0(iseq, ret, vals->nd_head, lfinish, Qfalse);
if (!lfinish[1]) {
lfinish[1] = NEW_LABEL(nd_line(node));
@@ -3510,19 +4574,14 @@ defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret,
if (!lfinish[1]) {
lfinish[1] = NEW_LABEL(nd_line(node));
}
- defined_expr(iseq, ret, node->nd_head, lfinish, Qfalse);
+ defined_expr0(iseq, ret, node->nd_head, lfinish, Qfalse);
ADD_INSNL(ret, nd_line(node), branchunless, lfinish[1]);
+ COMPILE(ret, "defined/colon2#nd_head", node->nd_head);
- if (rb_is_const_id(node->nd_mid)) {
- COMPILE(ret, "defined/colon2#nd_head", node->nd_head);
- ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_CONST),
- ID2SYM(node->nd_mid), needstr);
- }
- else {
- COMPILE(ret, "defined/colon2#nd_head", node->nd_head);
- ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_METHOD),
- ID2SYM(node->nd_mid), needstr);
- }
+ ADD_INSN3(ret, nd_line(node), defined,
+ (rb_is_const_id(node->nd_mid) ?
+ INT2FIX(DEFINED_CONST) : INT2FIX(DEFINED_METHOD)),
+ ID2SYM(node->nd_mid), needstr);
return 1;
case NODE_COLON3:
ADD_INSN1(ret, nd_line(node), putobject, rb_cObject);
@@ -3532,22 +4591,23 @@ defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret,
/* 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_CALL || type == NODE_OPCALL ||
(type == NODE_ATTRASGN && !private_recv_p(node)));
- if (!lfinish[1]) {
+ if (!lfinish[1] && (node->nd_args || explicit_receiver)) {
lfinish[1] = NEW_LABEL(nd_line(node));
}
if (node->nd_args) {
- defined_expr(iseq, ret, node->nd_args, lfinish, Qfalse);
+ defined_expr0(iseq, ret, node->nd_args, lfinish, Qfalse);
ADD_INSNL(ret, nd_line(node), branchunless, lfinish[1]);
}
if (explicit_receiver) {
- defined_expr(iseq, ret, node->nd_recv, lfinish, Qfalse);
+ defined_expr0(iseq, ret, node->nd_recv, lfinish, Qfalse);
ADD_INSNL(ret, nd_line(node), branchunless, lfinish[1]);
COMPILE(ret, "defined/recv", node->nd_recv);
ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_METHOD),
@@ -3593,7 +4653,6 @@ defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret,
case NODE_GASGN:
case NODE_IASGN:
case NODE_CDECL:
- case NODE_CVDECL:
case NODE_CVASGN:
expr_type = DEFINED_ASGN;
break;
@@ -3611,11 +4670,18 @@ defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret,
}
return 0;
}
-#undef defined_expr
+
+static VALUE
+build_defined_rescue_iseq(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *unused)
+{
+ ADD_INSN(ret, 0, putnil);
+ iseq_set_exception_local_table(iseq);
+ return Qnil;
+}
static int
-defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret,
- NODE *node, LABEL **lfinish, VALUE needstr)
+defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
+ const NODE *const node, LABEL **lfinish, VALUE needstr)
{
LINK_ELEMENT *lcur = ret->last;
int done = defined_expr0(iseq, ret, node, lfinish, needstr);
@@ -3623,11 +4689,11 @@ defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret,
int line = nd_line(node);
LABEL *lstart = NEW_LABEL(line);
LABEL *lend = NEW_LABEL(line);
- const rb_iseq_t *rescue = NEW_CHILD_ISEQ(NEW_NIL(),
- rb_str_concat(rb_str_new2
- ("defined guard in "),
- iseq->body->location.label),
- ISEQ_TYPE_DEFINED_GUARD, 0);
+ const rb_iseq_t *rescue;
+ rescue = new_child_iseq_ifunc(iseq, IFUNC_NEW(build_defined_rescue_iseq, 0, 0),
+ rb_str_concat(rb_str_new2("defined guard in "),
+ iseq->body->location.label),
+ iseq, ISEQ_TYPE_RESCUE, 0);
lstart->rescued = LABEL_RESCUE_BEG;
lend->rescued = LABEL_RESCUE_END;
APPEND_LABEL(ret, lcur, lstart);
@@ -3637,6 +4703,31 @@ defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret,
return done;
}
+static int
+compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr)
+{
+ const int line = nd_line(node);
+ if (!node->nd_head) {
+ VALUE str = rb_iseq_defined_string(DEFINED_NIL);
+ ADD_INSN1(ret, line, putobject, str);
+ }
+ else {
+ LABEL *lfinish[2];
+ LINK_ELEMENT *last = ret->last;
+ lfinish[0] = NEW_LABEL(line);
+ lfinish[1] = 0;
+ defined_expr(iseq, ret, node->nd_head, lfinish, needstr);
+ if (lfinish[1]) {
+ ELEM_INSERT_NEXT(last, &new_insn_body(iseq, line, BIN(putnil), 0)->link);
+ ADD_INSN(ret, line, swap);
+ ADD_INSN(ret, line, pop);
+ ADD_LABEL(ret, lfinish[1]);
+ }
+ ADD_LABEL(ret, lfinish[0]);
+ }
+ return COMPILE_OK;
+}
+
static VALUE
make_name_for_block(const rb_iseq_t *orig_iseq)
{
@@ -3663,7 +4754,7 @@ 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, NODE *node)
+ struct ensure_range *er, const NODE *const node)
{
enl->ensure_node = node;
enl->prev = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack; /* prev */
@@ -3690,7 +4781,7 @@ add_ensure_range(rb_iseq_t *iseq, struct ensure_range *erange,
}
static void
-add_ensure_iseq(LINK_ANCHOR *ret, rb_iseq_t *iseq, int is_return)
+add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return)
{
struct iseq_compile_data_ensure_node_stack *enlp =
ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
@@ -3699,7 +4790,7 @@ add_ensure_iseq(LINK_ANCHOR *ret, rb_iseq_t *iseq, int is_return)
INIT_ANCHOR(ensure);
while (enlp) {
- if (enlp->erange != 0) {
+ if (enlp->erange != NULL) {
DECL_ANCHOR(ensure_part);
LABEL *lstart = NEW_LABEL(0);
LABEL *lend = NEW_LABEL(0);
@@ -3709,7 +4800,7 @@ add_ensure_iseq(LINK_ANCHOR *ret, rb_iseq_t *iseq, int is_return)
ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enlp->prev;
ADD_LABEL(ensure_part, lstart);
- COMPILE_POPED(ensure_part, "ensure part", enlp->ensure_node);
+ COMPILE_POPPED(ensure_part, "ensure part", enlp->ensure_node);
ADD_LABEL(ensure_part, lend);
ADD_SEQ(ensure, ensure_part);
}
@@ -3725,7 +4816,8 @@ add_ensure_iseq(LINK_ANCHOR *ret, rb_iseq_t *iseq, int is_return)
}
static VALUE
-setup_args(rb_iseq_t *iseq, LINK_ANCHOR *args, NODE *argn, unsigned int *flag, struct rb_call_info_kw_arg **keywords)
+setup_args(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
+ unsigned int *flag, struct rb_call_info_kw_arg **keywords)
{
VALUE argc = INT2FIX(0);
int nsplat = 0;
@@ -3767,9 +4859,13 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR *args, NODE *argn, unsigned int *flag, s
INSERT_LIST(args_splat, tmp);
nsplat++;
*flag |= VM_CALL_ARGS_SPLAT;
+ if (nd_type(argn->nd_body) == NODE_HASH)
+ *flag |= VM_CALL_KW_SPLAT;
if (next_is_array) {
- argc = INT2FIX(compile_array(iseq, args, argn->nd_head, COMPILE_ARRAY_TYPE_ARGS) + 1);
+ int len = compile_array(iseq, args, argn->nd_head, COMPILE_ARRAY_TYPE_ARGS, NULL, flag, FALSE);
+ if (len < 0) return Qnil;
+ argc = INT2FIX(len + 1);
}
else {
argn = argn->nd_head;
@@ -3779,11 +4875,13 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR *args, NODE *argn, unsigned int *flag, s
}
case NODE_ARRAY:
{
- argc = INT2FIX(compile_array_(iseq, args, argn, COMPILE_ARRAY_TYPE_ARGS, keywords, FALSE));
+ int len = compile_array(iseq, args, argn, COMPILE_ARRAY_TYPE_ARGS, keywords, flag, FALSE);
+ if (len < 0) return Qnil;
+ argc = INT2FIX(len);
break;
}
default: {
- UNKNOWN_NODE("setup_arg", argn);
+ UNKNOWN_NODE("setup_arg", argn, Qnil);
}
}
}
@@ -3791,22 +4889,31 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR *args, NODE *argn, unsigned int *flag, s
if (nsplat > 1) {
int i;
for (i=1; i<nsplat; i++) {
- ADD_INSN(args_splat, nd_line(args), concatarray);
+ ADD_INSN(args_splat, nd_line(argn), concatarray);
}
}
- if (!LIST_SIZE_ZERO(args_splat)) {
+ if (!LIST_INSN_SIZE_ZERO(args_splat)) {
ADD_SEQ(args, args_splat);
}
if (*flag & VM_CALL_ARGS_BLOCKARG) {
+ if (LIST_INSN_SIZE_ONE(arg_block)) {
+ LINK_ELEMENT *elem = FIRST_ELEMENT(arg_block);
+ if (elem->type == ISEQ_ELEMENT_INSN) {
+ INSN *iobj = (INSN *)elem;
+ if (iobj->insn_id == BIN(getblockparam)) {
+ iobj->insn_id = BIN(getblockparamproxy);
+ }
+ }
+ }
ADD_SEQ(args, arg_block);
}
return argc;
}
static VALUE
-build_postexe_iseq(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *body)
+build_postexe_iseq(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *body)
{
int line = nd_line(body);
VALUE argc = INT2FIX(0);
@@ -3819,9 +4926,9 @@ build_postexe_iseq(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *body)
}
static void
-compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node)
+compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
{
- NODE *vars;
+ const NODE *vars;
LINK_ELEMENT *last;
int line = nd_line(node);
LABEL *fail_label = NEW_LABEL(line), *end_label = NEW_LABEL(line);
@@ -3840,11 +4947,11 @@ compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node)
ADD_INSN(ret, line, dup);
}
last = ret->last;
- COMPILE_POPED(ret, "capture", vars->nd_head);
+ COMPILE_POPPED(ret, "capture", vars->nd_head);
last = last->next; /* putobject :var */
cap = new_insn_send(iseq, line, idAREF, INT2FIX(1),
NULL, INT2FIX(0), NULL);
- INSERT_ELEM_PREV(last->next, (LINK_ELEMENT *)cap);
+ 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 */
@@ -3869,7 +4976,7 @@ compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node)
ADD_INSN(ret, line, pop);
for (vars = node; vars; vars = vars->nd_next) {
last = ret->last;
- COMPILE_POPED(ret, "capture", vars->nd_head);
+ COMPILE_POPPED(ret, "capture", vars->nd_head);
last = last->next; /* putobject :var */
((INSN*)last)->insn_id = BIN(putnil);
((INSN*)last)->operand_size = 0;
@@ -3878,739 +4985,1029 @@ compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node)
}
static int
-number_literal_p(NODE *n)
+number_literal_p(const NODE *n)
{
return (n && nd_type(n) == NODE_LIT && RB_INTEGER_TYPE_P(n->nd_lit));
}
-/**
- compile each node
-
- self: InstructionSequence
- node: Ruby compiled node
- poped: This node will be poped
- */
static int
-iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
+compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
{
- enum node_type type;
- LINK_ELEMENT *saved_last_element = 0;
- int line;
-
- if (node == 0) {
- if (!poped) {
- debugs("node: NODE_NIL(implicit)\n");
- ADD_INSN(ret, ISEQ_COMPILE_DATA(iseq)->last_line, putnil);
- }
- return COMPILE_OK;
- }
-
- line = (int)nd_line(node);
-
- if (ISEQ_COMPILE_DATA(iseq)->last_line == line) {
- /* ignore */
- }
- else {
- if (node->flags & NODE_FL_NEWLINE) {
- ISEQ_COMPILE_DATA(iseq)->last_line = line;
- ADD_TRACE(ret, line, RUBY_EVENT_LINE);
- saved_last_element = ret->last;
- }
- }
-
- debug_node_start(node);
-
- type = nd_type(node);
-
- switch (type) {
- case NODE_BLOCK:{
- while (node && nd_type(node) == NODE_BLOCK) {
- COMPILE_(ret, "BLOCK body", node->nd_head,
- (node->nd_next == 0 && poped == 0) ? 0 : 1);
- node = node->nd_next;
- }
- if (node) {
- COMPILE_(ret, "BLOCK next", node->nd_next, poped);
- }
- break;
- }
- case NODE_IF:{
- DECL_ANCHOR(cond_seq);
- DECL_ANCHOR(then_seq);
- DECL_ANCHOR(else_seq);
- LABEL *then_label, *else_label, *end_label;
-
- INIT_ANCHOR(cond_seq);
- INIT_ANCHOR(then_seq);
- INIT_ANCHOR(else_seq);
- then_label = NEW_LABEL(line);
- else_label = NEW_LABEL(line);
- end_label = NEW_LABEL(line);
-
- compile_branch_condition(iseq, cond_seq, node->nd_cond,
- then_label, else_label);
- COMPILE_(then_seq, "then", node->nd_body, poped);
- COMPILE_(else_seq, "else", node->nd_else, poped);
-
- ADD_SEQ(ret, cond_seq);
+ struct rb_iseq_constant_body *const body = iseq->body;
+ const NODE *const node_body = type == NODE_IF ? node->nd_body : node->nd_else;
+ const NODE *const node_else = type == NODE_IF ? node->nd_else : node->nd_body;
+ const int line = nd_line(node);
+ const int lineno = nd_first_lineno(node);
+ const int column = nd_first_column(node);
+ const int last_lineno = nd_last_lineno(node);
+ const int last_column = nd_last_column(node);
+ DECL_ANCHOR(cond_seq);
+ DECL_ANCHOR(then_seq);
+ DECL_ANCHOR(else_seq);
+ LABEL *then_label, *else_label, *end_label;
+ VALUE branches = 0;
+ int ci_size, ci_kw_size;
+
+ INIT_ANCHOR(cond_seq);
+ INIT_ANCHOR(then_seq);
+ INIT_ANCHOR(else_seq);
+ then_label = NEW_LABEL(line);
+ else_label = NEW_LABEL(line);
+ end_label = 0;
+
+ compile_branch_condition(iseq, cond_seq, node->nd_cond,
+ then_label, else_label);
+
+ ci_size = body->ci_size;
+ ci_kw_size = body->ci_kw_size;
+ CHECK(COMPILE_(then_seq, "then", node_body, popped));
+ if (!then_label->refcnt) {
+ body->ci_size = ci_size;
+ body->ci_kw_size = ci_kw_size;
+ }
+
+ ci_size = body->ci_size;
+ ci_kw_size = body->ci_kw_size;
+ CHECK(COMPILE_(else_seq, "else", node_else, popped));
+ if (!else_label->refcnt) {
+ body->ci_size = ci_size;
+ body->ci_kw_size = ci_kw_size;
+ }
+
+ ADD_SEQ(ret, cond_seq);
+
+ if (then_label->refcnt && else_label->refcnt) {
+ DECL_BRANCH_BASE(branches, lineno, column, last_lineno, last_column, type == NODE_IF ? "if" : "unless");
+ }
+
+ if (then_label->refcnt) {
ADD_LABEL(ret, then_label);
+ if (else_label->refcnt) {
+ ADD_TRACE_BRANCH_COVERAGE(
+ ret,
+ node_body ? nd_first_lineno(node_body) : lineno,
+ node_body ? nd_first_column(node_body) : column,
+ node_body ? nd_last_lineno(node_body) : last_lineno,
+ node_body ? nd_last_column(node_body) : last_column,
+ type == NODE_IF ? "then" : "else",
+ branches);
+ end_label = NEW_LABEL(line);
+ ADD_INSNL(then_seq, line, jump, end_label);
+ if (!popped) {
+ ADD_INSN(then_seq, line, pop);
+ }
+ }
ADD_SEQ(ret, then_seq);
- ADD_INSNL(ret, line, jump, end_label);
+ }
+ if (else_label->refcnt) {
ADD_LABEL(ret, else_label);
+ if (then_label->refcnt) {
+ ADD_TRACE_BRANCH_COVERAGE(
+ ret,
+ node_else ? nd_first_lineno(node_else) : lineno,
+ node_else ? nd_first_column(node_else) : column,
+ node_else ? nd_last_lineno(node_else) : last_lineno,
+ node_else ? nd_last_column(node_else) : last_column,
+ type == NODE_IF ? "else" : "then",
+ branches);
+ }
ADD_SEQ(ret, else_seq);
+ }
+ if (end_label) {
ADD_LABEL(ret, end_label);
+ }
- break;
- }
- case NODE_CASE:{
- NODE *vals;
- NODE *tempnode = node;
- LABEL *endlabel, *elselabel;
- DECL_ANCHOR(head);
- DECL_ANCHOR(body_seq);
- DECL_ANCHOR(cond_seq);
- int only_special_literals = 1;
- VALUE literals = rb_hash_new();
-
- INIT_ANCHOR(head);
- INIT_ANCHOR(body_seq);
- INIT_ANCHOR(cond_seq);
-
- rb_hash_tbl_raw(literals)->type = &cdhash_type;
-
- if (node->nd_head == 0) {
- COMPILE_(ret, "when", node->nd_body, poped);
- break;
- }
- COMPILE(head, "case base", node->nd_head);
-
- node = node->nd_body;
- type = nd_type(node);
- line = nd_line(node);
+ return COMPILE_OK;
+}
- if (type != NODE_WHEN) {
- COMPILE_ERROR(ERROR_ARGS "NODE_CASE: unexpected node. must be NODE_WHEN, but %s", ruby_node_name(type));
- debug_node_end();
- return COMPILE_NG;
- }
+static int
+compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
+{
+ const NODE *vals;
+ const NODE *node = orig_node;
+ LABEL *endlabel, *elselabel;
+ DECL_ANCHOR(head);
+ DECL_ANCHOR(body_seq);
+ DECL_ANCHOR(cond_seq);
+ int only_special_literals = 1;
+ VALUE literals = rb_hash_new();
+ int line, lineno, column, last_lineno, last_column;
+ enum node_type type;
+ VALUE branches = 0;
- endlabel = NEW_LABEL(line);
- elselabel = NEW_LABEL(line);
+ INIT_ANCHOR(head);
+ INIT_ANCHOR(body_seq);
+ INIT_ANCHOR(cond_seq);
- ADD_SEQ(ret, head); /* case VAL */
+ RHASH_TBL_RAW(literals)->type = &cdhash_type;
- while (type == NODE_WHEN) {
- LABEL *l1;
+ CHECK(COMPILE(head, "case base", node->nd_head));
- l1 = NEW_LABEL(line);
- ADD_LABEL(body_seq, l1);
- ADD_INSN(body_seq, line, pop);
- COMPILE_(body_seq, "when body", node->nd_body, poped);
- ADD_INSNL(body_seq, line, jump, endlabel);
+ DECL_BRANCH_BASE(branches, nd_first_lineno(node), nd_first_column(node), nd_last_lineno(node), nd_last_column(node), "case");
- vals = node->nd_head;
- if (vals) {
- switch (nd_type(vals)) {
- case NODE_ARRAY:
- only_special_literals = when_vals(iseq, cond_seq, vals, l1, only_special_literals, literals);
- break;
- case NODE_SPLAT:
- case NODE_ARGSCAT:
- case NODE_ARGSPUSH:
- only_special_literals = 0;
- ADD_INSN (cond_seq, nd_line(vals), dup);
- COMPILE(cond_seq, "when/cond splat", vals);
- ADD_INSN1(cond_seq, nd_line(vals), checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
- ADD_INSNL(cond_seq, nd_line(vals), branchif, l1);
- break;
- default:
- UNKNOWN_NODE("NODE_CASE", vals);
- }
- }
- else {
- EXPECT_NODE_NONULL("NODE_CASE", node, NODE_ARRAY);
- }
-
- node = node->nd_next;
- if (!node) {
- break;
- }
- type = nd_type(node);
- line = nd_line(node);
- }
- /* else */
- if (node) {
- ADD_LABEL(cond_seq, elselabel);
- ADD_INSN(cond_seq, line, pop);
- COMPILE_(cond_seq, "else", node, poped);
- ADD_INSNL(cond_seq, line, jump, endlabel);
- }
- else {
- debugs("== else (implicit)\n");
- ADD_LABEL(cond_seq, elselabel);
- ADD_INSN(cond_seq, nd_line(tempnode), pop);
- if (!poped) {
- ADD_INSN(cond_seq, nd_line(tempnode), putnil);
- }
- ADD_INSNL(cond_seq, nd_line(tempnode), jump, endlabel);
- }
-
- if (only_special_literals) {
- iseq_add_mark_object(iseq, literals);
-
- ADD_INSN(ret, nd_line(tempnode), dup);
- ADD_INSN2(ret, nd_line(tempnode), opt_case_dispatch, literals, elselabel);
- LABEL_REF(elselabel);
- }
-
- ADD_SEQ(ret, cond_seq);
- ADD_SEQ(ret, body_seq);
- ADD_LABEL(ret, endlabel);
- break;
- }
- case NODE_WHEN:{
- NODE *vals;
- NODE *val;
- NODE *orig_node = node;
- LABEL *endlabel;
- DECL_ANCHOR(body_seq);
-
- INIT_ANCHOR(body_seq);
- endlabel = NEW_LABEL(line);
-
- while (node && nd_type(node) == NODE_WHEN) {
- LABEL *l1 = NEW_LABEL(line = nd_line(node));
- ADD_LABEL(body_seq, l1);
- COMPILE_(body_seq, "when", node->nd_body, poped);
- ADD_INSNL(body_seq, line, jump, endlabel);
-
- vals = node->nd_head;
- if (!vals) {
- compile_bug(ERROR_ARGS "NODE_WHEN: must be NODE_ARRAY, but 0");
- }
+ node = node->nd_body;
+ EXPECT_NODE("NODE_CASE", node, NODE_WHEN, COMPILE_NG);
+ type = nd_type(node);
+ line = nd_line(node);
+ lineno = nd_first_lineno(node);
+ column = nd_first_column(node);
+ last_lineno = nd_last_lineno(node);
+ last_column = nd_last_column(node);
+
+ endlabel = NEW_LABEL(line);
+ elselabel = NEW_LABEL(line);
+
+ ADD_SEQ(ret, head); /* case VAL */
+
+ while (type == NODE_WHEN) {
+ LABEL *l1;
+
+ l1 = NEW_LABEL(line);
+ ADD_LABEL(body_seq, l1);
+ ADD_INSN(body_seq, line, pop);
+ ADD_TRACE_BRANCH_COVERAGE(
+ body_seq,
+ node->nd_body ? nd_first_lineno(node->nd_body) : lineno,
+ node->nd_body ? nd_first_column(node->nd_body) : column,
+ node->nd_body ? nd_last_lineno(node->nd_body) : last_lineno,
+ node->nd_body ? nd_last_column(node->nd_body) : last_column,
+ "when",
+ branches);
+ CHECK(COMPILE_(body_seq, "when body", node->nd_body, popped));
+ ADD_INSNL(body_seq, line, jump, endlabel);
+
+ vals = node->nd_head;
+ if (vals) {
switch (nd_type(vals)) {
case NODE_ARRAY:
- while (vals) {
- val = vals->nd_head;
- COMPILE(ret, "when2", val);
- ADD_INSNL(ret, nd_line(val), branchif, l1);
- vals = vals->nd_next;
- }
+ 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:
- ADD_INSN(ret, nd_line(vals), putnil);
- COMPILE(ret, "when2/cond splat", vals);
- ADD_INSN1(ret, nd_line(vals), checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
- ADD_INSNL(ret, nd_line(vals), branchif, l1);
+ only_special_literals = 0;
+ CHECK(when_splat_vals(iseq, cond_seq, vals, l1, only_special_literals, literals));
break;
default:
- UNKNOWN_NODE("NODE_WHEN", vals);
+ UNKNOWN_NODE("NODE_CASE", vals, COMPILE_NG);
}
- node = node->nd_next;
- }
- /* else */
- COMPILE_(ret, "else", node, poped);
- ADD_INSNL(ret, nd_line(orig_node), jump, endlabel);
-
- ADD_SEQ(ret, body_seq);
- ADD_LABEL(ret, endlabel);
-
- break;
- }
- case NODE_OPT_N:
- case NODE_WHILE:
- case NODE_UNTIL:{
- LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
- LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
- LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
- int prev_loopval_popped = ISEQ_COMPILE_DATA(iseq)->loopval_popped;
-
- struct iseq_compile_data_ensure_node_stack enl;
-
- LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(line); /* next */
- LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(line); /* redo */
- LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(line); /* break */
- LABEL *end_label = NEW_LABEL(line);
- LABEL *adjust_label = NEW_LABEL(line);
-
- LABEL *next_catch_label = NEW_LABEL(line);
- LABEL *tmp_label = NULL;
-
- ISEQ_COMPILE_DATA(iseq)->loopval_popped = 0;
- push_ensure_entry(iseq, &enl, 0, 0);
-
- if (type == NODE_OPT_N || node->nd_state == 1) {
- ADD_INSNL(ret, line, jump, next_label);
}
else {
- tmp_label = NEW_LABEL(line);
- ADD_INSNL(ret, line, jump, tmp_label);
+ EXPECT_NODE_NONULL("NODE_CASE", node, NODE_ARRAY, COMPILE_NG);
}
- ADD_LABEL(ret, adjust_label);
- ADD_INSN(ret, line, putnil);
- ADD_LABEL(ret, next_catch_label);
- ADD_INSN(ret, line, pop);
- ADD_INSNL(ret, line, jump, next_label);
- if (tmp_label) ADD_LABEL(ret, tmp_label);
-
- ADD_LABEL(ret, redo_label);
- COMPILE_POPED(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);
- }
- else if (type == NODE_UNTIL) {
- /* until */
- compile_branch_condition(iseq, ret, node->nd_cond,
- end_label, redo_label);
+ node = node->nd_next;
+ if (!node) {
+ break;
}
- else {
- ADD_CALL_RECEIVER(ret, line);
- ADD_CALL(ret, line, idGets, INT2FIX(0));
- ADD_INSNL(ret, line, branchif, redo_label);
- /* opt_n */
+ type = nd_type(node);
+ line = nd_line(node);
+ lineno = nd_first_lineno(node);
+ column = nd_first_column(node);
+ last_lineno = nd_last_lineno(node);
+ last_column = nd_last_column(node);
+ }
+ /* else */
+ if (node) {
+ ADD_LABEL(cond_seq, elselabel);
+ ADD_INSN(cond_seq, line, pop);
+ ADD_TRACE_BRANCH_COVERAGE(cond_seq, nd_first_lineno(node), nd_first_column(node), nd_last_lineno(node), nd_last_column(node), "else", branches);
+ CHECK(COMPILE_(cond_seq, "else", node, popped));
+ ADD_INSNL(cond_seq, line, jump, endlabel);
+ }
+ else {
+ debugs("== else (implicit)\n");
+ ADD_LABEL(cond_seq, elselabel);
+ ADD_INSN(cond_seq, nd_line(orig_node), pop);
+ ADD_TRACE_BRANCH_COVERAGE(cond_seq, nd_first_lineno(orig_node), nd_first_column(orig_node), nd_last_lineno(orig_node), nd_last_column(orig_node), "else", branches);
+ if (!popped) {
+ ADD_INSN(cond_seq, nd_line(orig_node), putnil);
}
+ ADD_INSNL(cond_seq, nd_line(orig_node), jump, endlabel);
+ }
- ADD_LABEL(ret, end_label);
- ADD_ADJUST_RESTORE(ret, adjust_label);
+ if (only_special_literals && ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
+ iseq_add_mark_object_compile_time(iseq, literals);
+
+ ADD_INSN(ret, nd_line(orig_node), dup);
+ ADD_INSN2(ret, nd_line(orig_node), opt_case_dispatch, literals, elselabel);
+ LABEL_REF(elselabel);
+ }
+
+ ADD_SEQ(ret, cond_seq);
+ ADD_SEQ(ret, body_seq);
+ ADD_LABEL(ret, endlabel);
+ return COMPILE_OK;
+}
- if (node->nd_state == Qundef) {
- /* ADD_INSN(ret, line, putundef); */
- compile_bug(ERROR_ARGS "unsupported: putundef");
+static int
+compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
+{
+ const NODE *vals;
+ const NODE *val;
+ const NODE *node = orig_node->nd_body;
+ LABEL *endlabel;
+ DECL_ANCHOR(body_seq);
+ VALUE branches = 0;
+
+ DECL_BRANCH_BASE(branches, nd_first_lineno(orig_node), nd_first_column(orig_node), nd_last_lineno(orig_node), nd_last_column(orig_node), "case");
+
+ INIT_ANCHOR(body_seq);
+ endlabel = NEW_LABEL(nd_line(node));
+
+ while (node && nd_type(node) == NODE_WHEN) {
+ const int line = nd_line(node);
+ const int lineno = nd_first_lineno(node);
+ const int column = nd_first_column(node);
+ const int last_lineno = nd_last_lineno(node);
+ const int last_column = nd_last_column(node);
+ LABEL *l1 = NEW_LABEL(line);
+ ADD_LABEL(body_seq, l1);
+ ADD_TRACE_BRANCH_COVERAGE(
+ body_seq,
+ node->nd_body ? nd_first_lineno(node->nd_body) : lineno,
+ node->nd_body ? nd_first_column(node->nd_body) : column,
+ node->nd_body ? nd_last_lineno(node->nd_body) : last_lineno,
+ node->nd_body ? nd_last_column(node->nd_body) : last_column,
+ "when",
+ branches);
+ CHECK(COMPILE_(body_seq, "when", node->nd_body, popped));
+ ADD_INSNL(body_seq, line, jump, endlabel);
+
+ vals = node->nd_head;
+ if (!vals) {
+ COMPILE_ERROR(ERROR_ARGS "NODE_WHEN: must be NODE_ARRAY, but 0");
+ return COMPILE_NG;
}
- else {
- ADD_INSN(ret, line, putnil);
+ switch (nd_type(vals)) {
+ case NODE_ARRAY:
+ 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, nd_line(vals), putnil);
+ CHECK(COMPILE(ret, "when2/cond splat", vals));
+ ADD_INSN1(ret, nd_line(vals), checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
+ ADD_INSNL(ret, nd_line(vals), branchif, l1);
+ break;
+ default:
+ UNKNOWN_NODE("NODE_WHEN", vals, COMPILE_NG);
}
+ node = node->nd_next;
+ }
+ /* else */
+ ADD_TRACE_BRANCH_COVERAGE(
+ ret,
+ node ? nd_first_lineno(node) : nd_first_lineno(orig_node),
+ node ? nd_first_column(node) : nd_first_column(orig_node),
+ node ? nd_last_lineno(node) : nd_last_lineno(orig_node),
+ node ? nd_last_column(node) : nd_last_column(orig_node),
+ "else",
+ branches);
+ CHECK(COMPILE_(ret, "else", node, popped));
+ ADD_INSNL(ret, nd_line(orig_node), jump, endlabel);
+
+ ADD_SEQ(ret, body_seq);
+ ADD_LABEL(ret, endlabel);
+ return COMPILE_OK;
+}
- ADD_LABEL(ret, break_label); /* break */
+static int
+compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
+{
+ const int line = (int)nd_line(node);
+ const int lineno = nd_first_lineno(node);
+ const int column = nd_first_column(node);
+ const int last_lineno = nd_last_lineno(node);
+ const int last_column = nd_last_column(node);
+ LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
+ LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
+ LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
+ int prev_loopval_popped = ISEQ_COMPILE_DATA(iseq)->loopval_popped;
+ VALUE branches = 0;
+
+ struct iseq_compile_data_ensure_node_stack enl;
+
+ LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(line); /* next */
+ LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(line); /* redo */
+ LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(line); /* break */
+ LABEL *end_label = NEW_LABEL(line);
+ LABEL *adjust_label = NEW_LABEL(line);
+
+ LABEL *next_catch_label = NEW_LABEL(line);
+ LABEL *tmp_label = NULL;
+
+ ISEQ_COMPILE_DATA(iseq)->loopval_popped = 0;
+ push_ensure_entry(iseq, &enl, NULL, NULL);
+
+ if (node->nd_state == 1) {
+ ADD_INSNL(ret, line, jump, next_label);
+ }
+ else {
+ tmp_label = NEW_LABEL(line);
+ ADD_INSNL(ret, line, jump, tmp_label);
+ }
+ ADD_LABEL(ret, adjust_label);
+ ADD_INSN(ret, line, putnil);
+ ADD_LABEL(ret, next_catch_label);
+ ADD_INSN(ret, line, pop);
+ ADD_INSNL(ret, line, jump, next_label);
+ if (tmp_label) ADD_LABEL(ret, tmp_label);
+
+ ADD_LABEL(ret, redo_label);
+ DECL_BRANCH_BASE(branches, lineno, column, last_lineno, last_column, type == NODE_WHILE ? "while" : "until");
+ ADD_TRACE_BRANCH_COVERAGE(
+ ret,
+ node->nd_body ? nd_first_lineno(node->nd_body) : lineno,
+ node->nd_body ? nd_first_column(node->nd_body) : column,
+ node->nd_body ? nd_last_lineno(node->nd_body) : last_lineno,
+ node->nd_body ? nd_last_column(node->nd_body) : last_column,
+ "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);
+ }
+ else {
+ /* until */
+ compile_branch_condition(iseq, ret, node->nd_cond,
+ end_label, redo_label);
+ }
- if (poped) {
- ADD_INSN(ret, line, pop);
- }
+ ADD_LABEL(ret, end_label);
+ ADD_ADJUST_RESTORE(ret, adjust_label);
- ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label,
- 0, break_label);
- ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, 0,
- next_catch_label);
- ADD_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, 0,
- ISEQ_COMPILE_DATA(iseq)->redo_label);
+ if (node->nd_state == Qundef) {
+ /* ADD_INSN(ret, line, putundef); */
+ COMPILE_ERROR(ERROR_ARGS "unsupported: putundef");
+ return COMPILE_NG;
+ }
+ else {
+ ADD_INSN(ret, line, putnil);
+ }
- ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
- ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
- ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label;
- ISEQ_COMPILE_DATA(iseq)->loopval_popped = prev_loopval_popped;
- ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->prev;
- break;
- }
- case NODE_FOR:
- if (node->nd_var) {
- /* massign to var in "for"
- * args.length == 1 && Array === (tmp = args[0]) ? tmp : args
- */
- NODE *var = node->nd_var;
- LABEL *not_single = NEW_LABEL(nd_line(var));
- LABEL *not_ary = NEW_LABEL(nd_line(var));
- COMPILE(ret, "for var", var);
- ADD_INSN(ret, line, dup);
- ADD_CALL(ret, line, idLength, INT2FIX(0));
- ADD_INSN1(ret, line, putobject, INT2FIX(1));
- ADD_CALL(ret, line, idEq, INT2FIX(1));
- ADD_INSNL(ret, line, branchunless, not_single);
- ADD_INSN(ret, line, dup);
- ADD_INSN1(ret, line, putobject, INT2FIX(0));
- ADD_CALL(ret, line, idAREF, INT2FIX(1));
- ADD_INSN1(ret, line, putobject, rb_cArray);
- ADD_INSN1(ret, line, topn, INT2FIX(1));
- ADD_CALL(ret, line, idEqq, INT2FIX(1));
- ADD_INSNL(ret, line, branchunless, not_ary);
- ADD_INSN(ret, line, swap);
- ADD_LABEL(ret, not_ary);
- ADD_INSN(ret, line, pop);
- ADD_LABEL(ret, not_single);
- break;
- }
- case NODE_ITER:{
- const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
- LABEL *retry_label = NEW_LABEL(line);
- LABEL *retry_end_l = NEW_LABEL(line);
+ ADD_LABEL(ret, break_label); /* break */
- ADD_LABEL(ret, retry_label);
- if (nd_type(node) == NODE_FOR) {
- COMPILE(ret, "iter caller (for)", node->nd_iter);
+ if (popped) {
+ ADD_INSN(ret, line, pop);
+ }
- ISEQ_COMPILE_DATA(iseq)->current_block = NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
- ISEQ_TYPE_BLOCK, line);
- ADD_SEND_WITH_BLOCK(ret, line, idEach, INT2FIX(0), ISEQ_COMPILE_DATA(iseq)->current_block);
- }
- else {
- ISEQ_COMPILE_DATA(iseq)->current_block = NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
- ISEQ_TYPE_BLOCK, line);
- COMPILE(ret, "iter caller", node->nd_iter);
- }
- ADD_LABEL(ret, retry_end_l);
+ ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, NULL,
+ break_label);
+ ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, NULL,
+ next_catch_label);
+ ADD_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, NULL,
+ ISEQ_COMPILE_DATA(iseq)->redo_label);
- if (poped) {
- ADD_INSN(ret, line, pop);
- }
+ ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
+ ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
+ ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label;
+ ISEQ_COMPILE_DATA(iseq)->loopval_popped = prev_loopval_popped;
+ ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->prev;
+ return COMPILE_OK;
+}
- ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
+static int
+compile_iter(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
+{
+ const int line = nd_line(node);
+ const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
+ LABEL *retry_label = NEW_LABEL(line);
+ LABEL *retry_end_l = NEW_LABEL(line);
+ const rb_iseq_t *child_iseq;
- ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, 0, retry_end_l);
+ ADD_LABEL(ret, retry_label);
+ if (nd_type(node) == NODE_FOR) {
+ CHECK(COMPILE(ret, "iter caller (for)", node->nd_iter));
- break;
- }
- case NODE_BREAK:{
- unsigned long level = 0;
+ 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, 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));
+ }
+ ADD_LABEL(ret, retry_end_l);
- if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0) {
- /* while/until */
- LABEL *splabel = NEW_LABEL(0);
- ADD_LABEL(ret, splabel);
- ADD_ADJUST(ret, line, ISEQ_COMPILE_DATA(iseq)->redo_label);
- 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, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
- ADD_ADJUST_RESTORE(ret, splabel);
+ if (popped) {
+ ADD_INSN(ret, line, pop);
+ }
- if (!poped) {
- ADD_INSN(ret, line, putnil);
- }
- }
- else if (iseq->body->type == ISEQ_TYPE_BLOCK) {
- break_by_insn:
- /* escape from block */
- COMPILE(ret, "break val (block)", node->nd_stts);
- ADD_INSN1(ret, line, throw, INT2FIX(level | TAG_BREAK));
- if (poped) {
- ADD_INSN(ret, line, pop);
- }
- }
- else if (iseq->body->type == ISEQ_TYPE_EVAL) {
- break_in_eval:
- COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with break");
- debug_node_end();
- return COMPILE_NG;
- }
- else {
- const rb_iseq_t *ip = iseq->body->parent_iseq;
+ ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
- while (ip) {
- if (!ISEQ_COMPILE_DATA(ip)) {
- ip = 0;
- break;
- }
+ ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, child_iseq, retry_end_l);
+ return COMPILE_OK;
+}
- level++;
- if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
- level = VM_THROW_NO_ESCAPE_FLAG;
- goto break_by_insn;
- }
- else if (ip->body->type == ISEQ_TYPE_BLOCK) {
- level <<= VM_THROW_LEVEL_SHIFT;
- goto break_by_insn;
- }
- else if (ip->body->type == ISEQ_TYPE_EVAL) {
- goto break_in_eval;
- }
+static int
+compile_for_masgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
+{
+ /* massign to var in "for"
+ * (args.length == 1 && Array.try_convert(args[0])) || args
+ */
+ const int line = nd_line(node);
+ const NODE *var = node->nd_var;
+ LABEL *not_single = NEW_LABEL(nd_line(var));
+ LABEL *not_ary = NEW_LABEL(nd_line(var));
+ CHECK(COMPILE(ret, "for var", var));
+ ADD_INSN(ret, line, dup);
+ ADD_CALL(ret, line, idLength, INT2FIX(0));
+ ADD_INSN1(ret, line, putobject, INT2FIX(1));
+ ADD_CALL(ret, line, idEq, INT2FIX(1));
+ ADD_INSNL(ret, line, branchunless, not_single);
+ ADD_INSN(ret, line, dup);
+ ADD_INSN1(ret, line, putobject, INT2FIX(0));
+ ADD_CALL(ret, line, idAREF, INT2FIX(1));
+ ADD_INSN1(ret, line, putobject, rb_cArray);
+ ADD_INSN(ret, line, swap);
+ ADD_CALL(ret, line, rb_intern("try_convert"), INT2FIX(1));
+ ADD_INSN(ret, line, dup);
+ ADD_INSNL(ret, line, branchunless, not_ary);
+ ADD_INSN(ret, line, swap);
+ ADD_LABEL(ret, not_ary);
+ ADD_INSN(ret, line, pop);
+ ADD_LABEL(ret, not_single);
+ return COMPILE_OK;
+}
- ip = ip->body->parent_iseq;
- }
- COMPILE_ERROR(ERROR_ARGS "Invalid break");
- debug_node_end();
- return COMPILE_NG;
+static int
+compile_break(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
+{
+ const int line = nd_line(node);
+ unsigned long throw_flag = 0;
+
+ if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0) {
+ /* while/until */
+ LABEL *splabel = NEW_LABEL(0);
+ ADD_LABEL(ret, splabel);
+ ADD_ADJUST(ret, line, 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, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
+ ADD_ADJUST_RESTORE(ret, splabel);
+
+ if (!popped) {
+ ADD_INSN(ret, line, putnil);
}
- break;
- }
- case NODE_NEXT:{
- unsigned long level = 0;
+ }
+ else if (iseq->body->type == ISEQ_TYPE_BLOCK) {
+ break_by_insn:
+ /* escape from block */
+ CHECK(COMPILE(ret, "break val (block)", node->nd_stts));
+ ADD_INSN1(ret, line, throw, INT2FIX(throw_flag | TAG_BREAK));
+ if (popped) {
+ ADD_INSN(ret, line, pop);
+ }
+ }
+ else if (iseq->body->type == ISEQ_TYPE_EVAL) {
+ break_in_eval:
+ COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with break");
+ return COMPILE_NG;
+ }
+ else {
+ const rb_iseq_t *ip = iseq->body->parent_iseq;
- if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0) {
- LABEL *splabel = NEW_LABEL(0);
- debugs("next in while loop\n");
- ADD_LABEL(ret, splabel);
- COMPILE(ret, "next val/valid syntax?", node->nd_stts);
- add_ensure_iseq(ret, iseq, 0);
- ADD_ADJUST(ret, line, ISEQ_COMPILE_DATA(iseq)->redo_label);
- ADD_INSNL(ret, line, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
- ADD_ADJUST_RESTORE(ret, splabel);
- if (!poped) {
- ADD_INSN(ret, line, putnil);
+ while (ip) {
+ if (!ISEQ_COMPILE_DATA(ip)) {
+ ip = 0;
+ break;
}
- }
- else if (ISEQ_COMPILE_DATA(iseq)->end_label) {
- LABEL *splabel = NEW_LABEL(0);
- debugs("next in block\n");
- ADD_LABEL(ret, splabel);
- ADD_ADJUST(ret, line, ISEQ_COMPILE_DATA(iseq)->start_label);
- COMPILE(ret, "next val", node->nd_stts);
- add_ensure_iseq(ret, iseq, 0);
- ADD_INSNL(ret, line, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
- ADD_ADJUST_RESTORE(ret, splabel);
- if (!poped) {
- ADD_INSN(ret, line, putnil);
+ if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
+ throw_flag = VM_THROW_NO_ESCAPE_FLAG;
+ goto break_by_insn;
}
+ else if (ip->body->type == ISEQ_TYPE_BLOCK) {
+ goto break_by_insn;
+ }
+ else if (ip->body->type == ISEQ_TYPE_EVAL) {
+ goto break_in_eval;
+ }
+
+ ip = ip->body->parent_iseq;
}
- else if (iseq->body->type == ISEQ_TYPE_EVAL) {
- next_in_eval:
- COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with next");
- }
- else {
- const rb_iseq_t *ip = iseq;
+ COMPILE_ERROR(ERROR_ARGS "Invalid break");
+ return COMPILE_NG;
+ }
+ return COMPILE_OK;
+}
- while (ip) {
- if (!ISEQ_COMPILE_DATA(ip)) {
- ip = 0;
- break;
- }
+static int
+compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
+{
+ const int line = nd_line(node);
+ unsigned long throw_flag = 0;
+
+ if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0) {
+ 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, ISEQ_COMPILE_DATA(iseq)->redo_label);
+ ADD_INSNL(ret, line, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
+ ADD_ADJUST_RESTORE(ret, splabel);
+ if (!popped) {
+ ADD_INSN(ret, line, putnil);
+ }
+ }
+ else if (ISEQ_COMPILE_DATA(iseq)->end_label) {
+ LABEL *splabel = NEW_LABEL(0);
+ debugs("next in block\n");
+ ADD_LABEL(ret, splabel);
+ ADD_ADJUST(ret, line, ISEQ_COMPILE_DATA(iseq)->start_label);
+ CHECK(COMPILE(ret, "next val", node->nd_stts));
+ add_ensure_iseq(ret, iseq, 0);
+ ADD_INSNL(ret, line, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
+ ADD_ADJUST_RESTORE(ret, splabel);
+ splabel->unremovable = FALSE;
- level = VM_THROW_NO_ESCAPE_FLAG;
- if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
- /* while loop */
- break;
- }
- else if (ip->body->type == ISEQ_TYPE_BLOCK) {
- break;
- }
- else if (ip->body->type == ISEQ_TYPE_EVAL) {
- goto next_in_eval;
- }
+ if (!popped) {
+ ADD_INSN(ret, line, putnil);
+ }
+ }
+ else if (iseq->body->type == ISEQ_TYPE_EVAL) {
+ next_in_eval:
+ COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with next");
+ return COMPILE_NG;
+ }
+ else {
+ const rb_iseq_t *ip = iseq;
- ip = ip->body->parent_iseq;
+ while (ip) {
+ if (!ISEQ_COMPILE_DATA(ip)) {
+ ip = 0;
+ break;
}
- if (ip != 0) {
- COMPILE(ret, "next val", node->nd_stts);
- ADD_INSN1(ret, line, throw, INT2FIX(level | TAG_NEXT));
- if (poped) {
- ADD_INSN(ret, line, pop);
- }
+ throw_flag = VM_THROW_NO_ESCAPE_FLAG;
+ if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
+ /* while loop */
+ break;
}
- else {
- COMPILE_ERROR(ERROR_ARGS "Invalid next");
+ else if (ip->body->type == ISEQ_TYPE_BLOCK) {
+ break;
+ }
+ else if (ip->body->type == ISEQ_TYPE_EVAL) {
+ goto next_in_eval;
}
+
+ ip = ip->body->parent_iseq;
}
- break;
- }
- case NODE_REDO:{
- if (ISEQ_COMPILE_DATA(iseq)->redo_label) {
- LABEL *splabel = NEW_LABEL(0);
- debugs("redo in while");
- ADD_LABEL(ret, splabel);
- ADD_ADJUST(ret, line, ISEQ_COMPILE_DATA(iseq)->redo_label);
- add_ensure_iseq(ret, iseq, 0);
- ADD_INSNL(ret, line, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
- ADD_ADJUST_RESTORE(ret, splabel);
- if (!poped) {
- ADD_INSN(ret, line, putnil);
+ if (ip != 0) {
+ CHECK(COMPILE(ret, "next val", node->nd_stts));
+ ADD_INSN1(ret, line, throw, INT2FIX(throw_flag | TAG_NEXT));
+
+ if (popped) {
+ ADD_INSN(ret, line, pop);
}
}
- else if (iseq->body->type == ISEQ_TYPE_EVAL) {
- redo_in_eval:
- COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with redo");
+ else {
+ COMPILE_ERROR(ERROR_ARGS "Invalid next");
+ return COMPILE_NG;
}
- else if (ISEQ_COMPILE_DATA(iseq)->start_label) {
- LABEL *splabel = NEW_LABEL(0);
+ }
+ return COMPILE_OK;
+}
- debugs("redo in block");
- ADD_LABEL(ret, splabel);
- add_ensure_iseq(ret, iseq, 0);
- ADD_ADJUST(ret, line, ISEQ_COMPILE_DATA(iseq)->start_label);
- ADD_INSNL(ret, line, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
- ADD_ADJUST_RESTORE(ret, splabel);
+static int
+compile_redo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
+{
+ const int line = nd_line(node);
- if (!poped) {
- ADD_INSN(ret, line, putnil);
- }
+ if (ISEQ_COMPILE_DATA(iseq)->redo_label) {
+ LABEL *splabel = NEW_LABEL(0);
+ debugs("redo in while");
+ ADD_LABEL(ret, splabel);
+ ADD_ADJUST(ret, line, ISEQ_COMPILE_DATA(iseq)->redo_label);
+ add_ensure_iseq(ret, iseq, 0);
+ ADD_INSNL(ret, line, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
+ ADD_ADJUST_RESTORE(ret, splabel);
+ if (!popped) {
+ ADD_INSN(ret, line, putnil);
}
- else {
- const rb_iseq_t *ip = iseq;
- const unsigned long level = VM_THROW_NO_ESCAPE_FLAG;
+ }
+ else if (iseq->body->type == ISEQ_TYPE_EVAL) {
+ redo_in_eval:
+ COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with redo");
+ return COMPILE_NG;
+ }
+ else if (ISEQ_COMPILE_DATA(iseq)->start_label) {
+ LABEL *splabel = NEW_LABEL(0);
- while (ip) {
- if (!ISEQ_COMPILE_DATA(ip)) {
- ip = 0;
- break;
- }
+ debugs("redo in block");
+ ADD_LABEL(ret, splabel);
+ add_ensure_iseq(ret, iseq, 0);
+ ADD_ADJUST(ret, line, ISEQ_COMPILE_DATA(iseq)->start_label);
+ ADD_INSNL(ret, line, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
+ ADD_ADJUST_RESTORE(ret, splabel);
- if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
- break;
- }
- else if (ip->body->type == ISEQ_TYPE_BLOCK) {
- break;
- }
- else if (ip->body->type == ISEQ_TYPE_EVAL) {
- goto redo_in_eval;
- }
+ if (!popped) {
+ ADD_INSN(ret, line, putnil);
+ }
+ }
+ else {
+ const rb_iseq_t *ip = iseq;
- ip = ip->body->parent_iseq;
+ while (ip) {
+ if (!ISEQ_COMPILE_DATA(ip)) {
+ ip = 0;
+ break;
}
- if (ip != 0) {
- ADD_INSN(ret, line, putnil);
- ADD_INSN1(ret, line, throw, INT2FIX(level | TAG_REDO));
- if (poped) {
- ADD_INSN(ret, line, pop);
- }
+ if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
+ break;
}
- else {
- COMPILE_ERROR(ERROR_ARGS "Invalid redo");
+ else if (ip->body->type == ISEQ_TYPE_BLOCK) {
+ break;
}
+ else if (ip->body->type == ISEQ_TYPE_EVAL) {
+ goto redo_in_eval;
+ }
+
+ ip = ip->body->parent_iseq;
}
- break;
- }
- case NODE_RETRY:{
- if (iseq->body->type == ISEQ_TYPE_RESCUE) {
+ if (ip != 0) {
ADD_INSN(ret, line, putnil);
- ADD_INSN1(ret, line, throw, INT2FIX(TAG_RETRY));
+ ADD_INSN1(ret, line, throw, INT2FIX(VM_THROW_NO_ESCAPE_FLAG | TAG_REDO));
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
}
else {
- COMPILE_ERROR(ERROR_ARGS "Invalid retry");
+ COMPILE_ERROR(ERROR_ARGS "Invalid redo");
+ return COMPILE_NG;
}
- break;
- }
- case NODE_BEGIN:{
- COMPILE_(ret, "NODE_BEGIN", node->nd_body, poped);
- break;
- }
- case NODE_RESCUE:{
- LABEL *lstart = NEW_LABEL(line);
- LABEL *lend = NEW_LABEL(line);
- LABEL *lcont = NEW_LABEL(line);
- const rb_iseq_t *rescue = NEW_CHILD_ISEQ(node->nd_resq,
- rb_str_concat(rb_str_new2("rescue in "), iseq->body->location.label),
- ISEQ_TYPE_RESCUE, line);
+ }
+ return COMPILE_OK;
+}
- lstart->rescued = LABEL_RESCUE_BEG;
- lend->rescued = LABEL_RESCUE_END;
- ADD_LABEL(ret, lstart);
- COMPILE(ret, "rescue head", node->nd_head);
- ADD_LABEL(ret, lend);
- if (node->nd_else) {
- ADD_INSN(ret, line, pop);
- COMPILE(ret, "rescue else", node->nd_else);
- }
- ADD_INSN(ret, line, nop);
- ADD_LABEL(ret, lcont);
+static int
+compile_retry(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
+{
+ const int line = nd_line(node);
- if (poped) {
+ if (iseq->body->type == ISEQ_TYPE_RESCUE) {
+ ADD_INSN(ret, line, putnil);
+ ADD_INSN1(ret, line, throw, INT2FIX(TAG_RETRY));
+
+ if (popped) {
ADD_INSN(ret, line, pop);
}
+ }
+ else {
+ COMPILE_ERROR(ERROR_ARGS "Invalid retry");
+ return COMPILE_NG;
+ }
+ return COMPILE_OK;
+}
- /* register catch entry */
- ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lcont);
- ADD_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, 0, lstart);
- break;
- }
- case NODE_RESBODY:{
- NODE *resq = node;
- NODE *narg;
- 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_ARRAY:
- while (narg) {
- ADD_GETLOCAL(ret, line, LVAR_ERRINFO, 0);
- COMPILE(ret, "rescue arg", narg->nd_head);
- ADD_INSN1(ret, line, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
- ADD_INSNL(ret, line, branchif, label_hit);
- narg = narg->nd_next;
- }
- break;
- case NODE_SPLAT:
- case NODE_ARGSCAT:
- case NODE_ARGSPUSH:
+static int
+compile_rescue(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
+{
+ const int line = nd_line(node);
+ LABEL *lstart = NEW_LABEL(line);
+ LABEL *lend = NEW_LABEL(line);
+ LABEL *lcont = NEW_LABEL(line);
+ const rb_iseq_t *rescue = NEW_CHILD_ISEQ(node->nd_resq,
+ rb_str_concat(rb_str_new2("rescue in "), iseq->body->location.label),
+ ISEQ_TYPE_RESCUE, line);
+
+ lstart->rescued = LABEL_RESCUE_BEG;
+ lend->rescued = LABEL_RESCUE_END;
+ ADD_LABEL(ret, lstart);
+ CHECK(COMPILE(ret, "rescue head", node->nd_head));
+ ADD_LABEL(ret, lend);
+ if (node->nd_else) {
+ ADD_INSN(ret, line, pop);
+ CHECK(COMPILE(ret, "rescue else", node->nd_else));
+ }
+ ADD_INSN(ret, line, nop);
+ ADD_LABEL(ret, lcont);
+
+ if (popped) {
+ ADD_INSN(ret, line, pop);
+ }
+
+ /* register catch entry */
+ ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lcont);
+ ADD_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
+ return COMPILE_OK;
+}
+
+static int
+compile_resbody(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
+{
+ const int line = nd_line(node);
+ const NODE *resq = node;
+ const NODE *narg;
+ 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_ARRAY:
+ while (narg) {
ADD_GETLOCAL(ret, line, LVAR_ERRINFO, 0);
- COMPILE(ret, "rescue/cond splat", narg);
- ADD_INSN1(ret, line, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE | VM_CHECKMATCH_ARRAY));
+ CHECK(COMPILE(ret, "rescue arg", narg->nd_head));
+ ADD_INSN1(ret, line, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
ADD_INSNL(ret, line, branchif, label_hit);
- break;
- default:
- UNKNOWN_NODE("NODE_RESBODY", narg);
+ narg = narg->nd_next;
}
- }
- else {
+ break;
+ case NODE_SPLAT:
+ case NODE_ARGSCAT:
+ case NODE_ARGSPUSH:
ADD_GETLOCAL(ret, line, LVAR_ERRINFO, 0);
- ADD_INSN1(ret, line, putobject, rb_eStandardError);
- ADD_INSN1(ret, line, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
+ CHECK(COMPILE(ret, "rescue/cond splat", narg));
+ ADD_INSN1(ret, line, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE | VM_CHECKMATCH_ARRAY));
ADD_INSNL(ret, line, branchif, label_hit);
+ break;
+ default:
+ UNKNOWN_NODE("NODE_RESBODY", narg, COMPILE_NG);
}
- ADD_INSNL(ret, line, jump, label_miss);
- ADD_LABEL(ret, label_hit);
- COMPILE(ret, "resbody body", resq->nd_body);
- if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization) {
- ADD_INSN(ret, line, nop);
- }
- ADD_INSN(ret, line, leave);
- ADD_LABEL(ret, label_miss);
- resq = resq->nd_head;
}
- break;
- }
- case NODE_ENSURE:{
- DECL_ANCHOR(ensr);
- const rb_iseq_t *ensure = NEW_CHILD_ISEQ(node->nd_ensr,
- rb_str_concat(rb_str_new2 ("ensure in "), iseq->body->location.label),
- ISEQ_TYPE_ENSURE, line);
- LABEL *lstart = NEW_LABEL(line);
- LABEL *lend = NEW_LABEL(line);
- LABEL *lcont = NEW_LABEL(line);
- struct ensure_range er;
- struct iseq_compile_data_ensure_node_stack enl;
- struct ensure_range *erange;
+ else {
+ ADD_GETLOCAL(ret, line, LVAR_ERRINFO, 0);
+ ADD_INSN1(ret, line, putobject, rb_eStandardError);
+ ADD_INSN1(ret, line, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
+ ADD_INSNL(ret, line, branchif, label_hit);
+ }
+ ADD_INSNL(ret, line, 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, nop);
+ }
+ ADD_INSN(ret, line, leave);
+ ADD_LABEL(ret, label_miss);
+ resq = resq->nd_head;
+ }
+ return COMPILE_OK;
+}
- INIT_ANCHOR(ensr);
- COMPILE_POPED(ensr, "ensure ensr", node->nd_ensr);
+static int
+compile_ensure(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
+{
+ const int line = nd_line(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->location.label),
+ ISEQ_TYPE_ENSURE, line);
+ LABEL *lstart = NEW_LABEL(line);
+ LABEL *lend = NEW_LABEL(line);
+ LABEL *lcont = NEW_LABEL(line);
+ LINK_ELEMENT *last;
+ int last_leave = 0;
+ struct ensure_range er;
+ struct iseq_compile_data_ensure_node_stack enl;
+ struct ensure_range *erange;
- er.begin = lstart;
- er.end = lend;
- er.next = 0;
- push_ensure_entry(iseq, &enl, &er, node->nd_ensr);
+ INIT_ANCHOR(ensr);
+ CHECK(COMPILE_POPPED(ensr, "ensure ensr", node->nd_ensr));
+ last = ensr->last;
+ last_leave = last && IS_INSN(last) && IS_INSN_ID(last, leave);
- ADD_LABEL(ret, lstart);
- COMPILE_(ret, "ensure head", node->nd_head, poped);
- ADD_LABEL(ret, lend);
- if (ensr->anchor.next == 0) {
- ADD_INSN(ret, line, nop);
- }
- else {
- ADD_SEQ(ret, ensr);
- }
- ADD_LABEL(ret, lcont);
+ er.begin = lstart;
+ er.end = lend;
+ er.next = 0;
+ push_ensure_entry(iseq, &enl, &er, node->nd_ensr);
- erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
+ ADD_LABEL(ret, lstart);
+ CHECK(COMPILE_(ret, "ensure head", node->nd_head, (popped | last_leave)));
+ ADD_LABEL(ret, lend);
+ ADD_SEQ(ret, ensr);
+ if (!popped && last_leave) ADD_INSN(ret, line, putnil);
+ ADD_LABEL(ret, lcont);
+ if (last_leave) ADD_INSN(ret, line, pop);
+
+ 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;
}
+ }
+
+ ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
+ return COMPILE_OK;
+}
+
+static int
+compile_return(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
+{
+ const int line = nd_line(node);
+
+ if (iseq) {
+ enum iseq_type type = iseq->body->type;
+ const rb_iseq_t *is = iseq;
+ enum iseq_type t = type;
+ const NODE *retval = node->nd_stts;
+ LABEL *splabel = 0;
+
+ while (t == ISEQ_TYPE_RESCUE || t == ISEQ_TYPE_ENSURE) {
+ if (!(is = is->body->parent_iseq)) break;
+ t = is->body->type;
+ }
+ switch (t) {
+ case ISEQ_TYPE_TOP:
+ case ISEQ_TYPE_MAIN:
+ 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, 0);
+ }
+
+ CHECK(COMPILE(ret, "return nd_stts (return val)", retval));
+
+ if (type == ISEQ_TYPE_METHOD) {
+ add_ensure_iseq(ret, iseq, 1);
+ ADD_TRACE(ret, RUBY_EVENT_RETURN);
+ ADD_INSN(ret, line, leave);
+ ADD_ADJUST_RESTORE(ret, splabel);
+
+ if (!popped) {
+ ADD_INSN(ret, line, putnil);
+ }
+ }
+ else {
+ ADD_INSN1(ret, line, throw, INT2FIX(TAG_RETURN));
+ if (popped) {
+ ADD_INSN(ret, line, pop);
+ }
+ }
+ }
+ return COMPILE_OK;
+}
+
+static int
+compile_evstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
+{
+ CHECK(COMPILE_(ret, "nd_body", node, popped));
+
+ if (!popped && !all_string_result_p(node)) {
+ const int line = nd_line(node);
+ const unsigned int flag = VM_CALL_FCALL;
+ LABEL *isstr = NEW_LABEL(line);
+ ADD_INSN(ret, line, dup);
+ ADD_INSN1(ret, line, checktype, INT2FIX(T_STRING));
+ ADD_INSNL(ret, line, branchif, isstr);
+ ADD_INSN(ret, line, dup);
+ ADD_SEND_R(ret, line, idTo_s, INT2FIX(0), NULL, INT2FIX(flag), NULL);
+ ADD_INSN(ret, line, tostring);
+ ADD_LABEL(ret, isstr);
+ }
+ return COMPILE_OK;
+}
+
+static int iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped);
+/**
+ compile each node
+
+ self: InstructionSequence
+ node: Ruby compiled node
+ popped: This node will be popped
+ */
+static int
+iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *node, int popped)
+{
+ if (node == 0) {
+ if (!popped) {
+ int lineno = ISEQ_COMPILE_DATA(iseq)->last_line;
+ if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq));
+ debugs("node: NODE_NIL(implicit)\n");
+ ADD_INSN(ret, lineno, putnil);
+ }
+ return COMPILE_OK;
+ }
+ return iseq_compile_each0(iseq, ret, node, popped);
+}
+
+static int
+iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped)
+{
+ const int line = (int)nd_line(node);
+ const enum node_type type = nd_type(node);
+ struct rb_iseq_constant_body *const body = iseq->body;
+
+ if (ISEQ_COMPILE_DATA(iseq)->last_line == line) {
+ /* 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);
+ }
+ }
- ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
+ debug_node_start(node);
+#undef BEFORE_RETURN
+#define BEFORE_RETURN debug_node_end()
+
+ switch (type) {
+ case NODE_BLOCK:{
+ while (node && nd_type(node) == NODE_BLOCK) {
+ CHECK(COMPILE_(ret, "BLOCK body", node->nd_head,
+ (node->nd_next ? 1 : popped)));
+ node = node->nd_next;
+ }
+ if (node) {
+ CHECK(COMPILE_(ret, "BLOCK next", node->nd_next, popped));
+ }
break;
}
+ case NODE_IF:
+ case NODE_UNLESS:
+ CHECK(compile_if(iseq, ret, node, popped, type));
+ break;
+ case NODE_CASE:
+ CHECK(compile_case(iseq, ret, node, popped));
+ break;
+ case NODE_CASE2:
+ CHECK(compile_case2(iseq, ret, node, popped));
+ break;
+ case NODE_WHILE:
+ case NODE_UNTIL:
+ CHECK(compile_loop(iseq, ret, node, popped, type));
+ break;
+ case NODE_FOR:
+ case NODE_ITER:
+ CHECK(compile_iter(iseq, ret, node, popped));
+ break;
+ case NODE_FOR_MASGN:
+ CHECK(compile_for_masgn(iseq, ret, node, popped));
+ break;
+ case NODE_BREAK:
+ CHECK(compile_break(iseq, ret, node, popped));
+ break;
+ case NODE_NEXT:
+ CHECK(compile_next(iseq, ret, node, popped));
+ break;
+ case NODE_REDO:
+ CHECK(compile_redo(iseq, ret, node, popped));
+ break;
+ case NODE_RETRY:
+ CHECK(compile_retry(iseq, ret, node, popped));
+ break;
+ case NODE_BEGIN:{
+ CHECK(COMPILE_(ret, "NODE_BEGIN", node->nd_body, popped));
+ break;
+ }
+ case NODE_RESCUE:
+ CHECK(compile_rescue(iseq, ret, node, popped));
+ break;
+ case NODE_RESBODY:
+ CHECK(compile_resbody(iseq, ret, node, popped));
+ break;
+ case NODE_ENSURE:
+ CHECK(compile_ensure(iseq, ret, node, popped));
+ break;
case NODE_AND:
case NODE_OR:{
LABEL *end_label = NEW_LABEL(line);
- COMPILE(ret, "nd_1st", node->nd_1st);
- if (!poped) {
+ CHECK(COMPILE(ret, "nd_1st", node->nd_1st));
+ if (!popped) {
ADD_INSN(ret, line, dup);
}
if (type == NODE_AND) {
@@ -4619,27 +6016,27 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
else {
ADD_INSNL(ret, line, branchif, end_label);
}
- if (!poped) {
+ if (!popped) {
ADD_INSN(ret, line, pop);
}
- COMPILE_(ret, "nd_2nd", node->nd_2nd, poped);
+ CHECK(COMPILE_(ret, "nd_2nd", node->nd_2nd, popped));
ADD_LABEL(ret, end_label);
break;
}
case NODE_MASGN:{
- compile_massign(iseq, ret, node, poped);
+ compile_massign(iseq, ret, node, popped);
break;
}
case NODE_LASGN:{
ID id = node->nd_vid;
- int idx = iseq->body->local_iseq->body->local_table_size - get_local_var_idx(iseq, id);
+ int idx = body->local_iseq->body->local_table_size - get_local_var_idx(iseq, id);
- debugs("lvar: %"PRIsVALUE" idx: %d\n", rb_id2str(id), idx);
- COMPILE(ret, "rvalue", node->nd_value);
+ debugs("lvar: %s idx: %d\n", rb_id2name(id), idx);
+ CHECK(COMPILE(ret, "rvalue", node->nd_value));
- if (!poped) {
+ if (!popped) {
ADD_INSN(ret, line, dup);
}
ADD_SETLOCAL(ret, line, idx, get_lvar_level(iseq));
@@ -4648,36 +6045,37 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
case NODE_DASGN:
case NODE_DASGN_CURR:{
int idx, lv, ls;
- COMPILE(ret, "dvalue", node->nd_value);
- debugi("dassn id", rb_id2str(node->nd_vid) ? node->nd_vid : '*');
+ ID id = node->nd_vid;
+ CHECK(COMPILE(ret, "dvalue", node->nd_value));
+ debugi("dassn id", rb_id2str(id) ? id : '*');
- if (!poped) {
+ if (!popped) {
ADD_INSN(ret, line, dup);
}
- idx = get_dyna_var_idx(iseq, node->nd_vid, &lv, &ls);
+ idx = get_dyna_var_idx(iseq, id, &lv, &ls);
if (idx < 0) {
- compile_bug(ERROR_ARGS "NODE_DASGN(_CURR): unknown id (%"PRIsVALUE")",
- rb_id2str(node->nd_vid));
+ COMPILE_ERROR(ERROR_ARGS "NODE_DASGN(_CURR): unknown id (%"PRIsVALUE")",
+ rb_id2str(id));
+ goto ng;
}
ADD_SETLOCAL(ret, line, ls - idx, lv);
break;
}
case NODE_GASGN:{
- COMPILE(ret, "lvalue", node->nd_value);
+ CHECK(COMPILE(ret, "lvalue", node->nd_value));
- if (!poped) {
+ if (!popped) {
ADD_INSN(ret, line, dup);
}
ADD_INSN1(ret, line, setglobal,
((VALUE)node->nd_entry | 1));
break;
}
- case NODE_IASGN:
- case NODE_IASGN2:{
- COMPILE(ret, "lvalue", node->nd_value);
- if (!poped) {
+ case NODE_IASGN:{
+ CHECK(COMPILE(ret, "lvalue", node->nd_value));
+ if (!popped) {
ADD_INSN(ret, line, dup);
}
ADD_INSN2(ret, line, setinstancevariable,
@@ -4686,9 +6084,9 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
break;
}
case NODE_CDECL:{
- COMPILE(ret, "lvalue", node->nd_value);
+ CHECK(COMPILE(ret, "lvalue", node->nd_value));
- if (!poped) {
+ if (!popped) {
ADD_INSN(ret, line, dup);
}
@@ -4704,8 +6102,8 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
break;
}
case NODE_CVASGN:{
- COMPILE(ret, "cvasgn val", node->nd_value);
- if (!poped) {
+ CHECK(COMPILE(ret, "cvasgn val", node->nd_value));
+ if (!popped) {
ADD_INSN(ret, line, dup);
}
ADD_INSN1(ret, line, setclassvariable,
@@ -4743,7 +6141,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
* nd_mid
*/
- if (!poped) {
+ if (!popped) {
ADD_INSN(ret, line, putnil);
}
asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN1 recv", node);
@@ -4756,15 +6154,15 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
default:
INIT_ANCHOR(args);
argc = setup_args(iseq, args, node->nd_args->nd_head, &flag, NULL);
+ CHECK(!NIL_P(argc));
ADD_SEQ(ret, args);
}
ADD_INSN1(ret, line, dupn, FIXNUM_INC(argc, 1 + boff));
ADD_SEND_WITH_FLAG(ret, line, idAREF, argc, INT2FIX(flag));
flag |= asgnflag;
- if (id == 0 || id == 1) {
- /* 0: or, 1: and
- a[x] ||= y
+ if (id == idOROP || id == idANDOP) {
+ /* a[x] ||= y or a[x] &&= y
unless/if a[x]
a[x]= y
@@ -4776,18 +6174,16 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
LABEL *lfin = NEW_LABEL(line);
ADD_INSN(ret, line, dup);
- if (id == 0) {
- /* or */
+ if (id == idOROP) {
ADD_INSNL(ret, line, branchif, label);
}
- else {
- /* and */
+ else { /* idANDOP */
ADD_INSNL(ret, line, branchunless, label);
}
ADD_INSN(ret, line, pop);
- COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body);
- if (!poped) {
+ CHECK(COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body));
+ if (!popped) {
ADD_INSN1(ret, line, setn, FIXNUM_INC(argc, 2+boff));
}
if (flag & VM_CALL_ARGS_SPLAT) {
@@ -4813,16 +6209,16 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_INSN(ret, line, pop);
ADD_INSNL(ret, line, jump, lfin);
ADD_LABEL(ret, label);
- if (!poped) {
+ if (!popped) {
ADD_INSN1(ret, line, setn, FIXNUM_INC(argc, 2+boff));
}
ADD_INSN1(ret, line, adjuststack, FIXNUM_INC(argc, 2+boff));
ADD_LABEL(ret, lfin);
}
else {
- COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body);
+ CHECK(COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body));
ADD_SEND(ret, line, id, INT2FIX(1));
- if (!poped) {
+ if (!popped) {
ADD_INSN1(ret, line, setn, FIXNUM_INC(argc, 2+boff));
}
if (flag & VM_CALL_ARGS_SPLAT) {
@@ -4908,16 +6304,16 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_INSN(ret, line, dup);
ADD_SEND(ret, line, vid, INT2FIX(0));
- if (atype == 0 || atype == 1) { /* 0: OR or 1: AND */
+ if (atype == idOROP || atype == idANDOP) {
ADD_INSN(ret, line, dup);
- if (atype == 0) {
+ if (atype == idOROP) {
ADD_INSNL(ret, line, branchif, lcfin);
}
- else {
+ else { /* idANDOP */
ADD_INSNL(ret, line, branchunless, lcfin);
}
ADD_INSN(ret, line, pop);
- COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value);
+ CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value));
ADD_INSN(ret, line, swap);
ADD_INSN1(ret, line, topn, INT2FIX(1));
ADD_SEND_WITH_FLAG(ret, line, aid, INT2FIX(1), INT2FIX(asgnflag));
@@ -4931,21 +6327,24 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
if (lskip) {
ADD_LABEL(ret, lskip);
}
- if (poped) {
+ if (popped) {
/* we can apply more optimize */
ADD_INSN(ret, line, pop);
}
}
else {
- COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value);
+ CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value));
ADD_SEND(ret, line, atype, INT2FIX(1));
- if (!poped) {
+ if (!popped) {
ADD_INSN(ret, line, swap);
ADD_INSN1(ret, line, topn, INT2FIX(1));
}
ADD_SEND_WITH_FLAG(ret, line, aid, INT2FIX(1), INT2FIX(asgnflag));
+ if (lskip && popped) {
+ ADD_LABEL(ret, lskip);
+ }
ADD_INSN(ret, line, pop);
- if (lskip) {
+ if (lskip && !popped) {
ADD_LABEL(ret, lskip);
}
}
@@ -4961,17 +6360,16 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_INSN1(ret, line, putobject, rb_cObject);
break;
case NODE_COLON2:
- COMPILE(ret, "NODE_OP_CDECL/colon2#nd_head", node->nd_head->nd_head);
+ 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)));
- debug_node_end();
- return COMPILE_NG;
+ goto ng;
}
mid = node->nd_head->nd_mid;
/* cref */
- if (node->nd_aid == 0) {
+ if (node->nd_aid == idOROP) {
lassign = NEW_LABEL(line);
ADD_INSN(ret, line, dup); /* cref cref */
ADD_INSN3(ret, line, defined, INT2FIX(DEFINED_CONST),
@@ -4981,19 +6379,19 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_INSN(ret, line, dup); /* cref cref */
ADD_INSN1(ret, line, getconstant, ID2SYM(mid)); /* cref obj */
- if (node->nd_aid == 0 || node->nd_aid == 1) {
+ if (node->nd_aid == idOROP || node->nd_aid == idANDOP) {
lfin = NEW_LABEL(line);
- if (!poped) ADD_INSN(ret, line, dup); /* cref [obj] obj */
- if (node->nd_aid == 0)
+ if (!popped) ADD_INSN(ret, line, dup); /* cref [obj] obj */
+ if (node->nd_aid == idOROP)
ADD_INSNL(ret, line, branchif, lfin);
- else
+ else /* idANDOP */
ADD_INSNL(ret, line, branchunless, lfin);
/* cref [obj] */
- if (!poped) ADD_INSN(ret, line, pop); /* cref */
+ if (!popped) ADD_INSN(ret, line, pop); /* cref */
if (lassign) ADD_LABEL(ret, lassign);
- COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value);
+ CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value));
/* cref value */
- if (poped)
+ if (popped)
ADD_INSN1(ret, line, topn, INT2FIX(1)); /* cref value cref */
else {
ADD_INSN1(ret, line, dupn, INT2FIX(2)); /* cref value cref value */
@@ -5001,16 +6399,16 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
}
ADD_INSN1(ret, line, setconstant, ID2SYM(mid)); /* cref [value] */
ADD_LABEL(ret, lfin); /* cref [value] */
- if (!poped) ADD_INSN(ret, line, swap); /* [value] cref */
+ if (!popped) ADD_INSN(ret, line, swap); /* [value] cref */
ADD_INSN(ret, line, pop); /* [value] */
}
else {
- COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value);
+ CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value));
/* cref obj value */
ADD_CALL(ret, line, node->nd_aid, INT2FIX(1));
/* cref value */
ADD_INSN(ret, line, swap); /* value cref */
- if (!poped) {
+ if (!popped) {
ADD_INSN1(ret, line, topn, INT2FIX(1)); /* value cref value */
ADD_INSN(ret, line, swap); /* value value cref */
}
@@ -5038,7 +6436,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
lassign = NEW_LABEL(line);
}
- COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", node->nd_head);
+ CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", node->nd_head));
ADD_INSN(ret, line, dup);
if (nd_type(node) == NODE_OP_ASGN_AND) {
@@ -5050,27 +6448,37 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_INSN(ret, line, pop);
ADD_LABEL(ret, lassign);
- 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));
ADD_LABEL(ret, lfin);
- if (poped) {
+ if (popped) {
/* we can apply more optimize */
ADD_INSN(ret, line, pop);
}
break;
}
case NODE_CALL:
+ case NODE_OPCALL:
/* optimization shortcut
* "literal".freeze -> opt_str_freeze("literal")
*/
if (node->nd_recv && nd_type(node->nd_recv) == NODE_STR &&
- node->nd_mid == idFreeze && node->nd_args == NULL &&
+ (node->nd_mid == idFreeze || node->nd_mid == idUMinus) &&
+ node->nd_args == NULL &&
ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
- VALUE str = rb_fstring(node->nd_recv->nd_lit);
- iseq_add_mark_object(iseq, str);
- ADD_INSN1(ret, line, opt_str_freeze, str);
- if (poped) {
+ VALUE str = freeze_literal(iseq, node->nd_recv->nd_lit);
+ if (node->nd_mid == idUMinus) {
+ ADD_INSN3(ret, line, opt_str_uminus, str,
+ new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE),
+ Qundef /* CALL_CACHE */);
+ }
+ else {
+ ADD_INSN3(ret, line, opt_str_freeze, str,
+ new_callinfo(iseq, idFreeze, 0, 0, NULL, FALSE),
+ Qundef /* CALL_CACHE */);
+ }
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
@@ -5082,14 +6490,14 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
nd_type(node->nd_args) == NODE_ARRAY && node->nd_args->nd_alen == 1 &&
nd_type(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);
- node->nd_args->nd_head->nd_lit = str;
- COMPILE(ret, "recv", node->nd_recv);
- ADD_INSN3(ret, line, opt_aref_with,
+ VALUE str = freeze_literal(iseq, node->nd_args->nd_head->nd_lit);
+ CHECK(COMPILE(ret, "recv", node->nd_recv));
+ ADD_INSN3(ret, line, opt_aref_with, str,
new_callinfo(iseq, idAREF, 1, 0, NULL, FALSE),
- NULL/* CALL_CACHE */, str);
- if (poped) {
+ NULL/* CALL_CACHE */);
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
@@ -5104,7 +6512,9 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
*/
DECL_ANCHOR(recv);
DECL_ANCHOR(args);
- LABEL *lskip = 0;
+ LABEL *else_label = 0;
+ LABEL *end_label = 0;
+ VALUE branches = 0;
ID mid = node->nd_mid;
VALUE argc;
unsigned int flag = 0;
@@ -5116,17 +6526,17 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
INIT_ANCHOR(args);
#if SUPPORT_JOKE
if (nd_type(node) == NODE_VCALL) {
- ID id_bitblt;
+ ID id_bitblt;
ID id_answer;
- CONST_ID(id_bitblt, "bitblt");
+ CONST_ID(id_bitblt, "bitblt");
CONST_ID(id_answer, "the_answer_to_life_the_universe_and_everything");
- if (mid == id_bitblt) {
- ADD_INSN(ret, line, bitblt);
- break;
- }
- else if (mid == id_answer) {
+ if (mid == id_bitblt) {
+ ADD_INSN(ret, line, bitblt);
+ break;
+ }
+ else if (mid == id_answer) {
ADD_INSN(ret, line, answer);
break;
}
@@ -5144,7 +6554,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
LABEL *label;
st_data_t data;
st_table *labels_table = ISEQ_COMPILE_DATA(iseq)->labels_table;
- ID label_name;
+ VALUE label_name;
if (!labels_table) {
labels_table = st_init_numtable();
@@ -5153,7 +6563,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
if (nd_type(node->nd_args->nd_head) == NODE_LIT &&
SYMBOL_P(node->nd_args->nd_head->nd_lit)) {
- label_name = SYM2ID(node->nd_args->nd_head->nd_lit);
+ label_name = node->nd_args->nd_head->nd_lit;
if (!st_lookup(labels_table, (st_data_t)label_name, &data)) {
label = NEW_LABEL(line);
label->position = line;
@@ -5165,6 +6575,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
}
else {
COMPILE_ERROR(ERROR_ARGS "invalid goto/label format");
+ goto ng;
}
@@ -5179,12 +6590,26 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
}
#endif
/* receiver */
- if (type == NODE_CALL || type == NODE_QCALL) {
- COMPILE(recv, "recv", node->nd_recv);
+ if (type == NODE_CALL || type == NODE_OPCALL || type == NODE_QCALL) {
+ int idx, level;
+
+ if (mid == idCall &&
+ nd_type(node->nd_recv) == NODE_LVAR &&
+ iseq_block_param_id_p(iseq, node->nd_recv->nd_vid, &idx, &level)) {
+ ADD_INSN2(recv, nd_line(node->nd_recv), getblockparamproxy, INT2FIX(idx + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
+ }
+ else {
+ CHECK(COMPILE(recv, "recv", node->nd_recv));
+ }
+
if (type == NODE_QCALL) {
- lskip = NEW_LABEL(line);
+ else_label = NEW_LABEL(line);
+ end_label = NEW_LABEL(line);
+
+ DECL_BRANCH_BASE(branches, nd_first_lineno(node), nd_first_column(node), nd_last_lineno(node), nd_last_column(node), "&.");
ADD_INSN(recv, line, dup);
- ADD_INSNL(recv, line, branchnil, lskip);
+ ADD_INSNL(recv, line, branchnil, else_label);
+ ADD_TRACE_BRANCH_COVERAGE(recv, nd_first_lineno(node), nd_first_column(node), nd_last_lineno(node), nd_last_column(node), "then", branches);
}
}
else if (type == NODE_FCALL || type == NODE_VCALL) {
@@ -5192,8 +6617,9 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
}
/* args */
- if (nd_type(node) != NODE_VCALL) {
+ if (type != NODE_VCALL) {
argc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
+ CHECK(!NIL_P(argc));
}
else {
argc = INT2FIX(0);
@@ -5205,7 +6631,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
debugp_param("call args argc", argc);
debugp_param("call method", ID2SYM(mid));
- switch (nd_type(node)) {
+ switch ((int)type) {
case NODE_VCALL:
flag |= VM_CALL_VCALL;
/* VCALL is funcall, so fall through */
@@ -5215,10 +6641,13 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_SEND_R(ret, line, mid, argc, parent_block, INT2FIX(flag), keywords);
- if (lskip) {
- ADD_LABEL(ret, lskip);
+ if (else_label && end_label) {
+ ADD_INSNL(ret, line, jump, end_label);
+ ADD_LABEL(ret, else_label);
+ ADD_TRACE_BRANCH_COVERAGE(ret, nd_first_lineno(node), nd_first_column(node), nd_last_lineno(node), nd_last_column(node), "else", branches);
+ ADD_LABEL(ret, end_label);
}
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
@@ -5233,51 +6662,56 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
INIT_ANCHOR(args);
ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
- if (nd_type(node) == NODE_SUPER) {
+ if (type == NODE_SUPER) {
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 = iseq->body->local_iseq;
+ const rb_iseq_t *liseq = body->local_iseq;
+ const struct rb_iseq_constant_body *const local_body = liseq->body;
+ const struct rb_iseq_param_keyword *const local_kwd = local_body->param.keyword;
int lvar_level = get_lvar_level(iseq);
- argc = liseq->body->param.lead_num;
+ argc = local_body->param.lead_num;
/* normal arguments */
- for (i = 0; i < liseq->body->param.lead_num; i++) {
- int idx = liseq->body->local_table_size - i;
+ for (i = 0; i < local_body->param.lead_num; i++) {
+ int idx = local_body->local_table_size - i;
ADD_GETLOCAL(args, line, idx, lvar_level);
}
- if (liseq->body->param.flags.has_opt) {
+ if (local_body->param.flags.has_opt) {
/* optional arguments */
int j;
- for (j = 0; j < liseq->body->param.opt_num; j++) {
- int idx = liseq->body->local_table_size - (i + j);
+ for (j = 0; j < local_body->param.opt_num; j++) {
+ int idx = local_body->local_table_size - (i + j);
ADD_GETLOCAL(args, line, idx, lvar_level);
}
i += j;
argc = i;
}
- if (liseq->body->param.flags.has_rest) {
+ if (local_body->param.flags.has_rest) {
/* rest argument */
- int idx = liseq->body->local_table_size - liseq->body->param.rest_start;
+ int idx = local_body->local_table_size - local_body->param.rest_start;
+
ADD_GETLOCAL(args, line, idx, lvar_level);
+ ADD_INSN1(args, line, splatarray, Qfalse);
- argc = liseq->body->param.rest_start + 1;
+ argc = local_body->param.rest_start + 1;
flag |= VM_CALL_ARGS_SPLAT;
}
- if (liseq->body->param.flags.has_post) {
+ if (local_body->param.flags.has_post) {
/* post arguments */
- int post_len = liseq->body->param.post_num;
- int post_start = liseq->body->param.post_start;
+ int post_len = local_body->param.post_num;
+ int post_start = local_body->param.post_start;
- if (liseq->body->param.flags.has_rest) {
+ if (local_body->param.flags.has_rest) {
int j;
for (j=0; j<post_len; j++) {
- int idx = liseq->body->local_table_size - (post_start + j);
+ int idx = local_body->local_table_size - (post_start + j);
ADD_GETLOCAL(args, line, idx, lvar_level);
}
ADD_INSN1(args, line, newarray, INT2FIX(j));
@@ -5287,46 +6721,46 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
else {
int j;
for (j=0; j<post_len; j++) {
- int idx = liseq->body->local_table_size - (post_start + j);
+ int idx = local_body->local_table_size - (post_start + j);
ADD_GETLOCAL(args, line, idx, lvar_level);
}
argc = post_len + post_start;
}
}
- if (liseq->body->param.flags.has_kw) { /* TODO: support keywords */
- int local_size = liseq->body->local_table_size;
+ if (local_body->param.flags.has_kw) { /* TODO: support keywords */
+ int local_size = local_body->local_table_size;
argc++;
ADD_INSN1(args, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
- if (liseq->body->param.flags.has_kwrest) {
- int idx = liseq->body->local_table_size - liseq->body->param.keyword->rest_start;
+ if (local_body->param.flags.has_kwrest) {
+ int idx = local_body->local_table_size - local_kwd->rest_start;
ADD_GETLOCAL(args, line, idx, lvar_level);
ADD_SEND (args, line, rb_intern("dup"), INT2FIX(0));
}
else {
ADD_INSN1(args, line, newhash, INT2FIX(0));
}
- for (i = 0; i < liseq->body->param.keyword->num; ++i) {
- ID id = liseq->body->param.keyword->table[i];
+ 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, line, putobject, ID2SYM(id));
ADD_GETLOCAL(args, line, idx, lvar_level);
}
ADD_SEND(args, line, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
- if (liseq->body->param.flags.has_rest) {
+ if (local_body->param.flags.has_rest) {
ADD_INSN1(args, line, newarray, INT2FIX(1));
ADD_INSN (args, line, concatarray);
--argc;
}
}
- else if (liseq->body->param.flags.has_kwrest) {
- int idx = liseq->body->local_table_size - liseq->body->param.keyword->rest_start;
+ else if (local_body->param.flags.has_kwrest) {
+ int idx = local_body->local_table_size - local_kwd->rest_start;
ADD_GETLOCAL(args, line, idx, lvar_level);
ADD_SEND (args, line, rb_intern("dup"), INT2FIX(0));
- if (liseq->body->param.flags.has_rest) {
+ if (local_body->param.flags.has_rest) {
ADD_INSN1(args, line, newarray, INT2FIX(1));
ADD_INSN (args, line, concatarray);
}
@@ -5336,104 +6770,66 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
}
}
- /* dummy receiver */
- ADD_INSN1(ret, line, putobject, nd_type(node) == NODE_ZSUPER ? Qfalse : Qtrue);
+ ADD_INSN(ret, line, putself);
ADD_SEQ(ret, args);
ADD_INSN3(ret, line, invokesuper,
- new_callinfo(iseq, 0, argc, flag | VM_CALL_SUPER | VM_CALL_FCALL, keywords, parent_block != NULL),
+ new_callinfo(iseq, 0, argc, flag | VM_CALL_SUPER | (type == NODE_ZSUPER ? VM_CALL_ZSUPER : 0) | VM_CALL_FCALL, keywords, parent_block != NULL),
Qnil, /* CALL_CACHE */
parent_block);
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
}
case NODE_ARRAY:{
- compile_array_(iseq, ret, node, COMPILE_ARRAY_TYPE_ARRAY, NULL, poped);
+ CHECK(compile_array(iseq, ret, node, COMPILE_ARRAY_TYPE_ARRAY, NULL, NULL, popped) >= 0);
break;
}
case NODE_ZARRAY:{
- if (!poped) {
+ if (!popped) {
ADD_INSN1(ret, line, newarray, INT2FIX(0));
}
break;
}
case NODE_VALUES:{
- NODE *n = node;
+ const NODE *n = node;
+ if (popped) {
+ COMPILE_ERROR(ERROR_ARGS "NODE_VALUES: must not be popped");
+ }
while (n) {
- COMPILE(ret, "values item", n->nd_head);
+ CHECK(COMPILE(ret, "values item", n->nd_head));
n = n->nd_next;
}
ADD_INSN1(ret, line, newarray, INT2FIX(node->nd_alen));
- if (poped) {
- ADD_INSN(ret, line, pop);
- }
break;
}
case NODE_HASH:{
DECL_ANCHOR(list);
- int type = node->nd_head ? nd_type(node->nd_head) : NODE_ZARRAY;
+ enum node_type type = node->nd_head ? nd_type(node->nd_head) : NODE_ZARRAY;
INIT_ANCHOR(list);
switch (type) {
case NODE_ARRAY:
- compile_array(iseq, list, node->nd_head, COMPILE_ARRAY_TYPE_HASH);
+ CHECK(compile_array(iseq, list, node->nd_head, COMPILE_ARRAY_TYPE_HASH, NULL, NULL, popped) >= 0);
ADD_SEQ(ret, list);
break;
case NODE_ZARRAY:
+ if (popped) break;
ADD_INSN1(ret, line, newhash, INT2FIX(0));
break;
default:
- compile_bug(ERROR_ARGS_AT(node->nd_head) "can't make hash with this node: %s",
- ruby_node_name(type));
- }
-
- if (poped) {
- ADD_INSN(ret, line, pop);
+ COMPILE_ERROR(ERROR_ARGS_AT(node->nd_head) "can't make hash with this node: %s",
+ ruby_node_name(type));
+ goto ng;
}
break;
}
- case NODE_RETURN:{
- rb_iseq_t *is = iseq;
-
- if (is) {
- if (is->body->type == ISEQ_TYPE_TOP) {
- COMPILE_ERROR(ERROR_ARGS "Invalid return");
- }
- else {
- LABEL *splabel = 0;
-
- if (is->body->type == ISEQ_TYPE_METHOD) {
- splabel = NEW_LABEL(0);
- ADD_LABEL(ret, splabel);
- ADD_ADJUST(ret, line, 0);
- }
-
- COMPILE(ret, "return nd_stts (return val)", node->nd_stts);
-
- if (is->body->type == ISEQ_TYPE_METHOD) {
- add_ensure_iseq(ret, iseq, 1);
- ADD_TRACE(ret, line, RUBY_EVENT_RETURN);
- ADD_INSN(ret, line, leave);
- ADD_ADJUST_RESTORE(ret, splabel);
-
- if (!poped) {
- ADD_INSN(ret, line, putnil);
- }
- }
- else {
- ADD_INSN1(ret, line, throw, INT2FIX(TAG_RETURN));
- if (poped) {
- ADD_INSN(ret, line, pop);
- }
- }
- }
- }
+ case NODE_RETURN:
+ CHECK(compile_return(iseq, ret, node, popped));
break;
- }
case NODE_YIELD:{
DECL_ANCHOR(args);
VALUE argc;
@@ -5441,14 +6837,15 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
struct rb_call_info_kw_arg *keywords = NULL;
INIT_ANCHOR(args);
- if (iseq->body->type == ISEQ_TYPE_TOP) {
+ if (body->type == ISEQ_TYPE_TOP ||
+ body->type == ISEQ_TYPE_MAIN) {
COMPILE_ERROR(ERROR_ARGS "Invalid yield");
- debug_node_end();
- return COMPILE_NG;
+ goto ng;
}
if (node->nd_head) {
argc = setup_args(iseq, args, node->nd_head, &flag, &keywords);
+ CHECK(!NIL_P(argc));
}
else {
argc = INT2FIX(0);
@@ -5457,17 +6854,17 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_SEQ(ret, args);
ADD_INSN1(ret, line, invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), flag, keywords, FALSE));
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
}
case NODE_LVAR:{
- if (!poped) {
+ if (!popped) {
ID id = node->nd_vid;
- int idx = iseq->body->local_iseq->body->local_table_size - get_local_var_idx(iseq, id);
+ int idx = body->local_iseq->body->local_table_size - get_local_var_idx(iseq, id);
- debugs("id: %"PRIsVALUE" idx: %d\n", rb_id2str(id), idx);
+ debugs("id: %s idx: %d\n", rb_id2name(id), idx);
ADD_GETLOCAL(ret, line, idx, get_lvar_level(iseq));
}
break;
@@ -5475,11 +6872,12 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
case NODE_DVAR:{
int lv, idx, ls;
debugi("nd_vid", node->nd_vid);
- if (!poped) {
+ if (!popped) {
idx = get_dyna_var_idx(iseq, node->nd_vid, &lv, &ls);
if (idx < 0) {
- compile_bug(ERROR_ARGS "unknown dvar (%"PRIsVALUE")",
- rb_id2str(node->nd_vid));
+ COMPILE_ERROR(ERROR_ARGS "unknown dvar (%"PRIsVALUE")",
+ rb_id2str(node->nd_vid));
+ goto ng;
}
ADD_GETLOCAL(ret, line, ls - idx, lv);
}
@@ -5488,14 +6886,14 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
case NODE_GVAR:{
ADD_INSN1(ret, line, getglobal,
((VALUE)node->nd_entry | 1));
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
}
case NODE_IVAR:{
debugi("nd_vid", node->nd_vid);
- if (!poped) {
+ if (!popped) {
ADD_INSN2(ret, line, getinstancevariable,
ID2SYM(node->nd_vid),
get_ivar_ic_value(iseq,node->nd_vid));
@@ -5507,11 +6905,11 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
LABEL *lend = NEW_LABEL(line);
- int ic_index = iseq->body->is_size++;
+ int ic_index = body->is_size++;
- ADD_INSN2(ret, line, getinlinecache, lend, INT2FIX(ic_index));
+ ADD_INSN2(ret, line, opt_getinlinecache, lend, INT2FIX(ic_index));
ADD_INSN1(ret, line, getconstant, ID2SYM(node->nd_vid));
- ADD_INSN1(ret, line, setinlinecache, INT2FIX(ic_index));
+ ADD_INSN1(ret, line, opt_setinlinecache, INT2FIX(ic_index));
ADD_LABEL(ret, lend);
}
else {
@@ -5519,20 +6917,20 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_INSN1(ret, line, getconstant, ID2SYM(node->nd_vid));
}
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
}
case NODE_CVAR:{
- if (!poped) {
+ if (!popped) {
ADD_INSN1(ret, line, getclassvariable,
ID2SYM(node->nd_vid));
}
break;
}
case NODE_NTH_REF:{
- if (!poped) {
+ if (!popped) {
if (!node->nd_nth) {
ADD_INSN(ret, line, putnil);
break;
@@ -5543,7 +6941,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
break;
}
case NODE_BACK_REF:{
- if (!poped) {
+ if (!popped) {
ADD_INSN2(ret, line, getspecial, INT2FIX(1) /* '~' */,
INT2FIX(0x01 | (node->nd_nth << 1)));
}
@@ -5564,12 +6962,12 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
INT2FIX(0));
break;
case NODE_MATCH2:
- COMPILE(recv, "receiver", node->nd_recv);
- COMPILE(val, "value", node->nd_value);
+ CHECK(COMPILE(recv, "receiver", node->nd_recv));
+ CHECK(COMPILE(val, "value", node->nd_value));
break;
case NODE_MATCH3:
- COMPILE(recv, "receiver", node->nd_value);
- COMPILE(val, "value", node->nd_recv);
+ CHECK(COMPILE(recv, "receiver", node->nd_value));
+ CHECK(COMPILE(val, "value", node->nd_recv));
break;
}
@@ -5598,36 +6996,38 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
compile_named_capture_assign(iseq, ret, node->nd_args);
}
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
}
case NODE_LIT:{
debugp_param("lit", node->nd_lit);
- if (!poped) {
+ if (!popped) {
ADD_INSN1(ret, line, putobject, node->nd_lit);
}
break;
}
case NODE_STR:{
debugp_param("nd_lit", node->nd_lit);
- if (!poped) {
- node->nd_lit = rb_fstring(node->nd_lit);
+ if (!popped) {
+ VALUE lit = node->nd_lit;
if (!ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
- ADD_INSN1(ret, line, putstring, node->nd_lit);
+ lit = freeze_literal(iseq, lit);
+ ADD_INSN1(ret, line, putstring, lit);
}
else {
if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
- VALUE debug_info = rb_ary_new_from_args(2, iseq->body->location.path, INT2FIX(line));
- VALUE str = rb_str_dup(node->nd_lit);
- rb_ivar_set(str, id_debug_created_info, rb_obj_freeze(debug_info));
- ADD_INSN1(ret, line, putobject, rb_obj_freeze(str));
- iseq_add_mark_object_compile_time(iseq, str);
+ 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 {
- ADD_INSN1(ret, line, putobject, node->nd_lit);
+ lit = rb_fstring(lit);
}
+ ADD_INSN1(ret, line, putobject, lit);
+ iseq_add_mark_object_compile_time(iseq, lit);
}
}
break;
@@ -5635,14 +7035,14 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
case NODE_DSTR:{
compile_dstr(iseq, ret, node);
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
else {
if (ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
VALUE debug_info = Qnil;
if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
- debug_info = rb_ary_new_from_args(2, iseq->body->location.path, INT2FIX(line));
+ debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX(line));
iseq_add_mark_object_compile_time(iseq, rb_obj_freeze(debug_info));
}
ADD_INSN1(ret, line, freezestring, debug_info);
@@ -5651,12 +7051,11 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
break;
}
case NODE_XSTR:{
- node->nd_lit = rb_fstring(node->nd_lit);
ADD_CALL_RECEIVER(ret, line);
- ADD_INSN1(ret, line, putobject, node->nd_lit);
+ ADD_INSN1(ret, line, putobject, freeze_literal(iseq, node->nd_lit));
ADD_CALL(ret, line, idBackquote, INT2FIX(1));
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
@@ -5666,79 +7065,70 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
compile_dstr(iseq, ret, node);
ADD_CALL(ret, line, idBackquote, INT2FIX(1));
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
}
- case NODE_EVSTR:{
- COMPILE(ret, "nd_body", node->nd_body);
-
- if (poped) {
- ADD_INSN(ret, line, pop);
- }
- else {
- ADD_INSN(ret, line, tostring);
- }
+ case NODE_EVSTR:
+ CHECK(compile_evstr(iseq, ret, node->nd_body, popped));
break;
- }
case NODE_DREGX:{
compile_dregx(iseq, ret, node);
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
}
- case NODE_DREGX_ONCE:{
- int ic_index = iseq->body->is_size++;
- NODE *dregx_node = NEW_NODE(NODE_DREGX, node->u1.value, node->u2.value, node->u3.value);
- NODE *block_node = NEW_NODE(NODE_SCOPE, 0, dregx_node, 0);
- const rb_iseq_t * block_iseq = NEW_CHILD_ISEQ(block_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line);
+ case NODE_ONCE:{
+ int ic_index = body->is_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, line, once, block_iseq, INT2FIX(ic_index));
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
}
case NODE_ARGSCAT:{
- if (poped) {
- COMPILE(ret, "argscat head", node->nd_head);
+ if (popped) {
+ CHECK(COMPILE(ret, "argscat head", node->nd_head));
ADD_INSN1(ret, line, splatarray, Qfalse);
ADD_INSN(ret, line, pop);
- COMPILE(ret, "argscat body", node->nd_body);
+ CHECK(COMPILE(ret, "argscat body", node->nd_body));
ADD_INSN1(ret, line, splatarray, Qfalse);
ADD_INSN(ret, line, pop);
}
else {
- COMPILE(ret, "argscat head", node->nd_head);
- COMPILE(ret, "argscat body", node->nd_body);
+ CHECK(COMPILE(ret, "argscat head", node->nd_head));
+ CHECK(COMPILE(ret, "argscat body", node->nd_body));
ADD_INSN(ret, line, concatarray);
}
break;
}
case NODE_ARGSPUSH:{
- if (poped) {
- COMPILE(ret, "arsgpush head", node->nd_head);
+ if (popped) {
+ CHECK(COMPILE(ret, "arsgpush head", node->nd_head));
ADD_INSN1(ret, line, splatarray, Qfalse);
ADD_INSN(ret, line, pop);
- COMPILE_(ret, "argspush body", node->nd_body, poped);
+ CHECK(COMPILE_(ret, "argspush body", node->nd_body, popped));
}
else {
- COMPILE(ret, "arsgpush head", node->nd_head);
- COMPILE_(ret, "argspush body", node->nd_body, poped);
+ CHECK(COMPILE(ret, "arsgpush head", node->nd_head));
+ CHECK(COMPILE_(ret, "argspush body", node->nd_body, popped));
ADD_INSN1(ret, line, newarray, INT2FIX(1));
ADD_INSN(ret, line, concatarray);
}
break;
}
case NODE_SPLAT:{
- COMPILE(ret, "splat", node->nd_head);
+ CHECK(COMPILE(ret, "splat", node->nd_head));
ADD_INSN1(ret, line, splatarray, Qtrue);
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
@@ -5755,7 +7145,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_INSN1(ret, line, putiseq, method_iseq);
ADD_SEND (ret, line, id_core_define_method, INT2FIX(2));
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
@@ -5769,12 +7159,12 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
debugp_param("defs/iseq", rb_iseqw_new(singleton_method));
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
- COMPILE(ret, "defs: recv", node->nd_recv);
+ CHECK(COMPILE(ret, "defs: recv", node->nd_recv));
ADD_INSN1(ret, line, putobject, ID2SYM(node->nd_mid));
ADD_INSN1(ret, line, putiseq, singleton_method);
ADD_SEND (ret, line, id_core_define_singleton_method, INT2FIX(3));
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
@@ -5782,22 +7172,22 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
case NODE_ALIAS:{
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
- COMPILE(ret, "alias arg1", node->u1.node);
- COMPILE(ret, "alias arg2", node->u2.node);
+ CHECK(COMPILE(ret, "alias arg1", node->nd_1st));
+ CHECK(COMPILE(ret, "alias arg2", node->nd_2nd));
ADD_SEND(ret, line, id_core_set_method_alias, INT2FIX(3));
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
}
case NODE_VALIAS:{
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
- ADD_INSN1(ret, line, putobject, ID2SYM(node->u1.id));
- ADD_INSN1(ret, line, putobject, ID2SYM(node->u2.id));
+ ADD_INSN1(ret, line, putobject, ID2SYM(node->nd_alias));
+ ADD_INSN1(ret, line, putobject, ID2SYM(node->nd_orig));
ADD_SEND(ret, line, id_core_set_variable_alias, INT2FIX(2));
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
@@ -5805,10 +7195,10 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
case NODE_UNDEF:{
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
- COMPILE(ret, "undef arg", node->u2.node);
+ CHECK(COMPILE(ret, "undef arg", node->nd_undef));
ADD_SEND(ret, line, id_core_undef_method, INT2FIX(2));
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
@@ -5817,15 +7207,14 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(node->nd_body,
rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(node->nd_cpath->nd_mid)),
ISEQ_TYPE_CLASS, line);
- VALUE noscope = compile_cpath(ret, iseq, node->nd_cpath);
- int flags = VM_DEFINECLASS_TYPE_CLASS;
+ const int flags = VM_DEFINECLASS_TYPE_CLASS |
+ (node->nd_super ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
+ compile_cpath(ret, iseq, node->nd_cpath);
- if (!noscope) flags |= VM_DEFINECLASS_FLAG_SCOPED;
- if (node->nd_super) flags |= VM_DEFINECLASS_FLAG_HAS_SUPERCLASS;
- COMPILE(ret, "super", node->nd_super);
+ CHECK(COMPILE(ret, "super", node->nd_super));
ADD_INSN3(ret, line, defineclass, ID2SYM(node->nd_cpath->nd_mid), class_iseq, INT2FIX(flags));
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
@@ -5834,31 +7223,30 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(node->nd_body,
rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(node->nd_cpath->nd_mid)),
ISEQ_TYPE_CLASS, line);
- VALUE noscope = compile_cpath(ret, iseq, node->nd_cpath);
- int flags = VM_DEFINECLASS_TYPE_MODULE;
+ const int flags = VM_DEFINECLASS_TYPE_MODULE |
+ compile_cpath(ret, iseq, node->nd_cpath);
- if (!noscope) flags |= VM_DEFINECLASS_FLAG_SCOPED;
ADD_INSN (ret, line, putnil); /* dummy */
ADD_INSN3(ret, line, defineclass, ID2SYM(node->nd_cpath->nd_mid), module_iseq, INT2FIX(flags));
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
}
case NODE_SCLASS:{
ID singletonclass;
- const rb_iseq_t *singleton_class = NEW_ISEQ(node->nd_body, rb_str_new2("singleton class"),
+ const rb_iseq_t *singleton_class = NEW_ISEQ(node->nd_body, rb_fstring_lit("singleton class"),
ISEQ_TYPE_CLASS, line);
- COMPILE(ret, "sclass#recv", node->nd_recv);
+ CHECK(COMPILE(ret, "sclass#recv", node->nd_recv));
ADD_INSN (ret, line, putnil);
CONST_ID(singletonclass, "singletonclass");
ADD_INSN3(ret, line, defineclass,
ID2SYM(singletonclass), singleton_class,
INT2FIX(VM_DEFINECLASS_TYPE_SINGLETON_CLASS));
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
@@ -5867,17 +7255,17 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
if (rb_is_const_id(node->nd_mid)) {
/* constant */
LABEL *lend = NEW_LABEL(line);
- int ic_index = iseq->body->is_size++;
+ int ic_index = body->is_size++;
DECL_ANCHOR(pref);
DECL_ANCHOR(body);
INIT_ANCHOR(pref);
INIT_ANCHOR(body);
- compile_colon2(iseq, node, pref, body);
- if (LIST_SIZE_ZERO(pref)) {
+ 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, line, getinlinecache, lend, INT2FIX(ic_index));
+ ADD_INSN2(ret, line, opt_getinlinecache, lend, INT2FIX(ic_index));
}
else {
ADD_INSN(ret, line, putnil);
@@ -5886,7 +7274,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_SEQ(ret, body);
if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
- ADD_INSN1(ret, line, setinlinecache, INT2FIX(ic_index));
+ ADD_INSN1(ret, line, opt_setinlinecache, INT2FIX(ic_index));
ADD_LABEL(ret, lend);
}
}
@@ -5898,23 +7286,23 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
else {
/* function call */
ADD_CALL_RECEIVER(ret, line);
- COMPILE(ret, "colon2#nd_head", node->nd_head);
+ CHECK(COMPILE(ret, "colon2#nd_head", node->nd_head));
ADD_CALL(ret, line, node->nd_mid, INT2FIX(1));
}
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
}
case NODE_COLON3:{
LABEL *lend = NEW_LABEL(line);
- int ic_index = iseq->body->is_size++;
+ int ic_index = body->is_size++;
debugi("colon3#nd_mid", node->nd_mid);
/* add cache insn */
if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
- ADD_INSN2(ret, line, getinlinecache, lend, INT2FIX(ic_index));
+ ADD_INSN2(ret, line, opt_getinlinecache, lend, INT2FIX(ic_index));
ADD_INSN(ret, line, pop);
}
@@ -5922,11 +7310,11 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_INSN1(ret, line, getconstant, ID2SYM(node->nd_mid));
if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
- ADD_INSN1(ret, line, setinlinecache, INT2FIX(ic_index));
+ ADD_INSN1(ret, line, opt_setinlinecache, INT2FIX(ic_index));
ADD_LABEL(ret, lend);
}
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
@@ -5935,22 +7323,21 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
case NODE_DOT3:{
int excl = type == NODE_DOT3;
VALUE flag = INT2FIX(excl);
- NODE *b = node->nd_beg;
- NODE *e = node->nd_end;
+ const NODE *b = node->nd_beg;
+ const NODE *e = node->nd_end;
if (number_literal_p(b) && number_literal_p(e)) {
- VALUE val = rb_range_new(b->nd_lit, e->nd_lit, excl);
- iseq_add_mark_object_compile_time(iseq, val);
- ADD_INSN1(ret, line, putobject, val);
- break;
- }
- COMPILE(ret, "min", (NODE *) node->nd_beg);
- COMPILE(ret, "max", (NODE *) node->nd_end);
- if (poped) {
- ADD_INSN(ret, line, pop);
- ADD_INSN(ret, line, pop);
+ if (!popped) {
+ VALUE val = rb_range_new(b->nd_lit, e->nd_lit, excl);
+ iseq_add_mark_object_compile_time(iseq, val);
+ ADD_INSN1(ret, line, putobject, val);
+ }
}
else {
- ADD_INSN1(ret, line, newrange, flag);
+ CHECK(COMPILE_(ret, "min", b, popped));
+ CHECK(COMPILE_(ret, "max", e, popped));
+ if (!popped) {
+ ADD_INSN1(ret, line, newrange, flag);
+ }
}
break;
}
@@ -5959,8 +7346,8 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
LABEL *lend = NEW_LABEL(line);
LABEL *ltrue = NEW_LABEL(line);
LABEL *lfalse = NEW_LABEL(line);
- compile_branch_condition(iseq, ret, node, ltrue, lfalse);
- ADD_INSNL(ret, line, jump, lend);
+ CHECK(compile_flip_flop(iseq, ret, node, type == NODE_FLIP2,
+ ltrue, lfalse));
ADD_LABEL(ret, ltrue);
ADD_INSN1(ret, line, putobject, Qtrue);
ADD_INSNL(ret, line, jump, lend);
@@ -5970,32 +7357,32 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
break;
}
case NODE_SELF:{
- if (!poped) {
+ if (!popped) {
ADD_INSN(ret, line, putself);
}
break;
}
case NODE_NIL:{
- if (!poped) {
+ if (!popped) {
ADD_INSN(ret, line, putnil);
}
break;
}
case NODE_TRUE:{
- if (!poped) {
+ if (!popped) {
ADD_INSN1(ret, line, putobject, Qtrue);
}
break;
}
case NODE_FALSE:{
- if (!poped) {
+ if (!popped) {
ADD_INSN1(ret, line, putobject, Qfalse);
}
break;
}
case NODE_ERRINFO:{
- if (!poped) {
- if (iseq->body->type == ISEQ_TYPE_RESCUE) {
+ if (!popped) {
+ if (body->type == ISEQ_TYPE_RESCUE) {
ADD_GETLOCAL(ret, line, LVAR_ERRINFO, 0);
}
else {
@@ -6018,38 +7405,23 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
}
break;
}
- case NODE_DEFINED:{
- if (poped) break;
- if (!node->nd_head) {
- VALUE str = rb_iseq_defined_string(DEFINED_NIL);
- ADD_INSN1(ret, nd_line(node), putobject, str);
- }
- else {
- LABEL *lfinish[2];
- lfinish[0] = NEW_LABEL(line);
- lfinish[1] = 0;
- ADD_INSN(ret, line, putnil);
- defined_expr(iseq, ret, node->nd_head, lfinish, Qtrue);
- ADD_INSN(ret, line, swap);
- ADD_INSN(ret, line, pop);
- if (lfinish[1]) {
- ADD_LABEL(ret, lfinish[1]);
- }
- ADD_LABEL(ret, lfinish[0]);
+ case NODE_DEFINED:
+ 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 = iseq->body->is_size++;
- const rb_iseq_t *once_iseq = NEW_CHILD_ISEQ((NODE *)IFUNC_NEW(build_postexe_iseq, node->nd_body, 0),
- make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line);
+ int is_index = body->is_size++;
+ const rb_iseq_t *once_iseq =
+ new_child_iseq_ifunc(iseq, IFUNC_NEW(build_postexe_iseq, node->nd_body, 0),
+ rb_fstring(make_name_for_block(iseq)), iseq, ISEQ_TYPE_BLOCK, line);
ADD_INSN2(ret, line, once, once_iseq, INT2FIX(is_index));
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
@@ -6057,29 +7429,31 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
case NODE_KW_ARG:
{
LABEL *end_label = NEW_LABEL(nd_line(node));
- NODE *default_value = node->nd_body->nd_value;
+ const NODE *default_value = node->nd_body->nd_value;
- if (default_value == (NODE *)-1) {
+ if (default_value == (const NODE *)-1) {
/* required argument. do nothing */
- compile_bug(ERROR_ARGS "unreachable");
+ COMPILE_ERROR(ERROR_ARGS "unreachable");
+ goto ng;
}
else if (nd_type(default_value) == NODE_LIT ||
nd_type(default_value) == NODE_NIL ||
nd_type(default_value) == NODE_TRUE ||
nd_type(default_value) == NODE_FALSE) {
- compile_bug(ERROR_ARGS "unreachable");
+ COMPILE_ERROR(ERROR_ARGS "unreachable");
+ goto ng;
}
else {
/* if keywordcheck(_kw_bits, nth_keyword)
* kw = default_value
* end
*/
- int kw_bits_idx = iseq->body->local_table_size - iseq->body->param.keyword->bits_start;
- int keyword_idx = iseq->body->param.keyword->num;
+ int kw_bits_idx = body->local_table_size - body->param.keyword->bits_start;
+ int keyword_idx = body->param.keyword->num;
ADD_INSN2(ret, line, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(keyword_idx));
ADD_INSNL(ret, line, branchif, end_label);
- COMPILE_POPED(ret, "keyword default argument", node->nd_body);
+ CHECK(COMPILE_POPPED(ret, "keyword default argument", node->nd_body));
ADD_LABEL(ret, end_label);
}
@@ -6087,8 +7461,8 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
}
case NODE_DSYM:{
compile_dstr(iseq, ret, node);
- if (!poped) {
- ADD_SEND(ret, line, idIntern, INT2FIX(0));
+ if (!popped) {
+ ADD_INSN(ret, line, intern);
}
else {
ADD_INSN(ret, line, pop);
@@ -6100,8 +7474,10 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
DECL_ANCHOR(args);
unsigned int flag = 0;
ID mid = node->nd_mid;
- LABEL *lskip = 0;
+ LABEL *else_label = 0;
+ LABEL *end_label = 0;
VALUE argc;
+ VALUE branches = 0;
/* optimization shortcut
* obj["literal"] = value -> opt_aset_with(obj, "literal", value)
@@ -6110,20 +7486,19 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
nd_type(node->nd_args) == NODE_ARRAY && node->nd_args->nd_alen == 2 &&
nd_type(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);
- node->nd_args->nd_head->nd_lit = str;
- iseq_add_mark_object(iseq, str);
- COMPILE(ret, "recv", node->nd_recv);
- COMPILE(ret, "value", node->nd_args->nd_next->nd_head);
- if (!poped) {
+ VALUE str = freeze_literal(iseq, 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, line, swap);
ADD_INSN1(ret, line, topn, INT2FIX(1));
}
- ADD_INSN3(ret, line, opt_aset_with,
+ ADD_INSN3(ret, line, opt_aset_with, str,
new_callinfo(iseq, idASET, 2, 0, NULL, FALSE),
- NULL/* CALL_CACHE */, str);
+ NULL/* CALL_CACHE */);
ADD_INSN(ret, line, pop);
break;
}
@@ -6131,6 +7506,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
INIT_ANCHOR(recv);
INIT_ANCHOR(args);
argc = setup_args(iseq, args, node->nd_args, &flag, NULL);
+ CHECK(!NIL_P(argc));
flag |= COMPILE_RECV(recv, "recv", node);
@@ -6141,10 +7517,13 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
/* safe nav attr */
mid = rb_id_attrset(mid);
ADD_INSN(recv, line, dup);
- lskip = NEW_LABEL(line);
- ADD_INSNL(recv, line, branchnil, lskip);
+ else_label = NEW_LABEL(line);
+ end_label = NEW_LABEL(line);
+ DECL_BRANCH_BASE(branches, nd_first_lineno(node), nd_first_column(node), nd_last_lineno(node), nd_last_column(node), "&.");
+ ADD_INSNL(recv, line, branchnil, else_label);
+ ADD_TRACE_BRANCH_COVERAGE(recv, nd_first_lineno(node), nd_first_column(node), nd_last_lineno(node), nd_last_column(node), "then", branches);
}
- if (!poped) {
+ if (!popped) {
ADD_INSN(ret, line, putnil);
ADD_SEQ(ret, recv);
ADD_SEQ(ret, args);
@@ -6174,23 +7553,16 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_SEQ(ret, args);
}
ADD_SEND_WITH_FLAG(ret, line, mid, argc, INT2FIX(flag));
- if (lskip) ADD_LABEL(ret, lskip);
+ if (else_label && end_label) {
+ ADD_INSNL(ret, line, jump, end_label);
+ ADD_LABEL(ret, else_label);
+ ADD_TRACE_BRANCH_COVERAGE(ret, nd_first_lineno(node), nd_first_column(node), nd_last_lineno(node), nd_last_column(node), "else", branches);
+ ADD_LABEL(ret, end_label);
+ }
ADD_INSN(ret, line, pop);
break;
}
- case NODE_PRELUDE:{
- const rb_compile_option_t *orig_opt = ISEQ_COMPILE_DATA(iseq)->option;
- if (node->nd_orig) {
- rb_compile_option_t new_opt = *orig_opt;
- rb_iseq_make_compile_option(&new_opt, node->nd_orig);
- ISEQ_COMPILE_DATA(iseq)->option = &new_opt;
- }
- COMPILE_POPED(ret, "prelude", node->nd_head);
- COMPILE_(ret, "body", node->nd_body, poped);
- ISEQ_COMPILE_DATA(iseq)->option = orig_opt;
- 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);
@@ -6199,23 +7571,18 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
ADD_CALL_WITH_BLOCK(ret, line, idLambda, argc, block);
- if (poped) {
+ if (popped) {
ADD_INSN(ret, line, pop);
}
break;
}
default:
- UNKNOWN_NODE("iseq_compile_each", node);
+ UNKNOWN_NODE("iseq_compile_each", node, COMPILE_NG);
+ ng:
+ debug_node_end();
return COMPILE_NG;
}
- /* check & remove redundant trace(line) */
- if (saved_last_element && ret /* ret can be 0 when error */ &&
- ret->last == saved_last_element &&
- ((INSN *)saved_last_element)->insn_id == BIN(trace)) {
- POP_ELEMENT(ret);
- }
-
debug_node_end();
return COMPILE_OK;
}
@@ -6271,7 +7638,7 @@ insn_data_to_s_detail(INSN *iobj)
case TS_OFFSET: /* label(destination position) */
{
LABEL *lobj = (LABEL *)OPERAND_AT(iobj, j);
- rb_str_catf(str, "<L%03d>", lobj->label_no);
+ rb_str_catf(str, LABEL_FORMAT, lobj->label_no);
break;
}
break;
@@ -6304,6 +7671,7 @@ insn_data_to_s_detail(INSN *iobj)
break;
}
case TS_IC: /* inline cache */
+ case TS_ISE: /* inline storage entry */
rb_str_catf(str, "<ic:%d>", FIX2INT(OPERAND_AT(iobj, j)));
break;
case TS_CALLINFO: /* call info */
@@ -6324,7 +7692,7 @@ insn_data_to_s_detail(INSN *iobj)
break;
case TS_FUNCPTR:
{
- rb_insn_func_t func = (rb_insn_func_t)OPERAND_AT(iobj, j);
+ const void *func = (const void *)OPERAND_AT(iobj, j);
#ifdef HAVE_DLADDR
Dl_info info;
if (dladdr(func, &info) && info.dli_sname) {
@@ -6348,7 +7716,13 @@ insn_data_to_s_detail(INSN *iobj)
}
static void
-dump_disasm_list(struct iseq_link_element *link)
+dump_disasm_list(const LINK_ELEMENT *link)
+{
+ dump_disasm_list_with_cursor(link, NULL, NULL);
+}
+
+static void
+dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, const LABEL *dest)
{
int pos = 0;
INSN *iobj;
@@ -6358,24 +7732,27 @@ dump_disasm_list(struct iseq_link_element *link)
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->line_no);
+ 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("<L%03d>\n", lobj->label_no);
+ printf(LABEL_FORMAT" [sp: %d]%s\n", lobj->label_no, lobj->sp,
+ dest == lobj ? " <---" : "");
break;
}
- case ISEQ_ELEMENT_NONE:
+ case ISEQ_ELEMENT_TRACE:
{
- printf("[none]\n");
+ TRACE *trace = (TRACE *)link;
+ printf("trace: %0x\n", trace->event);
break;
}
case ISEQ_ELEMENT_ADJUST:
@@ -6397,16 +7774,16 @@ dump_disasm_list(struct iseq_link_element *link)
const char *
rb_insns_name(int i)
{
- return insn_name_info[i];
+ return insn_name(i);
}
VALUE
rb_insns_name_array(void)
{
- VALUE ary = rb_ary_new();
+ VALUE ary = rb_ary_new_capa(VM_INSTRUCTION_SIZE);
int i;
- for (i = 0; i < numberof(insn_name_info); i++) {
- rb_ary_push(ary, rb_fstring(rb_str_new2(insn_name_info[i])));
+ for (i = 0; i < VM_INSTRUCTION_SIZE; i++) {
+ rb_ary_push(ary, rb_fstring_cstr(insn_name(i)));
}
return rb_obj_freeze(ary);
}
@@ -6416,7 +7793,7 @@ register_label(rb_iseq_t *iseq, struct st_table *labels_table, VALUE obj)
{
LABEL *label = 0;
st_data_t tmp;
- obj = rb_convert_type(obj, T_SYMBOL, "Symbol", "to_sym");
+ obj = rb_to_symbol_type(obj);
if (st_lookup(labels_table, obj, &tmp) == 0) {
label = NEW_LABEL(0);
@@ -6465,30 +7842,34 @@ iseq_build_from_ary_exception(rb_iseq_t *iseq, struct st_table *labels_table,
for (i=0; i<RARRAY_LEN(exception); i++) {
const rb_iseq_t *eiseq;
VALUE v, type;
- const VALUE *ptr;
LABEL *lstart, *lend, *lcont;
unsigned int sp;
- v = rb_convert_type(RARRAY_AREF(exception, i), T_ARRAY,
- "Array", "to_ary");
+ v = rb_to_array_type(RARRAY_AREF(exception, i));
if (RARRAY_LEN(v) != 6) {
rb_raise(rb_eSyntaxError, "wrong exception entry");
}
- ptr = RARRAY_CONST_PTR(v);
- type = get_exception_sym2type(ptr[0]);
- if (ptr[1] == Qnil) {
+ type = get_exception_sym2type(RARRAY_AREF(v, 0));
+ if (RARRAY_AREF(v, 1) == Qnil) {
eiseq = NULL;
}
else {
- eiseq = rb_iseqw_to_iseq(rb_iseq_load(ptr[1], (VALUE)iseq, Qnil));
- }
+ eiseq = rb_iseqw_to_iseq(rb_iseq_load(RARRAY_AREF(v, 1), (VALUE)iseq, Qnil));
+ }
- lstart = register_label(iseq, labels_table, ptr[2]);
- lend = register_label(iseq, labels_table, ptr[3]);
- lcont = register_label(iseq, labels_table, ptr[4]);
- sp = NUM2UINT(ptr[5]);
+ lstart = register_label(iseq, labels_table, RARRAY_AREF(v, 2));
+ lend = register_label(iseq, labels_table, RARRAY_AREF(v, 3));
+ 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;
+ }
- (void)sp;
+ lcont->sp = sp;
ADD_CATCH_ENTRY(type, lstart, lend, eiseq, lcont);
@@ -6528,7 +7909,6 @@ iseq_build_load_iseq(const rb_iseq_t *iseq, VALUE op)
}
loaded_iseq = rb_iseqw_to_iseq(iseqw);
- iseq_add_mark_object(iseq, (VALUE)loaded_iseq);
return loaded_iseq;
}
@@ -6568,12 +7948,26 @@ iseq_build_callinfo_from_hash(rb_iseq_t *iseq, VALUE op)
return (VALUE)new_callinfo(iseq, mid, orig_argc, flag, kw_arg, (flag & VM_CALL_ARGS_SIMPLE) == 0);
}
+static rb_event_flag_t
+event_name_to_flag(VALUE sym)
+{
+#define CHECK_EVENT(ev) if (sym == ID2SYM(rb_intern(#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);
+#undef CHECK_EVENT
+ return RUBY_EVENT_NONE;
+}
+
static int
-iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor,
+iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
VALUE body, VALUE labels_wrapper)
{
/* TODO: body should be frozen */
- const VALUE *ptr = RARRAY_CONST_PTR(body);
long i, len = RARRAY_LEN(body);
struct st_table *labels_table = DATA_PTR(labels_wrapper);
int j;
@@ -6590,11 +7984,17 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor,
}
for (i=0; i<len; i++) {
- VALUE obj = ptr[i];
+ VALUE obj = RARRAY_AREF(body, i);
if (SYMBOL_P(obj)) {
- LABEL *label = register_label(iseq, labels_table, obj);
- ADD_LABEL(anchor, label);
+ 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);
@@ -6638,12 +8038,14 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor,
break;
case TS_VALUE:
argv[j] = op;
- iseq_add_mark_object(iseq, op);
+ iseq_add_mark_object_compile_time(iseq, op);
break;
case TS_ISEQ:
{
if (op != Qnil) {
- argv[j] = (VALUE)iseq_build_load_iseq(iseq, op);
+ VALUE v = (VALUE)iseq_build_load_iseq(iseq, op);
+ argv[j] = v;
+ iseq_add_mark_object_compile_time(iseq, v);
}
else {
argv[j] = 0;
@@ -6651,9 +8053,11 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor,
}
break;
case TS_GENTRY:
- op = rb_convert_type(op, T_SYMBOL, "Symbol", "to_sym");
+ op = rb_to_symbol_type(op);
argv[j] = (VALUE)rb_global_entry(SYM2ID(op));
break;
+ case TS_ISE:
+ FL_SET(iseq, ISEQ_MARKABLE_ISEQ);
case TS_IC:
argv[j] = op;
if (NUM2UINT(op) >= iseq->body->is_size) {
@@ -6667,16 +8071,15 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor,
argv[j] = Qfalse;
break;
case TS_ID:
- argv[j] = rb_convert_type(op, T_SYMBOL,
- "Symbol", "to_sym");
+ argv[j] = rb_to_symbol_type(op);
break;
case TS_CDHASH:
{
int i;
- VALUE map = rb_hash_new();
+ VALUE map = rb_hash_new_with_size(RARRAY_LEN(op)/2);
- rb_hash_tbl_raw(map)->type = &cdhash_type;
- op = rb_convert_type(op, T_ARRAY, "Array", "to_ary");
+ 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);
@@ -6686,7 +8089,7 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor,
}
RB_GC_GUARD(op);
argv[j] = map;
- rb_iseq_add_mark_object(iseq, map);
+ iseq_add_mark_object_compile_time(iseq, map);
}
break;
case TS_FUNCPTR:
@@ -6718,20 +8121,18 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor,
return iseq_setup(iseq, anchor);
}
-#define CHECK_ARRAY(v) rb_convert_type((v), T_ARRAY, "Array", "to_ary")
-#define CHECK_SYMBOL(v) rb_convert_type((v), T_SYMBOL, "Symbol", "to_sym")
+#define CHECK_ARRAY(v) rb_to_array_type(v)
+#define CHECK_SYMBOL(v) rb_to_symbol_type(v)
static int
int_param(int *dst, VALUE param, VALUE sym)
{
VALUE val = rb_hash_aref(param, sym);
- switch (TYPE(val)) {
- case T_NIL:
- return FALSE;
- case T_FIXNUM:
+ if (FIXNUM_P(val)) {
*dst = FIX2INT(val);
return TRUE;
- default:
+ }
+ else if (!NIL_P(val)) {
rb_raise(rb_eTypeError, "invalid %+"PRIsVALUE" Fixnum: %+"PRIsVALUE,
sym, val);
}
@@ -6755,7 +8156,7 @@ iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords)
#define SYM(s) ID2SYM(rb_intern(#s))
(void)int_param(&keyword->bits_start, params, SYM(kwbits));
i = keyword->bits_start - keyword->num;
- ids = (VALUE *)&iseq->body->local_table[i];
+ ids = (ID *)&iseq->body->local_table[i];
#undef SYM
/* required args */
@@ -6772,10 +8173,14 @@ iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords)
default_values: /* note: we intentionally preserve `i' from previous loop */
default_len = len - i;
if (default_len == 0) {
+ keyword->table = ids;
return keyword;
}
+ else if (default_len < 0) {
+ UNREACHABLE;
+ }
- dvs = ALLOC_N(VALUE, default_len);
+ dvs = ALLOC_N(VALUE, (unsigned int)default_len);
for (j = 0; i < len; i++, j++) {
key = RARRAY_AREF(keywords, i);
@@ -6809,6 +8214,7 @@ rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
{
#define SYM(s) ID2SYM(rb_intern(#s))
int i, len;
+ unsigned int arg_size, local_size, stack_max;
ID *tbl;
struct st_table *labels_table = st_init_numtable();
VALUE labels_wrapper = Data_Wrap_Struct(0, 0, st_free_table, labels_table);
@@ -6833,11 +8239,6 @@ rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
}
}
- /*
- * we currently ignore misc params,
- * local_size, stack_size and param.size are all calculated
- */
-
#define INT_PARAM(F) int_param(&iseq->body->param.F, params, SYM(F))
if (INT_PARAM(lead_num)) {
iseq->body->param.flags.has_lead = TRUE;
@@ -6847,9 +8248,16 @@ rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
if (INT_PARAM(rest_start)) iseq->body->param.flags.has_rest = TRUE;
if (INT_PARAM(block_start)) iseq->body->param.flags.has_block = TRUE;
#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);
+#undef INT_PARAM
+ }
- switch (TYPE(arg_opt_labels)) {
- case T_ARRAY:
+ if (RB_TYPE_P(arg_opt_labels, T_ARRAY)) {
len = RARRAY_LENINT(arg_opt_labels);
iseq->body->param.flags.has_opt = !!(len - 1 >= 0);
@@ -6865,19 +8273,16 @@ rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
iseq->body->param.opt_num = len - 1;
iseq->body->param.opt_table = opt_table;
}
- case T_NIL:
- break;
- default:
+ }
+ else if (!NIL_P(arg_opt_labels)) {
rb_raise(rb_eTypeError, ":opt param is not an array: %+"PRIsVALUE,
arg_opt_labels);
}
- switch (TYPE(keywords)) {
- case T_ARRAY:
+ if (RB_TYPE_P(keywords, T_ARRAY)) {
iseq->body->param.keyword = iseq_build_kw(iseq, params, keywords);
- case T_NIL:
- break;
- default:
+ }
+ else if (!NIL_P(keywords)) {
rb_raise(rb_eTypeError, ":keywords param is not an array: %+"PRIsVALUE,
keywords);
}
@@ -6902,6 +8307,10 @@ rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
/* body */
iseq_build_from_ary_body(iseq, anchor, body, labels_wrapper);
+
+ iseq->body->param.size = arg_size;
+ iseq->body->local_table_size = local_size;
+ iseq->body->stack_max = stack_max;
}
/* for parser */
@@ -6912,20 +8321,22 @@ rb_dvar_defined(ID id, const struct rb_block *base_block)
const rb_iseq_t *iseq;
if (base_block && (iseq = vm_block_iseq(base_block)) != NULL) {
- while (iseq->body->type == ISEQ_TYPE_BLOCK ||
- iseq->body->type == ISEQ_TYPE_RESCUE ||
- iseq->body->type == ISEQ_TYPE_ENSURE ||
- iseq->body->type == ISEQ_TYPE_EVAL ||
- iseq->body->type == ISEQ_TYPE_MAIN
+ const struct rb_iseq_constant_body *body = iseq->body;
+ 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 < iseq->body->local_table_size; i++) {
- if (iseq->body->local_table[i] == id) {
+ for (i = 0; i < body->local_table_size; i++) {
+ if (body->local_table[i] == id) {
return 1;
}
}
- iseq = iseq->body->parent_iseq;
+ iseq = body->parent_iseq;
+ body = iseq->body;
}
}
return 0;
@@ -6938,10 +8349,10 @@ rb_local_defined(ID id, const struct rb_block *base_block)
if (base_block && (iseq = vm_block_iseq(base_block)) != NULL) {
unsigned int i;
- iseq = iseq->body->local_iseq;
+ const struct rb_iseq_constant_body *const body = iseq->body->local_iseq->body;
- for (i=0; i<iseq->body->local_table_size; i++) {
- if (iseq->body->local_table[i] == id) {
+ for (i=0; i<body->local_table_size; i++) {
+ if (body->local_table[i] == id) {
return 1;
}
}
@@ -6950,21 +8361,21 @@ rb_local_defined(ID id, const struct rb_block *base_block)
}
static int
-caller_location(VALUE *path, VALUE *absolute_path)
+caller_location(VALUE *path, VALUE *realpath)
{
- const rb_thread_t *const th = GET_THREAD();
+ const rb_execution_context_t *ec = GET_EC();
const rb_control_frame_t *const cfp =
- rb_vm_get_ruby_level_next_cfp(th, th->cfp);
+ rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
if (cfp) {
int line = rb_vm_get_sourceline(cfp);
- *path = cfp->iseq->body->location.path;
- *absolute_path = cfp->iseq->body->location.absolute_path;
+ *path = rb_iseq_path(cfp->iseq);
+ *realpath = rb_iseq_realpath(cfp->iseq);
return line;
}
else {
- *path = rb_fstring_cstr("<compiled>");
- *absolute_path = *path;
+ *path = rb_fstring_lit("<compiled>");
+ *realpath = *path;
return 1;
}
}
@@ -6977,28 +8388,29 @@ typedef struct {
static const rb_iseq_t *
method_for_self(VALUE name, VALUE arg, rb_insn_func_t func,
- VALUE (*build)(rb_iseq_t *, LINK_ANCHOR *, VALUE))
+ VALUE (*build)(rb_iseq_t *, LINK_ANCHOR *const, VALUE))
{
- VALUE path, absolute_path;
+ VALUE path, realpath;
accessor_args acc;
acc.arg = arg;
acc.func = func;
- acc.line = caller_location(&path, &absolute_path);
- return rb_iseq_new_with_opt((NODE *)IFUNC_NEW(build, (VALUE)&acc, 0),
- rb_sym2str(name), path, absolute_path,
- INT2FIX(acc.line), 0, ISEQ_TYPE_METHOD, 0);
+ acc.line = caller_location(&path, &realpath);
+ return rb_iseq_new_ifunc(IFUNC_NEW(build, (VALUE)&acc, 0),
+ rb_sym2str(name), path, realpath,
+ INT2FIX(acc.line), 0, ISEQ_TYPE_METHOD, 0);
}
static VALUE
-for_self_aref(rb_iseq_t *iseq, LINK_ANCHOR *ret, VALUE a)
+for_self_aref(rb_iseq_t *iseq, LINK_ANCHOR *const ret, VALUE a)
{
const accessor_args *const args = (void *)a;
const int line = args->line;
+ struct rb_iseq_constant_body *const body = iseq->body;
iseq_set_local_table(iseq, 0);
- iseq->body->param.lead_num = 0;
- iseq->body->param.size = 0;
+ body->param.lead_num = 0;
+ body->param.size = 0;
ADD_INSN1(ret, line, putobject, args->arg);
ADD_INSN1(ret, line, opt_call_c_function, (VALUE)args->func);
@@ -7006,15 +8418,16 @@ for_self_aref(rb_iseq_t *iseq, LINK_ANCHOR *ret, VALUE a)
}
static VALUE
-for_self_aset(rb_iseq_t *iseq, LINK_ANCHOR *ret, VALUE a)
+for_self_aset(rb_iseq_t *iseq, LINK_ANCHOR *const ret, VALUE a)
{
const accessor_args *const args = (void *)a;
const int line = args->line;
+ struct rb_iseq_constant_body *const body = iseq->body;
static const ID vars[] = {1, idUScore};
iseq_set_local_table(iseq, vars);
- iseq->body->param.lead_num = 1;
- iseq->body->param.size = 1;
+ body->param.lead_num = 1;
+ body->param.size = 1;
ADD_GETLOCAL(ret, line, numberof(vars)-1, 0);
ADD_INSN1(ret, line, putobject, args->arg);
@@ -7043,6 +8456,10 @@ rb_method_for_self_aset(VALUE name, VALUE arg, rb_insn_func_t func)
/* ISeq binary format */
+#ifndef IBF_ISEQ_DEBUG
+#define IBF_ISEQ_DEBUG 0
+#endif
+
typedef unsigned int ibf_offset_t;
#define IBF_OFFSET(ptr) ((ibf_offset_t)(VALUE)(ptr))
@@ -7062,15 +8479,6 @@ struct ibf_header {
ibf_offset_t object_list_offset;
};
-struct ibf_id_entry {
- enum {
- ibf_id_enc_ascii,
- ibf_id_enc_utf8,
- ibf_id_enc_other
- } enc : 2;
- char body[1];
-};
-
struct ibf_dump {
VALUE str;
VALUE iseq_list; /* [iseq0 offset, ...] */
@@ -7082,7 +8490,7 @@ struct ibf_dump {
rb_iseq_t * iseq_alloc(void);
struct ibf_load {
- const char *buff;
+ const RUBY_ALIGNAS(SIZEOF_VALUE) char *buff;
const struct ibf_header *header;
ID *id_list; /* [id0, ...] */
VALUE iseq_list; /* [iseq0, ...] */
@@ -7095,7 +8503,32 @@ struct ibf_load {
static ibf_offset_t
ibf_dump_pos(struct ibf_dump *dump)
{
- return (unsigned int)rb_str_strlen(dump->str);
+ long pos = RSTRING_LEN(dump->str);
+#if SIZEOF_LONG > SIZEOF_INT
+ if (pos >= UINT_MAX) {
+ rb_raise(rb_eRuntimeError, "dump size exceeds");
+ }
+#endif
+ return (unsigned int)pos;
+}
+
+static void
+ibf_dump_align(struct ibf_dump *dump, size_t align)
+{
+ ibf_offset_t pos = ibf_dump_pos(dump);
+ if (pos % align) {
+ static const char padding[sizeof(VALUE)];
+ size_t size = align - ((size_t)pos % align);
+#if SIZEOF_LONG > SIZEOF_INT
+ if (pos + size >= UINT_MAX) {
+ rb_raise(rb_eRuntimeError, "dump size exceeds");
+ }
+#endif
+ for (; size > sizeof(padding); size -= sizeof(padding)) {
+ rb_str_cat(dump->str, padding, sizeof(padding));
+ }
+ rb_str_cat(dump->str, padding, size);
+ }
}
static ibf_offset_t
@@ -7125,10 +8558,13 @@ ibf_load_alloc(const struct ibf_load *load, ibf_offset_t offset, int size)
return buff;
}
-#define IBF_W(b, type, n) (type *)(VALUE)ibf_dump_write(dump, (b), sizeof(type) * (n))
+#define IBF_W_ALIGN(type) (RUBY_ALIGNOF(type) > 1 ? ibf_dump_align(dump, RUBY_ALIGNOF(type)) : (void)0)
+
+#define IBF_W(b, type, n) (IBF_W_ALIGN(type), (type *)(VALUE)IBF_WP(b, type, n))
#define IBF_WV(variable) ibf_dump_write(dump, &(variable), sizeof(variable))
#define IBF_WP(b, type, n) ibf_dump_write(dump, (b), sizeof(type) * (n))
#define IBF_R(val, type, n) (type *)ibf_load_alloc(load, IBF_OFFSET(val), sizeof(type) * (n))
+#define IBF_ZERO(variable) memset(&(variable), 0, sizeof(variable))
static int
ibf_table_lookup(struct st_table *table, st_data_t key)
@@ -7243,7 +8679,8 @@ ibf_load_gentry(const struct ibf_load *load, const struct rb_global_entry *entry
static VALUE *
ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
{
- const int iseq_size = iseq->body->iseq_size;
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ const int iseq_size = body->iseq_size;
int code_index;
VALUE *code;
const VALUE *orig_code = rb_iseq_original_iseq(iseq);
@@ -7268,10 +8705,11 @@ ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
code[code_index] = (VALUE)ibf_dump_iseq(dump, (const rb_iseq_t *)op);
break;
case TS_IC:
+ case TS_ISE:
{
unsigned int i;
- for (i=0; i<iseq->body->is_size; i++) {
- if (op == (VALUE)&iseq->body->is_entries[i]) {
+ for (i=0; i<body->is_size; i++) {
+ if (op == (VALUE)&body->is_entries[i]) {
break;
}
}
@@ -7311,11 +8749,14 @@ ibf_load_code(const struct ibf_load *load, const rb_iseq_t *iseq, const struct r
int code_index;
VALUE *code = IBF_R(body->iseq_encoded, VALUE, iseq_size);
- struct rb_call_info *ci_entries = iseq->body->ci_entries;
- struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&iseq->body->ci_entries[iseq->body->ci_size];
- struct rb_call_cache *cc_entries = iseq->body->cc_entries;
- union iseq_inline_storage_entry *is_entries = iseq->body->is_entries;
+ struct rb_iseq_constant_body *load_body = iseq->body;
+ struct rb_call_info *ci_entries = load_body->ci_entries;
+ struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&load_body->ci_entries[load_body->ci_size];
+ struct rb_call_cache *cc_entries = load_body->cc_entries;
+ union iseq_inline_storage_entry *is_entries = load_body->is_entries;
+ load_body->iseq_encoded = code;
+ load_body->iseq_size = 0;
for (code_index=0; code_index<iseq_size;) {
const VALUE insn = code[code_index++];
const char *types = insn_op_types(insn);
@@ -7327,11 +8768,27 @@ ibf_load_code(const struct ibf_load *load, const rb_iseq_t *iseq, const struct r
switch (types[op_index]) {
case TS_CDHASH:
case TS_VALUE:
- code[code_index] = ibf_load_object(load, op);
- break;
+ {
+ VALUE v = ibf_load_object(load, op);
+ code[code_index] = v;
+ if (!SPECIAL_CONST_P(v)) {
+ RB_OBJ_WRITTEN(iseq, Qundef, v);
+ FL_SET(iseq, ISEQ_MARKABLE_ISEQ);
+ }
+ break;
+ }
case TS_ISEQ:
- code[code_index] = (VALUE)ibf_load_iseq(load, (const rb_iseq_t *)op);
- break;
+ {
+ VALUE v = (VALUE)ibf_load_iseq(load, (const rb_iseq_t *)op);
+ code[code_index] = v;
+ if (!SPECIAL_CONST_P(v)) {
+ RB_OBJ_WRITTEN(iseq, Qundef, v);
+ FL_SET(iseq, ISEQ_MARKABLE_ISEQ);
+ }
+ break;
+ }
+ case TS_ISE:
+ FL_SET(iseq, ISEQ_MARKABLE_ISEQ);
case TS_IC:
code[code_index] = (VALUE)&is_entries[(int)op];
break;
@@ -7352,12 +8809,14 @@ ibf_load_code(const struct ibf_load *load, const rb_iseq_t *iseq, const struct r
break;
default:
/* code[code_index] = op; */
- break;
+ continue;
}
}
- assert(insn_len(insn) == op_index+1);
- };
-
+ if (insn_len(insn) != op_index+1) {
+ rb_raise(rb_eRuntimeError, "operand size mismatch");
+ }
+ }
+ load_body->iseq_size = code_index;
return code;
}
@@ -7441,27 +8900,40 @@ ibf_load_param_keyword(const struct ibf_load *load, const struct rb_iseq_constan
}
}
-static struct iseq_line_info_entry *
-ibf_dump_line_info_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
+static struct iseq_insn_info_entry *
+ibf_dump_insns_info_body(struct ibf_dump *dump, const rb_iseq_t *iseq)
{
- return IBF_W(iseq->body->line_info_table, struct iseq_line_info_entry, iseq->body->line_info_size);
+ return IBF_W(iseq->body->insns_info.body, struct iseq_insn_info_entry, iseq->body->insns_info.size);
}
-static struct iseq_line_info_entry *
-ibf_load_line_info_table(const struct ibf_load *load, const struct rb_iseq_constant_body *body)
+static struct iseq_insn_info_entry *
+ibf_load_insns_info_body(const struct ibf_load *load, const struct rb_iseq_constant_body *body)
{
- return IBF_R(body->line_info_table, struct iseq_line_info_entry, body->line_info_size);
+ return IBF_R(body->insns_info.body, struct iseq_insn_info_entry, body->insns_info.size);
+}
+
+static unsigned int *
+ibf_dump_insns_info_positions(struct ibf_dump *dump, const struct rb_iseq_constant_body *body)
+{
+ return IBF_W(body->insns_info.positions, unsigned int, body->insns_info.size);
+}
+
+static unsigned int *
+ibf_load_insns_info_positions(const struct ibf_load *load, const struct rb_iseq_constant_body *body)
+{
+ return IBF_R(body->insns_info.positions, unsigned int, body->insns_info.size);
}
static ID *
ibf_dump_local_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
{
- const int size = iseq->body->local_table_size;
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ const int size = body->local_table_size;
ID *table = ALLOCA_N(ID, size);
int i;
for (i=0; i<size; i++) {
- table[i] = ibf_dump_id(dump, iseq->body->local_table[i]);
+ table[i] = ibf_dump_id(dump, body->local_table[i]);
}
return IBF_W(table, ID, size);
@@ -7529,9 +9001,10 @@ ibf_load_catch_table(const struct ibf_load *load, const struct rb_iseq_constant_
static struct rb_call_info *
ibf_dump_ci_entries(struct ibf_dump *dump, const rb_iseq_t *iseq)
{
- const unsigned int ci_size = iseq->body->ci_size;
- const unsigned int ci_kw_size = iseq->body->ci_kw_size;
- const struct rb_call_info *ci_entries = iseq->body->ci_entries;
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ const unsigned int ci_size = body->ci_size;
+ const unsigned int ci_kw_size = body->ci_kw_size;
+ const struct rb_call_info *ci_entries = body->ci_entries;
struct rb_call_info *dump_ci_entries;
struct rb_call_info_with_kwarg *dump_ci_kw_entries;
int byte_size = ci_size * sizeof(struct rb_call_info) +
@@ -7595,27 +9068,40 @@ static ibf_offset_t
ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
{
struct rb_iseq_constant_body dump_body;
+#if VM_INSN_INFO_TABLE_IMPL == 2
+ unsigned int *positions;
+#endif
dump_body = *iseq->body;
- dump_body.location.path = ibf_dump_object(dump, dump_body.location.path);
- dump_body.location.absolute_path = ibf_dump_object(dump, dump_body.location.absolute_path);
+ dump_body.location.pathobj = ibf_dump_object(dump, dump_body.location.pathobj); /* TODO: freeze */
dump_body.location.base_label = ibf_dump_object(dump, dump_body.location.base_label);
dump_body.location.label = ibf_dump_object(dump, dump_body.location.label);
- dump_body.iseq_encoded = ibf_dump_code(dump, iseq);
- dump_body.param.opt_table = ibf_dump_param_opt_table(dump, iseq);
- dump_body.param.keyword = ibf_dump_param_keyword(dump, iseq);
- dump_body.line_info_table = ibf_dump_line_info_table(dump, iseq);
- dump_body.local_table = ibf_dump_local_table(dump, iseq);
- dump_body.catch_table = ibf_dump_catch_table(dump, iseq);
- dump_body.parent_iseq = ibf_dump_iseq(dump, iseq->body->parent_iseq);
- dump_body.local_iseq = ibf_dump_iseq(dump, iseq->body->local_iseq);
- dump_body.is_entries = NULL;
- dump_body.ci_entries = ibf_dump_ci_entries(dump, iseq);
- dump_body.cc_entries = NULL;
- dump_body.mark_ary = ISEQ_FLIP_CNT(iseq);
+ dump_body.iseq_encoded = ibf_dump_code(dump, iseq);
+ dump_body.param.opt_table = ibf_dump_param_opt_table(dump, iseq);
+ dump_body.param.keyword = ibf_dump_param_keyword(dump, iseq);
+ dump_body.insns_info.body = ibf_dump_insns_info_body(dump, iseq);
+#if VM_INSN_INFO_TABLE_IMPL == 2
+ positions = rb_iseq_insns_info_decode_positions(&dump_body);
+ dump_body.insns_info.positions = positions;
+ dump_body.insns_info.succ_index_table = 0;
+#endif
+ dump_body.insns_info.positions = ibf_dump_insns_info_positions(dump, &dump_body);
+#if VM_INSN_INFO_TABLE_IMPL == 2
+ ruby_xfree(positions);
+#endif
+ dump_body.local_table = ibf_dump_local_table(dump, iseq);
+ dump_body.catch_table = ibf_dump_catch_table(dump, iseq);
+ dump_body.parent_iseq = ibf_dump_iseq(dump, iseq->body->parent_iseq);
+ dump_body.local_iseq = ibf_dump_iseq(dump, iseq->body->local_iseq);
+ dump_body.is_entries = NULL;
+ dump_body.ci_entries = ibf_dump_ci_entries(dump, iseq);
+ dump_body.cc_entries = NULL;
+ dump_body.variable.coverage = Qnil;
+ dump_body.variable.original_iseq = NULL;
- return ibf_dump_write(dump, &dump_body, sizeof(dump_body));
+ IBF_W_ALIGN(struct rb_iseq_constant_body);
+ return IBF_WV(dump_body);
}
static VALUE
@@ -7637,34 +9123,69 @@ ibf_load_iseq_each(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t of
/* memcpy(load_body, load->buff + offset, sizeof(*load_body)); */
load_body->type = body->type;
load_body->stack_max = body->stack_max;
- load_body->iseq_size = body->iseq_size;
load_body->param = body->param;
+ load_body->param.flags.has_kw = FALSE;
load_body->local_table_size = body->local_table_size;
load_body->is_size = body->is_size;
load_body->ci_size = body->ci_size;
load_body->ci_kw_size = body->ci_kw_size;
- load_body->line_info_size = body->line_info_size;
+ load_body->insns_info.size = body->insns_info.size;
- RB_OBJ_WRITE(iseq, &load_body->mark_ary, iseq_mark_ary_create((int)body->mark_ary));
+ ISEQ_COVERAGE_SET(iseq, Qnil);
+ ISEQ_ORIGINAL_ISEQ_CLEAR(iseq);
+ iseq->body->variable.flip_count = body->variable.flip_count;
+
+ {
+ VALUE realpath = Qnil, path = ibf_load_object(load, body->location.pathobj);
+ 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.path, ibf_load_location_str(load, body->location.path));
- RB_OBJ_WRITE(iseq, &load_body->location.absolute_path, ibf_load_location_str(load, body->location.absolute_path));
RB_OBJ_WRITE(iseq, &load_body->location.base_label, ibf_load_location_str(load, body->location.base_label));
RB_OBJ_WRITE(iseq, &load_body->location.label, ibf_load_location_str(load, body->location.label));
load_body->location.first_lineno = body->location.first_lineno;
-
- load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, body->is_size);
- load_body->ci_entries = ibf_load_ci_entries(load, body);
- load_body->cc_entries = ZALLOC_N(struct rb_call_cache, body->ci_size + body->ci_kw_size);
- load_body->param.opt_table = ibf_load_param_opt_table(load, body);
- load_body->param.keyword = ibf_load_param_keyword(load, body);
- load_body->line_info_table = ibf_load_line_info_table(load, body);
- load_body->local_table = ibf_load_local_table(load, body);
- load_body->catch_table = ibf_load_catch_table(load, body);
- load_body->parent_iseq = ibf_load_iseq(load, body->parent_iseq);
- load_body->local_iseq = ibf_load_iseq(load, body->local_iseq);
-
- load_body->iseq_encoded = ibf_load_code(load, iseq, body);
+ load_body->location.node_id = body->location.node_id;
+ load_body->location.code_location = body->location.code_location;
+ load_body->catch_except_p = body->catch_except_p;
+
+ load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, body->is_size);
+ load_body->ci_entries = ibf_load_ci_entries(load, body);
+ load_body->cc_entries = ZALLOC_N(struct rb_call_cache, body->ci_size + body->ci_kw_size);
+ load_body->param.opt_table = ibf_load_param_opt_table(load, body);
+ load_body->param.keyword = ibf_load_param_keyword(load, body);
+ load_body->param.flags.has_kw = body->param.flags.has_kw;
+ load_body->insns_info.body = ibf_load_insns_info_body(load, body);
+ load_body->insns_info.positions = ibf_load_insns_info_positions(load, body);
+ load_body->local_table = ibf_load_local_table(load, body);
+ load_body->catch_table = ibf_load_catch_table(load, body);
+ load_body->parent_iseq = ibf_load_iseq(load, body->parent_iseq);
+ load_body->local_iseq = ibf_load_iseq(load, body->local_iseq);
+
+ ibf_load_code(load, iseq, body);
+#if VM_INSN_INFO_TABLE_IMPL == 2
+ rb_iseq_insns_info_encode_positions(iseq);
+#endif
rb_iseq_translate_threaded_code(iseq);
}
@@ -7681,6 +9202,7 @@ ibf_dump_iseq_list(struct ibf_dump *dump, struct ibf_header *header)
list[i] = (ibf_offset_t)NUM2LONG(rb_ary_entry(dump->iseq_list, i));
}
+ ibf_dump_align(dump, sizeof(ibf_offset_t));
header->iseq_list_offset = ibf_dump_write(dump, list, sizeof(ibf_offset_t) * size);
header->iseq_list_size = (unsigned int)size;
}
@@ -7721,6 +9243,7 @@ ibf_dump_id_list(struct ibf_dump *dump, struct ibf_header *header)
st_foreach(dump->id_table, ibf_dump_id_list_i, (st_data_t)&arg);
+ ibf_dump_align(dump, sizeof(long));
header->id_list_offset = ibf_dump_write(dump, arg.list, sizeof(long) * size);
header->id_list_size = (unsigned int)size;
}
@@ -7739,6 +9262,8 @@ struct ibf_object_header {
unsigned int frozen: 1;
unsigned int internal: 1;
};
+static const size_t ibf_object_header_align =
+ RUBY_ALIGNOF(struct ibf_object_header);
enum ibf_object_class_index {
IBF_OBJECT_CLASS_OBJECT,
@@ -7749,7 +9274,7 @@ enum ibf_object_class_index {
struct ibf_object_string {
long encindex;
long len;
- char ptr[1];
+ char ptr[FLEX_ARY_LEN];
};
struct ibf_object_regexp {
@@ -7759,12 +9284,12 @@ struct ibf_object_regexp {
struct ibf_object_array {
long len;
- long ary[1];
+ long ary[FLEX_ARY_LEN];
};
struct ibf_object_hash {
long len;
- long keyval[1];
+ long keyval[FLEX_ARY_LEN];
};
struct ibf_object_struct_range {
@@ -7777,7 +9302,7 @@ struct ibf_object_struct_range {
struct ibf_object_bignum {
ssize_t slen;
- BDIGIT digits[1];
+ BDIGIT digits[FLEX_ARY_LEN];
};
enum ibf_object_data_type {
@@ -7792,20 +9317,36 @@ struct ibf_object_symbol {
long str;
};
-#define IBF_OBJHEADER(offset) (struct ibf_object_header *)(load->buff + (offset))
-#define IBF_OBJBODY(type, offset) (type *)(load->buff + sizeof(struct ibf_object_header) + (offset))
+#define IBF_ALIGNED_OFFSET(align, offset) /* offset > 0 */ \
+ ((((offset) - 1) / (align) + 1) * (align))
+#define IBF_OBJHEADER(offset) (const struct ibf_object_header *)\
+ ibf_load_check_offset(load, IBF_ALIGNED_OFFSET(ibf_object_header_align, offset))
+#define IBF_OBJBODY(type, offset) (const type *)\
+ ibf_load_check_offset(load, IBF_ALIGNED_OFFSET(RUBY_ALIGNOF(type), offset))
+
+static const void *
+ibf_load_check_offset(const struct ibf_load *load, size_t offset)
+{
+ if (offset >= (size_t)RSTRING_LEN(load->str)) {
+ rb_raise(rb_eIndexError, "object offset out of range: %"PRIdSIZE, offset);
+ }
+ return load->buff + offset;
+}
+
+NORETURN(static void ibf_dump_object_unsupported(struct ibf_dump *dump, VALUE obj));
static void
ibf_dump_object_unsupported(struct ibf_dump *dump, VALUE obj)
{
- rb_obj_info_dump(obj);
- rb_bug("ibf_dump_object_unsupported: unsupported");
+ char buff[0x100];
+ rb_raw_obj_info(buff, sizeof(buff), obj);
+ rb_raise(rb_eNotImpError, "ibf_dump_object_unsupported: %s", buff);
}
static VALUE
ibf_load_object_unsupported(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
{
- rb_bug("unsupported");
+ rb_raise(rb_eArgError, "unsupported");
return Qnil;
}
@@ -7833,7 +9374,7 @@ ibf_dump_object_class(struct ibf_dump *dump, VALUE obj)
static VALUE
ibf_load_object_class(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
{
- enum ibf_object_class_index *cindexp = IBF_OBJBODY(enum ibf_object_class_index, offset);
+ const enum ibf_object_class_index *cindexp = IBF_OBJBODY(enum ibf_object_class_index, offset);
enum ibf_object_class_index cindex = *cindexp;
switch (cindex) {
@@ -7845,7 +9386,7 @@ ibf_load_object_class(const struct ibf_load *load, const struct ibf_object_heade
return rb_eStandardError;
}
- rb_bug("ibf_load_object_class: unknown class (%d)", (int)cindex);
+ rb_raise(rb_eArgError, "ibf_load_object_class: unknown class (%d)", (int)cindex);
}
@@ -7853,13 +9394,13 @@ static void
ibf_dump_object_float(struct ibf_dump *dump, VALUE obj)
{
double dbl = RFLOAT_VALUE(obj);
- ibf_dump_write(dump, &dbl, sizeof(dbl));
+ (void)IBF_W(&dbl, double, 1);
}
static VALUE
ibf_load_object_float(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
{
- double *dblp = IBF_OBJBODY(double, offset);
+ const double *dblp = IBF_OBJBODY(double, offset);
return DBL2NUM(*dblp);
}
@@ -7869,6 +9410,7 @@ ibf_dump_object_string(struct ibf_dump *dump, VALUE obj)
long encindex = (long)rb_enc_get_index(obj);
long len = RSTRING_LEN(obj);
const char *ptr = RSTRING_PTR(obj);
+ long buff[2];
if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
rb_encoding *enc = rb_enc_from_index((int)encindex);
@@ -7876,8 +9418,9 @@ ibf_dump_object_string(struct ibf_dump *dump, VALUE obj)
encindex = RUBY_ENCINDEX_BUILTIN_MAX + ibf_dump_object(dump, rb_str_new2(enc_name));
}
- IBF_WV(encindex);
- IBF_WV(len);
+ buff[0] = encindex;
+ buff[1] = len;
+ (void)IBF_W(buff, long, 2);
IBF_WP(ptr, char, len);
}
@@ -7904,10 +9447,11 @@ static void
ibf_dump_object_regexp(struct ibf_dump *dump, VALUE obj)
{
struct ibf_object_regexp regexp;
- regexp.srcstr = RREGEXP_SRC(obj);
+ VALUE srcstr = RREGEXP_SRC(obj);
+ IBF_ZERO(regexp);
regexp.option = (char)rb_reg_options(obj);
- regexp.srcstr = (long)ibf_dump_object(dump, regexp.srcstr);
- IBF_WV(regexp);
+ regexp.srcstr = (long)ibf_dump_object(dump, srcstr);
+ (void)IBF_W(&regexp, struct ibf_object_regexp, 1);
}
static VALUE
@@ -7927,7 +9471,7 @@ static void
ibf_dump_object_array(struct ibf_dump *dump, VALUE obj)
{
long i, len = (int)RARRAY_LEN(obj);
- IBF_WV(len);
+ (void)IBF_W(&len, long, 1);
for (i=0; i<len; i++) {
long index = (long)ibf_dump_object(dump, RARRAY_AREF(obj, i));
IBF_WV(index);
@@ -7955,10 +9499,10 @@ static int
ibf_dump_object_hash_i(st_data_t key, st_data_t val, st_data_t ptr)
{
struct ibf_dump *dump = (struct ibf_dump *)ptr;
- long key_index = (long)ibf_dump_object(dump, (VALUE)key);
- long val_index = (long)ibf_dump_object(dump, (VALUE)val);
- IBF_WV(key_index);
- IBF_WV(val_index);
+ long keyval[2];
+ keyval[0] = (long)ibf_dump_object(dump, (VALUE)key);
+ keyval[1] = (long)ibf_dump_object(dump, (VALUE)val);
+ (void)IBF_W(keyval, long, 2);
return ST_CONTINUE;
}
@@ -7966,15 +9510,15 @@ static void
ibf_dump_object_hash(struct ibf_dump *dump, VALUE obj)
{
long len = RHASH_SIZE(obj);
- IBF_WV(len);
- if (len > 0) st_foreach(RHASH(obj)->ntbl, ibf_dump_object_hash_i, (st_data_t)dump);
+ (void)IBF_W(&len, long, 1);
+ if (len > 0) rb_hash_foreach(obj, ibf_dump_object_hash_i, (VALUE)dump);
}
static VALUE
ibf_load_object_hash(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
{
const struct ibf_object_hash *hash = IBF_OBJBODY(struct ibf_object_hash, offset);
- VALUE obj = rb_hash_new();
+ VALUE obj = rb_hash_new_with_size(hash->len);
int i;
for (i=0; i<hash->len; i++) {
@@ -7996,6 +9540,7 @@ ibf_dump_object_struct(struct ibf_dump *dump, VALUE obj)
if (rb_obj_is_kind_of(obj, rb_cRange)) {
struct ibf_object_struct_range range;
VALUE beg, end;
+ IBF_ZERO(range);
range.len = 3;
range.class_index = 0;
@@ -8003,10 +9548,12 @@ ibf_dump_object_struct(struct ibf_dump *dump, VALUE obj)
range.beg = (long)ibf_dump_object(dump, beg);
range.end = (long)ibf_dump_object(dump, end);
+ IBF_W_ALIGN(struct ibf_object_struct_range);
IBF_WV(range);
}
else {
- rb_bug("ibf_dump_object_struct: unsupported class");
+ rb_raise(rb_eNotImpError, "ibf_dump_object_struct: unsupported class %"PRIsVALUE,
+ rb_class_name(CLASS_OF(obj)));
}
}
@@ -8029,7 +9576,7 @@ ibf_dump_object_bignum(struct ibf_dump *dump, VALUE obj)
ssize_t slen = BIGNUM_SIGN(obj) > 0 ? len : len * -1;
BDIGIT *d = BIGNUM_DIGITS(obj);
- IBF_WV(slen);
+ (void)IBF_W(&slen, ssize_t, 1);
IBF_WP(d, BDIGIT, len);
}
@@ -8052,11 +9599,12 @@ ibf_dump_object_data(struct ibf_dump *dump, VALUE obj)
if (rb_data_is_encoding(obj)) {
rb_encoding *enc = rb_to_encoding(obj);
const char *name = rb_enc_name(enc);
- enum ibf_object_data_type type = IBF_OBJECT_DATA_ENCODING;
long len = strlen(name) + 1;
- IBF_WV(type);
- IBF_WV(len);
- IBF_WP(name, char, strlen(name) + 1);
+ long data[2];
+ data[0] = IBF_OBJECT_DATA_ENCODING;
+ data[1] = len;
+ (void)IBF_W(data, long, 2);
+ IBF_WP(name, char, len);
}
else {
ibf_dump_object_unsupported(dump, obj);
@@ -8066,11 +9614,12 @@ ibf_dump_object_data(struct ibf_dump *dump, VALUE obj)
static VALUE
ibf_load_object_data(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
{
- const enum ibf_object_data_type *typep = IBF_OBJBODY(enum ibf_object_data_type, offset);
- /* const long *lenp = IBF_OBJBODY(long, offset + sizeof(enum ibf_object_data_type)); */
- const char *data = IBF_OBJBODY(char, offset + sizeof(enum ibf_object_data_type) + sizeof(long));
+ const long *body = IBF_OBJBODY(long, offset);
+ const enum ibf_object_data_type type = (enum ibf_object_data_type)body[0];
+ /* const long len = body[1]; */
+ const char *data = (const char *)&body[2];
- switch (*typep) {
+ switch (type) {
case IBF_OBJECT_DATA_ENCODING:
{
VALUE encobj = rb_enc_from_encoding(rb_enc_find(data));
@@ -8084,11 +9633,11 @@ ibf_load_object_data(const struct ibf_load *load, const struct ibf_object_header
static void
ibf_dump_object_complex_rational(struct ibf_dump *dump, VALUE obj)
{
- long real = (long)ibf_dump_object(dump, RCOMPLEX(obj)->real);
- long imag = (long)ibf_dump_object(dump, RCOMPLEX(obj)->imag);
+ long data[2];
+ data[0] = (long)ibf_dump_object(dump, RCOMPLEX(obj)->real);
+ data[1] = (long)ibf_dump_object(dump, RCOMPLEX(obj)->imag);
- IBF_WV(real);
- IBF_WV(imag);
+ (void)IBF_W(data, long, 2);
}
static VALUE
@@ -8110,7 +9659,7 @@ ibf_dump_object_symbol(struct ibf_dump *dump, VALUE obj)
{
VALUE str = rb_sym2str(obj);
long str_index = (long)ibf_dump_object(dump, str);
- IBF_WV(str_index);
+ (void)IBF_W(&str_index, long, 1);
}
static VALUE
@@ -8156,16 +9705,20 @@ static ibf_dump_object_function dump_object_functions[RUBY_T_MASK+1] = {
ibf_dump_object_unsupported, /* T_ICLASS 0x1c */
ibf_dump_object_unsupported, /* T_ZOMBIE 0x1d */
ibf_dump_object_unsupported, /* 0x1e */
- ibf_dump_object_unsupported /* 0x1f */
+ ibf_dump_object_unsupported, /* 0x1f */
};
static ibf_offset_t
-lbf_dump_object_object(struct ibf_dump *dump, VALUE obj)
+ibf_dump_object_object(struct ibf_dump *dump, VALUE obj)
{
struct ibf_object_header obj_header;
- ibf_offset_t current_offset = ibf_dump_pos(dump);
+ ibf_offset_t current_offset;
+ IBF_ZERO(obj_header);
obj_header.type = TYPE(obj);
+ IBF_W_ALIGN(ibf_offset_t);
+ current_offset = ibf_dump_pos(dump);
+
if (SPECIAL_CONST_P(obj)) {
if (RB_TYPE_P(obj, T_SYMBOL) ||
RB_TYPE_P(obj, T_FLOAT)) {
@@ -8176,7 +9729,7 @@ lbf_dump_object_object(struct ibf_dump *dump, VALUE obj)
obj_header.frozen = TRUE;
obj_header.internal = TRUE;
IBF_WV(obj_header);
- IBF_WV(obj);
+ (void)IBF_W(&obj, VALUE, 1);
}
else {
obj_header.internal = (RBASIC_CLASS(obj) == 0) ? TRUE : FALSE;
@@ -8223,7 +9776,7 @@ static ibf_load_object_function load_object_functions[RUBY_T_MASK+1] = {
ibf_load_object_unsupported, /* T_ICLASS 0x1c */
ibf_load_object_unsupported, /* T_ZOMBIE 0x1d */
ibf_load_object_unsupported, /* 0x1e */
- ibf_load_object_unsupported /* 0x1f */
+ ibf_load_object_unsupported, /* 0x1f */
};
static VALUE
@@ -8241,9 +9794,25 @@ ibf_load_object(const struct ibf_load *load, VALUE object_index)
ibf_offset_t *offsets = (ibf_offset_t *)(load->header->object_list_offset + load->buff);
ibf_offset_t offset = offsets[object_index];
const struct ibf_object_header *header = IBF_OBJHEADER(offset);
+ size_t value_offset;
+
+#if IBF_ISEQ_DEBUG
+ fprintf(stderr, "ibf_load_object: list=%#x offsets=%p offset=%#x\n",
+ load->header->object_list_offset, offsets, offset);
+ fprintf(stderr, "ibf_load_object: type=%#x special=%d frozen=%d internal=%d\n",
+ header->type, header->special_const, header->frozen, header->internal);
+#endif
+ value_offset = (const char *)(header + 1) - load->buff;
+ if (value_offset >= (size_t)RSTRING_LEN(load->str)) {
+ rb_raise(rb_eIndexError, "object offset out of range: %"PRIdSIZE, value_offset);
+ }
+ offset = (ibf_offset_t)value_offset;
if (header->special_const) {
- VALUE *vp = IBF_OBJBODY(VALUE, offset);
+ const VALUE *vp = IBF_OBJBODY(VALUE, offset);
+#if IBF_ISEQ_DEBUG
+ fprintf(stderr, "ibf_load_object: vp=%p\n", vp);
+#endif
obj = *vp;
}
else {
@@ -8252,7 +9821,10 @@ ibf_load_object(const struct ibf_load *load, VALUE object_index)
rb_ary_store(load->obj_list, (long)object_index, obj);
}
- iseq_add_mark_object(load->iseq, obj);
+#if IBF_ISEQ_DEBUG
+ fprintf(stderr, "ibf_load_object: index=%#"PRIxVALUE" obj=%#"PRIxVALUE"\n",
+ object_index, obj);
+#endif
return obj;
}
}
@@ -8265,10 +9837,11 @@ ibf_dump_object_list(struct ibf_dump *dump, struct ibf_header *header)
for (i=0; i<RARRAY_LEN(dump->obj_list); i++) {
VALUE obj = RARRAY_AREF(dump->obj_list, i);
- ibf_offset_t offset = lbf_dump_object_object(dump, obj);
+ ibf_offset_t offset = ibf_dump_object_object(dump, obj);
rb_ary_push(list, UINT2NUM(offset));
}
size = i;
+ IBF_W_ALIGN(ibf_offset_t);
header->object_list_offset = ibf_dump_pos(dump);
for (i=0; i<size; i++) {
@@ -8333,7 +9906,7 @@ ibf_dump_setup(struct ibf_dump *dump, VALUE dumper_obj)
}
VALUE
-iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt)
+rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt)
{
struct ibf_dump *dump;
struct ibf_header header = {{0}};
@@ -8392,14 +9965,25 @@ ibf_iseq_list(const struct ibf_load *load)
}
void
-ibf_load_iseq_complete(rb_iseq_t *iseq)
+rb_ibf_load_iseq_complete(rb_iseq_t *iseq)
{
struct ibf_load *load = RTYPEDDATA_DATA(iseq->aux.loader.obj);
rb_iseq_t *prev_src_iseq = load->iseq;
+ const ibf_offset_t offset = ibf_iseq_list(load)[iseq->aux.loader.index];
load->iseq = iseq;
- ibf_load_iseq_each(load, iseq, ibf_iseq_list(load)[iseq->aux.loader.index]);
- ISEQ_COMPILE_DATA(iseq) = NULL;
+#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);
+#endif
+ if (offset % sizeof(VALUE)) {
+ rb_raise(rb_eArgError, "unaligned iseq offset: %#x @ %u",
+ offset, iseq->aux.loader.index);
+ }
+ ibf_load_iseq_each(load, iseq, offset);
+ ISEQ_COMPILE_DATA_CLEAR(iseq);
FL_UNSET(iseq, ISEQ_NOT_LOADED_YET);
+ rb_iseq_init_trace(iseq);
load->iseq = prev_src_iseq;
}
@@ -8407,7 +9991,7 @@ ibf_load_iseq_complete(rb_iseq_t *iseq)
const rb_iseq_t *
rb_iseq_complete(const rb_iseq_t *iseq)
{
- ibf_load_iseq_complete((rb_iseq_t *)iseq);
+ rb_ibf_load_iseq_complete((rb_iseq_t *)iseq);
return iseq;
}
#endif
@@ -8417,29 +10001,47 @@ ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq)
{
int iseq_index = (int)(VALUE)index_iseq;
+#if IBF_ISEQ_DEBUG
+ fprintf(stderr, "ibf_load_iseq: index_iseq=%p iseq_list=%p\n",
+ index_iseq, (void *)load->iseq_list);
+#endif
if (iseq_index == -1) {
return NULL;
}
else {
VALUE iseqv = rb_ary_entry(load->iseq_list, iseq_index);
+#if IBF_ISEQ_DEBUG
+ fprintf(stderr, "ibf_load_iseq: iseqv=%p\n", (void *)iseqv);
+#endif
if (iseqv != Qnil) {
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", iseq);
+#endif
FL_SET(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",
+ iseq, (void *)load->loader_obj, iseq_index);
+#endif
rb_ary_store(load->iseq_list, iseq_index, (VALUE)iseq);
#if !USE_LAZY_LOAD
- ibf_load_iseq_complete(iseq);
+#if IBF_ISEQ_DEBUG
+ fprintf(stderr, "ibf_load_iseq: loading iseq=%p\n", iseq);
+#endif
+ rb_ibf_load_iseq_complete(iseq);
#endif /* !USE_LAZY_LOAD */
- if (load->iseq) {
- iseq_add_mark_object(load->iseq, (VALUE)iseq);
- }
+#if IBF_ISEQ_DEBUG
+ fprintf(stderr, "ibf_load_iseq: iseq=%p loaded %p\n",
+ iseq, load->iseq);
+#endif
return iseq;
}
}
@@ -8476,27 +10078,35 @@ ibf_load_setup(struct ibf_load *load, VALUE loader_obj, VALUE str)
if (strcmp(load->buff + sizeof(struct ibf_header), RUBY_PLATFORM) != 0) {
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",
+ load->header->iseq_list_offset);
+ }
+ if (load->header->id_list_offset % RUBY_ALIGNOF(long)) {
+ rb_raise(rb_eArgError, "unaligned ID list offset: %u",
+ load->header->id_list_offset);
+ }
+ if (load->header->object_list_offset % RUBY_ALIGNOF(ibf_offset_t)) {
+ rb_raise(rb_eArgError, "unaligned object list offset: %u",
+ load->header->object_list_offset);
+ }
}
static void
ibf_loader_mark(void *ptr)
{
- if (ptr) {
- struct ibf_load *load = (struct ibf_load *)ptr;
- rb_gc_mark(load->str);
- rb_gc_mark(load->iseq_list);
- rb_gc_mark(load->obj_list);
- }
+ struct ibf_load *load = (struct ibf_load *)ptr;
+ rb_gc_mark(load->str);
+ rb_gc_mark(load->iseq_list);
+ rb_gc_mark(load->obj_list);
}
static void
ibf_loader_free(void *ptr)
{
- if (ptr) {
- struct ibf_load *load = (struct ibf_load *)ptr;
- ruby_xfree(load->id_list);
- ruby_xfree(load);
- }
+ struct ibf_load *load = (struct ibf_load *)ptr;
+ ruby_xfree(load->id_list);
+ ruby_xfree(load);
}
static size_t
@@ -8513,10 +10123,10 @@ static const rb_data_type_t ibf_load_type = {
};
const rb_iseq_t *
-iseq_ibf_load(VALUE str)
+rb_iseq_ibf_load(VALUE str)
{
struct ibf_load *load;
- const rb_iseq_t *iseq;
+ rb_iseq_t *iseq;
VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
ibf_load_setup(load, loader_obj, str);
@@ -8527,7 +10137,7 @@ iseq_ibf_load(VALUE str)
}
VALUE
-iseq_ibf_load_extra_data(VALUE str)
+rb_iseq_ibf_load_extra_data(VALUE str)
{
struct ibf_load *load;
VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
diff --git a/complex.c b/complex.c
index d5abacca27..cc798cccca 100644
--- a/complex.c
+++ b/complex.c
@@ -12,6 +12,7 @@
#endif
#include <math.h>
#include "internal.h"
+#include "id.h"
#define NDEBUG
#include "ruby_assert.h"
@@ -19,7 +20,11 @@
#define ZERO INT2FIX(0)
#define ONE INT2FIX(1)
#define TWO INT2FIX(2)
+#if USE_FLONUM
#define RFLOAT_0 DBL2NUM(0)
+#else
+static VALUE RFLOAT_0;
+#endif
#if defined(HAVE_SIGNBIT) && defined(__GNUC__) && defined(__sun) && \
!defined(signbit)
extern int signbit(double);
@@ -27,16 +32,16 @@ extern int signbit(double);
VALUE rb_cComplex;
-static VALUE nucomp_abs(VALUE self);
-static VALUE nucomp_arg(VALUE self);
-
-static ID id_abs, id_arg, id_convert,
- id_denominator, id_eqeq_p, id_expt, id_fdiv,
- id_negate, id_numerator, id_quo,
- id_real_p, id_to_f, id_to_i, id_to_r,
- id_i_real, id_i_imag,
+static ID id_abs, id_arg,
+ id_denominator, id_fdiv, id_numerator, id_quo,
+ id_real_p, id_i_real, id_i_imag,
id_finite_p, id_infinite_p, id_rationalize,
id_PI;
+#define id_to_i idTo_i
+#define id_to_r idTo_r
+#define id_negate idUMinus
+#define id_expt idPow
+#define id_to_f idTo_f
#define f_boolcast(x) ((x) ? Qtrue : Qfalse)
@@ -61,31 +66,32 @@ f_##n(VALUE x, VALUE y)\
return rb_funcall(x, id_##n, 1, y);\
}
-#define math1(n) \
-inline static VALUE \
-m_##n(VALUE x)\
-{\
- return rb_funcall(rb_mMath, id_##n, 1, x);\
-}
-
-#define math2(n) \
-inline static VALUE \
-m_##n(VALUE x, VALUE y)\
-{\
- return rb_funcall(rb_mMath, id_##n, 2, x, y);\
-}
-
#define PRESERVE_SIGNEDZERO
inline static VALUE
f_add(VALUE x, VALUE y)
{
-#ifndef PRESERVE_SIGNEDZERO
- if (FIXNUM_P(y) && FIXNUM_ZERO_P(y))
- return x;
- else if (FIXNUM_P(x) && FIXNUM_ZERO_P(x))
- return y;
-#endif
+ if (RB_INTEGER_TYPE_P(x) &&
+ LIKELY(rb_method_basic_definition_p(rb_cInteger, idPLUS))) {
+ if (FIXNUM_ZERO_P(x))
+ return y;
+ if (FIXNUM_ZERO_P(y))
+ return x;
+ return rb_int_plus(x, y);
+ }
+ else if (RB_FLOAT_TYPE_P(x) &&
+ LIKELY(rb_method_basic_definition_p(rb_cFloat, idPLUS))) {
+ if (FIXNUM_ZERO_P(y))
+ return x;
+ return rb_float_plus(x, y);
+ }
+ else if (RB_TYPE_P(x, T_RATIONAL) &&
+ LIKELY(rb_method_basic_definition_p(rb_cRational, idPLUS))) {
+ if (FIXNUM_ZERO_P(y))
+ return x;
+ return rb_rational_plus(x, y);
+ }
+
return rb_funcall(x, '+', 1, y);
}
@@ -97,54 +103,84 @@ f_div(VALUE x, VALUE y)
return rb_funcall(x, '/', 1, y);
}
-inline static VALUE
+inline static int
f_gt_p(VALUE x, VALUE y)
{
- if (FIXNUM_P(x) && FIXNUM_P(y))
- return f_boolcast(FIX2LONG(x) > FIX2LONG(y));
- return rb_funcall(x, '>', 1, y);
+ if (RB_INTEGER_TYPE_P(x)) {
+ if (FIXNUM_P(x) && FIXNUM_P(y))
+ return (SIGNED_VALUE)x > (SIGNED_VALUE)y;
+ return RTEST(rb_int_gt(x, y));
+ }
+ else if (RB_FLOAT_TYPE_P(x))
+ return RTEST(rb_float_gt(x, y));
+ else if (RB_TYPE_P(x, T_RATIONAL)) {
+ int const cmp = rb_cmpint(rb_rational_cmp(x, y), x, y);
+ return cmp > 0;
+ }
+ return RTEST(rb_funcall(x, '>', 1, y));
}
inline static VALUE
f_mul(VALUE x, VALUE y)
{
-#ifndef PRESERVE_SIGNEDZERO
- if (FIXNUM_P(y)) {
- long iy = FIX2LONG(y);
- if (iy == 0) {
- if (RB_INTEGER_TYPE_P(x))
- return ZERO;
- }
- else if (iy == 1)
- return x;
- }
- else if (FIXNUM_P(x)) {
- long ix = FIX2LONG(x);
- if (ix == 0) {
- if (RB_INTEGER_TYPE_P(y))
- return ZERO;
- }
- else if (ix == 1)
- return y;
+ if (RB_INTEGER_TYPE_P(x) &&
+ LIKELY(rb_method_basic_definition_p(rb_cInteger, idMULT))) {
+ if (FIXNUM_ZERO_P(y))
+ return ZERO;
+ if (FIXNUM_ZERO_P(x) && RB_INTEGER_TYPE_P(y))
+ return ZERO;
+ if (x == ONE) return y;
+ if (y == ONE) return x;
+ return rb_int_mul(x, y);
+ }
+ else if (RB_FLOAT_TYPE_P(x) &&
+ LIKELY(rb_method_basic_definition_p(rb_cFloat, idMULT))) {
+ if (y == ONE) return x;
+ return rb_float_mul(x, y);
+ }
+ else if (RB_TYPE_P(x, T_RATIONAL) &&
+ LIKELY(rb_method_basic_definition_p(rb_cRational, idMULT))) {
+ if (y == ONE) return x;
+ return rb_rational_mul(x, y);
+ }
+ else if (LIKELY(rb_method_basic_definition_p(CLASS_OF(x), idMULT))) {
+ if (y == ONE) return x;
}
-#endif
return rb_funcall(x, '*', 1, y);
}
inline static VALUE
f_sub(VALUE x, VALUE y)
{
-#ifndef PRESERVE_SIGNEDZERO
- if (FIXNUM_P(y) && FIXNUM_ZERO_P(y))
+ if (FIXNUM_ZERO_P(y) &&
+ LIKELY(rb_method_basic_definition_p(CLASS_OF(x), idMINUS))) {
return x;
-#endif
+ }
return rb_funcall(x, '-', 1, y);
}
fun1(abs)
fun1(arg)
fun1(denominator)
-fun1(negate)
+
+inline static VALUE
+f_negate(VALUE x)
+{
+ if (RB_INTEGER_TYPE_P(x)) {
+ return rb_int_uminus(x);
+ }
+ else if (RB_FLOAT_TYPE_P(x)) {
+ return rb_float_uminus(x);
+ }
+ else if (RB_TYPE_P(x, T_RATIONAL)) {
+ return rb_rational_uminus(x);
+ }
+ else if (RB_TYPE_P(x, T_COMPLEX)) {
+ return rb_complex_uminus(x);
+ }
+ return rb_funcall(x, id_negate, 0);
+}
+
fun1(numerator)
fun1(real_p)
@@ -165,97 +201,94 @@ f_to_f(VALUE x)
fun1(to_r)
-inline static VALUE
+inline static int
f_eqeq_p(VALUE x, VALUE y)
{
if (FIXNUM_P(x) && FIXNUM_P(y))
- return f_boolcast(x == y);
- return rb_funcall(x, id_eqeq_p, 1, y);
+ return x == y;
+ else if (RB_FLOAT_TYPE_P(x) || RB_FLOAT_TYPE_P(y))
+ return NUM2DBL(x) == NUM2DBL(y);
+ return (int)rb_equal(x, y);
}
fun2(expt)
fun2(fdiv)
fun2(quo)
-inline static VALUE
+inline static int
f_negative_p(VALUE x)
{
- if (FIXNUM_P(x))
- return f_boolcast(FIXNUM_NEGATIVE_P(x));
- return rb_funcall(x, '<', 1, ZERO);
+ if (RB_INTEGER_TYPE_P(x))
+ return INT_NEGATIVE_P(x);
+ else if (RB_FLOAT_TYPE_P(x))
+ return RFLOAT_VALUE(x) < 0.0;
+ else if (RB_TYPE_P(x, T_RATIONAL))
+ return INT_NEGATIVE_P(RRATIONAL(x)->num);
+ return rb_num_negative_p(x);
}
#define f_positive_p(x) (!f_negative_p(x))
-inline static VALUE
+inline static int
f_zero_p(VALUE x)
{
- if (FIXNUM_P(x)) {
- return f_boolcast(FIXNUM_ZERO_P(x));
- }
- else if (RB_TYPE_P(x, T_BIGNUM)) {
- return Qfalse;
+ if (RB_INTEGER_TYPE_P(x)) {
+ return FIXNUM_ZERO_P(x);
}
else if (RB_TYPE_P(x, T_RATIONAL)) {
- VALUE num = RRATIONAL(x)->num;
-
- return f_boolcast(FIXNUM_P(num) && FIXNUM_ZERO_P(num));
+ const VALUE num = RRATIONAL(x)->num;
+ return FIXNUM_ZERO_P(num);
}
- return rb_funcall(x, id_eqeq_p, 1, ZERO);
+ return (int)rb_equal(x, ZERO);
}
#define f_nonzero_p(x) (!f_zero_p(x))
-inline static VALUE
-f_one_p(VALUE x)
+VALUE rb_flo_is_finite_p(VALUE num);
+inline static int
+f_finite_p(VALUE x)
{
- if (FIXNUM_P(x)) {
- return f_boolcast(FIX2LONG(x) == 1);
+ if (RB_INTEGER_TYPE_P(x)) {
+ return TRUE;
}
- else if (RB_TYPE_P(x, T_BIGNUM)) {
- return Qfalse;
+ else if (RB_FLOAT_TYPE_P(x)) {
+ return (int)rb_flo_is_finite_p(x);
}
else if (RB_TYPE_P(x, T_RATIONAL)) {
- VALUE num = RRATIONAL(x)->num;
- VALUE den = RRATIONAL(x)->den;
-
- return f_boolcast(FIXNUM_P(num) && FIX2LONG(num) == 1 &&
- FIXNUM_P(den) && FIX2LONG(den) == 1);
+ return TRUE;
}
- return rb_funcall(x, id_eqeq_p, 1, ONE);
+ return RTEST(rb_funcallv(x, id_finite_p, 0, 0));
}
+VALUE rb_flo_is_infinite_p(VALUE num);
inline static VALUE
-f_kind_of_p(VALUE x, VALUE c)
+f_infinite_p(VALUE x)
{
- return rb_obj_is_kind_of(x, c);
-}
-
-inline static VALUE
-k_numeric_p(VALUE x)
-{
- return f_kind_of_p(x, rb_cNumeric);
-}
-
-inline static VALUE
-k_float_p(VALUE x)
-{
- return f_kind_of_p(x, rb_cFloat);
+ if (RB_INTEGER_TYPE_P(x)) {
+ return Qnil;
+ }
+ else if (RB_FLOAT_TYPE_P(x)) {
+ return rb_flo_is_infinite_p(x);
+ }
+ else if (RB_TYPE_P(x, T_RATIONAL)) {
+ return Qnil;
+ }
+ return rb_funcallv(x, id_infinite_p, 0, 0);
}
-inline static VALUE
-k_rational_p(VALUE x)
+inline static int
+f_kind_of_p(VALUE x, VALUE c)
{
- return f_kind_of_p(x, rb_cRational);
+ return (int)rb_obj_is_kind_of(x, c);
}
-inline static VALUE
-k_complex_p(VALUE x)
+inline static int
+k_numeric_p(VALUE x)
{
- return f_kind_of_p(x, rb_cComplex);
+ return f_kind_of_p(x, rb_cNumeric);
}
-#define k_exact_p(x) (!k_float_p(x))
+#define k_exact_p(x) (!RB_FLOAT_TYPE_P(x))
#define k_exact_zero_p(x) (k_exact_p(x) && f_zero_p(x))
@@ -272,6 +305,7 @@ nucomp_s_new_internal(VALUE klass, VALUE real, VALUE imag)
RCOMPLEX_SET_REAL(obj, real);
RCOMPLEX_SET_IMAG(obj, imag);
+ OBJ_FREEZE_RAW(obj);
return (VALUE)obj;
}
@@ -282,50 +316,22 @@ nucomp_s_alloc(VALUE klass)
return nucomp_s_new_internal(klass, ZERO, ZERO);
}
-#if 0
-static VALUE
-nucomp_s_new_bang(int argc, VALUE *argv, VALUE klass)
-{
- VALUE real, imag;
-
- switch (rb_scan_args(argc, argv, "11", &real, &imag)) {
- case 1:
- if (!k_numeric_p(real))
- real = f_to_i(real);
- imag = ZERO;
- break;
- default:
- if (!k_numeric_p(real))
- real = f_to_i(real);
- if (!k_numeric_p(imag))
- imag = f_to_i(imag);
- break;
- }
-
- return nucomp_s_new_internal(klass, real, imag);
-}
-#endif
-
inline static VALUE
f_complex_new_bang1(VALUE klass, VALUE x)
{
- assert(!k_complex_p(x));
+ assert(!RB_TYPE_P(x, T_COMPLEX));
return nucomp_s_new_internal(klass, x, ZERO);
}
inline static VALUE
f_complex_new_bang2(VALUE klass, VALUE x, VALUE y)
{
- assert(!k_complex_p(x));
- assert(!k_complex_p(y));
+ assert(!RB_TYPE_P(x, T_COMPLEX));
+ assert(!RB_TYPE_P(y, T_COMPLEX));
return nucomp_s_new_internal(klass, x, y);
}
#ifdef CANONICALIZATION_FOR_MATHN
-#define CANON
-#endif
-
-#ifdef CANON
static int canonicalization = 0;
RUBY_FUNC_EXPORTED void
@@ -351,26 +357,24 @@ nucomp_real_check(VALUE num)
inline static VALUE
nucomp_s_canonicalize_internal(VALUE klass, VALUE real, VALUE imag)
{
-#ifdef CANON
-#define CL_CANON
-#ifdef CL_CANON
+ int complex_r, complex_i;
+#ifdef CANONICALIZATION_FOR_MATHN
if (k_exact_zero_p(imag) && canonicalization)
return real;
-#else
- if (f_zero_p(imag) && canonicalization)
- return real;
#endif
-#endif
- if (f_real_p(real) && f_real_p(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);
- else if (f_real_p(real)) {
+ }
+ else if (!complex_r) {
get_dat1(imag);
return nucomp_s_new_internal(klass,
f_sub(real, dat->imag),
f_add(ZERO, dat->real));
}
- else if (f_real_p(imag)) {
+ else if (!complex_i) {
get_dat1(real);
return nucomp_s_new_internal(klass,
@@ -417,13 +421,16 @@ nucomp_s_new(int argc, VALUE *argv, VALUE klass)
inline static VALUE
f_complex_new2(VALUE klass, VALUE x, VALUE y)
{
- assert(!k_complex_p(x));
+ assert(!RB_TYPE_P(x, T_COMPLEX));
return nucomp_s_canonicalize_internal(klass, x, y);
}
+static VALUE nucomp_convert(VALUE klass, VALUE a1, VALUE a2, int raise);
+static VALUE nucomp_s_convert(int argc, VALUE *argv, VALUE klass);
+
/*
* call-seq:
- * Complex(x[, y]) -> numeric
+ * Complex(x[, y], exception: false) -> numeric
*
* Returns x+i*y;
*
@@ -432,6 +439,9 @@ f_complex_new2(VALUE klass, VALUE x, VALUE y)
* Complex(nil) #=> TypeError
* Complex(1, nil) #=> TypeError
*
+ * Complex(1, nil, exception: false) #=> nil
+ * Complex('1+2', exception: false) #=> nil
+ *
* Syntax of string form:
*
* string form = extra spaces , complex , extra spaces ;
@@ -457,7 +467,22 @@ f_complex_new2(VALUE klass, VALUE x, VALUE y)
static VALUE
nucomp_f_complex(int argc, VALUE *argv, VALUE klass)
{
- return rb_funcallv(rb_cComplex, id_convert, argc, argv);
+ VALUE a1, a2, opts = Qnil;
+ int raise = TRUE;
+
+ if (rb_scan_args(argc, argv, "11:", &a1, &a2, &opts) == 1) {
+ a2 = Qundef;
+ }
+ if (!NIL_P(opts)) {
+ static ID kwds[1];
+ VALUE exception;
+ if (!kwds[0]) {
+ kwds[0] = idException;
+ }
+ rb_get_kwargs(opts, kwds, 0, 1, &exception);
+ raise = (exception != Qfalse);
+ }
+ return nucomp_convert(rb_cComplex, a1, a2, raise);
}
#define imp1(n) \
@@ -467,20 +492,9 @@ m_##n##_bang(VALUE x)\
return rb_math_##n(x);\
}
-#define imp2(n) \
-inline static VALUE \
-m_##n##_bang(VALUE x, VALUE y)\
-{\
- return rb_math_##n(x, y);\
-}
-
-imp2(atan2)
imp1(cos)
imp1(cosh)
imp1(exp)
-imp2(hypot)
-
-#define m_hypot(x,y) m_hypot_bang((x),(y))
static VALUE
m_log_bang(VALUE x)
@@ -494,7 +508,7 @@ imp1(sinh)
static VALUE
m_cos(VALUE x)
{
- if (f_real_p(x))
+ if (!RB_TYPE_P(x, T_COMPLEX))
return m_cos_bang(x);
{
get_dat1(x);
@@ -509,7 +523,7 @@ m_cos(VALUE x)
static VALUE
m_sin(VALUE x)
{
- if (f_real_p(x))
+ if (!RB_TYPE_P(x, T_COMPLEX))
return m_sin_bang(x);
{
get_dat1(x);
@@ -521,41 +535,11 @@ m_sin(VALUE x)
}
}
-#if 0
-imp1(sqrt)
-
-VALUE
-rb_complex_sqrt(VALUE x)
-{
- int pos;
- VALUE a, re, im;
- get_dat1(x);
-
- pos = f_positive_p(dat->imag);
- a = f_abs(x);
- re = m_sqrt_bang(f_div(f_add(a, dat->real), TWO));
- im = m_sqrt_bang(f_div(f_sub(a, dat->real), TWO));
- if (!pos) im = f_negate(im);
- return f_complex_new2(rb_cComplex, re, im);
-}
-
-static VALUE
-m_sqrt(VALUE x)
-{
- if (f_real_p(x)) {
- if (f_positive_p(x))
- return m_sqrt_bang(x);
- return f_complex_new2(rb_cComplex, ZERO, m_sqrt_bang(f_negate(x)));
- }
- return rb_complex_sqrt(x);
-}
-#endif
-
static VALUE
f_complex_polar(VALUE klass, VALUE x, VALUE y)
{
- assert(!k_complex_p(x));
- assert(!k_complex_p(y));
+ assert(!RB_TYPE_P(x, T_COMPLEX));
+ assert(!RB_TYPE_P(y, T_COMPLEX));
if (f_zero_p(x) || f_zero_p(y)) {
if (canonicalization) return x;
return nucomp_s_new_internal(klass, x, RFLOAT_0);
@@ -594,6 +578,28 @@ f_complex_polar(VALUE klass, VALUE x, VALUE y)
f_mul(x, m_sin(y)));
}
+/* returns a Complex or Float of ang*PI-rotated abs */
+VALUE
+rb_dbl_complex_new_polar_pi(double abs, double ang)
+{
+ double fi;
+ const double fr = modf(ang, &fi);
+ 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));
+ }
+ else if (fr == 0.0) {
+ if (modf(fi / 2.0, &fi) != 0.0) abs = -abs;
+ return DBL2NUM(abs);
+ }
+ else {
+ ang *= M_PI;
+ return rb_complex_new(DBL2NUM(abs * cos(ang)), DBL2NUM(abs * sin(ang)));
+ }
+}
+
/*
* call-seq:
* Complex.polar(abs[, arg]) -> complex
@@ -632,8 +638,8 @@ nucomp_s_polar(int argc, VALUE *argv, VALUE klass)
* Complex(7).real #=> 7
* Complex(9, -4).real #=> 9
*/
-static VALUE
-nucomp_real(VALUE self)
+VALUE
+rb_complex_real(VALUE self)
{
get_dat1(self);
return dat->real;
@@ -649,8 +655,8 @@ nucomp_real(VALUE self)
* Complex(7).imaginary #=> 0
* Complex(9, -4).imaginary #=> -4
*/
-static VALUE
-nucomp_imag(VALUE self)
+VALUE
+rb_complex_imag(VALUE self)
{
get_dat1(self);
return dat->imag;
@@ -664,35 +670,12 @@ nucomp_imag(VALUE self)
*
* -Complex(1, 2) #=> (-1-2i)
*/
-static VALUE
-nucomp_negate(VALUE self)
-{
- get_dat1(self);
- return f_complex_new2(CLASS_OF(self),
- f_negate(dat->real), f_negate(dat->imag));
-}
-
-inline static VALUE
-f_addsub(VALUE self, VALUE other,
- VALUE (*func)(VALUE, VALUE), ID id)
+VALUE
+rb_complex_uminus(VALUE self)
{
- if (k_complex_p(other)) {
- VALUE real, imag;
-
- get_dat2(self, other);
-
- real = (*func)(adat->real, bdat->real);
- imag = (*func)(adat->imag, bdat->imag);
-
- return f_complex_new2(CLASS_OF(self), real, imag);
- }
- if (k_numeric_p(other) && f_real_p(other)) {
- get_dat1(self);
-
- return f_complex_new2(CLASS_OF(self),
- (*func)(dat->real, other), dat->imag);
- }
- return rb_num_coerce_bin(self, other, id);
+ get_dat1(self);
+ return f_complex_new2(CLASS_OF(self),
+ f_negate(dat->real), f_negate(dat->imag));
}
/*
@@ -710,9 +693,24 @@ f_addsub(VALUE self, VALUE other,
VALUE
rb_complex_plus(VALUE self, VALUE other)
{
- return f_addsub(self, other, f_add, '+');
+ if (RB_TYPE_P(other, T_COMPLEX)) {
+ VALUE real, imag;
+
+ get_dat2(self, other);
+
+ real = f_add(adat->real, bdat->real);
+ imag = f_add(adat->imag, bdat->imag);
+
+ return f_complex_new2(CLASS_OF(self), real, imag);
+ }
+ if (k_numeric_p(other) && f_real_p(other)) {
+ get_dat1(self);
+
+ return f_complex_new2(CLASS_OF(self),
+ f_add(dat->real, other), dat->imag);
+ }
+ return rb_num_coerce_bin(self, other, '+');
}
-#define nucomp_add rb_complex_plus
/*
* call-seq:
@@ -726,10 +724,26 @@ rb_complex_plus(VALUE self, VALUE other)
* Complex(9, 8) - 4 #=> (5+8i)
* Complex(20, 9) - 9.8 #=> (10.2+9i)
*/
-static VALUE
-nucomp_sub(VALUE self, VALUE other)
+VALUE
+rb_complex_minus(VALUE self, VALUE other)
{
- return f_addsub(self, other, f_sub, '-');
+ if (RB_TYPE_P(other, T_COMPLEX)) {
+ VALUE real, imag;
+
+ get_dat2(self, other);
+
+ real = f_sub(adat->real, bdat->real);
+ imag = f_sub(adat->imag, bdat->imag);
+
+ return f_complex_new2(CLASS_OF(self), real, imag);
+ }
+ if (k_numeric_p(other) && f_real_p(other)) {
+ get_dat1(self);
+
+ return f_complex_new2(CLASS_OF(self),
+ f_sub(dat->real, other), dat->imag);
+ }
+ return rb_num_coerce_bin(self, other, '-');
}
static VALUE
@@ -745,6 +759,19 @@ safe_mul(VALUE a, VALUE b, int az, int bz)
return f_mul(a, b);
}
+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);
+ *real = f_sub(safe_mul(areal, breal, arzero, brzero),
+ safe_mul(aimag, bimag, aizero, bizero));
+ *imag = f_add(safe_mul(areal, bimag, arzero, bizero),
+ safe_mul(aimag, breal, aizero, brzero));
+}
+
/*
* call-seq:
* cmp * numeric -> complex
@@ -760,21 +787,11 @@ safe_mul(VALUE a, VALUE b, int az, int bz)
VALUE
rb_complex_mul(VALUE self, VALUE other)
{
- if (k_complex_p(other)) {
+ if (RB_TYPE_P(other, T_COMPLEX)) {
VALUE real, imag;
- VALUE areal, aimag, breal, bimag;
- int arzero, aizero, brzero, bizero;
-
get_dat2(self, other);
- arzero = !!f_zero_p(areal = adat->real);
- aizero = !!f_zero_p(aimag = adat->imag);
- brzero = !!f_zero_p(breal = bdat->real);
- bizero = !!f_zero_p(bimag = bdat->imag);
- real = f_sub(safe_mul(areal, breal, arzero, brzero),
- safe_mul(aimag, bimag, aizero, bizero));
- imag = f_add(safe_mul(areal, bimag, arzero, bizero),
- safe_mul(aimag, breal, aizero, brzero));
+ comp_mul(adat->real, adat->imag, bdat->real, bdat->imag, &real, &imag);
return f_complex_new2(CLASS_OF(self), real, imag);
}
@@ -787,49 +804,42 @@ rb_complex_mul(VALUE self, VALUE other)
}
return rb_num_coerce_bin(self, other, '*');
}
-#define nucomp_mul rb_complex_mul
inline static VALUE
f_divide(VALUE self, VALUE other,
VALUE (*func)(VALUE, VALUE), ID id)
{
- if (k_complex_p(other)) {
+ if (RB_TYPE_P(other, T_COMPLEX)) {
+ VALUE r, n, x, y;
int flo;
get_dat2(self, other);
- flo = (k_float_p(adat->real) || k_float_p(adat->imag) ||
- k_float_p(bdat->real) || k_float_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))) {
- VALUE r, n;
-
r = (*func)(bdat->imag, bdat->real);
n = f_mul(bdat->real, f_add(ONE, f_mul(r, r)));
if (flo)
return f_complex_new2(CLASS_OF(self),
(*func)(self, n),
(*func)(f_negate(f_mul(self, r)), n));
- return f_complex_new2(CLASS_OF(self),
- (*func)(f_add(adat->real,
- f_mul(adat->imag, r)), n),
- (*func)(f_sub(adat->imag,
- f_mul(adat->real, r)), n));
+ 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 {
- VALUE r, n;
-
r = (*func)(bdat->real, bdat->imag);
n = f_mul(bdat->imag, f_add(ONE, f_mul(r, r)));
if (flo)
return f_complex_new2(CLASS_OF(self),
(*func)(f_mul(self, r), n),
(*func)(f_negate(self), n));
- return f_complex_new2(CLASS_OF(self),
- (*func)(f_add(f_mul(adat->real, r),
- adat->imag), n),
- (*func)(f_sub(f_mul(adat->imag, r),
- adat->real), n));
+ x = (*func)(f_add(f_mul(adat->real, r), adat->imag), n);
+ y = (*func)(f_sub(f_mul(adat->imag, r), adat->real), n);
}
+ x = rb_rational_canonicalize(x);
+ y = rb_rational_canonicalize(y);
+ return f_complex_new2(CLASS_OF(self), x, y);
}
if (k_numeric_p(other) && f_real_p(other)) {
get_dat1(self);
@@ -856,13 +866,13 @@ f_divide(VALUE self, VALUE other,
* Complex(9, 8) / 4 #=> ((9/4)+(2/1)*i)
* Complex(20, 9) / 9.8 #=> (2.0408163265306123+0.9183673469387754i)
*/
-static VALUE
-nucomp_div(VALUE self, VALUE other)
+VALUE
+rb_complex_div(VALUE self, VALUE other)
{
return f_divide(self, other, f_quo, id_quo);
}
-#define nucomp_quo nucomp_div
+#define nucomp_quo rb_complex_div
/*
* call-seq:
@@ -893,23 +903,23 @@ f_reciprocal(VALUE x)
* Complex('i') ** 2 #=> (-1+0i)
* Complex(-8) ** Rational(1, 3) #=> (1.0000000000000002+1.7320508075688772i)
*/
-static VALUE
-nucomp_expt(VALUE self, VALUE other)
+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);
- if (k_rational_p(other) && f_one_p(f_denominator(other)))
- other = f_numerator(other); /* c14n */
+ if (RB_TYPE_P(other, T_RATIONAL) && RRATIONAL(other)->den == LONG2FIX(1))
+ other = RRATIONAL(other)->num; /* c14n */
- if (k_complex_p(other)) {
+ if (RB_TYPE_P(other, T_COMPLEX)) {
get_dat1(other);
if (k_exact_zero_p(dat->imag))
other = dat->real; /* c14n */
}
- if (k_complex_p(other)) {
+ if (RB_TYPE_P(other, T_COMPLEX)) {
VALUE r, theta, nr, ntheta;
get_dat1(other);
@@ -924,38 +934,45 @@ nucomp_expt(VALUE self, VALUE other)
return f_complex_polar(CLASS_OF(self), nr, ntheta);
}
if (FIXNUM_P(other)) {
- if (f_gt_p(other, ZERO)) {
- VALUE x, z;
- long n;
-
- x = self;
- z = x;
- n = FIX2LONG(other) - 1;
-
- while (n) {
- long q, r;
-
- while (1) {
- get_dat1(x);
-
- q = n / 2;
- r = n % 2;
-
- if (r)
- break;
-
- x = nucomp_s_new_internal(CLASS_OF(self),
- f_sub(f_mul(dat->real, dat->real),
- f_mul(dat->imag, dat->imag)),
- f_mul(f_mul(TWO, dat->real), dat->imag));
- n = q;
- }
- z = f_mul(z, x);
- n--;
- }
- return z;
+ long n = FIX2LONG(other);
+ if (n == 0) {
+ return nucomp_s_new_internal(CLASS_OF(self), ONE, ZERO);
+ }
+ if (n < 0) {
+ self = f_reciprocal(self);
+ other = rb_int_uminus(other);
+ n = -n;
+ }
+ {
+ get_dat1(self);
+ VALUE xr = dat->real, xi = dat->imag, zr = xr, zi = xi;
+
+ if (f_zero_p(xi)) {
+ zr = rb_num_pow(zr, other);
+ }
+ else if (f_zero_p(xr)) {
+ zi = rb_num_pow(zi, other);
+ if (n & 2) zi = f_negate(zi);
+ if (!(n & 1)) {
+ VALUE tmp = zr;
+ zr = zi;
+ zi = tmp;
+ }
+ }
+ else {
+ while (--n) {
+ long q, r;
+
+ for (; q = n / 2, r = n % 2, r == 0; n = q) {
+ VALUE tmp = f_sub(f_mul(xr, xr), f_mul(xi, xi));
+ xi = f_mul(f_mul(TWO, xr), xi);
+ xr = tmp;
+ }
+ comp_mul(zr, zi, xr, xi, &zr, &zi);
+ }
+ }
+ return nucomp_s_new_internal(CLASS_OF(self), zr, zi);
}
- return f_expt(f_reciprocal(self), f_negate(other));
}
if (k_numeric_p(other) && f_real_p(other)) {
VALUE r, theta;
@@ -987,7 +1004,7 @@ nucomp_expt(VALUE self, VALUE other)
static VALUE
nucomp_eqeq_p(VALUE self, VALUE other)
{
- if (k_complex_p(other)) {
+ if (RB_TYPE_P(other, T_COMPLEX)) {
get_dat2(self, other);
return f_boolcast(f_eqeq_p(adat->real, bdat->real) &&
@@ -998,7 +1015,7 @@ nucomp_eqeq_p(VALUE self, VALUE other)
return f_boolcast(f_eqeq_p(dat->real, other) && f_zero_p(dat->imag));
}
- return f_eqeq_p(other, self);
+ return f_boolcast(f_eqeq_p(other, self));
}
/* :nodoc: */
@@ -1025,24 +1042,24 @@ nucomp_coerce(VALUE self, VALUE other)
* Complex(-1).abs #=> 1
* Complex(3.0, -4.0).abs #=> 5.0
*/
-static VALUE
-nucomp_abs(VALUE self)
+VALUE
+rb_complex_abs(VALUE self)
{
get_dat1(self);
if (f_zero_p(dat->real)) {
VALUE a = f_abs(dat->imag);
- if (k_float_p(dat->real) && !k_float_p(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 (!k_float_p(dat->real) && k_float_p(dat->imag))
+ if (!RB_FLOAT_TYPE_P(dat->real) && RB_FLOAT_TYPE_P(dat->imag))
a = f_to_f(a);
return a;
}
- return m_hypot(dat->real, dat->imag);
+ return rb_math_hypot(dat->real, dat->imag);
}
/*
@@ -1072,11 +1089,11 @@ nucomp_abs2(VALUE self)
*
* Complex.polar(3, Math::PI/2).arg #=> 1.5707963267948966
*/
-static VALUE
-nucomp_arg(VALUE self)
+VALUE
+rb_complex_arg(VALUE self)
{
get_dat1(self);
- return m_atan2_bang(dat->imag, dat->real);
+ return rb_math_atan2(dat->imag, dat->real);
}
/*
@@ -1118,22 +1135,13 @@ nucomp_polar(VALUE self)
*
* Complex(1, 2).conjugate #=> (1-2i)
*/
-static VALUE
-nucomp_conj(VALUE self)
+VALUE
+rb_complex_conjugate(VALUE self)
{
get_dat1(self);
return f_complex_new2(CLASS_OF(self), dat->real, f_negate(dat->imag));
}
-#if 0
-/* :nodoc: */
-static VALUE
-nucomp_true(VALUE self)
-{
- return Qtrue;
-}
-#endif
-
/*
* call-seq:
* cmp.real? -> false
@@ -1146,23 +1154,6 @@ nucomp_false(VALUE self)
return Qfalse;
}
-#if 0
-/* :nodoc: */
-static VALUE
-nucomp_exact_p(VALUE self)
-{
- get_dat1(self);
- return f_boolcast(k_exact_p(dat->real) && k_exact_p(dat->imag));
-}
-
-/* :nodoc: */
-static VALUE
-nucomp_inexact_p(VALUE self)
-{
- return f_boolcast(!nucomp_exact_p(self));
-}
-#endif
-
/*
* call-seq:
* cmp.denominator -> integer
@@ -1224,14 +1215,14 @@ nucomp_hash(VALUE self)
n = rb_hash(dat->imag);
h[1] = NUM2LONG(n);
v = rb_memhash(h, sizeof(h));
- return LONG2FIX(v);
+ return ST2FIX(v);
}
/* :nodoc: */
static VALUE
nucomp_eql_p(VALUE self, VALUE other)
{
- if (k_complex_p(other)) {
+ if (RB_TYPE_P(other, T_COMPLEX)) {
get_dat2(self, other);
return f_boolcast((CLASS_OF(adat->real) == CLASS_OF(bdat->real)) &&
@@ -1242,26 +1233,27 @@ nucomp_eql_p(VALUE self, VALUE other)
return Qfalse;
}
-inline static VALUE
+inline static int
f_signbit(VALUE x)
{
if (RB_FLOAT_TYPE_P(x)) {
double f = RFLOAT_VALUE(x);
- return f_boolcast(!isnan(f) && signbit(f));
+ return !isnan(f) && signbit(f);
}
return f_negative_p(x);
}
-inline static VALUE
+inline static int
f_tpositive_p(VALUE x)
{
- return f_boolcast(!f_signbit(x));
+ return !f_signbit(x);
}
static VALUE
f_format(VALUE self, VALUE (*func)(VALUE))
{
- VALUE s, impos;
+ VALUE s;
+ int impos;
get_dat1(self);
@@ -1326,34 +1318,26 @@ nucomp_inspect(VALUE self)
* call-seq:
* cmp.finite? -> true or false
*
- * Returns +true+ if +cmp+'s magnitude is finite number,
- * oterwise returns +false+.
+ * Returns +true+ if +cmp+'s real and imaginary parts are both finite numbers,
+ * otherwise returns +false+.
*/
static VALUE
rb_complex_finite_p(VALUE self)
{
- VALUE magnitude = nucomp_abs(self);
+ get_dat1(self);
- if (FINITE_TYPE_P(magnitude)) {
+ if (f_finite_p(dat->real) && f_finite_p(dat->imag)) {
return Qtrue;
}
- else if (RB_FLOAT_TYPE_P(magnitude)) {
- const double f = RFLOAT_VALUE(magnitude);
- return isinf(f) ? Qfalse : Qtrue;
- }
- else {
- return rb_funcall(magnitude, id_finite_p, 0);
- }
+ return Qfalse;
}
/*
* call-seq:
- * cmp.infinite? -> nil or 1 or -1
+ * cmp.infinite? -> nil or 1
*
- * Returns values corresponding to the value of +cmp+'s magnitude:
- *
- * +finite+:: +nil+
- * ++Infinity+:: ++1+
+ * Returns +1+ if +cmp+'s real or imaginary part is an infinite number,
+ * otherwise returns +nil+.
*
* For example:
*
@@ -1363,21 +1347,12 @@ rb_complex_finite_p(VALUE self)
static VALUE
rb_complex_infinite_p(VALUE self)
{
- VALUE magnitude = nucomp_abs(self);
+ get_dat1(self);
- if (FINITE_TYPE_P(magnitude)) {
+ if (NIL_P(f_infinite_p(dat->real)) && NIL_P(f_infinite_p(dat->imag))) {
return Qnil;
}
- if (RB_FLOAT_TYPE_P(magnitude)) {
- const double f = RFLOAT_VALUE(magnitude);
- if (isinf(f)) {
- return INT2FIX(f < 0 ? -1 : 1);
- }
- return Qnil;
- }
- else {
- return rb_funcall(magnitude, id_infinite_p, 0);
- }
+ return ONE;
}
/* :nodoc: */
@@ -1395,6 +1370,7 @@ nucomp_loader(VALUE self, VALUE a)
RCOMPLEX_SET_REAL(dat, rb_ivar_get(a, id_i_real));
RCOMPLEX_SET_IMAG(dat, rb_ivar_get(a, id_i_imag));
+ OBJ_FREEZE_RAW(self);
return self;
}
@@ -1438,12 +1414,16 @@ rb_complex_new(VALUE x, VALUE y)
}
VALUE
-rb_complex_polar(VALUE x, VALUE y)
+rb_complex_new_polar(VALUE x, VALUE y)
{
return f_complex_polar(rb_cComplex, x, y);
}
-static VALUE nucomp_s_convert(int argc, VALUE *argv, VALUE klass);
+VALUE
+rb_complex_polar(VALUE x, VALUE y)
+{
+ return rb_complex_new_polar(x, y);
+}
VALUE
rb_Complex(VALUE x, VALUE y)
@@ -1454,24 +1434,17 @@ rb_Complex(VALUE x, VALUE y)
return nucomp_s_convert(2, a, rb_cComplex);
}
+/*!
+ * Creates a Complex object.
+ *
+ * \param real real part value
+ * \param imag imaginary part value
+ * \return a new Complex object
+ */
VALUE
-rb_complex_set_real(VALUE cmp, VALUE r)
-{
- RCOMPLEX_SET_REAL(cmp, r);
- return cmp;
-}
-
-VALUE
-rb_complex_set_imag(VALUE cmp, VALUE i)
-{
- RCOMPLEX_SET_IMAG(cmp, i);
- return cmp;
-}
-
-VALUE
-rb_complex_abs(VALUE cmp)
+rb_dbl_complex_new(double real, double imag)
{
- return nucomp_abs(cmp);
+ return rb_complex_raw(DBL2NUM(real), DBL2NUM(imag));
}
/*
@@ -1563,7 +1536,7 @@ nucomp_rationalize(int argc, VALUE *argv, VALUE self)
{
get_dat1(self);
- rb_scan_args(argc, argv, "01", NULL);
+ rb_check_arity(argc, 0, 1);
if (!k_exact_zero_p(dat->imag)) {
rb_raise(rb_eRangeError, "can't convert %"PRIsVALUE" into Rational",
@@ -1809,7 +1782,7 @@ read_comp(const char **s, int strict,
return 0; /* e.g. "1@-" */
}
num2 = str2num(bb);
- *ret = rb_complex_polar(num, num2);
+ *ret = rb_complex_new_polar(num, num2);
if (!st)
return 0; /* e.g. "1@2." */
else
@@ -1852,8 +1825,7 @@ skip_ws(const char **s)
}
static int
-parse_comp(const char *s, int strict,
- VALUE *num)
+parse_comp(const char *s, int strict, VALUE *num)
{
char *buf, *b;
VALUE tmp;
@@ -1864,14 +1836,14 @@ parse_comp(const char *s, int strict,
skip_ws(&s);
if (!read_comp(&s, strict, num, &b)) {
- ret = 0;
+ ret = 0;
}
else {
- skip_ws(&s);
+ skip_ws(&s);
- if (strict)
- if (*s != '\0')
- ret = 0;
+ if (strict)
+ if (*s != '\0')
+ ret = 0;
}
ALLOCV_END(tmp);
@@ -1879,7 +1851,7 @@ parse_comp(const char *s, int strict,
}
static VALUE
-string_to_c_strict(VALUE self)
+string_to_c_strict(VALUE self, int raise)
{
char *s;
VALUE num;
@@ -1888,8 +1860,10 @@ string_to_c_strict(VALUE self)
s = RSTRING_PTR(self);
- if (!s || memchr(s, '\0', RSTRING_LEN(self)))
+ if (!s || memchr(s, '\0', RSTRING_LEN(self))) {
+ if (!raise) return Qnil;
rb_raise(rb_eArgError, "string contains null byte");
+ }
if (s && s[RSTRING_LEN(self)]) {
rb_str_modify(self);
@@ -1901,6 +1875,7 @@ string_to_c_strict(VALUE self)
s = (char *)"";
if (!parse_comp(s, 1, &num)) {
+ if (!raise) return Qnil;
rb_raise(rb_eArgError, "invalid value for convert(): %+"PRIsVALUE,
self);
}
@@ -1956,28 +1931,29 @@ string_to_c(VALUE self)
}
static VALUE
-nucomp_s_convert(int argc, VALUE *argv, VALUE klass)
+to_complex(VALUE val)
{
- VALUE a1, a2, backref;
-
- rb_scan_args(argc, argv, "11", &a1, &a2);
+ return rb_convert_type(val, T_COMPLEX, "Complex", "to_c");
+}
- if (NIL_P(a1) || (argc == 2 && NIL_P(a2)))
+static VALUE
+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");
-
- backref = rb_backref_get();
- rb_match_busy(backref);
+ }
if (RB_TYPE_P(a1, T_STRING)) {
- a1 = string_to_c_strict(a1);
+ 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);
+ a2 = string_to_c_strict(a2, raise);
+ if (NIL_P(a2)) return Qnil;
}
- rb_backref_set(backref);
-
if (RB_TYPE_P(a1, T_COMPLEX)) {
{
get_dat1(a1);
@@ -1997,16 +1973,19 @@ nucomp_s_convert(int argc, VALUE *argv, VALUE klass)
}
if (RB_TYPE_P(a1, T_COMPLEX)) {
- if (argc == 1 || (k_exact_zero_p(a2)))
+ if (a2 == Qundef || (k_exact_zero_p(a2)))
return a1;
}
- if (argc == 1) {
+ if (a2 == Qundef) {
if (k_numeric_p(a1) && !f_real_p(a1))
return a1;
/* should raise exception for consistency */
- if (!k_numeric_p(a1))
- return rb_convert_type(a1, T_COMPLEX, "Complex", "to_c");
+ if (!k_numeric_p(a1)) {
+ if (!raise)
+ return rb_protect(to_complex, a1, NULL);
+ return to_complex(a1);
+ }
}
else {
if ((k_numeric_p(a1) && k_numeric_p(a2)) &&
@@ -2017,13 +1996,35 @@ nucomp_s_convert(int argc, VALUE *argv, VALUE klass)
}
{
+ int argc;
VALUE argv2[2];
argv2[0] = a1;
- argv2[1] = a2;
+ if (a2 == Qundef) {
+ argv2[1] = Qnil;
+ argc = 1;
+ }
+ else {
+ if (!raise && !RB_INTEGER_TYPE_P(a2) && !RB_FLOAT_TYPE_P(a2) && !RB_TYPE_P(a2, T_RATIONAL))
+ return Qnil;
+ argv2[1] = a2;
+ argc = 2;
+ }
return nucomp_s_new(argc, argv2, klass);
}
}
+static VALUE
+nucomp_s_convert(int argc, VALUE *argv, VALUE klass)
+{
+ VALUE a1, a2;
+
+ if (rb_scan_args(argc, argv, "11", &a1, &a2) == 1) {
+ a2 = Qundef;
+ }
+
+ return nucomp_convert(klass, a1, a2, TRUE);
+}
+
/* --- */
/*
@@ -2075,8 +2076,8 @@ static VALUE
numeric_arg(VALUE self)
{
if (f_positive_p(self))
- return INT2FIX(0);
- return rb_const_get(rb_mMath, id_PI);
+ return INT2FIX(0);
+ return DBL2NUM(M_PI);
}
/*
@@ -2092,6 +2093,8 @@ numeric_rect(VALUE self)
return rb_assoc_new(self, INT2FIX(0));
}
+static VALUE float_arg(VALUE self);
+
/*
* call-seq:
* num.polar -> array
@@ -2101,7 +2104,25 @@ numeric_rect(VALUE self)
static VALUE
numeric_polar(VALUE self)
{
- return rb_assoc_new(f_abs(self), f_arg(self));
+ VALUE abs, arg;
+
+ if (RB_INTEGER_TYPE_P(self)) {
+ abs = rb_int_abs(self);
+ arg = numeric_arg(self);
+ }
+ else if (RB_FLOAT_TYPE_P(self)) {
+ abs = rb_float_abs(self);
+ arg = float_arg(self);
+ }
+ else if (RB_TYPE_P(self, T_RATIONAL)) {
+ abs = rb_rational_abs(self);
+ arg = numeric_arg(self);
+ }
+ else {
+ abs = f_abs(self);
+ arg = f_arg(self);
+ }
+ return rb_assoc_new(abs, arg);
}
/*
@@ -2175,22 +2196,13 @@ Init_Complex(void)
#undef rb_intern
#define rb_intern(str) rb_intern_const(str)
- assert(fprintf(stderr, "assert() is now active\n"));
-
id_abs = rb_intern("abs");
id_arg = rb_intern("arg");
- id_convert = rb_intern("convert");
id_denominator = rb_intern("denominator");
- id_eqeq_p = rb_intern("==");
- id_expt = rb_intern("**");
id_fdiv = rb_intern("fdiv");
- id_negate = rb_intern("-@");
id_numerator = rb_intern("numerator");
id_quo = rb_intern("quo");
id_real_p = rb_intern("real?");
- id_to_f = rb_intern("to_f");
- id_to_i = rb_intern("to_i");
- id_to_r = rb_intern("to_r");
id_i_real = rb_intern("@real");
id_i_imag = rb_intern("@image"); /* @image, not @imag */
id_finite_p = rb_intern("finite?");
@@ -2203,12 +2215,7 @@ Init_Complex(void)
rb_define_alloc_func(rb_cComplex, nucomp_s_alloc);
rb_undef_method(CLASS_OF(rb_cComplex), "allocate");
-#if 0
- rb_define_private_method(CLASS_OF(rb_cComplex), "new!", nucomp_s_new_bang, -1);
- rb_define_private_method(CLASS_OF(rb_cComplex), "new", nucomp_s_new, -1);
-#else
rb_undef_method(CLASS_OF(rb_cComplex), "new");
-#endif
rb_define_singleton_method(rb_cComplex, "rectangular", nucomp_s_new, -1);
rb_define_singleton_method(rb_cComplex, "rect", nucomp_s_new, -1);
@@ -2230,43 +2237,35 @@ Init_Complex(void)
rb_undef_method(rb_cComplex, "truncate");
rb_undef_method(rb_cComplex, "i");
- rb_define_method(rb_cComplex, "real", nucomp_real, 0);
- rb_define_method(rb_cComplex, "imaginary", nucomp_imag, 0);
- rb_define_method(rb_cComplex, "imag", nucomp_imag, 0);
+ rb_define_method(rb_cComplex, "real", rb_complex_real, 0);
+ rb_define_method(rb_cComplex, "imaginary", rb_complex_imag, 0);
+ rb_define_method(rb_cComplex, "imag", rb_complex_imag, 0);
- rb_define_method(rb_cComplex, "-@", nucomp_negate, 0);
- rb_define_method(rb_cComplex, "+", nucomp_add, 1);
- rb_define_method(rb_cComplex, "-", nucomp_sub, 1);
- rb_define_method(rb_cComplex, "*", nucomp_mul, 1);
- rb_define_method(rb_cComplex, "/", nucomp_div, 1);
+ rb_define_method(rb_cComplex, "-@", rb_complex_uminus, 0);
+ rb_define_method(rb_cComplex, "+", rb_complex_plus, 1);
+ rb_define_method(rb_cComplex, "-", rb_complex_minus, 1);
+ rb_define_method(rb_cComplex, "*", rb_complex_mul, 1);
+ rb_define_method(rb_cComplex, "/", rb_complex_div, 1);
rb_define_method(rb_cComplex, "quo", nucomp_quo, 1);
rb_define_method(rb_cComplex, "fdiv", nucomp_fdiv, 1);
- rb_define_method(rb_cComplex, "**", nucomp_expt, 1);
+ rb_define_method(rb_cComplex, "**", rb_complex_pow, 1);
rb_define_method(rb_cComplex, "==", nucomp_eqeq_p, 1);
rb_define_method(rb_cComplex, "coerce", nucomp_coerce, 1);
- rb_define_method(rb_cComplex, "abs", nucomp_abs, 0);
- rb_define_method(rb_cComplex, "magnitude", nucomp_abs, 0);
+ rb_define_method(rb_cComplex, "abs", rb_complex_abs, 0);
+ rb_define_method(rb_cComplex, "magnitude", rb_complex_abs, 0);
rb_define_method(rb_cComplex, "abs2", nucomp_abs2, 0);
- rb_define_method(rb_cComplex, "arg", nucomp_arg, 0);
- rb_define_method(rb_cComplex, "angle", nucomp_arg, 0);
- rb_define_method(rb_cComplex, "phase", nucomp_arg, 0);
+ rb_define_method(rb_cComplex, "arg", rb_complex_arg, 0);
+ rb_define_method(rb_cComplex, "angle", rb_complex_arg, 0);
+ rb_define_method(rb_cComplex, "phase", rb_complex_arg, 0);
rb_define_method(rb_cComplex, "rectangular", nucomp_rect, 0);
rb_define_method(rb_cComplex, "rect", nucomp_rect, 0);
rb_define_method(rb_cComplex, "polar", nucomp_polar, 0);
- rb_define_method(rb_cComplex, "conjugate", nucomp_conj, 0);
- rb_define_method(rb_cComplex, "conj", nucomp_conj, 0);
-#if 0
- rb_define_method(rb_cComplex, "~", nucomp_conj, 0); /* gcc */
-#endif
+ rb_define_method(rb_cComplex, "conjugate", rb_complex_conjugate, 0);
+ rb_define_method(rb_cComplex, "conj", rb_complex_conjugate, 0);
rb_define_method(rb_cComplex, "real?", nucomp_false, 0);
-#if 0
- rb_define_method(rb_cComplex, "complex?", nucomp_true, 0);
- rb_define_method(rb_cComplex, "exact?", nucomp_exact_p, 0);
- rb_define_method(rb_cComplex, "inexact?", nucomp_inexact_p, 0);
-#endif
rb_define_method(rb_cComplex, "numerator", nucomp_numerator, 0);
rb_define_method(rb_cComplex, "denominator", nucomp_denominator, 0);
@@ -2284,7 +2283,8 @@ Init_Complex(void)
rb_define_method(rb_cComplex, "infinite?", rb_complex_infinite_p, 0);
rb_define_private_method(rb_cComplex, "marshal_dump", nucomp_marshal_dump, 0);
- compat = rb_define_class_under(rb_cComplex, "compatible", rb_cObject); /* :nodoc: */
+ /* :nodoc: */
+ compat = rb_define_class_under(rb_cComplex, "compatible", rb_cObject);
rb_define_private_method(compat, "marshal_load", nucomp_marshal_load, 1);
rb_marshal_define_compat(rb_cComplex, compat, nucomp_dumper, nucomp_loader);
@@ -2327,6 +2327,10 @@ Init_Complex(void)
rb_define_const(rb_cComplex, "I",
f_complex_new_bang2(rb_cComplex, ZERO, ONE));
+#if !USE_FLONUM
+ rb_gc_register_mark_object(RFLOAT_0 = DBL2NUM(0.0));
+#endif
+
rb_provide("complex.so"); /* for backward compatibility */
}
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000000..282cf4618f
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,4078 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT()
+{
+AC_CONFIG_AUX_DIR(tool)
+AC_CONFIG_MACRO_DIRS(tool/m4)
+
+AC_PREREQ(2.67)
+
+dnl override AC_CHECKING
+dnl placed here due to aclocal(1)'s
+dnl ignoring this definition in separate files
+AC_DEFUN([AC_CHECKING],[dnl
+AC_REQUIRE([_COLORIZE_RESULT_PREPARE])dnl
+AS_MESSAGE([checking ${msg_checking}$1${msg_reset}...])])dnl
+
+AC_DISABLE_OPTION_CHECKING
+
+AC_ARG_VAR([cflags], [additional CFLAGS (ignored when CFLAGS is given)])
+AC_ARG_VAR([cppflags], [additional CPPFLAGS (ignored when CPPFLAGS is given)])
+AC_ARG_VAR([cxxflags], [additional CXXFLAGS (ignored when CXXFLAGS is given)])
+
+: "environment section" && {
+HAVE_BASERUBY=yes
+AC_ARG_WITH(baseruby,
+ AS_HELP_STRING([--with-baseruby=RUBY], [use RUBY as baseruby; RUBY is the pathname of ruby]),
+ [AS_CASE(["$withval"],
+ [*ruby*],[BASERUBY=$withval],
+ [no],[HAVE_BASERUBY=no],
+ [AC_MSG_ERROR(need ruby)])
+ ],
+ [
+ AC_PATH_PROG([BASERUBY], [ruby], [false])
+ ])
+AS_IF([test "$HAVE_BASERUBY" = yes -a "`RUBYOPT=- $BASERUBY -e 'print 42' 2>/dev/null`" = 42], [
+ AS_IF([test "`RUBYOPT=- $BASERUBY --disable=gems -e 'print 42' 2>/dev/null`" = 42], [
+ BASERUBY="$BASERUBY --disable=gems"
+ ])
+ $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
+])
+AC_SUBST(BASERUBY)
+AC_SUBST(HAVE_BASERUBY)
+
+: ${GIT=git}
+HAVE_GIT=yes
+AC_ARG_WITH(git,
+ AS_HELP_STRING([--without-git], [never use git]),
+ [AS_CASE([$withval],
+ [no], [GIT=never-use HAVE_GIT=no],
+ [yes], [],
+ [GIT=$withval])])
+AS_IF([test x"$HAVE_GIT" = xyes], [command -v "$GIT" > /dev/null || HAVE_GIT=no])
+AC_SUBST(GIT)
+AC_SUBST(HAVE_GIT)
+
+eval `sed -n ['s/^@%:@define RUBY_API_VERSION_\([A-Z][A-Z_0-9]*\) \([0-9][0-9]*\)/API_\1=\2/p'] $srcdir/include/ruby/version.h`
+RUBY_PROGRAM_VERSION=`sed -n 's/^@%:@define RUBY_VERSION "\(.*\)"/\1/p' $srcdir/version.h`
+MAJOR=`echo $RUBY_PROGRAM_VERSION | cut -d. -f1`
+MINOR=`echo $RUBY_PROGRAM_VERSION | cut -d. -f2`
+TEENY=`echo $RUBY_PROGRAM_VERSION | cut -d. -f3`
+for v in MAJOR MINOR TEENY; do
+ AS_IF([eval "test \"\$$v\" = ''"], [
+ AC_MSG_ERROR(could not determine $v number from version.h)
+ ])
+done
+AS_IF([test "$MAJOR.$MINOR" != "$API_MAJOR.$API_MINOR"], [
+ AC_MSG_ERROR([API version $API_MAJOR.$API_MINOR differs from program version $MAJOR.$MINOR])
+])
+AC_SUBST(MAJOR)
+AC_SUBST(MINOR)
+AC_SUBST(TEENY)
+AC_SUBST(RUBY_PROGRAM_VERSION)
+AC_SUBST(RUBY_API_VERSION, '$(MAJOR).$(MINOR)')
+RUBY_PATCHLEVEL=`sed -n 's/^#define RUBY_PATCHLEVEL //p' $srcdir/version.h`
+dnl checks for alternative programs
+AC_CANONICAL_BUILD
+RUBY_RM_RECURSIVE
+AC_ARG_WITH(gcc,
+ AS_HELP_STRING([--without-gcc], [never use gcc]),
+ [
+ AS_CASE([$withval],
+ [no], [: ${CC=cc}],
+ [yes], [: ${CC=gcc}],
+ [CC=$withval])])
+dnl If the user switches compilers, we can't believe the cache
+AS_IF([test ! -z "$ac_cv_prog_CC" -a ! -z "$CC" -a "$CC" != "$ac_cv_prog_CC"], [
+ AC_MSG_ERROR(cached CC is different -- throw away $cache_file
+(it is also a good idea to do 'make clean' before compiling))
+])
+test -z "$CC" || ac_cv_prog_CC="$CC"
+
+AS_IF([test "$program_prefix" = NONE], [
+ program_prefix=
+])
+AS_IF([test "$prefix" -ef .], [
+ AC_MSG_ERROR(--prefix cannot be the current working directory.)
+])
+RUBY_BASE_NAME=`echo ruby | sed "$program_transform_name"`
+RUBYW_BASE_NAME=`echo rubyw | sed "$program_transform_name"`
+AC_SUBST(RUBY_BASE_NAME)
+AC_SUBST(RUBYW_BASE_NAME)
+AC_SUBST(RUBY_VERSION_NAME, '${RUBY_BASE_NAME}-${ruby_version}')
+
+AC_CANONICAL_TARGET
+test x"$target_alias" = x &&
+target_os=`echo $target_os | sed 's/linux-gnu$/linux/;s/linux-gnu/linux-/'`
+ac_install_sh='' # unusable for extension libraries.
+
+AC_ARG_WITH(os-version-style,
+ AS_HELP_STRING([--with-os-version-style=TYPE],
+ [OS version number for target and target_os [[full]]]
+ [(full|teeny|minor+0|minor|major+0|major|none)]),
+ [os_version_style=$withval],
+ [os_version_style=full
+ AS_CASE($target_os, [[*[0-9].*]],
+ [AS_CASE([`/usr/bin/ruby -e 'puts RUBY_PLATFORM' 2>/dev/null`],
+ [[*-*[0-9].*.0]], [os_version_style=minor+0],
+ [[*-*[0-9].*.*]], [os_version_style=full],
+ [[*-*[0-9].0] ], [os_version_style=major+0],
+ [[*-*[0-9].*] ], [os_version_style=minor],
+ [[*-*[0-9]] ], [os_version_style=major],
+ )])
+ ])
+os_version_style_transform=
+AS_CASE("${os_version_style}",
+ [full|teeny], [],
+ [minor+0], [os_version_style_transform=['s/\([0-9]\.[0-9][0-9]*\)\.[0-9][.0-9]*$/\1.0/']],
+ [minor], [os_version_style_transform=['s/\([0-9]\.[0-9][0-9]*\)\.[0-9][.0-9]*$/\1/']],
+ [major+0], [os_version_style_transform=['s/\([0-9]\)\.[0-9][.0-9]*$/\1.0/']],
+ [major], [os_version_style_transform=['s/\([0-9]\)\.[0-9][.0-9]*$/\1/']],
+ [none], [os_version_style_transform=['s/[0-9]\.[0-9][.0-9]*$//']],
+ [AC_MSG_ERROR(unknown --with-os-version-style: $withval)])
+AS_IF([test -z "$target_alias" -a -n "$os_version_style_transform"],
+ [
+ target=`echo ${target} | sed "$os_version_style_transform"`
+ target_os=`echo ${target_os} | sed "$os_version_style_transform"`
+ ])
+
+AC_ARG_WITH(arch,
+ AS_HELP_STRING([--with-arch=ARCHS],
+ [build an Apple/NeXT Multi Architecture Binary (MAB);
+ ARCHS is a comma-delimited list of architectures for
+ which to build; if this option is disabled or omitted
+ entirely, then the package will be built only for the
+ target platform]),
+ [target_archs="$withval"], [unset target_archs])
+
+AC_ARG_ENABLE(load-relative,
+ AS_HELP_STRING([--enable-load-relative], [resolve load paths at run time]),
+ [load_relative=$enableval])
+
+AC_ARG_PROGRAM
+
+dnl Checks for programs.
+
+cflagspat=
+test -z "$optflags" ||
+ cflagspat="$cflagspat;s|"`eval echo '"'"${optflags}"'"' | sed 's/[[][|.*]]/\\&/g;s/^ */ /;s/ *$/ /'`'| |g'
+test -z "$debugflags" ||
+ cflagspat="$cflagspat;s|"`eval echo '"'"${debugflags}"'"' | sed 's/[[][|.*]]/\\&/g;s/^ */ /;s/ *$/ /'`'| |g'
+test -z "warnflags" ||
+ cflagspat="$cflagspat;s|"`eval echo '"'"${warnflags}"'"' | sed 's/[[][|.*]]/\\&/g;s/^ */ /;s/ *$/ /'`'| |g'
+AS_IF([test -z "${CFLAGS+set}"], [
+ cflags=`echo " $cflags " | sed "$cflagspat;s/^ *//;s/ *$//"`
+ orig_cflags="$cflags"
+ cflags="$cflags "'${optflags} ${debugflags} ${warnflags}'
+])
+AS_IF([test -z "${CXXFLAGS+set}"], [
+ cxxflags=`echo " $cxxflags " | sed "$cflagspat;s/^ *//;s/ *$//"`
+ orig_cxxflags="$cxxflags"
+ cxxflags="$cxxflags "'${optflags} ${debugflags} ${warnflags}'
+])
+
+AS_CASE(["$host_os:$build_os"],
+[darwin*:darwin*], [
+ AC_CHECK_TOOLS(CC, [clang gcc cc])
+ # Following Apple deployed clang are broken
+ # clang version 1.0 (http://llvm.org/svn/llvm-project/cfe/tags/Apple/clang-23 exported)
+ # Apple clang version 2.0 (tags/Apple/clang-137) (based on LLVM 2.9svn)
+ # Apple clang version 2.1 (tags/Apple/clang-163.7.1) (based on LLVM 3.0svn)
+ AS_IF([! $CC -E -xc - <<SRC >/dev/null], [
+ @%:@if defined __APPLE_CC__ && defined __clang_major__ && __clang_major__ < 3
+ @%:@error premature clang
+ @%:@endif
+SRC
+ AC_MSG_ERROR([clang version 3.0 or later is required])
+ ])
+])
+AS_IF([test x"${build}" != x"${host}"], [
+ AC_CHECK_TOOL(CC, gcc)
+])
+
+AC_PROG_CC
+AS_CASE([$CC],
+[gcc-*], [
+ gcc_prefix=gcc- gcc_suffix=`echo "$CC" | sed 's/^gcc//'`
+ AC_PROG_CXX(g++${gcc_suffix})],
+[clang-*], [
+ gcc_prefix=clang- gcc_suffix=`echo "$CC" | sed 's/^clang//'`
+ AC_PROG_CXX(clang++${gcc_suffix})],
+[gcc_prefix= gcc_suffix=])
+
+dnl Select the appropriate C++ compiler in OS X
+AS_CASE(["$build_os"],
+ [darwin1*.*], [
+ AS_CASE(["x$CC"],
+ [xgcc-4.2|x/usr/bin/gcc-4.2], [: ${CXX=g++-4.2}],
+ [xgcc|x/usr/bin/gcc], [: ${CXX=g++}],
+ [xcc|x/usr/bin/cc], [: ${CXX=c++}],
+ [xicc], [: ${CXX=icpc}],
+ [xclang|x/usr/bin/clang], [: ${CXX=clang++}])
+ ])
+test -z "$CXX" || ac_cv_prog_CXX="$CXX"
+
+AC_PROG_CXX
+RUBY_MINGW32
+AC_PROG_GCC_TRADITIONAL
+AC_SUBST(GCC)
+AS_CASE(["$target_os"],
+[solaris*], [AC_PATH_TOOL([LD], [ld], [/usr/ccs/bin/ld], [/usr/ccs/bin:$PATH])],
+[AC_CHECK_TOOL([LD], [ld], [ld])])
+AC_SUBST(LD)
+AS_IF([test "$GCC" = yes], [
+ linker_flag=-Wl,
+ : ${optflags=-O3}
+ gcc_major=`echo =__GNUC__ | $CC -E -xc - | sed '/^=/!d;s///'`
+ gcc_minor=`echo =__GNUC_MINOR__ | $CC -E -xc - | sed '/^=/!d;s///'`
+ test -n "$gcc_major" || gcc_major=0
+ test -n "$gcc_minor" || gcc_minor=0
+ AS_CASE(["x$CC"], [xicc], [
+ icc_version=`echo =__ICC | $CC -E -xc - | sed '/^=/!d;s///'`
+ ])
+ test -n "$icc_version" || icc_version=0
+ # RUBY_APPEND_OPTIONS(XCFLAGS, ["-include ruby/config.h" "-include ruby/missing.h"])
+], [
+ linker_flag=
+])
+
+AS_IF([test "$GCC" = yes -a "$gcc_major" -lt 3 ], [
+ AC_MSG_ERROR([too old GCC])
+])
+
+RUBY_PROG_GNU_LD
+RUBY_CPPOUTFILE
+
+: ${OUTFLAG='-o '}
+: ${COUTFLAG=${OUTFLAG}}
+: ${CSRCFLAG=''}
+AC_SUBST(OUTFLAG)
+AC_SUBST(COUTFLAG)
+AC_SUBST(CSRCFLAG)
+
+: ${MJIT_CC=$CC}
+AS_IF([test "x$cross_compiling" = xno], [
+ AC_PATH_PROG([MJIT_CC], ${MJIT_CC})
+ AS_CASE([$target_os],
+ [*mingw*], [command -v cygpath > /dev/null && MJIT_CC=`cygpath -ma $MJIT_CC`])
+ shift 2
+ MJIT_CC="$MJIT_CC${1+ }$*"
+])
+
+AS_CASE(["$build_os"],
+ [darwin1*.*], [
+ # Xcode linker warns for deprecated architecture and wrongly
+ # installed TBD files.
+ CC_WRAPPER=""
+ echo 'int main(void) {return 0;}' > conftest.c
+ AS_IF([$CC -framework Foundation -o conftest conftest.c 2>&1 |
+ grep '^ld: warning: text-based stub file' >/dev/null], [
+ CC_WRAPPER=`cd -P "$srcdir/tool" && pwd`/darwin-cc
+ CC="$CC_WRAPPER $CC"
+ ])
+ rm -fr conftest*
+ ])
+
+cc_version=
+for option in --version -v -V -qversion; do
+ cc_version_message=`$CC $option 2>&1`
+ cc_version_status=$?
+ AS_CASE($cc_version_status, [0], [:], [continue])
+ AS_CASE($cc_version_message, [*Warning*], [continue])
+ cc_version='$(CC) '$option
+ break
+done
+AC_SUBST(CC_VERSION, $cc_version)
+AC_SUBST(CC_VERSION_MESSAGE, $cc_version_message)
+
+: ${DLDFLAGS="$LDFLAGS"}
+
+RUBY_UNIVERSAL_ARCH
+AS_IF([test "$target_cpu" != "$host_cpu" -a "$GCC" = yes -a "$cross_compiling" = no -a "$universal_binary" = no], [
+ RUBY_DEFAULT_ARCH("$target_cpu")
+])
+
+AS_CASE(["$target_os"], [darwin*], [
+if libtool 2>&1 | grep no_warning_for_no_symbols > /dev/null; then
+ ac_cv_prog_ac_ct_RANLIB=:
+ ac_cv_prog_ac_ct_AR='libtool -static'
+ rb_cv_arflags='-no_warning_for_no_symbols -o'
+fi
+])
+AC_CHECK_TOOLS(RANLIB, [${gcc_prefix}ranlib${gcc_suffix} ranlib], :)
+AC_CHECK_TOOLS(AR, [${gcc_prefix}ar${gcc_suffix} ar])
+AS_IF([test -z "$AR"], [
+ AC_CHECK_PROGS(AR, aal, ar)
+])
+AC_CACHE_CHECK([for $AR flags], [rb_cv_arflags], [
+ AS_IF([$AR rcD conftest.a > /dev/null 2>&1 && rm conftest.a],
+ [rb_cv_arflags=rcD], [rb_cv_arflags=rcu])
+])
+AC_SUBST(ARFLAGS, ["$rb_cv_arflags "])
+
+AC_CHECK_TOOL(AS, as)
+ASFLAGS=$ASFLAGS
+AC_SUBST(ASFLAGS)
+
+AS_CASE(["$target_os"],[cygwin*|mingw*], [ac_cv_prog_ac_ct_OBJCOPY=":"])
+
+# BSD's ports and MacPorts prefix GNU binutils with 'g'
+AC_CHECK_TOOLS(OBJDUMP, [objdump gobjdump])
+AC_CHECK_TOOLS(OBJCOPY, [objcopy gobjcopy])
+
+AS_CASE(["$target_os"],
+[cygwin*|mingw*], [
+ AC_CHECK_TOOL(WINDRES, windres)
+ AC_CHECK_TOOL(DLLWRAP, dllwrap)
+ target=`echo $target | sed "s/^$target_cpu-/-/"`
+ target_alias=`echo $target_alias | sed "s/^$target_cpu-/-/"`
+ target_cpu=`echo $target_cpu | sed s/i.86/i386/`
+ AS_CASE(["$target"], [-*], [ target="$target_cpu${target}"])
+ AS_CASE(["$target_alias"], [-*], [ target_alias="$target_cpu${target_alias}"])
+ AS_CASE(["$target_os"],
+ [mingw*], [
+ test "$rb_cv_msvcrt" = "" && unset rb_cv_msvcrt
+ AC_CACHE_CHECK(for mingw32 runtime DLL, rb_cv_msvcrt, [
+ AC_TRY_LINK([@%:@include <stdio.h>],
+ [FILE* volatile f = stdin; return 0;],
+ [rb_cv_msvcrt=`$OBJDUMP -p conftest$ac_exeext |
+ tr A-Z a-z |
+ sed -n '/^[[ ]]*dll name: \(msvc.*\)\.dll$/{s//\1/p;q;}'`],
+ [rb_cv_msvcrt=msvcrt])
+ test "$rb_cv_msvcrt" = "" && rb_cv_msvcrt=msvcrt])
+ RT_VER=`echo "$rb_cv_msvcrt" | tr -cd [0-9]`
+ test "$RT_VER" = "" && RT_VER=60
+ AC_DEFINE_UNQUOTED(RUBY_MSVCRT_VERSION, $RT_VER)
+ sysconfdir=
+ ])
+ : ${enable_shared=yes}
+ ],
+[aix*], [AC_CHECK_TOOL(NM, nm, /usr/ccs/bin/nm, /usr/ccs/bin:$PATH)],
+[hiuxmpp*], [AC_DEFINE(__HIUX_MPP__)]) # by TOYODA Eizi <toyoda@npd.kishou.go.jp>
+AC_CHECK_TOOLS(NM, [${gcc_prefix}nm${gcc_suffix} nm])
+
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+AC_PROG_INSTALL
+AC_PROG_MKDIR_P
+AS_IF([test "x$MKDIR_P" = "x -d"], [
+ AS_IF([test x"$as_mkdir_p" != xfalse], [
+ MKDIR_P='mkdir -p'
+ echo "use 'mkdir -p' as MKDIR_P"
+ ], [
+ AC_MSG_ERROR([mkdir -p is required])
+ ])
+])
+MAKEDIRS="$MKDIR_P"
+AC_SUBST(MAKEDIRS)
+
+AC_CHECK_PROG([DTRACE], [${ac_tool_prefix}dtrace], [${ac_tool_prefix}dtrace])
+AS_IF([test "$cross_compiling:$ac_cv_prog_DTRACE" = no: -a -n "$ac_tool_prefix"], [
+ AC_CHECK_PROG([DTRACE], [dtrace], [dtrace])
+])
+
+AC_CHECK_PROGS(DOT, dot)
+AC_CHECK_PROGS(DOXYGEN, doxygen)
+
+AC_CHECK_PROG(PKG_CONFIG, pkg-config, [pkg-config], [], [],
+ [`"$as_dir/$ac_word$ac_exec_ext" --print-errors --version > /dev/null 2>&1 || echo "$as_dir/$ac_word$ac_exec_ext"`])
+
+# checks for UNIX variants that set C preprocessor variables
+AC_USE_SYSTEM_EXTENSIONS
+
+AC_SUBST(RM, ['rm -f'])
+AC_SUBST(CP, ['cp'])
+RMDIRS='$(top_srcdir)/tool/rmdirs'
+RMDIR=rmdir
+mkdir "rmdirs_$$_test" "rmdirs_$$_test/a"
+rmdir --ignore-fail-on-non-empty "rmdirs_$$_test" 2>/dev/null &&
+RMDIR='rmdir --ignore-fail-on-non-empty'
+$RMDIR -p "rmdirs_$$_test/a" 2>/dev/null &&
+{ test -d "rmdirs_$$_test" || RMDIRS="$RMDIR -p"; }
+rmdir "rmdirs_$$_test/a" "rmdirs_$$_test" 2>/dev/null
+AC_SUBST(RMDIR)
+AC_SUBST(RMDIRS)
+AC_SUBST(RMALL, ['rm -fr'])
+
+AC_MSG_CHECKING([for cd using physical directory])
+rm -fr conf$$.dir
+mkdir conf$$.dir &&
+(cd conf$$.dir && mkdir src build && cd src &&
+$as_ln_s ../build . > /dev/null 2>&1 && cd build &&
+for chdir in 'cd -P' 'PWD= cd'; do
+ /bin/sh -c "$chdir ../src && echo '$chdir' > cdcmd" 2> /dev/null && break
+done)
+AS_IF([test -f conf$$.dir/src/cdcmd], [
+ read CHDIR < conf$$.dir/src/cdcmd 2> /dev/null
+], [
+ CHDIR=cd
+])
+rm -fr conf$$.dir
+AC_MSG_RESULT([$CHDIR])
+AC_SUBST(CHDIR)
+}
+
+: "compiler section" && {
+RUBY_WERROR_FLAG([
+ AC_MSG_CHECKING([whether CFLAGS is valid])
+ AC_TRY_COMPILE([], [],
+ [AC_MSG_RESULT(yes)],
+ [
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR([something wrong with CFLAGS="$CFLAGS"])
+ ]
+ )
+
+ AC_MSG_CHECKING([whether LDFLAGS is valid])
+ {
+ mkdir tmp.$$.try_link &&
+ cd tmp.$$.try_link &&
+ cp ../confdefs.h . &&
+ echo '<?xml?><plist><dict><key>CFBundleIdentifier</key><string></string></dict></plist>' > Info.plist &&
+ :
+ } || AC_MSG_ERROR([failed to make temporary directory])
+ AC_TRY_LINK([], [],
+ [AC_MSG_RESULT(yes)],
+ [
+ cd .. && rm -fr tmp.$$.try_link
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR([something wrong with LDFLAGS="$LDFLAGS"])
+ ]
+ )
+ cd .. && rm -fr tmp.$$.try_link
+])
+
+: ${RPATHFLAG=''}
+rpathflag=''
+AS_IF([test x"${RPATHFLAG}" = x], [
+ AS_CASE(["$target_os"],
+ [hpux*], [AS_IF([test "$rb_cv_prog_gnu_ld" = no], [rpathflag='+b '])],
+ [aix*], [rpathflag='-blibpath:'],
+ [for rpathflag in -R "-rpath "; 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], [])
+ done])
+], [
+ rpathflag=`echo "$RPATHFLAG" | sed 's/%.*//'`
+])
+
+AS_CASE([$RUBY_PATCHLEVEL], [-*],
+ [RUBY_DEVEL=yes], [RUBY_DEVEL=no])
+particular_werror_flags=$RUBY_DEVEL
+AC_ARG_ENABLE(werror,
+ AS_HELP_STRING([--disable-werror],
+ [don't make warnings into errors
+ even if a compiler support -Werror feature
+ [[disabled by default unless development version]]]),
+ [particular_werror_flags=$enableval])
+
+rb_cv_warnflags="$warnflags"
+AS_CASE(["$GCC:${warnflags+set}:${extra_warnflags:+set}:"],
+[yes::*|yes:*:set:], [# GCC && (!warnflags || extra_warnflags)
+ AS_IF([test $gcc_major -ge 4], [
+ extra_warnflags="$extra_warnflags -Werror=extra-tokens"
+ ])
+ AS_IF([test $gcc_major -ge 5 -a $gcc_major -le 6], [
+ extra_warnflags="$extra_warnflags -Wno-maybe-uninitialized"
+ ])
+ # ICC doesn't support -Werror=
+ AS_IF([test $icc_version -gt 0], [
+ particular_werror_flags=no
+ ])
+ for wflag in \
+ -Werror=declaration-after-statement \
+ -Werror=deprecated-declarations \
+ -Werror=division-by-zero \
+ -Werror=duplicated-cond \
+ -Werror=implicit-function-declaration \
+ -Werror=implicit-int \
+ -Werror=misleading-indentation \
+ -Werror=pointer-arith \
+ -Werror=restrict \
+ -Werror=shorten-64-to-32 \
+ -Werror=write-strings \
+ -Wimplicit-fallthrough=0 \
+ -Wmissing-noreturn \
+ -Wno-cast-function-type \
+ -Wno-constant-logical-operand \
+ -Wno-long-long \
+ -Wno-missing-field-initializers \
+ -Wno-overlength-strings \
+ -Wno-packed-bitfield-compat \
+ -Wno-parentheses-equality \
+ -Wno-self-assign \
+ -Wno-tautological-compare \
+ -Wno-unused-parameter \
+ -Wno-unused-value \
+ -Wsuggest-attribute=format \
+ -Wsuggest-attribute=noreturn \
+ -Wunused-variable \
+ -diag-disable=175,188,2259 \
+ $extra_warnflags \
+ ; do
+ AS_IF([test "$particular_werror_flags" != yes], [
+ wflag=`echo x$wflag | sed 's/^x-Werror=/-W/;s/^x//'`
+ ])
+ ok=no
+ RUBY_TRY_CFLAGS($wflag, [
+ RUBY_APPEND_OPTIONS(warnflags, $wflag)
+ ok=yes
+ ])
+ AS_CASE([$ok:$wflag], [no:-Werror=*], [
+ wflag=`echo x$wflag | sed 's/^x-Werror=/-W/'`
+ RUBY_TRY_CFLAGS($wflag, [
+ RUBY_APPEND_OPTIONS(warnflags, $wflag)
+ particular_werror_flags=no
+ ])
+ ])
+ done
+ AS_CASE([" $warnflags "],[*" -Wno-missing-field-initializers "*], [wflag="-Wall -Wextra"],
+ [wflag=-Wall])
+ RUBY_TRY_CFLAGS($wflag, [warnflags="$wflag${warnflags+ $warnflags}"])
+ # Disable warnflags while conftest. -Werror=* flags might make bad OS capability guess.
+ rb_cv_warnflags="$warnflags"
+ warnflags=
+])
+RUBY_TRY_CFLAGS(-Qunused-arguments, [RUBY_APPEND_OPTIONS(rb_cv_wsuppress_flags, -Qunused-arguments)])
+
+AC_ARG_WITH(compress-debug-sections,
+ AS_HELP_STRING([--with-compress-debug-sections=type],
+ [enable debug section compression]),
+ [compress_debug_sections=$withval], [compress_debug_sections=])
+
+AS_IF([test "$GCC" = yes], [
+ # -D_FORTIFY_SOURCE
+ # When defined _FORTIFY_SOURCE, glibc enables some additional sanity
+ # argument check. The performance drop is very little and Ubuntu enables
+ # _FORTIFY_SOURCE=2 by default. So, let's support it for protecting us from
+ # a mistake of silly C extensions.
+
+ # TODO: check if link succeeds with _FORTIFY_SOURCE=2.
+ AS_CASE(["$target_os"],
+ [mingw*], [
+ fortify_source=no
+ ])
+ AC_ARG_ENABLE(fortify_source,
+ AS_HELP_STRING([--disable-fortify-source],
+ [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(-D_FORTIFY_SOURCE=2, [RUBY_APPEND_OPTION(XCFLAGS, -D_FORTIFY_SOURCE=2)])
+ ])
+
+ : ${MJIT_HEADER_FLAGS='-P -dD'}
+
+ # -fstack-protector
+ AS_CASE(["$target_os"],
+ [mingw*], [
+ stack_protector=no
+ ])
+ AS_IF([test -z "${stack_protector+set}"], [
+ AS_FOR(option, opt, [-fstack-protector-strong -fstack-protector], [
+ RUBY_TRY_CFLAGS(option, [stack_protector=yes])
+ AS_IF([test "x$stack_protector" = xyes], [
+ RUBY_TRY_LDFLAGS(option, [], [stack_protector=])
+ ])
+ AS_IF([test "x$stack_protector" = xyes], [stack_protector=option; break])
+ ])
+ ])
+ AS_CASE(["$stack_protector"], [-*], [
+ RUBY_APPEND_OPTION(XCFLAGS, $stack_protector)
+ RUBY_APPEND_OPTION(XLDFLAGS, $stack_protector)
+ RUBY_APPEND_OPTION(LDFLAGS, $stack_protector)
+ ])
+
+ AS_CASE("${compress_debug_sections:-zlib}",
+ [none|no], [], [
+ RUBY_TRY_LDFLAGS(${linker_flag}--compress-debug-sections=${compress_debug_sections:-zlib},
+ [compress_debug_sections=${compress_debug_sections:-zlib}],
+ [compress_debug_sections=no])
+ ])
+ AS_IF([test "x$compress_debug_sections" != xno], [
+ RUBY_APPEND_OPTION(DLDFLAGS, ${linker_flag}--compress-debug-sections=$compress_debug_sections)
+ ])
+
+ AS_CASE(["$target_os"],[mingw*], [
+ # On Windows platforms, system provided headers are VC++
+ # optimized. That is, C++ habits are often contaminated into
+ # various headers. Most frequent situation is the use of //
+ # comments. We bypass ANSI C mode for them. Otherwise
+ # extension libs cannot include those headers.
+
+ # Since math.h in some mingw64 wrongly declares frexp and modf
+ # to be pure, the variables pointed by the second arguments are
+ # considered uninitialized unexpectedly.
+ AC_CACHE_CHECK([whether frexp and modf are broken],
+ rb_cv_mingw64_broken_frexp_modf,
+ [
+ save_CFLAGS="$CFLAGS"
+ AS_IF([test "$particular_werror_flags" = "yes"], [
+ CFLAGS="$CFLAGS -Werror=uninitialized"
+ ], [
+ CFLAGS="$CFLAGS -Werror -Wuninitialized"
+ ])
+ AC_TRY_COMPILE([@%:@include <math.h>
+ int foo(double x)
+ {
+ int exp;
+ frexp(x, &exp);
+ return exp;
+ }], [if (foo(0.0)) return 1;],
+ [rb_cv_mingw64_broken_frexp_modf=no],
+ [rb_cv_mingw64_broken_frexp_modf=yes])
+ CFLAGS="$save_CFLAGS"
+ ])
+ AS_IF([test "$rb_cv_mingw64_broken_frexp_modf" = yes], [
+ AC_DEFINE(RUBY_MINGW64_BROKEN_FREXP_MODF)
+ ])
+ ],
+ [cygwin*|darwin*|netbsd*], [
+ # need lgamma_r(), finite()
+ ],
+ [solaris*], [
+ # ANSI (no XCFLAGS because this is C only)
+ # Because "-std=gnu99" affects existance 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)])
+
+ test "${debugflags+set}" || {RUBY_TRY_CFLAGS(-ggdb3, [debugflags=-ggdb3])}
+ test "${debugflags+set}" || {RUBY_TRY_CFLAGS(-ggdb, [debugflags=-ggdb])}
+ 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"])
+])
+AS_IF([test "$GCC" = yes], [
+ AS_IF([test "$gcc_major" -ge 4], [
+ RUBY_TRY_CFLAGS(-fvisibility=hidden, [visibility_option=yes], [visibility_option=no])
+ ])
+ AC_SUBST(WERRORFLAG, "-Werror")
+ AS_IF([test "$visibility_option" = yes], [
+ RUBY_APPEND_OPTION(XCFLAGS, -fvisibility=hidden)
+ AC_DEFINE(RUBY_SYMBOL_EXPORT_BEGIN, [_Pragma("GCC visibility push(default)")])
+ AC_DEFINE(RUBY_SYMBOL_EXPORT_END, [_Pragma("GCC visibility pop")])
+ ], [
+ RUBY_TRY_LDFLAGS([-Wl,-unexported_symbol,_Init_*], [visibility_option=ld], [visibility_option=no])
+ ])
+ test "$visibility_option" = no || OBJCOPY=:
+])
+
+AS_IF([test "$GCC" = yes], [
+ # optflags
+
+ AS_CASE(["$target_os"], [mingw*], [
+ RUBY_TRY_CFLAGS(-fno-omit-frame-pointer, [optflags="${optflags+$optflags }-fno-omit-frame-pointer"])
+ RUBY_TRY_CFLAGS(-static-libgcc, [static_libgcc=yes], [static_libgcc=no])
+ AS_IF([test "$static_libgcc" = yes], [
+ RUBY_APPEND_OPTION(EXTLDFLAGS, -static-libgcc)
+ ])
+ ])
+
+ # disable fast-math
+ for oflag in -fno-fast-math; do
+ RUBY_TRY_CFLAGS($oflag, [RUBY_APPEND_OPTION(CFLAGS, $oflag)])
+ done
+ for oflag in -fexcess-precision=standard -fp-model\ precise; do
+ RUBY_TRY_CFLAGS($oflag, [RUBY_APPEND_OPTION(XCFLAGS, $oflag)])
+ done
+])
+
+AS_CASE(["$target_cpu"], [[i[3-6]86*]], [
+ AC_CACHE_CHECK([for __sync_val_compare_and_swap], [rb_cv_gcc_compiler_cas], [
+ AC_TRY_LINK([unsigned long atomic_var;],
+ [__sync_val_compare_and_swap(&atomic_var, 0, 1);],
+ [rb_cv_gcc_compiler_cas=yes],
+ [
+ save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -march=i486"
+ AC_TRY_LINK([unsigned long atomic_var;],
+ [__sync_val_compare_and_swap(&atomic_var, 0, 1);],
+ [rb_cv_gcc_compiler_cas=i486],
+ [rb_cv_gcc_compiler_cas=no])
+ CFLAGS="$save_CFLAGS"
+ ])
+ ])
+ AS_IF([test "$rb_cv_gcc_compiler_cas" = i486], [ARCH_FLAG="-march=i486"])
+])
+
+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=])
+
+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\""
+}
+
+AC_CACHE_CHECK([whether compiler has statement and declarations in expressions],
+ rb_cv_have_stmt_and_decl_in_expr,
+ [AC_TRY_COMPILE([],[ __extension__ ({ int a = 0; a; }); ],
+ [rb_cv_have_stmt_and_decl_in_expr=yes],
+ [rb_cv_have_stmt_and_decl_in_expr=no])])
+AS_IF([test "$rb_cv_have_stmt_and_decl_in_expr" = yes], [
+ AC_DEFINE(HAVE_STMT_AND_DECL_IN_EXPR)
+])
+
+: "header and library section" && {
+AC_ARG_WITH(winnt-ver,
+ AS_HELP_STRING([--with-winnt-ver=0xXXXX], [target Windows NT version (default to 0x0600)]),
+ [with_winnt_ver="$withval"], [with_winnt_ver="0x0600"])
+AS_CASE(["$target_os"],
+[mingw*], [
+ RUBY_APPEND_OPTION(CPPFLAGS, -D_WIN32_WINNT=$with_winnt_ver)
+ RUBY_APPEND_OPTION(CPPFLAGS, -D__MINGW_USE_VC2005_COMPAT)
+])
+
+AS_CASE(["$target_os"],
+[freebsd*], [
+ AC_CACHE_CHECK([whether pthread should be enabled by default],
+ rb_cv_enable_pthread_default,
+ [AC_TRY_CPP([
+#include <osreldate.h>
+#if __FreeBSD_version < 502102
+#error pthread should be disabled on this platform
+#endif
+ ],
+ rb_cv_enable_pthread_default=yes,
+ rb_cv_enable_pthread_default=no)])
+ enable_pthread=$rb_cv_enable_pthread_default
+ ],
+[mingw*], [
+ enable_pthread=no
+ ],
+[
+ enable_pthread=yes
+ ])
+
+dnl Checks for libraries.
+AS_CASE(["$target_os"],[*bsd*|dragonfly*],[],[ac_cv_func_daemon=no])
+
+AS_UNSET(ORIG_LIBS)
+POSTLINK=:
+AC_SUBST(POSTLINK)
+AS_CASE(["$target_os"],
+[nextstep*], [ ],
+[openstep*], [ ],
+[rhapsody*], [ ],
+[darwin*], [
+ ORIG_LIBS="$LIBS"
+ RUBY_PREPEND_OPTION(LIBS, -lobjc)
+ RUBY_APPEND_OPTIONS(CPPFLAGS, -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT)
+ AC_MSG_CHECKING(whether Mac OS X 10.5 or later)
+ AC_TRY_CPP([#include <AvailabilityMacros.h>
+ #if MAC_OS_X_VERSION_MAX_ALLOWED <= 1040
+ #error pre OS X 10.4
+ [!<===== pre OS X 10.4 =====>]
+ #endif
+ ],
+ [macosx_10_5=yes], [macosx_10_5=no])
+ AC_MSG_RESULT($macosx_10_5)
+ AS_IF([test "${target_os@%:@darwin}" -ge 16], [
+ ac_cv_func___syscall=no
+ ac_cv_func_syscall=no
+ ac_cv_header_sys_syscall_h=no
+ ac_cv_header_syscall_h=no
+ ])
+ AS_IF([test $macosx_10_5 = yes], [
+ ac_cv_func_getcontext=no
+ ac_cv_func_setcontext=no
+ ], [
+ AC_DEFINE(BROKEN_SETREUID, 1)
+ AC_DEFINE(BROKEN_SETREGID, 1)
+ ])
+ incs=`$CC -v -E -xc - < /dev/null 2>&1 | sed ['1,/^@%:@include </d;s/^ *//;s|[^./][^/]*/\.\./||g;/\/include$/!d;s||/lib|;/\/usr\/lib/d']`
+ for d in `$CC -print-search-dirs | sed -e '/^libraries: */!d;s///' | tr : '\012' | fgrep -v /../ | sed -n 's|^\(/.*/lib\)/$|\1|p'`; do
+ incs=`echo "$incs" | fgrep -v "$d"`
+ done
+ for d in $incs; do
+ test -d "$d" && RUBY_APPEND_OPTIONS(LDFLAGS, "-L$d")
+ done
+ ac_cv_type_getgroups=gid_t # getgroups() on Rosetta fills garbage
+ ac_cv_lib_crypt_crypt=no
+ ac_cv_func_fdatasync=no # Mac OS X wrongly reports it has fdatasync()
+ ac_cv_func_vfork=no
+ AS_IF([test $gcc_major -lt 4 -o \( $gcc_major -eq 4 -a $gcc_minor -lt 3 \)], [
+ ac_cv_func___builtin_setjmp=no
+ ])
+ with_setjmp_type=sigsetjmp # to hijack SIGCHLD handler
+ AC_CACHE_CHECK(for broken crypt with 8bit chars, rb_cv_broken_crypt,
+ [AC_TRY_RUN([
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+void
+broken_crypt(const char *salt, const char *buf1, const char *buf2)
+{
+#if 0
+ printf("%.2x%.2x: %s -> %s\n", (unsigned char)salt[0], (unsigned char)salt[1],
+ buf1+2, buf2+2);
+#endif
+}
+
+int
+main()
+{
+ int i;
+ char salt[2], buf[256], *s;
+ for (i = 0; i < 128*128; i++) {
+ salt[0] = 0x80 | (i & 0x7f);
+ salt[1] = 0x80 | (i >> 7);
+ strcpy(buf, crypt("", salt));
+ if (strcmp(buf, s = crypt("", salt))) {
+ broken_crypt(salt, buf, s);
+ return 1;
+ }
+ }
+ salt[0] = salt[1] = ' ';
+ strcpy(buf, crypt("", salt));
+ salt[0] = salt[1] = 0x80 | ' ';
+ if (strcmp(buf, s = crypt("", salt))) {
+ broken_crypt(salt, buf, s);
+ return 1;
+ }
+ return 0;
+}
+],
+ rb_cv_broken_crypt=no,
+ rb_cv_broken_crypt=yes,
+ rb_cv_broken_crypt=yes)])
+ AS_IF([test "$rb_cv_broken_crypt" = yes], [
+ AC_DEFINE(BROKEN_CRYPT, 1)
+ ])
+ POSTLINK=""
+ AC_CHECK_PROGS(codesign, codesign)
+ AC_CHECK_PROGS(dsymutil, dsymutil)
+ AS_IF([test -n "$codesign"], [
+ POSTLINK="{ test -z '\$(RUBY_CODESIGN)' || $codesign -s '\$(RUBY_CODESIGN)' -f \$@; }${POSTLINK:+; $POSTLINK}"
+ ])
+ AS_IF([test -n "$dsymutil"], [
+ POSTLINK="$dsymutil \$@${POSTLINK:+; $POSTLINK}"
+ ])
+ AS_IF([test -n "${POSTLINK}"], [
+ LINK_SO="$LINK_SO
+\$(POSTLINK)"
+ ])
+ AC_CHECK_HEADERS(crt_externs.h, [], [], [
+ #include <crt_externs.h>
+ ])
+ ],
+[hpux*], [ LIBS="-lm $LIBS"
+ ac_cv_c_inline=no],
+[solaris*], [ LIBS="-lm $LIBS"
+ ac_cv_func_vfork=no
+ AC_MSG_CHECKING(whether _XOPEN_SOURCE is already given)
+ AC_TRY_COMPILE([#include <unistd.h>
+ #ifndef _XOPEN_SOURCE
+ #error _XOPEN_SOURCE is not defined
+ #endif
+ ], [],
+ [given_xopen_source=yes], [given_xopen_source=no])
+ AC_MSG_RESULT($given_xopen_source)
+ AS_IF([test $given_xopen_source = no], [
+ AC_MSG_CHECKING(appropriate _XOPEN_SOURCE value to define)
+ define_xopen_source=""
+ for tmp_xpg in 7 6 5; do
+ AS_IF([test x"$define_xopen_source" != x], [
+ break
+ ])
+ RUBY_WERROR_FLAG([AC_TRY_COMPILE([
+ #define _XOPEN_SOURCE ${tmp_xpg}00
+ #include <unistd.h>
+ #ifndef _XPG${tmp_xpg}
+ #error _XPG${tmp_xpg} should be defined by _XOPEN_SOURCE=${tmp_xpg}00
+ #endif
+ ], [],
+ [define_xopen_source=${tmp_xpg}00], [])
+ ])
+ done
+ AS_IF([test x"$define_xopen_source" = x], [
+ define_xopen_source=no
+ ])
+ AC_MSG_RESULT($define_xopen_source)
+ AS_IF([test x"$define_xopen_source" != xno], [
+ RUBY_APPEND_OPTIONS(CPPFLAGS, -D_XOPEN_SOURCE=$define_xopen_source)
+ ])
+ ])
+ ],
+[haiku*], [
+ LIBS="$LIBS" # m lib is include in root
+ ],
+[cygwin*], [ ac_cv_header_langinfo_h=yes
+ RUBY_APPEND_OPTIONS(CPPFLAGS, -D_XOPEN_SOURCE -D_GNU_SOURCE)
+ AC_CHECK_FUNCS(cygwin_conv_path)
+ AC_LIBOBJ([langinfo])
+ ],
+[mingw*], [ LIBS="-lshell32 -lws2_32 -liphlpapi -limagehlp -lshlwapi $LIBS"
+ ac_cv_header_a_out_h=no
+ ac_cv_header_pwd_h=no
+ ac_cv_header_utime_h=no
+ ac_cv_header_sys_ioctl_h=no
+ ac_cv_header_sys_param_h=no
+ ac_cv_header_sys_resource_h=no
+ ac_cv_header_sys_select_h=no
+ ac_cv_header_sys_time_h=no
+ ac_cv_header_sys_times_h=no
+ ac_cv_header_sys_socket_h=no
+ ac_cv_func_lstat=yes
+ ac_cv_func_times=yes
+ ac_cv_func_waitpid=yes
+ ac_cv_func_fsync=yes
+ ac_cv_func_seekdir=yes
+ ac_cv_func_telldir=yes
+ ac_cv_func_isinf=yes
+ ac_cv_func_isnan=yes
+ ac_cv_func_finite=yes
+ ac_cv_func_lchown=yes
+ ac_cv_func_link=yes
+ ac_cv_func_readlink=yes
+ ac_cv_func_symlink=yes
+ ac_cv_lib_crypt_crypt=no
+ 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=no
+ 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
+ AS_IF([test "$target_cpu" = x64], [
+ ac_cv_func___builtin_setjmp=yes
+ ac_cv_func_round=no
+ rb_cv_fiber_coroutine=yes
+ ])
+ ac_cv_func_tgamma=no
+ rb_cv_negative_time_t=yes
+ AC_CHECK_TYPE([NET_LUID], [], [],
+ [@%:@include <winsock2.h>
+ @%:@include <iphlpapi.h>])
+ AS_IF([test x"$ac_cv_type_NET_LUID" = xyes], [
+ AC_DEFINE(HAVE_TYPE_NET_LUID, 1)
+ ])
+ AC_CHECK_FUNCS(_gmtime64_s)
+ AC_CHECK_FUNCS(_wfreopen_s)
+ AC_LIBOBJ([langinfo])
+ ],
+[bsdi*], [ LIBS="-lm $LIBS"
+ AC_DEFINE(BROKEN_SETREUID, 1)
+ AC_DEFINE(BROKEN_SETREGID, 1)
+ ac_cv_sizeof_rlim_t=8],
+[freebsd*], [ LIBS="-lm $LIBS"
+ ac_cv_func_getpeername=no
+ ac_cv_func_getsockname=no
+ ac_cv_func_shutdown=no
+ ac_cv_func_close=no
+ ],
+[netbsd*], [ LIBS="-lm $LIBS"
+ ],
+[dragonfly*], [ LIBS="-lm $LIBS"
+ # isinf() and isnan() are macros on DragonFly.
+ ac_cv_func_isinf=yes
+ ac_cv_func_isnan=yes
+ ],
+[aix*],[ LIBS="-lm $LIBS"
+ ac_cv_func_round=no
+ ac_cv_func___builtin_setjmp=no
+ ],
+[linux*],[ LIBS="-lm $LIBS"
+ # __builtin_longjmp in ppc64* Linux does not restore
+ # the TOC register (r2), which is problematic
+ # when a global exit happens from JITted .so code.
+ AS_CASE(["$target_cpu"], [powerpc64*], [
+ ac_cv_func___builtin_setjmp=no
+ ])
+ # With gcc-8's -fcf-protection, MJIT's __builtin_longjmp fails.
+ AS_CASE(["$CC $CFLAGS "], [*" -fcf-protection "*], [cf_protection=yes], [cf_protection=no])
+ AS_IF([test "$cf_protection" = yes], [
+ ac_cv_func___builtin_setjmp=no
+ ])
+ ],
+[ LIBS="-lm $LIBS"])
+: ${ORIG_LIBS=$LIBS}
+
+AC_CHECK_LIB(crypt, crypt) # glibc (GNU/Linux, GNU/Hurd, GNU/kFreeBSD)
+AC_CHECK_LIB(dl, dlopen) # Dynamic linking for SunOS/Solaris and SYSV
+AC_CHECK_LIB(dld, shl_load) # Dynamic linking for HP-UX
+AC_CHECK_LIB(socket, shutdown) # SunOS/Solaris
+
+dnl Checks for header files.
+AC_HEADER_DIRENT
+dnl AC_HEADER_STDC has been checked in AC_USE_SYSTEM_EXTENSIONS
+AC_HEADER_STDBOOL
+AC_HEADER_SYS_WAIT
+
+AC_CHECK_HEADERS(a.out.h)
+AC_CHECK_HEADERS(atomic.h)
+AC_CHECK_HEADERS(direct.h)
+AC_CHECK_HEADERS(grp.h)
+AC_CHECK_HEADERS(fcntl.h)
+AC_CHECK_HEADERS(float.h)
+AC_CHECK_HEADERS(ieeefp.h)
+AC_CHECK_HEADERS(intrinsics.h)
+AC_CHECK_HEADERS(langinfo.h)
+AC_CHECK_HEADERS(limits.h)
+AC_CHECK_HEADERS(locale.h)
+AC_CHECK_HEADERS(malloc.h)
+AC_CHECK_HEADERS(malloc/malloc.h)
+AC_CHECK_HEADERS(malloc_np.h)
+AC_CHECK_HEADERS(net/socket.h)
+AC_CHECK_HEADERS(process.h)
+AC_CHECK_HEADERS(pwd.h)
+AC_CHECK_HEADERS(sanitizer/asan_interface.h)
+AC_CHECK_HEADERS(sanitizer/msan_interface.h)
+AC_CHECK_HEADERS(setjmpex.h)
+AC_CHECK_HEADERS(stdalign.h)
+AC_CHECK_HEADERS(sys/attr.h)
+AC_CHECK_HEADERS(sys/eventfd.h)
+AC_CHECK_HEADERS(sys/fcntl.h)
+AC_CHECK_HEADERS(sys/file.h)
+AC_CHECK_HEADERS(sys/id.h)
+AC_CHECK_HEADERS(sys/ioctl.h)
+AC_CHECK_HEADERS(sys/mkdev.h)
+AC_CHECK_HEADERS(sys/param.h)
+AC_CHECK_HEADERS(sys/prctl.h)
+AC_CHECK_HEADERS(sys/resource.h)
+AC_CHECK_HEADERS(sys/select.h)
+AC_CHECK_HEADERS(sys/sendfile.h)
+AC_CHECK_HEADERS(sys/socket.h)
+AC_CHECK_HEADERS(sys/syscall.h)
+AC_CHECK_HEADERS(sys/sysmacros.h)
+AC_CHECK_HEADERS(sys/time.h)
+AC_CHECK_HEADERS(sys/times.h)
+AC_CHECK_HEADERS(sys/uio.h)
+AC_CHECK_HEADERS(sys/utime.h)
+AC_CHECK_HEADERS(syscall.h)
+AC_CHECK_HEADERS(time.h)
+AC_CHECK_HEADERS(ucontext.h)
+AC_CHECK_HEADERS(utime.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],
+ AC_SEARCH_LIBS([__gmpz_init], [gmp],
+ [AC_DEFINE(HAVE_LIBGMP, 1)]))])
+
+AC_ARG_WITH([jemalloc],
+ [AS_HELP_STRING([--with-jemalloc],[use jemalloc allocator])],
+ [with_jemalloc=$withval], [with_jemalloc=no])
+AS_IF([test "x$with_jemalloc" != xno],[
+ AC_SEARCH_LIBS([malloc_conf], [jemalloc],
+ [
+ AC_DEFINE(HAVE_LIBJEMALLOC, 1)
+ with_jemalloc=yes
+ ],
+ [test x$with_jemalloc = xyes && with_jemalloc=no])
+ AC_CHECK_HEADER(jemalloc/jemalloc.h, [
+ AC_DEFINE(RUBY_ALTERNATIVE_MALLOC_HEADER, [<jemalloc/jemalloc.h>])
+ ],
+ [test x$with_jemalloc = xyes && with_jemalloc=no])
+ AS_IF([test "x$with_jemalloc" != xyes], [
+ AC_CACHE_CHECK([for jemalloc with JEMALLOC_MANGLE], rb_cv_jemalloc_demangle,
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([@%:@define JEMALLOC_MANGLE 1
+ @%:@ifdef RUBY_ALTERNATIVE_MALLOC_HEADER
+ @%:@include RUBY_ALTERNATIVE_MALLOC_HEADER
+ @%:@else
+ @%:@include <jemalloc.h>
+ @%:@endif], [return !&malloc_conf])],
+ [rb_cv_jemalloc_demangle=yes],
+ [rb_cv_jemalloc_demangle=no])
+ ])
+ ])
+ AS_IF([test "x$rb_cv_jemalloc_demangle" = xyes], [
+ AC_DEFINE(JEMALLOC_MANGLE)
+ with_jemalloc=yes
+ ])
+ AS_CASE(["$with_jemalloc"],
+ [yes],
+ [
+ AC_DEFINE(HAVE_MALLOC_CONF)
+ ac_cv_func_malloc_usable_size=yes
+ ],
+ [no],
+ [AC_MSG_ERROR([jemalloc requested but not found])
+ ])
+])
+
+dnl check for large file stuff
+mv confdefs.h confdefs1.h
+: > confdefs.h
+AC_SYS_LARGEFILE
+# On 32-bit Solaris, it is safe to define _LARGEFILE_SOURCE
+# which is not added by AC_SYS_LARGEFILE.
+AS_IF([test x"$enable_largefile" != xno], [
+ AS_CASE(["$target_os"], [solaris*], [
+ AC_MSG_CHECKING([wheather _LARGEFILE_SOURCE should be defined])
+ AS_CASE(["${ac_cv_sys_file_offset_bits}:${ac_cv_sys_large_files}"],
+ ["64:"|"64:no"|"64:unknown"], [
+ # insert _LARGEFILE_SOURCE before _FILE_OFFSET_BITS line
+ # that is the same order as "getconf LFS_CFLAGS" output
+ mv confdefs.h largefile0.h
+ : > confdefs.h
+ AC_DEFINE(_LARGEFILE_SOURCE)
+ cat largefile0.h >> confdefs.h
+ rm largefile0.h
+ AC_MSG_RESULT([yes])
+ ], [AC_MSG_RESULT([no])])
+ ])
+])
+mv confdefs.h largefile.h
+mv confdefs1.h confdefs.h
+cat largefile.h >> confdefs.h
+
+AS_CASE(["$target_os"],
+ [aix*], [
+ AS_CASE(["$target_cpu:$ac_cv_sys_large_files"],
+ [ppc64:*|powerpc64:*], [],
+ [*:no|*:unknown], [],
+ [
+ # AIX currently does not support a 32-bit call to posix_fadvise()
+ # if _LARGE_FILES is defined.
+ ac_cv_func_posix_fadvise=no
+ ])
+ ])
+
+AC_C_BIGENDIAN
+AC_C_CONST
+AC_C_CHAR_UNSIGNED
+AC_C_INLINE
+AC_C_VOLATILE
+AC_C_TYPEOF
+
+AS_CASE(":$ac_cv_c_const:$ac_cv_c_volatile:",
+ [*:no:*], [AC_MSG_ERROR(ANSI C-conforming const and volatile are mandatory)])
+
+AC_CHECK_TYPES([long long, off_t])
+
+AC_CACHE_CHECK([char bit], [rb_cv_char_bit],
+ [test "$universal_binary" = yes && cross_compiling=yes
+ AC_COMPUTE_INT([rb_cv_char_bit], [CHAR_BIT],
+ [AC_INCLUDES_DEFAULT([@%:@include <limits.h>])], [rb_cv_char_bit=8])
+ test "$universal_binary" = yes && cross_compiling=$real_cross_compiling])
+
+RUBY_CHECK_SIZEOF(int, [], [ILP])
+RUBY_CHECK_SIZEOF(short)
+RUBY_CHECK_SIZEOF(long, [int], [ILP LP])
+RUBY_CHECK_SIZEOF(long long)
+RUBY_CHECK_SIZEOF(__int64, [8], [ILP LP])
+RUBY_CHECK_SIZEOF(__int128, [16], [ILP LP])
+RUBY_CHECK_SIZEOF(off_t)
+RUBY_CHECK_SIZEOF(void*, [int long "long long"], [ILP LP LLP])
+RUBY_CHECK_SIZEOF(float)
+RUBY_CHECK_SIZEOF(double)
+RUBY_CHECK_SIZEOF(time_t, [long "long long"], [], [@%:@include <time.h>])
+RUBY_CHECK_SIZEOF(clock_t, [], [], [@%:@include <time.h>])
+
+AC_CACHE_CHECK(packed struct attribute, rb_cv_packed_struct,
+ [rb_cv_packed_struct=no
+ for mac in \
+ "__pragma(pack(push, 1)) x __pragma(pack(pop))" \
+ "x __attribute__((packed))" \
+ ; do
+ AC_TRY_COMPILE([@%:@define PACKED_STRUCT(x) $mac
+ PACKED_STRUCT(struct { int a; });], [],
+ [rb_cv_packed_struct=$mac; break])
+ done])
+AS_IF([test "$rb_cv_packed_struct" != no], [
+ AC_DEFINE_UNQUOTED([PACKED_STRUCT(x)], [$rb_cv_packed_struct])
+ RUBY_TRY_CFLAGS(-Wno-address-of-packed-member, [AC_DEFINE(USE_UNALIGNED_MEMBER_ACCESS)])
+], [
+ AC_DEFINE_UNQUOTED([PACKED_STRUCT(x)], x)
+])
+
+AS_IF([test "x$ac_cv_type_long_long" = xyes], [
+ RUBY_CHECK_PRINTF_PREFIX(long long, ll I64, LL)
+], [test "x$ac_cv_type___int64" = xyes], [
+ RUBY_CHECK_PRINTF_PREFIX(__int64, ll I64, LL)
+])
+
+RUBY_REPLACE_TYPE(pid_t, int, PIDT)
+RUBY_REPLACE_TYPE(uid_t, int, UIDT)
+RUBY_REPLACE_TYPE(gid_t, int, GIDT)
+RUBY_REPLACE_TYPE(time_t, [], TIMET, [@%:@include <time.h>])
+RUBY_REPLACE_TYPE(dev_t, [int long "long long"], DEVT)
+RUBY_REPLACE_TYPE(mode_t, ["unsigned short" "unsigned int" long], MODET, [@%:@include <sys/stat.h>])
+RUBY_REPLACE_TYPE(rlim_t, [int long "long long"], RLIM, [
+@%:@ifdef HAVE_SYS_TYPES_H
+@%:@include <sys/types.h>
+@%:@endif
+@%:@ifdef HAVE_SYS_TYPES_H
+@%:@include <sys/time.h>
+@%:@endif
+@%:@include <sys/resource.h>
+])
+RUBY_REPLACE_TYPE(off_t, [], OFFT)
+RUBY_REPLACE_TYPE(clockid_t, [], CLOCKID, [@%:@ifdef HAVE_TIME_H
+@%:@ include <time.h>
+@%:@endif
+@%:@ifdef HAVE_SYS_TIME_H
+@%:@ include <sys/time.h>
+@%:@endif])
+
+AC_CACHE_CHECK(for prototypes, rb_cv_have_prototypes,
+ [AC_TRY_COMPILE([int foo(int x) { return 0; }], [return foo(10);],
+ rb_cv_have_prototypes=yes,
+ rb_cv_have_prototypes=no)])
+AS_IF([test "$rb_cv_have_prototypes" = yes], [
+ AC_DEFINE(HAVE_PROTOTYPES)
+])
+
+AC_CACHE_CHECK(token paste string, rb_cv_tokenpaste,
+ [AC_TRY_COMPILE([@%:@define paste(a,b) a@%:@@%:@b],
+ [int xy = 1; return paste(x,y);],
+ rb_cv_tokenpaste=ansi,
+ rb_cv_tokenpaste=knr)])
+AS_IF([test "$rb_cv_tokenpaste" = ansi], [
+ AC_DEFINE(TOKEN_PASTE(x,y),[x@%:@@%:@y])
+], [
+ AC_DEFINE(TOKEN_PASTE(x,y),[x/**/y])
+])
+
+AC_CACHE_CHECK(stringization, rb_cv_stringization, [
+ rb_cv_stringization=no
+ for string in "#expr" '"expr"'; do
+ AC_COMPILE_IFELSE([
+ AC_LANG_BOOL_COMPILE_TRY([
+#define STRINGIZE0(expr) $string
+#define STRINGIZE(expr) STRINGIZE0(expr)
+#undef real_test_for_stringization
+#define test_for_stringization -.real_test_for_stringization.-
+const char stringized[[]] = STRINGIZE(test_for_stringization);
+], [sizeof(stringized) == 32])],
+ [rb_cv_stringization="$string"; break],
+ [rb_cv_stringization=no])
+ done]
+)
+AC_DEFINE(STRINGIZE(expr),STRINGIZE0(expr))
+AS_IF([test x"$rb_cv_stringization" != xno -a "$rb_cv_stringization" != "#expr"], [
+ AC_DEFINE_UNQUOTED(STRINGIZE0(expr),$rb_cv_stringization)
+ AC_DEFINE(OLD_FASHIONED_STRINGIZATION,1)
+])
+
+AC_CACHE_CHECK([string literal concatenation],
+ rb_cv_string_literal_concatenation, [
+ AC_COMPILE_IFELSE([
+ AC_LANG_BOOL_COMPILE_TRY([
+const char concatenated_literal[[]] = "literals" "to"
+ "be" "concatenated.";
+], [sizeof(concatenated_literal) == 26])],
+ [rb_cv_string_literal_concatenation=yes],
+ [rb_cv_string_literal_concatenation=no])]
+)
+AS_IF([test "$rb_cv_string_literal_concatenation" = no], [
+ AC_MSG_ERROR([No string literal concatenation])
+])
+
+AC_CACHE_CHECK(for variable length prototypes and stdarg.h, rb_cv_stdarg,
+ [AC_TRY_COMPILE([
+#include <stdarg.h>
+int foo(int x, ...) {
+ va_list va;
+ va_start(va, x);
+ va_arg(va, int);
+ va_arg(va, char *);
+ va_arg(va, double);
+ return 0;
+}
+], [return foo(10, "", 3.14);],
+ rb_cv_stdarg=yes,
+ rb_cv_stdarg=no)])
+AS_IF([test "$rb_cv_stdarg" = yes], [
+ AC_DEFINE(HAVE_STDARG_PROTOTYPES)
+])
+
+AC_CACHE_CHECK(for variable length macro, rb_cv_va_args_macro,
+ [AC_TRY_COMPILE([
+int foo(int x, ...);
+@%:@define FOO(a, ...) foo(a, @%:@@%:@__VA_ARGS__)
+], [FOO(1);FOO(1,2);FOO(1,2,3);],
+ rb_cv_va_args_macro=yes,
+ rb_cv_va_args_macro=no)])
+AS_IF([test "$rb_cv_va_args_macro" = yes], [
+ AC_DEFINE(HAVE_VA_ARGS_MACRO)
+])
+
+AC_CACHE_CHECK([for alignas() syntax], rb_cv_have_alignas, [
+rb_cv_have_alignas=no
+RUBY_WERROR_FLAG([
+for attr in \
+ "_Alignas(x)" \
+ "alignas(x)" \
+ "@<:@@<:@alignas(x)@:>@@:>@" \
+ "__declspec(aligned(x))" \
+ "__attribute__((__aligned__(x)))" \
+;
+do
+ # C11 _Alignas and GCC __attribute__((__aligned__)) behave
+ # slightly differently. What we want is GCC's. Check that
+ # here by something C11 does not allow (`struct ALIGNAS ...`)
+ AC_TRY_COMPILE(
+ [@%:@define ALIGNAS(x) $attr
+ struct ALIGNAS(128) conftest_tag { int foo; } foo; ], [],
+ [rb_cv_have_alignas="$attr"; break], [])
+done
+])])
+AS_IF([test "$rb_cv_have_alignas" != no], [
+ AC_DEFINE_UNQUOTED([RUBY_ALIGNAS(x)], $rb_cv_have_alignas)
+])
+
+AC_CACHE_CHECK([for alignof() syntax], rb_cv_have_alignof,[
+rb_cv_have_alignof=no
+RUBY_WERROR_FLAG([
+for expr in \
+ "alignof" \
+ "_Alignof" \
+ "__alignof" \
+ "__alignof__" \
+;
+do
+ AC_TRY_COMPILE([
+ @%:@ifdef HAVE_STDALIGN_H
+ @%:@include <stdalign.h>
+ @%:@endif],[return (int)$expr(int);],
+ [rb_cv_have_alignof="$expr"; break], [])
+done
+])])
+AS_IF([test "$rb_cv_have_alignof" != no], [
+ AC_DEFINE_UNQUOTED(RUBY_ALIGNOF, $rb_cv_have_alignof)
+])
+
+RUBY_FUNC_ATTRIBUTE(__const__, CONSTFUNC)
+RUBY_FUNC_ATTRIBUTE(__pure__, PUREFUNC)
+RUBY_FUNC_ATTRIBUTE(__noreturn__, NORETURN)
+RUBY_FUNC_ATTRIBUTE(__deprecated__, DEPRECATED)
+RUBY_FUNC_ATTRIBUTE(__deprecated__("by "@%:@n), DEPRECATED_BY(n,x), rb_cv_func_deprecated_by)
+RUBY_TYPE_ATTRIBUTE(__deprecated__ mesg, DEPRECATED_TYPE(mesg,x), rb_cv_type_deprecated)
+RUBY_FUNC_ATTRIBUTE(__noinline__, NOINLINE)
+RUBY_FUNC_ATTRIBUTE(__always_inline__, ALWAYS_INLINE)
+RUBY_FUNC_ATTRIBUTE(__no_sanitize__(san), NO_SANITIZE(san, x), rb_cv_func_no_sanitize)
+RUBY_FUNC_ATTRIBUTE(__no_sanitize_address__, NO_SANITIZE_ADDRESS)
+RUBY_FUNC_ATTRIBUTE(__no_address_safety_analysis__, NO_ADDRESS_SAFETY_ANALYSIS)
+RUBY_FUNC_ATTRIBUTE(__warn_unused_result__, WARN_UNUSED_RESULT)
+RUBY_FUNC_ATTRIBUTE(__unused__, MAYBE_UNUSED)
+RUBY_FUNC_ATTRIBUTE(__error__ mesg, ERRORFUNC(mesg,x), rb_cv_func___error__)
+RUBY_FUNC_ATTRIBUTE(__warning__ mesg, WARNINGFUNC(mesg,x), rb_cv_func___warning__)
+RUBY_FUNC_ATTRIBUTE(__weak__, WEAK, rb_cv_func_weak)
+AS_IF([test "$rb_cv_func_weak" != x], [
+ AC_DEFINE(HAVE_FUNC_WEAK)
+])
+
+if_i386=${universal_binary+[defined __i386__]}
+RUBY_FUNC_ATTRIBUTE(__stdcall__, FUNC_STDCALL, rb_cv_func_stdcall, ${if_i386})
+RUBY_FUNC_ATTRIBUTE(__cdecl__, FUNC_CDECL, rb_cv_func_cdecl, ${if_i386})
+RUBY_FUNC_ATTRIBUTE(__fastcall__, FUNC_FASTCALL, rb_cv_func_fastcall, ${if_i386})
+RUBY_FUNC_ATTRIBUTE(__optimize__("O0"), FUNC_UNOPTIMIZED, rb_cv_func_unoptimized)
+RUBY_FUNC_ATTRIBUTE(__optimize__("-Os","-fomit-frame-pointer"), FUNC_MINIMIZED, rb_cv_func_minimized)
+
+AS_IF([test "$GCC" = yes], [
+ AC_CACHE_CHECK([for function alias], [rb_cv_gcc_function_alias],
+ [rb_cv_gcc_function_alias=no
+ for a in alias weak,alias; do
+ AC_TRY_LINK([void foo(void) {}
+ void bar(void) __attribute__(($a("foo")));], [bar()],
+ [rb_cv_gcc_function_alias=$a; break])
+ done])
+ AS_IF([test "$rb_cv_gcc_function_alias" != no], [
+ AC_DEFINE(HAVE_ATTRIBUTE_FUNCTION_ALIAS)
+ AC_DEFINE_UNQUOTED([RUBY_ALIAS_FUNCTION_TYPE(type, prot, name, args)],
+ [type prot __attribute__(($rb_cv_gcc_function_alias(@%:@name)));])
+ AC_DEFINE_UNQUOTED([RUBY_ALIAS_FUNCTION_VOID(prot, name, args)],
+ [RUBY_ALIAS_FUNCTION_TYPE(void, prot, name, args)])
+ ])
+
+ AC_CACHE_CHECK([for __atomic builtins], [rb_cv_gcc_atomic_builtins], [
+ AC_TRY_LINK([unsigned int atomic_var;],
+ [
+ __atomic_exchange_n(&atomic_var, 0, __ATOMIC_SEQ_CST);
+ __atomic_exchange_n(&atomic_var, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&atomic_var, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_sub(&atomic_var, 1, __ATOMIC_SEQ_CST);
+ __atomic_or_fetch(&atomic_var, 1, __ATOMIC_SEQ_CST);
+ ],
+ [rb_cv_gcc_atomic_builtins=yes],
+ [rb_cv_gcc_atomic_builtins=no])])
+ AS_IF([test "$rb_cv_gcc_atomic_builtins" = yes], [
+ AC_DEFINE(HAVE_GCC_ATOMIC_BUILTINS)
+ ])
+
+ AC_CACHE_CHECK([for __sync builtins], [rb_cv_gcc_sync_builtins], [
+ AC_TRY_LINK([unsigned int atomic_var;],
+ [
+ __sync_lock_test_and_set(&atomic_var, 0);
+ __sync_lock_test_and_set(&atomic_var, 1);
+ __sync_fetch_and_add(&atomic_var, 1);
+ __sync_fetch_and_sub(&atomic_var, 1);
+ __sync_or_and_fetch(&atomic_var, 1);
+ __sync_val_compare_and_swap(&atomic_var, 0, 1);
+ ],
+ [rb_cv_gcc_sync_builtins=yes],
+ [rb_cv_gcc_sync_builtins=no])])
+ AS_IF([test "$rb_cv_gcc_sync_builtins" = yes], [
+ AC_DEFINE(HAVE_GCC_SYNC_BUILTINS)
+ ])
+
+ AC_CACHE_CHECK(for __builtin_unreachable, rb_cv_func___builtin_unreachable,
+ [RUBY_WERROR_FLAG(
+ [AC_TRY_LINK([volatile int zero;],
+ [if (zero) __builtin_unreachable();],
+ [rb_cv_func___builtin_unreachable=yes],
+ [rb_cv_func___builtin_unreachable=no])
+ ])
+ ])
+ AS_IF([test "$rb_cv_func___builtin_unreachable" = yes], [
+ AC_DEFINE_UNQUOTED(UNREACHABLE, [__builtin_unreachable()])
+ ])
+])
+
+AC_CACHE_CHECK(for exported function attribute, rb_cv_func_exported, [
+rb_cv_func_exported=no
+RUBY_WERROR_FLAG([
+for mac in '__attribute__ ((__visibility__("default")))' '__declspec(dllexport)'; do
+ AC_TRY_COMPILE([@%:@define RUBY_FUNC_EXPORTED $mac extern
+ RUBY_FUNC_EXPORTED void conftest_attribute_check(void);], [],
+ [rb_cv_func_exported="$mac"; break])
+done
+])])
+AS_IF([test "$rb_cv_func_exported" != no], [
+ AC_DEFINE_UNQUOTED(RUBY_FUNC_EXPORTED, [$rb_cv_func_exported extern])
+])
+
+RUBY_APPEND_OPTION(XCFLAGS, -DRUBY_EXPORT)
+
+AC_ARG_ENABLE(mathn,
+ AS_HELP_STRING([--disable-mathn], [disable canonicalization for mathn]),
+ [mathn=$enableval], [mathn=yes])
+test "x$mathn" = xyes || mathn=
+AC_SUBST(MATHN, $mathn)
+
+AC_CACHE_CHECK(for function name string predefined identifier,
+ rb_cv_function_name_string,
+ [rb_cv_function_name_string=no
+ RUBY_WERROR_FLAG([
+ for func in __func__ __FUNCTION__; do
+ AC_TRY_LINK([@%:@include <stdio.h>],
+ [puts($func);],
+ [rb_cv_function_name_string=$func
+ break])
+ done
+ ])]
+)
+AS_IF([test "$rb_cv_function_name_string" != no], [
+ AC_DEFINE_UNQUOTED(RUBY_FUNCTION_NAME_STRING, [$rb_cv_function_name_string])
+])
+
+AC_CACHE_CHECK(if enum over int is allowed, rb_cv_enum_over_int, [
+ rb_cv_enum_over_int=no
+ AS_IF([test "x$ac_cv_type_long_long" = xyes], [
+ type="unsigned long long" max="ULLONG_MAX"
+ ], [
+ type="unsigned long" max="ULONG_MAX"
+ ])
+ RUBY_WERROR_FLAG([
+ AC_COMPILE_IFELSE([
+ AC_LANG_BOOL_COMPILE_TRY([
+ @%:@include <limits.h>
+ enum {conftest_max = $max};
+ ], [
+ (conftest_max == $max) &&
+ (sizeof(conftest_max) == sizeof($type))
+ ]
+ )],
+ [rb_cv_enum_over_int=yes],
+ [rb_cv_enum_over_int=no]
+ )
+ ])
+])
+AS_IF([test $rb_cv_enum_over_int = yes], [
+ AC_DEFINE(ENUM_OVER_INT, 1)
+])
+
+dnl Check whether we need to define sys_nerr locally
+AC_CHECK_DECLS([sys_nerr], [], [], [$ac_includes_default
+@%:@include <errno.h>])
+
+AC_CHECK_DECLS([getenv])
+
+AS_CASE(["$target_cpu"],
+[alpha*|sh4|sh4el|sh4eb], [AS_CASE(["$target_os"::"$GCC"],
+ [*::yes], # gcc
+ [CFLAGS="-mieee $CFLAGS"],
+ [osf*], # ccc
+ [CFLAGS="-ieee $CFLAGS"],
+ )],
+[sparc*], [AC_LIBOBJ([sparc])])
+
+ac_cv_header_net_socket_h=${ac_cv_header_net_socket_h=no}
+AS_IF([test "$ac_cv_header_net_socket_h" = yes], [
+ ac_cv_header_sys_socket_h=${ac_cv_header_sys_socket_h=no}
+], [
+ ac_cv_header_sys_socket_h=${ac_cv_header_sys_socket_h=yes}
+])
+
+
+AC_TYPE_SIZE_T
+RUBY_CHECK_SIGNEDNESS(size_t, [AC_MSG_ERROR(size_t is signed)], [],
+ [@%:@include <sys/types.h>])
+RUBY_CHECK_SIZEOF(size_t, [int long void*], [], [@%:@include <sys/types.h>])
+RUBY_CHECK_SIZEOF(ptrdiff_t, size_t, [], [@%:@include <stddef.h>])
+RUBY_CHECK_PRINTF_PREFIX(size_t, z)
+RUBY_CHECK_PRINTF_PREFIX(ptrdiff_t, t)
+AC_STRUCT_ST_BLKSIZE
+AC_STRUCT_ST_BLOCKS
+AC_STRUCT_ST_RDEV
+RUBY_CHECK_SIZEOF([struct stat.st_size], [off_t int long "long long"], [], [@%:@include <sys/stat.h>])
+AS_IF([test "$ac_cv_member_struct_stat_st_blocks" = yes], [
+ RUBY_CHECK_SIZEOF([struct stat.st_blocks], [off_t int long "long long"], [], [@%:@include <sys/stat.h>])
+])
+RUBY_CHECK_SIZEOF([struct stat.st_ino], [long "long long"], [], [@%:@include <sys/stat.h>])
+AC_CHECK_MEMBERS([struct stat.st_atim])
+AC_CHECK_MEMBERS([struct stat.st_atimespec])
+AC_CHECK_MEMBERS([struct stat.st_atimensec])
+AC_CHECK_MEMBERS([struct stat.st_mtim])
+AC_CHECK_MEMBERS([struct stat.st_mtimespec])
+AC_CHECK_MEMBERS([struct stat.st_mtimensec])
+AC_CHECK_MEMBERS([struct stat.st_ctim])
+AC_CHECK_MEMBERS([struct stat.st_ctimespec])
+AC_CHECK_MEMBERS([struct stat.st_ctimensec])
+AC_CHECK_MEMBERS([struct stat.st_birthtimespec])
+
+AC_CHECK_TYPES([struct timeval], [], [], [@%:@ifdef HAVE_TIME_H
+@%:@include <time.h>
+@%:@endif
+@%:@ifdef HAVE_SYS_TIME_H
+@%:@include <sys/time.h>
+@%:@endif])
+
+AS_IF([test "${ac_cv_type_struct_timeval}" = yes], [
+ RUBY_CHECK_SIZEOF([struct timeval.tv_sec], [time_t long "long long"], [],
+ [@%:@ifdef HAVE_TIME_H
+@%:@include <time.h>
+@%:@endif
+@%:@ifdef HAVE_SYS_TIME_H
+@%:@include <sys/time.h>
+@%:@endif])
+ AS_CASE(${ac_cv_sizeof_struct_timeval_tv_sec},
+ [SIZEOF_INT], [t=int],
+ [SIZEOF_LONG], [t=long],
+ [SIZEOF_LONG_LONG], [t=LONG_LONG],
+ [t=])
+ AS_IF([test "${t}" != ""], [
+ AC_DEFINE_UNQUOTED(TYPEOF_TIMEVAL_TV_SEC, [$t])
+ ])
+])
+
+AC_CHECK_TYPES([struct timespec], [], [], [@%:@ifdef HAVE_TIME_H
+@%:@include <time.h>
+@%:@endif
+@%:@ifdef HAVE_SYS_TIME_H
+@%:@include <sys/time.h>
+@%:@endif])
+
+AC_CHECK_TYPES([struct timezone], [], [], [@%:@ifdef HAVE_TIME_H
+@%:@ include <time.h>
+@%:@endif
+@%:@ifdef HAVE_SYS_TIME_H
+@%:@ include <sys/time.h>
+@%:@endif])
+
+AC_CACHE_VAL([rb_cv_large_fd_select],
+ [AC_CHECK_TYPE(fd_mask, [rb_cv_large_fd_select=yes], [rb_cv_large_fd_select=no], [AC_INCLUDES_DEFAULT([])
+@%:@ifdef HAVE_SYS_SELECT_H
+@%:@ include <sys/select.h>
+@%:@endif])])
+AS_IF([test "$rb_cv_large_fd_select" = yes], [
+ AC_DEFINE(HAVE_RB_FD_INIT, 1)
+])
+
+RUBY_DEFINT(int8_t, 1)
+RUBY_DEFINT(uint8_t, 1, unsigned)
+RUBY_DEFINT(int16_t, 2)
+RUBY_DEFINT(uint16_t, 2, unsigned)
+RUBY_DEFINT(int32_t, 4)
+RUBY_DEFINT(uint32_t, 4, unsigned)
+RUBY_DEFINT(int64_t, 8)
+RUBY_DEFINT(uint64_t, 8, unsigned)
+RUBY_DEFINT(int128_t, 16)
+RUBY_DEFINT(uint128_t, 16, unsigned)
+RUBY_DEFINT(intptr_t, void*)
+RUBY_DEFINT(uintptr_t, void*, unsigned)
+AS_IF([test "x$rb_cv_type_intptr_t" != xno], [
+ RUBY_CHECK_PRINTF_PREFIX(intptr_t, '' ll I64 l, PTR)
+])
+RUBY_DEFINT(ssize_t, size_t, [], [@%:@include <sys/types.h>]) dnl may differ from int, so not use AC_TYPE_SSIZE_T.
+AS_IF([test "x$rb_cv_type_int64_t" != xno], [
+ RUBY_CHECK_PRINTF_PREFIX(int64_t, ll I64 l, 64)
+])
+
+AC_CACHE_CHECK(for stack end address, rb_cv_stack_end_address,
+[rb_cv_stack_end_address=no
+ AC_TRY_LINK(
+ [extern void *__libc_stack_end;],
+ [if (!__libc_stack_end) return 1;],
+ [rb_cv_stack_end_address="__libc_stack_end"])
+])
+AS_IF([test $rb_cv_stack_end_address != no], [
+ AC_DEFINE_UNQUOTED(STACK_END_ADDRESS, $rb_cv_stack_end_address)
+])
+
+dnl Checks for library functions.
+AC_TYPE_GETGROUPS
+AC_TYPE_SIGNAL
+AS_CASE(["${target_cpu}-${target_os}:${target_archs}"],
+[powerpc-darwin*], [
+ AC_LIBSOURCES(alloca.c)
+ AC_SUBST([ALLOCA], [\${LIBOBJDIR}alloca.${ac_objext}])
+ AC_DEFINE(C_ALLOCA)
+ AC_DEFINE_UNQUOTED(alloca, alloca)
+ ],
+[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)
+ ],
+[
+ AC_FUNC_ALLOCA
+ ])
+AS_IF([test "x$ALLOCA" = "x"], [
+ AC_CACHE_CHECK([for dynamic size alloca], rb_cv_dynamic_alloca, [
+ for chk in ok __chkstk; do
+ AC_TRY_LINK([
+ @%:@ifdef HAVE_ALLOCA_H
+ @%:@include <alloca.h>
+ @%:@endif
+ void $chk() {}
+ int dynamic_alloca_test;
+ int dynamic_alloca_result;],
+ [dynamic_alloca_result = alloca(dynamic_alloca_test) != 0;],
+ [rb_cv_dynamic_alloca=$chk; break])
+ done])
+ AS_IF([test "x$rb_cv_dynamic_alloca" = "x__chkstk"], [
+ AC_DEFINE_UNQUOTED(RUBY_ALLOCA_CHKSTK, _$rb_cv_dynamic_alloca)
+ AS_CASE("$target_cpu",
+ [x64|x86_64], [
+ AC_SUBST([ALLOCA], [\${LIBOBJDIR}x86_64-chkstk.${ac_objext}])
+ ],)
+ ])
+])
+AC_FUNC_MEMCMP
+
+# http://sources.redhat.com/ml/libc-hacker/2005-08/msg00008.html
+# Debian GNU/Linux Etch's libc6.1 2.3.6.ds1-13etch5 has this problem.
+# Debian GNU/Linux Lenny's libc6.1 2.7-10 has no problem.
+AC_CACHE_CHECK(for broken erfc of glibc-2.3.6 on IA64, rb_cv_broken_glibc_ia64_erfc,
+ [AC_TRY_RUN([
+#include <math.h>
+int
+main()
+{
+ erfc(10000.0);
+ return 0;
+}
+],
+ rb_cv_broken_glibc_ia64_erfc=no,
+ rb_cv_broken_glibc_ia64_erfc=yes,
+ rb_cv_broken_glibc_ia64_erfc=no)])
+AS_CASE([$rb_cv_broken_glibc_ia64_erfc],[yes],[ac_cv_func_erf=no])
+
+AS_CASE(["$target_os"],[freebsd*],[
+ AC_DEFINE(BROKEN_CLOSE)
+ AC_REPLACE_FUNCS(close)
+ ])
+
+AC_REPLACE_FUNCS(acosh)
+AC_REPLACE_FUNCS(cbrt)
+AC_REPLACE_FUNCS(crypt)
+AC_REPLACE_FUNCS(dup2)
+AC_REPLACE_FUNCS(erf)
+AC_REPLACE_FUNCS(explicit_bzero)
+AC_REPLACE_FUNCS(ffs)
+AC_REPLACE_FUNCS(finite)
+AC_REPLACE_FUNCS(flock)
+AC_REPLACE_FUNCS(hypot)
+AC_REPLACE_FUNCS(isinf)
+AC_REPLACE_FUNCS(isnan)
+AC_REPLACE_FUNCS(lgamma_r)
+AC_REPLACE_FUNCS(memmove)
+AC_REPLACE_FUNCS(nan)
+AC_REPLACE_FUNCS(nextafter)
+AC_REPLACE_FUNCS(setproctitle)
+AC_REPLACE_FUNCS(strchr)
+AC_REPLACE_FUNCS(strerror)
+AC_REPLACE_FUNCS(strlcat)
+AC_REPLACE_FUNCS(strlcpy)
+AC_REPLACE_FUNCS(strstr)
+AC_REPLACE_FUNCS(tgamma)
+
+# for missing/setproctitle.c
+AS_CASE(["$target_os"],
+[aix* | k*bsd*-gnu | kopensolaris*-gnu | linux* | darwin*], [AC_DEFINE(SPT_TYPE,SPT_REUSEARGV)],
+[hpux*], [AC_DEFINE(SPT_TYPE,SPT_PSTAT) ],
+[])
+AC_CHECK_HEADERS(sys/pstat.h)
+
+
+AC_CACHE_CHECK(for signbit, rb_cv_have_signbit,
+ [AC_TRY_LINK([
+#include <math.h>
+], [int v = signbit(-0.0);],
+ rb_cv_have_signbit=yes,
+ rb_cv_have_signbit=no)])
+AS_IF([test "$rb_cv_have_signbit" = yes], [
+ AC_DEFINE(HAVE_SIGNBIT)
+], [
+ AC_LIBOBJ([signbit])
+])
+
+AC_CACHE_CHECK(for broken memmem, rb_cv_broken_memmem, [
+ AC_TRY_RUN([
+@%:@include <string.h>
+
+int
+main(int argc, char **argv)
+{
+ const char *str = "hogefugafoobar";
+ const char *rs = "foo";
+ const char *empty = "";
+ char *p;
+
+ p = memmem(str, strlen(str), rs, strlen(rs));
+ if (p == str+8) {
+ p = memmem(str, strlen(str), empty, strlen(empty));
+ if (p == str)
+ return 0;
+ }
+ return 1;
+}
+ ],
+ rb_cv_broken_memmem=no,
+ rb_cv_broken_memmem=yes,
+ rb_cv_broken_memmem=yes)
+])
+test x"$rb_cv_broken_memmem" = xyes && ac_cv_func_memmem=no
+
+AC_FUNC_FORK
+
+AC_CHECK_FUNCS(__syscall)
+AC_CHECK_FUNCS(_longjmp) # used for AC_ARG_WITH(setjmp-type)
+# we don't use _setjmp if _longjmp doesn't exist.
+test x$ac_cv_func__longjmp = xno && ac_cv_func__setjmp=no
+AC_CHECK_FUNCS(arc4random_buf)
+AC_CHECK_FUNCS(atan2l atan2f)
+AC_CHECK_FUNCS(chroot)
+AC_CHECK_FUNCS(chsize)
+AC_CHECK_FUNCS(clock_gettime)
+AC_CHECK_FUNCS(cosh)
+AC_CHECK_FUNCS(crypt_r)
+AC_CHECK_FUNCS(daemon)
+AC_CHECK_FUNCS(dirfd)
+AC_CHECK_FUNCS(dl_iterate_phdr)
+AC_CHECK_FUNCS(dlopen)
+AC_CHECK_FUNCS(dladdr)
+AC_CHECK_FUNCS(dup)
+AC_CHECK_FUNCS(dup3)
+AC_CHECK_FUNCS(eaccess)
+AC_CHECK_FUNCS(endgrent)
+AC_CHECK_FUNCS(eventfd)
+AC_CHECK_FUNCS(fchmod)
+AC_CHECK_FUNCS(fchown)
+AC_CHECK_FUNCS(fcntl)
+AC_CHECK_FUNCS(fdatasync)
+AC_CHECK_FUNCS(fdopendir)
+AC_CHECK_FUNCS(fgetattrlist)
+AC_CHECK_FUNCS(fmod)
+AC_CHECK_FUNCS(fstatat)
+AC_CHECK_FUNCS(fsync)
+AC_CHECK_FUNCS(ftruncate)
+AC_CHECK_FUNCS(ftruncate64) # used for Win32 platform
+AC_CHECK_FUNCS(getattrlist)
+AC_CHECK_FUNCS(getcwd)
+AC_CHECK_FUNCS(getgidx)
+AC_CHECK_FUNCS(getgrnam)
+AC_CHECK_FUNCS(getgrnam_r)
+AC_CHECK_FUNCS(getgroups)
+AC_CHECK_FUNCS(getlogin)
+AC_CHECK_FUNCS(getlogin_r)
+AC_CHECK_FUNCS(getpgid)
+AC_CHECK_FUNCS(getpgrp)
+AC_CHECK_FUNCS(getpriority)
+AC_CHECK_FUNCS(getpwnam)
+AC_CHECK_FUNCS(getpwnam_r)
+AC_CHECK_FUNCS(getpwuid)
+AC_CHECK_FUNCS(getpwuid_r)
+AC_CHECK_FUNCS(getresgid)
+AC_CHECK_FUNCS(getresuid)
+AC_CHECK_FUNCS(getrlimit)
+AC_CHECK_FUNCS(getsid)
+AC_CHECK_FUNCS(gettimeofday) # for making ac_cv_func_gettimeofday
+AC_CHECK_FUNCS(getuidx)
+AC_CHECK_FUNCS(gmtime_r)
+AC_CHECK_FUNCS(grantpt)
+AC_CHECK_FUNCS(initgroups)
+AC_CHECK_FUNCS(ioctl)
+AC_CHECK_FUNCS(isfinite)
+AC_CHECK_FUNCS(issetugid)
+AC_CHECK_FUNCS(killpg)
+AC_CHECK_FUNCS(lchmod)
+AC_CHECK_FUNCS(lchown)
+AC_CHECK_FUNCS(link)
+AC_CHECK_FUNCS(llabs)
+AC_CHECK_FUNCS(lockf)
+AC_CHECK_FUNCS(log2)
+AC_CHECK_FUNCS(lstat)
+AC_CHECK_FUNCS(lutimes)
+AC_CHECK_FUNCS(malloc_usable_size)
+AC_CHECK_FUNCS(malloc_size)
+AC_CHECK_FUNCS(mblen)
+AC_CHECK_FUNCS(memalign)
+AC_CHECK_FUNCS(memset_s)
+AC_CHECK_FUNCS(writev)
+AC_CHECK_FUNCS(memrchr)
+AC_CHECK_FUNCS(memmem)
+AC_CHECK_FUNCS(mkfifo)
+AC_CHECK_FUNCS(mknod)
+AC_CHECK_FUNCS(mktime)
+AC_CHECK_FUNCS(openat)
+AC_CHECK_FUNCS(pipe2)
+AC_CHECK_FUNCS(poll)
+AC_CHECK_FUNCS(posix_fadvise)
+AC_CHECK_FUNCS(posix_memalign)
+AC_CHECK_FUNCS(ppoll)
+AC_CHECK_FUNCS(pread)
+AC_CHECK_FUNCS(pwrite)
+AC_CHECK_FUNCS(qsort_r)
+AC_CHECK_FUNCS(qsort_s)
+AC_CHECK_FUNCS(readlink)
+AC_CHECK_FUNCS(round)
+AC_CHECK_FUNCS(sched_getaffinity)
+AC_CHECK_FUNCS(seekdir)
+AC_CHECK_FUNCS(select_large_fdset)
+AC_CHECK_FUNCS(sendfile)
+AC_CHECK_FUNCS(setegid)
+AC_CHECK_FUNCS(setenv)
+AC_CHECK_FUNCS(seteuid)
+AC_CHECK_FUNCS(setgid)
+AC_CHECK_FUNCS(setgroups)
+AC_CHECK_FUNCS(setpgid)
+AC_CHECK_FUNCS(setpgrp)
+AC_CHECK_FUNCS(setregid)
+AC_CHECK_FUNCS(setresgid)
+AC_CHECK_FUNCS(setresuid)
+AC_CHECK_FUNCS(setreuid)
+AC_CHECK_FUNCS(setrgid)
+AC_CHECK_FUNCS(setrlimit)
+AC_CHECK_FUNCS(setruid)
+AC_CHECK_FUNCS(setsid)
+AC_CHECK_FUNCS(setuid)
+AC_CHECK_FUNCS(shutdown)
+AC_CHECK_FUNCS(sigaction)
+AC_CHECK_FUNCS(sigaltstack)
+AC_CHECK_FUNCS(sigprocmask)
+AC_CHECK_FUNCS(sinh)
+AC_CHECK_FUNCS(spawnv)
+AC_CHECK_FUNCS(symlink)
+AC_CHECK_FUNCS(syscall)
+AC_CHECK_FUNCS(sysconf)
+AC_CHECK_FUNCS(tanh)
+AC_CHECK_FUNCS(telldir)
+AC_CHECK_FUNCS(timegm)
+AC_CHECK_FUNCS(times)
+AC_CHECK_FUNCS(truncate)
+AC_CHECK_FUNCS(truncate64) # used for Win32
+AC_CHECK_FUNCS(unsetenv)
+AC_CHECK_FUNCS(utimensat)
+AC_CHECK_FUNCS(utimes)
+AC_CHECK_FUNCS(wait4)
+AC_CHECK_FUNCS(waitpid)
+
+AS_CASE(["$ac_cv_func_memset_s:$ac_cv_func_qsort_s"], [*yes*],
+ [RUBY_DEFINE_IF([!defined __STDC_WANT_LIB_EXT1__], [__STDC_WANT_LIB_EXT1__], 1)])
+
+AS_IF([test "$ac_cv_func_getcwd" = yes], [
+ AC_CACHE_CHECK(if getcwd allocates buffer if NULL is given, [rb_cv_getcwd_malloc],
+ [AC_TRY_RUN([
+@%:@include <stddef.h>
+@%:@include <stdio.h>
+@%:@ifdef HAVE_UNISTD_H
+@%:@include <unistd.h>
+@%:@endif
+@%:@ifndef EXIT_SUCCESS
+@%:@define EXIT_SUCCESS 0
+@%:@endif
+@%:@ifndef EXIT_FAILURE
+@%:@define EXIT_FAILURE 1
+@%:@endif
+
+int
+main(int argc, char **argv)
+{
+ if (!getcwd(NULL, 0)) return EXIT_FAILURE;
+ return EXIT_SUCCESS;
+}
+],
+ rb_cv_getcwd_malloc=yes,
+ rb_cv_getcwd_malloc=no,
+ AS_CASE($target_os,
+ [linux*|darwin*|*bsd|cygwin*|mingw*|mswin*],
+ [rb_cv_getcwd_malloc=yes],
+ [rb_cv_getcwd_malloc=no]))])
+ AS_IF([test "$rb_cv_getcwd_malloc" = no], [AC_DEFINE(NO_GETCWD_MALLOC, 1)])
+])
+
+AS_IF([test "$ac_cv_func_crypt_r" = yes],
+ [AC_CHECK_HEADERS(crypt.h)])
+AS_IF([test "$ac_cv_func_crypt_r:$ac_cv_header_crypt_h" = yes:yes],
+ [AC_CHECK_MEMBERS([struct crypt_data.initialized], [], [],
+ [AC_INCLUDES_DEFAULT([@%:@include <crypt.h>])])])
+
+RUBY_CHECK_BUILTIN_FUNC(__builtin_alloca_with_align, [__builtin_alloca_with_align(1, 4096)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_assume_aligned, [__builtin_assume_aligned((void*)32, 32)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_bswap16, [__builtin_bswap16(0)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_bswap32, [__builtin_bswap32(0)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_bswap64, [__builtin_bswap64(0)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_popcount, [__builtin_popcount(0)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_popcountll, [__builtin_popcountll(0)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_clz, [__builtin_clz(0)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_clzl, [__builtin_clzl(0)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_clzll, [__builtin_clzll(0)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_ctz, [__builtin_ctz(0)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_ctzll, [__builtin_ctzll(0)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_add_overflow, [int x;__builtin_add_overflow(0,0,&x)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_sub_overflow, [int x;__builtin_sub_overflow(0,0,&x)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_mul_overflow, [int x;__builtin_mul_overflow(0,0,&x)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_mul_overflow_p, [__builtin_mul_overflow_p(0,0,(int)0)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_constant_p, [__builtin_constant_p(0)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_choose_expr, [
+ [int x[__extension__(__builtin_choose_expr(1, 1, -1))]];
+ [int y[__extension__(__builtin_choose_expr(0, -1, 1))]];
+ ])
+AS_IF([test x$rb_cv_builtin___builtin_choose_expr = xyes], [
+ RUBY_CHECK_BUILTIN_FUNC(__builtin_choose_expr_constant_p, [
+ [int x[__extension__(__builtin_choose_expr(__builtin_constant_p(1), 1, -1))]];
+ [int y[__extension__(__builtin_choose_expr(__builtin_constant_p(foo), -1, 1))]];
+ ])
+])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_types_compatible_p, [__builtin_types_compatible_p(int, int)])
+RUBY_CHECK_BUILTIN_FUNC(__builtin_trap, [__builtin_trap()])
+
+AS_IF([test "$ac_cv_func_qsort_r" != no], [
+ AC_CACHE_CHECK(whether qsort_r is GNU version, rb_cv_gnu_qsort_r,
+ [AC_TRY_COMPILE([
+@%:@include <stdlib.h>
+void (qsort_r)(void *base, size_t nmemb, size_t size,
+ int (*compar)(const void *, const void *, void *),
+ void *arg);
+],[ ],
+ [rb_cv_gnu_qsort_r=yes],
+ [rb_cv_gnu_qsort_r=no])
+ ])
+ AC_CACHE_CHECK(whether qsort_r is BSD version, rb_cv_bsd_qsort_r,
+ [AC_TRY_COMPILE([
+@%:@include <stdlib.h>
+void (qsort_r)(void *base, size_t nmemb, size_t size,
+ void *arg, int (*compar)(void *, const void *, const void *));
+],[ ],
+ [rb_cv_bsd_qsort_r=yes],
+ [rb_cv_bsd_qsort_r=no])
+ ])
+ AS_CASE("$rb_cv_gnu_qsort_r:$rb_cv_bsd_qsort_r",
+ [yes:no], [
+ AC_DEFINE(HAVE_GNU_QSORT_R, 1)
+ ],
+ [no:yes], [
+ AC_DEFINE(HAVE_BSD_QSORT_R, 1)
+ ])
+])
+
+AC_CACHE_CHECK(whether atan2 handles Inf as C99, rb_cv_atan2_inf_c99, [
+ AS_IF([test $ac_cv_func_atan2f:$ac_cv_func_atan2l = yes:yes], [
+ AC_TRY_RUN([
+@%:@include <math.h>
+@%:@ifdef HAVE_UNISTD_H
+@%:@include <unistd.h>
+@%:@endif
+@%:@ifndef EXIT_SUCCESS
+@%:@define EXIT_SUCCESS 0
+@%:@endif
+@%:@ifndef EXIT_FAILURE
+@%:@define EXIT_FAILURE 1
+@%:@endif
+
+int
+main(int argc, char **argv)
+{
+ if (fabs(atan2(INFINITY, INFINITY) - M_PI_4) <= 0.01) return EXIT_SUCCESS;
+ return EXIT_FAILURE;
+}
+],
+ [rb_cv_atan2_inf_c99=yes],
+ [rb_cv_atan2_inf_c99=no],
+ [AS_CASE($target_os, [mingw*|mswin*], [rb_cv_atan2_inf_c99=no], [rb_cv_atan2_inf_c99=yes])]
+ )
+ ], [rb_cv_atan2_inf_c99=no])
+])
+AS_IF([test "x$rb_cv_atan2_inf_c99" = xyes], [AC_DEFINE(ATAN2_INF_C99)])
+
+# Some platform need -lrt for clock_gettime, but the other don't.
+AS_IF([test x"$ac_cv_func_clock_gettime" != xyes], [
+ # glibc 2.17 moves clock_* functions from librt to the main C library.
+ # http://sourceware.org/ml/libc-announce/2012/msg00001.html
+ AC_CHECK_LIB(rt, clock_gettime)
+ AS_IF([test x"$ac_cv_lib_rt_clock_gettime" = xyes], [
+ AC_DEFINE(HAVE_CLOCK_GETTIME, 1)
+ ])
+])
+AC_CHECK_FUNCS(clock_getres) # clock_getres should be tested after clock_gettime test including librt test.
+AC_CHECK_LIB([rt], [timer_create])
+AC_CHECK_LIB([rt], [timer_settime])
+AS_IF([test x"$ac_cv_lib_rt_timer_create" = xyes], [
+ AC_DEFINE(HAVE_TIMER_CREATE, 1)
+])
+AS_IF([test x"$ac_cv_lib_rt_timer_settime" = xyes], [
+ AC_DEFINE(HAVE_TIMER_SETTIME, 1)
+])
+
+AC_CACHE_CHECK(for unsetenv returns a value, rb_cv_unsetenv_return_value,
+ [AC_TRY_COMPILE([
+#include <stdlib.h>
+], [int v = unsetenv("foo");],
+ rb_cv_unsetenv_return_value=yes,
+ rb_cv_unsetenv_return_value=no)])
+AS_IF([test "$rb_cv_unsetenv_return_value" = no], [
+ AC_DEFINE(VOID_UNSETENV)
+])
+
+# End of setjmp check.
+
+AC_ARG_ENABLE(setreuid,
+ AS_HELP_STRING([--enable-setreuid], [use setreuid()/setregid() according to need even if obsolete]),
+ [use_setreuid=$enableval])
+AS_IF([test "$use_setreuid" = yes], [
+ AC_DEFINE(USE_SETREUID)
+ AC_DEFINE(USE_SETREGID)
+])
+AC_STRUCT_TIMEZONE
+AC_CACHE_CHECK(for struct tm.tm_gmtoff, rb_cv_member_struct_tm_tm_gmtoff,
+ [AC_TRY_COMPILE([
+@%:@define _BSD_SOURCE
+@%:@define _DEFAULT_SOURCE
+@%:@include <time.h>
+ ],
+ [struct tm t; t.tm_gmtoff = 3600;],
+ [rb_cv_member_struct_tm_tm_gmtoff=yes],
+ [rb_cv_member_struct_tm_tm_gmtoff=no])])
+AS_IF([test "$rb_cv_member_struct_tm_tm_gmtoff" = yes], [
+ AC_DEFINE(HAVE_STRUCT_TM_TM_GMTOFF)
+])
+AC_CACHE_CHECK(for external int daylight, rb_cv_have_daylight,
+ [AC_TRY_LINK([#include <time.h>
+ int i;],
+ [i = daylight;],
+ rb_cv_have_daylight=yes,
+ rb_cv_have_daylight=no)])
+AS_IF([test "$rb_cv_have_daylight" = yes], [
+ AC_DEFINE(HAVE_DAYLIGHT)
+])
+
+AC_CACHE_CHECK(for negative time_t for gmtime(3), rb_cv_negative_time_t,
+ [AC_TRY_RUN([
+#include <stdlib.h>
+#include <time.h>
+
+void
+check(tm, y, m, d, h, s)
+ struct tm *tm;
+ int y, m, d, h, s;
+{
+ if (!tm ||
+ tm->tm_year != y ||
+ tm->tm_mon != m-1 ||
+ tm->tm_mday != d ||
+ tm->tm_hour != h ||
+ tm->tm_sec != s) {
+ exit(1);
+ }
+}
+
+int
+main()
+{
+ time_t t = -1;
+ struct tm *tm;
+
+ check(gmtime(&t), 69, 12, 31, 23, 59);
+ t = ~(time_t)0 << 31;
+ check(gmtime(&t), 1, 12, 13, 20, 52);
+ return 0;
+}
+],
+ rb_cv_negative_time_t=yes,
+ rb_cv_negative_time_t=no,
+ rb_cv_negative_time_t=yes)])
+AS_IF([test "$rb_cv_negative_time_t" = yes], [
+ AC_DEFINE(NEGATIVE_TIME_T)
+])
+
+# [ruby-dev:40910] overflow of time on FreeBSD
+# http://www.freebsd.org/cgi/query-pr.cgi?pr=145341
+AC_CACHE_CHECK(for localtime(3) overflow correctly, rb_cv_localtime_overflow,
+ [AC_TRY_RUN([
+#include <stdlib.h>
+#include <time.h>
+
+void
+check(time_t t1)
+{
+ struct tm *tm;
+ time_t t2;
+ tm = localtime(&t1);
+ if (!tm)
+ return; /* overflow detected. ok. */
+ t2 = mktime(tm);
+ if (t1 == t2)
+ return; /* round-trip. ok. */
+ exit(1);
+}
+
+int
+main()
+{
+ time_t t;
+ if (~(time_t)0 <= 0) {
+ t = (((time_t)1) << (sizeof(time_t) * 8 - 2));
+ t |= t - 1;
+ }
+ else {
+ t = ~(time_t)0;
+ }
+ check(t);
+ return 0;
+}
+],
+ rb_cv_localtime_overflow=yes,
+ rb_cv_localtime_overflow=no,
+ rb_cv_localtime_overflow=no)])
+AS_IF([test "$rb_cv_localtime_overflow" = no], [
+ AC_DEFINE(LOCALTIME_OVERFLOW_PROBLEM)
+])
+
+AS_IF([test "$ac_cv_func_sigprocmask" = yes && test "$ac_cv_func_sigaction" = yes], [
+ AC_DEFINE(POSIX_SIGNAL)
+], [
+ AC_CHECK_FUNCS(sigsetmask)
+ AC_CACHE_CHECK(for BSD signal semantics, rb_cv_bsd_signal,
+ [AC_TRY_RUN([
+#include <stdio.h>
+#include <signal.h>
+
+void
+sig_handler(dummy)
+ int dummy;
+{
+}
+
+int
+main()
+{
+ signal(SIGINT, sig_handler);
+ kill(getpid(), SIGINT);
+ kill(getpid(), SIGINT);
+ return 0;
+}
+],
+ rb_cv_bsd_signal=yes,
+ rb_cv_bsd_signal=no,
+ rb_cv_bsd_signal=$ac_cv_func_sigsetmask)])
+ AS_IF([test "$rb_cv_bsd_signal" = yes], [
+ AC_DEFINE(BSD_SIGNAL)
+ ])
+])
+
+AC_CHECK_TYPES([sig_t],[],[],[@%:@include <signal.h>])
+
+AS_IF([test "$ac_cv_func_getpgid" = no], [
+ # AC_FUNC_GETPGRP fails when cross-compiling with old autoconf.
+ # autoconf is changed between 2.52d and 2.52f?
+ # http://lists.gnu.org/archive/html/bug-gnu-utils/2001-09/msg00181.html
+ # "autoconf cleanup for AC_FUNC_GETPGRP and GETPGRP_VOID"
+AC_FUNC_GETPGRP
+])
+AS_IF([test "$ac_cv_func_setpgid:$ac_cv_func_setpgrp" = no:yes], [
+ # AC_FUNC_SETPGRP fails when cross-compiling. (until autoconf 2.69?)
+ # https://lists.gnu.org/archive/html/bug-autoconf/2013-02/msg00002.html
+ # "AC_FUNC_SETPGRP fails to work properly when cross-compiling"
+AC_FUNC_SETPGRP
+])
+
+AS_IF([test x"$ac_cv_func_dirfd" = xno], [
+ AS_CASE(["$target_os"],[solaris*],
+ [AC_CHECK_MEMBERS([DIR.d_fd, DIR.dd_fd],,,[
+#include <sys/types.h>
+#include <dirent.h>
+])])
+])
+
+AS_IF([test x"$target_cpu" = xia64], [
+ AC_LIBOBJ([ia64])
+ AC_CACHE_CHECK(for __libc_ia64_register_backing_store_base,
+ rb_cv___libc_ia64_register_backing_store_base,
+ [rb_cv___libc_ia64_register_backing_store_base=no
+ AC_TRY_LINK(
+ [extern unsigned long __libc_ia64_register_backing_store_base;],
+ [unsigned long p = __libc_ia64_register_backing_store_base;
+ printf("%ld\n", p);],
+ [rb_cv___libc_ia64_register_backing_store_base=yes])])
+ AS_IF([test $rb_cv___libc_ia64_register_backing_store_base = yes], [
+ AC_DEFINE(HAVE___LIBC_IA64_REGISTER_BACKING_STORE_BASE)
+ ])
+])
+
+AC_CACHE_CHECK(whether right shift preserve sign bit, rb_cv_rshift_sign,
+ [AC_COMPILE_IFELSE([AC_LANG_BOOL_COMPILE_TRY([], [(-1==(-1>>1))])],
+ rb_cv_rshift_sign=yes,
+ rb_cv_rshift_sign=no)])
+AS_IF([test "$rb_cv_rshift_sign" = yes], [
+ AC_DEFINE(RSHIFT(x,y), ((x)>>(int)(y)))
+], [
+ AC_DEFINE(RSHIFT(x,y), (((x)<0) ? ~((~(x))>>(int)(y)) : (x)>>(int)(y)))
+])
+
+AS_CASE(["$ac_cv_func_gettimeofday:$ac_cv_func_clock_gettime"],
+[*yes*], [],
+[
+ AC_MSG_ERROR(clock_gettime() or gettimeofday() must exist)
+])
+
+AS_IF([test "$ac_cv_func_sysconf" = yes], [
+ RUBY_CHECK_SYSCONF(CLK_TCK)
+])
+
+AS_IF([test "${universal_binary-no}" = yes ], [
+ archflagpat=`eval echo '"'"${ARCH_FLAG}"'"' | sed 's/[[][|.*]]/\\&/g'`
+ save_CFLAGS="$CFLAGS" new_cflags=`echo "$CFLAGS" | sed "s|$archflagpat"'||'`
+ save_LDFLAGS="$LDFLAGS" new_ldflags=`echo "$LDFLAGS" | sed "s|$archflagpat"'||'`
+ stack_dir=
+ for archs in ${universal_archnames}; do
+ archs=`echo $archs | sed 's/=.*//'`
+ CFLAGS="$new_cflags -arch $archs"
+ LDFLAGS="$new_ldflags -arch $archs"
+ RUBY_STACK_GROW_DIRECTION($archs, dir)
+ AS_IF([test x$stack_dir = x], [
+ stack_dir=$dir
+ ], [test x$stack_dir != x$dir], [
+ stack_dir=no
+ ])
+ done
+ CFLAGS="$save_CFLAGS" LDFLAGS="$save_LDFLAGS"
+ AS_IF([test x$stack_dir = xno], [
+ for archs in ${universal_archnames}; do
+ archs=`echo $archs | sed 's/=.*//'`
+ eval dir=\$[rb_cv_stack_grow_dir_]AS_TR_SH([$archs])
+ RUBY_DEFINE_IF([defined __${archs}__], STACK_GROW_DIRECTION, $dir)
+ done
+ ], [
+ AC_DEFINE_UNQUOTED(STACK_GROW_DIRECTION, $stack_dir)
+ ])
+], [
+ RUBY_STACK_GROW_DIRECTION($target_cpu, dir)
+ AC_DEFINE_UNQUOTED(STACK_GROW_DIRECTION, $dir)
+])
+
+AC_ARG_ENABLE(fiber-coroutine,
+ AS_HELP_STRING([--disable-fiber-coroutine], [disable native coroutine implementation for fiber]),
+ [rb_cv_fiber_coroutine=$enableval])
+AS_CASE(["$rb_cv_fiber_coroutine"], [yes|''], [
+ AC_MSG_CHECKING(native coroutine implementation for ${target_cpu}-${target_os})
+ AS_CASE(["$target_cpu-$target_os"],
+ [x*64-darwin*], [
+ rb_cv_fiber_coroutine=amd64
+ ],
+ [x*64-linux], [
+ AS_CASE(["$ac_cv_sizeof_voidp"],
+ [8], [ rb_cv_fiber_coroutine=amd64 ],
+ [4], [ rb_cv_fiber_coroutine=x86 ],
+ [*], [ rb_cv_fiber_coroutine= ]
+ )
+ ],
+ [*86-linux], [
+ rb_cv_fiber_coroutine=x86
+ ],
+ [x64-mingw32], [
+ rb_cv_fiber_coroutine=win64
+ ],
+ [powerpc64le-linux], [
+ rb_cv_fiber_coroutine=ppc64le
+ ],
+ [*], [
+ rb_cv_fiber_coroutine=
+ ]
+ )
+ AC_MSG_RESULT(${rb_cv_fiber_coroutine:-no})
+])
+AS_IF([test "${rb_cv_fiber_coroutine:-no}" != no], [
+ COROUTINE_H=coroutine/$rb_cv_fiber_coroutine/Context.h
+ AC_DEFINE_UNQUOTED(FIBER_USE_COROUTINE, ["$COROUTINE_H"])
+ AC_SUBST(X_FIBER_COROUTINE_H, [$COROUTINE_H])
+])
+
+AS_IF([test x"$enable_pthread" = xyes], [
+ for pthread_lib in thr pthread pthreads c c_r root; do
+ AC_CHECK_LIB($pthread_lib, pthread_create,
+ rb_with_pthread=yes, rb_with_pthread=no)
+ AS_IF([test "$rb_with_pthread" = "yes"], [break])
+ done
+ AS_IF([test x"$rb_with_pthread" = xyes], [
+ AC_DEFINE(_REENTRANT)
+ AC_DEFINE(_THREAD_SAFE)
+ AC_DEFINE(HAVE_LIBPTHREAD)
+ AC_CHECK_HEADERS(pthread_np.h, [], [], [@%:@include <pthread.h>])
+ AS_CASE(["$pthread_lib:$target_os"],
+ [c:*], [],
+ [root:*], [],
+ [c_r:*|*:openbsd*|*:mirbsd*], [LIBS="-pthread $LIBS"],
+ [LIBS="-l$pthread_lib $LIBS"])
+ ], [
+ AC_MSG_WARN("Don't know how to find pthread library on your system -- thread support disabled")
+ ])
+ AC_CACHE_CHECK([whether pthread_t is scalar type], [rb_cv_scalar_pthread_t], [
+ AC_TRY_COMPILE([
+ @%:@include <pthread.h>
+ ], [
+ pthread_t thread_id;
+ thread_id = 0;
+ if (!thread_id) return 0;
+ ], [rb_cv_scalar_pthread_t=yes], [rb_cv_scalar_pthread_t=no])
+ ])
+ AS_IF([test x"$rb_cv_scalar_pthread_t" = xyes], [
+ : # RUBY_CHECK_SIZEOF(pthread_t, [void* int long], [], [@%:@include <pthread.h>])
+ ], [
+ AC_DEFINE(NON_SCALAR_THREAD_ID)
+ ])
+ AC_CHECK_FUNCS(sched_yield pthread_attr_setinheritsched \
+ pthread_attr_get_np pthread_attr_getstack pthread_attr_getguardsize \
+ pthread_get_stackaddr_np pthread_get_stacksize_np \
+ thr_stksegment pthread_stackseg_np pthread_getthrds_np \
+ pthread_condattr_setclock \
+ pthread_sigmask pthread_setname_np pthread_set_name_np)
+ AS_CASE(["$target_os"],[aix*],[ac_cv_func_pthread_getattr_np=no],[AC_CHECK_FUNCS(pthread_getattr_np)])
+ set_current_thread_name=
+ AS_IF([test "$ac_cv_func_pthread_setname_np" = yes], [
+ AC_CACHE_CHECK([arguments of pthread_setname_np], [rb_cv_func_pthread_setname_np_arguments],
+ [rb_cv_func_pthread_setname_np_arguments=
+ # Linux,AIX, (pthread_self(), name)
+ # NetBSD (pthread_self(), \"%s\", name)
+ # Darwin (name)
+ for mac in \
+ "(pthread_self(), name)" \
+ "(pthread_self(), \"%s\", name)" \
+ "(name)" \
+ ; do
+ AC_TRY_COMPILE([
+ @%:@include <pthread.h>
+ @%:@ifdef HAVE_PTHREAD_NP_H
+ @%:@include <pthread_np.h>
+ @%:@endif
+ @%:@define SET_THREAD_NAME(name) pthread_setname_np${mac}
+ ],
+ [if (SET_THREAD_NAME("conftest")) return 1;],
+ [rb_cv_func_pthread_setname_np_arguments="${mac}"
+ break])
+ done
+ ]
+ )
+ AS_IF([test -n "${rb_cv_func_pthread_setname_np_arguments}"], [
+ set_current_thread_name="pthread_setname_np${rb_cv_func_pthread_setname_np_arguments}"
+ ])
+ ], [test "$ac_cv_func_pthread_set_name_np" = yes], [
+ set_current_thread_name="pthread_set_name_np(pthread_self(), name)"
+ ])
+ AS_IF([test -n "$set_current_thread_name"], [
+ AC_DEFINE_UNQUOTED(SET_CURRENT_THREAD_NAME(name), $set_current_thread_name)
+ AS_CASE([$set_current_thread_name],
+ [*'pthread_self()'*], [
+ set_another_thread_name=`echo "$set_current_thread_name" | sed 's/pthread_self()/thid/'`
+ AC_DEFINE_UNQUOTED(SET_ANOTHER_THREAD_NAME(thid,name), $set_another_thread_name)
+ ])
+ ])
+])
+
+AS_IF([test x"$ac_cv_header_ucontext_h" = xno], [
+ AC_CACHE_CHECK([if signal.h defines ucontext_t], [rb_cv_ucontext_in_signal_h],
+ [AC_TRY_COMPILE([@%:@include <signal.h>],
+ [size_t size = sizeof(ucontext_t);],
+ [rb_cv_ucontext_in_signal_h=yes], [rb_cv_ucontext_in_signal_h=no])])
+ AS_IF([test x"$rb_cv_ucontext_in_signal_h" = xyes], [
+ AC_DEFINE_UNQUOTED(UCONTEXT_IN_SIGNAL_H, 1)
+ ])
+])
+AS_IF([test x"$ac_cv_header_ucontext_h" = xyes -o x"$rb_cv_ucontext_in_signal_h" = xyes], [
+ AC_CACHE_CHECK([if mcontext_t is a pointer], [rb_cv_mcontext_t_ptr],
+ [AC_TRY_COMPILE([
+ @%:@include <signal.h>
+ @%:@ifdef HAVE_UCONTEXT_H
+ @%:@include <ucontext.h>
+ @%:@endif
+ mcontext_t test(mcontext_t mc) {return mc+1;}
+ ],
+ [test(0);],
+ [rb_cv_mcontext_t_ptr=yes], [rb_cv_mcontext_t_ptr=no])])
+ AS_IF([test x"$rb_cv_mcontext_t_ptr" = xyes], [
+ AC_DEFINE_UNQUOTED(DEFINE_MCONTEXT_PTR(mc, uc), mcontext_t mc = (uc)->uc_mcontext)
+ ], [
+ AC_DEFINE_UNQUOTED(DEFINE_MCONTEXT_PTR(mc, uc), mcontext_t *mc = &(uc)->uc_mcontext)
+ ])
+ AS_IF([test x"$rb_with_pthread" = xyes], [
+ AC_CHECK_FUNCS(getcontext setcontext)
+ ])
+])
+
+AS_IF([test "$ac_cv_func_fork_works" = "yes" -a "$rb_with_pthread" = "yes"], [
+ AC_CACHE_CHECK([if fork works with pthread], rb_cv_fork_with_pthread,
+ [AC_TRY_RUN([
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+
+void *
+thread_func(void *dmy)
+{
+ return dmy;
+}
+
+int
+use_threads(void)
+{
+ pthread_t tid;
+ if (pthread_create(&tid, 0, thread_func, 0) != 0) {
+ return -1;
+ }
+ if (pthread_join(tid, 0) != 0) {
+ return -1;
+ }
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ pid_t pid;
+ if (use_threads()) return EXIT_FAILURE;
+ pid = fork();
+
+ if (pid) {
+ int loc;
+ sleep(1);
+ if (waitpid(pid, &loc, WNOHANG) == 0) {
+ kill(pid, SIGKILL);
+ return EXIT_FAILURE;
+ }
+ if (!WIFEXITED(loc) || WEXITSTATUS(loc) != EXIT_SUCCESS)
+ return EXIT_FAILURE;
+ }
+ else {
+ if (use_threads()) return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}],
+ rb_cv_fork_with_pthread=yes,
+ rb_cv_fork_with_pthread=no,
+ rb_cv_fork_with_pthread=yes)])
+ test x$rb_cv_fork_with_pthread = xyes || AC_DEFINE(CANNOT_FORK_WITH_PTHREAD)
+])
+}
+
+: "runtime section" && {
+dnl wheather use dln_a_out or not
+AC_ARG_WITH(dln-a-out,
+ AS_HELP_STRING([--with-dln-a-out], [use dln_a_out if possible]),
+ [
+ AS_CASE([$withval],
+ [yes], [
+ AS_IF([test "$enable_shared" = yes], [
+ AC_MSG_ERROR(dln_a_out can not make shared library)
+ ])
+ with_dln_a_out=yes],
+ [
+ with_dln_a_out=no])], [with_dln_a_out=no])
+
+AC_CACHE_CHECK(whether ELF binaries are produced, rb_cv_binary_elf,
+[AC_TRY_LINK([],[], [
+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)
+ AS_IF([test "$with_dln_a_out" = yes], [
+ AC_MSG_ERROR(dln_a_out does not work with ELF)
+ ])
+ AC_CHECK_HEADERS([elf.h elf_abi.h])
+ AS_IF([test $ac_cv_header_elf_h = yes -o $ac_cv_header_elf_abi_h = yes], [
+ AC_LIBOBJ([addr2line])
+ AS_IF([test "x$compress_debug_sections" = xzlib], [
+ AC_CHECK_LIB([z], [uncompress])
+ ])
+ ])
+])
+
+AC_CHECK_HEADERS([mach-o/loader.h])
+AS_IF([test "$ac_cv_header_mach_o_loader_h" = yes], [
+ AC_LIBOBJ([addr2line])
+])
+
+AS_CASE(["$target_os"],
+[linux* | gnu* | k*bsd*-gnu | bsdi* | kopensolaris*-gnu], [
+ AS_IF([test "$rb_cv_binary_elf" = no], [
+ with_dln_a_out=yes
+ ], [
+ LDFLAGS="$LDFLAGS -rdynamic"
+ ])])
+LIBEXT=a
+
+AC_SUBST(DLDFLAGS)dnl
+AC_SUBST(ARCH_FLAG)dnl
+AC_SUBST(MJIT_HEADER_FLAGS)dnl
+AC_SUBST(MJIT_HEADER_INSTALL_DIR)dnl
+AC_SUBST(MJIT_CC)dnl
+AS_IF([test "$GCC" = "yes"], [
+ AS_CASE(["$target_os"],[aix*],[mjit_std_cflag="-std=gnu99"])
+])
+AC_SUBST(MJIT_CFLAGS, [${MJIT_CFLAGS-"-w ${mjit_std_cflag} ${orig_cflags}"}])dnl
+AC_SUBST(MJIT_OPTFLAGS, [${MJIT_OPTFLAGS-'$(optflags)'}])dnl
+AC_SUBST(MJIT_DEBUGFLAGS, [${MJIT_DEBUGFLAGS-'$(debugflags)'}])dnl
+AC_SUBST(MJIT_LDSHARED)dnl
+
+AC_SUBST(STATIC)dnl
+AC_SUBST(CCDLFLAGS)dnl
+AC_SUBST(LDSHARED)dnl
+AC_SUBST(LDSHAREDXX)dnl
+AC_SUBST(DLEXT)dnl
+AC_SUBST(DLEXT2)dnl
+AC_SUBST(LIBEXT)dnl
+AC_SUBST(ASMEXT, S)dnl
+
+STATIC=
+
+AS_IF([test "$with_dln_a_out" != yes], [
+ rb_cv_dlopen=unknown
+ AC_MSG_CHECKING(whether OS depend dynamic link works)
+ AS_IF([test "$GCC" = yes], [
+ AS_CASE(["$target_os"],
+ [darwin*], [
+ # The -fno-common is needed if we wish to embed the Ruby interpreter
+ # into a plugin module of some project (as opposed to embedding it
+ # within the project's application). The -I/usr/local/include is
+ # needed because CPP as discovered by configure (cc -E -traditional)
+ # fails to consult /usr/local/include by default. This causes
+ # mkmf.rb's have_header() to fail if the desired resource happens to be
+ # installed in the /usr/local tree.
+ RUBY_APPEND_OPTION(CCDLFLAGS, -fno-common)],
+ [bsdi*|cygwin*|mingw*|aix*|interix*], [ ],
+ [
+ RUBY_APPEND_OPTION(CCDLFLAGS, -fPIC)])
+ ], [
+ AS_CASE(["$target_os"],
+ [hpux*], [CCDLFLAGS="$CCDLFLAGS +Z"],
+ [solaris*|irix*], [CCDLFLAGS="$CCDLFLAGS -KPIC"],
+ [sunos*], [CCDLFLAGS="$CCDLFLAGS -PIC"],
+ [esix*|uxpds*], [CCDLFLAGS="$CCDLFLAGS -KPIC"],
+ [: ${CCDLFLAGS=""}])
+ ])
+
+
+ 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_CASE(["$target_os"],
+ [hpux*], [ DLDFLAGS="$DLDFLAGS -E"
+ : ${LDSHARED='$(LD) -b'}
+ XLDFLAGS="$XLDFLAGS -Wl,-E"
+ : ${LIBPATHENV=SHLIB_PATH}
+ rb_cv_dlopen=yes],
+ [solaris*], [ AS_IF([test "$GCC" = yes], [
+ : ${LDSHARED='$(CC) -shared'}
+ AS_IF([test "$rb_cv_prog_gnu_ld" = yes], [
+ LDFLAGS="$LDFLAGS -Wl,-E"
+ ])
+ ], [
+ : ${LDSHARED='$(CC) -G'}
+ ])
+ AS_IF([test "$ac_cv_sizeof_voidp" = 8], [
+ : ${LIBPATHENV=LD_LIBRARY_PATH_64}
+ : ${PRELOADENV=LD_PRELOAD_64}
+ ], [
+ : ${LIBPATHENV=LD_LIBRARY_PATH_32}
+ : ${PRELOADENV=LD_PRELOAD_32}
+ ])
+ rb_cv_dlopen=yes],
+ [sunos*], [ : ${LDSHARED='$(LD) -assert nodefinitions'}
+ rb_cv_dlopen=yes],
+ [irix*], [ : ${LDSHARED='$(LD) -shared'}
+ rb_cv_dlopen=yes],
+ [sysv4*], [ : ${LDSHARED='$(LD) -G'}
+ rb_cv_dlopen=yes],
+ [nto-qnx*], [ : ${LDSHARED='$(CC) -shared'}
+ rb_cv_dlopen=yes],
+ [esix*|uxpds*], [ : ${LDSHARED='$(LD) -G'}
+ rb_cv_dlopen=yes],
+ [osf*], [ : ${LDSHARED='$(LD) -shared -expect_unresolved "*"'}
+ rb_cv_dlopen=yes],
+ [bsdi3*], [ AS_CASE(["$CC"],
+ [*shlicc*], [ : ${LDSHARED='$(CC) -r'}
+ rb_cv_dlopen=yes])],
+ [linux* | gnu* | k*bsd*-gnu | netbsd* | bsdi* | kopensolaris*-gnu | haiku*], [
+ : ${LDSHARED='$(CC) -shared'}
+ AS_IF([test "$rb_cv_binary_elf" = yes], [
+ LDFLAGS="$LDFLAGS -Wl,-export-dynamic"
+ ])
+ 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'}
+ AS_IF([test "$rb_cv_binary_elf" = yes], [
+ LDFLAGS="$LDFLAGS -rdynamic"
+ DLDFLAGS="$DLDFLAGS "'-Wl,-soname,$@'
+ ], [
+ test "$GCC" = yes && test "$rb_cv_prog_gnu_ld" = yes || LDSHARED='$(LD) -Bshareable'
+ ])
+ rb_cv_dlopen=yes],
+ [openbsd*|mirbsd*], [ : ${LDSHARED='$(CC) -shared ${CCDLFLAGS}'}
+ AS_IF([test "$rb_cv_binary_elf" = yes], [
+ LDFLAGS="$LDFLAGS -Wl,-E"
+ ])
+ rb_cv_dlopen=yes],
+ [darwin*], [ : ${LDSHARED='$(CC) -dynamic -bundle'}
+ : ${DLDSHARED='$(CC) -dynamiclib'}
+ : ${LDFLAGS=""}
+ : ${LIBPATHENV=DYLD_FALLBACK_LIBRARY_PATH}
+ : ${PRELOADENV=DYLD_INSERT_LIBRARIES}
+ rb_cv_dlopen=yes],
+ [aix*], [ : ${LDSHARED='$(CC)'}
+ AS_IF([test "$GCC" = yes], [
+ LDSHARED="$LDSHARED ${linker_flag}-G -shared"
+ ], [
+ LDSHARED="$LDSHARED ${linker_flag}-G"
+ ])
+ EXTDLDFLAGS='-e$(TARGET_ENTRY)'
+ XLDFLAGS="${linker_flag}"'-bE:$(ARCHFILE)'" ${linker_flag}-brtl"
+ XLDFLAGS="$XLDFLAGS ${linker_flag}-blibpath:${prefix}/lib:${LIBPATH:-/usr/lib:/lib}"
+ : ${ARCHFILE="ruby.imp"}
+ TRY_LINK='$(CC) -oconftest $(INCFLAGS) -I$(hdrdir) $(CPPFLAGS)'
+ TRY_LINK="$TRY_LINK"' $(CFLAGS) $(src) $(LIBPATH) $(LDFLAGS) $(LOCAL_LIBS) $(LIBS)'
+ : ${LIBPATHENV=LIBPATH}
+ : ${PRELOADENV=LDR_PRELOAD}
+ rb_cv_dlopen=yes],
+ [nto-qnx*], [ DLDFLAGS="$DLDFLAGS -L/lib -L/usr/lib -L/usr/local/lib"
+ : ${LDSHARED='$(LD) -Bshareable -x'}
+ LDFLAGS="$LDFLAGS -L/lib -L/usr/lib -L/usr/local/lib"
+ rb_cv_dlopen=yes],
+ [cygwin*|mingw*], [
+ : ${LDSHARED='$(CC) -shared'}
+ XLDFLAGS="$XLDFLAGS -Wl,--stack,0x00200000,--enable-auto-import"
+ DLDFLAGS="${DLDFLAGS} -Wl,--enable-auto-image-base,--enable-auto-import"
+ : ${LIBPATHENV=PATH}
+ : ${PRELOADENV=""}
+ rb_cv_dlopen=yes],
+ [hiuxmpp], [ : ${LDSHARED='$(LD) -r'}],
+ [atheos*], [ : ${LDSHARED='$(CC) -shared'}
+ rb_cv_dlopen=yes],
+ [ : ${LDSHARED='$(LD)'}])
+ AC_MSG_RESULT($rb_cv_dlopen)
+
+ AS_IF([test "$rb_cv_dlopen" = yes], [
+ AS_CASE(["$target_os"],
+ [darwin*], [
+ for flag in \
+ "-undefined dynamic_lookup" \
+ "-multiply_defined suppress" \
+ ; 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])
+ ])
+ 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}"],
+ [*'$(CC)'*], [
+ LDSHAREDXX=`echo "${LDSHARED}" | sed 's/\$(CC)/$(CXX)/'`
+ ],
+ [*'${CC}'*], [
+ LDSHAREDXX=`echo "${LDSHARED}" | sed 's/\${CC}/${CXX}/'`
+ ],
+ [*$CC*], [
+ LDSHAREDXX=`echo "${LDSHARED}" | sed "s|$CC|$CXX|"`
+ ],
+ [ld" "*], [
+ ])
+])
+AS_CASE([${RPATHFLAG}],[*'%1$'*],[: ${LIBPATHFLAG=' -L%1$-s'}],[: ${LIBPATHFLAG=' -L%s'}])
+
+AC_SUBST(LINK_SO)
+AC_SUBST(LIBPATHFLAG)
+AC_SUBST(RPATHFLAG)
+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])
+ AS_IF([test "x$ac_cv_lib_procstat_procstat_open_sysctl" = xyes], [
+ AC_CHECK_FUNCS(procstat_getvmmap)
+ ])
+ ])
+AS_CASE(["$target_cpu-$target_os"],
+[*-darwin*], [
+ AC_CHECK_HEADERS([execinfo.h])
+ AS_IF([test "x$ac_cv_header_execinfo_h" = xyes], [
+ AC_CHECK_LIB([execinfo], [backtrace])
+ AC_CHECK_HEADERS([libunwind.h])
+ ])],
+[*-freebsd*|x86_64-netbsd*], [
+ AC_CHECK_HEADERS([execinfo.h])
+ AS_IF([test "x$ac_cv_header_execinfo_h" = xyes], [
+ AC_CHECK_LIB([execinfo], [backtrace])
+ AC_CHECK_LIB([unwind], [unw_backtrace])
+ ])])
+AC_CHECK_FUNCS(backtrace)
+
+AS_IF([test "x$ac_cv_func_backtrace" = xyes], [
+ AC_CACHE_CHECK(for broken backtrace, rb_cv_broken_backtrace,
+ [AC_TRY_RUN([
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <signal.h>
+#include <execinfo.h>
+
+#define TRACE_SIZE 256
+
+void sigsegv(int signum, siginfo_t *info, void *ctx){
+ void *trace[TRACE_SIZE];
+ int n = backtrace(trace, TRACE_SIZE);
+ if (n > 0) {
+ /*fprintf(stdout, "backtrace:%d\n",n);*/
+ } else {
+ _exit(EXIT_FAILURE);
+ }
+ _exit(EXIT_SUCCESS);
+}
+int
+main(void)
+{
+ volatile int *a = NULL;
+ stack_t ss;
+ struct sigaction sa;
+
+ ss.ss_sp = malloc(16*1024);
+ if (ss.ss_sp == NULL) {
+ fprintf(stderr, "cannot allocate memory for sigaltstack\n");
+ return EXIT_FAILURE;
+ }
+ ss.ss_size = 16*1024;
+ ss.ss_flags = 0;
+ if (sigaltstack(&ss, NULL) == -1) {
+ fprintf(stderr, "sigaltstack failed\n");
+ return EXIT_FAILURE;
+ }
+ memset(&sa, 0, sizeof(struct sigaction));
+ sigemptyset(&sa.sa_mask);
+ sa.sa_sigaction = sigsegv;
+ sa.sa_flags |= SA_SIGINFO;
+ sa.sa_flags |= SA_ONSTACK;
+ sigaction(SIGSEGV, &sa, NULL);
+ a[0] = 1;
+ return EXIT_SUCCESS;
+}
+],
+ rb_cv_broken_backtrace=no,
+ rb_cv_broken_backtrace=yes,
+ rb_cv_broken_backtrace=no)])
+ AS_IF([test "$rb_cv_broken_backtrace" = yes], [
+ AC_DEFINE(BROKEN_BACKTRACE, 1)
+ ])
+])
+
+AC_ARG_WITH(valgrind,
+ AS_HELP_STRING([--without-valgrind],[disable valgrind memcheck support]),
+ [], with_valgrind=yes)
+AS_IF([test x$with_valgrind != xno],
+ [AC_CHECK_HEADERS(valgrind/memcheck.h)])
+
+dln_a_out_works=no
+AS_IF([test "$ac_cv_header_a_out_h" = yes], [
+ AS_IF([test "$with_dln_a_out" = yes || test "$rb_cv_dlopen" = unknown], [
+ cat confdefs.h > config.h
+ AC_CACHE_CHECK(whether matz's dln works, rb_cv_dln_a_out,
+ [AC_TRY_COMPILE([
+#define USE_DLN_A_OUT
+#include "dln.c"
+],
+ [],
+ rb_cv_dln_a_out=yes,
+ rb_cv_dln_a_out=no)])
+ AS_IF([test "$rb_cv_dln_a_out" = yes], [
+ dln_a_out_works=yes
+ AC_DEFINE(USE_DLN_A_OUT)
+ ])
+ ])
+])
+
+AS_IF([test "$dln_a_out_works" = yes], [
+ AS_IF([test "$GCC" = yes], [
+ STATIC=-static
+ ], [
+ STATIC=-Bstatic
+ ])
+ DLEXT=so
+ CCDLFLAGS=
+], [
+ AS_CASE(["$target_os"],
+ [hpux*], [
+ DLEXT=sl],
+ [darwin*], [
+ SOEXT=dylib
+ DLEXT=bundle],
+ [cygwin*|mingw*|*djgpp*], [
+ LOAD_RELATIVE=1
+ SOEXT=dll
+ DLEXT=so],
+ [
+ DLEXT=so])
+])
+: ${SOEXT="${DLEXT}"}
+AC_SUBST(SOEXT)
+AS_IF([test "$rb_cv_dlopen:$load_relative" = yes:yes], [
+ AS_IF([test "$ac_cv_func_dladdr" = yes], [
+ LOAD_RELATIVE=1
+ ])
+])
+AS_IF([test x"$LOAD_RELATIVE" = x1], [
+ load_relative=yes
+], [
+ unset load_relative
+])
+
+len=2 # .rb
+n=`expr "$DLEXT" : '.*'`; test "$n" -gt "$len" && len=$n
+n=`expr "$DLEXT2" : '.*'`; test "$n" -gt "$len" && len=$n
+AC_DEFINE_UNQUOTED(DLEXT_MAXLEN, `expr $len + 1`)
+test ".$DLEXT" = "." || AC_DEFINE_UNQUOTED(DLEXT, ".$DLEXT")
+test ".$DLEXT2" = "." || AC_DEFINE_UNQUOTED(DLEXT2, ".$DLEXT2")
+AC_SUBST(DLEXT)
+
+AS_IF([test "$with_dln_a_out" = yes], [
+ STRIP=true
+], [
+ AC_CHECK_TOOL(STRIP, strip, :)dnl
+])
+
+AS_CASE(["$target_os"],
+ [linux* | gnu* | k*bsd*-gnu | kopensolaris*-gnu], [
+ STRIP="$STRIP -S -x"],
+ [darwin*], [
+ STRIP="$STRIP -A -n"])
+
+AC_ARG_WITH(ext,
+ AC_HELP_STRING([--with-ext=EXTS],
+ [pass to --with-ext option of extmk.rb]))
+AC_ARG_WITH(out-ext,
+ AC_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])
+AS_IF([test -n "$setup"], [
+ AS_IF([! test -f "ext/$setup" -o -f "$srcdir/ext/$setup"], [
+ AC_MSG_ERROR(Setup file $setup not found under ext or $srcdir/ext)
+ ])
+], [test -f "$srcdir/ext/Setup.$target_os"], [
+ setup="Setup.$target_os"
+], [
+ setup=
+ for file in "$srcdir"/ext/Setup.*; do
+ AS_CASE(["$file"], [*~|*.bak|*.orig|*.rej|*.tmp], [continue])
+ setup=`basename "$file"`
+ AS_CASE(["$target_os"], [`expr "$setup" : 'Setup.\(.*\)'`*], [break])
+ platform=`sed '/^option *platform */!d;s///;s/|/*|/g;q' "$file"`
+ AS_IF([test "x$platform" != x], [
+ eval "AS_CASE([\"\$target_os\"], [$platform*], [break])"
+ ])
+ setup=
+ done
+ : ${setup:=Setup}
+])
+AC_SUBST(setup)
+
+rubylibprefix='${libdir}/${RUBY_BASE_NAME}'
+AC_ARG_WITH(rubylibprefix,
+ AS_HELP_STRING([--with-rubylibprefix=DIR], [prefix for ruby libraries [[LIBDIR/RUBY_BASE_NAME]]]),
+ [AS_IF([test "x$withval" = xno], [
+ AC_MSG_ERROR([No ruby, No libprefix])
+ ])
+ rubylibprefix="$withval"])
+AC_SUBST(rubylibprefix)
+
+AS_IF([test x"${exec_prefix}" != xNONE], [
+ RUBY_EXEC_PREFIX="$exec_prefix"
+], [test x"$prefix" != xNONE], [
+ RUBY_EXEC_PREFIX="$prefix"
+], [
+ RUBY_EXEC_PREFIX=$ac_default_prefix
+])
+pat=`echo "${RUBY_EXEC_PREFIX}" | tr -c '\012' .`'\(.*\)'
+for var in bindir libdir rubylibprefix; do
+ eval val='"$'$var'"'
+ AS_CASE(["$val"], ["${RUBY_EXEC_PREFIX}"*], [val='${exec_prefix}'"`expr \"$val\" : \"$pat\"`"])
+ eval $var='"$val"'
+done
+
+BTESTRUBY='$(MINIRUBY)'
+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"]']`
+ XRUBY_RUBYLIBDIR=`${RUBY-$BASERUBY} -rrbconfig -e ['puts RbConfig::CONFIG["rubylibdir"]']`
+ XRUBY_RUBYHDRDIR=`${RUBY-$BASERUBY} -rrbconfig -e ['puts RbConfig::CONFIG["rubyhdrdir"]']`
+ AC_SUBST(XRUBY_LIBDIR)
+ AC_SUBST(XRUBY_RUBYLIBDIR)
+ AC_SUBST(XRUBY_RUBYHDRDIR)
+ PREP='$(arch)-fake.rb'
+ RUNRUBY_COMMAND='$(MINIRUBY) -I`cd $(srcdir)/lib; pwd`'
+ RUNRUBY='$(RUNRUBY_COMMAND)'
+ XRUBY='$(MINIRUBY)'
+ BOOTSTRAPRUBY='$(BASERUBY)'
+ TEST_RUNNABLE=no
+ CROSS_COMPILING=yes
+], [
+ MINIRUBY='./miniruby$(EXEEXT) -I$(srcdir)/lib -I.'
+ MINIRUBY="$MINIRUBY"' -I$(EXTOUT)/common'
+ PREP='miniruby$(EXEEXT)'
+ RUNRUBY_COMMAND='$(MINIRUBY) $(srcdir)/tool/runruby.rb --extout=$(EXTOUT) $(RUNRUBYOPT)'
+ RUNRUBY='$(RUNRUBY_COMMAND) --'
+ XRUBY='$(RUNRUBY)'
+ BOOTSTRAPRUBY='$(MINIRUBY)'
+ TEST_RUNNABLE=yes
+ CROSS_COMPILING=no
+])
+AC_SUBST(TEST_RUNNABLE)
+AC_SUBST(CROSS_COMPILING)
+AC_SUBST(MINIRUBY)
+AC_SUBST(BTESTRUBY)
+AC_SUBST(PREP)
+AC_SUBST(RUNRUBY_COMMAND)
+AC_SUBST(RUNRUBY)
+AC_SUBST(XRUBY)
+AC_SUBST(BOOTSTRAPRUBY)
+AC_SUBST(EXTOUT, [${EXTOUT=.ext}])
+
+FIRSTMAKEFILE=""
+LIBRUBY_A='lib$(RUBY_SO_NAME)-static.a'
+LIBRUBY='$(LIBRUBY_A)'
+LIBRUBYARG_STATIC='-l$(RUBY_SO_NAME)-static'
+LIBRUBYARG='$(LIBRUBYARG_STATIC)'
+SOLIBS='$(MAINLIBS)'
+
+AS_CASE(["$target_os"],
+ [cygwin*|mingw*|haiku*|darwin*], [
+ : ${DLDLIBS=""}
+ ],
+ [
+ DLDLIBS="$DLDLIBS -lc"
+ ])
+
+AC_ARG_ENABLE(multiarch,
+ AS_HELP_STRING([--enable-multiarch], [enable multiarch compatible directories]),
+ [multiarch=], [unset multiarch])
+AS_IF([test ${multiarch+set}], [
+ AC_DEFINE(ENABLE_MULTIARCH)
+ MJIT_HEADER_INSTALL_DIR=include/'${arch}/${RUBY_VERSION_NAME}'
+], [
+ MJIT_HEADER_INSTALL_DIR=include/'${RUBY_VERSION_NAME}/${arch}'
+])
+
+archlibdir='${libdir}/${arch}'
+sitearchlibdir='${libdir}/${sitearch}'
+archincludedir='${includedir}/${arch}'
+sitearchincludedir='${includedir}/${sitearch}'
+
+AC_ARG_WITH(soname,
+ AS_HELP_STRING([--with-soname=SONAME], [base name of shared library]),
+ [RUBY_SO_NAME=$withval],
+ [
+ AS_CASE(["$target_os"],
+ [darwin*], [
+ RUBY_SO_NAME='$(RUBY_BASE_NAME).$(RUBY_API_VERSION)'
+ ],
+ [cygwin*], [
+ RUBY_SO_NAME='$(RUBY_BASE_NAME)$(MAJOR)$(MINOR)0'
+ ],
+ [mingw*], [
+ RUBY_SO_NAME="${rb_cv_msvcrt}"'-$(RUBY_BASE_NAME)$(MAJOR)$(MINOR)0'
+ AS_IF([test x"${target_cpu}" != xi386], [
+ RUBY_SO_NAME="${target_cpu}-${RUBY_SO_NAME}"
+ ])
+ ],
+ [RUBY_SO_NAME='$(RUBY_BASE_NAME)'])
+ ])
+
+LIBRUBY_LDSHARED=${DLDSHARED=${LDSHARED}}
+LIBRUBY_DLDFLAGS=$DLDFLAGS
+LIBRUBY_SO='lib$(RUBY_SO_NAME).$(SOEXT).$(RUBY_PROGRAM_VERSION)'
+LIBRUBY_SONAME='lib$(RUBY_SO_NAME).$(SOEXT).$(RUBY_API_VERSION)'
+LIBRUBY_ALIASES='lib$(RUBY_SO_NAME).$(SOEXT)'
+ENABLE_SHARED=no
+
+AC_ARG_ENABLE(shared,
+ AS_HELP_STRING([--enable-shared], [build a shared library for Ruby]),
+ [enable_shared=$enableval])
+libprefix=${multiarch+'$(archlibdir)'}${multiarch-'$(libdir)'}
+LIBRUBY_RELATIVE=${load_relative-no}
+AS_CASE("$enable_shared", [yes], [
+ LIBRUBY='$(LIBRUBY_SO)'
+ LIBRUBYARG_SHARED='-l$(RUBY_SO_NAME)'
+ LIBRUBYARG='$(LIBRUBYARG_SHARED)'
+ LIBRUBY_RELATIVE=no
+ test -z "$CCDLFLAGS" || CFLAGS="$CFLAGS $CCDLFLAGS"
+ ENABLE_SHARED=yes
+
+ # libdir can be overridden in config.site file (on OpenSUSE at least).
+ libdir_basename=lib
+ AS_IF([test "$bindir" = '${exec_prefix}/bin'], [
+ AS_CASE(["$libdir"], ['${exec_prefix}/'*], [libdir_basename=`basename "$libdir"`])
+ ])
+ AC_DEFINE_UNQUOTED(LIBDIR_BASENAME, ["${libdir_basename}"])
+ libdir_basename="${libdir_basename}"${multiarch+'/${arch}'}
+
+ AS_CASE(["$target_os"],
+ [freebsd*|dragonfly*], [],
+ [
+ AS_IF([test "$GCC" = yes], [
+ RUBY_TRY_LDFLAGS([${linker_flag}--no-undefined], [no_undefined=yes], [no_undefined=no])
+ AS_IF([test "no_undefined" = yes], [
+ RUBY_APPEND_OPTION(EXTLDFLAGS, [${linker_flag}--no-undefined])
+ ])
+ ])
+ ])
+
+ AS_CASE(["$target_os"],
+ [sunos4*], [
+ LIBRUBY_ALIASES='$(LIBRUBY_SONAME) lib$(RUBY_SO_NAME).$(SOEXT)'
+ ],
+ [linux* | gnu* | k*bsd*-gnu | atheos* | kopensolaris*-gnu | haiku*], [
+ RUBY_APPEND_OPTIONS(LIBRUBY_DLDFLAGS, ['-Wl,-soname,$(LIBRUBY_SONAME)' "$LDFLAGS_OPTDIR"])
+ LIBRUBY_ALIASES='$(LIBRUBY_SONAME) lib$(RUBY_SO_NAME).$(SOEXT)'
+ AS_IF([test "$load_relative" = yes], [
+ libprefix="'\$\${ORIGIN}/../${libdir_basename}'"
+ LIBRUBY_RPATHFLAGS="-Wl,-rpath,${libprefix}"
+ LIBRUBY_RELATIVE=yes
+ ])
+ ],
+ [freebsd*|dragonfly*], [
+ LIBRUBY_SO='lib$(RUBY_SO_NAME).$(SOEXT).$(MAJOR)$(MINOR)'
+ LIBRUBY_SONAME='$(LIBRUBY_SO)'
+ AS_IF([test "$rb_cv_binary_elf" != "yes" ], [
+ LIBRUBY_SO="$LIBRUBY_SO.\$(TEENY)"
+ LIBRUBY_ALIASES=''
+ ])
+ ],
+ [netbsd*], [
+ LIBRUBY_SONAME='lib$(RUBY_SO_NAME).$(SOEXT).$(MAJOR)$(MINOR)'
+ LIBRUBY_SO="${LIBRUBY_SONAME}"'.$(TEENY)'
+ RUBY_APPEND_OPTIONS(LIBRUBY_DLDFLAGS, ['-Wl,-soname,$(LIBRUBY_SONAME)' "$LDFLAGS_OPTDIR"])
+ AS_IF([test "$rb_cv_binary_elf" = yes], [ # ELF platforms
+ LIBRUBY_ALIASES='$(LIBRUBY_SONAME) lib$(RUBY_SO_NAME).$(SOEXT)'
+ ], [ # a.out platforms
+ LIBRUBY_ALIASES=""
+ ])
+ ],
+ [openbsd*|mirbsd*], [
+ LIBRUBY_SO='lib$(RUBY_SO_NAME).$(SOEXT).$(MAJOR).'`expr ${MINOR} \* 10 + ${TEENY}`
+ ],
+ [solaris*], [
+ LIBRUBY_SO='lib$(RUBY_SO_NAME).$(SOEXT).$(MAJOR)'
+ LIBRUBY_SONAME='lib$(RUBY_SO_NAME).$(SOEXT).$(RUBY_PROGRAM_VERSION)'
+ LIBRUBY_ALIASES='$(LIBRUBY_SONAME) lib$(RUBY_SO_NAME).$(SOEXT)'
+ RUBY_APPEND_OPTIONS(LIBRUBY_DLDFLAGS, ["${linker_flag}-h${linker_flag:+,}"'$(@F)'])
+ XLDFLAGS="$XLDFLAGS "'-R${libdir}'
+ ],
+ [hpux*], [
+ XLDFLAGS="$XLDFLAGS "'-Wl,+s,+b,$(libdir)'
+ LIBRUBY_ALIASES='$(LIBRUBY_SONAME) lib$(RUBY_SO_NAME).$(SOEXT)'
+ ],
+ [aix*], [
+ RUBY_APPEND_OPTIONS(LIBRUBY_DLDFLAGS, ["${linker_flag}-bnoentry" "$XLDFLAGS" "$LDFLAGS_OPTDIR"])
+ LIBRUBYARG_SHARED='-L${libdir} -l${RUBY_SO_NAME}'
+ LIBS="$LIBS -lm -lc"
+ ],
+ [darwin*], [
+ LIBRUBY_SO='lib$(RUBY_SO_NAME).$(SOEXT)'
+ LIBRUBY_SONAME='$(LIBRUBY_SO)'
+ LIBRUBY_ALIASES='lib$(RUBY_INSTALL_NAME).$(SOEXT)'
+ AS_IF([test "$load_relative" = yes], [
+ libprefix="@executable_path/../${libdir_basename}"
+ LIBRUBY_RELATIVE=yes
+ ])
+ LIBRUBY_DLDFLAGS="$LIBRUBY_DLDFLAGS -install_name ${libprefix}"'/$(LIBRUBY_SONAME)'
+ LIBRUBY_DLDFLAGS="$LIBRUBY_DLDFLAGS "'-compatibility_version $(RUBY_API_VERSION)'
+ LIBRUBY_DLDFLAGS="$LIBRUBY_DLDFLAGS "'-current_version $(RUBY_PROGRAM_VERSION)'
+ AS_IF([test "$visibility_option" = ld], [
+ LIBRUBY_DLDFLAGS="$LIBRUBY_DLDFLAGS "'-Wl,-unexported_symbol,_Init_*'
+ LIBRUBY_DLDFLAGS="$LIBRUBY_DLDFLAGS "'-Wl,-unexported_symbol,_ruby_static_id_*'
+ LIBRUBY_DLDFLAGS="$LIBRUBY_DLDFLAGS "'-Wl,-unexported_symbol,*_threadptr_*'
+ ])
+ LIBRUBY_DLDFLAGS="$LIBRUBY_DLDFLAGS "' $(XLDFLAGS)'
+ ],
+ [interix*], [
+ LIBRUBYARG_SHARED='-L. -L${libdir} -l$(RUBY_SO_NAME)'
+ ],
+ [mingw*|cygwin*|mswin*], [
+ LIBRUBY_RELATIVE=yes
+ ])
+], [
+ LIBRUBYARG_SHARED=
+
+ # enable PIE if possible
+ AC_ARG_ENABLE(pie,
+ AS_HELP_STRING([--disable-pie], [disable PIE feature]),
+ [pie=$enableval], [pie=])
+ AS_IF([test "$GCC" = yes -a -z "$EXTSTATIC" -a "x$pie" != xno], [
+ RUBY_TRY_CFLAGS(-fPIE, [pie=yes], [pie=no])
+ AS_IF([test "$pie" = yes], [
+ # Use -fPIE when testing -pie. RUBY_TRY_LDFLAGS sets
+ # $save_CFLAGS internally, so set other name here.
+ save_CFLAGS_before_pie="$CFLAGS"
+ CFLAGS="$CFLAGS -fPIE"
+
+ # gcc need -pie but clang need -Wl,-pie.
+ for pie in -pie -Wl,-pie; do
+ RUBY_TRY_LDFLAGS([$pie], [], [pie=])
+ AS_IF([test "x$pie" != x], [
+ RUBY_APPEND_OPTION(XCFLAGS, -fPIE)
+ RUBY_APPEND_OPTION(XLDFLAGS, $pie)
+ break
+ ])
+ done
+ CFLAGS="$save_CFLAGS_before_pie"
+ ])
+ ])
+])
+AS_IF([test "$enable_rpath" = yes], [
+ test -z "$LIBRUBY_RPATHFLAGS" || LIBRUBY_RPATHFLAGS="$LIBRUBY_RPATHFLAGS "
+ rpathflag="${RPATHFLAG}"
+ AS_CASE(["${cross_compiling}${load_relative}"], [*yes*], [], [rpathflag="$RPATHFLAG$LIBPATHFLAG"])
+ rpathflag=`IFS="$PATH_SEPARATOR"
+ echo x "$rpathflag" |
+ sed "s/^x *//;s${IFS}"'%1\\$-s'"${IFS}${libprefix}${IFS}g;s${IFS}%s${IFS}${libprefix}${IFS}g"
+ `
+ LIBRUBY_RPATHFLAGS="$LIBRUBY_RPATHFLAGS${rpathflag}"
+ LIBRUBYARG_SHARED="$LIBRUBY_RPATHFLAGS $LIBRUBYARG_SHARED"
+ LIBRUBYARG_STATIC="$LIBRUBY_RPATHFLAGS $LIBRUBYARG_STATIC"
+])
+AC_SUBST(LIBRUBY_RELATIVE)
+
+LDFLAGS="-L. $LDFLAGS"
+AC_SUBST(ARCHFILE)
+
+AS_IF([test "$EXEEXT" = .exe], [
+ EXECUTABLE_EXTS='".exe",".com",".cmd",".bat"'
+ AC_DEFINE_UNQUOTED(EXECUTABLE_EXTS, $EXECUTABLE_EXTS)
+ EXECUTABLE_EXTS=`echo $EXECUTABLE_EXTS | tr -d '"' | tr , ' '`
+ AC_SUBST(EXECUTABLE_EXTS)
+])
+
+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_MSG_RESULT(yes)]
+ )
+])
+
+AC_ARG_ENABLE(dtrace,
+ AS_HELP_STRING([--enable-dtrace],
+ [enable DTrace for tracing inside ruby. enabled by default on systems having dtrace]),
+ [enable_dtrace=$enableval], [enable_dtrace=auto])
+
+LIBRUBY_A_OBJS='$(OBJS)'
+DTRACE_REBUILD=
+AS_CASE(["${enable_dtrace}"],
+[yes|auto], [
+ RUBY_DTRACE_AVAILABLE()
+], [
+ rb_cv_dtrace_available=no
+])
+AS_IF([test "${enable_dtrace}" = yes], [dnl
+ AS_IF([test -z "$DTRACE"], [dnl
+ AC_MSG_ERROR([dtrace(1) is missing])
+ ], [test "$cross_compiling" = yes], [dnl
+ AC_MSG_ERROR([--enable-dtrace, however, cross compiling])
+ ], [test "${rb_cv_dtrace_available}" = "no"], [dnl
+ AC_MSG_ERROR([--enable-dtrace, however, USDT is not available])
+ ])
+])
+AS_CASE([$rb_cv_dtrace_available],
+[yes*], [dnl
+ RUBY_DTRACE_POSTPROCESS()
+ AS_IF([test "$rb_cv_prog_dtrace_g" != no], [dnl
+ DTRACE_OBJ='probes.$(OBJEXT)'
+ ])
+ AS_IF([test "$rb_cv_prog_dtrace_g" = rebuild], [dnl
+ DTRACE_REBUILD=yes
+ LIBRUBY_A_OBJS='$(DTRACE_GLOMMED_OBJ)'
+ ])
+ AS_CASE("${target_os}", [freebsd*], [dnl
+ # FreeBSD's dtrace requires libelf
+ LIBS="-lelf $LIBS"
+ ])
+ DTRACE_EXT=d
+], [dnl
+ enable_dtrace=no
+ DTRACE_EXT=dmyh
+])
+AC_SUBST(DTRACE_EXT)
+AC_SUBST(DTRACE_OBJ)
+AC_SUBST(DTRACE_REBUILD)
+AC_SUBST(DTRACE_OPT)
+AC_SUBST(LIBRUBY_A_OBJS)
+
+AC_ARG_ENABLE(gcov,
+ AS_HELP_STRING([--enable-gcov], [enable coverage measurement by gcov]),
+ [gcov=yes])
+AS_IF([test x"$gcov" = xyes], [
+ CFLAGS="$CFLAGS -coverage"
+ LDFLAGS="$LDFLAGS -coverage"
+])
+
+RUBY_SETJMP_TYPE
+}
+
+: "build section" && {
+dnl build rdoc index if requested
+RDOCTARGET=""
+CAPITARGET=""
+AC_ARG_ENABLE(install-doc,
+ AS_HELP_STRING([--disable-install-doc], [do not install either rdoc indexes or C API documents during install]),
+ [install_doc=$enableval], [install_doc=yes])
+AC_ARG_ENABLE(install-rdoc,
+ AS_HELP_STRING([--disable-install-rdoc], [do not install rdoc indexes during install]),
+ [install_rdoc=$enableval], [install_rdoc=yes])
+AC_ARG_ENABLE(install-capi,
+ AS_HELP_STRING([--disable-install-capi], [do not install C API documents during install]),
+ [install_capi=$enableval], [install_capi=no])
+
+AS_IF([test "$install_doc" != no], [
+ AS_IF([test "$install_rdoc" != no], [
+ RDOCTARGET="rdoc"
+ ], [
+ RDOCTARGET="nodoc"
+ ])
+ AS_IF([test "$install_capi" != no -a -n "$DOXYGEN"], [
+ CAPITARGET="capi"
+ ], [
+ CAPITARGET="nodoc"
+ ])
+], [
+ RDOCTARGET="nodoc"
+ CAPITARGET="nodoc"
+])
+
+AC_SUBST(RDOCTARGET)
+AC_SUBST(CAPITARGET)
+
+AS_CASE(["$RDOCTARGET:$CAPITARGET"],[nodoc:nodoc],[INSTALLDOC=nodoc],[INSTALLDOC=all])
+AC_SUBST(INSTALLDOC)
+
+AC_ARG_ENABLE(jit-support,
+ AS_HELP_STRING([--disable-jit-support], [disable JIT features]),
+ [MJIT_SUPPORT=$enableval
+ AS_IF([test x"$enable_jit_support" = "xyes"],
+ [AC_DEFINE(USE_MJIT, 1)],
+ [AC_DEFINE(USE_MJIT, 0)])],
+ [MJIT_SUPPORT=yes
+ AC_DEFINE(USE_MJIT, 1)])
+
+AC_SUBST(MJIT_SUPPORT)
+
+AC_ARG_ENABLE(install-static-library,
+ AS_HELP_STRING([--disable-install-static-library], [do not install static ruby library]),
+ [INSTALL_STATIC_LIBRARY=$enableval],
+ AS_IF([test x"$enable_shared" = xyes],
+ [INSTALL_STATIC_LIBRARY=no],
+ [INSTALL_STATIC_LIBRARY=yes]))
+AC_SUBST(INSTALL_STATIC_LIBRARY)
+
+AS_IF([test "$rb_with_pthread" = "yes"], [
+ THREAD_MODEL=pthread
+])
+AC_CACHE_CHECK([for prefix of external symbols], rb_cv_symbol_prefix, [
+ AC_TRY_COMPILE([extern void conftest_external(void) {}], [], [
+ rb_cv_symbol_prefix=`$NM conftest.$ac_objext |
+ sed -n ['/.*T[ ]\([^ ]*\)conftest_external.*/!d;s//\1/p;q']`
+ ],
+ [rb_cv_symbol_prefix=''])
+ test -n "$rb_cv_symbol_prefix" || rb_cv_symbol_prefix=NONE
+])
+SYMBOL_PREFIX="$rb_cv_symbol_prefix"
+test "x$SYMBOL_PREFIX" = xNONE && SYMBOL_PREFIX=''
+DLNOBJ=dln.o
+AC_ARG_ENABLE(dln,
+ AC_HELP_STRING([--disable-dln], [disable dynamic link feature]),
+ [test "$enableval" = yes || DLNOBJ=dmydln.o])
+AC_SUBST(DLNOBJ)
+MINIDLNOBJ=dmydln.o
+
+AS_CASE(["$target_os"],
+ [linux*], [
+ ],
+ [netbsd*], [
+ RUBY_APPEND_OPTION(CFLAGS, -pipe)
+ ],
+ [darwin*], [
+ RUBY_APPEND_OPTION(CFLAGS, -pipe)
+ AC_COMPILE_IFELSE([
+ AC_LANG_BOOL_COMPILE_TRY([@%:@include <AvailabilityMacros.h>],
+ [MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7])],
+ [dnl
+ RUBY_APPEND_OPTION(XLDFLAGS, [-framework Security])
+ RUBY_APPEND_OPTION(LIBRUBYARG_STATIC, [-framework Security])
+ ]dnl
+ )
+ RUBY_APPEND_OPTION(XLDFLAGS, [-framework Foundation])
+ RUBY_APPEND_OPTION(LIBRUBYARG_STATIC, [-framework Foundation])
+ ],
+ [osf*], [
+ AS_IF([test "$GCC" != "yes" ], [
+ # compile something small: taint.c is fine for this.
+ # the main point is the '-v' flag of 'cc'.
+ AS_CASE(["`cc -v -I. -c main.c -o /tmp/main.o 2>&1`"],
+ [*/gemc_cc*], [ # we have the new DEC GEM CC
+ CFLAGS="$CFLAGS -oldc"
+ ],
+ [ # we have the old MIPS CC
+ ])
+ # cleanup
+ rm -f /tmp/main.o
+ CFLAGS="$CFLAGS -std"
+ ])
+ ],
+ [cygwin*|mingw*], [
+ LIBRUBY_DLDFLAGS="${LIBRUBY_DLDFLAGS}"' -Wl,--out-implib=$(LIBRUBY)'
+ AS_CASE(["$target_os"],
+ [cygwin*], [
+ AS_IF([test x"$enable_shared" = xyes], [
+ LIBRUBY_SO='cyg$(RUBY_SO_NAME)'.dll
+ LIBRUBY_DLDFLAGS="${LIBRUBY_DLDFLAGS}"' $(RUBYDEF)'
+ ])
+ ],
+ [mingw*], [
+ AS_IF([test x"$enable_shared" = xyes], [
+ LIBRUBY_SO='$(RUBY_SO_NAME)'.dll
+ LIBRUBY_DLDFLAGS="${LIBRUBY_DLDFLAGS}"' $(RUBYDEF)'
+ ])
+ EXPORT_PREFIX=' '
+ DLDFLAGS="${DLDFLAGS}"' $(DEFFILE)'
+ AC_LIBOBJ([win32/win32])
+ AC_LIBOBJ([win32/file])
+ COMMON_LIBS=m
+# COMMON_MACROS="WIN32_LEAN_AND_MEAN="
+ COMMON_HEADERS="winsock2.h windows.h"
+ THREAD_MODEL=win32
+ PLATFORM_DIR=win32
+ ])
+ LIBRUBY_ALIASES=''
+ FIRSTMAKEFILE=GNUmakefile:cygwin/GNUmakefile.in
+ AS_IF([test x"$enable_shared" = xyes], [
+ LIBRUBY='lib$(RUBY_SO_NAME).dll.a'
+ ], [
+ LIBRUBY_SO=dummy
+ LIBRUBY='lib$(RUBY_SO_NAME).a'
+ LIBRUBYARG='-l$(RUBY_SO_NAME)'
+ ])
+ ],
+ [hpux*], [
+ AS_CASE(["$YACC"],[*yacc*], [
+ XCFLAGS="$XCFLAGS -DYYMAXDEPTH=300"
+ YACC="$YACC -Nl40000 -Nm40000"
+ ])
+])
+
+MINIOBJS="$MINIDLNOBJ"
+
+AS_CASE(["$THREAD_MODEL"],
+[pthread], [AC_CHECK_HEADERS(pthread.h)],
+[win32], [],
+[""], [AC_MSG_ERROR(thread model is missing)],
+ [AC_MSG_ERROR(unknown thread model $THREAD_MODEL)])
+
+AC_ARG_ENABLE(debug-env,
+ AS_HELP_STRING([--enable-debug-env], [enable RUBY_DEBUG environment variable]),
+ [AC_SUBST(ENABLE_DEBUG_ENV, yes)])
+
+AS_CASE(["$FIRSTMAKEFILE"], [*GNUmakefile:*], [gnumake=yes], [
+ AC_MSG_CHECKING([if ${MAKE-make} is GNU make])
+ mkdir conftest.dir
+ echo "all:; @echo yes" > conftest.dir/GNUmakefile
+ echo "all:; @echo no" > conftest.dir/Makefile
+ gnumake=`(cd conftest.dir; ${MAKE-make})`
+ rm -fr conftest.dir
+ AS_CASE(["$gnumake"],
+ [*yes*], [
+ FIRSTMAKEFILE=GNUmakefile:template/GNUmakefile.in
+ gnumake=yes],
+ [
+ gnumake=no])
+ AC_MSG_RESULT($gnumake)
+])
+AS_IF([test "$gnumake" = yes], [ NULLCMD=: ], [
+ AC_MSG_CHECKING([for safe null command for ${MAKE-make}])
+ mkdir conftest.dir
+ NULLCMD=
+ for cmd in : true; do
+ echo 'A=1' > conftest.dir/Makefile
+ echo 'B=$(A:1=@'$cmd')' >> conftest.dir/Makefile
+ echo 'all:; $B 1 2 3 4 5 6 7 8 9' >> conftest.dir/Makefile
+ AS_IF([(cd conftest.dir; ${MAKE-make} >/dev/null 2>/dev/null)], [
+ NULLCMD=$cmd
+ break
+ ])
+ done
+ rm -fr conftest.dir
+ AS_IF([test -z "$NULLCMD"], [
+ AC_MSG_ERROR(no candidate for safe null command)
+ ])
+ AC_MSG_RESULT($NULLCMD)
+])
+AC_SUBST(NULLCMD)
+
+AS_IF([test "${universal_binary-no}" = yes ], [
+ AC_CACHE_CHECK([for architecture macros], rb_cv_architecture_macros, [
+ mv confdefs.h confdefs1.h
+ : > confdefs.h
+ AC_TRY_COMPILE([@%:@if defined __`echo ${universal_archnames} |
+ sed 's/=[^ ]*//g;s/ /__ || defined __/g'`__
+@%:@else
+@%:@error
+>>>>>><<<<<<
+@%:@endif], [],
+[
+ rb_cv_architecture_macros=yes
+ mv -f confdefs1.h confdefs.h
+], [
+ rb_cv_architecture_macros=no
+ archflagpat=`eval echo '"'"${ARCH_FLAG}"'"' | sed 's/[[][|.*]]/\\&/g'`
+ new_cflags=`echo "$CFLAGS" | sed "s|$archflagpat"'||'`
+ for archs in ${universal_archnames}; do
+ cpu=${archs@%:@*=}
+ archs=${archs%=*}
+ CFLAGS="$new_cflags -arch $archs"
+ archs="__${archs}__"
+ AC_MSG_CHECKING([for macro ${archs} on ${cpu}])
+ AC_TRY_COMPILE([@%:@ifndef ${archs}
+@%:@error
+@%:@endif], [], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])])
+ done
+ mv -f confdefs1.h confdefs.h
+ AC_MSG_ERROR([failed])
+ ])])
+ AC_CACHE_CHECK(whether __ARCHITECTURE__ is available, rb_cv_architecture_available,
+ AC_TRY_COMPILE([@%:@include <stdio.h>
+ const char arch[[]] = __ARCHITECTURE__;], [puts(arch);],
+ [rb_cv_architecture_available=yes], [rb_cv_architecture_available=no]))
+])
+
+: ${MJIT_LDSHARED=`echo "$LDSHARED" | sed ['s|\$(LD)|'"${LD}"'|g;s|\$(CC)|$(MJIT_CC)|g']`}
+
+MAINLIBS="$LIBS"
+LIBS=$ORIG_LIBS
+AS_IF([test -n "${LIBS}"], [
+ libspat=`echo "${LIBS}" | sed 's/[[][|.*$^]]/\\&/g;s/^ */ /;s/^ *$/ /'`
+ MAINFLAGS=`echo " $MAINLIBS " | sed "s|$libspat"'||;s/^ *//;s/ *$//'`
+])
+LIBRUBYARG_STATIC="${LIBRUBYARG_STATIC} \$(MAINLIBS)"
+CPPFLAGS="$CPPFLAGS "'$(DEFS)'
+test -z "$CPPFLAGS" || CPPFLAGS="$CPPFLAGS "; CPPFLAGS="$CPPFLAGS"'${cppflags}'
+AS_IF([test -n "${cflags+set}"], [
+ cflagspat=`eval echo '"'"${cflags}"'"' | sed 's/[[][|.*]]/\\&/g;s/^ */ /;s/^ *$/ /'`
+ CFLAGS=`echo " $CFLAGS " | sed "s|$cflagspat"'|${cflags}|;s/^ *//;s/ *$//'`
+])
+AS_IF([test -n "${cxxflags+set}"], [
+ cxxflagspat=`eval echo '"'"${cxxflags}"'"' | sed 's/[[][|.*]]/\\&/g;s/^ */ /;s/^ *$/ /'`
+ CXXFLAGS=`echo " $CXXFLAGS " | sed "s|$cxxflagspat"'|${cxxflags}|;s/^ *//;s/ *$//'`
+])
+AS_IF([test "${ARCH_FLAG}"], [
+ archflagpat=`eval echo '"'"${ARCH_FLAG}"'"' | sed 's/[[][|.*]]/\\&/g'`
+ CFLAGS=`echo "$CFLAGS" | sed "s| *$archflagpat"'||'`
+ CXXFLAGS=`echo "$CXXFLAGS" | sed "s| *$archflagpat"'||'`
+ LDFLAGS=`echo "$LDFLAGS" | sed "s| *$archflagpat"'||'`
+])
+warnflags="$rb_cv_warnflags"
+AC_SUBST(cppflags)dnl
+AC_SUBST(cflags, ["${orig_cflags:+$orig_cflags }"'${optflags} ${debugflags} ${warnflags}'])dnl
+AC_SUBST(cxxflags, ["${orig_cxxflags:+$orig_cxxflags }"'${optflags} ${debugflags} ${warnflags}'])dnl
+AC_SUBST(optflags)dnl
+AC_SUBST(debugflags)dnl
+AC_SUBST(warnflags)dnl
+AC_SUBST(strict_warnflags)dnl
+AC_SUBST(XCFLAGS)dnl
+AC_SUBST(XLDFLAGS)dnl
+AC_SUBST(EXTLDFLAGS)dnl
+AC_SUBST(EXTDLDFLAGS)dnl
+AC_SUBST(LIBRUBY_LDSHARED)
+AC_SUBST(LIBRUBY_DLDFLAGS)
+AC_SUBST(RUBY_INSTALL_NAME)
+AC_SUBST(rubyw_install_name)
+AC_SUBST(RUBYW_INSTALL_NAME)
+AC_SUBST(RUBY_SO_NAME)
+AC_SUBST(LIBRUBY_A)
+AC_SUBST(LIBRUBY_SO)
+AC_SUBST(LIBRUBY_SONAME)
+AC_SUBST(LIBRUBY_ALIASES)
+AC_SUBST(LIBRUBY)
+AC_SUBST(LIBRUBYARG)
+AC_SUBST(LIBRUBYARG_STATIC)
+AC_SUBST(LIBRUBYARG_SHARED)
+AC_SUBST(SOLIBS)
+AC_SUBST(DLDLIBS)
+AC_SUBST(DLDSHARED)
+AC_SUBST(ENABLE_SHARED)
+AC_SUBST(MAINLIBS)
+AC_SUBST(COMMON_LIBS)
+AC_SUBST(COMMON_MACROS)
+AC_SUBST(COMMON_HEADERS)
+AC_SUBST(EXPORT_PREFIX)
+AC_SUBST(SYMBOL_PREFIX)
+AC_SUBST(MINIOBJS)
+AC_SUBST(THREAD_MODEL)
+AC_SUBST(PLATFORM_DIR)
+
+firstmf=`echo $FIRSTMAKEFILE | sed 's/:.*//'`
+firsttmpl=`echo $FIRSTMAKEFILE | sed 's/.*://'`
+MAKEFILES="Makefile $firstmf"
+MAKEFILES="`echo $MAKEFILES`"
+AC_SUBST(MAKEFILES)
+
+ri_prefix=
+test "$program_prefix" != NONE &&
+ ri_prefix=$program_prefix
+
+ri_suffix=
+test "$program_suffix" != NONE &&
+ ri_suffix=$program_suffix
+
+RUBY_INSTALL_NAME="${ri_prefix}"'$(RUBY_BASE_NAME)'"${ri_suffix}"
+AS_CASE(["$target_os"],
+ [cygwin*|mingw*], [
+ RUBYW_INSTALL_NAME="${ri_prefix}"'$(RUBYW_BASE_NAME)'"${ri_suffix}"
+ rubyw_install_name='$(RUBYW_INSTALL_NAME)'
+ ])
+
+rubylibdir='${rubylibprefix}/${ruby_version}'
+rubyarchdir=${multiarch+'${rubyarchprefix}/${ruby_version}'}${multiarch-'${rubylibdir}/${arch}'}
+
+rubyarchprefix=${multiarch+'${archlibdir}/${RUBY_BASE_NAME}'}${multiarch-'${rubylibprefix}/${arch}'}
+AC_ARG_WITH(rubyarchprefix,
+ AS_HELP_STRING([--with-rubyarchprefix=DIR],
+ [prefix for architecture dependent ruby libraries [[RUBYLIBPREFIX/ARCH]]]),
+ [rubyarchprefix="$withval"])
+AC_SUBST(rubyarchprefix)
+
+rubysitearchprefix=${multiarch+'${sitearchlibdir}/${RUBY_BASE_NAME}'}${multiarch-'${rubylibprefix}/${sitearch}'}
+AC_ARG_WITH(rubysitearchprefix,
+ AS_HELP_STRING([--with-rubysitearchprefix=DIR],
+ [prefix for architecture dependent site libraries [[RUBYLIBPREFIX/SITEARCH]]]),
+ [rubysitearchprefix="$withval"])
+AC_SUBST(rubysitearchprefix)
+
+RI_BASE_NAME=`echo ${RUBY_BASE_NAME} | sed 's/ruby/ri/'`
+ridir='${datarootdir}/${RI_BASE_NAME}'
+AC_ARG_WITH(ridir,
+ AS_HELP_STRING([--with-ridir=DIR], [ri documentation [[DATAROOTDIR/ri]]]),
+ [ridir=$withval])
+AC_SUBST(ridir)
+AC_SUBST(RI_BASE_NAME)
+
+AC_ARG_WITH(ruby-version,
+ AS_HELP_STRING([--with-ruby-version=STR], [ruby version string for version specific directories [[full]] (full|minor|STR)]),
+ [ruby_version=$withval],
+ [ruby_version=full])
+unset RUBY_LIB_VERSION
+unset RUBY_LIB_VERSION_STYLE
+AS_CASE(["$ruby_version"],
+ [full], [RUBY_LIB_VERSION_STYLE='3 /* full */'],
+ [minor], [RUBY_LIB_VERSION_STYLE='2 /* minor */'])
+AS_IF([test ${RUBY_LIB_VERSION_STYLE+set}], [
+ {
+ echo "#define RUBY_LIB_VERSION_STYLE $RUBY_LIB_VERSION_STYLE"
+ echo '#define STRINGIZE(x) x'
+ test -f revision.h -o -f "${srcdir}/revision.h" || echo '#define RUBY_REVISION 0'
+ echo '#include "version.h"'
+ echo 'ruby_version=RUBY_LIB_VERSION'
+ } > conftest.c
+ ruby_version="`$CPP -I. -I"${srcdir}" -I"${srcdir}/include" conftest.c | sed '/^ruby_version=/!d;s/ //g'`"
+ eval $ruby_version
+], [test -z "${ruby_version}"], [
+ AC_MSG_ERROR([No ruby version, No place for bundled libraries])
+], [
+ RUBY_LIB_VERSION="${ruby_version}"
+])
+AC_SUBST(RUBY_LIB_VERSION_STYLE)
+AC_SUBST(RUBY_LIB_VERSION)
+
+AC_ARG_WITH(sitedir,
+ AS_HELP_STRING([--with-sitedir=DIR], [site libraries in DIR [[RUBY_LIB_PREFIX/site_ruby]], "no" to disable site directory]),
+ [sitedir=$withval],
+ [sitedir='${rubylibprefix}/site_ruby'])
+sitelibdir='${sitedir}/${ruby_version}'
+
+AC_ARG_WITH(sitearchdir,
+ AS_HELP_STRING([--with-sitearchdir=DIR],
+ [architecture dependent site libraries in DIR [[SITEDIR/SITEARCH]], "no" to disable site directory]),
+ [sitearchdir=$withval],
+ [sitearchdir=${multiarch+'${rubysitearchprefix}/site_ruby/${ruby_version}'}${multiarch-'${sitelibdir}/${sitearch}'}])
+
+AC_ARG_WITH(vendordir,
+ AS_HELP_STRING([--with-vendordir=DIR], [vendor libraries in DIR [[RUBY_LIB_PREFIX/vendor_ruby]], "no" to disable vendor directory]),
+ [vendordir=$withval],
+ [vendordir='${rubylibprefix}/vendor_ruby'])
+vendorlibdir='${vendordir}/${ruby_version}'
+
+AC_ARG_WITH(vendorarchdir,
+ AS_HELP_STRING([--with-vendorarchdir=DIR],
+ [architecture dependent vendor libraries in DIR [[VENDORDIR/SITEARCH]], "no" to disable vendor directory]),
+ [vendorarchdir=$withval],
+ [vendorarchdir=${multiarch+'${rubysitearchprefix}/vendor_ruby/${ruby_version}'}${multiarch-'${vendorlibdir}/${sitearch}'}])
+
+AS_IF([test "${LOAD_RELATIVE+set}"], [
+ AC_DEFINE_UNQUOTED(LOAD_RELATIVE, $LOAD_RELATIVE)
+ RUBY_EXEC_PREFIX=''
+])
+
+AC_SUBST(RUBY_EXEC_PREFIX)
+
+AC_SUBST(libdirname, ${multiarch+arch}libdir)
+AC_SUBST(archlibdir)dnl
+AC_SUBST(sitearchlibdir)dnl
+AC_SUBST(archincludedir)dnl
+AC_SUBST(sitearchincludedir)dnl
+AC_SUBST(arch)dnl
+AC_SUBST(sitearch)dnl
+AC_SUBST(ruby_version)dnl
+AC_SUBST(rubylibdir)dnl
+AC_SUBST(rubyarchdir)dnl
+AC_SUBST(sitedir)dnl
+AC_SUBST(sitelibdir)dnl
+AC_SUBST(sitearchdir)dnl
+AC_SUBST(vendordir)dnl
+AC_SUBST(vendorlibdir)dnl
+AC_SUBST(vendorarchdir)dnl
+
+AC_SUBST(CONFIGURE, "`echo $0 | sed 's|.*/||'`")dnl
+AC_SUBST(configure_args, "`echo "${ac_configure_args}" | sed 's/\\$/$$/g'`")dnl
+
+AS_IF([test "${universal_binary-no}" = yes ], [
+ arch="universal-${target_os}"
+ AS_IF([test "${rb_cv_architecture_available}" = yes], [
+ AC_DEFINE_UNQUOTED(RUBY_PLATFORM_CPU, __ARCHITECTURE__)
+ ], [
+ for archs in ${universal_archnames}; do
+ cpu=`echo $archs | sed 's/.*=//'`
+ archs=`echo $archs | sed 's/=.*//'`
+ RUBY_DEFINE_IF([defined __${archs}__ &&! defined RUBY_PLATFORM_CPU], RUBY_PLATFORM_CPU, ["${cpu}"])
+ done
+ ])
+ ints='long int short'
+ test "$ac_cv_type_long_long" = yes && ints="'long long' $ints"
+ AC_SUBST(UNIVERSAL_ARCHNAMES, "${universal_archnames}")
+ AC_SUBST(UNIVERSAL_INTS, "${ints}")
+ AC_DEFINE_UNQUOTED(RUBY_PLATFORM_OS, "${target_os}")
+ AC_DEFINE_UNQUOTED(RUBY_ARCH, "universal-"RUBY_PLATFORM_OS)
+ AC_DEFINE_UNQUOTED(RUBY_PLATFORM, "universal."RUBY_PLATFORM_CPU"-"RUBY_PLATFORM_OS)
+], [
+ arch="${target_cpu}-${target_os}"
+ AC_DEFINE_UNQUOTED(RUBY_PLATFORM, "$arch")
+])
+
+unset sitearch
+AS_CASE(["$target_os"],[mingw*],[sitearch="$target_cpu-$rb_cv_msvcrt"])
+: ${sitearch='${arch}'}
+
+AC_ARG_WITH(search-path,
+ AS_HELP_STRING([--with-search-path=DIR], [specify the additional search path]),
+ [search_path=$withval])
+AS_IF([test "$search_path" != ""], [
+ AC_SUBST(RUBY_SEARCH_PATH, $search_path)
+])
+
+AC_ARG_WITH(rubyhdrdir,
+ AS_HELP_STRING([--with-rubyhdrdir=DIR], [core headers in DIR [[INCLUDEDIR/RUBY_BASE_NAME-RUBY_VERSION]]]),
+ [rubyhdrdir=$withval],
+ [rubyhdrdir='${includedir}/${RUBY_VERSION_NAME}'])
+
+AC_ARG_WITH(rubyarchhdrdir,
+ AS_HELP_STRING([--with-rubyarchhdrdir=DIR],
+ [architecture dependent core headers in DIR [[$(rubyhdrdir)/$(arch)]]]),
+ [rubyarchhdrdir=$withval],
+ [rubyarchhdrdir=${multiarch+'${archincludedir}/${RUBY_VERSION_NAME}'}${multiarch-'${rubyhdrdir}/${arch}'}])
+
+AC_ARG_WITH(sitehdrdir,
+ AS_HELP_STRING([--with-sitehdrdir=DIR], [core site headers in DIR [[RUBYHDRDIR/site_ruby]]]),
+ [sitehdrdir=$withval],
+ [sitehdrdir='${rubyhdrdir}/site_ruby'])
+
+AC_ARG_WITH(sitearchhdrdir,
+ AS_HELP_STRING([--with-sitearchhdrdir=DIR],
+ [architecture dependent core site headers in DIR [[RUBYHDRDIR/site_ruby]]]),
+ [sitearchhdrdir=$withval],
+ [sitearchhdrdir=${multiarch+'${sitearchincludedir}/${RUBY_VERSION_NAME}/site_ruby'}${multiarch-'${sitehdrdir}/${sitearch}'}])
+
+AC_ARG_WITH(vendorhdrdir,
+ AS_HELP_STRING([--with-vendorhdrdir=DIR], [core vendor headers in DIR [[RUBYHDRDIR/vendor_ruby]]]),
+ [vendorhdrdir=$withval],
+ [vendorhdrdir='${rubyhdrdir}/vendor_ruby'])
+
+AC_ARG_WITH(vendorarchhdrdir,
+ AS_HELP_STRING([--with-vendorarchhdrdir=DIR],
+ [architecture dependent core vendor headers in DIR [[RUBYHDRDIR/vendor_ruby]]]),
+ [vendorarchhdrdir=$withval],
+ [vendorarchhdrdir=${multiarch+'${sitearchincludedir}/${RUBY_VERSION_NAME}/vendor_ruby'}${multiarch-'${vendorhdrdir}/${sitearch}'}])
+
+AC_SUBST(rubyhdrdir)dnl
+AC_SUBST(sitehdrdir)dnl
+AC_SUBST(vendorhdrdir)dnl
+AC_SUBST(rubyarchhdrdir)dnl
+AC_SUBST(sitearchhdrdir)dnl
+AC_SUBST(vendorarchhdrdir)dnl
+
+AC_ARG_WITH(mantype,
+ AS_HELP_STRING([--with-mantype=TYPE], [specify man page type; TYPE is one of man and doc]),
+ [
+ AS_CASE(["$withval"],
+ [man|man.gz|man.bz2|doc|doc.gz|doc.bz2], [MANTYPE=$withval],
+ [AC_MSG_ERROR(invalid man type: $withval)])
+ ])
+AS_IF([test -z "$MANTYPE"], [
+ dnl Looks for nroff with -mdoc support.
+ AC_CACHE_VAL([ac_cv_path_NROFF], [
+ AC_PATH_PROGS_FEATURE_CHECK([NROFF],
+ [nroff awf mandoc],
+ [$ac_path_NROFF -mdoc ${srcdir}/man/ruby.1 \
+ >/dev/null 2>&1 &&
+ ac_cv_path_NROFF=$ac_path_NROFF \
+ ac_path_NROFF_found=:],
+ [], ["/usr/bin:/usr/ucb"]
+ )
+ ])
+ AS_IF([test -n "$ac_cv_path_NROFF"], [
+ MANTYPE=doc
+ ], [
+ MANTYPE=man
+ ])
+])
+AC_SUBST(MANTYPE)
+
+AC_ARG_ENABLE(rubygems,
+ AS_HELP_STRING([--disable-rubygems], [disable rubygems by default]),
+ [enable_rubygems="$enableval"], [enable_rubygems=yes])
+AS_IF([test x"$enable_rubygems" = xno], [
+ USE_RUBYGEMS=no
+], [
+ USE_RUBYGEMS=yes
+])
+AC_SUBST(USE_RUBYGEMS)
+
+arch_hdrdir="${EXTOUT}/include/${arch}/ruby"
+AS_MKDIR_P("${arch_hdrdir}")
+config_h="${arch_hdrdir}/config.h"
+guard=INCLUDE_RUBY_CONFIG_H
+{
+ echo "#ifndef $guard"
+ echo "#define $guard 1"
+ grep -v "^#define PACKAGE_" confdefs.h
+ echo "#endif /* $guard */"
+} | tr -d '\015' |
+(
+ AS_IF([test "x$CONFIGURE_TTY" = xyes], [color=--color], [color=])
+ exec ${srcdir}/tool/ifchange $color "${config_h}" -
+) >&AS_MESSAGE_FD || AC_MSG_ERROR([failed to create ${config_h}])
+tr -d '\015' < largefile.h > confdefs.h
+rm largefile.h
+
+BUILTIN_ENCS=["`sed -n -e '/^BUILTIN_ENCS[ ]*=/{' \
+ -e s/// -e :l -e '/\\\\$/N' -e 's/\\\\\\n/ /' -e 't l' -e p \
+ -e '}' "${srcdir}/enc/Makefile.in"`"]
+BUILTIN_ENCOBJS=
+for e in $BUILTIN_ENCS; do BUILTIN_ENCOBJS="$BUILTIN_ENCOBJS "`echo $e | sed 's/\.c$/.$(OBJEXT)/'`; done
+AC_SUBST(BUILTIN_ENCOBJS)
+
+BUILTIN_TRANSES=["`sed -n -e '/^BUILTIN_TRANSES[ ]*=/{' \
+ -e s/// -e :l -e '/\\\\$/N' -e 's/\\\\\\n/ /' -e 't l' -e p \
+ -e '}' "${srcdir}/enc/Makefile.in"`"]
+BUILTIN_TRANSSRCS=
+BUILTIN_TRANSOBJS=
+for e in $BUILTIN_TRANSES; do
+ BUILTIN_TRANSSRCS="$BUILTIN_TRANSSRCS "`echo $e | sed 's/\.trans$/.c/'`
+ BUILTIN_TRANSOBJS="$BUILTIN_TRANSOBJS "`echo $e | sed 's/\.trans$/.$(OBJEXT)/'`
+done
+AC_SUBST(BUILTIN_TRANSSRCS)
+AC_SUBST(BUILTIN_TRANSOBJS)
+
+PACKAGE=$RUBY_BASE_NAME
+AC_SUBST(PACKAGE)
+AS_MESSAGE([$PACKAGE library version = $ruby_version])
+
+AS_IF([test x"$CC_WRAPPER" != x], [
+ CC='$(CC_WRAPPER) '"${CC@%:@$CC_WRAPPER }"
+ CPP='$(CC_WRAPPER) '"${CPP@%:@$CC_WRAPPER }"
+ CC_WRAPPER='$(rubyarchdir)/darwin-cc'
+ XCC_WRAPPER='$(top_srcdir)/tool/darwin-cc'
+])
+AC_SUBST(CC_WRAPPER, '')
+AC_SUBST(XCC_WRAPPER)
+
+AS_CASE([" $CPP "], [*" $CC "*], [CPP=`echo " $CPP " | sed "s| $CC |"' $(CC) |;s/^ *//;s/ *$//'`])
+
+AS_IF([test x"$firstmf" != x], [
+ AC_CONFIG_FILES($firstmf:$firsttmpl, [], [firstmf="$firstmf" firsttmpl="$firsttmpl"])
+])
+AC_CONFIG_FILES(Makefile, [
+ tmpmk=confmk$$.tmp
+ {
+ AS_IF([test ${VCS+set}], [
+ :
+ ], [svn info "$srcdir" > /dev/null 2>&1], [
+ VCS='svn'
+ ], [git_dir=`$GIT --work-tree="$srcdir" --git-dir="$srcdir/.git" rev-parse --git-dir 2>/dev/null`], [
+ AS_IF([test -d "$git_dir/svn"], [
+ VCS='$(GIT) svn'
+ ], [
+ VCS='$(GIT)'
+ ])
+ ], [
+ VCS='echo cannot'
+ ])
+ AS_CASE("$VCS",
+ [svn], [VCSUP='$(VCS) up $(SVNUPOPTIONS)'],
+ ['$(GIT) svn'], [VCSUP='$(VCS) rebase $(GITSVNREBASEOPTIONS)'],
+ ['$(GIT)'|git], [VCSUP='$(VCS) pull $(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"
+ 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
+ ], [
+ echo 'distclean-local::; @$(RM) GNUmakefile uncommon.mk'
+ ])
+ } > $tmpmk && AS_IF([! grep '^ruby:' $tmpmk > /dev/null], [
+ AS_IF([test "${gnumake}" = yes], [
+ tmpgmk=confgmk$$.tmp
+ {
+ echo "include $tmpmk"
+ echo "-include uncommon.mk"
+ } > $tmpgmk
+ ], [
+ tmpgmk=$tmpmk
+ ]) &&
+ test -z "`${MAKE-make} -f $tmpgmk info-program | grep '^PROGRAM=ruby$'`" &&
+ echo 'ruby: $(PROGRAM);' >> $tmpmk
+ test "$tmpmk" = "$tmpgmk" || rm -f "$tmpgmk"
+ ]) && mv -f $tmpmk Makefile],
+[EXEEXT='$EXEEXT' gnumake='$gnumake' GIT='$GIT'])
+
+AC_ARG_WITH([ruby-pc],
+ AC_HELP_STRING([--with-ruby-pc=FILENAME], [pc file basename]),
+ [ruby_pc="$withval"],
+ [ruby_pc="${RUBY_BASE_NAME}-${MAJOR}.${MINOR}.pc"])
+AC_SUBST(ruby_pc)
+AC_SUBST(exec, [exec])
+
+AC_ARG_WITH(destdir,
+ AS_HELP_STRING([--with-destdir=DESTDIR], [specify default directory to install]),
+ [DESTDIR="$withval"])
+AC_SUBST(DESTDIR)
+
+AC_CONFIG_FILES($ruby_pc:template/ruby.pc.in,
+ [
+ AS_IF([sed ['s/\$(\([A-Za-z_][A-Za-z0-9_]*\))/${\1}/g;s/@[A-Za-z_][A-Za-z0-9_]*@//'] $ruby_pc > ruby.tmp.pc &&
+ {
+ test -z "$PKG_CONFIG" ||
+ PKG_CONFIG_PATH=. $PKG_CONFIG --print-errors ruby.tmp
+ }],
+ [
+ mv -f ruby.tmp.pc $ruby_pc
+ ], [
+ exit 1
+ ])
+ ],
+ [ruby_pc='$ruby_pc' PKG_CONFIG='$PKG_CONFIG'])
+
+AC_OUTPUT
+}
+}
+
+AS_IF([test "$silent" = yes], [], [
+AS_IF([${FOLD+:} false], [], [
+AS_IF([test "`echo abcdefg hijklmno | fold -s -w10 | sed 1d`" = hijklmno], [FOLD="fold"], [FOLD=])
+])
+AS_REQUIRE_SHELL_FN([config_summary],
+ [AS_FUNCTION_DESCRIBE([config_summary], [NAME, VAL], [configuration summary])],
+ [AS_IF([test -z "$2"], [], [
+ AS_ECHO_N([" * $1: "]) | dd bs=1 count=26 2>/dev/null
+ AS_IF([test "$FOLD"], [
+ echo "$2" | fold -s -w50 |
+ sed '1!s/^/ /;$!s/$/\\/'
+ ], [echo "$2"])
+ ])]
+)
+
+echo "---"
+echo "Configuration summary for $RUBY_BASE_NAME version $RUBY_PROGRAM_VERSION"
+echo ""
+config_summary "Installation prefix" "$prefix"
+config_summary "exec prefix" "$exec_prefix"
+config_summary "arch" "$arch"
+config_summary "site arch" "$sitearch"
+config_summary "RUBY_BASE_NAME" "$RUBY_BASE_NAME"
+config_summary "enable shared" "$enable_shared"
+config_summary "ruby lib prefix" "$rubylibprefix"
+config_summary "site libraries path" "$rubysitearchprefix"
+config_summary "vendor path" "$vendordir"
+config_summary "target OS" "$target_os"
+config_summary "compiler" "$CC"
+config_summary "with pthread" "$enable_pthread"
+config_summary "enable shared libs" "$ENABLE_SHARED"
+config_summary "dynamic library ext" "$DLEXT"
+config_summary "CFLAGS" "$cflags"
+config_summary "CPPFLAGS" "$cppflags"
+config_summary "LDFLAGS" "$LDFLAGS"
+config_summary "DLDFLAGS" "$DLDFLAGS"
+config_summary "optflags" "$optflags"
+config_summary "debugflags" "$debugflags"
+config_summary "warnflags" "$warnflags"
+config_summary "strip command" "$STRIP"
+config_summary "install doc" "$install_doc"
+config_summary "JIT support" "$MJIT_SUPPORT"
+config_summary "man page type" "$MANTYPE"
+config_summary "search path" "$search_path"
+config_summary "static-linked-ext" ${EXTSTATIC:+"yes"}
+echo ""
+echo "---"
+])
diff --git a/configure.in b/configure.in
deleted file mode 100644
index fea39e2986..0000000000
--- a/configure.in
+++ /dev/null
@@ -1,4710 +0,0 @@
-dnl Process this file with autoconf to produce a configure script.
-AC_INIT()
-{
-AC_CONFIG_AUX_DIR(tool)
-
-AC_PREREQ(2.67)
-
-AC_DEFUN([RUBY_PREREQ_AC],
- [m4_if(m4_version_compare(m4_defn([m4_PACKAGE_VERSION]), [$1]), [-1],
- AC_MSG_ERROR([Autoconf version ]$1[ or higher is required]$2))])
-
-AC_DISABLE_OPTION_CHECKING
-
-AC_ARG_VAR([cflags], [additional CFLAGS])
-AC_ARG_VAR([cppflags], [additional CPPFLAGS])
-AC_ARG_VAR([cxxflags], [additional CXXFLAGS])
-
-AC_DEFUN([RUBY_RM_RECURSIVE], [
-m4_if(m4_version_compare(m4_defn([m4_PACKAGE_VERSION]), [2.70]), [-1], [
-# suppress error messages, rm: cannot remove 'conftest.dSYM', from
-# AC_EGREP_CPP with CFLAGS=-g on Darwin.
-#
-# TODO: remove this hack when AC_PREREQ() becomes 2.70 or later.
-AS_CASE([$build_os], [darwin*], [
-rm() {
- rm_recursive=''
- for arg do
- AS_CASE("$arg",
- [--*], [],
- [-*r*], [break],
- [conftest.*], [if test -d "$arg"; then rm_recursive=-r; break; fi],
- [])
- done
- command rm $rm_recursive "[$]@"
-}
-])])])
-
-{ # environment section
-
-AC_ARG_WITH(baseruby,
- AS_HELP_STRING([--with-baseruby=RUBY], [use RUBY as baseruby; RUBY is the pathname of ruby]),
- [
- AS_CASE(["$withval"],[*ruby*],[BASERUBY=$withval],[AC_MSG_ERROR(need ruby)])
- ],
- [
- AC_PATH_PROG([BASERUBY], [ruby], [false])
- ])
-if test "`RUBYOPT=- $BASERUBY -e 'print 42' 2>/dev/null`" = 42; then
- if test "`RUBYOPT=- $BASERUBY --disable=gems -e 'print 42' 2>/dev/null`" = 42; then
- BASERUBY="$BASERUBY --disable=gems"
- fi
- $BASERUBY -C "$srcdir/tool" downloader.rb -e gnu config.guess config.sub
- HAVE_BASERUBY=yes
-else
- BASERUBY="echo executable host ruby is required. use --with-baseruby option.; false"
- HAVE_BASERUBY=no
-fi
-AC_SUBST(BASERUBY)
-AC_SUBST(HAVE_BASERUBY)
-
-AC_DEFUN([RUBY_MINGW32],
-[AS_CASE(["$host_os"],
-[cygwin*], [
-AC_CACHE_CHECK(for mingw32 environment, rb_cv_mingw32,
-[AC_TRY_CPP([
-#ifndef __MINGW32__
-# error
-#endif
-], rb_cv_mingw32=yes,rb_cv_mingw32=no)
-rm -f conftest*])
-if test "$rb_cv_mingw32" = yes; then
- target_os="mingw32"
- : ${ac_tool_prefix:="`expr "$CC" : ['\(.*-\)g\?cc[^/]*$']`"}
-fi
-])
-AS_CASE(["$target_os"], [mingw*msvc], [
-target_os="`echo ${target_os} | sed 's/msvc$//'`"
-])
-AS_CASE(["$target_cpu-$target_os"], [x86_64-mingw*], [
-target_cpu=x64
-])
-])
-
-AC_DEFUN([RUBY_NACL],
-[
- AS_CASE(["${host_os}"],
-[nacl], [
- ac_cv_exeext=.nexe
- host_vendor=chromium
- ac_cv_host=chromium
- AC_MSG_CHECKING([wheather \$NACL_SDK_ROOT is set])
- if test x"${NACL_SDK_ROOT}" = x; then
- AC_MSG_RESULT([no])
- AC_MSG_ERROR([You need to set \$NACL_SDK_ROOT environment variable to build for NativeClient])
- fi
- AC_MSG_RESULT([yes])
-
- nacl_cv_build_variant=glibc
- AC_ARG_WITH(newlib,
- AS_HELP_STRING([--with-newlib], [uses newlib version of NativeClient SDK]),
- [AS_CASE([$withval],
- [no], [nacl_cv_build_variant=glibc],
- [yes], [nacl_cv_build_variant=newlib])])
-
- AS_CASE(["$target_cpu"],
- [x86_64], [nacl_cv_cpu_nick=x86
- nacl_cv_cpu_nick2=x86_64],
- [i?86], [nacl_cv_cpu_nick=x86
- nacl_cv_cpu_nick2=x86_32],
- [le32], [nacl_cv_cpu_nick=pnacl
- nacl_cv_cpu_nick2=pnacl
- ac_cv_exeext=.pexe],
- [nacl_cv_cpu_nick=$target_cpu])
- AS_CASE(["$build_os"],
- [linux*], [nacl_cv_os_nick=linux],
- [darwin*], [nacl_cv_os_nick=mac],
- [cygwin*|mingw*], [nacl_cv_os_nick=win],
- [nacl_cv_os_nick=$build_os])
-
- host="$host_cpu-chromium-$host_os-"
- ac_tool_prefix="$host_cpu-nacl-"
-
- AC_MSG_CHECKING([NativeClient toolchain])
- if test x"$nacl_cv_cpu_nick" = xpnacl; then
- NACL_TOOLCHAIN="${nacl_cv_os_nick}_pnacl"
- ac_tool_prefix=pnacl-
- elif test -d \
- "${NACL_SDK_ROOT}/toolchain/${nacl_cv_os_nick}_${nacl_cv_cpu_nick}_${nacl_cv_build_variant}"; then
- NACL_TOOLCHAIN="${nacl_cv_os_nick}_${nacl_cv_cpu_nick}_${nacl_cv_build_variant}"
- elif test -d \
- "${NACL_SDK_ROOT}/toolchain/${nacl_cv_os_nick}_x86_${nacl_cv_cpu_nick}/${nacl_cv_build_variant}"; then
- NACL_TOOLCHAIN="${nacl_cv_os_nick}_x86_${nacl_cv_cpu_nick}/${nacl_cv_build_variant}"
- else
- AS_CASE(
- ["${nacl_cv_build_variant}"],
- [glibc], [if test \
- -d "${NACL_SDK_ROOT}/toolchain/${nacl_cv_os_nick}_${nacl_cv_cpu_nick}_newlib" \
- -a -d "${NACL_SDK_ROOT}/toolchain/${nacl_cv_os_nick}_${nacl_cv_cpu_nick}"; then
- NACL_TOOLCHAIN="${nacl_cv_os_nick}_${nacl_cv_cpu_nick}"
- fi],
- [newlib], [ NACL_TOOLCHAIN="${nacl_cv_os_nick}_${nacl_cv_cpu_nick}" ])
- fi
- if test ! -e "${NACL_SDK_ROOT}/toolchain/${NACL_TOOLCHAIN}/${ac_tool_prefix}gcc"; then
- if test "${build_cpu}" = i686 -a -e "${NACL_SDK_ROOT}/toolchain/${NACL_TOOLCHAIN}/nacl-gcc"; then
- ac_tool_prefix=nacl-
- fi
- if test "${build_cpu}" = x86_64 -a -e "${NACL_SDK_ROOT}/toolchain/${NACL_TOOLCHAIN}/nacl-gcc"; then
- ac_tool_prefix=nacl64-
- fi
- fi
- if test -z "${NACL_TOOLCHAIN}"; then
- AC_MSG_ERROR([Unrecognized --host and --build combination or NaCl SDK is not installed])
- fi
- AC_MSG_RESULT(${NACL_TOOLCHAIN})
-
- AC_MSG_CHECKING([path to SDK])
- if ! echo -- "${PATH}" | grep -F "${NACL_SDK_ROOT}/toolchain/${NACL_TOOLCHAIN}/bin" > /dev/null; then
- PATH="${PATH}:${NACL_SDK_ROOT}/toolchain/${NACL_TOOLCHAIN}/bin"
- fi
- AC_MSG_RESULT(${NACL_SDK_ROOT}/toolchain/${NACL_TOOLCHAIN}/bin)
-
- RUBY_APPEND_OPTIONS(XCFLAGS, '-I$(NACL_SDK_ROOT)/include')
- if test x"${nacl_cv_cpu_nick}" = xpnacl; then
- RUBY_APPEND_OPTIONS(XCFLAGS, '-isystem $(NACL_SDK_ROOT)/include/pnacl')
- elif test x"${nacl_cv_build_variant}" = xnewlib; then
- RUBY_APPEND_OPTIONS(XCFLAGS, '-isystem $(NACL_SDK_ROOT)/include/newlib')
- fi
-
- AC_MSG_CHECKING([nacl library path])
- if test -d "${NACL_SDK_ROOT}/lib/${nacl_cv_build_variant}_${nacl_cv_cpu_nick2}/Release"; then
- nacl_cv_libpath="${nacl_cv_build_variant}_${nacl_cv_cpu_nick2}"
- elif test -d "${NACL_SDK_ROOT}/lib/${nacl_cv_cpu_nick2}/Release"; then
- nacl_cv_libpath="${nacl_cv_cpu_nick2}"
- else
- AC_MSG_ERROR([not found])
- fi
- AC_MSG_RESULT([${nacl_cv_libpath}])
- RUBY_APPEND_OPTIONS(XLDFLAGS, '-L$(NACL_SDK_ROOT)/'"lib/${nacl_cv_libpath}/Release")
-
- AC_SUBST(NACL_TOOLCHAIN)
- AC_SUBST(NACL_SDK_ROOT)
- AC_SUBST(NACL_SDK_VARIANT, "${nacl_cv_build_variant}")
- AC_SUBST(NACL_LIB_PATH, "${nacl_cv_libpath}")
- AC_CHECK_TOOLS(CC, [clang gcc])
- AC_CHECK_TOOLS(CXX, [clang++ g++])
-])])
-
-AC_DEFUN([RUBY_NACL_CHECK_PEPPER_TYPES],
-[AS_CASE(["${host_os}"],
-[nacl], [
- AC_CHECK_TYPES([struct PPB_Core, struct PPB_Messaging, struct PPB_Var,
- struct PPB_URLLoader, struct PPB_URLRequestInfo,
- struct PPB_URLResponseInfo, struct PPB_FileRef,
- struct PPP_Instance])
-])
-])
-
-AC_DEFUN([RUBY_CPPOUTFILE],
-[AC_CACHE_CHECK(whether ${CPP} accepts -o, rb_cv_cppoutfile,
-[save_CPPFLAGS="$CPPFLAGS"
-CPPFLAGS='-o conftest-1.i'
-rb_cv_cppoutfile=no
-AC_TRY_CPP([test-for-cppout],
- [grep test-for-cppout conftest-1.i > /dev/null && rb_cv_cppoutfile=yes])
-CPPFLAGS="$save_CPPFLAGS"
-rm -f conftest*])
-if test "$rb_cv_cppoutfile" = yes; then
- CPPOUTFILE='-o conftest.i'
-elif test "$rb_cv_cppoutfile" = no; then
- CPPOUTFILE='> conftest.i'
-elif test -n "$rb_cv_cppoutfile"; then
- CPPOUTFILE="$rb_cv_cppoutfile"
-fi
-AC_SUBST(CPPOUTFILE)])
-
-AC_DEFUN([RUBY_PROG_GNU_LD],
-[AC_CACHE_CHECK(whether the linker is GNU ld, rb_cv_prog_gnu_ld,
-[if `$CC $CFLAGS $CPPFLAGS $LDFLAGS --print-prog-name=ld 2>&1` -v 2>&1 | grep "GNU ld" > /dev/null; then
- rb_cv_prog_gnu_ld=yes
-else
- rb_cv_prog_gnu_ld=no
-fi
-])
-GNU_LD=$rb_cv_prog_gnu_ld
-AC_SUBST(GNU_LD)])
-
-eval `sed -n ['s/^@%:@define RUBY_API_VERSION_\([A-Z][A-Z_0-9]*\) \([0-9][0-9]*\)/API_\1=\2/p'] $srcdir/include/ruby/version.h`
-RUBY_PROGRAM_VERSION=`sed -n 's/^@%:@define RUBY_VERSION "\(.*\)"/\1/p' $srcdir/version.h`
-MAJOR=`echo $RUBY_PROGRAM_VERSION | cut -d. -f1`
-MINOR=`echo $RUBY_PROGRAM_VERSION | cut -d. -f2`
-TEENY=`echo $RUBY_PROGRAM_VERSION | cut -d. -f3`
-for v in MAJOR MINOR TEENY; do
- if eval "test \"\$$v\" = ''"; then
- AC_MSG_ERROR(could not determine $v number from version.h)
- fi
-done
-AS_IF([test "$MAJOR.$MINOR" != "$API_MAJOR.$API_MINOR"], [
- AC_MSG_ERROR([API version $API_MAJOR.$API_MINOR differs from program version $MAJOR.$MINOR])
-])
-AC_SUBST(MAJOR)
-AC_SUBST(MINOR)
-AC_SUBST(TEENY)
-AC_SUBST(RUBY_PROGRAM_VERSION)
-RUBY_PATCHLEVEL=`sed -n 's/^#define RUBY_PATCHLEVEL //p' $srcdir/version.h`
-AC_DEFINE(CANONICALIZATION_FOR_MATHN)
-dnl checks for alternative programs
-AC_CANONICAL_BUILD
-RUBY_RM_RECURSIVE
-AC_ARG_WITH(gcc,
- AS_HELP_STRING([--without-gcc], [never use gcc]),
- [
- AS_CASE([$withval],
- [no], [: ${CC=cc}],
- [yes], [: ${CC=gcc}],
- [CC=$withval])])
-dnl If the user switches compilers, we can't believe the cache
-if test ! -z "$ac_cv_prog_CC" -a ! -z "$CC" -a "$CC" != "$ac_cv_prog_CC"
-then
- AC_MSG_ERROR(cached CC is different -- throw away $cache_file
-(it is also a good idea to do 'make clean' before compiling))
-fi
-test -z "$CC" || ac_cv_prog_CC="$CC"
-
-if test "$program_prefix" = NONE; then
- program_prefix=
-fi
-if test "$prefix" -ef .; then
- AC_MSG_ERROR(--prefix cannot be the current working directory.)
-fi
-RUBY_BASE_NAME=`echo ruby | sed "$program_transform_name"`
-RUBYW_BASE_NAME=`echo rubyw | sed "$program_transform_name"`
-AC_SUBST(RUBY_BASE_NAME)
-AC_SUBST(RUBYW_BASE_NAME)
-AC_SUBST(RUBY_VERSION_NAME, '${RUBY_BASE_NAME}-${ruby_version}')
-
-AC_CANONICAL_TARGET
-test x"$target_alias" = x &&
-target_os=`echo $target_os | sed 's/linux-gnu$/linux/;s/linux-gnu/linux-/'`
-ac_install_sh='' # unusable for extension libraries.
-
-AC_ARG_WITH(os-version-style,
- AS_HELP_STRING([--with-os-version-style=TYPE],
- [OS version number for target and target_os [[full]]]
- [(full|teeny|minor+0|minor|major+0|major|none)]),
- [os_version_style=$withval],
- [os_version_style=full
- AS_CASE($target_os, [[*[0-9].*]],
- [AS_CASE([`/usr/bin/ruby -e 'puts RUBY_PLATFORM' 2>/dev/null`],
- [[*-*[0-9].*.0]], [os_version_style=minor+0],
- [[*-*[0-9].*.*]], [os_version_style=full],
- [[*-*[0-9].0] ], [os_version_style=major+0],
- [[*-*[0-9].*] ], [os_version_style=minor],
- [[*-*[0-9]] ], [os_version_style=major],
- )])
- ])
-os_version_style_transform=
-AS_CASE("${os_version_style}",
- [full|teeny], [],
- [minor+0], [os_version_style_transform=['s/\([0-9]\.[0-9][0-9]*\)\.[0-9][.0-9]*$/\1.0/']],
- [minor], [os_version_style_transform=['s/\([0-9]\.[0-9][0-9]*\)\.[0-9][.0-9]*$/\1/']],
- [major+0], [os_version_style_transform=['s/\([0-9]\)\.[0-9][.0-9]*$/\1.0/']],
- [major], [os_version_style_transform=['s/\([0-9]\)\.[0-9][.0-9]*$/\1/']],
- [none], [os_version_style_transform=['s/[0-9]\.[0-9][.0-9]*$//']],
- [AC_MSG_ERROR(unknown --with-os-version-style: $withval)])
-AS_IF([test -z "$target_alias" -a -n "$os_version_style_transform"],
- [
- target=`echo ${target} | sed "$os_version_style_transform"`
- target_os=`echo ${target_os} | sed "$os_version_style_transform"`
- ])
-
-AC_DEFUN([RUBY_APPEND_OPTION],
- [# RUBY_APPEND_OPTION($1)
- AS_CASE([" [$]{$1-} "],
- [*" $2 "*], [], [' '], [ $1="$2"], [ $1="[$]$1 $2"])])
-AC_DEFUN([RUBY_APPEND_OPTIONS],
- [# RUBY_APPEND_OPTIONS($1)
- for rb_opt in $2; do
- AS_CASE([" [$]{$1-} "],
- [*" [$]{rb_opt} "*], [], [' '], [ $1="[$]{rb_opt}"], [ $1="[$]$1 [$]{rb_opt}"])
- done])
-AC_DEFUN([RUBY_PREPEND_OPTION],
- [# RUBY_PREPEND_OPTION($1)
- AS_CASE([" [$]{$1-} "],
- [*" $2 "*], [], [' '], [ $1="$2"], [ $1="$2 [$]$1"])])
-AC_DEFUN([RUBY_PREPEND_OPTIONS],
- [# RUBY_PREPEND_OPTIONS($1)
- unset rb_opts; for rb_opt in $2; do
- AS_CASE([" [$]{rb_opts} [$]{$1-} "],
- [*" [$]{rb_opt} "*], [], [' '], [ $1="[$]{rb_opt}"], [ rb_opts="[$]{rb_opts}[$]{rb_opt} "])
- done
- $1="[$]{rb_opts}[$]$1"])
-
-AC_ARG_WITH(arch,
- AS_HELP_STRING([--with-arch=ARCHS],
- [build an Apple/NeXT Multi Architecture Binary (MAB);
- ARCHS is a comma-delimited list of architectures for
- which to build; if this option is disabled or omitted
- entirely, then the package will be built only for the
- target platform]),
- [target_archs="$withval"], [unset target_archs])
-
-AC_DEFUN([RUBY_DEFAULT_ARCH], [
-AC_MSG_CHECKING([arch option])
-AS_CASE([$1],
- [*64], [ARCH_FLAG=-m64],
- [[i[3-6]86]], [ARCH_FLAG=-m32],
- [AC_MSG_ERROR(unknown target architecture: $target_archs)]
- )
-AC_MSG_RESULT([$ARCH_FLAG])
-])
-
-AC_DEFUN([RUBY_UNIVERSAL_ARCH], [
-# RUBY_UNIVERSAL_ARCH begin
-ARCH_FLAG=`expr " $CXXFLAGS " : ['.* \(-m[0-9][0-9]*\) ']`
-test ${CXXFLAGS+set} && CXXFLAGS=`echo "$CXXFLAGS" | sed [-e 's/ *-arch *[^ ]*//g' -e 's/ *-m32//g' -e 's/ *-m64//g']`
-ARCH_FLAG=`expr " $CFLAGS " : ['.* \(-m[0-9][0-9]*\) ']`
-test ${CFLAGS+set} && CFLAGS=`echo "$CFLAGS" | sed [-e 's/ *-arch *[^ ]*//g' -e 's/ *-m32//g' -e 's/ *-m64//g']`
-test ${LDFLAGS+set} && LDFLAGS=`echo "$LDFLAGS" | sed [-e 's/ *-arch *[^ ]*//g' -e 's/ *-m32//g' -e 's/ *-m64//g']`
-unset universal_binary universal_archnames
-if test ${target_archs+set}; then
- AC_MSG_CHECKING([target architectures])
- target_archs=`echo $target_archs | tr , ' '`
- # /usr/lib/arch_tool -archify_list $TARGET_ARCHS
- for archs in $target_archs
- do
- AS_CASE([",$universal_binary,"],[*",$archs,"*], [],[
- cpu=`$SHELL "$ac_aux_dir/config.sub" "${archs}-${target_os}" 2>&1` || {
- AC_MSG_RESULT([failed])
- AC_MSG_ERROR([$cpu])
- }
- cpu=`echo $cpu | sed 's/-.*-.*//'`
- universal_binary="${universal_binary+$universal_binary,}$cpu"
- universal_archnames="${universal_archnames} ${archs}=${cpu}"
- ARCH_FLAG="${ARCH_FLAG+$ARCH_FLAG }-arch $archs"
- ])
- done
- target_archs="$universal_binary"
- unset universal_binary
- AS_CASE(["$target_archs"],
- [*,*], [universal_binary=yes],
- [unset universal_archnames])
- AC_MSG_RESULT([$target_archs])
-
- target=`echo $target | sed "s/^$target_cpu-/-/"`
- target_alias=`echo $target_alias | sed "s/^$target_cpu-/-/"`
- if test "${universal_binary-no}" = yes; then
- AC_SUBST(try_header,try_compile)
- target_cpu=universal
- real_cross_compiling=$cross_compiling
- else
- if test x"$target_cpu" != x"${target_archs}"; then
- echo 'int main(){return 0;}' > conftest.c
- if $CC $CFLAGS $ARCH_FLAG -o conftest conftest.c > /dev/null 2>&1; then
- rm -fr conftest.*
- else
- RUBY_DEFAULT_ARCH("$target_archs")
- fi
- fi
- target_cpu=${target_archs}
- fi
- AS_CASE(["$target"], [-*], [ target="$target_cpu${target}"])
- AS_CASE(["$target_alias"], [-*], [ target_alias="$target_cpu${target_alias}"])
-else
- if test x"$target_alias" = x; then
- AS_CASE(["$target_os"],
- [darwin*], [
- AC_MSG_CHECKING([for real target cpu])
- target=`echo $target | sed "s/^$target_cpu-/-/"`
- target_cpu=`$CC -E - 2>/dev/null <<EOF |
-#ifdef __x86_64__
-"processor-name=x86_64"
-#endif
-#ifdef __i386__
-"processor-name=i386"
-#endif
-#ifdef __ppc__
-"processor-name=powerpc"
-#endif
-#ifdef __ppc64__
-"processor-name=powerpc64"
-#endif
-EOF
- sed -n 's/^"processor-name=\(.*\)"/\1/p'`
- target="$target_cpu${target}"
- AC_MSG_RESULT([$target_cpu])
- ])
- fi
- target_archs="$target_cpu"
-fi
-if test "${target_archs}" != "${rb_cv_target_archs-${target_archs}}"; then
- AC_MSG_ERROR([target arch(s) has changed from ${rb_cv_target_archs-nothing} to ${target_archs}])
-else
- rb_cv_target_archs=${target_archs}
-fi
-if test "x${ARCH_FLAG}" != x; then
- CFLAGS="${CFLAGS:+$CFLAGS }${ARCH_FLAG}"
- LDFLAGS="${LDFLAGS:+$LDFLAGS }${ARCH_FLAG}"
-fi
-# RUBY_UNIVERSAL_ARCH end
-])
-
-AC_ARG_ENABLE(load-relative,
- AS_HELP_STRING([--enable-load-relative], [resolve load paths at run time]),
- [load_relative=$enableval])
-
-AC_ARG_PROGRAM
-
-dnl Checks for programs.
-
-cflagspat=
-test -z "$optflags" ||
- cflagspat="$cflagspat;s|"`eval echo '"'"${optflags}"'"' | sed 's/[[][|.*]]/\\&/g;s/^ */ /;s/ *$/ /'`'| |g'
-test -z "$debugflags" ||
- cflagspat="$cflagspat;s|"`eval echo '"'"${debugflags}"'"' | sed 's/[[][|.*]]/\\&/g;s/^ */ /;s/ *$/ /'`'| |g'
-test -z "warnflags" ||
- cflagspat="$cflagspat;s|"`eval echo '"'"${warnflags}"'"' | sed 's/[[][|.*]]/\\&/g;s/^ */ /;s/ *$/ /'`'| |g'
-if test -z "${CFLAGS+set}"; then
- cflags=`echo " $cflags " | sed "$cflagspat;s/^ *//;s/ *$//"`
- orig_cflags="$cflags"
- cflags="$cflags "'${optflags} ${debugflags} ${warnflags}'
-fi
-if test -z "${CXXFLAGS+set}"; then
- cxxflags=`echo " $cxxflags " | sed "$cflagspat;s/^ *//;s/ *$//"`
- orig_cxxflags="$cxxflags"
- cxxflags="$cxxflags "'${optflags} ${debugflags} ${warnflags}'
-fi
-
-RUBY_NACL
-AS_CASE(["$host_os:$build_os"],
-[darwin*:darwin*], [
- AC_CHECK_TOOLS(CC, [clang gcc cc])
- # Following Apple deployed clang are broken
- # clang version 1.0 (http://llvm.org/svn/llvm-project/cfe/tags/Apple/clang-23 exported)
- # Apple clang version 2.0 (tags/Apple/clang-137) (based on LLVM 2.9svn)
- # Apple clang version 2.1 (tags/Apple/clang-163.7.1) (based on LLVM 3.0svn)
- if ! $CC -E -xc - <<SRC >/dev/null; then
- @%:@if defined __APPLE_CC__ && defined __clang_major__ && __clang_major__ < 3
- @%:@error premature clang
- @%:@endif
-SRC
- AC_MSG_ERROR([clang version 3.0 or later is required])
- fi
-])
-if test x"${build}" != x"${host}"; then
- AC_CHECK_TOOL(CC, gcc)
-fi
-
-AC_PROG_CC
-
-dnl Select the appropriate C++ compiler in OS X
-AS_CASE(["$build_os"],
- [darwin1*.*], [
- AS_CASE(["x$CC"],
- [xgcc-4.2|x/usr/bin/gcc-4.2], [: ${CXX=g++-4.2}],
- [xgcc|x/usr/bin/gcc], [: ${CXX=g++}],
- [xcc|x/usr/bin/cc], [: ${CXX=c++}],
- [xicc], [: ${CXX=icpc}],
- [xclang|x/usr/bin/clang], [: ${CXX=clang++}])
- ])
-test -z "$CXX" || ac_cv_prog_CXX="$CXX"
-
-AC_PROG_CXX
-RUBY_MINGW32
-AC_PROG_GCC_TRADITIONAL
-AC_SUBST(GCC)
-AS_CASE(["$target_os"],
-[solaris*], [AC_PATH_TOOL([LD], [ld], [/usr/ccs/bin/ld], [/usr/ccs/bin:$PATH])],
-[AC_CHECK_TOOL([LD], [ld], [ld])])
-AC_SUBST(LD)
-if test "$GCC" = yes; then
- linker_flag=-Wl,
- : ${optflags=-O3}
- gcc_major=`echo =__GNUC__ | $CC -E -xc - | sed '/^=/!d;s///'`
- gcc_minor=`echo =__GNUC_MINOR__ | $CC -E -xc - | sed '/^=/!d;s///'`
- test -n "$gcc_major" || gcc_major=0
- test -n "$gcc_minor" || gcc_minor=0
- # RUBY_APPEND_OPTIONS(XCFLAGS, ["-include ruby/config.h" "-include ruby/missing.h"])
-else
- linker_flag=
-fi
-
-RUBY_PROG_GNU_LD
-RUBY_CPPOUTFILE
-
-: ${OUTFLAG='-o '}
-: ${COUTFLAG=${OUTFLAG}}
-: ${CSRCFLAG=''}
-AC_SUBST(OUTFLAG)
-AC_SUBST(COUTFLAG)
-AC_SUBST(CSRCFLAG)
-
-cc_version=
-for option in --version -v -V -qversion; do
- cc_version_message=`$CC $option 2>&1`
- cc_version_status=$?
- AS_CASE($cc_version_status, [0], [:], [continue])
- AS_CASE($cc_version_message, [*Warning*], [continue])
- cc_version='$(CC) '$option
- break
-done
-AC_SUBST(CC_VERSION, $cc_version)
-AC_SUBST(CC_VERSION_MESSAGE, $cc_version_message)
-
-RUBY_UNIVERSAL_ARCH
-if test "$target_cpu" != "$host_cpu" -a "$GCC" = yes -a "$cross_compiling" = no -a "$universal_binary" = no; then
- RUBY_DEFAULT_ARCH("$target_cpu")
-fi
-
-AS_CASE(["$target_cpu-$target_os"], [[i[3-6]86*]], [
- AC_CACHE_CHECK([for __sync_val_compare_and_swap], [rb_cv_gcc_compiler_cas], [
- AC_TRY_LINK([unsigned long atomic_var;],
- [
- __sync_val_compare_and_swap(&atomic_var, 0, 1);
- ],
- [rb_cv_gcc_compiler_cas=yes],
- [rb_cv_gcc_compiler_cas=no])])
- if test "$rb_cv_gcc_compiler_cas" = no; then
- unset rb_cv_gcc_compiler_cas
- save_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS -march=i486"
- AC_CACHE_CHECK([for __sync_val_compare_and_swap with -march=i486], [rb_cv_gcc_compiler_cas], [
- AC_TRY_LINK([unsigned long atomic_var;],
- [
- __sync_val_compare_and_swap(&atomic_var, 0, 1);
- ],
- [rb_cv_gcc_compiler_cas=yes
- ARCH_FLAG="-march=i486"],
- [rb_cv_gcc_compiler_cas=no])])
- CFLAGS="$save_CFLAGS"
- fi])
-
-AC_CHECK_TOOL(RANLIB, ranlib, :)
-AC_CHECK_TOOL(AR, ar)
-if test -z "$AR"; then
- AC_CHECK_PROGS(AR, aal, ar)
-fi
-AC_CACHE_CHECK([for $AR D option], [rb_cv_ar_D_option], [
- AS_IF([$AR rcD conftest.a > /dev/null 2>&1 && rm conftest.a],
- [rb_cv_ar_D_option=yes], [rb_cv_ar_D_option=no])
-])
-AS_IF([test "$rb_cv_ar_D_option" = yes], [ARFLAGS='rcD '], [ARFLAGS='rcu '])
-AC_SUBST(ARFLAGS)
-
-AC_CHECK_TOOL(AS, as)
-ASFLAGS=$ASFLAGS
-AC_SUBST(ASFLAGS)
-
-AS_CASE(["$target_os"],[cygwin*|mingw*], [ac_cv_prog_ac_ct_OBJCOPY=":"])
-
-# BSD's ports and MacPorts prefix GNU binutils with 'g'
-AC_CHECK_TOOLS(OBJDUMP, [objdump gobjdump])
-AC_CHECK_TOOLS(OBJCOPY, [objcopy gobjcopy])
-
-AS_CASE(["$target_os"],
-[cygwin*|mingw*], [
- AC_CHECK_TOOL(WINDRES, windres)
- AC_CHECK_TOOL(DLLWRAP, dllwrap)
- target=`echo $target | sed "s/^$target_cpu-/-/"`
- target_alias=`echo $target_alias | sed "s/^$target_cpu-/-/"`
- target_cpu=`echo $target_cpu | sed s/i.86/i386/`
- AS_CASE(["$target"], [-*], [ target="$target_cpu${target}"])
- AS_CASE(["$target_alias"], [-*], [ target_alias="$target_cpu${target_alias}"])
- AS_CASE(["$target_os"],
- [mingw*], [
- test "$rb_cv_msvcrt" = "" && unset rb_cv_msvcrt
- AC_CACHE_CHECK(for mingw32 runtime DLL, rb_cv_msvcrt, [
- AC_TRY_LINK([@%:@include <stdio.h>],
- [FILE* volatile f = stdin; return 0;],
- [rb_cv_msvcrt=`$OBJDUMP -p conftest$ac_exeext |
- tr A-Z a-z |
- sed -n '/^[[ ]]*dll name: \(msvc.*\)\.dll$/{s//\1/p;q;}'`],
- [rb_cv_msvcrt=msvcrt])
- test "$rb_cv_msvcrt" = "" && rb_cv_msvcrt=msvcrt])
- RT_VER=`echo "$rb_cv_msvcrt" | tr -cd [0-9]`
- test "$RT_VER" = "" && RT_VER=60
- AC_DEFINE_UNQUOTED(RUBY_MSVCRT_VERSION, $RT_VER)
- ])
- : ${enable_shared=yes}
- ],
-[aix*], [AC_CHECK_TOOL(NM, nm, /usr/ccs/bin/nm, /usr/ccs/bin:$PATH)],
-[hiuxmpp*], [AC_DEFINE(__HIUX_MPP__)]) # by TOYODA Eizi <toyoda@npd.kishou.go.jp>
-AC_CHECK_TOOL(NM, nm)
-
-AC_PROG_LN_S
-AC_PROG_MAKE_SET
-AC_PROG_INSTALL
-AC_PROG_MKDIR_P
-if test "x$MKDIR_P" = "x -d"; then
- if test x"$as_mkdir_p" != xfalse; then
- MKDIR_P='mkdir -p'
- echo "use 'mkdir -p' as MKDIR_P"
- else
- AC_MSG_ERROR([mkdir -p is required])
- fi
-fi
-MAKEDIRS="$MKDIR_P"
-AC_SUBST(MAKEDIRS)
-
-AC_DEFUN([RUBY_DTRACE_AVAILABLE],
-[AC_CACHE_CHECK(whether dtrace USDT is available, rb_cv_dtrace_available,
-[
- echo "provider conftest{ probe fire(); };" > conftest_provider.d
- if $DTRACE -h -o conftest_provider.h -s conftest_provider.d >/dev/null 2>/dev/null; then
- AC_TRY_COMPILE([@%:@include "conftest_provider.h"], [CONFTEST_FIRE();], [
- # DTrace is available on the system
- rb_cv_dtrace_available=yes
- ], [rb_cv_dtrace_available=no])
- else
- # DTrace is not available while dtrace command exists
- # for example FreeBSD 8 or FreeBSD 9 without DTrace build option
- rb_cv_dtrace_available=no
- fi
- rm -f conftest.[co] conftest_provider.[dho]
-])
-])
-
-AC_DEFUN([RUBY_DTRACE_POSTPROCESS],
-[AC_CACHE_CHECK(whether $DTRACE needs post processing, rb_cv_prog_dtrace_g,
-[
- rb_cv_prog_dtrace_g=no
- if {
- cat >conftest_provider.d <<_PROBES &&
- provider conftest {
- probe fire();
- };
-_PROBES
- $DTRACE -h -o conftest_provider.h -s conftest_provider.d >/dev/null 2>/dev/null &&
- :
- }; then
- AC_TRY_COMPILE([@%:@include "conftest_provider.h"], [CONFTEST_FIRE();], [
- if {
- cp -p conftest.${ac_objext} conftest.${ac_objext}.save &&
- $DTRACE -G -s conftest_provider.d conftest.${ac_objext} 2>/dev/null &&
- :
- }; then
- if cmp -s conftest.o conftest.${ac_objext}.save; then
- rb_cv_prog_dtrace_g=yes
- else
- rb_cv_prog_dtrace_g=rebuild
- fi
- fi])
- fi
- rm -f conftest.[co] conftest_provider.[dho]
-])
-])
-
-AC_CHECK_PROG([DTRACE], [${ac_tool_prefix}dtrace], [${ac_tool_prefix}dtrace])
-if test "$cross_compiling:$ac_cv_prog_DTRACE" = no: -a -n "$ac_tool_prefix"; then
- AC_CHECK_PROG([DTRACE], [dtrace], [dtrace])
-fi
-
-AC_CHECK_PROGS(DOT, dot)
-AC_CHECK_PROGS(DOXYGEN, doxygen)
-AS_CASE(["${host_os}"], [nacl], [AC_PATH_PROG(PYTHON, python)])
-
-AC_CHECK_PROG(PKG_CONFIG, pkg-config, [pkg-config], [], [],
- [`"$as_dir/$ac_word$ac_exec_ext" --print-errors --version > /dev/null 2>&1 || echo "$as_dir/$ac_word$ac_exec_ext"`])
-
-# checks for UNIX variants that set C preprocessor variables
-AC_USE_SYSTEM_EXTENSIONS
-
-AC_SUBST(RM, ['rm -f'])
-AC_SUBST(CP, ['cp'])
-RMDIRS='$(top_srcdir)/tool/rmdirs'
-RMDIR=rmdir
-mkdir "rmdirs_$$_test" "rmdirs_$$_test/a"
-rmdir --ignore-fail-on-non-empty "rmdirs_$$_test" 2>/dev/null &&
-RMDIR='rmdir --ignore-fail-on-non-empty'
-$RMDIR -p "rmdirs_$$_test/a" 2>/dev/null &&
-{ test -d "rmdirs_$$_test" || RMDIRS="$RMDIR -p"; }
-rmdir "rmdirs_$$_test/a" "rmdirs_$$_test" 2>/dev/null
-AC_SUBST(RMDIR)
-AC_SUBST(RMDIRS)
-AC_SUBST(RMALL, ['rm -fr'])
-
-AC_MSG_CHECKING([for cd using physical directory])
-rm -fr conf$$.dir
-mkdir conf$$.dir &&
-(cd conf$$.dir && mkdir src build && cd src &&
-$as_ln_s ../build . > /dev/null 2>&1 && cd build &&
-for chdir in 'cd -P' 'PWD= cd'; do
- /bin/sh -c "$chdir ../src && echo '$chdir' > cdcmd" 2> /dev/null && break
-done)
-if test -f conf$$.dir/src/cdcmd; then
- read CHDIR < conf$$.dir/src/cdcmd 2> /dev/null
-else
- CHDIR=cd
-fi
-rm -fr conf$$.dir
-AC_MSG_RESULT([$CHDIR])
-AC_SUBST(CHDIR)
-
-}
-{ # compiler section
-
-AC_DEFUN([RUBY_WERROR_FLAG], [dnl
-save_CFLAGS="$CFLAGS"
-CFLAGS="$CFLAGS $rb_cv_warnflags"
-if test "${ac_c_werror_flag+set}"; then
- rb_c_werror_flag="$ac_c_werror_flag"
-else
- unset rb_c_werror_flag
-fi
-ac_c_werror_flag=yes
-$1
-CFLAGS="$save_CFLAGS"
-save_CFLAGS=
-if test "${rb_c_werror_flag+set}"; then
- ac_c_werror_flag="$rb_c_werror_flag"
-else
- unset ac_c_werror_flag
-fi])
-
-RUBY_WERROR_FLAG([
- AC_MSG_CHECKING([whether CFLAGS is valid])
- AC_TRY_COMPILE([], [],
- [AC_MSG_RESULT(yes)],
- [
- AC_MSG_RESULT(no)
- AC_MSG_ERROR([something wrong with CFLAGS="$CFLAGS"])
- ]
- )
- AC_MSG_CHECKING([whether LDFLAGS is valid])
- {
- mkdir tmp.$$.try_link &&
- cd tmp.$$.try_link &&
- cp ../confdefs.h . &&
- echo '<?xml?><plist><dict><key>CFBundleIdentifier</key><string></string></dict></plist>' > Info.plist &&
- :
- } || AC_MSG_ERROR([failed to make temporary directory])
- AC_TRY_LINK([], [],
- [AC_MSG_RESULT(yes)],
- [
- cd .. && rm -fr tmp.$$.try_link
- AC_MSG_RESULT(no)
- AC_MSG_ERROR([something wrong with LDFLAGS="$LDFLAGS"])
- ]
- )
- cd .. && rm -fr tmp.$$.try_link
-])
-
-AC_DEFUN([RUBY_TRY_CFLAGS], [
- AC_MSG_CHECKING([whether ]$1[ is accepted as CFLAGS])
- RUBY_WERROR_FLAG([
- CFLAGS="[$]CFLAGS $1"
- AC_TRY_COMPILE([$4], [$5],
- [$2
- AC_MSG_RESULT(yes)],
- [$3
- AC_MSG_RESULT(no)])
- ])
-])
-
-AC_DEFUN([RUBY_TRY_LDFLAGS], [
- save_LDFLAGS="$LDFLAGS"
- LDFLAGS="[$]LDFLAGS $1"
- AC_MSG_CHECKING([whether $1 is accepted as LDFLAGS])
- RUBY_WERROR_FLAG([
- AC_TRY_LINK([$4], [$5],
- [$2
- AC_MSG_RESULT(yes)],
- [$3
- AC_MSG_RESULT(no)])
- ])
- LDFLAGS="$save_LDFLAGS"
- save_LDFLAGS=
-])
-
-: ${DLDFLAGS="$LDFLAGS"}
-
-AS_CASE([$RUBY_PATCHLEVEL], [-*],
- [particular_werror_flags=yes], [particular_werror_flags=no])
-AC_ARG_ENABLE(werror,
- AS_HELP_STRING([--disable-werror],
- [don't make warnings into errors
- even if a compiler support -Werror feature
- [[disabled by default unless development version]]]),
- [particular_werror_flags=$enableval])
-
-rb_cv_warnflags="$warnflags"
-if test "$GCC:${warnflags+set}:no" = yes::no; then
- if test $gcc_major -ge 4; then
- extra_warning=-Werror=extra-tokens
- else
- extra_warning=
- fi
- if test $gcc_major -ge 5 -a $gcc_major -le 6; then
- extra_warning="$extra_warning -Wno-maybe-uninitialized"
- fi
- for wflag in -Wno-unused-parameter -Wno-parentheses -Wno-long-long \
- -diag-disable=2259 \
- -Wno-missing-field-initializers \
- -Wno-tautological-compare \
- -Wno-parentheses-equality \
- -Wno-constant-logical-operand \
- -Wno-self-assign \
- -Wunused-variable \
- -Werror=implicit-int \
- -Werror=pointer-arith \
- -Werror=write-strings \
- -Werror=declaration-after-statement \
- -Werror=shorten-64-to-32 \
- -Werror=implicit-function-declaration \
- -Werror=division-by-zero \
- -Werror=deprecated-declarations \
- -Wno-packed-bitfield-compat \
- -Wsuggest-attribute=noreturn \
- -Wsuggest-attribute=format \
- -Wimplicit-fallthrough=0 \
- $extra_warning \
- ; do
- if test "$particular_werror_flags" != yes; then
- wflag=`echo x$wflag | sed 's/^x-Werror=/-W/;s/^x//'`
- fi
- ok=no
- RUBY_TRY_CFLAGS($wflag, [
- RUBY_APPEND_OPTIONS(warnflags, $wflag)
- ok=yes
- ])
- AS_CASE([$ok:$wflag], [no:-Werror=*], [
- wflag=`echo x$wflag | sed 's/^x-Werror=/-W/'`
- RUBY_TRY_CFLAGS($wflag, [
- RUBY_APPEND_OPTIONS(warnflags, $wflag)
- particular_werror_flags=no
- ])
- ])
- done
- AS_CASE([" $warnflags "],[*" -Wno-missing-field-initializers "*], [wflag="-Wall -Wextra"],
- [wflag=-Wall])
- RUBY_TRY_CFLAGS($wflag, [warnflags="$wflag${warnflags+ $warnflags}"])
- # Disable warnflags while conftest. -Werror=* flags might make bad OS capability guess.
- rb_cv_warnflags="$warnflags"
- warnflags=
-fi
-RUBY_TRY_CFLAGS(-Qunused-arguments, [RUBY_APPEND_OPTIONS(rb_cv_wsuppress_flags, -Qunused-arguments)])
-
-for n in infinity nan; do
- m=AS_TR_CPP($n)
- AC_CACHE_CHECK([whether $m is available without C99 option], rb_cv_$n,
- [AC_COMPILE_IFELSE(
- [AC_LANG_BOOL_COMPILE_TRY(AC_INCLUDES_DEFAULT([@%:@include <math.h>
-@%:@ifndef $m
-@%:@error no $m
-@%:@endif
-]), [1])], [eval rb_cv_$n=yes], [eval rb_cv_$n=no])])
- if eval test '"$rb_cv_'$n'"' = yes; then
- AC_DEFINE_UNQUOTED([HAVE_]$m)
- fi
-done
-
-if test "$GCC" = yes; then
- # NaCl's glibc build generates undefined references to __memset_chk.
- # TODO(sbc): Remove this once NaCl's glibc is fixed.
- AS_CASE(["$target_os"], [nacl], [], [
- # -D_FORTIFY_SOURCE
- # When defined _FORTIFY_SOURCE, glibc enables some additional sanity
- # argument check. The performance drop is very little and Ubuntu enables
- # _FORTIFY_SOURCE=2 by default. So, let's support it for protecting us from
- # a mistake of silly C extensions.
- RUBY_TRY_CFLAGS(-D_FORTIFY_SOURCE=2, [RUBY_APPEND_OPTION(XCFLAGS, -D_FORTIFY_SOURCE=2)])
- ])
-
- # -fstack-protector
- AS_CASE(["$target_os"],
- [mingw*|nacl], [
- stack_protector=no
- ])
- if test -z "${stack_protector+set}"; then
- RUBY_TRY_CFLAGS(-fstack-protector, [stack_protector=yes], [stack_protector=no])
- if test "x$stack_protector" = xyes; then
- RUBY_TRY_LDFLAGS(-fstack-protector, [], [stack_protector=broken])
- fi
- fi
- if test "x$stack_protector" = xyes; then
- RUBY_APPEND_OPTION(XCFLAGS, -fstack-protector)
- RUBY_APPEND_OPTION(XLDFLAGS, -fstack-protector)
- RUBY_APPEND_OPTION(LDFLAGS, -fstack-protector)
- fi
-
- RUBY_TRY_LDFLAGS(${linker_flag}--compress-debug-sections=zlib,
- [compress_debug_sections=yes],
- [compress_debug_sections=no])
- if test "x$compress_debug_sections" = xyes; then
- RUBY_APPEND_OPTION(DLDFLAGS, ${linker_flag}--compress-debug-sections=zlib)
- fi
-
- AS_CASE(["$target_os"],[mingw*], [
- # On Windows platforms, system provided headers are VC++
- # optimized. That is, C++ habits are often contaminated into
- # various headers. Most frequent situation is the use of //
- # comments. We bypass ANSI C mode for them. Otherwise
- # extension libs cannot include those headers.
- ],
- [cygwin*|darwin*|netbsd*|nacl], [
- # need lgamma_r(), finite()
- ],
- [
- # ANSI (no XCFLAGS because this is C only)
- 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)])
-
- test "${debugflags+set}" || {RUBY_TRY_CFLAGS(-ggdb3, [debugflags=-ggdb3])}
- test "${debugflags+set}" || {RUBY_TRY_CFLAGS(-ggdb, [debugflags=-ggdb])}
- test "${debugflags+set}" || {RUBY_TRY_CFLAGS(-g3, [debugflags=-g3])}
-fi
-test $ac_cv_prog_cc_g = yes && : ${debugflags=-g}
-
-if test "$GCC" = ""; then
- AS_CASE(["$target_os"],[aix*],[warnflags="$warnflags -qinfo=por" rb_cv_warnflags="$rb_cv_warnflags -qinfo=por"])
-fi
-if test "$GCC" = yes; then
- if test "$gcc_major" -ge 4; then
- RUBY_TRY_CFLAGS(-fvisibility=hidden, [visibility_option=yes], [visibility_option=no])
- fi
- AC_SUBST(WERRORFLAG, "-Werror")
- if test "$visibility_option" = yes; then
- RUBY_APPEND_OPTION(XCFLAGS, -fvisibility=hidden)
- AC_DEFINE(RUBY_SYMBOL_EXPORT_BEGIN, [_Pragma("GCC visibility push(default)")])
- AC_DEFINE(RUBY_SYMBOL_EXPORT_END, [_Pragma("GCC visibility pop")])
- else
- RUBY_TRY_LDFLAGS([-Wl,-unexported_symbol,_Init_*], [visibility_option=ld], [visibility_option=no])
- fi
- test "$visibility_option" = no -o "$host_os" = nacl || OBJCOPY=:
-fi
-
-if test "$GCC" = yes; then
- # optflags
-
- AS_CASE(["$target_os"], [mingw*], [
- RUBY_TRY_CFLAGS(-fno-omit-frame-pointer, [optflags="${optflags+$optflags }-fno-omit-frame-pointer"])
- RUBY_TRY_CFLAGS(-static-libgcc, [static_libgcc=yes], [static_libgcc=no])
- if test "$static_libgcc" = yes; then
- RUBY_APPEND_OPTION(EXTLDFLAGS, -static-libgcc)
- fi
- ])
-
- # disable fast-math
- for oflag in -fno-fast-math; do
- RUBY_TRY_CFLAGS($oflag, [RUBY_APPEND_OPTION(optflags, $oflag)])
- done
- for oflag in -fexcess-precision=standard -fp-model\ precise; do
- RUBY_TRY_CFLAGS($oflag, [RUBY_APPEND_OPTION(XCFLAGS, $oflag)])
- done
-fi
-
-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|g;s/^ //"`
- LDFLAGS="$LDFLAGS $val"
- LDFLAGS_OPTDIR="$val"
- OPT_DIR="$withval"
- ], [OPT_DIR=])
-
-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\""
-
-}
-{ # header and library section
-
-AC_ARG_WITH(winnt-ver,
- AS_HELP_STRING([--with-winnt-ver=0xXXXX], [target Windows NT version (default to 0x0501)]),
- [with_winnt_ver="$withval"], [with_winnt_ver="0x0501"])
-AS_CASE(["$target_os"],
-[mingw*], [
- RUBY_APPEND_OPTION(CPPFLAGS, -D_WIN32_WINNT=$with_winnt_ver)
- RUBY_APPEND_OPTION(CPPFLAGS, -D__MINGW_USE_VC2005_COMPAT)
-])
-
-AS_CASE(["$target_os"],
-[freebsd*], [
- AC_CACHE_CHECK([whether pthread should be enabled by default],
- rb_cv_enable_pthread_default,
- [AC_TRY_CPP([
-#include <osreldate.h>
-#if __FreeBSD_version < 502102
-#error pthread should be disabled on this platform
-#endif
- ],
- rb_cv_enable_pthread_default=yes,
- rb_cv_enable_pthread_default=no)])
- enable_pthread=$rb_cv_enable_pthread_default
- ],
-[mingw*], [
- enable_pthread=no
- ],
-[
- enable_pthread=yes
- ])
-
-AC_ARG_ENABLE(pthread,
- AS_HELP_STRING([--enable-pthread], [obsolete, and ignored]))
-
-dnl Checks for libraries.
-AS_CASE(["$target_os"],[*bsd*|dragonfly*],[],[ac_cv_func_daemon=no])
-
-POSTLINK=:
-AC_SUBST(POSTLINK)
-AS_CASE(["$target_os"],
-[nextstep*], [ ],
-[openstep*], [ ],
-[rhapsody*], [ ],
-[darwin*], [ RUBY_PREPEND_OPTION(LIBS, -lobjc)
- RUBY_APPEND_OPTIONS(CPPFLAGS, -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT)
- AC_MSG_CHECKING(whether Mac OS X 10.5 or later)
- AC_TRY_CPP([#include <AvailabilityMacros.h>
- #if MAC_OS_X_VERSION_MAX_ALLOWED <= 1040
- #error pre OS X 10.4
- [!<===== pre OS X 10.4 =====>]
- #endif
- ],
- [macosx_10_5=yes], [macosx_10_5=no])
- AC_MSG_RESULT($macosx_10_5)
- if test $macosx_10_5 = yes; then
- ac_cv_func_getcontext=no
- ac_cv_func_setcontext=no
- else
- AC_DEFINE(BROKEN_SETREUID, 1)
- AC_DEFINE(BROKEN_SETREGID, 1)
- fi
- incs=`$CC -v -E -xc - < /dev/null 2>&1 | sed ['1,/^@%:@include </d;s/^ *//;s|[^./][^/]*/\.\./||g;/\/include$/!d;s||/lib|;/\/usr\/lib/d']`
- for d in `$CC -print-search-dirs | sed -e '/^libraries: */!d;s///' | tr : '\012' | fgrep -v /../ | sed -n 's|^\(/.*/lib\)/$|\1|p'`; do
- incs=`echo "$incs" | fgrep -v "$d"`
- done
- for d in $incs; do
- test -d "$d" && RUBY_APPEND_OPTIONS(LDFLAGS, "-L$d")
- done
- ac_cv_type_getgroups=gid_t # getgroups() on Rosetta fills garbage
- ac_cv_lib_crypt_crypt=no
- ac_cv_func_fdatasync=no # Mac OS X wrongly reports it has fdatasync()
- ac_cv_func_vfork=no
- if test $gcc_major -lt 4 -o \( $gcc_major -eq 4 -a $gcc_minor -lt 3 \); then
- ac_cv_func___builtin_setjmp=no
- fi
- AC_CACHE_CHECK(for broken crypt with 8bit chars, rb_cv_broken_crypt,
- [AC_TRY_RUN([
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-
-void
-broken_crypt(const char *salt, const char *buf1, const char *buf2)
-{
-#if 0
- printf("%.2x%.2x: %s -> %s\n", (unsigned char)salt[0], (unsigned char)salt[1],
- buf1+2, buf2+2);
-#endif
-}
-
-int
-main()
-{
- int i;
- char salt[2], buf[256], *s;
- for (i = 0; i < 128*128; i++) {
- salt[0] = 0x80 | (i & 0x7f);
- salt[1] = 0x80 | (i >> 7);
- strcpy(buf, crypt("", salt));
- if (strcmp(buf, s = crypt("", salt))) {
- broken_crypt(salt, buf, s);
- return 1;
- }
- }
- salt[0] = salt[1] = ' ';
- strcpy(buf, crypt("", salt));
- salt[0] = salt[1] = 0x80 | ' ';
- if (strcmp(buf, s = crypt("", salt))) {
- broken_crypt(salt, buf, s);
- return 1;
- }
- return 0;
-}
-],
- rb_cv_broken_crypt=no,
- rb_cv_broken_crypt=yes,
- rb_cv_broken_crypt=yes)])
- if test "$rb_cv_broken_crypt" = yes; then
- AC_DEFINE(BROKEN_CRYPT, 1)
- fi
- AC_CHECK_PROGS(codesign, codesign)
- if test -n "$codesign"; then
- POSTLINK="test -z '\$(RUBY_CODESIGN)' || $codesign -s '\$(RUBY_CODESIGN)' -f \$@"
- LINK_SO="$LINK_SO
-\$(POSTLINK)"
- fi
- AC_CHECK_HEADERS(crt_externs.h, [], [], [
- #include <crt_externs.h>
- ])
- ],
-[hpux*], [ LIBS="-lm $LIBS"
- ac_cv_c_inline=no],
-[solaris*], [ LIBS="-lm $LIBS"
- ac_cv_func_vfork=no
- AC_MSG_CHECKING(whether _XOPEN_SOURCE is already given)
- AC_TRY_COMPILE([#include <unistd.h>
- #ifndef _XOPEN_SOURCE
- #error _XOPEN_SOURCE is not defined
- #endif
- ], [],
- [given_xopen_source=yes], [given_xopen_source=no])
- AC_MSG_RESULT($given_xopen_source)
- if test $given_xopen_source = no; then
- # On Solaris, with gcc, -std=iso9899:1999 in $ansi_options
- # is often also needed in CPPFLAGS, because some feature
- # definitions vary depending on such standards options.
- AS_CASE(["${ansi_options}"],
- [*-std=iso9899:1999*], [
- RUBY_APPEND_OPTIONS(CPPFLAGS, ${ansi_options})
- ])
- AC_MSG_CHECKING(appropriate _XOPEN_SOURCE value to define)
- define_xopen_source=""
- for tmp_xpg in 7 6 5; do
- if test x"$define_xopen_source" != x; then
- break
- fi
- # Both AC_TRY_CPP and AC_TRY_COMPILE should pass
- # because some options may not be set to CPPFLAGS.
- AC_TRY_CPP([
- #define _XOPEN_SOURCE ${tmp_xpg}00
- #include <unistd.h>
- #ifndef _XPG${tmp_xpg}
- #error _XPG${tmp_xpg} should be defined by _XOPEN_SOURCE=${tmp_xpg}00
- #endif
- ], [
- AC_TRY_COMPILE([
- #define _XOPEN_SOURCE ${tmp_xpg}00
- #include <unistd.h>
- #ifndef _XPG${tmp_xpg}
- #error _XPG${tmp_xpg} should be defined by _XOPEN_SOURCE=${tmp_xpg}00
- #endif
- ], [],
- [define_xopen_source=${tmp_xpg}00], [])
- ], [])
- done
- if test x"$define_xopen_source" = x; then
- define_xopen_source=no
- fi
- AC_MSG_RESULT($define_xopen_source)
- if test x"$define_xopen_source" != xno; then
- RUBY_APPEND_OPTIONS(CPPFLAGS, -D_XOPEN_SOURCE=$define_xopen_source)
- fi
- fi
- ],
-[haiku*], [
- LIBS="$LIBS" # m lib is include in root
- ],
-[cygwin*], [ ac_cv_header_langinfo_h=yes
- RUBY_APPEND_OPTIONS(CPPFLAGS, -D_XOPEN_SOURCE -D_GNU_SOURCE)
- AC_CHECK_FUNCS(cygwin_conv_path)
- AC_LIBOBJ([langinfo])
- ],
-[mingw*], [ LIBS="-lshell32 -lws2_32 -liphlpapi -limagehlp -lshlwapi $LIBS"
- ac_cv_header_a_out_h=no
- ac_cv_header_pwd_h=no
- ac_cv_header_utime_h=no
- ac_cv_header_sys_ioctl_h=no
- ac_cv_header_sys_param_h=no
- ac_cv_header_sys_resource_h=no
- ac_cv_header_sys_select_h=no
- ac_cv_header_sys_time_h=no
- ac_cv_header_sys_times_h=no
- ac_cv_header_sys_socket_h=no
- ac_cv_func_lstat=yes
- ac_cv_func_times=yes
- ac_cv_func_waitpid=yes
- ac_cv_func_fsync=yes
- ac_cv_func_seekdir=yes
- ac_cv_func_telldir=yes
- ac_cv_func_isinf=yes
- ac_cv_func_isnan=yes
- ac_cv_func_finite=yes
- ac_cv_func_lchown=yes
- ac_cv_func_link=yes
- ac_cv_func_readlink=yes
- ac_cv_func_symlink=yes
- ac_cv_lib_crypt_crypt=no
- 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=no
- 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
- AS_IF([test "$target_cpu" = x64], [
- ac_cv_func___builtin_setjmp=no
- ac_cv_func_round=no
- ])
- AC_CHECK_TYPE([NET_LUID], [], [],
- [@%:@include <winsock2.h>
- @%:@include <iphlpapi.h>])
- if test x"$ac_cv_type_NET_LUID" = xyes; then
- AC_DEFINE(HAVE_TYPE_NET_LUID, 1)
- fi
- AC_CHECK_FUNCS(_gmtime64_s)
- AC_CHECK_FUNCS(_wfreopen_s)
- AC_LIBOBJ([langinfo])
- ],
-[bsdi*], [ LIBS="-lm $LIBS"
- AC_DEFINE(BROKEN_SETREUID, 1)
- AC_DEFINE(BROKEN_SETREGID, 1)
- ac_cv_sizeof_rlim_t=8],
-[freebsd*], [ LIBS="-lm $LIBS"
- ac_cv_func_getpeername=no
- ac_cv_func_getsockname=no
- ac_cv_func_shutdown=no
- ac_cv_func_close=no
- ],
-[netbsd*], [ LIBS="-lm $LIBS"
- ],
-[dragonfly*], [ LIBS="-lm $LIBS"
- # isinf() and isnan() are macros on DragonFly.
- ac_cv_func_isinf=yes
- ac_cv_func_isnan=yes
- ],
-[aix*],[ LIBS="-lm $LIBS"
- ac_cv_func_round=no
- ],
-[nacl], [
- LIBS="-lm $LIBS"
- if test "${nacl_cv_build_variant}" = "newlib"; then
- RUBY_APPEND_OPTION(CPPFLAGS, -DNACL_NEWLIB)
- else
- RUBY_APPEND_OPTION(XCFLAGS, -fPIC)
- fi
- ],
-[ LIBS="-lm $LIBS"])
-
-AC_CHECK_LIB(crypt, crypt) # glibc (GNU/Linux, GNU/Hurd, GNU/kFreeBSD)
-AC_CHECK_LIB(dl, dlopen) # Dynamic linking for SunOS/Solaris and SYSV
-AC_CHECK_LIB(dld, shl_load) # Dynamic linking for HP-UX
-AC_CHECK_LIB(socket, shutdown) # SunOS/Solaris
-
-dnl Checks for header files.
-AC_HEADER_DIRENT
-dnl AC_HEADER_STDC has been checked in AC_USE_SYSTEM_EXTENSIONS
-AC_HEADER_STDBOOL
-AC_HEADER_SYS_WAIT
-
-AC_CHECK_HEADERS(a.out.h)
-AC_CHECK_HEADERS(atomic.h)
-AC_CHECK_HEADERS(direct.h)
-AC_CHECK_HEADERS(grp.h)
-AC_CHECK_HEADERS(fcntl.h)
-AC_CHECK_HEADERS(float.h)
-AC_CHECK_HEADERS(ieeefp.h)
-AC_CHECK_HEADERS(intrinsics.h)
-AC_CHECK_HEADERS(langinfo.h)
-AC_CHECK_HEADERS(limits.h)
-AC_CHECK_HEADERS(locale.h)
-AC_CHECK_HEADERS(malloc.h)
-AC_CHECK_HEADERS(malloc/malloc.h)
-AC_CHECK_HEADERS(malloc_np.h)
-AC_CHECK_HEADERS(net/socket.h)
-AC_CHECK_HEADERS(process.h)
-AC_CHECK_HEADERS(pwd.h)
-AC_CHECK_HEADERS(setjmpex.h)
-AC_CHECK_HEADERS(sys/attr.h)
-AC_CHECK_HEADERS(sys/fcntl.h)
-AC_CHECK_HEADERS(sys/file.h)
-AC_CHECK_HEADERS(sys/id.h)
-AC_CHECK_HEADERS(sys/ioctl.h)
-AC_CHECK_HEADERS(sys/mkdev.h)
-AC_CHECK_HEADERS(sys/param.h)
-AC_CHECK_HEADERS(sys/prctl.h)
-AC_CHECK_HEADERS(sys/resource.h)
-AC_CHECK_HEADERS(sys/select.h)
-AC_CHECK_HEADERS(sys/sendfile.h)
-AC_CHECK_HEADERS(sys/socket.h)
-AC_CHECK_HEADERS(sys/syscall.h)
-AC_CHECK_HEADERS(sys/sysmacros.h)
-AC_CHECK_HEADERS(sys/time.h)
-AC_CHECK_HEADERS(sys/times.h)
-AC_CHECK_HEADERS(sys/uio.h)
-AC_CHECK_HEADERS(sys/utime.h)
-AC_CHECK_HEADERS(syscall.h)
-AC_CHECK_HEADERS(time.h)
-AC_CHECK_HEADERS(ucontext.h)
-AC_CHECK_HEADERS(utime.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],
- AC_CHECK_LIB([gmp], [__gmpz_init]))
- with_gmp="$ac_cv_lib_gmp___gmpz_init"
- AS_IF([test -z "$with_gmp"], [with_gmp=no])])
-
-AC_ARG_WITH([jemalloc],
- [AS_HELP_STRING([--with-jemalloc],[use jemalloc allocator])],
- [with_jemalloc=$withval], [with_jemalloc=no])
-AS_IF([test "x$with_jemalloc" = xyes],[
- AC_CHECK_LIB([jemalloc], [malloc_conf], [], [with_jemalloc=no])
- AC_CHECK_HEADER(jemalloc/jemalloc.h, [
- AC_DEFINE(RUBY_ALTERNATIVE_MALLOC_HEADER, [<jemalloc/jemalloc.h>])
- ])
- AS_IF([test "x$with_jemalloc" = xno], [
- AC_CACHE_CHECK([for jemalloc with JEMALLOC_MANGLE], rb_cv_jemalloc_demangle,
- [AC_LINK_IFELSE([AC_LANG_PROGRAM([@%:@define JEMALLOC_MANGLE 1
- @%:@ifdef RUBY_ALTERNATIVE_MALLOC_HEADER
- @%:@include RUBY_ALTERNATIVE_MALLOC_HEADER
- @%:@else
- @%:@include <jemalloc.h>
- @%:@endif], [return !&malloc_conf])],
- [rb_cv_jemalloc_demangle=yes],
- [rb_cv_jemalloc_demangle=no])
- ])
- ])
- AS_IF([test "x$rb_cv_jemalloc_demangle" = xyes], [
- AC_DEFINE(JEMALLOC_MANGLE)
- with_jemalloc=yes
- ])
- AS_IF([test "x$with_jemalloc" = xyes],
- [
- ac_cv_func_malloc_usable_size=yes
- ],
- [AC_MSG_ERROR([jemalloc requested but not found])
- ])
-])
-
-dnl check for large file stuff
-mv confdefs.h confdefs1.h
-: > confdefs.h
-AC_SYS_LARGEFILE
-# On 32-bit Solaris, it is safe to define _LARGEFILE_SOURCE
-# which is not added by AC_SYS_LARGEFILE.
-if test x"$enable_largefile" != xno; then
- AS_CASE(["$target_os"], [solaris*], [
- AC_MSG_CHECKING([wheather _LARGEFILE_SOURCE should be defined])
- AS_CASE(["${ac_cv_sys_file_offset_bits}:${ac_cv_sys_large_files}"],
- ["64:"|"64:no"|"64:unknown"], [
- # insert _LARGEFILE_SOURCE before _FILE_OFFSET_BITS line
- # that is the same order as "getconf LFS_CFLAGS" output
- mv confdefs.h largefile0.h
- : > confdefs.h
- AC_DEFINE(_LARGEFILE_SOURCE)
- cat largefile0.h >> confdefs.h
- rm largefile0.h
- AC_MSG_RESULT([yes])
- ], [AC_MSG_RESULT([no])])
- ])
-fi
-mv confdefs.h largefile.h
-mv confdefs1.h confdefs.h
-cat largefile.h >> confdefs.h
-
-AS_CASE(["$target_os"],
- [mingw*], [ac_cv_type_off_t=yes;ac_cv_sizeof_off_t=8],
- [aix*], [
- AS_CASE(["$target_cpu:$ac_cv_sys_large_files"],
- [ppc64:*|powerpc64:*], [],
- [*:no|*:unknown], [],
- [
- # AIX currently does not support a 32-bit call to posix_fadvise()
- # if _LARGE_FILES is defined.
- ac_cv_func_posix_fadvise=no
- ])
- ])
-
-AC_C_BIGENDIAN
-AC_C_CONST
-AC_C_CHAR_UNSIGNED
-AC_C_INLINE
-AC_C_VOLATILE
-AC_C_TYPEOF
-
-AS_CASE(":$ac_cv_c_const:$ac_cv_c_volatile:",
- [*:no:*], [AC_MSG_ERROR(ANSI C-conforming const and volatile are mandatory)])
-
-AC_CHECK_TYPES([long long, off_t])
-
-AC_CACHE_CHECK([char bit], [rb_cv_char_bit],
- [test "$universal_binary" = yes && cross_compiling=yes
- AC_COMPUTE_INT([rb_cv_char_bit], [CHAR_BIT],
- [AC_INCLUDES_DEFAULT([@%:@include <limits.h>])], [rb_cv_char_bit=8])
- test "$universal_binary" = yes && cross_compiling=$real_cross_compiling])
-
-dnl RUBY_CHECK_SIZEOF [typename], [maybe same size types], [macros], [include]
-AC_DEFUN([RUBY_CHECK_SIZEOF],
-[dnl
-AS_VAR_PUSHDEF([rbcv_var], [rbcv_sizeof_var])dnl
-AS_VAR_PUSHDEF([cond], [rbcv_sizeof_cond])dnl
-AS_VAR_PUSHDEF([t], [rbcv_sizeof_type])dnl
-AS_VAR_PUSHDEF([s], [rbcv_sizeof_size])dnl
-]
-[m4_bmatch([$1], [\.], [], [if test "$universal_binary" = yes; then])
-AC_CACHE_CHECK([size of $1], [AS_TR_SH([ac_cv_sizeof_$1])], [
- unset AS_TR_SH(ac_cv_sizeof_$1)
- rbcv_var="
-typedef m4_bpatsubst([$1], [\..*]) ac__type_sizeof_;
-static ac__type_sizeof_ *rbcv_ptr;
-@%:@define AS_TR_CPP(SIZEOF_$1) sizeof((*rbcv_ptr)[]m4_bmatch([$1], [\.], .m4_bpatsubst([$1], [^[^.]*\.])))
-"
- m4_ifval([$2], [test -z "${AS_TR_SH(ac_cv_sizeof_$1)+set}" && {
- for t in $2; do
- AC_COMPILE_IFELSE(
- [AC_LANG_BOOL_COMPILE_TRY(AC_INCLUDES_DEFAULT([$4]
- [$rbcv_var]),
- [AS_TR_CPP(SIZEOF_$1) == sizeof($t)])], [
- AS_TR_SH(ac_cv_sizeof_$1)=AS_TR_CPP([SIZEOF_]$t)
- break])
- done
- }], [
- AC_COMPUTE_INT([AS_TR_SH(ac_cv_sizeof_$1)], [AS_TR_CPP(SIZEOF_$1)],
- [AC_INCLUDES_DEFAULT([$4])
-$rbcv_var],
- [AS_TR_SH(ac_cv_sizeof_$1)=])
- ])
- unset cond
- m4_ifval([$3], [test -z "${AS_TR_SH(ac_cv_sizeof_$1)+set}" && {
- for s in 32 64 128; do
- for t in $3; do
- cond="${cond}
-@%:@${cond+el}if defined(__${t}${s}__) || defined(__${t}${s}) || defined(_${t}${s}) || defined(${t}${s})"
- hdr="AC_INCLUDES_DEFAULT([$4
-@%:@if defined(__${t}${s}__) || defined(__${t}${s}) || defined(_${t}${s}) || defined(${t}${s})
-@%:@ define AS_TR_CPP(HAVE_$1) 1
-@%:@else
-@%:@ define AS_TR_CPP(HAVE_$1) 0
-@%:@endif])"
- AC_COMPILE_IFELSE([AC_LANG_BOOL_COMPILE_TRY([$hdr], [!AS_TR_CPP(HAVE_$1)])], [continue])
- AC_COMPILE_IFELSE([AC_LANG_BOOL_COMPILE_TRY([$hdr]
- [$rbcv_var],
- [AS_TR_CPP(HAVE_$1) == (AS_TR_CPP(SIZEOF_$1) == ($s / $rb_cv_char_bit))])],
- [AS_TR_SH(ac_cv_sizeof_$1)="${AS_TR_SH(ac_cv_sizeof_$1)+${AS_TR_SH(ac_cv_sizeof_$1)-} }${t}${s}"; continue])
- AC_COMPILE_IFELSE([AC_LANG_BOOL_COMPILE_TRY([$hdr]
-[
-@%:@if AS_TR_CPP(HAVE_$1)
-$rbcv_var
-@%:@else
-@%:@define AS_TR_CPP(SIZEOF_$1) 0
-@%:@endif
-],
- [AS_TR_CPP(HAVE_$1) == (AS_TR_CPP(SIZEOF_$1) == (m4_bmatch([$2], [^[0-9][0-9]*$], [$2], [($s / $rb_cv_char_bit)])))])],
- [AS_TR_SH(ac_cv_sizeof_$1)="${AS_TR_SH(ac_cv_sizeof_$1)+${AS_TR_SH(ac_cv_sizeof_$1)-} }${t}${s}m4_bmatch([$2], [^[0-9][0-9]*$], [:$2])"])
- done
- done
- }])
- test "${AS_TR_SH(ac_cv_sizeof_$1)@%:@@<:@1-9@:>@}" = "${AS_TR_SH(ac_cv_sizeof_$1)}" &&
- m4_ifval([$2][$3],
- [test "${AS_TR_SH(ac_cv_sizeof_$1)@%:@SIZEOF_}" = "${AS_TR_SH(ac_cv_sizeof_$1)}" && ]){
- test "$universal_binary" = yes && cross_compiling=yes
- AC_COMPUTE_INT([t], AS_TR_CPP(SIZEOF_$1), [AC_INCLUDES_DEFAULT([$4])]
-[${cond+$cond
-@%:@else}
-$rbcv_var
-${cond+@%:@endif}
-@%:@ifndef AS_TR_CPP(SIZEOF_$1)
-@%:@define AS_TR_CPP(SIZEOF_$1) 0
-@%:@endif], [t=0])
- test "$universal_binary" = yes && cross_compiling=$real_cross_compiling
- if test ${t-0} != 0; then
- AS_TR_SH(ac_cv_sizeof_$1)="${AS_TR_SH(ac_cv_sizeof_$1)+${AS_TR_SH(ac_cv_sizeof_$1)-} }${t}"
- fi
- }
- : ${AS_TR_SH(ac_cv_sizeof_$1)=0}
-])
-{
- unset cond
- for t in ${AS_TR_SH(ac_cv_sizeof_$1)-}; do
- AS_CASE(["$t"],
- [[[0-9]*|SIZEOF_*]], [
- ${cond+echo "@%:@else"}
- echo "[@%:@define ]AS_TR_CPP(SIZEOF_$1) $t"
- break
- ],
- [
- s=`expr $t : ['.*[^0-9]\([0-9][0-9]*\)$']`
- AS_CASE([$t], [*:*], [t="${t%:*}"], [s=`expr $s / $rb_cv_char_bit`])
- echo "@%:@${cond+el}if defined(__${t}__) || defined(__${t}) || defined(_${t}) || defined($t)"
- echo "@%:@define AS_TR_CPP(SIZEOF_$1) $s"
- cond=1
- ])
- done
- ${cond+echo "@%:@endif"}
-} >> confdefs.h
-m4_bmatch([$1], [\.], [], [else
-AC_CHECK_SIZEOF([$1], 0, [$4])
-fi])
-AS_VAR_POPDEF([rbcv_var])dnl
-AS_VAR_POPDEF([cond])dnl
-AS_VAR_POPDEF([t])dnl
-AS_VAR_POPDEF([s])dnl
-])
-
-RUBY_CHECK_SIZEOF(int, [], [ILP])
-RUBY_CHECK_SIZEOF(short)
-RUBY_CHECK_SIZEOF(long, [int], [ILP LP])
-RUBY_CHECK_SIZEOF(long long)
-RUBY_CHECK_SIZEOF(__int64, [8], [ILP LP])
-RUBY_CHECK_SIZEOF(__int128, [16], [ILP LP])
-RUBY_CHECK_SIZEOF(off_t)
-RUBY_CHECK_SIZEOF(void*, [int long "long long"], [ILP LP LLP])
-RUBY_CHECK_SIZEOF(float)
-RUBY_CHECK_SIZEOF(double)
-RUBY_CHECK_SIZEOF(time_t, [long "long long"], [], [@%:@include <time.h>])
-RUBY_CHECK_SIZEOF(clock_t, [], [], [@%:@include <time.h>])
-
-AC_CACHE_CHECK(packed struct attribute, rb_cv_packed_struct,
- [rb_cv_packed_struct=no
- for mac in \
- "__pragma(pack(push, 1)) x __pragma(pack(pop))" \
- "x __attribute__((packed))" \
- ; do
- AC_TRY_COMPILE([@%:@define PACKED_STRUCT(x) $mac
- PACKED_STRUCT(struct { int a; });], [],
- [rb_cv_packed_struct=$mac; break])
- done])
-packed_struct_unaligned=x
-if test "$rb_cv_packed_struct" != no; then
- AC_DEFINE_UNQUOTED([PACKED_STRUCT(x)], [$rb_cv_packed_struct])
-else
- AC_DEFINE_UNQUOTED([PACKED_STRUCT(x)], x)
-fi
-AC_DEFINE_UNQUOTED(PACKED_STRUCT_UNALIGNED(x), $packed_struct_unaligned)
-
-AC_DEFUN([RUBY_CHECK_PRINTF_PREFIX], [
-AC_CACHE_CHECK([for printf prefix for $1], [rb_cv_pri_prefix_]AS_TR_SH($1),[
- [rb_cv_pri_prefix_]AS_TR_SH($1)=[NONE]
- RUBY_WERROR_FLAG(RUBY_APPEND_OPTIONS(CFLAGS, $rb_cv_wsuppress_flags)
- for pri in $2; do
- AC_TRY_COMPILE(
- [@%:@include <stdio.h>
- @%:@include <stddef.h>
- @%:@ifdef __GNUC__
- @%:@define PRINTF_ARGS(decl, string_index, first_to_check) \
- decl __attribute__((format(printf, string_index, first_to_check)))
- @%:@else
- @%:@define PRINTF_ARGS(decl, string_index, first_to_check) decl
- @%:@endif
- PRINTF_ARGS(void test_sprintf(const char*, ...), 1, 2);],
- [printf("%]${pri}[d", (]$1[)42);
- test_sprintf("%]${pri}[d", (]$1[)42);],
- [rb_cv_pri_prefix_]AS_TR_SH($1)[=[$pri]; break])
- done)])
-if test "[$rb_cv_pri_prefix_]AS_TR_SH($1)" != NONE; then
- AC_DEFINE_UNQUOTED([PRI_]m4_ifval($3,$3,AS_TR_CPP(m4_bpatsubst([$1],[_t$])))[_PREFIX],
- "[$rb_cv_pri_prefix_]AS_TR_SH($1)")
-fi
-])
-
-if test "x$ac_cv_type_long_long" = xyes; then
- RUBY_CHECK_PRINTF_PREFIX(long long, ll I64, LL)
-elif test "x$ac_cv_type___int64" = xyes; then
- RUBY_CHECK_PRINTF_PREFIX(__int64, ll I64, LL)
-fi
-
-dnl RUBY_CHECK_SIGNEDNESS [typename] [if-signed] [if-unsigned] [included]
-AC_DEFUN([RUBY_CHECK_SIGNEDNESS], [dnl
- AC_COMPILE_IFELSE([AC_LANG_BOOL_COMPILE_TRY([AC_INCLUDES_DEFAULT([$4])], [($1)-1 > 0])],
- [$3], [$2])])
-
-dnl RUBY_REPLACE_TYPE [typename] [default type] [macro type] [included]
-AC_DEFUN([RUBY_REPLACE_TYPE], [dnl
- AC_CHECK_TYPE([$1],
- [n="patsubst([$1],["],[\\"])"],
- [n="patsubst([$2],["],[\\"])"],
- [$4])
- AC_CACHE_CHECK([for convertible type of [$1]], rb_cv_[$1]_convertible, [
- u= t=
- AS_CASE(["$n "],
- [*" signed "*], [ ],
- [*" unsigned "*], [
- u=U],
- [RUBY_CHECK_SIGNEDNESS($n, [], [u=U], [$4])])
- if test x"$t" = x; then
- for t in "long long" long int short; do
- test -n "$u" && t="unsigned $t"
- AC_COMPILE_IFELSE(
- [AC_LANG_BOOL_COMPILE_TRY([AC_INCLUDES_DEFAULT([$4])]
- [typedef $n rbcv_conftest_target_type;
- typedef $t rbcv_conftest_replace_type;
- extern rbcv_conftest_target_type rbcv_conftest_var;
- extern rbcv_conftest_replace_type rbcv_conftest_var;
- extern rbcv_conftest_target_type rbcv_conftest_func(void);
- extern rbcv_conftest_replace_type rbcv_conftest_func(void);
- ], [sizeof(rbcv_conftest_target_type) == sizeof(rbcv_conftest_replace_type)])],
- [n="$t"; break])
- done
- fi
- AS_CASE([" $n "],
- [*" long long "*], [
- t=LL],
- [*" long "*], [
- t=LONG],
- [
- t=INT])
- rb_cv_[$1]_convertible=${u}${t}])
- if test "${AS_TR_SH(ac_cv_type_[$1])}" = "yes"; then
- n="$1"
- else
- AS_CASE(["${rb_cv_[$1]_convertible}"],
- [*LL], [n="long long"],
- [*LONG], [n="long"],
- [n="int"])
- AS_CASE(["${rb_cv_[$1]_convertible}"],
- [U*], [n="unsigned $n"])
- fi
- AS_CASE("${rb_cv_[$1]_convertible}", [U*], [u=+1], [u=-1])
- AC_DEFINE_UNQUOTED(rb_[$1], $n)
- AC_DEFINE_UNQUOTED([SIGNEDNESS_OF_]AS_TR_CPP($1), $u)
- AC_DEFINE_UNQUOTED([$3]2NUM[(v)], [${rb_cv_[$1]_convertible}2NUM(v)])
- AC_DEFINE_UNQUOTED(NUM2[$3][(v)], [NUM2${rb_cv_[$1]_convertible}(v)])
- AC_DEFINE_UNQUOTED(PRI_[$3]_PREFIX,
- [PRI_`echo ${rb_cv_[$1]_convertible} | sed ['s/^U//']`_PREFIX])
-])
-RUBY_REPLACE_TYPE(pid_t, int, PIDT)
-RUBY_REPLACE_TYPE(uid_t, int, UIDT)
-RUBY_REPLACE_TYPE(gid_t, int, GIDT)
-RUBY_REPLACE_TYPE(time_t, [], TIMET, [@%:@include <time.h>])
-RUBY_REPLACE_TYPE(dev_t, [int long "long long"], DEVT)
-RUBY_REPLACE_TYPE(mode_t, ["unsigned int" long], MODET, [@%:@include <sys/stat.h>])
-RUBY_REPLACE_TYPE(rlim_t, [int long "long long"], RLIM, [
-@%:@ifdef HAVE_SYS_TYPES_H
-@%:@include <sys/types.h>
-@%:@endif
-@%:@ifdef HAVE_SYS_TYPES_H
-@%:@include <sys/time.h>
-@%:@endif
-@%:@include <sys/resource.h>
-])
-RUBY_REPLACE_TYPE(off_t, [], OFFT)
-RUBY_REPLACE_TYPE(clockid_t, [], CLOCKID)
-
-AC_CACHE_CHECK(for prototypes, rb_cv_have_prototypes,
- [AC_TRY_COMPILE([int foo(int x) { return 0; }], [return foo(10);],
- rb_cv_have_prototypes=yes,
- rb_cv_have_prototypes=no)])
-if test "$rb_cv_have_prototypes" = yes; then
- AC_DEFINE(HAVE_PROTOTYPES)
-fi
-
-AC_CACHE_CHECK(token paste string, rb_cv_tokenpaste,
- [AC_TRY_COMPILE([@%:@define paste(a,b) a@%:@@%:@b],
- [int xy = 1; return paste(x,y);],
- rb_cv_tokenpaste=ansi,
- rb_cv_tokenpaste=knr)])
-if test "$rb_cv_tokenpaste" = ansi; then
- AC_DEFINE(TOKEN_PASTE(x,y),[x@%:@@%:@y])
-else
- AC_DEFINE(TOKEN_PASTE(x,y),[x/**/y])
-fi
-
-AC_CACHE_CHECK(stringization, rb_cv_stringization, [
- rb_cv_stringization=no
- for string in "#expr" '"expr"'; do
- AC_COMPILE_IFELSE([
- AC_LANG_BOOL_COMPILE_TRY([
-#define STRINGIZE0(expr) $string
-#define STRINGIZE(expr) STRINGIZE0(expr)
-#undef real_test_for_stringization
-#define test_for_stringization -.real_test_for_stringization.-
-const char stringized[[]] = STRINGIZE(test_for_stringization);
-], [sizeof(stringized) == 32])],
- [rb_cv_stringization="$string"; break],
- [rb_cv_stringization=no])
- done]
-)
-AC_DEFINE(STRINGIZE(expr),STRINGIZE0(expr))
-if test x"$rb_cv_stringization" != xno -a "$rb_cv_stringization" != "#expr"; then
- AC_DEFINE_UNQUOTED(STRINGIZE0(expr),$rb_cv_stringization)
- AC_DEFINE(OLD_FASHIONED_STRINGIZATION,1)
-fi
-
-AC_CACHE_CHECK([string literal concatenation],
- rb_cv_string_literal_concatenation, [
- AC_COMPILE_IFELSE([
- AC_LANG_BOOL_COMPILE_TRY([
-const char concatenated_literal[[]] = "literals" "to"
- "be" "concatenated.";
-], [sizeof(concatenated_literal) == 26])],
- [rb_cv_string_literal_concatenation=yes],
- [rb_cv_string_literal_concatenation=no])]
-)
-if test "$rb_cv_string_literal_concatenation" = no; then
- AC_DEFINE(NO_STRING_LITERAL_CONCATENATION,1)
-fi
-
-AC_CACHE_CHECK(for variable length prototypes and stdarg.h, rb_cv_stdarg,
- [AC_TRY_COMPILE([
-#include <stdarg.h>
-int foo(int x, ...) {
- va_list va;
- va_start(va, x);
- va_arg(va, int);
- va_arg(va, char *);
- va_arg(va, double);
- return 0;
-}
-], [return foo(10, "", 3.14);],
- rb_cv_stdarg=yes,
- rb_cv_stdarg=no)])
-if test "$rb_cv_stdarg" = yes; then
- AC_DEFINE(HAVE_STDARG_PROTOTYPES)
-fi
-
-AC_CACHE_CHECK(for variable length macro, rb_cv_va_args_macro,
- [AC_TRY_COMPILE([
-int foo(int x, ...);
-@%:@define FOO(a, ...) foo(a, @%:@@%:@__VA_ARGS__)
-], [FOO(1);FOO(1,2);FOO(1,2,3);],
- rb_cv_va_args_macro=yes,
- rb_cv_va_args_macro=no)])
-if test "$rb_cv_va_args_macro" = yes; then
- AC_DEFINE(HAVE_VA_ARGS_MACRO)
-fi
-
-AC_DEFUN([RUBY_DEFINE_IF], [dnl
- m4_ifval([$1], [AS_LITERAL_IF([$1], [], [test "X$1" = X || ])cat <<EOH >> confdefs.h
-@%:@if $1
-EOH
-])dnl
-AC_DEFINE_UNQUOTED($2, $3)dnl
- m4_ifval([$1], [AS_LITERAL_IF([$1], [], [test "X$1" = X || ])cat <<EOH >> confdefs.h
-@%:@endif /* $1 */
-EOH
-])dnl
-])dnl
-
-dnl RUBY_DECL_ATTRIBUTE(attrib, macroname, cachevar, condition, type, code)
-AC_DEFUN([RUBY_DECL_ATTRIBUTE], [dnl
-m4_ifval([$2], dnl
- [AS_VAR_PUSHDEF([attrib], m4_bpatsubst([$2], [(.*)], []))], dnl
- [AS_VAR_PUSHDEF([attrib], m4_toupper(m4_format(%.4s, [$5]))[_]AS_TR_CPP($1))] dnl
-)dnl
-m4_ifval([$3], dnl
- [AS_VAR_PUSHDEF([rbcv],[$3])], dnl
- [AS_VAR_PUSHDEF([rbcv],[rb_cv_]m4_format(%.4s, [$5])[_][$1])]dnl
-)dnl
-m4_pushdef([attrib_code],[m4_bpatsubst([$1],["],[\\"])])dnl
-m4_pushdef([attrib_params],[m4_bpatsubst([$2(x)],[^[^()]*(\([^()]*\)).*],[\1])])dnl
-m4_ifval([$4], [rbcv_cond=["$4"]; test "$rbcv_cond" || unset rbcv_cond])
-AC_CACHE_CHECK(for m4_ifval([$2],[m4_bpatsubst([$2], [(.*)], [])],[$1]) [$5] attribute, rbcv, dnl
-[rbcv=x
-RUBY_WERROR_FLAG([
-for mac in \
- "__attribute__ ((attrib_code)) x" \
- "x __attribute__ ((attrib_code))" \
- "__declspec(attrib_code) x" \
- x; do
- m4_ifval([$4],mac="$mac"${rbcv_cond+" /* only if $rbcv_cond */"})
- AC_TRY_COMPILE(
- m4_ifval([$4],${rbcv_cond+[@%:@if ]$rbcv_cond})
-[@%:@define ]attrib[](attrib_params)[ $mac]
-m4_ifval([$4],${rbcv_cond+[@%:@else]}
-${rbcv_cond+[@%:@define ]attrib[](attrib_params)[ x]}
-${rbcv_cond+[@%:@endif]})
-$6
-@%:@define mesg ("")
- attrib[](attrib_params)[;], [],
- [rbcv="$mac"; break])
-done
-])])
-if test "$rbcv" != x; then
- RUBY_DEFINE_IF(m4_ifval([$4],[${rbcv_cond}]), attrib[](attrib_params)[], $rbcv)
-fi
-m4_ifval([$4], [unset rbcv_cond]) dnl
-m4_popdef([attrib_params])dnl
-m4_popdef([attrib_code])dnl
-AS_VAR_POPDEF([attrib])dnl
-AS_VAR_POPDEF([rbcv])dnl
-])
-
-dnl RUBY_FUNC_ATTRIBUTE(attrib, macroname, cachevar, condition)
-AC_DEFUN([RUBY_FUNC_ATTRIBUTE], [dnl
- RUBY_DECL_ATTRIBUTE([$1], [$2], [$3], [$4],
- [function], [@%:@define x int conftest_attribute_check(void)]
- )
-])
-
-dnl RUBY_TYPE_ATTRIBUTE(attrib, macroname, cachevar, condition)
-AC_DEFUN([RUBY_TYPE_ATTRIBUTE], [dnl
- RUBY_DECL_ATTRIBUTE([$1], [$2], [$3], [$4],
- [type], [
-@%:@define x struct conftest_attribute_check {int i;}
-])
-])
-
-RUBY_FUNC_ATTRIBUTE(__const__, CONSTFUNC)
-RUBY_FUNC_ATTRIBUTE(__pure__, PUREFUNC)
-RUBY_FUNC_ATTRIBUTE(__noreturn__, NORETURN)
-RUBY_FUNC_ATTRIBUTE(__deprecated__, DEPRECATED)
-RUBY_FUNC_ATTRIBUTE(__deprecated__("by "@%:@n), DEPRECATED_BY(n,x), rb_cv_func_deprecated_by)
-RUBY_TYPE_ATTRIBUTE(__deprecated__ mesg, DEPRECATED_TYPE(mesg,x), rb_cv_type_deprecated)
-RUBY_FUNC_ATTRIBUTE(__noinline__, NOINLINE)
-RUBY_FUNC_ATTRIBUTE(__always_inline__, ALWAYS_INLINE)
-RUBY_FUNC_ATTRIBUTE(__warn_unused_result__, WARN_UNUSED_RESULT)
-RUBY_FUNC_ATTRIBUTE(__unused__, MAYBE_UNUSED)
-RUBY_FUNC_ATTRIBUTE(__error__ mesg, ERRORFUNC(mesg,x), rb_cv_func___error__)
-RUBY_FUNC_ATTRIBUTE(__warning__ mesg, WARNINGFUNC(mesg,x), rb_cv_func___warning__)
-RUBY_FUNC_ATTRIBUTE(__weak__, WEAK, rb_cv_func_weak)
-if test "$rb_cv_func_weak" != x; then
- AC_DEFINE(HAVE_FUNC_WEAK)
-fi
-
-if_i386=${universal_binary+[defined __i386__]}
-RUBY_FUNC_ATTRIBUTE(__stdcall__, FUNC_STDCALL, rb_cv_func_stdcall, ${if_i386})
-RUBY_FUNC_ATTRIBUTE(__cdecl__, FUNC_CDECL, rb_cv_func_cdecl, ${if_i386})
-RUBY_FUNC_ATTRIBUTE(__fastcall__, FUNC_FASTCALL, rb_cv_func_fastcall, ${if_i386})
-RUBY_FUNC_ATTRIBUTE(__optimize__("O0"), FUNC_UNOPTIMIZED, rb_cv_func_unoptimized)
-RUBY_FUNC_ATTRIBUTE(__optimize__("-Os","-fomit-frame-pointer"), FUNC_MINIMIZED, rb_cv_func_minimized)
-
-if test "$GCC" = yes; then
- AC_CACHE_CHECK([for function alias], [rb_cv_gcc_function_alias],
- [rb_cv_gcc_function_alias=no
- for a in alias weak,alias; do
- AC_TRY_LINK([void foo(void) {}
- void bar(void) __attribute__(($a("foo")));], [bar()],
- [rb_cv_gcc_function_alias=$a; break])
- done])
- if test "$rb_cv_gcc_function_alias" != no; then
- AC_DEFINE(HAVE_ATTRIBUTE_FUNCTION_ALIAS)
- AC_DEFINE_UNQUOTED([RUBY_ALIAS_FUNCTION_TYPE(type, prot, name, args)],
- [type prot __attribute__(($rb_cv_gcc_function_alias(@%:@name)));])
- AC_DEFINE_UNQUOTED([RUBY_ALIAS_FUNCTION_VOID(prot, name, args)],
- [RUBY_ALIAS_FUNCTION_TYPE(void, prot, name, args)])
- fi
-
- AC_CACHE_CHECK([for __atomic builtins], [rb_cv_gcc_atomic_builtins], [
- AC_TRY_LINK([unsigned char atomic_var;],
- [
- __atomic_exchange_n(&atomic_var, 0, __ATOMIC_SEQ_CST);
- __atomic_exchange_n(&atomic_var, 1, __ATOMIC_SEQ_CST);
- __atomic_fetch_add(&atomic_var, 1, __ATOMIC_SEQ_CST);
- __atomic_fetch_sub(&atomic_var, 1, __ATOMIC_SEQ_CST);
- __atomic_or_fetch(&atomic_var, 1, __ATOMIC_SEQ_CST);
- ],
- [rb_cv_gcc_atomic_builtins=yes],
- [rb_cv_gcc_atomic_builtins=no])])
- if test "$rb_cv_gcc_atomic_builtins" = yes; then
- AC_DEFINE(HAVE_GCC_ATOMIC_BUILTINS)
- fi
-
- AC_CACHE_CHECK([for __sync builtins], [rb_cv_gcc_sync_builtins], [
- AC_TRY_LINK([unsigned char atomic_var;],
- [
- __sync_lock_test_and_set(&atomic_var, 0);
- __sync_lock_test_and_set(&atomic_var, 1);
- __sync_fetch_and_add(&atomic_var, 1);
- __sync_fetch_and_sub(&atomic_var, 1);
- __sync_or_and_fetch(&atomic_var, 1);
- __sync_val_compare_and_swap(&atomic_var, 0, 1);
- ],
- [rb_cv_gcc_sync_builtins=yes],
- [rb_cv_gcc_sync_builtins=no])])
- if test "$rb_cv_gcc_sync_builtins" = yes; then
- AC_DEFINE(HAVE_GCC_SYNC_BUILTINS)
- fi
-
- AC_CACHE_CHECK(for __builtin_unreachable, rb_cv_func___builtin_unreachable,
- [RUBY_WERROR_FLAG(
- [AC_TRY_LINK([volatile int zero;],
- [if (zero) __builtin_unreachable();],
- [rb_cv_func___builtin_unreachable=yes],
- [rb_cv_func___builtin_unreachable=no])
- ])
- ])
- if test "$rb_cv_func___builtin_unreachable" = yes; then
- AC_DEFINE_UNQUOTED(UNREACHABLE, [__builtin_unreachable()])
- fi
-fi
-
-AC_CACHE_CHECK(for exported function attribute, rb_cv_func_exported, [
-rb_cv_func_exported=no
-RUBY_WERROR_FLAG([
-for mac in '__attribute__ ((__visibility__("default")))' '__declspec(dllexport)'; do
- AC_TRY_COMPILE([@%:@define RUBY_FUNC_EXPORTED $mac extern
- RUBY_FUNC_EXPORTED void conftest_attribute_check(void);], [],
- [rb_cv_func_exported="$mac"; break])
-done
-])])
-if test "$rb_cv_func_exported" != no; then
- AC_DEFINE_UNQUOTED(RUBY_FUNC_EXPORTED, [$rb_cv_func_exported extern])
-fi
-
-RUBY_APPEND_OPTION(XCFLAGS, -DRUBY_EXPORT)
-
-AC_CACHE_CHECK(for function name string predefined identifier,
- rb_cv_function_name_string,
- [rb_cv_function_name_string=no
- RUBY_WERROR_FLAG([
- for func in __func__ __FUNCTION__; do
- AC_TRY_LINK([@%:@include <stdio.h>],
- [puts($func);],
- [rb_cv_function_name_string=$func
- break])
- done
- ])]
-)
-if test "$rb_cv_function_name_string" != no; then
- AC_DEFINE_UNQUOTED(RUBY_FUNCTION_NAME_STRING, [$rb_cv_function_name_string])
-fi
-
-AC_CACHE_CHECK(if enum over int is allowed, rb_cv_enum_over_int, [
- rb_cv_enum_over_int=no
- if test "x$ac_cv_type_long_long" = xyes; then
- type="unsigned long long" max="ULLONG_MAX"
- else
- type="unsigned long" max="ULONG_MAX"
- fi
- RUBY_WERROR_FLAG([
- AC_COMPILE_IFELSE([
- AC_LANG_BOOL_COMPILE_TRY([
- @%:@include <limits.h>
- enum {conftest_max = $max};
- ], [
- (conftest_max == $max) &&
- (sizeof(conftest_max) == sizeof($type))
- ]
- )],
- [rb_cv_enum_over_int=yes],
- [rb_cv_enum_over_int=no]
- )
- ])
-])
-if test $rb_cv_enum_over_int = yes; then
- AC_DEFINE(ENUM_OVER_INT, 1)
-fi
-
-dnl Check whether we need to define sys_nerr locally
-AC_CHECK_DECLS([sys_nerr], [], [], [$ac_includes_default
-@%:@include <errno.h>])
-
-AC_CHECK_DECLS([getenv])
-
-AS_CASE(["$target_cpu"],
-[alpha*|sh4|sh4el|sh4eb], [AS_CASE(["$target_os"::"$GCC"],
- [*::yes], # gcc
- [CFLAGS="-mieee $CFLAGS"],
- [osf*], # ccc
- [CFLAGS="-ieee $CFLAGS"],
- )],
-[sparc*], [AC_LIBOBJ([sparc])])
-
-ac_cv_header_net_socket_h=${ac_cv_header_net_socket_h=no}
-if test "$ac_cv_header_net_socket_h" = yes; then
- ac_cv_header_sys_socket_h=${ac_cv_header_sys_socket_h=no}
-else
- ac_cv_header_sys_socket_h=${ac_cv_header_sys_socket_h=yes}
-fi
-
-
-AC_TYPE_SIZE_T
-RUBY_CHECK_SIGNEDNESS(size_t, [AC_MSG_ERROR(size_t is signed)], [],
- [@%:@include <sys/types.h>])
-RUBY_CHECK_SIZEOF(size_t, [int long void*], [], [@%:@include <sys/types.h>])
-RUBY_CHECK_SIZEOF(ptrdiff_t, size_t, [], [@%:@include <stddef.h>])
-RUBY_CHECK_PRINTF_PREFIX(size_t, z)
-RUBY_CHECK_PRINTF_PREFIX(ptrdiff_t, t)
-AC_STRUCT_ST_BLKSIZE
-AC_STRUCT_ST_BLOCKS
-AC_STRUCT_ST_RDEV
-RUBY_CHECK_SIZEOF([struct stat.st_size], [off_t int long "long long"], [], [@%:@include <sys/stat.h>])
-if test "$ac_cv_member_struct_stat_st_blocks" = yes; then
- RUBY_CHECK_SIZEOF([struct stat.st_blocks], [off_t int long "long long"], [], [@%:@include <sys/stat.h>])
-fi
-RUBY_CHECK_SIZEOF([struct stat.st_ino], [long "long long"], [], [@%:@include <sys/stat.h>])
-AC_CHECK_MEMBERS([struct stat.st_atim])
-AC_CHECK_MEMBERS([struct stat.st_atimespec])
-AC_CHECK_MEMBERS([struct stat.st_atimensec])
-AC_CHECK_MEMBERS([struct stat.st_mtim])
-AC_CHECK_MEMBERS([struct stat.st_mtimespec])
-AC_CHECK_MEMBERS([struct stat.st_mtimensec])
-AC_CHECK_MEMBERS([struct stat.st_ctim])
-AC_CHECK_MEMBERS([struct stat.st_ctimespec])
-AC_CHECK_MEMBERS([struct stat.st_ctimensec])
-AC_CHECK_MEMBERS([struct stat.st_birthtimespec])
-
-AC_CHECK_TYPES([struct timeval], [], [], [@%:@ifdef HAVE_TIME_H
-@%:@include <time.h>
-@%:@endif
-@%:@ifdef HAVE_SYS_TIME_H
-@%:@include <sys/time.h>
-@%:@endif])
-
-if test "${ac_cv_type_struct_timeval}" = yes; then
- RUBY_CHECK_SIZEOF([struct timeval.tv_sec], [time_t long "long long"], [],
- [@%:@ifdef HAVE_TIME_H
-@%:@include <time.h>
-@%:@endif
-@%:@ifdef HAVE_SYS_TIME_H
-@%:@include <sys/time.h>
-@%:@endif])
- AS_CASE(${ac_cv_sizeof_struct_timeval_tv_sec},
- [SIZEOF_INT], [t=int],
- [SIZEOF_LONG], [t=long],
- [SIZEOF_LONG_LONG], [t=LONG_LONG],
- [t=])
- if test "${t}" != ""; then
- AC_DEFINE_UNQUOTED(TYPEOF_TIMEVAL_TV_SEC, [$t])
- fi
-fi
-
-AC_CHECK_TYPES([struct timespec], [], [], [@%:@ifdef HAVE_TIME_H
-@%:@include <time.h>
-@%:@endif
-@%:@ifdef HAVE_SYS_TIME_H
-@%:@include <sys/time.h>
-@%:@endif])
-
-AC_CHECK_TYPES([struct timezone], [], [], [@%:@ifdef HAVE_TIME_H
-@%:@ include <time.h>
-@%:@endif
-@%:@ifdef HAVE_SYS_TIME_H
-@%:@ include <sys/time.h>
-@%:@endif])
-
-AC_CHECK_TYPES([clockid_t], [], [], [@%:@ifdef HAVE_TIME_H
-@%:@ include <time.h>
-@%:@endif
-@%:@ifdef HAVE_SYS_TIME_H
-@%:@ include <sys/time.h>
-@%:@endif])
-
-AC_CACHE_VAL([rb_cv_large_fd_select],
- [AC_CHECK_TYPE(fd_mask, [rb_cv_large_fd_select=yes], [rb_cv_large_fd_select=no])])
-if test "$rb_cv_large_fd_select" = yes; then
- AC_DEFINE(HAVE_RB_FD_INIT, 1)
-fi
-
-dnl RUBY_DEFINT TYPENAME, SIZE, [UNSIGNED], [INCLUDES = DEFAULT-INCLUDES]
-AC_DEFUN([RUBY_DEFINT], [dnl
-AS_VAR_PUSHDEF([cond], [rb_defint_cond])dnl
-AS_VAR_PUSHDEF([type], [rb_defint_type])dnl
-AC_CACHE_CHECK([for $1], [rb_cv_type_$1],
-[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT([$4])
-typedef $1 t; int s = sizeof(t) == 42;])],
- [rb_cv_type_$1=yes],
- [AS_CASE([m4_bmatch([$2], [^[1-9][0-9]*$], $2, [$ac_cv_sizeof_]AS_TR_SH($2))],
- ["1"], [ rb_cv_type_$1="m4_if([$3], [], [signed ], [$3 ])char"],
- ["$ac_cv_sizeof_short"], [ rb_cv_type_$1="m4_if([$3], [], [], [$3 ])short"],
- ["$ac_cv_sizeof_int"], [ rb_cv_type_$1="m4_if([$3], [], [], [$3 ])int"],
- ["$ac_cv_sizeof_long"], [ rb_cv_type_$1="m4_if([$3], [], [], [$3 ])long"],
- ["$ac_cv_sizeof_long_long"], [ rb_cv_type_$1="m4_if([$3], [], [], [$3 ])long long"],
- ["${ac_cv_sizeof___int64@%:@*:}"], [ rb_cv_type_$1="m4_if([$3], [], [], [$3 ])__int64"],
- ["${ac_cv_sizeof___int128@%:@*:}"], [ rb_cv_type_$1="m4_if([$3], [], [], [$3 ])__int128"],
- [ rb_cv_type_$1=no])])])
-if test "${rb_cv_type_$1}" != no; then
- type="${rb_cv_type_$1@%:@@%:@unsigned }"
- AS_IF([test "$type" != yes && eval 'test -n "${ac_cv_sizeof_'$type'+set}"'], [
- eval cond='"${ac_cv_sizeof_'$type'}"'
- AS_CASE([$cond], [*:*], [
- cond=AS_TR_CPP($type)
- echo "@%:@if defined SIZEOF_"$cond" && SIZEOF_"$cond" > 0" >> confdefs.h
- ], [cond=])
- ], [cond=])
- AC_DEFINE([HAVE_]AS_TR_CPP($1), 1)
- if test "${rb_cv_type_$1}" = yes; then
- m4_bmatch([$2], [^[1-9][0-9]*$], [AC_CHECK_SIZEOF([$1], 0, [AC_INCLUDES_DEFAULT([$4])])],
- [RUBY_CHECK_SIZEOF([$1], [$2], [], [AC_INCLUDES_DEFAULT([$4])])])
- else
- AC_DEFINE_UNQUOTED($1, [$rb_cv_type_$1])
- AC_DEFINE_UNQUOTED([SIZEOF_]AS_TR_CPP($1), [SIZEOF_]AS_TR_CPP([$type]))
- fi
- test -n "$cond" && echo "@%:@endif /* $cond */" >> confdefs.h
-fi
-AS_VAR_POPDEF([cond])dnl
-AS_VAR_POPDEF([type])dnl
-])
-
-RUBY_DEFINT(int8_t, 1)
-RUBY_DEFINT(uint8_t, 1, unsigned)
-RUBY_DEFINT(int16_t, 2)
-RUBY_DEFINT(uint16_t, 2, unsigned)
-RUBY_DEFINT(int32_t, 4)
-RUBY_DEFINT(uint32_t, 4, unsigned)
-RUBY_DEFINT(int64_t, 8)
-RUBY_DEFINT(uint64_t, 8, unsigned)
-RUBY_DEFINT(int128_t, 16)
-RUBY_DEFINT(uint128_t, 16, unsigned)
-RUBY_DEFINT(intptr_t, void*)
-RUBY_DEFINT(uintptr_t, void*, unsigned)
-RUBY_DEFINT(ssize_t, size_t, [], [@%:@include <sys/types.h>]) dnl may differ from int, so not use AC_TYPE_SSIZE_T.
-
-RUBY_NACL_CHECK_PEPPER_TYPES
-
-AC_CACHE_CHECK(for stack end address, rb_cv_stack_end_address,
-[rb_cv_stack_end_address=no
- AC_TRY_LINK(
- [extern void *__libc_stack_end;],
- [if (!__libc_stack_end) return 1;],
- [rb_cv_stack_end_address="__libc_stack_end"])
-])
-if test $rb_cv_stack_end_address != no; then
- AC_DEFINE_UNQUOTED(STACK_END_ADDRESS, $rb_cv_stack_end_address)
-fi
-
-# posix_memalign(memptr, alignment, size) implemented for OpenBSD 4.8 doesn't work if alignment > MALLOC_PAGESIZE.
-# [ruby-core:42158] https://bugs.ruby-lang.org/issues/5901
-# OpenBSD 5.2 fixed the problem. (src/lib/libc/stdlib/malloc.c:1.142)
-# MirOS #10semel has the problem but fixed in the repository. (src/lib/libc/stdlib/malloc.c:1.9)
-AS_CASE(["$target_os"],
-[openbsd*|mirbsd*], [
- AC_CACHE_CHECK(for heap align log on openbsd, rb_cv_page_size_log,
- [rb_cv_page_size_log=no
- for page_log in 12 13; do
- AC_TRY_RUN([
-#include <math.h>
-#include <unistd.h>
-
-int
-main() {
- if ((int)log2((double)sysconf(_SC_PAGESIZE)) != $page_log) return 1;
- return 0;
-}
- ],
- rb_cv_page_size_log="$page_log"; break)
- done])
- if test $rb_cv_page_size_log != no; then
- AC_DEFINE_UNQUOTED(HEAP_ALIGN_LOG, $rb_cv_page_size_log)
- else
- AC_DEFINE_UNQUOTED(HEAP_ALIGN_LOG, 12)
- fi
-])
-
-dnl Checks for library functions.
-AC_TYPE_GETGROUPS
-AC_TYPE_SIGNAL
-AS_CASE(["${target_cpu}-${target_os}:${target_archs}"],
-[powerpc-darwin*], [
- AC_LIBSOURCES(alloca.c)
- AC_SUBST([ALLOCA], [\${LIBOBJDIR}alloca.${ac_objext}])
- AC_DEFINE(C_ALLOCA)
- AC_DEFINE_UNQUOTED(alloca, alloca)
- ],
-[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)
- ],
-[
- AC_FUNC_ALLOCA
- ])
-if test "x$ALLOCA" = "x"; then
- AC_CACHE_CHECK([for dynamic size alloca], rb_cv_dynamic_alloca, [
- for chk in ok __chkstk; do
- AC_TRY_LINK([
- @%:@ifdef HAVE_ALLOCA_H
- @%:@include <alloca.h>
- @%:@endif
- void $chk() {}
- int dynamic_alloca_test;
- int dynamic_alloca_result;],
- [dynamic_alloca_result = alloca(dynamic_alloca_test) != 0;],
- [rb_cv_dynamic_alloca=$chk; break])
- done])
- if test "x$rb_cv_dynamic_alloca" = "x__chkstk"; then
- AC_DEFINE_UNQUOTED(RUBY_ALLOCA_CHKSTK, _$rb_cv_dynamic_alloca)
- AS_CASE("$target_cpu",
- [x64|x86_64], [
- AC_SUBST([ALLOCA], [\${LIBOBJDIR}x86_64-chkstk.${ac_objext}])
- ],)
- fi
-fi
-AC_FUNC_MEMCMP
-
-# http://sources.redhat.com/ml/libc-hacker/2005-08/msg00008.html
-# Debian GNU/Linux Etch's libc6.1 2.3.6.ds1-13etch5 has this problem.
-# Debian GNU/Linux Lenny's libc6.1 2.7-10 has no problem.
-AC_CACHE_CHECK(for broken erfc of glibc-2.3.6 on IA64, rb_cv_broken_glibc_ia64_erfc,
- [AC_TRY_RUN([
-#include <math.h>
-int
-main()
-{
- erfc(10000.0);
- return 0;
-}
-],
- rb_cv_broken_glibc_ia64_erfc=no,
- rb_cv_broken_glibc_ia64_erfc=yes,
- rb_cv_broken_glibc_ia64_erfc=no)])
-AS_CASE([$rb_cv_broken_glibc_ia64_erfc],[yes],[ac_cv_func_erf=no])
-
-AS_CASE(["$target_os"],[freebsd*],[
- AC_DEFINE(BROKEN_CLOSE)
- AC_REPLACE_FUNCS(close)
- ])
-
-AC_REPLACE_FUNCS(acosh)
-AC_REPLACE_FUNCS(cbrt)
-AC_REPLACE_FUNCS(crypt)
-AC_REPLACE_FUNCS(dup2)
-AC_REPLACE_FUNCS(erf)
-AC_REPLACE_FUNCS(explicit_bzero)
-AC_REPLACE_FUNCS(ffs)
-AC_REPLACE_FUNCS(finite)
-AC_REPLACE_FUNCS(flock)
-AC_REPLACE_FUNCS(hypot)
-AC_REPLACE_FUNCS(isinf)
-AC_REPLACE_FUNCS(isnan)
-AC_REPLACE_FUNCS(lgamma_r)
-AC_REPLACE_FUNCS(memmove)
-AC_REPLACE_FUNCS(nextafter)
-AC_REPLACE_FUNCS(setproctitle)
-AC_REPLACE_FUNCS(strchr)
-AC_REPLACE_FUNCS(strerror)
-AC_REPLACE_FUNCS(strlcat)
-AC_REPLACE_FUNCS(strlcpy)
-AC_REPLACE_FUNCS(strstr)
-AC_REPLACE_FUNCS(tgamma)
-
-# for missing/setproctitle.c
-AS_CASE(["$target_os"],
-[aix* | k*bsd*-gnu | kopensolaris*-gnu | linux* | darwin*], [AC_DEFINE(SPT_TYPE,SPT_REUSEARGV)],
-[hpux*], [AC_DEFINE(SPT_TYPE,SPT_PSTAT) ],
-[])
-AC_CHECK_HEADERS(sys/pstat.h)
-
-
-AC_CACHE_CHECK(for signbit, rb_cv_have_signbit,
- [AC_TRY_LINK([
-#include <math.h>
-], [int v = signbit(-0.0);],
- rb_cv_have_signbit=yes,
- rb_cv_have_signbit=no)])
-if test "$rb_cv_have_signbit" = yes; then
- AC_DEFINE(HAVE_SIGNBIT)
-else
- AC_LIBOBJ([signbit])
-fi
-
-AC_CACHE_CHECK(for broken memmem, rb_cv_broken_memmem, [
- AC_TRY_RUN([
-@%:@include <string.h>
-
-int
-main(int argc, char **argv)
-{
- const char *str = "hogefugafoobar";
- const char *rs = "foo";
- const char *empty = "";
- char *p;
-
- p = memmem(str, strlen(str), rs, strlen(rs));
- if (p == str+8) {
- p = memmem(str, strlen(str), empty, strlen(empty));
- if (p == str)
- return 0;
- }
- return 1;
-}
- ],
- rb_cv_broken_memmem=no,
- rb_cv_broken_memmem=yes,
- rb_cv_broken_memmem=yes)
-])
-test x"$rb_cv_broken_memmem" = xyes && ac_cv_func_memmem=no
-
-AC_FUNC_FORK
-
-AC_CHECK_FUNCS(__syscall)
-AC_CHECK_FUNCS(_longjmp) # used for AC_ARG_WITH(setjmp-type)
-# we don't use _setjmp if _longjmp doesn't exist.
-test x$ac_cv_func__longjmp = xno && ac_cv_func__setjmp=no
-AC_CHECK_FUNCS(arc4random_buf)
-AC_CHECK_FUNCS(atan2l atan2f)
-AC_CHECK_FUNCS(chroot)
-AC_CHECK_FUNCS(chsize)
-AC_CHECK_FUNCS(clock_gettime)
-AC_CHECK_FUNCS(cosh)
-AC_CHECK_FUNCS(crypt_r)
-AC_CHECK_FUNCS(daemon)
-AC_CHECK_FUNCS(dirfd)
-AC_CHECK_FUNCS(dl_iterate_phdr)
-AC_CHECK_FUNCS(dlopen)
-AC_CHECK_FUNCS(dladdr)
-AC_CHECK_FUNCS(dup)
-AC_CHECK_FUNCS(dup3)
-AC_CHECK_FUNCS(eaccess)
-AC_CHECK_FUNCS(endgrent)
-AC_CHECK_FUNCS(fchmod)
-AC_CHECK_FUNCS(fchown)
-AC_CHECK_FUNCS(fcntl)
-AC_CHECK_FUNCS(fdatasync)
-AC_CHECK_FUNCS(fgetattrlist)
-AC_CHECK_FUNCS(fmod)
-AC_CHECK_FUNCS(fsync)
-AC_CHECK_FUNCS(ftruncate)
-AC_CHECK_FUNCS(ftruncate64) # used for Win32 platform
-AC_CHECK_FUNCS(getattrlist)
-AC_CHECK_FUNCS(getcwd)
-AC_CHECK_FUNCS(getgidx)
-AC_CHECK_FUNCS(getgrnam)
-AC_CHECK_FUNCS(getgrnam_r)
-AC_CHECK_FUNCS(getgroups)
-AC_CHECK_FUNCS(getpgid)
-AC_CHECK_FUNCS(getpgrp)
-AC_CHECK_FUNCS(getpriority)
-AC_CHECK_FUNCS(getpwnam_r)
-AC_CHECK_FUNCS(getresgid)
-AC_CHECK_FUNCS(getresuid)
-AC_CHECK_FUNCS(getrlimit)
-AC_CHECK_FUNCS(getsid)
-AC_CHECK_FUNCS(gettimeofday) # for making ac_cv_func_gettimeofday
-AC_CHECK_FUNCS(getuidx)
-AC_CHECK_FUNCS(gmtime_r)
-AC_CHECK_FUNCS(initgroups)
-AC_CHECK_FUNCS(ioctl)
-AC_CHECK_FUNCS(isfinite)
-AC_CHECK_FUNCS(issetugid)
-AC_CHECK_FUNCS(killpg)
-AC_CHECK_FUNCS(lchmod)
-AC_CHECK_FUNCS(lchown)
-AC_CHECK_FUNCS(link)
-AC_CHECK_FUNCS(llabs)
-AC_CHECK_FUNCS(lockf)
-AC_CHECK_FUNCS(log2)
-AC_CHECK_FUNCS(lstat)
-AC_CHECK_FUNCS(malloc_usable_size)
-AC_CHECK_FUNCS(malloc_size)
-AC_CHECK_FUNCS(mblen)
-AC_CHECK_FUNCS(memalign)
-AC_CHECK_FUNCS(memset_s)
-AC_CHECK_FUNCS(writev)
-AC_CHECK_FUNCS(memrchr)
-AC_CHECK_FUNCS(memmem)
-AC_CHECK_FUNCS(mkfifo)
-AC_CHECK_FUNCS(mknod)
-AC_CHECK_FUNCS(mktime)
-AC_CHECK_FUNCS(pipe2)
-AC_CHECK_FUNCS(poll)
-AC_CHECK_FUNCS(posix_fadvise)
-AC_CHECK_FUNCS(posix_memalign)
-AC_CHECK_FUNCS(ppoll)
-AC_CHECK_FUNCS(pread)
-AC_CHECK_FUNCS(qsort_r)
-AC_CHECK_FUNCS(qsort_s)
-AC_CHECK_FUNCS(readlink)
-AC_CHECK_FUNCS(round)
-AC_CHECK_FUNCS(sched_getaffinity)
-AC_CHECK_FUNCS(seekdir)
-AC_CHECK_FUNCS(select_large_fdset)
-AC_CHECK_FUNCS(sendfile)
-AC_CHECK_FUNCS(setegid)
-AC_CHECK_FUNCS(setenv)
-AC_CHECK_FUNCS(seteuid)
-AC_CHECK_FUNCS(setgid)
-AC_CHECK_FUNCS(setgroups)
-AC_CHECK_FUNCS(setpgid)
-AC_CHECK_FUNCS(setpgrp)
-AC_CHECK_FUNCS(setregid)
-AC_CHECK_FUNCS(setresgid)
-AC_CHECK_FUNCS(setresuid)
-AC_CHECK_FUNCS(setreuid)
-AC_CHECK_FUNCS(setrgid)
-AC_CHECK_FUNCS(setrlimit)
-AC_CHECK_FUNCS(setruid)
-AC_CHECK_FUNCS(setsid)
-AC_CHECK_FUNCS(setuid)
-AC_CHECK_FUNCS(shutdown)
-AC_CHECK_FUNCS(sigaction)
-AC_CHECK_FUNCS(sigaltstack)
-AC_CHECK_FUNCS(sigprocmask)
-AC_CHECK_FUNCS(sinh)
-AC_CHECK_FUNCS(spawnv)
-AC_CHECK_FUNCS(symlink)
-AC_CHECK_FUNCS(syscall)
-AC_CHECK_FUNCS(sysconf)
-AC_CHECK_FUNCS(tanh)
-AC_CHECK_FUNCS(telldir)
-AC_CHECK_FUNCS(timegm)
-AC_CHECK_FUNCS(times)
-AC_CHECK_FUNCS(truncate)
-AC_CHECK_FUNCS(truncate64) # used for Win32
-AC_CHECK_FUNCS(unsetenv)
-AC_CHECK_FUNCS(utimensat)
-AC_CHECK_FUNCS(utimes)
-AC_CHECK_FUNCS(wait4)
-AC_CHECK_FUNCS(waitpid)
-
-AS_IF([test "$ac_cv_func_memset_s" = yes],
- [RUBY_DEFINE_IF([!defined __STDC_WANT_LIB_EXT1__], [__STDC_WANT_LIB_EXT1__], 1)])
-
-AS_IF([test "$ac_cv_func_getcwd" = yes], [
- AC_CACHE_CHECK(if getcwd allocates buffer if NULL is given, [rb_cv_getcwd_malloc],
- [AC_TRY_RUN([
-@%:@include <stddef.h>
-@%:@include <stdio.h>
-@%:@ifdef HAVE_UNISTD_H
-@%:@include <unistd.h>
-@%:@endif
-@%:@ifndef EXIT_SUCCESS
-@%:@define EXIT_SUCCESS 0
-@%:@endif
-@%:@ifndef EXIT_FAILURE
-@%:@define EXIT_FAILURE 1
-@%:@endif
-
-int
-main(int argc, char **argv)
-{
- if (!getcwd(NULL, 0)) return EXIT_FAILURE;
- return EXIT_SUCCESS;
-}
-],
- rb_cv_getcwd_malloc=yes,
- rb_cv_getcwd_malloc=no,
- AS_CASE($target_os,
- [linux*|darwin*|*bsd|cygwin*|mingw*|mswin*],
- [rb_cv_getcwd_malloc=yes],
- [rb_cv_getcwd_malloc=no]))])
- AS_IF([test "$rb_cv_getcwd_malloc" = no], [AC_DEFINE(NO_GETCWD_MALLOC, 1)])
-])
-
-AS_IF([test "$ac_cv_func_crypt_r" = yes],
- [AC_CHECK_MEMBERS([struct crypt_data.initialized], [], [],
- [AC_INCLUDES_DEFAULT([@%:@include <crypt.h>])])])
-
-AC_DEFUN([RUBY_CHECK_BUILTIN_FUNC], [dnl
-AC_CACHE_CHECK([for $1], AS_TR_SH(rb_cv_builtin_$1),
- [AC_LINK_IFELSE(
- [AC_LANG_PROGRAM([int foo;], [$2;])],
- [AS_TR_SH(rb_cv_builtin_$1)=yes],
- [AS_TR_SH(rb_cv_builtin_$1)=no])])
-if test "${AS_TR_SH(rb_cv_builtin_$1)}" != no; then
- AC_DEFINE(AS_TR_CPP(HAVE_BUILTIN_$1))
-fi])
-RUBY_CHECK_BUILTIN_FUNC(__builtin_bswap16, [__builtin_bswap16(0)])
-RUBY_CHECK_BUILTIN_FUNC(__builtin_bswap32, [__builtin_bswap32(0)])
-RUBY_CHECK_BUILTIN_FUNC(__builtin_bswap64, [__builtin_bswap64(0)])
-RUBY_CHECK_BUILTIN_FUNC(__builtin_popcount, [__builtin_popcount(0)])
-RUBY_CHECK_BUILTIN_FUNC(__builtin_popcountll, [__builtin_popcountll(0)])
-RUBY_CHECK_BUILTIN_FUNC(__builtin_clz, [__builtin_clz(0)])
-RUBY_CHECK_BUILTIN_FUNC(__builtin_clzl, [__builtin_clzl(0)])
-RUBY_CHECK_BUILTIN_FUNC(__builtin_clzll, [__builtin_clzll(0)])
-RUBY_CHECK_BUILTIN_FUNC(__builtin_ctz, [__builtin_ctz(0)])
-RUBY_CHECK_BUILTIN_FUNC(__builtin_ctzll, [__builtin_ctzll(0)])
-RUBY_CHECK_BUILTIN_FUNC(__builtin_constant_p, [__builtin_constant_p(0)])
-RUBY_CHECK_BUILTIN_FUNC(__builtin_choose_expr, [
- [int x[__extension__(__builtin_choose_expr(1, 1, -1))]];
- [int y[__extension__(__builtin_choose_expr(0, -1, 1))]];
- ])
-if test x$rb_cv_builtin___builtin_choose_expr = xyes; then
- RUBY_CHECK_BUILTIN_FUNC(__builtin_choose_expr_constant_p, [
- [int x[__extension__(__builtin_choose_expr(__builtin_constant_p(1), 1, -1))]];
- [int y[__extension__(__builtin_choose_expr(__builtin_constant_p(foo), -1, 1))]];
- ])
-fi
-RUBY_CHECK_BUILTIN_FUNC(__builtin_types_compatible_p, [__builtin_types_compatible_p(int, int)])
-
-if test "$ac_cv_func_qsort_r" != no; then
- AC_CACHE_CHECK(whether qsort_r is GNU version, rb_cv_gnu_qsort_r,
- [AC_TRY_COMPILE([
-@%:@include <stdlib.h>
-void qsort_r(void *base, size_t nmemb, size_t size,
- int (*compar)(const void *, const void *, void *),
- void *arg);
-],[ ],
- [rb_cv_gnu_qsort_r=yes],
- [rb_cv_gnu_qsort_r=no])
- ])
- AC_CACHE_CHECK(whether qsort_r is BSD version, rb_cv_bsd_qsort_r,
- [AC_TRY_COMPILE([
-@%:@include <stdlib.h>
-void qsort_r(void *base, size_t nmemb, size_t size,
- void *arg, int (*compar)(void *, const void *, const void *));
-],[ ],
- [rb_cv_bsd_qsort_r=yes],
- [rb_cv_bsd_qsort_r=no])
- ])
- AS_CASE("$rb_cv_gnu_qsort_r:$rb_cv_bsd_qsort_r",
- [yes:no], [
- AC_DEFINE(HAVE_GNU_QSORT_R, 1)
- ],
- [no:yes], [
- AC_DEFINE(HAVE_BSD_QSORT_R, 1)
- ])
-fi
-
-AC_CACHE_CHECK(whether atan2 handles Inf as C99, rb_cv_atan2_inf_c99, [
- AS_IF([test $ac_cv_func_atan2f:$ac_cv_func_atan2l = yes:yes], [
- AC_TRY_RUN([
-@%:@include <math.h>
-@%:@ifdef HAVE_UNISTD_H
-@%:@include <unistd.h>
-@%:@endif
-@%:@ifndef EXIT_SUCCESS
-@%:@define EXIT_SUCCESS 0
-@%:@endif
-@%:@ifndef EXIT_FAILURE
-@%:@define EXIT_FAILURE 1
-@%:@endif
-
-int
-main(int argc, char **argv)
-{
- if (fabs(atan2(INFINITY, INFINITY) - M_PI_4) <= 0.01) return EXIT_SUCCESS;
- return EXIT_FAILURE;
-}
-],
- [rb_cv_atan2_inf_c99=yes],
- [rb_cv_atan2_inf_c99=no],
- [AS_CASE($target_os, [mingw*|mswin*], [rb_cv_atan2_inf_c99=no], [rb_cv_atan2_inf_c99=yes])]
- )
- ], [rb_cv_atan2_inf_c99=no])
-])
-AS_IF([test "x$rb_cv_atan2_inf_c99" = xyes], [AC_DEFINE(ATAN2_INF_C99)])
-
-AS_IF([test "x$ac_cv_func_lgamma_r" = xyes], [
- AC_CACHE_CHECK(whether lgamma_r handles +0.0 and -0.0, rb_cv_lgamma_r_pm0, [
- AC_TRY_RUN([
-@%:@include <math.h>
-@%:@ifdef HAVE_UNISTD_H
-@%:@include <unistd.h>
-@%:@endif
-@%:@ifndef EXIT_SUCCESS
-@%:@define EXIT_SUCCESS 0
-@%:@endif
-@%:@ifndef EXIT_FAILURE
-@%:@define EXIT_FAILURE 1
-@%:@endif
-
-int
-main(int argc, char **argv)
-{
- int sign = 0;
- double x = lgamma_r(-0.0, &sign);
-
- /* should be [+inf, -1] */
- if (x <= 0) return EXIT_FAILURE;
- if (!isinf(x)) return EXIT_FAILURE;
- if (sign != -1) return EXIT_FAILURE;
-
- /* should be [+inf, 1] */
- x = lgamma_r(+0.0, &sign);
- if (x <= 0) return EXIT_FAILURE;
- if (!isinf(x)) return EXIT_FAILURE;
- if (sign != 1) return EXIT_FAILURE;
- return EXIT_SUCCESS;
-}
-],
- [rb_cv_lgamma_r_pm0=yes],
- [rb_cv_lgamma_r_pm0=no],
- [rb_cv_lgamma_r_pm0=yes]
- )
- ])
- AS_IF([test "x$rb_cv_lgamma_r_pm0" = xno], [AC_DEFINE(LGAMMA_R_PM0_FIX)])
-])
-
-# Some platform need -lrt for clock_gettime, but the other don't.
-if test x"$ac_cv_func_clock_gettime" != xyes; then
- # glibc 2.17 moves clock_* functions from librt to the main C library.
- # http://sourceware.org/ml/libc-announce/2012/msg00001.html
- AC_CHECK_LIB(rt, clock_gettime)
- if test x"$ac_cv_lib_rt_clock_gettime" = xyes; then
- AC_DEFINE(HAVE_CLOCK_GETTIME, 1)
- fi
-fi
-AC_CHECK_FUNCS(clock_getres) # clock_getres should be tested after clock_gettime test including librt test.
-
-AC_CACHE_CHECK(for unsetenv returns a value, rb_cv_unsetenv_return_value,
- [AC_TRY_COMPILE([
-#include <stdlib.h>
-], [int v = unsetenv("foo");],
- rb_cv_unsetenv_return_value=yes,
- rb_cv_unsetenv_return_value=no)])
-if test "$rb_cv_unsetenv_return_value" = no; then
- AC_DEFINE(VOID_UNSETENV)
-fi
-
-# used for AC_ARG_WITH(setjmp-type)
-AC_DEFUN([RUBY_CHECK_SETJMP], [
-AC_CACHE_CHECK([for ]$1[ as a macro or function], ac_cv_func_$1,
- [AC_TRY_COMPILE([
-@%:@include <setjmp.h>
-]AC_INCLUDES_DEFAULT([$3])[
-@%:@define JMPARGS_1 env
-@%:@define JMPARGS_2 env,1
-@%:@define JMPARGS JMPARGS_]m4_ifval($2,2,1)[
-],
- m4_ifval($2,$2,jmp_buf)[ env; $1(JMPARGS);],
- ac_cv_func_$1=yes,
- ac_cv_func_$1=no)]
-)
-AS_IF([test "$ac_cv_func_]$1[" = yes], [AC_DEFINE([HAVE_]AS_TR_CPP($1), 1)])
-])
-
-AC_DEFUN([RUBY_CHECK_BUILTIN_SETJMP], [
-if test x"${ac_cv_func___builtin_setjmp}" = xyes; then
- unset ac_cv_func___builtin_setjmp
-fi
-AC_CACHE_CHECK(for __builtin_setjmp, ac_cv_func___builtin_setjmp,
- [
- ac_cv_func___builtin_setjmp=no
- for cast in "" "(void **)"; do
- RUBY_WERROR_FLAG(
- [AC_TRY_LINK([@%:@include <setjmp.h>
- @%:@include <stdio.h>
- jmp_buf jb;
- @%:@ifdef NORETURN
- NORETURN(void t(void));
- @%:@endif
- void t(void) {__builtin_longjmp($cast jb, 1);}
- int jump(void) {(void)(__builtin_setjmp($cast jb) ? 1 : 0); return 0;}],
- [
- void (*volatile f)(void) = t;
- if (!jump()) printf("%d\n", f != 0);
- ],
- [ac_cv_func___builtin_setjmp="yes with cast ($cast)"])
- ])
- test "$ac_cv_func___builtin_setjmp" = no || break
- done])
-])
-
-AC_DEFUN([RUBY_SETJMP_TYPE], [
-RUBY_CHECK_BUILTIN_SETJMP
-RUBY_CHECK_SETJMP(_setjmpex, [], [@%:@include <setjmpex.h>])
-RUBY_CHECK_SETJMP(_setjmp)
-RUBY_CHECK_SETJMP(sigsetjmp, [sigjmp_buf])
-AC_MSG_CHECKING(for setjmp type)
-setjmp_suffix=
-AC_ARG_WITH(setjmp-type,
- AS_HELP_STRING([--with-setjmp-type], [select setjmp type]),
- [
- AS_CASE([$withval],
- [__builtin_setjmp], [setjmp=__builtin_setjmp],
- [_setjmp], [ setjmp_prefix=_],
- [sigsetjmp], [ setjmp_prefix=sig],
- [setjmp], [ setjmp_prefix=],
- [setjmpex], [ setjmp_prefix= setjmp_suffix=ex],
- [''], [ unset setjmp_prefix],
- [ AC_MSG_ERROR(invalid setjmp type: $withval)])], [unset setjmp_prefix])
-setjmp_cast=
-if test ${setjmp_prefix+set}; then
- if test "${setjmp_prefix}" && eval test '$ac_cv_func_'${setjmp_prefix}setjmp${setjmp_suffix} = no; then
- AC_MSG_ERROR(${setjmp_prefix}setjmp${setjmp_suffix} is not available)
- fi
-elif { AS_CASE("$ac_cv_func___builtin_setjmp", [yes*], [true], [false]) }; then
- setjmp_cast=`expr "$ac_cv_func___builtin_setjmp" : "yes with cast (\(.*\))"`
- setjmp_prefix=__builtin_
- setjmp_suffix=
-elif test "$ac_cv_header_setjmpex_h:$ac_cv_func__setjmpex" = yes:yes; then
- setjmp_prefix=
- setjmp_suffix=ex
-elif test "$ac_cv_func__setjmp" = yes; then
- setjmp_prefix=_
- setjmp_suffix=
-elif test "$ac_cv_func_sigsetjmp" = yes; then
- AS_CASE([$target_os],[solaris*|cygwin*],[setjmp_prefix=],[setjmp_prefix=sig])
- setjmp_suffix=
-else
- setjmp_prefix=
- setjmp_suffix=
-fi
-if test x$setjmp_prefix = xsig; then
- setjmp_sigmask=yes
-else
- unset setjmp_sigmask
-fi
-AC_MSG_RESULT(${setjmp_prefix}setjmp${setjmp_suffix}${setjmp_cast:+\($setjmp_cast\)})
-AC_DEFINE_UNQUOTED([RUBY_SETJMP(env)], [${setjmp_prefix}setjmp${setjmp_suffix}($setjmp_cast(env)${setjmp_sigmask+,0})])
-AC_DEFINE_UNQUOTED([RUBY_LONGJMP(env,val)], [${setjmp_prefix}longjmp($setjmp_cast(env),val)])
-AC_DEFINE_UNQUOTED(RUBY_JMP_BUF, ${setjmp_sigmask+${setjmp_prefix}}jmp_buf)
-AS_IF([test x$setjmp_suffix = xex], [AC_DEFINE_UNQUOTED(RUBY_USE_SETJMPEX, 1)])
-])
-# End of setjmp check.
-
-AC_ARG_ENABLE(setreuid,
- AS_HELP_STRING([--enable-setreuid], [use setreuid()/setregid() according to need even if obsolete]),
- [use_setreuid=$enableval])
-if test "$use_setreuid" = yes; then
- AC_DEFINE(USE_SETREUID)
- AC_DEFINE(USE_SETREGID)
-fi
-AC_STRUCT_TIMEZONE
-AC_CACHE_CHECK(for struct tm.tm_gmtoff, rb_cv_member_struct_tm_tm_gmtoff,
- [AC_TRY_COMPILE([
-@%:@define _BSD_SOURCE
-@%:@define _DEFAULT_SOURCE
-@%:@include <time.h>
- ],
- [struct tm t; t.tm_gmtoff = 3600;],
- [rb_cv_member_struct_tm_tm_gmtoff=yes],
- [rb_cv_member_struct_tm_tm_gmtoff=no])])
-if test "$rb_cv_member_struct_tm_tm_gmtoff" = yes; then
- AC_DEFINE(HAVE_STRUCT_TM_TM_GMTOFF)
-fi
-AC_CACHE_CHECK(for external int daylight, rb_cv_have_daylight,
- [AC_TRY_LINK([#include <time.h>
- int i;],
- [i = daylight;],
- rb_cv_have_daylight=yes,
- rb_cv_have_daylight=no)])
-if test "$rb_cv_have_daylight" = yes; then
- AC_DEFINE(HAVE_DAYLIGHT)
-fi
-AC_DEFUN([RUBY_CHECK_VARTYPE], [dnl
-AC_CACHE_CHECK([for external $1], AS_TR_SH(rb_cv_var_$1),
- [AS_TR_SH(rb_cv_var_$1)=no
- AC_TRY_COMPILE([
-#ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE 1
-#endif
-$2
-;
-const volatile void *volatile t;],
- [t = &(&$1)[0];],
- [for t in $3; do
- AC_TRY_COMPILE([
-#ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE 1
-#endif
-$2
-;
-extern $t $1;
-const volatile void *volatile t;],
- [t = &(&$1)[0];],
- [AS_TR_SH(rb_cv_var_$1)=$t; break])
- done])])
-if test "${AS_TR_SH(rb_cv_var_$1)}" != no; then
- AC_DEFINE(AS_TR_CPP(HAVE_VAR_$1))
- AC_DEFINE_UNQUOTED(AS_TR_CPP(TYPEOF_VAR_$1), ${AS_TR_SH(rb_cv_var_$1)})
-fi])
-RUBY_CHECK_VARTYPE(timezone, [@%:@include <time.h>], [long int])
-RUBY_CHECK_VARTYPE(altzone, [@%:@include <time.h>], [long int])
-AC_CHECK_FUNCS(timezone)
-if test "$ac_cv_func_timezone" = yes; then
- AC_CACHE_CHECK([whether timezone requires zero arguments], rb_cv_func_timezone_void,
- [AC_TRY_COMPILE([@%:@include <time.h>],
- [(void)timezone(0, 0);],
- [rb_cv_func_timezone_void=no],
- [rb_cv_func_timezone_void=yes])]
- )
- if test $rb_cv_func_timezone_void = yes; then
- AC_DEFINE(TIMEZONE_VOID)
- fi
-fi
-
-AC_CACHE_CHECK(for negative time_t for gmtime(3), rb_cv_negative_time_t,
- [AC_TRY_RUN([
-#include <stdlib.h>
-#include <time.h>
-
-void
-check(tm, y, m, d, h, s)
- struct tm *tm;
- int y, m, d, h, s;
-{
- if (!tm ||
- tm->tm_year != y ||
- tm->tm_mon != m-1 ||
- tm->tm_mday != d ||
- tm->tm_hour != h ||
- tm->tm_sec != s) {
- exit(1);
- }
-}
-
-int
-main()
-{
- time_t t = -1;
- struct tm *tm;
-
- check(gmtime(&t), 69, 12, 31, 23, 59);
- t = ~(time_t)0 << 31;
- check(gmtime(&t), 1, 12, 13, 20, 52);
- return 0;
-}
-],
- rb_cv_negative_time_t=yes,
- rb_cv_negative_time_t=no,
- rb_cv_negative_time_t=yes)])
-if test "$rb_cv_negative_time_t" = yes; then
- AC_DEFINE(NEGATIVE_TIME_T)
-fi
-
-# [ruby-dev:40910] overflow of time on FreeBSD
-# http://www.freebsd.org/cgi/query-pr.cgi?pr=145341
-AC_CACHE_CHECK(for localtime(3) overflow correctly, rb_cv_localtime_overflow,
- [AC_TRY_RUN([
-#include <stdlib.h>
-#include <time.h>
-
-void
-check(time_t t1)
-{
- struct tm *tm;
- time_t t2;
- tm = localtime(&t1);
- if (!tm)
- return; /* overflow detected. ok. */
- t2 = mktime(tm);
- if (t1 == t2)
- return; /* round-trip. ok. */
- exit(1);
-}
-
-int
-main()
-{
- time_t t;
- if (~(time_t)0 <= 0) {
- t = (((time_t)1) << (sizeof(time_t) * 8 - 2));
- t |= t - 1;
- }
- else {
- t = ~(time_t)0;
- }
- check(t);
- return 0;
-}
-],
- rb_cv_localtime_overflow=yes,
- rb_cv_localtime_overflow=no,
- rb_cv_localtime_overflow=no)])
-if test "$rb_cv_localtime_overflow" = no; then
- AC_DEFINE(LOCALTIME_OVERFLOW_PROBLEM)
-fi
-
-if test "$ac_cv_func_sigprocmask" = yes && test "$ac_cv_func_sigaction" = yes; then
- AC_DEFINE(POSIX_SIGNAL)
-else
- AC_CHECK_FUNCS(sigsetmask)
- AC_CACHE_CHECK(for BSD signal semantics, rb_cv_bsd_signal,
- [AC_TRY_RUN([
-#include <stdio.h>
-#include <signal.h>
-
-void
-sig_handler(dummy)
- int dummy;
-{
-}
-
-int
-main()
-{
- signal(SIGINT, sig_handler);
- kill(getpid(), SIGINT);
- kill(getpid(), SIGINT);
- return 0;
-}
-],
- rb_cv_bsd_signal=yes,
- rb_cv_bsd_signal=no,
- rb_cv_bsd_signal=$ac_cv_func_sigsetmask)])
- if test "$rb_cv_bsd_signal" = yes; then
- AC_DEFINE(BSD_SIGNAL)
- fi
-fi
-
-AC_CHECK_TYPES([sig_t],[],[],[@%:@include <signal.h>])
-
-if test "$ac_cv_func_getpgid" = no; then
- # AC_FUNC_GETPGRP fails when cross-compiling with old autoconf.
- # autoconf is changed between 2.52d and 2.52f?
- # http://lists.gnu.org/archive/html/bug-gnu-utils/2001-09/msg00181.html
- # "autoconf cleanup for AC_FUNC_GETPGRP and GETPGRP_VOID"
-AC_FUNC_GETPGRP
-fi
-if test "$ac_cv_func_setpgid:$ac_cv_func_setpgrp" = no:yes; then
- # AC_FUNC_SETPGRP fails when cross-compiling. (until autoconf 2.69?)
- # https://lists.gnu.org/archive/html/bug-autoconf/2013-02/msg00002.html
- # "AC_FUNC_SETPGRP fails to work properly when cross-compiling"
-AC_FUNC_SETPGRP
-fi
-
-if test x"$ac_cv_func_dirfd" = xno; then
- AS_CASE(["$target_os"],[solaris*],
- [AC_CHECK_MEMBERS([DIR.d_fd, DIR.dd_fd],,,[
-#include <sys/types.h>
-#include <dirent.h>
-])])
-fi
-
-if test x"$target_cpu" = xia64; then
- AC_LIBOBJ([ia64])
- AC_CACHE_CHECK(for __libc_ia64_register_backing_store_base,
- rb_cv___libc_ia64_register_backing_store_base,
- [rb_cv___libc_ia64_register_backing_store_base=no
- AC_TRY_LINK(
- [extern unsigned long __libc_ia64_register_backing_store_base;],
- [unsigned long p = __libc_ia64_register_backing_store_base;
- printf("%ld\n", p);],
- [rb_cv___libc_ia64_register_backing_store_base=yes])])
- if test $rb_cv___libc_ia64_register_backing_store_base = yes; then
- AC_DEFINE(HAVE___LIBC_IA64_REGISTER_BACKING_STORE_BASE)
- fi
-fi
-
-AC_CACHE_CHECK(whether right shift preserve sign bit, rb_cv_rshift_sign,
- [AC_COMPILE_IFELSE([AC_LANG_BOOL_COMPILE_TRY([], [(-1==(-1>>1))])],
- rb_cv_rshift_sign=yes,
- rb_cv_rshift_sign=no)])
-if test "$rb_cv_rshift_sign" = yes; then
- AC_DEFINE(RSHIFT(x,y), ((x)>>(int)(y)))
-else
- AC_DEFINE(RSHIFT(x,y), (((x)<0) ? ~((~(x))>>(int)(y)) : (x)>>(int)(y)))
-fi
-
-if test x"$ac_cv_func_gettimeofday" != xyes; then
- AC_MSG_ERROR(gettimeofday() must exist)
-fi
-
-if test "$ac_cv_func_sysconf" = yes; then
- AC_DEFUN([RUBY_CHECK_SYSCONF], [dnl
- AC_CACHE_CHECK([whether _SC_$1 is supported], rb_cv_have_sc_[]m4_tolower($1),
- [AC_TRY_COMPILE([#include <unistd.h>
- ],
- [_SC_$1 >= 0],
- rb_cv_have_sc_[]m4_tolower($1)=yes,
- rb_cv_have_sc_[]m4_tolower($1)=no)
- ])
- if test "$rb_cv_have_sc_[]m4_tolower($1)" = yes; then
- AC_DEFINE(HAVE__SC_$1)
- fi
- ])
- RUBY_CHECK_SYSCONF(CLK_TCK)
-fi
-
-AC_DEFUN([RUBY_STACK_GROW_DIRECTION], [
- AS_VAR_PUSHDEF([stack_grow_dir], [rb_cv_stack_grow_dir_$1])
- AC_CACHE_CHECK(stack growing direction on $1, stack_grow_dir, [
-AS_CASE(["$1"],
-[m68*|x86*|x64|i?86|ia64|ppc*|sparc*|alpha*], [ $2=-1],
-[hppa*], [ $2=+1],
-[
- AC_TRY_RUN([
-/* recurse to get rid of inlining */
-static int
-stack_growup_p(addr, n)
- volatile int *addr, n;
-{
- volatile int end;
- if (n > 0)
- return *addr = stack_growup_p(addr, n - 1);
- else
- return (&end > addr);
-}
-int main()
-{
- int x;
- return stack_growup_p(&x, 10);
-}
-], $2=-1, $2=+1, $2=0)
- ])
-eval stack_grow_dir=\$$2])
-eval $2=\$stack_grow_dir
-AS_VAR_POPDEF([stack_grow_dir])])
-if test "${universal_binary-no}" = yes ; then
- archflagpat=`eval echo '"'"${ARCH_FLAG}"'"' | sed 's/[[][|.*]]/\\&/g'`
- save_CFLAGS="$CFLAGS" new_cflags=`echo "$CFLAGS" | sed "s|$archflagpat"'||'`
- save_LDFLAGS="$LDFLAGS" new_ldflags=`echo "$LDFLAGS" | sed "s|$archflagpat"'||'`
- stack_dir=
- for archs in ${universal_archnames}; do
- archs=`echo $archs | sed 's/=.*//'`
- CFLAGS="$new_cflags -arch $archs"
- LDFLAGS="$new_ldflags -arch $archs"
- RUBY_STACK_GROW_DIRECTION($archs, dir)
- if test x$stack_dir = x; then
- stack_dir=$dir
- elif test x$stack_dir != x$dir; then
- stack_dir=no
- fi
- done
- CFLAGS="$save_CFLAGS" LDFLAGS="$save_LDFLAGS"
- if test x$stack_dir = xno; then
- for archs in ${universal_archnames}; do
- archs=`echo $archs | sed 's/=.*//'`
- eval dir=\$[rb_cv_stack_grow_dir_]AS_TR_SH([$archs])
- RUBY_DEFINE_IF([defined __${archs}__], STACK_GROW_DIRECTION, $dir)
- done
- else
- AC_DEFINE_UNQUOTED(STACK_GROW_DIRECTION, $stack_dir)
- fi
-else
- RUBY_STACK_GROW_DIRECTION($target_cpu, dir)
- AC_DEFINE_UNQUOTED(STACK_GROW_DIRECTION, $dir)
-fi
-
-if test x"$enable_pthread" = xyes; then
- for pthread_lib in thr pthread pthreads c c_r root; do
- AC_CHECK_LIB($pthread_lib, pthread_kill,
- rb_with_pthread=yes, rb_with_pthread=no)
- if test "$rb_with_pthread" = "yes"; then break; fi
- done
- if test x"$rb_with_pthread" = xyes; then
- AC_DEFINE(_REENTRANT)
- AC_DEFINE(_THREAD_SAFE)
- AC_DEFINE(HAVE_LIBPTHREAD)
- AC_CHECK_HEADERS(pthread_np.h, [], [], [@%:@include <pthread.h>])
- AS_CASE([$pthread_lib],
- [c], [],
- [root], [],
- [c_r], [MAINLIBS="-pthread $MAINLIBS"],
- [AS_CASE(["$target_os"],
- [openbsd*|mirbsd*], [LIBS="-pthread $LIBS"],
- [LIBS="-l$pthread_lib $LIBS"])])
- else
- AC_MSG_WARN("Don't know how to find pthread library on your system -- thread support disabled")
- fi
- AC_CACHE_CHECK([whether pthread_t is scalar type], [rb_cv_scalar_pthread_t], [
- AC_TRY_COMPILE([
- @%:@include <pthread.h>
- ], [
- pthread_t thread_id;
- thread_id = 0;
- if (!thread_id) return 0;
- ], [rb_cv_scalar_pthread_t=yes], [rb_cv_scalar_pthread_t=no])
- ])
- if test x"$rb_cv_scalar_pthread_t" = xyes; then
- : # RUBY_CHECK_SIZEOF(pthread_t, [void* int long], [], [@%:@include <pthread.h>])
- else
- AC_DEFINE(NON_SCALAR_THREAD_ID)
- fi
- AC_CHECK_FUNCS(sched_yield pthread_attr_setinheritsched \
- pthread_attr_get_np pthread_attr_getstack\
- pthread_get_stackaddr_np pthread_get_stacksize_np \
- thr_stksegment pthread_stackseg_np pthread_getthrds_np \
- pthread_cond_init pthread_condattr_setclock pthread_condattr_init \
- pthread_sigmask pthread_setname_np pthread_set_name_np)
- AS_CASE(["$target_os"],[aix*],[ac_cv_func_pthread_getattr_np=no],[AC_CHECK_FUNCS(pthread_getattr_np)])
- if test "${host_os}" = "nacl"; then
- ac_cv_func_pthread_attr_init=no
- else
- AC_CHECK_FUNCS(pthread_attr_init)
- fi
- set_current_thread_name=
- if test "$ac_cv_func_pthread_setname_np" = yes; then
- AC_CACHE_CHECK([arguments of pthread_setname_np], [rb_cv_func_pthread_setname_np_arguments],
- [rb_cv_func_pthread_setname_np_arguments=
- # Linux,AIX, (pthread_self(), name)
- # NetBSD (pthread_self(), name, \"%s\")
- # Darwin (name)
- for mac in \
- "(pthread_self(), name)" \
- "(pthread_self(), name, \"%s\")" \
- "(name)" \
- ; do
- AC_TRY_COMPILE([
- @%:@include <pthread.h>
- @%:@ifdef HAVE_PTHREAD_NP_H
- @%:@include <pthread_np.h>
- @%:@endif
- @%:@define SET_THREAD_NAME(name) pthread_setname_np${mac}
- ],
- [if (SET_THREAD_NAME("conftest")) return 1;],
- [rb_cv_func_pthread_setname_np_arguments="${mac}"
- break])
- done
- ]
- )
- if test -n "${rb_cv_func_pthread_setname_np_arguments}"; then
- set_current_thread_name="pthread_setname_np${rb_cv_func_pthread_setname_np_arguments}"
- fi
- elif test "$ac_cv_func_pthread_set_name_np" = yes; then
- set_current_thread_name="pthread_set_name_np(pthread_self(), name)"
- fi
- AS_IF([test -n "$set_current_thread_name"], [
- AC_DEFINE_UNQUOTED(SET_CURRENT_THREAD_NAME(name), $set_current_thread_name)
- AS_CASE([$set_current_thread_name],
- [*'pthread_self()'*], [
- set_another_thread_name=`echo "$set_current_thread_name" | sed 's/pthread_self()/thid/'`
- AC_DEFINE_UNQUOTED(SET_ANOTHER_THREAD_NAME(thid,name), $set_another_thread_name)
- ])
- ])
-fi
-
-if test x"$ac_cv_header_ucontext_h" = xno; then
- AC_CACHE_CHECK([if signal.h defines ucontext_t], [rb_cv_ucontext_in_signal_h],
- [AC_TRY_COMPILE([@%:@include <signal.h>],
- [size_t size = sizeof(ucontext_t);],
- [rb_cv_ucontext_in_signal_h=yes], [rb_cv_ucontext_in_signal_h=no])])
- if test x"$rb_cv_ucontext_in_signal_h" = xyes; then
- AC_DEFINE_UNQUOTED(UCONTEXT_IN_SIGNAL_H, 1)
- fi
-fi
-if test x"$ac_cv_header_ucontext_h" = xyes -o x"$rb_cv_ucontext_in_signal_h" = xyes; then
- AC_CACHE_CHECK([if mcontext_t is a pointer], [rb_cv_mcontext_t_ptr],
- [AC_TRY_COMPILE([
- @%:@include <signal.h>
- @%:@ifdef HAVE_UCONTEXT_H
- @%:@include <ucontext.h>
- @%:@endif
- mcontext_t test(mcontext_t mc) {return mc+1;}
- ],
- [test(0);],
- [rb_cv_mcontext_t_ptr=yes], [rb_cv_mcontext_t_ptr=no])])
- if test x"$rb_cv_mcontext_t_ptr" = xyes; then
- AC_DEFINE_UNQUOTED(DEFINE_MCONTEXT_PTR(mc, uc), mcontext_t mc = (uc)->uc_mcontext)
- else
- AC_DEFINE_UNQUOTED(DEFINE_MCONTEXT_PTR(mc, uc), mcontext_t *mc = &(uc)->uc_mcontext)
- fi
- if test x"$rb_with_pthread" = xyes; then
- AC_CHECK_FUNCS(getcontext setcontext)
- fi
-fi
-
-if test "$ac_cv_func_fork_works" = "yes" -a "$rb_with_pthread" = "yes"; then
- AC_CACHE_CHECK([if fork works with pthread], rb_cv_fork_with_pthread,
- [AC_TRY_RUN([
-#include <stdlib.h>
-#include <unistd.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <signal.h>
-#ifndef EXIT_SUCCESS
-#define EXIT_SUCCESS 0
-#endif
-#ifndef EXIT_FAILURE
-#define EXIT_FAILURE 1
-#endif
-
-void *
-thread_func(void *dmy)
-{
- return dmy;
-}
-
-int
-use_threads(void)
-{
- pthread_t tid;
- if (pthread_create(&tid, 0, thread_func, 0) != 0) {
- return -1;
- }
- if (pthread_join(tid, 0) != 0) {
- return -1;
- }
- return 0;
-}
-
-int
-main(int argc, char *argv[])
-{
- pid_t pid;
- if (use_threads()) return EXIT_FAILURE;
- pid = fork();
-
- if (pid) {
- int loc;
- sleep(1);
- if (waitpid(pid, &loc, WNOHANG) == 0) {
- kill(pid, SIGKILL);
- return EXIT_FAILURE;
- }
- if (!WIFEXITED(loc) || WEXITSTATUS(loc) != EXIT_SUCCESS)
- return EXIT_FAILURE;
- }
- else {
- if (use_threads()) return EXIT_FAILURE;
- }
-
- return EXIT_SUCCESS;
-}],
- rb_cv_fork_with_pthread=yes,
- rb_cv_fork_with_pthread=no,
- rb_cv_fork_with_pthread=yes)])
- test x$rb_cv_fork_with_pthread = xyes || AC_DEFINE(CANNOT_FORK_WITH_PTHREAD)
-fi
-
-
-}
-{ # runtime section
-
-dnl wheather use dln_a_out or not
-AC_ARG_WITH(dln-a-out,
- AS_HELP_STRING([--with-dln-a-out], [use dln_a_out if possible]),
- [
- AS_CASE([$withval],
- [yes], [
- if test "$enable_shared" = yes; then
- AC_MSG_ERROR(dln_a_out can not make shared library)
- fi
- with_dln_a_out=yes],
- [
- with_dln_a_out=no])], [with_dln_a_out=no])
-
-AC_CACHE_CHECK(whether ELF binaries are produced, rb_cv_binary_elf,
-[AC_TRY_LINK([],[], [
-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)])
-
-if test "$rb_cv_binary_elf" = yes; then
- AC_DEFINE(USE_ELF)
- if test "$with_dln_a_out" = yes; then
- AC_MSG_ERROR(dln_a_out does not work with ELF)
- fi
- AC_CHECK_HEADERS([elf.h elf_abi.h])
- if test $ac_cv_header_elf_h = yes -o $ac_cv_header_elf_abi_h = yes; then
- AC_LIBOBJ([addr2line])
- fi
-fi
-
-AS_CASE(["$target_os"],
-[linux* | gnu* | k*bsd*-gnu | bsdi* | kopensolaris*-gnu | nacl], [
- if test "$rb_cv_binary_elf" = no; then
- with_dln_a_out=yes
- else
- LDFLAGS="$LDFLAGS -rdynamic"
- fi])
-LIBEXT=a
-
-AC_SUBST(DLDFLAGS)dnl
-AC_SUBST(ARCH_FLAG)dnl
-
-AC_SUBST(STATIC)dnl
-AC_SUBST(CCDLFLAGS)dnl
-AC_SUBST(LDSHARED)dnl
-AC_SUBST(LDSHAREDXX)dnl
-AC_SUBST(DLEXT)dnl
-AC_SUBST(DLEXT2)dnl
-AC_SUBST(LIBEXT)dnl
-AC_SUBST(ASMEXT, S)dnl
-
-STATIC=
-: ${PATHFLAG=''}
-
-if test "$with_dln_a_out" != yes; then
- rb_cv_dlopen=unknown
- AC_MSG_CHECKING(whether OS depend dynamic link works)
- if test "$GCC" = yes; then
- AS_CASE(["$target_os"],
- [darwin*], [
- # The -fno-common is needed if we wish to embed the Ruby interpreter
- # into a plugin module of some project (as opposed to embedding it
- # within the project's application). The -I/usr/local/include is
- # needed because CPP as discovered by configure (cc -E -traditional)
- # fails to consult /usr/local/include by default. This causes
- # mkmf.rb's have_header() to fail if the desired resource happens to be
- # installed in the /usr/local tree.
- RUBY_APPEND_OPTION(CCDLFLAGS, -fno-common)],
- [bsdi*|cygwin*|mingw*|aix*|interix*], [ ],
- [
- RUBY_APPEND_OPTION(CCDLFLAGS, -fPIC)])
- else
- AS_CASE(["$target_os"],
- [hpux*], [CCDLFLAGS="$CCDLFLAGS +Z"],
- [solaris*|irix*], [CCDLFLAGS="$CCDLFLAGS -KPIC"],
- [sunos*], [CCDLFLAGS="$CCDLFLAGS -PIC"],
- [esix*|uxpds*], [CCDLFLAGS="$CCDLFLAGS -KPIC"],
- [: ${CCDLFLAGS=""}])
- fi
-
-
- 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_CASE(["$target_os"],
- [hpux*], [ DLDFLAGS="$DLDFLAGS -E"
- : ${LDSHARED='$(LD) -b'}
- XLDFLAGS="$XLDFLAGS -Wl,-E"
- : ${LIBPATHENV=SHLIB_PATH}
- if test "$rb_cv_prog_gnu_ld" = no; then
- RPATHFLAG=' +b %1$-s'
- fi
- rb_cv_dlopen=yes],
- [solaris*], [ if test "$GCC" = yes; then
- : ${LDSHARED='$(CC) -shared'}
- if test "$rb_cv_prog_gnu_ld" = yes; then
- LDFLAGS="$LDFLAGS -Wl,-E"
- fi
- else
- : ${LDSHARED='$(CC) -G'}
- fi
- if test "$ac_cv_sizeof_voidp" = 8; then
- : ${LIBPATHENV=LD_LIBRARY_PATH_64}
- : ${PRELOADENV=LD_PRELOAD_64}
- else
- : ${LIBPATHENV=LD_LIBRARY_PATH_32}
- : ${PRELOADENV=LD_PRELOAD_32}
- fi
- rb_cv_dlopen=yes],
- [sunos*], [ : ${LDSHARED='$(LD) -assert nodefinitions'}
- rb_cv_dlopen=yes],
- [irix*], [ : ${LDSHARED='$(LD) -shared'}
- rb_cv_dlopen=yes],
- [sysv4*], [ : ${LDSHARED='$(LD) -G'}
- rb_cv_dlopen=yes],
- [nto-qnx*], [ : ${LDSHARED='$(CC) -shared'}
- rb_cv_dlopen=yes],
- [esix*|uxpds*], [ : ${LDSHARED='$(LD) -G'}
- rb_cv_dlopen=yes],
- [osf*], [ : ${LDSHARED='$(LD) -shared -expect_unresolved "*"'}
- rb_cv_dlopen=yes],
- [bsdi3*], [ AS_CASE(["$CC"],
- [*shlicc*], [ : ${LDSHARED='$(CC) -r'}
- rb_cv_dlopen=yes])],
- [linux* | gnu* | k*bsd*-gnu | netbsd* | bsdi* | kopensolaris*-gnu | haiku*], [
- : ${LDSHARED='$(CC) -shared'}
- if test "$rb_cv_binary_elf" = yes; then
- LDFLAGS="$LDFLAGS -Wl,-export-dynamic"
- fi
- 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'}
- if test "$rb_cv_binary_elf" = yes; then
- LDFLAGS="$LDFLAGS -rdynamic"
- DLDFLAGS="$DLDFLAGS "'-Wl,-soname,$@'
- else
- test "$GCC" = yes && test "$rb_cv_prog_gnu_ld" = yes || LDSHARED='$(LD) -Bshareable'
- fi
- rb_cv_dlopen=yes],
- [openbsd*|mirbsd*], [ : ${LDSHARED='$(CC) -shared ${CCDLFLAGS}'}
- if test "$rb_cv_binary_elf" = yes; then
- LDFLAGS="$LDFLAGS -Wl,-E"
- fi
- rb_cv_dlopen=yes],
- [darwin*], [ : ${LDSHARED='$(CC) -dynamic -bundle'}
- : ${LDFLAGS=""}
- : ${LIBPATHENV=DYLD_LIBRARY_PATH}
- : ${PRELOADENV=DYLD_INSERT_LIBRARIES}
- rb_cv_dlopen=yes],
- [aix*], [ : ${LDSHARED='$(CC)'}
- LDSHARED="$LDSHARED ${linker_flag}-G"
- EXTDLDFLAGS='-e$(TARGET_ENTRY)'
- XLDFLAGS="${linker_flag}"'-bE:$(ARCHFILE)'" ${linker_flag}-brtl"
- XLDFLAGS="$XLDFLAGS ${linker_flag}-blibpath:${prefix}/lib:${LIBPATH:-/usr/lib:/lib}"
- : ${ARCHFILE="ruby.imp"}
- TRY_LINK='$(CC) $(LDFLAGS) -oconftest $(INCFLAGS) -I$(hdrdir) $(CPPFLAGS)'
- TRY_LINK="$TRY_LINK"' $(CFLAGS) $(src) $(LIBPATH) $(LOCAL_LIBS) $(LIBS)'
- : ${LIBPATHENV=LIBPATH}
- RPATHFLAG=" ${linker_flag}-blibpath:%1\$-s:${prefix}/lib:${LIBPATH:-/usr/lib:/lib}"
- rb_cv_dlopen=yes],
- [nto-qnx*], [ DLDFLAGS="$DLDFLAGS -L/lib -L/usr/lib -L/usr/local/lib"
- : ${LDSHARED='$(LD) -Bshareable -x'}
- LDFLAGS="$LDFLAGS -L/lib -L/usr/lib -L/usr/local/lib"
- rb_cv_dlopen=yes],
- [cygwin*|mingw*], [
- : ${LDSHARED='$(CC) -shared $(if $(filter-out -g -g0,$(debugflags)),,-s)'}
- XLDFLAGS="$XLDFLAGS -Wl,--stack,0x00200000,--enable-auto-import"
- DLDFLAGS="${DLDFLAGS} -Wl,--enable-auto-image-base,--enable-auto-import"
- : ${LIBPATHENV=""}
- : ${PRELOADENV=""}
- rb_cv_dlopen=yes],
- [hiuxmpp], [ : ${LDSHARED='$(LD) -r'}],
- [atheos*], [ : ${LDSHARED='$(CC) -shared'}
- rb_cv_dlopen=yes],
- [nacl], [ LDSHARED='$(CC) -shared' ],
- [ : ${LDSHARED='$(LD)'}])
- AC_MSG_RESULT($rb_cv_dlopen)
-
- if test "$rb_cv_dlopen" = yes; then
- AS_CASE(["$target_os"],
- [darwin*], [
- for flag in \
- "-undefined dynamic_lookup" \
- "-multiply_defined suppress" \
- ; do
- test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`"
- RUBY_TRY_LDFLAGS([$flag], [], [flag=])
- if test "x$flag" != x; then
- RUBY_APPEND_OPTIONS(DLDFLAGS, [$flag])
- fi
- done
- ])
- fi
-
- if test "$enable_rpath" = yes; then
- if test x"${RPATHFLAG}" = x; then
- for rpathflag in -R "-rpath "; do
- AS_CASE("$rpathflag",
- [*" "], [AS_CASE(["${linker_flag}"],
- [*,], [rpathflag=`echo "$rpathflag" | tr ' ' ,`])])
- rpathflag="${linker_flag}${rpathflag}"
- RUBY_TRY_LDFLAGS([${rpathflag}.], [], [rpathflag=])
- if test "x${rpathflag}" != x; then
- RPATHFLAG=" ${rpathflag}%1\$-s"
- break
- fi
- done
- fi
- fi
-fi
-if test "${LDSHAREDXX}" = ""; then
- AS_CASE(["${LDSHARED}"],
- [*'$(CC)'*], [
- LDSHAREDXX=`echo "${LDSHARED}" | sed 's/\$(CC)/$(CXX)/'`
- ],
- [*'${CC}'*], [
- LDSHAREDXX=`echo "${LDSHARED}" | sed 's/\${CC}/${CXX}/'`
- ],
- [*$CC*], [
- LDSHAREDXX=`echo "${LDSHARED}" | sed "s|$CC|$CXX|"`
- ],
- [ld" "*], [
- ])
-fi
-AS_CASE([${RPATHFLAG}],[*'%1$'*],[: ${LIBPATHFLAG=' -L%1$-s'}],[: ${LIBPATHFLAG=' -L%s'}])
-
-AC_SUBST(LINK_SO)
-AC_SUBST(LIBPATHFLAG)
-AC_SUBST(RPATHFLAG)
-AC_SUBST(LIBPATHENV, "${LIBPATHENV-LD_LIBRARY_PATH}")
-AC_SUBST(PRELOADENV, "${PRELOADENV-LD_PRELOAD}")
-AC_SUBST(TRY_LINK)
-
-if test "x$OPT_DIR" != x; then
- 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/ *$//'`
- if test x"$val" != x; then
- test x"${LDFLAGS}" = x || LDFLAGS="$LDFLAGS "
- LDFLAGS="$LDFLAGS$val"
- test x"${DLDFLAGS}" = x || DLDFLAGS="$DLDFLAGS "
- DLDFLAGS="$DLDFLAGS$val"
- fi
- LDFLAGS_OPTDIR="$val"
-fi
-
-AS_CASE(["$target_os"],
-[freebsd*], [
- AC_CHECK_LIB([procstat], [procstat_open_sysctl])
- if test "x$ac_cv_lib_procstat_procstat_open_sysctl" = xyes; then
- AC_CHECK_FUNCS(procstat_getvmmap)
- fi
- ])
-AS_CASE(["$target_cpu-$target_os"],
-[*-darwin*], [
- AC_CHECK_HEADERS([execinfo.h])
- if test "x$ac_cv_header_execinfo_h" = xyes; then
- AC_CHECK_LIB([execinfo], [backtrace])
- AC_CHECK_HEADERS([libunwind.h])
- fi],
-[*-freebsd*|x86_64-netbsd*], [
- AC_CHECK_HEADERS([execinfo.h])
- if test "x$ac_cv_header_execinfo_h" = xyes; then
- AC_CHECK_LIB([execinfo], [backtrace])
- AC_CHECK_LIB([unwind], [unw_backtrace])
- fi])
-AC_CHECK_FUNCS(backtrace)
-
-if test "x$ac_cv_func_backtrace" = xyes; then
- AC_CACHE_CHECK(for broken backtrace, rb_cv_broken_backtrace,
- [AC_TRY_RUN([
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <signal.h>
-#include <execinfo.h>
-
-#define TRACE_SIZE 256
-
-void sigsegv(int signum, siginfo_t *info, void *ctx){
- void *trace[TRACE_SIZE];
- int n = backtrace(trace, TRACE_SIZE);
- if (n > 0) {
- /*fprintf(stdout, "backtrace:%d\n",n);*/
- } else {
- _exit(EXIT_FAILURE);
- }
- _exit(EXIT_SUCCESS);
-}
-int
-main(void)
-{
- volatile int *a = NULL;
- stack_t ss;
- ss.ss_sp = malloc(SIGSTKSZ);
- if (ss.ss_sp == NULL) {
- fprintf(stderr, "cannot allocate memory for sigaltstack\n");
- return EXIT_FAILURE;
- }
- ss.ss_size = SIGSTKSZ;
- ss.ss_flags = 0;
- if (sigaltstack(&ss, NULL) == -1) {
- fprintf(stderr, "sigaltstack failed\n");
- return EXIT_FAILURE;
- }
- struct sigaction sa;
- memset(&sa, 0, sizeof(struct sigaction));
- sigemptyset(&sa.sa_mask);
- sa.sa_sigaction = sigsegv;
- sa.sa_flags |= SA_SIGINFO;
- sa.sa_flags |= SA_ONSTACK;
- sigaction(SIGSEGV, &sa, NULL);
- a[0] = 1;
- return EXIT_SUCCESS;
-}
-],
- rb_cv_broken_backtrace=no,
- rb_cv_broken_backtrace=yes,
- rb_cv_broken_backtrace=no)])
- if test "$rb_cv_broken_backtrace" = yes; then
- AC_DEFINE(BROKEN_BACKTRACE, 1)
- fi
-fi
-
-AC_ARG_WITH(valgrind,
- AS_HELP_STRING([--without-valgrind],[disable valgrind memcheck support]),
- [], with_valgrind=yes)
-AS_IF([test x$with_valgrind != xno],
- [AC_CHECK_HEADERS(valgrind/memcheck.h)])
-
-dln_a_out_works=no
-if test "$ac_cv_header_a_out_h" = yes; then
- if test "$with_dln_a_out" = yes || test "$rb_cv_dlopen" = unknown; then
- cat confdefs.h > config.h
- AC_CACHE_CHECK(whether matz's dln works, rb_cv_dln_a_out,
- [AC_TRY_COMPILE([
-#define USE_DLN_A_OUT
-#include "dln.c"
-],
- [],
- rb_cv_dln_a_out=yes,
- rb_cv_dln_a_out=no)])
- if test "$rb_cv_dln_a_out" = yes; then
- dln_a_out_works=yes
- AC_DEFINE(USE_DLN_A_OUT)
- fi
- fi
-fi
-
-if test "$dln_a_out_works" = yes; then
- if test "$GCC" = yes; then
- STATIC=-static
- else
- STATIC=-Bstatic
- fi
- DLEXT=so
- CCDLFLAGS=
-else
- AS_CASE(["$target_os"],
- [hpux*], [
- DLEXT=sl],
- [darwin*], [
- RUBY_APPEND_OPTION(XLDFLAGS, [-Wl,-u,_objc_msgSend])
- DLEXT=bundle],
- [cygwin*|mingw*|*djgpp*], [
- LOAD_RELATIVE=1
- DLEXT=so],
- [
- DLEXT=so])
-fi
-if test "$rb_cv_dlopen:$load_relative" = yes:yes; then
- if test "$ac_cv_func_dladdr" = yes; then
- LOAD_RELATIVE=1
- fi
-fi
-if test x"$LOAD_RELATIVE" = x1; then
- load_relative=yes
-else
- unset load_relative
-fi
-
-len=2 # .rb
-n=`expr "$DLEXT" : '.*'`; test "$n" -gt "$len" && len=$n
-n=`expr "$DLEXT2" : '.*'`; test "$n" -gt "$len" && len=$n
-AC_DEFINE_UNQUOTED(DLEXT_MAXLEN, `expr $len + 1`)
-test ".$DLEXT" = "." || AC_DEFINE_UNQUOTED(DLEXT, ".$DLEXT")
-test ".$DLEXT2" = "." || AC_DEFINE_UNQUOTED(DLEXT2, ".$DLEXT2")
-AC_SUBST(DLEXT)
-
-if test "$with_dln_a_out" = yes; then
- STRIP=true
-else
- AC_CHECK_TOOL(STRIP, strip, :)dnl
-fi
-
-AS_CASE(["$target_os"],
- [linux* | gnu* | k*bsd*-gnu | kopensolaris*-gnu], [
- STRIP="$STRIP -S -x"],
- [darwin*], [
- STRIP="$STRIP -A -n"])
-
-AC_ARG_WITH(ext,
- AC_HELP_STRING([--with-ext=EXTS],
- [pass to --with-ext option of extmk.rb]))
-AC_ARG_WITH(out-ext,
- AC_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])
-if test -n "$setup"; then
- if ! test -f "ext/$setup" -o -f "$srcdir/ext/$setup"; then
- AC_MSG_ERROR(Setup file $setup not found under ext or $srcdir/ext)
- fi
-elif test -f "$srcdir/ext/Setup.$target_os"; then
- setup="Setup.$target_os"
-else
- setup=
- for file in "$srcdir"/ext/Setup.*; do
- AS_CASE(["$file"], [*~|*.bak|*.orig|*.rej|*.tmp], [continue])
- setup=`basename "$file"`
- AS_CASE(["$target_os"], [`expr "$setup" : 'Setup.\(.*\)'`*], [break])
- platform=`sed '/^option *platform */!d;s///;s/|/*|/g;q' "$file"`
- if test "x$platform" != x; then
- eval "AS_CASE([\"\$target_os\"], [$platform*], [break])"
- fi
- setup=
- done
- : ${setup:=Setup}
-fi
-AC_SUBST(setup)
-
-rubylibprefix='${libdir}/${RUBY_BASE_NAME}'
-AC_ARG_WITH(rubylibprefix,
- AS_HELP_STRING([--with-rubylibprefix=DIR], [prefix for ruby libraries [[LIBDIR/RUBY_BASE_NAME]]]),
- [if test "x$withval" = xno; then
- AC_MSG_ERROR([No ruby, No libprefix])
- fi
- rubylibprefix="$withval"])
-AC_SUBST(rubylibprefix)
-
-if test x"${exec_prefix}" != xNONE; then
- RUBY_EXEC_PREFIX="$exec_prefix"
-elif test x"$prefix" != xNONE; then
- RUBY_EXEC_PREFIX="$prefix"
-else
- RUBY_EXEC_PREFIX=$ac_default_prefix
-fi
-pat=`echo "${RUBY_EXEC_PREFIX}" | tr -c '\012' .`'\(.*\)'
-for var in bindir libdir rubylibprefix; do
- eval val='"$'$var'"'
- AS_CASE(["$val"], ["${RUBY_EXEC_PREFIX}"*], [val='${exec_prefix}'"`expr \"$val\" : \"$pat\"`"])
- eval $var='"$val"'
-done
-
-BTESTRUBY='$(MINIRUBY)'
-if test x"$cross_compiling" = xyes; then
- test x"$MINIRUBY" = x && MINIRUBY="${RUBY-$BASERUBY} -I`$CHDIR .; pwd` "-r'$(arch)-fake'
- XRUBY_LIBDIR=`${RUBY-$BASERUBY} -rrbconfig -e ['puts RbConfig::CONFIG["libdir"]']`
- XRUBY_RUBYLIBDIR=`${RUBY-$BASERUBY} -rrbconfig -e ['puts RbConfig::CONFIG["rubylibdir"]']`
- XRUBY_RUBYHDRDIR=`${RUBY-$BASERUBY} -rrbconfig -e ['puts RbConfig::CONFIG["rubyhdrdir"]']`
- AC_SUBST(XRUBY_LIBDIR)
- AC_SUBST(XRUBY_RUBYLIBDIR)
- AC_SUBST(XRUBY_RUBYHDRDIR)
- PREP='$(arch)-fake.rb'
- RUNRUBY_COMMAND='$(MINIRUBY) -I`cd $(srcdir)/lib; pwd`'
- RUNRUBY='$(RUNRUBY_COMMAND)'
- XRUBY='$(MINIRUBY)'
- BOOTSTRAPRUBY='$(BASERUBY)'
- TEST_RUNNABLE=no
- CROSS_COMPILING=yes
-
- if test "$host_os" = "nacl"; then
- if test "$build_cpu" = "$host_cpu" || test "${nacl_cv_cpu_nick}" = "x86" -a "$host_cpu" = "i686"; then
- nacl_cv_sel_ldr='`$(MINIRUBY) $(srcdir)/nacl/nacl-config.rb sel_ldr`'
- nacl_cv_irt_core='`$(MINIRUBY) $(srcdir)/nacl/nacl-config.rb irt_core`'
- nacl_cv_runnable_ld='`$(MINIRUBY) $(srcdir)/nacl/nacl-config.rb runnable_ld`'
- nacl_cv_host_lib='`$(MINIRUBY) $(srcdir)/nacl/nacl-config.rb host_lib`'
- TEST_RUNNABLE=yes
- BTESTRUBY="${nacl_cv_sel_ldr} -a -B ${nacl_cv_irt_core} -w 1:3 -w 2:4"
- BTESTRUBY="$BTESTRUBY -- ${nacl_cv_runnable_ld} --library-path ${nacl_cv_host_lib}"
- BTESTRUBY="$BTESTRUBY `pwd`/"'miniruby$(EXEEXT) -I`cd $(srcdir)/lib; pwd` -I.'
- BTESTRUBY="$BTESTRUBY"' -I$(EXTOUT)/common 3>&1 4>&2 1>/dev/null 2>/dev/null '
- fi
- fi
-else
- MINIRUBY='./miniruby$(EXEEXT) -I$(srcdir)/lib -I.'
- MINIRUBY="$MINIRUBY"' -I$(EXTOUT)/common'
- PREP='miniruby$(EXEEXT)'
- RUNRUBY_COMMAND='$(MINIRUBY) $(srcdir)/tool/runruby.rb --extout=$(EXTOUT) $(RUNRUBYOPT)'
- RUNRUBY='$(RUNRUBY_COMMAND) --'
- XRUBY='$(RUNRUBY)'
- BOOTSTRAPRUBY='$(MINIRUBY)'
- TEST_RUNNABLE=yes
- CROSS_COMPILING=no
-fi
-AC_SUBST(TEST_RUNNABLE)
-AC_SUBST(CROSS_COMPILING)
-AC_SUBST(MINIRUBY)
-AC_SUBST(BTESTRUBY)
-AC_SUBST(PREP)
-AC_SUBST(RUNRUBY_COMMAND)
-AC_SUBST(RUNRUBY)
-AC_SUBST(XRUBY)
-AC_SUBST(BOOTSTRAPRUBY)
-AC_SUBST(EXTOUT, [${EXTOUT=.ext}])
-
-FIRSTMAKEFILE=""
-LIBRUBY_A='lib$(RUBY_SO_NAME)-static.a'
-LIBRUBY='$(LIBRUBY_A)'
-LIBRUBYARG_STATIC='-l$(RUBY_SO_NAME)-static'
-LIBRUBYARG='$(LIBRUBYARG_STATIC)'
-SOLIBS=
-
-AS_CASE(["$target_os"],
- [cygwin*|mingw*|haiku*|darwin*], [
- : ${DLDLIBS=""}
- ],
- [
- DLDLIBS="$DLDLIBS -lc"
- ])
-
-AC_ARG_ENABLE(multiarch,
- AS_HELP_STRING([--enable-multiarch], [enable multiarch compatible directories]),
- [multiarch=], [unset multiarch])
-if test ${multiarch+set}; then
- AC_DEFINE(ENABLE_MULTIARCH)
-fi
-
-archlibdir='${libdir}/${arch}'
-sitearchlibdir='${libdir}/${sitearch}'
-archincludedir='${includedir}/${arch}'
-sitearchincludedir='${includedir}/${sitearch}'
-
-AC_ARG_WITH(soname,
- AS_HELP_STRING([--with-soname=SONAME], [base name of shared library]),
- [RUBY_SO_NAME=$withval], [RUBY_SO_NAME='$(RUBY_BASE_NAME)'])
-
-LIBRUBY_LDSHARED=$LDSHARED
-LIBRUBY_DLDFLAGS=$DLDFLAGS
-LIBRUBY_SO='lib$(RUBY_SO_NAME).so.$(RUBY_PROGRAM_VERSION)'
-LIBRUBY_ALIASES='lib$(RUBY_SO_NAME).so'
-ENABLE_SHARED=no
-
-AC_ARG_ENABLE(shared,
- AS_HELP_STRING([--enable-shared], [build a shared library for Ruby]),
- [enable_shared=$enableval])
-libprefix=${multiarch+'$(archlibdir)'}${multiarch-'$(libdir)'}
-LIBRUBY_RELATIVE=${load_relative-no}
-AS_CASE("$enable_shared", [yes], [
- LIBRUBY='$(LIBRUBY_SO)'
- LIBRUBYARG_SHARED='-l$(RUBY_SO_NAME)'
- LIBRUBYARG='$(LIBRUBYARG_SHARED)'
- LIBRUBY_RELATIVE=no
- test -z "$CCDLFLAGS" || CFLAGS="$CFLAGS $CCDLFLAGS"
- ENABLE_SHARED=yes
- if test "$rb_cv_binary_elf" = yes; then
- SOLIBS='$(LIBS)'
- fi
-
- # libdir can be overridden in config.site file (on OpenSUSE at least).
- libdir_basename=lib
- if test "$bindir" = '${exec_prefix}/bin'; then
- AS_CASE(["$libdir"], ['${exec_prefix}/'*], [libdir_basename=`basename "$libdir"`])
- fi
- AC_DEFINE_UNQUOTED(LIBDIR_BASENAME, ["${libdir_basename}"])
- libdir_basename="${libdir_basename}"${multiarch+'/${arch}'}
-
- AS_CASE(["$target_os"],
- [freebsd*|dragonfly*], [],
- [
- if test "$GCC" = yes; then
- RUBY_TRY_LDFLAGS([${linker_flag}--no-undefined], [no_undefined=yes], [no_undefined=no])
- if test "no_undefined" = yes; then
- RUBY_APPEND_OPTION(EXTLDFLAGS, [${linker_flag}--no-undefined])
- fi
- fi
- ])
-
- AS_CASE(["$target_os"],
- [sunos4*], [
- LIBRUBY_ALIASES='lib$(RUBY_SO_NAME).so.$(MAJOR).$(MINOR) lib$(RUBY_SO_NAME).so'
- ],
- [linux* | gnu* | k*bsd*-gnu | atheos* | kopensolaris*-gnu | haiku*], [
- RUBY_APPEND_OPTIONS(LIBRUBY_DLDFLAGS, ['-Wl,-soname,lib$(RUBY_SO_NAME).so.$(MAJOR).$(MINOR)' "$LDFLAGS_OPTDIR"])
- LIBRUBY_ALIASES='lib$(RUBY_SO_NAME).so.$(MAJOR).$(MINOR) lib$(RUBY_SO_NAME).so'
- if test "$load_relative" = yes; then
- libprefix="'\$\${ORIGIN}/../${libdir_basename}'"
- LIBRUBY_RPATHFLAGS="-Wl,-rpath,${libprefix}"
- LIBRUBY_RELATIVE=yes
- fi
- ],
- [freebsd*|dragonfly*], [
- SOLIBS='$(LIBS)'
- LIBRUBY_SO='lib$(RUBY_SO_NAME).so.$(MAJOR)$(MINOR)'
- if test "$rb_cv_binary_elf" != "yes" ; then
- LIBRUBY_SO="$LIBRUBY_SO.\$(TEENY)"
- LIBRUBY_ALIASES=''
- fi
- ],
- [netbsd*], [
- SOLIBS='$(LIBS)'
- LIBRUBY_SO='lib$(RUBY_SO_NAME).so.$(MAJOR)$(MINOR).$(TEENY)'
- RUBY_APPEND_OPTIONS(LIBRUBY_DLDFLAGS, ['-Wl,-soname,lib$(RUBY_SO_NAME).so.$(MAJOR)$(MINOR)' "$LDFLAGS_OPTDIR"])
- if test "$rb_cv_binary_elf" = yes; then # ELF platforms
- LIBRUBY_ALIASES='lib$(RUBY_SO_NAME).so.$(MAJOR)$(MINOR) lib$(RUBY_SO_NAME).so'
- else # a.out platforms
- LIBRUBY_ALIASES=""
- fi
- ],
- [openbsd*|mirbsd*], [
- SOLIBS='$(LIBS)'
- LIBRUBY_SO='lib$(RUBY_SO_NAME).so.$(MAJOR).'`expr ${MINOR} \* 10 + ${TEENY}`
- ],
- [solaris*], [
- SOLIBS='$(LIBS)'
- LIBRUBY_SO='lib$(RUBY_SO_NAME).so.$(MAJOR)'
- LIBRUBY_ALIASES='lib$(RUBY_SO_NAME).so.$(RUBY_PROGRAM_VERSION) lib$(RUBY_SO_NAME).so'
- if test "$GCC" = yes; then
- LIBRUBY_DLDFLAGS="$DLDFLAGS "'-Wl,-h,$(@F)'
- else
- LIBRUBY_DLDFLAGS="$DLDFLAGS "'-h $(@F)'
- fi
- XLDFLAGS="$XLDFLAGS "'-R${libdir}'
- ],
- [hpux*], [
- XLDFLAGS="$XLDFLAGS "'-Wl,+s,+b,$(libdir)'
- LIBRUBY_SO='lib$(RUBY_SO_NAME).sl.$(RUBY_PROGRAM_VERSION)'
- LIBRUBY_ALIASES='lib$(RUBY_SO_NAME).sl.$(MAJOR).$(MINOR) lib$(RUBY_SO_NAME).sl'
- ],
- [aix*], [
- RUBY_APPEND_OPTIONS(LIBRUBY_DLDFLAGS, ["${linker_flag}-bnoentry" "$XLDFLAGS" "$LDFLAGS_OPTDIR"])
- LIBRUBYARG_SHARED='-L${libdir} -l${RUBY_SO_NAME}'
- SOLIBS='-lm -lc'
- ],
- [darwin*], [
- RUBY_SO_NAME="$RUBY_SO_NAME"'.$(RUBY_PROGRAM_VERSION)'
- LIBRUBY_LDSHARED='$(CC) -dynamiclib'
- if test "$load_relative" = yes; then
- libprefix="@executable_path/../${libdir_basename}"
- LIBRUBY_RELATIVE=yes
- fi
- LIBRUBY_DLDFLAGS="$LIBRUBY_DLDFLAGS "'-install_name '${libprefix}'/$(LIBRUBY_SO)'
- LIBRUBY_DLDFLAGS="$LIBRUBY_DLDFLAGS "'-current_version $(MAJOR).$(MINOR)'
- LIBRUBY_DLDFLAGS="$LIBRUBY_DLDFLAGS "'-compatibility_version $(RUBY_PROGRAM_VERSION)'
- if test "$visibility_option" = ld; then
- LIBRUBY_DLDFLAGS="$LIBRUBY_DLDFLAGS "'-Wl,-unexported_symbol,_Init_*'
- LIBRUBY_DLDFLAGS="$LIBRUBY_DLDFLAGS "'-Wl,-unexported_symbol,_ruby_static_id_*'
- LIBRUBY_DLDFLAGS="$LIBRUBY_DLDFLAGS "'-Wl,-unexported_symbol,*_threadptr_*'
- fi
- LIBRUBY_DLDFLAGS="$LIBRUBY_DLDFLAGS "' $(XLDFLAGS)'
- LIBRUBY_SO='lib$(RUBY_SO_NAME).dylib'
- LIBRUBY_ALIASES='lib$(RUBY_BASE_NAME).$(MAJOR).$(MINOR).dylib lib$(RUBY_INSTALL_NAME).dylib'
- SOLIBS='$(LIBS)'
- ],
- [interix*], [
- LIBRUBYARG_SHARED='-L. -L${libdir} -l$(RUBY_SO_NAME)'
- ],
- [mingw*|cygwin*|mswin*], [
- LIBRUBY_RELATIVE=yes
- ])
-], [
- LIBRUBYARG_SHARED=
-
- # enable PIE if possible
- AC_ARG_ENABLE(pie,
- AS_HELP_STRING([--disable-pie], [disable PIE feature]),
- [pie=$enableval], [pie=])
- AS_CASE(["$target_os"],
- [nacl], [
- # -pie implies -shared for NaCl.
- pie=no
- ])
- if test "$GCC" = yes -a -z "$EXTSTATIC" -a "x$pie" != xno; then
- RUBY_TRY_CFLAGS(-fPIE, [pie=yes], [pie=no])
- if test "$pie" = yes; then
- # Use -fPIE when testing -pie. RUBY_TRY_LDFLAGS sets
- # $save_CFLAGS internally, so set other name here.
- save_CFLAGS_before_pie="$CFLAGS"
- CFLAGS="$CFLAGS -fPIE"
-
- # gcc need -pie but clang need -Wl,-pie.
- for pie in -pie -Wl,-pie; do
- RUBY_TRY_LDFLAGS([$pie], [], [pie=])
- if test "x$pie" != x; then
- RUBY_APPEND_OPTION(XCFLAGS, -fPIE)
- RUBY_APPEND_OPTION(XLDFLAGS, $pie)
- break
- fi
- done
- CFLAGS="$save_CFLAGS_before_pie"
- fi
- fi
-])
-if test "$enable_rpath" = yes; then
- test -z "$LIBRUBY_RPATHFLAGS" || LIBRUBY_RPATHFLAGS="$LIBRUBY_RPATHFLAGS "
- rpathflag="${RPATHFLAG}"
- AS_CASE(["${cross_compiling}${load_relative}"], [*yes*], [], [rpathflag="$RPATHFLAG$LIBPATHFLAG"])
- rpathflag=`IFS="$PATH_SEPARATOR"
- echo x "$rpathflag" |
- sed "s/^x *//;s${IFS}"'%1\\$-s'"${IFS}${libprefix}${IFS}g;s${IFS}%s${IFS}${libprefix}${IFS}g"
- `
- LIBRUBY_RPATHFLAGS="$LIBRUBY_RPATHFLAGS${rpathflag}"
- LIBRUBYARG_SHARED="$LIBRUBY_RPATHFLAGS $LIBRUBYARG_SHARED"
- LIBRUBYARG_STATIC="$LIBRUBY_RPATHFLAGS $LIBRUBYARG_STATIC"
-fi
-AC_SUBST(LIBRUBY_RELATIVE)
-
-LDFLAGS="-L. $LDFLAGS"
-AC_SUBST(ARCHFILE)
-
-if test "$EXEEXT" = .exe; then
- EXECUTABLE_EXTS='".exe",".com",".cmd",".bat"'
- AC_DEFINE_UNQUOTED(EXECUTABLE_EXTS, $EXECUTABLE_EXTS)
- EXECUTABLE_EXTS=`echo $EXECUTABLE_EXTS | tr -d '"' | tr , ' '`
- AC_SUBST(EXECUTABLE_EXTS)
-fi
-
-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"' ruby-runner$(EXEEXT)'
- AC_MSG_RESULT(yes)]
- )
-])
-
-AC_ARG_ENABLE(dtrace,
- AS_HELP_STRING([--enable-dtrace],
- [enable DTrace for tracing inside ruby. enabled by default on systems having dtrace]),
- [enable_dtrace=$enableval], [enable_dtrace=auto])
-
-if test "${enable_dtrace}" = "auto"; then
- if test x"$DTRACE" != x -a x"$cross_compiling" != xyes; then
- RUBY_DTRACE_AVAILABLE()
- enable_dtrace=$rb_cv_dtrace_available
- else
- enable_dtrace=no
- fi
-fi
-
-LIBRUBY_A_OBJS='$(OBJS)'
-DTRACE_REBUILD=
-if test "${enable_dtrace}" = "yes"; then
- if test -z "$DTRACE"; then
- AC_MSG_ERROR([dtrace(1) is missing])
- elif test "$cross_compiling" = yes; then
- AC_MSG_ERROR([--enable-dtrace, however, cross compiling])
- else
- RUBY_DTRACE_AVAILABLE()
- enable_dtrace=$rb_cv_dtrace_available
- if test "${enable_dtrace}" = "no"; then
- AC_MSG_ERROR([--enable-dtrace, however, USDT is not available])
- fi
- RUBY_DTRACE_POSTPROCESS()
- if test "$rb_cv_prog_dtrace_g" != 'no'; then
- DTRACE_OBJ='probes.$(OBJEXT)'
- fi
- if test "$rb_cv_prog_dtrace_g" = 'rebuild'; then
- DTRACE_REBUILD=yes
- LIBRUBY_A_OBJS='$(DTRACE_GLOMMED_OBJ)'
- fi
- AS_CASE("${target_os}", [freebsd*], [
- # FreeBSD's dtrace requires libelf
- LIBS="-lelf $LIBS"
- ])
- fi
- DTRACE_EXT=d
-else
- DTRACE_EXT=dmyh
-fi
-AC_SUBST(DTRACE_EXT)
-AC_SUBST(DTRACE_OBJ)
-AC_SUBST(DTRACE_REBUILD)
-AC_SUBST(LIBRUBY_A_OBJS)
-
-RUBY_SETJMP_TYPE
-}
-{ # build section
-
-dnl build rdoc index if requested
-RDOCTARGET=""
-CAPITARGET=""
-AC_ARG_ENABLE(install-doc,
- AS_HELP_STRING([--disable-install-doc], [do not install either rdoc indexes or C API documents during install]),
- [install_doc=$enableval], [install_doc=yes])
-AC_ARG_ENABLE(install-rdoc,
- AS_HELP_STRING([--disable-install-rdoc], [do not install rdoc indexes during install]),
- [install_rdoc=$enableval], [install_rdoc=yes])
-AC_ARG_ENABLE(install-capi,
- AS_HELP_STRING([--disable-install-capi], [do not install C API documents during install]),
- [install_capi=$enableval], [install_capi=no])
-
-if test "$install_doc" != no; then
- if test "$install_rdoc" != no; then
- RDOCTARGET="rdoc"
- else
- RDOCTARGET="nodoc"
- fi
- if test "$install_capi" != no -a -n "$DOXYGEN"; then
- CAPITARGET="capi"
- else
- CAPITARGET="nodoc"
- fi
-else
- RDOCTARGET="nodoc"
- CAPITARGET="nodoc"
-fi
-
-AC_SUBST(RDOCTARGET)
-AC_SUBST(CAPITARGET)
-
-AS_CASE(["$RDOCTARGET:$CAPITARGET"],[nodoc:nodoc],[INSTALLDOC=nodoc],[INSTALLDOC=all])
-AC_SUBST(INSTALLDOC)
-
-AC_ARG_ENABLE(install-static-library,
- AS_HELP_STRING([--disable-install-static-library], [do not install static ruby library]),
- [INSTALL_STATIC_LIBRARY=$enableval],
- AS_IF([test x"$enable_shared" = xyes],
- [INSTALL_STATIC_LIBRARY=no],
- [INSTALL_STATIC_LIBRARY=yes]))
-AC_SUBST(INSTALL_STATIC_LIBRARY)
-
-if test "$rb_with_pthread" = "yes"; then
- THREAD_MODEL=pthread
-fi
-AC_CACHE_CHECK([for prefix of external symbols], rb_cv_symbol_prefix, [
- AC_TRY_COMPILE([extern void conftest_external(void) {}], [], [
- rb_cv_symbol_prefix=`$NM conftest.$ac_objext |
- sed -n ['/.*T[ ]\([^ ]*\)conftest_external.*/!d;s//\1/p;q']`
- ],
- [rb_cv_symbol_prefix=''])
- test -n "$rb_cv_symbol_prefix" || rb_cv_symbol_prefix=NONE
-])
-SYMBOL_PREFIX="$rb_cv_symbol_prefix"
-test "x$SYMBOL_PREFIX" = xNONE && SYMBOL_PREFIX=''
-DLNOBJ=dln.o
-AC_ARG_ENABLE(dln,
- AC_HELP_STRING([--disable-dln], [disable dynamic link feature]),
- [test "$enableval" = yes || DLNOBJ=dmydln.o])
-AC_SUBST(DLNOBJ)
-MINIDLNOBJ=dmydln.o
-
-AS_CASE(["$target_os"],
- [linux*], [
- ],
- [netbsd*], [
- RUBY_APPEND_OPTION(CFLAGS, -pipe)
- ],
- [darwin*], [
- RUBY_APPEND_OPTION(CFLAGS, -pipe)
- RUBY_APPEND_OPTION(XLDFLAGS, [-framework CoreFoundation])
- RUBY_APPEND_OPTION(LIBRUBYARG_STATIC, [-framework CoreFoundation])
- ],
- [osf*], [
- if test "$GCC" != "yes" ; then
- # compile something small: taint.c is fine for this.
- # the main point is the '-v' flag of 'cc'.
- AS_CASE(["`cc -v -I. -c main.c -o /tmp/main.o 2>&1`"],
- [*/gemc_cc*], [ # we have the new DEC GEM CC
- CFLAGS="$CFLAGS -oldc"
- ],
- [ # we have the old MIPS CC
- ])
- # cleanup
- rm -f /tmp/main.o
- CFLAGS="$CFLAGS -std"
- fi
- ],
- [cygwin*|mingw*], [
- RUBY_SO_NAME="${RUBY_SO_NAME}"'$(MAJOR)$(MINOR)0'
- LIBRUBY_DLDFLAGS="${DLDFLAGS}"' -Wl,--out-implib=$(LIBRUBY)'
- AS_CASE(["$target_os"],
- [cygwin*], [
- if test x"$enable_shared" = xyes; then
- LIBRUBY_SO='cyg$(RUBY_SO_NAME)'.dll
- LIBRUBY_DLDFLAGS="${LIBRUBY_DLDFLAGS}"' $(RUBYDEF)'
- fi
- ],
- [mingw*], [
- RUBY_SO_NAME="${rb_cv_msvcrt}-${RUBY_SO_NAME}"
- if test x"${target_cpu}" != xi386; then
- RUBY_SO_NAME="${target_cpu}-${RUBY_SO_NAME}"
- fi
- if test x"$enable_shared" = xyes; then
- LIBRUBY_SO='$(RUBY_SO_NAME)'.dll
- LIBRUBY_DLDFLAGS="${LIBRUBY_DLDFLAGS}"' $(RUBYDEF)'
- fi
- EXPORT_PREFIX=' '
- DLDFLAGS="${DLDFLAGS}"' $(DEFFILE)'
- AC_LIBOBJ([win32/win32])
- AC_LIBOBJ([win32/file])
- COMMON_LIBS=m
-# COMMON_MACROS="WIN32_LEAN_AND_MEAN="
- COMMON_HEADERS="winsock2.h windows.h"
- THREAD_MODEL=win32
- PLATFORM_DIR=win32
- ])
- LIBRUBY_ALIASES=''
- FIRSTMAKEFILE=GNUmakefile:cygwin/GNUmakefile.in
- SOLIBS='$(LIBS)'
- if test x"$enable_shared" = xyes; then
- LIBRUBY='lib$(RUBY_SO_NAME).dll.a'
- else
- LIBRUBY_SO=dummy
- LIBRUBY='lib$(RUBY_SO_NAME).a'
- LIBRUBYARG='-l$(RUBY_SO_NAME)'
- fi
- ],
- [hpux*], [
- AS_CASE(["$YACC"],[*yacc*], [
- XCFLAGS="$XCFLAGS -DYYMAXDEPTH=300"
- YACC="$YACC -Nl40000 -Nm40000"
- ])],
- [nacl], [
- FIRSTMAKEFILE=GNUmakefile:nacl/GNUmakefile.in
- ])
-
-AS_CASE(["$with_gmp: $SOLIBS "], [no:* | *' -lgmp '*|*' $(LIBS) '*], [],
- [SOLIBS="-lgmp $SOLIBS"])
-
-AS_CASE(["$with_jemalloc: $LIBS "], [no:* | *' -ljemalloc '*], [],
- [LIBS="-ljemalloc $LIBS"])
-
-MINIOBJS="$MINIDLNOBJ"
-
-AS_CASE(["$THREAD_MODEL"],
-[pthread], [AC_CHECK_HEADERS(pthread.h)],
-[win32], [],
-[""], [AC_MSG_ERROR(thread model is missing)],
- [AC_MSG_ERROR(unknown thread model $THREAD_MODEL)])
-
-AC_ARG_ENABLE(debug-env,
- AS_HELP_STRING([--enable-debug-env], [enable RUBY_DEBUG environment variable]),
- [AC_DEFINE(RUBY_DEBUG_ENV)])
-
-AS_CASE(["$FIRSTMAKEFILE"], [*GNUmakefile:*], [gnumake=yes], [
- AC_MSG_CHECKING([if ${MAKE-make} is GNU make])
- mkdir conftest.dir
- echo "all:; @echo yes" > conftest.dir/GNUmakefile
- echo "all:; @echo no" > conftest.dir/Makefile
- gnumake=`(cd conftest.dir; ${MAKE-make})`
- rm -fr conftest.dir
- AS_CASE(["$gnumake"],
- [*yes*], [
- FIRSTMAKEFILE=GNUmakefile:template/GNUmakefile.in
- gnumake=yes],
- [
- gnumake=no])
- AC_MSG_RESULT($gnumake)
-])
-AS_IF([test "$gnumake" = yes], [ NULLCMD=: ], [
- AC_MSG_CHECKING([for safe null command for ${MAKE-make}])
- mkdir conftest.dir
- NULLCMD=
- for cmd in : true; do
- echo 'A=1' > conftest.dir/Makefile
- echo 'B=$(A:1=@'$cmd')' >> conftest.dir/Makefile
- echo 'all:; $B 1 2 3 4 5 6 7 8 9' >> conftest.dir/Makefile
- if (cd conftest.dir; ${MAKE-make} >/dev/null 2>/dev/null); then
- NULLCMD=$cmd
- break
- fi
- done
- rm -fr conftest.dir
- if test -z "$NULLCMD"; then
- AC_MSG_ERROR(no candidate for safe null command)
- fi
- AC_MSG_RESULT($NULLCMD)
-])
-AC_SUBST(NULLCMD)
-
-if test "${universal_binary-no}" = yes ; then
- AC_CACHE_CHECK([for architecture macros], rb_cv_architecture_macros, [
- mv confdefs.h confdefs1.h
- : > confdefs.h
- AC_TRY_COMPILE([@%:@if defined __`echo ${universal_archnames} |
- sed 's/=[^ ]*//g;s/ /__ || defined __/g'`__
-@%:@else
-@%:@error
->>>>>><<<<<<
-@%:@endif], [],
-[
- rb_cv_architecture_macros=yes
- mv -f confdefs1.h confdefs.h
-], [
- rb_cv_architecture_macros=no
- archflagpat=`eval echo '"'"${ARCH_FLAG}"'"' | sed 's/[[][|.*]]/\\&/g'`
- new_cflags=`echo "$CFLAGS" | sed "s|$archflagpat"'||'`
- for archs in ${universal_archnames}; do
- cpu=${archs@%:@*=}
- archs=${archs%=*}
- CFLAGS="$new_cflags -arch $archs"
- archs="__${archs}__"
- AC_MSG_CHECKING([for macro ${archs} on ${cpu}])
- AC_TRY_COMPILE([@%:@ifndef ${archs}
-@%:@error
-@%:@endif], [], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])])
- done
- mv -f confdefs1.h confdefs.h
- AC_MSG_ERROR([failed])
- ])])
- AC_CACHE_CHECK(whether __ARCHITECTURE__ is available, rb_cv_architecture_available,
- AC_TRY_COMPILE([@%:@include <stdio.h>
- const char arch[[]] = __ARCHITECTURE__;], [puts(arch);],
- [rb_cv_architecture_available=yes], [rb_cv_architecture_available=no]))
-fi
-
-CPPFLAGS="$CPPFLAGS "'$(DEFS)'
-test -z "$CPPFLAGS" || CPPFLAGS="$CPPFLAGS "; CPPFLAGS="$CPPFLAGS"'${cppflags}'
-if test -n "${cflags+set}"; then
- cflagspat=`eval echo '"'"${cflags}"'"' | sed 's/[[][|.*]]/\\&/g;s/^ */ /;s/^ *$/ /'`
- CFLAGS=`echo " $CFLAGS " | sed "s|$cflagspat"'|${cflags}|;s/^ *//;s/ *$//'`
-fi
-if test -n "${cxxflags+set}"; then
- cxxflagspat=`eval echo '"'"${cxxflags}"'"' | sed 's/[[][|.*]]/\\&/g;s/^ */ /;s/^ *$/ /'`
- CXXFLAGS=`echo " $CXXFLAGS " | sed "s|$cxxflagspat"'|${cxxflags}|;s/^ *//;s/ *$//'`
-fi
-if test "${ARCH_FLAG}"; then
- archflagpat=`eval echo '"'"${ARCH_FLAG}"'"' | sed 's/[[][|.*]]/\\&/g'`
- CFLAGS=`echo "$CFLAGS" | sed "s| *$archflagpat"'||'`
- CXXFLAGS=`echo "$CXXFLAGS" | sed "s| *$archflagpat"'||'`
- LDFLAGS=`echo "$LDFLAGS" | sed "s| *$archflagpat"'||'`
-fi
-warnflags="$rb_cv_warnflags"
-AC_SUBST(cppflags)dnl
-AC_SUBST(cflags, ["${orig_cflags:+$orig_cflags }"'${optflags} ${debugflags} ${warnflags}'])dnl
-AC_SUBST(cxxflags, ["${orig_cxxflags:+$orig_cxxflags }"'${optflags} ${debugflags} ${warnflags}'])dnl
-AC_SUBST(optflags)dnl
-AC_SUBST(debugflags)dnl
-AC_SUBST(warnflags)dnl
-AC_SUBST(strict_warnflags)dnl
-AC_SUBST(XCFLAGS)dnl
-AC_SUBST(XLDFLAGS)dnl
-AC_SUBST(EXTLDFLAGS)dnl
-AC_SUBST(EXTDLDFLAGS)dnl
-AC_SUBST(LIBRUBY_LDSHARED)
-AC_SUBST(LIBRUBY_DLDFLAGS)
-AC_SUBST(RUBY_INSTALL_NAME)
-AC_SUBST(rubyw_install_name)
-AC_SUBST(RUBYW_INSTALL_NAME)
-AC_SUBST(RUBY_SO_NAME)
-AC_SUBST(LIBRUBY_A)
-AC_SUBST(LIBRUBY_SO)
-AC_SUBST(LIBRUBY_ALIASES)
-AC_SUBST(LIBRUBY)
-AC_SUBST(LIBRUBYARG)
-AC_SUBST(LIBRUBYARG_STATIC)
-AC_SUBST(LIBRUBYARG_SHARED)
-AC_SUBST(SOLIBS)
-AC_SUBST(DLDLIBS)
-AC_SUBST(ENABLE_SHARED)
-AC_SUBST(MAINLIBS)
-AC_SUBST(COMMON_LIBS)
-AC_SUBST(COMMON_MACROS)
-AC_SUBST(COMMON_HEADERS)
-AC_SUBST(EXPORT_PREFIX)
-AC_SUBST(SYMBOL_PREFIX)
-AC_SUBST(MINIOBJS)
-AC_SUBST(THREAD_MODEL)
-AC_SUBST(PLATFORM_DIR)
-
-firstmf=`echo $FIRSTMAKEFILE | sed 's/:.*//'`
-firsttmpl=`echo $FIRSTMAKEFILE | sed 's/.*://'`
-MAKEFILES="Makefile $firstmf"
-MAKEFILES="`echo $MAKEFILES`"
-AC_SUBST(MAKEFILES)
-
-ri_prefix=
-test "$program_prefix" != NONE &&
- ri_prefix=$program_prefix
-
-ri_suffix=
-test "$program_suffix" != NONE &&
- ri_suffix=$program_suffix
-
-RUBY_INSTALL_NAME="${ri_prefix}"'$(RUBY_BASE_NAME)'"${ri_suffix}"
-AS_CASE(["$target_os"],
- [cygwin*|mingw*], [
- RUBYW_INSTALL_NAME="${ri_prefix}"'$(RUBYW_BASE_NAME)'"${ri_suffix}"
- rubyw_install_name='$(RUBYW_INSTALL_NAME)'
- ])
-
-rubylibdir='${rubylibprefix}/${ruby_version}'
-rubyarchdir=${multiarch+'${rubyarchprefix}/${ruby_version}'}${multiarch-'${rubylibdir}/${arch}'}
-
-rubyarchprefix=${multiarch+'${archlibdir}/${RUBY_BASE_NAME}'}${multiarch-'${rubylibprefix}/${arch}'}
-AC_ARG_WITH(rubyarchprefix,
- AS_HELP_STRING([--with-rubyarchprefix=DIR],
- [prefix for architecture dependent ruby libraries [[RUBYLIBPREFIX/ARCH]]]),
- [rubyarchprefix="$withval"])
-AC_SUBST(rubyarchprefix)
-
-rubysitearchprefix=${multiarch+'${sitearchlibdir}/${RUBY_BASE_NAME}'}${multiarch-'${rubylibprefix}/${sitearch}'}
-AC_ARG_WITH(rubysitearchprefix,
- AS_HELP_STRING([--with-rubysitearchprefix=DIR],
- [prefix for architecture dependent site libraries [[RUBYLIBPREFIX/SITEARCH]]]),
- [rubysitearchprefix="$withval"])
-AC_SUBST(rubysitearchprefix)
-
-RI_BASE_NAME=`echo ${RUBY_BASE_NAME} | sed 's/ruby/ri/'`
-ridir='${datarootdir}/${RI_BASE_NAME}'
-AC_ARG_WITH(ridir,
- AS_HELP_STRING([--with-ridir=DIR], [ri documentation [[DATAROOTDIR/ri]]]),
- [ridir=$withval])
-AC_SUBST(ridir)
-AC_SUBST(RI_BASE_NAME)
-
-AC_ARG_WITH(ruby-version,
- AS_HELP_STRING([--with-ruby-version=STR], [ruby version string for version specific directories [[full]] (full|minor|STR)]),
- [ruby_version=$withval],
- [ruby_version=full])
-unset RUBY_LIB_VERSION
-unset RUBY_LIB_VERSION_STYLE
-AS_CASE(["$ruby_version"],
- [full], [RUBY_LIB_VERSION_STYLE='3 /* full */'],
- [minor], [RUBY_LIB_VERSION_STYLE='2 /* minor */'])
-if test ${RUBY_LIB_VERSION_STYLE+set}; then
- {
- echo "#define RUBY_LIB_VERSION_STYLE $RUBY_LIB_VERSION_STYLE"
- echo '#define STRINGIZE(x) x'
- test -f revision.h -o -f "${srcdir}/revision.h" || echo '#define RUBY_REVISION 0'
- echo '#include "version.h"'
- echo 'ruby_version=RUBY_LIB_VERSION'
- } > conftest.c
- ruby_version="`$CPP -I. -I"${srcdir}" -I"${srcdir}/include" conftest.c | sed '/^ruby_version=/!d;s/ //g'`"
- eval $ruby_version
-elif test -z "${ruby_version}"; then
- AC_MSG_ERROR([No ruby version, No place for bundled libraries])
-else
- RUBY_LIB_VERSION="${ruby_version}"
-fi
-AC_SUBST(RUBY_LIB_VERSION_STYLE)
-AC_SUBST(RUBY_LIB_VERSION)
-
-AC_ARG_WITH(sitedir,
- AS_HELP_STRING([--with-sitedir=DIR], [site libraries in DIR [[RUBY_LIB_PREFIX/site_ruby]], "no" to disable site directory]),
- [sitedir=$withval],
- [sitedir='${rubylibprefix}/site_ruby'])
-sitelibdir='${sitedir}/${ruby_version}'
-
-AC_ARG_WITH(sitearchdir,
- AS_HELP_STRING([--with-sitearchdir=DIR],
- [architecture dependent site libraries in DIR [[SITEDIR/SITEARCH]], "no" to disable site directory]),
- [sitearchdir=$withval],
- [sitearchdir=${multiarch+'${rubysitearchprefix}/site_ruby/${ruby_version}'}${multiarch-'${sitelibdir}/${sitearch}'}])
-
-AC_ARG_WITH(vendordir,
- AS_HELP_STRING([--with-vendordir=DIR], [vendor libraries in DIR [[RUBY_LIB_PREFIX/vendor_ruby]], "no" to disable vendor directory]),
- [vendordir=$withval],
- [vendordir='${rubylibprefix}/vendor_ruby'])
-vendorlibdir='${vendordir}/${ruby_version}'
-
-AC_ARG_WITH(vendorarchdir,
- AS_HELP_STRING([--with-vendorarchdir=DIR],
- [architecture dependent vendor libraries in DIR [[VENDORDIR/SITEARCH]], "no" to disable vendor directory]),
- [vendorarchdir=$withval],
- [vendorarchdir=${multiarch+'${rubysitearchprefix}/vendor_ruby/${ruby_version}'}${multiarch-'${vendorlibdir}/${sitearch}'}])
-
-if test "${LOAD_RELATIVE+set}"; then
- AC_DEFINE_UNQUOTED(LOAD_RELATIVE, $LOAD_RELATIVE)
- RUBY_EXEC_PREFIX=''
-fi
-
-AC_SUBST(RUBY_EXEC_PREFIX)
-
-AC_SUBST(libdirname, ${multiarch+arch}libdir)
-AC_SUBST(archlibdir)dnl
-AC_SUBST(sitearchlibdir)dnl
-AC_SUBST(archincludedir)dnl
-AC_SUBST(sitearchincludedir)dnl
-AC_SUBST(arch)dnl
-AC_SUBST(sitearch)dnl
-AC_SUBST(ruby_version)dnl
-AC_SUBST(rubylibdir)dnl
-AC_SUBST(rubyarchdir)dnl
-AC_SUBST(sitedir)dnl
-AC_SUBST(sitelibdir)dnl
-AC_SUBST(sitearchdir)dnl
-AC_SUBST(vendordir)dnl
-AC_SUBST(vendorlibdir)dnl
-AC_SUBST(vendorarchdir)dnl
-
-AC_SUBST(CONFIGURE, "`echo $0 | sed 's|.*/||'`")dnl
-AC_SUBST(configure_args, "`echo "${ac_configure_args}" | sed 's/\\$/$$/g'`")dnl
-
-if test "${universal_binary-no}" = yes ; then
- arch="universal-${target_os}"
- if test "${rb_cv_architecture_available}" = yes; then
- AC_DEFINE_UNQUOTED(RUBY_PLATFORM_CPU, __ARCHITECTURE__)
- else
- for archs in ${universal_archnames}; do
- cpu=`echo $archs | sed 's/.*=//'`
- archs=`echo $archs | sed 's/=.*//'`
- RUBY_DEFINE_IF([defined __${archs}__], RUBY_PLATFORM_CPU, ["${cpu}"])
- done
- fi
- ints='long int short'
- test "$ac_cv_type_long_long" = yes && ints="'long long' $ints"
- AC_SUBST(UNIVERSAL_ARCHNAMES, "${universal_archnames}")
- AC_SUBST(UNIVERSAL_INTS, "${ints}")
- AC_DEFINE_UNQUOTED(RUBY_PLATFORM_OS, "${target_os}")
- AC_DEFINE_UNQUOTED(RUBY_ARCH, "universal-"RUBY_PLATFORM_OS)
- AC_DEFINE_UNQUOTED(RUBY_PLATFORM, "universal."RUBY_PLATFORM_CPU"-"RUBY_PLATFORM_OS)
-else
- arch="${target_cpu}-${target_os}"
- AS_CASE(["$arch"], [le32-nacl], [arch="pnacl"])
- AC_DEFINE_UNQUOTED(RUBY_PLATFORM, "$arch")
-fi
-
-unset sitearch
-AS_CASE(["$target_os"],[mingw*],[sitearch="$target_cpu-$rb_cv_msvcrt"])
-: ${sitearch='${arch}'}
-
-AC_ARG_WITH(search-path,
- AS_HELP_STRING([--with-search-path=DIR], [specify the additional search path]),
- [search_path=$withval])
-if test "$search_path" != ""; then
- AC_SUBST(RUBY_SEARCH_PATH, $search_path)
-fi
-
-AC_ARG_WITH(rubyhdrdir,
- AS_HELP_STRING([--with-rubyhdrdir=DIR], [core headers in DIR [[INCLUDEDIR/RUBY_BASE_NAME-RUBY_VERSION]]]),
- [rubyhdrdir=$withval],
- [rubyhdrdir='${includedir}/${RUBY_VERSION_NAME}'])
-
-AC_ARG_WITH(rubyarchhdrdir,
- AS_HELP_STRING([--with-rubyarchhdrdir=DIR],
- [architecture dependent core headers in DIR [[$(rubyhdrdir)/$(arch)]]]),
- [rubyarchhdrdir=$withval],
- [rubyarchhdrdir=${multiarch+'${archincludedir}/${RUBY_VERSION_NAME}'}${multiarch-'${rubyhdrdir}/${arch}'}])
-
-AC_ARG_WITH(sitehdrdir,
- AS_HELP_STRING([--with-sitehdrdir=DIR], [core site headers in DIR [[RUBYHDRDIR/site_ruby]]]),
- [sitehdrdir=$withval],
- [sitehdrdir='${rubyhdrdir}/site_ruby'])
-
-AC_ARG_WITH(sitearchhdrdir,
- AS_HELP_STRING([--with-sitearchhdrdir=DIR],
- [architecture dependent core site headers in DIR [[RUBYHDRDIR/site_ruby]]]),
- [sitearchhdrdir=$withval],
- [sitearchhdrdir=${multiarch+'${sitearchincludedir}/${RUBY_VERSION_NAME}/site_ruby'}${multiarch-'${sitehdrdir}/${sitearch}'}])
-
-AC_ARG_WITH(vendorhdrdir,
- AS_HELP_STRING([--with-vendorhdrdir=DIR], [core vendor headers in DIR [[RUBYHDRDIR/vendor_ruby]]]),
- [vendorhdrdir=$withval],
- [vendorhdrdir='${rubyhdrdir}/vendor_ruby'])
-
-AC_ARG_WITH(vendorarchhdrdir,
- AS_HELP_STRING([--with-vendorarchhdrdir=DIR],
- [architecture dependent core vendor headers in DIR [[RUBYHDRDIR/vendor_ruby]]]),
- [vendorarchhdrdir=$withval],
- [vendorarchhdrdir=${multiarch+'${sitearchincludedir}/${RUBY_VERSION_NAME}/vendor_ruby'}${multiarch-'${vendorhdrdir}/${sitearch}'}])
-
-AC_SUBST(rubyhdrdir)dnl
-AC_SUBST(sitehdrdir)dnl
-AC_SUBST(vendorhdrdir)dnl
-AC_SUBST(rubyarchhdrdir)dnl
-AC_SUBST(sitearchhdrdir)dnl
-AC_SUBST(vendorarchhdrdir)dnl
-
-AC_ARG_WITH(mantype,
- AS_HELP_STRING([--with-mantype=TYPE], [specify man page type; TYPE is one of man and doc]),
- [
- AS_CASE(["$withval"],
- [man|man.gz|man.bz2|doc|doc.gz|doc.bz2], [MANTYPE=$withval],
- [AC_MSG_ERROR(invalid man type: $withval)])
- ])
-if test -z "$MANTYPE"; then
- AC_PATH_PROGS(NROFF, nroff awf, /bin/false, "/usr/bin:/usr/ucb")
- if ${NROFF} -mdoc ${srcdir}/man/ruby.1 >/dev/null 2>&1; then
- MANTYPE=doc
- else
- MANTYPE=man
- fi
-fi
-AC_SUBST(MANTYPE)
-
-AC_ARG_ENABLE(rubygems,
- AS_HELP_STRING([--disable-rubygems], [disable rubygems by default]),
- [enable_rubygems="$enableval"], [enable_rubygems=yes])
-if test x"$enable_rubygems" = xno; then
- AC_DEFINE(DISABLE_RUBYGEMS, 1)
- USE_RUBYGEMS=NO
-else
- USE_RUBYGEMS=YES
-fi
-AC_SUBST(USE_RUBYGEMS)
-
-arch_hdrdir="${EXTOUT}/include/${arch}/ruby"
-AS_MKDIR_P("${arch_hdrdir}")
-config_h="${arch_hdrdir}/config.h"
-guard=INCLUDE_RUBY_CONFIG_H
-{
- echo "#ifndef $guard"
- echo "#define $guard 1"
- grep -v "^#define PACKAGE_" confdefs.h
- echo "#endif /* $guard */"
-} | tr -d '\015' |
-(
- if test "x$CONFIGURE_TTY" = xyes; then color=--color; else color=; fi
- exec ${srcdir}/tool/ifchange $color "${config_h}" -
-) || AC_MSG_ERROR([failed to create ${config_h}])
-tr -d '\015' < largefile.h > confdefs.h
-rm largefile.h
-
-BUILTIN_ENCS=["`sed -n -e '/^BUILTIN_ENCS[ ]*=/{' \
- -e s/// -e :l -e '/\\\\$/N' -e 's/\\\\\\n/ /' -e 't l' -e p \
- -e '}' "${srcdir}/enc/Makefile.in"`"]
-BUILTIN_ENCOBJS=
-for e in $BUILTIN_ENCS; do BUILTIN_ENCOBJS="$BUILTIN_ENCOBJS "`echo $e | sed 's/\.c$/.$(OBJEXT)/'`; done
-AC_SUBST(BUILTIN_ENCOBJS)
-
-BUILTIN_TRANSES=["`sed -n -e '/^BUILTIN_TRANSES[ ]*=/{' \
- -e s/// -e :l -e '/\\\\$/N' -e 's/\\\\\\n/ /' -e 't l' -e p \
- -e '}' "${srcdir}/enc/Makefile.in"`"]
-BUILTIN_TRANSSRCS=
-BUILTIN_TRANSOBJS=
-for e in $BUILTIN_TRANSES; do
- BUILTIN_TRANSSRCS="$BUILTIN_TRANSSRCS "`echo $e | sed 's/\.trans$/.c/'`
- BUILTIN_TRANSOBJS="$BUILTIN_TRANSOBJS "`echo $e | sed 's/\.trans$/.$(OBJEXT)/'`
-done
-AC_SUBST(BUILTIN_TRANSSRCS)
-AC_SUBST(BUILTIN_TRANSOBJS)
-
-PACKAGE=$RUBY_BASE_NAME
-AC_SUBST(PACKAGE)
-AS_MESSAGE([$PACKAGE library version = $ruby_version])
-
-AS_CASE([" $CPP "], [*" $CC "*], [CPP=`echo " $CPP " | sed "s| $CC |"' $(CC) |;s/^ *//;s/ *$//'`])
-
-if test x"$firstmf" != x; then
- AC_CONFIG_FILES($firstmf:$firsttmpl, [], [firstmf="$firstmf" firsttmpl="$firsttmpl"])
-fi
-AC_CONFIG_FILES(Makefile, [
- tmpmk=confmk$$.tmp
- {
- if test ${VCS+set}; then
- :
- elif svn info "$srcdir" > /dev/null 2>&1; then
- VCS='svn'
- elif git_dir=`git --work-tree="$srcdir" --git-dir="$srcdir/.git" rev-parse --git-dir 2>/dev/null`; then
- if test -d "$git_dir/svn"; then
- VCS='git svn'
- else
- VCS='git'
- fi
- else
- VCS='echo cannot'
- fi
- AS_CASE("$VCS",
- [svn], [VCSUP='$(VCS) up $(SVNUPOPTIONS)'],
- ["git svn"], [VCSUP='$(VCS) rebase $(GITSVNREBASEOPTIONS)'],
- [git], [VCSUP='$(VCS) pull $(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"
- sed '/^MISSING/s/\$U\././g;/^VCS *=/s#@VCS@#'"$VCS"'#;/^VCSUP *=/s#@VCSUP@#'"$VCSUP"'#' Makefile
- echo; test x"$EXEEXT" = x || echo 'miniruby: miniruby$(EXEEXT)'
- if test "$gnumake" != yes; then
- echo ['$(MKFILES): $(srcdir)/common.mk']
- sed ['s/{\$([^(){}]*)[^{}]*}//g'] ${srcdir}/common.mk
- else
- echo 'distclean-local::; @$(RM) GNUmakefile uncommon.mk'
- fi
- } > $tmpmk && if ! grep '^ruby:' $tmpmk > /dev/null; then
- if test "${gnumake}" = yes; then
- tmpgmk=confgmk$$.tmp
- {
- echo "include $tmpmk"
- echo "-include uncommon.mk"
- } > $tmpgmk
- else
- tmpgmk=$tmpmk
- fi &&
- test -z "`${MAKE-make} -f $tmpgmk info-program | grep '^PROGRAM=ruby$'`" &&
- echo 'ruby: $(PROGRAM);' >> $tmpmk
- test "$tmpmk" = "$tmpgmk" || rm -f "$tmpgmk"
- fi && mv -f $tmpmk Makefile],
-[EXEEXT='$EXEEXT' gnumake='$gnumake'])
-
-AC_ARG_WITH([ruby-pc],
- AC_HELP_STRING([--with-ruby-pc=FILENAME], [pc file basename]),
- [ruby_pc="$withval"],
- [ruby_pc="${RUBY_BASE_NAME}-${MAJOR}.${MINOR}.pc"])
-AC_SUBST(ruby_pc)
-AC_SUBST(exec, [exec])
-
-AC_ARG_WITH(destdir,
- AS_HELP_STRING([--with-destdir=DESTDIR], [specify default directory to install]),
- [DESTDIR="$withval"])
-AC_SUBST(DESTDIR)
-
-AC_CONFIG_FILES($ruby_pc:template/ruby.pc.in,
- [
- if sed ['s/\$(\([A-Za-z_][A-Za-z0-9_]*\))/${\1}/g;s/@[A-Za-z_][A-Za-z0-9_]*@//'] $ruby_pc > ruby.tmp.pc &&
- {
- test -z "$PKG_CONFIG" ||
- PKG_CONFIG_PATH=. $PKG_CONFIG --print-errors ruby.tmp
- }
- then
- mv -f ruby.tmp.pc $ruby_pc
- else
- exit 1
- fi
- ],
- [ruby_pc='$ruby_pc' PKG_CONFIG='$PKG_CONFIG'])
-
-AC_OUTPUT
-}
-}
-
-AS_IF([${FOLD+:} false], [], [
-AS_IF([test "`echo abcdefg hijklmno | fold -s -w10 | sed 1d`" = hijklmno], [FOLD="fold"], [FOLD=])
-])
-AS_REQUIRE_SHELL_FN([config_summary],
- [AS_FUNCTION_DESCRIBE([config_summary], [NAME, VAL], [configuration summary])],
- [AS_IF([test -z "$2"], [], [
- AS_ECHO_N([" * $1: "]) | head -c26
- AS_IF([test "$FOLD"], [
- echo "$2" | fold -s -w50 |
- sed '1!s/^/ /;$!s/$/\\/'
- ], [echo "$2"])
- ])]
-)
-
-echo "---"
-echo "Configuration summary for $RUBY_BASE_NAME version $RUBY_PROGRAM_VERSION"
-echo ""
-config_summary "Installation prefix" "$prefix"
-config_summary "exec prefix" "$exec_prefix"
-config_summary "arch" "$arch"
-config_summary "site arch" "$sitearch"
-config_summary "RUBY_BASE_NAME" "$RUBY_BASE_NAME"
-config_summary "enable shared" "$enable_shared"
-config_summary "ruby lib prefix" "$rubylibprefix"
-config_summary "site libraries path" "$rubysitearchprefix"
-config_summary "vendor path" "$vendordir"
-config_summary "target OS" "$target_os"
-config_summary "compiler" "$CC"
-config_summary "with pthread" "$enable_pthread"
-config_summary "enable shared libs" "$ENABLE_SHARED"
-config_summary "dynamic library ext" "$DLEXT"
-config_summary "CFLAGS" "$cflags"
-config_summary "CPPFLAGS" "$cppflags"
-config_summary "LDFLAGS" "$LDFLAGS"
-config_summary "optflags" "$optflags"
-config_summary "debugflags" "$debugflags"
-config_summary "warnflags" "$warnflags"
-config_summary "strip command" "$STRIP"
-config_summary "install doc" "$install_doc"
-config_summary "man page type" "$MANTYPE"
-config_summary "search path" "$search_path"
-config_summary "static-linked-ext" ${EXTSTATIC:+"yes"}
-echo ""
-echo "---"
diff --git a/constant.h b/constant.h
index 76b59fa047..fcccf07384 100644
--- a/constant.h
+++ b/constant.h
@@ -46,5 +46,6 @@ int rb_public_const_defined(VALUE klass, ID id);
int rb_public_const_defined_at(VALUE klass, ID id);
int rb_public_const_defined_from(VALUE klass, ID id);
rb_const_entry_t *rb_const_lookup(VALUE klass, ID id);
+int rb_autoloading_value(VALUE mod, ID id, VALUE *value, rb_const_flag_t *flag);
#endif /* CONSTANT_H */
diff --git a/cont.c b/cont.c
index 2d8b98dbf4..c19bcdd447 100644
--- a/cont.c
+++ b/cont.c
@@ -13,6 +13,7 @@
#include "vm_core.h"
#include "gc.h"
#include "eval_intern.h"
+#include "mjit.h"
/* FIBER_USE_NATIVE enables Fiber performance improvement using system
* dependent method such as make/setcontext on POSIX system or
@@ -25,6 +26,27 @@
* in Proc. of 51th Programming Symposium, pp.21--28 (2010) (in Japanese).
*/
+/*
+ Enable FIBER_USE_COROUTINE to make fiber yield/resume much faster by using native assembly implementations.
+
+ rvm install ruby-head-ioquatix-native-fiber --url https://github.com/ioquatix/ruby --branch native-fiber
+
+ # Without libcoro
+ koyoko% ./build/bin/ruby ./fiber_benchmark.rb 10000 1000
+ setup time for 10000 fibers: 0.099961
+ execution time for 1000 messages: 19.505909
+
+ # With libcoro
+ koyoko% ./build/bin/ruby ./fiber_benchmark.rb 10000 1000
+ setup time for 10000 fibers: 0.099268
+ execution time for 1000 messages: 8.491746
+*/
+
+#ifdef FIBER_USE_COROUTINE
+#include FIBER_USE_COROUTINE
+#define FIBER_USE_NATIVE 1
+#endif
+
#if !defined(FIBER_USE_NATIVE)
# if defined(HAVE_GETCONTEXT) && defined(HAVE_SETCONTEXT)
# if 0
@@ -76,8 +98,15 @@ static long pagesize;
enum context_type {
CONTINUATION_CONTEXT = 0,
- FIBER_CONTEXT = 1,
- ROOT_FIBER_CONTEXT = 2
+ FIBER_CONTEXT = 1
+};
+
+struct cont_saved_vm_stack {
+ VALUE *ptr;
+#ifdef CAPTURE_JUST_VALID_VM_STACK
+ size_t slen; /* length of stack (head of ec->vm_stack) */
+ size_t clen; /* length of control frames (tail of ec->vm_stack) */
+#endif
};
typedef struct rb_context_struct {
@@ -85,11 +114,9 @@ typedef struct rb_context_struct {
int argc;
VALUE self;
VALUE value;
- VALUE *vm_stack;
-#ifdef CAPTURE_JUST_VALID_VM_STACK
- size_t vm_stack_slen; /* length of stack (head of th->stack) */
- size_t vm_stack_clen; /* length of control frames (tail of th->stack) */
-#endif
+
+ struct cont_saved_vm_stack saved_vm_stack;
+
struct {
VALUE *stack;
VALUE *stack_src;
@@ -100,43 +127,79 @@ typedef struct rb_context_struct {
int register_stack_size;
#endif
} machine;
- rb_thread_t saved_thread; /* selected properties of GET_THREAD() (see cont_save_thread) */
+ rb_execution_context_t saved_ec;
rb_jmpbuf_t jmpbuf;
rb_ensure_entry_t *ensure_array;
- rb_ensure_list_t *ensure_list;
+ /* Pointer to MJIT info about the continuation. */
+ struct mjit_cont *mjit_cont;
} rb_context_t;
+
+/*
+ * Fiber status:
+ * [Fiber.new] ------> FIBER_CREATED
+ * | [Fiber#resume]
+ * v
+ * +--> FIBER_RESUMED ----+
+ * [Fiber#resume] | | [Fiber.yield] |
+ * | v |
+ * +-- FIBER_SUSPENDED | [Terminate]
+ * |
+ * FIBER_TERMINATED <-+
+ */
enum fiber_status {
- CREATED,
- RUNNING,
- TERMINATED
+ FIBER_CREATED,
+ FIBER_RESUMED,
+ FIBER_SUSPENDED,
+ FIBER_TERMINATED
};
-#if FIBER_USE_NATIVE && !defined(_WIN32)
-#define MAX_MACHINE_STACK_CACHE 10
-static int machine_stack_cache_index = 0;
-typedef struct machine_stack_cache_struct {
- void *ptr;
- size_t size;
-} machine_stack_cache_t;
-static machine_stack_cache_t machine_stack_cache[MAX_MACHINE_STACK_CACHE];
-static machine_stack_cache_t terminated_machine_stack;
+#define FIBER_CREATED_P(fib) ((fib)->status == FIBER_CREATED)
+#define FIBER_RESUMED_P(fib) ((fib)->status == FIBER_RESUMED)
+#define FIBER_SUSPENDED_P(fib) ((fib)->status == FIBER_SUSPENDED)
+#define FIBER_TERMINATED_P(fib) ((fib)->status == FIBER_TERMINATED)
+#define FIBER_RUNNABLE_P(fib) (FIBER_CREATED_P(fib) || FIBER_SUSPENDED_P(fib))
+
+#if FIBER_USE_NATIVE && !defined(FIBER_USE_COROUTINE) && !defined(_WIN32)
+static inline int
+fiber_context_create(ucontext_t *context, void (*func)(), void *arg, void *ptr, size_t size)
+{
+ if (getcontext(context) < 0) return -1;
+ /*
+ * getcontext() may fail by some reasons:
+ * 1. SELinux policy banned one of "rt_sigprocmask",
+ * "sigprocmask" or "swapcontext";
+ * 2. libseccomp (aka. syscall filter) banned one of them.
+ */
+ context->uc_link = NULL;
+ context->uc_stack.ss_sp = ptr;
+ context->uc_stack.ss_size = size;
+ makecontext(context, func, 0);
+ return 0;
+}
#endif
struct rb_fiber_struct {
rb_context_t cont;
+ VALUE first_proc;
struct rb_fiber_struct *prev;
- enum fiber_status status;
+ BITFIELD(enum fiber_status, status, 2);
/* If a fiber invokes "transfer",
* then this fiber can't "resume" any more after that.
* You shouldn't mix "transfer" and "resume".
*/
- int transferred;
+ unsigned int transferred : 1;
#if FIBER_USE_NATIVE
-#ifdef _WIN32
+#if defined(FIBER_USE_COROUTINE)
+#define FIBER_ALLOCATE_STACK
+ coroutine_context context;
+ void *ss_sp;
+ size_t ss_size;
+#elif defined(_WIN32)
void *fib_handle;
#else
+#define FIBER_ALLOCATE_STACK
ucontext_t context;
/* Because context.uc_stack.ss_sp and context.uc_stack.ss_size
* are not necessarily valid after makecontext() or swapcontext(),
@@ -148,125 +211,245 @@ struct rb_fiber_struct {
#endif
};
+#ifdef FIBER_ALLOCATE_STACK
+#define MAX_MACHINE_STACK_CACHE 10
+static int machine_stack_cache_index = 0;
+typedef struct machine_stack_cache_struct {
+ void *ptr;
+ size_t size;
+} machine_stack_cache_t;
+static machine_stack_cache_t machine_stack_cache[MAX_MACHINE_STACK_CACHE];
+static machine_stack_cache_t terminated_machine_stack;
+#endif
+
+static const char *
+fiber_status_name(enum fiber_status s)
+{
+ switch (s) {
+ case FIBER_CREATED: return "created";
+ case FIBER_RESUMED: return "resumed";
+ case FIBER_SUSPENDED: return "suspended";
+ case FIBER_TERMINATED: return "terminated";
+ }
+ VM_UNREACHABLE(fiber_status_name);
+ return NULL;
+}
+
+static void
+fiber_verify(const rb_fiber_t *fib)
+{
+#if VM_CHECK_MODE > 0
+ VM_ASSERT(fib->cont.saved_ec.fiber_ptr == fib);
+
+ switch (fib->status) {
+ case FIBER_RESUMED:
+ VM_ASSERT(fib->cont.saved_ec.vm_stack != NULL);
+ break;
+ case FIBER_SUSPENDED:
+ VM_ASSERT(fib->cont.saved_ec.vm_stack != NULL);
+ break;
+ case FIBER_CREATED:
+ case FIBER_TERMINATED:
+ /* TODO */
+ break;
+ default:
+ VM_UNREACHABLE(fiber_verify);
+ }
+#endif
+}
+
+#if VM_CHECK_MODE > 0
+void
+rb_ec_verify(const rb_execution_context_t *ec)
+{
+ /* TODO */
+}
+#endif
+
+static void
+fiber_status_set(rb_fiber_t *fib, enum fiber_status s)
+{
+ if (0) fprintf(stderr, "fib: %p, status: %s -> %s\n", (void *)fib, fiber_status_name(fib->status), fiber_status_name(s));
+ VM_ASSERT(!FIBER_TERMINATED_P(fib));
+ VM_ASSERT(fib->status != s);
+ fiber_verify(fib);
+ fib->status = s;
+}
+
+void
+rb_ec_set_vm_stack(rb_execution_context_t *ec, VALUE *stack, size_t size)
+{
+ ec->vm_stack = stack;
+ ec->vm_stack_size = size;
+}
+
+static inline void
+ec_switch(rb_thread_t *th, rb_fiber_t *fib)
+{
+ rb_execution_context_t *ec = &fib->cont.saved_ec;
+
+ ruby_current_execution_context_ptr = th->ec = ec;
+
+ /*
+ * timer-thread may set trap interrupt on previous th->ec at any time;
+ * ensure we do not delay (or lose) the trap interrupt handling.
+ */
+ if (th->vm->main_thread == th && rb_signal_buff_size() > 0) {
+ RUBY_VM_SET_TRAP_INTERRUPT(ec);
+ }
+
+ VM_ASSERT(ec->fiber_ptr->cont.self == 0 || ec->vm_stack != NULL);
+}
+
static const rb_data_type_t cont_data_type, fiber_data_type;
static VALUE rb_cContinuation;
static VALUE rb_cFiber;
static VALUE rb_eFiberError;
-#define GetContPtr(obj, ptr) \
- TypedData_Get_Struct((obj), rb_context_t, &cont_data_type, (ptr))
+static rb_context_t *
+cont_ptr(VALUE obj)
+{
+ rb_context_t *cont;
-#define GetFiberPtr(obj, ptr) do {\
- TypedData_Get_Struct((obj), rb_fiber_t, &fiber_data_type, (ptr)); \
- if (!(ptr)) rb_raise(rb_eFiberError, "uninitialized fiber"); \
-} while (0)
+ TypedData_Get_Struct(obj, rb_context_t, &cont_data_type, cont);
-NOINLINE(static VALUE cont_capture(volatile int *stat));
+ return cont;
+}
+
+static rb_fiber_t *
+fiber_ptr(VALUE obj)
+{
+ rb_fiber_t *fib;
+
+ TypedData_Get_Struct(obj, rb_fiber_t, &fiber_data_type, fib);
+ if (!fib) rb_raise(rb_eFiberError, "uninitialized fiber");
+
+ return fib;
+}
+
+NOINLINE(static VALUE cont_capture(volatile int *volatile stat));
#define THREAD_MUST_BE_RUNNING(th) do { \
- if (!(th)->tag) rb_raise(rb_eThreadError, "not running thread"); \
+ if (!(th)->ec->tag) rb_raise(rb_eThreadError, "not running thread"); \
} while (0)
+static VALUE
+cont_thread_value(const rb_context_t *cont)
+{
+ return cont->saved_ec.thread_ptr->self;
+}
+
static void
cont_mark(void *ptr)
{
+ rb_context_t *cont = ptr;
+
RUBY_MARK_ENTER("cont");
- if (ptr) {
- rb_context_t *cont = ptr;
- rb_gc_mark(cont->value);
+ rb_gc_mark(cont->value);
- rb_thread_mark(&cont->saved_thread);
- rb_gc_mark(cont->saved_thread.self);
+ rb_execution_context_mark(&cont->saved_ec);
+ rb_gc_mark(cont_thread_value(cont));
- if (cont->vm_stack) {
+ if (cont->saved_vm_stack.ptr) {
#ifdef CAPTURE_JUST_VALID_VM_STACK
- rb_gc_mark_locations(cont->vm_stack,
- cont->vm_stack + cont->vm_stack_slen + cont->vm_stack_clen);
+ rb_gc_mark_locations(cont->saved_vm_stack.ptr,
+ cont->saved_vm_stack.ptr + cont->saved_vm_stack.slen + cont->saved_vm_stack.clen);
#else
- rb_gc_mark_locations(cont->vm_stack,
- cont->vm_stack, cont->saved_thread.stack_size);
+ rb_gc_mark_locations(cont->saved_vm_stack.ptr,
+ cont->saved_vm_stack.ptr, cont->saved_ec.stack_size);
#endif
+ }
+
+ if (cont->machine.stack) {
+ if (cont->type == CONTINUATION_CONTEXT) {
+ /* cont */
+ rb_gc_mark_locations(cont->machine.stack,
+ cont->machine.stack + cont->machine.stack_size);
}
+ else {
+ /* fiber */
+ const rb_fiber_t *fib = (rb_fiber_t*)cont;
- if (cont->machine.stack) {
- if (cont->type == CONTINUATION_CONTEXT) {
- /* cont */
+ if (!FIBER_TERMINATED_P(fib)) {
rb_gc_mark_locations(cont->machine.stack,
cont->machine.stack + cont->machine.stack_size);
- }
- else {
- /* fiber */
- rb_thread_t *th;
- rb_fiber_t *fib = (rb_fiber_t*)cont;
- GetThreadPtr(cont->saved_thread.self, th);
- if ((th->fiber != fib) && fib->status == RUNNING) {
- rb_gc_mark_locations(cont->machine.stack,
- cont->machine.stack + cont->machine.stack_size);
- }
}
}
+ }
#ifdef __ia64
- if (cont->machine.register_stack) {
- rb_gc_mark_locations(cont->machine.register_stack,
- cont->machine.register_stack + cont->machine.register_stack_size);
- }
-#endif
+ if (cont->machine.register_stack) {
+ rb_gc_mark_locations(cont->machine.register_stack,
+ cont->machine.register_stack + cont->machine.register_stack_size);
}
+#endif
+
RUBY_MARK_LEAVE("cont");
}
+static int
+fiber_is_root_p(const rb_fiber_t *fib)
+{
+ return fib == fib->cont.saved_ec.thread_ptr->root_fiber;
+}
+
static void
cont_free(void *ptr)
{
+ rb_context_t *cont = ptr;
+
RUBY_FREE_ENTER("cont");
- if (ptr) {
- rb_context_t *cont = ptr;
- RUBY_FREE_UNLESS_NULL(cont->saved_thread.stack);
+ ruby_xfree(cont->saved_ec.vm_stack);
+
#if FIBER_USE_NATIVE
- if (cont->type == CONTINUATION_CONTEXT) {
- /* cont */
- ruby_xfree(cont->ensure_array);
- RUBY_FREE_UNLESS_NULL(cont->machine.stack);
- }
- else {
- /* fiber */
- rb_fiber_t *fib = (rb_fiber_t*)cont;
- const rb_thread_t *const th = GET_THREAD();
+ if (cont->type == CONTINUATION_CONTEXT) {
+ /* cont */
+ ruby_xfree(cont->ensure_array);
+ RUBY_FREE_UNLESS_NULL(cont->machine.stack);
+ }
+ else {
+ /* fiber */
+ rb_fiber_t *fib = (rb_fiber_t*)cont;
+#if defined(FIBER_USE_COROUTINE)
+ coroutine_destroy(&fib->context);
+ if (fib->ss_sp != NULL) {
+ if (fiber_is_root_p(fib)) {
+ rb_bug("Illegal root fiber parameter");
+ }
#ifdef _WIN32
- if (th && th->fiber != fib && cont->type != ROOT_FIBER_CONTEXT) {
- /* don't delete root fiber handle */
- if (fib->fib_handle) {
- DeleteFiber(fib->fib_handle);
- }
+ VirtualFree((void*)fib->ss_sp, 0, MEM_RELEASE);
+#else
+ munmap((void*)fib->ss_sp, fib->ss_size);
+#endif
+ fib->ss_sp = NULL;
+ }
+#elif defined(_WIN32)
+ if (!fiber_is_root_p(fib)) {
+ /* don't delete root fiber handle */
+ if (fib->fib_handle) {
+ DeleteFiber(fib->fib_handle);
}
+ }
#else /* not WIN32 */
- if (th && th->fiber != fib) {
- if (fib->ss_sp) {
- if (cont->type == ROOT_FIBER_CONTEXT) {
- rb_bug("Illegal root fiber parameter");
- }
- munmap((void*)fib->ss_sp, fib->ss_size);
- }
- }
- else {
- /* It may reached here when finalize */
- /* TODO examine whether it is a bug */
- /* rb_bug("cont_free: release self"); */
- }
-#endif
+ /* fib->ss_sp == NULL is possible for root fiber */
+ if (fib->ss_sp != NULL) {
+ munmap((void*)fib->ss_sp, fib->ss_size);
}
+#endif
+ }
#else /* not FIBER_USE_NATIVE */
- ruby_xfree(cont->ensure_array);
- RUBY_FREE_UNLESS_NULL(cont->machine.stack);
+ ruby_xfree(cont->ensure_array);
+ RUBY_FREE_UNLESS_NULL(cont->machine.stack);
#endif
#ifdef __ia64
- RUBY_FREE_UNLESS_NULL(cont->machine.register_stack);
+ RUBY_FREE_UNLESS_NULL(cont->machine.register_stack);
#endif
- RUBY_FREE_UNLESS_NULL(cont->vm_stack);
+ RUBY_FREE_UNLESS_NULL(cont->saved_vm_stack.ptr);
- /* free rb_cont_t or rb_fiber_t */
- ruby_xfree(ptr);
+ if (mjit_enabled && cont->mjit_cont != NULL) {
+ mjit_cont_free(cont->mjit_cont);
}
+ /* free rb_cont_t or rb_fiber_t */
+ ruby_xfree(ptr);
RUBY_FREE_LEAVE("cont");
}
@@ -277,13 +460,13 @@ cont_memsize(const void *ptr)
size_t size = 0;
size = sizeof(*cont);
- if (cont->vm_stack) {
+ if (cont->saved_vm_stack.ptr) {
#ifdef CAPTURE_JUST_VALID_VM_STACK
- size_t n = (cont->vm_stack_slen + cont->vm_stack_clen);
+ size_t n = (cont->saved_vm_stack.slen + cont->saved_vm_stack.clen);
#else
- size_t n = cont->saved_thread.stack_size;
+ size_t n = cont->saved_ec.vm_stack_size;
#endif
- size += n * sizeof(*cont->vm_stack);
+ size += n * sizeof(*cont->saved_vm_stack.ptr);
}
if (cont->machine.stack) {
@@ -298,37 +481,49 @@ cont_memsize(const void *ptr)
}
void
-rb_fiber_mark_self(rb_fiber_t *fib)
+rb_fiber_mark_self(const rb_fiber_t *fib)
{
- if (fib)
+ if (fib->cont.self) {
rb_gc_mark(fib->cont.self);
+ }
+ else {
+ rb_execution_context_mark(&fib->cont.saved_ec);
+ }
}
static void
fiber_mark(void *ptr)
{
+ rb_fiber_t *fib = ptr;
RUBY_MARK_ENTER("cont");
- if (ptr) {
- rb_fiber_t *fib = ptr;
- rb_fiber_mark_self(fib->prev);
- cont_mark(&fib->cont);
+ fiber_verify(fib);
+ rb_gc_mark(fib->first_proc);
+ if (fib->prev) rb_fiber_mark_self(fib->prev);
+
+#if !FIBER_USE_NATIVE
+ if (fib->status == FIBER_TERMINATED) {
+ /* FIBER_TERMINATED fiber should not mark machine stack */
+ if (fib->cont.saved_ec.machine.stack_end != NULL) {
+ fib->cont.saved_ec.machine.stack_end = NULL;
+ }
}
+#endif
+
+ cont_mark(&fib->cont);
RUBY_MARK_LEAVE("cont");
}
static void
fiber_free(void *ptr)
{
+ rb_fiber_t *fib = ptr;
RUBY_FREE_ENTER("fiber");
- if (ptr) {
- rb_fiber_t *fib = ptr;
- if (fib->cont.type != ROOT_FIBER_CONTEXT &&
- fib->cont.saved_thread.local_storage) {
- st_free_table(fib->cont.saved_thread.local_storage);
- }
- cont_free(&fib->cont);
+ if (fib->cont.saved_ec.local_storage) {
+ st_free_table(fib->cont.saved_ec.local_storage);
}
+
+ cont_free(&fib->cont);
RUBY_FREE_LEAVE("fiber");
}
@@ -336,12 +531,15 @@ static size_t
fiber_memsize(const void *ptr)
{
const rb_fiber_t *fib = ptr;
- size_t size = 0;
+ size_t size = sizeof(*fib);
+ const rb_execution_context_t *saved_ec = &fib->cont.saved_ec;
+ const rb_thread_t *th = rb_ec_thread_ptr(saved_ec);
- size = sizeof(*fib);
- if (fib->cont.type != ROOT_FIBER_CONTEXT &&
- fib->cont.saved_thread.local_storage != NULL) {
- size += st_memsize(fib->cont.saved_thread.local_storage);
+ /*
+ * vm.c::thread_memsize already counts th->ec->local_storage
+ */
+ if (saved_ec->local_storage && fib != th->root_fiber) {
+ size += st_memsize(saved_ec->local_storage);
}
size += cont_memsize(&fib->cont);
return size;
@@ -363,18 +561,18 @@ cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont)
{
size_t size;
- SET_MACHINE_STACK_END(&th->machine.stack_end);
+ SET_MACHINE_STACK_END(&th->ec->machine.stack_end);
#ifdef __ia64
- th->machine.register_stack_end = rb_ia64_bsp();
+ th->ec->machine.register_stack_end = rb_ia64_bsp();
#endif
- if (th->machine.stack_start > th->machine.stack_end) {
- size = cont->machine.stack_size = th->machine.stack_start - th->machine.stack_end;
- cont->machine.stack_src = th->machine.stack_end;
+ if (th->ec->machine.stack_start > th->ec->machine.stack_end) {
+ size = cont->machine.stack_size = th->ec->machine.stack_start - th->ec->machine.stack_end;
+ cont->machine.stack_src = th->ec->machine.stack_end;
}
else {
- size = cont->machine.stack_size = th->machine.stack_end - th->machine.stack_start;
- cont->machine.stack_src = th->machine.stack_start;
+ size = cont->machine.stack_size = th->ec->machine.stack_end - th->ec->machine.stack_start;
+ cont->machine.stack_src = th->ec->machine.stack_start;
}
if (cont->machine.stack) {
@@ -389,8 +587,8 @@ cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont)
#ifdef __ia64
rb_ia64_flushrs();
- size = cont->machine.register_stack_size = th->machine.register_stack_end - th->machine.register_stack_start;
- cont->machine.register_stack_src = th->machine.register_stack_start;
+ size = cont->machine.register_stack_size = th->ec->machine.register_stack_end - th->ec->machine.register_stack_start;
+ cont->machine.register_stack_src = th->ec->machine.register_stack_start;
if (cont->machine.register_stack) {
REALLOC_N(cont->machine.register_stack, VALUE, size);
}
@@ -411,48 +609,42 @@ static const rb_data_type_t cont_data_type = {
static inline void
cont_save_thread(rb_context_t *cont, rb_thread_t *th)
{
- rb_thread_t *sth = &cont->saved_thread;
+ rb_execution_context_t *sec = &cont->saved_ec;
+
+ VM_ASSERT(th->status == THREAD_RUNNABLE);
/* save thread context */
- sth->stack = th->stack;
- sth->stack_size = th->stack_size;
- sth->local_storage = th->local_storage;
- sth->cfp = th->cfp;
- sth->safe_level = th->safe_level;
- sth->raised_flag = th->raised_flag;
- sth->state = th->state;
- sth->status = th->status;
- sth->tag = th->tag;
- sth->protect_tag = th->protect_tag;
- sth->errinfo = th->errinfo;
- sth->first_proc = th->first_proc;
- sth->root_lep = th->root_lep;
- sth->root_svar = th->root_svar;
- sth->ensure_list = th->ensure_list;
-
- sth->trace_arg = th->trace_arg;
-
- /* saved_thread->machine.stack_(start|end) should be NULL */
+ *sec = *th->ec;
+
+ /* saved_ec->machine.stack_end should be NULL */
/* because it may happen GC afterward */
- sth->machine.stack_start = 0;
- sth->machine.stack_end = 0;
+ sec->machine.stack_end = NULL;
+
#ifdef __ia64
- sth->machine.register_stack_start = 0;
- sth->machine.register_stack_end = 0;
+ sec->machine.register_stack_start = NULL;
+ sec->machine.register_stack_end = NULL;
#endif
}
static void
+cont_init_mjit_cont(rb_context_t *cont)
+{
+ VM_ASSERT(cont->mjit_cont == NULL);
+ if (mjit_enabled) {
+ cont->mjit_cont = mjit_cont_new(&(cont->saved_ec));
+ }
+}
+
+static void
cont_init(rb_context_t *cont, rb_thread_t *th)
{
/* save thread context */
cont_save_thread(cont, th);
- cont->saved_thread.self = th->self;
- cont->saved_thread.machine.stack_maxsize = th->machine.stack_maxsize;
- cont->saved_thread.fiber = th->fiber;
- cont->saved_thread.local_storage = 0;
- cont->saved_thread.local_storage_recursive_hash = Qnil;
- cont->saved_thread.local_storage_recursive_hash_for_trace = Qnil;
+ cont->saved_ec.thread_ptr = 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);
}
static rb_context_t *
@@ -469,34 +661,74 @@ cont_new(VALUE klass)
return cont;
}
-static VALUE
-cont_capture(volatile int *stat)
-#if defined(__clang__) && \
- __clang_major__ == 3 && __clang_minor__ == 8 && __clang_patch__ == 0
-__attribute__ ((optnone))
+void
+rb_fiber_init_mjit_cont(struct rb_fiber_struct *fiber)
+{
+ // Currently this function is meant for root_fiber. Others go through cont_new.
+ // XXX: Is this mjit_cont `mjit_cont_free`d?
+ cont_init_mjit_cont(&fiber->cont);
+}
+
+#if 0
+void
+show_vm_stack(const rb_execution_context_t *ec)
+{
+ VALUE *p = ec->vm_stack;
+ while (p < ec->cfp->sp) {
+ fprintf(stderr, "%3d ", (int)(p - ec->vm_stack));
+ rb_obj_info_dump(*p);
+ p++;
+ }
+}
+
+void
+show_vm_pcs(const rb_control_frame_t *cfp,
+ const rb_control_frame_t *end_of_cfp)
+{
+ int i=0;
+ while (cfp != end_of_cfp) {
+ int pc = 0;
+ if (cfp->iseq) {
+ pc = cfp->pc - cfp->iseq->body->iseq_encoded;
+ }
+ fprintf(stderr, "%2d pc: %d\n", i++, pc);
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
+ }
+}
+#endif
+COMPILER_WARNING_PUSH
+#ifdef __clang__
+COMPILER_WARNING_IGNORED(-Wduplicate-decl-specifier)
#endif
+static VALUE
+cont_capture(volatile int *volatile stat)
{
- rb_context_t *cont;
+ rb_context_t *volatile cont;
rb_thread_t *th = GET_THREAD();
volatile VALUE contval;
+ const rb_execution_context_t *ec = th->ec;
THREAD_MUST_BE_RUNNING(th);
- rb_vm_stack_to_heap(th);
+ rb_vm_stack_to_heap(th->ec);
cont = cont_new(rb_cContinuation);
contval = cont->self;
#ifdef CAPTURE_JUST_VALID_VM_STACK
- cont->vm_stack_slen = th->cfp->sp - th->stack;
- cont->vm_stack_clen = th->stack + th->stack_size - (VALUE*)th->cfp;
- cont->vm_stack = ALLOC_N(VALUE, cont->vm_stack_slen + cont->vm_stack_clen);
- MEMCPY(cont->vm_stack, th->stack, VALUE, cont->vm_stack_slen);
- MEMCPY(cont->vm_stack + cont->vm_stack_slen, (VALUE*)th->cfp, VALUE, cont->vm_stack_clen);
+ cont->saved_vm_stack.slen = ec->cfp->sp - ec->vm_stack;
+ cont->saved_vm_stack.clen = ec->vm_stack + ec->vm_stack_size - (VALUE*)ec->cfp;
+ cont->saved_vm_stack.ptr = ALLOC_N(VALUE, cont->saved_vm_stack.slen + cont->saved_vm_stack.clen);
+ MEMCPY(cont->saved_vm_stack.ptr,
+ ec->vm_stack,
+ VALUE, cont->saved_vm_stack.slen);
+ MEMCPY(cont->saved_vm_stack.ptr + cont->saved_vm_stack.slen,
+ (VALUE*)ec->cfp,
+ VALUE,
+ cont->saved_vm_stack.clen);
#else
- cont->vm_stack = ALLOC_N(VALUE, th->stack_size);
- MEMCPY(cont->vm_stack, th->stack, VALUE, th->stack_size);
+ cont->saved_vm_stack.ptr = ALLOC_N(VALUE, ec->vm_stack_size);
+ MEMCPY(cont->saved_vm_stack.ptr, ec->vm_stack, VALUE, ec->vm_stack_size);
#endif
- cont->saved_thread.stack = NULL;
-
+ rb_ec_set_vm_stack(&cont->saved_ec, NULL, 0);
cont_save_machine_stack(th, cont);
/* backup ensure_list to array for search in another context */
@@ -504,10 +736,10 @@ __attribute__ ((optnone))
rb_ensure_list_t *p;
int size = 0;
rb_ensure_entry_t *entry;
- for (p=th->ensure_list; p; p=p->next)
+ for (p=th->ec->ensure_list; p; p=p->next)
size++;
entry = cont->ensure_array = ALLOC_N(rb_ensure_entry_t,size+1);
- for (p=th->ensure_list; p; p=p->next) {
+ for (p=th->ec->ensure_list; p; p=p->next) {
if (!p->entry.marker)
p->entry.marker = rb_ary_tmp_new(0); /* dummy object */
*entry++ = p->entry;
@@ -530,59 +762,79 @@ __attribute__ ((optnone))
return contval;
}
}
+COMPILER_WARNING_POP
+
+static inline void
+fiber_restore_thread(rb_thread_t *th, rb_fiber_t *fib)
+{
+ ec_switch(th, fib);
+ VM_ASSERT(th->ec->fiber_ptr == fib);
+}
static inline void
cont_restore_thread(rb_context_t *cont)
{
- rb_thread_t *th = GET_THREAD(), *sth = &cont->saved_thread;
+ rb_thread_t *th = GET_THREAD();
/* restore thread context */
if (cont->type == CONTINUATION_CONTEXT) {
/* continuation */
- rb_fiber_t *fib;
+ rb_execution_context_t *sec = &cont->saved_ec;
+ rb_fiber_t *fib = NULL;
- th->fiber = sth->fiber;
- fib = th->fiber ? th->fiber : th->root_fiber;
+ if (sec->fiber_ptr != NULL) {
+ fib = sec->fiber_ptr;
+ }
+ else if (th->root_fiber) {
+ fib = th->root_fiber;
+ }
- if (fib && fib->cont.saved_thread.stack) {
- th->stack_size = fib->cont.saved_thread.stack_size;
- th->stack = fib->cont.saved_thread.stack;
+ if (fib && th->ec != &fib->cont.saved_ec) {
+ ec_switch(th, fib);
}
+
+ if (th->ec->trace_arg != sec->trace_arg) {
+ rb_raise(rb_eRuntimeError, "can't call across trace_func");
+ }
+
+ /* copy vm stack */
#ifdef CAPTURE_JUST_VALID_VM_STACK
- MEMCPY(th->stack, cont->vm_stack, VALUE, cont->vm_stack_slen);
- MEMCPY(th->stack + sth->stack_size - cont->vm_stack_clen,
- cont->vm_stack + cont->vm_stack_slen, VALUE, cont->vm_stack_clen);
+ MEMCPY(th->ec->vm_stack,
+ cont->saved_vm_stack.ptr,
+ VALUE, cont->saved_vm_stack.slen);
+ MEMCPY(th->ec->vm_stack + th->ec->vm_stack_size - cont->saved_vm_stack.clen,
+ cont->saved_vm_stack.ptr + cont->saved_vm_stack.slen,
+ VALUE, cont->saved_vm_stack.clen);
#else
- MEMCPY(th->stack, cont->vm_stack, VALUE, sth->stack_size);
+ MEMCPY(th->ec->vm_stack, cont->saved_vm_stack.ptr, VALUE, sec->vm_stack_size);
#endif
+ /* other members of ec */
+
+ th->ec->cfp = sec->cfp;
+ th->ec->raised_flag = sec->raised_flag;
+ th->ec->tag = sec->tag;
+ th->ec->protect_tag = sec->protect_tag;
+ th->ec->root_lep = sec->root_lep;
+ th->ec->root_svar = sec->root_svar;
+ th->ec->ensure_list = sec->ensure_list;
+ th->ec->errinfo = sec->errinfo;
+
+ VM_ASSERT(th->ec->vm_stack != NULL);
}
else {
/* fiber */
- th->stack = sth->stack;
- sth->stack = NULL;
- th->stack_size = sth->stack_size;
- th->local_storage = sth->local_storage;
- th->local_storage_recursive_hash = sth->local_storage_recursive_hash;
- th->local_storage_recursive_hash_for_trace = sth->local_storage_recursive_hash_for_trace;
- th->fiber = (rb_fiber_t*)cont;
- }
-
- th->cfp = sth->cfp;
- th->safe_level = sth->safe_level;
- th->raised_flag = sth->raised_flag;
- th->state = sth->state;
- th->status = sth->status;
- th->tag = sth->tag;
- th->protect_tag = sth->protect_tag;
- th->errinfo = sth->errinfo;
- th->first_proc = sth->first_proc;
- th->root_lep = sth->root_lep;
- th->root_svar = sth->root_svar;
- th->ensure_list = sth->ensure_list;
+ fiber_restore_thread(th, (rb_fiber_t*)cont);
+ }
}
#if FIBER_USE_NATIVE
-#ifdef _WIN32
+#if defined(FIBER_USE_COROUTINE)
+static COROUTINE
+fiber_entry(coroutine_context * from, coroutine_context * to)
+{
+ rb_fiber_start();
+}
+#elif defined(_WIN32)
static void
fiber_set_stack_location(void)
{
@@ -590,17 +842,27 @@ fiber_set_stack_location(void)
VALUE *ptr;
SET_MACHINE_STACK_END(&ptr);
- th->machine.stack_start = (void*)(((VALUE)ptr & RB_PAGE_MASK) + STACK_UPPER((void *)&ptr, 0, RB_PAGE_SIZE));
+ th->ec->machine.stack_start = (void*)(((VALUE)ptr & RB_PAGE_MASK) + STACK_UPPER((void *)&ptr, 0, RB_PAGE_SIZE));
}
+NORETURN(static VOID CALLBACK fiber_entry(void *arg));
static VOID CALLBACK
fiber_entry(void *arg)
{
fiber_set_stack_location();
rb_fiber_start();
}
-#else /* _WIN32 */
+#else
+NORETURN(static void fiber_entry(void *arg));
+static void
+fiber_entry(void *arg)
+{
+ rb_fiber_start();
+}
+#endif
+#endif
+#ifdef FIBER_ALLOCATE_STACK
/*
* FreeBSD require a first (i.e. addr) argument of mmap(2) is not NULL
* if MAP_STACK is passed.
@@ -612,50 +874,76 @@ fiber_entry(void *arg)
#define FIBER_STACK_FLAGS (MAP_PRIVATE | MAP_ANON)
#endif
+#define ERRNOMSG strerror(errno)
+
static char*
fiber_machine_stack_alloc(size_t size)
{
char *ptr;
+#ifdef _WIN32
+ DWORD old_protect;
+#endif
if (machine_stack_cache_index > 0) {
- if (machine_stack_cache[machine_stack_cache_index - 1].size == (size / sizeof(VALUE))) {
- ptr = machine_stack_cache[machine_stack_cache_index - 1].ptr;
- machine_stack_cache_index--;
- machine_stack_cache[machine_stack_cache_index].ptr = NULL;
- machine_stack_cache[machine_stack_cache_index].size = 0;
- }
- else{
+ if (machine_stack_cache[machine_stack_cache_index - 1].size == (size / sizeof(VALUE))) {
+ ptr = machine_stack_cache[machine_stack_cache_index - 1].ptr;
+ machine_stack_cache_index--;
+ machine_stack_cache[machine_stack_cache_index].ptr = NULL;
+ machine_stack_cache[machine_stack_cache_index].size = 0;
+ } else {
/* TODO handle multiple machine stack size */
- rb_bug("machine_stack_cache size is not canonicalized");
- }
- }
- else {
- void *page;
- STACK_GROW_DIR_DETECTION;
+ rb_bug("machine_stack_cache size is not canonicalized");
+ }
+ } else {
+#ifdef _WIN32
+ ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE);
- errno = 0;
- ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, FIBER_STACK_FLAGS, -1, 0);
- if (ptr == MAP_FAILED) {
- rb_raise(rb_eFiberError, "can't alloc machine stack to fiber: %s", strerror(errno));
- }
+ if (!ptr) {
+ rb_raise(rb_eFiberError, "can't allocate machine stack to fiber: %s", ERRNOMSG);
+ }
- /* guard page setup */
- page = ptr + STACK_DIR_UPPER(size - RB_PAGE_SIZE, 0);
- if (mprotect(page, RB_PAGE_SIZE, PROT_NONE) < 0) {
- rb_raise(rb_eFiberError, "mprotect failed");
- }
+ if (!VirtualProtect(ptr, RB_PAGE_SIZE, PAGE_READWRITE | PAGE_GUARD, &old_protect)) {
+ rb_raise(rb_eFiberError, "can't set a guard page: %s", ERRNOMSG);
+ }
+#else
+ void *page;
+ STACK_GROW_DIR_DETECTION;
+
+ errno = 0;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, FIBER_STACK_FLAGS, -1, 0);
+ if (ptr == MAP_FAILED) {
+ rb_raise(rb_eFiberError, "can't alloc machine stack to fiber: %s", ERRNOMSG);
+ }
+
+ /* guard page setup */
+ page = ptr + STACK_DIR_UPPER(size - RB_PAGE_SIZE, 0);
+ if (mprotect(page, RB_PAGE_SIZE, PROT_NONE) < 0) {
+ rb_raise(rb_eFiberError, "can't set a guard page: %s", ERRNOMSG);
+ }
+#endif
}
return ptr;
}
#endif
+#if FIBER_USE_NATIVE
static void
fiber_initialize_machine_stack_context(rb_fiber_t *fib, size_t size)
{
- rb_thread_t *sth = &fib->cont.saved_thread;
+ rb_execution_context_t *sec = &fib->cont.saved_ec;
-#ifdef _WIN32
+#if defined(FIBER_USE_COROUTINE)
+ char *ptr;
+ STACK_GROW_DIR_DETECTION;
+
+ ptr = fiber_machine_stack_alloc(size);
+ fib->ss_sp = ptr;
+ fib->ss_size = size;
+ coroutine_initialize(&fib->context, fiber_entry, ptr+size, size);
+ sec->machine.stack_start = (VALUE*)(ptr + STACK_DIR_UPPER(0, size));
+ sec->machine.stack_maxsize = size - RB_PAGE_SIZE;
+#elif defined(_WIN32)
# if defined(_MSC_VER) && _MSC_VER <= 1200
# define CreateFiberEx(cs, stacksize, flags, entry, param) \
CreateFiber((stacksize), (entry), (param))
@@ -669,22 +957,19 @@ fiber_initialize_machine_stack_context(rb_fiber_t *fib, size_t size)
rb_raise(rb_eFiberError, "can't create fiber");
}
}
- sth->machine.stack_maxsize = size;
+ sec->machine.stack_maxsize = size;
#else /* not WIN32 */
- ucontext_t *context = &fib->context;
char *ptr;
STACK_GROW_DIR_DETECTION;
- getcontext(context);
ptr = fiber_machine_stack_alloc(size);
- context->uc_link = NULL;
- context->uc_stack.ss_sp = ptr;
- context->uc_stack.ss_size = size;
fib->ss_sp = ptr;
fib->ss_size = size;
- makecontext(context, rb_fiber_start, 0);
- sth->machine.stack_start = (VALUE*)(ptr + STACK_DIR_UPPER(0, size));
- sth->machine.stack_maxsize = size - RB_PAGE_SIZE;
+ if (fiber_context_create(&fib->context, fiber_entry, NULL, fib->ss_sp, fib->ss_size)) {
+ rb_raise(rb_eFiberError, "can't get context for creating fiber: %s", ERRNOMSG);
+ }
+ sec->machine.stack_start = (VALUE*)(ptr + STACK_DIR_UPPER(0, size));
+ sec->machine.stack_maxsize = size - RB_PAGE_SIZE;
#endif
#ifdef __ia64
sth->machine.register_stack_maxsize = sth->machine.stack_maxsize;
@@ -696,50 +981,44 @@ NOINLINE(static void fiber_setcontext(rb_fiber_t *newfib, rb_fiber_t *oldfib));
static void
fiber_setcontext(rb_fiber_t *newfib, rb_fiber_t *oldfib)
{
- rb_thread_t *th = GET_THREAD(), *sth = &newfib->cont.saved_thread;
-
- if (newfib->status != RUNNING) {
- fiber_initialize_machine_stack_context(newfib, th->vm->default_params.fiber_machine_stack_size);
- }
-
- /* restore thread context */
- cont_restore_thread(&newfib->cont);
- th->machine.stack_maxsize = sth->machine.stack_maxsize;
- if (sth->machine.stack_end && (newfib != oldfib)) {
- rb_bug("fiber_setcontext: sth->machine.stack_end has non zero value");
- }
+ rb_thread_t *th = GET_THREAD();
- /* save oldfib's machine stack */
- if (oldfib->status != TERMINATED) {
+ /* save oldfib's machine stack / TODO: is it needed? */
+ if (!FIBER_TERMINATED_P(oldfib)) {
STACK_GROW_DIR_DETECTION;
- SET_MACHINE_STACK_END(&th->machine.stack_end);
+ SET_MACHINE_STACK_END(&th->ec->machine.stack_end);
if (STACK_DIR_UPPER(0, 1)) {
- oldfib->cont.machine.stack_size = th->machine.stack_start - th->machine.stack_end;
- oldfib->cont.machine.stack = th->machine.stack_end;
+ oldfib->cont.machine.stack_size = th->ec->machine.stack_start - th->ec->machine.stack_end;
+ oldfib->cont.machine.stack = th->ec->machine.stack_end;
}
else {
- oldfib->cont.machine.stack_size = th->machine.stack_end - th->machine.stack_start;
- oldfib->cont.machine.stack = th->machine.stack_start;
+ oldfib->cont.machine.stack_size = th->ec->machine.stack_end - th->ec->machine.stack_start;
+ oldfib->cont.machine.stack = th->ec->machine.stack_start;
}
}
+
/* exchange machine_stack_start between oldfib and newfib */
- oldfib->cont.saved_thread.machine.stack_start = th->machine.stack_start;
- th->machine.stack_start = sth->machine.stack_start;
+ oldfib->cont.saved_ec.machine.stack_start = th->ec->machine.stack_start;
+
/* oldfib->machine.stack_end should be NULL */
- oldfib->cont.saved_thread.machine.stack_end = 0;
-#ifndef _WIN32
- if (!newfib->context.uc_stack.ss_sp && th->root_fiber != newfib) {
- rb_bug("non_root_fiber->context.uc_stac.ss_sp should not be NULL");
- }
-#endif
+ oldfib->cont.saved_ec.machine.stack_end = NULL;
+
+ /* restore thread context */
+ fiber_restore_thread(th, newfib);
+
/* swap machine context */
-#ifdef _WIN32
+#if defined(FIBER_USE_COROUTINE)
+ coroutine_transfer(&oldfib->context, &newfib->context);
+#elif defined(_WIN32)
SwitchToFiber(newfib->fib_handle);
#else
+ if (!newfib->context.uc_stack.ss_sp && th->root_fiber != newfib) {
+ rb_bug("non_root_fiber->context.uc_stac.ss_sp should not be NULL");
+ }
swapcontext(&oldfib->context, &newfib->context);
#endif
}
-#endif
+#endif /* FIBER_USE_NATIVE */
NOINLINE(NORETURN(static void cont_restore_1(rb_context_t *)));
@@ -1001,7 +1280,7 @@ rollback_ensure_stack(VALUE self,rb_ensure_list_t *current,rb_ensure_entry_t *ta
{
rb_ensure_list_t *p;
rb_ensure_entry_t *entry;
- size_t i;
+ size_t i, j;
size_t cur_size;
size_t target_size;
size_t base_point;
@@ -1039,11 +1318,11 @@ rollback_ensure_stack(VALUE self,rb_ensure_list_t *current,rb_ensure_entry_t *ta
cur_size--;
}
/* push ensure stack */
- while (i--) {
- func = (VALUE (*)(ANYARGS)) lookup_rollback_func(target[i].e_proc);
- if ((VALUE)func != Qundef) {
- (*func)(target[i].data2);
- }
+ for (j = 0; j < i; j++) {
+ func = (VALUE (*)(ANYARGS)) lookup_rollback_func(target[i - j - 1].e_proc);
+ if ((VALUE)func != Qundef) {
+ (*func)(target[i - j - 1].data2);
+ }
}
}
@@ -1066,28 +1345,25 @@ rollback_ensure_stack(VALUE self,rb_ensure_list_t *current,rb_ensure_entry_t *ta
static VALUE
rb_cont_call(int argc, VALUE *argv, VALUE contval)
{
- rb_context_t *cont;
+ rb_context_t *cont = cont_ptr(contval);
rb_thread_t *th = GET_THREAD();
- GetContPtr(contval, cont);
- if (cont->saved_thread.self != th->self) {
+ if (cont_thread_value(cont) != th->self) {
rb_raise(rb_eRuntimeError, "continuation called across threads");
}
- if (cont->saved_thread.protect_tag != th->protect_tag) {
+ if (cont->saved_ec.protect_tag != th->ec->protect_tag) {
rb_raise(rb_eRuntimeError, "continuation called across stack rewinding barrier");
}
- if (cont->saved_thread.fiber) {
- if (th->fiber != cont->saved_thread.fiber) {
+ if (cont->saved_ec.fiber_ptr) {
+ if (th->ec->fiber_ptr != cont->saved_ec.fiber_ptr) {
rb_raise(rb_eRuntimeError, "continuation called across fiber");
}
}
- rollback_ensure_stack(contval, th->ensure_list, cont->ensure_array);
+ rollback_ensure_stack(contval, th->ec->ensure_list, cont->ensure_array);
cont->argc = argc;
cont->value = make_passing_arg(argc, argv);
- /* restore `tracing' context. see [Feature #4347] */
- th->trace_arg = cont->saved_thread.trace_arg;
cont_restore_0(cont, &contval);
return Qnil; /* unreachable */
}
@@ -1106,8 +1382,9 @@ rb_cont_call(int argc, VALUE *argv, VALUE contval)
* the programmer and not the VM.
*
* As opposed to other stackless light weight concurrency models, each fiber
- * comes with a small 4KB stack. This enables the fiber to be paused from deeply
- * nested function calls within the fiber block.
+ * comes with a stack. This enables the fiber to be paused from deeply
+ * nested function calls within the fiber block. See the ruby(1)
+ * manpage to configure the size of the fiber stack(s).
*
* When a fiber is created it will not run automatically. Rather it must
* be explicitly asked to run using the <code>Fiber#resume</code> method.
@@ -1185,8 +1462,12 @@ fiber_t_alloc(VALUE fibval)
fib->cont.self = fibval;
fib->cont.type = FIBER_CONTEXT;
cont_init(&fib->cont, th);
+ fib->cont.saved_ec.fiber_ptr = fib;
fib->prev = NULL;
- fib->status = CREATED;
+
+ /* fib->status == 0 == CREATED
+ * So that we don't need to set status: fiber_status_set(fib, FIBER_CREATED); */
+ VM_ASSERT(FIBER_CREATED_P(fib));
DATA_PTR(fibval) = fib;
@@ -1194,7 +1475,7 @@ fiber_t_alloc(VALUE fibval)
}
rb_control_frame_t *
-rb_vm_push_frame(rb_thread_t *th,
+rb_vm_push_frame(rb_execution_context_t *sec,
const rb_iseq_t *iseq,
VALUE type,
VALUE self,
@@ -1210,36 +1491,41 @@ fiber_init(VALUE fibval, VALUE proc)
{
rb_fiber_t *fib = fiber_t_alloc(fibval);
rb_context_t *cont = &fib->cont;
- rb_thread_t *th = &cont->saved_thread;
+ rb_execution_context_t *sec = &cont->saved_ec;
rb_thread_t *cth = GET_THREAD();
+ rb_vm_t *vm = cth->vm;
+ size_t fib_stack_bytes = vm->default_params.fiber_vm_stack_size;
+ size_t thr_stack_bytes = vm->default_params.thread_vm_stack_size;
+ VALUE *vm_stack;
/* initialize cont */
- cont->vm_stack = 0;
-
- th->stack = NULL;
- th->stack_size = 0;
-
- th->stack_size = cth->vm->default_params.fiber_vm_stack_size / sizeof(VALUE);
- th->stack = ALLOC_N(VALUE, th->stack_size);
- th->cfp = (void *)(th->stack + th->stack_size);
+ cont->saved_vm_stack.ptr = NULL;
+ if (fib_stack_bytes == thr_stack_bytes) {
+ vm_stack = rb_thread_recycle_stack(fib_stack_bytes / sizeof(VALUE));
+ }
+ else {
+ vm_stack = ruby_xmalloc(fib_stack_bytes);
+ }
+ rb_ec_set_vm_stack(sec, vm_stack, fib_stack_bytes / sizeof(VALUE));
+ sec->cfp = (void *)(sec->vm_stack + sec->vm_stack_size);
- rb_vm_push_frame(th,
+ rb_vm_push_frame(sec,
NULL,
VM_FRAME_MAGIC_DUMMY | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH | VM_FRAME_FLAG_CFRAME,
Qnil, /* self */
VM_BLOCK_HANDLER_NONE,
0, /* specval */
NULL, /* pc */
- th->stack, /* sp */
+ sec->vm_stack, /* sp */
0, /* local_size */
0);
- th->tag = 0;
- th->local_storage = st_init_numtable();
- th->local_storage_recursive_hash = Qnil;
- th->local_storage_recursive_hash_for_trace = Qnil;
+ sec->tag = NULL;
+ sec->local_storage = NULL;
+ sec->local_storage_recursive_hash = Qnil;
+ sec->local_storage_recursive_hash_for_trace = Qnil;
- th->first_proc = proc;
+ fib->first_proc = proc;
#if !FIBER_USE_NATIVE
MEMCPY(&cont->jmpbuf, &cth->root_jmpbuf, rb_jmpbuf_t, 1);
@@ -1261,77 +1547,130 @@ rb_fiber_new(VALUE (*func)(ANYARGS), VALUE obj)
return fiber_init(fiber_alloc(rb_cFiber), rb_proc_new(func, obj));
}
-static void rb_fiber_terminate(rb_fiber_t *fib);
+static void rb_fiber_terminate(rb_fiber_t *fib, int need_interrupt);
void
rb_fiber_start(void)
{
- rb_thread_t *th = GET_THREAD();
- rb_fiber_t *fib = th->fiber;
+ rb_thread_t * volatile th = GET_THREAD();
+ rb_fiber_t *fib = th->ec->fiber_ptr;
rb_proc_t *proc;
- int state;
+ enum ruby_tag_type state;
+ int need_interrupt = TRUE;
- TH_PUSH_TAG(th);
- if ((state = EXEC_TAG()) == 0) {
+ VM_ASSERT(th->ec == ruby_current_execution_context_ptr);
+ VM_ASSERT(FIBER_RESUMED_P(fib));
+
+ EC_PUSH_TAG(th->ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
rb_context_t *cont = &VAR_FROM_MEMORY(fib)->cont;
int argc;
const VALUE *argv, args = cont->value;
- GetProcPtr(cont->saved_thread.first_proc, proc);
+ GetProcPtr(fib->first_proc, proc);
argv = (argc = cont->argc) > 1 ? RARRAY_CONST_PTR(args) : &args;
cont->value = Qnil;
- th->errinfo = Qnil;
- th->root_lep = rb_vm_ep_local_ep(vm_block_ep(&proc->block));
- th->root_svar = Qfalse;
- fib->status = RUNNING;
+ th->ec->errinfo = Qnil;
+ th->ec->root_lep = rb_vm_proc_local_ep(fib->first_proc);
+ th->ec->root_svar = Qfalse;
- EXEC_EVENT_HOOK(th, RUBY_EVENT_FIBER_SWITCH, th->self, 0, 0, 0, Qnil);
- cont->value = rb_vm_invoke_proc(th, proc, argc, argv, VM_BLOCK_HANDLER_NONE);
+ EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_FIBER_SWITCH, th->self, 0, 0, 0, Qnil);
+ cont->value = rb_vm_invoke_proc(th->ec, proc, argc, argv, VM_BLOCK_HANDLER_NONE);
}
- TH_POP_TAG();
+ EC_POP_TAG();
if (state) {
+ VALUE err = th->ec->errinfo;
+ VM_ASSERT(FIBER_RESUMED_P(fib));
+
if (state == TAG_RAISE || state == TAG_FATAL) {
- rb_threadptr_pending_interrupt_enque(th, th->errinfo);
+ rb_threadptr_pending_interrupt_enque(th, err);
}
else {
- VALUE err = rb_vm_make_jump_tag_but_local_jump(state, th->errinfo);
- if (!NIL_P(err))
+ err = rb_vm_make_jump_tag_but_local_jump(state, err);
+ if (!NIL_P(err)) {
rb_threadptr_pending_interrupt_enque(th, err);
+ }
}
- RUBY_VM_SET_INTERRUPT(th);
+ need_interrupt = TRUE;
}
- rb_fiber_terminate(fib);
- rb_bug("rb_fiber_start: unreachable");
+ rb_fiber_terminate(fib, need_interrupt);
+ VM_UNREACHABLE(rb_fiber_start);
}
static rb_fiber_t *
root_fiber_alloc(rb_thread_t *th)
{
- rb_fiber_t *fib;
- /* no need to allocate vm stack */
- fib = fiber_t_alloc(fiber_alloc(rb_cFiber));
- fib->cont.type = ROOT_FIBER_CONTEXT;
+ VALUE fibval = fiber_alloc(rb_cFiber);
+ rb_fiber_t *fib = th->ec->fiber_ptr;
+
+ VM_ASSERT(DATA_PTR(fibval) == NULL);
+ VM_ASSERT(fib->cont.type == FIBER_CONTEXT);
+ VM_ASSERT(fib->status == FIBER_RESUMED);
+
+ th->root_fiber = fib;
+ DATA_PTR(fibval) = fib;
+ fib->cont.self = fibval;
+
#if FIBER_USE_NATIVE
-#ifdef _WIN32
- fib->fib_handle = ConvertThreadToFiber(0);
+#if defined(FIBER_USE_COROUTINE)
+ coroutine_initialize(&fib->context, NULL, NULL, 0);
+#elif defined(_WIN32)
+ /* setup fib_handle for root Fiber */
+ if (fib->fib_handle == 0) {
+ if ((fib->fib_handle = ConvertThreadToFiber(0)) == 0) {
+ rb_bug("root_fiber_alloc: ConvertThreadToFiber() failed - %s\n", rb_w32_strerror(-1));
+ }
+ }
+ else {
+ rb_bug("root_fiber_alloc: fib_handle is not NULL.");
+ }
#endif
#endif
- fib->status = RUNNING;
return fib;
}
+void
+rb_threadptr_root_fiber_setup(rb_thread_t *th)
+{
+ rb_fiber_t *fib = ruby_mimmalloc(sizeof(rb_fiber_t));
+ MEMZERO(fib, rb_fiber_t, 1);
+ fib->cont.type = FIBER_CONTEXT;
+ fib->cont.saved_ec.fiber_ptr = fib;
+ fib->cont.saved_ec.thread_ptr = th;
+ fiber_status_set(fib, FIBER_RESUMED); /* skip CREATED */
+ th->ec = &fib->cont.saved_ec;
+
+ /* NOTE: On WIN32, fib_handle is not allocated yet. */
+}
+
+void
+rb_threadptr_root_fiber_release(rb_thread_t *th)
+{
+ if (th->root_fiber) {
+ /* ignore. A root fiber object will free th->ec */
+ }
+ else {
+ VM_ASSERT(th->ec->fiber_ptr->cont.type == FIBER_CONTEXT);
+ VM_ASSERT(th->ec->fiber_ptr->cont.self == 0);
+ fiber_free(th->ec->fiber_ptr);
+
+ if (th->ec == ruby_current_execution_context_ptr) {
+ ruby_current_execution_context_ptr = NULL;
+ }
+ th->ec = NULL;
+ }
+}
+
static inline rb_fiber_t*
fiber_current(void)
{
- rb_thread_t *th = GET_THREAD();
- if (th->fiber == 0) {
- /* save root */
- rb_fiber_t *fib = root_fiber_alloc(th);
- th->root_fiber = th->fiber = fib;
+ rb_execution_context_t *ec = GET_EC();
+ if (ec->fiber_ptr->cont.self == 0) {
+ root_fiber_alloc(rb_ec_thread_ptr(ec));
}
- return th->fiber;
+ return ec->fiber_ptr;
}
static inline rb_fiber_t*
@@ -1341,7 +1680,10 @@ return_fiber(void)
rb_fiber_t *prev = fib->prev;
if (!prev) {
- rb_fiber_t *root_fiber = GET_THREAD()->root_fiber;
+ rb_thread_t *th = GET_THREAD();
+ rb_fiber_t *root_fiber = th->root_fiber;
+
+ VM_ASSERT(root_fiber != NULL);
if (root_fiber == fib) {
rb_raise(rb_eFiberError, "can't yield from root fiber");
@@ -1365,58 +1707,76 @@ fiber_store(rb_fiber_t *next_fib, rb_thread_t *th)
{
rb_fiber_t *fib;
- if (th->fiber) {
- fib = th->fiber;
- cont_save_thread(&fib->cont, th);
+ if (th->ec->fiber_ptr != NULL) {
+ fib = th->ec->fiber_ptr;
}
else {
- /* create current fiber */
- fib = root_fiber_alloc(th);
- th->root_fiber = th->fiber = fib;
+ /* create root fiber */
+ fib = root_fiber_alloc(th);
+ }
+
+ VM_ASSERT(FIBER_RESUMED_P(fib) || FIBER_TERMINATED_P(fib));
+ VM_ASSERT(FIBER_RUNNABLE_P(next_fib));
+
+#if FIBER_USE_NATIVE
+ if (FIBER_CREATED_P(next_fib)) {
+ fiber_initialize_machine_stack_context(next_fib, th->vm->default_params.fiber_machine_stack_size);
}
+#endif
+
+ if (FIBER_RESUMED_P(fib)) fiber_status_set(fib, FIBER_SUSPENDED);
+
+#if FIBER_USE_NATIVE == 0
+ /* should (re-)allocate stack are before fib->status change to pass fiber_verify() */
+ cont_save_machine_stack(th, &fib->cont);
+#endif
+
+ fiber_status_set(next_fib, FIBER_RESUMED);
#if FIBER_USE_NATIVE
fiber_setcontext(next_fib, fib);
/* restored */
-#ifndef _WIN32
+#ifdef MAX_MACHINE_STACK_CACHE
if (terminated_machine_stack.ptr) {
- if (machine_stack_cache_index < MAX_MACHINE_STACK_CACHE) {
- machine_stack_cache[machine_stack_cache_index].ptr = terminated_machine_stack.ptr;
- machine_stack_cache[machine_stack_cache_index].size = terminated_machine_stack.size;
- machine_stack_cache_index++;
- }
- else {
- if (terminated_machine_stack.ptr != fib->cont.machine.stack) {
- munmap((void*)terminated_machine_stack.ptr, terminated_machine_stack.size * sizeof(VALUE));
- }
- else {
- rb_bug("terminated fiber resumed");
- }
- }
- terminated_machine_stack.ptr = NULL;
- terminated_machine_stack.size = 0;
+ if (machine_stack_cache_index < MAX_MACHINE_STACK_CACHE) {
+ machine_stack_cache[machine_stack_cache_index++] = terminated_machine_stack;
+ }
+ else {
+ if (terminated_machine_stack.ptr != fib->cont.machine.stack) {
+#ifdef _WIN32
+ VirtualFree(terminated_machine_stack.ptr, 0, MEM_RELEASE);
+#else
+ munmap((void*)terminated_machine_stack.ptr, terminated_machine_stack.size * sizeof(VALUE));
+#endif
+ }
+ else {
+ rb_bug("terminated fiber resumed");
+ }
+ }
+ terminated_machine_stack.ptr = NULL;
+ terminated_machine_stack.size = 0;
}
#endif /* not _WIN32 */
- fib = th->fiber;
+ fib = th->ec->fiber_ptr;
if (fib->cont.argc == -1) rb_exc_raise(fib->cont.value);
return fib->cont.value;
#else /* FIBER_USE_NATIVE */
- cont_save_machine_stack(th, &fib->cont);
+ fib->cont.saved_ec.machine.stack_end = NULL;
if (ruby_setjmp(fib->cont.jmpbuf)) {
- /* restored */
- fib = th->fiber;
- if (fib->cont.argc == -1) rb_exc_raise(fib->cont.value);
- if (next_fib->cont.value == Qundef) {
- cont_restore_0(&next_fib->cont, &next_fib->cont.value);
- rb_bug("rb_fiber_resume: unreachable");
- }
- return fib->cont.value;
+ /* restored */
+ fib = th->ec->fiber_ptr;
+ if (fib->cont.argc == -1) rb_exc_raise(fib->cont.value);
+ if (next_fib->cont.value == Qundef) {
+ cont_restore_0(&next_fib->cont, &next_fib->cont.value);
+ VM_UNREACHABLE(fiber_store);
+ }
+ return fib->cont.value;
}
else {
- VALUE undef = Qundef;
- cont_restore_0(&next_fib->cont, &undef);
- rb_bug("rb_fiber_resume: unreachable");
+ VALUE undef = Qundef;
+ cont_restore_0(&next_fib->cont, &undef);
+ VM_UNREACHABLE(fiber_store);
}
#endif /* FIBER_USE_NATIVE */
}
@@ -1428,53 +1788,59 @@ fiber_switch(rb_fiber_t *fib, int argc, const VALUE *argv, int is_resume)
rb_context_t *cont = &fib->cont;
rb_thread_t *th = GET_THREAD();
- if (th->fiber == fib) {
+ /* make sure the root_fiber object is available */
+ if (th->root_fiber == NULL) root_fiber_alloc(th);
+
+ if (th->ec->fiber_ptr == fib) {
/* ignore fiber context switch
* because destination fiber is same as current fiber
*/
return make_passing_arg(argc, argv);
}
- if (cont->saved_thread.self != th->self) {
+ if (cont_thread_value(cont) != th->self) {
rb_raise(rb_eFiberError, "fiber called across threads");
}
- else if (cont->saved_thread.protect_tag != th->protect_tag) {
+ else if (cont->saved_ec.protect_tag != th->ec->protect_tag) {
rb_raise(rb_eFiberError, "fiber called across stack rewinding barrier");
}
- else if (fib->status == TERMINATED) {
+ else if (FIBER_TERMINATED_P(fib)) {
value = rb_exc_new2(rb_eFiberError, "dead fiber called");
- if (th->fiber->status != TERMINATED) rb_exc_raise(value);
-
- /* th->fiber is also dead => switch to root fiber */
- /* (this means we're being called from rb_fiber_terminate, */
- /* and the terminated fiber's return_fiber() is already dead) */
- cont = &th->root_fiber->cont;
- cont->argc = -1;
- cont->value = value;
+ if (!FIBER_TERMINATED_P(th->ec->fiber_ptr)) {
+ rb_exc_raise(value);
+ VM_UNREACHABLE(fiber_switch);
+ }
+ else {
+ /* th->ec->fiber_ptr is also dead => switch to root fiber */
+ /* (this means we're being called from rb_fiber_terminate, */
+ /* and the terminated fiber's return_fiber() is already dead) */
+ VM_ASSERT(FIBER_SUSPENDED_P(th->root_fiber));
+
+ cont = &th->root_fiber->cont;
+ cont->argc = -1;
+ cont->value = value;
#if FIBER_USE_NATIVE
- fiber_setcontext(th->root_fiber, th->fiber);
+ fiber_setcontext(th->root_fiber, th->ec->fiber_ptr);
#else
- cont_restore_0(cont, &value);
+ cont_restore_0(cont, &value);
#endif
- /* unreachable */
+ VM_UNREACHABLE(fiber_switch);
+ }
}
if (is_resume) {
fib->prev = fiber_current();
}
- else {
- /* restore `tracing' context. see [Feature #4347] */
- th->trace_arg = cont->saved_thread.trace_arg;
- }
+
+ VM_ASSERT(FIBER_RUNNABLE_P(fib));
cont->argc = argc;
cont->value = make_passing_arg(argc, argv);
-
value = fiber_store(fib, th);
- RUBY_VM_CHECK_INTS(th);
+ RUBY_VM_CHECK_INTS(th->ec);
- EXEC_EVENT_HOOK(th, RUBY_EVENT_FIBER_SWITCH, th->self, 0, 0, 0, Qnil);
+ EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_FIBER_SWITCH, th->self, 0, 0, 0, Qnil);
return value;
}
@@ -1482,35 +1848,68 @@ fiber_switch(rb_fiber_t *fib, int argc, const VALUE *argv, int is_resume)
VALUE
rb_fiber_transfer(VALUE fibval, int argc, const VALUE *argv)
{
- rb_fiber_t *fib;
- GetFiberPtr(fibval, fib);
- return fiber_switch(fib, argc, argv, 0);
+ return fiber_switch(fiber_ptr(fibval), argc, argv, 0);
+}
+
+void
+rb_fiber_close(rb_fiber_t *fib)
+{
+ rb_execution_context_t *ec = &fib->cont.saved_ec;
+ VALUE *vm_stack = ec->vm_stack;
+ size_t stack_bytes = ec->vm_stack_size * sizeof(VALUE);
+
+ fiber_status_set(fib, FIBER_TERMINATED);
+ if (stack_bytes == rb_ec_vm_ptr(ec)->default_params.thread_vm_stack_size) {
+ rb_thread_recycle_stack_release(vm_stack);
+ }
+ else {
+ ruby_xfree(vm_stack);
+ }
+ rb_ec_set_vm_stack(ec, NULL, 0);
+
+#if !FIBER_USE_NATIVE
+ /* should not mark machine stack any more */
+ ec->machine.stack_end = NULL;
+#endif
}
static void
-rb_fiber_terminate(rb_fiber_t *fib)
+rb_fiber_terminate(rb_fiber_t *fib, int need_interrupt)
{
VALUE value = fib->cont.value;
- fib->status = TERMINATED;
-#if FIBER_USE_NATIVE && !defined(_WIN32)
+ rb_fiber_t *ret_fib;
+
+ VM_ASSERT(FIBER_RESUMED_P(fib));
+ rb_fiber_close(fib);
+
+#if FIBER_USE_NATIVE
+#if defined(FIBER_USE_COROUTINE)
+ coroutine_destroy(&fib->context);
+#elif !defined(_WIN32)
+ fib->context.uc_stack.ss_sp = NULL;
+#endif
+
+#ifdef MAX_MACHINE_STACK_CACHE
/* Ruby must not switch to other thread until storing terminated_machine_stack */
terminated_machine_stack.ptr = fib->ss_sp;
terminated_machine_stack.size = fib->ss_size / sizeof(VALUE);
fib->ss_sp = NULL;
- fib->context.uc_stack.ss_sp = NULL;
fib->cont.machine.stack = NULL;
fib->cont.machine.stack_size = 0;
#endif
- fiber_switch(return_fiber(), 1, &value, 0);
+#endif
+
+ ret_fib = return_fiber();
+ if (need_interrupt) RUBY_VM_SET_INTERRUPT(&ret_fib->cont.saved_ec);
+ fiber_switch(ret_fib, 1, &value, 0);
}
VALUE
rb_fiber_resume(VALUE fibval, int argc, const VALUE *argv)
{
- rb_fiber_t *fib;
- GetFiberPtr(fibval, fib);
+ rb_fiber_t *fib = fiber_ptr(fibval);
- if (fib->prev != 0 || fib->cont.type == ROOT_FIBER_CONTEXT) {
+ if (fib->prev != 0 || fiber_is_root_p(fib)) {
rb_raise(rb_eFiberError, "double resume");
}
if (fib->transferred != 0) {
@@ -1527,13 +1926,10 @@ rb_fiber_yield(int argc, const VALUE *argv)
}
void
-rb_fiber_reset_root_local_storage(VALUE thval)
+rb_fiber_reset_root_local_storage(rb_thread_t *th)
{
- rb_thread_t *th;
-
- GetThreadPtr(thval, th);
- if (th->root_fiber && th->root_fiber != th->fiber) {
- th->local_storage = th->root_fiber->cont.saved_thread.local_storage;
+ if (th->root_fiber && th->root_fiber != th->ec->fiber_ptr) {
+ th->ec->local_storage = th->root_fiber->cont.saved_ec.local_storage;
}
}
@@ -1549,9 +1945,7 @@ rb_fiber_reset_root_local_storage(VALUE thval)
VALUE
rb_fiber_alive_p(VALUE fibval)
{
- rb_fiber_t *fib;
- GetFiberPtr(fibval, fib);
- return fib->status != TERMINATED ? Qtrue : Qfalse;
+ return FIBER_TERMINATED_P(fiber_ptr(fibval)) ? Qfalse : Qtrue;
}
/*
@@ -1623,8 +2017,7 @@ rb_fiber_m_resume(int argc, VALUE *argv, VALUE fib)
static VALUE
rb_fiber_m_transfer(int argc, VALUE *argv, VALUE fibval)
{
- rb_fiber_t *fib;
- GetFiberPtr(fibval, fib);
+ rb_fiber_t *fib = fiber_ptr(fibval);
fib->transferred = 1;
return fiber_switch(fib, argc, argv, 0);
}
@@ -1659,7 +2052,45 @@ rb_fiber_s_current(VALUE klass)
return rb_fiber_current();
}
+/*
+ * call-seq:
+ * fiber.to_s -> string
+ *
+ * Returns fiber information string.
+ *
+ */
+
+static VALUE
+fiber_to_s(VALUE fibval)
+{
+ const rb_fiber_t *fib = fiber_ptr(fibval);
+ const rb_proc_t *proc;
+ char status_info[0x10];
+
+ snprintf(status_info, 0x10, " (%s)", fiber_status_name(fib->status));
+ if (!rb_obj_is_proc(fib->first_proc)) {
+ VALUE str = rb_any_to_s(fibval);
+ strlcat(status_info, ">", sizeof(status_info));
+ rb_str_set_len(str, RSTRING_LEN(str)-1);
+ rb_str_cat_cstr(str, status_info);
+ return str;
+ }
+ GetProcPtr(fib->first_proc, proc);
+ return rb_block_to_s(fibval, &proc->block, status_info);
+}
+#ifdef HAVE_WORKING_FORK
+void
+rb_fiber_atfork(rb_thread_t *th)
+{
+ if (th->root_fiber) {
+ if (&th->root_fiber->cont.saved_ec != th->ec) {
+ th->root_fiber = th->ec->fiber_ptr;
+ }
+ th->root_fiber->prev = 0;
+ }
+}
+#endif
/*
* Document-class: FiberError
@@ -1687,7 +2118,7 @@ Init_Cont(void)
#else /* not WIN32 */
pagesize = sysconf(_SC_PAGESIZE);
#endif
- SET_MACHINE_STACK_END(&th->machine.stack_end);
+ SET_MACHINE_STACK_END(&th->ec->machine.stack_end);
#endif
rb_cFiber = rb_define_class("Fiber", rb_cObject);
@@ -1696,6 +2127,8 @@ Init_Cont(void)
rb_define_singleton_method(rb_cFiber, "yield", rb_fiber_s_yield, -1);
rb_define_method(rb_cFiber, "initialize", rb_fiber_init, 0);
rb_define_method(rb_cFiber, "resume", rb_fiber_m_resume, -1);
+ rb_define_method(rb_cFiber, "to_s", fiber_to_s, 0);
+ rb_define_alias(rb_cFiber, "inspect", "to_s");
}
RUBY_SYMBOL_EXPORT_BEGIN
diff --git a/coroutine/amd64/Context.S b/coroutine/amd64/Context.S
new file mode 100644
index 0000000000..6193993e03
--- /dev/null
+++ b/coroutine/amd64/Context.S
@@ -0,0 +1,46 @@
+##
+## This file is part of the "Coroutine" project and released under the MIT License.
+##
+## Created by Samuel Williams on 10/5/2018.
+## Copyright, 2018, by Samuel Williams. All rights reserved.
+##
+
+.text
+
+# For older linkers
+.globl _coroutine_transfer
+_coroutine_transfer:
+
+.globl coroutine_transfer
+coroutine_transfer:
+ # Save caller state
+ pushq %rbp
+ pushq %rbx
+ pushq %r12
+ pushq %r13
+ pushq %r14
+ pushq %r15
+
+ # Save caller stack pointer
+ movq %rsp, (%rdi)
+
+ # Restore callee stack pointer
+ movq (%rsi), %rsp
+
+ # Restore callee stack
+ popq %r15
+ popq %r14
+ popq %r13
+ popq %r12
+ popq %rbx
+ popq %rbp
+
+ # Put the first argument into the return value
+ movq %rdi, %rax
+
+ # We pop the return address and jump to it
+ ret
+
+#if defined(__linux__) && defined(__ELF__)
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/coroutine/amd64/Context.h b/coroutine/amd64/Context.h
new file mode 100644
index 0000000000..1801c1e2c5
--- /dev/null
+++ b/coroutine/amd64/Context.h
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the "Coroutine" project and released under the MIT License.
+ *
+ * Created by Samuel Williams on 10/5/2018.
+ * Copyright, 2018, by Samuel Williams. All rights reserved.
+*/
+
+#pragma once
+
+#include <assert.h>
+#include <string.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+#define COROUTINE __attribute__((noreturn)) void
+
+const size_t COROUTINE_REGISTERS = 6;
+
+typedef struct
+{
+ void **stack_pointer;
+} coroutine_context;
+
+typedef COROUTINE(* coroutine_start)(coroutine_context *from, coroutine_context *self);
+
+static inline void coroutine_initialize(
+ coroutine_context *context,
+ coroutine_start start,
+ void *stack_pointer,
+ size_t stack_size
+) {
+ /* Force 16-byte alignment */
+ context->stack_pointer = (void**)((uintptr_t)stack_pointer & ~0xF);
+
+ if (!start) {
+ assert(!context->stack_pointer);
+ /* We are main coroutine for this thread */
+ return;
+ }
+
+ *--context->stack_pointer = NULL;
+ *--context->stack_pointer = (void*)start;
+
+ context->stack_pointer -= COROUTINE_REGISTERS;
+ memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
+}
+
+coroutine_context * coroutine_transfer(coroutine_context * current, coroutine_context * target);
+
+static inline void coroutine_destroy(coroutine_context * context)
+{
+ context->stack_pointer = NULL;
+}
+
+#if __cplusplus
+}
+#endif
diff --git a/coroutine/arm32/Context.S b/coroutine/arm32/Context.S
new file mode 100644
index 0000000000..c2b93d0a34
--- /dev/null
+++ b/coroutine/arm32/Context.S
@@ -0,0 +1,14 @@
+##
+## This file is part of the "Coroutine" project and released under the MIT License.
+##
+## Created by Samuel Williams on 10/5/2018.
+## Copyright, 2018, by Samuel Williams. All rights reserved.
+##
+
+.text
+
+.globl coroutine_transfer
+coroutine_transfer:
+ stmia r1!, {r4-r11,sp,lr}
+ ldmia r0!, {r4-r11,sp,pc}
+ bx lr
diff --git a/coroutine/arm32/Context.h b/coroutine/arm32/Context.h
new file mode 100644
index 0000000000..60732df7a0
--- /dev/null
+++ b/coroutine/arm32/Context.h
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the "Coroutine" project and released under the MIT License.
+ *
+ * Created by Samuel Williams on 10/5/2018.
+ * Copyright, 2018, by Samuel Williams. All rights reserved.
+*/
+
+#pragma once
+
+#include <assert.h>
+#include <string.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+#define COROUTINE __attribute__((noreturn)) void
+
+const size_t COROUTINE_REGISTERS = 9;
+
+typedef struct
+{
+ void **stack_pointer;
+} coroutine_context;
+
+typedef COROUTINE(* coroutine_start)(coroutine_context *from, coroutine_context *self);
+
+static inline void coroutine_initialize(
+ coroutine_context *context,
+ coroutine_start start,
+ void *stack_pointer,
+ size_t stack_size
+) {
+ context->stack_pointer = (void**)stack_pointer;
+
+ if (!start) {
+ assert(!context->stack_pointer);
+ /* We are main coroutine for this thread */
+ return;
+ }
+
+ *--context->stack_pointer = (void*)start;
+
+ context->stack_pointer -= COROUTINE_REGISTERS;
+ memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
+}
+
+coroutine_context * coroutine_transfer(coroutine_context * current, coroutine_context * target);
+
+static inline void coroutine_destroy(coroutine_context * context)
+{
+}
+
+#if __cplusplus
+}
+#endif
diff --git a/coroutine/arm64/Context.S b/coroutine/arm64/Context.S
new file mode 100644
index 0000000000..f6e5f0a6bc
--- /dev/null
+++ b/coroutine/arm64/Context.S
@@ -0,0 +1,59 @@
+##
+## This file is part of the "Coroutine" project and released under the MIT License.
+##
+## Created by Samuel Williams on 10/5/2018.
+## Copyright, 2018, by Samuel Williams. All rights reserved.
+##
+
+.text
+.align 2
+
+.global coroutine_transfer
+coroutine_transfer:
+
+ # Make space on the stack for caller registers
+ sub sp, sp, 0xb0
+
+ # Save caller registers
+ stp d8, d9, [sp, 0x00]
+ stp d10, d11, [sp, 0x10]
+ stp d12, d13, [sp, 0x20]
+ stp d14, d15, [sp, 0x30]
+ stp x19, x20, [sp, 0x40]
+ stp x21, x22, [sp, 0x50]
+ stp x23, x24, [sp, 0x60]
+ stp x25, x26, [sp, 0x70]
+ stp x27, x28, [sp, 0x80]
+ stp x29, x30, [sp, 0x90]
+
+ # Save return address
+ str x30, [sp, 0xa0]
+
+ # Save stack pointer to x0 (first argument)
+ mov x2, sp
+ str x2, [x0, 0]
+
+ # Load stack pointer from x1 (second argument)
+ ldr x3, [x1, 0]
+ mov sp, x3
+
+ # Restore caller registers
+ ldp d8, d9, [sp, 0x00]
+ ldp d10, d11, [sp, 0x10]
+ ldp d12, d13, [sp, 0x20]
+ ldp d14, d15, [sp, 0x30]
+ ldp x19, x20, [sp, 0x40]
+ ldp x21, x22, [sp, 0x50]
+ ldp x23, x24, [sp, 0x60]
+ ldp x25, x26, [sp, 0x70]
+ ldp x27, x28, [sp, 0x80]
+ ldp x29, x30, [sp, 0x90]
+
+ # Load return address into x4
+ ldr x4, [sp, 0xa0]
+
+ # Pop stack frame
+ add sp, sp, 0xb0
+
+ # Jump to return address (in x4)
+ ret x4
diff --git a/coroutine/arm64/Context.h b/coroutine/arm64/Context.h
new file mode 100644
index 0000000000..03b91fd937
--- /dev/null
+++ b/coroutine/arm64/Context.h
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the "Coroutine" project and released under the MIT License.
+ *
+ * Created by Samuel Williams on 10/5/2018.
+ * Copyright, 2018, by Samuel Williams. All rights reserved.
+*/
+
+#pragma once
+
+#include <assert.h>
+#include <string.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+#define COROUTINE __attribute__((noreturn)) void
+
+const size_t COROUTINE_REGISTERS = 0xb0 / 8;
+
+typedef struct
+{
+ void **stack_pointer;
+} coroutine_context;
+
+typedef COROUTINE(* coroutine_start)(coroutine_context *from, coroutine_context *self);
+
+static inline void coroutine_initialize(
+ coroutine_context *context,
+ coroutine_start start,
+ void *stack_pointer,
+ size_t stack_size
+) {
+ /* Force 16-byte alignment */
+ context->stack_pointer = (void**)((uintptr_t)stack_pointer & ~0xF);
+
+ if (!start) {
+ assert(!context->stack_pointer);
+ /* We are main coroutine for this thread */
+ return;
+ }
+
+ context->stack_pointer -= COROUTINE_REGISTERS;
+ memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
+
+ context->stack_pointer[0xa0 / 8] = (void*)start;
+}
+
+coroutine_context * coroutine_transfer(coroutine_context * current, coroutine_context * target);
+
+static inline void coroutine_destroy(coroutine_context * context)
+{
+}
+
+#if __cplusplus
+}
+#endif
diff --git a/coroutine/ppc64le/Context.S b/coroutine/ppc64le/Context.S
new file mode 100644
index 0000000000..1b39086f8f
--- /dev/null
+++ b/coroutine/ppc64le/Context.S
@@ -0,0 +1,72 @@
+.text
+.align 2
+
+.globl coroutine_transfer
+.type coroutine_transfer, @function
+coroutine_transfer:
+ # Make space on the stack for caller registers
+ addi 1,1,-152
+
+ # Save caller registers
+ std 14,0(1)
+ std 15,8(1)
+ std 16,16(1)
+ std 17,24(1)
+ std 18,32(1)
+ std 19,40(1)
+ std 20,48(1)
+ std 21,56(1)
+ std 22,64(1)
+ std 23,72(1)
+ std 24,80(1)
+ std 25,88(1)
+ std 26,96(1)
+ std 27,104(1)
+ std 28,112(1)
+ std 29,120(1)
+ std 30,128(1)
+ std 31,136(1)
+
+ # Save return address
+ mflr 0
+ std 0,144(1)
+
+ # Save stack pointer to first argument
+ std 1,0(3)
+
+ # Load stack pointer from second argument
+ ld 1,0(4)
+
+ # Restore caller registers
+ ld 14,0(1)
+ ld 15,8(1)
+ ld 16,16(1)
+ ld 17,24(1)
+ ld 18,32(1)
+ ld 19,40(1)
+ ld 20,48(1)
+ ld 21,56(1)
+ ld 22,64(1)
+ ld 23,72(1)
+ ld 24,80(1)
+ ld 25,88(1)
+ ld 26,96(1)
+ ld 27,104(1)
+ ld 28,112(1)
+ ld 29,120(1)
+ ld 30,128(1)
+ ld 31,136(1)
+
+ # Load return address
+ ld 0,144(1)
+ mtlr 0
+
+ # Pop stack frame
+ addi 1,1,152
+
+ # Jump to return address
+ blr
+
+#if defined(__linux__) && defined(__ELF__)
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/coroutine/ppc64le/Context.h b/coroutine/ppc64le/Context.h
new file mode 100644
index 0000000000..de592f5a46
--- /dev/null
+++ b/coroutine/ppc64le/Context.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <assert.h>
+#include <string.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+#define COROUTINE __attribute__((noreturn)) void
+
+const size_t COROUTINE_REGISTERS =
+ 19 /* 18 general purpose registers (r14-r31) and 1 return address */
+ + 4; /* space for fiber_entry() to store the link register */
+
+typedef struct
+{
+ void **stack_pointer;
+} coroutine_context;
+
+typedef COROUTINE(* coroutine_start)(coroutine_context *from, coroutine_context *self);
+
+static inline void coroutine_initialize(
+ coroutine_context *context,
+ coroutine_start start,
+ void *stack_pointer,
+ size_t stack_size
+) {
+ /* Force 16-byte alignment */
+ context->stack_pointer = (void**)((uintptr_t)stack_pointer & ~0xF);
+
+ if (!start) {
+ assert(!context->stack_pointer);
+ /* We are main coroutine for this thread */
+ return;
+ }
+
+ context->stack_pointer -= COROUTINE_REGISTERS;
+ 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;
+}
+
+coroutine_context * coroutine_transfer(coroutine_context * current, coroutine_context * target);
+
+static inline void coroutine_destroy(coroutine_context * context)
+{
+ context->stack_pointer = NULL;
+}
+
+#if __cplusplus
+}
+#endif
diff --git a/coroutine/win32/Context.asm b/coroutine/win32/Context.asm
new file mode 100644
index 0000000000..2647ea4bc4
--- /dev/null
+++ b/coroutine/win32/Context.asm
@@ -0,0 +1,55 @@
+;;
+;; This file is part of the "Coroutine" project and released under the MIT License.
+;;
+;; Created by Samuel Williams on 10/5/2018.
+;; Copyright, 2018, by Samuel Williams. All rights reserved.
+;;
+
+.386
+.model flat
+
+.code
+
+assume fs:nothing
+
+; Using fastcall is a big win (and it's the same has how x64 works).
+; In coroutine transfer, the arguments are passed in ecx and edx. We don't need
+; to touch these in order to pass them to the destination coroutine.
+
+@coroutine_transfer@8 proc
+ ; Save the thread information block:
+ push fs:[0]
+ push fs:[4]
+ push fs:[8]
+
+ ; Save caller registers:
+ push ebp
+ push ebx
+ push edi
+ push esi
+
+ ; Save caller stack pointer:
+ mov dword ptr [ecx], esp
+
+ ; Restore callee stack pointer:
+ mov esp, dword ptr [edx]
+
+ ; Restore callee stack:
+ pop esi
+ pop edi
+ pop ebx
+ pop ebp
+
+ ; Restore the thread information block:
+ pop fs:[8]
+ pop fs:[4]
+ pop fs:[0]
+
+ ; Save the first argument as the return value:
+ mov eax, dword ptr ecx
+
+ ; Jump to the address on the stack:
+ ret
+@coroutine_transfer@8 endp
+
+end
diff --git a/coroutine/win32/Context.h b/coroutine/win32/Context.h
new file mode 100644
index 0000000000..aa9f17ddab
--- /dev/null
+++ b/coroutine/win32/Context.h
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the "Coroutine" project and released under the MIT License.
+ *
+ * Created by Samuel Williams on 10/5/2018.
+ * Copyright, 2018, by Samuel Williams. All rights reserved.
+*/
+
+#pragma once
+
+#include <assert.h>
+#include <string.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+#define COROUTINE __declspec(noreturn) void __fastcall
+
+/* This doesn't include thread information block */
+const size_t COROUTINE_REGISTERS = 4;
+
+typedef struct
+{
+ void **stack_pointer;
+} coroutine_context;
+
+typedef void(__fastcall * coroutine_start)(coroutine_context *from, coroutine_context *self);
+
+static inline void coroutine_initialize(
+ coroutine_context *context,
+ coroutine_start start,
+ void *stack_pointer,
+ size_t stack_size
+) {
+ context->stack_pointer = (void**)stack_pointer;
+
+ if (!start) {
+ assert(!context->stack_pointer);
+ /* We are main coroutine for this thread */
+ return;
+ }
+
+ *--context->stack_pointer = (void*)start;
+
+ /* Windows Thread Information Block */
+ *--context->stack_pointer = 0; /* fs:[0] */
+ *--context->stack_pointer = (void*)stack_pointer; /* fs:[4] */
+ *--context->stack_pointer = (void*)((char *)stack_pointer - stack_size); /* fs:[8] */
+
+ context->stack_pointer -= COROUTINE_REGISTERS;
+ memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
+}
+
+coroutine_context * __fastcall coroutine_transfer(coroutine_context * current, coroutine_context * target);
+
+static inline void coroutine_destroy(coroutine_context * context)
+{
+}
+
+#if __cplusplus
+}
+#endif
diff --git a/coroutine/win64/Context.S b/coroutine/win64/Context.S
new file mode 100644
index 0000000000..4b16e0ce8c
--- /dev/null
+++ b/coroutine/win64/Context.S
@@ -0,0 +1,77 @@
+##
+## This file is part of the "Coroutine" project and released under the MIT License.
+##
+## Created by Samuel Williams on 4/11/2018.
+## Copyright, 2018, by Samuel Williams. All rights reserved.
+##
+
+.text
+
+.globl coroutine_transfer
+coroutine_transfer:
+ # Save the thread information block:
+ pushq %gs:8
+ pushq %gs:16
+
+ # Save caller registers:
+ pushq %rbp
+ pushq %rbx
+ pushq %rdi
+ pushq %rsi
+ pushq %r12
+ pushq %r13
+ pushq %r14
+ pushq %r15
+
+ movaps %xmm15, -168(%rsp)
+ movaps %xmm14, -152(%rsp)
+ movaps %xmm13, -136(%rsp)
+ movaps %xmm12, -120(%rsp)
+ movaps %xmm11, -104(%rsp)
+ movaps %xmm10, -88(%rsp)
+ movaps %xmm9, -72(%rsp)
+ movaps %xmm8, -56(%rsp)
+ movaps %xmm7, -40(%rsp)
+ movaps %xmm6, -24(%rsp)
+
+ # Save caller stack pointer:
+ mov %rsp, (%rcx)
+
+ # Restore callee stack pointer:
+ mov (%rdx), %rsp
+
+ movaps -24(%rsp), %xmm6
+ movaps -40(%rsp), %xmm7
+ movaps -56(%rsp), %xmm8
+ movaps -72(%rsp), %xmm9
+ movaps -88(%rsp), %xmm10
+ movaps -104(%rsp), %xmm11
+ movaps -120(%rsp), %xmm12
+ movaps -136(%rsp), %xmm13
+ movaps -152(%rsp), %xmm14
+ movaps -168(%rsp), %xmm15
+
+ # Restore callee stack:
+ popq %r15
+ popq %r14
+ popq %r13
+ popq %r12
+ popq %rsi
+ popq %rdi
+ popq %rbx
+ popq %rbp
+
+ # Restore the thread information block:
+ popq %gs:16
+ popq %gs:8
+
+ # Put the first argument into the return value:
+ mov %rcx, %rax
+
+ # We pop the return address and jump to it:
+ ret
+
+.globl coroutine_trampoline
+coroutine_trampoline:
+ # Do not remove this. This forces 16-byte alignment when entering the coroutine.
+ ret
diff --git a/coroutine/win64/Context.asm b/coroutine/win64/Context.asm
new file mode 100644
index 0000000000..59673ffa3e
--- /dev/null
+++ b/coroutine/win64/Context.asm
@@ -0,0 +1,79 @@
+;;
+;; This file is part of the "Coroutine" project and released under the MIT License.
+;;
+;; Created by Samuel Williams on 10/5/2018.
+;; Copyright, 2018, by Samuel Williams. All rights reserved.
+;;
+
+.code
+
+coroutine_transfer proc
+ ; Save the thread information block:
+ push qword ptr gs:[8]
+ push qword ptr gs:[16]
+
+ ; Save caller registers:
+ push rbp
+ push rbx
+ push rdi
+ push rsi
+ push r12
+ push r13
+ push r14
+ push r15
+
+ movaps [rsp - 24], xmm6
+ movaps [rsp - 40], xmm7
+ movaps [rsp - 56], xmm8
+ movaps [rsp - 72], xmm9
+ movaps [rsp - 88], xmm10
+ movaps [rsp - 104], xmm11
+ movaps [rsp - 120], xmm12
+ movaps [rsp - 136], xmm13
+ movaps [rsp - 152], xmm14
+ movaps [rsp - 168], xmm15
+
+ ; Save caller stack pointer:
+ mov [rcx], rsp
+
+ ; Restore callee stack pointer:
+ mov rsp, [rdx]
+
+ movaps xmm15, [rsp - 168]
+ movaps xmm14, [rsp - 152]
+ movaps xmm13, [rsp - 136]
+ movaps xmm12, [rsp - 120]
+ movaps xmm11, [rsp - 104]
+ movaps xmm10, [rsp - 88]
+ movaps xmm9, [rsp - 72]
+ movaps xmm8, [rsp - 56]
+ movaps xmm7, [rsp - 40]
+ movaps xmm6, [rsp - 24]
+
+ ; Restore callee stack:
+ pop r15
+ pop r14
+ pop r13
+ pop r12
+ pop rsi
+ pop rdi
+ pop rbx
+ pop rbp
+
+ ; Restore the thread information block:
+ pop qword ptr gs:[16]
+ pop qword ptr gs:[8]
+
+ ; Put the first argument into the return value:
+ mov rax, rcx
+
+ ; We pop the return address and jump to it:
+ ret
+coroutine_transfer endp
+
+coroutine_trampoline proc
+ ; Do not remove this. This forces 16-byte alignment when entering the coroutine.
+ ret
+coroutine_trampoline endp
+
+end
diff --git a/coroutine/win64/Context.h b/coroutine/win64/Context.h
new file mode 100644
index 0000000000..16a8f583ab
--- /dev/null
+++ b/coroutine/win64/Context.h
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the "Coroutine" project and released under the MIT License.
+ *
+ * Created by Samuel Williams on 10/5/2018.
+ * Copyright, 2018, by Samuel Williams. All rights reserved.
+*/
+
+#pragma once
+
+#include <assert.h>
+#include <string.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+#define COROUTINE __declspec(noreturn) void
+
+const size_t COROUTINE_REGISTERS = 8;
+const size_t COROUTINE_XMM_REGISTERS = 1+10*2;
+
+typedef struct
+{
+ void **stack_pointer;
+} coroutine_context;
+
+typedef void(* coroutine_start)(coroutine_context *from, coroutine_context *self);
+
+void coroutine_trampoline();
+
+static inline void coroutine_initialize(
+ coroutine_context *context,
+ coroutine_start start,
+ void *stack_pointer,
+ size_t stack_size
+) {
+ /* Force 16-byte alignment */
+ context->stack_pointer = (void**)((uintptr_t)stack_pointer & ~0xF);
+
+ if (!start) {
+ assert(!context->stack_pointer);
+ /* We are main coroutine for this thread */
+ return;
+ }
+
+ /* Win64 ABI requires space for arguments */
+ context->stack_pointer -= 4;
+
+ /* Return address */
+ *--context->stack_pointer = 0;
+ *--context->stack_pointer = (void*)start;
+ *--context->stack_pointer = (void*)coroutine_trampoline;
+
+ /* Windows Thread Information Block */
+ /* *--context->stack_pointer = 0; */ /* gs:[0x00] is not used */
+ *--context->stack_pointer = (void*)stack_pointer; /* gs:[0x08] */
+ *--context->stack_pointer = (void*)((char *)stack_pointer - stack_size); /* gs:[0x10] */
+
+ context->stack_pointer -= COROUTINE_REGISTERS;
+ memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
+ memset(context->stack_pointer - COROUTINE_XMM_REGISTERS, 0, sizeof(void*) * COROUTINE_XMM_REGISTERS);
+}
+
+coroutine_context * coroutine_transfer(coroutine_context * current, coroutine_context * target);
+
+static inline void coroutine_destroy(coroutine_context * context)
+{
+}
+
+#if __cplusplus
+}
+#endif
diff --git a/coroutine/x86/Context.S b/coroutine/x86/Context.S
new file mode 100644
index 0000000000..d6a0a2def7
--- /dev/null
+++ b/coroutine/x86/Context.S
@@ -0,0 +1,43 @@
+##
+## This file is part of the "Coroutine" project and released under the MIT License.
+##
+## Created by Samuel Williams on 3/11/2018.
+## Copyright, 2018, by Samuel Williams. All rights reserved.
+##
+
+.text
+
+.globl coroutine_transfer
+coroutine_transfer:
+
+# For older linkers
+.globl _coroutine_transfer
+_coroutine_transfer:
+
+ # Save caller registers
+ pushl %ebp
+ pushl %ebx
+ pushl %edi
+ pushl %esi
+
+ # Save caller stack pointer
+ movl %esp, (%ecx)
+
+ # Restore callee stack pointer
+ movl (%edx), %esp
+
+ # Restore callee stack
+ popl %esi
+ popl %edi
+ popl %ebx
+ popl %ebp
+
+ # Save the first argument as the return value
+ movl %ecx, %eax
+
+ # Jump to the address on the stack
+ ret
+
+#if defined(__linux__) && defined(__ELF__)
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/coroutine/x86/Context.h b/coroutine/x86/Context.h
new file mode 100644
index 0000000000..b077227a1d
--- /dev/null
+++ b/coroutine/x86/Context.h
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the "Coroutine" project and released under the MIT License.
+ *
+ * Created by Samuel Williams on 3/11/2018.
+ * Copyright, 2018, by Samuel Williams. All rights reserved.
+*/
+
+#pragma once
+
+#include <assert.h>
+#include <string.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+#define COROUTINE __attribute__((noreturn, fastcall)) void
+
+const size_t COROUTINE_REGISTERS = 4;
+
+typedef struct
+{
+ void **stack_pointer;
+} coroutine_context;
+
+typedef COROUTINE(* coroutine_start)(coroutine_context *from, coroutine_context *self) __attribute__((fastcall));
+
+static inline void coroutine_initialize(
+ coroutine_context *context,
+ coroutine_start start,
+ void *stack_pointer,
+ size_t stack_size
+) {
+ /* Force 16-byte alignment */
+ context->stack_pointer = (void**)((uintptr_t)stack_pointer & ~0xF);
+
+ if (!start) {
+ assert(!context->stack_pointer);
+ /* We are main coroutine for this thread */
+ return;
+ }
+
+ *--context->stack_pointer = NULL;
+ *--context->stack_pointer = (void*)start;
+
+ context->stack_pointer -= COROUTINE_REGISTERS;
+ memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
+}
+
+coroutine_context * coroutine_transfer(coroutine_context * current, coroutine_context * target) __attribute__((fastcall));
+
+static inline void coroutine_destroy(coroutine_context * context)
+{
+ context->stack_pointer = NULL;
+}
+
+#if __cplusplus
+}
+#endif
diff --git a/cygwin/GNUmakefile.in b/cygwin/GNUmakefile.in
index 2f93102efe..192cf43b37 100644
--- a/cygwin/GNUmakefile.in
+++ b/cygwin/GNUmakefile.in
@@ -1,5 +1,3 @@
-override MFLAGS := $(filter-out -j%,$(MFLAGS))
-override MAKEFLAGS := $(filter-out -j%,$(MAKEFLAGS))
include Makefile
ENABLE_SHARED=@ENABLE_SHARED@
diff --git a/debug.c b/debug.c
index 8a5e69bbf5..e4ad163ad1 100644
--- a/debug.c
+++ b/debug.c
@@ -11,10 +11,12 @@
#include "ruby/ruby.h"
#include "ruby/encoding.h"
+#include "ruby/io.h"
#include "ruby/util.h"
#include "vm_debug.h"
#include "eval_intern.h"
#include "vm_core.h"
+#include "symbol.h"
#include "id.h"
/* for gdb */
@@ -29,12 +31,41 @@ const union {
enum ruby_encoding_consts encoding_consts;
enum ruby_coderange_type enc_coderange_types;
enum ruby_econv_flag_type econv_flag_types;
+ enum ruby_robject_flags robject_flags;
+ enum ruby_rmodule_flags rmodule_flags;
+ enum ruby_rstring_flags rstring_flags;
+ enum ruby_rarray_flags rarray_flags;
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_NODE_TYPESHIFT = NODE_TYPESHIFT,
RUBY_NODE_TYPEMASK = NODE_TYPEMASK,
RUBY_NODE_LSHIFT = NODE_LSHIFT,
RUBY_NODE_FL_NEWLINE = NODE_FL_NEWLINE
} various;
+ union {
+ 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;
} ruby_dummy_gdb_enums;
const SIGNED_VALUE RUBY_NODE_LMASK = NODE_LMASK;
@@ -106,25 +137,87 @@ ruby_debug_breakpoint(void)
/* */
}
-static void
-set_debug_option(const char *str, int len, void *arg)
-{
-#if defined _WIN32 && RUBY_MSVCRT_VERSION >= 80
- extern int ruby_w32_rtc_error;
+#if defined _WIN32
+# if RUBY_MSVCRT_VERSION >= 80
+extern int ruby_w32_rtc_error;
+# endif
+#endif
+#if defined _WIN32 || defined __CYGWIN__
+#include <windows.h>
+UINT ruby_w32_codepage[2];
#endif
+extern int ruby_rgengc_debug;
+
+int
+ruby_env_debug_option(const char *str, int len, void *arg)
+{
+ int ov;
+ 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; \
+ return 1; \
} \
} while (0)
+#define NAME_MATCH_VALUE(name) \
+ ((size_t)len >= sizeof(name)-1 && \
+ strncmp(str, (name), sizeof(name)-1) == 0 && \
+ ((len == sizeof(name)-1 && !(len = 0)) || \
+ (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; \
+ } 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); \
+ } \
+ } while (0)
+#define SET_WHEN_UINT(name, vals, num, req) \
+ if (NAME_MATCH_VALUE(name)) SET_UINT_LIST(name, vals, num);
+
SET_WHEN("gc_stress", *ruby_initial_gc_stress_ptr, Qtrue);
SET_WHEN("core", ruby_enable_coredump, 1);
-#if defined _WIN32 && RUBY_MSVCRT_VERSION >= 80
+ if (NAME_MATCH_VALUE("rgengc")) {
+ 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
SET_WHEN("rtc_error", ruby_w32_rtc_error, 1);
+# endif
#endif
- fprintf(stderr, "unexpected debug option: %.*s\n", len, str);
+#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;
+ }
+#endif
+ return 0;
+}
+
+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);
+ }
}
void
diff --git a/debug_counter.c b/debug_counter.c
new file mode 100644
index 0000000000..8c4004af41
--- /dev/null
+++ b/debug_counter.c
@@ -0,0 +1,55 @@
+/**********************************************************************
+
+ debug_counter.c -
+
+ created at: Tue Feb 21 16:51:18 2017
+
+ Copyright (C) 2017 Koichi Sasada
+
+**********************************************************************/
+
+#include "debug_counter.h"
+#if USE_DEBUG_COUNTER
+#include <stdio.h>
+#include <locale.h>
+#include "internal.h"
+
+static const char *const debug_counter_names[] = {
+ ""
+#define RB_DEBUG_COUNTER(name) #name,
+#include "debug_counter.h"
+#undef RB_DEBUG_COUNTER
+};
+
+size_t rb_debug_counter[numberof(debug_counter_names)];
+
+void
+rb_debug_counter_show_results(const char *msg)
+{
+ const char *env = getenv("RUBY_DEBUG_COUNTER_DISABLE");
+
+ setlocale(LC_NUMERIC, "");
+
+ if (env == NULL || strcmp("1", env) != 0) {
+ int i;
+ fprintf(stderr, "[RUBY_DEBUG_COUNTER]\t%d %s\n", getpid(), msg);
+ 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]);
+ }
+ }
+}
+
+__attribute__((destructor))
+static void
+debug_counter_show_results_at_exit(void)
+{
+ rb_debug_counter_show_results("normal exit.");
+}
+#else
+void
+rb_debug_counter_show_results(const char *msg)
+{
+}
+#endif /* USE_DEBUG_COUNTER */
diff --git a/debug_counter.h b/debug_counter.h
new file mode 100644
index 0000000000..bd03204af4
--- /dev/null
+++ b/debug_counter.h
@@ -0,0 +1,283 @@
+/**********************************************************************
+
+ debug_counter.h -
+
+ created at: Tue Feb 21 16:51:18 2017
+
+ Copyright (C) 2017 Koichi Sasada
+
+**********************************************************************/
+
+#ifndef USE_DEBUG_COUNTER
+#define USE_DEBUG_COUNTER 0
+#endif
+
+#ifdef RB_DEBUG_COUNTER
+
+/*
+ * method cache (mc) counts.
+ *
+ * * mc_inline_hit/miss: inline mc hit/miss counts (VM send insn)
+ * * mc_global_hit/miss: global method cache hit/miss counts
+ * two types: (1) inline cache miss (VM send insn)
+ * (2) called from C (rb_funcall).
+ * * mc_global_state_miss: inline mc miss by global_state miss.
+ * * mc_class_serial_miss: ... by mc_class_serial_miss
+ * * mc_cme_complement: cme complement counts.
+ * * mc_cme_complement_hit: cme cache hit counts.
+ * * mc_search_super: search_method() call counts.
+ */
+RB_DEBUG_COUNTER(mc_inline_hit)
+RB_DEBUG_COUNTER(mc_inline_miss)
+RB_DEBUG_COUNTER(mc_global_hit)
+RB_DEBUG_COUNTER(mc_global_miss)
+RB_DEBUG_COUNTER(mc_global_state_miss)
+RB_DEBUG_COUNTER(mc_class_serial_miss)
+RB_DEBUG_COUNTER(mc_cme_complement)
+RB_DEBUG_COUNTER(mc_cme_complement_hit)
+RB_DEBUG_COUNTER(mc_search_super)
+
+/*
+ * control frame push counts.
+ *
+ * * frame_push: frame push counts.
+ * * frame_push_*: frame push counts per each type.
+ * * frame_R2R: Ruby frame to Ruby frame
+ * * frame_R2C: Ruby frame to C frame
+ * * frame_C2C: C frame to C frame
+ * * frame_C2R: C frame to Ruby frame
+ */
+RB_DEBUG_COUNTER(frame_push)
+RB_DEBUG_COUNTER(frame_push_method)
+RB_DEBUG_COUNTER(frame_push_block)
+RB_DEBUG_COUNTER(frame_push_class)
+RB_DEBUG_COUNTER(frame_push_top)
+RB_DEBUG_COUNTER(frame_push_cfunc)
+RB_DEBUG_COUNTER(frame_push_ifunc)
+RB_DEBUG_COUNTER(frame_push_eval)
+RB_DEBUG_COUNTER(frame_push_rescue)
+RB_DEBUG_COUNTER(frame_push_dummy)
+
+RB_DEBUG_COUNTER(frame_R2R)
+RB_DEBUG_COUNTER(frame_R2C)
+RB_DEBUG_COUNTER(frame_C2C)
+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)
+ * * ivar_get/set_base: call counts of "rb_ivar_get/set()".
+ * because of (1) ic miss.
+ * (2) direct call by C extensions.
+ */
+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_oorange)
+RB_DEBUG_COUNTER(ivar_set_ic_miss_noobject)
+RB_DEBUG_COUNTER(ivar_get_base)
+RB_DEBUG_COUNTER(ivar_set_base)
+
+/* local variable counts
+ *
+ * * lvar_get: total lvar get counts (VM insn)
+ * * lvar_get_dynamic: lvar get counts if accessing upper env (VM insn)
+ * * lvar_set*: same as "get"
+ * * lvar_set_slowpath: counts using vm_env_write_slowpath()
+ */
+RB_DEBUG_COUNTER(lvar_get)
+RB_DEBUG_COUNTER(lvar_get_dynamic)
+RB_DEBUG_COUNTER(lvar_set)
+RB_DEBUG_COUNTER(lvar_set_dynamic)
+RB_DEBUG_COUNTER(lvar_set_slowpath)
+
+/* GC counts:
+ *
+ * * count: simple count
+ * * _minor: minor gc
+ * * _major: major gc
+ * * other suffix is corresponding to last_gc_info or
+ * gc_profile_record_flag in gc.c.
+ */
+RB_DEBUG_COUNTER(gc_count)
+RB_DEBUG_COUNTER(gc_minor_newobj)
+RB_DEBUG_COUNTER(gc_minor_malloc)
+RB_DEBUG_COUNTER(gc_minor_method)
+RB_DEBUG_COUNTER(gc_minor_capi)
+RB_DEBUG_COUNTER(gc_minor_stress)
+RB_DEBUG_COUNTER(gc_major_nofree)
+RB_DEBUG_COUNTER(gc_major_oldgen)
+RB_DEBUG_COUNTER(gc_major_shady)
+RB_DEBUG_COUNTER(gc_major_force)
+RB_DEBUG_COUNTER(gc_major_oldmalloc)
+
+/* object allocation counts:
+ *
+ * * obj_newobj: newobj counts
+ * * obj_newobj_slowpath: newobj with slowpath counts
+ * * obj_newobj_wb_unprotected: newobj for wb_unprotecte.
+ * * obj_free: obj_free() counts
+ * * obj_promote: promoted counts (oldgen)
+ * * obj_wb_unprotect: wb unprotect counts
+ *
+ * * obj_[type]_[attr]: *free'ed counts* for each type.
+ * Note that it is not a allocated counts.
+ * * [type]
+ * * _obj: T_OBJECT
+ * * _str: T_STRING
+ * * _ary: T_ARRAY
+ * * _xxx: T_XXX (hash, struct, ...)
+ *
+ * * [attr]
+ * * _ptr: R?? is not embed.
+ * * _embed: R?? is embed.
+ * * _transient: R?? uses transient heap.
+ * * type specific attr.
+ * * str_shared: str is shared.
+ * * str_nofree: nofree
+ * * str_fstr: fstr
+ * * hash_empty: hash is empty
+ * * hash_under4: has under 4 entries
+ * * hash_ge4: has n entries (4<=n<8)
+ * * hash_ge8: has n entries (8<=n)
+ * * data_empty: T_DATA but no memory free.
+ * * data_xfree: free'ed by xfree().
+ * * data_imm_free: free'ed immediately.
+ * * data_zombie: free'ed with zombie.
+ * * imemo_*: T_IMEMO with each type.
+ */
+RB_DEBUG_COUNTER(obj_newobj)
+RB_DEBUG_COUNTER(obj_newobj_slowpath)
+RB_DEBUG_COUNTER(obj_newobj_wb_unprotected)
+RB_DEBUG_COUNTER(obj_free)
+RB_DEBUG_COUNTER(obj_promote)
+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_str_ptr)
+RB_DEBUG_COUNTER(obj_str_embed)
+RB_DEBUG_COUNTER(obj_str_shared)
+RB_DEBUG_COUNTER(obj_str_nofree)
+RB_DEBUG_COUNTER(obj_str_fstr)
+
+RB_DEBUG_COUNTER(obj_ary_embed)
+RB_DEBUG_COUNTER(obj_ary_transient)
+RB_DEBUG_COUNTER(obj_ary_ptr)
+
+RB_DEBUG_COUNTER(obj_hash_empty)
+RB_DEBUG_COUNTER(obj_hash_under4)
+RB_DEBUG_COUNTER(obj_hash_ge4)
+RB_DEBUG_COUNTER(obj_hash_ge8)
+RB_DEBUG_COUNTER(obj_hash_ar)
+RB_DEBUG_COUNTER(obj_hash_st)
+RB_DEBUG_COUNTER(obj_hash_transient)
+
+RB_DEBUG_COUNTER(obj_hash_force_convert)
+
+RB_DEBUG_COUNTER(obj_struct_embed)
+RB_DEBUG_COUNTER(obj_struct_transient)
+RB_DEBUG_COUNTER(obj_struct_ptr)
+
+RB_DEBUG_COUNTER(obj_regexp_ptr)
+
+RB_DEBUG_COUNTER(obj_data_empty)
+RB_DEBUG_COUNTER(obj_data_xfree)
+RB_DEBUG_COUNTER(obj_data_imm_free)
+RB_DEBUG_COUNTER(obj_data_zombie)
+
+RB_DEBUG_COUNTER(obj_match_ptr)
+RB_DEBUG_COUNTER(obj_file_ptr)
+RB_DEBUG_COUNTER(obj_bignum_ptr)
+
+RB_DEBUG_COUNTER(obj_symbol)
+
+RB_DEBUG_COUNTER(obj_imemo_ment)
+RB_DEBUG_COUNTER(obj_imemo_iseq)
+RB_DEBUG_COUNTER(obj_imemo_env)
+RB_DEBUG_COUNTER(obj_imemo_tmpbuf)
+RB_DEBUG_COUNTER(obj_imemo_ast)
+RB_DEBUG_COUNTER(obj_imemo_cref)
+RB_DEBUG_COUNTER(obj_imemo_svar)
+RB_DEBUG_COUNTER(obj_imemo_throw_data)
+RB_DEBUG_COUNTER(obj_imemo_ifunc)
+RB_DEBUG_COUNTER(obj_imemo_memo)
+RB_DEBUG_COUNTER(obj_imemo_parser_strterm)
+
+RB_DEBUG_COUNTER(obj_iclass_ptr)
+RB_DEBUG_COUNTER(obj_class_ptr)
+RB_DEBUG_COUNTER(obj_module_ptr)
+
+/* heap function counts
+ *
+ * * heap_xmalloc/realloc/xfree: call counts
+ */
+RB_DEBUG_COUNTER(heap_xmalloc)
+RB_DEBUG_COUNTER(heap_xrealloc)
+RB_DEBUG_COUNTER(heap_xfree)
+
+/* transient_heap */
+RB_DEBUG_COUNTER(theap_alloc)
+RB_DEBUG_COUNTER(theap_alloc_fail)
+RB_DEBUG_COUNTER(theap_evacuate)
+
+/* load (not implemented yet) */
+/*
+RB_DEBUG_COUNTER(load_files)
+RB_DEBUG_COUNTER(load_path_is_not_realpath)
+*/
+#endif
+
+#ifndef RUBY_DEBUG_COUNTER_H
+#define RUBY_DEBUG_COUNTER_H 1
+
+#if !defined(__GNUC__) && USE_DEBUG_COUNTER
+#error "USE_DEBUG_COUNTER is not supported by other than __GNUC__"
+#endif
+
+enum rb_debug_counter_type {
+#define RB_DEBUG_COUNTER(name) RB_DEBUG_COUNTER_##name,
+#include __FILE__
+ RB_DEBUG_COUNTER_MAX
+#undef RB_DEBUG_COUNTER
+};
+
+#if USE_DEBUG_COUNTER
+#include "ruby/ruby.h"
+
+extern size_t rb_debug_counter[];
+
+inline static int
+rb_debug_counter_add(enum rb_debug_counter_type type, int add, int cond)
+{
+ if (cond) {
+ rb_debug_counter[(int)type] += add;
+ }
+ return cond;
+}
+
+#define RB_DEBUG_COUNTER_INC(type) rb_debug_counter_add(RB_DEBUG_COUNTER_##type, 1, 1)
+#define RB_DEBUG_COUNTER_INC_UNLESS(type, cond) (!rb_debug_counter_add(RB_DEBUG_COUNTER_##type, 1, !(cond)))
+#define RB_DEBUG_COUNTER_INC_IF(type, cond) rb_debug_counter_add(RB_DEBUG_COUNTER_##type, 1, (cond))
+
+#else
+#define RB_DEBUG_COUNTER_INC(type) ((void)0)
+#define RB_DEBUG_COUNTER_INC_UNLESS(type, cond) (cond)
+#define RB_DEBUG_COUNTER_INC_IF(type, cond) (cond)
+#endif
+
+void rb_debug_counter_show_results(const char *msg);
+
+#endif /* RUBY_DEBUG_COUNTER_H */
diff --git a/defs/gmake.mk b/defs/gmake.mk
index 2233627f8c..d8a43802ef 100644
--- a/defs/gmake.mk
+++ b/defs/gmake.mk
@@ -1,13 +1,28 @@
# -*- makefile-gmake -*-
gnumake = yes
-
-CHECK_TARGETS := exam love check%
-TEST_TARGETS := $(filter check test check% test% btest%,$(MAKECMDGOALS))
-TEST_TARGETS += $(subst check,test-all,$(patsubst check-%,test-%,$(TEST_TARGETS)))
-TEST_TARGETS := $(patsubst test-%,yes-test-%,$(patsubst btest-%,yes-btest-%,$(TEST_TARGETS)))
-TEST_DEPENDS := $(if $(TEST_TARGETS),$(filter all main exts,$(MAKECMDGOALS)))
-TEST_DEPENDS += $(if $(filter $(CHECK_TARGETS),$(MAKECMDGOALS)),main)
-TEST_DEPENDS += $(if $(filter main,$(TEST_DEPENDS)),$(if $(filter all,$(INSTALLDOC)),docs))
+override gnumake_recursive := $(if $(findstring n,$(firstword $(MFLAGS))),,+)
+override mflags := $(filter-out -j%,$(MFLAGS))
+MSPECOPT += $(if $(filter -j%,$(MFLAGS)),-j)
+
+CHECK_TARGETS := great exam love check test check% test% btest%
+# expand test targets, and those dependents
+TEST_TARGETS := $(filter $(CHECK_TARGETS),$(MAKECMDGOALS))
+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_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))
+TEST_DEPENDS := $(filter-out love $(TEST_TARGETS),$(TEST_DEPENDS))
+TEST_TARGETS := $(patsubst test-all,test test-testframework test-almost,$(patsubst check-%,test test-%,$(TEST_TARGETS)))
+TEST_DEPENDS := $(filter-out test-all $(TEST_TARGETS),$(TEST_DEPENDS))
+TEST_TARGETS := $(patsubst test,test-short,$(TEST_TARGETS))
+TEST_DEPENDS := $(filter-out test $(TEST_TARGETS),$(TEST_DEPENDS))
+TEST_TARGETS := $(patsubst test-short,btest-ruby test-knownbug test-basic,$(TEST_TARGETS))
+TEST_DEPENDS := $(filter-out test-short $(TEST_TARGETS),$(TEST_DEPENDS))
+TEST_DEPENDS += $(if $(filter great exam love check,$(MAKECMDGOALS)),all exts)
ifneq ($(filter -O0 -Od,$(optflags)),)
override XCFLAGS := $(filter-out -D_FORTIFY_SOURCE=%,$(XCFLAGS))
@@ -36,25 +51,25 @@ define archcmd
%.i: %.$(1).i
endef
-$(foreach arch,$(filter -arch=%,$(subst -arch ,-arch=,$(ARCH_FLAG))),\
+$(foreach arch,$(arch_flags),\
$(eval $(call archcmd,$(patsubst -arch=%,%,$(value arch)),$(patsubst -arch=%,-arch %,$(value arch)))))
endif
-ifneq ($(filter $(CHECK_TARGETS) test,$(MAKECMDGOALS)),)
-yes-test-basic: $(TEST_DEPENDS) yes-test-knownbug
-yes-test-knownbug: $(TEST_DEPENDS) yes-btest-ruby
-yes-btest-ruby: $(TEST_DEPENDS)
-endif
-ifneq ($(filter $(CHECK_TARGETS),$(MAKECMDGOALS)) $(filter yes-test-all,$(TEST_TARGETS)),)
-yes-test-testframework yes-test-almost yes-test-ruby: $(filter-out %test-all %test-ruby check%,$(TEST_TARGETS)) \
- yes-test-basic
-endif
-ifneq ($(filter $(CHECK_TARGETS),$(MAKECMDGOALS))$(if $(filter test-all,$(MAKECMDGOALS)),$(filter test-knownbug,$(MAKECMDGOALS))),)
-yes-test-testframework yes-test-almost yes-test-ruby: yes-test-knownbug
-yes-test-almost: yes-test-testframework
+.PHONY: $(addprefix yes-,$(TEST_TARGETS))
+
+ifneq ($(filter-out btest%,$(TEST_TARGETS)),)
+$(addprefix yes-,$(TEST_TARGETS)): $(TEST_DEPENDS)
endif
-$(TEST_TARGETS): $(TEST_DEPENDS)
+ORDERED_TEST_TARGETS := $(filter $(TEST_TARGETS), \
+ btest-ruby test-knownbug test-basic \
+ test-testframework test-ruby test-almost test-all \
+ test-spec test-bundler-prepare test-bundler \
+ )
+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)))
ifneq ($(if $(filter install,$(MAKECMDGOALS)),$(filter uninstall,$(MAKECMDGOALS))),)
install-targets := $(filter install uninstall,$(MAKECMDGOALS))
@@ -66,11 +81,6 @@ install-prereq: uninstall
uninstall sudo-precheck: all $(if $(filter all,$(INSTALLDOC)),docs)
endif
-ifneq ($(filter exam,$(MAKECMDGOALS)),)
-test-rubyspec: check
-yes-test-all no-test-all: test
-endif
-
ifneq ($(filter love,$(MAKECMDGOALS)),)
showflags: up
sudo-precheck: test yes-test-testframework no-test-testframework
@@ -78,6 +88,9 @@ install-prereq: sudo-precheck
yes-test-all no-test-all: install
yes-test-almost no-test-almost: install
endif
+ifneq ($(filter great,$(MAKECMDGOALS)),)
+love: test-rubyspec
+endif
$(srcdir)/missing/des_tables.c: $(srcdir)/missing/crypt.c
ifeq ($(if $(filter yes,$(CROSS_COMPILING)),,$(CC)),)
@@ -96,7 +109,7 @@ endif
STUBPROGRAM = rubystub$(EXEEXT)
IGNOREDPATTERNS = %~ .% %.orig %.rej \#%\#
SCRIPTBINDIR := $(if $(EXEEXT),,exec/)
-SCRIPTPROGRAMS = $(addprefix $(SCRIPTBINDIR),$(addsuffix $(EXEEXT),$(filter-out $(IGNOREDPATTERNS),$(notdir $(wildcard $(srcdir)/bin/*)))))
+SCRIPTPROGRAMS = $(addprefix $(SCRIPTBINDIR),$(addsuffix $(EXEEXT),$(filter-out $(IGNOREDPATTERNS),$(notdir $(wildcard $(srcdir)/libexec/*)))))
stub: $(STUBPROGRAM)
scriptbin: $(SCRIPTPROGRAMS)
@@ -125,3 +138,68 @@ $(SCRIPTBINDIR)%$(EXEEXT): bin/% $(STUBPROGRAM) \
$(TIMESTAMPDIR)/.exec.time:
$(Q) mkdir exec
$(Q) exit > $@
+
+.PHONY: commit
+commit: $(if $(filter commit,$(MAKECMDGOALS)),$(filter-out commit,$(MAKECMDGOALS)))
+ @$(BASERUBY) -C "$(srcdir)" -I./tool -rvcs -e 'VCS.detect(".").commit'
+ +$(Q) \
+ { \
+ $(CHDIR) "$(srcdir)"; \
+ sed 's/^@.*@$$//;s/@[A-Za-z_][A-Za-z_0-9]*@//g;/^all-incs:/d' defs/gmake.mk Makefile.in; \
+ sed 's/{[.;]*$$([a-zA-Z0-9_]*)}//g' common.mk; \
+ } | \
+ $(MAKE) $(mflags) Q=$(Q) ECHO=$(ECHO) srcdir="$(srcdir)" srcs_vpath="$(srcdir)/" CHDIR="$(CHDIR)" \
+ BOOTSTRAPRUBY="$(BOOTSTRAPRUBY)" MINIRUBY="$(BASERUBY)" BASERUBY="$(BASERUBY)" \
+ VCSUP="" ENC_MK=.top-enc.mk REVISION_FORCE=PHONY CONFIGURE="$(CONFIGURE)" -f - \
+ update-src srcs all-incs
+
+ifeq ($(words $(filter update-gems extract-gems,$(MAKECMDGOALS))),2)
+extract-gems: update-gems
+endif
+
+ifeq ($(filter 0 1,$(words $(arch_flags))),)
+$(foreach x,$(patsubst -arch=%,%,$(arch_flags)), \
+ $(eval $$(MJIT_HEADER:.h=)-$(value x).h \
+ $$(MJIT_MIN_HEADER:.h=)-$(value x).h \
+ $$(TIMESTAMPDIR)/$$(MJIT_HEADER:.h=)-$(value x).time \
+ : ARCH_FLAG := -arch $(value x)))
+
+$(foreach x,$(patsubst -arch=%,%,$(arch_flags)), \
+ $(eval $$(MJIT_HEADER:.h=)-$(value x).h: \
+ $$(TIMESTAMPDIR)/$$(MJIT_HEADER:.h=)-$(value x).time))
+
+mjit_min_headers := $(patsubst -arch=%,$(MJIT_MIN_HEADER:.h=-%.h),$(arch_flags))
+$(MJIT_MIN_HEADER): $(mjit_min_headers) $(PREP)
+ @ set -e; set $(patsubst -arch=%,%,$(arch_flags)); \
+ cd $(@D); h=$(@F:.h=); \
+ exec > $(@F).new; \
+ echo '#if 0'; \
+ for arch; do\
+ echo "#elif defined __$${arch}__"; \
+ echo "# include \"$$h-$$arch.h\""; \
+ done; \
+ echo "#else"; echo "# error unsupported platform"; echo "#endif"
+ $(IFCHANGE) $@ $@.new
+ $(Q) $(MAKEDIRS) $(MJIT_HEADER_INSTALL_DIR)
+ $(Q) $(MAKE_LINK) $@ $(MJIT_HEADER_INSTALL_DIR)/$(@F)
+
+endif
+
+# 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)
+
+# Query on the generated rdoc
+#
+# $ make rdoc:Integer#+
+rdoc\:%: PHONY
+ $(Q)$(RUNRUBY) $(srcdir)/libexec/ri --no-standard-docs --doc-dir=$(RDOCOUT) $(patsubst rdoc:%,%,$@)
+
+test_%.rb test/%: programs PHONY
+ +$(Q)$(exec) $(RUNRUBY) "$(srcdir)/test/runner.rb" --ruby="$(RUNRUBY)" $(TEST_EXCLUDES) $(TESTOPTS) -- $(patsubst test/%,%,$@)
+
+clean-srcs-ext::
+ $(Q)$(RM) $(patsubst $(srcdir)/%,%,$(EXT_SRCS))
+
+clean-srcs-extra::
+ $(Q)$(RM) $(patsubst $(srcdir)/%,%,$(EXTRA_SRCS))
diff --git a/defs/id.def b/defs/id.def
index 4fe9d3ccd8..bb7cc6f922 100644
--- a/defs/id.def
+++ b/defs/id.def
@@ -38,11 +38,16 @@ firstline, predefined = __LINE__+1, %[\
to_a
to_s
to_i
+ to_f
+ to_r
bt
bt_locations
call
mesg
exception
+ not NOT
+ and AND
+ or OR
_ UScore
"/*NULL*/" NULL
@@ -58,8 +63,6 @@ firstline, predefined = __LINE__+1, %[\
core#define_method
core#define_singleton_method
core#set_postexe
- core#hash_from_ary
- core#hash_merge_ary
core#hash_merge_ptr
core#hash_merge_kwd
@@ -76,7 +79,6 @@ token_ops = %[\
UPlus +@ UPLUS
UMinus -@ UMINUS
Pow ** POW
- DSTAR **
Cmp <=> CMP
PLUS +
MINUS -
@@ -93,13 +95,14 @@ token_ops = %[\
Eqq === EQQ
Neq != NEQ
Not !
+ And &
+ Or |
Backquote `
EqTilde =~ MATCH
NeqTilde !~ NMATCH
AREF []
ASET []=
COLON2 ::
- COLON3 ::
ANDOP &&
OROP ||
ANDDOT &.
diff --git a/defs/keywords b/defs/keywords
index e0d931cd1f..fc30ec2d15 100644
--- a/defs/keywords
+++ b/defs/keywords
@@ -1,8 +1,8 @@
%{
-struct kwtable {int name, id[2], state;};
+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(/*!ANSI{*/const char *, unsigned int/*}!ANSI*/);
#define rb_reserved_word(str, len) reserved_word(str, len)
%}
diff --git a/defs/known_errors.def b/defs/known_errors.def
index b9c490d3a2..e9694cfbda 100644
--- a/defs/known_errors.def
+++ b/defs/known_errors.def
@@ -1,148 +1,157 @@
-EPERM
-ENOENT
-ESRCH
-EINTR
-EIO
-ENXIO
E2BIG
-ENOEXEC
-EBADF
-ECHILD
-EAGAIN
-ENOMEM
EACCES
-EFAULT
-ENOTBLK
-EBUSY
-EEXIST
-EXDEV
-ENODEV
-ENOTDIR
-EISDIR
-EINVAL
-ENFILE
-EMFILE
-ENOTTY
-ETXTBSY
-EFBIG
-ENOSPC
-ESPIPE
-EROFS
-EMLINK
-EPIPE
-EDOM
-ERANGE
-EDEADLK
-ENAMETOOLONG
-ENOLCK
-ENOSYS
-ENOTEMPTY
-ELOOP
-EWOULDBLOCK
-ENOMSG
-EIDRM
-ECHRNG
-EL2NSYNC
-EL3HLT
-EL3RST
-ELNRNG
-EUNATCH
-ENOCSI
-EL2HLT
+EADDRINUSE
+EADDRNOTAVAIL
+EADV
+EAFNOSUPPORT
+EAGAIN
+EALREADY
+EAUTH
+EBADARCH
EBADE
+EBADEXEC
+EBADF
+EBADFD
+EBADMACHO
+EBADMSG
EBADR
-EXFULL
-ENOANO
+EBADRPC
EBADRQC
EBADSLT
-EDEADLOCK
EBFONT
-ENOSTR
-ENODATA
-ETIME
-ENOSR
-ENONET
-ENOPKG
-EREMOTE
-ENOLINK
-EADV
-ESRMNT
+EBUSY
+ECANCELED
+ECAPMODE
+ECHILD
+ECHRNG
ECOMM
-EPROTO
-EMULTIHOP
-EDOTDOT
-EBADMSG
-EOVERFLOW
-ENOTUNIQ
-EBADFD
-EREMCHG
-ELIBACC
-ELIBBAD
-ELIBSCN
-ELIBMAX
-ELIBEXEC
-EILSEQ
-ERESTART
-ESTRPIPE
-EUSERS
-ENOTSOCK
-EDESTADDRREQ
-EMSGSIZE
-EPROTOTYPE
-ENOPROTOOPT
-EPROTONOSUPPORT
-ESOCKTNOSUPPORT
-EOPNOTSUPP
-EPFNOSUPPORT
-EAFNOSUPPORT
-EADDRINUSE
-EADDRNOTAVAIL
-ENETDOWN
-ENETUNREACH
-ENETRESET
ECONNABORTED
-ECONNRESET
-ENOBUFS
-EISCONN
-ENOTCONN
-ESHUTDOWN
-ETOOMANYREFS
-ETIMEDOUT
ECONNREFUSED
+ECONNRESET
+EDEADLK
+EDEADLOCK
+EDESTADDRREQ
+EDEVERR
+EDOM
+EDOOFUS
+EDOTDOT
+EDQUOT
+EEXIST
+EFAULT
+EFBIG
+EFTYPE
EHOSTDOWN
EHOSTUNREACH
-EALREADY
+EHWPOISON
+EIDRM
+EILSEQ
EINPROGRESS
-ESTALE
-EUCLEAN
-ENOTNAM
-ENAVAIL
+EINTR
+EINVAL
+EIO
+EIPSEC
+EISCONN
+EISDIR
EISNAM
-EREMOTEIO
-EDQUOT
-ECANCELED
EKEYEXPIRED
EKEYREJECTED
EKEYREVOKED
+EL2HLT
+EL2NSYNC
+EL3HLT
+EL3RST
+ELAST
+ELIBACC
+ELIBBAD
+ELIBEXEC
+ELIBMAX
+ELIBSCN
+ELNRNG
+ELOOP
EMEDIUMTYPE
+EMFILE
+EMLINK
+EMSGSIZE
+EMULTIHOP
+ENAMETOOLONG
+ENAVAIL
+ENEEDAUTH
+ENETDOWN
+ENETRESET
+ENETUNREACH
+ENFILE
+ENOANO
+ENOATTR
+ENOBUFS
+ENOCSI
+ENODATA
+ENODEV
+ENOENT
+ENOEXEC
ENOKEY
+ENOLCK
+ENOLINK
ENOMEDIUM
+ENOMEM
+ENOMSG
+ENONET
+ENOPKG
+ENOPOLICY
+ENOPROTOOPT
+ENOSPC
+ENOSR
+ENOSTR
+ENOSYS
+ENOTBLK
+ENOTCAPABLE
+ENOTCONN
+ENOTDIR
+ENOTEMPTY
+ENOTNAM
ENOTRECOVERABLE
-EOWNERDEAD
-ERFKILL
-EAUTH
-EBADRPC
-EDOOFUS
-EFTYPE
-ENEEDAUTH
-ENOATTR
+ENOTSOCK
ENOTSUP
+ENOTTY
+ENOTUNIQ
+ENXIO
+EOPNOTSUPP
+EOVERFLOW
+EOWNERDEAD
+EPERM
+EPFNOSUPPORT
+EPIPE
EPROCLIM
EPROCUNAVAIL
EPROGMISMATCH
EPROGUNAVAIL
+EPROTO
+EPROTONOSUPPORT
+EPROTOTYPE
+EPWROFF
+EQFULL
+ERANGE
+EREMCHG
+EREMOTE
+EREMOTEIO
+ERESTART
+ERFKILL
+EROFS
ERPCMISMATCH
-EIPSEC
-EHWPOISON
-ECAPMODE
-ENOTCAPABLE
+ESHLIBVERS
+ESHUTDOWN
+ESOCKTNOSUPPORT
+ESPIPE
+ESRCH
+ESRMNT
+ESTALE
+ESTRPIPE
+ETIME
+ETIMEDOUT
+ETOOMANYREFS
+ETXTBSY
+EUCLEAN
+EUNATCH
+EUSERS
+EWOULDBLOCK
+EXDEV
+EXFULL
diff --git a/defs/lex.c.src b/defs/lex.c.src
index e0d931cd1f..fc30ec2d15 100644
--- a/defs/lex.c.src
+++ b/defs/lex.c.src
@@ -1,8 +1,8 @@
%{
-struct kwtable {int name, id[2], state;};
+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(/*!ANSI{*/const char *, unsigned int/*}!ANSI*/);
#define rb_reserved_word(str, len) reserved_word(str, len)
%}
diff --git a/defs/universal.mk b/defs/universal.mk
new file mode 100644
index 0000000000..c34a31b356
--- /dev/null
+++ b/defs/universal.mk
@@ -0,0 +1,5 @@
+arch_flags := $(filter -arch=%,$(subst -arch ,-arch=,$(ARCH_FLAG)))
+ifeq ($(filter 0 1,$(words $(arch_flags))),)
+override MJIT_HEADER_SUFFIX = -%
+override MJIT_HEADER_ARCH = -$(word 2,$(ARCH_FLAG))
+endif
diff --git a/dir.c b/dir.c
index f701fe928c..38167514c0 100644
--- a/dir.c
+++ b/dir.c
@@ -11,7 +11,10 @@
**********************************************************************/
+#include "ruby/encoding.h"
+#include "ruby/thread.h"
#include "internal.h"
+#include "id.h"
#include "encindex.h"
#include <sys/types.h>
@@ -21,6 +24,25 @@
#include <unistd.h>
#endif
+#ifndef O_CLOEXEC
+# define O_CLOEXEC 0
+#endif
+
+#ifndef USE_OPENDIR_AT
+# if defined(HAVE_FDOPENDIR) && defined(HAVE_DIRFD) && \
+ defined(HAVE_OPENAT) && defined(HAVE_FSTATAT)
+# define USE_OPENDIR_AT 1
+# else
+# define USE_OPENDIR_AT 0
+# endif
+#endif
+#if USE_OPENDIR_AT
+# include <fcntl.h>
+#endif
+#ifndef AT_FDCWD
+# define AT_FDCWD -1
+#endif
+
#undef HAVE_DIRENT_NAMLEN
#if defined HAVE_DIRENT_H && !defined _WIN32
# include <dirent.h>
@@ -45,10 +67,6 @@
# include "win32/dir.h"
# endif
#endif
-#if defined(__native_client__) && defined(NACL_NEWLIB)
-# include "nacl/dirent.h"
-# include "nacl/stat.h"
-#endif
#include <errno.h>
@@ -64,6 +82,8 @@ char *strchr(char*,char);
#include "ruby/util.h"
+#define vm_initialized rb_cThread
+
/* define system APIs */
#ifdef _WIN32
#undef chdir
@@ -74,6 +94,7 @@ char *strchr(char*,char);
#define rmdir(p) rb_w32_urmdir(p)
#undef opendir
#define opendir(p) rb_w32_uopendir(p)
+#define ruby_getcwd() rb_w32_ugetcwd(NULL, 0)
#define IS_WIN32 1
#else
#define IS_WIN32 0
@@ -156,7 +177,7 @@ has_nonascii(const char *ptr, size_t len)
#endif
#ifndef IFTODT
-# define IFTODT(m) (((m) & S_IFMT) / ((~S_IFMT & S_IFMT-1) + 1))
+# define IFTODT(m) (((m) & S_IFMT) / ((~S_IFMT & (S_IFMT-1)) + 1))
#endif
typedef enum {
@@ -421,7 +442,7 @@ VALUE rb_cDir;
struct dir_data {
DIR *dir;
- VALUE path;
+ const VALUE path;
rb_encoding *enc;
};
@@ -436,9 +457,8 @@ static void
dir_free(void *ptr)
{
struct dir_data *dir = ptr;
- if (dir) {
- if (dir->dir) closedir(dir->dir);
- }
+
+ if (dir->dir) closedir(dir->dir);
xfree(dir);
}
@@ -451,20 +471,11 @@ dir_memsize(const void *ptr)
static const rb_data_type_t dir_data_type = {
"dir",
{dir_mark, dir_free, dir_memsize,},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
};
static VALUE dir_close(VALUE);
-#define GlobPathValue(str, safe) \
- /* can contain null bytes as separators */ \
- (!RB_TYPE_P((str), T_STRING) ? \
- (void)FilePathValue(str) : \
- (void)(check_safe_glob((str), (safe)), \
- check_glob_encoding(str), (str)))
-#define check_safe_glob(str, safe) ((safe) ? rb_check_safe_obj(str) : (void)0)
-#define check_glob_encoding(str) rb_enc_check((str), rb_enc_from_encoding(rb_usascii_encoding()))
-
static VALUE
dir_s_alloc(VALUE klass)
{
@@ -472,12 +483,34 @@ dir_s_alloc(VALUE klass)
VALUE obj = TypedData_Make_Struct(klass, struct dir_data, &dir_data_type, dirp);
dirp->dir = NULL;
- dirp->path = Qnil;
+ RB_OBJ_WRITE(obj, &dirp->path, Qnil);
dirp->enc = NULL;
return obj;
}
+static void *
+nogvl_opendir(void *ptr)
+{
+ const char *path = ptr;
+
+ return (void *)opendir(path);
+}
+
+static DIR *
+opendir_without_gvl(const char *path)
+{
+ if (vm_initialized) {
+ union { const void *in; void *out; } u;
+
+ u.in = path;
+
+ return rb_thread_call_without_gvl(nogvl_opendir, u.out, RUBY_UBF_IO, 0);
+ }
+ else
+ return opendir(path);
+}
+
/*
* call-seq:
* Dir.new( string ) -> aDir
@@ -485,7 +518,7 @@ dir_s_alloc(VALUE klass)
*
* Returns a new directory object for the named directory.
*
- * The optional <i>enc</i> argument specifies the encoding of the directory.
+ * The optional <i>encoding</i> keyword argument specifies the encoding of the directory.
* If not specified, the filesystem encoding is used.
*/
static VALUE
@@ -513,7 +546,7 @@ dir_initialize(int argc, VALUE *argv, VALUE dir)
}
}
- GlobPathValue(dirname, FALSE);
+ FilePathValue(dirname);
orig = rb_str_dup_frozen(dirname);
dirname = rb_str_encode_ospath(dirname);
dirname = rb_str_dup_frozen(dirname);
@@ -521,21 +554,21 @@ dir_initialize(int argc, VALUE *argv, VALUE dir)
TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dp);
if (dp->dir) closedir(dp->dir);
dp->dir = NULL;
- dp->path = Qnil;
+ RB_OBJ_WRITE(dir, &dp->path, Qnil);
dp->enc = fsenc;
path = RSTRING_PTR(dirname);
- dp->dir = opendir(path);
+ dp->dir = opendir_without_gvl(path);
if (dp->dir == NULL) {
int e = errno;
if (rb_gc_for_fd(e)) {
- dp->dir = opendir(path);
+ 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(path);
+ dp->dir = opendir_without_gvl(path);
}
}
#endif
@@ -544,7 +577,7 @@ dir_initialize(int argc, VALUE *argv, VALUE dir)
rb_syserr_fail_path(e, orig);
}
}
- dp->path = orig;
+ RB_OBJ_WRITE(dir, &dp->path, orig);
return dir;
}
@@ -556,7 +589,7 @@ dir_initialize(int argc, VALUE *argv, VALUE dir)
* Dir.open( string ) {| aDir | block } -> anObject
* Dir.open( string, encoding: enc ) {| aDir | block } -> anObject
*
- * The optional <i>enc</i> argument specifies the encoding of the directory.
+ * The optional <i>encoding</i> keyword argument specifies the encoding of the directory.
* If not specified, the filesystem encoding is used.
*
* With no block, <code>open</code> is a synonym for
@@ -579,6 +612,8 @@ dir_s_open(int argc, VALUE *argv, VALUE klass)
return dir;
}
+NORETURN(static void dir_closed(void));
+
static void
dir_closed(void)
{
@@ -623,7 +658,7 @@ dir_inspect(VALUE dir)
rb_str_cat2(str, ">");
return str;
}
- return rb_funcallv(dir, rb_intern("to_s"), 0, 0);
+ return rb_funcallv(dir, idTo_s, 0, 0);
}
/* Workaround for Solaris 10 that does not have dirfd.
@@ -707,6 +742,8 @@ fundamental_encoding_p(rb_encoding *enc)
#else
# define READDIR(dir, enc) readdir((dir))
#endif
+
+/* safe to use without GVL */
static int
to_be_skipped(const struct dirent *dp)
{
@@ -759,6 +796,14 @@ dir_read(VALUE dir)
}
}
+static VALUE dir_each_entry(VALUE, VALUE (*)(VALUE, VALUE), VALUE, int);
+
+static VALUE
+dir_yield(VALUE arg, VALUE path)
+{
+ return rb_yield(path);
+}
+
/*
* call-seq:
* dir.each { |filename| block } -> dir
@@ -782,11 +827,17 @@ dir_read(VALUE dir)
static VALUE
dir_each(VALUE dir)
{
+ RETURN_ENUMERATOR(dir, 0, 0);
+ return dir_each_entry(dir, dir_yield, Qnil, FALSE);
+}
+
+static VALUE
+dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE), VALUE arg, int children_only)
+{
struct dir_data *dirp;
struct dirent *dp;
IF_NORMALIZE_UTF8PATH(int norm_p);
- RETURN_ENUMERATOR(dir, 0, 0);
GetDIR(dir, dirp);
rewinddir(dirp->dir);
IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp->dir, RSTRING_PTR(dirp->path)));
@@ -794,6 +845,11 @@ dir_each(VALUE dir)
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))) {
@@ -802,7 +858,7 @@ dir_each(VALUE dir)
else
#endif
path = rb_external_str_new_with_enc(name, namlen, dirp->enc);
- rb_yield(path);
+ (*each)(arg, path);
if (dirp->dir == NULL) dir_closed();
}
return dir;
@@ -934,6 +990,14 @@ dir_close(VALUE dir)
return Qnil;
}
+static void *
+nogvl_chdir(void *ptr)
+{
+ const char *path = ptr;
+
+ return (void *)(VALUE)chdir(path);
+}
+
static void
dir_chdir(VALUE path)
{
@@ -1016,9 +1080,8 @@ dir_s_chdir(int argc, VALUE *argv, VALUE obj)
{
VALUE path = Qnil;
- if (rb_scan_args(argc, argv, "01", &path) == 1) {
- FilePathValue(path);
- path = rb_str_encode_ospath(path);
+ if (rb_check_arity(argc, 0, 1) == 1) {
+ path = rb_str_encode_ospath(rb_get_path(argv[0]));
}
else {
const char *dist = getenv("HOME");
@@ -1042,31 +1105,62 @@ dir_s_chdir(int argc, VALUE *argv, VALUE obj)
args.done = FALSE;
return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args);
}
- dir_chdir(path);
+ 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);
+ }
return INT2FIX(0);
}
+#ifndef _WIN32
VALUE
-rb_dir_getwd(void)
+rb_dir_getwd_ospath(void)
{
char *path;
VALUE cwd;
- int fsenc = rb_enc_to_index(rb_filesystem_encoding());
+ VALUE path_guard;
- if (fsenc == ENCINDEX_US_ASCII) fsenc = ENCINDEX_ASCII;
- path = my_getcwd();
+#undef RUBY_UNTYPED_DATA_WARNING
+#define RUBY_UNTYPED_DATA_WARNING 0
+ path_guard = Data_Wrap_Struct((VALUE)0, NULL, RUBY_DEFAULT_FREE, NULL);
+ path = ruby_getcwd();
+ DATA_PTR(path_guard) = path;
#ifdef __APPLE__
cwd = rb_str_normalize_ospath(path, strlen(path));
OBJ_TAINT(cwd);
#else
cwd = rb_tainted_str_new2(path);
#endif
- rb_enc_associate_index(cwd, fsenc);
+ DATA_PTR(path_guard) = 0;
xfree(path);
return cwd;
}
+#endif
+
+VALUE
+rb_dir_getwd(void)
+{
+ rb_encoding *fs = rb_filesystem_encoding();
+ int fsenc = rb_enc_to_index(fs);
+ VALUE cwd = rb_dir_getwd_ospath();
+
+ switch (fsenc) {
+ case ENCINDEX_US_ASCII:
+ fsenc = ENCINDEX_ASCII;
+ case ENCINDEX_ASCII:
+ break;
+#if defined _WIN32 || defined __APPLE__
+ default:
+ return rb_str_conv_enc(cwd, NULL, fs);
+#endif
+ }
+ return rb_enc_associate_index(cwd, fsenc);
+}
/*
* call-seq:
@@ -1129,6 +1223,19 @@ dir_s_chroot(VALUE dir, VALUE path)
#define dir_s_chroot rb_f_notimplement
#endif
+struct mkdir_arg {
+ const char *path;
+ mode_t mode;
+};
+
+static void *
+nogvl_mkdir(void *ptr)
+{
+ struct mkdir_arg *m = ptr;
+
+ return (void *)(VALUE)mkdir(m->path, m->mode);
+}
+
/*
* call-seq:
* Dir.mkdir( string [, integer] ) -> 0
@@ -1147,23 +1254,34 @@ dir_s_chroot(VALUE dir, VALUE path)
static VALUE
dir_s_mkdir(int argc, VALUE *argv, VALUE obj)
{
+ struct mkdir_arg m;
VALUE path, vmode;
- int mode;
+ int r;
if (rb_scan_args(argc, argv, "11", &path, &vmode) == 2) {
- mode = NUM2INT(vmode);
+ m.mode = NUM2MODET(vmode);
}
else {
- mode = 0777;
+ m.mode = 0777;
}
path = check_dirname(path);
- if (mkdir(RSTRING_PTR(path), mode) == -1)
+ 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);
return INT2FIX(0);
}
+static void *
+nogvl_rmdir(void *ptr)
+{
+ const char *path = ptr;
+
+ return (void *)(VALUE)rmdir(path);
+}
+
/*
* call-seq:
* Dir.delete( string ) -> 0
@@ -1176,8 +1294,13 @@ dir_s_mkdir(int argc, VALUE *argv, VALUE obj)
static VALUE
dir_s_rmdir(VALUE obj, VALUE dir)
{
+ const char *p;
+ int r;
+
dir = check_dirname(dir);
- if (rmdir(RSTRING_PTR(dir)) < 0)
+ 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);
return INT2FIX(0);
@@ -1241,29 +1364,55 @@ to_be_ignored(int e)
}
#ifdef _WIN32
-#define STAT(p, s) rb_w32_ustati64((p), (s))
+#define STAT(p, s) rb_w32_ustati128((p), (s))
#undef lstat
-#define lstat(p, s) rb_w32_ulstati64((p), (s))
+#define lstat(p, s) rb_w32_ulstati128((p), (s))
#else
#define STAT(p, s) stat((p), (s))
#endif
+typedef int ruby_glob_errfunc(const char*, VALUE, const void*, int);
+typedef struct {
+ ruby_glob_func *match;
+ ruby_glob_errfunc *error;
+} ruby_glob_funcs_t;
+
+static const char *
+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;
+ }
+#endif
+ return *path ? path : ".";
+}
+
/* System call with warning */
static int
-do_stat(const char *path, struct stat *pst, int flags, rb_encoding *enc)
+do_stat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc)
{
+#if USE_OPENDIR_AT
+ int ret = fstatat(fd, at_subpath(fd, baselen, path), pst, 0);
+#else
int ret = STAT(path, pst);
+#endif
if (ret < 0 && !to_be_ignored(errno))
sys_warning(path, enc);
return ret;
}
-#if defined HAVE_LSTAT || defined lstat
+#if defined HAVE_LSTAT || defined lstat || USE_OPENDIR_AT
static int
-do_lstat(const char *path, struct stat *pst, int flags, rb_encoding *enc)
+do_lstat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc)
{
+#if USE_OPENDIR_AT
+ int ret = fstatat(fd, at_subpath(fd, baselen, path), pst, AT_SYMLINK_NOFOLLOW);
+#else
int ret = lstat(path, pst);
+#endif
if (ret < 0 && !to_be_ignored(errno))
sys_warning(path, enc);
@@ -1273,8 +1422,85 @@ do_lstat(const char *path, struct stat *pst, int flags, rb_encoding *enc)
#define do_lstat do_stat
#endif
+struct opendir_at_arg {
+ int basefd;
+ const char *path;
+};
+
+static void *
+with_gvl_gc_for_fd(void *ptr)
+{
+ int *e = ptr;
+
+ return (void *)(rb_gc_for_fd(*e) ? Qtrue : Qfalse);
+}
+
+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);
+ else
+ return rb_gc_for_fd(e) ? Qtrue : Qfalse;
+}
+
+static void *
+nogvl_opendir_at(void *ptr)
+{
+ const struct opendir_at_arg *oaa = ptr;
+ DIR *dirp;
+
+#if USE_OPENDIR_AT
+ const int opendir_flags = (O_RDONLY|O_CLOEXEC|
+# ifdef O_DIRECTORY
+ O_DIRECTORY|
+# endif /* O_DIRECTORY */
+ 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;
+ }
+ }
+#else /* !USE_OPENDIR_AT */
+ dirp = opendir(oaa->path);
+ if (!dirp && gc_for_fd_with_gvl(errno))
+ dirp = opendir(oaa->path);
+#endif /* !USE_OPENDIR_AT */
+
+ return dirp;
+}
+
+static DIR *
+opendir_at(int basefd, const char *path)
+{
+ struct opendir_at_arg oaa;
+
+ oaa.basefd = basefd;
+ oaa.path = path;
+
+ if (vm_initialized)
+ return rb_thread_call_without_gvl(nogvl_opendir_at, &oaa, RUBY_UBF_IO, 0);
+ else
+ return nogvl_opendir_at(&oaa);
+}
+
static DIR *
-do_opendir(const char *path, int flags, rb_encoding *enc)
+do_opendir(const int basefd, size_t baselen, const char *path, int flags, rb_encoding *enc,
+ ruby_glob_errfunc *errfunc, VALUE arg, int *status)
{
DIR *dirp;
#ifdef _WIN32
@@ -1285,18 +1511,18 @@ do_opendir(const char *path, int flags, rb_encoding *enc)
path = RSTRING_PTR(tmp);
}
#endif
- dirp = opendir(path);
+ dirp = opendir_at(basefd, at_subpath(basefd, baselen, path));
if (!dirp) {
int e = errno;
- switch (rb_gc_for_fd(e)) {
- default:
- dirp = opendir(path);
- if (dirp) break;
- e = errno;
- /* fallback */
- case 0:
- if (to_be_ignored(e)) break;
- sys_warning(path, enc);
+
+ *status = 0;
+ if (!to_be_ignored(e)) {
+ if (errfunc) {
+ *status = (*errfunc)(path, arg, enc, e);
+ }
+ else {
+ sys_warning(path, enc);
+ }
}
}
#ifdef _WIN32
@@ -1307,7 +1533,7 @@ do_opendir(const char *path, int flags, rb_encoding *enc)
}
/* Globing pattern */
-enum glob_pattern_type { PLAIN, ALPHA, MAGICAL, RECURSIVE, MATCH_ALL, MATCH_DIR };
+enum glob_pattern_type { PLAIN, ALPHA, BRACE, MAGICAL, RECURSIVE, MATCH_ALL, MATCH_DIR };
/* Return nonzero if S has any special globbing chars in it. */
static enum glob_pattern_type
@@ -1315,15 +1541,20 @@ has_magic(const char *p, const char *pend, int flags, rb_encoding *enc)
{
const int escape = !(flags & FNM_NOESCAPE);
int hasalpha = 0;
+ int hasmagical = 0;
register char c;
while (p < pend && (c = *p++) != 0) {
switch (c) {
+ case '{':
+ return BRACE;
+
case '*':
case '?':
case '[':
- return MAGICAL;
+ hasmagical = 1;
+ break;
case '\\':
if (escape && p++ >= pend)
@@ -1348,7 +1579,7 @@ has_magic(const char *p, const char *pend, int flags, rb_encoding *enc)
p = Next(p-1, pend, enc);
}
- return hasalpha ? ALPHA : PLAIN;
+ return hasmagical ? MAGICAL : hasalpha ? ALPHA : PLAIN;
}
/* Find separator in globbing pattern. */
@@ -1369,6 +1600,13 @@ find_dirsep(const char *p, const char *pend, int flags, rb_encoding *enc)
open = 0;
continue;
+ case '{':
+ open = 1;
+ continue;
+ case '}':
+ open = 0;
+ continue;
+
case '/':
if (!open)
return (char *)p-1;
@@ -1501,7 +1739,7 @@ glob_free_pattern(struct glob_pattern *list)
}
static char *
-join_path(const char *path, long len, int dirsep, const char *name, size_t namlen)
+join_path(const char *path, size_t len, int dirsep, const char *name, size_t namlen)
{
char *buf = GLOB_ALLOC_N(char, len+namlen+(dirsep?1:0)+1);
@@ -1516,8 +1754,13 @@ join_path(const char *path, long len, int dirsep, const char *name, size_t namle
}
#ifdef HAVE_GETATTRLIST
+# if defined HAVE_FGETATTRLIST
+# define is_case_sensitive(dirp, path) is_case_sensitive(dirp)
+# else
+# define is_case_sensitive(dirp, path) is_case_sensitive(path)
+# endif
static int
-is_case_sensitive(DIR *dirp)
+is_case_sensitive(DIR *dirp, const char *path)
{
struct {
u_int32_t length;
@@ -1528,8 +1771,13 @@ is_case_sensitive(DIR *dirp)
const int idx = VOL_CAPABILITIES_FORMAT;
const uint32_t mask = VOL_CAP_FMT_CASE_SENSITIVE;
+# if defined HAVE_FGETATTRLIST
if (fgetattrlist(dirfd(dirp), &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW))
return -1;
+# else
+ if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW))
+ return -1;
+# endif
if (!(cap->valid[idx] & mask))
return -1;
return (cap->capabilities[idx] & mask) != 0;
@@ -1680,6 +1928,8 @@ replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int f
struct glob_args {
void (*func)(const char *, VALUE, void *);
const char *path;
+ const char *base;
+ size_t baselen;
VALUE value;
rb_encoding *enc;
};
@@ -1695,6 +1945,61 @@ glob_func_caller(VALUE val)
return Qnil;
}
+struct glob_error_args {
+ const char *path;
+ rb_encoding *enc;
+ int error;
+};
+
+static VALUE
+glob_func_warning(VALUE val)
+{
+ struct glob_error_args *arg = (struct glob_error_args *)val;
+ rb_syserr_enc_warning(arg->error, arg->enc, "%s", arg->path);
+ return Qnil;
+}
+
+#if 0
+static int
+rb_glob_warning(const char *path, VALUE a, const void *enc, int error)
+{
+ int status;
+ struct glob_error_args args;
+
+ args.path = path;
+ args.enc = enc;
+ args.error = error;
+ rb_protect(glob_func_warning, (VALUE)&args, &status);
+ return status;
+}
+#endif
+
+static VALUE
+glob_func_error(VALUE val)
+{
+ struct glob_error_args *arg = (struct glob_error_args *)val;
+ VALUE path = rb_enc_str_new_cstr(arg->path, arg->enc);
+ rb_syserr_fail_str(arg->error, path);
+ return Qnil;
+}
+
+static int
+rb_glob_error(const char *path, VALUE a, const void *enc, int error)
+{
+ int status;
+ struct glob_error_args args;
+ VALUE (*errfunc)(VALUE) = glob_func_error;
+
+ if (error == EACCES) {
+ errfunc = glob_func_warning;
+ }
+ args.path = path;
+ args.enc = enc;
+ args.error = error;
+ rb_protect(errfunc, (VALUE)&args, &status);
+ return status;
+}
+
static inline int
dirent_match(const char *pat, rb_encoding *enc, const char *name, const struct dirent *dp, int flags)
{
@@ -1707,24 +2012,101 @@ dirent_match(const char *pat, rb_encoding *enc, const char *name, const struct d
return 0;
}
+struct push_glob_args {
+ int fd;
+ const char *path;
+ size_t baselen;
+ size_t namelen;
+ int dirsep; /* '/' should be placed before appending child entry's name to 'path'. */
+ rb_pathtype_t pathtype; /* type of 'path' */
+ int flags;
+ const ruby_glob_funcs_t *funcs;
+ VALUE arg;
+};
+
+struct dirent_brace_args {
+ const char *name;
+ const struct dirent *dp;
+ int flags;
+};
+
+static int
+dirent_match_brace(const char *pattern, VALUE val, void *enc)
+{
+ struct dirent_brace_args *arg = (struct dirent_brace_args *)val;
+
+ return dirent_match(pattern, enc, arg->name, arg->dp, arg->flags);
+}
+
+/* join paths from pattern list of glob_make_pattern() */
+static char*
+join_path_from_pattern(struct glob_pattern **beg)
+{
+ struct glob_pattern *p;
+ char *path = NULL;
+ 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);
+ 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';
+ }
+ }
+ }
+ return path;
+}
+
+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);
+
static int
glob_helper(
+ int fd,
const char *path,
- long pathlen,
+ size_t baselen,
+ size_t namelen,
int dirsep, /* '/' should be placed before appending child entry's name to 'path'. */
rb_pathtype_t pathtype, /* type of 'path' */
struct glob_pattern **beg,
struct glob_pattern **end,
int flags,
- ruby_glob_func *func,
+ const ruby_glob_funcs_t *funcs,
VALUE arg,
rb_encoding *enc)
{
struct stat st;
int status = 0;
struct glob_pattern **cur, **new_beg, **new_end;
- int plain = 0, magical = 0, recursive = 0, match_all = 0, match_dir = 0;
+ int plain = 0, brace = 0, magical = 0, recursive = 0, match_all = 0, match_dir = 0;
int escape = !(flags & FNM_NOESCAPE);
+ size_t pathlen = baselen + namelen;
for (cur = beg; cur < end; ++cur) {
struct glob_pattern *p = *cur;
@@ -1743,6 +2125,11 @@ glob_helper(
magical = 1;
#endif
break;
+ case BRACE:
+ if (!recursive) {
+ brace = 1;
+ }
+ break;
case MAGICAL:
magical = 2;
break;
@@ -1757,17 +2144,35 @@ glob_helper(
}
}
+ 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;
+ }
+
if (*path) {
if (match_all && pathtype == path_unknown) {
- if (do_lstat(path, &st, flags, enc) == 0) {
+ 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) {
- if (do_stat(path, &st, flags, enc) == 0) {
+ 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 {
@@ -1775,13 +2180,16 @@ glob_helper(
}
}
if (match_all && pathtype > path_noent) {
- status = glob_call_func(func, path, arg, enc);
+ 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) {
- char *tmp = join_path(path, pathlen, dirsep, "", 0);
+ 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(func, tmp, arg, enc);
+ status = glob_call_func(funcs->match, tmp, arg, enc);
GLOB_FREE(tmp);
if (status) return status;
}
@@ -1800,12 +2208,14 @@ glob_helper(
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(plainname, flags, enc);
+ dirp = do_opendir(fd, basename, plainname, flags, enc, funcs->error, arg, &status);
GLOB_FREE(plainname);
}
else
+# else
+ ;
# endif
- dirp = do_opendir(*path ? path : ".", flags, enc);
+ 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)) {
@@ -1813,7 +2223,7 @@ glob_helper(
goto literally;
}
# endif
- return 0;
+ return status;
}
IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp, *path ? path : "."));
@@ -1824,7 +2234,7 @@ glob_helper(
}
# endif
# ifdef HAVE_GETATTRLIST
- if (is_case_sensitive(dirp) == 0)
+ if (is_case_sensitive(dirp, path) == 0)
flags |= FNM_CASEFOLD;
# endif
while ((dp = READDIR(dirp, enc)) != NULL) {
@@ -1835,21 +2245,22 @@ glob_helper(
int dotfile = 0;
IF_NORMALIZE_UTF8PATH(VALUE utf8str = Qnil);
- if (recursive && dp->d_name[0] == '.') {
+ name = dp->d_name;
+ namlen = NAMLEN(dp);
+ if (recursive && name[0] == '.') {
++dotfile;
- if (!dp->d_name[1]) {
+ if (namlen == 1) {
/* unless DOTMATCH, skip current directories not to recurse infinitely */
if (!(flags & FNM_DOTMATCH)) continue;
++dotfile;
+ new_pathtype = path_directory; /* force to skip stat/lstat */
}
- else if (dp->d_name[1] == '.' && !dp->d_name[2]) {
+ else if (namlen == 2 && name[1] == '.') {
/* always skip parent directories not to recurse infinitely */
continue;
}
}
- name = dp->d_name;
- namlen = NAMLEN(dp);
# if NORMALIZE_UTF8PATH
if (norm_p && has_nonascii(name, namlen)) {
if (!NIL_P(utf8str = rb_str_normalize_ospath(name, namlen))) {
@@ -1864,16 +2275,16 @@ glob_helper(
break;
}
name = buf + pathlen + (dirsep != 0);
- if (recursive && dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1)) {
#ifdef DT_UNKNOWN
- if ((new_pathtype = dp->d_type) != (rb_pathtype_t)DT_UNKNOWN)
- /* Got it. We need nothing more. */
- ;
- else
- /* fall back to call lstat(2) */
+ 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(buf, &st, flags, enc) == 0)
+ if (do_lstat(fd, baselen, buf, &st, flags, enc) == 0)
new_pathtype = IFTODT(st.st_mode);
else
new_pathtype = path_noent;
@@ -1888,13 +2299,24 @@ glob_helper(
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)
- *new_end++ = p; /* append recursive pattern */
+ 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) {
@@ -1911,9 +2333,9 @@ glob_helper(
}
}
- status = glob_helper(buf, name - buf + namlen, 1,
+ status = glob_helper(fd, buf, baselen, name - buf - baselen + namlen, 1,
new_pathtype, new_beg, new_end,
- flags, func, arg, enc);
+ flags, funcs, arg, enc);
GLOB_FREE(buf);
GLOB_FREE(new_beg);
if (status) break;
@@ -1970,14 +2392,16 @@ glob_helper(
}
#if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
if ((*cur)->type == ALPHA) {
- long base = pathlen + (dirsep != 0);
- buf = replace_real_basename(buf, base, enc, IF_NORMALIZE_UTF8PATH(1)+0,
+ buf = replace_real_basename(buf, pathlen + (dirsep != 0), enc,
+ IF_NORMALIZE_UTF8PATH(1)+0,
flags, &new_pathtype);
+ if (!buf) break;
}
#endif
- status = glob_helper(buf, pathlen + strlen(buf + pathlen), 1,
+ status = glob_helper(fd, buf, baselen,
+ namelen + strlen(buf + pathlen), 1,
new_pathtype, new_beg, new_end,
- flags, func, arg, enc);
+ flags, funcs, arg, enc);
GLOB_FREE(buf);
GLOB_FREE(new_beg);
if (status) break;
@@ -1991,15 +2415,64 @@ glob_helper(
}
static int
-ruby_glob0(const char *path, int flags, ruby_glob_func *func, VALUE arg, rb_encoding *enc)
+push_caller(const char *path, VALUE val, void *enc)
+{
+ struct push_glob_args *arg = (struct push_glob_args *)val;
+ struct glob_pattern *list;
+ int status;
+
+ list = glob_make_pattern(path, path + strlen(path), arg->flags, enc);
+ if (!list) {
+ 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);
+ glob_free_pattern(list);
+ return status;
+}
+
+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);
+
+struct push_glob0_args {
+ int fd;
+ const char *base;
+ int flags;
+ const ruby_glob_funcs_t *funcs;
+ VALUE arg;
+};
+
+static int
+push_glob0_caller(const char *path, VALUE val, void *enc)
+{
+ struct push_glob0_args *arg = (struct push_glob0_args *)val;
+ return ruby_glob0(path, arg->fd, arg->base, arg->flags, arg->funcs, arg->arg, 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)
{
struct glob_pattern *list;
const char *root, *start;
char *buf;
- size_t n;
- int status;
+ size_t n, baselen = 0;
+ int status, dirsep = FALSE;
start = root = path;
+
+ if (*root == '{') {
+ struct push_glob0_args args;
+ args.fd = fd;
+ args.base = base;
+ args.flags = flags;
+ args.funcs = funcs;
+ args.arg = arg;
+ return ruby_brace_expand(path, flags, push_glob0_caller, (VALUE)&args, enc, Qfalse);
+ }
+
flags |= FNM_SYSCASE;
#if defined DOSISH
root = rb_enc_path_skip_prefix(root, root + strlen(root), enc);
@@ -2008,6 +2481,12 @@ ruby_glob0(const char *path, int flags, ruby_glob_func *func, VALUE arg, rb_enco
if (*root == '/') root++;
n = root - start;
+ if (!n && base) {
+ n = strlen(base);
+ baselen = n;
+ start = base;
+ dirsep = TRUE;
+ }
buf = GLOB_ALLOC_N(char, n + 1);
if (!buf) return -1;
MEMCPY(buf, start, char, n);
@@ -2018,8 +2497,9 @@ ruby_glob0(const char *path, int flags, ruby_glob_func *func, VALUE arg, rb_enco
GLOB_FREE(buf);
return -1;
}
- status = glob_helper(buf, n, 0, path_unknown, &list, &list + 1,
- flags, func, arg, enc);
+ status = glob_helper(fd, buf, baselen, n-baselen, dirsep,
+ path_unknown, &list, &list + 1,
+ flags, funcs, arg, enc);
glob_free_pattern(list);
GLOB_FREE(buf);
@@ -2029,8 +2509,11 @@ ruby_glob0(const char *path, int flags, ruby_glob_func *func, VALUE arg, rb_enco
int
ruby_glob(const char *path, int flags, ruby_glob_func *func, VALUE arg)
{
- return ruby_glob0(path, flags & ~GLOB_VERBOSE, func, arg,
- rb_ascii8bit_encoding());
+ ruby_glob_funcs_t funcs;
+ funcs.match = func;
+ funcs.error = NULL;
+ return ruby_glob0(path, AT_FDCWD, 0, flags & ~GLOB_VERBOSE,
+ &funcs, arg, rb_ascii8bit_encoding());
}
static int
@@ -2044,6 +2527,10 @@ rb_glob_caller(const char *path, VALUE a, void *enc)
return status;
}
+static const ruby_glob_funcs_t rb_glob_funcs = {
+ rb_glob_caller, rb_glob_error,
+};
+
void
rb_glob(const char *path, void (*func)(const char *, VALUE, void *), VALUE arg)
{
@@ -2054,8 +2541,8 @@ rb_glob(const char *path, void (*func)(const char *, VALUE, void *), VALUE arg)
args.value = arg;
args.enc = rb_ascii8bit_encoding();
- status = ruby_glob0(path, GLOB_VERBOSE, rb_glob_caller, (VALUE)&args,
- args.enc);
+ status = ruby_glob0(path, AT_FDCWD, 0, GLOB_VERBOSE, &rb_glob_funcs,
+ (VALUE)&args, args.enc);
if (status) GLOB_JUMP_TAG(status);
}
@@ -2075,7 +2562,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)
+ rb_encoding *enc, VALUE var)
{
const int escape = !(flags & FNM_NOESCAPE);
const char *p = str;
@@ -2120,7 +2607,7 @@ ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg,
}
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);
+ status = ruby_brace_expand(buf, flags, func, arg, enc, var);
if (status) break;
}
GLOB_FREE(buf);
@@ -2129,11 +2616,12 @@ ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg,
status = glob_call_func(func, s, arg, enc);
}
+ RB_GC_GUARD(var);
return status;
}
struct brace_args {
- ruby_glob_func *func;
+ ruby_glob_funcs_t funcs;
VALUE value;
int flags;
};
@@ -2143,7 +2631,7 @@ glob_brace(const char *path, VALUE val, void *enc)
{
struct brace_args *arg = (struct brace_args *)val;
- return ruby_glob0(path, arg->flags, arg->func, arg->value, enc);
+ return ruby_glob0(path, AT_FDCWD, 0, arg->flags, &arg->funcs, arg->value, enc);
}
int
@@ -2152,10 +2640,11 @@ ruby_brace_glob_with_enc(const char *str, int flags, ruby_glob_func *func, VALUE
struct brace_args args;
flags &= ~GLOB_VERBOSE;
- args.func = func;
+ args.funcs.match = func;
+ args.funcs.error = NULL;
args.value = arg;
args.flags = flags;
- return ruby_brace_expand(str, flags, glob_brace, (VALUE)&args, enc);
+ return ruby_brace_expand(str, flags, glob_brace, (VALUE)&args, enc, Qfalse);
}
int
@@ -2164,23 +2653,11 @@ ruby_brace_glob(const char *str, int flags, ruby_glob_func *func, VALUE arg)
return ruby_brace_glob_with_enc(str, flags, func, arg, rb_ascii8bit_encoding());
}
-struct push_glob_args {
- struct glob_args glob;
- int flags;
-};
-
static int
-push_caller(const char *path, VALUE val, void *enc)
+push_glob(VALUE ary, VALUE str, VALUE base, int flags)
{
- struct push_glob_args *arg = (struct push_glob_args *)val;
-
- return ruby_glob0(path, arg->flags, rb_glob_caller, (VALUE)&arg->glob, enc);
-}
-
-static int
-push_glob(VALUE ary, VALUE str, int flags)
-{
- struct push_glob_args args;
+ struct glob_args args;
+ int fd;
rb_encoding *enc = rb_enc_get(str);
#if defined _WIN32 || defined __APPLE__
@@ -2191,48 +2668,75 @@ push_glob(VALUE ary, VALUE str, int flags)
if (rb_enc_to_index(enc) == ENCINDEX_US_ASCII)
enc = rb_ascii8bit_encoding();
flags |= GLOB_VERBOSE;
- args.glob.func = push_pattern;
- args.glob.value = ary;
- args.glob.enc = enc;
- args.flags = flags;
+ args.func = push_pattern;
+ args.value = ary;
+ args.enc = enc;
+ 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();
+#ifdef HAVE_DIRFD
+ if ((fd = dirfd(dirp->dir)) == -1)
+ rb_sys_fail_path(dir_inspect(base));
+#endif
+ base = dirp->path;
+ }
+ args.base = RSTRING_PTR(base);
+ }
#if defined _WIN32 || defined __APPLE__
enc = rb_utf8_encoding();
#endif
- RB_GC_GUARD(str);
- return ruby_brace_expand(RSTRING_PTR(str), flags,
- push_caller, (VALUE)&args, enc);
+ return ruby_glob0(RSTRING_PTR(str), fd, args.base, flags, &rb_glob_funcs,
+ (VALUE)&args, enc);
}
static VALUE
-rb_push_glob(VALUE str, int flags) /* '\0' is delimiter */
+rb_push_glob(VALUE str, VALUE base, int flags) /* '\0' is delimiter */
{
long offset = 0;
+ long len;
VALUE ary;
+ int warned = FALSE;
- GlobPathValue(str, TRUE);
+ /* can contain null bytes as separators */
+ if (!RB_TYPE_P((str), T_STRING)) {
+ FilePathValue(str);
+ }
+ else {
+ rb_check_safe_obj(str);
+ rb_enc_check(str, rb_enc_from_encoding(rb_usascii_encoding()));
+ }
ary = rb_ary_new();
- while (offset < RSTRING_LEN(str)) {
- char *p, *pend;
+ while (offset < (len = RSTRING_LEN(str))) {
int status;
- p = RSTRING_PTR(str) + offset;
- status = push_glob(ary, rb_enc_str_new(p, strlen(p), rb_enc_get(str)),
- flags);
+ long rest = len - offset;
+ const char *pbeg = RSTRING_PTR(str), *p = pbeg + offset;
+ const char *pend = memchr(p, '\0', rest);
+ if (pend) {
+ if (!warned) {
+ rb_warn("use glob patterns list instead of nul-separated patterns");
+ warned = TRUE;
+ }
+ rest = ++pend - p;
+ offset = pend - pbeg;
+ }
+ else {
+ offset = len;
+ }
+ status = push_glob(ary, rb_str_subseq(str, p-pbeg, rest),
+ base, flags);
if (status) GLOB_JUMP_TAG(status);
- if (offset >= RSTRING_LEN(str)) break;
- p += strlen(p) + 1;
- pend = RSTRING_PTR(str) + RSTRING_LEN(str);
- while (p < pend && !*p)
- p++;
- offset = p - RSTRING_PTR(str);
}
return ary;
}
static VALUE
-dir_globs(long argc, const VALUE *argv, int flags)
+dir_globs(long argc, const VALUE *argv, VALUE base, int flags)
{
VALUE ary = rb_ary_new();
long i;
@@ -2240,47 +2744,84 @@ dir_globs(long argc, const VALUE *argv, int flags)
for (i = 0; i < argc; ++i) {
int status;
VALUE str = argv[i];
- GlobPathValue(str, TRUE);
- status = push_glob(ary, str, flags);
+ FilePathValue(str);
+ status = push_glob(ary, str, base, flags);
if (status) GLOB_JUMP_TAG(status);
}
return ary;
}
+static void
+dir_glob_options(VALUE opt, VALUE *base, int *flags)
+{
+ ID kw[2];
+ VALUE args[2];
+ kw[0] = rb_intern("base");
+ if (flags) kw[1] = rb_intern("flags");
+ rb_get_kwargs(opt, kw, 0, flags ? 2 : 1, args);
+ if (args[0] == Qundef || NIL_P(args[0])) {
+ *base = Qnil;
+ }
+#if USE_OPENDIR_AT
+ else if (rb_typeddata_is_kind_of(args[0], &dir_data_type)) {
+ *base = args[0];
+ }
+#endif
+ else {
+ FilePathValue(args[0]);
+ if (!RSTRING_LEN(args[0])) args[0] = Qnil;
+ *base = args[0];
+ }
+ if (flags && args[1] != Qundef) {
+ *flags = NUM2INT(args[1]);
+ }
+}
+
/*
* call-seq:
- * Dir[ string [, string ...] ] -> array
+ * Dir[ string [, string ...] [, base: path] ] -> array
*
* Equivalent to calling
- * <code>Dir.glob([</code><i>string,...</i><code>],0)</code>.
+ * <code>Dir.glob([</code><i>string,...</i><code>], 0)</code>.
*
*/
static VALUE
dir_s_aref(int argc, VALUE *argv, VALUE obj)
{
+ VALUE opts, base;
+ argc = rb_scan_args(argc, argv, "*:", NULL, &opts);
+ dir_glob_options(opts, &base, NULL);
if (argc == 1) {
- return rb_push_glob(argv[0], 0);
+ return rb_push_glob(argv[0], base, 0);
}
- return dir_globs(argc, argv, 0);
+ return dir_globs(argc, argv, base, 0);
}
/*
* call-seq:
- * Dir.glob( pattern, [flags] ) -> matches
- * Dir.glob( pattern, [flags] ) { |filename| block } -> nil
+ * Dir.glob( pattern, [flags], [base: path] ) -> array
+ * Dir.glob( pattern, [flags], [base: path] ) { |filename| block } -> nil
*
- * Expands +pattern+, which is an Array of patterns or a pattern String, and
- * returns the results as +matches+ or as arguments given to the block.
+ * Expands +pattern+, which is a pattern string or an Array of pattern
+ * strings, and returns an array containing the matching filenames.
+ * If a block is given, calls the block once for each matching filename,
+ * passing the filename as a parameter to the block.
*
- * Note that this pattern is not a regexp, it's closer to a shell glob. See
- * File::fnmatch for the meaning of the +flags+ parameter. Note that case
- * sensitivity depends on your system (so File::FNM_CASEFOLD is ignored), as
- * does the order in which the results are returned.
+ * The optional +base+ keyword argument specifies the base directory for
+ * interpreting relative pathnames instead of the current working directory.
+ * As the results are not prefixed with the base directory name in this
+ * case, you will need to prepend the base directory name if you want real
+ * paths.
+ *
+ * Note that the pattern is not a regexp, it's closer to a shell glob.
+ * See File::fnmatch for the meaning of the +flags+ parameter.
+ * Case sensitivity depends on your system (File::FNM_CASEFOLD is ignored),
+ * as does the order in which the results are returned.
*
* <code>*</code>::
* Matches any file. Can be restricted by other values in the glob.
- * Equivalent to <code>/ .* /x</code> in regexp.
+ * Equivalent to <code>/ .* /mx</code> in regexp.
*
* <code>*</code>:: Matches all files
* <code>c*</code>:: Matches all files beginning with <code>c</code>
@@ -2325,11 +2866,16 @@ dir_s_aref(int argc, VALUE *argv, VALUE obj)
* Dir.glob("*.{rb,h}") #=> ["main.rb", "config.h"]
* Dir.glob("*") #=> ["config.h", "main.rb"]
* Dir.glob("*", File::FNM_DOTMATCH) #=> [".", "..", "config.h", "main.rb"]
+ * Dir.glob(["*.rb", "*.h"]) #=> ["main.rb", "config.h"]
*
* rbfiles = File.join("**", "*.rb")
* Dir.glob(rbfiles) #=> ["main.rb",
* # "lib/song.rb",
* # "lib/song/karaoke.rb"]
+ *
+ * Dir.glob(rbfiles, base: "lib") #=> ["song.rb",
+ * # "song/karaoke.rb"]
+ *
* libdirs = File.join("**", "lib")
* Dir.glob(libdirs) #=> ["lib"]
*
@@ -2343,21 +2889,23 @@ dir_s_aref(int argc, VALUE *argv, VALUE obj)
static VALUE
dir_s_glob(int argc, VALUE *argv, VALUE obj)
{
- VALUE str, rflags, ary;
+ VALUE str, rflags, ary, opts, base;
int flags;
- if (rb_scan_args(argc, argv, "11", &str, &rflags) == 2)
+ argc = rb_scan_args(argc, argv, "11:", &str, &rflags, &opts);
+ if (argc == 2)
flags = NUM2INT(rflags);
else
flags = 0;
+ dir_glob_options(opts, &base, &flags);
ary = rb_check_array_type(str);
if (NIL_P(ary)) {
- ary = rb_push_glob(str, flags);
+ ary = rb_push_glob(str, base, flags);
}
else {
VALUE v = ary;
- ary = dir_globs(RARRAY_LEN(v), RARRAY_CONST_PTR(v), flags);
+ ary = dir_globs(RARRAY_LEN(v), RARRAY_CONST_PTR(v), base, flags);
RB_GC_GUARD(v);
}
@@ -2411,6 +2959,14 @@ dir_foreach(int argc, VALUE *argv, VALUE io)
return Qnil;
}
+static VALUE
+dir_collect(VALUE dir)
+{
+ VALUE ary = rb_ary_new();
+ dir_each_entry(dir, rb_ary_push, ary, FALSE);
+ return ary;
+}
+
/*
* call-seq:
* Dir.entries( dirname ) -> array
@@ -2420,8 +2976,8 @@ dir_foreach(int argc, VALUE *argv, VALUE io)
* directory. Will raise a <code>SystemCallError</code> if the named
* directory doesn't exist.
*
- * The optional <i>enc</i> argument specifies the encoding of the directory.
- * If not specified, the filesystem encoding is used.
+ * The optional <i>encoding</i> keyword argument specifies the encoding of the
+ * directory. If not specified, the filesystem encoding is used.
*
* Dir.entries("testdir") #=> [".", "..", "config.h", "main.rb"]
*
@@ -2432,7 +2988,115 @@ dir_entries(int argc, VALUE *argv, VALUE io)
VALUE dir;
dir = dir_open_dir(argc, argv);
- return rb_ensure(rb_Array, dir, dir_close, dir);
+ return rb_ensure(dir_collect, dir, dir_close, dir);
+}
+
+static VALUE
+dir_each_child(VALUE dir)
+{
+ return dir_each_entry(dir, dir_yield, Qnil, TRUE);
+}
+
+/*
+ * call-seq:
+ * Dir.each_child( dirname ) {| filename | block } -> nil
+ * Dir.each_child( dirname, encoding: enc ) {| filename | block } -> nil
+ * Dir.each_child( dirname ) -> an_enumerator
+ * Dir.each_child( dirname, encoding: enc ) -> an_enumerator
+ *
+ * Calls the block once for each entry except for "." and ".." in the
+ * named directory, passing the filename of each entry as a parameter
+ * to the block.
+ *
+ * If no block is given, an enumerator is returned instead.
+ *
+ * Dir.each_child("testdir") {|x| puts "Got #{x}" }
+ *
+ * <em>produces:</em>
+ *
+ * Got config.h
+ * Got main.rb
+ *
+ */
+static VALUE
+dir_s_each_child(int argc, VALUE *argv, VALUE io)
+{
+ VALUE dir;
+
+ RETURN_ENUMERATOR(io, argc, argv);
+ dir = dir_open_dir(argc, argv);
+ rb_ensure(dir_each_child, dir, dir_close, dir);
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * dir.each_child {| filename | block } -> nil
+ * dir.each_child -> an_enumerator
+ *
+ * Calls the block once for each entry except for "." and ".." in
+ * this directory, passing the filename of each entry as a parameter
+ * to the block.
+ *
+ * If no block is given, an enumerator is returned instead.
+ *
+ * d = Dir.new("testdir")
+ * d.each_child {|x| puts "Got #{x}" }
+ *
+ * <em>produces:</em>
+ *
+ * Got config.h
+ * Got main.rb
+ *
+ */
+static VALUE
+dir_each_child_m(VALUE dir)
+{
+ RETURN_ENUMERATOR(dir, 0, 0);
+ return dir_each_entry(dir, dir_yield, Qnil, TRUE);
+}
+
+/*
+ * call-seq:
+ * dir.children -> array
+ *
+ * Returns an array containing all of the filenames except for "."
+ * and ".." in this directory.
+ *
+ * d = Dir.new("testdir")
+ * d.children #=> ["config.h", "main.rb"]
+ *
+ */
+static VALUE
+dir_collect_children(VALUE dir)
+{
+ VALUE ary = rb_ary_new();
+ dir_each_entry(dir, rb_ary_push, ary, TRUE);
+ return ary;
+}
+
+/*
+ * call-seq:
+ * Dir.children( dirname ) -> array
+ * Dir.children( dirname, encoding: enc ) -> array
+ *
+ * Returns an array containing all of the filenames except for "."
+ * and ".." in the given directory. Will raise a
+ * <code>SystemCallError</code> if the named directory doesn't exist.
+ *
+ * The optional <i>encoding</i> keyword argument specifies the encoding of the
+ * directory. If not specified, the filesystem encoding is used.
+ *
+ * Dir.children("testdir") #=> ["config.h", "main.rb"]
+ *
+ */
+static VALUE
+dir_s_children(int argc, VALUE *argv, VALUE io)
+{
+ VALUE dir;
+
+ dir = dir_open_dir(argc, argv);
+ return rb_ensure(dir_collect_children, dir, dir_close, dir);
}
static int
@@ -2566,7 +3230,7 @@ file_s_fnmatch(int argc, VALUE *argv, VALUE obj)
else
flags = 0;
- StringValue(pattern);
+ StringValueCStr(pattern);
FilePathStringValue(path);
if (flags & FNM_EXTGLOB) {
@@ -2575,7 +3239,7 @@ file_s_fnmatch(int argc, VALUE *argv, VALUE obj)
args.value = path;
args.flags = flags;
if (ruby_brace_expand(RSTRING_PTR(pattern), flags, fnmatch_brace,
- (VALUE)&args, rb_enc_get(pattern)) > 0)
+ (VALUE)&args, rb_enc_get(pattern), pattern) > 0)
return Qtrue;
}
else {
@@ -2645,6 +3309,38 @@ rb_dir_exists_p(VALUE obj, VALUE fname)
return rb_file_directory_p(obj, fname);
}
+static void *
+nogvl_dir_empty_p(void *ptr)
+{
+ const char *path = ptr;
+ DIR *dir = opendir(path);
+ struct dirent *dp;
+ 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;
+ }
+ }
+ while ((dp = READDIR(dir, NULL)) != NULL) {
+ if (!to_be_skipped(dp)) {
+ result = Qfalse;
+ break;
+ }
+ }
+ closedir(dir);
+ return (void *)result;
+}
+
/*
* call-seq:
* Dir.empty?(path_name) -> true or false
@@ -2655,13 +3351,11 @@ rb_dir_exists_p(VALUE obj, VALUE fname)
static VALUE
rb_dir_s_empty_p(VALUE obj, VALUE dirname)
{
- DIR *dir;
- struct dirent *dp;
- VALUE result = Qtrue, orig;
+ VALUE result, orig;
const char *path;
enum {false_on_notdir = 1};
- GlobPathValue(dirname, FALSE);
+ FilePathValue(dirname);
orig = rb_str_dup_frozen(dirname);
dirname = rb_str_encode_ospath(dirname);
dirname = rb_str_dup_frozen(dirname);
@@ -2686,28 +3380,11 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirname)
}
#endif
- dir = opendir(path);
- if (!dir) {
- int e = errno;
- switch (rb_gc_for_fd(e)) {
- default:
- dir = opendir(path);
- if (dir) break;
- e = errno;
- /* fall through */
- case 0:
- if (false_on_notdir && e == ENOTDIR) return Qfalse;
- rb_syserr_fail_path(e, orig);
- }
- }
- errno = 0;
- while ((dp = READDIR(dir, NULL)) != NULL) {
- if (!to_be_skipped(dp)) {
- result = Qfalse;
- break;
- }
+ 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);
}
- closedir(dir);
return result;
}
@@ -2733,6 +3410,8 @@ Init_Dir(void)
rb_define_singleton_method(rb_cDir, "open", dir_s_open, -1);
rb_define_singleton_method(rb_cDir, "foreach", dir_foreach, -1);
rb_define_singleton_method(rb_cDir, "entries", dir_entries, -1);
+ rb_define_singleton_method(rb_cDir, "each_child", dir_s_each_child, -1);
+ rb_define_singleton_method(rb_cDir, "children", dir_s_children, -1);
rb_define_method(rb_cDir,"initialize", dir_initialize, -1);
rb_define_method(rb_cDir,"fileno", dir_fileno, 0);
@@ -2741,6 +3420,8 @@ Init_Dir(void)
rb_define_method(rb_cDir,"inspect", dir_inspect, 0);
rb_define_method(rb_cDir,"read", dir_read, 0);
rb_define_method(rb_cDir,"each", dir_each, 0);
+ rb_define_method(rb_cDir,"each_child", dir_each_child_m, 0);
+ rb_define_method(rb_cDir,"children", dir_collect_children, 0);
rb_define_method(rb_cDir,"rewind", dir_rewind, 0);
rb_define_method(rb_cDir,"tell", dir_tell, 0);
rb_define_method(rb_cDir,"seek", dir_seek, 1);
diff --git a/dln.c b/dln.c
index 5af9a57802..7acea5f888 100644
--- a/dln.c
+++ b/dln.c
@@ -22,6 +22,7 @@
static void dln_loaderror(const char *format, ...);
#endif
#include "dln.h"
+#include "internal.h"
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
@@ -231,13 +232,13 @@ load_header(int fd, struct exec *hdrp, long disp)
# define R_RIGHTSHIFT(r) (reloc_r_rightshift[(r)->r_type])
# define R_BITSIZE(r) (reloc_r_bitsize[(r)->r_type])
# define R_LENGTH(r) (reloc_r_length[(r)->r_type])
-static int reloc_r_rightshift[] = {
+static const int reloc_r_rightshift[] = {
0, 0, 0, 0, 0, 0, 2, 2, 10, 0, 0, 0, 0, 0, 0,
};
-static int reloc_r_bitsize[] = {
+static const int reloc_r_bitsize[] = {
8, 16, 32, 8, 16, 32, 30, 22, 22, 22, 13, 10, 32, 32, 16,
};
-static int reloc_r_length[] = {
+static const int reloc_r_length[] = {
0, 1, 2, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
};
# define R_PCREL(r) \
@@ -1242,6 +1243,20 @@ rb_w32_check_imported(HMODULE ext, HMODULE mine)
#define translit_separator(str) (void)(str)
#endif
+#ifdef USE_DLN_DLOPEN
+COMPILER_WARNING_PUSH
+#if defined(__clang__) || GCC_VERSION_SINCE(4, 2, 0)
+COMPILER_WARNING_IGNORED(-Wpedantic)
+#endif
+static bool
+dln_incompatible_library_p(void *handle)
+{
+ void *ex = dlsym(handle, EXTERNAL_PREFIX"ruby_xmalloc");
+ return ex && ex != ruby_xmalloc;
+}
+COMPILER_WARNING_POP
+#endif
+
void*
dln_load(const char *file)
{
@@ -1250,7 +1265,6 @@ dln_load(const char *file)
#endif
#if !defined(_AIX) && !defined(NeXT)
const char *error = 0;
-#define DLN_ERROR() (error = dln_strerror(), strcpy(ALLOCA_N(char, strlen(error) + 1), error))
#endif
#if defined _WIN32
@@ -1330,10 +1344,11 @@ dln_load(const char *file)
}
# if defined RUBY_EXPORT
{
- void *ex = dlsym(handle, EXTERNAL_PREFIX"ruby_xmalloc");
- if (ex && ex != ruby_xmalloc) {
+ if (dln_incompatible_library_p(handle)) {
-# if defined __APPLE__
+# if defined __APPLE__ && \
+ defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
+ (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11)
/* dlclose() segfaults */
rb_fatal("%s - %s", incompatible, file);
# else
@@ -1347,7 +1362,8 @@ dln_load(const char *file)
init_fct = (void(*)())(VALUE)dlsym(handle, buf);
if (init_fct == NULL) {
- error = DLN_ERROR();
+ const size_t errlen = strlen(error = dln_strerror()) + 1;
+ error = memcpy(ALLOCA_N(char, errlen), error, errlen);
dlclose(handle);
goto failed;
}
diff --git a/dmydln.c b/dmydln.c
index 0fc0a5325e..d05cda0b8e 100644
--- a/dmydln.c
+++ b/dmydln.c
@@ -6,5 +6,5 @@ dln_load(const char *file)
{
rb_loaderror("this executable file can't load extension libraries");
- UNREACHABLE;
+ UNREACHABLE_RETURN(NULL);
}
diff --git a/doc/.document b/doc/.document
index b48c0387a7..d739c9f6bc 100644
--- a/doc/.document
+++ b/doc/.document
@@ -1,4 +1,3 @@
*.rdoc
-ChangeLog*
NEWS-*
syntax
diff --git a/doc/ChangeLog-0.60_to_1.1 b/doc/ChangeLog-0.60_to_1.1
index 33b0326892..bd5f140dc3 100644
--- a/doc/ChangeLog-0.60_to_1.1
+++ b/doc/ChangeLog-0.60_to_1.1
@@ -3166,7 +3166,7 @@ Fri Aug 11 14:37:03 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
* io.c: マクロREAD_DATA_PENDINGã®å®šç¾©ã‚’変更(Linux対応)
- * io.c (io_fptr_finalize): fptrã®é–‹æ”¾æ™‚ã®å‡¦ç†ã‚’指定ã§ãるよã†ã«ï¼Ž
+ * io.c (io_fptr_finalize): fptrã®è§£æ”¾æ™‚ã®å‡¦ç†ã‚’指定ã§ãるよã†ã«ï¼Ž
Wed Aug 9 16:52:41 1995 Yukihiro Matsumoto <matz@caelum.co.jp>
@@ -3448,7 +3448,7 @@ Thu May 18 12:27:23 1995 Yukihiro Matsumoto <matz@ix-02>
ç„¡ããªã£ãŸ(ã¨æ€ã†).
* gc.c (gc): the_scopeをマークã—ã¦ã„ãªã‹ã£ãŸã®ã§ï¼Œãƒ­ãƒ¼ã‚«ãƒ«å¤‰æ•°ã®æŒ‡
- ã—ã¦ã„るオブジェクトãŒé–“é•ã£ã¦é–‹æ”¾ã•れる場åˆãŒã‚ã£ãŸï¼Ž
+ ã—ã¦ã„るオブジェクトãŒé–“é•ã£ã¦è§£æ”¾ã•れる場åˆãŒã‚ã£ãŸï¼Ž
* gc.c (mark_locations_array): 若干ã®é«˜é€ŸåŒ–.
diff --git a/doc/ChangeLog-1.9.3 b/doc/ChangeLog-1.9.3
index 93b7763ef0..eecfc44325 100644
--- a/doc/ChangeLog-1.9.3
+++ b/doc/ChangeLog-1.9.3
@@ -3325,7 +3325,7 @@ Thu May 19 06:30:38 2011 Eric Hodel <drbrain@segment7.net>
Thu May 19 06:16:41 2011 Eric Hodel <drbrain@segment7.net>
* lib: Convert tabs to spaces for ruby files per
- http://bugs.ruby-lang.org/projects/ruby/wiki/DeveloperHowto#coding-style
+ https://bugs.ruby-lang.org/projects/ruby/wiki/DeveloperHowto#coding-style
Patch by Steve Klabnik [Ruby 1.9 - Bug #4730]
Patch by Jason Dew [Ruby 1.9 - Feature #4718]
@@ -12814,7 +12814,7 @@ Thu Aug 5 22:09:30 2010 Tanaka Akira <akr@fsij.org>
Thu Aug 5 21:20:31 2010 Yusuke Endoh <mame@tsg.ne.jp>
* compile.c (NODE_ARGSCAT, NODE_ARGSPUSH): drop unused ARGSCAT
- results when poped is true. [ruby-dev:41933], [Bug #3658]
+ results when popped is true. [ruby-dev:41933], [Bug #3658]
This is retry of r28870 and r28873 which were reverted.
Thu Aug 5 20:13:49 2010 Nobuyoshi Nakada <nobu@ruby-lang.org>
@@ -82601,7 +82601,7 @@ Sat Sep 18 14:10:23 2004 Yukihiro Matsumoto <matz@ruby-lang.org>
* dir.c (dir_s_chdir): avoid memory leak and unnecessary chdir to
the original directory when exception has caused in changing
- direcotry or within block. thanks to Johan Holmberg
+ directory or within block. thanks to Johan Holmberg
<holmberg@iar.se> [ruby-core:03446]
Fri Sep 17 20:29:33 2004 NAKAMURA Usaku <usa@ruby-lang.org>
diff --git a/doc/ChangeLog-2.4.0 b/doc/ChangeLog-2.4.0
index 1714b7caec..96b5ecb077 100644
--- a/doc/ChangeLog-2.4.0
+++ b/doc/ChangeLog-2.4.0
@@ -4690,7 +4690,7 @@ Thu May 19 13:22:44 2016 Kazuki Yamaguchi <k@rhe.jp>
* ext/openssl/ossl.c (Init_openssl): register an ex_data index for
X509_STORE and X509_STORE_CTX respectively. Since they don't share
the ex_data index registry, we can't use the same index.
- (ossl_verify_cb): use the the correct index.
+ (ossl_verify_cb): use the correct index.
* ext/openssl/ossl_ssl.c (ossl_ssl_verify_callback): ditto.
diff --git a/doc/ChangeLog-2016 b/doc/ChangeLog-2016
index c708428a93..14fcba55ab 100644
--- a/doc/ChangeLog-2016
+++ b/doc/ChangeLog-2016
@@ -1,5 +1,6 @@
------------------------------------------------------------------------
-r56645 | naruse | 2016-11-07 00:56:27 +0900 (Mon, 07 Nov 2016) | 1 line
+r57181 | matz | 2016-12-26 01:35:51 +0900 (Mon, 26 Dec 2016) | 2 lines
+
+version.h (RUBY_VERSION): 2.5.0 development has started.
-Obsolete ChangeLog [Feature #12283]
------------------------------------------------------------------------
diff --git a/doc/ChangeLog-2017 b/doc/ChangeLog-2017
new file mode 100644
index 0000000000..82c4f7c623
--- /dev/null
+++ b/doc/ChangeLog-2017
@@ -0,0 +1,6 @@
+------------------------------------------------------------------------
+r61474 | matz | 2017-12-25 23:05:59 +0900 (Mon, 25 Dec 2017) | 2 lines
+
+version.h (RUBY_VERSION): 2.6.0 development has started.
+
+------------------------------------------------------------------------
diff --git a/doc/ChangeLog-YARV b/doc/ChangeLog-YARV
index 3a49ebaef1..a8b999dff2 100644
--- a/doc/ChangeLog-YARV
+++ b/doc/ChangeLog-YARV
@@ -5783,7 +5783,7 @@ Sun Dec 31 17:42:05 2006 Koichi Sasada <ko1@atdot.net>
* disasm.c : fix ID to String method
- * compile.c : NODE_SUPER, NODE_ZSUPER check 'poped'
+ * compile.c : NODE_SUPER, NODE_ZSUPER check 'popped'
and NODE_RETURN check outer type
and NODE_DREGX_ONCE supported (temporarily)
diff --git a/doc/NEWS-1.8.7 b/doc/NEWS-1.8.7
index 38571fe103..5da39ff265 100644
--- a/doc/NEWS-1.8.7
+++ b/doc/NEWS-1.8.7
@@ -417,7 +417,7 @@ with all sufficient information, see the ChangeLog file.
* FTP
* URI('ftp://example.com/foo').path #=> 'foo'
* URI('ftp://example.com/%2Ffoo').path #=> '/foo'
- * URI::FTP.build([nil, 'example.com', nil, '/foo', 'i').to_s #=> 'ftp://example.com/%2Ffoo;type=i'
+ * URI::FTP.build([nil, 'example.com', nil, '/foo', 'i']).to_s #=> 'ftp://example.com/%2Ffoo;type=i'
* URI merge
* URI('http://a/b/c/d;p?q').merge('?y') == URI('http://a/b/c/d;p?y')
* URI('http://a/b/c/d;p?q').merge('/./g') == URI('http://a/g')
@@ -518,7 +518,7 @@ with all sufficient information, see the ChangeLog file.
New method to set the entity expansion limit. By default the limit is
set to 10000. See the following URL for details.
- http://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/
+ https://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/
* stringio
diff --git a/doc/NEWS-1.9.2 b/doc/NEWS-1.9.2
index 9cf58c9aff..fedb1f6633 100644
--- a/doc/NEWS-1.9.2
+++ b/doc/NEWS-1.9.2
@@ -263,7 +263,7 @@ with all sufficient information, see the ChangeLog file.
New method to set the entity expansion limit. By default the limit is
set to 10000. See the following URL for details.
- http://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/
+ https://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/
* RDoc
diff --git a/doc/NEWS-2.0.0 b/doc/NEWS-2.0.0
index 9ad7254317..414789dcd1 100644
--- a/doc/NEWS-2.0.0
+++ b/doc/NEWS-2.0.0
@@ -94,7 +94,7 @@ with all sufficient information, see the ChangeLog file.
required caller size.
* Kernel#to_enum and enum_for accept a block for lazy size evaluation.
* incompatible changes:
- * system() and exec() closes non-standard file descriptors
+ * system() and exec() close non-standard file descriptors
(The default of :close_others option is changed to true by default.)
* respond_to? against a protected method now returns false unless
the second argument is true.
@@ -497,7 +497,7 @@ with all sufficient information, see the ChangeLog file.
* Dir.mktmpdir uses FileUtils.remove_entry instead of
FileUtils.remove_entry_secure. This means that applications should not
change the permission of the created temporary directory to make
- accessible from other users.
+ writable from other users.
* yaml
* Syck has been removed. YAML now completely depends on libyaml being
@@ -528,4 +528,3 @@ with all sufficient information, see the ChangeLog file.
* NUM2SHORT() and NUM2USHORT() added. They are similar to NUM2INT, but short.
* rb_newobj_of() and NEWOBJ_OF() added. They create a new object of a given class.
-
diff --git a/doc/NEWS-2.4.0 b/doc/NEWS-2.4.0
new file mode 100644
index 0000000000..28e855cde1
--- /dev/null
+++ b/doc/NEWS-2.4.0
@@ -0,0 +1,397 @@
+# -*- rdoc -*-
+
+= NEWS for Ruby 2.4.0
+
+This document is a list of user visible feature changes made between
+releases except for bug fixes.
+
+Note that each entry is kept so brief that no reason behind or
+reference information is supplied with. For a full list of changes
+with all sufficient information, see the ChangeLog file or Redmine
+(e.g. <tt>https://bugs.ruby-lang.org/issues/$FEATURE_OR_BUG_NUMBER</tt>)
+
+== Changes since the 2.3.0 release
+
+=== Language changes
+
+* Multiple assignment in conditional expression is now allowed.
+ [Feature #10617]
+
+* Refinements is enabled at method by Symbol#to_proc. [Feature #9451]
+
+* Refinements is enabled with Kernel#send and BasicObject#__send__.
+ [Feature #11476]
+
+* Rescue modifier now applicable to method arguments.
+ [Feature #12686]
+
+* Toplevel return is now allowed. [Feature #4840]
+
+=== Core classes updates (outstanding ones only)
+
+* Array
+
+ * Array#concat [Feature #12333]
+ Now takes multiple arguments.
+
+ * Array#max and Array#min. [Feature #12172]
+ This may cause a tiny incompatibility: if you redefine
+ Enumerable#max and call max to an Array, your redefinition will be
+ now ignored. You should also redefine Array#max.
+
+ * Array#pack [Feature #12754]
+ Now takes optional argument `buffer:' to reuse already allocated buffer.
+
+ * Array#sum [Feature #12217]
+ This is different from Enumerable#sum in that Array#sum doesn't depend on
+ the definition of each method.
+
+* Comparable
+
+ * Comparable#clamp. [Feature #10594]
+
+* Dir
+
+ * Dir.empty?. [Feature #10121]
+
+* Enumerable
+
+ * Enumerable#chunk called without a block now return an Enumerator
+ [Feature #2172]
+ * Enumerable#sum [Feature #12217]
+ * Enumerable#uniq [Feature #11090]
+
+* Enumerator::Lazy
+
+ * Enumerator::Lazy#chunk_while [GH-1186]
+ * Enumerator::Lazy#uniq [Feature #11090]
+
+* File
+
+ * File.empty?. [Feature #9969]
+
+* Float
+
+ * Float#ceil, Float#floor, and Float#truncate now take an optional
+ digits, as well as Float#round. [Feature #12245]
+
+ * Float#round now takes an optional keyword argument, half option, and
+ the default behavior is round-up. [Bug #12548] [Bug #12958]
+ half option can be one of :even, :up, and :down. [Feature #12953]
+
+* Hash
+
+ * Hash#compact and Hash#compact! [Feature #11818]
+ * Hash#transform_values and Hash#transform_values! [Feature #12512]
+
+* Integer
+
+ * Fixnum and Bignum are unified into Integer [Feature #12005]
+
+ * Integer#ceil, Integer#floor, and Integer#truncate now take an optional
+ digits, as well as Integer#round. [Feature #12245]
+
+ * Integer#digits for extracting columns of place-value notation [Feature #12447]
+
+ * Integer#round now takes an optional keyword argument, half option, and the
+ default behavior is round-up now. [Bug #12548] [Bug #12958]
+ half option can be one of :even, :up, and :down. [Feature #12953]
+
+* IO
+
+ * IO#gets, IO#readline, IO#each_line, IO#readlines, IO.foreach now takes
+ an optional keyword argument, chomp flag. [Feature #12553]
+
+* Kernel
+
+ * Kernel#clone now takes an optional keyword argument, freeze flag.
+ [Feature #12300]
+
+* MatchData
+
+ * MatchData#named_captures [Feature #11999]
+ * MatchData#values_at supports named captures [Feature #9179]
+
+* Module
+
+ * Module#refine accepts a module as the argument now. [Feature #12534]
+ * Module.used_modules [Feature #7418]
+
+* Numeric
+
+ * Numeric#finite?, Numeric#infinite? [Feature #12039]
+
+* Process
+
+ * Support CLOCK_MONOTONIC_RAW_APPROX, CLOCK_UPTIME_RAW, and
+ CLOCK_UPTIME_RAW_APPROX which are introduced by macOS 10.12.
+
+* Rational
+
+ * Rational#round now takes an optional keyword argument, half option, and
+ the default behavior is round-up now. [Bug #12548] [Bug #12958]
+ half option can be one of :even, :up, and :down. [Feature #12953]
+
+* Regexp
+
+ * meta character \X matches Unicode 9.0 characters with some workarounds
+ for UTR #51 Unicode Emoji, Version 4.0 emoji zwj sequences.
+
+ * Regexp#match? [Feature #8110]
+ This returns bool and doesn't save backref.
+
+ * Update to Onigmo 6.0.0.
+
+* Regexp/String: Update Unicode version from 8.0.0 to 9.0.0 [Feature #12513]
+
+* RubyVM::Env
+
+ * RubyVM::Env was removed.
+
+* String
+
+ * String#casecmp? [Feature #12786]
+
+ * String#concat, String#prepend [Feature #12333]
+ Now takes multiple arguments.
+
+ * String#each_line, String#lines now takes an optional keyword argument,
+ chomp flag. [Feature #12553]
+
+ * String#match? [Feature #12898]
+
+ * String#unpack1 [Feature #12752]
+
+ * String#upcase, String#downcase, String#capitalize, String#swapcase and
+ their bang variants work for all of Unicode, and are no longer limited
+ to ASCII. Supported encodings are UTF-8, UTF-16BE/LE, UTF-32BE/LE, and
+ ISO-8859-1~16. Variations are available with options. See the documentation
+ of String#downcase for details. [Feature #10085]
+
+ * String.new(capacity: size) [Feature #12024]
+
+* StringIO
+
+ * StringIO#gets, StringIO#readline, StringIO#each_line, StringIO#readlines now takes
+ an optional keyword argument, chomp flag. [Feature #12553]
+
+* Symbol
+
+ * Symbol#casecmp? [Feature #12786]
+
+ * Symbol#match now returns MatchData. [Bug #11991]
+
+ * Symbol#match? [Feature #12898]
+
+ * Symbol#upcase, Symbol#downcase, Symbol#capitalize, and Symbol#swapcase now
+ work for all of Unicode. See the documentation of String#downcase
+ for details. [Feature #10085]
+
+* Thread
+
+ * Thread#report_on_exception and Thread.report_on_exception
+ [Feature #6647]
+
+* TracePoint
+
+ * TracePoint#callee_id [Feature #12747]
+
+* Warning
+
+ * New module named Warning is introduced. By default it has only
+ one singleton method, named warn. This makes it possible for
+ 3rd-party libraries to control the way warnings are handled.
+ [Feature #12299]
+
+=== Stdlib updates (outstanding ones only)
+
+* CGI
+
+ * Don't allow , as a separator [Bug #12791]
+
+* CSV
+
+ * Add a liberal_parsing option. [Feature #11839]
+
+* IPAddr
+
+ * IPAddr#== and IPAddr#<=> no longer raise an exception if coercion fails.
+ [Bug #12799]
+
+* IRB
+
+ * Binding#irb: Start a REPL session like `binding.pry` at r56624.
+
+* Logger
+
+ * Allow specifying logger parameters in constructor such
+ as level, progname, datetime_format, formatter. [Feature #12224]
+ * Add shift_period_suffix option. [Feature #10772]
+
+* Net::HTTP
+
+ * New method: Net::HTTP.post [Feature #12375]
+
+* Net::FTP
+
+ * Support TLS (RFC 4217).
+ * Support hash style options for Net::FTP.new.
+ * Add a new optional argument pathname to Net::FTP#status.
+ Contributed by soleboxy. [GH-1478] [Feature #12965]
+
+* OpenSSL
+
+ * Includes Ruby/OpenSSL 2.0. OpenSSL has been extracted as a Gem and is
+ maintained at a separate repository now: https://github.com/ruby/openssl.
+ It still remains as a 'default gem'. [Feature #9612]
+ Refer to ext/openssl/History.md for the full release note.
+
+* optparse
+
+ * Add an into option. [Feature #11191]
+
+* pathname
+
+ * New method: Pathname#empty? [Feature #12596]
+
+* Readline
+
+ * Readline.quoting_detection_proc and Readline.quoting_detection_proc=
+ [Feature #12659]
+
+* REXML
+
+ * REXML::Element#[]: If String or Symbol is specified, attribute
+ value is returned. Otherwise, Nth child is returned. This is
+ backward compatible change.
+
+* set
+
+ * New methods: Set#compare_by_identity and Set#compare_by_identity?.
+ [Feature #12210]
+
+* WEBrick
+
+ * Don't allow , as a separator [Bug #12791]
+
+=== Compatibility issues (excluding feature bug fixes)
+
+* Array#sum and Enumerable#sum are implemented. [Feature #12217]
+ Ruby itself has no compatibility problem because Ruby didn't have sum method
+ for arrays before Ruby 2.4.
+ However many third party gems, activesupport, facets, simple_stats, etc,
+ defines sum method. These implementations are mostly compatible but
+ there are subtle differences.
+ Ruby's sum method should be mostly compatible but it is impossible to
+ be perfectly compatible with all of them.
+
+* Fixnum and Bignum are unified into Integer [Feature #12005]
+ Fixnum class and Bignum class is removed.
+ Integer class is changed from abstract class to concrete class.
+ For example, 0 is an instance of Integer: 0.class returns Integer.
+ The constants Fixnum and Bignum is bound to Integer.
+ So obj.kind_of?(Fixnum) works as obj.kind_of?(Integer).
+ At C-level, Fixnum object and Bignum object should be distinguished by
+ FIXNUM_P(obj) and RB_TYPE_P(obj, T_BIGNUM).
+ RUBY_INTEGER_UNIFICATION can be used to detect this feature at C-level.
+ 0.class == Integer can be used to detect this feature at Ruby-level.
+ The C-level constants, rb_cFixnum and rb_cBignum, are removed.
+ They can cause compilation failure.
+
+* String/Symbol#upcase/downcase/swapcase/capitalize(!) now work for all of
+ Unicode, not only for ASCII. [Feature #10085]
+ No change is needed if the data is in ASCII anyway or if the limitation
+ to ASCII was only tolerated while waiting for a more extensive implementation.
+ A change (using the :ascii option) is needed in cases where Unicode data
+ is processed, but the operation has to be limited to ASCII only.
+ A good example of this are internationalized domain names.
+
+* TRUE / FALSE / NIL
+ These constants are now obsoleted. [Feature #12574]
+ Use true / false / nil resp. instead.
+
+=== Stdlib compatibility issues (excluding feature bug fixes)
+
+* DateTime
+
+ * DateTime#to_time now preserves timezone. [Bug #12189]
+
+* Psych
+
+ * Update to Psych 2.2.2
+
+* RDoc
+
+ * Update to RDoc 5.0.0
+
+* RubyGems
+
+ * Update to RubyGems 2.6.8
+
+* shellwords
+
+ * Shellwords.shellwords (shellsplit) treats the backslash as escape
+ character only when followed by one of the following characters:
+ $ ` " \ <newline>
+ [Bug #10055]
+
+* Time
+
+ * Time#to_time now preserves timezone. [Bug #12271]
+
+* thread
+
+ * the extension library is removed. Till 2.0 it was a pure ruby script
+ "thread.rb", which has precedence over "thread.so", and has been provided
+ in $LOADED_FEATURES since 2.1.
+
+* Tk
+
+ * Tk is removed from stdlib. [Feature #8539]
+ https://github.com/ruby/tk is the new upstream.
+
+* XMLRPC
+
+ * XMLRPC is removed from stdlib, and bundled as gem. [Feature #12160][ruby-core:74239]
+ https://github.com/ruby/xmlrpc is the new upstream.
+
+* Zlib
+
+ * Zlib.gzip and Zlib.gunzip [Feature #13020]
+
+=== C API updates
+
+* ruby_show_version() will no longer exits the process, if
+ RUBY_SHOW_COPYRIGHT_TO_DIE is set to 0. This will be the default in
+ the future.
+
+* rb_gc_adjust_memory_usage() [Feature #12690]
+
+=== Supported platform changes
+
+* FreeBSD < 4 is no longer supported
+
+=== Implementation improvements
+
+* In some condition, `[x, y].max` and `[x, y].min` are optimized
+ so that a temporal array is not created. The concrete condition is
+ an implementation detail: currently, the array literal must have no
+ splat, must have at least one expression but literal, the length must
+ be <= 0x100, and Array#max and min must not be redefined. It will work
+ in most casual and real-life use case where it is written with intent
+ to `Math.max(x, y)`.
+
+* Thread deadlock detection now shows their backtrace and dependency. [Feature #8214]
+
+* st_table (st.c) internal data structure is improved. [Feature #12142]
+
+* Rational is extensively optimized. [Feature #12484]
+
+=== Miscellaneous changes
+
+* ChangeLog is removed from the repository.
+ It is generated from commit messages in Subversion by `make dist`.
+ Also note that now people should follow Git style commit message.
+ The template is written at
+ [Short (50 chars or less) summary of changes](https://git-scm.com/book/ch5-2.html).
+ [Feature #12283]
diff --git a/doc/NEWS-2.5.0 b/doc/NEWS-2.5.0
new file mode 100644
index 0000000000..e7613d5caa
--- /dev/null
+++ b/doc/NEWS-2.5.0
@@ -0,0 +1,567 @@
+# -*- rdoc -*-
+
+= NEWS for Ruby 2.5.0
+
+This document is a list of user visible feature changes made between
+releases except for bug fixes.
+
+Note that each entry is kept so brief that no reason behind or
+reference information is supplied with. For a full list of changes
+with all sufficient information, see the ChangeLog file or Redmine
+(e.g. <tt>https://bugs.ruby-lang.org/issues/$FEATURE_OR_BUG_NUMBER</tt>)
+
+== Changes since the 2.4.0 release
+
+=== Language changes
+
+* Top-level constant look-up is removed. [Feature #11547]
+
+* rescue/else/ensure are allowed inside do/end blocks. [Feature #12906]
+
+* refinements take place in string interpolations. [Feature #13812]
+
+=== Core classes updates (outstanding ones only)
+
+* Array
+
+ * New methods:
+
+ * Array#append [Feature #12746]
+ * Array#prepend [Feature #12746]
+
+* Data
+
+ * Is deprecated. It was a base class for C extensions, and it's not
+ necessary to expose in Ruby level. [Feature #3072]
+
+* Exception
+
+ * New methods:
+
+ * Exception#full_message to retrieve a String expression of an exception,
+ formatted in the same way in which Ruby prints out an uncaught
+ exception. [Feature #14141] [experimental]
+
+* Dir
+
+ * Dir.glob provides new optional keyword argument, +:base+ . [Feature #13056]
+ * Dir.chdir (without block arg), Dir.open, Dir.new, Dir.mkdir, Dir.rmdir,
+ Dir.empty? releases GVL
+
+ * New methods:
+
+ * Dir.children [Feature #11302]
+ * Dir.each_child [Feature #11302]
+
+* Enumerable
+
+ * Enumerable#any?, Enumerable#all?, Enumerable#none? and Enumerable#one?
+ accept a pattern argument. [Feature #11286]
+
+* File
+
+ * File.open accepts +:newline+ option to imply text mode. [Bug #13350]
+ * File#path raises an IOError for files opened with
+ File::Constants::TMPFILE option. [Feature #13568]
+ * File.stat, File.exist? and other <code>rb_stat()</code>-using methods
+ release GVL. [Bug #13941]
+ * File.rename releases GVL. [Feature #13951]
+ * File::Stat#atime, File::Stat#mtime and File::Stat#ctime support fractional
+ second timestamps on Windows 8 and later. [Feature #13726]
+ * File::Stat#ino and File.indentical? support ReFS 128bit ino on Windows 8.1
+ and later. [Feature #13731]
+ * File.readable?, File.readable_real?, File.writable?, File.writable_real?,
+ File.executable?, File.executable_real?, File.mkfifo, File.readlink,
+ File.truncate, File#truncate, File.chmod, File.lchmod, File.chown,
+ File.lchown, File.unlink, File.utime, File.lstat release GVL
+
+ * New method:
+
+ * File.lutime [Feature #4052]
+
+* Hash
+
+ * New methods:
+
+ * Hash#transform_keys [Feature #13583]
+ * Hash#transform_keys! [Feature #13583]
+ * Hash#slice [Feature #8499]
+
+* IO
+
+ * IO.copy_stream tries copy offload with copy_file_range(2) [Feature #13867]
+
+ * New methods:
+
+ * IO#pread [Feature #4532]
+ * IO#pwrite [Feature #4532]
+ * IO#write accepts multiple arguments [Feature #9323]
+
+* IOError
+
+ * IO#close might raise an error with message "stream closed",
+ but it is refined to "stream closed in another thread". The new message
+ is more clear for user. [Bug #13405]
+
+* Integer
+
+ * Integer#round, Integer#floor, Integer#ceil and Integer#truncate always
+ return an Integer. [Bug #13420]
+ * Integer#pow accepts modulo argument for calculating modular
+ exponentiation. [Feature #12508] [Feature #11003]
+
+ * New methods:
+
+ * Integer#allbits?, Integer#anybits?, Integer#nobits? [Feature #12753]
+ * Integer.sqrt [Feature #13219]
+
+* Kernel
+
+ * Kernel#yield_self [Feature #6721]
+ * Kernel#pp [Feature #14123]
+ * Kernel#warn(..., uplevel:n) [Feature #12882]
+
+* Method
+
+ * New methods:
+
+ * Method#=== that invokes Method#call, as same as Proc#=== [Feature #14142]
+
+* Module
+
+ * Module#attr, Module#attr_accessor, Module#attr_reader and Module#attr_writer
+ become public. [Feature #14132]
+ * Module#define_method, Module#alias_method, Module#undef_method and
+ Module#remove_method become public. [Feature #14133]
+
+* Numeric
+
+ * Numeric#step no longer hides errors from coerce method when
+ given a step value which cannot be compared with #> to 0. [Feature #7688]
+ * Numerical comparison operators (<,<=,>=,>) no longer hide exceptions
+ from #coerce method internally. Return nil in #coerce if the coercion is
+ impossible. [Feature #7688]
+
+* Process
+
+ * Precision of Process.times is improved if getrusage(2) exists. [Feature #11952]
+
+ * New method:
+
+ * Process.last_status as an alias of $? [Feature #14043]
+
+* Range
+ * Range#initialize no longer hides exceptions when comparing begin and
+ end with #<=> and raise a "bad value for range" ArgumentError
+ but instead lets the exception from the #<=> call go through. [Feature #7688]
+
+* Regexp
+
+ * Update to Onigmo 6.1.3-669ac9997619954c298da971fcfacccf36909d05.
+
+ * Support absence operator https://github.com/k-takata/Onigmo/issues/82
+
+ * Support new 5 emoji-related Unicode character properties
+
+* RubyVM::InstructionSequence
+
+ * New method:
+
+ * RubyVM::InstructionSequence#each_child
+ * RubyVM::InstructionSequence#trace_points
+
+* String
+
+ * <code>String#-@</code> deduplicates unfrozen strings. Already-frozen
+ strings remain unchanged for compatibility. [Feature #13077]
+ * <code>-"literal"</code> (<code>String#-@</code>) optimized to return the same object
+ (same as <code>"literal".freeze</code> in Ruby 2.1+) [Feature #13295]
+ * String#casecmp and String#casecmp? return nil for non-string arguments
+ instead of raising a TypeError. [Bug #13312]
+ * String#start_with? accepts a regexp [Feature #13712]
+
+ * New methods:
+
+ * String#delete_prefix, String#delete_prefix! [Feature #12694]
+ * String#delete_suffix, String#delete_suffix! [Feature #13665]
+ * String#each_grapheme_cluster and String#grapheme_clusters to
+ enumerate grapheme clusters [Feature #13780]
+ * String#undump to unescape String#dump'ed string [Feature #12275]
+
+* Struct
+
+ * Struct.new takes `keyword_init: true` option to initialize members
+ with keyword arguments. [Feature #11925]
+
+* Regexp/String: Update Unicode version from 9.0.0 to 10.0.0 [Feature #13685]
+
+* Thread
+
+ * Description set by Thread#name= is now visible on Windows 10.
+
+ * New method:
+ * Thread#fetch [Feature #13009]
+
+ * The default of Thread.report_on_exception is now true,
+ showing unhandled exceptions terminating threads on $stderr. [Feature #14143]
+
+* Time
+
+ * Time.at receives 3rd argument which specifies the unit of 2nd argument. [Feature #13919]
+
+* KeyError
+
+ * New methods:
+
+ * KeyError#receiver [Feature #12063]
+ * KeyError#key [Feature #12063]
+
+* FrozenError
+
+ * New exception class. [Feature #13224]
+
+=== Stdlib updates (outstanding ones only)
+
+* BigDecimal
+
+ * Update to BigDecimal 1.3.4
+
+ * The following features are added:
+
+ * BigDecimal::VERSION
+
+ * The following features have been deprecated,
+ and are planned to be removed in the version 1.4.0:
+
+ * BigDecimal.new
+
+ * BigDecimal.ver
+
+ * BigDecimal#clone and #dup now do not make a new instance,
+ but returns the receiver itself.
+
+* Coverage
+
+ * Support branch coverage and method coverage measurement. [Feature #13901]
+ Branch coverage tells you which branches are executed, and which not.
+ Method coverage tells you which methods are invoked, and which not.
+ By running a test suite with this new feature, you can know which branches
+ and methods are executed by a test, and evaluate total coverage of a test
+ suite more strictly.
+
+ You can specify the measuring target by an option to `Coverage.start`:
+
+ Coverage.start(lines: true, branches: true, methods: true)
+
+ After some Ruby files are loaded, you can use `Coverage.result` to get
+ the coverage result:
+
+ Coverage.result
+ #=> { "/path/to/file.rb"=>
+ # { :lines => [1, 2, 0, nil, ...],
+ # :branches =>
+ # { [:if, 0, 2, 1, 6, 4] =>
+ # { [:then, 1, 3, 2, 3, 8] => 0,
+ # [:else, 2, 5, 2, 5, 8] => 2
+ # }
+ # },
+ # :methods => {
+ # [Object, :foo, 1, 0, 7, 3] => 2
+ # }
+ # }
+ # }
+
+ The result type of line coverage is not changed; it is just an array that
+ contains numbers, which means the count that each line was executed,
+ or `nil`s, which means that the line is not relevant.
+
+ The result type of branch coverage is:
+
+ { (jump base) => { (jump target) => (counter) } }
+
+ where jump base and targets have the format
+
+ [type, unique-id, start lineno, start column, end lineno, end column]
+
+ For example, `[:if, 0, 2, 1, 6, 4]` reads an `if` statement that ranges from
+ line 2 and column 1, to line 6 and column 4. `[:then, 1, 3, 2, 3, 8]` reads
+ a `then` clause that ranges from line 3 and column 2, to line 3 and column 8.
+ Note that lineno starts from 1, and that columnno starts from 0. So, the
+ above example shows a branch from the `if` to the `then` was never executed,
+ and a branch from the `if` to the `else` was executed twice.
+
+ The result type of method coverage is:
+
+ { (method key) => (counter) }
+
+ where method key has the format
+
+ [class, method-name, start lineno, start column, end lineno, end column]
+
+ For example, `[Object, :foo, 1, 0, 7, 3]` reads `Object#foo` that ranges from
+ line 1 and column 0, to line 7 and column 3. The above example shows this
+ `Object#foo` was invoked twice.
+
+ Note: To keep compatibility, passing no option to `Coverage.start` will measure
+ only line coverage, and `Coverage.result` will return the old format:
+
+ Coverage.result
+ #=> { "/path/to/file.rb"=> [1, 2, 0, nil, ...] }
+
+* DRb
+
+ * ACL::ACLEntry.new no longer suppresses IPAddr::InvalidPrefixError.
+
+* ERB
+
+ * Add ERB#result_with_hash to render a template with local variables passed
+ with a Hash object. [Feature #8631]
+
+ * Default template file encoding is changed from ASCII-8BIT to UTF-8 in erb
+ command. [Bug #14095]
+
+ * Carriage returns are changed to be trimmed properly if trim_mode is specified
+ and used. Duplicated newlines will be removed on Windows. [Bug #5339] [Bug #11464]
+
+* IPAddr
+
+ * IPAddr no longer accepts invalid address mask. [Bug #13399]
+ * IPAddr#ipv4_compat and IPAddr#ipv4_compat? are marked for deprecation. [Bug #13769]
+
+ * New methods:
+
+ * IPAddr#prefix
+ * IPAddr#loopback?
+ * IPAddr#private? [Feature #11666]
+ * IPAddr#link_local? [Feature #10912]
+
+* IRB
+
+ * Print backtrace and error message in reverse order [Feature #8661] [experimental]
+ * `binding.irb` automatically requires irb and runs [Bug #13099] [experimental]
+ * `binding.irb` on its start shows source around the line where it was called [Feature #14124]
+
+* Matrix
+
+ * New methods:
+
+ * Matrix.combine and Matrix#combine [Feature #10903]
+ * Matrix#hadamard_product and Matrix#entrywise_product
+
+* Net::HTTP
+
+ * Net::HTTP.new supports no_proxy parameter [Feature #11195]
+ * Net::HTTP#min_version and Net::HTTP#max_version [Feature #9450]
+ * Add more HTTP status classes
+ * Net::HTTP::STATUS_CODES is added as HTTP Status Code Repository [Misc #12935]
+ * Net::HTTP#proxy_user and Net::HTTP#proxy_pass reflect http_proxy environment
+ variable if the system's environment variable is multiuser safe. [Bug #12921]
+
+* open-uri
+ * URI.open method defined as an alias to open-uri's Kernel.open.
+ open-uri's Kernel.open will be deprecated in future.
+
+* OpenSSL
+
+ * Updated Ruby/OpenSSL from version 2.0 to 2.1. Changes are noted in
+ "Version 2.1.0" section in ext/openssl/History.md.
+
+* Pathname
+
+ * New method:
+
+ * Pathname#glob [Feature #7360]
+
+* Psych
+
+ * Update to Psych 3.0.2.
+
+ * Convert fallback option to a keyword argument
+ https://github.com/ruby/psych/pull/342
+ * Add :symbolize_names option to Psych.load, Psych.safe_load like JSON.parse
+ https://github.com/ruby/psych/pull/333, https://github.com/ruby/psych/pull/337
+ * Add Psych::Handler#event_location
+ https://github.com/ruby/psych/pull/326
+ * Make frozen string literal = true
+ https://github.com/ruby/psych/pull/320
+ * Preserve time zone offset when deserializing times
+ https://github.com/ruby/psych/pull/316
+ * Remove deprecated method aliases for syck gem
+ https://github.com/ruby/psych/pull/312
+
+* RbConfig
+
+ * RbConfig::LIMITS is added to provide the limits of C types.
+ This is available when rbconfig/sizeof is loaded.
+
+* Ripper
+
+ * Ripper::EXPR_BEG and so on for Ripper#state.
+
+ * New method:
+
+ * Ripper#state to tell the state of scanner. [Feature #13686]
+
+* RDoc
+
+ * Update to RDoc 6.0.1.
+
+ * Replace IRB based lexer with Ripper.
+ * https://github.com/ruby/rdoc/pull/512
+ * This much improves the speed of generating documents.
+ * It also facilitates supporting new syntax in the future.
+ * Support many new syntaxes of Ruby from the past few years.
+ * Use "frozen_string_literal: true".
+ Performance survey: https://gist.github.com/aycabta/abdfaa75ea8a6877eeb734e942e73800
+ * Support did_you_mean.
+
+* Rubygems
+
+ * Update to Rubygems 2.7.3.
+ * http://blog.rubygems.org/2017/11/28/2.7.3-released.html
+ * http://blog.rubygems.org/2017/11/08/2.7.2-released.html
+ * http://blog.rubygems.org/2017/11/03/2.7.1-released.html
+ * http://blog.rubygems.org/2017/11/01/2.7.0-released.html
+ * http://blog.rubygems.org/2017/10/09/2.6.14-released.html
+ * http://blog.rubygems.org/2017/08/27/2.6.13-released.html
+
+* SecureRandom
+
+ * New method:
+
+ * SecureRandom.alphanumeric
+
+* Set
+
+ * New methods:
+
+ * Set#to_s as alias to #inspect [Feature #13676]
+ * Set#=== as alias to #include? [Feature #13801]
+ * Set#reset [Feature #6589]
+
+* StringIO
+
+ * StringIO#write accepts multiple arguments
+
+* StringScanner
+
+ * New methods:
+
+ * StringScanner#size, StringScanner#captures, StringScanner#values_at [Feature #836]
+
+* URI
+
+ * Relative path operations no longer collapse consecutive slashes to a single slash. [Bug #8352]
+
+* WEBrick
+
+ * Add Server Name Indication (SNI) support [Feature #13729]
+ * support Proc objects as body responses [Feature #855]
+ * released as a RubyGem [Feature #13173]
+ * avoid unintended behavior from Kernel#open [Misc #14216]
+
+* Zlib
+
+ * Zlib::GzipWriter#write accepts multiple arguments
+
+=== Compatibility issues (excluding feature bug fixes)
+
+* Socket
+
+ * BasicSocket#read_nonblock and BasicSocket#write_nonblock no
+ longer set the O_NONBLOCK file description flag as side effect
+ (on Linux only) [Feature #13362]
+
+* Random
+
+ * Random.raw_seed renamed to become Random.urandom. It is now
+ applicable to non-seeding purposes due to [Bug #9569].
+
+* Socket
+
+ * Socket::Ifaddr#vhid is added [Feature #13803]
+
+* ConditionVariable, Queue and SizedQueue reimplemented for speed.
+ They no longer subclass Struct. [Feature #13552]
+
+=== Stdlib compatibility issues (excluding feature bug fixes)
+
+* Gemification
+
+ * Promote following standard libraries to default gems.
+ * cmath
+ * csv
+ * date
+ * dbm
+ * etc
+ * fcntl
+ * fiddle
+ * fileutils
+ * gdbm
+ * ipaddr
+ * scanf
+ * sdbm
+ * stringio
+ * strscan
+ * webrick
+ * zlib
+
+* Logger
+
+ * Logger.new("| command") had been working to open a command
+ unintentionally. It was prohibited, and now Logger#initialize
+ treats a String argument only as a filename, as its specification. [Bug #14212]
+
+* Net::HTTP
+
+ * Net::HTTP#start now passes :ENV to p_addr by default. [Bug #13351]
+ To avoid this, pass nil explicitly.
+
+* mathn.rb
+
+ * Removed from stdlib. [Feature #10169]
+
+* Rubygems
+
+ * Removed "ubygems.rb" file from stdlib. It's needless since Ruby 1.9.
+
+=== C API updates
+
+=== Supported platform changes
+
+* Drop support of NaCl platform
+
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=239656#c160
+
+=== Implementation improvements
+
+* (This might not be a "user visible feature change" but) Hash class's
+ hash function is now SipHash13. [Feature #13017]
+
+* SecureRandom now prefers OS-provided sources than OpenSSL. [Bug #9569]
+
+* Mutex rewritten to be smaller and faster [Feature #13517]
+
+* Performance of block passing using block parameters is improved by
+ lazy Proc allocation [Feature #14045]
+
+* Dynamic instrumentation for TracePoint hooks instead of using "trace"
+ instruction to avoid overhead [Feature #14104]
+
+* ERB now generates code from a template twice as fast as Ruby 2.4
+
+=== Miscellaneous changes
+
+* Print backtrace and error message in reverse order if $stderr is unchanged
+ and a tty. [Feature #8661] [experimental]
+
+* Print error message in bold/underlined text if $stderr is unchanged and a
+ tty. [Feature #14140] [experimental]
+
+* configure option --with-ext now mandates its arguments. So for
+ instance if you run ./configure --with-ext=openssl,+ then the
+ openssl library is guaranteed compiled, otherwise the build fails
+ abnormally.
+
+ Note however to always add the ",+" at the end of the argument.
+ Otherwise nothing but openssl are built. [Feature #13302]
diff --git a/doc/contributing.rdoc b/doc/contributing.rdoc
index d0340d4d82..b92dc7427e 100644
--- a/doc/contributing.rdoc
+++ b/doc/contributing.rdoc
@@ -53,10 +53,10 @@ on your ticket.
You can report downstream issues for the following distributions via their bug tracker:
-* {debian}[http://bugs.debian.org/cgi-bin/pkgreport.cgi?src=ruby-defaults]
+* {debian}[https://bugs.debian.org/cgi-bin/pkgreport.cgi?src=ruby-defaults]
* {freebsd}[http://www.freebsd.org/cgi/query-pr-summary.cgi?text=ruby]
* {redhat}[https://bugzilla.redhat.com/buglist.cgi?bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&bug_status=MODIFIED]
-* {macports}[http://trac.macports.org/query?status=assigned&status=new&status=reopened&port=~ruby]
+* {macports}[https://trac.macports.org/query?status=assigned&status=new&status=reopened&port=~ruby]
* etc (add your distribution bug tracker here)
=== Platform Maintainers
@@ -66,14 +66,10 @@ maintainer for a specific platform.
The current active platform maintainers are as follows:
-[mswin32, mswin64 (Microsoft Windows)]
+[mswin64 (Microsoft Windows)]
NAKAMURA Usaku (usa)
[mingw32 (Minimalist GNU for Windows)]
Nobuyoshi Nakada (nobu)
-[IA-64 (Debian GNU/Linux)]
- TAKANO Mitsuhiro (takano32)
-[Symbian OS]
- Alexander Zavorine (azov)
[AIX]
Yutaka Kanemoto (kanemoto)
[FreeBSD]
@@ -81,8 +77,8 @@ The current active platform maintainers are as follows:
[Solaris]
Naohisa Goto (ngoto)
[RHEL, CentOS]
- KOSAKI Motohiro kosaki
-[Mac OS X]
+ KOSAKI Motohiro (kosaki)
+[macOS]
Kenta Murata (mrkn)
[cygwin, bcc32, djgpp, wince, ...]
none. (Maintainer WANTED)
@@ -256,7 +252,7 @@ the {ruby-core documentation on
ruby-lang.org}[https://www.ruby-lang.org/en/community/ruby-core/].
This guide will use git for contributing. The {git
-homepage}[http://git-scm.com/] has installation instructions with links to
+homepage}[https://git-scm.com/] has installation instructions with links to
documentation for learning more about git. There is a mirror of the subversion
repository on {github}[https://github.com/ruby/ruby].
@@ -287,7 +283,7 @@ Now let's build CRuby:
* Checkout the CRuby source code:
- git clone git://github.com/ruby/ruby.git ruby-trunk
+ git clone https://github.com/ruby/ruby.git ruby-trunk
* Generate the configuration files and build:
@@ -319,7 +315,7 @@ For older versions of Ruby you will need to run the build setup again after
checking out the associated branch in git, for example if you wanted to
checkout 1.9.3:
- git clone git://github.com/ruby/ruby.git --branch ruby_1_9_3
+ git clone https://github.com/ruby/ruby.git --branch ruby_1_9_3
Once you checked out the source code, you can update the local copy by:
@@ -332,11 +328,11 @@ Or, update, build, install and check, by just:
== Contributing Documentation
If you're interested in contributing documentation directly to CRuby there is
-a wealth of information available at
-{documenting-ruby.org}[http://documenting-ruby.org/].
+some information available at
+{Contributing}[https://github.com/ruby/ruby#contributing].
There is also the {Ruby Reference
-Manual}[https://bugs.ruby-lang.org/projects/rurema] in Japanese.
+Manual}[https://github.com/rurema/doctree/wiki] in Japanese.
== Contributing A Patch
@@ -360,7 +356,7 @@ To improve the chance your patch will be accepted please follow these simple rul
First thing you should do is check out the code if you haven't already:
- git clone git://github.com/ruby/ruby.git ruby-trunk
+ git clone https://github.com/ruby/ruby.git ruby-trunk
Now create a dedicated branch:
@@ -376,7 +372,8 @@ trunk, or edge Ruby.
Here are some general rules to follow when writing Ruby and C code for CRuby:
-* Indent 4 spaces for C with tabs for eight-space indentation (emacs default)
+* Indent 4 spaces for C without tabs (old codes might use tabs for eight-space indentation,
+ but newer codes recommend to use spaces only)
* Indent 2 space tabs for Ruby
* Do not use TABs in ruby codes
* ANSI C style for 1.9+ for function declarations
@@ -456,7 +453,7 @@ Since git is a distributed system, you are welcome to host your git repository
on any {publicly accessible hosting
site}[https://git.wiki.kernel.org/index.php/GitHosting], including {hosting your
own}[https://www.kernel.org/pub/software/scm/git/docs/user-manual.html#public-repositories]
-You may use the {'git format-patch'}[http://git-scm.com/docs/git-format-patch]
+You may use the {'git format-patch'}[https://git-scm.com/docs/git-format-patch]
command to generate patch files to upload to redmine. You may also use
-the {'git request-pull'}[http://git-scm.com/docs/git-request-pull] command for
+the {'git request-pull'}[https://git-scm.com/docs/git-request-pull] command for
formatting pull request messages to redmine.
diff --git a/doc/contributors.rdoc b/doc/contributors.rdoc
index ced4eb1cd0..7c3722032b 100644
--- a/doc/contributors.rdoc
+++ b/doc/contributors.rdoc
@@ -37,6 +37,9 @@ arton
* He is the distributor of ActiveScriptRuby and experimental 1.9.0-x installers for win32.
* Wrote patches for win32ole, gc.c, tmpdir.rb
+Sergey Avseyev
+* Added IO#pread and IO#pwrite.
+
== B
Daniel Berger
@@ -57,6 +60,9 @@ Oliver M. Bolzer
Alexey Borzenkov
* a patch for mkmf.rb
+Evan Brodie
+* a patch for documentation of Float#round
+
Richard Brown
* a patch for configure.in
@@ -82,7 +88,7 @@ Christoph
* patches for set.rb
Sean Chittenden
-* pathces for net/http, cgi
+* patches for net/http, cgi
William D. Clinger
* ruby_strtod is based on his paper.
@@ -103,7 +109,7 @@ Martin Duerst (duerst)
* M17N
Paul Duncan
-* pathces for rdoc
+* patches for rdoc
Alexander Dymo
* a patch for lib/benchmark.rb
@@ -597,6 +603,15 @@ Richard M. Stallman
Robin Stocker
* documentation
+Joshua Stowers
+* a patch for array.c
+
+Marcus Stollsteimer (stomar)
+* committer
+* a maintainer of www.ruby-lang.org
+* patches for cgi (HTML5 tag maker), numeric.c, bigdecimal, ostruct.rb, prime.rb, and others
+* documentation
+
Adam Strzelecki
* a patch for compile.c
diff --git a/doc/extension.ja.rdoc b/doc/extension.ja.rdoc
index 2117fe20aa..58a1c852f2 100644
--- a/doc/extension.ja.rdoc
+++ b/doc/extension.ja.rdoc
@@ -1,8 +1,10 @@
# extension.ja.rdoc - -*- RDoc -*- created at: Mon Aug 7 16:45:54 JST 1995
+= Rubyã®æ‹¡å¼µãƒ©ã‚¤ãƒ–ラリã®ä½œã‚Šæ–¹
+
Rubyã®æ‹¡å¼µãƒ©ã‚¤ãƒ–ラリã®ä½œã‚Šæ–¹ã‚’説明ã—ã¾ã™ï¼Ž
-= 基礎知識
+== 基礎知識
Cã®å¤‰æ•°ã«ã¯åž‹ãŒã‚り,データã«ã¯åž‹ãŒã‚りã¾ã›ã‚“.ã§ã™ã‹ã‚‰ï¼ŒãŸ
ã¨ãˆã°ãƒã‚¤ãƒ³ã‚¿ã‚’intã®å¤‰æ•°ã«ä»£å…¥ã™ã‚‹ã¨ï¼Œãã®å€¤ã¯æ•´æ•°ã¨ã—ã¦å–
@@ -23,7 +25,7 @@ VALUEã‹ã‚‰Cã«ã¨ã£ã¦æ„味ã®ã‚るデータをå–り出ã™ãŸã‚ã«ã¯
ã®ä¸¡æ–¹ãŒå¿…è¦ã§ã™ï¼Ž(1)を忘れるã¨é–“é•ã£ãŸãƒ‡ãƒ¼ã‚¿ã®å¤‰æ›ãŒè¡Œã‚れ
ã¦ï¼Œæœ€æ‚ªãƒ—ログラムãŒcore dumpã—ã¾ã™ï¼Ž
-== データタイプ
+=== データタイプ
Rubyã«ã¯ãƒ¦ãƒ¼ã‚¶ãŒä½¿ã†å¯èƒ½æ€§ã®ã‚る以下ã®ã‚¿ã‚¤ãƒ—ãŒã‚りã¾ã™ï¼Ž
@@ -57,7 +59,7 @@ T_SYMBOL :: シンボル
ã»ã¨ã‚“ã©ã®ã‚¿ã‚¤ãƒ—ã¯Cã®æ§‹é€ ä½“ã§å®Ÿè£…ã•れã¦ã„ã¾ã™ï¼Ž
-== VALUEã®ãƒ‡ãƒ¼ã‚¿ã‚¿ã‚¤ãƒ—ã‚’ãƒã‚§ãƒƒã‚¯ã™ã‚‹
+=== VALUEã®ãƒ‡ãƒ¼ã‚¿ã‚¿ã‚¤ãƒ—ã‚’ãƒã‚§ãƒƒã‚¯ã™ã‚‹
ruby.hã§ã¯TYPE()ã¨ã„ã†ãƒžã‚¯ãƒ­ãŒå®šç¾©ã•れã¦ã„ã¦ï¼ŒVALUEã®ãƒ‡ãƒ¼ã‚¿
タイプを知るã“ã¨ãŒå‡ºæ¥ã¾ã™ï¼ŽTYPE()マクロã¯ä¸Šã§ç´¹ä»‹ã—ãŸT_XXXX
@@ -94,7 +96,7 @@ FIXNUMã¨NILã«é–¢ã—ã¦ã¯ã‚ˆã‚Šé«˜é€Ÿãªåˆ¤åˆ¥ãƒžã‚¯ãƒ­ãŒç”¨æ„ã•れã¦ã„ã
FIXNUM_P(obj)
NIL_P(obj)
-== VALUEã‚’Cã®ãƒ‡ãƒ¼ã‚¿ã«å¤‰æ›ã™ã‚‹
+=== VALUEã‚’Cã®ãƒ‡ãƒ¼ã‚¿ã«å¤‰æ›ã™ã‚‹
データタイプãŒT_NIL,T_FALSE,T_TRUEã§ã‚る時,データã¯ãれãž
れnil,false,trueã§ã™ï¼Žã“ã®ãƒ‡ãƒ¼ã‚¿ã‚¿ã‚¤ãƒ—ã®ã‚ªãƒ–ジェクトã¯ã²ã¨
@@ -155,7 +157,7 @@ Rubyã®æ§‹é€ ä½“を直接アクセスã™ã‚‹æ™‚ã«æ°—ã‚’ã¤ã‘ãªã‘れã°ãªã‚‰ã
ãªã„ã“ã¨ã§ã™ï¼Žç›´æŽ¥å¤‰æ›´ã—ãŸå ´åˆï¼Œã‚ªãƒ–ジェクトã®å†…å®¹ã®æ•´åˆæ€§ãŒ
ã¨ã‚Œãªããªã£ã¦ï¼Œæ€ã‚ã¬ãƒã‚°ã®åŽŸå› ã«ãªã‚Šã¾ã™ï¼Ž
-== Cã®ãƒ‡ãƒ¼ã‚¿ã‚’VALUEã«å¤‰æ›ã™ã‚‹
+=== Cã®ãƒ‡ãƒ¼ã‚¿ã‚’VALUEã«å¤‰æ›ã™ã‚‹
VALUEã®å®Ÿéš›ã®æ§‹é€ ã¯
@@ -182,13 +184,13 @@ FIXNUMã«é–¢ã—ã¦ã¯å¤‰æ›ãƒžã‚¯ãƒ­ã‚’経由ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ï¼ŽCã®æ
ã¦ä½¿ã„分ã‘ã¦ãã ã•ã„.
INT2FIX() :: ã‚‚ã¨ã®æ•´æ•°ãŒ31bitã¾ãŸã¯63bit以内ã«åŽã¾ã‚‹è‡ªä¿¡
- ãŒã‚る時
+ ãŒã‚る時
INT2NUM() :: ä»»æ„ã®æ•´æ•°ã‹ã‚‰VALUEã¸
INT2NUM()ã¯æ•´æ•°ãŒFIXNUMã®ç¯„囲ã«åŽã¾ã‚‰ãªã„å ´åˆï¼ŒBignumã«å¤‰æ›
ã—ã¦ãれã¾ã™(ãŒï¼Œå°‘ã—é…ã„).
-== Rubyã®ãƒ‡ãƒ¼ã‚¿ã‚’æ“作ã™ã‚‹
+=== Rubyã®ãƒ‡ãƒ¼ã‚¿ã‚’æ“作ã™ã‚‹
先程も述ã¹ãŸé€šã‚Šï¼ŒRubyã®æ§‹é€ ä½“をアクセスã™ã‚‹æ™‚ã«å†…å®¹ã®æ›´æ–°ã‚’
行ã†ã“ã¨ã¯å‹§ã‚られã¾ã›ã‚“.ã§ï¼ŒRubyã®ãƒ‡ãƒ¼ã‚¿ã‚’æ“作ã™ã‚‹æ™‚ã«ã¯
@@ -197,7 +199,7 @@ RubyãŒç”¨æ„ã—ã¦ã„る関数を用ã„ã¦ãã ã•ã„.
ã“ã“ã§ã¯ã‚‚ã£ã¨ã‚‚使ã‚れるã§ã‚ã‚ã†æ–‡å­—列ã¨é…列ã®ç”Ÿæˆ/æ“作を行
ã†é–¢æ•°ã‚’ã‚ã’ã¾ã™(全部ã§ã¯ãªã„ã§ã™).
-=== 文字列ã«å¯¾ã™ã‚‹é–¢æ•°
+==== 文字列ã«å¯¾ã™ã‚‹é–¢æ•°
rb_str_new(const char *ptr, long len) ::
@@ -224,6 +226,10 @@ rb_tainted_str_new_cstr(const char *ptr) ::
Cã®æ–‡å­—列ã‹ã‚‰æ±šæŸ“マークãŒä»˜åŠ ã•れãŸRubyã®æ–‡å­—列を生æˆã™ã‚‹ï¼Ž
+rb_str_append(VALUE str1, VALUE str2) ::
+
+ Rubyã®æ–‡å­—列str1ã«Rubyã®æ–‡å­—列str2を追加ã™ã‚‹ï¼Ž
+
rb_sprintf(const char *format, ...) ::
rb_vsprintf(const char *format, va_list ap) ::
@@ -249,15 +255,15 @@ rb_str_vcatf(VALUE str, const char* format, va_list ap) ::
Cã®æ–‡å­—列formatã¨ç¶šã引数をprintf(3)ã®ãƒ•ォーマットã«ã—ãŸãŒã£ã¦
æ•´å½¢ã—,Rubyã®æ–‡å­—列strã«è¿½åŠ ã™ã‚‹ï¼Žã“ã®é–¢æ•°ã®æ©Ÿèƒ½ã¯ï¼Œãれãžã‚Œ
- rb_str_cat2(str, rb_sprintf(format, ...)) ã‚„
- rb_str_cat2(str, rb_vsprintf(format, ap)) ã¨åŒç­‰ã§ã‚る.
+ rb_str_append(str, rb_sprintf(format, ...)) ã‚„
+ rb_str_append(str, rb_vsprintf(format, ap)) ã¨åŒç­‰ã§ã‚る.
rb_enc_str_new(const char *ptr, long len, rb_encoding *enc) ::
rb_enc_str_new_cstr(const char *ptr, rb_encoding *enc) ::
指定ã•れãŸã‚¨ãƒ³ã‚³ãƒ¼ãƒ‡ã‚£ãƒ³ã‚°ã§Rubyã®æ–‡å­—列を生æˆã™ã‚‹.
-rb_enc_str_new_literal(const char *ptr) ::
+rb_enc_str_new_literal(const char *ptr, rb_encoding *enc) ::
Cã®ãƒªãƒ†ãƒ©ãƒ«æ–‡å­—列ã‹ã‚‰æŒ‡å®šã•れãŸã‚¨ãƒ³ã‚³ãƒ¼ãƒ‡ã‚£ãƒ³ã‚°ã§Rubyã®æ–‡å­—列を生æˆã™ã‚‹ï¼Ž
@@ -295,7 +301,7 @@ rb_str_set_len(VALUE str, long len) ::
lenãƒã‚¤ãƒˆã¾ã§ã®å†…容ã¯ä¿å­˜ã•れる.lenã¯strã®å®¹é‡ã‚’è¶Šãˆã¦ã„
ã¦ã¯ãªã‚‰ãªã„.
-=== é…列ã«å¯¾ã™ã‚‹é–¢æ•°
+==== é…列ã«å¯¾ã™ã‚‹é–¢æ•°
rb_ary_new() ::
@@ -326,7 +332,7 @@ rb_ary_to_ary(VALUE obj) ::
引数aryã«é…列を渡ã•ãªã‘れã°ãªã‚‰ãªã„. ã•ã‚‚ãªã„ã¨
コアをåã.
-rb_ary_aref(argc, VALUE *argv, VALUE ary) ::
+rb_ary_aref(int argc, const VALUE *argv, VALUE ary) ::
Array#[]ã¨åŒç­‰.
@@ -353,14 +359,14 @@ rb_ary_cat(VALUE ary, const VALUE *ptr, long len) ::
é…列aryã«ptrã‹ã‚‰len個ã®ã‚ªãƒ–ジェクトを追加ã™ã‚‹ï¼Ž
-= Rubyã®æ©Ÿèƒ½ã‚’使ã†
+== Rubyã®æ©Ÿèƒ½ã‚’使ã†
原ç†çš„ã«Rubyã§æ›¸ã‘ã‚‹ã“ã¨ã¯Cã§ã‚‚書ã‘ã¾ã™ï¼ŽRubyãã®ã‚‚ã®ãŒCã§è¨˜
è¿°ã•れã¦ã„ã‚‹ã‚“ã§ã™ã‹ã‚‰ï¼Œå½“ç„¶ã¨ã„ãˆã°å½“ç„¶ãªã‚“ã§ã™ã‘ã©ï¼Žã“ã“ã§
ã¯Rubyã®æ‹¡å¼µã«ä½¿ã†ã“ã¨ãŒå¤šã„ã ã‚ã†ã¨äºˆæ¸¬ã•れる機能を中心ã«ç´¹
介ã—ã¾ã™ï¼Ž
-== Rubyã«æ©Ÿèƒ½ã‚’追加ã™ã‚‹
+=== Rubyã«æ©Ÿèƒ½ã‚’追加ã™ã‚‹
Rubyã§æä¾›ã•れã¦ã„る関数を使ãˆã°Rubyã‚¤ãƒ³ã‚¿ãƒ—ãƒªã‚¿ã«æ–°ã—ã„æ©Ÿèƒ½
を追加ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ï¼ŽRubyã§ã¯ä»¥ä¸‹ã®æ©Ÿèƒ½ã‚’追加ã™ã‚‹é–¢æ•°ãŒ
@@ -372,7 +378,7 @@ Rubyã§æä¾›ã•れã¦ã„る関数を使ãˆã°Rubyã‚¤ãƒ³ã‚¿ãƒ—ãƒªã‚¿ã«æ–°ã—ã„
ã§ã¯é †ã«ç´¹ä»‹ã—ã¾ã™ï¼Ž
-=== クラス/モジュール定義
+==== クラス/モジュール定義
クラスやモジュールを定義ã™ã‚‹ãŸã‚ã«ã¯ï¼Œä»¥ä¸‹ã®é–¢æ•°ã‚’使ã„ã¾ã™ï¼Ž
@@ -389,15 +395,15 @@ Rubyã§æä¾›ã•れã¦ã„る関数を使ãˆã°Rubyã‚¤ãƒ³ã‚¿ãƒ—ãƒªã‚¿ã«æ–°ã—ã„
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
VALUE rb_define_module_under(VALUE outer, const char *name)
-=== メソッド/特異メソッド定義
+==== メソッド/特異メソッド定義
メソッドや特異メソッドを定義ã™ã‚‹ã«ã¯ä»¥ä¸‹ã®é–¢æ•°ã‚’使ã„ã¾ã™ï¼Ž
void rb_define_method(VALUE klass, const char *name,
- VALUE (*func)(), int argc)
+ VALUE (*func)(ANYARGS), int argc)
void rb_define_singleton_method(VALUE object, const char *name,
- VALUE (*func)(), int argc)
+ VALUE (*func)(ANYARGS), int argc)
念ã®ãŸã‚説明ã™ã‚‹ã¨ã€Œç‰¹ç•°ãƒ¡ã‚½ãƒƒãƒ‰ã€ã¨ã¯ï¼Œãã®ç‰¹å®šã®ã‚ªãƒ–ジェク
トã«å¯¾ã—ã¦ã ã‘有効ãªãƒ¡ã‚½ãƒƒãƒ‰ã§ã™ï¼ŽRubyã§ã¯ã‚ˆãSmalltalkã«ãŠ
@@ -423,9 +429,9 @@ argcãŒ-1ã®æ™‚ã¯å¼•æ•°ã‚’é…列ã«å…¥ã‚Œã¦æ¸¡ã•れã¾ã™ï¼ŽargcãŒ-2ã®æ™‚ã
private/protectedãªãƒ¡ã‚½ãƒƒãƒ‰ã‚’定義ã™ã‚‹ãµãŸã¤ã®é–¢æ•°ãŒã‚りã¾ã™.
void rb_define_private_method(VALUE klass, const char *name,
- VALUE (*func)(), int argc)
+ VALUE (*func)(ANYARGS), int argc)
void rb_define_protected_method(VALUE klass, const char *name,
- VALUE (*func)(), int argc)
+ VALUE (*func)(ANYARGS), int argc)
privateメソッドã¨ã¯é–¢æ•°å½¢å¼ã§ã—ã‹å‘¼ã³å‡ºã™ã“ã¨ã®å‡ºæ¥ãªã„メソッ
ドã§ã™ï¼Ž
@@ -446,12 +452,12 @@ privateメソッドã§ã‚‚ã‚ã‚‹ã‚‚ã®ã§ã™ï¼Žä¾‹ã‚’ã‚ã’ã‚‹ã¨Mathモジュー
通りã§ã™ï¼Ž
void rb_define_module_function(VALUE module, const char *name,
- VALUE (*func)(), int argc)
+ VALUE (*func)(ANYARGS), int argc)
関数的メソッド(Kernelモジュールã®private method)を定義ã™ã‚‹ãŸ
ã‚ã®é–¢æ•°ã¯ä»¥ä¸‹ã®é€šã‚Šã§ã™ï¼Ž
- void rb_define_global_function(const char *name, VALUE (*func)(), int argc)
+ void rb_define_global_function(const char *name, VALUE (*func)(ANYARGS), int argc)
メソッドã®åˆ¥åを定義ã™ã‚‹ãŸã‚ã®é–¢æ•°ã¯ä»¥ä¸‹ã®é€šã‚Šã§ã™ï¼Ž
@@ -483,7 +489,7 @@ funcã¯ã‚¯ãƒ©ã‚¹ã‚’引数ã¨ã—ã¦å—ã‘å–ã£ã¦ï¼Œæ–°ã—ã割り当ã¦ã‚‰ã‚Œã
VALUE rb_current_receiver(void)
-=== 定数定義
+==== 定数定義
拡張ライブラリãŒå¿…è¦ãªå®šæ•°ã¯ã‚らã‹ã˜ã‚定義ã—ã¦ãŠã„ãŸæ–¹ãŒè‰¯ã„
ã§ã—ょã†ï¼Žå®šæ•°ã‚’定義ã™ã‚‹é–¢æ•°ã¯äºŒã¤ã‚りã¾ã™ï¼Ž
@@ -494,7 +500,7 @@ funcã¯ã‚¯ãƒ©ã‚¹ã‚’引数ã¨ã—ã¦å—ã‘å–ã£ã¦ï¼Œæ–°ã—ã割り当ã¦ã‚‰ã‚Œã
å‰è€…ã¯ç‰¹å®šã®ã‚¯ãƒ©ã‚¹/モジュールã«å±žã™ã‚‹å®šæ•°ã‚’定義ã™ã‚‹ã‚‚ã®ï¼Œå¾Œ
者ã¯ã‚°ãƒ­ãƒ¼ãƒãƒ«ãªå®šæ•°ã‚’定義ã™ã‚‹ã‚‚ã®ã§ã™ï¼Ž
-== Rubyã®æ©Ÿèƒ½ã‚’Cã‹ã‚‰å‘¼ã³å‡ºã™
+=== Rubyã®æ©Ÿèƒ½ã‚’Cã‹ã‚‰å‘¼ã³å‡ºã™
æ—¢ã«ã€Ž1.5 Rubyã®ãƒ‡ãƒ¼ã‚¿ã‚’æ“作ã™ã‚‹ã€ã§ä¸€éƒ¨ç´¹ä»‹ã—ãŸã‚ˆã†ãªé–¢æ•°ã‚’
使ãˆã°ï¼ŒRubyã®æ©Ÿèƒ½ã‚’実ç¾ã—ã¦ã„る関数を直接呼ã³å‡ºã™ã“ã¨ãŒå‡ºæ¥
@@ -505,7 +511,7 @@ funcã¯ã‚¯ãƒ©ã‚¹ã‚’引数ã¨ã—ã¦å—ã‘å–ã£ã¦ï¼Œæ–°ã—ã割り当ã¦ã‚‰ã‚Œã
ãれ以外ã«ã‚‚Rubyã®æ©Ÿèƒ½ã‚’呼ã³å‡ºã™æ–¹æ³•ã¯ã„ãã¤ã‹ã‚りã¾ã™ï¼Ž
-=== Rubyã®ãƒ—ログラムをevalã™ã‚‹
+==== Rubyã®ãƒ—ログラムをevalã™ã‚‹
Cã‹ã‚‰Rubyã®æ©Ÿèƒ½ã‚’呼ã³å‡ºã™ã‚‚ã£ã¨ã‚‚ç°¡å˜ãªæ–¹æ³•ã¨ã—ã¦ï¼Œæ–‡å­—列ã§
与ãˆã‚‰ã‚ŒãŸRubyã®ãƒ—ログラムを評価ã™ã‚‹ä»¥ä¸‹ã®é–¢æ•°ãŒã‚りã¾ã™ï¼Ž
@@ -523,7 +529,7 @@ Cã‹ã‚‰Rubyã®æ©Ÿèƒ½ã‚’呼ã³å‡ºã™ã‚‚ã£ã¨ã‚‚ç°¡å˜ãªæ–¹æ³•ã¨ã—ã¦ï¼Œæ–‡å­—
ã“ã®é–¢æ•°ã¯ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã™ã‚‹ã¨nilã‚’è¿”ã—ã¾ã™ï¼Žãã—ã¦ï¼ŒæˆåŠŸæ™‚ã«ã¯
*stateã¯ã‚¼ãƒ­ã«ï¼Œã•ã‚‚ãªãã°éžã‚¼ãƒ­ã«ãªã‚Šã¾ã™ï¼Ž
-=== IDã¾ãŸã¯ã‚·ãƒ³ãƒœãƒ«
+==== IDã¾ãŸã¯ã‚·ãƒ³ãƒœãƒ«
Cã‹ã‚‰æ–‡å­—列を経由ã›ãšã«Rubyã®ãƒ¡ã‚½ãƒƒãƒ‰ã‚’呼ã³å‡ºã™ã“ã¨ã‚‚ã§ãã¾
ã™ï¼Žãã®å‰ã«ï¼ŒRubyインタプリタ内ã§ãƒ¡ã‚½ãƒƒãƒ‰ã‚„変数åを指定ã™ã‚‹
@@ -566,7 +572,7 @@ Rubyã‹ã‚‰å¼•æ•°ã¨ã—ã¦ä¸Žãˆã‚‰ã‚ŒãŸã‚·ãƒ³ãƒœãƒ«(ã¾ãŸã¯æ–‡å­—列)ã‚’ã‚·ãƒ
ã“れらã®é–¢æ•°ã¯ï¼ŒIDã®ä»£ã‚りã«ã‚·ãƒ³ãƒœãƒ«ã‚’è¿”ã™ã“ã¨ã‚’除ã‘ã°ä¸Šè¨˜ã®
関数ã¨åŒã˜ã§ã™ï¼Ž
-=== Cã‹ã‚‰Rubyã®ãƒ¡ã‚½ãƒƒãƒ‰ã‚’呼ã³å‡ºã™
+==== Cã‹ã‚‰Rubyã®ãƒ¡ã‚½ãƒƒãƒ‰ã‚’呼ã³å‡ºã™
Cã‹ã‚‰æ–‡å­—列を経由ã›ãšã«Rubyã®ãƒ¡ã‚½ãƒƒãƒ‰ã‚’呼ã³å‡ºã™ãŸã‚ã«ã¯ä»¥ä¸‹
ã®é–¢æ•°ã‚’使ã„ã¾ã™ï¼Ž
@@ -582,7 +588,7 @@ Cã‹ã‚‰æ–‡å­—列を経由ã›ãšã«Rubyã®ãƒ¡ã‚½ãƒƒãƒ‰ã‚’呼ã³å‡ºã™ãŸã‚ã«ã¯
applyã«ã¯å¼•æ•°ã¨ã—ã¦Rubyã®é…列を与ãˆã¾ã™ï¼Ž
-=== 変数/定数をå‚ç…§/æ›´æ–°ã™ã‚‹
+==== 変数/定数をå‚ç…§/æ›´æ–°ã™ã‚‹
Cã‹ã‚‰é–¢æ•°ã‚’使ã£ã¦å‚照・更新ã§ãã‚‹ã®ã¯ï¼Œå®šæ•°ï¼Œã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹å¤‰
æ•°ã§ã™ï¼Žå¤§åŸŸå¤‰æ•°ã¯ä¸€éƒ¨ã®ã‚‚ã®ã¯Cã®å¤§åŸŸå¤‰æ•°ã¨ã—ã¦ã‚¢ã‚¯ã‚»ã‚¹ã§ã
@@ -603,11 +609,11 @@ idã¯rb_intern()ã§å¾—られるもã®ã‚’使ã£ã¦ãã ã•ã„.
定数を新ã—ã定義ã™ã‚‹ãŸã‚ã«ã¯ã€Ž2.1.3 定数定義ã€ã§ç´¹ä»‹ã•
れã¦ã„る関数を使ã£ã¦ãã ã•ã„.
-= Rubyã¨Cã¨ã®æƒ…報共有
+== Rubyã¨Cã¨ã®æƒ…報共有
C言語ã¨Rubyã®é–“ã§æƒ…報を共有ã™ã‚‹æ–¹æ³•ã«ã¤ã„ã¦è§£èª¬ã—ã¾ã™ï¼Ž
-== Cã‹ã‚‰å‚ç…§ã§ãã‚‹Rubyã®å®šæ•°
+=== Cã‹ã‚‰å‚ç…§ã§ãã‚‹Rubyã®å®šæ•°
以下ã®Rubyã®å®šæ•°ã¯Cã®ãƒ¬ãƒ™ãƒ«ã‹ã‚‰å‚ç…§ã§ãã¾ã™ï¼Ž
@@ -620,7 +626,7 @@ Qnil ::
C言語ã‹ã‚‰è¦‹ãŸã€Œnilã€ï¼Ž
-== Cã¨Rubyã§å…±æœ‰ã•れる大域変数
+=== Cã¨Rubyã§å…±æœ‰ã•れる大域変数
Cã¨Rubyã§å¤§åŸŸå¤‰æ•°ã‚’使ã£ã¦æƒ…報を共有ã§ãã¾ã™ï¼Žå…±æœ‰ã§ãる大域
変数ã«ã¯ã„ãã¤ã‹ã®ç¨®é¡žãŒã‚りã¾ã™ï¼Žãã®ãªã‹ã§ã‚‚ã£ã¨ã‚‚良ã使ã‚
@@ -642,7 +648,7 @@ Cã¨Rubyã§å¤§åŸŸå¤‰æ•°ã‚’使ã£ã¦æƒ…報を共有ã§ãã¾ã™ï¼Žå…±æœ‰ã§ãã‚‹
値ã®å‚照や設定ã¯hookã§è¡Œã†å¿…è¦ãŒã‚りã¾ã™ï¼Ž
void rb_define_hooked_variable(const char *name, VALUE *var,
- VALUE (*getter)(), void (*setter)())
+ VALUE (*getter)(), void (*setter)())
ã“ã®é–¢æ•°ã¯Cã®é–¢æ•°ã«ã‚ˆã£ã¦hookã®ã¤ã‘られãŸå¤§åŸŸå¤‰æ•°ã‚’定義ã—ã¾
ã™ï¼Žå¤‰æ•°ãŒå‚ç…§ã•ã‚ŒãŸæ™‚ã«ã¯é–¢æ•°getterãŒï¼Œå¤‰æ•°ã«å€¤ãŒã‚»ãƒƒãƒˆã•れ
@@ -662,7 +668,7 @@ getterã¨setterã®ä»•æ§˜ã¯æ¬¡ã®é€šã‚Šã§ã™ï¼Ž
ã•れã¾ã™.
void rb_define_virtual_variable(const char *name,
- VALUE (*getter)(), void (*setter)())
+ VALUE (*getter)(), void (*setter)())
ã“ã®é–¢æ•°ã«ã‚ˆã£ã¦å®šç¾©ã•れãŸRubyã®å¤§åŸŸå¤‰æ•°ãŒå‚ç…§ã•ã‚ŒãŸæ™‚ã«ã¯
getterãŒï¼Œå¤‰æ•°ã«å€¤ãŒã‚»ãƒƒãƒˆã•ã‚ŒãŸæ™‚ã«ã¯setterãŒå‘¼ã°ã‚Œã¾ã™ï¼Ž
@@ -672,7 +678,7 @@ getterã¨setterã®ä»•様ã¯ä»¥ä¸‹ã®é€šã‚Šã§ã™ï¼Ž
(*getter)(ID id);
(*setter)(VALUE val, ID id);
-== Cã®ãƒ‡ãƒ¼ã‚¿ã‚’Rubyオブジェクトã«ã™ã‚‹
+=== Cã®ãƒ‡ãƒ¼ã‚¿ã‚’Rubyオブジェクトã«ã™ã‚‹
Cã®ä¸–界ã§å®šç¾©ã•れãŸãƒ‡ãƒ¼ã‚¿(構造体)ã‚’Rubyã®ã‚ªãƒ–ジェクトã¨ã—ã¦
å–り扱ã„ãŸã„å ´åˆãŒã‚りãˆã¾ã™ï¼Žã“ã®ã‚ˆã†ãªå ´åˆã¯TypedData_XXX
@@ -685,7 +691,7 @@ Cã®ä¸–界ã§å®šç¾©ã•れãŸãƒ‡ãƒ¼ã‚¿(構造体)ã‚’Rubyã®ã‚ªãƒ–ジェクトã¨ã
ãŒã‚りã¾ã™ï¼Ž
++
-=== 構造体ã‹ã‚‰ã‚ªãƒ–ジェクトã¸
+==== 構造体ã‹ã‚‰ã‚ªãƒ–ジェクトã¸
構造体ã¸ã®ãƒã‚¤ãƒ³ã‚¿svalã‚’Rubyオブジェクトã«å¤‰æ›ã™ã‚‹ã«ã¯æ¬¡ã®ãƒž
クロを使ã„ã¾ã™ã€‚
@@ -705,6 +711,8 @@ Dataã‹ã‚‰æ´¾ç”Ÿã—ãªã„å ´åˆã«ã¯, å¿…ãšrb_undef_alloc_func(klass)
rb_data_type_tã¯æ¬¡ã®ã‚ˆã†ã«å®šç¾©ã•れã¦ã„ã¾ã™ï¼Ž
+ typedef struct rb_data_type_struct rb_data_type_t;
+
struct rb_data_type_struct {
const char *wrap_struct_name;
struct {
@@ -775,8 +783,8 @@ RUBY_TYPED_WB_PROTECTED ::
メソッドã®å®Ÿè£…ã«é©åˆ‡ã«ãƒ©ã‚¤ãƒˆãƒãƒªã‚¢ã‚’挿入ã™ã‚‹è²¬ä»»ãŒã‚りã¾ã™ï¼Ž
ã•ã‚‚ãªãã°Rubyã¯å®Ÿè¡Œæ™‚ã«ã‚¯ãƒ©ãƒƒã‚·ãƒ¥ã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ï¼Ž
- ライトãƒãƒªã‚¢ã«ã¤ã„ã¦ã¯doc/extension.rdocã®Appendix D
- "Generational GC"ã‚‚å‚ç…§ã—ã¦ãã ã•ã„.
+ ライトãƒãƒªã‚¢ã«ã¤ã„ã¦ã¯doc/extension.ja.rdocã®Appendix D
+ "世代別GC"ã‚‚å‚ç…§ã—ã¦ãã ã•ã„.
Cã®æ§‹é€ ä½“ã®å‰²å½“ã¨å¯¾å¿œã™ã‚‹ã‚ªãƒ–ジェクトã®ç”Ÿæˆã‚’åŒæ™‚ã«è¡Œã†ãƒžã‚¯
@@ -793,7 +801,7 @@ klass, data_typeã¯Data_Wrap_Structã¨åŒã˜åƒãã‚’ã—ã¾ã™ï¼Žtype
ã¯å‰²ã‚Šå½“ã¦ã‚‹C構造体ã®åž‹ã§ã™ï¼Žå‰²ã‚Šå½“ã¦ã‚‰ã‚ŒãŸæ§‹é€ ä½“ã¯å¤‰æ•°sval
ã«ä»£å…¥ã•れã¾ã™ï¼Žã“ã®å¤‰æ•°ã®åž‹ã¯ (type*) ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ï¼Ž
-=== オブジェクトã‹ã‚‰æ§‹é€ ä½“ã¸
+==== オブジェクトã‹ã‚‰æ§‹é€ ä½“ã¸
TypedData_Wrap_Structã‚„TypedData_Make_Structã§ç”Ÿæˆã—ãŸã‚ªãƒ–ジェ
クトã‹ã‚‰æ§‹é€ ä½“ã¸ã®ãƒã‚¤ãƒ³ã‚¿ã‚’復元ã™ã‚‹ã«ã¯ä»¥ä¸‹ã®ãƒžã‚¯ãƒ­ã‚’用ã„ã¾
@@ -806,7 +814,9 @@ Cã®æ§‹é€ ä½“ã¸ã®ãƒã‚¤ãƒ³ã‚¿ã¯å¤‰æ•°svalã«ä»£å…¥ã•れã¾ã™ï¼Ž
ã“れらã®ãƒžã‚¯ãƒ­ã®ä½¿ã„æ–¹ã¯ã¡ã‚‡ã£ã¨åˆ†ã‹ã‚Šã«ãã„ã®ã§ï¼Œå¾Œã§èª¬æ˜Žã™
る例題をå‚ç…§ã—ã¦ãã ã•ã„.
-== ディレクトリを作る
+== 例: dbmã®æ‹¡å¼µãƒ©ã‚¤ãƒ–ラリã®ä½œæˆ
+
+=== ディレクトリを作る
% mkdir ext/dbm
@@ -816,14 +826,14 @@ Ruby 1.1ã‹ã‚‰ã¯ä»»æ„ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã§ãƒ€ã‚¤ãƒŠãƒŸãƒƒã‚¯ãƒ©ã‚¤ãƒ–ラリ
ライブラリ用ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’作る必è¦ãŒã‚りã¾ã™ï¼Žåå‰ã¯é©å½“ã«
é¸ã‚“ã§æ§‹ã„ã¾ã›ã‚“.
-== 設計ã™ã‚‹
+=== 設計ã™ã‚‹
ã¾ã‚,当然ãªã‚“ã§ã™ã‘ã©ï¼Œã©ã†ã„ã†æ©Ÿèƒ½ã‚’実ç¾ã™ã‚‹ã‹ã©ã†ã‹ã¾ãšè¨­
計ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ï¼Žã©ã‚“ãªã‚¯ãƒ©ã‚¹ã‚’ã¤ãã‚‹ã‹ï¼Œãã®ã‚¯ãƒ©ã‚¹ã«ã¯
ã©ã‚“ãªãƒ¡ã‚½ãƒƒãƒ‰ãŒã‚ã‚‹ã‹ï¼Œã‚¯ãƒ©ã‚¹ãŒæä¾›ã™ã‚‹å®šæ•°ãªã©ã«ã¤ã„ã¦è¨­è¨ˆ
ã—ã¾ã™ï¼Ž
-== Cコードを書ã
+=== Cコードを書ã
拡張ライブラリ本体ã¨ãªã‚‹C言語ã®ã‚½ãƒ¼ã‚¹ã‚’書ãã¾ã™ï¼ŽC言語ã®ã‚½ãƒ¼
スãŒã²ã¨ã¤ã®æ™‚ã«ã¯ã€Œãƒ©ã‚¤ãƒ–ラリå.cã€ã‚’é¸ã¶ã¨è‰¯ã„ã§ã—ょã†ï¼ŽC
@@ -865,7 +875,7 @@ Rubyã¯æ‹¡å¼µãƒ©ã‚¤ãƒ–ラリをロードã™ã‚‹æ™‚ã«ã€ŒInit_ライブラリåã€
DBMライブラリã¯dbmã®ãƒ‡ãƒ¼ã‚¿ã¨å¯¾å¿œã™ã‚‹ã‚ªãƒ–ジェクトã«ãªã‚‹ã¯ãšã§
ã™ã‹ã‚‰ï¼ŒCã®ä¸–界ã®dbmã‚’Rubyã®ä¸–界ã«å–り込む必è¦ãŒã‚りã¾ã™ï¼Ž
-dbm.cã§ã¯Data_Make_Structを以下ã®ã‚ˆã†ã«ä½¿ã£ã¦ã„ã¾ã™ï¼Ž
+dbm.cã§ã¯TypedData_Make_Structを以下ã®ã‚ˆã†ã«ä½¿ã£ã¦ã„ã¾ã™ï¼Ž
struct dbmdata {
int di_size;
@@ -923,7 +933,7 @@ fdbm_delete()ã¯ã“ã®ã‚ˆã†ã«ãªã£ã¦ã„ã¾ã™ï¼Ž
/* ... */
if (rb_scan_args(argc, argv, "11", &file, &vmode) == 1) {
- mode = 0666; /* default value */
+ mode = 0666; /* default value */
}
/* ... */
@@ -960,7 +970,7 @@ Cã®å¤§åŸŸå¤‰æ•°ã¯ä»¥ä¸‹ã®é–¢æ•°ã‚’使ã£ã¦Rubyインタプリタã«å¤‰æ•°ã®
void rb_global_variable(VALUE *var)
-== extconf.rbを用æ„ã™ã‚‹
+=== extconf.rbを用æ„ã™ã‚‹
Makefileを作る場åˆã®é››åž‹ã«ãªã‚‹extconf.rbã¨ã„ã†ãƒ•ァイルを作り
ã¾ã™ï¼Žextconf.rbã¯ãƒ©ã‚¤ãƒ–ラリã®ã‚³ãƒ³ãƒ‘イルã«å¿…è¦ãªæ¡ä»¶ã®ãƒã‚§ãƒƒ
@@ -991,7 +1001,7 @@ Makefileを作る場åˆã®é››åž‹ã«ãªã‚‹extconf.rbã¨ã„ã†ãƒ•ァイルを作ã‚
パイルã—ãªã„時ã«ã¯create_makefileを呼ã°ãªã‘れã°Makefileã¯ç”Ÿ
æˆã•れãšï¼Œã‚³ãƒ³ãƒ‘イルも行ã‚れã¾ã›ã‚“.
-== dependを用æ„ã™ã‚‹
+=== dependを用æ„ã™ã‚‹
ã‚‚ã—,ディレクトリã«dependã¨ã„ã†ãƒ•ァイルãŒå­˜åœ¨ã™ã‚Œã°ï¼Œ
MakefileãŒä¾å­˜é–¢ä¿‚ã‚’ãƒã‚§ãƒƒã‚¯ã—ã¦ãれã¾ã™ï¼Ž
@@ -1000,7 +1010,7 @@ MakefileãŒä¾å­˜é–¢ä¿‚ã‚’ãƒã‚§ãƒƒã‚¯ã—ã¦ãれã¾ã™ï¼Ž
ãªã©ã§ä½œã‚‹ã“ã¨ãŒå‡ºæ¥ã¾ã™ï¼Žã‚ã£ã¦æã¯ç„¡ã„ã§ã—ょã†ï¼Ž
-== Makefileを生æˆã™ã‚‹
+=== Makefileを生æˆã™ã‚‹
Makefileを実際ã«ç”Ÿæˆã™ã‚‹ãŸã‚ã«ã¯
@@ -1022,7 +1032,7 @@ vendor_ruby ディレクトリã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã™ã‚‹å ´åˆã«ã¯
ディレクトリをext以下ã«ç”¨æ„ã—ãŸå ´åˆã«ã¯Ruby全体ã®makeã®æ™‚ã«
自動的ã«MakefileãŒç”Ÿæˆã•れã¾ã™ã®ã§ï¼Œã“ã®ã‚¹ãƒ†ãƒƒãƒ—ã¯ä¸è¦ã§ã™ï¼Ž
-== makeã™ã‚‹
+=== makeã™ã‚‹
動的リンクライブラリを生æˆã™ã‚‹å ´åˆã«ã¯ãã®å ´ã§makeã—ã¦ãã ã•
ã„.必è¦ã§ã‚れ㰠make install ã§ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¾ã™ï¼Ž
@@ -1040,26 +1050,26 @@ extconf.rbã‚’æ›¸ãæ›ãˆã‚‹ãªã©ã—ã¦Makefileã®å†ç”ŸæˆãŒå¿…è¦ãªæ™‚ã¯ã
を作り,ãã“ã« æ‹¡å¼µå­ .rb ã®ãƒ•ァイルを置ã„ã¦ãŠã‘ã°åŒæ™‚ã«ã‚¤ãƒ³
ストールã•れã¾ã™ï¼Ž
-== デãƒãƒƒã‚°
+=== デãƒãƒƒã‚°
ã¾ã‚,デãƒãƒƒã‚°ã—ãªã„ã¨å‹•ã‹ãªã„ã§ã—ょã†ã­ï¼Žext/Setupã«ãƒ‡ã‚£ãƒ¬
クトリåを書ãã¨é™çš„ã«ãƒªãƒ³ã‚¯ã™ã‚‹ã®ã§ãƒ‡ãƒãƒƒã‚¬ãŒä½¿ãˆã‚‹ã‚ˆã†ã«ãª
りã¾ã™ï¼Žãã®åˆ†ã‚³ãƒ³ãƒ‘イルãŒé…ããªã‚Šã¾ã™ã‘ã©ï¼Ž
-== ã§ãã‚ãŒã‚Š
+=== ã§ãã‚ãŒã‚Š
後ã¯ã“ã£ãり使ã†ãªã‚Šï¼Œåºƒã公開ã™ã‚‹ãªã‚Šï¼Œå£²ã‚‹ãªã‚Šï¼Œã”自由ã«ãŠ
使ã„ãã ã•ã„.Rubyã®ä½œè€…ã¯æ‹¡å¼µãƒ©ã‚¤ãƒ–ラリã«é–¢ã—ã¦ä¸€åˆ‡ã®æ¨©åˆ©ã‚’
主張ã—ã¾ã›ã‚“.
-= Appendix A. Rubyã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã®åˆ†é¡ž
+== Appendix A. Rubyã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã®åˆ†é¡ž
Rubyã®ã‚½ãƒ¼ã‚¹ã¯ã„ãã¤ã‹ã«åˆ†é¡žã™ã‚‹ã“ã¨ãŒå‡ºæ¥ã¾ã™ï¼Žã“ã®ã†ã¡ã‚¯ãƒ©
スライブラリã®éƒ¨åˆ†ã¯åŸºæœ¬çš„ã«æ‹¡å¼µãƒ©ã‚¤ãƒ–ラリã¨åŒã˜ä½œã‚Šæ–¹ã«ãªã£
ã¦ã„ã¾ã™ï¼Žã“れらã®ã‚½ãƒ¼ã‚¹ã¯ä»Šã¾ã§ã®èª¬æ˜Žã§ã»ã¨ã‚“ã©ç†è§£ã§ãã‚‹ã¨
æ€ã„ã¾ã™ï¼Ž
-== Ruby言語ã®ã‚³ã‚¢
+=== Ruby言語ã®ã‚³ã‚¢
class.c :: クラスã¨ãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«
error.c :: 例外クラスã¨ä¾‹å¤–機構
@@ -1068,14 +1078,14 @@ load.c :: ライブラリã®ãƒ­ãƒ¼ãƒ‰
object.c :: オブジェクト
variable.c :: 変数ã¨å®šæ•°
-== Rubyã®æ§‹æ–‡è§£æžå™¨
+=== Rubyã®æ§‹æ–‡è§£æžå™¨
parse.y :: å­—å¥è§£æžå™¨ã¨æ§‹æ–‡å®šç¾©
parse.c :: 自動生æˆ
defs/keywords :: 予約語
lex.c :: 自動生æˆ
-== Rubyã®è©•価器 (通称YARV)
+=== Rubyã®è©•価器 (通称YARV)
compile.c
eval.c
@@ -1101,7 +1111,7 @@ lex.c :: 自動生æˆ
-> opt*.inc : 自動生æˆ
-> vm.inc : 自動生æˆ
-== æ­£è¦è¡¨ç¾ã‚¨ãƒ³ã‚¸ãƒ³ (鬼車)
+=== æ­£è¦è¡¨ç¾ã‚¨ãƒ³ã‚¸ãƒ³ (鬼車)
regex.c
regcomp.c
@@ -1111,7 +1121,7 @@ lex.c :: 自動生æˆ
regparse.c
regsyntax.c
-== ユーティリティ関数
+=== ユーティリティ関数
debug.c :: Cデãƒãƒƒã‚¬ç”¨ã®ãƒ‡ãƒãƒƒã‚°ã‚·ãƒ³ãƒœãƒ«
dln.c :: 動的ローディング
@@ -1119,7 +1129,7 @@ st.c :: 汎用ãƒãƒƒã‚·ãƒ¥è¡¨
strftime.c :: 時刻整形
util.c :: ãã®ä»–ã®ãƒ¦ãƒ¼ãƒ†ã‚£ãƒªãƒ†ã‚£
-== Rubyコマンドã®å®Ÿè£…
+=== Rubyコマンドã®å®Ÿè£…
dmyext.c
dmydln.c
@@ -1133,7 +1143,7 @@ util.c :: ãã®ä»–ã®ãƒ¦ãƒ¼ãƒ†ã‚£ãƒªãƒ†ã‚£
gem_prelude.rb
prelude.rb
-== クラスライブラリ
+=== クラスライブラリ
array.c :: Array
bignum.c :: Bignum
@@ -1165,24 +1175,24 @@ time.c :: Time
defs/known_errors.def :: 例外クラス Errno::*
-> known_errors.inc :: 自動生æˆ
-== 多言語化
+=== 多言語化
encoding.c :: Encoding
transcode.c :: Encoding::Converter
enc/*.c :: エンコーディングクラス群
enc/trans/* :: コードãƒã‚¤ãƒ³ãƒˆå¯¾å¿œè¡¨
-== gorubyコマンドã®å®Ÿè£…
+=== gorubyコマンドã®å®Ÿè£…
goruby.c
golf_prelude.rb : goruby固有ã®ãƒ©ã‚¤ãƒ–ラリ
-> golf_prelude.c : 自動生æˆ
-= Appendix B. 拡張用関数リファレンス
+== Appendix B. 拡張用関数リファレンス
C言語ã‹ã‚‰Rubyã®æ©Ÿèƒ½ã‚’利用ã™ã‚‹APIã¯ä»¥ä¸‹ã®é€šã‚Šã§ã‚る.
-== åž‹
+=== åž‹
VALUE ::
@@ -1191,7 +1201,7 @@ VALUE ::
体ã§ã‚る.VALUE型をã“れらã«ã‚­ãƒ£ã‚¹ãƒˆã™ã‚‹ãŸã‚ã«Rã§å§‹ã¾ã‚‹æ§‹é€ ä½“
åã‚’å…¨ã¦å¤§æ–‡å­—ã«ã—ãŸåå‰ã®ãƒžã‚¯ãƒ­ãŒç”¨æ„ã•れã¦ã„る.
-== 変数・定数
+=== 変数・定数
Qnil ::
@@ -1205,7 +1215,7 @@ Qfalse ::
定数: falseオブジェクト
-== Cデータã®ã‚«ãƒ—セル化
+=== Cデータã®ã‚«ãƒ—セル化
Data_Wrap_Struct(VALUE klass, void (*mark)(), void (*free)(), void *sval) ::
@@ -1224,7 +1234,7 @@ Data_Get_Struct(data, type, sval) ::
dataã‹ã‚‰typeåž‹ã®ãƒã‚¤ãƒ³ã‚¿ã‚’å–り出ã—変数svalã«ä»£å…¥ã™ã‚‹ãƒžã‚¯ãƒ­ï¼Ž
-== åž‹ãƒã‚§ãƒƒã‚¯
+=== åž‹ãƒã‚§ãƒƒã‚¯
RB_TYPE_P(value, type)
TYPE(value)
@@ -1235,7 +1245,7 @@ Data_Get_Struct(data, type, sval) ::
void Check_Type(VALUE value, int type)
SafeStringValue(value)
-== 型変æ›
+=== 型変æ›
FIX2INT(value), INT2FIX(i)
FIX2LONG(value), LONG2FIX(l)
@@ -1258,7 +1268,7 @@ Data_Get_Struct(data, type, sval) ::
StringValueCStr(value)
rb_str_new2(s)
-== クラス/モジュール定義
+=== クラス/モジュール定義
VALUE rb_define_class(const char *name, VALUE super) ::
@@ -1286,7 +1296,7 @@ void rb_extend_object(VALUE object, VALUE module) ::
オブジェクトをモジュール(ã§å®šç¾©ã•れã¦ã„るメソッド)ã§æ‹¡å¼µã™ã‚‹ï¼Ž
-== 大域変数定義
+=== 大域変数定義
void rb_define_variable(const char *name, VALUE *var) ::
@@ -1315,10 +1325,15 @@ void rb_define_hooked_variable(const char *name, VALUE *var, VALUE (*getter)(),
void rb_global_variable(VALUE *var) ::
- GCã®ãŸã‚,Rubyプログラムã‹ã‚‰ã¯ã‚¢ã‚¯ã‚»ã‚¹ã•れãªã„ãŒ, Rubyオブ
- ジェクトをå«ã‚€å¤§åŸŸå¤‰æ•°ã‚’マークã™ã‚‹ï¼Ž
+ マークã™ã‚‹å¿…è¦ã®ã‚ã‚‹Rubyオブジェクトをå«ã‚€å¤§åŸŸå¤‰æ•°ã‚’,GC
+ ã«ã‚ˆã£ã¦è§£æ”¾ã•れãªã„よã†ã«ä¿è­·ã™ã‚‹ï¼Ž
+
+void rb_gc_register_mark_object(VALUE *var) ::
+
+ マークã™ã‚‹å¿…è¦ã®ã‚ã‚‹Rubyオブジェクトを,GCã«ã‚ˆã£ã¦è§£æ”¾ã•
+ れãªã„よã†ã«ç™»éŒ²ã™ã‚‹ï¼Ž
-== 定数
+=== 定数
void rb_define_const(VALUE klass, const char *name, VALUE val) ::
@@ -1332,9 +1347,9 @@ void rb_define_global_const(const char *name, VALUE val) ::
ã¨åŒã˜æ„味.
-== メソッド定義
+=== メソッド定義
-rb_define_method(VALUE klass, const char *name, VALUE (*func)(), int argc) ::
+rb_define_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc) ::
メソッドを定義ã™ã‚‹ï¼Žargcã¯selfを除ãå¼•æ•°ã®æ•°ï¼ŽargcãŒ-1ã®æ™‚,
関数ã«ã¯å¼•æ•°ã®æ•°(selfã‚’å«ã¾ãªã„)を第1引数, 引数ã®é…列を第2
@@ -1342,11 +1357,11 @@ rb_define_method(VALUE klass, const char *name, VALUE (*func)(), int argc) ::
第1引数ãŒself, 第2引数ãŒargs(argsã¯å¼•æ•°ã‚’å«ã‚€Rubyã®é…列)ã¨
ã„ã†å½¢å¼ã§ä¸Žãˆã‚‰ã‚Œã‚‹ï¼Ž
-rb_define_private_method(VALUE klass, const char *name, VALUE (*func)(), int argc) ::
+rb_define_private_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc) ::
privateメソッドを定義ã™ã‚‹ï¼Žå¼•æ•°ã¯rb_define_method()ã¨åŒã˜ï¼Ž
-rb_define_singleton_method(VALUE klass, const char *name, VALUE (*func)(), int argc) ::
+rb_define_singleton_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc) ::
特異メソッドを定義ã™ã‚‹ï¼Žå¼•æ•°ã¯rb_define_method()ã¨åŒã˜ï¼Ž
@@ -1409,10 +1424,10 @@ int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optiona
optional (optionalãŒè² ã®å ´åˆã¯-optional-1) 個ã®IDã¯çœç•¥å¯èƒ½
キーワードã§ã™ï¼Žå¿…須キーワードãŒkeyword_hash中ã«ãªã„å ´åˆï¼Œ
"missing keyword"ArgumentErrorãŒç™ºç”Ÿã—ã¾ã™ï¼Žçœç•¥å¯èƒ½ã‚­ãƒ¼ãƒ¯ãƒ¼
- ドãŒãªã„å ´åˆã¯ï¼Œvalues中ã®å¯¾å¿œã™ã‚‹è¦ç´ ã¯å¤‰æ›´ã•れã¾ã›ã‚“.
- keyword_hashã«ä½¿ç”¨ã•れãªã„è¦ç´ ãŒã‚ã‚‹å ´åˆã¯ï¼ŒoptionalãŒè² ãªã‚‰
- æ–°ã—ã„Hashã¨ã—ã¦çœç•¥å¯èƒ½å¼•æ•°ã®æ¬¡ã«ä¿å­˜ã•れã¾ã™ãŒï¼Œãã†ã§ãªã‘
- れã°"unknown keyword"ArgumentErrorãŒç™ºç”Ÿã—ã¾ã™ï¼Ž
+ ドãŒãªã„å ´åˆã¯ï¼Œvalues中ã®å¯¾å¿œã™ã‚‹è¦ç´ ã«ã¯QundefãŒã‚»ãƒƒãƒˆã•れ
+ ã¾ã™ï¼Žkeyword_hashã«ä½¿ç”¨ã•れãªã„è¦ç´ ãŒã‚ã‚‹å ´åˆã¯ï¼ŒoptionalãŒ
+ è² ãªã‚‰ç„¡è¦–ã•れã¾ã™ãŒï¼Œãã†ã§ãªã‘れã°"unknown keyword"
+ ArgumentErrorãŒç™ºç”Ÿã—ã¾ã™ï¼Ž
VALUE rb_extract_keywords(VALUE *original_hash) ::
@@ -1421,7 +1436,7 @@ VALUE rb_extract_keywords(VALUE *original_hash) ::
å…ˆã«ã¯ï¼Œå…ƒã®HashãŒSymbol以外ã®ã‚­ãƒ¼ã‚’å«ã‚“ã§ã„ãŸå ´åˆã¯ãれらãŒ
コピーã•れãŸåˆ¥ã®æ–°ã—ã„Hash,ãã†ã§ãªã‘れã°0ãŒä¿å­˜ã•れã¾ã™ï¼Ž
-== Rubyメソッド呼ã³å‡ºã—
+=== Rubyメソッド呼ã³å‡ºã—
VALUE rb_funcall(VALUE recv, ID mid, int narg, ...) ::
@@ -1461,7 +1476,7 @@ int rb_respond_to(VALUE obj, ID id) ::
objãŒidã§ç¤ºã•れるメソッドをæŒã¤ã‹ã©ã†ã‹ã‚’è¿”ã™ï¼Ž
-== インスタンス変数
+=== インスタンス変数
VALUE rb_iv_get(VALUE obj, const char *name) ::
@@ -1474,7 +1489,7 @@ VALUE rb_iv_set(VALUE obj, const char *name, VALUE val) ::
objã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹å¤‰æ•°ã‚’valã«ã‚»ãƒƒãƒˆã™ã‚‹ï¼Ž
-== 制御構造
+=== 制御構造
VALUE rb_block_call(VALUE obj, ID mid, int argc, VALUE * argv, VALUE (*func) (ANYARGS), VALUE data2) ::
@@ -1536,7 +1551,7 @@ void rb_iter_break_value(VALUE value) ::
ç¾åœ¨ã®æœ€ã‚‚内å´ã®ãƒ–ロックをvalueã§çµ‚了ã™ã‚‹ï¼Žãƒ–ロックã¯å¼•æ•°ã§
与ãˆã‚‰ã‚ŒãŸvalueã‚’è¿”ã™ï¼Žã“ã®é–¢æ•°ã¯ç›´æŽ¥ã®å‘¼ã³å‡ºã—å…ƒã«æˆ»ã‚‰ãªã„.
-== 例外・エラー
+=== 例外・エラー
void rb_warning(const char *fmt, ...) ::
@@ -1568,7 +1583,7 @@ void rb_bug(const char *fmt, ...) ::
ãã¯Object#inspect)を使ã£ãŸVALUEã®å‡ºåŠ›ã«åˆ©ç”¨ã§ãる.ã“れã¯
"%i"ã¨è¡çªã™ã‚‹ãŸã‚,整数ã«ã¯"%d"を使用ã™ã‚‹ã“ã¨ï¼Ž
-== Rubyã®åˆæœŸåŒ–・実行
+=== Rubyã®åˆæœŸåŒ–・実行
Rubyをアプリケーションã«åŸ‹ã‚込む場åˆã«ã¯ä»¥ä¸‹ã®ã‚¤ãƒ³ã‚¿ãƒ•ェース
を使ã†ï¼Žé€šå¸¸ã®æ‹¡å¼µãƒ©ã‚¤ãƒ–ラリã«ã¯å¿…è¦ãªã„.
@@ -1592,7 +1607,7 @@ void ruby_script(char *name) ::
Rubyã®ã‚¹ã‚¯ãƒªãƒ—トå($0)を設定ã™ã‚‹ï¼Ž
-== インタプリタã®ã‚¤ãƒ™ãƒ³ãƒˆã®ãƒ•ック
+=== インタプリタã®ã‚¤ãƒ™ãƒ³ãƒˆã®ãƒ•ック
void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data) ::
@@ -1622,7 +1637,7 @@ int rb_remove_event_hook(rb_event_hook_func_t func) ::
指定ã•れãŸãƒ•ック関数を削除ã—ã¾ã™ï¼Ž
-== メモリ使用é‡
+=== メモリ使用é‡
void rb_gc_adjust_memory_usage(ssize_t diff) ::
@@ -1634,7 +1649,7 @@ void rb_gc_adjust_memory_usage(ssize_t diff) ::
ã™ï¼Žãƒ¡ãƒ¢ãƒªãƒ–ロックãŒè§£æ”¾ã•れãŸã‚Šï¼Œãƒ¡ãƒ¢ãƒªãƒ–ロックãŒã‚ˆã‚Šå°ã•ã„サイズã§å†
確ä¿ã•れãŸã‚Šã—ãŸå ´åˆãªã©ã§ã™ï¼Žã“ã®é–¢æ•°ã¯GCを引ãèµ·ã“ã™ã‹ã‚‚ã—れã¾ã›ã‚“.
-== äº’æ›æ€§ã®ãŸã‚ã®ãƒžã‚¯ãƒ­
+=== äº’æ›æ€§ã®ãŸã‚ã®ãƒžã‚¯ãƒ­
APIã®äº’æ›æ€§ã‚’ãƒã‚§ãƒƒã‚¯ã™ã‚‹ãŸã‚ã«ä»¥ä¸‹ã®ãƒžã‚¯ãƒ­ãŒãƒ‡ãƒ•ォルトã§å®šç¾©ã•れã¦ã„ã¾ã™ï¼Ž
@@ -1676,7 +1691,7 @@ RB_EVENT_HOOKS_HAVE_CALLBACK_DATA ::
rb_add_event_hook() ãŒãƒ•ãƒƒã‚¯é–¢æ•°ã«æ¸¡ã™ data を第3引数ã¨ã—ã¦
å—ã‘å–ã‚‹ã“ã¨ã‚’æ„味ã™ã‚‹ï¼Ž
-= Appendix C. extconf.rbã§ä½¿ãˆã‚‹é–¢æ•°ãŸã¡
+== Appendix C. extconf.rbã§ä½¿ãˆã‚‹é–¢æ•°ãŸã¡
extconf.rbã®ä¸­ã§ã¯åˆ©ç”¨å¯èƒ½ãªã‚³ãƒ³ãƒ‘イルæ¡ä»¶ãƒã‚§ãƒƒã‚¯ã®é–¢æ•°ã¯ä»¥
下ã®é€šã‚Šã§ã‚る.
@@ -1802,7 +1817,7 @@ pkg_config(pkg, option=nil) ::
optionãŒæŒ‡å®šã•れãŸå ´åˆã¯ï¼Œä¸Šè¨˜ã®é…列ã®ä»£ã‚りã«ãã®ã‚ªãƒ—ションを
指定ã—ã¦å¾—られãŸå‡ºåŠ›ã‚’stripã—ãŸã‚‚ã®ã‚’è¿”ã™ï¼Ž
-= Appendix D. 世代別GC
+== Appendix D. 世代別GC
Ruby 2.1ã‹ã‚‰ä¸–代別GCã«å¯¾å¿œã—ã¾ã—ãŸï¼Žæˆ‘々ã¯ã“れをRGenGCã¨å‘¼ã‚“ã§ã„ã¾ã™ï¼Ž
RGenGCã¯ï¼ŒéŽåŽ»ã®æ‹¡å¼µãƒ©ã‚¤ãƒ–ラリã«ï¼ˆã»ã¼ï¼‰äº’æ›æ€§ã‚’ä¿ã¤ã‚ˆã†ã«é–‹ç™ºã•れã¦ã„ã‚‹
diff --git a/doc/extension.rdoc b/doc/extension.rdoc
index 42a445602b..b7cba0d5e8 100644
--- a/doc/extension.rdoc
+++ b/doc/extension.rdoc
@@ -1,8 +1,10 @@
# extension.rdoc - -*- RDoc -*- created at: Mon Aug 7 16:45:54 JST 1995
+= Creating Extension Libraries for Ruby
+
This document explains how to make extension libraries for Ruby.
-= Basic Knowledge
+== Basic Knowledge
In C, variables have types and data do not have types. In contrast,
Ruby variables do not have a static type, and data themselves have
@@ -18,7 +20,7 @@ To retrieve C data from a VALUE, you need to:
Converting to the wrong data type may cause serious problems.
-== Data Types
+=== Data Types
The Ruby interpreter has the following data types:
@@ -52,7 +54,7 @@ T_ZOMBIE :: object awaiting finalization
Most of the types are represented by C structures.
-== Check Data Type of the VALUE
+=== Check Data Type of the VALUE
The macro TYPE() defined in ruby.h shows the data type of the VALUE.
TYPE() returns the constant number T_XXXX described above. To handle
@@ -86,7 +88,7 @@ There are also faster check macros for fixnums and nil.
FIXNUM_P(obj)
NIL_P(obj)
-== Convert VALUE into C Data
+=== Convert VALUE into C Data
The data for type T_NIL, T_FALSE, T_TRUE are nil, false, true
respectively. They are singletons for the data type.
@@ -139,7 +141,7 @@ Notice: Do not change the value of the structure directly, unless you
are responsible for the result. This ends up being the cause of
interesting bugs.
-== Convert C Data into VALUE
+=== Convert C Data into VALUE
To convert C data to Ruby values:
@@ -165,14 +167,14 @@ INT2NUM() :: for arbitrary sized integers.
INT2NUM() converts an integer into a Bignum if it is out of the FIXNUM
range, but is a bit slower.
-== Manipulating Ruby Data
+=== Manipulating Ruby Data
As I already mentioned, it is not recommended to modify an object's
internal structure. To manipulate objects, use the functions supplied
by the Ruby interpreter. Some (not all) of the useful functions are
listed below:
-=== String Functions
+==== String Functions
rb_str_new(const char *ptr, long len) ::
@@ -208,6 +210,10 @@ rb_vsprintf(const char *format, va_list ap) ::
must be a VALUE). Since it conflicts with "%i", for integers in
format strings, use "%d".
+rb_str_append(VALUE str1, VALUE str2) ::
+
+ Appends Ruby string str2 to Ruby string str1.
+
rb_str_cat(VALUE str, const char *ptr, long len) ::
Appends len bytes of data from ptr to the Ruby string.
@@ -223,15 +229,15 @@ rb_str_vcatf(VALUE str, const char* format, va_list ap) ::
Appends C string format and successive arguments to Ruby string
str according to a printf-like format. These functions are
- equivalent to rb_str_cat2(str, rb_sprintf(format, ...)) and
- rb_str_cat2(str, rb_vsprintf(format, ap)), respectively.
+ equivalent to rb_str_append(str, rb_sprintf(format, ...)) and
+ rb_str_append(str, rb_vsprintf(format, ap)), respectively.
rb_enc_str_new(const char *ptr, long len, rb_encoding *enc) ::
rb_enc_str_new_cstr(const char *ptr, rb_encoding *enc) ::
Creates a new Ruby string with the specified encoding.
-rb_enc_str_new_literal(const char *ptr) ::
+rb_enc_str_new_literal(const char *ptr, rb_encoding *enc) ::
Creates a new Ruby string from a C string literal with the specified
encoding.
@@ -273,7 +279,7 @@ rb_str_set_len(VALUE str, long len) ::
up to len bytes, regardless RSTRING_LEN(str). len must not exceed
the capacity of str.
-=== Array Functions
+==== Array Functions
rb_ary_new() ::
@@ -303,7 +309,7 @@ rb_ary_to_ary(VALUE obj) ::
There are many functions to operate an array. They may dump core if other
types are given.
-rb_ary_aref(argc, VALUE *argv, VALUE ary) ::
+rb_ary_aref(int argc, const VALUE *argv, VALUE ary) ::
Equivalent to Array#[].
@@ -330,9 +336,9 @@ rb_ary_cat(VALUE ary, const VALUE *ptr, long len) ::
Appends len elements of objects from ptr to the array.
-= Extending Ruby with C
+== Extending Ruby with C
-== Adding New Features to Ruby
+=== Adding New Features to Ruby
You can add new features (classes, methods, etc.) to the Ruby
interpreter. Ruby provides APIs for defining the following things:
@@ -341,7 +347,7 @@ interpreter. Ruby provides APIs for defining the following things:
- Methods, Singleton Methods
- Constants
-=== Class and Module Definition
+==== Class and Module Definition
To define a class or module, use the functions below:
@@ -356,15 +362,15 @@ To define nested classes or modules, use the functions below:
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
VALUE rb_define_module_under(VALUE outer, const char *name)
-=== Method and Singleton Method Definition
+==== Method and Singleton Method Definition
To define methods or singleton methods, use these functions:
void rb_define_method(VALUE klass, const char *name,
- VALUE (*func)(), int argc)
+ VALUE (*func)(ANYARGS), int argc)
void rb_define_singleton_method(VALUE object, const char *name,
- VALUE (*func)(), int argc)
+ VALUE (*func)(ANYARGS), int argc)
The `argc' represents the number of the arguments to the C function,
which must be less than 17. But I doubt you'll need that many.
@@ -396,9 +402,9 @@ as the name of method to be defined. See also ID or Symbol below.
There are two functions to define private/protected methods:
void rb_define_private_method(VALUE klass, const char *name,
- VALUE (*func)(), int argc)
+ VALUE (*func)(ANYARGS), int argc)
void rb_define_protected_method(VALUE klass, const char *name,
- VALUE (*func)(), int argc)
+ VALUE (*func)(ANYARGS), int argc)
At last, rb_define_module_function defines a module function,
which are private AND singleton methods of the module.
@@ -415,12 +421,12 @@ or
To define module functions, use:
void rb_define_module_function(VALUE module, const char *name,
- VALUE (*func)(), int argc)
+ VALUE (*func)(ANYARGS), int argc)
In addition, function-like methods, which are private methods defined
in the Kernel module, can be defined using:
- void rb_define_global_function(const char *name, VALUE (*func)(), int argc)
+ void rb_define_global_function(const char *name, VALUE (*func)(ANYARGS), int argc)
To define an alias for the method,
@@ -449,7 +455,7 @@ available), you can use:
VALUE rb_current_receiver(void)
-=== Constant Definition
+==== Constant Definition
We have 2 functions to define constants:
@@ -459,11 +465,11 @@ We have 2 functions to define constants:
The former is to define a constant under specified class/module. The
latter is to define a global constant.
-== Use Ruby Features from C
+=== Use Ruby Features from C
There are several ways to invoke Ruby's features from C code.
-=== Evaluate Ruby Programs in a String
+==== Evaluate Ruby Programs in a String
The easiest way to use Ruby's functionality from a C program is to
evaluate the string as Ruby program. This function will do the job:
@@ -481,7 +487,7 @@ function:
It returns nil when an error occurred. Moreover, *state is zero if str was
successfully evaluated, or nonzero otherwise.
-=== ID or Symbol
+==== ID or Symbol
You can invoke methods directly, without parsing the string. First I
need to explain about ID. ID is the integer number to represent
@@ -532,7 +538,7 @@ and to convert Ruby Symbol object to ID, use
ID SYM2ID(VALUE symbol)
-=== Invoke Ruby Method from C
+==== Invoke Ruby Method from C
To invoke methods directly, you can use the function below
@@ -541,7 +547,7 @@ To invoke methods directly, you can use the function below
This function invokes a method on the recv, with the method name
specified by the symbol mid.
-=== Accessing the Variables and Constants
+==== Accessing the Variables and Constants
You can access class variables and instance variables using access
functions. Also, global variables can be shared between both
@@ -560,7 +566,7 @@ To access the constants of the class/module:
See also Constant Definition above.
-= Information Sharing Between Ruby and C
+== Information Sharing Between Ruby and C
=== Ruby Constants That Can Be Accessed From C
@@ -576,7 +582,7 @@ Qnil ::
Ruby nil in C scope.
-== Global Variables Shared Between C and Ruby
+=== Global Variables Shared Between C and Ruby
Information can be shared between the two environments using shared global
variables. To define them, you can use functions listed below:
@@ -596,7 +602,7 @@ You can define hooked variables. The accessor functions (getter and
setter) are called on access to the hooked variables.
void rb_define_hooked_variable(const char *name, VALUE *var,
- VALUE (*getter)(), void (*setter)())
+ VALUE (*getter)(), void (*setter)())
If you need to supply either setter or getter, just supply 0 for the
hook you don't need. If both hooks are 0, rb_define_hooked_variable()
@@ -611,14 +617,14 @@ Also you can define a Ruby global variable without a corresponding C
variable. The value of the variable will be set/get only by hooks.
void rb_define_virtual_variable(const char *name,
- VALUE (*getter)(), void (*setter)())
+ VALUE (*getter)(), void (*setter)())
The prototypes of the getter and setter functions are as follows:
VALUE (*getter)(ID id);
void (*setter)(VALUE val, ID id);
-== Encapsulate C Data into a Ruby Object
+=== Encapsulate C Data into a Ruby Object
Sometimes you need to expose your struct in the C world as a Ruby
object.
@@ -632,7 +638,7 @@ In the future version of Ruby, it is possible old macros will not
work.
++
-=== C struct to Ruby object
+==== C struct to Ruby object
You can convert sval, a pointer to your struct, into a Ruby object
with the next macro.
@@ -652,6 +658,8 @@ If it doesn't, you have to call rb_undef_alloc_func(klass).
rb_data_type_t is defined like this. Let's take a look at each
member of the struct.
+ typedef struct rb_data_type_struct rb_data_type_t;
+
struct rb_data_type_struct {
const char *wrap_struct_name;
struct {
@@ -735,10 +743,10 @@ Arguments klass and data_type work like their counterparts in
TypedData_Wrap_Struct(). A pointer to the allocated structure will
be assigned to sval, which should be a pointer of the type specified.
-=== Ruby object to C struct
+==== Ruby object to C struct
To retrieve the C pointer from the Data object, use the macro
-Data_Get_Struct().
+TypedData_Get_Struct().
TypedData_Get_Struct(obj, type, &data_type, sval)
@@ -746,23 +754,23 @@ A pointer to the structure will be assigned to the variable sval.
See the example below for details.
-= Example - Creating the dbm Extension
+== Example - Creating the dbm Extension
OK, here's the example of making an extension library. This is the
extension to access DBMs. The full source is included in the ext/
directory in the Ruby's source tree.
-== Make the Directory
+=== Make the Directory
% mkdir ext/dbm
Make a directory for the extension library under ext directory.
-== Design the Library
+=== Design the Library
You need to design the library features, before making it.
-== Write the C Code
+=== Write the C Code
You need to write C code for your extension library. If your library
has only one source file, choosing ``LIBRARY.c'' as a file name is
@@ -802,7 +810,7 @@ Here's the example of an initializing function.
}
The dbm extension wraps the dbm struct in the C environment using
-Data_Make_Struct.
+TypedData_Make_Struct.
struct dbmdata {
int di_size;
@@ -853,7 +861,7 @@ arguments like this:
{
/* ... */
if (rb_scan_args(argc, argv, "11", &file, &vmode) == 1) {
- mode = 0666; /* default value */
+ mode = 0666; /* default value */
}
/* ... */
}
@@ -887,7 +895,11 @@ but are not exported to the Ruby world. You need to protect them by
void rb_global_variable(VALUE *var)
-== Prepare extconf.rb
+or the objects themselves by
+
+ void rb_gc_register_mark_object(VALUE object)
+
+=== Prepare extconf.rb
If the file named extconf.rb exists, it will be executed to generate
Makefile.
@@ -936,7 +948,7 @@ If a compilation condition is not fulfilled, you should not call
``create_makefile''. The Makefile will not be generated, compilation will
not be done.
-== Prepare Depend (Optional)
+=== Prepare Depend (Optional)
If the file named depend exists, Makefile will include that file to
check dependencies. You can make this file by invoking
@@ -945,7 +957,7 @@ check dependencies. You can make this file by invoking
It's harmless. Prepare it.
-== Generate Makefile
+=== Generate Makefile
Try generating the Makefile by:
@@ -960,7 +972,7 @@ You don't need this step if you put the extension library under the ext
directory of the ruby source tree. In that case, compilation of the
interpreter will do this step for you.
-== Run make
+=== Run make
Type
@@ -969,21 +981,21 @@ Type
to compile your extension. You don't need this step either if you have
put the extension library under the ext directory of the ruby source tree.
-== Debug
+=== Debug
You may need to rb_debug the extension. Extensions can be linked
statically by adding the directory name in the ext/Setup file so that
you can inspect the extension with the debugger.
-== Done! Now You Have the Extension Library
+=== Done! Now You Have the Extension Library
You can do anything you want with your library. The author of Ruby
will not claim any restrictions on your code depending on the Ruby API.
Feel free to use, modify, distribute or sell your program.
-= Appendix A. Ruby Source Files Overview
+== Appendix A. Ruby Source Files Overview
-== Ruby Language Core
+=== Ruby Language Core
class.c :: classes and modules
error.c :: exception classes and exception mechanism
@@ -992,14 +1004,14 @@ load.c :: library loading
object.c :: objects
variable.c :: variables and constants
-== Ruby Syntax Parser
+=== Ruby Syntax Parser
parse.y :: grammar definition
parse.c :: automatically generated from parse.y
defs/keywords :: reserved keywords
lex.c :: automatically generated from keywords
-== Ruby Evaluator (a.k.a. YARV)
+=== Ruby Evaluator (a.k.a. YARV)
compile.c
eval.c
@@ -1025,7 +1037,7 @@ lex.c :: automatically generated from keywords
-> opt*.inc : automatically generated
-> vm.inc : automatically generated
-== Regular Expression Engine (Oniguruma)
+=== Regular Expression Engine (Oniguruma)
regex.c
regcomp.c
@@ -1035,7 +1047,7 @@ lex.c :: automatically generated from keywords
regparse.c
regsyntax.c
-== Utility Functions
+=== Utility Functions
debug.c :: debug symbols for C debugger
dln.c :: dynamic loading
@@ -1043,7 +1055,7 @@ st.c :: general purpose hash table
strftime.c :: formatting times
util.c :: misc utilities
-== Ruby Interpreter Implementation
+=== Ruby Interpreter Implementation
dmyext.c
dmydln.c
@@ -1057,7 +1069,7 @@ util.c :: misc utilities
gem_prelude.rb
prelude.rb
-== Class Library
+=== Class Library
array.c :: Array
bignum.c :: Bignum
@@ -1089,22 +1101,22 @@ time.c :: Time
defs/known_errors.def :: Errno::* exception classes
-> known_errors.inc :: automatically generated
-== Multilingualization
+=== Multilingualization
encoding.c :: Encoding
transcode.c :: Encoding::Converter
enc/*.c :: encoding classes
enc/trans/* :: codepoint mapping tables
-== goruby Interpreter Implementation
+=== goruby Interpreter Implementation
goruby.c
golf_prelude.rb : goruby specific libraries.
-> golf_prelude.c : automatically generated
-= Appendix B. Ruby Extension API Reference
+== Appendix B. Ruby Extension API Reference
-== Types
+=== Types
VALUE ::
@@ -1112,7 +1124,7 @@ VALUE ::
such as struct RString, etc. To refer the values in structures, use
casting macros like RSTRING(obj).
-== Variables and Constants
+=== Variables and Constants
Qnil ::
@@ -1126,7 +1138,7 @@ Qfalse ::
false object
-== C Pointer Wrapping
+=== C Pointer Wrapping
Data_Wrap_Struct(VALUE klass, void (*mark)(), void (*free)(), void *sval) ::
@@ -1146,7 +1158,7 @@ Data_Get_Struct(data, type, sval) ::
This macro retrieves the pointer value from DATA, and assigns it to
the variable sval.
-== Checking Data Types
+=== Checking Data Types
RB_TYPE_P(value, type) ::
@@ -1176,11 +1188,11 @@ void Check_Type(VALUE value, int type) ::
Ensures +value+ is of the given internal +type+ or raises a TypeError
-SaveStringValue(value) ::
+SafeStringValue(value) ::
Checks that +value+ is a String and is not tainted
-== Data Type Conversion
+=== Data Type Conversion
FIX2INT(value), INT2FIX(i) ::
@@ -1264,7 +1276,7 @@ rb_str_new2(s) ::
char * -> String
-== Defining Classes and Modules
+=== Defining Classes and Modules
VALUE rb_define_class(const char *name, VALUE super) ::
@@ -1291,7 +1303,7 @@ void rb_extend_object(VALUE object, VALUE module) ::
Extend the object with the module's attributes.
-== Defining Global Variables
+=== Defining Global Variables
void rb_define_variable(const char *name, VALUE *var) ::
@@ -1329,10 +1341,13 @@ void rb_define_hooked_variable(const char *name, VALUE *var, VALUE (*getter)(),
void rb_global_variable(VALUE *var) ::
- GC requires C global variables which hold Ruby values to be marked.
- rb_global_variable tells GC to protect these variables.
+ Tells GC to protect C global variable, which holds Ruby value to be marked.
+
+void rb_gc_register_mark_object(VALUE object) ::
-== Constant Definition
+ Tells GC to protect the +object+, which may not be referenced anywhere.
+
+=== Constant Definition
void rb_define_const(VALUE klass, const char *name, VALUE val) ::
@@ -1344,9 +1359,9 @@ void rb_define_global_const(const char *name, VALUE val) ::
rb_define_const(rb_cObject, name, val)
-== Method Definition
+=== Method Definition
-rb_define_method(VALUE klass, const char *name, VALUE (*func)(), int argc) ::
+rb_define_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc) ::
Defines a method for the class. func is the function pointer. argc
is the number of arguments. if argc is -1, the function will receive
@@ -1354,12 +1369,12 @@ rb_define_method(VALUE klass, const char *name, VALUE (*func)(), int argc) ::
receive 2 arguments, self and args, where args is a Ruby array of
the method arguments.
-rb_define_private_method(VALUE klass, const char *name, VALUE (*func)(), int argc) ::
+rb_define_private_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc) ::
Defines a private method for the class. Arguments are same as
rb_define_method().
-rb_define_singleton_method(VALUE klass, const char *name, VALUE (*func)(), int argc) ::
+rb_define_singleton_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc) ::
Defines a singleton method. Arguments are same as rb_define_method().
@@ -1431,10 +1446,9 @@ int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optiona
+optional+ is negative) number of IDs are optional. If a
mandatory key is not contained in +keyword_hash+, raises "missing
keyword" +ArgumentError+. If an optional key is not present in
- +keyword_hash+, the corresponding element in +values+ is not changed.
- If +optional+ is negative, rest of +keyword_hash+ are stored in the
- next to optional +values+ as a new Hash, otherwise raises "unknown
- keyword" +ArgumentError+.
+ +keyword_hash+, the corresponding element in +values+ is set to +Qundef+.
+ If +optional+ is negative, rest of +keyword_hash+ are ignored, otherwise
+ raises "unknown keyword" +ArgumentError+.
Be warned, handling keyword arguments in the C API is less efficient
than handling them in Ruby. Consider using a Ruby wrapper method
@@ -1448,7 +1462,7 @@ VALUE rb_extract_keywords(VALUE *original_hash) ::
non-symbol keys, then they are copied to another hash and the new hash
is stored through +original_hash+, else 0 is stored.
-== Invoking Ruby method
+=== Invoking Ruby method
VALUE rb_funcall(VALUE recv, ID mid, int narg, ...) ::
@@ -1486,7 +1500,7 @@ int rb_respond_to(VALUE obj, ID id) ::
Returns true if the object responds to the message specified by id.
-== Instance Variables
+=== Instance Variables
VALUE rb_iv_get(VALUE obj, const char *name) ::
@@ -1497,7 +1511,7 @@ VALUE rb_iv_set(VALUE obj, const char *name, VALUE val) ::
Sets the value of the instance variable.
-== Control Structure
+=== Control Structure
VALUE rb_block_call(VALUE recv, ID mid, int argc, VALUE * argv, VALUE (*func) (ANYARGS), VALUE data2) ::
@@ -1563,7 +1577,7 @@ void rb_iter_break_value(VALUE value) ::
return the given argument value. This function never return to the
caller.
-== Exceptions and Errors
+=== Exceptions and Errors
void rb_warn(const char *fmt, ...) ::
@@ -1598,7 +1612,97 @@ Note: In the format string, "%"PRIsVALUE can be used for Object#to_s
must be a VALUE). Since it conflicts with "%i", for integers in
format strings, use "%d".
-== Initialize and Start the Interpreter
+=== Threading
+
+As of Ruby 1.9, Ruby supports native 1:1 threading with one kernel
+thread per Ruby Thread object. Currently, there is a GVL (Global VM Lock)
+which prevents simultaneous execution of Ruby code which may be released
+by the rb_thread_call_without_gvl and rb_thread_call_without_gvl2 functions.
+These functions are tricky-to-use and documented in thread.c; do not
+use them before reading comments in thread.c.
+
+void rb_thread_schedule(void) ::
+
+ Give the scheduler a hint to pass execution to another thread.
+
+=== Input/Output (IO) on a single file descriptor
+
+int rb_io_wait_readable(int fd) ::
+
+ Wait indefinitely for the given FD to become readable, allowing other
+ threads to be scheduled. Returns a true value if a read may be
+ performed, false if there is an unrecoverable error.
+
+int rb_io_wait_writable(int fd) ::
+
+ Like rb_io_wait_readable, but for writability.
+
+int rb_wait_for_single_fd(int fd, int events, struct timeval *timeout) ::
+
+ Allows waiting on a single FD for one or multiple events with a
+ specified timeout.
+
+ +events+ is a mask of any combination of the following values:
+
+ * RB_WAITFD_IN - wait for readability of normal data
+ * RB_WAITFD_OUT - wait for writability
+ * RB_WAITFD_PRI - wait for readability of urgent data
+
+ Use a NULL +timeout+ to wait indefinitely.
+
+=== I/O Multiplexing
+
+Ruby supports I/O multiplexing based on the select(2) system call.
+The Linux select_tut(2) manpage
+<http://man7.org/linux/man-pages/man2/select_tut.2.html>
+provides a good overview on how to use select(2), and the Ruby API has
+analogous functions and data structures to the well-known select API.
+Understanding of select(2) is required to understand this section.
+
+typedef struct rb_fdset_t ::
+
+ The data structure which wraps the fd_set bitmap used by select(2).
+ This allows Ruby to use FD sets larger than that allowed by
+ historic limitations on modern platforms.
+
+void rb_fd_init(rb_fdset_t *) ::
+
+ Initializes the rb_fdset_t, it must be initialized before other rb_fd_*
+ operations. Analogous to calling malloc(3) to allocate an fd_set.
+
+void rb_fd_term(rb_fdset_t *) ::
+
+ Destroys the rb_fdset_t, releasing any memory and resources it used.
+ It must be reinitialized using rb_fd_init before future use.
+ Analogous to calling free(3) to release memory for an fd_set.
+
+void rb_fd_zero(rb_fdset_t *) ::
+
+ Clears all FDs from the rb_fdset_t, analogous to FD_ZERO(3).
+
+void rb_fd_set(int fd, rb_fdset_t *) ::
+
+ Adds a given FD in the rb_fdset_t, analogous to FD_SET(3).
+
+void rb_fd_clr(int fd, rb_fdset_t *) ::
+
+ Removes a given FD from the rb_fdset_t, analogous to FD_CLR(3).
+
+int rb_fd_isset(int fd, const rb_fdset_t *) ::
+
+ Returns true if a given FD is set in the rb_fdset_t, false if not.
+ Analogous to FD_ISSET(3).
+
+int rb_thread_fd_select(int nfds, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, struct timeval *timeout) ::
+
+ Analogous to the select(2) system call, but allows other Ruby
+ threads to be scheduled while waiting.
+
+ When only waiting on a single FD, favor rb_io_wait_readable,
+ rb_io_wait_writable, or rb_wait_for_single_fd functions since
+ they can be optimized for specific platforms (currently, only Linux).
+
+=== Initialize and Start the Interpreter
The embedding API functions are below (not needed for extension libraries):
@@ -1623,7 +1727,7 @@ void ruby_script(char *name) ::
Specifies the name of the script ($0).
-== Hooks for the Interpreter Events
+=== Hooks for the Interpreter Events
void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data) ::
@@ -1653,7 +1757,7 @@ int rb_remove_event_hook(rb_event_hook_func_t func) ::
Removes the specified hook function.
-== Memory usage
+=== Memory usage
void rb_gc_adjust_memory_usage(ssize_t diff) ::
@@ -1665,7 +1769,7 @@ void rb_gc_adjust_memory_usage(ssize_t diff) ::
is decreased; a memory block is freed or a block is reallocated as
smaller size. This function may trigger the GC.
-== Macros for Compatibility
+=== Macros for Compatibility
Some macros to check API compatibilities are available by default.
@@ -1705,25 +1809,25 @@ RB_EVENT_HOOKS_HAVE_CALLBACK_DATA ::
Means that rb_add_event_hook() takes the third argument `data', to be
passed to the given event hook function.
-= Appendix C. Functions available for use in extconf.rb
+== Appendix C. Functions available for use in extconf.rb
See documentation for {mkmf}[rdoc-ref:MakeMakefile].
-= Appendix D. Generational GC
+== Appendix D. Generational GC
Ruby 2.1 introduced a generational garbage collector (called RGenGC).
RGenGC (mostly) keeps compatibility.
Generally, the use of the technique called write barriers is required in
extension libraries for generational GC
-(http://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29).
+(https://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29).
RGenGC works fine without write barriers in extension libraries.
If your library adheres to the following tips, performance can
be further improved. Especially, the "Don't touch pointers directly" section is
important.
-== Incompatibility
+=== Incompatibility
You can't write RBASIC(obj)->klass field directly because it is const
value now.
@@ -1742,13 +1846,13 @@ VALUE rb_obj_reveal(VALUE obj, VALUE klass) ::
Reset RBasic::klass to be klass.
We expect the `klass' is hidden class by rb_obj_hide().
-== Write barriers
+=== Write barriers
RGenGC doesn't require write barriers to support generational GC.
However, caring about write barrier can improve the performance of
RGenGC. Please check the following tips.
-=== Don't touch pointers directly
+==== Don't touch pointers directly
In MRI (include/ruby/ruby.h), some macros to acquire pointers to the
internal data structures are supported such as RARRAY_PTR(),
@@ -1757,7 +1861,7 @@ RSTRUCT_PTR() and so on.
DO NOT USE THESE MACROS and instead use the corresponding C-APIs such as
rb_ary_aref(), rb_ary_store() and so on.
-=== Consider whether to insert write barriers
+==== Consider whether to insert write barriers
You don't need to care about write barriers if you only use built-in
types.
@@ -1779,7 +1883,7 @@ introduce critical bugs. And inserting write barriers has several areas
of overhead. Basically we don't recommend you insert write barriers.
Please carefully consider the risks.
-=== Combine with built-in types
+==== Combine with built-in types
Please consider utilizing built-in types. Most built-in types support
write barrier, so you can use them to avoid manually inserting write
@@ -1794,7 +1898,7 @@ references.
With use of such techniques, you don't need to insert write barriers
anymore.
-=== Insert write barriers
+==== Insert write barriers
\[AGAIN] Inserting write barriers is a very difficult hack, and it is
easy to introduce critical bugs. And inserting write barriers has
@@ -1808,7 +1912,7 @@ available in include/ruby/ruby.h. An example is available in iseq.c.
For a complete guide for RGenGC and write barriers, please refer to
<https://bugs.ruby-lang.org/projects/ruby-trunk/wiki/RGenGC>.
-= Appendix E. RB_GC_GUARD to protect from premature GC
+== Appendix E. RB_GC_GUARD to protect from premature GC
C Ruby currently uses conservative garbage collection, thus VALUE
variables must remain visible on the stack or registers to ensure any
diff --git a/doc/globals.rdoc b/doc/globals.rdoc
index 3bc7e05623..1e70555988 100644
--- a/doc/globals.rdoc
+++ b/doc/globals.rdoc
@@ -10,7 +10,7 @@ $':: The string to the right of the last successful match.
$+:: The highest group matched by the last successful match.
$1:: The Nth group of the last successful match. May be > 1.
$~:: The information about the last match in the current scope.
-$=:: The flag for case insensitive, nil by default.
+$=:: This variable is no longer effective. Deprecated.
$/:: The input record separator, newline by default.
$\:: The output record separator for the print and IO#write. Default is nil.
$,:: The output field separator for the print and Array#join.
diff --git a/doc/irb/irb.rd.ja b/doc/irb/irb.rd.ja
index 416dec3906..85b6536ee4 100644
--- a/doc/irb/irb.rd.ja
+++ b/doc/irb/irb.rd.ja
@@ -83,7 +83,6 @@ irb起動時ã«``~/.irbrc''を読ã¿è¾¼ã¿ã¾ã™. ã‚‚ã—存在ã—ãªã„å ´åˆã¯
ã§ãã¾ã™.
IRB.conf[:IRB_NAME]="irb"
- IRB.conf[:MATH_MODE]=false
IRB.conf[:USE_TRACER]=false
IRB.conf[:USE_LOADER]=false
IRB.conf[:IGNORE_SIGINT]=true
@@ -207,9 +206,6 @@ irb拡張コマンドã¯, ç°¡å˜ãªåå‰ã¨é ­ã«`irb_'ã‚’ã¤ã‘ãŸåå‰ã¨ä¸¡
nil: 通常モードã§ã‚れã°, inspect modeã¨ãªã‚Š, mathãƒ¢ãƒ¼ãƒ‰ã®æ™‚ã¯, non
inspect modeã¨ãªã‚‹.
---- conf.math_mode
- å‚ç…§ã®ã¿. bcモード(分数, 行列ã®è¨ˆç®—ãŒã§ãã¾ã™)ã‹ã©ã†ã‹?
-
--- conf.use_loader = true/false
load/require時ã«irbã®file読ã¿è¾¼ã¿æ©Ÿèƒ½ã‚’用ã„るモードã®ã‚¹ã‚¤ãƒƒãƒ(デフォ
ルトã¯ç”¨ã„ãªã„). ã“ã®ãƒ¢ãƒ¼ãƒ‰ã¯IRB全体ã«å映ã•れる.
@@ -409,4 +405,3 @@ rubyã§ã¯, 以下ã®ãƒ—ログラムã¯ã‚¨ãƒ©ãƒ¼ã«ãªã‚Šã¾ã™.
% comment-end: "\n"
% End:
%
-
diff --git a/doc/maintainers.rdoc b/doc/maintainers.rdoc
index d22d09f7a4..e7baf933c6 100644
--- a/doc/maintainers.rdoc
+++ b/doc/maintainers.rdoc
@@ -34,7 +34,9 @@ Yukihiro Matsumoto (matz)
Zachary Scott (zzak)
-== Library Maintainers
+== Standard Library Maintainers
+
+=== Libraries
[lib/English.rb]
_unmaintained_
@@ -46,44 +48,22 @@ Zachary Scott (zzak)
_unmaintained_
[lib/cgi.rb, lib/cgi/*]
Takeyuki Fujioka (xibbar)
-[lib/cmath.rb]
- _unmaintained_
-[lib/csv.rb]
- James Edward Gray II (jeg2)
[lib/drb.rb, lib/drb/*]
Masatoshi SEKI (seki)
[lib/debug.rb]
_unmaintained_
[lib/delegate.rb]
_unmaintained_
-[lib/e2mmap.rb]
- Keiju ISHITSUKA (keiju)
[lib/erb.rb]
- Masatoshi SEKI (seki)
-[lib/fileutils.rb]
- _unmaintained_
+ Masatoshi SEKI (seki), Takashi Kokubun (k0kubun)
[lib/find.rb]
Kazuki Tsujimoto (ktsj)
-[lib/forwardable.rb]
- Keiju ISHITSUKA (keiju)
[lib/getoptlong.rb]
_unmaintained_
-[lib/ipaddr.rb]
- Akinori MUSHA (knu)
-[lib/irb.rb, lib/irb/*]
- Keiju ISHITSUKA (keiju)
-[lib/logger.rb]
- Naotoshi Seo (sonots)
-[lib/mathn.rb]
- Keiju ISHITSUKA (keiju)
-[lib/matrix.rb]
- Marc-Andre Lafortune (marcandre)
[lib/mkmf.rb]
_unmaintained_
[lib/monitor.rb]
Shugo Maeda (shugo)
-[lib/mutex_m.rb]
- Keiju ISHITSUKA (keiju)
[lib/net/ftp.rb]
Shugo Maeda (shugo)
[lib/net/imap.rb]
@@ -104,14 +84,10 @@ Zachary Scott (zzak)
_unmaintained_
[lib/optparse.rb, lib/optparse/*]
Nobuyuki Nakada (nobu)
-[lib/ostruct.rb]
- Marc-Andre Lafortune (marcandre)
[lib/pp.rb]
Tanaka Akira (akr)
[lib/prettyprint.rb]
Tanaka Akira (akr)
-[lib/prime.rb]
- Yuki Sonoda (yugui)
[lib/profile.rb]
_unmaintained_
[lib/profiler.rb]
@@ -120,48 +96,31 @@ Zachary Scott (zzak)
_unmaintained_
[lib/racc/*]
Aaron Patterson (tenderlove)
-[lib/rbconfig/*]
- _unmaintained_
-[lib/rdoc.rb, lib/rdoc/*]
- Eric Hodel (drbrain)
[lib/resolv-replace.rb]
Tanaka Akira (akr)
[lib/resolv.rb]
Tanaka Akira (akr)
-[lib/rexml/*]
- Kouhei Sutou (kou)
[lib/rinda/*]
Masatoshi SEKI (seki)
-[lib/rss.rb, lib/rss/*]
- Kouhei Sutou (kou)
[lib/rubygems.rb, lib/ubygems.rb, lib/rubygems/*]
- Eric Hodel (drbrain)
-[lib/scanf.rb]
- David A. Black (dblack)
+ Eric Hodel (drbrain), Hiroshi SHIBATA (hsbt)
+ https://github.com/rubygems/rubygems
[lib/set.rb]
Akinori MUSHA (knu)
[lib/securerandom.rb]
Tanaka Akira (akr)
-[lib/shell.rb, lib/shell/*]
- Keiju ISHITSUKA (keiju)
[lib/shellwords.rb]
Akinori MUSHA (knu)
[lib/singleton.rb]
Yukihiro Matsumoto (matz)
-[lib/sync.rb]
- Keiju ISHITSUKA (keiju)
[lib/tempfile.rb]
_unmaintained_
[lib/tmpdir.rb]
_unmaintained_
-[lib/thwait.rb]
- Keiju ISHITSUKA (keiju)
[lib/time.rb]
Tanaka Akira (akr)
[lib/timeout.rb]
Yukihiro Matsumoto (matz)
-[lib/tracer.rb]
- Keiju ISHITSUKA (keiju)
[lib/tsort.rb]
Tanaka Akira (akr)
[lib/un.rb]
@@ -172,59 +131,31 @@ Zachary Scott (zzak)
YAMADA, Akira (akira)
[lib/weakref.rb]
_unmaintained_
-[lib/webrick.rb, lib/webrick/*]
- Hiroshi Nakamura (nahi)
[lib/yaml.rb, lib/yaml/*]
- Aaron Patterson (tenderlove)
+ Aaron Patterson (tenderlove), Hiroshi SHIBATA (hsbt)
-== Extension Maintainers
+=== Extensions
-[ext/bigdecimal]
- Kenta Murata (mrkn)
[ext/cgi]
Nobuyoshi Nakada (nobu)
[ext/continuation]
Koichi Sasada (ko1)
[ext/coverage]
Yusuke Endoh (mame)
-[ext/date]
- _unmaintained_
-[ext/dbm]
- _unmaintained_
[ext/digest, ext/digest/*]
Akinori MUSHA (knu)
-[ext/etc]
- _unmaintained_
-[ext/fcntl]
- _unmaintained_
[ext/fiber]
Koichi Sasada (ko1)
-[ext/fiddle]
- Aaron Patterson (tenderlove)
-[ext/gdbm]
- Yukihiro Matsumoto (matz)
-[ext/io/console]
- Nobuyuki Nakada (nobu)
[ext/io/nonblock]
Nobuyuki Nakada (nobu)
[ext/io/wait]
Nobuyuki Nakada (nobu)
-[ext/json]
- NARUSE, Yui (naruse)
-[ext/mathn/complex]
- Keiju ISHITSUKA (keiju)
-[ext/mathn/rational]
- Keiju ISHITSUKA (keiju)
[ext/nkf]
NARUSE, Yui (narse)
[ext/objspace]
_unmaintained_
-[ext/openssl]
- Kazuki Yamaguchi (rhe)
[ext/pathname]
Tanaka Akira (akr)
-[ext/psych]
- Aaron Patterson (tenderlove)
[ext/pty]
_unmaintained_
[ext/racc]
@@ -233,20 +164,133 @@ Zachary Scott (zzak)
TAKAO Kouji (kouji)
[ext/ripper]
_unmaintained_
-[ext/sdbm]
- Yukihiro Matsumoto (matz)
[ext/socket]
* Tanaka Akira (akr)
* API change needs matz's approval
-[ext/stringio]
- Nobuyuki Nakada (nobu)
-[ext/strscan]
- _unmaintained_
[ext/syslog]
Akinori MUSHA (knu)
[ext/win32]
NAKAMURA Usaku (usa)
[ext/win32ole]
Masaki Suketa (suke)
-[ext/zlib]
+
+== Default gems Maintainers
+
+=== Libraries
+
+[lib/bundler.rb, lib/bundler/*]
+ Hiroshi SHIBATA (hsbt)
+ https://github.com/bundler/bundler
+[lib/cmath.rb]
_unmaintained_
+ https://github.com/ruby/cmath
+[lib/csv.rb]
+ Kenta Murata (mrkn), Kouhei Sutou (kou)
+ https://github.com/ruby/csv
+[lib/e2mmap.rb]
+ Keiju ISHITSUKA (keiju)
+[lib/fileutils.rb]
+ _unmaintained_
+ https://github.com/ruby/fileutils
+[lib/forwardable.rb]
+ Keiju ISHITSUKA (keiju)
+[lib/ipaddr.rb]
+ Akinori MUSHA (knu)
+[lib/irb.rb, lib/irb/*]
+ Keiju ISHITSUKA (keiju)
+[lib/logger.rb]
+ Naotoshi Seo (sonots)
+[lib/matrix.rb]
+ Marc-Andre Lafortune (marcandre)
+[lib/mutex_m.rb]
+ Keiju ISHITSUKA (keiju)
+[lib/ostruct.rb]
+ Marc-Andre Lafortune (marcandre)
+[lib/prime.rb]
+ Yuki Sonoda (yugui)
+[lib/rdoc.rb, lib/rdoc/*]
+ Eric Hodel (drbrain), Hiroshi SHIBATA (hsbt)
+ https://github.com/ruby/rdoc
+[lib/rexml/*]
+ Kouhei Sutou (kou)
+[lib/rss.rb, lib/rss/*]
+ Kouhei Sutou (kou)
+[lib/scanf.rb]
+ David A. Black (dblack)
+ https://github.com/ruby/scanf
+[lib/shell.rb, lib/shell/*]
+ Keiju ISHITSUKA (keiju)
+[lib/sync.rb]
+ Keiju ISHITSUKA (keiju)
+[lib/thwait.rb]
+ Keiju ISHITSUKA (keiju)
+[lib/tracer.rb]
+ Keiju ISHITSUKA (keiju)
+[lib/webrick.rb, lib/webrick/*]
+ Eric Wong (normalperson)
+ https://bugs.ruby-lang.org/
+
+=== Extensions
+
+[ext/bigdecimal]
+ Kenta Murata (mrkn)
+ https://github.com/ruby/bigdecimal
+[ext/date]
+ _unmaintained_
+ https://github.com/ruby/date
+[ext/dbm]
+ _unmaintained_
+ https://github.com/ruby/dbm
+[ext/etc]
+ Ruby core team
+ https://github.com/ruby/etc
+[ext/fcntl]
+ Ruby core team
+ https://github.com/ruby/fcntl
+[ext/fiddle]
+ Aaron Patterson (tenderlove)
+ https://github.com/ruby/fiddle
+[ext/gdbm]
+ Yukihiro Matsumoto (matz)
+ https://github.com/ruby/gdbm
+[ext/io/console]
+ Nobuyuki Nakada (nobu)
+ https://github.com/ruby/io-console
+[ext/json]
+ NARUSE, Yui (naruse), Hiroshi SHIBATA (hsbt)
+ https://github.com/flori/json
+[ext/openssl]
+ Kazuki Yamaguchi (rhe)
+ https://github.com/ruby/openssl
+[ext/psych]
+ Aaron Patterson (tenderlove), Hiroshi SHIBATA(hsbt)
+ https://github.com/ruby/psych
+[ext/sdbm]
+ Yukihiro Matsumoto (matz)
+ https://github.com/ruby/sdbm
+[ext/stringio]
+ Nobuyuki Nakada (nobu)
+ https://github.com/ruby/stringio
+[ext/strscan]
+ _unmaintained_
+ https://github.com/ruby/strscan
+[ext/zlib]
+ NARUSE, Yui (naruse)
+ https://github.com/ruby/zlib
+
+== Bundled gems upstream repositories
+
+[did_you_mean]
+ https://github.com/yuki24/did_you_mean
+[minitest]
+ https://github.com/seattlerb/minitest
+[net-telnet]
+ https://github.com/ruby/net-telnet
+[power_assert]
+ https://github.com/k-tsj/power_assert
+[rake]
+ https://github.com/ruby/rake
+[test-unit]
+ https://github.com/test-unit/test-unit
+[xmlrpc]
+ https://github.com/ruby/xmlrpc
diff --git a/doc/regexp.rdoc b/doc/regexp.rdoc
index 49fce51a8d..9218a75b67 100644
--- a/doc/regexp.rdoc
+++ b/doc/regexp.rdoc
@@ -63,9 +63,10 @@ The following are <i>metacharacters</i> <tt>(</tt>, <tt>)</tt>,
<tt>[</tt>, <tt>]</tt>, <tt>{</tt>, <tt>}</tt>, <tt>.</tt>, <tt>?</tt>,
<tt>+</tt>, <tt>*</tt>. They have a specific meaning when appearing in a
pattern. To match them literally they must be backslash-escaped. To match
-a backslash literally backslash-escape that: <tt>\\\\\\</tt>.
+a backslash literally, backslash-escape it: <tt>\\\\</tt>.
/1 \+ 2 = 3\?/.match('Does 1 + 2 = 3?') #=> #<MatchData "1 + 2 = 3?">
+ /a\\\\b/.match('a\\\\b') #=> #<MatchData "a\\b">
Patterns behave like double-quoted strings so can contain the same
backslash escapes.
@@ -227,7 +228,7 @@ Capture groups can be referred to by name when defined with the
constructs.
/\$(?<dollars>\d+)\.(?<cents>\d+)/.match("$3.67")
- => #<MatchData "$3.67" dollars:"3" cents:"67">
+ #=> #<MatchData "$3.67" dollars:"3" cents:"67">
/\$(?<dollars>\d+)\.(?<cents>\d+)/.match("$3.67")[:dollars] #=> "3"
Named groups can be backreferenced with <tt>\k<</tt><i>name</i><tt>></tt>,
@@ -540,10 +541,15 @@ options which control how the pattern can match.
subexpression level with the
<tt>(?</tt><i>on</i><tt>-</tt><i>off</i><tt>)</tt> construct, which
enables options <i>on</i>, and disables options <i>off</i> for the
-expression enclosed by the parentheses.
+expression enclosed by the parentheses:
- /a(?i:b)c/.match('aBc') #=> #<MatchData "aBc">
- /a(?i:b)c/.match('abc') #=> #<MatchData "abc">
+ /a(?i:b)c/.match('aBc') #=> #<MatchData "aBc">
+ /a(?-i:b)c/i.match('ABC') #=> nil
+
+Additionally, these options can also be toggled for the remainder of the
+pattern:
+
+ /a(?i)bc/.match('abC') #=> #<MatchData "abC">
Options may also be used with <tt>Regexp.new</tt>:
@@ -607,9 +613,9 @@ regexp's encoding can be explicitly fixed by supplying
<tt>Regexp.new</tt>:
r = Regexp.new("a".force_encoding("iso-8859-1"),Regexp::FIXEDENCODING)
- r =~"a\u3042"
- #=> Encoding::CompatibilityError: incompatible encoding regexp match
- (ISO-8859-1 regexp with UTF-8 string)
+ r =~ "a\u3042"
+ # raises Encoding::CompatibilityError: incompatible encoding regexp match
+ # (ISO-8859-1 regexp with UTF-8 string)
== Special global variables
diff --git a/doc/signals.rdoc b/doc/signals.rdoc
new file mode 100644
index 0000000000..fdd9206afb
--- /dev/null
+++ b/doc/signals.rdoc
@@ -0,0 +1,106 @@
+= Caveats for implementing Signal.trap callbacks
+
+As with implementing signal handlers in C or most other languages,
+all code passed to Signal.trap must be reentrant. If you are not
+familiar with reentrancy, you need to read up on it at
+{Wikipedia}[https://en.wikipedia.org/wiki/Reentrancy_(computing)] or
+elsewhere before reading the rest of this document.
+
+Most importantly, "thread-safety" does not guarantee reentrancy;
+and methods such as Mutex#lock and Mutex#synchronize which are
+commonly used for thread-safety even prevent reentrancy.
+
+== An implementation detail of the Ruby VM
+
+The Ruby VM defers Signal.trap callbacks from running until it is safe
+for its internal data structures, but it does not know when it is safe
+for data structures in YOUR code. Ruby implements deferred signal
+handling by registering short C functions with only
+{async-signal-safe functions}[http://man7.org/linux/man-pages/man7/signal-safety.7.html] as
+signal handlers. These short C functions only do enough tell the VM to
+run callbacks registered via Signal.trap later in the main VM loop.
+
+== Unsafe methods to call in Signal.trap blocks
+
+When in doubt, consider anything not listed as safe below as being
+unsafe.
+
+* Mutex#lock, Mutex#synchronize and any code using them are explicitly
+ unsafe. This includes Monitor in the standard library which uses
+ Mutex to provide reentrancy.
+
+* Dir.chdir with block
+
+* any IO write operations when IO#sync is false;
+ including IO#write, IO#write_nonblock, IO#puts.
+ Pipes and sockets default to `IO#sync = true', so it is safe to
+ write to them unless IO#sync was disabled.
+
+* File#flock, as the underlying flock(2) call is not specified by POSIX
+
+== Commonly safe operations inside Signal.trap blocks
+
+* Assignment and retrieval of local, instance, and class variables
+
+* Most object allocations and initializations of common types
+ including Array, Hash, String, Struct, Time.
+
+* Common Array, Hash, String, Struct operations which do not execute a block
+ are generally safe; but beware if iteration is occurring elsewhere.
+
+* Hash#[], Hash#[]= (unless Hash.new was given an unsafe block)
+
+* Thread::Queue#push and Thread::SizedQueue#push (since Ruby 2.1)
+
+* Creating a new Thread via Thread.new/Thread.start can used to get
+ around the unusability of Mutexes inside a signal handler
+
+* Signal.trap is safe to use inside blocks passed to Signal.trap
+
+* arithmetic on Integer and Float (`+', `-', '%', '*', '/')
+
+ Additionally, signal handlers do not run between two successive
+ local variable accesses, so shortcuts such as `+=' and `-=' will
+ not trigger a data race when used on Integer and Float classes in
+ signal handlers.
+
+== System call wrapper methods which are safe inside Signal.trap
+
+Since Ruby has wrappers around many
+{async-signal-safe C functions}[http://man7.org/linux/man-pages/man7/signal-safety.7.html]
+the corresponding wrappers for many IO, File, Dir, and Socket methods
+are safe.
+
+(Incomplete list)
+
+* Dir.chdir (without block arg)
+* Dir.mkdir
+* Dir.open
+* File#truncate
+* File.link
+* File.open
+* File.readlink
+* File.rename
+* File.stat
+* File.symlink
+* File.truncate
+* File.unlink
+* File.utime
+* IO#close
+* IO#dup
+* IO#fsync
+* IO#read
+* IO#read_nonblock
+* IO#stat
+* IO#sysread
+* IO#syswrite
+* IO.select
+* IO.pipe
+* Process.clock_gettime
+* Process.exit!
+* Process.fork
+* Process.kill
+* Process.pid
+* Process.ppid
+* Process.waitpid
+...
diff --git a/doc/standard_library.rdoc b/doc/standard_library.rdoc
index d97e09e977..09dc61bc52 100644
--- a/doc/standard_library.rdoc
+++ b/doc/standard_library.rdoc
@@ -12,27 +12,15 @@ Abbrev:: Calculates a set of unique abbreviations for a given set of strings
Base64:: Support for encoding and decoding binary data using a Base64 representation
Benchmark:: Provides methods to measure and report the time used to execute code
CGI:: Support for the Common Gateway Interface protocol
-CMath:: Provides Trigonometric and Transcendental functions for complex numbers
-ConditionVariable:: Augments the Mutex class
-CSV:: Provides an interface to read and write CSV files and data
DEBUGGER__:: Debugging functionality for Ruby
Delegator:: Provides three abilities to delegate method calls to an object
DRb:: Distributed object system for Ruby
-E2MM:: Module for defining custom exceptions with specific messages
English.rb:: Require 'English.rb' to reference global variables with less cryptic names
ERB:: An easy to use but powerful templating system for Ruby
-FileUtils:: Several file utility methods for copying, moving, removing, etc
Find:: This module supports top-down traversal of a set of file paths
-Forwardable:: Provides delegation of specified methods to a designated object
GetoptLong:: Parse command line options similar to the GNU C getopt_long()
-IPAddr:: Provides methods to manipulate IPv4 and IPv6 IP addresses
-IRB:: Interactive Ruby command-line tool for REPL (Read Eval Print Loop)
-Logger:: Provides a simple logging utility for outputting messages
-mathn.rb:: Deprecated library that extends math operations
MakeMakefile:: Module used to generate a Makefile for C extensions
-Matrix:: Represents a mathematical matrix.
Monitor:: Provides an object or module to use safely by more than one thread
-Mutex_m:: Mixin to extend objects to be handled like a Mutex
Net::FTP:: Support for the File Transfer Protocol
Net::HTTP:: HTTP client api for Ruby
Net::IMAP:: Ruby client api for Internet Message Access Protocol
@@ -42,71 +30,100 @@ Observable:: Provides a mechanism for publish/subscribe pattern in Ruby
OpenURI:: An easy-to-use wrapper for Net::HTTP, Net::HTTPS and Net::FTP
Open3:: Provides access to stdin, stdout and stderr when running other programs
OptionParser:: Ruby-oriented class for command-line option analysis
-OpenStruct:: Class to build custom data structures, similar to a Hash
PP:: Provides a PrettyPrinter for Ruby objects
PrettyPrinter:: Implements a pretty printing algorithm for readable structure
-Prime:: Prime numbers and factorization library
profile.rb:: Runs the Ruby Profiler__
Profiler__:: Provides a way to profile your Ruby application
PStore:: Implements a file based persistence mechanism based on a Hash
-Queue:: Synchronized communication between threads
Racc:: A LALR(1) parser generator written in Ruby.
RbConfig:: Information of your configure and build of Ruby
-RDoc:: Produces HTML and command-line documentation for Ruby
resolv-replace.rb:: Replace Socket DNS with Resolv
Resolv:: Thread-aware DNS resolver library in Ruby
-REXML:: An XML toolkit for Ruby
Rinda:: The Linda distributed computing paradigm in Ruby
-RSS:: Family of libraries that support various formats of XML "feeds"
Gem:: Package management framework for Ruby
-Scanf:: A Ruby implementation of the C function scanf(3)
SecureRandom:: Interface for secure random number generator
Set:: Provides a class to deal with collections of unordered, unique values
-Shell:: An idiomatic Ruby interface for common UNIX shell commands
Shellwords:: Manipulates strings with word parsing rules of UNIX Bourne shell
Singleton:: Implementation of the Singleton pattern for Ruby
-Synchronizer:: A module that provides a two-phase lock with a counter
Tempfile:: A utility class for managing temporary files
-ThreadsWait:: Watches for termination of multiple threads
Time:: Extends the Time class with methods for parsing and conversion
Timeout:: Auto-terminate potentially long-running operations in Ruby
tmpdir.rb:: Extends the Dir class to manage the OS temporary file path
-Tracer:: Outputs a source level execution trace of a Ruby program
TSort:: Topological sorting using Tarjan's algorithm
un.rb:: Utilities to replace common UNIX commands
URI:: A Ruby module providing support for Uniform Resource Identifiers
WeakRef:: Allows a referenced object to be garbage-collected
-WEBrick:: An HTTP server toolkit for Ruby
-XMLRPC:: Remote Procedure Call over HTTP support for Ruby
YAML:: Ruby client library for the Psych YAML implementation
== Extensions
-BigDecimal:: Provides arbitrary-precision floating point decimal arithmetic
Coverage:: Provides coverage measurement for Ruby
+Digest:: Provides a framework for message digest libraries
+IO:: Extensions for Ruby IO class, including #wait and ::console
+NKF:: Ruby extension for Network Kanji Filter
+objspace:: Extends ObjectSpace module to add methods for internal statistics
+Pathname:: Representation of the name of a file or directory on the filesystem
+PTY:: Creates and manages pseudo terminals
+Readline:: Provides an interface for GNU Readline and Edit Line (libedit)
+Ripper:: Provides an interface for parsing Ruby programs into S-expressions
+Socket:: Access underlying OS socket implementations
+Syslog:: Ruby interface for the POSIX system logging facility
+WIN32OLE:: Provides an interface for OLE Automation in Ruby
+
+= Default gems
+
+== Libraries
+
+Bundler:: Manage your Ruby application's gem dependencies
+CMath:: Provides Trigonometric and Transcendental functions for complex numbers
+CSV:: Provides an interface to read and write CSV files and data
+E2MM:: Module for defining custom exceptions with specific messages
+FileUtils:: Several file utility methods for copying, moving, removing, etc
+Forwardable:: Provides delegation of specified methods to a designated object
+IPAddr:: Provides methods to manipulate IPv4 and IPv6 IP addresses
+IRB:: Interactive Ruby command-line tool for REPL (Read Eval Print Loop)
+Logger:: Provides a simple logging utility for outputting messages
+Matrix:: Represents a mathematical matrix.
+Mutex_m:: Mixin to extend objects to be handled like a Mutex
+OpenStruct:: Class to build custom data structures, similar to a Hash
+Prime:: Prime numbers and factorization library
+RDoc:: Produces HTML and command-line documentation for Ruby
+REXML:: An XML toolkit for Ruby
+RSS:: Family of libraries that support various formats of XML "feeds"
+Scanf:: A Ruby implementation of the C function scanf(3)
+Shell:: An idiomatic Ruby interface for common UNIX shell commands
+Synchronizer:: A module that provides a two-phase lock with a counter
+ThreadsWait:: Watches for termination of multiple threads
+Tracer:: Outputs a source level execution trace of a Ruby program
+WEBrick:: An HTTP server toolkit for Ruby
+
+== Extensions
+
+BigDecimal:: Provides arbitrary-precision floating point decimal arithmetic
Date:: A subclass of Object includes Comparable module for handling dates
DateTime:: Subclass of Date to handling dates, hours, minutes, seconds, offsets
DBM:: Provides a wrapper for the UNIX-style Database Manager Library
-Digest:: Provides a framework for message digest libraries
Etc:: Provides access to information typically stored in UNIX /etc directory
Fcntl:: Loads constants defined in the OS fcntl.h C header file
Fiddle:: A libffi wrapper for Ruby
GDBM:: Ruby extension for the GNU dbm (gdbm) library
-IO:: Extensions for Ruby IO class, including #wait and ::console
+IO::console:: Console interface
JSON:: Implements Javascript Object Notation for Ruby
-NKF:: Ruby extension for Network Kanji Filter
-objspace:: Extends ObjectSpace module to add methods for internal statistics
OpenSSL:: Provides SSL, TLS and general purpose cryptography for Ruby
-Pathname:: Representation of the name of a file or directory on the filesystem
Psych:: A YAML parser and emitter for Ruby
-PTY:: Creates and manages pseudo terminals
-Readline:: Provides an interface for GNU Readline and Edit Line (libedit)
-Ripper:: Provides an interface for parsing Ruby programs into S-expressions
SDBM:: Provides a simple file-based key-value store with String keys and values
-Socket:: Access underlying OS socket implementations
StringIO:: Pseudo I/O on String objects
StringScanner:: Provides lexical scanning operations on a String
-Syslog:: Ruby interface for the POSIX system logging facility
-Tk:: Provides a framework for building a Graphical User Interface (GUI)
-WIN32OLE:: Provides an interface for OLE Automation in Ruby
Zlib:: Ruby interface for the zlib compression/decompression library
+
+= Bundled gems
+
+== Libraries
+
+DidYouMean:: "Did you mean?" experience in Ruby
+MiniTest:: A test suite with TDD, BDD, mocking and benchmarking
+Net::Telnet:: Telnet client library for Ruby
+PowerAssert:: Power Assert for Ruby.
+Rake:: Ruby build program with capabilities similar to make
+Test::Unit:: A compatibility layer for MiniTest
+XMLRPC:: Remote Procedure Call over HTTP support for Ruby
diff --git a/doc/syntax/calling_methods.rdoc b/doc/syntax/calling_methods.rdoc
index ec86ef05ee..b86d60ad88 100644
--- a/doc/syntax/calling_methods.rdoc
+++ b/doc/syntax/calling_methods.rdoc
@@ -27,13 +27,32 @@ This sends the +my_method+ message to +my_object+. Any object can be a
receiver but depending on the method's visibility sending a message may raise a
NoMethodError.
-You may use <code>&.</code> to designate a receiver, then +my_method+ is not
-invoked and the result is +nil+ when the receiver is +nil+. In that case, the
-arguments of +my_method+ are not evaluated.
-
You may also use <code>::</code> to designate a receiver, but this is rarely
used due to the potential for confusion with <code>::</code> for namespaces.
+=== Safe navigation operator
+
+<code>&.</code>, called "safe navigation operator", allows to skip method call
+when receiver is +nil+. It returns +nil+ and doesn't evaluate method's arguments
+if the call is skipped.
+
+ REGEX = /(ruby) is (\w+)/i
+ "Ruby is awesome!".match(REGEX).values_at(1, 2)
+ # => ["Ruby", "awesome"]
+ "Python is fascinating!".match(REGEX).values_at(1, 2)
+ # NoMethodError: undefined method `values_at' for nil:NilClass
+ "Python is fascinating!".match(REGEX)&.values_at(1, 2)
+ # => nil
+
+This allows to easily chain methods which could return empty value. Note that
+<code>&.</code> skips only one next call, so for a longer chain it is necessary
+to add operator on each level:
+
+ "Python is fascinating!".match(REGEX)&.values_at(1, 2).join(' - ')
+ # NoMethodError: undefined method `join' for nil:NilClass
+ "Python is fascinating!".match(REGEX)&.values_at(1, 2)&.join(' - ')
+ # => nil
+
== Arguments
There are three types of arguments when sending a message, the positional
diff --git a/doc/syntax/literals.rdoc b/doc/syntax/literals.rdoc
index f5b9738868..08eefd21de 100644
--- a/doc/syntax/literals.rdoc
+++ b/doc/syntax/literals.rdoc
@@ -71,6 +71,33 @@ Examples:
All these numbers have the same decimal value, 170. Like integers and floats
you may use an underscore for readability.
+=== Rational numbers
+
+Numbers suffixed by +r+ are Rational numbers.
+
+ 12r #=> (12/1)
+ 12.3r #=> (123/10)
+
+Rational numbers are exact, whereas Float numbers are inexact.
+
+ 0.1r + 0.2r #=> (3/10)
+ 0.1 + 0.2 #=> 0.30000000000000004
+
+=== Complex numbers
+
+Numbers suffixed by +i+ are Complex (or imaginary) numbers.
+
+ 1i #=> (0+1i)
+ 1i * 1i #=> (-1+0i)
+
+Also Rational numbers may be imaginary numbers.
+
+ 12.3ri #=> (0+(123/10)*i)
+
++i+ must be placed after +r+, the opposite is not allowed.
+
+ 12.3ir #=> syntax error
+
== Strings
The most common way of writing strings is using <tt>"</tt>:
@@ -108,7 +135,7 @@ sequences are as follows:
\c\M-x same as above
\c? or \C-? delete, ASCII 7Fh (DEL)
-Any other character followed by a backslash is interpreted as the
+Any other character following a backslash is interpreted as the
character itself.
Double-quote strings allow interpolation of other values using
@@ -306,6 +333,7 @@ its ending value.
(1..2) # includes its ending value
(1...2) # excludes its ending value
+ (1..) # endless range, representing infinite sequence from 1 to Infinity
You may create a range of any object. See the Range documentation for details
on the methods you need to implement.
diff --git a/doc/syntax/methods.rdoc b/doc/syntax/methods.rdoc
index d41c6fcba5..5d070f8586 100644
--- a/doc/syntax/methods.rdoc
+++ b/doc/syntax/methods.rdoc
@@ -31,7 +31,7 @@ indicates an extended character. Ruby allows method names and other identifiers
to contain such characters. Ruby programs cannot contain some characters like
ASCII NUL (<code>\x00</code>).
-The following are the examples of valid ruby methods:
+The following are examples of valid Ruby methods:
def hello
"hello"
@@ -45,18 +45,18 @@ Typically method names are US-ASCII compatible since the keys to type them
exist on all keyboards.
Method names may end with a <code>!</code> (bang or exclamation mark), a
-<code>?</code> (question mark) or <code>=</code> equals sign.
+<code>?</code> (question mark), or <code>=</code> (equals sign).
-The bang methods (<code>!</code> at the end of method name) are called and
+The bang methods (<code>!</code> at the end of the method name) are called and
executed just like any other method. However, by convention, a method with an
-exclamation point or bang is considered dangerous. In ruby core library the
+exclamation point or bang is considered dangerous. In Ruby's core library the
dangerous method implies that when a method ends with a bang (<code>!</code>),
it indicates that unlike its non-bang equivalent, permanently modifies its
-receiver. Almost always, ruby core library will have a non-bang
+receiver. Almost always, the Ruby core library will have a non-bang
counterpart (method name which does NOT end with <code>!</code>) of every bang
method (method name which does end with <code>!</code>) that does not modify
-the receiver. This convention is typically true for ruby core library but
-may or may not hold true for other ruby libraries.
+the receiver. This convention is typically true for the Ruby core library but
+may or may not hold true for other Ruby libraries.
Methods that end with a question mark by convention return boolean, but they
may not always return just +true+ or +false+. Often, they will return an
@@ -66,8 +66,8 @@ Methods that end with an equals sign indicate an assignment method. For
assignment methods, the return value is ignored and the arguments are returned
instead.
-These are method names for the various ruby operators. Each of these
-operators accept only one argument. Following the operator is the typical
+These are method names for the various Ruby operators. Each of these
+operators accepts only one argument. Following the operator is the typical
use or name of the operator. Creating an alternate meaning for the operator
may lead to confusion as the user expects plus to add things, minus to
subtract things, etc. Additionally, you cannot alter the precedence of the
@@ -147,14 +147,18 @@ evaluated.
1 + 1 # this expression is never evaluated
end
-Note that for assignment methods the return value will always be ignored.
-Instead, the argument will be returned:
+Note that for assignment methods the return value will be ignored when using
+the assignment syntax. Instead, the argument will be returned:
def a=(value)
return 1 + value
end
- p(a = 5) # prints 5
+ p(self.a = 5) # prints 5
+
+The actual return value will be returned when invoking the method directly:
+
+ p send(:a=, 5) # prints 6
== Scope
@@ -351,12 +355,23 @@ converted to an Array:
gather_arguments 1, 2, 3 # prints [1, 2, 3]
-The array argument must be the last positional argument, it must appear before
-any keyword arguments.
+The array argument must appear before any keyword arguments.
+
+It is possible to gather arguments at the beginning or in the middle:
+
+ def gather_arguments(first_arg, *middle_arguments, last_arg)
+ p middle_arguments
+ end
+
+ gather_arguments 1, 2, 3, 4 # prints [2, 3]
The array argument will capture a Hash as the last entry if a hash was sent by
the caller after all positional arguments.
+ def gather_arguments(*arguments)
+ p arguments
+ end
+
gather_arguments 1, a: 2 # prints [1, {:a=>2}]
However, this only occurs if the method does not declare any keyword arguments.
diff --git a/doc/syntax/refinements.rdoc b/doc/syntax/refinements.rdoc
index 1419fea509..fc554bb476 100644
--- a/doc/syntax/refinements.rdoc
+++ b/doc/syntax/refinements.rdoc
@@ -7,7 +7,7 @@ changes. This can cause unintended side-effects or breakage of programs.
Refinements are designed to reduce the impact of monkey patching on other
users of the monkey-patched class. Refinements provide a way to extend a
-class locally.
+class locally. Refinements can modify both classes and modules.
Here is a basic refinement:
@@ -26,8 +26,7 @@ Here is a basic refinement:
end
First, a class +C+ is defined. Next a refinement for +C+ is created using
-Module#refine. Refinements only modify classes, not modules so the argument
-must be a class.
+Module#refine.
Module#refine creates an anonymous module that contains the changes or
refinements to the class (+C+ in the example). +self+ in the refine block is
@@ -49,13 +48,13 @@ until the end of the current class or module definition, or until the end of
the current file if used at the top-level.
You may activate refinements in a string passed to Kernel#eval. Refinements
-are active the end of the eval string.
+are active until the end of the eval string.
Refinements are lexical in scope. Refinements are only active within a scope
-after the call to using. Any code before the using statement will not have the
+after the call to +using+. Any code before the +using+ statement will not have the
refinement activated.
-When control is transferred outside the scope the refinement is deactivated.
+When control is transferred outside the scope, the refinement is deactivated.
This means that if you require or load a file or call a method that is defined
outside the current scope the refinement will be deactivated:
@@ -80,7 +79,7 @@ outside the current scope the refinement will be deactivated:
x.foo # prints "C#foo in M"
call_foo(x) #=> raises NoMethodError
-If a method is defined in a scope where a refinement is active the refinement
+If a method is defined in a scope where a refinement is active, the refinement
will be active when the method is called. This example spans multiple files:
c.rb:
@@ -159,8 +158,8 @@ In a class:
end
# not activated here
-Note that the refinements in M are not activated automatically if the class
-Foo is reopened later.
+Note that the refinements in +M+ are *not* activated automatically if the class
++Foo+ is reopened later.
In eval:
@@ -180,9 +179,9 @@ When not evaluated:
end
# not activated here
-When defining multiple refinements in the same module, inside a refine block
-all refinements from the same module are active when a refined method is
-called:
+When defining multiple refinements in the same module inside multiple +refine+ blocks,
+all refinements from the same module are active when a refined method
+(any of the +to_json+ methods from the example below) is called:
module ToJSON
refine Integer do
@@ -225,12 +224,13 @@ If no method was found at any point this repeats with the superclass of +C+.
Note that methods in a subclass have priority over refinements in a
superclass. For example, if the method <code>/</code> is defined in a
-refinement for Integer <code>1 / 2</code> invokes the original Fixnum#/
-because Fixnum is a subclass of Integer and is searched before the refinements
-for the superclass Integer.
+refinement for Numeric <code>1 / 2</code> invokes the original Integer#/
+because Integer is a subclass of Numeric and is searched before the refinements
+for the superclass Numeric. Since the method <code>/</code> is also present
+in child +Integer+, the method lookup does not move up to the superclass.
-If a method +foo+ is defined on Integer in a refinement, <code>1.foo</code>
-invokes that method since +foo+ does not exist on Fixnum.
+However, if a method +foo+ is defined on Numeric in a refinement, <code>1.foo</code>
+invokes that method since +foo+ does not exist on Integer.
== +super+
@@ -247,14 +247,35 @@ Note that +super+ in a method of a refinement invokes the method in the
refined class even if there is another refinement which has been activated in
the same context.
-== Indirect Method Calls
+== Methods Introspection
-When using indirect method access such as Kernel#send, Kernel#method or
-Kernel#respond_to? refinements are not honored for the caller context during
-method lookup.
+When using introspection methods such as Kernel#method or Kernel#methods refinements are not honored.
This behavior may be changed in the future.
+== Refinement inheritance by Module#include
+
+When a module X is included into a module Y, Y inherits refinements from X.
+
+For example, C inherits refinements from A and B in the following code:
+
+ module A
+ refine X do ... end
+ refine Y do ... end
+ end
+ module B
+ refine Z do ... end
+ end
+ module C
+ include A
+ include B
+ end
+
+ using C
+ # Refinements in A and B are activated here.
+
+Refinements in descendants have higher precedence than those of ancestors.
+
== Further Reading
See https://bugs.ruby-lang.org/projects/ruby-trunk/wiki/RefinementsSpec for the
diff --git a/template/yarvarch.en b/doc/yarvarch.en
index 7a76e25b7e..7a76e25b7e 100644
--- a/template/yarvarch.en
+++ b/doc/yarvarch.en
diff --git a/template/yarvarch.ja b/doc/yarvarch.ja
index 2739ec6b14..2739ec6b14 100644
--- a/template/yarvarch.ja
+++ b/doc/yarvarch.ja
diff --git a/enc/ascii.c b/enc/ascii.c
index d34cc20582..8b32c414fe 100644
--- a/enc/ascii.c
+++ b/enc/ascii.c
@@ -29,9 +29,12 @@
*/
#include "regenc.h"
-#include "encindex.h"
+#ifdef RUBY
+# include "encindex.h"
+#endif
+
#ifndef ENCINDEX_ASCII
-#define ENCINDEX_ASCII 0
+# define ENCINDEX_ASCII 0
#endif
OnigEncodingDefine(ascii, ASCII) = {
@@ -51,9 +54,9 @@ OnigEncodingDefine(ascii, ASCII) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ onigenc_single_byte_ascii_only_case_map,
ENCINDEX_ASCII,
ONIGENC_FLAG_NONE,
- onigenc_single_byte_ascii_only_case_map,
};
ENC_ALIAS("BINARY", "ASCII-8BIT")
ENC_REPLICATE("IBM437", "ASCII-8BIT")
diff --git a/enc/big5.c b/enc/big5.c
index fc2bcadcc1..ab4fb69819 100644
--- a/enc/big5.c
+++ b/enc/big5.c
@@ -300,9 +300,9 @@ OnigEncodingDefine(big5, BIG5) = {
onigenc_not_support_get_ctype_code_range,
big5_left_adjust_char_head,
big5_is_allowed_reverse_match,
+ onigenc_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_ascii_only_case_map,
};
/*
@@ -335,9 +335,9 @@ OnigEncodingDefine(big5_hkscs, BIG5_HKSCS) = {
onigenc_not_support_get_ctype_code_range,
big5_left_adjust_char_head,
big5_is_allowed_reverse_match,
+ onigenc_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_ascii_only_case_map,
};
ENC_ALIAS("Big5-HKSCS:2008", "Big5-HKSCS")
@@ -370,7 +370,7 @@ OnigEncodingDefine(big5_uao, BIG5_UAO) = {
onigenc_not_support_get_ctype_code_range,
big5_left_adjust_char_head,
big5_is_allowed_reverse_match,
+ onigenc_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_ascii_only_case_map,
};
diff --git a/enc/cp949.c b/enc/cp949.c
index f832cd5758..bd2c8d21a4 100644
--- a/enc/cp949.c
+++ b/enc/cp949.c
@@ -211,9 +211,9 @@ OnigEncodingDefine(cp949, CP949) = {
onigenc_not_support_get_ctype_code_range,
cp949_left_adjust_char_head,
cp949_is_allowed_reverse_match,
+ onigenc_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_ascii_only_case_map,
};
/*
* Name: CP949
diff --git a/enc/depend b/enc/depend
index fd2e5ea996..b9e8e3ba8b 100644
--- a/enc/depend
+++ b/enc/depend
@@ -158,7 +158,7 @@ clean:
% end
% @ignore_error = $nmake ? '' : ' 2> /dev/null || true'
% unless inplace
- $(Q)$(RM) enc/unicode/name2ctype.h
+ $(Q)$(RM) enc/unicode/*/casefold.h enc/unicode/*/name2ctype.h
$(Q)$(RM) enc/jis/props.h
-$(Q)$(RMDIR) enc/unicode<%=@ignore_error%>
% end
@@ -169,7 +169,7 @@ clean:
clean-srcs:
$(Q)$(RM) <%=pathrep['$(TRANSCSRCS)']%>
-$(Q)$(RMDIR) <%=pathrep['enc/trans']%><%=@ignore_error%>
- $(Q)$(RM) enc/unicode/name2ctype.h
+ $(Q)$(RM) enc/unicode/*/casefold.h enc/unicode/*/name2ctype.h
$(Q)$(RM) enc/jis/props.h
-$(Q)$(RMDIR) <%=pathrep['enc/unicode']%><%=@ignore_error%>
-$(Q)$(RMDIR) <%=pathrep['enc/props']%><%=@ignore_error%>
@@ -184,18 +184,21 @@ enc/ascii.$(OBJEXT): config.h
enc/ascii.$(OBJEXT): defines.h
enc/ascii.$(OBJEXT): enc/ascii.c
enc/ascii.$(OBJEXT): missing.h
+enc/ascii.$(OBJEXT): onigmo.h
enc/ascii.$(OBJEXT): oniguruma.h
enc/big5.$(OBJEXT): $(top_srcdir)/regenc.h
enc/big5.$(OBJEXT): config.h
enc/big5.$(OBJEXT): defines.h
enc/big5.$(OBJEXT): enc/big5.c
enc/big5.$(OBJEXT): missing.h
+enc/big5.$(OBJEXT): onigmo.h
enc/big5.$(OBJEXT): oniguruma.h
enc/cp949.$(OBJEXT): $(top_srcdir)/regenc.h
enc/cp949.$(OBJEXT): config.h
enc/cp949.$(OBJEXT): defines.h
enc/cp949.$(OBJEXT): enc/cp949.c
enc/cp949.$(OBJEXT): missing.h
+enc/cp949.$(OBJEXT): onigmo.h
enc/cp949.$(OBJEXT): oniguruma.h
enc/emacs_mule.$(OBJEXT): $(hdrdir)/ruby/ruby.h
enc/emacs_mule.$(OBJEXT): $(top_srcdir)/regenc.h
@@ -206,6 +209,7 @@ enc/emacs_mule.$(OBJEXT): defines.h
enc/emacs_mule.$(OBJEXT): enc/emacs_mule.c
enc/emacs_mule.$(OBJEXT): intern.h
enc/emacs_mule.$(OBJEXT): missing.h
+enc/emacs_mule.$(OBJEXT): onigmo.h
enc/emacs_mule.$(OBJEXT): oniguruma.h
enc/emacs_mule.$(OBJEXT): st.h
enc/emacs_mule.$(OBJEXT): subst.h
@@ -221,6 +225,7 @@ enc/encdb.$(OBJEXT): encoding.h
enc/encdb.$(OBJEXT): intern.h
enc/encdb.$(OBJEXT): io.h
enc/encdb.$(OBJEXT): missing.h
+enc/encdb.$(OBJEXT): onigmo.h
enc/encdb.$(OBJEXT): oniguruma.h
enc/encdb.$(OBJEXT): st.h
enc/encdb.$(OBJEXT): subst.h
@@ -235,6 +240,7 @@ enc/euc_jp.$(OBJEXT): enc/jis/props.h
enc/euc_jp.$(OBJEXT): enc/jis/props.kwd
enc/euc_jp.$(OBJEXT): intern.h
enc/euc_jp.$(OBJEXT): missing.h
+enc/euc_jp.$(OBJEXT): onigmo.h
enc/euc_jp.$(OBJEXT): oniguruma.h
enc/euc_jp.$(OBJEXT): st.h
enc/euc_jp.$(OBJEXT): subst.h
@@ -243,18 +249,21 @@ enc/euc_kr.$(OBJEXT): config.h
enc/euc_kr.$(OBJEXT): defines.h
enc/euc_kr.$(OBJEXT): enc/euc_kr.c
enc/euc_kr.$(OBJEXT): missing.h
+enc/euc_kr.$(OBJEXT): onigmo.h
enc/euc_kr.$(OBJEXT): oniguruma.h
enc/euc_tw.$(OBJEXT): $(top_srcdir)/regenc.h
enc/euc_tw.$(OBJEXT): config.h
enc/euc_tw.$(OBJEXT): defines.h
enc/euc_tw.$(OBJEXT): enc/euc_tw.c
enc/euc_tw.$(OBJEXT): missing.h
+enc/euc_tw.$(OBJEXT): onigmo.h
enc/euc_tw.$(OBJEXT): oniguruma.h
enc/gb18030.$(OBJEXT): $(top_srcdir)/regenc.h
enc/gb18030.$(OBJEXT): config.h
enc/gb18030.$(OBJEXT): defines.h
enc/gb18030.$(OBJEXT): enc/gb18030.c
enc/gb18030.$(OBJEXT): missing.h
+enc/gb18030.$(OBJEXT): onigmo.h
enc/gb18030.$(OBJEXT): oniguruma.h
enc/gb2312.$(OBJEXT): $(hdrdir)/ruby/ruby.h
enc/gb2312.$(OBJEXT): $(top_srcdir)/regenc.h
@@ -265,6 +274,7 @@ enc/gb2312.$(OBJEXT): enc/gb2312.c
enc/gb2312.$(OBJEXT): encoding.h
enc/gb2312.$(OBJEXT): intern.h
enc/gb2312.$(OBJEXT): missing.h
+enc/gb2312.$(OBJEXT): onigmo.h
enc/gb2312.$(OBJEXT): oniguruma.h
enc/gb2312.$(OBJEXT): st.h
enc/gb2312.$(OBJEXT): subst.h
@@ -273,6 +283,7 @@ enc/gbk.$(OBJEXT): config.h
enc/gbk.$(OBJEXT): defines.h
enc/gbk.$(OBJEXT): enc/gbk.c
enc/gbk.$(OBJEXT): missing.h
+enc/gbk.$(OBJEXT): onigmo.h
enc/gbk.$(OBJEXT): oniguruma.h
enc/iso_8859_1.$(OBJEXT): $(top_srcdir)/regenc.h
enc/iso_8859_1.$(OBJEXT): config.h
@@ -280,6 +291,7 @@ enc/iso_8859_1.$(OBJEXT): defines.h
enc/iso_8859_1.$(OBJEXT): enc/iso_8859.h
enc/iso_8859_1.$(OBJEXT): enc/iso_8859_1.c
enc/iso_8859_1.$(OBJEXT): missing.h
+enc/iso_8859_1.$(OBJEXT): onigmo.h
enc/iso_8859_1.$(OBJEXT): oniguruma.h
enc/iso_8859_10.$(OBJEXT): $(top_srcdir)/regenc.h
enc/iso_8859_10.$(OBJEXT): config.h
@@ -287,12 +299,14 @@ enc/iso_8859_10.$(OBJEXT): defines.h
enc/iso_8859_10.$(OBJEXT): enc/iso_8859.h
enc/iso_8859_10.$(OBJEXT): enc/iso_8859_10.c
enc/iso_8859_10.$(OBJEXT): missing.h
+enc/iso_8859_10.$(OBJEXT): onigmo.h
enc/iso_8859_10.$(OBJEXT): oniguruma.h
enc/iso_8859_11.$(OBJEXT): $(top_srcdir)/regenc.h
enc/iso_8859_11.$(OBJEXT): config.h
enc/iso_8859_11.$(OBJEXT): defines.h
enc/iso_8859_11.$(OBJEXT): enc/iso_8859_11.c
enc/iso_8859_11.$(OBJEXT): missing.h
+enc/iso_8859_11.$(OBJEXT): onigmo.h
enc/iso_8859_11.$(OBJEXT): oniguruma.h
enc/iso_8859_13.$(OBJEXT): $(top_srcdir)/regenc.h
enc/iso_8859_13.$(OBJEXT): config.h
@@ -300,6 +314,7 @@ enc/iso_8859_13.$(OBJEXT): defines.h
enc/iso_8859_13.$(OBJEXT): enc/iso_8859.h
enc/iso_8859_13.$(OBJEXT): enc/iso_8859_13.c
enc/iso_8859_13.$(OBJEXT): missing.h
+enc/iso_8859_13.$(OBJEXT): onigmo.h
enc/iso_8859_13.$(OBJEXT): oniguruma.h
enc/iso_8859_14.$(OBJEXT): $(top_srcdir)/regenc.h
enc/iso_8859_14.$(OBJEXT): config.h
@@ -307,6 +322,7 @@ enc/iso_8859_14.$(OBJEXT): defines.h
enc/iso_8859_14.$(OBJEXT): enc/iso_8859.h
enc/iso_8859_14.$(OBJEXT): enc/iso_8859_14.c
enc/iso_8859_14.$(OBJEXT): missing.h
+enc/iso_8859_14.$(OBJEXT): onigmo.h
enc/iso_8859_14.$(OBJEXT): oniguruma.h
enc/iso_8859_15.$(OBJEXT): $(top_srcdir)/regenc.h
enc/iso_8859_15.$(OBJEXT): config.h
@@ -314,6 +330,7 @@ enc/iso_8859_15.$(OBJEXT): defines.h
enc/iso_8859_15.$(OBJEXT): enc/iso_8859.h
enc/iso_8859_15.$(OBJEXT): enc/iso_8859_15.c
enc/iso_8859_15.$(OBJEXT): missing.h
+enc/iso_8859_15.$(OBJEXT): onigmo.h
enc/iso_8859_15.$(OBJEXT): oniguruma.h
enc/iso_8859_16.$(OBJEXT): $(top_srcdir)/regenc.h
enc/iso_8859_16.$(OBJEXT): config.h
@@ -321,6 +338,7 @@ enc/iso_8859_16.$(OBJEXT): defines.h
enc/iso_8859_16.$(OBJEXT): enc/iso_8859.h
enc/iso_8859_16.$(OBJEXT): enc/iso_8859_16.c
enc/iso_8859_16.$(OBJEXT): missing.h
+enc/iso_8859_16.$(OBJEXT): onigmo.h
enc/iso_8859_16.$(OBJEXT): oniguruma.h
enc/iso_8859_2.$(OBJEXT): $(top_srcdir)/regenc.h
enc/iso_8859_2.$(OBJEXT): config.h
@@ -328,6 +346,7 @@ enc/iso_8859_2.$(OBJEXT): defines.h
enc/iso_8859_2.$(OBJEXT): enc/iso_8859.h
enc/iso_8859_2.$(OBJEXT): enc/iso_8859_2.c
enc/iso_8859_2.$(OBJEXT): missing.h
+enc/iso_8859_2.$(OBJEXT): onigmo.h
enc/iso_8859_2.$(OBJEXT): oniguruma.h
enc/iso_8859_3.$(OBJEXT): $(top_srcdir)/regenc.h
enc/iso_8859_3.$(OBJEXT): config.h
@@ -335,6 +354,7 @@ enc/iso_8859_3.$(OBJEXT): defines.h
enc/iso_8859_3.$(OBJEXT): enc/iso_8859.h
enc/iso_8859_3.$(OBJEXT): enc/iso_8859_3.c
enc/iso_8859_3.$(OBJEXT): missing.h
+enc/iso_8859_3.$(OBJEXT): onigmo.h
enc/iso_8859_3.$(OBJEXT): oniguruma.h
enc/iso_8859_4.$(OBJEXT): $(top_srcdir)/regenc.h
enc/iso_8859_4.$(OBJEXT): config.h
@@ -342,30 +362,35 @@ enc/iso_8859_4.$(OBJEXT): defines.h
enc/iso_8859_4.$(OBJEXT): enc/iso_8859.h
enc/iso_8859_4.$(OBJEXT): enc/iso_8859_4.c
enc/iso_8859_4.$(OBJEXT): missing.h
+enc/iso_8859_4.$(OBJEXT): onigmo.h
enc/iso_8859_4.$(OBJEXT): oniguruma.h
enc/iso_8859_5.$(OBJEXT): $(top_srcdir)/regenc.h
enc/iso_8859_5.$(OBJEXT): config.h
enc/iso_8859_5.$(OBJEXT): defines.h
enc/iso_8859_5.$(OBJEXT): enc/iso_8859_5.c
enc/iso_8859_5.$(OBJEXT): missing.h
+enc/iso_8859_5.$(OBJEXT): onigmo.h
enc/iso_8859_5.$(OBJEXT): oniguruma.h
enc/iso_8859_6.$(OBJEXT): $(top_srcdir)/regenc.h
enc/iso_8859_6.$(OBJEXT): config.h
enc/iso_8859_6.$(OBJEXT): defines.h
enc/iso_8859_6.$(OBJEXT): enc/iso_8859_6.c
enc/iso_8859_6.$(OBJEXT): missing.h
+enc/iso_8859_6.$(OBJEXT): onigmo.h
enc/iso_8859_6.$(OBJEXT): oniguruma.h
enc/iso_8859_7.$(OBJEXT): $(top_srcdir)/regenc.h
enc/iso_8859_7.$(OBJEXT): config.h
enc/iso_8859_7.$(OBJEXT): defines.h
enc/iso_8859_7.$(OBJEXT): enc/iso_8859_7.c
enc/iso_8859_7.$(OBJEXT): missing.h
+enc/iso_8859_7.$(OBJEXT): onigmo.h
enc/iso_8859_7.$(OBJEXT): oniguruma.h
enc/iso_8859_8.$(OBJEXT): $(top_srcdir)/regenc.h
enc/iso_8859_8.$(OBJEXT): config.h
enc/iso_8859_8.$(OBJEXT): defines.h
enc/iso_8859_8.$(OBJEXT): enc/iso_8859_8.c
enc/iso_8859_8.$(OBJEXT): missing.h
+enc/iso_8859_8.$(OBJEXT): onigmo.h
enc/iso_8859_8.$(OBJEXT): oniguruma.h
enc/iso_8859_9.$(OBJEXT): $(top_srcdir)/regenc.h
enc/iso_8859_9.$(OBJEXT): config.h
@@ -373,18 +398,21 @@ enc/iso_8859_9.$(OBJEXT): defines.h
enc/iso_8859_9.$(OBJEXT): enc/iso_8859.h
enc/iso_8859_9.$(OBJEXT): enc/iso_8859_9.c
enc/iso_8859_9.$(OBJEXT): missing.h
+enc/iso_8859_9.$(OBJEXT): onigmo.h
enc/iso_8859_9.$(OBJEXT): oniguruma.h
enc/koi8_r.$(OBJEXT): $(top_srcdir)/regenc.h
enc/koi8_r.$(OBJEXT): config.h
enc/koi8_r.$(OBJEXT): defines.h
enc/koi8_r.$(OBJEXT): enc/koi8_r.c
enc/koi8_r.$(OBJEXT): missing.h
+enc/koi8_r.$(OBJEXT): onigmo.h
enc/koi8_r.$(OBJEXT): oniguruma.h
enc/koi8_u.$(OBJEXT): $(top_srcdir)/regenc.h
enc/koi8_u.$(OBJEXT): config.h
enc/koi8_u.$(OBJEXT): defines.h
enc/koi8_u.$(OBJEXT): enc/koi8_u.c
enc/koi8_u.$(OBJEXT): missing.h
+enc/koi8_u.$(OBJEXT): onigmo.h
enc/koi8_u.$(OBJEXT): oniguruma.h
enc/shift_jis.$(OBJEXT): $(hdrdir)/ruby/ruby.h
enc/shift_jis.$(OBJEXT): $(top_srcdir)/regenc.h
@@ -395,8 +423,10 @@ enc/shift_jis.$(OBJEXT): defines.h
enc/shift_jis.$(OBJEXT): enc/jis/props.h
enc/shift_jis.$(OBJEXT): enc/jis/props.kwd
enc/shift_jis.$(OBJEXT): enc/shift_jis.c
+enc/shift_jis.$(OBJEXT): enc/shift_jis.h
enc/shift_jis.$(OBJEXT): intern.h
enc/shift_jis.$(OBJEXT): missing.h
+enc/shift_jis.$(OBJEXT): onigmo.h
enc/shift_jis.$(OBJEXT): oniguruma.h
enc/shift_jis.$(OBJEXT): st.h
enc/shift_jis.$(OBJEXT): subst.h
@@ -601,16 +631,19 @@ enc/trans/utf_16_32.$(OBJEXT): intern.h
enc/trans/utf_16_32.$(OBJEXT): missing.h
enc/trans/utf_16_32.$(OBJEXT): st.h
enc/trans/utf_16_32.$(OBJEXT): subst.h
+enc/unicode.$(OBJEXT): $(UNICODE_HDR_DIR)/casefold.h
+enc/unicode.$(OBJEXT): $(UNICODE_HDR_DIR)/name2ctype.h
enc/unicode.$(OBJEXT): $(hdrdir)/ruby/ruby.h
enc/unicode.$(OBJEXT): $(top_srcdir)/regenc.h
enc/unicode.$(OBJEXT): $(top_srcdir)/regint.h
enc/unicode.$(OBJEXT): config.h
enc/unicode.$(OBJEXT): defines.h
enc/unicode.$(OBJEXT): enc/unicode.c
-enc/unicode.$(OBJEXT): $(UNICODE_HDR_DIR)/casefold.h
-enc/unicode.$(OBJEXT): $(UNICODE_HDR_DIR)/name2ctype.h
+enc/unicode.$(OBJEXT): enc/unicode/10.0.0/casefold.h
+enc/unicode.$(OBJEXT): enc/unicode/10.0.0/name2ctype.h
enc/unicode.$(OBJEXT): intern.h
enc/unicode.$(OBJEXT): missing.h
+enc/unicode.$(OBJEXT): onigmo.h
enc/unicode.$(OBJEXT): oniguruma.h
enc/unicode.$(OBJEXT): st.h
enc/unicode.$(OBJEXT): subst.h
@@ -620,6 +653,7 @@ enc/us_ascii.$(OBJEXT): config.h
enc/us_ascii.$(OBJEXT): defines.h
enc/us_ascii.$(OBJEXT): enc/us_ascii.c
enc/us_ascii.$(OBJEXT): missing.h
+enc/us_ascii.$(OBJEXT): onigmo.h
enc/us_ascii.$(OBJEXT): oniguruma.h
enc/utf_16be.$(OBJEXT): $(top_srcdir)/regenc.h
enc/utf_16be.$(OBJEXT): config.h
@@ -627,6 +661,7 @@ enc/utf_16be.$(OBJEXT): defines.h
enc/utf_16be.$(OBJEXT): enc/iso_8859.h
enc/utf_16be.$(OBJEXT): enc/utf_16be.c
enc/utf_16be.$(OBJEXT): missing.h
+enc/utf_16be.$(OBJEXT): onigmo.h
enc/utf_16be.$(OBJEXT): oniguruma.h
enc/utf_16le.$(OBJEXT): $(top_srcdir)/regenc.h
enc/utf_16le.$(OBJEXT): config.h
@@ -634,6 +669,7 @@ enc/utf_16le.$(OBJEXT): defines.h
enc/utf_16le.$(OBJEXT): enc/iso_8859.h
enc/utf_16le.$(OBJEXT): enc/utf_16le.c
enc/utf_16le.$(OBJEXT): missing.h
+enc/utf_16le.$(OBJEXT): onigmo.h
enc/utf_16le.$(OBJEXT): oniguruma.h
enc/utf_32be.$(OBJEXT): $(top_srcdir)/regenc.h
enc/utf_32be.$(OBJEXT): config.h
@@ -641,6 +677,7 @@ enc/utf_32be.$(OBJEXT): defines.h
enc/utf_32be.$(OBJEXT): enc/iso_8859.h
enc/utf_32be.$(OBJEXT): enc/utf_32be.c
enc/utf_32be.$(OBJEXT): missing.h
+enc/utf_32be.$(OBJEXT): onigmo.h
enc/utf_32be.$(OBJEXT): oniguruma.h
enc/utf_32le.$(OBJEXT): $(top_srcdir)/regenc.h
enc/utf_32le.$(OBJEXT): config.h
@@ -648,6 +685,7 @@ enc/utf_32le.$(OBJEXT): defines.h
enc/utf_32le.$(OBJEXT): enc/iso_8859.h
enc/utf_32le.$(OBJEXT): enc/utf_32le.c
enc/utf_32le.$(OBJEXT): missing.h
+enc/utf_32le.$(OBJEXT): onigmo.h
enc/utf_32le.$(OBJEXT): oniguruma.h
enc/utf_8.$(OBJEXT): $(top_srcdir)/encindex.h
enc/utf_8.$(OBJEXT): $(top_srcdir)/regenc.h
@@ -655,25 +693,51 @@ enc/utf_8.$(OBJEXT): config.h
enc/utf_8.$(OBJEXT): defines.h
enc/utf_8.$(OBJEXT): enc/utf_8.c
enc/utf_8.$(OBJEXT): missing.h
+enc/utf_8.$(OBJEXT): onigmo.h
enc/utf_8.$(OBJEXT): oniguruma.h
enc/windows_1250.$(OBJEXT): $(top_srcdir)/regenc.h
enc/windows_1250.$(OBJEXT): config.h
enc/windows_1250.$(OBJEXT): defines.h
+enc/windows_1250.$(OBJEXT): enc/iso_8859.h
enc/windows_1250.$(OBJEXT): enc/windows_1250.c
enc/windows_1250.$(OBJEXT): missing.h
+enc/windows_1250.$(OBJEXT): onigmo.h
enc/windows_1250.$(OBJEXT): oniguruma.h
enc/windows_1251.$(OBJEXT): $(top_srcdir)/regenc.h
enc/windows_1251.$(OBJEXT): config.h
enc/windows_1251.$(OBJEXT): defines.h
enc/windows_1251.$(OBJEXT): enc/windows_1251.c
enc/windows_1251.$(OBJEXT): missing.h
+enc/windows_1251.$(OBJEXT): onigmo.h
enc/windows_1251.$(OBJEXT): oniguruma.h
enc/windows_1252.$(OBJEXT): $(top_srcdir)/regenc.h
enc/windows_1252.$(OBJEXT): config.h
enc/windows_1252.$(OBJEXT): defines.h
+enc/windows_1252.$(OBJEXT): enc/iso_8859.h
enc/windows_1252.$(OBJEXT): enc/windows_1252.c
enc/windows_1252.$(OBJEXT): missing.h
+enc/windows_1252.$(OBJEXT): onigmo.h
enc/windows_1252.$(OBJEXT): oniguruma.h
+enc/windows_1253.$(OBJEXT): $(top_srcdir)/regenc.h
+enc/windows_1253.$(OBJEXT): config.h
+enc/windows_1253.$(OBJEXT): defines.h
+enc/windows_1253.$(OBJEXT): enc/windows_1253.c
+enc/windows_1253.$(OBJEXT): missing.h
+enc/windows_1253.$(OBJEXT): onigmo.h
+enc/windows_1254.$(OBJEXT): $(top_srcdir)/regenc.h
+enc/windows_1254.$(OBJEXT): config.h
+enc/windows_1254.$(OBJEXT): defines.h
+enc/windows_1254.$(OBJEXT): enc/iso_8859.h
+enc/windows_1254.$(OBJEXT): enc/windows_1254.c
+enc/windows_1254.$(OBJEXT): missing.h
+enc/windows_1254.$(OBJEXT): onigmo.h
+enc/windows_1257.$(OBJEXT): $(top_srcdir)/regenc.h
+enc/windows_1257.$(OBJEXT): config.h
+enc/windows_1257.$(OBJEXT): defines.h
+enc/windows_1257.$(OBJEXT): enc/iso_8859.h
+enc/windows_1257.$(OBJEXT): enc/windows_1257.c
+enc/windows_1257.$(OBJEXT): missing.h
+enc/windows_1257.$(OBJEXT): onigmo.h
enc/windows_31j.$(OBJEXT): $(hdrdir)/ruby/ruby.h
enc/windows_31j.$(OBJEXT): $(top_srcdir)/regenc.h
enc/windows_31j.$(OBJEXT): $(top_srcdir)/regint.h
@@ -683,9 +747,11 @@ enc/windows_31j.$(OBJEXT): defines.h
enc/windows_31j.$(OBJEXT): enc/jis/props.h
enc/windows_31j.$(OBJEXT): enc/jis/props.kwd
enc/windows_31j.$(OBJEXT): enc/shift_jis.c
+enc/windows_31j.$(OBJEXT): enc/shift_jis.h
enc/windows_31j.$(OBJEXT): enc/windows_31j.c
enc/windows_31j.$(OBJEXT): intern.h
enc/windows_31j.$(OBJEXT): missing.h
+enc/windows_31j.$(OBJEXT): onigmo.h
enc/windows_31j.$(OBJEXT): oniguruma.h
enc/windows_31j.$(OBJEXT): st.h
enc/windows_31j.$(OBJEXT): subst.h
diff --git a/enc/emacs_mule.c b/enc/emacs_mule.c
index a53f243dfe..f92eb183cf 100644
--- a/enc/emacs_mule.c
+++ b/enc/emacs_mule.c
@@ -27,7 +27,7 @@
* SUCH DAMAGE.
*/
-#include "regint.h"
+#include "regenc.h"
#define emacsmule_islead(c) ((UChar )(c) < 0x9e)
@@ -334,9 +334,9 @@ OnigEncodingDefine(emacs_mule, Emacs_Mule) = {
onigenc_not_support_get_ctype_code_range,
left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ onigenc_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_ascii_only_case_map,
};
ENC_REPLICATE("stateless-ISO-2022-JP", "Emacs-Mule")
diff --git a/enc/euc_jp.c b/enc/euc_jp.c
index f9604b8d6e..ded051af69 100644
--- a/enc/euc_jp.c
+++ b/enc/euc_jp.c
@@ -28,7 +28,7 @@
* SUCH DAMAGE.
*/
-#include "regint.h"
+#include "regenc.h"
#define eucjp_islead(c) ((UChar )((c) - 0xa1) > 0xfe - 0xa1)
@@ -576,9 +576,9 @@ OnigEncodingDefine(euc_jp, EUC_JP) = {
get_ctype_code_range,
left_adjust_char_head,
is_allowed_reverse_match,
+ onigenc_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_ascii_only_case_map,
};
/*
* Name: EUC-JP
diff --git a/enc/euc_kr.c b/enc/euc_kr.c
index eb17f476e9..21d6ab4e1c 100644
--- a/enc/euc_kr.c
+++ b/enc/euc_kr.c
@@ -188,8 +188,33 @@ OnigEncodingDefine(euc_kr, EUC_KR) = {
onigenc_not_support_get_ctype_code_range,
euckr_left_adjust_char_head,
euckr_is_allowed_reverse_match,
+ onigenc_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_ascii_only_case_map,
};
ENC_ALIAS("eucKR", "EUC-KR")
+
+#ifndef RUBY
+/* Same with OnigEncodingEUC_KR except the name */
+OnigEncodingDefine(euc_cn, EUC_CN) = {
+ euckr_mbc_enc_len,
+ "EUC-CN", /* name */
+ 2, /* max enc length */
+ 1, /* min enc length */
+ onigenc_is_mbc_newline_0x0a,
+ euckr_mbc_to_code,
+ onigenc_mb2_code_to_mbclen,
+ euckr_code_to_mbc,
+ euckr_mbc_case_fold,
+ onigenc_ascii_apply_all_case_fold,
+ onigenc_ascii_get_case_fold_codes_by_str,
+ onigenc_minimum_property_name_to_ctype,
+ euckr_is_code_ctype,
+ onigenc_not_support_get_ctype_code_range,
+ euckr_left_adjust_char_head,
+ euckr_is_allowed_reverse_match,
+ onigenc_ascii_only_case_map,
+ 0,
+ ONIGENC_FLAG_NONE,
+};
+#endif /* RUBY */
diff --git a/enc/euc_tw.c b/enc/euc_tw.c
index e7d5187c4a..1c5659cb1d 100644
--- a/enc/euc_tw.c
+++ b/enc/euc_tw.c
@@ -221,8 +221,8 @@ OnigEncodingDefine(euc_tw, EUC_TW) = {
onigenc_not_support_get_ctype_code_range,
euctw_left_adjust_char_head,
euctw_is_allowed_reverse_match,
+ onigenc_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_ascii_only_case_map,
};
ENC_ALIAS("eucTW", "EUC-TW")
diff --git a/enc/gb18030.c b/enc/gb18030.c
index 8a00332991..63d2e633ec 100644
--- a/enc/gb18030.c
+++ b/enc/gb18030.c
@@ -597,8 +597,7 @@ OnigEncodingDefine(gb18030, GB18030) = {
onigenc_not_support_get_ctype_code_range,
gb18030_left_adjust_char_head,
gb18030_is_allowed_reverse_match,
+ onigenc_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_ascii_only_case_map,
};
-
diff --git a/enc/gbk.c b/enc/gbk.c
index d3bb1a5864..31032553bf 100644
--- a/enc/gbk.c
+++ b/enc/gbk.c
@@ -211,9 +211,9 @@ OnigEncodingDefine(gbk, GBK) = {
onigenc_not_support_get_ctype_code_range,
gbk_left_adjust_char_head,
gbk_is_allowed_reverse_match,
+ onigenc_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_ascii_only_case_map,
};
/*
* Name: GBK
diff --git a/enc/iso_8859_1.c b/enc/iso_8859_1.c
index 2440c9f5a1..7af0888c3e 100644
--- a/enc/iso_8859_1.c
+++ b/enc/iso_8859_1.c
@@ -256,45 +256,46 @@ is_code_ctype(OnigCodePoint code, unsigned int ctype, OnigEncoding enc ARG_UNUSE
}
static int
-case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==SHARP_s) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == SHARP_s) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 'S';
- code = (flags&ONIGENC_CASE_TITLECASE) ? 's' : 'S';
+ code = (flags & ONIGENC_CASE_TITLECASE) ? 's' : 'S';
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 's';
code = 's';
}
}
else if ((EncISO_8859_1_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
code += 0x20;
}
- else if (code==0xAA || code==0xBA || code==0xB5 || code==0xFF) ;
- else if ((EncISO_8859_1_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ else if (code == 0xAA || code == 0xBA || code == 0xB5 || code == 0xFF)
+ ;
+ else if ((EncISO_8859_1_CtypeTable[code] & BIT_CTYPE_LOWER)
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
code -= 0x20;
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(iso_8859_1, ISO_8859_1) = {
@@ -314,8 +315,8 @@ OnigEncodingDefine(iso_8859_1, ISO_8859_1) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
ENC_ALIAS("ISO8859-1", "ISO-8859-1")
diff --git a/enc/iso_8859_10.c b/enc/iso_8859_10.c
index e06f15f9d0..cae4be2db0 100644
--- a/enc/iso_8859_10.c
+++ b/enc/iso_8859_10.c
@@ -215,9 +215,9 @@ apply_all_case_fold(OnigCaseFoldType flag,
static int
get_case_fold_codes_by_str(OnigCaseFoldType flag,
- const OnigUChar* p, const OnigUChar* end,
- OnigCaseFoldCodeItem items[],
- OnigEncoding enc ARG_UNUSED)
+ const OnigUChar* p, const OnigUChar* end,
+ OnigCaseFoldCodeItem items[],
+ OnigEncoding enc ARG_UNUSED)
{
return onigenc_get_case_fold_codes_by_str_with_map(
numberof(CaseFoldMap), CaseFoldMap, 1,
@@ -225,48 +225,49 @@ get_case_fold_codes_by_str(OnigCaseFoldType flag,
}
static int
-case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==SHARP_s) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == SHARP_s) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 'S';
- code = (flags&ONIGENC_CASE_TITLECASE) ? 's' : 'S';
+ code = (flags & ONIGENC_CASE_TITLECASE) ? 's' : 'S';
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 's';
code = 's';
}
}
- else if (code==0xBD || code==0xFF) ;
+ else if (code == 0xBD || code == 0xFF)
+ ;
else if ((EncISO_8859_10_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
code = ENC_ISO_8859_10_TO_LOWER_CASE(code);
}
- else if ((EncISO_8859_10_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ else if ((EncISO_8859_10_CtypeTable[code] & BIT_CTYPE_LOWER)
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code>=0xA0 && code<=0xBF)
+ if (code >= 0xA0 && code <= 0xBF)
code -= 0x10;
else
code -= 0x20;
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(iso_8859_10, ISO_8859_10) = {
@@ -286,8 +287,8 @@ OnigEncodingDefine(iso_8859_10, ISO_8859_10) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
ENC_ALIAS("ISO8859-10", "ISO-8859-10")
diff --git a/enc/iso_8859_11.c b/enc/iso_8859_11.c
index a5522da2e3..85e8f2cdb4 100644
--- a/enc/iso_8859_11.c
+++ b/enc/iso_8859_11.c
@@ -93,9 +93,9 @@ OnigEncodingDefine(iso_8859_11, ISO_8859_11) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ onigenc_single_byte_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_single_byte_ascii_only_case_map,
};
ENC_ALIAS("ISO8859-11", "ISO-8859-11")
diff --git a/enc/iso_8859_13.c b/enc/iso_8859_13.c
index 6e49e16dfb..fe1ddd7065 100644
--- a/enc/iso_8859_13.c
+++ b/enc/iso_8859_13.c
@@ -208,9 +208,9 @@ apply_all_case_fold(OnigCaseFoldType flag,
static int
get_case_fold_codes_by_str(OnigCaseFoldType flag,
- const OnigUChar* p, const OnigUChar* end,
- OnigCaseFoldCodeItem items[],
- OnigEncoding enc ARG_UNUSED)
+ const OnigUChar* p, const OnigUChar* end,
+ OnigCaseFoldCodeItem items[],
+ OnigEncoding enc ARG_UNUSED)
{
return onigenc_get_case_fold_codes_by_str_with_map(
numberof(CaseFoldMap), CaseFoldMap, 1,
@@ -218,38 +218,39 @@ get_case_fold_codes_by_str(OnigCaseFoldType flag,
}
static int
-case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==SHARP_s) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == SHARP_s) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 'S';
- code = (flags&ONIGENC_CASE_TITLECASE) ? 's' : 'S';
+ code = (flags & ONIGENC_CASE_TITLECASE) ? 's' : 'S';
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 's';
code = 's';
}
}
else if ((EncISO_8859_13_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
code = ENC_ISO_8859_13_TO_LOWER_CASE(code);
}
- else if (code==0xB5) ;
- else if ((EncISO_8859_13_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ else if (code == 0xB5)
+ ;
+ else if ((EncISO_8859_13_CtypeTable[code] & BIT_CTYPE_LOWER)
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code==0xB8 || code==0xBA || code==0xBF) {
+ if (code == 0xB8 || code == 0xBA || code == 0xBF) {
code -= 0x10;
}
else {
@@ -257,11 +258,11 @@ case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
}
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(iso_8859_13, ISO_8859_13) = {
@@ -281,8 +282,8 @@ OnigEncodingDefine(iso_8859_13, ISO_8859_13) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
ENC_ALIAS("ISO8859-13", "ISO-8859-13")
diff --git a/enc/iso_8859_14.c b/enc/iso_8859_14.c
index 22df367dd9..647514a016 100644
--- a/enc/iso_8859_14.c
+++ b/enc/iso_8859_14.c
@@ -217,9 +217,9 @@ apply_all_case_fold(OnigCaseFoldType flag,
static int
get_case_fold_codes_by_str(OnigCaseFoldType flag,
- const OnigUChar* p, const OnigUChar* end,
- OnigCaseFoldCodeItem items[],
- OnigEncoding enc ARG_UNUSED)
+ const OnigUChar* p, const OnigUChar* end,
+ OnigCaseFoldCodeItem items[],
+ OnigEncoding enc ARG_UNUSED)
{
return onigenc_get_case_fold_codes_by_str_with_map(
numberof(CaseFoldMap), CaseFoldMap, 1,
@@ -227,58 +227,58 @@ get_case_fold_codes_by_str(OnigCaseFoldType flag,
}
static int
-case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==SHARP_s) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == SHARP_s) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 'S';
- code = (flags&ONIGENC_CASE_TITLECASE) ? 's' : 'S';
+ code = (flags & ONIGENC_CASE_TITLECASE) ? 's' : 'S';
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 's';
code = 's';
}
}
- /* else if (code==0xAA || code==0xBA) ; */
+ /* else if (code == 0xAA || code == 0xBA) ; */
else if ((EncISO_8859_14_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
code = ENC_ISO_8859_14_TO_LOWER_CASE(code);
}
- else if ((EncISO_8859_14_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ else if ((EncISO_8859_14_CtypeTable[code] & BIT_CTYPE_LOWER)
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if(code == 0xA2 || code == 0xA5 || code == 0xB1 || code == 0xB3 || code == 0xB5 || code == 0xBE)
+ if (code == 0xA2 || code == 0xA5 || code == 0xB1 || code == 0xB3 || code == 0xB5 || code == 0xBE)
code -= 0x1;
- else if(code == 0xAB)
+ else if (code == 0xAB)
code -= 0x5;
- else if(code == 0xFF)
+ else if (code == 0xFF)
code -= 0x50;
- else if(code == 0xB9)
+ else if (code == 0xB9)
code -= 0x2;
- else if(code == 0xBF)
+ else if (code == 0xBF)
code -= 0x4;
- else if(code == 0xB8 || code == 0xBA || code == 0xBC)
+ else if (code == 0xB8 || code == 0xBA || code == 0xBC)
code -= 0x10;
else
code -= 0x20;
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(iso_8859_14, ISO_8859_14) = {
@@ -298,8 +298,8 @@ OnigEncodingDefine(iso_8859_14, ISO_8859_14) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
ENC_ALIAS("ISO8859-14", "ISO-8859-14")
diff --git a/enc/iso_8859_15.c b/enc/iso_8859_15.c
index 06b00b90b3..377a3afc7b 100644
--- a/enc/iso_8859_15.c
+++ b/enc/iso_8859_15.c
@@ -211,9 +211,9 @@ apply_all_case_fold(OnigCaseFoldType flag,
static int
get_case_fold_codes_by_str(OnigCaseFoldType flag,
- const OnigUChar* p, const OnigUChar* end,
- OnigCaseFoldCodeItem items[],
- OnigEncoding enc ARG_UNUSED)
+ const OnigUChar* p, const OnigUChar* end,
+ OnigCaseFoldCodeItem items[],
+ OnigEncoding enc ARG_UNUSED)
{
return onigenc_get_case_fold_codes_by_str_with_map(
numberof(CaseFoldMap), CaseFoldMap, 1,
@@ -221,54 +221,55 @@ get_case_fold_codes_by_str(OnigCaseFoldType flag,
}
static int
-case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==SHARP_s) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == SHARP_s) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 'S';
- code = (flags&ONIGENC_CASE_TITLECASE) ? 's' : 'S';
+ code = (flags & ONIGENC_CASE_TITLECASE) ? 's' : 'S';
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 's';
code = 's';
}
}
- else if (code==0xAA || code==0xBA || code==0xB5) ;
+ else if (code == 0xAA || code == 0xBA || code == 0xB5)
+ ;
else if ((EncISO_8859_15_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
code = ENC_ISO_8859_15_TO_LOWER_CASE(code);
}
- else if ((EncISO_8859_15_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ else if ((EncISO_8859_15_CtypeTable[code] & BIT_CTYPE_LOWER)
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code==0xA8)
+ if (code == 0xA8)
code -= 2;
- else if (code==0xB8)
+ else if (code == 0xB8)
code -= 4;
- else if (code==0xBD)
+ else if (code == 0xBD)
code -= 1;
- else if (code==0xFF)
+ else if (code == 0xFF)
code -= 0x41;
else
code -= 0x20;
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(iso_8859_15, ISO_8859_15) = {
@@ -288,8 +289,8 @@ OnigEncodingDefine(iso_8859_15, ISO_8859_15) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
ENC_ALIAS("ISO8859-15", "ISO-8859-15")
diff --git a/enc/iso_8859_16.c b/enc/iso_8859_16.c
index c8695e65f7..135630eb73 100644
--- a/enc/iso_8859_16.c
+++ b/enc/iso_8859_16.c
@@ -213,9 +213,9 @@ apply_all_case_fold(OnigCaseFoldType flag,
static int
get_case_fold_codes_by_str(OnigCaseFoldType flag,
- const OnigUChar* p, const OnigUChar* end,
- OnigCaseFoldCodeItem items[],
- OnigEncoding enc ARG_UNUSED)
+ const OnigUChar* p, const OnigUChar* end,
+ OnigCaseFoldCodeItem items[],
+ OnigEncoding enc ARG_UNUSED)
{
return onigenc_get_case_fold_codes_by_str_with_map(
numberof(CaseFoldMap), CaseFoldMap, 1,
@@ -223,57 +223,57 @@ get_case_fold_codes_by_str(OnigCaseFoldType flag,
}
static int
-case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==SHARP_s) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == SHARP_s) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 'S';
- code = (flags&ONIGENC_CASE_TITLECASE) ? 's' : 'S';
+ code = (flags & ONIGENC_CASE_TITLECASE) ? 's' : 'S';
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 's';
code = 's';
}
}
else if ((EncISO_8859_16_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
code = ENC_ISO_8859_16_TO_LOWER_CASE(code);
}
- else if ((EncISO_8859_16_CtypeTable[code]&BIT_CTYPE_LOWER)
+ else if ((EncISO_8859_16_CtypeTable[code] & BIT_CTYPE_LOWER)
&& (flags&ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code==0xA2 || code==0xBD)
+ if (code == 0xA2 || code == 0xBD)
code--;
- else if (code==0xB3 || code==0xBA || code==0xBF)
+ else if (code == 0xB3 || code == 0xBA || code == 0xBF)
code -= 0x10;
- else if (code==0xA8 || code==0xAE)
+ else if (code == 0xA8 || code == 0xAE)
code -= 0x02;
- else if (code==0xB9)
+ else if (code == 0xB9)
code -= 0x07;
- else if (code==0xB8)
+ else if (code == 0xB8)
code -= 0x04;
- else if (code==0xFF)
+ else if (code == 0xFF)
code -= 0x41;
else
code -= 0x20;
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(iso_8859_16, ISO_8859_16) = {
@@ -293,8 +293,8 @@ OnigEncodingDefine(iso_8859_16, ISO_8859_16) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
ENC_ALIAS("ISO8859-16", "ISO-8859-16")
diff --git a/enc/iso_8859_2.c b/enc/iso_8859_2.c
index 00de8ec757..3a05c6320d 100644
--- a/enc/iso_8859_2.c
+++ b/enc/iso_8859_2.c
@@ -221,50 +221,50 @@ is_code_ctype(OnigCodePoint code, unsigned int ctype, OnigEncoding enc ARG_UNUSE
}
static int
-case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==SHARP_s) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == SHARP_s) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 'S';
- code = (flags&ONIGENC_CASE_TITLECASE) ? 's' : 'S';
+ code = (flags & ONIGENC_CASE_TITLECASE) ? 's' : 'S';
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 's';
code = 's';
}
}
else if ((EncISO_8859_2_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
code = ENC_ISO_8859_2_TO_LOWER_CASE(code);
}
- else if ((EncISO_8859_2_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
- if (code>=0xB1 && code<=0xBF){
+ else if ((EncISO_8859_2_CtypeTable[code] & BIT_CTYPE_LOWER)
+ && (flags & ONIGENC_CASE_UPCASE)) {
+ if (code >= 0xB1 && code <= 0xBF) {
flags |= ONIGENC_CASE_MODIFIED;
code -= 0x10;
}
- else{
+ else {
flags |= ONIGENC_CASE_MODIFIED;
code -= 0x20;
}
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(iso_8859_2, ISO_8859_2) = {
@@ -284,8 +284,8 @@ OnigEncodingDefine(iso_8859_2, ISO_8859_2) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
ENC_ALIAS("ISO8859-2", "ISO-8859-2")
diff --git a/enc/iso_8859_3.c b/enc/iso_8859_3.c
index 365d9a77de..2a343eac63 100644
--- a/enc/iso_8859_3.c
+++ b/enc/iso_8859_3.c
@@ -223,45 +223,46 @@ get_case_fold_codes_by_str(OnigCaseFoldType flag,
#define DOTLESS_i (0xB9)
#define I_WITH_DOT_ABOVE (0xA9)
static int
-case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==SHARP_s) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == SHARP_s) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 'S';
- code = (flags&ONIGENC_CASE_TITLECASE) ? 's' : 'S';
+ code = (flags & ONIGENC_CASE_TITLECASE) ? 's' : 'S';
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 's';
code = 's';
}
}
- else if (code==0xB5) ;
+ else if (code == 0xB5)
+ ;
else if ((EncISO_8859_3_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code=='I')
- code = flags&ONIGENC_CASE_FOLD_TURKISH_AZERI ? DOTLESS_i : 'i';
+ if (code == 'I')
+ code = flags & ONIGENC_CASE_FOLD_TURKISH_AZERI ? DOTLESS_i : 'i';
else
code = ENC_ISO_8859_3_TO_LOWER_CASE(code);
}
else if ((EncISO_8859_3_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code=='i')
- code = flags&ONIGENC_CASE_FOLD_TURKISH_AZERI ? I_WITH_DOT_ABOVE : 'I';
- else if (code==DOTLESS_i)
+ if (code == 'i')
+ code = flags & ONIGENC_CASE_FOLD_TURKISH_AZERI ? I_WITH_DOT_ABOVE : 'I';
+ else if (code == DOTLESS_i)
code = 'I';
- else if (code>=0xB0 && code<=0xBF ) {
+ else if (code >= 0xB0 && code <= 0xBF) {
code -= 0x10;
}
else {
@@ -269,11 +270,11 @@ case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
}
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(iso_8859_3, ISO_8859_3) = {
@@ -293,8 +294,8 @@ OnigEncodingDefine(iso_8859_3, ISO_8859_3) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
ENC_ALIAS("ISO8859-3", "ISO-8859-3")
diff --git a/enc/iso_8859_4.c b/enc/iso_8859_4.c
index 6d27300e22..e2134e8c0b 100644
--- a/enc/iso_8859_4.c
+++ b/enc/iso_8859_4.c
@@ -232,31 +232,32 @@ case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==SHARP_s) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == SHARP_s) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 'S';
- code = (flags&ONIGENC_CASE_TITLECASE) ? 's' : 'S';
+ code = (flags & ONIGENC_CASE_TITLECASE) ? 's' : 'S';
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 's';
code = 's';
}
}
else if ((EncISO_8859_4_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
code = ENC_ISO_8859_4_TO_LOWER_CASE(code);
}
- else if (code==0xA2) ;
+ else if (code == 0xA2)
+ ;
else if ((EncISO_8859_4_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code>=0xA0&&code<=0xBF) {
- if (code==0xBF)
+ if (code >= 0xA0 && code <= 0xBF) {
+ if (code == 0xBF)
code -= 0x02;
else
code -= 0x10;
@@ -265,11 +266,11 @@ case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
code -= 0x20;
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(iso_8859_4, ISO_8859_4) = {
@@ -289,8 +290,8 @@ OnigEncodingDefine(iso_8859_4, ISO_8859_4) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
ENC_ALIAS("ISO8859-4", "ISO-8859-4")
diff --git a/enc/iso_8859_5.c b/enc/iso_8859_5.c
index 5d67639f5e..6fafc35823 100644
--- a/enc/iso_8859_5.c
+++ b/enc/iso_8859_5.c
@@ -210,35 +210,35 @@ get_case_fold_codes_by_str(OnigCaseFoldType flag,
}
static int
-case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
if ((EncISO_8859_5_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
code = ENC_ISO_8859_5_TO_LOWER_CASE(code);
}
else if ((EncISO_8859_5_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if (0xF1<=code && code<=0xFF)
+ if (0xF1 <= code && code <= 0xFF)
code -= 0x50;
else
code -= 0x20;
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(iso_8859_5, ISO_8859_5) = {
@@ -258,8 +258,8 @@ OnigEncodingDefine(iso_8859_5, ISO_8859_5) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
ENC_ALIAS("ISO8859-5", "ISO-8859-5")
diff --git a/enc/iso_8859_6.c b/enc/iso_8859_6.c
index 64dc5aceac..6d852ac8c0 100644
--- a/enc/iso_8859_6.c
+++ b/enc/iso_8859_6.c
@@ -93,9 +93,9 @@ OnigEncodingDefine(iso_8859_6, ISO_8859_6) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ onigenc_single_byte_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_single_byte_ascii_only_case_map,
};
ENC_ALIAS("ISO8859-6", "ISO-8859-6")
diff --git a/enc/iso_8859_7.c b/enc/iso_8859_7.c
index 475fecc19c..ac973f74ba 100644
--- a/enc/iso_8859_7.c
+++ b/enc/iso_8859_7.c
@@ -206,58 +206,58 @@ get_case_fold_codes_by_str(OnigCaseFoldType flag,
}
static int
-case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==0xF2) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == 0xF2) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
code = 0xD3;
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
code = 0xF3;
}
}
else if ((EncISO_8859_7_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
code = ENC_ISO_8859_7_TO_LOWER_CASE(code);
}
- else if (code==0xC0 || code==0xE0)
- ;
+ else if (code == 0xC0 || code == 0xE0)
+ ;
else if ((EncISO_8859_7_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code==0xDC) {
- code-=0x26;
+ if (code == 0xDC) {
+ code -= 0x26;
}
- else if (code>=0xDD && code<=0xDF) {
- code-=0x25;
+ else if (code >= 0xDD && code <= 0xDF) {
+ code -= 0x25;
}
- else if (code==0xFC) {
- code-=0x40;
+ else if (code == 0xFC) {
+ code -= 0x40;
}
- else if (code==0xFD || code==0xFE) {
- code-=0x3F;
+ else if (code == 0xFD || code == 0xFE) {
+ code -= 0x3F;
}
else {
- code-=0x20;
+ code -= 0x20;
}
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(iso_8859_7, ISO_8859_7) = {
@@ -277,8 +277,8 @@ OnigEncodingDefine(iso_8859_7, ISO_8859_7) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
ENC_ALIAS("ISO8859-7", "ISO-8859-7")
diff --git a/enc/iso_8859_8.c b/enc/iso_8859_8.c
index 4777762849..0a7a29e82e 100644
--- a/enc/iso_8859_8.c
+++ b/enc/iso_8859_8.c
@@ -93,9 +93,9 @@ OnigEncodingDefine(iso_8859_8, ISO_8859_8) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ onigenc_single_byte_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_single_byte_ascii_only_case_map,
};
ENC_ALIAS("ISO8859-8", "ISO-8859-8")
diff --git a/enc/iso_8859_9.c b/enc/iso_8859_9.c
index 064a04d480..004eec310f 100644
--- a/enc/iso_8859_9.c
+++ b/enc/iso_8859_9.c
@@ -204,9 +204,9 @@ apply_all_case_fold(OnigCaseFoldType flag,
static int
get_case_fold_codes_by_str(OnigCaseFoldType flag,
- const OnigUChar* p, const OnigUChar* end,
- OnigCaseFoldCodeItem items[],
- OnigEncoding enc ARG_UNUSED)
+ const OnigUChar* p, const OnigUChar* end,
+ OnigCaseFoldCodeItem items[],
+ OnigEncoding enc ARG_UNUSED)
{
return onigenc_get_case_fold_codes_by_str_with_map(
numberof(CaseFoldMap), CaseFoldMap, 1,
@@ -216,53 +216,54 @@ get_case_fold_codes_by_str(OnigCaseFoldType flag,
#define DOTLESS_i (0xFD)
#define I_WITH_DOT_ABOVE (0xDD)
static int
-case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==SHARP_s) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == SHARP_s) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 'S';
- code = (flags&ONIGENC_CASE_TITLECASE) ? 's' : 'S';
+ code = (flags & ONIGENC_CASE_TITLECASE) ? 's' : 'S';
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 's';
code = 's';
}
}
- else if (code==0xAA || code==0xB5 || code==0xBA || code==0xFF) ;
+ else if (code == 0xAA || code == 0xB5 || code == 0xBA || code == 0xFF)
+ ;
else if ((EncISO_8859_9_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code=='I')
- code = flags&ONIGENC_CASE_FOLD_TURKISH_AZERI ? DOTLESS_i : 'i';
+ if (code == 'I')
+ code = flags & ONIGENC_CASE_FOLD_TURKISH_AZERI ? DOTLESS_i : 'i';
else
code = ENC_ISO_8859_9_TO_LOWER_CASE(code);
}
else if ((EncISO_8859_9_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code=='i')
- code = flags&ONIGENC_CASE_FOLD_TURKISH_AZERI ? I_WITH_DOT_ABOVE : 'I';
- else if (code==DOTLESS_i)
+ if (code == 'i')
+ code = flags & ONIGENC_CASE_FOLD_TURKISH_AZERI ? I_WITH_DOT_ABOVE : 'I';
+ else if (code == DOTLESS_i)
code = 'I';
else
code -= 0x20;
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(iso_8859_9, ISO_8859_9) = {
@@ -282,8 +283,8 @@ OnigEncodingDefine(iso_8859_9, ISO_8859_9) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
ENC_ALIAS("ISO8859-9", "ISO-8859-9")
diff --git a/enc/jis/props.h b/enc/jis/props.h
deleted file mode 100644
index 4ae2e1fd23..0000000000
--- a/enc/jis/props.h
+++ /dev/null
@@ -1,227 +0,0 @@
-/* C code produced by gperf version 3.0.4 */
-/* Command-line: gperf -k1,3 -7 -c -j1 -i1 -t -C -P -t --ignore-case -H onig_jis_property_hash -Q onig_jis_property_pool -N onig_jis_property enc/jis/props.kwd */
-
-#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
- && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
- && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
- && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
- && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
- && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
- && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
- && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
- && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
- && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
- && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
- && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
- && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
- && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
- && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
- && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
- && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
- && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
- && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
- && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
- && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
- && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
- && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
-/* The character set is not based on ISO-646. */
-error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
-#endif
-
-#line 1 "enc/jis/props.kwd"
-/* -*- c -*- */
-#define GPERF_DOWNCASE 1
-#define GPERF_CASE_STRNCMP 1
-
-static inline int
-gperf_case_strncmp(const char *s1, const char *s2, unsigned int n)
-{
- const UChar *str = (const UChar *)s1;
- const UChar *s = (const UChar *)s2;
- return onigenc_with_ascii_strnicmp(ONIG_ENCODING_ASCII, str, str + n, s, n);
-}
-
-enum onigenc_jis_ctype {
- onigenc_jis_min = ONIGENC_MAX_STD_CTYPE,
- onigenc_jis_hiragana,
- onigenc_jis_katakana,
- onigenc_jis_han,
- onigenc_jis_latin,
- onigenc_jis_greek,
- onigenc_jis_cyrillic,
- onigenc_jis_max
-};
-
-enum {PropertyListNum = onigenc_jis_max - onigenc_jis_min - 1};
-
-static const OnigCodePoint* const PropertyList[PropertyListNum] = {
- CR_Hiragana,
- CR_Katakana,
- CR_Han,
- CR_Latin,
- CR_Greek,
- CR_Cyrillic,
-};
-
-struct enc_property {
- signed char name;
- unsigned char ctype;
-};
-
-static const struct enc_property *onig_jis_property(const char *str, unsigned int len);
-#line 43 "enc/jis/props.kwd"
-struct enc_property;
-
-#define TOTAL_KEYWORDS 6
-#define MIN_WORD_LENGTH 3
-#define MAX_WORD_LENGTH 8
-#define MIN_HASH_VALUE 5
-#define MAX_HASH_VALUE 12
-/* maximum key range = 8, duplicates = 0 */
-
-#ifndef GPERF_DOWNCASE
-#define GPERF_DOWNCASE 1
-static unsigned char gperf_downcase[256] =
- {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
- 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, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
- 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
- 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
- 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
- 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
- 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
- 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
- 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
- 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
- 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
- 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
- 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
- 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
- 255
- };
-#endif
-
-#ifndef GPERF_CASE_STRNCMP
-#define GPERF_CASE_STRNCMP 1
-static int
-gperf_case_strncmp (s1, s2, n)
- register const char *s1;
- register const char *s2;
- register unsigned int n;
-{
- for (; n > 0;)
- {
- unsigned char c1 = gperf_downcase[(unsigned char)*s1++];
- unsigned char c2 = gperf_downcase[(unsigned char)*s2++];
- if (c1 != 0 && c1 == c2)
- {
- n--;
- continue;
- }
- return (int)c1 - (int)c2;
- }
- return 0;
-}
-#endif
-
-#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) || defined(__GNUC_STDC_INLINE__)
-inline
-#elif defined(__GNUC__)
-__inline
-#endif
-static unsigned int
-onig_jis_property_hash (str, len)
- register const char *str;
- register unsigned int len;
-{
- static const unsigned char asso_values[] =
- {
- 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- 13, 13, 13, 13, 13, 13, 13, 3, 13, 2,
- 13, 1, 1, 13, 13, 2, 1, 13, 1, 13,
- 13, 13, 1, 13, 1, 13, 13, 13, 13, 13,
- 13, 13, 13, 13, 13, 13, 13, 13, 13, 3,
- 13, 2, 13, 1, 1, 13, 13, 2, 1, 13,
- 1, 13, 13, 13, 1, 13, 1, 13, 13, 13,
- 13, 13, 13, 13, 13, 13, 13, 13
- };
- return len + asso_values[(unsigned char)str[2]] + asso_values[(unsigned char)str[0]];
-}
-
-struct onig_jis_property_pool_t
- {
- char onig_jis_property_pool_str5[sizeof("han")];
- char onig_jis_property_pool_str7[sizeof("latin")];
- char onig_jis_property_pool_str8[sizeof("greek")];
- char onig_jis_property_pool_str10[sizeof("hiragana")];
- char onig_jis_property_pool_str11[sizeof("katakana")];
- char onig_jis_property_pool_str12[sizeof("cyrillic")];
- };
-static const struct onig_jis_property_pool_t onig_jis_property_pool_contents =
- {
- "han",
- "latin",
- "greek",
- "hiragana",
- "katakana",
- "cyrillic"
- };
-#define onig_jis_property_pool ((const char *) &onig_jis_property_pool_contents)
-#ifdef __GNUC__
-__inline
-#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
-__attribute__ ((__gnu_inline__))
-#endif
-#endif
-const struct enc_property *
-onig_jis_property (str, len)
- register const char *str;
- register unsigned int len;
-{
- static const struct enc_property wordlist[] =
- {
- {-1}, {-1}, {-1}, {-1}, {-1},
-#line 48 "enc/jis/props.kwd"
- {(char)offsetof(struct onig_jis_property_pool_t, onig_jis_property_pool_str5), onigenc_jis_han},
- {-1},
-#line 49 "enc/jis/props.kwd"
- {(char)offsetof(struct onig_jis_property_pool_t, onig_jis_property_pool_str7), onigenc_jis_latin},
-#line 50 "enc/jis/props.kwd"
- {(char)offsetof(struct onig_jis_property_pool_t, onig_jis_property_pool_str8), onigenc_jis_greek},
- {-1},
-#line 46 "enc/jis/props.kwd"
- {(char)offsetof(struct onig_jis_property_pool_t, onig_jis_property_pool_str10), onigenc_jis_hiragana},
-#line 47 "enc/jis/props.kwd"
- {(char)offsetof(struct onig_jis_property_pool_t, onig_jis_property_pool_str11), onigenc_jis_katakana},
-#line 51 "enc/jis/props.kwd"
- {(char)offsetof(struct onig_jis_property_pool_t, onig_jis_property_pool_str12), onigenc_jis_cyrillic}
- };
-
- if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
- {
- register int key = onig_jis_property_hash (str, len);
-
- if (key <= MAX_HASH_VALUE && key >= 0)
- {
- register int o = wordlist[key].name;
- if (o >= 0)
- {
- register const char *s = o + onig_jis_property_pool;
-
- if ((((unsigned char)*str ^ (unsigned char)*s) & ~32) == 0 && !gperf_case_strncmp (str, s, len) && s[len] == '\0')
- return &wordlist[key];
- }
- }
- }
- return 0;
-}
-#line 52 "enc/jis/props.kwd"
-
diff --git a/enc/jis/props.h.blt b/enc/jis/props.h.blt
index 4ae2e1fd23..54aa94f8bc 100644
--- a/enc/jis/props.h.blt
+++ b/enc/jis/props.h.blt
@@ -1,4 +1,4 @@
-/* C code produced by gperf version 3.0.4 */
+/* ANSI-C code produced by gperf version 3.1 */
/* Command-line: gperf -k1,3 -7 -c -j1 -i1 -t -C -P -t --ignore-case -H onig_jis_property_hash -Q onig_jis_property_pool -N onig_jis_property enc/jis/props.kwd */
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
@@ -25,20 +25,21 @@
&& ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
&& ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
/* The character set is not based on ISO-646. */
-error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#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 "enc/jis/props.kwd"
/* -*- c -*- */
#define GPERF_DOWNCASE 1
#define GPERF_CASE_STRNCMP 1
static inline int
-gperf_case_strncmp(const char *s1, const char *s2, unsigned int n)
+gperf_case_strncmp(const char *s1, const char *s2, size_t n)
{
const UChar *str = (const UChar *)s1;
const UChar *s = (const UChar *)s2;
- return onigenc_with_ascii_strnicmp(ONIG_ENCODING_ASCII, str, str + n, s, n);
+ return onigenc_with_ascii_strnicmp(ONIG_ENCODING_ASCII, str, str + n, s, (int)n);
}
enum onigenc_jis_ctype {
@@ -68,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(/*const char *str, unsigned int len*/);
#line 43 "enc/jis/props.kwd"
struct enc_property;
@@ -107,10 +108,7 @@ static unsigned char gperf_downcase[256] =
#ifndef GPERF_CASE_STRNCMP
#define GPERF_CASE_STRNCMP 1
static int
-gperf_case_strncmp (s1, s2, n)
- register const char *s1;
- register const char *s2;
- register unsigned int n;
+gperf_case_strncmp (register const char *s1, register const char *s2, register size_t n)
{
for (; n > 0;)
{
@@ -127,15 +125,15 @@ gperf_case_strncmp (s1, s2, n)
}
#endif
-#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) || defined(__GNUC_STDC_INLINE__)
-inline
-#elif defined(__GNUC__)
+#ifdef __GNUC__
__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
#endif
static unsigned int
-onig_jis_property_hash (str, len)
- register const char *str;
- register unsigned int len;
+onig_jis_property_hash (register const char *str, register size_t len)
{
static const unsigned char asso_values[] =
{
@@ -153,7 +151,7 @@ onig_jis_property_hash (str, len)
1, 13, 13, 13, 1, 13, 1, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13
};
- return len + asso_values[(unsigned char)str[2]] + asso_values[(unsigned char)str[0]];
+ return (unsigned int)len + asso_values[(unsigned char)str[2]] + asso_values[(unsigned char)str[0]];
}
struct onig_jis_property_pool_t
@@ -175,41 +173,33 @@ static const struct onig_jis_property_pool_t onig_jis_property_pool_contents =
"cyrillic"
};
#define onig_jis_property_pool ((const char *) &onig_jis_property_pool_contents)
-#ifdef __GNUC__
-__inline
-#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
-__attribute__ ((__gnu_inline__))
-#endif
-#endif
const struct enc_property *
-onig_jis_property (str, len)
- register const char *str;
- register unsigned int len;
+onig_jis_property (register const char *str, register size_t len)
{
static const struct enc_property wordlist[] =
{
{-1}, {-1}, {-1}, {-1}, {-1},
#line 48 "enc/jis/props.kwd"
- {(char)offsetof(struct onig_jis_property_pool_t, onig_jis_property_pool_str5), onigenc_jis_han},
+ {gperf_offsetof(onig_jis_property_pool, 5), onigenc_jis_han},
{-1},
#line 49 "enc/jis/props.kwd"
- {(char)offsetof(struct onig_jis_property_pool_t, onig_jis_property_pool_str7), onigenc_jis_latin},
+ {gperf_offsetof(onig_jis_property_pool, 7), onigenc_jis_latin},
#line 50 "enc/jis/props.kwd"
- {(char)offsetof(struct onig_jis_property_pool_t, onig_jis_property_pool_str8), onigenc_jis_greek},
+ {gperf_offsetof(onig_jis_property_pool, 8), onigenc_jis_greek},
{-1},
#line 46 "enc/jis/props.kwd"
- {(char)offsetof(struct onig_jis_property_pool_t, onig_jis_property_pool_str10), onigenc_jis_hiragana},
+ {gperf_offsetof(onig_jis_property_pool, 10), onigenc_jis_hiragana},
#line 47 "enc/jis/props.kwd"
- {(char)offsetof(struct onig_jis_property_pool_t, onig_jis_property_pool_str11), onigenc_jis_katakana},
+ {gperf_offsetof(onig_jis_property_pool, 11), onigenc_jis_katakana},
#line 51 "enc/jis/props.kwd"
- {(char)offsetof(struct onig_jis_property_pool_t, onig_jis_property_pool_str12), onigenc_jis_cyrillic}
+ {gperf_offsetof(onig_jis_property_pool, 12), onigenc_jis_cyrillic}
};
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
{
- register int key = onig_jis_property_hash (str, len);
+ register unsigned int key = onig_jis_property_hash (str, len);
- if (key <= MAX_HASH_VALUE && key >= 0)
+ if (key <= MAX_HASH_VALUE)
{
register int o = wordlist[key].name;
if (o >= 0)
diff --git a/enc/jis/props.kwd b/enc/jis/props.kwd
index f3235c0100..659cf0aff4 100644
--- a/enc/jis/props.kwd
+++ b/enc/jis/props.kwd
@@ -3,11 +3,11 @@
#define GPERF_CASE_STRNCMP 1
static inline int
-gperf_case_strncmp(const char *s1, const char *s2, unsigned int n)
+gperf_case_strncmp(const char *s1, const char *s2, size_t n)
{
const UChar *str = (const UChar *)s1;
const UChar *s = (const UChar *)s2;
- return onigenc_with_ascii_strnicmp(ONIG_ENCODING_ASCII, str, str + n, s, n);
+ return onigenc_with_ascii_strnicmp(ONIG_ENCODING_ASCII, str, str + n, s, (int)n);
}
enum onigenc_jis_ctype {
@@ -37,7 +37,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(/*!ANSI{*/const char *str, unsigned int len/*}!ANSI*/);
%}
struct enc_property;
diff --git a/enc/jis/props.src b/enc/jis/props.src
index f3235c0100..659cf0aff4 100644
--- a/enc/jis/props.src
+++ b/enc/jis/props.src
@@ -3,11 +3,11 @@
#define GPERF_CASE_STRNCMP 1
static inline int
-gperf_case_strncmp(const char *s1, const char *s2, unsigned int n)
+gperf_case_strncmp(const char *s1, const char *s2, size_t n)
{
const UChar *str = (const UChar *)s1;
const UChar *s = (const UChar *)s2;
- return onigenc_with_ascii_strnicmp(ONIG_ENCODING_ASCII, str, str + n, s, n);
+ return onigenc_with_ascii_strnicmp(ONIG_ENCODING_ASCII, str, str + n, s, (int)n);
}
enum onigenc_jis_ctype {
@@ -37,7 +37,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(/*!ANSI{*/const char *str, unsigned int len/*}!ANSI*/);
%}
struct enc_property;
diff --git a/enc/koi8_r.c b/enc/koi8_r.c
index a3c05cd27b..a520975774 100644
--- a/enc/koi8_r.c
+++ b/enc/koi8_r.c
@@ -214,9 +214,8 @@ OnigEncodingDefine(koi8_r, KOI8_R) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ onigenc_single_byte_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_single_byte_ascii_only_case_map,
};
ENC_ALIAS("CP878", "KOI8-R")
-
diff --git a/enc/koi8_u.c b/enc/koi8_u.c
index f97d74d3f0..50bb78bd04 100644
--- a/enc/koi8_u.c
+++ b/enc/koi8_u.c
@@ -218,7 +218,7 @@ OnigEncodingDefine(koi8_u, KOI8_U) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ onigenc_single_byte_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_single_byte_ascii_only_case_map,
};
diff --git a/enc/make_encmake.rb b/enc/make_encmake.rb
index 4ab85f36e3..bc0597e3f4 100755
--- a/enc/make_encmake.rb
+++ b/enc/make_encmake.rb
@@ -121,7 +121,11 @@ ENCS, ENC_DEPS = target_encodings
ATRANS, TRANS = target_transcoders
if File.exist?(depend = File.join($srcdir, "depend"))
- erb = ERB.new(File.read(depend), nil, '%')
+ if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
+ erb = ERB.new(File.read(depend), trim_mode: '%')
+ else
+ erb = ERB.new(File.read(depend), nil, '%')
+ end
erb.filename = depend
tmp = erb.result(binding)
dep = "\n#### depend ####\n\n" << depend_rules(tmp).join
@@ -135,7 +139,11 @@ open(ARGV[0], 'wb') {|f|
}
if MODULE_TYPE == :static
filename = "encinit.c.erb"
- erb = ERB.new(File.read(File.join($srcdir, filename)), nil, '%-')
+ if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
+ erb = ERB.new(File.read(File.join($srcdir, filename)), trim_mode: '%-')
+ else
+ erb = ERB.new(File.read(File.join($srcdir, filename)), nil, '%-')
+ end
erb.filename = "enc/#{filename}"
tmp = erb.result(binding)
begin
diff --git a/enc/mktable.c b/enc/mktable.c
index 49acf628d0..4edd5a0ff7 100644
--- a/enc/mktable.c
+++ b/enc/mktable.c
@@ -2,7 +2,7 @@
mktable.c
**********************************************************************/
/*-
- * Copyright (c) 2002-2007 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
+ * Copyright (c) 2002-2016 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,7 +31,10 @@
#include <stdio.h>
#include <locale.h>
+#ifndef __USE_ISOC99
#define __USE_ISOC99
+#endif
+
#include <ctype.h>
#include "regenc.h"
@@ -1108,11 +1111,13 @@ static int exec(FILE* fp, ENC_INFO* einfo)
#define NCOL 8
int c, val, enc;
+ int r;
enc = einfo->num;
- fprintf(fp, "static const unsigned short Enc%s_CtypeTable[256] = {\n",
- einfo->name);
+ r = fprintf(fp, "static const unsigned short Enc%s_CtypeTable[256] = {\n",
+ einfo->name);
+ if (r < 0) return -1;
for (c = 0; c < 256; c++) {
val = 0;
@@ -1131,20 +1136,33 @@ static int exec(FILE* fp, ENC_INFO* einfo)
if (IsWord (enc, c)) val |= BIT_CTYPE_WORD;
if (IsAscii (enc, c)) val |= BIT_CTYPE_ASCII;
- if (c % NCOL == 0) fputs(" ", fp);
- fprintf(fp, "0x%04x", val);
- if (c != 255) fputs(",", fp);
+ if (c % NCOL == 0) {
+ r = fputs(" ", fp);
+ if (r < 0) return -1;
+ }
+ r = fprintf(fp, "0x%04x", val);
+ if (r < 0) return -1;
+
+ if (c != 255) {
+ r = fputs(",", fp);
+ if (r < 0) return -1;
+ }
if (c != 0 && c % NCOL == (NCOL-1))
- fputs("\n", fp);
+ r = fputs("\n", fp);
else
- fputs(" ", fp);
+ r = fputs(" ", fp);
+
+ if (r < 0) return -1;
}
- fprintf(fp, "};\n");
+ r = fprintf(fp, "};\n");
+ if (r < 0) return -1;
+
return 0;
}
extern int main(int argc ARG_UNUSED, char* argv[] ARG_UNUSED)
{
+ int r;
int i;
FILE* fp = stdout;
@@ -1155,7 +1173,11 @@ extern int main(int argc ARG_UNUSED, char* argv[] ARG_UNUSED)
/* setlocale(LC_ALL, "fr_FR.iso88591"); */
for (i = 0; i < (int )(sizeof(Info)/sizeof(ENC_INFO)); i++) {
- exec(fp, &Info[i]);
+ r = exec(fp, &Info[i]);
+ if (r < 0) {
+ fprintf(stderr, "FAIL exec(): %d\n", r);
+ return -1;
+ }
}
return 0;
diff --git a/enc/prelude.rb b/enc/prelude.rb
deleted file mode 100644
index be7c0c9445..0000000000
--- a/enc/prelude.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-begin
- require 'unicode_normalize'
-rescue LoadError
-end
diff --git a/enc/shift_jis.c b/enc/shift_jis.c
index c1552bfd13..65fd5102de 100644
--- a/enc/shift_jis.c
+++ b/enc/shift_jis.c
@@ -1,9 +1,9 @@
/**********************************************************************
- sjis.c - Onigmo (Oniguruma-mod) (regular expression library)
+ shift_jis.c - Onigmo (Oniguruma-mod) (regular expression library)
**********************************************************************/
/*-
- * Copyright (c) 2002-2008 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
- * Copyright (c) 2011 K.Takata <kentkt AT csc DOT jp>
+ * Copyright (c) 2002-2009 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
+ * Copyright (c) 2011-2017 K.Takata <kentkt AT csc DOT jp>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,524 +28,8 @@
* SUCH DAMAGE.
*/
-#include "regint.h"
+#include "shift_jis.h"
-static const int EncLen_SJIS[] = {
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1
-};
-
-static const char SJIS_CAN_BE_TRAIL_TABLE[256] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0
-};
-
-static const OnigPairCaseFoldCodes CaseFoldMap[] = {
- /* Fullwidth Alphabet */
- { 0x8260, 0x8281 },
- { 0x8261, 0x8282 },
- { 0x8262, 0x8283 },
- { 0x8263, 0x8284 },
- { 0x8264, 0x8285 },
- { 0x8265, 0x8286 },
- { 0x8266, 0x8287 },
- { 0x8267, 0x8288 },
- { 0x8268, 0x8289 },
- { 0x8269, 0x828a },
- { 0x826a, 0x828b },
- { 0x826b, 0x828c },
- { 0x826c, 0x828d },
- { 0x826d, 0x828e },
- { 0x826e, 0x828f },
- { 0x826f, 0x8290 },
- { 0x8270, 0x8291 },
- { 0x8271, 0x8292 },
- { 0x8272, 0x8293 },
- { 0x8273, 0x8294 },
- { 0x8274, 0x8295 },
- { 0x8275, 0x8296 },
- { 0x8276, 0x8297 },
- { 0x8277, 0x8298 },
- { 0x8278, 0x8299 },
- { 0x8279, 0x829a },
-
- /* Greek */
- { 0x839f, 0x83bf },
- { 0x83a0, 0x83c0 },
- { 0x83a1, 0x83c1 },
- { 0x83a2, 0x83c2 },
- { 0x83a3, 0x83c3 },
- { 0x83a4, 0x83c4 },
- { 0x83a5, 0x83c5 },
- { 0x83a6, 0x83c6 },
- { 0x83a7, 0x83c7 },
- { 0x83a8, 0x83c8 },
- { 0x83a9, 0x83c9 },
- { 0x83aa, 0x83ca },
- { 0x83ab, 0x83cb },
- { 0x83ac, 0x83cc },
- { 0x83ad, 0x83cd },
- { 0x83ae, 0x83ce },
- { 0x83af, 0x83cf },
- { 0x83b0, 0x83d0 },
- { 0x83b1, 0x83d1 },
- { 0x83b2, 0x83d2 },
- { 0x83b3, 0x83d3 },
- { 0x83b4, 0x83d4 },
- { 0x83b5, 0x83d5 },
- { 0x83b6, 0x83d6 },
-
- /* Cyrillic */
- { 0x8440, 0x8470 },
- { 0x8441, 0x8471 },
- { 0x8442, 0x8472 },
- { 0x8443, 0x8473 },
- { 0x8444, 0x8474 },
- { 0x8445, 0x8475 },
- { 0x8446, 0x8476 },
- { 0x8447, 0x8477 },
- { 0x8448, 0x8478 },
- { 0x8449, 0x8479 },
- { 0x844a, 0x847a },
- { 0x844b, 0x847b },
- { 0x844c, 0x847c },
- { 0x844d, 0x847d },
- { 0x844e, 0x847e },
- { 0x844f, 0x8480 },
- { 0x8450, 0x8481 },
- { 0x8451, 0x8482 },
- { 0x8452, 0x8483 },
- { 0x8453, 0x8484 },
- { 0x8454, 0x8485 },
- { 0x8455, 0x8486 },
- { 0x8456, 0x8487 },
- { 0x8457, 0x8488 },
- { 0x8458, 0x8489 },
- { 0x8459, 0x848a },
- { 0x845a, 0x848b },
- { 0x845b, 0x848c },
- { 0x845c, 0x848d },
- { 0x845d, 0x848e },
- { 0x845e, 0x848f },
- { 0x845f, 0x8490 },
- { 0x8460, 0x8491 },
-};
-
-#define SJIS_ISMB_FIRST(byte) (EncLen_SJIS[byte] > 1)
-#define SJIS_ISMB_TRAIL(byte) SJIS_CAN_BE_TRAIL_TABLE[(byte)]
-
-typedef enum { FAILURE = -2, ACCEPT = -1, S0 = 0, S1 } state_t;
-#define A ACCEPT
-#define F FAILURE
-static const signed char trans[][0x100] = {
- { /* S0 0 1 2 3 4 5 6 7 8 9 a b c d e f */
- /* 0 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* 1 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* 2 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* 3 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* 4 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* 5 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* 6 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* 7 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* 8 */ F, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- /* 9 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- /* a */ F, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* b */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* c */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* d */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* e */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- /* f */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, F, F, F
- },
- { /* S1 0 1 2 3 4 5 6 7 8 9 a b c d e f */
- /* 0 */ F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F,
- /* 1 */ F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F,
- /* 2 */ F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F,
- /* 3 */ F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F,
- /* 4 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* 5 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* 6 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* 7 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, F,
- /* 8 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* 9 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* a */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* b */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* c */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* d */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* e */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
- /* f */ A, A, A, A, A, A, A, A, A, A, A, A, A, F, F, F
- }
-};
-#undef A
-#undef F
-
-static int
-mbc_enc_len(const UChar* p, const UChar* e, OnigEncoding enc ARG_UNUSED)
-{
- int firstbyte = *p++;
- state_t s;
- s = trans[0][firstbyte];
- if (s < 0) return s == ACCEPT ? ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(1) :
- ONIGENC_CONSTRUCT_MBCLEN_INVALID();
- if (p == e) return ONIGENC_CONSTRUCT_MBCLEN_NEEDMORE(EncLen_SJIS[firstbyte]-1);
- s = trans[s][*p++];
- return s == ACCEPT ? ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(2) :
- ONIGENC_CONSTRUCT_MBCLEN_INVALID();
-}
-
-static int
-code_to_mbclen(OnigCodePoint code, OnigEncoding enc ARG_UNUSED)
-{
- if (code < 256) {
- if (EncLen_SJIS[(int )code] == 1)
- return 1;
- else
- return ONIGERR_INVALID_CODE_POINT_VALUE;
- }
- else if (code <= 0xffff) {
- int low = code & 0xff;
- if (! SJIS_ISMB_TRAIL(low))
- return ONIGERR_INVALID_CODE_POINT_VALUE;
- return 2;
- }
- else
- return ONIGERR_TOO_BIG_WIDE_CHAR_VALUE;
-}
-
-static OnigCodePoint
-mbc_to_code(const UChar* p, const UChar* end, OnigEncoding enc)
-{
- int c, i, len;
- OnigCodePoint n;
-
- len = mbc_enc_len(p, end, enc);
- c = *p++;
- n = c;
- if (len == 1) return n;
-
- for (i = 1; i < len; i++) {
- if (p >= end) break;
- c = *p++;
- n <<= 8; n += c;
- }
- return n;
-}
-
-static int
-code_to_mbc(OnigCodePoint code, UChar *buf, OnigEncoding enc)
-{
- UChar *p = buf;
-
- if ((code & 0xff00) != 0) *p++ = (UChar )(((code >> 8) & 0xff));
- *p++ = (UChar )(code & 0xff);
-
-#if 0
- if (mbc_enc_len(buf, p, enc) != (p - buf))
- return REGERR_INVALID_CODE_POINT_VALUE;
-#endif
- return (int )(p - buf);
-}
-
-static int
-apply_all_case_fold(OnigCaseFoldType flag,
- OnigApplyAllCaseFoldFunc f, void* arg, OnigEncoding enc)
-{
- return onigenc_apply_all_case_fold_with_map(
- numberof(CaseFoldMap), CaseFoldMap, 0,
- flag, f, arg);
-}
-
-static OnigCodePoint
-get_lower_case(OnigCodePoint code)
-{
- if (ONIGENC_IS_IN_RANGE(code, 0x8260, 0x8279)) {
- /* Fullwidth Alphabet */
- return (OnigCodePoint )(code + 0x0021);
- }
- else if (ONIGENC_IS_IN_RANGE(code, 0x839f, 0x83b6)) {
- /* Greek */
- return (OnigCodePoint )(code + 0x0020);
- }
- else if (ONIGENC_IS_IN_RANGE(code, 0x8440, 0x8460)) {
- /* Cyrillic */
- int d = (code >= 0x844f) ? 1 : 0;
- return (OnigCodePoint )(code + (0x0030 + d));
- }
- return code;
-}
-
-static OnigCodePoint
-get_upper_case(OnigCodePoint code)
-{
- if (ONIGENC_IS_IN_RANGE(code, 0x8281, 0x829a)) {
- /* Fullwidth Alphabet */
- return (OnigCodePoint )(code - 0x0021);
- }
- else if (ONIGENC_IS_IN_RANGE(code, 0x83bf, 0x83d6)) {
- /* Greek */
- return (OnigCodePoint )(code - 0x0020);
- }
- else if (ONIGENC_IS_IN_RANGE(code, 0x8470, 0x847e) ||
- ONIGENC_IS_IN_RANGE(code, 0x8480, 0x8491)) {
- /* Cyrillic */
- int d = (code >= 0x8480) ? 1 : 0;
- return (OnigCodePoint )(code - (0x0030 - d));
- }
- return code;
-}
-
-static int
-get_case_fold_codes_by_str(OnigCaseFoldType flag,
- const OnigUChar* p, const OnigUChar* end,
- OnigCaseFoldCodeItem items[], OnigEncoding enc)
-{
- int len;
- OnigCodePoint code, code_lo, code_up;
-
- code = mbc_to_code(p, end, enc);
- if (ONIGENC_IS_ASCII_CODE(code))
- return onigenc_ascii_get_case_fold_codes_by_str(flag, p, end, items, enc);
-
- len = mbc_enc_len(p, end, enc);
- code_lo = get_lower_case(code);
- code_up = get_upper_case(code);
-
- if (code != code_lo) {
- items[0].byte_len = len;
- items[0].code_len = 1;
- items[0].code[0] = code_lo;
- return 1;
- }
- else if (code != code_up) {
- items[0].byte_len = len;
- items[0].code_len = 1;
- items[0].code[0] = code_up;
- return 1;
- }
-
- return 0;
-}
-
-static int
-mbc_case_fold(OnigCaseFoldType flag,
- const UChar** pp, const UChar* end, UChar* lower,
- OnigEncoding enc)
-{
- const UChar* p = *pp;
-
- if (ONIGENC_IS_MBC_ASCII(p)) {
- *lower = ONIGENC_ASCII_CODE_TO_LOWER_CASE(*p);
- (*pp)++;
- return 1;
- }
- else {
- OnigCodePoint code;
- int len;
-
- code = get_lower_case(mbc_to_code(p, end, enc));
- len = code_to_mbc(code, lower, enc);
- (*pp) += len;
- return len; /* return byte length of converted char to lower */
- }
-}
-
-#if 0
-static int
-is_mbc_ambiguous(OnigCaseFoldType flag,
- const UChar** pp, const UChar* end)
-{
- return onigenc_mbn_is_mbc_ambiguous(enc, flag, pp, end);
-
-}
-#endif
-
-#if 0
-static int
-is_code_ctype(OnigCodePoint code, unsigned int ctype)
-{
- if (code < 128)
- return ONIGENC_IS_ASCII_CODE_CTYPE(code, ctype);
- else {
- if (CTYPE_IS_WORD_GRAPH_PRINT(ctype)) {
- return (code_to_mbclen(code) > 1 ? TRUE : FALSE);
- }
- }
-
- return FALSE;
-}
-#endif
-
-static UChar*
-left_adjust_char_head(const UChar* start, const UChar* s, const UChar* end, OnigEncoding enc)
-{
- const UChar *p;
- int len;
-
- if (s <= start) return (UChar* )s;
- p = s;
-
- if (SJIS_ISMB_TRAIL(*p)) {
- while (p > start) {
- if (! SJIS_ISMB_FIRST(*--p)) {
- p++;
- break;
- }
- }
- }
- len = mbc_enc_len(p, end, enc);
- if (p + len > s) return (UChar* )p;
- p += len;
- return (UChar* )(p + ((s - p) & ~1));
-}
-
-static int
-is_allowed_reverse_match(const UChar* s, const UChar* end, OnigEncoding enc ARG_UNUSED)
-{
- const UChar c = *s;
- return (SJIS_ISMB_TRAIL(c) ? FALSE : TRUE);
-}
-
-
-static const OnigCodePoint CR_Hiragana[] = {
- 1,
- 0x829f, 0x82f1
-}; /* CR_Hiragana */
-
-static const OnigCodePoint CR_Katakana[] = {
- 4,
- 0x00a6, 0x00af,
- 0x00b1, 0x00dd,
- 0x8340, 0x837e,
- 0x8380, 0x8396,
-}; /* CR_Katakana */
-
-#ifdef ENC_CP932
-static const OnigCodePoint CR_Han[] = {
- 6,
- 0x8157, 0x8157,
- 0x889f, 0x9872, /* Kanji level 1 */
- 0x989f, 0x9ffc, /* Kanji level 2 */
- 0xe040, 0xeaa4, /* Kanji level 2 */
- 0xed40, 0xeeec, /* NEC-selected IBM extended characters (without symbols) */
- 0xfa5c, 0xfc4b, /* IBM extended characters (without symbols) */
-}; /* CR_Han */
-#else
-static const OnigCodePoint CR_Han[] = {
- 4,
- 0x8157, 0x8157,
- 0x889f, 0x9872, /* Kanji level 1 */
- 0x989f, 0x9ffc, /* Kanji level 2 */
- 0xe040, 0xeaa4, /* Kanji level 2 */
-}; /* CR_Han */
-#endif
-
-static const OnigCodePoint CR_Latin[] = {
- 4,
- 0x0041, 0x005a,
- 0x0061, 0x007a,
- 0x8260, 0x8279,
- 0x8281, 0x829a,
-}; /* CR_Latin */
-
-static const OnigCodePoint CR_Greek[] = {
- 2,
- 0x839f, 0x83b6,
- 0x83bf, 0x83d6,
-}; /* CR_Greek */
-
-static const OnigCodePoint CR_Cyrillic[] = {
- 3,
- 0x8440, 0x8460,
- 0x8470, 0x847f,
- 0x8480, 0x8491,
-}; /* CR_Cyrillic */
-
-#include "enc/jis/props.h"
-
-static int
-property_name_to_ctype(OnigEncoding enc, const UChar* p, const UChar* end)
-{
- const UChar *s = p, *e = end;
- const struct enc_property *prop =
- onig_jis_property((const char* )s, (unsigned int )(e - s));
-
- if (!prop) {
- return onigenc_minimum_property_name_to_ctype(enc, s, e);
- }
-
- return (int )prop->ctype;
-}
-
-static int
-is_code_ctype(OnigCodePoint code, unsigned int ctype, OnigEncoding enc)
-{
- if (ctype <= ONIGENC_MAX_STD_CTYPE) {
- if (code < 128)
- return ONIGENC_IS_ASCII_CODE_CTYPE(code, ctype);
- else {
- if (CTYPE_IS_WORD_GRAPH_PRINT(ctype)) {
- return TRUE;
- }
- }
- }
- else {
- ctype -= (ONIGENC_MAX_STD_CTYPE + 1);
- if (ctype >= (unsigned int )PropertyListNum)
- return ONIGERR_TYPE_BUG;
-
- return onig_is_in_code_range((UChar* )PropertyList[ctype], code);
- }
-
- return FALSE;
-}
-
-static int
-get_ctype_code_range(OnigCtype ctype, OnigCodePoint* sb_out,
- const OnigCodePoint* ranges[], OnigEncoding enc ARG_UNUSED)
-{
- if (ctype <= ONIGENC_MAX_STD_CTYPE) {
- return ONIG_NO_SUPPORT_CONFIG;
- }
- else {
- *sb_out = 0x80;
-
- ctype -= (ONIGENC_MAX_STD_CTYPE + 1);
- if (ctype >= (OnigCtype )PropertyListNum)
- return ONIGERR_TYPE_BUG;
-
- *ranges = PropertyList[ctype];
- return 0;
- }
-}
-
-#ifndef ENC_CP932
OnigEncodingDefine(shift_jis, Shift_JIS) = {
mbc_enc_len,
"Shift_JIS", /* name */
@@ -563,9 +47,9 @@ OnigEncodingDefine(shift_jis, Shift_JIS) = {
get_ctype_code_range,
left_adjust_char_head,
is_allowed_reverse_match,
+ onigenc_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_ascii_only_case_map,
};
/*
* Name: Shift_JIS
@@ -581,4 +65,3 @@ OnigEncodingDefine(shift_jis, Shift_JIS) = {
*/
ENC_REPLICATE("MacJapanese", "Shift_JIS")
ENC_ALIAS("MacJapan", "MacJapanese")
-#endif
diff --git a/enc/shift_jis.h b/enc/shift_jis.h
new file mode 100644
index 0000000000..d552401595
--- /dev/null
+++ b/enc/shift_jis.h
@@ -0,0 +1,546 @@
+/**********************************************************************
+ shift_jis.h - Onigmo (Oniguruma-mod) (regular expression library)
+**********************************************************************/
+/*-
+ * Copyright (c) 2002-2008 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
+ * Copyright (c) 2011 K.Takata <kentkt AT csc DOT jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "regenc.h"
+
+static const int EncLen_SJIS[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1
+};
+
+static const char SJIS_CAN_BE_TRAIL_TABLE[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0
+};
+
+static const OnigPairCaseFoldCodes CaseFoldMap[] = {
+ /* Fullwidth Alphabet */
+ { 0x8260, 0x8281 },
+ { 0x8261, 0x8282 },
+ { 0x8262, 0x8283 },
+ { 0x8263, 0x8284 },
+ { 0x8264, 0x8285 },
+ { 0x8265, 0x8286 },
+ { 0x8266, 0x8287 },
+ { 0x8267, 0x8288 },
+ { 0x8268, 0x8289 },
+ { 0x8269, 0x828a },
+ { 0x826a, 0x828b },
+ { 0x826b, 0x828c },
+ { 0x826c, 0x828d },
+ { 0x826d, 0x828e },
+ { 0x826e, 0x828f },
+ { 0x826f, 0x8290 },
+ { 0x8270, 0x8291 },
+ { 0x8271, 0x8292 },
+ { 0x8272, 0x8293 },
+ { 0x8273, 0x8294 },
+ { 0x8274, 0x8295 },
+ { 0x8275, 0x8296 },
+ { 0x8276, 0x8297 },
+ { 0x8277, 0x8298 },
+ { 0x8278, 0x8299 },
+ { 0x8279, 0x829a },
+
+ /* Greek */
+ { 0x839f, 0x83bf },
+ { 0x83a0, 0x83c0 },
+ { 0x83a1, 0x83c1 },
+ { 0x83a2, 0x83c2 },
+ { 0x83a3, 0x83c3 },
+ { 0x83a4, 0x83c4 },
+ { 0x83a5, 0x83c5 },
+ { 0x83a6, 0x83c6 },
+ { 0x83a7, 0x83c7 },
+ { 0x83a8, 0x83c8 },
+ { 0x83a9, 0x83c9 },
+ { 0x83aa, 0x83ca },
+ { 0x83ab, 0x83cb },
+ { 0x83ac, 0x83cc },
+ { 0x83ad, 0x83cd },
+ { 0x83ae, 0x83ce },
+ { 0x83af, 0x83cf },
+ { 0x83b0, 0x83d0 },
+ { 0x83b1, 0x83d1 },
+ { 0x83b2, 0x83d2 },
+ { 0x83b3, 0x83d3 },
+ { 0x83b4, 0x83d4 },
+ { 0x83b5, 0x83d5 },
+ { 0x83b6, 0x83d6 },
+
+ /* Cyrillic */
+ { 0x8440, 0x8470 },
+ { 0x8441, 0x8471 },
+ { 0x8442, 0x8472 },
+ { 0x8443, 0x8473 },
+ { 0x8444, 0x8474 },
+ { 0x8445, 0x8475 },
+ { 0x8446, 0x8476 },
+ { 0x8447, 0x8477 },
+ { 0x8448, 0x8478 },
+ { 0x8449, 0x8479 },
+ { 0x844a, 0x847a },
+ { 0x844b, 0x847b },
+ { 0x844c, 0x847c },
+ { 0x844d, 0x847d },
+ { 0x844e, 0x847e },
+ { 0x844f, 0x8480 },
+ { 0x8450, 0x8481 },
+ { 0x8451, 0x8482 },
+ { 0x8452, 0x8483 },
+ { 0x8453, 0x8484 },
+ { 0x8454, 0x8485 },
+ { 0x8455, 0x8486 },
+ { 0x8456, 0x8487 },
+ { 0x8457, 0x8488 },
+ { 0x8458, 0x8489 },
+ { 0x8459, 0x848a },
+ { 0x845a, 0x848b },
+ { 0x845b, 0x848c },
+ { 0x845c, 0x848d },
+ { 0x845d, 0x848e },
+ { 0x845e, 0x848f },
+ { 0x845f, 0x8490 },
+ { 0x8460, 0x8491 },
+};
+
+#define SJIS_ISMB_FIRST(byte) (EncLen_SJIS[byte] > 1)
+#define SJIS_ISMB_TRAIL(byte) SJIS_CAN_BE_TRAIL_TABLE[(byte)]
+
+typedef enum { FAILURE = -2, ACCEPT = -1, S0 = 0, S1 } state_t;
+#define A ACCEPT
+#define F FAILURE
+static const signed char trans[][0x100] = {
+ { /* S0 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* 0 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* 1 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* 2 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* 3 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* 4 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* 5 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* 6 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* 7 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* 8 */ F, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 9 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* a */ F, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* b */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* c */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* d */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* e */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* f */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, F, F, F
+ },
+ { /* S1 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* 0 */ F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F,
+ /* 1 */ F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F,
+ /* 2 */ F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F,
+ /* 3 */ F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F,
+ /* 4 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* 5 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* 6 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* 7 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, F,
+ /* 8 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* 9 */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* a */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* b */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* c */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* d */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* e */ A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,
+ /* f */ A, A, A, A, A, A, A, A, A, A, A, A, A, F, F, F
+ }
+};
+#undef A
+#undef F
+
+static int
+mbc_enc_len(const UChar* p, const UChar* e, OnigEncoding enc ARG_UNUSED)
+{
+ int firstbyte = *p++;
+ state_t s;
+ s = trans[0][firstbyte];
+ if (s < 0) return s == ACCEPT ? ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(1) :
+ ONIGENC_CONSTRUCT_MBCLEN_INVALID();
+ if (p == e) return ONIGENC_CONSTRUCT_MBCLEN_NEEDMORE(EncLen_SJIS[firstbyte]-1);
+ s = trans[s][*p++];
+ return s == ACCEPT ? ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(2) :
+ ONIGENC_CONSTRUCT_MBCLEN_INVALID();
+}
+
+static int
+code_to_mbclen(OnigCodePoint code, OnigEncoding enc ARG_UNUSED)
+{
+ if (code < 256) {
+ if (EncLen_SJIS[(int )code] == 1)
+ return 1;
+ else
+ return ONIGERR_INVALID_CODE_POINT_VALUE;
+ }
+ else if (code <= 0xffff) {
+ int low = code & 0xff;
+ if (! SJIS_ISMB_TRAIL(low))
+ return ONIGERR_INVALID_CODE_POINT_VALUE;
+ return 2;
+ }
+ else
+ return ONIGERR_TOO_BIG_WIDE_CHAR_VALUE;
+}
+
+static OnigCodePoint
+mbc_to_code(const UChar* p, const UChar* end, OnigEncoding enc)
+{
+ int c, i, len;
+ OnigCodePoint n;
+
+ len = mbc_enc_len(p, end, enc);
+ c = *p++;
+ n = c;
+ if (len == 1) return n;
+
+ for (i = 1; i < len; i++) {
+ if (p >= end) break;
+ c = *p++;
+ n <<= 8; n += c;
+ }
+ return n;
+}
+
+static int
+code_to_mbc(OnigCodePoint code, UChar *buf, OnigEncoding enc)
+{
+ UChar *p = buf;
+
+ if ((code & 0xff00) != 0) *p++ = (UChar )(((code >> 8) & 0xff));
+ *p++ = (UChar )(code & 0xff);
+
+#if 0
+ if (mbc_enc_len(buf, p, enc) != (p - buf))
+ return REGERR_INVALID_CODE_POINT_VALUE;
+#endif
+ return (int )(p - buf);
+}
+
+static int
+apply_all_case_fold(OnigCaseFoldType flag,
+ OnigApplyAllCaseFoldFunc f, void* arg, OnigEncoding enc)
+{
+ return onigenc_apply_all_case_fold_with_map(
+ numberof(CaseFoldMap), CaseFoldMap, 0,
+ flag, f, arg);
+}
+
+static OnigCodePoint
+get_lower_case(OnigCodePoint code)
+{
+ if (ONIGENC_IS_IN_RANGE(code, 0x8260, 0x8279)) {
+ /* Fullwidth Alphabet */
+ return (OnigCodePoint )(code + 0x0021);
+ }
+ else if (ONIGENC_IS_IN_RANGE(code, 0x839f, 0x83b6)) {
+ /* Greek */
+ return (OnigCodePoint )(code + 0x0020);
+ }
+ else if (ONIGENC_IS_IN_RANGE(code, 0x8440, 0x8460)) {
+ /* Cyrillic */
+ int d = (code >= 0x844f) ? 1 : 0;
+ return (OnigCodePoint )(code + (0x0030 + d));
+ }
+ return code;
+}
+
+static OnigCodePoint
+get_upper_case(OnigCodePoint code)
+{
+ if (ONIGENC_IS_IN_RANGE(code, 0x8281, 0x829a)) {
+ /* Fullwidth Alphabet */
+ return (OnigCodePoint )(code - 0x0021);
+ }
+ else if (ONIGENC_IS_IN_RANGE(code, 0x83bf, 0x83d6)) {
+ /* Greek */
+ return (OnigCodePoint )(code - 0x0020);
+ }
+ else if (ONIGENC_IS_IN_RANGE(code, 0x8470, 0x847e) ||
+ ONIGENC_IS_IN_RANGE(code, 0x8480, 0x8491)) {
+ /* Cyrillic */
+ int d = (code >= 0x8480) ? 1 : 0;
+ return (OnigCodePoint )(code - (0x0030 - d));
+ }
+ return code;
+}
+
+static int
+get_case_fold_codes_by_str(OnigCaseFoldType flag,
+ const OnigUChar* p, const OnigUChar* end,
+ OnigCaseFoldCodeItem items[], OnigEncoding enc)
+{
+ int len;
+ OnigCodePoint code, code_lo, code_up;
+
+ code = mbc_to_code(p, end, enc);
+ if (ONIGENC_IS_ASCII_CODE(code))
+ return onigenc_ascii_get_case_fold_codes_by_str(flag, p, end, items, enc);
+
+ len = mbc_enc_len(p, end, enc);
+ code_lo = get_lower_case(code);
+ code_up = get_upper_case(code);
+
+ if (code != code_lo) {
+ items[0].byte_len = len;
+ items[0].code_len = 1;
+ items[0].code[0] = code_lo;
+ return 1;
+ }
+ else if (code != code_up) {
+ items[0].byte_len = len;
+ items[0].code_len = 1;
+ items[0].code[0] = code_up;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+mbc_case_fold(OnigCaseFoldType flag,
+ const UChar** pp, const UChar* end, UChar* lower,
+ OnigEncoding enc)
+{
+ const UChar* p = *pp;
+
+ if (ONIGENC_IS_MBC_ASCII(p)) {
+ *lower = ONIGENC_ASCII_CODE_TO_LOWER_CASE(*p);
+ (*pp)++;
+ return 1;
+ }
+ else {
+ OnigCodePoint code;
+ int len;
+
+ code = get_lower_case(mbc_to_code(p, end, enc));
+ len = code_to_mbc(code, lower, enc);
+ (*pp) += len;
+ return len; /* return byte length of converted char to lower */
+ }
+}
+
+#if 0
+static int
+is_mbc_ambiguous(OnigCaseFoldType flag,
+ const UChar** pp, const UChar* end)
+{
+ return onigenc_mbn_is_mbc_ambiguous(enc, flag, pp, end);
+
+}
+#endif
+
+#if 0
+static int
+is_code_ctype(OnigCodePoint code, unsigned int ctype)
+{
+ if (code < 128)
+ return ONIGENC_IS_ASCII_CODE_CTYPE(code, ctype);
+ else {
+ if (CTYPE_IS_WORD_GRAPH_PRINT(ctype)) {
+ return (code_to_mbclen(code) > 1 ? TRUE : FALSE);
+ }
+ }
+
+ return FALSE;
+}
+#endif
+
+static UChar*
+left_adjust_char_head(const UChar* start, const UChar* s, const UChar* end, OnigEncoding enc)
+{
+ const UChar *p;
+ int len;
+
+ if (s <= start) return (UChar* )s;
+ p = s;
+
+ if (SJIS_ISMB_TRAIL(*p)) {
+ while (p > start) {
+ if (! SJIS_ISMB_FIRST(*--p)) {
+ p++;
+ break;
+ }
+ }
+ }
+ len = mbc_enc_len(p, end, enc);
+ if (p + len > s) return (UChar* )p;
+ p += len;
+ return (UChar* )(p + ((s - p) & ~1));
+}
+
+static int
+is_allowed_reverse_match(const UChar* s, const UChar* end, OnigEncoding enc ARG_UNUSED)
+{
+ const UChar c = *s;
+ return (SJIS_ISMB_TRAIL(c) ? FALSE : TRUE);
+}
+
+
+static const OnigCodePoint CR_Hiragana[] = {
+ 1,
+ 0x829f, 0x82f1
+}; /* CR_Hiragana */
+
+static const OnigCodePoint CR_Katakana[] = {
+ 4,
+ 0x00a6, 0x00af,
+ 0x00b1, 0x00dd,
+ 0x8340, 0x837e,
+ 0x8380, 0x8396,
+}; /* CR_Katakana */
+
+#ifdef ENC_CP932
+static const OnigCodePoint CR_Han[] = {
+ 6,
+ 0x8157, 0x8157,
+ 0x889f, 0x9872, /* Kanji level 1 */
+ 0x989f, 0x9ffc, /* Kanji level 2 */
+ 0xe040, 0xeaa4, /* Kanji level 2 */
+ 0xed40, 0xeeec, /* NEC-selected IBM extended characters (without symbols) */
+ 0xfa5c, 0xfc4b, /* IBM extended characters (without symbols) */
+}; /* CR_Han */
+#else
+static const OnigCodePoint CR_Han[] = {
+ 4,
+ 0x8157, 0x8157,
+ 0x889f, 0x9872, /* Kanji level 1 */
+ 0x989f, 0x9ffc, /* Kanji level 2 */
+ 0xe040, 0xeaa4, /* Kanji level 2 */
+}; /* CR_Han */
+#endif
+
+static const OnigCodePoint CR_Latin[] = {
+ 4,
+ 0x0041, 0x005a,
+ 0x0061, 0x007a,
+ 0x8260, 0x8279,
+ 0x8281, 0x829a,
+}; /* CR_Latin */
+
+static const OnigCodePoint CR_Greek[] = {
+ 2,
+ 0x839f, 0x83b6,
+ 0x83bf, 0x83d6,
+}; /* CR_Greek */
+
+static const OnigCodePoint CR_Cyrillic[] = {
+ 3,
+ 0x8440, 0x8460,
+ 0x8470, 0x847f,
+ 0x8480, 0x8491,
+}; /* CR_Cyrillic */
+
+#include "enc/jis/props.h"
+
+static int
+property_name_to_ctype(OnigEncoding enc, const UChar* p, const UChar* end)
+{
+ const UChar *s = p, *e = end;
+ const struct enc_property *prop =
+ onig_jis_property((const char* )s, (unsigned int )(e - s));
+
+ if (!prop) {
+ return onigenc_minimum_property_name_to_ctype(enc, s, e);
+ }
+
+ return (int )prop->ctype;
+}
+
+static int
+is_code_ctype(OnigCodePoint code, unsigned int ctype, OnigEncoding enc)
+{
+ if (ctype <= ONIGENC_MAX_STD_CTYPE) {
+ if (code < 128)
+ return ONIGENC_IS_ASCII_CODE_CTYPE(code, ctype);
+ else {
+ if (CTYPE_IS_WORD_GRAPH_PRINT(ctype)) {
+ return TRUE;
+ }
+ }
+ }
+ else {
+ ctype -= (ONIGENC_MAX_STD_CTYPE + 1);
+ if (ctype >= (unsigned int )PropertyListNum)
+ return ONIGERR_TYPE_BUG;
+
+ return onig_is_in_code_range((UChar* )PropertyList[ctype], code);
+ }
+
+ return FALSE;
+}
+
+static int
+get_ctype_code_range(OnigCtype ctype, OnigCodePoint* sb_out,
+ const OnigCodePoint* ranges[], OnigEncoding enc ARG_UNUSED)
+{
+ if (ctype <= ONIGENC_MAX_STD_CTYPE) {
+ return ONIG_NO_SUPPORT_CONFIG;
+ }
+ else {
+ *sb_out = 0x80;
+
+ ctype -= (ONIGENC_MAX_STD_CTYPE + 1);
+ if (ctype >= (OnigCtype )PropertyListNum)
+ return ONIGERR_TYPE_BUG;
+
+ *ranges = PropertyList[ctype];
+ return 0;
+ }
+}
diff --git a/enc/trans/GB/GB12345%UCS.src b/enc/trans/GB/GB12345%UCS.src
index 0b4115bed9..9f1daad751 100644
--- a/enc/trans/GB/GB12345%UCS.src
+++ b/enc/trans/GB/GB12345%UCS.src
@@ -9,62 +9,11 @@ DST_UNIT_BITS 16
BEGIN_MAP
#
-# This mapping data is made from the mapping data provided by Unicode, Inc.
-# Original notice:
-#
-# Name: GB12345-80 to Unicode table (complete, hex format)
-# Unicode version: 1.1
-# Table version: 0.0d1
-# Table format: Format A
-# Date: 6 December 1993
-# Author: Glenn Adams <glenn@metis.com>
-# John H. Jenkins <John_Jenkins@taligent.com>
-#
-# Copyright (c) 1991-1994 Unicode, Inc. All Rights reserved.
-#
-# This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
-# No claims are made as to fitness for any particular purpose. No
-# warranties of any kind are expressed or implied. The recipient
-# agrees to determine applicability of information provided. If this
-# file has been provided on magnetic media by Unicode, Inc., the sole
-# remedy for any claim will be exchange of defective media within 90
-# days of receipt.
-#
-# Recipient is granted the right to make copies in any form for
-# internal distribution and to freely use the information supplied
-# in the creation of products supporting Unicode. Unicode, Inc.
-# specifically excludes the right to re-distribute this file directly
-# to third parties or other organizations whether for profit or not.
-#
-# General notes:
-#
-# This table contains the data Metis and Taligent currently have on how
-# GB12345-90 characters map into Unicode.
-#
-# Format: Three tab-separated columns
-# Column #1 is the GB12345 code (in hex as 0xXXXX)
-# Column #2 is the Unicode (in hex as 0xXXXX)
-# Column #3 the Unicode name (follows a comment sign, '#')
-# The official names for Unicode characters U+4E00
-# to U+9FA5, inclusive, is "CJK UNIFIED IDEOGRAPH-XXXX",
-# where XXXX is the code point. Including all these
-# names in this file increases its size substantially
-# and needlessly. The token "<CJK>" is used for the
-# name of these characters. If necessary, it can be
-# expanded algorithmically by a parser or editor.
-#
-# The entries are in GB12345 order
-#
-# The following algorithms can be used to change the hex form
-# of GB12345 to other standard forms:
-#
-# To change hex to EUC form, add 0x8080
-# To change hex to kuten form, first subtract 0x2020. Then
-# the high and low bytes correspond to the ku and ten of
-# the kuten form. For example, 0x2121 -> 0x0101 -> 0101;
-# 0x777E -> 0x575E -> 8794
-#
-# Any comments or problems, contact <John_Jenkins@taligent.com>
+# The mapping data below was created from a file provided by Unicode, Inc.
+# (The Unicode Consortium). The file was used to create a product supporting
+# Unicode, as explicitly permitted in the file's copyright notice. Please note
+# that Unicode, Inc. never made any claims as to fitness of that file for any
+# particular purpose, and has ceased to publish the file many years ago.
#
#
0x2121 = 0x3000
diff --git a/enc/trans/GB/GB2312%UCS.src b/enc/trans/GB/GB2312%UCS.src
index d196bab535..dc222203fd 100644
--- a/enc/trans/GB/GB2312%UCS.src
+++ b/enc/trans/GB/GB2312%UCS.src
@@ -9,76 +9,11 @@ DST_UNIT_BITS 16
BEGIN_MAP
#
-# This mapping data is made from the mapping data provided by Unicode, Inc.
-# Original notice:
-#
-# Name: GB2312-80 to Unicode table (complete, hex format)
-# Unicode version: 3.0
-# Table version: 1.0
-# Table format: Format A
-# Date: 1999 October 8
-#
-# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved.
-#
-# This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
-# No claims are made as to fitness for any particular purpose. No
-# warranties of any kind are expressed or implied. The recipient
-# agrees to determine applicability of information provided. If this
-# file has been provided on optical media by Unicode, Inc., the sole
-# remedy for any claim will be exchange of defective media within 90
-# days of receipt.
-#
-# Unicode, Inc. hereby grants the right to freely use the information
-# supplied in this file in the creation of products supporting the
-# Unicode Standard, and to make copies of this file in any form for
-# internal or external distribution as long as this notice remains
-# attached.
-#
-# General notes:
-#
-#
-# This table contains one set of mappings from GB2312-80 into Unicode.
-# Note that these data are *possible* mappings only and may not be the
-# same as those used by actual products, nor may they be the best suited
-# for all uses. For more information on the mappings between various code
-# pages incorporating the repertoire of GB2312-80 and Unicode, consult the
-# VENDORS mapping data. Normative information on the mapping between
-# GB2312-80 and Unicode may be found in the Unihan.txt file in the
-# latest Unicode Character Database.
-#
-# If you have carefully considered the fact that the mappings in
-# this table are only one possible set of mappings between GB2312-80 and
-# Unicode and have no normative status, but still feel that you
-# have located an error in the table that requires fixing, you may
-# report any such error to errata@unicode.org.
-#
-#
-# Format: Three tab-separated columns
-# Column #1 is the GB2312 code (in hex as 0xXXXX)
-# Column #2 is the Unicode (in hex as 0xXXXX)
-# Column #3 the Unicode name (follows a comment sign, '#')
-# The official names for Unicode characters U+4E00
-# to U+9FA5, inclusive, is "CJK UNIFIED IDEOGRAPH-XXXX",
-# where XXXX is the code point. Including all these
-# names in this file increases its size substantially
-# and needlessly. The token "<CJK>" is used for the
-# name of these characters. If necessary, it can be
-# expanded algorithmically by a parser or editor.
-#
-# The entries are in GB2312 order
-#
-# The following algorithms can be used to change the hex form
-# of GB2312 to other standard forms:
-#
-# To change hex to EUC form, add 0x8080
-# To change hex to kuten form, first subtract 0x2020. Then
-# the high and low bytes correspond to the ku and ten of
-# the kuten form. For example, 0x2121 -> 0x0101 -> 0101;
-# 0x777E -> 0x575E -> 8794
-#
-# Version history
-# 1.0 version updates 0.0d2 version by correcting mapping for 0x212C
-# from U+2225 to U+2016.
+# The mapping data below was created from a file provided by Unicode, Inc.
+# (The Unicode Consortium). The file was used to create a product supporting
+# Unicode, as explicitly permitted in the file's copyright notice. Please note
+# that Unicode, Inc. never made any claims as to fitness of that file for any
+# particular purpose, and has ceased to publish the file many years ago.
#
#
diff --git a/enc/trans/GB/UCS%GB12345.src b/enc/trans/GB/UCS%GB12345.src
index 8ec8318438..a876bdaca8 100644
--- a/enc/trans/GB/UCS%GB12345.src
+++ b/enc/trans/GB/UCS%GB12345.src
@@ -9,62 +9,11 @@ DST_UNIT_BITS 16
BEGIN_MAP
#
-# This mapping data is made from the mapping data provided by Unicode, Inc.
-# Original notice:
-#
-# Name: GB12345-80 to Unicode table (complete, hex format)
-# Unicode version: 1.1
-# Table version: 0.0d1
-# Table format: Format A
-# Date: 6 December 1993
-# Author: Glenn Adams <glenn@metis.com>
-# John H. Jenkins <John_Jenkins@taligent.com>
-#
-# Copyright (c) 1991-1994 Unicode, Inc. All Rights reserved.
-#
-# This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
-# No claims are made as to fitness for any particular purpose. No
-# warranties of any kind are expressed or implied. The recipient
-# agrees to determine applicability of information provided. If this
-# file has been provided on magnetic media by Unicode, Inc., the sole
-# remedy for any claim will be exchange of defective media within 90
-# days of receipt.
-#
-# Recipient is granted the right to make copies in any form for
-# internal distribution and to freely use the information supplied
-# in the creation of products supporting Unicode. Unicode, Inc.
-# specifically excludes the right to re-distribute this file directly
-# to third parties or other organizations whether for profit or not.
-#
-# General notes:
-#
-# This table contains the data Metis and Taligent currently have on how
-# GB12345-90 characters map into Unicode.
-#
-# Format: Three tab-separated columns
-# Column #1 is the GB12345 code (in hex as 0xXXXX)
-# Column #2 is the Unicode (in hex as 0xXXXX)
-# Column #3 the Unicode name (follows a comment sign, '#')
-# The official names for Unicode characters U+4E00
-# to U+9FA5, inclusive, is "CJK UNIFIED IDEOGRAPH-XXXX",
-# where XXXX is the code point. Including all these
-# names in this file increases its size substantially
-# and needlessly. The token "<CJK>" is used for the
-# name of these characters. If necessary, it can be
-# expanded algorithmically by a parser or editor.
-#
-# The entries are in GB12345 order
-#
-# The following algorithms can be used to change the hex form
-# of GB12345 to other standard forms:
-#
-# To change hex to EUC form, add 0x8080
-# To change hex to kuten form, first subtract 0x2020. Then
-# the high and low bytes correspond to the ku and ten of
-# the kuten form. For example, 0x2121 -> 0x0101 -> 0101;
-# 0x777E -> 0x575E -> 8794
-#
-# Any comments or problems, contact <John_Jenkins@taligent.com>
+# The mapping data below was created from a file provided by Unicode, Inc.
+# (The Unicode Consortium). The file was used to create a product supporting
+# Unicode, as explicitly permitted in the file's copyright notice. Please note
+# that Unicode, Inc. never made any claims as to fitness of that file for any
+# particular purpose, and has ceased to publish the file many years ago.
#
#
0x00A4 = 0x2168
diff --git a/enc/trans/GB/UCS%GB2312.src b/enc/trans/GB/UCS%GB2312.src
index 3293fea604..4f4d52a54a 100644
--- a/enc/trans/GB/UCS%GB2312.src
+++ b/enc/trans/GB/UCS%GB2312.src
@@ -9,76 +9,11 @@ DST_UNIT_BITS 16
BEGIN_MAP
#
-# This mapping data is made from the mapping data provided by Unicode, Inc.
-# Original notice:
-#
-# Name: GB2312-80 to Unicode table (complete, hex format)
-# Unicode version: 3.0
-# Table version: 1.0
-# Table format: Format A
-# Date: 1999 October 8
-#
-# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved.
-#
-# This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
-# No claims are made as to fitness for any particular purpose. No
-# warranties of any kind are expressed or implied. The recipient
-# agrees to determine applicability of information provided. If this
-# file has been provided on optical media by Unicode, Inc., the sole
-# remedy for any claim will be exchange of defective media within 90
-# days of receipt.
-#
-# Unicode, Inc. hereby grants the right to freely use the information
-# supplied in this file in the creation of products supporting the
-# Unicode Standard, and to make copies of this file in any form for
-# internal or external distribution as long as this notice remains
-# attached.
-#
-# General notes:
-#
-#
-# This table contains one set of mappings from GB2312-80 into Unicode.
-# Note that these data are *possible* mappings only and may not be the
-# same as those used by actual products, nor may they be the best suited
-# for all uses. For more information on the mappings between various code
-# pages incorporating the repertoire of GB2312-80 and Unicode, consult the
-# VENDORS mapping data. Normative information on the mapping between
-# GB2312-80 and Unicode may be found in the Unihan.txt file in the
-# latest Unicode Character Database.
-#
-# If you have carefully considered the fact that the mappings in
-# this table are only one possible set of mappings between GB2312-80 and
-# Unicode and have no normative status, but still feel that you
-# have located an error in the table that requires fixing, you may
-# report any such error to errata@unicode.org.
-#
-#
-# Format: Three tab-separated columns
-# Column #1 is the GB2312 code (in hex as 0xXXXX)
-# Column #2 is the Unicode (in hex as 0xXXXX)
-# Column #3 the Unicode name (follows a comment sign, '#')
-# The official names for Unicode characters U+4E00
-# to U+9FA5, inclusive, is "CJK UNIFIED IDEOGRAPH-XXXX",
-# where XXXX is the code point. Including all these
-# names in this file increases its size substantially
-# and needlessly. The token "<CJK>" is used for the
-# name of these characters. If necessary, it can be
-# expanded algorithmically by a parser or editor.
-#
-# The entries are in GB2312 order
-#
-# The following algorithms can be used to change the hex form
-# of GB2312 to other standard forms:
-#
-# To change hex to EUC form, add 0x8080
-# To change hex to kuten form, first subtract 0x2020. Then
-# the high and low bytes correspond to the ku and ten of
-# the kuten form. For example, 0x2121 -> 0x0101 -> 0101;
-# 0x777E -> 0x575E -> 8794
-#
-# Version history
-# 1.0 version updates 0.0d2 version by correcting mapping for 0x212C
-# from U+2225 to U+2016.
+# The mapping data below was created from a file provided by Unicode, Inc.
+# (The Unicode Consortium). The file was used to create a product supporting
+# Unicode, as explicitly permitted in the file's copyright notice. Please note
+# that Unicode, Inc. never made any claims as to fitness of that file for any
+# particular purpose, and has ceased to publish the file many years ago.
#
#
0x00A4 = 0x2168
diff --git a/enc/unicode.c b/enc/unicode.c
index e72b2e64b2..6e8c3d8816 100644
--- a/enc/unicode.c
+++ b/enc/unicode.c
@@ -139,17 +139,17 @@ code3_equal(const OnigCodePoint *x, const OnigCodePoint *y)
/* macros related to ONIGENC_CASE flags */
/* defined here because not used in other files */
-#define ONIGENC_CASE_SPECIALS (ONIGENC_CASE_TITLECASE|ONIGENC_CASE_IS_TITLECASE|ONIGENC_CASE_UP_SPECIAL|ONIGENC_CASE_DOWN_SPECIAL)
+#define ONIGENC_CASE_SPECIALS (ONIGENC_CASE_TITLECASE | ONIGENC_CASE_IS_TITLECASE | ONIGENC_CASE_UP_SPECIAL | ONIGENC_CASE_DOWN_SPECIAL)
/* macros for length in CaseMappingSpecials array in enc/unicode/casefold.h */
#define SpecialsLengthOffset 25 /* needs to be higher than the 22 bits used for Unicode codepoints */
-#define SpecialsLengthExtract(n) ((n)>>SpecialsLengthOffset)
-#define SpecialsCodepointExtract(n) ((n)&((1<<SpecialsLengthOffset)-1))
-#define SpecialsLengthEncode(n) ((n)<<SpecialsLengthOffset)
+#define SpecialsLengthExtract(n) ((n) >> SpecialsLengthOffset)
+#define SpecialsCodepointExtract(n) ((n) & ((1 << SpecialsLengthOffset) - 1))
+#define SpecialsLengthEncode(n) ((n) << SpecialsLengthOffset)
-#define OnigSpecialIndexMask (((1<<OnigSpecialIndexWidth)-1)<<OnigSpecialIndexShift)
-#define OnigSpecialIndexEncode(n) ((n)<<OnigSpecialIndexShift)
-#define OnigSpecialIndexDecode(n) (((n)&OnigSpecialIndexMask)>>OnigSpecialIndexShift)
+#define OnigSpecialIndexMask (((1 << OnigSpecialIndexWidth) - 1) << OnigSpecialIndexShift)
+#define OnigSpecialIndexEncode(n) ((n) << OnigSpecialIndexShift)
+#define OnigSpecialIndexDecode(n) (((n) & OnigSpecialIndexMask) >> OnigSpecialIndexShift)
/* macros to shorten "enc/unicode/casefold.h", undefined immediately after including the file */
#define U ONIGENC_CASE_UPCASE
@@ -660,137 +660,141 @@ onigenc_unicode_case_map(OnigCaseFoldType* flagP,
OnigUChar* to, OnigUChar* to_end,
const struct OnigEncodingTypeST* enc)
{
- OnigCodePoint code;
- OnigUChar *to_start = to;
- OnigCaseFoldType flags = *flagP;
- int codepoint_length;
-
- to_end -= CASE_MAPPING_SLACK;
- /* copy flags ONIGENC_CASE_UPCASE and ONIGENC_CASE_DOWNCASE over to
- * ONIGENC_CASE_UP_SPECIAL and ONIGENC_CASE_DOWN_SPECIAL */
- flags |= (flags&(ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE))<<ONIGENC_CASE_SPECIAL_OFFSET;
-
- while (*pp<end && to<=to_end) {
- codepoint_length = ONIGENC_PRECISE_MBC_ENC_LEN(enc, *pp, end);
- if (codepoint_length < 0)
- return codepoint_length; /* encoding invalid */
- code = ONIGENC_MBC_TO_CODE(enc, *pp, end);
- *pp += codepoint_length;
-
- if (code<='z') { /* ASCII comes first */
- if (code>='a' && code<='z') {
- if (flags&ONIGENC_CASE_UPCASE) {
- MODIFIED;
- if (flags&ONIGENC_CASE_FOLD_TURKISH_AZERI && code=='i')
- code = I_WITH_DOT_ABOVE;
- else
- code += 'A'-'a';
- }
- }
- else if (code>='A' && code<='Z') {
- if (flags&(ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD)) {
- MODIFIED;
- if (flags&ONIGENC_CASE_FOLD_TURKISH_AZERI && code=='I')
- code = DOTLESS_i;
- else
- code += 'a'-'A';
- }
- }
+ OnigCodePoint code;
+ OnigUChar *to_start = to;
+ OnigCaseFoldType flags = *flagP;
+ int codepoint_length;
+
+ to_end -= CASE_MAPPING_SLACK;
+ /* copy flags ONIGENC_CASE_UPCASE and ONIGENC_CASE_DOWNCASE over to
+ * ONIGENC_CASE_UP_SPECIAL and ONIGENC_CASE_DOWN_SPECIAL */
+ flags |= (flags & (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE)) << ONIGENC_CASE_SPECIAL_OFFSET;
+
+ while (*pp < end && to <= to_end) {
+ codepoint_length = ONIGENC_PRECISE_MBC_ENC_LEN(enc, *pp, end);
+ if (codepoint_length < 0)
+ return codepoint_length; /* encoding invalid */
+ code = ONIGENC_MBC_TO_CODE(enc, *pp, end);
+ *pp += codepoint_length;
+
+ if (code <= 'z') { /* ASCII comes first */
+ if (code >= 'a' && code <= 'z') {
+ if (flags & ONIGENC_CASE_UPCASE) {
+ MODIFIED;
+ if (flags & ONIGENC_CASE_FOLD_TURKISH_AZERI && code == 'i')
+ code = I_WITH_DOT_ABOVE;
+ else {
+ code -= 'a';
+ code += 'A';
+ }
}
- else if (!(flags&ONIGENC_CASE_ASCII_ONLY) && code>=0x00B5) { /* deal with non-ASCII; micron sign (U+00B5) is lowest affected */
- const CodePointList3 *folded;
-
- if (code==I_WITH_DOT_ABOVE) {
- if (flags&(ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD)) {
- MODIFIED;
- code = 'i';
- if (!(flags&ONIGENC_CASE_FOLD_TURKISH_AZERI)) { /* make dot above explicit */
- to += ONIGENC_CODE_TO_MBC(enc, code, to);
- code = DOT_ABOVE;
- }
- }
- }
- else if (code==DOTLESS_i) { /* handle this manually, because it isn't involved in folding */
- if (flags&ONIGENC_CASE_UPCASE)
- MODIFIED, code = 'I';
+ }
+ else if (code >= 'A' && code <= 'Z') {
+ if (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD)) {
+ MODIFIED;
+ if (flags & ONIGENC_CASE_FOLD_TURKISH_AZERI && code == 'I')
+ code = DOTLESS_i;
+ else
+ code += 'a' - 'A';
+ }
+ }
+ }
+ else if (!(flags & ONIGENC_CASE_ASCII_ONLY) && code >= 0x00B5) { /* deal with non-ASCII; micron sign (U+00B5) is lowest affected */
+ const CodePointList3 *folded;
+
+ if (code == I_WITH_DOT_ABOVE) {
+ if (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD)) {
+ MODIFIED;
+ code = 'i';
+ if (!(flags & ONIGENC_CASE_FOLD_TURKISH_AZERI)) { /* make dot above explicit */
+ to += ONIGENC_CODE_TO_MBC(enc, code, to);
+ code = DOT_ABOVE;
+ }
+ }
+ }
+ else if (code == DOTLESS_i) { /* handle this manually, because it isn't involved in folding */
+ if (flags & ONIGENC_CASE_UPCASE) {
+ MODIFIED;
+ code = 'I';
+ }
+ }
+ else if ((folded = onigenc_unicode_fold_lookup(code)) != 0) { /* data about character found in CaseFold_11_Table */
+ if ((flags & ONIGENC_CASE_TITLECASE) && code>=0x1C90 && code<=0x1CBF) { /* Georgian MTAVRULI */
+ MODIFIED;
+ code += 0x10D0 - 0x1C90;
+ }
+ else if ((flags & ONIGENC_CASE_TITLECASE) /* Titlecase needed, */
+ && (OnigCaseFoldFlags(folded->n) & ONIGENC_CASE_IS_TITLECASE)) { /* but already Titlecase */
+ /* already Titlecase, no changes needed */
+ }
+ else if (flags & OnigCaseFoldFlags(folded->n)) { /* needs and data availability match */
+ const OnigCodePoint *next;
+ int count;
+
+ MODIFIED;
+ if (flags & OnigCaseFoldFlags(folded->n) & ONIGENC_CASE_SPECIALS) { /* special */
+ const OnigCodePoint *SpecialsStart = CaseMappingSpecials + OnigSpecialIndexDecode(folded->n);
+
+ if (OnigCaseFoldFlags(folded->n) & ONIGENC_CASE_IS_TITLECASE) { /* swapCASE available */
+ if ((flags & (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE))
+ == (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE)) /* swapCASE needed */
+ goto SpecialsCopy;
+ else /* swapCASE not needed */
+ SpecialsStart += SpecialsLengthExtract(*SpecialsStart);
}
- else if ((folded = onigenc_unicode_fold_lookup(code)) != 0) { /* data about character found in CaseFold_11_Table */
- if ((flags&ONIGENC_CASE_TITLECASE) /* Titlecase needed, */
- && (OnigCaseFoldFlags(folded->n)&ONIGENC_CASE_IS_TITLECASE)) { /* but already Titlecase */
- /* already Titlecase, no changes needed */
- }
- else if (flags&OnigCaseFoldFlags(folded->n)) { /* needs and data availability match */
- const OnigCodePoint *next;
- int count;
-
- MODIFIED;
- if (flags&OnigCaseFoldFlags(folded->n)&ONIGENC_CASE_SPECIALS) { /* special */
- OnigCodePoint *SpecialsStart = CaseMappingSpecials + OnigSpecialIndexDecode(folded->n);
-
- if (OnigCaseFoldFlags(folded->n)&ONIGENC_CASE_IS_TITLECASE) { /* swapCASE available */
- if ((flags&(ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE))
- == (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE)) /* swapCASE needed */
- goto SpecialsCopy;
- else /* swapCASE not needed */
- SpecialsStart += SpecialsLengthExtract(*SpecialsStart);
- }
- if (OnigCaseFoldFlags(folded->n)&ONIGENC_CASE_TITLECASE) { /* Titlecase available */
- if (flags&ONIGENC_CASE_TITLECASE) /* Titlecase needed, but not yet Titlecase */
- goto SpecialsCopy;
- else /* Titlecase not needed */
- SpecialsStart += SpecialsLengthExtract(*SpecialsStart);
- }
- if (OnigCaseFoldFlags(folded->n)&ONIGENC_CASE_DOWN_SPECIAL) {
- if (!(flags&ONIGENC_CASE_DOWN_SPECIAL))
- SpecialsStart += SpecialsLengthExtract(*SpecialsStart);
- }
- /* here, we know we use ONIGENC_CASE_UP_SPECIAL, and the position is right */
- SpecialsCopy:
- count = SpecialsLengthExtract(*SpecialsStart);
- next = SpecialsStart;
- code = SpecialsCodepointExtract(*next++);
- }
- else { /* no specials */
- count = OnigCodePointCount(folded->n);
- next = folded->code;
- code = *next++;
- }
- if (count==1)
- ;
- else if (count==2) {
- to += ONIGENC_CODE_TO_MBC(enc, code, to);
- code = *next;
- }
- else { /* count == 3 */
- to += ONIGENC_CODE_TO_MBC(enc, code, to);
- to += ONIGENC_CODE_TO_MBC(enc, *next++, to);
- code = *next;
- }
- }
+ if (OnigCaseFoldFlags(folded->n) & ONIGENC_CASE_TITLECASE) { /* Titlecase available */
+ if (flags & ONIGENC_CASE_TITLECASE) /* Titlecase needed, but not yet Titlecase */
+ goto SpecialsCopy;
+ else /* Titlecase not needed */
+ SpecialsStart += SpecialsLengthExtract(*SpecialsStart);
}
- else if ((folded = onigenc_unicode_unfold1_lookup(code)) != 0) { /* data about character found in CaseUnfold_11_Table */
- if (flags&OnigCaseFoldFlags(folded->n)) { /* needs and data availability match */
- MODIFIED;
- if (flags&OnigCaseFoldFlags(folded->n)&ONIGENC_CASE_TITLECASE)
- code = folded->code[1];
- else
- code = folded->code[0];
- }
- else if ((flags&(ONIGENC_CASE_UPCASE))
- && (code==0x03B9||code==0x03BC)) { /* GREEK SMALL LETTERs IOTA/MU */
- MODIFIED;
- code = folded->code[1];
- }
+ if (OnigCaseFoldFlags(folded->n) & ONIGENC_CASE_DOWN_SPECIAL) {
+ if (!(flags & ONIGENC_CASE_DOWN_SPECIAL))
+ SpecialsStart += SpecialsLengthExtract(*SpecialsStart);
}
+ /* here, we know we use ONIGENC_CASE_UP_SPECIAL, and the position is right */
+SpecialsCopy:
+ count = SpecialsLengthExtract(*SpecialsStart);
+ next = SpecialsStart;
+ code = SpecialsCodepointExtract(*next++);
+ }
+ else { /* no specials */
+ count = OnigCodePointCount(folded->n);
+ next = folded->code;
+ code = *next++;
+ }
+ if (count == 1)
+ ;
+ else if (count == 2) {
+ to += ONIGENC_CODE_TO_MBC(enc, code, to);
+ code = *next;
+ }
+ else { /* count == 3 */
+ to += ONIGENC_CODE_TO_MBC(enc, code, to);
+ to += ONIGENC_CODE_TO_MBC(enc, *next++, to);
+ code = *next;
+ }
}
- to += ONIGENC_CODE_TO_MBC(enc, code, to);
- /* switch from titlecase to lowercase for capitalize */
- if (flags & ONIGENC_CASE_TITLECASE)
- flags ^= (ONIGENC_CASE_UPCASE |ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE|
- ONIGENC_CASE_UP_SPECIAL|ONIGENC_CASE_DOWN_SPECIAL);
+ }
+ else if ((folded = onigenc_unicode_unfold1_lookup(code)) != 0) { /* data about character found in CaseUnfold_11_Table */
+ if ((flags & ONIGENC_CASE_TITLECASE) /* Titlecase needed, */
+ && (OnigCaseFoldFlags(folded->n) & ONIGENC_CASE_IS_TITLECASE)) { /* but already Titlecase */
+ /* already Titlecase, no changes needed */
+ }
+ else if (flags & OnigCaseFoldFlags(folded->n)) { /* needs and data availability match */
+ MODIFIED;
+ code = folded->code[(flags & OnigCaseFoldFlags(folded->n) & ONIGENC_CASE_TITLECASE) ? 1 : 0];
+ }
+ }
}
- *flagP = flags;
- return (int)(to-to_start);
+ to += ONIGENC_CODE_TO_MBC(enc, code, to);
+ /* switch from titlecase to lowercase for capitalize */
+ if (flags & ONIGENC_CASE_TITLECASE)
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE |
+ ONIGENC_CASE_UP_SPECIAL | ONIGENC_CASE_DOWN_SPECIAL);
+ }
+ *flagP = flags;
+ return (int )(to - to_start);
}
#if 0
diff --git a/enc/unicode/12.1.0/casefold.h b/enc/unicode/12.1.0/casefold.h
new file mode 100644
index 0000000000..4c62f0faee
--- /dev/null
+++ b/enc/unicode/12.1.0/casefold.h
@@ -0,0 +1,7428 @@
+/* DO NOT EDIT THIS FILE. */
+/* Generated by enc/unicode/case-folding.rb */
+
+#if defined ONIG_UNICODE_VERSION_STRING && !( \
+ ONIG_UNICODE_VERSION_MAJOR == 12 && \
+ ONIG_UNICODE_VERSION_MINOR == 1 && \
+ ONIG_UNICODE_VERSION_TEENY == 0 && \
+ 1)
+# error ONIG_UNICODE_VERSION_STRING mismatch
+#endif
+#define ONIG_UNICODE_VERSION_STRING "12.1.0"
+#define ONIG_UNICODE_VERSION_MAJOR 12
+#define ONIG_UNICODE_VERSION_MINOR 1
+#define ONIG_UNICODE_VERSION_TEENY 0
+
+static const CaseFold_11_Type CaseFold_11_Table[] = {
+#define CaseFold (*(CaseFold_11_Type (*)[1485])(CaseFold_11_Table+0))
+ {0x0041, {1|F|D, {0x0061}}},
+ {0x0042, {1|F|D, {0x0062}}},
+ {0x0043, {1|F|D, {0x0063}}},
+ {0x0044, {1|F|D, {0x0064}}},
+ {0x0045, {1|F|D, {0x0065}}},
+ {0x0046, {1|F|D, {0x0066}}},
+ {0x0047, {1|F|D, {0x0067}}},
+ {0x0048, {1|F|D, {0x0068}}},
+ {0x004a, {1|F|D, {0x006a}}},
+ {0x004b, {1|F|D, {0x006b}}},
+ {0x004c, {1|F|D, {0x006c}}},
+ {0x004d, {1|F|D, {0x006d}}},
+ {0x004e, {1|F|D, {0x006e}}},
+ {0x004f, {1|F|D, {0x006f}}},
+ {0x0050, {1|F|D, {0x0070}}},
+ {0x0051, {1|F|D, {0x0071}}},
+ {0x0052, {1|F|D, {0x0072}}},
+ {0x0053, {1|F|D, {0x0073}}},
+ {0x0054, {1|F|D, {0x0074}}},
+ {0x0055, {1|F|D, {0x0075}}},
+ {0x0056, {1|F|D, {0x0076}}},
+ {0x0057, {1|F|D, {0x0077}}},
+ {0x0058, {1|F|D, {0x0078}}},
+ {0x0059, {1|F|D, {0x0079}}},
+ {0x005a, {1|F|D, {0x007a}}},
+ {0x00b5, {1|F|SU|I(0), {0x03bc}}},
+ {0x00c0, {1|F|D, {0x00e0}}},
+ {0x00c1, {1|F|D, {0x00e1}}},
+ {0x00c2, {1|F|D, {0x00e2}}},
+ {0x00c3, {1|F|D, {0x00e3}}},
+ {0x00c4, {1|F|D, {0x00e4}}},
+ {0x00c5, {1|F|D, {0x00e5}}},
+ {0x00c6, {1|F|D, {0x00e6}}},
+ {0x00c7, {1|F|D, {0x00e7}}},
+ {0x00c8, {1|F|D, {0x00e8}}},
+ {0x00c9, {1|F|D, {0x00e9}}},
+ {0x00ca, {1|F|D, {0x00ea}}},
+ {0x00cb, {1|F|D, {0x00eb}}},
+ {0x00cc, {1|F|D, {0x00ec}}},
+ {0x00cd, {1|F|D, {0x00ed}}},
+ {0x00ce, {1|F|D, {0x00ee}}},
+ {0x00cf, {1|F|D, {0x00ef}}},
+ {0x00d0, {1|F|D, {0x00f0}}},
+ {0x00d1, {1|F|D, {0x00f1}}},
+ {0x00d2, {1|F|D, {0x00f2}}},
+ {0x00d3, {1|F|D, {0x00f3}}},
+ {0x00d4, {1|F|D, {0x00f4}}},
+ {0x00d5, {1|F|D, {0x00f5}}},
+ {0x00d6, {1|F|D, {0x00f6}}},
+ {0x00d8, {1|F|D, {0x00f8}}},
+ {0x00d9, {1|F|D, {0x00f9}}},
+ {0x00da, {1|F|D, {0x00fa}}},
+ {0x00db, {1|F|D, {0x00fb}}},
+ {0x00dc, {1|F|D, {0x00fc}}},
+ {0x00dd, {1|F|D, {0x00fd}}},
+ {0x00de, {1|F|D, {0x00fe}}},
+ {0x00df, {2|F|ST|SU|I(1), {0x0073, 0x0073}}},
+ {0x0100, {1|F|D, {0x0101}}},
+ {0x0102, {1|F|D, {0x0103}}},
+ {0x0104, {1|F|D, {0x0105}}},
+ {0x0106, {1|F|D, {0x0107}}},
+ {0x0108, {1|F|D, {0x0109}}},
+ {0x010a, {1|F|D, {0x010b}}},
+ {0x010c, {1|F|D, {0x010d}}},
+ {0x010e, {1|F|D, {0x010f}}},
+ {0x0110, {1|F|D, {0x0111}}},
+ {0x0112, {1|F|D, {0x0113}}},
+ {0x0114, {1|F|D, {0x0115}}},
+ {0x0116, {1|F|D, {0x0117}}},
+ {0x0118, {1|F|D, {0x0119}}},
+ {0x011a, {1|F|D, {0x011b}}},
+ {0x011c, {1|F|D, {0x011d}}},
+ {0x011e, {1|F|D, {0x011f}}},
+ {0x0120, {1|F|D, {0x0121}}},
+ {0x0122, {1|F|D, {0x0123}}},
+ {0x0124, {1|F|D, {0x0125}}},
+ {0x0126, {1|F|D, {0x0127}}},
+ {0x0128, {1|F|D, {0x0129}}},
+ {0x012a, {1|F|D, {0x012b}}},
+ {0x012c, {1|F|D, {0x012d}}},
+ {0x012e, {1|F|D, {0x012f}}},
+ {0x0132, {1|F|D, {0x0133}}},
+ {0x0134, {1|F|D, {0x0135}}},
+ {0x0136, {1|F|D, {0x0137}}},
+ {0x0139, {1|F|D, {0x013a}}},
+ {0x013b, {1|F|D, {0x013c}}},
+ {0x013d, {1|F|D, {0x013e}}},
+ {0x013f, {1|F|D, {0x0140}}},
+ {0x0141, {1|F|D, {0x0142}}},
+ {0x0143, {1|F|D, {0x0144}}},
+ {0x0145, {1|F|D, {0x0146}}},
+ {0x0147, {1|F|D, {0x0148}}},
+ {0x0149, {2|F|SU|I(5), {0x02bc, 0x006e}}},
+ {0x014a, {1|F|D, {0x014b}}},
+ {0x014c, {1|F|D, {0x014d}}},
+ {0x014e, {1|F|D, {0x014f}}},
+ {0x0150, {1|F|D, {0x0151}}},
+ {0x0152, {1|F|D, {0x0153}}},
+ {0x0154, {1|F|D, {0x0155}}},
+ {0x0156, {1|F|D, {0x0157}}},
+ {0x0158, {1|F|D, {0x0159}}},
+ {0x015a, {1|F|D, {0x015b}}},
+ {0x015c, {1|F|D, {0x015d}}},
+ {0x015e, {1|F|D, {0x015f}}},
+ {0x0160, {1|F|D, {0x0161}}},
+ {0x0162, {1|F|D, {0x0163}}},
+ {0x0164, {1|F|D, {0x0165}}},
+ {0x0166, {1|F|D, {0x0167}}},
+ {0x0168, {1|F|D, {0x0169}}},
+ {0x016a, {1|F|D, {0x016b}}},
+ {0x016c, {1|F|D, {0x016d}}},
+ {0x016e, {1|F|D, {0x016f}}},
+ {0x0170, {1|F|D, {0x0171}}},
+ {0x0172, {1|F|D, {0x0173}}},
+ {0x0174, {1|F|D, {0x0175}}},
+ {0x0176, {1|F|D, {0x0177}}},
+ {0x0178, {1|F|D, {0x00ff}}},
+ {0x0179, {1|F|D, {0x017a}}},
+ {0x017b, {1|F|D, {0x017c}}},
+ {0x017d, {1|F|D, {0x017e}}},
+ {0x017f, {1|F|SU|I(7), {0x0073}}},
+ {0x0181, {1|F|D, {0x0253}}},
+ {0x0182, {1|F|D, {0x0183}}},
+ {0x0184, {1|F|D, {0x0185}}},
+ {0x0186, {1|F|D, {0x0254}}},
+ {0x0187, {1|F|D, {0x0188}}},
+ {0x0189, {1|F|D, {0x0256}}},
+ {0x018a, {1|F|D, {0x0257}}},
+ {0x018b, {1|F|D, {0x018c}}},
+ {0x018e, {1|F|D, {0x01dd}}},
+ {0x018f, {1|F|D, {0x0259}}},
+ {0x0190, {1|F|D, {0x025b}}},
+ {0x0191, {1|F|D, {0x0192}}},
+ {0x0193, {1|F|D, {0x0260}}},
+ {0x0194, {1|F|D, {0x0263}}},
+ {0x0196, {1|F|D, {0x0269}}},
+ {0x0197, {1|F|D, {0x0268}}},
+ {0x0198, {1|F|D, {0x0199}}},
+ {0x019c, {1|F|D, {0x026f}}},
+ {0x019d, {1|F|D, {0x0272}}},
+ {0x019f, {1|F|D, {0x0275}}},
+ {0x01a0, {1|F|D, {0x01a1}}},
+ {0x01a2, {1|F|D, {0x01a3}}},
+ {0x01a4, {1|F|D, {0x01a5}}},
+ {0x01a6, {1|F|D, {0x0280}}},
+ {0x01a7, {1|F|D, {0x01a8}}},
+ {0x01a9, {1|F|D, {0x0283}}},
+ {0x01ac, {1|F|D, {0x01ad}}},
+ {0x01ae, {1|F|D, {0x0288}}},
+ {0x01af, {1|F|D, {0x01b0}}},
+ {0x01b1, {1|F|D, {0x028a}}},
+ {0x01b2, {1|F|D, {0x028b}}},
+ {0x01b3, {1|F|D, {0x01b4}}},
+ {0x01b5, {1|F|D, {0x01b6}}},
+ {0x01b7, {1|F|D, {0x0292}}},
+ {0x01b8, {1|F|D, {0x01b9}}},
+ {0x01bc, {1|F|D, {0x01bd}}},
+ {0x01c4, {1|F|D|ST|I(8), {0x01c6}}},
+ {0x01c5, {1|F|D|IT|SU|I(9), {0x01c6}}},
+ {0x01c7, {1|F|D|ST|I(12), {0x01c9}}},
+ {0x01c8, {1|F|D|IT|SU|I(13), {0x01c9}}},
+ {0x01ca, {1|F|D|ST|I(16), {0x01cc}}},
+ {0x01cb, {1|F|D|IT|SU|I(17), {0x01cc}}},
+ {0x01cd, {1|F|D, {0x01ce}}},
+ {0x01cf, {1|F|D, {0x01d0}}},
+ {0x01d1, {1|F|D, {0x01d2}}},
+ {0x01d3, {1|F|D, {0x01d4}}},
+ {0x01d5, {1|F|D, {0x01d6}}},
+ {0x01d7, {1|F|D, {0x01d8}}},
+ {0x01d9, {1|F|D, {0x01da}}},
+ {0x01db, {1|F|D, {0x01dc}}},
+ {0x01de, {1|F|D, {0x01df}}},
+ {0x01e0, {1|F|D, {0x01e1}}},
+ {0x01e2, {1|F|D, {0x01e3}}},
+ {0x01e4, {1|F|D, {0x01e5}}},
+ {0x01e6, {1|F|D, {0x01e7}}},
+ {0x01e8, {1|F|D, {0x01e9}}},
+ {0x01ea, {1|F|D, {0x01eb}}},
+ {0x01ec, {1|F|D, {0x01ed}}},
+ {0x01ee, {1|F|D, {0x01ef}}},
+ {0x01f0, {2|F|SU|I(20), {0x006a, 0x030c}}},
+ {0x01f1, {1|F|D|ST|I(22), {0x01f3}}},
+ {0x01f2, {1|F|D|IT|SU|I(23), {0x01f3}}},
+ {0x01f4, {1|F|D, {0x01f5}}},
+ {0x01f6, {1|F|D, {0x0195}}},
+ {0x01f7, {1|F|D, {0x01bf}}},
+ {0x01f8, {1|F|D, {0x01f9}}},
+ {0x01fa, {1|F|D, {0x01fb}}},
+ {0x01fc, {1|F|D, {0x01fd}}},
+ {0x01fe, {1|F|D, {0x01ff}}},
+ {0x0200, {1|F|D, {0x0201}}},
+ {0x0202, {1|F|D, {0x0203}}},
+ {0x0204, {1|F|D, {0x0205}}},
+ {0x0206, {1|F|D, {0x0207}}},
+ {0x0208, {1|F|D, {0x0209}}},
+ {0x020a, {1|F|D, {0x020b}}},
+ {0x020c, {1|F|D, {0x020d}}},
+ {0x020e, {1|F|D, {0x020f}}},
+ {0x0210, {1|F|D, {0x0211}}},
+ {0x0212, {1|F|D, {0x0213}}},
+ {0x0214, {1|F|D, {0x0215}}},
+ {0x0216, {1|F|D, {0x0217}}},
+ {0x0218, {1|F|D, {0x0219}}},
+ {0x021a, {1|F|D, {0x021b}}},
+ {0x021c, {1|F|D, {0x021d}}},
+ {0x021e, {1|F|D, {0x021f}}},
+ {0x0220, {1|F|D, {0x019e}}},
+ {0x0222, {1|F|D, {0x0223}}},
+ {0x0224, {1|F|D, {0x0225}}},
+ {0x0226, {1|F|D, {0x0227}}},
+ {0x0228, {1|F|D, {0x0229}}},
+ {0x022a, {1|F|D, {0x022b}}},
+ {0x022c, {1|F|D, {0x022d}}},
+ {0x022e, {1|F|D, {0x022f}}},
+ {0x0230, {1|F|D, {0x0231}}},
+ {0x0232, {1|F|D, {0x0233}}},
+ {0x023a, {1|F|D, {0x2c65}}},
+ {0x023b, {1|F|D, {0x023c}}},
+ {0x023d, {1|F|D, {0x019a}}},
+ {0x023e, {1|F|D, {0x2c66}}},
+ {0x0241, {1|F|D, {0x0242}}},
+ {0x0243, {1|F|D, {0x0180}}},
+ {0x0244, {1|F|D, {0x0289}}},
+ {0x0245, {1|F|D, {0x028c}}},
+ {0x0246, {1|F|D, {0x0247}}},
+ {0x0248, {1|F|D, {0x0249}}},
+ {0x024a, {1|F|D, {0x024b}}},
+ {0x024c, {1|F|D, {0x024d}}},
+ {0x024e, {1|F|D, {0x024f}}},
+ {0x0345, {1|F|SU|I(26), {0x03b9}}},
+ {0x0370, {1|F|D, {0x0371}}},
+ {0x0372, {1|F|D, {0x0373}}},
+ {0x0376, {1|F|D, {0x0377}}},
+ {0x037f, {1|F|D, {0x03f3}}},
+ {0x0386, {1|F|D, {0x03ac}}},
+ {0x0388, {1|F|D, {0x03ad}}},
+ {0x0389, {1|F|D, {0x03ae}}},
+ {0x038a, {1|F|D, {0x03af}}},
+ {0x038c, {1|F|D, {0x03cc}}},
+ {0x038e, {1|F|D, {0x03cd}}},
+ {0x038f, {1|F|D, {0x03ce}}},
+ {0x0390, {3|F|SU|I(27), {0x03b9, 0x0308, 0x0301}}},
+ {0x0391, {1|F|D, {0x03b1}}},
+ {0x0392, {1|F|D, {0x03b2}}},
+ {0x0393, {1|F|D, {0x03b3}}},
+ {0x0394, {1|F|D, {0x03b4}}},
+ {0x0395, {1|F|D, {0x03b5}}},
+ {0x0396, {1|F|D, {0x03b6}}},
+ {0x0397, {1|F|D, {0x03b7}}},
+ {0x0398, {1|F|D, {0x03b8}}},
+ {0x0399, {1|F|D, {0x03b9}}},
+ {0x039a, {1|F|D, {0x03ba}}},
+ {0x039b, {1|F|D, {0x03bb}}},
+ {0x039c, {1|F|D, {0x03bc}}},
+ {0x039d, {1|F|D, {0x03bd}}},
+ {0x039e, {1|F|D, {0x03be}}},
+ {0x039f, {1|F|D, {0x03bf}}},
+ {0x03a0, {1|F|D, {0x03c0}}},
+ {0x03a1, {1|F|D, {0x03c1}}},
+ {0x03a3, {1|F|D, {0x03c3}}},
+ {0x03a4, {1|F|D, {0x03c4}}},
+ {0x03a5, {1|F|D, {0x03c5}}},
+ {0x03a6, {1|F|D, {0x03c6}}},
+ {0x03a7, {1|F|D, {0x03c7}}},
+ {0x03a8, {1|F|D, {0x03c8}}},
+ {0x03a9, {1|F|D, {0x03c9}}},
+ {0x03aa, {1|F|D, {0x03ca}}},
+ {0x03ab, {1|F|D, {0x03cb}}},
+ {0x03b0, {3|F|SU|I(30), {0x03c5, 0x0308, 0x0301}}},
+ {0x03c2, {1|F|SU|I(33), {0x03c3}}},
+ {0x03cf, {1|F|D, {0x03d7}}},
+ {0x03d0, {1|F|SU|I(34), {0x03b2}}},
+ {0x03d1, {1|F|SU|I(35), {0x03b8}}},
+ {0x03d5, {1|F|SU|I(36), {0x03c6}}},
+ {0x03d6, {1|F|SU|I(37), {0x03c0}}},
+ {0x03d8, {1|F|D, {0x03d9}}},
+ {0x03da, {1|F|D, {0x03db}}},
+ {0x03dc, {1|F|D, {0x03dd}}},
+ {0x03de, {1|F|D, {0x03df}}},
+ {0x03e0, {1|F|D, {0x03e1}}},
+ {0x03e2, {1|F|D, {0x03e3}}},
+ {0x03e4, {1|F|D, {0x03e5}}},
+ {0x03e6, {1|F|D, {0x03e7}}},
+ {0x03e8, {1|F|D, {0x03e9}}},
+ {0x03ea, {1|F|D, {0x03eb}}},
+ {0x03ec, {1|F|D, {0x03ed}}},
+ {0x03ee, {1|F|D, {0x03ef}}},
+ {0x03f0, {1|F|SU|I(38), {0x03ba}}},
+ {0x03f1, {1|F|SU|I(39), {0x03c1}}},
+ {0x03f4, {1|F|D, {0x03b8}}},
+ {0x03f5, {1|F|SU|I(40), {0x03b5}}},
+ {0x03f7, {1|F|D, {0x03f8}}},
+ {0x03f9, {1|F|D, {0x03f2}}},
+ {0x03fa, {1|F|D, {0x03fb}}},
+ {0x03fd, {1|F|D, {0x037b}}},
+ {0x03fe, {1|F|D, {0x037c}}},
+ {0x03ff, {1|F|D, {0x037d}}},
+ {0x0400, {1|F|D, {0x0450}}},
+ {0x0401, {1|F|D, {0x0451}}},
+ {0x0402, {1|F|D, {0x0452}}},
+ {0x0403, {1|F|D, {0x0453}}},
+ {0x0404, {1|F|D, {0x0454}}},
+ {0x0405, {1|F|D, {0x0455}}},
+ {0x0406, {1|F|D, {0x0456}}},
+ {0x0407, {1|F|D, {0x0457}}},
+ {0x0408, {1|F|D, {0x0458}}},
+ {0x0409, {1|F|D, {0x0459}}},
+ {0x040a, {1|F|D, {0x045a}}},
+ {0x040b, {1|F|D, {0x045b}}},
+ {0x040c, {1|F|D, {0x045c}}},
+ {0x040d, {1|F|D, {0x045d}}},
+ {0x040e, {1|F|D, {0x045e}}},
+ {0x040f, {1|F|D, {0x045f}}},
+ {0x0410, {1|F|D, {0x0430}}},
+ {0x0411, {1|F|D, {0x0431}}},
+ {0x0412, {1|F|D, {0x0432}}},
+ {0x0413, {1|F|D, {0x0433}}},
+ {0x0414, {1|F|D, {0x0434}}},
+ {0x0415, {1|F|D, {0x0435}}},
+ {0x0416, {1|F|D, {0x0436}}},
+ {0x0417, {1|F|D, {0x0437}}},
+ {0x0418, {1|F|D, {0x0438}}},
+ {0x0419, {1|F|D, {0x0439}}},
+ {0x041a, {1|F|D, {0x043a}}},
+ {0x041b, {1|F|D, {0x043b}}},
+ {0x041c, {1|F|D, {0x043c}}},
+ {0x041d, {1|F|D, {0x043d}}},
+ {0x041e, {1|F|D, {0x043e}}},
+ {0x041f, {1|F|D, {0x043f}}},
+ {0x0420, {1|F|D, {0x0440}}},
+ {0x0421, {1|F|D, {0x0441}}},
+ {0x0422, {1|F|D, {0x0442}}},
+ {0x0423, {1|F|D, {0x0443}}},
+ {0x0424, {1|F|D, {0x0444}}},
+ {0x0425, {1|F|D, {0x0445}}},
+ {0x0426, {1|F|D, {0x0446}}},
+ {0x0427, {1|F|D, {0x0447}}},
+ {0x0428, {1|F|D, {0x0448}}},
+ {0x0429, {1|F|D, {0x0449}}},
+ {0x042a, {1|F|D, {0x044a}}},
+ {0x042b, {1|F|D, {0x044b}}},
+ {0x042c, {1|F|D, {0x044c}}},
+ {0x042d, {1|F|D, {0x044d}}},
+ {0x042e, {1|F|D, {0x044e}}},
+ {0x042f, {1|F|D, {0x044f}}},
+ {0x0460, {1|F|D, {0x0461}}},
+ {0x0462, {1|F|D, {0x0463}}},
+ {0x0464, {1|F|D, {0x0465}}},
+ {0x0466, {1|F|D, {0x0467}}},
+ {0x0468, {1|F|D, {0x0469}}},
+ {0x046a, {1|F|D, {0x046b}}},
+ {0x046c, {1|F|D, {0x046d}}},
+ {0x046e, {1|F|D, {0x046f}}},
+ {0x0470, {1|F|D, {0x0471}}},
+ {0x0472, {1|F|D, {0x0473}}},
+ {0x0474, {1|F|D, {0x0475}}},
+ {0x0476, {1|F|D, {0x0477}}},
+ {0x0478, {1|F|D, {0x0479}}},
+ {0x047a, {1|F|D, {0x047b}}},
+ {0x047c, {1|F|D, {0x047d}}},
+ {0x047e, {1|F|D, {0x047f}}},
+ {0x0480, {1|F|D, {0x0481}}},
+ {0x048a, {1|F|D, {0x048b}}},
+ {0x048c, {1|F|D, {0x048d}}},
+ {0x048e, {1|F|D, {0x048f}}},
+ {0x0490, {1|F|D, {0x0491}}},
+ {0x0492, {1|F|D, {0x0493}}},
+ {0x0494, {1|F|D, {0x0495}}},
+ {0x0496, {1|F|D, {0x0497}}},
+ {0x0498, {1|F|D, {0x0499}}},
+ {0x049a, {1|F|D, {0x049b}}},
+ {0x049c, {1|F|D, {0x049d}}},
+ {0x049e, {1|F|D, {0x049f}}},
+ {0x04a0, {1|F|D, {0x04a1}}},
+ {0x04a2, {1|F|D, {0x04a3}}},
+ {0x04a4, {1|F|D, {0x04a5}}},
+ {0x04a6, {1|F|D, {0x04a7}}},
+ {0x04a8, {1|F|D, {0x04a9}}},
+ {0x04aa, {1|F|D, {0x04ab}}},
+ {0x04ac, {1|F|D, {0x04ad}}},
+ {0x04ae, {1|F|D, {0x04af}}},
+ {0x04b0, {1|F|D, {0x04b1}}},
+ {0x04b2, {1|F|D, {0x04b3}}},
+ {0x04b4, {1|F|D, {0x04b5}}},
+ {0x04b6, {1|F|D, {0x04b7}}},
+ {0x04b8, {1|F|D, {0x04b9}}},
+ {0x04ba, {1|F|D, {0x04bb}}},
+ {0x04bc, {1|F|D, {0x04bd}}},
+ {0x04be, {1|F|D, {0x04bf}}},
+ {0x04c0, {1|F|D, {0x04cf}}},
+ {0x04c1, {1|F|D, {0x04c2}}},
+ {0x04c3, {1|F|D, {0x04c4}}},
+ {0x04c5, {1|F|D, {0x04c6}}},
+ {0x04c7, {1|F|D, {0x04c8}}},
+ {0x04c9, {1|F|D, {0x04ca}}},
+ {0x04cb, {1|F|D, {0x04cc}}},
+ {0x04cd, {1|F|D, {0x04ce}}},
+ {0x04d0, {1|F|D, {0x04d1}}},
+ {0x04d2, {1|F|D, {0x04d3}}},
+ {0x04d4, {1|F|D, {0x04d5}}},
+ {0x04d6, {1|F|D, {0x04d7}}},
+ {0x04d8, {1|F|D, {0x04d9}}},
+ {0x04da, {1|F|D, {0x04db}}},
+ {0x04dc, {1|F|D, {0x04dd}}},
+ {0x04de, {1|F|D, {0x04df}}},
+ {0x04e0, {1|F|D, {0x04e1}}},
+ {0x04e2, {1|F|D, {0x04e3}}},
+ {0x04e4, {1|F|D, {0x04e5}}},
+ {0x04e6, {1|F|D, {0x04e7}}},
+ {0x04e8, {1|F|D, {0x04e9}}},
+ {0x04ea, {1|F|D, {0x04eb}}},
+ {0x04ec, {1|F|D, {0x04ed}}},
+ {0x04ee, {1|F|D, {0x04ef}}},
+ {0x04f0, {1|F|D, {0x04f1}}},
+ {0x04f2, {1|F|D, {0x04f3}}},
+ {0x04f4, {1|F|D, {0x04f5}}},
+ {0x04f6, {1|F|D, {0x04f7}}},
+ {0x04f8, {1|F|D, {0x04f9}}},
+ {0x04fa, {1|F|D, {0x04fb}}},
+ {0x04fc, {1|F|D, {0x04fd}}},
+ {0x04fe, {1|F|D, {0x04ff}}},
+ {0x0500, {1|F|D, {0x0501}}},
+ {0x0502, {1|F|D, {0x0503}}},
+ {0x0504, {1|F|D, {0x0505}}},
+ {0x0506, {1|F|D, {0x0507}}},
+ {0x0508, {1|F|D, {0x0509}}},
+ {0x050a, {1|F|D, {0x050b}}},
+ {0x050c, {1|F|D, {0x050d}}},
+ {0x050e, {1|F|D, {0x050f}}},
+ {0x0510, {1|F|D, {0x0511}}},
+ {0x0512, {1|F|D, {0x0513}}},
+ {0x0514, {1|F|D, {0x0515}}},
+ {0x0516, {1|F|D, {0x0517}}},
+ {0x0518, {1|F|D, {0x0519}}},
+ {0x051a, {1|F|D, {0x051b}}},
+ {0x051c, {1|F|D, {0x051d}}},
+ {0x051e, {1|F|D, {0x051f}}},
+ {0x0520, {1|F|D, {0x0521}}},
+ {0x0522, {1|F|D, {0x0523}}},
+ {0x0524, {1|F|D, {0x0525}}},
+ {0x0526, {1|F|D, {0x0527}}},
+ {0x0528, {1|F|D, {0x0529}}},
+ {0x052a, {1|F|D, {0x052b}}},
+ {0x052c, {1|F|D, {0x052d}}},
+ {0x052e, {1|F|D, {0x052f}}},
+ {0x0531, {1|F|D, {0x0561}}},
+ {0x0532, {1|F|D, {0x0562}}},
+ {0x0533, {1|F|D, {0x0563}}},
+ {0x0534, {1|F|D, {0x0564}}},
+ {0x0535, {1|F|D, {0x0565}}},
+ {0x0536, {1|F|D, {0x0566}}},
+ {0x0537, {1|F|D, {0x0567}}},
+ {0x0538, {1|F|D, {0x0568}}},
+ {0x0539, {1|F|D, {0x0569}}},
+ {0x053a, {1|F|D, {0x056a}}},
+ {0x053b, {1|F|D, {0x056b}}},
+ {0x053c, {1|F|D, {0x056c}}},
+ {0x053d, {1|F|D, {0x056d}}},
+ {0x053e, {1|F|D, {0x056e}}},
+ {0x053f, {1|F|D, {0x056f}}},
+ {0x0540, {1|F|D, {0x0570}}},
+ {0x0541, {1|F|D, {0x0571}}},
+ {0x0542, {1|F|D, {0x0572}}},
+ {0x0543, {1|F|D, {0x0573}}},
+ {0x0544, {1|F|D, {0x0574}}},
+ {0x0545, {1|F|D, {0x0575}}},
+ {0x0546, {1|F|D, {0x0576}}},
+ {0x0547, {1|F|D, {0x0577}}},
+ {0x0548, {1|F|D, {0x0578}}},
+ {0x0549, {1|F|D, {0x0579}}},
+ {0x054a, {1|F|D, {0x057a}}},
+ {0x054b, {1|F|D, {0x057b}}},
+ {0x054c, {1|F|D, {0x057c}}},
+ {0x054d, {1|F|D, {0x057d}}},
+ {0x054e, {1|F|D, {0x057e}}},
+ {0x054f, {1|F|D, {0x057f}}},
+ {0x0550, {1|F|D, {0x0580}}},
+ {0x0551, {1|F|D, {0x0581}}},
+ {0x0552, {1|F|D, {0x0582}}},
+ {0x0553, {1|F|D, {0x0583}}},
+ {0x0554, {1|F|D, {0x0584}}},
+ {0x0555, {1|F|D, {0x0585}}},
+ {0x0556, {1|F|D, {0x0586}}},
+ {0x0587, {2|F|ST|SU|I(41), {0x0565, 0x0582}}},
+ {0x10a0, {1|F|D, {0x2d00}}},
+ {0x10a1, {1|F|D, {0x2d01}}},
+ {0x10a2, {1|F|D, {0x2d02}}},
+ {0x10a3, {1|F|D, {0x2d03}}},
+ {0x10a4, {1|F|D, {0x2d04}}},
+ {0x10a5, {1|F|D, {0x2d05}}},
+ {0x10a6, {1|F|D, {0x2d06}}},
+ {0x10a7, {1|F|D, {0x2d07}}},
+ {0x10a8, {1|F|D, {0x2d08}}},
+ {0x10a9, {1|F|D, {0x2d09}}},
+ {0x10aa, {1|F|D, {0x2d0a}}},
+ {0x10ab, {1|F|D, {0x2d0b}}},
+ {0x10ac, {1|F|D, {0x2d0c}}},
+ {0x10ad, {1|F|D, {0x2d0d}}},
+ {0x10ae, {1|F|D, {0x2d0e}}},
+ {0x10af, {1|F|D, {0x2d0f}}},
+ {0x10b0, {1|F|D, {0x2d10}}},
+ {0x10b1, {1|F|D, {0x2d11}}},
+ {0x10b2, {1|F|D, {0x2d12}}},
+ {0x10b3, {1|F|D, {0x2d13}}},
+ {0x10b4, {1|F|D, {0x2d14}}},
+ {0x10b5, {1|F|D, {0x2d15}}},
+ {0x10b6, {1|F|D, {0x2d16}}},
+ {0x10b7, {1|F|D, {0x2d17}}},
+ {0x10b8, {1|F|D, {0x2d18}}},
+ {0x10b9, {1|F|D, {0x2d19}}},
+ {0x10ba, {1|F|D, {0x2d1a}}},
+ {0x10bb, {1|F|D, {0x2d1b}}},
+ {0x10bc, {1|F|D, {0x2d1c}}},
+ {0x10bd, {1|F|D, {0x2d1d}}},
+ {0x10be, {1|F|D, {0x2d1e}}},
+ {0x10bf, {1|F|D, {0x2d1f}}},
+ {0x10c0, {1|F|D, {0x2d20}}},
+ {0x10c1, {1|F|D, {0x2d21}}},
+ {0x10c2, {1|F|D, {0x2d22}}},
+ {0x10c3, {1|F|D, {0x2d23}}},
+ {0x10c4, {1|F|D, {0x2d24}}},
+ {0x10c5, {1|F|D, {0x2d25}}},
+ {0x10c7, {1|F|D, {0x2d27}}},
+ {0x10cd, {1|F|D, {0x2d2d}}},
+ {0x13f8, {1|F|U, {0x13f0}}},
+ {0x13f9, {1|F|U, {0x13f1}}},
+ {0x13fa, {1|F|U, {0x13f2}}},
+ {0x13fb, {1|F|U, {0x13f3}}},
+ {0x13fc, {1|F|U, {0x13f4}}},
+ {0x13fd, {1|F|U, {0x13f5}}},
+ {0x1c80, {1|F|SU|I(45), {0x0432}}},
+ {0x1c81, {1|F|SU|I(46), {0x0434}}},
+ {0x1c82, {1|F|SU|I(47), {0x043e}}},
+ {0x1c83, {1|F|SU|I(48), {0x0441}}},
+ {0x1c84, {1|F|SU|I(49), {0x0442}}},
+ {0x1c85, {1|F|SU|I(50), {0x0442}}},
+ {0x1c86, {1|F|SU|I(51), {0x044a}}},
+ {0x1c87, {1|F|SU|I(52), {0x0463}}},
+ {0x1c88, {1|F|SU|I(53), {0xa64b}}},
+ {0x1c90, {1|F|D, {0x10d0}}},
+ {0x1c91, {1|F|D, {0x10d1}}},
+ {0x1c92, {1|F|D, {0x10d2}}},
+ {0x1c93, {1|F|D, {0x10d3}}},
+ {0x1c94, {1|F|D, {0x10d4}}},
+ {0x1c95, {1|F|D, {0x10d5}}},
+ {0x1c96, {1|F|D, {0x10d6}}},
+ {0x1c97, {1|F|D, {0x10d7}}},
+ {0x1c98, {1|F|D, {0x10d8}}},
+ {0x1c99, {1|F|D, {0x10d9}}},
+ {0x1c9a, {1|F|D, {0x10da}}},
+ {0x1c9b, {1|F|D, {0x10db}}},
+ {0x1c9c, {1|F|D, {0x10dc}}},
+ {0x1c9d, {1|F|D, {0x10dd}}},
+ {0x1c9e, {1|F|D, {0x10de}}},
+ {0x1c9f, {1|F|D, {0x10df}}},
+ {0x1ca0, {1|F|D, {0x10e0}}},
+ {0x1ca1, {1|F|D, {0x10e1}}},
+ {0x1ca2, {1|F|D, {0x10e2}}},
+ {0x1ca3, {1|F|D, {0x10e3}}},
+ {0x1ca4, {1|F|D, {0x10e4}}},
+ {0x1ca5, {1|F|D, {0x10e5}}},
+ {0x1ca6, {1|F|D, {0x10e6}}},
+ {0x1ca7, {1|F|D, {0x10e7}}},
+ {0x1ca8, {1|F|D, {0x10e8}}},
+ {0x1ca9, {1|F|D, {0x10e9}}},
+ {0x1caa, {1|F|D, {0x10ea}}},
+ {0x1cab, {1|F|D, {0x10eb}}},
+ {0x1cac, {1|F|D, {0x10ec}}},
+ {0x1cad, {1|F|D, {0x10ed}}},
+ {0x1cae, {1|F|D, {0x10ee}}},
+ {0x1caf, {1|F|D, {0x10ef}}},
+ {0x1cb0, {1|F|D, {0x10f0}}},
+ {0x1cb1, {1|F|D, {0x10f1}}},
+ {0x1cb2, {1|F|D, {0x10f2}}},
+ {0x1cb3, {1|F|D, {0x10f3}}},
+ {0x1cb4, {1|F|D, {0x10f4}}},
+ {0x1cb5, {1|F|D, {0x10f5}}},
+ {0x1cb6, {1|F|D, {0x10f6}}},
+ {0x1cb7, {1|F|D, {0x10f7}}},
+ {0x1cb8, {1|F|D, {0x10f8}}},
+ {0x1cb9, {1|F|D, {0x10f9}}},
+ {0x1cba, {1|F|D, {0x10fa}}},
+ {0x1cbd, {1|F|D, {0x10fd}}},
+ {0x1cbe, {1|F|D, {0x10fe}}},
+ {0x1cbf, {1|F|D, {0x10ff}}},
+ {0x1e00, {1|F|D, {0x1e01}}},
+ {0x1e02, {1|F|D, {0x1e03}}},
+ {0x1e04, {1|F|D, {0x1e05}}},
+ {0x1e06, {1|F|D, {0x1e07}}},
+ {0x1e08, {1|F|D, {0x1e09}}},
+ {0x1e0a, {1|F|D, {0x1e0b}}},
+ {0x1e0c, {1|F|D, {0x1e0d}}},
+ {0x1e0e, {1|F|D, {0x1e0f}}},
+ {0x1e10, {1|F|D, {0x1e11}}},
+ {0x1e12, {1|F|D, {0x1e13}}},
+ {0x1e14, {1|F|D, {0x1e15}}},
+ {0x1e16, {1|F|D, {0x1e17}}},
+ {0x1e18, {1|F|D, {0x1e19}}},
+ {0x1e1a, {1|F|D, {0x1e1b}}},
+ {0x1e1c, {1|F|D, {0x1e1d}}},
+ {0x1e1e, {1|F|D, {0x1e1f}}},
+ {0x1e20, {1|F|D, {0x1e21}}},
+ {0x1e22, {1|F|D, {0x1e23}}},
+ {0x1e24, {1|F|D, {0x1e25}}},
+ {0x1e26, {1|F|D, {0x1e27}}},
+ {0x1e28, {1|F|D, {0x1e29}}},
+ {0x1e2a, {1|F|D, {0x1e2b}}},
+ {0x1e2c, {1|F|D, {0x1e2d}}},
+ {0x1e2e, {1|F|D, {0x1e2f}}},
+ {0x1e30, {1|F|D, {0x1e31}}},
+ {0x1e32, {1|F|D, {0x1e33}}},
+ {0x1e34, {1|F|D, {0x1e35}}},
+ {0x1e36, {1|F|D, {0x1e37}}},
+ {0x1e38, {1|F|D, {0x1e39}}},
+ {0x1e3a, {1|F|D, {0x1e3b}}},
+ {0x1e3c, {1|F|D, {0x1e3d}}},
+ {0x1e3e, {1|F|D, {0x1e3f}}},
+ {0x1e40, {1|F|D, {0x1e41}}},
+ {0x1e42, {1|F|D, {0x1e43}}},
+ {0x1e44, {1|F|D, {0x1e45}}},
+ {0x1e46, {1|F|D, {0x1e47}}},
+ {0x1e48, {1|F|D, {0x1e49}}},
+ {0x1e4a, {1|F|D, {0x1e4b}}},
+ {0x1e4c, {1|F|D, {0x1e4d}}},
+ {0x1e4e, {1|F|D, {0x1e4f}}},
+ {0x1e50, {1|F|D, {0x1e51}}},
+ {0x1e52, {1|F|D, {0x1e53}}},
+ {0x1e54, {1|F|D, {0x1e55}}},
+ {0x1e56, {1|F|D, {0x1e57}}},
+ {0x1e58, {1|F|D, {0x1e59}}},
+ {0x1e5a, {1|F|D, {0x1e5b}}},
+ {0x1e5c, {1|F|D, {0x1e5d}}},
+ {0x1e5e, {1|F|D, {0x1e5f}}},
+ {0x1e60, {1|F|D, {0x1e61}}},
+ {0x1e62, {1|F|D, {0x1e63}}},
+ {0x1e64, {1|F|D, {0x1e65}}},
+ {0x1e66, {1|F|D, {0x1e67}}},
+ {0x1e68, {1|F|D, {0x1e69}}},
+ {0x1e6a, {1|F|D, {0x1e6b}}},
+ {0x1e6c, {1|F|D, {0x1e6d}}},
+ {0x1e6e, {1|F|D, {0x1e6f}}},
+ {0x1e70, {1|F|D, {0x1e71}}},
+ {0x1e72, {1|F|D, {0x1e73}}},
+ {0x1e74, {1|F|D, {0x1e75}}},
+ {0x1e76, {1|F|D, {0x1e77}}},
+ {0x1e78, {1|F|D, {0x1e79}}},
+ {0x1e7a, {1|F|D, {0x1e7b}}},
+ {0x1e7c, {1|F|D, {0x1e7d}}},
+ {0x1e7e, {1|F|D, {0x1e7f}}},
+ {0x1e80, {1|F|D, {0x1e81}}},
+ {0x1e82, {1|F|D, {0x1e83}}},
+ {0x1e84, {1|F|D, {0x1e85}}},
+ {0x1e86, {1|F|D, {0x1e87}}},
+ {0x1e88, {1|F|D, {0x1e89}}},
+ {0x1e8a, {1|F|D, {0x1e8b}}},
+ {0x1e8c, {1|F|D, {0x1e8d}}},
+ {0x1e8e, {1|F|D, {0x1e8f}}},
+ {0x1e90, {1|F|D, {0x1e91}}},
+ {0x1e92, {1|F|D, {0x1e93}}},
+ {0x1e94, {1|F|D, {0x1e95}}},
+ {0x1e96, {2|F|SU|I(54), {0x0068, 0x0331}}},
+ {0x1e97, {2|F|SU|I(56), {0x0074, 0x0308}}},
+ {0x1e98, {2|F|SU|I(58), {0x0077, 0x030a}}},
+ {0x1e99, {2|F|SU|I(60), {0x0079, 0x030a}}},
+ {0x1e9a, {2|F|SU|I(62), {0x0061, 0x02be}}},
+ {0x1e9b, {1|F|SU|I(64), {0x1e61}}},
+ {0x1e9e, {2|F|SL|I(65), {0x0073, 0x0073}}},
+ {0x1ea0, {1|F|D, {0x1ea1}}},
+ {0x1ea2, {1|F|D, {0x1ea3}}},
+ {0x1ea4, {1|F|D, {0x1ea5}}},
+ {0x1ea6, {1|F|D, {0x1ea7}}},
+ {0x1ea8, {1|F|D, {0x1ea9}}},
+ {0x1eaa, {1|F|D, {0x1eab}}},
+ {0x1eac, {1|F|D, {0x1ead}}},
+ {0x1eae, {1|F|D, {0x1eaf}}},
+ {0x1eb0, {1|F|D, {0x1eb1}}},
+ {0x1eb2, {1|F|D, {0x1eb3}}},
+ {0x1eb4, {1|F|D, {0x1eb5}}},
+ {0x1eb6, {1|F|D, {0x1eb7}}},
+ {0x1eb8, {1|F|D, {0x1eb9}}},
+ {0x1eba, {1|F|D, {0x1ebb}}},
+ {0x1ebc, {1|F|D, {0x1ebd}}},
+ {0x1ebe, {1|F|D, {0x1ebf}}},
+ {0x1ec0, {1|F|D, {0x1ec1}}},
+ {0x1ec2, {1|F|D, {0x1ec3}}},
+ {0x1ec4, {1|F|D, {0x1ec5}}},
+ {0x1ec6, {1|F|D, {0x1ec7}}},
+ {0x1ec8, {1|F|D, {0x1ec9}}},
+ {0x1eca, {1|F|D, {0x1ecb}}},
+ {0x1ecc, {1|F|D, {0x1ecd}}},
+ {0x1ece, {1|F|D, {0x1ecf}}},
+ {0x1ed0, {1|F|D, {0x1ed1}}},
+ {0x1ed2, {1|F|D, {0x1ed3}}},
+ {0x1ed4, {1|F|D, {0x1ed5}}},
+ {0x1ed6, {1|F|D, {0x1ed7}}},
+ {0x1ed8, {1|F|D, {0x1ed9}}},
+ {0x1eda, {1|F|D, {0x1edb}}},
+ {0x1edc, {1|F|D, {0x1edd}}},
+ {0x1ede, {1|F|D, {0x1edf}}},
+ {0x1ee0, {1|F|D, {0x1ee1}}},
+ {0x1ee2, {1|F|D, {0x1ee3}}},
+ {0x1ee4, {1|F|D, {0x1ee5}}},
+ {0x1ee6, {1|F|D, {0x1ee7}}},
+ {0x1ee8, {1|F|D, {0x1ee9}}},
+ {0x1eea, {1|F|D, {0x1eeb}}},
+ {0x1eec, {1|F|D, {0x1eed}}},
+ {0x1eee, {1|F|D, {0x1eef}}},
+ {0x1ef0, {1|F|D, {0x1ef1}}},
+ {0x1ef2, {1|F|D, {0x1ef3}}},
+ {0x1ef4, {1|F|D, {0x1ef5}}},
+ {0x1ef6, {1|F|D, {0x1ef7}}},
+ {0x1ef8, {1|F|D, {0x1ef9}}},
+ {0x1efa, {1|F|D, {0x1efb}}},
+ {0x1efc, {1|F|D, {0x1efd}}},
+ {0x1efe, {1|F|D, {0x1eff}}},
+ {0x1f08, {1|F|D, {0x1f00}}},
+ {0x1f09, {1|F|D, {0x1f01}}},
+ {0x1f0a, {1|F|D, {0x1f02}}},
+ {0x1f0b, {1|F|D, {0x1f03}}},
+ {0x1f0c, {1|F|D, {0x1f04}}},
+ {0x1f0d, {1|F|D, {0x1f05}}},
+ {0x1f0e, {1|F|D, {0x1f06}}},
+ {0x1f0f, {1|F|D, {0x1f07}}},
+ {0x1f18, {1|F|D, {0x1f10}}},
+ {0x1f19, {1|F|D, {0x1f11}}},
+ {0x1f1a, {1|F|D, {0x1f12}}},
+ {0x1f1b, {1|F|D, {0x1f13}}},
+ {0x1f1c, {1|F|D, {0x1f14}}},
+ {0x1f1d, {1|F|D, {0x1f15}}},
+ {0x1f28, {1|F|D, {0x1f20}}},
+ {0x1f29, {1|F|D, {0x1f21}}},
+ {0x1f2a, {1|F|D, {0x1f22}}},
+ {0x1f2b, {1|F|D, {0x1f23}}},
+ {0x1f2c, {1|F|D, {0x1f24}}},
+ {0x1f2d, {1|F|D, {0x1f25}}},
+ {0x1f2e, {1|F|D, {0x1f26}}},
+ {0x1f2f, {1|F|D, {0x1f27}}},
+ {0x1f38, {1|F|D, {0x1f30}}},
+ {0x1f39, {1|F|D, {0x1f31}}},
+ {0x1f3a, {1|F|D, {0x1f32}}},
+ {0x1f3b, {1|F|D, {0x1f33}}},
+ {0x1f3c, {1|F|D, {0x1f34}}},
+ {0x1f3d, {1|F|D, {0x1f35}}},
+ {0x1f3e, {1|F|D, {0x1f36}}},
+ {0x1f3f, {1|F|D, {0x1f37}}},
+ {0x1f48, {1|F|D, {0x1f40}}},
+ {0x1f49, {1|F|D, {0x1f41}}},
+ {0x1f4a, {1|F|D, {0x1f42}}},
+ {0x1f4b, {1|F|D, {0x1f43}}},
+ {0x1f4c, {1|F|D, {0x1f44}}},
+ {0x1f4d, {1|F|D, {0x1f45}}},
+ {0x1f50, {2|F|SU|I(66), {0x03c5, 0x0313}}},
+ {0x1f52, {3|F|SU|I(68), {0x03c5, 0x0313, 0x0300}}},
+ {0x1f54, {3|F|SU|I(71), {0x03c5, 0x0313, 0x0301}}},
+ {0x1f56, {3|F|SU|I(74), {0x03c5, 0x0313, 0x0342}}},
+ {0x1f59, {1|F|D, {0x1f51}}},
+ {0x1f5b, {1|F|D, {0x1f53}}},
+ {0x1f5d, {1|F|D, {0x1f55}}},
+ {0x1f5f, {1|F|D, {0x1f57}}},
+ {0x1f68, {1|F|D, {0x1f60}}},
+ {0x1f69, {1|F|D, {0x1f61}}},
+ {0x1f6a, {1|F|D, {0x1f62}}},
+ {0x1f6b, {1|F|D, {0x1f63}}},
+ {0x1f6c, {1|F|D, {0x1f64}}},
+ {0x1f6d, {1|F|D, {0x1f65}}},
+ {0x1f6e, {1|F|D, {0x1f66}}},
+ {0x1f6f, {1|F|D, {0x1f67}}},
+ {0x1f80, {2|F|ST|SU|I(77), {0x1f00, 0x03b9}}},
+ {0x1f81, {2|F|ST|SU|I(80), {0x1f01, 0x03b9}}},
+ {0x1f82, {2|F|ST|SU|I(83), {0x1f02, 0x03b9}}},
+ {0x1f83, {2|F|ST|SU|I(86), {0x1f03, 0x03b9}}},
+ {0x1f84, {2|F|ST|SU|I(89), {0x1f04, 0x03b9}}},
+ {0x1f85, {2|F|ST|SU|I(92), {0x1f05, 0x03b9}}},
+ {0x1f86, {2|F|ST|SU|I(95), {0x1f06, 0x03b9}}},
+ {0x1f87, {2|F|ST|SU|I(98), {0x1f07, 0x03b9}}},
+ {0x1f88, {2|F|IT|SL|SU|I(101), {0x1f00, 0x03b9}}},
+ {0x1f89, {2|F|IT|SL|SU|I(106), {0x1f01, 0x03b9}}},
+ {0x1f8a, {2|F|IT|SL|SU|I(111), {0x1f02, 0x03b9}}},
+ {0x1f8b, {2|F|IT|SL|SU|I(116), {0x1f03, 0x03b9}}},
+ {0x1f8c, {2|F|IT|SL|SU|I(121), {0x1f04, 0x03b9}}},
+ {0x1f8d, {2|F|IT|SL|SU|I(126), {0x1f05, 0x03b9}}},
+ {0x1f8e, {2|F|IT|SL|SU|I(131), {0x1f06, 0x03b9}}},
+ {0x1f8f, {2|F|IT|SL|SU|I(136), {0x1f07, 0x03b9}}},
+ {0x1f90, {2|F|ST|SU|I(141), {0x1f20, 0x03b9}}},
+ {0x1f91, {2|F|ST|SU|I(144), {0x1f21, 0x03b9}}},
+ {0x1f92, {2|F|ST|SU|I(147), {0x1f22, 0x03b9}}},
+ {0x1f93, {2|F|ST|SU|I(150), {0x1f23, 0x03b9}}},
+ {0x1f94, {2|F|ST|SU|I(153), {0x1f24, 0x03b9}}},
+ {0x1f95, {2|F|ST|SU|I(156), {0x1f25, 0x03b9}}},
+ {0x1f96, {2|F|ST|SU|I(159), {0x1f26, 0x03b9}}},
+ {0x1f97, {2|F|ST|SU|I(162), {0x1f27, 0x03b9}}},
+ {0x1f98, {2|F|IT|SL|SU|I(165), {0x1f20, 0x03b9}}},
+ {0x1f99, {2|F|IT|SL|SU|I(170), {0x1f21, 0x03b9}}},
+ {0x1f9a, {2|F|IT|SL|SU|I(175), {0x1f22, 0x03b9}}},
+ {0x1f9b, {2|F|IT|SL|SU|I(180), {0x1f23, 0x03b9}}},
+ {0x1f9c, {2|F|IT|SL|SU|I(185), {0x1f24, 0x03b9}}},
+ {0x1f9d, {2|F|IT|SL|SU|I(190), {0x1f25, 0x03b9}}},
+ {0x1f9e, {2|F|IT|SL|SU|I(195), {0x1f26, 0x03b9}}},
+ {0x1f9f, {2|F|IT|SL|SU|I(200), {0x1f27, 0x03b9}}},
+ {0x1fa0, {2|F|ST|SU|I(205), {0x1f60, 0x03b9}}},
+ {0x1fa1, {2|F|ST|SU|I(208), {0x1f61, 0x03b9}}},
+ {0x1fa2, {2|F|ST|SU|I(211), {0x1f62, 0x03b9}}},
+ {0x1fa3, {2|F|ST|SU|I(214), {0x1f63, 0x03b9}}},
+ {0x1fa4, {2|F|ST|SU|I(217), {0x1f64, 0x03b9}}},
+ {0x1fa5, {2|F|ST|SU|I(220), {0x1f65, 0x03b9}}},
+ {0x1fa6, {2|F|ST|SU|I(223), {0x1f66, 0x03b9}}},
+ {0x1fa7, {2|F|ST|SU|I(226), {0x1f67, 0x03b9}}},
+ {0x1fa8, {2|F|IT|SL|SU|I(229), {0x1f60, 0x03b9}}},
+ {0x1fa9, {2|F|IT|SL|SU|I(234), {0x1f61, 0x03b9}}},
+ {0x1faa, {2|F|IT|SL|SU|I(239), {0x1f62, 0x03b9}}},
+ {0x1fab, {2|F|IT|SL|SU|I(244), {0x1f63, 0x03b9}}},
+ {0x1fac, {2|F|IT|SL|SU|I(249), {0x1f64, 0x03b9}}},
+ {0x1fad, {2|F|IT|SL|SU|I(254), {0x1f65, 0x03b9}}},
+ {0x1fae, {2|F|IT|SL|SU|I(259), {0x1f66, 0x03b9}}},
+ {0x1faf, {2|F|IT|SL|SU|I(264), {0x1f67, 0x03b9}}},
+ {0x1fb2, {2|F|ST|SU|I(269), {0x1f70, 0x03b9}}},
+ {0x1fb3, {2|F|ST|SU|I(273), {0x03b1, 0x03b9}}},
+ {0x1fb4, {2|F|ST|SU|I(276), {0x03ac, 0x03b9}}},
+ {0x1fb6, {2|F|SU|I(280), {0x03b1, 0x0342}}},
+ {0x1fb7, {3|F|ST|SU|I(282), {0x03b1, 0x0342, 0x03b9}}},
+ {0x1fb8, {1|F|D, {0x1fb0}}},
+ {0x1fb9, {1|F|D, {0x1fb1}}},
+ {0x1fba, {1|F|D, {0x1f70}}},
+ {0x1fbb, {1|F|D, {0x1f71}}},
+ {0x1fbc, {2|F|IT|SL|SU|I(288), {0x03b1, 0x03b9}}},
+ {0x1fbe, {1|F|SU|I(293), {0x03b9}}},
+ {0x1fc2, {2|F|ST|SU|I(294), {0x1f74, 0x03b9}}},
+ {0x1fc3, {2|F|ST|SU|I(298), {0x03b7, 0x03b9}}},
+ {0x1fc4, {2|F|ST|SU|I(301), {0x03ae, 0x03b9}}},
+ {0x1fc6, {2|F|SU|I(305), {0x03b7, 0x0342}}},
+ {0x1fc7, {3|F|ST|SU|I(307), {0x03b7, 0x0342, 0x03b9}}},
+ {0x1fc8, {1|F|D, {0x1f72}}},
+ {0x1fc9, {1|F|D, {0x1f73}}},
+ {0x1fca, {1|F|D, {0x1f74}}},
+ {0x1fcb, {1|F|D, {0x1f75}}},
+ {0x1fcc, {2|F|IT|SL|SU|I(313), {0x03b7, 0x03b9}}},
+ {0x1fd2, {3|F|SU|I(318), {0x03b9, 0x0308, 0x0300}}},
+ {0x1fd3, {3|F|SU|I(321), {0x03b9, 0x0308, 0x0301}}},
+ {0x1fd6, {2|F|SU|I(324), {0x03b9, 0x0342}}},
+ {0x1fd7, {3|F|SU|I(326), {0x03b9, 0x0308, 0x0342}}},
+ {0x1fd8, {1|F|D, {0x1fd0}}},
+ {0x1fd9, {1|F|D, {0x1fd1}}},
+ {0x1fda, {1|F|D, {0x1f76}}},
+ {0x1fdb, {1|F|D, {0x1f77}}},
+ {0x1fe2, {3|F|SU|I(329), {0x03c5, 0x0308, 0x0300}}},
+ {0x1fe3, {3|F|SU|I(332), {0x03c5, 0x0308, 0x0301}}},
+ {0x1fe4, {2|F|SU|I(335), {0x03c1, 0x0313}}},
+ {0x1fe6, {2|F|SU|I(337), {0x03c5, 0x0342}}},
+ {0x1fe7, {3|F|SU|I(339), {0x03c5, 0x0308, 0x0342}}},
+ {0x1fe8, {1|F|D, {0x1fe0}}},
+ {0x1fe9, {1|F|D, {0x1fe1}}},
+ {0x1fea, {1|F|D, {0x1f7a}}},
+ {0x1feb, {1|F|D, {0x1f7b}}},
+ {0x1fec, {1|F|D, {0x1fe5}}},
+ {0x1ff2, {2|F|ST|SU|I(342), {0x1f7c, 0x03b9}}},
+ {0x1ff3, {2|F|ST|SU|I(346), {0x03c9, 0x03b9}}},
+ {0x1ff4, {2|F|ST|SU|I(349), {0x03ce, 0x03b9}}},
+ {0x1ff6, {2|F|SU|I(353), {0x03c9, 0x0342}}},
+ {0x1ff7, {3|F|ST|SU|I(355), {0x03c9, 0x0342, 0x03b9}}},
+ {0x1ff8, {1|F|D, {0x1f78}}},
+ {0x1ff9, {1|F|D, {0x1f79}}},
+ {0x1ffa, {1|F|D, {0x1f7c}}},
+ {0x1ffb, {1|F|D, {0x1f7d}}},
+ {0x1ffc, {2|F|IT|SL|SU|I(361), {0x03c9, 0x03b9}}},
+ {0x2126, {1|F|D, {0x03c9}}},
+ {0x212a, {1|F|D, {0x006b}}},
+ {0x212b, {1|F|D, {0x00e5}}},
+ {0x2132, {1|F|D, {0x214e}}},
+ {0x2160, {1|F|D, {0x2170}}},
+ {0x2161, {1|F|D, {0x2171}}},
+ {0x2162, {1|F|D, {0x2172}}},
+ {0x2163, {1|F|D, {0x2173}}},
+ {0x2164, {1|F|D, {0x2174}}},
+ {0x2165, {1|F|D, {0x2175}}},
+ {0x2166, {1|F|D, {0x2176}}},
+ {0x2167, {1|F|D, {0x2177}}},
+ {0x2168, {1|F|D, {0x2178}}},
+ {0x2169, {1|F|D, {0x2179}}},
+ {0x216a, {1|F|D, {0x217a}}},
+ {0x216b, {1|F|D, {0x217b}}},
+ {0x216c, {1|F|D, {0x217c}}},
+ {0x216d, {1|F|D, {0x217d}}},
+ {0x216e, {1|F|D, {0x217e}}},
+ {0x216f, {1|F|D, {0x217f}}},
+ {0x2183, {1|F|D, {0x2184}}},
+ {0x24b6, {1|F|D, {0x24d0}}},
+ {0x24b7, {1|F|D, {0x24d1}}},
+ {0x24b8, {1|F|D, {0x24d2}}},
+ {0x24b9, {1|F|D, {0x24d3}}},
+ {0x24ba, {1|F|D, {0x24d4}}},
+ {0x24bb, {1|F|D, {0x24d5}}},
+ {0x24bc, {1|F|D, {0x24d6}}},
+ {0x24bd, {1|F|D, {0x24d7}}},
+ {0x24be, {1|F|D, {0x24d8}}},
+ {0x24bf, {1|F|D, {0x24d9}}},
+ {0x24c0, {1|F|D, {0x24da}}},
+ {0x24c1, {1|F|D, {0x24db}}},
+ {0x24c2, {1|F|D, {0x24dc}}},
+ {0x24c3, {1|F|D, {0x24dd}}},
+ {0x24c4, {1|F|D, {0x24de}}},
+ {0x24c5, {1|F|D, {0x24df}}},
+ {0x24c6, {1|F|D, {0x24e0}}},
+ {0x24c7, {1|F|D, {0x24e1}}},
+ {0x24c8, {1|F|D, {0x24e2}}},
+ {0x24c9, {1|F|D, {0x24e3}}},
+ {0x24ca, {1|F|D, {0x24e4}}},
+ {0x24cb, {1|F|D, {0x24e5}}},
+ {0x24cc, {1|F|D, {0x24e6}}},
+ {0x24cd, {1|F|D, {0x24e7}}},
+ {0x24ce, {1|F|D, {0x24e8}}},
+ {0x24cf, {1|F|D, {0x24e9}}},
+ {0x2c00, {1|F|D, {0x2c30}}},
+ {0x2c01, {1|F|D, {0x2c31}}},
+ {0x2c02, {1|F|D, {0x2c32}}},
+ {0x2c03, {1|F|D, {0x2c33}}},
+ {0x2c04, {1|F|D, {0x2c34}}},
+ {0x2c05, {1|F|D, {0x2c35}}},
+ {0x2c06, {1|F|D, {0x2c36}}},
+ {0x2c07, {1|F|D, {0x2c37}}},
+ {0x2c08, {1|F|D, {0x2c38}}},
+ {0x2c09, {1|F|D, {0x2c39}}},
+ {0x2c0a, {1|F|D, {0x2c3a}}},
+ {0x2c0b, {1|F|D, {0x2c3b}}},
+ {0x2c0c, {1|F|D, {0x2c3c}}},
+ {0x2c0d, {1|F|D, {0x2c3d}}},
+ {0x2c0e, {1|F|D, {0x2c3e}}},
+ {0x2c0f, {1|F|D, {0x2c3f}}},
+ {0x2c10, {1|F|D, {0x2c40}}},
+ {0x2c11, {1|F|D, {0x2c41}}},
+ {0x2c12, {1|F|D, {0x2c42}}},
+ {0x2c13, {1|F|D, {0x2c43}}},
+ {0x2c14, {1|F|D, {0x2c44}}},
+ {0x2c15, {1|F|D, {0x2c45}}},
+ {0x2c16, {1|F|D, {0x2c46}}},
+ {0x2c17, {1|F|D, {0x2c47}}},
+ {0x2c18, {1|F|D, {0x2c48}}},
+ {0x2c19, {1|F|D, {0x2c49}}},
+ {0x2c1a, {1|F|D, {0x2c4a}}},
+ {0x2c1b, {1|F|D, {0x2c4b}}},
+ {0x2c1c, {1|F|D, {0x2c4c}}},
+ {0x2c1d, {1|F|D, {0x2c4d}}},
+ {0x2c1e, {1|F|D, {0x2c4e}}},
+ {0x2c1f, {1|F|D, {0x2c4f}}},
+ {0x2c20, {1|F|D, {0x2c50}}},
+ {0x2c21, {1|F|D, {0x2c51}}},
+ {0x2c22, {1|F|D, {0x2c52}}},
+ {0x2c23, {1|F|D, {0x2c53}}},
+ {0x2c24, {1|F|D, {0x2c54}}},
+ {0x2c25, {1|F|D, {0x2c55}}},
+ {0x2c26, {1|F|D, {0x2c56}}},
+ {0x2c27, {1|F|D, {0x2c57}}},
+ {0x2c28, {1|F|D, {0x2c58}}},
+ {0x2c29, {1|F|D, {0x2c59}}},
+ {0x2c2a, {1|F|D, {0x2c5a}}},
+ {0x2c2b, {1|F|D, {0x2c5b}}},
+ {0x2c2c, {1|F|D, {0x2c5c}}},
+ {0x2c2d, {1|F|D, {0x2c5d}}},
+ {0x2c2e, {1|F|D, {0x2c5e}}},
+ {0x2c60, {1|F|D, {0x2c61}}},
+ {0x2c62, {1|F|D, {0x026b}}},
+ {0x2c63, {1|F|D, {0x1d7d}}},
+ {0x2c64, {1|F|D, {0x027d}}},
+ {0x2c67, {1|F|D, {0x2c68}}},
+ {0x2c69, {1|F|D, {0x2c6a}}},
+ {0x2c6b, {1|F|D, {0x2c6c}}},
+ {0x2c6d, {1|F|D, {0x0251}}},
+ {0x2c6e, {1|F|D, {0x0271}}},
+ {0x2c6f, {1|F|D, {0x0250}}},
+ {0x2c70, {1|F|D, {0x0252}}},
+ {0x2c72, {1|F|D, {0x2c73}}},
+ {0x2c75, {1|F|D, {0x2c76}}},
+ {0x2c7e, {1|F|D, {0x023f}}},
+ {0x2c7f, {1|F|D, {0x0240}}},
+ {0x2c80, {1|F|D, {0x2c81}}},
+ {0x2c82, {1|F|D, {0x2c83}}},
+ {0x2c84, {1|F|D, {0x2c85}}},
+ {0x2c86, {1|F|D, {0x2c87}}},
+ {0x2c88, {1|F|D, {0x2c89}}},
+ {0x2c8a, {1|F|D, {0x2c8b}}},
+ {0x2c8c, {1|F|D, {0x2c8d}}},
+ {0x2c8e, {1|F|D, {0x2c8f}}},
+ {0x2c90, {1|F|D, {0x2c91}}},
+ {0x2c92, {1|F|D, {0x2c93}}},
+ {0x2c94, {1|F|D, {0x2c95}}},
+ {0x2c96, {1|F|D, {0x2c97}}},
+ {0x2c98, {1|F|D, {0x2c99}}},
+ {0x2c9a, {1|F|D, {0x2c9b}}},
+ {0x2c9c, {1|F|D, {0x2c9d}}},
+ {0x2c9e, {1|F|D, {0x2c9f}}},
+ {0x2ca0, {1|F|D, {0x2ca1}}},
+ {0x2ca2, {1|F|D, {0x2ca3}}},
+ {0x2ca4, {1|F|D, {0x2ca5}}},
+ {0x2ca6, {1|F|D, {0x2ca7}}},
+ {0x2ca8, {1|F|D, {0x2ca9}}},
+ {0x2caa, {1|F|D, {0x2cab}}},
+ {0x2cac, {1|F|D, {0x2cad}}},
+ {0x2cae, {1|F|D, {0x2caf}}},
+ {0x2cb0, {1|F|D, {0x2cb1}}},
+ {0x2cb2, {1|F|D, {0x2cb3}}},
+ {0x2cb4, {1|F|D, {0x2cb5}}},
+ {0x2cb6, {1|F|D, {0x2cb7}}},
+ {0x2cb8, {1|F|D, {0x2cb9}}},
+ {0x2cba, {1|F|D, {0x2cbb}}},
+ {0x2cbc, {1|F|D, {0x2cbd}}},
+ {0x2cbe, {1|F|D, {0x2cbf}}},
+ {0x2cc0, {1|F|D, {0x2cc1}}},
+ {0x2cc2, {1|F|D, {0x2cc3}}},
+ {0x2cc4, {1|F|D, {0x2cc5}}},
+ {0x2cc6, {1|F|D, {0x2cc7}}},
+ {0x2cc8, {1|F|D, {0x2cc9}}},
+ {0x2cca, {1|F|D, {0x2ccb}}},
+ {0x2ccc, {1|F|D, {0x2ccd}}},
+ {0x2cce, {1|F|D, {0x2ccf}}},
+ {0x2cd0, {1|F|D, {0x2cd1}}},
+ {0x2cd2, {1|F|D, {0x2cd3}}},
+ {0x2cd4, {1|F|D, {0x2cd5}}},
+ {0x2cd6, {1|F|D, {0x2cd7}}},
+ {0x2cd8, {1|F|D, {0x2cd9}}},
+ {0x2cda, {1|F|D, {0x2cdb}}},
+ {0x2cdc, {1|F|D, {0x2cdd}}},
+ {0x2cde, {1|F|D, {0x2cdf}}},
+ {0x2ce0, {1|F|D, {0x2ce1}}},
+ {0x2ce2, {1|F|D, {0x2ce3}}},
+ {0x2ceb, {1|F|D, {0x2cec}}},
+ {0x2ced, {1|F|D, {0x2cee}}},
+ {0x2cf2, {1|F|D, {0x2cf3}}},
+ {0xa640, {1|F|D, {0xa641}}},
+ {0xa642, {1|F|D, {0xa643}}},
+ {0xa644, {1|F|D, {0xa645}}},
+ {0xa646, {1|F|D, {0xa647}}},
+ {0xa648, {1|F|D, {0xa649}}},
+ {0xa64a, {1|F|D, {0xa64b}}},
+ {0xa64c, {1|F|D, {0xa64d}}},
+ {0xa64e, {1|F|D, {0xa64f}}},
+ {0xa650, {1|F|D, {0xa651}}},
+ {0xa652, {1|F|D, {0xa653}}},
+ {0xa654, {1|F|D, {0xa655}}},
+ {0xa656, {1|F|D, {0xa657}}},
+ {0xa658, {1|F|D, {0xa659}}},
+ {0xa65a, {1|F|D, {0xa65b}}},
+ {0xa65c, {1|F|D, {0xa65d}}},
+ {0xa65e, {1|F|D, {0xa65f}}},
+ {0xa660, {1|F|D, {0xa661}}},
+ {0xa662, {1|F|D, {0xa663}}},
+ {0xa664, {1|F|D, {0xa665}}},
+ {0xa666, {1|F|D, {0xa667}}},
+ {0xa668, {1|F|D, {0xa669}}},
+ {0xa66a, {1|F|D, {0xa66b}}},
+ {0xa66c, {1|F|D, {0xa66d}}},
+ {0xa680, {1|F|D, {0xa681}}},
+ {0xa682, {1|F|D, {0xa683}}},
+ {0xa684, {1|F|D, {0xa685}}},
+ {0xa686, {1|F|D, {0xa687}}},
+ {0xa688, {1|F|D, {0xa689}}},
+ {0xa68a, {1|F|D, {0xa68b}}},
+ {0xa68c, {1|F|D, {0xa68d}}},
+ {0xa68e, {1|F|D, {0xa68f}}},
+ {0xa690, {1|F|D, {0xa691}}},
+ {0xa692, {1|F|D, {0xa693}}},
+ {0xa694, {1|F|D, {0xa695}}},
+ {0xa696, {1|F|D, {0xa697}}},
+ {0xa698, {1|F|D, {0xa699}}},
+ {0xa69a, {1|F|D, {0xa69b}}},
+ {0xa722, {1|F|D, {0xa723}}},
+ {0xa724, {1|F|D, {0xa725}}},
+ {0xa726, {1|F|D, {0xa727}}},
+ {0xa728, {1|F|D, {0xa729}}},
+ {0xa72a, {1|F|D, {0xa72b}}},
+ {0xa72c, {1|F|D, {0xa72d}}},
+ {0xa72e, {1|F|D, {0xa72f}}},
+ {0xa732, {1|F|D, {0xa733}}},
+ {0xa734, {1|F|D, {0xa735}}},
+ {0xa736, {1|F|D, {0xa737}}},
+ {0xa738, {1|F|D, {0xa739}}},
+ {0xa73a, {1|F|D, {0xa73b}}},
+ {0xa73c, {1|F|D, {0xa73d}}},
+ {0xa73e, {1|F|D, {0xa73f}}},
+ {0xa740, {1|F|D, {0xa741}}},
+ {0xa742, {1|F|D, {0xa743}}},
+ {0xa744, {1|F|D, {0xa745}}},
+ {0xa746, {1|F|D, {0xa747}}},
+ {0xa748, {1|F|D, {0xa749}}},
+ {0xa74a, {1|F|D, {0xa74b}}},
+ {0xa74c, {1|F|D, {0xa74d}}},
+ {0xa74e, {1|F|D, {0xa74f}}},
+ {0xa750, {1|F|D, {0xa751}}},
+ {0xa752, {1|F|D, {0xa753}}},
+ {0xa754, {1|F|D, {0xa755}}},
+ {0xa756, {1|F|D, {0xa757}}},
+ {0xa758, {1|F|D, {0xa759}}},
+ {0xa75a, {1|F|D, {0xa75b}}},
+ {0xa75c, {1|F|D, {0xa75d}}},
+ {0xa75e, {1|F|D, {0xa75f}}},
+ {0xa760, {1|F|D, {0xa761}}},
+ {0xa762, {1|F|D, {0xa763}}},
+ {0xa764, {1|F|D, {0xa765}}},
+ {0xa766, {1|F|D, {0xa767}}},
+ {0xa768, {1|F|D, {0xa769}}},
+ {0xa76a, {1|F|D, {0xa76b}}},
+ {0xa76c, {1|F|D, {0xa76d}}},
+ {0xa76e, {1|F|D, {0xa76f}}},
+ {0xa779, {1|F|D, {0xa77a}}},
+ {0xa77b, {1|F|D, {0xa77c}}},
+ {0xa77d, {1|F|D, {0x1d79}}},
+ {0xa77e, {1|F|D, {0xa77f}}},
+ {0xa780, {1|F|D, {0xa781}}},
+ {0xa782, {1|F|D, {0xa783}}},
+ {0xa784, {1|F|D, {0xa785}}},
+ {0xa786, {1|F|D, {0xa787}}},
+ {0xa78b, {1|F|D, {0xa78c}}},
+ {0xa78d, {1|F|D, {0x0265}}},
+ {0xa790, {1|F|D, {0xa791}}},
+ {0xa792, {1|F|D, {0xa793}}},
+ {0xa796, {1|F|D, {0xa797}}},
+ {0xa798, {1|F|D, {0xa799}}},
+ {0xa79a, {1|F|D, {0xa79b}}},
+ {0xa79c, {1|F|D, {0xa79d}}},
+ {0xa79e, {1|F|D, {0xa79f}}},
+ {0xa7a0, {1|F|D, {0xa7a1}}},
+ {0xa7a2, {1|F|D, {0xa7a3}}},
+ {0xa7a4, {1|F|D, {0xa7a5}}},
+ {0xa7a6, {1|F|D, {0xa7a7}}},
+ {0xa7a8, {1|F|D, {0xa7a9}}},
+ {0xa7aa, {1|F|D, {0x0266}}},
+ {0xa7ab, {1|F|D, {0x025c}}},
+ {0xa7ac, {1|F|D, {0x0261}}},
+ {0xa7ad, {1|F|D, {0x026c}}},
+ {0xa7ae, {1|F|D, {0x026a}}},
+ {0xa7b0, {1|F|D, {0x029e}}},
+ {0xa7b1, {1|F|D, {0x0287}}},
+ {0xa7b2, {1|F|D, {0x029d}}},
+ {0xa7b3, {1|F|D, {0xab53}}},
+ {0xa7b4, {1|F|D, {0xa7b5}}},
+ {0xa7b6, {1|F|D, {0xa7b7}}},
+ {0xa7b8, {1|F|D, {0xa7b9}}},
+ {0xa7ba, {1|F|D, {0xa7bb}}},
+ {0xa7bc, {1|F|D, {0xa7bd}}},
+ {0xa7be, {1|F|D, {0xa7bf}}},
+ {0xa7c2, {1|F|D, {0xa7c3}}},
+ {0xa7c4, {1|F|D, {0xa794}}},
+ {0xa7c5, {1|F|D, {0x0282}}},
+ {0xa7c6, {1|F|D, {0x1d8e}}},
+ {0xab70, {1|F|U, {0x13a0}}},
+ {0xab71, {1|F|U, {0x13a1}}},
+ {0xab72, {1|F|U, {0x13a2}}},
+ {0xab73, {1|F|U, {0x13a3}}},
+ {0xab74, {1|F|U, {0x13a4}}},
+ {0xab75, {1|F|U, {0x13a5}}},
+ {0xab76, {1|F|U, {0x13a6}}},
+ {0xab77, {1|F|U, {0x13a7}}},
+ {0xab78, {1|F|U, {0x13a8}}},
+ {0xab79, {1|F|U, {0x13a9}}},
+ {0xab7a, {1|F|U, {0x13aa}}},
+ {0xab7b, {1|F|U, {0x13ab}}},
+ {0xab7c, {1|F|U, {0x13ac}}},
+ {0xab7d, {1|F|U, {0x13ad}}},
+ {0xab7e, {1|F|U, {0x13ae}}},
+ {0xab7f, {1|F|U, {0x13af}}},
+ {0xab80, {1|F|U, {0x13b0}}},
+ {0xab81, {1|F|U, {0x13b1}}},
+ {0xab82, {1|F|U, {0x13b2}}},
+ {0xab83, {1|F|U, {0x13b3}}},
+ {0xab84, {1|F|U, {0x13b4}}},
+ {0xab85, {1|F|U, {0x13b5}}},
+ {0xab86, {1|F|U, {0x13b6}}},
+ {0xab87, {1|F|U, {0x13b7}}},
+ {0xab88, {1|F|U, {0x13b8}}},
+ {0xab89, {1|F|U, {0x13b9}}},
+ {0xab8a, {1|F|U, {0x13ba}}},
+ {0xab8b, {1|F|U, {0x13bb}}},
+ {0xab8c, {1|F|U, {0x13bc}}},
+ {0xab8d, {1|F|U, {0x13bd}}},
+ {0xab8e, {1|F|U, {0x13be}}},
+ {0xab8f, {1|F|U, {0x13bf}}},
+ {0xab90, {1|F|U, {0x13c0}}},
+ {0xab91, {1|F|U, {0x13c1}}},
+ {0xab92, {1|F|U, {0x13c2}}},
+ {0xab93, {1|F|U, {0x13c3}}},
+ {0xab94, {1|F|U, {0x13c4}}},
+ {0xab95, {1|F|U, {0x13c5}}},
+ {0xab96, {1|F|U, {0x13c6}}},
+ {0xab97, {1|F|U, {0x13c7}}},
+ {0xab98, {1|F|U, {0x13c8}}},
+ {0xab99, {1|F|U, {0x13c9}}},
+ {0xab9a, {1|F|U, {0x13ca}}},
+ {0xab9b, {1|F|U, {0x13cb}}},
+ {0xab9c, {1|F|U, {0x13cc}}},
+ {0xab9d, {1|F|U, {0x13cd}}},
+ {0xab9e, {1|F|U, {0x13ce}}},
+ {0xab9f, {1|F|U, {0x13cf}}},
+ {0xaba0, {1|F|U, {0x13d0}}},
+ {0xaba1, {1|F|U, {0x13d1}}},
+ {0xaba2, {1|F|U, {0x13d2}}},
+ {0xaba3, {1|F|U, {0x13d3}}},
+ {0xaba4, {1|F|U, {0x13d4}}},
+ {0xaba5, {1|F|U, {0x13d5}}},
+ {0xaba6, {1|F|U, {0x13d6}}},
+ {0xaba7, {1|F|U, {0x13d7}}},
+ {0xaba8, {1|F|U, {0x13d8}}},
+ {0xaba9, {1|F|U, {0x13d9}}},
+ {0xabaa, {1|F|U, {0x13da}}},
+ {0xabab, {1|F|U, {0x13db}}},
+ {0xabac, {1|F|U, {0x13dc}}},
+ {0xabad, {1|F|U, {0x13dd}}},
+ {0xabae, {1|F|U, {0x13de}}},
+ {0xabaf, {1|F|U, {0x13df}}},
+ {0xabb0, {1|F|U, {0x13e0}}},
+ {0xabb1, {1|F|U, {0x13e1}}},
+ {0xabb2, {1|F|U, {0x13e2}}},
+ {0xabb3, {1|F|U, {0x13e3}}},
+ {0xabb4, {1|F|U, {0x13e4}}},
+ {0xabb5, {1|F|U, {0x13e5}}},
+ {0xabb6, {1|F|U, {0x13e6}}},
+ {0xabb7, {1|F|U, {0x13e7}}},
+ {0xabb8, {1|F|U, {0x13e8}}},
+ {0xabb9, {1|F|U, {0x13e9}}},
+ {0xabba, {1|F|U, {0x13ea}}},
+ {0xabbb, {1|F|U, {0x13eb}}},
+ {0xabbc, {1|F|U, {0x13ec}}},
+ {0xabbd, {1|F|U, {0x13ed}}},
+ {0xabbe, {1|F|U, {0x13ee}}},
+ {0xabbf, {1|F|U, {0x13ef}}},
+ {0xfb00, {2|F|ST|SU|I(366), {0x0066, 0x0066}}},
+ {0xfb01, {2|F|ST|SU|I(370), {0x0066, 0x0069}}},
+ {0xfb02, {2|F|ST|SU|I(374), {0x0066, 0x006c}}},
+ {0xfb03, {3|F|ST|SU|I(378), {0x0066, 0x0066, 0x0069}}},
+ {0xfb04, {3|F|ST|SU|I(384), {0x0066, 0x0066, 0x006c}}},
+ {0xfb05, {2|F|ST|SU|I(390), {0x0073, 0x0074}}},
+ {0xfb06, {2|F|ST|SU|I(394), {0x0073, 0x0074}}},
+ {0xfb13, {2|F|ST|SU|I(398), {0x0574, 0x0576}}},
+ {0xfb14, {2|F|ST|SU|I(402), {0x0574, 0x0565}}},
+ {0xfb15, {2|F|ST|SU|I(406), {0x0574, 0x056b}}},
+ {0xfb16, {2|F|ST|SU|I(410), {0x057e, 0x0576}}},
+ {0xfb17, {2|F|ST|SU|I(414), {0x0574, 0x056d}}},
+ {0xff21, {1|F|D, {0xff41}}},
+ {0xff22, {1|F|D, {0xff42}}},
+ {0xff23, {1|F|D, {0xff43}}},
+ {0xff24, {1|F|D, {0xff44}}},
+ {0xff25, {1|F|D, {0xff45}}},
+ {0xff26, {1|F|D, {0xff46}}},
+ {0xff27, {1|F|D, {0xff47}}},
+ {0xff28, {1|F|D, {0xff48}}},
+ {0xff29, {1|F|D, {0xff49}}},
+ {0xff2a, {1|F|D, {0xff4a}}},
+ {0xff2b, {1|F|D, {0xff4b}}},
+ {0xff2c, {1|F|D, {0xff4c}}},
+ {0xff2d, {1|F|D, {0xff4d}}},
+ {0xff2e, {1|F|D, {0xff4e}}},
+ {0xff2f, {1|F|D, {0xff4f}}},
+ {0xff30, {1|F|D, {0xff50}}},
+ {0xff31, {1|F|D, {0xff51}}},
+ {0xff32, {1|F|D, {0xff52}}},
+ {0xff33, {1|F|D, {0xff53}}},
+ {0xff34, {1|F|D, {0xff54}}},
+ {0xff35, {1|F|D, {0xff55}}},
+ {0xff36, {1|F|D, {0xff56}}},
+ {0xff37, {1|F|D, {0xff57}}},
+ {0xff38, {1|F|D, {0xff58}}},
+ {0xff39, {1|F|D, {0xff59}}},
+ {0xff3a, {1|F|D, {0xff5a}}},
+ {0x10400, {1|F|D, {0x10428}}},
+ {0x10401, {1|F|D, {0x10429}}},
+ {0x10402, {1|F|D, {0x1042a}}},
+ {0x10403, {1|F|D, {0x1042b}}},
+ {0x10404, {1|F|D, {0x1042c}}},
+ {0x10405, {1|F|D, {0x1042d}}},
+ {0x10406, {1|F|D, {0x1042e}}},
+ {0x10407, {1|F|D, {0x1042f}}},
+ {0x10408, {1|F|D, {0x10430}}},
+ {0x10409, {1|F|D, {0x10431}}},
+ {0x1040a, {1|F|D, {0x10432}}},
+ {0x1040b, {1|F|D, {0x10433}}},
+ {0x1040c, {1|F|D, {0x10434}}},
+ {0x1040d, {1|F|D, {0x10435}}},
+ {0x1040e, {1|F|D, {0x10436}}},
+ {0x1040f, {1|F|D, {0x10437}}},
+ {0x10410, {1|F|D, {0x10438}}},
+ {0x10411, {1|F|D, {0x10439}}},
+ {0x10412, {1|F|D, {0x1043a}}},
+ {0x10413, {1|F|D, {0x1043b}}},
+ {0x10414, {1|F|D, {0x1043c}}},
+ {0x10415, {1|F|D, {0x1043d}}},
+ {0x10416, {1|F|D, {0x1043e}}},
+ {0x10417, {1|F|D, {0x1043f}}},
+ {0x10418, {1|F|D, {0x10440}}},
+ {0x10419, {1|F|D, {0x10441}}},
+ {0x1041a, {1|F|D, {0x10442}}},
+ {0x1041b, {1|F|D, {0x10443}}},
+ {0x1041c, {1|F|D, {0x10444}}},
+ {0x1041d, {1|F|D, {0x10445}}},
+ {0x1041e, {1|F|D, {0x10446}}},
+ {0x1041f, {1|F|D, {0x10447}}},
+ {0x10420, {1|F|D, {0x10448}}},
+ {0x10421, {1|F|D, {0x10449}}},
+ {0x10422, {1|F|D, {0x1044a}}},
+ {0x10423, {1|F|D, {0x1044b}}},
+ {0x10424, {1|F|D, {0x1044c}}},
+ {0x10425, {1|F|D, {0x1044d}}},
+ {0x10426, {1|F|D, {0x1044e}}},
+ {0x10427, {1|F|D, {0x1044f}}},
+ {0x104b0, {1|F|D, {0x104d8}}},
+ {0x104b1, {1|F|D, {0x104d9}}},
+ {0x104b2, {1|F|D, {0x104da}}},
+ {0x104b3, {1|F|D, {0x104db}}},
+ {0x104b4, {1|F|D, {0x104dc}}},
+ {0x104b5, {1|F|D, {0x104dd}}},
+ {0x104b6, {1|F|D, {0x104de}}},
+ {0x104b7, {1|F|D, {0x104df}}},
+ {0x104b8, {1|F|D, {0x104e0}}},
+ {0x104b9, {1|F|D, {0x104e1}}},
+ {0x104ba, {1|F|D, {0x104e2}}},
+ {0x104bb, {1|F|D, {0x104e3}}},
+ {0x104bc, {1|F|D, {0x104e4}}},
+ {0x104bd, {1|F|D, {0x104e5}}},
+ {0x104be, {1|F|D, {0x104e6}}},
+ {0x104bf, {1|F|D, {0x104e7}}},
+ {0x104c0, {1|F|D, {0x104e8}}},
+ {0x104c1, {1|F|D, {0x104e9}}},
+ {0x104c2, {1|F|D, {0x104ea}}},
+ {0x104c3, {1|F|D, {0x104eb}}},
+ {0x104c4, {1|F|D, {0x104ec}}},
+ {0x104c5, {1|F|D, {0x104ed}}},
+ {0x104c6, {1|F|D, {0x104ee}}},
+ {0x104c7, {1|F|D, {0x104ef}}},
+ {0x104c8, {1|F|D, {0x104f0}}},
+ {0x104c9, {1|F|D, {0x104f1}}},
+ {0x104ca, {1|F|D, {0x104f2}}},
+ {0x104cb, {1|F|D, {0x104f3}}},
+ {0x104cc, {1|F|D, {0x104f4}}},
+ {0x104cd, {1|F|D, {0x104f5}}},
+ {0x104ce, {1|F|D, {0x104f6}}},
+ {0x104cf, {1|F|D, {0x104f7}}},
+ {0x104d0, {1|F|D, {0x104f8}}},
+ {0x104d1, {1|F|D, {0x104f9}}},
+ {0x104d2, {1|F|D, {0x104fa}}},
+ {0x104d3, {1|F|D, {0x104fb}}},
+ {0x10c80, {1|F|D, {0x10cc0}}},
+ {0x10c81, {1|F|D, {0x10cc1}}},
+ {0x10c82, {1|F|D, {0x10cc2}}},
+ {0x10c83, {1|F|D, {0x10cc3}}},
+ {0x10c84, {1|F|D, {0x10cc4}}},
+ {0x10c85, {1|F|D, {0x10cc5}}},
+ {0x10c86, {1|F|D, {0x10cc6}}},
+ {0x10c87, {1|F|D, {0x10cc7}}},
+ {0x10c88, {1|F|D, {0x10cc8}}},
+ {0x10c89, {1|F|D, {0x10cc9}}},
+ {0x10c8a, {1|F|D, {0x10cca}}},
+ {0x10c8b, {1|F|D, {0x10ccb}}},
+ {0x10c8c, {1|F|D, {0x10ccc}}},
+ {0x10c8d, {1|F|D, {0x10ccd}}},
+ {0x10c8e, {1|F|D, {0x10cce}}},
+ {0x10c8f, {1|F|D, {0x10ccf}}},
+ {0x10c90, {1|F|D, {0x10cd0}}},
+ {0x10c91, {1|F|D, {0x10cd1}}},
+ {0x10c92, {1|F|D, {0x10cd2}}},
+ {0x10c93, {1|F|D, {0x10cd3}}},
+ {0x10c94, {1|F|D, {0x10cd4}}},
+ {0x10c95, {1|F|D, {0x10cd5}}},
+ {0x10c96, {1|F|D, {0x10cd6}}},
+ {0x10c97, {1|F|D, {0x10cd7}}},
+ {0x10c98, {1|F|D, {0x10cd8}}},
+ {0x10c99, {1|F|D, {0x10cd9}}},
+ {0x10c9a, {1|F|D, {0x10cda}}},
+ {0x10c9b, {1|F|D, {0x10cdb}}},
+ {0x10c9c, {1|F|D, {0x10cdc}}},
+ {0x10c9d, {1|F|D, {0x10cdd}}},
+ {0x10c9e, {1|F|D, {0x10cde}}},
+ {0x10c9f, {1|F|D, {0x10cdf}}},
+ {0x10ca0, {1|F|D, {0x10ce0}}},
+ {0x10ca1, {1|F|D, {0x10ce1}}},
+ {0x10ca2, {1|F|D, {0x10ce2}}},
+ {0x10ca3, {1|F|D, {0x10ce3}}},
+ {0x10ca4, {1|F|D, {0x10ce4}}},
+ {0x10ca5, {1|F|D, {0x10ce5}}},
+ {0x10ca6, {1|F|D, {0x10ce6}}},
+ {0x10ca7, {1|F|D, {0x10ce7}}},
+ {0x10ca8, {1|F|D, {0x10ce8}}},
+ {0x10ca9, {1|F|D, {0x10ce9}}},
+ {0x10caa, {1|F|D, {0x10cea}}},
+ {0x10cab, {1|F|D, {0x10ceb}}},
+ {0x10cac, {1|F|D, {0x10cec}}},
+ {0x10cad, {1|F|D, {0x10ced}}},
+ {0x10cae, {1|F|D, {0x10cee}}},
+ {0x10caf, {1|F|D, {0x10cef}}},
+ {0x10cb0, {1|F|D, {0x10cf0}}},
+ {0x10cb1, {1|F|D, {0x10cf1}}},
+ {0x10cb2, {1|F|D, {0x10cf2}}},
+ {0x118a0, {1|F|D, {0x118c0}}},
+ {0x118a1, {1|F|D, {0x118c1}}},
+ {0x118a2, {1|F|D, {0x118c2}}},
+ {0x118a3, {1|F|D, {0x118c3}}},
+ {0x118a4, {1|F|D, {0x118c4}}},
+ {0x118a5, {1|F|D, {0x118c5}}},
+ {0x118a6, {1|F|D, {0x118c6}}},
+ {0x118a7, {1|F|D, {0x118c7}}},
+ {0x118a8, {1|F|D, {0x118c8}}},
+ {0x118a9, {1|F|D, {0x118c9}}},
+ {0x118aa, {1|F|D, {0x118ca}}},
+ {0x118ab, {1|F|D, {0x118cb}}},
+ {0x118ac, {1|F|D, {0x118cc}}},
+ {0x118ad, {1|F|D, {0x118cd}}},
+ {0x118ae, {1|F|D, {0x118ce}}},
+ {0x118af, {1|F|D, {0x118cf}}},
+ {0x118b0, {1|F|D, {0x118d0}}},
+ {0x118b1, {1|F|D, {0x118d1}}},
+ {0x118b2, {1|F|D, {0x118d2}}},
+ {0x118b3, {1|F|D, {0x118d3}}},
+ {0x118b4, {1|F|D, {0x118d4}}},
+ {0x118b5, {1|F|D, {0x118d5}}},
+ {0x118b6, {1|F|D, {0x118d6}}},
+ {0x118b7, {1|F|D, {0x118d7}}},
+ {0x118b8, {1|F|D, {0x118d8}}},
+ {0x118b9, {1|F|D, {0x118d9}}},
+ {0x118ba, {1|F|D, {0x118da}}},
+ {0x118bb, {1|F|D, {0x118db}}},
+ {0x118bc, {1|F|D, {0x118dc}}},
+ {0x118bd, {1|F|D, {0x118dd}}},
+ {0x118be, {1|F|D, {0x118de}}},
+ {0x118bf, {1|F|D, {0x118df}}},
+ {0x16e40, {1|F|D, {0x16e60}}},
+ {0x16e41, {1|F|D, {0x16e61}}},
+ {0x16e42, {1|F|D, {0x16e62}}},
+ {0x16e43, {1|F|D, {0x16e63}}},
+ {0x16e44, {1|F|D, {0x16e64}}},
+ {0x16e45, {1|F|D, {0x16e65}}},
+ {0x16e46, {1|F|D, {0x16e66}}},
+ {0x16e47, {1|F|D, {0x16e67}}},
+ {0x16e48, {1|F|D, {0x16e68}}},
+ {0x16e49, {1|F|D, {0x16e69}}},
+ {0x16e4a, {1|F|D, {0x16e6a}}},
+ {0x16e4b, {1|F|D, {0x16e6b}}},
+ {0x16e4c, {1|F|D, {0x16e6c}}},
+ {0x16e4d, {1|F|D, {0x16e6d}}},
+ {0x16e4e, {1|F|D, {0x16e6e}}},
+ {0x16e4f, {1|F|D, {0x16e6f}}},
+ {0x16e50, {1|F|D, {0x16e70}}},
+ {0x16e51, {1|F|D, {0x16e71}}},
+ {0x16e52, {1|F|D, {0x16e72}}},
+ {0x16e53, {1|F|D, {0x16e73}}},
+ {0x16e54, {1|F|D, {0x16e74}}},
+ {0x16e55, {1|F|D, {0x16e75}}},
+ {0x16e56, {1|F|D, {0x16e76}}},
+ {0x16e57, {1|F|D, {0x16e77}}},
+ {0x16e58, {1|F|D, {0x16e78}}},
+ {0x16e59, {1|F|D, {0x16e79}}},
+ {0x16e5a, {1|F|D, {0x16e7a}}},
+ {0x16e5b, {1|F|D, {0x16e7b}}},
+ {0x16e5c, {1|F|D, {0x16e7c}}},
+ {0x16e5d, {1|F|D, {0x16e7d}}},
+ {0x16e5e, {1|F|D, {0x16e7e}}},
+ {0x16e5f, {1|F|D, {0x16e7f}}},
+ {0x1e900, {1|F|D, {0x1e922}}},
+ {0x1e901, {1|F|D, {0x1e923}}},
+ {0x1e902, {1|F|D, {0x1e924}}},
+ {0x1e903, {1|F|D, {0x1e925}}},
+ {0x1e904, {1|F|D, {0x1e926}}},
+ {0x1e905, {1|F|D, {0x1e927}}},
+ {0x1e906, {1|F|D, {0x1e928}}},
+ {0x1e907, {1|F|D, {0x1e929}}},
+ {0x1e908, {1|F|D, {0x1e92a}}},
+ {0x1e909, {1|F|D, {0x1e92b}}},
+ {0x1e90a, {1|F|D, {0x1e92c}}},
+ {0x1e90b, {1|F|D, {0x1e92d}}},
+ {0x1e90c, {1|F|D, {0x1e92e}}},
+ {0x1e90d, {1|F|D, {0x1e92f}}},
+ {0x1e90e, {1|F|D, {0x1e930}}},
+ {0x1e90f, {1|F|D, {0x1e931}}},
+ {0x1e910, {1|F|D, {0x1e932}}},
+ {0x1e911, {1|F|D, {0x1e933}}},
+ {0x1e912, {1|F|D, {0x1e934}}},
+ {0x1e913, {1|F|D, {0x1e935}}},
+ {0x1e914, {1|F|D, {0x1e936}}},
+ {0x1e915, {1|F|D, {0x1e937}}},
+ {0x1e916, {1|F|D, {0x1e938}}},
+ {0x1e917, {1|F|D, {0x1e939}}},
+ {0x1e918, {1|F|D, {0x1e93a}}},
+ {0x1e919, {1|F|D, {0x1e93b}}},
+ {0x1e91a, {1|F|D, {0x1e93c}}},
+ {0x1e91b, {1|F|D, {0x1e93d}}},
+ {0x1e91c, {1|F|D, {0x1e93e}}},
+ {0x1e91d, {1|F|D, {0x1e93f}}},
+ {0x1e91e, {1|F|D, {0x1e940}}},
+ {0x1e91f, {1|F|D, {0x1e941}}},
+ {0x1e920, {1|F|D, {0x1e942}}},
+ {0x1e921, {1|F|D, {0x1e943}}},
+#define CaseFold_Locale (*(CaseFold_11_Type (*)[2])(CaseFold_11_Table+1485))
+ {0x0049, {1|F|D, {0x0069}}},
+ {0x0130, {2|F|D, {0x0069, 0x0307}}},
+};
+
+/* ANSI-C code produced by gperf version 3.1 */
+/* Command-line: gperf -7 -k1,2,3 -F,-1 -c -j1 -i1 -t -T -E -C -H onigenc_unicode_CaseFold_11_hash -N onigenc_unicode_CaseFold_11_lookup -n */
+
+/* maximum key range = 3500, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+/*ARGSUSED*/
+static unsigned int
+onigenc_unicode_CaseFold_11_hash(const OnigCodePoint code)
+{
+ static const unsigned short asso_values[] =
+ {
+ 5, 273, 4, 8, 3, 1, 86, 9, 2, 289,
+ 290, 3, 3510, 3510, 3510, 3510, 3510, 3510, 3510, 3510,
+ 3510, 3510, 3510, 3510, 3510, 50, 3510, 3510, 3510, 3510,
+ 3510, 3510, 3510, 225, 3510, 3510, 3510, 3510, 3510, 28,
+ 3510, 3510, 3510, 3510, 3510, 3510, 3510, 3510, 3510, 394,
+ 3510, 3510, 3510, 3510, 3510, 3510, 3510, 47, 3510, 3510,
+ 255, 40, 286, 1, 3510, 3510, 599, 8, 3510, 3510,
+ 3510, 3510, 3510, 282, 3510, 3510, 267, 667, 473, 39,
+ 2019, 189, 47, 175, 2001, 107, 1626, 6, 12, 25,
+ 1961, 678, 1128, 526, 1945, 148, 1923, 371, 1720, 134,
+ 1857, 80, 1375, 66, 1705, 300, 1635, 445, 1611, 472,
+ 1795, 216, 1303, 499, 1552, 270, 1511, 243, 121, 619,
+ 1284, 540, 875, 592, 1484, 567, 412, 703, 1692, 387,
+ 1782, 781, 1767, 664, 1718, 648, 1316, 608, 1647, 715,
+ 1592, 771, 1544, 1029, 1563, 887, 1296, 861, 1194, 978,
+ 95, 899, 1257, 835, 1335, 765, 1529, 984, 862, 938,
+ 1460, 759, 329, 1079, 1159, 940, 234, 1101, 1204, 990,
+ 949, 1493, 92, 1438, 77, 1391, 7, 1073, 44, 1377,
+ 2, 1435, 4, 1321, 428, 1274, 332, 1206, 11, 1426,
+ 46, 478, 200, 1502, 31, 1400, 153, 1663, 352, 1820,
+ 229, 1733, 265, 1405, 315, 1879, 198
+ };
+ return asso_values[bits_of(code, 2)+79] + asso_values[bits_of(code, 1)] + asso_values[bits_of(code, 0)];
+}
+
+static const CodePointList3 *
+onigenc_unicode_CaseFold_11_lookup(const OnigCodePoint code)
+{
+ enum
+ {
+ MIN_CODE_VALUE = 0x41,
+ MAX_CODE_VALUE = 0x1e921,
+ TOTAL_KEYWORDS = 1487,
+ MIN_WORD_LENGTH = 3,
+ MAX_WORD_LENGTH = 3,
+ MIN_HASH_VALUE = 10,
+ MAX_HASH_VALUE = 3509
+ };
+
+ static const short wordlist[] =
+ {
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ /*0x1fe7*/ 848,
+ /*0x10408*/ 1268,
+ /*0x1f88*/ 775,
+ /*0x0408*/ 305,
+ /*0x0208*/ 194,
+ /*0x0108*/ 61,
+ /*0xab88*/ 1166,
+ /*0x10409*/ 1269,
+ /*0x1f89*/ 776,
+ /*0x0409*/ 306,
+ /*0x0388*/ 235,
+ /*0x2c67*/ 962,
+ /*0xab89*/ 1167,
+ /*0x2c08*/ 919,
+ -1,
+ /*0x0189*/ 126,
+ /*0x0389*/ 236,
+ -1,
+ /*0x2c6d*/ 965,
+ /*0x2c09*/ 920,
+ /*0x1040a*/ 1270,
+ /*0x1f8a*/ 777,
+ /*0x040a*/ 307,
+ /*0x020a*/ 195,
+ /*0x010a*/ 62,
+ /*0xab8a*/ 1168,
+ /*0x2c88*/ 977,
+ /*0x1ff3*/ 855,
+ /*0x018a*/ 127,
+ /*0x038a*/ 237,
+ -1,
+ /*0x2ced*/ 1024,
+ /*0x2c0a*/ 921,
+ -1,
+ /*0x10400*/ 1260,
+ /*0x1f80*/ 767,
+ /*0x0400*/ 297,
+ /*0x0200*/ 190,
+ /*0x0100*/ 57,
+ /*0xab80*/ 1158,
+ /*0x1fe3*/ 845,
+ /*0x1e88*/ 653,
+ /*0x10403*/ 1263,
+ /*0x1f83*/ 770,
+ /*0x0403*/ 300,
+ /*0x2c8a*/ 978,
+ /*0x2c00*/ 911,
+ /*0xab83*/ 1161,
+ /*0x1c88*/ 538,
+ /*0x10c88*/ 1344,
+ /*0x2183*/ 884,
+ /*0x2c63*/ 960,
+ /*0x1e908*/ 1459,
+ /*0x2c6f*/ 967,
+ /*0x2c03*/ 914,
+ /*0x10c89*/ 1345,
+ -1, -1,
+ /*0x1e909*/ 1460,
+ /*0x2c80*/ 973,
+ /*0x1e8a*/ 654,
+ /*0x10418*/ 1284,
+ /*0x1f98*/ 791,
+ /*0x0418*/ 321,
+ /*0x0218*/ 202,
+ /*0x0118*/ 69,
+ /*0xab98*/ 1182,
+ -1,
+ /*0x10c8a*/ 1346,
+ /*0x0198*/ 137,
+ /*0x0398*/ 249,
+ /*0x1e90a*/ 1461,
+ /*0xa780*/ 1105,
+ /*0x2c18*/ 935,
+ /*0x1e80*/ 649,
+ /*0x10416*/ 1282,
+ /*0x1f96*/ 789,
+ /*0x0416*/ 319,
+ /*0x0216*/ 201,
+ /*0x0116*/ 68,
+ /*0xab96*/ 1180,
+ /*0x1c80*/ 530,
+ /*0x10c80*/ 1336,
+ /*0x0196*/ 135,
+ /*0x0396*/ 247,
+ /*0x1e900*/ 1451,
+ /*0x2c98*/ 985,
+ /*0x2c16*/ 933,
+ -1,
+ /*0x1c83*/ 533,
+ /*0x10c83*/ 1339,
+ /*0x1fc7*/ 830,
+ -1,
+ /*0x1e903*/ 1454,
+ /*0x0147*/ 91,
+ /*0x0047*/ 6,
+ -1, -1,
+ /*0x01c7*/ 159,
+ /*0xa798*/ 1114,
+ /*0x2c96*/ 984,
+ /*0x1e98*/ 662,
+ /*0x10406*/ 1266,
+ /*0x1f86*/ 773,
+ /*0x0406*/ 303,
+ /*0x0206*/ 193,
+ /*0x0106*/ 60,
+ /*0xab86*/ 1164,
+ /*0x1c98*/ 547,
+ /*0x10c98*/ 1360,
+ /*0x0186*/ 124,
+ /*0x0386*/ 234,
+ /*0x1e918*/ 1475,
+ /*0xa796*/ 1113,
+ /*0x2c06*/ 917,
+ /*0x1e96*/ 660,
+ /*0x10427*/ 1299,
+ /*0x1fa7*/ 806,
+ /*0x0427*/ 336,
+ -1, -1,
+ /*0xaba7*/ 1197,
+ /*0x1c96*/ 545,
+ /*0x10c96*/ 1358,
+ /*0x01a7*/ 145,
+ /*0x03a7*/ 263,
+ /*0x1e916*/ 1473,
+ /*0x2c86*/ 976,
+ /*0x2c27*/ 950,
+ /*0x10414*/ 1280,
+ /*0x1f94*/ 787,
+ /*0x0414*/ 317,
+ /*0x0214*/ 200,
+ /*0x0114*/ 67,
+ /*0xab94*/ 1178,
+ -1, -1,
+ /*0x0194*/ 134,
+ /*0x0394*/ 245,
+ -1,
+ /*0xa786*/ 1108,
+ /*0x2c14*/ 931,
+ /*0x1e86*/ 652,
+ /*0x10410*/ 1276,
+ /*0x1f90*/ 783,
+ /*0x0410*/ 313,
+ /*0x0210*/ 198,
+ /*0x0110*/ 65,
+ /*0xab90*/ 1174,
+ /*0x1c86*/ 536,
+ /*0x10c86*/ 1342,
+ /*0x0190*/ 131,
+ /*0x0390*/ 241,
+ /*0x1e906*/ 1457,
+ /*0x2c94*/ 983,
+ /*0x2c10*/ 927,
+ -1,
+ /*0x03f5*/ 290,
+ /*0xfb00*/ 1222,
+ -1,
+ /*0x2c75*/ 970,
+ -1, -1,
+ /*0x1ca7*/ 562,
+ /*0x10ca7*/ 1375,
+ -1,
+ /*0xfb03*/ 1225,
+ -1,
+ /*0x2c90*/ 981,
+ /*0x1e94*/ 659,
+ /*0x10404*/ 1264,
+ /*0x1f84*/ 771,
+ /*0x0404*/ 301,
+ /*0x0204*/ 192,
+ /*0x0104*/ 59,
+ /*0xab84*/ 1162,
+ /*0x1c94*/ 543,
+ /*0x10c94*/ 1356,
+ /*0x0184*/ 123,
+ -1,
+ /*0x1e914*/ 1471,
+ /*0xa790*/ 1111,
+ /*0x2c04*/ 915,
+ /*0x1e90*/ 657,
+ /*0x10402*/ 1262,
+ /*0x1f82*/ 769,
+ /*0x0402*/ 299,
+ /*0x0202*/ 191,
+ /*0x0102*/ 58,
+ /*0xab82*/ 1160,
+ /*0x1c90*/ 539,
+ /*0x10c90*/ 1352,
+ /*0x0182*/ 122,
+ -1,
+ /*0x1e910*/ 1467,
+ /*0x2c84*/ 975,
+ /*0x2c02*/ 913,
+ /*0x017f*/ 120,
+ -1,
+ /*0xfb16*/ 1232,
+ -1, -1,
+ /*0x03ff*/ 296,
+ /*0x01f1*/ 181,
+ /*0x03f1*/ 288,
+ /*0x2c7f*/ 972,
+ -1, -1,
+ /*0xa784*/ 1107,
+ /*0x2c82*/ 974,
+ /*0x1e84*/ 651,
+ /*0x10420*/ 1292,
+ /*0x1fa0*/ 799,
+ /*0x0420*/ 329,
+ /*0x0220*/ 206,
+ /*0x0120*/ 73,
+ /*0xaba0*/ 1190,
+ /*0x1c84*/ 534,
+ /*0x10c84*/ 1340,
+ /*0x01a0*/ 141,
+ /*0x03a0*/ 257,
+ /*0x1e904*/ 1455,
+ /*0xa782*/ 1106,
+ /*0x2c20*/ 943,
+ /*0x1e82*/ 650,
+ /*0x1ff9*/ 860,
+ /*0xfb06*/ 1228,
+ -1,
+ /*0x0179*/ 117,
+ -1,
+ /*0x1fd7*/ 839,
+ /*0x1c82*/ 532,
+ /*0x10c82*/ 1338,
+ /*0x03f9*/ 292,
+ /*0x0057*/ 21,
+ /*0x1e902*/ 1453,
+ /*0x2ca0*/ 989,
+ /*0x01d7*/ 168,
+ /*0x10426*/ 1298,
+ /*0x1fa6*/ 805,
+ /*0x0426*/ 335,
+ /*0x0226*/ 209,
+ /*0x0126*/ 76,
+ /*0xaba6*/ 1196,
+ -1, -1,
+ /*0x01a6*/ 144,
+ /*0x03a6*/ 262,
+ -1,
+ /*0xa7a0*/ 1118,
+ /*0x2c26*/ 949,
+ /*0x1ea0*/ 667,
+ /*0x13f9*/ 525,
+ /*0xfb14*/ 1230,
+ -1, -1,
+ /*0x1e08*/ 589,
+ -1,
+ /*0x1ca0*/ 555,
+ /*0x10ca0*/ 1368,
+ -1,
+ /*0x1ffb*/ 862,
+ /*0x1e920*/ 1483,
+ /*0x2ca6*/ 992,
+ /*0x017b*/ 118,
+ /*0x10424*/ 1296,
+ /*0x1fa4*/ 803,
+ /*0x0424*/ 333,
+ /*0x0224*/ 208,
+ /*0x0124*/ 75,
+ /*0xaba4*/ 1194,
+ -1, -1,
+ /*0x01a4*/ 143,
+ /*0x03a4*/ 260,
+ /*0x1e0a*/ 590,
+ /*0xa7a6*/ 1121,
+ /*0x2c24*/ 947,
+ /*0x1ea6*/ 670,
+ /*0x037f*/ 233,
+ -1, -1, -1, -1, -1,
+ /*0x1ca6*/ 561,
+ /*0x10ca6*/ 1374,
+ /*0x1f08*/ 715,
+ /*0x13fb*/ 527,
+ /*0x1e00*/ 585,
+ /*0x2ca4*/ 991,
+ /*0x0508*/ 425,
+ /*0x1f6d*/ 764,
+ /*0x1f09*/ 716,
+ /*0xfb04*/ 1226,
+ /*0x1041a*/ 1286,
+ /*0x1f9a*/ 793,
+ /*0x041a*/ 323,
+ /*0x021a*/ 203,
+ /*0x011a*/ 70,
+ /*0xab9a*/ 1184,
+ -1, -1,
+ /*0xa7a4*/ 1120,
+ /*0x039a*/ 251,
+ /*0x1ea4*/ 669,
+ /*0x1f0a*/ 717,
+ /*0x2c1a*/ 937,
+ /*0xfb02*/ 1224,
+ /*0x048a*/ 362,
+ /*0x050a*/ 426,
+ -1,
+ /*0x1ca4*/ 559,
+ /*0x10ca4*/ 1372,
+ /*0x017d*/ 119,
+ /*0x10c7*/ 522,
+ /*0x1e18*/ 597,
+ -1, -1,
+ /*0x03fd*/ 294,
+ /*0x2c9a*/ 986,
+ -1, -1,
+ /*0x0480*/ 361,
+ /*0x0500*/ 421,
+ /*0x1fd3*/ 837,
+ -1,
+ /*0x1f6f*/ 766,
+ /*0x1feb*/ 852,
+ /*0x0053*/ 17,
+ /*0x1e16*/ 596,
+ -1,
+ /*0x01d3*/ 166,
+ /*0xa79a*/ 1115,
+ -1,
+ /*0x1e9a*/ 664,
+ -1, -1,
+ /*0x13fd*/ 529,
+ /*0x2c6b*/ 964,
+ -1,
+ /*0x10a7*/ 491,
+ /*0x1c9a*/ 549,
+ /*0x10c9a*/ 1362,
+ -1,
+ /*0x00df*/ 56,
+ /*0x1e91a*/ 1477,
+ /*0x1f18*/ 723,
+ /*0x1ff7*/ 858,
+ -1,
+ /*0x0498*/ 369,
+ /*0x0518*/ 433,
+ /*0x2ceb*/ 1023,
+ -1, -1,
+ /*0x01f7*/ 185,
+ /*0x03f7*/ 291,
+ /*0x1e06*/ 588,
+ /*0x1f5f*/ 758,
+ -1,
+ /*0x00dd*/ 54,
+ -1, -1,
+ /*0x00c7*/ 33,
+ /*0x0496*/ 368,
+ /*0x0516*/ 432,
+ /*0x10412*/ 1278,
+ /*0x1f92*/ 785,
+ /*0x0412*/ 315,
+ /*0x0212*/ 199,
+ /*0x0112*/ 66,
+ /*0xab92*/ 1176,
+ /*0x24c7*/ 902,
+ /*0x1f5d*/ 757,
+ -1,
+ /*0x0392*/ 243,
+ -1,
+ /*0x104c7*/ 1323,
+ /*0x2c12*/ 929,
+ /*0x04c7*/ 393,
+ /*0x0547*/ 467,
+ -1, -1,
+ /*0x1fb2*/ 815,
+ /*0x1e14*/ 595,
+ /*0x0232*/ 215,
+ /*0x0132*/ 81,
+ /*0xabb2*/ 1208,
+ -1, -1,
+ /*0x01b2*/ 151,
+ /*0x2c92*/ 982,
+ /*0x0506*/ 424,
+ -1, -1, -1, -1, -1,
+ /*0x1e10*/ 593,
+ -1, -1, -1, -1, -1,
+ /*0xa792*/ 1112,
+ -1,
+ /*0x1e92*/ 658,
+ /*0x2cb2*/ 998,
+ /*0x1faf*/ 814,
+ /*0x042f*/ 344,
+ -1, -1,
+ /*0xabaf*/ 1205,
+ /*0x1c92*/ 541,
+ /*0x10c92*/ 1354,
+ /*0x01af*/ 149,
+ -1,
+ /*0x1e912*/ 1469,
+ /*0x0494*/ 367,
+ /*0x0514*/ 431,
+ /*0xa7b2*/ 1130,
+ -1,
+ /*0x1eb2*/ 676,
+ -1,
+ /*0x1fe9*/ 850,
+ /*0x1e04*/ 587,
+ -1, -1, -1,
+ /*0x1cb2*/ 573,
+ /*0x10cb2*/ 1386,
+ -1,
+ /*0x0490*/ 365,
+ /*0x0510*/ 429,
+ -1,
+ /*0x2c69*/ 963,
+ /*0x10a0*/ 484,
+ -1, -1,
+ /*0x1e02*/ 586,
+ /*0x1041c*/ 1288,
+ /*0x1f9c*/ 795,
+ /*0x041c*/ 325,
+ /*0x021c*/ 204,
+ /*0x011c*/ 71,
+ /*0xab9c*/ 1186,
+ -1, -1,
+ /*0x019c*/ 138,
+ /*0x039c*/ 253,
+ -1, -1,
+ /*0x2c1c*/ 939,
+ -1,
+ /*0x1caf*/ 570,
+ /*0x10caf*/ 1383,
+ -1, -1, -1, -1,
+ /*0x0504*/ 423,
+ -1, -1,
+ /*0x10a6*/ 490,
+ -1,
+ /*0x2c9c*/ 987,
+ /*0x1e20*/ 601,
+ /*0x1041e*/ 1290,
+ /*0x1f9e*/ 797,
+ /*0x041e*/ 327,
+ /*0x021e*/ 205,
+ /*0x011e*/ 72,
+ /*0xab9e*/ 1188,
+ -1,
+ /*0x0502*/ 422,
+ /*0x0470*/ 353,
+ /*0x039e*/ 255,
+ /*0x0170*/ 112,
+ /*0xa79c*/ 1116,
+ /*0x2c1e*/ 941,
+ -1,
+ /*0x01f0*/ 180,
+ /*0x03f0*/ 287,
+ -1, -1,
+ /*0x2c70*/ 968,
+ -1,
+ /*0x1c9c*/ 551,
+ /*0x10c9c*/ 1364,
+ -1,
+ /*0x10a4*/ 488,
+ /*0x1e91c*/ 1479,
+ /*0x2c9e*/ 988,
+ /*0x1e26*/ 604,
+ /*0x10422*/ 1294,
+ /*0x1fa2*/ 801,
+ /*0x0422*/ 331,
+ /*0x0222*/ 207,
+ /*0x0122*/ 74,
+ /*0xaba2*/ 1192,
+ /*0x04a0*/ 373,
+ /*0x0520*/ 437,
+ /*0x01a2*/ 142,
+ -1, -1,
+ /*0xa79e*/ 1117,
+ /*0x2c22*/ 945,
+ /*0x1e9e*/ 666,
+ /*0x118a7*/ 1394,
+ -1, -1, -1, -1,
+ /*0x1ef0*/ 707,
+ /*0x1c9e*/ 553,
+ /*0x10c9e*/ 1366,
+ -1, -1,
+ /*0x1e91e*/ 1481,
+ /*0x2ca2*/ 990,
+ /*0x1e24*/ 603,
+ /*0x1040e*/ 1274,
+ /*0x1f8e*/ 781,
+ /*0x040e*/ 311,
+ /*0x020e*/ 197,
+ /*0x010e*/ 64,
+ /*0xab8e*/ 1172,
+ /*0x04a6*/ 376,
+ /*0x0526*/ 440,
+ /*0x018e*/ 129,
+ /*0x038e*/ 239,
+ /*0xff27*/ 1240,
+ /*0xa7a2*/ 1119,
+ /*0x2c0e*/ 925,
+ /*0x1ea2*/ 668,
+ -1,
+ /*0x1faa*/ 809,
+ /*0x042a*/ 339,
+ /*0x022a*/ 211,
+ /*0x012a*/ 78,
+ /*0xabaa*/ 1200,
+ /*0x1ca2*/ 557,
+ /*0x10ca2*/ 1370,
+ -1,
+ /*0x03aa*/ 266,
+ -1,
+ /*0x2c8e*/ 980,
+ /*0x2c2a*/ 953,
+ -1, -1,
+ /*0x1e1a*/ 598,
+ -1, -1, -1,
+ /*0x04a4*/ 375,
+ /*0x0524*/ 439,
+ -1, -1, -1,
+ /*0x0370*/ 230,
+ /*0x2caa*/ 994,
+ /*0x1e8e*/ 656,
+ -1,
+ /*0x1fae*/ 813,
+ /*0x042e*/ 343,
+ /*0x022e*/ 213,
+ /*0x012e*/ 80,
+ /*0xabae*/ 1204,
+ -1,
+ /*0x10c8e*/ 1350,
+ /*0x01ae*/ 148,
+ -1,
+ /*0x1e90e*/ 1465,
+ /*0xa7aa*/ 1123,
+ /*0x2c2e*/ 957,
+ /*0x1eaa*/ 672,
+ -1, -1, -1, -1, -1,
+ /*0x1f1a*/ 725,
+ /*0x1caa*/ 565,
+ /*0x10caa*/ 1378,
+ /*0x049a*/ 370,
+ /*0x051a*/ 434,
+ -1,
+ /*0x2cae*/ 996,
+ /*0x1fac*/ 811,
+ /*0x042c*/ 341,
+ /*0x022c*/ 212,
+ /*0x012c*/ 79,
+ /*0xabac*/ 1202,
+ -1, -1,
+ /*0x01ac*/ 147,
+ /*0x2165*/ 873,
+ /*0x00d3*/ 45,
+ /*0x2167*/ 875,
+ /*0x2c2c*/ 955,
+ /*0xa7ae*/ 1127,
+ /*0x2161*/ 869,
+ /*0x1eae*/ 674,
+ /*0x118a0*/ 1387,
+ /*0x1fba*/ 822,
+ /*0x216d*/ 881,
+ /*0x023a*/ 216,
+ /*0x10b2*/ 502,
+ /*0xabba*/ 1216,
+ /*0x1cae*/ 569,
+ /*0x10cae*/ 1382,
+ /*0x104d3*/ 1335,
+ /*0x2cac*/ 995,
+ /*0x1f6b*/ 762,
+ /*0x0553*/ 479,
+ /*0x1fa8*/ 807,
+ /*0x0428*/ 337,
+ /*0x0228*/ 210,
+ /*0x0128*/ 77,
+ /*0xaba8*/ 1198,
+ -1,
+ /*0x1e12*/ 594,
+ -1,
+ /*0x03a8*/ 264,
+ -1,
+ /*0xa7ac*/ 1125,
+ /*0x2c28*/ 951,
+ /*0x1eac*/ 673,
+ /*0x2cba*/ 1002,
+ -1,
+ /*0x118a6*/ 1393,
+ -1,
+ /*0x10af*/ 499,
+ -1,
+ /*0x1cac*/ 567,
+ /*0x10cac*/ 1380,
+ -1,
+ /*0x1e32*/ 610,
+ /*0x2163*/ 871,
+ /*0x2ca8*/ 993,
+ /*0x216f*/ 883,
+ /*0xa7ba*/ 1135,
+ -1,
+ /*0x1eba*/ 680,
+ /*0x1fb8*/ 820,
+ -1, -1, -1,
+ /*0xabb8*/ 1214,
+ -1,
+ /*0x1cba*/ 581,
+ /*0x01b8*/ 155,
+ /*0xa7a8*/ 1122,
+ /*0xff26*/ 1239,
+ /*0x1ea8*/ 671,
+ /*0x0492*/ 366,
+ /*0x0512*/ 430,
+ /*0x118a4*/ 1391,
+ -1, -1,
+ /*0x1fb6*/ 818,
+ /*0x1ca8*/ 563,
+ /*0x10ca8*/ 1376,
+ /*0x0136*/ 83,
+ /*0xabb6*/ 1212,
+ -1, -1,
+ /*0xa688*/ 1053,
+ /*0x2cb8*/ 1001,
+ /*0x104b2*/ 1302,
+ -1,
+ /*0x04b2*/ 382,
+ /*0x0532*/ 446,
+ /*0x1040c*/ 1272,
+ /*0x1f8c*/ 779,
+ /*0x040c*/ 309,
+ /*0x020c*/ 196,
+ /*0x010c*/ 63,
+ /*0xab8c*/ 1170,
+ -1,
+ /*0xff24*/ 1237,
+ /*0xa7b8*/ 1134,
+ /*0x038c*/ 238,
+ /*0x1eb8*/ 679,
+ /*0x2cb6*/ 1000,
+ /*0x2c0c*/ 923,
+ /*0xa68a*/ 1054,
+ -1, -1, -1,
+ /*0x1cb8*/ 579,
+ -1, -1,
+ /*0x1f2f*/ 736,
+ -1,
+ /*0x1e1c*/ 599,
+ /*0xa779*/ 1101,
+ /*0xa7b6*/ 1133,
+ /*0x2c8c*/ 979,
+ /*0x1eb6*/ 678,
+ /*0xa680*/ 1049,
+ /*0x0230*/ 214,
+ /*0x0130*/ 1486,
+ /*0xabb0*/ 1206,
+ -1, -1,
+ /*0x1cb6*/ 577,
+ /*0x03b0*/ 268,
+ -1,
+ /*0x1f69*/ 760,
+ /*0xa726*/ 1065,
+ /*0x1fbc*/ 824,
+ -1,
+ /*0x1e8c*/ 655,
+ -1,
+ /*0xabbc*/ 1218,
+ -1, -1,
+ /*0x01bc*/ 156,
+ /*0x10a2*/ 486,
+ -1,
+ /*0x10c8c*/ 1348,
+ /*0x1e1e*/ 600,
+ /*0x2cb0*/ 997,
+ /*0x1e90c*/ 1463,
+ -1,
+ /*0x1f1c*/ 727,
+ /*0xa698*/ 1061,
+ /*0x1e70*/ 641,
+ /*0x049c*/ 371,
+ /*0x051c*/ 435,
+ -1,
+ /*0xa77b*/ 1102,
+ -1, -1,
+ /*0x2cbc*/ 1003,
+ /*0xa7b0*/ 1128,
+ /*0xa724*/ 1064,
+ /*0x1eb0*/ 675,
+ -1, -1,
+ /*0xa696*/ 1060,
+ -1, -1, -1,
+ /*0x1cb0*/ 571,
+ /*0x10cb0*/ 1384,
+ -1,
+ /*0xa7bc*/ 1136,
+ /*0x1e22*/ 602,
+ /*0x1ebc*/ 681,
+ -1, -1, -1, -1,
+ /*0x1fd2*/ 836,
+ /*0x049e*/ 372,
+ /*0x051e*/ 436,
+ /*0x0152*/ 97,
+ /*0x0052*/ 16,
+ /*0x10aa*/ 494,
+ /*0x1fcc*/ 835,
+ /*0x04f0*/ 413,
+ /*0x024c*/ 227,
+ /*0x014c*/ 94,
+ /*0x004c*/ 10,
+ -1,
+ /*0x1fbe*/ 825,
+ /*0xa686*/ 1052,
+ /*0x023e*/ 219,
+ -1,
+ /*0xabbe*/ 1220,
+ -1, -1,
+ /*0x118b2*/ 1405,
+ -1,
+ /*0x1e0e*/ 592,
+ /*0x1fb4*/ 817,
+ -1,
+ /*0x2cd2*/ 1014,
+ /*0x0134*/ 82,
+ /*0xabb4*/ 1210,
+ /*0xa77d*/ 1103,
+ /*0x04a2*/ 374,
+ /*0x0522*/ 438,
+ /*0x2ccc*/ 1011,
+ -1,
+ /*0x10ae*/ 498,
+ -1, -1,
+ /*0x1e2a*/ 606,
+ /*0x2cbe*/ 1004,
+ -1, -1,
+ /*0x1ed2*/ 692,
+ /*0xa694*/ 1059,
+ -1,
+ /*0xff32*/ 1251,
+ -1,
+ /*0x118af*/ 1402,
+ /*0x1ecc*/ 689,
+ /*0x2cb4*/ 999,
+ -1, -1,
+ /*0xa7be*/ 1137,
+ -1,
+ /*0x1ebe*/ 682,
+ /*0x1f0e*/ 721,
+ -1,
+ /*0xa690*/ 1057,
+ /*0x048e*/ 364,
+ /*0x050e*/ 428,
+ /*0x10ac*/ 496,
+ /*0x1cbe*/ 583,
+ /*0xa7b4*/ 1132,
+ -1,
+ /*0x1eb4*/ 677,
+ /*0x1e2e*/ 608,
+ -1, -1, -1,
+ /*0x1f2a*/ 731,
+ /*0xff2f*/ 1248,
+ /*0x1cb4*/ 575,
+ /*0x04aa*/ 378,
+ /*0x052a*/ 442,
+ -1, -1,
+ /*0x10ba*/ 510,
+ -1, -1,
+ /*0x1fca*/ 833,
+ -1,
+ /*0x024a*/ 226,
+ /*0x014a*/ 93,
+ /*0x004a*/ 8,
+ /*0xa684*/ 1051,
+ /*0x2126*/ 864,
+ /*0x01ca*/ 161,
+ /*0x10a8*/ 492,
+ -1, -1,
+ /*0x1e2c*/ 607,
+ -1, -1, -1, -1, -1,
+ /*0x1f2e*/ 735,
+ -1,
+ /*0xa682*/ 1050,
+ /*0x04ae*/ 380,
+ /*0x052e*/ 444,
+ -1,
+ /*0xa732*/ 1070,
+ /*0x2cca*/ 1010,
+ -1,
+ /*0x1fc4*/ 828,
+ /*0x1e3a*/ 614,
+ /*0x0244*/ 222,
+ -1,
+ /*0x0044*/ 3,
+ /*0x004f*/ 13,
+ -1,
+ /*0x01c4*/ 157,
+ /*0x01cf*/ 164,
+ /*0x03cf*/ 270,
+ -1,
+ /*0x10b8*/ 508,
+ /*0x1e28*/ 605,
+ /*0x1eca*/ 688,
+ /*0x1fab*/ 810,
+ /*0x042b*/ 340,
+ /*0x1f2c*/ 733,
+ -1,
+ /*0xabab*/ 1201,
+ /*0x04ac*/ 379,
+ /*0x052c*/ 443,
+ -1,
+ /*0x03ab*/ 267,
+ -1,
+ /*0x2cc4*/ 1007,
+ /*0x2c2b*/ 954,
+ /*0x1fc2*/ 826,
+ /*0x10b6*/ 506,
+ /*0x24ba*/ 889,
+ /*0x118a2*/ 1389,
+ /*0x0042*/ 1,
+ -1,
+ /*0x1f3a*/ 739,
+ /*0x104ba*/ 1310,
+ /*0x03c2*/ 269,
+ /*0x04ba*/ 386,
+ /*0x053a*/ 454,
+ /*0xa7c4*/ 1139,
+ /*0x1fc8*/ 831,
+ /*0x1ec4*/ 685,
+ /*0x0248*/ 225,
+ /*0x1e38*/ 613,
+ /*0x0048*/ 7,
+ /*0x1f28*/ 729,
+ -1,
+ /*0x01c8*/ 160,
+ /*0x04a8*/ 377,
+ /*0x0528*/ 441,
+ -1, -1,
+ /*0x2cc2*/ 1006,
+ /*0xa7ab*/ 1124,
+ /*0xff22*/ 1235,
+ -1, -1, -1, -1,
+ /*0x1e36*/ 612,
+ -1, -1,
+ /*0x1cab*/ 566,
+ /*0x10cab*/ 1379,
+ /*0x2cc8*/ 1009,
+ /*0xa7c2*/ 1138,
+ -1,
+ /*0x1ec2*/ 684,
+ /*0x10b0*/ 500,
+ -1,
+ /*0x24b8*/ 887,
+ /*0x216b*/ 879,
+ /*0x118aa*/ 1397,
+ /*0x1e0c*/ 591,
+ /*0x1f38*/ 737,
+ /*0x104b8*/ 1308,
+ -1,
+ /*0x04b8*/ 385,
+ /*0x0538*/ 452,
+ /*0x1ec8*/ 687,
+ /*0x10bc*/ 512,
+ /*0x1fd6*/ 838,
+ /*0x0150*/ 96,
+ /*0x0050*/ 14,
+ /*0x0156*/ 99,
+ /*0x0056*/ 20,
+ /*0x24b6*/ 885,
+ /*0x03d0*/ 271,
+ -1,
+ /*0x03d6*/ 274,
+ /*0x1fdb*/ 843,
+ /*0x104b6*/ 1306,
+ -1,
+ /*0x04b6*/ 384,
+ /*0x0536*/ 450,
+ /*0xff2a*/ 1243,
+ -1,
+ /*0x01db*/ 170,
+ /*0x1e30*/ 609,
+ /*0x118ae*/ 1401,
+ -1, -1, -1,
+ /*0x2cd0*/ 1013,
+ /*0x1f0c*/ 719,
+ /*0x2cd6*/ 1016,
+ /*0xa69a*/ 1062,
+ /*0x048c*/ 363,
+ /*0x050c*/ 427,
+ -1,
+ /*0x1e3c*/ 615,
+ /*0xa722*/ 1063,
+ -1, -1, -1, -1, -1, -1,
+ /*0x1ed0*/ 691,
+ /*0x1fc6*/ 829,
+ /*0x1ed6*/ 694,
+ /*0x0246*/ 224,
+ /*0xff2e*/ 1247,
+ /*0x0046*/ 5,
+ /*0x118ac*/ 1399,
+ -1,
+ /*0x2132*/ 867,
+ /*0x024e*/ 228,
+ /*0x014e*/ 95,
+ /*0x004e*/ 12,
+ /*0x104b0*/ 1300,
+ /*0x1fda*/ 842,
+ /*0x04b0*/ 381,
+ -1,
+ /*0x015a*/ 101,
+ /*0x005a*/ 24,
+ /*0x10be*/ 514,
+ /*0x24bc*/ 891,
+ -1,
+ /*0x03da*/ 276,
+ /*0x118ba*/ 1413,
+ /*0x1f3c*/ 741,
+ /*0x104bc*/ 1312,
+ /*0x2cc6*/ 1008,
+ /*0x04bc*/ 387,
+ /*0x053c*/ 456,
+ /*0x10b4*/ 504,
+ /*0xff2c*/ 1245,
+ -1,
+ /*0x2cce*/ 1012,
+ -1,
+ /*0x118a8*/ 1395,
+ /*0xa72a*/ 1067,
+ -1,
+ /*0x1e52*/ 626,
+ /*0x2cda*/ 1018,
+ /*0xa7c6*/ 1141,
+ -1,
+ /*0x1ec6*/ 686,
+ -1,
+ /*0x1e4c*/ 623,
+ -1, -1,
+ /*0xff3a*/ 1259,
+ /*0x1ece*/ 690,
+ /*0xa652*/ 1035,
+ /*0x1e3e*/ 616,
+ /*0x2169*/ 877,
+ -1, -1,
+ /*0x1eda*/ 696,
+ /*0xa64c*/ 1032,
+ /*0x00d2*/ 44,
+ -1,
+ /*0xff28*/ 1241,
+ -1,
+ /*0x1e34*/ 611,
+ /*0xa692*/ 1058,
+ /*0x00cc*/ 38,
+ /*0xa72e*/ 1069,
+ /*0x118b8*/ 1411,
+ -1, -1, -1, -1,
+ /*0x1f52*/ 752,
+ /*0x104d2*/ 1334,
+ /*0x24cc*/ 907,
+ /*0x04d2*/ 398,
+ /*0x0552*/ 478,
+ -1,
+ /*0x1f4c*/ 749,
+ /*0x104cc*/ 1328,
+ /*0x24be*/ 893,
+ /*0x2cc0*/ 1005,
+ /*0x054c*/ 472,
+ /*0x118b6*/ 1409,
+ /*0x1f3e*/ 743,
+ /*0x104be*/ 1314,
+ -1,
+ /*0x04be*/ 388,
+ /*0x053e*/ 458,
+ -1,
+ /*0xff38*/ 1257,
+ /*0xa72c*/ 1068,
+ -1, -1, -1,
+ /*0x104b4*/ 1304,
+ /*0x1ec0*/ 683,
+ /*0x04b4*/ 383,
+ /*0x0534*/ 448,
+ -1, -1,
+ /*0x1fe2*/ 844,
+ /*0x0462*/ 346,
+ -1,
+ /*0x0162*/ 105,
+ -1,
+ /*0xff36*/ 1255,
+ /*0xa73a*/ 1074,
+ /*0x01e2*/ 173,
+ /*0x03e2*/ 280,
+ /*0x0154*/ 98,
+ /*0x0054*/ 18,
+ /*0x2c62*/ 959,
+ /*0x10c4*/ 520,
+ -1, -1, -1,
+ /*0x1e4a*/ 622,
+ /*0xa728*/ 1066,
+ -1, -1, -1,
+ /*0x118b0*/ 1403,
+ -1, -1,
+ /*0x2ce2*/ 1022,
+ -1,
+ /*0x10ab*/ 495,
+ /*0xa64a*/ 1031,
+ /*0x1fd8*/ 840,
+ -1,
+ /*0x2cd4*/ 1015,
+ /*0x0158*/ 100,
+ /*0x0058*/ 22,
+ /*0x118bc*/ 1415,
+ /*0x00ca*/ 36,
+ -1,
+ /*0x03d8*/ 275,
+ -1,
+ /*0x10c2*/ 518,
+ /*0x1ee2*/ 700,
+ -1, -1,
+ /*0x1e44*/ 619,
+ /*0x24ca*/ 905,
+ /*0xff30*/ 1249,
+ /*0x1ed4*/ 693,
+ /*0xa738*/ 1073,
+ /*0x1f4a*/ 747,
+ /*0x104ca*/ 1326,
+ -1, -1,
+ /*0x054a*/ 470,
+ /*0x2cd8*/ 1017,
+ /*0xa644*/ 1028,
+ /*0x1040d*/ 1273,
+ /*0x1f8d*/ 780,
+ /*0x040d*/ 310,
+ -1, -1,
+ /*0xab8d*/ 1171,
+ /*0x00c4*/ 30,
+ /*0x00cf*/ 41,
+ /*0xa736*/ 1072,
+ -1, -1,
+ /*0x212a*/ 865,
+ /*0x2c0d*/ 924,
+ /*0x1ed8*/ 695,
+ /*0x1e42*/ 618,
+ /*0x24c4*/ 899,
+ /*0x24cf*/ 910,
+ -1, -1, -1,
+ /*0x104c4*/ 1320,
+ /*0x104cf*/ 1331,
+ -1,
+ /*0x0544*/ 464,
+ /*0x054f*/ 475,
+ /*0xa642*/ 1027,
+ /*0x1e48*/ 621,
+ -1, -1, -1, -1, -1,
+ /*0x00c2*/ 28,
+ /*0x1f2b*/ 732,
+ -1,
+ /*0x118be*/ 1417,
+ /*0x0055*/ 19,
+ /*0xa648*/ 1030,
+ /*0xa78d*/ 1110,
+ /*0x01d5*/ 167,
+ /*0x03d5*/ 273,
+ /*0x24c2*/ 897,
+ -1, -1,
+ /*0x00c8*/ 34,
+ /*0x118b4*/ 1407,
+ /*0x104c2*/ 1318,
+ -1,
+ /*0x10c8d*/ 1349,
+ /*0x0542*/ 462,
+ -1,
+ /*0x1e90d*/ 1464,
+ -1,
+ /*0x24c8*/ 903,
+ -1, -1, -1,
+ /*0x1f48*/ 745,
+ /*0x104c8*/ 1324,
+ /*0xa73c*/ 1075,
+ -1,
+ /*0x0548*/ 468,
+ -1, -1,
+ /*0xa68e*/ 1056,
+ /*0x1e50*/ 625,
+ -1,
+ /*0x1e56*/ 628,
+ /*0xff34*/ 1253,
+ /*0x0245*/ 223,
+ /*0x0145*/ 90,
+ /*0x0045*/ 4,
+ -1,
+ /*0x16e5f*/ 1450,
+ /*0x01c5*/ 158,
+ -1,
+ /*0xa650*/ 1034,
+ /*0x1fd9*/ 841,
+ /*0xa656*/ 1037,
+ /*0x1fec*/ 853,
+ /*0x046c*/ 351,
+ /*0x0059*/ 23,
+ /*0x016c*/ 110,
+ /*0x00d0*/ 42,
+ /*0x01d9*/ 169,
+ /*0x00d6*/ 48,
+ /*0x01ec*/ 178,
+ /*0x03ec*/ 285,
+ /*0x16e5d*/ 1448,
+ -1, -1,
+ /*0x16e47*/ 1426,
+ -1, -1,
+ /*0x00db*/ 52,
+ -1,
+ /*0x1f50*/ 751,
+ /*0x104d0*/ 1332,
+ /*0x1f56*/ 754,
+ /*0x04d0*/ 397,
+ /*0x0550*/ 476,
+ /*0x04d6*/ 400,
+ /*0x0556*/ 482,
+ /*0xa752*/ 1086,
+ /*0xa7c5*/ 1140,
+ /*0x1e46*/ 620,
+ -1,
+ /*0x1f5b*/ 756,
+ -1,
+ /*0xa74c*/ 1083,
+ -1,
+ /*0x1e4e*/ 624,
+ -1, -1, -1,
+ /*0xa73e*/ 1076,
+ /*0xa646*/ 1029,
+ /*0x1e5a*/ 630,
+ /*0x1eec*/ 705,
+ -1, -1, -1,
+ /*0xa64e*/ 1033,
+ /*0x00c6*/ 32,
+ -1,
+ /*0xa734*/ 1071,
+ /*0x10c0*/ 516,
+ -1,
+ /*0xa65a*/ 1039,
+ /*0x00ce*/ 40,
+ /*0x1fc9*/ 832,
+ -1,
+ /*0x24c6*/ 901,
+ /*0x0149*/ 92,
+ /*0x0049*/ 1485,
+ /*0x00da*/ 51,
+ -1,
+ /*0x104c6*/ 1322,
+ /*0x24ce*/ 909,
+ /*0x118ab*/ 1398,
+ /*0x0546*/ 466,
+ -1, -1,
+ /*0x104ce*/ 1330,
+ -1, -1,
+ /*0x054e*/ 474,
+ /*0x1fea*/ 851,
+ /*0x046a*/ 350,
+ -1,
+ /*0x016a*/ 109,
+ /*0x04da*/ 402,
+ /*0x0345*/ 229,
+ -1,
+ /*0x01ea*/ 177,
+ /*0x03ea*/ 284,
+ /*0x1e40*/ 617,
+ /*0x1fa9*/ 808,
+ /*0x0429*/ 338,
+ -1, -1,
+ /*0xaba9*/ 1199,
+ /*0xff2b*/ 1244,
+ -1,
+ /*0x01a9*/ 146,
+ /*0x03a9*/ 265,
+ -1,
+ /*0xa640*/ 1026,
+ /*0x2c29*/ 952,
+ /*0x1fc3*/ 827,
+ -1,
+ /*0x0243*/ 221,
+ /*0x0143*/ 89,
+ /*0x0043*/ 2,
+ /*0x00c0*/ 26,
+ /*0x10421*/ 1293,
+ /*0x1fa1*/ 800,
+ /*0x0421*/ 330,
+ -1,
+ /*0xa74a*/ 1082,
+ /*0xaba1*/ 1191,
+ -1, -1,
+ /*0x24c0*/ 895,
+ /*0x03a1*/ 258,
+ -1,
+ /*0x1eea*/ 704,
+ /*0x2c21*/ 944,
+ /*0x104c0*/ 1316,
+ /*0x1fb9*/ 821,
+ /*0x04c0*/ 389,
+ /*0x0540*/ 460,
+ /*0x0139*/ 84,
+ /*0xabb9*/ 1215,
+ /*0x1fe8*/ 849,
+ /*0x0468*/ 349,
+ -1,
+ /*0x0168*/ 108,
+ -1, -1,
+ /*0x1e62*/ 634,
+ /*0x01e8*/ 176,
+ /*0x03e8*/ 283,
+ /*0x1ca9*/ 564,
+ /*0x10ca9*/ 1377,
+ /*0xa744*/ 1079,
+ /*0x1e54*/ 627,
+ -1,
+ /*0x1fcb*/ 834,
+ -1, -1,
+ /*0xa662*/ 1043,
+ /*0x004b*/ 9,
+ -1, -1,
+ /*0x01cb*/ 162,
+ /*0xa68c*/ 1055,
+ /*0xa654*/ 1036,
+ -1, -1, -1, -1,
+ /*0x1ca1*/ 556,
+ /*0x10ca1*/ 1369,
+ /*0x00d4*/ 46,
+ -1,
+ /*0x1e921*/ 1484,
+ -1,
+ /*0x1e58*/ 629,
+ -1,
+ /*0x16e57*/ 1442,
+ /*0xa742*/ 1078,
+ -1,
+ /*0x1ee8*/ 703,
+ /*0x04e2*/ 406,
+ /*0x1cb9*/ 580,
+ -1,
+ /*0x1f54*/ 753,
+ -1,
+ /*0xa658*/ 1038,
+ /*0x04d4*/ 399,
+ /*0x0554*/ 480,
+ -1,
+ /*0xa748*/ 1081,
+ -1, -1,
+ /*0x00d8*/ 49,
+ /*0x10417*/ 1283,
+ /*0x1f97*/ 790,
+ /*0x0417*/ 320,
+ /*0x1fe4*/ 846,
+ /*0x0464*/ 347,
+ /*0xab97*/ 1181,
+ /*0x0164*/ 106,
+ -1,
+ /*0x0197*/ 136,
+ /*0x0397*/ 248,
+ /*0x01e4*/ 174,
+ /*0x03e4*/ 281,
+ /*0x2c17*/ 934,
+ -1,
+ /*0x2c64*/ 961,
+ /*0x04d8*/ 401,
+ -1, -1,
+ /*0x0460*/ 345,
+ -1,
+ /*0x0160*/ 104,
+ -1, -1, -1,
+ /*0x01e0*/ 172,
+ /*0x03e0*/ 279,
+ /*0x1ff4*/ 856,
+ /*0x0474*/ 355,
+ /*0x2c60*/ 958,
+ /*0x0174*/ 114,
+ -1,
+ /*0x1ffc*/ 863,
+ /*0x047c*/ 359,
+ /*0x01f4*/ 183,
+ /*0x03f4*/ 289,
+ /*0xa750*/ 1085,
+ -1,
+ /*0xa756*/ 1088,
+ /*0x01fc*/ 188,
+ /*0x1f0d*/ 720,
+ /*0x1e97*/ 661,
+ /*0x2ce0*/ 1021,
+ /*0x1ee4*/ 701,
+ -1,
+ /*0x10c5*/ 521,
+ -1, -1,
+ /*0x1c97*/ 546,
+ /*0x10c97*/ 1359,
+ -1, -1,
+ /*0x1e917*/ 1474,
+ -1,
+ /*0x046e*/ 352,
+ -1,
+ /*0x016e*/ 111,
+ /*0x1ee0*/ 699,
+ /*0x00d5*/ 47,
+ /*0x13fc*/ 528,
+ /*0x01ee*/ 179,
+ /*0x03ee*/ 286,
+ /*0x1fe6*/ 847,
+ /*0x0466*/ 348,
+ /*0x2c6e*/ 966,
+ /*0x0166*/ 107,
+ /*0x1ef4*/ 709,
+ -1,
+ /*0x015e*/ 103,
+ /*0x01e6*/ 175,
+ /*0x03e6*/ 282,
+ /*0x1efc*/ 713,
+ /*0x01de*/ 171,
+ /*0x03de*/ 278,
+ -1,
+ /*0x0555*/ 481,
+ /*0xa746*/ 1080,
+ -1, -1,
+ /*0x16e53*/ 1438,
+ -1, -1,
+ /*0xa74e*/ 1084,
+ -1, -1, -1, -1,
+ /*0x1e6c*/ 639,
+ /*0xa75a*/ 1090,
+ /*0x2cde*/ 1020,
+ -1,
+ /*0x0051*/ 15,
+ /*0x1eee*/ 706,
+ /*0x00c5*/ 31,
+ /*0x01d1*/ 165,
+ /*0x03d1*/ 272,
+ -1, -1,
+ /*0xa66c*/ 1048,
+ -1,
+ /*0x212b*/ 866,
+ /*0x1ee6*/ 702,
+ /*0x24c5*/ 900,
+ /*0x00d9*/ 50,
+ /*0x1ede*/ 698,
+ -1, -1,
+ /*0x104c5*/ 1321,
+ -1,
+ /*0x04c5*/ 392,
+ /*0x0545*/ 465,
+ /*0x1fad*/ 812,
+ /*0x042d*/ 342,
+ -1, -1,
+ /*0xabad*/ 1203,
+ /*0x1f59*/ 755,
+ -1,
+ /*0x1f6c*/ 763,
+ -1, -1,
+ /*0x04ec*/ 411,
+ /*0x2c2d*/ 956,
+ /*0x015c*/ 102,
+ -1,
+ /*0xfb17*/ 1233,
+ -1,
+ /*0xa740*/ 1077,
+ /*0x03dc*/ 277,
+ /*0x1ff2*/ 854,
+ /*0x0472*/ 354,
+ -1,
+ /*0x0172*/ 113,
+ -1, -1,
+ /*0x10a9*/ 493,
+ /*0x01f2*/ 182,
+ /*0x10425*/ 1297,
+ /*0x1fa5*/ 804,
+ /*0x0425*/ 334,
+ /*0x2c72*/ 969,
+ -1,
+ /*0xaba5*/ 1195,
+ -1,
+ /*0x2cdc*/ 1019,
+ -1,
+ /*0x03a5*/ 261,
+ /*0x10c3*/ 519,
+ /*0xa7ad*/ 1126,
+ /*0x2c25*/ 948,
+ -1, -1, -1,
+ /*0x2cf2*/ 1025,
+ /*0x10a1*/ 485,
+ /*0x1e6a*/ 638,
+ /*0x00c9*/ 35,
+ /*0x1cad*/ 568,
+ /*0x10cad*/ 1381,
+ /*0x1edc*/ 697,
+ /*0x004d*/ 11,
+ -1, -1,
+ /*0x01cd*/ 163,
+ -1,
+ /*0x24c9*/ 904,
+ /*0xa66a*/ 1047,
+ /*0x10b9*/ 509,
+ /*0x1ef2*/ 708,
+ /*0x1f49*/ 746,
+ /*0x104c9*/ 1325,
+ /*0xa762*/ 1094,
+ /*0x04c9*/ 394,
+ /*0x0549*/ 469,
+ /*0x013f*/ 87,
+ /*0xabbf*/ 1221,
+ -1,
+ /*0xa754*/ 1087,
+ /*0x10423*/ 1295,
+ /*0x1fa3*/ 802,
+ /*0x0423*/ 332,
+ -1, -1,
+ /*0xaba3*/ 1193,
+ /*0x1ca5*/ 560,
+ /*0x10ca5*/ 1373,
+ /*0x1f6a*/ 761,
+ /*0x03a3*/ 259,
+ -1,
+ /*0x04ea*/ 410,
+ /*0x2c23*/ 946,
+ -1,
+ /*0x0241*/ 220,
+ /*0x0141*/ 88,
+ /*0x0041*/ 0,
+ /*0x00c3*/ 29,
+ /*0x1f29*/ 730,
+ -1, -1,
+ /*0xa758*/ 1089,
+ -1, -1,
+ /*0x1e68*/ 637,
+ -1,
+ /*0x24c3*/ 898,
+ -1, -1, -1, -1,
+ /*0x104c3*/ 1319,
+ -1,
+ /*0x04c3*/ 391,
+ /*0x0543*/ 463,
+ /*0xa668*/ 1046,
+ /*0x0372*/ 231,
+ -1, -1,
+ /*0x1cbf*/ 584,
+ -1, -1, -1,
+ /*0x023d*/ 218,
+ /*0x013d*/ 86,
+ /*0xabbd*/ 1219,
+ /*0x24b9*/ 888,
+ /*0x1ca3*/ 558,
+ /*0x10ca3*/ 1371,
+ -1,
+ /*0x1f39*/ 738,
+ /*0x104b9*/ 1309,
+ -1, -1,
+ /*0x0539*/ 453,
+ /*0x1f68*/ 759,
+ /*0x00cb*/ 37,
+ -1,
+ /*0x04e8*/ 409,
+ /*0x1041d*/ 1289,
+ /*0x1f9d*/ 796,
+ /*0x041d*/ 326,
+ -1, -1,
+ /*0xab9d*/ 1187,
+ /*0x24cb*/ 906,
+ -1,
+ /*0x019d*/ 139,
+ /*0x039d*/ 254,
+ /*0x1f4b*/ 748,
+ /*0x104cb*/ 1327,
+ /*0x2c1d*/ 940,
+ /*0x04cb*/ 395,
+ /*0x054b*/ 471,
+ /*0x10407*/ 1267,
+ /*0x1f87*/ 774,
+ /*0x0407*/ 304,
+ /*0x0587*/ 483,
+ -1,
+ /*0xab87*/ 1165,
+ /*0x1e64*/ 635,
+ -1,
+ /*0x0187*/ 125,
+ /*0x1041b*/ 1287,
+ /*0x1f9b*/ 794,
+ /*0x041b*/ 324,
+ /*0x2c07*/ 918,
+ /*0x1cbd*/ 582,
+ /*0xab9b*/ 1185,
+ -1, -1,
+ /*0xa664*/ 1044,
+ /*0x039b*/ 252,
+ -1,
+ /*0x1e60*/ 633,
+ /*0x2c1b*/ 938,
+ /*0x1fbb*/ 823,
+ -1,
+ /*0x023b*/ 217,
+ /*0x013b*/ 85,
+ /*0xabbb*/ 1217,
+ -1, -1,
+ /*0x1e74*/ 643,
+ /*0xab73*/ 1145,
+ /*0xa660*/ 1042,
+ /*0x1c9d*/ 552,
+ /*0x10c9d*/ 1365,
+ /*0x1e7c*/ 647,
+ -1,
+ /*0x1e91d*/ 1480,
+ -1,
+ /*0x1ff6*/ 857,
+ /*0x0476*/ 356,
+ /*0x04e4*/ 407,
+ /*0x0176*/ 115,
+ -1, -1, -1,
+ /*0x01f6*/ 184,
+ /*0x2162*/ 870,
+ /*0x1c87*/ 537,
+ /*0x10c87*/ 1343,
+ /*0x1e9b*/ 665,
+ /*0x118a9*/ 1396,
+ /*0x1e907*/ 1458,
+ /*0xa76c*/ 1099,
+ -1,
+ /*0x04e0*/ 405,
+ /*0x1e6e*/ 640,
+ /*0x1c9b*/ 550,
+ /*0x10c9b*/ 1363,
+ -1, -1,
+ /*0x1e91b*/ 1478,
+ -1, -1,
+ /*0x04f4*/ 415,
+ /*0x1e66*/ 636,
+ -1, -1,
+ /*0x1e5e*/ 632,
+ /*0x04fc*/ 419,
+ /*0x118a1*/ 1388,
+ -1,
+ /*0xabb1*/ 1207,
+ -1,
+ /*0xff29*/ 1242,
+ /*0x01b1*/ 150,
+ /*0xa666*/ 1045,
+ -1,
+ /*0x1ef6*/ 710,
+ /*0xa65e*/ 1041,
+ /*0x10419*/ 1285,
+ /*0x1f99*/ 792,
+ /*0x0419*/ 322,
+ /*0x118b9*/ 1412,
+ /*0x10ad*/ 497,
+ /*0xab99*/ 1183,
+ /*0x00de*/ 55,
+ /*0x1f6e*/ 765,
+ -1,
+ /*0x0399*/ 250,
+ /*0x04ee*/ 412,
+ -1,
+ /*0x2c19*/ 936,
+ /*0xff21*/ 1234,
+ /*0x1fb7*/ 819,
+ /*0x10413*/ 1279,
+ /*0x1f93*/ 786,
+ /*0x0413*/ 316,
+ /*0xabb7*/ 1213,
+ /*0x04e6*/ 408,
+ /*0xab93*/ 1177,
+ /*0x01b7*/ 154,
+ /*0x04de*/ 404,
+ /*0x0193*/ 133,
+ /*0x0393*/ 244,
+ /*0xa7b1*/ 1129,
+ /*0xff39*/ 1258,
+ /*0x2c13*/ 930,
+ /*0x00d1*/ 43,
+ /*0x1ffa*/ 861,
+ /*0x047a*/ 358,
+ /*0x10a5*/ 489,
+ -1, -1,
+ /*0x1cb1*/ 572,
+ /*0x10cb1*/ 1385,
+ /*0x01fa*/ 187,
+ /*0x03fa*/ 293,
+ -1, -1,
+ /*0x1e99*/ 663,
+ /*0xa76a*/ 1098,
+ /*0x104d1*/ 1333,
+ /*0x1e5c*/ 631,
+ /*0x0376*/ 232,
+ /*0x0551*/ 477,
+ -1,
+ /*0x1c99*/ 548,
+ /*0x10c99*/ 1361,
+ /*0x10cd*/ 523,
+ -1,
+ /*0x1e919*/ 1476,
+ /*0x1e72*/ 642,
+ -1,
+ /*0xa65c*/ 1040,
+ -1,
+ /*0x13fa*/ 526,
+ -1, -1, -1,
+ /*0x1cb7*/ 578,
+ /*0x00dc*/ 53,
+ /*0x1c93*/ 542,
+ /*0x10c93*/ 1355,
+ /*0x10bf*/ 515,
+ /*0x1f2d*/ 734,
+ /*0x1e913*/ 1470,
+ /*0xabb5*/ 1211,
+ /*0x1efa*/ 712,
+ -1,
+ /*0x01b5*/ 153,
+ -1,
+ /*0x10a3*/ 487,
+ /*0xab75*/ 1147,
+ -1, -1, -1,
+ /*0x04dc*/ 403,
+ /*0x1fb3*/ 816,
+ -1, -1, -1,
+ /*0xabb3*/ 1209,
+ /*0x10c1*/ 517,
+ -1,
+ /*0x01b3*/ 152,
+ /*0x04f2*/ 414,
+ -1,
+ /*0xa768*/ 1097,
+ -1,
+ /*0x1041f*/ 1291,
+ /*0x1f9f*/ 798,
+ /*0x041f*/ 328,
+ -1, -1,
+ /*0xab9f*/ 1189,
+ -1,
+ /*0x00cd*/ 39,
+ /*0x019f*/ 140,
+ /*0x039f*/ 256,
+ /*0x216c*/ 880,
+ -1,
+ /*0x2c1f*/ 942,
+ -1, -1, -1,
+ /*0x24cd*/ 908,
+ -1, -1,
+ /*0x1cb5*/ 576,
+ /*0x1f4d*/ 750,
+ /*0x104cd*/ 1329,
+ /*0x10bd*/ 513,
+ /*0x04cd*/ 396,
+ /*0x054d*/ 473,
+ /*0xa7b3*/ 1131,
+ /*0x1ff8*/ 859,
+ /*0x0478*/ 357,
+ /*0xab7f*/ 1157,
+ /*0x0178*/ 116,
+ /*0xab71*/ 1143,
+ /*0x24bf*/ 894,
+ -1,
+ /*0x01f8*/ 186,
+ /*0x1cb3*/ 574,
+ /*0x1f3f*/ 744,
+ /*0x104bf*/ 1315,
+ -1, -1,
+ /*0x053f*/ 459,
+ -1,
+ /*0x00c1*/ 27,
+ -1, -1, -1, -1, -1,
+ /*0x1c9f*/ 554,
+ /*0x10c9f*/ 1367,
+ /*0xfb13*/ 1229,
+ /*0x24c1*/ 896,
+ /*0x1e91f*/ 1482,
+ -1,
+ /*0x13f8*/ 524,
+ /*0xa764*/ 1095,
+ /*0x104c1*/ 1317,
+ -1,
+ /*0x04c1*/ 390,
+ /*0x0541*/ 461,
+ /*0xab79*/ 1151,
+ -1, -1,
+ /*0x10415*/ 1281,
+ /*0x1f95*/ 788,
+ /*0x0415*/ 318,
+ /*0x1ef8*/ 711,
+ -1,
+ /*0xab95*/ 1179,
+ /*0xa760*/ 1093,
+ -1, -1,
+ /*0x0395*/ 246,
+ -1, -1,
+ /*0x2c15*/ 932,
+ -1, -1,
+ /*0x10bb*/ 511,
+ /*0x216a*/ 878,
+ /*0x24bd*/ 892,
+ -1,
+ /*0x118ad*/ 1400,
+ -1,
+ /*0x1f3d*/ 742,
+ /*0x104bd*/ 1313,
+ -1,
+ /*0x047e*/ 360,
+ /*0x053d*/ 457,
+ /*0x16e52*/ 1437,
+ -1, -1, -1,
+ /*0x01fe*/ 189,
+ /*0x03fe*/ 295,
+ /*0x16e4c*/ 1431,
+ /*0xab7b*/ 1153,
+ /*0x2c7e*/ 971,
+ -1, -1, -1, -1, -1,
+ /*0x1f1d*/ 728,
+ /*0xa76e*/ 1100,
+ /*0xff2d*/ 1246,
+ -1, -1, -1,
+ /*0x118a5*/ 1392,
+ /*0x1c95*/ 544,
+ /*0x10c95*/ 1357,
+ -1,
+ /*0xa766*/ 1096,
+ /*0x1e915*/ 1472,
+ -1,
+ /*0xa75e*/ 1092,
+ -1, -1, -1, -1, -1, -1,
+ /*0x10b1*/ 501,
+ /*0x1e76*/ 644,
+ /*0x1efe*/ 714,
+ /*0x2168*/ 876,
+ /*0x1f1b*/ 726,
+ -1,
+ /*0x10411*/ 1277,
+ /*0x1f91*/ 784,
+ /*0x0411*/ 314,
+ /*0xff25*/ 1238,
+ -1,
+ /*0xab91*/ 1175,
+ /*0x24bb*/ 890,
+ -1,
+ /*0x0191*/ 132,
+ /*0x0391*/ 242,
+ /*0x1f3b*/ 740,
+ /*0x104bb*/ 1311,
+ /*0x2c11*/ 928,
+ /*0x118bf*/ 1418,
+ /*0x053b*/ 455,
+ -1, -1,
+ /*0xab7d*/ 1155,
+ -1, -1,
+ /*0x10b7*/ 507,
+ /*0x118a3*/ 1390,
+ /*0x1040f*/ 1275,
+ /*0x1f8f*/ 782,
+ /*0x040f*/ 312,
+ -1, -1,
+ /*0xab8f*/ 1173,
+ -1,
+ /*0x04f6*/ 416,
+ /*0x018f*/ 130,
+ /*0x038f*/ 240,
+ -1, -1,
+ /*0x2c0f*/ 926,
+ -1,
+ /*0x16e4a*/ 1429,
+ -1,
+ /*0x1040b*/ 1271,
+ /*0x1f8b*/ 778,
+ /*0x040b*/ 308,
+ -1,
+ /*0xa75c*/ 1091,
+ /*0xab8b*/ 1169,
+ /*0xff23*/ 1236,
+ -1,
+ /*0x018b*/ 128,
+ /*0x1c91*/ 540,
+ /*0x10c91*/ 1353,
+ -1,
+ /*0x2c0b*/ 922,
+ /*0x1e911*/ 1468,
+ -1,
+ /*0x2164*/ 872,
+ /*0xab77*/ 1149,
+ -1,
+ /*0x104b1*/ 1301,
+ -1,
+ /*0xfb15*/ 1231,
+ /*0x0531*/ 445,
+ -1,
+ /*0x118bd*/ 1416,
+ /*0x16e44*/ 1423,
+ /*0x16e4f*/ 1434,
+ -1,
+ /*0x1e7a*/ 646,
+ -1,
+ /*0x2160*/ 868,
+ /*0x1f19*/ 724,
+ /*0x10b5*/ 505,
+ /*0x10c8f*/ 1351,
+ -1, -1,
+ /*0x1e90f*/ 1466,
+ -1, -1,
+ /*0xa78b*/ 1109,
+ /*0x24b7*/ 886,
+ /*0x10405*/ 1265,
+ /*0x1f85*/ 772,
+ /*0x0405*/ 302,
+ -1,
+ /*0x104b7*/ 1307,
+ /*0xab85*/ 1163,
+ /*0x10b3*/ 503,
+ /*0x0537*/ 451,
+ /*0x10c8b*/ 1347,
+ -1,
+ /*0x16e42*/ 1421,
+ /*0x1e90b*/ 1462,
+ /*0x2c05*/ 916,
+ -1, -1, -1, -1, -1,
+ /*0x10401*/ 1261,
+ /*0x1f81*/ 768,
+ /*0x0401*/ 298,
+ /*0x04fa*/ 418,
+ /*0x16e48*/ 1427,
+ /*0xab81*/ 1159,
+ /*0x216e*/ 882,
+ -1,
+ /*0x0181*/ 121,
+ -1, -1, -1,
+ /*0x2c01*/ 912,
+ -1, -1,
+ /*0x2166*/ 874,
+ -1, -1, -1, -1,
+ /*0x118bb*/ 1414,
+ /*0x00b5*/ 25,
+ -1, -1, -1, -1, -1, -1,
+ -1,
+ /*0x1c85*/ 535,
+ /*0x10c85*/ 1341,
+ -1, -1,
+ /*0x1e905*/ 1456,
+ -1,
+ /*0x104b5*/ 1305,
+ -1, -1,
+ /*0x0535*/ 449,
+ -1, -1, -1, -1,
+ /*0x16e50*/ 1435,
+ -1,
+ /*0x16e56*/ 1441,
+ -1,
+ /*0x1c81*/ 531,
+ /*0x10c81*/ 1337,
+ -1,
+ /*0x104b3*/ 1303,
+ /*0x1e901*/ 1452,
+ -1,
+ /*0x0533*/ 447,
+ /*0x16e5b*/ 1446,
+ -1,
+ /*0x1e78*/ 645,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1,
+ /*0x118b1*/ 1404,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1,
+ /*0x16e46*/ 1425,
+ /*0xab70*/ 1142,
+ -1, -1, -1,
+ /*0xff31*/ 1250,
+ /*0x16e4e*/ 1433,
+ /*0x04f8*/ 417,
+ /*0x118b7*/ 1410,
+ -1, -1, -1,
+ /*0x16e5a*/ 1445,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ /*0xfb05*/ 1227,
+ -1, -1, -1, -1, -1, -1,
+ -1,
+ /*0xff37*/ 1256,
+ /*0x1e7e*/ 648,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1,
+ /*0xfb01*/ 1223,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1,
+ /*0x16e40*/ 1419,
+ -1, -1, -1, -1, -1,
+ /*0x118b5*/ 1408,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1,
+ /*0x04fe*/ 420,
+ -1, -1, -1, -1, -1,
+ /*0x118b3*/ 1406,
+ -1, -1, -1, -1, -1, -1,
+ -1,
+ /*0xff35*/ 1254,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1,
+ /*0xff33*/ 1252,
+ -1, -1, -1, -1, -1,
+ /*0x16e54*/ 1439,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1,
+ /*0x16e58*/ 1443,
+ -1, -1, -1, -1, -1,
+ /*0x1f0f*/ 722,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1,
+ /*0x1f0b*/ 718,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1,
+ /*0x16e55*/ 1440,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ /*0x16e45*/ 1424,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1,
+ /*0x16e59*/ 1444,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ /*0xa77e*/ 1104,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1,
+ /*0x16e49*/ 1428,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1,
+ /*0x16e43*/ 1422,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1,
+ /*0x16e4b*/ 1430,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ /*0x16e5e*/ 1449,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1,
+ /*0x16e51*/ 1436,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1,
+ /*0x16e5c*/ 1447,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1,
+ /*0x16e4d*/ 1432,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1,
+ /*0x16e41*/ 1420,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1,
+ /*0xab74*/ 1146,
+ -1, -1, -1, -1,
+ /*0xab7c*/ 1154,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ /*0xab72*/ 1144,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ /*0xab76*/ 1148,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1,
+ /*0xab7a*/ 1152,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1,
+ /*0xab78*/ 1150,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ /*0xab7e*/ 1156
+ };
+
+ if (code <= MAX_CODE_VALUE && code >= MIN_CODE_VALUE)
+ {
+ register unsigned int key = onigenc_unicode_CaseFold_11_hash(code);
+
+ if (key <= MAX_HASH_VALUE)
+ {
+ register short s = wordlist[key];
+
+ if (s >= 0 && code1_equal(code, CaseFold_11_Table[s].from))
+ return &CaseFold_11_Table[s].to;
+ }
+ }
+ return 0;
+}
+
+static const CaseUnfold_11_Type CaseUnfold_11_Table[] = {
+#define CaseUnfold_11 (*(CaseUnfold_11_Type (*)[1352])(CaseUnfold_11_Table+0))
+ {0x0061, {1|U, {0x0041}}},
+ {0x0062, {1|U, {0x0042}}},
+ {0x0063, {1|U, {0x0043}}},
+ {0x0064, {1|U, {0x0044}}},
+ {0x0065, {1|U, {0x0045}}},
+ {0x0066, {1|U, {0x0046}}},
+ {0x0067, {1|U, {0x0047}}},
+ {0x0068, {1|U, {0x0048}}},
+ {0x006a, {1|U, {0x004a}}},
+ {0x006b, {2|U, {0x004b, 0x212a}}},
+ {0x006c, {1|U, {0x004c}}},
+ {0x006d, {1|U, {0x004d}}},
+ {0x006e, {1|U, {0x004e}}},
+ {0x006f, {1|U, {0x004f}}},
+ {0x0070, {1|U, {0x0050}}},
+ {0x0071, {1|U, {0x0051}}},
+ {0x0072, {1|U, {0x0052}}},
+ {0x0073, {2|U, {0x0053, 0x017f}}},
+ {0x0074, {1|U, {0x0054}}},
+ {0x0075, {1|U, {0x0055}}},
+ {0x0076, {1|U, {0x0056}}},
+ {0x0077, {1|U, {0x0057}}},
+ {0x0078, {1|U, {0x0058}}},
+ {0x0079, {1|U, {0x0059}}},
+ {0x007a, {1|U, {0x005a}}},
+ {0x00e0, {1|U, {0x00c0}}},
+ {0x00e1, {1|U, {0x00c1}}},
+ {0x00e2, {1|U, {0x00c2}}},
+ {0x00e3, {1|U, {0x00c3}}},
+ {0x00e4, {1|U, {0x00c4}}},
+ {0x00e5, {2|U, {0x00c5, 0x212b}}},
+ {0x00e6, {1|U, {0x00c6}}},
+ {0x00e7, {1|U, {0x00c7}}},
+ {0x00e8, {1|U, {0x00c8}}},
+ {0x00e9, {1|U, {0x00c9}}},
+ {0x00ea, {1|U, {0x00ca}}},
+ {0x00eb, {1|U, {0x00cb}}},
+ {0x00ec, {1|U, {0x00cc}}},
+ {0x00ed, {1|U, {0x00cd}}},
+ {0x00ee, {1|U, {0x00ce}}},
+ {0x00ef, {1|U, {0x00cf}}},
+ {0x00f0, {1|U, {0x00d0}}},
+ {0x00f1, {1|U, {0x00d1}}},
+ {0x00f2, {1|U, {0x00d2}}},
+ {0x00f3, {1|U, {0x00d3}}},
+ {0x00f4, {1|U, {0x00d4}}},
+ {0x00f5, {1|U, {0x00d5}}},
+ {0x00f6, {1|U, {0x00d6}}},
+ {0x00f8, {1|U, {0x00d8}}},
+ {0x00f9, {1|U, {0x00d9}}},
+ {0x00fa, {1|U, {0x00da}}},
+ {0x00fb, {1|U, {0x00db}}},
+ {0x00fc, {1|U, {0x00dc}}},
+ {0x00fd, {1|U, {0x00dd}}},
+ {0x00fe, {1|U, {0x00de}}},
+ {0x00ff, {1|U, {0x0178}}},
+ {0x0101, {1|U, {0x0100}}},
+ {0x0103, {1|U, {0x0102}}},
+ {0x0105, {1|U, {0x0104}}},
+ {0x0107, {1|U, {0x0106}}},
+ {0x0109, {1|U, {0x0108}}},
+ {0x010b, {1|U, {0x010a}}},
+ {0x010d, {1|U, {0x010c}}},
+ {0x010f, {1|U, {0x010e}}},
+ {0x0111, {1|U, {0x0110}}},
+ {0x0113, {1|U, {0x0112}}},
+ {0x0115, {1|U, {0x0114}}},
+ {0x0117, {1|U, {0x0116}}},
+ {0x0119, {1|U, {0x0118}}},
+ {0x011b, {1|U, {0x011a}}},
+ {0x011d, {1|U, {0x011c}}},
+ {0x011f, {1|U, {0x011e}}},
+ {0x0121, {1|U, {0x0120}}},
+ {0x0123, {1|U, {0x0122}}},
+ {0x0125, {1|U, {0x0124}}},
+ {0x0127, {1|U, {0x0126}}},
+ {0x0129, {1|U, {0x0128}}},
+ {0x012b, {1|U, {0x012a}}},
+ {0x012d, {1|U, {0x012c}}},
+ {0x012f, {1|U, {0x012e}}},
+ {0x0133, {1|U, {0x0132}}},
+ {0x0135, {1|U, {0x0134}}},
+ {0x0137, {1|U, {0x0136}}},
+ {0x013a, {1|U, {0x0139}}},
+ {0x013c, {1|U, {0x013b}}},
+ {0x013e, {1|U, {0x013d}}},
+ {0x0140, {1|U, {0x013f}}},
+ {0x0142, {1|U, {0x0141}}},
+ {0x0144, {1|U, {0x0143}}},
+ {0x0146, {1|U, {0x0145}}},
+ {0x0148, {1|U, {0x0147}}},
+ {0x014b, {1|U, {0x014a}}},
+ {0x014d, {1|U, {0x014c}}},
+ {0x014f, {1|U, {0x014e}}},
+ {0x0151, {1|U, {0x0150}}},
+ {0x0153, {1|U, {0x0152}}},
+ {0x0155, {1|U, {0x0154}}},
+ {0x0157, {1|U, {0x0156}}},
+ {0x0159, {1|U, {0x0158}}},
+ {0x015b, {1|U, {0x015a}}},
+ {0x015d, {1|U, {0x015c}}},
+ {0x015f, {1|U, {0x015e}}},
+ {0x0161, {1|U, {0x0160}}},
+ {0x0163, {1|U, {0x0162}}},
+ {0x0165, {1|U, {0x0164}}},
+ {0x0167, {1|U, {0x0166}}},
+ {0x0169, {1|U, {0x0168}}},
+ {0x016b, {1|U, {0x016a}}},
+ {0x016d, {1|U, {0x016c}}},
+ {0x016f, {1|U, {0x016e}}},
+ {0x0171, {1|U, {0x0170}}},
+ {0x0173, {1|U, {0x0172}}},
+ {0x0175, {1|U, {0x0174}}},
+ {0x0177, {1|U, {0x0176}}},
+ {0x017a, {1|U, {0x0179}}},
+ {0x017c, {1|U, {0x017b}}},
+ {0x017e, {1|U, {0x017d}}},
+ {0x0180, {1|U, {0x0243}}},
+ {0x0183, {1|U, {0x0182}}},
+ {0x0185, {1|U, {0x0184}}},
+ {0x0188, {1|U, {0x0187}}},
+ {0x018c, {1|U, {0x018b}}},
+ {0x0192, {1|U, {0x0191}}},
+ {0x0195, {1|U, {0x01f6}}},
+ {0x0199, {1|U, {0x0198}}},
+ {0x019a, {1|U, {0x023d}}},
+ {0x019e, {1|U, {0x0220}}},
+ {0x01a1, {1|U, {0x01a0}}},
+ {0x01a3, {1|U, {0x01a2}}},
+ {0x01a5, {1|U, {0x01a4}}},
+ {0x01a8, {1|U, {0x01a7}}},
+ {0x01ad, {1|U, {0x01ac}}},
+ {0x01b0, {1|U, {0x01af}}},
+ {0x01b4, {1|U, {0x01b3}}},
+ {0x01b6, {1|U, {0x01b5}}},
+ {0x01b9, {1|U, {0x01b8}}},
+ {0x01bd, {1|U, {0x01bc}}},
+ {0x01bf, {1|U, {0x01f7}}},
+ {0x01c6, {2|U|ST, {0x01c4, 0x01c5}}},
+ {0x01c9, {2|U|ST, {0x01c7, 0x01c8}}},
+ {0x01cc, {2|U|ST, {0x01ca, 0x01cb}}},
+ {0x01ce, {1|U, {0x01cd}}},
+ {0x01d0, {1|U, {0x01cf}}},
+ {0x01d2, {1|U, {0x01d1}}},
+ {0x01d4, {1|U, {0x01d3}}},
+ {0x01d6, {1|U, {0x01d5}}},
+ {0x01d8, {1|U, {0x01d7}}},
+ {0x01da, {1|U, {0x01d9}}},
+ {0x01dc, {1|U, {0x01db}}},
+ {0x01dd, {1|U, {0x018e}}},
+ {0x01df, {1|U, {0x01de}}},
+ {0x01e1, {1|U, {0x01e0}}},
+ {0x01e3, {1|U, {0x01e2}}},
+ {0x01e5, {1|U, {0x01e4}}},
+ {0x01e7, {1|U, {0x01e6}}},
+ {0x01e9, {1|U, {0x01e8}}},
+ {0x01eb, {1|U, {0x01ea}}},
+ {0x01ed, {1|U, {0x01ec}}},
+ {0x01ef, {1|U, {0x01ee}}},
+ {0x01f3, {2|U|ST, {0x01f1, 0x01f2}}},
+ {0x01f5, {1|U, {0x01f4}}},
+ {0x01f9, {1|U, {0x01f8}}},
+ {0x01fb, {1|U, {0x01fa}}},
+ {0x01fd, {1|U, {0x01fc}}},
+ {0x01ff, {1|U, {0x01fe}}},
+ {0x0201, {1|U, {0x0200}}},
+ {0x0203, {1|U, {0x0202}}},
+ {0x0205, {1|U, {0x0204}}},
+ {0x0207, {1|U, {0x0206}}},
+ {0x0209, {1|U, {0x0208}}},
+ {0x020b, {1|U, {0x020a}}},
+ {0x020d, {1|U, {0x020c}}},
+ {0x020f, {1|U, {0x020e}}},
+ {0x0211, {1|U, {0x0210}}},
+ {0x0213, {1|U, {0x0212}}},
+ {0x0215, {1|U, {0x0214}}},
+ {0x0217, {1|U, {0x0216}}},
+ {0x0219, {1|U, {0x0218}}},
+ {0x021b, {1|U, {0x021a}}},
+ {0x021d, {1|U, {0x021c}}},
+ {0x021f, {1|U, {0x021e}}},
+ {0x0223, {1|U, {0x0222}}},
+ {0x0225, {1|U, {0x0224}}},
+ {0x0227, {1|U, {0x0226}}},
+ {0x0229, {1|U, {0x0228}}},
+ {0x022b, {1|U, {0x022a}}},
+ {0x022d, {1|U, {0x022c}}},
+ {0x022f, {1|U, {0x022e}}},
+ {0x0231, {1|U, {0x0230}}},
+ {0x0233, {1|U, {0x0232}}},
+ {0x023c, {1|U, {0x023b}}},
+ {0x023f, {1|U, {0x2c7e}}},
+ {0x0240, {1|U, {0x2c7f}}},
+ {0x0242, {1|U, {0x0241}}},
+ {0x0247, {1|U, {0x0246}}},
+ {0x0249, {1|U, {0x0248}}},
+ {0x024b, {1|U, {0x024a}}},
+ {0x024d, {1|U, {0x024c}}},
+ {0x024f, {1|U, {0x024e}}},
+ {0x0250, {1|U, {0x2c6f}}},
+ {0x0251, {1|U, {0x2c6d}}},
+ {0x0252, {1|U, {0x2c70}}},
+ {0x0253, {1|U, {0x0181}}},
+ {0x0254, {1|U, {0x0186}}},
+ {0x0256, {1|U, {0x0189}}},
+ {0x0257, {1|U, {0x018a}}},
+ {0x0259, {1|U, {0x018f}}},
+ {0x025b, {1|U, {0x0190}}},
+ {0x025c, {1|U, {0xa7ab}}},
+ {0x0260, {1|U, {0x0193}}},
+ {0x0261, {1|U, {0xa7ac}}},
+ {0x0263, {1|U, {0x0194}}},
+ {0x0265, {1|U, {0xa78d}}},
+ {0x0266, {1|U, {0xa7aa}}},
+ {0x0268, {1|U, {0x0197}}},
+ {0x0269, {1|U, {0x0196}}},
+ {0x026a, {1|U, {0xa7ae}}},
+ {0x026b, {1|U, {0x2c62}}},
+ {0x026c, {1|U, {0xa7ad}}},
+ {0x026f, {1|U, {0x019c}}},
+ {0x0271, {1|U, {0x2c6e}}},
+ {0x0272, {1|U, {0x019d}}},
+ {0x0275, {1|U, {0x019f}}},
+ {0x027d, {1|U, {0x2c64}}},
+ {0x0280, {1|U, {0x01a6}}},
+ {0x0282, {1|U, {0xa7c5}}},
+ {0x0283, {1|U, {0x01a9}}},
+ {0x0287, {1|U, {0xa7b1}}},
+ {0x0288, {1|U, {0x01ae}}},
+ {0x0289, {1|U, {0x0244}}},
+ {0x028a, {1|U, {0x01b1}}},
+ {0x028b, {1|U, {0x01b2}}},
+ {0x028c, {1|U, {0x0245}}},
+ {0x0292, {1|U, {0x01b7}}},
+ {0x029d, {1|U, {0xa7b2}}},
+ {0x029e, {1|U, {0xa7b0}}},
+ {0x0371, {1|U, {0x0370}}},
+ {0x0373, {1|U, {0x0372}}},
+ {0x0377, {1|U, {0x0376}}},
+ {0x037b, {1|U, {0x03fd}}},
+ {0x037c, {1|U, {0x03fe}}},
+ {0x037d, {1|U, {0x03ff}}},
+ {0x03ac, {1|U, {0x0386}}},
+ {0x03ad, {1|U, {0x0388}}},
+ {0x03ae, {1|U, {0x0389}}},
+ {0x03af, {1|U, {0x038a}}},
+ {0x03b1, {1|U, {0x0391}}},
+ {0x03b2, {2|U, {0x0392, 0x03d0}}},
+ {0x03b3, {1|U, {0x0393}}},
+ {0x03b4, {1|U, {0x0394}}},
+ {0x03b5, {2|U, {0x0395, 0x03f5}}},
+ {0x03b6, {1|U, {0x0396}}},
+ {0x03b7, {1|U, {0x0397}}},
+ {0x03b8, {3|U, {0x0398, 0x03d1, 0x03f4}}},
+ {0x03b9, {3|U, {0x0399, 0x0345, 0x1fbe}}},
+ {0x03ba, {2|U, {0x039a, 0x03f0}}},
+ {0x03bb, {1|U, {0x039b}}},
+ {0x03bc, {2|U, {0x039c, 0x00b5}}},
+ {0x03bd, {1|U, {0x039d}}},
+ {0x03be, {1|U, {0x039e}}},
+ {0x03bf, {1|U, {0x039f}}},
+ {0x03c0, {2|U, {0x03a0, 0x03d6}}},
+ {0x03c1, {2|U, {0x03a1, 0x03f1}}},
+ {0x03c3, {2|U, {0x03a3, 0x03c2}}},
+ {0x03c4, {1|U, {0x03a4}}},
+ {0x03c5, {1|U, {0x03a5}}},
+ {0x03c6, {2|U, {0x03a6, 0x03d5}}},
+ {0x03c7, {1|U, {0x03a7}}},
+ {0x03c8, {1|U, {0x03a8}}},
+ {0x03c9, {2|U, {0x03a9, 0x2126}}},
+ {0x03ca, {1|U, {0x03aa}}},
+ {0x03cb, {1|U, {0x03ab}}},
+ {0x03cc, {1|U, {0x038c}}},
+ {0x03cd, {1|U, {0x038e}}},
+ {0x03ce, {1|U, {0x038f}}},
+ {0x03d7, {1|U, {0x03cf}}},
+ {0x03d9, {1|U, {0x03d8}}},
+ {0x03db, {1|U, {0x03da}}},
+ {0x03dd, {1|U, {0x03dc}}},
+ {0x03df, {1|U, {0x03de}}},
+ {0x03e1, {1|U, {0x03e0}}},
+ {0x03e3, {1|U, {0x03e2}}},
+ {0x03e5, {1|U, {0x03e4}}},
+ {0x03e7, {1|U, {0x03e6}}},
+ {0x03e9, {1|U, {0x03e8}}},
+ {0x03eb, {1|U, {0x03ea}}},
+ {0x03ed, {1|U, {0x03ec}}},
+ {0x03ef, {1|U, {0x03ee}}},
+ {0x03f2, {1|U, {0x03f9}}},
+ {0x03f3, {1|U, {0x037f}}},
+ {0x03f8, {1|U, {0x03f7}}},
+ {0x03fb, {1|U, {0x03fa}}},
+ {0x0430, {1|U, {0x0410}}},
+ {0x0431, {1|U, {0x0411}}},
+ {0x0432, {2|U, {0x0412, 0x1c80}}},
+ {0x0433, {1|U, {0x0413}}},
+ {0x0434, {2|U, {0x0414, 0x1c81}}},
+ {0x0435, {1|U, {0x0415}}},
+ {0x0436, {1|U, {0x0416}}},
+ {0x0437, {1|U, {0x0417}}},
+ {0x0438, {1|U, {0x0418}}},
+ {0x0439, {1|U, {0x0419}}},
+ {0x043a, {1|U, {0x041a}}},
+ {0x043b, {1|U, {0x041b}}},
+ {0x043c, {1|U, {0x041c}}},
+ {0x043d, {1|U, {0x041d}}},
+ {0x043e, {2|U, {0x041e, 0x1c82}}},
+ {0x043f, {1|U, {0x041f}}},
+ {0x0440, {1|U, {0x0420}}},
+ {0x0441, {2|U, {0x0421, 0x1c83}}},
+ {0x0442, {3|U, {0x0422, 0x1c84, 0x1c85}}},
+ {0x0443, {1|U, {0x0423}}},
+ {0x0444, {1|U, {0x0424}}},
+ {0x0445, {1|U, {0x0425}}},
+ {0x0446, {1|U, {0x0426}}},
+ {0x0447, {1|U, {0x0427}}},
+ {0x0448, {1|U, {0x0428}}},
+ {0x0449, {1|U, {0x0429}}},
+ {0x044a, {2|U, {0x042a, 0x1c86}}},
+ {0x044b, {1|U, {0x042b}}},
+ {0x044c, {1|U, {0x042c}}},
+ {0x044d, {1|U, {0x042d}}},
+ {0x044e, {1|U, {0x042e}}},
+ {0x044f, {1|U, {0x042f}}},
+ {0x0450, {1|U, {0x0400}}},
+ {0x0451, {1|U, {0x0401}}},
+ {0x0452, {1|U, {0x0402}}},
+ {0x0453, {1|U, {0x0403}}},
+ {0x0454, {1|U, {0x0404}}},
+ {0x0455, {1|U, {0x0405}}},
+ {0x0456, {1|U, {0x0406}}},
+ {0x0457, {1|U, {0x0407}}},
+ {0x0458, {1|U, {0x0408}}},
+ {0x0459, {1|U, {0x0409}}},
+ {0x045a, {1|U, {0x040a}}},
+ {0x045b, {1|U, {0x040b}}},
+ {0x045c, {1|U, {0x040c}}},
+ {0x045d, {1|U, {0x040d}}},
+ {0x045e, {1|U, {0x040e}}},
+ {0x045f, {1|U, {0x040f}}},
+ {0x0461, {1|U, {0x0460}}},
+ {0x0463, {2|U, {0x0462, 0x1c87}}},
+ {0x0465, {1|U, {0x0464}}},
+ {0x0467, {1|U, {0x0466}}},
+ {0x0469, {1|U, {0x0468}}},
+ {0x046b, {1|U, {0x046a}}},
+ {0x046d, {1|U, {0x046c}}},
+ {0x046f, {1|U, {0x046e}}},
+ {0x0471, {1|U, {0x0470}}},
+ {0x0473, {1|U, {0x0472}}},
+ {0x0475, {1|U, {0x0474}}},
+ {0x0477, {1|U, {0x0476}}},
+ {0x0479, {1|U, {0x0478}}},
+ {0x047b, {1|U, {0x047a}}},
+ {0x047d, {1|U, {0x047c}}},
+ {0x047f, {1|U, {0x047e}}},
+ {0x0481, {1|U, {0x0480}}},
+ {0x048b, {1|U, {0x048a}}},
+ {0x048d, {1|U, {0x048c}}},
+ {0x048f, {1|U, {0x048e}}},
+ {0x0491, {1|U, {0x0490}}},
+ {0x0493, {1|U, {0x0492}}},
+ {0x0495, {1|U, {0x0494}}},
+ {0x0497, {1|U, {0x0496}}},
+ {0x0499, {1|U, {0x0498}}},
+ {0x049b, {1|U, {0x049a}}},
+ {0x049d, {1|U, {0x049c}}},
+ {0x049f, {1|U, {0x049e}}},
+ {0x04a1, {1|U, {0x04a0}}},
+ {0x04a3, {1|U, {0x04a2}}},
+ {0x04a5, {1|U, {0x04a4}}},
+ {0x04a7, {1|U, {0x04a6}}},
+ {0x04a9, {1|U, {0x04a8}}},
+ {0x04ab, {1|U, {0x04aa}}},
+ {0x04ad, {1|U, {0x04ac}}},
+ {0x04af, {1|U, {0x04ae}}},
+ {0x04b1, {1|U, {0x04b0}}},
+ {0x04b3, {1|U, {0x04b2}}},
+ {0x04b5, {1|U, {0x04b4}}},
+ {0x04b7, {1|U, {0x04b6}}},
+ {0x04b9, {1|U, {0x04b8}}},
+ {0x04bb, {1|U, {0x04ba}}},
+ {0x04bd, {1|U, {0x04bc}}},
+ {0x04bf, {1|U, {0x04be}}},
+ {0x04c2, {1|U, {0x04c1}}},
+ {0x04c4, {1|U, {0x04c3}}},
+ {0x04c6, {1|U, {0x04c5}}},
+ {0x04c8, {1|U, {0x04c7}}},
+ {0x04ca, {1|U, {0x04c9}}},
+ {0x04cc, {1|U, {0x04cb}}},
+ {0x04ce, {1|U, {0x04cd}}},
+ {0x04cf, {1|U, {0x04c0}}},
+ {0x04d1, {1|U, {0x04d0}}},
+ {0x04d3, {1|U, {0x04d2}}},
+ {0x04d5, {1|U, {0x04d4}}},
+ {0x04d7, {1|U, {0x04d6}}},
+ {0x04d9, {1|U, {0x04d8}}},
+ {0x04db, {1|U, {0x04da}}},
+ {0x04dd, {1|U, {0x04dc}}},
+ {0x04df, {1|U, {0x04de}}},
+ {0x04e1, {1|U, {0x04e0}}},
+ {0x04e3, {1|U, {0x04e2}}},
+ {0x04e5, {1|U, {0x04e4}}},
+ {0x04e7, {1|U, {0x04e6}}},
+ {0x04e9, {1|U, {0x04e8}}},
+ {0x04eb, {1|U, {0x04ea}}},
+ {0x04ed, {1|U, {0x04ec}}},
+ {0x04ef, {1|U, {0x04ee}}},
+ {0x04f1, {1|U, {0x04f0}}},
+ {0x04f3, {1|U, {0x04f2}}},
+ {0x04f5, {1|U, {0x04f4}}},
+ {0x04f7, {1|U, {0x04f6}}},
+ {0x04f9, {1|U, {0x04f8}}},
+ {0x04fb, {1|U, {0x04fa}}},
+ {0x04fd, {1|U, {0x04fc}}},
+ {0x04ff, {1|U, {0x04fe}}},
+ {0x0501, {1|U, {0x0500}}},
+ {0x0503, {1|U, {0x0502}}},
+ {0x0505, {1|U, {0x0504}}},
+ {0x0507, {1|U, {0x0506}}},
+ {0x0509, {1|U, {0x0508}}},
+ {0x050b, {1|U, {0x050a}}},
+ {0x050d, {1|U, {0x050c}}},
+ {0x050f, {1|U, {0x050e}}},
+ {0x0511, {1|U, {0x0510}}},
+ {0x0513, {1|U, {0x0512}}},
+ {0x0515, {1|U, {0x0514}}},
+ {0x0517, {1|U, {0x0516}}},
+ {0x0519, {1|U, {0x0518}}},
+ {0x051b, {1|U, {0x051a}}},
+ {0x051d, {1|U, {0x051c}}},
+ {0x051f, {1|U, {0x051e}}},
+ {0x0521, {1|U, {0x0520}}},
+ {0x0523, {1|U, {0x0522}}},
+ {0x0525, {1|U, {0x0524}}},
+ {0x0527, {1|U, {0x0526}}},
+ {0x0529, {1|U, {0x0528}}},
+ {0x052b, {1|U, {0x052a}}},
+ {0x052d, {1|U, {0x052c}}},
+ {0x052f, {1|U, {0x052e}}},
+ {0x0561, {1|U, {0x0531}}},
+ {0x0562, {1|U, {0x0532}}},
+ {0x0563, {1|U, {0x0533}}},
+ {0x0564, {1|U, {0x0534}}},
+ {0x0565, {1|U, {0x0535}}},
+ {0x0566, {1|U, {0x0536}}},
+ {0x0567, {1|U, {0x0537}}},
+ {0x0568, {1|U, {0x0538}}},
+ {0x0569, {1|U, {0x0539}}},
+ {0x056a, {1|U, {0x053a}}},
+ {0x056b, {1|U, {0x053b}}},
+ {0x056c, {1|U, {0x053c}}},
+ {0x056d, {1|U, {0x053d}}},
+ {0x056e, {1|U, {0x053e}}},
+ {0x056f, {1|U, {0x053f}}},
+ {0x0570, {1|U, {0x0540}}},
+ {0x0571, {1|U, {0x0541}}},
+ {0x0572, {1|U, {0x0542}}},
+ {0x0573, {1|U, {0x0543}}},
+ {0x0574, {1|U, {0x0544}}},
+ {0x0575, {1|U, {0x0545}}},
+ {0x0576, {1|U, {0x0546}}},
+ {0x0577, {1|U, {0x0547}}},
+ {0x0578, {1|U, {0x0548}}},
+ {0x0579, {1|U, {0x0549}}},
+ {0x057a, {1|U, {0x054a}}},
+ {0x057b, {1|U, {0x054b}}},
+ {0x057c, {1|U, {0x054c}}},
+ {0x057d, {1|U, {0x054d}}},
+ {0x057e, {1|U, {0x054e}}},
+ {0x057f, {1|U, {0x054f}}},
+ {0x0580, {1|U, {0x0550}}},
+ {0x0581, {1|U, {0x0551}}},
+ {0x0582, {1|U, {0x0552}}},
+ {0x0583, {1|U, {0x0553}}},
+ {0x0584, {1|U, {0x0554}}},
+ {0x0585, {1|U, {0x0555}}},
+ {0x0586, {1|U, {0x0556}}},
+ {0x10d0, {1|U|IT, {0x1c90}}},
+ {0x10d1, {1|U|IT, {0x1c91}}},
+ {0x10d2, {1|U|IT, {0x1c92}}},
+ {0x10d3, {1|U|IT, {0x1c93}}},
+ {0x10d4, {1|U|IT, {0x1c94}}},
+ {0x10d5, {1|U|IT, {0x1c95}}},
+ {0x10d6, {1|U|IT, {0x1c96}}},
+ {0x10d7, {1|U|IT, {0x1c97}}},
+ {0x10d8, {1|U|IT, {0x1c98}}},
+ {0x10d9, {1|U|IT, {0x1c99}}},
+ {0x10da, {1|U|IT, {0x1c9a}}},
+ {0x10db, {1|U|IT, {0x1c9b}}},
+ {0x10dc, {1|U|IT, {0x1c9c}}},
+ {0x10dd, {1|U|IT, {0x1c9d}}},
+ {0x10de, {1|U|IT, {0x1c9e}}},
+ {0x10df, {1|U|IT, {0x1c9f}}},
+ {0x10e0, {1|U|IT, {0x1ca0}}},
+ {0x10e1, {1|U|IT, {0x1ca1}}},
+ {0x10e2, {1|U|IT, {0x1ca2}}},
+ {0x10e3, {1|U|IT, {0x1ca3}}},
+ {0x10e4, {1|U|IT, {0x1ca4}}},
+ {0x10e5, {1|U|IT, {0x1ca5}}},
+ {0x10e6, {1|U|IT, {0x1ca6}}},
+ {0x10e7, {1|U|IT, {0x1ca7}}},
+ {0x10e8, {1|U|IT, {0x1ca8}}},
+ {0x10e9, {1|U|IT, {0x1ca9}}},
+ {0x10ea, {1|U|IT, {0x1caa}}},
+ {0x10eb, {1|U|IT, {0x1cab}}},
+ {0x10ec, {1|U|IT, {0x1cac}}},
+ {0x10ed, {1|U|IT, {0x1cad}}},
+ {0x10ee, {1|U|IT, {0x1cae}}},
+ {0x10ef, {1|U|IT, {0x1caf}}},
+ {0x10f0, {1|U|IT, {0x1cb0}}},
+ {0x10f1, {1|U|IT, {0x1cb1}}},
+ {0x10f2, {1|U|IT, {0x1cb2}}},
+ {0x10f3, {1|U|IT, {0x1cb3}}},
+ {0x10f4, {1|U|IT, {0x1cb4}}},
+ {0x10f5, {1|U|IT, {0x1cb5}}},
+ {0x10f6, {1|U|IT, {0x1cb6}}},
+ {0x10f7, {1|U|IT, {0x1cb7}}},
+ {0x10f8, {1|U|IT, {0x1cb8}}},
+ {0x10f9, {1|U|IT, {0x1cb9}}},
+ {0x10fa, {1|U|IT, {0x1cba}}},
+ {0x10fd, {1|U|IT, {0x1cbd}}},
+ {0x10fe, {1|U|IT, {0x1cbe}}},
+ {0x10ff, {1|U|IT, {0x1cbf}}},
+ {0x13a0, {1|D, {0xab70}}},
+ {0x13a1, {1|D, {0xab71}}},
+ {0x13a2, {1|D, {0xab72}}},
+ {0x13a3, {1|D, {0xab73}}},
+ {0x13a4, {1|D, {0xab74}}},
+ {0x13a5, {1|D, {0xab75}}},
+ {0x13a6, {1|D, {0xab76}}},
+ {0x13a7, {1|D, {0xab77}}},
+ {0x13a8, {1|D, {0xab78}}},
+ {0x13a9, {1|D, {0xab79}}},
+ {0x13aa, {1|D, {0xab7a}}},
+ {0x13ab, {1|D, {0xab7b}}},
+ {0x13ac, {1|D, {0xab7c}}},
+ {0x13ad, {1|D, {0xab7d}}},
+ {0x13ae, {1|D, {0xab7e}}},
+ {0x13af, {1|D, {0xab7f}}},
+ {0x13b0, {1|D, {0xab80}}},
+ {0x13b1, {1|D, {0xab81}}},
+ {0x13b2, {1|D, {0xab82}}},
+ {0x13b3, {1|D, {0xab83}}},
+ {0x13b4, {1|D, {0xab84}}},
+ {0x13b5, {1|D, {0xab85}}},
+ {0x13b6, {1|D, {0xab86}}},
+ {0x13b7, {1|D, {0xab87}}},
+ {0x13b8, {1|D, {0xab88}}},
+ {0x13b9, {1|D, {0xab89}}},
+ {0x13ba, {1|D, {0xab8a}}},
+ {0x13bb, {1|D, {0xab8b}}},
+ {0x13bc, {1|D, {0xab8c}}},
+ {0x13bd, {1|D, {0xab8d}}},
+ {0x13be, {1|D, {0xab8e}}},
+ {0x13bf, {1|D, {0xab8f}}},
+ {0x13c0, {1|D, {0xab90}}},
+ {0x13c1, {1|D, {0xab91}}},
+ {0x13c2, {1|D, {0xab92}}},
+ {0x13c3, {1|D, {0xab93}}},
+ {0x13c4, {1|D, {0xab94}}},
+ {0x13c5, {1|D, {0xab95}}},
+ {0x13c6, {1|D, {0xab96}}},
+ {0x13c7, {1|D, {0xab97}}},
+ {0x13c8, {1|D, {0xab98}}},
+ {0x13c9, {1|D, {0xab99}}},
+ {0x13ca, {1|D, {0xab9a}}},
+ {0x13cb, {1|D, {0xab9b}}},
+ {0x13cc, {1|D, {0xab9c}}},
+ {0x13cd, {1|D, {0xab9d}}},
+ {0x13ce, {1|D, {0xab9e}}},
+ {0x13cf, {1|D, {0xab9f}}},
+ {0x13d0, {1|D, {0xaba0}}},
+ {0x13d1, {1|D, {0xaba1}}},
+ {0x13d2, {1|D, {0xaba2}}},
+ {0x13d3, {1|D, {0xaba3}}},
+ {0x13d4, {1|D, {0xaba4}}},
+ {0x13d5, {1|D, {0xaba5}}},
+ {0x13d6, {1|D, {0xaba6}}},
+ {0x13d7, {1|D, {0xaba7}}},
+ {0x13d8, {1|D, {0xaba8}}},
+ {0x13d9, {1|D, {0xaba9}}},
+ {0x13da, {1|D, {0xabaa}}},
+ {0x13db, {1|D, {0xabab}}},
+ {0x13dc, {1|D, {0xabac}}},
+ {0x13dd, {1|D, {0xabad}}},
+ {0x13de, {1|D, {0xabae}}},
+ {0x13df, {1|D, {0xabaf}}},
+ {0x13e0, {1|D, {0xabb0}}},
+ {0x13e1, {1|D, {0xabb1}}},
+ {0x13e2, {1|D, {0xabb2}}},
+ {0x13e3, {1|D, {0xabb3}}},
+ {0x13e4, {1|D, {0xabb4}}},
+ {0x13e5, {1|D, {0xabb5}}},
+ {0x13e6, {1|D, {0xabb6}}},
+ {0x13e7, {1|D, {0xabb7}}},
+ {0x13e8, {1|D, {0xabb8}}},
+ {0x13e9, {1|D, {0xabb9}}},
+ {0x13ea, {1|D, {0xabba}}},
+ {0x13eb, {1|D, {0xabbb}}},
+ {0x13ec, {1|D, {0xabbc}}},
+ {0x13ed, {1|D, {0xabbd}}},
+ {0x13ee, {1|D, {0xabbe}}},
+ {0x13ef, {1|D, {0xabbf}}},
+ {0x13f0, {1|D, {0x13f8}}},
+ {0x13f1, {1|D, {0x13f9}}},
+ {0x13f2, {1|D, {0x13fa}}},
+ {0x13f3, {1|D, {0x13fb}}},
+ {0x13f4, {1|D, {0x13fc}}},
+ {0x13f5, {1|D, {0x13fd}}},
+ {0x1d79, {1|U, {0xa77d}}},
+ {0x1d7d, {1|U, {0x2c63}}},
+ {0x1d8e, {1|U, {0xa7c6}}},
+ {0x1e01, {1|U, {0x1e00}}},
+ {0x1e03, {1|U, {0x1e02}}},
+ {0x1e05, {1|U, {0x1e04}}},
+ {0x1e07, {1|U, {0x1e06}}},
+ {0x1e09, {1|U, {0x1e08}}},
+ {0x1e0b, {1|U, {0x1e0a}}},
+ {0x1e0d, {1|U, {0x1e0c}}},
+ {0x1e0f, {1|U, {0x1e0e}}},
+ {0x1e11, {1|U, {0x1e10}}},
+ {0x1e13, {1|U, {0x1e12}}},
+ {0x1e15, {1|U, {0x1e14}}},
+ {0x1e17, {1|U, {0x1e16}}},
+ {0x1e19, {1|U, {0x1e18}}},
+ {0x1e1b, {1|U, {0x1e1a}}},
+ {0x1e1d, {1|U, {0x1e1c}}},
+ {0x1e1f, {1|U, {0x1e1e}}},
+ {0x1e21, {1|U, {0x1e20}}},
+ {0x1e23, {1|U, {0x1e22}}},
+ {0x1e25, {1|U, {0x1e24}}},
+ {0x1e27, {1|U, {0x1e26}}},
+ {0x1e29, {1|U, {0x1e28}}},
+ {0x1e2b, {1|U, {0x1e2a}}},
+ {0x1e2d, {1|U, {0x1e2c}}},
+ {0x1e2f, {1|U, {0x1e2e}}},
+ {0x1e31, {1|U, {0x1e30}}},
+ {0x1e33, {1|U, {0x1e32}}},
+ {0x1e35, {1|U, {0x1e34}}},
+ {0x1e37, {1|U, {0x1e36}}},
+ {0x1e39, {1|U, {0x1e38}}},
+ {0x1e3b, {1|U, {0x1e3a}}},
+ {0x1e3d, {1|U, {0x1e3c}}},
+ {0x1e3f, {1|U, {0x1e3e}}},
+ {0x1e41, {1|U, {0x1e40}}},
+ {0x1e43, {1|U, {0x1e42}}},
+ {0x1e45, {1|U, {0x1e44}}},
+ {0x1e47, {1|U, {0x1e46}}},
+ {0x1e49, {1|U, {0x1e48}}},
+ {0x1e4b, {1|U, {0x1e4a}}},
+ {0x1e4d, {1|U, {0x1e4c}}},
+ {0x1e4f, {1|U, {0x1e4e}}},
+ {0x1e51, {1|U, {0x1e50}}},
+ {0x1e53, {1|U, {0x1e52}}},
+ {0x1e55, {1|U, {0x1e54}}},
+ {0x1e57, {1|U, {0x1e56}}},
+ {0x1e59, {1|U, {0x1e58}}},
+ {0x1e5b, {1|U, {0x1e5a}}},
+ {0x1e5d, {1|U, {0x1e5c}}},
+ {0x1e5f, {1|U, {0x1e5e}}},
+ {0x1e61, {2|U, {0x1e60, 0x1e9b}}},
+ {0x1e63, {1|U, {0x1e62}}},
+ {0x1e65, {1|U, {0x1e64}}},
+ {0x1e67, {1|U, {0x1e66}}},
+ {0x1e69, {1|U, {0x1e68}}},
+ {0x1e6b, {1|U, {0x1e6a}}},
+ {0x1e6d, {1|U, {0x1e6c}}},
+ {0x1e6f, {1|U, {0x1e6e}}},
+ {0x1e71, {1|U, {0x1e70}}},
+ {0x1e73, {1|U, {0x1e72}}},
+ {0x1e75, {1|U, {0x1e74}}},
+ {0x1e77, {1|U, {0x1e76}}},
+ {0x1e79, {1|U, {0x1e78}}},
+ {0x1e7b, {1|U, {0x1e7a}}},
+ {0x1e7d, {1|U, {0x1e7c}}},
+ {0x1e7f, {1|U, {0x1e7e}}},
+ {0x1e81, {1|U, {0x1e80}}},
+ {0x1e83, {1|U, {0x1e82}}},
+ {0x1e85, {1|U, {0x1e84}}},
+ {0x1e87, {1|U, {0x1e86}}},
+ {0x1e89, {1|U, {0x1e88}}},
+ {0x1e8b, {1|U, {0x1e8a}}},
+ {0x1e8d, {1|U, {0x1e8c}}},
+ {0x1e8f, {1|U, {0x1e8e}}},
+ {0x1e91, {1|U, {0x1e90}}},
+ {0x1e93, {1|U, {0x1e92}}},
+ {0x1e95, {1|U, {0x1e94}}},
+ {0x1ea1, {1|U, {0x1ea0}}},
+ {0x1ea3, {1|U, {0x1ea2}}},
+ {0x1ea5, {1|U, {0x1ea4}}},
+ {0x1ea7, {1|U, {0x1ea6}}},
+ {0x1ea9, {1|U, {0x1ea8}}},
+ {0x1eab, {1|U, {0x1eaa}}},
+ {0x1ead, {1|U, {0x1eac}}},
+ {0x1eaf, {1|U, {0x1eae}}},
+ {0x1eb1, {1|U, {0x1eb0}}},
+ {0x1eb3, {1|U, {0x1eb2}}},
+ {0x1eb5, {1|U, {0x1eb4}}},
+ {0x1eb7, {1|U, {0x1eb6}}},
+ {0x1eb9, {1|U, {0x1eb8}}},
+ {0x1ebb, {1|U, {0x1eba}}},
+ {0x1ebd, {1|U, {0x1ebc}}},
+ {0x1ebf, {1|U, {0x1ebe}}},
+ {0x1ec1, {1|U, {0x1ec0}}},
+ {0x1ec3, {1|U, {0x1ec2}}},
+ {0x1ec5, {1|U, {0x1ec4}}},
+ {0x1ec7, {1|U, {0x1ec6}}},
+ {0x1ec9, {1|U, {0x1ec8}}},
+ {0x1ecb, {1|U, {0x1eca}}},
+ {0x1ecd, {1|U, {0x1ecc}}},
+ {0x1ecf, {1|U, {0x1ece}}},
+ {0x1ed1, {1|U, {0x1ed0}}},
+ {0x1ed3, {1|U, {0x1ed2}}},
+ {0x1ed5, {1|U, {0x1ed4}}},
+ {0x1ed7, {1|U, {0x1ed6}}},
+ {0x1ed9, {1|U, {0x1ed8}}},
+ {0x1edb, {1|U, {0x1eda}}},
+ {0x1edd, {1|U, {0x1edc}}},
+ {0x1edf, {1|U, {0x1ede}}},
+ {0x1ee1, {1|U, {0x1ee0}}},
+ {0x1ee3, {1|U, {0x1ee2}}},
+ {0x1ee5, {1|U, {0x1ee4}}},
+ {0x1ee7, {1|U, {0x1ee6}}},
+ {0x1ee9, {1|U, {0x1ee8}}},
+ {0x1eeb, {1|U, {0x1eea}}},
+ {0x1eed, {1|U, {0x1eec}}},
+ {0x1eef, {1|U, {0x1eee}}},
+ {0x1ef1, {1|U, {0x1ef0}}},
+ {0x1ef3, {1|U, {0x1ef2}}},
+ {0x1ef5, {1|U, {0x1ef4}}},
+ {0x1ef7, {1|U, {0x1ef6}}},
+ {0x1ef9, {1|U, {0x1ef8}}},
+ {0x1efb, {1|U, {0x1efa}}},
+ {0x1efd, {1|U, {0x1efc}}},
+ {0x1eff, {1|U, {0x1efe}}},
+ {0x1f00, {1|U, {0x1f08}}},
+ {0x1f01, {1|U, {0x1f09}}},
+ {0x1f02, {1|U, {0x1f0a}}},
+ {0x1f03, {1|U, {0x1f0b}}},
+ {0x1f04, {1|U, {0x1f0c}}},
+ {0x1f05, {1|U, {0x1f0d}}},
+ {0x1f06, {1|U, {0x1f0e}}},
+ {0x1f07, {1|U, {0x1f0f}}},
+ {0x1f10, {1|U, {0x1f18}}},
+ {0x1f11, {1|U, {0x1f19}}},
+ {0x1f12, {1|U, {0x1f1a}}},
+ {0x1f13, {1|U, {0x1f1b}}},
+ {0x1f14, {1|U, {0x1f1c}}},
+ {0x1f15, {1|U, {0x1f1d}}},
+ {0x1f20, {1|U, {0x1f28}}},
+ {0x1f21, {1|U, {0x1f29}}},
+ {0x1f22, {1|U, {0x1f2a}}},
+ {0x1f23, {1|U, {0x1f2b}}},
+ {0x1f24, {1|U, {0x1f2c}}},
+ {0x1f25, {1|U, {0x1f2d}}},
+ {0x1f26, {1|U, {0x1f2e}}},
+ {0x1f27, {1|U, {0x1f2f}}},
+ {0x1f30, {1|U, {0x1f38}}},
+ {0x1f31, {1|U, {0x1f39}}},
+ {0x1f32, {1|U, {0x1f3a}}},
+ {0x1f33, {1|U, {0x1f3b}}},
+ {0x1f34, {1|U, {0x1f3c}}},
+ {0x1f35, {1|U, {0x1f3d}}},
+ {0x1f36, {1|U, {0x1f3e}}},
+ {0x1f37, {1|U, {0x1f3f}}},
+ {0x1f40, {1|U, {0x1f48}}},
+ {0x1f41, {1|U, {0x1f49}}},
+ {0x1f42, {1|U, {0x1f4a}}},
+ {0x1f43, {1|U, {0x1f4b}}},
+ {0x1f44, {1|U, {0x1f4c}}},
+ {0x1f45, {1|U, {0x1f4d}}},
+ {0x1f51, {1|U, {0x1f59}}},
+ {0x1f53, {1|U, {0x1f5b}}},
+ {0x1f55, {1|U, {0x1f5d}}},
+ {0x1f57, {1|U, {0x1f5f}}},
+ {0x1f60, {1|U, {0x1f68}}},
+ {0x1f61, {1|U, {0x1f69}}},
+ {0x1f62, {1|U, {0x1f6a}}},
+ {0x1f63, {1|U, {0x1f6b}}},
+ {0x1f64, {1|U, {0x1f6c}}},
+ {0x1f65, {1|U, {0x1f6d}}},
+ {0x1f66, {1|U, {0x1f6e}}},
+ {0x1f67, {1|U, {0x1f6f}}},
+ {0x1f70, {1|U, {0x1fba}}},
+ {0x1f71, {1|U, {0x1fbb}}},
+ {0x1f72, {1|U, {0x1fc8}}},
+ {0x1f73, {1|U, {0x1fc9}}},
+ {0x1f74, {1|U, {0x1fca}}},
+ {0x1f75, {1|U, {0x1fcb}}},
+ {0x1f76, {1|U, {0x1fda}}},
+ {0x1f77, {1|U, {0x1fdb}}},
+ {0x1f78, {1|U, {0x1ff8}}},
+ {0x1f79, {1|U, {0x1ff9}}},
+ {0x1f7a, {1|U, {0x1fea}}},
+ {0x1f7b, {1|U, {0x1feb}}},
+ {0x1f7c, {1|U, {0x1ffa}}},
+ {0x1f7d, {1|U, {0x1ffb}}},
+ {0x1fb0, {1|U, {0x1fb8}}},
+ {0x1fb1, {1|U, {0x1fb9}}},
+ {0x1fd0, {1|U, {0x1fd8}}},
+ {0x1fd1, {1|U, {0x1fd9}}},
+ {0x1fe0, {1|U, {0x1fe8}}},
+ {0x1fe1, {1|U, {0x1fe9}}},
+ {0x1fe5, {1|U, {0x1fec}}},
+ {0x214e, {1|U, {0x2132}}},
+ {0x2170, {1|U, {0x2160}}},
+ {0x2171, {1|U, {0x2161}}},
+ {0x2172, {1|U, {0x2162}}},
+ {0x2173, {1|U, {0x2163}}},
+ {0x2174, {1|U, {0x2164}}},
+ {0x2175, {1|U, {0x2165}}},
+ {0x2176, {1|U, {0x2166}}},
+ {0x2177, {1|U, {0x2167}}},
+ {0x2178, {1|U, {0x2168}}},
+ {0x2179, {1|U, {0x2169}}},
+ {0x217a, {1|U, {0x216a}}},
+ {0x217b, {1|U, {0x216b}}},
+ {0x217c, {1|U, {0x216c}}},
+ {0x217d, {1|U, {0x216d}}},
+ {0x217e, {1|U, {0x216e}}},
+ {0x217f, {1|U, {0x216f}}},
+ {0x2184, {1|U, {0x2183}}},
+ {0x24d0, {1|U, {0x24b6}}},
+ {0x24d1, {1|U, {0x24b7}}},
+ {0x24d2, {1|U, {0x24b8}}},
+ {0x24d3, {1|U, {0x24b9}}},
+ {0x24d4, {1|U, {0x24ba}}},
+ {0x24d5, {1|U, {0x24bb}}},
+ {0x24d6, {1|U, {0x24bc}}},
+ {0x24d7, {1|U, {0x24bd}}},
+ {0x24d8, {1|U, {0x24be}}},
+ {0x24d9, {1|U, {0x24bf}}},
+ {0x24da, {1|U, {0x24c0}}},
+ {0x24db, {1|U, {0x24c1}}},
+ {0x24dc, {1|U, {0x24c2}}},
+ {0x24dd, {1|U, {0x24c3}}},
+ {0x24de, {1|U, {0x24c4}}},
+ {0x24df, {1|U, {0x24c5}}},
+ {0x24e0, {1|U, {0x24c6}}},
+ {0x24e1, {1|U, {0x24c7}}},
+ {0x24e2, {1|U, {0x24c8}}},
+ {0x24e3, {1|U, {0x24c9}}},
+ {0x24e4, {1|U, {0x24ca}}},
+ {0x24e5, {1|U, {0x24cb}}},
+ {0x24e6, {1|U, {0x24cc}}},
+ {0x24e7, {1|U, {0x24cd}}},
+ {0x24e8, {1|U, {0x24ce}}},
+ {0x24e9, {1|U, {0x24cf}}},
+ {0x2c30, {1|U, {0x2c00}}},
+ {0x2c31, {1|U, {0x2c01}}},
+ {0x2c32, {1|U, {0x2c02}}},
+ {0x2c33, {1|U, {0x2c03}}},
+ {0x2c34, {1|U, {0x2c04}}},
+ {0x2c35, {1|U, {0x2c05}}},
+ {0x2c36, {1|U, {0x2c06}}},
+ {0x2c37, {1|U, {0x2c07}}},
+ {0x2c38, {1|U, {0x2c08}}},
+ {0x2c39, {1|U, {0x2c09}}},
+ {0x2c3a, {1|U, {0x2c0a}}},
+ {0x2c3b, {1|U, {0x2c0b}}},
+ {0x2c3c, {1|U, {0x2c0c}}},
+ {0x2c3d, {1|U, {0x2c0d}}},
+ {0x2c3e, {1|U, {0x2c0e}}},
+ {0x2c3f, {1|U, {0x2c0f}}},
+ {0x2c40, {1|U, {0x2c10}}},
+ {0x2c41, {1|U, {0x2c11}}},
+ {0x2c42, {1|U, {0x2c12}}},
+ {0x2c43, {1|U, {0x2c13}}},
+ {0x2c44, {1|U, {0x2c14}}},
+ {0x2c45, {1|U, {0x2c15}}},
+ {0x2c46, {1|U, {0x2c16}}},
+ {0x2c47, {1|U, {0x2c17}}},
+ {0x2c48, {1|U, {0x2c18}}},
+ {0x2c49, {1|U, {0x2c19}}},
+ {0x2c4a, {1|U, {0x2c1a}}},
+ {0x2c4b, {1|U, {0x2c1b}}},
+ {0x2c4c, {1|U, {0x2c1c}}},
+ {0x2c4d, {1|U, {0x2c1d}}},
+ {0x2c4e, {1|U, {0x2c1e}}},
+ {0x2c4f, {1|U, {0x2c1f}}},
+ {0x2c50, {1|U, {0x2c20}}},
+ {0x2c51, {1|U, {0x2c21}}},
+ {0x2c52, {1|U, {0x2c22}}},
+ {0x2c53, {1|U, {0x2c23}}},
+ {0x2c54, {1|U, {0x2c24}}},
+ {0x2c55, {1|U, {0x2c25}}},
+ {0x2c56, {1|U, {0x2c26}}},
+ {0x2c57, {1|U, {0x2c27}}},
+ {0x2c58, {1|U, {0x2c28}}},
+ {0x2c59, {1|U, {0x2c29}}},
+ {0x2c5a, {1|U, {0x2c2a}}},
+ {0x2c5b, {1|U, {0x2c2b}}},
+ {0x2c5c, {1|U, {0x2c2c}}},
+ {0x2c5d, {1|U, {0x2c2d}}},
+ {0x2c5e, {1|U, {0x2c2e}}},
+ {0x2c61, {1|U, {0x2c60}}},
+ {0x2c65, {1|U, {0x023a}}},
+ {0x2c66, {1|U, {0x023e}}},
+ {0x2c68, {1|U, {0x2c67}}},
+ {0x2c6a, {1|U, {0x2c69}}},
+ {0x2c6c, {1|U, {0x2c6b}}},
+ {0x2c73, {1|U, {0x2c72}}},
+ {0x2c76, {1|U, {0x2c75}}},
+ {0x2c81, {1|U, {0x2c80}}},
+ {0x2c83, {1|U, {0x2c82}}},
+ {0x2c85, {1|U, {0x2c84}}},
+ {0x2c87, {1|U, {0x2c86}}},
+ {0x2c89, {1|U, {0x2c88}}},
+ {0x2c8b, {1|U, {0x2c8a}}},
+ {0x2c8d, {1|U, {0x2c8c}}},
+ {0x2c8f, {1|U, {0x2c8e}}},
+ {0x2c91, {1|U, {0x2c90}}},
+ {0x2c93, {1|U, {0x2c92}}},
+ {0x2c95, {1|U, {0x2c94}}},
+ {0x2c97, {1|U, {0x2c96}}},
+ {0x2c99, {1|U, {0x2c98}}},
+ {0x2c9b, {1|U, {0x2c9a}}},
+ {0x2c9d, {1|U, {0x2c9c}}},
+ {0x2c9f, {1|U, {0x2c9e}}},
+ {0x2ca1, {1|U, {0x2ca0}}},
+ {0x2ca3, {1|U, {0x2ca2}}},
+ {0x2ca5, {1|U, {0x2ca4}}},
+ {0x2ca7, {1|U, {0x2ca6}}},
+ {0x2ca9, {1|U, {0x2ca8}}},
+ {0x2cab, {1|U, {0x2caa}}},
+ {0x2cad, {1|U, {0x2cac}}},
+ {0x2caf, {1|U, {0x2cae}}},
+ {0x2cb1, {1|U, {0x2cb0}}},
+ {0x2cb3, {1|U, {0x2cb2}}},
+ {0x2cb5, {1|U, {0x2cb4}}},
+ {0x2cb7, {1|U, {0x2cb6}}},
+ {0x2cb9, {1|U, {0x2cb8}}},
+ {0x2cbb, {1|U, {0x2cba}}},
+ {0x2cbd, {1|U, {0x2cbc}}},
+ {0x2cbf, {1|U, {0x2cbe}}},
+ {0x2cc1, {1|U, {0x2cc0}}},
+ {0x2cc3, {1|U, {0x2cc2}}},
+ {0x2cc5, {1|U, {0x2cc4}}},
+ {0x2cc7, {1|U, {0x2cc6}}},
+ {0x2cc9, {1|U, {0x2cc8}}},
+ {0x2ccb, {1|U, {0x2cca}}},
+ {0x2ccd, {1|U, {0x2ccc}}},
+ {0x2ccf, {1|U, {0x2cce}}},
+ {0x2cd1, {1|U, {0x2cd0}}},
+ {0x2cd3, {1|U, {0x2cd2}}},
+ {0x2cd5, {1|U, {0x2cd4}}},
+ {0x2cd7, {1|U, {0x2cd6}}},
+ {0x2cd9, {1|U, {0x2cd8}}},
+ {0x2cdb, {1|U, {0x2cda}}},
+ {0x2cdd, {1|U, {0x2cdc}}},
+ {0x2cdf, {1|U, {0x2cde}}},
+ {0x2ce1, {1|U, {0x2ce0}}},
+ {0x2ce3, {1|U, {0x2ce2}}},
+ {0x2cec, {1|U, {0x2ceb}}},
+ {0x2cee, {1|U, {0x2ced}}},
+ {0x2cf3, {1|U, {0x2cf2}}},
+ {0x2d00, {1|U, {0x10a0}}},
+ {0x2d01, {1|U, {0x10a1}}},
+ {0x2d02, {1|U, {0x10a2}}},
+ {0x2d03, {1|U, {0x10a3}}},
+ {0x2d04, {1|U, {0x10a4}}},
+ {0x2d05, {1|U, {0x10a5}}},
+ {0x2d06, {1|U, {0x10a6}}},
+ {0x2d07, {1|U, {0x10a7}}},
+ {0x2d08, {1|U, {0x10a8}}},
+ {0x2d09, {1|U, {0x10a9}}},
+ {0x2d0a, {1|U, {0x10aa}}},
+ {0x2d0b, {1|U, {0x10ab}}},
+ {0x2d0c, {1|U, {0x10ac}}},
+ {0x2d0d, {1|U, {0x10ad}}},
+ {0x2d0e, {1|U, {0x10ae}}},
+ {0x2d0f, {1|U, {0x10af}}},
+ {0x2d10, {1|U, {0x10b0}}},
+ {0x2d11, {1|U, {0x10b1}}},
+ {0x2d12, {1|U, {0x10b2}}},
+ {0x2d13, {1|U, {0x10b3}}},
+ {0x2d14, {1|U, {0x10b4}}},
+ {0x2d15, {1|U, {0x10b5}}},
+ {0x2d16, {1|U, {0x10b6}}},
+ {0x2d17, {1|U, {0x10b7}}},
+ {0x2d18, {1|U, {0x10b8}}},
+ {0x2d19, {1|U, {0x10b9}}},
+ {0x2d1a, {1|U, {0x10ba}}},
+ {0x2d1b, {1|U, {0x10bb}}},
+ {0x2d1c, {1|U, {0x10bc}}},
+ {0x2d1d, {1|U, {0x10bd}}},
+ {0x2d1e, {1|U, {0x10be}}},
+ {0x2d1f, {1|U, {0x10bf}}},
+ {0x2d20, {1|U, {0x10c0}}},
+ {0x2d21, {1|U, {0x10c1}}},
+ {0x2d22, {1|U, {0x10c2}}},
+ {0x2d23, {1|U, {0x10c3}}},
+ {0x2d24, {1|U, {0x10c4}}},
+ {0x2d25, {1|U, {0x10c5}}},
+ {0x2d27, {1|U, {0x10c7}}},
+ {0x2d2d, {1|U, {0x10cd}}},
+ {0xa641, {1|U, {0xa640}}},
+ {0xa643, {1|U, {0xa642}}},
+ {0xa645, {1|U, {0xa644}}},
+ {0xa647, {1|U, {0xa646}}},
+ {0xa649, {1|U, {0xa648}}},
+ {0xa64b, {2|U, {0xa64a, 0x1c88}}},
+ {0xa64d, {1|U, {0xa64c}}},
+ {0xa64f, {1|U, {0xa64e}}},
+ {0xa651, {1|U, {0xa650}}},
+ {0xa653, {1|U, {0xa652}}},
+ {0xa655, {1|U, {0xa654}}},
+ {0xa657, {1|U, {0xa656}}},
+ {0xa659, {1|U, {0xa658}}},
+ {0xa65b, {1|U, {0xa65a}}},
+ {0xa65d, {1|U, {0xa65c}}},
+ {0xa65f, {1|U, {0xa65e}}},
+ {0xa661, {1|U, {0xa660}}},
+ {0xa663, {1|U, {0xa662}}},
+ {0xa665, {1|U, {0xa664}}},
+ {0xa667, {1|U, {0xa666}}},
+ {0xa669, {1|U, {0xa668}}},
+ {0xa66b, {1|U, {0xa66a}}},
+ {0xa66d, {1|U, {0xa66c}}},
+ {0xa681, {1|U, {0xa680}}},
+ {0xa683, {1|U, {0xa682}}},
+ {0xa685, {1|U, {0xa684}}},
+ {0xa687, {1|U, {0xa686}}},
+ {0xa689, {1|U, {0xa688}}},
+ {0xa68b, {1|U, {0xa68a}}},
+ {0xa68d, {1|U, {0xa68c}}},
+ {0xa68f, {1|U, {0xa68e}}},
+ {0xa691, {1|U, {0xa690}}},
+ {0xa693, {1|U, {0xa692}}},
+ {0xa695, {1|U, {0xa694}}},
+ {0xa697, {1|U, {0xa696}}},
+ {0xa699, {1|U, {0xa698}}},
+ {0xa69b, {1|U, {0xa69a}}},
+ {0xa723, {1|U, {0xa722}}},
+ {0xa725, {1|U, {0xa724}}},
+ {0xa727, {1|U, {0xa726}}},
+ {0xa729, {1|U, {0xa728}}},
+ {0xa72b, {1|U, {0xa72a}}},
+ {0xa72d, {1|U, {0xa72c}}},
+ {0xa72f, {1|U, {0xa72e}}},
+ {0xa733, {1|U, {0xa732}}},
+ {0xa735, {1|U, {0xa734}}},
+ {0xa737, {1|U, {0xa736}}},
+ {0xa739, {1|U, {0xa738}}},
+ {0xa73b, {1|U, {0xa73a}}},
+ {0xa73d, {1|U, {0xa73c}}},
+ {0xa73f, {1|U, {0xa73e}}},
+ {0xa741, {1|U, {0xa740}}},
+ {0xa743, {1|U, {0xa742}}},
+ {0xa745, {1|U, {0xa744}}},
+ {0xa747, {1|U, {0xa746}}},
+ {0xa749, {1|U, {0xa748}}},
+ {0xa74b, {1|U, {0xa74a}}},
+ {0xa74d, {1|U, {0xa74c}}},
+ {0xa74f, {1|U, {0xa74e}}},
+ {0xa751, {1|U, {0xa750}}},
+ {0xa753, {1|U, {0xa752}}},
+ {0xa755, {1|U, {0xa754}}},
+ {0xa757, {1|U, {0xa756}}},
+ {0xa759, {1|U, {0xa758}}},
+ {0xa75b, {1|U, {0xa75a}}},
+ {0xa75d, {1|U, {0xa75c}}},
+ {0xa75f, {1|U, {0xa75e}}},
+ {0xa761, {1|U, {0xa760}}},
+ {0xa763, {1|U, {0xa762}}},
+ {0xa765, {1|U, {0xa764}}},
+ {0xa767, {1|U, {0xa766}}},
+ {0xa769, {1|U, {0xa768}}},
+ {0xa76b, {1|U, {0xa76a}}},
+ {0xa76d, {1|U, {0xa76c}}},
+ {0xa76f, {1|U, {0xa76e}}},
+ {0xa77a, {1|U, {0xa779}}},
+ {0xa77c, {1|U, {0xa77b}}},
+ {0xa77f, {1|U, {0xa77e}}},
+ {0xa781, {1|U, {0xa780}}},
+ {0xa783, {1|U, {0xa782}}},
+ {0xa785, {1|U, {0xa784}}},
+ {0xa787, {1|U, {0xa786}}},
+ {0xa78c, {1|U, {0xa78b}}},
+ {0xa791, {1|U, {0xa790}}},
+ {0xa793, {1|U, {0xa792}}},
+ {0xa794, {1|U, {0xa7c4}}},
+ {0xa797, {1|U, {0xa796}}},
+ {0xa799, {1|U, {0xa798}}},
+ {0xa79b, {1|U, {0xa79a}}},
+ {0xa79d, {1|U, {0xa79c}}},
+ {0xa79f, {1|U, {0xa79e}}},
+ {0xa7a1, {1|U, {0xa7a0}}},
+ {0xa7a3, {1|U, {0xa7a2}}},
+ {0xa7a5, {1|U, {0xa7a4}}},
+ {0xa7a7, {1|U, {0xa7a6}}},
+ {0xa7a9, {1|U, {0xa7a8}}},
+ {0xa7b5, {1|U, {0xa7b4}}},
+ {0xa7b7, {1|U, {0xa7b6}}},
+ {0xa7b9, {1|U, {0xa7b8}}},
+ {0xa7bb, {1|U, {0xa7ba}}},
+ {0xa7bd, {1|U, {0xa7bc}}},
+ {0xa7bf, {1|U, {0xa7be}}},
+ {0xa7c3, {1|U, {0xa7c2}}},
+ {0xab53, {1|U, {0xa7b3}}},
+ {0xff41, {1|U, {0xff21}}},
+ {0xff42, {1|U, {0xff22}}},
+ {0xff43, {1|U, {0xff23}}},
+ {0xff44, {1|U, {0xff24}}},
+ {0xff45, {1|U, {0xff25}}},
+ {0xff46, {1|U, {0xff26}}},
+ {0xff47, {1|U, {0xff27}}},
+ {0xff48, {1|U, {0xff28}}},
+ {0xff49, {1|U, {0xff29}}},
+ {0xff4a, {1|U, {0xff2a}}},
+ {0xff4b, {1|U, {0xff2b}}},
+ {0xff4c, {1|U, {0xff2c}}},
+ {0xff4d, {1|U, {0xff2d}}},
+ {0xff4e, {1|U, {0xff2e}}},
+ {0xff4f, {1|U, {0xff2f}}},
+ {0xff50, {1|U, {0xff30}}},
+ {0xff51, {1|U, {0xff31}}},
+ {0xff52, {1|U, {0xff32}}},
+ {0xff53, {1|U, {0xff33}}},
+ {0xff54, {1|U, {0xff34}}},
+ {0xff55, {1|U, {0xff35}}},
+ {0xff56, {1|U, {0xff36}}},
+ {0xff57, {1|U, {0xff37}}},
+ {0xff58, {1|U, {0xff38}}},
+ {0xff59, {1|U, {0xff39}}},
+ {0xff5a, {1|U, {0xff3a}}},
+ {0x10428, {1|U, {0x10400}}},
+ {0x10429, {1|U, {0x10401}}},
+ {0x1042a, {1|U, {0x10402}}},
+ {0x1042b, {1|U, {0x10403}}},
+ {0x1042c, {1|U, {0x10404}}},
+ {0x1042d, {1|U, {0x10405}}},
+ {0x1042e, {1|U, {0x10406}}},
+ {0x1042f, {1|U, {0x10407}}},
+ {0x10430, {1|U, {0x10408}}},
+ {0x10431, {1|U, {0x10409}}},
+ {0x10432, {1|U, {0x1040a}}},
+ {0x10433, {1|U, {0x1040b}}},
+ {0x10434, {1|U, {0x1040c}}},
+ {0x10435, {1|U, {0x1040d}}},
+ {0x10436, {1|U, {0x1040e}}},
+ {0x10437, {1|U, {0x1040f}}},
+ {0x10438, {1|U, {0x10410}}},
+ {0x10439, {1|U, {0x10411}}},
+ {0x1043a, {1|U, {0x10412}}},
+ {0x1043b, {1|U, {0x10413}}},
+ {0x1043c, {1|U, {0x10414}}},
+ {0x1043d, {1|U, {0x10415}}},
+ {0x1043e, {1|U, {0x10416}}},
+ {0x1043f, {1|U, {0x10417}}},
+ {0x10440, {1|U, {0x10418}}},
+ {0x10441, {1|U, {0x10419}}},
+ {0x10442, {1|U, {0x1041a}}},
+ {0x10443, {1|U, {0x1041b}}},
+ {0x10444, {1|U, {0x1041c}}},
+ {0x10445, {1|U, {0x1041d}}},
+ {0x10446, {1|U, {0x1041e}}},
+ {0x10447, {1|U, {0x1041f}}},
+ {0x10448, {1|U, {0x10420}}},
+ {0x10449, {1|U, {0x10421}}},
+ {0x1044a, {1|U, {0x10422}}},
+ {0x1044b, {1|U, {0x10423}}},
+ {0x1044c, {1|U, {0x10424}}},
+ {0x1044d, {1|U, {0x10425}}},
+ {0x1044e, {1|U, {0x10426}}},
+ {0x1044f, {1|U, {0x10427}}},
+ {0x104d8, {1|U, {0x104b0}}},
+ {0x104d9, {1|U, {0x104b1}}},
+ {0x104da, {1|U, {0x104b2}}},
+ {0x104db, {1|U, {0x104b3}}},
+ {0x104dc, {1|U, {0x104b4}}},
+ {0x104dd, {1|U, {0x104b5}}},
+ {0x104de, {1|U, {0x104b6}}},
+ {0x104df, {1|U, {0x104b7}}},
+ {0x104e0, {1|U, {0x104b8}}},
+ {0x104e1, {1|U, {0x104b9}}},
+ {0x104e2, {1|U, {0x104ba}}},
+ {0x104e3, {1|U, {0x104bb}}},
+ {0x104e4, {1|U, {0x104bc}}},
+ {0x104e5, {1|U, {0x104bd}}},
+ {0x104e6, {1|U, {0x104be}}},
+ {0x104e7, {1|U, {0x104bf}}},
+ {0x104e8, {1|U, {0x104c0}}},
+ {0x104e9, {1|U, {0x104c1}}},
+ {0x104ea, {1|U, {0x104c2}}},
+ {0x104eb, {1|U, {0x104c3}}},
+ {0x104ec, {1|U, {0x104c4}}},
+ {0x104ed, {1|U, {0x104c5}}},
+ {0x104ee, {1|U, {0x104c6}}},
+ {0x104ef, {1|U, {0x104c7}}},
+ {0x104f0, {1|U, {0x104c8}}},
+ {0x104f1, {1|U, {0x104c9}}},
+ {0x104f2, {1|U, {0x104ca}}},
+ {0x104f3, {1|U, {0x104cb}}},
+ {0x104f4, {1|U, {0x104cc}}},
+ {0x104f5, {1|U, {0x104cd}}},
+ {0x104f6, {1|U, {0x104ce}}},
+ {0x104f7, {1|U, {0x104cf}}},
+ {0x104f8, {1|U, {0x104d0}}},
+ {0x104f9, {1|U, {0x104d1}}},
+ {0x104fa, {1|U, {0x104d2}}},
+ {0x104fb, {1|U, {0x104d3}}},
+ {0x10cc0, {1|U, {0x10c80}}},
+ {0x10cc1, {1|U, {0x10c81}}},
+ {0x10cc2, {1|U, {0x10c82}}},
+ {0x10cc3, {1|U, {0x10c83}}},
+ {0x10cc4, {1|U, {0x10c84}}},
+ {0x10cc5, {1|U, {0x10c85}}},
+ {0x10cc6, {1|U, {0x10c86}}},
+ {0x10cc7, {1|U, {0x10c87}}},
+ {0x10cc8, {1|U, {0x10c88}}},
+ {0x10cc9, {1|U, {0x10c89}}},
+ {0x10cca, {1|U, {0x10c8a}}},
+ {0x10ccb, {1|U, {0x10c8b}}},
+ {0x10ccc, {1|U, {0x10c8c}}},
+ {0x10ccd, {1|U, {0x10c8d}}},
+ {0x10cce, {1|U, {0x10c8e}}},
+ {0x10ccf, {1|U, {0x10c8f}}},
+ {0x10cd0, {1|U, {0x10c90}}},
+ {0x10cd1, {1|U, {0x10c91}}},
+ {0x10cd2, {1|U, {0x10c92}}},
+ {0x10cd3, {1|U, {0x10c93}}},
+ {0x10cd4, {1|U, {0x10c94}}},
+ {0x10cd5, {1|U, {0x10c95}}},
+ {0x10cd6, {1|U, {0x10c96}}},
+ {0x10cd7, {1|U, {0x10c97}}},
+ {0x10cd8, {1|U, {0x10c98}}},
+ {0x10cd9, {1|U, {0x10c99}}},
+ {0x10cda, {1|U, {0x10c9a}}},
+ {0x10cdb, {1|U, {0x10c9b}}},
+ {0x10cdc, {1|U, {0x10c9c}}},
+ {0x10cdd, {1|U, {0x10c9d}}},
+ {0x10cde, {1|U, {0x10c9e}}},
+ {0x10cdf, {1|U, {0x10c9f}}},
+ {0x10ce0, {1|U, {0x10ca0}}},
+ {0x10ce1, {1|U, {0x10ca1}}},
+ {0x10ce2, {1|U, {0x10ca2}}},
+ {0x10ce3, {1|U, {0x10ca3}}},
+ {0x10ce4, {1|U, {0x10ca4}}},
+ {0x10ce5, {1|U, {0x10ca5}}},
+ {0x10ce6, {1|U, {0x10ca6}}},
+ {0x10ce7, {1|U, {0x10ca7}}},
+ {0x10ce8, {1|U, {0x10ca8}}},
+ {0x10ce9, {1|U, {0x10ca9}}},
+ {0x10cea, {1|U, {0x10caa}}},
+ {0x10ceb, {1|U, {0x10cab}}},
+ {0x10cec, {1|U, {0x10cac}}},
+ {0x10ced, {1|U, {0x10cad}}},
+ {0x10cee, {1|U, {0x10cae}}},
+ {0x10cef, {1|U, {0x10caf}}},
+ {0x10cf0, {1|U, {0x10cb0}}},
+ {0x10cf1, {1|U, {0x10cb1}}},
+ {0x10cf2, {1|U, {0x10cb2}}},
+ {0x118c0, {1|U, {0x118a0}}},
+ {0x118c1, {1|U, {0x118a1}}},
+ {0x118c2, {1|U, {0x118a2}}},
+ {0x118c3, {1|U, {0x118a3}}},
+ {0x118c4, {1|U, {0x118a4}}},
+ {0x118c5, {1|U, {0x118a5}}},
+ {0x118c6, {1|U, {0x118a6}}},
+ {0x118c7, {1|U, {0x118a7}}},
+ {0x118c8, {1|U, {0x118a8}}},
+ {0x118c9, {1|U, {0x118a9}}},
+ {0x118ca, {1|U, {0x118aa}}},
+ {0x118cb, {1|U, {0x118ab}}},
+ {0x118cc, {1|U, {0x118ac}}},
+ {0x118cd, {1|U, {0x118ad}}},
+ {0x118ce, {1|U, {0x118ae}}},
+ {0x118cf, {1|U, {0x118af}}},
+ {0x118d0, {1|U, {0x118b0}}},
+ {0x118d1, {1|U, {0x118b1}}},
+ {0x118d2, {1|U, {0x118b2}}},
+ {0x118d3, {1|U, {0x118b3}}},
+ {0x118d4, {1|U, {0x118b4}}},
+ {0x118d5, {1|U, {0x118b5}}},
+ {0x118d6, {1|U, {0x118b6}}},
+ {0x118d7, {1|U, {0x118b7}}},
+ {0x118d8, {1|U, {0x118b8}}},
+ {0x118d9, {1|U, {0x118b9}}},
+ {0x118da, {1|U, {0x118ba}}},
+ {0x118db, {1|U, {0x118bb}}},
+ {0x118dc, {1|U, {0x118bc}}},
+ {0x118dd, {1|U, {0x118bd}}},
+ {0x118de, {1|U, {0x118be}}},
+ {0x118df, {1|U, {0x118bf}}},
+ {0x16e60, {1|U, {0x16e40}}},
+ {0x16e61, {1|U, {0x16e41}}},
+ {0x16e62, {1|U, {0x16e42}}},
+ {0x16e63, {1|U, {0x16e43}}},
+ {0x16e64, {1|U, {0x16e44}}},
+ {0x16e65, {1|U, {0x16e45}}},
+ {0x16e66, {1|U, {0x16e46}}},
+ {0x16e67, {1|U, {0x16e47}}},
+ {0x16e68, {1|U, {0x16e48}}},
+ {0x16e69, {1|U, {0x16e49}}},
+ {0x16e6a, {1|U, {0x16e4a}}},
+ {0x16e6b, {1|U, {0x16e4b}}},
+ {0x16e6c, {1|U, {0x16e4c}}},
+ {0x16e6d, {1|U, {0x16e4d}}},
+ {0x16e6e, {1|U, {0x16e4e}}},
+ {0x16e6f, {1|U, {0x16e4f}}},
+ {0x16e70, {1|U, {0x16e50}}},
+ {0x16e71, {1|U, {0x16e51}}},
+ {0x16e72, {1|U, {0x16e52}}},
+ {0x16e73, {1|U, {0x16e53}}},
+ {0x16e74, {1|U, {0x16e54}}},
+ {0x16e75, {1|U, {0x16e55}}},
+ {0x16e76, {1|U, {0x16e56}}},
+ {0x16e77, {1|U, {0x16e57}}},
+ {0x16e78, {1|U, {0x16e58}}},
+ {0x16e79, {1|U, {0x16e59}}},
+ {0x16e7a, {1|U, {0x16e5a}}},
+ {0x16e7b, {1|U, {0x16e5b}}},
+ {0x16e7c, {1|U, {0x16e5c}}},
+ {0x16e7d, {1|U, {0x16e5d}}},
+ {0x16e7e, {1|U, {0x16e5e}}},
+ {0x16e7f, {1|U, {0x16e5f}}},
+ {0x1e922, {1|U, {0x1e900}}},
+ {0x1e923, {1|U, {0x1e901}}},
+ {0x1e924, {1|U, {0x1e902}}},
+ {0x1e925, {1|U, {0x1e903}}},
+ {0x1e926, {1|U, {0x1e904}}},
+ {0x1e927, {1|U, {0x1e905}}},
+ {0x1e928, {1|U, {0x1e906}}},
+ {0x1e929, {1|U, {0x1e907}}},
+ {0x1e92a, {1|U, {0x1e908}}},
+ {0x1e92b, {1|U, {0x1e909}}},
+ {0x1e92c, {1|U, {0x1e90a}}},
+ {0x1e92d, {1|U, {0x1e90b}}},
+ {0x1e92e, {1|U, {0x1e90c}}},
+ {0x1e92f, {1|U, {0x1e90d}}},
+ {0x1e930, {1|U, {0x1e90e}}},
+ {0x1e931, {1|U, {0x1e90f}}},
+ {0x1e932, {1|U, {0x1e910}}},
+ {0x1e933, {1|U, {0x1e911}}},
+ {0x1e934, {1|U, {0x1e912}}},
+ {0x1e935, {1|U, {0x1e913}}},
+ {0x1e936, {1|U, {0x1e914}}},
+ {0x1e937, {1|U, {0x1e915}}},
+ {0x1e938, {1|U, {0x1e916}}},
+ {0x1e939, {1|U, {0x1e917}}},
+ {0x1e93a, {1|U, {0x1e918}}},
+ {0x1e93b, {1|U, {0x1e919}}},
+ {0x1e93c, {1|U, {0x1e91a}}},
+ {0x1e93d, {1|U, {0x1e91b}}},
+ {0x1e93e, {1|U, {0x1e91c}}},
+ {0x1e93f, {1|U, {0x1e91d}}},
+ {0x1e940, {1|U, {0x1e91e}}},
+ {0x1e941, {1|U, {0x1e91f}}},
+ {0x1e942, {1|U, {0x1e920}}},
+ {0x1e943, {1|U, {0x1e921}}},
+#define CaseUnfold_11_Locale (*(CaseUnfold_11_Type (*)[1])(CaseUnfold_11_Table+1352))
+ {0x0069, {1|U, {0x0049}}},
+};
+
+/* ANSI-C code produced by gperf version 3.1 */
+/* Command-line: gperf -7 -k1,2,3 -F,-1 -c -j1 -i1 -t -T -E -C -H onigenc_unicode_CaseUnfold_11_hash -N onigenc_unicode_CaseUnfold_11_lookup -n */
+
+/* maximum key range = 2507, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+/*ARGSUSED*/
+static unsigned int
+onigenc_unicode_CaseUnfold_11_hash(const OnigCodePoint code)
+{
+ static const unsigned short asso_values[] =
+ {
+ 1, 2510, 2, 7, 4, 582, 9, 308, 197, 674,
+ 297, 20, 2, 3, 303, 351, 2510, 2510, 2510, 2510,
+ 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 112,
+ 2510, 2510, 2510, 2510, 2510, 2510, 2510, 120, 2510, 2510,
+ 2510, 2510, 2510, 1, 2510, 2510, 2510, 2510, 2510, 2510,
+ 2510, 2510, 2510, 278, 2510, 2510, 2510, 2510, 2510, 2510,
+ 2510, 2510, 12, 1, 7, 8, 218, 878, 222, 1178,
+ 480, 1102, 54, 1340, 151, 1615, 8, 15, 106, 1607,
+ 225, 854, 87, 490, 44, 1351, 5, 1281, 3, 1470,
+ 11, 1215, 377, 10, 441, 599, 152, 1642, 127, 1334,
+ 702, 841, 594, 827, 123, 916, 146, 1118, 117, 1363,
+ 254, 812, 249, 1096, 1630, 945, 437, 745, 1600, 718,
+ 1593, 704, 152, 1005, 383, 1064, 1493, 975, 236, 676,
+ 761, 579, 1017, 557, 1241, 628, 1195, 614, 1506, 464,
+ 1576, 535, 1432, 513, 1159, 423, 1026, 276, 1460, 291,
+ 1453, 392, 1263, 201, 1442, 85, 1412, 70, 1430, 100,
+ 1632, 129, 1410, 1, 1386, 25, 1373, 35, 656, 55,
+ 1188, 45, 1308, 160, 687, 227, 355, 175, 1201, 328,
+ 1030, 367, 1483, 414, 1479, 1166, 1418, 783, 994, 937,
+ 1083, 959, 1463, 967
+ };
+ return asso_values[bits_of(code, 2)+66] + asso_values[bits_of(code, 1)+4] + asso_values[bits_of(code, 0)];
+}
+
+static const CodePointList3 *
+onigenc_unicode_CaseUnfold_11_lookup(const OnigCodePoint code)
+{
+ enum
+ {
+ MIN_CODE_VALUE = 0x61,
+ MAX_CODE_VALUE = 0x1e943,
+ TOTAL_KEYWORDS = 1353,
+ MIN_WORD_LENGTH = 3,
+ MAX_WORD_LENGTH = 3,
+ MIN_HASH_VALUE = 3,
+ MAX_HASH_VALUE = 2509
+ };
+
+ static const short wordlist[] =
+ {
+ -1, -1, -1,
+ /*0x13e1*/ 589,
+ /*0x0461*/ 340,
+ /*0x04e1*/ 400,
+ /*0x0061*/ 0,
+ -1,
+ /*0x104e1*/ 1176,
+ /*0x1e61*/ 661,
+ /*0x1ee1*/ 720,
+ /*0x0161*/ 102,
+ /*0x2ce1*/ 952,
+ -1,
+ /*0x049b*/ 365,
+ -1, -1,
+ /*0x24e1*/ 840,
+ /*0x1e1b*/ 626,
+ /*0x048b*/ 357,
+ /*0x011b*/ 69,
+ /*0x2c9b*/ 917,
+ /*0x03e1*/ 280,
+ /*0x1e0b*/ 618,
+ /*0x1e8b*/ 682,
+ /*0x010b*/ 61,
+ /*0x2c8b*/ 909,
+ /*0x13e3*/ 591,
+ /*0x0463*/ 341,
+ /*0x04e3*/ 401,
+ /*0x0063*/ 2,
+ -1,
+ /*0x104e3*/ 1178,
+ /*0x1e63*/ 662,
+ /*0x1ee3*/ 721,
+ /*0x0163*/ 103,
+ /*0x2ce3*/ 953,
+ /*0x13e5*/ 593,
+ /*0x0465*/ 342,
+ /*0x04e5*/ 402,
+ /*0x0065*/ 4,
+ /*0x24e3*/ 842,
+ /*0x104e5*/ 1180,
+ /*0x1e65*/ 663,
+ /*0x1ee5*/ 722,
+ /*0x0165*/ 104,
+ /*0x03e3*/ 281,
+ /*0x13e9*/ 597,
+ /*0x0469*/ 344,
+ /*0x04e9*/ 404,
+ /*0x0069*/ 1352,
+ /*0x24e5*/ 844,
+ /*0x104e9*/ 1184,
+ /*0x1e69*/ 665,
+ /*0x1ee9*/ 724,
+ /*0x0169*/ 106,
+ /*0x03e5*/ 282,
+ /*0x13e7*/ 595,
+ /*0x0467*/ 343,
+ /*0x04e7*/ 403,
+ /*0x0067*/ 6,
+ /*0x24e9*/ 848,
+ /*0x104e7*/ 1182,
+ /*0x1e67*/ 664,
+ /*0x1ee7*/ 723,
+ /*0x0167*/ 105,
+ /*0x03e9*/ 284,
+ -1, -1, -1, -1,
+ /*0x24e7*/ 846,
+ /*0x13db*/ 583,
+ /*0x045b*/ 335,
+ /*0x04db*/ 397,
+ -1,
+ /*0x03e7*/ 283,
+ /*0x104db*/ 1170,
+ /*0x1e5b*/ 658,
+ /*0x1edb*/ 717,
+ /*0x015b*/ 99,
+ /*0x2cdb*/ 949,
+ -1, -1, -1, -1,
+ /*0x24db*/ 834,
+ /*0x13d9*/ 581,
+ /*0x0459*/ 333,
+ /*0x04d9*/ 396,
+ /*0xa761*/ 1064,
+ /*0x03db*/ 277,
+ /*0x104d9*/ 1168,
+ /*0x1e59*/ 657,
+ /*0x1ed9*/ 716,
+ /*0x0159*/ 98,
+ /*0x2cd9*/ 948,
+ -1, -1, -1, -1,
+ /*0x24d9*/ 832,
+ /*0x13dd*/ 585,
+ /*0x045d*/ 337,
+ /*0x04dd*/ 398,
+ -1,
+ /*0x03d9*/ 276,
+ /*0x104dd*/ 1172,
+ /*0x1e5d*/ 659,
+ /*0x1edd*/ 718,
+ /*0x015d*/ 100,
+ /*0x2cdd*/ 950,
+ -1, -1,
+ /*0xa763*/ 1065,
+ -1,
+ /*0x24dd*/ 836,
+ /*0x10ce1*/ 1236,
+ -1,
+ /*0x13aa*/ 534,
+ -1,
+ /*0x03dd*/ 278,
+ /*0x10e1*/ 495,
+ /*0x1042a*/ 1129,
+ /*0xa765*/ 1066,
+ /*0x13a6*/ 530,
+ -1, -1, -1,
+ /*0x13a0*/ 524,
+ -1,
+ /*0x13df*/ 587,
+ /*0x045f*/ 339,
+ /*0x04df*/ 399,
+ /*0xa769*/ 1068,
+ -1,
+ /*0x104df*/ 1174,
+ /*0x1e5f*/ 660,
+ /*0x1edf*/ 719,
+ /*0x015f*/ 101,
+ /*0x2cdf*/ 951,
+ /*0x10ce3*/ 1238,
+ -1, -1,
+ /*0xa767*/ 1067,
+ /*0x24df*/ 838,
+ /*0x10e3*/ 497,
+ -1,
+ /*0x13a8*/ 532,
+ -1,
+ /*0x03df*/ 279,
+ /*0x10ce5*/ 1240,
+ /*0x10428*/ 1127,
+ -1,
+ /*0x13b8*/ 548,
+ /*0x0438*/ 300,
+ /*0x10e5*/ 499,
+ -1,
+ /*0x10438*/ 1143,
+ /*0xa75b*/ 1061,
+ -1,
+ /*0x10ce9*/ 1244,
+ /*0x13eb*/ 599,
+ /*0x046b*/ 345,
+ /*0x04eb*/ 405,
+ /*0x006b*/ 9,
+ /*0x10e9*/ 503,
+ /*0x104eb*/ 1186,
+ /*0x1e6b*/ 666,
+ /*0x1eeb*/ 725,
+ /*0x016b*/ 107,
+ /*0x10ce7*/ 1242,
+ -1,
+ /*0x03b8*/ 253,
+ /*0xa759*/ 1060,
+ -1,
+ /*0x10e7*/ 501,
+ /*0x13ef*/ 603,
+ /*0x046f*/ 347,
+ /*0x04ef*/ 407,
+ /*0x006f*/ 13,
+ /*0x03eb*/ 285,
+ /*0x104ef*/ 1190,
+ /*0x1e6f*/ 668,
+ /*0x1eef*/ 727,
+ /*0x016f*/ 109,
+ /*0x10cdb*/ 1230,
+ -1, -1,
+ /*0xa75d*/ 1062,
+ -1,
+ /*0x10db*/ 489,
+ -1, -1, -1, -1,
+ /*0x03ef*/ 287,
+ -1, -1,
+ /*0x0261*/ 210,
+ -1,
+ /*0x10cd9*/ 1228,
+ -1,
+ /*0x13d7*/ 579,
+ /*0x0457*/ 331,
+ /*0x04d7*/ 395,
+ /*0x10d9*/ 487,
+ -1,
+ /*0x021b*/ 178,
+ /*0x1e57*/ 656,
+ /*0x1ed7*/ 715,
+ /*0x0157*/ 97,
+ /*0x2cd7*/ 947,
+ /*0x020b*/ 170,
+ -1, -1,
+ /*0x10cdd*/ 1232,
+ /*0x24d7*/ 830,
+ /*0xa75f*/ 1063,
+ -1,
+ /*0x1f61*/ 777,
+ /*0x10dd*/ 491,
+ /*0x03d7*/ 275,
+ /*0x0263*/ 211,
+ /*0x1f14*/ 748,
+ -1, -1,
+ /*0x1d8e*/ 612,
+ /*0xa661*/ 1013,
+ /*0x13ed*/ 601,
+ /*0x046d*/ 346,
+ /*0x04ed*/ 406,
+ /*0x006d*/ 11,
+ /*0x0265*/ 212,
+ /*0x104ed*/ 1188,
+ /*0x1e6d*/ 667,
+ /*0x1eed*/ 726,
+ /*0x016d*/ 108,
+ /*0x13be*/ 554,
+ /*0x043e*/ 306,
+ -1, -1,
+ /*0x1043e*/ 1149,
+ /*0x0269*/ 215,
+ /*0x1f63*/ 779,
+ /*0x10cdf*/ 1234,
+ /*0x013e*/ 85,
+ -1,
+ /*0x03ed*/ 286,
+ /*0xa76b*/ 1069,
+ /*0x10df*/ 493,
+ /*0x13ae*/ 538,
+ /*0xa663*/ 1014,
+ -1,
+ /*0x1f65*/ 781,
+ /*0x1042e*/ 1133,
+ /*0x13ac*/ 536,
+ /*0x03be*/ 259,
+ -1, -1,
+ /*0x1042c*/ 1131,
+ -1,
+ /*0xa665*/ 1015,
+ /*0x1f12*/ 746,
+ /*0xa76f*/ 1071,
+ -1, -1, -1,
+ /*0x025b*/ 207,
+ -1,
+ /*0x03ae*/ 244,
+ -1,
+ /*0xa669*/ 1017,
+ /*0x1f06*/ 742,
+ /*0x1f67*/ 783,
+ /*0x03ac*/ 242,
+ /*0x10ceb*/ 1246,
+ -1,
+ /*0x13d1*/ 573,
+ /*0x0451*/ 325,
+ /*0x04d1*/ 392,
+ /*0x10eb*/ 505,
+ /*0xa667*/ 1016,
+ /*0x0259*/ 206,
+ /*0x1e51*/ 653,
+ /*0x1ed1*/ 712,
+ /*0x0151*/ 94,
+ /*0x2cd1*/ 944,
+ -1, -1,
+ /*0xa757*/ 1059,
+ /*0x10cef*/ 1250,
+ /*0x24d1*/ 824,
+ /*0x13d3*/ 575,
+ /*0x0453*/ 327,
+ /*0x04d3*/ 393,
+ /*0x10ef*/ 509,
+ /*0xa65b*/ 1010,
+ -1,
+ /*0x1e53*/ 654,
+ /*0x1ed3*/ 713,
+ /*0x0153*/ 95,
+ /*0x2cd3*/ 945,
+ -1,
+ /*0xab53*/ 1100,
+ /*0x0561*/ 440,
+ /*0x1f10*/ 744,
+ /*0x24d3*/ 826,
+ -1, -1,
+ /*0x01e1*/ 151,
+ -1,
+ /*0xa659*/ 1009,
+ -1,
+ /*0x051b*/ 429,
+ -1,
+ /*0xa76d*/ 1070,
+ /*0x10cd7*/ 1226,
+ -1,
+ /*0x050b*/ 421,
+ -1, -1,
+ /*0x10d7*/ 485,
+ -1, -1, -1, -1,
+ /*0xa65d*/ 1011,
+ -1,
+ /*0x0563*/ 442,
+ /*0x13f1*/ 605,
+ /*0x0471*/ 348,
+ /*0x04f1*/ 408,
+ /*0x0071*/ 15,
+ /*0x01e3*/ 152,
+ /*0x104f1*/ 1192,
+ /*0x1e71*/ 669,
+ /*0x1ef1*/ 728,
+ /*0x0171*/ 110,
+ /*0x0565*/ 444,
+ -1, -1,
+ /*0x1f26*/ 756,
+ /*0x10ced*/ 1248,
+ /*0x01e5*/ 153,
+ -1,
+ /*0x1f20*/ 750,
+ -1,
+ /*0x10ed*/ 507,
+ /*0x0569*/ 448,
+ -1, -1,
+ /*0x118db*/ 1281,
+ /*0x0192*/ 122,
+ /*0x01e9*/ 155,
+ -1,
+ /*0xa65f*/ 1012,
+ /*0x13ee*/ 602,
+ /*0x026b*/ 217,
+ /*0x0567*/ 446,
+ /*0x006e*/ 12,
+ -1,
+ /*0x104ee*/ 1189,
+ -1,
+ /*0x01e7*/ 154,
+ /*0xa751*/ 1056,
+ /*0x2cee*/ 955,
+ /*0x118d9*/ 1279,
+ -1,
+ /*0x13f3*/ 607,
+ /*0x0473*/ 349,
+ /*0x04f3*/ 409,
+ /*0x0073*/ 17,
+ /*0x026f*/ 219,
+ /*0x104f3*/ 1194,
+ /*0x1e73*/ 670,
+ /*0x1ef3*/ 729,
+ /*0x0173*/ 111,
+ /*0x2cf3*/ 956,
+ /*0x2c61*/ 896,
+ /*0xa753*/ 1057,
+ -1,
+ /*0x118dd*/ 1283,
+ -1, -1,
+ /*0x13ba*/ 550,
+ /*0x043a*/ 302,
+ /*0xa66b*/ 1018,
+ /*0x03f3*/ 289,
+ /*0x1043a*/ 1145,
+ -1, -1,
+ /*0x10cd1*/ 1220,
+ /*0x013a*/ 83,
+ /*0x13d5*/ 577,
+ /*0x0455*/ 329,
+ /*0x04d5*/ 394,
+ /*0x10d1*/ 479,
+ -1,
+ /*0x0257*/ 205,
+ /*0x1e55*/ 655,
+ /*0x1ed5*/ 714,
+ /*0x0155*/ 96,
+ /*0x2cd5*/ 946,
+ /*0x03ba*/ 255,
+ -1,
+ /*0x0586*/ 477,
+ /*0x10cd3*/ 1222,
+ /*0x24d5*/ 828,
+ /*0x01dd*/ 149,
+ -1,
+ /*0x118df*/ 1285,
+ /*0x10d3*/ 481,
+ /*0x2c65*/ 897,
+ -1,
+ /*0x018c*/ 121,
+ /*0x13f5*/ 609,
+ /*0x0475*/ 350,
+ /*0x04f5*/ 410,
+ /*0x0075*/ 19,
+ /*0x1f57*/ 775,
+ /*0x104f5*/ 1196,
+ /*0x1e75*/ 671,
+ /*0x1ef5*/ 730,
+ /*0x0175*/ 112,
+ /*0x13cf*/ 571,
+ /*0x044f*/ 323,
+ /*0x04cf*/ 391,
+ /*0xa657*/ 1008,
+ /*0x1044f*/ 1166,
+ /*0x1e92a*/ 1326,
+ /*0x1e4f*/ 652,
+ /*0x1ecf*/ 711,
+ /*0x014f*/ 93,
+ /*0x2ccf*/ 943,
+ -1,
+ /*0x1e926*/ 1322,
+ /*0x1f00*/ 736,
+ /*0x01df*/ 150,
+ /*0x13b2*/ 542,
+ /*0x0432*/ 294,
+ /*0x1f02*/ 738,
+ -1,
+ /*0x10432*/ 1137,
+ /*0x10cf1*/ 1252,
+ /*0x2d16*/ 979,
+ -1,
+ /*0x2d14*/ 977,
+ /*0x2c5b*/ 892,
+ /*0x10f1*/ 511,
+ /*0x2d0a*/ 967,
+ -1,
+ /*0x2d1b*/ 984,
+ /*0x2d18*/ 981,
+ /*0xa66d*/ 1019,
+ /*0x01a8*/ 130,
+ -1,
+ /*0x2d0b*/ 968,
+ /*0x03b2*/ 247,
+ /*0x1e928*/ 1324,
+ /*0x0188*/ 120,
+ /*0x019e*/ 126,
+ -1,
+ /*0x2c59*/ 890,
+ /*0x056b*/ 450,
+ /*0x1e938*/ 1340,
+ /*0x13c9*/ 565,
+ /*0x0449*/ 317,
+ -1,
+ /*0x01eb*/ 156,
+ /*0x10449*/ 1160,
+ /*0x10cee*/ 1249,
+ /*0x1e49*/ 649,
+ /*0x1ec9*/ 708,
+ /*0x0251*/ 200,
+ /*0x2cc9*/ 940,
+ /*0x10ee*/ 508,
+ -1,
+ /*0x2c5d*/ 894,
+ /*0x056f*/ 454,
+ -1,
+ /*0xa755*/ 1058,
+ -1,
+ /*0x118d7*/ 1277,
+ /*0x01ef*/ 158,
+ /*0x03c9*/ 269,
+ /*0x2d12*/ 975,
+ -1,
+ /*0x10f3*/ 513,
+ /*0x0253*/ 202,
+ -1, -1, -1, -1,
+ /*0x0491*/ 360,
+ /*0x1f51*/ 772,
+ /*0x2d06*/ 963,
+ /*0xa794*/ 1082,
+ /*0x1e11*/ 621,
+ /*0x1e91*/ 685,
+ /*0x0111*/ 64,
+ /*0x2c91*/ 912,
+ /*0xa79b*/ 1085,
+ /*0xa651*/ 1005,
+ -1, -1, -1, -1,
+ /*0x10cd5*/ 1224,
+ -1,
+ /*0x1f53*/ 773,
+ -1,
+ /*0xa74f*/ 1055,
+ /*0x10d5*/ 483,
+ -1,
+ /*0x13cd*/ 569,
+ /*0x044d*/ 321,
+ -1,
+ /*0xa653*/ 1006,
+ /*0x1044d*/ 1164,
+ -1,
+ /*0x1e4d*/ 651,
+ /*0x1ecd*/ 710,
+ /*0x014d*/ 92,
+ /*0x2ccd*/ 942,
+ -1,
+ /*0x0271*/ 220,
+ /*0x0180*/ 117,
+ -1,
+ /*0x2d10*/ 973,
+ /*0x2c38*/ 857,
+ /*0x056d*/ 452,
+ -1, -1,
+ /*0x03cd*/ 273,
+ /*0x10f5*/ 515,
+ /*0x01ed*/ 157,
+ /*0x13cb*/ 567,
+ /*0x044b*/ 319,
+ /*0x10ccf*/ 1218,
+ -1,
+ /*0x1044b*/ 1162,
+ -1,
+ /*0x1e4b*/ 650,
+ /*0x1ecb*/ 709,
+ /*0x014b*/ 91,
+ /*0x2ccb*/ 941,
+ /*0x1f71*/ 785,
+ /*0x2d0c*/ 969,
+ /*0x1e93e*/ 1346,
+ -1, -1, -1,
+ /*0xa749*/ 1052,
+ -1, -1,
+ /*0x03cb*/ 271,
+ -1,
+ /*0x118d1*/ 1271,
+ /*0x13c3*/ 559,
+ /*0x0443*/ 311,
+ -1,
+ /*0x1e92e*/ 1330,
+ /*0x10443*/ 1154,
+ -1,
+ /*0x1e43*/ 646,
+ /*0x1ec3*/ 705,
+ /*0x1e92c*/ 1328,
+ /*0x2cc3*/ 937,
+ /*0x2d20*/ 989,
+ /*0x0580*/ 471,
+ -1, -1,
+ /*0x118d3*/ 1273,
+ /*0x0582*/ 473,
+ -1, -1, -1,
+ /*0x03c3*/ 263,
+ /*0x2c57*/ 888,
+ /*0x10cc9*/ 1212,
+ /*0x13c1*/ 557,
+ /*0x0441*/ 309,
+ -1,
+ /*0x00e1*/ 26,
+ /*0x10441*/ 1152,
+ /*0x1f73*/ 787,
+ /*0x1e41*/ 645,
+ /*0x1ec1*/ 704,
+ -1,
+ /*0x2cc1*/ 936,
+ -1, -1,
+ /*0x2d08*/ 965,
+ /*0x2d1e*/ 987,
+ -1,
+ /*0x13a4*/ 528,
+ -1,
+ /*0xa78c*/ 1079,
+ -1,
+ /*0x03c1*/ 262,
+ -1,
+ /*0xa74d*/ 1054,
+ /*0x049d*/ 366,
+ -1, -1, -1,
+ /*0x1e1d*/ 627,
+ /*0x00e3*/ 28,
+ /*0x011d*/ 70,
+ /*0x2c9d*/ 918,
+ /*0x1f55*/ 774,
+ /*0x0275*/ 222,
+ -1,
+ /*0x2c3e*/ 863,
+ -1,
+ /*0x13c7*/ 563,
+ /*0x0447*/ 315,
+ /*0x00e5*/ 30,
+ /*0xa655*/ 1007,
+ /*0x10447*/ 1158,
+ /*0x024f*/ 198,
+ /*0x1e47*/ 648,
+ /*0x1ec7*/ 707,
+ /*0xa74b*/ 1053,
+ /*0x2cc7*/ 939,
+ /*0x0371*/ 236,
+ -1,
+ /*0x00e9*/ 34,
+ /*0x10ccd*/ 1216,
+ /*0x13c5*/ 561,
+ /*0x0445*/ 313,
+ /*0x0571*/ 456,
+ /*0x1f75*/ 789,
+ /*0x10445*/ 1156,
+ /*0x03c7*/ 267,
+ /*0x1e45*/ 647,
+ /*0x1ec5*/ 706,
+ /*0x00e7*/ 32,
+ /*0x2cc5*/ 938,
+ -1, -1, -1, -1, -1, -1,
+ /*0xa743*/ 1049,
+ -1, -1,
+ /*0x03c5*/ 265,
+ /*0xa64f*/ 1004,
+ /*0x10ccb*/ 1214,
+ -1, -1,
+ /*0x2c51*/ 882,
+ -1,
+ /*0x1f32*/ 760,
+ -1,
+ /*0x13e6*/ 594,
+ /*0x056e*/ 453,
+ /*0x2d00*/ 957,
+ /*0x0066*/ 5,
+ /*0x0249*/ 195,
+ /*0x104e6*/ 1181,
+ /*0x2d02*/ 959,
+ /*0x0373*/ 237,
+ -1,
+ /*0x2d0e*/ 971,
+ /*0xa741*/ 1048,
+ /*0x2c53*/ 884,
+ -1,
+ /*0x0573*/ 458,
+ /*0x24e6*/ 845,
+ /*0x10cc3*/ 1206,
+ /*0x118d5*/ 1275,
+ -1,
+ /*0x01f3*/ 159,
+ -1,
+ /*0x13bf*/ 555,
+ /*0x043f*/ 307,
+ /*0x04bf*/ 383,
+ -1,
+ /*0x1043f*/ 1150,
+ /*0x028a*/ 230,
+ /*0x1e3f*/ 644,
+ /*0x1ebf*/ 703,
+ /*0x019a*/ 125,
+ /*0x2cbf*/ 935,
+ /*0x0211*/ 173,
+ /*0x13ec*/ 600,
+ /*0x028b*/ 231,
+ /*0xa649*/ 1001,
+ /*0x006c*/ 10,
+ -1,
+ /*0x104ec*/ 1187,
+ /*0x10cc1*/ 1204,
+ /*0x1e93a*/ 1342,
+ /*0x03bf*/ 260,
+ /*0x2cec*/ 954,
+ /*0x1f04*/ 740,
+ -1, -1, -1,
+ /*0xa747*/ 1051,
+ /*0x13a2*/ 526,
+ /*0x118cf*/ 1269,
+ /*0x13b7*/ 547,
+ /*0x0437*/ 299,
+ /*0x04b7*/ 379,
+ /*0x1f11*/ 745,
+ /*0x10437*/ 1142,
+ /*0x024d*/ 197,
+ /*0x1e37*/ 640,
+ /*0x1eb7*/ 699,
+ /*0x0137*/ 82,
+ /*0x2cb7*/ 931,
+ -1,
+ /*0xa745*/ 1050,
+ /*0x0575*/ 460,
+ /*0x0292*/ 233,
+ /*0x13b5*/ 545,
+ /*0x0435*/ 297,
+ /*0x04b5*/ 378,
+ /*0x01f5*/ 160,
+ /*0x10435*/ 1140,
+ /*0x03b7*/ 252,
+ /*0x1e35*/ 639,
+ /*0x1eb5*/ 698,
+ /*0x0135*/ 81,
+ /*0x2cb5*/ 930,
+ /*0x10cc7*/ 1210,
+ -1, -1,
+ /*0x024b*/ 196,
+ -1,
+ /*0x16e61*/ 1287,
+ -1, -1, -1,
+ /*0x03b5*/ 250,
+ /*0xa64d*/ 1003,
+ -1, -1,
+ /*0x00eb*/ 36,
+ /*0x10cc5*/ 1208,
+ /*0x2c73*/ 902,
+ /*0x118c9*/ 1263,
+ /*0x13b3*/ 543,
+ /*0x0433*/ 295,
+ /*0x04b3*/ 377,
+ /*0x1e932*/ 1334,
+ /*0x10433*/ 1138,
+ -1,
+ /*0x1e33*/ 638,
+ /*0x1eb3*/ 697,
+ /*0x0133*/ 80,
+ /*0x2cb3*/ 929,
+ -1,
+ /*0x00ef*/ 40,
+ /*0x16e63*/ 1289,
+ -1,
+ /*0x2c3a*/ 859,
+ /*0xa64b*/ 1002,
+ /*0x13c0*/ 556,
+ /*0x0440*/ 308,
+ /*0xa73f*/ 1047,
+ /*0x03b3*/ 248,
+ /*0x10440*/ 1151,
+ -1,
+ /*0x16e65*/ 1291,
+ /*0x2c55*/ 886,
+ /*0x0140*/ 86,
+ /*0x10ce6*/ 1241,
+ /*0x01c9*/ 139,
+ -1, -1,
+ /*0x1f43*/ 769,
+ /*0x10e6*/ 500,
+ -1,
+ /*0x16e69*/ 1295,
+ -1,
+ /*0x028c*/ 232,
+ /*0x03c0*/ 261,
+ -1,
+ /*0xa643*/ 998,
+ -1,
+ /*0x0479*/ 352,
+ /*0x04f9*/ 412,
+ /*0x0079*/ 23,
+ /*0x16e67*/ 1293,
+ /*0x104f9*/ 1200,
+ /*0x1e79*/ 673,
+ /*0x1ef9*/ 732,
+ /*0xa737*/ 1043,
+ /*0x0511*/ 424,
+ /*0x118cd*/ 1267,
+ /*0x1d79*/ 610,
+ /*0x021d*/ 179,
+ /*0x1f41*/ 767,
+ -1, -1,
+ /*0x2c4f*/ 880,
+ -1,
+ /*0x10cec*/ 1247,
+ -1, -1,
+ /*0xa641*/ 997,
+ /*0xa735*/ 1042,
+ /*0x10ec*/ 506,
+ /*0x2171*/ 807,
+ /*0x00ed*/ 38,
+ -1,
+ /*0x0247*/ 194,
+ /*0x1f24*/ 754,
+ /*0x13ad*/ 537,
+ /*0x2c32*/ 851,
+ /*0x04ad*/ 374,
+ /*0x118cb*/ 1265,
+ /*0x1042d*/ 1132,
+ /*0x2d1a*/ 983,
+ /*0x1e2d*/ 635,
+ /*0x1ead*/ 694,
+ /*0x012d*/ 78,
+ /*0x2cad*/ 926,
+ -1, -1,
+ /*0x0288*/ 228,
+ /*0x029e*/ 235,
+ -1,
+ /*0x13a5*/ 529,
+ -1,
+ /*0x04a5*/ 370,
+ /*0x0584*/ 475,
+ /*0x03ad*/ 243,
+ /*0xa733*/ 1041,
+ /*0x1e25*/ 631,
+ /*0x1ea5*/ 690,
+ /*0x0125*/ 74,
+ /*0x2ca5*/ 922,
+ /*0x118c3*/ 1257,
+ -1,
+ /*0xa647*/ 1000,
+ /*0x2c49*/ 874,
+ /*0x13a3*/ 527,
+ -1,
+ /*0x04a3*/ 369,
+ -1,
+ /*0x1f45*/ 771,
+ /*0x2173*/ 809,
+ /*0x1e23*/ 630,
+ /*0x1ea3*/ 689,
+ /*0x0123*/ 73,
+ /*0x2ca3*/ 921,
+ /*0xff59*/ 1125,
+ /*0x0266*/ 213,
+ /*0xa645*/ 999,
+ -1, -1,
+ /*0x048f*/ 359,
+ -1, -1,
+ /*0x118c1*/ 1255,
+ /*0x1e0f*/ 620,
+ /*0x1e8f*/ 684,
+ /*0x010f*/ 63,
+ /*0x2c8f*/ 911,
+ /*0xa69b*/ 1033,
+ -1, -1, -1,
+ /*0x1e943*/ 1351,
+ /*0xa68b*/ 1025,
+ -1, -1,
+ /*0x023f*/ 191,
+ /*0x1f66*/ 782,
+ -1,
+ /*0x10cc0*/ 1203,
+ -1, -1,
+ /*0x1fe1*/ 803,
+ -1,
+ /*0x0481*/ 356,
+ /*0x2d1c*/ 985,
+ -1,
+ /*0x026c*/ 218,
+ /*0x1e01*/ 613,
+ /*0x1e81*/ 677,
+ /*0x0101*/ 56,
+ /*0x2c81*/ 904,
+ -1,
+ /*0x2c4d*/ 878,
+ /*0x1e941*/ 1349,
+ /*0x0280*/ 224,
+ /*0x16e6b*/ 1297,
+ /*0x2175*/ 811,
+ /*0x118c7*/ 1261,
+ /*0x0282*/ 225,
+ -1, -1, -1,
+ /*0xa72d*/ 1039,
+ -1,
+ /*0x051d*/ 430,
+ /*0x10f9*/ 519,
+ -1, -1,
+ /*0x1e924*/ 1320,
+ -1,
+ /*0x16e6f*/ 1301,
+ /*0x118c5*/ 1259,
+ /*0x00f1*/ 42,
+ -1,
+ /*0x2c4b*/ 876,
+ /*0x1fe5*/ 804,
+ -1,
+ /*0xa725*/ 1035,
+ -1,
+ /*0x13a7*/ 531,
+ -1,
+ /*0x04a7*/ 371,
+ /*0x1f22*/ 752,
+ /*0x2d04*/ 961,
+ /*0x1f37*/ 765,
+ /*0x1e27*/ 632,
+ /*0x1ea7*/ 691,
+ /*0x0127*/ 75,
+ /*0x2ca7*/ 923,
+ -1, -1,
+ /*0xa723*/ 1034,
+ -1,
+ /*0x2d11*/ 974,
+ -1, -1,
+ /*0x2c43*/ 868,
+ -1,
+ /*0x1f35*/ 763,
+ /*0x00ee*/ 39,
+ -1,
+ /*0x047b*/ 353,
+ /*0x04fb*/ 413,
+ -1,
+ /*0x0233*/ 189,
+ /*0x104fb*/ 1202,
+ /*0x1e7b*/ 674,
+ /*0x1efb*/ 733,
+ /*0x13b1*/ 541,
+ /*0x0431*/ 293,
+ /*0x04b1*/ 376,
+ /*0x00f3*/ 44,
+ /*0x10431*/ 1136,
+ -1,
+ /*0x1e31*/ 637,
+ /*0x1eb1*/ 696,
+ -1,
+ /*0x2cb1*/ 928,
+ /*0x2c41*/ 866,
+ /*0x03fb*/ 291,
+ /*0x0240*/ 192,
+ /*0x0566*/ 445,
+ /*0x16e6d*/ 1299,
+ /*0x047d*/ 354,
+ /*0x04fd*/ 414,
+ /*0x1f33*/ 761,
+ -1,
+ /*0x03b1*/ 246,
+ /*0x1e7d*/ 675,
+ /*0x1efd*/ 734,
+ /*0xff57*/ 1123,
+ /*0x047f*/ 355,
+ /*0x04ff*/ 415,
+ /*0x1d7d*/ 611,
+ -1, -1,
+ /*0x1e7f*/ 676,
+ /*0x1eff*/ 735,
+ /*0x13bd*/ 553,
+ /*0x043d*/ 305,
+ /*0x04bd*/ 382,
+ /*0x1f40*/ 766,
+ /*0x1043d*/ 1148,
+ /*0xa791*/ 1080,
+ /*0x1e3d*/ 643,
+ /*0x1ebd*/ 702,
+ /*0x01bf*/ 137,
+ /*0x2cbd*/ 934,
+ -1, -1,
+ /*0x1e93f*/ 1347,
+ -1,
+ /*0x056c*/ 451,
+ /*0x2c47*/ 872,
+ -1, -1, -1,
+ /*0x03bd*/ 258,
+ /*0x00f5*/ 46,
+ -1,
+ /*0x007a*/ 24,
+ -1,
+ /*0x104fa*/ 1201,
+ /*0x1f79*/ 793,
+ -1,
+ /*0x017a*/ 114,
+ /*0xa727*/ 1036,
+ /*0x2c45*/ 870,
+ /*0x13b9*/ 549,
+ /*0x0439*/ 301,
+ /*0x04b9*/ 380,
+ /*0x022d*/ 186,
+ /*0x10439*/ 1144,
+ -1,
+ /*0x1e39*/ 641,
+ /*0x1eb9*/ 700,
+ /*0x1e922*/ 1318,
+ /*0x2cb9*/ 932,
+ /*0x1e937*/ 1339,
+ -1,
+ /*0x13c2*/ 558,
+ /*0x0442*/ 310,
+ /*0x04c2*/ 384,
+ -1,
+ /*0x10442*/ 1153,
+ -1,
+ /*0x0225*/ 182,
+ /*0x03b9*/ 254,
+ /*0x0142*/ 87,
+ /*0x13d0*/ 572,
+ /*0x0450*/ 324,
+ -1,
+ /*0x1e935*/ 1337,
+ /*0x13f2*/ 606,
+ -1,
+ /*0x2c66*/ 898,
+ /*0x0072*/ 16,
+ /*0x2d24*/ 993,
+ /*0x104f2*/ 1193,
+ -1,
+ /*0x0223*/ 181,
+ -1,
+ /*0x2d1d*/ 986,
+ /*0x24d0*/ 823,
+ /*0x118c0*/ 1254,
+ /*0xff51*/ 1117,
+ -1,
+ /*0x1f25*/ 755,
+ -1, -1,
+ /*0xa7c3*/ 1099,
+ -1,
+ /*0x03f2*/ 288,
+ /*0x020f*/ 172,
+ -1,
+ /*0x2c3f*/ 864,
+ -1,
+ /*0xa77f*/ 1074,
+ -1,
+ /*0x1e933*/ 1335,
+ /*0xff53*/ 1119,
+ /*0x1f23*/ 753,
+ -1,
+ /*0x16e71*/ 1303,
+ -1,
+ /*0xa73d*/ 1046,
+ /*0x2c6c*/ 901,
+ /*0x13bb*/ 551,
+ /*0x043b*/ 303,
+ /*0x04bb*/ 381,
+ -1,
+ /*0x1043b*/ 1146,
+ -1,
+ /*0x1e3b*/ 642,
+ /*0x1ebb*/ 701,
+ /*0x1e940*/ 1348,
+ /*0x2cbb*/ 933,
+ /*0x0201*/ 165,
+ -1, -1, -1,
+ /*0x10fd*/ 521,
+ -1,
+ /*0x2c37*/ 856,
+ /*0xa77a*/ 1072,
+ -1,
+ /*0x03bb*/ 256,
+ -1,
+ /*0x0579*/ 464,
+ /*0x10ff*/ 523,
+ /*0x16e6e*/ 1300,
+ -1,
+ /*0xa79d*/ 1086,
+ /*0x01f9*/ 161,
+ /*0x017c*/ 115,
+ /*0xa739*/ 1044,
+ -1,
+ /*0x2c35*/ 854,
+ /*0x1f01*/ 737,
+ /*0x13af*/ 539,
+ -1,
+ /*0x04af*/ 375,
+ /*0x16e73*/ 1305,
+ /*0x1042f*/ 1134,
+ -1,
+ /*0x1e2f*/ 636,
+ /*0x1eaf*/ 695,
+ /*0x012f*/ 79,
+ /*0x2caf*/ 927,
+ -1, -1,
+ /*0x1e05*/ 615,
+ /*0x1e85*/ 679,
+ /*0x0105*/ 58,
+ /*0x2c85*/ 906,
+ /*0x0227*/ 183,
+ /*0x10fa*/ 520,
+ /*0x052d*/ 438,
+ /*0x03af*/ 245,
+ -1, -1,
+ /*0x13a9*/ 533,
+ /*0x01ad*/ 131,
+ /*0x04a9*/ 372,
+ /*0x2c33*/ 852,
+ /*0x10429*/ 1128,
+ /*0x1e92d*/ 1329,
+ /*0x1e29*/ 633,
+ /*0x1ea9*/ 692,
+ /*0x0129*/ 76,
+ /*0x2ca9*/ 924,
+ -1,
+ /*0x0525*/ 434,
+ -1,
+ /*0x10cc2*/ 1205,
+ -1,
+ /*0x1f27*/ 757,
+ /*0x01a5*/ 129,
+ -1, -1,
+ /*0x2c40*/ 865,
+ /*0x1e925*/ 1321,
+ -1,
+ /*0x10cd0*/ 1219,
+ /*0x0231*/ 188,
+ /*0x2d22*/ 991,
+ /*0x0523*/ 433,
+ /*0x10cf2*/ 1253,
+ /*0x10d0*/ 478,
+ /*0x16e75*/ 1307,
+ -1,
+ /*0x01a3*/ 128,
+ /*0x10f2*/ 512,
+ -1,
+ /*0xa73b*/ 1045,
+ /*0x1e923*/ 1319,
+ /*0x1fd1*/ 801,
+ /*0x1f7b*/ 795,
+ /*0x027d*/ 223,
+ /*0x050f*/ 423,
+ -1,
+ /*0xff55*/ 1121,
+ /*0x13ce*/ 570,
+ /*0x044e*/ 322,
+ /*0x04ce*/ 390,
+ /*0x1f31*/ 759,
+ /*0x1044e*/ 1165,
+ -1, -1,
+ /*0xa7bf*/ 1098,
+ /*0x0477*/ 351,
+ /*0x04f7*/ 411,
+ /*0x0077*/ 21,
+ /*0xa77c*/ 1073,
+ /*0x104f7*/ 1198,
+ /*0x1e77*/ 672,
+ /*0x1ef7*/ 731,
+ /*0x0177*/ 113,
+ -1,
+ /*0x1f7d*/ 797,
+ -1,
+ /*0x03ce*/ 274,
+ -1,
+ /*0x0501*/ 416,
+ -1, -1,
+ /*0xa72f*/ 1040,
+ /*0x1e03*/ 614,
+ /*0x1e83*/ 678,
+ /*0x0103*/ 57,
+ /*0x2c83*/ 905,
+ /*0x13e8*/ 596,
+ /*0xff4f*/ 1115,
+ -1,
+ /*0x0068*/ 7,
+ -1,
+ /*0x104e8*/ 1183,
+ /*0xa7b7*/ 1094,
+ /*0x13c6*/ 562,
+ /*0x0446*/ 314,
+ /*0x04c6*/ 386,
+ -1,
+ /*0x10446*/ 1157,
+ -1,
+ /*0x13f0*/ 604,
+ /*0x24e8*/ 847,
+ /*0x0146*/ 89,
+ /*0x0070*/ 14,
+ /*0xa729*/ 1037,
+ /*0x104f0*/ 1191,
+ -1,
+ /*0xa7b5*/ 1093,
+ -1, -1,
+ /*0x1f7a*/ 794,
+ -1,
+ /*0x0242*/ 193,
+ /*0x03c6*/ 266,
+ -1, -1,
+ /*0x0499*/ 364,
+ /*0x0527*/ 435,
+ -1, -1,
+ /*0x1e19*/ 625,
+ /*0x0250*/ 199,
+ /*0x0119*/ 68,
+ /*0x2c99*/ 916,
+ -1,
+ /*0x0272*/ 221,
+ /*0x1e927*/ 1323,
+ /*0x0581*/ 472,
+ -1,
+ /*0xff49*/ 1109,
+ -1, -1,
+ /*0x037b*/ 239,
+ /*0x1f42*/ 768,
+ -1, -1,
+ /*0x00e6*/ 31,
+ -1,
+ /*0x057b*/ 466,
+ -1,
+ /*0x13c4*/ 560,
+ /*0x0444*/ 312,
+ /*0x04c4*/ 385,
+ /*0x01fb*/ 162,
+ /*0x10444*/ 1155,
+ -1,
+ /*0x1f72*/ 786,
+ -1,
+ /*0x0144*/ 88,
+ -1, -1,
+ /*0x2d2d*/ 996,
+ -1, -1,
+ /*0x037d*/ 241,
+ /*0x1e931*/ 1333,
+ -1, -1, -1,
+ /*0x03c4*/ 264,
+ /*0x057d*/ 468,
+ /*0x2179*/ 815,
+ /*0x13d6*/ 578,
+ /*0x0456*/ 330,
+ -1,
+ /*0x01fd*/ 163,
+ /*0x2d25*/ 994,
+ /*0x00ec*/ 37,
+ /*0x057f*/ 470,
+ -1, -1,
+ /*0x029d*/ 234,
+ /*0x10cce*/ 1217,
+ /*0x01ff*/ 164,
+ -1, -1,
+ /*0x24d6*/ 829,
+ -1,
+ /*0xff4d*/ 1113,
+ -1,
+ /*0x2d23*/ 992,
+ /*0x01bd*/ 136,
+ /*0x0495*/ 362,
+ -1,
+ /*0x10f7*/ 517,
+ /*0x1e93d*/ 1345,
+ /*0x1e15*/ 623,
+ /*0x1e95*/ 687,
+ /*0x0115*/ 66,
+ /*0x2c95*/ 914,
+ -1,
+ /*0x022f*/ 187,
+ -1,
+ /*0x2d0f*/ 972,
+ -1,
+ /*0x057a*/ 465,
+ /*0x118c2*/ 1256,
+ /*0x0205*/ 167,
+ -1,
+ /*0x1f7c*/ 796,
+ /*0xff4b*/ 1111,
+ /*0x10ce8*/ 1243,
+ -1, -1, -1,
+ /*0x118d0*/ 1270,
+ /*0x10e8*/ 502,
+ /*0x13ea*/ 598,
+ /*0x10cc6*/ 1209,
+ -1,
+ /*0x006a*/ 8,
+ /*0x01b9*/ 135,
+ /*0x104ea*/ 1185,
+ /*0x0229*/ 184,
+ /*0x10cf0*/ 1251,
+ /*0x1e939*/ 1341,
+ /*0xa7a5*/ 1090,
+ /*0x2d01*/ 958,
+ /*0x1f05*/ 741,
+ /*0x10f0*/ 510,
+ /*0x2c31*/ 850,
+ -1,
+ /*0xff43*/ 1103,
+ -1, -1, -1, -1,
+ /*0x1e942*/ 1350,
+ -1, -1,
+ /*0xa7a3*/ 1089,
+ /*0x0572*/ 457,
+ /*0x01d0*/ 142,
+ /*0x13a1*/ 525,
+ -1,
+ /*0x04a1*/ 368,
+ -1, -1, -1,
+ /*0x1e21*/ 629,
+ /*0x1ea1*/ 688,
+ /*0x0121*/ 72,
+ /*0x2ca1*/ 920,
+ /*0xa691*/ 1028,
+ /*0xff41*/ 1101,
+ /*0x1e07*/ 616,
+ /*0x1e87*/ 680,
+ /*0x0107*/ 59,
+ /*0x2c87*/ 907,
+ -1,
+ /*0x2c3d*/ 862,
+ -1,
+ /*0x0493*/ 361,
+ -1,
+ /*0x10cc4*/ 1207,
+ /*0x2d27*/ 995,
+ /*0x1e13*/ 622,
+ /*0x1e93*/ 686,
+ /*0x0113*/ 65,
+ /*0x2c93*/ 913,
+ -1, -1,
+ /*0x13ab*/ 535,
+ /*0x00f9*/ 49,
+ /*0x04ab*/ 373,
+ -1,
+ /*0x1042b*/ 1130,
+ /*0xa781*/ 1075,
+ /*0x1e2b*/ 634,
+ /*0x1eab*/ 693,
+ /*0x012b*/ 77,
+ /*0x2cab*/ 925,
+ /*0x13e4*/ 592,
+ /*0x0203*/ 166,
+ /*0x1e93b*/ 1343,
+ /*0x0064*/ 3,
+ /*0x10cd6*/ 1225,
+ /*0x104e4*/ 1179,
+ /*0x037c*/ 240,
+ /*0xff47*/ 1107,
+ /*0x2c39*/ 858,
+ /*0x10d6*/ 484,
+ /*0x1f77*/ 791,
+ /*0x0268*/ 214,
+ /*0x057c*/ 467,
+ /*0x13e2*/ 590,
+ /*0x24e4*/ 843,
+ /*0x16e66*/ 1292,
+ /*0x0062*/ 1,
+ -1,
+ /*0x104e2*/ 1177,
+ -1,
+ /*0x2c42*/ 867,
+ /*0xff45*/ 1105,
+ /*0x1f03*/ 739,
+ -1, -1,
+ /*0x052f*/ 439,
+ -1,
+ /*0x24e2*/ 841,
+ -1,
+ /*0x2c50*/ 881,
+ -1,
+ /*0x0505*/ 418,
+ -1,
+ /*0xa7a7*/ 1091,
+ /*0x1e92f*/ 1331,
+ -1,
+ /*0x0185*/ 119,
+ /*0x13e0*/ 588,
+ /*0x0219*/ 177,
+ /*0x13da*/ 582,
+ /*0x045a*/ 334,
+ -1,
+ /*0x104e0*/ 1175,
+ /*0x217b*/ 817,
+ /*0x104da*/ 1169,
+ /*0x1f70*/ 784,
+ /*0x16e6c*/ 1298,
+ /*0x0529*/ 436,
+ /*0x0078*/ 22,
+ /*0x10cea*/ 1245,
+ /*0x104f8*/ 1199,
+ /*0x24e0*/ 839,
+ -1,
+ /*0x24da*/ 833,
+ /*0x10ea*/ 504,
+ -1,
+ /*0x1e929*/ 1325,
+ /*0x13dc*/ 584,
+ /*0x045c*/ 336,
+ /*0x13cc*/ 568,
+ /*0x044c*/ 320,
+ /*0x04cc*/ 389,
+ /*0x104dc*/ 1171,
+ /*0x1044c*/ 1163,
+ /*0x03f8*/ 290,
+ /*0x217d*/ 819,
+ /*0x118ce*/ 1268,
+ /*0x2c3b*/ 860,
+ -1,
+ /*0x13d8*/ 580,
+ /*0x0458*/ 332,
+ /*0x24dc*/ 835,
+ -1,
+ /*0x217f*/ 821,
+ /*0x104d8*/ 1167,
+ -1, -1,
+ /*0xa72b*/ 1038,
+ /*0x03cc*/ 272,
+ /*0x0585*/ 476,
+ /*0x13d4*/ 576,
+ /*0x0454*/ 328,
+ -1,
+ /*0x24d8*/ 831,
+ -1,
+ /*0x1f44*/ 770,
+ /*0x0256*/ 204,
+ /*0x13d2*/ 574,
+ /*0x0452*/ 326,
+ /*0x0377*/ 238,
+ -1, -1,
+ /*0xa7bd*/ 1097,
+ /*0x01ce*/ 141,
+ /*0x24d4*/ 827,
+ /*0x0577*/ 462,
+ -1, -1,
+ /*0x017e*/ 116,
+ /*0x0497*/ 363,
+ /*0x217a*/ 816,
+ /*0x24d2*/ 825,
+ /*0x118c6*/ 1260,
+ /*0x1e17*/ 624,
+ /*0x0215*/ 175,
+ /*0x0117*/ 67,
+ /*0x2c97*/ 915,
+ /*0x0503*/ 417,
+ -1,
+ /*0x0076*/ 20,
+ /*0x13f4*/ 608,
+ /*0x104f6*/ 1197,
+ /*0x0183*/ 118,
+ /*0x0074*/ 18,
+ /*0x10ce4*/ 1239,
+ /*0x104f4*/ 1195,
+ -1,
+ /*0x0568*/ 447,
+ -1,
+ /*0x10e4*/ 498,
+ /*0x13bc*/ 552,
+ /*0x043c*/ 304,
+ /*0xa7b9*/ 1095,
+ -1,
+ /*0x1043c*/ 1147,
+ /*0x1f15*/ 749,
+ -1,
+ /*0x10ce2*/ 1237,
+ /*0x013c*/ 84,
+ /*0x01c6*/ 138,
+ /*0x0570*/ 455,
+ /*0x026a*/ 216,
+ /*0x10e2*/ 496,
+ /*0x13c8*/ 564,
+ /*0x0448*/ 316,
+ /*0x04c8*/ 387,
+ /*0x2172*/ 808,
+ /*0x10448*/ 1159,
+ -1,
+ /*0x03bc*/ 257,
+ -1,
+ /*0x0148*/ 90,
+ /*0x16e79*/ 1311,
+ -1,
+ /*0x0519*/ 428,
+ /*0x00fb*/ 51,
+ -1, -1,
+ /*0x118c4*/ 1258,
+ /*0x0199*/ 124,
+ -1,
+ /*0x10ce0*/ 1235,
+ /*0x03c8*/ 268,
+ /*0x10cda*/ 1229,
+ -1,
+ /*0x0583*/ 474,
+ /*0x10e0*/ 494,
+ -1,
+ /*0x10da*/ 488,
+ -1, -1, -1,
+ /*0x2c4e*/ 879,
+ /*0x0207*/ 168,
+ /*0x10f8*/ 518,
+ -1, -1,
+ /*0x00fd*/ 53,
+ -1,
+ /*0x2d05*/ 962,
+ /*0x118d6*/ 1276,
+ /*0x10cdc*/ 1231,
+ -1,
+ /*0x10ccc*/ 1215,
+ /*0x0213*/ 174,
+ /*0x00ff*/ 55,
+ /*0x10dc*/ 490,
+ -1,
+ /*0x1f21*/ 751,
+ -1, -1,
+ /*0xa7bb*/ 1096,
+ -1,
+ /*0x10cd8*/ 1227,
+ /*0x1f07*/ 743,
+ -1,
+ /*0x022b*/ 185,
+ -1,
+ /*0x10d8*/ 486,
+ /*0x217c*/ 818,
+ -1,
+ /*0x2c68*/ 899,
+ -1, -1,
+ /*0x10cd4*/ 1223,
+ /*0x1f13*/ 747,
+ -1,
+ /*0x01d6*/ 145,
+ /*0x2c46*/ 871,
+ /*0x10d4*/ 482,
+ -1,
+ /*0x10cd2*/ 1221,
+ /*0x00fa*/ 50,
+ /*0x13ca*/ 566,
+ /*0x044a*/ 318,
+ /*0x04ca*/ 388,
+ /*0x10d2*/ 480,
+ /*0x1044a*/ 1161,
+ /*0x2184*/ 822,
+ /*0x10fe*/ 522,
+ /*0x0515*/ 426,
+ -1, -1, -1, -1,
+ /*0x0195*/ 123,
+ -1,
+ /*0x1f64*/ 780,
+ -1,
+ /*0xa785*/ 1077,
+ /*0x13b6*/ 546,
+ /*0x0436*/ 298,
+ /*0x03ca*/ 270,
+ -1,
+ /*0x10436*/ 1141,
+ /*0x10f6*/ 516,
+ -1,
+ /*0x13b4*/ 544,
+ /*0x0434*/ 296,
+ /*0x10f4*/ 514,
+ /*0x1f62*/ 778,
+ /*0x10434*/ 1139,
+ -1,
+ /*0x0260*/ 209,
+ -1,
+ /*0xa7a9*/ 1092,
+ /*0x048d*/ 358,
+ /*0x056a*/ 449,
+ /*0x00f2*/ 43,
+ /*0x03b6*/ 251,
+ /*0x1e0d*/ 619,
+ /*0x1e8d*/ 683,
+ /*0x010d*/ 62,
+ /*0x2c8d*/ 910,
+ /*0x2c44*/ 869,
+ /*0x2d03*/ 960,
+ /*0x03b4*/ 249,
+ /*0x10cc8*/ 1211,
+ /*0x1e09*/ 617,
+ /*0x1e89*/ 681,
+ /*0x0109*/ 60,
+ /*0x2c89*/ 908,
+ -1,
+ /*0x025c*/ 208,
+ /*0x1f60*/ 776,
+ -1, -1,
+ /*0x13b0*/ 540,
+ /*0x0430*/ 292,
+ /*0x13de*/ 586,
+ /*0x045e*/ 338,
+ /*0x10430*/ 1135,
+ /*0x1f78*/ 792,
+ /*0x0521*/ 432,
+ /*0x104de*/ 1173,
+ /*0x214e*/ 805,
+ /*0x2c56*/ 887,
+ -1,
+ /*0x01a1*/ 127,
+ /*0x0507*/ 419,
+ -1,
+ /*0x049f*/ 367,
+ /*0x2177*/ 813,
+ /*0x24de*/ 837,
+ -1,
+ /*0x1e1f*/ 628,
+ /*0x0254*/ 203,
+ /*0x011f*/ 71,
+ /*0x2c9f*/ 919,
+ -1,
+ /*0x0513*/ 425,
+ -1,
+ /*0x2d19*/ 982,
+ /*0x0252*/ 201,
+ -1, -1, -1, -1, -1, -1,
+ -1,
+ /*0x00fc*/ 52,
+ /*0x052b*/ 437,
+ /*0x0217*/ 176,
+ -1,
+ /*0xa783*/ 1076,
+ /*0x16e7b*/ 1313,
+ -1, -1, -1, -1,
+ /*0x1e92b*/ 1327,
+ /*0x0564*/ 443,
+ -1, -1, -1, -1,
+ /*0x2170*/ 806,
+ -1, -1, -1,
+ /*0x2c6a*/ 900,
+ -1, -1, -1,
+ /*0x0562*/ 441,
+ /*0x023c*/ 190,
+ /*0x10cca*/ 1213,
+ /*0x16e7d*/ 1315,
+ /*0x118da*/ 1280,
+ -1, -1, -1,
+ /*0x1f76*/ 790,
+ -1, -1,
+ /*0x16e7f*/ 1317,
+ /*0x1f74*/ 788,
+ -1, -1, -1, -1,
+ /*0xa799*/ 1084,
+ -1, -1,
+ /*0xa68f*/ 1027,
+ -1,
+ /*0x118dc*/ 1282,
+ -1,
+ /*0x118cc*/ 1266,
+ -1, -1, -1, -1, -1, -1,
+ /*0x01da*/ 147,
+ /*0x0578*/ 463,
+ /*0x2d15*/ 978,
+ /*0x118d8*/ 1278,
+ -1, -1, -1,
+ /*0x16e7a*/ 1312,
+ -1, -1, -1, -1, -1,
+ /*0xa681*/ 1020,
+ /*0x118d4*/ 1274,
+ -1, -1, -1,
+ /*0x01dc*/ 148,
+ -1,
+ /*0x01cc*/ 140,
+ /*0x118d2*/ 1272,
+ -1, -1, -1, -1, -1,
+ /*0x10cde*/ 1233,
+ -1, -1,
+ /*0x01d8*/ 146,
+ -1,
+ /*0x10de*/ 492,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1,
+ /*0x01d4*/ 144,
+ -1,
+ /*0x16e72*/ 1304,
+ -1, -1,
+ /*0x057e*/ 469,
+ -1,
+ /*0x01d2*/ 143,
+ -1,
+ /*0x00e8*/ 33,
+ -1, -1,
+ /*0x0517*/ 427,
+ -1,
+ /*0x2d21*/ 990,
+ -1, -1, -1, -1, -1,
+ /*0x2d07*/ 964,
+ /*0x0576*/ 461,
+ /*0x00f0*/ 41,
+ /*0xff42*/ 1102,
+ -1,
+ /*0x0574*/ 459,
+ /*0x118c8*/ 1262,
+ -1,
+ /*0x2c5a*/ 891,
+ -1, -1,
+ /*0x2d13*/ 976,
+ /*0xff50*/ 1116,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ /*0x020d*/ 171,
+ /*0x1e93c*/ 1344,
+ -1,
+ /*0x2c5c*/ 893,
+ -1,
+ /*0x2c4c*/ 877,
+ -1,
+ /*0x1f36*/ 764,
+ /*0x0209*/ 169,
+ -1, -1, -1,
+ /*0x16e7c*/ 1314,
+ -1,
+ /*0x1f34*/ 762,
+ /*0x2c58*/ 889,
+ -1, -1, -1,
+ /*0x1fb1*/ 799,
+ -1,
+ /*0xa7a1*/ 1088,
+ -1, -1, -1, -1,
+ /*0x2c54*/ 885,
+ /*0xa787*/ 1078,
+ -1, -1, -1, -1, -1,
+ /*0x2c52*/ 883,
+ -1,
+ /*0x021f*/ 180,
+ -1, -1,
+ /*0xa793*/ 1081,
+ -1, -1, -1, -1, -1,
+ /*0x1f30*/ 758,
+ -1, -1, -1,
+ /*0x0283*/ 226,
+ -1, -1, -1,
+ /*0x2c76*/ 903,
+ /*0x118ca*/ 1264,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ /*0x2c3c*/ 861,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ /*0x2c48*/ 873,
+ -1, -1, -1, -1, -1, -1,
+ /*0x00ea*/ 35,
+ -1, -1, -1, -1, -1, -1,
+ -1,
+ /*0x2178*/ 814,
+ /*0x16e77*/ 1309,
+ -1,
+ /*0x01b6*/ 134,
+ -1, -1,
+ /*0x1fd0*/ 800,
+ /*0x1e936*/ 1338,
+ -1, -1,
+ /*0x01b4*/ 133,
+ -1,
+ /*0x050d*/ 422,
+ /*0x2d17*/ 980,
+ /*0x1e934*/ 1336,
+ /*0x118de*/ 1284,
+ -1, -1, -1, -1,
+ /*0x0509*/ 420,
+ -1, -1,
+ /*0x16e68*/ 1294,
+ -1, -1, -1, -1,
+ /*0xff4e*/ 1114,
+ -1, -1, -1, -1, -1, -1,
+ -1,
+ /*0x16e70*/ 1302,
+ -1, -1, -1,
+ /*0x01b0*/ 132,
+ -1, -1, -1,
+ /*0x1e930*/ 1332,
+ /*0x217e*/ 820,
+ -1,
+ /*0x051f*/ 431,
+ -1, -1, -1, -1, -1, -1,
+ -1,
+ /*0x2c4a*/ 875,
+ -1,
+ /*0x00e4*/ 29,
+ -1,
+ /*0xa685*/ 1022,
+ -1,
+ /*0x2176*/ 812,
+ -1,
+ /*0xa797*/ 1083,
+ /*0xff46*/ 1106,
+ /*0x2174*/ 810,
+ -1, -1, -1, -1,
+ /*0x00e2*/ 27,
+ -1,
+ /*0x2c36*/ 855,
+ -1, -1, -1, -1, -1, -1,
+ /*0x2c34*/ 853,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1,
+ /*0x00e0*/ 25,
+ -1, -1, -1, -1, -1, -1,
+ -1,
+ /*0x00f8*/ 48,
+ -1, -1, -1, -1, -1, -1,
+ /*0x2c30*/ 849,
+ /*0xff44*/ 1104,
+ /*0x2c5e*/ 895,
+ -1, -1, -1, -1,
+ /*0x0287*/ 227,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1,
+ /*0xff56*/ 1122,
+ -1, -1,
+ /*0xa683*/ 1021,
+ -1, -1, -1, -1, -1, -1,
+ -1,
+ /*0x16e6a*/ 1296,
+ -1, -1, -1,
+ /*0x00fe*/ 54,
+ -1, -1,
+ /*0x2d0d*/ 970,
+ -1, -1, -1, -1, -1, -1,
+ -1,
+ /*0x2d09*/ 966,
+ -1, -1, -1, -1,
+ /*0x00f6*/ 47,
+ -1, -1, -1,
+ /*0x00f4*/ 45,
+ -1, -1, -1, -1,
+ /*0xa699*/ 1032,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ /*0x2d1f*/ 988,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ /*0x16e64*/ 1290,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ /*0x16e62*/ 1288,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1,
+ /*0xa79f*/ 1087,
+ -1, -1,
+ /*0xa695*/ 1030,
+ -1, -1, -1, -1, -1, -1,
+ /*0x16e60*/ 1286,
+ -1, -1, -1, -1, -1, -1,
+ -1,
+ /*0x16e78*/ 1310,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1,
+ /*0xff5a*/ 1126,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1,
+ /*0xa687*/ 1023,
+ /*0x16e7e*/ 1316,
+ -1, -1,
+ /*0xff4c*/ 1112,
+ -1, -1, -1, -1, -1, -1,
+ /*0xa693*/ 1029,
+ -1, -1,
+ /*0xff58*/ 1124,
+ -1, -1,
+ /*0x16e76*/ 1308,
+ -1, -1, -1,
+ /*0x16e74*/ 1306,
+ -1, -1, -1,
+ /*0xff54*/ 1120,
+ -1, -1, -1, -1, -1, -1,
+ /*0xff52*/ 1118,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1,
+ /*0xff48*/ 1108,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1,
+ /*0x1fe0*/ 802,
+ /*0x0289*/ 229,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1,
+ /*0xa697*/ 1031,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1,
+ /*0xff4a*/ 1110,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ /*0xa68d*/ 1026,
+ -1, -1, -1, -1, -1, -1,
+ -1,
+ /*0xa689*/ 1024,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1,
+ /*0x1fb0*/ 798
+ };
+
+ if (code <= MAX_CODE_VALUE && code >= MIN_CODE_VALUE)
+ {
+ register unsigned int key = onigenc_unicode_CaseUnfold_11_hash(code);
+
+ if (key <= MAX_HASH_VALUE)
+ {
+ register short s = wordlist[key];
+
+ if (s >= 0 && code1_equal(code, CaseUnfold_11_Table[s].from))
+ return &CaseUnfold_11_Table[s].to;
+ }
+ }
+ return 0;
+}
+
+static const CaseUnfold_12_Type CaseUnfold_12_Table[] = {
+#define CaseUnfold_12 (*(CaseUnfold_12_Type (*)[58])(CaseUnfold_12_Table+0))
+ {{0x0061, 0x02be}, {1, {0x1e9a}}},
+ {{0x0066, 0x0066}, {1, {0xfb00}}},
+ {{0x0066, 0x0069}, {1, {0xfb01}}},
+ {{0x0066, 0x006c}, {1, {0xfb02}}},
+ {{0x0068, 0x0331}, {1, {0x1e96}}},
+ {{0x006a, 0x030c}, {1, {0x01f0}}},
+ {{0x0073, 0x0073}, {2, {0x00df, 0x1e9e}}},
+ {{0x0073, 0x0074}, {2, {0xfb05, 0xfb06}}},
+ {{0x0074, 0x0308}, {1, {0x1e97}}},
+ {{0x0077, 0x030a}, {1, {0x1e98}}},
+ {{0x0079, 0x030a}, {1, {0x1e99}}},
+ {{0x02bc, 0x006e}, {1, {0x0149}}},
+ {{0x03ac, 0x03b9}, {1, {0x1fb4}}},
+ {{0x03ae, 0x03b9}, {1, {0x1fc4}}},
+ {{0x03b1, 0x0342}, {1, {0x1fb6}}},
+ {{0x03b1, 0x03b9}, {2, {0x1fb3, 0x1fbc}}},
+ {{0x03b7, 0x0342}, {1, {0x1fc6}}},
+ {{0x03b7, 0x03b9}, {2, {0x1fc3, 0x1fcc}}},
+ {{0x03b9, 0x0342}, {1, {0x1fd6}}},
+ {{0x03c1, 0x0313}, {1, {0x1fe4}}},
+ {{0x03c5, 0x0313}, {1, {0x1f50}}},
+ {{0x03c5, 0x0342}, {1, {0x1fe6}}},
+ {{0x03c9, 0x0342}, {1, {0x1ff6}}},
+ {{0x03c9, 0x03b9}, {2, {0x1ff3, 0x1ffc}}},
+ {{0x03ce, 0x03b9}, {1, {0x1ff4}}},
+ {{0x0565, 0x0582}, {1, {0x0587}}},
+ {{0x0574, 0x0565}, {1, {0xfb14}}},
+ {{0x0574, 0x056b}, {1, {0xfb15}}},
+ {{0x0574, 0x056d}, {1, {0xfb17}}},
+ {{0x0574, 0x0576}, {1, {0xfb13}}},
+ {{0x057e, 0x0576}, {1, {0xfb16}}},
+ {{0x1f00, 0x03b9}, {2, {0x1f80, 0x1f88}}},
+ {{0x1f01, 0x03b9}, {2, {0x1f81, 0x1f89}}},
+ {{0x1f02, 0x03b9}, {2, {0x1f82, 0x1f8a}}},
+ {{0x1f03, 0x03b9}, {2, {0x1f83, 0x1f8b}}},
+ {{0x1f04, 0x03b9}, {2, {0x1f84, 0x1f8c}}},
+ {{0x1f05, 0x03b9}, {2, {0x1f85, 0x1f8d}}},
+ {{0x1f06, 0x03b9}, {2, {0x1f86, 0x1f8e}}},
+ {{0x1f07, 0x03b9}, {2, {0x1f87, 0x1f8f}}},
+ {{0x1f20, 0x03b9}, {2, {0x1f90, 0x1f98}}},
+ {{0x1f21, 0x03b9}, {2, {0x1f91, 0x1f99}}},
+ {{0x1f22, 0x03b9}, {2, {0x1f92, 0x1f9a}}},
+ {{0x1f23, 0x03b9}, {2, {0x1f93, 0x1f9b}}},
+ {{0x1f24, 0x03b9}, {2, {0x1f94, 0x1f9c}}},
+ {{0x1f25, 0x03b9}, {2, {0x1f95, 0x1f9d}}},
+ {{0x1f26, 0x03b9}, {2, {0x1f96, 0x1f9e}}},
+ {{0x1f27, 0x03b9}, {2, {0x1f97, 0x1f9f}}},
+ {{0x1f60, 0x03b9}, {2, {0x1fa0, 0x1fa8}}},
+ {{0x1f61, 0x03b9}, {2, {0x1fa1, 0x1fa9}}},
+ {{0x1f62, 0x03b9}, {2, {0x1fa2, 0x1faa}}},
+ {{0x1f63, 0x03b9}, {2, {0x1fa3, 0x1fab}}},
+ {{0x1f64, 0x03b9}, {2, {0x1fa4, 0x1fac}}},
+ {{0x1f65, 0x03b9}, {2, {0x1fa5, 0x1fad}}},
+ {{0x1f66, 0x03b9}, {2, {0x1fa6, 0x1fae}}},
+ {{0x1f67, 0x03b9}, {2, {0x1fa7, 0x1faf}}},
+ {{0x1f70, 0x03b9}, {1, {0x1fb2}}},
+ {{0x1f74, 0x03b9}, {1, {0x1fc2}}},
+ {{0x1f7c, 0x03b9}, {1, {0x1ff2}}},
+#define CaseUnfold_12_Locale (*(CaseUnfold_12_Type (*)[1])(CaseUnfold_12_Table+58))
+ {{0x0069, 0x0307}, {1, {0x0130}}},
+};
+
+/* ANSI-C code produced by gperf version 3.1 */
+/* Command-line: gperf -7 -k1,2,3,4,5,6 -F,-1 -c -j1 -i1 -t -T -E -C -H onigenc_unicode_CaseUnfold_12_hash -N onigenc_unicode_CaseUnfold_12_lookup -n */
+
+/* maximum key range = 71, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+/*ARGSUSED*/
+static unsigned int
+onigenc_unicode_CaseUnfold_12_hash(const OnigCodePoint *codes)
+{
+ static const unsigned char asso_values[] =
+ {
+ 3, 58, 54, 57, 56, 16, 8, 2, 43, 82,
+ 3, 1, 23, 82, 82, 82, 82, 82, 82, 4,
+ 82, 82, 82, 82, 82, 82, 82, 82, 82, 82,
+ 82, 82, 52, 51, 50, 49, 48, 47, 46, 45,
+ 82, 82, 82, 82, 43, 82, 42, 82, 82, 13,
+ 82, 82, 82, 82, 82, 11, 82, 1, 82, 82,
+ 14, 82, 1, 82, 82, 31, 3, 82, 82, 30,
+ 82, 82, 82, 10, 82, 82, 82, 82, 37, 82,
+ 82, 82, 82, 82, 82, 82, 82, 82, 82, 82,
+ 82, 82, 82, 82, 82, 82, 37, 15, 36, 35,
+ 34, 17, 1, 33, 12, 4, 23, 23, 26, 21,
+ 13, 82, 27, 82, 82, 2, 5, 82, 11, 16,
+ 82, 15, 82, 82, 23, 82, 8, 82
+ };
+ return asso_values[bits_at(codes, 5)] + asso_values[bits_at(codes, 4)] + asso_values[bits_at(codes, 3)] + asso_values[bits_at(codes, 2)] + asso_values[bits_at(codes, 1)] + asso_values[bits_at(codes, 0)];
+}
+
+static const CodePointList2 *
+onigenc_unicode_CaseUnfold_12_lookup(const OnigCodePoint *codes)
+{
+ enum
+ {
+ MIN_CODE_VALUE = 0x61,
+ MAX_CODE_VALUE = 0x1f7c,
+ TOTAL_KEYWORDS = 59,
+ MIN_WORD_LENGTH = 6,
+ MAX_WORD_LENGTH = 6,
+ MIN_HASH_VALUE = 11,
+ MAX_HASH_VALUE = 81
+ };
+
+ static const short wordlist[] =
+ {
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1,
+ /*0x1f66,0x03b9*/ 53,
+ /*0x1f07,0x03b9*/ 38,
+ /*0x1f00,0x03b9*/ 31,
+ /*0x0066,0x0066*/ 1,
+ /*0x1f74,0x03b9*/ 56,
+ /*0x0073,0x0073*/ 6,
+ /*0x0066,0x0069*/ 2,
+ /*0x1f06,0x03b9*/ 37,
+ /*0x0073,0x0074*/ 7,
+ /*0x03b9,0x0342*/ 18,
+ /*0x03c9,0x03b9*/ 23,
+ /*0x03b7,0x03b9*/ 17,
+ /*0x0069,0x0307*/ 58,
+ /*0x03b1,0x03b9*/ 15,
+ /*0x1f61,0x03b9*/ 48,
+ /*0x1f05,0x03b9*/ 36,
+ /*0x1f65,0x03b9*/ 52,
+ /*0x0574,0x0576*/ 29,
+ /*0x03c9,0x0342*/ 22,
+ /*0x03b7,0x0342*/ 16,
+ /*0x057e,0x0576*/ 30,
+ /*0x03b1,0x0342*/ 14,
+ /*0x1f7c,0x03b9*/ 57,
+ /*0x0574,0x0565*/ 26,
+ /*0x0079,0x030a*/ 10,
+ /*0x0077,0x030a*/ 9,
+ /*0x1f70,0x03b9*/ 55,
+ /*0x0574,0x056d*/ 28,
+ /*0x0066,0x006c*/ 3,
+ /*0x0574,0x056b*/ 27,
+ /*0x0061,0x02be*/ 0,
+ /*0x0068,0x0331*/ 4,
+ /*0x1f67,0x03b9*/ 54,
+ /*0x1f64,0x03b9*/ 51,
+ /*0x1f63,0x03b9*/ 50,
+ /*0x1f62,0x03b9*/ 49,
+ /*0x1f60,0x03b9*/ 47,
+ /*0x03ce,0x03b9*/ 24,
+ /*0x03c5,0x0342*/ 21,
+ /*0x03c5,0x0313*/ 20,
+ /*0x03c1,0x0313*/ 19,
+ /*0x02bc,0x006e*/ 11,
+ /*0x03ae,0x03b9*/ 13,
+ /*0x03ac,0x03b9*/ 12,
+ /*0x1f27,0x03b9*/ 46,
+ /*0x1f26,0x03b9*/ 45,
+ /*0x1f25,0x03b9*/ 44,
+ /*0x1f24,0x03b9*/ 43,
+ /*0x1f23,0x03b9*/ 42,
+ /*0x1f22,0x03b9*/ 41,
+ /*0x1f21,0x03b9*/ 40,
+ /*0x1f20,0x03b9*/ 39,
+ /*0x006a,0x030c*/ 5,
+ /*0x1f02,0x03b9*/ 33,
+ /*0x0074,0x0308*/ 8,
+ /*0x1f04,0x03b9*/ 35,
+ /*0x1f03,0x03b9*/ 34,
+ /*0x1f01,0x03b9*/ 32,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ /*0x0565,0x0582*/ 25
+ };
+
+ if (codes[0] <= MAX_CODE_VALUE && codes[0] >= MIN_CODE_VALUE &&
+ codes[1] <= MAX_CODE_VALUE && codes[1] >= MIN_CODE_VALUE)
+ {
+ register unsigned int key = onigenc_unicode_CaseUnfold_12_hash(codes);
+
+ if (key <= MAX_HASH_VALUE)
+ {
+ register short s = wordlist[key];
+
+ if (s >= 0 && code2_equal(codes, CaseUnfold_12_Table[s].from))
+ return &CaseUnfold_12_Table[s].to;
+ }
+ }
+ return 0;
+}
+
+static const CaseUnfold_13_Type CaseUnfold_13_Table[] = {
+#define CaseUnfold_13 (*(CaseUnfold_13_Type (*)[14])(CaseUnfold_13_Table+0))
+ {{0x0066, 0x0066, 0x0069}, {1, {0xfb03}}},
+ {{0x0066, 0x0066, 0x006c}, {1, {0xfb04}}},
+ {{0x03b1, 0x0342, 0x03b9}, {1, {0x1fb7}}},
+ {{0x03b7, 0x0342, 0x03b9}, {1, {0x1fc7}}},
+ {{0x03b9, 0x0308, 0x0300}, {1, {0x1fd2}}},
+ {{0x03b9, 0x0308, 0x0301}, {2, {0x0390, 0x1fd3}}},
+ {{0x03b9, 0x0308, 0x0342}, {1, {0x1fd7}}},
+ {{0x03c5, 0x0308, 0x0300}, {1, {0x1fe2}}},
+ {{0x03c5, 0x0308, 0x0301}, {2, {0x03b0, 0x1fe3}}},
+ {{0x03c5, 0x0308, 0x0342}, {1, {0x1fe7}}},
+ {{0x03c5, 0x0313, 0x0300}, {1, {0x1f52}}},
+ {{0x03c5, 0x0313, 0x0301}, {1, {0x1f54}}},
+ {{0x03c5, 0x0313, 0x0342}, {1, {0x1f56}}},
+ {{0x03c9, 0x0342, 0x03b9}, {1, {0x1ff7}}},
+};
+
+/* ANSI-C code produced by gperf version 3.1 */
+/* Command-line: gperf -7 -k1,2,3,4,5,6,7,8,9 -F,-1 -c -j1 -i1 -t -T -E -C -H onigenc_unicode_CaseUnfold_13_hash -N onigenc_unicode_CaseUnfold_13_lookup -n */
+
+/* maximum key range = 20, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+/*ARGSUSED*/
+static unsigned int
+onigenc_unicode_CaseUnfold_13_hash(const OnigCodePoint *codes)
+{
+ static const unsigned char asso_values[] =
+ {
+ 7, 4, 47, 47, 47, 47, 1, 1, 2, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 1,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 11,
+ 47, 47, 47, 47, 47, 10, 47, 2, 47, 47,
+ 47, 47, 47, 47, 47, 47, 1, 47, 47, 1,
+ 47, 47, 47, 9, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 1, 47, 47, 2, 47, 47, 1, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47
+ };
+ return asso_values[bits_at(codes, 8)] + asso_values[bits_at(codes, 7)] + asso_values[bits_at(codes, 6)] + asso_values[bits_at(codes, 5)] + asso_values[bits_at(codes, 4)] + asso_values[bits_at(codes, 3)] + asso_values[bits_at(codes, 2)] + asso_values[bits_at(codes, 1)] + asso_values[bits_at(codes, 0)];
+}
+
+static const CodePointList2 *
+onigenc_unicode_CaseUnfold_13_lookup(const OnigCodePoint *codes)
+{
+ enum
+ {
+ MIN_CODE_VALUE = 0x66,
+ MAX_CODE_VALUE = 0x3c9,
+ TOTAL_KEYWORDS = 14,
+ MIN_WORD_LENGTH = 9,
+ MAX_WORD_LENGTH = 9,
+ MIN_HASH_VALUE = 27,
+ MAX_HASH_VALUE = 46
+ };
+
+ static const short wordlist[] =
+ {
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1,
+ /*0x03c5,0x0313,0x0342*/ 12,
+ /*0x03c5,0x0308,0x0342*/ 9,
+ /*0x03b9,0x0308,0x0342*/ 6,
+ /*0x03c5,0x0313,0x0301*/ 11,
+ /*0x03c5,0x0308,0x0301*/ 8,
+ /*0x03b9,0x0308,0x0301*/ 5,
+ /*0x03c5,0x0313,0x0300*/ 10,
+ /*0x03c5,0x0308,0x0300*/ 7,
+ /*0x03b9,0x0308,0x0300*/ 4,
+ /*0x03c9,0x0342,0x03b9*/ 13,
+ /*0x03b7,0x0342,0x03b9*/ 3,
+ /*0x03b1,0x0342,0x03b9*/ 2,
+ -1, -1, -1, -1, -1, -1,
+ /*0x0066,0x0066,0x006c*/ 1,
+ /*0x0066,0x0066,0x0069*/ 0
+ };
+
+ if (codes[0] <= MAX_CODE_VALUE && codes[0] >= MIN_CODE_VALUE &&
+ codes[1] <= MAX_CODE_VALUE && codes[1] >= MIN_CODE_VALUE &&
+ codes[2] <= MAX_CODE_VALUE && codes[2] >= MIN_CODE_VALUE)
+ {
+ register unsigned int key = onigenc_unicode_CaseUnfold_13_hash(codes);
+
+ if (key <= MAX_HASH_VALUE)
+ {
+ register short s = wordlist[key];
+
+ if (s >= 0 && code3_equal(codes, CaseUnfold_13_Table[s].from))
+ return &CaseUnfold_13_Table[s].to;
+ }
+ }
+ return 0;
+}
+
+static const OnigCodePoint CaseMappingSpecials[] = {
+ L(1)|0x039C,
+ L(2)|0x0053, 0x0073, L(2)|0x0053, 0x0053,
+ L(2)|0x02BC, 0x004E,
+ L(1)|0x0053,
+ L(1)|0x01C5,
+ L(2)|0x0064, 0x017D, L(1)|0x01C4,
+ L(1)|0x01C8,
+ L(2)|0x006C, 0x004A, L(1)|0x01C7,
+ L(1)|0x01CB,
+ L(2)|0x006E, 0x004A, L(1)|0x01CA,
+ L(2)|0x004A, 0x030C,
+ L(1)|0x01F2,
+ L(2)|0x0064, 0x005A, L(1)|0x01F1,
+ L(1)|0x0399,
+ L(3)|0x0399, 0x0308, 0x0301,
+ L(3)|0x03A5, 0x0308, 0x0301,
+ L(1)|0x03A3,
+ L(1)|0x0392,
+ L(1)|0x0398,
+ L(1)|0x03A6,
+ L(1)|0x03A0,
+ L(1)|0x039A,
+ L(1)|0x03A1,
+ L(1)|0x0395,
+ L(2)|0x0535, 0x0582, L(2)|0x0535, 0x0552,
+ L(1)|0x0412,
+ L(1)|0x0414,
+ L(1)|0x041E,
+ L(1)|0x0421,
+ L(1)|0x0422,
+ L(1)|0x0422,
+ L(1)|0x042A,
+ L(1)|0x0462,
+ L(1)|0xA64A,
+ L(2)|0x0048, 0x0331,
+ L(2)|0x0054, 0x0308,
+ L(2)|0x0057, 0x030A,
+ L(2)|0x0059, 0x030A,
+ L(2)|0x0041, 0x02BE,
+ L(1)|0x1E60,
+ L(1)|0x00DF,
+ L(2)|0x03A5, 0x0313,
+ L(3)|0x03A5, 0x0313, 0x0300,
+ L(3)|0x03A5, 0x0313, 0x0301,
+ L(3)|0x03A5, 0x0313, 0x0342,
+ L(1)|0x1F88, L(2)|0x1F08, 0x0399,
+ L(1)|0x1F89, L(2)|0x1F09, 0x0399,
+ L(1)|0x1F8A, L(2)|0x1F0A, 0x0399,
+ L(1)|0x1F8B, L(2)|0x1F0B, 0x0399,
+ L(1)|0x1F8C, L(2)|0x1F0C, 0x0399,
+ L(1)|0x1F8D, L(2)|0x1F0D, 0x0399,
+ L(1)|0x1F8E, L(2)|0x1F0E, 0x0399,
+ L(1)|0x1F8F, L(2)|0x1F0F, 0x0399,
+ L(2)|0x1F00, 0x0399, L(1)|0x1F80, L(2)|0x1F08, 0x0399,
+ L(2)|0x1F01, 0x0399, L(1)|0x1F81, L(2)|0x1F09, 0x0399,
+ L(2)|0x1F02, 0x0399, L(1)|0x1F82, L(2)|0x1F0A, 0x0399,
+ L(2)|0x1F03, 0x0399, L(1)|0x1F83, L(2)|0x1F0B, 0x0399,
+ L(2)|0x1F04, 0x0399, L(1)|0x1F84, L(2)|0x1F0C, 0x0399,
+ L(2)|0x1F05, 0x0399, L(1)|0x1F85, L(2)|0x1F0D, 0x0399,
+ L(2)|0x1F06, 0x0399, L(1)|0x1F86, L(2)|0x1F0E, 0x0399,
+ L(2)|0x1F07, 0x0399, L(1)|0x1F87, L(2)|0x1F0F, 0x0399,
+ L(1)|0x1F98, L(2)|0x1F28, 0x0399,
+ L(1)|0x1F99, L(2)|0x1F29, 0x0399,
+ L(1)|0x1F9A, L(2)|0x1F2A, 0x0399,
+ L(1)|0x1F9B, L(2)|0x1F2B, 0x0399,
+ L(1)|0x1F9C, L(2)|0x1F2C, 0x0399,
+ L(1)|0x1F9D, L(2)|0x1F2D, 0x0399,
+ L(1)|0x1F9E, L(2)|0x1F2E, 0x0399,
+ L(1)|0x1F9F, L(2)|0x1F2F, 0x0399,
+ L(2)|0x1F20, 0x0399, L(1)|0x1F90, L(2)|0x1F28, 0x0399,
+ L(2)|0x1F21, 0x0399, L(1)|0x1F91, L(2)|0x1F29, 0x0399,
+ L(2)|0x1F22, 0x0399, L(1)|0x1F92, L(2)|0x1F2A, 0x0399,
+ L(2)|0x1F23, 0x0399, L(1)|0x1F93, L(2)|0x1F2B, 0x0399,
+ L(2)|0x1F24, 0x0399, L(1)|0x1F94, L(2)|0x1F2C, 0x0399,
+ L(2)|0x1F25, 0x0399, L(1)|0x1F95, L(2)|0x1F2D, 0x0399,
+ L(2)|0x1F26, 0x0399, L(1)|0x1F96, L(2)|0x1F2E, 0x0399,
+ L(2)|0x1F27, 0x0399, L(1)|0x1F97, L(2)|0x1F2F, 0x0399,
+ L(1)|0x1FA8, L(2)|0x1F68, 0x0399,
+ L(1)|0x1FA9, L(2)|0x1F69, 0x0399,
+ L(1)|0x1FAA, L(2)|0x1F6A, 0x0399,
+ L(1)|0x1FAB, L(2)|0x1F6B, 0x0399,
+ L(1)|0x1FAC, L(2)|0x1F6C, 0x0399,
+ L(1)|0x1FAD, L(2)|0x1F6D, 0x0399,
+ L(1)|0x1FAE, L(2)|0x1F6E, 0x0399,
+ L(1)|0x1FAF, L(2)|0x1F6F, 0x0399,
+ L(2)|0x1F60, 0x0399, L(1)|0x1FA0, L(2)|0x1F68, 0x0399,
+ L(2)|0x1F61, 0x0399, L(1)|0x1FA1, L(2)|0x1F69, 0x0399,
+ L(2)|0x1F62, 0x0399, L(1)|0x1FA2, L(2)|0x1F6A, 0x0399,
+ L(2)|0x1F63, 0x0399, L(1)|0x1FA3, L(2)|0x1F6B, 0x0399,
+ L(2)|0x1F64, 0x0399, L(1)|0x1FA4, L(2)|0x1F6C, 0x0399,
+ L(2)|0x1F65, 0x0399, L(1)|0x1FA5, L(2)|0x1F6D, 0x0399,
+ L(2)|0x1F66, 0x0399, L(1)|0x1FA6, L(2)|0x1F6E, 0x0399,
+ L(2)|0x1F67, 0x0399, L(1)|0x1FA7, L(2)|0x1F6F, 0x0399,
+ L(2)|0x1FBA, 0x0345, L(2)|0x1FBA, 0x0399,
+ L(1)|0x1FBC, L(2)|0x0391, 0x0399,
+ L(2)|0x0386, 0x0345, L(2)|0x0386, 0x0399,
+ L(2)|0x0391, 0x0342,
+ L(3)|0x0391, 0x0342, 0x0345, L(3)|0x0391, 0x0342, 0x0399,
+ L(2)|0x03B1, 0x0399, L(1)|0x1FB3, L(2)|0x0391, 0x0399,
+ L(1)|0x0399,
+ L(2)|0x1FCA, 0x0345, L(2)|0x1FCA, 0x0399,
+ L(1)|0x1FCC, L(2)|0x0397, 0x0399,
+ L(2)|0x0389, 0x0345, L(2)|0x0389, 0x0399,
+ L(2)|0x0397, 0x0342,
+ L(3)|0x0397, 0x0342, 0x0345, L(3)|0x0397, 0x0342, 0x0399,
+ L(2)|0x03B7, 0x0399, L(1)|0x1FC3, L(2)|0x0397, 0x0399,
+ L(3)|0x0399, 0x0308, 0x0300,
+ L(3)|0x0399, 0x0308, 0x0301,
+ L(2)|0x0399, 0x0342,
+ L(3)|0x0399, 0x0308, 0x0342,
+ L(3)|0x03A5, 0x0308, 0x0300,
+ L(3)|0x03A5, 0x0308, 0x0301,
+ L(2)|0x03A1, 0x0313,
+ L(2)|0x03A5, 0x0342,
+ L(3)|0x03A5, 0x0308, 0x0342,
+ L(2)|0x1FFA, 0x0345, L(2)|0x1FFA, 0x0399,
+ L(1)|0x1FFC, L(2)|0x03A9, 0x0399,
+ L(2)|0x038F, 0x0345, L(2)|0x038F, 0x0399,
+ L(2)|0x03A9, 0x0342,
+ L(3)|0x03A9, 0x0342, 0x0345, L(3)|0x03A9, 0x0342, 0x0399,
+ L(2)|0x03C9, 0x0399, L(1)|0x1FF3, L(2)|0x03A9, 0x0399,
+ L(2)|0x0046, 0x0066, L(2)|0x0046, 0x0046,
+ L(2)|0x0046, 0x0069, L(2)|0x0046, 0x0049,
+ L(2)|0x0046, 0x006C, L(2)|0x0046, 0x004C,
+ L(3)|0x0046, 0x0066, 0x0069, L(3)|0x0046, 0x0046, 0x0049,
+ L(3)|0x0046, 0x0066, 0x006C, L(3)|0x0046, 0x0046, 0x004C,
+ L(2)|0x0053, 0x0074, L(2)|0x0053, 0x0054,
+ L(2)|0x0053, 0x0074, L(2)|0x0053, 0x0054,
+ L(2)|0x0544, 0x0576, L(2)|0x0544, 0x0546,
+ L(2)|0x0544, 0x0565, L(2)|0x0544, 0x0535,
+ L(2)|0x0544, 0x056B, L(2)|0x0544, 0x053B,
+ L(2)|0x054E, 0x0576, L(2)|0x054E, 0x0546,
+ L(2)|0x0544, 0x056D, L(2)|0x0544, 0x053D,
+};
diff --git a/enc/unicode/12.1.0/name2ctype.h b/enc/unicode/12.1.0/name2ctype.h
new file mode 100644
index 0000000000..b2270d5cac
--- /dev/null
+++ b/enc/unicode/12.1.0/name2ctype.h
@@ -0,0 +1,41810 @@
+/* ANSI-C code produced by gperf version 3.1 */
+/* Command-line: gperf -7 -c -j1 -i1 -t -C -P -T -H uniname2ctype_hash -Q uniname2ctype_pool -N uniname2ctype_p */
+#ifndef USE_UNICODE_PROPERTIES
+/* Computed positions: -k'1,3' */
+#else /* USE_UNICODE_PROPERTIES */
+/* Computed positions: -k'1-3,5-6,12,16,$' */
+#endif /* USE_UNICODE_PROPERTIES */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646. */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>."
+#endif
+
+
+
+/* 'NEWLINE': [[:NEWLINE:]] */
+static const OnigCodePoint CR_NEWLINE[] = {
+ 1,
+ 0x000a, 0x000a,
+}; /* CR_NEWLINE */
+
+/* 'Alpha': [[:Alpha:]] */
+static const OnigCodePoint CR_Alpha[] = {
+ 679,
+ 0x0041, 0x005a,
+ 0x0061, 0x007a,
+ 0x00aa, 0x00aa,
+ 0x00b5, 0x00b5,
+ 0x00ba, 0x00ba,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00f6,
+ 0x00f8, 0x02c1,
+ 0x02c6, 0x02d1,
+ 0x02e0, 0x02e4,
+ 0x02ec, 0x02ec,
+ 0x02ee, 0x02ee,
+ 0x0345, 0x0345,
+ 0x0370, 0x0374,
+ 0x0376, 0x0377,
+ 0x037a, 0x037d,
+ 0x037f, 0x037f,
+ 0x0386, 0x0386,
+ 0x0388, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03f5,
+ 0x03f7, 0x0481,
+ 0x048a, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x0559,
+ 0x0560, 0x0588,
+ 0x05b0, 0x05bd,
+ 0x05bf, 0x05bf,
+ 0x05c1, 0x05c2,
+ 0x05c4, 0x05c5,
+ 0x05c7, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f2,
+ 0x0610, 0x061a,
+ 0x0620, 0x0657,
+ 0x0659, 0x065f,
+ 0x066e, 0x06d3,
+ 0x06d5, 0x06dc,
+ 0x06e1, 0x06e8,
+ 0x06ed, 0x06ef,
+ 0x06fa, 0x06fc,
+ 0x06ff, 0x06ff,
+ 0x0710, 0x073f,
+ 0x074d, 0x07b1,
+ 0x07ca, 0x07ea,
+ 0x07f4, 0x07f5,
+ 0x07fa, 0x07fa,
+ 0x0800, 0x0817,
+ 0x081a, 0x082c,
+ 0x0840, 0x0858,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x08d4, 0x08df,
+ 0x08e3, 0x08e9,
+ 0x08f0, 0x093b,
+ 0x093d, 0x094c,
+ 0x094e, 0x0950,
+ 0x0955, 0x0963,
+ 0x0971, 0x0983,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bd, 0x09c4,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09cc,
+ 0x09ce, 0x09ce,
+ 0x09d7, 0x09d7,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e3,
+ 0x09f0, 0x09f1,
+ 0x09fc, 0x09fc,
+ 0x0a01, 0x0a03,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a3e, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4c,
+ 0x0a51, 0x0a51,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a70, 0x0a75,
+ 0x0a81, 0x0a83,
+ 0x0a85, 0x0a8d,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0abd, 0x0ac5,
+ 0x0ac7, 0x0ac9,
+ 0x0acb, 0x0acc,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae3,
+ 0x0af9, 0x0afc,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3d, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4c,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b63,
+ 0x0b71, 0x0b71,
+ 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, 0x0bcc,
+ 0x0bd0, 0x0bd0,
+ 0x0bd7, 0x0bd7,
+ 0x0c00, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4c,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c63,
+ 0x0c80, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbd, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccc,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0cf1, 0x0cf2,
+ 0x0d00, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d3a,
+ 0x0d3d, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4c,
+ 0x0d4e, 0x0d4e,
+ 0x0d54, 0x0d57,
+ 0x0d5f, 0x0d63,
+ 0x0d7a, 0x0d7f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df3,
+ 0x0e01, 0x0e3a,
+ 0x0e40, 0x0e46,
+ 0x0e4d, 0x0e4d,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e86, 0x0e8a,
+ 0x0e8c, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ecd, 0x0ecd,
+ 0x0edc, 0x0edf,
+ 0x0f00, 0x0f00,
+ 0x0f40, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f71, 0x0f81,
+ 0x0f88, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x1000, 0x1036,
+ 0x1038, 0x1038,
+ 0x103b, 0x103f,
+ 0x1050, 0x108f,
+ 0x109a, 0x109d,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x10fa,
+ 0x10fc, 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,
+ 0x1380, 0x138f,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0x1401, 0x166c,
+ 0x166f, 0x167f,
+ 0x1681, 0x169a,
+ 0x16a0, 0x16ea,
+ 0x16ee, 0x16f8,
+ 0x1700, 0x170c,
+ 0x170e, 0x1713,
+ 0x1720, 0x1733,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17b3,
+ 0x17b6, 0x17c8,
+ 0x17d7, 0x17d7,
+ 0x17dc, 0x17dc,
+ 0x1820, 0x1878,
+ 0x1880, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191e,
+ 0x1920, 0x192b,
+ 0x1930, 0x1938,
+ 0x1950, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19ab,
+ 0x19b0, 0x19c9,
+ 0x1a00, 0x1a1b,
+ 0x1a20, 0x1a5e,
+ 0x1a61, 0x1a74,
+ 0x1aa7, 0x1aa7,
+ 0x1b00, 0x1b33,
+ 0x1b35, 0x1b43,
+ 0x1b45, 0x1b4b,
+ 0x1b80, 0x1ba9,
+ 0x1bac, 0x1baf,
+ 0x1bba, 0x1be5,
+ 0x1be7, 0x1bf1,
+ 0x1c00, 0x1c36,
+ 0x1c4d, 0x1c4f,
+ 0x1c5a, 0x1c7d,
+ 0x1c80, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x1ce9, 0x1cec,
+ 0x1cee, 0x1cf3,
+ 0x1cf5, 0x1cf6,
+ 0x1cfa, 0x1cfa,
+ 0x1d00, 0x1dbf,
+ 0x1de7, 0x1df4,
+ 0x1e00, 0x1f15,
+ 0x1f18, 0x1f1d,
+ 0x1f20, 0x1f45,
+ 0x1f48, 0x1f4d,
+ 0x1f50, 0x1f57,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f7d,
+ 0x1f80, 0x1fb4,
+ 0x1fb6, 0x1fbc,
+ 0x1fbe, 0x1fbe,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fcc,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fdb,
+ 0x1fe0, 0x1fec,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ffc,
+ 0x2071, 0x2071,
+ 0x207f, 0x207f,
+ 0x2090, 0x209c,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210a, 0x2113,
+ 0x2115, 0x2115,
+ 0x2119, 0x211d,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212a, 0x212d,
+ 0x212f, 0x2139,
+ 0x213c, 0x213f,
+ 0x2145, 0x2149,
+ 0x214e, 0x214e,
+ 0x2160, 0x2188,
+ 0x24b6, 0x24e9,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2ce4,
+ 0x2ceb, 0x2cee,
+ 0x2cf2, 0x2cf3,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0x2d30, 0x2d67,
+ 0x2d6f, 0x2d6f,
+ 0x2d80, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x2de0, 0x2dff,
+ 0x2e2f, 0x2e2f,
+ 0x3005, 0x3007,
+ 0x3021, 0x3029,
+ 0x3031, 0x3035,
+ 0x3038, 0x303c,
+ 0x3041, 0x3096,
+ 0x309d, 0x309f,
+ 0x30a1, 0x30fa,
+ 0x30fc, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x31a0, 0x31ba,
+ 0x31f0, 0x31ff,
+ 0x3400, 0x4db5,
+ 0x4e00, 0x9fef,
+ 0xa000, 0xa48c,
+ 0xa4d0, 0xa4fd,
+ 0xa500, 0xa60c,
+ 0xa610, 0xa61f,
+ 0xa62a, 0xa62b,
+ 0xa640, 0xa66e,
+ 0xa674, 0xa67b,
+ 0xa67f, 0xa6ef,
+ 0xa717, 0xa71f,
+ 0xa722, 0xa788,
+ 0xa78b, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f7, 0xa805,
+ 0xa807, 0xa827,
+ 0xa840, 0xa873,
+ 0xa880, 0xa8c3,
+ 0xa8c5, 0xa8c5,
+ 0xa8f2, 0xa8f7,
+ 0xa8fb, 0xa8fb,
+ 0xa8fd, 0xa8ff,
+ 0xa90a, 0xa92a,
+ 0xa930, 0xa952,
+ 0xa960, 0xa97c,
+ 0xa980, 0xa9b2,
+ 0xa9b4, 0xa9bf,
+ 0xa9cf, 0xa9cf,
+ 0xa9e0, 0xa9ef,
+ 0xa9fa, 0xa9fe,
+ 0xaa00, 0xaa36,
+ 0xaa40, 0xaa4d,
+ 0xaa60, 0xaa76,
+ 0xaa7a, 0xaabe,
+ 0xaac0, 0xaac0,
+ 0xaac2, 0xaac2,
+ 0xaadb, 0xaadd,
+ 0xaae0, 0xaaef,
+ 0xaaf2, 0xaaf5,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xab30, 0xab5a,
+ 0xab5c, 0xab67,
+ 0xab70, 0xabea,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xf900, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb28,
+ 0xfb2a, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3d,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfb,
+ 0xfe70, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0xff21, 0xff3a,
+ 0xff41, 0xff5a,
+ 0xff66, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0x10000, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+ 0x10140, 0x10174,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x10300, 0x1031f,
+ 0x1032d, 0x1034a,
+ 0x10350, 0x1037a,
+ 0x10380, 0x1039d,
+ 0x103a0, 0x103c3,
+ 0x103c8, 0x103cf,
+ 0x103d1, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10860, 0x10876,
+ 0x10880, 0x1089e,
+ 0x108e0, 0x108f2,
+ 0x108f4, 0x108f5,
+ 0x10900, 0x10915,
+ 0x10920, 0x10939,
+ 0x10980, 0x109b7,
+ 0x109be, 0x109bf,
+ 0x10a00, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a35,
+ 0x10a60, 0x10a7c,
+ 0x10a80, 0x10a9c,
+ 0x10ac0, 0x10ac7,
+ 0x10ac9, 0x10ae4,
+ 0x10b00, 0x10b35,
+ 0x10b40, 0x10b55,
+ 0x10b60, 0x10b72,
+ 0x10b80, 0x10b91,
+ 0x10c00, 0x10c48,
+ 0x10c80, 0x10cb2,
+ 0x10cc0, 0x10cf2,
+ 0x10d00, 0x10d27,
+ 0x10f00, 0x10f1c,
+ 0x10f27, 0x10f27,
+ 0x10f30, 0x10f45,
+ 0x10fe0, 0x10ff6,
+ 0x11000, 0x11045,
+ 0x11082, 0x110b8,
+ 0x110d0, 0x110e8,
+ 0x11100, 0x11132,
+ 0x11144, 0x11146,
+ 0x11150, 0x11172,
+ 0x11176, 0x11176,
+ 0x11180, 0x111bf,
+ 0x111c1, 0x111c4,
+ 0x111da, 0x111da,
+ 0x111dc, 0x111dc,
+ 0x11200, 0x11211,
+ 0x11213, 0x11234,
+ 0x11237, 0x11237,
+ 0x1123e, 0x1123e,
+ 0x11280, 0x11286,
+ 0x11288, 0x11288,
+ 0x1128a, 0x1128d,
+ 0x1128f, 0x1129d,
+ 0x1129f, 0x112a8,
+ 0x112b0, 0x112e8,
+ 0x11300, 0x11303,
+ 0x11305, 0x1130c,
+ 0x1130f, 0x11310,
+ 0x11313, 0x11328,
+ 0x1132a, 0x11330,
+ 0x11332, 0x11333,
+ 0x11335, 0x11339,
+ 0x1133d, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134b, 0x1134c,
+ 0x11350, 0x11350,
+ 0x11357, 0x11357,
+ 0x1135d, 0x11363,
+ 0x11400, 0x11441,
+ 0x11443, 0x11445,
+ 0x11447, 0x1144a,
+ 0x1145f, 0x1145f,
+ 0x11480, 0x114c1,
+ 0x114c4, 0x114c5,
+ 0x114c7, 0x114c7,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115be,
+ 0x115d8, 0x115dd,
+ 0x11600, 0x1163e,
+ 0x11640, 0x11640,
+ 0x11644, 0x11644,
+ 0x11680, 0x116b5,
+ 0x116b8, 0x116b8,
+ 0x11700, 0x1171a,
+ 0x1171d, 0x1172a,
+ 0x11800, 0x11838,
+ 0x118a0, 0x118df,
+ 0x118ff, 0x118ff,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d7,
+ 0x119da, 0x119df,
+ 0x119e1, 0x119e1,
+ 0x119e3, 0x119e4,
+ 0x11a00, 0x11a32,
+ 0x11a35, 0x11a3e,
+ 0x11a50, 0x11a97,
+ 0x11a9d, 0x11a9d,
+ 0x11ac0, 0x11af8,
+ 0x11c00, 0x11c08,
+ 0x11c0a, 0x11c36,
+ 0x11c38, 0x11c3e,
+ 0x11c40, 0x11c40,
+ 0x11c72, 0x11c8f,
+ 0x11c92, 0x11ca7,
+ 0x11ca9, 0x11cb6,
+ 0x11d00, 0x11d06,
+ 0x11d08, 0x11d09,
+ 0x11d0b, 0x11d36,
+ 0x11d3a, 0x11d3a,
+ 0x11d3c, 0x11d3d,
+ 0x11d3f, 0x11d41,
+ 0x11d43, 0x11d43,
+ 0x11d46, 0x11d47,
+ 0x11d60, 0x11d65,
+ 0x11d67, 0x11d68,
+ 0x11d6a, 0x11d8e,
+ 0x11d90, 0x11d91,
+ 0x11d93, 0x11d96,
+ 0x11d98, 0x11d98,
+ 0x11ee0, 0x11ef6,
+ 0x12000, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16ad0, 0x16aed,
+ 0x16b00, 0x16b2f,
+ 0x16b40, 0x16b43,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e7f,
+ 0x16f00, 0x16f4a,
+ 0x16f4f, 0x16f87,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe1,
+ 0x16fe3, 0x16fe3,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9e, 0x1bc9e,
+ 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, 0x1d6c0,
+ 0x1d6c2, 0x1d6da,
+ 0x1d6dc, 0x1d6fa,
+ 0x1d6fc, 0x1d714,
+ 0x1d716, 0x1d734,
+ 0x1d736, 0x1d74e,
+ 0x1d750, 0x1d76e,
+ 0x1d770, 0x1d788,
+ 0x1d78a, 0x1d7a8,
+ 0x1d7aa, 0x1d7c2,
+ 0x1d7c4, 0x1d7cb,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e100, 0x1e12c,
+ 0x1e137, 0x1e13d,
+ 0x1e14e, 0x1e14e,
+ 0x1e2c0, 0x1e2eb,
+ 0x1e800, 0x1e8c4,
+ 0x1e900, 0x1e943,
+ 0x1e947, 0x1e947,
+ 0x1e94b, 0x1e94b,
+ 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,
+ 0x1f130, 0x1f149,
+ 0x1f150, 0x1f169,
+ 0x1f170, 0x1f189,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+}; /* CR_Alpha */
+
+/* 'Blank': [[:Blank:]] */
+static const OnigCodePoint CR_Blank[] = {
+ 8,
+ 0x0009, 0x0009,
+ 0x0020, 0x0020,
+ 0x00a0, 0x00a0,
+ 0x1680, 0x1680,
+ 0x2000, 0x200a,
+ 0x202f, 0x202f,
+ 0x205f, 0x205f,
+ 0x3000, 0x3000,
+}; /* CR_Blank */
+
+/* 'Cntrl': [[:Cntrl:]] */
+static const OnigCodePoint CR_Cntrl[] = {
+ 2,
+ 0x0000, 0x001f,
+ 0x007f, 0x009f,
+}; /* CR_Cntrl */
+
+/* 'Digit': [[:Digit:]] */
+static const OnigCodePoint CR_Digit[] = {
+ 59,
+ 0x0030, 0x0039,
+ 0x0660, 0x0669,
+ 0x06f0, 0x06f9,
+ 0x07c0, 0x07c9,
+ 0x0966, 0x096f,
+ 0x09e6, 0x09ef,
+ 0x0a66, 0x0a6f,
+ 0x0ae6, 0x0aef,
+ 0x0b66, 0x0b6f,
+ 0x0be6, 0x0bef,
+ 0x0c66, 0x0c6f,
+ 0x0ce6, 0x0cef,
+ 0x0d66, 0x0d6f,
+ 0x0de6, 0x0def,
+ 0x0e50, 0x0e59,
+ 0x0ed0, 0x0ed9,
+ 0x0f20, 0x0f29,
+ 0x1040, 0x1049,
+ 0x1090, 0x1099,
+ 0x17e0, 0x17e9,
+ 0x1810, 0x1819,
+ 0x1946, 0x194f,
+ 0x19d0, 0x19d9,
+ 0x1a80, 0x1a89,
+ 0x1a90, 0x1a99,
+ 0x1b50, 0x1b59,
+ 0x1bb0, 0x1bb9,
+ 0x1c40, 0x1c49,
+ 0x1c50, 0x1c59,
+ 0xa620, 0xa629,
+ 0xa8d0, 0xa8d9,
+ 0xa900, 0xa909,
+ 0xa9d0, 0xa9d9,
+ 0xa9f0, 0xa9f9,
+ 0xaa50, 0xaa59,
+ 0xabf0, 0xabf9,
+ 0xff10, 0xff19,
+ 0x104a0, 0x104a9,
+ 0x10d30, 0x10d39,
+ 0x11066, 0x1106f,
+ 0x110f0, 0x110f9,
+ 0x11136, 0x1113f,
+ 0x111d0, 0x111d9,
+ 0x112f0, 0x112f9,
+ 0x11450, 0x11459,
+ 0x114d0, 0x114d9,
+ 0x11650, 0x11659,
+ 0x116c0, 0x116c9,
+ 0x11730, 0x11739,
+ 0x118e0, 0x118e9,
+ 0x11c50, 0x11c59,
+ 0x11d50, 0x11d59,
+ 0x11da0, 0x11da9,
+ 0x16a60, 0x16a69,
+ 0x16b50, 0x16b59,
+ 0x1d7ce, 0x1d7ff,
+ 0x1e140, 0x1e149,
+ 0x1e2f0, 0x1e2f9,
+ 0x1e950, 0x1e959,
+}; /* CR_Digit */
+
+/* 'Graph': [[:Graph:]] */
+static const OnigCodePoint CR_Graph[] = {
+ 671,
+ 0x0021, 0x007e,
+ 0x00a1, 0x0377,
+ 0x037a, 0x037f,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x058a,
+ 0x058d, 0x058f,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f4,
+ 0x0600, 0x061c,
+ 0x061e, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x07fd, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x08d3, 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,
+ 0x0b56, 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,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c77, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d00, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4f,
+ 0x0d54, 0x0d63,
+ 0x0d66, 0x0d7f,
+ 0x0d82, 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, 0x0ecd,
+ 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, 0x167f,
+ 0x1681, 0x169c,
+ 0x16a0, 0x16f8,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 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, 0x1abe,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cc7,
+ 0x1cd0, 0x1cfa,
+ 0x1d00, 0x1df9,
+ 0x1dfb, 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,
+ 0x200b, 0x2027,
+ 0x202a, 0x202e,
+ 0x2030, 0x205e,
+ 0x2060, 0x2064,
+ 0x2066, 0x2071,
+ 0x2074, 0x208e,
+ 0x2090, 0x209c,
+ 0x20a0, 0x20bf,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x218b,
+ 0x2190, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b98, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 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, 0x2e4f,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3001, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x3190, 0x31ba,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x4db5,
+ 0x4dc0, 0x9fef,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa6f7,
+ 0xa700, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f7, 0xa82b,
+ 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, 0xab67,
+ 0xab70, 0xabed,
+ 0xabf0, 0xabf9,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xe000, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbc1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfd,
+ 0xfe00, 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, 0xfffd,
+ 0x10000, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+ 0x10100, 0x10102,
+ 0x10107, 0x10133,
+ 0x10137, 0x1018e,
+ 0x10190, 0x1019b,
+ 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, 0x1056f,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 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,
+ 0x10f00, 0x10f27,
+ 0x10f30, 0x10f59,
+ 0x10fe0, 0x10ff6,
+ 0x11000, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x1107f, 0x110c1,
+ 0x110cd, 0x110cd,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x11146,
+ 0x11150, 0x11176,
+ 0x11180, 0x111cd,
+ 0x111d0, 0x111df,
+ 0x111e1, 0x111f4,
+ 0x11200, 0x11211,
+ 0x11213, 0x1123e,
+ 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, 0x11459,
+ 0x1145b, 0x1145b,
+ 0x1145d, 0x1145f,
+ 0x11480, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115dd,
+ 0x11600, 0x11644,
+ 0x11650, 0x11659,
+ 0x11660, 0x1166c,
+ 0x11680, 0x116b8,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x1171a,
+ 0x1171d, 0x1172b,
+ 0x11730, 0x1173f,
+ 0x11800, 0x1183b,
+ 0x118a0, 0x118f2,
+ 0x118ff, 0x118ff,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d7,
+ 0x119da, 0x119e4,
+ 0x11a00, 0x11a47,
+ 0x11a50, 0x11aa2,
+ 0x11ac0, 0x11af8,
+ 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,
+ 0x11fc0, 0x11ff1,
+ 0x11fff, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12470, 0x12474,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x13430, 0x13438,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16a6e, 0x16a6f,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af5,
+ 0x16b00, 0x16b45,
+ 0x16b50, 0x16b59,
+ 0x16b5b, 0x16b61,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e9a,
+ 0x16f00, 0x16f4a,
+ 0x16f4f, 0x16f87,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe3,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9c, 0x1bca3,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1e8,
+ 0x1d200, 0x1d245,
+ 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,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e100, 0x1e12c,
+ 0x1e130, 0x1e13d,
+ 0x1e140, 0x1e149,
+ 0x1e14e, 0x1e14f,
+ 0x1e2c0, 0x1e2f9,
+ 0x1e2ff, 0x1e2ff,
+ 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, 0x1f10c,
+ 0x1f110, 0x1f16c,
+ 0x1f170, 0x1f1ac,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23b,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f260, 0x1f265,
+ 0x1f300, 0x1f6d5,
+ 0x1f6e0, 0x1f6ec,
+ 0x1f6f0, 0x1f6fa,
+ 0x1f700, 0x1f773,
+ 0x1f780, 0x1f7d8,
+ 0x1f7e0, 0x1f7eb,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1f900, 0x1f90b,
+ 0x1f90d, 0x1f971,
+ 0x1f973, 0x1f976,
+ 0x1f97a, 0x1f9a2,
+ 0x1f9a5, 0x1f9aa,
+ 0x1f9ae, 0x1f9ca,
+ 0x1f9cd, 0x1fa53,
+ 0x1fa60, 0x1fa6d,
+ 0x1fa70, 0x1fa73,
+ 0x1fa78, 0x1fa7a,
+ 0x1fa80, 0x1fa82,
+ 0x1fa90, 0x1fa95,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+ 0xe0001, 0xe0001,
+ 0xe0020, 0xe007f,
+ 0xe0100, 0xe01ef,
+ 0xf0000, 0xffffd,
+ 0x100000, 0x10fffd,
+}; /* CR_Graph */
+
+/* 'Lower': [[:Lower:]] */
+static const OnigCodePoint CR_Lower[] = {
+ 649,
+ 0x0061, 0x007a,
+ 0x00aa, 0x00aa,
+ 0x00b5, 0x00b5,
+ 0x00ba, 0x00ba,
+ 0x00df, 0x00f6,
+ 0x00f8, 0x00ff,
+ 0x0101, 0x0101,
+ 0x0103, 0x0103,
+ 0x0105, 0x0105,
+ 0x0107, 0x0107,
+ 0x0109, 0x0109,
+ 0x010b, 0x010b,
+ 0x010d, 0x010d,
+ 0x010f, 0x010f,
+ 0x0111, 0x0111,
+ 0x0113, 0x0113,
+ 0x0115, 0x0115,
+ 0x0117, 0x0117,
+ 0x0119, 0x0119,
+ 0x011b, 0x011b,
+ 0x011d, 0x011d,
+ 0x011f, 0x011f,
+ 0x0121, 0x0121,
+ 0x0123, 0x0123,
+ 0x0125, 0x0125,
+ 0x0127, 0x0127,
+ 0x0129, 0x0129,
+ 0x012b, 0x012b,
+ 0x012d, 0x012d,
+ 0x012f, 0x012f,
+ 0x0131, 0x0131,
+ 0x0133, 0x0133,
+ 0x0135, 0x0135,
+ 0x0137, 0x0138,
+ 0x013a, 0x013a,
+ 0x013c, 0x013c,
+ 0x013e, 0x013e,
+ 0x0140, 0x0140,
+ 0x0142, 0x0142,
+ 0x0144, 0x0144,
+ 0x0146, 0x0146,
+ 0x0148, 0x0149,
+ 0x014b, 0x014b,
+ 0x014d, 0x014d,
+ 0x014f, 0x014f,
+ 0x0151, 0x0151,
+ 0x0153, 0x0153,
+ 0x0155, 0x0155,
+ 0x0157, 0x0157,
+ 0x0159, 0x0159,
+ 0x015b, 0x015b,
+ 0x015d, 0x015d,
+ 0x015f, 0x015f,
+ 0x0161, 0x0161,
+ 0x0163, 0x0163,
+ 0x0165, 0x0165,
+ 0x0167, 0x0167,
+ 0x0169, 0x0169,
+ 0x016b, 0x016b,
+ 0x016d, 0x016d,
+ 0x016f, 0x016f,
+ 0x0171, 0x0171,
+ 0x0173, 0x0173,
+ 0x0175, 0x0175,
+ 0x0177, 0x0177,
+ 0x017a, 0x017a,
+ 0x017c, 0x017c,
+ 0x017e, 0x0180,
+ 0x0183, 0x0183,
+ 0x0185, 0x0185,
+ 0x0188, 0x0188,
+ 0x018c, 0x018d,
+ 0x0192, 0x0192,
+ 0x0195, 0x0195,
+ 0x0199, 0x019b,
+ 0x019e, 0x019e,
+ 0x01a1, 0x01a1,
+ 0x01a3, 0x01a3,
+ 0x01a5, 0x01a5,
+ 0x01a8, 0x01a8,
+ 0x01aa, 0x01ab,
+ 0x01ad, 0x01ad,
+ 0x01b0, 0x01b0,
+ 0x01b4, 0x01b4,
+ 0x01b6, 0x01b6,
+ 0x01b9, 0x01ba,
+ 0x01bd, 0x01bf,
+ 0x01c6, 0x01c6,
+ 0x01c9, 0x01c9,
+ 0x01cc, 0x01cc,
+ 0x01ce, 0x01ce,
+ 0x01d0, 0x01d0,
+ 0x01d2, 0x01d2,
+ 0x01d4, 0x01d4,
+ 0x01d6, 0x01d6,
+ 0x01d8, 0x01d8,
+ 0x01da, 0x01da,
+ 0x01dc, 0x01dd,
+ 0x01df, 0x01df,
+ 0x01e1, 0x01e1,
+ 0x01e3, 0x01e3,
+ 0x01e5, 0x01e5,
+ 0x01e7, 0x01e7,
+ 0x01e9, 0x01e9,
+ 0x01eb, 0x01eb,
+ 0x01ed, 0x01ed,
+ 0x01ef, 0x01f0,
+ 0x01f3, 0x01f3,
+ 0x01f5, 0x01f5,
+ 0x01f9, 0x01f9,
+ 0x01fb, 0x01fb,
+ 0x01fd, 0x01fd,
+ 0x01ff, 0x01ff,
+ 0x0201, 0x0201,
+ 0x0203, 0x0203,
+ 0x0205, 0x0205,
+ 0x0207, 0x0207,
+ 0x0209, 0x0209,
+ 0x020b, 0x020b,
+ 0x020d, 0x020d,
+ 0x020f, 0x020f,
+ 0x0211, 0x0211,
+ 0x0213, 0x0213,
+ 0x0215, 0x0215,
+ 0x0217, 0x0217,
+ 0x0219, 0x0219,
+ 0x021b, 0x021b,
+ 0x021d, 0x021d,
+ 0x021f, 0x021f,
+ 0x0221, 0x0221,
+ 0x0223, 0x0223,
+ 0x0225, 0x0225,
+ 0x0227, 0x0227,
+ 0x0229, 0x0229,
+ 0x022b, 0x022b,
+ 0x022d, 0x022d,
+ 0x022f, 0x022f,
+ 0x0231, 0x0231,
+ 0x0233, 0x0239,
+ 0x023c, 0x023c,
+ 0x023f, 0x0240,
+ 0x0242, 0x0242,
+ 0x0247, 0x0247,
+ 0x0249, 0x0249,
+ 0x024b, 0x024b,
+ 0x024d, 0x024d,
+ 0x024f, 0x0293,
+ 0x0295, 0x02b8,
+ 0x02c0, 0x02c1,
+ 0x02e0, 0x02e4,
+ 0x0345, 0x0345,
+ 0x0371, 0x0371,
+ 0x0373, 0x0373,
+ 0x0377, 0x0377,
+ 0x037a, 0x037d,
+ 0x0390, 0x0390,
+ 0x03ac, 0x03ce,
+ 0x03d0, 0x03d1,
+ 0x03d5, 0x03d7,
+ 0x03d9, 0x03d9,
+ 0x03db, 0x03db,
+ 0x03dd, 0x03dd,
+ 0x03df, 0x03df,
+ 0x03e1, 0x03e1,
+ 0x03e3, 0x03e3,
+ 0x03e5, 0x03e5,
+ 0x03e7, 0x03e7,
+ 0x03e9, 0x03e9,
+ 0x03eb, 0x03eb,
+ 0x03ed, 0x03ed,
+ 0x03ef, 0x03f3,
+ 0x03f5, 0x03f5,
+ 0x03f8, 0x03f8,
+ 0x03fb, 0x03fc,
+ 0x0430, 0x045f,
+ 0x0461, 0x0461,
+ 0x0463, 0x0463,
+ 0x0465, 0x0465,
+ 0x0467, 0x0467,
+ 0x0469, 0x0469,
+ 0x046b, 0x046b,
+ 0x046d, 0x046d,
+ 0x046f, 0x046f,
+ 0x0471, 0x0471,
+ 0x0473, 0x0473,
+ 0x0475, 0x0475,
+ 0x0477, 0x0477,
+ 0x0479, 0x0479,
+ 0x047b, 0x047b,
+ 0x047d, 0x047d,
+ 0x047f, 0x047f,
+ 0x0481, 0x0481,
+ 0x048b, 0x048b,
+ 0x048d, 0x048d,
+ 0x048f, 0x048f,
+ 0x0491, 0x0491,
+ 0x0493, 0x0493,
+ 0x0495, 0x0495,
+ 0x0497, 0x0497,
+ 0x0499, 0x0499,
+ 0x049b, 0x049b,
+ 0x049d, 0x049d,
+ 0x049f, 0x049f,
+ 0x04a1, 0x04a1,
+ 0x04a3, 0x04a3,
+ 0x04a5, 0x04a5,
+ 0x04a7, 0x04a7,
+ 0x04a9, 0x04a9,
+ 0x04ab, 0x04ab,
+ 0x04ad, 0x04ad,
+ 0x04af, 0x04af,
+ 0x04b1, 0x04b1,
+ 0x04b3, 0x04b3,
+ 0x04b5, 0x04b5,
+ 0x04b7, 0x04b7,
+ 0x04b9, 0x04b9,
+ 0x04bb, 0x04bb,
+ 0x04bd, 0x04bd,
+ 0x04bf, 0x04bf,
+ 0x04c2, 0x04c2,
+ 0x04c4, 0x04c4,
+ 0x04c6, 0x04c6,
+ 0x04c8, 0x04c8,
+ 0x04ca, 0x04ca,
+ 0x04cc, 0x04cc,
+ 0x04ce, 0x04cf,
+ 0x04d1, 0x04d1,
+ 0x04d3, 0x04d3,
+ 0x04d5, 0x04d5,
+ 0x04d7, 0x04d7,
+ 0x04d9, 0x04d9,
+ 0x04db, 0x04db,
+ 0x04dd, 0x04dd,
+ 0x04df, 0x04df,
+ 0x04e1, 0x04e1,
+ 0x04e3, 0x04e3,
+ 0x04e5, 0x04e5,
+ 0x04e7, 0x04e7,
+ 0x04e9, 0x04e9,
+ 0x04eb, 0x04eb,
+ 0x04ed, 0x04ed,
+ 0x04ef, 0x04ef,
+ 0x04f1, 0x04f1,
+ 0x04f3, 0x04f3,
+ 0x04f5, 0x04f5,
+ 0x04f7, 0x04f7,
+ 0x04f9, 0x04f9,
+ 0x04fb, 0x04fb,
+ 0x04fd, 0x04fd,
+ 0x04ff, 0x04ff,
+ 0x0501, 0x0501,
+ 0x0503, 0x0503,
+ 0x0505, 0x0505,
+ 0x0507, 0x0507,
+ 0x0509, 0x0509,
+ 0x050b, 0x050b,
+ 0x050d, 0x050d,
+ 0x050f, 0x050f,
+ 0x0511, 0x0511,
+ 0x0513, 0x0513,
+ 0x0515, 0x0515,
+ 0x0517, 0x0517,
+ 0x0519, 0x0519,
+ 0x051b, 0x051b,
+ 0x051d, 0x051d,
+ 0x051f, 0x051f,
+ 0x0521, 0x0521,
+ 0x0523, 0x0523,
+ 0x0525, 0x0525,
+ 0x0527, 0x0527,
+ 0x0529, 0x0529,
+ 0x052b, 0x052b,
+ 0x052d, 0x052d,
+ 0x052f, 0x052f,
+ 0x0560, 0x0588,
+ 0x10d0, 0x10fa,
+ 0x10fd, 0x10ff,
+ 0x13f8, 0x13fd,
+ 0x1c80, 0x1c88,
+ 0x1d00, 0x1dbf,
+ 0x1e01, 0x1e01,
+ 0x1e03, 0x1e03,
+ 0x1e05, 0x1e05,
+ 0x1e07, 0x1e07,
+ 0x1e09, 0x1e09,
+ 0x1e0b, 0x1e0b,
+ 0x1e0d, 0x1e0d,
+ 0x1e0f, 0x1e0f,
+ 0x1e11, 0x1e11,
+ 0x1e13, 0x1e13,
+ 0x1e15, 0x1e15,
+ 0x1e17, 0x1e17,
+ 0x1e19, 0x1e19,
+ 0x1e1b, 0x1e1b,
+ 0x1e1d, 0x1e1d,
+ 0x1e1f, 0x1e1f,
+ 0x1e21, 0x1e21,
+ 0x1e23, 0x1e23,
+ 0x1e25, 0x1e25,
+ 0x1e27, 0x1e27,
+ 0x1e29, 0x1e29,
+ 0x1e2b, 0x1e2b,
+ 0x1e2d, 0x1e2d,
+ 0x1e2f, 0x1e2f,
+ 0x1e31, 0x1e31,
+ 0x1e33, 0x1e33,
+ 0x1e35, 0x1e35,
+ 0x1e37, 0x1e37,
+ 0x1e39, 0x1e39,
+ 0x1e3b, 0x1e3b,
+ 0x1e3d, 0x1e3d,
+ 0x1e3f, 0x1e3f,
+ 0x1e41, 0x1e41,
+ 0x1e43, 0x1e43,
+ 0x1e45, 0x1e45,
+ 0x1e47, 0x1e47,
+ 0x1e49, 0x1e49,
+ 0x1e4b, 0x1e4b,
+ 0x1e4d, 0x1e4d,
+ 0x1e4f, 0x1e4f,
+ 0x1e51, 0x1e51,
+ 0x1e53, 0x1e53,
+ 0x1e55, 0x1e55,
+ 0x1e57, 0x1e57,
+ 0x1e59, 0x1e59,
+ 0x1e5b, 0x1e5b,
+ 0x1e5d, 0x1e5d,
+ 0x1e5f, 0x1e5f,
+ 0x1e61, 0x1e61,
+ 0x1e63, 0x1e63,
+ 0x1e65, 0x1e65,
+ 0x1e67, 0x1e67,
+ 0x1e69, 0x1e69,
+ 0x1e6b, 0x1e6b,
+ 0x1e6d, 0x1e6d,
+ 0x1e6f, 0x1e6f,
+ 0x1e71, 0x1e71,
+ 0x1e73, 0x1e73,
+ 0x1e75, 0x1e75,
+ 0x1e77, 0x1e77,
+ 0x1e79, 0x1e79,
+ 0x1e7b, 0x1e7b,
+ 0x1e7d, 0x1e7d,
+ 0x1e7f, 0x1e7f,
+ 0x1e81, 0x1e81,
+ 0x1e83, 0x1e83,
+ 0x1e85, 0x1e85,
+ 0x1e87, 0x1e87,
+ 0x1e89, 0x1e89,
+ 0x1e8b, 0x1e8b,
+ 0x1e8d, 0x1e8d,
+ 0x1e8f, 0x1e8f,
+ 0x1e91, 0x1e91,
+ 0x1e93, 0x1e93,
+ 0x1e95, 0x1e9d,
+ 0x1e9f, 0x1e9f,
+ 0x1ea1, 0x1ea1,
+ 0x1ea3, 0x1ea3,
+ 0x1ea5, 0x1ea5,
+ 0x1ea7, 0x1ea7,
+ 0x1ea9, 0x1ea9,
+ 0x1eab, 0x1eab,
+ 0x1ead, 0x1ead,
+ 0x1eaf, 0x1eaf,
+ 0x1eb1, 0x1eb1,
+ 0x1eb3, 0x1eb3,
+ 0x1eb5, 0x1eb5,
+ 0x1eb7, 0x1eb7,
+ 0x1eb9, 0x1eb9,
+ 0x1ebb, 0x1ebb,
+ 0x1ebd, 0x1ebd,
+ 0x1ebf, 0x1ebf,
+ 0x1ec1, 0x1ec1,
+ 0x1ec3, 0x1ec3,
+ 0x1ec5, 0x1ec5,
+ 0x1ec7, 0x1ec7,
+ 0x1ec9, 0x1ec9,
+ 0x1ecb, 0x1ecb,
+ 0x1ecd, 0x1ecd,
+ 0x1ecf, 0x1ecf,
+ 0x1ed1, 0x1ed1,
+ 0x1ed3, 0x1ed3,
+ 0x1ed5, 0x1ed5,
+ 0x1ed7, 0x1ed7,
+ 0x1ed9, 0x1ed9,
+ 0x1edb, 0x1edb,
+ 0x1edd, 0x1edd,
+ 0x1edf, 0x1edf,
+ 0x1ee1, 0x1ee1,
+ 0x1ee3, 0x1ee3,
+ 0x1ee5, 0x1ee5,
+ 0x1ee7, 0x1ee7,
+ 0x1ee9, 0x1ee9,
+ 0x1eeb, 0x1eeb,
+ 0x1eed, 0x1eed,
+ 0x1eef, 0x1eef,
+ 0x1ef1, 0x1ef1,
+ 0x1ef3, 0x1ef3,
+ 0x1ef5, 0x1ef5,
+ 0x1ef7, 0x1ef7,
+ 0x1ef9, 0x1ef9,
+ 0x1efb, 0x1efb,
+ 0x1efd, 0x1efd,
+ 0x1eff, 0x1f07,
+ 0x1f10, 0x1f15,
+ 0x1f20, 0x1f27,
+ 0x1f30, 0x1f37,
+ 0x1f40, 0x1f45,
+ 0x1f50, 0x1f57,
+ 0x1f60, 0x1f67,
+ 0x1f70, 0x1f7d,
+ 0x1f80, 0x1f87,
+ 0x1f90, 0x1f97,
+ 0x1fa0, 0x1fa7,
+ 0x1fb0, 0x1fb4,
+ 0x1fb6, 0x1fb7,
+ 0x1fbe, 0x1fbe,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fc7,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fd7,
+ 0x1fe0, 0x1fe7,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ff7,
+ 0x2071, 0x2071,
+ 0x207f, 0x207f,
+ 0x2090, 0x209c,
+ 0x210a, 0x210a,
+ 0x210e, 0x210f,
+ 0x2113, 0x2113,
+ 0x212f, 0x212f,
+ 0x2134, 0x2134,
+ 0x2139, 0x2139,
+ 0x213c, 0x213d,
+ 0x2146, 0x2149,
+ 0x214e, 0x214e,
+ 0x2170, 0x217f,
+ 0x2184, 0x2184,
+ 0x24d0, 0x24e9,
+ 0x2c30, 0x2c5e,
+ 0x2c61, 0x2c61,
+ 0x2c65, 0x2c66,
+ 0x2c68, 0x2c68,
+ 0x2c6a, 0x2c6a,
+ 0x2c6c, 0x2c6c,
+ 0x2c71, 0x2c71,
+ 0x2c73, 0x2c74,
+ 0x2c76, 0x2c7d,
+ 0x2c81, 0x2c81,
+ 0x2c83, 0x2c83,
+ 0x2c85, 0x2c85,
+ 0x2c87, 0x2c87,
+ 0x2c89, 0x2c89,
+ 0x2c8b, 0x2c8b,
+ 0x2c8d, 0x2c8d,
+ 0x2c8f, 0x2c8f,
+ 0x2c91, 0x2c91,
+ 0x2c93, 0x2c93,
+ 0x2c95, 0x2c95,
+ 0x2c97, 0x2c97,
+ 0x2c99, 0x2c99,
+ 0x2c9b, 0x2c9b,
+ 0x2c9d, 0x2c9d,
+ 0x2c9f, 0x2c9f,
+ 0x2ca1, 0x2ca1,
+ 0x2ca3, 0x2ca3,
+ 0x2ca5, 0x2ca5,
+ 0x2ca7, 0x2ca7,
+ 0x2ca9, 0x2ca9,
+ 0x2cab, 0x2cab,
+ 0x2cad, 0x2cad,
+ 0x2caf, 0x2caf,
+ 0x2cb1, 0x2cb1,
+ 0x2cb3, 0x2cb3,
+ 0x2cb5, 0x2cb5,
+ 0x2cb7, 0x2cb7,
+ 0x2cb9, 0x2cb9,
+ 0x2cbb, 0x2cbb,
+ 0x2cbd, 0x2cbd,
+ 0x2cbf, 0x2cbf,
+ 0x2cc1, 0x2cc1,
+ 0x2cc3, 0x2cc3,
+ 0x2cc5, 0x2cc5,
+ 0x2cc7, 0x2cc7,
+ 0x2cc9, 0x2cc9,
+ 0x2ccb, 0x2ccb,
+ 0x2ccd, 0x2ccd,
+ 0x2ccf, 0x2ccf,
+ 0x2cd1, 0x2cd1,
+ 0x2cd3, 0x2cd3,
+ 0x2cd5, 0x2cd5,
+ 0x2cd7, 0x2cd7,
+ 0x2cd9, 0x2cd9,
+ 0x2cdb, 0x2cdb,
+ 0x2cdd, 0x2cdd,
+ 0x2cdf, 0x2cdf,
+ 0x2ce1, 0x2ce1,
+ 0x2ce3, 0x2ce4,
+ 0x2cec, 0x2cec,
+ 0x2cee, 0x2cee,
+ 0x2cf3, 0x2cf3,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0xa641, 0xa641,
+ 0xa643, 0xa643,
+ 0xa645, 0xa645,
+ 0xa647, 0xa647,
+ 0xa649, 0xa649,
+ 0xa64b, 0xa64b,
+ 0xa64d, 0xa64d,
+ 0xa64f, 0xa64f,
+ 0xa651, 0xa651,
+ 0xa653, 0xa653,
+ 0xa655, 0xa655,
+ 0xa657, 0xa657,
+ 0xa659, 0xa659,
+ 0xa65b, 0xa65b,
+ 0xa65d, 0xa65d,
+ 0xa65f, 0xa65f,
+ 0xa661, 0xa661,
+ 0xa663, 0xa663,
+ 0xa665, 0xa665,
+ 0xa667, 0xa667,
+ 0xa669, 0xa669,
+ 0xa66b, 0xa66b,
+ 0xa66d, 0xa66d,
+ 0xa681, 0xa681,
+ 0xa683, 0xa683,
+ 0xa685, 0xa685,
+ 0xa687, 0xa687,
+ 0xa689, 0xa689,
+ 0xa68b, 0xa68b,
+ 0xa68d, 0xa68d,
+ 0xa68f, 0xa68f,
+ 0xa691, 0xa691,
+ 0xa693, 0xa693,
+ 0xa695, 0xa695,
+ 0xa697, 0xa697,
+ 0xa699, 0xa699,
+ 0xa69b, 0xa69d,
+ 0xa723, 0xa723,
+ 0xa725, 0xa725,
+ 0xa727, 0xa727,
+ 0xa729, 0xa729,
+ 0xa72b, 0xa72b,
+ 0xa72d, 0xa72d,
+ 0xa72f, 0xa731,
+ 0xa733, 0xa733,
+ 0xa735, 0xa735,
+ 0xa737, 0xa737,
+ 0xa739, 0xa739,
+ 0xa73b, 0xa73b,
+ 0xa73d, 0xa73d,
+ 0xa73f, 0xa73f,
+ 0xa741, 0xa741,
+ 0xa743, 0xa743,
+ 0xa745, 0xa745,
+ 0xa747, 0xa747,
+ 0xa749, 0xa749,
+ 0xa74b, 0xa74b,
+ 0xa74d, 0xa74d,
+ 0xa74f, 0xa74f,
+ 0xa751, 0xa751,
+ 0xa753, 0xa753,
+ 0xa755, 0xa755,
+ 0xa757, 0xa757,
+ 0xa759, 0xa759,
+ 0xa75b, 0xa75b,
+ 0xa75d, 0xa75d,
+ 0xa75f, 0xa75f,
+ 0xa761, 0xa761,
+ 0xa763, 0xa763,
+ 0xa765, 0xa765,
+ 0xa767, 0xa767,
+ 0xa769, 0xa769,
+ 0xa76b, 0xa76b,
+ 0xa76d, 0xa76d,
+ 0xa76f, 0xa778,
+ 0xa77a, 0xa77a,
+ 0xa77c, 0xa77c,
+ 0xa77f, 0xa77f,
+ 0xa781, 0xa781,
+ 0xa783, 0xa783,
+ 0xa785, 0xa785,
+ 0xa787, 0xa787,
+ 0xa78c, 0xa78c,
+ 0xa78e, 0xa78e,
+ 0xa791, 0xa791,
+ 0xa793, 0xa795,
+ 0xa797, 0xa797,
+ 0xa799, 0xa799,
+ 0xa79b, 0xa79b,
+ 0xa79d, 0xa79d,
+ 0xa79f, 0xa79f,
+ 0xa7a1, 0xa7a1,
+ 0xa7a3, 0xa7a3,
+ 0xa7a5, 0xa7a5,
+ 0xa7a7, 0xa7a7,
+ 0xa7a9, 0xa7a9,
+ 0xa7af, 0xa7af,
+ 0xa7b5, 0xa7b5,
+ 0xa7b7, 0xa7b7,
+ 0xa7b9, 0xa7b9,
+ 0xa7bb, 0xa7bb,
+ 0xa7bd, 0xa7bd,
+ 0xa7bf, 0xa7bf,
+ 0xa7c3, 0xa7c3,
+ 0xa7f8, 0xa7fa,
+ 0xab30, 0xab5a,
+ 0xab5c, 0xab67,
+ 0xab70, 0xabbf,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xff41, 0xff5a,
+ 0x10428, 0x1044f,
+ 0x104d8, 0x104fb,
+ 0x10cc0, 0x10cf2,
+ 0x118c0, 0x118df,
+ 0x16e60, 0x16e7f,
+ 0x1d41a, 0x1d433,
+ 0x1d44e, 0x1d454,
+ 0x1d456, 0x1d467,
+ 0x1d482, 0x1d49b,
+ 0x1d4b6, 0x1d4b9,
+ 0x1d4bb, 0x1d4bb,
+ 0x1d4bd, 0x1d4c3,
+ 0x1d4c5, 0x1d4cf,
+ 0x1d4ea, 0x1d503,
+ 0x1d51e, 0x1d537,
+ 0x1d552, 0x1d56b,
+ 0x1d586, 0x1d59f,
+ 0x1d5ba, 0x1d5d3,
+ 0x1d5ee, 0x1d607,
+ 0x1d622, 0x1d63b,
+ 0x1d656, 0x1d66f,
+ 0x1d68a, 0x1d6a5,
+ 0x1d6c2, 0x1d6da,
+ 0x1d6dc, 0x1d6e1,
+ 0x1d6fc, 0x1d714,
+ 0x1d716, 0x1d71b,
+ 0x1d736, 0x1d74e,
+ 0x1d750, 0x1d755,
+ 0x1d770, 0x1d788,
+ 0x1d78a, 0x1d78f,
+ 0x1d7aa, 0x1d7c2,
+ 0x1d7c4, 0x1d7c9,
+ 0x1d7cb, 0x1d7cb,
+ 0x1e922, 0x1e943,
+}; /* CR_Lower */
+
+/* 'Print': [[:Print:]] */
+static const OnigCodePoint CR_Print[] = {
+ 668,
+ 0x0020, 0x007e,
+ 0x00a0, 0x0377,
+ 0x037a, 0x037f,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x058a,
+ 0x058d, 0x058f,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f4,
+ 0x0600, 0x061c,
+ 0x061e, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x07fd, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x08d3, 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,
+ 0x0b56, 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,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c77, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d00, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4f,
+ 0x0d54, 0x0d63,
+ 0x0d66, 0x0d7f,
+ 0x0d82, 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, 0x0ecd,
+ 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, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 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, 0x1abe,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cc7,
+ 0x1cd0, 0x1cfa,
+ 0x1d00, 0x1df9,
+ 0x1dfb, 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, 0x2027,
+ 0x202a, 0x2064,
+ 0x2066, 0x2071,
+ 0x2074, 0x208e,
+ 0x2090, 0x209c,
+ 0x20a0, 0x20bf,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x218b,
+ 0x2190, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b98, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 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, 0x2e4f,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x3190, 0x31ba,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x4db5,
+ 0x4dc0, 0x9fef,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa6f7,
+ 0xa700, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f7, 0xa82b,
+ 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, 0xab67,
+ 0xab70, 0xabed,
+ 0xabf0, 0xabf9,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xe000, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbc1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfd,
+ 0xfe00, 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, 0xfffd,
+ 0x10000, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+ 0x10100, 0x10102,
+ 0x10107, 0x10133,
+ 0x10137, 0x1018e,
+ 0x10190, 0x1019b,
+ 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, 0x1056f,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 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,
+ 0x10f00, 0x10f27,
+ 0x10f30, 0x10f59,
+ 0x10fe0, 0x10ff6,
+ 0x11000, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x1107f, 0x110c1,
+ 0x110cd, 0x110cd,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x11146,
+ 0x11150, 0x11176,
+ 0x11180, 0x111cd,
+ 0x111d0, 0x111df,
+ 0x111e1, 0x111f4,
+ 0x11200, 0x11211,
+ 0x11213, 0x1123e,
+ 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, 0x11459,
+ 0x1145b, 0x1145b,
+ 0x1145d, 0x1145f,
+ 0x11480, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115dd,
+ 0x11600, 0x11644,
+ 0x11650, 0x11659,
+ 0x11660, 0x1166c,
+ 0x11680, 0x116b8,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x1171a,
+ 0x1171d, 0x1172b,
+ 0x11730, 0x1173f,
+ 0x11800, 0x1183b,
+ 0x118a0, 0x118f2,
+ 0x118ff, 0x118ff,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d7,
+ 0x119da, 0x119e4,
+ 0x11a00, 0x11a47,
+ 0x11a50, 0x11aa2,
+ 0x11ac0, 0x11af8,
+ 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,
+ 0x11fc0, 0x11ff1,
+ 0x11fff, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12470, 0x12474,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x13430, 0x13438,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16a6e, 0x16a6f,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af5,
+ 0x16b00, 0x16b45,
+ 0x16b50, 0x16b59,
+ 0x16b5b, 0x16b61,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e9a,
+ 0x16f00, 0x16f4a,
+ 0x16f4f, 0x16f87,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe3,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9c, 0x1bca3,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1e8,
+ 0x1d200, 0x1d245,
+ 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,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e100, 0x1e12c,
+ 0x1e130, 0x1e13d,
+ 0x1e140, 0x1e149,
+ 0x1e14e, 0x1e14f,
+ 0x1e2c0, 0x1e2f9,
+ 0x1e2ff, 0x1e2ff,
+ 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, 0x1f10c,
+ 0x1f110, 0x1f16c,
+ 0x1f170, 0x1f1ac,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23b,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f260, 0x1f265,
+ 0x1f300, 0x1f6d5,
+ 0x1f6e0, 0x1f6ec,
+ 0x1f6f0, 0x1f6fa,
+ 0x1f700, 0x1f773,
+ 0x1f780, 0x1f7d8,
+ 0x1f7e0, 0x1f7eb,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1f900, 0x1f90b,
+ 0x1f90d, 0x1f971,
+ 0x1f973, 0x1f976,
+ 0x1f97a, 0x1f9a2,
+ 0x1f9a5, 0x1f9aa,
+ 0x1f9ae, 0x1f9ca,
+ 0x1f9cd, 0x1fa53,
+ 0x1fa60, 0x1fa6d,
+ 0x1fa70, 0x1fa73,
+ 0x1fa78, 0x1fa7a,
+ 0x1fa80, 0x1fa82,
+ 0x1fa90, 0x1fa95,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+ 0xe0001, 0xe0001,
+ 0xe0020, 0xe007f,
+ 0xe0100, 0xe01ef,
+ 0xf0000, 0xffffd,
+ 0x100000, 0x10fffd,
+}; /* CR_Print */
+
+/* 'XPosixPunct': [[:Punct:]] */
+static const OnigCodePoint CR_XPosixPunct[] = {
+ 177,
+ 0x0021, 0x002f,
+ 0x003a, 0x0040,
+ 0x005b, 0x0060,
+ 0x007b, 0x007e,
+ 0x00a1, 0x00a1,
+ 0x00a7, 0x00a7,
+ 0x00ab, 0x00ab,
+ 0x00b6, 0x00b7,
+ 0x00bb, 0x00bb,
+ 0x00bf, 0x00bf,
+ 0x037e, 0x037e,
+ 0x0387, 0x0387,
+ 0x055a, 0x055f,
+ 0x0589, 0x058a,
+ 0x05be, 0x05be,
+ 0x05c0, 0x05c0,
+ 0x05c3, 0x05c3,
+ 0x05c6, 0x05c6,
+ 0x05f3, 0x05f4,
+ 0x0609, 0x060a,
+ 0x060c, 0x060d,
+ 0x061b, 0x061b,
+ 0x061e, 0x061f,
+ 0x066a, 0x066d,
+ 0x06d4, 0x06d4,
+ 0x0700, 0x070d,
+ 0x07f7, 0x07f9,
+ 0x0830, 0x083e,
+ 0x085e, 0x085e,
+ 0x0964, 0x0965,
+ 0x0970, 0x0970,
+ 0x09fd, 0x09fd,
+ 0x0a76, 0x0a76,
+ 0x0af0, 0x0af0,
+ 0x0c77, 0x0c77,
+ 0x0c84, 0x0c84,
+ 0x0df4, 0x0df4,
+ 0x0e4f, 0x0e4f,
+ 0x0e5a, 0x0e5b,
+ 0x0f04, 0x0f12,
+ 0x0f14, 0x0f14,
+ 0x0f3a, 0x0f3d,
+ 0x0f85, 0x0f85,
+ 0x0fd0, 0x0fd4,
+ 0x0fd9, 0x0fda,
+ 0x104a, 0x104f,
+ 0x10fb, 0x10fb,
+ 0x1360, 0x1368,
+ 0x1400, 0x1400,
+ 0x166e, 0x166e,
+ 0x169b, 0x169c,
+ 0x16eb, 0x16ed,
+ 0x1735, 0x1736,
+ 0x17d4, 0x17d6,
+ 0x17d8, 0x17da,
+ 0x1800, 0x180a,
+ 0x1944, 0x1945,
+ 0x1a1e, 0x1a1f,
+ 0x1aa0, 0x1aa6,
+ 0x1aa8, 0x1aad,
+ 0x1b5a, 0x1b60,
+ 0x1bfc, 0x1bff,
+ 0x1c3b, 0x1c3f,
+ 0x1c7e, 0x1c7f,
+ 0x1cc0, 0x1cc7,
+ 0x1cd3, 0x1cd3,
+ 0x2010, 0x2027,
+ 0x2030, 0x2043,
+ 0x2045, 0x2051,
+ 0x2053, 0x205e,
+ 0x207d, 0x207e,
+ 0x208d, 0x208e,
+ 0x2308, 0x230b,
+ 0x2329, 0x232a,
+ 0x2768, 0x2775,
+ 0x27c5, 0x27c6,
+ 0x27e6, 0x27ef,
+ 0x2983, 0x2998,
+ 0x29d8, 0x29db,
+ 0x29fc, 0x29fd,
+ 0x2cf9, 0x2cfc,
+ 0x2cfe, 0x2cff,
+ 0x2d70, 0x2d70,
+ 0x2e00, 0x2e2e,
+ 0x2e30, 0x2e4f,
+ 0x3001, 0x3003,
+ 0x3008, 0x3011,
+ 0x3014, 0x301f,
+ 0x3030, 0x3030,
+ 0x303d, 0x303d,
+ 0x30a0, 0x30a0,
+ 0x30fb, 0x30fb,
+ 0xa4fe, 0xa4ff,
+ 0xa60d, 0xa60f,
+ 0xa673, 0xa673,
+ 0xa67e, 0xa67e,
+ 0xa6f2, 0xa6f7,
+ 0xa874, 0xa877,
+ 0xa8ce, 0xa8cf,
+ 0xa8f8, 0xa8fa,
+ 0xa8fc, 0xa8fc,
+ 0xa92e, 0xa92f,
+ 0xa95f, 0xa95f,
+ 0xa9c1, 0xa9cd,
+ 0xa9de, 0xa9df,
+ 0xaa5c, 0xaa5f,
+ 0xaade, 0xaadf,
+ 0xaaf0, 0xaaf1,
+ 0xabeb, 0xabeb,
+ 0xfd3e, 0xfd3f,
+ 0xfe10, 0xfe19,
+ 0xfe30, 0xfe52,
+ 0xfe54, 0xfe61,
+ 0xfe63, 0xfe63,
+ 0xfe68, 0xfe68,
+ 0xfe6a, 0xfe6b,
+ 0xff01, 0xff03,
+ 0xff05, 0xff0a,
+ 0xff0c, 0xff0f,
+ 0xff1a, 0xff1b,
+ 0xff1f, 0xff20,
+ 0xff3b, 0xff3d,
+ 0xff3f, 0xff3f,
+ 0xff5b, 0xff5b,
+ 0xff5d, 0xff5d,
+ 0xff5f, 0xff65,
+ 0x10100, 0x10102,
+ 0x1039f, 0x1039f,
+ 0x103d0, 0x103d0,
+ 0x1056f, 0x1056f,
+ 0x10857, 0x10857,
+ 0x1091f, 0x1091f,
+ 0x1093f, 0x1093f,
+ 0x10a50, 0x10a58,
+ 0x10a7f, 0x10a7f,
+ 0x10af0, 0x10af6,
+ 0x10b39, 0x10b3f,
+ 0x10b99, 0x10b9c,
+ 0x10f55, 0x10f59,
+ 0x11047, 0x1104d,
+ 0x110bb, 0x110bc,
+ 0x110be, 0x110c1,
+ 0x11140, 0x11143,
+ 0x11174, 0x11175,
+ 0x111c5, 0x111c8,
+ 0x111cd, 0x111cd,
+ 0x111db, 0x111db,
+ 0x111dd, 0x111df,
+ 0x11238, 0x1123d,
+ 0x112a9, 0x112a9,
+ 0x1144b, 0x1144f,
+ 0x1145b, 0x1145b,
+ 0x1145d, 0x1145d,
+ 0x114c6, 0x114c6,
+ 0x115c1, 0x115d7,
+ 0x11641, 0x11643,
+ 0x11660, 0x1166c,
+ 0x1173c, 0x1173e,
+ 0x1183b, 0x1183b,
+ 0x119e2, 0x119e2,
+ 0x11a3f, 0x11a46,
+ 0x11a9a, 0x11a9c,
+ 0x11a9e, 0x11aa2,
+ 0x11c41, 0x11c45,
+ 0x11c70, 0x11c71,
+ 0x11ef7, 0x11ef8,
+ 0x11fff, 0x11fff,
+ 0x12470, 0x12474,
+ 0x16a6e, 0x16a6f,
+ 0x16af5, 0x16af5,
+ 0x16b37, 0x16b3b,
+ 0x16b44, 0x16b44,
+ 0x16e97, 0x16e9a,
+ 0x16fe2, 0x16fe2,
+ 0x1bc9f, 0x1bc9f,
+ 0x1da87, 0x1da8b,
+ 0x1e95e, 0x1e95f,
+}; /* CR_XPosixPunct */
+
+/* 'Space': [[:Space:]] */
+static const OnigCodePoint CR_Space[] = {
+ 10,
+ 0x0009, 0x000d,
+ 0x0020, 0x0020,
+ 0x0085, 0x0085,
+ 0x00a0, 0x00a0,
+ 0x1680, 0x1680,
+ 0x2000, 0x200a,
+ 0x2028, 0x2029,
+ 0x202f, 0x202f,
+ 0x205f, 0x205f,
+ 0x3000, 0x3000,
+}; /* CR_Space */
+
+/* 'Upper': [[:Upper:]] */
+static const OnigCodePoint CR_Upper[] = {
+ 641,
+ 0x0041, 0x005a,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00de,
+ 0x0100, 0x0100,
+ 0x0102, 0x0102,
+ 0x0104, 0x0104,
+ 0x0106, 0x0106,
+ 0x0108, 0x0108,
+ 0x010a, 0x010a,
+ 0x010c, 0x010c,
+ 0x010e, 0x010e,
+ 0x0110, 0x0110,
+ 0x0112, 0x0112,
+ 0x0114, 0x0114,
+ 0x0116, 0x0116,
+ 0x0118, 0x0118,
+ 0x011a, 0x011a,
+ 0x011c, 0x011c,
+ 0x011e, 0x011e,
+ 0x0120, 0x0120,
+ 0x0122, 0x0122,
+ 0x0124, 0x0124,
+ 0x0126, 0x0126,
+ 0x0128, 0x0128,
+ 0x012a, 0x012a,
+ 0x012c, 0x012c,
+ 0x012e, 0x012e,
+ 0x0130, 0x0130,
+ 0x0132, 0x0132,
+ 0x0134, 0x0134,
+ 0x0136, 0x0136,
+ 0x0139, 0x0139,
+ 0x013b, 0x013b,
+ 0x013d, 0x013d,
+ 0x013f, 0x013f,
+ 0x0141, 0x0141,
+ 0x0143, 0x0143,
+ 0x0145, 0x0145,
+ 0x0147, 0x0147,
+ 0x014a, 0x014a,
+ 0x014c, 0x014c,
+ 0x014e, 0x014e,
+ 0x0150, 0x0150,
+ 0x0152, 0x0152,
+ 0x0154, 0x0154,
+ 0x0156, 0x0156,
+ 0x0158, 0x0158,
+ 0x015a, 0x015a,
+ 0x015c, 0x015c,
+ 0x015e, 0x015e,
+ 0x0160, 0x0160,
+ 0x0162, 0x0162,
+ 0x0164, 0x0164,
+ 0x0166, 0x0166,
+ 0x0168, 0x0168,
+ 0x016a, 0x016a,
+ 0x016c, 0x016c,
+ 0x016e, 0x016e,
+ 0x0170, 0x0170,
+ 0x0172, 0x0172,
+ 0x0174, 0x0174,
+ 0x0176, 0x0176,
+ 0x0178, 0x0179,
+ 0x017b, 0x017b,
+ 0x017d, 0x017d,
+ 0x0181, 0x0182,
+ 0x0184, 0x0184,
+ 0x0186, 0x0187,
+ 0x0189, 0x018b,
+ 0x018e, 0x0191,
+ 0x0193, 0x0194,
+ 0x0196, 0x0198,
+ 0x019c, 0x019d,
+ 0x019f, 0x01a0,
+ 0x01a2, 0x01a2,
+ 0x01a4, 0x01a4,
+ 0x01a6, 0x01a7,
+ 0x01a9, 0x01a9,
+ 0x01ac, 0x01ac,
+ 0x01ae, 0x01af,
+ 0x01b1, 0x01b3,
+ 0x01b5, 0x01b5,
+ 0x01b7, 0x01b8,
+ 0x01bc, 0x01bc,
+ 0x01c4, 0x01c4,
+ 0x01c7, 0x01c7,
+ 0x01ca, 0x01ca,
+ 0x01cd, 0x01cd,
+ 0x01cf, 0x01cf,
+ 0x01d1, 0x01d1,
+ 0x01d3, 0x01d3,
+ 0x01d5, 0x01d5,
+ 0x01d7, 0x01d7,
+ 0x01d9, 0x01d9,
+ 0x01db, 0x01db,
+ 0x01de, 0x01de,
+ 0x01e0, 0x01e0,
+ 0x01e2, 0x01e2,
+ 0x01e4, 0x01e4,
+ 0x01e6, 0x01e6,
+ 0x01e8, 0x01e8,
+ 0x01ea, 0x01ea,
+ 0x01ec, 0x01ec,
+ 0x01ee, 0x01ee,
+ 0x01f1, 0x01f1,
+ 0x01f4, 0x01f4,
+ 0x01f6, 0x01f8,
+ 0x01fa, 0x01fa,
+ 0x01fc, 0x01fc,
+ 0x01fe, 0x01fe,
+ 0x0200, 0x0200,
+ 0x0202, 0x0202,
+ 0x0204, 0x0204,
+ 0x0206, 0x0206,
+ 0x0208, 0x0208,
+ 0x020a, 0x020a,
+ 0x020c, 0x020c,
+ 0x020e, 0x020e,
+ 0x0210, 0x0210,
+ 0x0212, 0x0212,
+ 0x0214, 0x0214,
+ 0x0216, 0x0216,
+ 0x0218, 0x0218,
+ 0x021a, 0x021a,
+ 0x021c, 0x021c,
+ 0x021e, 0x021e,
+ 0x0220, 0x0220,
+ 0x0222, 0x0222,
+ 0x0224, 0x0224,
+ 0x0226, 0x0226,
+ 0x0228, 0x0228,
+ 0x022a, 0x022a,
+ 0x022c, 0x022c,
+ 0x022e, 0x022e,
+ 0x0230, 0x0230,
+ 0x0232, 0x0232,
+ 0x023a, 0x023b,
+ 0x023d, 0x023e,
+ 0x0241, 0x0241,
+ 0x0243, 0x0246,
+ 0x0248, 0x0248,
+ 0x024a, 0x024a,
+ 0x024c, 0x024c,
+ 0x024e, 0x024e,
+ 0x0370, 0x0370,
+ 0x0372, 0x0372,
+ 0x0376, 0x0376,
+ 0x037f, 0x037f,
+ 0x0386, 0x0386,
+ 0x0388, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x038f,
+ 0x0391, 0x03a1,
+ 0x03a3, 0x03ab,
+ 0x03cf, 0x03cf,
+ 0x03d2, 0x03d4,
+ 0x03d8, 0x03d8,
+ 0x03da, 0x03da,
+ 0x03dc, 0x03dc,
+ 0x03de, 0x03de,
+ 0x03e0, 0x03e0,
+ 0x03e2, 0x03e2,
+ 0x03e4, 0x03e4,
+ 0x03e6, 0x03e6,
+ 0x03e8, 0x03e8,
+ 0x03ea, 0x03ea,
+ 0x03ec, 0x03ec,
+ 0x03ee, 0x03ee,
+ 0x03f4, 0x03f4,
+ 0x03f7, 0x03f7,
+ 0x03f9, 0x03fa,
+ 0x03fd, 0x042f,
+ 0x0460, 0x0460,
+ 0x0462, 0x0462,
+ 0x0464, 0x0464,
+ 0x0466, 0x0466,
+ 0x0468, 0x0468,
+ 0x046a, 0x046a,
+ 0x046c, 0x046c,
+ 0x046e, 0x046e,
+ 0x0470, 0x0470,
+ 0x0472, 0x0472,
+ 0x0474, 0x0474,
+ 0x0476, 0x0476,
+ 0x0478, 0x0478,
+ 0x047a, 0x047a,
+ 0x047c, 0x047c,
+ 0x047e, 0x047e,
+ 0x0480, 0x0480,
+ 0x048a, 0x048a,
+ 0x048c, 0x048c,
+ 0x048e, 0x048e,
+ 0x0490, 0x0490,
+ 0x0492, 0x0492,
+ 0x0494, 0x0494,
+ 0x0496, 0x0496,
+ 0x0498, 0x0498,
+ 0x049a, 0x049a,
+ 0x049c, 0x049c,
+ 0x049e, 0x049e,
+ 0x04a0, 0x04a0,
+ 0x04a2, 0x04a2,
+ 0x04a4, 0x04a4,
+ 0x04a6, 0x04a6,
+ 0x04a8, 0x04a8,
+ 0x04aa, 0x04aa,
+ 0x04ac, 0x04ac,
+ 0x04ae, 0x04ae,
+ 0x04b0, 0x04b0,
+ 0x04b2, 0x04b2,
+ 0x04b4, 0x04b4,
+ 0x04b6, 0x04b6,
+ 0x04b8, 0x04b8,
+ 0x04ba, 0x04ba,
+ 0x04bc, 0x04bc,
+ 0x04be, 0x04be,
+ 0x04c0, 0x04c1,
+ 0x04c3, 0x04c3,
+ 0x04c5, 0x04c5,
+ 0x04c7, 0x04c7,
+ 0x04c9, 0x04c9,
+ 0x04cb, 0x04cb,
+ 0x04cd, 0x04cd,
+ 0x04d0, 0x04d0,
+ 0x04d2, 0x04d2,
+ 0x04d4, 0x04d4,
+ 0x04d6, 0x04d6,
+ 0x04d8, 0x04d8,
+ 0x04da, 0x04da,
+ 0x04dc, 0x04dc,
+ 0x04de, 0x04de,
+ 0x04e0, 0x04e0,
+ 0x04e2, 0x04e2,
+ 0x04e4, 0x04e4,
+ 0x04e6, 0x04e6,
+ 0x04e8, 0x04e8,
+ 0x04ea, 0x04ea,
+ 0x04ec, 0x04ec,
+ 0x04ee, 0x04ee,
+ 0x04f0, 0x04f0,
+ 0x04f2, 0x04f2,
+ 0x04f4, 0x04f4,
+ 0x04f6, 0x04f6,
+ 0x04f8, 0x04f8,
+ 0x04fa, 0x04fa,
+ 0x04fc, 0x04fc,
+ 0x04fe, 0x04fe,
+ 0x0500, 0x0500,
+ 0x0502, 0x0502,
+ 0x0504, 0x0504,
+ 0x0506, 0x0506,
+ 0x0508, 0x0508,
+ 0x050a, 0x050a,
+ 0x050c, 0x050c,
+ 0x050e, 0x050e,
+ 0x0510, 0x0510,
+ 0x0512, 0x0512,
+ 0x0514, 0x0514,
+ 0x0516, 0x0516,
+ 0x0518, 0x0518,
+ 0x051a, 0x051a,
+ 0x051c, 0x051c,
+ 0x051e, 0x051e,
+ 0x0520, 0x0520,
+ 0x0522, 0x0522,
+ 0x0524, 0x0524,
+ 0x0526, 0x0526,
+ 0x0528, 0x0528,
+ 0x052a, 0x052a,
+ 0x052c, 0x052c,
+ 0x052e, 0x052e,
+ 0x0531, 0x0556,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x13a0, 0x13f5,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x1e00, 0x1e00,
+ 0x1e02, 0x1e02,
+ 0x1e04, 0x1e04,
+ 0x1e06, 0x1e06,
+ 0x1e08, 0x1e08,
+ 0x1e0a, 0x1e0a,
+ 0x1e0c, 0x1e0c,
+ 0x1e0e, 0x1e0e,
+ 0x1e10, 0x1e10,
+ 0x1e12, 0x1e12,
+ 0x1e14, 0x1e14,
+ 0x1e16, 0x1e16,
+ 0x1e18, 0x1e18,
+ 0x1e1a, 0x1e1a,
+ 0x1e1c, 0x1e1c,
+ 0x1e1e, 0x1e1e,
+ 0x1e20, 0x1e20,
+ 0x1e22, 0x1e22,
+ 0x1e24, 0x1e24,
+ 0x1e26, 0x1e26,
+ 0x1e28, 0x1e28,
+ 0x1e2a, 0x1e2a,
+ 0x1e2c, 0x1e2c,
+ 0x1e2e, 0x1e2e,
+ 0x1e30, 0x1e30,
+ 0x1e32, 0x1e32,
+ 0x1e34, 0x1e34,
+ 0x1e36, 0x1e36,
+ 0x1e38, 0x1e38,
+ 0x1e3a, 0x1e3a,
+ 0x1e3c, 0x1e3c,
+ 0x1e3e, 0x1e3e,
+ 0x1e40, 0x1e40,
+ 0x1e42, 0x1e42,
+ 0x1e44, 0x1e44,
+ 0x1e46, 0x1e46,
+ 0x1e48, 0x1e48,
+ 0x1e4a, 0x1e4a,
+ 0x1e4c, 0x1e4c,
+ 0x1e4e, 0x1e4e,
+ 0x1e50, 0x1e50,
+ 0x1e52, 0x1e52,
+ 0x1e54, 0x1e54,
+ 0x1e56, 0x1e56,
+ 0x1e58, 0x1e58,
+ 0x1e5a, 0x1e5a,
+ 0x1e5c, 0x1e5c,
+ 0x1e5e, 0x1e5e,
+ 0x1e60, 0x1e60,
+ 0x1e62, 0x1e62,
+ 0x1e64, 0x1e64,
+ 0x1e66, 0x1e66,
+ 0x1e68, 0x1e68,
+ 0x1e6a, 0x1e6a,
+ 0x1e6c, 0x1e6c,
+ 0x1e6e, 0x1e6e,
+ 0x1e70, 0x1e70,
+ 0x1e72, 0x1e72,
+ 0x1e74, 0x1e74,
+ 0x1e76, 0x1e76,
+ 0x1e78, 0x1e78,
+ 0x1e7a, 0x1e7a,
+ 0x1e7c, 0x1e7c,
+ 0x1e7e, 0x1e7e,
+ 0x1e80, 0x1e80,
+ 0x1e82, 0x1e82,
+ 0x1e84, 0x1e84,
+ 0x1e86, 0x1e86,
+ 0x1e88, 0x1e88,
+ 0x1e8a, 0x1e8a,
+ 0x1e8c, 0x1e8c,
+ 0x1e8e, 0x1e8e,
+ 0x1e90, 0x1e90,
+ 0x1e92, 0x1e92,
+ 0x1e94, 0x1e94,
+ 0x1e9e, 0x1e9e,
+ 0x1ea0, 0x1ea0,
+ 0x1ea2, 0x1ea2,
+ 0x1ea4, 0x1ea4,
+ 0x1ea6, 0x1ea6,
+ 0x1ea8, 0x1ea8,
+ 0x1eaa, 0x1eaa,
+ 0x1eac, 0x1eac,
+ 0x1eae, 0x1eae,
+ 0x1eb0, 0x1eb0,
+ 0x1eb2, 0x1eb2,
+ 0x1eb4, 0x1eb4,
+ 0x1eb6, 0x1eb6,
+ 0x1eb8, 0x1eb8,
+ 0x1eba, 0x1eba,
+ 0x1ebc, 0x1ebc,
+ 0x1ebe, 0x1ebe,
+ 0x1ec0, 0x1ec0,
+ 0x1ec2, 0x1ec2,
+ 0x1ec4, 0x1ec4,
+ 0x1ec6, 0x1ec6,
+ 0x1ec8, 0x1ec8,
+ 0x1eca, 0x1eca,
+ 0x1ecc, 0x1ecc,
+ 0x1ece, 0x1ece,
+ 0x1ed0, 0x1ed0,
+ 0x1ed2, 0x1ed2,
+ 0x1ed4, 0x1ed4,
+ 0x1ed6, 0x1ed6,
+ 0x1ed8, 0x1ed8,
+ 0x1eda, 0x1eda,
+ 0x1edc, 0x1edc,
+ 0x1ede, 0x1ede,
+ 0x1ee0, 0x1ee0,
+ 0x1ee2, 0x1ee2,
+ 0x1ee4, 0x1ee4,
+ 0x1ee6, 0x1ee6,
+ 0x1ee8, 0x1ee8,
+ 0x1eea, 0x1eea,
+ 0x1eec, 0x1eec,
+ 0x1eee, 0x1eee,
+ 0x1ef0, 0x1ef0,
+ 0x1ef2, 0x1ef2,
+ 0x1ef4, 0x1ef4,
+ 0x1ef6, 0x1ef6,
+ 0x1ef8, 0x1ef8,
+ 0x1efa, 0x1efa,
+ 0x1efc, 0x1efc,
+ 0x1efe, 0x1efe,
+ 0x1f08, 0x1f0f,
+ 0x1f18, 0x1f1d,
+ 0x1f28, 0x1f2f,
+ 0x1f38, 0x1f3f,
+ 0x1f48, 0x1f4d,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f5f,
+ 0x1f68, 0x1f6f,
+ 0x1fb8, 0x1fbb,
+ 0x1fc8, 0x1fcb,
+ 0x1fd8, 0x1fdb,
+ 0x1fe8, 0x1fec,
+ 0x1ff8, 0x1ffb,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210b, 0x210d,
+ 0x2110, 0x2112,
+ 0x2115, 0x2115,
+ 0x2119, 0x211d,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212a, 0x212d,
+ 0x2130, 0x2133,
+ 0x213e, 0x213f,
+ 0x2145, 0x2145,
+ 0x2160, 0x216f,
+ 0x2183, 0x2183,
+ 0x24b6, 0x24cf,
+ 0x2c00, 0x2c2e,
+ 0x2c60, 0x2c60,
+ 0x2c62, 0x2c64,
+ 0x2c67, 0x2c67,
+ 0x2c69, 0x2c69,
+ 0x2c6b, 0x2c6b,
+ 0x2c6d, 0x2c70,
+ 0x2c72, 0x2c72,
+ 0x2c75, 0x2c75,
+ 0x2c7e, 0x2c80,
+ 0x2c82, 0x2c82,
+ 0x2c84, 0x2c84,
+ 0x2c86, 0x2c86,
+ 0x2c88, 0x2c88,
+ 0x2c8a, 0x2c8a,
+ 0x2c8c, 0x2c8c,
+ 0x2c8e, 0x2c8e,
+ 0x2c90, 0x2c90,
+ 0x2c92, 0x2c92,
+ 0x2c94, 0x2c94,
+ 0x2c96, 0x2c96,
+ 0x2c98, 0x2c98,
+ 0x2c9a, 0x2c9a,
+ 0x2c9c, 0x2c9c,
+ 0x2c9e, 0x2c9e,
+ 0x2ca0, 0x2ca0,
+ 0x2ca2, 0x2ca2,
+ 0x2ca4, 0x2ca4,
+ 0x2ca6, 0x2ca6,
+ 0x2ca8, 0x2ca8,
+ 0x2caa, 0x2caa,
+ 0x2cac, 0x2cac,
+ 0x2cae, 0x2cae,
+ 0x2cb0, 0x2cb0,
+ 0x2cb2, 0x2cb2,
+ 0x2cb4, 0x2cb4,
+ 0x2cb6, 0x2cb6,
+ 0x2cb8, 0x2cb8,
+ 0x2cba, 0x2cba,
+ 0x2cbc, 0x2cbc,
+ 0x2cbe, 0x2cbe,
+ 0x2cc0, 0x2cc0,
+ 0x2cc2, 0x2cc2,
+ 0x2cc4, 0x2cc4,
+ 0x2cc6, 0x2cc6,
+ 0x2cc8, 0x2cc8,
+ 0x2cca, 0x2cca,
+ 0x2ccc, 0x2ccc,
+ 0x2cce, 0x2cce,
+ 0x2cd0, 0x2cd0,
+ 0x2cd2, 0x2cd2,
+ 0x2cd4, 0x2cd4,
+ 0x2cd6, 0x2cd6,
+ 0x2cd8, 0x2cd8,
+ 0x2cda, 0x2cda,
+ 0x2cdc, 0x2cdc,
+ 0x2cde, 0x2cde,
+ 0x2ce0, 0x2ce0,
+ 0x2ce2, 0x2ce2,
+ 0x2ceb, 0x2ceb,
+ 0x2ced, 0x2ced,
+ 0x2cf2, 0x2cf2,
+ 0xa640, 0xa640,
+ 0xa642, 0xa642,
+ 0xa644, 0xa644,
+ 0xa646, 0xa646,
+ 0xa648, 0xa648,
+ 0xa64a, 0xa64a,
+ 0xa64c, 0xa64c,
+ 0xa64e, 0xa64e,
+ 0xa650, 0xa650,
+ 0xa652, 0xa652,
+ 0xa654, 0xa654,
+ 0xa656, 0xa656,
+ 0xa658, 0xa658,
+ 0xa65a, 0xa65a,
+ 0xa65c, 0xa65c,
+ 0xa65e, 0xa65e,
+ 0xa660, 0xa660,
+ 0xa662, 0xa662,
+ 0xa664, 0xa664,
+ 0xa666, 0xa666,
+ 0xa668, 0xa668,
+ 0xa66a, 0xa66a,
+ 0xa66c, 0xa66c,
+ 0xa680, 0xa680,
+ 0xa682, 0xa682,
+ 0xa684, 0xa684,
+ 0xa686, 0xa686,
+ 0xa688, 0xa688,
+ 0xa68a, 0xa68a,
+ 0xa68c, 0xa68c,
+ 0xa68e, 0xa68e,
+ 0xa690, 0xa690,
+ 0xa692, 0xa692,
+ 0xa694, 0xa694,
+ 0xa696, 0xa696,
+ 0xa698, 0xa698,
+ 0xa69a, 0xa69a,
+ 0xa722, 0xa722,
+ 0xa724, 0xa724,
+ 0xa726, 0xa726,
+ 0xa728, 0xa728,
+ 0xa72a, 0xa72a,
+ 0xa72c, 0xa72c,
+ 0xa72e, 0xa72e,
+ 0xa732, 0xa732,
+ 0xa734, 0xa734,
+ 0xa736, 0xa736,
+ 0xa738, 0xa738,
+ 0xa73a, 0xa73a,
+ 0xa73c, 0xa73c,
+ 0xa73e, 0xa73e,
+ 0xa740, 0xa740,
+ 0xa742, 0xa742,
+ 0xa744, 0xa744,
+ 0xa746, 0xa746,
+ 0xa748, 0xa748,
+ 0xa74a, 0xa74a,
+ 0xa74c, 0xa74c,
+ 0xa74e, 0xa74e,
+ 0xa750, 0xa750,
+ 0xa752, 0xa752,
+ 0xa754, 0xa754,
+ 0xa756, 0xa756,
+ 0xa758, 0xa758,
+ 0xa75a, 0xa75a,
+ 0xa75c, 0xa75c,
+ 0xa75e, 0xa75e,
+ 0xa760, 0xa760,
+ 0xa762, 0xa762,
+ 0xa764, 0xa764,
+ 0xa766, 0xa766,
+ 0xa768, 0xa768,
+ 0xa76a, 0xa76a,
+ 0xa76c, 0xa76c,
+ 0xa76e, 0xa76e,
+ 0xa779, 0xa779,
+ 0xa77b, 0xa77b,
+ 0xa77d, 0xa77e,
+ 0xa780, 0xa780,
+ 0xa782, 0xa782,
+ 0xa784, 0xa784,
+ 0xa786, 0xa786,
+ 0xa78b, 0xa78b,
+ 0xa78d, 0xa78d,
+ 0xa790, 0xa790,
+ 0xa792, 0xa792,
+ 0xa796, 0xa796,
+ 0xa798, 0xa798,
+ 0xa79a, 0xa79a,
+ 0xa79c, 0xa79c,
+ 0xa79e, 0xa79e,
+ 0xa7a0, 0xa7a0,
+ 0xa7a2, 0xa7a2,
+ 0xa7a4, 0xa7a4,
+ 0xa7a6, 0xa7a6,
+ 0xa7a8, 0xa7a8,
+ 0xa7aa, 0xa7ae,
+ 0xa7b0, 0xa7b4,
+ 0xa7b6, 0xa7b6,
+ 0xa7b8, 0xa7b8,
+ 0xa7ba, 0xa7ba,
+ 0xa7bc, 0xa7bc,
+ 0xa7be, 0xa7be,
+ 0xa7c2, 0xa7c2,
+ 0xa7c4, 0xa7c6,
+ 0xff21, 0xff3a,
+ 0x10400, 0x10427,
+ 0x104b0, 0x104d3,
+ 0x10c80, 0x10cb2,
+ 0x118a0, 0x118bf,
+ 0x16e40, 0x16e5f,
+ 0x1d400, 0x1d419,
+ 0x1d434, 0x1d44d,
+ 0x1d468, 0x1d481,
+ 0x1d49c, 0x1d49c,
+ 0x1d49e, 0x1d49f,
+ 0x1d4a2, 0x1d4a2,
+ 0x1d4a5, 0x1d4a6,
+ 0x1d4a9, 0x1d4ac,
+ 0x1d4ae, 0x1d4b5,
+ 0x1d4d0, 0x1d4e9,
+ 0x1d504, 0x1d505,
+ 0x1d507, 0x1d50a,
+ 0x1d50d, 0x1d514,
+ 0x1d516, 0x1d51c,
+ 0x1d538, 0x1d539,
+ 0x1d53b, 0x1d53e,
+ 0x1d540, 0x1d544,
+ 0x1d546, 0x1d546,
+ 0x1d54a, 0x1d550,
+ 0x1d56c, 0x1d585,
+ 0x1d5a0, 0x1d5b9,
+ 0x1d5d4, 0x1d5ed,
+ 0x1d608, 0x1d621,
+ 0x1d63c, 0x1d655,
+ 0x1d670, 0x1d689,
+ 0x1d6a8, 0x1d6c0,
+ 0x1d6e2, 0x1d6fa,
+ 0x1d71c, 0x1d734,
+ 0x1d756, 0x1d76e,
+ 0x1d790, 0x1d7a8,
+ 0x1d7ca, 0x1d7ca,
+ 0x1e900, 0x1e921,
+ 0x1f130, 0x1f149,
+ 0x1f150, 0x1f169,
+ 0x1f170, 0x1f189,
+}; /* CR_Upper */
+
+/* 'XDigit': [[:XDigit:]] */
+static const OnigCodePoint CR_XDigit[] = {
+ 3,
+ 0x0030, 0x0039,
+ 0x0041, 0x0046,
+ 0x0061, 0x0066,
+}; /* CR_XDigit */
+
+/* 'Word': [[:Word:]] */
+static const OnigCodePoint CR_Word[] = {
+ 716,
+ 0x0030, 0x0039,
+ 0x0041, 0x005a,
+ 0x005f, 0x005f,
+ 0x0061, 0x007a,
+ 0x00aa, 0x00aa,
+ 0x00b5, 0x00b5,
+ 0x00ba, 0x00ba,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00f6,
+ 0x00f8, 0x02c1,
+ 0x02c6, 0x02d1,
+ 0x02e0, 0x02e4,
+ 0x02ec, 0x02ec,
+ 0x02ee, 0x02ee,
+ 0x0300, 0x0374,
+ 0x0376, 0x0377,
+ 0x037a, 0x037d,
+ 0x037f, 0x037f,
+ 0x0386, 0x0386,
+ 0x0388, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03f5,
+ 0x03f7, 0x0481,
+ 0x0483, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x0559,
+ 0x0560, 0x0588,
+ 0x0591, 0x05bd,
+ 0x05bf, 0x05bf,
+ 0x05c1, 0x05c2,
+ 0x05c4, 0x05c5,
+ 0x05c7, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f2,
+ 0x0610, 0x061a,
+ 0x0620, 0x0669,
+ 0x066e, 0x06d3,
+ 0x06d5, 0x06dc,
+ 0x06df, 0x06e8,
+ 0x06ea, 0x06fc,
+ 0x06ff, 0x06ff,
+ 0x0710, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07f5,
+ 0x07fa, 0x07fa,
+ 0x07fd, 0x07fd,
+ 0x0800, 0x082d,
+ 0x0840, 0x085b,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x08d3, 0x08e1,
+ 0x08e3, 0x0963,
+ 0x0966, 0x096f,
+ 0x0971, 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, 0x09f1,
+ 0x09fc, 0x09fc,
+ 0x09fe, 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, 0x0a75,
+ 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, 0x0aef,
+ 0x0af9, 0x0aff,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b63,
+ 0x0b66, 0x0b6f,
+ 0x0b71, 0x0b71,
+ 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, 0x0bef,
+ 0x0c00, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c80, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d00, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4e,
+ 0x0d54, 0x0d57,
+ 0x0d5f, 0x0d63,
+ 0x0d66, 0x0d6f,
+ 0x0d7a, 0x0d7f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0de6, 0x0def,
+ 0x0df2, 0x0df3,
+ 0x0e01, 0x0e3a,
+ 0x0e40, 0x0e4e,
+ 0x0e50, 0x0e59,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e86, 0x0e8a,
+ 0x0e8c, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edf,
+ 0x0f00, 0x0f00,
+ 0x0f18, 0x0f19,
+ 0x0f20, 0x0f29,
+ 0x0f35, 0x0f35,
+ 0x0f37, 0x0f37,
+ 0x0f39, 0x0f39,
+ 0x0f3e, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f71, 0x0f84,
+ 0x0f86, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fc6, 0x0fc6,
+ 0x1000, 0x1049,
+ 0x1050, 0x109d,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x10fa,
+ 0x10fc, 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, 0x135f,
+ 0x1380, 0x138f,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0x1401, 0x166c,
+ 0x166f, 0x167f,
+ 0x1681, 0x169a,
+ 0x16a0, 0x16ea,
+ 0x16ee, 0x16f8,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1734,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17d3,
+ 0x17d7, 0x17d7,
+ 0x17dc, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x180b, 0x180d,
+ 0x1810, 0x1819,
+ 0x1820, 0x1878,
+ 0x1880, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191e,
+ 0x1920, 0x192b,
+ 0x1930, 0x193b,
+ 0x1946, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19ab,
+ 0x19b0, 0x19c9,
+ 0x19d0, 0x19d9,
+ 0x1a00, 0x1a1b,
+ 0x1a20, 0x1a5e,
+ 0x1a60, 0x1a7c,
+ 0x1a7f, 0x1a89,
+ 0x1a90, 0x1a99,
+ 0x1aa7, 0x1aa7,
+ 0x1ab0, 0x1abe,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b59,
+ 0x1b6b, 0x1b73,
+ 0x1b80, 0x1bf3,
+ 0x1c00, 0x1c37,
+ 0x1c40, 0x1c49,
+ 0x1c4d, 0x1c7d,
+ 0x1c80, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x1cd0, 0x1cd2,
+ 0x1cd4, 0x1cfa,
+ 0x1d00, 0x1df9,
+ 0x1dfb, 0x1f15,
+ 0x1f18, 0x1f1d,
+ 0x1f20, 0x1f45,
+ 0x1f48, 0x1f4d,
+ 0x1f50, 0x1f57,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f7d,
+ 0x1f80, 0x1fb4,
+ 0x1fb6, 0x1fbc,
+ 0x1fbe, 0x1fbe,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fcc,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fdb,
+ 0x1fe0, 0x1fec,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ffc,
+ 0x203f, 0x2040,
+ 0x2054, 0x2054,
+ 0x2071, 0x2071,
+ 0x207f, 0x207f,
+ 0x2090, 0x209c,
+ 0x20d0, 0x20f0,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210a, 0x2113,
+ 0x2115, 0x2115,
+ 0x2119, 0x211d,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212a, 0x212d,
+ 0x212f, 0x2139,
+ 0x213c, 0x213f,
+ 0x2145, 0x2149,
+ 0x214e, 0x214e,
+ 0x2160, 0x2188,
+ 0x24b6, 0x24e9,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2ce4,
+ 0x2ceb, 0x2cf3,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0x2d30, 0x2d67,
+ 0x2d6f, 0x2d6f,
+ 0x2d7f, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x2de0, 0x2dff,
+ 0x2e2f, 0x2e2f,
+ 0x3005, 0x3007,
+ 0x3021, 0x302f,
+ 0x3031, 0x3035,
+ 0x3038, 0x303c,
+ 0x3041, 0x3096,
+ 0x3099, 0x309a,
+ 0x309d, 0x309f,
+ 0x30a1, 0x30fa,
+ 0x30fc, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x31a0, 0x31ba,
+ 0x31f0, 0x31ff,
+ 0x3400, 0x4db5,
+ 0x4e00, 0x9fef,
+ 0xa000, 0xa48c,
+ 0xa4d0, 0xa4fd,
+ 0xa500, 0xa60c,
+ 0xa610, 0xa62b,
+ 0xa640, 0xa672,
+ 0xa674, 0xa67d,
+ 0xa67f, 0xa6f1,
+ 0xa717, 0xa71f,
+ 0xa722, 0xa788,
+ 0xa78b, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f7, 0xa827,
+ 0xa840, 0xa873,
+ 0xa880, 0xa8c5,
+ 0xa8d0, 0xa8d9,
+ 0xa8e0, 0xa8f7,
+ 0xa8fb, 0xa8fb,
+ 0xa8fd, 0xa92d,
+ 0xa930, 0xa953,
+ 0xa960, 0xa97c,
+ 0xa980, 0xa9c0,
+ 0xa9cf, 0xa9d9,
+ 0xa9e0, 0xa9fe,
+ 0xaa00, 0xaa36,
+ 0xaa40, 0xaa4d,
+ 0xaa50, 0xaa59,
+ 0xaa60, 0xaa76,
+ 0xaa7a, 0xaac2,
+ 0xaadb, 0xaadd,
+ 0xaae0, 0xaaef,
+ 0xaaf2, 0xaaf6,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xab30, 0xab5a,
+ 0xab5c, 0xab67,
+ 0xab70, 0xabea,
+ 0xabec, 0xabed,
+ 0xabf0, 0xabf9,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xf900, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb28,
+ 0xfb2a, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3d,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfb,
+ 0xfe00, 0xfe0f,
+ 0xfe20, 0xfe2f,
+ 0xfe33, 0xfe34,
+ 0xfe4d, 0xfe4f,
+ 0xfe70, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0xff10, 0xff19,
+ 0xff21, 0xff3a,
+ 0xff3f, 0xff3f,
+ 0xff41, 0xff5a,
+ 0xff66, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0x10000, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+ 0x10140, 0x10174,
+ 0x101fd, 0x101fd,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x102e0, 0x102e0,
+ 0x10300, 0x1031f,
+ 0x1032d, 0x1034a,
+ 0x10350, 0x1037a,
+ 0x10380, 0x1039d,
+ 0x103a0, 0x103c3,
+ 0x103c8, 0x103cf,
+ 0x103d1, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10860, 0x10876,
+ 0x10880, 0x1089e,
+ 0x108e0, 0x108f2,
+ 0x108f4, 0x108f5,
+ 0x10900, 0x10915,
+ 0x10920, 0x10939,
+ 0x10980, 0x109b7,
+ 0x109be, 0x109bf,
+ 0x10a00, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a35,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a3f,
+ 0x10a60, 0x10a7c,
+ 0x10a80, 0x10a9c,
+ 0x10ac0, 0x10ac7,
+ 0x10ac9, 0x10ae6,
+ 0x10b00, 0x10b35,
+ 0x10b40, 0x10b55,
+ 0x10b60, 0x10b72,
+ 0x10b80, 0x10b91,
+ 0x10c00, 0x10c48,
+ 0x10c80, 0x10cb2,
+ 0x10cc0, 0x10cf2,
+ 0x10d00, 0x10d27,
+ 0x10d30, 0x10d39,
+ 0x10f00, 0x10f1c,
+ 0x10f27, 0x10f27,
+ 0x10f30, 0x10f50,
+ 0x10fe0, 0x10ff6,
+ 0x11000, 0x11046,
+ 0x11066, 0x1106f,
+ 0x1107f, 0x110ba,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x1113f,
+ 0x11144, 0x11146,
+ 0x11150, 0x11173,
+ 0x11176, 0x11176,
+ 0x11180, 0x111c4,
+ 0x111c9, 0x111cc,
+ 0x111d0, 0x111da,
+ 0x111dc, 0x111dc,
+ 0x11200, 0x11211,
+ 0x11213, 0x11237,
+ 0x1123e, 0x1123e,
+ 0x11280, 0x11286,
+ 0x11288, 0x11288,
+ 0x1128a, 0x1128d,
+ 0x1128f, 0x1129d,
+ 0x1129f, 0x112a8,
+ 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, 0x1144a,
+ 0x11450, 0x11459,
+ 0x1145e, 0x1145f,
+ 0x11480, 0x114c5,
+ 0x114c7, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115c0,
+ 0x115d8, 0x115dd,
+ 0x11600, 0x11640,
+ 0x11644, 0x11644,
+ 0x11650, 0x11659,
+ 0x11680, 0x116b8,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x1171a,
+ 0x1171d, 0x1172b,
+ 0x11730, 0x11739,
+ 0x11800, 0x1183a,
+ 0x118a0, 0x118e9,
+ 0x118ff, 0x118ff,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d7,
+ 0x119da, 0x119e1,
+ 0x119e3, 0x119e4,
+ 0x11a00, 0x11a3e,
+ 0x11a47, 0x11a47,
+ 0x11a50, 0x11a99,
+ 0x11a9d, 0x11a9d,
+ 0x11ac0, 0x11af8,
+ 0x11c00, 0x11c08,
+ 0x11c0a, 0x11c36,
+ 0x11c38, 0x11c40,
+ 0x11c50, 0x11c59,
+ 0x11c72, 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, 0x11ef6,
+ 0x12000, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af4,
+ 0x16b00, 0x16b36,
+ 0x16b40, 0x16b43,
+ 0x16b50, 0x16b59,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e7f,
+ 0x16f00, 0x16f4a,
+ 0x16f4f, 0x16f87,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe1,
+ 0x16fe3, 0x16fe3,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9d, 0x1bc9e,
+ 0x1d165, 0x1d169,
+ 0x1d16d, 0x1d172,
+ 0x1d17b, 0x1d182,
+ 0x1d185, 0x1d18b,
+ 0x1d1aa, 0x1d1ad,
+ 0x1d242, 0x1d244,
+ 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, 0x1d6c0,
+ 0x1d6c2, 0x1d6da,
+ 0x1d6dc, 0x1d6fa,
+ 0x1d6fc, 0x1d714,
+ 0x1d716, 0x1d734,
+ 0x1d736, 0x1d74e,
+ 0x1d750, 0x1d76e,
+ 0x1d770, 0x1d788,
+ 0x1d78a, 0x1d7a8,
+ 0x1d7aa, 0x1d7c2,
+ 0x1d7c4, 0x1d7cb,
+ 0x1d7ce, 0x1d7ff,
+ 0x1da00, 0x1da36,
+ 0x1da3b, 0x1da6c,
+ 0x1da75, 0x1da75,
+ 0x1da84, 0x1da84,
+ 0x1da9b, 0x1da9f,
+ 0x1daa1, 0x1daaf,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e100, 0x1e12c,
+ 0x1e130, 0x1e13d,
+ 0x1e140, 0x1e149,
+ 0x1e14e, 0x1e14e,
+ 0x1e2c0, 0x1e2f9,
+ 0x1e800, 0x1e8c4,
+ 0x1e8d0, 0x1e8d6,
+ 0x1e900, 0x1e94b,
+ 0x1e950, 0x1e959,
+ 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,
+ 0x1f130, 0x1f149,
+ 0x1f150, 0x1f169,
+ 0x1f170, 0x1f189,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+ 0xe0100, 0xe01ef,
+}; /* CR_Word */
+
+/* 'Alnum': [[:Alnum:]] */
+static const OnigCodePoint CR_Alnum[] = {
+ 715,
+ 0x0030, 0x0039,
+ 0x0041, 0x005a,
+ 0x0061, 0x007a,
+ 0x00aa, 0x00aa,
+ 0x00b5, 0x00b5,
+ 0x00ba, 0x00ba,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00f6,
+ 0x00f8, 0x02c1,
+ 0x02c6, 0x02d1,
+ 0x02e0, 0x02e4,
+ 0x02ec, 0x02ec,
+ 0x02ee, 0x02ee,
+ 0x0345, 0x0345,
+ 0x0370, 0x0374,
+ 0x0376, 0x0377,
+ 0x037a, 0x037d,
+ 0x037f, 0x037f,
+ 0x0386, 0x0386,
+ 0x0388, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03f5,
+ 0x03f7, 0x0481,
+ 0x048a, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x0559,
+ 0x0560, 0x0588,
+ 0x05b0, 0x05bd,
+ 0x05bf, 0x05bf,
+ 0x05c1, 0x05c2,
+ 0x05c4, 0x05c5,
+ 0x05c7, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f2,
+ 0x0610, 0x061a,
+ 0x0620, 0x0657,
+ 0x0659, 0x0669,
+ 0x066e, 0x06d3,
+ 0x06d5, 0x06dc,
+ 0x06e1, 0x06e8,
+ 0x06ed, 0x06fc,
+ 0x06ff, 0x06ff,
+ 0x0710, 0x073f,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07ea,
+ 0x07f4, 0x07f5,
+ 0x07fa, 0x07fa,
+ 0x0800, 0x0817,
+ 0x081a, 0x082c,
+ 0x0840, 0x0858,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x08d4, 0x08df,
+ 0x08e3, 0x08e9,
+ 0x08f0, 0x093b,
+ 0x093d, 0x094c,
+ 0x094e, 0x0950,
+ 0x0955, 0x0963,
+ 0x0966, 0x096f,
+ 0x0971, 0x0983,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bd, 0x09c4,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09cc,
+ 0x09ce, 0x09ce,
+ 0x09d7, 0x09d7,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e3,
+ 0x09e6, 0x09f1,
+ 0x09fc, 0x09fc,
+ 0x0a01, 0x0a03,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a3e, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4c,
+ 0x0a51, 0x0a51,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a66, 0x0a75,
+ 0x0a81, 0x0a83,
+ 0x0a85, 0x0a8d,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0abd, 0x0ac5,
+ 0x0ac7, 0x0ac9,
+ 0x0acb, 0x0acc,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae3,
+ 0x0ae6, 0x0aef,
+ 0x0af9, 0x0afc,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3d, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4c,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b63,
+ 0x0b66, 0x0b6f,
+ 0x0b71, 0x0b71,
+ 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, 0x0bcc,
+ 0x0bd0, 0x0bd0,
+ 0x0bd7, 0x0bd7,
+ 0x0be6, 0x0bef,
+ 0x0c00, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4c,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c80, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbd, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccc,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d00, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d3a,
+ 0x0d3d, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4c,
+ 0x0d4e, 0x0d4e,
+ 0x0d54, 0x0d57,
+ 0x0d5f, 0x0d63,
+ 0x0d66, 0x0d6f,
+ 0x0d7a, 0x0d7f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0de6, 0x0def,
+ 0x0df2, 0x0df3,
+ 0x0e01, 0x0e3a,
+ 0x0e40, 0x0e46,
+ 0x0e4d, 0x0e4d,
+ 0x0e50, 0x0e59,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e86, 0x0e8a,
+ 0x0e8c, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ecd, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edf,
+ 0x0f00, 0x0f00,
+ 0x0f20, 0x0f29,
+ 0x0f40, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f71, 0x0f81,
+ 0x0f88, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x1000, 0x1036,
+ 0x1038, 0x1038,
+ 0x103b, 0x1049,
+ 0x1050, 0x109d,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x10fa,
+ 0x10fc, 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,
+ 0x1380, 0x138f,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0x1401, 0x166c,
+ 0x166f, 0x167f,
+ 0x1681, 0x169a,
+ 0x16a0, 0x16ea,
+ 0x16ee, 0x16f8,
+ 0x1700, 0x170c,
+ 0x170e, 0x1713,
+ 0x1720, 0x1733,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17b3,
+ 0x17b6, 0x17c8,
+ 0x17d7, 0x17d7,
+ 0x17dc, 0x17dc,
+ 0x17e0, 0x17e9,
+ 0x1810, 0x1819,
+ 0x1820, 0x1878,
+ 0x1880, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191e,
+ 0x1920, 0x192b,
+ 0x1930, 0x1938,
+ 0x1946, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19ab,
+ 0x19b0, 0x19c9,
+ 0x19d0, 0x19d9,
+ 0x1a00, 0x1a1b,
+ 0x1a20, 0x1a5e,
+ 0x1a61, 0x1a74,
+ 0x1a80, 0x1a89,
+ 0x1a90, 0x1a99,
+ 0x1aa7, 0x1aa7,
+ 0x1b00, 0x1b33,
+ 0x1b35, 0x1b43,
+ 0x1b45, 0x1b4b,
+ 0x1b50, 0x1b59,
+ 0x1b80, 0x1ba9,
+ 0x1bac, 0x1be5,
+ 0x1be7, 0x1bf1,
+ 0x1c00, 0x1c36,
+ 0x1c40, 0x1c49,
+ 0x1c4d, 0x1c7d,
+ 0x1c80, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x1ce9, 0x1cec,
+ 0x1cee, 0x1cf3,
+ 0x1cf5, 0x1cf6,
+ 0x1cfa, 0x1cfa,
+ 0x1d00, 0x1dbf,
+ 0x1de7, 0x1df4,
+ 0x1e00, 0x1f15,
+ 0x1f18, 0x1f1d,
+ 0x1f20, 0x1f45,
+ 0x1f48, 0x1f4d,
+ 0x1f50, 0x1f57,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f7d,
+ 0x1f80, 0x1fb4,
+ 0x1fb6, 0x1fbc,
+ 0x1fbe, 0x1fbe,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fcc,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fdb,
+ 0x1fe0, 0x1fec,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ffc,
+ 0x2071, 0x2071,
+ 0x207f, 0x207f,
+ 0x2090, 0x209c,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210a, 0x2113,
+ 0x2115, 0x2115,
+ 0x2119, 0x211d,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212a, 0x212d,
+ 0x212f, 0x2139,
+ 0x213c, 0x213f,
+ 0x2145, 0x2149,
+ 0x214e, 0x214e,
+ 0x2160, 0x2188,
+ 0x24b6, 0x24e9,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2ce4,
+ 0x2ceb, 0x2cee,
+ 0x2cf2, 0x2cf3,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0x2d30, 0x2d67,
+ 0x2d6f, 0x2d6f,
+ 0x2d80, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x2de0, 0x2dff,
+ 0x2e2f, 0x2e2f,
+ 0x3005, 0x3007,
+ 0x3021, 0x3029,
+ 0x3031, 0x3035,
+ 0x3038, 0x303c,
+ 0x3041, 0x3096,
+ 0x309d, 0x309f,
+ 0x30a1, 0x30fa,
+ 0x30fc, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x31a0, 0x31ba,
+ 0x31f0, 0x31ff,
+ 0x3400, 0x4db5,
+ 0x4e00, 0x9fef,
+ 0xa000, 0xa48c,
+ 0xa4d0, 0xa4fd,
+ 0xa500, 0xa60c,
+ 0xa610, 0xa62b,
+ 0xa640, 0xa66e,
+ 0xa674, 0xa67b,
+ 0xa67f, 0xa6ef,
+ 0xa717, 0xa71f,
+ 0xa722, 0xa788,
+ 0xa78b, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f7, 0xa805,
+ 0xa807, 0xa827,
+ 0xa840, 0xa873,
+ 0xa880, 0xa8c3,
+ 0xa8c5, 0xa8c5,
+ 0xa8d0, 0xa8d9,
+ 0xa8f2, 0xa8f7,
+ 0xa8fb, 0xa8fb,
+ 0xa8fd, 0xa92a,
+ 0xa930, 0xa952,
+ 0xa960, 0xa97c,
+ 0xa980, 0xa9b2,
+ 0xa9b4, 0xa9bf,
+ 0xa9cf, 0xa9d9,
+ 0xa9e0, 0xa9fe,
+ 0xaa00, 0xaa36,
+ 0xaa40, 0xaa4d,
+ 0xaa50, 0xaa59,
+ 0xaa60, 0xaa76,
+ 0xaa7a, 0xaabe,
+ 0xaac0, 0xaac0,
+ 0xaac2, 0xaac2,
+ 0xaadb, 0xaadd,
+ 0xaae0, 0xaaef,
+ 0xaaf2, 0xaaf5,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xab30, 0xab5a,
+ 0xab5c, 0xab67,
+ 0xab70, 0xabea,
+ 0xabf0, 0xabf9,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xf900, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb28,
+ 0xfb2a, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3d,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfb,
+ 0xfe70, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0xff10, 0xff19,
+ 0xff21, 0xff3a,
+ 0xff41, 0xff5a,
+ 0xff66, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0x10000, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+ 0x10140, 0x10174,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x10300, 0x1031f,
+ 0x1032d, 0x1034a,
+ 0x10350, 0x1037a,
+ 0x10380, 0x1039d,
+ 0x103a0, 0x103c3,
+ 0x103c8, 0x103cf,
+ 0x103d1, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10860, 0x10876,
+ 0x10880, 0x1089e,
+ 0x108e0, 0x108f2,
+ 0x108f4, 0x108f5,
+ 0x10900, 0x10915,
+ 0x10920, 0x10939,
+ 0x10980, 0x109b7,
+ 0x109be, 0x109bf,
+ 0x10a00, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a35,
+ 0x10a60, 0x10a7c,
+ 0x10a80, 0x10a9c,
+ 0x10ac0, 0x10ac7,
+ 0x10ac9, 0x10ae4,
+ 0x10b00, 0x10b35,
+ 0x10b40, 0x10b55,
+ 0x10b60, 0x10b72,
+ 0x10b80, 0x10b91,
+ 0x10c00, 0x10c48,
+ 0x10c80, 0x10cb2,
+ 0x10cc0, 0x10cf2,
+ 0x10d00, 0x10d27,
+ 0x10d30, 0x10d39,
+ 0x10f00, 0x10f1c,
+ 0x10f27, 0x10f27,
+ 0x10f30, 0x10f45,
+ 0x10fe0, 0x10ff6,
+ 0x11000, 0x11045,
+ 0x11066, 0x1106f,
+ 0x11082, 0x110b8,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11132,
+ 0x11136, 0x1113f,
+ 0x11144, 0x11146,
+ 0x11150, 0x11172,
+ 0x11176, 0x11176,
+ 0x11180, 0x111bf,
+ 0x111c1, 0x111c4,
+ 0x111d0, 0x111da,
+ 0x111dc, 0x111dc,
+ 0x11200, 0x11211,
+ 0x11213, 0x11234,
+ 0x11237, 0x11237,
+ 0x1123e, 0x1123e,
+ 0x11280, 0x11286,
+ 0x11288, 0x11288,
+ 0x1128a, 0x1128d,
+ 0x1128f, 0x1129d,
+ 0x1129f, 0x112a8,
+ 0x112b0, 0x112e8,
+ 0x112f0, 0x112f9,
+ 0x11300, 0x11303,
+ 0x11305, 0x1130c,
+ 0x1130f, 0x11310,
+ 0x11313, 0x11328,
+ 0x1132a, 0x11330,
+ 0x11332, 0x11333,
+ 0x11335, 0x11339,
+ 0x1133d, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134b, 0x1134c,
+ 0x11350, 0x11350,
+ 0x11357, 0x11357,
+ 0x1135d, 0x11363,
+ 0x11400, 0x11441,
+ 0x11443, 0x11445,
+ 0x11447, 0x1144a,
+ 0x11450, 0x11459,
+ 0x1145f, 0x1145f,
+ 0x11480, 0x114c1,
+ 0x114c4, 0x114c5,
+ 0x114c7, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115be,
+ 0x115d8, 0x115dd,
+ 0x11600, 0x1163e,
+ 0x11640, 0x11640,
+ 0x11644, 0x11644,
+ 0x11650, 0x11659,
+ 0x11680, 0x116b5,
+ 0x116b8, 0x116b8,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x1171a,
+ 0x1171d, 0x1172a,
+ 0x11730, 0x11739,
+ 0x11800, 0x11838,
+ 0x118a0, 0x118e9,
+ 0x118ff, 0x118ff,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d7,
+ 0x119da, 0x119df,
+ 0x119e1, 0x119e1,
+ 0x119e3, 0x119e4,
+ 0x11a00, 0x11a32,
+ 0x11a35, 0x11a3e,
+ 0x11a50, 0x11a97,
+ 0x11a9d, 0x11a9d,
+ 0x11ac0, 0x11af8,
+ 0x11c00, 0x11c08,
+ 0x11c0a, 0x11c36,
+ 0x11c38, 0x11c3e,
+ 0x11c40, 0x11c40,
+ 0x11c50, 0x11c59,
+ 0x11c72, 0x11c8f,
+ 0x11c92, 0x11ca7,
+ 0x11ca9, 0x11cb6,
+ 0x11d00, 0x11d06,
+ 0x11d08, 0x11d09,
+ 0x11d0b, 0x11d36,
+ 0x11d3a, 0x11d3a,
+ 0x11d3c, 0x11d3d,
+ 0x11d3f, 0x11d41,
+ 0x11d43, 0x11d43,
+ 0x11d46, 0x11d47,
+ 0x11d50, 0x11d59,
+ 0x11d60, 0x11d65,
+ 0x11d67, 0x11d68,
+ 0x11d6a, 0x11d8e,
+ 0x11d90, 0x11d91,
+ 0x11d93, 0x11d96,
+ 0x11d98, 0x11d98,
+ 0x11da0, 0x11da9,
+ 0x11ee0, 0x11ef6,
+ 0x12000, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16ad0, 0x16aed,
+ 0x16b00, 0x16b2f,
+ 0x16b40, 0x16b43,
+ 0x16b50, 0x16b59,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e7f,
+ 0x16f00, 0x16f4a,
+ 0x16f4f, 0x16f87,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe1,
+ 0x16fe3, 0x16fe3,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9e, 0x1bc9e,
+ 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, 0x1d6c0,
+ 0x1d6c2, 0x1d6da,
+ 0x1d6dc, 0x1d6fa,
+ 0x1d6fc, 0x1d714,
+ 0x1d716, 0x1d734,
+ 0x1d736, 0x1d74e,
+ 0x1d750, 0x1d76e,
+ 0x1d770, 0x1d788,
+ 0x1d78a, 0x1d7a8,
+ 0x1d7aa, 0x1d7c2,
+ 0x1d7c4, 0x1d7cb,
+ 0x1d7ce, 0x1d7ff,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e100, 0x1e12c,
+ 0x1e137, 0x1e13d,
+ 0x1e140, 0x1e149,
+ 0x1e14e, 0x1e14e,
+ 0x1e2c0, 0x1e2eb,
+ 0x1e2f0, 0x1e2f9,
+ 0x1e800, 0x1e8c4,
+ 0x1e900, 0x1e943,
+ 0x1e947, 0x1e947,
+ 0x1e94b, 0x1e94b,
+ 0x1e950, 0x1e959,
+ 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,
+ 0x1f130, 0x1f149,
+ 0x1f150, 0x1f169,
+ 0x1f170, 0x1f189,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+}; /* CR_Alnum */
+
+/* 'ASCII': [[:ASCII:]] */
+static const OnigCodePoint CR_ASCII[] = {
+ 1,
+ 0x0000, 0x007f,
+}; /* CR_ASCII */
+
+/* 'Punct' */
+static const OnigCodePoint CR_Punct[] = {
+ 182,
+ 0x0021, 0x0023,
+ 0x0025, 0x002a,
+ 0x002c, 0x002f,
+ 0x003a, 0x003b,
+ 0x003f, 0x0040,
+ 0x005b, 0x005d,
+ 0x005f, 0x005f,
+ 0x007b, 0x007b,
+ 0x007d, 0x007d,
+ 0x00a1, 0x00a1,
+ 0x00a7, 0x00a7,
+ 0x00ab, 0x00ab,
+ 0x00b6, 0x00b7,
+ 0x00bb, 0x00bb,
+ 0x00bf, 0x00bf,
+ 0x037e, 0x037e,
+ 0x0387, 0x0387,
+ 0x055a, 0x055f,
+ 0x0589, 0x058a,
+ 0x05be, 0x05be,
+ 0x05c0, 0x05c0,
+ 0x05c3, 0x05c3,
+ 0x05c6, 0x05c6,
+ 0x05f3, 0x05f4,
+ 0x0609, 0x060a,
+ 0x060c, 0x060d,
+ 0x061b, 0x061b,
+ 0x061e, 0x061f,
+ 0x066a, 0x066d,
+ 0x06d4, 0x06d4,
+ 0x0700, 0x070d,
+ 0x07f7, 0x07f9,
+ 0x0830, 0x083e,
+ 0x085e, 0x085e,
+ 0x0964, 0x0965,
+ 0x0970, 0x0970,
+ 0x09fd, 0x09fd,
+ 0x0a76, 0x0a76,
+ 0x0af0, 0x0af0,
+ 0x0c77, 0x0c77,
+ 0x0c84, 0x0c84,
+ 0x0df4, 0x0df4,
+ 0x0e4f, 0x0e4f,
+ 0x0e5a, 0x0e5b,
+ 0x0f04, 0x0f12,
+ 0x0f14, 0x0f14,
+ 0x0f3a, 0x0f3d,
+ 0x0f85, 0x0f85,
+ 0x0fd0, 0x0fd4,
+ 0x0fd9, 0x0fda,
+ 0x104a, 0x104f,
+ 0x10fb, 0x10fb,
+ 0x1360, 0x1368,
+ 0x1400, 0x1400,
+ 0x166e, 0x166e,
+ 0x169b, 0x169c,
+ 0x16eb, 0x16ed,
+ 0x1735, 0x1736,
+ 0x17d4, 0x17d6,
+ 0x17d8, 0x17da,
+ 0x1800, 0x180a,
+ 0x1944, 0x1945,
+ 0x1a1e, 0x1a1f,
+ 0x1aa0, 0x1aa6,
+ 0x1aa8, 0x1aad,
+ 0x1b5a, 0x1b60,
+ 0x1bfc, 0x1bff,
+ 0x1c3b, 0x1c3f,
+ 0x1c7e, 0x1c7f,
+ 0x1cc0, 0x1cc7,
+ 0x1cd3, 0x1cd3,
+ 0x2010, 0x2027,
+ 0x2030, 0x2043,
+ 0x2045, 0x2051,
+ 0x2053, 0x205e,
+ 0x207d, 0x207e,
+ 0x208d, 0x208e,
+ 0x2308, 0x230b,
+ 0x2329, 0x232a,
+ 0x2768, 0x2775,
+ 0x27c5, 0x27c6,
+ 0x27e6, 0x27ef,
+ 0x2983, 0x2998,
+ 0x29d8, 0x29db,
+ 0x29fc, 0x29fd,
+ 0x2cf9, 0x2cfc,
+ 0x2cfe, 0x2cff,
+ 0x2d70, 0x2d70,
+ 0x2e00, 0x2e2e,
+ 0x2e30, 0x2e4f,
+ 0x3001, 0x3003,
+ 0x3008, 0x3011,
+ 0x3014, 0x301f,
+ 0x3030, 0x3030,
+ 0x303d, 0x303d,
+ 0x30a0, 0x30a0,
+ 0x30fb, 0x30fb,
+ 0xa4fe, 0xa4ff,
+ 0xa60d, 0xa60f,
+ 0xa673, 0xa673,
+ 0xa67e, 0xa67e,
+ 0xa6f2, 0xa6f7,
+ 0xa874, 0xa877,
+ 0xa8ce, 0xa8cf,
+ 0xa8f8, 0xa8fa,
+ 0xa8fc, 0xa8fc,
+ 0xa92e, 0xa92f,
+ 0xa95f, 0xa95f,
+ 0xa9c1, 0xa9cd,
+ 0xa9de, 0xa9df,
+ 0xaa5c, 0xaa5f,
+ 0xaade, 0xaadf,
+ 0xaaf0, 0xaaf1,
+ 0xabeb, 0xabeb,
+ 0xfd3e, 0xfd3f,
+ 0xfe10, 0xfe19,
+ 0xfe30, 0xfe52,
+ 0xfe54, 0xfe61,
+ 0xfe63, 0xfe63,
+ 0xfe68, 0xfe68,
+ 0xfe6a, 0xfe6b,
+ 0xff01, 0xff03,
+ 0xff05, 0xff0a,
+ 0xff0c, 0xff0f,
+ 0xff1a, 0xff1b,
+ 0xff1f, 0xff20,
+ 0xff3b, 0xff3d,
+ 0xff3f, 0xff3f,
+ 0xff5b, 0xff5b,
+ 0xff5d, 0xff5d,
+ 0xff5f, 0xff65,
+ 0x10100, 0x10102,
+ 0x1039f, 0x1039f,
+ 0x103d0, 0x103d0,
+ 0x1056f, 0x1056f,
+ 0x10857, 0x10857,
+ 0x1091f, 0x1091f,
+ 0x1093f, 0x1093f,
+ 0x10a50, 0x10a58,
+ 0x10a7f, 0x10a7f,
+ 0x10af0, 0x10af6,
+ 0x10b39, 0x10b3f,
+ 0x10b99, 0x10b9c,
+ 0x10f55, 0x10f59,
+ 0x11047, 0x1104d,
+ 0x110bb, 0x110bc,
+ 0x110be, 0x110c1,
+ 0x11140, 0x11143,
+ 0x11174, 0x11175,
+ 0x111c5, 0x111c8,
+ 0x111cd, 0x111cd,
+ 0x111db, 0x111db,
+ 0x111dd, 0x111df,
+ 0x11238, 0x1123d,
+ 0x112a9, 0x112a9,
+ 0x1144b, 0x1144f,
+ 0x1145b, 0x1145b,
+ 0x1145d, 0x1145d,
+ 0x114c6, 0x114c6,
+ 0x115c1, 0x115d7,
+ 0x11641, 0x11643,
+ 0x11660, 0x1166c,
+ 0x1173c, 0x1173e,
+ 0x1183b, 0x1183b,
+ 0x119e2, 0x119e2,
+ 0x11a3f, 0x11a46,
+ 0x11a9a, 0x11a9c,
+ 0x11a9e, 0x11aa2,
+ 0x11c41, 0x11c45,
+ 0x11c70, 0x11c71,
+ 0x11ef7, 0x11ef8,
+ 0x11fff, 0x11fff,
+ 0x12470, 0x12474,
+ 0x16a6e, 0x16a6f,
+ 0x16af5, 0x16af5,
+ 0x16b37, 0x16b3b,
+ 0x16b44, 0x16b44,
+ 0x16e97, 0x16e9a,
+ 0x16fe2, 0x16fe2,
+ 0x1bc9f, 0x1bc9f,
+ 0x1da87, 0x1da8b,
+ 0x1e95e, 0x1e95f,
+}; /* CR_Punct */
+
+#ifdef USE_UNICODE_PROPERTIES
+/* 'Any': - */
+static const OnigCodePoint CR_Any[] = {
+ 1,
+ 0x0000, 0x10ffff,
+}; /* CR_Any */
+
+/* 'Assigned': - */
+static const OnigCodePoint CR_Assigned[] = {
+ 666,
+ 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, 0x061c,
+ 0x061e, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x07fd, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x08d3, 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,
+ 0x0b56, 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,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c77, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d00, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4f,
+ 0x0d54, 0x0d63,
+ 0x0d66, 0x0d7f,
+ 0x0d82, 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, 0x0ecd,
+ 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, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 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, 0x1abe,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cc7,
+ 0x1cd0, 0x1cfa,
+ 0x1d00, 0x1df9,
+ 0x1dfb, 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, 0x20bf,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x218b,
+ 0x2190, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b98, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 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, 0x2e4f,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x3190, 0x31ba,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x4db5,
+ 0x4dc0, 0x9fef,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa6f7,
+ 0xa700, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f7, 0xa82b,
+ 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, 0xab67,
+ 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, 0xfbc1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfd,
+ 0xfe00, 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, 0xfffd,
+ 0x10000, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+ 0x10100, 0x10102,
+ 0x10107, 0x10133,
+ 0x10137, 0x1018e,
+ 0x10190, 0x1019b,
+ 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, 0x1056f,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 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,
+ 0x10f00, 0x10f27,
+ 0x10f30, 0x10f59,
+ 0x10fe0, 0x10ff6,
+ 0x11000, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x1107f, 0x110c1,
+ 0x110cd, 0x110cd,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x11146,
+ 0x11150, 0x11176,
+ 0x11180, 0x111cd,
+ 0x111d0, 0x111df,
+ 0x111e1, 0x111f4,
+ 0x11200, 0x11211,
+ 0x11213, 0x1123e,
+ 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, 0x11459,
+ 0x1145b, 0x1145b,
+ 0x1145d, 0x1145f,
+ 0x11480, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115dd,
+ 0x11600, 0x11644,
+ 0x11650, 0x11659,
+ 0x11660, 0x1166c,
+ 0x11680, 0x116b8,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x1171a,
+ 0x1171d, 0x1172b,
+ 0x11730, 0x1173f,
+ 0x11800, 0x1183b,
+ 0x118a0, 0x118f2,
+ 0x118ff, 0x118ff,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d7,
+ 0x119da, 0x119e4,
+ 0x11a00, 0x11a47,
+ 0x11a50, 0x11aa2,
+ 0x11ac0, 0x11af8,
+ 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,
+ 0x11fc0, 0x11ff1,
+ 0x11fff, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12470, 0x12474,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x13430, 0x13438,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16a6e, 0x16a6f,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af5,
+ 0x16b00, 0x16b45,
+ 0x16b50, 0x16b59,
+ 0x16b5b, 0x16b61,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e9a,
+ 0x16f00, 0x16f4a,
+ 0x16f4f, 0x16f87,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe3,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9c, 0x1bca3,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1e8,
+ 0x1d200, 0x1d245,
+ 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,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e100, 0x1e12c,
+ 0x1e130, 0x1e13d,
+ 0x1e140, 0x1e149,
+ 0x1e14e, 0x1e14f,
+ 0x1e2c0, 0x1e2f9,
+ 0x1e2ff, 0x1e2ff,
+ 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, 0x1f10c,
+ 0x1f110, 0x1f16c,
+ 0x1f170, 0x1f1ac,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23b,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f260, 0x1f265,
+ 0x1f300, 0x1f6d5,
+ 0x1f6e0, 0x1f6ec,
+ 0x1f6f0, 0x1f6fa,
+ 0x1f700, 0x1f773,
+ 0x1f780, 0x1f7d8,
+ 0x1f7e0, 0x1f7eb,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1f900, 0x1f90b,
+ 0x1f90d, 0x1f971,
+ 0x1f973, 0x1f976,
+ 0x1f97a, 0x1f9a2,
+ 0x1f9a5, 0x1f9aa,
+ 0x1f9ae, 0x1f9ca,
+ 0x1f9cd, 0x1fa53,
+ 0x1fa60, 0x1fa6d,
+ 0x1fa70, 0x1fa73,
+ 0x1fa78, 0x1fa7a,
+ 0x1fa80, 0x1fa82,
+ 0x1fa90, 0x1fa95,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+ 0xe0001, 0xe0001,
+ 0xe0020, 0xe007f,
+ 0xe0100, 0xe01ef,
+ 0xf0000, 0xffffd,
+ 0x100000, 0x10fffd,
+}; /* CR_Assigned */
+
+/* 'C': Major Category */
+static const OnigCodePoint CR_C[] = {
+ 668,
+ 0x0000, 0x001f,
+ 0x007f, 0x009f,
+ 0x00ad, 0x00ad,
+ 0x0378, 0x0379,
+ 0x0380, 0x0383,
+ 0x038b, 0x038b,
+ 0x038d, 0x038d,
+ 0x03a2, 0x03a2,
+ 0x0530, 0x0530,
+ 0x0557, 0x0558,
+ 0x058b, 0x058c,
+ 0x0590, 0x0590,
+ 0x05c8, 0x05cf,
+ 0x05eb, 0x05ee,
+ 0x05f5, 0x0605,
+ 0x061c, 0x061d,
+ 0x06dd, 0x06dd,
+ 0x070e, 0x070f,
+ 0x074b, 0x074c,
+ 0x07b2, 0x07bf,
+ 0x07fb, 0x07fc,
+ 0x082e, 0x082f,
+ 0x083f, 0x083f,
+ 0x085c, 0x085d,
+ 0x085f, 0x085f,
+ 0x086b, 0x089f,
+ 0x08b5, 0x08b5,
+ 0x08be, 0x08d2,
+ 0x08e2, 0x08e2,
+ 0x0984, 0x0984,
+ 0x098d, 0x098e,
+ 0x0991, 0x0992,
+ 0x09a9, 0x09a9,
+ 0x09b1, 0x09b1,
+ 0x09b3, 0x09b5,
+ 0x09ba, 0x09bb,
+ 0x09c5, 0x09c6,
+ 0x09c9, 0x09ca,
+ 0x09cf, 0x09d6,
+ 0x09d8, 0x09db,
+ 0x09de, 0x09de,
+ 0x09e4, 0x09e5,
+ 0x09ff, 0x0a00,
+ 0x0a04, 0x0a04,
+ 0x0a0b, 0x0a0e,
+ 0x0a11, 0x0a12,
+ 0x0a29, 0x0a29,
+ 0x0a31, 0x0a31,
+ 0x0a34, 0x0a34,
+ 0x0a37, 0x0a37,
+ 0x0a3a, 0x0a3b,
+ 0x0a3d, 0x0a3d,
+ 0x0a43, 0x0a46,
+ 0x0a49, 0x0a4a,
+ 0x0a4e, 0x0a50,
+ 0x0a52, 0x0a58,
+ 0x0a5d, 0x0a5d,
+ 0x0a5f, 0x0a65,
+ 0x0a77, 0x0a80,
+ 0x0a84, 0x0a84,
+ 0x0a8e, 0x0a8e,
+ 0x0a92, 0x0a92,
+ 0x0aa9, 0x0aa9,
+ 0x0ab1, 0x0ab1,
+ 0x0ab4, 0x0ab4,
+ 0x0aba, 0x0abb,
+ 0x0ac6, 0x0ac6,
+ 0x0aca, 0x0aca,
+ 0x0ace, 0x0acf,
+ 0x0ad1, 0x0adf,
+ 0x0ae4, 0x0ae5,
+ 0x0af2, 0x0af8,
+ 0x0b00, 0x0b00,
+ 0x0b04, 0x0b04,
+ 0x0b0d, 0x0b0e,
+ 0x0b11, 0x0b12,
+ 0x0b29, 0x0b29,
+ 0x0b31, 0x0b31,
+ 0x0b34, 0x0b34,
+ 0x0b3a, 0x0b3b,
+ 0x0b45, 0x0b46,
+ 0x0b49, 0x0b4a,
+ 0x0b4e, 0x0b55,
+ 0x0b58, 0x0b5b,
+ 0x0b5e, 0x0b5e,
+ 0x0b64, 0x0b65,
+ 0x0b78, 0x0b81,
+ 0x0b84, 0x0b84,
+ 0x0b8b, 0x0b8d,
+ 0x0b91, 0x0b91,
+ 0x0b96, 0x0b98,
+ 0x0b9b, 0x0b9b,
+ 0x0b9d, 0x0b9d,
+ 0x0ba0, 0x0ba2,
+ 0x0ba5, 0x0ba7,
+ 0x0bab, 0x0bad,
+ 0x0bba, 0x0bbd,
+ 0x0bc3, 0x0bc5,
+ 0x0bc9, 0x0bc9,
+ 0x0bce, 0x0bcf,
+ 0x0bd1, 0x0bd6,
+ 0x0bd8, 0x0be5,
+ 0x0bfb, 0x0bff,
+ 0x0c0d, 0x0c0d,
+ 0x0c11, 0x0c11,
+ 0x0c29, 0x0c29,
+ 0x0c3a, 0x0c3c,
+ 0x0c45, 0x0c45,
+ 0x0c49, 0x0c49,
+ 0x0c4e, 0x0c54,
+ 0x0c57, 0x0c57,
+ 0x0c5b, 0x0c5f,
+ 0x0c64, 0x0c65,
+ 0x0c70, 0x0c76,
+ 0x0c8d, 0x0c8d,
+ 0x0c91, 0x0c91,
+ 0x0ca9, 0x0ca9,
+ 0x0cb4, 0x0cb4,
+ 0x0cba, 0x0cbb,
+ 0x0cc5, 0x0cc5,
+ 0x0cc9, 0x0cc9,
+ 0x0cce, 0x0cd4,
+ 0x0cd7, 0x0cdd,
+ 0x0cdf, 0x0cdf,
+ 0x0ce4, 0x0ce5,
+ 0x0cf0, 0x0cf0,
+ 0x0cf3, 0x0cff,
+ 0x0d04, 0x0d04,
+ 0x0d0d, 0x0d0d,
+ 0x0d11, 0x0d11,
+ 0x0d45, 0x0d45,
+ 0x0d49, 0x0d49,
+ 0x0d50, 0x0d53,
+ 0x0d64, 0x0d65,
+ 0x0d80, 0x0d81,
+ 0x0d84, 0x0d84,
+ 0x0d97, 0x0d99,
+ 0x0db2, 0x0db2,
+ 0x0dbc, 0x0dbc,
+ 0x0dbe, 0x0dbf,
+ 0x0dc7, 0x0dc9,
+ 0x0dcb, 0x0dce,
+ 0x0dd5, 0x0dd5,
+ 0x0dd7, 0x0dd7,
+ 0x0de0, 0x0de5,
+ 0x0df0, 0x0df1,
+ 0x0df5, 0x0e00,
+ 0x0e3b, 0x0e3e,
+ 0x0e5c, 0x0e80,
+ 0x0e83, 0x0e83,
+ 0x0e85, 0x0e85,
+ 0x0e8b, 0x0e8b,
+ 0x0ea4, 0x0ea4,
+ 0x0ea6, 0x0ea6,
+ 0x0ebe, 0x0ebf,
+ 0x0ec5, 0x0ec5,
+ 0x0ec7, 0x0ec7,
+ 0x0ece, 0x0ecf,
+ 0x0eda, 0x0edb,
+ 0x0ee0, 0x0eff,
+ 0x0f48, 0x0f48,
+ 0x0f6d, 0x0f70,
+ 0x0f98, 0x0f98,
+ 0x0fbd, 0x0fbd,
+ 0x0fcd, 0x0fcd,
+ 0x0fdb, 0x0fff,
+ 0x10c6, 0x10c6,
+ 0x10c8, 0x10cc,
+ 0x10ce, 0x10cf,
+ 0x1249, 0x1249,
+ 0x124e, 0x124f,
+ 0x1257, 0x1257,
+ 0x1259, 0x1259,
+ 0x125e, 0x125f,
+ 0x1289, 0x1289,
+ 0x128e, 0x128f,
+ 0x12b1, 0x12b1,
+ 0x12b6, 0x12b7,
+ 0x12bf, 0x12bf,
+ 0x12c1, 0x12c1,
+ 0x12c6, 0x12c7,
+ 0x12d7, 0x12d7,
+ 0x1311, 0x1311,
+ 0x1316, 0x1317,
+ 0x135b, 0x135c,
+ 0x137d, 0x137f,
+ 0x139a, 0x139f,
+ 0x13f6, 0x13f7,
+ 0x13fe, 0x13ff,
+ 0x169d, 0x169f,
+ 0x16f9, 0x16ff,
+ 0x170d, 0x170d,
+ 0x1715, 0x171f,
+ 0x1737, 0x173f,
+ 0x1754, 0x175f,
+ 0x176d, 0x176d,
+ 0x1771, 0x1771,
+ 0x1774, 0x177f,
+ 0x17de, 0x17df,
+ 0x17ea, 0x17ef,
+ 0x17fa, 0x17ff,
+ 0x180e, 0x180f,
+ 0x181a, 0x181f,
+ 0x1879, 0x187f,
+ 0x18ab, 0x18af,
+ 0x18f6, 0x18ff,
+ 0x191f, 0x191f,
+ 0x192c, 0x192f,
+ 0x193c, 0x193f,
+ 0x1941, 0x1943,
+ 0x196e, 0x196f,
+ 0x1975, 0x197f,
+ 0x19ac, 0x19af,
+ 0x19ca, 0x19cf,
+ 0x19db, 0x19dd,
+ 0x1a1c, 0x1a1d,
+ 0x1a5f, 0x1a5f,
+ 0x1a7d, 0x1a7e,
+ 0x1a8a, 0x1a8f,
+ 0x1a9a, 0x1a9f,
+ 0x1aae, 0x1aaf,
+ 0x1abf, 0x1aff,
+ 0x1b4c, 0x1b4f,
+ 0x1b7d, 0x1b7f,
+ 0x1bf4, 0x1bfb,
+ 0x1c38, 0x1c3a,
+ 0x1c4a, 0x1c4c,
+ 0x1c89, 0x1c8f,
+ 0x1cbb, 0x1cbc,
+ 0x1cc8, 0x1ccf,
+ 0x1cfb, 0x1cff,
+ 0x1dfa, 0x1dfa,
+ 0x1f16, 0x1f17,
+ 0x1f1e, 0x1f1f,
+ 0x1f46, 0x1f47,
+ 0x1f4e, 0x1f4f,
+ 0x1f58, 0x1f58,
+ 0x1f5a, 0x1f5a,
+ 0x1f5c, 0x1f5c,
+ 0x1f5e, 0x1f5e,
+ 0x1f7e, 0x1f7f,
+ 0x1fb5, 0x1fb5,
+ 0x1fc5, 0x1fc5,
+ 0x1fd4, 0x1fd5,
+ 0x1fdc, 0x1fdc,
+ 0x1ff0, 0x1ff1,
+ 0x1ff5, 0x1ff5,
+ 0x1fff, 0x1fff,
+ 0x200b, 0x200f,
+ 0x202a, 0x202e,
+ 0x2060, 0x206f,
+ 0x2072, 0x2073,
+ 0x208f, 0x208f,
+ 0x209d, 0x209f,
+ 0x20c0, 0x20cf,
+ 0x20f1, 0x20ff,
+ 0x218c, 0x218f,
+ 0x2427, 0x243f,
+ 0x244b, 0x245f,
+ 0x2b74, 0x2b75,
+ 0x2b96, 0x2b97,
+ 0x2c2f, 0x2c2f,
+ 0x2c5f, 0x2c5f,
+ 0x2cf4, 0x2cf8,
+ 0x2d26, 0x2d26,
+ 0x2d28, 0x2d2c,
+ 0x2d2e, 0x2d2f,
+ 0x2d68, 0x2d6e,
+ 0x2d71, 0x2d7e,
+ 0x2d97, 0x2d9f,
+ 0x2da7, 0x2da7,
+ 0x2daf, 0x2daf,
+ 0x2db7, 0x2db7,
+ 0x2dbf, 0x2dbf,
+ 0x2dc7, 0x2dc7,
+ 0x2dcf, 0x2dcf,
+ 0x2dd7, 0x2dd7,
+ 0x2ddf, 0x2ddf,
+ 0x2e50, 0x2e7f,
+ 0x2e9a, 0x2e9a,
+ 0x2ef4, 0x2eff,
+ 0x2fd6, 0x2fef,
+ 0x2ffc, 0x2fff,
+ 0x3040, 0x3040,
+ 0x3097, 0x3098,
+ 0x3100, 0x3104,
+ 0x3130, 0x3130,
+ 0x318f, 0x318f,
+ 0x31bb, 0x31bf,
+ 0x31e4, 0x31ef,
+ 0x321f, 0x321f,
+ 0x4db6, 0x4dbf,
+ 0x9ff0, 0x9fff,
+ 0xa48d, 0xa48f,
+ 0xa4c7, 0xa4cf,
+ 0xa62c, 0xa63f,
+ 0xa6f8, 0xa6ff,
+ 0xa7c0, 0xa7c1,
+ 0xa7c7, 0xa7f6,
+ 0xa82c, 0xa82f,
+ 0xa83a, 0xa83f,
+ 0xa878, 0xa87f,
+ 0xa8c6, 0xa8cd,
+ 0xa8da, 0xa8df,
+ 0xa954, 0xa95e,
+ 0xa97d, 0xa97f,
+ 0xa9ce, 0xa9ce,
+ 0xa9da, 0xa9dd,
+ 0xa9ff, 0xa9ff,
+ 0xaa37, 0xaa3f,
+ 0xaa4e, 0xaa4f,
+ 0xaa5a, 0xaa5b,
+ 0xaac3, 0xaada,
+ 0xaaf7, 0xab00,
+ 0xab07, 0xab08,
+ 0xab0f, 0xab10,
+ 0xab17, 0xab1f,
+ 0xab27, 0xab27,
+ 0xab2f, 0xab2f,
+ 0xab68, 0xab6f,
+ 0xabee, 0xabef,
+ 0xabfa, 0xabff,
+ 0xd7a4, 0xd7af,
+ 0xd7c7, 0xd7ca,
+ 0xd7fc, 0xf8ff,
+ 0xfa6e, 0xfa6f,
+ 0xfada, 0xfaff,
+ 0xfb07, 0xfb12,
+ 0xfb18, 0xfb1c,
+ 0xfb37, 0xfb37,
+ 0xfb3d, 0xfb3d,
+ 0xfb3f, 0xfb3f,
+ 0xfb42, 0xfb42,
+ 0xfb45, 0xfb45,
+ 0xfbc2, 0xfbd2,
+ 0xfd40, 0xfd4f,
+ 0xfd90, 0xfd91,
+ 0xfdc8, 0xfdef,
+ 0xfdfe, 0xfdff,
+ 0xfe1a, 0xfe1f,
+ 0xfe53, 0xfe53,
+ 0xfe67, 0xfe67,
+ 0xfe6c, 0xfe6f,
+ 0xfe75, 0xfe75,
+ 0xfefd, 0xff00,
+ 0xffbf, 0xffc1,
+ 0xffc8, 0xffc9,
+ 0xffd0, 0xffd1,
+ 0xffd8, 0xffd9,
+ 0xffdd, 0xffdf,
+ 0xffe7, 0xffe7,
+ 0xffef, 0xfffb,
+ 0xfffe, 0xffff,
+ 0x1000c, 0x1000c,
+ 0x10027, 0x10027,
+ 0x1003b, 0x1003b,
+ 0x1003e, 0x1003e,
+ 0x1004e, 0x1004f,
+ 0x1005e, 0x1007f,
+ 0x100fb, 0x100ff,
+ 0x10103, 0x10106,
+ 0x10134, 0x10136,
+ 0x1018f, 0x1018f,
+ 0x1019c, 0x1019f,
+ 0x101a1, 0x101cf,
+ 0x101fe, 0x1027f,
+ 0x1029d, 0x1029f,
+ 0x102d1, 0x102df,
+ 0x102fc, 0x102ff,
+ 0x10324, 0x1032c,
+ 0x1034b, 0x1034f,
+ 0x1037b, 0x1037f,
+ 0x1039e, 0x1039e,
+ 0x103c4, 0x103c7,
+ 0x103d6, 0x103ff,
+ 0x1049e, 0x1049f,
+ 0x104aa, 0x104af,
+ 0x104d4, 0x104d7,
+ 0x104fc, 0x104ff,
+ 0x10528, 0x1052f,
+ 0x10564, 0x1056e,
+ 0x10570, 0x105ff,
+ 0x10737, 0x1073f,
+ 0x10756, 0x1075f,
+ 0x10768, 0x107ff,
+ 0x10806, 0x10807,
+ 0x10809, 0x10809,
+ 0x10836, 0x10836,
+ 0x10839, 0x1083b,
+ 0x1083d, 0x1083e,
+ 0x10856, 0x10856,
+ 0x1089f, 0x108a6,
+ 0x108b0, 0x108df,
+ 0x108f3, 0x108f3,
+ 0x108f6, 0x108fa,
+ 0x1091c, 0x1091e,
+ 0x1093a, 0x1093e,
+ 0x10940, 0x1097f,
+ 0x109b8, 0x109bb,
+ 0x109d0, 0x109d1,
+ 0x10a04, 0x10a04,
+ 0x10a07, 0x10a0b,
+ 0x10a14, 0x10a14,
+ 0x10a18, 0x10a18,
+ 0x10a36, 0x10a37,
+ 0x10a3b, 0x10a3e,
+ 0x10a49, 0x10a4f,
+ 0x10a59, 0x10a5f,
+ 0x10aa0, 0x10abf,
+ 0x10ae7, 0x10aea,
+ 0x10af7, 0x10aff,
+ 0x10b36, 0x10b38,
+ 0x10b56, 0x10b57,
+ 0x10b73, 0x10b77,
+ 0x10b92, 0x10b98,
+ 0x10b9d, 0x10ba8,
+ 0x10bb0, 0x10bff,
+ 0x10c49, 0x10c7f,
+ 0x10cb3, 0x10cbf,
+ 0x10cf3, 0x10cf9,
+ 0x10d28, 0x10d2f,
+ 0x10d3a, 0x10e5f,
+ 0x10e7f, 0x10eff,
+ 0x10f28, 0x10f2f,
+ 0x10f5a, 0x10fdf,
+ 0x10ff7, 0x10fff,
+ 0x1104e, 0x11051,
+ 0x11070, 0x1107e,
+ 0x110bd, 0x110bd,
+ 0x110c2, 0x110cf,
+ 0x110e9, 0x110ef,
+ 0x110fa, 0x110ff,
+ 0x11135, 0x11135,
+ 0x11147, 0x1114f,
+ 0x11177, 0x1117f,
+ 0x111ce, 0x111cf,
+ 0x111e0, 0x111e0,
+ 0x111f5, 0x111ff,
+ 0x11212, 0x11212,
+ 0x1123f, 0x1127f,
+ 0x11287, 0x11287,
+ 0x11289, 0x11289,
+ 0x1128e, 0x1128e,
+ 0x1129e, 0x1129e,
+ 0x112aa, 0x112af,
+ 0x112eb, 0x112ef,
+ 0x112fa, 0x112ff,
+ 0x11304, 0x11304,
+ 0x1130d, 0x1130e,
+ 0x11311, 0x11312,
+ 0x11329, 0x11329,
+ 0x11331, 0x11331,
+ 0x11334, 0x11334,
+ 0x1133a, 0x1133a,
+ 0x11345, 0x11346,
+ 0x11349, 0x1134a,
+ 0x1134e, 0x1134f,
+ 0x11351, 0x11356,
+ 0x11358, 0x1135c,
+ 0x11364, 0x11365,
+ 0x1136d, 0x1136f,
+ 0x11375, 0x113ff,
+ 0x1145a, 0x1145a,
+ 0x1145c, 0x1145c,
+ 0x11460, 0x1147f,
+ 0x114c8, 0x114cf,
+ 0x114da, 0x1157f,
+ 0x115b6, 0x115b7,
+ 0x115de, 0x115ff,
+ 0x11645, 0x1164f,
+ 0x1165a, 0x1165f,
+ 0x1166d, 0x1167f,
+ 0x116b9, 0x116bf,
+ 0x116ca, 0x116ff,
+ 0x1171b, 0x1171c,
+ 0x1172c, 0x1172f,
+ 0x11740, 0x117ff,
+ 0x1183c, 0x1189f,
+ 0x118f3, 0x118fe,
+ 0x11900, 0x1199f,
+ 0x119a8, 0x119a9,
+ 0x119d8, 0x119d9,
+ 0x119e5, 0x119ff,
+ 0x11a48, 0x11a4f,
+ 0x11aa3, 0x11abf,
+ 0x11af9, 0x11bff,
+ 0x11c09, 0x11c09,
+ 0x11c37, 0x11c37,
+ 0x11c46, 0x11c4f,
+ 0x11c6d, 0x11c6f,
+ 0x11c90, 0x11c91,
+ 0x11ca8, 0x11ca8,
+ 0x11cb7, 0x11cff,
+ 0x11d07, 0x11d07,
+ 0x11d0a, 0x11d0a,
+ 0x11d37, 0x11d39,
+ 0x11d3b, 0x11d3b,
+ 0x11d3e, 0x11d3e,
+ 0x11d48, 0x11d4f,
+ 0x11d5a, 0x11d5f,
+ 0x11d66, 0x11d66,
+ 0x11d69, 0x11d69,
+ 0x11d8f, 0x11d8f,
+ 0x11d92, 0x11d92,
+ 0x11d99, 0x11d9f,
+ 0x11daa, 0x11edf,
+ 0x11ef9, 0x11fbf,
+ 0x11ff2, 0x11ffe,
+ 0x1239a, 0x123ff,
+ 0x1246f, 0x1246f,
+ 0x12475, 0x1247f,
+ 0x12544, 0x12fff,
+ 0x1342f, 0x143ff,
+ 0x14647, 0x167ff,
+ 0x16a39, 0x16a3f,
+ 0x16a5f, 0x16a5f,
+ 0x16a6a, 0x16a6d,
+ 0x16a70, 0x16acf,
+ 0x16aee, 0x16aef,
+ 0x16af6, 0x16aff,
+ 0x16b46, 0x16b4f,
+ 0x16b5a, 0x16b5a,
+ 0x16b62, 0x16b62,
+ 0x16b78, 0x16b7c,
+ 0x16b90, 0x16e3f,
+ 0x16e9b, 0x16eff,
+ 0x16f4b, 0x16f4e,
+ 0x16f88, 0x16f8e,
+ 0x16fa0, 0x16fdf,
+ 0x16fe4, 0x16fff,
+ 0x187f8, 0x187ff,
+ 0x18af3, 0x1afff,
+ 0x1b11f, 0x1b14f,
+ 0x1b153, 0x1b163,
+ 0x1b168, 0x1b16f,
+ 0x1b2fc, 0x1bbff,
+ 0x1bc6b, 0x1bc6f,
+ 0x1bc7d, 0x1bc7f,
+ 0x1bc89, 0x1bc8f,
+ 0x1bc9a, 0x1bc9b,
+ 0x1bca0, 0x1cfff,
+ 0x1d0f6, 0x1d0ff,
+ 0x1d127, 0x1d128,
+ 0x1d173, 0x1d17a,
+ 0x1d1e9, 0x1d1ff,
+ 0x1d246, 0x1d2df,
+ 0x1d2f4, 0x1d2ff,
+ 0x1d357, 0x1d35f,
+ 0x1d379, 0x1d3ff,
+ 0x1d455, 0x1d455,
+ 0x1d49d, 0x1d49d,
+ 0x1d4a0, 0x1d4a1,
+ 0x1d4a3, 0x1d4a4,
+ 0x1d4a7, 0x1d4a8,
+ 0x1d4ad, 0x1d4ad,
+ 0x1d4ba, 0x1d4ba,
+ 0x1d4bc, 0x1d4bc,
+ 0x1d4c4, 0x1d4c4,
+ 0x1d506, 0x1d506,
+ 0x1d50b, 0x1d50c,
+ 0x1d515, 0x1d515,
+ 0x1d51d, 0x1d51d,
+ 0x1d53a, 0x1d53a,
+ 0x1d53f, 0x1d53f,
+ 0x1d545, 0x1d545,
+ 0x1d547, 0x1d549,
+ 0x1d551, 0x1d551,
+ 0x1d6a6, 0x1d6a7,
+ 0x1d7cc, 0x1d7cd,
+ 0x1da8c, 0x1da9a,
+ 0x1daa0, 0x1daa0,
+ 0x1dab0, 0x1dfff,
+ 0x1e007, 0x1e007,
+ 0x1e019, 0x1e01a,
+ 0x1e022, 0x1e022,
+ 0x1e025, 0x1e025,
+ 0x1e02b, 0x1e0ff,
+ 0x1e12d, 0x1e12f,
+ 0x1e13e, 0x1e13f,
+ 0x1e14a, 0x1e14d,
+ 0x1e150, 0x1e2bf,
+ 0x1e2fa, 0x1e2fe,
+ 0x1e300, 0x1e7ff,
+ 0x1e8c5, 0x1e8c6,
+ 0x1e8d7, 0x1e8ff,
+ 0x1e94c, 0x1e94f,
+ 0x1e95a, 0x1e95d,
+ 0x1e960, 0x1ec70,
+ 0x1ecb5, 0x1ed00,
+ 0x1ed3e, 0x1edff,
+ 0x1ee04, 0x1ee04,
+ 0x1ee20, 0x1ee20,
+ 0x1ee23, 0x1ee23,
+ 0x1ee25, 0x1ee26,
+ 0x1ee28, 0x1ee28,
+ 0x1ee33, 0x1ee33,
+ 0x1ee38, 0x1ee38,
+ 0x1ee3a, 0x1ee3a,
+ 0x1ee3c, 0x1ee41,
+ 0x1ee43, 0x1ee46,
+ 0x1ee48, 0x1ee48,
+ 0x1ee4a, 0x1ee4a,
+ 0x1ee4c, 0x1ee4c,
+ 0x1ee50, 0x1ee50,
+ 0x1ee53, 0x1ee53,
+ 0x1ee55, 0x1ee56,
+ 0x1ee58, 0x1ee58,
+ 0x1ee5a, 0x1ee5a,
+ 0x1ee5c, 0x1ee5c,
+ 0x1ee5e, 0x1ee5e,
+ 0x1ee60, 0x1ee60,
+ 0x1ee63, 0x1ee63,
+ 0x1ee65, 0x1ee66,
+ 0x1ee6b, 0x1ee6b,
+ 0x1ee73, 0x1ee73,
+ 0x1ee78, 0x1ee78,
+ 0x1ee7d, 0x1ee7d,
+ 0x1ee7f, 0x1ee7f,
+ 0x1ee8a, 0x1ee8a,
+ 0x1ee9c, 0x1eea0,
+ 0x1eea4, 0x1eea4,
+ 0x1eeaa, 0x1eeaa,
+ 0x1eebc, 0x1eeef,
+ 0x1eef2, 0x1efff,
+ 0x1f02c, 0x1f02f,
+ 0x1f094, 0x1f09f,
+ 0x1f0af, 0x1f0b0,
+ 0x1f0c0, 0x1f0c0,
+ 0x1f0d0, 0x1f0d0,
+ 0x1f0f6, 0x1f0ff,
+ 0x1f10d, 0x1f10f,
+ 0x1f16d, 0x1f16f,
+ 0x1f1ad, 0x1f1e5,
+ 0x1f203, 0x1f20f,
+ 0x1f23c, 0x1f23f,
+ 0x1f249, 0x1f24f,
+ 0x1f252, 0x1f25f,
+ 0x1f266, 0x1f2ff,
+ 0x1f6d6, 0x1f6df,
+ 0x1f6ed, 0x1f6ef,
+ 0x1f6fb, 0x1f6ff,
+ 0x1f774, 0x1f77f,
+ 0x1f7d9, 0x1f7df,
+ 0x1f7ec, 0x1f7ff,
+ 0x1f80c, 0x1f80f,
+ 0x1f848, 0x1f84f,
+ 0x1f85a, 0x1f85f,
+ 0x1f888, 0x1f88f,
+ 0x1f8ae, 0x1f8ff,
+ 0x1f90c, 0x1f90c,
+ 0x1f972, 0x1f972,
+ 0x1f977, 0x1f979,
+ 0x1f9a3, 0x1f9a4,
+ 0x1f9ab, 0x1f9ad,
+ 0x1f9cb, 0x1f9cc,
+ 0x1fa54, 0x1fa5f,
+ 0x1fa6e, 0x1fa6f,
+ 0x1fa74, 0x1fa77,
+ 0x1fa7b, 0x1fa7f,
+ 0x1fa83, 0x1fa8f,
+ 0x1fa96, 0x1ffff,
+ 0x2a6d7, 0x2a6ff,
+ 0x2b735, 0x2b73f,
+ 0x2b81e, 0x2b81f,
+ 0x2cea2, 0x2ceaf,
+ 0x2ebe1, 0x2f7ff,
+ 0x2fa1e, 0xe00ff,
+ 0xe01f0, 0x10ffff,
+}; /* CR_C */
+
+/* 'Cc': General Category */
+#define CR_Cc CR_Cntrl
+
+/* 'Cf': General Category */
+static const OnigCodePoint CR_Cf[] = {
+ 20,
+ 0x00ad, 0x00ad,
+ 0x0600, 0x0605,
+ 0x061c, 0x061c,
+ 0x06dd, 0x06dd,
+ 0x070f, 0x070f,
+ 0x08e2, 0x08e2,
+ 0x180e, 0x180e,
+ 0x200b, 0x200f,
+ 0x202a, 0x202e,
+ 0x2060, 0x2064,
+ 0x2066, 0x206f,
+ 0xfeff, 0xfeff,
+ 0xfff9, 0xfffb,
+ 0x110bd, 0x110bd,
+ 0x110cd, 0x110cd,
+ 0x13430, 0x13438,
+ 0x1bca0, 0x1bca3,
+ 0x1d173, 0x1d17a,
+ 0xe0001, 0xe0001,
+ 0xe0020, 0xe007f,
+}; /* CR_Cf */
+
+/* 'Cn': General Category */
+static const OnigCodePoint CR_Cn[] = {
+ 666,
+ 0x0378, 0x0379,
+ 0x0380, 0x0383,
+ 0x038b, 0x038b,
+ 0x038d, 0x038d,
+ 0x03a2, 0x03a2,
+ 0x0530, 0x0530,
+ 0x0557, 0x0558,
+ 0x058b, 0x058c,
+ 0x0590, 0x0590,
+ 0x05c8, 0x05cf,
+ 0x05eb, 0x05ee,
+ 0x05f5, 0x05ff,
+ 0x061d, 0x061d,
+ 0x070e, 0x070e,
+ 0x074b, 0x074c,
+ 0x07b2, 0x07bf,
+ 0x07fb, 0x07fc,
+ 0x082e, 0x082f,
+ 0x083f, 0x083f,
+ 0x085c, 0x085d,
+ 0x085f, 0x085f,
+ 0x086b, 0x089f,
+ 0x08b5, 0x08b5,
+ 0x08be, 0x08d2,
+ 0x0984, 0x0984,
+ 0x098d, 0x098e,
+ 0x0991, 0x0992,
+ 0x09a9, 0x09a9,
+ 0x09b1, 0x09b1,
+ 0x09b3, 0x09b5,
+ 0x09ba, 0x09bb,
+ 0x09c5, 0x09c6,
+ 0x09c9, 0x09ca,
+ 0x09cf, 0x09d6,
+ 0x09d8, 0x09db,
+ 0x09de, 0x09de,
+ 0x09e4, 0x09e5,
+ 0x09ff, 0x0a00,
+ 0x0a04, 0x0a04,
+ 0x0a0b, 0x0a0e,
+ 0x0a11, 0x0a12,
+ 0x0a29, 0x0a29,
+ 0x0a31, 0x0a31,
+ 0x0a34, 0x0a34,
+ 0x0a37, 0x0a37,
+ 0x0a3a, 0x0a3b,
+ 0x0a3d, 0x0a3d,
+ 0x0a43, 0x0a46,
+ 0x0a49, 0x0a4a,
+ 0x0a4e, 0x0a50,
+ 0x0a52, 0x0a58,
+ 0x0a5d, 0x0a5d,
+ 0x0a5f, 0x0a65,
+ 0x0a77, 0x0a80,
+ 0x0a84, 0x0a84,
+ 0x0a8e, 0x0a8e,
+ 0x0a92, 0x0a92,
+ 0x0aa9, 0x0aa9,
+ 0x0ab1, 0x0ab1,
+ 0x0ab4, 0x0ab4,
+ 0x0aba, 0x0abb,
+ 0x0ac6, 0x0ac6,
+ 0x0aca, 0x0aca,
+ 0x0ace, 0x0acf,
+ 0x0ad1, 0x0adf,
+ 0x0ae4, 0x0ae5,
+ 0x0af2, 0x0af8,
+ 0x0b00, 0x0b00,
+ 0x0b04, 0x0b04,
+ 0x0b0d, 0x0b0e,
+ 0x0b11, 0x0b12,
+ 0x0b29, 0x0b29,
+ 0x0b31, 0x0b31,
+ 0x0b34, 0x0b34,
+ 0x0b3a, 0x0b3b,
+ 0x0b45, 0x0b46,
+ 0x0b49, 0x0b4a,
+ 0x0b4e, 0x0b55,
+ 0x0b58, 0x0b5b,
+ 0x0b5e, 0x0b5e,
+ 0x0b64, 0x0b65,
+ 0x0b78, 0x0b81,
+ 0x0b84, 0x0b84,
+ 0x0b8b, 0x0b8d,
+ 0x0b91, 0x0b91,
+ 0x0b96, 0x0b98,
+ 0x0b9b, 0x0b9b,
+ 0x0b9d, 0x0b9d,
+ 0x0ba0, 0x0ba2,
+ 0x0ba5, 0x0ba7,
+ 0x0bab, 0x0bad,
+ 0x0bba, 0x0bbd,
+ 0x0bc3, 0x0bc5,
+ 0x0bc9, 0x0bc9,
+ 0x0bce, 0x0bcf,
+ 0x0bd1, 0x0bd6,
+ 0x0bd8, 0x0be5,
+ 0x0bfb, 0x0bff,
+ 0x0c0d, 0x0c0d,
+ 0x0c11, 0x0c11,
+ 0x0c29, 0x0c29,
+ 0x0c3a, 0x0c3c,
+ 0x0c45, 0x0c45,
+ 0x0c49, 0x0c49,
+ 0x0c4e, 0x0c54,
+ 0x0c57, 0x0c57,
+ 0x0c5b, 0x0c5f,
+ 0x0c64, 0x0c65,
+ 0x0c70, 0x0c76,
+ 0x0c8d, 0x0c8d,
+ 0x0c91, 0x0c91,
+ 0x0ca9, 0x0ca9,
+ 0x0cb4, 0x0cb4,
+ 0x0cba, 0x0cbb,
+ 0x0cc5, 0x0cc5,
+ 0x0cc9, 0x0cc9,
+ 0x0cce, 0x0cd4,
+ 0x0cd7, 0x0cdd,
+ 0x0cdf, 0x0cdf,
+ 0x0ce4, 0x0ce5,
+ 0x0cf0, 0x0cf0,
+ 0x0cf3, 0x0cff,
+ 0x0d04, 0x0d04,
+ 0x0d0d, 0x0d0d,
+ 0x0d11, 0x0d11,
+ 0x0d45, 0x0d45,
+ 0x0d49, 0x0d49,
+ 0x0d50, 0x0d53,
+ 0x0d64, 0x0d65,
+ 0x0d80, 0x0d81,
+ 0x0d84, 0x0d84,
+ 0x0d97, 0x0d99,
+ 0x0db2, 0x0db2,
+ 0x0dbc, 0x0dbc,
+ 0x0dbe, 0x0dbf,
+ 0x0dc7, 0x0dc9,
+ 0x0dcb, 0x0dce,
+ 0x0dd5, 0x0dd5,
+ 0x0dd7, 0x0dd7,
+ 0x0de0, 0x0de5,
+ 0x0df0, 0x0df1,
+ 0x0df5, 0x0e00,
+ 0x0e3b, 0x0e3e,
+ 0x0e5c, 0x0e80,
+ 0x0e83, 0x0e83,
+ 0x0e85, 0x0e85,
+ 0x0e8b, 0x0e8b,
+ 0x0ea4, 0x0ea4,
+ 0x0ea6, 0x0ea6,
+ 0x0ebe, 0x0ebf,
+ 0x0ec5, 0x0ec5,
+ 0x0ec7, 0x0ec7,
+ 0x0ece, 0x0ecf,
+ 0x0eda, 0x0edb,
+ 0x0ee0, 0x0eff,
+ 0x0f48, 0x0f48,
+ 0x0f6d, 0x0f70,
+ 0x0f98, 0x0f98,
+ 0x0fbd, 0x0fbd,
+ 0x0fcd, 0x0fcd,
+ 0x0fdb, 0x0fff,
+ 0x10c6, 0x10c6,
+ 0x10c8, 0x10cc,
+ 0x10ce, 0x10cf,
+ 0x1249, 0x1249,
+ 0x124e, 0x124f,
+ 0x1257, 0x1257,
+ 0x1259, 0x1259,
+ 0x125e, 0x125f,
+ 0x1289, 0x1289,
+ 0x128e, 0x128f,
+ 0x12b1, 0x12b1,
+ 0x12b6, 0x12b7,
+ 0x12bf, 0x12bf,
+ 0x12c1, 0x12c1,
+ 0x12c6, 0x12c7,
+ 0x12d7, 0x12d7,
+ 0x1311, 0x1311,
+ 0x1316, 0x1317,
+ 0x135b, 0x135c,
+ 0x137d, 0x137f,
+ 0x139a, 0x139f,
+ 0x13f6, 0x13f7,
+ 0x13fe, 0x13ff,
+ 0x169d, 0x169f,
+ 0x16f9, 0x16ff,
+ 0x170d, 0x170d,
+ 0x1715, 0x171f,
+ 0x1737, 0x173f,
+ 0x1754, 0x175f,
+ 0x176d, 0x176d,
+ 0x1771, 0x1771,
+ 0x1774, 0x177f,
+ 0x17de, 0x17df,
+ 0x17ea, 0x17ef,
+ 0x17fa, 0x17ff,
+ 0x180f, 0x180f,
+ 0x181a, 0x181f,
+ 0x1879, 0x187f,
+ 0x18ab, 0x18af,
+ 0x18f6, 0x18ff,
+ 0x191f, 0x191f,
+ 0x192c, 0x192f,
+ 0x193c, 0x193f,
+ 0x1941, 0x1943,
+ 0x196e, 0x196f,
+ 0x1975, 0x197f,
+ 0x19ac, 0x19af,
+ 0x19ca, 0x19cf,
+ 0x19db, 0x19dd,
+ 0x1a1c, 0x1a1d,
+ 0x1a5f, 0x1a5f,
+ 0x1a7d, 0x1a7e,
+ 0x1a8a, 0x1a8f,
+ 0x1a9a, 0x1a9f,
+ 0x1aae, 0x1aaf,
+ 0x1abf, 0x1aff,
+ 0x1b4c, 0x1b4f,
+ 0x1b7d, 0x1b7f,
+ 0x1bf4, 0x1bfb,
+ 0x1c38, 0x1c3a,
+ 0x1c4a, 0x1c4c,
+ 0x1c89, 0x1c8f,
+ 0x1cbb, 0x1cbc,
+ 0x1cc8, 0x1ccf,
+ 0x1cfb, 0x1cff,
+ 0x1dfa, 0x1dfa,
+ 0x1f16, 0x1f17,
+ 0x1f1e, 0x1f1f,
+ 0x1f46, 0x1f47,
+ 0x1f4e, 0x1f4f,
+ 0x1f58, 0x1f58,
+ 0x1f5a, 0x1f5a,
+ 0x1f5c, 0x1f5c,
+ 0x1f5e, 0x1f5e,
+ 0x1f7e, 0x1f7f,
+ 0x1fb5, 0x1fb5,
+ 0x1fc5, 0x1fc5,
+ 0x1fd4, 0x1fd5,
+ 0x1fdc, 0x1fdc,
+ 0x1ff0, 0x1ff1,
+ 0x1ff5, 0x1ff5,
+ 0x1fff, 0x1fff,
+ 0x2065, 0x2065,
+ 0x2072, 0x2073,
+ 0x208f, 0x208f,
+ 0x209d, 0x209f,
+ 0x20c0, 0x20cf,
+ 0x20f1, 0x20ff,
+ 0x218c, 0x218f,
+ 0x2427, 0x243f,
+ 0x244b, 0x245f,
+ 0x2b74, 0x2b75,
+ 0x2b96, 0x2b97,
+ 0x2c2f, 0x2c2f,
+ 0x2c5f, 0x2c5f,
+ 0x2cf4, 0x2cf8,
+ 0x2d26, 0x2d26,
+ 0x2d28, 0x2d2c,
+ 0x2d2e, 0x2d2f,
+ 0x2d68, 0x2d6e,
+ 0x2d71, 0x2d7e,
+ 0x2d97, 0x2d9f,
+ 0x2da7, 0x2da7,
+ 0x2daf, 0x2daf,
+ 0x2db7, 0x2db7,
+ 0x2dbf, 0x2dbf,
+ 0x2dc7, 0x2dc7,
+ 0x2dcf, 0x2dcf,
+ 0x2dd7, 0x2dd7,
+ 0x2ddf, 0x2ddf,
+ 0x2e50, 0x2e7f,
+ 0x2e9a, 0x2e9a,
+ 0x2ef4, 0x2eff,
+ 0x2fd6, 0x2fef,
+ 0x2ffc, 0x2fff,
+ 0x3040, 0x3040,
+ 0x3097, 0x3098,
+ 0x3100, 0x3104,
+ 0x3130, 0x3130,
+ 0x318f, 0x318f,
+ 0x31bb, 0x31bf,
+ 0x31e4, 0x31ef,
+ 0x321f, 0x321f,
+ 0x4db6, 0x4dbf,
+ 0x9ff0, 0x9fff,
+ 0xa48d, 0xa48f,
+ 0xa4c7, 0xa4cf,
+ 0xa62c, 0xa63f,
+ 0xa6f8, 0xa6ff,
+ 0xa7c0, 0xa7c1,
+ 0xa7c7, 0xa7f6,
+ 0xa82c, 0xa82f,
+ 0xa83a, 0xa83f,
+ 0xa878, 0xa87f,
+ 0xa8c6, 0xa8cd,
+ 0xa8da, 0xa8df,
+ 0xa954, 0xa95e,
+ 0xa97d, 0xa97f,
+ 0xa9ce, 0xa9ce,
+ 0xa9da, 0xa9dd,
+ 0xa9ff, 0xa9ff,
+ 0xaa37, 0xaa3f,
+ 0xaa4e, 0xaa4f,
+ 0xaa5a, 0xaa5b,
+ 0xaac3, 0xaada,
+ 0xaaf7, 0xab00,
+ 0xab07, 0xab08,
+ 0xab0f, 0xab10,
+ 0xab17, 0xab1f,
+ 0xab27, 0xab27,
+ 0xab2f, 0xab2f,
+ 0xab68, 0xab6f,
+ 0xabee, 0xabef,
+ 0xabfa, 0xabff,
+ 0xd7a4, 0xd7af,
+ 0xd7c7, 0xd7ca,
+ 0xd7fc, 0xd7ff,
+ 0xfa6e, 0xfa6f,
+ 0xfada, 0xfaff,
+ 0xfb07, 0xfb12,
+ 0xfb18, 0xfb1c,
+ 0xfb37, 0xfb37,
+ 0xfb3d, 0xfb3d,
+ 0xfb3f, 0xfb3f,
+ 0xfb42, 0xfb42,
+ 0xfb45, 0xfb45,
+ 0xfbc2, 0xfbd2,
+ 0xfd40, 0xfd4f,
+ 0xfd90, 0xfd91,
+ 0xfdc8, 0xfdef,
+ 0xfdfe, 0xfdff,
+ 0xfe1a, 0xfe1f,
+ 0xfe53, 0xfe53,
+ 0xfe67, 0xfe67,
+ 0xfe6c, 0xfe6f,
+ 0xfe75, 0xfe75,
+ 0xfefd, 0xfefe,
+ 0xff00, 0xff00,
+ 0xffbf, 0xffc1,
+ 0xffc8, 0xffc9,
+ 0xffd0, 0xffd1,
+ 0xffd8, 0xffd9,
+ 0xffdd, 0xffdf,
+ 0xffe7, 0xffe7,
+ 0xffef, 0xfff8,
+ 0xfffe, 0xffff,
+ 0x1000c, 0x1000c,
+ 0x10027, 0x10027,
+ 0x1003b, 0x1003b,
+ 0x1003e, 0x1003e,
+ 0x1004e, 0x1004f,
+ 0x1005e, 0x1007f,
+ 0x100fb, 0x100ff,
+ 0x10103, 0x10106,
+ 0x10134, 0x10136,
+ 0x1018f, 0x1018f,
+ 0x1019c, 0x1019f,
+ 0x101a1, 0x101cf,
+ 0x101fe, 0x1027f,
+ 0x1029d, 0x1029f,
+ 0x102d1, 0x102df,
+ 0x102fc, 0x102ff,
+ 0x10324, 0x1032c,
+ 0x1034b, 0x1034f,
+ 0x1037b, 0x1037f,
+ 0x1039e, 0x1039e,
+ 0x103c4, 0x103c7,
+ 0x103d6, 0x103ff,
+ 0x1049e, 0x1049f,
+ 0x104aa, 0x104af,
+ 0x104d4, 0x104d7,
+ 0x104fc, 0x104ff,
+ 0x10528, 0x1052f,
+ 0x10564, 0x1056e,
+ 0x10570, 0x105ff,
+ 0x10737, 0x1073f,
+ 0x10756, 0x1075f,
+ 0x10768, 0x107ff,
+ 0x10806, 0x10807,
+ 0x10809, 0x10809,
+ 0x10836, 0x10836,
+ 0x10839, 0x1083b,
+ 0x1083d, 0x1083e,
+ 0x10856, 0x10856,
+ 0x1089f, 0x108a6,
+ 0x108b0, 0x108df,
+ 0x108f3, 0x108f3,
+ 0x108f6, 0x108fa,
+ 0x1091c, 0x1091e,
+ 0x1093a, 0x1093e,
+ 0x10940, 0x1097f,
+ 0x109b8, 0x109bb,
+ 0x109d0, 0x109d1,
+ 0x10a04, 0x10a04,
+ 0x10a07, 0x10a0b,
+ 0x10a14, 0x10a14,
+ 0x10a18, 0x10a18,
+ 0x10a36, 0x10a37,
+ 0x10a3b, 0x10a3e,
+ 0x10a49, 0x10a4f,
+ 0x10a59, 0x10a5f,
+ 0x10aa0, 0x10abf,
+ 0x10ae7, 0x10aea,
+ 0x10af7, 0x10aff,
+ 0x10b36, 0x10b38,
+ 0x10b56, 0x10b57,
+ 0x10b73, 0x10b77,
+ 0x10b92, 0x10b98,
+ 0x10b9d, 0x10ba8,
+ 0x10bb0, 0x10bff,
+ 0x10c49, 0x10c7f,
+ 0x10cb3, 0x10cbf,
+ 0x10cf3, 0x10cf9,
+ 0x10d28, 0x10d2f,
+ 0x10d3a, 0x10e5f,
+ 0x10e7f, 0x10eff,
+ 0x10f28, 0x10f2f,
+ 0x10f5a, 0x10fdf,
+ 0x10ff7, 0x10fff,
+ 0x1104e, 0x11051,
+ 0x11070, 0x1107e,
+ 0x110c2, 0x110cc,
+ 0x110ce, 0x110cf,
+ 0x110e9, 0x110ef,
+ 0x110fa, 0x110ff,
+ 0x11135, 0x11135,
+ 0x11147, 0x1114f,
+ 0x11177, 0x1117f,
+ 0x111ce, 0x111cf,
+ 0x111e0, 0x111e0,
+ 0x111f5, 0x111ff,
+ 0x11212, 0x11212,
+ 0x1123f, 0x1127f,
+ 0x11287, 0x11287,
+ 0x11289, 0x11289,
+ 0x1128e, 0x1128e,
+ 0x1129e, 0x1129e,
+ 0x112aa, 0x112af,
+ 0x112eb, 0x112ef,
+ 0x112fa, 0x112ff,
+ 0x11304, 0x11304,
+ 0x1130d, 0x1130e,
+ 0x11311, 0x11312,
+ 0x11329, 0x11329,
+ 0x11331, 0x11331,
+ 0x11334, 0x11334,
+ 0x1133a, 0x1133a,
+ 0x11345, 0x11346,
+ 0x11349, 0x1134a,
+ 0x1134e, 0x1134f,
+ 0x11351, 0x11356,
+ 0x11358, 0x1135c,
+ 0x11364, 0x11365,
+ 0x1136d, 0x1136f,
+ 0x11375, 0x113ff,
+ 0x1145a, 0x1145a,
+ 0x1145c, 0x1145c,
+ 0x11460, 0x1147f,
+ 0x114c8, 0x114cf,
+ 0x114da, 0x1157f,
+ 0x115b6, 0x115b7,
+ 0x115de, 0x115ff,
+ 0x11645, 0x1164f,
+ 0x1165a, 0x1165f,
+ 0x1166d, 0x1167f,
+ 0x116b9, 0x116bf,
+ 0x116ca, 0x116ff,
+ 0x1171b, 0x1171c,
+ 0x1172c, 0x1172f,
+ 0x11740, 0x117ff,
+ 0x1183c, 0x1189f,
+ 0x118f3, 0x118fe,
+ 0x11900, 0x1199f,
+ 0x119a8, 0x119a9,
+ 0x119d8, 0x119d9,
+ 0x119e5, 0x119ff,
+ 0x11a48, 0x11a4f,
+ 0x11aa3, 0x11abf,
+ 0x11af9, 0x11bff,
+ 0x11c09, 0x11c09,
+ 0x11c37, 0x11c37,
+ 0x11c46, 0x11c4f,
+ 0x11c6d, 0x11c6f,
+ 0x11c90, 0x11c91,
+ 0x11ca8, 0x11ca8,
+ 0x11cb7, 0x11cff,
+ 0x11d07, 0x11d07,
+ 0x11d0a, 0x11d0a,
+ 0x11d37, 0x11d39,
+ 0x11d3b, 0x11d3b,
+ 0x11d3e, 0x11d3e,
+ 0x11d48, 0x11d4f,
+ 0x11d5a, 0x11d5f,
+ 0x11d66, 0x11d66,
+ 0x11d69, 0x11d69,
+ 0x11d8f, 0x11d8f,
+ 0x11d92, 0x11d92,
+ 0x11d99, 0x11d9f,
+ 0x11daa, 0x11edf,
+ 0x11ef9, 0x11fbf,
+ 0x11ff2, 0x11ffe,
+ 0x1239a, 0x123ff,
+ 0x1246f, 0x1246f,
+ 0x12475, 0x1247f,
+ 0x12544, 0x12fff,
+ 0x1342f, 0x1342f,
+ 0x13439, 0x143ff,
+ 0x14647, 0x167ff,
+ 0x16a39, 0x16a3f,
+ 0x16a5f, 0x16a5f,
+ 0x16a6a, 0x16a6d,
+ 0x16a70, 0x16acf,
+ 0x16aee, 0x16aef,
+ 0x16af6, 0x16aff,
+ 0x16b46, 0x16b4f,
+ 0x16b5a, 0x16b5a,
+ 0x16b62, 0x16b62,
+ 0x16b78, 0x16b7c,
+ 0x16b90, 0x16e3f,
+ 0x16e9b, 0x16eff,
+ 0x16f4b, 0x16f4e,
+ 0x16f88, 0x16f8e,
+ 0x16fa0, 0x16fdf,
+ 0x16fe4, 0x16fff,
+ 0x187f8, 0x187ff,
+ 0x18af3, 0x1afff,
+ 0x1b11f, 0x1b14f,
+ 0x1b153, 0x1b163,
+ 0x1b168, 0x1b16f,
+ 0x1b2fc, 0x1bbff,
+ 0x1bc6b, 0x1bc6f,
+ 0x1bc7d, 0x1bc7f,
+ 0x1bc89, 0x1bc8f,
+ 0x1bc9a, 0x1bc9b,
+ 0x1bca4, 0x1cfff,
+ 0x1d0f6, 0x1d0ff,
+ 0x1d127, 0x1d128,
+ 0x1d1e9, 0x1d1ff,
+ 0x1d246, 0x1d2df,
+ 0x1d2f4, 0x1d2ff,
+ 0x1d357, 0x1d35f,
+ 0x1d379, 0x1d3ff,
+ 0x1d455, 0x1d455,
+ 0x1d49d, 0x1d49d,
+ 0x1d4a0, 0x1d4a1,
+ 0x1d4a3, 0x1d4a4,
+ 0x1d4a7, 0x1d4a8,
+ 0x1d4ad, 0x1d4ad,
+ 0x1d4ba, 0x1d4ba,
+ 0x1d4bc, 0x1d4bc,
+ 0x1d4c4, 0x1d4c4,
+ 0x1d506, 0x1d506,
+ 0x1d50b, 0x1d50c,
+ 0x1d515, 0x1d515,
+ 0x1d51d, 0x1d51d,
+ 0x1d53a, 0x1d53a,
+ 0x1d53f, 0x1d53f,
+ 0x1d545, 0x1d545,
+ 0x1d547, 0x1d549,
+ 0x1d551, 0x1d551,
+ 0x1d6a6, 0x1d6a7,
+ 0x1d7cc, 0x1d7cd,
+ 0x1da8c, 0x1da9a,
+ 0x1daa0, 0x1daa0,
+ 0x1dab0, 0x1dfff,
+ 0x1e007, 0x1e007,
+ 0x1e019, 0x1e01a,
+ 0x1e022, 0x1e022,
+ 0x1e025, 0x1e025,
+ 0x1e02b, 0x1e0ff,
+ 0x1e12d, 0x1e12f,
+ 0x1e13e, 0x1e13f,
+ 0x1e14a, 0x1e14d,
+ 0x1e150, 0x1e2bf,
+ 0x1e2fa, 0x1e2fe,
+ 0x1e300, 0x1e7ff,
+ 0x1e8c5, 0x1e8c6,
+ 0x1e8d7, 0x1e8ff,
+ 0x1e94c, 0x1e94f,
+ 0x1e95a, 0x1e95d,
+ 0x1e960, 0x1ec70,
+ 0x1ecb5, 0x1ed00,
+ 0x1ed3e, 0x1edff,
+ 0x1ee04, 0x1ee04,
+ 0x1ee20, 0x1ee20,
+ 0x1ee23, 0x1ee23,
+ 0x1ee25, 0x1ee26,
+ 0x1ee28, 0x1ee28,
+ 0x1ee33, 0x1ee33,
+ 0x1ee38, 0x1ee38,
+ 0x1ee3a, 0x1ee3a,
+ 0x1ee3c, 0x1ee41,
+ 0x1ee43, 0x1ee46,
+ 0x1ee48, 0x1ee48,
+ 0x1ee4a, 0x1ee4a,
+ 0x1ee4c, 0x1ee4c,
+ 0x1ee50, 0x1ee50,
+ 0x1ee53, 0x1ee53,
+ 0x1ee55, 0x1ee56,
+ 0x1ee58, 0x1ee58,
+ 0x1ee5a, 0x1ee5a,
+ 0x1ee5c, 0x1ee5c,
+ 0x1ee5e, 0x1ee5e,
+ 0x1ee60, 0x1ee60,
+ 0x1ee63, 0x1ee63,
+ 0x1ee65, 0x1ee66,
+ 0x1ee6b, 0x1ee6b,
+ 0x1ee73, 0x1ee73,
+ 0x1ee78, 0x1ee78,
+ 0x1ee7d, 0x1ee7d,
+ 0x1ee7f, 0x1ee7f,
+ 0x1ee8a, 0x1ee8a,
+ 0x1ee9c, 0x1eea0,
+ 0x1eea4, 0x1eea4,
+ 0x1eeaa, 0x1eeaa,
+ 0x1eebc, 0x1eeef,
+ 0x1eef2, 0x1efff,
+ 0x1f02c, 0x1f02f,
+ 0x1f094, 0x1f09f,
+ 0x1f0af, 0x1f0b0,
+ 0x1f0c0, 0x1f0c0,
+ 0x1f0d0, 0x1f0d0,
+ 0x1f0f6, 0x1f0ff,
+ 0x1f10d, 0x1f10f,
+ 0x1f16d, 0x1f16f,
+ 0x1f1ad, 0x1f1e5,
+ 0x1f203, 0x1f20f,
+ 0x1f23c, 0x1f23f,
+ 0x1f249, 0x1f24f,
+ 0x1f252, 0x1f25f,
+ 0x1f266, 0x1f2ff,
+ 0x1f6d6, 0x1f6df,
+ 0x1f6ed, 0x1f6ef,
+ 0x1f6fb, 0x1f6ff,
+ 0x1f774, 0x1f77f,
+ 0x1f7d9, 0x1f7df,
+ 0x1f7ec, 0x1f7ff,
+ 0x1f80c, 0x1f80f,
+ 0x1f848, 0x1f84f,
+ 0x1f85a, 0x1f85f,
+ 0x1f888, 0x1f88f,
+ 0x1f8ae, 0x1f8ff,
+ 0x1f90c, 0x1f90c,
+ 0x1f972, 0x1f972,
+ 0x1f977, 0x1f979,
+ 0x1f9a3, 0x1f9a4,
+ 0x1f9ab, 0x1f9ad,
+ 0x1f9cb, 0x1f9cc,
+ 0x1fa54, 0x1fa5f,
+ 0x1fa6e, 0x1fa6f,
+ 0x1fa74, 0x1fa77,
+ 0x1fa7b, 0x1fa7f,
+ 0x1fa83, 0x1fa8f,
+ 0x1fa96, 0x1ffff,
+ 0x2a6d7, 0x2a6ff,
+ 0x2b735, 0x2b73f,
+ 0x2b81e, 0x2b81f,
+ 0x2cea2, 0x2ceaf,
+ 0x2ebe1, 0x2f7ff,
+ 0x2fa1e, 0xe0000,
+ 0xe0002, 0xe001f,
+ 0xe0080, 0xe00ff,
+ 0xe01f0, 0xeffff,
+ 0xffffe, 0xfffff,
+ 0x10fffe, 0x10ffff,
+}; /* CR_Cn */
+
+/* 'Co': General Category */
+static const OnigCodePoint CR_Co[] = {
+ 3,
+ 0xe000, 0xf8ff,
+ 0xf0000, 0xffffd,
+ 0x100000, 0x10fffd,
+}; /* CR_Co */
+
+/* 'Cs': General Category */
+static const OnigCodePoint CR_Cs[] = {
+ 1,
+ 0xd800, 0xdfff,
+}; /* CR_Cs */
+
+/* 'L': Major Category */
+static const OnigCodePoint CR_L[] = {
+ 609,
+ 0x0041, 0x005a,
+ 0x0061, 0x007a,
+ 0x00aa, 0x00aa,
+ 0x00b5, 0x00b5,
+ 0x00ba, 0x00ba,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00f6,
+ 0x00f8, 0x02c1,
+ 0x02c6, 0x02d1,
+ 0x02e0, 0x02e4,
+ 0x02ec, 0x02ec,
+ 0x02ee, 0x02ee,
+ 0x0370, 0x0374,
+ 0x0376, 0x0377,
+ 0x037a, 0x037d,
+ 0x037f, 0x037f,
+ 0x0386, 0x0386,
+ 0x0388, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03f5,
+ 0x03f7, 0x0481,
+ 0x048a, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x0559,
+ 0x0560, 0x0588,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f2,
+ 0x0620, 0x064a,
+ 0x066e, 0x066f,
+ 0x0671, 0x06d3,
+ 0x06d5, 0x06d5,
+ 0x06e5, 0x06e6,
+ 0x06ee, 0x06ef,
+ 0x06fa, 0x06fc,
+ 0x06ff, 0x06ff,
+ 0x0710, 0x0710,
+ 0x0712, 0x072f,
+ 0x074d, 0x07a5,
+ 0x07b1, 0x07b1,
+ 0x07ca, 0x07ea,
+ 0x07f4, 0x07f5,
+ 0x07fa, 0x07fa,
+ 0x0800, 0x0815,
+ 0x081a, 0x081a,
+ 0x0824, 0x0824,
+ 0x0828, 0x0828,
+ 0x0840, 0x0858,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x0904, 0x0939,
+ 0x093d, 0x093d,
+ 0x0950, 0x0950,
+ 0x0958, 0x0961,
+ 0x0971, 0x0980,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bd, 0x09bd,
+ 0x09ce, 0x09ce,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e1,
+ 0x09f0, 0x09f1,
+ 0x09fc, 0x09fc,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a72, 0x0a74,
+ 0x0a85, 0x0a8d,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0abd, 0x0abd,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae1,
+ 0x0af9, 0x0af9,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3d, 0x0b3d,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b61,
+ 0x0b71, 0x0b71,
+ 0x0b83, 0x0b83,
+ 0x0b85, 0x0b8a,
+ 0x0b8e, 0x0b90,
+ 0x0b92, 0x0b95,
+ 0x0b99, 0x0b9a,
+ 0x0b9c, 0x0b9c,
+ 0x0b9e, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb9,
+ 0x0bd0, 0x0bd0,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3d, 0x0c3d,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c61,
+ 0x0c80, 0x0c80,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbd, 0x0cbd,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce1,
+ 0x0cf1, 0x0cf2,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d3a,
+ 0x0d3d, 0x0d3d,
+ 0x0d4e, 0x0d4e,
+ 0x0d54, 0x0d56,
+ 0x0d5f, 0x0d61,
+ 0x0d7a, 0x0d7f,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0e01, 0x0e30,
+ 0x0e32, 0x0e33,
+ 0x0e40, 0x0e46,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e86, 0x0e8a,
+ 0x0e8c, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0eb0,
+ 0x0eb2, 0x0eb3,
+ 0x0ebd, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0edc, 0x0edf,
+ 0x0f00, 0x0f00,
+ 0x0f40, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f88, 0x0f8c,
+ 0x1000, 0x102a,
+ 0x103f, 0x103f,
+ 0x1050, 0x1055,
+ 0x105a, 0x105d,
+ 0x1061, 0x1061,
+ 0x1065, 0x1066,
+ 0x106e, 0x1070,
+ 0x1075, 0x1081,
+ 0x108e, 0x108e,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x10fa,
+ 0x10fc, 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,
+ 0x1380, 0x138f,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0x1401, 0x166c,
+ 0x166f, 0x167f,
+ 0x1681, 0x169a,
+ 0x16a0, 0x16ea,
+ 0x16f1, 0x16f8,
+ 0x1700, 0x170c,
+ 0x170e, 0x1711,
+ 0x1720, 0x1731,
+ 0x1740, 0x1751,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1780, 0x17b3,
+ 0x17d7, 0x17d7,
+ 0x17dc, 0x17dc,
+ 0x1820, 0x1878,
+ 0x1880, 0x1884,
+ 0x1887, 0x18a8,
+ 0x18aa, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191e,
+ 0x1950, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19ab,
+ 0x19b0, 0x19c9,
+ 0x1a00, 0x1a16,
+ 0x1a20, 0x1a54,
+ 0x1aa7, 0x1aa7,
+ 0x1b05, 0x1b33,
+ 0x1b45, 0x1b4b,
+ 0x1b83, 0x1ba0,
+ 0x1bae, 0x1baf,
+ 0x1bba, 0x1be5,
+ 0x1c00, 0x1c23,
+ 0x1c4d, 0x1c4f,
+ 0x1c5a, 0x1c7d,
+ 0x1c80, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x1ce9, 0x1cec,
+ 0x1cee, 0x1cf3,
+ 0x1cf5, 0x1cf6,
+ 0x1cfa, 0x1cfa,
+ 0x1d00, 0x1dbf,
+ 0x1e00, 0x1f15,
+ 0x1f18, 0x1f1d,
+ 0x1f20, 0x1f45,
+ 0x1f48, 0x1f4d,
+ 0x1f50, 0x1f57,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f7d,
+ 0x1f80, 0x1fb4,
+ 0x1fb6, 0x1fbc,
+ 0x1fbe, 0x1fbe,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fcc,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fdb,
+ 0x1fe0, 0x1fec,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ffc,
+ 0x2071, 0x2071,
+ 0x207f, 0x207f,
+ 0x2090, 0x209c,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210a, 0x2113,
+ 0x2115, 0x2115,
+ 0x2119, 0x211d,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212a, 0x212d,
+ 0x212f, 0x2139,
+ 0x213c, 0x213f,
+ 0x2145, 0x2149,
+ 0x214e, 0x214e,
+ 0x2183, 0x2184,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2ce4,
+ 0x2ceb, 0x2cee,
+ 0x2cf2, 0x2cf3,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0x2d30, 0x2d67,
+ 0x2d6f, 0x2d6f,
+ 0x2d80, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x2e2f, 0x2e2f,
+ 0x3005, 0x3006,
+ 0x3031, 0x3035,
+ 0x303b, 0x303c,
+ 0x3041, 0x3096,
+ 0x309d, 0x309f,
+ 0x30a1, 0x30fa,
+ 0x30fc, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x31a0, 0x31ba,
+ 0x31f0, 0x31ff,
+ 0x3400, 0x4db5,
+ 0x4e00, 0x9fef,
+ 0xa000, 0xa48c,
+ 0xa4d0, 0xa4fd,
+ 0xa500, 0xa60c,
+ 0xa610, 0xa61f,
+ 0xa62a, 0xa62b,
+ 0xa640, 0xa66e,
+ 0xa67f, 0xa69d,
+ 0xa6a0, 0xa6e5,
+ 0xa717, 0xa71f,
+ 0xa722, 0xa788,
+ 0xa78b, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f7, 0xa801,
+ 0xa803, 0xa805,
+ 0xa807, 0xa80a,
+ 0xa80c, 0xa822,
+ 0xa840, 0xa873,
+ 0xa882, 0xa8b3,
+ 0xa8f2, 0xa8f7,
+ 0xa8fb, 0xa8fb,
+ 0xa8fd, 0xa8fe,
+ 0xa90a, 0xa925,
+ 0xa930, 0xa946,
+ 0xa960, 0xa97c,
+ 0xa984, 0xa9b2,
+ 0xa9cf, 0xa9cf,
+ 0xa9e0, 0xa9e4,
+ 0xa9e6, 0xa9ef,
+ 0xa9fa, 0xa9fe,
+ 0xaa00, 0xaa28,
+ 0xaa40, 0xaa42,
+ 0xaa44, 0xaa4b,
+ 0xaa60, 0xaa76,
+ 0xaa7a, 0xaa7a,
+ 0xaa7e, 0xaaaf,
+ 0xaab1, 0xaab1,
+ 0xaab5, 0xaab6,
+ 0xaab9, 0xaabd,
+ 0xaac0, 0xaac0,
+ 0xaac2, 0xaac2,
+ 0xaadb, 0xaadd,
+ 0xaae0, 0xaaea,
+ 0xaaf2, 0xaaf4,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xab30, 0xab5a,
+ 0xab5c, 0xab67,
+ 0xab70, 0xabe2,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xf900, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb1d,
+ 0xfb1f, 0xfb28,
+ 0xfb2a, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3d,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfb,
+ 0xfe70, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0xff21, 0xff3a,
+ 0xff41, 0xff5a,
+ 0xff66, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0x10000, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x10300, 0x1031f,
+ 0x1032d, 0x10340,
+ 0x10342, 0x10349,
+ 0x10350, 0x10375,
+ 0x10380, 0x1039d,
+ 0x103a0, 0x103c3,
+ 0x103c8, 0x103cf,
+ 0x10400, 0x1049d,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10860, 0x10876,
+ 0x10880, 0x1089e,
+ 0x108e0, 0x108f2,
+ 0x108f4, 0x108f5,
+ 0x10900, 0x10915,
+ 0x10920, 0x10939,
+ 0x10980, 0x109b7,
+ 0x109be, 0x109bf,
+ 0x10a00, 0x10a00,
+ 0x10a10, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a35,
+ 0x10a60, 0x10a7c,
+ 0x10a80, 0x10a9c,
+ 0x10ac0, 0x10ac7,
+ 0x10ac9, 0x10ae4,
+ 0x10b00, 0x10b35,
+ 0x10b40, 0x10b55,
+ 0x10b60, 0x10b72,
+ 0x10b80, 0x10b91,
+ 0x10c00, 0x10c48,
+ 0x10c80, 0x10cb2,
+ 0x10cc0, 0x10cf2,
+ 0x10d00, 0x10d23,
+ 0x10f00, 0x10f1c,
+ 0x10f27, 0x10f27,
+ 0x10f30, 0x10f45,
+ 0x10fe0, 0x10ff6,
+ 0x11003, 0x11037,
+ 0x11083, 0x110af,
+ 0x110d0, 0x110e8,
+ 0x11103, 0x11126,
+ 0x11144, 0x11144,
+ 0x11150, 0x11172,
+ 0x11176, 0x11176,
+ 0x11183, 0x111b2,
+ 0x111c1, 0x111c4,
+ 0x111da, 0x111da,
+ 0x111dc, 0x111dc,
+ 0x11200, 0x11211,
+ 0x11213, 0x1122b,
+ 0x11280, 0x11286,
+ 0x11288, 0x11288,
+ 0x1128a, 0x1128d,
+ 0x1128f, 0x1129d,
+ 0x1129f, 0x112a8,
+ 0x112b0, 0x112de,
+ 0x11305, 0x1130c,
+ 0x1130f, 0x11310,
+ 0x11313, 0x11328,
+ 0x1132a, 0x11330,
+ 0x11332, 0x11333,
+ 0x11335, 0x11339,
+ 0x1133d, 0x1133d,
+ 0x11350, 0x11350,
+ 0x1135d, 0x11361,
+ 0x11400, 0x11434,
+ 0x11447, 0x1144a,
+ 0x1145f, 0x1145f,
+ 0x11480, 0x114af,
+ 0x114c4, 0x114c5,
+ 0x114c7, 0x114c7,
+ 0x11580, 0x115ae,
+ 0x115d8, 0x115db,
+ 0x11600, 0x1162f,
+ 0x11644, 0x11644,
+ 0x11680, 0x116aa,
+ 0x116b8, 0x116b8,
+ 0x11700, 0x1171a,
+ 0x11800, 0x1182b,
+ 0x118a0, 0x118df,
+ 0x118ff, 0x118ff,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d0,
+ 0x119e1, 0x119e1,
+ 0x119e3, 0x119e3,
+ 0x11a00, 0x11a00,
+ 0x11a0b, 0x11a32,
+ 0x11a3a, 0x11a3a,
+ 0x11a50, 0x11a50,
+ 0x11a5c, 0x11a89,
+ 0x11a9d, 0x11a9d,
+ 0x11ac0, 0x11af8,
+ 0x11c00, 0x11c08,
+ 0x11c0a, 0x11c2e,
+ 0x11c40, 0x11c40,
+ 0x11c72, 0x11c8f,
+ 0x11d00, 0x11d06,
+ 0x11d08, 0x11d09,
+ 0x11d0b, 0x11d30,
+ 0x11d46, 0x11d46,
+ 0x11d60, 0x11d65,
+ 0x11d67, 0x11d68,
+ 0x11d6a, 0x11d89,
+ 0x11d98, 0x11d98,
+ 0x11ee0, 0x11ef2,
+ 0x12000, 0x12399,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16ad0, 0x16aed,
+ 0x16b00, 0x16b2f,
+ 0x16b40, 0x16b43,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e7f,
+ 0x16f00, 0x16f4a,
+ 0x16f50, 0x16f50,
+ 0x16f93, 0x16f9f,
+ 0x16fe0, 0x16fe1,
+ 0x16fe3, 0x16fe3,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 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, 0x1d6c0,
+ 0x1d6c2, 0x1d6da,
+ 0x1d6dc, 0x1d6fa,
+ 0x1d6fc, 0x1d714,
+ 0x1d716, 0x1d734,
+ 0x1d736, 0x1d74e,
+ 0x1d750, 0x1d76e,
+ 0x1d770, 0x1d788,
+ 0x1d78a, 0x1d7a8,
+ 0x1d7aa, 0x1d7c2,
+ 0x1d7c4, 0x1d7cb,
+ 0x1e100, 0x1e12c,
+ 0x1e137, 0x1e13d,
+ 0x1e14e, 0x1e14e,
+ 0x1e2c0, 0x1e2eb,
+ 0x1e800, 0x1e8c4,
+ 0x1e900, 0x1e943,
+ 0x1e94b, 0x1e94b,
+ 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,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+}; /* CR_L */
+
+/* 'LC': General Category */
+static const OnigCodePoint CR_LC[] = {
+ 131,
+ 0x0041, 0x005a,
+ 0x0061, 0x007a,
+ 0x00b5, 0x00b5,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00f6,
+ 0x00f8, 0x01ba,
+ 0x01bc, 0x01bf,
+ 0x01c4, 0x0293,
+ 0x0295, 0x02af,
+ 0x0370, 0x0373,
+ 0x0376, 0x0377,
+ 0x037b, 0x037d,
+ 0x037f, 0x037f,
+ 0x0386, 0x0386,
+ 0x0388, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03f5,
+ 0x03f7, 0x0481,
+ 0x048a, 0x052f,
+ 0x0531, 0x0556,
+ 0x0560, 0x0588,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x10fa,
+ 0x10fd, 0x10ff,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0x1c80, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x1d00, 0x1d2b,
+ 0x1d6b, 0x1d77,
+ 0x1d79, 0x1d9a,
+ 0x1e00, 0x1f15,
+ 0x1f18, 0x1f1d,
+ 0x1f20, 0x1f45,
+ 0x1f48, 0x1f4d,
+ 0x1f50, 0x1f57,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f7d,
+ 0x1f80, 0x1fb4,
+ 0x1fb6, 0x1fbc,
+ 0x1fbe, 0x1fbe,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fcc,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fdb,
+ 0x1fe0, 0x1fec,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ffc,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210a, 0x2113,
+ 0x2115, 0x2115,
+ 0x2119, 0x211d,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212a, 0x212d,
+ 0x212f, 0x2134,
+ 0x2139, 0x2139,
+ 0x213c, 0x213f,
+ 0x2145, 0x2149,
+ 0x214e, 0x214e,
+ 0x2183, 0x2184,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2c7b,
+ 0x2c7e, 0x2ce4,
+ 0x2ceb, 0x2cee,
+ 0x2cf2, 0x2cf3,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0xa640, 0xa66d,
+ 0xa680, 0xa69b,
+ 0xa722, 0xa76f,
+ 0xa771, 0xa787,
+ 0xa78b, 0xa78e,
+ 0xa790, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7fa, 0xa7fa,
+ 0xab30, 0xab5a,
+ 0xab60, 0xab67,
+ 0xab70, 0xabbf,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xff21, 0xff3a,
+ 0xff41, 0xff5a,
+ 0x10400, 0x1044f,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+ 0x10c80, 0x10cb2,
+ 0x10cc0, 0x10cf2,
+ 0x118a0, 0x118df,
+ 0x16e40, 0x16e7f,
+ 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, 0x1d6c0,
+ 0x1d6c2, 0x1d6da,
+ 0x1d6dc, 0x1d6fa,
+ 0x1d6fc, 0x1d714,
+ 0x1d716, 0x1d734,
+ 0x1d736, 0x1d74e,
+ 0x1d750, 0x1d76e,
+ 0x1d770, 0x1d788,
+ 0x1d78a, 0x1d7a8,
+ 0x1d7aa, 0x1d7c2,
+ 0x1d7c4, 0x1d7cb,
+ 0x1e900, 0x1e943,
+}; /* CR_LC */
+
+/* 'Ll': General Category */
+static const OnigCodePoint CR_Ll[] = {
+ 642,
+ 0x0061, 0x007a,
+ 0x00b5, 0x00b5,
+ 0x00df, 0x00f6,
+ 0x00f8, 0x00ff,
+ 0x0101, 0x0101,
+ 0x0103, 0x0103,
+ 0x0105, 0x0105,
+ 0x0107, 0x0107,
+ 0x0109, 0x0109,
+ 0x010b, 0x010b,
+ 0x010d, 0x010d,
+ 0x010f, 0x010f,
+ 0x0111, 0x0111,
+ 0x0113, 0x0113,
+ 0x0115, 0x0115,
+ 0x0117, 0x0117,
+ 0x0119, 0x0119,
+ 0x011b, 0x011b,
+ 0x011d, 0x011d,
+ 0x011f, 0x011f,
+ 0x0121, 0x0121,
+ 0x0123, 0x0123,
+ 0x0125, 0x0125,
+ 0x0127, 0x0127,
+ 0x0129, 0x0129,
+ 0x012b, 0x012b,
+ 0x012d, 0x012d,
+ 0x012f, 0x012f,
+ 0x0131, 0x0131,
+ 0x0133, 0x0133,
+ 0x0135, 0x0135,
+ 0x0137, 0x0138,
+ 0x013a, 0x013a,
+ 0x013c, 0x013c,
+ 0x013e, 0x013e,
+ 0x0140, 0x0140,
+ 0x0142, 0x0142,
+ 0x0144, 0x0144,
+ 0x0146, 0x0146,
+ 0x0148, 0x0149,
+ 0x014b, 0x014b,
+ 0x014d, 0x014d,
+ 0x014f, 0x014f,
+ 0x0151, 0x0151,
+ 0x0153, 0x0153,
+ 0x0155, 0x0155,
+ 0x0157, 0x0157,
+ 0x0159, 0x0159,
+ 0x015b, 0x015b,
+ 0x015d, 0x015d,
+ 0x015f, 0x015f,
+ 0x0161, 0x0161,
+ 0x0163, 0x0163,
+ 0x0165, 0x0165,
+ 0x0167, 0x0167,
+ 0x0169, 0x0169,
+ 0x016b, 0x016b,
+ 0x016d, 0x016d,
+ 0x016f, 0x016f,
+ 0x0171, 0x0171,
+ 0x0173, 0x0173,
+ 0x0175, 0x0175,
+ 0x0177, 0x0177,
+ 0x017a, 0x017a,
+ 0x017c, 0x017c,
+ 0x017e, 0x0180,
+ 0x0183, 0x0183,
+ 0x0185, 0x0185,
+ 0x0188, 0x0188,
+ 0x018c, 0x018d,
+ 0x0192, 0x0192,
+ 0x0195, 0x0195,
+ 0x0199, 0x019b,
+ 0x019e, 0x019e,
+ 0x01a1, 0x01a1,
+ 0x01a3, 0x01a3,
+ 0x01a5, 0x01a5,
+ 0x01a8, 0x01a8,
+ 0x01aa, 0x01ab,
+ 0x01ad, 0x01ad,
+ 0x01b0, 0x01b0,
+ 0x01b4, 0x01b4,
+ 0x01b6, 0x01b6,
+ 0x01b9, 0x01ba,
+ 0x01bd, 0x01bf,
+ 0x01c6, 0x01c6,
+ 0x01c9, 0x01c9,
+ 0x01cc, 0x01cc,
+ 0x01ce, 0x01ce,
+ 0x01d0, 0x01d0,
+ 0x01d2, 0x01d2,
+ 0x01d4, 0x01d4,
+ 0x01d6, 0x01d6,
+ 0x01d8, 0x01d8,
+ 0x01da, 0x01da,
+ 0x01dc, 0x01dd,
+ 0x01df, 0x01df,
+ 0x01e1, 0x01e1,
+ 0x01e3, 0x01e3,
+ 0x01e5, 0x01e5,
+ 0x01e7, 0x01e7,
+ 0x01e9, 0x01e9,
+ 0x01eb, 0x01eb,
+ 0x01ed, 0x01ed,
+ 0x01ef, 0x01f0,
+ 0x01f3, 0x01f3,
+ 0x01f5, 0x01f5,
+ 0x01f9, 0x01f9,
+ 0x01fb, 0x01fb,
+ 0x01fd, 0x01fd,
+ 0x01ff, 0x01ff,
+ 0x0201, 0x0201,
+ 0x0203, 0x0203,
+ 0x0205, 0x0205,
+ 0x0207, 0x0207,
+ 0x0209, 0x0209,
+ 0x020b, 0x020b,
+ 0x020d, 0x020d,
+ 0x020f, 0x020f,
+ 0x0211, 0x0211,
+ 0x0213, 0x0213,
+ 0x0215, 0x0215,
+ 0x0217, 0x0217,
+ 0x0219, 0x0219,
+ 0x021b, 0x021b,
+ 0x021d, 0x021d,
+ 0x021f, 0x021f,
+ 0x0221, 0x0221,
+ 0x0223, 0x0223,
+ 0x0225, 0x0225,
+ 0x0227, 0x0227,
+ 0x0229, 0x0229,
+ 0x022b, 0x022b,
+ 0x022d, 0x022d,
+ 0x022f, 0x022f,
+ 0x0231, 0x0231,
+ 0x0233, 0x0239,
+ 0x023c, 0x023c,
+ 0x023f, 0x0240,
+ 0x0242, 0x0242,
+ 0x0247, 0x0247,
+ 0x0249, 0x0249,
+ 0x024b, 0x024b,
+ 0x024d, 0x024d,
+ 0x024f, 0x0293,
+ 0x0295, 0x02af,
+ 0x0371, 0x0371,
+ 0x0373, 0x0373,
+ 0x0377, 0x0377,
+ 0x037b, 0x037d,
+ 0x0390, 0x0390,
+ 0x03ac, 0x03ce,
+ 0x03d0, 0x03d1,
+ 0x03d5, 0x03d7,
+ 0x03d9, 0x03d9,
+ 0x03db, 0x03db,
+ 0x03dd, 0x03dd,
+ 0x03df, 0x03df,
+ 0x03e1, 0x03e1,
+ 0x03e3, 0x03e3,
+ 0x03e5, 0x03e5,
+ 0x03e7, 0x03e7,
+ 0x03e9, 0x03e9,
+ 0x03eb, 0x03eb,
+ 0x03ed, 0x03ed,
+ 0x03ef, 0x03f3,
+ 0x03f5, 0x03f5,
+ 0x03f8, 0x03f8,
+ 0x03fb, 0x03fc,
+ 0x0430, 0x045f,
+ 0x0461, 0x0461,
+ 0x0463, 0x0463,
+ 0x0465, 0x0465,
+ 0x0467, 0x0467,
+ 0x0469, 0x0469,
+ 0x046b, 0x046b,
+ 0x046d, 0x046d,
+ 0x046f, 0x046f,
+ 0x0471, 0x0471,
+ 0x0473, 0x0473,
+ 0x0475, 0x0475,
+ 0x0477, 0x0477,
+ 0x0479, 0x0479,
+ 0x047b, 0x047b,
+ 0x047d, 0x047d,
+ 0x047f, 0x047f,
+ 0x0481, 0x0481,
+ 0x048b, 0x048b,
+ 0x048d, 0x048d,
+ 0x048f, 0x048f,
+ 0x0491, 0x0491,
+ 0x0493, 0x0493,
+ 0x0495, 0x0495,
+ 0x0497, 0x0497,
+ 0x0499, 0x0499,
+ 0x049b, 0x049b,
+ 0x049d, 0x049d,
+ 0x049f, 0x049f,
+ 0x04a1, 0x04a1,
+ 0x04a3, 0x04a3,
+ 0x04a5, 0x04a5,
+ 0x04a7, 0x04a7,
+ 0x04a9, 0x04a9,
+ 0x04ab, 0x04ab,
+ 0x04ad, 0x04ad,
+ 0x04af, 0x04af,
+ 0x04b1, 0x04b1,
+ 0x04b3, 0x04b3,
+ 0x04b5, 0x04b5,
+ 0x04b7, 0x04b7,
+ 0x04b9, 0x04b9,
+ 0x04bb, 0x04bb,
+ 0x04bd, 0x04bd,
+ 0x04bf, 0x04bf,
+ 0x04c2, 0x04c2,
+ 0x04c4, 0x04c4,
+ 0x04c6, 0x04c6,
+ 0x04c8, 0x04c8,
+ 0x04ca, 0x04ca,
+ 0x04cc, 0x04cc,
+ 0x04ce, 0x04cf,
+ 0x04d1, 0x04d1,
+ 0x04d3, 0x04d3,
+ 0x04d5, 0x04d5,
+ 0x04d7, 0x04d7,
+ 0x04d9, 0x04d9,
+ 0x04db, 0x04db,
+ 0x04dd, 0x04dd,
+ 0x04df, 0x04df,
+ 0x04e1, 0x04e1,
+ 0x04e3, 0x04e3,
+ 0x04e5, 0x04e5,
+ 0x04e7, 0x04e7,
+ 0x04e9, 0x04e9,
+ 0x04eb, 0x04eb,
+ 0x04ed, 0x04ed,
+ 0x04ef, 0x04ef,
+ 0x04f1, 0x04f1,
+ 0x04f3, 0x04f3,
+ 0x04f5, 0x04f5,
+ 0x04f7, 0x04f7,
+ 0x04f9, 0x04f9,
+ 0x04fb, 0x04fb,
+ 0x04fd, 0x04fd,
+ 0x04ff, 0x04ff,
+ 0x0501, 0x0501,
+ 0x0503, 0x0503,
+ 0x0505, 0x0505,
+ 0x0507, 0x0507,
+ 0x0509, 0x0509,
+ 0x050b, 0x050b,
+ 0x050d, 0x050d,
+ 0x050f, 0x050f,
+ 0x0511, 0x0511,
+ 0x0513, 0x0513,
+ 0x0515, 0x0515,
+ 0x0517, 0x0517,
+ 0x0519, 0x0519,
+ 0x051b, 0x051b,
+ 0x051d, 0x051d,
+ 0x051f, 0x051f,
+ 0x0521, 0x0521,
+ 0x0523, 0x0523,
+ 0x0525, 0x0525,
+ 0x0527, 0x0527,
+ 0x0529, 0x0529,
+ 0x052b, 0x052b,
+ 0x052d, 0x052d,
+ 0x052f, 0x052f,
+ 0x0560, 0x0588,
+ 0x10d0, 0x10fa,
+ 0x10fd, 0x10ff,
+ 0x13f8, 0x13fd,
+ 0x1c80, 0x1c88,
+ 0x1d00, 0x1d2b,
+ 0x1d6b, 0x1d77,
+ 0x1d79, 0x1d9a,
+ 0x1e01, 0x1e01,
+ 0x1e03, 0x1e03,
+ 0x1e05, 0x1e05,
+ 0x1e07, 0x1e07,
+ 0x1e09, 0x1e09,
+ 0x1e0b, 0x1e0b,
+ 0x1e0d, 0x1e0d,
+ 0x1e0f, 0x1e0f,
+ 0x1e11, 0x1e11,
+ 0x1e13, 0x1e13,
+ 0x1e15, 0x1e15,
+ 0x1e17, 0x1e17,
+ 0x1e19, 0x1e19,
+ 0x1e1b, 0x1e1b,
+ 0x1e1d, 0x1e1d,
+ 0x1e1f, 0x1e1f,
+ 0x1e21, 0x1e21,
+ 0x1e23, 0x1e23,
+ 0x1e25, 0x1e25,
+ 0x1e27, 0x1e27,
+ 0x1e29, 0x1e29,
+ 0x1e2b, 0x1e2b,
+ 0x1e2d, 0x1e2d,
+ 0x1e2f, 0x1e2f,
+ 0x1e31, 0x1e31,
+ 0x1e33, 0x1e33,
+ 0x1e35, 0x1e35,
+ 0x1e37, 0x1e37,
+ 0x1e39, 0x1e39,
+ 0x1e3b, 0x1e3b,
+ 0x1e3d, 0x1e3d,
+ 0x1e3f, 0x1e3f,
+ 0x1e41, 0x1e41,
+ 0x1e43, 0x1e43,
+ 0x1e45, 0x1e45,
+ 0x1e47, 0x1e47,
+ 0x1e49, 0x1e49,
+ 0x1e4b, 0x1e4b,
+ 0x1e4d, 0x1e4d,
+ 0x1e4f, 0x1e4f,
+ 0x1e51, 0x1e51,
+ 0x1e53, 0x1e53,
+ 0x1e55, 0x1e55,
+ 0x1e57, 0x1e57,
+ 0x1e59, 0x1e59,
+ 0x1e5b, 0x1e5b,
+ 0x1e5d, 0x1e5d,
+ 0x1e5f, 0x1e5f,
+ 0x1e61, 0x1e61,
+ 0x1e63, 0x1e63,
+ 0x1e65, 0x1e65,
+ 0x1e67, 0x1e67,
+ 0x1e69, 0x1e69,
+ 0x1e6b, 0x1e6b,
+ 0x1e6d, 0x1e6d,
+ 0x1e6f, 0x1e6f,
+ 0x1e71, 0x1e71,
+ 0x1e73, 0x1e73,
+ 0x1e75, 0x1e75,
+ 0x1e77, 0x1e77,
+ 0x1e79, 0x1e79,
+ 0x1e7b, 0x1e7b,
+ 0x1e7d, 0x1e7d,
+ 0x1e7f, 0x1e7f,
+ 0x1e81, 0x1e81,
+ 0x1e83, 0x1e83,
+ 0x1e85, 0x1e85,
+ 0x1e87, 0x1e87,
+ 0x1e89, 0x1e89,
+ 0x1e8b, 0x1e8b,
+ 0x1e8d, 0x1e8d,
+ 0x1e8f, 0x1e8f,
+ 0x1e91, 0x1e91,
+ 0x1e93, 0x1e93,
+ 0x1e95, 0x1e9d,
+ 0x1e9f, 0x1e9f,
+ 0x1ea1, 0x1ea1,
+ 0x1ea3, 0x1ea3,
+ 0x1ea5, 0x1ea5,
+ 0x1ea7, 0x1ea7,
+ 0x1ea9, 0x1ea9,
+ 0x1eab, 0x1eab,
+ 0x1ead, 0x1ead,
+ 0x1eaf, 0x1eaf,
+ 0x1eb1, 0x1eb1,
+ 0x1eb3, 0x1eb3,
+ 0x1eb5, 0x1eb5,
+ 0x1eb7, 0x1eb7,
+ 0x1eb9, 0x1eb9,
+ 0x1ebb, 0x1ebb,
+ 0x1ebd, 0x1ebd,
+ 0x1ebf, 0x1ebf,
+ 0x1ec1, 0x1ec1,
+ 0x1ec3, 0x1ec3,
+ 0x1ec5, 0x1ec5,
+ 0x1ec7, 0x1ec7,
+ 0x1ec9, 0x1ec9,
+ 0x1ecb, 0x1ecb,
+ 0x1ecd, 0x1ecd,
+ 0x1ecf, 0x1ecf,
+ 0x1ed1, 0x1ed1,
+ 0x1ed3, 0x1ed3,
+ 0x1ed5, 0x1ed5,
+ 0x1ed7, 0x1ed7,
+ 0x1ed9, 0x1ed9,
+ 0x1edb, 0x1edb,
+ 0x1edd, 0x1edd,
+ 0x1edf, 0x1edf,
+ 0x1ee1, 0x1ee1,
+ 0x1ee3, 0x1ee3,
+ 0x1ee5, 0x1ee5,
+ 0x1ee7, 0x1ee7,
+ 0x1ee9, 0x1ee9,
+ 0x1eeb, 0x1eeb,
+ 0x1eed, 0x1eed,
+ 0x1eef, 0x1eef,
+ 0x1ef1, 0x1ef1,
+ 0x1ef3, 0x1ef3,
+ 0x1ef5, 0x1ef5,
+ 0x1ef7, 0x1ef7,
+ 0x1ef9, 0x1ef9,
+ 0x1efb, 0x1efb,
+ 0x1efd, 0x1efd,
+ 0x1eff, 0x1f07,
+ 0x1f10, 0x1f15,
+ 0x1f20, 0x1f27,
+ 0x1f30, 0x1f37,
+ 0x1f40, 0x1f45,
+ 0x1f50, 0x1f57,
+ 0x1f60, 0x1f67,
+ 0x1f70, 0x1f7d,
+ 0x1f80, 0x1f87,
+ 0x1f90, 0x1f97,
+ 0x1fa0, 0x1fa7,
+ 0x1fb0, 0x1fb4,
+ 0x1fb6, 0x1fb7,
+ 0x1fbe, 0x1fbe,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fc7,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fd7,
+ 0x1fe0, 0x1fe7,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ff7,
+ 0x210a, 0x210a,
+ 0x210e, 0x210f,
+ 0x2113, 0x2113,
+ 0x212f, 0x212f,
+ 0x2134, 0x2134,
+ 0x2139, 0x2139,
+ 0x213c, 0x213d,
+ 0x2146, 0x2149,
+ 0x214e, 0x214e,
+ 0x2184, 0x2184,
+ 0x2c30, 0x2c5e,
+ 0x2c61, 0x2c61,
+ 0x2c65, 0x2c66,
+ 0x2c68, 0x2c68,
+ 0x2c6a, 0x2c6a,
+ 0x2c6c, 0x2c6c,
+ 0x2c71, 0x2c71,
+ 0x2c73, 0x2c74,
+ 0x2c76, 0x2c7b,
+ 0x2c81, 0x2c81,
+ 0x2c83, 0x2c83,
+ 0x2c85, 0x2c85,
+ 0x2c87, 0x2c87,
+ 0x2c89, 0x2c89,
+ 0x2c8b, 0x2c8b,
+ 0x2c8d, 0x2c8d,
+ 0x2c8f, 0x2c8f,
+ 0x2c91, 0x2c91,
+ 0x2c93, 0x2c93,
+ 0x2c95, 0x2c95,
+ 0x2c97, 0x2c97,
+ 0x2c99, 0x2c99,
+ 0x2c9b, 0x2c9b,
+ 0x2c9d, 0x2c9d,
+ 0x2c9f, 0x2c9f,
+ 0x2ca1, 0x2ca1,
+ 0x2ca3, 0x2ca3,
+ 0x2ca5, 0x2ca5,
+ 0x2ca7, 0x2ca7,
+ 0x2ca9, 0x2ca9,
+ 0x2cab, 0x2cab,
+ 0x2cad, 0x2cad,
+ 0x2caf, 0x2caf,
+ 0x2cb1, 0x2cb1,
+ 0x2cb3, 0x2cb3,
+ 0x2cb5, 0x2cb5,
+ 0x2cb7, 0x2cb7,
+ 0x2cb9, 0x2cb9,
+ 0x2cbb, 0x2cbb,
+ 0x2cbd, 0x2cbd,
+ 0x2cbf, 0x2cbf,
+ 0x2cc1, 0x2cc1,
+ 0x2cc3, 0x2cc3,
+ 0x2cc5, 0x2cc5,
+ 0x2cc7, 0x2cc7,
+ 0x2cc9, 0x2cc9,
+ 0x2ccb, 0x2ccb,
+ 0x2ccd, 0x2ccd,
+ 0x2ccf, 0x2ccf,
+ 0x2cd1, 0x2cd1,
+ 0x2cd3, 0x2cd3,
+ 0x2cd5, 0x2cd5,
+ 0x2cd7, 0x2cd7,
+ 0x2cd9, 0x2cd9,
+ 0x2cdb, 0x2cdb,
+ 0x2cdd, 0x2cdd,
+ 0x2cdf, 0x2cdf,
+ 0x2ce1, 0x2ce1,
+ 0x2ce3, 0x2ce4,
+ 0x2cec, 0x2cec,
+ 0x2cee, 0x2cee,
+ 0x2cf3, 0x2cf3,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0xa641, 0xa641,
+ 0xa643, 0xa643,
+ 0xa645, 0xa645,
+ 0xa647, 0xa647,
+ 0xa649, 0xa649,
+ 0xa64b, 0xa64b,
+ 0xa64d, 0xa64d,
+ 0xa64f, 0xa64f,
+ 0xa651, 0xa651,
+ 0xa653, 0xa653,
+ 0xa655, 0xa655,
+ 0xa657, 0xa657,
+ 0xa659, 0xa659,
+ 0xa65b, 0xa65b,
+ 0xa65d, 0xa65d,
+ 0xa65f, 0xa65f,
+ 0xa661, 0xa661,
+ 0xa663, 0xa663,
+ 0xa665, 0xa665,
+ 0xa667, 0xa667,
+ 0xa669, 0xa669,
+ 0xa66b, 0xa66b,
+ 0xa66d, 0xa66d,
+ 0xa681, 0xa681,
+ 0xa683, 0xa683,
+ 0xa685, 0xa685,
+ 0xa687, 0xa687,
+ 0xa689, 0xa689,
+ 0xa68b, 0xa68b,
+ 0xa68d, 0xa68d,
+ 0xa68f, 0xa68f,
+ 0xa691, 0xa691,
+ 0xa693, 0xa693,
+ 0xa695, 0xa695,
+ 0xa697, 0xa697,
+ 0xa699, 0xa699,
+ 0xa69b, 0xa69b,
+ 0xa723, 0xa723,
+ 0xa725, 0xa725,
+ 0xa727, 0xa727,
+ 0xa729, 0xa729,
+ 0xa72b, 0xa72b,
+ 0xa72d, 0xa72d,
+ 0xa72f, 0xa731,
+ 0xa733, 0xa733,
+ 0xa735, 0xa735,
+ 0xa737, 0xa737,
+ 0xa739, 0xa739,
+ 0xa73b, 0xa73b,
+ 0xa73d, 0xa73d,
+ 0xa73f, 0xa73f,
+ 0xa741, 0xa741,
+ 0xa743, 0xa743,
+ 0xa745, 0xa745,
+ 0xa747, 0xa747,
+ 0xa749, 0xa749,
+ 0xa74b, 0xa74b,
+ 0xa74d, 0xa74d,
+ 0xa74f, 0xa74f,
+ 0xa751, 0xa751,
+ 0xa753, 0xa753,
+ 0xa755, 0xa755,
+ 0xa757, 0xa757,
+ 0xa759, 0xa759,
+ 0xa75b, 0xa75b,
+ 0xa75d, 0xa75d,
+ 0xa75f, 0xa75f,
+ 0xa761, 0xa761,
+ 0xa763, 0xa763,
+ 0xa765, 0xa765,
+ 0xa767, 0xa767,
+ 0xa769, 0xa769,
+ 0xa76b, 0xa76b,
+ 0xa76d, 0xa76d,
+ 0xa76f, 0xa76f,
+ 0xa771, 0xa778,
+ 0xa77a, 0xa77a,
+ 0xa77c, 0xa77c,
+ 0xa77f, 0xa77f,
+ 0xa781, 0xa781,
+ 0xa783, 0xa783,
+ 0xa785, 0xa785,
+ 0xa787, 0xa787,
+ 0xa78c, 0xa78c,
+ 0xa78e, 0xa78e,
+ 0xa791, 0xa791,
+ 0xa793, 0xa795,
+ 0xa797, 0xa797,
+ 0xa799, 0xa799,
+ 0xa79b, 0xa79b,
+ 0xa79d, 0xa79d,
+ 0xa79f, 0xa79f,
+ 0xa7a1, 0xa7a1,
+ 0xa7a3, 0xa7a3,
+ 0xa7a5, 0xa7a5,
+ 0xa7a7, 0xa7a7,
+ 0xa7a9, 0xa7a9,
+ 0xa7af, 0xa7af,
+ 0xa7b5, 0xa7b5,
+ 0xa7b7, 0xa7b7,
+ 0xa7b9, 0xa7b9,
+ 0xa7bb, 0xa7bb,
+ 0xa7bd, 0xa7bd,
+ 0xa7bf, 0xa7bf,
+ 0xa7c3, 0xa7c3,
+ 0xa7fa, 0xa7fa,
+ 0xab30, 0xab5a,
+ 0xab60, 0xab67,
+ 0xab70, 0xabbf,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xff41, 0xff5a,
+ 0x10428, 0x1044f,
+ 0x104d8, 0x104fb,
+ 0x10cc0, 0x10cf2,
+ 0x118c0, 0x118df,
+ 0x16e60, 0x16e7f,
+ 0x1d41a, 0x1d433,
+ 0x1d44e, 0x1d454,
+ 0x1d456, 0x1d467,
+ 0x1d482, 0x1d49b,
+ 0x1d4b6, 0x1d4b9,
+ 0x1d4bb, 0x1d4bb,
+ 0x1d4bd, 0x1d4c3,
+ 0x1d4c5, 0x1d4cf,
+ 0x1d4ea, 0x1d503,
+ 0x1d51e, 0x1d537,
+ 0x1d552, 0x1d56b,
+ 0x1d586, 0x1d59f,
+ 0x1d5ba, 0x1d5d3,
+ 0x1d5ee, 0x1d607,
+ 0x1d622, 0x1d63b,
+ 0x1d656, 0x1d66f,
+ 0x1d68a, 0x1d6a5,
+ 0x1d6c2, 0x1d6da,
+ 0x1d6dc, 0x1d6e1,
+ 0x1d6fc, 0x1d714,
+ 0x1d716, 0x1d71b,
+ 0x1d736, 0x1d74e,
+ 0x1d750, 0x1d755,
+ 0x1d770, 0x1d788,
+ 0x1d78a, 0x1d78f,
+ 0x1d7aa, 0x1d7c2,
+ 0x1d7c4, 0x1d7c9,
+ 0x1d7cb, 0x1d7cb,
+ 0x1e922, 0x1e943,
+}; /* CR_Ll */
+
+/* 'Lm': General Category */
+static const OnigCodePoint CR_Lm[] = {
+ 60,
+ 0x02b0, 0x02c1,
+ 0x02c6, 0x02d1,
+ 0x02e0, 0x02e4,
+ 0x02ec, 0x02ec,
+ 0x02ee, 0x02ee,
+ 0x0374, 0x0374,
+ 0x037a, 0x037a,
+ 0x0559, 0x0559,
+ 0x0640, 0x0640,
+ 0x06e5, 0x06e6,
+ 0x07f4, 0x07f5,
+ 0x07fa, 0x07fa,
+ 0x081a, 0x081a,
+ 0x0824, 0x0824,
+ 0x0828, 0x0828,
+ 0x0971, 0x0971,
+ 0x0e46, 0x0e46,
+ 0x0ec6, 0x0ec6,
+ 0x10fc, 0x10fc,
+ 0x17d7, 0x17d7,
+ 0x1843, 0x1843,
+ 0x1aa7, 0x1aa7,
+ 0x1c78, 0x1c7d,
+ 0x1d2c, 0x1d6a,
+ 0x1d78, 0x1d78,
+ 0x1d9b, 0x1dbf,
+ 0x2071, 0x2071,
+ 0x207f, 0x207f,
+ 0x2090, 0x209c,
+ 0x2c7c, 0x2c7d,
+ 0x2d6f, 0x2d6f,
+ 0x2e2f, 0x2e2f,
+ 0x3005, 0x3005,
+ 0x3031, 0x3035,
+ 0x303b, 0x303b,
+ 0x309d, 0x309e,
+ 0x30fc, 0x30fe,
+ 0xa015, 0xa015,
+ 0xa4f8, 0xa4fd,
+ 0xa60c, 0xa60c,
+ 0xa67f, 0xa67f,
+ 0xa69c, 0xa69d,
+ 0xa717, 0xa71f,
+ 0xa770, 0xa770,
+ 0xa788, 0xa788,
+ 0xa7f8, 0xa7f9,
+ 0xa9cf, 0xa9cf,
+ 0xa9e6, 0xa9e6,
+ 0xaa70, 0xaa70,
+ 0xaadd, 0xaadd,
+ 0xaaf3, 0xaaf4,
+ 0xab5c, 0xab5f,
+ 0xff70, 0xff70,
+ 0xff9e, 0xff9f,
+ 0x16b40, 0x16b43,
+ 0x16f93, 0x16f9f,
+ 0x16fe0, 0x16fe1,
+ 0x16fe3, 0x16fe3,
+ 0x1e137, 0x1e13d,
+ 0x1e94b, 0x1e94b,
+}; /* CR_Lm */
+
+/* 'Lo': General Category */
+static const OnigCodePoint CR_Lo[] = {
+ 476,
+ 0x00aa, 0x00aa,
+ 0x00ba, 0x00ba,
+ 0x01bb, 0x01bb,
+ 0x01c0, 0x01c3,
+ 0x0294, 0x0294,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f2,
+ 0x0620, 0x063f,
+ 0x0641, 0x064a,
+ 0x066e, 0x066f,
+ 0x0671, 0x06d3,
+ 0x06d5, 0x06d5,
+ 0x06ee, 0x06ef,
+ 0x06fa, 0x06fc,
+ 0x06ff, 0x06ff,
+ 0x0710, 0x0710,
+ 0x0712, 0x072f,
+ 0x074d, 0x07a5,
+ 0x07b1, 0x07b1,
+ 0x07ca, 0x07ea,
+ 0x0800, 0x0815,
+ 0x0840, 0x0858,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x0904, 0x0939,
+ 0x093d, 0x093d,
+ 0x0950, 0x0950,
+ 0x0958, 0x0961,
+ 0x0972, 0x0980,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bd, 0x09bd,
+ 0x09ce, 0x09ce,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e1,
+ 0x09f0, 0x09f1,
+ 0x09fc, 0x09fc,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a72, 0x0a74,
+ 0x0a85, 0x0a8d,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0abd, 0x0abd,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae1,
+ 0x0af9, 0x0af9,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3d, 0x0b3d,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b61,
+ 0x0b71, 0x0b71,
+ 0x0b83, 0x0b83,
+ 0x0b85, 0x0b8a,
+ 0x0b8e, 0x0b90,
+ 0x0b92, 0x0b95,
+ 0x0b99, 0x0b9a,
+ 0x0b9c, 0x0b9c,
+ 0x0b9e, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb9,
+ 0x0bd0, 0x0bd0,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3d, 0x0c3d,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c61,
+ 0x0c80, 0x0c80,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbd, 0x0cbd,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce1,
+ 0x0cf1, 0x0cf2,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d3a,
+ 0x0d3d, 0x0d3d,
+ 0x0d4e, 0x0d4e,
+ 0x0d54, 0x0d56,
+ 0x0d5f, 0x0d61,
+ 0x0d7a, 0x0d7f,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0e01, 0x0e30,
+ 0x0e32, 0x0e33,
+ 0x0e40, 0x0e45,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e86, 0x0e8a,
+ 0x0e8c, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0eb0,
+ 0x0eb2, 0x0eb3,
+ 0x0ebd, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0edc, 0x0edf,
+ 0x0f00, 0x0f00,
+ 0x0f40, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f88, 0x0f8c,
+ 0x1000, 0x102a,
+ 0x103f, 0x103f,
+ 0x1050, 0x1055,
+ 0x105a, 0x105d,
+ 0x1061, 0x1061,
+ 0x1065, 0x1066,
+ 0x106e, 0x1070,
+ 0x1075, 0x1081,
+ 0x108e, 0x108e,
+ 0x1100, 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,
+ 0x1380, 0x138f,
+ 0x1401, 0x166c,
+ 0x166f, 0x167f,
+ 0x1681, 0x169a,
+ 0x16a0, 0x16ea,
+ 0x16f1, 0x16f8,
+ 0x1700, 0x170c,
+ 0x170e, 0x1711,
+ 0x1720, 0x1731,
+ 0x1740, 0x1751,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1780, 0x17b3,
+ 0x17dc, 0x17dc,
+ 0x1820, 0x1842,
+ 0x1844, 0x1878,
+ 0x1880, 0x1884,
+ 0x1887, 0x18a8,
+ 0x18aa, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191e,
+ 0x1950, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19ab,
+ 0x19b0, 0x19c9,
+ 0x1a00, 0x1a16,
+ 0x1a20, 0x1a54,
+ 0x1b05, 0x1b33,
+ 0x1b45, 0x1b4b,
+ 0x1b83, 0x1ba0,
+ 0x1bae, 0x1baf,
+ 0x1bba, 0x1be5,
+ 0x1c00, 0x1c23,
+ 0x1c4d, 0x1c4f,
+ 0x1c5a, 0x1c77,
+ 0x1ce9, 0x1cec,
+ 0x1cee, 0x1cf3,
+ 0x1cf5, 0x1cf6,
+ 0x1cfa, 0x1cfa,
+ 0x2135, 0x2138,
+ 0x2d30, 0x2d67,
+ 0x2d80, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x3006, 0x3006,
+ 0x303c, 0x303c,
+ 0x3041, 0x3096,
+ 0x309f, 0x309f,
+ 0x30a1, 0x30fa,
+ 0x30ff, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x31a0, 0x31ba,
+ 0x31f0, 0x31ff,
+ 0x3400, 0x4db5,
+ 0x4e00, 0x9fef,
+ 0xa000, 0xa014,
+ 0xa016, 0xa48c,
+ 0xa4d0, 0xa4f7,
+ 0xa500, 0xa60b,
+ 0xa610, 0xa61f,
+ 0xa62a, 0xa62b,
+ 0xa66e, 0xa66e,
+ 0xa6a0, 0xa6e5,
+ 0xa78f, 0xa78f,
+ 0xa7f7, 0xa7f7,
+ 0xa7fb, 0xa801,
+ 0xa803, 0xa805,
+ 0xa807, 0xa80a,
+ 0xa80c, 0xa822,
+ 0xa840, 0xa873,
+ 0xa882, 0xa8b3,
+ 0xa8f2, 0xa8f7,
+ 0xa8fb, 0xa8fb,
+ 0xa8fd, 0xa8fe,
+ 0xa90a, 0xa925,
+ 0xa930, 0xa946,
+ 0xa960, 0xa97c,
+ 0xa984, 0xa9b2,
+ 0xa9e0, 0xa9e4,
+ 0xa9e7, 0xa9ef,
+ 0xa9fa, 0xa9fe,
+ 0xaa00, 0xaa28,
+ 0xaa40, 0xaa42,
+ 0xaa44, 0xaa4b,
+ 0xaa60, 0xaa6f,
+ 0xaa71, 0xaa76,
+ 0xaa7a, 0xaa7a,
+ 0xaa7e, 0xaaaf,
+ 0xaab1, 0xaab1,
+ 0xaab5, 0xaab6,
+ 0xaab9, 0xaabd,
+ 0xaac0, 0xaac0,
+ 0xaac2, 0xaac2,
+ 0xaadb, 0xaadc,
+ 0xaae0, 0xaaea,
+ 0xaaf2, 0xaaf2,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xabc0, 0xabe2,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xf900, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb1d, 0xfb1d,
+ 0xfb1f, 0xfb28,
+ 0xfb2a, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3d,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfb,
+ 0xfe70, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0xff66, 0xff6f,
+ 0xff71, 0xff9d,
+ 0xffa0, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0x10000, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x10300, 0x1031f,
+ 0x1032d, 0x10340,
+ 0x10342, 0x10349,
+ 0x10350, 0x10375,
+ 0x10380, 0x1039d,
+ 0x103a0, 0x103c3,
+ 0x103c8, 0x103cf,
+ 0x10450, 0x1049d,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10860, 0x10876,
+ 0x10880, 0x1089e,
+ 0x108e0, 0x108f2,
+ 0x108f4, 0x108f5,
+ 0x10900, 0x10915,
+ 0x10920, 0x10939,
+ 0x10980, 0x109b7,
+ 0x109be, 0x109bf,
+ 0x10a00, 0x10a00,
+ 0x10a10, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a35,
+ 0x10a60, 0x10a7c,
+ 0x10a80, 0x10a9c,
+ 0x10ac0, 0x10ac7,
+ 0x10ac9, 0x10ae4,
+ 0x10b00, 0x10b35,
+ 0x10b40, 0x10b55,
+ 0x10b60, 0x10b72,
+ 0x10b80, 0x10b91,
+ 0x10c00, 0x10c48,
+ 0x10d00, 0x10d23,
+ 0x10f00, 0x10f1c,
+ 0x10f27, 0x10f27,
+ 0x10f30, 0x10f45,
+ 0x10fe0, 0x10ff6,
+ 0x11003, 0x11037,
+ 0x11083, 0x110af,
+ 0x110d0, 0x110e8,
+ 0x11103, 0x11126,
+ 0x11144, 0x11144,
+ 0x11150, 0x11172,
+ 0x11176, 0x11176,
+ 0x11183, 0x111b2,
+ 0x111c1, 0x111c4,
+ 0x111da, 0x111da,
+ 0x111dc, 0x111dc,
+ 0x11200, 0x11211,
+ 0x11213, 0x1122b,
+ 0x11280, 0x11286,
+ 0x11288, 0x11288,
+ 0x1128a, 0x1128d,
+ 0x1128f, 0x1129d,
+ 0x1129f, 0x112a8,
+ 0x112b0, 0x112de,
+ 0x11305, 0x1130c,
+ 0x1130f, 0x11310,
+ 0x11313, 0x11328,
+ 0x1132a, 0x11330,
+ 0x11332, 0x11333,
+ 0x11335, 0x11339,
+ 0x1133d, 0x1133d,
+ 0x11350, 0x11350,
+ 0x1135d, 0x11361,
+ 0x11400, 0x11434,
+ 0x11447, 0x1144a,
+ 0x1145f, 0x1145f,
+ 0x11480, 0x114af,
+ 0x114c4, 0x114c5,
+ 0x114c7, 0x114c7,
+ 0x11580, 0x115ae,
+ 0x115d8, 0x115db,
+ 0x11600, 0x1162f,
+ 0x11644, 0x11644,
+ 0x11680, 0x116aa,
+ 0x116b8, 0x116b8,
+ 0x11700, 0x1171a,
+ 0x11800, 0x1182b,
+ 0x118ff, 0x118ff,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d0,
+ 0x119e1, 0x119e1,
+ 0x119e3, 0x119e3,
+ 0x11a00, 0x11a00,
+ 0x11a0b, 0x11a32,
+ 0x11a3a, 0x11a3a,
+ 0x11a50, 0x11a50,
+ 0x11a5c, 0x11a89,
+ 0x11a9d, 0x11a9d,
+ 0x11ac0, 0x11af8,
+ 0x11c00, 0x11c08,
+ 0x11c0a, 0x11c2e,
+ 0x11c40, 0x11c40,
+ 0x11c72, 0x11c8f,
+ 0x11d00, 0x11d06,
+ 0x11d08, 0x11d09,
+ 0x11d0b, 0x11d30,
+ 0x11d46, 0x11d46,
+ 0x11d60, 0x11d65,
+ 0x11d67, 0x11d68,
+ 0x11d6a, 0x11d89,
+ 0x11d98, 0x11d98,
+ 0x11ee0, 0x11ef2,
+ 0x12000, 0x12399,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16ad0, 0x16aed,
+ 0x16b00, 0x16b2f,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16f00, 0x16f4a,
+ 0x16f50, 0x16f50,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1e100, 0x1e12c,
+ 0x1e14e, 0x1e14e,
+ 0x1e2c0, 0x1e2eb,
+ 0x1e800, 0x1e8c4,
+ 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,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+}; /* CR_Lo */
+
+/* 'Lt': General Category */
+static const OnigCodePoint CR_Lt[] = {
+ 10,
+ 0x01c5, 0x01c5,
+ 0x01c8, 0x01c8,
+ 0x01cb, 0x01cb,
+ 0x01f2, 0x01f2,
+ 0x1f88, 0x1f8f,
+ 0x1f98, 0x1f9f,
+ 0x1fa8, 0x1faf,
+ 0x1fbc, 0x1fbc,
+ 0x1fcc, 0x1fcc,
+ 0x1ffc, 0x1ffc,
+}; /* CR_Lt */
+
+/* 'Lu': General Category */
+static const OnigCodePoint CR_Lu[] = {
+ 636,
+ 0x0041, 0x005a,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00de,
+ 0x0100, 0x0100,
+ 0x0102, 0x0102,
+ 0x0104, 0x0104,
+ 0x0106, 0x0106,
+ 0x0108, 0x0108,
+ 0x010a, 0x010a,
+ 0x010c, 0x010c,
+ 0x010e, 0x010e,
+ 0x0110, 0x0110,
+ 0x0112, 0x0112,
+ 0x0114, 0x0114,
+ 0x0116, 0x0116,
+ 0x0118, 0x0118,
+ 0x011a, 0x011a,
+ 0x011c, 0x011c,
+ 0x011e, 0x011e,
+ 0x0120, 0x0120,
+ 0x0122, 0x0122,
+ 0x0124, 0x0124,
+ 0x0126, 0x0126,
+ 0x0128, 0x0128,
+ 0x012a, 0x012a,
+ 0x012c, 0x012c,
+ 0x012e, 0x012e,
+ 0x0130, 0x0130,
+ 0x0132, 0x0132,
+ 0x0134, 0x0134,
+ 0x0136, 0x0136,
+ 0x0139, 0x0139,
+ 0x013b, 0x013b,
+ 0x013d, 0x013d,
+ 0x013f, 0x013f,
+ 0x0141, 0x0141,
+ 0x0143, 0x0143,
+ 0x0145, 0x0145,
+ 0x0147, 0x0147,
+ 0x014a, 0x014a,
+ 0x014c, 0x014c,
+ 0x014e, 0x014e,
+ 0x0150, 0x0150,
+ 0x0152, 0x0152,
+ 0x0154, 0x0154,
+ 0x0156, 0x0156,
+ 0x0158, 0x0158,
+ 0x015a, 0x015a,
+ 0x015c, 0x015c,
+ 0x015e, 0x015e,
+ 0x0160, 0x0160,
+ 0x0162, 0x0162,
+ 0x0164, 0x0164,
+ 0x0166, 0x0166,
+ 0x0168, 0x0168,
+ 0x016a, 0x016a,
+ 0x016c, 0x016c,
+ 0x016e, 0x016e,
+ 0x0170, 0x0170,
+ 0x0172, 0x0172,
+ 0x0174, 0x0174,
+ 0x0176, 0x0176,
+ 0x0178, 0x0179,
+ 0x017b, 0x017b,
+ 0x017d, 0x017d,
+ 0x0181, 0x0182,
+ 0x0184, 0x0184,
+ 0x0186, 0x0187,
+ 0x0189, 0x018b,
+ 0x018e, 0x0191,
+ 0x0193, 0x0194,
+ 0x0196, 0x0198,
+ 0x019c, 0x019d,
+ 0x019f, 0x01a0,
+ 0x01a2, 0x01a2,
+ 0x01a4, 0x01a4,
+ 0x01a6, 0x01a7,
+ 0x01a9, 0x01a9,
+ 0x01ac, 0x01ac,
+ 0x01ae, 0x01af,
+ 0x01b1, 0x01b3,
+ 0x01b5, 0x01b5,
+ 0x01b7, 0x01b8,
+ 0x01bc, 0x01bc,
+ 0x01c4, 0x01c4,
+ 0x01c7, 0x01c7,
+ 0x01ca, 0x01ca,
+ 0x01cd, 0x01cd,
+ 0x01cf, 0x01cf,
+ 0x01d1, 0x01d1,
+ 0x01d3, 0x01d3,
+ 0x01d5, 0x01d5,
+ 0x01d7, 0x01d7,
+ 0x01d9, 0x01d9,
+ 0x01db, 0x01db,
+ 0x01de, 0x01de,
+ 0x01e0, 0x01e0,
+ 0x01e2, 0x01e2,
+ 0x01e4, 0x01e4,
+ 0x01e6, 0x01e6,
+ 0x01e8, 0x01e8,
+ 0x01ea, 0x01ea,
+ 0x01ec, 0x01ec,
+ 0x01ee, 0x01ee,
+ 0x01f1, 0x01f1,
+ 0x01f4, 0x01f4,
+ 0x01f6, 0x01f8,
+ 0x01fa, 0x01fa,
+ 0x01fc, 0x01fc,
+ 0x01fe, 0x01fe,
+ 0x0200, 0x0200,
+ 0x0202, 0x0202,
+ 0x0204, 0x0204,
+ 0x0206, 0x0206,
+ 0x0208, 0x0208,
+ 0x020a, 0x020a,
+ 0x020c, 0x020c,
+ 0x020e, 0x020e,
+ 0x0210, 0x0210,
+ 0x0212, 0x0212,
+ 0x0214, 0x0214,
+ 0x0216, 0x0216,
+ 0x0218, 0x0218,
+ 0x021a, 0x021a,
+ 0x021c, 0x021c,
+ 0x021e, 0x021e,
+ 0x0220, 0x0220,
+ 0x0222, 0x0222,
+ 0x0224, 0x0224,
+ 0x0226, 0x0226,
+ 0x0228, 0x0228,
+ 0x022a, 0x022a,
+ 0x022c, 0x022c,
+ 0x022e, 0x022e,
+ 0x0230, 0x0230,
+ 0x0232, 0x0232,
+ 0x023a, 0x023b,
+ 0x023d, 0x023e,
+ 0x0241, 0x0241,
+ 0x0243, 0x0246,
+ 0x0248, 0x0248,
+ 0x024a, 0x024a,
+ 0x024c, 0x024c,
+ 0x024e, 0x024e,
+ 0x0370, 0x0370,
+ 0x0372, 0x0372,
+ 0x0376, 0x0376,
+ 0x037f, 0x037f,
+ 0x0386, 0x0386,
+ 0x0388, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x038f,
+ 0x0391, 0x03a1,
+ 0x03a3, 0x03ab,
+ 0x03cf, 0x03cf,
+ 0x03d2, 0x03d4,
+ 0x03d8, 0x03d8,
+ 0x03da, 0x03da,
+ 0x03dc, 0x03dc,
+ 0x03de, 0x03de,
+ 0x03e0, 0x03e0,
+ 0x03e2, 0x03e2,
+ 0x03e4, 0x03e4,
+ 0x03e6, 0x03e6,
+ 0x03e8, 0x03e8,
+ 0x03ea, 0x03ea,
+ 0x03ec, 0x03ec,
+ 0x03ee, 0x03ee,
+ 0x03f4, 0x03f4,
+ 0x03f7, 0x03f7,
+ 0x03f9, 0x03fa,
+ 0x03fd, 0x042f,
+ 0x0460, 0x0460,
+ 0x0462, 0x0462,
+ 0x0464, 0x0464,
+ 0x0466, 0x0466,
+ 0x0468, 0x0468,
+ 0x046a, 0x046a,
+ 0x046c, 0x046c,
+ 0x046e, 0x046e,
+ 0x0470, 0x0470,
+ 0x0472, 0x0472,
+ 0x0474, 0x0474,
+ 0x0476, 0x0476,
+ 0x0478, 0x0478,
+ 0x047a, 0x047a,
+ 0x047c, 0x047c,
+ 0x047e, 0x047e,
+ 0x0480, 0x0480,
+ 0x048a, 0x048a,
+ 0x048c, 0x048c,
+ 0x048e, 0x048e,
+ 0x0490, 0x0490,
+ 0x0492, 0x0492,
+ 0x0494, 0x0494,
+ 0x0496, 0x0496,
+ 0x0498, 0x0498,
+ 0x049a, 0x049a,
+ 0x049c, 0x049c,
+ 0x049e, 0x049e,
+ 0x04a0, 0x04a0,
+ 0x04a2, 0x04a2,
+ 0x04a4, 0x04a4,
+ 0x04a6, 0x04a6,
+ 0x04a8, 0x04a8,
+ 0x04aa, 0x04aa,
+ 0x04ac, 0x04ac,
+ 0x04ae, 0x04ae,
+ 0x04b0, 0x04b0,
+ 0x04b2, 0x04b2,
+ 0x04b4, 0x04b4,
+ 0x04b6, 0x04b6,
+ 0x04b8, 0x04b8,
+ 0x04ba, 0x04ba,
+ 0x04bc, 0x04bc,
+ 0x04be, 0x04be,
+ 0x04c0, 0x04c1,
+ 0x04c3, 0x04c3,
+ 0x04c5, 0x04c5,
+ 0x04c7, 0x04c7,
+ 0x04c9, 0x04c9,
+ 0x04cb, 0x04cb,
+ 0x04cd, 0x04cd,
+ 0x04d0, 0x04d0,
+ 0x04d2, 0x04d2,
+ 0x04d4, 0x04d4,
+ 0x04d6, 0x04d6,
+ 0x04d8, 0x04d8,
+ 0x04da, 0x04da,
+ 0x04dc, 0x04dc,
+ 0x04de, 0x04de,
+ 0x04e0, 0x04e0,
+ 0x04e2, 0x04e2,
+ 0x04e4, 0x04e4,
+ 0x04e6, 0x04e6,
+ 0x04e8, 0x04e8,
+ 0x04ea, 0x04ea,
+ 0x04ec, 0x04ec,
+ 0x04ee, 0x04ee,
+ 0x04f0, 0x04f0,
+ 0x04f2, 0x04f2,
+ 0x04f4, 0x04f4,
+ 0x04f6, 0x04f6,
+ 0x04f8, 0x04f8,
+ 0x04fa, 0x04fa,
+ 0x04fc, 0x04fc,
+ 0x04fe, 0x04fe,
+ 0x0500, 0x0500,
+ 0x0502, 0x0502,
+ 0x0504, 0x0504,
+ 0x0506, 0x0506,
+ 0x0508, 0x0508,
+ 0x050a, 0x050a,
+ 0x050c, 0x050c,
+ 0x050e, 0x050e,
+ 0x0510, 0x0510,
+ 0x0512, 0x0512,
+ 0x0514, 0x0514,
+ 0x0516, 0x0516,
+ 0x0518, 0x0518,
+ 0x051a, 0x051a,
+ 0x051c, 0x051c,
+ 0x051e, 0x051e,
+ 0x0520, 0x0520,
+ 0x0522, 0x0522,
+ 0x0524, 0x0524,
+ 0x0526, 0x0526,
+ 0x0528, 0x0528,
+ 0x052a, 0x052a,
+ 0x052c, 0x052c,
+ 0x052e, 0x052e,
+ 0x0531, 0x0556,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x13a0, 0x13f5,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x1e00, 0x1e00,
+ 0x1e02, 0x1e02,
+ 0x1e04, 0x1e04,
+ 0x1e06, 0x1e06,
+ 0x1e08, 0x1e08,
+ 0x1e0a, 0x1e0a,
+ 0x1e0c, 0x1e0c,
+ 0x1e0e, 0x1e0e,
+ 0x1e10, 0x1e10,
+ 0x1e12, 0x1e12,
+ 0x1e14, 0x1e14,
+ 0x1e16, 0x1e16,
+ 0x1e18, 0x1e18,
+ 0x1e1a, 0x1e1a,
+ 0x1e1c, 0x1e1c,
+ 0x1e1e, 0x1e1e,
+ 0x1e20, 0x1e20,
+ 0x1e22, 0x1e22,
+ 0x1e24, 0x1e24,
+ 0x1e26, 0x1e26,
+ 0x1e28, 0x1e28,
+ 0x1e2a, 0x1e2a,
+ 0x1e2c, 0x1e2c,
+ 0x1e2e, 0x1e2e,
+ 0x1e30, 0x1e30,
+ 0x1e32, 0x1e32,
+ 0x1e34, 0x1e34,
+ 0x1e36, 0x1e36,
+ 0x1e38, 0x1e38,
+ 0x1e3a, 0x1e3a,
+ 0x1e3c, 0x1e3c,
+ 0x1e3e, 0x1e3e,
+ 0x1e40, 0x1e40,
+ 0x1e42, 0x1e42,
+ 0x1e44, 0x1e44,
+ 0x1e46, 0x1e46,
+ 0x1e48, 0x1e48,
+ 0x1e4a, 0x1e4a,
+ 0x1e4c, 0x1e4c,
+ 0x1e4e, 0x1e4e,
+ 0x1e50, 0x1e50,
+ 0x1e52, 0x1e52,
+ 0x1e54, 0x1e54,
+ 0x1e56, 0x1e56,
+ 0x1e58, 0x1e58,
+ 0x1e5a, 0x1e5a,
+ 0x1e5c, 0x1e5c,
+ 0x1e5e, 0x1e5e,
+ 0x1e60, 0x1e60,
+ 0x1e62, 0x1e62,
+ 0x1e64, 0x1e64,
+ 0x1e66, 0x1e66,
+ 0x1e68, 0x1e68,
+ 0x1e6a, 0x1e6a,
+ 0x1e6c, 0x1e6c,
+ 0x1e6e, 0x1e6e,
+ 0x1e70, 0x1e70,
+ 0x1e72, 0x1e72,
+ 0x1e74, 0x1e74,
+ 0x1e76, 0x1e76,
+ 0x1e78, 0x1e78,
+ 0x1e7a, 0x1e7a,
+ 0x1e7c, 0x1e7c,
+ 0x1e7e, 0x1e7e,
+ 0x1e80, 0x1e80,
+ 0x1e82, 0x1e82,
+ 0x1e84, 0x1e84,
+ 0x1e86, 0x1e86,
+ 0x1e88, 0x1e88,
+ 0x1e8a, 0x1e8a,
+ 0x1e8c, 0x1e8c,
+ 0x1e8e, 0x1e8e,
+ 0x1e90, 0x1e90,
+ 0x1e92, 0x1e92,
+ 0x1e94, 0x1e94,
+ 0x1e9e, 0x1e9e,
+ 0x1ea0, 0x1ea0,
+ 0x1ea2, 0x1ea2,
+ 0x1ea4, 0x1ea4,
+ 0x1ea6, 0x1ea6,
+ 0x1ea8, 0x1ea8,
+ 0x1eaa, 0x1eaa,
+ 0x1eac, 0x1eac,
+ 0x1eae, 0x1eae,
+ 0x1eb0, 0x1eb0,
+ 0x1eb2, 0x1eb2,
+ 0x1eb4, 0x1eb4,
+ 0x1eb6, 0x1eb6,
+ 0x1eb8, 0x1eb8,
+ 0x1eba, 0x1eba,
+ 0x1ebc, 0x1ebc,
+ 0x1ebe, 0x1ebe,
+ 0x1ec0, 0x1ec0,
+ 0x1ec2, 0x1ec2,
+ 0x1ec4, 0x1ec4,
+ 0x1ec6, 0x1ec6,
+ 0x1ec8, 0x1ec8,
+ 0x1eca, 0x1eca,
+ 0x1ecc, 0x1ecc,
+ 0x1ece, 0x1ece,
+ 0x1ed0, 0x1ed0,
+ 0x1ed2, 0x1ed2,
+ 0x1ed4, 0x1ed4,
+ 0x1ed6, 0x1ed6,
+ 0x1ed8, 0x1ed8,
+ 0x1eda, 0x1eda,
+ 0x1edc, 0x1edc,
+ 0x1ede, 0x1ede,
+ 0x1ee0, 0x1ee0,
+ 0x1ee2, 0x1ee2,
+ 0x1ee4, 0x1ee4,
+ 0x1ee6, 0x1ee6,
+ 0x1ee8, 0x1ee8,
+ 0x1eea, 0x1eea,
+ 0x1eec, 0x1eec,
+ 0x1eee, 0x1eee,
+ 0x1ef0, 0x1ef0,
+ 0x1ef2, 0x1ef2,
+ 0x1ef4, 0x1ef4,
+ 0x1ef6, 0x1ef6,
+ 0x1ef8, 0x1ef8,
+ 0x1efa, 0x1efa,
+ 0x1efc, 0x1efc,
+ 0x1efe, 0x1efe,
+ 0x1f08, 0x1f0f,
+ 0x1f18, 0x1f1d,
+ 0x1f28, 0x1f2f,
+ 0x1f38, 0x1f3f,
+ 0x1f48, 0x1f4d,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f5f,
+ 0x1f68, 0x1f6f,
+ 0x1fb8, 0x1fbb,
+ 0x1fc8, 0x1fcb,
+ 0x1fd8, 0x1fdb,
+ 0x1fe8, 0x1fec,
+ 0x1ff8, 0x1ffb,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210b, 0x210d,
+ 0x2110, 0x2112,
+ 0x2115, 0x2115,
+ 0x2119, 0x211d,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212a, 0x212d,
+ 0x2130, 0x2133,
+ 0x213e, 0x213f,
+ 0x2145, 0x2145,
+ 0x2183, 0x2183,
+ 0x2c00, 0x2c2e,
+ 0x2c60, 0x2c60,
+ 0x2c62, 0x2c64,
+ 0x2c67, 0x2c67,
+ 0x2c69, 0x2c69,
+ 0x2c6b, 0x2c6b,
+ 0x2c6d, 0x2c70,
+ 0x2c72, 0x2c72,
+ 0x2c75, 0x2c75,
+ 0x2c7e, 0x2c80,
+ 0x2c82, 0x2c82,
+ 0x2c84, 0x2c84,
+ 0x2c86, 0x2c86,
+ 0x2c88, 0x2c88,
+ 0x2c8a, 0x2c8a,
+ 0x2c8c, 0x2c8c,
+ 0x2c8e, 0x2c8e,
+ 0x2c90, 0x2c90,
+ 0x2c92, 0x2c92,
+ 0x2c94, 0x2c94,
+ 0x2c96, 0x2c96,
+ 0x2c98, 0x2c98,
+ 0x2c9a, 0x2c9a,
+ 0x2c9c, 0x2c9c,
+ 0x2c9e, 0x2c9e,
+ 0x2ca0, 0x2ca0,
+ 0x2ca2, 0x2ca2,
+ 0x2ca4, 0x2ca4,
+ 0x2ca6, 0x2ca6,
+ 0x2ca8, 0x2ca8,
+ 0x2caa, 0x2caa,
+ 0x2cac, 0x2cac,
+ 0x2cae, 0x2cae,
+ 0x2cb0, 0x2cb0,
+ 0x2cb2, 0x2cb2,
+ 0x2cb4, 0x2cb4,
+ 0x2cb6, 0x2cb6,
+ 0x2cb8, 0x2cb8,
+ 0x2cba, 0x2cba,
+ 0x2cbc, 0x2cbc,
+ 0x2cbe, 0x2cbe,
+ 0x2cc0, 0x2cc0,
+ 0x2cc2, 0x2cc2,
+ 0x2cc4, 0x2cc4,
+ 0x2cc6, 0x2cc6,
+ 0x2cc8, 0x2cc8,
+ 0x2cca, 0x2cca,
+ 0x2ccc, 0x2ccc,
+ 0x2cce, 0x2cce,
+ 0x2cd0, 0x2cd0,
+ 0x2cd2, 0x2cd2,
+ 0x2cd4, 0x2cd4,
+ 0x2cd6, 0x2cd6,
+ 0x2cd8, 0x2cd8,
+ 0x2cda, 0x2cda,
+ 0x2cdc, 0x2cdc,
+ 0x2cde, 0x2cde,
+ 0x2ce0, 0x2ce0,
+ 0x2ce2, 0x2ce2,
+ 0x2ceb, 0x2ceb,
+ 0x2ced, 0x2ced,
+ 0x2cf2, 0x2cf2,
+ 0xa640, 0xa640,
+ 0xa642, 0xa642,
+ 0xa644, 0xa644,
+ 0xa646, 0xa646,
+ 0xa648, 0xa648,
+ 0xa64a, 0xa64a,
+ 0xa64c, 0xa64c,
+ 0xa64e, 0xa64e,
+ 0xa650, 0xa650,
+ 0xa652, 0xa652,
+ 0xa654, 0xa654,
+ 0xa656, 0xa656,
+ 0xa658, 0xa658,
+ 0xa65a, 0xa65a,
+ 0xa65c, 0xa65c,
+ 0xa65e, 0xa65e,
+ 0xa660, 0xa660,
+ 0xa662, 0xa662,
+ 0xa664, 0xa664,
+ 0xa666, 0xa666,
+ 0xa668, 0xa668,
+ 0xa66a, 0xa66a,
+ 0xa66c, 0xa66c,
+ 0xa680, 0xa680,
+ 0xa682, 0xa682,
+ 0xa684, 0xa684,
+ 0xa686, 0xa686,
+ 0xa688, 0xa688,
+ 0xa68a, 0xa68a,
+ 0xa68c, 0xa68c,
+ 0xa68e, 0xa68e,
+ 0xa690, 0xa690,
+ 0xa692, 0xa692,
+ 0xa694, 0xa694,
+ 0xa696, 0xa696,
+ 0xa698, 0xa698,
+ 0xa69a, 0xa69a,
+ 0xa722, 0xa722,
+ 0xa724, 0xa724,
+ 0xa726, 0xa726,
+ 0xa728, 0xa728,
+ 0xa72a, 0xa72a,
+ 0xa72c, 0xa72c,
+ 0xa72e, 0xa72e,
+ 0xa732, 0xa732,
+ 0xa734, 0xa734,
+ 0xa736, 0xa736,
+ 0xa738, 0xa738,
+ 0xa73a, 0xa73a,
+ 0xa73c, 0xa73c,
+ 0xa73e, 0xa73e,
+ 0xa740, 0xa740,
+ 0xa742, 0xa742,
+ 0xa744, 0xa744,
+ 0xa746, 0xa746,
+ 0xa748, 0xa748,
+ 0xa74a, 0xa74a,
+ 0xa74c, 0xa74c,
+ 0xa74e, 0xa74e,
+ 0xa750, 0xa750,
+ 0xa752, 0xa752,
+ 0xa754, 0xa754,
+ 0xa756, 0xa756,
+ 0xa758, 0xa758,
+ 0xa75a, 0xa75a,
+ 0xa75c, 0xa75c,
+ 0xa75e, 0xa75e,
+ 0xa760, 0xa760,
+ 0xa762, 0xa762,
+ 0xa764, 0xa764,
+ 0xa766, 0xa766,
+ 0xa768, 0xa768,
+ 0xa76a, 0xa76a,
+ 0xa76c, 0xa76c,
+ 0xa76e, 0xa76e,
+ 0xa779, 0xa779,
+ 0xa77b, 0xa77b,
+ 0xa77d, 0xa77e,
+ 0xa780, 0xa780,
+ 0xa782, 0xa782,
+ 0xa784, 0xa784,
+ 0xa786, 0xa786,
+ 0xa78b, 0xa78b,
+ 0xa78d, 0xa78d,
+ 0xa790, 0xa790,
+ 0xa792, 0xa792,
+ 0xa796, 0xa796,
+ 0xa798, 0xa798,
+ 0xa79a, 0xa79a,
+ 0xa79c, 0xa79c,
+ 0xa79e, 0xa79e,
+ 0xa7a0, 0xa7a0,
+ 0xa7a2, 0xa7a2,
+ 0xa7a4, 0xa7a4,
+ 0xa7a6, 0xa7a6,
+ 0xa7a8, 0xa7a8,
+ 0xa7aa, 0xa7ae,
+ 0xa7b0, 0xa7b4,
+ 0xa7b6, 0xa7b6,
+ 0xa7b8, 0xa7b8,
+ 0xa7ba, 0xa7ba,
+ 0xa7bc, 0xa7bc,
+ 0xa7be, 0xa7be,
+ 0xa7c2, 0xa7c2,
+ 0xa7c4, 0xa7c6,
+ 0xff21, 0xff3a,
+ 0x10400, 0x10427,
+ 0x104b0, 0x104d3,
+ 0x10c80, 0x10cb2,
+ 0x118a0, 0x118bf,
+ 0x16e40, 0x16e5f,
+ 0x1d400, 0x1d419,
+ 0x1d434, 0x1d44d,
+ 0x1d468, 0x1d481,
+ 0x1d49c, 0x1d49c,
+ 0x1d49e, 0x1d49f,
+ 0x1d4a2, 0x1d4a2,
+ 0x1d4a5, 0x1d4a6,
+ 0x1d4a9, 0x1d4ac,
+ 0x1d4ae, 0x1d4b5,
+ 0x1d4d0, 0x1d4e9,
+ 0x1d504, 0x1d505,
+ 0x1d507, 0x1d50a,
+ 0x1d50d, 0x1d514,
+ 0x1d516, 0x1d51c,
+ 0x1d538, 0x1d539,
+ 0x1d53b, 0x1d53e,
+ 0x1d540, 0x1d544,
+ 0x1d546, 0x1d546,
+ 0x1d54a, 0x1d550,
+ 0x1d56c, 0x1d585,
+ 0x1d5a0, 0x1d5b9,
+ 0x1d5d4, 0x1d5ed,
+ 0x1d608, 0x1d621,
+ 0x1d63c, 0x1d655,
+ 0x1d670, 0x1d689,
+ 0x1d6a8, 0x1d6c0,
+ 0x1d6e2, 0x1d6fa,
+ 0x1d71c, 0x1d734,
+ 0x1d756, 0x1d76e,
+ 0x1d790, 0x1d7a8,
+ 0x1d7ca, 0x1d7ca,
+ 0x1e900, 0x1e921,
+}; /* CR_Lu */
+
+/* 'M': Major Category */
+static const OnigCodePoint CR_M[] = {
+ 280,
+ 0x0300, 0x036f,
+ 0x0483, 0x0489,
+ 0x0591, 0x05bd,
+ 0x05bf, 0x05bf,
+ 0x05c1, 0x05c2,
+ 0x05c4, 0x05c5,
+ 0x05c7, 0x05c7,
+ 0x0610, 0x061a,
+ 0x064b, 0x065f,
+ 0x0670, 0x0670,
+ 0x06d6, 0x06dc,
+ 0x06df, 0x06e4,
+ 0x06e7, 0x06e8,
+ 0x06ea, 0x06ed,
+ 0x0711, 0x0711,
+ 0x0730, 0x074a,
+ 0x07a6, 0x07b0,
+ 0x07eb, 0x07f3,
+ 0x07fd, 0x07fd,
+ 0x0816, 0x0819,
+ 0x081b, 0x0823,
+ 0x0825, 0x0827,
+ 0x0829, 0x082d,
+ 0x0859, 0x085b,
+ 0x08d3, 0x08e1,
+ 0x08e3, 0x0903,
+ 0x093a, 0x093c,
+ 0x093e, 0x094f,
+ 0x0951, 0x0957,
+ 0x0962, 0x0963,
+ 0x0981, 0x0983,
+ 0x09bc, 0x09bc,
+ 0x09be, 0x09c4,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09cd,
+ 0x09d7, 0x09d7,
+ 0x09e2, 0x09e3,
+ 0x09fe, 0x09fe,
+ 0x0a01, 0x0a03,
+ 0x0a3c, 0x0a3c,
+ 0x0a3e, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a51, 0x0a51,
+ 0x0a70, 0x0a71,
+ 0x0a75, 0x0a75,
+ 0x0a81, 0x0a83,
+ 0x0abc, 0x0abc,
+ 0x0abe, 0x0ac5,
+ 0x0ac7, 0x0ac9,
+ 0x0acb, 0x0acd,
+ 0x0ae2, 0x0ae3,
+ 0x0afa, 0x0aff,
+ 0x0b01, 0x0b03,
+ 0x0b3c, 0x0b3c,
+ 0x0b3e, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b62, 0x0b63,
+ 0x0b82, 0x0b82,
+ 0x0bbe, 0x0bc2,
+ 0x0bc6, 0x0bc8,
+ 0x0bca, 0x0bcd,
+ 0x0bd7, 0x0bd7,
+ 0x0c00, 0x0c04,
+ 0x0c3e, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c62, 0x0c63,
+ 0x0c81, 0x0c83,
+ 0x0cbc, 0x0cbc,
+ 0x0cbe, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0ce2, 0x0ce3,
+ 0x0d00, 0x0d03,
+ 0x0d3b, 0x0d3c,
+ 0x0d3e, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4d,
+ 0x0d57, 0x0d57,
+ 0x0d62, 0x0d63,
+ 0x0d82, 0x0d83,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df3,
+ 0x0e31, 0x0e31,
+ 0x0e34, 0x0e3a,
+ 0x0e47, 0x0e4e,
+ 0x0eb1, 0x0eb1,
+ 0x0eb4, 0x0ebc,
+ 0x0ec8, 0x0ecd,
+ 0x0f18, 0x0f19,
+ 0x0f35, 0x0f35,
+ 0x0f37, 0x0f37,
+ 0x0f39, 0x0f39,
+ 0x0f3e, 0x0f3f,
+ 0x0f71, 0x0f84,
+ 0x0f86, 0x0f87,
+ 0x0f8d, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fc6, 0x0fc6,
+ 0x102b, 0x103e,
+ 0x1056, 0x1059,
+ 0x105e, 0x1060,
+ 0x1062, 0x1064,
+ 0x1067, 0x106d,
+ 0x1071, 0x1074,
+ 0x1082, 0x108d,
+ 0x108f, 0x108f,
+ 0x109a, 0x109d,
+ 0x135d, 0x135f,
+ 0x1712, 0x1714,
+ 0x1732, 0x1734,
+ 0x1752, 0x1753,
+ 0x1772, 0x1773,
+ 0x17b4, 0x17d3,
+ 0x17dd, 0x17dd,
+ 0x180b, 0x180d,
+ 0x1885, 0x1886,
+ 0x18a9, 0x18a9,
+ 0x1920, 0x192b,
+ 0x1930, 0x193b,
+ 0x1a17, 0x1a1b,
+ 0x1a55, 0x1a5e,
+ 0x1a60, 0x1a7c,
+ 0x1a7f, 0x1a7f,
+ 0x1ab0, 0x1abe,
+ 0x1b00, 0x1b04,
+ 0x1b34, 0x1b44,
+ 0x1b6b, 0x1b73,
+ 0x1b80, 0x1b82,
+ 0x1ba1, 0x1bad,
+ 0x1be6, 0x1bf3,
+ 0x1c24, 0x1c37,
+ 0x1cd0, 0x1cd2,
+ 0x1cd4, 0x1ce8,
+ 0x1ced, 0x1ced,
+ 0x1cf4, 0x1cf4,
+ 0x1cf7, 0x1cf9,
+ 0x1dc0, 0x1df9,
+ 0x1dfb, 0x1dff,
+ 0x20d0, 0x20f0,
+ 0x2cef, 0x2cf1,
+ 0x2d7f, 0x2d7f,
+ 0x2de0, 0x2dff,
+ 0x302a, 0x302f,
+ 0x3099, 0x309a,
+ 0xa66f, 0xa672,
+ 0xa674, 0xa67d,
+ 0xa69e, 0xa69f,
+ 0xa6f0, 0xa6f1,
+ 0xa802, 0xa802,
+ 0xa806, 0xa806,
+ 0xa80b, 0xa80b,
+ 0xa823, 0xa827,
+ 0xa880, 0xa881,
+ 0xa8b4, 0xa8c5,
+ 0xa8e0, 0xa8f1,
+ 0xa8ff, 0xa8ff,
+ 0xa926, 0xa92d,
+ 0xa947, 0xa953,
+ 0xa980, 0xa983,
+ 0xa9b3, 0xa9c0,
+ 0xa9e5, 0xa9e5,
+ 0xaa29, 0xaa36,
+ 0xaa43, 0xaa43,
+ 0xaa4c, 0xaa4d,
+ 0xaa7b, 0xaa7d,
+ 0xaab0, 0xaab0,
+ 0xaab2, 0xaab4,
+ 0xaab7, 0xaab8,
+ 0xaabe, 0xaabf,
+ 0xaac1, 0xaac1,
+ 0xaaeb, 0xaaef,
+ 0xaaf5, 0xaaf6,
+ 0xabe3, 0xabea,
+ 0xabec, 0xabed,
+ 0xfb1e, 0xfb1e,
+ 0xfe00, 0xfe0f,
+ 0xfe20, 0xfe2f,
+ 0x101fd, 0x101fd,
+ 0x102e0, 0x102e0,
+ 0x10376, 0x1037a,
+ 0x10a01, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a0f,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a3f,
+ 0x10ae5, 0x10ae6,
+ 0x10d24, 0x10d27,
+ 0x10f46, 0x10f50,
+ 0x11000, 0x11002,
+ 0x11038, 0x11046,
+ 0x1107f, 0x11082,
+ 0x110b0, 0x110ba,
+ 0x11100, 0x11102,
+ 0x11127, 0x11134,
+ 0x11145, 0x11146,
+ 0x11173, 0x11173,
+ 0x11180, 0x11182,
+ 0x111b3, 0x111c0,
+ 0x111c9, 0x111cc,
+ 0x1122c, 0x11237,
+ 0x1123e, 0x1123e,
+ 0x112df, 0x112ea,
+ 0x11300, 0x11303,
+ 0x1133b, 0x1133c,
+ 0x1133e, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134b, 0x1134d,
+ 0x11357, 0x11357,
+ 0x11362, 0x11363,
+ 0x11366, 0x1136c,
+ 0x11370, 0x11374,
+ 0x11435, 0x11446,
+ 0x1145e, 0x1145e,
+ 0x114b0, 0x114c3,
+ 0x115af, 0x115b5,
+ 0x115b8, 0x115c0,
+ 0x115dc, 0x115dd,
+ 0x11630, 0x11640,
+ 0x116ab, 0x116b7,
+ 0x1171d, 0x1172b,
+ 0x1182c, 0x1183a,
+ 0x119d1, 0x119d7,
+ 0x119da, 0x119e0,
+ 0x119e4, 0x119e4,
+ 0x11a01, 0x11a0a,
+ 0x11a33, 0x11a39,
+ 0x11a3b, 0x11a3e,
+ 0x11a47, 0x11a47,
+ 0x11a51, 0x11a5b,
+ 0x11a8a, 0x11a99,
+ 0x11c2f, 0x11c36,
+ 0x11c38, 0x11c3f,
+ 0x11c92, 0x11ca7,
+ 0x11ca9, 0x11cb6,
+ 0x11d31, 0x11d36,
+ 0x11d3a, 0x11d3a,
+ 0x11d3c, 0x11d3d,
+ 0x11d3f, 0x11d45,
+ 0x11d47, 0x11d47,
+ 0x11d8a, 0x11d8e,
+ 0x11d90, 0x11d91,
+ 0x11d93, 0x11d97,
+ 0x11ef3, 0x11ef6,
+ 0x16af0, 0x16af4,
+ 0x16b30, 0x16b36,
+ 0x16f4f, 0x16f4f,
+ 0x16f51, 0x16f87,
+ 0x16f8f, 0x16f92,
+ 0x1bc9d, 0x1bc9e,
+ 0x1d165, 0x1d169,
+ 0x1d16d, 0x1d172,
+ 0x1d17b, 0x1d182,
+ 0x1d185, 0x1d18b,
+ 0x1d1aa, 0x1d1ad,
+ 0x1d242, 0x1d244,
+ 0x1da00, 0x1da36,
+ 0x1da3b, 0x1da6c,
+ 0x1da75, 0x1da75,
+ 0x1da84, 0x1da84,
+ 0x1da9b, 0x1da9f,
+ 0x1daa1, 0x1daaf,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e130, 0x1e136,
+ 0x1e2ec, 0x1e2ef,
+ 0x1e8d0, 0x1e8d6,
+ 0x1e944, 0x1e94a,
+ 0xe0100, 0xe01ef,
+}; /* CR_M */
+
+/* 'Mc': General Category */
+static const OnigCodePoint CR_Mc[] = {
+ 168,
+ 0x0903, 0x0903,
+ 0x093b, 0x093b,
+ 0x093e, 0x0940,
+ 0x0949, 0x094c,
+ 0x094e, 0x094f,
+ 0x0982, 0x0983,
+ 0x09be, 0x09c0,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09cc,
+ 0x09d7, 0x09d7,
+ 0x0a03, 0x0a03,
+ 0x0a3e, 0x0a40,
+ 0x0a83, 0x0a83,
+ 0x0abe, 0x0ac0,
+ 0x0ac9, 0x0ac9,
+ 0x0acb, 0x0acc,
+ 0x0b02, 0x0b03,
+ 0x0b3e, 0x0b3e,
+ 0x0b40, 0x0b40,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4c,
+ 0x0b57, 0x0b57,
+ 0x0bbe, 0x0bbf,
+ 0x0bc1, 0x0bc2,
+ 0x0bc6, 0x0bc8,
+ 0x0bca, 0x0bcc,
+ 0x0bd7, 0x0bd7,
+ 0x0c01, 0x0c03,
+ 0x0c41, 0x0c44,
+ 0x0c82, 0x0c83,
+ 0x0cbe, 0x0cbe,
+ 0x0cc0, 0x0cc4,
+ 0x0cc7, 0x0cc8,
+ 0x0cca, 0x0ccb,
+ 0x0cd5, 0x0cd6,
+ 0x0d02, 0x0d03,
+ 0x0d3e, 0x0d40,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4c,
+ 0x0d57, 0x0d57,
+ 0x0d82, 0x0d83,
+ 0x0dcf, 0x0dd1,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df3,
+ 0x0f3e, 0x0f3f,
+ 0x0f7f, 0x0f7f,
+ 0x102b, 0x102c,
+ 0x1031, 0x1031,
+ 0x1038, 0x1038,
+ 0x103b, 0x103c,
+ 0x1056, 0x1057,
+ 0x1062, 0x1064,
+ 0x1067, 0x106d,
+ 0x1083, 0x1084,
+ 0x1087, 0x108c,
+ 0x108f, 0x108f,
+ 0x109a, 0x109c,
+ 0x17b6, 0x17b6,
+ 0x17be, 0x17c5,
+ 0x17c7, 0x17c8,
+ 0x1923, 0x1926,
+ 0x1929, 0x192b,
+ 0x1930, 0x1931,
+ 0x1933, 0x1938,
+ 0x1a19, 0x1a1a,
+ 0x1a55, 0x1a55,
+ 0x1a57, 0x1a57,
+ 0x1a61, 0x1a61,
+ 0x1a63, 0x1a64,
+ 0x1a6d, 0x1a72,
+ 0x1b04, 0x1b04,
+ 0x1b35, 0x1b35,
+ 0x1b3b, 0x1b3b,
+ 0x1b3d, 0x1b41,
+ 0x1b43, 0x1b44,
+ 0x1b82, 0x1b82,
+ 0x1ba1, 0x1ba1,
+ 0x1ba6, 0x1ba7,
+ 0x1baa, 0x1baa,
+ 0x1be7, 0x1be7,
+ 0x1bea, 0x1bec,
+ 0x1bee, 0x1bee,
+ 0x1bf2, 0x1bf3,
+ 0x1c24, 0x1c2b,
+ 0x1c34, 0x1c35,
+ 0x1ce1, 0x1ce1,
+ 0x1cf7, 0x1cf7,
+ 0x302e, 0x302f,
+ 0xa823, 0xa824,
+ 0xa827, 0xa827,
+ 0xa880, 0xa881,
+ 0xa8b4, 0xa8c3,
+ 0xa952, 0xa953,
+ 0xa983, 0xa983,
+ 0xa9b4, 0xa9b5,
+ 0xa9ba, 0xa9bb,
+ 0xa9be, 0xa9c0,
+ 0xaa2f, 0xaa30,
+ 0xaa33, 0xaa34,
+ 0xaa4d, 0xaa4d,
+ 0xaa7b, 0xaa7b,
+ 0xaa7d, 0xaa7d,
+ 0xaaeb, 0xaaeb,
+ 0xaaee, 0xaaef,
+ 0xaaf5, 0xaaf5,
+ 0xabe3, 0xabe4,
+ 0xabe6, 0xabe7,
+ 0xabe9, 0xabea,
+ 0xabec, 0xabec,
+ 0x11000, 0x11000,
+ 0x11002, 0x11002,
+ 0x11082, 0x11082,
+ 0x110b0, 0x110b2,
+ 0x110b7, 0x110b8,
+ 0x1112c, 0x1112c,
+ 0x11145, 0x11146,
+ 0x11182, 0x11182,
+ 0x111b3, 0x111b5,
+ 0x111bf, 0x111c0,
+ 0x1122c, 0x1122e,
+ 0x11232, 0x11233,
+ 0x11235, 0x11235,
+ 0x112e0, 0x112e2,
+ 0x11302, 0x11303,
+ 0x1133e, 0x1133f,
+ 0x11341, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134b, 0x1134d,
+ 0x11357, 0x11357,
+ 0x11362, 0x11363,
+ 0x11435, 0x11437,
+ 0x11440, 0x11441,
+ 0x11445, 0x11445,
+ 0x114b0, 0x114b2,
+ 0x114b9, 0x114b9,
+ 0x114bb, 0x114be,
+ 0x114c1, 0x114c1,
+ 0x115af, 0x115b1,
+ 0x115b8, 0x115bb,
+ 0x115be, 0x115be,
+ 0x11630, 0x11632,
+ 0x1163b, 0x1163c,
+ 0x1163e, 0x1163e,
+ 0x116ac, 0x116ac,
+ 0x116ae, 0x116af,
+ 0x116b6, 0x116b6,
+ 0x11720, 0x11721,
+ 0x11726, 0x11726,
+ 0x1182c, 0x1182e,
+ 0x11838, 0x11838,
+ 0x119d1, 0x119d3,
+ 0x119dc, 0x119df,
+ 0x119e4, 0x119e4,
+ 0x11a39, 0x11a39,
+ 0x11a57, 0x11a58,
+ 0x11a97, 0x11a97,
+ 0x11c2f, 0x11c2f,
+ 0x11c3e, 0x11c3e,
+ 0x11ca9, 0x11ca9,
+ 0x11cb1, 0x11cb1,
+ 0x11cb4, 0x11cb4,
+ 0x11d8a, 0x11d8e,
+ 0x11d93, 0x11d94,
+ 0x11d96, 0x11d96,
+ 0x11ef5, 0x11ef6,
+ 0x16f51, 0x16f87,
+ 0x1d165, 0x1d166,
+ 0x1d16d, 0x1d172,
+}; /* CR_Mc */
+
+/* 'Me': General Category */
+static const OnigCodePoint CR_Me[] = {
+ 5,
+ 0x0488, 0x0489,
+ 0x1abe, 0x1abe,
+ 0x20dd, 0x20e0,
+ 0x20e2, 0x20e4,
+ 0xa670, 0xa672,
+}; /* CR_Me */
+
+/* 'Mn': General Category */
+static const OnigCodePoint CR_Mn[] = {
+ 318,
+ 0x0300, 0x036f,
+ 0x0483, 0x0487,
+ 0x0591, 0x05bd,
+ 0x05bf, 0x05bf,
+ 0x05c1, 0x05c2,
+ 0x05c4, 0x05c5,
+ 0x05c7, 0x05c7,
+ 0x0610, 0x061a,
+ 0x064b, 0x065f,
+ 0x0670, 0x0670,
+ 0x06d6, 0x06dc,
+ 0x06df, 0x06e4,
+ 0x06e7, 0x06e8,
+ 0x06ea, 0x06ed,
+ 0x0711, 0x0711,
+ 0x0730, 0x074a,
+ 0x07a6, 0x07b0,
+ 0x07eb, 0x07f3,
+ 0x07fd, 0x07fd,
+ 0x0816, 0x0819,
+ 0x081b, 0x0823,
+ 0x0825, 0x0827,
+ 0x0829, 0x082d,
+ 0x0859, 0x085b,
+ 0x08d3, 0x08e1,
+ 0x08e3, 0x0902,
+ 0x093a, 0x093a,
+ 0x093c, 0x093c,
+ 0x0941, 0x0948,
+ 0x094d, 0x094d,
+ 0x0951, 0x0957,
+ 0x0962, 0x0963,
+ 0x0981, 0x0981,
+ 0x09bc, 0x09bc,
+ 0x09c1, 0x09c4,
+ 0x09cd, 0x09cd,
+ 0x09e2, 0x09e3,
+ 0x09fe, 0x09fe,
+ 0x0a01, 0x0a02,
+ 0x0a3c, 0x0a3c,
+ 0x0a41, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a51, 0x0a51,
+ 0x0a70, 0x0a71,
+ 0x0a75, 0x0a75,
+ 0x0a81, 0x0a82,
+ 0x0abc, 0x0abc,
+ 0x0ac1, 0x0ac5,
+ 0x0ac7, 0x0ac8,
+ 0x0acd, 0x0acd,
+ 0x0ae2, 0x0ae3,
+ 0x0afa, 0x0aff,
+ 0x0b01, 0x0b01,
+ 0x0b3c, 0x0b3c,
+ 0x0b3f, 0x0b3f,
+ 0x0b41, 0x0b44,
+ 0x0b4d, 0x0b4d,
+ 0x0b56, 0x0b56,
+ 0x0b62, 0x0b63,
+ 0x0b82, 0x0b82,
+ 0x0bc0, 0x0bc0,
+ 0x0bcd, 0x0bcd,
+ 0x0c00, 0x0c00,
+ 0x0c04, 0x0c04,
+ 0x0c3e, 0x0c40,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c62, 0x0c63,
+ 0x0c81, 0x0c81,
+ 0x0cbc, 0x0cbc,
+ 0x0cbf, 0x0cbf,
+ 0x0cc6, 0x0cc6,
+ 0x0ccc, 0x0ccd,
+ 0x0ce2, 0x0ce3,
+ 0x0d00, 0x0d01,
+ 0x0d3b, 0x0d3c,
+ 0x0d41, 0x0d44,
+ 0x0d4d, 0x0d4d,
+ 0x0d62, 0x0d63,
+ 0x0dca, 0x0dca,
+ 0x0dd2, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0e31, 0x0e31,
+ 0x0e34, 0x0e3a,
+ 0x0e47, 0x0e4e,
+ 0x0eb1, 0x0eb1,
+ 0x0eb4, 0x0ebc,
+ 0x0ec8, 0x0ecd,
+ 0x0f18, 0x0f19,
+ 0x0f35, 0x0f35,
+ 0x0f37, 0x0f37,
+ 0x0f39, 0x0f39,
+ 0x0f71, 0x0f7e,
+ 0x0f80, 0x0f84,
+ 0x0f86, 0x0f87,
+ 0x0f8d, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fc6, 0x0fc6,
+ 0x102d, 0x1030,
+ 0x1032, 0x1037,
+ 0x1039, 0x103a,
+ 0x103d, 0x103e,
+ 0x1058, 0x1059,
+ 0x105e, 0x1060,
+ 0x1071, 0x1074,
+ 0x1082, 0x1082,
+ 0x1085, 0x1086,
+ 0x108d, 0x108d,
+ 0x109d, 0x109d,
+ 0x135d, 0x135f,
+ 0x1712, 0x1714,
+ 0x1732, 0x1734,
+ 0x1752, 0x1753,
+ 0x1772, 0x1773,
+ 0x17b4, 0x17b5,
+ 0x17b7, 0x17bd,
+ 0x17c6, 0x17c6,
+ 0x17c9, 0x17d3,
+ 0x17dd, 0x17dd,
+ 0x180b, 0x180d,
+ 0x1885, 0x1886,
+ 0x18a9, 0x18a9,
+ 0x1920, 0x1922,
+ 0x1927, 0x1928,
+ 0x1932, 0x1932,
+ 0x1939, 0x193b,
+ 0x1a17, 0x1a18,
+ 0x1a1b, 0x1a1b,
+ 0x1a56, 0x1a56,
+ 0x1a58, 0x1a5e,
+ 0x1a60, 0x1a60,
+ 0x1a62, 0x1a62,
+ 0x1a65, 0x1a6c,
+ 0x1a73, 0x1a7c,
+ 0x1a7f, 0x1a7f,
+ 0x1ab0, 0x1abd,
+ 0x1b00, 0x1b03,
+ 0x1b34, 0x1b34,
+ 0x1b36, 0x1b3a,
+ 0x1b3c, 0x1b3c,
+ 0x1b42, 0x1b42,
+ 0x1b6b, 0x1b73,
+ 0x1b80, 0x1b81,
+ 0x1ba2, 0x1ba5,
+ 0x1ba8, 0x1ba9,
+ 0x1bab, 0x1bad,
+ 0x1be6, 0x1be6,
+ 0x1be8, 0x1be9,
+ 0x1bed, 0x1bed,
+ 0x1bef, 0x1bf1,
+ 0x1c2c, 0x1c33,
+ 0x1c36, 0x1c37,
+ 0x1cd0, 0x1cd2,
+ 0x1cd4, 0x1ce0,
+ 0x1ce2, 0x1ce8,
+ 0x1ced, 0x1ced,
+ 0x1cf4, 0x1cf4,
+ 0x1cf8, 0x1cf9,
+ 0x1dc0, 0x1df9,
+ 0x1dfb, 0x1dff,
+ 0x20d0, 0x20dc,
+ 0x20e1, 0x20e1,
+ 0x20e5, 0x20f0,
+ 0x2cef, 0x2cf1,
+ 0x2d7f, 0x2d7f,
+ 0x2de0, 0x2dff,
+ 0x302a, 0x302d,
+ 0x3099, 0x309a,
+ 0xa66f, 0xa66f,
+ 0xa674, 0xa67d,
+ 0xa69e, 0xa69f,
+ 0xa6f0, 0xa6f1,
+ 0xa802, 0xa802,
+ 0xa806, 0xa806,
+ 0xa80b, 0xa80b,
+ 0xa825, 0xa826,
+ 0xa8c4, 0xa8c5,
+ 0xa8e0, 0xa8f1,
+ 0xa8ff, 0xa8ff,
+ 0xa926, 0xa92d,
+ 0xa947, 0xa951,
+ 0xa980, 0xa982,
+ 0xa9b3, 0xa9b3,
+ 0xa9b6, 0xa9b9,
+ 0xa9bc, 0xa9bd,
+ 0xa9e5, 0xa9e5,
+ 0xaa29, 0xaa2e,
+ 0xaa31, 0xaa32,
+ 0xaa35, 0xaa36,
+ 0xaa43, 0xaa43,
+ 0xaa4c, 0xaa4c,
+ 0xaa7c, 0xaa7c,
+ 0xaab0, 0xaab0,
+ 0xaab2, 0xaab4,
+ 0xaab7, 0xaab8,
+ 0xaabe, 0xaabf,
+ 0xaac1, 0xaac1,
+ 0xaaec, 0xaaed,
+ 0xaaf6, 0xaaf6,
+ 0xabe5, 0xabe5,
+ 0xabe8, 0xabe8,
+ 0xabed, 0xabed,
+ 0xfb1e, 0xfb1e,
+ 0xfe00, 0xfe0f,
+ 0xfe20, 0xfe2f,
+ 0x101fd, 0x101fd,
+ 0x102e0, 0x102e0,
+ 0x10376, 0x1037a,
+ 0x10a01, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a0f,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a3f,
+ 0x10ae5, 0x10ae6,
+ 0x10d24, 0x10d27,
+ 0x10f46, 0x10f50,
+ 0x11001, 0x11001,
+ 0x11038, 0x11046,
+ 0x1107f, 0x11081,
+ 0x110b3, 0x110b6,
+ 0x110b9, 0x110ba,
+ 0x11100, 0x11102,
+ 0x11127, 0x1112b,
+ 0x1112d, 0x11134,
+ 0x11173, 0x11173,
+ 0x11180, 0x11181,
+ 0x111b6, 0x111be,
+ 0x111c9, 0x111cc,
+ 0x1122f, 0x11231,
+ 0x11234, 0x11234,
+ 0x11236, 0x11237,
+ 0x1123e, 0x1123e,
+ 0x112df, 0x112df,
+ 0x112e3, 0x112ea,
+ 0x11300, 0x11301,
+ 0x1133b, 0x1133c,
+ 0x11340, 0x11340,
+ 0x11366, 0x1136c,
+ 0x11370, 0x11374,
+ 0x11438, 0x1143f,
+ 0x11442, 0x11444,
+ 0x11446, 0x11446,
+ 0x1145e, 0x1145e,
+ 0x114b3, 0x114b8,
+ 0x114ba, 0x114ba,
+ 0x114bf, 0x114c0,
+ 0x114c2, 0x114c3,
+ 0x115b2, 0x115b5,
+ 0x115bc, 0x115bd,
+ 0x115bf, 0x115c0,
+ 0x115dc, 0x115dd,
+ 0x11633, 0x1163a,
+ 0x1163d, 0x1163d,
+ 0x1163f, 0x11640,
+ 0x116ab, 0x116ab,
+ 0x116ad, 0x116ad,
+ 0x116b0, 0x116b5,
+ 0x116b7, 0x116b7,
+ 0x1171d, 0x1171f,
+ 0x11722, 0x11725,
+ 0x11727, 0x1172b,
+ 0x1182f, 0x11837,
+ 0x11839, 0x1183a,
+ 0x119d4, 0x119d7,
+ 0x119da, 0x119db,
+ 0x119e0, 0x119e0,
+ 0x11a01, 0x11a0a,
+ 0x11a33, 0x11a38,
+ 0x11a3b, 0x11a3e,
+ 0x11a47, 0x11a47,
+ 0x11a51, 0x11a56,
+ 0x11a59, 0x11a5b,
+ 0x11a8a, 0x11a96,
+ 0x11a98, 0x11a99,
+ 0x11c30, 0x11c36,
+ 0x11c38, 0x11c3d,
+ 0x11c3f, 0x11c3f,
+ 0x11c92, 0x11ca7,
+ 0x11caa, 0x11cb0,
+ 0x11cb2, 0x11cb3,
+ 0x11cb5, 0x11cb6,
+ 0x11d31, 0x11d36,
+ 0x11d3a, 0x11d3a,
+ 0x11d3c, 0x11d3d,
+ 0x11d3f, 0x11d45,
+ 0x11d47, 0x11d47,
+ 0x11d90, 0x11d91,
+ 0x11d95, 0x11d95,
+ 0x11d97, 0x11d97,
+ 0x11ef3, 0x11ef4,
+ 0x16af0, 0x16af4,
+ 0x16b30, 0x16b36,
+ 0x16f4f, 0x16f4f,
+ 0x16f8f, 0x16f92,
+ 0x1bc9d, 0x1bc9e,
+ 0x1d167, 0x1d169,
+ 0x1d17b, 0x1d182,
+ 0x1d185, 0x1d18b,
+ 0x1d1aa, 0x1d1ad,
+ 0x1d242, 0x1d244,
+ 0x1da00, 0x1da36,
+ 0x1da3b, 0x1da6c,
+ 0x1da75, 0x1da75,
+ 0x1da84, 0x1da84,
+ 0x1da9b, 0x1da9f,
+ 0x1daa1, 0x1daaf,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e130, 0x1e136,
+ 0x1e2ec, 0x1e2ef,
+ 0x1e8d0, 0x1e8d6,
+ 0x1e944, 0x1e94a,
+ 0xe0100, 0xe01ef,
+}; /* CR_Mn */
+
+/* 'N': Major Category */
+static const OnigCodePoint CR_N[] = {
+ 130,
+ 0x0030, 0x0039,
+ 0x00b2, 0x00b3,
+ 0x00b9, 0x00b9,
+ 0x00bc, 0x00be,
+ 0x0660, 0x0669,
+ 0x06f0, 0x06f9,
+ 0x07c0, 0x07c9,
+ 0x0966, 0x096f,
+ 0x09e6, 0x09ef,
+ 0x09f4, 0x09f9,
+ 0x0a66, 0x0a6f,
+ 0x0ae6, 0x0aef,
+ 0x0b66, 0x0b6f,
+ 0x0b72, 0x0b77,
+ 0x0be6, 0x0bf2,
+ 0x0c66, 0x0c6f,
+ 0x0c78, 0x0c7e,
+ 0x0ce6, 0x0cef,
+ 0x0d58, 0x0d5e,
+ 0x0d66, 0x0d78,
+ 0x0de6, 0x0def,
+ 0x0e50, 0x0e59,
+ 0x0ed0, 0x0ed9,
+ 0x0f20, 0x0f33,
+ 0x1040, 0x1049,
+ 0x1090, 0x1099,
+ 0x1369, 0x137c,
+ 0x16ee, 0x16f0,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1810, 0x1819,
+ 0x1946, 0x194f,
+ 0x19d0, 0x19da,
+ 0x1a80, 0x1a89,
+ 0x1a90, 0x1a99,
+ 0x1b50, 0x1b59,
+ 0x1bb0, 0x1bb9,
+ 0x1c40, 0x1c49,
+ 0x1c50, 0x1c59,
+ 0x2070, 0x2070,
+ 0x2074, 0x2079,
+ 0x2080, 0x2089,
+ 0x2150, 0x2182,
+ 0x2185, 0x2189,
+ 0x2460, 0x249b,
+ 0x24ea, 0x24ff,
+ 0x2776, 0x2793,
+ 0x2cfd, 0x2cfd,
+ 0x3007, 0x3007,
+ 0x3021, 0x3029,
+ 0x3038, 0x303a,
+ 0x3192, 0x3195,
+ 0x3220, 0x3229,
+ 0x3248, 0x324f,
+ 0x3251, 0x325f,
+ 0x3280, 0x3289,
+ 0x32b1, 0x32bf,
+ 0xa620, 0xa629,
+ 0xa6e6, 0xa6ef,
+ 0xa830, 0xa835,
+ 0xa8d0, 0xa8d9,
+ 0xa900, 0xa909,
+ 0xa9d0, 0xa9d9,
+ 0xa9f0, 0xa9f9,
+ 0xaa50, 0xaa59,
+ 0xabf0, 0xabf9,
+ 0xff10, 0xff19,
+ 0x10107, 0x10133,
+ 0x10140, 0x10178,
+ 0x1018a, 0x1018b,
+ 0x102e1, 0x102fb,
+ 0x10320, 0x10323,
+ 0x10341, 0x10341,
+ 0x1034a, 0x1034a,
+ 0x103d1, 0x103d5,
+ 0x104a0, 0x104a9,
+ 0x10858, 0x1085f,
+ 0x10879, 0x1087f,
+ 0x108a7, 0x108af,
+ 0x108fb, 0x108ff,
+ 0x10916, 0x1091b,
+ 0x109bc, 0x109bd,
+ 0x109c0, 0x109cf,
+ 0x109d2, 0x109ff,
+ 0x10a40, 0x10a48,
+ 0x10a7d, 0x10a7e,
+ 0x10a9d, 0x10a9f,
+ 0x10aeb, 0x10aef,
+ 0x10b58, 0x10b5f,
+ 0x10b78, 0x10b7f,
+ 0x10ba9, 0x10baf,
+ 0x10cfa, 0x10cff,
+ 0x10d30, 0x10d39,
+ 0x10e60, 0x10e7e,
+ 0x10f1d, 0x10f26,
+ 0x10f51, 0x10f54,
+ 0x11052, 0x1106f,
+ 0x110f0, 0x110f9,
+ 0x11136, 0x1113f,
+ 0x111d0, 0x111d9,
+ 0x111e1, 0x111f4,
+ 0x112f0, 0x112f9,
+ 0x11450, 0x11459,
+ 0x114d0, 0x114d9,
+ 0x11650, 0x11659,
+ 0x116c0, 0x116c9,
+ 0x11730, 0x1173b,
+ 0x118e0, 0x118f2,
+ 0x11c50, 0x11c6c,
+ 0x11d50, 0x11d59,
+ 0x11da0, 0x11da9,
+ 0x11fc0, 0x11fd4,
+ 0x12400, 0x1246e,
+ 0x16a60, 0x16a69,
+ 0x16b50, 0x16b59,
+ 0x16b5b, 0x16b61,
+ 0x16e80, 0x16e96,
+ 0x1d2e0, 0x1d2f3,
+ 0x1d360, 0x1d378,
+ 0x1d7ce, 0x1d7ff,
+ 0x1e140, 0x1e149,
+ 0x1e2f0, 0x1e2f9,
+ 0x1e8c7, 0x1e8cf,
+ 0x1e950, 0x1e959,
+ 0x1ec71, 0x1ecab,
+ 0x1ecad, 0x1ecaf,
+ 0x1ecb1, 0x1ecb4,
+ 0x1ed01, 0x1ed2d,
+ 0x1ed2f, 0x1ed3d,
+ 0x1f100, 0x1f10c,
+}; /* CR_N */
+
+/* 'Nd': General Category */
+#define CR_Nd CR_Digit
+
+/* 'Nl': General Category */
+static const OnigCodePoint CR_Nl[] = {
+ 12,
+ 0x16ee, 0x16f0,
+ 0x2160, 0x2182,
+ 0x2185, 0x2188,
+ 0x3007, 0x3007,
+ 0x3021, 0x3029,
+ 0x3038, 0x303a,
+ 0xa6e6, 0xa6ef,
+ 0x10140, 0x10174,
+ 0x10341, 0x10341,
+ 0x1034a, 0x1034a,
+ 0x103d1, 0x103d5,
+ 0x12400, 0x1246e,
+}; /* CR_Nl */
+
+/* 'No': General Category */
+static const OnigCodePoint CR_No[] = {
+ 70,
+ 0x00b2, 0x00b3,
+ 0x00b9, 0x00b9,
+ 0x00bc, 0x00be,
+ 0x09f4, 0x09f9,
+ 0x0b72, 0x0b77,
+ 0x0bf0, 0x0bf2,
+ 0x0c78, 0x0c7e,
+ 0x0d58, 0x0d5e,
+ 0x0d70, 0x0d78,
+ 0x0f2a, 0x0f33,
+ 0x1369, 0x137c,
+ 0x17f0, 0x17f9,
+ 0x19da, 0x19da,
+ 0x2070, 0x2070,
+ 0x2074, 0x2079,
+ 0x2080, 0x2089,
+ 0x2150, 0x215f,
+ 0x2189, 0x2189,
+ 0x2460, 0x249b,
+ 0x24ea, 0x24ff,
+ 0x2776, 0x2793,
+ 0x2cfd, 0x2cfd,
+ 0x3192, 0x3195,
+ 0x3220, 0x3229,
+ 0x3248, 0x324f,
+ 0x3251, 0x325f,
+ 0x3280, 0x3289,
+ 0x32b1, 0x32bf,
+ 0xa830, 0xa835,
+ 0x10107, 0x10133,
+ 0x10175, 0x10178,
+ 0x1018a, 0x1018b,
+ 0x102e1, 0x102fb,
+ 0x10320, 0x10323,
+ 0x10858, 0x1085f,
+ 0x10879, 0x1087f,
+ 0x108a7, 0x108af,
+ 0x108fb, 0x108ff,
+ 0x10916, 0x1091b,
+ 0x109bc, 0x109bd,
+ 0x109c0, 0x109cf,
+ 0x109d2, 0x109ff,
+ 0x10a40, 0x10a48,
+ 0x10a7d, 0x10a7e,
+ 0x10a9d, 0x10a9f,
+ 0x10aeb, 0x10aef,
+ 0x10b58, 0x10b5f,
+ 0x10b78, 0x10b7f,
+ 0x10ba9, 0x10baf,
+ 0x10cfa, 0x10cff,
+ 0x10e60, 0x10e7e,
+ 0x10f1d, 0x10f26,
+ 0x10f51, 0x10f54,
+ 0x11052, 0x11065,
+ 0x111e1, 0x111f4,
+ 0x1173a, 0x1173b,
+ 0x118ea, 0x118f2,
+ 0x11c5a, 0x11c6c,
+ 0x11fc0, 0x11fd4,
+ 0x16b5b, 0x16b61,
+ 0x16e80, 0x16e96,
+ 0x1d2e0, 0x1d2f3,
+ 0x1d360, 0x1d378,
+ 0x1e8c7, 0x1e8cf,
+ 0x1ec71, 0x1ecab,
+ 0x1ecad, 0x1ecaf,
+ 0x1ecb1, 0x1ecb4,
+ 0x1ed01, 0x1ed2d,
+ 0x1ed2f, 0x1ed3d,
+ 0x1f100, 0x1f10c,
+}; /* CR_No */
+
+/* 'P': Major Category */
+#define CR_P CR_Punct
+
+/* 'Pc': General Category */
+static const OnigCodePoint CR_Pc[] = {
+ 6,
+ 0x005f, 0x005f,
+ 0x203f, 0x2040,
+ 0x2054, 0x2054,
+ 0xfe33, 0xfe34,
+ 0xfe4d, 0xfe4f,
+ 0xff3f, 0xff3f,
+}; /* CR_Pc */
+
+/* 'Pd': General Category */
+static const OnigCodePoint CR_Pd[] = {
+ 17,
+ 0x002d, 0x002d,
+ 0x058a, 0x058a,
+ 0x05be, 0x05be,
+ 0x1400, 0x1400,
+ 0x1806, 0x1806,
+ 0x2010, 0x2015,
+ 0x2e17, 0x2e17,
+ 0x2e1a, 0x2e1a,
+ 0x2e3a, 0x2e3b,
+ 0x2e40, 0x2e40,
+ 0x301c, 0x301c,
+ 0x3030, 0x3030,
+ 0x30a0, 0x30a0,
+ 0xfe31, 0xfe32,
+ 0xfe58, 0xfe58,
+ 0xfe63, 0xfe63,
+ 0xff0d, 0xff0d,
+}; /* CR_Pd */
+
+/* 'Pe': General Category */
+static const OnigCodePoint CR_Pe[] = {
+ 72,
+ 0x0029, 0x0029,
+ 0x005d, 0x005d,
+ 0x007d, 0x007d,
+ 0x0f3b, 0x0f3b,
+ 0x0f3d, 0x0f3d,
+ 0x169c, 0x169c,
+ 0x2046, 0x2046,
+ 0x207e, 0x207e,
+ 0x208e, 0x208e,
+ 0x2309, 0x2309,
+ 0x230b, 0x230b,
+ 0x232a, 0x232a,
+ 0x2769, 0x2769,
+ 0x276b, 0x276b,
+ 0x276d, 0x276d,
+ 0x276f, 0x276f,
+ 0x2771, 0x2771,
+ 0x2773, 0x2773,
+ 0x2775, 0x2775,
+ 0x27c6, 0x27c6,
+ 0x27e7, 0x27e7,
+ 0x27e9, 0x27e9,
+ 0x27eb, 0x27eb,
+ 0x27ed, 0x27ed,
+ 0x27ef, 0x27ef,
+ 0x2984, 0x2984,
+ 0x2986, 0x2986,
+ 0x2988, 0x2988,
+ 0x298a, 0x298a,
+ 0x298c, 0x298c,
+ 0x298e, 0x298e,
+ 0x2990, 0x2990,
+ 0x2992, 0x2992,
+ 0x2994, 0x2994,
+ 0x2996, 0x2996,
+ 0x2998, 0x2998,
+ 0x29d9, 0x29d9,
+ 0x29db, 0x29db,
+ 0x29fd, 0x29fd,
+ 0x2e23, 0x2e23,
+ 0x2e25, 0x2e25,
+ 0x2e27, 0x2e27,
+ 0x2e29, 0x2e29,
+ 0x3009, 0x3009,
+ 0x300b, 0x300b,
+ 0x300d, 0x300d,
+ 0x300f, 0x300f,
+ 0x3011, 0x3011,
+ 0x3015, 0x3015,
+ 0x3017, 0x3017,
+ 0x3019, 0x3019,
+ 0x301b, 0x301b,
+ 0x301e, 0x301f,
+ 0xfd3e, 0xfd3e,
+ 0xfe18, 0xfe18,
+ 0xfe36, 0xfe36,
+ 0xfe38, 0xfe38,
+ 0xfe3a, 0xfe3a,
+ 0xfe3c, 0xfe3c,
+ 0xfe3e, 0xfe3e,
+ 0xfe40, 0xfe40,
+ 0xfe42, 0xfe42,
+ 0xfe44, 0xfe44,
+ 0xfe48, 0xfe48,
+ 0xfe5a, 0xfe5a,
+ 0xfe5c, 0xfe5c,
+ 0xfe5e, 0xfe5e,
+ 0xff09, 0xff09,
+ 0xff3d, 0xff3d,
+ 0xff5d, 0xff5d,
+ 0xff60, 0xff60,
+ 0xff63, 0xff63,
+}; /* CR_Pe */
+
+/* 'Pf': General Category */
+static const OnigCodePoint CR_Pf[] = {
+ 10,
+ 0x00bb, 0x00bb,
+ 0x2019, 0x2019,
+ 0x201d, 0x201d,
+ 0x203a, 0x203a,
+ 0x2e03, 0x2e03,
+ 0x2e05, 0x2e05,
+ 0x2e0a, 0x2e0a,
+ 0x2e0d, 0x2e0d,
+ 0x2e1d, 0x2e1d,
+ 0x2e21, 0x2e21,
+}; /* CR_Pf */
+
+/* 'Pi': General Category */
+static const OnigCodePoint CR_Pi[] = {
+ 11,
+ 0x00ab, 0x00ab,
+ 0x2018, 0x2018,
+ 0x201b, 0x201c,
+ 0x201f, 0x201f,
+ 0x2039, 0x2039,
+ 0x2e02, 0x2e02,
+ 0x2e04, 0x2e04,
+ 0x2e09, 0x2e09,
+ 0x2e0c, 0x2e0c,
+ 0x2e1c, 0x2e1c,
+ 0x2e20, 0x2e20,
+}; /* CR_Pi */
+
+/* 'Po': General Category */
+static const OnigCodePoint CR_Po[] = {
+ 179,
+ 0x0021, 0x0023,
+ 0x0025, 0x0027,
+ 0x002a, 0x002a,
+ 0x002c, 0x002c,
+ 0x002e, 0x002f,
+ 0x003a, 0x003b,
+ 0x003f, 0x0040,
+ 0x005c, 0x005c,
+ 0x00a1, 0x00a1,
+ 0x00a7, 0x00a7,
+ 0x00b6, 0x00b7,
+ 0x00bf, 0x00bf,
+ 0x037e, 0x037e,
+ 0x0387, 0x0387,
+ 0x055a, 0x055f,
+ 0x0589, 0x0589,
+ 0x05c0, 0x05c0,
+ 0x05c3, 0x05c3,
+ 0x05c6, 0x05c6,
+ 0x05f3, 0x05f4,
+ 0x0609, 0x060a,
+ 0x060c, 0x060d,
+ 0x061b, 0x061b,
+ 0x061e, 0x061f,
+ 0x066a, 0x066d,
+ 0x06d4, 0x06d4,
+ 0x0700, 0x070d,
+ 0x07f7, 0x07f9,
+ 0x0830, 0x083e,
+ 0x085e, 0x085e,
+ 0x0964, 0x0965,
+ 0x0970, 0x0970,
+ 0x09fd, 0x09fd,
+ 0x0a76, 0x0a76,
+ 0x0af0, 0x0af0,
+ 0x0c77, 0x0c77,
+ 0x0c84, 0x0c84,
+ 0x0df4, 0x0df4,
+ 0x0e4f, 0x0e4f,
+ 0x0e5a, 0x0e5b,
+ 0x0f04, 0x0f12,
+ 0x0f14, 0x0f14,
+ 0x0f85, 0x0f85,
+ 0x0fd0, 0x0fd4,
+ 0x0fd9, 0x0fda,
+ 0x104a, 0x104f,
+ 0x10fb, 0x10fb,
+ 0x1360, 0x1368,
+ 0x166e, 0x166e,
+ 0x16eb, 0x16ed,
+ 0x1735, 0x1736,
+ 0x17d4, 0x17d6,
+ 0x17d8, 0x17da,
+ 0x1800, 0x1805,
+ 0x1807, 0x180a,
+ 0x1944, 0x1945,
+ 0x1a1e, 0x1a1f,
+ 0x1aa0, 0x1aa6,
+ 0x1aa8, 0x1aad,
+ 0x1b5a, 0x1b60,
+ 0x1bfc, 0x1bff,
+ 0x1c3b, 0x1c3f,
+ 0x1c7e, 0x1c7f,
+ 0x1cc0, 0x1cc7,
+ 0x1cd3, 0x1cd3,
+ 0x2016, 0x2017,
+ 0x2020, 0x2027,
+ 0x2030, 0x2038,
+ 0x203b, 0x203e,
+ 0x2041, 0x2043,
+ 0x2047, 0x2051,
+ 0x2053, 0x2053,
+ 0x2055, 0x205e,
+ 0x2cf9, 0x2cfc,
+ 0x2cfe, 0x2cff,
+ 0x2d70, 0x2d70,
+ 0x2e00, 0x2e01,
+ 0x2e06, 0x2e08,
+ 0x2e0b, 0x2e0b,
+ 0x2e0e, 0x2e16,
+ 0x2e18, 0x2e19,
+ 0x2e1b, 0x2e1b,
+ 0x2e1e, 0x2e1f,
+ 0x2e2a, 0x2e2e,
+ 0x2e30, 0x2e39,
+ 0x2e3c, 0x2e3f,
+ 0x2e41, 0x2e41,
+ 0x2e43, 0x2e4f,
+ 0x3001, 0x3003,
+ 0x303d, 0x303d,
+ 0x30fb, 0x30fb,
+ 0xa4fe, 0xa4ff,
+ 0xa60d, 0xa60f,
+ 0xa673, 0xa673,
+ 0xa67e, 0xa67e,
+ 0xa6f2, 0xa6f7,
+ 0xa874, 0xa877,
+ 0xa8ce, 0xa8cf,
+ 0xa8f8, 0xa8fa,
+ 0xa8fc, 0xa8fc,
+ 0xa92e, 0xa92f,
+ 0xa95f, 0xa95f,
+ 0xa9c1, 0xa9cd,
+ 0xa9de, 0xa9df,
+ 0xaa5c, 0xaa5f,
+ 0xaade, 0xaadf,
+ 0xaaf0, 0xaaf1,
+ 0xabeb, 0xabeb,
+ 0xfe10, 0xfe16,
+ 0xfe19, 0xfe19,
+ 0xfe30, 0xfe30,
+ 0xfe45, 0xfe46,
+ 0xfe49, 0xfe4c,
+ 0xfe50, 0xfe52,
+ 0xfe54, 0xfe57,
+ 0xfe5f, 0xfe61,
+ 0xfe68, 0xfe68,
+ 0xfe6a, 0xfe6b,
+ 0xff01, 0xff03,
+ 0xff05, 0xff07,
+ 0xff0a, 0xff0a,
+ 0xff0c, 0xff0c,
+ 0xff0e, 0xff0f,
+ 0xff1a, 0xff1b,
+ 0xff1f, 0xff20,
+ 0xff3c, 0xff3c,
+ 0xff61, 0xff61,
+ 0xff64, 0xff65,
+ 0x10100, 0x10102,
+ 0x1039f, 0x1039f,
+ 0x103d0, 0x103d0,
+ 0x1056f, 0x1056f,
+ 0x10857, 0x10857,
+ 0x1091f, 0x1091f,
+ 0x1093f, 0x1093f,
+ 0x10a50, 0x10a58,
+ 0x10a7f, 0x10a7f,
+ 0x10af0, 0x10af6,
+ 0x10b39, 0x10b3f,
+ 0x10b99, 0x10b9c,
+ 0x10f55, 0x10f59,
+ 0x11047, 0x1104d,
+ 0x110bb, 0x110bc,
+ 0x110be, 0x110c1,
+ 0x11140, 0x11143,
+ 0x11174, 0x11175,
+ 0x111c5, 0x111c8,
+ 0x111cd, 0x111cd,
+ 0x111db, 0x111db,
+ 0x111dd, 0x111df,
+ 0x11238, 0x1123d,
+ 0x112a9, 0x112a9,
+ 0x1144b, 0x1144f,
+ 0x1145b, 0x1145b,
+ 0x1145d, 0x1145d,
+ 0x114c6, 0x114c6,
+ 0x115c1, 0x115d7,
+ 0x11641, 0x11643,
+ 0x11660, 0x1166c,
+ 0x1173c, 0x1173e,
+ 0x1183b, 0x1183b,
+ 0x119e2, 0x119e2,
+ 0x11a3f, 0x11a46,
+ 0x11a9a, 0x11a9c,
+ 0x11a9e, 0x11aa2,
+ 0x11c41, 0x11c45,
+ 0x11c70, 0x11c71,
+ 0x11ef7, 0x11ef8,
+ 0x11fff, 0x11fff,
+ 0x12470, 0x12474,
+ 0x16a6e, 0x16a6f,
+ 0x16af5, 0x16af5,
+ 0x16b37, 0x16b3b,
+ 0x16b44, 0x16b44,
+ 0x16e97, 0x16e9a,
+ 0x16fe2, 0x16fe2,
+ 0x1bc9f, 0x1bc9f,
+ 0x1da87, 0x1da8b,
+ 0x1e95e, 0x1e95f,
+}; /* CR_Po */
+
+/* 'Ps': General Category */
+static const OnigCodePoint CR_Ps[] = {
+ 75,
+ 0x0028, 0x0028,
+ 0x005b, 0x005b,
+ 0x007b, 0x007b,
+ 0x0f3a, 0x0f3a,
+ 0x0f3c, 0x0f3c,
+ 0x169b, 0x169b,
+ 0x201a, 0x201a,
+ 0x201e, 0x201e,
+ 0x2045, 0x2045,
+ 0x207d, 0x207d,
+ 0x208d, 0x208d,
+ 0x2308, 0x2308,
+ 0x230a, 0x230a,
+ 0x2329, 0x2329,
+ 0x2768, 0x2768,
+ 0x276a, 0x276a,
+ 0x276c, 0x276c,
+ 0x276e, 0x276e,
+ 0x2770, 0x2770,
+ 0x2772, 0x2772,
+ 0x2774, 0x2774,
+ 0x27c5, 0x27c5,
+ 0x27e6, 0x27e6,
+ 0x27e8, 0x27e8,
+ 0x27ea, 0x27ea,
+ 0x27ec, 0x27ec,
+ 0x27ee, 0x27ee,
+ 0x2983, 0x2983,
+ 0x2985, 0x2985,
+ 0x2987, 0x2987,
+ 0x2989, 0x2989,
+ 0x298b, 0x298b,
+ 0x298d, 0x298d,
+ 0x298f, 0x298f,
+ 0x2991, 0x2991,
+ 0x2993, 0x2993,
+ 0x2995, 0x2995,
+ 0x2997, 0x2997,
+ 0x29d8, 0x29d8,
+ 0x29da, 0x29da,
+ 0x29fc, 0x29fc,
+ 0x2e22, 0x2e22,
+ 0x2e24, 0x2e24,
+ 0x2e26, 0x2e26,
+ 0x2e28, 0x2e28,
+ 0x2e42, 0x2e42,
+ 0x3008, 0x3008,
+ 0x300a, 0x300a,
+ 0x300c, 0x300c,
+ 0x300e, 0x300e,
+ 0x3010, 0x3010,
+ 0x3014, 0x3014,
+ 0x3016, 0x3016,
+ 0x3018, 0x3018,
+ 0x301a, 0x301a,
+ 0x301d, 0x301d,
+ 0xfd3f, 0xfd3f,
+ 0xfe17, 0xfe17,
+ 0xfe35, 0xfe35,
+ 0xfe37, 0xfe37,
+ 0xfe39, 0xfe39,
+ 0xfe3b, 0xfe3b,
+ 0xfe3d, 0xfe3d,
+ 0xfe3f, 0xfe3f,
+ 0xfe41, 0xfe41,
+ 0xfe43, 0xfe43,
+ 0xfe47, 0xfe47,
+ 0xfe59, 0xfe59,
+ 0xfe5b, 0xfe5b,
+ 0xfe5d, 0xfe5d,
+ 0xff08, 0xff08,
+ 0xff3b, 0xff3b,
+ 0xff5b, 0xff5b,
+ 0xff5f, 0xff5f,
+ 0xff62, 0xff62,
+}; /* CR_Ps */
+
+/* 'S': Major Category */
+static const OnigCodePoint CR_S[] = {
+ 226,
+ 0x0024, 0x0024,
+ 0x002b, 0x002b,
+ 0x003c, 0x003e,
+ 0x005e, 0x005e,
+ 0x0060, 0x0060,
+ 0x007c, 0x007c,
+ 0x007e, 0x007e,
+ 0x00a2, 0x00a6,
+ 0x00a8, 0x00a9,
+ 0x00ac, 0x00ac,
+ 0x00ae, 0x00b1,
+ 0x00b4, 0x00b4,
+ 0x00b8, 0x00b8,
+ 0x00d7, 0x00d7,
+ 0x00f7, 0x00f7,
+ 0x02c2, 0x02c5,
+ 0x02d2, 0x02df,
+ 0x02e5, 0x02eb,
+ 0x02ed, 0x02ed,
+ 0x02ef, 0x02ff,
+ 0x0375, 0x0375,
+ 0x0384, 0x0385,
+ 0x03f6, 0x03f6,
+ 0x0482, 0x0482,
+ 0x058d, 0x058f,
+ 0x0606, 0x0608,
+ 0x060b, 0x060b,
+ 0x060e, 0x060f,
+ 0x06de, 0x06de,
+ 0x06e9, 0x06e9,
+ 0x06fd, 0x06fe,
+ 0x07f6, 0x07f6,
+ 0x07fe, 0x07ff,
+ 0x09f2, 0x09f3,
+ 0x09fa, 0x09fb,
+ 0x0af1, 0x0af1,
+ 0x0b70, 0x0b70,
+ 0x0bf3, 0x0bfa,
+ 0x0c7f, 0x0c7f,
+ 0x0d4f, 0x0d4f,
+ 0x0d79, 0x0d79,
+ 0x0e3f, 0x0e3f,
+ 0x0f01, 0x0f03,
+ 0x0f13, 0x0f13,
+ 0x0f15, 0x0f17,
+ 0x0f1a, 0x0f1f,
+ 0x0f34, 0x0f34,
+ 0x0f36, 0x0f36,
+ 0x0f38, 0x0f38,
+ 0x0fbe, 0x0fc5,
+ 0x0fc7, 0x0fcc,
+ 0x0fce, 0x0fcf,
+ 0x0fd5, 0x0fd8,
+ 0x109e, 0x109f,
+ 0x1390, 0x1399,
+ 0x166d, 0x166d,
+ 0x17db, 0x17db,
+ 0x1940, 0x1940,
+ 0x19de, 0x19ff,
+ 0x1b61, 0x1b6a,
+ 0x1b74, 0x1b7c,
+ 0x1fbd, 0x1fbd,
+ 0x1fbf, 0x1fc1,
+ 0x1fcd, 0x1fcf,
+ 0x1fdd, 0x1fdf,
+ 0x1fed, 0x1fef,
+ 0x1ffd, 0x1ffe,
+ 0x2044, 0x2044,
+ 0x2052, 0x2052,
+ 0x207a, 0x207c,
+ 0x208a, 0x208c,
+ 0x20a0, 0x20bf,
+ 0x2100, 0x2101,
+ 0x2103, 0x2106,
+ 0x2108, 0x2109,
+ 0x2114, 0x2114,
+ 0x2116, 0x2118,
+ 0x211e, 0x2123,
+ 0x2125, 0x2125,
+ 0x2127, 0x2127,
+ 0x2129, 0x2129,
+ 0x212e, 0x212e,
+ 0x213a, 0x213b,
+ 0x2140, 0x2144,
+ 0x214a, 0x214d,
+ 0x214f, 0x214f,
+ 0x218a, 0x218b,
+ 0x2190, 0x2307,
+ 0x230c, 0x2328,
+ 0x232b, 0x2426,
+ 0x2440, 0x244a,
+ 0x249c, 0x24e9,
+ 0x2500, 0x2767,
+ 0x2794, 0x27c4,
+ 0x27c7, 0x27e5,
+ 0x27f0, 0x2982,
+ 0x2999, 0x29d7,
+ 0x29dc, 0x29fb,
+ 0x29fe, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b98, 0x2bff,
+ 0x2ce5, 0x2cea,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3004, 0x3004,
+ 0x3012, 0x3013,
+ 0x3020, 0x3020,
+ 0x3036, 0x3037,
+ 0x303e, 0x303f,
+ 0x309b, 0x309c,
+ 0x3190, 0x3191,
+ 0x3196, 0x319f,
+ 0x31c0, 0x31e3,
+ 0x3200, 0x321e,
+ 0x322a, 0x3247,
+ 0x3250, 0x3250,
+ 0x3260, 0x327f,
+ 0x328a, 0x32b0,
+ 0x32c0, 0x33ff,
+ 0x4dc0, 0x4dff,
+ 0xa490, 0xa4c6,
+ 0xa700, 0xa716,
+ 0xa720, 0xa721,
+ 0xa789, 0xa78a,
+ 0xa828, 0xa82b,
+ 0xa836, 0xa839,
+ 0xaa77, 0xaa79,
+ 0xab5b, 0xab5b,
+ 0xfb29, 0xfb29,
+ 0xfbb2, 0xfbc1,
+ 0xfdfc, 0xfdfd,
+ 0xfe62, 0xfe62,
+ 0xfe64, 0xfe66,
+ 0xfe69, 0xfe69,
+ 0xff04, 0xff04,
+ 0xff0b, 0xff0b,
+ 0xff1c, 0xff1e,
+ 0xff3e, 0xff3e,
+ 0xff40, 0xff40,
+ 0xff5c, 0xff5c,
+ 0xff5e, 0xff5e,
+ 0xffe0, 0xffe6,
+ 0xffe8, 0xffee,
+ 0xfffc, 0xfffd,
+ 0x10137, 0x1013f,
+ 0x10179, 0x10189,
+ 0x1018c, 0x1018e,
+ 0x10190, 0x1019b,
+ 0x101a0, 0x101a0,
+ 0x101d0, 0x101fc,
+ 0x10877, 0x10878,
+ 0x10ac8, 0x10ac8,
+ 0x1173f, 0x1173f,
+ 0x11fd5, 0x11ff1,
+ 0x16b3c, 0x16b3f,
+ 0x16b45, 0x16b45,
+ 0x1bc9c, 0x1bc9c,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d164,
+ 0x1d16a, 0x1d16c,
+ 0x1d183, 0x1d184,
+ 0x1d18c, 0x1d1a9,
+ 0x1d1ae, 0x1d1e8,
+ 0x1d200, 0x1d241,
+ 0x1d245, 0x1d245,
+ 0x1d300, 0x1d356,
+ 0x1d6c1, 0x1d6c1,
+ 0x1d6db, 0x1d6db,
+ 0x1d6fb, 0x1d6fb,
+ 0x1d715, 0x1d715,
+ 0x1d735, 0x1d735,
+ 0x1d74f, 0x1d74f,
+ 0x1d76f, 0x1d76f,
+ 0x1d789, 0x1d789,
+ 0x1d7a9, 0x1d7a9,
+ 0x1d7c3, 0x1d7c3,
+ 0x1d800, 0x1d9ff,
+ 0x1da37, 0x1da3a,
+ 0x1da6d, 0x1da74,
+ 0x1da76, 0x1da83,
+ 0x1da85, 0x1da86,
+ 0x1e14f, 0x1e14f,
+ 0x1e2ff, 0x1e2ff,
+ 0x1ecac, 0x1ecac,
+ 0x1ecb0, 0x1ecb0,
+ 0x1ed2e, 0x1ed2e,
+ 0x1eef0, 0x1eef1,
+ 0x1f000, 0x1f02b,
+ 0x1f030, 0x1f093,
+ 0x1f0a0, 0x1f0ae,
+ 0x1f0b1, 0x1f0bf,
+ 0x1f0c1, 0x1f0cf,
+ 0x1f0d1, 0x1f0f5,
+ 0x1f110, 0x1f16c,
+ 0x1f170, 0x1f1ac,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23b,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f260, 0x1f265,
+ 0x1f300, 0x1f6d5,
+ 0x1f6e0, 0x1f6ec,
+ 0x1f6f0, 0x1f6fa,
+ 0x1f700, 0x1f773,
+ 0x1f780, 0x1f7d8,
+ 0x1f7e0, 0x1f7eb,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1f900, 0x1f90b,
+ 0x1f90d, 0x1f971,
+ 0x1f973, 0x1f976,
+ 0x1f97a, 0x1f9a2,
+ 0x1f9a5, 0x1f9aa,
+ 0x1f9ae, 0x1f9ca,
+ 0x1f9cd, 0x1fa53,
+ 0x1fa60, 0x1fa6d,
+ 0x1fa70, 0x1fa73,
+ 0x1fa78, 0x1fa7a,
+ 0x1fa80, 0x1fa82,
+ 0x1fa90, 0x1fa95,
+}; /* CR_S */
+
+/* 'Sc': General Category */
+static const OnigCodePoint CR_Sc[] = {
+ 21,
+ 0x0024, 0x0024,
+ 0x00a2, 0x00a5,
+ 0x058f, 0x058f,
+ 0x060b, 0x060b,
+ 0x07fe, 0x07ff,
+ 0x09f2, 0x09f3,
+ 0x09fb, 0x09fb,
+ 0x0af1, 0x0af1,
+ 0x0bf9, 0x0bf9,
+ 0x0e3f, 0x0e3f,
+ 0x17db, 0x17db,
+ 0x20a0, 0x20bf,
+ 0xa838, 0xa838,
+ 0xfdfc, 0xfdfc,
+ 0xfe69, 0xfe69,
+ 0xff04, 0xff04,
+ 0xffe0, 0xffe1,
+ 0xffe5, 0xffe6,
+ 0x11fdd, 0x11fe0,
+ 0x1e2ff, 0x1e2ff,
+ 0x1ecb0, 0x1ecb0,
+}; /* CR_Sc */
+
+/* 'Sk': General Category */
+static const OnigCodePoint CR_Sk[] = {
+ 29,
+ 0x005e, 0x005e,
+ 0x0060, 0x0060,
+ 0x00a8, 0x00a8,
+ 0x00af, 0x00af,
+ 0x00b4, 0x00b4,
+ 0x00b8, 0x00b8,
+ 0x02c2, 0x02c5,
+ 0x02d2, 0x02df,
+ 0x02e5, 0x02eb,
+ 0x02ed, 0x02ed,
+ 0x02ef, 0x02ff,
+ 0x0375, 0x0375,
+ 0x0384, 0x0385,
+ 0x1fbd, 0x1fbd,
+ 0x1fbf, 0x1fc1,
+ 0x1fcd, 0x1fcf,
+ 0x1fdd, 0x1fdf,
+ 0x1fed, 0x1fef,
+ 0x1ffd, 0x1ffe,
+ 0x309b, 0x309c,
+ 0xa700, 0xa716,
+ 0xa720, 0xa721,
+ 0xa789, 0xa78a,
+ 0xab5b, 0xab5b,
+ 0xfbb2, 0xfbc1,
+ 0xff3e, 0xff3e,
+ 0xff40, 0xff40,
+ 0xffe3, 0xffe3,
+ 0x1f3fb, 0x1f3ff,
+}; /* CR_Sk */
+
+/* 'Sm': General Category */
+static const OnigCodePoint CR_Sm[] = {
+ 64,
+ 0x002b, 0x002b,
+ 0x003c, 0x003e,
+ 0x007c, 0x007c,
+ 0x007e, 0x007e,
+ 0x00ac, 0x00ac,
+ 0x00b1, 0x00b1,
+ 0x00d7, 0x00d7,
+ 0x00f7, 0x00f7,
+ 0x03f6, 0x03f6,
+ 0x0606, 0x0608,
+ 0x2044, 0x2044,
+ 0x2052, 0x2052,
+ 0x207a, 0x207c,
+ 0x208a, 0x208c,
+ 0x2118, 0x2118,
+ 0x2140, 0x2144,
+ 0x214b, 0x214b,
+ 0x2190, 0x2194,
+ 0x219a, 0x219b,
+ 0x21a0, 0x21a0,
+ 0x21a3, 0x21a3,
+ 0x21a6, 0x21a6,
+ 0x21ae, 0x21ae,
+ 0x21ce, 0x21cf,
+ 0x21d2, 0x21d2,
+ 0x21d4, 0x21d4,
+ 0x21f4, 0x22ff,
+ 0x2320, 0x2321,
+ 0x237c, 0x237c,
+ 0x239b, 0x23b3,
+ 0x23dc, 0x23e1,
+ 0x25b7, 0x25b7,
+ 0x25c1, 0x25c1,
+ 0x25f8, 0x25ff,
+ 0x266f, 0x266f,
+ 0x27c0, 0x27c4,
+ 0x27c7, 0x27e5,
+ 0x27f0, 0x27ff,
+ 0x2900, 0x2982,
+ 0x2999, 0x29d7,
+ 0x29dc, 0x29fb,
+ 0x29fe, 0x2aff,
+ 0x2b30, 0x2b44,
+ 0x2b47, 0x2b4c,
+ 0xfb29, 0xfb29,
+ 0xfe62, 0xfe62,
+ 0xfe64, 0xfe66,
+ 0xff0b, 0xff0b,
+ 0xff1c, 0xff1e,
+ 0xff5c, 0xff5c,
+ 0xff5e, 0xff5e,
+ 0xffe2, 0xffe2,
+ 0xffe9, 0xffec,
+ 0x1d6c1, 0x1d6c1,
+ 0x1d6db, 0x1d6db,
+ 0x1d6fb, 0x1d6fb,
+ 0x1d715, 0x1d715,
+ 0x1d735, 0x1d735,
+ 0x1d74f, 0x1d74f,
+ 0x1d76f, 0x1d76f,
+ 0x1d789, 0x1d789,
+ 0x1d7a9, 0x1d7a9,
+ 0x1d7c3, 0x1d7c3,
+ 0x1eef0, 0x1eef1,
+}; /* CR_Sm */
+
+/* 'So': General Category */
+static const OnigCodePoint CR_So[] = {
+ 180,
+ 0x00a6, 0x00a6,
+ 0x00a9, 0x00a9,
+ 0x00ae, 0x00ae,
+ 0x00b0, 0x00b0,
+ 0x0482, 0x0482,
+ 0x058d, 0x058e,
+ 0x060e, 0x060f,
+ 0x06de, 0x06de,
+ 0x06e9, 0x06e9,
+ 0x06fd, 0x06fe,
+ 0x07f6, 0x07f6,
+ 0x09fa, 0x09fa,
+ 0x0b70, 0x0b70,
+ 0x0bf3, 0x0bf8,
+ 0x0bfa, 0x0bfa,
+ 0x0c7f, 0x0c7f,
+ 0x0d4f, 0x0d4f,
+ 0x0d79, 0x0d79,
+ 0x0f01, 0x0f03,
+ 0x0f13, 0x0f13,
+ 0x0f15, 0x0f17,
+ 0x0f1a, 0x0f1f,
+ 0x0f34, 0x0f34,
+ 0x0f36, 0x0f36,
+ 0x0f38, 0x0f38,
+ 0x0fbe, 0x0fc5,
+ 0x0fc7, 0x0fcc,
+ 0x0fce, 0x0fcf,
+ 0x0fd5, 0x0fd8,
+ 0x109e, 0x109f,
+ 0x1390, 0x1399,
+ 0x166d, 0x166d,
+ 0x1940, 0x1940,
+ 0x19de, 0x19ff,
+ 0x1b61, 0x1b6a,
+ 0x1b74, 0x1b7c,
+ 0x2100, 0x2101,
+ 0x2103, 0x2106,
+ 0x2108, 0x2109,
+ 0x2114, 0x2114,
+ 0x2116, 0x2117,
+ 0x211e, 0x2123,
+ 0x2125, 0x2125,
+ 0x2127, 0x2127,
+ 0x2129, 0x2129,
+ 0x212e, 0x212e,
+ 0x213a, 0x213b,
+ 0x214a, 0x214a,
+ 0x214c, 0x214d,
+ 0x214f, 0x214f,
+ 0x218a, 0x218b,
+ 0x2195, 0x2199,
+ 0x219c, 0x219f,
+ 0x21a1, 0x21a2,
+ 0x21a4, 0x21a5,
+ 0x21a7, 0x21ad,
+ 0x21af, 0x21cd,
+ 0x21d0, 0x21d1,
+ 0x21d3, 0x21d3,
+ 0x21d5, 0x21f3,
+ 0x2300, 0x2307,
+ 0x230c, 0x231f,
+ 0x2322, 0x2328,
+ 0x232b, 0x237b,
+ 0x237d, 0x239a,
+ 0x23b4, 0x23db,
+ 0x23e2, 0x2426,
+ 0x2440, 0x244a,
+ 0x249c, 0x24e9,
+ 0x2500, 0x25b6,
+ 0x25b8, 0x25c0,
+ 0x25c2, 0x25f7,
+ 0x2600, 0x266e,
+ 0x2670, 0x2767,
+ 0x2794, 0x27bf,
+ 0x2800, 0x28ff,
+ 0x2b00, 0x2b2f,
+ 0x2b45, 0x2b46,
+ 0x2b4d, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b98, 0x2bff,
+ 0x2ce5, 0x2cea,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3004, 0x3004,
+ 0x3012, 0x3013,
+ 0x3020, 0x3020,
+ 0x3036, 0x3037,
+ 0x303e, 0x303f,
+ 0x3190, 0x3191,
+ 0x3196, 0x319f,
+ 0x31c0, 0x31e3,
+ 0x3200, 0x321e,
+ 0x322a, 0x3247,
+ 0x3250, 0x3250,
+ 0x3260, 0x327f,
+ 0x328a, 0x32b0,
+ 0x32c0, 0x33ff,
+ 0x4dc0, 0x4dff,
+ 0xa490, 0xa4c6,
+ 0xa828, 0xa82b,
+ 0xa836, 0xa837,
+ 0xa839, 0xa839,
+ 0xaa77, 0xaa79,
+ 0xfdfd, 0xfdfd,
+ 0xffe4, 0xffe4,
+ 0xffe8, 0xffe8,
+ 0xffed, 0xffee,
+ 0xfffc, 0xfffd,
+ 0x10137, 0x1013f,
+ 0x10179, 0x10189,
+ 0x1018c, 0x1018e,
+ 0x10190, 0x1019b,
+ 0x101a0, 0x101a0,
+ 0x101d0, 0x101fc,
+ 0x10877, 0x10878,
+ 0x10ac8, 0x10ac8,
+ 0x1173f, 0x1173f,
+ 0x11fd5, 0x11fdc,
+ 0x11fe1, 0x11ff1,
+ 0x16b3c, 0x16b3f,
+ 0x16b45, 0x16b45,
+ 0x1bc9c, 0x1bc9c,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d164,
+ 0x1d16a, 0x1d16c,
+ 0x1d183, 0x1d184,
+ 0x1d18c, 0x1d1a9,
+ 0x1d1ae, 0x1d1e8,
+ 0x1d200, 0x1d241,
+ 0x1d245, 0x1d245,
+ 0x1d300, 0x1d356,
+ 0x1d800, 0x1d9ff,
+ 0x1da37, 0x1da3a,
+ 0x1da6d, 0x1da74,
+ 0x1da76, 0x1da83,
+ 0x1da85, 0x1da86,
+ 0x1e14f, 0x1e14f,
+ 0x1ecac, 0x1ecac,
+ 0x1ed2e, 0x1ed2e,
+ 0x1f000, 0x1f02b,
+ 0x1f030, 0x1f093,
+ 0x1f0a0, 0x1f0ae,
+ 0x1f0b1, 0x1f0bf,
+ 0x1f0c1, 0x1f0cf,
+ 0x1f0d1, 0x1f0f5,
+ 0x1f110, 0x1f16c,
+ 0x1f170, 0x1f1ac,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23b,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f260, 0x1f265,
+ 0x1f300, 0x1f3fa,
+ 0x1f400, 0x1f6d5,
+ 0x1f6e0, 0x1f6ec,
+ 0x1f6f0, 0x1f6fa,
+ 0x1f700, 0x1f773,
+ 0x1f780, 0x1f7d8,
+ 0x1f7e0, 0x1f7eb,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1f900, 0x1f90b,
+ 0x1f90d, 0x1f971,
+ 0x1f973, 0x1f976,
+ 0x1f97a, 0x1f9a2,
+ 0x1f9a5, 0x1f9aa,
+ 0x1f9ae, 0x1f9ca,
+ 0x1f9cd, 0x1fa53,
+ 0x1fa60, 0x1fa6d,
+ 0x1fa70, 0x1fa73,
+ 0x1fa78, 0x1fa7a,
+ 0x1fa80, 0x1fa82,
+ 0x1fa90, 0x1fa95,
+}; /* CR_So */
+
+/* 'Z': Major Category */
+static const OnigCodePoint CR_Z[] = {
+ 8,
+ 0x0020, 0x0020,
+ 0x00a0, 0x00a0,
+ 0x1680, 0x1680,
+ 0x2000, 0x200a,
+ 0x2028, 0x2029,
+ 0x202f, 0x202f,
+ 0x205f, 0x205f,
+ 0x3000, 0x3000,
+}; /* CR_Z */
+
+/* 'Zl': General Category */
+static const OnigCodePoint CR_Zl[] = {
+ 1,
+ 0x2028, 0x2028,
+}; /* CR_Zl */
+
+/* 'Zp': General Category */
+static const OnigCodePoint CR_Zp[] = {
+ 1,
+ 0x2029, 0x2029,
+}; /* CR_Zp */
+
+/* 'Zs': General Category */
+static const OnigCodePoint CR_Zs[] = {
+ 7,
+ 0x0020, 0x0020,
+ 0x00a0, 0x00a0,
+ 0x1680, 0x1680,
+ 0x2000, 0x200a,
+ 0x202f, 0x202f,
+ 0x205f, 0x205f,
+ 0x3000, 0x3000,
+}; /* CR_Zs */
+
+/* 'Math': Derived Property */
+static const OnigCodePoint CR_Math[] = {
+ 138,
+ 0x002b, 0x002b,
+ 0x003c, 0x003e,
+ 0x005e, 0x005e,
+ 0x007c, 0x007c,
+ 0x007e, 0x007e,
+ 0x00ac, 0x00ac,
+ 0x00b1, 0x00b1,
+ 0x00d7, 0x00d7,
+ 0x00f7, 0x00f7,
+ 0x03d0, 0x03d2,
+ 0x03d5, 0x03d5,
+ 0x03f0, 0x03f1,
+ 0x03f4, 0x03f6,
+ 0x0606, 0x0608,
+ 0x2016, 0x2016,
+ 0x2032, 0x2034,
+ 0x2040, 0x2040,
+ 0x2044, 0x2044,
+ 0x2052, 0x2052,
+ 0x2061, 0x2064,
+ 0x207a, 0x207e,
+ 0x208a, 0x208e,
+ 0x20d0, 0x20dc,
+ 0x20e1, 0x20e1,
+ 0x20e5, 0x20e6,
+ 0x20eb, 0x20ef,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210a, 0x2113,
+ 0x2115, 0x2115,
+ 0x2118, 0x211d,
+ 0x2124, 0x2124,
+ 0x2128, 0x2129,
+ 0x212c, 0x212d,
+ 0x212f, 0x2131,
+ 0x2133, 0x2138,
+ 0x213c, 0x2149,
+ 0x214b, 0x214b,
+ 0x2190, 0x21a7,
+ 0x21a9, 0x21ae,
+ 0x21b0, 0x21b1,
+ 0x21b6, 0x21b7,
+ 0x21bc, 0x21db,
+ 0x21dd, 0x21dd,
+ 0x21e4, 0x21e5,
+ 0x21f4, 0x22ff,
+ 0x2308, 0x230b,
+ 0x2320, 0x2321,
+ 0x237c, 0x237c,
+ 0x239b, 0x23b5,
+ 0x23b7, 0x23b7,
+ 0x23d0, 0x23d0,
+ 0x23dc, 0x23e2,
+ 0x25a0, 0x25a1,
+ 0x25ae, 0x25b7,
+ 0x25bc, 0x25c1,
+ 0x25c6, 0x25c7,
+ 0x25ca, 0x25cb,
+ 0x25cf, 0x25d3,
+ 0x25e2, 0x25e2,
+ 0x25e4, 0x25e4,
+ 0x25e7, 0x25ec,
+ 0x25f8, 0x25ff,
+ 0x2605, 0x2606,
+ 0x2640, 0x2640,
+ 0x2642, 0x2642,
+ 0x2660, 0x2663,
+ 0x266d, 0x266f,
+ 0x27c0, 0x27ff,
+ 0x2900, 0x2aff,
+ 0x2b30, 0x2b44,
+ 0x2b47, 0x2b4c,
+ 0xfb29, 0xfb29,
+ 0xfe61, 0xfe66,
+ 0xfe68, 0xfe68,
+ 0xff0b, 0xff0b,
+ 0xff1c, 0xff1e,
+ 0xff3c, 0xff3c,
+ 0xff3e, 0xff3e,
+ 0xff5c, 0xff5c,
+ 0xff5e, 0xff5e,
+ 0xffe2, 0xffe2,
+ 0xffe9, 0xffec,
+ 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, 0x1d7ff,
+ 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,
+}; /* CR_Math */
+
+/* 'Alphabetic': Derived Property */
+#define CR_Alphabetic CR_Alpha
+
+/* 'Lowercase': Derived Property */
+#define CR_Lowercase CR_Lower
+
+/* 'Uppercase': Derived Property */
+#define CR_Uppercase CR_Upper
+
+/* 'Cased': Derived Property */
+static const OnigCodePoint CR_Cased[] = {
+ 140,
+ 0x0041, 0x005a,
+ 0x0061, 0x007a,
+ 0x00aa, 0x00aa,
+ 0x00b5, 0x00b5,
+ 0x00ba, 0x00ba,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00f6,
+ 0x00f8, 0x01ba,
+ 0x01bc, 0x01bf,
+ 0x01c4, 0x0293,
+ 0x0295, 0x02b8,
+ 0x02c0, 0x02c1,
+ 0x02e0, 0x02e4,
+ 0x0345, 0x0345,
+ 0x0370, 0x0373,
+ 0x0376, 0x0377,
+ 0x037a, 0x037d,
+ 0x037f, 0x037f,
+ 0x0386, 0x0386,
+ 0x0388, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03f5,
+ 0x03f7, 0x0481,
+ 0x048a, 0x052f,
+ 0x0531, 0x0556,
+ 0x0560, 0x0588,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x10fa,
+ 0x10fd, 0x10ff,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0x1c80, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x1d00, 0x1dbf,
+ 0x1e00, 0x1f15,
+ 0x1f18, 0x1f1d,
+ 0x1f20, 0x1f45,
+ 0x1f48, 0x1f4d,
+ 0x1f50, 0x1f57,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f7d,
+ 0x1f80, 0x1fb4,
+ 0x1fb6, 0x1fbc,
+ 0x1fbe, 0x1fbe,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fcc,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fdb,
+ 0x1fe0, 0x1fec,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ffc,
+ 0x2071, 0x2071,
+ 0x207f, 0x207f,
+ 0x2090, 0x209c,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210a, 0x2113,
+ 0x2115, 0x2115,
+ 0x2119, 0x211d,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212a, 0x212d,
+ 0x212f, 0x2134,
+ 0x2139, 0x2139,
+ 0x213c, 0x213f,
+ 0x2145, 0x2149,
+ 0x214e, 0x214e,
+ 0x2160, 0x217f,
+ 0x2183, 0x2184,
+ 0x24b6, 0x24e9,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2ce4,
+ 0x2ceb, 0x2cee,
+ 0x2cf2, 0x2cf3,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0xa640, 0xa66d,
+ 0xa680, 0xa69d,
+ 0xa722, 0xa787,
+ 0xa78b, 0xa78e,
+ 0xa790, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f8, 0xa7fa,
+ 0xab30, 0xab5a,
+ 0xab5c, 0xab67,
+ 0xab70, 0xabbf,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xff21, 0xff3a,
+ 0xff41, 0xff5a,
+ 0x10400, 0x1044f,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+ 0x10c80, 0x10cb2,
+ 0x10cc0, 0x10cf2,
+ 0x118a0, 0x118df,
+ 0x16e40, 0x16e7f,
+ 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, 0x1d6c0,
+ 0x1d6c2, 0x1d6da,
+ 0x1d6dc, 0x1d6fa,
+ 0x1d6fc, 0x1d714,
+ 0x1d716, 0x1d734,
+ 0x1d736, 0x1d74e,
+ 0x1d750, 0x1d76e,
+ 0x1d770, 0x1d788,
+ 0x1d78a, 0x1d7a8,
+ 0x1d7aa, 0x1d7c2,
+ 0x1d7c4, 0x1d7cb,
+ 0x1e900, 0x1e943,
+ 0x1f130, 0x1f149,
+ 0x1f150, 0x1f169,
+ 0x1f170, 0x1f189,
+}; /* CR_Cased */
+
+/* 'Case_Ignorable': Derived Property */
+static const OnigCodePoint CR_Case_Ignorable[] = {
+ 401,
+ 0x0027, 0x0027,
+ 0x002e, 0x002e,
+ 0x003a, 0x003a,
+ 0x005e, 0x005e,
+ 0x0060, 0x0060,
+ 0x00a8, 0x00a8,
+ 0x00ad, 0x00ad,
+ 0x00af, 0x00af,
+ 0x00b4, 0x00b4,
+ 0x00b7, 0x00b8,
+ 0x02b0, 0x036f,
+ 0x0374, 0x0375,
+ 0x037a, 0x037a,
+ 0x0384, 0x0385,
+ 0x0387, 0x0387,
+ 0x0483, 0x0489,
+ 0x0559, 0x0559,
+ 0x0591, 0x05bd,
+ 0x05bf, 0x05bf,
+ 0x05c1, 0x05c2,
+ 0x05c4, 0x05c5,
+ 0x05c7, 0x05c7,
+ 0x05f4, 0x05f4,
+ 0x0600, 0x0605,
+ 0x0610, 0x061a,
+ 0x061c, 0x061c,
+ 0x0640, 0x0640,
+ 0x064b, 0x065f,
+ 0x0670, 0x0670,
+ 0x06d6, 0x06dd,
+ 0x06df, 0x06e8,
+ 0x06ea, 0x06ed,
+ 0x070f, 0x070f,
+ 0x0711, 0x0711,
+ 0x0730, 0x074a,
+ 0x07a6, 0x07b0,
+ 0x07eb, 0x07f5,
+ 0x07fa, 0x07fa,
+ 0x07fd, 0x07fd,
+ 0x0816, 0x082d,
+ 0x0859, 0x085b,
+ 0x08d3, 0x0902,
+ 0x093a, 0x093a,
+ 0x093c, 0x093c,
+ 0x0941, 0x0948,
+ 0x094d, 0x094d,
+ 0x0951, 0x0957,
+ 0x0962, 0x0963,
+ 0x0971, 0x0971,
+ 0x0981, 0x0981,
+ 0x09bc, 0x09bc,
+ 0x09c1, 0x09c4,
+ 0x09cd, 0x09cd,
+ 0x09e2, 0x09e3,
+ 0x09fe, 0x09fe,
+ 0x0a01, 0x0a02,
+ 0x0a3c, 0x0a3c,
+ 0x0a41, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a51, 0x0a51,
+ 0x0a70, 0x0a71,
+ 0x0a75, 0x0a75,
+ 0x0a81, 0x0a82,
+ 0x0abc, 0x0abc,
+ 0x0ac1, 0x0ac5,
+ 0x0ac7, 0x0ac8,
+ 0x0acd, 0x0acd,
+ 0x0ae2, 0x0ae3,
+ 0x0afa, 0x0aff,
+ 0x0b01, 0x0b01,
+ 0x0b3c, 0x0b3c,
+ 0x0b3f, 0x0b3f,
+ 0x0b41, 0x0b44,
+ 0x0b4d, 0x0b4d,
+ 0x0b56, 0x0b56,
+ 0x0b62, 0x0b63,
+ 0x0b82, 0x0b82,
+ 0x0bc0, 0x0bc0,
+ 0x0bcd, 0x0bcd,
+ 0x0c00, 0x0c00,
+ 0x0c04, 0x0c04,
+ 0x0c3e, 0x0c40,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c62, 0x0c63,
+ 0x0c81, 0x0c81,
+ 0x0cbc, 0x0cbc,
+ 0x0cbf, 0x0cbf,
+ 0x0cc6, 0x0cc6,
+ 0x0ccc, 0x0ccd,
+ 0x0ce2, 0x0ce3,
+ 0x0d00, 0x0d01,
+ 0x0d3b, 0x0d3c,
+ 0x0d41, 0x0d44,
+ 0x0d4d, 0x0d4d,
+ 0x0d62, 0x0d63,
+ 0x0dca, 0x0dca,
+ 0x0dd2, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0e31, 0x0e31,
+ 0x0e34, 0x0e3a,
+ 0x0e46, 0x0e4e,
+ 0x0eb1, 0x0eb1,
+ 0x0eb4, 0x0ebc,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0f18, 0x0f19,
+ 0x0f35, 0x0f35,
+ 0x0f37, 0x0f37,
+ 0x0f39, 0x0f39,
+ 0x0f71, 0x0f7e,
+ 0x0f80, 0x0f84,
+ 0x0f86, 0x0f87,
+ 0x0f8d, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fc6, 0x0fc6,
+ 0x102d, 0x1030,
+ 0x1032, 0x1037,
+ 0x1039, 0x103a,
+ 0x103d, 0x103e,
+ 0x1058, 0x1059,
+ 0x105e, 0x1060,
+ 0x1071, 0x1074,
+ 0x1082, 0x1082,
+ 0x1085, 0x1086,
+ 0x108d, 0x108d,
+ 0x109d, 0x109d,
+ 0x10fc, 0x10fc,
+ 0x135d, 0x135f,
+ 0x1712, 0x1714,
+ 0x1732, 0x1734,
+ 0x1752, 0x1753,
+ 0x1772, 0x1773,
+ 0x17b4, 0x17b5,
+ 0x17b7, 0x17bd,
+ 0x17c6, 0x17c6,
+ 0x17c9, 0x17d3,
+ 0x17d7, 0x17d7,
+ 0x17dd, 0x17dd,
+ 0x180b, 0x180e,
+ 0x1843, 0x1843,
+ 0x1885, 0x1886,
+ 0x18a9, 0x18a9,
+ 0x1920, 0x1922,
+ 0x1927, 0x1928,
+ 0x1932, 0x1932,
+ 0x1939, 0x193b,
+ 0x1a17, 0x1a18,
+ 0x1a1b, 0x1a1b,
+ 0x1a56, 0x1a56,
+ 0x1a58, 0x1a5e,
+ 0x1a60, 0x1a60,
+ 0x1a62, 0x1a62,
+ 0x1a65, 0x1a6c,
+ 0x1a73, 0x1a7c,
+ 0x1a7f, 0x1a7f,
+ 0x1aa7, 0x1aa7,
+ 0x1ab0, 0x1abe,
+ 0x1b00, 0x1b03,
+ 0x1b34, 0x1b34,
+ 0x1b36, 0x1b3a,
+ 0x1b3c, 0x1b3c,
+ 0x1b42, 0x1b42,
+ 0x1b6b, 0x1b73,
+ 0x1b80, 0x1b81,
+ 0x1ba2, 0x1ba5,
+ 0x1ba8, 0x1ba9,
+ 0x1bab, 0x1bad,
+ 0x1be6, 0x1be6,
+ 0x1be8, 0x1be9,
+ 0x1bed, 0x1bed,
+ 0x1bef, 0x1bf1,
+ 0x1c2c, 0x1c33,
+ 0x1c36, 0x1c37,
+ 0x1c78, 0x1c7d,
+ 0x1cd0, 0x1cd2,
+ 0x1cd4, 0x1ce0,
+ 0x1ce2, 0x1ce8,
+ 0x1ced, 0x1ced,
+ 0x1cf4, 0x1cf4,
+ 0x1cf8, 0x1cf9,
+ 0x1d2c, 0x1d6a,
+ 0x1d78, 0x1d78,
+ 0x1d9b, 0x1df9,
+ 0x1dfb, 0x1dff,
+ 0x1fbd, 0x1fbd,
+ 0x1fbf, 0x1fc1,
+ 0x1fcd, 0x1fcf,
+ 0x1fdd, 0x1fdf,
+ 0x1fed, 0x1fef,
+ 0x1ffd, 0x1ffe,
+ 0x200b, 0x200f,
+ 0x2018, 0x2019,
+ 0x2024, 0x2024,
+ 0x2027, 0x2027,
+ 0x202a, 0x202e,
+ 0x2060, 0x2064,
+ 0x2066, 0x206f,
+ 0x2071, 0x2071,
+ 0x207f, 0x207f,
+ 0x2090, 0x209c,
+ 0x20d0, 0x20f0,
+ 0x2c7c, 0x2c7d,
+ 0x2cef, 0x2cf1,
+ 0x2d6f, 0x2d6f,
+ 0x2d7f, 0x2d7f,
+ 0x2de0, 0x2dff,
+ 0x2e2f, 0x2e2f,
+ 0x3005, 0x3005,
+ 0x302a, 0x302d,
+ 0x3031, 0x3035,
+ 0x303b, 0x303b,
+ 0x3099, 0x309e,
+ 0x30fc, 0x30fe,
+ 0xa015, 0xa015,
+ 0xa4f8, 0xa4fd,
+ 0xa60c, 0xa60c,
+ 0xa66f, 0xa672,
+ 0xa674, 0xa67d,
+ 0xa67f, 0xa67f,
+ 0xa69c, 0xa69f,
+ 0xa6f0, 0xa6f1,
+ 0xa700, 0xa721,
+ 0xa770, 0xa770,
+ 0xa788, 0xa78a,
+ 0xa7f8, 0xa7f9,
+ 0xa802, 0xa802,
+ 0xa806, 0xa806,
+ 0xa80b, 0xa80b,
+ 0xa825, 0xa826,
+ 0xa8c4, 0xa8c5,
+ 0xa8e0, 0xa8f1,
+ 0xa8ff, 0xa8ff,
+ 0xa926, 0xa92d,
+ 0xa947, 0xa951,
+ 0xa980, 0xa982,
+ 0xa9b3, 0xa9b3,
+ 0xa9b6, 0xa9b9,
+ 0xa9bc, 0xa9bd,
+ 0xa9cf, 0xa9cf,
+ 0xa9e5, 0xa9e6,
+ 0xaa29, 0xaa2e,
+ 0xaa31, 0xaa32,
+ 0xaa35, 0xaa36,
+ 0xaa43, 0xaa43,
+ 0xaa4c, 0xaa4c,
+ 0xaa70, 0xaa70,
+ 0xaa7c, 0xaa7c,
+ 0xaab0, 0xaab0,
+ 0xaab2, 0xaab4,
+ 0xaab7, 0xaab8,
+ 0xaabe, 0xaabf,
+ 0xaac1, 0xaac1,
+ 0xaadd, 0xaadd,
+ 0xaaec, 0xaaed,
+ 0xaaf3, 0xaaf4,
+ 0xaaf6, 0xaaf6,
+ 0xab5b, 0xab5f,
+ 0xabe5, 0xabe5,
+ 0xabe8, 0xabe8,
+ 0xabed, 0xabed,
+ 0xfb1e, 0xfb1e,
+ 0xfbb2, 0xfbc1,
+ 0xfe00, 0xfe0f,
+ 0xfe13, 0xfe13,
+ 0xfe20, 0xfe2f,
+ 0xfe52, 0xfe52,
+ 0xfe55, 0xfe55,
+ 0xfeff, 0xfeff,
+ 0xff07, 0xff07,
+ 0xff0e, 0xff0e,
+ 0xff1a, 0xff1a,
+ 0xff3e, 0xff3e,
+ 0xff40, 0xff40,
+ 0xff70, 0xff70,
+ 0xff9e, 0xff9f,
+ 0xffe3, 0xffe3,
+ 0xfff9, 0xfffb,
+ 0x101fd, 0x101fd,
+ 0x102e0, 0x102e0,
+ 0x10376, 0x1037a,
+ 0x10a01, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a0f,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a3f,
+ 0x10ae5, 0x10ae6,
+ 0x10d24, 0x10d27,
+ 0x10f46, 0x10f50,
+ 0x11001, 0x11001,
+ 0x11038, 0x11046,
+ 0x1107f, 0x11081,
+ 0x110b3, 0x110b6,
+ 0x110b9, 0x110ba,
+ 0x110bd, 0x110bd,
+ 0x110cd, 0x110cd,
+ 0x11100, 0x11102,
+ 0x11127, 0x1112b,
+ 0x1112d, 0x11134,
+ 0x11173, 0x11173,
+ 0x11180, 0x11181,
+ 0x111b6, 0x111be,
+ 0x111c9, 0x111cc,
+ 0x1122f, 0x11231,
+ 0x11234, 0x11234,
+ 0x11236, 0x11237,
+ 0x1123e, 0x1123e,
+ 0x112df, 0x112df,
+ 0x112e3, 0x112ea,
+ 0x11300, 0x11301,
+ 0x1133b, 0x1133c,
+ 0x11340, 0x11340,
+ 0x11366, 0x1136c,
+ 0x11370, 0x11374,
+ 0x11438, 0x1143f,
+ 0x11442, 0x11444,
+ 0x11446, 0x11446,
+ 0x1145e, 0x1145e,
+ 0x114b3, 0x114b8,
+ 0x114ba, 0x114ba,
+ 0x114bf, 0x114c0,
+ 0x114c2, 0x114c3,
+ 0x115b2, 0x115b5,
+ 0x115bc, 0x115bd,
+ 0x115bf, 0x115c0,
+ 0x115dc, 0x115dd,
+ 0x11633, 0x1163a,
+ 0x1163d, 0x1163d,
+ 0x1163f, 0x11640,
+ 0x116ab, 0x116ab,
+ 0x116ad, 0x116ad,
+ 0x116b0, 0x116b5,
+ 0x116b7, 0x116b7,
+ 0x1171d, 0x1171f,
+ 0x11722, 0x11725,
+ 0x11727, 0x1172b,
+ 0x1182f, 0x11837,
+ 0x11839, 0x1183a,
+ 0x119d4, 0x119d7,
+ 0x119da, 0x119db,
+ 0x119e0, 0x119e0,
+ 0x11a01, 0x11a0a,
+ 0x11a33, 0x11a38,
+ 0x11a3b, 0x11a3e,
+ 0x11a47, 0x11a47,
+ 0x11a51, 0x11a56,
+ 0x11a59, 0x11a5b,
+ 0x11a8a, 0x11a96,
+ 0x11a98, 0x11a99,
+ 0x11c30, 0x11c36,
+ 0x11c38, 0x11c3d,
+ 0x11c3f, 0x11c3f,
+ 0x11c92, 0x11ca7,
+ 0x11caa, 0x11cb0,
+ 0x11cb2, 0x11cb3,
+ 0x11cb5, 0x11cb6,
+ 0x11d31, 0x11d36,
+ 0x11d3a, 0x11d3a,
+ 0x11d3c, 0x11d3d,
+ 0x11d3f, 0x11d45,
+ 0x11d47, 0x11d47,
+ 0x11d90, 0x11d91,
+ 0x11d95, 0x11d95,
+ 0x11d97, 0x11d97,
+ 0x11ef3, 0x11ef4,
+ 0x13430, 0x13438,
+ 0x16af0, 0x16af4,
+ 0x16b30, 0x16b36,
+ 0x16b40, 0x16b43,
+ 0x16f4f, 0x16f4f,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe1,
+ 0x16fe3, 0x16fe3,
+ 0x1bc9d, 0x1bc9e,
+ 0x1bca0, 0x1bca3,
+ 0x1d167, 0x1d169,
+ 0x1d173, 0x1d182,
+ 0x1d185, 0x1d18b,
+ 0x1d1aa, 0x1d1ad,
+ 0x1d242, 0x1d244,
+ 0x1da00, 0x1da36,
+ 0x1da3b, 0x1da6c,
+ 0x1da75, 0x1da75,
+ 0x1da84, 0x1da84,
+ 0x1da9b, 0x1da9f,
+ 0x1daa1, 0x1daaf,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e130, 0x1e13d,
+ 0x1e2ec, 0x1e2ef,
+ 0x1e8d0, 0x1e8d6,
+ 0x1e944, 0x1e94b,
+ 0x1f3fb, 0x1f3ff,
+ 0xe0001, 0xe0001,
+ 0xe0020, 0xe007f,
+ 0xe0100, 0xe01ef,
+}; /* CR_Case_Ignorable */
+
+/* 'Changes_When_Lowercased': Derived Property */
+static const OnigCodePoint CR_Changes_When_Lowercased[] = {
+ 599,
+ 0x0041, 0x005a,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00de,
+ 0x0100, 0x0100,
+ 0x0102, 0x0102,
+ 0x0104, 0x0104,
+ 0x0106, 0x0106,
+ 0x0108, 0x0108,
+ 0x010a, 0x010a,
+ 0x010c, 0x010c,
+ 0x010e, 0x010e,
+ 0x0110, 0x0110,
+ 0x0112, 0x0112,
+ 0x0114, 0x0114,
+ 0x0116, 0x0116,
+ 0x0118, 0x0118,
+ 0x011a, 0x011a,
+ 0x011c, 0x011c,
+ 0x011e, 0x011e,
+ 0x0120, 0x0120,
+ 0x0122, 0x0122,
+ 0x0124, 0x0124,
+ 0x0126, 0x0126,
+ 0x0128, 0x0128,
+ 0x012a, 0x012a,
+ 0x012c, 0x012c,
+ 0x012e, 0x012e,
+ 0x0130, 0x0130,
+ 0x0132, 0x0132,
+ 0x0134, 0x0134,
+ 0x0136, 0x0136,
+ 0x0139, 0x0139,
+ 0x013b, 0x013b,
+ 0x013d, 0x013d,
+ 0x013f, 0x013f,
+ 0x0141, 0x0141,
+ 0x0143, 0x0143,
+ 0x0145, 0x0145,
+ 0x0147, 0x0147,
+ 0x014a, 0x014a,
+ 0x014c, 0x014c,
+ 0x014e, 0x014e,
+ 0x0150, 0x0150,
+ 0x0152, 0x0152,
+ 0x0154, 0x0154,
+ 0x0156, 0x0156,
+ 0x0158, 0x0158,
+ 0x015a, 0x015a,
+ 0x015c, 0x015c,
+ 0x015e, 0x015e,
+ 0x0160, 0x0160,
+ 0x0162, 0x0162,
+ 0x0164, 0x0164,
+ 0x0166, 0x0166,
+ 0x0168, 0x0168,
+ 0x016a, 0x016a,
+ 0x016c, 0x016c,
+ 0x016e, 0x016e,
+ 0x0170, 0x0170,
+ 0x0172, 0x0172,
+ 0x0174, 0x0174,
+ 0x0176, 0x0176,
+ 0x0178, 0x0179,
+ 0x017b, 0x017b,
+ 0x017d, 0x017d,
+ 0x0181, 0x0182,
+ 0x0184, 0x0184,
+ 0x0186, 0x0187,
+ 0x0189, 0x018b,
+ 0x018e, 0x0191,
+ 0x0193, 0x0194,
+ 0x0196, 0x0198,
+ 0x019c, 0x019d,
+ 0x019f, 0x01a0,
+ 0x01a2, 0x01a2,
+ 0x01a4, 0x01a4,
+ 0x01a6, 0x01a7,
+ 0x01a9, 0x01a9,
+ 0x01ac, 0x01ac,
+ 0x01ae, 0x01af,
+ 0x01b1, 0x01b3,
+ 0x01b5, 0x01b5,
+ 0x01b7, 0x01b8,
+ 0x01bc, 0x01bc,
+ 0x01c4, 0x01c5,
+ 0x01c7, 0x01c8,
+ 0x01ca, 0x01cb,
+ 0x01cd, 0x01cd,
+ 0x01cf, 0x01cf,
+ 0x01d1, 0x01d1,
+ 0x01d3, 0x01d3,
+ 0x01d5, 0x01d5,
+ 0x01d7, 0x01d7,
+ 0x01d9, 0x01d9,
+ 0x01db, 0x01db,
+ 0x01de, 0x01de,
+ 0x01e0, 0x01e0,
+ 0x01e2, 0x01e2,
+ 0x01e4, 0x01e4,
+ 0x01e6, 0x01e6,
+ 0x01e8, 0x01e8,
+ 0x01ea, 0x01ea,
+ 0x01ec, 0x01ec,
+ 0x01ee, 0x01ee,
+ 0x01f1, 0x01f2,
+ 0x01f4, 0x01f4,
+ 0x01f6, 0x01f8,
+ 0x01fa, 0x01fa,
+ 0x01fc, 0x01fc,
+ 0x01fe, 0x01fe,
+ 0x0200, 0x0200,
+ 0x0202, 0x0202,
+ 0x0204, 0x0204,
+ 0x0206, 0x0206,
+ 0x0208, 0x0208,
+ 0x020a, 0x020a,
+ 0x020c, 0x020c,
+ 0x020e, 0x020e,
+ 0x0210, 0x0210,
+ 0x0212, 0x0212,
+ 0x0214, 0x0214,
+ 0x0216, 0x0216,
+ 0x0218, 0x0218,
+ 0x021a, 0x021a,
+ 0x021c, 0x021c,
+ 0x021e, 0x021e,
+ 0x0220, 0x0220,
+ 0x0222, 0x0222,
+ 0x0224, 0x0224,
+ 0x0226, 0x0226,
+ 0x0228, 0x0228,
+ 0x022a, 0x022a,
+ 0x022c, 0x022c,
+ 0x022e, 0x022e,
+ 0x0230, 0x0230,
+ 0x0232, 0x0232,
+ 0x023a, 0x023b,
+ 0x023d, 0x023e,
+ 0x0241, 0x0241,
+ 0x0243, 0x0246,
+ 0x0248, 0x0248,
+ 0x024a, 0x024a,
+ 0x024c, 0x024c,
+ 0x024e, 0x024e,
+ 0x0370, 0x0370,
+ 0x0372, 0x0372,
+ 0x0376, 0x0376,
+ 0x037f, 0x037f,
+ 0x0386, 0x0386,
+ 0x0388, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x038f,
+ 0x0391, 0x03a1,
+ 0x03a3, 0x03ab,
+ 0x03cf, 0x03cf,
+ 0x03d8, 0x03d8,
+ 0x03da, 0x03da,
+ 0x03dc, 0x03dc,
+ 0x03de, 0x03de,
+ 0x03e0, 0x03e0,
+ 0x03e2, 0x03e2,
+ 0x03e4, 0x03e4,
+ 0x03e6, 0x03e6,
+ 0x03e8, 0x03e8,
+ 0x03ea, 0x03ea,
+ 0x03ec, 0x03ec,
+ 0x03ee, 0x03ee,
+ 0x03f4, 0x03f4,
+ 0x03f7, 0x03f7,
+ 0x03f9, 0x03fa,
+ 0x03fd, 0x042f,
+ 0x0460, 0x0460,
+ 0x0462, 0x0462,
+ 0x0464, 0x0464,
+ 0x0466, 0x0466,
+ 0x0468, 0x0468,
+ 0x046a, 0x046a,
+ 0x046c, 0x046c,
+ 0x046e, 0x046e,
+ 0x0470, 0x0470,
+ 0x0472, 0x0472,
+ 0x0474, 0x0474,
+ 0x0476, 0x0476,
+ 0x0478, 0x0478,
+ 0x047a, 0x047a,
+ 0x047c, 0x047c,
+ 0x047e, 0x047e,
+ 0x0480, 0x0480,
+ 0x048a, 0x048a,
+ 0x048c, 0x048c,
+ 0x048e, 0x048e,
+ 0x0490, 0x0490,
+ 0x0492, 0x0492,
+ 0x0494, 0x0494,
+ 0x0496, 0x0496,
+ 0x0498, 0x0498,
+ 0x049a, 0x049a,
+ 0x049c, 0x049c,
+ 0x049e, 0x049e,
+ 0x04a0, 0x04a0,
+ 0x04a2, 0x04a2,
+ 0x04a4, 0x04a4,
+ 0x04a6, 0x04a6,
+ 0x04a8, 0x04a8,
+ 0x04aa, 0x04aa,
+ 0x04ac, 0x04ac,
+ 0x04ae, 0x04ae,
+ 0x04b0, 0x04b0,
+ 0x04b2, 0x04b2,
+ 0x04b4, 0x04b4,
+ 0x04b6, 0x04b6,
+ 0x04b8, 0x04b8,
+ 0x04ba, 0x04ba,
+ 0x04bc, 0x04bc,
+ 0x04be, 0x04be,
+ 0x04c0, 0x04c1,
+ 0x04c3, 0x04c3,
+ 0x04c5, 0x04c5,
+ 0x04c7, 0x04c7,
+ 0x04c9, 0x04c9,
+ 0x04cb, 0x04cb,
+ 0x04cd, 0x04cd,
+ 0x04d0, 0x04d0,
+ 0x04d2, 0x04d2,
+ 0x04d4, 0x04d4,
+ 0x04d6, 0x04d6,
+ 0x04d8, 0x04d8,
+ 0x04da, 0x04da,
+ 0x04dc, 0x04dc,
+ 0x04de, 0x04de,
+ 0x04e0, 0x04e0,
+ 0x04e2, 0x04e2,
+ 0x04e4, 0x04e4,
+ 0x04e6, 0x04e6,
+ 0x04e8, 0x04e8,
+ 0x04ea, 0x04ea,
+ 0x04ec, 0x04ec,
+ 0x04ee, 0x04ee,
+ 0x04f0, 0x04f0,
+ 0x04f2, 0x04f2,
+ 0x04f4, 0x04f4,
+ 0x04f6, 0x04f6,
+ 0x04f8, 0x04f8,
+ 0x04fa, 0x04fa,
+ 0x04fc, 0x04fc,
+ 0x04fe, 0x04fe,
+ 0x0500, 0x0500,
+ 0x0502, 0x0502,
+ 0x0504, 0x0504,
+ 0x0506, 0x0506,
+ 0x0508, 0x0508,
+ 0x050a, 0x050a,
+ 0x050c, 0x050c,
+ 0x050e, 0x050e,
+ 0x0510, 0x0510,
+ 0x0512, 0x0512,
+ 0x0514, 0x0514,
+ 0x0516, 0x0516,
+ 0x0518, 0x0518,
+ 0x051a, 0x051a,
+ 0x051c, 0x051c,
+ 0x051e, 0x051e,
+ 0x0520, 0x0520,
+ 0x0522, 0x0522,
+ 0x0524, 0x0524,
+ 0x0526, 0x0526,
+ 0x0528, 0x0528,
+ 0x052a, 0x052a,
+ 0x052c, 0x052c,
+ 0x052e, 0x052e,
+ 0x0531, 0x0556,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x13a0, 0x13f5,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x1e00, 0x1e00,
+ 0x1e02, 0x1e02,
+ 0x1e04, 0x1e04,
+ 0x1e06, 0x1e06,
+ 0x1e08, 0x1e08,
+ 0x1e0a, 0x1e0a,
+ 0x1e0c, 0x1e0c,
+ 0x1e0e, 0x1e0e,
+ 0x1e10, 0x1e10,
+ 0x1e12, 0x1e12,
+ 0x1e14, 0x1e14,
+ 0x1e16, 0x1e16,
+ 0x1e18, 0x1e18,
+ 0x1e1a, 0x1e1a,
+ 0x1e1c, 0x1e1c,
+ 0x1e1e, 0x1e1e,
+ 0x1e20, 0x1e20,
+ 0x1e22, 0x1e22,
+ 0x1e24, 0x1e24,
+ 0x1e26, 0x1e26,
+ 0x1e28, 0x1e28,
+ 0x1e2a, 0x1e2a,
+ 0x1e2c, 0x1e2c,
+ 0x1e2e, 0x1e2e,
+ 0x1e30, 0x1e30,
+ 0x1e32, 0x1e32,
+ 0x1e34, 0x1e34,
+ 0x1e36, 0x1e36,
+ 0x1e38, 0x1e38,
+ 0x1e3a, 0x1e3a,
+ 0x1e3c, 0x1e3c,
+ 0x1e3e, 0x1e3e,
+ 0x1e40, 0x1e40,
+ 0x1e42, 0x1e42,
+ 0x1e44, 0x1e44,
+ 0x1e46, 0x1e46,
+ 0x1e48, 0x1e48,
+ 0x1e4a, 0x1e4a,
+ 0x1e4c, 0x1e4c,
+ 0x1e4e, 0x1e4e,
+ 0x1e50, 0x1e50,
+ 0x1e52, 0x1e52,
+ 0x1e54, 0x1e54,
+ 0x1e56, 0x1e56,
+ 0x1e58, 0x1e58,
+ 0x1e5a, 0x1e5a,
+ 0x1e5c, 0x1e5c,
+ 0x1e5e, 0x1e5e,
+ 0x1e60, 0x1e60,
+ 0x1e62, 0x1e62,
+ 0x1e64, 0x1e64,
+ 0x1e66, 0x1e66,
+ 0x1e68, 0x1e68,
+ 0x1e6a, 0x1e6a,
+ 0x1e6c, 0x1e6c,
+ 0x1e6e, 0x1e6e,
+ 0x1e70, 0x1e70,
+ 0x1e72, 0x1e72,
+ 0x1e74, 0x1e74,
+ 0x1e76, 0x1e76,
+ 0x1e78, 0x1e78,
+ 0x1e7a, 0x1e7a,
+ 0x1e7c, 0x1e7c,
+ 0x1e7e, 0x1e7e,
+ 0x1e80, 0x1e80,
+ 0x1e82, 0x1e82,
+ 0x1e84, 0x1e84,
+ 0x1e86, 0x1e86,
+ 0x1e88, 0x1e88,
+ 0x1e8a, 0x1e8a,
+ 0x1e8c, 0x1e8c,
+ 0x1e8e, 0x1e8e,
+ 0x1e90, 0x1e90,
+ 0x1e92, 0x1e92,
+ 0x1e94, 0x1e94,
+ 0x1e9e, 0x1e9e,
+ 0x1ea0, 0x1ea0,
+ 0x1ea2, 0x1ea2,
+ 0x1ea4, 0x1ea4,
+ 0x1ea6, 0x1ea6,
+ 0x1ea8, 0x1ea8,
+ 0x1eaa, 0x1eaa,
+ 0x1eac, 0x1eac,
+ 0x1eae, 0x1eae,
+ 0x1eb0, 0x1eb0,
+ 0x1eb2, 0x1eb2,
+ 0x1eb4, 0x1eb4,
+ 0x1eb6, 0x1eb6,
+ 0x1eb8, 0x1eb8,
+ 0x1eba, 0x1eba,
+ 0x1ebc, 0x1ebc,
+ 0x1ebe, 0x1ebe,
+ 0x1ec0, 0x1ec0,
+ 0x1ec2, 0x1ec2,
+ 0x1ec4, 0x1ec4,
+ 0x1ec6, 0x1ec6,
+ 0x1ec8, 0x1ec8,
+ 0x1eca, 0x1eca,
+ 0x1ecc, 0x1ecc,
+ 0x1ece, 0x1ece,
+ 0x1ed0, 0x1ed0,
+ 0x1ed2, 0x1ed2,
+ 0x1ed4, 0x1ed4,
+ 0x1ed6, 0x1ed6,
+ 0x1ed8, 0x1ed8,
+ 0x1eda, 0x1eda,
+ 0x1edc, 0x1edc,
+ 0x1ede, 0x1ede,
+ 0x1ee0, 0x1ee0,
+ 0x1ee2, 0x1ee2,
+ 0x1ee4, 0x1ee4,
+ 0x1ee6, 0x1ee6,
+ 0x1ee8, 0x1ee8,
+ 0x1eea, 0x1eea,
+ 0x1eec, 0x1eec,
+ 0x1eee, 0x1eee,
+ 0x1ef0, 0x1ef0,
+ 0x1ef2, 0x1ef2,
+ 0x1ef4, 0x1ef4,
+ 0x1ef6, 0x1ef6,
+ 0x1ef8, 0x1ef8,
+ 0x1efa, 0x1efa,
+ 0x1efc, 0x1efc,
+ 0x1efe, 0x1efe,
+ 0x1f08, 0x1f0f,
+ 0x1f18, 0x1f1d,
+ 0x1f28, 0x1f2f,
+ 0x1f38, 0x1f3f,
+ 0x1f48, 0x1f4d,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f5f,
+ 0x1f68, 0x1f6f,
+ 0x1f88, 0x1f8f,
+ 0x1f98, 0x1f9f,
+ 0x1fa8, 0x1faf,
+ 0x1fb8, 0x1fbc,
+ 0x1fc8, 0x1fcc,
+ 0x1fd8, 0x1fdb,
+ 0x1fe8, 0x1fec,
+ 0x1ff8, 0x1ffc,
+ 0x2126, 0x2126,
+ 0x212a, 0x212b,
+ 0x2132, 0x2132,
+ 0x2160, 0x216f,
+ 0x2183, 0x2183,
+ 0x24b6, 0x24cf,
+ 0x2c00, 0x2c2e,
+ 0x2c60, 0x2c60,
+ 0x2c62, 0x2c64,
+ 0x2c67, 0x2c67,
+ 0x2c69, 0x2c69,
+ 0x2c6b, 0x2c6b,
+ 0x2c6d, 0x2c70,
+ 0x2c72, 0x2c72,
+ 0x2c75, 0x2c75,
+ 0x2c7e, 0x2c80,
+ 0x2c82, 0x2c82,
+ 0x2c84, 0x2c84,
+ 0x2c86, 0x2c86,
+ 0x2c88, 0x2c88,
+ 0x2c8a, 0x2c8a,
+ 0x2c8c, 0x2c8c,
+ 0x2c8e, 0x2c8e,
+ 0x2c90, 0x2c90,
+ 0x2c92, 0x2c92,
+ 0x2c94, 0x2c94,
+ 0x2c96, 0x2c96,
+ 0x2c98, 0x2c98,
+ 0x2c9a, 0x2c9a,
+ 0x2c9c, 0x2c9c,
+ 0x2c9e, 0x2c9e,
+ 0x2ca0, 0x2ca0,
+ 0x2ca2, 0x2ca2,
+ 0x2ca4, 0x2ca4,
+ 0x2ca6, 0x2ca6,
+ 0x2ca8, 0x2ca8,
+ 0x2caa, 0x2caa,
+ 0x2cac, 0x2cac,
+ 0x2cae, 0x2cae,
+ 0x2cb0, 0x2cb0,
+ 0x2cb2, 0x2cb2,
+ 0x2cb4, 0x2cb4,
+ 0x2cb6, 0x2cb6,
+ 0x2cb8, 0x2cb8,
+ 0x2cba, 0x2cba,
+ 0x2cbc, 0x2cbc,
+ 0x2cbe, 0x2cbe,
+ 0x2cc0, 0x2cc0,
+ 0x2cc2, 0x2cc2,
+ 0x2cc4, 0x2cc4,
+ 0x2cc6, 0x2cc6,
+ 0x2cc8, 0x2cc8,
+ 0x2cca, 0x2cca,
+ 0x2ccc, 0x2ccc,
+ 0x2cce, 0x2cce,
+ 0x2cd0, 0x2cd0,
+ 0x2cd2, 0x2cd2,
+ 0x2cd4, 0x2cd4,
+ 0x2cd6, 0x2cd6,
+ 0x2cd8, 0x2cd8,
+ 0x2cda, 0x2cda,
+ 0x2cdc, 0x2cdc,
+ 0x2cde, 0x2cde,
+ 0x2ce0, 0x2ce0,
+ 0x2ce2, 0x2ce2,
+ 0x2ceb, 0x2ceb,
+ 0x2ced, 0x2ced,
+ 0x2cf2, 0x2cf2,
+ 0xa640, 0xa640,
+ 0xa642, 0xa642,
+ 0xa644, 0xa644,
+ 0xa646, 0xa646,
+ 0xa648, 0xa648,
+ 0xa64a, 0xa64a,
+ 0xa64c, 0xa64c,
+ 0xa64e, 0xa64e,
+ 0xa650, 0xa650,
+ 0xa652, 0xa652,
+ 0xa654, 0xa654,
+ 0xa656, 0xa656,
+ 0xa658, 0xa658,
+ 0xa65a, 0xa65a,
+ 0xa65c, 0xa65c,
+ 0xa65e, 0xa65e,
+ 0xa660, 0xa660,
+ 0xa662, 0xa662,
+ 0xa664, 0xa664,
+ 0xa666, 0xa666,
+ 0xa668, 0xa668,
+ 0xa66a, 0xa66a,
+ 0xa66c, 0xa66c,
+ 0xa680, 0xa680,
+ 0xa682, 0xa682,
+ 0xa684, 0xa684,
+ 0xa686, 0xa686,
+ 0xa688, 0xa688,
+ 0xa68a, 0xa68a,
+ 0xa68c, 0xa68c,
+ 0xa68e, 0xa68e,
+ 0xa690, 0xa690,
+ 0xa692, 0xa692,
+ 0xa694, 0xa694,
+ 0xa696, 0xa696,
+ 0xa698, 0xa698,
+ 0xa69a, 0xa69a,
+ 0xa722, 0xa722,
+ 0xa724, 0xa724,
+ 0xa726, 0xa726,
+ 0xa728, 0xa728,
+ 0xa72a, 0xa72a,
+ 0xa72c, 0xa72c,
+ 0xa72e, 0xa72e,
+ 0xa732, 0xa732,
+ 0xa734, 0xa734,
+ 0xa736, 0xa736,
+ 0xa738, 0xa738,
+ 0xa73a, 0xa73a,
+ 0xa73c, 0xa73c,
+ 0xa73e, 0xa73e,
+ 0xa740, 0xa740,
+ 0xa742, 0xa742,
+ 0xa744, 0xa744,
+ 0xa746, 0xa746,
+ 0xa748, 0xa748,
+ 0xa74a, 0xa74a,
+ 0xa74c, 0xa74c,
+ 0xa74e, 0xa74e,
+ 0xa750, 0xa750,
+ 0xa752, 0xa752,
+ 0xa754, 0xa754,
+ 0xa756, 0xa756,
+ 0xa758, 0xa758,
+ 0xa75a, 0xa75a,
+ 0xa75c, 0xa75c,
+ 0xa75e, 0xa75e,
+ 0xa760, 0xa760,
+ 0xa762, 0xa762,
+ 0xa764, 0xa764,
+ 0xa766, 0xa766,
+ 0xa768, 0xa768,
+ 0xa76a, 0xa76a,
+ 0xa76c, 0xa76c,
+ 0xa76e, 0xa76e,
+ 0xa779, 0xa779,
+ 0xa77b, 0xa77b,
+ 0xa77d, 0xa77e,
+ 0xa780, 0xa780,
+ 0xa782, 0xa782,
+ 0xa784, 0xa784,
+ 0xa786, 0xa786,
+ 0xa78b, 0xa78b,
+ 0xa78d, 0xa78d,
+ 0xa790, 0xa790,
+ 0xa792, 0xa792,
+ 0xa796, 0xa796,
+ 0xa798, 0xa798,
+ 0xa79a, 0xa79a,
+ 0xa79c, 0xa79c,
+ 0xa79e, 0xa79e,
+ 0xa7a0, 0xa7a0,
+ 0xa7a2, 0xa7a2,
+ 0xa7a4, 0xa7a4,
+ 0xa7a6, 0xa7a6,
+ 0xa7a8, 0xa7a8,
+ 0xa7aa, 0xa7ae,
+ 0xa7b0, 0xa7b4,
+ 0xa7b6, 0xa7b6,
+ 0xa7b8, 0xa7b8,
+ 0xa7ba, 0xa7ba,
+ 0xa7bc, 0xa7bc,
+ 0xa7be, 0xa7be,
+ 0xa7c2, 0xa7c2,
+ 0xa7c4, 0xa7c6,
+ 0xff21, 0xff3a,
+ 0x10400, 0x10427,
+ 0x104b0, 0x104d3,
+ 0x10c80, 0x10cb2,
+ 0x118a0, 0x118bf,
+ 0x16e40, 0x16e5f,
+ 0x1e900, 0x1e921,
+}; /* CR_Changes_When_Lowercased */
+
+/* 'Changes_When_Uppercased': Derived Property */
+static const OnigCodePoint CR_Changes_When_Uppercased[] = {
+ 616,
+ 0x0061, 0x007a,
+ 0x00b5, 0x00b5,
+ 0x00df, 0x00f6,
+ 0x00f8, 0x00ff,
+ 0x0101, 0x0101,
+ 0x0103, 0x0103,
+ 0x0105, 0x0105,
+ 0x0107, 0x0107,
+ 0x0109, 0x0109,
+ 0x010b, 0x010b,
+ 0x010d, 0x010d,
+ 0x010f, 0x010f,
+ 0x0111, 0x0111,
+ 0x0113, 0x0113,
+ 0x0115, 0x0115,
+ 0x0117, 0x0117,
+ 0x0119, 0x0119,
+ 0x011b, 0x011b,
+ 0x011d, 0x011d,
+ 0x011f, 0x011f,
+ 0x0121, 0x0121,
+ 0x0123, 0x0123,
+ 0x0125, 0x0125,
+ 0x0127, 0x0127,
+ 0x0129, 0x0129,
+ 0x012b, 0x012b,
+ 0x012d, 0x012d,
+ 0x012f, 0x012f,
+ 0x0131, 0x0131,
+ 0x0133, 0x0133,
+ 0x0135, 0x0135,
+ 0x0137, 0x0137,
+ 0x013a, 0x013a,
+ 0x013c, 0x013c,
+ 0x013e, 0x013e,
+ 0x0140, 0x0140,
+ 0x0142, 0x0142,
+ 0x0144, 0x0144,
+ 0x0146, 0x0146,
+ 0x0148, 0x0149,
+ 0x014b, 0x014b,
+ 0x014d, 0x014d,
+ 0x014f, 0x014f,
+ 0x0151, 0x0151,
+ 0x0153, 0x0153,
+ 0x0155, 0x0155,
+ 0x0157, 0x0157,
+ 0x0159, 0x0159,
+ 0x015b, 0x015b,
+ 0x015d, 0x015d,
+ 0x015f, 0x015f,
+ 0x0161, 0x0161,
+ 0x0163, 0x0163,
+ 0x0165, 0x0165,
+ 0x0167, 0x0167,
+ 0x0169, 0x0169,
+ 0x016b, 0x016b,
+ 0x016d, 0x016d,
+ 0x016f, 0x016f,
+ 0x0171, 0x0171,
+ 0x0173, 0x0173,
+ 0x0175, 0x0175,
+ 0x0177, 0x0177,
+ 0x017a, 0x017a,
+ 0x017c, 0x017c,
+ 0x017e, 0x0180,
+ 0x0183, 0x0183,
+ 0x0185, 0x0185,
+ 0x0188, 0x0188,
+ 0x018c, 0x018c,
+ 0x0192, 0x0192,
+ 0x0195, 0x0195,
+ 0x0199, 0x019a,
+ 0x019e, 0x019e,
+ 0x01a1, 0x01a1,
+ 0x01a3, 0x01a3,
+ 0x01a5, 0x01a5,
+ 0x01a8, 0x01a8,
+ 0x01ad, 0x01ad,
+ 0x01b0, 0x01b0,
+ 0x01b4, 0x01b4,
+ 0x01b6, 0x01b6,
+ 0x01b9, 0x01b9,
+ 0x01bd, 0x01bd,
+ 0x01bf, 0x01bf,
+ 0x01c5, 0x01c6,
+ 0x01c8, 0x01c9,
+ 0x01cb, 0x01cc,
+ 0x01ce, 0x01ce,
+ 0x01d0, 0x01d0,
+ 0x01d2, 0x01d2,
+ 0x01d4, 0x01d4,
+ 0x01d6, 0x01d6,
+ 0x01d8, 0x01d8,
+ 0x01da, 0x01da,
+ 0x01dc, 0x01dd,
+ 0x01df, 0x01df,
+ 0x01e1, 0x01e1,
+ 0x01e3, 0x01e3,
+ 0x01e5, 0x01e5,
+ 0x01e7, 0x01e7,
+ 0x01e9, 0x01e9,
+ 0x01eb, 0x01eb,
+ 0x01ed, 0x01ed,
+ 0x01ef, 0x01f0,
+ 0x01f2, 0x01f3,
+ 0x01f5, 0x01f5,
+ 0x01f9, 0x01f9,
+ 0x01fb, 0x01fb,
+ 0x01fd, 0x01fd,
+ 0x01ff, 0x01ff,
+ 0x0201, 0x0201,
+ 0x0203, 0x0203,
+ 0x0205, 0x0205,
+ 0x0207, 0x0207,
+ 0x0209, 0x0209,
+ 0x020b, 0x020b,
+ 0x020d, 0x020d,
+ 0x020f, 0x020f,
+ 0x0211, 0x0211,
+ 0x0213, 0x0213,
+ 0x0215, 0x0215,
+ 0x0217, 0x0217,
+ 0x0219, 0x0219,
+ 0x021b, 0x021b,
+ 0x021d, 0x021d,
+ 0x021f, 0x021f,
+ 0x0223, 0x0223,
+ 0x0225, 0x0225,
+ 0x0227, 0x0227,
+ 0x0229, 0x0229,
+ 0x022b, 0x022b,
+ 0x022d, 0x022d,
+ 0x022f, 0x022f,
+ 0x0231, 0x0231,
+ 0x0233, 0x0233,
+ 0x023c, 0x023c,
+ 0x023f, 0x0240,
+ 0x0242, 0x0242,
+ 0x0247, 0x0247,
+ 0x0249, 0x0249,
+ 0x024b, 0x024b,
+ 0x024d, 0x024d,
+ 0x024f, 0x0254,
+ 0x0256, 0x0257,
+ 0x0259, 0x0259,
+ 0x025b, 0x025c,
+ 0x0260, 0x0261,
+ 0x0263, 0x0263,
+ 0x0265, 0x0266,
+ 0x0268, 0x026c,
+ 0x026f, 0x026f,
+ 0x0271, 0x0272,
+ 0x0275, 0x0275,
+ 0x027d, 0x027d,
+ 0x0280, 0x0280,
+ 0x0282, 0x0283,
+ 0x0287, 0x028c,
+ 0x0292, 0x0292,
+ 0x029d, 0x029e,
+ 0x0345, 0x0345,
+ 0x0371, 0x0371,
+ 0x0373, 0x0373,
+ 0x0377, 0x0377,
+ 0x037b, 0x037d,
+ 0x0390, 0x0390,
+ 0x03ac, 0x03ce,
+ 0x03d0, 0x03d1,
+ 0x03d5, 0x03d7,
+ 0x03d9, 0x03d9,
+ 0x03db, 0x03db,
+ 0x03dd, 0x03dd,
+ 0x03df, 0x03df,
+ 0x03e1, 0x03e1,
+ 0x03e3, 0x03e3,
+ 0x03e5, 0x03e5,
+ 0x03e7, 0x03e7,
+ 0x03e9, 0x03e9,
+ 0x03eb, 0x03eb,
+ 0x03ed, 0x03ed,
+ 0x03ef, 0x03f3,
+ 0x03f5, 0x03f5,
+ 0x03f8, 0x03f8,
+ 0x03fb, 0x03fb,
+ 0x0430, 0x045f,
+ 0x0461, 0x0461,
+ 0x0463, 0x0463,
+ 0x0465, 0x0465,
+ 0x0467, 0x0467,
+ 0x0469, 0x0469,
+ 0x046b, 0x046b,
+ 0x046d, 0x046d,
+ 0x046f, 0x046f,
+ 0x0471, 0x0471,
+ 0x0473, 0x0473,
+ 0x0475, 0x0475,
+ 0x0477, 0x0477,
+ 0x0479, 0x0479,
+ 0x047b, 0x047b,
+ 0x047d, 0x047d,
+ 0x047f, 0x047f,
+ 0x0481, 0x0481,
+ 0x048b, 0x048b,
+ 0x048d, 0x048d,
+ 0x048f, 0x048f,
+ 0x0491, 0x0491,
+ 0x0493, 0x0493,
+ 0x0495, 0x0495,
+ 0x0497, 0x0497,
+ 0x0499, 0x0499,
+ 0x049b, 0x049b,
+ 0x049d, 0x049d,
+ 0x049f, 0x049f,
+ 0x04a1, 0x04a1,
+ 0x04a3, 0x04a3,
+ 0x04a5, 0x04a5,
+ 0x04a7, 0x04a7,
+ 0x04a9, 0x04a9,
+ 0x04ab, 0x04ab,
+ 0x04ad, 0x04ad,
+ 0x04af, 0x04af,
+ 0x04b1, 0x04b1,
+ 0x04b3, 0x04b3,
+ 0x04b5, 0x04b5,
+ 0x04b7, 0x04b7,
+ 0x04b9, 0x04b9,
+ 0x04bb, 0x04bb,
+ 0x04bd, 0x04bd,
+ 0x04bf, 0x04bf,
+ 0x04c2, 0x04c2,
+ 0x04c4, 0x04c4,
+ 0x04c6, 0x04c6,
+ 0x04c8, 0x04c8,
+ 0x04ca, 0x04ca,
+ 0x04cc, 0x04cc,
+ 0x04ce, 0x04cf,
+ 0x04d1, 0x04d1,
+ 0x04d3, 0x04d3,
+ 0x04d5, 0x04d5,
+ 0x04d7, 0x04d7,
+ 0x04d9, 0x04d9,
+ 0x04db, 0x04db,
+ 0x04dd, 0x04dd,
+ 0x04df, 0x04df,
+ 0x04e1, 0x04e1,
+ 0x04e3, 0x04e3,
+ 0x04e5, 0x04e5,
+ 0x04e7, 0x04e7,
+ 0x04e9, 0x04e9,
+ 0x04eb, 0x04eb,
+ 0x04ed, 0x04ed,
+ 0x04ef, 0x04ef,
+ 0x04f1, 0x04f1,
+ 0x04f3, 0x04f3,
+ 0x04f5, 0x04f5,
+ 0x04f7, 0x04f7,
+ 0x04f9, 0x04f9,
+ 0x04fb, 0x04fb,
+ 0x04fd, 0x04fd,
+ 0x04ff, 0x04ff,
+ 0x0501, 0x0501,
+ 0x0503, 0x0503,
+ 0x0505, 0x0505,
+ 0x0507, 0x0507,
+ 0x0509, 0x0509,
+ 0x050b, 0x050b,
+ 0x050d, 0x050d,
+ 0x050f, 0x050f,
+ 0x0511, 0x0511,
+ 0x0513, 0x0513,
+ 0x0515, 0x0515,
+ 0x0517, 0x0517,
+ 0x0519, 0x0519,
+ 0x051b, 0x051b,
+ 0x051d, 0x051d,
+ 0x051f, 0x051f,
+ 0x0521, 0x0521,
+ 0x0523, 0x0523,
+ 0x0525, 0x0525,
+ 0x0527, 0x0527,
+ 0x0529, 0x0529,
+ 0x052b, 0x052b,
+ 0x052d, 0x052d,
+ 0x052f, 0x052f,
+ 0x0561, 0x0587,
+ 0x10d0, 0x10fa,
+ 0x10fd, 0x10ff,
+ 0x13f8, 0x13fd,
+ 0x1c80, 0x1c88,
+ 0x1d79, 0x1d79,
+ 0x1d7d, 0x1d7d,
+ 0x1d8e, 0x1d8e,
+ 0x1e01, 0x1e01,
+ 0x1e03, 0x1e03,
+ 0x1e05, 0x1e05,
+ 0x1e07, 0x1e07,
+ 0x1e09, 0x1e09,
+ 0x1e0b, 0x1e0b,
+ 0x1e0d, 0x1e0d,
+ 0x1e0f, 0x1e0f,
+ 0x1e11, 0x1e11,
+ 0x1e13, 0x1e13,
+ 0x1e15, 0x1e15,
+ 0x1e17, 0x1e17,
+ 0x1e19, 0x1e19,
+ 0x1e1b, 0x1e1b,
+ 0x1e1d, 0x1e1d,
+ 0x1e1f, 0x1e1f,
+ 0x1e21, 0x1e21,
+ 0x1e23, 0x1e23,
+ 0x1e25, 0x1e25,
+ 0x1e27, 0x1e27,
+ 0x1e29, 0x1e29,
+ 0x1e2b, 0x1e2b,
+ 0x1e2d, 0x1e2d,
+ 0x1e2f, 0x1e2f,
+ 0x1e31, 0x1e31,
+ 0x1e33, 0x1e33,
+ 0x1e35, 0x1e35,
+ 0x1e37, 0x1e37,
+ 0x1e39, 0x1e39,
+ 0x1e3b, 0x1e3b,
+ 0x1e3d, 0x1e3d,
+ 0x1e3f, 0x1e3f,
+ 0x1e41, 0x1e41,
+ 0x1e43, 0x1e43,
+ 0x1e45, 0x1e45,
+ 0x1e47, 0x1e47,
+ 0x1e49, 0x1e49,
+ 0x1e4b, 0x1e4b,
+ 0x1e4d, 0x1e4d,
+ 0x1e4f, 0x1e4f,
+ 0x1e51, 0x1e51,
+ 0x1e53, 0x1e53,
+ 0x1e55, 0x1e55,
+ 0x1e57, 0x1e57,
+ 0x1e59, 0x1e59,
+ 0x1e5b, 0x1e5b,
+ 0x1e5d, 0x1e5d,
+ 0x1e5f, 0x1e5f,
+ 0x1e61, 0x1e61,
+ 0x1e63, 0x1e63,
+ 0x1e65, 0x1e65,
+ 0x1e67, 0x1e67,
+ 0x1e69, 0x1e69,
+ 0x1e6b, 0x1e6b,
+ 0x1e6d, 0x1e6d,
+ 0x1e6f, 0x1e6f,
+ 0x1e71, 0x1e71,
+ 0x1e73, 0x1e73,
+ 0x1e75, 0x1e75,
+ 0x1e77, 0x1e77,
+ 0x1e79, 0x1e79,
+ 0x1e7b, 0x1e7b,
+ 0x1e7d, 0x1e7d,
+ 0x1e7f, 0x1e7f,
+ 0x1e81, 0x1e81,
+ 0x1e83, 0x1e83,
+ 0x1e85, 0x1e85,
+ 0x1e87, 0x1e87,
+ 0x1e89, 0x1e89,
+ 0x1e8b, 0x1e8b,
+ 0x1e8d, 0x1e8d,
+ 0x1e8f, 0x1e8f,
+ 0x1e91, 0x1e91,
+ 0x1e93, 0x1e93,
+ 0x1e95, 0x1e9b,
+ 0x1ea1, 0x1ea1,
+ 0x1ea3, 0x1ea3,
+ 0x1ea5, 0x1ea5,
+ 0x1ea7, 0x1ea7,
+ 0x1ea9, 0x1ea9,
+ 0x1eab, 0x1eab,
+ 0x1ead, 0x1ead,
+ 0x1eaf, 0x1eaf,
+ 0x1eb1, 0x1eb1,
+ 0x1eb3, 0x1eb3,
+ 0x1eb5, 0x1eb5,
+ 0x1eb7, 0x1eb7,
+ 0x1eb9, 0x1eb9,
+ 0x1ebb, 0x1ebb,
+ 0x1ebd, 0x1ebd,
+ 0x1ebf, 0x1ebf,
+ 0x1ec1, 0x1ec1,
+ 0x1ec3, 0x1ec3,
+ 0x1ec5, 0x1ec5,
+ 0x1ec7, 0x1ec7,
+ 0x1ec9, 0x1ec9,
+ 0x1ecb, 0x1ecb,
+ 0x1ecd, 0x1ecd,
+ 0x1ecf, 0x1ecf,
+ 0x1ed1, 0x1ed1,
+ 0x1ed3, 0x1ed3,
+ 0x1ed5, 0x1ed5,
+ 0x1ed7, 0x1ed7,
+ 0x1ed9, 0x1ed9,
+ 0x1edb, 0x1edb,
+ 0x1edd, 0x1edd,
+ 0x1edf, 0x1edf,
+ 0x1ee1, 0x1ee1,
+ 0x1ee3, 0x1ee3,
+ 0x1ee5, 0x1ee5,
+ 0x1ee7, 0x1ee7,
+ 0x1ee9, 0x1ee9,
+ 0x1eeb, 0x1eeb,
+ 0x1eed, 0x1eed,
+ 0x1eef, 0x1eef,
+ 0x1ef1, 0x1ef1,
+ 0x1ef3, 0x1ef3,
+ 0x1ef5, 0x1ef5,
+ 0x1ef7, 0x1ef7,
+ 0x1ef9, 0x1ef9,
+ 0x1efb, 0x1efb,
+ 0x1efd, 0x1efd,
+ 0x1eff, 0x1f07,
+ 0x1f10, 0x1f15,
+ 0x1f20, 0x1f27,
+ 0x1f30, 0x1f37,
+ 0x1f40, 0x1f45,
+ 0x1f50, 0x1f57,
+ 0x1f60, 0x1f67,
+ 0x1f70, 0x1f7d,
+ 0x1f80, 0x1fb4,
+ 0x1fb6, 0x1fb7,
+ 0x1fbc, 0x1fbc,
+ 0x1fbe, 0x1fbe,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fc7,
+ 0x1fcc, 0x1fcc,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fd7,
+ 0x1fe0, 0x1fe7,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ff7,
+ 0x1ffc, 0x1ffc,
+ 0x214e, 0x214e,
+ 0x2170, 0x217f,
+ 0x2184, 0x2184,
+ 0x24d0, 0x24e9,
+ 0x2c30, 0x2c5e,
+ 0x2c61, 0x2c61,
+ 0x2c65, 0x2c66,
+ 0x2c68, 0x2c68,
+ 0x2c6a, 0x2c6a,
+ 0x2c6c, 0x2c6c,
+ 0x2c73, 0x2c73,
+ 0x2c76, 0x2c76,
+ 0x2c81, 0x2c81,
+ 0x2c83, 0x2c83,
+ 0x2c85, 0x2c85,
+ 0x2c87, 0x2c87,
+ 0x2c89, 0x2c89,
+ 0x2c8b, 0x2c8b,
+ 0x2c8d, 0x2c8d,
+ 0x2c8f, 0x2c8f,
+ 0x2c91, 0x2c91,
+ 0x2c93, 0x2c93,
+ 0x2c95, 0x2c95,
+ 0x2c97, 0x2c97,
+ 0x2c99, 0x2c99,
+ 0x2c9b, 0x2c9b,
+ 0x2c9d, 0x2c9d,
+ 0x2c9f, 0x2c9f,
+ 0x2ca1, 0x2ca1,
+ 0x2ca3, 0x2ca3,
+ 0x2ca5, 0x2ca5,
+ 0x2ca7, 0x2ca7,
+ 0x2ca9, 0x2ca9,
+ 0x2cab, 0x2cab,
+ 0x2cad, 0x2cad,
+ 0x2caf, 0x2caf,
+ 0x2cb1, 0x2cb1,
+ 0x2cb3, 0x2cb3,
+ 0x2cb5, 0x2cb5,
+ 0x2cb7, 0x2cb7,
+ 0x2cb9, 0x2cb9,
+ 0x2cbb, 0x2cbb,
+ 0x2cbd, 0x2cbd,
+ 0x2cbf, 0x2cbf,
+ 0x2cc1, 0x2cc1,
+ 0x2cc3, 0x2cc3,
+ 0x2cc5, 0x2cc5,
+ 0x2cc7, 0x2cc7,
+ 0x2cc9, 0x2cc9,
+ 0x2ccb, 0x2ccb,
+ 0x2ccd, 0x2ccd,
+ 0x2ccf, 0x2ccf,
+ 0x2cd1, 0x2cd1,
+ 0x2cd3, 0x2cd3,
+ 0x2cd5, 0x2cd5,
+ 0x2cd7, 0x2cd7,
+ 0x2cd9, 0x2cd9,
+ 0x2cdb, 0x2cdb,
+ 0x2cdd, 0x2cdd,
+ 0x2cdf, 0x2cdf,
+ 0x2ce1, 0x2ce1,
+ 0x2ce3, 0x2ce3,
+ 0x2cec, 0x2cec,
+ 0x2cee, 0x2cee,
+ 0x2cf3, 0x2cf3,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0xa641, 0xa641,
+ 0xa643, 0xa643,
+ 0xa645, 0xa645,
+ 0xa647, 0xa647,
+ 0xa649, 0xa649,
+ 0xa64b, 0xa64b,
+ 0xa64d, 0xa64d,
+ 0xa64f, 0xa64f,
+ 0xa651, 0xa651,
+ 0xa653, 0xa653,
+ 0xa655, 0xa655,
+ 0xa657, 0xa657,
+ 0xa659, 0xa659,
+ 0xa65b, 0xa65b,
+ 0xa65d, 0xa65d,
+ 0xa65f, 0xa65f,
+ 0xa661, 0xa661,
+ 0xa663, 0xa663,
+ 0xa665, 0xa665,
+ 0xa667, 0xa667,
+ 0xa669, 0xa669,
+ 0xa66b, 0xa66b,
+ 0xa66d, 0xa66d,
+ 0xa681, 0xa681,
+ 0xa683, 0xa683,
+ 0xa685, 0xa685,
+ 0xa687, 0xa687,
+ 0xa689, 0xa689,
+ 0xa68b, 0xa68b,
+ 0xa68d, 0xa68d,
+ 0xa68f, 0xa68f,
+ 0xa691, 0xa691,
+ 0xa693, 0xa693,
+ 0xa695, 0xa695,
+ 0xa697, 0xa697,
+ 0xa699, 0xa699,
+ 0xa69b, 0xa69b,
+ 0xa723, 0xa723,
+ 0xa725, 0xa725,
+ 0xa727, 0xa727,
+ 0xa729, 0xa729,
+ 0xa72b, 0xa72b,
+ 0xa72d, 0xa72d,
+ 0xa72f, 0xa72f,
+ 0xa733, 0xa733,
+ 0xa735, 0xa735,
+ 0xa737, 0xa737,
+ 0xa739, 0xa739,
+ 0xa73b, 0xa73b,
+ 0xa73d, 0xa73d,
+ 0xa73f, 0xa73f,
+ 0xa741, 0xa741,
+ 0xa743, 0xa743,
+ 0xa745, 0xa745,
+ 0xa747, 0xa747,
+ 0xa749, 0xa749,
+ 0xa74b, 0xa74b,
+ 0xa74d, 0xa74d,
+ 0xa74f, 0xa74f,
+ 0xa751, 0xa751,
+ 0xa753, 0xa753,
+ 0xa755, 0xa755,
+ 0xa757, 0xa757,
+ 0xa759, 0xa759,
+ 0xa75b, 0xa75b,
+ 0xa75d, 0xa75d,
+ 0xa75f, 0xa75f,
+ 0xa761, 0xa761,
+ 0xa763, 0xa763,
+ 0xa765, 0xa765,
+ 0xa767, 0xa767,
+ 0xa769, 0xa769,
+ 0xa76b, 0xa76b,
+ 0xa76d, 0xa76d,
+ 0xa76f, 0xa76f,
+ 0xa77a, 0xa77a,
+ 0xa77c, 0xa77c,
+ 0xa77f, 0xa77f,
+ 0xa781, 0xa781,
+ 0xa783, 0xa783,
+ 0xa785, 0xa785,
+ 0xa787, 0xa787,
+ 0xa78c, 0xa78c,
+ 0xa791, 0xa791,
+ 0xa793, 0xa794,
+ 0xa797, 0xa797,
+ 0xa799, 0xa799,
+ 0xa79b, 0xa79b,
+ 0xa79d, 0xa79d,
+ 0xa79f, 0xa79f,
+ 0xa7a1, 0xa7a1,
+ 0xa7a3, 0xa7a3,
+ 0xa7a5, 0xa7a5,
+ 0xa7a7, 0xa7a7,
+ 0xa7a9, 0xa7a9,
+ 0xa7b5, 0xa7b5,
+ 0xa7b7, 0xa7b7,
+ 0xa7b9, 0xa7b9,
+ 0xa7bb, 0xa7bb,
+ 0xa7bd, 0xa7bd,
+ 0xa7bf, 0xa7bf,
+ 0xa7c3, 0xa7c3,
+ 0xab53, 0xab53,
+ 0xab70, 0xabbf,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xff41, 0xff5a,
+ 0x10428, 0x1044f,
+ 0x104d8, 0x104fb,
+ 0x10cc0, 0x10cf2,
+ 0x118c0, 0x118df,
+ 0x16e60, 0x16e7f,
+ 0x1e922, 0x1e943,
+}; /* CR_Changes_When_Uppercased */
+
+/* 'Changes_When_Titlecased': Derived Property */
+static const OnigCodePoint CR_Changes_When_Titlecased[] = {
+ 615,
+ 0x0061, 0x007a,
+ 0x00b5, 0x00b5,
+ 0x00df, 0x00f6,
+ 0x00f8, 0x00ff,
+ 0x0101, 0x0101,
+ 0x0103, 0x0103,
+ 0x0105, 0x0105,
+ 0x0107, 0x0107,
+ 0x0109, 0x0109,
+ 0x010b, 0x010b,
+ 0x010d, 0x010d,
+ 0x010f, 0x010f,
+ 0x0111, 0x0111,
+ 0x0113, 0x0113,
+ 0x0115, 0x0115,
+ 0x0117, 0x0117,
+ 0x0119, 0x0119,
+ 0x011b, 0x011b,
+ 0x011d, 0x011d,
+ 0x011f, 0x011f,
+ 0x0121, 0x0121,
+ 0x0123, 0x0123,
+ 0x0125, 0x0125,
+ 0x0127, 0x0127,
+ 0x0129, 0x0129,
+ 0x012b, 0x012b,
+ 0x012d, 0x012d,
+ 0x012f, 0x012f,
+ 0x0131, 0x0131,
+ 0x0133, 0x0133,
+ 0x0135, 0x0135,
+ 0x0137, 0x0137,
+ 0x013a, 0x013a,
+ 0x013c, 0x013c,
+ 0x013e, 0x013e,
+ 0x0140, 0x0140,
+ 0x0142, 0x0142,
+ 0x0144, 0x0144,
+ 0x0146, 0x0146,
+ 0x0148, 0x0149,
+ 0x014b, 0x014b,
+ 0x014d, 0x014d,
+ 0x014f, 0x014f,
+ 0x0151, 0x0151,
+ 0x0153, 0x0153,
+ 0x0155, 0x0155,
+ 0x0157, 0x0157,
+ 0x0159, 0x0159,
+ 0x015b, 0x015b,
+ 0x015d, 0x015d,
+ 0x015f, 0x015f,
+ 0x0161, 0x0161,
+ 0x0163, 0x0163,
+ 0x0165, 0x0165,
+ 0x0167, 0x0167,
+ 0x0169, 0x0169,
+ 0x016b, 0x016b,
+ 0x016d, 0x016d,
+ 0x016f, 0x016f,
+ 0x0171, 0x0171,
+ 0x0173, 0x0173,
+ 0x0175, 0x0175,
+ 0x0177, 0x0177,
+ 0x017a, 0x017a,
+ 0x017c, 0x017c,
+ 0x017e, 0x0180,
+ 0x0183, 0x0183,
+ 0x0185, 0x0185,
+ 0x0188, 0x0188,
+ 0x018c, 0x018c,
+ 0x0192, 0x0192,
+ 0x0195, 0x0195,
+ 0x0199, 0x019a,
+ 0x019e, 0x019e,
+ 0x01a1, 0x01a1,
+ 0x01a3, 0x01a3,
+ 0x01a5, 0x01a5,
+ 0x01a8, 0x01a8,
+ 0x01ad, 0x01ad,
+ 0x01b0, 0x01b0,
+ 0x01b4, 0x01b4,
+ 0x01b6, 0x01b6,
+ 0x01b9, 0x01b9,
+ 0x01bd, 0x01bd,
+ 0x01bf, 0x01bf,
+ 0x01c4, 0x01c4,
+ 0x01c6, 0x01c7,
+ 0x01c9, 0x01ca,
+ 0x01cc, 0x01cc,
+ 0x01ce, 0x01ce,
+ 0x01d0, 0x01d0,
+ 0x01d2, 0x01d2,
+ 0x01d4, 0x01d4,
+ 0x01d6, 0x01d6,
+ 0x01d8, 0x01d8,
+ 0x01da, 0x01da,
+ 0x01dc, 0x01dd,
+ 0x01df, 0x01df,
+ 0x01e1, 0x01e1,
+ 0x01e3, 0x01e3,
+ 0x01e5, 0x01e5,
+ 0x01e7, 0x01e7,
+ 0x01e9, 0x01e9,
+ 0x01eb, 0x01eb,
+ 0x01ed, 0x01ed,
+ 0x01ef, 0x01f1,
+ 0x01f3, 0x01f3,
+ 0x01f5, 0x01f5,
+ 0x01f9, 0x01f9,
+ 0x01fb, 0x01fb,
+ 0x01fd, 0x01fd,
+ 0x01ff, 0x01ff,
+ 0x0201, 0x0201,
+ 0x0203, 0x0203,
+ 0x0205, 0x0205,
+ 0x0207, 0x0207,
+ 0x0209, 0x0209,
+ 0x020b, 0x020b,
+ 0x020d, 0x020d,
+ 0x020f, 0x020f,
+ 0x0211, 0x0211,
+ 0x0213, 0x0213,
+ 0x0215, 0x0215,
+ 0x0217, 0x0217,
+ 0x0219, 0x0219,
+ 0x021b, 0x021b,
+ 0x021d, 0x021d,
+ 0x021f, 0x021f,
+ 0x0223, 0x0223,
+ 0x0225, 0x0225,
+ 0x0227, 0x0227,
+ 0x0229, 0x0229,
+ 0x022b, 0x022b,
+ 0x022d, 0x022d,
+ 0x022f, 0x022f,
+ 0x0231, 0x0231,
+ 0x0233, 0x0233,
+ 0x023c, 0x023c,
+ 0x023f, 0x0240,
+ 0x0242, 0x0242,
+ 0x0247, 0x0247,
+ 0x0249, 0x0249,
+ 0x024b, 0x024b,
+ 0x024d, 0x024d,
+ 0x024f, 0x0254,
+ 0x0256, 0x0257,
+ 0x0259, 0x0259,
+ 0x025b, 0x025c,
+ 0x0260, 0x0261,
+ 0x0263, 0x0263,
+ 0x0265, 0x0266,
+ 0x0268, 0x026c,
+ 0x026f, 0x026f,
+ 0x0271, 0x0272,
+ 0x0275, 0x0275,
+ 0x027d, 0x027d,
+ 0x0280, 0x0280,
+ 0x0282, 0x0283,
+ 0x0287, 0x028c,
+ 0x0292, 0x0292,
+ 0x029d, 0x029e,
+ 0x0345, 0x0345,
+ 0x0371, 0x0371,
+ 0x0373, 0x0373,
+ 0x0377, 0x0377,
+ 0x037b, 0x037d,
+ 0x0390, 0x0390,
+ 0x03ac, 0x03ce,
+ 0x03d0, 0x03d1,
+ 0x03d5, 0x03d7,
+ 0x03d9, 0x03d9,
+ 0x03db, 0x03db,
+ 0x03dd, 0x03dd,
+ 0x03df, 0x03df,
+ 0x03e1, 0x03e1,
+ 0x03e3, 0x03e3,
+ 0x03e5, 0x03e5,
+ 0x03e7, 0x03e7,
+ 0x03e9, 0x03e9,
+ 0x03eb, 0x03eb,
+ 0x03ed, 0x03ed,
+ 0x03ef, 0x03f3,
+ 0x03f5, 0x03f5,
+ 0x03f8, 0x03f8,
+ 0x03fb, 0x03fb,
+ 0x0430, 0x045f,
+ 0x0461, 0x0461,
+ 0x0463, 0x0463,
+ 0x0465, 0x0465,
+ 0x0467, 0x0467,
+ 0x0469, 0x0469,
+ 0x046b, 0x046b,
+ 0x046d, 0x046d,
+ 0x046f, 0x046f,
+ 0x0471, 0x0471,
+ 0x0473, 0x0473,
+ 0x0475, 0x0475,
+ 0x0477, 0x0477,
+ 0x0479, 0x0479,
+ 0x047b, 0x047b,
+ 0x047d, 0x047d,
+ 0x047f, 0x047f,
+ 0x0481, 0x0481,
+ 0x048b, 0x048b,
+ 0x048d, 0x048d,
+ 0x048f, 0x048f,
+ 0x0491, 0x0491,
+ 0x0493, 0x0493,
+ 0x0495, 0x0495,
+ 0x0497, 0x0497,
+ 0x0499, 0x0499,
+ 0x049b, 0x049b,
+ 0x049d, 0x049d,
+ 0x049f, 0x049f,
+ 0x04a1, 0x04a1,
+ 0x04a3, 0x04a3,
+ 0x04a5, 0x04a5,
+ 0x04a7, 0x04a7,
+ 0x04a9, 0x04a9,
+ 0x04ab, 0x04ab,
+ 0x04ad, 0x04ad,
+ 0x04af, 0x04af,
+ 0x04b1, 0x04b1,
+ 0x04b3, 0x04b3,
+ 0x04b5, 0x04b5,
+ 0x04b7, 0x04b7,
+ 0x04b9, 0x04b9,
+ 0x04bb, 0x04bb,
+ 0x04bd, 0x04bd,
+ 0x04bf, 0x04bf,
+ 0x04c2, 0x04c2,
+ 0x04c4, 0x04c4,
+ 0x04c6, 0x04c6,
+ 0x04c8, 0x04c8,
+ 0x04ca, 0x04ca,
+ 0x04cc, 0x04cc,
+ 0x04ce, 0x04cf,
+ 0x04d1, 0x04d1,
+ 0x04d3, 0x04d3,
+ 0x04d5, 0x04d5,
+ 0x04d7, 0x04d7,
+ 0x04d9, 0x04d9,
+ 0x04db, 0x04db,
+ 0x04dd, 0x04dd,
+ 0x04df, 0x04df,
+ 0x04e1, 0x04e1,
+ 0x04e3, 0x04e3,
+ 0x04e5, 0x04e5,
+ 0x04e7, 0x04e7,
+ 0x04e9, 0x04e9,
+ 0x04eb, 0x04eb,
+ 0x04ed, 0x04ed,
+ 0x04ef, 0x04ef,
+ 0x04f1, 0x04f1,
+ 0x04f3, 0x04f3,
+ 0x04f5, 0x04f5,
+ 0x04f7, 0x04f7,
+ 0x04f9, 0x04f9,
+ 0x04fb, 0x04fb,
+ 0x04fd, 0x04fd,
+ 0x04ff, 0x04ff,
+ 0x0501, 0x0501,
+ 0x0503, 0x0503,
+ 0x0505, 0x0505,
+ 0x0507, 0x0507,
+ 0x0509, 0x0509,
+ 0x050b, 0x050b,
+ 0x050d, 0x050d,
+ 0x050f, 0x050f,
+ 0x0511, 0x0511,
+ 0x0513, 0x0513,
+ 0x0515, 0x0515,
+ 0x0517, 0x0517,
+ 0x0519, 0x0519,
+ 0x051b, 0x051b,
+ 0x051d, 0x051d,
+ 0x051f, 0x051f,
+ 0x0521, 0x0521,
+ 0x0523, 0x0523,
+ 0x0525, 0x0525,
+ 0x0527, 0x0527,
+ 0x0529, 0x0529,
+ 0x052b, 0x052b,
+ 0x052d, 0x052d,
+ 0x052f, 0x052f,
+ 0x0561, 0x0587,
+ 0x13f8, 0x13fd,
+ 0x1c80, 0x1c88,
+ 0x1d79, 0x1d79,
+ 0x1d7d, 0x1d7d,
+ 0x1d8e, 0x1d8e,
+ 0x1e01, 0x1e01,
+ 0x1e03, 0x1e03,
+ 0x1e05, 0x1e05,
+ 0x1e07, 0x1e07,
+ 0x1e09, 0x1e09,
+ 0x1e0b, 0x1e0b,
+ 0x1e0d, 0x1e0d,
+ 0x1e0f, 0x1e0f,
+ 0x1e11, 0x1e11,
+ 0x1e13, 0x1e13,
+ 0x1e15, 0x1e15,
+ 0x1e17, 0x1e17,
+ 0x1e19, 0x1e19,
+ 0x1e1b, 0x1e1b,
+ 0x1e1d, 0x1e1d,
+ 0x1e1f, 0x1e1f,
+ 0x1e21, 0x1e21,
+ 0x1e23, 0x1e23,
+ 0x1e25, 0x1e25,
+ 0x1e27, 0x1e27,
+ 0x1e29, 0x1e29,
+ 0x1e2b, 0x1e2b,
+ 0x1e2d, 0x1e2d,
+ 0x1e2f, 0x1e2f,
+ 0x1e31, 0x1e31,
+ 0x1e33, 0x1e33,
+ 0x1e35, 0x1e35,
+ 0x1e37, 0x1e37,
+ 0x1e39, 0x1e39,
+ 0x1e3b, 0x1e3b,
+ 0x1e3d, 0x1e3d,
+ 0x1e3f, 0x1e3f,
+ 0x1e41, 0x1e41,
+ 0x1e43, 0x1e43,
+ 0x1e45, 0x1e45,
+ 0x1e47, 0x1e47,
+ 0x1e49, 0x1e49,
+ 0x1e4b, 0x1e4b,
+ 0x1e4d, 0x1e4d,
+ 0x1e4f, 0x1e4f,
+ 0x1e51, 0x1e51,
+ 0x1e53, 0x1e53,
+ 0x1e55, 0x1e55,
+ 0x1e57, 0x1e57,
+ 0x1e59, 0x1e59,
+ 0x1e5b, 0x1e5b,
+ 0x1e5d, 0x1e5d,
+ 0x1e5f, 0x1e5f,
+ 0x1e61, 0x1e61,
+ 0x1e63, 0x1e63,
+ 0x1e65, 0x1e65,
+ 0x1e67, 0x1e67,
+ 0x1e69, 0x1e69,
+ 0x1e6b, 0x1e6b,
+ 0x1e6d, 0x1e6d,
+ 0x1e6f, 0x1e6f,
+ 0x1e71, 0x1e71,
+ 0x1e73, 0x1e73,
+ 0x1e75, 0x1e75,
+ 0x1e77, 0x1e77,
+ 0x1e79, 0x1e79,
+ 0x1e7b, 0x1e7b,
+ 0x1e7d, 0x1e7d,
+ 0x1e7f, 0x1e7f,
+ 0x1e81, 0x1e81,
+ 0x1e83, 0x1e83,
+ 0x1e85, 0x1e85,
+ 0x1e87, 0x1e87,
+ 0x1e89, 0x1e89,
+ 0x1e8b, 0x1e8b,
+ 0x1e8d, 0x1e8d,
+ 0x1e8f, 0x1e8f,
+ 0x1e91, 0x1e91,
+ 0x1e93, 0x1e93,
+ 0x1e95, 0x1e9b,
+ 0x1ea1, 0x1ea1,
+ 0x1ea3, 0x1ea3,
+ 0x1ea5, 0x1ea5,
+ 0x1ea7, 0x1ea7,
+ 0x1ea9, 0x1ea9,
+ 0x1eab, 0x1eab,
+ 0x1ead, 0x1ead,
+ 0x1eaf, 0x1eaf,
+ 0x1eb1, 0x1eb1,
+ 0x1eb3, 0x1eb3,
+ 0x1eb5, 0x1eb5,
+ 0x1eb7, 0x1eb7,
+ 0x1eb9, 0x1eb9,
+ 0x1ebb, 0x1ebb,
+ 0x1ebd, 0x1ebd,
+ 0x1ebf, 0x1ebf,
+ 0x1ec1, 0x1ec1,
+ 0x1ec3, 0x1ec3,
+ 0x1ec5, 0x1ec5,
+ 0x1ec7, 0x1ec7,
+ 0x1ec9, 0x1ec9,
+ 0x1ecb, 0x1ecb,
+ 0x1ecd, 0x1ecd,
+ 0x1ecf, 0x1ecf,
+ 0x1ed1, 0x1ed1,
+ 0x1ed3, 0x1ed3,
+ 0x1ed5, 0x1ed5,
+ 0x1ed7, 0x1ed7,
+ 0x1ed9, 0x1ed9,
+ 0x1edb, 0x1edb,
+ 0x1edd, 0x1edd,
+ 0x1edf, 0x1edf,
+ 0x1ee1, 0x1ee1,
+ 0x1ee3, 0x1ee3,
+ 0x1ee5, 0x1ee5,
+ 0x1ee7, 0x1ee7,
+ 0x1ee9, 0x1ee9,
+ 0x1eeb, 0x1eeb,
+ 0x1eed, 0x1eed,
+ 0x1eef, 0x1eef,
+ 0x1ef1, 0x1ef1,
+ 0x1ef3, 0x1ef3,
+ 0x1ef5, 0x1ef5,
+ 0x1ef7, 0x1ef7,
+ 0x1ef9, 0x1ef9,
+ 0x1efb, 0x1efb,
+ 0x1efd, 0x1efd,
+ 0x1eff, 0x1f07,
+ 0x1f10, 0x1f15,
+ 0x1f20, 0x1f27,
+ 0x1f30, 0x1f37,
+ 0x1f40, 0x1f45,
+ 0x1f50, 0x1f57,
+ 0x1f60, 0x1f67,
+ 0x1f70, 0x1f7d,
+ 0x1f80, 0x1f87,
+ 0x1f90, 0x1f97,
+ 0x1fa0, 0x1fa7,
+ 0x1fb0, 0x1fb4,
+ 0x1fb6, 0x1fb7,
+ 0x1fbe, 0x1fbe,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fc7,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fd7,
+ 0x1fe0, 0x1fe7,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ff7,
+ 0x214e, 0x214e,
+ 0x2170, 0x217f,
+ 0x2184, 0x2184,
+ 0x24d0, 0x24e9,
+ 0x2c30, 0x2c5e,
+ 0x2c61, 0x2c61,
+ 0x2c65, 0x2c66,
+ 0x2c68, 0x2c68,
+ 0x2c6a, 0x2c6a,
+ 0x2c6c, 0x2c6c,
+ 0x2c73, 0x2c73,
+ 0x2c76, 0x2c76,
+ 0x2c81, 0x2c81,
+ 0x2c83, 0x2c83,
+ 0x2c85, 0x2c85,
+ 0x2c87, 0x2c87,
+ 0x2c89, 0x2c89,
+ 0x2c8b, 0x2c8b,
+ 0x2c8d, 0x2c8d,
+ 0x2c8f, 0x2c8f,
+ 0x2c91, 0x2c91,
+ 0x2c93, 0x2c93,
+ 0x2c95, 0x2c95,
+ 0x2c97, 0x2c97,
+ 0x2c99, 0x2c99,
+ 0x2c9b, 0x2c9b,
+ 0x2c9d, 0x2c9d,
+ 0x2c9f, 0x2c9f,
+ 0x2ca1, 0x2ca1,
+ 0x2ca3, 0x2ca3,
+ 0x2ca5, 0x2ca5,
+ 0x2ca7, 0x2ca7,
+ 0x2ca9, 0x2ca9,
+ 0x2cab, 0x2cab,
+ 0x2cad, 0x2cad,
+ 0x2caf, 0x2caf,
+ 0x2cb1, 0x2cb1,
+ 0x2cb3, 0x2cb3,
+ 0x2cb5, 0x2cb5,
+ 0x2cb7, 0x2cb7,
+ 0x2cb9, 0x2cb9,
+ 0x2cbb, 0x2cbb,
+ 0x2cbd, 0x2cbd,
+ 0x2cbf, 0x2cbf,
+ 0x2cc1, 0x2cc1,
+ 0x2cc3, 0x2cc3,
+ 0x2cc5, 0x2cc5,
+ 0x2cc7, 0x2cc7,
+ 0x2cc9, 0x2cc9,
+ 0x2ccb, 0x2ccb,
+ 0x2ccd, 0x2ccd,
+ 0x2ccf, 0x2ccf,
+ 0x2cd1, 0x2cd1,
+ 0x2cd3, 0x2cd3,
+ 0x2cd5, 0x2cd5,
+ 0x2cd7, 0x2cd7,
+ 0x2cd9, 0x2cd9,
+ 0x2cdb, 0x2cdb,
+ 0x2cdd, 0x2cdd,
+ 0x2cdf, 0x2cdf,
+ 0x2ce1, 0x2ce1,
+ 0x2ce3, 0x2ce3,
+ 0x2cec, 0x2cec,
+ 0x2cee, 0x2cee,
+ 0x2cf3, 0x2cf3,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0xa641, 0xa641,
+ 0xa643, 0xa643,
+ 0xa645, 0xa645,
+ 0xa647, 0xa647,
+ 0xa649, 0xa649,
+ 0xa64b, 0xa64b,
+ 0xa64d, 0xa64d,
+ 0xa64f, 0xa64f,
+ 0xa651, 0xa651,
+ 0xa653, 0xa653,
+ 0xa655, 0xa655,
+ 0xa657, 0xa657,
+ 0xa659, 0xa659,
+ 0xa65b, 0xa65b,
+ 0xa65d, 0xa65d,
+ 0xa65f, 0xa65f,
+ 0xa661, 0xa661,
+ 0xa663, 0xa663,
+ 0xa665, 0xa665,
+ 0xa667, 0xa667,
+ 0xa669, 0xa669,
+ 0xa66b, 0xa66b,
+ 0xa66d, 0xa66d,
+ 0xa681, 0xa681,
+ 0xa683, 0xa683,
+ 0xa685, 0xa685,
+ 0xa687, 0xa687,
+ 0xa689, 0xa689,
+ 0xa68b, 0xa68b,
+ 0xa68d, 0xa68d,
+ 0xa68f, 0xa68f,
+ 0xa691, 0xa691,
+ 0xa693, 0xa693,
+ 0xa695, 0xa695,
+ 0xa697, 0xa697,
+ 0xa699, 0xa699,
+ 0xa69b, 0xa69b,
+ 0xa723, 0xa723,
+ 0xa725, 0xa725,
+ 0xa727, 0xa727,
+ 0xa729, 0xa729,
+ 0xa72b, 0xa72b,
+ 0xa72d, 0xa72d,
+ 0xa72f, 0xa72f,
+ 0xa733, 0xa733,
+ 0xa735, 0xa735,
+ 0xa737, 0xa737,
+ 0xa739, 0xa739,
+ 0xa73b, 0xa73b,
+ 0xa73d, 0xa73d,
+ 0xa73f, 0xa73f,
+ 0xa741, 0xa741,
+ 0xa743, 0xa743,
+ 0xa745, 0xa745,
+ 0xa747, 0xa747,
+ 0xa749, 0xa749,
+ 0xa74b, 0xa74b,
+ 0xa74d, 0xa74d,
+ 0xa74f, 0xa74f,
+ 0xa751, 0xa751,
+ 0xa753, 0xa753,
+ 0xa755, 0xa755,
+ 0xa757, 0xa757,
+ 0xa759, 0xa759,
+ 0xa75b, 0xa75b,
+ 0xa75d, 0xa75d,
+ 0xa75f, 0xa75f,
+ 0xa761, 0xa761,
+ 0xa763, 0xa763,
+ 0xa765, 0xa765,
+ 0xa767, 0xa767,
+ 0xa769, 0xa769,
+ 0xa76b, 0xa76b,
+ 0xa76d, 0xa76d,
+ 0xa76f, 0xa76f,
+ 0xa77a, 0xa77a,
+ 0xa77c, 0xa77c,
+ 0xa77f, 0xa77f,
+ 0xa781, 0xa781,
+ 0xa783, 0xa783,
+ 0xa785, 0xa785,
+ 0xa787, 0xa787,
+ 0xa78c, 0xa78c,
+ 0xa791, 0xa791,
+ 0xa793, 0xa794,
+ 0xa797, 0xa797,
+ 0xa799, 0xa799,
+ 0xa79b, 0xa79b,
+ 0xa79d, 0xa79d,
+ 0xa79f, 0xa79f,
+ 0xa7a1, 0xa7a1,
+ 0xa7a3, 0xa7a3,
+ 0xa7a5, 0xa7a5,
+ 0xa7a7, 0xa7a7,
+ 0xa7a9, 0xa7a9,
+ 0xa7b5, 0xa7b5,
+ 0xa7b7, 0xa7b7,
+ 0xa7b9, 0xa7b9,
+ 0xa7bb, 0xa7bb,
+ 0xa7bd, 0xa7bd,
+ 0xa7bf, 0xa7bf,
+ 0xa7c3, 0xa7c3,
+ 0xab53, 0xab53,
+ 0xab70, 0xabbf,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xff41, 0xff5a,
+ 0x10428, 0x1044f,
+ 0x104d8, 0x104fb,
+ 0x10cc0, 0x10cf2,
+ 0x118c0, 0x118df,
+ 0x16e60, 0x16e7f,
+ 0x1e922, 0x1e943,
+}; /* CR_Changes_When_Titlecased */
+
+/* 'Changes_When_Casefolded': Derived Property */
+static const OnigCodePoint CR_Changes_When_Casefolded[] = {
+ 612,
+ 0x0041, 0x005a,
+ 0x00b5, 0x00b5,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00df,
+ 0x0100, 0x0100,
+ 0x0102, 0x0102,
+ 0x0104, 0x0104,
+ 0x0106, 0x0106,
+ 0x0108, 0x0108,
+ 0x010a, 0x010a,
+ 0x010c, 0x010c,
+ 0x010e, 0x010e,
+ 0x0110, 0x0110,
+ 0x0112, 0x0112,
+ 0x0114, 0x0114,
+ 0x0116, 0x0116,
+ 0x0118, 0x0118,
+ 0x011a, 0x011a,
+ 0x011c, 0x011c,
+ 0x011e, 0x011e,
+ 0x0120, 0x0120,
+ 0x0122, 0x0122,
+ 0x0124, 0x0124,
+ 0x0126, 0x0126,
+ 0x0128, 0x0128,
+ 0x012a, 0x012a,
+ 0x012c, 0x012c,
+ 0x012e, 0x012e,
+ 0x0130, 0x0130,
+ 0x0132, 0x0132,
+ 0x0134, 0x0134,
+ 0x0136, 0x0136,
+ 0x0139, 0x0139,
+ 0x013b, 0x013b,
+ 0x013d, 0x013d,
+ 0x013f, 0x013f,
+ 0x0141, 0x0141,
+ 0x0143, 0x0143,
+ 0x0145, 0x0145,
+ 0x0147, 0x0147,
+ 0x0149, 0x014a,
+ 0x014c, 0x014c,
+ 0x014e, 0x014e,
+ 0x0150, 0x0150,
+ 0x0152, 0x0152,
+ 0x0154, 0x0154,
+ 0x0156, 0x0156,
+ 0x0158, 0x0158,
+ 0x015a, 0x015a,
+ 0x015c, 0x015c,
+ 0x015e, 0x015e,
+ 0x0160, 0x0160,
+ 0x0162, 0x0162,
+ 0x0164, 0x0164,
+ 0x0166, 0x0166,
+ 0x0168, 0x0168,
+ 0x016a, 0x016a,
+ 0x016c, 0x016c,
+ 0x016e, 0x016e,
+ 0x0170, 0x0170,
+ 0x0172, 0x0172,
+ 0x0174, 0x0174,
+ 0x0176, 0x0176,
+ 0x0178, 0x0179,
+ 0x017b, 0x017b,
+ 0x017d, 0x017d,
+ 0x017f, 0x017f,
+ 0x0181, 0x0182,
+ 0x0184, 0x0184,
+ 0x0186, 0x0187,
+ 0x0189, 0x018b,
+ 0x018e, 0x0191,
+ 0x0193, 0x0194,
+ 0x0196, 0x0198,
+ 0x019c, 0x019d,
+ 0x019f, 0x01a0,
+ 0x01a2, 0x01a2,
+ 0x01a4, 0x01a4,
+ 0x01a6, 0x01a7,
+ 0x01a9, 0x01a9,
+ 0x01ac, 0x01ac,
+ 0x01ae, 0x01af,
+ 0x01b1, 0x01b3,
+ 0x01b5, 0x01b5,
+ 0x01b7, 0x01b8,
+ 0x01bc, 0x01bc,
+ 0x01c4, 0x01c5,
+ 0x01c7, 0x01c8,
+ 0x01ca, 0x01cb,
+ 0x01cd, 0x01cd,
+ 0x01cf, 0x01cf,
+ 0x01d1, 0x01d1,
+ 0x01d3, 0x01d3,
+ 0x01d5, 0x01d5,
+ 0x01d7, 0x01d7,
+ 0x01d9, 0x01d9,
+ 0x01db, 0x01db,
+ 0x01de, 0x01de,
+ 0x01e0, 0x01e0,
+ 0x01e2, 0x01e2,
+ 0x01e4, 0x01e4,
+ 0x01e6, 0x01e6,
+ 0x01e8, 0x01e8,
+ 0x01ea, 0x01ea,
+ 0x01ec, 0x01ec,
+ 0x01ee, 0x01ee,
+ 0x01f1, 0x01f2,
+ 0x01f4, 0x01f4,
+ 0x01f6, 0x01f8,
+ 0x01fa, 0x01fa,
+ 0x01fc, 0x01fc,
+ 0x01fe, 0x01fe,
+ 0x0200, 0x0200,
+ 0x0202, 0x0202,
+ 0x0204, 0x0204,
+ 0x0206, 0x0206,
+ 0x0208, 0x0208,
+ 0x020a, 0x020a,
+ 0x020c, 0x020c,
+ 0x020e, 0x020e,
+ 0x0210, 0x0210,
+ 0x0212, 0x0212,
+ 0x0214, 0x0214,
+ 0x0216, 0x0216,
+ 0x0218, 0x0218,
+ 0x021a, 0x021a,
+ 0x021c, 0x021c,
+ 0x021e, 0x021e,
+ 0x0220, 0x0220,
+ 0x0222, 0x0222,
+ 0x0224, 0x0224,
+ 0x0226, 0x0226,
+ 0x0228, 0x0228,
+ 0x022a, 0x022a,
+ 0x022c, 0x022c,
+ 0x022e, 0x022e,
+ 0x0230, 0x0230,
+ 0x0232, 0x0232,
+ 0x023a, 0x023b,
+ 0x023d, 0x023e,
+ 0x0241, 0x0241,
+ 0x0243, 0x0246,
+ 0x0248, 0x0248,
+ 0x024a, 0x024a,
+ 0x024c, 0x024c,
+ 0x024e, 0x024e,
+ 0x0345, 0x0345,
+ 0x0370, 0x0370,
+ 0x0372, 0x0372,
+ 0x0376, 0x0376,
+ 0x037f, 0x037f,
+ 0x0386, 0x0386,
+ 0x0388, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x038f,
+ 0x0391, 0x03a1,
+ 0x03a3, 0x03ab,
+ 0x03c2, 0x03c2,
+ 0x03cf, 0x03d1,
+ 0x03d5, 0x03d6,
+ 0x03d8, 0x03d8,
+ 0x03da, 0x03da,
+ 0x03dc, 0x03dc,
+ 0x03de, 0x03de,
+ 0x03e0, 0x03e0,
+ 0x03e2, 0x03e2,
+ 0x03e4, 0x03e4,
+ 0x03e6, 0x03e6,
+ 0x03e8, 0x03e8,
+ 0x03ea, 0x03ea,
+ 0x03ec, 0x03ec,
+ 0x03ee, 0x03ee,
+ 0x03f0, 0x03f1,
+ 0x03f4, 0x03f5,
+ 0x03f7, 0x03f7,
+ 0x03f9, 0x03fa,
+ 0x03fd, 0x042f,
+ 0x0460, 0x0460,
+ 0x0462, 0x0462,
+ 0x0464, 0x0464,
+ 0x0466, 0x0466,
+ 0x0468, 0x0468,
+ 0x046a, 0x046a,
+ 0x046c, 0x046c,
+ 0x046e, 0x046e,
+ 0x0470, 0x0470,
+ 0x0472, 0x0472,
+ 0x0474, 0x0474,
+ 0x0476, 0x0476,
+ 0x0478, 0x0478,
+ 0x047a, 0x047a,
+ 0x047c, 0x047c,
+ 0x047e, 0x047e,
+ 0x0480, 0x0480,
+ 0x048a, 0x048a,
+ 0x048c, 0x048c,
+ 0x048e, 0x048e,
+ 0x0490, 0x0490,
+ 0x0492, 0x0492,
+ 0x0494, 0x0494,
+ 0x0496, 0x0496,
+ 0x0498, 0x0498,
+ 0x049a, 0x049a,
+ 0x049c, 0x049c,
+ 0x049e, 0x049e,
+ 0x04a0, 0x04a0,
+ 0x04a2, 0x04a2,
+ 0x04a4, 0x04a4,
+ 0x04a6, 0x04a6,
+ 0x04a8, 0x04a8,
+ 0x04aa, 0x04aa,
+ 0x04ac, 0x04ac,
+ 0x04ae, 0x04ae,
+ 0x04b0, 0x04b0,
+ 0x04b2, 0x04b2,
+ 0x04b4, 0x04b4,
+ 0x04b6, 0x04b6,
+ 0x04b8, 0x04b8,
+ 0x04ba, 0x04ba,
+ 0x04bc, 0x04bc,
+ 0x04be, 0x04be,
+ 0x04c0, 0x04c1,
+ 0x04c3, 0x04c3,
+ 0x04c5, 0x04c5,
+ 0x04c7, 0x04c7,
+ 0x04c9, 0x04c9,
+ 0x04cb, 0x04cb,
+ 0x04cd, 0x04cd,
+ 0x04d0, 0x04d0,
+ 0x04d2, 0x04d2,
+ 0x04d4, 0x04d4,
+ 0x04d6, 0x04d6,
+ 0x04d8, 0x04d8,
+ 0x04da, 0x04da,
+ 0x04dc, 0x04dc,
+ 0x04de, 0x04de,
+ 0x04e0, 0x04e0,
+ 0x04e2, 0x04e2,
+ 0x04e4, 0x04e4,
+ 0x04e6, 0x04e6,
+ 0x04e8, 0x04e8,
+ 0x04ea, 0x04ea,
+ 0x04ec, 0x04ec,
+ 0x04ee, 0x04ee,
+ 0x04f0, 0x04f0,
+ 0x04f2, 0x04f2,
+ 0x04f4, 0x04f4,
+ 0x04f6, 0x04f6,
+ 0x04f8, 0x04f8,
+ 0x04fa, 0x04fa,
+ 0x04fc, 0x04fc,
+ 0x04fe, 0x04fe,
+ 0x0500, 0x0500,
+ 0x0502, 0x0502,
+ 0x0504, 0x0504,
+ 0x0506, 0x0506,
+ 0x0508, 0x0508,
+ 0x050a, 0x050a,
+ 0x050c, 0x050c,
+ 0x050e, 0x050e,
+ 0x0510, 0x0510,
+ 0x0512, 0x0512,
+ 0x0514, 0x0514,
+ 0x0516, 0x0516,
+ 0x0518, 0x0518,
+ 0x051a, 0x051a,
+ 0x051c, 0x051c,
+ 0x051e, 0x051e,
+ 0x0520, 0x0520,
+ 0x0522, 0x0522,
+ 0x0524, 0x0524,
+ 0x0526, 0x0526,
+ 0x0528, 0x0528,
+ 0x052a, 0x052a,
+ 0x052c, 0x052c,
+ 0x052e, 0x052e,
+ 0x0531, 0x0556,
+ 0x0587, 0x0587,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x13f8, 0x13fd,
+ 0x1c80, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x1e00, 0x1e00,
+ 0x1e02, 0x1e02,
+ 0x1e04, 0x1e04,
+ 0x1e06, 0x1e06,
+ 0x1e08, 0x1e08,
+ 0x1e0a, 0x1e0a,
+ 0x1e0c, 0x1e0c,
+ 0x1e0e, 0x1e0e,
+ 0x1e10, 0x1e10,
+ 0x1e12, 0x1e12,
+ 0x1e14, 0x1e14,
+ 0x1e16, 0x1e16,
+ 0x1e18, 0x1e18,
+ 0x1e1a, 0x1e1a,
+ 0x1e1c, 0x1e1c,
+ 0x1e1e, 0x1e1e,
+ 0x1e20, 0x1e20,
+ 0x1e22, 0x1e22,
+ 0x1e24, 0x1e24,
+ 0x1e26, 0x1e26,
+ 0x1e28, 0x1e28,
+ 0x1e2a, 0x1e2a,
+ 0x1e2c, 0x1e2c,
+ 0x1e2e, 0x1e2e,
+ 0x1e30, 0x1e30,
+ 0x1e32, 0x1e32,
+ 0x1e34, 0x1e34,
+ 0x1e36, 0x1e36,
+ 0x1e38, 0x1e38,
+ 0x1e3a, 0x1e3a,
+ 0x1e3c, 0x1e3c,
+ 0x1e3e, 0x1e3e,
+ 0x1e40, 0x1e40,
+ 0x1e42, 0x1e42,
+ 0x1e44, 0x1e44,
+ 0x1e46, 0x1e46,
+ 0x1e48, 0x1e48,
+ 0x1e4a, 0x1e4a,
+ 0x1e4c, 0x1e4c,
+ 0x1e4e, 0x1e4e,
+ 0x1e50, 0x1e50,
+ 0x1e52, 0x1e52,
+ 0x1e54, 0x1e54,
+ 0x1e56, 0x1e56,
+ 0x1e58, 0x1e58,
+ 0x1e5a, 0x1e5a,
+ 0x1e5c, 0x1e5c,
+ 0x1e5e, 0x1e5e,
+ 0x1e60, 0x1e60,
+ 0x1e62, 0x1e62,
+ 0x1e64, 0x1e64,
+ 0x1e66, 0x1e66,
+ 0x1e68, 0x1e68,
+ 0x1e6a, 0x1e6a,
+ 0x1e6c, 0x1e6c,
+ 0x1e6e, 0x1e6e,
+ 0x1e70, 0x1e70,
+ 0x1e72, 0x1e72,
+ 0x1e74, 0x1e74,
+ 0x1e76, 0x1e76,
+ 0x1e78, 0x1e78,
+ 0x1e7a, 0x1e7a,
+ 0x1e7c, 0x1e7c,
+ 0x1e7e, 0x1e7e,
+ 0x1e80, 0x1e80,
+ 0x1e82, 0x1e82,
+ 0x1e84, 0x1e84,
+ 0x1e86, 0x1e86,
+ 0x1e88, 0x1e88,
+ 0x1e8a, 0x1e8a,
+ 0x1e8c, 0x1e8c,
+ 0x1e8e, 0x1e8e,
+ 0x1e90, 0x1e90,
+ 0x1e92, 0x1e92,
+ 0x1e94, 0x1e94,
+ 0x1e9a, 0x1e9b,
+ 0x1e9e, 0x1e9e,
+ 0x1ea0, 0x1ea0,
+ 0x1ea2, 0x1ea2,
+ 0x1ea4, 0x1ea4,
+ 0x1ea6, 0x1ea6,
+ 0x1ea8, 0x1ea8,
+ 0x1eaa, 0x1eaa,
+ 0x1eac, 0x1eac,
+ 0x1eae, 0x1eae,
+ 0x1eb0, 0x1eb0,
+ 0x1eb2, 0x1eb2,
+ 0x1eb4, 0x1eb4,
+ 0x1eb6, 0x1eb6,
+ 0x1eb8, 0x1eb8,
+ 0x1eba, 0x1eba,
+ 0x1ebc, 0x1ebc,
+ 0x1ebe, 0x1ebe,
+ 0x1ec0, 0x1ec0,
+ 0x1ec2, 0x1ec2,
+ 0x1ec4, 0x1ec4,
+ 0x1ec6, 0x1ec6,
+ 0x1ec8, 0x1ec8,
+ 0x1eca, 0x1eca,
+ 0x1ecc, 0x1ecc,
+ 0x1ece, 0x1ece,
+ 0x1ed0, 0x1ed0,
+ 0x1ed2, 0x1ed2,
+ 0x1ed4, 0x1ed4,
+ 0x1ed6, 0x1ed6,
+ 0x1ed8, 0x1ed8,
+ 0x1eda, 0x1eda,
+ 0x1edc, 0x1edc,
+ 0x1ede, 0x1ede,
+ 0x1ee0, 0x1ee0,
+ 0x1ee2, 0x1ee2,
+ 0x1ee4, 0x1ee4,
+ 0x1ee6, 0x1ee6,
+ 0x1ee8, 0x1ee8,
+ 0x1eea, 0x1eea,
+ 0x1eec, 0x1eec,
+ 0x1eee, 0x1eee,
+ 0x1ef0, 0x1ef0,
+ 0x1ef2, 0x1ef2,
+ 0x1ef4, 0x1ef4,
+ 0x1ef6, 0x1ef6,
+ 0x1ef8, 0x1ef8,
+ 0x1efa, 0x1efa,
+ 0x1efc, 0x1efc,
+ 0x1efe, 0x1efe,
+ 0x1f08, 0x1f0f,
+ 0x1f18, 0x1f1d,
+ 0x1f28, 0x1f2f,
+ 0x1f38, 0x1f3f,
+ 0x1f48, 0x1f4d,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f5f,
+ 0x1f68, 0x1f6f,
+ 0x1f80, 0x1faf,
+ 0x1fb2, 0x1fb4,
+ 0x1fb7, 0x1fbc,
+ 0x1fc2, 0x1fc4,
+ 0x1fc7, 0x1fcc,
+ 0x1fd8, 0x1fdb,
+ 0x1fe8, 0x1fec,
+ 0x1ff2, 0x1ff4,
+ 0x1ff7, 0x1ffc,
+ 0x2126, 0x2126,
+ 0x212a, 0x212b,
+ 0x2132, 0x2132,
+ 0x2160, 0x216f,
+ 0x2183, 0x2183,
+ 0x24b6, 0x24cf,
+ 0x2c00, 0x2c2e,
+ 0x2c60, 0x2c60,
+ 0x2c62, 0x2c64,
+ 0x2c67, 0x2c67,
+ 0x2c69, 0x2c69,
+ 0x2c6b, 0x2c6b,
+ 0x2c6d, 0x2c70,
+ 0x2c72, 0x2c72,
+ 0x2c75, 0x2c75,
+ 0x2c7e, 0x2c80,
+ 0x2c82, 0x2c82,
+ 0x2c84, 0x2c84,
+ 0x2c86, 0x2c86,
+ 0x2c88, 0x2c88,
+ 0x2c8a, 0x2c8a,
+ 0x2c8c, 0x2c8c,
+ 0x2c8e, 0x2c8e,
+ 0x2c90, 0x2c90,
+ 0x2c92, 0x2c92,
+ 0x2c94, 0x2c94,
+ 0x2c96, 0x2c96,
+ 0x2c98, 0x2c98,
+ 0x2c9a, 0x2c9a,
+ 0x2c9c, 0x2c9c,
+ 0x2c9e, 0x2c9e,
+ 0x2ca0, 0x2ca0,
+ 0x2ca2, 0x2ca2,
+ 0x2ca4, 0x2ca4,
+ 0x2ca6, 0x2ca6,
+ 0x2ca8, 0x2ca8,
+ 0x2caa, 0x2caa,
+ 0x2cac, 0x2cac,
+ 0x2cae, 0x2cae,
+ 0x2cb0, 0x2cb0,
+ 0x2cb2, 0x2cb2,
+ 0x2cb4, 0x2cb4,
+ 0x2cb6, 0x2cb6,
+ 0x2cb8, 0x2cb8,
+ 0x2cba, 0x2cba,
+ 0x2cbc, 0x2cbc,
+ 0x2cbe, 0x2cbe,
+ 0x2cc0, 0x2cc0,
+ 0x2cc2, 0x2cc2,
+ 0x2cc4, 0x2cc4,
+ 0x2cc6, 0x2cc6,
+ 0x2cc8, 0x2cc8,
+ 0x2cca, 0x2cca,
+ 0x2ccc, 0x2ccc,
+ 0x2cce, 0x2cce,
+ 0x2cd0, 0x2cd0,
+ 0x2cd2, 0x2cd2,
+ 0x2cd4, 0x2cd4,
+ 0x2cd6, 0x2cd6,
+ 0x2cd8, 0x2cd8,
+ 0x2cda, 0x2cda,
+ 0x2cdc, 0x2cdc,
+ 0x2cde, 0x2cde,
+ 0x2ce0, 0x2ce0,
+ 0x2ce2, 0x2ce2,
+ 0x2ceb, 0x2ceb,
+ 0x2ced, 0x2ced,
+ 0x2cf2, 0x2cf2,
+ 0xa640, 0xa640,
+ 0xa642, 0xa642,
+ 0xa644, 0xa644,
+ 0xa646, 0xa646,
+ 0xa648, 0xa648,
+ 0xa64a, 0xa64a,
+ 0xa64c, 0xa64c,
+ 0xa64e, 0xa64e,
+ 0xa650, 0xa650,
+ 0xa652, 0xa652,
+ 0xa654, 0xa654,
+ 0xa656, 0xa656,
+ 0xa658, 0xa658,
+ 0xa65a, 0xa65a,
+ 0xa65c, 0xa65c,
+ 0xa65e, 0xa65e,
+ 0xa660, 0xa660,
+ 0xa662, 0xa662,
+ 0xa664, 0xa664,
+ 0xa666, 0xa666,
+ 0xa668, 0xa668,
+ 0xa66a, 0xa66a,
+ 0xa66c, 0xa66c,
+ 0xa680, 0xa680,
+ 0xa682, 0xa682,
+ 0xa684, 0xa684,
+ 0xa686, 0xa686,
+ 0xa688, 0xa688,
+ 0xa68a, 0xa68a,
+ 0xa68c, 0xa68c,
+ 0xa68e, 0xa68e,
+ 0xa690, 0xa690,
+ 0xa692, 0xa692,
+ 0xa694, 0xa694,
+ 0xa696, 0xa696,
+ 0xa698, 0xa698,
+ 0xa69a, 0xa69a,
+ 0xa722, 0xa722,
+ 0xa724, 0xa724,
+ 0xa726, 0xa726,
+ 0xa728, 0xa728,
+ 0xa72a, 0xa72a,
+ 0xa72c, 0xa72c,
+ 0xa72e, 0xa72e,
+ 0xa732, 0xa732,
+ 0xa734, 0xa734,
+ 0xa736, 0xa736,
+ 0xa738, 0xa738,
+ 0xa73a, 0xa73a,
+ 0xa73c, 0xa73c,
+ 0xa73e, 0xa73e,
+ 0xa740, 0xa740,
+ 0xa742, 0xa742,
+ 0xa744, 0xa744,
+ 0xa746, 0xa746,
+ 0xa748, 0xa748,
+ 0xa74a, 0xa74a,
+ 0xa74c, 0xa74c,
+ 0xa74e, 0xa74e,
+ 0xa750, 0xa750,
+ 0xa752, 0xa752,
+ 0xa754, 0xa754,
+ 0xa756, 0xa756,
+ 0xa758, 0xa758,
+ 0xa75a, 0xa75a,
+ 0xa75c, 0xa75c,
+ 0xa75e, 0xa75e,
+ 0xa760, 0xa760,
+ 0xa762, 0xa762,
+ 0xa764, 0xa764,
+ 0xa766, 0xa766,
+ 0xa768, 0xa768,
+ 0xa76a, 0xa76a,
+ 0xa76c, 0xa76c,
+ 0xa76e, 0xa76e,
+ 0xa779, 0xa779,
+ 0xa77b, 0xa77b,
+ 0xa77d, 0xa77e,
+ 0xa780, 0xa780,
+ 0xa782, 0xa782,
+ 0xa784, 0xa784,
+ 0xa786, 0xa786,
+ 0xa78b, 0xa78b,
+ 0xa78d, 0xa78d,
+ 0xa790, 0xa790,
+ 0xa792, 0xa792,
+ 0xa796, 0xa796,
+ 0xa798, 0xa798,
+ 0xa79a, 0xa79a,
+ 0xa79c, 0xa79c,
+ 0xa79e, 0xa79e,
+ 0xa7a0, 0xa7a0,
+ 0xa7a2, 0xa7a2,
+ 0xa7a4, 0xa7a4,
+ 0xa7a6, 0xa7a6,
+ 0xa7a8, 0xa7a8,
+ 0xa7aa, 0xa7ae,
+ 0xa7b0, 0xa7b4,
+ 0xa7b6, 0xa7b6,
+ 0xa7b8, 0xa7b8,
+ 0xa7ba, 0xa7ba,
+ 0xa7bc, 0xa7bc,
+ 0xa7be, 0xa7be,
+ 0xa7c2, 0xa7c2,
+ 0xa7c4, 0xa7c6,
+ 0xab70, 0xabbf,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xff21, 0xff3a,
+ 0x10400, 0x10427,
+ 0x104b0, 0x104d3,
+ 0x10c80, 0x10cb2,
+ 0x118a0, 0x118bf,
+ 0x16e40, 0x16e5f,
+ 0x1e900, 0x1e921,
+}; /* CR_Changes_When_Casefolded */
+
+/* 'Changes_When_Casemapped': Derived Property */
+static const OnigCodePoint CR_Changes_When_Casemapped[] = {
+ 123,
+ 0x0041, 0x005a,
+ 0x0061, 0x007a,
+ 0x00b5, 0x00b5,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00f6,
+ 0x00f8, 0x0137,
+ 0x0139, 0x018c,
+ 0x018e, 0x019a,
+ 0x019c, 0x01a9,
+ 0x01ac, 0x01b9,
+ 0x01bc, 0x01bd,
+ 0x01bf, 0x01bf,
+ 0x01c4, 0x0220,
+ 0x0222, 0x0233,
+ 0x023a, 0x0254,
+ 0x0256, 0x0257,
+ 0x0259, 0x0259,
+ 0x025b, 0x025c,
+ 0x0260, 0x0261,
+ 0x0263, 0x0263,
+ 0x0265, 0x0266,
+ 0x0268, 0x026c,
+ 0x026f, 0x026f,
+ 0x0271, 0x0272,
+ 0x0275, 0x0275,
+ 0x027d, 0x027d,
+ 0x0280, 0x0280,
+ 0x0282, 0x0283,
+ 0x0287, 0x028c,
+ 0x0292, 0x0292,
+ 0x029d, 0x029e,
+ 0x0345, 0x0345,
+ 0x0370, 0x0373,
+ 0x0376, 0x0377,
+ 0x037b, 0x037d,
+ 0x037f, 0x037f,
+ 0x0386, 0x0386,
+ 0x0388, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03d1,
+ 0x03d5, 0x03f5,
+ 0x03f7, 0x03fb,
+ 0x03fd, 0x0481,
+ 0x048a, 0x052f,
+ 0x0531, 0x0556,
+ 0x0561, 0x0587,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x10fa,
+ 0x10fd, 0x10ff,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0x1c80, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x1d79, 0x1d79,
+ 0x1d7d, 0x1d7d,
+ 0x1d8e, 0x1d8e,
+ 0x1e00, 0x1e9b,
+ 0x1e9e, 0x1e9e,
+ 0x1ea0, 0x1f15,
+ 0x1f18, 0x1f1d,
+ 0x1f20, 0x1f45,
+ 0x1f48, 0x1f4d,
+ 0x1f50, 0x1f57,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f7d,
+ 0x1f80, 0x1fb4,
+ 0x1fb6, 0x1fbc,
+ 0x1fbe, 0x1fbe,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fcc,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fdb,
+ 0x1fe0, 0x1fec,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ffc,
+ 0x2126, 0x2126,
+ 0x212a, 0x212b,
+ 0x2132, 0x2132,
+ 0x214e, 0x214e,
+ 0x2160, 0x217f,
+ 0x2183, 0x2184,
+ 0x24b6, 0x24e9,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2c70,
+ 0x2c72, 0x2c73,
+ 0x2c75, 0x2c76,
+ 0x2c7e, 0x2ce3,
+ 0x2ceb, 0x2cee,
+ 0x2cf2, 0x2cf3,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0xa640, 0xa66d,
+ 0xa680, 0xa69b,
+ 0xa722, 0xa72f,
+ 0xa732, 0xa76f,
+ 0xa779, 0xa787,
+ 0xa78b, 0xa78d,
+ 0xa790, 0xa794,
+ 0xa796, 0xa7ae,
+ 0xa7b0, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xab53, 0xab53,
+ 0xab70, 0xabbf,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xff21, 0xff3a,
+ 0xff41, 0xff5a,
+ 0x10400, 0x1044f,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+ 0x10c80, 0x10cb2,
+ 0x10cc0, 0x10cf2,
+ 0x118a0, 0x118df,
+ 0x16e40, 0x16e7f,
+ 0x1e900, 0x1e943,
+}; /* CR_Changes_When_Casemapped */
+
+/* 'ID_Start': Derived Property */
+static const OnigCodePoint CR_ID_Start[] = {
+ 609,
+ 0x0041, 0x005a,
+ 0x0061, 0x007a,
+ 0x00aa, 0x00aa,
+ 0x00b5, 0x00b5,
+ 0x00ba, 0x00ba,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00f6,
+ 0x00f8, 0x02c1,
+ 0x02c6, 0x02d1,
+ 0x02e0, 0x02e4,
+ 0x02ec, 0x02ec,
+ 0x02ee, 0x02ee,
+ 0x0370, 0x0374,
+ 0x0376, 0x0377,
+ 0x037a, 0x037d,
+ 0x037f, 0x037f,
+ 0x0386, 0x0386,
+ 0x0388, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03f5,
+ 0x03f7, 0x0481,
+ 0x048a, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x0559,
+ 0x0560, 0x0588,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f2,
+ 0x0620, 0x064a,
+ 0x066e, 0x066f,
+ 0x0671, 0x06d3,
+ 0x06d5, 0x06d5,
+ 0x06e5, 0x06e6,
+ 0x06ee, 0x06ef,
+ 0x06fa, 0x06fc,
+ 0x06ff, 0x06ff,
+ 0x0710, 0x0710,
+ 0x0712, 0x072f,
+ 0x074d, 0x07a5,
+ 0x07b1, 0x07b1,
+ 0x07ca, 0x07ea,
+ 0x07f4, 0x07f5,
+ 0x07fa, 0x07fa,
+ 0x0800, 0x0815,
+ 0x081a, 0x081a,
+ 0x0824, 0x0824,
+ 0x0828, 0x0828,
+ 0x0840, 0x0858,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x0904, 0x0939,
+ 0x093d, 0x093d,
+ 0x0950, 0x0950,
+ 0x0958, 0x0961,
+ 0x0971, 0x0980,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bd, 0x09bd,
+ 0x09ce, 0x09ce,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e1,
+ 0x09f0, 0x09f1,
+ 0x09fc, 0x09fc,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a72, 0x0a74,
+ 0x0a85, 0x0a8d,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0abd, 0x0abd,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae1,
+ 0x0af9, 0x0af9,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3d, 0x0b3d,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b61,
+ 0x0b71, 0x0b71,
+ 0x0b83, 0x0b83,
+ 0x0b85, 0x0b8a,
+ 0x0b8e, 0x0b90,
+ 0x0b92, 0x0b95,
+ 0x0b99, 0x0b9a,
+ 0x0b9c, 0x0b9c,
+ 0x0b9e, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb9,
+ 0x0bd0, 0x0bd0,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3d, 0x0c3d,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c61,
+ 0x0c80, 0x0c80,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbd, 0x0cbd,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce1,
+ 0x0cf1, 0x0cf2,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d3a,
+ 0x0d3d, 0x0d3d,
+ 0x0d4e, 0x0d4e,
+ 0x0d54, 0x0d56,
+ 0x0d5f, 0x0d61,
+ 0x0d7a, 0x0d7f,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0e01, 0x0e30,
+ 0x0e32, 0x0e33,
+ 0x0e40, 0x0e46,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e86, 0x0e8a,
+ 0x0e8c, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0eb0,
+ 0x0eb2, 0x0eb3,
+ 0x0ebd, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0edc, 0x0edf,
+ 0x0f00, 0x0f00,
+ 0x0f40, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f88, 0x0f8c,
+ 0x1000, 0x102a,
+ 0x103f, 0x103f,
+ 0x1050, 0x1055,
+ 0x105a, 0x105d,
+ 0x1061, 0x1061,
+ 0x1065, 0x1066,
+ 0x106e, 0x1070,
+ 0x1075, 0x1081,
+ 0x108e, 0x108e,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x10fa,
+ 0x10fc, 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,
+ 0x1380, 0x138f,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0x1401, 0x166c,
+ 0x166f, 0x167f,
+ 0x1681, 0x169a,
+ 0x16a0, 0x16ea,
+ 0x16ee, 0x16f8,
+ 0x1700, 0x170c,
+ 0x170e, 0x1711,
+ 0x1720, 0x1731,
+ 0x1740, 0x1751,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1780, 0x17b3,
+ 0x17d7, 0x17d7,
+ 0x17dc, 0x17dc,
+ 0x1820, 0x1878,
+ 0x1880, 0x18a8,
+ 0x18aa, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191e,
+ 0x1950, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19ab,
+ 0x19b0, 0x19c9,
+ 0x1a00, 0x1a16,
+ 0x1a20, 0x1a54,
+ 0x1aa7, 0x1aa7,
+ 0x1b05, 0x1b33,
+ 0x1b45, 0x1b4b,
+ 0x1b83, 0x1ba0,
+ 0x1bae, 0x1baf,
+ 0x1bba, 0x1be5,
+ 0x1c00, 0x1c23,
+ 0x1c4d, 0x1c4f,
+ 0x1c5a, 0x1c7d,
+ 0x1c80, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x1ce9, 0x1cec,
+ 0x1cee, 0x1cf3,
+ 0x1cf5, 0x1cf6,
+ 0x1cfa, 0x1cfa,
+ 0x1d00, 0x1dbf,
+ 0x1e00, 0x1f15,
+ 0x1f18, 0x1f1d,
+ 0x1f20, 0x1f45,
+ 0x1f48, 0x1f4d,
+ 0x1f50, 0x1f57,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f7d,
+ 0x1f80, 0x1fb4,
+ 0x1fb6, 0x1fbc,
+ 0x1fbe, 0x1fbe,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fcc,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fdb,
+ 0x1fe0, 0x1fec,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ffc,
+ 0x2071, 0x2071,
+ 0x207f, 0x207f,
+ 0x2090, 0x209c,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210a, 0x2113,
+ 0x2115, 0x2115,
+ 0x2118, 0x211d,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212a, 0x2139,
+ 0x213c, 0x213f,
+ 0x2145, 0x2149,
+ 0x214e, 0x214e,
+ 0x2160, 0x2188,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2ce4,
+ 0x2ceb, 0x2cee,
+ 0x2cf2, 0x2cf3,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0x2d30, 0x2d67,
+ 0x2d6f, 0x2d6f,
+ 0x2d80, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x3005, 0x3007,
+ 0x3021, 0x3029,
+ 0x3031, 0x3035,
+ 0x3038, 0x303c,
+ 0x3041, 0x3096,
+ 0x309b, 0x309f,
+ 0x30a1, 0x30fa,
+ 0x30fc, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x31a0, 0x31ba,
+ 0x31f0, 0x31ff,
+ 0x3400, 0x4db5,
+ 0x4e00, 0x9fef,
+ 0xa000, 0xa48c,
+ 0xa4d0, 0xa4fd,
+ 0xa500, 0xa60c,
+ 0xa610, 0xa61f,
+ 0xa62a, 0xa62b,
+ 0xa640, 0xa66e,
+ 0xa67f, 0xa69d,
+ 0xa6a0, 0xa6ef,
+ 0xa717, 0xa71f,
+ 0xa722, 0xa788,
+ 0xa78b, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f7, 0xa801,
+ 0xa803, 0xa805,
+ 0xa807, 0xa80a,
+ 0xa80c, 0xa822,
+ 0xa840, 0xa873,
+ 0xa882, 0xa8b3,
+ 0xa8f2, 0xa8f7,
+ 0xa8fb, 0xa8fb,
+ 0xa8fd, 0xa8fe,
+ 0xa90a, 0xa925,
+ 0xa930, 0xa946,
+ 0xa960, 0xa97c,
+ 0xa984, 0xa9b2,
+ 0xa9cf, 0xa9cf,
+ 0xa9e0, 0xa9e4,
+ 0xa9e6, 0xa9ef,
+ 0xa9fa, 0xa9fe,
+ 0xaa00, 0xaa28,
+ 0xaa40, 0xaa42,
+ 0xaa44, 0xaa4b,
+ 0xaa60, 0xaa76,
+ 0xaa7a, 0xaa7a,
+ 0xaa7e, 0xaaaf,
+ 0xaab1, 0xaab1,
+ 0xaab5, 0xaab6,
+ 0xaab9, 0xaabd,
+ 0xaac0, 0xaac0,
+ 0xaac2, 0xaac2,
+ 0xaadb, 0xaadd,
+ 0xaae0, 0xaaea,
+ 0xaaf2, 0xaaf4,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xab30, 0xab5a,
+ 0xab5c, 0xab67,
+ 0xab70, 0xabe2,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xf900, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb1d,
+ 0xfb1f, 0xfb28,
+ 0xfb2a, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3d,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfb,
+ 0xfe70, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0xff21, 0xff3a,
+ 0xff41, 0xff5a,
+ 0xff66, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0x10000, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+ 0x10140, 0x10174,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x10300, 0x1031f,
+ 0x1032d, 0x1034a,
+ 0x10350, 0x10375,
+ 0x10380, 0x1039d,
+ 0x103a0, 0x103c3,
+ 0x103c8, 0x103cf,
+ 0x103d1, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10860, 0x10876,
+ 0x10880, 0x1089e,
+ 0x108e0, 0x108f2,
+ 0x108f4, 0x108f5,
+ 0x10900, 0x10915,
+ 0x10920, 0x10939,
+ 0x10980, 0x109b7,
+ 0x109be, 0x109bf,
+ 0x10a00, 0x10a00,
+ 0x10a10, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a35,
+ 0x10a60, 0x10a7c,
+ 0x10a80, 0x10a9c,
+ 0x10ac0, 0x10ac7,
+ 0x10ac9, 0x10ae4,
+ 0x10b00, 0x10b35,
+ 0x10b40, 0x10b55,
+ 0x10b60, 0x10b72,
+ 0x10b80, 0x10b91,
+ 0x10c00, 0x10c48,
+ 0x10c80, 0x10cb2,
+ 0x10cc0, 0x10cf2,
+ 0x10d00, 0x10d23,
+ 0x10f00, 0x10f1c,
+ 0x10f27, 0x10f27,
+ 0x10f30, 0x10f45,
+ 0x10fe0, 0x10ff6,
+ 0x11003, 0x11037,
+ 0x11083, 0x110af,
+ 0x110d0, 0x110e8,
+ 0x11103, 0x11126,
+ 0x11144, 0x11144,
+ 0x11150, 0x11172,
+ 0x11176, 0x11176,
+ 0x11183, 0x111b2,
+ 0x111c1, 0x111c4,
+ 0x111da, 0x111da,
+ 0x111dc, 0x111dc,
+ 0x11200, 0x11211,
+ 0x11213, 0x1122b,
+ 0x11280, 0x11286,
+ 0x11288, 0x11288,
+ 0x1128a, 0x1128d,
+ 0x1128f, 0x1129d,
+ 0x1129f, 0x112a8,
+ 0x112b0, 0x112de,
+ 0x11305, 0x1130c,
+ 0x1130f, 0x11310,
+ 0x11313, 0x11328,
+ 0x1132a, 0x11330,
+ 0x11332, 0x11333,
+ 0x11335, 0x11339,
+ 0x1133d, 0x1133d,
+ 0x11350, 0x11350,
+ 0x1135d, 0x11361,
+ 0x11400, 0x11434,
+ 0x11447, 0x1144a,
+ 0x1145f, 0x1145f,
+ 0x11480, 0x114af,
+ 0x114c4, 0x114c5,
+ 0x114c7, 0x114c7,
+ 0x11580, 0x115ae,
+ 0x115d8, 0x115db,
+ 0x11600, 0x1162f,
+ 0x11644, 0x11644,
+ 0x11680, 0x116aa,
+ 0x116b8, 0x116b8,
+ 0x11700, 0x1171a,
+ 0x11800, 0x1182b,
+ 0x118a0, 0x118df,
+ 0x118ff, 0x118ff,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d0,
+ 0x119e1, 0x119e1,
+ 0x119e3, 0x119e3,
+ 0x11a00, 0x11a00,
+ 0x11a0b, 0x11a32,
+ 0x11a3a, 0x11a3a,
+ 0x11a50, 0x11a50,
+ 0x11a5c, 0x11a89,
+ 0x11a9d, 0x11a9d,
+ 0x11ac0, 0x11af8,
+ 0x11c00, 0x11c08,
+ 0x11c0a, 0x11c2e,
+ 0x11c40, 0x11c40,
+ 0x11c72, 0x11c8f,
+ 0x11d00, 0x11d06,
+ 0x11d08, 0x11d09,
+ 0x11d0b, 0x11d30,
+ 0x11d46, 0x11d46,
+ 0x11d60, 0x11d65,
+ 0x11d67, 0x11d68,
+ 0x11d6a, 0x11d89,
+ 0x11d98, 0x11d98,
+ 0x11ee0, 0x11ef2,
+ 0x12000, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16ad0, 0x16aed,
+ 0x16b00, 0x16b2f,
+ 0x16b40, 0x16b43,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e7f,
+ 0x16f00, 0x16f4a,
+ 0x16f50, 0x16f50,
+ 0x16f93, 0x16f9f,
+ 0x16fe0, 0x16fe1,
+ 0x16fe3, 0x16fe3,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 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, 0x1d6c0,
+ 0x1d6c2, 0x1d6da,
+ 0x1d6dc, 0x1d6fa,
+ 0x1d6fc, 0x1d714,
+ 0x1d716, 0x1d734,
+ 0x1d736, 0x1d74e,
+ 0x1d750, 0x1d76e,
+ 0x1d770, 0x1d788,
+ 0x1d78a, 0x1d7a8,
+ 0x1d7aa, 0x1d7c2,
+ 0x1d7c4, 0x1d7cb,
+ 0x1e100, 0x1e12c,
+ 0x1e137, 0x1e13d,
+ 0x1e14e, 0x1e14e,
+ 0x1e2c0, 0x1e2eb,
+ 0x1e800, 0x1e8c4,
+ 0x1e900, 0x1e943,
+ 0x1e94b, 0x1e94b,
+ 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,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+}; /* CR_ID_Start */
+
+/* 'ID_Continue': Derived Property */
+static const OnigCodePoint CR_ID_Continue[] = {
+ 713,
+ 0x0030, 0x0039,
+ 0x0041, 0x005a,
+ 0x005f, 0x005f,
+ 0x0061, 0x007a,
+ 0x00aa, 0x00aa,
+ 0x00b5, 0x00b5,
+ 0x00b7, 0x00b7,
+ 0x00ba, 0x00ba,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00f6,
+ 0x00f8, 0x02c1,
+ 0x02c6, 0x02d1,
+ 0x02e0, 0x02e4,
+ 0x02ec, 0x02ec,
+ 0x02ee, 0x02ee,
+ 0x0300, 0x0374,
+ 0x0376, 0x0377,
+ 0x037a, 0x037d,
+ 0x037f, 0x037f,
+ 0x0386, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03f5,
+ 0x03f7, 0x0481,
+ 0x0483, 0x0487,
+ 0x048a, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x0559,
+ 0x0560, 0x0588,
+ 0x0591, 0x05bd,
+ 0x05bf, 0x05bf,
+ 0x05c1, 0x05c2,
+ 0x05c4, 0x05c5,
+ 0x05c7, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f2,
+ 0x0610, 0x061a,
+ 0x0620, 0x0669,
+ 0x066e, 0x06d3,
+ 0x06d5, 0x06dc,
+ 0x06df, 0x06e8,
+ 0x06ea, 0x06fc,
+ 0x06ff, 0x06ff,
+ 0x0710, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07f5,
+ 0x07fa, 0x07fa,
+ 0x07fd, 0x07fd,
+ 0x0800, 0x082d,
+ 0x0840, 0x085b,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x08d3, 0x08e1,
+ 0x08e3, 0x0963,
+ 0x0966, 0x096f,
+ 0x0971, 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, 0x09f1,
+ 0x09fc, 0x09fc,
+ 0x09fe, 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, 0x0a75,
+ 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, 0x0aef,
+ 0x0af9, 0x0aff,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b63,
+ 0x0b66, 0x0b6f,
+ 0x0b71, 0x0b71,
+ 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, 0x0bef,
+ 0x0c00, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c80, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d00, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4e,
+ 0x0d54, 0x0d57,
+ 0x0d5f, 0x0d63,
+ 0x0d66, 0x0d6f,
+ 0x0d7a, 0x0d7f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0de6, 0x0def,
+ 0x0df2, 0x0df3,
+ 0x0e01, 0x0e3a,
+ 0x0e40, 0x0e4e,
+ 0x0e50, 0x0e59,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e86, 0x0e8a,
+ 0x0e8c, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edf,
+ 0x0f00, 0x0f00,
+ 0x0f18, 0x0f19,
+ 0x0f20, 0x0f29,
+ 0x0f35, 0x0f35,
+ 0x0f37, 0x0f37,
+ 0x0f39, 0x0f39,
+ 0x0f3e, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f71, 0x0f84,
+ 0x0f86, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fc6, 0x0fc6,
+ 0x1000, 0x1049,
+ 0x1050, 0x109d,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x10fa,
+ 0x10fc, 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, 0x135f,
+ 0x1369, 0x1371,
+ 0x1380, 0x138f,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0x1401, 0x166c,
+ 0x166f, 0x167f,
+ 0x1681, 0x169a,
+ 0x16a0, 0x16ea,
+ 0x16ee, 0x16f8,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1734,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17d3,
+ 0x17d7, 0x17d7,
+ 0x17dc, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x180b, 0x180d,
+ 0x1810, 0x1819,
+ 0x1820, 0x1878,
+ 0x1880, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191e,
+ 0x1920, 0x192b,
+ 0x1930, 0x193b,
+ 0x1946, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19ab,
+ 0x19b0, 0x19c9,
+ 0x19d0, 0x19da,
+ 0x1a00, 0x1a1b,
+ 0x1a20, 0x1a5e,
+ 0x1a60, 0x1a7c,
+ 0x1a7f, 0x1a89,
+ 0x1a90, 0x1a99,
+ 0x1aa7, 0x1aa7,
+ 0x1ab0, 0x1abd,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b59,
+ 0x1b6b, 0x1b73,
+ 0x1b80, 0x1bf3,
+ 0x1c00, 0x1c37,
+ 0x1c40, 0x1c49,
+ 0x1c4d, 0x1c7d,
+ 0x1c80, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x1cd0, 0x1cd2,
+ 0x1cd4, 0x1cfa,
+ 0x1d00, 0x1df9,
+ 0x1dfb, 0x1f15,
+ 0x1f18, 0x1f1d,
+ 0x1f20, 0x1f45,
+ 0x1f48, 0x1f4d,
+ 0x1f50, 0x1f57,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f7d,
+ 0x1f80, 0x1fb4,
+ 0x1fb6, 0x1fbc,
+ 0x1fbe, 0x1fbe,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fcc,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fdb,
+ 0x1fe0, 0x1fec,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ffc,
+ 0x203f, 0x2040,
+ 0x2054, 0x2054,
+ 0x2071, 0x2071,
+ 0x207f, 0x207f,
+ 0x2090, 0x209c,
+ 0x20d0, 0x20dc,
+ 0x20e1, 0x20e1,
+ 0x20e5, 0x20f0,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210a, 0x2113,
+ 0x2115, 0x2115,
+ 0x2118, 0x211d,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212a, 0x2139,
+ 0x213c, 0x213f,
+ 0x2145, 0x2149,
+ 0x214e, 0x214e,
+ 0x2160, 0x2188,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2ce4,
+ 0x2ceb, 0x2cf3,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0x2d30, 0x2d67,
+ 0x2d6f, 0x2d6f,
+ 0x2d7f, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x2de0, 0x2dff,
+ 0x3005, 0x3007,
+ 0x3021, 0x302f,
+ 0x3031, 0x3035,
+ 0x3038, 0x303c,
+ 0x3041, 0x3096,
+ 0x3099, 0x309f,
+ 0x30a1, 0x30fa,
+ 0x30fc, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x31a0, 0x31ba,
+ 0x31f0, 0x31ff,
+ 0x3400, 0x4db5,
+ 0x4e00, 0x9fef,
+ 0xa000, 0xa48c,
+ 0xa4d0, 0xa4fd,
+ 0xa500, 0xa60c,
+ 0xa610, 0xa62b,
+ 0xa640, 0xa66f,
+ 0xa674, 0xa67d,
+ 0xa67f, 0xa6f1,
+ 0xa717, 0xa71f,
+ 0xa722, 0xa788,
+ 0xa78b, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f7, 0xa827,
+ 0xa840, 0xa873,
+ 0xa880, 0xa8c5,
+ 0xa8d0, 0xa8d9,
+ 0xa8e0, 0xa8f7,
+ 0xa8fb, 0xa8fb,
+ 0xa8fd, 0xa92d,
+ 0xa930, 0xa953,
+ 0xa960, 0xa97c,
+ 0xa980, 0xa9c0,
+ 0xa9cf, 0xa9d9,
+ 0xa9e0, 0xa9fe,
+ 0xaa00, 0xaa36,
+ 0xaa40, 0xaa4d,
+ 0xaa50, 0xaa59,
+ 0xaa60, 0xaa76,
+ 0xaa7a, 0xaac2,
+ 0xaadb, 0xaadd,
+ 0xaae0, 0xaaef,
+ 0xaaf2, 0xaaf6,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xab30, 0xab5a,
+ 0xab5c, 0xab67,
+ 0xab70, 0xabea,
+ 0xabec, 0xabed,
+ 0xabf0, 0xabf9,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xf900, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb28,
+ 0xfb2a, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3d,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfb,
+ 0xfe00, 0xfe0f,
+ 0xfe20, 0xfe2f,
+ 0xfe33, 0xfe34,
+ 0xfe4d, 0xfe4f,
+ 0xfe70, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0xff10, 0xff19,
+ 0xff21, 0xff3a,
+ 0xff3f, 0xff3f,
+ 0xff41, 0xff5a,
+ 0xff66, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0x10000, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+ 0x10140, 0x10174,
+ 0x101fd, 0x101fd,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x102e0, 0x102e0,
+ 0x10300, 0x1031f,
+ 0x1032d, 0x1034a,
+ 0x10350, 0x1037a,
+ 0x10380, 0x1039d,
+ 0x103a0, 0x103c3,
+ 0x103c8, 0x103cf,
+ 0x103d1, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10860, 0x10876,
+ 0x10880, 0x1089e,
+ 0x108e0, 0x108f2,
+ 0x108f4, 0x108f5,
+ 0x10900, 0x10915,
+ 0x10920, 0x10939,
+ 0x10980, 0x109b7,
+ 0x109be, 0x109bf,
+ 0x10a00, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a35,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a3f,
+ 0x10a60, 0x10a7c,
+ 0x10a80, 0x10a9c,
+ 0x10ac0, 0x10ac7,
+ 0x10ac9, 0x10ae6,
+ 0x10b00, 0x10b35,
+ 0x10b40, 0x10b55,
+ 0x10b60, 0x10b72,
+ 0x10b80, 0x10b91,
+ 0x10c00, 0x10c48,
+ 0x10c80, 0x10cb2,
+ 0x10cc0, 0x10cf2,
+ 0x10d00, 0x10d27,
+ 0x10d30, 0x10d39,
+ 0x10f00, 0x10f1c,
+ 0x10f27, 0x10f27,
+ 0x10f30, 0x10f50,
+ 0x10fe0, 0x10ff6,
+ 0x11000, 0x11046,
+ 0x11066, 0x1106f,
+ 0x1107f, 0x110ba,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x1113f,
+ 0x11144, 0x11146,
+ 0x11150, 0x11173,
+ 0x11176, 0x11176,
+ 0x11180, 0x111c4,
+ 0x111c9, 0x111cc,
+ 0x111d0, 0x111da,
+ 0x111dc, 0x111dc,
+ 0x11200, 0x11211,
+ 0x11213, 0x11237,
+ 0x1123e, 0x1123e,
+ 0x11280, 0x11286,
+ 0x11288, 0x11288,
+ 0x1128a, 0x1128d,
+ 0x1128f, 0x1129d,
+ 0x1129f, 0x112a8,
+ 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, 0x1144a,
+ 0x11450, 0x11459,
+ 0x1145e, 0x1145f,
+ 0x11480, 0x114c5,
+ 0x114c7, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115c0,
+ 0x115d8, 0x115dd,
+ 0x11600, 0x11640,
+ 0x11644, 0x11644,
+ 0x11650, 0x11659,
+ 0x11680, 0x116b8,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x1171a,
+ 0x1171d, 0x1172b,
+ 0x11730, 0x11739,
+ 0x11800, 0x1183a,
+ 0x118a0, 0x118e9,
+ 0x118ff, 0x118ff,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d7,
+ 0x119da, 0x119e1,
+ 0x119e3, 0x119e4,
+ 0x11a00, 0x11a3e,
+ 0x11a47, 0x11a47,
+ 0x11a50, 0x11a99,
+ 0x11a9d, 0x11a9d,
+ 0x11ac0, 0x11af8,
+ 0x11c00, 0x11c08,
+ 0x11c0a, 0x11c36,
+ 0x11c38, 0x11c40,
+ 0x11c50, 0x11c59,
+ 0x11c72, 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, 0x11ef6,
+ 0x12000, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af4,
+ 0x16b00, 0x16b36,
+ 0x16b40, 0x16b43,
+ 0x16b50, 0x16b59,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e7f,
+ 0x16f00, 0x16f4a,
+ 0x16f4f, 0x16f87,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe1,
+ 0x16fe3, 0x16fe3,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9d, 0x1bc9e,
+ 0x1d165, 0x1d169,
+ 0x1d16d, 0x1d172,
+ 0x1d17b, 0x1d182,
+ 0x1d185, 0x1d18b,
+ 0x1d1aa, 0x1d1ad,
+ 0x1d242, 0x1d244,
+ 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, 0x1d6c0,
+ 0x1d6c2, 0x1d6da,
+ 0x1d6dc, 0x1d6fa,
+ 0x1d6fc, 0x1d714,
+ 0x1d716, 0x1d734,
+ 0x1d736, 0x1d74e,
+ 0x1d750, 0x1d76e,
+ 0x1d770, 0x1d788,
+ 0x1d78a, 0x1d7a8,
+ 0x1d7aa, 0x1d7c2,
+ 0x1d7c4, 0x1d7cb,
+ 0x1d7ce, 0x1d7ff,
+ 0x1da00, 0x1da36,
+ 0x1da3b, 0x1da6c,
+ 0x1da75, 0x1da75,
+ 0x1da84, 0x1da84,
+ 0x1da9b, 0x1da9f,
+ 0x1daa1, 0x1daaf,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e100, 0x1e12c,
+ 0x1e130, 0x1e13d,
+ 0x1e140, 0x1e149,
+ 0x1e14e, 0x1e14e,
+ 0x1e2c0, 0x1e2f9,
+ 0x1e800, 0x1e8c4,
+ 0x1e8d0, 0x1e8d6,
+ 0x1e900, 0x1e94b,
+ 0x1e950, 0x1e959,
+ 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,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+ 0xe0100, 0xe01ef,
+}; /* CR_ID_Continue */
+
+/* 'XID_Start': Derived Property */
+static const OnigCodePoint CR_XID_Start[] = {
+ 616,
+ 0x0041, 0x005a,
+ 0x0061, 0x007a,
+ 0x00aa, 0x00aa,
+ 0x00b5, 0x00b5,
+ 0x00ba, 0x00ba,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00f6,
+ 0x00f8, 0x02c1,
+ 0x02c6, 0x02d1,
+ 0x02e0, 0x02e4,
+ 0x02ec, 0x02ec,
+ 0x02ee, 0x02ee,
+ 0x0370, 0x0374,
+ 0x0376, 0x0377,
+ 0x037b, 0x037d,
+ 0x037f, 0x037f,
+ 0x0386, 0x0386,
+ 0x0388, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03f5,
+ 0x03f7, 0x0481,
+ 0x048a, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x0559,
+ 0x0560, 0x0588,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f2,
+ 0x0620, 0x064a,
+ 0x066e, 0x066f,
+ 0x0671, 0x06d3,
+ 0x06d5, 0x06d5,
+ 0x06e5, 0x06e6,
+ 0x06ee, 0x06ef,
+ 0x06fa, 0x06fc,
+ 0x06ff, 0x06ff,
+ 0x0710, 0x0710,
+ 0x0712, 0x072f,
+ 0x074d, 0x07a5,
+ 0x07b1, 0x07b1,
+ 0x07ca, 0x07ea,
+ 0x07f4, 0x07f5,
+ 0x07fa, 0x07fa,
+ 0x0800, 0x0815,
+ 0x081a, 0x081a,
+ 0x0824, 0x0824,
+ 0x0828, 0x0828,
+ 0x0840, 0x0858,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x0904, 0x0939,
+ 0x093d, 0x093d,
+ 0x0950, 0x0950,
+ 0x0958, 0x0961,
+ 0x0971, 0x0980,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bd, 0x09bd,
+ 0x09ce, 0x09ce,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e1,
+ 0x09f0, 0x09f1,
+ 0x09fc, 0x09fc,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a72, 0x0a74,
+ 0x0a85, 0x0a8d,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0abd, 0x0abd,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae1,
+ 0x0af9, 0x0af9,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3d, 0x0b3d,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b61,
+ 0x0b71, 0x0b71,
+ 0x0b83, 0x0b83,
+ 0x0b85, 0x0b8a,
+ 0x0b8e, 0x0b90,
+ 0x0b92, 0x0b95,
+ 0x0b99, 0x0b9a,
+ 0x0b9c, 0x0b9c,
+ 0x0b9e, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb9,
+ 0x0bd0, 0x0bd0,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3d, 0x0c3d,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c61,
+ 0x0c80, 0x0c80,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbd, 0x0cbd,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce1,
+ 0x0cf1, 0x0cf2,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d3a,
+ 0x0d3d, 0x0d3d,
+ 0x0d4e, 0x0d4e,
+ 0x0d54, 0x0d56,
+ 0x0d5f, 0x0d61,
+ 0x0d7a, 0x0d7f,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0e01, 0x0e30,
+ 0x0e32, 0x0e32,
+ 0x0e40, 0x0e46,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e86, 0x0e8a,
+ 0x0e8c, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0eb0,
+ 0x0eb2, 0x0eb2,
+ 0x0ebd, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0edc, 0x0edf,
+ 0x0f00, 0x0f00,
+ 0x0f40, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f88, 0x0f8c,
+ 0x1000, 0x102a,
+ 0x103f, 0x103f,
+ 0x1050, 0x1055,
+ 0x105a, 0x105d,
+ 0x1061, 0x1061,
+ 0x1065, 0x1066,
+ 0x106e, 0x1070,
+ 0x1075, 0x1081,
+ 0x108e, 0x108e,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x10fa,
+ 0x10fc, 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,
+ 0x1380, 0x138f,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0x1401, 0x166c,
+ 0x166f, 0x167f,
+ 0x1681, 0x169a,
+ 0x16a0, 0x16ea,
+ 0x16ee, 0x16f8,
+ 0x1700, 0x170c,
+ 0x170e, 0x1711,
+ 0x1720, 0x1731,
+ 0x1740, 0x1751,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1780, 0x17b3,
+ 0x17d7, 0x17d7,
+ 0x17dc, 0x17dc,
+ 0x1820, 0x1878,
+ 0x1880, 0x18a8,
+ 0x18aa, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191e,
+ 0x1950, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19ab,
+ 0x19b0, 0x19c9,
+ 0x1a00, 0x1a16,
+ 0x1a20, 0x1a54,
+ 0x1aa7, 0x1aa7,
+ 0x1b05, 0x1b33,
+ 0x1b45, 0x1b4b,
+ 0x1b83, 0x1ba0,
+ 0x1bae, 0x1baf,
+ 0x1bba, 0x1be5,
+ 0x1c00, 0x1c23,
+ 0x1c4d, 0x1c4f,
+ 0x1c5a, 0x1c7d,
+ 0x1c80, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x1ce9, 0x1cec,
+ 0x1cee, 0x1cf3,
+ 0x1cf5, 0x1cf6,
+ 0x1cfa, 0x1cfa,
+ 0x1d00, 0x1dbf,
+ 0x1e00, 0x1f15,
+ 0x1f18, 0x1f1d,
+ 0x1f20, 0x1f45,
+ 0x1f48, 0x1f4d,
+ 0x1f50, 0x1f57,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f7d,
+ 0x1f80, 0x1fb4,
+ 0x1fb6, 0x1fbc,
+ 0x1fbe, 0x1fbe,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fcc,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fdb,
+ 0x1fe0, 0x1fec,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ffc,
+ 0x2071, 0x2071,
+ 0x207f, 0x207f,
+ 0x2090, 0x209c,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210a, 0x2113,
+ 0x2115, 0x2115,
+ 0x2118, 0x211d,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212a, 0x2139,
+ 0x213c, 0x213f,
+ 0x2145, 0x2149,
+ 0x214e, 0x214e,
+ 0x2160, 0x2188,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2ce4,
+ 0x2ceb, 0x2cee,
+ 0x2cf2, 0x2cf3,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0x2d30, 0x2d67,
+ 0x2d6f, 0x2d6f,
+ 0x2d80, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x3005, 0x3007,
+ 0x3021, 0x3029,
+ 0x3031, 0x3035,
+ 0x3038, 0x303c,
+ 0x3041, 0x3096,
+ 0x309d, 0x309f,
+ 0x30a1, 0x30fa,
+ 0x30fc, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x31a0, 0x31ba,
+ 0x31f0, 0x31ff,
+ 0x3400, 0x4db5,
+ 0x4e00, 0x9fef,
+ 0xa000, 0xa48c,
+ 0xa4d0, 0xa4fd,
+ 0xa500, 0xa60c,
+ 0xa610, 0xa61f,
+ 0xa62a, 0xa62b,
+ 0xa640, 0xa66e,
+ 0xa67f, 0xa69d,
+ 0xa6a0, 0xa6ef,
+ 0xa717, 0xa71f,
+ 0xa722, 0xa788,
+ 0xa78b, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f7, 0xa801,
+ 0xa803, 0xa805,
+ 0xa807, 0xa80a,
+ 0xa80c, 0xa822,
+ 0xa840, 0xa873,
+ 0xa882, 0xa8b3,
+ 0xa8f2, 0xa8f7,
+ 0xa8fb, 0xa8fb,
+ 0xa8fd, 0xa8fe,
+ 0xa90a, 0xa925,
+ 0xa930, 0xa946,
+ 0xa960, 0xa97c,
+ 0xa984, 0xa9b2,
+ 0xa9cf, 0xa9cf,
+ 0xa9e0, 0xa9e4,
+ 0xa9e6, 0xa9ef,
+ 0xa9fa, 0xa9fe,
+ 0xaa00, 0xaa28,
+ 0xaa40, 0xaa42,
+ 0xaa44, 0xaa4b,
+ 0xaa60, 0xaa76,
+ 0xaa7a, 0xaa7a,
+ 0xaa7e, 0xaaaf,
+ 0xaab1, 0xaab1,
+ 0xaab5, 0xaab6,
+ 0xaab9, 0xaabd,
+ 0xaac0, 0xaac0,
+ 0xaac2, 0xaac2,
+ 0xaadb, 0xaadd,
+ 0xaae0, 0xaaea,
+ 0xaaf2, 0xaaf4,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xab30, 0xab5a,
+ 0xab5c, 0xab67,
+ 0xab70, 0xabe2,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xf900, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb1d,
+ 0xfb1f, 0xfb28,
+ 0xfb2a, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfc5d,
+ 0xfc64, 0xfd3d,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdf9,
+ 0xfe71, 0xfe71,
+ 0xfe73, 0xfe73,
+ 0xfe77, 0xfe77,
+ 0xfe79, 0xfe79,
+ 0xfe7b, 0xfe7b,
+ 0xfe7d, 0xfe7d,
+ 0xfe7f, 0xfefc,
+ 0xff21, 0xff3a,
+ 0xff41, 0xff5a,
+ 0xff66, 0xff9d,
+ 0xffa0, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0x10000, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+ 0x10140, 0x10174,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x10300, 0x1031f,
+ 0x1032d, 0x1034a,
+ 0x10350, 0x10375,
+ 0x10380, 0x1039d,
+ 0x103a0, 0x103c3,
+ 0x103c8, 0x103cf,
+ 0x103d1, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10860, 0x10876,
+ 0x10880, 0x1089e,
+ 0x108e0, 0x108f2,
+ 0x108f4, 0x108f5,
+ 0x10900, 0x10915,
+ 0x10920, 0x10939,
+ 0x10980, 0x109b7,
+ 0x109be, 0x109bf,
+ 0x10a00, 0x10a00,
+ 0x10a10, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a35,
+ 0x10a60, 0x10a7c,
+ 0x10a80, 0x10a9c,
+ 0x10ac0, 0x10ac7,
+ 0x10ac9, 0x10ae4,
+ 0x10b00, 0x10b35,
+ 0x10b40, 0x10b55,
+ 0x10b60, 0x10b72,
+ 0x10b80, 0x10b91,
+ 0x10c00, 0x10c48,
+ 0x10c80, 0x10cb2,
+ 0x10cc0, 0x10cf2,
+ 0x10d00, 0x10d23,
+ 0x10f00, 0x10f1c,
+ 0x10f27, 0x10f27,
+ 0x10f30, 0x10f45,
+ 0x10fe0, 0x10ff6,
+ 0x11003, 0x11037,
+ 0x11083, 0x110af,
+ 0x110d0, 0x110e8,
+ 0x11103, 0x11126,
+ 0x11144, 0x11144,
+ 0x11150, 0x11172,
+ 0x11176, 0x11176,
+ 0x11183, 0x111b2,
+ 0x111c1, 0x111c4,
+ 0x111da, 0x111da,
+ 0x111dc, 0x111dc,
+ 0x11200, 0x11211,
+ 0x11213, 0x1122b,
+ 0x11280, 0x11286,
+ 0x11288, 0x11288,
+ 0x1128a, 0x1128d,
+ 0x1128f, 0x1129d,
+ 0x1129f, 0x112a8,
+ 0x112b0, 0x112de,
+ 0x11305, 0x1130c,
+ 0x1130f, 0x11310,
+ 0x11313, 0x11328,
+ 0x1132a, 0x11330,
+ 0x11332, 0x11333,
+ 0x11335, 0x11339,
+ 0x1133d, 0x1133d,
+ 0x11350, 0x11350,
+ 0x1135d, 0x11361,
+ 0x11400, 0x11434,
+ 0x11447, 0x1144a,
+ 0x1145f, 0x1145f,
+ 0x11480, 0x114af,
+ 0x114c4, 0x114c5,
+ 0x114c7, 0x114c7,
+ 0x11580, 0x115ae,
+ 0x115d8, 0x115db,
+ 0x11600, 0x1162f,
+ 0x11644, 0x11644,
+ 0x11680, 0x116aa,
+ 0x116b8, 0x116b8,
+ 0x11700, 0x1171a,
+ 0x11800, 0x1182b,
+ 0x118a0, 0x118df,
+ 0x118ff, 0x118ff,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d0,
+ 0x119e1, 0x119e1,
+ 0x119e3, 0x119e3,
+ 0x11a00, 0x11a00,
+ 0x11a0b, 0x11a32,
+ 0x11a3a, 0x11a3a,
+ 0x11a50, 0x11a50,
+ 0x11a5c, 0x11a89,
+ 0x11a9d, 0x11a9d,
+ 0x11ac0, 0x11af8,
+ 0x11c00, 0x11c08,
+ 0x11c0a, 0x11c2e,
+ 0x11c40, 0x11c40,
+ 0x11c72, 0x11c8f,
+ 0x11d00, 0x11d06,
+ 0x11d08, 0x11d09,
+ 0x11d0b, 0x11d30,
+ 0x11d46, 0x11d46,
+ 0x11d60, 0x11d65,
+ 0x11d67, 0x11d68,
+ 0x11d6a, 0x11d89,
+ 0x11d98, 0x11d98,
+ 0x11ee0, 0x11ef2,
+ 0x12000, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16ad0, 0x16aed,
+ 0x16b00, 0x16b2f,
+ 0x16b40, 0x16b43,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e7f,
+ 0x16f00, 0x16f4a,
+ 0x16f50, 0x16f50,
+ 0x16f93, 0x16f9f,
+ 0x16fe0, 0x16fe1,
+ 0x16fe3, 0x16fe3,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 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, 0x1d6c0,
+ 0x1d6c2, 0x1d6da,
+ 0x1d6dc, 0x1d6fa,
+ 0x1d6fc, 0x1d714,
+ 0x1d716, 0x1d734,
+ 0x1d736, 0x1d74e,
+ 0x1d750, 0x1d76e,
+ 0x1d770, 0x1d788,
+ 0x1d78a, 0x1d7a8,
+ 0x1d7aa, 0x1d7c2,
+ 0x1d7c4, 0x1d7cb,
+ 0x1e100, 0x1e12c,
+ 0x1e137, 0x1e13d,
+ 0x1e14e, 0x1e14e,
+ 0x1e2c0, 0x1e2eb,
+ 0x1e800, 0x1e8c4,
+ 0x1e900, 0x1e943,
+ 0x1e94b, 0x1e94b,
+ 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,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+}; /* CR_XID_Start */
+
+/* 'XID_Continue': Derived Property */
+static const OnigCodePoint CR_XID_Continue[] = {
+ 720,
+ 0x0030, 0x0039,
+ 0x0041, 0x005a,
+ 0x005f, 0x005f,
+ 0x0061, 0x007a,
+ 0x00aa, 0x00aa,
+ 0x00b5, 0x00b5,
+ 0x00b7, 0x00b7,
+ 0x00ba, 0x00ba,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00f6,
+ 0x00f8, 0x02c1,
+ 0x02c6, 0x02d1,
+ 0x02e0, 0x02e4,
+ 0x02ec, 0x02ec,
+ 0x02ee, 0x02ee,
+ 0x0300, 0x0374,
+ 0x0376, 0x0377,
+ 0x037b, 0x037d,
+ 0x037f, 0x037f,
+ 0x0386, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03f5,
+ 0x03f7, 0x0481,
+ 0x0483, 0x0487,
+ 0x048a, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x0559,
+ 0x0560, 0x0588,
+ 0x0591, 0x05bd,
+ 0x05bf, 0x05bf,
+ 0x05c1, 0x05c2,
+ 0x05c4, 0x05c5,
+ 0x05c7, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f2,
+ 0x0610, 0x061a,
+ 0x0620, 0x0669,
+ 0x066e, 0x06d3,
+ 0x06d5, 0x06dc,
+ 0x06df, 0x06e8,
+ 0x06ea, 0x06fc,
+ 0x06ff, 0x06ff,
+ 0x0710, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07f5,
+ 0x07fa, 0x07fa,
+ 0x07fd, 0x07fd,
+ 0x0800, 0x082d,
+ 0x0840, 0x085b,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x08d3, 0x08e1,
+ 0x08e3, 0x0963,
+ 0x0966, 0x096f,
+ 0x0971, 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, 0x09f1,
+ 0x09fc, 0x09fc,
+ 0x09fe, 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, 0x0a75,
+ 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, 0x0aef,
+ 0x0af9, 0x0aff,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b63,
+ 0x0b66, 0x0b6f,
+ 0x0b71, 0x0b71,
+ 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, 0x0bef,
+ 0x0c00, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c80, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d00, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4e,
+ 0x0d54, 0x0d57,
+ 0x0d5f, 0x0d63,
+ 0x0d66, 0x0d6f,
+ 0x0d7a, 0x0d7f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0de6, 0x0def,
+ 0x0df2, 0x0df3,
+ 0x0e01, 0x0e3a,
+ 0x0e40, 0x0e4e,
+ 0x0e50, 0x0e59,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e86, 0x0e8a,
+ 0x0e8c, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edf,
+ 0x0f00, 0x0f00,
+ 0x0f18, 0x0f19,
+ 0x0f20, 0x0f29,
+ 0x0f35, 0x0f35,
+ 0x0f37, 0x0f37,
+ 0x0f39, 0x0f39,
+ 0x0f3e, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f71, 0x0f84,
+ 0x0f86, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fc6, 0x0fc6,
+ 0x1000, 0x1049,
+ 0x1050, 0x109d,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x10fa,
+ 0x10fc, 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, 0x135f,
+ 0x1369, 0x1371,
+ 0x1380, 0x138f,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0x1401, 0x166c,
+ 0x166f, 0x167f,
+ 0x1681, 0x169a,
+ 0x16a0, 0x16ea,
+ 0x16ee, 0x16f8,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1734,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17d3,
+ 0x17d7, 0x17d7,
+ 0x17dc, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x180b, 0x180d,
+ 0x1810, 0x1819,
+ 0x1820, 0x1878,
+ 0x1880, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191e,
+ 0x1920, 0x192b,
+ 0x1930, 0x193b,
+ 0x1946, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19ab,
+ 0x19b0, 0x19c9,
+ 0x19d0, 0x19da,
+ 0x1a00, 0x1a1b,
+ 0x1a20, 0x1a5e,
+ 0x1a60, 0x1a7c,
+ 0x1a7f, 0x1a89,
+ 0x1a90, 0x1a99,
+ 0x1aa7, 0x1aa7,
+ 0x1ab0, 0x1abd,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b59,
+ 0x1b6b, 0x1b73,
+ 0x1b80, 0x1bf3,
+ 0x1c00, 0x1c37,
+ 0x1c40, 0x1c49,
+ 0x1c4d, 0x1c7d,
+ 0x1c80, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x1cd0, 0x1cd2,
+ 0x1cd4, 0x1cfa,
+ 0x1d00, 0x1df9,
+ 0x1dfb, 0x1f15,
+ 0x1f18, 0x1f1d,
+ 0x1f20, 0x1f45,
+ 0x1f48, 0x1f4d,
+ 0x1f50, 0x1f57,
+ 0x1f59, 0x1f59,
+ 0x1f5b, 0x1f5b,
+ 0x1f5d, 0x1f5d,
+ 0x1f5f, 0x1f7d,
+ 0x1f80, 0x1fb4,
+ 0x1fb6, 0x1fbc,
+ 0x1fbe, 0x1fbe,
+ 0x1fc2, 0x1fc4,
+ 0x1fc6, 0x1fcc,
+ 0x1fd0, 0x1fd3,
+ 0x1fd6, 0x1fdb,
+ 0x1fe0, 0x1fec,
+ 0x1ff2, 0x1ff4,
+ 0x1ff6, 0x1ffc,
+ 0x203f, 0x2040,
+ 0x2054, 0x2054,
+ 0x2071, 0x2071,
+ 0x207f, 0x207f,
+ 0x2090, 0x209c,
+ 0x20d0, 0x20dc,
+ 0x20e1, 0x20e1,
+ 0x20e5, 0x20f0,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210a, 0x2113,
+ 0x2115, 0x2115,
+ 0x2118, 0x211d,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212a, 0x2139,
+ 0x213c, 0x213f,
+ 0x2145, 0x2149,
+ 0x214e, 0x214e,
+ 0x2160, 0x2188,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2ce4,
+ 0x2ceb, 0x2cf3,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0x2d30, 0x2d67,
+ 0x2d6f, 0x2d6f,
+ 0x2d7f, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x2de0, 0x2dff,
+ 0x3005, 0x3007,
+ 0x3021, 0x302f,
+ 0x3031, 0x3035,
+ 0x3038, 0x303c,
+ 0x3041, 0x3096,
+ 0x3099, 0x309a,
+ 0x309d, 0x309f,
+ 0x30a1, 0x30fa,
+ 0x30fc, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x31a0, 0x31ba,
+ 0x31f0, 0x31ff,
+ 0x3400, 0x4db5,
+ 0x4e00, 0x9fef,
+ 0xa000, 0xa48c,
+ 0xa4d0, 0xa4fd,
+ 0xa500, 0xa60c,
+ 0xa610, 0xa62b,
+ 0xa640, 0xa66f,
+ 0xa674, 0xa67d,
+ 0xa67f, 0xa6f1,
+ 0xa717, 0xa71f,
+ 0xa722, 0xa788,
+ 0xa78b, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f7, 0xa827,
+ 0xa840, 0xa873,
+ 0xa880, 0xa8c5,
+ 0xa8d0, 0xa8d9,
+ 0xa8e0, 0xa8f7,
+ 0xa8fb, 0xa8fb,
+ 0xa8fd, 0xa92d,
+ 0xa930, 0xa953,
+ 0xa960, 0xa97c,
+ 0xa980, 0xa9c0,
+ 0xa9cf, 0xa9d9,
+ 0xa9e0, 0xa9fe,
+ 0xaa00, 0xaa36,
+ 0xaa40, 0xaa4d,
+ 0xaa50, 0xaa59,
+ 0xaa60, 0xaa76,
+ 0xaa7a, 0xaac2,
+ 0xaadb, 0xaadd,
+ 0xaae0, 0xaaef,
+ 0xaaf2, 0xaaf6,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xab30, 0xab5a,
+ 0xab5c, 0xab67,
+ 0xab70, 0xabea,
+ 0xabec, 0xabed,
+ 0xabf0, 0xabf9,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xf900, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb28,
+ 0xfb2a, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfc5d,
+ 0xfc64, 0xfd3d,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdf9,
+ 0xfe00, 0xfe0f,
+ 0xfe20, 0xfe2f,
+ 0xfe33, 0xfe34,
+ 0xfe4d, 0xfe4f,
+ 0xfe71, 0xfe71,
+ 0xfe73, 0xfe73,
+ 0xfe77, 0xfe77,
+ 0xfe79, 0xfe79,
+ 0xfe7b, 0xfe7b,
+ 0xfe7d, 0xfe7d,
+ 0xfe7f, 0xfefc,
+ 0xff10, 0xff19,
+ 0xff21, 0xff3a,
+ 0xff3f, 0xff3f,
+ 0xff41, 0xff5a,
+ 0xff66, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0x10000, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+ 0x10140, 0x10174,
+ 0x101fd, 0x101fd,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x102e0, 0x102e0,
+ 0x10300, 0x1031f,
+ 0x1032d, 0x1034a,
+ 0x10350, 0x1037a,
+ 0x10380, 0x1039d,
+ 0x103a0, 0x103c3,
+ 0x103c8, 0x103cf,
+ 0x103d1, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10860, 0x10876,
+ 0x10880, 0x1089e,
+ 0x108e0, 0x108f2,
+ 0x108f4, 0x108f5,
+ 0x10900, 0x10915,
+ 0x10920, 0x10939,
+ 0x10980, 0x109b7,
+ 0x109be, 0x109bf,
+ 0x10a00, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a35,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a3f,
+ 0x10a60, 0x10a7c,
+ 0x10a80, 0x10a9c,
+ 0x10ac0, 0x10ac7,
+ 0x10ac9, 0x10ae6,
+ 0x10b00, 0x10b35,
+ 0x10b40, 0x10b55,
+ 0x10b60, 0x10b72,
+ 0x10b80, 0x10b91,
+ 0x10c00, 0x10c48,
+ 0x10c80, 0x10cb2,
+ 0x10cc0, 0x10cf2,
+ 0x10d00, 0x10d27,
+ 0x10d30, 0x10d39,
+ 0x10f00, 0x10f1c,
+ 0x10f27, 0x10f27,
+ 0x10f30, 0x10f50,
+ 0x10fe0, 0x10ff6,
+ 0x11000, 0x11046,
+ 0x11066, 0x1106f,
+ 0x1107f, 0x110ba,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x1113f,
+ 0x11144, 0x11146,
+ 0x11150, 0x11173,
+ 0x11176, 0x11176,
+ 0x11180, 0x111c4,
+ 0x111c9, 0x111cc,
+ 0x111d0, 0x111da,
+ 0x111dc, 0x111dc,
+ 0x11200, 0x11211,
+ 0x11213, 0x11237,
+ 0x1123e, 0x1123e,
+ 0x11280, 0x11286,
+ 0x11288, 0x11288,
+ 0x1128a, 0x1128d,
+ 0x1128f, 0x1129d,
+ 0x1129f, 0x112a8,
+ 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, 0x1144a,
+ 0x11450, 0x11459,
+ 0x1145e, 0x1145f,
+ 0x11480, 0x114c5,
+ 0x114c7, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115c0,
+ 0x115d8, 0x115dd,
+ 0x11600, 0x11640,
+ 0x11644, 0x11644,
+ 0x11650, 0x11659,
+ 0x11680, 0x116b8,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x1171a,
+ 0x1171d, 0x1172b,
+ 0x11730, 0x11739,
+ 0x11800, 0x1183a,
+ 0x118a0, 0x118e9,
+ 0x118ff, 0x118ff,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d7,
+ 0x119da, 0x119e1,
+ 0x119e3, 0x119e4,
+ 0x11a00, 0x11a3e,
+ 0x11a47, 0x11a47,
+ 0x11a50, 0x11a99,
+ 0x11a9d, 0x11a9d,
+ 0x11ac0, 0x11af8,
+ 0x11c00, 0x11c08,
+ 0x11c0a, 0x11c36,
+ 0x11c38, 0x11c40,
+ 0x11c50, 0x11c59,
+ 0x11c72, 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, 0x11ef6,
+ 0x12000, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af4,
+ 0x16b00, 0x16b36,
+ 0x16b40, 0x16b43,
+ 0x16b50, 0x16b59,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e7f,
+ 0x16f00, 0x16f4a,
+ 0x16f4f, 0x16f87,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe1,
+ 0x16fe3, 0x16fe3,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9d, 0x1bc9e,
+ 0x1d165, 0x1d169,
+ 0x1d16d, 0x1d172,
+ 0x1d17b, 0x1d182,
+ 0x1d185, 0x1d18b,
+ 0x1d1aa, 0x1d1ad,
+ 0x1d242, 0x1d244,
+ 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, 0x1d6c0,
+ 0x1d6c2, 0x1d6da,
+ 0x1d6dc, 0x1d6fa,
+ 0x1d6fc, 0x1d714,
+ 0x1d716, 0x1d734,
+ 0x1d736, 0x1d74e,
+ 0x1d750, 0x1d76e,
+ 0x1d770, 0x1d788,
+ 0x1d78a, 0x1d7a8,
+ 0x1d7aa, 0x1d7c2,
+ 0x1d7c4, 0x1d7cb,
+ 0x1d7ce, 0x1d7ff,
+ 0x1da00, 0x1da36,
+ 0x1da3b, 0x1da6c,
+ 0x1da75, 0x1da75,
+ 0x1da84, 0x1da84,
+ 0x1da9b, 0x1da9f,
+ 0x1daa1, 0x1daaf,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e100, 0x1e12c,
+ 0x1e130, 0x1e13d,
+ 0x1e140, 0x1e149,
+ 0x1e14e, 0x1e14e,
+ 0x1e2c0, 0x1e2f9,
+ 0x1e800, 0x1e8c4,
+ 0x1e8d0, 0x1e8d6,
+ 0x1e900, 0x1e94b,
+ 0x1e950, 0x1e959,
+ 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,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+ 0xe0100, 0xe01ef,
+}; /* CR_XID_Continue */
+
+/* 'Default_Ignorable_Code_Point': Derived Property */
+static const OnigCodePoint CR_Default_Ignorable_Code_Point[] = {
+ 17,
+ 0x00ad, 0x00ad,
+ 0x034f, 0x034f,
+ 0x061c, 0x061c,
+ 0x115f, 0x1160,
+ 0x17b4, 0x17b5,
+ 0x180b, 0x180e,
+ 0x200b, 0x200f,
+ 0x202a, 0x202e,
+ 0x2060, 0x206f,
+ 0x3164, 0x3164,
+ 0xfe00, 0xfe0f,
+ 0xfeff, 0xfeff,
+ 0xffa0, 0xffa0,
+ 0xfff0, 0xfff8,
+ 0x1bca0, 0x1bca3,
+ 0x1d173, 0x1d17a,
+ 0xe0000, 0xe0fff,
+}; /* CR_Default_Ignorable_Code_Point */
+
+/* 'Grapheme_Extend': Derived Property */
+static const OnigCodePoint CR_Grapheme_Extend[] = {
+ 335,
+ 0x0300, 0x036f,
+ 0x0483, 0x0489,
+ 0x0591, 0x05bd,
+ 0x05bf, 0x05bf,
+ 0x05c1, 0x05c2,
+ 0x05c4, 0x05c5,
+ 0x05c7, 0x05c7,
+ 0x0610, 0x061a,
+ 0x064b, 0x065f,
+ 0x0670, 0x0670,
+ 0x06d6, 0x06dc,
+ 0x06df, 0x06e4,
+ 0x06e7, 0x06e8,
+ 0x06ea, 0x06ed,
+ 0x0711, 0x0711,
+ 0x0730, 0x074a,
+ 0x07a6, 0x07b0,
+ 0x07eb, 0x07f3,
+ 0x07fd, 0x07fd,
+ 0x0816, 0x0819,
+ 0x081b, 0x0823,
+ 0x0825, 0x0827,
+ 0x0829, 0x082d,
+ 0x0859, 0x085b,
+ 0x08d3, 0x08e1,
+ 0x08e3, 0x0902,
+ 0x093a, 0x093a,
+ 0x093c, 0x093c,
+ 0x0941, 0x0948,
+ 0x094d, 0x094d,
+ 0x0951, 0x0957,
+ 0x0962, 0x0963,
+ 0x0981, 0x0981,
+ 0x09bc, 0x09bc,
+ 0x09be, 0x09be,
+ 0x09c1, 0x09c4,
+ 0x09cd, 0x09cd,
+ 0x09d7, 0x09d7,
+ 0x09e2, 0x09e3,
+ 0x09fe, 0x09fe,
+ 0x0a01, 0x0a02,
+ 0x0a3c, 0x0a3c,
+ 0x0a41, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a51, 0x0a51,
+ 0x0a70, 0x0a71,
+ 0x0a75, 0x0a75,
+ 0x0a81, 0x0a82,
+ 0x0abc, 0x0abc,
+ 0x0ac1, 0x0ac5,
+ 0x0ac7, 0x0ac8,
+ 0x0acd, 0x0acd,
+ 0x0ae2, 0x0ae3,
+ 0x0afa, 0x0aff,
+ 0x0b01, 0x0b01,
+ 0x0b3c, 0x0b3c,
+ 0x0b3e, 0x0b3f,
+ 0x0b41, 0x0b44,
+ 0x0b4d, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b62, 0x0b63,
+ 0x0b82, 0x0b82,
+ 0x0bbe, 0x0bbe,
+ 0x0bc0, 0x0bc0,
+ 0x0bcd, 0x0bcd,
+ 0x0bd7, 0x0bd7,
+ 0x0c00, 0x0c00,
+ 0x0c04, 0x0c04,
+ 0x0c3e, 0x0c40,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c62, 0x0c63,
+ 0x0c81, 0x0c81,
+ 0x0cbc, 0x0cbc,
+ 0x0cbf, 0x0cbf,
+ 0x0cc2, 0x0cc2,
+ 0x0cc6, 0x0cc6,
+ 0x0ccc, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0ce2, 0x0ce3,
+ 0x0d00, 0x0d01,
+ 0x0d3b, 0x0d3c,
+ 0x0d3e, 0x0d3e,
+ 0x0d41, 0x0d44,
+ 0x0d4d, 0x0d4d,
+ 0x0d57, 0x0d57,
+ 0x0d62, 0x0d63,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dcf,
+ 0x0dd2, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0ddf, 0x0ddf,
+ 0x0e31, 0x0e31,
+ 0x0e34, 0x0e3a,
+ 0x0e47, 0x0e4e,
+ 0x0eb1, 0x0eb1,
+ 0x0eb4, 0x0ebc,
+ 0x0ec8, 0x0ecd,
+ 0x0f18, 0x0f19,
+ 0x0f35, 0x0f35,
+ 0x0f37, 0x0f37,
+ 0x0f39, 0x0f39,
+ 0x0f71, 0x0f7e,
+ 0x0f80, 0x0f84,
+ 0x0f86, 0x0f87,
+ 0x0f8d, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fc6, 0x0fc6,
+ 0x102d, 0x1030,
+ 0x1032, 0x1037,
+ 0x1039, 0x103a,
+ 0x103d, 0x103e,
+ 0x1058, 0x1059,
+ 0x105e, 0x1060,
+ 0x1071, 0x1074,
+ 0x1082, 0x1082,
+ 0x1085, 0x1086,
+ 0x108d, 0x108d,
+ 0x109d, 0x109d,
+ 0x135d, 0x135f,
+ 0x1712, 0x1714,
+ 0x1732, 0x1734,
+ 0x1752, 0x1753,
+ 0x1772, 0x1773,
+ 0x17b4, 0x17b5,
+ 0x17b7, 0x17bd,
+ 0x17c6, 0x17c6,
+ 0x17c9, 0x17d3,
+ 0x17dd, 0x17dd,
+ 0x180b, 0x180d,
+ 0x1885, 0x1886,
+ 0x18a9, 0x18a9,
+ 0x1920, 0x1922,
+ 0x1927, 0x1928,
+ 0x1932, 0x1932,
+ 0x1939, 0x193b,
+ 0x1a17, 0x1a18,
+ 0x1a1b, 0x1a1b,
+ 0x1a56, 0x1a56,
+ 0x1a58, 0x1a5e,
+ 0x1a60, 0x1a60,
+ 0x1a62, 0x1a62,
+ 0x1a65, 0x1a6c,
+ 0x1a73, 0x1a7c,
+ 0x1a7f, 0x1a7f,
+ 0x1ab0, 0x1abe,
+ 0x1b00, 0x1b03,
+ 0x1b34, 0x1b3a,
+ 0x1b3c, 0x1b3c,
+ 0x1b42, 0x1b42,
+ 0x1b6b, 0x1b73,
+ 0x1b80, 0x1b81,
+ 0x1ba2, 0x1ba5,
+ 0x1ba8, 0x1ba9,
+ 0x1bab, 0x1bad,
+ 0x1be6, 0x1be6,
+ 0x1be8, 0x1be9,
+ 0x1bed, 0x1bed,
+ 0x1bef, 0x1bf1,
+ 0x1c2c, 0x1c33,
+ 0x1c36, 0x1c37,
+ 0x1cd0, 0x1cd2,
+ 0x1cd4, 0x1ce0,
+ 0x1ce2, 0x1ce8,
+ 0x1ced, 0x1ced,
+ 0x1cf4, 0x1cf4,
+ 0x1cf8, 0x1cf9,
+ 0x1dc0, 0x1df9,
+ 0x1dfb, 0x1dff,
+ 0x200c, 0x200c,
+ 0x20d0, 0x20f0,
+ 0x2cef, 0x2cf1,
+ 0x2d7f, 0x2d7f,
+ 0x2de0, 0x2dff,
+ 0x302a, 0x302f,
+ 0x3099, 0x309a,
+ 0xa66f, 0xa672,
+ 0xa674, 0xa67d,
+ 0xa69e, 0xa69f,
+ 0xa6f0, 0xa6f1,
+ 0xa802, 0xa802,
+ 0xa806, 0xa806,
+ 0xa80b, 0xa80b,
+ 0xa825, 0xa826,
+ 0xa8c4, 0xa8c5,
+ 0xa8e0, 0xa8f1,
+ 0xa8ff, 0xa8ff,
+ 0xa926, 0xa92d,
+ 0xa947, 0xa951,
+ 0xa980, 0xa982,
+ 0xa9b3, 0xa9b3,
+ 0xa9b6, 0xa9b9,
+ 0xa9bc, 0xa9bd,
+ 0xa9e5, 0xa9e5,
+ 0xaa29, 0xaa2e,
+ 0xaa31, 0xaa32,
+ 0xaa35, 0xaa36,
+ 0xaa43, 0xaa43,
+ 0xaa4c, 0xaa4c,
+ 0xaa7c, 0xaa7c,
+ 0xaab0, 0xaab0,
+ 0xaab2, 0xaab4,
+ 0xaab7, 0xaab8,
+ 0xaabe, 0xaabf,
+ 0xaac1, 0xaac1,
+ 0xaaec, 0xaaed,
+ 0xaaf6, 0xaaf6,
+ 0xabe5, 0xabe5,
+ 0xabe8, 0xabe8,
+ 0xabed, 0xabed,
+ 0xfb1e, 0xfb1e,
+ 0xfe00, 0xfe0f,
+ 0xfe20, 0xfe2f,
+ 0xff9e, 0xff9f,
+ 0x101fd, 0x101fd,
+ 0x102e0, 0x102e0,
+ 0x10376, 0x1037a,
+ 0x10a01, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a0f,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a3f,
+ 0x10ae5, 0x10ae6,
+ 0x10d24, 0x10d27,
+ 0x10f46, 0x10f50,
+ 0x11001, 0x11001,
+ 0x11038, 0x11046,
+ 0x1107f, 0x11081,
+ 0x110b3, 0x110b6,
+ 0x110b9, 0x110ba,
+ 0x11100, 0x11102,
+ 0x11127, 0x1112b,
+ 0x1112d, 0x11134,
+ 0x11173, 0x11173,
+ 0x11180, 0x11181,
+ 0x111b6, 0x111be,
+ 0x111c9, 0x111cc,
+ 0x1122f, 0x11231,
+ 0x11234, 0x11234,
+ 0x11236, 0x11237,
+ 0x1123e, 0x1123e,
+ 0x112df, 0x112df,
+ 0x112e3, 0x112ea,
+ 0x11300, 0x11301,
+ 0x1133b, 0x1133c,
+ 0x1133e, 0x1133e,
+ 0x11340, 0x11340,
+ 0x11357, 0x11357,
+ 0x11366, 0x1136c,
+ 0x11370, 0x11374,
+ 0x11438, 0x1143f,
+ 0x11442, 0x11444,
+ 0x11446, 0x11446,
+ 0x1145e, 0x1145e,
+ 0x114b0, 0x114b0,
+ 0x114b3, 0x114b8,
+ 0x114ba, 0x114ba,
+ 0x114bd, 0x114bd,
+ 0x114bf, 0x114c0,
+ 0x114c2, 0x114c3,
+ 0x115af, 0x115af,
+ 0x115b2, 0x115b5,
+ 0x115bc, 0x115bd,
+ 0x115bf, 0x115c0,
+ 0x115dc, 0x115dd,
+ 0x11633, 0x1163a,
+ 0x1163d, 0x1163d,
+ 0x1163f, 0x11640,
+ 0x116ab, 0x116ab,
+ 0x116ad, 0x116ad,
+ 0x116b0, 0x116b5,
+ 0x116b7, 0x116b7,
+ 0x1171d, 0x1171f,
+ 0x11722, 0x11725,
+ 0x11727, 0x1172b,
+ 0x1182f, 0x11837,
+ 0x11839, 0x1183a,
+ 0x119d4, 0x119d7,
+ 0x119da, 0x119db,
+ 0x119e0, 0x119e0,
+ 0x11a01, 0x11a0a,
+ 0x11a33, 0x11a38,
+ 0x11a3b, 0x11a3e,
+ 0x11a47, 0x11a47,
+ 0x11a51, 0x11a56,
+ 0x11a59, 0x11a5b,
+ 0x11a8a, 0x11a96,
+ 0x11a98, 0x11a99,
+ 0x11c30, 0x11c36,
+ 0x11c38, 0x11c3d,
+ 0x11c3f, 0x11c3f,
+ 0x11c92, 0x11ca7,
+ 0x11caa, 0x11cb0,
+ 0x11cb2, 0x11cb3,
+ 0x11cb5, 0x11cb6,
+ 0x11d31, 0x11d36,
+ 0x11d3a, 0x11d3a,
+ 0x11d3c, 0x11d3d,
+ 0x11d3f, 0x11d45,
+ 0x11d47, 0x11d47,
+ 0x11d90, 0x11d91,
+ 0x11d95, 0x11d95,
+ 0x11d97, 0x11d97,
+ 0x11ef3, 0x11ef4,
+ 0x16af0, 0x16af4,
+ 0x16b30, 0x16b36,
+ 0x16f4f, 0x16f4f,
+ 0x16f8f, 0x16f92,
+ 0x1bc9d, 0x1bc9e,
+ 0x1d165, 0x1d165,
+ 0x1d167, 0x1d169,
+ 0x1d16e, 0x1d172,
+ 0x1d17b, 0x1d182,
+ 0x1d185, 0x1d18b,
+ 0x1d1aa, 0x1d1ad,
+ 0x1d242, 0x1d244,
+ 0x1da00, 0x1da36,
+ 0x1da3b, 0x1da6c,
+ 0x1da75, 0x1da75,
+ 0x1da84, 0x1da84,
+ 0x1da9b, 0x1da9f,
+ 0x1daa1, 0x1daaf,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e130, 0x1e136,
+ 0x1e2ec, 0x1e2ef,
+ 0x1e8d0, 0x1e8d6,
+ 0x1e944, 0x1e94a,
+ 0xe0020, 0xe007f,
+ 0xe0100, 0xe01ef,
+}; /* CR_Grapheme_Extend */
+
+/* 'Grapheme_Base': Derived Property */
+static const OnigCodePoint CR_Grapheme_Base[] = {
+ 819,
+ 0x0020, 0x007e,
+ 0x00a0, 0x00ac,
+ 0x00ae, 0x02ff,
+ 0x0370, 0x0377,
+ 0x037a, 0x037f,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x0482,
+ 0x048a, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x058a,
+ 0x058d, 0x058f,
+ 0x05be, 0x05be,
+ 0x05c0, 0x05c0,
+ 0x05c3, 0x05c3,
+ 0x05c6, 0x05c6,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f4,
+ 0x0606, 0x060f,
+ 0x061b, 0x061b,
+ 0x061e, 0x064a,
+ 0x0660, 0x066f,
+ 0x0671, 0x06d5,
+ 0x06de, 0x06de,
+ 0x06e5, 0x06e6,
+ 0x06e9, 0x06e9,
+ 0x06ee, 0x070d,
+ 0x0710, 0x0710,
+ 0x0712, 0x072f,
+ 0x074d, 0x07a5,
+ 0x07b1, 0x07b1,
+ 0x07c0, 0x07ea,
+ 0x07f4, 0x07fa,
+ 0x07fe, 0x0815,
+ 0x081a, 0x081a,
+ 0x0824, 0x0824,
+ 0x0828, 0x0828,
+ 0x0830, 0x083e,
+ 0x0840, 0x0858,
+ 0x085e, 0x085e,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x0903, 0x0939,
+ 0x093b, 0x093b,
+ 0x093d, 0x0940,
+ 0x0949, 0x094c,
+ 0x094e, 0x0950,
+ 0x0958, 0x0961,
+ 0x0964, 0x0980,
+ 0x0982, 0x0983,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bd, 0x09bd,
+ 0x09bf, 0x09c0,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09cc,
+ 0x09ce, 0x09ce,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e1,
+ 0x09e6, 0x09fd,
+ 0x0a03, 0x0a03,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a3e, 0x0a40,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a66, 0x0a6f,
+ 0x0a72, 0x0a74,
+ 0x0a76, 0x0a76,
+ 0x0a83, 0x0a83,
+ 0x0a85, 0x0a8d,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0abd, 0x0ac0,
+ 0x0ac9, 0x0ac9,
+ 0x0acb, 0x0acc,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae1,
+ 0x0ae6, 0x0af1,
+ 0x0af9, 0x0af9,
+ 0x0b02, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3d, 0x0b3d,
+ 0x0b40, 0x0b40,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4c,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b61,
+ 0x0b66, 0x0b77,
+ 0x0b83, 0x0b83,
+ 0x0b85, 0x0b8a,
+ 0x0b8e, 0x0b90,
+ 0x0b92, 0x0b95,
+ 0x0b99, 0x0b9a,
+ 0x0b9c, 0x0b9c,
+ 0x0b9e, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb9,
+ 0x0bbf, 0x0bbf,
+ 0x0bc1, 0x0bc2,
+ 0x0bc6, 0x0bc8,
+ 0x0bca, 0x0bcc,
+ 0x0bd0, 0x0bd0,
+ 0x0be6, 0x0bfa,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3d, 0x0c3d,
+ 0x0c41, 0x0c44,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c61,
+ 0x0c66, 0x0c6f,
+ 0x0c77, 0x0c80,
+ 0x0c82, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbd, 0x0cbe,
+ 0x0cc0, 0x0cc1,
+ 0x0cc3, 0x0cc4,
+ 0x0cc7, 0x0cc8,
+ 0x0cca, 0x0ccb,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce1,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d3a,
+ 0x0d3d, 0x0d3d,
+ 0x0d3f, 0x0d40,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4c,
+ 0x0d4e, 0x0d4f,
+ 0x0d54, 0x0d56,
+ 0x0d58, 0x0d61,
+ 0x0d66, 0x0d7f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dd0, 0x0dd1,
+ 0x0dd8, 0x0dde,
+ 0x0de6, 0x0def,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e30,
+ 0x0e32, 0x0e33,
+ 0x0e3f, 0x0e46,
+ 0x0e4f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e86, 0x0e8a,
+ 0x0e8c, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0eb0,
+ 0x0eb2, 0x0eb3,
+ 0x0ebd, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edf,
+ 0x0f00, 0x0f17,
+ 0x0f1a, 0x0f34,
+ 0x0f36, 0x0f36,
+ 0x0f38, 0x0f38,
+ 0x0f3a, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f7f, 0x0f7f,
+ 0x0f85, 0x0f85,
+ 0x0f88, 0x0f8c,
+ 0x0fbe, 0x0fc5,
+ 0x0fc7, 0x0fcc,
+ 0x0fce, 0x0fda,
+ 0x1000, 0x102c,
+ 0x1031, 0x1031,
+ 0x1038, 0x1038,
+ 0x103b, 0x103c,
+ 0x103f, 0x1057,
+ 0x105a, 0x105d,
+ 0x1061, 0x1070,
+ 0x1075, 0x1081,
+ 0x1083, 0x1084,
+ 0x1087, 0x108c,
+ 0x108e, 0x109c,
+ 0x109e, 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,
+ 0x1360, 0x137c,
+ 0x1380, 0x1399,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0x1400, 0x169c,
+ 0x16a0, 0x16f8,
+ 0x1700, 0x170c,
+ 0x170e, 0x1711,
+ 0x1720, 0x1731,
+ 0x1735, 0x1736,
+ 0x1740, 0x1751,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1780, 0x17b3,
+ 0x17b6, 0x17b6,
+ 0x17be, 0x17c5,
+ 0x17c7, 0x17c8,
+ 0x17d4, 0x17dc,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180a,
+ 0x1810, 0x1819,
+ 0x1820, 0x1878,
+ 0x1880, 0x1884,
+ 0x1887, 0x18a8,
+ 0x18aa, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191e,
+ 0x1923, 0x1926,
+ 0x1929, 0x192b,
+ 0x1930, 0x1931,
+ 0x1933, 0x1938,
+ 0x1940, 0x1940,
+ 0x1944, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19ab,
+ 0x19b0, 0x19c9,
+ 0x19d0, 0x19da,
+ 0x19de, 0x1a16,
+ 0x1a19, 0x1a1a,
+ 0x1a1e, 0x1a55,
+ 0x1a57, 0x1a57,
+ 0x1a61, 0x1a61,
+ 0x1a63, 0x1a64,
+ 0x1a6d, 0x1a72,
+ 0x1a80, 0x1a89,
+ 0x1a90, 0x1a99,
+ 0x1aa0, 0x1aad,
+ 0x1b04, 0x1b33,
+ 0x1b3b, 0x1b3b,
+ 0x1b3d, 0x1b41,
+ 0x1b43, 0x1b4b,
+ 0x1b50, 0x1b6a,
+ 0x1b74, 0x1b7c,
+ 0x1b82, 0x1ba1,
+ 0x1ba6, 0x1ba7,
+ 0x1baa, 0x1baa,
+ 0x1bae, 0x1be5,
+ 0x1be7, 0x1be7,
+ 0x1bea, 0x1bec,
+ 0x1bee, 0x1bee,
+ 0x1bf2, 0x1bf3,
+ 0x1bfc, 0x1c2b,
+ 0x1c34, 0x1c35,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cc7,
+ 0x1cd3, 0x1cd3,
+ 0x1ce1, 0x1ce1,
+ 0x1ce9, 0x1cec,
+ 0x1cee, 0x1cf3,
+ 0x1cf5, 0x1cf7,
+ 0x1cfa, 0x1cfa,
+ 0x1d00, 0x1dbf,
+ 0x1e00, 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, 0x200a,
+ 0x2010, 0x2027,
+ 0x202f, 0x205f,
+ 0x2070, 0x2071,
+ 0x2074, 0x208e,
+ 0x2090, 0x209c,
+ 0x20a0, 0x20bf,
+ 0x2100, 0x218b,
+ 0x2190, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b98, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2cee,
+ 0x2cf2, 0x2cf3,
+ 0x2cf9, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0x2d30, 0x2d67,
+ 0x2d6f, 0x2d70,
+ 0x2d80, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x2e00, 0x2e4f,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x3029,
+ 0x3030, 0x303f,
+ 0x3041, 0x3096,
+ 0x309b, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x3190, 0x31ba,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x4db5,
+ 0x4dc0, 0x9fef,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa66e,
+ 0xa673, 0xa673,
+ 0xa67e, 0xa69d,
+ 0xa6a0, 0xa6ef,
+ 0xa6f2, 0xa6f7,
+ 0xa700, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f7, 0xa801,
+ 0xa803, 0xa805,
+ 0xa807, 0xa80a,
+ 0xa80c, 0xa824,
+ 0xa827, 0xa82b,
+ 0xa830, 0xa839,
+ 0xa840, 0xa877,
+ 0xa880, 0xa8c3,
+ 0xa8ce, 0xa8d9,
+ 0xa8f2, 0xa8fe,
+ 0xa900, 0xa925,
+ 0xa92e, 0xa946,
+ 0xa952, 0xa953,
+ 0xa95f, 0xa97c,
+ 0xa983, 0xa9b2,
+ 0xa9b4, 0xa9b5,
+ 0xa9ba, 0xa9bb,
+ 0xa9be, 0xa9cd,
+ 0xa9cf, 0xa9d9,
+ 0xa9de, 0xa9e4,
+ 0xa9e6, 0xa9fe,
+ 0xaa00, 0xaa28,
+ 0xaa2f, 0xaa30,
+ 0xaa33, 0xaa34,
+ 0xaa40, 0xaa42,
+ 0xaa44, 0xaa4b,
+ 0xaa4d, 0xaa4d,
+ 0xaa50, 0xaa59,
+ 0xaa5c, 0xaa7b,
+ 0xaa7d, 0xaaaf,
+ 0xaab1, 0xaab1,
+ 0xaab5, 0xaab6,
+ 0xaab9, 0xaabd,
+ 0xaac0, 0xaac0,
+ 0xaac2, 0xaac2,
+ 0xaadb, 0xaaeb,
+ 0xaaee, 0xaaf5,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xab30, 0xab67,
+ 0xab70, 0xabe4,
+ 0xabe6, 0xabe7,
+ 0xabe9, 0xabec,
+ 0xabf0, 0xabf9,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xf900, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb1d,
+ 0xfb1f, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbc1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfd,
+ 0xfe10, 0xfe19,
+ 0xfe30, 0xfe52,
+ 0xfe54, 0xfe66,
+ 0xfe68, 0xfe6b,
+ 0xfe70, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0xff01, 0xff9d,
+ 0xffa0, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0xffe0, 0xffe6,
+ 0xffe8, 0xffee,
+ 0xfffc, 0xfffd,
+ 0x10000, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+ 0x10100, 0x10102,
+ 0x10107, 0x10133,
+ 0x10137, 0x1018e,
+ 0x10190, 0x1019b,
+ 0x101a0, 0x101a0,
+ 0x101d0, 0x101fc,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x102e1, 0x102fb,
+ 0x10300, 0x10323,
+ 0x1032d, 0x1034a,
+ 0x10350, 0x10375,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x103c3,
+ 0x103c8, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x1056f, 0x1056f,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 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, 0x10a00,
+ 0x10a10, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a35,
+ 0x10a40, 0x10a48,
+ 0x10a50, 0x10a58,
+ 0x10a60, 0x10a9f,
+ 0x10ac0, 0x10ae4,
+ 0x10aeb, 0x10af6,
+ 0x10b00, 0x10b35,
+ 0x10b39, 0x10b55,
+ 0x10b58, 0x10b72,
+ 0x10b78, 0x10b91,
+ 0x10b99, 0x10b9c,
+ 0x10ba9, 0x10baf,
+ 0x10c00, 0x10c48,
+ 0x10c80, 0x10cb2,
+ 0x10cc0, 0x10cf2,
+ 0x10cfa, 0x10d23,
+ 0x10d30, 0x10d39,
+ 0x10e60, 0x10e7e,
+ 0x10f00, 0x10f27,
+ 0x10f30, 0x10f45,
+ 0x10f51, 0x10f59,
+ 0x10fe0, 0x10ff6,
+ 0x11000, 0x11000,
+ 0x11002, 0x11037,
+ 0x11047, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x11082, 0x110b2,
+ 0x110b7, 0x110b8,
+ 0x110bb, 0x110bc,
+ 0x110be, 0x110c1,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11103, 0x11126,
+ 0x1112c, 0x1112c,
+ 0x11136, 0x11146,
+ 0x11150, 0x11172,
+ 0x11174, 0x11176,
+ 0x11182, 0x111b5,
+ 0x111bf, 0x111c8,
+ 0x111cd, 0x111cd,
+ 0x111d0, 0x111df,
+ 0x111e1, 0x111f4,
+ 0x11200, 0x11211,
+ 0x11213, 0x1122e,
+ 0x11232, 0x11233,
+ 0x11235, 0x11235,
+ 0x11238, 0x1123d,
+ 0x11280, 0x11286,
+ 0x11288, 0x11288,
+ 0x1128a, 0x1128d,
+ 0x1128f, 0x1129d,
+ 0x1129f, 0x112a9,
+ 0x112b0, 0x112de,
+ 0x112e0, 0x112e2,
+ 0x112f0, 0x112f9,
+ 0x11302, 0x11303,
+ 0x11305, 0x1130c,
+ 0x1130f, 0x11310,
+ 0x11313, 0x11328,
+ 0x1132a, 0x11330,
+ 0x11332, 0x11333,
+ 0x11335, 0x11339,
+ 0x1133d, 0x1133d,
+ 0x1133f, 0x1133f,
+ 0x11341, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134b, 0x1134d,
+ 0x11350, 0x11350,
+ 0x1135d, 0x11363,
+ 0x11400, 0x11437,
+ 0x11440, 0x11441,
+ 0x11445, 0x11445,
+ 0x11447, 0x11459,
+ 0x1145b, 0x1145b,
+ 0x1145d, 0x1145d,
+ 0x1145f, 0x1145f,
+ 0x11480, 0x114af,
+ 0x114b1, 0x114b2,
+ 0x114b9, 0x114b9,
+ 0x114bb, 0x114bc,
+ 0x114be, 0x114be,
+ 0x114c1, 0x114c1,
+ 0x114c4, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115ae,
+ 0x115b0, 0x115b1,
+ 0x115b8, 0x115bb,
+ 0x115be, 0x115be,
+ 0x115c1, 0x115db,
+ 0x11600, 0x11632,
+ 0x1163b, 0x1163c,
+ 0x1163e, 0x1163e,
+ 0x11641, 0x11644,
+ 0x11650, 0x11659,
+ 0x11660, 0x1166c,
+ 0x11680, 0x116aa,
+ 0x116ac, 0x116ac,
+ 0x116ae, 0x116af,
+ 0x116b6, 0x116b6,
+ 0x116b8, 0x116b8,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x1171a,
+ 0x11720, 0x11721,
+ 0x11726, 0x11726,
+ 0x11730, 0x1173f,
+ 0x11800, 0x1182e,
+ 0x11838, 0x11838,
+ 0x1183b, 0x1183b,
+ 0x118a0, 0x118f2,
+ 0x118ff, 0x118ff,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d3,
+ 0x119dc, 0x119df,
+ 0x119e1, 0x119e4,
+ 0x11a00, 0x11a00,
+ 0x11a0b, 0x11a32,
+ 0x11a39, 0x11a3a,
+ 0x11a3f, 0x11a46,
+ 0x11a50, 0x11a50,
+ 0x11a57, 0x11a58,
+ 0x11a5c, 0x11a89,
+ 0x11a97, 0x11a97,
+ 0x11a9a, 0x11aa2,
+ 0x11ac0, 0x11af8,
+ 0x11c00, 0x11c08,
+ 0x11c0a, 0x11c2f,
+ 0x11c3e, 0x11c3e,
+ 0x11c40, 0x11c45,
+ 0x11c50, 0x11c6c,
+ 0x11c70, 0x11c8f,
+ 0x11ca9, 0x11ca9,
+ 0x11cb1, 0x11cb1,
+ 0x11cb4, 0x11cb4,
+ 0x11d00, 0x11d06,
+ 0x11d08, 0x11d09,
+ 0x11d0b, 0x11d30,
+ 0x11d46, 0x11d46,
+ 0x11d50, 0x11d59,
+ 0x11d60, 0x11d65,
+ 0x11d67, 0x11d68,
+ 0x11d6a, 0x11d8e,
+ 0x11d93, 0x11d94,
+ 0x11d96, 0x11d96,
+ 0x11d98, 0x11d98,
+ 0x11da0, 0x11da9,
+ 0x11ee0, 0x11ef2,
+ 0x11ef5, 0x11ef8,
+ 0x11fc0, 0x11ff1,
+ 0x11fff, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12470, 0x12474,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16a6e, 0x16a6f,
+ 0x16ad0, 0x16aed,
+ 0x16af5, 0x16af5,
+ 0x16b00, 0x16b2f,
+ 0x16b37, 0x16b45,
+ 0x16b50, 0x16b59,
+ 0x16b5b, 0x16b61,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e9a,
+ 0x16f00, 0x16f4a,
+ 0x16f50, 0x16f87,
+ 0x16f93, 0x16f9f,
+ 0x16fe0, 0x16fe3,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9c, 0x1bc9c,
+ 0x1bc9f, 0x1bc9f,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d164,
+ 0x1d166, 0x1d166,
+ 0x1d16a, 0x1d16d,
+ 0x1d183, 0x1d184,
+ 0x1d18c, 0x1d1a9,
+ 0x1d1ae, 0x1d1e8,
+ 0x1d200, 0x1d241,
+ 0x1d245, 0x1d245,
+ 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, 0x1d9ff,
+ 0x1da37, 0x1da3a,
+ 0x1da6d, 0x1da74,
+ 0x1da76, 0x1da83,
+ 0x1da85, 0x1da8b,
+ 0x1e100, 0x1e12c,
+ 0x1e137, 0x1e13d,
+ 0x1e140, 0x1e149,
+ 0x1e14e, 0x1e14f,
+ 0x1e2c0, 0x1e2eb,
+ 0x1e2f0, 0x1e2f9,
+ 0x1e2ff, 0x1e2ff,
+ 0x1e800, 0x1e8c4,
+ 0x1e8c7, 0x1e8cf,
+ 0x1e900, 0x1e943,
+ 0x1e94b, 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, 0x1f10c,
+ 0x1f110, 0x1f16c,
+ 0x1f170, 0x1f1ac,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23b,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f260, 0x1f265,
+ 0x1f300, 0x1f6d5,
+ 0x1f6e0, 0x1f6ec,
+ 0x1f6f0, 0x1f6fa,
+ 0x1f700, 0x1f773,
+ 0x1f780, 0x1f7d8,
+ 0x1f7e0, 0x1f7eb,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1f900, 0x1f90b,
+ 0x1f90d, 0x1f971,
+ 0x1f973, 0x1f976,
+ 0x1f97a, 0x1f9a2,
+ 0x1f9a5, 0x1f9aa,
+ 0x1f9ae, 0x1f9ca,
+ 0x1f9cd, 0x1fa53,
+ 0x1fa60, 0x1fa6d,
+ 0x1fa70, 0x1fa73,
+ 0x1fa78, 0x1fa7a,
+ 0x1fa80, 0x1fa82,
+ 0x1fa90, 0x1fa95,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+}; /* CR_Grapheme_Base */
+
+/* 'Grapheme_Link': Derived Property */
+static const OnigCodePoint CR_Grapheme_Link[] = {
+ 52,
+ 0x094d, 0x094d,
+ 0x09cd, 0x09cd,
+ 0x0a4d, 0x0a4d,
+ 0x0acd, 0x0acd,
+ 0x0b4d, 0x0b4d,
+ 0x0bcd, 0x0bcd,
+ 0x0c4d, 0x0c4d,
+ 0x0ccd, 0x0ccd,
+ 0x0d3b, 0x0d3c,
+ 0x0d4d, 0x0d4d,
+ 0x0dca, 0x0dca,
+ 0x0e3a, 0x0e3a,
+ 0x0eba, 0x0eba,
+ 0x0f84, 0x0f84,
+ 0x1039, 0x103a,
+ 0x1714, 0x1714,
+ 0x1734, 0x1734,
+ 0x17d2, 0x17d2,
+ 0x1a60, 0x1a60,
+ 0x1b44, 0x1b44,
+ 0x1baa, 0x1bab,
+ 0x1bf2, 0x1bf3,
+ 0x2d7f, 0x2d7f,
+ 0xa806, 0xa806,
+ 0xa8c4, 0xa8c4,
+ 0xa953, 0xa953,
+ 0xa9c0, 0xa9c0,
+ 0xaaf6, 0xaaf6,
+ 0xabed, 0xabed,
+ 0x10a3f, 0x10a3f,
+ 0x11046, 0x11046,
+ 0x1107f, 0x1107f,
+ 0x110b9, 0x110b9,
+ 0x11133, 0x11134,
+ 0x111c0, 0x111c0,
+ 0x11235, 0x11235,
+ 0x112ea, 0x112ea,
+ 0x1134d, 0x1134d,
+ 0x11442, 0x11442,
+ 0x114c2, 0x114c2,
+ 0x115bf, 0x115bf,
+ 0x1163f, 0x1163f,
+ 0x116b6, 0x116b6,
+ 0x1172b, 0x1172b,
+ 0x11839, 0x11839,
+ 0x119e0, 0x119e0,
+ 0x11a34, 0x11a34,
+ 0x11a47, 0x11a47,
+ 0x11a99, 0x11a99,
+ 0x11c3f, 0x11c3f,
+ 0x11d44, 0x11d45,
+ 0x11d97, 0x11d97,
+}; /* CR_Grapheme_Link */
+
+/* 'Common': Script */
+static const OnigCodePoint CR_Common[] = {
+ 172,
+ 0x0000, 0x0040,
+ 0x005b, 0x0060,
+ 0x007b, 0x00a9,
+ 0x00ab, 0x00b9,
+ 0x00bb, 0x00bf,
+ 0x00d7, 0x00d7,
+ 0x00f7, 0x00f7,
+ 0x02b9, 0x02df,
+ 0x02e5, 0x02e9,
+ 0x02ec, 0x02ff,
+ 0x0374, 0x0374,
+ 0x037e, 0x037e,
+ 0x0385, 0x0385,
+ 0x0387, 0x0387,
+ 0x0589, 0x0589,
+ 0x0605, 0x0605,
+ 0x060c, 0x060c,
+ 0x061b, 0x061b,
+ 0x061f, 0x061f,
+ 0x0640, 0x0640,
+ 0x06dd, 0x06dd,
+ 0x08e2, 0x08e2,
+ 0x0964, 0x0965,
+ 0x0e3f, 0x0e3f,
+ 0x0fd5, 0x0fd8,
+ 0x10fb, 0x10fb,
+ 0x16eb, 0x16ed,
+ 0x1735, 0x1736,
+ 0x1802, 0x1803,
+ 0x1805, 0x1805,
+ 0x1cd3, 0x1cd3,
+ 0x1ce1, 0x1ce1,
+ 0x1ce9, 0x1cec,
+ 0x1cee, 0x1cf3,
+ 0x1cf5, 0x1cf7,
+ 0x1cfa, 0x1cfa,
+ 0x2000, 0x200b,
+ 0x200e, 0x2064,
+ 0x2066, 0x2070,
+ 0x2074, 0x207e,
+ 0x2080, 0x208e,
+ 0x20a0, 0x20bf,
+ 0x2100, 0x2125,
+ 0x2127, 0x2129,
+ 0x212c, 0x2131,
+ 0x2133, 0x214d,
+ 0x214f, 0x215f,
+ 0x2189, 0x218b,
+ 0x2190, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x27ff,
+ 0x2900, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b98, 0x2bff,
+ 0x2e00, 0x2e4f,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x3004,
+ 0x3006, 0x3006,
+ 0x3008, 0x3020,
+ 0x3030, 0x3037,
+ 0x303c, 0x303f,
+ 0x309b, 0x309c,
+ 0x30a0, 0x30a0,
+ 0x30fb, 0x30fc,
+ 0x3190, 0x319f,
+ 0x31c0, 0x31e3,
+ 0x3220, 0x325f,
+ 0x327f, 0x32cf,
+ 0x32ff, 0x32ff,
+ 0x3358, 0x33ff,
+ 0x4dc0, 0x4dff,
+ 0xa700, 0xa721,
+ 0xa788, 0xa78a,
+ 0xa830, 0xa839,
+ 0xa92e, 0xa92e,
+ 0xa9cf, 0xa9cf,
+ 0xab5b, 0xab5b,
+ 0xfd3e, 0xfd3f,
+ 0xfe10, 0xfe19,
+ 0xfe30, 0xfe52,
+ 0xfe54, 0xfe66,
+ 0xfe68, 0xfe6b,
+ 0xfeff, 0xfeff,
+ 0xff01, 0xff20,
+ 0xff3b, 0xff40,
+ 0xff5b, 0xff65,
+ 0xff70, 0xff70,
+ 0xff9e, 0xff9f,
+ 0xffe0, 0xffe6,
+ 0xffe8, 0xffee,
+ 0xfff9, 0xfffd,
+ 0x10100, 0x10102,
+ 0x10107, 0x10133,
+ 0x10137, 0x1013f,
+ 0x10190, 0x1019b,
+ 0x101d0, 0x101fc,
+ 0x102e1, 0x102fb,
+ 0x16fe2, 0x16fe3,
+ 0x1bca0, 0x1bca3,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d166,
+ 0x1d16a, 0x1d17a,
+ 0x1d183, 0x1d184,
+ 0x1d18c, 0x1d1a9,
+ 0x1d1ae, 0x1d1e8,
+ 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, 0x1d7ff,
+ 0x1ec71, 0x1ecb4,
+ 0x1ed01, 0x1ed3d,
+ 0x1f000, 0x1f02b,
+ 0x1f030, 0x1f093,
+ 0x1f0a0, 0x1f0ae,
+ 0x1f0b1, 0x1f0bf,
+ 0x1f0c1, 0x1f0cf,
+ 0x1f0d1, 0x1f0f5,
+ 0x1f100, 0x1f10c,
+ 0x1f110, 0x1f16c,
+ 0x1f170, 0x1f1ac,
+ 0x1f1e6, 0x1f1ff,
+ 0x1f201, 0x1f202,
+ 0x1f210, 0x1f23b,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f260, 0x1f265,
+ 0x1f300, 0x1f6d5,
+ 0x1f6e0, 0x1f6ec,
+ 0x1f6f0, 0x1f6fa,
+ 0x1f700, 0x1f773,
+ 0x1f780, 0x1f7d8,
+ 0x1f7e0, 0x1f7eb,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1f900, 0x1f90b,
+ 0x1f90d, 0x1f971,
+ 0x1f973, 0x1f976,
+ 0x1f97a, 0x1f9a2,
+ 0x1f9a5, 0x1f9aa,
+ 0x1f9ae, 0x1f9ca,
+ 0x1f9cd, 0x1fa53,
+ 0x1fa60, 0x1fa6d,
+ 0x1fa70, 0x1fa73,
+ 0x1fa78, 0x1fa7a,
+ 0x1fa80, 0x1fa82,
+ 0x1fa90, 0x1fa95,
+ 0xe0001, 0xe0001,
+ 0xe0020, 0xe007f,
+}; /* CR_Common */
+
+/* 'Latin': Script */
+static const OnigCodePoint CR_Latin[] = {
+ 32,
+ 0x0041, 0x005a,
+ 0x0061, 0x007a,
+ 0x00aa, 0x00aa,
+ 0x00ba, 0x00ba,
+ 0x00c0, 0x00d6,
+ 0x00d8, 0x00f6,
+ 0x00f8, 0x02b8,
+ 0x02e0, 0x02e4,
+ 0x1d00, 0x1d25,
+ 0x1d2c, 0x1d5c,
+ 0x1d62, 0x1d65,
+ 0x1d6b, 0x1d77,
+ 0x1d79, 0x1dbe,
+ 0x1e00, 0x1eff,
+ 0x2071, 0x2071,
+ 0x207f, 0x207f,
+ 0x2090, 0x209c,
+ 0x212a, 0x212b,
+ 0x2132, 0x2132,
+ 0x214e, 0x214e,
+ 0x2160, 0x2188,
+ 0x2c60, 0x2c7f,
+ 0xa722, 0xa787,
+ 0xa78b, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f7, 0xa7ff,
+ 0xab30, 0xab5a,
+ 0xab5c, 0xab64,
+ 0xab66, 0xab67,
+ 0xfb00, 0xfb06,
+ 0xff21, 0xff3a,
+ 0xff41, 0xff5a,
+}; /* CR_Latin */
+
+/* 'Greek': Script */
+static const OnigCodePoint CR_Greek[] = {
+ 36,
+ 0x0370, 0x0373,
+ 0x0375, 0x0377,
+ 0x037a, 0x037d,
+ 0x037f, 0x037f,
+ 0x0384, 0x0384,
+ 0x0386, 0x0386,
+ 0x0388, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03e1,
+ 0x03f0, 0x03ff,
+ 0x1d26, 0x1d2a,
+ 0x1d5d, 0x1d61,
+ 0x1d66, 0x1d6a,
+ 0x1dbf, 0x1dbf,
+ 0x1f00, 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,
+ 0x2126, 0x2126,
+ 0xab65, 0xab65,
+ 0x10140, 0x1018e,
+ 0x101a0, 0x101a0,
+ 0x1d200, 0x1d245,
+}; /* CR_Greek */
+
+/* 'Cyrillic': Script */
+static const OnigCodePoint CR_Cyrillic[] = {
+ 8,
+ 0x0400, 0x0484,
+ 0x0487, 0x052f,
+ 0x1c80, 0x1c88,
+ 0x1d2b, 0x1d2b,
+ 0x1d78, 0x1d78,
+ 0x2de0, 0x2dff,
+ 0xa640, 0xa69f,
+ 0xfe2e, 0xfe2f,
+}; /* CR_Cyrillic */
+
+/* 'Armenian': Script */
+static const OnigCodePoint CR_Armenian[] = {
+ 5,
+ 0x0531, 0x0556,
+ 0x0559, 0x0588,
+ 0x058a, 0x058a,
+ 0x058d, 0x058f,
+ 0xfb13, 0xfb17,
+}; /* CR_Armenian */
+
+/* 'Hebrew': Script */
+static const OnigCodePoint CR_Hebrew[] = {
+ 9,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f4,
+ 0xfb1d, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfb4f,
+}; /* CR_Hebrew */
+
+/* 'Arabic': Script */
+static const OnigCodePoint CR_Arabic[] = {
+ 57,
+ 0x0600, 0x0604,
+ 0x0606, 0x060b,
+ 0x060d, 0x061a,
+ 0x061c, 0x061c,
+ 0x061e, 0x061e,
+ 0x0620, 0x063f,
+ 0x0641, 0x064a,
+ 0x0656, 0x066f,
+ 0x0671, 0x06dc,
+ 0x06de, 0x06ff,
+ 0x0750, 0x077f,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x08d3, 0x08e1,
+ 0x08e3, 0x08ff,
+ 0xfb50, 0xfbc1,
+ 0xfbd3, 0xfd3d,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfd,
+ 0xfe70, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0x10e60, 0x10e7e,
+ 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,
+}; /* CR_Arabic */
+
+/* 'Syriac': Script */
+static const OnigCodePoint CR_Syriac[] = {
+ 4,
+ 0x0700, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x074f,
+ 0x0860, 0x086a,
+}; /* CR_Syriac */
+
+/* 'Thaana': Script */
+static const OnigCodePoint CR_Thaana[] = {
+ 1,
+ 0x0780, 0x07b1,
+}; /* CR_Thaana */
+
+/* 'Devanagari': Script */
+static const OnigCodePoint CR_Devanagari[] = {
+ 4,
+ 0x0900, 0x0950,
+ 0x0955, 0x0963,
+ 0x0966, 0x097f,
+ 0xa8e0, 0xa8ff,
+}; /* CR_Devanagari */
+
+/* 'Bengali': Script */
+static const OnigCodePoint CR_Bengali[] = {
+ 14,
+ 0x0980, 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,
+}; /* CR_Bengali */
+
+/* 'Gurmukhi': Script */
+static const OnigCodePoint CR_Gurmukhi[] = {
+ 16,
+ 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,
+}; /* CR_Gurmukhi */
+
+/* 'Gujarati': Script */
+static const OnigCodePoint CR_Gujarati[] = {
+ 14,
+ 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,
+}; /* CR_Gujarati */
+
+/* 'Oriya': Script */
+static const OnigCodePoint CR_Oriya[] = {
+ 14,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b63,
+ 0x0b66, 0x0b77,
+}; /* CR_Oriya */
+
+/* 'Tamil': Script */
+static const OnigCodePoint CR_Tamil[] = {
+ 18,
+ 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,
+ 0x11fc0, 0x11ff1,
+ 0x11fff, 0x11fff,
+}; /* CR_Tamil */
+
+/* 'Telugu': Script */
+static const OnigCodePoint CR_Telugu[] = {
+ 12,
+ 0x0c00, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c77, 0x0c7f,
+}; /* CR_Telugu */
+
+/* 'Kannada': Script */
+static const OnigCodePoint CR_Kannada[] = {
+ 13,
+ 0x0c80, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+}; /* CR_Kannada */
+
+/* 'Malayalam': Script */
+static const OnigCodePoint CR_Malayalam[] = {
+ 8,
+ 0x0d00, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4f,
+ 0x0d54, 0x0d63,
+ 0x0d66, 0x0d7f,
+}; /* CR_Malayalam */
+
+/* 'Sinhala': Script */
+static const OnigCodePoint CR_Sinhala[] = {
+ 13,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0de6, 0x0def,
+ 0x0df2, 0x0df4,
+ 0x111e1, 0x111f4,
+}; /* CR_Sinhala */
+
+/* 'Thai': Script */
+static const OnigCodePoint CR_Thai[] = {
+ 2,
+ 0x0e01, 0x0e3a,
+ 0x0e40, 0x0e5b,
+}; /* CR_Thai */
+
+/* 'Lao': Script */
+static const OnigCodePoint CR_Lao[] = {
+ 11,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e86, 0x0e8a,
+ 0x0e8c, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edf,
+}; /* CR_Lao */
+
+/* 'Tibetan': Script */
+static const OnigCodePoint CR_Tibetan[] = {
+ 7,
+ 0x0f00, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f71, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fbe, 0x0fcc,
+ 0x0fce, 0x0fd4,
+ 0x0fd9, 0x0fda,
+}; /* CR_Tibetan */
+
+/* 'Myanmar': Script */
+static const OnigCodePoint CR_Myanmar[] = {
+ 3,
+ 0x1000, 0x109f,
+ 0xa9e0, 0xa9fe,
+ 0xaa60, 0xaa7f,
+}; /* CR_Myanmar */
+
+/* 'Georgian': Script */
+static const OnigCodePoint CR_Georgian[] = {
+ 10,
+ 0x10a0, 0x10c5,
+ 0x10c7, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x10fa,
+ 0x10fc, 0x10ff,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cbf,
+ 0x2d00, 0x2d25,
+ 0x2d27, 0x2d27,
+ 0x2d2d, 0x2d2d,
+}; /* CR_Georgian */
+
+/* 'Hangul': Script */
+static const OnigCodePoint CR_Hangul[] = {
+ 14,
+ 0x1100, 0x11ff,
+ 0x302e, 0x302f,
+ 0x3131, 0x318e,
+ 0x3200, 0x321e,
+ 0x3260, 0x327e,
+ 0xa960, 0xa97c,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xffa0, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+}; /* CR_Hangul */
+
+/* 'Ethiopic': Script */
+static const OnigCodePoint CR_Ethiopic[] = {
+ 32,
+ 0x1200, 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,
+ 0x2d80, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+}; /* CR_Ethiopic */
+
+/* 'Cherokee': Script */
+static const OnigCodePoint CR_Cherokee[] = {
+ 3,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0xab70, 0xabbf,
+}; /* CR_Cherokee */
+
+/* 'Canadian_Aboriginal': Script */
+static const OnigCodePoint CR_Canadian_Aboriginal[] = {
+ 2,
+ 0x1400, 0x167f,
+ 0x18b0, 0x18f5,
+}; /* CR_Canadian_Aboriginal */
+
+/* 'Ogham': Script */
+static const OnigCodePoint CR_Ogham[] = {
+ 1,
+ 0x1680, 0x169c,
+}; /* CR_Ogham */
+
+/* 'Runic': Script */
+static const OnigCodePoint CR_Runic[] = {
+ 2,
+ 0x16a0, 0x16ea,
+ 0x16ee, 0x16f8,
+}; /* CR_Runic */
+
+/* 'Khmer': Script */
+static const OnigCodePoint CR_Khmer[] = {
+ 4,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x19e0, 0x19ff,
+}; /* CR_Khmer */
+
+/* 'Mongolian': Script */
+static const OnigCodePoint CR_Mongolian[] = {
+ 7,
+ 0x1800, 0x1801,
+ 0x1804, 0x1804,
+ 0x1806, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1878,
+ 0x1880, 0x18aa,
+ 0x11660, 0x1166c,
+}; /* CR_Mongolian */
+
+/* 'Hiragana': Script */
+static const OnigCodePoint CR_Hiragana[] = {
+ 5,
+ 0x3041, 0x3096,
+ 0x309d, 0x309f,
+ 0x1b001, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1f200, 0x1f200,
+}; /* CR_Hiragana */
+
+/* 'Katakana': Script */
+static const OnigCodePoint CR_Katakana[] = {
+ 9,
+ 0x30a1, 0x30fa,
+ 0x30fd, 0x30ff,
+ 0x31f0, 0x31ff,
+ 0x32d0, 0x32fe,
+ 0x3300, 0x3357,
+ 0xff66, 0xff6f,
+ 0xff71, 0xff9d,
+ 0x1b000, 0x1b000,
+ 0x1b164, 0x1b167,
+}; /* CR_Katakana */
+
+/* 'Bopomofo': Script */
+static const OnigCodePoint CR_Bopomofo[] = {
+ 3,
+ 0x02ea, 0x02eb,
+ 0x3105, 0x312f,
+ 0x31a0, 0x31ba,
+}; /* CR_Bopomofo */
+
+/* 'Han': Script */
+static const OnigCodePoint CR_Han[] = {
+ 17,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x3005, 0x3005,
+ 0x3007, 0x3007,
+ 0x3021, 0x3029,
+ 0x3038, 0x303b,
+ 0x3400, 0x4db5,
+ 0x4e00, 0x9fef,
+ 0xf900, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+}; /* CR_Han */
+
+/* 'Yi': Script */
+static const OnigCodePoint CR_Yi[] = {
+ 2,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+}; /* CR_Yi */
+
+/* 'Old_Italic': Script */
+static const OnigCodePoint CR_Old_Italic[] = {
+ 2,
+ 0x10300, 0x10323,
+ 0x1032d, 0x1032f,
+}; /* CR_Old_Italic */
+
+/* 'Gothic': Script */
+static const OnigCodePoint CR_Gothic[] = {
+ 1,
+ 0x10330, 0x1034a,
+}; /* CR_Gothic */
+
+/* 'Deseret': Script */
+static const OnigCodePoint CR_Deseret[] = {
+ 1,
+ 0x10400, 0x1044f,
+}; /* CR_Deseret */
+
+/* 'Inherited': Script */
+static const OnigCodePoint CR_Inherited[] = {
+ 28,
+ 0x0300, 0x036f,
+ 0x0485, 0x0486,
+ 0x064b, 0x0655,
+ 0x0670, 0x0670,
+ 0x0951, 0x0954,
+ 0x1ab0, 0x1abe,
+ 0x1cd0, 0x1cd2,
+ 0x1cd4, 0x1ce0,
+ 0x1ce2, 0x1ce8,
+ 0x1ced, 0x1ced,
+ 0x1cf4, 0x1cf4,
+ 0x1cf8, 0x1cf9,
+ 0x1dc0, 0x1df9,
+ 0x1dfb, 0x1dff,
+ 0x200c, 0x200d,
+ 0x20d0, 0x20f0,
+ 0x302a, 0x302d,
+ 0x3099, 0x309a,
+ 0xfe00, 0xfe0f,
+ 0xfe20, 0xfe2d,
+ 0x101fd, 0x101fd,
+ 0x102e0, 0x102e0,
+ 0x1133b, 0x1133b,
+ 0x1d167, 0x1d169,
+ 0x1d17b, 0x1d182,
+ 0x1d185, 0x1d18b,
+ 0x1d1aa, 0x1d1ad,
+ 0xe0100, 0xe01ef,
+}; /* CR_Inherited */
+
+/* 'Tagalog': Script */
+static const OnigCodePoint CR_Tagalog[] = {
+ 2,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+}; /* CR_Tagalog */
+
+/* 'Hanunoo': Script */
+static const OnigCodePoint CR_Hanunoo[] = {
+ 1,
+ 0x1720, 0x1734,
+}; /* CR_Hanunoo */
+
+/* 'Buhid': Script */
+static const OnigCodePoint CR_Buhid[] = {
+ 1,
+ 0x1740, 0x1753,
+}; /* CR_Buhid */
+
+/* 'Tagbanwa': Script */
+static const OnigCodePoint CR_Tagbanwa[] = {
+ 3,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+}; /* CR_Tagbanwa */
+
+/* 'Limbu': Script */
+static const OnigCodePoint CR_Limbu[] = {
+ 5,
+ 0x1900, 0x191e,
+ 0x1920, 0x192b,
+ 0x1930, 0x193b,
+ 0x1940, 0x1940,
+ 0x1944, 0x194f,
+}; /* CR_Limbu */
+
+/* 'Tai_Le': Script */
+static const OnigCodePoint CR_Tai_Le[] = {
+ 2,
+ 0x1950, 0x196d,
+ 0x1970, 0x1974,
+}; /* CR_Tai_Le */
+
+/* 'Linear_B': Script */
+static const OnigCodePoint CR_Linear_B[] = {
+ 7,
+ 0x10000, 0x1000b,
+ 0x1000d, 0x10026,
+ 0x10028, 0x1003a,
+ 0x1003c, 0x1003d,
+ 0x1003f, 0x1004d,
+ 0x10050, 0x1005d,
+ 0x10080, 0x100fa,
+}; /* CR_Linear_B */
+
+/* 'Ugaritic': Script */
+static const OnigCodePoint CR_Ugaritic[] = {
+ 2,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x1039f,
+}; /* CR_Ugaritic */
+
+/* 'Shavian': Script */
+static const OnigCodePoint CR_Shavian[] = {
+ 1,
+ 0x10450, 0x1047f,
+}; /* CR_Shavian */
+
+/* 'Osmanya': Script */
+static const OnigCodePoint CR_Osmanya[] = {
+ 2,
+ 0x10480, 0x1049d,
+ 0x104a0, 0x104a9,
+}; /* CR_Osmanya */
+
+/* 'Cypriot': Script */
+static const OnigCodePoint CR_Cypriot[] = {
+ 6,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x1083f,
+}; /* CR_Cypriot */
+
+/* 'Braille': Script */
+static const OnigCodePoint CR_Braille[] = {
+ 1,
+ 0x2800, 0x28ff,
+}; /* CR_Braille */
+
+/* 'Buginese': Script */
+static const OnigCodePoint CR_Buginese[] = {
+ 2,
+ 0x1a00, 0x1a1b,
+ 0x1a1e, 0x1a1f,
+}; /* CR_Buginese */
+
+/* 'Coptic': Script */
+static const OnigCodePoint CR_Coptic[] = {
+ 3,
+ 0x03e2, 0x03ef,
+ 0x2c80, 0x2cf3,
+ 0x2cf9, 0x2cff,
+}; /* CR_Coptic */
+
+/* 'New_Tai_Lue': Script */
+static const OnigCodePoint CR_New_Tai_Lue[] = {
+ 4,
+ 0x1980, 0x19ab,
+ 0x19b0, 0x19c9,
+ 0x19d0, 0x19da,
+ 0x19de, 0x19df,
+}; /* CR_New_Tai_Lue */
+
+/* 'Glagolitic': Script */
+static const OnigCodePoint CR_Glagolitic[] = {
+ 7,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+}; /* CR_Glagolitic */
+
+/* 'Tifinagh': Script */
+static const OnigCodePoint CR_Tifinagh[] = {
+ 3,
+ 0x2d30, 0x2d67,
+ 0x2d6f, 0x2d70,
+ 0x2d7f, 0x2d7f,
+}; /* CR_Tifinagh */
+
+/* 'Syloti_Nagri': Script */
+static const OnigCodePoint CR_Syloti_Nagri[] = {
+ 1,
+ 0xa800, 0xa82b,
+}; /* CR_Syloti_Nagri */
+
+/* 'Old_Persian': Script */
+static const OnigCodePoint CR_Old_Persian[] = {
+ 2,
+ 0x103a0, 0x103c3,
+ 0x103c8, 0x103d5,
+}; /* CR_Old_Persian */
+
+/* 'Kharoshthi': Script */
+static const OnigCodePoint CR_Kharoshthi[] = {
+ 8,
+ 0x10a00, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a35,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a48,
+ 0x10a50, 0x10a58,
+}; /* CR_Kharoshthi */
+
+/* 'Balinese': Script */
+static const OnigCodePoint CR_Balinese[] = {
+ 2,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+}; /* CR_Balinese */
+
+/* 'Cuneiform': Script */
+static const OnigCodePoint CR_Cuneiform[] = {
+ 4,
+ 0x12000, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12470, 0x12474,
+ 0x12480, 0x12543,
+}; /* CR_Cuneiform */
+
+/* 'Phoenician': Script */
+static const OnigCodePoint CR_Phoenician[] = {
+ 2,
+ 0x10900, 0x1091b,
+ 0x1091f, 0x1091f,
+}; /* CR_Phoenician */
+
+/* 'Phags_Pa': Script */
+static const OnigCodePoint CR_Phags_Pa[] = {
+ 1,
+ 0xa840, 0xa877,
+}; /* CR_Phags_Pa */
+
+/* 'Nko': Script */
+static const OnigCodePoint CR_Nko[] = {
+ 2,
+ 0x07c0, 0x07fa,
+ 0x07fd, 0x07ff,
+}; /* CR_Nko */
+
+/* 'Sundanese': Script */
+static const OnigCodePoint CR_Sundanese[] = {
+ 2,
+ 0x1b80, 0x1bbf,
+ 0x1cc0, 0x1cc7,
+}; /* CR_Sundanese */
+
+/* 'Lepcha': Script */
+static const OnigCodePoint CR_Lepcha[] = {
+ 3,
+ 0x1c00, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c4f,
+}; /* CR_Lepcha */
+
+/* 'Ol_Chiki': Script */
+static const OnigCodePoint CR_Ol_Chiki[] = {
+ 1,
+ 0x1c50, 0x1c7f,
+}; /* CR_Ol_Chiki */
+
+/* 'Vai': Script */
+static const OnigCodePoint CR_Vai[] = {
+ 1,
+ 0xa500, 0xa62b,
+}; /* CR_Vai */
+
+/* 'Saurashtra': Script */
+static const OnigCodePoint CR_Saurashtra[] = {
+ 2,
+ 0xa880, 0xa8c5,
+ 0xa8ce, 0xa8d9,
+}; /* CR_Saurashtra */
+
+/* 'Kayah_Li': Script */
+static const OnigCodePoint CR_Kayah_Li[] = {
+ 2,
+ 0xa900, 0xa92d,
+ 0xa92f, 0xa92f,
+}; /* CR_Kayah_Li */
+
+/* 'Rejang': Script */
+static const OnigCodePoint CR_Rejang[] = {
+ 2,
+ 0xa930, 0xa953,
+ 0xa95f, 0xa95f,
+}; /* CR_Rejang */
+
+/* 'Lycian': Script */
+static const OnigCodePoint CR_Lycian[] = {
+ 1,
+ 0x10280, 0x1029c,
+}; /* CR_Lycian */
+
+/* 'Carian': Script */
+static const OnigCodePoint CR_Carian[] = {
+ 1,
+ 0x102a0, 0x102d0,
+}; /* CR_Carian */
+
+/* 'Lydian': Script */
+static const OnigCodePoint CR_Lydian[] = {
+ 2,
+ 0x10920, 0x10939,
+ 0x1093f, 0x1093f,
+}; /* CR_Lydian */
+
+/* 'Cham': Script */
+static const OnigCodePoint CR_Cham[] = {
+ 4,
+ 0xaa00, 0xaa36,
+ 0xaa40, 0xaa4d,
+ 0xaa50, 0xaa59,
+ 0xaa5c, 0xaa5f,
+}; /* CR_Cham */
+
+/* 'Tai_Tham': Script */
+static const OnigCodePoint CR_Tai_Tham[] = {
+ 5,
+ 0x1a20, 0x1a5e,
+ 0x1a60, 0x1a7c,
+ 0x1a7f, 0x1a89,
+ 0x1a90, 0x1a99,
+ 0x1aa0, 0x1aad,
+}; /* CR_Tai_Tham */
+
+/* 'Tai_Viet': Script */
+static const OnigCodePoint CR_Tai_Viet[] = {
+ 2,
+ 0xaa80, 0xaac2,
+ 0xaadb, 0xaadf,
+}; /* CR_Tai_Viet */
+
+/* 'Avestan': Script */
+static const OnigCodePoint CR_Avestan[] = {
+ 2,
+ 0x10b00, 0x10b35,
+ 0x10b39, 0x10b3f,
+}; /* CR_Avestan */
+
+/* 'Egyptian_Hieroglyphs': Script */
+static const OnigCodePoint CR_Egyptian_Hieroglyphs[] = {
+ 2,
+ 0x13000, 0x1342e,
+ 0x13430, 0x13438,
+}; /* CR_Egyptian_Hieroglyphs */
+
+/* 'Samaritan': Script */
+static const OnigCodePoint CR_Samaritan[] = {
+ 2,
+ 0x0800, 0x082d,
+ 0x0830, 0x083e,
+}; /* CR_Samaritan */
+
+/* 'Lisu': Script */
+static const OnigCodePoint CR_Lisu[] = {
+ 1,
+ 0xa4d0, 0xa4ff,
+}; /* CR_Lisu */
+
+/* 'Bamum': Script */
+static const OnigCodePoint CR_Bamum[] = {
+ 2,
+ 0xa6a0, 0xa6f7,
+ 0x16800, 0x16a38,
+}; /* CR_Bamum */
+
+/* 'Javanese': Script */
+static const OnigCodePoint CR_Javanese[] = {
+ 3,
+ 0xa980, 0xa9cd,
+ 0xa9d0, 0xa9d9,
+ 0xa9de, 0xa9df,
+}; /* CR_Javanese */
+
+/* 'Meetei_Mayek': Script */
+static const OnigCodePoint CR_Meetei_Mayek[] = {
+ 3,
+ 0xaae0, 0xaaf6,
+ 0xabc0, 0xabed,
+ 0xabf0, 0xabf9,
+}; /* CR_Meetei_Mayek */
+
+/* 'Imperial_Aramaic': Script */
+static const OnigCodePoint CR_Imperial_Aramaic[] = {
+ 2,
+ 0x10840, 0x10855,
+ 0x10857, 0x1085f,
+}; /* CR_Imperial_Aramaic */
+
+/* 'Old_South_Arabian': Script */
+static const OnigCodePoint CR_Old_South_Arabian[] = {
+ 1,
+ 0x10a60, 0x10a7f,
+}; /* CR_Old_South_Arabian */
+
+/* 'Inscriptional_Parthian': Script */
+static const OnigCodePoint CR_Inscriptional_Parthian[] = {
+ 2,
+ 0x10b40, 0x10b55,
+ 0x10b58, 0x10b5f,
+}; /* CR_Inscriptional_Parthian */
+
+/* 'Inscriptional_Pahlavi': Script */
+static const OnigCodePoint CR_Inscriptional_Pahlavi[] = {
+ 2,
+ 0x10b60, 0x10b72,
+ 0x10b78, 0x10b7f,
+}; /* CR_Inscriptional_Pahlavi */
+
+/* 'Old_Turkic': Script */
+static const OnigCodePoint CR_Old_Turkic[] = {
+ 1,
+ 0x10c00, 0x10c48,
+}; /* CR_Old_Turkic */
+
+/* 'Kaithi': Script */
+static const OnigCodePoint CR_Kaithi[] = {
+ 2,
+ 0x11080, 0x110c1,
+ 0x110cd, 0x110cd,
+}; /* CR_Kaithi */
+
+/* 'Batak': Script */
+static const OnigCodePoint CR_Batak[] = {
+ 2,
+ 0x1bc0, 0x1bf3,
+ 0x1bfc, 0x1bff,
+}; /* CR_Batak */
+
+/* 'Brahmi': Script */
+static const OnigCodePoint CR_Brahmi[] = {
+ 3,
+ 0x11000, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x1107f, 0x1107f,
+}; /* CR_Brahmi */
+
+/* 'Mandaic': Script */
+static const OnigCodePoint CR_Mandaic[] = {
+ 2,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+}; /* CR_Mandaic */
+
+/* 'Chakma': Script */
+static const OnigCodePoint CR_Chakma[] = {
+ 2,
+ 0x11100, 0x11134,
+ 0x11136, 0x11146,
+}; /* CR_Chakma */
+
+/* 'Meroitic_Cursive': Script */
+static const OnigCodePoint CR_Meroitic_Cursive[] = {
+ 3,
+ 0x109a0, 0x109b7,
+ 0x109bc, 0x109cf,
+ 0x109d2, 0x109ff,
+}; /* CR_Meroitic_Cursive */
+
+/* 'Meroitic_Hieroglyphs': Script */
+static const OnigCodePoint CR_Meroitic_Hieroglyphs[] = {
+ 1,
+ 0x10980, 0x1099f,
+}; /* CR_Meroitic_Hieroglyphs */
+
+/* 'Miao': Script */
+static const OnigCodePoint CR_Miao[] = {
+ 3,
+ 0x16f00, 0x16f4a,
+ 0x16f4f, 0x16f87,
+ 0x16f8f, 0x16f9f,
+}; /* CR_Miao */
+
+/* 'Sharada': Script */
+static const OnigCodePoint CR_Sharada[] = {
+ 2,
+ 0x11180, 0x111cd,
+ 0x111d0, 0x111df,
+}; /* CR_Sharada */
+
+/* 'Sora_Sompeng': Script */
+static const OnigCodePoint CR_Sora_Sompeng[] = {
+ 2,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+}; /* CR_Sora_Sompeng */
+
+/* 'Takri': Script */
+static const OnigCodePoint CR_Takri[] = {
+ 2,
+ 0x11680, 0x116b8,
+ 0x116c0, 0x116c9,
+}; /* CR_Takri */
+
+/* 'Caucasian_Albanian': Script */
+static const OnigCodePoint CR_Caucasian_Albanian[] = {
+ 2,
+ 0x10530, 0x10563,
+ 0x1056f, 0x1056f,
+}; /* CR_Caucasian_Albanian */
+
+/* 'Bassa_Vah': Script */
+static const OnigCodePoint CR_Bassa_Vah[] = {
+ 2,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af5,
+}; /* CR_Bassa_Vah */
+
+/* 'Duployan': Script */
+static const OnigCodePoint CR_Duployan[] = {
+ 5,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9c, 0x1bc9f,
+}; /* CR_Duployan */
+
+/* 'Elbasan': Script */
+static const OnigCodePoint CR_Elbasan[] = {
+ 1,
+ 0x10500, 0x10527,
+}; /* CR_Elbasan */
+
+/* 'Grantha': Script */
+static const OnigCodePoint CR_Grantha[] = {
+ 15,
+ 0x11300, 0x11303,
+ 0x11305, 0x1130c,
+ 0x1130f, 0x11310,
+ 0x11313, 0x11328,
+ 0x1132a, 0x11330,
+ 0x11332, 0x11333,
+ 0x11335, 0x11339,
+ 0x1133c, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134b, 0x1134d,
+ 0x11350, 0x11350,
+ 0x11357, 0x11357,
+ 0x1135d, 0x11363,
+ 0x11366, 0x1136c,
+ 0x11370, 0x11374,
+}; /* CR_Grantha */
+
+/* 'Pahawh_Hmong': Script */
+static const OnigCodePoint CR_Pahawh_Hmong[] = {
+ 5,
+ 0x16b00, 0x16b45,
+ 0x16b50, 0x16b59,
+ 0x16b5b, 0x16b61,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+}; /* CR_Pahawh_Hmong */
+
+/* 'Khojki': Script */
+static const OnigCodePoint CR_Khojki[] = {
+ 2,
+ 0x11200, 0x11211,
+ 0x11213, 0x1123e,
+}; /* CR_Khojki */
+
+/* 'Linear_A': Script */
+static const OnigCodePoint CR_Linear_A[] = {
+ 3,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+}; /* CR_Linear_A */
+
+/* 'Mahajani': Script */
+static const OnigCodePoint CR_Mahajani[] = {
+ 1,
+ 0x11150, 0x11176,
+}; /* CR_Mahajani */
+
+/* 'Manichaean': Script */
+static const OnigCodePoint CR_Manichaean[] = {
+ 2,
+ 0x10ac0, 0x10ae6,
+ 0x10aeb, 0x10af6,
+}; /* CR_Manichaean */
+
+/* 'Mende_Kikakui': Script */
+static const OnigCodePoint CR_Mende_Kikakui[] = {
+ 2,
+ 0x1e800, 0x1e8c4,
+ 0x1e8c7, 0x1e8d6,
+}; /* CR_Mende_Kikakui */
+
+/* 'Modi': Script */
+static const OnigCodePoint CR_Modi[] = {
+ 2,
+ 0x11600, 0x11644,
+ 0x11650, 0x11659,
+}; /* CR_Modi */
+
+/* 'Mro': Script */
+static const OnigCodePoint CR_Mro[] = {
+ 3,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16a6e, 0x16a6f,
+}; /* CR_Mro */
+
+/* 'Old_North_Arabian': Script */
+static const OnigCodePoint CR_Old_North_Arabian[] = {
+ 1,
+ 0x10a80, 0x10a9f,
+}; /* CR_Old_North_Arabian */
+
+/* 'Nabataean': Script */
+static const OnigCodePoint CR_Nabataean[] = {
+ 2,
+ 0x10880, 0x1089e,
+ 0x108a7, 0x108af,
+}; /* CR_Nabataean */
+
+/* 'Palmyrene': Script */
+static const OnigCodePoint CR_Palmyrene[] = {
+ 1,
+ 0x10860, 0x1087f,
+}; /* CR_Palmyrene */
+
+/* 'Pau_Cin_Hau': Script */
+static const OnigCodePoint CR_Pau_Cin_Hau[] = {
+ 1,
+ 0x11ac0, 0x11af8,
+}; /* CR_Pau_Cin_Hau */
+
+/* 'Old_Permic': Script */
+static const OnigCodePoint CR_Old_Permic[] = {
+ 1,
+ 0x10350, 0x1037a,
+}; /* CR_Old_Permic */
+
+/* 'Psalter_Pahlavi': Script */
+static const OnigCodePoint CR_Psalter_Pahlavi[] = {
+ 3,
+ 0x10b80, 0x10b91,
+ 0x10b99, 0x10b9c,
+ 0x10ba9, 0x10baf,
+}; /* CR_Psalter_Pahlavi */
+
+/* 'Siddham': Script */
+static const OnigCodePoint CR_Siddham[] = {
+ 2,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115dd,
+}; /* CR_Siddham */
+
+/* 'Khudawadi': Script */
+static const OnigCodePoint CR_Khudawadi[] = {
+ 2,
+ 0x112b0, 0x112ea,
+ 0x112f0, 0x112f9,
+}; /* CR_Khudawadi */
+
+/* 'Tirhuta': Script */
+static const OnigCodePoint CR_Tirhuta[] = {
+ 2,
+ 0x11480, 0x114c7,
+ 0x114d0, 0x114d9,
+}; /* CR_Tirhuta */
+
+/* 'Warang_Citi': Script */
+static const OnigCodePoint CR_Warang_Citi[] = {
+ 2,
+ 0x118a0, 0x118f2,
+ 0x118ff, 0x118ff,
+}; /* CR_Warang_Citi */
+
+/* 'Ahom': Script */
+static const OnigCodePoint CR_Ahom[] = {
+ 3,
+ 0x11700, 0x1171a,
+ 0x1171d, 0x1172b,
+ 0x11730, 0x1173f,
+}; /* CR_Ahom */
+
+/* 'Anatolian_Hieroglyphs': Script */
+static const OnigCodePoint CR_Anatolian_Hieroglyphs[] = {
+ 1,
+ 0x14400, 0x14646,
+}; /* CR_Anatolian_Hieroglyphs */
+
+/* 'Hatran': Script */
+static const OnigCodePoint CR_Hatran[] = {
+ 3,
+ 0x108e0, 0x108f2,
+ 0x108f4, 0x108f5,
+ 0x108fb, 0x108ff,
+}; /* CR_Hatran */
+
+/* 'Multani': Script */
+static const OnigCodePoint CR_Multani[] = {
+ 5,
+ 0x11280, 0x11286,
+ 0x11288, 0x11288,
+ 0x1128a, 0x1128d,
+ 0x1128f, 0x1129d,
+ 0x1129f, 0x112a9,
+}; /* CR_Multani */
+
+/* 'Old_Hungarian': Script */
+static const OnigCodePoint CR_Old_Hungarian[] = {
+ 3,
+ 0x10c80, 0x10cb2,
+ 0x10cc0, 0x10cf2,
+ 0x10cfa, 0x10cff,
+}; /* CR_Old_Hungarian */
+
+/* 'SignWriting': Script */
+static const OnigCodePoint CR_SignWriting[] = {
+ 3,
+ 0x1d800, 0x1da8b,
+ 0x1da9b, 0x1da9f,
+ 0x1daa1, 0x1daaf,
+}; /* CR_SignWriting */
+
+/* 'Adlam': Script */
+static const OnigCodePoint CR_Adlam[] = {
+ 3,
+ 0x1e900, 0x1e94b,
+ 0x1e950, 0x1e959,
+ 0x1e95e, 0x1e95f,
+}; /* CR_Adlam */
+
+/* 'Bhaiksuki': Script */
+static const OnigCodePoint CR_Bhaiksuki[] = {
+ 4,
+ 0x11c00, 0x11c08,
+ 0x11c0a, 0x11c36,
+ 0x11c38, 0x11c45,
+ 0x11c50, 0x11c6c,
+}; /* CR_Bhaiksuki */
+
+/* 'Marchen': Script */
+static const OnigCodePoint CR_Marchen[] = {
+ 3,
+ 0x11c70, 0x11c8f,
+ 0x11c92, 0x11ca7,
+ 0x11ca9, 0x11cb6,
+}; /* CR_Marchen */
+
+/* 'Newa': Script */
+static const OnigCodePoint CR_Newa[] = {
+ 3,
+ 0x11400, 0x11459,
+ 0x1145b, 0x1145b,
+ 0x1145d, 0x1145f,
+}; /* CR_Newa */
+
+/* 'Osage': Script */
+static const OnigCodePoint CR_Osage[] = {
+ 2,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+}; /* CR_Osage */
+
+/* 'Tangut': Script */
+static const OnigCodePoint CR_Tangut[] = {
+ 3,
+ 0x16fe0, 0x16fe0,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+}; /* CR_Tangut */
+
+/* 'Masaram_Gondi': Script */
+static const OnigCodePoint CR_Masaram_Gondi[] = {
+ 7,
+ 0x11d00, 0x11d06,
+ 0x11d08, 0x11d09,
+ 0x11d0b, 0x11d36,
+ 0x11d3a, 0x11d3a,
+ 0x11d3c, 0x11d3d,
+ 0x11d3f, 0x11d47,
+ 0x11d50, 0x11d59,
+}; /* CR_Masaram_Gondi */
+
+/* 'Nushu': Script */
+static const OnigCodePoint CR_Nushu[] = {
+ 2,
+ 0x16fe1, 0x16fe1,
+ 0x1b170, 0x1b2fb,
+}; /* CR_Nushu */
+
+/* 'Soyombo': Script */
+static const OnigCodePoint CR_Soyombo[] = {
+ 1,
+ 0x11a50, 0x11aa2,
+}; /* CR_Soyombo */
+
+/* 'Zanabazar_Square': Script */
+static const OnigCodePoint CR_Zanabazar_Square[] = {
+ 1,
+ 0x11a00, 0x11a47,
+}; /* CR_Zanabazar_Square */
+
+/* 'Dogra': Script */
+static const OnigCodePoint CR_Dogra[] = {
+ 1,
+ 0x11800, 0x1183b,
+}; /* CR_Dogra */
+
+/* 'Gunjala_Gondi': Script */
+static const OnigCodePoint CR_Gunjala_Gondi[] = {
+ 6,
+ 0x11d60, 0x11d65,
+ 0x11d67, 0x11d68,
+ 0x11d6a, 0x11d8e,
+ 0x11d90, 0x11d91,
+ 0x11d93, 0x11d98,
+ 0x11da0, 0x11da9,
+}; /* CR_Gunjala_Gondi */
+
+/* 'Makasar': Script */
+static const OnigCodePoint CR_Makasar[] = {
+ 1,
+ 0x11ee0, 0x11ef8,
+}; /* CR_Makasar */
+
+/* 'Medefaidrin': Script */
+static const OnigCodePoint CR_Medefaidrin[] = {
+ 1,
+ 0x16e40, 0x16e9a,
+}; /* CR_Medefaidrin */
+
+/* 'Hanifi_Rohingya': Script */
+static const OnigCodePoint CR_Hanifi_Rohingya[] = {
+ 2,
+ 0x10d00, 0x10d27,
+ 0x10d30, 0x10d39,
+}; /* CR_Hanifi_Rohingya */
+
+/* 'Sogdian': Script */
+static const OnigCodePoint CR_Sogdian[] = {
+ 1,
+ 0x10f30, 0x10f59,
+}; /* CR_Sogdian */
+
+/* 'Old_Sogdian': Script */
+static const OnigCodePoint CR_Old_Sogdian[] = {
+ 1,
+ 0x10f00, 0x10f27,
+}; /* CR_Old_Sogdian */
+
+/* 'Elymaic': Script */
+static const OnigCodePoint CR_Elymaic[] = {
+ 1,
+ 0x10fe0, 0x10ff6,
+}; /* CR_Elymaic */
+
+/* 'Nandinagari': Script */
+static const OnigCodePoint CR_Nandinagari[] = {
+ 3,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d7,
+ 0x119da, 0x119e4,
+}; /* CR_Nandinagari */
+
+/* 'Nyiakeng_Puachue_Hmong': Script */
+static const OnigCodePoint CR_Nyiakeng_Puachue_Hmong[] = {
+ 4,
+ 0x1e100, 0x1e12c,
+ 0x1e130, 0x1e13d,
+ 0x1e140, 0x1e149,
+ 0x1e14e, 0x1e14f,
+}; /* CR_Nyiakeng_Puachue_Hmong */
+
+/* 'Wancho': Script */
+static const OnigCodePoint CR_Wancho[] = {
+ 2,
+ 0x1e2c0, 0x1e2f9,
+ 0x1e2ff, 0x1e2ff,
+}; /* CR_Wancho */
+
+/* 'White_Space': Binary Property */
+#define CR_White_Space CR_Space
+
+/* 'Bidi_Control': Binary Property */
+static const OnigCodePoint CR_Bidi_Control[] = {
+ 4,
+ 0x061c, 0x061c,
+ 0x200e, 0x200f,
+ 0x202a, 0x202e,
+ 0x2066, 0x2069,
+}; /* CR_Bidi_Control */
+
+/* 'Join_Control': Binary Property */
+static const OnigCodePoint CR_Join_Control[] = {
+ 1,
+ 0x200c, 0x200d,
+}; /* CR_Join_Control */
+
+/* 'Dash': Binary Property */
+static const OnigCodePoint CR_Dash[] = {
+ 21,
+ 0x002d, 0x002d,
+ 0x058a, 0x058a,
+ 0x05be, 0x05be,
+ 0x1400, 0x1400,
+ 0x1806, 0x1806,
+ 0x2010, 0x2015,
+ 0x2053, 0x2053,
+ 0x207b, 0x207b,
+ 0x208b, 0x208b,
+ 0x2212, 0x2212,
+ 0x2e17, 0x2e17,
+ 0x2e1a, 0x2e1a,
+ 0x2e3a, 0x2e3b,
+ 0x2e40, 0x2e40,
+ 0x301c, 0x301c,
+ 0x3030, 0x3030,
+ 0x30a0, 0x30a0,
+ 0xfe31, 0xfe32,
+ 0xfe58, 0xfe58,
+ 0xfe63, 0xfe63,
+ 0xff0d, 0xff0d,
+}; /* CR_Dash */
+
+/* 'Hyphen': Binary Property */
+static const OnigCodePoint CR_Hyphen[] = {
+ 10,
+ 0x002d, 0x002d,
+ 0x00ad, 0x00ad,
+ 0x058a, 0x058a,
+ 0x1806, 0x1806,
+ 0x2010, 0x2011,
+ 0x2e17, 0x2e17,
+ 0x30fb, 0x30fb,
+ 0xfe63, 0xfe63,
+ 0xff0d, 0xff0d,
+ 0xff65, 0xff65,
+}; /* CR_Hyphen */
+
+/* 'Quotation_Mark': Binary Property */
+static const OnigCodePoint CR_Quotation_Mark[] = {
+ 13,
+ 0x0022, 0x0022,
+ 0x0027, 0x0027,
+ 0x00ab, 0x00ab,
+ 0x00bb, 0x00bb,
+ 0x2018, 0x201f,
+ 0x2039, 0x203a,
+ 0x2e42, 0x2e42,
+ 0x300c, 0x300f,
+ 0x301d, 0x301f,
+ 0xfe41, 0xfe44,
+ 0xff02, 0xff02,
+ 0xff07, 0xff07,
+ 0xff62, 0xff63,
+}; /* CR_Quotation_Mark */
+
+/* 'Terminal_Punctuation': Binary Property */
+static const OnigCodePoint CR_Terminal_Punctuation[] = {
+ 102,
+ 0x0021, 0x0021,
+ 0x002c, 0x002c,
+ 0x002e, 0x002e,
+ 0x003a, 0x003b,
+ 0x003f, 0x003f,
+ 0x037e, 0x037e,
+ 0x0387, 0x0387,
+ 0x0589, 0x0589,
+ 0x05c3, 0x05c3,
+ 0x060c, 0x060c,
+ 0x061b, 0x061b,
+ 0x061e, 0x061f,
+ 0x06d4, 0x06d4,
+ 0x0700, 0x070a,
+ 0x070c, 0x070c,
+ 0x07f8, 0x07f9,
+ 0x0830, 0x083e,
+ 0x085e, 0x085e,
+ 0x0964, 0x0965,
+ 0x0e5a, 0x0e5b,
+ 0x0f08, 0x0f08,
+ 0x0f0d, 0x0f12,
+ 0x104a, 0x104b,
+ 0x1361, 0x1368,
+ 0x166e, 0x166e,
+ 0x16eb, 0x16ed,
+ 0x1735, 0x1736,
+ 0x17d4, 0x17d6,
+ 0x17da, 0x17da,
+ 0x1802, 0x1805,
+ 0x1808, 0x1809,
+ 0x1944, 0x1945,
+ 0x1aa8, 0x1aab,
+ 0x1b5a, 0x1b5b,
+ 0x1b5d, 0x1b5f,
+ 0x1c3b, 0x1c3f,
+ 0x1c7e, 0x1c7f,
+ 0x203c, 0x203d,
+ 0x2047, 0x2049,
+ 0x2e2e, 0x2e2e,
+ 0x2e3c, 0x2e3c,
+ 0x2e41, 0x2e41,
+ 0x2e4c, 0x2e4c,
+ 0x2e4e, 0x2e4f,
+ 0x3001, 0x3002,
+ 0xa4fe, 0xa4ff,
+ 0xa60d, 0xa60f,
+ 0xa6f3, 0xa6f7,
+ 0xa876, 0xa877,
+ 0xa8ce, 0xa8cf,
+ 0xa92f, 0xa92f,
+ 0xa9c7, 0xa9c9,
+ 0xaa5d, 0xaa5f,
+ 0xaadf, 0xaadf,
+ 0xaaf0, 0xaaf1,
+ 0xabeb, 0xabeb,
+ 0xfe50, 0xfe52,
+ 0xfe54, 0xfe57,
+ 0xff01, 0xff01,
+ 0xff0c, 0xff0c,
+ 0xff0e, 0xff0e,
+ 0xff1a, 0xff1b,
+ 0xff1f, 0xff1f,
+ 0xff61, 0xff61,
+ 0xff64, 0xff64,
+ 0x1039f, 0x1039f,
+ 0x103d0, 0x103d0,
+ 0x10857, 0x10857,
+ 0x1091f, 0x1091f,
+ 0x10a56, 0x10a57,
+ 0x10af0, 0x10af5,
+ 0x10b3a, 0x10b3f,
+ 0x10b99, 0x10b9c,
+ 0x10f55, 0x10f59,
+ 0x11047, 0x1104d,
+ 0x110be, 0x110c1,
+ 0x11141, 0x11143,
+ 0x111c5, 0x111c6,
+ 0x111cd, 0x111cd,
+ 0x111de, 0x111df,
+ 0x11238, 0x1123c,
+ 0x112a9, 0x112a9,
+ 0x1144b, 0x1144d,
+ 0x1145b, 0x1145b,
+ 0x115c2, 0x115c5,
+ 0x115c9, 0x115d7,
+ 0x11641, 0x11642,
+ 0x1173c, 0x1173e,
+ 0x11a42, 0x11a43,
+ 0x11a9b, 0x11a9c,
+ 0x11aa1, 0x11aa2,
+ 0x11c41, 0x11c43,
+ 0x11c71, 0x11c71,
+ 0x11ef7, 0x11ef8,
+ 0x12470, 0x12474,
+ 0x16a6e, 0x16a6f,
+ 0x16af5, 0x16af5,
+ 0x16b37, 0x16b39,
+ 0x16b44, 0x16b44,
+ 0x16e97, 0x16e98,
+ 0x1bc9f, 0x1bc9f,
+ 0x1da87, 0x1da8a,
+}; /* CR_Terminal_Punctuation */
+
+/* 'Other_Math': Binary Property */
+static const OnigCodePoint CR_Other_Math[] = {
+ 134,
+ 0x005e, 0x005e,
+ 0x03d0, 0x03d2,
+ 0x03d5, 0x03d5,
+ 0x03f0, 0x03f1,
+ 0x03f4, 0x03f5,
+ 0x2016, 0x2016,
+ 0x2032, 0x2034,
+ 0x2040, 0x2040,
+ 0x2061, 0x2064,
+ 0x207d, 0x207e,
+ 0x208d, 0x208e,
+ 0x20d0, 0x20dc,
+ 0x20e1, 0x20e1,
+ 0x20e5, 0x20e6,
+ 0x20eb, 0x20ef,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210a, 0x2113,
+ 0x2115, 0x2115,
+ 0x2119, 0x211d,
+ 0x2124, 0x2124,
+ 0x2128, 0x2129,
+ 0x212c, 0x212d,
+ 0x212f, 0x2131,
+ 0x2133, 0x2138,
+ 0x213c, 0x213f,
+ 0x2145, 0x2149,
+ 0x2195, 0x2199,
+ 0x219c, 0x219f,
+ 0x21a1, 0x21a2,
+ 0x21a4, 0x21a5,
+ 0x21a7, 0x21a7,
+ 0x21a9, 0x21ad,
+ 0x21b0, 0x21b1,
+ 0x21b6, 0x21b7,
+ 0x21bc, 0x21cd,
+ 0x21d0, 0x21d1,
+ 0x21d3, 0x21d3,
+ 0x21d5, 0x21db,
+ 0x21dd, 0x21dd,
+ 0x21e4, 0x21e5,
+ 0x2308, 0x230b,
+ 0x23b4, 0x23b5,
+ 0x23b7, 0x23b7,
+ 0x23d0, 0x23d0,
+ 0x23e2, 0x23e2,
+ 0x25a0, 0x25a1,
+ 0x25ae, 0x25b6,
+ 0x25bc, 0x25c0,
+ 0x25c6, 0x25c7,
+ 0x25ca, 0x25cb,
+ 0x25cf, 0x25d3,
+ 0x25e2, 0x25e2,
+ 0x25e4, 0x25e4,
+ 0x25e7, 0x25ec,
+ 0x2605, 0x2606,
+ 0x2640, 0x2640,
+ 0x2642, 0x2642,
+ 0x2660, 0x2663,
+ 0x266d, 0x266e,
+ 0x27c5, 0x27c6,
+ 0x27e6, 0x27ef,
+ 0x2983, 0x2998,
+ 0x29d8, 0x29db,
+ 0x29fc, 0x29fd,
+ 0xfe61, 0xfe61,
+ 0xfe63, 0xfe63,
+ 0xfe68, 0xfe68,
+ 0xff3c, 0xff3c,
+ 0xff3e, 0xff3e,
+ 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, 0x1d6c0,
+ 0x1d6c2, 0x1d6da,
+ 0x1d6dc, 0x1d6fa,
+ 0x1d6fc, 0x1d714,
+ 0x1d716, 0x1d734,
+ 0x1d736, 0x1d74e,
+ 0x1d750, 0x1d76e,
+ 0x1d770, 0x1d788,
+ 0x1d78a, 0x1d7a8,
+ 0x1d7aa, 0x1d7c2,
+ 0x1d7c4, 0x1d7cb,
+ 0x1d7ce, 0x1d7ff,
+ 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,
+}; /* CR_Other_Math */
+
+/* 'Hex_Digit': Binary Property */
+static const OnigCodePoint CR_Hex_Digit[] = {
+ 6,
+ 0x0030, 0x0039,
+ 0x0041, 0x0046,
+ 0x0061, 0x0066,
+ 0xff10, 0xff19,
+ 0xff21, 0xff26,
+ 0xff41, 0xff46,
+}; /* CR_Hex_Digit */
+
+/* 'ASCII_Hex_Digit': Binary Property */
+#define CR_ASCII_Hex_Digit CR_XDigit
+
+/* 'Other_Alphabetic': Binary Property */
+static const OnigCodePoint CR_Other_Alphabetic[] = {
+ 221,
+ 0x0345, 0x0345,
+ 0x05b0, 0x05bd,
+ 0x05bf, 0x05bf,
+ 0x05c1, 0x05c2,
+ 0x05c4, 0x05c5,
+ 0x05c7, 0x05c7,
+ 0x0610, 0x061a,
+ 0x064b, 0x0657,
+ 0x0659, 0x065f,
+ 0x0670, 0x0670,
+ 0x06d6, 0x06dc,
+ 0x06e1, 0x06e4,
+ 0x06e7, 0x06e8,
+ 0x06ed, 0x06ed,
+ 0x0711, 0x0711,
+ 0x0730, 0x073f,
+ 0x07a6, 0x07b0,
+ 0x0816, 0x0817,
+ 0x081b, 0x0823,
+ 0x0825, 0x0827,
+ 0x0829, 0x082c,
+ 0x08d4, 0x08df,
+ 0x08e3, 0x08e9,
+ 0x08f0, 0x0903,
+ 0x093a, 0x093b,
+ 0x093e, 0x094c,
+ 0x094e, 0x094f,
+ 0x0955, 0x0957,
+ 0x0962, 0x0963,
+ 0x0981, 0x0983,
+ 0x09be, 0x09c4,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09cc,
+ 0x09d7, 0x09d7,
+ 0x09e2, 0x09e3,
+ 0x0a01, 0x0a03,
+ 0x0a3e, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4c,
+ 0x0a51, 0x0a51,
+ 0x0a70, 0x0a71,
+ 0x0a75, 0x0a75,
+ 0x0a81, 0x0a83,
+ 0x0abe, 0x0ac5,
+ 0x0ac7, 0x0ac9,
+ 0x0acb, 0x0acc,
+ 0x0ae2, 0x0ae3,
+ 0x0afa, 0x0afc,
+ 0x0b01, 0x0b03,
+ 0x0b3e, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4c,
+ 0x0b56, 0x0b57,
+ 0x0b62, 0x0b63,
+ 0x0b82, 0x0b82,
+ 0x0bbe, 0x0bc2,
+ 0x0bc6, 0x0bc8,
+ 0x0bca, 0x0bcc,
+ 0x0bd7, 0x0bd7,
+ 0x0c00, 0x0c03,
+ 0x0c3e, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4c,
+ 0x0c55, 0x0c56,
+ 0x0c62, 0x0c63,
+ 0x0c81, 0x0c83,
+ 0x0cbe, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccc,
+ 0x0cd5, 0x0cd6,
+ 0x0ce2, 0x0ce3,
+ 0x0d00, 0x0d03,
+ 0x0d3e, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4c,
+ 0x0d57, 0x0d57,
+ 0x0d62, 0x0d63,
+ 0x0d82, 0x0d83,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df3,
+ 0x0e31, 0x0e31,
+ 0x0e34, 0x0e3a,
+ 0x0e4d, 0x0e4d,
+ 0x0eb1, 0x0eb1,
+ 0x0eb4, 0x0eb9,
+ 0x0ebb, 0x0ebc,
+ 0x0ecd, 0x0ecd,
+ 0x0f71, 0x0f81,
+ 0x0f8d, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x102b, 0x1036,
+ 0x1038, 0x1038,
+ 0x103b, 0x103e,
+ 0x1056, 0x1059,
+ 0x105e, 0x1060,
+ 0x1062, 0x1064,
+ 0x1067, 0x106d,
+ 0x1071, 0x1074,
+ 0x1082, 0x108d,
+ 0x108f, 0x108f,
+ 0x109a, 0x109d,
+ 0x1712, 0x1713,
+ 0x1732, 0x1733,
+ 0x1752, 0x1753,
+ 0x1772, 0x1773,
+ 0x17b6, 0x17c8,
+ 0x1885, 0x1886,
+ 0x18a9, 0x18a9,
+ 0x1920, 0x192b,
+ 0x1930, 0x1938,
+ 0x1a17, 0x1a1b,
+ 0x1a55, 0x1a5e,
+ 0x1a61, 0x1a74,
+ 0x1b00, 0x1b04,
+ 0x1b35, 0x1b43,
+ 0x1b80, 0x1b82,
+ 0x1ba1, 0x1ba9,
+ 0x1bac, 0x1bad,
+ 0x1be7, 0x1bf1,
+ 0x1c24, 0x1c36,
+ 0x1de7, 0x1df4,
+ 0x24b6, 0x24e9,
+ 0x2de0, 0x2dff,
+ 0xa674, 0xa67b,
+ 0xa69e, 0xa69f,
+ 0xa802, 0xa802,
+ 0xa80b, 0xa80b,
+ 0xa823, 0xa827,
+ 0xa880, 0xa881,
+ 0xa8b4, 0xa8c3,
+ 0xa8c5, 0xa8c5,
+ 0xa8ff, 0xa8ff,
+ 0xa926, 0xa92a,
+ 0xa947, 0xa952,
+ 0xa980, 0xa983,
+ 0xa9b4, 0xa9bf,
+ 0xa9e5, 0xa9e5,
+ 0xaa29, 0xaa36,
+ 0xaa43, 0xaa43,
+ 0xaa4c, 0xaa4d,
+ 0xaa7b, 0xaa7d,
+ 0xaab0, 0xaab0,
+ 0xaab2, 0xaab4,
+ 0xaab7, 0xaab8,
+ 0xaabe, 0xaabe,
+ 0xaaeb, 0xaaef,
+ 0xaaf5, 0xaaf5,
+ 0xabe3, 0xabea,
+ 0xfb1e, 0xfb1e,
+ 0x10376, 0x1037a,
+ 0x10a01, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a0f,
+ 0x10d24, 0x10d27,
+ 0x11000, 0x11002,
+ 0x11038, 0x11045,
+ 0x11082, 0x11082,
+ 0x110b0, 0x110b8,
+ 0x11100, 0x11102,
+ 0x11127, 0x11132,
+ 0x11145, 0x11146,
+ 0x11180, 0x11182,
+ 0x111b3, 0x111bf,
+ 0x1122c, 0x11234,
+ 0x11237, 0x11237,
+ 0x1123e, 0x1123e,
+ 0x112df, 0x112e8,
+ 0x11300, 0x11303,
+ 0x1133e, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134b, 0x1134c,
+ 0x11357, 0x11357,
+ 0x11362, 0x11363,
+ 0x11435, 0x11441,
+ 0x11443, 0x11445,
+ 0x114b0, 0x114c1,
+ 0x115af, 0x115b5,
+ 0x115b8, 0x115be,
+ 0x115dc, 0x115dd,
+ 0x11630, 0x1163e,
+ 0x11640, 0x11640,
+ 0x116ab, 0x116b5,
+ 0x1171d, 0x1172a,
+ 0x1182c, 0x11838,
+ 0x119d1, 0x119d7,
+ 0x119da, 0x119df,
+ 0x119e4, 0x119e4,
+ 0x11a01, 0x11a0a,
+ 0x11a35, 0x11a39,
+ 0x11a3b, 0x11a3e,
+ 0x11a51, 0x11a5b,
+ 0x11a8a, 0x11a97,
+ 0x11c2f, 0x11c36,
+ 0x11c38, 0x11c3e,
+ 0x11c92, 0x11ca7,
+ 0x11ca9, 0x11cb6,
+ 0x11d31, 0x11d36,
+ 0x11d3a, 0x11d3a,
+ 0x11d3c, 0x11d3d,
+ 0x11d3f, 0x11d41,
+ 0x11d43, 0x11d43,
+ 0x11d47, 0x11d47,
+ 0x11d8a, 0x11d8e,
+ 0x11d90, 0x11d91,
+ 0x11d93, 0x11d96,
+ 0x11ef3, 0x11ef6,
+ 0x16f4f, 0x16f4f,
+ 0x16f51, 0x16f87,
+ 0x16f8f, 0x16f92,
+ 0x1bc9e, 0x1bc9e,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e947, 0x1e947,
+ 0x1f130, 0x1f149,
+ 0x1f150, 0x1f169,
+ 0x1f170, 0x1f189,
+}; /* CR_Other_Alphabetic */
+
+/* 'Ideographic': Binary Property */
+static const OnigCodePoint CR_Ideographic[] = {
+ 16,
+ 0x3006, 0x3007,
+ 0x3021, 0x3029,
+ 0x3038, 0x303a,
+ 0x3400, 0x4db5,
+ 0x4e00, 0x9fef,
+ 0xf900, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b170, 0x1b2fb,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+}; /* CR_Ideographic */
+
+/* 'Diacritic': Binary Property */
+static const OnigCodePoint CR_Diacritic[] = {
+ 171,
+ 0x005e, 0x005e,
+ 0x0060, 0x0060,
+ 0x00a8, 0x00a8,
+ 0x00af, 0x00af,
+ 0x00b4, 0x00b4,
+ 0x00b7, 0x00b8,
+ 0x02b0, 0x034e,
+ 0x0350, 0x0357,
+ 0x035d, 0x0362,
+ 0x0374, 0x0375,
+ 0x037a, 0x037a,
+ 0x0384, 0x0385,
+ 0x0483, 0x0487,
+ 0x0559, 0x0559,
+ 0x0591, 0x05a1,
+ 0x05a3, 0x05bd,
+ 0x05bf, 0x05bf,
+ 0x05c1, 0x05c2,
+ 0x05c4, 0x05c4,
+ 0x064b, 0x0652,
+ 0x0657, 0x0658,
+ 0x06df, 0x06e0,
+ 0x06e5, 0x06e6,
+ 0x06ea, 0x06ec,
+ 0x0730, 0x074a,
+ 0x07a6, 0x07b0,
+ 0x07eb, 0x07f5,
+ 0x0818, 0x0819,
+ 0x08e3, 0x08fe,
+ 0x093c, 0x093c,
+ 0x094d, 0x094d,
+ 0x0951, 0x0954,
+ 0x0971, 0x0971,
+ 0x09bc, 0x09bc,
+ 0x09cd, 0x09cd,
+ 0x0a3c, 0x0a3c,
+ 0x0a4d, 0x0a4d,
+ 0x0abc, 0x0abc,
+ 0x0acd, 0x0acd,
+ 0x0afd, 0x0aff,
+ 0x0b3c, 0x0b3c,
+ 0x0b4d, 0x0b4d,
+ 0x0bcd, 0x0bcd,
+ 0x0c4d, 0x0c4d,
+ 0x0cbc, 0x0cbc,
+ 0x0ccd, 0x0ccd,
+ 0x0d3b, 0x0d3c,
+ 0x0d4d, 0x0d4d,
+ 0x0dca, 0x0dca,
+ 0x0e47, 0x0e4c,
+ 0x0e4e, 0x0e4e,
+ 0x0eba, 0x0eba,
+ 0x0ec8, 0x0ecc,
+ 0x0f18, 0x0f19,
+ 0x0f35, 0x0f35,
+ 0x0f37, 0x0f37,
+ 0x0f39, 0x0f39,
+ 0x0f3e, 0x0f3f,
+ 0x0f82, 0x0f84,
+ 0x0f86, 0x0f87,
+ 0x0fc6, 0x0fc6,
+ 0x1037, 0x1037,
+ 0x1039, 0x103a,
+ 0x1063, 0x1064,
+ 0x1069, 0x106d,
+ 0x1087, 0x108d,
+ 0x108f, 0x108f,
+ 0x109a, 0x109b,
+ 0x135d, 0x135f,
+ 0x17c9, 0x17d3,
+ 0x17dd, 0x17dd,
+ 0x1939, 0x193b,
+ 0x1a75, 0x1a7c,
+ 0x1a7f, 0x1a7f,
+ 0x1ab0, 0x1abd,
+ 0x1b34, 0x1b34,
+ 0x1b44, 0x1b44,
+ 0x1b6b, 0x1b73,
+ 0x1baa, 0x1bab,
+ 0x1c36, 0x1c37,
+ 0x1c78, 0x1c7d,
+ 0x1cd0, 0x1ce8,
+ 0x1ced, 0x1ced,
+ 0x1cf4, 0x1cf4,
+ 0x1cf7, 0x1cf9,
+ 0x1d2c, 0x1d6a,
+ 0x1dc4, 0x1dcf,
+ 0x1df5, 0x1df9,
+ 0x1dfd, 0x1dff,
+ 0x1fbd, 0x1fbd,
+ 0x1fbf, 0x1fc1,
+ 0x1fcd, 0x1fcf,
+ 0x1fdd, 0x1fdf,
+ 0x1fed, 0x1fef,
+ 0x1ffd, 0x1ffe,
+ 0x2cef, 0x2cf1,
+ 0x2e2f, 0x2e2f,
+ 0x302a, 0x302f,
+ 0x3099, 0x309c,
+ 0x30fc, 0x30fc,
+ 0xa66f, 0xa66f,
+ 0xa67c, 0xa67d,
+ 0xa67f, 0xa67f,
+ 0xa69c, 0xa69d,
+ 0xa6f0, 0xa6f1,
+ 0xa700, 0xa721,
+ 0xa788, 0xa78a,
+ 0xa7f8, 0xa7f9,
+ 0xa8c4, 0xa8c4,
+ 0xa8e0, 0xa8f1,
+ 0xa92b, 0xa92e,
+ 0xa953, 0xa953,
+ 0xa9b3, 0xa9b3,
+ 0xa9c0, 0xa9c0,
+ 0xa9e5, 0xa9e5,
+ 0xaa7b, 0xaa7d,
+ 0xaabf, 0xaac2,
+ 0xaaf6, 0xaaf6,
+ 0xab5b, 0xab5f,
+ 0xabec, 0xabed,
+ 0xfb1e, 0xfb1e,
+ 0xfe20, 0xfe2f,
+ 0xff3e, 0xff3e,
+ 0xff40, 0xff40,
+ 0xff70, 0xff70,
+ 0xff9e, 0xff9f,
+ 0xffe3, 0xffe3,
+ 0x102e0, 0x102e0,
+ 0x10ae5, 0x10ae6,
+ 0x10d22, 0x10d27,
+ 0x10f46, 0x10f50,
+ 0x110b9, 0x110ba,
+ 0x11133, 0x11134,
+ 0x11173, 0x11173,
+ 0x111c0, 0x111c0,
+ 0x111ca, 0x111cc,
+ 0x11235, 0x11236,
+ 0x112e9, 0x112ea,
+ 0x1133c, 0x1133c,
+ 0x1134d, 0x1134d,
+ 0x11366, 0x1136c,
+ 0x11370, 0x11374,
+ 0x11442, 0x11442,
+ 0x11446, 0x11446,
+ 0x114c2, 0x114c3,
+ 0x115bf, 0x115c0,
+ 0x1163f, 0x1163f,
+ 0x116b6, 0x116b7,
+ 0x1172b, 0x1172b,
+ 0x11839, 0x1183a,
+ 0x119e0, 0x119e0,
+ 0x11a34, 0x11a34,
+ 0x11a47, 0x11a47,
+ 0x11a99, 0x11a99,
+ 0x11c3f, 0x11c3f,
+ 0x11d42, 0x11d42,
+ 0x11d44, 0x11d45,
+ 0x11d97, 0x11d97,
+ 0x16af0, 0x16af4,
+ 0x16b30, 0x16b36,
+ 0x16f8f, 0x16f9f,
+ 0x1d167, 0x1d169,
+ 0x1d16d, 0x1d172,
+ 0x1d17b, 0x1d182,
+ 0x1d185, 0x1d18b,
+ 0x1d1aa, 0x1d1ad,
+ 0x1e130, 0x1e136,
+ 0x1e2ec, 0x1e2ef,
+ 0x1e8d0, 0x1e8d6,
+ 0x1e944, 0x1e946,
+ 0x1e948, 0x1e94a,
+}; /* CR_Diacritic */
+
+/* 'Extender': Binary Property */
+static const OnigCodePoint CR_Extender[] = {
+ 31,
+ 0x00b7, 0x00b7,
+ 0x02d0, 0x02d1,
+ 0x0640, 0x0640,
+ 0x07fa, 0x07fa,
+ 0x0e46, 0x0e46,
+ 0x0ec6, 0x0ec6,
+ 0x180a, 0x180a,
+ 0x1843, 0x1843,
+ 0x1aa7, 0x1aa7,
+ 0x1c36, 0x1c36,
+ 0x1c7b, 0x1c7b,
+ 0x3005, 0x3005,
+ 0x3031, 0x3035,
+ 0x309d, 0x309e,
+ 0x30fc, 0x30fe,
+ 0xa015, 0xa015,
+ 0xa60c, 0xa60c,
+ 0xa9cf, 0xa9cf,
+ 0xa9e6, 0xa9e6,
+ 0xaa70, 0xaa70,
+ 0xaadd, 0xaadd,
+ 0xaaf3, 0xaaf4,
+ 0xff70, 0xff70,
+ 0x1135d, 0x1135d,
+ 0x115c6, 0x115c8,
+ 0x11a98, 0x11a98,
+ 0x16b42, 0x16b43,
+ 0x16fe0, 0x16fe1,
+ 0x16fe3, 0x16fe3,
+ 0x1e13c, 0x1e13d,
+ 0x1e944, 0x1e946,
+}; /* CR_Extender */
+
+/* 'Other_Lowercase': Binary Property */
+static const OnigCodePoint CR_Other_Lowercase[] = {
+ 20,
+ 0x00aa, 0x00aa,
+ 0x00ba, 0x00ba,
+ 0x02b0, 0x02b8,
+ 0x02c0, 0x02c1,
+ 0x02e0, 0x02e4,
+ 0x0345, 0x0345,
+ 0x037a, 0x037a,
+ 0x1d2c, 0x1d6a,
+ 0x1d78, 0x1d78,
+ 0x1d9b, 0x1dbf,
+ 0x2071, 0x2071,
+ 0x207f, 0x207f,
+ 0x2090, 0x209c,
+ 0x2170, 0x217f,
+ 0x24d0, 0x24e9,
+ 0x2c7c, 0x2c7d,
+ 0xa69c, 0xa69d,
+ 0xa770, 0xa770,
+ 0xa7f8, 0xa7f9,
+ 0xab5c, 0xab5f,
+}; /* CR_Other_Lowercase */
+
+/* 'Other_Uppercase': Binary Property */
+static const OnigCodePoint CR_Other_Uppercase[] = {
+ 5,
+ 0x2160, 0x216f,
+ 0x24b6, 0x24cf,
+ 0x1f130, 0x1f149,
+ 0x1f150, 0x1f169,
+ 0x1f170, 0x1f189,
+}; /* CR_Other_Uppercase */
+
+/* 'Noncharacter_Code_Point': Binary Property */
+static const OnigCodePoint CR_Noncharacter_Code_Point[] = {
+ 18,
+ 0xfdd0, 0xfdef,
+ 0xfffe, 0xffff,
+ 0x1fffe, 0x1ffff,
+ 0x2fffe, 0x2ffff,
+ 0x3fffe, 0x3ffff,
+ 0x4fffe, 0x4ffff,
+ 0x5fffe, 0x5ffff,
+ 0x6fffe, 0x6ffff,
+ 0x7fffe, 0x7ffff,
+ 0x8fffe, 0x8ffff,
+ 0x9fffe, 0x9ffff,
+ 0xafffe, 0xaffff,
+ 0xbfffe, 0xbffff,
+ 0xcfffe, 0xcffff,
+ 0xdfffe, 0xdffff,
+ 0xefffe, 0xeffff,
+ 0xffffe, 0xfffff,
+ 0x10fffe, 0x10ffff,
+}; /* CR_Noncharacter_Code_Point */
+
+/* 'Other_Grapheme_Extend': Binary Property */
+static const OnigCodePoint CR_Other_Grapheme_Extend[] = {
+ 24,
+ 0x09be, 0x09be,
+ 0x09d7, 0x09d7,
+ 0x0b3e, 0x0b3e,
+ 0x0b57, 0x0b57,
+ 0x0bbe, 0x0bbe,
+ 0x0bd7, 0x0bd7,
+ 0x0cc2, 0x0cc2,
+ 0x0cd5, 0x0cd6,
+ 0x0d3e, 0x0d3e,
+ 0x0d57, 0x0d57,
+ 0x0dcf, 0x0dcf,
+ 0x0ddf, 0x0ddf,
+ 0x1b35, 0x1b35,
+ 0x200c, 0x200c,
+ 0x302e, 0x302f,
+ 0xff9e, 0xff9f,
+ 0x1133e, 0x1133e,
+ 0x11357, 0x11357,
+ 0x114b0, 0x114b0,
+ 0x114bd, 0x114bd,
+ 0x115af, 0x115af,
+ 0x1d165, 0x1d165,
+ 0x1d16e, 0x1d172,
+ 0xe0020, 0xe007f,
+}; /* CR_Other_Grapheme_Extend */
+
+/* 'IDS_Binary_Operator': Binary Property */
+static const OnigCodePoint CR_IDS_Binary_Operator[] = {
+ 2,
+ 0x2ff0, 0x2ff1,
+ 0x2ff4, 0x2ffb,
+}; /* CR_IDS_Binary_Operator */
+
+/* 'IDS_Trinary_Operator': Binary Property */
+static const OnigCodePoint CR_IDS_Trinary_Operator[] = {
+ 1,
+ 0x2ff2, 0x2ff3,
+}; /* CR_IDS_Trinary_Operator */
+
+/* 'Radical': Binary Property */
+static const OnigCodePoint CR_Radical[] = {
+ 3,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+}; /* CR_Radical */
+
+/* 'Unified_Ideograph': Binary Property */
+static const OnigCodePoint CR_Unified_Ideograph[] = {
+ 14,
+ 0x3400, 0x4db5,
+ 0x4e00, 0x9fef,
+ 0xfa0e, 0xfa0f,
+ 0xfa11, 0xfa11,
+ 0xfa13, 0xfa14,
+ 0xfa1f, 0xfa1f,
+ 0xfa21, 0xfa21,
+ 0xfa23, 0xfa24,
+ 0xfa27, 0xfa29,
+ 0x20000, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+}; /* CR_Unified_Ideograph */
+
+/* 'Other_Default_Ignorable_Code_Point': Binary Property */
+static const OnigCodePoint CR_Other_Default_Ignorable_Code_Point[] = {
+ 11,
+ 0x034f, 0x034f,
+ 0x115f, 0x1160,
+ 0x17b4, 0x17b5,
+ 0x2065, 0x2065,
+ 0x3164, 0x3164,
+ 0xffa0, 0xffa0,
+ 0xfff0, 0xfff8,
+ 0xe0000, 0xe0000,
+ 0xe0002, 0xe001f,
+ 0xe0080, 0xe00ff,
+ 0xe01f0, 0xe0fff,
+}; /* CR_Other_Default_Ignorable_Code_Point */
+
+/* 'Deprecated': Binary Property */
+static const OnigCodePoint CR_Deprecated[] = {
+ 8,
+ 0x0149, 0x0149,
+ 0x0673, 0x0673,
+ 0x0f77, 0x0f77,
+ 0x0f79, 0x0f79,
+ 0x17a3, 0x17a4,
+ 0x206a, 0x206f,
+ 0x2329, 0x232a,
+ 0xe0001, 0xe0001,
+}; /* CR_Deprecated */
+
+/* 'Soft_Dotted': Binary Property */
+static const OnigCodePoint CR_Soft_Dotted[] = {
+ 31,
+ 0x0069, 0x006a,
+ 0x012f, 0x012f,
+ 0x0249, 0x0249,
+ 0x0268, 0x0268,
+ 0x029d, 0x029d,
+ 0x02b2, 0x02b2,
+ 0x03f3, 0x03f3,
+ 0x0456, 0x0456,
+ 0x0458, 0x0458,
+ 0x1d62, 0x1d62,
+ 0x1d96, 0x1d96,
+ 0x1da4, 0x1da4,
+ 0x1da8, 0x1da8,
+ 0x1e2d, 0x1e2d,
+ 0x1ecb, 0x1ecb,
+ 0x2071, 0x2071,
+ 0x2148, 0x2149,
+ 0x2c7c, 0x2c7c,
+ 0x1d422, 0x1d423,
+ 0x1d456, 0x1d457,
+ 0x1d48a, 0x1d48b,
+ 0x1d4be, 0x1d4bf,
+ 0x1d4f2, 0x1d4f3,
+ 0x1d526, 0x1d527,
+ 0x1d55a, 0x1d55b,
+ 0x1d58e, 0x1d58f,
+ 0x1d5c2, 0x1d5c3,
+ 0x1d5f6, 0x1d5f7,
+ 0x1d62a, 0x1d62b,
+ 0x1d65e, 0x1d65f,
+ 0x1d692, 0x1d693,
+}; /* CR_Soft_Dotted */
+
+/* 'Logical_Order_Exception': Binary Property */
+static const OnigCodePoint CR_Logical_Order_Exception[] = {
+ 7,
+ 0x0e40, 0x0e44,
+ 0x0ec0, 0x0ec4,
+ 0x19b5, 0x19b7,
+ 0x19ba, 0x19ba,
+ 0xaab5, 0xaab6,
+ 0xaab9, 0xaab9,
+ 0xaabb, 0xaabc,
+}; /* CR_Logical_Order_Exception */
+
+/* 'Other_ID_Start': Binary Property */
+static const OnigCodePoint CR_Other_ID_Start[] = {
+ 4,
+ 0x1885, 0x1886,
+ 0x2118, 0x2118,
+ 0x212e, 0x212e,
+ 0x309b, 0x309c,
+}; /* CR_Other_ID_Start */
+
+/* 'Other_ID_Continue': Binary Property */
+static const OnigCodePoint CR_Other_ID_Continue[] = {
+ 4,
+ 0x00b7, 0x00b7,
+ 0x0387, 0x0387,
+ 0x1369, 0x1371,
+ 0x19da, 0x19da,
+}; /* CR_Other_ID_Continue */
+
+/* 'Sentence_Terminal': Binary Property */
+static const OnigCodePoint CR_Sentence_Terminal[] = {
+ 74,
+ 0x0021, 0x0021,
+ 0x002e, 0x002e,
+ 0x003f, 0x003f,
+ 0x0589, 0x0589,
+ 0x061e, 0x061f,
+ 0x06d4, 0x06d4,
+ 0x0700, 0x0702,
+ 0x07f9, 0x07f9,
+ 0x0837, 0x0837,
+ 0x0839, 0x0839,
+ 0x083d, 0x083e,
+ 0x0964, 0x0965,
+ 0x104a, 0x104b,
+ 0x1362, 0x1362,
+ 0x1367, 0x1368,
+ 0x166e, 0x166e,
+ 0x1735, 0x1736,
+ 0x1803, 0x1803,
+ 0x1809, 0x1809,
+ 0x1944, 0x1945,
+ 0x1aa8, 0x1aab,
+ 0x1b5a, 0x1b5b,
+ 0x1b5e, 0x1b5f,
+ 0x1c3b, 0x1c3c,
+ 0x1c7e, 0x1c7f,
+ 0x203c, 0x203d,
+ 0x2047, 0x2049,
+ 0x2e2e, 0x2e2e,
+ 0x2e3c, 0x2e3c,
+ 0x3002, 0x3002,
+ 0xa4ff, 0xa4ff,
+ 0xa60e, 0xa60f,
+ 0xa6f3, 0xa6f3,
+ 0xa6f7, 0xa6f7,
+ 0xa876, 0xa877,
+ 0xa8ce, 0xa8cf,
+ 0xa92f, 0xa92f,
+ 0xa9c8, 0xa9c9,
+ 0xaa5d, 0xaa5f,
+ 0xaaf0, 0xaaf1,
+ 0xabeb, 0xabeb,
+ 0xfe52, 0xfe52,
+ 0xfe56, 0xfe57,
+ 0xff01, 0xff01,
+ 0xff0e, 0xff0e,
+ 0xff1f, 0xff1f,
+ 0xff61, 0xff61,
+ 0x10a56, 0x10a57,
+ 0x10f55, 0x10f59,
+ 0x11047, 0x11048,
+ 0x110be, 0x110c1,
+ 0x11141, 0x11143,
+ 0x111c5, 0x111c6,
+ 0x111cd, 0x111cd,
+ 0x111de, 0x111df,
+ 0x11238, 0x11239,
+ 0x1123b, 0x1123c,
+ 0x112a9, 0x112a9,
+ 0x1144b, 0x1144c,
+ 0x115c2, 0x115c3,
+ 0x115c9, 0x115d7,
+ 0x11641, 0x11642,
+ 0x1173c, 0x1173e,
+ 0x11a42, 0x11a43,
+ 0x11a9b, 0x11a9c,
+ 0x11c41, 0x11c42,
+ 0x11ef7, 0x11ef8,
+ 0x16a6e, 0x16a6f,
+ 0x16af5, 0x16af5,
+ 0x16b37, 0x16b38,
+ 0x16b44, 0x16b44,
+ 0x16e98, 0x16e98,
+ 0x1bc9f, 0x1bc9f,
+ 0x1da88, 0x1da88,
+}; /* CR_Sentence_Terminal */
+
+/* 'Variation_Selector': Binary Property */
+static const OnigCodePoint CR_Variation_Selector[] = {
+ 3,
+ 0x180b, 0x180d,
+ 0xfe00, 0xfe0f,
+ 0xe0100, 0xe01ef,
+}; /* CR_Variation_Selector */
+
+/* 'Pattern_White_Space': Binary Property */
+static const OnigCodePoint CR_Pattern_White_Space[] = {
+ 5,
+ 0x0009, 0x000d,
+ 0x0020, 0x0020,
+ 0x0085, 0x0085,
+ 0x200e, 0x200f,
+ 0x2028, 0x2029,
+}; /* CR_Pattern_White_Space */
+
+/* 'Pattern_Syntax': Binary Property */
+static const OnigCodePoint CR_Pattern_Syntax[] = {
+ 28,
+ 0x0021, 0x002f,
+ 0x003a, 0x0040,
+ 0x005b, 0x005e,
+ 0x0060, 0x0060,
+ 0x007b, 0x007e,
+ 0x00a1, 0x00a7,
+ 0x00a9, 0x00a9,
+ 0x00ab, 0x00ac,
+ 0x00ae, 0x00ae,
+ 0x00b0, 0x00b1,
+ 0x00b6, 0x00b6,
+ 0x00bb, 0x00bb,
+ 0x00bf, 0x00bf,
+ 0x00d7, 0x00d7,
+ 0x00f7, 0x00f7,
+ 0x2010, 0x2027,
+ 0x2030, 0x203e,
+ 0x2041, 0x2053,
+ 0x2055, 0x205e,
+ 0x2190, 0x245f,
+ 0x2500, 0x2775,
+ 0x2794, 0x2bff,
+ 0x2e00, 0x2e7f,
+ 0x3001, 0x3003,
+ 0x3008, 0x3020,
+ 0x3030, 0x3030,
+ 0xfd3e, 0xfd3f,
+ 0xfe45, 0xfe46,
+}; /* CR_Pattern_Syntax */
+
+/* 'Prepended_Concatenation_Mark': Binary Property */
+static const OnigCodePoint CR_Prepended_Concatenation_Mark[] = {
+ 6,
+ 0x0600, 0x0605,
+ 0x06dd, 0x06dd,
+ 0x070f, 0x070f,
+ 0x08e2, 0x08e2,
+ 0x110bd, 0x110bd,
+ 0x110cd, 0x110cd,
+}; /* CR_Prepended_Concatenation_Mark */
+
+/* 'Regional_Indicator': Binary Property */
+static const OnigCodePoint CR_Regional_Indicator[] = {
+ 1,
+ 0x1f1e6, 0x1f1ff,
+}; /* CR_Regional_Indicator */
+
+/* 'Emoji': Emoji */
+static const OnigCodePoint CR_Emoji[] = {
+ 151,
+ 0x0023, 0x0023,
+ 0x002a, 0x002a,
+ 0x0030, 0x0039,
+ 0x00a9, 0x00a9,
+ 0x00ae, 0x00ae,
+ 0x203c, 0x203c,
+ 0x2049, 0x2049,
+ 0x2122, 0x2122,
+ 0x2139, 0x2139,
+ 0x2194, 0x2199,
+ 0x21a9, 0x21aa,
+ 0x231a, 0x231b,
+ 0x2328, 0x2328,
+ 0x23cf, 0x23cf,
+ 0x23e9, 0x23f3,
+ 0x23f8, 0x23fa,
+ 0x24c2, 0x24c2,
+ 0x25aa, 0x25ab,
+ 0x25b6, 0x25b6,
+ 0x25c0, 0x25c0,
+ 0x25fb, 0x25fe,
+ 0x2600, 0x2604,
+ 0x260e, 0x260e,
+ 0x2611, 0x2611,
+ 0x2614, 0x2615,
+ 0x2618, 0x2618,
+ 0x261d, 0x261d,
+ 0x2620, 0x2620,
+ 0x2622, 0x2623,
+ 0x2626, 0x2626,
+ 0x262a, 0x262a,
+ 0x262e, 0x262f,
+ 0x2638, 0x263a,
+ 0x2640, 0x2640,
+ 0x2642, 0x2642,
+ 0x2648, 0x2653,
+ 0x265f, 0x2660,
+ 0x2663, 0x2663,
+ 0x2665, 0x2666,
+ 0x2668, 0x2668,
+ 0x267b, 0x267b,
+ 0x267e, 0x267f,
+ 0x2692, 0x2697,
+ 0x2699, 0x2699,
+ 0x269b, 0x269c,
+ 0x26a0, 0x26a1,
+ 0x26aa, 0x26ab,
+ 0x26b0, 0x26b1,
+ 0x26bd, 0x26be,
+ 0x26c4, 0x26c5,
+ 0x26c8, 0x26c8,
+ 0x26ce, 0x26cf,
+ 0x26d1, 0x26d1,
+ 0x26d3, 0x26d4,
+ 0x26e9, 0x26ea,
+ 0x26f0, 0x26f5,
+ 0x26f7, 0x26fa,
+ 0x26fd, 0x26fd,
+ 0x2702, 0x2702,
+ 0x2705, 0x2705,
+ 0x2708, 0x270d,
+ 0x270f, 0x270f,
+ 0x2712, 0x2712,
+ 0x2714, 0x2714,
+ 0x2716, 0x2716,
+ 0x271d, 0x271d,
+ 0x2721, 0x2721,
+ 0x2728, 0x2728,
+ 0x2733, 0x2734,
+ 0x2744, 0x2744,
+ 0x2747, 0x2747,
+ 0x274c, 0x274c,
+ 0x274e, 0x274e,
+ 0x2753, 0x2755,
+ 0x2757, 0x2757,
+ 0x2763, 0x2764,
+ 0x2795, 0x2797,
+ 0x27a1, 0x27a1,
+ 0x27b0, 0x27b0,
+ 0x27bf, 0x27bf,
+ 0x2934, 0x2935,
+ 0x2b05, 0x2b07,
+ 0x2b1b, 0x2b1c,
+ 0x2b50, 0x2b50,
+ 0x2b55, 0x2b55,
+ 0x3030, 0x3030,
+ 0x303d, 0x303d,
+ 0x3297, 0x3297,
+ 0x3299, 0x3299,
+ 0x1f004, 0x1f004,
+ 0x1f0cf, 0x1f0cf,
+ 0x1f170, 0x1f171,
+ 0x1f17e, 0x1f17f,
+ 0x1f18e, 0x1f18e,
+ 0x1f191, 0x1f19a,
+ 0x1f1e6, 0x1f1ff,
+ 0x1f201, 0x1f202,
+ 0x1f21a, 0x1f21a,
+ 0x1f22f, 0x1f22f,
+ 0x1f232, 0x1f23a,
+ 0x1f250, 0x1f251,
+ 0x1f300, 0x1f321,
+ 0x1f324, 0x1f393,
+ 0x1f396, 0x1f397,
+ 0x1f399, 0x1f39b,
+ 0x1f39e, 0x1f3f0,
+ 0x1f3f3, 0x1f3f5,
+ 0x1f3f7, 0x1f4fd,
+ 0x1f4ff, 0x1f53d,
+ 0x1f549, 0x1f54e,
+ 0x1f550, 0x1f567,
+ 0x1f56f, 0x1f570,
+ 0x1f573, 0x1f57a,
+ 0x1f587, 0x1f587,
+ 0x1f58a, 0x1f58d,
+ 0x1f590, 0x1f590,
+ 0x1f595, 0x1f596,
+ 0x1f5a4, 0x1f5a5,
+ 0x1f5a8, 0x1f5a8,
+ 0x1f5b1, 0x1f5b2,
+ 0x1f5bc, 0x1f5bc,
+ 0x1f5c2, 0x1f5c4,
+ 0x1f5d1, 0x1f5d3,
+ 0x1f5dc, 0x1f5de,
+ 0x1f5e1, 0x1f5e1,
+ 0x1f5e3, 0x1f5e3,
+ 0x1f5e8, 0x1f5e8,
+ 0x1f5ef, 0x1f5ef,
+ 0x1f5f3, 0x1f5f3,
+ 0x1f5fa, 0x1f64f,
+ 0x1f680, 0x1f6c5,
+ 0x1f6cb, 0x1f6d2,
+ 0x1f6d5, 0x1f6d5,
+ 0x1f6e0, 0x1f6e5,
+ 0x1f6e9, 0x1f6e9,
+ 0x1f6eb, 0x1f6ec,
+ 0x1f6f0, 0x1f6f0,
+ 0x1f6f3, 0x1f6fa,
+ 0x1f7e0, 0x1f7eb,
+ 0x1f90d, 0x1f93a,
+ 0x1f93c, 0x1f945,
+ 0x1f947, 0x1f971,
+ 0x1f973, 0x1f976,
+ 0x1f97a, 0x1f9a2,
+ 0x1f9a5, 0x1f9aa,
+ 0x1f9ae, 0x1f9ca,
+ 0x1f9cd, 0x1f9ff,
+ 0x1fa70, 0x1fa73,
+ 0x1fa78, 0x1fa7a,
+ 0x1fa80, 0x1fa82,
+ 0x1fa90, 0x1fa95,
+}; /* CR_Emoji */
+
+/* 'Emoji_Presentation': Emoji */
+static const OnigCodePoint CR_Emoji_Presentation[] = {
+ 81,
+ 0x231a, 0x231b,
+ 0x23e9, 0x23ec,
+ 0x23f0, 0x23f0,
+ 0x23f3, 0x23f3,
+ 0x25fd, 0x25fe,
+ 0x2614, 0x2615,
+ 0x2648, 0x2653,
+ 0x267f, 0x267f,
+ 0x2693, 0x2693,
+ 0x26a1, 0x26a1,
+ 0x26aa, 0x26ab,
+ 0x26bd, 0x26be,
+ 0x26c4, 0x26c5,
+ 0x26ce, 0x26ce,
+ 0x26d4, 0x26d4,
+ 0x26ea, 0x26ea,
+ 0x26f2, 0x26f3,
+ 0x26f5, 0x26f5,
+ 0x26fa, 0x26fa,
+ 0x26fd, 0x26fd,
+ 0x2705, 0x2705,
+ 0x270a, 0x270b,
+ 0x2728, 0x2728,
+ 0x274c, 0x274c,
+ 0x274e, 0x274e,
+ 0x2753, 0x2755,
+ 0x2757, 0x2757,
+ 0x2795, 0x2797,
+ 0x27b0, 0x27b0,
+ 0x27bf, 0x27bf,
+ 0x2b1b, 0x2b1c,
+ 0x2b50, 0x2b50,
+ 0x2b55, 0x2b55,
+ 0x1f004, 0x1f004,
+ 0x1f0cf, 0x1f0cf,
+ 0x1f18e, 0x1f18e,
+ 0x1f191, 0x1f19a,
+ 0x1f1e6, 0x1f1ff,
+ 0x1f201, 0x1f201,
+ 0x1f21a, 0x1f21a,
+ 0x1f22f, 0x1f22f,
+ 0x1f232, 0x1f236,
+ 0x1f238, 0x1f23a,
+ 0x1f250, 0x1f251,
+ 0x1f300, 0x1f320,
+ 0x1f32d, 0x1f335,
+ 0x1f337, 0x1f37c,
+ 0x1f37e, 0x1f393,
+ 0x1f3a0, 0x1f3ca,
+ 0x1f3cf, 0x1f3d3,
+ 0x1f3e0, 0x1f3f0,
+ 0x1f3f4, 0x1f3f4,
+ 0x1f3f8, 0x1f43e,
+ 0x1f440, 0x1f440,
+ 0x1f442, 0x1f4fc,
+ 0x1f4ff, 0x1f53d,
+ 0x1f54b, 0x1f54e,
+ 0x1f550, 0x1f567,
+ 0x1f57a, 0x1f57a,
+ 0x1f595, 0x1f596,
+ 0x1f5a4, 0x1f5a4,
+ 0x1f5fb, 0x1f64f,
+ 0x1f680, 0x1f6c5,
+ 0x1f6cc, 0x1f6cc,
+ 0x1f6d0, 0x1f6d2,
+ 0x1f6d5, 0x1f6d5,
+ 0x1f6eb, 0x1f6ec,
+ 0x1f6f4, 0x1f6fa,
+ 0x1f7e0, 0x1f7eb,
+ 0x1f90d, 0x1f93a,
+ 0x1f93c, 0x1f945,
+ 0x1f947, 0x1f971,
+ 0x1f973, 0x1f976,
+ 0x1f97a, 0x1f9a2,
+ 0x1f9a5, 0x1f9aa,
+ 0x1f9ae, 0x1f9ca,
+ 0x1f9cd, 0x1f9ff,
+ 0x1fa70, 0x1fa73,
+ 0x1fa78, 0x1fa7a,
+ 0x1fa80, 0x1fa82,
+ 0x1fa90, 0x1fa95,
+}; /* CR_Emoji_Presentation */
+
+/* 'Emoji_Modifier': Emoji */
+static const OnigCodePoint CR_Emoji_Modifier[] = {
+ 1,
+ 0x1f3fb, 0x1f3ff,
+}; /* CR_Emoji_Modifier */
+
+/* 'Emoji_Modifier_Base': Emoji */
+static const OnigCodePoint CR_Emoji_Modifier_Base[] = {
+ 36,
+ 0x261d, 0x261d,
+ 0x26f9, 0x26f9,
+ 0x270a, 0x270d,
+ 0x1f385, 0x1f385,
+ 0x1f3c2, 0x1f3c4,
+ 0x1f3c7, 0x1f3c7,
+ 0x1f3ca, 0x1f3cc,
+ 0x1f442, 0x1f443,
+ 0x1f446, 0x1f450,
+ 0x1f466, 0x1f478,
+ 0x1f47c, 0x1f47c,
+ 0x1f481, 0x1f483,
+ 0x1f485, 0x1f487,
+ 0x1f48f, 0x1f48f,
+ 0x1f491, 0x1f491,
+ 0x1f4aa, 0x1f4aa,
+ 0x1f574, 0x1f575,
+ 0x1f57a, 0x1f57a,
+ 0x1f590, 0x1f590,
+ 0x1f595, 0x1f596,
+ 0x1f645, 0x1f647,
+ 0x1f64b, 0x1f64f,
+ 0x1f6a3, 0x1f6a3,
+ 0x1f6b4, 0x1f6b6,
+ 0x1f6c0, 0x1f6c0,
+ 0x1f6cc, 0x1f6cc,
+ 0x1f90f, 0x1f90f,
+ 0x1f918, 0x1f91f,
+ 0x1f926, 0x1f926,
+ 0x1f930, 0x1f939,
+ 0x1f93c, 0x1f93e,
+ 0x1f9b5, 0x1f9b6,
+ 0x1f9b8, 0x1f9b9,
+ 0x1f9bb, 0x1f9bb,
+ 0x1f9cd, 0x1f9cf,
+ 0x1f9d1, 0x1f9dd,
+}; /* CR_Emoji_Modifier_Base */
+
+/* 'Emoji_Component': Emoji */
+static const OnigCodePoint CR_Emoji_Component[] = {
+ 10,
+ 0x0023, 0x0023,
+ 0x002a, 0x002a,
+ 0x0030, 0x0039,
+ 0x200d, 0x200d,
+ 0x20e3, 0x20e3,
+ 0xfe0f, 0xfe0f,
+ 0x1f1e6, 0x1f1ff,
+ 0x1f3fb, 0x1f3ff,
+ 0x1f9b0, 0x1f9b3,
+ 0xe0020, 0xe007f,
+}; /* CR_Emoji_Component */
+
+/* 'Extended_Pictographic': Emoji */
+static const OnigCodePoint CR_Extended_Pictographic[] = {
+ 77,
+ 0x00a9, 0x00a9,
+ 0x00ae, 0x00ae,
+ 0x203c, 0x203c,
+ 0x2049, 0x2049,
+ 0x2122, 0x2122,
+ 0x2139, 0x2139,
+ 0x2194, 0x2199,
+ 0x21a9, 0x21aa,
+ 0x231a, 0x231b,
+ 0x2328, 0x2328,
+ 0x2388, 0x2388,
+ 0x23cf, 0x23cf,
+ 0x23e9, 0x23f3,
+ 0x23f8, 0x23fa,
+ 0x24c2, 0x24c2,
+ 0x25aa, 0x25ab,
+ 0x25b6, 0x25b6,
+ 0x25c0, 0x25c0,
+ 0x25fb, 0x25fe,
+ 0x2600, 0x2605,
+ 0x2607, 0x2612,
+ 0x2614, 0x2685,
+ 0x2690, 0x2705,
+ 0x2708, 0x2712,
+ 0x2714, 0x2714,
+ 0x2716, 0x2716,
+ 0x271d, 0x271d,
+ 0x2721, 0x2721,
+ 0x2728, 0x2728,
+ 0x2733, 0x2734,
+ 0x2744, 0x2744,
+ 0x2747, 0x2747,
+ 0x274c, 0x274c,
+ 0x274e, 0x274e,
+ 0x2753, 0x2755,
+ 0x2757, 0x2757,
+ 0x2763, 0x2767,
+ 0x2795, 0x2797,
+ 0x27a1, 0x27a1,
+ 0x27b0, 0x27b0,
+ 0x27bf, 0x27bf,
+ 0x2934, 0x2935,
+ 0x2b05, 0x2b07,
+ 0x2b1b, 0x2b1c,
+ 0x2b50, 0x2b50,
+ 0x2b55, 0x2b55,
+ 0x3030, 0x3030,
+ 0x303d, 0x303d,
+ 0x3297, 0x3297,
+ 0x3299, 0x3299,
+ 0x1f000, 0x1f0ff,
+ 0x1f10d, 0x1f10f,
+ 0x1f12f, 0x1f12f,
+ 0x1f16c, 0x1f171,
+ 0x1f17e, 0x1f17f,
+ 0x1f18e, 0x1f18e,
+ 0x1f191, 0x1f19a,
+ 0x1f1ad, 0x1f1e5,
+ 0x1f201, 0x1f20f,
+ 0x1f21a, 0x1f21a,
+ 0x1f22f, 0x1f22f,
+ 0x1f232, 0x1f23a,
+ 0x1f23c, 0x1f23f,
+ 0x1f249, 0x1f3fa,
+ 0x1f400, 0x1f53d,
+ 0x1f546, 0x1f64f,
+ 0x1f680, 0x1f6ff,
+ 0x1f774, 0x1f77f,
+ 0x1f7d5, 0x1f7ff,
+ 0x1f80c, 0x1f80f,
+ 0x1f848, 0x1f84f,
+ 0x1f85a, 0x1f85f,
+ 0x1f888, 0x1f88f,
+ 0x1f8ae, 0x1f8ff,
+ 0x1f90c, 0x1f93a,
+ 0x1f93c, 0x1f945,
+ 0x1f947, 0x1fffd,
+}; /* CR_Extended_Pictographic */
+
+/* 'Unknown': Script */
+static const OnigCodePoint CR_Unknown[] = {
+ 664,
+ 0x0378, 0x0379,
+ 0x0380, 0x0383,
+ 0x038b, 0x038b,
+ 0x038d, 0x038d,
+ 0x03a2, 0x03a2,
+ 0x0530, 0x0530,
+ 0x0557, 0x0558,
+ 0x058b, 0x058c,
+ 0x0590, 0x0590,
+ 0x05c8, 0x05cf,
+ 0x05eb, 0x05ee,
+ 0x05f5, 0x05ff,
+ 0x061d, 0x061d,
+ 0x070e, 0x070e,
+ 0x074b, 0x074c,
+ 0x07b2, 0x07bf,
+ 0x07fb, 0x07fc,
+ 0x082e, 0x082f,
+ 0x083f, 0x083f,
+ 0x085c, 0x085d,
+ 0x085f, 0x085f,
+ 0x086b, 0x089f,
+ 0x08b5, 0x08b5,
+ 0x08be, 0x08d2,
+ 0x0984, 0x0984,
+ 0x098d, 0x098e,
+ 0x0991, 0x0992,
+ 0x09a9, 0x09a9,
+ 0x09b1, 0x09b1,
+ 0x09b3, 0x09b5,
+ 0x09ba, 0x09bb,
+ 0x09c5, 0x09c6,
+ 0x09c9, 0x09ca,
+ 0x09cf, 0x09d6,
+ 0x09d8, 0x09db,
+ 0x09de, 0x09de,
+ 0x09e4, 0x09e5,
+ 0x09ff, 0x0a00,
+ 0x0a04, 0x0a04,
+ 0x0a0b, 0x0a0e,
+ 0x0a11, 0x0a12,
+ 0x0a29, 0x0a29,
+ 0x0a31, 0x0a31,
+ 0x0a34, 0x0a34,
+ 0x0a37, 0x0a37,
+ 0x0a3a, 0x0a3b,
+ 0x0a3d, 0x0a3d,
+ 0x0a43, 0x0a46,
+ 0x0a49, 0x0a4a,
+ 0x0a4e, 0x0a50,
+ 0x0a52, 0x0a58,
+ 0x0a5d, 0x0a5d,
+ 0x0a5f, 0x0a65,
+ 0x0a77, 0x0a80,
+ 0x0a84, 0x0a84,
+ 0x0a8e, 0x0a8e,
+ 0x0a92, 0x0a92,
+ 0x0aa9, 0x0aa9,
+ 0x0ab1, 0x0ab1,
+ 0x0ab4, 0x0ab4,
+ 0x0aba, 0x0abb,
+ 0x0ac6, 0x0ac6,
+ 0x0aca, 0x0aca,
+ 0x0ace, 0x0acf,
+ 0x0ad1, 0x0adf,
+ 0x0ae4, 0x0ae5,
+ 0x0af2, 0x0af8,
+ 0x0b00, 0x0b00,
+ 0x0b04, 0x0b04,
+ 0x0b0d, 0x0b0e,
+ 0x0b11, 0x0b12,
+ 0x0b29, 0x0b29,
+ 0x0b31, 0x0b31,
+ 0x0b34, 0x0b34,
+ 0x0b3a, 0x0b3b,
+ 0x0b45, 0x0b46,
+ 0x0b49, 0x0b4a,
+ 0x0b4e, 0x0b55,
+ 0x0b58, 0x0b5b,
+ 0x0b5e, 0x0b5e,
+ 0x0b64, 0x0b65,
+ 0x0b78, 0x0b81,
+ 0x0b84, 0x0b84,
+ 0x0b8b, 0x0b8d,
+ 0x0b91, 0x0b91,
+ 0x0b96, 0x0b98,
+ 0x0b9b, 0x0b9b,
+ 0x0b9d, 0x0b9d,
+ 0x0ba0, 0x0ba2,
+ 0x0ba5, 0x0ba7,
+ 0x0bab, 0x0bad,
+ 0x0bba, 0x0bbd,
+ 0x0bc3, 0x0bc5,
+ 0x0bc9, 0x0bc9,
+ 0x0bce, 0x0bcf,
+ 0x0bd1, 0x0bd6,
+ 0x0bd8, 0x0be5,
+ 0x0bfb, 0x0bff,
+ 0x0c0d, 0x0c0d,
+ 0x0c11, 0x0c11,
+ 0x0c29, 0x0c29,
+ 0x0c3a, 0x0c3c,
+ 0x0c45, 0x0c45,
+ 0x0c49, 0x0c49,
+ 0x0c4e, 0x0c54,
+ 0x0c57, 0x0c57,
+ 0x0c5b, 0x0c5f,
+ 0x0c64, 0x0c65,
+ 0x0c70, 0x0c76,
+ 0x0c8d, 0x0c8d,
+ 0x0c91, 0x0c91,
+ 0x0ca9, 0x0ca9,
+ 0x0cb4, 0x0cb4,
+ 0x0cba, 0x0cbb,
+ 0x0cc5, 0x0cc5,
+ 0x0cc9, 0x0cc9,
+ 0x0cce, 0x0cd4,
+ 0x0cd7, 0x0cdd,
+ 0x0cdf, 0x0cdf,
+ 0x0ce4, 0x0ce5,
+ 0x0cf0, 0x0cf0,
+ 0x0cf3, 0x0cff,
+ 0x0d04, 0x0d04,
+ 0x0d0d, 0x0d0d,
+ 0x0d11, 0x0d11,
+ 0x0d45, 0x0d45,
+ 0x0d49, 0x0d49,
+ 0x0d50, 0x0d53,
+ 0x0d64, 0x0d65,
+ 0x0d80, 0x0d81,
+ 0x0d84, 0x0d84,
+ 0x0d97, 0x0d99,
+ 0x0db2, 0x0db2,
+ 0x0dbc, 0x0dbc,
+ 0x0dbe, 0x0dbf,
+ 0x0dc7, 0x0dc9,
+ 0x0dcb, 0x0dce,
+ 0x0dd5, 0x0dd5,
+ 0x0dd7, 0x0dd7,
+ 0x0de0, 0x0de5,
+ 0x0df0, 0x0df1,
+ 0x0df5, 0x0e00,
+ 0x0e3b, 0x0e3e,
+ 0x0e5c, 0x0e80,
+ 0x0e83, 0x0e83,
+ 0x0e85, 0x0e85,
+ 0x0e8b, 0x0e8b,
+ 0x0ea4, 0x0ea4,
+ 0x0ea6, 0x0ea6,
+ 0x0ebe, 0x0ebf,
+ 0x0ec5, 0x0ec5,
+ 0x0ec7, 0x0ec7,
+ 0x0ece, 0x0ecf,
+ 0x0eda, 0x0edb,
+ 0x0ee0, 0x0eff,
+ 0x0f48, 0x0f48,
+ 0x0f6d, 0x0f70,
+ 0x0f98, 0x0f98,
+ 0x0fbd, 0x0fbd,
+ 0x0fcd, 0x0fcd,
+ 0x0fdb, 0x0fff,
+ 0x10c6, 0x10c6,
+ 0x10c8, 0x10cc,
+ 0x10ce, 0x10cf,
+ 0x1249, 0x1249,
+ 0x124e, 0x124f,
+ 0x1257, 0x1257,
+ 0x1259, 0x1259,
+ 0x125e, 0x125f,
+ 0x1289, 0x1289,
+ 0x128e, 0x128f,
+ 0x12b1, 0x12b1,
+ 0x12b6, 0x12b7,
+ 0x12bf, 0x12bf,
+ 0x12c1, 0x12c1,
+ 0x12c6, 0x12c7,
+ 0x12d7, 0x12d7,
+ 0x1311, 0x1311,
+ 0x1316, 0x1317,
+ 0x135b, 0x135c,
+ 0x137d, 0x137f,
+ 0x139a, 0x139f,
+ 0x13f6, 0x13f7,
+ 0x13fe, 0x13ff,
+ 0x169d, 0x169f,
+ 0x16f9, 0x16ff,
+ 0x170d, 0x170d,
+ 0x1715, 0x171f,
+ 0x1737, 0x173f,
+ 0x1754, 0x175f,
+ 0x176d, 0x176d,
+ 0x1771, 0x1771,
+ 0x1774, 0x177f,
+ 0x17de, 0x17df,
+ 0x17ea, 0x17ef,
+ 0x17fa, 0x17ff,
+ 0x180f, 0x180f,
+ 0x181a, 0x181f,
+ 0x1879, 0x187f,
+ 0x18ab, 0x18af,
+ 0x18f6, 0x18ff,
+ 0x191f, 0x191f,
+ 0x192c, 0x192f,
+ 0x193c, 0x193f,
+ 0x1941, 0x1943,
+ 0x196e, 0x196f,
+ 0x1975, 0x197f,
+ 0x19ac, 0x19af,
+ 0x19ca, 0x19cf,
+ 0x19db, 0x19dd,
+ 0x1a1c, 0x1a1d,
+ 0x1a5f, 0x1a5f,
+ 0x1a7d, 0x1a7e,
+ 0x1a8a, 0x1a8f,
+ 0x1a9a, 0x1a9f,
+ 0x1aae, 0x1aaf,
+ 0x1abf, 0x1aff,
+ 0x1b4c, 0x1b4f,
+ 0x1b7d, 0x1b7f,
+ 0x1bf4, 0x1bfb,
+ 0x1c38, 0x1c3a,
+ 0x1c4a, 0x1c4c,
+ 0x1c89, 0x1c8f,
+ 0x1cbb, 0x1cbc,
+ 0x1cc8, 0x1ccf,
+ 0x1cfb, 0x1cff,
+ 0x1dfa, 0x1dfa,
+ 0x1f16, 0x1f17,
+ 0x1f1e, 0x1f1f,
+ 0x1f46, 0x1f47,
+ 0x1f4e, 0x1f4f,
+ 0x1f58, 0x1f58,
+ 0x1f5a, 0x1f5a,
+ 0x1f5c, 0x1f5c,
+ 0x1f5e, 0x1f5e,
+ 0x1f7e, 0x1f7f,
+ 0x1fb5, 0x1fb5,
+ 0x1fc5, 0x1fc5,
+ 0x1fd4, 0x1fd5,
+ 0x1fdc, 0x1fdc,
+ 0x1ff0, 0x1ff1,
+ 0x1ff5, 0x1ff5,
+ 0x1fff, 0x1fff,
+ 0x2065, 0x2065,
+ 0x2072, 0x2073,
+ 0x208f, 0x208f,
+ 0x209d, 0x209f,
+ 0x20c0, 0x20cf,
+ 0x20f1, 0x20ff,
+ 0x218c, 0x218f,
+ 0x2427, 0x243f,
+ 0x244b, 0x245f,
+ 0x2b74, 0x2b75,
+ 0x2b96, 0x2b97,
+ 0x2c2f, 0x2c2f,
+ 0x2c5f, 0x2c5f,
+ 0x2cf4, 0x2cf8,
+ 0x2d26, 0x2d26,
+ 0x2d28, 0x2d2c,
+ 0x2d2e, 0x2d2f,
+ 0x2d68, 0x2d6e,
+ 0x2d71, 0x2d7e,
+ 0x2d97, 0x2d9f,
+ 0x2da7, 0x2da7,
+ 0x2daf, 0x2daf,
+ 0x2db7, 0x2db7,
+ 0x2dbf, 0x2dbf,
+ 0x2dc7, 0x2dc7,
+ 0x2dcf, 0x2dcf,
+ 0x2dd7, 0x2dd7,
+ 0x2ddf, 0x2ddf,
+ 0x2e50, 0x2e7f,
+ 0x2e9a, 0x2e9a,
+ 0x2ef4, 0x2eff,
+ 0x2fd6, 0x2fef,
+ 0x2ffc, 0x2fff,
+ 0x3040, 0x3040,
+ 0x3097, 0x3098,
+ 0x3100, 0x3104,
+ 0x3130, 0x3130,
+ 0x318f, 0x318f,
+ 0x31bb, 0x31bf,
+ 0x31e4, 0x31ef,
+ 0x321f, 0x321f,
+ 0x4db6, 0x4dbf,
+ 0x9ff0, 0x9fff,
+ 0xa48d, 0xa48f,
+ 0xa4c7, 0xa4cf,
+ 0xa62c, 0xa63f,
+ 0xa6f8, 0xa6ff,
+ 0xa7c0, 0xa7c1,
+ 0xa7c7, 0xa7f6,
+ 0xa82c, 0xa82f,
+ 0xa83a, 0xa83f,
+ 0xa878, 0xa87f,
+ 0xa8c6, 0xa8cd,
+ 0xa8da, 0xa8df,
+ 0xa954, 0xa95e,
+ 0xa97d, 0xa97f,
+ 0xa9ce, 0xa9ce,
+ 0xa9da, 0xa9dd,
+ 0xa9ff, 0xa9ff,
+ 0xaa37, 0xaa3f,
+ 0xaa4e, 0xaa4f,
+ 0xaa5a, 0xaa5b,
+ 0xaac3, 0xaada,
+ 0xaaf7, 0xab00,
+ 0xab07, 0xab08,
+ 0xab0f, 0xab10,
+ 0xab17, 0xab1f,
+ 0xab27, 0xab27,
+ 0xab2f, 0xab2f,
+ 0xab68, 0xab6f,
+ 0xabee, 0xabef,
+ 0xabfa, 0xabff,
+ 0xd7a4, 0xd7af,
+ 0xd7c7, 0xd7ca,
+ 0xd7fc, 0xf8ff,
+ 0xfa6e, 0xfa6f,
+ 0xfada, 0xfaff,
+ 0xfb07, 0xfb12,
+ 0xfb18, 0xfb1c,
+ 0xfb37, 0xfb37,
+ 0xfb3d, 0xfb3d,
+ 0xfb3f, 0xfb3f,
+ 0xfb42, 0xfb42,
+ 0xfb45, 0xfb45,
+ 0xfbc2, 0xfbd2,
+ 0xfd40, 0xfd4f,
+ 0xfd90, 0xfd91,
+ 0xfdc8, 0xfdef,
+ 0xfdfe, 0xfdff,
+ 0xfe1a, 0xfe1f,
+ 0xfe53, 0xfe53,
+ 0xfe67, 0xfe67,
+ 0xfe6c, 0xfe6f,
+ 0xfe75, 0xfe75,
+ 0xfefd, 0xfefe,
+ 0xff00, 0xff00,
+ 0xffbf, 0xffc1,
+ 0xffc8, 0xffc9,
+ 0xffd0, 0xffd1,
+ 0xffd8, 0xffd9,
+ 0xffdd, 0xffdf,
+ 0xffe7, 0xffe7,
+ 0xffef, 0xfff8,
+ 0xfffe, 0xffff,
+ 0x1000c, 0x1000c,
+ 0x10027, 0x10027,
+ 0x1003b, 0x1003b,
+ 0x1003e, 0x1003e,
+ 0x1004e, 0x1004f,
+ 0x1005e, 0x1007f,
+ 0x100fb, 0x100ff,
+ 0x10103, 0x10106,
+ 0x10134, 0x10136,
+ 0x1018f, 0x1018f,
+ 0x1019c, 0x1019f,
+ 0x101a1, 0x101cf,
+ 0x101fe, 0x1027f,
+ 0x1029d, 0x1029f,
+ 0x102d1, 0x102df,
+ 0x102fc, 0x102ff,
+ 0x10324, 0x1032c,
+ 0x1034b, 0x1034f,
+ 0x1037b, 0x1037f,
+ 0x1039e, 0x1039e,
+ 0x103c4, 0x103c7,
+ 0x103d6, 0x103ff,
+ 0x1049e, 0x1049f,
+ 0x104aa, 0x104af,
+ 0x104d4, 0x104d7,
+ 0x104fc, 0x104ff,
+ 0x10528, 0x1052f,
+ 0x10564, 0x1056e,
+ 0x10570, 0x105ff,
+ 0x10737, 0x1073f,
+ 0x10756, 0x1075f,
+ 0x10768, 0x107ff,
+ 0x10806, 0x10807,
+ 0x10809, 0x10809,
+ 0x10836, 0x10836,
+ 0x10839, 0x1083b,
+ 0x1083d, 0x1083e,
+ 0x10856, 0x10856,
+ 0x1089f, 0x108a6,
+ 0x108b0, 0x108df,
+ 0x108f3, 0x108f3,
+ 0x108f6, 0x108fa,
+ 0x1091c, 0x1091e,
+ 0x1093a, 0x1093e,
+ 0x10940, 0x1097f,
+ 0x109b8, 0x109bb,
+ 0x109d0, 0x109d1,
+ 0x10a04, 0x10a04,
+ 0x10a07, 0x10a0b,
+ 0x10a14, 0x10a14,
+ 0x10a18, 0x10a18,
+ 0x10a36, 0x10a37,
+ 0x10a3b, 0x10a3e,
+ 0x10a49, 0x10a4f,
+ 0x10a59, 0x10a5f,
+ 0x10aa0, 0x10abf,
+ 0x10ae7, 0x10aea,
+ 0x10af7, 0x10aff,
+ 0x10b36, 0x10b38,
+ 0x10b56, 0x10b57,
+ 0x10b73, 0x10b77,
+ 0x10b92, 0x10b98,
+ 0x10b9d, 0x10ba8,
+ 0x10bb0, 0x10bff,
+ 0x10c49, 0x10c7f,
+ 0x10cb3, 0x10cbf,
+ 0x10cf3, 0x10cf9,
+ 0x10d28, 0x10d2f,
+ 0x10d3a, 0x10e5f,
+ 0x10e7f, 0x10eff,
+ 0x10f28, 0x10f2f,
+ 0x10f5a, 0x10fdf,
+ 0x10ff7, 0x10fff,
+ 0x1104e, 0x11051,
+ 0x11070, 0x1107e,
+ 0x110c2, 0x110cc,
+ 0x110ce, 0x110cf,
+ 0x110e9, 0x110ef,
+ 0x110fa, 0x110ff,
+ 0x11135, 0x11135,
+ 0x11147, 0x1114f,
+ 0x11177, 0x1117f,
+ 0x111ce, 0x111cf,
+ 0x111e0, 0x111e0,
+ 0x111f5, 0x111ff,
+ 0x11212, 0x11212,
+ 0x1123f, 0x1127f,
+ 0x11287, 0x11287,
+ 0x11289, 0x11289,
+ 0x1128e, 0x1128e,
+ 0x1129e, 0x1129e,
+ 0x112aa, 0x112af,
+ 0x112eb, 0x112ef,
+ 0x112fa, 0x112ff,
+ 0x11304, 0x11304,
+ 0x1130d, 0x1130e,
+ 0x11311, 0x11312,
+ 0x11329, 0x11329,
+ 0x11331, 0x11331,
+ 0x11334, 0x11334,
+ 0x1133a, 0x1133a,
+ 0x11345, 0x11346,
+ 0x11349, 0x1134a,
+ 0x1134e, 0x1134f,
+ 0x11351, 0x11356,
+ 0x11358, 0x1135c,
+ 0x11364, 0x11365,
+ 0x1136d, 0x1136f,
+ 0x11375, 0x113ff,
+ 0x1145a, 0x1145a,
+ 0x1145c, 0x1145c,
+ 0x11460, 0x1147f,
+ 0x114c8, 0x114cf,
+ 0x114da, 0x1157f,
+ 0x115b6, 0x115b7,
+ 0x115de, 0x115ff,
+ 0x11645, 0x1164f,
+ 0x1165a, 0x1165f,
+ 0x1166d, 0x1167f,
+ 0x116b9, 0x116bf,
+ 0x116ca, 0x116ff,
+ 0x1171b, 0x1171c,
+ 0x1172c, 0x1172f,
+ 0x11740, 0x117ff,
+ 0x1183c, 0x1189f,
+ 0x118f3, 0x118fe,
+ 0x11900, 0x1199f,
+ 0x119a8, 0x119a9,
+ 0x119d8, 0x119d9,
+ 0x119e5, 0x119ff,
+ 0x11a48, 0x11a4f,
+ 0x11aa3, 0x11abf,
+ 0x11af9, 0x11bff,
+ 0x11c09, 0x11c09,
+ 0x11c37, 0x11c37,
+ 0x11c46, 0x11c4f,
+ 0x11c6d, 0x11c6f,
+ 0x11c90, 0x11c91,
+ 0x11ca8, 0x11ca8,
+ 0x11cb7, 0x11cff,
+ 0x11d07, 0x11d07,
+ 0x11d0a, 0x11d0a,
+ 0x11d37, 0x11d39,
+ 0x11d3b, 0x11d3b,
+ 0x11d3e, 0x11d3e,
+ 0x11d48, 0x11d4f,
+ 0x11d5a, 0x11d5f,
+ 0x11d66, 0x11d66,
+ 0x11d69, 0x11d69,
+ 0x11d8f, 0x11d8f,
+ 0x11d92, 0x11d92,
+ 0x11d99, 0x11d9f,
+ 0x11daa, 0x11edf,
+ 0x11ef9, 0x11fbf,
+ 0x11ff2, 0x11ffe,
+ 0x1239a, 0x123ff,
+ 0x1246f, 0x1246f,
+ 0x12475, 0x1247f,
+ 0x12544, 0x12fff,
+ 0x1342f, 0x1342f,
+ 0x13439, 0x143ff,
+ 0x14647, 0x167ff,
+ 0x16a39, 0x16a3f,
+ 0x16a5f, 0x16a5f,
+ 0x16a6a, 0x16a6d,
+ 0x16a70, 0x16acf,
+ 0x16aee, 0x16aef,
+ 0x16af6, 0x16aff,
+ 0x16b46, 0x16b4f,
+ 0x16b5a, 0x16b5a,
+ 0x16b62, 0x16b62,
+ 0x16b78, 0x16b7c,
+ 0x16b90, 0x16e3f,
+ 0x16e9b, 0x16eff,
+ 0x16f4b, 0x16f4e,
+ 0x16f88, 0x16f8e,
+ 0x16fa0, 0x16fdf,
+ 0x16fe4, 0x16fff,
+ 0x187f8, 0x187ff,
+ 0x18af3, 0x1afff,
+ 0x1b11f, 0x1b14f,
+ 0x1b153, 0x1b163,
+ 0x1b168, 0x1b16f,
+ 0x1b2fc, 0x1bbff,
+ 0x1bc6b, 0x1bc6f,
+ 0x1bc7d, 0x1bc7f,
+ 0x1bc89, 0x1bc8f,
+ 0x1bc9a, 0x1bc9b,
+ 0x1bca4, 0x1cfff,
+ 0x1d0f6, 0x1d0ff,
+ 0x1d127, 0x1d128,
+ 0x1d1e9, 0x1d1ff,
+ 0x1d246, 0x1d2df,
+ 0x1d2f4, 0x1d2ff,
+ 0x1d357, 0x1d35f,
+ 0x1d379, 0x1d3ff,
+ 0x1d455, 0x1d455,
+ 0x1d49d, 0x1d49d,
+ 0x1d4a0, 0x1d4a1,
+ 0x1d4a3, 0x1d4a4,
+ 0x1d4a7, 0x1d4a8,
+ 0x1d4ad, 0x1d4ad,
+ 0x1d4ba, 0x1d4ba,
+ 0x1d4bc, 0x1d4bc,
+ 0x1d4c4, 0x1d4c4,
+ 0x1d506, 0x1d506,
+ 0x1d50b, 0x1d50c,
+ 0x1d515, 0x1d515,
+ 0x1d51d, 0x1d51d,
+ 0x1d53a, 0x1d53a,
+ 0x1d53f, 0x1d53f,
+ 0x1d545, 0x1d545,
+ 0x1d547, 0x1d549,
+ 0x1d551, 0x1d551,
+ 0x1d6a6, 0x1d6a7,
+ 0x1d7cc, 0x1d7cd,
+ 0x1da8c, 0x1da9a,
+ 0x1daa0, 0x1daa0,
+ 0x1dab0, 0x1dfff,
+ 0x1e007, 0x1e007,
+ 0x1e019, 0x1e01a,
+ 0x1e022, 0x1e022,
+ 0x1e025, 0x1e025,
+ 0x1e02b, 0x1e0ff,
+ 0x1e12d, 0x1e12f,
+ 0x1e13e, 0x1e13f,
+ 0x1e14a, 0x1e14d,
+ 0x1e150, 0x1e2bf,
+ 0x1e2fa, 0x1e2fe,
+ 0x1e300, 0x1e7ff,
+ 0x1e8c5, 0x1e8c6,
+ 0x1e8d7, 0x1e8ff,
+ 0x1e94c, 0x1e94f,
+ 0x1e95a, 0x1e95d,
+ 0x1e960, 0x1ec70,
+ 0x1ecb5, 0x1ed00,
+ 0x1ed3e, 0x1edff,
+ 0x1ee04, 0x1ee04,
+ 0x1ee20, 0x1ee20,
+ 0x1ee23, 0x1ee23,
+ 0x1ee25, 0x1ee26,
+ 0x1ee28, 0x1ee28,
+ 0x1ee33, 0x1ee33,
+ 0x1ee38, 0x1ee38,
+ 0x1ee3a, 0x1ee3a,
+ 0x1ee3c, 0x1ee41,
+ 0x1ee43, 0x1ee46,
+ 0x1ee48, 0x1ee48,
+ 0x1ee4a, 0x1ee4a,
+ 0x1ee4c, 0x1ee4c,
+ 0x1ee50, 0x1ee50,
+ 0x1ee53, 0x1ee53,
+ 0x1ee55, 0x1ee56,
+ 0x1ee58, 0x1ee58,
+ 0x1ee5a, 0x1ee5a,
+ 0x1ee5c, 0x1ee5c,
+ 0x1ee5e, 0x1ee5e,
+ 0x1ee60, 0x1ee60,
+ 0x1ee63, 0x1ee63,
+ 0x1ee65, 0x1ee66,
+ 0x1ee6b, 0x1ee6b,
+ 0x1ee73, 0x1ee73,
+ 0x1ee78, 0x1ee78,
+ 0x1ee7d, 0x1ee7d,
+ 0x1ee7f, 0x1ee7f,
+ 0x1ee8a, 0x1ee8a,
+ 0x1ee9c, 0x1eea0,
+ 0x1eea4, 0x1eea4,
+ 0x1eeaa, 0x1eeaa,
+ 0x1eebc, 0x1eeef,
+ 0x1eef2, 0x1efff,
+ 0x1f02c, 0x1f02f,
+ 0x1f094, 0x1f09f,
+ 0x1f0af, 0x1f0b0,
+ 0x1f0c0, 0x1f0c0,
+ 0x1f0d0, 0x1f0d0,
+ 0x1f0f6, 0x1f0ff,
+ 0x1f10d, 0x1f10f,
+ 0x1f16d, 0x1f16f,
+ 0x1f1ad, 0x1f1e5,
+ 0x1f203, 0x1f20f,
+ 0x1f23c, 0x1f23f,
+ 0x1f249, 0x1f24f,
+ 0x1f252, 0x1f25f,
+ 0x1f266, 0x1f2ff,
+ 0x1f6d6, 0x1f6df,
+ 0x1f6ed, 0x1f6ef,
+ 0x1f6fb, 0x1f6ff,
+ 0x1f774, 0x1f77f,
+ 0x1f7d9, 0x1f7df,
+ 0x1f7ec, 0x1f7ff,
+ 0x1f80c, 0x1f80f,
+ 0x1f848, 0x1f84f,
+ 0x1f85a, 0x1f85f,
+ 0x1f888, 0x1f88f,
+ 0x1f8ae, 0x1f8ff,
+ 0x1f90c, 0x1f90c,
+ 0x1f972, 0x1f972,
+ 0x1f977, 0x1f979,
+ 0x1f9a3, 0x1f9a4,
+ 0x1f9ab, 0x1f9ad,
+ 0x1f9cb, 0x1f9cc,
+ 0x1fa54, 0x1fa5f,
+ 0x1fa6e, 0x1fa6f,
+ 0x1fa74, 0x1fa77,
+ 0x1fa7b, 0x1fa7f,
+ 0x1fa83, 0x1fa8f,
+ 0x1fa96, 0x1ffff,
+ 0x2a6d7, 0x2a6ff,
+ 0x2b735, 0x2b73f,
+ 0x2b81e, 0x2b81f,
+ 0x2cea2, 0x2ceaf,
+ 0x2ebe1, 0x2f7ff,
+ 0x2fa1e, 0xe0000,
+ 0xe0002, 0xe001f,
+ 0xe0080, 0xe00ff,
+ 0xe01f0, 0x10ffff,
+}; /* CR_Unknown */
+
+#ifdef USE_UNICODE_AGE_PROPERTIES
+/* 'Age_1_1': Derived Age 1.1 */
+static const OnigCodePoint CR_Age_1_1[] = {
+ 288,
+ 0x0000, 0x01f5,
+ 0x01fa, 0x0217,
+ 0x0250, 0x02a8,
+ 0x02b0, 0x02de,
+ 0x02e0, 0x02e9,
+ 0x0300, 0x0345,
+ 0x0360, 0x0361,
+ 0x0374, 0x0375,
+ 0x037a, 0x037a,
+ 0x037e, 0x037e,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03ce,
+ 0x03d0, 0x03d6,
+ 0x03da, 0x03da,
+ 0x03dc, 0x03dc,
+ 0x03de, 0x03de,
+ 0x03e0, 0x03e0,
+ 0x03e2, 0x03f3,
+ 0x0401, 0x040c,
+ 0x040e, 0x044f,
+ 0x0451, 0x045c,
+ 0x045e, 0x0486,
+ 0x0490, 0x04c4,
+ 0x04c7, 0x04c8,
+ 0x04cb, 0x04cc,
+ 0x04d0, 0x04eb,
+ 0x04ee, 0x04f5,
+ 0x04f8, 0x04f9,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x0589,
+ 0x05b0, 0x05b9,
+ 0x05bb, 0x05c3,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x060c, 0x060c,
+ 0x061b, 0x061b,
+ 0x061f, 0x061f,
+ 0x0621, 0x063a,
+ 0x0640, 0x0652,
+ 0x0660, 0x066d,
+ 0x0670, 0x06b7,
+ 0x06ba, 0x06be,
+ 0x06c0, 0x06ce,
+ 0x06d0, 0x06ed,
+ 0x06f0, 0x06f9,
+ 0x0901, 0x0903,
+ 0x0905, 0x0939,
+ 0x093c, 0x094d,
+ 0x0950, 0x0954,
+ 0x0958, 0x0970,
+ 0x0981, 0x0983,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bc, 0x09bc,
+ 0x09be, 0x09c4,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09cd,
+ 0x09d7, 0x09d7,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e3,
+ 0x09e6, 0x09fa,
+ 0x0a02, 0x0a02,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a3c, 0x0a3c,
+ 0x0a3e, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a66, 0x0a74,
+ 0x0a81, 0x0a83,
+ 0x0a85, 0x0a8b,
+ 0x0a8d, 0x0a8d,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0abc, 0x0ac5,
+ 0x0ac7, 0x0ac9,
+ 0x0acb, 0x0acd,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae0,
+ 0x0ae6, 0x0aef,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b36, 0x0b39,
+ 0x0b3c, 0x0b43,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b61,
+ 0x0b66, 0x0b70,
+ 0x0b82, 0x0b83,
+ 0x0b85, 0x0b8a,
+ 0x0b8e, 0x0b90,
+ 0x0b92, 0x0b95,
+ 0x0b99, 0x0b9a,
+ 0x0b9c, 0x0b9c,
+ 0x0b9e, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb5,
+ 0x0bb7, 0x0bb9,
+ 0x0bbe, 0x0bc2,
+ 0x0bc6, 0x0bc8,
+ 0x0bca, 0x0bcd,
+ 0x0bd7, 0x0bd7,
+ 0x0be7, 0x0bf2,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c3e, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c60, 0x0c61,
+ 0x0c66, 0x0c6f,
+ 0x0c82, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbe, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce1,
+ 0x0ce6, 0x0cef,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d28,
+ 0x0d2a, 0x0d39,
+ 0x0d3e, 0x0d43,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4d,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d61,
+ 0x0d66, 0x0d6f,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edd,
+ 0x10a0, 0x10c5,
+ 0x10d0, 0x10f6,
+ 0x10fb, 0x10fb,
+ 0x1100, 0x1159,
+ 0x115f, 0x11a2,
+ 0x11a8, 0x11f9,
+ 0x1e00, 0x1e9a,
+ 0x1ea0, 0x1ef9,
+ 0x1f00, 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, 0x202e,
+ 0x2030, 0x2046,
+ 0x206a, 0x2070,
+ 0x2074, 0x208e,
+ 0x20a0, 0x20aa,
+ 0x20d0, 0x20e1,
+ 0x2100, 0x2138,
+ 0x2153, 0x2182,
+ 0x2190, 0x21ea,
+ 0x2200, 0x22f1,
+ 0x2300, 0x2300,
+ 0x2302, 0x237a,
+ 0x2400, 0x2424,
+ 0x2440, 0x244a,
+ 0x2460, 0x24ea,
+ 0x2500, 0x2595,
+ 0x25a0, 0x25ef,
+ 0x2600, 0x2613,
+ 0x261a, 0x266f,
+ 0x2701, 0x2704,
+ 0x2706, 0x2709,
+ 0x270c, 0x2727,
+ 0x2729, 0x274b,
+ 0x274d, 0x274d,
+ 0x274f, 0x2752,
+ 0x2756, 0x2756,
+ 0x2758, 0x275e,
+ 0x2761, 0x2767,
+ 0x2776, 0x2794,
+ 0x2798, 0x27af,
+ 0x27b1, 0x27be,
+ 0x3000, 0x3037,
+ 0x303f, 0x303f,
+ 0x3041, 0x3094,
+ 0x3099, 0x309e,
+ 0x30a1, 0x30fe,
+ 0x3105, 0x312c,
+ 0x3131, 0x318e,
+ 0x3190, 0x319f,
+ 0x3200, 0x321c,
+ 0x3220, 0x3243,
+ 0x3260, 0x327b,
+ 0x327f, 0x32b0,
+ 0x32c0, 0x32cb,
+ 0x32d0, 0x32fe,
+ 0x3300, 0x3376,
+ 0x337b, 0x33dd,
+ 0x33e0, 0x33fe,
+ 0x4e00, 0x9fa5,
+ 0xe000, 0xfa2d,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1e, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfb,
+ 0xfe20, 0xfe23,
+ 0xfe30, 0xfe44,
+ 0xfe49, 0xfe52,
+ 0xfe54, 0xfe66,
+ 0xfe68, 0xfe6b,
+ 0xfe70, 0xfe72,
+ 0xfe74, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0xfeff, 0xfeff,
+ 0xff01, 0xff5e,
+ 0xff61, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0xffe0, 0xffe6,
+ 0xffe8, 0xffee,
+ 0xfffd, 0xffff,
+}; /* CR_Age_1_1 */
+
+/* 'Age_2_0': Derived Age 2.0 */
+static const OnigCodePoint CR_Age_2_0[] = {
+ 312,
+ 0x0000, 0x01f5,
+ 0x01fa, 0x0217,
+ 0x0250, 0x02a8,
+ 0x02b0, 0x02de,
+ 0x02e0, 0x02e9,
+ 0x0300, 0x0345,
+ 0x0360, 0x0361,
+ 0x0374, 0x0375,
+ 0x037a, 0x037a,
+ 0x037e, 0x037e,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03ce,
+ 0x03d0, 0x03d6,
+ 0x03da, 0x03da,
+ 0x03dc, 0x03dc,
+ 0x03de, 0x03de,
+ 0x03e0, 0x03e0,
+ 0x03e2, 0x03f3,
+ 0x0401, 0x040c,
+ 0x040e, 0x044f,
+ 0x0451, 0x045c,
+ 0x045e, 0x0486,
+ 0x0490, 0x04c4,
+ 0x04c7, 0x04c8,
+ 0x04cb, 0x04cc,
+ 0x04d0, 0x04eb,
+ 0x04ee, 0x04f5,
+ 0x04f8, 0x04f9,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x0589,
+ 0x0591, 0x05a1,
+ 0x05a3, 0x05b9,
+ 0x05bb, 0x05c4,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x060c, 0x060c,
+ 0x061b, 0x061b,
+ 0x061f, 0x061f,
+ 0x0621, 0x063a,
+ 0x0640, 0x0652,
+ 0x0660, 0x066d,
+ 0x0670, 0x06b7,
+ 0x06ba, 0x06be,
+ 0x06c0, 0x06ce,
+ 0x06d0, 0x06ed,
+ 0x06f0, 0x06f9,
+ 0x0901, 0x0903,
+ 0x0905, 0x0939,
+ 0x093c, 0x094d,
+ 0x0950, 0x0954,
+ 0x0958, 0x0970,
+ 0x0981, 0x0983,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bc, 0x09bc,
+ 0x09be, 0x09c4,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09cd,
+ 0x09d7, 0x09d7,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e3,
+ 0x09e6, 0x09fa,
+ 0x0a02, 0x0a02,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a3c, 0x0a3c,
+ 0x0a3e, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a66, 0x0a74,
+ 0x0a81, 0x0a83,
+ 0x0a85, 0x0a8b,
+ 0x0a8d, 0x0a8d,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0abc, 0x0ac5,
+ 0x0ac7, 0x0ac9,
+ 0x0acb, 0x0acd,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae0,
+ 0x0ae6, 0x0aef,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b36, 0x0b39,
+ 0x0b3c, 0x0b43,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b61,
+ 0x0b66, 0x0b70,
+ 0x0b82, 0x0b83,
+ 0x0b85, 0x0b8a,
+ 0x0b8e, 0x0b90,
+ 0x0b92, 0x0b95,
+ 0x0b99, 0x0b9a,
+ 0x0b9c, 0x0b9c,
+ 0x0b9e, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb5,
+ 0x0bb7, 0x0bb9,
+ 0x0bbe, 0x0bc2,
+ 0x0bc6, 0x0bc8,
+ 0x0bca, 0x0bcd,
+ 0x0bd7, 0x0bd7,
+ 0x0be7, 0x0bf2,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c3e, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c60, 0x0c61,
+ 0x0c66, 0x0c6f,
+ 0x0c82, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbe, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce1,
+ 0x0ce6, 0x0cef,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d28,
+ 0x0d2a, 0x0d39,
+ 0x0d3e, 0x0d43,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4d,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d61,
+ 0x0d66, 0x0d6f,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edd,
+ 0x0f00, 0x0f47,
+ 0x0f49, 0x0f69,
+ 0x0f71, 0x0f8b,
+ 0x0f90, 0x0f95,
+ 0x0f97, 0x0f97,
+ 0x0f99, 0x0fad,
+ 0x0fb1, 0x0fb7,
+ 0x0fb9, 0x0fb9,
+ 0x10a0, 0x10c5,
+ 0x10d0, 0x10f6,
+ 0x10fb, 0x10fb,
+ 0x1100, 0x1159,
+ 0x115f, 0x11a2,
+ 0x11a8, 0x11f9,
+ 0x1e00, 0x1e9b,
+ 0x1ea0, 0x1ef9,
+ 0x1f00, 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, 0x202e,
+ 0x2030, 0x2046,
+ 0x206a, 0x2070,
+ 0x2074, 0x208e,
+ 0x20a0, 0x20ab,
+ 0x20d0, 0x20e1,
+ 0x2100, 0x2138,
+ 0x2153, 0x2182,
+ 0x2190, 0x21ea,
+ 0x2200, 0x22f1,
+ 0x2300, 0x2300,
+ 0x2302, 0x237a,
+ 0x2400, 0x2424,
+ 0x2440, 0x244a,
+ 0x2460, 0x24ea,
+ 0x2500, 0x2595,
+ 0x25a0, 0x25ef,
+ 0x2600, 0x2613,
+ 0x261a, 0x266f,
+ 0x2701, 0x2704,
+ 0x2706, 0x2709,
+ 0x270c, 0x2727,
+ 0x2729, 0x274b,
+ 0x274d, 0x274d,
+ 0x274f, 0x2752,
+ 0x2756, 0x2756,
+ 0x2758, 0x275e,
+ 0x2761, 0x2767,
+ 0x2776, 0x2794,
+ 0x2798, 0x27af,
+ 0x27b1, 0x27be,
+ 0x3000, 0x3037,
+ 0x303f, 0x303f,
+ 0x3041, 0x3094,
+ 0x3099, 0x309e,
+ 0x30a1, 0x30fe,
+ 0x3105, 0x312c,
+ 0x3131, 0x318e,
+ 0x3190, 0x319f,
+ 0x3200, 0x321c,
+ 0x3220, 0x3243,
+ 0x3260, 0x327b,
+ 0x327f, 0x32b0,
+ 0x32c0, 0x32cb,
+ 0x32d0, 0x32fe,
+ 0x3300, 0x3376,
+ 0x337b, 0x33dd,
+ 0x33e0, 0x33fe,
+ 0x4e00, 0x9fa5,
+ 0xac00, 0xd7a3,
+ 0xd800, 0xfa2d,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1e, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfb,
+ 0xfe20, 0xfe23,
+ 0xfe30, 0xfe44,
+ 0xfe49, 0xfe52,
+ 0xfe54, 0xfe66,
+ 0xfe68, 0xfe6b,
+ 0xfe70, 0xfe72,
+ 0xfe74, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0xfeff, 0xfeff,
+ 0xff01, 0xff5e,
+ 0xff61, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0xffe0, 0xffe6,
+ 0xffe8, 0xffee,
+ 0xfffd, 0xffff,
+ 0x1fffe, 0x1ffff,
+ 0x2fffe, 0x2ffff,
+ 0x3fffe, 0x3ffff,
+ 0x4fffe, 0x4ffff,
+ 0x5fffe, 0x5ffff,
+ 0x6fffe, 0x6ffff,
+ 0x7fffe, 0x7ffff,
+ 0x8fffe, 0x8ffff,
+ 0x9fffe, 0x9ffff,
+ 0xafffe, 0xaffff,
+ 0xbfffe, 0xbffff,
+ 0xcfffe, 0xcffff,
+ 0xdfffe, 0xdffff,
+ 0xefffe, 0x10ffff,
+}; /* CR_Age_2_0 */
+
+/* 'Age_2_1': Derived Age 2.1 */
+static const OnigCodePoint CR_Age_2_1[] = {
+ 312,
+ 0x0000, 0x01f5,
+ 0x01fa, 0x0217,
+ 0x0250, 0x02a8,
+ 0x02b0, 0x02de,
+ 0x02e0, 0x02e9,
+ 0x0300, 0x0345,
+ 0x0360, 0x0361,
+ 0x0374, 0x0375,
+ 0x037a, 0x037a,
+ 0x037e, 0x037e,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03ce,
+ 0x03d0, 0x03d6,
+ 0x03da, 0x03da,
+ 0x03dc, 0x03dc,
+ 0x03de, 0x03de,
+ 0x03e0, 0x03e0,
+ 0x03e2, 0x03f3,
+ 0x0401, 0x040c,
+ 0x040e, 0x044f,
+ 0x0451, 0x045c,
+ 0x045e, 0x0486,
+ 0x0490, 0x04c4,
+ 0x04c7, 0x04c8,
+ 0x04cb, 0x04cc,
+ 0x04d0, 0x04eb,
+ 0x04ee, 0x04f5,
+ 0x04f8, 0x04f9,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x0589,
+ 0x0591, 0x05a1,
+ 0x05a3, 0x05b9,
+ 0x05bb, 0x05c4,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x060c, 0x060c,
+ 0x061b, 0x061b,
+ 0x061f, 0x061f,
+ 0x0621, 0x063a,
+ 0x0640, 0x0652,
+ 0x0660, 0x066d,
+ 0x0670, 0x06b7,
+ 0x06ba, 0x06be,
+ 0x06c0, 0x06ce,
+ 0x06d0, 0x06ed,
+ 0x06f0, 0x06f9,
+ 0x0901, 0x0903,
+ 0x0905, 0x0939,
+ 0x093c, 0x094d,
+ 0x0950, 0x0954,
+ 0x0958, 0x0970,
+ 0x0981, 0x0983,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bc, 0x09bc,
+ 0x09be, 0x09c4,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09cd,
+ 0x09d7, 0x09d7,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e3,
+ 0x09e6, 0x09fa,
+ 0x0a02, 0x0a02,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a3c, 0x0a3c,
+ 0x0a3e, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a66, 0x0a74,
+ 0x0a81, 0x0a83,
+ 0x0a85, 0x0a8b,
+ 0x0a8d, 0x0a8d,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0abc, 0x0ac5,
+ 0x0ac7, 0x0ac9,
+ 0x0acb, 0x0acd,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae0,
+ 0x0ae6, 0x0aef,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b36, 0x0b39,
+ 0x0b3c, 0x0b43,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b61,
+ 0x0b66, 0x0b70,
+ 0x0b82, 0x0b83,
+ 0x0b85, 0x0b8a,
+ 0x0b8e, 0x0b90,
+ 0x0b92, 0x0b95,
+ 0x0b99, 0x0b9a,
+ 0x0b9c, 0x0b9c,
+ 0x0b9e, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb5,
+ 0x0bb7, 0x0bb9,
+ 0x0bbe, 0x0bc2,
+ 0x0bc6, 0x0bc8,
+ 0x0bca, 0x0bcd,
+ 0x0bd7, 0x0bd7,
+ 0x0be7, 0x0bf2,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c3e, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c60, 0x0c61,
+ 0x0c66, 0x0c6f,
+ 0x0c82, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbe, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce1,
+ 0x0ce6, 0x0cef,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d28,
+ 0x0d2a, 0x0d39,
+ 0x0d3e, 0x0d43,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4d,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d61,
+ 0x0d66, 0x0d6f,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edd,
+ 0x0f00, 0x0f47,
+ 0x0f49, 0x0f69,
+ 0x0f71, 0x0f8b,
+ 0x0f90, 0x0f95,
+ 0x0f97, 0x0f97,
+ 0x0f99, 0x0fad,
+ 0x0fb1, 0x0fb7,
+ 0x0fb9, 0x0fb9,
+ 0x10a0, 0x10c5,
+ 0x10d0, 0x10f6,
+ 0x10fb, 0x10fb,
+ 0x1100, 0x1159,
+ 0x115f, 0x11a2,
+ 0x11a8, 0x11f9,
+ 0x1e00, 0x1e9b,
+ 0x1ea0, 0x1ef9,
+ 0x1f00, 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, 0x202e,
+ 0x2030, 0x2046,
+ 0x206a, 0x2070,
+ 0x2074, 0x208e,
+ 0x20a0, 0x20ac,
+ 0x20d0, 0x20e1,
+ 0x2100, 0x2138,
+ 0x2153, 0x2182,
+ 0x2190, 0x21ea,
+ 0x2200, 0x22f1,
+ 0x2300, 0x2300,
+ 0x2302, 0x237a,
+ 0x2400, 0x2424,
+ 0x2440, 0x244a,
+ 0x2460, 0x24ea,
+ 0x2500, 0x2595,
+ 0x25a0, 0x25ef,
+ 0x2600, 0x2613,
+ 0x261a, 0x266f,
+ 0x2701, 0x2704,
+ 0x2706, 0x2709,
+ 0x270c, 0x2727,
+ 0x2729, 0x274b,
+ 0x274d, 0x274d,
+ 0x274f, 0x2752,
+ 0x2756, 0x2756,
+ 0x2758, 0x275e,
+ 0x2761, 0x2767,
+ 0x2776, 0x2794,
+ 0x2798, 0x27af,
+ 0x27b1, 0x27be,
+ 0x3000, 0x3037,
+ 0x303f, 0x303f,
+ 0x3041, 0x3094,
+ 0x3099, 0x309e,
+ 0x30a1, 0x30fe,
+ 0x3105, 0x312c,
+ 0x3131, 0x318e,
+ 0x3190, 0x319f,
+ 0x3200, 0x321c,
+ 0x3220, 0x3243,
+ 0x3260, 0x327b,
+ 0x327f, 0x32b0,
+ 0x32c0, 0x32cb,
+ 0x32d0, 0x32fe,
+ 0x3300, 0x3376,
+ 0x337b, 0x33dd,
+ 0x33e0, 0x33fe,
+ 0x4e00, 0x9fa5,
+ 0xac00, 0xd7a3,
+ 0xd800, 0xfa2d,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1e, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfb,
+ 0xfe20, 0xfe23,
+ 0xfe30, 0xfe44,
+ 0xfe49, 0xfe52,
+ 0xfe54, 0xfe66,
+ 0xfe68, 0xfe6b,
+ 0xfe70, 0xfe72,
+ 0xfe74, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0xfeff, 0xfeff,
+ 0xff01, 0xff5e,
+ 0xff61, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0xffe0, 0xffe6,
+ 0xffe8, 0xffee,
+ 0xfffc, 0xffff,
+ 0x1fffe, 0x1ffff,
+ 0x2fffe, 0x2ffff,
+ 0x3fffe, 0x3ffff,
+ 0x4fffe, 0x4ffff,
+ 0x5fffe, 0x5ffff,
+ 0x6fffe, 0x6ffff,
+ 0x7fffe, 0x7ffff,
+ 0x8fffe, 0x8ffff,
+ 0x9fffe, 0x9ffff,
+ 0xafffe, 0xaffff,
+ 0xbfffe, 0xbffff,
+ 0xcfffe, 0xcffff,
+ 0xdfffe, 0xdffff,
+ 0xefffe, 0x10ffff,
+}; /* CR_Age_2_1 */
+
+/* 'Age_3_0': Derived Age 3.0 */
+static const OnigCodePoint CR_Age_3_0[] = {
+ 369,
+ 0x0000, 0x021f,
+ 0x0222, 0x0233,
+ 0x0250, 0x02ad,
+ 0x02b0, 0x02ee,
+ 0x0300, 0x034e,
+ 0x0360, 0x0362,
+ 0x0374, 0x0375,
+ 0x037a, 0x037a,
+ 0x037e, 0x037e,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03ce,
+ 0x03d0, 0x03d7,
+ 0x03da, 0x03f3,
+ 0x0400, 0x0486,
+ 0x0488, 0x0489,
+ 0x048c, 0x04c4,
+ 0x04c7, 0x04c8,
+ 0x04cb, 0x04cc,
+ 0x04d0, 0x04f5,
+ 0x04f8, 0x04f9,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x0591, 0x05a1,
+ 0x05a3, 0x05b9,
+ 0x05bb, 0x05c4,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x060c, 0x060c,
+ 0x061b, 0x061b,
+ 0x061f, 0x061f,
+ 0x0621, 0x063a,
+ 0x0640, 0x0655,
+ 0x0660, 0x066d,
+ 0x0670, 0x06ed,
+ 0x06f0, 0x06fe,
+ 0x0700, 0x070d,
+ 0x070f, 0x072c,
+ 0x0730, 0x074a,
+ 0x0780, 0x07b0,
+ 0x0901, 0x0903,
+ 0x0905, 0x0939,
+ 0x093c, 0x094d,
+ 0x0950, 0x0954,
+ 0x0958, 0x0970,
+ 0x0981, 0x0983,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bc, 0x09bc,
+ 0x09be, 0x09c4,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09cd,
+ 0x09d7, 0x09d7,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e3,
+ 0x09e6, 0x09fa,
+ 0x0a02, 0x0a02,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a3c, 0x0a3c,
+ 0x0a3e, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a66, 0x0a74,
+ 0x0a81, 0x0a83,
+ 0x0a85, 0x0a8b,
+ 0x0a8d, 0x0a8d,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0abc, 0x0ac5,
+ 0x0ac7, 0x0ac9,
+ 0x0acb, 0x0acd,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae0,
+ 0x0ae6, 0x0aef,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b36, 0x0b39,
+ 0x0b3c, 0x0b43,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b61,
+ 0x0b66, 0x0b70,
+ 0x0b82, 0x0b83,
+ 0x0b85, 0x0b8a,
+ 0x0b8e, 0x0b90,
+ 0x0b92, 0x0b95,
+ 0x0b99, 0x0b9a,
+ 0x0b9c, 0x0b9c,
+ 0x0b9e, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb5,
+ 0x0bb7, 0x0bb9,
+ 0x0bbe, 0x0bc2,
+ 0x0bc6, 0x0bc8,
+ 0x0bca, 0x0bcd,
+ 0x0bd7, 0x0bd7,
+ 0x0be7, 0x0bf2,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c3e, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c60, 0x0c61,
+ 0x0c66, 0x0c6f,
+ 0x0c82, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbe, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce1,
+ 0x0ce6, 0x0cef,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d28,
+ 0x0d2a, 0x0d39,
+ 0x0d3e, 0x0d43,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4d,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d61,
+ 0x0d66, 0x0d6f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edd,
+ 0x0f00, 0x0f47,
+ 0x0f49, 0x0f6a,
+ 0x0f71, 0x0f8b,
+ 0x0f90, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fbe, 0x0fcc,
+ 0x0fcf, 0x0fcf,
+ 0x1000, 0x1021,
+ 0x1023, 0x1027,
+ 0x1029, 0x102a,
+ 0x102c, 0x1032,
+ 0x1036, 0x1039,
+ 0x1040, 0x1059,
+ 0x10a0, 0x10c5,
+ 0x10d0, 0x10f6,
+ 0x10fb, 0x10fb,
+ 0x1100, 0x1159,
+ 0x115f, 0x11a2,
+ 0x11a8, 0x11f9,
+ 0x1200, 0x1206,
+ 0x1208, 0x1246,
+ 0x1248, 0x1248,
+ 0x124a, 0x124d,
+ 0x1250, 0x1256,
+ 0x1258, 0x1258,
+ 0x125a, 0x125d,
+ 0x1260, 0x1286,
+ 0x1288, 0x1288,
+ 0x128a, 0x128d,
+ 0x1290, 0x12ae,
+ 0x12b0, 0x12b0,
+ 0x12b2, 0x12b5,
+ 0x12b8, 0x12be,
+ 0x12c0, 0x12c0,
+ 0x12c2, 0x12c5,
+ 0x12c8, 0x12ce,
+ 0x12d0, 0x12d6,
+ 0x12d8, 0x12ee,
+ 0x12f0, 0x130e,
+ 0x1310, 0x1310,
+ 0x1312, 0x1315,
+ 0x1318, 0x131e,
+ 0x1320, 0x1346,
+ 0x1348, 0x135a,
+ 0x1361, 0x137c,
+ 0x13a0, 0x13f4,
+ 0x1401, 0x1676,
+ 0x1680, 0x169c,
+ 0x16a0, 0x16f0,
+ 0x1780, 0x17dc,
+ 0x17e0, 0x17e9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 0x1880, 0x18a9,
+ 0x1e00, 0x1e9b,
+ 0x1ea0, 0x1ef9,
+ 0x1f00, 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, 0x2046,
+ 0x2048, 0x204d,
+ 0x206a, 0x2070,
+ 0x2074, 0x208e,
+ 0x20a0, 0x20af,
+ 0x20d0, 0x20e3,
+ 0x2100, 0x213a,
+ 0x2153, 0x2183,
+ 0x2190, 0x21f3,
+ 0x2200, 0x22f1,
+ 0x2300, 0x237b,
+ 0x237d, 0x239a,
+ 0x2400, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x24ea,
+ 0x2500, 0x2595,
+ 0x25a0, 0x25f7,
+ 0x2600, 0x2613,
+ 0x2619, 0x2671,
+ 0x2701, 0x2704,
+ 0x2706, 0x2709,
+ 0x270c, 0x2727,
+ 0x2729, 0x274b,
+ 0x274d, 0x274d,
+ 0x274f, 0x2752,
+ 0x2756, 0x2756,
+ 0x2758, 0x275e,
+ 0x2761, 0x2767,
+ 0x2776, 0x2794,
+ 0x2798, 0x27af,
+ 0x27b1, 0x27be,
+ 0x2800, 0x28ff,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303a,
+ 0x303e, 0x303f,
+ 0x3041, 0x3094,
+ 0x3099, 0x309e,
+ 0x30a1, 0x30fe,
+ 0x3105, 0x312c,
+ 0x3131, 0x318e,
+ 0x3190, 0x31b7,
+ 0x3200, 0x321c,
+ 0x3220, 0x3243,
+ 0x3260, 0x327b,
+ 0x327f, 0x32b0,
+ 0x32c0, 0x32cb,
+ 0x32d0, 0x32fe,
+ 0x3300, 0x3376,
+ 0x337b, 0x33dd,
+ 0x33e0, 0x33fe,
+ 0x3400, 0x4db5,
+ 0x4e00, 0x9fa5,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4a1,
+ 0xa4a4, 0xa4b3,
+ 0xa4b5, 0xa4c0,
+ 0xa4c2, 0xa4c4,
+ 0xa4c6, 0xa4c6,
+ 0xac00, 0xd7a3,
+ 0xd800, 0xfa2d,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdf0, 0xfdfb,
+ 0xfe20, 0xfe23,
+ 0xfe30, 0xfe44,
+ 0xfe49, 0xfe52,
+ 0xfe54, 0xfe66,
+ 0xfe68, 0xfe6b,
+ 0xfe70, 0xfe72,
+ 0xfe74, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0xfeff, 0xfeff,
+ 0xff01, 0xff5e,
+ 0xff61, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0xffe0, 0xffe6,
+ 0xffe8, 0xffee,
+ 0xfff9, 0xffff,
+ 0x1fffe, 0x1ffff,
+ 0x2fffe, 0x2ffff,
+ 0x3fffe, 0x3ffff,
+ 0x4fffe, 0x4ffff,
+ 0x5fffe, 0x5ffff,
+ 0x6fffe, 0x6ffff,
+ 0x7fffe, 0x7ffff,
+ 0x8fffe, 0x8ffff,
+ 0x9fffe, 0x9ffff,
+ 0xafffe, 0xaffff,
+ 0xbfffe, 0xbffff,
+ 0xcfffe, 0xcffff,
+ 0xdfffe, 0xdffff,
+ 0xefffe, 0x10ffff,
+}; /* CR_Age_3_0 */
+
+/* 'Age_3_1': Derived Age 3.1 */
+static const OnigCodePoint CR_Age_3_1[] = {
+ 402,
+ 0x0000, 0x021f,
+ 0x0222, 0x0233,
+ 0x0250, 0x02ad,
+ 0x02b0, 0x02ee,
+ 0x0300, 0x034e,
+ 0x0360, 0x0362,
+ 0x0374, 0x0375,
+ 0x037a, 0x037a,
+ 0x037e, 0x037e,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03ce,
+ 0x03d0, 0x03d7,
+ 0x03da, 0x03f5,
+ 0x0400, 0x0486,
+ 0x0488, 0x0489,
+ 0x048c, 0x04c4,
+ 0x04c7, 0x04c8,
+ 0x04cb, 0x04cc,
+ 0x04d0, 0x04f5,
+ 0x04f8, 0x04f9,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x0591, 0x05a1,
+ 0x05a3, 0x05b9,
+ 0x05bb, 0x05c4,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x060c, 0x060c,
+ 0x061b, 0x061b,
+ 0x061f, 0x061f,
+ 0x0621, 0x063a,
+ 0x0640, 0x0655,
+ 0x0660, 0x066d,
+ 0x0670, 0x06ed,
+ 0x06f0, 0x06fe,
+ 0x0700, 0x070d,
+ 0x070f, 0x072c,
+ 0x0730, 0x074a,
+ 0x0780, 0x07b0,
+ 0x0901, 0x0903,
+ 0x0905, 0x0939,
+ 0x093c, 0x094d,
+ 0x0950, 0x0954,
+ 0x0958, 0x0970,
+ 0x0981, 0x0983,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bc, 0x09bc,
+ 0x09be, 0x09c4,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09cd,
+ 0x09d7, 0x09d7,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e3,
+ 0x09e6, 0x09fa,
+ 0x0a02, 0x0a02,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a3c, 0x0a3c,
+ 0x0a3e, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a66, 0x0a74,
+ 0x0a81, 0x0a83,
+ 0x0a85, 0x0a8b,
+ 0x0a8d, 0x0a8d,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0abc, 0x0ac5,
+ 0x0ac7, 0x0ac9,
+ 0x0acb, 0x0acd,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae0,
+ 0x0ae6, 0x0aef,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b36, 0x0b39,
+ 0x0b3c, 0x0b43,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b61,
+ 0x0b66, 0x0b70,
+ 0x0b82, 0x0b83,
+ 0x0b85, 0x0b8a,
+ 0x0b8e, 0x0b90,
+ 0x0b92, 0x0b95,
+ 0x0b99, 0x0b9a,
+ 0x0b9c, 0x0b9c,
+ 0x0b9e, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb5,
+ 0x0bb7, 0x0bb9,
+ 0x0bbe, 0x0bc2,
+ 0x0bc6, 0x0bc8,
+ 0x0bca, 0x0bcd,
+ 0x0bd7, 0x0bd7,
+ 0x0be7, 0x0bf2,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c3e, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c60, 0x0c61,
+ 0x0c66, 0x0c6f,
+ 0x0c82, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbe, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce1,
+ 0x0ce6, 0x0cef,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d28,
+ 0x0d2a, 0x0d39,
+ 0x0d3e, 0x0d43,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4d,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d61,
+ 0x0d66, 0x0d6f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edd,
+ 0x0f00, 0x0f47,
+ 0x0f49, 0x0f6a,
+ 0x0f71, 0x0f8b,
+ 0x0f90, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fbe, 0x0fcc,
+ 0x0fcf, 0x0fcf,
+ 0x1000, 0x1021,
+ 0x1023, 0x1027,
+ 0x1029, 0x102a,
+ 0x102c, 0x1032,
+ 0x1036, 0x1039,
+ 0x1040, 0x1059,
+ 0x10a0, 0x10c5,
+ 0x10d0, 0x10f6,
+ 0x10fb, 0x10fb,
+ 0x1100, 0x1159,
+ 0x115f, 0x11a2,
+ 0x11a8, 0x11f9,
+ 0x1200, 0x1206,
+ 0x1208, 0x1246,
+ 0x1248, 0x1248,
+ 0x124a, 0x124d,
+ 0x1250, 0x1256,
+ 0x1258, 0x1258,
+ 0x125a, 0x125d,
+ 0x1260, 0x1286,
+ 0x1288, 0x1288,
+ 0x128a, 0x128d,
+ 0x1290, 0x12ae,
+ 0x12b0, 0x12b0,
+ 0x12b2, 0x12b5,
+ 0x12b8, 0x12be,
+ 0x12c0, 0x12c0,
+ 0x12c2, 0x12c5,
+ 0x12c8, 0x12ce,
+ 0x12d0, 0x12d6,
+ 0x12d8, 0x12ee,
+ 0x12f0, 0x130e,
+ 0x1310, 0x1310,
+ 0x1312, 0x1315,
+ 0x1318, 0x131e,
+ 0x1320, 0x1346,
+ 0x1348, 0x135a,
+ 0x1361, 0x137c,
+ 0x13a0, 0x13f4,
+ 0x1401, 0x1676,
+ 0x1680, 0x169c,
+ 0x16a0, 0x16f0,
+ 0x1780, 0x17dc,
+ 0x17e0, 0x17e9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 0x1880, 0x18a9,
+ 0x1e00, 0x1e9b,
+ 0x1ea0, 0x1ef9,
+ 0x1f00, 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, 0x2046,
+ 0x2048, 0x204d,
+ 0x206a, 0x2070,
+ 0x2074, 0x208e,
+ 0x20a0, 0x20af,
+ 0x20d0, 0x20e3,
+ 0x2100, 0x213a,
+ 0x2153, 0x2183,
+ 0x2190, 0x21f3,
+ 0x2200, 0x22f1,
+ 0x2300, 0x237b,
+ 0x237d, 0x239a,
+ 0x2400, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x24ea,
+ 0x2500, 0x2595,
+ 0x25a0, 0x25f7,
+ 0x2600, 0x2613,
+ 0x2619, 0x2671,
+ 0x2701, 0x2704,
+ 0x2706, 0x2709,
+ 0x270c, 0x2727,
+ 0x2729, 0x274b,
+ 0x274d, 0x274d,
+ 0x274f, 0x2752,
+ 0x2756, 0x2756,
+ 0x2758, 0x275e,
+ 0x2761, 0x2767,
+ 0x2776, 0x2794,
+ 0x2798, 0x27af,
+ 0x27b1, 0x27be,
+ 0x2800, 0x28ff,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303a,
+ 0x303e, 0x303f,
+ 0x3041, 0x3094,
+ 0x3099, 0x309e,
+ 0x30a1, 0x30fe,
+ 0x3105, 0x312c,
+ 0x3131, 0x318e,
+ 0x3190, 0x31b7,
+ 0x3200, 0x321c,
+ 0x3220, 0x3243,
+ 0x3260, 0x327b,
+ 0x327f, 0x32b0,
+ 0x32c0, 0x32cb,
+ 0x32d0, 0x32fe,
+ 0x3300, 0x3376,
+ 0x337b, 0x33dd,
+ 0x33e0, 0x33fe,
+ 0x3400, 0x4db5,
+ 0x4e00, 0x9fa5,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4a1,
+ 0xa4a4, 0xa4b3,
+ 0xa4b5, 0xa4c0,
+ 0xa4c2, 0xa4c4,
+ 0xa4c6, 0xa4c6,
+ 0xac00, 0xd7a3,
+ 0xd800, 0xfa2d,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfb,
+ 0xfe20, 0xfe23,
+ 0xfe30, 0xfe44,
+ 0xfe49, 0xfe52,
+ 0xfe54, 0xfe66,
+ 0xfe68, 0xfe6b,
+ 0xfe70, 0xfe72,
+ 0xfe74, 0xfe74,
+ 0xfe76, 0xfefc,
+ 0xfeff, 0xfeff,
+ 0xff01, 0xff5e,
+ 0xff61, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0xffe0, 0xffe6,
+ 0xffe8, 0xffee,
+ 0xfff9, 0xffff,
+ 0x10300, 0x1031e,
+ 0x10320, 0x10323,
+ 0x10330, 0x1034a,
+ 0x10400, 0x10425,
+ 0x10428, 0x1044d,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d12a, 0x1d1dd,
+ 0x1d400, 0x1d454,
+ 0x1d456, 0x1d49c,
+ 0x1d49e, 0x1d49f,
+ 0x1d4a2, 0x1d4a2,
+ 0x1d4a5, 0x1d4a6,
+ 0x1d4a9, 0x1d4ac,
+ 0x1d4ae, 0x1d4b9,
+ 0x1d4bb, 0x1d4bb,
+ 0x1d4bd, 0x1d4c0,
+ 0x1d4c2, 0x1d4c3,
+ 0x1d4c5, 0x1d505,
+ 0x1d507, 0x1d50a,
+ 0x1d50d, 0x1d514,
+ 0x1d516, 0x1d51c,
+ 0x1d51e, 0x1d539,
+ 0x1d53b, 0x1d53e,
+ 0x1d540, 0x1d544,
+ 0x1d546, 0x1d546,
+ 0x1d54a, 0x1d550,
+ 0x1d552, 0x1d6a3,
+ 0x1d6a8, 0x1d7c9,
+ 0x1d7ce, 0x1d7ff,
+ 0x1fffe, 0x2a6d6,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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,
+ 0xefffe, 0x10ffff,
+}; /* CR_Age_3_1 */
+
+/* 'Age_3_2': Derived Age 3.2 */
+static const OnigCodePoint CR_Age_3_2[] = {
+ 397,
+ 0x0000, 0x0220,
+ 0x0222, 0x0233,
+ 0x0250, 0x02ad,
+ 0x02b0, 0x02ee,
+ 0x0300, 0x034f,
+ 0x0360, 0x036f,
+ 0x0374, 0x0375,
+ 0x037a, 0x037a,
+ 0x037e, 0x037e,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03ce,
+ 0x03d0, 0x03f6,
+ 0x0400, 0x0486,
+ 0x0488, 0x04ce,
+ 0x04d0, 0x04f5,
+ 0x04f8, 0x04f9,
+ 0x0500, 0x050f,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x0591, 0x05a1,
+ 0x05a3, 0x05b9,
+ 0x05bb, 0x05c4,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x060c, 0x060c,
+ 0x061b, 0x061b,
+ 0x061f, 0x061f,
+ 0x0621, 0x063a,
+ 0x0640, 0x0655,
+ 0x0660, 0x06ed,
+ 0x06f0, 0x06fe,
+ 0x0700, 0x070d,
+ 0x070f, 0x072c,
+ 0x0730, 0x074a,
+ 0x0780, 0x07b1,
+ 0x0901, 0x0903,
+ 0x0905, 0x0939,
+ 0x093c, 0x094d,
+ 0x0950, 0x0954,
+ 0x0958, 0x0970,
+ 0x0981, 0x0983,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bc, 0x09bc,
+ 0x09be, 0x09c4,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09cd,
+ 0x09d7, 0x09d7,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e3,
+ 0x09e6, 0x09fa,
+ 0x0a02, 0x0a02,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a3c, 0x0a3c,
+ 0x0a3e, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a66, 0x0a74,
+ 0x0a81, 0x0a83,
+ 0x0a85, 0x0a8b,
+ 0x0a8d, 0x0a8d,
+ 0x0a8f, 0x0a91,
+ 0x0a93, 0x0aa8,
+ 0x0aaa, 0x0ab0,
+ 0x0ab2, 0x0ab3,
+ 0x0ab5, 0x0ab9,
+ 0x0abc, 0x0ac5,
+ 0x0ac7, 0x0ac9,
+ 0x0acb, 0x0acd,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae0,
+ 0x0ae6, 0x0aef,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b36, 0x0b39,
+ 0x0b3c, 0x0b43,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b61,
+ 0x0b66, 0x0b70,
+ 0x0b82, 0x0b83,
+ 0x0b85, 0x0b8a,
+ 0x0b8e, 0x0b90,
+ 0x0b92, 0x0b95,
+ 0x0b99, 0x0b9a,
+ 0x0b9c, 0x0b9c,
+ 0x0b9e, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb5,
+ 0x0bb7, 0x0bb9,
+ 0x0bbe, 0x0bc2,
+ 0x0bc6, 0x0bc8,
+ 0x0bca, 0x0bcd,
+ 0x0bd7, 0x0bd7,
+ 0x0be7, 0x0bf2,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c3e, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c60, 0x0c61,
+ 0x0c66, 0x0c6f,
+ 0x0c82, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbe, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce1,
+ 0x0ce6, 0x0cef,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d28,
+ 0x0d2a, 0x0d39,
+ 0x0d3e, 0x0d43,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4d,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d61,
+ 0x0d66, 0x0d6f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edd,
+ 0x0f00, 0x0f47,
+ 0x0f49, 0x0f6a,
+ 0x0f71, 0x0f8b,
+ 0x0f90, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fbe, 0x0fcc,
+ 0x0fcf, 0x0fcf,
+ 0x1000, 0x1021,
+ 0x1023, 0x1027,
+ 0x1029, 0x102a,
+ 0x102c, 0x1032,
+ 0x1036, 0x1039,
+ 0x1040, 0x1059,
+ 0x10a0, 0x10c5,
+ 0x10d0, 0x10f8,
+ 0x10fb, 0x10fb,
+ 0x1100, 0x1159,
+ 0x115f, 0x11a2,
+ 0x11a8, 0x11f9,
+ 0x1200, 0x1206,
+ 0x1208, 0x1246,
+ 0x1248, 0x1248,
+ 0x124a, 0x124d,
+ 0x1250, 0x1256,
+ 0x1258, 0x1258,
+ 0x125a, 0x125d,
+ 0x1260, 0x1286,
+ 0x1288, 0x1288,
+ 0x128a, 0x128d,
+ 0x1290, 0x12ae,
+ 0x12b0, 0x12b0,
+ 0x12b2, 0x12b5,
+ 0x12b8, 0x12be,
+ 0x12c0, 0x12c0,
+ 0x12c2, 0x12c5,
+ 0x12c8, 0x12ce,
+ 0x12d0, 0x12d6,
+ 0x12d8, 0x12ee,
+ 0x12f0, 0x130e,
+ 0x1310, 0x1310,
+ 0x1312, 0x1315,
+ 0x1318, 0x131e,
+ 0x1320, 0x1346,
+ 0x1348, 0x135a,
+ 0x1361, 0x137c,
+ 0x13a0, 0x13f4,
+ 0x1401, 0x1676,
+ 0x1680, 0x169c,
+ 0x16a0, 0x16f0,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dc,
+ 0x17e0, 0x17e9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 0x1880, 0x18a9,
+ 0x1e00, 0x1e9b,
+ 0x1ea0, 0x1ef9,
+ 0x1f00, 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, 0x2052,
+ 0x2057, 0x2057,
+ 0x205f, 0x2063,
+ 0x206a, 0x2071,
+ 0x2074, 0x208e,
+ 0x20a0, 0x20b1,
+ 0x20d0, 0x20ea,
+ 0x2100, 0x213a,
+ 0x213d, 0x214b,
+ 0x2153, 0x2183,
+ 0x2190, 0x23ce,
+ 0x2400, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x24fe,
+ 0x2500, 0x2613,
+ 0x2616, 0x2617,
+ 0x2619, 0x267d,
+ 0x2680, 0x2689,
+ 0x2701, 0x2704,
+ 0x2706, 0x2709,
+ 0x270c, 0x2727,
+ 0x2729, 0x274b,
+ 0x274d, 0x274d,
+ 0x274f, 0x2752,
+ 0x2756, 0x2756,
+ 0x2758, 0x275e,
+ 0x2761, 0x2794,
+ 0x2798, 0x27af,
+ 0x27b1, 0x27be,
+ 0x27d0, 0x27eb,
+ 0x27f0, 0x2aff,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312c,
+ 0x3131, 0x318e,
+ 0x3190, 0x31b7,
+ 0x31f0, 0x321c,
+ 0x3220, 0x3243,
+ 0x3251, 0x327b,
+ 0x327f, 0x32cb,
+ 0x32d0, 0x32fe,
+ 0x3300, 0x3376,
+ 0x337b, 0x33dd,
+ 0x33e0, 0x33fe,
+ 0x3400, 0x4db5,
+ 0x4e00, 0x9fa5,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xac00, 0xd7a3,
+ 0xd800, 0xfa2d,
+ 0xfa30, 0xfa6a,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfc,
+ 0xfe00, 0xfe0f,
+ 0xfe20, 0xfe23,
+ 0xfe30, 0xfe46,
+ 0xfe49, 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, 0xffff,
+ 0x10300, 0x1031e,
+ 0x10320, 0x10323,
+ 0x10330, 0x1034a,
+ 0x10400, 0x10425,
+ 0x10428, 0x1044d,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d12a, 0x1d1dd,
+ 0x1d400, 0x1d454,
+ 0x1d456, 0x1d49c,
+ 0x1d49e, 0x1d49f,
+ 0x1d4a2, 0x1d4a2,
+ 0x1d4a5, 0x1d4a6,
+ 0x1d4a9, 0x1d4ac,
+ 0x1d4ae, 0x1d4b9,
+ 0x1d4bb, 0x1d4bb,
+ 0x1d4bd, 0x1d4c0,
+ 0x1d4c2, 0x1d4c3,
+ 0x1d4c5, 0x1d505,
+ 0x1d507, 0x1d50a,
+ 0x1d50d, 0x1d514,
+ 0x1d516, 0x1d51c,
+ 0x1d51e, 0x1d539,
+ 0x1d53b, 0x1d53e,
+ 0x1d540, 0x1d544,
+ 0x1d546, 0x1d546,
+ 0x1d54a, 0x1d550,
+ 0x1d552, 0x1d6a3,
+ 0x1d6a8, 0x1d7c9,
+ 0x1d7ce, 0x1d7ff,
+ 0x1fffe, 0x2a6d6,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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,
+ 0xefffe, 0x10ffff,
+}; /* CR_Age_3_2 */
+
+/* 'Age_4_0': Derived Age 4.0 */
+static const OnigCodePoint CR_Age_4_0[] = {
+ 412,
+ 0x0000, 0x0236,
+ 0x0250, 0x0357,
+ 0x035d, 0x036f,
+ 0x0374, 0x0375,
+ 0x037a, 0x037a,
+ 0x037e, 0x037e,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03ce,
+ 0x03d0, 0x03fb,
+ 0x0400, 0x0486,
+ 0x0488, 0x04ce,
+ 0x04d0, 0x04f5,
+ 0x04f8, 0x04f9,
+ 0x0500, 0x050f,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x0591, 0x05a1,
+ 0x05a3, 0x05b9,
+ 0x05bb, 0x05c4,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x0600, 0x0603,
+ 0x060c, 0x0615,
+ 0x061b, 0x061b,
+ 0x061f, 0x061f,
+ 0x0621, 0x063a,
+ 0x0640, 0x0658,
+ 0x0660, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x074f,
+ 0x0780, 0x07b1,
+ 0x0901, 0x0939,
+ 0x093c, 0x094d,
+ 0x0950, 0x0954,
+ 0x0958, 0x0970,
+ 0x0981, 0x0983,
+ 0x0985, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09a8,
+ 0x09aa, 0x09b0,
+ 0x09b2, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bc, 0x09c4,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09cd,
+ 0x09d7, 0x09d7,
+ 0x09dc, 0x09dd,
+ 0x09df, 0x09e3,
+ 0x09e6, 0x09fa,
+ 0x0a01, 0x0a03,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a3c, 0x0a3c,
+ 0x0a3e, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a66, 0x0a74,
+ 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, 0x0aef,
+ 0x0af1, 0x0af1,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b43,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b61,
+ 0x0b66, 0x0b71,
+ 0x0b82, 0x0b83,
+ 0x0b85, 0x0b8a,
+ 0x0b8e, 0x0b90,
+ 0x0b92, 0x0b95,
+ 0x0b99, 0x0b9a,
+ 0x0b9c, 0x0b9c,
+ 0x0b9e, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb5,
+ 0x0bb7, 0x0bb9,
+ 0x0bbe, 0x0bc2,
+ 0x0bc6, 0x0bc8,
+ 0x0bca, 0x0bcd,
+ 0x0bd7, 0x0bd7,
+ 0x0be7, 0x0bfa,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c3e, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c60, 0x0c61,
+ 0x0c66, 0x0c6f,
+ 0x0c82, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce1,
+ 0x0ce6, 0x0cef,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d28,
+ 0x0d2a, 0x0d39,
+ 0x0d3e, 0x0d43,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4d,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d61,
+ 0x0d66, 0x0d6f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edd,
+ 0x0f00, 0x0f47,
+ 0x0f49, 0x0f6a,
+ 0x0f71, 0x0f8b,
+ 0x0f90, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fbe, 0x0fcc,
+ 0x0fcf, 0x0fcf,
+ 0x1000, 0x1021,
+ 0x1023, 0x1027,
+ 0x1029, 0x102a,
+ 0x102c, 0x1032,
+ 0x1036, 0x1039,
+ 0x1040, 0x1059,
+ 0x10a0, 0x10c5,
+ 0x10d0, 0x10f8,
+ 0x10fb, 0x10fb,
+ 0x1100, 0x1159,
+ 0x115f, 0x11a2,
+ 0x11a8, 0x11f9,
+ 0x1200, 0x1206,
+ 0x1208, 0x1246,
+ 0x1248, 0x1248,
+ 0x124a, 0x124d,
+ 0x1250, 0x1256,
+ 0x1258, 0x1258,
+ 0x125a, 0x125d,
+ 0x1260, 0x1286,
+ 0x1288, 0x1288,
+ 0x128a, 0x128d,
+ 0x1290, 0x12ae,
+ 0x12b0, 0x12b0,
+ 0x12b2, 0x12b5,
+ 0x12b8, 0x12be,
+ 0x12c0, 0x12c0,
+ 0x12c2, 0x12c5,
+ 0x12c8, 0x12ce,
+ 0x12d0, 0x12d6,
+ 0x12d8, 0x12ee,
+ 0x12f0, 0x130e,
+ 0x1310, 0x1310,
+ 0x1312, 0x1315,
+ 0x1318, 0x131e,
+ 0x1320, 0x1346,
+ 0x1348, 0x135a,
+ 0x1361, 0x137c,
+ 0x13a0, 0x13f4,
+ 0x1401, 0x1676,
+ 0x1680, 0x169c,
+ 0x16a0, 0x16f0,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 0x1880, 0x18a9,
+ 0x1900, 0x191c,
+ 0x1920, 0x192b,
+ 0x1930, 0x193b,
+ 0x1940, 0x1940,
+ 0x1944, 0x196d,
+ 0x1970, 0x1974,
+ 0x19e0, 0x19ff,
+ 0x1d00, 0x1d6b,
+ 0x1e00, 0x1e9b,
+ 0x1ea0, 0x1ef9,
+ 0x1f00, 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, 0x2054,
+ 0x2057, 0x2057,
+ 0x205f, 0x2063,
+ 0x206a, 0x2071,
+ 0x2074, 0x208e,
+ 0x20a0, 0x20b1,
+ 0x20d0, 0x20ea,
+ 0x2100, 0x213b,
+ 0x213d, 0x214b,
+ 0x2153, 0x2183,
+ 0x2190, 0x23d0,
+ 0x2400, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x2617,
+ 0x2619, 0x267d,
+ 0x2680, 0x2691,
+ 0x26a0, 0x26a1,
+ 0x2701, 0x2704,
+ 0x2706, 0x2709,
+ 0x270c, 0x2727,
+ 0x2729, 0x274b,
+ 0x274d, 0x274d,
+ 0x274f, 0x2752,
+ 0x2756, 0x2756,
+ 0x2758, 0x275e,
+ 0x2761, 0x2794,
+ 0x2798, 0x27af,
+ 0x27b1, 0x27be,
+ 0x27d0, 0x27eb,
+ 0x27f0, 0x2b0d,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312c,
+ 0x3131, 0x318e,
+ 0x3190, 0x31b7,
+ 0x31f0, 0x321e,
+ 0x3220, 0x3243,
+ 0x3250, 0x327d,
+ 0x327f, 0x32fe,
+ 0x3300, 0x4db5,
+ 0x4dc0, 0x9fa5,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xac00, 0xd7a3,
+ 0xd800, 0xfa2d,
+ 0xfa30, 0xfa6a,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 0xfe0f,
+ 0xfe20, 0xfe23,
+ 0xfe30, 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, 0x1013f,
+ 0x10300, 0x1031e,
+ 0x10320, 0x10323,
+ 0x10330, 0x1034a,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x1039f,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x1083f,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d12a, 0x1d1dd,
+ 0x1d300, 0x1d356,
+ 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, 0x1d6a3,
+ 0x1d6a8, 0x1d7c9,
+ 0x1d7ce, 0x1d7ff,
+ 0x1fffe, 0x2a6d6,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_4_0 */
+
+/* 'Age_4_1': Derived Age 4.1 */
+static const OnigCodePoint CR_Age_4_1[] = {
+ 430,
+ 0x0000, 0x0241,
+ 0x0250, 0x036f,
+ 0x0374, 0x0375,
+ 0x037a, 0x037a,
+ 0x037e, 0x037e,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03ce,
+ 0x03d0, 0x0486,
+ 0x0488, 0x04ce,
+ 0x04d0, 0x04f9,
+ 0x0500, 0x050f,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x0591, 0x05b9,
+ 0x05bb, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x0600, 0x0603,
+ 0x060b, 0x0615,
+ 0x061b, 0x061b,
+ 0x061e, 0x061f,
+ 0x0621, 0x063a,
+ 0x0640, 0x065e,
+ 0x0660, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x076d,
+ 0x0780, 0x07b1,
+ 0x0901, 0x0939,
+ 0x093c, 0x094d,
+ 0x0950, 0x0954,
+ 0x0958, 0x0970,
+ 0x097d, 0x097d,
+ 0x0981, 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, 0x09fa,
+ 0x0a01, 0x0a03,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a3c, 0x0a3c,
+ 0x0a3e, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a66, 0x0a74,
+ 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, 0x0aef,
+ 0x0af1, 0x0af1,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b43,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b61,
+ 0x0b66, 0x0b71,
+ 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,
+ 0x0bd7, 0x0bd7,
+ 0x0be6, 0x0bfa,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c3e, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c60, 0x0c61,
+ 0x0c66, 0x0c6f,
+ 0x0c82, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce1,
+ 0x0ce6, 0x0cef,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d28,
+ 0x0d2a, 0x0d39,
+ 0x0d3e, 0x0d43,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4d,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d61,
+ 0x0d66, 0x0d6f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edd,
+ 0x0f00, 0x0f47,
+ 0x0f49, 0x0f6a,
+ 0x0f71, 0x0f8b,
+ 0x0f90, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fbe, 0x0fcc,
+ 0x0fcf, 0x0fd1,
+ 0x1000, 0x1021,
+ 0x1023, 0x1027,
+ 0x1029, 0x102a,
+ 0x102c, 0x1032,
+ 0x1036, 0x1039,
+ 0x1040, 0x1059,
+ 0x10a0, 0x10c5,
+ 0x10d0, 0x10fc,
+ 0x1100, 0x1159,
+ 0x115f, 0x11a2,
+ 0x11a8, 0x11f9,
+ 0x1200, 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,
+ 0x135f, 0x137c,
+ 0x1380, 0x1399,
+ 0x13a0, 0x13f4,
+ 0x1401, 0x1676,
+ 0x1680, 0x169c,
+ 0x16a0, 0x16f0,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 0x1880, 0x18a9,
+ 0x1900, 0x191c,
+ 0x1920, 0x192b,
+ 0x1930, 0x193b,
+ 0x1940, 0x1940,
+ 0x1944, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19a9,
+ 0x19b0, 0x19c9,
+ 0x19d0, 0x19d9,
+ 0x19de, 0x1a1b,
+ 0x1a1e, 0x1a1f,
+ 0x1d00, 0x1dc3,
+ 0x1e00, 0x1e9b,
+ 0x1ea0, 0x1ef9,
+ 0x1f00, 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, 0x2063,
+ 0x206a, 0x2071,
+ 0x2074, 0x208e,
+ 0x2090, 0x2094,
+ 0x20a0, 0x20b5,
+ 0x20d0, 0x20eb,
+ 0x2100, 0x214c,
+ 0x2153, 0x2183,
+ 0x2190, 0x23db,
+ 0x2400, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x269c,
+ 0x26a0, 0x26b1,
+ 0x2701, 0x2704,
+ 0x2706, 0x2709,
+ 0x270c, 0x2727,
+ 0x2729, 0x274b,
+ 0x274d, 0x274d,
+ 0x274f, 0x2752,
+ 0x2756, 0x2756,
+ 0x2758, 0x275e,
+ 0x2761, 0x2794,
+ 0x2798, 0x27af,
+ 0x27b1, 0x27be,
+ 0x27c0, 0x27c6,
+ 0x27d0, 0x27eb,
+ 0x27f0, 0x2b13,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c80, 0x2cea,
+ 0x2cf9, 0x2d25,
+ 0x2d30, 0x2d65,
+ 0x2d6f, 0x2d6f,
+ 0x2d80, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x2e00, 0x2e17,
+ 0x2e1c, 0x2e1d,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312c,
+ 0x3131, 0x318e,
+ 0x3190, 0x31b7,
+ 0x31c0, 0x31cf,
+ 0x31f0, 0x321e,
+ 0x3220, 0x3243,
+ 0x3250, 0x32fe,
+ 0x3300, 0x4db5,
+ 0x4dc0, 0x9fbb,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa700, 0xa716,
+ 0xa800, 0xa82b,
+ 0xac00, 0xd7a3,
+ 0xd800, 0xfa2d,
+ 0xfa30, 0xfa6a,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 0xfe19,
+ 0xfe20, 0xfe23,
+ 0xfe30, 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, 0x1018a,
+ 0x10300, 0x1031e,
+ 0x10320, 0x10323,
+ 0x10330, 0x1034a,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x103c3,
+ 0x103c8, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x1083f,
+ 0x10a00, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a33,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a47,
+ 0x10a50, 0x10a58,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d12a, 0x1d1dd,
+ 0x1d200, 0x1d245,
+ 0x1d300, 0x1d356,
+ 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, 0x1d7c9,
+ 0x1d7ce, 0x1d7ff,
+ 0x1fffe, 0x2a6d6,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_4_1 */
+
+/* 'Age_5_0': Derived Age 5.0 */
+static const OnigCodePoint CR_Age_5_0[] = {
+ 440,
+ 0x0000, 0x036f,
+ 0x0374, 0x0375,
+ 0x037a, 0x037e,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x03ce,
+ 0x03d0, 0x0486,
+ 0x0488, 0x0513,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x0600, 0x0603,
+ 0x060b, 0x0615,
+ 0x061b, 0x061b,
+ 0x061e, 0x061f,
+ 0x0621, 0x063a,
+ 0x0640, 0x065e,
+ 0x0660, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x076d,
+ 0x0780, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x0901, 0x0939,
+ 0x093c, 0x094d,
+ 0x0950, 0x0954,
+ 0x0958, 0x0970,
+ 0x097b, 0x097f,
+ 0x0981, 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, 0x09fa,
+ 0x0a01, 0x0a03,
+ 0x0a05, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a28,
+ 0x0a2a, 0x0a30,
+ 0x0a32, 0x0a33,
+ 0x0a35, 0x0a36,
+ 0x0a38, 0x0a39,
+ 0x0a3c, 0x0a3c,
+ 0x0a3e, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a59, 0x0a5c,
+ 0x0a5e, 0x0a5e,
+ 0x0a66, 0x0a74,
+ 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, 0x0aef,
+ 0x0af1, 0x0af1,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b43,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b61,
+ 0x0b66, 0x0b71,
+ 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,
+ 0x0bd7, 0x0bd7,
+ 0x0be6, 0x0bfa,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c3e, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c60, 0x0c61,
+ 0x0c66, 0x0c6f,
+ 0x0c82, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d28,
+ 0x0d2a, 0x0d39,
+ 0x0d3e, 0x0d43,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4d,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d61,
+ 0x0d66, 0x0d6f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edd,
+ 0x0f00, 0x0f47,
+ 0x0f49, 0x0f6a,
+ 0x0f71, 0x0f8b,
+ 0x0f90, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fbe, 0x0fcc,
+ 0x0fcf, 0x0fd1,
+ 0x1000, 0x1021,
+ 0x1023, 0x1027,
+ 0x1029, 0x102a,
+ 0x102c, 0x1032,
+ 0x1036, 0x1039,
+ 0x1040, 0x1059,
+ 0x10a0, 0x10c5,
+ 0x10d0, 0x10fc,
+ 0x1100, 0x1159,
+ 0x115f, 0x11a2,
+ 0x11a8, 0x11f9,
+ 0x1200, 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,
+ 0x135f, 0x137c,
+ 0x1380, 0x1399,
+ 0x13a0, 0x13f4,
+ 0x1401, 0x1676,
+ 0x1680, 0x169c,
+ 0x16a0, 0x16f0,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 0x1880, 0x18a9,
+ 0x1900, 0x191c,
+ 0x1920, 0x192b,
+ 0x1930, 0x193b,
+ 0x1940, 0x1940,
+ 0x1944, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19a9,
+ 0x19b0, 0x19c9,
+ 0x19d0, 0x19d9,
+ 0x19de, 0x1a1b,
+ 0x1a1e, 0x1a1f,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1d00, 0x1dca,
+ 0x1dfe, 0x1e9b,
+ 0x1ea0, 0x1ef9,
+ 0x1f00, 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, 0x2063,
+ 0x206a, 0x2071,
+ 0x2074, 0x208e,
+ 0x2090, 0x2094,
+ 0x20a0, 0x20b5,
+ 0x20d0, 0x20ef,
+ 0x2100, 0x214e,
+ 0x2153, 0x2184,
+ 0x2190, 0x23e7,
+ 0x2400, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x269c,
+ 0x26a0, 0x26b2,
+ 0x2701, 0x2704,
+ 0x2706, 0x2709,
+ 0x270c, 0x2727,
+ 0x2729, 0x274b,
+ 0x274d, 0x274d,
+ 0x274f, 0x2752,
+ 0x2756, 0x2756,
+ 0x2758, 0x275e,
+ 0x2761, 0x2794,
+ 0x2798, 0x27af,
+ 0x27b1, 0x27be,
+ 0x27c0, 0x27ca,
+ 0x27d0, 0x27eb,
+ 0x27f0, 0x2b1a,
+ 0x2b20, 0x2b23,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2c6c,
+ 0x2c74, 0x2c77,
+ 0x2c80, 0x2cea,
+ 0x2cf9, 0x2d25,
+ 0x2d30, 0x2d65,
+ 0x2d6f, 0x2d6f,
+ 0x2d80, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x2e00, 0x2e17,
+ 0x2e1c, 0x2e1d,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312c,
+ 0x3131, 0x318e,
+ 0x3190, 0x31b7,
+ 0x31c0, 0x31cf,
+ 0x31f0, 0x321e,
+ 0x3220, 0x3243,
+ 0x3250, 0x32fe,
+ 0x3300, 0x4db5,
+ 0x4dc0, 0x9fbb,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa700, 0xa71a,
+ 0xa720, 0xa721,
+ 0xa800, 0xa82b,
+ 0xa840, 0xa877,
+ 0xac00, 0xd7a3,
+ 0xd800, 0xfa2d,
+ 0xfa30, 0xfa6a,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 0xfe19,
+ 0xfe20, 0xfe23,
+ 0xfe30, 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, 0x1018a,
+ 0x10300, 0x1031e,
+ 0x10320, 0x10323,
+ 0x10330, 0x1034a,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x103c3,
+ 0x103c8, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x1083f,
+ 0x10900, 0x10919,
+ 0x1091f, 0x1091f,
+ 0x10a00, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a33,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a47,
+ 0x10a50, 0x10a58,
+ 0x12000, 0x1236e,
+ 0x12400, 0x12462,
+ 0x12470, 0x12473,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d12a, 0x1d1dd,
+ 0x1d200, 0x1d245,
+ 0x1d300, 0x1d356,
+ 0x1d360, 0x1d371,
+ 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, 0x1d7ff,
+ 0x1fffe, 0x2a6d6,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_5_0 */
+
+/* 'Age_5_1': Derived Age 5.1 */
+static const OnigCodePoint CR_Age_5_1[] = {
+ 455,
+ 0x0000, 0x0377,
+ 0x037a, 0x037e,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x0523,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x0600, 0x0603,
+ 0x0606, 0x061b,
+ 0x061e, 0x061f,
+ 0x0621, 0x065e,
+ 0x0660, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x0901, 0x0939,
+ 0x093c, 0x094d,
+ 0x0950, 0x0954,
+ 0x0958, 0x0972,
+ 0x097b, 0x097f,
+ 0x0981, 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, 0x09fa,
+ 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, 0x0a75,
+ 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, 0x0aef,
+ 0x0af1, 0x0af1,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b63,
+ 0x0b66, 0x0b71,
+ 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,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c59,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c78, 0x0c7f,
+ 0x0c82, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d28,
+ 0x0d2a, 0x0d39,
+ 0x0d3d, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4d,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d63,
+ 0x0d66, 0x0d75,
+ 0x0d79, 0x0d7f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edd,
+ 0x0f00, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f71, 0x0f8b,
+ 0x0f90, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fbe, 0x0fcc,
+ 0x0fce, 0x0fd4,
+ 0x1000, 0x1099,
+ 0x109e, 0x10c5,
+ 0x10d0, 0x10fc,
+ 0x1100, 0x1159,
+ 0x115f, 0x11a2,
+ 0x11a8, 0x11f9,
+ 0x1200, 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,
+ 0x135f, 0x137c,
+ 0x1380, 0x1399,
+ 0x13a0, 0x13f4,
+ 0x1401, 0x1676,
+ 0x1680, 0x169c,
+ 0x16a0, 0x16f0,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 0x1880, 0x18aa,
+ 0x1900, 0x191c,
+ 0x1920, 0x192b,
+ 0x1930, 0x193b,
+ 0x1940, 0x1940,
+ 0x1944, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19a9,
+ 0x19b0, 0x19c9,
+ 0x19d0, 0x19d9,
+ 0x19de, 0x1a1b,
+ 0x1a1e, 0x1a1f,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1baa,
+ 0x1bae, 0x1bb9,
+ 0x1c00, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c7f,
+ 0x1d00, 0x1de6,
+ 0x1dfe, 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,
+ 0x206a, 0x2071,
+ 0x2074, 0x208e,
+ 0x2090, 0x2094,
+ 0x20a0, 0x20b5,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x214f,
+ 0x2153, 0x2188,
+ 0x2190, 0x23e7,
+ 0x2400, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x269d,
+ 0x26a0, 0x26bc,
+ 0x26c0, 0x26c3,
+ 0x2701, 0x2704,
+ 0x2706, 0x2709,
+ 0x270c, 0x2727,
+ 0x2729, 0x274b,
+ 0x274d, 0x274d,
+ 0x274f, 0x2752,
+ 0x2756, 0x2756,
+ 0x2758, 0x275e,
+ 0x2761, 0x2794,
+ 0x2798, 0x27af,
+ 0x27b1, 0x27be,
+ 0x27c0, 0x27ca,
+ 0x27cc, 0x27cc,
+ 0x27d0, 0x2b4c,
+ 0x2b50, 0x2b54,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2c6f,
+ 0x2c71, 0x2c7d,
+ 0x2c80, 0x2cea,
+ 0x2cf9, 0x2d25,
+ 0x2d30, 0x2d65,
+ 0x2d6f, 0x2d6f,
+ 0x2d80, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x2de0, 0x2e30,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312d,
+ 0x3131, 0x318e,
+ 0x3190, 0x31b7,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x3243,
+ 0x3250, 0x32fe,
+ 0x3300, 0x4db5,
+ 0x4dc0, 0x9fc3,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa500, 0xa62b,
+ 0xa640, 0xa65f,
+ 0xa662, 0xa673,
+ 0xa67c, 0xa697,
+ 0xa700, 0xa78c,
+ 0xa7fb, 0xa82b,
+ 0xa840, 0xa877,
+ 0xa880, 0xa8c4,
+ 0xa8ce, 0xa8d9,
+ 0xa900, 0xa953,
+ 0xa95f, 0xa95f,
+ 0xaa00, 0xaa36,
+ 0xaa40, 0xaa4d,
+ 0xaa50, 0xaa59,
+ 0xaa5c, 0xaa5f,
+ 0xac00, 0xd7a3,
+ 0xd800, 0xfa2d,
+ 0xfa30, 0xfa6a,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 0xfe19,
+ 0xfe20, 0xfe26,
+ 0xfe30, 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, 0x1018a,
+ 0x10190, 0x1019b,
+ 0x101d0, 0x101fd,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x10300, 0x1031e,
+ 0x10320, 0x10323,
+ 0x10330, 0x1034a,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x103c3,
+ 0x103c8, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x1083f,
+ 0x10900, 0x10919,
+ 0x1091f, 0x10939,
+ 0x1093f, 0x1093f,
+ 0x10a00, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a33,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a47,
+ 0x10a50, 0x10a58,
+ 0x12000, 0x1236e,
+ 0x12400, 0x12462,
+ 0x12470, 0x12473,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1dd,
+ 0x1d200, 0x1d245,
+ 0x1d300, 0x1d356,
+ 0x1d360, 0x1d371,
+ 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, 0x1d7ff,
+ 0x1f000, 0x1f02b,
+ 0x1f030, 0x1f093,
+ 0x1fffe, 0x2a6d6,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_5_1 */
+
+/* 'Age_5_2': Derived Age 5.2 */
+static const OnigCodePoint CR_Age_5_2[] = {
+ 495,
+ 0x0000, 0x0377,
+ 0x037a, 0x037e,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x0525,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x0600, 0x0603,
+ 0x0606, 0x061b,
+ 0x061e, 0x061f,
+ 0x0621, 0x065e,
+ 0x0660, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x0800, 0x082d,
+ 0x0830, 0x083e,
+ 0x0900, 0x0939,
+ 0x093c, 0x094e,
+ 0x0950, 0x0955,
+ 0x0958, 0x0972,
+ 0x0979, 0x097f,
+ 0x0981, 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, 0x09fb,
+ 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, 0x0a75,
+ 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, 0x0aef,
+ 0x0af1, 0x0af1,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b5c, 0x0b5d,
+ 0x0b5f, 0x0b63,
+ 0x0b66, 0x0b71,
+ 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,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c59,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c78, 0x0c7f,
+ 0x0c82, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d28,
+ 0x0d2a, 0x0d39,
+ 0x0d3d, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4d,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d63,
+ 0x0d66, 0x0d75,
+ 0x0d79, 0x0d7f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edd,
+ 0x0f00, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f71, 0x0f8b,
+ 0x0f90, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fbe, 0x0fcc,
+ 0x0fce, 0x0fd8,
+ 0x1000, 0x10c5,
+ 0x10d0, 0x10fc,
+ 0x1100, 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,
+ 0x135f, 0x137c,
+ 0x1380, 0x1399,
+ 0x13a0, 0x13f4,
+ 0x1400, 0x169c,
+ 0x16a0, 0x16f0,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 0x1880, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191c,
+ 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,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1baa,
+ 0x1bae, 0x1bb9,
+ 0x1c00, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c7f,
+ 0x1cd0, 0x1cf2,
+ 0x1d00, 0x1de6,
+ 0x1dfd, 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,
+ 0x206a, 0x2071,
+ 0x2074, 0x208e,
+ 0x2090, 0x2094,
+ 0x20a0, 0x20b8,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x2189,
+ 0x2190, 0x23e8,
+ 0x2400, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x26cd,
+ 0x26cf, 0x26e1,
+ 0x26e3, 0x26e3,
+ 0x26e8, 0x26ff,
+ 0x2701, 0x2704,
+ 0x2706, 0x2709,
+ 0x270c, 0x2727,
+ 0x2729, 0x274b,
+ 0x274d, 0x274d,
+ 0x274f, 0x2752,
+ 0x2756, 0x275e,
+ 0x2761, 0x2794,
+ 0x2798, 0x27af,
+ 0x27b1, 0x27be,
+ 0x27c0, 0x27ca,
+ 0x27cc, 0x27cc,
+ 0x27d0, 0x2b4c,
+ 0x2b50, 0x2b59,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2cf1,
+ 0x2cf9, 0x2d25,
+ 0x2d30, 0x2d65,
+ 0x2d6f, 0x2d6f,
+ 0x2d80, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x2de0, 0x2e31,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312d,
+ 0x3131, 0x318e,
+ 0x3190, 0x31b7,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x32fe,
+ 0x3300, 0x4db5,
+ 0x4dc0, 0x9fcb,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa65f,
+ 0xa662, 0xa673,
+ 0xa67c, 0xa697,
+ 0xa6a0, 0xa6f7,
+ 0xa700, 0xa78c,
+ 0xa7fb, 0xa82b,
+ 0xa830, 0xa839,
+ 0xa840, 0xa877,
+ 0xa880, 0xa8c4,
+ 0xa8ce, 0xa8d9,
+ 0xa8e0, 0xa8fb,
+ 0xa900, 0xa953,
+ 0xa95f, 0xa97c,
+ 0xa980, 0xa9cd,
+ 0xa9cf, 0xa9d9,
+ 0xa9de, 0xa9df,
+ 0xaa00, 0xaa36,
+ 0xaa40, 0xaa4d,
+ 0xaa50, 0xaa59,
+ 0xaa5c, 0xaa7b,
+ 0xaa80, 0xaac2,
+ 0xaadb, 0xaadf,
+ 0xabc0, 0xabed,
+ 0xabf0, 0xabf9,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xd800, 0xfa2d,
+ 0xfa30, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbb1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 0xfe19,
+ 0xfe20, 0xfe26,
+ 0xfe30, 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, 0x1018a,
+ 0x10190, 0x1019b,
+ 0x101d0, 0x101fd,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x10300, 0x1031e,
+ 0x10320, 0x10323,
+ 0x10330, 0x1034a,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x103c3,
+ 0x103c8, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10857, 0x1085f,
+ 0x10900, 0x1091b,
+ 0x1091f, 0x10939,
+ 0x1093f, 0x1093f,
+ 0x10a00, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a33,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a47,
+ 0x10a50, 0x10a58,
+ 0x10a60, 0x10a7f,
+ 0x10b00, 0x10b35,
+ 0x10b39, 0x10b55,
+ 0x10b58, 0x10b72,
+ 0x10b78, 0x10b7f,
+ 0x10c00, 0x10c48,
+ 0x10e60, 0x10e7e,
+ 0x11080, 0x110c1,
+ 0x12000, 0x1236e,
+ 0x12400, 0x12462,
+ 0x12470, 0x12473,
+ 0x13000, 0x1342e,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1dd,
+ 0x1d200, 0x1d245,
+ 0x1d300, 0x1d356,
+ 0x1d360, 0x1d371,
+ 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, 0x1d7ff,
+ 0x1f000, 0x1f02b,
+ 0x1f030, 0x1f093,
+ 0x1f100, 0x1f10a,
+ 0x1f110, 0x1f12e,
+ 0x1f131, 0x1f131,
+ 0x1f13d, 0x1f13d,
+ 0x1f13f, 0x1f13f,
+ 0x1f142, 0x1f142,
+ 0x1f146, 0x1f146,
+ 0x1f14a, 0x1f14e,
+ 0x1f157, 0x1f157,
+ 0x1f15f, 0x1f15f,
+ 0x1f179, 0x1f179,
+ 0x1f17b, 0x1f17c,
+ 0x1f17f, 0x1f17f,
+ 0x1f18a, 0x1f18d,
+ 0x1f190, 0x1f190,
+ 0x1f200, 0x1f200,
+ 0x1f210, 0x1f231,
+ 0x1f240, 0x1f248,
+ 0x1fffe, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_5_2 */
+
+/* 'Age_6_0': Derived Age 6.0 */
+static const OnigCodePoint CR_Age_6_0[] = {
+ 511,
+ 0x0000, 0x0377,
+ 0x037a, 0x037e,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x0527,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x0600, 0x0603,
+ 0x0606, 0x061b,
+ 0x061e, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x0800, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x0900, 0x0977,
+ 0x0979, 0x097f,
+ 0x0981, 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, 0x09fb,
+ 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, 0x0a75,
+ 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, 0x0aef,
+ 0x0af1, 0x0af1,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 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,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c59,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c78, 0x0c7f,
+ 0x0c82, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d3a,
+ 0x0d3d, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4e,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d63,
+ 0x0d66, 0x0d75,
+ 0x0d79, 0x0d7f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 0x0ed0, 0x0ed9,
+ 0x0edc, 0x0edd,
+ 0x0f00, 0x0f47,
+ 0x0f49, 0x0f6c,
+ 0x0f71, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fbe, 0x0fcc,
+ 0x0fce, 0x0fda,
+ 0x1000, 0x10c5,
+ 0x10d0, 0x10fc,
+ 0x1100, 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, 0x13f4,
+ 0x1400, 0x169c,
+ 0x16a0, 0x16f0,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 0x1880, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191c,
+ 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,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1baa,
+ 0x1bae, 0x1bb9,
+ 0x1bc0, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c7f,
+ 0x1cd0, 0x1cf2,
+ 0x1d00, 0x1de6,
+ 0x1dfc, 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,
+ 0x206a, 0x2071,
+ 0x2074, 0x208e,
+ 0x2090, 0x209c,
+ 0x20a0, 0x20b9,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x2189,
+ 0x2190, 0x23f3,
+ 0x2400, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x26ff,
+ 0x2701, 0x27ca,
+ 0x27cc, 0x27cc,
+ 0x27ce, 0x2b4c,
+ 0x2b50, 0x2b59,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 0x2cf1,
+ 0x2cf9, 0x2d25,
+ 0x2d30, 0x2d65,
+ 0x2d6f, 0x2d70,
+ 0x2d7f, 0x2d96,
+ 0x2da0, 0x2da6,
+ 0x2da8, 0x2dae,
+ 0x2db0, 0x2db6,
+ 0x2db8, 0x2dbe,
+ 0x2dc0, 0x2dc6,
+ 0x2dc8, 0x2dce,
+ 0x2dd0, 0x2dd6,
+ 0x2dd8, 0x2dde,
+ 0x2de0, 0x2e31,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312d,
+ 0x3131, 0x318e,
+ 0x3190, 0x31ba,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x32fe,
+ 0x3300, 0x4db5,
+ 0x4dc0, 0x9fcb,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa673,
+ 0xa67c, 0xa697,
+ 0xa6a0, 0xa6f7,
+ 0xa700, 0xa78e,
+ 0xa790, 0xa791,
+ 0xa7a0, 0xa7a9,
+ 0xa7fa, 0xa82b,
+ 0xa830, 0xa839,
+ 0xa840, 0xa877,
+ 0xa880, 0xa8c4,
+ 0xa8ce, 0xa8d9,
+ 0xa8e0, 0xa8fb,
+ 0xa900, 0xa953,
+ 0xa95f, 0xa97c,
+ 0xa980, 0xa9cd,
+ 0xa9cf, 0xa9d9,
+ 0xa9de, 0xa9df,
+ 0xaa00, 0xaa36,
+ 0xaa40, 0xaa4d,
+ 0xaa50, 0xaa59,
+ 0xaa5c, 0xaa7b,
+ 0xaa80, 0xaac2,
+ 0xaadb, 0xaadf,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xabc0, 0xabed,
+ 0xabf0, 0xabf9,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xd800, 0xfa2d,
+ 0xfa30, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfb36,
+ 0xfb38, 0xfb3c,
+ 0xfb3e, 0xfb3e,
+ 0xfb40, 0xfb41,
+ 0xfb43, 0xfb44,
+ 0xfb46, 0xfbc1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 0xfe19,
+ 0xfe20, 0xfe26,
+ 0xfe30, 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, 0x1018a,
+ 0x10190, 0x1019b,
+ 0x101d0, 0x101fd,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x10300, 0x1031e,
+ 0x10320, 0x10323,
+ 0x10330, 0x1034a,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x103c3,
+ 0x103c8, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10857, 0x1085f,
+ 0x10900, 0x1091b,
+ 0x1091f, 0x10939,
+ 0x1093f, 0x1093f,
+ 0x10a00, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a33,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a47,
+ 0x10a50, 0x10a58,
+ 0x10a60, 0x10a7f,
+ 0x10b00, 0x10b35,
+ 0x10b39, 0x10b55,
+ 0x10b58, 0x10b72,
+ 0x10b78, 0x10b7f,
+ 0x10c00, 0x10c48,
+ 0x10e60, 0x10e7e,
+ 0x11000, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x11080, 0x110c1,
+ 0x12000, 0x1236e,
+ 0x12400, 0x12462,
+ 0x12470, 0x12473,
+ 0x13000, 0x1342e,
+ 0x16800, 0x16a38,
+ 0x1b000, 0x1b001,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1dd,
+ 0x1d200, 0x1d245,
+ 0x1d300, 0x1d356,
+ 0x1d360, 0x1d371,
+ 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, 0x1d7ff,
+ 0x1f000, 0x1f02b,
+ 0x1f030, 0x1f093,
+ 0x1f0a0, 0x1f0ae,
+ 0x1f0b1, 0x1f0be,
+ 0x1f0c1, 0x1f0cf,
+ 0x1f0d1, 0x1f0df,
+ 0x1f100, 0x1f10a,
+ 0x1f110, 0x1f12e,
+ 0x1f130, 0x1f169,
+ 0x1f170, 0x1f19a,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23a,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f300, 0x1f320,
+ 0x1f330, 0x1f335,
+ 0x1f337, 0x1f37c,
+ 0x1f380, 0x1f393,
+ 0x1f3a0, 0x1f3c4,
+ 0x1f3c6, 0x1f3ca,
+ 0x1f3e0, 0x1f3f0,
+ 0x1f400, 0x1f43e,
+ 0x1f440, 0x1f440,
+ 0x1f442, 0x1f4f7,
+ 0x1f4f9, 0x1f4fc,
+ 0x1f500, 0x1f53d,
+ 0x1f550, 0x1f567,
+ 0x1f5fb, 0x1f5ff,
+ 0x1f601, 0x1f610,
+ 0x1f612, 0x1f614,
+ 0x1f616, 0x1f616,
+ 0x1f618, 0x1f618,
+ 0x1f61a, 0x1f61a,
+ 0x1f61c, 0x1f61e,
+ 0x1f620, 0x1f625,
+ 0x1f628, 0x1f62b,
+ 0x1f62d, 0x1f62d,
+ 0x1f630, 0x1f633,
+ 0x1f635, 0x1f640,
+ 0x1f645, 0x1f64f,
+ 0x1f680, 0x1f6c5,
+ 0x1f700, 0x1f773,
+ 0x1fffe, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_6_0 */
+
+/* 'Age_6_1': Derived Age 6.1 */
+static const OnigCodePoint CR_Age_6_1[] = {
+ 549,
+ 0x0000, 0x0377,
+ 0x037a, 0x037e,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x0527,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x058f, 0x058f,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x0600, 0x0604,
+ 0x0606, 0x061b,
+ 0x061e, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x0800, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x08a0, 0x08a0,
+ 0x08a2, 0x08ac,
+ 0x08e4, 0x08fe,
+ 0x0900, 0x0977,
+ 0x0979, 0x097f,
+ 0x0981, 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, 0x09fb,
+ 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, 0x0a75,
+ 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,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 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,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c59,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c78, 0x0c7f,
+ 0x0c82, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d3a,
+ 0x0d3d, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4e,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d63,
+ 0x0d66, 0x0d75,
+ 0x0d79, 0x0d7f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 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, 0x13f4,
+ 0x1400, 0x169c,
+ 0x16a0, 0x16f0,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 0x1880, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191c,
+ 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,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c7f,
+ 0x1cc0, 0x1cc7,
+ 0x1cd0, 0x1cf6,
+ 0x1d00, 0x1de6,
+ 0x1dfc, 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,
+ 0x206a, 0x2071,
+ 0x2074, 0x208e,
+ 0x2090, 0x209c,
+ 0x20a0, 0x20b9,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x2189,
+ 0x2190, 0x23f3,
+ 0x2400, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x26ff,
+ 0x2701, 0x2b4c,
+ 0x2b50, 0x2b59,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 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, 0x2e3b,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312d,
+ 0x3131, 0x318e,
+ 0x3190, 0x31ba,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x32fe,
+ 0x3300, 0x4db5,
+ 0x4dc0, 0x9fcc,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa697,
+ 0xa69f, 0xa6f7,
+ 0xa700, 0xa78e,
+ 0xa790, 0xa793,
+ 0xa7a0, 0xa7aa,
+ 0xa7f8, 0xa82b,
+ 0xa830, 0xa839,
+ 0xa840, 0xa877,
+ 0xa880, 0xa8c4,
+ 0xa8ce, 0xa8d9,
+ 0xa8e0, 0xa8fb,
+ 0xa900, 0xa953,
+ 0xa95f, 0xa97c,
+ 0xa980, 0xa9cd,
+ 0xa9cf, 0xa9d9,
+ 0xa9de, 0xa9df,
+ 0xaa00, 0xaa36,
+ 0xaa40, 0xaa4d,
+ 0xaa50, 0xaa59,
+ 0xaa5c, 0xaa7b,
+ 0xaa80, 0xaac2,
+ 0xaadb, 0xaaf6,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xabc0, 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, 0xfbc1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 0xfe19,
+ 0xfe20, 0xfe26,
+ 0xfe30, 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, 0x1018a,
+ 0x10190, 0x1019b,
+ 0x101d0, 0x101fd,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x10300, 0x1031e,
+ 0x10320, 0x10323,
+ 0x10330, 0x1034a,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x103c3,
+ 0x103c8, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10857, 0x1085f,
+ 0x10900, 0x1091b,
+ 0x1091f, 0x10939,
+ 0x1093f, 0x1093f,
+ 0x10980, 0x109b7,
+ 0x109be, 0x109bf,
+ 0x10a00, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a33,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a47,
+ 0x10a50, 0x10a58,
+ 0x10a60, 0x10a7f,
+ 0x10b00, 0x10b35,
+ 0x10b39, 0x10b55,
+ 0x10b58, 0x10b72,
+ 0x10b78, 0x10b7f,
+ 0x10c00, 0x10c48,
+ 0x10e60, 0x10e7e,
+ 0x11000, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x11080, 0x110c1,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x11143,
+ 0x11180, 0x111c8,
+ 0x111d0, 0x111d9,
+ 0x11680, 0x116b7,
+ 0x116c0, 0x116c9,
+ 0x12000, 0x1236e,
+ 0x12400, 0x12462,
+ 0x12470, 0x12473,
+ 0x13000, 0x1342e,
+ 0x16800, 0x16a38,
+ 0x16f00, 0x16f44,
+ 0x16f50, 0x16f7e,
+ 0x16f8f, 0x16f9f,
+ 0x1b000, 0x1b001,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1dd,
+ 0x1d200, 0x1d245,
+ 0x1d300, 0x1d356,
+ 0x1d360, 0x1d371,
+ 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, 0x1d7ff,
+ 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, 0x1f0be,
+ 0x1f0c1, 0x1f0cf,
+ 0x1f0d1, 0x1f0df,
+ 0x1f100, 0x1f10a,
+ 0x1f110, 0x1f12e,
+ 0x1f130, 0x1f16b,
+ 0x1f170, 0x1f19a,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23a,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f300, 0x1f320,
+ 0x1f330, 0x1f335,
+ 0x1f337, 0x1f37c,
+ 0x1f380, 0x1f393,
+ 0x1f3a0, 0x1f3c4,
+ 0x1f3c6, 0x1f3ca,
+ 0x1f3e0, 0x1f3f0,
+ 0x1f400, 0x1f43e,
+ 0x1f440, 0x1f440,
+ 0x1f442, 0x1f4f7,
+ 0x1f4f9, 0x1f4fc,
+ 0x1f500, 0x1f53d,
+ 0x1f540, 0x1f543,
+ 0x1f550, 0x1f567,
+ 0x1f5fb, 0x1f640,
+ 0x1f645, 0x1f64f,
+ 0x1f680, 0x1f6c5,
+ 0x1f700, 0x1f773,
+ 0x1fffe, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_6_1 */
+
+/* 'Age_6_2': Derived Age 6.2 */
+static const OnigCodePoint CR_Age_6_2[] = {
+ 549,
+ 0x0000, 0x0377,
+ 0x037a, 0x037e,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x0527,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x058f, 0x058f,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x0600, 0x0604,
+ 0x0606, 0x061b,
+ 0x061e, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x0800, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x08a0, 0x08a0,
+ 0x08a2, 0x08ac,
+ 0x08e4, 0x08fe,
+ 0x0900, 0x0977,
+ 0x0979, 0x097f,
+ 0x0981, 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, 0x09fb,
+ 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, 0x0a75,
+ 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,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 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,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c59,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c78, 0x0c7f,
+ 0x0c82, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d3a,
+ 0x0d3d, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4e,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d63,
+ 0x0d66, 0x0d75,
+ 0x0d79, 0x0d7f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 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, 0x13f4,
+ 0x1400, 0x169c,
+ 0x16a0, 0x16f0,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 0x1880, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191c,
+ 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,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c7f,
+ 0x1cc0, 0x1cc7,
+ 0x1cd0, 0x1cf6,
+ 0x1d00, 0x1de6,
+ 0x1dfc, 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,
+ 0x206a, 0x2071,
+ 0x2074, 0x208e,
+ 0x2090, 0x209c,
+ 0x20a0, 0x20ba,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x2189,
+ 0x2190, 0x23f3,
+ 0x2400, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x26ff,
+ 0x2701, 0x2b4c,
+ 0x2b50, 0x2b59,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 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, 0x2e3b,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312d,
+ 0x3131, 0x318e,
+ 0x3190, 0x31ba,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x32fe,
+ 0x3300, 0x4db5,
+ 0x4dc0, 0x9fcc,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa697,
+ 0xa69f, 0xa6f7,
+ 0xa700, 0xa78e,
+ 0xa790, 0xa793,
+ 0xa7a0, 0xa7aa,
+ 0xa7f8, 0xa82b,
+ 0xa830, 0xa839,
+ 0xa840, 0xa877,
+ 0xa880, 0xa8c4,
+ 0xa8ce, 0xa8d9,
+ 0xa8e0, 0xa8fb,
+ 0xa900, 0xa953,
+ 0xa95f, 0xa97c,
+ 0xa980, 0xa9cd,
+ 0xa9cf, 0xa9d9,
+ 0xa9de, 0xa9df,
+ 0xaa00, 0xaa36,
+ 0xaa40, 0xaa4d,
+ 0xaa50, 0xaa59,
+ 0xaa5c, 0xaa7b,
+ 0xaa80, 0xaac2,
+ 0xaadb, 0xaaf6,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xabc0, 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, 0xfbc1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 0xfe19,
+ 0xfe20, 0xfe26,
+ 0xfe30, 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, 0x1018a,
+ 0x10190, 0x1019b,
+ 0x101d0, 0x101fd,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x10300, 0x1031e,
+ 0x10320, 0x10323,
+ 0x10330, 0x1034a,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x103c3,
+ 0x103c8, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10857, 0x1085f,
+ 0x10900, 0x1091b,
+ 0x1091f, 0x10939,
+ 0x1093f, 0x1093f,
+ 0x10980, 0x109b7,
+ 0x109be, 0x109bf,
+ 0x10a00, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a33,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a47,
+ 0x10a50, 0x10a58,
+ 0x10a60, 0x10a7f,
+ 0x10b00, 0x10b35,
+ 0x10b39, 0x10b55,
+ 0x10b58, 0x10b72,
+ 0x10b78, 0x10b7f,
+ 0x10c00, 0x10c48,
+ 0x10e60, 0x10e7e,
+ 0x11000, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x11080, 0x110c1,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x11143,
+ 0x11180, 0x111c8,
+ 0x111d0, 0x111d9,
+ 0x11680, 0x116b7,
+ 0x116c0, 0x116c9,
+ 0x12000, 0x1236e,
+ 0x12400, 0x12462,
+ 0x12470, 0x12473,
+ 0x13000, 0x1342e,
+ 0x16800, 0x16a38,
+ 0x16f00, 0x16f44,
+ 0x16f50, 0x16f7e,
+ 0x16f8f, 0x16f9f,
+ 0x1b000, 0x1b001,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1dd,
+ 0x1d200, 0x1d245,
+ 0x1d300, 0x1d356,
+ 0x1d360, 0x1d371,
+ 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, 0x1d7ff,
+ 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, 0x1f0be,
+ 0x1f0c1, 0x1f0cf,
+ 0x1f0d1, 0x1f0df,
+ 0x1f100, 0x1f10a,
+ 0x1f110, 0x1f12e,
+ 0x1f130, 0x1f16b,
+ 0x1f170, 0x1f19a,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23a,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f300, 0x1f320,
+ 0x1f330, 0x1f335,
+ 0x1f337, 0x1f37c,
+ 0x1f380, 0x1f393,
+ 0x1f3a0, 0x1f3c4,
+ 0x1f3c6, 0x1f3ca,
+ 0x1f3e0, 0x1f3f0,
+ 0x1f400, 0x1f43e,
+ 0x1f440, 0x1f440,
+ 0x1f442, 0x1f4f7,
+ 0x1f4f9, 0x1f4fc,
+ 0x1f500, 0x1f53d,
+ 0x1f540, 0x1f543,
+ 0x1f550, 0x1f567,
+ 0x1f5fb, 0x1f640,
+ 0x1f645, 0x1f64f,
+ 0x1f680, 0x1f6c5,
+ 0x1f700, 0x1f773,
+ 0x1fffe, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_6_2 */
+
+/* 'Age_6_3': Derived Age 6.3 */
+static const OnigCodePoint CR_Age_6_3[] = {
+ 549,
+ 0x0000, 0x0377,
+ 0x037a, 0x037e,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x0527,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x058f, 0x058f,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x0600, 0x0604,
+ 0x0606, 0x061c,
+ 0x061e, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x0800, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x08a0, 0x08a0,
+ 0x08a2, 0x08ac,
+ 0x08e4, 0x08fe,
+ 0x0900, 0x0977,
+ 0x0979, 0x097f,
+ 0x0981, 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, 0x09fb,
+ 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, 0x0a75,
+ 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,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 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,
+ 0x0c01, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c33,
+ 0x0c35, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c59,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c78, 0x0c7f,
+ 0x0c82, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d02, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d3a,
+ 0x0d3d, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4e,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d63,
+ 0x0d66, 0x0d75,
+ 0x0d79, 0x0d7f,
+ 0x0d82, 0x0d83,
+ 0x0d85, 0x0d96,
+ 0x0d9a, 0x0db1,
+ 0x0db3, 0x0dbb,
+ 0x0dbd, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0dd8, 0x0ddf,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0e82,
+ 0x0e84, 0x0e84,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 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, 0x13f4,
+ 0x1400, 0x169c,
+ 0x16a0, 0x16f0,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 0x1880, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x191c,
+ 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,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c7f,
+ 0x1cc0, 0x1cc7,
+ 0x1cd0, 0x1cf6,
+ 0x1d00, 0x1de6,
+ 0x1dfc, 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, 0x20ba,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x2189,
+ 0x2190, 0x23f3,
+ 0x2400, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x26ff,
+ 0x2701, 0x2b4c,
+ 0x2b50, 0x2b59,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 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, 0x2e3b,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312d,
+ 0x3131, 0x318e,
+ 0x3190, 0x31ba,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x32fe,
+ 0x3300, 0x4db5,
+ 0x4dc0, 0x9fcc,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa697,
+ 0xa69f, 0xa6f7,
+ 0xa700, 0xa78e,
+ 0xa790, 0xa793,
+ 0xa7a0, 0xa7aa,
+ 0xa7f8, 0xa82b,
+ 0xa830, 0xa839,
+ 0xa840, 0xa877,
+ 0xa880, 0xa8c4,
+ 0xa8ce, 0xa8d9,
+ 0xa8e0, 0xa8fb,
+ 0xa900, 0xa953,
+ 0xa95f, 0xa97c,
+ 0xa980, 0xa9cd,
+ 0xa9cf, 0xa9d9,
+ 0xa9de, 0xa9df,
+ 0xaa00, 0xaa36,
+ 0xaa40, 0xaa4d,
+ 0xaa50, 0xaa59,
+ 0xaa5c, 0xaa7b,
+ 0xaa80, 0xaac2,
+ 0xaadb, 0xaaf6,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab26,
+ 0xab28, 0xab2e,
+ 0xabc0, 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, 0xfbc1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 0xfe19,
+ 0xfe20, 0xfe26,
+ 0xfe30, 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, 0x1018a,
+ 0x10190, 0x1019b,
+ 0x101d0, 0x101fd,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x10300, 0x1031e,
+ 0x10320, 0x10323,
+ 0x10330, 0x1034a,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x103c3,
+ 0x103c8, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10857, 0x1085f,
+ 0x10900, 0x1091b,
+ 0x1091f, 0x10939,
+ 0x1093f, 0x1093f,
+ 0x10980, 0x109b7,
+ 0x109be, 0x109bf,
+ 0x10a00, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a33,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a47,
+ 0x10a50, 0x10a58,
+ 0x10a60, 0x10a7f,
+ 0x10b00, 0x10b35,
+ 0x10b39, 0x10b55,
+ 0x10b58, 0x10b72,
+ 0x10b78, 0x10b7f,
+ 0x10c00, 0x10c48,
+ 0x10e60, 0x10e7e,
+ 0x11000, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x11080, 0x110c1,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x11143,
+ 0x11180, 0x111c8,
+ 0x111d0, 0x111d9,
+ 0x11680, 0x116b7,
+ 0x116c0, 0x116c9,
+ 0x12000, 0x1236e,
+ 0x12400, 0x12462,
+ 0x12470, 0x12473,
+ 0x13000, 0x1342e,
+ 0x16800, 0x16a38,
+ 0x16f00, 0x16f44,
+ 0x16f50, 0x16f7e,
+ 0x16f8f, 0x16f9f,
+ 0x1b000, 0x1b001,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1dd,
+ 0x1d200, 0x1d245,
+ 0x1d300, 0x1d356,
+ 0x1d360, 0x1d371,
+ 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, 0x1d7ff,
+ 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, 0x1f0be,
+ 0x1f0c1, 0x1f0cf,
+ 0x1f0d1, 0x1f0df,
+ 0x1f100, 0x1f10a,
+ 0x1f110, 0x1f12e,
+ 0x1f130, 0x1f16b,
+ 0x1f170, 0x1f19a,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23a,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f300, 0x1f320,
+ 0x1f330, 0x1f335,
+ 0x1f337, 0x1f37c,
+ 0x1f380, 0x1f393,
+ 0x1f3a0, 0x1f3c4,
+ 0x1f3c6, 0x1f3ca,
+ 0x1f3e0, 0x1f3f0,
+ 0x1f400, 0x1f43e,
+ 0x1f440, 0x1f440,
+ 0x1f442, 0x1f4f7,
+ 0x1f4f9, 0x1f4fc,
+ 0x1f500, 0x1f53d,
+ 0x1f540, 0x1f543,
+ 0x1f550, 0x1f567,
+ 0x1f5fb, 0x1f640,
+ 0x1f645, 0x1f64f,
+ 0x1f680, 0x1f6c5,
+ 0x1f700, 0x1f773,
+ 0x1fffe, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_6_3 */
+
+/* 'Age_7_0': Derived Age 7.0 */
+static const OnigCodePoint CR_Age_7_0[] = {
+ 610,
+ 0x0000, 0x0377,
+ 0x037a, 0x037f,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x058d, 0x058f,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x0600, 0x061c,
+ 0x061e, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x0800, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x08a0, 0x08b2,
+ 0x08e4, 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, 0x09fb,
+ 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, 0x0a75,
+ 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,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 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, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c59,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c78, 0x0c7f,
+ 0x0c81, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d01, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d3a,
+ 0x0d3d, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4e,
+ 0x0d57, 0x0d57,
+ 0x0d60, 0x0d63,
+ 0x0d66, 0x0d75,
+ 0x0d79, 0x0d7f,
+ 0x0d82, 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,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 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, 0x13f4,
+ 0x1400, 0x169c,
+ 0x16a0, 0x16f8,
+ 0x1700, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 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, 0x1abe,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c7f,
+ 0x1cc0, 0x1cc7,
+ 0x1cd0, 0x1cf6,
+ 0x1cf8, 0x1cf9,
+ 0x1d00, 0x1df5,
+ 0x1dfc, 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, 0x20bd,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x2189,
+ 0x2190, 0x23fa,
+ 0x2400, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b98, 0x2bb9,
+ 0x2bbd, 0x2bc8,
+ 0x2bca, 0x2bd1,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 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, 0x2e42,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312d,
+ 0x3131, 0x318e,
+ 0x3190, 0x31ba,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x32fe,
+ 0x3300, 0x4db5,
+ 0x4dc0, 0x9fcc,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa69d,
+ 0xa69f, 0xa6f7,
+ 0xa700, 0xa78e,
+ 0xa790, 0xa7ad,
+ 0xa7b0, 0xa7b1,
+ 0xa7f7, 0xa82b,
+ 0xa830, 0xa839,
+ 0xa840, 0xa877,
+ 0xa880, 0xa8c4,
+ 0xa8ce, 0xa8d9,
+ 0xa8e0, 0xa8fb,
+ 0xa900, 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, 0xab5f,
+ 0xab64, 0xab65,
+ 0xabc0, 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, 0xfbc1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 0xfe19,
+ 0xfe20, 0xfe2d,
+ 0xfe30, 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, 0x1018c,
+ 0x10190, 0x1019b,
+ 0x101a0, 0x101a0,
+ 0x101d0, 0x101fd,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x102e0, 0x102fb,
+ 0x10300, 0x10323,
+ 0x10330, 0x1034a,
+ 0x10350, 0x1037a,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x103c3,
+ 0x103c8, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x1056f, 0x1056f,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080a, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083c, 0x1083c,
+ 0x1083f, 0x10855,
+ 0x10857, 0x1089e,
+ 0x108a7, 0x108af,
+ 0x10900, 0x1091b,
+ 0x1091f, 0x10939,
+ 0x1093f, 0x1093f,
+ 0x10980, 0x109b7,
+ 0x109be, 0x109bf,
+ 0x10a00, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a13,
+ 0x10a15, 0x10a17,
+ 0x10a19, 0x10a33,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a47,
+ 0x10a50, 0x10a58,
+ 0x10a60, 0x10a9f,
+ 0x10ac0, 0x10ae6,
+ 0x10aeb, 0x10af6,
+ 0x10b00, 0x10b35,
+ 0x10b39, 0x10b55,
+ 0x10b58, 0x10b72,
+ 0x10b78, 0x10b91,
+ 0x10b99, 0x10b9c,
+ 0x10ba9, 0x10baf,
+ 0x10c00, 0x10c48,
+ 0x10e60, 0x10e7e,
+ 0x11000, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x1107f, 0x110c1,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x11143,
+ 0x11150, 0x11176,
+ 0x11180, 0x111c8,
+ 0x111cd, 0x111cd,
+ 0x111d0, 0x111da,
+ 0x111e1, 0x111f4,
+ 0x11200, 0x11211,
+ 0x11213, 0x1123d,
+ 0x112b0, 0x112ea,
+ 0x112f0, 0x112f9,
+ 0x11301, 0x11303,
+ 0x11305, 0x1130c,
+ 0x1130f, 0x11310,
+ 0x11313, 0x11328,
+ 0x1132a, 0x11330,
+ 0x11332, 0x11333,
+ 0x11335, 0x11339,
+ 0x1133c, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134b, 0x1134d,
+ 0x11357, 0x11357,
+ 0x1135d, 0x11363,
+ 0x11366, 0x1136c,
+ 0x11370, 0x11374,
+ 0x11480, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115c9,
+ 0x11600, 0x11644,
+ 0x11650, 0x11659,
+ 0x11680, 0x116b7,
+ 0x116c0, 0x116c9,
+ 0x118a0, 0x118f2,
+ 0x118ff, 0x118ff,
+ 0x11ac0, 0x11af8,
+ 0x12000, 0x12398,
+ 0x12400, 0x1246e,
+ 0x12470, 0x12474,
+ 0x13000, 0x1342e,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16a6e, 0x16a6f,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af5,
+ 0x16b00, 0x16b45,
+ 0x16b50, 0x16b59,
+ 0x16b5b, 0x16b61,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16f00, 0x16f44,
+ 0x16f50, 0x16f7e,
+ 0x16f8f, 0x16f9f,
+ 0x1b000, 0x1b001,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9c, 0x1bca3,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1dd,
+ 0x1d200, 0x1d245,
+ 0x1d300, 0x1d356,
+ 0x1d360, 0x1d371,
+ 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, 0x1d7ff,
+ 0x1e800, 0x1e8c4,
+ 0x1e8c7, 0x1e8d6,
+ 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, 0x1f10c,
+ 0x1f110, 0x1f12e,
+ 0x1f130, 0x1f16b,
+ 0x1f170, 0x1f19a,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23a,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f300, 0x1f32c,
+ 0x1f330, 0x1f37d,
+ 0x1f380, 0x1f3ce,
+ 0x1f3d4, 0x1f3f7,
+ 0x1f400, 0x1f4fe,
+ 0x1f500, 0x1f54a,
+ 0x1f550, 0x1f579,
+ 0x1f57b, 0x1f5a3,
+ 0x1f5a5, 0x1f642,
+ 0x1f645, 0x1f6cf,
+ 0x1f6e0, 0x1f6ec,
+ 0x1f6f0, 0x1f6f3,
+ 0x1f700, 0x1f773,
+ 0x1f780, 0x1f7d4,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1fffe, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_7_0 */
+
+/* 'Age_8_0': Derived Age 8.0 */
+static const OnigCodePoint CR_Age_8_0[] = {
+ 623,
+ 0x0000, 0x0377,
+ 0x037a, 0x037f,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x058d, 0x058f,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x0600, 0x061c,
+ 0x061e, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x0800, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x08a0, 0x08b4,
+ 0x08e3, 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, 0x09fb,
+ 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, 0x0a75,
+ 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, 0x0af9,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 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, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c78, 0x0c7f,
+ 0x0c81, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d01, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d3a,
+ 0x0d3d, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4e,
+ 0x0d57, 0x0d57,
+ 0x0d5f, 0x0d63,
+ 0x0d66, 0x0d75,
+ 0x0d79, 0x0d7f,
+ 0x0d82, 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,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 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, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 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, 0x1abe,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c7f,
+ 0x1cc0, 0x1cc7,
+ 0x1cd0, 0x1cf6,
+ 0x1cf8, 0x1cf9,
+ 0x1d00, 0x1df5,
+ 0x1dfc, 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, 0x20be,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x218b,
+ 0x2190, 0x23fa,
+ 0x2400, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b98, 0x2bb9,
+ 0x2bbd, 0x2bc8,
+ 0x2bca, 0x2bd1,
+ 0x2bec, 0x2bef,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 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, 0x2e42,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312d,
+ 0x3131, 0x318e,
+ 0x3190, 0x31ba,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x32fe,
+ 0x3300, 0x4db5,
+ 0x4dc0, 0x9fd5,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa6f7,
+ 0xa700, 0xa7ad,
+ 0xa7b0, 0xa7b7,
+ 0xa7f7, 0xa82b,
+ 0xa830, 0xa839,
+ 0xa840, 0xa877,
+ 0xa880, 0xa8c4,
+ 0xa8ce, 0xa8d9,
+ 0xa8e0, 0xa8fd,
+ 0xa900, 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, 0xab65,
+ 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, 0xfbc1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 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, 0x1018c,
+ 0x10190, 0x1019b,
+ 0x101a0, 0x101a0,
+ 0x101d0, 0x101fd,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x102e0, 0x102fb,
+ 0x10300, 0x10323,
+ 0x10330, 0x1034a,
+ 0x10350, 0x1037a,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x103c3,
+ 0x103c8, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x1056f, 0x1056f,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 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, 0x10a33,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a47,
+ 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, 0x10cff,
+ 0x10e60, 0x10e7e,
+ 0x11000, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x1107f, 0x110c1,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x11143,
+ 0x11150, 0x11176,
+ 0x11180, 0x111cd,
+ 0x111d0, 0x111df,
+ 0x111e1, 0x111f4,
+ 0x11200, 0x11211,
+ 0x11213, 0x1123d,
+ 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,
+ 0x1133c, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134b, 0x1134d,
+ 0x11350, 0x11350,
+ 0x11357, 0x11357,
+ 0x1135d, 0x11363,
+ 0x11366, 0x1136c,
+ 0x11370, 0x11374,
+ 0x11480, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115dd,
+ 0x11600, 0x11644,
+ 0x11650, 0x11659,
+ 0x11680, 0x116b7,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x11719,
+ 0x1171d, 0x1172b,
+ 0x11730, 0x1173f,
+ 0x118a0, 0x118f2,
+ 0x118ff, 0x118ff,
+ 0x11ac0, 0x11af8,
+ 0x12000, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12470, 0x12474,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16a6e, 0x16a6f,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af5,
+ 0x16b00, 0x16b45,
+ 0x16b50, 0x16b59,
+ 0x16b5b, 0x16b61,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16f00, 0x16f44,
+ 0x16f50, 0x16f7e,
+ 0x16f8f, 0x16f9f,
+ 0x1b000, 0x1b001,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9c, 0x1bca3,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1e8,
+ 0x1d200, 0x1d245,
+ 0x1d300, 0x1d356,
+ 0x1d360, 0x1d371,
+ 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,
+ 0x1e800, 0x1e8c4,
+ 0x1e8c7, 0x1e8d6,
+ 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, 0x1f10c,
+ 0x1f110, 0x1f12e,
+ 0x1f130, 0x1f16b,
+ 0x1f170, 0x1f19a,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23a,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f300, 0x1f579,
+ 0x1f57b, 0x1f5a3,
+ 0x1f5a5, 0x1f6d0,
+ 0x1f6e0, 0x1f6ec,
+ 0x1f6f0, 0x1f6f3,
+ 0x1f700, 0x1f773,
+ 0x1f780, 0x1f7d4,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1f910, 0x1f918,
+ 0x1f980, 0x1f984,
+ 0x1f9c0, 0x1f9c0,
+ 0x1fffe, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_8_0 */
+
+/* 'Age_9_0': Derived Age 9.0 */
+static const OnigCodePoint CR_Age_9_0[] = {
+ 648,
+ 0x0000, 0x0377,
+ 0x037a, 0x037f,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x058d, 0x058f,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x0600, 0x061c,
+ 0x061e, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x0800, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x08d4, 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, 0x09fb,
+ 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, 0x0a75,
+ 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, 0x0af9,
+ 0x0b01, 0x0b03,
+ 0x0b05, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b28,
+ 0x0b2a, 0x0b30,
+ 0x0b32, 0x0b33,
+ 0x0b35, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b56, 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, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c78, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d01, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d3a,
+ 0x0d3d, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4f,
+ 0x0d54, 0x0d63,
+ 0x0d66, 0x0d7f,
+ 0x0d82, 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,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 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, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 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, 0x1abe,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c88,
+ 0x1cc0, 0x1cc7,
+ 0x1cd0, 0x1cf6,
+ 0x1cf8, 0x1cf9,
+ 0x1d00, 0x1df5,
+ 0x1dfb, 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, 0x20be,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x218b,
+ 0x2190, 0x23fe,
+ 0x2400, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b98, 0x2bb9,
+ 0x2bbd, 0x2bc8,
+ 0x2bca, 0x2bd1,
+ 0x2bec, 0x2bef,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 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, 0x2e44,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312d,
+ 0x3131, 0x318e,
+ 0x3190, 0x31ba,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x32fe,
+ 0x3300, 0x4db5,
+ 0x4dc0, 0x9fd5,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa6f7,
+ 0xa700, 0xa7ae,
+ 0xa7b0, 0xa7b7,
+ 0xa7f7, 0xa82b,
+ 0xa830, 0xa839,
+ 0xa840, 0xa877,
+ 0xa880, 0xa8c5,
+ 0xa8ce, 0xa8d9,
+ 0xa8e0, 0xa8fd,
+ 0xa900, 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, 0xab65,
+ 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, 0xfbc1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 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, 0x1019b,
+ 0x101a0, 0x101a0,
+ 0x101d0, 0x101fd,
+ 0x10280, 0x1029c,
+ 0x102a0, 0x102d0,
+ 0x102e0, 0x102fb,
+ 0x10300, 0x10323,
+ 0x10330, 0x1034a,
+ 0x10350, 0x1037a,
+ 0x10380, 0x1039d,
+ 0x1039f, 0x103c3,
+ 0x103c8, 0x103d5,
+ 0x10400, 0x1049d,
+ 0x104a0, 0x104a9,
+ 0x104b0, 0x104d3,
+ 0x104d8, 0x104fb,
+ 0x10500, 0x10527,
+ 0x10530, 0x10563,
+ 0x1056f, 0x1056f,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 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, 0x10a33,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a47,
+ 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, 0x10cff,
+ 0x10e60, 0x10e7e,
+ 0x11000, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x1107f, 0x110c1,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x11143,
+ 0x11150, 0x11176,
+ 0x11180, 0x111cd,
+ 0x111d0, 0x111df,
+ 0x111e1, 0x111f4,
+ 0x11200, 0x11211,
+ 0x11213, 0x1123e,
+ 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,
+ 0x1133c, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134b, 0x1134d,
+ 0x11350, 0x11350,
+ 0x11357, 0x11357,
+ 0x1135d, 0x11363,
+ 0x11366, 0x1136c,
+ 0x11370, 0x11374,
+ 0x11400, 0x11459,
+ 0x1145b, 0x1145b,
+ 0x1145d, 0x1145d,
+ 0x11480, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115dd,
+ 0x11600, 0x11644,
+ 0x11650, 0x11659,
+ 0x11660, 0x1166c,
+ 0x11680, 0x116b7,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x11719,
+ 0x1171d, 0x1172b,
+ 0x11730, 0x1173f,
+ 0x118a0, 0x118f2,
+ 0x118ff, 0x118ff,
+ 0x11ac0, 0x11af8,
+ 0x11c00, 0x11c08,
+ 0x11c0a, 0x11c36,
+ 0x11c38, 0x11c45,
+ 0x11c50, 0x11c6c,
+ 0x11c70, 0x11c8f,
+ 0x11c92, 0x11ca7,
+ 0x11ca9, 0x11cb6,
+ 0x12000, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12470, 0x12474,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16a6e, 0x16a6f,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af5,
+ 0x16b00, 0x16b45,
+ 0x16b50, 0x16b59,
+ 0x16b5b, 0x16b61,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16f00, 0x16f44,
+ 0x16f50, 0x16f7e,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe0,
+ 0x17000, 0x187ec,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b001,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9c, 0x1bca3,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1e8,
+ 0x1d200, 0x1d245,
+ 0x1d300, 0x1d356,
+ 0x1d360, 0x1d371,
+ 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,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e800, 0x1e8c4,
+ 0x1e8c7, 0x1e8d6,
+ 0x1e900, 0x1e94a,
+ 0x1e950, 0x1e959,
+ 0x1e95e, 0x1e95f,
+ 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, 0x1f10c,
+ 0x1f110, 0x1f12e,
+ 0x1f130, 0x1f16b,
+ 0x1f170, 0x1f1ac,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23b,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f300, 0x1f6d2,
+ 0x1f6e0, 0x1f6ec,
+ 0x1f6f0, 0x1f6f6,
+ 0x1f700, 0x1f773,
+ 0x1f780, 0x1f7d4,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1f910, 0x1f91e,
+ 0x1f920, 0x1f927,
+ 0x1f930, 0x1f930,
+ 0x1f933, 0x1f93e,
+ 0x1f940, 0x1f94b,
+ 0x1f950, 0x1f95e,
+ 0x1f980, 0x1f991,
+ 0x1f9c0, 0x1f9c0,
+ 0x1fffe, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_9_0 */
+
+/* 'Age_10_0': Derived Age 10.0 */
+static const OnigCodePoint CR_Age_10_0[] = {
+ 659,
+ 0x0000, 0x0377,
+ 0x037a, 0x037f,
+ 0x0384, 0x038a,
+ 0x038c, 0x038c,
+ 0x038e, 0x03a1,
+ 0x03a3, 0x052f,
+ 0x0531, 0x0556,
+ 0x0559, 0x055f,
+ 0x0561, 0x0587,
+ 0x0589, 0x058a,
+ 0x058d, 0x058f,
+ 0x0591, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05f0, 0x05f4,
+ 0x0600, 0x061c,
+ 0x061e, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x0800, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x08d4, 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, 0x09fd,
+ 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, 0x0a75,
+ 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,
+ 0x0b56, 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, 0x0c03,
+ 0x0c05, 0x0c0c,
+ 0x0c0e, 0x0c10,
+ 0x0c12, 0x0c28,
+ 0x0c2a, 0x0c39,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c78, 0x0c83,
+ 0x0c85, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d00, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4f,
+ 0x0d54, 0x0d63,
+ 0x0d66, 0x0d7f,
+ 0x0d82, 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,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 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, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 0x1819,
+ 0x1820, 0x1877,
+ 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, 0x1abe,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c88,
+ 0x1cc0, 0x1cc7,
+ 0x1cd0, 0x1cf9,
+ 0x1d00, 0x1df9,
+ 0x1dfb, 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, 0x20bf,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x218b,
+ 0x2190, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b98, 0x2bb9,
+ 0x2bbd, 0x2bc8,
+ 0x2bca, 0x2bd2,
+ 0x2bec, 0x2bef,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 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, 0x2e49,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312e,
+ 0x3131, 0x318e,
+ 0x3190, 0x31ba,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x32fe,
+ 0x3300, 0x4db5,
+ 0x4dc0, 0x9fea,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa6f7,
+ 0xa700, 0xa7ae,
+ 0xa7b0, 0xa7b7,
+ 0xa7f7, 0xa82b,
+ 0xa830, 0xa839,
+ 0xa840, 0xa877,
+ 0xa880, 0xa8c5,
+ 0xa8ce, 0xa8d9,
+ 0xa8e0, 0xa8fd,
+ 0xa900, 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, 0xab65,
+ 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, 0xfbc1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 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, 0x1019b,
+ 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, 0x1056f,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 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, 0x10a33,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a47,
+ 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, 0x10cff,
+ 0x10e60, 0x10e7e,
+ 0x11000, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x1107f, 0x110c1,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x11143,
+ 0x11150, 0x11176,
+ 0x11180, 0x111cd,
+ 0x111d0, 0x111df,
+ 0x111e1, 0x111f4,
+ 0x11200, 0x11211,
+ 0x11213, 0x1123e,
+ 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,
+ 0x1133c, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134b, 0x1134d,
+ 0x11350, 0x11350,
+ 0x11357, 0x11357,
+ 0x1135d, 0x11363,
+ 0x11366, 0x1136c,
+ 0x11370, 0x11374,
+ 0x11400, 0x11459,
+ 0x1145b, 0x1145b,
+ 0x1145d, 0x1145d,
+ 0x11480, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115dd,
+ 0x11600, 0x11644,
+ 0x11650, 0x11659,
+ 0x11660, 0x1166c,
+ 0x11680, 0x116b7,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x11719,
+ 0x1171d, 0x1172b,
+ 0x11730, 0x1173f,
+ 0x118a0, 0x118f2,
+ 0x118ff, 0x118ff,
+ 0x11a00, 0x11a47,
+ 0x11a50, 0x11a83,
+ 0x11a86, 0x11a9c,
+ 0x11a9e, 0x11aa2,
+ 0x11ac0, 0x11af8,
+ 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,
+ 0x12000, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12470, 0x12474,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16a6e, 0x16a6f,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af5,
+ 0x16b00, 0x16b45,
+ 0x16b50, 0x16b59,
+ 0x16b5b, 0x16b61,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16f00, 0x16f44,
+ 0x16f50, 0x16f7e,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe1,
+ 0x17000, 0x187ec,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9c, 0x1bca3,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1e8,
+ 0x1d200, 0x1d245,
+ 0x1d300, 0x1d356,
+ 0x1d360, 0x1d371,
+ 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,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e800, 0x1e8c4,
+ 0x1e8c7, 0x1e8d6,
+ 0x1e900, 0x1e94a,
+ 0x1e950, 0x1e959,
+ 0x1e95e, 0x1e95f,
+ 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, 0x1f10c,
+ 0x1f110, 0x1f12e,
+ 0x1f130, 0x1f16b,
+ 0x1f170, 0x1f1ac,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23b,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f260, 0x1f265,
+ 0x1f300, 0x1f6d4,
+ 0x1f6e0, 0x1f6ec,
+ 0x1f6f0, 0x1f6f8,
+ 0x1f700, 0x1f773,
+ 0x1f780, 0x1f7d4,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1f900, 0x1f90b,
+ 0x1f910, 0x1f93e,
+ 0x1f940, 0x1f94c,
+ 0x1f950, 0x1f96b,
+ 0x1f980, 0x1f997,
+ 0x1f9c0, 0x1f9c0,
+ 0x1f9d0, 0x1f9e6,
+ 0x1fffe, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_10_0 */
+
+/* 'Age_11_0': Derived Age 11.0 */
+static const OnigCodePoint CR_Age_11_0[] = {
+ 668,
+ 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, 0x061c,
+ 0x061e, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x07fd, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x08d3, 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,
+ 0x0b56, 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,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c78, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d00, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4f,
+ 0x0d54, 0x0d63,
+ 0x0d66, 0x0d7f,
+ 0x0d82, 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,
+ 0x0e87, 0x0e88,
+ 0x0e8a, 0x0e8a,
+ 0x0e8d, 0x0e8d,
+ 0x0e94, 0x0e97,
+ 0x0e99, 0x0e9f,
+ 0x0ea1, 0x0ea3,
+ 0x0ea5, 0x0ea5,
+ 0x0ea7, 0x0ea7,
+ 0x0eaa, 0x0eab,
+ 0x0ead, 0x0eb9,
+ 0x0ebb, 0x0ebd,
+ 0x0ec0, 0x0ec4,
+ 0x0ec6, 0x0ec6,
+ 0x0ec8, 0x0ecd,
+ 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, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 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, 0x1abe,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cc7,
+ 0x1cd0, 0x1cf9,
+ 0x1d00, 0x1df9,
+ 0x1dfb, 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, 0x20bf,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x218b,
+ 0x2190, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b98, 0x2bc8,
+ 0x2bca, 0x2bfe,
+ 0x2c00, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 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, 0x2e4e,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x3190, 0x31ba,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x32fe,
+ 0x3300, 0x4db5,
+ 0x4dc0, 0x9fef,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa6f7,
+ 0xa700, 0xa7b9,
+ 0xa7f7, 0xa82b,
+ 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, 0xab65,
+ 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, 0xfbc1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 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, 0x1019b,
+ 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, 0x1056f,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 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,
+ 0x10f00, 0x10f27,
+ 0x10f30, 0x10f59,
+ 0x11000, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x1107f, 0x110c1,
+ 0x110cd, 0x110cd,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x11146,
+ 0x11150, 0x11176,
+ 0x11180, 0x111cd,
+ 0x111d0, 0x111df,
+ 0x111e1, 0x111f4,
+ 0x11200, 0x11211,
+ 0x11213, 0x1123e,
+ 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, 0x11459,
+ 0x1145b, 0x1145b,
+ 0x1145d, 0x1145e,
+ 0x11480, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115dd,
+ 0x11600, 0x11644,
+ 0x11650, 0x11659,
+ 0x11660, 0x1166c,
+ 0x11680, 0x116b7,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x1171a,
+ 0x1171d, 0x1172b,
+ 0x11730, 0x1173f,
+ 0x11800, 0x1183b,
+ 0x118a0, 0x118f2,
+ 0x118ff, 0x118ff,
+ 0x11a00, 0x11a47,
+ 0x11a50, 0x11a83,
+ 0x11a86, 0x11aa2,
+ 0x11ac0, 0x11af8,
+ 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,
+ 0x12000, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12470, 0x12474,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16a6e, 0x16a6f,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af5,
+ 0x16b00, 0x16b45,
+ 0x16b50, 0x16b59,
+ 0x16b5b, 0x16b61,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e9a,
+ 0x16f00, 0x16f44,
+ 0x16f50, 0x16f7e,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe1,
+ 0x17000, 0x187f1,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9c, 0x1bca3,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1e8,
+ 0x1d200, 0x1d245,
+ 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,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e800, 0x1e8c4,
+ 0x1e8c7, 0x1e8d6,
+ 0x1e900, 0x1e94a,
+ 0x1e950, 0x1e959,
+ 0x1e95e, 0x1e95f,
+ 0x1ec71, 0x1ecb4,
+ 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, 0x1f10c,
+ 0x1f110, 0x1f16b,
+ 0x1f170, 0x1f1ac,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23b,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f260, 0x1f265,
+ 0x1f300, 0x1f6d4,
+ 0x1f6e0, 0x1f6ec,
+ 0x1f6f0, 0x1f6f9,
+ 0x1f700, 0x1f773,
+ 0x1f780, 0x1f7d8,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1f900, 0x1f90b,
+ 0x1f910, 0x1f93e,
+ 0x1f940, 0x1f970,
+ 0x1f973, 0x1f976,
+ 0x1f97a, 0x1f97a,
+ 0x1f97c, 0x1f9a2,
+ 0x1f9b0, 0x1f9b9,
+ 0x1f9c0, 0x1f9c2,
+ 0x1f9d0, 0x1f9ff,
+ 0x1fa60, 0x1fa6d,
+ 0x1fffe, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_11_0 */
+
+/* 'Age_12_0': Derived Age 12.0 */
+static const OnigCodePoint CR_Age_12_0[] = {
+ 677,
+ 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, 0x061c,
+ 0x061e, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x07fd, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x08d3, 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,
+ 0x0b56, 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,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c77, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d00, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4f,
+ 0x0d54, 0x0d63,
+ 0x0d66, 0x0d7f,
+ 0x0d82, 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, 0x0ecd,
+ 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, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 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, 0x1abe,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cc7,
+ 0x1cd0, 0x1cfa,
+ 0x1d00, 0x1df9,
+ 0x1dfb, 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, 0x20bf,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x218b,
+ 0x2190, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b98, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 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, 0x2e4f,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x3190, 0x31ba,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x32fe,
+ 0x3300, 0x4db5,
+ 0x4dc0, 0x9fef,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa6f7,
+ 0xa700, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f7, 0xa82b,
+ 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, 0xab67,
+ 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, 0xfbc1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 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, 0x1019b,
+ 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, 0x1056f,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 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,
+ 0x10f00, 0x10f27,
+ 0x10f30, 0x10f59,
+ 0x10fe0, 0x10ff6,
+ 0x11000, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x1107f, 0x110c1,
+ 0x110cd, 0x110cd,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x11146,
+ 0x11150, 0x11176,
+ 0x11180, 0x111cd,
+ 0x111d0, 0x111df,
+ 0x111e1, 0x111f4,
+ 0x11200, 0x11211,
+ 0x11213, 0x1123e,
+ 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, 0x11459,
+ 0x1145b, 0x1145b,
+ 0x1145d, 0x1145f,
+ 0x11480, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115dd,
+ 0x11600, 0x11644,
+ 0x11650, 0x11659,
+ 0x11660, 0x1166c,
+ 0x11680, 0x116b8,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x1171a,
+ 0x1171d, 0x1172b,
+ 0x11730, 0x1173f,
+ 0x11800, 0x1183b,
+ 0x118a0, 0x118f2,
+ 0x118ff, 0x118ff,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d7,
+ 0x119da, 0x119e4,
+ 0x11a00, 0x11a47,
+ 0x11a50, 0x11aa2,
+ 0x11ac0, 0x11af8,
+ 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,
+ 0x11fc0, 0x11ff1,
+ 0x11fff, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12470, 0x12474,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x13430, 0x13438,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16a6e, 0x16a6f,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af5,
+ 0x16b00, 0x16b45,
+ 0x16b50, 0x16b59,
+ 0x16b5b, 0x16b61,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e9a,
+ 0x16f00, 0x16f4a,
+ 0x16f4f, 0x16f87,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe3,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9c, 0x1bca3,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1e8,
+ 0x1d200, 0x1d245,
+ 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,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e100, 0x1e12c,
+ 0x1e130, 0x1e13d,
+ 0x1e140, 0x1e149,
+ 0x1e14e, 0x1e14f,
+ 0x1e2c0, 0x1e2f9,
+ 0x1e2ff, 0x1e2ff,
+ 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, 0x1f10c,
+ 0x1f110, 0x1f16c,
+ 0x1f170, 0x1f1ac,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23b,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f260, 0x1f265,
+ 0x1f300, 0x1f6d5,
+ 0x1f6e0, 0x1f6ec,
+ 0x1f6f0, 0x1f6fa,
+ 0x1f700, 0x1f773,
+ 0x1f780, 0x1f7d8,
+ 0x1f7e0, 0x1f7eb,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1f900, 0x1f90b,
+ 0x1f90d, 0x1f971,
+ 0x1f973, 0x1f976,
+ 0x1f97a, 0x1f9a2,
+ 0x1f9a5, 0x1f9aa,
+ 0x1f9ae, 0x1f9ca,
+ 0x1f9cd, 0x1fa53,
+ 0x1fa60, 0x1fa6d,
+ 0x1fa70, 0x1fa73,
+ 0x1fa78, 0x1fa7a,
+ 0x1fa80, 0x1fa82,
+ 0x1fa90, 0x1fa95,
+ 0x1fffe, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_12_0 */
+
+/* 'Age_12_1': Derived Age 12.1 */
+static const OnigCodePoint CR_Age_12_1[] = {
+ 676,
+ 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, 0x061c,
+ 0x061e, 0x070d,
+ 0x070f, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x07fd, 0x082d,
+ 0x0830, 0x083e,
+ 0x0840, 0x085b,
+ 0x085e, 0x085e,
+ 0x0860, 0x086a,
+ 0x08a0, 0x08b4,
+ 0x08b6, 0x08bd,
+ 0x08d3, 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,
+ 0x0b56, 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,
+ 0x0c3d, 0x0c44,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c58, 0x0c5a,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c77, 0x0c8c,
+ 0x0c8e, 0x0c90,
+ 0x0c92, 0x0ca8,
+ 0x0caa, 0x0cb3,
+ 0x0cb5, 0x0cb9,
+ 0x0cbc, 0x0cc4,
+ 0x0cc6, 0x0cc8,
+ 0x0cca, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cde, 0x0cde,
+ 0x0ce0, 0x0ce3,
+ 0x0ce6, 0x0cef,
+ 0x0cf1, 0x0cf2,
+ 0x0d00, 0x0d03,
+ 0x0d05, 0x0d0c,
+ 0x0d0e, 0x0d10,
+ 0x0d12, 0x0d44,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4f,
+ 0x0d54, 0x0d63,
+ 0x0d66, 0x0d7f,
+ 0x0d82, 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, 0x0ecd,
+ 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, 0x170c,
+ 0x170e, 0x1714,
+ 0x1720, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x176c,
+ 0x176e, 0x1770,
+ 0x1772, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x180e,
+ 0x1810, 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, 0x1abe,
+ 0x1b00, 0x1b4b,
+ 0x1b50, 0x1b7c,
+ 0x1b80, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cc7,
+ 0x1cd0, 0x1cfa,
+ 0x1d00, 0x1df9,
+ 0x1dfb, 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, 0x20bf,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x218b,
+ 0x2190, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x2b73,
+ 0x2b76, 0x2b95,
+ 0x2b98, 0x2c2e,
+ 0x2c30, 0x2c5e,
+ 0x2c60, 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, 0x2e4f,
+ 0x2e80, 0x2e99,
+ 0x2e9b, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3000, 0x303f,
+ 0x3041, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x312f,
+ 0x3131, 0x318e,
+ 0x3190, 0x31ba,
+ 0x31c0, 0x31e3,
+ 0x31f0, 0x321e,
+ 0x3220, 0x4db5,
+ 0x4dc0, 0x9fef,
+ 0xa000, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa6f7,
+ 0xa700, 0xa7bf,
+ 0xa7c2, 0xa7c6,
+ 0xa7f7, 0xa82b,
+ 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, 0xab67,
+ 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, 0xfbc1,
+ 0xfbd3, 0xfd3f,
+ 0xfd50, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdd0, 0xfdfd,
+ 0xfe00, 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, 0x1019b,
+ 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, 0x1056f,
+ 0x10600, 0x10736,
+ 0x10740, 0x10755,
+ 0x10760, 0x10767,
+ 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,
+ 0x10f00, 0x10f27,
+ 0x10f30, 0x10f59,
+ 0x10fe0, 0x10ff6,
+ 0x11000, 0x1104d,
+ 0x11052, 0x1106f,
+ 0x1107f, 0x110c1,
+ 0x110cd, 0x110cd,
+ 0x110d0, 0x110e8,
+ 0x110f0, 0x110f9,
+ 0x11100, 0x11134,
+ 0x11136, 0x11146,
+ 0x11150, 0x11176,
+ 0x11180, 0x111cd,
+ 0x111d0, 0x111df,
+ 0x111e1, 0x111f4,
+ 0x11200, 0x11211,
+ 0x11213, 0x1123e,
+ 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, 0x11459,
+ 0x1145b, 0x1145b,
+ 0x1145d, 0x1145f,
+ 0x11480, 0x114c7,
+ 0x114d0, 0x114d9,
+ 0x11580, 0x115b5,
+ 0x115b8, 0x115dd,
+ 0x11600, 0x11644,
+ 0x11650, 0x11659,
+ 0x11660, 0x1166c,
+ 0x11680, 0x116b8,
+ 0x116c0, 0x116c9,
+ 0x11700, 0x1171a,
+ 0x1171d, 0x1172b,
+ 0x11730, 0x1173f,
+ 0x11800, 0x1183b,
+ 0x118a0, 0x118f2,
+ 0x118ff, 0x118ff,
+ 0x119a0, 0x119a7,
+ 0x119aa, 0x119d7,
+ 0x119da, 0x119e4,
+ 0x11a00, 0x11a47,
+ 0x11a50, 0x11aa2,
+ 0x11ac0, 0x11af8,
+ 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,
+ 0x11fc0, 0x11ff1,
+ 0x11fff, 0x12399,
+ 0x12400, 0x1246e,
+ 0x12470, 0x12474,
+ 0x12480, 0x12543,
+ 0x13000, 0x1342e,
+ 0x13430, 0x13438,
+ 0x14400, 0x14646,
+ 0x16800, 0x16a38,
+ 0x16a40, 0x16a5e,
+ 0x16a60, 0x16a69,
+ 0x16a6e, 0x16a6f,
+ 0x16ad0, 0x16aed,
+ 0x16af0, 0x16af5,
+ 0x16b00, 0x16b45,
+ 0x16b50, 0x16b59,
+ 0x16b5b, 0x16b61,
+ 0x16b63, 0x16b77,
+ 0x16b7d, 0x16b8f,
+ 0x16e40, 0x16e9a,
+ 0x16f00, 0x16f4a,
+ 0x16f4f, 0x16f87,
+ 0x16f8f, 0x16f9f,
+ 0x16fe0, 0x16fe3,
+ 0x17000, 0x187f7,
+ 0x18800, 0x18af2,
+ 0x1b000, 0x1b11e,
+ 0x1b150, 0x1b152,
+ 0x1b164, 0x1b167,
+ 0x1b170, 0x1b2fb,
+ 0x1bc00, 0x1bc6a,
+ 0x1bc70, 0x1bc7c,
+ 0x1bc80, 0x1bc88,
+ 0x1bc90, 0x1bc99,
+ 0x1bc9c, 0x1bca3,
+ 0x1d000, 0x1d0f5,
+ 0x1d100, 0x1d126,
+ 0x1d129, 0x1d1e8,
+ 0x1d200, 0x1d245,
+ 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,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e100, 0x1e12c,
+ 0x1e130, 0x1e13d,
+ 0x1e140, 0x1e149,
+ 0x1e14e, 0x1e14f,
+ 0x1e2c0, 0x1e2f9,
+ 0x1e2ff, 0x1e2ff,
+ 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, 0x1f10c,
+ 0x1f110, 0x1f16c,
+ 0x1f170, 0x1f1ac,
+ 0x1f1e6, 0x1f202,
+ 0x1f210, 0x1f23b,
+ 0x1f240, 0x1f248,
+ 0x1f250, 0x1f251,
+ 0x1f260, 0x1f265,
+ 0x1f300, 0x1f6d5,
+ 0x1f6e0, 0x1f6ec,
+ 0x1f6f0, 0x1f6fa,
+ 0x1f700, 0x1f773,
+ 0x1f780, 0x1f7d8,
+ 0x1f7e0, 0x1f7eb,
+ 0x1f800, 0x1f80b,
+ 0x1f810, 0x1f847,
+ 0x1f850, 0x1f859,
+ 0x1f860, 0x1f887,
+ 0x1f890, 0x1f8ad,
+ 0x1f900, 0x1f90b,
+ 0x1f90d, 0x1f971,
+ 0x1f973, 0x1f976,
+ 0x1f97a, 0x1f9a2,
+ 0x1f9a5, 0x1f9aa,
+ 0x1f9ae, 0x1f9ca,
+ 0x1f9cd, 0x1fa53,
+ 0x1fa60, 0x1fa6d,
+ 0x1fa70, 0x1fa73,
+ 0x1fa78, 0x1fa7a,
+ 0x1fa80, 0x1fa82,
+ 0x1fa90, 0x1fa95,
+ 0x1fffe, 0x2a6d6,
+ 0x2a700, 0x2b734,
+ 0x2b740, 0x2b81d,
+ 0x2b820, 0x2cea1,
+ 0x2ceb0, 0x2ebe0,
+ 0x2f800, 0x2fa1d,
+ 0x2fffe, 0x2ffff,
+ 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_12_1 */
+
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+/* 'Grapheme_Cluster_Break_Prepend': Grapheme_Cluster_Break=Prepend */
+static const OnigCodePoint CR_Grapheme_Cluster_Break_Prepend[] = {
+ 11,
+ 0x0600, 0x0605,
+ 0x06dd, 0x06dd,
+ 0x070f, 0x070f,
+ 0x08e2, 0x08e2,
+ 0x0d4e, 0x0d4e,
+ 0x110bd, 0x110bd,
+ 0x110cd, 0x110cd,
+ 0x111c2, 0x111c3,
+ 0x11a3a, 0x11a3a,
+ 0x11a84, 0x11a89,
+ 0x11d46, 0x11d46,
+}; /* CR_Grapheme_Cluster_Break_Prepend */
+
+/* 'Grapheme_Cluster_Break_CR': Grapheme_Cluster_Break=CR */
+static const OnigCodePoint CR_Grapheme_Cluster_Break_CR[] = {
+ 1,
+ 0x000d, 0x000d,
+}; /* CR_Grapheme_Cluster_Break_CR */
+
+/* 'Grapheme_Cluster_Break_LF': Grapheme_Cluster_Break=LF */
+#define CR_Grapheme_Cluster_Break_LF CR_NEWLINE
+
+/* 'Grapheme_Cluster_Break_Control': Grapheme_Cluster_Break=Control */
+static const OnigCodePoint CR_Grapheme_Cluster_Break_Control[] = {
+ 19,
+ 0x0000, 0x0009,
+ 0x000b, 0x000c,
+ 0x000e, 0x001f,
+ 0x007f, 0x009f,
+ 0x00ad, 0x00ad,
+ 0x061c, 0x061c,
+ 0x180e, 0x180e,
+ 0x200b, 0x200b,
+ 0x200e, 0x200f,
+ 0x2028, 0x202e,
+ 0x2060, 0x206f,
+ 0xfeff, 0xfeff,
+ 0xfff0, 0xfffb,
+ 0x13430, 0x13438,
+ 0x1bca0, 0x1bca3,
+ 0x1d173, 0x1d17a,
+ 0xe0000, 0xe001f,
+ 0xe0080, 0xe00ff,
+ 0xe01f0, 0xe0fff,
+}; /* CR_Grapheme_Cluster_Break_Control */
+
+/* 'Grapheme_Cluster_Break_Extend': Grapheme_Cluster_Break=Extend */
+static const OnigCodePoint CR_Grapheme_Cluster_Break_Extend[] = {
+ 336,
+ 0x0300, 0x036f,
+ 0x0483, 0x0489,
+ 0x0591, 0x05bd,
+ 0x05bf, 0x05bf,
+ 0x05c1, 0x05c2,
+ 0x05c4, 0x05c5,
+ 0x05c7, 0x05c7,
+ 0x0610, 0x061a,
+ 0x064b, 0x065f,
+ 0x0670, 0x0670,
+ 0x06d6, 0x06dc,
+ 0x06df, 0x06e4,
+ 0x06e7, 0x06e8,
+ 0x06ea, 0x06ed,
+ 0x0711, 0x0711,
+ 0x0730, 0x074a,
+ 0x07a6, 0x07b0,
+ 0x07eb, 0x07f3,
+ 0x07fd, 0x07fd,
+ 0x0816, 0x0819,
+ 0x081b, 0x0823,
+ 0x0825, 0x0827,
+ 0x0829, 0x082d,
+ 0x0859, 0x085b,
+ 0x08d3, 0x08e1,
+ 0x08e3, 0x0902,
+ 0x093a, 0x093a,
+ 0x093c, 0x093c,
+ 0x0941, 0x0948,
+ 0x094d, 0x094d,
+ 0x0951, 0x0957,
+ 0x0962, 0x0963,
+ 0x0981, 0x0981,
+ 0x09bc, 0x09bc,
+ 0x09be, 0x09be,
+ 0x09c1, 0x09c4,
+ 0x09cd, 0x09cd,
+ 0x09d7, 0x09d7,
+ 0x09e2, 0x09e3,
+ 0x09fe, 0x09fe,
+ 0x0a01, 0x0a02,
+ 0x0a3c, 0x0a3c,
+ 0x0a41, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a51, 0x0a51,
+ 0x0a70, 0x0a71,
+ 0x0a75, 0x0a75,
+ 0x0a81, 0x0a82,
+ 0x0abc, 0x0abc,
+ 0x0ac1, 0x0ac5,
+ 0x0ac7, 0x0ac8,
+ 0x0acd, 0x0acd,
+ 0x0ae2, 0x0ae3,
+ 0x0afa, 0x0aff,
+ 0x0b01, 0x0b01,
+ 0x0b3c, 0x0b3c,
+ 0x0b3e, 0x0b3f,
+ 0x0b41, 0x0b44,
+ 0x0b4d, 0x0b4d,
+ 0x0b56, 0x0b57,
+ 0x0b62, 0x0b63,
+ 0x0b82, 0x0b82,
+ 0x0bbe, 0x0bbe,
+ 0x0bc0, 0x0bc0,
+ 0x0bcd, 0x0bcd,
+ 0x0bd7, 0x0bd7,
+ 0x0c00, 0x0c00,
+ 0x0c04, 0x0c04,
+ 0x0c3e, 0x0c40,
+ 0x0c46, 0x0c48,
+ 0x0c4a, 0x0c4d,
+ 0x0c55, 0x0c56,
+ 0x0c62, 0x0c63,
+ 0x0c81, 0x0c81,
+ 0x0cbc, 0x0cbc,
+ 0x0cbf, 0x0cbf,
+ 0x0cc2, 0x0cc2,
+ 0x0cc6, 0x0cc6,
+ 0x0ccc, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0ce2, 0x0ce3,
+ 0x0d00, 0x0d01,
+ 0x0d3b, 0x0d3c,
+ 0x0d3e, 0x0d3e,
+ 0x0d41, 0x0d44,
+ 0x0d4d, 0x0d4d,
+ 0x0d57, 0x0d57,
+ 0x0d62, 0x0d63,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0dcf,
+ 0x0dd2, 0x0dd4,
+ 0x0dd6, 0x0dd6,
+ 0x0ddf, 0x0ddf,
+ 0x0e31, 0x0e31,
+ 0x0e34, 0x0e3a,
+ 0x0e47, 0x0e4e,
+ 0x0eb1, 0x0eb1,
+ 0x0eb4, 0x0ebc,
+ 0x0ec8, 0x0ecd,
+ 0x0f18, 0x0f19,
+ 0x0f35, 0x0f35,
+ 0x0f37, 0x0f37,
+ 0x0f39, 0x0f39,
+ 0x0f71, 0x0f7e,
+ 0x0f80, 0x0f84,
+ 0x0f86, 0x0f87,
+ 0x0f8d, 0x0f97,
+ 0x0f99, 0x0fbc,
+ 0x0fc6, 0x0fc6,
+ 0x102d, 0x1030,
+ 0x1032, 0x1037,
+ 0x1039, 0x103a,
+ 0x103d, 0x103e,
+ 0x1058, 0x1059,
+ 0x105e, 0x1060,
+ 0x1071, 0x1074,
+ 0x1082, 0x1082,
+ 0x1085, 0x1086,
+ 0x108d, 0x108d,
+ 0x109d, 0x109d,
+ 0x135d, 0x135f,
+ 0x1712, 0x1714,
+ 0x1732, 0x1734,
+ 0x1752, 0x1753,
+ 0x1772, 0x1773,
+ 0x17b4, 0x17b5,
+ 0x17b7, 0x17bd,
+ 0x17c6, 0x17c6,
+ 0x17c9, 0x17d3,
+ 0x17dd, 0x17dd,
+ 0x180b, 0x180d,
+ 0x1885, 0x1886,
+ 0x18a9, 0x18a9,
+ 0x1920, 0x1922,
+ 0x1927, 0x1928,
+ 0x1932, 0x1932,
+ 0x1939, 0x193b,
+ 0x1a17, 0x1a18,
+ 0x1a1b, 0x1a1b,
+ 0x1a56, 0x1a56,
+ 0x1a58, 0x1a5e,
+ 0x1a60, 0x1a60,
+ 0x1a62, 0x1a62,
+ 0x1a65, 0x1a6c,
+ 0x1a73, 0x1a7c,
+ 0x1a7f, 0x1a7f,
+ 0x1ab0, 0x1abe,
+ 0x1b00, 0x1b03,
+ 0x1b34, 0x1b3a,
+ 0x1b3c, 0x1b3c,
+ 0x1b42, 0x1b42,
+ 0x1b6b, 0x1b73,
+ 0x1b80, 0x1b81,
+ 0x1ba2, 0x1ba5,
+ 0x1ba8, 0x1ba9,
+ 0x1bab, 0x1bad,
+ 0x1be6, 0x1be6,
+ 0x1be8, 0x1be9,
+ 0x1bed, 0x1bed,
+ 0x1bef, 0x1bf1,
+ 0x1c2c, 0x1c33,
+ 0x1c36, 0x1c37,
+ 0x1cd0, 0x1cd2,
+ 0x1cd4, 0x1ce0,
+ 0x1ce2, 0x1ce8,
+ 0x1ced, 0x1ced,
+ 0x1cf4, 0x1cf4,
+ 0x1cf8, 0x1cf9,
+ 0x1dc0, 0x1df9,
+ 0x1dfb, 0x1dff,
+ 0x200c, 0x200c,
+ 0x20d0, 0x20f0,
+ 0x2cef, 0x2cf1,
+ 0x2d7f, 0x2d7f,
+ 0x2de0, 0x2dff,
+ 0x302a, 0x302f,
+ 0x3099, 0x309a,
+ 0xa66f, 0xa672,
+ 0xa674, 0xa67d,
+ 0xa69e, 0xa69f,
+ 0xa6f0, 0xa6f1,
+ 0xa802, 0xa802,
+ 0xa806, 0xa806,
+ 0xa80b, 0xa80b,
+ 0xa825, 0xa826,
+ 0xa8c4, 0xa8c5,
+ 0xa8e0, 0xa8f1,
+ 0xa8ff, 0xa8ff,
+ 0xa926, 0xa92d,
+ 0xa947, 0xa951,
+ 0xa980, 0xa982,
+ 0xa9b3, 0xa9b3,
+ 0xa9b6, 0xa9b9,
+ 0xa9bc, 0xa9bd,
+ 0xa9e5, 0xa9e5,
+ 0xaa29, 0xaa2e,
+ 0xaa31, 0xaa32,
+ 0xaa35, 0xaa36,
+ 0xaa43, 0xaa43,
+ 0xaa4c, 0xaa4c,
+ 0xaa7c, 0xaa7c,
+ 0xaab0, 0xaab0,
+ 0xaab2, 0xaab4,
+ 0xaab7, 0xaab8,
+ 0xaabe, 0xaabf,
+ 0xaac1, 0xaac1,
+ 0xaaec, 0xaaed,
+ 0xaaf6, 0xaaf6,
+ 0xabe5, 0xabe5,
+ 0xabe8, 0xabe8,
+ 0xabed, 0xabed,
+ 0xfb1e, 0xfb1e,
+ 0xfe00, 0xfe0f,
+ 0xfe20, 0xfe2f,
+ 0xff9e, 0xff9f,
+ 0x101fd, 0x101fd,
+ 0x102e0, 0x102e0,
+ 0x10376, 0x1037a,
+ 0x10a01, 0x10a03,
+ 0x10a05, 0x10a06,
+ 0x10a0c, 0x10a0f,
+ 0x10a38, 0x10a3a,
+ 0x10a3f, 0x10a3f,
+ 0x10ae5, 0x10ae6,
+ 0x10d24, 0x10d27,
+ 0x10f46, 0x10f50,
+ 0x11001, 0x11001,
+ 0x11038, 0x11046,
+ 0x1107f, 0x11081,
+ 0x110b3, 0x110b6,
+ 0x110b9, 0x110ba,
+ 0x11100, 0x11102,
+ 0x11127, 0x1112b,
+ 0x1112d, 0x11134,
+ 0x11173, 0x11173,
+ 0x11180, 0x11181,
+ 0x111b6, 0x111be,
+ 0x111c9, 0x111cc,
+ 0x1122f, 0x11231,
+ 0x11234, 0x11234,
+ 0x11236, 0x11237,
+ 0x1123e, 0x1123e,
+ 0x112df, 0x112df,
+ 0x112e3, 0x112ea,
+ 0x11300, 0x11301,
+ 0x1133b, 0x1133c,
+ 0x1133e, 0x1133e,
+ 0x11340, 0x11340,
+ 0x11357, 0x11357,
+ 0x11366, 0x1136c,
+ 0x11370, 0x11374,
+ 0x11438, 0x1143f,
+ 0x11442, 0x11444,
+ 0x11446, 0x11446,
+ 0x1145e, 0x1145e,
+ 0x114b0, 0x114b0,
+ 0x114b3, 0x114b8,
+ 0x114ba, 0x114ba,
+ 0x114bd, 0x114bd,
+ 0x114bf, 0x114c0,
+ 0x114c2, 0x114c3,
+ 0x115af, 0x115af,
+ 0x115b2, 0x115b5,
+ 0x115bc, 0x115bd,
+ 0x115bf, 0x115c0,
+ 0x115dc, 0x115dd,
+ 0x11633, 0x1163a,
+ 0x1163d, 0x1163d,
+ 0x1163f, 0x11640,
+ 0x116ab, 0x116ab,
+ 0x116ad, 0x116ad,
+ 0x116b0, 0x116b5,
+ 0x116b7, 0x116b7,
+ 0x1171d, 0x1171f,
+ 0x11722, 0x11725,
+ 0x11727, 0x1172b,
+ 0x1182f, 0x11837,
+ 0x11839, 0x1183a,
+ 0x119d4, 0x119d7,
+ 0x119da, 0x119db,
+ 0x119e0, 0x119e0,
+ 0x11a01, 0x11a0a,
+ 0x11a33, 0x11a38,
+ 0x11a3b, 0x11a3e,
+ 0x11a47, 0x11a47,
+ 0x11a51, 0x11a56,
+ 0x11a59, 0x11a5b,
+ 0x11a8a, 0x11a96,
+ 0x11a98, 0x11a99,
+ 0x11c30, 0x11c36,
+ 0x11c38, 0x11c3d,
+ 0x11c3f, 0x11c3f,
+ 0x11c92, 0x11ca7,
+ 0x11caa, 0x11cb0,
+ 0x11cb2, 0x11cb3,
+ 0x11cb5, 0x11cb6,
+ 0x11d31, 0x11d36,
+ 0x11d3a, 0x11d3a,
+ 0x11d3c, 0x11d3d,
+ 0x11d3f, 0x11d45,
+ 0x11d47, 0x11d47,
+ 0x11d90, 0x11d91,
+ 0x11d95, 0x11d95,
+ 0x11d97, 0x11d97,
+ 0x11ef3, 0x11ef4,
+ 0x16af0, 0x16af4,
+ 0x16b30, 0x16b36,
+ 0x16f4f, 0x16f4f,
+ 0x16f8f, 0x16f92,
+ 0x1bc9d, 0x1bc9e,
+ 0x1d165, 0x1d165,
+ 0x1d167, 0x1d169,
+ 0x1d16e, 0x1d172,
+ 0x1d17b, 0x1d182,
+ 0x1d185, 0x1d18b,
+ 0x1d1aa, 0x1d1ad,
+ 0x1d242, 0x1d244,
+ 0x1da00, 0x1da36,
+ 0x1da3b, 0x1da6c,
+ 0x1da75, 0x1da75,
+ 0x1da84, 0x1da84,
+ 0x1da9b, 0x1da9f,
+ 0x1daa1, 0x1daaf,
+ 0x1e000, 0x1e006,
+ 0x1e008, 0x1e018,
+ 0x1e01b, 0x1e021,
+ 0x1e023, 0x1e024,
+ 0x1e026, 0x1e02a,
+ 0x1e130, 0x1e136,
+ 0x1e2ec, 0x1e2ef,
+ 0x1e8d0, 0x1e8d6,
+ 0x1e944, 0x1e94a,
+ 0x1f3fb, 0x1f3ff,
+ 0xe0020, 0xe007f,
+ 0xe0100, 0xe01ef,
+}; /* CR_Grapheme_Cluster_Break_Extend */
+
+/* 'Grapheme_Cluster_Break_Regional_Indicator': Grapheme_Cluster_Break=Regional_Indicator */
+#define CR_Grapheme_Cluster_Break_Regional_Indicator CR_Regional_Indicator
+
+/* 'Grapheme_Cluster_Break_SpacingMark': Grapheme_Cluster_Break=SpacingMark */
+static const OnigCodePoint CR_Grapheme_Cluster_Break_SpacingMark[] = {
+ 152,
+ 0x0903, 0x0903,
+ 0x093b, 0x093b,
+ 0x093e, 0x0940,
+ 0x0949, 0x094c,
+ 0x094e, 0x094f,
+ 0x0982, 0x0983,
+ 0x09bf, 0x09c0,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09cc,
+ 0x0a03, 0x0a03,
+ 0x0a3e, 0x0a40,
+ 0x0a83, 0x0a83,
+ 0x0abe, 0x0ac0,
+ 0x0ac9, 0x0ac9,
+ 0x0acb, 0x0acc,
+ 0x0b02, 0x0b03,
+ 0x0b40, 0x0b40,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4c,
+ 0x0bbf, 0x0bbf,
+ 0x0bc1, 0x0bc2,
+ 0x0bc6, 0x0bc8,
+ 0x0bca, 0x0bcc,
+ 0x0c01, 0x0c03,
+ 0x0c41, 0x0c44,
+ 0x0c82, 0x0c83,
+ 0x0cbe, 0x0cbe,
+ 0x0cc0, 0x0cc1,
+ 0x0cc3, 0x0cc4,
+ 0x0cc7, 0x0cc8,
+ 0x0cca, 0x0ccb,
+ 0x0d02, 0x0d03,
+ 0x0d3f, 0x0d40,
+ 0x0d46, 0x0d48,
+ 0x0d4a, 0x0d4c,
+ 0x0d82, 0x0d83,
+ 0x0dd0, 0x0dd1,
+ 0x0dd8, 0x0dde,
+ 0x0df2, 0x0df3,
+ 0x0e33, 0x0e33,
+ 0x0eb3, 0x0eb3,
+ 0x0f3e, 0x0f3f,
+ 0x0f7f, 0x0f7f,
+ 0x1031, 0x1031,
+ 0x103b, 0x103c,
+ 0x1056, 0x1057,
+ 0x1084, 0x1084,
+ 0x17b6, 0x17b6,
+ 0x17be, 0x17c5,
+ 0x17c7, 0x17c8,
+ 0x1923, 0x1926,
+ 0x1929, 0x192b,
+ 0x1930, 0x1931,
+ 0x1933, 0x1938,
+ 0x1a19, 0x1a1a,
+ 0x1a55, 0x1a55,
+ 0x1a57, 0x1a57,
+ 0x1a6d, 0x1a72,
+ 0x1b04, 0x1b04,
+ 0x1b3b, 0x1b3b,
+ 0x1b3d, 0x1b41,
+ 0x1b43, 0x1b44,
+ 0x1b82, 0x1b82,
+ 0x1ba1, 0x1ba1,
+ 0x1ba6, 0x1ba7,
+ 0x1baa, 0x1baa,
+ 0x1be7, 0x1be7,
+ 0x1bea, 0x1bec,
+ 0x1bee, 0x1bee,
+ 0x1bf2, 0x1bf3,
+ 0x1c24, 0x1c2b,
+ 0x1c34, 0x1c35,
+ 0x1ce1, 0x1ce1,
+ 0x1cf7, 0x1cf7,
+ 0xa823, 0xa824,
+ 0xa827, 0xa827,
+ 0xa880, 0xa881,
+ 0xa8b4, 0xa8c3,
+ 0xa952, 0xa953,
+ 0xa983, 0xa983,
+ 0xa9b4, 0xa9b5,
+ 0xa9ba, 0xa9bb,
+ 0xa9be, 0xa9c0,
+ 0xaa2f, 0xaa30,
+ 0xaa33, 0xaa34,
+ 0xaa4d, 0xaa4d,
+ 0xaaeb, 0xaaeb,
+ 0xaaee, 0xaaef,
+ 0xaaf5, 0xaaf5,
+ 0xabe3, 0xabe4,
+ 0xabe6, 0xabe7,
+ 0xabe9, 0xabea,
+ 0xabec, 0xabec,
+ 0x11000, 0x11000,
+ 0x11002, 0x11002,
+ 0x11082, 0x11082,
+ 0x110b0, 0x110b2,
+ 0x110b7, 0x110b8,
+ 0x1112c, 0x1112c,
+ 0x11145, 0x11146,
+ 0x11182, 0x11182,
+ 0x111b3, 0x111b5,
+ 0x111bf, 0x111c0,
+ 0x1122c, 0x1122e,
+ 0x11232, 0x11233,
+ 0x11235, 0x11235,
+ 0x112e0, 0x112e2,
+ 0x11302, 0x11303,
+ 0x1133f, 0x1133f,
+ 0x11341, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134b, 0x1134d,
+ 0x11362, 0x11363,
+ 0x11435, 0x11437,
+ 0x11440, 0x11441,
+ 0x11445, 0x11445,
+ 0x114b1, 0x114b2,
+ 0x114b9, 0x114b9,
+ 0x114bb, 0x114bc,
+ 0x114be, 0x114be,
+ 0x114c1, 0x114c1,
+ 0x115b0, 0x115b1,
+ 0x115b8, 0x115bb,
+ 0x115be, 0x115be,
+ 0x11630, 0x11632,
+ 0x1163b, 0x1163c,
+ 0x1163e, 0x1163e,
+ 0x116ac, 0x116ac,
+ 0x116ae, 0x116af,
+ 0x116b6, 0x116b6,
+ 0x11720, 0x11721,
+ 0x11726, 0x11726,
+ 0x1182c, 0x1182e,
+ 0x11838, 0x11838,
+ 0x119d1, 0x119d3,
+ 0x119dc, 0x119df,
+ 0x119e4, 0x119e4,
+ 0x11a39, 0x11a39,
+ 0x11a57, 0x11a58,
+ 0x11a97, 0x11a97,
+ 0x11c2f, 0x11c2f,
+ 0x11c3e, 0x11c3e,
+ 0x11ca9, 0x11ca9,
+ 0x11cb1, 0x11cb1,
+ 0x11cb4, 0x11cb4,
+ 0x11d8a, 0x11d8e,
+ 0x11d93, 0x11d94,
+ 0x11d96, 0x11d96,
+ 0x11ef5, 0x11ef6,
+ 0x16f51, 0x16f87,
+ 0x1d166, 0x1d166,
+ 0x1d16d, 0x1d16d,
+}; /* CR_Grapheme_Cluster_Break_SpacingMark */
+
+/* 'Grapheme_Cluster_Break_L': Grapheme_Cluster_Break=L */
+static const OnigCodePoint CR_Grapheme_Cluster_Break_L[] = {
+ 2,
+ 0x1100, 0x115f,
+ 0xa960, 0xa97c,
+}; /* CR_Grapheme_Cluster_Break_L */
+
+/* 'Grapheme_Cluster_Break_V': Grapheme_Cluster_Break=V */
+static const OnigCodePoint CR_Grapheme_Cluster_Break_V[] = {
+ 2,
+ 0x1160, 0x11a7,
+ 0xd7b0, 0xd7c6,
+}; /* CR_Grapheme_Cluster_Break_V */
+
+/* 'Grapheme_Cluster_Break_T': Grapheme_Cluster_Break=T */
+static const OnigCodePoint CR_Grapheme_Cluster_Break_T[] = {
+ 2,
+ 0x11a8, 0x11ff,
+ 0xd7cb, 0xd7fb,
+}; /* CR_Grapheme_Cluster_Break_T */
+
+/* 'Grapheme_Cluster_Break_LV': Grapheme_Cluster_Break=LV */
+static const OnigCodePoint CR_Grapheme_Cluster_Break_LV[] = {
+ 399,
+ 0xac00, 0xac00,
+ 0xac1c, 0xac1c,
+ 0xac38, 0xac38,
+ 0xac54, 0xac54,
+ 0xac70, 0xac70,
+ 0xac8c, 0xac8c,
+ 0xaca8, 0xaca8,
+ 0xacc4, 0xacc4,
+ 0xace0, 0xace0,
+ 0xacfc, 0xacfc,
+ 0xad18, 0xad18,
+ 0xad34, 0xad34,
+ 0xad50, 0xad50,
+ 0xad6c, 0xad6c,
+ 0xad88, 0xad88,
+ 0xada4, 0xada4,
+ 0xadc0, 0xadc0,
+ 0xaddc, 0xaddc,
+ 0xadf8, 0xadf8,
+ 0xae14, 0xae14,
+ 0xae30, 0xae30,
+ 0xae4c, 0xae4c,
+ 0xae68, 0xae68,
+ 0xae84, 0xae84,
+ 0xaea0, 0xaea0,
+ 0xaebc, 0xaebc,
+ 0xaed8, 0xaed8,
+ 0xaef4, 0xaef4,
+ 0xaf10, 0xaf10,
+ 0xaf2c, 0xaf2c,
+ 0xaf48, 0xaf48,
+ 0xaf64, 0xaf64,
+ 0xaf80, 0xaf80,
+ 0xaf9c, 0xaf9c,
+ 0xafb8, 0xafb8,
+ 0xafd4, 0xafd4,
+ 0xaff0, 0xaff0,
+ 0xb00c, 0xb00c,
+ 0xb028, 0xb028,
+ 0xb044, 0xb044,
+ 0xb060, 0xb060,
+ 0xb07c, 0xb07c,
+ 0xb098, 0xb098,
+ 0xb0b4, 0xb0b4,
+ 0xb0d0, 0xb0d0,
+ 0xb0ec, 0xb0ec,
+ 0xb108, 0xb108,
+ 0xb124, 0xb124,
+ 0xb140, 0xb140,
+ 0xb15c, 0xb15c,
+ 0xb178, 0xb178,
+ 0xb194, 0xb194,
+ 0xb1b0, 0xb1b0,
+ 0xb1cc, 0xb1cc,
+ 0xb1e8, 0xb1e8,
+ 0xb204, 0xb204,
+ 0xb220, 0xb220,
+ 0xb23c, 0xb23c,
+ 0xb258, 0xb258,
+ 0xb274, 0xb274,
+ 0xb290, 0xb290,
+ 0xb2ac, 0xb2ac,
+ 0xb2c8, 0xb2c8,
+ 0xb2e4, 0xb2e4,
+ 0xb300, 0xb300,
+ 0xb31c, 0xb31c,
+ 0xb338, 0xb338,
+ 0xb354, 0xb354,
+ 0xb370, 0xb370,
+ 0xb38c, 0xb38c,
+ 0xb3a8, 0xb3a8,
+ 0xb3c4, 0xb3c4,
+ 0xb3e0, 0xb3e0,
+ 0xb3fc, 0xb3fc,
+ 0xb418, 0xb418,
+ 0xb434, 0xb434,
+ 0xb450, 0xb450,
+ 0xb46c, 0xb46c,
+ 0xb488, 0xb488,
+ 0xb4a4, 0xb4a4,
+ 0xb4c0, 0xb4c0,
+ 0xb4dc, 0xb4dc,
+ 0xb4f8, 0xb4f8,
+ 0xb514, 0xb514,
+ 0xb530, 0xb530,
+ 0xb54c, 0xb54c,
+ 0xb568, 0xb568,
+ 0xb584, 0xb584,
+ 0xb5a0, 0xb5a0,
+ 0xb5bc, 0xb5bc,
+ 0xb5d8, 0xb5d8,
+ 0xb5f4, 0xb5f4,
+ 0xb610, 0xb610,
+ 0xb62c, 0xb62c,
+ 0xb648, 0xb648,
+ 0xb664, 0xb664,
+ 0xb680, 0xb680,
+ 0xb69c, 0xb69c,
+ 0xb6b8, 0xb6b8,
+ 0xb6d4, 0xb6d4,
+ 0xb6f0, 0xb6f0,
+ 0xb70c, 0xb70c,
+ 0xb728, 0xb728,
+ 0xb744, 0xb744,
+ 0xb760, 0xb760,
+ 0xb77c, 0xb77c,
+ 0xb798, 0xb798,
+ 0xb7b4, 0xb7b4,
+ 0xb7d0, 0xb7d0,
+ 0xb7ec, 0xb7ec,
+ 0xb808, 0xb808,
+ 0xb824, 0xb824,
+ 0xb840, 0xb840,
+ 0xb85c, 0xb85c,
+ 0xb878, 0xb878,
+ 0xb894, 0xb894,
+ 0xb8b0, 0xb8b0,
+ 0xb8cc, 0xb8cc,
+ 0xb8e8, 0xb8e8,
+ 0xb904, 0xb904,
+ 0xb920, 0xb920,
+ 0xb93c, 0xb93c,
+ 0xb958, 0xb958,
+ 0xb974, 0xb974,
+ 0xb990, 0xb990,
+ 0xb9ac, 0xb9ac,
+ 0xb9c8, 0xb9c8,
+ 0xb9e4, 0xb9e4,
+ 0xba00, 0xba00,
+ 0xba1c, 0xba1c,
+ 0xba38, 0xba38,
+ 0xba54, 0xba54,
+ 0xba70, 0xba70,
+ 0xba8c, 0xba8c,
+ 0xbaa8, 0xbaa8,
+ 0xbac4, 0xbac4,
+ 0xbae0, 0xbae0,
+ 0xbafc, 0xbafc,
+ 0xbb18, 0xbb18,
+ 0xbb34, 0xbb34,
+ 0xbb50, 0xbb50,
+ 0xbb6c, 0xbb6c,
+ 0xbb88, 0xbb88,
+ 0xbba4, 0xbba4,
+ 0xbbc0, 0xbbc0,
+ 0xbbdc, 0xbbdc,
+ 0xbbf8, 0xbbf8,
+ 0xbc14, 0xbc14,
+ 0xbc30, 0xbc30,
+ 0xbc4c, 0xbc4c,
+ 0xbc68, 0xbc68,
+ 0xbc84, 0xbc84,
+ 0xbca0, 0xbca0,
+ 0xbcbc, 0xbcbc,
+ 0xbcd8, 0xbcd8,
+ 0xbcf4, 0xbcf4,
+ 0xbd10, 0xbd10,
+ 0xbd2c, 0xbd2c,
+ 0xbd48, 0xbd48,
+ 0xbd64, 0xbd64,
+ 0xbd80, 0xbd80,
+ 0xbd9c, 0xbd9c,
+ 0xbdb8, 0xbdb8,
+ 0xbdd4, 0xbdd4,
+ 0xbdf0, 0xbdf0,
+ 0xbe0c, 0xbe0c,
+ 0xbe28, 0xbe28,
+ 0xbe44, 0xbe44,
+ 0xbe60, 0xbe60,
+ 0xbe7c, 0xbe7c,
+ 0xbe98, 0xbe98,
+ 0xbeb4, 0xbeb4,
+ 0xbed0, 0xbed0,
+ 0xbeec, 0xbeec,
+ 0xbf08, 0xbf08,
+ 0xbf24, 0xbf24,
+ 0xbf40, 0xbf40,
+ 0xbf5c, 0xbf5c,
+ 0xbf78, 0xbf78,
+ 0xbf94, 0xbf94,
+ 0xbfb0, 0xbfb0,
+ 0xbfcc, 0xbfcc,
+ 0xbfe8, 0xbfe8,
+ 0xc004, 0xc004,
+ 0xc020, 0xc020,
+ 0xc03c, 0xc03c,
+ 0xc058, 0xc058,
+ 0xc074, 0xc074,
+ 0xc090, 0xc090,
+ 0xc0ac, 0xc0ac,
+ 0xc0c8, 0xc0c8,
+ 0xc0e4, 0xc0e4,
+ 0xc100, 0xc100,
+ 0xc11c, 0xc11c,
+ 0xc138, 0xc138,
+ 0xc154, 0xc154,
+ 0xc170, 0xc170,
+ 0xc18c, 0xc18c,
+ 0xc1a8, 0xc1a8,
+ 0xc1c4, 0xc1c4,
+ 0xc1e0, 0xc1e0,
+ 0xc1fc, 0xc1fc,
+ 0xc218, 0xc218,
+ 0xc234, 0xc234,
+ 0xc250, 0xc250,
+ 0xc26c, 0xc26c,
+ 0xc288, 0xc288,
+ 0xc2a4, 0xc2a4,
+ 0xc2c0, 0xc2c0,
+ 0xc2dc, 0xc2dc,
+ 0xc2f8, 0xc2f8,
+ 0xc314, 0xc314,
+ 0xc330, 0xc330,
+ 0xc34c, 0xc34c,
+ 0xc368, 0xc368,
+ 0xc384, 0xc384,
+ 0xc3a0, 0xc3a0,
+ 0xc3bc, 0xc3bc,
+ 0xc3d8, 0xc3d8,
+ 0xc3f4, 0xc3f4,
+ 0xc410, 0xc410,
+ 0xc42c, 0xc42c,
+ 0xc448, 0xc448,
+ 0xc464, 0xc464,
+ 0xc480, 0xc480,
+ 0xc49c, 0xc49c,
+ 0xc4b8, 0xc4b8,
+ 0xc4d4, 0xc4d4,
+ 0xc4f0, 0xc4f0,
+ 0xc50c, 0xc50c,
+ 0xc528, 0xc528,
+ 0xc544, 0xc544,
+ 0xc560, 0xc560,
+ 0xc57c, 0xc57c,
+ 0xc598, 0xc598,
+ 0xc5b4, 0xc5b4,
+ 0xc5d0, 0xc5d0,
+ 0xc5ec, 0xc5ec,
+ 0xc608, 0xc608,
+ 0xc624, 0xc624,
+ 0xc640, 0xc640,
+ 0xc65c, 0xc65c,
+ 0xc678, 0xc678,
+ 0xc694, 0xc694,
+ 0xc6b0, 0xc6b0,
+ 0xc6cc, 0xc6cc,
+ 0xc6e8, 0xc6e8,
+ 0xc704, 0xc704,
+ 0xc720, 0xc720,
+ 0xc73c, 0xc73c,
+ 0xc758, 0xc758,
+ 0xc774, 0xc774,
+ 0xc790, 0xc790,
+ 0xc7ac, 0xc7ac,
+ 0xc7c8, 0xc7c8,
+ 0xc7e4, 0xc7e4,
+ 0xc800, 0xc800,
+ 0xc81c, 0xc81c,
+ 0xc838, 0xc838,
+ 0xc854, 0xc854,
+ 0xc870, 0xc870,
+ 0xc88c, 0xc88c,
+ 0xc8a8, 0xc8a8,
+ 0xc8c4, 0xc8c4,
+ 0xc8e0, 0xc8e0,
+ 0xc8fc, 0xc8fc,
+ 0xc918, 0xc918,
+ 0xc934, 0xc934,
+ 0xc950, 0xc950,
+ 0xc96c, 0xc96c,
+ 0xc988, 0xc988,
+ 0xc9a4, 0xc9a4,
+ 0xc9c0, 0xc9c0,
+ 0xc9dc, 0xc9dc,
+ 0xc9f8, 0xc9f8,
+ 0xca14, 0xca14,
+ 0xca30, 0xca30,
+ 0xca4c, 0xca4c,
+ 0xca68, 0xca68,
+ 0xca84, 0xca84,
+ 0xcaa0, 0xcaa0,
+ 0xcabc, 0xcabc,
+ 0xcad8, 0xcad8,
+ 0xcaf4, 0xcaf4,
+ 0xcb10, 0xcb10,
+ 0xcb2c, 0xcb2c,
+ 0xcb48, 0xcb48,
+ 0xcb64, 0xcb64,
+ 0xcb80, 0xcb80,
+ 0xcb9c, 0xcb9c,
+ 0xcbb8, 0xcbb8,
+ 0xcbd4, 0xcbd4,
+ 0xcbf0, 0xcbf0,
+ 0xcc0c, 0xcc0c,
+ 0xcc28, 0xcc28,
+ 0xcc44, 0xcc44,
+ 0xcc60, 0xcc60,
+ 0xcc7c, 0xcc7c,
+ 0xcc98, 0xcc98,
+ 0xccb4, 0xccb4,
+ 0xccd0, 0xccd0,
+ 0xccec, 0xccec,
+ 0xcd08, 0xcd08,
+ 0xcd24, 0xcd24,
+ 0xcd40, 0xcd40,
+ 0xcd5c, 0xcd5c,
+ 0xcd78, 0xcd78,
+ 0xcd94, 0xcd94,
+ 0xcdb0, 0xcdb0,
+ 0xcdcc, 0xcdcc,
+ 0xcde8, 0xcde8,
+ 0xce04, 0xce04,
+ 0xce20, 0xce20,
+ 0xce3c, 0xce3c,
+ 0xce58, 0xce58,
+ 0xce74, 0xce74,
+ 0xce90, 0xce90,
+ 0xceac, 0xceac,
+ 0xcec8, 0xcec8,
+ 0xcee4, 0xcee4,
+ 0xcf00, 0xcf00,
+ 0xcf1c, 0xcf1c,
+ 0xcf38, 0xcf38,
+ 0xcf54, 0xcf54,
+ 0xcf70, 0xcf70,
+ 0xcf8c, 0xcf8c,
+ 0xcfa8, 0xcfa8,
+ 0xcfc4, 0xcfc4,
+ 0xcfe0, 0xcfe0,
+ 0xcffc, 0xcffc,
+ 0xd018, 0xd018,
+ 0xd034, 0xd034,
+ 0xd050, 0xd050,
+ 0xd06c, 0xd06c,
+ 0xd088, 0xd088,
+ 0xd0a4, 0xd0a4,
+ 0xd0c0, 0xd0c0,
+ 0xd0dc, 0xd0dc,
+ 0xd0f8, 0xd0f8,
+ 0xd114, 0xd114,
+ 0xd130, 0xd130,
+ 0xd14c, 0xd14c,
+ 0xd168, 0xd168,
+ 0xd184, 0xd184,
+ 0xd1a0, 0xd1a0,
+ 0xd1bc, 0xd1bc,
+ 0xd1d8, 0xd1d8,
+ 0xd1f4, 0xd1f4,
+ 0xd210, 0xd210,
+ 0xd22c, 0xd22c,
+ 0xd248, 0xd248,
+ 0xd264, 0xd264,
+ 0xd280, 0xd280,
+ 0xd29c, 0xd29c,
+ 0xd2b8, 0xd2b8,
+ 0xd2d4, 0xd2d4,
+ 0xd2f0, 0xd2f0,
+ 0xd30c, 0xd30c,
+ 0xd328, 0xd328,
+ 0xd344, 0xd344,
+ 0xd360, 0xd360,
+ 0xd37c, 0xd37c,
+ 0xd398, 0xd398,
+ 0xd3b4, 0xd3b4,
+ 0xd3d0, 0xd3d0,
+ 0xd3ec, 0xd3ec,
+ 0xd408, 0xd408,
+ 0xd424, 0xd424,
+ 0xd440, 0xd440,
+ 0xd45c, 0xd45c,
+ 0xd478, 0xd478,
+ 0xd494, 0xd494,
+ 0xd4b0, 0xd4b0,
+ 0xd4cc, 0xd4cc,
+ 0xd4e8, 0xd4e8,
+ 0xd504, 0xd504,
+ 0xd520, 0xd520,
+ 0xd53c, 0xd53c,
+ 0xd558, 0xd558,
+ 0xd574, 0xd574,
+ 0xd590, 0xd590,
+ 0xd5ac, 0xd5ac,
+ 0xd5c8, 0xd5c8,
+ 0xd5e4, 0xd5e4,
+ 0xd600, 0xd600,
+ 0xd61c, 0xd61c,
+ 0xd638, 0xd638,
+ 0xd654, 0xd654,
+ 0xd670, 0xd670,
+ 0xd68c, 0xd68c,
+ 0xd6a8, 0xd6a8,
+ 0xd6c4, 0xd6c4,
+ 0xd6e0, 0xd6e0,
+ 0xd6fc, 0xd6fc,
+ 0xd718, 0xd718,
+ 0xd734, 0xd734,
+ 0xd750, 0xd750,
+ 0xd76c, 0xd76c,
+ 0xd788, 0xd788,
+}; /* CR_Grapheme_Cluster_Break_LV */
+
+/* 'Grapheme_Cluster_Break_LVT': Grapheme_Cluster_Break=LVT */
+static const OnigCodePoint CR_Grapheme_Cluster_Break_LVT[] = {
+ 399,
+ 0xac01, 0xac1b,
+ 0xac1d, 0xac37,
+ 0xac39, 0xac53,
+ 0xac55, 0xac6f,
+ 0xac71, 0xac8b,
+ 0xac8d, 0xaca7,
+ 0xaca9, 0xacc3,
+ 0xacc5, 0xacdf,
+ 0xace1, 0xacfb,
+ 0xacfd, 0xad17,
+ 0xad19, 0xad33,
+ 0xad35, 0xad4f,
+ 0xad51, 0xad6b,
+ 0xad6d, 0xad87,
+ 0xad89, 0xada3,
+ 0xada5, 0xadbf,
+ 0xadc1, 0xaddb,
+ 0xaddd, 0xadf7,
+ 0xadf9, 0xae13,
+ 0xae15, 0xae2f,
+ 0xae31, 0xae4b,
+ 0xae4d, 0xae67,
+ 0xae69, 0xae83,
+ 0xae85, 0xae9f,
+ 0xaea1, 0xaebb,
+ 0xaebd, 0xaed7,
+ 0xaed9, 0xaef3,
+ 0xaef5, 0xaf0f,
+ 0xaf11, 0xaf2b,
+ 0xaf2d, 0xaf47,
+ 0xaf49, 0xaf63,
+ 0xaf65, 0xaf7f,
+ 0xaf81, 0xaf9b,
+ 0xaf9d, 0xafb7,
+ 0xafb9, 0xafd3,
+ 0xafd5, 0xafef,
+ 0xaff1, 0xb00b,
+ 0xb00d, 0xb027,
+ 0xb029, 0xb043,
+ 0xb045, 0xb05f,
+ 0xb061, 0xb07b,
+ 0xb07d, 0xb097,
+ 0xb099, 0xb0b3,
+ 0xb0b5, 0xb0cf,
+ 0xb0d1, 0xb0eb,
+ 0xb0ed, 0xb107,
+ 0xb109, 0xb123,
+ 0xb125, 0xb13f,
+ 0xb141, 0xb15b,
+ 0xb15d, 0xb177,
+ 0xb179, 0xb193,
+ 0xb195, 0xb1af,
+ 0xb1b1, 0xb1cb,
+ 0xb1cd, 0xb1e7,
+ 0xb1e9, 0xb203,
+ 0xb205, 0xb21f,
+ 0xb221, 0xb23b,
+ 0xb23d, 0xb257,
+ 0xb259, 0xb273,
+ 0xb275, 0xb28f,
+ 0xb291, 0xb2ab,
+ 0xb2ad, 0xb2c7,
+ 0xb2c9, 0xb2e3,
+ 0xb2e5, 0xb2ff,
+ 0xb301, 0xb31b,
+ 0xb31d, 0xb337,
+ 0xb339, 0xb353,
+ 0xb355, 0xb36f,
+ 0xb371, 0xb38b,
+ 0xb38d, 0xb3a7,
+ 0xb3a9, 0xb3c3,
+ 0xb3c5, 0xb3df,
+ 0xb3e1, 0xb3fb,
+ 0xb3fd, 0xb417,
+ 0xb419, 0xb433,
+ 0xb435, 0xb44f,
+ 0xb451, 0xb46b,
+ 0xb46d, 0xb487,
+ 0xb489, 0xb4a3,
+ 0xb4a5, 0xb4bf,
+ 0xb4c1, 0xb4db,
+ 0xb4dd, 0xb4f7,
+ 0xb4f9, 0xb513,
+ 0xb515, 0xb52f,
+ 0xb531, 0xb54b,
+ 0xb54d, 0xb567,
+ 0xb569, 0xb583,
+ 0xb585, 0xb59f,
+ 0xb5a1, 0xb5bb,
+ 0xb5bd, 0xb5d7,
+ 0xb5d9, 0xb5f3,
+ 0xb5f5, 0xb60f,
+ 0xb611, 0xb62b,
+ 0xb62d, 0xb647,
+ 0xb649, 0xb663,
+ 0xb665, 0xb67f,
+ 0xb681, 0xb69b,
+ 0xb69d, 0xb6b7,
+ 0xb6b9, 0xb6d3,
+ 0xb6d5, 0xb6ef,
+ 0xb6f1, 0xb70b,
+ 0xb70d, 0xb727,
+ 0xb729, 0xb743,
+ 0xb745, 0xb75f,
+ 0xb761, 0xb77b,
+ 0xb77d, 0xb797,
+ 0xb799, 0xb7b3,
+ 0xb7b5, 0xb7cf,
+ 0xb7d1, 0xb7eb,
+ 0xb7ed, 0xb807,
+ 0xb809, 0xb823,
+ 0xb825, 0xb83f,
+ 0xb841, 0xb85b,
+ 0xb85d, 0xb877,
+ 0xb879, 0xb893,
+ 0xb895, 0xb8af,
+ 0xb8b1, 0xb8cb,
+ 0xb8cd, 0xb8e7,
+ 0xb8e9, 0xb903,
+ 0xb905, 0xb91f,
+ 0xb921, 0xb93b,
+ 0xb93d, 0xb957,
+ 0xb959, 0xb973,
+ 0xb975, 0xb98f,
+ 0xb991, 0xb9ab,
+ 0xb9ad, 0xb9c7,
+ 0xb9c9, 0xb9e3,
+ 0xb9e5, 0xb9ff,
+ 0xba01, 0xba1b,
+ 0xba1d, 0xba37,
+ 0xba39, 0xba53,
+ 0xba55, 0xba6f,
+ 0xba71, 0xba8b,
+ 0xba8d, 0xbaa7,
+ 0xbaa9, 0xbac3,
+ 0xbac5, 0xbadf,
+ 0xbae1, 0xbafb,
+ 0xbafd, 0xbb17,
+ 0xbb19, 0xbb33,
+ 0xbb35, 0xbb4f,
+ 0xbb51, 0xbb6b,
+ 0xbb6d, 0xbb87,
+ 0xbb89, 0xbba3,
+ 0xbba5, 0xbbbf,
+ 0xbbc1, 0xbbdb,
+ 0xbbdd, 0xbbf7,
+ 0xbbf9, 0xbc13,
+ 0xbc15, 0xbc2f,
+ 0xbc31, 0xbc4b,
+ 0xbc4d, 0xbc67,
+ 0xbc69, 0xbc83,
+ 0xbc85, 0xbc9f,
+ 0xbca1, 0xbcbb,
+ 0xbcbd, 0xbcd7,
+ 0xbcd9, 0xbcf3,
+ 0xbcf5, 0xbd0f,
+ 0xbd11, 0xbd2b,
+ 0xbd2d, 0xbd47,
+ 0xbd49, 0xbd63,
+ 0xbd65, 0xbd7f,
+ 0xbd81, 0xbd9b,
+ 0xbd9d, 0xbdb7,
+ 0xbdb9, 0xbdd3,
+ 0xbdd5, 0xbdef,
+ 0xbdf1, 0xbe0b,
+ 0xbe0d, 0xbe27,
+ 0xbe29, 0xbe43,
+ 0xbe45, 0xbe5f,
+ 0xbe61, 0xbe7b,
+ 0xbe7d, 0xbe97,
+ 0xbe99, 0xbeb3,
+ 0xbeb5, 0xbecf,
+ 0xbed1, 0xbeeb,
+ 0xbeed, 0xbf07,
+ 0xbf09, 0xbf23,
+ 0xbf25, 0xbf3f,
+ 0xbf41, 0xbf5b,
+ 0xbf5d, 0xbf77,
+ 0xbf79, 0xbf93,
+ 0xbf95, 0xbfaf,
+ 0xbfb1, 0xbfcb,
+ 0xbfcd, 0xbfe7,
+ 0xbfe9, 0xc003,
+ 0xc005, 0xc01f,
+ 0xc021, 0xc03b,
+ 0xc03d, 0xc057,
+ 0xc059, 0xc073,
+ 0xc075, 0xc08f,
+ 0xc091, 0xc0ab,
+ 0xc0ad, 0xc0c7,
+ 0xc0c9, 0xc0e3,
+ 0xc0e5, 0xc0ff,
+ 0xc101, 0xc11b,
+ 0xc11d, 0xc137,
+ 0xc139, 0xc153,
+ 0xc155, 0xc16f,
+ 0xc171, 0xc18b,
+ 0xc18d, 0xc1a7,
+ 0xc1a9, 0xc1c3,
+ 0xc1c5, 0xc1df,
+ 0xc1e1, 0xc1fb,
+ 0xc1fd, 0xc217,
+ 0xc219, 0xc233,
+ 0xc235, 0xc24f,
+ 0xc251, 0xc26b,
+ 0xc26d, 0xc287,
+ 0xc289, 0xc2a3,
+ 0xc2a5, 0xc2bf,
+ 0xc2c1, 0xc2db,
+ 0xc2dd, 0xc2f7,
+ 0xc2f9, 0xc313,
+ 0xc315, 0xc32f,
+ 0xc331, 0xc34b,
+ 0xc34d, 0xc367,
+ 0xc369, 0xc383,
+ 0xc385, 0xc39f,
+ 0xc3a1, 0xc3bb,
+ 0xc3bd, 0xc3d7,
+ 0xc3d9, 0xc3f3,
+ 0xc3f5, 0xc40f,
+ 0xc411, 0xc42b,
+ 0xc42d, 0xc447,
+ 0xc449, 0xc463,
+ 0xc465, 0xc47f,
+ 0xc481, 0xc49b,
+ 0xc49d, 0xc4b7,
+ 0xc4b9, 0xc4d3,
+ 0xc4d5, 0xc4ef,
+ 0xc4f1, 0xc50b,
+ 0xc50d, 0xc527,
+ 0xc529, 0xc543,
+ 0xc545, 0xc55f,
+ 0xc561, 0xc57b,
+ 0xc57d, 0xc597,
+ 0xc599, 0xc5b3,
+ 0xc5b5, 0xc5cf,
+ 0xc5d1, 0xc5eb,
+ 0xc5ed, 0xc607,
+ 0xc609, 0xc623,
+ 0xc625, 0xc63f,
+ 0xc641, 0xc65b,
+ 0xc65d, 0xc677,
+ 0xc679, 0xc693,
+ 0xc695, 0xc6af,
+ 0xc6b1, 0xc6cb,
+ 0xc6cd, 0xc6e7,
+ 0xc6e9, 0xc703,
+ 0xc705, 0xc71f,
+ 0xc721, 0xc73b,
+ 0xc73d, 0xc757,
+ 0xc759, 0xc773,
+ 0xc775, 0xc78f,
+ 0xc791, 0xc7ab,
+ 0xc7ad, 0xc7c7,
+ 0xc7c9, 0xc7e3,
+ 0xc7e5, 0xc7ff,
+ 0xc801, 0xc81b,
+ 0xc81d, 0xc837,
+ 0xc839, 0xc853,
+ 0xc855, 0xc86f,
+ 0xc871, 0xc88b,
+ 0xc88d, 0xc8a7,
+ 0xc8a9, 0xc8c3,
+ 0xc8c5, 0xc8df,
+ 0xc8e1, 0xc8fb,
+ 0xc8fd, 0xc917,
+ 0xc919, 0xc933,
+ 0xc935, 0xc94f,
+ 0xc951, 0xc96b,
+ 0xc96d, 0xc987,
+ 0xc989, 0xc9a3,
+ 0xc9a5, 0xc9bf,
+ 0xc9c1, 0xc9db,
+ 0xc9dd, 0xc9f7,
+ 0xc9f9, 0xca13,
+ 0xca15, 0xca2f,
+ 0xca31, 0xca4b,
+ 0xca4d, 0xca67,
+ 0xca69, 0xca83,
+ 0xca85, 0xca9f,
+ 0xcaa1, 0xcabb,
+ 0xcabd, 0xcad7,
+ 0xcad9, 0xcaf3,
+ 0xcaf5, 0xcb0f,
+ 0xcb11, 0xcb2b,
+ 0xcb2d, 0xcb47,
+ 0xcb49, 0xcb63,
+ 0xcb65, 0xcb7f,
+ 0xcb81, 0xcb9b,
+ 0xcb9d, 0xcbb7,
+ 0xcbb9, 0xcbd3,
+ 0xcbd5, 0xcbef,
+ 0xcbf1, 0xcc0b,
+ 0xcc0d, 0xcc27,
+ 0xcc29, 0xcc43,
+ 0xcc45, 0xcc5f,
+ 0xcc61, 0xcc7b,
+ 0xcc7d, 0xcc97,
+ 0xcc99, 0xccb3,
+ 0xccb5, 0xcccf,
+ 0xccd1, 0xcceb,
+ 0xcced, 0xcd07,
+ 0xcd09, 0xcd23,
+ 0xcd25, 0xcd3f,
+ 0xcd41, 0xcd5b,
+ 0xcd5d, 0xcd77,
+ 0xcd79, 0xcd93,
+ 0xcd95, 0xcdaf,
+ 0xcdb1, 0xcdcb,
+ 0xcdcd, 0xcde7,
+ 0xcde9, 0xce03,
+ 0xce05, 0xce1f,
+ 0xce21, 0xce3b,
+ 0xce3d, 0xce57,
+ 0xce59, 0xce73,
+ 0xce75, 0xce8f,
+ 0xce91, 0xceab,
+ 0xcead, 0xcec7,
+ 0xcec9, 0xcee3,
+ 0xcee5, 0xceff,
+ 0xcf01, 0xcf1b,
+ 0xcf1d, 0xcf37,
+ 0xcf39, 0xcf53,
+ 0xcf55, 0xcf6f,
+ 0xcf71, 0xcf8b,
+ 0xcf8d, 0xcfa7,
+ 0xcfa9, 0xcfc3,
+ 0xcfc5, 0xcfdf,
+ 0xcfe1, 0xcffb,
+ 0xcffd, 0xd017,
+ 0xd019, 0xd033,
+ 0xd035, 0xd04f,
+ 0xd051, 0xd06b,
+ 0xd06d, 0xd087,
+ 0xd089, 0xd0a3,
+ 0xd0a5, 0xd0bf,
+ 0xd0c1, 0xd0db,
+ 0xd0dd, 0xd0f7,
+ 0xd0f9, 0xd113,
+ 0xd115, 0xd12f,
+ 0xd131, 0xd14b,
+ 0xd14d, 0xd167,
+ 0xd169, 0xd183,
+ 0xd185, 0xd19f,
+ 0xd1a1, 0xd1bb,
+ 0xd1bd, 0xd1d7,
+ 0xd1d9, 0xd1f3,
+ 0xd1f5, 0xd20f,
+ 0xd211, 0xd22b,
+ 0xd22d, 0xd247,
+ 0xd249, 0xd263,
+ 0xd265, 0xd27f,
+ 0xd281, 0xd29b,
+ 0xd29d, 0xd2b7,
+ 0xd2b9, 0xd2d3,
+ 0xd2d5, 0xd2ef,
+ 0xd2f1, 0xd30b,
+ 0xd30d, 0xd327,
+ 0xd329, 0xd343,
+ 0xd345, 0xd35f,
+ 0xd361, 0xd37b,
+ 0xd37d, 0xd397,
+ 0xd399, 0xd3b3,
+ 0xd3b5, 0xd3cf,
+ 0xd3d1, 0xd3eb,
+ 0xd3ed, 0xd407,
+ 0xd409, 0xd423,
+ 0xd425, 0xd43f,
+ 0xd441, 0xd45b,
+ 0xd45d, 0xd477,
+ 0xd479, 0xd493,
+ 0xd495, 0xd4af,
+ 0xd4b1, 0xd4cb,
+ 0xd4cd, 0xd4e7,
+ 0xd4e9, 0xd503,
+ 0xd505, 0xd51f,
+ 0xd521, 0xd53b,
+ 0xd53d, 0xd557,
+ 0xd559, 0xd573,
+ 0xd575, 0xd58f,
+ 0xd591, 0xd5ab,
+ 0xd5ad, 0xd5c7,
+ 0xd5c9, 0xd5e3,
+ 0xd5e5, 0xd5ff,
+ 0xd601, 0xd61b,
+ 0xd61d, 0xd637,
+ 0xd639, 0xd653,
+ 0xd655, 0xd66f,
+ 0xd671, 0xd68b,
+ 0xd68d, 0xd6a7,
+ 0xd6a9, 0xd6c3,
+ 0xd6c5, 0xd6df,
+ 0xd6e1, 0xd6fb,
+ 0xd6fd, 0xd717,
+ 0xd719, 0xd733,
+ 0xd735, 0xd74f,
+ 0xd751, 0xd76b,
+ 0xd76d, 0xd787,
+ 0xd789, 0xd7a3,
+}; /* CR_Grapheme_Cluster_Break_LVT */
+
+/* 'Grapheme_Cluster_Break_ZWJ': Grapheme_Cluster_Break=ZWJ */
+static const OnigCodePoint CR_Grapheme_Cluster_Break_ZWJ[] = {
+ 1,
+ 0x200d, 0x200d,
+}; /* CR_Grapheme_Cluster_Break_ZWJ */
+
+/* 'In_Basic_Latin': Block */
+#define CR_In_Basic_Latin CR_ASCII
+
+/* 'In_Latin_1_Supplement': Block */
+static const OnigCodePoint CR_In_Latin_1_Supplement[] = {
+ 1,
+ 0x0080, 0x00ff,
+}; /* CR_In_Latin_1_Supplement */
+
+/* 'In_Latin_Extended_A': Block */
+static const OnigCodePoint CR_In_Latin_Extended_A[] = {
+ 1,
+ 0x0100, 0x017f,
+}; /* CR_In_Latin_Extended_A */
+
+/* 'In_Latin_Extended_B': Block */
+static const OnigCodePoint CR_In_Latin_Extended_B[] = {
+ 1,
+ 0x0180, 0x024f,
+}; /* CR_In_Latin_Extended_B */
+
+/* 'In_IPA_Extensions': Block */
+static const OnigCodePoint CR_In_IPA_Extensions[] = {
+ 1,
+ 0x0250, 0x02af,
+}; /* CR_In_IPA_Extensions */
+
+/* 'In_Spacing_Modifier_Letters': Block */
+static const OnigCodePoint CR_In_Spacing_Modifier_Letters[] = {
+ 1,
+ 0x02b0, 0x02ff,
+}; /* CR_In_Spacing_Modifier_Letters */
+
+/* 'In_Combining_Diacritical_Marks': Block */
+static const OnigCodePoint CR_In_Combining_Diacritical_Marks[] = {
+ 1,
+ 0x0300, 0x036f,
+}; /* CR_In_Combining_Diacritical_Marks */
+
+/* 'In_Greek_and_Coptic': Block */
+static const OnigCodePoint CR_In_Greek_and_Coptic[] = {
+ 1,
+ 0x0370, 0x03ff,
+}; /* CR_In_Greek_and_Coptic */
+
+/* 'In_Cyrillic': Block */
+static const OnigCodePoint CR_In_Cyrillic[] = {
+ 1,
+ 0x0400, 0x04ff,
+}; /* CR_In_Cyrillic */
+
+/* 'In_Cyrillic_Supplement': Block */
+static const OnigCodePoint CR_In_Cyrillic_Supplement[] = {
+ 1,
+ 0x0500, 0x052f,
+}; /* CR_In_Cyrillic_Supplement */
+
+/* 'In_Armenian': Block */
+static const OnigCodePoint CR_In_Armenian[] = {
+ 1,
+ 0x0530, 0x058f,
+}; /* CR_In_Armenian */
+
+/* 'In_Hebrew': Block */
+static const OnigCodePoint CR_In_Hebrew[] = {
+ 1,
+ 0x0590, 0x05ff,
+}; /* CR_In_Hebrew */
+
+/* 'In_Arabic': Block */
+static const OnigCodePoint CR_In_Arabic[] = {
+ 1,
+ 0x0600, 0x06ff,
+}; /* CR_In_Arabic */
+
+/* 'In_Syriac': Block */
+static const OnigCodePoint CR_In_Syriac[] = {
+ 1,
+ 0x0700, 0x074f,
+}; /* CR_In_Syriac */
+
+/* 'In_Arabic_Supplement': Block */
+static const OnigCodePoint CR_In_Arabic_Supplement[] = {
+ 1,
+ 0x0750, 0x077f,
+}; /* CR_In_Arabic_Supplement */
+
+/* 'In_Thaana': Block */
+static const OnigCodePoint CR_In_Thaana[] = {
+ 1,
+ 0x0780, 0x07bf,
+}; /* CR_In_Thaana */
+
+/* 'In_NKo': Block */
+static const OnigCodePoint CR_In_NKo[] = {
+ 1,
+ 0x07c0, 0x07ff,
+}; /* CR_In_NKo */
+
+/* 'In_Samaritan': Block */
+static const OnigCodePoint CR_In_Samaritan[] = {
+ 1,
+ 0x0800, 0x083f,
+}; /* CR_In_Samaritan */
+
+/* 'In_Mandaic': Block */
+static const OnigCodePoint CR_In_Mandaic[] = {
+ 1,
+ 0x0840, 0x085f,
+}; /* CR_In_Mandaic */
+
+/* 'In_Syriac_Supplement': Block */
+static const OnigCodePoint CR_In_Syriac_Supplement[] = {
+ 1,
+ 0x0860, 0x086f,
+}; /* CR_In_Syriac_Supplement */
+
+/* 'In_Arabic_Extended_A': Block */
+static const OnigCodePoint CR_In_Arabic_Extended_A[] = {
+ 1,
+ 0x08a0, 0x08ff,
+}; /* CR_In_Arabic_Extended_A */
+
+/* 'In_Devanagari': Block */
+static const OnigCodePoint CR_In_Devanagari[] = {
+ 1,
+ 0x0900, 0x097f,
+}; /* CR_In_Devanagari */
+
+/* 'In_Bengali': Block */
+static const OnigCodePoint CR_In_Bengali[] = {
+ 1,
+ 0x0980, 0x09ff,
+}; /* CR_In_Bengali */
+
+/* 'In_Gurmukhi': Block */
+static const OnigCodePoint CR_In_Gurmukhi[] = {
+ 1,
+ 0x0a00, 0x0a7f,
+}; /* CR_In_Gurmukhi */
+
+/* 'In_Gujarati': Block */
+static const OnigCodePoint CR_In_Gujarati[] = {
+ 1,
+ 0x0a80, 0x0aff,
+}; /* CR_In_Gujarati */
+
+/* 'In_Oriya': Block */
+static const OnigCodePoint CR_In_Oriya[] = {
+ 1,
+ 0x0b00, 0x0b7f,
+}; /* CR_In_Oriya */
+
+/* 'In_Tamil': Block */
+static const OnigCodePoint CR_In_Tamil[] = {
+ 1,
+ 0x0b80, 0x0bff,
+}; /* CR_In_Tamil */
+
+/* 'In_Telugu': Block */
+static const OnigCodePoint CR_In_Telugu[] = {
+ 1,
+ 0x0c00, 0x0c7f,
+}; /* CR_In_Telugu */
+
+/* 'In_Kannada': Block */
+static const OnigCodePoint CR_In_Kannada[] = {
+ 1,
+ 0x0c80, 0x0cff,
+}; /* CR_In_Kannada */
+
+/* 'In_Malayalam': Block */
+static const OnigCodePoint CR_In_Malayalam[] = {
+ 1,
+ 0x0d00, 0x0d7f,
+}; /* CR_In_Malayalam */
+
+/* 'In_Sinhala': Block */
+static const OnigCodePoint CR_In_Sinhala[] = {
+ 1,
+ 0x0d80, 0x0dff,
+}; /* CR_In_Sinhala */
+
+/* 'In_Thai': Block */
+static const OnigCodePoint CR_In_Thai[] = {
+ 1,
+ 0x0e00, 0x0e7f,
+}; /* CR_In_Thai */
+
+/* 'In_Lao': Block */
+static const OnigCodePoint CR_In_Lao[] = {
+ 1,
+ 0x0e80, 0x0eff,
+}; /* CR_In_Lao */
+
+/* 'In_Tibetan': Block */
+static const OnigCodePoint CR_In_Tibetan[] = {
+ 1,
+ 0x0f00, 0x0fff,
+}; /* CR_In_Tibetan */
+
+/* 'In_Myanmar': Block */
+static const OnigCodePoint CR_In_Myanmar[] = {
+ 1,
+ 0x1000, 0x109f,
+}; /* CR_In_Myanmar */
+
+/* 'In_Georgian': Block */
+static const OnigCodePoint CR_In_Georgian[] = {
+ 1,
+ 0x10a0, 0x10ff,
+}; /* CR_In_Georgian */
+
+/* 'In_Hangul_Jamo': Block */
+static const OnigCodePoint CR_In_Hangul_Jamo[] = {
+ 1,
+ 0x1100, 0x11ff,
+}; /* CR_In_Hangul_Jamo */
+
+/* 'In_Ethiopic': Block */
+static const OnigCodePoint CR_In_Ethiopic[] = {
+ 1,
+ 0x1200, 0x137f,
+}; /* CR_In_Ethiopic */
+
+/* 'In_Ethiopic_Supplement': Block */
+static const OnigCodePoint CR_In_Ethiopic_Supplement[] = {
+ 1,
+ 0x1380, 0x139f,
+}; /* CR_In_Ethiopic_Supplement */
+
+/* 'In_Cherokee': Block */
+static const OnigCodePoint CR_In_Cherokee[] = {
+ 1,
+ 0x13a0, 0x13ff,
+}; /* CR_In_Cherokee */
+
+/* 'In_Unified_Canadian_Aboriginal_Syllabics': Block */
+static const OnigCodePoint CR_In_Unified_Canadian_Aboriginal_Syllabics[] = {
+ 1,
+ 0x1400, 0x167f,
+}; /* CR_In_Unified_Canadian_Aboriginal_Syllabics */
+
+/* 'In_Ogham': Block */
+static const OnigCodePoint CR_In_Ogham[] = {
+ 1,
+ 0x1680, 0x169f,
+}; /* CR_In_Ogham */
+
+/* 'In_Runic': Block */
+static const OnigCodePoint CR_In_Runic[] = {
+ 1,
+ 0x16a0, 0x16ff,
+}; /* CR_In_Runic */
+
+/* 'In_Tagalog': Block */
+static const OnigCodePoint CR_In_Tagalog[] = {
+ 1,
+ 0x1700, 0x171f,
+}; /* CR_In_Tagalog */
+
+/* 'In_Hanunoo': Block */
+static const OnigCodePoint CR_In_Hanunoo[] = {
+ 1,
+ 0x1720, 0x173f,
+}; /* CR_In_Hanunoo */
+
+/* 'In_Buhid': Block */
+static const OnigCodePoint CR_In_Buhid[] = {
+ 1,
+ 0x1740, 0x175f,
+}; /* CR_In_Buhid */
+
+/* 'In_Tagbanwa': Block */
+static const OnigCodePoint CR_In_Tagbanwa[] = {
+ 1,
+ 0x1760, 0x177f,
+}; /* CR_In_Tagbanwa */
+
+/* 'In_Khmer': Block */
+static const OnigCodePoint CR_In_Khmer[] = {
+ 1,
+ 0x1780, 0x17ff,
+}; /* CR_In_Khmer */
+
+/* 'In_Mongolian': Block */
+static const OnigCodePoint CR_In_Mongolian[] = {
+ 1,
+ 0x1800, 0x18af,
+}; /* CR_In_Mongolian */
+
+/* 'In_Unified_Canadian_Aboriginal_Syllabics_Extended': Block */
+static const OnigCodePoint CR_In_Unified_Canadian_Aboriginal_Syllabics_Extended[] = {
+ 1,
+ 0x18b0, 0x18ff,
+}; /* CR_In_Unified_Canadian_Aboriginal_Syllabics_Extended */
+
+/* 'In_Limbu': Block */
+static const OnigCodePoint CR_In_Limbu[] = {
+ 1,
+ 0x1900, 0x194f,
+}; /* CR_In_Limbu */
+
+/* 'In_Tai_Le': Block */
+static const OnigCodePoint CR_In_Tai_Le[] = {
+ 1,
+ 0x1950, 0x197f,
+}; /* CR_In_Tai_Le */
+
+/* 'In_New_Tai_Lue': Block */
+static const OnigCodePoint CR_In_New_Tai_Lue[] = {
+ 1,
+ 0x1980, 0x19df,
+}; /* CR_In_New_Tai_Lue */
+
+/* 'In_Khmer_Symbols': Block */
+static const OnigCodePoint CR_In_Khmer_Symbols[] = {
+ 1,
+ 0x19e0, 0x19ff,
+}; /* CR_In_Khmer_Symbols */
+
+/* 'In_Buginese': Block */
+static const OnigCodePoint CR_In_Buginese[] = {
+ 1,
+ 0x1a00, 0x1a1f,
+}; /* CR_In_Buginese */
+
+/* 'In_Tai_Tham': Block */
+static const OnigCodePoint CR_In_Tai_Tham[] = {
+ 1,
+ 0x1a20, 0x1aaf,
+}; /* CR_In_Tai_Tham */
+
+/* 'In_Combining_Diacritical_Marks_Extended': Block */
+static const OnigCodePoint CR_In_Combining_Diacritical_Marks_Extended[] = {
+ 1,
+ 0x1ab0, 0x1aff,
+}; /* CR_In_Combining_Diacritical_Marks_Extended */
+
+/* 'In_Balinese': Block */
+static const OnigCodePoint CR_In_Balinese[] = {
+ 1,
+ 0x1b00, 0x1b7f,
+}; /* CR_In_Balinese */
+
+/* 'In_Sundanese': Block */
+static const OnigCodePoint CR_In_Sundanese[] = {
+ 1,
+ 0x1b80, 0x1bbf,
+}; /* CR_In_Sundanese */
+
+/* 'In_Batak': Block */
+static const OnigCodePoint CR_In_Batak[] = {
+ 1,
+ 0x1bc0, 0x1bff,
+}; /* CR_In_Batak */
+
+/* 'In_Lepcha': Block */
+static const OnigCodePoint CR_In_Lepcha[] = {
+ 1,
+ 0x1c00, 0x1c4f,
+}; /* CR_In_Lepcha */
+
+/* 'In_Ol_Chiki': Block */
+#define CR_In_Ol_Chiki CR_Ol_Chiki
+
+/* 'In_Cyrillic_Extended_C': Block */
+static const OnigCodePoint CR_In_Cyrillic_Extended_C[] = {
+ 1,
+ 0x1c80, 0x1c8f,
+}; /* CR_In_Cyrillic_Extended_C */
+
+/* 'In_Georgian_Extended': Block */
+static const OnigCodePoint CR_In_Georgian_Extended[] = {
+ 1,
+ 0x1c90, 0x1cbf,
+}; /* CR_In_Georgian_Extended */
+
+/* 'In_Sundanese_Supplement': Block */
+static const OnigCodePoint CR_In_Sundanese_Supplement[] = {
+ 1,
+ 0x1cc0, 0x1ccf,
+}; /* CR_In_Sundanese_Supplement */
+
+/* 'In_Vedic_Extensions': Block */
+static const OnigCodePoint CR_In_Vedic_Extensions[] = {
+ 1,
+ 0x1cd0, 0x1cff,
+}; /* CR_In_Vedic_Extensions */
+
+/* 'In_Phonetic_Extensions': Block */
+static const OnigCodePoint CR_In_Phonetic_Extensions[] = {
+ 1,
+ 0x1d00, 0x1d7f,
+}; /* CR_In_Phonetic_Extensions */
+
+/* 'In_Phonetic_Extensions_Supplement': Block */
+static const OnigCodePoint CR_In_Phonetic_Extensions_Supplement[] = {
+ 1,
+ 0x1d80, 0x1dbf,
+}; /* CR_In_Phonetic_Extensions_Supplement */
+
+/* 'In_Combining_Diacritical_Marks_Supplement': Block */
+static const OnigCodePoint CR_In_Combining_Diacritical_Marks_Supplement[] = {
+ 1,
+ 0x1dc0, 0x1dff,
+}; /* CR_In_Combining_Diacritical_Marks_Supplement */
+
+/* 'In_Latin_Extended_Additional': Block */
+static const OnigCodePoint CR_In_Latin_Extended_Additional[] = {
+ 1,
+ 0x1e00, 0x1eff,
+}; /* CR_In_Latin_Extended_Additional */
+
+/* 'In_Greek_Extended': Block */
+static const OnigCodePoint CR_In_Greek_Extended[] = {
+ 1,
+ 0x1f00, 0x1fff,
+}; /* CR_In_Greek_Extended */
+
+/* 'In_General_Punctuation': Block */
+static const OnigCodePoint CR_In_General_Punctuation[] = {
+ 1,
+ 0x2000, 0x206f,
+}; /* CR_In_General_Punctuation */
+
+/* 'In_Superscripts_and_Subscripts': Block */
+static const OnigCodePoint CR_In_Superscripts_and_Subscripts[] = {
+ 1,
+ 0x2070, 0x209f,
+}; /* CR_In_Superscripts_and_Subscripts */
+
+/* 'In_Currency_Symbols': Block */
+static const OnigCodePoint CR_In_Currency_Symbols[] = {
+ 1,
+ 0x20a0, 0x20cf,
+}; /* CR_In_Currency_Symbols */
+
+/* 'In_Combining_Diacritical_Marks_for_Symbols': Block */
+static const OnigCodePoint CR_In_Combining_Diacritical_Marks_for_Symbols[] = {
+ 1,
+ 0x20d0, 0x20ff,
+}; /* CR_In_Combining_Diacritical_Marks_for_Symbols */
+
+/* 'In_Letterlike_Symbols': Block */
+static const OnigCodePoint CR_In_Letterlike_Symbols[] = {
+ 1,
+ 0x2100, 0x214f,
+}; /* CR_In_Letterlike_Symbols */
+
+/* 'In_Number_Forms': Block */
+static const OnigCodePoint CR_In_Number_Forms[] = {
+ 1,
+ 0x2150, 0x218f,
+}; /* CR_In_Number_Forms */
+
+/* 'In_Arrows': Block */
+static const OnigCodePoint CR_In_Arrows[] = {
+ 1,
+ 0x2190, 0x21ff,
+}; /* CR_In_Arrows */
+
+/* 'In_Mathematical_Operators': Block */
+static const OnigCodePoint CR_In_Mathematical_Operators[] = {
+ 1,
+ 0x2200, 0x22ff,
+}; /* CR_In_Mathematical_Operators */
+
+/* 'In_Miscellaneous_Technical': Block */
+static const OnigCodePoint CR_In_Miscellaneous_Technical[] = {
+ 1,
+ 0x2300, 0x23ff,
+}; /* CR_In_Miscellaneous_Technical */
+
+/* 'In_Control_Pictures': Block */
+static const OnigCodePoint CR_In_Control_Pictures[] = {
+ 1,
+ 0x2400, 0x243f,
+}; /* CR_In_Control_Pictures */
+
+/* 'In_Optical_Character_Recognition': Block */
+static const OnigCodePoint CR_In_Optical_Character_Recognition[] = {
+ 1,
+ 0x2440, 0x245f,
+}; /* CR_In_Optical_Character_Recognition */
+
+/* 'In_Enclosed_Alphanumerics': Block */
+static const OnigCodePoint CR_In_Enclosed_Alphanumerics[] = {
+ 1,
+ 0x2460, 0x24ff,
+}; /* CR_In_Enclosed_Alphanumerics */
+
+/* 'In_Box_Drawing': Block */
+static const OnigCodePoint CR_In_Box_Drawing[] = {
+ 1,
+ 0x2500, 0x257f,
+}; /* CR_In_Box_Drawing */
+
+/* 'In_Block_Elements': Block */
+static const OnigCodePoint CR_In_Block_Elements[] = {
+ 1,
+ 0x2580, 0x259f,
+}; /* CR_In_Block_Elements */
+
+/* 'In_Geometric_Shapes': Block */
+static const OnigCodePoint CR_In_Geometric_Shapes[] = {
+ 1,
+ 0x25a0, 0x25ff,
+}; /* CR_In_Geometric_Shapes */
+
+/* 'In_Miscellaneous_Symbols': Block */
+static const OnigCodePoint CR_In_Miscellaneous_Symbols[] = {
+ 1,
+ 0x2600, 0x26ff,
+}; /* CR_In_Miscellaneous_Symbols */
+
+/* 'In_Dingbats': Block */
+static const OnigCodePoint CR_In_Dingbats[] = {
+ 1,
+ 0x2700, 0x27bf,
+}; /* CR_In_Dingbats */
+
+/* 'In_Miscellaneous_Mathematical_Symbols_A': Block */
+static const OnigCodePoint CR_In_Miscellaneous_Mathematical_Symbols_A[] = {
+ 1,
+ 0x27c0, 0x27ef,
+}; /* CR_In_Miscellaneous_Mathematical_Symbols_A */
+
+/* 'In_Supplemental_Arrows_A': Block */
+static const OnigCodePoint CR_In_Supplemental_Arrows_A[] = {
+ 1,
+ 0x27f0, 0x27ff,
+}; /* CR_In_Supplemental_Arrows_A */
+
+/* 'In_Braille_Patterns': Block */
+#define CR_In_Braille_Patterns CR_Braille
+
+/* 'In_Supplemental_Arrows_B': Block */
+static const OnigCodePoint CR_In_Supplemental_Arrows_B[] = {
+ 1,
+ 0x2900, 0x297f,
+}; /* CR_In_Supplemental_Arrows_B */
+
+/* 'In_Miscellaneous_Mathematical_Symbols_B': Block */
+static const OnigCodePoint CR_In_Miscellaneous_Mathematical_Symbols_B[] = {
+ 1,
+ 0x2980, 0x29ff,
+}; /* CR_In_Miscellaneous_Mathematical_Symbols_B */
+
+/* 'In_Supplemental_Mathematical_Operators': Block */
+static const OnigCodePoint CR_In_Supplemental_Mathematical_Operators[] = {
+ 1,
+ 0x2a00, 0x2aff,
+}; /* CR_In_Supplemental_Mathematical_Operators */
+
+/* 'In_Miscellaneous_Symbols_and_Arrows': Block */
+static const OnigCodePoint CR_In_Miscellaneous_Symbols_and_Arrows[] = {
+ 1,
+ 0x2b00, 0x2bff,
+}; /* CR_In_Miscellaneous_Symbols_and_Arrows */
+
+/* 'In_Glagolitic': Block */
+static const OnigCodePoint CR_In_Glagolitic[] = {
+ 1,
+ 0x2c00, 0x2c5f,
+}; /* CR_In_Glagolitic */
+
+/* 'In_Latin_Extended_C': Block */
+static const OnigCodePoint CR_In_Latin_Extended_C[] = {
+ 1,
+ 0x2c60, 0x2c7f,
+}; /* CR_In_Latin_Extended_C */
+
+/* 'In_Coptic': Block */
+static const OnigCodePoint CR_In_Coptic[] = {
+ 1,
+ 0x2c80, 0x2cff,
+}; /* CR_In_Coptic */
+
+/* 'In_Georgian_Supplement': Block */
+static const OnigCodePoint CR_In_Georgian_Supplement[] = {
+ 1,
+ 0x2d00, 0x2d2f,
+}; /* CR_In_Georgian_Supplement */
+
+/* 'In_Tifinagh': Block */
+static const OnigCodePoint CR_In_Tifinagh[] = {
+ 1,
+ 0x2d30, 0x2d7f,
+}; /* CR_In_Tifinagh */
+
+/* 'In_Ethiopic_Extended': Block */
+static const OnigCodePoint CR_In_Ethiopic_Extended[] = {
+ 1,
+ 0x2d80, 0x2ddf,
+}; /* CR_In_Ethiopic_Extended */
+
+/* 'In_Cyrillic_Extended_A': Block */
+static const OnigCodePoint CR_In_Cyrillic_Extended_A[] = {
+ 1,
+ 0x2de0, 0x2dff,
+}; /* CR_In_Cyrillic_Extended_A */
+
+/* 'In_Supplemental_Punctuation': Block */
+static const OnigCodePoint CR_In_Supplemental_Punctuation[] = {
+ 1,
+ 0x2e00, 0x2e7f,
+}; /* CR_In_Supplemental_Punctuation */
+
+/* 'In_CJK_Radicals_Supplement': Block */
+static const OnigCodePoint CR_In_CJK_Radicals_Supplement[] = {
+ 1,
+ 0x2e80, 0x2eff,
+}; /* CR_In_CJK_Radicals_Supplement */
+
+/* 'In_Kangxi_Radicals': Block */
+static const OnigCodePoint CR_In_Kangxi_Radicals[] = {
+ 1,
+ 0x2f00, 0x2fdf,
+}; /* CR_In_Kangxi_Radicals */
+
+/* 'In_Ideographic_Description_Characters': Block */
+static const OnigCodePoint CR_In_Ideographic_Description_Characters[] = {
+ 1,
+ 0x2ff0, 0x2fff,
+}; /* CR_In_Ideographic_Description_Characters */
+
+/* 'In_CJK_Symbols_and_Punctuation': Block */
+static const OnigCodePoint CR_In_CJK_Symbols_and_Punctuation[] = {
+ 1,
+ 0x3000, 0x303f,
+}; /* CR_In_CJK_Symbols_and_Punctuation */
+
+/* 'In_Hiragana': Block */
+static const OnigCodePoint CR_In_Hiragana[] = {
+ 1,
+ 0x3040, 0x309f,
+}; /* CR_In_Hiragana */
+
+/* 'In_Katakana': Block */
+static const OnigCodePoint CR_In_Katakana[] = {
+ 1,
+ 0x30a0, 0x30ff,
+}; /* CR_In_Katakana */
+
+/* 'In_Bopomofo': Block */
+static const OnigCodePoint CR_In_Bopomofo[] = {
+ 1,
+ 0x3100, 0x312f,
+}; /* CR_In_Bopomofo */
+
+/* 'In_Hangul_Compatibility_Jamo': Block */
+static const OnigCodePoint CR_In_Hangul_Compatibility_Jamo[] = {
+ 1,
+ 0x3130, 0x318f,
+}; /* CR_In_Hangul_Compatibility_Jamo */
+
+/* 'In_Kanbun': Block */
+static const OnigCodePoint CR_In_Kanbun[] = {
+ 1,
+ 0x3190, 0x319f,
+}; /* CR_In_Kanbun */
+
+/* 'In_Bopomofo_Extended': Block */
+static const OnigCodePoint CR_In_Bopomofo_Extended[] = {
+ 1,
+ 0x31a0, 0x31bf,
+}; /* CR_In_Bopomofo_Extended */
+
+/* 'In_CJK_Strokes': Block */
+static const OnigCodePoint CR_In_CJK_Strokes[] = {
+ 1,
+ 0x31c0, 0x31ef,
+}; /* CR_In_CJK_Strokes */
+
+/* 'In_Katakana_Phonetic_Extensions': Block */
+static const OnigCodePoint CR_In_Katakana_Phonetic_Extensions[] = {
+ 1,
+ 0x31f0, 0x31ff,
+}; /* CR_In_Katakana_Phonetic_Extensions */
+
+/* 'In_Enclosed_CJK_Letters_and_Months': Block */
+static const OnigCodePoint CR_In_Enclosed_CJK_Letters_and_Months[] = {
+ 1,
+ 0x3200, 0x32ff,
+}; /* CR_In_Enclosed_CJK_Letters_and_Months */
+
+/* 'In_CJK_Compatibility': Block */
+static const OnigCodePoint CR_In_CJK_Compatibility[] = {
+ 1,
+ 0x3300, 0x33ff,
+}; /* CR_In_CJK_Compatibility */
+
+/* 'In_CJK_Unified_Ideographs_Extension_A': Block */
+static const OnigCodePoint CR_In_CJK_Unified_Ideographs_Extension_A[] = {
+ 1,
+ 0x3400, 0x4dbf,
+}; /* CR_In_CJK_Unified_Ideographs_Extension_A */
+
+/* 'In_Yijing_Hexagram_Symbols': Block */
+static const OnigCodePoint CR_In_Yijing_Hexagram_Symbols[] = {
+ 1,
+ 0x4dc0, 0x4dff,
+}; /* CR_In_Yijing_Hexagram_Symbols */
+
+/* 'In_CJK_Unified_Ideographs': Block */
+static const OnigCodePoint CR_In_CJK_Unified_Ideographs[] = {
+ 1,
+ 0x4e00, 0x9fff,
+}; /* CR_In_CJK_Unified_Ideographs */
+
+/* 'In_Yi_Syllables': Block */
+static const OnigCodePoint CR_In_Yi_Syllables[] = {
+ 1,
+ 0xa000, 0xa48f,
+}; /* CR_In_Yi_Syllables */
+
+/* 'In_Yi_Radicals': Block */
+static const OnigCodePoint CR_In_Yi_Radicals[] = {
+ 1,
+ 0xa490, 0xa4cf,
+}; /* CR_In_Yi_Radicals */
+
+/* 'In_Lisu': Block */
+#define CR_In_Lisu CR_Lisu
+
+/* 'In_Vai': Block */
+static const OnigCodePoint CR_In_Vai[] = {
+ 1,
+ 0xa500, 0xa63f,
+}; /* CR_In_Vai */
+
+/* 'In_Cyrillic_Extended_B': Block */
+static const OnigCodePoint CR_In_Cyrillic_Extended_B[] = {
+ 1,
+ 0xa640, 0xa69f,
+}; /* CR_In_Cyrillic_Extended_B */
+
+/* 'In_Bamum': Block */
+static const OnigCodePoint CR_In_Bamum[] = {
+ 1,
+ 0xa6a0, 0xa6ff,
+}; /* CR_In_Bamum */
+
+/* 'In_Modifier_Tone_Letters': Block */
+static const OnigCodePoint CR_In_Modifier_Tone_Letters[] = {
+ 1,
+ 0xa700, 0xa71f,
+}; /* CR_In_Modifier_Tone_Letters */
+
+/* 'In_Latin_Extended_D': Block */
+static const OnigCodePoint CR_In_Latin_Extended_D[] = {
+ 1,
+ 0xa720, 0xa7ff,
+}; /* CR_In_Latin_Extended_D */
+
+/* 'In_Syloti_Nagri': Block */
+static const OnigCodePoint CR_In_Syloti_Nagri[] = {
+ 1,
+ 0xa800, 0xa82f,
+}; /* CR_In_Syloti_Nagri */
+
+/* 'In_Common_Indic_Number_Forms': Block */
+static const OnigCodePoint CR_In_Common_Indic_Number_Forms[] = {
+ 1,
+ 0xa830, 0xa83f,
+}; /* CR_In_Common_Indic_Number_Forms */
+
+/* 'In_Phags_pa': Block */
+static const OnigCodePoint CR_In_Phags_pa[] = {
+ 1,
+ 0xa840, 0xa87f,
+}; /* CR_In_Phags_pa */
+
+/* 'In_Saurashtra': Block */
+static const OnigCodePoint CR_In_Saurashtra[] = {
+ 1,
+ 0xa880, 0xa8df,
+}; /* CR_In_Saurashtra */
+
+/* 'In_Devanagari_Extended': Block */
+static const OnigCodePoint CR_In_Devanagari_Extended[] = {
+ 1,
+ 0xa8e0, 0xa8ff,
+}; /* CR_In_Devanagari_Extended */
+
+/* 'In_Kayah_Li': Block */
+static const OnigCodePoint CR_In_Kayah_Li[] = {
+ 1,
+ 0xa900, 0xa92f,
+}; /* CR_In_Kayah_Li */
+
+/* 'In_Rejang': Block */
+static const OnigCodePoint CR_In_Rejang[] = {
+ 1,
+ 0xa930, 0xa95f,
+}; /* CR_In_Rejang */
+
+/* 'In_Hangul_Jamo_Extended_A': Block */
+static const OnigCodePoint CR_In_Hangul_Jamo_Extended_A[] = {
+ 1,
+ 0xa960, 0xa97f,
+}; /* CR_In_Hangul_Jamo_Extended_A */
+
+/* 'In_Javanese': Block */
+static const OnigCodePoint CR_In_Javanese[] = {
+ 1,
+ 0xa980, 0xa9df,
+}; /* CR_In_Javanese */
+
+/* 'In_Myanmar_Extended_B': Block */
+static const OnigCodePoint CR_In_Myanmar_Extended_B[] = {
+ 1,
+ 0xa9e0, 0xa9ff,
+}; /* CR_In_Myanmar_Extended_B */
+
+/* 'In_Cham': Block */
+static const OnigCodePoint CR_In_Cham[] = {
+ 1,
+ 0xaa00, 0xaa5f,
+}; /* CR_In_Cham */
+
+/* 'In_Myanmar_Extended_A': Block */
+static const OnigCodePoint CR_In_Myanmar_Extended_A[] = {
+ 1,
+ 0xaa60, 0xaa7f,
+}; /* CR_In_Myanmar_Extended_A */
+
+/* 'In_Tai_Viet': Block */
+static const OnigCodePoint CR_In_Tai_Viet[] = {
+ 1,
+ 0xaa80, 0xaadf,
+}; /* CR_In_Tai_Viet */
+
+/* 'In_Meetei_Mayek_Extensions': Block */
+static const OnigCodePoint CR_In_Meetei_Mayek_Extensions[] = {
+ 1,
+ 0xaae0, 0xaaff,
+}; /* CR_In_Meetei_Mayek_Extensions */
+
+/* 'In_Ethiopic_Extended_A': Block */
+static const OnigCodePoint CR_In_Ethiopic_Extended_A[] = {
+ 1,
+ 0xab00, 0xab2f,
+}; /* CR_In_Ethiopic_Extended_A */
+
+/* 'In_Latin_Extended_E': Block */
+static const OnigCodePoint CR_In_Latin_Extended_E[] = {
+ 1,
+ 0xab30, 0xab6f,
+}; /* CR_In_Latin_Extended_E */
+
+/* 'In_Cherokee_Supplement': Block */
+static const OnigCodePoint CR_In_Cherokee_Supplement[] = {
+ 1,
+ 0xab70, 0xabbf,
+}; /* CR_In_Cherokee_Supplement */
+
+/* 'In_Meetei_Mayek': Block */
+static const OnigCodePoint CR_In_Meetei_Mayek[] = {
+ 1,
+ 0xabc0, 0xabff,
+}; /* CR_In_Meetei_Mayek */
+
+/* 'In_Hangul_Syllables': Block */
+static const OnigCodePoint CR_In_Hangul_Syllables[] = {
+ 1,
+ 0xac00, 0xd7af,
+}; /* CR_In_Hangul_Syllables */
+
+/* 'In_Hangul_Jamo_Extended_B': Block */
+static const OnigCodePoint CR_In_Hangul_Jamo_Extended_B[] = {
+ 1,
+ 0xd7b0, 0xd7ff,
+}; /* CR_In_Hangul_Jamo_Extended_B */
+
+/* 'In_High_Surrogates': Block */
+static const OnigCodePoint CR_In_High_Surrogates[] = {
+ 1,
+ 0xd800, 0xdb7f,
+}; /* CR_In_High_Surrogates */
+
+/* 'In_High_Private_Use_Surrogates': Block */
+static const OnigCodePoint CR_In_High_Private_Use_Surrogates[] = {
+ 1,
+ 0xdb80, 0xdbff,
+}; /* CR_In_High_Private_Use_Surrogates */
+
+/* 'In_Low_Surrogates': Block */
+static const OnigCodePoint CR_In_Low_Surrogates[] = {
+ 1,
+ 0xdc00, 0xdfff,
+}; /* CR_In_Low_Surrogates */
+
+/* 'In_Private_Use_Area': Block */
+static const OnigCodePoint CR_In_Private_Use_Area[] = {
+ 1,
+ 0xe000, 0xf8ff,
+}; /* CR_In_Private_Use_Area */
+
+/* 'In_CJK_Compatibility_Ideographs': Block */
+static const OnigCodePoint CR_In_CJK_Compatibility_Ideographs[] = {
+ 1,
+ 0xf900, 0xfaff,
+}; /* CR_In_CJK_Compatibility_Ideographs */
+
+/* 'In_Alphabetic_Presentation_Forms': Block */
+static const OnigCodePoint CR_In_Alphabetic_Presentation_Forms[] = {
+ 1,
+ 0xfb00, 0xfb4f,
+}; /* CR_In_Alphabetic_Presentation_Forms */
+
+/* 'In_Arabic_Presentation_Forms_A': Block */
+static const OnigCodePoint CR_In_Arabic_Presentation_Forms_A[] = {
+ 1,
+ 0xfb50, 0xfdff,
+}; /* CR_In_Arabic_Presentation_Forms_A */
+
+/* 'In_Variation_Selectors': Block */
+static const OnigCodePoint CR_In_Variation_Selectors[] = {
+ 1,
+ 0xfe00, 0xfe0f,
+}; /* CR_In_Variation_Selectors */
+
+/* 'In_Vertical_Forms': Block */
+static const OnigCodePoint CR_In_Vertical_Forms[] = {
+ 1,
+ 0xfe10, 0xfe1f,
+}; /* CR_In_Vertical_Forms */
+
+/* 'In_Combining_Half_Marks': Block */
+static const OnigCodePoint CR_In_Combining_Half_Marks[] = {
+ 1,
+ 0xfe20, 0xfe2f,
+}; /* CR_In_Combining_Half_Marks */
+
+/* 'In_CJK_Compatibility_Forms': Block */
+static const OnigCodePoint CR_In_CJK_Compatibility_Forms[] = {
+ 1,
+ 0xfe30, 0xfe4f,
+}; /* CR_In_CJK_Compatibility_Forms */
+
+/* 'In_Small_Form_Variants': Block */
+static const OnigCodePoint CR_In_Small_Form_Variants[] = {
+ 1,
+ 0xfe50, 0xfe6f,
+}; /* CR_In_Small_Form_Variants */
+
+/* 'In_Arabic_Presentation_Forms_B': Block */
+static const OnigCodePoint CR_In_Arabic_Presentation_Forms_B[] = {
+ 1,
+ 0xfe70, 0xfeff,
+}; /* CR_In_Arabic_Presentation_Forms_B */
+
+/* 'In_Halfwidth_and_Fullwidth_Forms': Block */
+static const OnigCodePoint CR_In_Halfwidth_and_Fullwidth_Forms[] = {
+ 1,
+ 0xff00, 0xffef,
+}; /* CR_In_Halfwidth_and_Fullwidth_Forms */
+
+/* 'In_Specials': Block */
+static const OnigCodePoint CR_In_Specials[] = {
+ 1,
+ 0xfff0, 0xffff,
+}; /* CR_In_Specials */
+
+/* 'In_Linear_B_Syllabary': Block */
+static const OnigCodePoint CR_In_Linear_B_Syllabary[] = {
+ 1,
+ 0x10000, 0x1007f,
+}; /* CR_In_Linear_B_Syllabary */
+
+/* 'In_Linear_B_Ideograms': Block */
+static const OnigCodePoint CR_In_Linear_B_Ideograms[] = {
+ 1,
+ 0x10080, 0x100ff,
+}; /* CR_In_Linear_B_Ideograms */
+
+/* 'In_Aegean_Numbers': Block */
+static const OnigCodePoint CR_In_Aegean_Numbers[] = {
+ 1,
+ 0x10100, 0x1013f,
+}; /* CR_In_Aegean_Numbers */
+
+/* 'In_Ancient_Greek_Numbers': Block */
+static const OnigCodePoint CR_In_Ancient_Greek_Numbers[] = {
+ 1,
+ 0x10140, 0x1018f,
+}; /* CR_In_Ancient_Greek_Numbers */
+
+/* 'In_Ancient_Symbols': Block */
+static const OnigCodePoint CR_In_Ancient_Symbols[] = {
+ 1,
+ 0x10190, 0x101cf,
+}; /* CR_In_Ancient_Symbols */
+
+/* 'In_Phaistos_Disc': Block */
+static const OnigCodePoint CR_In_Phaistos_Disc[] = {
+ 1,
+ 0x101d0, 0x101ff,
+}; /* CR_In_Phaistos_Disc */
+
+/* 'In_Lycian': Block */
+static const OnigCodePoint CR_In_Lycian[] = {
+ 1,
+ 0x10280, 0x1029f,
+}; /* CR_In_Lycian */
+
+/* 'In_Carian': Block */
+static const OnigCodePoint CR_In_Carian[] = {
+ 1,
+ 0x102a0, 0x102df,
+}; /* CR_In_Carian */
+
+/* 'In_Coptic_Epact_Numbers': Block */
+static const OnigCodePoint CR_In_Coptic_Epact_Numbers[] = {
+ 1,
+ 0x102e0, 0x102ff,
+}; /* CR_In_Coptic_Epact_Numbers */
+
+/* 'In_Old_Italic': Block */
+static const OnigCodePoint CR_In_Old_Italic[] = {
+ 1,
+ 0x10300, 0x1032f,
+}; /* CR_In_Old_Italic */
+
+/* 'In_Gothic': Block */
+static const OnigCodePoint CR_In_Gothic[] = {
+ 1,
+ 0x10330, 0x1034f,
+}; /* CR_In_Gothic */
+
+/* 'In_Old_Permic': Block */
+static const OnigCodePoint CR_In_Old_Permic[] = {
+ 1,
+ 0x10350, 0x1037f,
+}; /* CR_In_Old_Permic */
+
+/* 'In_Ugaritic': Block */
+static const OnigCodePoint CR_In_Ugaritic[] = {
+ 1,
+ 0x10380, 0x1039f,
+}; /* CR_In_Ugaritic */
+
+/* 'In_Old_Persian': Block */
+static const OnigCodePoint CR_In_Old_Persian[] = {
+ 1,
+ 0x103a0, 0x103df,
+}; /* CR_In_Old_Persian */
+
+/* 'In_Deseret': Block */
+#define CR_In_Deseret CR_Deseret
+
+/* 'In_Shavian': Block */
+#define CR_In_Shavian CR_Shavian
+
+/* 'In_Osmanya': Block */
+static const OnigCodePoint CR_In_Osmanya[] = {
+ 1,
+ 0x10480, 0x104af,
+}; /* CR_In_Osmanya */
+
+/* 'In_Osage': Block */
+static const OnigCodePoint CR_In_Osage[] = {
+ 1,
+ 0x104b0, 0x104ff,
+}; /* CR_In_Osage */
+
+/* 'In_Elbasan': Block */
+static const OnigCodePoint CR_In_Elbasan[] = {
+ 1,
+ 0x10500, 0x1052f,
+}; /* CR_In_Elbasan */
+
+/* 'In_Caucasian_Albanian': Block */
+static const OnigCodePoint CR_In_Caucasian_Albanian[] = {
+ 1,
+ 0x10530, 0x1056f,
+}; /* CR_In_Caucasian_Albanian */
+
+/* 'In_Linear_A': Block */
+static const OnigCodePoint CR_In_Linear_A[] = {
+ 1,
+ 0x10600, 0x1077f,
+}; /* CR_In_Linear_A */
+
+/* 'In_Cypriot_Syllabary': Block */
+static const OnigCodePoint CR_In_Cypriot_Syllabary[] = {
+ 1,
+ 0x10800, 0x1083f,
+}; /* CR_In_Cypriot_Syllabary */
+
+/* 'In_Imperial_Aramaic': Block */
+static const OnigCodePoint CR_In_Imperial_Aramaic[] = {
+ 1,
+ 0x10840, 0x1085f,
+}; /* CR_In_Imperial_Aramaic */
+
+/* 'In_Palmyrene': Block */
+#define CR_In_Palmyrene CR_Palmyrene
+
+/* 'In_Nabataean': Block */
+static const OnigCodePoint CR_In_Nabataean[] = {
+ 1,
+ 0x10880, 0x108af,
+}; /* CR_In_Nabataean */
+
+/* 'In_Hatran': Block */
+static const OnigCodePoint CR_In_Hatran[] = {
+ 1,
+ 0x108e0, 0x108ff,
+}; /* CR_In_Hatran */
+
+/* 'In_Phoenician': Block */
+static const OnigCodePoint CR_In_Phoenician[] = {
+ 1,
+ 0x10900, 0x1091f,
+}; /* CR_In_Phoenician */
+
+/* 'In_Lydian': Block */
+static const OnigCodePoint CR_In_Lydian[] = {
+ 1,
+ 0x10920, 0x1093f,
+}; /* CR_In_Lydian */
+
+/* 'In_Meroitic_Hieroglyphs': Block */
+#define CR_In_Meroitic_Hieroglyphs CR_Meroitic_Hieroglyphs
+
+/* 'In_Meroitic_Cursive': Block */
+static const OnigCodePoint CR_In_Meroitic_Cursive[] = {
+ 1,
+ 0x109a0, 0x109ff,
+}; /* CR_In_Meroitic_Cursive */
+
+/* 'In_Kharoshthi': Block */
+static const OnigCodePoint CR_In_Kharoshthi[] = {
+ 1,
+ 0x10a00, 0x10a5f,
+}; /* CR_In_Kharoshthi */
+
+/* 'In_Old_South_Arabian': Block */
+#define CR_In_Old_South_Arabian CR_Old_South_Arabian
+
+/* 'In_Old_North_Arabian': Block */
+#define CR_In_Old_North_Arabian CR_Old_North_Arabian
+
+/* 'In_Manichaean': Block */
+static const OnigCodePoint CR_In_Manichaean[] = {
+ 1,
+ 0x10ac0, 0x10aff,
+}; /* CR_In_Manichaean */
+
+/* 'In_Avestan': Block */
+static const OnigCodePoint CR_In_Avestan[] = {
+ 1,
+ 0x10b00, 0x10b3f,
+}; /* CR_In_Avestan */
+
+/* 'In_Inscriptional_Parthian': Block */
+static const OnigCodePoint CR_In_Inscriptional_Parthian[] = {
+ 1,
+ 0x10b40, 0x10b5f,
+}; /* CR_In_Inscriptional_Parthian */
+
+/* 'In_Inscriptional_Pahlavi': Block */
+static const OnigCodePoint CR_In_Inscriptional_Pahlavi[] = {
+ 1,
+ 0x10b60, 0x10b7f,
+}; /* CR_In_Inscriptional_Pahlavi */
+
+/* 'In_Psalter_Pahlavi': Block */
+static const OnigCodePoint CR_In_Psalter_Pahlavi[] = {
+ 1,
+ 0x10b80, 0x10baf,
+}; /* CR_In_Psalter_Pahlavi */
+
+/* 'In_Old_Turkic': Block */
+static const OnigCodePoint CR_In_Old_Turkic[] = {
+ 1,
+ 0x10c00, 0x10c4f,
+}; /* CR_In_Old_Turkic */
+
+/* 'In_Old_Hungarian': Block */
+static const OnigCodePoint CR_In_Old_Hungarian[] = {
+ 1,
+ 0x10c80, 0x10cff,
+}; /* CR_In_Old_Hungarian */
+
+/* 'In_Hanifi_Rohingya': Block */
+static const OnigCodePoint CR_In_Hanifi_Rohingya[] = {
+ 1,
+ 0x10d00, 0x10d3f,
+}; /* CR_In_Hanifi_Rohingya */
+
+/* 'In_Rumi_Numeral_Symbols': Block */
+static const OnigCodePoint CR_In_Rumi_Numeral_Symbols[] = {
+ 1,
+ 0x10e60, 0x10e7f,
+}; /* CR_In_Rumi_Numeral_Symbols */
+
+/* 'In_Old_Sogdian': Block */
+static const OnigCodePoint CR_In_Old_Sogdian[] = {
+ 1,
+ 0x10f00, 0x10f2f,
+}; /* CR_In_Old_Sogdian */
+
+/* 'In_Sogdian': Block */
+static const OnigCodePoint CR_In_Sogdian[] = {
+ 1,
+ 0x10f30, 0x10f6f,
+}; /* CR_In_Sogdian */
+
+/* 'In_Elymaic': Block */
+static const OnigCodePoint CR_In_Elymaic[] = {
+ 1,
+ 0x10fe0, 0x10fff,
+}; /* CR_In_Elymaic */
+
+/* 'In_Brahmi': Block */
+static const OnigCodePoint CR_In_Brahmi[] = {
+ 1,
+ 0x11000, 0x1107f,
+}; /* CR_In_Brahmi */
+
+/* 'In_Kaithi': Block */
+static const OnigCodePoint CR_In_Kaithi[] = {
+ 1,
+ 0x11080, 0x110cf,
+}; /* CR_In_Kaithi */
+
+/* 'In_Sora_Sompeng': Block */
+static const OnigCodePoint CR_In_Sora_Sompeng[] = {
+ 1,
+ 0x110d0, 0x110ff,
+}; /* CR_In_Sora_Sompeng */
+
+/* 'In_Chakma': Block */
+static const OnigCodePoint CR_In_Chakma[] = {
+ 1,
+ 0x11100, 0x1114f,
+}; /* CR_In_Chakma */
+
+/* 'In_Mahajani': Block */
+static const OnigCodePoint CR_In_Mahajani[] = {
+ 1,
+ 0x11150, 0x1117f,
+}; /* CR_In_Mahajani */
+
+/* 'In_Sharada': Block */
+static const OnigCodePoint CR_In_Sharada[] = {
+ 1,
+ 0x11180, 0x111df,
+}; /* CR_In_Sharada */
+
+/* 'In_Sinhala_Archaic_Numbers': Block */
+static const OnigCodePoint CR_In_Sinhala_Archaic_Numbers[] = {
+ 1,
+ 0x111e0, 0x111ff,
+}; /* CR_In_Sinhala_Archaic_Numbers */
+
+/* 'In_Khojki': Block */
+static const OnigCodePoint CR_In_Khojki[] = {
+ 1,
+ 0x11200, 0x1124f,
+}; /* CR_In_Khojki */
+
+/* 'In_Multani': Block */
+static const OnigCodePoint CR_In_Multani[] = {
+ 1,
+ 0x11280, 0x112af,
+}; /* CR_In_Multani */
+
+/* 'In_Khudawadi': Block */
+static const OnigCodePoint CR_In_Khudawadi[] = {
+ 1,
+ 0x112b0, 0x112ff,
+}; /* CR_In_Khudawadi */
+
+/* 'In_Grantha': Block */
+static const OnigCodePoint CR_In_Grantha[] = {
+ 1,
+ 0x11300, 0x1137f,
+}; /* CR_In_Grantha */
+
+/* 'In_Newa': Block */
+static const OnigCodePoint CR_In_Newa[] = {
+ 1,
+ 0x11400, 0x1147f,
+}; /* CR_In_Newa */
+
+/* 'In_Tirhuta': Block */
+static const OnigCodePoint CR_In_Tirhuta[] = {
+ 1,
+ 0x11480, 0x114df,
+}; /* CR_In_Tirhuta */
+
+/* 'In_Siddham': Block */
+static const OnigCodePoint CR_In_Siddham[] = {
+ 1,
+ 0x11580, 0x115ff,
+}; /* CR_In_Siddham */
+
+/* 'In_Modi': Block */
+static const OnigCodePoint CR_In_Modi[] = {
+ 1,
+ 0x11600, 0x1165f,
+}; /* CR_In_Modi */
+
+/* 'In_Mongolian_Supplement': Block */
+static const OnigCodePoint CR_In_Mongolian_Supplement[] = {
+ 1,
+ 0x11660, 0x1167f,
+}; /* CR_In_Mongolian_Supplement */
+
+/* 'In_Takri': Block */
+static const OnigCodePoint CR_In_Takri[] = {
+ 1,
+ 0x11680, 0x116cf,
+}; /* CR_In_Takri */
+
+/* 'In_Ahom': Block */
+static const OnigCodePoint CR_In_Ahom[] = {
+ 1,
+ 0x11700, 0x1173f,
+}; /* CR_In_Ahom */
+
+/* 'In_Dogra': Block */
+static const OnigCodePoint CR_In_Dogra[] = {
+ 1,
+ 0x11800, 0x1184f,
+}; /* CR_In_Dogra */
+
+/* 'In_Warang_Citi': Block */
+static const OnigCodePoint CR_In_Warang_Citi[] = {
+ 1,
+ 0x118a0, 0x118ff,
+}; /* CR_In_Warang_Citi */
+
+/* 'In_Nandinagari': Block */
+static const OnigCodePoint CR_In_Nandinagari[] = {
+ 1,
+ 0x119a0, 0x119ff,
+}; /* CR_In_Nandinagari */
+
+/* 'In_Zanabazar_Square': Block */
+static const OnigCodePoint CR_In_Zanabazar_Square[] = {
+ 1,
+ 0x11a00, 0x11a4f,
+}; /* CR_In_Zanabazar_Square */
+
+/* 'In_Soyombo': Block */
+static const OnigCodePoint CR_In_Soyombo[] = {
+ 1,
+ 0x11a50, 0x11aaf,
+}; /* CR_In_Soyombo */
+
+/* 'In_Pau_Cin_Hau': Block */
+static const OnigCodePoint CR_In_Pau_Cin_Hau[] = {
+ 1,
+ 0x11ac0, 0x11aff,
+}; /* CR_In_Pau_Cin_Hau */
+
+/* 'In_Bhaiksuki': Block */
+static const OnigCodePoint CR_In_Bhaiksuki[] = {
+ 1,
+ 0x11c00, 0x11c6f,
+}; /* CR_In_Bhaiksuki */
+
+/* 'In_Marchen': Block */
+static const OnigCodePoint CR_In_Marchen[] = {
+ 1,
+ 0x11c70, 0x11cbf,
+}; /* CR_In_Marchen */
+
+/* 'In_Masaram_Gondi': Block */
+static const OnigCodePoint CR_In_Masaram_Gondi[] = {
+ 1,
+ 0x11d00, 0x11d5f,
+}; /* CR_In_Masaram_Gondi */
+
+/* 'In_Gunjala_Gondi': Block */
+static const OnigCodePoint CR_In_Gunjala_Gondi[] = {
+ 1,
+ 0x11d60, 0x11daf,
+}; /* CR_In_Gunjala_Gondi */
+
+/* 'In_Makasar': Block */
+static const OnigCodePoint CR_In_Makasar[] = {
+ 1,
+ 0x11ee0, 0x11eff,
+}; /* CR_In_Makasar */
+
+/* 'In_Tamil_Supplement': Block */
+static const OnigCodePoint CR_In_Tamil_Supplement[] = {
+ 1,
+ 0x11fc0, 0x11fff,
+}; /* CR_In_Tamil_Supplement */
+
+/* 'In_Cuneiform': Block */
+static const OnigCodePoint CR_In_Cuneiform[] = {
+ 1,
+ 0x12000, 0x123ff,
+}; /* CR_In_Cuneiform */
+
+/* 'In_Cuneiform_Numbers_and_Punctuation': Block */
+static const OnigCodePoint CR_In_Cuneiform_Numbers_and_Punctuation[] = {
+ 1,
+ 0x12400, 0x1247f,
+}; /* CR_In_Cuneiform_Numbers_and_Punctuation */
+
+/* 'In_Early_Dynastic_Cuneiform': Block */
+static const OnigCodePoint CR_In_Early_Dynastic_Cuneiform[] = {
+ 1,
+ 0x12480, 0x1254f,
+}; /* CR_In_Early_Dynastic_Cuneiform */
+
+/* 'In_Egyptian_Hieroglyphs': Block */
+static const OnigCodePoint CR_In_Egyptian_Hieroglyphs[] = {
+ 1,
+ 0x13000, 0x1342f,
+}; /* CR_In_Egyptian_Hieroglyphs */
+
+/* 'In_Egyptian_Hieroglyph_Format_Controls': Block */
+static const OnigCodePoint CR_In_Egyptian_Hieroglyph_Format_Controls[] = {
+ 1,
+ 0x13430, 0x1343f,
+}; /* CR_In_Egyptian_Hieroglyph_Format_Controls */
+
+/* 'In_Anatolian_Hieroglyphs': Block */
+static const OnigCodePoint CR_In_Anatolian_Hieroglyphs[] = {
+ 1,
+ 0x14400, 0x1467f,
+}; /* CR_In_Anatolian_Hieroglyphs */
+
+/* 'In_Bamum_Supplement': Block */
+static const OnigCodePoint CR_In_Bamum_Supplement[] = {
+ 1,
+ 0x16800, 0x16a3f,
+}; /* CR_In_Bamum_Supplement */
+
+/* 'In_Mro': Block */
+static const OnigCodePoint CR_In_Mro[] = {
+ 1,
+ 0x16a40, 0x16a6f,
+}; /* CR_In_Mro */
+
+/* 'In_Bassa_Vah': Block */
+static const OnigCodePoint CR_In_Bassa_Vah[] = {
+ 1,
+ 0x16ad0, 0x16aff,
+}; /* CR_In_Bassa_Vah */
+
+/* 'In_Pahawh_Hmong': Block */
+static const OnigCodePoint CR_In_Pahawh_Hmong[] = {
+ 1,
+ 0x16b00, 0x16b8f,
+}; /* CR_In_Pahawh_Hmong */
+
+/* 'In_Medefaidrin': Block */
+static const OnigCodePoint CR_In_Medefaidrin[] = {
+ 1,
+ 0x16e40, 0x16e9f,
+}; /* CR_In_Medefaidrin */
+
+/* 'In_Miao': Block */
+static const OnigCodePoint CR_In_Miao[] = {
+ 1,
+ 0x16f00, 0x16f9f,
+}; /* CR_In_Miao */
+
+/* 'In_Ideographic_Symbols_and_Punctuation': Block */
+static const OnigCodePoint CR_In_Ideographic_Symbols_and_Punctuation[] = {
+ 1,
+ 0x16fe0, 0x16fff,
+}; /* CR_In_Ideographic_Symbols_and_Punctuation */
+
+/* 'In_Tangut': Block */
+static const OnigCodePoint CR_In_Tangut[] = {
+ 1,
+ 0x17000, 0x187ff,
+}; /* CR_In_Tangut */
+
+/* 'In_Tangut_Components': Block */
+static const OnigCodePoint CR_In_Tangut_Components[] = {
+ 1,
+ 0x18800, 0x18aff,
+}; /* CR_In_Tangut_Components */
+
+/* 'In_Kana_Supplement': Block */
+static const OnigCodePoint CR_In_Kana_Supplement[] = {
+ 1,
+ 0x1b000, 0x1b0ff,
+}; /* CR_In_Kana_Supplement */
+
+/* 'In_Kana_Extended_A': Block */
+static const OnigCodePoint CR_In_Kana_Extended_A[] = {
+ 1,
+ 0x1b100, 0x1b12f,
+}; /* CR_In_Kana_Extended_A */
+
+/* 'In_Small_Kana_Extension': Block */
+static const OnigCodePoint CR_In_Small_Kana_Extension[] = {
+ 1,
+ 0x1b130, 0x1b16f,
+}; /* CR_In_Small_Kana_Extension */
+
+/* 'In_Nushu': Block */
+static const OnigCodePoint CR_In_Nushu[] = {
+ 1,
+ 0x1b170, 0x1b2ff,
+}; /* CR_In_Nushu */
+
+/* 'In_Duployan': Block */
+static const OnigCodePoint CR_In_Duployan[] = {
+ 1,
+ 0x1bc00, 0x1bc9f,
+}; /* CR_In_Duployan */
+
+/* 'In_Shorthand_Format_Controls': Block */
+static const OnigCodePoint CR_In_Shorthand_Format_Controls[] = {
+ 1,
+ 0x1bca0, 0x1bcaf,
+}; /* CR_In_Shorthand_Format_Controls */
+
+/* 'In_Byzantine_Musical_Symbols': Block */
+static const OnigCodePoint CR_In_Byzantine_Musical_Symbols[] = {
+ 1,
+ 0x1d000, 0x1d0ff,
+}; /* CR_In_Byzantine_Musical_Symbols */
+
+/* 'In_Musical_Symbols': Block */
+static const OnigCodePoint CR_In_Musical_Symbols[] = {
+ 1,
+ 0x1d100, 0x1d1ff,
+}; /* CR_In_Musical_Symbols */
+
+/* 'In_Ancient_Greek_Musical_Notation': Block */
+static const OnigCodePoint CR_In_Ancient_Greek_Musical_Notation[] = {
+ 1,
+ 0x1d200, 0x1d24f,
+}; /* CR_In_Ancient_Greek_Musical_Notation */
+
+/* 'In_Mayan_Numerals': Block */
+static const OnigCodePoint CR_In_Mayan_Numerals[] = {
+ 1,
+ 0x1d2e0, 0x1d2ff,
+}; /* CR_In_Mayan_Numerals */
+
+/* 'In_Tai_Xuan_Jing_Symbols': Block */
+static const OnigCodePoint CR_In_Tai_Xuan_Jing_Symbols[] = {
+ 1,
+ 0x1d300, 0x1d35f,
+}; /* CR_In_Tai_Xuan_Jing_Symbols */
+
+/* 'In_Counting_Rod_Numerals': Block */
+static const OnigCodePoint CR_In_Counting_Rod_Numerals[] = {
+ 1,
+ 0x1d360, 0x1d37f,
+}; /* CR_In_Counting_Rod_Numerals */
+
+/* 'In_Mathematical_Alphanumeric_Symbols': Block */
+static const OnigCodePoint CR_In_Mathematical_Alphanumeric_Symbols[] = {
+ 1,
+ 0x1d400, 0x1d7ff,
+}; /* CR_In_Mathematical_Alphanumeric_Symbols */
+
+/* 'In_Sutton_SignWriting': Block */
+static const OnigCodePoint CR_In_Sutton_SignWriting[] = {
+ 1,
+ 0x1d800, 0x1daaf,
+}; /* CR_In_Sutton_SignWriting */
+
+/* 'In_Glagolitic_Supplement': Block */
+static const OnigCodePoint CR_In_Glagolitic_Supplement[] = {
+ 1,
+ 0x1e000, 0x1e02f,
+}; /* CR_In_Glagolitic_Supplement */
+
+/* 'In_Nyiakeng_Puachue_Hmong': Block */
+static const OnigCodePoint CR_In_Nyiakeng_Puachue_Hmong[] = {
+ 1,
+ 0x1e100, 0x1e14f,
+}; /* CR_In_Nyiakeng_Puachue_Hmong */
+
+/* 'In_Wancho': Block */
+static const OnigCodePoint CR_In_Wancho[] = {
+ 1,
+ 0x1e2c0, 0x1e2ff,
+}; /* CR_In_Wancho */
+
+/* 'In_Mende_Kikakui': Block */
+static const OnigCodePoint CR_In_Mende_Kikakui[] = {
+ 1,
+ 0x1e800, 0x1e8df,
+}; /* CR_In_Mende_Kikakui */
+
+/* 'In_Adlam': Block */
+static const OnigCodePoint CR_In_Adlam[] = {
+ 1,
+ 0x1e900, 0x1e95f,
+}; /* CR_In_Adlam */
+
+/* 'In_Indic_Siyaq_Numbers': Block */
+static const OnigCodePoint CR_In_Indic_Siyaq_Numbers[] = {
+ 1,
+ 0x1ec70, 0x1ecbf,
+}; /* CR_In_Indic_Siyaq_Numbers */
+
+/* 'In_Ottoman_Siyaq_Numbers': Block */
+static const OnigCodePoint CR_In_Ottoman_Siyaq_Numbers[] = {
+ 1,
+ 0x1ed00, 0x1ed4f,
+}; /* CR_In_Ottoman_Siyaq_Numbers */
+
+/* 'In_Arabic_Mathematical_Alphabetic_Symbols': Block */
+static const OnigCodePoint CR_In_Arabic_Mathematical_Alphabetic_Symbols[] = {
+ 1,
+ 0x1ee00, 0x1eeff,
+}; /* CR_In_Arabic_Mathematical_Alphabetic_Symbols */
+
+/* 'In_Mahjong_Tiles': Block */
+static const OnigCodePoint CR_In_Mahjong_Tiles[] = {
+ 1,
+ 0x1f000, 0x1f02f,
+}; /* CR_In_Mahjong_Tiles */
+
+/* 'In_Domino_Tiles': Block */
+static const OnigCodePoint CR_In_Domino_Tiles[] = {
+ 1,
+ 0x1f030, 0x1f09f,
+}; /* CR_In_Domino_Tiles */
+
+/* 'In_Playing_Cards': Block */
+static const OnigCodePoint CR_In_Playing_Cards[] = {
+ 1,
+ 0x1f0a0, 0x1f0ff,
+}; /* CR_In_Playing_Cards */
+
+/* 'In_Enclosed_Alphanumeric_Supplement': Block */
+static const OnigCodePoint CR_In_Enclosed_Alphanumeric_Supplement[] = {
+ 1,
+ 0x1f100, 0x1f1ff,
+}; /* CR_In_Enclosed_Alphanumeric_Supplement */
+
+/* 'In_Enclosed_Ideographic_Supplement': Block */
+static const OnigCodePoint CR_In_Enclosed_Ideographic_Supplement[] = {
+ 1,
+ 0x1f200, 0x1f2ff,
+}; /* CR_In_Enclosed_Ideographic_Supplement */
+
+/* 'In_Miscellaneous_Symbols_and_Pictographs': Block */
+static const OnigCodePoint CR_In_Miscellaneous_Symbols_and_Pictographs[] = {
+ 1,
+ 0x1f300, 0x1f5ff,
+}; /* CR_In_Miscellaneous_Symbols_and_Pictographs */
+
+/* 'In_Emoticons': Block */
+static const OnigCodePoint CR_In_Emoticons[] = {
+ 1,
+ 0x1f600, 0x1f64f,
+}; /* CR_In_Emoticons */
+
+/* 'In_Ornamental_Dingbats': Block */
+static const OnigCodePoint CR_In_Ornamental_Dingbats[] = {
+ 1,
+ 0x1f650, 0x1f67f,
+}; /* CR_In_Ornamental_Dingbats */
+
+/* 'In_Transport_and_Map_Symbols': Block */
+static const OnigCodePoint CR_In_Transport_and_Map_Symbols[] = {
+ 1,
+ 0x1f680, 0x1f6ff,
+}; /* CR_In_Transport_and_Map_Symbols */
+
+/* 'In_Alchemical_Symbols': Block */
+static const OnigCodePoint CR_In_Alchemical_Symbols[] = {
+ 1,
+ 0x1f700, 0x1f77f,
+}; /* CR_In_Alchemical_Symbols */
+
+/* 'In_Geometric_Shapes_Extended': Block */
+static const OnigCodePoint CR_In_Geometric_Shapes_Extended[] = {
+ 1,
+ 0x1f780, 0x1f7ff,
+}; /* CR_In_Geometric_Shapes_Extended */
+
+/* 'In_Supplemental_Arrows_C': Block */
+static const OnigCodePoint CR_In_Supplemental_Arrows_C[] = {
+ 1,
+ 0x1f800, 0x1f8ff,
+}; /* CR_In_Supplemental_Arrows_C */
+
+/* 'In_Supplemental_Symbols_and_Pictographs': Block */
+static const OnigCodePoint CR_In_Supplemental_Symbols_and_Pictographs[] = {
+ 1,
+ 0x1f900, 0x1f9ff,
+}; /* CR_In_Supplemental_Symbols_and_Pictographs */
+
+/* 'In_Chess_Symbols': Block */
+static const OnigCodePoint CR_In_Chess_Symbols[] = {
+ 1,
+ 0x1fa00, 0x1fa6f,
+}; /* CR_In_Chess_Symbols */
+
+/* 'In_Symbols_and_Pictographs_Extended_A': Block */
+static const OnigCodePoint CR_In_Symbols_and_Pictographs_Extended_A[] = {
+ 1,
+ 0x1fa70, 0x1faff,
+}; /* CR_In_Symbols_and_Pictographs_Extended_A */
+
+/* 'In_CJK_Unified_Ideographs_Extension_B': Block */
+static const OnigCodePoint CR_In_CJK_Unified_Ideographs_Extension_B[] = {
+ 1,
+ 0x20000, 0x2a6df,
+}; /* CR_In_CJK_Unified_Ideographs_Extension_B */
+
+/* 'In_CJK_Unified_Ideographs_Extension_C': Block */
+static const OnigCodePoint CR_In_CJK_Unified_Ideographs_Extension_C[] = {
+ 1,
+ 0x2a700, 0x2b73f,
+}; /* CR_In_CJK_Unified_Ideographs_Extension_C */
+
+/* 'In_CJK_Unified_Ideographs_Extension_D': Block */
+static const OnigCodePoint CR_In_CJK_Unified_Ideographs_Extension_D[] = {
+ 1,
+ 0x2b740, 0x2b81f,
+}; /* CR_In_CJK_Unified_Ideographs_Extension_D */
+
+/* 'In_CJK_Unified_Ideographs_Extension_E': Block */
+static const OnigCodePoint CR_In_CJK_Unified_Ideographs_Extension_E[] = {
+ 1,
+ 0x2b820, 0x2ceaf,
+}; /* CR_In_CJK_Unified_Ideographs_Extension_E */
+
+/* 'In_CJK_Unified_Ideographs_Extension_F': Block */
+static const OnigCodePoint CR_In_CJK_Unified_Ideographs_Extension_F[] = {
+ 1,
+ 0x2ceb0, 0x2ebef,
+}; /* CR_In_CJK_Unified_Ideographs_Extension_F */
+
+/* 'In_CJK_Compatibility_Ideographs_Supplement': Block */
+static const OnigCodePoint CR_In_CJK_Compatibility_Ideographs_Supplement[] = {
+ 1,
+ 0x2f800, 0x2fa1f,
+}; /* CR_In_CJK_Compatibility_Ideographs_Supplement */
+
+/* 'In_Tags': Block */
+static const OnigCodePoint CR_In_Tags[] = {
+ 1,
+ 0xe0000, 0xe007f,
+}; /* CR_In_Tags */
+
+/* 'In_Variation_Selectors_Supplement': Block */
+static const OnigCodePoint CR_In_Variation_Selectors_Supplement[] = {
+ 1,
+ 0xe0100, 0xe01ef,
+}; /* CR_In_Variation_Selectors_Supplement */
+
+/* 'In_Supplementary_Private_Use_Area_A': Block */
+static const OnigCodePoint CR_In_Supplementary_Private_Use_Area_A[] = {
+ 1,
+ 0xf0000, 0xfffff,
+}; /* CR_In_Supplementary_Private_Use_Area_A */
+
+/* 'In_Supplementary_Private_Use_Area_B': Block */
+static const OnigCodePoint CR_In_Supplementary_Private_Use_Area_B[] = {
+ 1,
+ 0x100000, 0x10ffff,
+}; /* CR_In_Supplementary_Private_Use_Area_B */
+
+/* 'In_No_Block': Block */
+static const OnigCodePoint CR_In_No_Block[] = {
+ 53,
+ 0x0870, 0x089f,
+ 0x2fe0, 0x2fef,
+ 0x10200, 0x1027f,
+ 0x103e0, 0x103ff,
+ 0x10570, 0x105ff,
+ 0x10780, 0x107ff,
+ 0x108b0, 0x108df,
+ 0x10940, 0x1097f,
+ 0x10aa0, 0x10abf,
+ 0x10bb0, 0x10bff,
+ 0x10c50, 0x10c7f,
+ 0x10d40, 0x10e5f,
+ 0x10e80, 0x10eff,
+ 0x10f70, 0x10fdf,
+ 0x11250, 0x1127f,
+ 0x11380, 0x113ff,
+ 0x114e0, 0x1157f,
+ 0x116d0, 0x116ff,
+ 0x11740, 0x117ff,
+ 0x11850, 0x1189f,
+ 0x11900, 0x1199f,
+ 0x11ab0, 0x11abf,
+ 0x11b00, 0x11bff,
+ 0x11cc0, 0x11cff,
+ 0x11db0, 0x11edf,
+ 0x11f00, 0x11fbf,
+ 0x12550, 0x12fff,
+ 0x13440, 0x143ff,
+ 0x14680, 0x167ff,
+ 0x16a70, 0x16acf,
+ 0x16b90, 0x16e3f,
+ 0x16ea0, 0x16eff,
+ 0x16fa0, 0x16fdf,
+ 0x18b00, 0x1afff,
+ 0x1b300, 0x1bbff,
+ 0x1bcb0, 0x1cfff,
+ 0x1d250, 0x1d2df,
+ 0x1d380, 0x1d3ff,
+ 0x1dab0, 0x1dfff,
+ 0x1e030, 0x1e0ff,
+ 0x1e150, 0x1e2bf,
+ 0x1e300, 0x1e7ff,
+ 0x1e8e0, 0x1e8ff,
+ 0x1e960, 0x1ec6f,
+ 0x1ecc0, 0x1ecff,
+ 0x1ed50, 0x1edff,
+ 0x1ef00, 0x1efff,
+ 0x1fb00, 0x1ffff,
+ 0x2a6e0, 0x2a6ff,
+ 0x2ebf0, 0x2f7ff,
+ 0x2fa20, 0xdffff,
+ 0xe0080, 0xe00ff,
+ 0xe01f0, 0xeffff,
+}; /* CR_In_No_Block */
+
+#endif /* USE_UNICODE_PROPERTIES */
+static const OnigCodePoint* const CodeRanges[] = {
+ CR_NEWLINE,
+ CR_Alpha,
+ CR_Blank,
+ CR_Cntrl,
+ CR_Digit,
+ CR_Graph,
+ CR_Lower,
+ CR_Print,
+ CR_XPosixPunct,
+ CR_Space,
+ CR_Upper,
+ CR_XDigit,
+ CR_Word,
+ CR_Alnum,
+ CR_ASCII,
+ CR_Punct,
+#ifdef USE_UNICODE_PROPERTIES
+ CR_Any,
+ CR_Assigned,
+ CR_C,
+ CR_Cc,
+ CR_Cf,
+ CR_Cn,
+ CR_Co,
+ CR_Cs,
+ CR_L,
+ CR_LC,
+ CR_Ll,
+ CR_Lm,
+ CR_Lo,
+ CR_Lt,
+ CR_Lu,
+ CR_M,
+ CR_Mc,
+ CR_Me,
+ CR_Mn,
+ CR_N,
+ CR_Nd,
+ CR_Nl,
+ CR_No,
+ CR_P,
+ CR_Pc,
+ CR_Pd,
+ CR_Pe,
+ CR_Pf,
+ CR_Pi,
+ CR_Po,
+ CR_Ps,
+ CR_S,
+ CR_Sc,
+ CR_Sk,
+ CR_Sm,
+ CR_So,
+ CR_Z,
+ CR_Zl,
+ CR_Zp,
+ CR_Zs,
+ CR_Math,
+ CR_Alphabetic,
+ CR_Lowercase,
+ CR_Uppercase,
+ CR_Cased,
+ CR_Case_Ignorable,
+ CR_Changes_When_Lowercased,
+ CR_Changes_When_Uppercased,
+ CR_Changes_When_Titlecased,
+ CR_Changes_When_Casefolded,
+ CR_Changes_When_Casemapped,
+ CR_ID_Start,
+ CR_ID_Continue,
+ CR_XID_Start,
+ CR_XID_Continue,
+ CR_Default_Ignorable_Code_Point,
+ CR_Grapheme_Extend,
+ CR_Grapheme_Base,
+ CR_Grapheme_Link,
+ CR_Common,
+ CR_Latin,
+ CR_Greek,
+ CR_Cyrillic,
+ CR_Armenian,
+ CR_Hebrew,
+ CR_Arabic,
+ CR_Syriac,
+ CR_Thaana,
+ CR_Devanagari,
+ CR_Bengali,
+ CR_Gurmukhi,
+ CR_Gujarati,
+ CR_Oriya,
+ CR_Tamil,
+ CR_Telugu,
+ CR_Kannada,
+ CR_Malayalam,
+ CR_Sinhala,
+ CR_Thai,
+ CR_Lao,
+ CR_Tibetan,
+ CR_Myanmar,
+ CR_Georgian,
+ CR_Hangul,
+ CR_Ethiopic,
+ CR_Cherokee,
+ CR_Canadian_Aboriginal,
+ CR_Ogham,
+ CR_Runic,
+ CR_Khmer,
+ CR_Mongolian,
+ CR_Hiragana,
+ CR_Katakana,
+ CR_Bopomofo,
+ CR_Han,
+ CR_Yi,
+ CR_Old_Italic,
+ CR_Gothic,
+ CR_Deseret,
+ CR_Inherited,
+ CR_Tagalog,
+ CR_Hanunoo,
+ CR_Buhid,
+ CR_Tagbanwa,
+ CR_Limbu,
+ CR_Tai_Le,
+ CR_Linear_B,
+ CR_Ugaritic,
+ CR_Shavian,
+ CR_Osmanya,
+ CR_Cypriot,
+ CR_Braille,
+ CR_Buginese,
+ CR_Coptic,
+ CR_New_Tai_Lue,
+ CR_Glagolitic,
+ CR_Tifinagh,
+ CR_Syloti_Nagri,
+ CR_Old_Persian,
+ CR_Kharoshthi,
+ CR_Balinese,
+ CR_Cuneiform,
+ CR_Phoenician,
+ CR_Phags_Pa,
+ CR_Nko,
+ CR_Sundanese,
+ CR_Lepcha,
+ CR_Ol_Chiki,
+ CR_Vai,
+ CR_Saurashtra,
+ CR_Kayah_Li,
+ CR_Rejang,
+ CR_Lycian,
+ CR_Carian,
+ CR_Lydian,
+ CR_Cham,
+ CR_Tai_Tham,
+ CR_Tai_Viet,
+ CR_Avestan,
+ CR_Egyptian_Hieroglyphs,
+ CR_Samaritan,
+ CR_Lisu,
+ CR_Bamum,
+ CR_Javanese,
+ CR_Meetei_Mayek,
+ CR_Imperial_Aramaic,
+ CR_Old_South_Arabian,
+ CR_Inscriptional_Parthian,
+ CR_Inscriptional_Pahlavi,
+ CR_Old_Turkic,
+ CR_Kaithi,
+ CR_Batak,
+ CR_Brahmi,
+ CR_Mandaic,
+ CR_Chakma,
+ CR_Meroitic_Cursive,
+ CR_Meroitic_Hieroglyphs,
+ CR_Miao,
+ CR_Sharada,
+ CR_Sora_Sompeng,
+ CR_Takri,
+ CR_Caucasian_Albanian,
+ CR_Bassa_Vah,
+ CR_Duployan,
+ CR_Elbasan,
+ CR_Grantha,
+ CR_Pahawh_Hmong,
+ CR_Khojki,
+ CR_Linear_A,
+ CR_Mahajani,
+ CR_Manichaean,
+ CR_Mende_Kikakui,
+ CR_Modi,
+ CR_Mro,
+ CR_Old_North_Arabian,
+ CR_Nabataean,
+ CR_Palmyrene,
+ CR_Pau_Cin_Hau,
+ CR_Old_Permic,
+ CR_Psalter_Pahlavi,
+ CR_Siddham,
+ CR_Khudawadi,
+ CR_Tirhuta,
+ CR_Warang_Citi,
+ CR_Ahom,
+ CR_Anatolian_Hieroglyphs,
+ CR_Hatran,
+ CR_Multani,
+ CR_Old_Hungarian,
+ CR_SignWriting,
+ CR_Adlam,
+ CR_Bhaiksuki,
+ CR_Marchen,
+ CR_Newa,
+ CR_Osage,
+ CR_Tangut,
+ CR_Masaram_Gondi,
+ CR_Nushu,
+ CR_Soyombo,
+ CR_Zanabazar_Square,
+ CR_Dogra,
+ CR_Gunjala_Gondi,
+ CR_Makasar,
+ CR_Medefaidrin,
+ CR_Hanifi_Rohingya,
+ CR_Sogdian,
+ CR_Old_Sogdian,
+ CR_Elymaic,
+ CR_Nandinagari,
+ CR_Nyiakeng_Puachue_Hmong,
+ CR_Wancho,
+ CR_White_Space,
+ CR_Bidi_Control,
+ CR_Join_Control,
+ CR_Dash,
+ CR_Hyphen,
+ CR_Quotation_Mark,
+ CR_Terminal_Punctuation,
+ CR_Other_Math,
+ CR_Hex_Digit,
+ CR_ASCII_Hex_Digit,
+ CR_Other_Alphabetic,
+ CR_Ideographic,
+ CR_Diacritic,
+ CR_Extender,
+ CR_Other_Lowercase,
+ CR_Other_Uppercase,
+ CR_Noncharacter_Code_Point,
+ CR_Other_Grapheme_Extend,
+ CR_IDS_Binary_Operator,
+ CR_IDS_Trinary_Operator,
+ CR_Radical,
+ CR_Unified_Ideograph,
+ CR_Other_Default_Ignorable_Code_Point,
+ CR_Deprecated,
+ CR_Soft_Dotted,
+ CR_Logical_Order_Exception,
+ CR_Other_ID_Start,
+ CR_Other_ID_Continue,
+ CR_Sentence_Terminal,
+ CR_Variation_Selector,
+ CR_Pattern_White_Space,
+ CR_Pattern_Syntax,
+ CR_Prepended_Concatenation_Mark,
+ CR_Regional_Indicator,
+ CR_Emoji,
+ CR_Emoji_Presentation,
+ CR_Emoji_Modifier,
+ CR_Emoji_Modifier_Base,
+ CR_Emoji_Component,
+ CR_Extended_Pictographic,
+ CR_Unknown,
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ CR_Age_1_1,
+ CR_Age_2_0,
+ CR_Age_2_1,
+ CR_Age_3_0,
+ CR_Age_3_1,
+ CR_Age_3_2,
+ CR_Age_4_0,
+ CR_Age_4_1,
+ CR_Age_5_0,
+ CR_Age_5_1,
+ CR_Age_5_2,
+ CR_Age_6_0,
+ CR_Age_6_1,
+ CR_Age_6_2,
+ CR_Age_6_3,
+ CR_Age_7_0,
+ CR_Age_8_0,
+ CR_Age_9_0,
+ CR_Age_10_0,
+ CR_Age_11_0,
+ CR_Age_12_0,
+ CR_Age_12_1,
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ CR_Grapheme_Cluster_Break_Prepend,
+ CR_Grapheme_Cluster_Break_CR,
+ CR_Grapheme_Cluster_Break_LF,
+ CR_Grapheme_Cluster_Break_Control,
+ CR_Grapheme_Cluster_Break_Extend,
+ CR_Grapheme_Cluster_Break_Regional_Indicator,
+ CR_Grapheme_Cluster_Break_SpacingMark,
+ CR_Grapheme_Cluster_Break_L,
+ CR_Grapheme_Cluster_Break_V,
+ CR_Grapheme_Cluster_Break_T,
+ CR_Grapheme_Cluster_Break_LV,
+ CR_Grapheme_Cluster_Break_LVT,
+ CR_Grapheme_Cluster_Break_ZWJ,
+ CR_In_Basic_Latin,
+ CR_In_Latin_1_Supplement,
+ CR_In_Latin_Extended_A,
+ CR_In_Latin_Extended_B,
+ CR_In_IPA_Extensions,
+ CR_In_Spacing_Modifier_Letters,
+ CR_In_Combining_Diacritical_Marks,
+ CR_In_Greek_and_Coptic,
+ CR_In_Cyrillic,
+ CR_In_Cyrillic_Supplement,
+ CR_In_Armenian,
+ CR_In_Hebrew,
+ CR_In_Arabic,
+ CR_In_Syriac,
+ CR_In_Arabic_Supplement,
+ CR_In_Thaana,
+ CR_In_NKo,
+ CR_In_Samaritan,
+ CR_In_Mandaic,
+ CR_In_Syriac_Supplement,
+ CR_In_Arabic_Extended_A,
+ CR_In_Devanagari,
+ CR_In_Bengali,
+ CR_In_Gurmukhi,
+ CR_In_Gujarati,
+ CR_In_Oriya,
+ CR_In_Tamil,
+ CR_In_Telugu,
+ CR_In_Kannada,
+ CR_In_Malayalam,
+ CR_In_Sinhala,
+ CR_In_Thai,
+ CR_In_Lao,
+ CR_In_Tibetan,
+ CR_In_Myanmar,
+ CR_In_Georgian,
+ CR_In_Hangul_Jamo,
+ CR_In_Ethiopic,
+ CR_In_Ethiopic_Supplement,
+ CR_In_Cherokee,
+ CR_In_Unified_Canadian_Aboriginal_Syllabics,
+ CR_In_Ogham,
+ CR_In_Runic,
+ CR_In_Tagalog,
+ CR_In_Hanunoo,
+ CR_In_Buhid,
+ CR_In_Tagbanwa,
+ CR_In_Khmer,
+ CR_In_Mongolian,
+ CR_In_Unified_Canadian_Aboriginal_Syllabics_Extended,
+ CR_In_Limbu,
+ CR_In_Tai_Le,
+ CR_In_New_Tai_Lue,
+ CR_In_Khmer_Symbols,
+ CR_In_Buginese,
+ CR_In_Tai_Tham,
+ CR_In_Combining_Diacritical_Marks_Extended,
+ CR_In_Balinese,
+ CR_In_Sundanese,
+ CR_In_Batak,
+ CR_In_Lepcha,
+ CR_In_Ol_Chiki,
+ CR_In_Cyrillic_Extended_C,
+ CR_In_Georgian_Extended,
+ CR_In_Sundanese_Supplement,
+ CR_In_Vedic_Extensions,
+ CR_In_Phonetic_Extensions,
+ CR_In_Phonetic_Extensions_Supplement,
+ CR_In_Combining_Diacritical_Marks_Supplement,
+ CR_In_Latin_Extended_Additional,
+ CR_In_Greek_Extended,
+ CR_In_General_Punctuation,
+ CR_In_Superscripts_and_Subscripts,
+ CR_In_Currency_Symbols,
+ CR_In_Combining_Diacritical_Marks_for_Symbols,
+ CR_In_Letterlike_Symbols,
+ CR_In_Number_Forms,
+ CR_In_Arrows,
+ CR_In_Mathematical_Operators,
+ CR_In_Miscellaneous_Technical,
+ CR_In_Control_Pictures,
+ CR_In_Optical_Character_Recognition,
+ CR_In_Enclosed_Alphanumerics,
+ CR_In_Box_Drawing,
+ CR_In_Block_Elements,
+ CR_In_Geometric_Shapes,
+ CR_In_Miscellaneous_Symbols,
+ CR_In_Dingbats,
+ CR_In_Miscellaneous_Mathematical_Symbols_A,
+ CR_In_Supplemental_Arrows_A,
+ CR_In_Braille_Patterns,
+ CR_In_Supplemental_Arrows_B,
+ CR_In_Miscellaneous_Mathematical_Symbols_B,
+ CR_In_Supplemental_Mathematical_Operators,
+ CR_In_Miscellaneous_Symbols_and_Arrows,
+ CR_In_Glagolitic,
+ CR_In_Latin_Extended_C,
+ CR_In_Coptic,
+ CR_In_Georgian_Supplement,
+ CR_In_Tifinagh,
+ CR_In_Ethiopic_Extended,
+ CR_In_Cyrillic_Extended_A,
+ CR_In_Supplemental_Punctuation,
+ CR_In_CJK_Radicals_Supplement,
+ CR_In_Kangxi_Radicals,
+ CR_In_Ideographic_Description_Characters,
+ CR_In_CJK_Symbols_and_Punctuation,
+ CR_In_Hiragana,
+ CR_In_Katakana,
+ CR_In_Bopomofo,
+ CR_In_Hangul_Compatibility_Jamo,
+ CR_In_Kanbun,
+ CR_In_Bopomofo_Extended,
+ CR_In_CJK_Strokes,
+ CR_In_Katakana_Phonetic_Extensions,
+ CR_In_Enclosed_CJK_Letters_and_Months,
+ CR_In_CJK_Compatibility,
+ CR_In_CJK_Unified_Ideographs_Extension_A,
+ CR_In_Yijing_Hexagram_Symbols,
+ CR_In_CJK_Unified_Ideographs,
+ CR_In_Yi_Syllables,
+ CR_In_Yi_Radicals,
+ CR_In_Lisu,
+ CR_In_Vai,
+ CR_In_Cyrillic_Extended_B,
+ CR_In_Bamum,
+ CR_In_Modifier_Tone_Letters,
+ CR_In_Latin_Extended_D,
+ CR_In_Syloti_Nagri,
+ CR_In_Common_Indic_Number_Forms,
+ CR_In_Phags_pa,
+ CR_In_Saurashtra,
+ CR_In_Devanagari_Extended,
+ CR_In_Kayah_Li,
+ CR_In_Rejang,
+ CR_In_Hangul_Jamo_Extended_A,
+ CR_In_Javanese,
+ CR_In_Myanmar_Extended_B,
+ CR_In_Cham,
+ CR_In_Myanmar_Extended_A,
+ CR_In_Tai_Viet,
+ CR_In_Meetei_Mayek_Extensions,
+ CR_In_Ethiopic_Extended_A,
+ CR_In_Latin_Extended_E,
+ CR_In_Cherokee_Supplement,
+ CR_In_Meetei_Mayek,
+ CR_In_Hangul_Syllables,
+ CR_In_Hangul_Jamo_Extended_B,
+ CR_In_High_Surrogates,
+ CR_In_High_Private_Use_Surrogates,
+ CR_In_Low_Surrogates,
+ CR_In_Private_Use_Area,
+ CR_In_CJK_Compatibility_Ideographs,
+ CR_In_Alphabetic_Presentation_Forms,
+ CR_In_Arabic_Presentation_Forms_A,
+ CR_In_Variation_Selectors,
+ CR_In_Vertical_Forms,
+ CR_In_Combining_Half_Marks,
+ CR_In_CJK_Compatibility_Forms,
+ CR_In_Small_Form_Variants,
+ CR_In_Arabic_Presentation_Forms_B,
+ CR_In_Halfwidth_and_Fullwidth_Forms,
+ CR_In_Specials,
+ CR_In_Linear_B_Syllabary,
+ CR_In_Linear_B_Ideograms,
+ CR_In_Aegean_Numbers,
+ CR_In_Ancient_Greek_Numbers,
+ CR_In_Ancient_Symbols,
+ CR_In_Phaistos_Disc,
+ CR_In_Lycian,
+ CR_In_Carian,
+ CR_In_Coptic_Epact_Numbers,
+ CR_In_Old_Italic,
+ CR_In_Gothic,
+ CR_In_Old_Permic,
+ CR_In_Ugaritic,
+ CR_In_Old_Persian,
+ CR_In_Deseret,
+ CR_In_Shavian,
+ CR_In_Osmanya,
+ CR_In_Osage,
+ CR_In_Elbasan,
+ CR_In_Caucasian_Albanian,
+ CR_In_Linear_A,
+ CR_In_Cypriot_Syllabary,
+ CR_In_Imperial_Aramaic,
+ CR_In_Palmyrene,
+ CR_In_Nabataean,
+ CR_In_Hatran,
+ CR_In_Phoenician,
+ CR_In_Lydian,
+ CR_In_Meroitic_Hieroglyphs,
+ CR_In_Meroitic_Cursive,
+ CR_In_Kharoshthi,
+ CR_In_Old_South_Arabian,
+ CR_In_Old_North_Arabian,
+ CR_In_Manichaean,
+ CR_In_Avestan,
+ CR_In_Inscriptional_Parthian,
+ CR_In_Inscriptional_Pahlavi,
+ CR_In_Psalter_Pahlavi,
+ CR_In_Old_Turkic,
+ CR_In_Old_Hungarian,
+ CR_In_Hanifi_Rohingya,
+ CR_In_Rumi_Numeral_Symbols,
+ CR_In_Old_Sogdian,
+ CR_In_Sogdian,
+ CR_In_Elymaic,
+ CR_In_Brahmi,
+ CR_In_Kaithi,
+ CR_In_Sora_Sompeng,
+ CR_In_Chakma,
+ CR_In_Mahajani,
+ CR_In_Sharada,
+ CR_In_Sinhala_Archaic_Numbers,
+ CR_In_Khojki,
+ CR_In_Multani,
+ CR_In_Khudawadi,
+ CR_In_Grantha,
+ CR_In_Newa,
+ CR_In_Tirhuta,
+ CR_In_Siddham,
+ CR_In_Modi,
+ CR_In_Mongolian_Supplement,
+ CR_In_Takri,
+ CR_In_Ahom,
+ CR_In_Dogra,
+ CR_In_Warang_Citi,
+ CR_In_Nandinagari,
+ CR_In_Zanabazar_Square,
+ CR_In_Soyombo,
+ CR_In_Pau_Cin_Hau,
+ CR_In_Bhaiksuki,
+ CR_In_Marchen,
+ CR_In_Masaram_Gondi,
+ CR_In_Gunjala_Gondi,
+ CR_In_Makasar,
+ CR_In_Tamil_Supplement,
+ CR_In_Cuneiform,
+ CR_In_Cuneiform_Numbers_and_Punctuation,
+ CR_In_Early_Dynastic_Cuneiform,
+ CR_In_Egyptian_Hieroglyphs,
+ CR_In_Egyptian_Hieroglyph_Format_Controls,
+ CR_In_Anatolian_Hieroglyphs,
+ CR_In_Bamum_Supplement,
+ CR_In_Mro,
+ CR_In_Bassa_Vah,
+ CR_In_Pahawh_Hmong,
+ CR_In_Medefaidrin,
+ CR_In_Miao,
+ CR_In_Ideographic_Symbols_and_Punctuation,
+ CR_In_Tangut,
+ CR_In_Tangut_Components,
+ CR_In_Kana_Supplement,
+ CR_In_Kana_Extended_A,
+ CR_In_Small_Kana_Extension,
+ CR_In_Nushu,
+ CR_In_Duployan,
+ CR_In_Shorthand_Format_Controls,
+ CR_In_Byzantine_Musical_Symbols,
+ CR_In_Musical_Symbols,
+ CR_In_Ancient_Greek_Musical_Notation,
+ CR_In_Mayan_Numerals,
+ CR_In_Tai_Xuan_Jing_Symbols,
+ CR_In_Counting_Rod_Numerals,
+ CR_In_Mathematical_Alphanumeric_Symbols,
+ CR_In_Sutton_SignWriting,
+ CR_In_Glagolitic_Supplement,
+ CR_In_Nyiakeng_Puachue_Hmong,
+ CR_In_Wancho,
+ CR_In_Mende_Kikakui,
+ CR_In_Adlam,
+ CR_In_Indic_Siyaq_Numbers,
+ CR_In_Ottoman_Siyaq_Numbers,
+ CR_In_Arabic_Mathematical_Alphabetic_Symbols,
+ CR_In_Mahjong_Tiles,
+ CR_In_Domino_Tiles,
+ CR_In_Playing_Cards,
+ CR_In_Enclosed_Alphanumeric_Supplement,
+ CR_In_Enclosed_Ideographic_Supplement,
+ CR_In_Miscellaneous_Symbols_and_Pictographs,
+ CR_In_Emoticons,
+ CR_In_Ornamental_Dingbats,
+ CR_In_Transport_and_Map_Symbols,
+ CR_In_Alchemical_Symbols,
+ CR_In_Geometric_Shapes_Extended,
+ CR_In_Supplemental_Arrows_C,
+ CR_In_Supplemental_Symbols_and_Pictographs,
+ CR_In_Chess_Symbols,
+ CR_In_Symbols_and_Pictographs_Extended_A,
+ CR_In_CJK_Unified_Ideographs_Extension_B,
+ CR_In_CJK_Unified_Ideographs_Extension_C,
+ CR_In_CJK_Unified_Ideographs_Extension_D,
+ CR_In_CJK_Unified_Ideographs_Extension_E,
+ CR_In_CJK_Unified_Ideographs_Extension_F,
+ CR_In_CJK_Compatibility_Ideographs_Supplement,
+ CR_In_Tags,
+ CR_In_Variation_Selectors_Supplement,
+ CR_In_Supplementary_Private_Use_Area_A,
+ CR_In_Supplementary_Private_Use_Area_B,
+ CR_In_No_Block,
+#endif /* USE_UNICODE_PROPERTIES */
+};
+struct uniname2ctype_struct {
+ short name;
+ unsigned short ctype;
+};
+#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
+);
+
+#ifndef USE_UNICODE_PROPERTIES
+#define TOTAL_KEYWORDS 15
+#define MIN_WORD_LENGTH 4
+#define MAX_WORD_LENGTH 11
+#define MIN_HASH_VALUE 6
+#define MAX_HASH_VALUE 20
+/* maximum key range = 15, duplicates = 0 */
+#else /* USE_UNICODE_PROPERTIES */
+#ifndef USE_UNICODE_AGE_PROPERTIES
+#define TOTAL_KEYWORDS 814
+#else /* USE_UNICODE_AGE_PROPERTIES */
+#define TOTAL_KEYWORDS 836
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+#define MIN_WORD_LENGTH 1
+#define MAX_WORD_LENGTH 44
+#define MIN_HASH_VALUE 11
+#define MAX_HASH_VALUE 6098
+/* maximum key range = 6088, duplicates = 0 */
+#endif /* USE_UNICODE_PROPERTIES */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+uniname2ctype_hash (register const char *str, register size_t len)
+{
+#ifndef USE_UNICODE_PROPERTIES
+ static const unsigned char asso_values[] =
+#else /* USE_UNICODE_PROPERTIES */
+ static const unsigned short asso_values[] =
+#endif /* USE_UNICODE_PROPERTIES */
+ {
+#ifndef USE_UNICODE_PROPERTIES
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 3, 12, 5,
+ 4, 21, 21, 10, 21, 1, 21, 21, 11, 21,
+ 2, 1, 1, 21, 1, 7, 4, 6, 21, 1,
+ 4, 21, 21, 21, 21, 21, 21, 21
+#else /* USE_UNICODE_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, 6099, 6099, 6099,
+#ifndef USE_UNICODE_AGE_PROPERTIES
+ 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099,
+ 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099, 6099,
+#else /* USE_UNICODE_AGE_PROPERTIES */
+ 6099, 6099, 6099, 6099, 6099, 6099, 17, 6099, 3, 1,
+ 4, 13, 3, 22, 9, 16, 12, 5, 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, 1023, 1071, 1051, 4, 1492, 9, 500, 88,
+ 8, 18, 1371, 797, 54, 203, 310, 619, 1608, 603,
+ 364, 1438, 20, 1, 3, 6099, 6099, 6099, 6099, 6099
+#endif /* USE_UNICODE_PROPERTIES */
+ };
+#ifndef USE_UNICODE_PROPERTIES
+ return len + asso_values[(unsigned char)str[2]] + asso_values[(unsigned char)str[0]];
+#else /* USE_UNICODE_PROPERTIES */
+ register unsigned int hval = (unsigned int)len;
+
+ switch (hval)
+ {
+ default:
+ hval += asso_values[(unsigned char)str[15]];
+ /*FALLTHROUGH*/
+ case 15:
+ case 14:
+ case 13:
+ case 12:
+ hval += asso_values[(unsigned char)str[11]];
+ /*FALLTHROUGH*/
+ case 11:
+ case 10:
+ case 9:
+ case 8:
+ case 7:
+ case 6:
+ hval += asso_values[(unsigned char)str[5]];
+ /*FALLTHROUGH*/
+ case 5:
+ hval += asso_values[(unsigned char)str[4]];
+ /*FALLTHROUGH*/
+ case 4:
+ case 3:
+ hval += asso_values[(unsigned char)str[2]];
+ /*FALLTHROUGH*/
+ case 2:
+ hval += asso_values[(unsigned char)str[1]];
+ /*FALLTHROUGH*/
+ case 1:
+ hval += asso_values[(unsigned char)str[0]+2];
+ break;
+ }
+ return hval + asso_values[(unsigned char)str[len - 1]];
+#endif /* USE_UNICODE_PROPERTIES */
+}
+
+struct uniname2ctype_pool_t
+ {
+#ifndef USE_UNICODE_PROPERTIES
+ char uniname2ctype_pool_str6[sizeof("word")];
+ char uniname2ctype_pool_str7[sizeof("print")];
+ char uniname2ctype_pool_str8[sizeof("punct")];
+ char uniname2ctype_pool_str9[sizeof("alpha")];
+ char uniname2ctype_pool_str10[sizeof("alnum")];
+ char uniname2ctype_pool_str11[sizeof("xdigit")];
+ char uniname2ctype_pool_str12[sizeof("upper")];
+ char uniname2ctype_pool_str13[sizeof("ascii")];
+ char uniname2ctype_pool_str14[sizeof("cntrl")];
+ char uniname2ctype_pool_str15[sizeof("space")];
+ char uniname2ctype_pool_str16[sizeof("xposixpunct")];
+ char uniname2ctype_pool_str17[sizeof("lower")];
+ char uniname2ctype_pool_str18[sizeof("graph")];
+ char uniname2ctype_pool_str19[sizeof("digit")];
+ char uniname2ctype_pool_str20[sizeof("blank")];
+#else /* USE_UNICODE_PROPERTIES */
+ char uniname2ctype_pool_str11[sizeof("yi")];
+ char uniname2ctype_pool_str17[sizeof("yiii")];
+ char uniname2ctype_pool_str22[sizeof("lana")];
+ char uniname2ctype_pool_str24[sizeof("z")];
+ char uniname2ctype_pool_str25[sizeof("lina")];
+ char uniname2ctype_pool_str33[sizeof("maka")];
+ char uniname2ctype_pool_str35[sizeof("mani")];
+ char uniname2ctype_pool_str36[sizeof("mn")];
+ char uniname2ctype_pool_str45[sizeof("miao")];
+ char uniname2ctype_pool_str46[sizeof("lo")];
+ char uniname2ctype_pool_str47[sizeof("ci")];
+ char uniname2ctype_pool_str48[sizeof("lao")];
+ char uniname2ctype_pool_str49[sizeof("laoo")];
+ char uniname2ctype_pool_str52[sizeof("inkannada")];
+ char uniname2ctype_pool_str55[sizeof("cn")];
+ char uniname2ctype_pool_str64[sizeof("pi")];
+ char uniname2ctype_pool_str66[sizeof("innko")];
+ char uniname2ctype_pool_str67[sizeof("zzzz")];
+ char uniname2ctype_pool_str71[sizeof("gran")];
+ char uniname2ctype_pool_str75[sizeof("co")];
+ char uniname2ctype_pool_str83[sizeof("lineara")];
+ char uniname2ctype_pool_str86[sizeof("mark")];
+ char uniname2ctype_pool_str92[sizeof("po")];
+ char uniname2ctype_pool_str94[sizeof("me")];
+ char uniname2ctype_pool_str100[sizeof("cari")];
+ char uniname2ctype_pool_str101[sizeof("inkharoshthi")];
+ char uniname2ctype_pool_str102[sizeof("kana")];
+ char uniname2ctype_pool_str103[sizeof("loe")];
+ char uniname2ctype_pool_str107[sizeof("m")];
+ char uniname2ctype_pool_str108[sizeof("grek")];
+ char uniname2ctype_pool_str111[sizeof("mro")];
+ char uniname2ctype_pool_str112[sizeof("mroo")];
+ char uniname2ctype_pool_str115[sizeof("carian")];
+ char uniname2ctype_pool_str117[sizeof("geor")];
+ char uniname2ctype_pool_str118[sizeof("greek")];
+ char uniname2ctype_pool_str122[sizeof("gonm")];
+ char uniname2ctype_pool_str129[sizeof("mendekikakui")];
+ char uniname2ctype_pool_str130[sizeof("pe")];
+ char uniname2ctype_pool_str131[sizeof("mero")];
+ char uniname2ctype_pool_str134[sizeof("inosmanya")];
+ char uniname2ctype_pool_str139[sizeof("cakm")];
+ char uniname2ctype_pool_str145[sizeof("inmanichaean")];
+ char uniname2ctype_pool_str146[sizeof("inmro")];
+ char uniname2ctype_pool_str148[sizeof("inmiao")];
+ char uniname2ctype_pool_str149[sizeof("inchakma")];
+ char uniname2ctype_pool_str151[sizeof("c")];
+ char uniname2ctype_pool_str152[sizeof("mandaic")];
+ char uniname2ctype_pool_str153[sizeof("meeteimayek")];
+ char uniname2ctype_pool_str161[sizeof("inarmenian")];
+ char uniname2ctype_pool_str177[sizeof("inmyanmar")];
+ char uniname2ctype_pool_str178[sizeof("inmakasar")];
+ char uniname2ctype_pool_str183[sizeof("common")];
+ char uniname2ctype_pool_str186[sizeof("lm")];
+ char uniname2ctype_pool_str190[sizeof("marc")];
+ char uniname2ctype_pool_str203[sizeof("inrunic")];
+ char uniname2ctype_pool_str204[sizeof("incarian")];
+ char uniname2ctype_pool_str210[sizeof("inideographicsymbolsandpunctuation")];
+ char uniname2ctype_pool_str212[sizeof("inkhmer")];
+ char uniname2ctype_pool_str213[sizeof("qaai")];
+ char uniname2ctype_pool_str218[sizeof("inahom")];
+ char uniname2ctype_pool_str226[sizeof("merc")];
+ char uniname2ctype_pool_str231[sizeof("combiningmark")];
+ char uniname2ctype_pool_str236[sizeof("lc")];
+ char uniname2ctype_pool_str237[sizeof("perm")];
+ char uniname2ctype_pool_str246[sizeof("mc")];
+ char uniname2ctype_pool_str250[sizeof("connectorpunctuation")];
+ char uniname2ctype_pool_str253[sizeof("cans")];
+ char uniname2ctype_pool_str260[sizeof("incuneiformnumbersandpunctuation")];
+ char uniname2ctype_pool_str263[sizeof("armi")];
+ char uniname2ctype_pool_str265[sizeof("cc")];
+ char uniname2ctype_pool_str267[sizeof("armn")];
+ char uniname2ctype_pool_str268[sizeof("incherokee")];
+ char uniname2ctype_pool_str270[sizeof("prependedconcatenationmark")];
+ char uniname2ctype_pool_str274[sizeof("incuneiform")];
+ char uniname2ctype_pool_str275[sizeof("inavestan")];
+ char uniname2ctype_pool_str281[sizeof("inipaextensions")];
+ char uniname2ctype_pool_str282[sizeof("pc")];
+ char uniname2ctype_pool_str283[sizeof("armenian")];
+ char uniname2ctype_pool_str285[sizeof("insharada")];
+ char uniname2ctype_pool_str289[sizeof("inmarchen")];
+ char uniname2ctype_pool_str293[sizeof("makasar")];
+ char uniname2ctype_pool_str297[sizeof("masaramgondi")];
+ char uniname2ctype_pool_str301[sizeof("inarrows")];
+ char uniname2ctype_pool_str311[sizeof("incyrillic")];
+ char uniname2ctype_pool_str313[sizeof("incham")];
+ char uniname2ctype_pool_str315[sizeof("qmark")];
+ char uniname2ctype_pool_str320[sizeof("ri")];
+ char uniname2ctype_pool_str322[sizeof("qaac")];
+ char uniname2ctype_pool_str328[sizeof("insamaritan")];
+ char uniname2ctype_pool_str331[sizeof("latn")];
+ char uniname2ctype_pool_str335[sizeof("inmasaramgondi")];
+ char uniname2ctype_pool_str338[sizeof("inthaana")];
+ char uniname2ctype_pool_str340[sizeof("latin")];
+ char uniname2ctype_pool_str342[sizeof("inthai")];
+ char uniname2ctype_pool_str345[sizeof("lineseparator")];
+ char uniname2ctype_pool_str346[sizeof("pcm")];
+ char uniname2ctype_pool_str348[sizeof("inkatakana")];
+ char uniname2ctype_pool_str352[sizeof("inkaithi")];
+ char uniname2ctype_pool_str357[sizeof("inzanabazarsquare")];
+ char uniname2ctype_pool_str362[sizeof("inscriptionalparthian")];
+ char uniname2ctype_pool_str366[sizeof("initialpunctuation")];
+ char uniname2ctype_pool_str373[sizeof("mtei")];
+ char uniname2ctype_pool_str376[sizeof("vai")];
+ char uniname2ctype_pool_str377[sizeof("vaii")];
+ char uniname2ctype_pool_str386[sizeof("inkhmersymbols")];
+ char uniname2ctype_pool_str399[sizeof("insyriac")];
+ char uniname2ctype_pool_str401[sizeof("intakri")];
+ char uniname2ctype_pool_str404[sizeof("arabic")];
+ char uniname2ctype_pool_str411[sizeof("zs")];
+ char uniname2ctype_pool_str418[sizeof("katakana")];
+ char uniname2ctype_pool_str426[sizeof("prti")];
+ char uniname2ctype_pool_str442[sizeof("ascii")];
+ char uniname2ctype_pool_str445[sizeof("cs")];
+ char uniname2ctype_pool_str462[sizeof("ps")];
+ char uniname2ctype_pool_str468[sizeof("mand")];
+ char uniname2ctype_pool_str470[sizeof("privateuse")];
+ char uniname2ctype_pool_str475[sizeof("inruminumeralsymbols")];
+ char uniname2ctype_pool_str480[sizeof("inmyanmarextendeda")];
+ char uniname2ctype_pool_str481[sizeof("modi")];
+ char uniname2ctype_pool_str486[sizeof("incjkcompatibilityforms")];
+ char uniname2ctype_pool_str488[sizeof("inkanaextendeda")];
+ char uniname2ctype_pool_str491[sizeof("incjkcompatibilityideographs")];
+ char uniname2ctype_pool_str500[sizeof("brai")];
+ char uniname2ctype_pool_str504[sizeof("mend")];
+ char uniname2ctype_pool_str505[sizeof("ideo")];
+ char uniname2ctype_pool_str506[sizeof("letter")];
+ char uniname2ctype_pool_str509[sizeof("l")];
+ char uniname2ctype_pool_str511[sizeof("inmeeteimayek")];
+ char uniname2ctype_pool_str520[sizeof("inideographicdescriptioncharacters")];
+ char uniname2ctype_pool_str535[sizeof("xidcontinue")];
+ char uniname2ctype_pool_str538[sizeof("knda")];
+ char uniname2ctype_pool_str541[sizeof("innandinagari")];
+ char uniname2ctype_pool_str543[sizeof("kannada")];
+ char uniname2ctype_pool_str556[sizeof("inmodi")];
+ char uniname2ctype_pool_str558[sizeof("inlao")];
+ char uniname2ctype_pool_str560[sizeof("inoldnortharabian")];
+ char uniname2ctype_pool_str565[sizeof("intransportandmapsymbols")];
+ char uniname2ctype_pool_str566[sizeof("letternumber")];
+ char uniname2ctype_pool_str568[sizeof("gothic")];
+ char uniname2ctype_pool_str572[sizeof("inlineara")];
+ char uniname2ctype_pool_str577[sizeof("inmendekikakui")];
+ char uniname2ctype_pool_str578[sizeof("xidc")];
+ char uniname2ctype_pool_str579[sizeof("mongolian")];
+ char uniname2ctype_pool_str582[sizeof("inmiscellaneousmathematicalsymbolsa")];
+ char uniname2ctype_pool_str583[sizeof("inspecials")];
+ char uniname2ctype_pool_str590[sizeof("grlink")];
+ char uniname2ctype_pool_str594[sizeof("brahmi")];
+ char uniname2ctype_pool_str596[sizeof("inemoticons")];
+ char uniname2ctype_pool_str597[sizeof("kali")];
+ char uniname2ctype_pool_str600[sizeof("inolditalic")];
+ char uniname2ctype_pool_str604[sizeof("inmedefaidrin")];
+ char uniname2ctype_pool_str605[sizeof("inchesssymbols")];
+ char uniname2ctype_pool_str608[sizeof("incjkcompatibilityideographssupplement")];
+ char uniname2ctype_pool_str614[sizeof("inadlam")];
+ char uniname2ctype_pool_str624[sizeof("psalterpahlavi")];
+ char uniname2ctype_pool_str625[sizeof("incommonindicnumberforms")];
+ char uniname2ctype_pool_str630[sizeof("lt")];
+ char uniname2ctype_pool_str636[sizeof("innewa")];
+ char uniname2ctype_pool_str639[sizeof("sk")];
+ char uniname2ctype_pool_str642[sizeof("control")];
+ char uniname2ctype_pool_str645[sizeof("inancientsymbols")];
+ char uniname2ctype_pool_str647[sizeof("palm")];
+ char uniname2ctype_pool_str650[sizeof("inlycian")];
+ char uniname2ctype_pool_str657[sizeof("so")];
+ char uniname2ctype_pool_str660[sizeof("patternwhitespace")];
+ char uniname2ctype_pool_str668[sizeof("xids")];
+ char uniname2ctype_pool_str672[sizeof("inmandaic")];
+ char uniname2ctype_pool_str675[sizeof("idc")];
+ char uniname2ctype_pool_str678[sizeof("meroiticcursive")];
+ char uniname2ctype_pool_str695[sizeof("inwarangciti")];
+ char uniname2ctype_pool_str696[sizeof("sora")];
+ char uniname2ctype_pool_str697[sizeof("inopticalcharacterrecognition")];
+ char uniname2ctype_pool_str703[sizeof("inoldsogdian")];
+ char uniname2ctype_pool_str705[sizeof("inmalayalam")];
+ char uniname2ctype_pool_str707[sizeof("bamum")];
+ char uniname2ctype_pool_str708[sizeof("inkanasupplement")];
+ char uniname2ctype_pool_str713[sizeof("insundanese")];
+ char uniname2ctype_pool_str720[sizeof("grext")];
+ char uniname2ctype_pool_str737[sizeof("print")];
+ char uniname2ctype_pool_str738[sizeof("intaitham")];
+ char uniname2ctype_pool_str742[sizeof("lower")];
+ char uniname2ctype_pool_str753[sizeof("joinc")];
+ char uniname2ctype_pool_str755[sizeof("inoldsoutharabian")];
+ char uniname2ctype_pool_str760[sizeof("incjkstrokes")];
+ char uniname2ctype_pool_str761[sizeof("batk")];
+ char uniname2ctype_pool_str766[sizeof("samr")];
+ char uniname2ctype_pool_str767[sizeof("inwancho")];
+ char uniname2ctype_pool_str771[sizeof("batak")];
+ char uniname2ctype_pool_str772[sizeof("vs")];
+ char uniname2ctype_pool_str776[sizeof("patws")];
+ char uniname2ctype_pool_str783[sizeof("samaritan")];
+ char uniname2ctype_pool_str787[sizeof("idsbinaryoperator")];
+ char uniname2ctype_pool_str791[sizeof("pauc")];
+ char uniname2ctype_pool_str794[sizeof("insmallkanaextension")];
+ char uniname2ctype_pool_str797[sizeof("sm")];
+ char uniname2ctype_pool_str799[sizeof("indominotiles")];
+ char uniname2ctype_pool_str802[sizeof("alnum")];
+ char uniname2ctype_pool_str809[sizeof("insylotinagri")];
+ char uniname2ctype_pool_str814[sizeof("inugaritic")];
+ char uniname2ctype_pool_str818[sizeof("incontrolpictures")];
+ char uniname2ctype_pool_str821[sizeof("inlinearbideograms")];
+ char uniname2ctype_pool_str822[sizeof("inmusicalsymbols")];
+ char uniname2ctype_pool_str823[sizeof("s")];
+ char uniname2ctype_pool_str824[sizeof("ital")];
+ char uniname2ctype_pool_str825[sizeof("inmodifiertoneletters")];
+ char uniname2ctype_pool_str828[sizeof("inancientgreekmusicalnotation")];
+ char uniname2ctype_pool_str834[sizeof("patternsyntax")];
+ char uniname2ctype_pool_str838[sizeof("lisu")];
+ char uniname2ctype_pool_str842[sizeof("lowercase")];
+ char uniname2ctype_pool_str845[sizeof("cwcm")];
+ char uniname2ctype_pool_str847[sizeof("sc")];
+ char uniname2ctype_pool_str848[sizeof("bass")];
+ char uniname2ctype_pool_str855[sizeof("ids")];
+ char uniname2ctype_pool_str857[sizeof("inlatinextendeda")];
+ char uniname2ctype_pool_str862[sizeof("oriya")];
+ char uniname2ctype_pool_str875[sizeof("intaile")];
+ char uniname2ctype_pool_str886[sizeof("inmiscellaneoussymbols")];
+ char uniname2ctype_pool_str895[sizeof("inmiscellaneoussymbolsandarrows")];
+ char uniname2ctype_pool_str898[sizeof("incaucasianalbanian")];
+ char uniname2ctype_pool_str900[sizeof("inmiscellaneoussymbolsandpictographs")];
+ char uniname2ctype_pool_str906[sizeof("inoldturkic")];
+ char uniname2ctype_pool_str907[sizeof("insaurashtra")];
+ char uniname2ctype_pool_str924[sizeof("idcontinue")];
+ char uniname2ctype_pool_str926[sizeof("intamil")];
+ char uniname2ctype_pool_str928[sizeof("inmultani")];
+ char uniname2ctype_pool_str929[sizeof("inlatinextendede")];
+ char uniname2ctype_pool_str930[sizeof("pd")];
+ char uniname2ctype_pool_str946[sizeof("bali")];
+ char uniname2ctype_pool_str961[sizeof("blank")];
+ char uniname2ctype_pool_str963[sizeof("idst")];
+ char uniname2ctype_pool_str974[sizeof("inlydian")];
+ char uniname2ctype_pool_str986[sizeof("innewtailue")];
+ char uniname2ctype_pool_str994[sizeof("bengali")];
+ char uniname2ctype_pool_str995[sizeof("runr")];
+ char uniname2ctype_pool_str1005[sizeof("zl")];
+ char uniname2ctype_pool_str1009[sizeof("incyrillicextendeda")];
+ char uniname2ctype_pool_str1010[sizeof("ll")];
+ char uniname2ctype_pool_str1013[sizeof("indeseret")];
+ char uniname2ctype_pool_str1014[sizeof("intaixuanjingsymbols")];
+ char uniname2ctype_pool_str1015[sizeof("inancientgreeknumbers")];
+ char uniname2ctype_pool_str1021[sizeof("idstart")];
+ char uniname2ctype_pool_str1025[sizeof("inmeeteimayekextensions")];
+ char uniname2ctype_pool_str1028[sizeof("balinese")];
+ char uniname2ctype_pool_str1032[sizeof("dia")];
+ char uniname2ctype_pool_str1033[sizeof("di")];
+ char uniname2ctype_pool_str1035[sizeof("inspacingmodifierletters")];
+ char uniname2ctype_pool_str1036[sizeof("inearlydynasticcuneiform")];
+ char uniname2ctype_pool_str1049[sizeof("plrd")];
+ char uniname2ctype_pool_str1067[sizeof("canadianaboriginal")];
+ char uniname2ctype_pool_str1070[sizeof("zinh")];
+ char uniname2ctype_pool_str1072[sizeof("sind")];
+ char uniname2ctype_pool_str1080[sizeof("osage")];
+ char uniname2ctype_pool_str1081[sizeof("inlatinextendedc")];
+ char uniname2ctype_pool_str1085[sizeof("uideo")];
+ char uniname2ctype_pool_str1087[sizeof("incountingrodnumerals")];
+ char uniname2ctype_pool_str1090[sizeof("xidstart")];
+ char uniname2ctype_pool_str1091[sizeof("xdigit")];
+ char uniname2ctype_pool_str1093[sizeof("osma")];
+ char uniname2ctype_pool_str1097[sizeof("inkhudawadi")];
+ char uniname2ctype_pool_str1102[sizeof("inhanifirohingya")];
+ char uniname2ctype_pool_str1105[sizeof("gong")];
+ char uniname2ctype_pool_str1107[sizeof("ingrantha")];
+ char uniname2ctype_pool_str1109[sizeof("bidic")];
+ char uniname2ctype_pool_str1119[sizeof("mong")];
+ char uniname2ctype_pool_str1120[sizeof("cased")];
+ char uniname2ctype_pool_str1121[sizeof("incyrillicextendedc")];
+ char uniname2ctype_pool_str1134[sizeof("inhiragana")];
+ char uniname2ctype_pool_str1140[sizeof("sinhala")];
+ char uniname2ctype_pool_str1142[sizeof("adlm")];
+ char uniname2ctype_pool_str1146[sizeof("glagolitic")];
+ char uniname2ctype_pool_str1147[sizeof("sterm")];
+ char uniname2ctype_pool_str1149[sizeof("bamu")];
+ char uniname2ctype_pool_str1150[sizeof("georgian")];
+ char uniname2ctype_pool_str1151[sizeof("inosage")];
+ char uniname2ctype_pool_str1152[sizeof("gunjalagondi")];
+ char uniname2ctype_pool_str1153[sizeof("phoenician")];
+ char uniname2ctype_pool_str1157[sizeof("multani")];
+ char uniname2ctype_pool_str1158[sizeof("kaithi")];
+ char uniname2ctype_pool_str1164[sizeof("joincontrol")];
+ char uniname2ctype_pool_str1168[sizeof("runic")];
+ char uniname2ctype_pool_str1170[sizeof("ingeneralpunctuation")];
+ char uniname2ctype_pool_str1171[sizeof("inmahajani")];
+ char uniname2ctype_pool_str1174[sizeof("incyrillicsupplement")];
+ char uniname2ctype_pool_str1175[sizeof("lowercaseletter")];
+ char uniname2ctype_pool_str1176[sizeof("marchen")];
+ char uniname2ctype_pool_str1177[sizeof("graphemelink")];
+ char uniname2ctype_pool_str1178[sizeof("ingeorgian")];
+ char uniname2ctype_pool_str1180[sizeof("khojki")];
+ char uniname2ctype_pool_str1181[sizeof("cham")];
+ char uniname2ctype_pool_str1182[sizeof("inogham")];
+ char uniname2ctype_pool_str1183[sizeof("cher")];
+ char uniname2ctype_pool_str1185[sizeof("chakma")];
+ 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_str1207[sizeof("incherokeesupplement")];
+ char uniname2ctype_pool_str1208[sizeof("diacritic")];
+ char uniname2ctype_pool_str1209[sizeof("manichaean")];
+ char uniname2ctype_pool_str1210[sizeof("xsux")];
+ char uniname2ctype_pool_str1212[sizeof("inolchiki")];
+ char uniname2ctype_pool_str1227[sizeof("quotationmark")];
+ char uniname2ctype_pool_str1231[sizeof("adlam")];
+ char uniname2ctype_pool_str1232[sizeof("inethiopic")];
+ char uniname2ctype_pool_str1233[sizeof("graphemebase")];
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ char uniname2ctype_pool_str1234[sizeof("age=11.0")];
+ char uniname2ctype_pool_str1235[sizeof("age=12.1")];
+ char uniname2ctype_pool_str1236[sizeof("age=10.0")];
+ char uniname2ctype_pool_str1237[sizeof("age=12.0")];
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ char uniname2ctype_pool_str1243[sizeof("casedletter")];
+ char uniname2ctype_pool_str1244[sizeof("ingurmukhi")];
+ char uniname2ctype_pool_str1245[sizeof("odi")];
+ char uniname2ctype_pool_str1246[sizeof("incjkunifiedideographsextensiona")];
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ char uniname2ctype_pool_str1247[sizeof("age=1.1")];
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ char uniname2ctype_pool_str1248[sizeof("lu")];
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ char uniname2ctype_pool_str1249[sizeof("age=4.1")];
+ char uniname2ctype_pool_str1250[sizeof("age=2.1")];
+ char uniname2ctype_pool_str1251[sizeof("age=4.0")];
+ char uniname2ctype_pool_str1252[sizeof("age=2.0")];
+ char uniname2ctype_pool_str1253[sizeof("age=9.0")];
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ char uniname2ctype_pool_str1254[sizeof("intamilsupplement")];
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ char uniname2ctype_pool_str1255[sizeof("age=6.1")];
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ char uniname2ctype_pool_str1256[sizeof("unknown")];
+#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=3.1")];
+ char uniname2ctype_pool_str1260[sizeof("age=8.0")];
+ char uniname2ctype_pool_str1261[sizeof("age=3.0")];
+ char uniname2ctype_pool_str1262[sizeof("age=3.2")];
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ char uniname2ctype_pool_str1263[sizeof("cwt")];
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ char uniname2ctype_pool_str1264[sizeof("age=7.0")];
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ char uniname2ctype_pool_str1266[sizeof("unassigned")];
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ char uniname2ctype_pool_str1267[sizeof("age=6.3")];
+ char uniname2ctype_pool_str1268[sizeof("age=5.1")];
+ char uniname2ctype_pool_str1270[sizeof("age=5.0")];
+ char uniname2ctype_pool_str1271[sizeof("age=5.2")];
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ char uniname2ctype_pool_str1274[sizeof("ahom")];
+ char uniname2ctype_pool_str1282[sizeof("incjkunifiedideographsextensione")];
+ char uniname2ctype_pool_str1285[sizeof("khmr")];
+ char uniname2ctype_pool_str1289[sizeof("insinhala")];
+ char uniname2ctype_pool_str1292[sizeof("inmiscellaneoustechnical")];
+ char uniname2ctype_pool_str1297[sizeof("saur")];
+ 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_str1319[sizeof("variationselector")];
+ char uniname2ctype_pool_str1331[sizeof("logicalorderexception")];
+ char uniname2ctype_pool_str1340[sizeof("khmer")];
+ char uniname2ctype_pool_str1343[sizeof("limbu")];
+ char uniname2ctype_pool_str1354[sizeof("inscriptionalpahlavi")];
+ char uniname2ctype_pool_str1355[sizeof("oidc")];
+ char uniname2ctype_pool_str1358[sizeof("incjkunifiedideographsextensionc")];
+ char uniname2ctype_pool_str1360[sizeof("cntrl")];
+ char uniname2ctype_pool_str1365[sizeof("inlatinextendedadditional")];
+ char uniname2ctype_pool_str1366[sizeof("decimalnumber")];
+ char uniname2ctype_pool_str1367[sizeof("insorasompeng")];
+ char uniname2ctype_pool_str1369[sizeof("radical")];
+ char uniname2ctype_pool_str1373[sizeof("emojimodifier")];
+ char uniname2ctype_pool_str1375[sizeof("kharoshthi")];
+ char uniname2ctype_pool_str1380[sizeof("n")];
+ char uniname2ctype_pool_str1384[sizeof("math")];
+ char uniname2ctype_pool_str1387[sizeof("goth")];
+ char uniname2ctype_pool_str1400[sizeof("anatolianhieroglyphs")];
+ char uniname2ctype_pool_str1401[sizeof("inenclosedalphanumerics")];
+ char uniname2ctype_pool_str1407[sizeof("nandinagari")];
+ char uniname2ctype_pool_str1409[sizeof("no")];
+ char uniname2ctype_pool_str1419[sizeof("nko")];
+ char uniname2ctype_pool_str1420[sizeof("nkoo")];
+ char uniname2ctype_pool_str1422[sizeof("ingreekandcoptic")];
+ char uniname2ctype_pool_str1423[sizeof("olck")];
+ char uniname2ctype_pool_str1426[sizeof("p")];
+ char uniname2ctype_pool_str1428[sizeof("grantha")];
+ char uniname2ctype_pool_str1434[sizeof("olchiki")];
+ char uniname2ctype_pool_str1438[sizeof("incjkunifiedideographs")];
+ char uniname2ctype_pool_str1441[sizeof("zanb")];
+ char uniname2ctype_pool_str1442[sizeof("intirhuta")];
+ char uniname2ctype_pool_str1445[sizeof("oids")];
+ char uniname2ctype_pool_str1448[sizeof("inhatran")];
+ char uniname2ctype_pool_str1449[sizeof("linb")];
+ char uniname2ctype_pool_str1450[sizeof("xpeo")];
+ char uniname2ctype_pool_str1451[sizeof("mult")];
+ char uniname2ctype_pool_str1454[sizeof("saurashtra")];
+ char uniname2ctype_pool_str1457[sizeof("kthi")];
+ char uniname2ctype_pool_str1462[sizeof("inbhaiksuki")];
+ char uniname2ctype_pool_str1466[sizeof("olower")];
+ char uniname2ctype_pool_str1470[sizeof("innabataean")];
+ char uniname2ctype_pool_str1471[sizeof("inphoenician")];
+ char uniname2ctype_pool_str1475[sizeof("inkanbun")];
+ char uniname2ctype_pool_str1476[sizeof("inmeroitichieroglyphs")];
+ char uniname2ctype_pool_str1478[sizeof("inkayahli")];
+ char uniname2ctype_pool_str1481[sizeof("phnx")];
+ char uniname2ctype_pool_str1485[sizeof("inoriya")];
+ char uniname2ctype_pool_str1489[sizeof("enclosingmark")];
+ char uniname2ctype_pool_str1495[sizeof("sd")];
+ char uniname2ctype_pool_str1497[sizeof("inelbasan")];
+ char uniname2ctype_pool_str1498[sizeof("wara")];
+ char uniname2ctype_pool_str1499[sizeof("inenclosedideographicsupplement")];
+ char uniname2ctype_pool_str1501[sizeof("sidd")];
+ char uniname2ctype_pool_str1507[sizeof("linearb")];
+ char uniname2ctype_pool_str1509[sizeof("hani")];
+ char uniname2ctype_pool_str1512[sizeof("han")];
+ char uniname2ctype_pool_str1517[sizeof("inenclosedalphanumericsupplement")];
+ char uniname2ctype_pool_str1519[sizeof("medf")];
+ char uniname2ctype_pool_str1520[sizeof("bidicontrol")];
+ char uniname2ctype_pool_str1523[sizeof("hano")];
+ char uniname2ctype_pool_str1524[sizeof("inphaistosdisc")];
+ char uniname2ctype_pool_str1529[sizeof("limb")];
+ char uniname2ctype_pool_str1531[sizeof("inkangxiradicals")];
+ char uniname2ctype_pool_str1533[sizeof("lepc")];
+ char uniname2ctype_pool_str1535[sizeof("medefaidrin")];
+ char uniname2ctype_pool_str1536[sizeof("braille")];
+ char uniname2ctype_pool_str1537[sizeof("regionalindicator")];
+ char uniname2ctype_pool_str1542[sizeof("inlowsurrogates")];
+ char uniname2ctype_pool_str1544[sizeof("inshorthandformatcontrols")];
+ char uniname2ctype_pool_str1547[sizeof("brah")];
+ char uniname2ctype_pool_str1548[sizeof("inkhojki")];
+ char uniname2ctype_pool_str1549[sizeof("inoldhungarian")];
+ char uniname2ctype_pool_str1552[sizeof("hanunoo")];
+ char uniname2ctype_pool_str1555[sizeof("hira")];
+ char uniname2ctype_pool_str1557[sizeof("beng")];
+ char uniname2ctype_pool_str1563[sizeof("emojimodifierbase")];
+ char uniname2ctype_pool_str1565[sizeof("inarabic")];
+ char uniname2ctype_pool_str1567[sizeof("lyci")];
+ char uniname2ctype_pool_str1569[sizeof("ahex")];
+ char uniname2ctype_pool_str1572[sizeof("inherited")];
+ char uniname2ctype_pool_str1580[sizeof("glag")];
+ char uniname2ctype_pool_str1582[sizeof("lycian")];
+ char uniname2ctype_pool_str1587[sizeof("indogra")];
+ char uniname2ctype_pool_str1594[sizeof("dsrt")];
+ char uniname2ctype_pool_str1597[sizeof("arab")];
+ char uniname2ctype_pool_str1602[sizeof("mymr")];
+ char uniname2ctype_pool_str1607[sizeof("myanmar")];
+ char uniname2ctype_pool_str1613[sizeof("phli")];
+ char uniname2ctype_pool_str1617[sizeof("inimperialaramaic")];
+ char uniname2ctype_pool_str1622[sizeof("ingreekextended")];
+ char uniname2ctype_pool_str1623[sizeof("inanatolianhieroglyphs")];
+ char uniname2ctype_pool_str1629[sizeof("punctuation")];
+ char uniname2ctype_pool_str1631[sizeof("takri")];
+ char uniname2ctype_pool_str1635[sizeof("graphemeextend")];
+ char uniname2ctype_pool_str1638[sizeof("invai")];
+ char uniname2ctype_pool_str1643[sizeof("cwl")];
+ char uniname2ctype_pool_str1654[sizeof("ingeometricshapes")];
+ char uniname2ctype_pool_str1655[sizeof("emojicomponent")];
+ char uniname2ctype_pool_str1662[sizeof("coptic")];
+ char uniname2ctype_pool_str1671[sizeof("deseret")];
+ char uniname2ctype_pool_str1675[sizeof("inarabicpresentationformsa")];
+ char uniname2ctype_pool_str1676[sizeof("takr")];
+ char uniname2ctype_pool_str1677[sizeof("inbasiclatin")];
+ char uniname2ctype_pool_str1682[sizeof("incjkunifiedideographsextensiond")];
+ char uniname2ctype_pool_str1686[sizeof("sinh")];
+ char uniname2ctype_pool_str1687[sizeof("sund")];
+ char uniname2ctype_pool_str1691[sizeof("shavian")];
+ char uniname2ctype_pool_str1692[sizeof("taile")];
+ char uniname2ctype_pool_str1699[sizeof("insundanesesupplement")];
+ char uniname2ctype_pool_str1702[sizeof("inelymaic")];
+ char uniname2ctype_pool_str1703[sizeof("insoyombo")];
+ char uniname2ctype_pool_str1704[sizeof("bhks")];
+ char uniname2ctype_pool_str1714[sizeof("bhaiksuki")];
+ char uniname2ctype_pool_str1716[sizeof("incjkcompatibility")];
+ char uniname2ctype_pool_str1722[sizeof("inhanunoo")];
+ char uniname2ctype_pool_str1724[sizeof("intangut")];
+ char uniname2ctype_pool_str1728[sizeof("sogdian")];
+ char uniname2ctype_pool_str1729[sizeof("inlatinextendedd")];
+ char uniname2ctype_pool_str1730[sizeof("sogo")];
+ char uniname2ctype_pool_str1731[sizeof("insinhalaarchaicnumbers")];
+ char uniname2ctype_pool_str1732[sizeof("ideographic")];
+ char uniname2ctype_pool_str1733[sizeof("ugar")];
+ char uniname2ctype_pool_str1740[sizeof("copt")];
+ char uniname2ctype_pool_str1742[sizeof("imperialaramaic")];
+ char uniname2ctype_pool_str1745[sizeof("insogdian")];
+ char uniname2ctype_pool_str1746[sizeof("indingbats")];
+ char uniname2ctype_pool_str1750[sizeof("format")];
+ char uniname2ctype_pool_str1752[sizeof("ininscriptionalpahlavi")];
+ char uniname2ctype_pool_str1757[sizeof("ininscriptionalparthian")];
+ char uniname2ctype_pool_str1766[sizeof("grbase")];
+ char uniname2ctype_pool_str1769[sizeof("inbatak")];
+ char uniname2ctype_pool_str1776[sizeof("cprt")];
+ char uniname2ctype_pool_str1780[sizeof("cwcf")];
+ char uniname2ctype_pool_str1788[sizeof("cuneiform")];
+ char uniname2ctype_pool_str1791[sizeof("term")];
+ char uniname2ctype_pool_str1806[sizeof("intibetan")];
+ char uniname2ctype_pool_str1810[sizeof("intags")];
+ char uniname2ctype_pool_str1811[sizeof("asciihexdigit")];
+ char uniname2ctype_pool_str1813[sizeof("sentenceterminal")];
+ char uniname2ctype_pool_str1816[sizeof("inmayannumerals")];
+ char uniname2ctype_pool_str1821[sizeof("nand")];
+ char uniname2ctype_pool_str1825[sizeof("patsyn")];
+ char uniname2ctype_pool_str1826[sizeof("hatran")];
+ char uniname2ctype_pool_str1828[sizeof("inblockelements")];
+ char uniname2ctype_pool_str1838[sizeof("inornamentaldingbats")];
+ char uniname2ctype_pool_str1842[sizeof("innumberforms")];
+ char uniname2ctype_pool_str1843[sizeof("oldpersian")];
+ char uniname2ctype_pool_str1846[sizeof("inshavian")];
+ char uniname2ctype_pool_str1848[sizeof("bopo")];
+ char uniname2ctype_pool_str1861[sizeof("hatr")];
+ char uniname2ctype_pool_str1866[sizeof("caseignorable")];
+ char uniname2ctype_pool_str1871[sizeof("inoldpersian")];
+ char uniname2ctype_pool_str1878[sizeof("modifierletter")];
+ char uniname2ctype_pool_str1881[sizeof("cwu")];
+ char uniname2ctype_pool_str1891[sizeof("lydi")];
+ char uniname2ctype_pool_str1892[sizeof("inbyzantinemusicalsymbols")];
+ char uniname2ctype_pool_str1896[sizeof("ingeometricshapesextended")];
+ char uniname2ctype_pool_str1904[sizeof("inmyanmarextendedb")];
+ char uniname2ctype_pool_str1905[sizeof("innushu")];
+ char uniname2ctype_pool_str1906[sizeof("lydian")];
+ char uniname2ctype_pool_str1911[sizeof("inunifiedcanadianaboriginalsyllabics")];
+ char uniname2ctype_pool_str1915[sizeof("orkh")];
+ char uniname2ctype_pool_str1928[sizeof("inyiradicals")];
+ char uniname2ctype_pool_str1929[sizeof("inkatakanaphoneticextensions")];
+ char uniname2ctype_pool_str1930[sizeof("inethiopicextendeda")];
+ char uniname2ctype_pool_str1932[sizeof("incoptic")];
+ char uniname2ctype_pool_str1936[sizeof("inarabicextendeda")];
+ char uniname2ctype_pool_str1947[sizeof("oldpermic")];
+ char uniname2ctype_pool_str1950[sizeof("incjksymbolsandpunctuation")];
+ char uniname2ctype_pool_str1951[sizeof("word")];
+ char uniname2ctype_pool_str1958[sizeof("bopomofo")];
+ char uniname2ctype_pool_str1961[sizeof("ogam")];
+ char uniname2ctype_pool_str1964[sizeof("inlisu")];
+ char uniname2ctype_pool_str1967[sizeof("inoldpermic")];
+ char uniname2ctype_pool_str1968[sizeof("innoblock")];
+ char uniname2ctype_pool_str1971[sizeof("taiviet")];
+ char uniname2ctype_pool_str1985[sizeof("inbraillepatterns")];
+ char uniname2ctype_pool_str1991[sizeof("alpha")];
+ char uniname2ctype_pool_str1993[sizeof("inbalinese")];
+ char uniname2ctype_pool_str1994[sizeof("sorasompeng")];
+ char uniname2ctype_pool_str1996[sizeof("closepunctuation")];
+ char uniname2ctype_pool_str2006[sizeof("inmiscellaneousmathematicalsymbolsb")];
+ char uniname2ctype_pool_str2010[sizeof("inlepcha")];
+ char uniname2ctype_pool_str2014[sizeof("insyriacsupplement")];
+ char uniname2ctype_pool_str2016[sizeof("newa")];
+ char uniname2ctype_pool_str2023[sizeof("spacingmark")];
+ char uniname2ctype_pool_str2024[sizeof("inpalmyrene")];
+ char uniname2ctype_pool_str2033[sizeof("cyrl")];
+ char uniname2ctype_pool_str2043[sizeof("assigned")];
+ char uniname2ctype_pool_str2048[sizeof("mlym")];
+ char uniname2ctype_pool_str2055[sizeof("malayalam")];
+ char uniname2ctype_pool_str2058[sizeof("ext")];
+ char uniname2ctype_pool_str2062[sizeof("newtailue")];
+ char uniname2ctype_pool_str2070[sizeof("space")];
+ char uniname2ctype_pool_str2073[sizeof("intelugu")];
+ char uniname2ctype_pool_str2078[sizeof("idsb")];
+ char uniname2ctype_pool_str2083[sizeof("indevanagari")];
+ char uniname2ctype_pool_str2084[sizeof("avestan")];
+ char uniname2ctype_pool_str2085[sizeof("cf")];
+ char uniname2ctype_pool_str2093[sizeof("palmyrene")];
+ char uniname2ctype_pool_str2095[sizeof("inethiopicsupplement")];
+ char uniname2ctype_pool_str2097[sizeof("soyo")];
+ char uniname2ctype_pool_str2098[sizeof("xposixpunct")];
+ char uniname2ctype_pool_str2102[sizeof("pf")];
+ char uniname2ctype_pool_str2103[sizeof("sarb")];
+ char uniname2ctype_pool_str2109[sizeof("zanabazarsquare")];
+ char uniname2ctype_pool_str2110[sizeof("ugaritic")];
+ char uniname2ctype_pool_str2112[sizeof("osge")];
+ char uniname2ctype_pool_str2114[sizeof("java")];
+ char uniname2ctype_pool_str2117[sizeof("sharada")];
+ char uniname2ctype_pool_str2119[sizeof("dogra")];
+ char uniname2ctype_pool_str2135[sizeof("bugi")];
+ char uniname2ctype_pool_str2137[sizeof("meroitichieroglyphs")];
+ char uniname2ctype_pool_str2145[sizeof("separator")];
+ char uniname2ctype_pool_str2146[sizeof("ingeorgiansupplement")];
+ char uniname2ctype_pool_str2149[sizeof("sogd")];
+ char uniname2ctype_pool_str2150[sizeof("tale")];
+ char uniname2ctype_pool_str2153[sizeof("inunifiedcanadianaboriginalsyllabicsextended")];
+ char uniname2ctype_pool_str2161[sizeof("terminalpunctuation")];
+ char uniname2ctype_pool_str2165[sizeof("shrd")];
+ char uniname2ctype_pool_str2166[sizeof("graph")];
+ char uniname2ctype_pool_str2167[sizeof("olditalic")];
+ char uniname2ctype_pool_str2170[sizeof("dogr")];
+ char uniname2ctype_pool_str2173[sizeof("gujr")];
+ char uniname2ctype_pool_str2181[sizeof("phag")];
+ char uniname2ctype_pool_str2182[sizeof("gujarati")];
+ char uniname2ctype_pool_str2195[sizeof("inhanguljamo")];
+ char uniname2ctype_pool_str2199[sizeof("javanese")];
+ char uniname2ctype_pool_str2201[sizeof("taml")];
+ char uniname2ctype_pool_str2204[sizeof("inphoneticextensions")];
+ char uniname2ctype_pool_str2207[sizeof("siddham")];
+ char uniname2ctype_pool_str2217[sizeof("buginese")];
+ char uniname2ctype_pool_str2218[sizeof("inmongoliansupplement")];
+ char uniname2ctype_pool_str2222[sizeof("invariationselectors")];
+ char uniname2ctype_pool_str2224[sizeof("inhanguljamoextendeda")];
+ char uniname2ctype_pool_str2225[sizeof("inverticalforms")];
+ char uniname2ctype_pool_str2228[sizeof("syrc")];
+ char uniname2ctype_pool_str2229[sizeof("number")];
+ char uniname2ctype_pool_str2235[sizeof("incopticepactnumbers")];
+ char uniname2ctype_pool_str2238[sizeof("avst")];
+ char uniname2ctype_pool_str2244[sizeof("inbamum")];
+ char uniname2ctype_pool_str2247[sizeof("nd")];
+ char uniname2ctype_pool_str2248[sizeof("insuttonsignwriting")];
+ char uniname2ctype_pool_str2252[sizeof("extender")];
+ char uniname2ctype_pool_str2258[sizeof("intaiviet")];
+ char uniname2ctype_pool_str2260[sizeof("hex")];
+ char uniname2ctype_pool_str2268[sizeof("incjkunifiedideographsextensionf")];
+ char uniname2ctype_pool_str2271[sizeof("other")];
+ char uniname2ctype_pool_str2272[sizeof("otheridcontinue")];
+ char uniname2ctype_pool_str2278[sizeof("shaw")];
+ char uniname2ctype_pool_str2282[sizeof("dash")];
+ char uniname2ctype_pool_str2285[sizeof("othernumber")];
+ char uniname2ctype_pool_str2294[sizeof("orya")];
+ char uniname2ctype_pool_str2302[sizeof("invedicextensions")];
+ char uniname2ctype_pool_str2305[sizeof("sgnw")];
+ char uniname2ctype_pool_str2312[sizeof("caucasianalbanian")];
+ char uniname2ctype_pool_str2315[sizeof("inmathematicalalphanumericsymbols")];
+ char uniname2ctype_pool_str2321[sizeof("inphoneticextensionssupplement")];
+ char uniname2ctype_pool_str2339[sizeof("invariationselectorssupplement")];
+ char uniname2ctype_pool_str2343[sizeof("induployan")];
+ char uniname2ctype_pool_str2344[sizeof("syriac")];
+ char uniname2ctype_pool_str2357[sizeof("oalpha")];
+ char uniname2ctype_pool_str2361[sizeof("innyiakengpuachuehmong")];
+ char uniname2ctype_pool_str2364[sizeof("incombiningdiacriticalmarks")];
+ char uniname2ctype_pool_str2365[sizeof("inethiopicextended")];
+ char uniname2ctype_pool_str2373[sizeof("nl")];
+ char uniname2ctype_pool_str2374[sizeof("incombiningdiacriticalmarksforsymbols")];
+ char uniname2ctype_pool_str2375[sizeof("khudawadi")];
+ char uniname2ctype_pool_str2378[sizeof("otheralphabetic")];
+ char uniname2ctype_pool_str2389[sizeof("oldhungarian")];
+ char uniname2ctype_pool_str2396[sizeof("incurrencysymbols")];
+ char uniname2ctype_pool_str2397[sizeof("incjkradicalssupplement")];
+ char uniname2ctype_pool_str2398[sizeof("inglagolitic")];
+ char uniname2ctype_pool_str2415[sizeof("intifinagh")];
+ char uniname2ctype_pool_str2416[sizeof("ingeorgianextended")];
+ char uniname2ctype_pool_str2427[sizeof("surrogate")];
+ char uniname2ctype_pool_str2433[sizeof("incyrillicextendedb")];
+ char uniname2ctype_pool_str2440[sizeof("ethi")];
+ char uniname2ctype_pool_str2451[sizeof("titlecaseletter")];
+ char uniname2ctype_pool_str2454[sizeof("rohg")];
+ char uniname2ctype_pool_str2458[sizeof("inmeroiticcursive")];
+ char uniname2ctype_pool_str2460[sizeof("idstrinaryoperator")];
+ char uniname2ctype_pool_str2470[sizeof("inphagspa")];
+ char uniname2ctype_pool_str2475[sizeof("lepcha")];
+ char uniname2ctype_pool_str2479[sizeof("intagalog")];
+ char uniname2ctype_pool_str2480[sizeof("mathsymbol")];
+ char uniname2ctype_pool_str2481[sizeof("incombiningdiacriticalmarkssupplement")];
+ char uniname2ctype_pool_str2506[sizeof("inbrahmi")];
+ char uniname2ctype_pool_str2513[sizeof("insymbolsandpictographsextendeda")];
+ char uniname2ctype_pool_str2519[sizeof("inlinearbsyllabary")];
+ char uniname2ctype_pool_str2529[sizeof("oldturkic")];
+ char uniname2ctype_pool_str2534[sizeof("inbengali")];
+ char uniname2ctype_pool_str2540[sizeof("wancho")];
+ char uniname2ctype_pool_str2542[sizeof("osmanya")];
+ char uniname2ctype_pool_str2548[sizeof("buhd")];
+ char uniname2ctype_pool_str2552[sizeof("insmallformvariants")];
+ char uniname2ctype_pool_str2561[sizeof("indevanagariextended")];
+ char uniname2ctype_pool_str2562[sizeof("softdotted")];
+ char uniname2ctype_pool_str2564[sizeof("inbuginese")];
+ char uniname2ctype_pool_str2566[sizeof("mahj")];
+ char uniname2ctype_pool_str2567[sizeof("inlatin1supplement")];
+ char uniname2ctype_pool_str2570[sizeof("ingothic")];
+ char uniname2ctype_pool_str2575[sizeof("mahajani")];
+ char uniname2ctype_pool_str2576[sizeof("hang")];
+ char uniname2ctype_pool_str2579[sizeof("sylo")];
+ char uniname2ctype_pool_str2586[sizeof("warangciti")];
+ char uniname2ctype_pool_str2595[sizeof("ingujarati")];
+ char uniname2ctype_pool_str2603[sizeof("tirhuta")];
+ char uniname2ctype_pool_str2606[sizeof("incombiningdiacriticalmarksextended")];
+ char uniname2ctype_pool_str2609[sizeof("spaceseparator")];
+ char uniname2ctype_pool_str2614[sizeof("ingunjalagondi")];
+ char uniname2ctype_pool_str2624[sizeof("wcho")];
+ char uniname2ctype_pool_str2631[sizeof("hiragana")];
+ char uniname2ctype_pool_str2634[sizeof("extendedpictographic")];
+ char uniname2ctype_pool_str2643[sizeof("inrejang")];
+ char uniname2ctype_pool_str2644[sizeof("inottomansiyaqnumbers")];
+ char uniname2ctype_pool_str2648[sizeof("nchar")];
+ char uniname2ctype_pool_str2650[sizeof("cyrillic")];
+ char uniname2ctype_pool_str2653[sizeof("khoj")];
+ char uniname2ctype_pool_str2656[sizeof("inlimbu")];
+ char uniname2ctype_pool_str2663[sizeof("hmng")];
+ char uniname2ctype_pool_str2665[sizeof("thaa")];
+ char uniname2ctype_pool_str2668[sizeof("thai")];
+ char uniname2ctype_pool_str2670[sizeof("incjkunifiedideographsextensionb")];
+ char uniname2ctype_pool_str2673[sizeof("deva")];
+ char uniname2ctype_pool_str2676[sizeof("thaana")];
+ char uniname2ctype_pool_str2688[sizeof("phagspa")];
+ char uniname2ctype_pool_str2691[sizeof("devanagari")];
+ char uniname2ctype_pool_str2692[sizeof("tang")];
+ char uniname2ctype_pool_str2694[sizeof("currencysymbol")];
+ char uniname2ctype_pool_str2698[sizeof("tagbanwa")];
+ char uniname2ctype_pool_str2701[sizeof("inenclosedcjklettersandmonths")];
+ char uniname2ctype_pool_str2702[sizeof("tamil")];
+ char uniname2ctype_pool_str2721[sizeof("tirh")];
+ char uniname2ctype_pool_str2723[sizeof("digit")];
+ char uniname2ctype_pool_str2732[sizeof("talu")];
+ char uniname2ctype_pool_str2747[sizeof("zp")];
+ char uniname2ctype_pool_str2750[sizeof("inpaucinhau")];
+ char uniname2ctype_pool_str2760[sizeof("taitham")];
+ char uniname2ctype_pool_str2764[sizeof("otherlowercase")];
+ char uniname2ctype_pool_str2768[sizeof("telu")];
+ char uniname2ctype_pool_str2769[sizeof("inaegeannumbers")];
+ char uniname2ctype_pool_str2777[sizeof("otherletter")];
+ char uniname2ctype_pool_str2780[sizeof("whitespace")];
+ char uniname2ctype_pool_str2793[sizeof("nonspacingmark")];
+ char uniname2ctype_pool_str2816[sizeof("graphemeclusterbreak=spacingmark")];
+ char uniname2ctype_pool_str2821[sizeof("inletterlikesymbols")];
+ char uniname2ctype_pool_str2834[sizeof("intagbanwa")];
+ char uniname2ctype_pool_str2841[sizeof("oldsogdian")];
+ char uniname2ctype_pool_str2848[sizeof("otheridstart")];
+ char uniname2ctype_pool_str2852[sizeof("graphemeclusterbreak=cr")];
+ char uniname2ctype_pool_str2855[sizeof("narb")];
+ char uniname2ctype_pool_str2856[sizeof("changeswhencasemapped")];
+ char uniname2ctype_pool_str2859[sizeof("inbopomofo")];
+ char uniname2ctype_pool_str2862[sizeof("tangut")];
+ char uniname2ctype_pool_str2867[sizeof("graphemeclusterbreak=regionalindicator")];
+ char uniname2ctype_pool_str2871[sizeof("noncharactercodepoint")];
+ char uniname2ctype_pool_str2883[sizeof("otheruppercase")];
+ char uniname2ctype_pool_str2885[sizeof("rjng")];
+ char uniname2ctype_pool_str2886[sizeof("sylotinagri")];
+ char uniname2ctype_pool_str2904[sizeof("inhangulsyllables")];
+ char uniname2ctype_pool_str2905[sizeof("emojipresentation")];
+ char uniname2ctype_pool_str2906[sizeof("inindicsiyaqnumbers")];
+ char uniname2ctype_pool_str2909[sizeof("inbassavah")];
+ char uniname2ctype_pool_str2912[sizeof("ogrext")];
+ char uniname2ctype_pool_str2926[sizeof("othersymbol")];
+ char uniname2ctype_pool_str2938[sizeof("oupper")];
+ char uniname2ctype_pool_str2941[sizeof("inbuhid")];
+ char uniname2ctype_pool_str2963[sizeof("hmnp")];
+ char uniname2ctype_pool_str2964[sizeof("inpsalterpahlavi")];
+ char uniname2ctype_pool_str2967[sizeof("finalpunctuation")];
+ 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("omath")];
+ char uniname2ctype_pool_str3000[sizeof("any")];
+ char uniname2ctype_pool_str3001[sizeof("elba")];
+ char uniname2ctype_pool_str3002[sizeof("changeswhentitlecased")];
+ char uniname2ctype_pool_str3005[sizeof("incombininghalfmarks")];
+ char uniname2ctype_pool_str3006[sizeof("intangutcomponents")];
+ char uniname2ctype_pool_str3012[sizeof("hebr")];
+ char uniname2ctype_pool_str3028[sizeof("deprecated")];
+ char uniname2ctype_pool_str3045[sizeof("inarabicmathematicalalphabeticsymbols")];
+ char uniname2ctype_pool_str3055[sizeof("inprivateusearea")];
+ char uniname2ctype_pool_str3089[sizeof("kayahli")];
+ char uniname2ctype_pool_str3098[sizeof("inplayingcards")];
+ char uniname2ctype_pool_str3099[sizeof("inarabicpresentationformsb")];
+ char uniname2ctype_pool_str3100[sizeof("ogham")];
+ char uniname2ctype_pool_str3101[sizeof("elym")];
+ char uniname2ctype_pool_str3107[sizeof("graphemeclusterbreak=t")];
+ char uniname2ctype_pool_str3109[sizeof("graphemeclusterbreak=lvt")];
+ char uniname2ctype_pool_str3111[sizeof("nbat")];
+ char uniname2ctype_pool_str3125[sizeof("nabataean")];
+ char uniname2ctype_pool_str3126[sizeof("hangul")];
+ char uniname2ctype_pool_str3134[sizeof("elymaic")];
+ char uniname2ctype_pool_str3158[sizeof("inhebrew")];
+ char uniname2ctype_pool_str3165[sizeof("injavanese")];
+ char uniname2ctype_pool_str3169[sizeof("symbol")];
+ char uniname2ctype_pool_str3176[sizeof("inmathematicaloperators")];
+ char uniname2ctype_pool_str3180[sizeof("inarabicsupplement")];
+ char uniname2ctype_pool_str3185[sizeof("cypriot")];
+ char uniname2ctype_pool_str3194[sizeof("hung")];
+ char uniname2ctype_pool_str3205[sizeof("wspace")];
+ char uniname2ctype_pool_str3209[sizeof("changeswhenlowercased")];
+ char uniname2ctype_pool_str3215[sizeof("elbasan")];
+ char uniname2ctype_pool_str3218[sizeof("hluw")];
+ char uniname2ctype_pool_str3237[sizeof("insuperscriptsandsubscripts")];
+ char uniname2ctype_pool_str3239[sizeof("graphemeclusterbreak=extend")];
+ char uniname2ctype_pool_str3240[sizeof("graphemeclusterbreak=prepend")];
+ char uniname2ctype_pool_str3248[sizeof("nshu")];
+ char uniname2ctype_pool_str3254[sizeof("oldnortharabian")];
+ char uniname2ctype_pool_str3266[sizeof("inyijinghexagramsymbols")];
+ char uniname2ctype_pool_str3286[sizeof("hexdigit")];
+ char uniname2ctype_pool_str3297[sizeof("graphemeclusterbreak=l")];
+ char uniname2ctype_pool_str3303[sizeof("graphemeclusterbreak=control")];
+ char uniname2ctype_pool_str3309[sizeof("bassavah")];
+ char uniname2ctype_pool_str3317[sizeof("otherdefaultignorablecodepoint")];
+ char uniname2ctype_pool_str3328[sizeof("changeswhenuppercased")];
+ char uniname2ctype_pool_str3329[sizeof("inalchemicalsymbols")];
+ char uniname2ctype_pool_str3348[sizeof("insupplementalarrowsa")];
+ char uniname2ctype_pool_str3349[sizeof("inyisyllables")];
+ char uniname2ctype_pool_str3351[sizeof("tibt")];
+ char uniname2ctype_pool_str3360[sizeof("othermath")];
+ char uniname2ctype_pool_str3363[sizeof("tibetan")];
+ char uniname2ctype_pool_str3365[sizeof("inmahjongtiles")];
+ char uniname2ctype_pool_str3433[sizeof("signwriting")];
+ char uniname2ctype_pool_str3436[sizeof("nushu")];
+ char uniname2ctype_pool_str3439[sizeof("modifiersymbol")];
+ char uniname2ctype_pool_str3442[sizeof("inhalfwidthandfullwidthforms")];
+ char uniname2ctype_pool_str3458[sizeof("upper")];
+ char uniname2ctype_pool_str3460[sizeof("insupplementalarrowsc")];
+ char uniname2ctype_pool_str3511[sizeof("insupplementalmathematicaloperators")];
+ char uniname2ctype_pool_str3512[sizeof("incypriotsyllabary")];
+ char uniname2ctype_pool_str3517[sizeof("dupl")];
+ char uniname2ctype_pool_str3531[sizeof("tavt")];
+ char uniname2ctype_pool_str3532[sizeof("inpahawhhmong")];
+ char uniname2ctype_pool_str3533[sizeof("alphabetic")];
+ char uniname2ctype_pool_str3550[sizeof("dashpunctuation")];
+ char uniname2ctype_pool_str3558[sizeof("uppercase")];
+ char uniname2ctype_pool_str3613[sizeof("soyombo")];
+ char uniname2ctype_pool_str3614[sizeof("hanifirohingya")];
+ char uniname2ctype_pool_str3616[sizeof("otherpunctuation")];
+ char uniname2ctype_pool_str3628[sizeof("defaultignorablecodepoint")];
+ char uniname2ctype_pool_str3648[sizeof("inhanguljamoextendedb")];
+ char uniname2ctype_pool_str3664[sizeof("aghb")];
+ char uniname2ctype_pool_str3703[sizeof("tifinagh")];
+ char uniname2ctype_pool_str3705[sizeof("inlatinextendedb")];
+ char uniname2ctype_pool_str3714[sizeof("tfng")];
+ char uniname2ctype_pool_str3766[sizeof("inhighprivateusesurrogates")];
+ char uniname2ctype_pool_str3791[sizeof("changeswhencasefolded")];
+ char uniname2ctype_pool_str3805[sizeof("dep")];
+ char uniname2ctype_pool_str3819[sizeof("oldsoutharabian")];
+ char uniname2ctype_pool_str3821[sizeof("graphemeclusterbreak=lf")];
+ char uniname2ctype_pool_str3842[sizeof("pahawhhmong")];
+ char uniname2ctype_pool_str3845[sizeof("unifiedideograph")];
+ char uniname2ctype_pool_str3891[sizeof("uppercaseletter")];
+ char uniname2ctype_pool_str3924[sizeof("insupplementalpunctuation")];
+ char uniname2ctype_pool_str3942[sizeof("ethiopic")];
+ char uniname2ctype_pool_str3976[sizeof("inglagoliticsupplement")];
+ char uniname2ctype_pool_str3995[sizeof("rejang")];
+ char uniname2ctype_pool_str4087[sizeof("inbopomofoextended")];
+ char uniname2ctype_pool_str4109[sizeof("tagb")];
+ char uniname2ctype_pool_str4137[sizeof("othergraphemeextend")];
+ char uniname2ctype_pool_str4162[sizeof("inegyptianhieroglyphs")];
+ char uniname2ctype_pool_str4175[sizeof("inegyptianhieroglyphformatcontrols")];
+ char uniname2ctype_pool_str4203[sizeof("hebrew")];
+ char uniname2ctype_pool_str4254[sizeof("tglg")];
+ char uniname2ctype_pool_str4276[sizeof("tagalog")];
+ char uniname2ctype_pool_str4291[sizeof("graphemeclusterbreak=zwj")];
+ char uniname2ctype_pool_str4321[sizeof("zyyy")];
+ char uniname2ctype_pool_str4360[sizeof("hyphen")];
+ char uniname2ctype_pool_str4397[sizeof("inboxdrawing")];
+ char uniname2ctype_pool_str4405[sizeof("graphemeclusterbreak=v")];
+ char uniname2ctype_pool_str4406[sizeof("graphemeclusterbreak=lv")];
+ char uniname2ctype_pool_str4460[sizeof("telugu")];
+ char uniname2ctype_pool_str4485[sizeof("duployan")];
+ char uniname2ctype_pool_str4528[sizeof("openpunctuation")];
+ char uniname2ctype_pool_str4674[sizeof("insupplementaryprivateuseareaa")];
+ char uniname2ctype_pool_str4683[sizeof("inhighsurrogates")];
+ char uniname2ctype_pool_str4772[sizeof("insupplementalarrowsb")];
+ char uniname2ctype_pool_str4948[sizeof("insupplementalsymbolsandpictographs")];
+ char uniname2ctype_pool_str4955[sizeof("egyp")];
+ char uniname2ctype_pool_str4986[sizeof("inhangulcompatibilityjamo")];
+ char uniname2ctype_pool_str5114[sizeof("nyiakengpuachuehmong")];
+ char uniname2ctype_pool_str5608[sizeof("egyptianhieroglyphs")];
+ char uniname2ctype_pool_str6098[sizeof("insupplementaryprivateuseareab")];
+#endif /* USE_UNICODE_PROPERTIES */
+ };
+static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
+ {
+#ifndef USE_UNICODE_PROPERTIES
+ "word",
+#else /* USE_UNICODE_PROPERTIES */
+ "yi",
+ "yiii",
+ "lana",
+ "z",
+ "lina",
+ "maka",
+ "mani",
+ "mn",
+ "miao",
+ "lo",
+ "ci",
+ "lao",
+ "laoo",
+ "inkannada",
+ "cn",
+ "pi",
+ "innko",
+ "zzzz",
+ "gran",
+ "co",
+ "lineara",
+ "mark",
+ "po",
+ "me",
+ "cari",
+ "inkharoshthi",
+ "kana",
+ "loe",
+ "m",
+ "grek",
+ "mro",
+ "mroo",
+ "carian",
+ "geor",
+ "greek",
+ "gonm",
+ "mendekikakui",
+ "pe",
+ "mero",
+ "inosmanya",
+ "cakm",
+ "inmanichaean",
+ "inmro",
+ "inmiao",
+ "inchakma",
+ "c",
+ "mandaic",
+ "meeteimayek",
+ "inarmenian",
+ "inmyanmar",
+ "inmakasar",
+ "common",
+ "lm",
+ "marc",
+ "inrunic",
+ "incarian",
+ "inideographicsymbolsandpunctuation",
+ "inkhmer",
+ "qaai",
+ "inahom",
+ "merc",
+ "combiningmark",
+ "lc",
+ "perm",
+ "mc",
+ "connectorpunctuation",
+ "cans",
+ "incuneiformnumbersandpunctuation",
+ "armi",
+ "cc",
+ "armn",
+ "incherokee",
+ "prependedconcatenationmark",
+ "incuneiform",
+ "inavestan",
+ "inipaextensions",
+ "pc",
+ "armenian",
+ "insharada",
+ "inmarchen",
+ "makasar",
+ "masaramgondi",
+ "inarrows",
+ "incyrillic",
+ "incham",
+ "qmark",
+ "ri",
+ "qaac",
+ "insamaritan",
+ "latn",
+ "inmasaramgondi",
+ "inthaana",
+ "latin",
+ "inthai",
+ "lineseparator",
+ "pcm",
+ "inkatakana",
+ "inkaithi",
+ "inzanabazarsquare",
+ "inscriptionalparthian",
+ "initialpunctuation",
+ "mtei",
+ "vai",
+ "vaii",
+ "inkhmersymbols",
+ "insyriac",
+ "intakri",
+ "arabic",
+ "zs",
+ "katakana",
+ "prti",
+ "ascii",
+ "cs",
+ "ps",
+ "mand",
+ "privateuse",
+ "inruminumeralsymbols",
+ "inmyanmarextendeda",
+ "modi",
+ "incjkcompatibilityforms",
+ "inkanaextendeda",
+ "incjkcompatibilityideographs",
+ "brai",
+ "mend",
+ "ideo",
+ "letter",
+ "l",
+ "inmeeteimayek",
+ "inideographicdescriptioncharacters",
+ "xidcontinue",
+ "knda",
+ "innandinagari",
+ "kannada",
+ "inmodi",
+ "inlao",
+ "inoldnortharabian",
+ "intransportandmapsymbols",
+ "letternumber",
+ "gothic",
+ "inlineara",
+ "inmendekikakui",
+ "xidc",
+ "mongolian",
+ "inmiscellaneousmathematicalsymbolsa",
+ "inspecials",
+ "grlink",
+ "brahmi",
+ "inemoticons",
+ "kali",
+ "inolditalic",
+ "inmedefaidrin",
+ "inchesssymbols",
+ "incjkcompatibilityideographssupplement",
+ "inadlam",
+ "psalterpahlavi",
+ "incommonindicnumberforms",
+ "lt",
+ "innewa",
+ "sk",
+ "control",
+ "inancientsymbols",
+ "palm",
+ "inlycian",
+ "so",
+ "patternwhitespace",
+ "xids",
+ "inmandaic",
+ "idc",
+ "meroiticcursive",
+ "inwarangciti",
+ "sora",
+ "inopticalcharacterrecognition",
+ "inoldsogdian",
+ "inmalayalam",
+ "bamum",
+ "inkanasupplement",
+ "insundanese",
+ "grext",
+#endif /* USE_UNICODE_PROPERTIES */
+ "print",
+#ifndef USE_UNICODE_PROPERTIES
+ "punct",
+ "alpha",
+#else /* USE_UNICODE_PROPERTIES */
+ "intaitham",
+ "lower",
+ "joinc",
+ "inoldsoutharabian",
+ "incjkstrokes",
+ "batk",
+ "samr",
+ "inwancho",
+ "batak",
+ "vs",
+ "patws",
+ "samaritan",
+ "idsbinaryoperator",
+ "pauc",
+ "insmallkanaextension",
+ "sm",
+ "indominotiles",
+#endif /* USE_UNICODE_PROPERTIES */
+ "alnum",
+#ifdef USE_UNICODE_PROPERTIES
+ "insylotinagri",
+ "inugaritic",
+ "incontrolpictures",
+ "inlinearbideograms",
+ "inmusicalsymbols",
+ "s",
+ "ital",
+ "inmodifiertoneletters",
+ "inancientgreekmusicalnotation",
+ "patternsyntax",
+ "lisu",
+ "lowercase",
+ "cwcm",
+ "sc",
+ "bass",
+ "ids",
+ "inlatinextendeda",
+ "oriya",
+ "intaile",
+ "inmiscellaneoussymbols",
+ "inmiscellaneoussymbolsandarrows",
+ "incaucasianalbanian",
+ "inmiscellaneoussymbolsandpictographs",
+ "inoldturkic",
+ "insaurashtra",
+ "idcontinue",
+ "intamil",
+ "inmultani",
+ "inlatinextendede",
+ "pd",
+ "bali",
+ "blank",
+ "idst",
+ "inlydian",
+ "innewtailue",
+ "bengali",
+ "runr",
+ "zl",
+ "incyrillicextendeda",
+ "ll",
+ "indeseret",
+ "intaixuanjingsymbols",
+ "inancientgreeknumbers",
+ "idstart",
+ "inmeeteimayekextensions",
+ "balinese",
+ "dia",
+ "di",
+ "inspacingmodifierletters",
+ "inearlydynasticcuneiform",
+ "plrd",
+ "canadianaboriginal",
+ "zinh",
+ "sind",
+ "osage",
+ "inlatinextendedc",
+ "uideo",
+ "incountingrodnumerals",
+ "xidstart",
+#endif /* USE_UNICODE_PROPERTIES */
+ "xdigit",
+#ifndef USE_UNICODE_PROPERTIES
+ "upper",
+ "ascii",
+#else /* USE_UNICODE_PROPERTIES */
+ "osma",
+ "inkhudawadi",
+ "inhanifirohingya",
+ "gong",
+ "ingrantha",
+ "bidic",
+ "mong",
+ "cased",
+ "incyrillicextendedc",
+ "inhiragana",
+ "sinhala",
+ "adlm",
+ "glagolitic",
+ "sterm",
+ "bamu",
+ "georgian",
+ "inosage",
+ "gunjalagondi",
+ "phoenician",
+ "multani",
+ "kaithi",
+ "joincontrol",
+ "runic",
+ "ingeneralpunctuation",
+ "inmahajani",
+ "incyrillicsupplement",
+ "lowercaseletter",
+ "marchen",
+ "graphemelink",
+ "ingeorgian",
+ "khojki",
+ "cham",
+ "inogham",
+ "cher",
+ "chakma",
+ "emoji",
+ "insiddham",
+ "cherokee",
+ "khar",
+ "inmongolian",
+ "incherokeesupplement",
+ "diacritic",
+ "manichaean",
+ "xsux",
+ "inolchiki",
+ "quotationmark",
+ "adlam",
+ "inethiopic",
+ "graphemebase",
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ "age=11.0",
+ "age=12.1",
+ "age=10.0",
+ "age=12.0",
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ "casedletter",
+ "ingurmukhi",
+ "odi",
+ "incjkunifiedideographsextensiona",
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ "age=1.1",
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ "lu",
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ "age=4.1",
+ "age=2.1",
+ "age=4.0",
+ "age=2.0",
+ "age=9.0",
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ "intamilsupplement",
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ "age=6.1",
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ "unknown",
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ "age=6.0",
+ "age=6.2",
+ "age=3.1",
+ "age=8.0",
+ "age=3.0",
+ "age=3.2",
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ "cwt",
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ "age=7.0",
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ "unassigned",
+#ifdef USE_UNICODE_AGE_PROPERTIES
+ "age=6.3",
+ "age=5.1",
+ "age=5.0",
+ "age=5.2",
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ "ahom",
+ "incjkunifiedideographsextensione",
+ "khmr",
+ "insinhala",
+ "inmiscellaneoustechnical",
+ "saur",
+ "guru",
+ "sundanese",
+ "punct",
+ "paucinhau",
+ "gurmukhi",
+ "variationselector",
+ "logicalorderexception",
+ "khmer",
+ "limbu",
+ "inscriptionalpahlavi",
+ "oidc",
+ "incjkunifiedideographsextensionc",
+#endif /* USE_UNICODE_PROPERTIES */
+ "cntrl",
+#ifdef USE_UNICODE_PROPERTIES
+ "inlatinextendedadditional",
+ "decimalnumber",
+ "insorasompeng",
+ "radical",
+ "emojimodifier",
+ "kharoshthi",
+ "n",
+ "math",
+ "goth",
+ "anatolianhieroglyphs",
+ "inenclosedalphanumerics",
+ "nandinagari",
+ "no",
+ "nko",
+ "nkoo",
+ "ingreekandcoptic",
+ "olck",
+ "p",
+ "grantha",
+ "olchiki",
+ "incjkunifiedideographs",
+ "zanb",
+ "intirhuta",
+ "oids",
+ "inhatran",
+ "linb",
+ "xpeo",
+ "mult",
+ "saurashtra",
+ "kthi",
+ "inbhaiksuki",
+ "olower",
+ "innabataean",
+ "inphoenician",
+ "inkanbun",
+ "inmeroitichieroglyphs",
+ "inkayahli",
+ "phnx",
+ "inoriya",
+ "enclosingmark",
+ "sd",
+ "inelbasan",
+ "wara",
+ "inenclosedideographicsupplement",
+ "sidd",
+ "linearb",
+ "hani",
+ "han",
+ "inenclosedalphanumericsupplement",
+ "medf",
+ "bidicontrol",
+ "hano",
+ "inphaistosdisc",
+ "limb",
+ "inkangxiradicals",
+ "lepc",
+ "medefaidrin",
+ "braille",
+ "regionalindicator",
+ "inlowsurrogates",
+ "inshorthandformatcontrols",
+ "brah",
+ "inkhojki",
+ "inoldhungarian",
+ "hanunoo",
+ "hira",
+ "beng",
+ "emojimodifierbase",
+ "inarabic",
+ "lyci",
+ "ahex",
+ "inherited",
+ "glag",
+ "lycian",
+ "indogra",
+ "dsrt",
+ "arab",
+ "mymr",
+ "myanmar",
+ "phli",
+ "inimperialaramaic",
+ "ingreekextended",
+ "inanatolianhieroglyphs",
+ "punctuation",
+ "takri",
+ "graphemeextend",
+ "invai",
+ "cwl",
+ "ingeometricshapes",
+ "emojicomponent",
+ "coptic",
+ "deseret",
+ "inarabicpresentationformsa",
+ "takr",
+ "inbasiclatin",
+ "incjkunifiedideographsextensiond",
+ "sinh",
+ "sund",
+ "shavian",
+ "taile",
+ "insundanesesupplement",
+ "inelymaic",
+ "insoyombo",
+ "bhks",
+ "bhaiksuki",
+ "incjkcompatibility",
+ "inhanunoo",
+ "intangut",
+ "sogdian",
+ "inlatinextendedd",
+ "sogo",
+ "insinhalaarchaicnumbers",
+ "ideographic",
+ "ugar",
+ "copt",
+ "imperialaramaic",
+ "insogdian",
+ "indingbats",
+ "format",
+ "ininscriptionalpahlavi",
+ "ininscriptionalparthian",
+ "grbase",
+ "inbatak",
+ "cprt",
+ "cwcf",
+ "cuneiform",
+ "term",
+ "intibetan",
+ "intags",
+ "asciihexdigit",
+ "sentenceterminal",
+ "inmayannumerals",
+ "nand",
+ "patsyn",
+ "hatran",
+ "inblockelements",
+ "inornamentaldingbats",
+ "innumberforms",
+ "oldpersian",
+ "inshavian",
+ "bopo",
+ "hatr",
+ "caseignorable",
+ "inoldpersian",
+ "modifierletter",
+ "cwu",
+ "lydi",
+ "inbyzantinemusicalsymbols",
+ "ingeometricshapesextended",
+ "inmyanmarextendedb",
+ "innushu",
+ "lydian",
+ "inunifiedcanadianaboriginalsyllabics",
+ "orkh",
+ "inyiradicals",
+ "inkatakanaphoneticextensions",
+ "inethiopicextendeda",
+ "incoptic",
+ "inarabicextendeda",
+ "oldpermic",
+ "incjksymbolsandpunctuation",
+ "word",
+ "bopomofo",
+ "ogam",
+ "inlisu",
+ "inoldpermic",
+ "innoblock",
+ "taiviet",
+ "inbraillepatterns",
+ "alpha",
+ "inbalinese",
+ "sorasompeng",
+ "closepunctuation",
+ "inmiscellaneousmathematicalsymbolsb",
+ "inlepcha",
+ "insyriacsupplement",
+ "newa",
+ "spacingmark",
+ "inpalmyrene",
+ "cyrl",
+ "assigned",
+ "mlym",
+ "malayalam",
+ "ext",
+ "newtailue",
+#endif /* USE_UNICODE_PROPERTIES */
+ "space",
+#ifdef USE_UNICODE_PROPERTIES
+ "intelugu",
+ "idsb",
+ "indevanagari",
+ "avestan",
+ "cf",
+ "palmyrene",
+ "inethiopicsupplement",
+ "soyo",
+#endif /* USE_UNICODE_PROPERTIES */
+ "xposixpunct",
+#ifndef USE_UNICODE_PROPERTIES
+ "lower",
+#else /* USE_UNICODE_PROPERTIES */
+ "pf",
+ "sarb",
+ "zanabazarsquare",
+ "ugaritic",
+ "osge",
+ "java",
+ "sharada",
+ "dogra",
+ "bugi",
+ "meroitichieroglyphs",
+ "separator",
+ "ingeorgiansupplement",
+ "sogd",
+ "tale",
+ "inunifiedcanadianaboriginalsyllabicsextended",
+ "terminalpunctuation",
+ "shrd",
+#endif /* USE_UNICODE_PROPERTIES */
+ "graph",
+#ifdef USE_UNICODE_PROPERTIES
+ "olditalic",
+ "dogr",
+ "gujr",
+ "phag",
+ "gujarati",
+ "inhanguljamo",
+ "javanese",
+ "taml",
+ "inphoneticextensions",
+ "siddham",
+ "buginese",
+ "inmongoliansupplement",
+ "invariationselectors",
+ "inhanguljamoextendeda",
+ "inverticalforms",
+ "syrc",
+ "number",
+ "incopticepactnumbers",
+ "avst",
+ "inbamum",
+ "nd",
+ "insuttonsignwriting",
+ "extender",
+ "intaiviet",
+ "hex",
+ "incjkunifiedideographsextensionf",
+ "other",
+ "otheridcontinue",
+ "shaw",
+ "dash",
+ "othernumber",
+ "orya",
+ "invedicextensions",
+ "sgnw",
+ "caucasianalbanian",
+ "inmathematicalalphanumericsymbols",
+ "inphoneticextensionssupplement",
+ "invariationselectorssupplement",
+ "induployan",
+ "syriac",
+ "oalpha",
+ "innyiakengpuachuehmong",
+ "incombiningdiacriticalmarks",
+ "inethiopicextended",
+ "nl",
+ "incombiningdiacriticalmarksforsymbols",
+ "khudawadi",
+ "otheralphabetic",
+ "oldhungarian",
+ "incurrencysymbols",
+ "incjkradicalssupplement",
+ "inglagolitic",
+ "intifinagh",
+ "ingeorgianextended",
+ "surrogate",
+ "incyrillicextendedb",
+ "ethi",
+ "titlecaseletter",
+ "rohg",
+ "inmeroiticcursive",
+ "idstrinaryoperator",
+ "inphagspa",
+ "lepcha",
+ "intagalog",
+ "mathsymbol",
+ "incombiningdiacriticalmarkssupplement",
+ "inbrahmi",
+ "insymbolsandpictographsextendeda",
+ "inlinearbsyllabary",
+ "oldturkic",
+ "inbengali",
+ "wancho",
+ "osmanya",
+ "buhd",
+ "insmallformvariants",
+ "indevanagariextended",
+ "softdotted",
+ "inbuginese",
+ "mahj",
+ "inlatin1supplement",
+ "ingothic",
+ "mahajani",
+ "hang",
+ "sylo",
+ "warangciti",
+ "ingujarati",
+ "tirhuta",
+ "incombiningdiacriticalmarksextended",
+ "spaceseparator",
+ "ingunjalagondi",
+ "wcho",
+ "hiragana",
+ "extendedpictographic",
+ "inrejang",
+ "inottomansiyaqnumbers",
+ "nchar",
+ "cyrillic",
+ "khoj",
+ "inlimbu",
+ "hmng",
+ "thaa",
+ "thai",
+ "incjkunifiedideographsextensionb",
+ "deva",
+ "thaana",
+ "phagspa",
+ "devanagari",
+ "tang",
+ "currencysymbol",
+ "tagbanwa",
+ "inenclosedcjklettersandmonths",
+ "tamil",
+ "tirh",
+#endif /* USE_UNICODE_PROPERTIES */
+ "digit",
+#ifndef USE_UNICODE_PROPERTIES
+ "blank"
+#else /* USE_UNICODE_PROPERTIES */
+ "talu",
+ "zp",
+ "inpaucinhau",
+ "taitham",
+ "otherlowercase",
+ "telu",
+ "inaegeannumbers",
+ "otherletter",
+ "whitespace",
+ "nonspacingmark",
+ "graphemeclusterbreak=spacingmark",
+ "inletterlikesymbols",
+ "intagbanwa",
+ "oldsogdian",
+ "otheridstart",
+ "graphemeclusterbreak=cr",
+ "narb",
+ "changeswhencasemapped",
+ "inbopomofo",
+ "tangut",
+ "graphemeclusterbreak=regionalindicator",
+ "noncharactercodepoint",
+ "otheruppercase",
+ "rjng",
+ "sylotinagri",
+ "inhangulsyllables",
+ "emojipresentation",
+ "inindicsiyaqnumbers",
+ "inbassavah",
+ "ogrext",
+ "othersymbol",
+ "oupper",
+ "inbuhid",
+ "hmnp",
+ "inpsalterpahlavi",
+ "finalpunctuation",
+ "phlp",
+ "inbamumsupplement",
+ "buhid",
+ "paragraphseparator",
+ "inalphabeticpresentationforms",
+ "omath",
+ "any",
+ "elba",
+ "changeswhentitlecased",
+ "incombininghalfmarks",
+ "intangutcomponents",
+ "hebr",
+ "deprecated",
+ "inarabicmathematicalalphabeticsymbols",
+ "inprivateusearea",
+ "kayahli",
+ "inplayingcards",
+ "inarabicpresentationformsb",
+ "ogham",
+ "elym",
+ "graphemeclusterbreak=t",
+ "graphemeclusterbreak=lvt",
+ "nbat",
+ "nabataean",
+ "hangul",
+ "elymaic",
+ "inhebrew",
+ "injavanese",
+ "symbol",
+ "inmathematicaloperators",
+ "inarabicsupplement",
+ "cypriot",
+ "hung",
+ "wspace",
+ "changeswhenlowercased",
+ "elbasan",
+ "hluw",
+ "insuperscriptsandsubscripts",
+ "graphemeclusterbreak=extend",
+ "graphemeclusterbreak=prepend",
+ "nshu",
+ "oldnortharabian",
+ "inyijinghexagramsymbols",
+ "hexdigit",
+ "graphemeclusterbreak=l",
+ "graphemeclusterbreak=control",
+ "bassavah",
+ "otherdefaultignorablecodepoint",
+ "changeswhenuppercased",
+ "inalchemicalsymbols",
+ "insupplementalarrowsa",
+ "inyisyllables",
+ "tibt",
+ "othermath",
+ "tibetan",
+ "inmahjongtiles",
+ "signwriting",
+ "nushu",
+ "modifiersymbol",
+ "inhalfwidthandfullwidthforms",
+ "upper",
+ "insupplementalarrowsc",
+ "insupplementalmathematicaloperators",
+ "incypriotsyllabary",
+ "dupl",
+ "tavt",
+ "inpahawhhmong",
+ "alphabetic",
+ "dashpunctuation",
+ "uppercase",
+ "soyombo",
+ "hanifirohingya",
+ "otherpunctuation",
+ "defaultignorablecodepoint",
+ "inhanguljamoextendedb",
+ "aghb",
+ "tifinagh",
+ "inlatinextendedb",
+ "tfng",
+ "inhighprivateusesurrogates",
+ "changeswhencasefolded",
+ "dep",
+ "oldsoutharabian",
+ "graphemeclusterbreak=lf",
+ "pahawhhmong",
+ "unifiedideograph",
+ "uppercaseletter",
+ "insupplementalpunctuation",
+ "ethiopic",
+ "inglagoliticsupplement",
+ "rejang",
+ "inbopomofoextended",
+ "tagb",
+ "othergraphemeextend",
+ "inegyptianhieroglyphs",
+ "inegyptianhieroglyphformatcontrols",
+ "hebrew",
+ "tglg",
+ "tagalog",
+ "graphemeclusterbreak=zwj",
+ "zyyy",
+ "hyphen",
+ "inboxdrawing",
+ "graphemeclusterbreak=v",
+ "graphemeclusterbreak=lv",
+ "telugu",
+ "duployan",
+ "openpunctuation",
+ "insupplementaryprivateuseareaa",
+ "inhighsurrogates",
+ "insupplementalarrowsb",
+ "insupplementalsymbolsandpictographs",
+ "egyp",
+ "inhangulcompatibilityjamo",
+ "nyiakengpuachuehmong",
+ "egyptianhieroglyphs",
+ "insupplementaryprivateuseareab"
+#endif /* USE_UNICODE_PROPERTIES */
+ };
+#define uniname2ctype_pool ((const char *) &uniname2ctype_pool_contents)
+const struct uniname2ctype_struct *
+uniname2ctype_p (register const char *str, register size_t len)
+{
+ static const struct uniname2ctype_struct wordlist[] =
+ {
+#ifdef USE_UNICODE_PROPERTIES
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1},
+ {uniname2ctype_offset(str11), 111},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str17), 111},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str22), 152},
+ {-1},
+ {uniname2ctype_offset(str24), 52},
+ {uniname2ctype_offset(str25), 184},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str33), 218},
+ {-1},
+ {uniname2ctype_offset(str35), 186},
+ {uniname2ctype_offset(str36), 34},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str45), 173},
+ {uniname2ctype_offset(str46), 28},
+ {uniname2ctype_offset(str47), 61},
+ {uniname2ctype_offset(str48), 95},
+ {uniname2ctype_offset(str49), 95},
+ {-1}, {-1},
+ {uniname2ctype_offset(str52), 331},
+ {-1}, {-1},
+ {uniname2ctype_offset(str55), 21},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str64), 44},
+ {-1},
+ {uniname2ctype_offset(str66), 319},
+ {uniname2ctype_offset(str67), 267},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str71), 181},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str75), 22},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str83), 184},
+ {-1}, {-1},
+ {uniname2ctype_offset(str86), 31},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str92), 45},
+ {-1},
+ {uniname2ctype_offset(str94), 33},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str100), 149},
+ {uniname2ctype_offset(str101), 496},
+ {uniname2ctype_offset(str102), 108},
+ {uniname2ctype_offset(str103), 252},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str107), 31},
+ {uniname2ctype_offset(str108), 77},
+ {-1}, {-1},
+ {uniname2ctype_offset(str111), 189},
+ {uniname2ctype_offset(str112), 189},
+ {-1}, {-1},
+ {uniname2ctype_offset(str115), 149},
+ {-1},
+ {uniname2ctype_offset(str117), 98},
+ {uniname2ctype_offset(str118), 77},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str122), 212},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str129), 187},
+ {uniname2ctype_offset(str130), 42},
+ {uniname2ctype_offset(str131), 172},
+ {-1}, {-1},
+ {uniname2ctype_offset(str134), 482},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str139), 170},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str145), 499},
+ {uniname2ctype_offset(str146), 548},
+ {-1},
+ {uniname2ctype_offset(str148), 552},
+ {uniname2ctype_offset(str149), 514},
+ {-1},
+ {uniname2ctype_offset(str151), 18},
+ {uniname2ctype_offset(str152), 169},
+ {uniname2ctype_offset(str153), 160},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str161), 313},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str177), 337},
+ {uniname2ctype_offset(str178), 539},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str183), 75},
+ {-1}, {-1},
+ {uniname2ctype_offset(str186), 27},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str190), 208},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str203), 345},
+ {uniname2ctype_offset(str204), 473},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str210), 553},
+ {-1},
+ {uniname2ctype_offset(str212), 350},
+ {uniname2ctype_offset(str213), 115},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str218), 528},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str226), 171},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str231), 31},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str236), 25},
+ {uniname2ctype_offset(str237), 194},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str246), 32},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str250), 40},
+ {-1}, {-1},
+ {uniname2ctype_offset(str253), 102},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str260), 542},
+ {-1}, {-1},
+ {uniname2ctype_offset(str263), 161},
+ {-1},
+ {uniname2ctype_offset(str265), 19},
+ {-1},
+ {uniname2ctype_offset(str267), 79},
+ {uniname2ctype_offset(str268), 342},
+ {-1},
+ {uniname2ctype_offset(str270), 259},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str274), 541},
+ {uniname2ctype_offset(str275), 500},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str281), 307},
+ {uniname2ctype_offset(str282), 40},
+ {uniname2ctype_offset(str283), 79},
+ {-1},
+ {uniname2ctype_offset(str285), 516},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str289), 536},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str293), 218},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str297), 212},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str301), 380},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str311), 311},
+ {-1},
+ {uniname2ctype_offset(str313), 441},
+ {-1},
+ {uniname2ctype_offset(str315), 232},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str320), 260},
+ {-1},
+ {uniname2ctype_offset(str322), 129},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str328), 320},
+ {-1}, {-1},
+ {uniname2ctype_offset(str331), 76},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str335), 537},
+ {-1}, {-1},
+ {uniname2ctype_offset(str338), 318},
+ {-1},
+ {uniname2ctype_offset(str340), 76},
+ {-1},
+ {uniname2ctype_offset(str342), 334},
+ {-1}, {-1},
+ {uniname2ctype_offset(str345), 53},
+ {uniname2ctype_offset(str346), 259},
+ {-1},
+ {uniname2ctype_offset(str348), 411},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str352), 512},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str357), 532},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str362), 163},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str366), 44},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str373), 160},
+ {-1}, {-1},
+ {uniname2ctype_offset(str376), 144},
+ {uniname2ctype_offset(str377), 144},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str386), 356},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str399), 316},
+ {-1},
+ {uniname2ctype_offset(str401), 527},
+ {-1}, {-1},
+ {uniname2ctype_offset(str404), 81},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str411), 55},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str418), 108},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str426), 163},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str442), 14},
+ {-1}, {-1},
+ {uniname2ctype_offset(str445), 23},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str462), 46},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str468), 169},
+ {-1},
+ {uniname2ctype_offset(str470), 22},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str475), 507},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str480), 442},
+ {uniname2ctype_offset(str481), 188},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str486), 461},
+ {-1},
+ {uniname2ctype_offset(str488), 557},
+ {-1}, {-1},
+ {uniname2ctype_offset(str491), 455},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str500), 127},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str504), 187},
+ {uniname2ctype_offset(str505), 238},
+ {uniname2ctype_offset(str506), 24},
+ {-1}, {-1},
+ {uniname2ctype_offset(str509), 24},
+ {-1},
+ {uniname2ctype_offset(str511), 448},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str520), 408},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str535), 70},
+ {-1}, {-1},
+ {uniname2ctype_offset(str538), 91},
+ {-1}, {-1},
+ {uniname2ctype_offset(str541), 531},
+ {-1},
+ {uniname2ctype_offset(str543), 91},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str556), 525},
+ {-1},
+ {uniname2ctype_offset(str558), 335},
+ {-1},
+ {uniname2ctype_offset(str560), 498},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str565), 586},
+ {uniname2ctype_offset(str566), 37},
+ {-1},
+ {uniname2ctype_offset(str568), 113},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str572), 486},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str577), 573},
+ {uniname2ctype_offset(str578), 70},
+ {uniname2ctype_offset(str579), 106},
+ {-1}, {-1},
+ {uniname2ctype_offset(str582), 391},
+ {uniname2ctype_offset(str583), 465},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str590), 74},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str594), 168},
+ {-1},
+ {uniname2ctype_offset(str596), 584},
+ {uniname2ctype_offset(str597), 146},
+ {-1}, {-1},
+ {uniname2ctype_offset(str600), 475},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str604), 551},
+ {uniname2ctype_offset(str605), 591},
+ {-1}, {-1},
+ {uniname2ctype_offset(str608), 598},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str614), 574},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str624), 195},
+ {uniname2ctype_offset(str625), 432},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str630), 29},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str636), 522},
+ {-1}, {-1},
+ {uniname2ctype_offset(str639), 49},
+ {-1}, {-1},
+ {uniname2ctype_offset(str642), 19},
+ {-1}, {-1},
+ {uniname2ctype_offset(str645), 470},
+ {-1},
+ {uniname2ctype_offset(str647), 192},
+ {-1}, {-1},
+ {uniname2ctype_offset(str650), 472},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str657), 51},
+ {-1}, {-1},
+ {uniname2ctype_offset(str660), 257},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str668), 69},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str672), 321},
+ {-1}, {-1},
+ {uniname2ctype_offset(str675), 68},
+ {-1}, {-1},
+ {uniname2ctype_offset(str678), 171},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str695), 530},
+ {uniname2ctype_offset(str696), 175},
+ {uniname2ctype_offset(str697), 384},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str703), 508},
+ {-1},
+ {uniname2ctype_offset(str705), 332},
+ {-1},
+ {uniname2ctype_offset(str707), 158},
+ {uniname2ctype_offset(str708), 556},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str713), 361},
+ {-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), 358},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str742), 6},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str753), 229},
+ {-1},
+ {uniname2ctype_offset(str755), 497},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str760), 416},
+ {uniname2ctype_offset(str761), 167},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str766), 156},
+ {uniname2ctype_offset(str767), 572},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str771), 167},
+ {uniname2ctype_offset(str772), 256},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str776), 257},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str783), 156},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str787), 245},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str791), 193},
+ {-1}, {-1},
+ {uniname2ctype_offset(str794), 558},
+ {-1}, {-1},
+ {uniname2ctype_offset(str797), 50},
+ {-1},
+ {uniname2ctype_offset(str799), 579},
+ {-1}, {-1},
+ {uniname2ctype_offset(str802), 13},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str809), 431},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str814), 478},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str818), 383},
+ {-1}, {-1},
+ {uniname2ctype_offset(str821), 467},
+ {uniname2ctype_offset(str822), 563},
+ {uniname2ctype_offset(str823), 47},
+ {uniname2ctype_offset(str824), 112},
+ {uniname2ctype_offset(str825), 429},
+ {-1}, {-1},
+ {uniname2ctype_offset(str828), 564},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str834), 258},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str838), 157},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str842), 58},
+ {-1}, {-1},
+ {uniname2ctype_offset(str845), 66},
+ {-1},
+ {uniname2ctype_offset(str847), 48},
+ {uniname2ctype_offset(str848), 178},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str855), 67},
+ {-1},
+ {uniname2ctype_offset(str857), 305},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str862), 88},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str875), 354},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str886), 389},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str895), 397},
+ {-1}, {-1},
+ {uniname2ctype_offset(str898), 485},
+ {-1},
+ {uniname2ctype_offset(str900), 583},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str906), 504},
+ {uniname2ctype_offset(str907), 434},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str924), 68},
+ {-1},
+ {uniname2ctype_offset(str926), 329},
+ {-1},
+ {uniname2ctype_offset(str928), 519},
+ {uniname2ctype_offset(str929), 446},
+ {uniname2ctype_offset(str930), 41},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str946), 136},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str961), 2},
+ {-1},
+ {uniname2ctype_offset(str963), 246},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str974), 493},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1},
+ {uniname2ctype_offset(str986), 355},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str994), 85},
+ {uniname2ctype_offset(str995), 104},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1005), 53},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1009), 404},
+ {uniname2ctype_offset(str1010), 26},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1013), 480},
+ {uniname2ctype_offset(str1014), 566},
+ {uniname2ctype_offset(str1015), 469},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1021), 67},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1025), 444},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1028), 136},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1032), 239},
+ {uniname2ctype_offset(str1033), 71},
+ {-1},
+ {uniname2ctype_offset(str1035), 308},
+ {uniname2ctype_offset(str1036), 543},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1049), 173},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1067), 102},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1070), 115},
+ {-1},
+ {uniname2ctype_offset(str1072), 197},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1080), 210},
+ {uniname2ctype_offset(str1081), 399},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1085), 248},
+ {-1},
+ {uniname2ctype_offset(str1087), 567},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1090), 69},
+ {uniname2ctype_offset(str1091), 11},
+ {-1},
+ {uniname2ctype_offset(str1093), 125},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1097), 520},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1102), 506},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1105), 217},
+ {-1},
+ {uniname2ctype_offset(str1107), 521},
+ {-1},
+ {uniname2ctype_offset(str1109), 228},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1119), 106},
+ {uniname2ctype_offset(str1120), 60},
+ {uniname2ctype_offset(str1121), 365},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1134), 410},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1140), 93},
+ {-1},
+ {uniname2ctype_offset(str1142), 206},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1146), 131},
+ {uniname2ctype_offset(str1147), 255},
+ {-1},
+ {uniname2ctype_offset(str1149), 158},
+ {uniname2ctype_offset(str1150), 98},
+ {uniname2ctype_offset(str1151), 483},
+ {uniname2ctype_offset(str1152), 217},
+ {uniname2ctype_offset(str1153), 138},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1157), 203},
+ {uniname2ctype_offset(str1158), 166},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1164), 229},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1168), 104},
+ {-1},
+ {uniname2ctype_offset(str1170), 374},
+ {uniname2ctype_offset(str1171), 515},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1174), 312},
+ {uniname2ctype_offset(str1175), 26},
+ {uniname2ctype_offset(str1176), 208},
+ {uniname2ctype_offset(str1177), 74},
+ {uniname2ctype_offset(str1178), 338},
+ {-1},
+ {uniname2ctype_offset(str1180), 183},
+ {uniname2ctype_offset(str1181), 151},
+ {uniname2ctype_offset(str1182), 344},
+ {uniname2ctype_offset(str1183), 101},
+ {-1},
+ {uniname2ctype_offset(str1185), 170},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1190), 261},
+ {uniname2ctype_offset(str1191), 524},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1197), 101},
+ {uniname2ctype_offset(str1198), 135},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1203), 351},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1207), 447},
+ {uniname2ctype_offset(str1208), 239},
+ {uniname2ctype_offset(str1209), 186},
+ {uniname2ctype_offset(str1210), 137},
+ {-1},
+ {uniname2ctype_offset(str1212), 364},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1227), 232},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1231), 206},
+ {uniname2ctype_offset(str1232), 340},
+ {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), 287},
+ {uniname2ctype_offset(str1235), 289},
+ {uniname2ctype_offset(str1236), 286},
+ {uniname2ctype_offset(str1237), 288},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ {uniname2ctype_offset(str1243), 25},
+ {uniname2ctype_offset(str1244), 326},
+ {uniname2ctype_offset(str1245), 249},
+ {uniname2ctype_offset(str1246), 420},
+#ifndef USE_UNICODE_AGE_PROPERTIES
+ {-1},
+#else /* USE_UNICODE_AGE_PROPERTIES */
+ {uniname2ctype_offset(str1247), 268},
+#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), 275},
+ {uniname2ctype_offset(str1250), 270},
+ {uniname2ctype_offset(str1251), 274},
+ {uniname2ctype_offset(str1252), 269},
+ {uniname2ctype_offset(str1253), 285},
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ {uniname2ctype_offset(str1254), 540},
+#ifndef USE_UNICODE_AGE_PROPERTIES
+ {-1},
+#else /* USE_UNICODE_AGE_PROPERTIES */
+ {uniname2ctype_offset(str1255), 280},
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ {uniname2ctype_offset(str1256), 267},
+#ifndef USE_UNICODE_AGE_PROPERTIES
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+#else /* USE_UNICODE_AGE_PROPERTIES */
+ {uniname2ctype_offset(str1257), 279},
+ {uniname2ctype_offset(str1258), 281},
+ {uniname2ctype_offset(str1259), 272},
+ {uniname2ctype_offset(str1260), 284},
+ {uniname2ctype_offset(str1261), 271},
+ {uniname2ctype_offset(str1262), 273},
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ {uniname2ctype_offset(str1263), 64},
+#ifndef USE_UNICODE_AGE_PROPERTIES
+ {-1}, {-1},
+#else /* USE_UNICODE_AGE_PROPERTIES */
+ {uniname2ctype_offset(str1264), 283},
+ {-1},
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ {uniname2ctype_offset(str1266), 21},
+#ifndef USE_UNICODE_AGE_PROPERTIES
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+#else /* USE_UNICODE_AGE_PROPERTIES */
+ {uniname2ctype_offset(str1267), 282},
+ {uniname2ctype_offset(str1268), 277},
+ {-1},
+ {uniname2ctype_offset(str1270), 276},
+ {uniname2ctype_offset(str1271), 278},
+ {-1}, {-1},
+#endif /* USE_UNICODE_AGE_PROPERTIES */
+ {uniname2ctype_offset(str1274), 200},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1282), 596},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1285), 105},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1289), 333},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1292), 382},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1297), 145},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1300), 86},
+ {uniname2ctype_offset(str1301), 141},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1306), 15},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1314), 193},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1317), 86},
+ {-1},
+ {uniname2ctype_offset(str1319), 256},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1331), 252},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1340), 105},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1343), 120},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str1354), 164},
+ {uniname2ctype_offset(str1355), 254},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1358), 594},
+ {-1},
+ {uniname2ctype_offset(str1360), 3},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1365), 372},
+ {uniname2ctype_offset(str1366), 36},
+ {uniname2ctype_offset(str1367), 513},
+ {-1},
+ {uniname2ctype_offset(str1369), 247},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1373), 263},
+ {-1},
+ {uniname2ctype_offset(str1375), 135},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1380), 35},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1384), 56},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1387), 113},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1400), 201},
+ {uniname2ctype_offset(str1401), 385},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1407), 224},
+ {-1},
+ {uniname2ctype_offset(str1409), 38},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1419), 140},
+ {uniname2ctype_offset(str1420), 140},
+ {-1},
+ {uniname2ctype_offset(str1422), 310},
+ {uniname2ctype_offset(str1423), 143},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1426), 39},
+ {-1},
+ {uniname2ctype_offset(str1428), 181},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1434), 143},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1438), 422},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1441), 215},
+ {uniname2ctype_offset(str1442), 523},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1445), 253},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1448), 491},
+ {uniname2ctype_offset(str1449), 122},
+ {uniname2ctype_offset(str1450), 134},
+ {uniname2ctype_offset(str1451), 203},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1454), 145},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1457), 166},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1462), 535},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1466), 241},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1470), 490},
+ {uniname2ctype_offset(str1471), 492},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1475), 414},
+ {uniname2ctype_offset(str1476), 494},
+ {-1},
+ {uniname2ctype_offset(str1478), 436},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1481), 138},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1485), 328},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1489), 33},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1495), 251},
+ {-1},
+ {uniname2ctype_offset(str1497), 484},
+ {uniname2ctype_offset(str1498), 199},
+ {uniname2ctype_offset(str1499), 582},
+ {-1},
+ {uniname2ctype_offset(str1501), 196},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1507), 122},
+ {-1},
+ {uniname2ctype_offset(str1509), 110},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1512), 110},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1517), 581},
+ {-1},
+ {uniname2ctype_offset(str1519), 219},
+ {uniname2ctype_offset(str1520), 228},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1523), 117},
+ {uniname2ctype_offset(str1524), 471},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1529), 120},
+ {-1},
+ {uniname2ctype_offset(str1531), 407},
+ {-1},
+ {uniname2ctype_offset(str1533), 142},
+ {-1},
+ {uniname2ctype_offset(str1535), 219},
+ {uniname2ctype_offset(str1536), 127},
+ {uniname2ctype_offset(str1537), 260},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1542), 453},
+ {-1},
+ {uniname2ctype_offset(str1544), 561},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1547), 168},
+ {uniname2ctype_offset(str1548), 518},
+ {uniname2ctype_offset(str1549), 505},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1552), 117},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1555), 107},
+ {-1},
+ {uniname2ctype_offset(str1557), 85},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1563), 264},
+ {-1},
+ {uniname2ctype_offset(str1565), 315},
+ {-1},
+ {uniname2ctype_offset(str1567), 148},
+ {-1},
+ {uniname2ctype_offset(str1569), 236},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1572), 115},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1580), 131},
+ {-1},
+ {uniname2ctype_offset(str1582), 148},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1587), 529},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1594), 114},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1597), 81},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1602), 97},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1607), 97},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1613), 164},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1617), 488},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1622), 373},
+ {uniname2ctype_offset(str1623), 546},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1629), 39},
+ {-1},
+ {uniname2ctype_offset(str1631), 176},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1635), 72},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1638), 426},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1643), 62},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str1654), 388},
+ {uniname2ctype_offset(str1655), 265},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1662), 129},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1671), 114},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1675), 457},
+ {uniname2ctype_offset(str1676), 176},
+ {uniname2ctype_offset(str1677), 303},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1682), 595},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1686), 93},
+ {uniname2ctype_offset(str1687), 141},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1691), 124},
+ {uniname2ctype_offset(str1692), 121},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1699), 367},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1702), 510},
+ {uniname2ctype_offset(str1703), 533},
+ {uniname2ctype_offset(str1704), 207},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1714), 207},
+ {-1},
+ {uniname2ctype_offset(str1716), 419},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1722), 347},
+ {-1},
+ {uniname2ctype_offset(str1724), 554},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1728), 221},
+ {uniname2ctype_offset(str1729), 430},
+ {uniname2ctype_offset(str1730), 222},
+ {uniname2ctype_offset(str1731), 517},
+ {uniname2ctype_offset(str1732), 238},
+ {uniname2ctype_offset(str1733), 123},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1740), 129},
+ {-1},
+ {uniname2ctype_offset(str1742), 161},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1745), 509},
+ {uniname2ctype_offset(str1746), 390},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1750), 20},
+ {-1},
+ {uniname2ctype_offset(str1752), 502},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1757), 501},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1766), 73},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1769), 362},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1776), 126},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1780), 65},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1788), 137},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1791), 233},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1806), 336},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1810), 599},
+ {uniname2ctype_offset(str1811), 236},
+ {-1},
+ {uniname2ctype_offset(str1813), 255},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1816), 565},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1821), 224},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1825), 258},
+ {uniname2ctype_offset(str1826), 202},
+ {-1},
+ {uniname2ctype_offset(str1828), 387},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1838), 585},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1842), 379},
+ {uniname2ctype_offset(str1843), 134},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1846), 481},
+ {-1},
+ {uniname2ctype_offset(str1848), 109},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1861), 202},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1866), 61},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1871), 479},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1878), 27},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1881), 63},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1891), 150},
+ {uniname2ctype_offset(str1892), 562},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1896), 588},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1904), 440},
+ {uniname2ctype_offset(str1905), 559},
+ {uniname2ctype_offset(str1906), 150},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1911), 343},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1915), 165},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1928), 424},
+ {uniname2ctype_offset(str1929), 417},
+ {uniname2ctype_offset(str1930), 445},
+ {-1},
+ {uniname2ctype_offset(str1932), 400},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1936), 323},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str1947), 194},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1950), 409},
+ {uniname2ctype_offset(str1951), 12},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1958), 109},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1961), 103},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1964), 425},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1967), 477},
+ {uniname2ctype_offset(str1968), 603},
+ {-1}, {-1},
+ {uniname2ctype_offset(str1971), 153},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1985), 393},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str1991), 1},
+ {-1},
+ {uniname2ctype_offset(str1993), 360},
+ {uniname2ctype_offset(str1994), 175},
+ {-1},
+ {uniname2ctype_offset(str1996), 42},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2006), 395},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2010), 363},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2014), 322},
+ {-1},
+ {uniname2ctype_offset(str2016), 209},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2023), 32},
+ {uniname2ctype_offset(str2024), 489},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2033), 78},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2043), 17},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2048), 92},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2055), 92},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2058), 240},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2062), 130},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2070), 9},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2073), 330},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2078), 245},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2083), 324},
+ {uniname2ctype_offset(str2084), 154},
+ {uniname2ctype_offset(str2085), 20},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2093), 192},
+ {-1},
+ {uniname2ctype_offset(str2095), 341},
+ {-1},
+ {uniname2ctype_offset(str2097), 214},
+ {uniname2ctype_offset(str2098), 8},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2102), 43},
+ {uniname2ctype_offset(str2103), 162},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2109), 215},
+ {uniname2ctype_offset(str2110), 123},
+ {-1},
+ {uniname2ctype_offset(str2112), 210},
+ {-1},
+ {uniname2ctype_offset(str2114), 159},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2117), 174},
+ {-1},
+ {uniname2ctype_offset(str2119), 216},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2135), 128},
+ {-1},
+ {uniname2ctype_offset(str2137), 172},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2145), 52},
+ {uniname2ctype_offset(str2146), 401},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2149), 221},
+ {uniname2ctype_offset(str2150), 121},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2153), 352},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2161), 233},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2165), 174},
+ {uniname2ctype_offset(str2166), 5},
+ {uniname2ctype_offset(str2167), 112},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2170), 216},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2173), 87},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2181), 139},
+ {uniname2ctype_offset(str2182), 87},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2195), 339},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2199), 159},
+ {-1},
+ {uniname2ctype_offset(str2201), 89},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2204), 369},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2207), 196},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2217), 128},
+ {uniname2ctype_offset(str2218), 526},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2222), 458},
+ {-1},
+ {uniname2ctype_offset(str2224), 438},
+ {uniname2ctype_offset(str2225), 459},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2228), 82},
+ {uniname2ctype_offset(str2229), 35},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2235), 474},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2238), 154},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2244), 428},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2247), 36},
+ {uniname2ctype_offset(str2248), 569},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2252), 240},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2258), 443},
+ {-1},
+ {uniname2ctype_offset(str2260), 235},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2268), 597},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2271), 18},
+ {uniname2ctype_offset(str2272), 254},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2278), 124},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2282), 230},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2285), 38},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2294), 88},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2302), 368},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2305), 205},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2312), 177},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2315), 568},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2321), 370},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2339), 600},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2343), 560},
+ {uniname2ctype_offset(str2344), 82},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2357), 237},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2361), 571},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2364), 309},
+ {uniname2ctype_offset(str2365), 403},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2373), 37},
+ {uniname2ctype_offset(str2374), 377},
+ {uniname2ctype_offset(str2375), 197},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2378), 237},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str2389), 204},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2396), 376},
+ {uniname2ctype_offset(str2397), 406},
+ {uniname2ctype_offset(str2398), 398},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2415), 402},
+ {uniname2ctype_offset(str2416), 366},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str2427), 23},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2433), 427},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2440), 100},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str2451), 29},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2454), 220},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2458), 495},
+ {-1},
+ {uniname2ctype_offset(str2460), 246},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2470), 433},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2475), 142},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2479), 346},
+ {uniname2ctype_offset(str2480), 50},
+ {uniname2ctype_offset(str2481), 371},
+ {-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(str2506), 511},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2513), 592},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2519), 466},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2529), 165},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2534), 325},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2540), 226},
+ {-1},
+ {uniname2ctype_offset(str2542), 125},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2548), 118},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2552), 462},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2561), 435},
+ {uniname2ctype_offset(str2562), 251},
+ {-1},
+ {uniname2ctype_offset(str2564), 357},
+ {-1},
+ {uniname2ctype_offset(str2566), 185},
+ {uniname2ctype_offset(str2567), 304},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2570), 476},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2575), 185},
+ {uniname2ctype_offset(str2576), 99},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2579), 133},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2586), 199},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2595), 327},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2603), 198},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2606), 359},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2609), 55},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2614), 538},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2624), 226},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2631), 107},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2634), 266},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2643), 437},
+ {uniname2ctype_offset(str2644), 576},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2648), 243},
+ {-1},
+ {uniname2ctype_offset(str2650), 78},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2653), 183},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2656), 353},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2663), 182},
+ {-1},
+ {uniname2ctype_offset(str2665), 83},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2668), 94},
+ {-1},
+ {uniname2ctype_offset(str2670), 593},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2673), 84},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2676), 83},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2688), 139},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2691), 84},
+ {uniname2ctype_offset(str2692), 211},
+ {-1},
+ {uniname2ctype_offset(str2694), 48},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2698), 119},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2701), 418},
+ {uniname2ctype_offset(str2702), 89},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2721), 198},
+ {-1},
+ {uniname2ctype_offset(str2723), 4},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2732), 130},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2747), 54},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2750), 534},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2760), 152},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2764), 241},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2768), 90},
+ {uniname2ctype_offset(str2769), 468},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2777), 28},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2780), 227},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2793), 34},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2816), 296},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2821), 378},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2834), 349},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2841), 222},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2848), 253},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2852), 291},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2855), 190},
+ {uniname2ctype_offset(str2856), 66},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2859), 412},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2862), 211},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2867), 295},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2871), 243},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2883), 242},
+ {-1},
+ {uniname2ctype_offset(str2885), 147},
+ {uniname2ctype_offset(str2886), 133},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2904), 449},
+ {uniname2ctype_offset(str2905), 262},
+ {uniname2ctype_offset(str2906), 575},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2909), 549},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2912), 244},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2926), 51},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2938), 242},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2941), 348},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2963), 225},
+ {uniname2ctype_offset(str2964), 503},
+ {-1}, {-1},
+ {uniname2ctype_offset(str2967), 43},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2980), 195},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2984), 547},
+ {-1},
+ {uniname2ctype_offset(str2986), 118},
+ {uniname2ctype_offset(str2987), 54},
+ {uniname2ctype_offset(str2988), 456},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str2993), 234},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3000), 16},
+ {uniname2ctype_offset(str3001), 180},
+ {uniname2ctype_offset(str3002), 64},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3005), 460},
+ {uniname2ctype_offset(str3006), 555},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3012), 80},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3028), 250},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3045), 577},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3055), 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},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3089), 146},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3098), 580},
+ {uniname2ctype_offset(str3099), 463},
+ {uniname2ctype_offset(str3100), 103},
+ {uniname2ctype_offset(str3101), 223},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3107), 299},
+ {-1},
+ {uniname2ctype_offset(str3109), 301},
+ {-1},
+ {uniname2ctype_offset(str3111), 191},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3125), 191},
+ {uniname2ctype_offset(str3126), 99},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3134), 223},
+ {-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(str3158), 314},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3165), 439},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3169), 47},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3176), 381},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3180), 317},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3185), 126},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3194), 204},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str3205), 227},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3209), 62},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3215), 180},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3218), 201},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3237), 375},
+ {-1},
+ {uniname2ctype_offset(str3239), 294},
+ {uniname2ctype_offset(str3240), 290},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3248), 213},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3254), 190},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3266), 421},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str3286), 235},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str3297), 297},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3303), 293},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3309), 178},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3317), 249},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str3328), 63},
+ {uniname2ctype_offset(str3329), 587},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3348), 392},
+ {uniname2ctype_offset(str3349), 423},
+ {-1},
+ {uniname2ctype_offset(str3351), 96},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3360), 234},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3363), 96},
+ {-1},
+ {uniname2ctype_offset(str3365), 578},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-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(str3433), 205},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3436), 213},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3439), 49},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3442), 464},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3458), 10},
+ {-1},
+ {uniname2ctype_offset(str3460), 589},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-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(str3511), 396},
+ {uniname2ctype_offset(str3512), 487},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3517), 179},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3531), 153},
+ {uniname2ctype_offset(str3532), 550},
+ {uniname2ctype_offset(str3533), 57},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3550), 41},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3558), 59},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-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(str3613), 214},
+ {uniname2ctype_offset(str3614), 220},
+ {-1},
+ {uniname2ctype_offset(str3616), 45},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3628), 71},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1},
+ {uniname2ctype_offset(str3648), 450},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {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}, {-1}, {-1}, {-1},
+ {-1}, {-1},
+ {uniname2ctype_offset(str3703), 132},
+ {-1},
+ {uniname2ctype_offset(str3705), 306},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3714), 132},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-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(str3766), 452},
+ {-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(str3791), 65},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3805), 250},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3819), 162},
+ {-1},
+ {uniname2ctype_offset(str3821), 292},
+ {-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), 248},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-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(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), 405},
+ {-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}, {-1}, {-1}, {-1},
+ {-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), 570},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str3995), 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},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-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(str4087), 415},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str4109), 119},
+ {-1}, {-1}, {-1}, {-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(str4137), 244},
+ {-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(str4162), 544},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str4175), 545},
+ {-1}, {-1}, {-1}, {-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(str4203), 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}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str4254), 116},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str4276), 116},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str4291), 302},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-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(str4321), 75},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-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(str4360), 231},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-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(str4397), 386},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str4405), 298},
+ {uniname2ctype_offset(str4406), 300},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-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(str4460), 90},
+ {-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(str4485), 179},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-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(str4528), 46},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-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), 601},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {uniname2ctype_offset(str4683), 451},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-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(str4772), 394},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-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(str4948), 590},
+#endif /* USE_UNICODE_PROPERTIES */
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+#ifndef USE_UNICODE_PROPERTIES
+ {uniname2ctype_offset(str6), 12},
+ {uniname2ctype_offset(str7), 7},
+ {uniname2ctype_offset(str8), 15},
+ {uniname2ctype_offset(str9), 1},
+ {uniname2ctype_offset(str10), 13},
+ {uniname2ctype_offset(str11), 11},
+ {uniname2ctype_offset(str12), 10},
+ {uniname2ctype_offset(str13), 14},
+ {uniname2ctype_offset(str14), 3},
+ {uniname2ctype_offset(str15), 9},
+ {uniname2ctype_offset(str16), 8},
+ {uniname2ctype_offset(str17), 6},
+ {uniname2ctype_offset(str18), 5},
+ {uniname2ctype_offset(str19), 4},
+ {uniname2ctype_offset(str20), 2}
+#else /* USE_UNICODE_PROPERTIES */
+ {uniname2ctype_offset(str4955), 155},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-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(str4986), 413},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-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(str5114), 225},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-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(str5608), 155},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-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), 602}
+#endif /* USE_UNICODE_PROPERTIES */
+ };
+
+ if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+ {
+ register unsigned int key = uniname2ctype_hash (str, len);
+
+ if (key <= MAX_HASH_VALUE)
+ {
+ register int o = wordlist[key].name;
+ if (o >= 0)
+ {
+ register const char *s = o + uniname2ctype_pool;
+
+ if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0')
+ return &wordlist[key];
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+uniname2ctype(const UChar *name, unsigned int len)
+{
+ const struct uniname2ctype_struct *p = uniname2ctype_p((const char *)name, len);
+ if (p) return p->ctype;
+ return -1;
+}
+#if defined ONIG_UNICODE_VERSION_STRING && !( \
+ ONIG_UNICODE_VERSION_MAJOR == 12 && \
+ ONIG_UNICODE_VERSION_MINOR == 1 && \
+ ONIG_UNICODE_VERSION_TEENY == 0 && \
+ 1)
+# error ONIG_UNICODE_VERSION_STRING mismatch
+#endif
+#define ONIG_UNICODE_VERSION_STRING "12.1.0"
+#define ONIG_UNICODE_VERSION_MAJOR 12
+#define ONIG_UNICODE_VERSION_MINOR 1
+#define ONIG_UNICODE_VERSION_TEENY 0
+#if defined ONIG_UNICODE_EMOJI_VERSION_STRING && !( \
+ ONIG_UNICODE_EMOJI_VERSION_MAJOR == 12 && \
+ ONIG_UNICODE_EMOJI_VERSION_MINOR == 0 && \
+ 1)
+# error ONIG_UNICODE_EMOJI_VERSION_STRING mismatch
+#endif
+#define ONIG_UNICODE_EMOJI_VERSION_STRING "12.0"
+#define ONIG_UNICODE_EMOJI_VERSION_MAJOR 12
+#define ONIG_UNICODE_EMOJI_VERSION_MINOR 0
diff --git a/enc/unicode/9.0.0/casefold.h b/enc/unicode/9.0.0/casefold.h
deleted file mode 100644
index f8f11673a7..0000000000
--- a/enc/unicode/9.0.0/casefold.h
+++ /dev/null
@@ -1,7068 +0,0 @@
-/* DO NOT EDIT THIS FILE. */
-/* Generated by enc/unicode/case-folding.rb */
-
-#if defined ONIG_UNICODE_VERSION_STRING && !( \
- ONIG_UNICODE_VERSION_MAJOR == 9 && \
- ONIG_UNICODE_VERSION_MINOR == 0 && \
- ONIG_UNICODE_VERSION_TEENY == 0 && \
- 1)
-# error ONIG_UNICODE_VERSION_STRING mismatch
-#endif
-#define ONIG_UNICODE_VERSION_STRING "9.0.0"
-#define ONIG_UNICODE_VERSION_MAJOR 9
-#define ONIG_UNICODE_VERSION_MINOR 0
-#define ONIG_UNICODE_VERSION_TEENY 0
-
-static const CaseFold_11_Type CaseFold_11_Table[] = {
-#define CaseFold (*(CaseFold_11_Type (*)[1399])(CaseFold_11_Table+0))
- {0x0041, {1|F|D, {0x0061}}},
- {0x0042, {1|F|D, {0x0062}}},
- {0x0043, {1|F|D, {0x0063}}},
- {0x0044, {1|F|D, {0x0064}}},
- {0x0045, {1|F|D, {0x0065}}},
- {0x0046, {1|F|D, {0x0066}}},
- {0x0047, {1|F|D, {0x0067}}},
- {0x0048, {1|F|D, {0x0068}}},
- {0x004a, {1|F|D, {0x006a}}},
- {0x004b, {1|F|D, {0x006b}}},
- {0x004c, {1|F|D, {0x006c}}},
- {0x004d, {1|F|D, {0x006d}}},
- {0x004e, {1|F|D, {0x006e}}},
- {0x004f, {1|F|D, {0x006f}}},
- {0x0050, {1|F|D, {0x0070}}},
- {0x0051, {1|F|D, {0x0071}}},
- {0x0052, {1|F|D, {0x0072}}},
- {0x0053, {1|F|D, {0x0073}}},
- {0x0054, {1|F|D, {0x0074}}},
- {0x0055, {1|F|D, {0x0075}}},
- {0x0056, {1|F|D, {0x0076}}},
- {0x0057, {1|F|D, {0x0077}}},
- {0x0058, {1|F|D, {0x0078}}},
- {0x0059, {1|F|D, {0x0079}}},
- {0x005a, {1|F|D, {0x007a}}},
- {0x00b5, {1|F|SU|I(0), {0x03bc}}},
- {0x00c0, {1|F|D, {0x00e0}}},
- {0x00c1, {1|F|D, {0x00e1}}},
- {0x00c2, {1|F|D, {0x00e2}}},
- {0x00c3, {1|F|D, {0x00e3}}},
- {0x00c4, {1|F|D, {0x00e4}}},
- {0x00c5, {1|F|D, {0x00e5}}},
- {0x00c6, {1|F|D, {0x00e6}}},
- {0x00c7, {1|F|D, {0x00e7}}},
- {0x00c8, {1|F|D, {0x00e8}}},
- {0x00c9, {1|F|D, {0x00e9}}},
- {0x00ca, {1|F|D, {0x00ea}}},
- {0x00cb, {1|F|D, {0x00eb}}},
- {0x00cc, {1|F|D, {0x00ec}}},
- {0x00cd, {1|F|D, {0x00ed}}},
- {0x00ce, {1|F|D, {0x00ee}}},
- {0x00cf, {1|F|D, {0x00ef}}},
- {0x00d0, {1|F|D, {0x00f0}}},
- {0x00d1, {1|F|D, {0x00f1}}},
- {0x00d2, {1|F|D, {0x00f2}}},
- {0x00d3, {1|F|D, {0x00f3}}},
- {0x00d4, {1|F|D, {0x00f4}}},
- {0x00d5, {1|F|D, {0x00f5}}},
- {0x00d6, {1|F|D, {0x00f6}}},
- {0x00d8, {1|F|D, {0x00f8}}},
- {0x00d9, {1|F|D, {0x00f9}}},
- {0x00da, {1|F|D, {0x00fa}}},
- {0x00db, {1|F|D, {0x00fb}}},
- {0x00dc, {1|F|D, {0x00fc}}},
- {0x00dd, {1|F|D, {0x00fd}}},
- {0x00de, {1|F|D, {0x00fe}}},
- {0x00df, {2|F|ST|SU|I(1), {0x0073, 0x0073}}},
- {0x0100, {1|F|D, {0x0101}}},
- {0x0102, {1|F|D, {0x0103}}},
- {0x0104, {1|F|D, {0x0105}}},
- {0x0106, {1|F|D, {0x0107}}},
- {0x0108, {1|F|D, {0x0109}}},
- {0x010a, {1|F|D, {0x010b}}},
- {0x010c, {1|F|D, {0x010d}}},
- {0x010e, {1|F|D, {0x010f}}},
- {0x0110, {1|F|D, {0x0111}}},
- {0x0112, {1|F|D, {0x0113}}},
- {0x0114, {1|F|D, {0x0115}}},
- {0x0116, {1|F|D, {0x0117}}},
- {0x0118, {1|F|D, {0x0119}}},
- {0x011a, {1|F|D, {0x011b}}},
- {0x011c, {1|F|D, {0x011d}}},
- {0x011e, {1|F|D, {0x011f}}},
- {0x0120, {1|F|D, {0x0121}}},
- {0x0122, {1|F|D, {0x0123}}},
- {0x0124, {1|F|D, {0x0125}}},
- {0x0126, {1|F|D, {0x0127}}},
- {0x0128, {1|F|D, {0x0129}}},
- {0x012a, {1|F|D, {0x012b}}},
- {0x012c, {1|F|D, {0x012d}}},
- {0x012e, {1|F|D, {0x012f}}},
- {0x0132, {1|F|D, {0x0133}}},
- {0x0134, {1|F|D, {0x0135}}},
- {0x0136, {1|F|D, {0x0137}}},
- {0x0139, {1|F|D, {0x013a}}},
- {0x013b, {1|F|D, {0x013c}}},
- {0x013d, {1|F|D, {0x013e}}},
- {0x013f, {1|F|D, {0x0140}}},
- {0x0141, {1|F|D, {0x0142}}},
- {0x0143, {1|F|D, {0x0144}}},
- {0x0145, {1|F|D, {0x0146}}},
- {0x0147, {1|F|D, {0x0148}}},
- {0x0149, {2|F|SU|I(5), {0x02bc, 0x006e}}},
- {0x014a, {1|F|D, {0x014b}}},
- {0x014c, {1|F|D, {0x014d}}},
- {0x014e, {1|F|D, {0x014f}}},
- {0x0150, {1|F|D, {0x0151}}},
- {0x0152, {1|F|D, {0x0153}}},
- {0x0154, {1|F|D, {0x0155}}},
- {0x0156, {1|F|D, {0x0157}}},
- {0x0158, {1|F|D, {0x0159}}},
- {0x015a, {1|F|D, {0x015b}}},
- {0x015c, {1|F|D, {0x015d}}},
- {0x015e, {1|F|D, {0x015f}}},
- {0x0160, {1|F|D, {0x0161}}},
- {0x0162, {1|F|D, {0x0163}}},
- {0x0164, {1|F|D, {0x0165}}},
- {0x0166, {1|F|D, {0x0167}}},
- {0x0168, {1|F|D, {0x0169}}},
- {0x016a, {1|F|D, {0x016b}}},
- {0x016c, {1|F|D, {0x016d}}},
- {0x016e, {1|F|D, {0x016f}}},
- {0x0170, {1|F|D, {0x0171}}},
- {0x0172, {1|F|D, {0x0173}}},
- {0x0174, {1|F|D, {0x0175}}},
- {0x0176, {1|F|D, {0x0177}}},
- {0x0178, {1|F|D, {0x00ff}}},
- {0x0179, {1|F|D, {0x017a}}},
- {0x017b, {1|F|D, {0x017c}}},
- {0x017d, {1|F|D, {0x017e}}},
- {0x017f, {1|F|SU|I(7), {0x0073}}},
- {0x0181, {1|F|D, {0x0253}}},
- {0x0182, {1|F|D, {0x0183}}},
- {0x0184, {1|F|D, {0x0185}}},
- {0x0186, {1|F|D, {0x0254}}},
- {0x0187, {1|F|D, {0x0188}}},
- {0x0189, {1|F|D, {0x0256}}},
- {0x018a, {1|F|D, {0x0257}}},
- {0x018b, {1|F|D, {0x018c}}},
- {0x018e, {1|F|D, {0x01dd}}},
- {0x018f, {1|F|D, {0x0259}}},
- {0x0190, {1|F|D, {0x025b}}},
- {0x0191, {1|F|D, {0x0192}}},
- {0x0193, {1|F|D, {0x0260}}},
- {0x0194, {1|F|D, {0x0263}}},
- {0x0196, {1|F|D, {0x0269}}},
- {0x0197, {1|F|D, {0x0268}}},
- {0x0198, {1|F|D, {0x0199}}},
- {0x019c, {1|F|D, {0x026f}}},
- {0x019d, {1|F|D, {0x0272}}},
- {0x019f, {1|F|D, {0x0275}}},
- {0x01a0, {1|F|D, {0x01a1}}},
- {0x01a2, {1|F|D, {0x01a3}}},
- {0x01a4, {1|F|D, {0x01a5}}},
- {0x01a6, {1|F|D, {0x0280}}},
- {0x01a7, {1|F|D, {0x01a8}}},
- {0x01a9, {1|F|D, {0x0283}}},
- {0x01ac, {1|F|D, {0x01ad}}},
- {0x01ae, {1|F|D, {0x0288}}},
- {0x01af, {1|F|D, {0x01b0}}},
- {0x01b1, {1|F|D, {0x028a}}},
- {0x01b2, {1|F|D, {0x028b}}},
- {0x01b3, {1|F|D, {0x01b4}}},
- {0x01b5, {1|F|D, {0x01b6}}},
- {0x01b7, {1|F|D, {0x0292}}},
- {0x01b8, {1|F|D, {0x01b9}}},
- {0x01bc, {1|F|D, {0x01bd}}},
- {0x01c4, {1|F|D|ST|I(8), {0x01c6}}},
- {0x01c5, {1|F|D|IT|SU|I(9), {0x01c6}}},
- {0x01c7, {1|F|D|ST|I(12), {0x01c9}}},
- {0x01c8, {1|F|D|IT|SU|I(13), {0x01c9}}},
- {0x01ca, {1|F|D|ST|I(16), {0x01cc}}},
- {0x01cb, {1|F|D|IT|SU|I(17), {0x01cc}}},
- {0x01cd, {1|F|D, {0x01ce}}},
- {0x01cf, {1|F|D, {0x01d0}}},
- {0x01d1, {1|F|D, {0x01d2}}},
- {0x01d3, {1|F|D, {0x01d4}}},
- {0x01d5, {1|F|D, {0x01d6}}},
- {0x01d7, {1|F|D, {0x01d8}}},
- {0x01d9, {1|F|D, {0x01da}}},
- {0x01db, {1|F|D, {0x01dc}}},
- {0x01de, {1|F|D, {0x01df}}},
- {0x01e0, {1|F|D, {0x01e1}}},
- {0x01e2, {1|F|D, {0x01e3}}},
- {0x01e4, {1|F|D, {0x01e5}}},
- {0x01e6, {1|F|D, {0x01e7}}},
- {0x01e8, {1|F|D, {0x01e9}}},
- {0x01ea, {1|F|D, {0x01eb}}},
- {0x01ec, {1|F|D, {0x01ed}}},
- {0x01ee, {1|F|D, {0x01ef}}},
- {0x01f0, {2|F|SU|I(20), {0x006a, 0x030c}}},
- {0x01f1, {1|F|D|ST|I(22), {0x01f3}}},
- {0x01f2, {1|F|D|IT|SU|I(23), {0x01f3}}},
- {0x01f4, {1|F|D, {0x01f5}}},
- {0x01f6, {1|F|D, {0x0195}}},
- {0x01f7, {1|F|D, {0x01bf}}},
- {0x01f8, {1|F|D, {0x01f9}}},
- {0x01fa, {1|F|D, {0x01fb}}},
- {0x01fc, {1|F|D, {0x01fd}}},
- {0x01fe, {1|F|D, {0x01ff}}},
- {0x0200, {1|F|D, {0x0201}}},
- {0x0202, {1|F|D, {0x0203}}},
- {0x0204, {1|F|D, {0x0205}}},
- {0x0206, {1|F|D, {0x0207}}},
- {0x0208, {1|F|D, {0x0209}}},
- {0x020a, {1|F|D, {0x020b}}},
- {0x020c, {1|F|D, {0x020d}}},
- {0x020e, {1|F|D, {0x020f}}},
- {0x0210, {1|F|D, {0x0211}}},
- {0x0212, {1|F|D, {0x0213}}},
- {0x0214, {1|F|D, {0x0215}}},
- {0x0216, {1|F|D, {0x0217}}},
- {0x0218, {1|F|D, {0x0219}}},
- {0x021a, {1|F|D, {0x021b}}},
- {0x021c, {1|F|D, {0x021d}}},
- {0x021e, {1|F|D, {0x021f}}},
- {0x0220, {1|F|D, {0x019e}}},
- {0x0222, {1|F|D, {0x0223}}},
- {0x0224, {1|F|D, {0x0225}}},
- {0x0226, {1|F|D, {0x0227}}},
- {0x0228, {1|F|D, {0x0229}}},
- {0x022a, {1|F|D, {0x022b}}},
- {0x022c, {1|F|D, {0x022d}}},
- {0x022e, {1|F|D, {0x022f}}},
- {0x0230, {1|F|D, {0x0231}}},
- {0x0232, {1|F|D, {0x0233}}},
- {0x023a, {1|F|D, {0x2c65}}},
- {0x023b, {1|F|D, {0x023c}}},
- {0x023d, {1|F|D, {0x019a}}},
- {0x023e, {1|F|D, {0x2c66}}},
- {0x0241, {1|F|D, {0x0242}}},
- {0x0243, {1|F|D, {0x0180}}},
- {0x0244, {1|F|D, {0x0289}}},
- {0x0245, {1|F|D, {0x028c}}},
- {0x0246, {1|F|D, {0x0247}}},
- {0x0248, {1|F|D, {0x0249}}},
- {0x024a, {1|F|D, {0x024b}}},
- {0x024c, {1|F|D, {0x024d}}},
- {0x024e, {1|F|D, {0x024f}}},
- {0x0345, {1|F|SU|I(26), {0x03b9}}},
- {0x0370, {1|F|D, {0x0371}}},
- {0x0372, {1|F|D, {0x0373}}},
- {0x0376, {1|F|D, {0x0377}}},
- {0x037f, {1|F|D, {0x03f3}}},
- {0x0386, {1|F|D, {0x03ac}}},
- {0x0388, {1|F|D, {0x03ad}}},
- {0x0389, {1|F|D, {0x03ae}}},
- {0x038a, {1|F|D, {0x03af}}},
- {0x038c, {1|F|D, {0x03cc}}},
- {0x038e, {1|F|D, {0x03cd}}},
- {0x038f, {1|F|D, {0x03ce}}},
- {0x0390, {3|F|SU|I(27), {0x03b9, 0x0308, 0x0301}}},
- {0x0391, {1|F|D, {0x03b1}}},
- {0x0392, {1|F|D, {0x03b2}}},
- {0x0393, {1|F|D, {0x03b3}}},
- {0x0394, {1|F|D, {0x03b4}}},
- {0x0395, {1|F|D, {0x03b5}}},
- {0x0396, {1|F|D, {0x03b6}}},
- {0x0397, {1|F|D, {0x03b7}}},
- {0x0398, {1|F|D, {0x03b8}}},
- {0x0399, {1|F|D, {0x03b9}}},
- {0x039a, {1|F|D, {0x03ba}}},
- {0x039b, {1|F|D, {0x03bb}}},
- {0x039c, {1|F|D, {0x03bc}}},
- {0x039d, {1|F|D, {0x03bd}}},
- {0x039e, {1|F|D, {0x03be}}},
- {0x039f, {1|F|D, {0x03bf}}},
- {0x03a0, {1|F|D, {0x03c0}}},
- {0x03a1, {1|F|D, {0x03c1}}},
- {0x03a3, {1|F|D, {0x03c3}}},
- {0x03a4, {1|F|D, {0x03c4}}},
- {0x03a5, {1|F|D, {0x03c5}}},
- {0x03a6, {1|F|D, {0x03c6}}},
- {0x03a7, {1|F|D, {0x03c7}}},
- {0x03a8, {1|F|D, {0x03c8}}},
- {0x03a9, {1|F|D, {0x03c9}}},
- {0x03aa, {1|F|D, {0x03ca}}},
- {0x03ab, {1|F|D, {0x03cb}}},
- {0x03b0, {3|F|SU|I(30), {0x03c5, 0x0308, 0x0301}}},
- {0x03c2, {1|F|SU|I(33), {0x03c3}}},
- {0x03cf, {1|F|D, {0x03d7}}},
- {0x03d0, {1|F|SU|I(34), {0x03b2}}},
- {0x03d1, {1|F|SU|I(35), {0x03b8}}},
- {0x03d5, {1|F|SU|I(36), {0x03c6}}},
- {0x03d6, {1|F|SU|I(37), {0x03c0}}},
- {0x03d8, {1|F|D, {0x03d9}}},
- {0x03da, {1|F|D, {0x03db}}},
- {0x03dc, {1|F|D, {0x03dd}}},
- {0x03de, {1|F|D, {0x03df}}},
- {0x03e0, {1|F|D, {0x03e1}}},
- {0x03e2, {1|F|D, {0x03e3}}},
- {0x03e4, {1|F|D, {0x03e5}}},
- {0x03e6, {1|F|D, {0x03e7}}},
- {0x03e8, {1|F|D, {0x03e9}}},
- {0x03ea, {1|F|D, {0x03eb}}},
- {0x03ec, {1|F|D, {0x03ed}}},
- {0x03ee, {1|F|D, {0x03ef}}},
- {0x03f0, {1|F|SU|I(38), {0x03ba}}},
- {0x03f1, {1|F|SU|I(39), {0x03c1}}},
- {0x03f4, {1|F|D, {0x03b8}}},
- {0x03f5, {1|F|SU|I(40), {0x03b5}}},
- {0x03f7, {1|F|D, {0x03f8}}},
- {0x03f9, {1|F|D, {0x03f2}}},
- {0x03fa, {1|F|D, {0x03fb}}},
- {0x03fd, {1|F|D, {0x037b}}},
- {0x03fe, {1|F|D, {0x037c}}},
- {0x03ff, {1|F|D, {0x037d}}},
- {0x0400, {1|F|D, {0x0450}}},
- {0x0401, {1|F|D, {0x0451}}},
- {0x0402, {1|F|D, {0x0452}}},
- {0x0403, {1|F|D, {0x0453}}},
- {0x0404, {1|F|D, {0x0454}}},
- {0x0405, {1|F|D, {0x0455}}},
- {0x0406, {1|F|D, {0x0456}}},
- {0x0407, {1|F|D, {0x0457}}},
- {0x0408, {1|F|D, {0x0458}}},
- {0x0409, {1|F|D, {0x0459}}},
- {0x040a, {1|F|D, {0x045a}}},
- {0x040b, {1|F|D, {0x045b}}},
- {0x040c, {1|F|D, {0x045c}}},
- {0x040d, {1|F|D, {0x045d}}},
- {0x040e, {1|F|D, {0x045e}}},
- {0x040f, {1|F|D, {0x045f}}},
- {0x0410, {1|F|D, {0x0430}}},
- {0x0411, {1|F|D, {0x0431}}},
- {0x0412, {1|F|D, {0x0432}}},
- {0x0413, {1|F|D, {0x0433}}},
- {0x0414, {1|F|D, {0x0434}}},
- {0x0415, {1|F|D, {0x0435}}},
- {0x0416, {1|F|D, {0x0436}}},
- {0x0417, {1|F|D, {0x0437}}},
- {0x0418, {1|F|D, {0x0438}}},
- {0x0419, {1|F|D, {0x0439}}},
- {0x041a, {1|F|D, {0x043a}}},
- {0x041b, {1|F|D, {0x043b}}},
- {0x041c, {1|F|D, {0x043c}}},
- {0x041d, {1|F|D, {0x043d}}},
- {0x041e, {1|F|D, {0x043e}}},
- {0x041f, {1|F|D, {0x043f}}},
- {0x0420, {1|F|D, {0x0440}}},
- {0x0421, {1|F|D, {0x0441}}},
- {0x0422, {1|F|D, {0x0442}}},
- {0x0423, {1|F|D, {0x0443}}},
- {0x0424, {1|F|D, {0x0444}}},
- {0x0425, {1|F|D, {0x0445}}},
- {0x0426, {1|F|D, {0x0446}}},
- {0x0427, {1|F|D, {0x0447}}},
- {0x0428, {1|F|D, {0x0448}}},
- {0x0429, {1|F|D, {0x0449}}},
- {0x042a, {1|F|D, {0x044a}}},
- {0x042b, {1|F|D, {0x044b}}},
- {0x042c, {1|F|D, {0x044c}}},
- {0x042d, {1|F|D, {0x044d}}},
- {0x042e, {1|F|D, {0x044e}}},
- {0x042f, {1|F|D, {0x044f}}},
- {0x0460, {1|F|D, {0x0461}}},
- {0x0462, {1|F|D, {0x0463}}},
- {0x0464, {1|F|D, {0x0465}}},
- {0x0466, {1|F|D, {0x0467}}},
- {0x0468, {1|F|D, {0x0469}}},
- {0x046a, {1|F|D, {0x046b}}},
- {0x046c, {1|F|D, {0x046d}}},
- {0x046e, {1|F|D, {0x046f}}},
- {0x0470, {1|F|D, {0x0471}}},
- {0x0472, {1|F|D, {0x0473}}},
- {0x0474, {1|F|D, {0x0475}}},
- {0x0476, {1|F|D, {0x0477}}},
- {0x0478, {1|F|D, {0x0479}}},
- {0x047a, {1|F|D, {0x047b}}},
- {0x047c, {1|F|D, {0x047d}}},
- {0x047e, {1|F|D, {0x047f}}},
- {0x0480, {1|F|D, {0x0481}}},
- {0x048a, {1|F|D, {0x048b}}},
- {0x048c, {1|F|D, {0x048d}}},
- {0x048e, {1|F|D, {0x048f}}},
- {0x0490, {1|F|D, {0x0491}}},
- {0x0492, {1|F|D, {0x0493}}},
- {0x0494, {1|F|D, {0x0495}}},
- {0x0496, {1|F|D, {0x0497}}},
- {0x0498, {1|F|D, {0x0499}}},
- {0x049a, {1|F|D, {0x049b}}},
- {0x049c, {1|F|D, {0x049d}}},
- {0x049e, {1|F|D, {0x049f}}},
- {0x04a0, {1|F|D, {0x04a1}}},
- {0x04a2, {1|F|D, {0x04a3}}},
- {0x04a4, {1|F|D, {0x04a5}}},
- {0x04a6, {1|F|D, {0x04a7}}},
- {0x04a8, {1|F|D, {0x04a9}}},
- {0x04aa, {1|F|D, {0x04ab}}},
- {0x04ac, {1|F|D, {0x04ad}}},
- {0x04ae, {1|F|D, {0x04af}}},
- {0x04b0, {1|F|D, {0x04b1}}},
- {0x04b2, {1|F|D, {0x04b3}}},
- {0x04b4, {1|F|D, {0x04b5}}},
- {0x04b6, {1|F|D, {0x04b7}}},
- {0x04b8, {1|F|D, {0x04b9}}},
- {0x04ba, {1|F|D, {0x04bb}}},
- {0x04bc, {1|F|D, {0x04bd}}},
- {0x04be, {1|F|D, {0x04bf}}},
- {0x04c0, {1|F|D, {0x04cf}}},
- {0x04c1, {1|F|D, {0x04c2}}},
- {0x04c3, {1|F|D, {0x04c4}}},
- {0x04c5, {1|F|D, {0x04c6}}},
- {0x04c7, {1|F|D, {0x04c8}}},
- {0x04c9, {1|F|D, {0x04ca}}},
- {0x04cb, {1|F|D, {0x04cc}}},
- {0x04cd, {1|F|D, {0x04ce}}},
- {0x04d0, {1|F|D, {0x04d1}}},
- {0x04d2, {1|F|D, {0x04d3}}},
- {0x04d4, {1|F|D, {0x04d5}}},
- {0x04d6, {1|F|D, {0x04d7}}},
- {0x04d8, {1|F|D, {0x04d9}}},
- {0x04da, {1|F|D, {0x04db}}},
- {0x04dc, {1|F|D, {0x04dd}}},
- {0x04de, {1|F|D, {0x04df}}},
- {0x04e0, {1|F|D, {0x04e1}}},
- {0x04e2, {1|F|D, {0x04e3}}},
- {0x04e4, {1|F|D, {0x04e5}}},
- {0x04e6, {1|F|D, {0x04e7}}},
- {0x04e8, {1|F|D, {0x04e9}}},
- {0x04ea, {1|F|D, {0x04eb}}},
- {0x04ec, {1|F|D, {0x04ed}}},
- {0x04ee, {1|F|D, {0x04ef}}},
- {0x04f0, {1|F|D, {0x04f1}}},
- {0x04f2, {1|F|D, {0x04f3}}},
- {0x04f4, {1|F|D, {0x04f5}}},
- {0x04f6, {1|F|D, {0x04f7}}},
- {0x04f8, {1|F|D, {0x04f9}}},
- {0x04fa, {1|F|D, {0x04fb}}},
- {0x04fc, {1|F|D, {0x04fd}}},
- {0x04fe, {1|F|D, {0x04ff}}},
- {0x0500, {1|F|D, {0x0501}}},
- {0x0502, {1|F|D, {0x0503}}},
- {0x0504, {1|F|D, {0x0505}}},
- {0x0506, {1|F|D, {0x0507}}},
- {0x0508, {1|F|D, {0x0509}}},
- {0x050a, {1|F|D, {0x050b}}},
- {0x050c, {1|F|D, {0x050d}}},
- {0x050e, {1|F|D, {0x050f}}},
- {0x0510, {1|F|D, {0x0511}}},
- {0x0512, {1|F|D, {0x0513}}},
- {0x0514, {1|F|D, {0x0515}}},
- {0x0516, {1|F|D, {0x0517}}},
- {0x0518, {1|F|D, {0x0519}}},
- {0x051a, {1|F|D, {0x051b}}},
- {0x051c, {1|F|D, {0x051d}}},
- {0x051e, {1|F|D, {0x051f}}},
- {0x0520, {1|F|D, {0x0521}}},
- {0x0522, {1|F|D, {0x0523}}},
- {0x0524, {1|F|D, {0x0525}}},
- {0x0526, {1|F|D, {0x0527}}},
- {0x0528, {1|F|D, {0x0529}}},
- {0x052a, {1|F|D, {0x052b}}},
- {0x052c, {1|F|D, {0x052d}}},
- {0x052e, {1|F|D, {0x052f}}},
- {0x0531, {1|F|D, {0x0561}}},
- {0x0532, {1|F|D, {0x0562}}},
- {0x0533, {1|F|D, {0x0563}}},
- {0x0534, {1|F|D, {0x0564}}},
- {0x0535, {1|F|D, {0x0565}}},
- {0x0536, {1|F|D, {0x0566}}},
- {0x0537, {1|F|D, {0x0567}}},
- {0x0538, {1|F|D, {0x0568}}},
- {0x0539, {1|F|D, {0x0569}}},
- {0x053a, {1|F|D, {0x056a}}},
- {0x053b, {1|F|D, {0x056b}}},
- {0x053c, {1|F|D, {0x056c}}},
- {0x053d, {1|F|D, {0x056d}}},
- {0x053e, {1|F|D, {0x056e}}},
- {0x053f, {1|F|D, {0x056f}}},
- {0x0540, {1|F|D, {0x0570}}},
- {0x0541, {1|F|D, {0x0571}}},
- {0x0542, {1|F|D, {0x0572}}},
- {0x0543, {1|F|D, {0x0573}}},
- {0x0544, {1|F|D, {0x0574}}},
- {0x0545, {1|F|D, {0x0575}}},
- {0x0546, {1|F|D, {0x0576}}},
- {0x0547, {1|F|D, {0x0577}}},
- {0x0548, {1|F|D, {0x0578}}},
- {0x0549, {1|F|D, {0x0579}}},
- {0x054a, {1|F|D, {0x057a}}},
- {0x054b, {1|F|D, {0x057b}}},
- {0x054c, {1|F|D, {0x057c}}},
- {0x054d, {1|F|D, {0x057d}}},
- {0x054e, {1|F|D, {0x057e}}},
- {0x054f, {1|F|D, {0x057f}}},
- {0x0550, {1|F|D, {0x0580}}},
- {0x0551, {1|F|D, {0x0581}}},
- {0x0552, {1|F|D, {0x0582}}},
- {0x0553, {1|F|D, {0x0583}}},
- {0x0554, {1|F|D, {0x0584}}},
- {0x0555, {1|F|D, {0x0585}}},
- {0x0556, {1|F|D, {0x0586}}},
- {0x0587, {2|F|ST|SU|I(41), {0x0565, 0x0582}}},
- {0x10a0, {1|F|D, {0x2d00}}},
- {0x10a1, {1|F|D, {0x2d01}}},
- {0x10a2, {1|F|D, {0x2d02}}},
- {0x10a3, {1|F|D, {0x2d03}}},
- {0x10a4, {1|F|D, {0x2d04}}},
- {0x10a5, {1|F|D, {0x2d05}}},
- {0x10a6, {1|F|D, {0x2d06}}},
- {0x10a7, {1|F|D, {0x2d07}}},
- {0x10a8, {1|F|D, {0x2d08}}},
- {0x10a9, {1|F|D, {0x2d09}}},
- {0x10aa, {1|F|D, {0x2d0a}}},
- {0x10ab, {1|F|D, {0x2d0b}}},
- {0x10ac, {1|F|D, {0x2d0c}}},
- {0x10ad, {1|F|D, {0x2d0d}}},
- {0x10ae, {1|F|D, {0x2d0e}}},
- {0x10af, {1|F|D, {0x2d0f}}},
- {0x10b0, {1|F|D, {0x2d10}}},
- {0x10b1, {1|F|D, {0x2d11}}},
- {0x10b2, {1|F|D, {0x2d12}}},
- {0x10b3, {1|F|D, {0x2d13}}},
- {0x10b4, {1|F|D, {0x2d14}}},
- {0x10b5, {1|F|D, {0x2d15}}},
- {0x10b6, {1|F|D, {0x2d16}}},
- {0x10b7, {1|F|D, {0x2d17}}},
- {0x10b8, {1|F|D, {0x2d18}}},
- {0x10b9, {1|F|D, {0x2d19}}},
- {0x10ba, {1|F|D, {0x2d1a}}},
- {0x10bb, {1|F|D, {0x2d1b}}},
- {0x10bc, {1|F|D, {0x2d1c}}},
- {0x10bd, {1|F|D, {0x2d1d}}},
- {0x10be, {1|F|D, {0x2d1e}}},
- {0x10bf, {1|F|D, {0x2d1f}}},
- {0x10c0, {1|F|D, {0x2d20}}},
- {0x10c1, {1|F|D, {0x2d21}}},
- {0x10c2, {1|F|D, {0x2d22}}},
- {0x10c3, {1|F|D, {0x2d23}}},
- {0x10c4, {1|F|D, {0x2d24}}},
- {0x10c5, {1|F|D, {0x2d25}}},
- {0x10c7, {1|F|D, {0x2d27}}},
- {0x10cd, {1|F|D, {0x2d2d}}},
- {0x13f8, {1|F|U, {0x13f0}}},
- {0x13f9, {1|F|U, {0x13f1}}},
- {0x13fa, {1|F|U, {0x13f2}}},
- {0x13fb, {1|F|U, {0x13f3}}},
- {0x13fc, {1|F|U, {0x13f4}}},
- {0x13fd, {1|F|U, {0x13f5}}},
- {0x1c80, {1|F|SU|I(45), {0x0432}}},
- {0x1c81, {1|F|SU|I(46), {0x0434}}},
- {0x1c82, {1|F|SU|I(47), {0x043e}}},
- {0x1c83, {1|F|SU|I(48), {0x0441}}},
- {0x1c84, {1|F|SU|I(49), {0x0442}}},
- {0x1c85, {1|F|SU|I(50), {0x0442}}},
- {0x1c86, {1|F|SU|I(51), {0x044a}}},
- {0x1c87, {1|F|SU|I(52), {0x0463}}},
- {0x1c88, {1|F|SU|I(53), {0xa64b}}},
- {0x1e00, {1|F|D, {0x1e01}}},
- {0x1e02, {1|F|D, {0x1e03}}},
- {0x1e04, {1|F|D, {0x1e05}}},
- {0x1e06, {1|F|D, {0x1e07}}},
- {0x1e08, {1|F|D, {0x1e09}}},
- {0x1e0a, {1|F|D, {0x1e0b}}},
- {0x1e0c, {1|F|D, {0x1e0d}}},
- {0x1e0e, {1|F|D, {0x1e0f}}},
- {0x1e10, {1|F|D, {0x1e11}}},
- {0x1e12, {1|F|D, {0x1e13}}},
- {0x1e14, {1|F|D, {0x1e15}}},
- {0x1e16, {1|F|D, {0x1e17}}},
- {0x1e18, {1|F|D, {0x1e19}}},
- {0x1e1a, {1|F|D, {0x1e1b}}},
- {0x1e1c, {1|F|D, {0x1e1d}}},
- {0x1e1e, {1|F|D, {0x1e1f}}},
- {0x1e20, {1|F|D, {0x1e21}}},
- {0x1e22, {1|F|D, {0x1e23}}},
- {0x1e24, {1|F|D, {0x1e25}}},
- {0x1e26, {1|F|D, {0x1e27}}},
- {0x1e28, {1|F|D, {0x1e29}}},
- {0x1e2a, {1|F|D, {0x1e2b}}},
- {0x1e2c, {1|F|D, {0x1e2d}}},
- {0x1e2e, {1|F|D, {0x1e2f}}},
- {0x1e30, {1|F|D, {0x1e31}}},
- {0x1e32, {1|F|D, {0x1e33}}},
- {0x1e34, {1|F|D, {0x1e35}}},
- {0x1e36, {1|F|D, {0x1e37}}},
- {0x1e38, {1|F|D, {0x1e39}}},
- {0x1e3a, {1|F|D, {0x1e3b}}},
- {0x1e3c, {1|F|D, {0x1e3d}}},
- {0x1e3e, {1|F|D, {0x1e3f}}},
- {0x1e40, {1|F|D, {0x1e41}}},
- {0x1e42, {1|F|D, {0x1e43}}},
- {0x1e44, {1|F|D, {0x1e45}}},
- {0x1e46, {1|F|D, {0x1e47}}},
- {0x1e48, {1|F|D, {0x1e49}}},
- {0x1e4a, {1|F|D, {0x1e4b}}},
- {0x1e4c, {1|F|D, {0x1e4d}}},
- {0x1e4e, {1|F|D, {0x1e4f}}},
- {0x1e50, {1|F|D, {0x1e51}}},
- {0x1e52, {1|F|D, {0x1e53}}},
- {0x1e54, {1|F|D, {0x1e55}}},
- {0x1e56, {1|F|D, {0x1e57}}},
- {0x1e58, {1|F|D, {0x1e59}}},
- {0x1e5a, {1|F|D, {0x1e5b}}},
- {0x1e5c, {1|F|D, {0x1e5d}}},
- {0x1e5e, {1|F|D, {0x1e5f}}},
- {0x1e60, {1|F|D, {0x1e61}}},
- {0x1e62, {1|F|D, {0x1e63}}},
- {0x1e64, {1|F|D, {0x1e65}}},
- {0x1e66, {1|F|D, {0x1e67}}},
- {0x1e68, {1|F|D, {0x1e69}}},
- {0x1e6a, {1|F|D, {0x1e6b}}},
- {0x1e6c, {1|F|D, {0x1e6d}}},
- {0x1e6e, {1|F|D, {0x1e6f}}},
- {0x1e70, {1|F|D, {0x1e71}}},
- {0x1e72, {1|F|D, {0x1e73}}},
- {0x1e74, {1|F|D, {0x1e75}}},
- {0x1e76, {1|F|D, {0x1e77}}},
- {0x1e78, {1|F|D, {0x1e79}}},
- {0x1e7a, {1|F|D, {0x1e7b}}},
- {0x1e7c, {1|F|D, {0x1e7d}}},
- {0x1e7e, {1|F|D, {0x1e7f}}},
- {0x1e80, {1|F|D, {0x1e81}}},
- {0x1e82, {1|F|D, {0x1e83}}},
- {0x1e84, {1|F|D, {0x1e85}}},
- {0x1e86, {1|F|D, {0x1e87}}},
- {0x1e88, {1|F|D, {0x1e89}}},
- {0x1e8a, {1|F|D, {0x1e8b}}},
- {0x1e8c, {1|F|D, {0x1e8d}}},
- {0x1e8e, {1|F|D, {0x1e8f}}},
- {0x1e90, {1|F|D, {0x1e91}}},
- {0x1e92, {1|F|D, {0x1e93}}},
- {0x1e94, {1|F|D, {0x1e95}}},
- {0x1e96, {2|F|SU|I(54), {0x0068, 0x0331}}},
- {0x1e97, {2|F|SU|I(56), {0x0074, 0x0308}}},
- {0x1e98, {2|F|SU|I(58), {0x0077, 0x030a}}},
- {0x1e99, {2|F|SU|I(60), {0x0079, 0x030a}}},
- {0x1e9a, {2|F|SU|I(62), {0x0061, 0x02be}}},
- {0x1e9b, {1|F|SU|I(64), {0x1e61}}},
- {0x1e9e, {2|F|SL|I(65), {0x0073, 0x0073}}},
- {0x1ea0, {1|F|D, {0x1ea1}}},
- {0x1ea2, {1|F|D, {0x1ea3}}},
- {0x1ea4, {1|F|D, {0x1ea5}}},
- {0x1ea6, {1|F|D, {0x1ea7}}},
- {0x1ea8, {1|F|D, {0x1ea9}}},
- {0x1eaa, {1|F|D, {0x1eab}}},
- {0x1eac, {1|F|D, {0x1ead}}},
- {0x1eae, {1|F|D, {0x1eaf}}},
- {0x1eb0, {1|F|D, {0x1eb1}}},
- {0x1eb2, {1|F|D, {0x1eb3}}},
- {0x1eb4, {1|F|D, {0x1eb5}}},
- {0x1eb6, {1|F|D, {0x1eb7}}},
- {0x1eb8, {1|F|D, {0x1eb9}}},
- {0x1eba, {1|F|D, {0x1ebb}}},
- {0x1ebc, {1|F|D, {0x1ebd}}},
- {0x1ebe, {1|F|D, {0x1ebf}}},
- {0x1ec0, {1|F|D, {0x1ec1}}},
- {0x1ec2, {1|F|D, {0x1ec3}}},
- {0x1ec4, {1|F|D, {0x1ec5}}},
- {0x1ec6, {1|F|D, {0x1ec7}}},
- {0x1ec8, {1|F|D, {0x1ec9}}},
- {0x1eca, {1|F|D, {0x1ecb}}},
- {0x1ecc, {1|F|D, {0x1ecd}}},
- {0x1ece, {1|F|D, {0x1ecf}}},
- {0x1ed0, {1|F|D, {0x1ed1}}},
- {0x1ed2, {1|F|D, {0x1ed3}}},
- {0x1ed4, {1|F|D, {0x1ed5}}},
- {0x1ed6, {1|F|D, {0x1ed7}}},
- {0x1ed8, {1|F|D, {0x1ed9}}},
- {0x1eda, {1|F|D, {0x1edb}}},
- {0x1edc, {1|F|D, {0x1edd}}},
- {0x1ede, {1|F|D, {0x1edf}}},
- {0x1ee0, {1|F|D, {0x1ee1}}},
- {0x1ee2, {1|F|D, {0x1ee3}}},
- {0x1ee4, {1|F|D, {0x1ee5}}},
- {0x1ee6, {1|F|D, {0x1ee7}}},
- {0x1ee8, {1|F|D, {0x1ee9}}},
- {0x1eea, {1|F|D, {0x1eeb}}},
- {0x1eec, {1|F|D, {0x1eed}}},
- {0x1eee, {1|F|D, {0x1eef}}},
- {0x1ef0, {1|F|D, {0x1ef1}}},
- {0x1ef2, {1|F|D, {0x1ef3}}},
- {0x1ef4, {1|F|D, {0x1ef5}}},
- {0x1ef6, {1|F|D, {0x1ef7}}},
- {0x1ef8, {1|F|D, {0x1ef9}}},
- {0x1efa, {1|F|D, {0x1efb}}},
- {0x1efc, {1|F|D, {0x1efd}}},
- {0x1efe, {1|F|D, {0x1eff}}},
- {0x1f08, {1|F|D, {0x1f00}}},
- {0x1f09, {1|F|D, {0x1f01}}},
- {0x1f0a, {1|F|D, {0x1f02}}},
- {0x1f0b, {1|F|D, {0x1f03}}},
- {0x1f0c, {1|F|D, {0x1f04}}},
- {0x1f0d, {1|F|D, {0x1f05}}},
- {0x1f0e, {1|F|D, {0x1f06}}},
- {0x1f0f, {1|F|D, {0x1f07}}},
- {0x1f18, {1|F|D, {0x1f10}}},
- {0x1f19, {1|F|D, {0x1f11}}},
- {0x1f1a, {1|F|D, {0x1f12}}},
- {0x1f1b, {1|F|D, {0x1f13}}},
- {0x1f1c, {1|F|D, {0x1f14}}},
- {0x1f1d, {1|F|D, {0x1f15}}},
- {0x1f28, {1|F|D, {0x1f20}}},
- {0x1f29, {1|F|D, {0x1f21}}},
- {0x1f2a, {1|F|D, {0x1f22}}},
- {0x1f2b, {1|F|D, {0x1f23}}},
- {0x1f2c, {1|F|D, {0x1f24}}},
- {0x1f2d, {1|F|D, {0x1f25}}},
- {0x1f2e, {1|F|D, {0x1f26}}},
- {0x1f2f, {1|F|D, {0x1f27}}},
- {0x1f38, {1|F|D, {0x1f30}}},
- {0x1f39, {1|F|D, {0x1f31}}},
- {0x1f3a, {1|F|D, {0x1f32}}},
- {0x1f3b, {1|F|D, {0x1f33}}},
- {0x1f3c, {1|F|D, {0x1f34}}},
- {0x1f3d, {1|F|D, {0x1f35}}},
- {0x1f3e, {1|F|D, {0x1f36}}},
- {0x1f3f, {1|F|D, {0x1f37}}},
- {0x1f48, {1|F|D, {0x1f40}}},
- {0x1f49, {1|F|D, {0x1f41}}},
- {0x1f4a, {1|F|D, {0x1f42}}},
- {0x1f4b, {1|F|D, {0x1f43}}},
- {0x1f4c, {1|F|D, {0x1f44}}},
- {0x1f4d, {1|F|D, {0x1f45}}},
- {0x1f50, {2|F|SU|I(66), {0x03c5, 0x0313}}},
- {0x1f52, {3|F|SU|I(68), {0x03c5, 0x0313, 0x0300}}},
- {0x1f54, {3|F|SU|I(71), {0x03c5, 0x0313, 0x0301}}},
- {0x1f56, {3|F|SU|I(74), {0x03c5, 0x0313, 0x0342}}},
- {0x1f59, {1|F|D, {0x1f51}}},
- {0x1f5b, {1|F|D, {0x1f53}}},
- {0x1f5d, {1|F|D, {0x1f55}}},
- {0x1f5f, {1|F|D, {0x1f57}}},
- {0x1f68, {1|F|D, {0x1f60}}},
- {0x1f69, {1|F|D, {0x1f61}}},
- {0x1f6a, {1|F|D, {0x1f62}}},
- {0x1f6b, {1|F|D, {0x1f63}}},
- {0x1f6c, {1|F|D, {0x1f64}}},
- {0x1f6d, {1|F|D, {0x1f65}}},
- {0x1f6e, {1|F|D, {0x1f66}}},
- {0x1f6f, {1|F|D, {0x1f67}}},
- {0x1f80, {2|F|ST|SU|I(77), {0x1f00, 0x03b9}}},
- {0x1f81, {2|F|ST|SU|I(80), {0x1f01, 0x03b9}}},
- {0x1f82, {2|F|ST|SU|I(83), {0x1f02, 0x03b9}}},
- {0x1f83, {2|F|ST|SU|I(86), {0x1f03, 0x03b9}}},
- {0x1f84, {2|F|ST|SU|I(89), {0x1f04, 0x03b9}}},
- {0x1f85, {2|F|ST|SU|I(92), {0x1f05, 0x03b9}}},
- {0x1f86, {2|F|ST|SU|I(95), {0x1f06, 0x03b9}}},
- {0x1f87, {2|F|ST|SU|I(98), {0x1f07, 0x03b9}}},
- {0x1f88, {2|F|IT|SL|SU|I(101), {0x1f00, 0x03b9}}},
- {0x1f89, {2|F|IT|SL|SU|I(106), {0x1f01, 0x03b9}}},
- {0x1f8a, {2|F|IT|SL|SU|I(111), {0x1f02, 0x03b9}}},
- {0x1f8b, {2|F|IT|SL|SU|I(116), {0x1f03, 0x03b9}}},
- {0x1f8c, {2|F|IT|SL|SU|I(121), {0x1f04, 0x03b9}}},
- {0x1f8d, {2|F|IT|SL|SU|I(126), {0x1f05, 0x03b9}}},
- {0x1f8e, {2|F|IT|SL|SU|I(131), {0x1f06, 0x03b9}}},
- {0x1f8f, {2|F|IT|SL|SU|I(136), {0x1f07, 0x03b9}}},
- {0x1f90, {2|F|ST|SU|I(141), {0x1f20, 0x03b9}}},
- {0x1f91, {2|F|ST|SU|I(144), {0x1f21, 0x03b9}}},
- {0x1f92, {2|F|ST|SU|I(147), {0x1f22, 0x03b9}}},
- {0x1f93, {2|F|ST|SU|I(150), {0x1f23, 0x03b9}}},
- {0x1f94, {2|F|ST|SU|I(153), {0x1f24, 0x03b9}}},
- {0x1f95, {2|F|ST|SU|I(156), {0x1f25, 0x03b9}}},
- {0x1f96, {2|F|ST|SU|I(159), {0x1f26, 0x03b9}}},
- {0x1f97, {2|F|ST|SU|I(162), {0x1f27, 0x03b9}}},
- {0x1f98, {2|F|IT|SL|SU|I(165), {0x1f20, 0x03b9}}},
- {0x1f99, {2|F|IT|SL|SU|I(170), {0x1f21, 0x03b9}}},
- {0x1f9a, {2|F|IT|SL|SU|I(175), {0x1f22, 0x03b9}}},
- {0x1f9b, {2|F|IT|SL|SU|I(180), {0x1f23, 0x03b9}}},
- {0x1f9c, {2|F|IT|SL|SU|I(185), {0x1f24, 0x03b9}}},
- {0x1f9d, {2|F|IT|SL|SU|I(190), {0x1f25, 0x03b9}}},
- {0x1f9e, {2|F|IT|SL|SU|I(195), {0x1f26, 0x03b9}}},
- {0x1f9f, {2|F|IT|SL|SU|I(200), {0x1f27, 0x03b9}}},
- {0x1fa0, {2|F|ST|SU|I(205), {0x1f60, 0x03b9}}},
- {0x1fa1, {2|F|ST|SU|I(208), {0x1f61, 0x03b9}}},
- {0x1fa2, {2|F|ST|SU|I(211), {0x1f62, 0x03b9}}},
- {0x1fa3, {2|F|ST|SU|I(214), {0x1f63, 0x03b9}}},
- {0x1fa4, {2|F|ST|SU|I(217), {0x1f64, 0x03b9}}},
- {0x1fa5, {2|F|ST|SU|I(220), {0x1f65, 0x03b9}}},
- {0x1fa6, {2|F|ST|SU|I(223), {0x1f66, 0x03b9}}},
- {0x1fa7, {2|F|ST|SU|I(226), {0x1f67, 0x03b9}}},
- {0x1fa8, {2|F|IT|SL|SU|I(229), {0x1f60, 0x03b9}}},
- {0x1fa9, {2|F|IT|SL|SU|I(234), {0x1f61, 0x03b9}}},
- {0x1faa, {2|F|IT|SL|SU|I(239), {0x1f62, 0x03b9}}},
- {0x1fab, {2|F|IT|SL|SU|I(244), {0x1f63, 0x03b9}}},
- {0x1fac, {2|F|IT|SL|SU|I(249), {0x1f64, 0x03b9}}},
- {0x1fad, {2|F|IT|SL|SU|I(254), {0x1f65, 0x03b9}}},
- {0x1fae, {2|F|IT|SL|SU|I(259), {0x1f66, 0x03b9}}},
- {0x1faf, {2|F|IT|SL|SU|I(264), {0x1f67, 0x03b9}}},
- {0x1fb2, {2|F|ST|SU|I(269), {0x1f70, 0x03b9}}},
- {0x1fb3, {2|F|ST|SU|I(273), {0x03b1, 0x03b9}}},
- {0x1fb4, {2|F|ST|SU|I(276), {0x03ac, 0x03b9}}},
- {0x1fb6, {2|F|SU|I(280), {0x03b1, 0x0342}}},
- {0x1fb7, {3|F|ST|SU|I(282), {0x03b1, 0x0342, 0x03b9}}},
- {0x1fb8, {1|F|D, {0x1fb0}}},
- {0x1fb9, {1|F|D, {0x1fb1}}},
- {0x1fba, {1|F|D, {0x1f70}}},
- {0x1fbb, {1|F|D, {0x1f71}}},
- {0x1fbc, {2|F|IT|SL|SU|I(288), {0x03b1, 0x03b9}}},
- {0x1fbe, {1|F|SU|I(293), {0x03b9}}},
- {0x1fc2, {2|F|ST|SU|I(294), {0x1f74, 0x03b9}}},
- {0x1fc3, {2|F|ST|SU|I(298), {0x03b7, 0x03b9}}},
- {0x1fc4, {2|F|ST|SU|I(301), {0x03ae, 0x03b9}}},
- {0x1fc6, {2|F|SU|I(305), {0x03b7, 0x0342}}},
- {0x1fc7, {3|F|ST|SU|I(307), {0x03b7, 0x0342, 0x03b9}}},
- {0x1fc8, {1|F|D, {0x1f72}}},
- {0x1fc9, {1|F|D, {0x1f73}}},
- {0x1fca, {1|F|D, {0x1f74}}},
- {0x1fcb, {1|F|D, {0x1f75}}},
- {0x1fcc, {2|F|IT|SL|SU|I(313), {0x03b7, 0x03b9}}},
- {0x1fd2, {3|F|SU|I(318), {0x03b9, 0x0308, 0x0300}}},
- {0x1fd3, {3|F|SU|I(321), {0x03b9, 0x0308, 0x0301}}},
- {0x1fd6, {2|F|SU|I(324), {0x03b9, 0x0342}}},
- {0x1fd7, {3|F|SU|I(326), {0x03b9, 0x0308, 0x0342}}},
- {0x1fd8, {1|F|D, {0x1fd0}}},
- {0x1fd9, {1|F|D, {0x1fd1}}},
- {0x1fda, {1|F|D, {0x1f76}}},
- {0x1fdb, {1|F|D, {0x1f77}}},
- {0x1fe2, {3|F|SU|I(329), {0x03c5, 0x0308, 0x0300}}},
- {0x1fe3, {3|F|SU|I(332), {0x03c5, 0x0308, 0x0301}}},
- {0x1fe4, {2|F|SU|I(335), {0x03c1, 0x0313}}},
- {0x1fe6, {2|F|SU|I(337), {0x03c5, 0x0342}}},
- {0x1fe7, {3|F|SU|I(339), {0x03c5, 0x0308, 0x0342}}},
- {0x1fe8, {1|F|D, {0x1fe0}}},
- {0x1fe9, {1|F|D, {0x1fe1}}},
- {0x1fea, {1|F|D, {0x1f7a}}},
- {0x1feb, {1|F|D, {0x1f7b}}},
- {0x1fec, {1|F|D, {0x1fe5}}},
- {0x1ff2, {2|F|ST|SU|I(342), {0x1f7c, 0x03b9}}},
- {0x1ff3, {2|F|ST|SU|I(346), {0x03c9, 0x03b9}}},
- {0x1ff4, {2|F|ST|SU|I(349), {0x03ce, 0x03b9}}},
- {0x1ff6, {2|F|SU|I(353), {0x03c9, 0x0342}}},
- {0x1ff7, {3|F|ST|SU|I(355), {0x03c9, 0x0342, 0x03b9}}},
- {0x1ff8, {1|F|D, {0x1f78}}},
- {0x1ff9, {1|F|D, {0x1f79}}},
- {0x1ffa, {1|F|D, {0x1f7c}}},
- {0x1ffb, {1|F|D, {0x1f7d}}},
- {0x1ffc, {2|F|IT|SL|SU|I(361), {0x03c9, 0x03b9}}},
- {0x2126, {1|F|D, {0x03c9}}},
- {0x212a, {1|F|D, {0x006b}}},
- {0x212b, {1|F|D, {0x00e5}}},
- {0x2132, {1|F|D, {0x214e}}},
- {0x2160, {1|F|D, {0x2170}}},
- {0x2161, {1|F|D, {0x2171}}},
- {0x2162, {1|F|D, {0x2172}}},
- {0x2163, {1|F|D, {0x2173}}},
- {0x2164, {1|F|D, {0x2174}}},
- {0x2165, {1|F|D, {0x2175}}},
- {0x2166, {1|F|D, {0x2176}}},
- {0x2167, {1|F|D, {0x2177}}},
- {0x2168, {1|F|D, {0x2178}}},
- {0x2169, {1|F|D, {0x2179}}},
- {0x216a, {1|F|D, {0x217a}}},
- {0x216b, {1|F|D, {0x217b}}},
- {0x216c, {1|F|D, {0x217c}}},
- {0x216d, {1|F|D, {0x217d}}},
- {0x216e, {1|F|D, {0x217e}}},
- {0x216f, {1|F|D, {0x217f}}},
- {0x2183, {1|F|D, {0x2184}}},
- {0x24b6, {1|F|D, {0x24d0}}},
- {0x24b7, {1|F|D, {0x24d1}}},
- {0x24b8, {1|F|D, {0x24d2}}},
- {0x24b9, {1|F|D, {0x24d3}}},
- {0x24ba, {1|F|D, {0x24d4}}},
- {0x24bb, {1|F|D, {0x24d5}}},
- {0x24bc, {1|F|D, {0x24d6}}},
- {0x24bd, {1|F|D, {0x24d7}}},
- {0x24be, {1|F|D, {0x24d8}}},
- {0x24bf, {1|F|D, {0x24d9}}},
- {0x24c0, {1|F|D, {0x24da}}},
- {0x24c1, {1|F|D, {0x24db}}},
- {0x24c2, {1|F|D, {0x24dc}}},
- {0x24c3, {1|F|D, {0x24dd}}},
- {0x24c4, {1|F|D, {0x24de}}},
- {0x24c5, {1|F|D, {0x24df}}},
- {0x24c6, {1|F|D, {0x24e0}}},
- {0x24c7, {1|F|D, {0x24e1}}},
- {0x24c8, {1|F|D, {0x24e2}}},
- {0x24c9, {1|F|D, {0x24e3}}},
- {0x24ca, {1|F|D, {0x24e4}}},
- {0x24cb, {1|F|D, {0x24e5}}},
- {0x24cc, {1|F|D, {0x24e6}}},
- {0x24cd, {1|F|D, {0x24e7}}},
- {0x24ce, {1|F|D, {0x24e8}}},
- {0x24cf, {1|F|D, {0x24e9}}},
- {0x2c00, {1|F|D, {0x2c30}}},
- {0x2c01, {1|F|D, {0x2c31}}},
- {0x2c02, {1|F|D, {0x2c32}}},
- {0x2c03, {1|F|D, {0x2c33}}},
- {0x2c04, {1|F|D, {0x2c34}}},
- {0x2c05, {1|F|D, {0x2c35}}},
- {0x2c06, {1|F|D, {0x2c36}}},
- {0x2c07, {1|F|D, {0x2c37}}},
- {0x2c08, {1|F|D, {0x2c38}}},
- {0x2c09, {1|F|D, {0x2c39}}},
- {0x2c0a, {1|F|D, {0x2c3a}}},
- {0x2c0b, {1|F|D, {0x2c3b}}},
- {0x2c0c, {1|F|D, {0x2c3c}}},
- {0x2c0d, {1|F|D, {0x2c3d}}},
- {0x2c0e, {1|F|D, {0x2c3e}}},
- {0x2c0f, {1|F|D, {0x2c3f}}},
- {0x2c10, {1|F|D, {0x2c40}}},
- {0x2c11, {1|F|D, {0x2c41}}},
- {0x2c12, {1|F|D, {0x2c42}}},
- {0x2c13, {1|F|D, {0x2c43}}},
- {0x2c14, {1|F|D, {0x2c44}}},
- {0x2c15, {1|F|D, {0x2c45}}},
- {0x2c16, {1|F|D, {0x2c46}}},
- {0x2c17, {1|F|D, {0x2c47}}},
- {0x2c18, {1|F|D, {0x2c48}}},
- {0x2c19, {1|F|D, {0x2c49}}},
- {0x2c1a, {1|F|D, {0x2c4a}}},
- {0x2c1b, {1|F|D, {0x2c4b}}},
- {0x2c1c, {1|F|D, {0x2c4c}}},
- {0x2c1d, {1|F|D, {0x2c4d}}},
- {0x2c1e, {1|F|D, {0x2c4e}}},
- {0x2c1f, {1|F|D, {0x2c4f}}},
- {0x2c20, {1|F|D, {0x2c50}}},
- {0x2c21, {1|F|D, {0x2c51}}},
- {0x2c22, {1|F|D, {0x2c52}}},
- {0x2c23, {1|F|D, {0x2c53}}},
- {0x2c24, {1|F|D, {0x2c54}}},
- {0x2c25, {1|F|D, {0x2c55}}},
- {0x2c26, {1|F|D, {0x2c56}}},
- {0x2c27, {1|F|D, {0x2c57}}},
- {0x2c28, {1|F|D, {0x2c58}}},
- {0x2c29, {1|F|D, {0x2c59}}},
- {0x2c2a, {1|F|D, {0x2c5a}}},
- {0x2c2b, {1|F|D, {0x2c5b}}},
- {0x2c2c, {1|F|D, {0x2c5c}}},
- {0x2c2d, {1|F|D, {0x2c5d}}},
- {0x2c2e, {1|F|D, {0x2c5e}}},
- {0x2c60, {1|F|D, {0x2c61}}},
- {0x2c62, {1|F|D, {0x026b}}},
- {0x2c63, {1|F|D, {0x1d7d}}},
- {0x2c64, {1|F|D, {0x027d}}},
- {0x2c67, {1|F|D, {0x2c68}}},
- {0x2c69, {1|F|D, {0x2c6a}}},
- {0x2c6b, {1|F|D, {0x2c6c}}},
- {0x2c6d, {1|F|D, {0x0251}}},
- {0x2c6e, {1|F|D, {0x0271}}},
- {0x2c6f, {1|F|D, {0x0250}}},
- {0x2c70, {1|F|D, {0x0252}}},
- {0x2c72, {1|F|D, {0x2c73}}},
- {0x2c75, {1|F|D, {0x2c76}}},
- {0x2c7e, {1|F|D, {0x023f}}},
- {0x2c7f, {1|F|D, {0x0240}}},
- {0x2c80, {1|F|D, {0x2c81}}},
- {0x2c82, {1|F|D, {0x2c83}}},
- {0x2c84, {1|F|D, {0x2c85}}},
- {0x2c86, {1|F|D, {0x2c87}}},
- {0x2c88, {1|F|D, {0x2c89}}},
- {0x2c8a, {1|F|D, {0x2c8b}}},
- {0x2c8c, {1|F|D, {0x2c8d}}},
- {0x2c8e, {1|F|D, {0x2c8f}}},
- {0x2c90, {1|F|D, {0x2c91}}},
- {0x2c92, {1|F|D, {0x2c93}}},
- {0x2c94, {1|F|D, {0x2c95}}},
- {0x2c96, {1|F|D, {0x2c97}}},
- {0x2c98, {1|F|D, {0x2c99}}},
- {0x2c9a, {1|F|D, {0x2c9b}}},
- {0x2c9c, {1|F|D, {0x2c9d}}},
- {0x2c9e, {1|F|D, {0x2c9f}}},
- {0x2ca0, {1|F|D, {0x2ca1}}},
- {0x2ca2, {1|F|D, {0x2ca3}}},
- {0x2ca4, {1|F|D, {0x2ca5}}},
- {0x2ca6, {1|F|D, {0x2ca7}}},
- {0x2ca8, {1|F|D, {0x2ca9}}},
- {0x2caa, {1|F|D, {0x2cab}}},
- {0x2cac, {1|F|D, {0x2cad}}},
- {0x2cae, {1|F|D, {0x2caf}}},
- {0x2cb0, {1|F|D, {0x2cb1}}},
- {0x2cb2, {1|F|D, {0x2cb3}}},
- {0x2cb4, {1|F|D, {0x2cb5}}},
- {0x2cb6, {1|F|D, {0x2cb7}}},
- {0x2cb8, {1|F|D, {0x2cb9}}},
- {0x2cba, {1|F|D, {0x2cbb}}},
- {0x2cbc, {1|F|D, {0x2cbd}}},
- {0x2cbe, {1|F|D, {0x2cbf}}},
- {0x2cc0, {1|F|D, {0x2cc1}}},
- {0x2cc2, {1|F|D, {0x2cc3}}},
- {0x2cc4, {1|F|D, {0x2cc5}}},
- {0x2cc6, {1|F|D, {0x2cc7}}},
- {0x2cc8, {1|F|D, {0x2cc9}}},
- {0x2cca, {1|F|D, {0x2ccb}}},
- {0x2ccc, {1|F|D, {0x2ccd}}},
- {0x2cce, {1|F|D, {0x2ccf}}},
- {0x2cd0, {1|F|D, {0x2cd1}}},
- {0x2cd2, {1|F|D, {0x2cd3}}},
- {0x2cd4, {1|F|D, {0x2cd5}}},
- {0x2cd6, {1|F|D, {0x2cd7}}},
- {0x2cd8, {1|F|D, {0x2cd9}}},
- {0x2cda, {1|F|D, {0x2cdb}}},
- {0x2cdc, {1|F|D, {0x2cdd}}},
- {0x2cde, {1|F|D, {0x2cdf}}},
- {0x2ce0, {1|F|D, {0x2ce1}}},
- {0x2ce2, {1|F|D, {0x2ce3}}},
- {0x2ceb, {1|F|D, {0x2cec}}},
- {0x2ced, {1|F|D, {0x2cee}}},
- {0x2cf2, {1|F|D, {0x2cf3}}},
- {0xa640, {1|F|D, {0xa641}}},
- {0xa642, {1|F|D, {0xa643}}},
- {0xa644, {1|F|D, {0xa645}}},
- {0xa646, {1|F|D, {0xa647}}},
- {0xa648, {1|F|D, {0xa649}}},
- {0xa64a, {1|F|D, {0xa64b}}},
- {0xa64c, {1|F|D, {0xa64d}}},
- {0xa64e, {1|F|D, {0xa64f}}},
- {0xa650, {1|F|D, {0xa651}}},
- {0xa652, {1|F|D, {0xa653}}},
- {0xa654, {1|F|D, {0xa655}}},
- {0xa656, {1|F|D, {0xa657}}},
- {0xa658, {1|F|D, {0xa659}}},
- {0xa65a, {1|F|D, {0xa65b}}},
- {0xa65c, {1|F|D, {0xa65d}}},
- {0xa65e, {1|F|D, {0xa65f}}},
- {0xa660, {1|F|D, {0xa661}}},
- {0xa662, {1|F|D, {0xa663}}},
- {0xa664, {1|F|D, {0xa665}}},
- {0xa666, {1|F|D, {0xa667}}},
- {0xa668, {1|F|D, {0xa669}}},
- {0xa66a, {1|F|D, {0xa66b}}},
- {0xa66c, {1|F|D, {0xa66d}}},
- {0xa680, {1|F|D, {0xa681}}},
- {0xa682, {1|F|D, {0xa683}}},
- {0xa684, {1|F|D, {0xa685}}},
- {0xa686, {1|F|D, {0xa687}}},
- {0xa688, {1|F|D, {0xa689}}},
- {0xa68a, {1|F|D, {0xa68b}}},
- {0xa68c, {1|F|D, {0xa68d}}},
- {0xa68e, {1|F|D, {0xa68f}}},
- {0xa690, {1|F|D, {0xa691}}},
- {0xa692, {1|F|D, {0xa693}}},
- {0xa694, {1|F|D, {0xa695}}},
- {0xa696, {1|F|D, {0xa697}}},
- {0xa698, {1|F|D, {0xa699}}},
- {0xa69a, {1|F|D, {0xa69b}}},
- {0xa722, {1|F|D, {0xa723}}},
- {0xa724, {1|F|D, {0xa725}}},
- {0xa726, {1|F|D, {0xa727}}},
- {0xa728, {1|F|D, {0xa729}}},
- {0xa72a, {1|F|D, {0xa72b}}},
- {0xa72c, {1|F|D, {0xa72d}}},
- {0xa72e, {1|F|D, {0xa72f}}},
- {0xa732, {1|F|D, {0xa733}}},
- {0xa734, {1|F|D, {0xa735}}},
- {0xa736, {1|F|D, {0xa737}}},
- {0xa738, {1|F|D, {0xa739}}},
- {0xa73a, {1|F|D, {0xa73b}}},
- {0xa73c, {1|F|D, {0xa73d}}},
- {0xa73e, {1|F|D, {0xa73f}}},
- {0xa740, {1|F|D, {0xa741}}},
- {0xa742, {1|F|D, {0xa743}}},
- {0xa744, {1|F|D, {0xa745}}},
- {0xa746, {1|F|D, {0xa747}}},
- {0xa748, {1|F|D, {0xa749}}},
- {0xa74a, {1|F|D, {0xa74b}}},
- {0xa74c, {1|F|D, {0xa74d}}},
- {0xa74e, {1|F|D, {0xa74f}}},
- {0xa750, {1|F|D, {0xa751}}},
- {0xa752, {1|F|D, {0xa753}}},
- {0xa754, {1|F|D, {0xa755}}},
- {0xa756, {1|F|D, {0xa757}}},
- {0xa758, {1|F|D, {0xa759}}},
- {0xa75a, {1|F|D, {0xa75b}}},
- {0xa75c, {1|F|D, {0xa75d}}},
- {0xa75e, {1|F|D, {0xa75f}}},
- {0xa760, {1|F|D, {0xa761}}},
- {0xa762, {1|F|D, {0xa763}}},
- {0xa764, {1|F|D, {0xa765}}},
- {0xa766, {1|F|D, {0xa767}}},
- {0xa768, {1|F|D, {0xa769}}},
- {0xa76a, {1|F|D, {0xa76b}}},
- {0xa76c, {1|F|D, {0xa76d}}},
- {0xa76e, {1|F|D, {0xa76f}}},
- {0xa779, {1|F|D, {0xa77a}}},
- {0xa77b, {1|F|D, {0xa77c}}},
- {0xa77d, {1|F|D, {0x1d79}}},
- {0xa77e, {1|F|D, {0xa77f}}},
- {0xa780, {1|F|D, {0xa781}}},
- {0xa782, {1|F|D, {0xa783}}},
- {0xa784, {1|F|D, {0xa785}}},
- {0xa786, {1|F|D, {0xa787}}},
- {0xa78b, {1|F|D, {0xa78c}}},
- {0xa78d, {1|F|D, {0x0265}}},
- {0xa790, {1|F|D, {0xa791}}},
- {0xa792, {1|F|D, {0xa793}}},
- {0xa796, {1|F|D, {0xa797}}},
- {0xa798, {1|F|D, {0xa799}}},
- {0xa79a, {1|F|D, {0xa79b}}},
- {0xa79c, {1|F|D, {0xa79d}}},
- {0xa79e, {1|F|D, {0xa79f}}},
- {0xa7a0, {1|F|D, {0xa7a1}}},
- {0xa7a2, {1|F|D, {0xa7a3}}},
- {0xa7a4, {1|F|D, {0xa7a5}}},
- {0xa7a6, {1|F|D, {0xa7a7}}},
- {0xa7a8, {1|F|D, {0xa7a9}}},
- {0xa7aa, {1|F|D, {0x0266}}},
- {0xa7ab, {1|F|D, {0x025c}}},
- {0xa7ac, {1|F|D, {0x0261}}},
- {0xa7ad, {1|F|D, {0x026c}}},
- {0xa7ae, {1|F|D, {0x026a}}},
- {0xa7b0, {1|F|D, {0x029e}}},
- {0xa7b1, {1|F|D, {0x0287}}},
- {0xa7b2, {1|F|D, {0x029d}}},
- {0xa7b3, {1|F|D, {0xab53}}},
- {0xa7b4, {1|F|D, {0xa7b5}}},
- {0xa7b6, {1|F|D, {0xa7b7}}},
- {0xab70, {1|F|U, {0x13a0}}},
- {0xab71, {1|F|U, {0x13a1}}},
- {0xab72, {1|F|U, {0x13a2}}},
- {0xab73, {1|F|U, {0x13a3}}},
- {0xab74, {1|F|U, {0x13a4}}},
- {0xab75, {1|F|U, {0x13a5}}},
- {0xab76, {1|F|U, {0x13a6}}},
- {0xab77, {1|F|U, {0x13a7}}},
- {0xab78, {1|F|U, {0x13a8}}},
- {0xab79, {1|F|U, {0x13a9}}},
- {0xab7a, {1|F|U, {0x13aa}}},
- {0xab7b, {1|F|U, {0x13ab}}},
- {0xab7c, {1|F|U, {0x13ac}}},
- {0xab7d, {1|F|U, {0x13ad}}},
- {0xab7e, {1|F|U, {0x13ae}}},
- {0xab7f, {1|F|U, {0x13af}}},
- {0xab80, {1|F|U, {0x13b0}}},
- {0xab81, {1|F|U, {0x13b1}}},
- {0xab82, {1|F|U, {0x13b2}}},
- {0xab83, {1|F|U, {0x13b3}}},
- {0xab84, {1|F|U, {0x13b4}}},
- {0xab85, {1|F|U, {0x13b5}}},
- {0xab86, {1|F|U, {0x13b6}}},
- {0xab87, {1|F|U, {0x13b7}}},
- {0xab88, {1|F|U, {0x13b8}}},
- {0xab89, {1|F|U, {0x13b9}}},
- {0xab8a, {1|F|U, {0x13ba}}},
- {0xab8b, {1|F|U, {0x13bb}}},
- {0xab8c, {1|F|U, {0x13bc}}},
- {0xab8d, {1|F|U, {0x13bd}}},
- {0xab8e, {1|F|U, {0x13be}}},
- {0xab8f, {1|F|U, {0x13bf}}},
- {0xab90, {1|F|U, {0x13c0}}},
- {0xab91, {1|F|U, {0x13c1}}},
- {0xab92, {1|F|U, {0x13c2}}},
- {0xab93, {1|F|U, {0x13c3}}},
- {0xab94, {1|F|U, {0x13c4}}},
- {0xab95, {1|F|U, {0x13c5}}},
- {0xab96, {1|F|U, {0x13c6}}},
- {0xab97, {1|F|U, {0x13c7}}},
- {0xab98, {1|F|U, {0x13c8}}},
- {0xab99, {1|F|U, {0x13c9}}},
- {0xab9a, {1|F|U, {0x13ca}}},
- {0xab9b, {1|F|U, {0x13cb}}},
- {0xab9c, {1|F|U, {0x13cc}}},
- {0xab9d, {1|F|U, {0x13cd}}},
- {0xab9e, {1|F|U, {0x13ce}}},
- {0xab9f, {1|F|U, {0x13cf}}},
- {0xaba0, {1|F|U, {0x13d0}}},
- {0xaba1, {1|F|U, {0x13d1}}},
- {0xaba2, {1|F|U, {0x13d2}}},
- {0xaba3, {1|F|U, {0x13d3}}},
- {0xaba4, {1|F|U, {0x13d4}}},
- {0xaba5, {1|F|U, {0x13d5}}},
- {0xaba6, {1|F|U, {0x13d6}}},
- {0xaba7, {1|F|U, {0x13d7}}},
- {0xaba8, {1|F|U, {0x13d8}}},
- {0xaba9, {1|F|U, {0x13d9}}},
- {0xabaa, {1|F|U, {0x13da}}},
- {0xabab, {1|F|U, {0x13db}}},
- {0xabac, {1|F|U, {0x13dc}}},
- {0xabad, {1|F|U, {0x13dd}}},
- {0xabae, {1|F|U, {0x13de}}},
- {0xabaf, {1|F|U, {0x13df}}},
- {0xabb0, {1|F|U, {0x13e0}}},
- {0xabb1, {1|F|U, {0x13e1}}},
- {0xabb2, {1|F|U, {0x13e2}}},
- {0xabb3, {1|F|U, {0x13e3}}},
- {0xabb4, {1|F|U, {0x13e4}}},
- {0xabb5, {1|F|U, {0x13e5}}},
- {0xabb6, {1|F|U, {0x13e6}}},
- {0xabb7, {1|F|U, {0x13e7}}},
- {0xabb8, {1|F|U, {0x13e8}}},
- {0xabb9, {1|F|U, {0x13e9}}},
- {0xabba, {1|F|U, {0x13ea}}},
- {0xabbb, {1|F|U, {0x13eb}}},
- {0xabbc, {1|F|U, {0x13ec}}},
- {0xabbd, {1|F|U, {0x13ed}}},
- {0xabbe, {1|F|U, {0x13ee}}},
- {0xabbf, {1|F|U, {0x13ef}}},
- {0xfb00, {2|F|ST|SU|I(366), {0x0066, 0x0066}}},
- {0xfb01, {2|F|ST|SU|I(370), {0x0066, 0x0069}}},
- {0xfb02, {2|F|ST|SU|I(374), {0x0066, 0x006c}}},
- {0xfb03, {3|F|ST|SU|I(378), {0x0066, 0x0066, 0x0069}}},
- {0xfb04, {3|F|ST|SU|I(384), {0x0066, 0x0066, 0x006c}}},
- {0xfb05, {2|F|ST|SU|I(390), {0x0073, 0x0074}}},
- {0xfb06, {2|F|ST|SU|I(394), {0x0073, 0x0074}}},
- {0xfb13, {2|F|ST|SU|I(398), {0x0574, 0x0576}}},
- {0xfb14, {2|F|ST|SU|I(402), {0x0574, 0x0565}}},
- {0xfb15, {2|F|ST|SU|I(406), {0x0574, 0x056b}}},
- {0xfb16, {2|F|ST|SU|I(410), {0x057e, 0x0576}}},
- {0xfb17, {2|F|ST|SU|I(414), {0x0574, 0x056d}}},
- {0xff21, {1|F|D, {0xff41}}},
- {0xff22, {1|F|D, {0xff42}}},
- {0xff23, {1|F|D, {0xff43}}},
- {0xff24, {1|F|D, {0xff44}}},
- {0xff25, {1|F|D, {0xff45}}},
- {0xff26, {1|F|D, {0xff46}}},
- {0xff27, {1|F|D, {0xff47}}},
- {0xff28, {1|F|D, {0xff48}}},
- {0xff29, {1|F|D, {0xff49}}},
- {0xff2a, {1|F|D, {0xff4a}}},
- {0xff2b, {1|F|D, {0xff4b}}},
- {0xff2c, {1|F|D, {0xff4c}}},
- {0xff2d, {1|F|D, {0xff4d}}},
- {0xff2e, {1|F|D, {0xff4e}}},
- {0xff2f, {1|F|D, {0xff4f}}},
- {0xff30, {1|F|D, {0xff50}}},
- {0xff31, {1|F|D, {0xff51}}},
- {0xff32, {1|F|D, {0xff52}}},
- {0xff33, {1|F|D, {0xff53}}},
- {0xff34, {1|F|D, {0xff54}}},
- {0xff35, {1|F|D, {0xff55}}},
- {0xff36, {1|F|D, {0xff56}}},
- {0xff37, {1|F|D, {0xff57}}},
- {0xff38, {1|F|D, {0xff58}}},
- {0xff39, {1|F|D, {0xff59}}},
- {0xff3a, {1|F|D, {0xff5a}}},
- {0x10400, {1|F|D, {0x10428}}},
- {0x10401, {1|F|D, {0x10429}}},
- {0x10402, {1|F|D, {0x1042a}}},
- {0x10403, {1|F|D, {0x1042b}}},
- {0x10404, {1|F|D, {0x1042c}}},
- {0x10405, {1|F|D, {0x1042d}}},
- {0x10406, {1|F|D, {0x1042e}}},
- {0x10407, {1|F|D, {0x1042f}}},
- {0x10408, {1|F|D, {0x10430}}},
- {0x10409, {1|F|D, {0x10431}}},
- {0x1040a, {1|F|D, {0x10432}}},
- {0x1040b, {1|F|D, {0x10433}}},
- {0x1040c, {1|F|D, {0x10434}}},
- {0x1040d, {1|F|D, {0x10435}}},
- {0x1040e, {1|F|D, {0x10436}}},
- {0x1040f, {1|F|D, {0x10437}}},
- {0x10410, {1|F|D, {0x10438}}},
- {0x10411, {1|F|D, {0x10439}}},
- {0x10412, {1|F|D, {0x1043a}}},
- {0x10413, {1|F|D, {0x1043b}}},
- {0x10414, {1|F|D, {0x1043c}}},
- {0x10415, {1|F|D, {0x1043d}}},
- {0x10416, {1|F|D, {0x1043e}}},
- {0x10417, {1|F|D, {0x1043f}}},
- {0x10418, {1|F|D, {0x10440}}},
- {0x10419, {1|F|D, {0x10441}}},
- {0x1041a, {1|F|D, {0x10442}}},
- {0x1041b, {1|F|D, {0x10443}}},
- {0x1041c, {1|F|D, {0x10444}}},
- {0x1041d, {1|F|D, {0x10445}}},
- {0x1041e, {1|F|D, {0x10446}}},
- {0x1041f, {1|F|D, {0x10447}}},
- {0x10420, {1|F|D, {0x10448}}},
- {0x10421, {1|F|D, {0x10449}}},
- {0x10422, {1|F|D, {0x1044a}}},
- {0x10423, {1|F|D, {0x1044b}}},
- {0x10424, {1|F|D, {0x1044c}}},
- {0x10425, {1|F|D, {0x1044d}}},
- {0x10426, {1|F|D, {0x1044e}}},
- {0x10427, {1|F|D, {0x1044f}}},
- {0x104b0, {1|F|D, {0x104d8}}},
- {0x104b1, {1|F|D, {0x104d9}}},
- {0x104b2, {1|F|D, {0x104da}}},
- {0x104b3, {1|F|D, {0x104db}}},
- {0x104b4, {1|F|D, {0x104dc}}},
- {0x104b5, {1|F|D, {0x104dd}}},
- {0x104b6, {1|F|D, {0x104de}}},
- {0x104b7, {1|F|D, {0x104df}}},
- {0x104b8, {1|F|D, {0x104e0}}},
- {0x104b9, {1|F|D, {0x104e1}}},
- {0x104ba, {1|F|D, {0x104e2}}},
- {0x104bb, {1|F|D, {0x104e3}}},
- {0x104bc, {1|F|D, {0x104e4}}},
- {0x104bd, {1|F|D, {0x104e5}}},
- {0x104be, {1|F|D, {0x104e6}}},
- {0x104bf, {1|F|D, {0x104e7}}},
- {0x104c0, {1|F|D, {0x104e8}}},
- {0x104c1, {1|F|D, {0x104e9}}},
- {0x104c2, {1|F|D, {0x104ea}}},
- {0x104c3, {1|F|D, {0x104eb}}},
- {0x104c4, {1|F|D, {0x104ec}}},
- {0x104c5, {1|F|D, {0x104ed}}},
- {0x104c6, {1|F|D, {0x104ee}}},
- {0x104c7, {1|F|D, {0x104ef}}},
- {0x104c8, {1|F|D, {0x104f0}}},
- {0x104c9, {1|F|D, {0x104f1}}},
- {0x104ca, {1|F|D, {0x104f2}}},
- {0x104cb, {1|F|D, {0x104f3}}},
- {0x104cc, {1|F|D, {0x104f4}}},
- {0x104cd, {1|F|D, {0x104f5}}},
- {0x104ce, {1|F|D, {0x104f6}}},
- {0x104cf, {1|F|D, {0x104f7}}},
- {0x104d0, {1|F|D, {0x104f8}}},
- {0x104d1, {1|F|D, {0x104f9}}},
- {0x104d2, {1|F|D, {0x104fa}}},
- {0x104d3, {1|F|D, {0x104fb}}},
- {0x10c80, {1|F|D, {0x10cc0}}},
- {0x10c81, {1|F|D, {0x10cc1}}},
- {0x10c82, {1|F|D, {0x10cc2}}},
- {0x10c83, {1|F|D, {0x10cc3}}},
- {0x10c84, {1|F|D, {0x10cc4}}},
- {0x10c85, {1|F|D, {0x10cc5}}},
- {0x10c86, {1|F|D, {0x10cc6}}},
- {0x10c87, {1|F|D, {0x10cc7}}},
- {0x10c88, {1|F|D, {0x10cc8}}},
- {0x10c89, {1|F|D, {0x10cc9}}},
- {0x10c8a, {1|F|D, {0x10cca}}},
- {0x10c8b, {1|F|D, {0x10ccb}}},
- {0x10c8c, {1|F|D, {0x10ccc}}},
- {0x10c8d, {1|F|D, {0x10ccd}}},
- {0x10c8e, {1|F|D, {0x10cce}}},
- {0x10c8f, {1|F|D, {0x10ccf}}},
- {0x10c90, {1|F|D, {0x10cd0}}},
- {0x10c91, {1|F|D, {0x10cd1}}},
- {0x10c92, {1|F|D, {0x10cd2}}},
- {0x10c93, {1|F|D, {0x10cd3}}},
- {0x10c94, {1|F|D, {0x10cd4}}},
- {0x10c95, {1|F|D, {0x10cd5}}},
- {0x10c96, {1|F|D, {0x10cd6}}},
- {0x10c97, {1|F|D, {0x10cd7}}},
- {0x10c98, {1|F|D, {0x10cd8}}},
- {0x10c99, {1|F|D, {0x10cd9}}},
- {0x10c9a, {1|F|D, {0x10cda}}},
- {0x10c9b, {1|F|D, {0x10cdb}}},
- {0x10c9c, {1|F|D, {0x10cdc}}},
- {0x10c9d, {1|F|D, {0x10cdd}}},
- {0x10c9e, {1|F|D, {0x10cde}}},
- {0x10c9f, {1|F|D, {0x10cdf}}},
- {0x10ca0, {1|F|D, {0x10ce0}}},
- {0x10ca1, {1|F|D, {0x10ce1}}},
- {0x10ca2, {1|F|D, {0x10ce2}}},
- {0x10ca3, {1|F|D, {0x10ce3}}},
- {0x10ca4, {1|F|D, {0x10ce4}}},
- {0x10ca5, {1|F|D, {0x10ce5}}},
- {0x10ca6, {1|F|D, {0x10ce6}}},
- {0x10ca7, {1|F|D, {0x10ce7}}},
- {0x10ca8, {1|F|D, {0x10ce8}}},
- {0x10ca9, {1|F|D, {0x10ce9}}},
- {0x10caa, {1|F|D, {0x10cea}}},
- {0x10cab, {1|F|D, {0x10ceb}}},
- {0x10cac, {1|F|D, {0x10cec}}},
- {0x10cad, {1|F|D, {0x10ced}}},
- {0x10cae, {1|F|D, {0x10cee}}},
- {0x10caf, {1|F|D, {0x10cef}}},
- {0x10cb0, {1|F|D, {0x10cf0}}},
- {0x10cb1, {1|F|D, {0x10cf1}}},
- {0x10cb2, {1|F|D, {0x10cf2}}},
- {0x118a0, {1|F|D, {0x118c0}}},
- {0x118a1, {1|F|D, {0x118c1}}},
- {0x118a2, {1|F|D, {0x118c2}}},
- {0x118a3, {1|F|D, {0x118c3}}},
- {0x118a4, {1|F|D, {0x118c4}}},
- {0x118a5, {1|F|D, {0x118c5}}},
- {0x118a6, {1|F|D, {0x118c6}}},
- {0x118a7, {1|F|D, {0x118c7}}},
- {0x118a8, {1|F|D, {0x118c8}}},
- {0x118a9, {1|F|D, {0x118c9}}},
- {0x118aa, {1|F|D, {0x118ca}}},
- {0x118ab, {1|F|D, {0x118cb}}},
- {0x118ac, {1|F|D, {0x118cc}}},
- {0x118ad, {1|F|D, {0x118cd}}},
- {0x118ae, {1|F|D, {0x118ce}}},
- {0x118af, {1|F|D, {0x118cf}}},
- {0x118b0, {1|F|D, {0x118d0}}},
- {0x118b1, {1|F|D, {0x118d1}}},
- {0x118b2, {1|F|D, {0x118d2}}},
- {0x118b3, {1|F|D, {0x118d3}}},
- {0x118b4, {1|F|D, {0x118d4}}},
- {0x118b5, {1|F|D, {0x118d5}}},
- {0x118b6, {1|F|D, {0x118d6}}},
- {0x118b7, {1|F|D, {0x118d7}}},
- {0x118b8, {1|F|D, {0x118d8}}},
- {0x118b9, {1|F|D, {0x118d9}}},
- {0x118ba, {1|F|D, {0x118da}}},
- {0x118bb, {1|F|D, {0x118db}}},
- {0x118bc, {1|F|D, {0x118dc}}},
- {0x118bd, {1|F|D, {0x118dd}}},
- {0x118be, {1|F|D, {0x118de}}},
- {0x118bf, {1|F|D, {0x118df}}},
- {0x1e900, {1|F|D, {0x1e922}}},
- {0x1e901, {1|F|D, {0x1e923}}},
- {0x1e902, {1|F|D, {0x1e924}}},
- {0x1e903, {1|F|D, {0x1e925}}},
- {0x1e904, {1|F|D, {0x1e926}}},
- {0x1e905, {1|F|D, {0x1e927}}},
- {0x1e906, {1|F|D, {0x1e928}}},
- {0x1e907, {1|F|D, {0x1e929}}},
- {0x1e908, {1|F|D, {0x1e92a}}},
- {0x1e909, {1|F|D, {0x1e92b}}},
- {0x1e90a, {1|F|D, {0x1e92c}}},
- {0x1e90b, {1|F|D, {0x1e92d}}},
- {0x1e90c, {1|F|D, {0x1e92e}}},
- {0x1e90d, {1|F|D, {0x1e92f}}},
- {0x1e90e, {1|F|D, {0x1e930}}},
- {0x1e90f, {1|F|D, {0x1e931}}},
- {0x1e910, {1|F|D, {0x1e932}}},
- {0x1e911, {1|F|D, {0x1e933}}},
- {0x1e912, {1|F|D, {0x1e934}}},
- {0x1e913, {1|F|D, {0x1e935}}},
- {0x1e914, {1|F|D, {0x1e936}}},
- {0x1e915, {1|F|D, {0x1e937}}},
- {0x1e916, {1|F|D, {0x1e938}}},
- {0x1e917, {1|F|D, {0x1e939}}},
- {0x1e918, {1|F|D, {0x1e93a}}},
- {0x1e919, {1|F|D, {0x1e93b}}},
- {0x1e91a, {1|F|D, {0x1e93c}}},
- {0x1e91b, {1|F|D, {0x1e93d}}},
- {0x1e91c, {1|F|D, {0x1e93e}}},
- {0x1e91d, {1|F|D, {0x1e93f}}},
- {0x1e91e, {1|F|D, {0x1e940}}},
- {0x1e91f, {1|F|D, {0x1e941}}},
- {0x1e920, {1|F|D, {0x1e942}}},
- {0x1e921, {1|F|D, {0x1e943}}},
-#define CaseFold_Locale (*(CaseFold_11_Type (*)[2])(CaseFold_11_Table+1399))
- {0x0049, {1|F|D, {0x0069}}},
- {0x0130, {2|F|D, {0x0069, 0x0307}}},
-};
-
-/* C code produced by gperf version 3.0.4 */
-/* Command-line: gperf -7 -k1,2,3 -F,-1 -c -j1 -i1 -t -T -E -C -H onigenc_unicode_CaseFold_11_hash -N onigenc_unicode_CaseFold_11_lookup -n */
-
-/* maximum key range = 3623, duplicates = 0 */
-
-#ifdef __GNUC__
-__inline
-#else
-#ifdef __cplusplus
-inline
-#endif
-#endif
-/*ARGSUSED*/
-static unsigned int
-onigenc_unicode_CaseFold_11_hash(const OnigCodePoint code)
-{
- static const unsigned short asso_values[] =
- {
- 1, 3627, 2, 28, 3, 303, 218, 5, 21, 167,
- 2, 199, 194, 7, 3627, 3627, 3627, 3627, 3627, 3627,
- 3627, 3627, 3627, 3627, 3627, 3627, 3627, 28, 3627, 3627,
- 3627, 3627, 3627, 3627, 3627, 282, 3627, 3627, 3627, 3627,
- 3627, 113, 3627, 3627, 3627, 3627, 3627, 3627, 3627, 3627,
- 3627, 318, 3627, 3627, 3627, 3627, 3627, 3627, 3627, 1197,
- 3627, 3627, 149, 73, 513, 1, 3627, 3627, 267, 17,
- 3627, 3627, 3627, 3627, 3627, 286, 3627, 3627, 289, 617,
- 291, 28, 1163, 444, 36, 430, 954, 110, 1767, 5,
- 11, 22, 1761, 486, 1921, 250, 1746, 122, 1905, 163,
- 1716, 262, 1880, 80, 1503, 68, 1704, 157, 1681, 376,
- 1673, 209, 1822, 203, 1406, 498, 1622, 362, 1588, 316,
- 475, 599, 1228, 538, 1160, 585, 1510, 544, 331, 685,
- 1672, 603, 1536, 840, 1684, 786, 1594, 743, 1380, 700,
- 142, 839, 1302, 865, 1173, 1329, 1730, 1043, 1449, 969,
- 1437, 1108, 1360, 925, 1497, 723, 154, 810, 391, 1083,
- 1777, 1047, 436, 1051, 47, 1342, 8, 974, 98, 1318,
- 781, 1314, 148, 1403, 39, 1357, 18, 1265, 11, 928,
- 92, 1205, 2, 1295, 3, 1181, 187, 1151, 385, 1132,
- 107, 1280, 8, 1678, 41, 511, 42, 1655, 78, 294,
- 134, 1857, 17, 784, 2, 1113, 57, 496, 52
- };
- return asso_values[bits_of(code, 2)+81] + asso_values[bits_of(code, 1)+2] + asso_values[bits_of(code, 0)];
-}
-
-#ifdef __GNUC__
-__inline
-#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
-__attribute__ ((__gnu_inline__))
-#endif
-#endif
-static const CodePointList3 *
-onigenc_unicode_CaseFold_11_lookup(const OnigCodePoint code)
-{
- enum
- {
- MIN_CODE_VALUE = 0x41,
- MAX_CODE_VALUE = 0x1e921,
- TOTAL_KEYWORDS = 1401,
- MIN_WORD_LENGTH = 3,
- MAX_WORD_LENGTH = 3,
- MIN_HASH_VALUE = 4,
- MAX_HASH_VALUE = 3626
- };
-
- static const short wordlist[] =
- {
- -1, -1, -1, -1,
- /*0x1ffb*/ 816,
- /*0x1fe7*/ 802,
- /*0x017b*/ 118,
- /*0x1f88*/ 729,
- /*0x0408*/ 305,
- /*0x0108*/ 61,
- /*0x10408*/ 1214,
- /*0x0055*/ 19,
- /*0xab88*/ 1112,
- /*0x1f89*/ 730,
- /*0x0409*/ 306,
- /*0x2c67*/ 916,
- /*0x10409*/ 1215,
- /*0x2c08*/ 873,
- /*0xab89*/ 1113,
- /*0x1ff9*/ 814,
- /*0x2c6f*/ 921,
- /*0x0179*/ 117,
- -1,
- /*0x2c09*/ 874,
- /*0x1f8a*/ 731,
- /*0x040a*/ 307,
- /*0x010a*/ 62,
- /*0x1040a*/ 1216,
- /*0x2c88*/ 931,
- /*0xab8a*/ 1114,
- /*0x1f80*/ 721,
- /*0x0400*/ 297,
- /*0x0100*/ 57,
- /*0x10400*/ 1206,
- /*0x2c0a*/ 875,
- /*0xab80*/ 1104,
- /*0x10c88*/ 1290,
- /*0x00d5*/ 47,
- /*0x1f83*/ 724,
- /*0x0403*/ 300,
- /*0x2c00*/ 865,
- /*0x10403*/ 1209,
- /*0x10c89*/ 1291,
- /*0xab83*/ 1107,
- /*0x1ff3*/ 809,
- /*0x2c8a*/ 932,
- /*0x1e908*/ 1373,
- /*0x00df*/ 56,
- /*0x2c03*/ 868,
- /*0x1fd3*/ 791,
- /*0x0053*/ 17,
- /*0x2c80*/ 927,
- /*0x1e909*/ 1374,
- /*0x10c8a*/ 1292,
- /*0x2183*/ 838,
- -1,
- /*0x017f*/ 120,
- -1,
- /*0xa780*/ 1059,
- /*0x10c80*/ 1282,
- -1,
- /*0x017d*/ 119,
- -1,
- /*0x1e90a*/ 1375,
- /*0x2c7f*/ 926,
- -1, -1,
- /*0x10c83*/ 1285,
- /*0x00dd*/ 54,
- /*0x1e900*/ 1365,
- /*0x1f98*/ 745,
- /*0x0418*/ 321,
- /*0x0118*/ 69,
- /*0x10418*/ 1230,
- /*0x037f*/ 233,
- /*0xab98*/ 1128,
- /*0x00d3*/ 45,
- /*0x1e903*/ 1368,
- -1,
- /*0x1e88*/ 607,
- /*0x2c18*/ 889,
- -1,
- /*0x1f96*/ 743,
- /*0x0416*/ 319,
- /*0x0116*/ 68,
- /*0x10416*/ 1228,
- -1,
- /*0xab96*/ 1126,
- -1, -1,
- /*0x2c75*/ 924,
- /*0x2c98*/ 939,
- /*0x2c16*/ 887,
- -1,
- /*0x1fe3*/ 799,
- -1,
- /*0x1e8a*/ 608,
- -1,
- /*0xa798*/ 1068,
- /*0x10c98*/ 1306,
- /*0x1fd7*/ 793,
- /*0x0057*/ 21,
- /*0x1e80*/ 603,
- /*0x2c96*/ 938,
- /*0x2c63*/ 914,
- -1, -1, -1, -1,
- /*0x1e918*/ 1389,
- /*0xa796*/ 1067,
- /*0x10c96*/ 1304,
- /*0x1f86*/ 727,
- /*0x0406*/ 303,
- /*0x0106*/ 60,
- /*0x10406*/ 1212,
- /*0x13fb*/ 527,
- /*0xab86*/ 1110,
- -1,
- /*0x2c6d*/ 919,
- -1,
- /*0x1e916*/ 1387,
- /*0x2c06*/ 871,
- -1,
- /*0x1f90*/ 737,
- /*0x0410*/ 313,
- /*0x0110*/ 65,
- /*0x10410*/ 1222,
- -1,
- /*0xab90*/ 1120,
- /*0x2ced*/ 978,
- /*0x13f9*/ 525,
- -1,
- /*0x2c86*/ 930,
- /*0x2c10*/ 881,
- -1,
- /*0x1ff7*/ 812,
- -1, -1, -1,
- /*0xa786*/ 1062,
- /*0x10c86*/ 1288,
- /*0x1e98*/ 616,
- -1,
- /*0x1fbb*/ 777,
- /*0x2c90*/ 935,
- /*0x013b*/ 85,
- -1, -1,
- /*0xabbb*/ 1163,
- /*0x1fdb*/ 797,
- /*0x1e906*/ 1371,
- /*0xa790*/ 1065,
- /*0x10c90*/ 1298,
- /*0x1e96*/ 614,
- /*0x1e08*/ 543,
- /*0x1fcb*/ 788,
- /*0x004b*/ 9,
- -1,
- /*0x1f9a*/ 747,
- /*0x041a*/ 323,
- /*0x011a*/ 70,
- /*0x1041a*/ 1232,
- /*0x1e910*/ 1381,
- /*0xab9a*/ 1130,
- /*0x1f92*/ 739,
- /*0x0412*/ 315,
- /*0x0112*/ 66,
- /*0x10412*/ 1224,
- /*0x2c1a*/ 891,
- /*0xab92*/ 1122,
- /*0x13fd*/ 529,
- /*0x1e0a*/ 544,
- /*0x0388*/ 235,
- -1,
- /*0x2c12*/ 883,
- /*0x03d5*/ 273,
- /*0x00db*/ 52,
- /*0x1e00*/ 539,
- /*0x0389*/ 236,
- /*0x2c9a*/ 940,
- -1, -1,
- /*0x00cb*/ 37,
- /*0x1e86*/ 606,
- /*0x03f9*/ 292,
- /*0x2c92*/ 936,
- /*0xa79a*/ 1069,
- /*0x10c9a*/ 1308,
- /*0x1fe9*/ 804,
- /*0x038a*/ 237,
- -1, -1,
- /*0xa792*/ 1066,
- /*0x10c92*/ 1300,
- -1,
- /*0x1e90*/ 611,
- -1,
- /*0x1e91a*/ 1391,
- /*0x2c69*/ 917,
- /*0x0508*/ 425,
- -1, -1,
- /*0x0555*/ 481,
- /*0x1e912*/ 1383,
- /*0x1fa0*/ 753,
- /*0x0420*/ 329,
- /*0x0120*/ 73,
- /*0x10420*/ 1238,
- /*0x03f1*/ 288,
- /*0xaba0*/ 1136,
- /*0x1f9e*/ 751,
- /*0x041e*/ 327,
- /*0x011e*/ 72,
- /*0x1041e*/ 1236,
- /*0x2c20*/ 897,
- /*0xab9e*/ 1134,
- /*0x050a*/ 426,
- /*0x1e18*/ 551,
- -1,
- /*0x03ff*/ 296,
- /*0x2c1e*/ 895,
- /*0x048a*/ 362,
- /*0x0500*/ 421,
- /*0x0208*/ 194,
- /*0x03fd*/ 294,
- /*0x2ca0*/ 943,
- -1,
- /*0x0480*/ 361,
- -1,
- /*0x1e16*/ 550,
- /*0x1e9a*/ 618,
- /*0x2c9e*/ 942,
- /*0xa7a0*/ 1072,
- /*0x10ca0*/ 1314,
- -1,
- /*0x0398*/ 249,
- /*0x1e92*/ 612,
- -1,
- /*0xa79e*/ 1071,
- /*0x10c9e*/ 1312,
- /*0x020a*/ 195,
- /*0x0553*/ 479,
- -1,
- /*0x1e920*/ 1397,
- -1,
- /*0x03f5*/ 290,
- /*0x0200*/ 190,
- /*0x0396*/ 247,
- /*0x104d3*/ 1281,
- /*0x1e91e*/ 1395,
- -1,
- /*0x1f8e*/ 735,
- /*0x040e*/ 311,
- /*0x010e*/ 64,
- /*0x1040e*/ 1220,
- -1,
- /*0xab8e*/ 1118,
- -1, -1,
- /*0x1e06*/ 542,
- -1,
- /*0x2c0e*/ 879,
- /*0x0518*/ 433,
- /*0x1f94*/ 741,
- /*0x0414*/ 317,
- /*0x0114*/ 67,
- /*0x10414*/ 1226,
- /*0x0498*/ 369,
- /*0xab94*/ 1124,
- /*0x2165*/ 827,
- /*0x2167*/ 829,
- /*0x1e10*/ 547,
- /*0x2c8e*/ 934,
- /*0x2c14*/ 885,
- /*0x0516*/ 432,
- /*0x216f*/ 837,
- /*0x1ea0*/ 621,
- /*0x0386*/ 234,
- /*0x2161*/ 823,
- /*0x0496*/ 368,
- /*0x10c8e*/ 1296,
- -1,
- /*0x1e9e*/ 620,
- -1,
- /*0x2c94*/ 937,
- -1,
- /*0x0218*/ 202,
- -1, -1,
- /*0x0390*/ 241,
- /*0x1e90e*/ 1379,
- -1,
- /*0x10c94*/ 1302,
- -1,
- /*0xa77b*/ 1056,
- /*0x1ff6*/ 811,
- /*0x0476*/ 356,
- /*0x0176*/ 115,
- /*0x0216*/ 201,
- -1, -1,
- /*0x03f7*/ 291,
- /*0x1e914*/ 1385,
- -1,
- /*0x0506*/ 424,
- -1,
- /*0x1e1a*/ 552,
- -1, -1,
- /*0xa779*/ 1055,
- -1,
- /*0x01d5*/ 167,
- /*0x1e12*/ 548,
- -1,
- /*0x0189*/ 126,
- /*0x0376*/ 232,
- /*0x0510*/ 429,
- /*0x1fa6*/ 759,
- /*0x0426*/ 335,
- /*0x0126*/ 76,
- /*0x10426*/ 1244,
- /*0x0490*/ 365,
- /*0xaba6*/ 1142,
- /*0x1e8e*/ 610,
- /*0x039a*/ 251,
- /*0x018a*/ 127,
- -1,
- /*0x2c26*/ 903,
- /*0x0206*/ 193,
- -1,
- /*0x0392*/ 243,
- -1,
- /*0x1faf*/ 768,
- /*0x042f*/ 344,
- -1,
- /*0x1e94*/ 613,
- /*0x053b*/ 455,
- /*0xabaf*/ 1151,
- /*0x2ca6*/ 946,
- -1,
- /*0x0210*/ 198,
- -1, -1,
- /*0x104bb*/ 1257,
- /*0x01f1*/ 181,
- /*0xa7a6*/ 1075,
- /*0x10ca6*/ 1320,
- -1,
- /*0x054b*/ 471,
- /*0xa77d*/ 1057,
- /*0x01d3*/ 166,
- /*0x051a*/ 434,
- /*0x1e20*/ 555,
- /*0x04cb*/ 395,
- -1,
- /*0x104cb*/ 1273,
- /*0x049a*/ 370,
- /*0x0512*/ 430,
- /*0x1e1e*/ 554,
- /*0x2163*/ 825,
- /*0x023b*/ 217,
- /*0x10caf*/ 1329,
- /*0x0492*/ 366,
- /*0x1fa4*/ 757,
- /*0x0424*/ 333,
- /*0x0124*/ 75,
- /*0x10424*/ 1242,
- /*0x1ef6*/ 664,
- /*0xaba4*/ 1140,
- -1,
- /*0x03a0*/ 257,
- /*0x0198*/ 137,
- -1,
- /*0x2c24*/ 901,
- /*0x216d*/ 835,
- /*0x021a*/ 203,
- /*0x039e*/ 255,
- /*0x1f9c*/ 749,
- /*0x041c*/ 325,
- /*0x011c*/ 71,
- /*0x1041c*/ 1234,
- /*0x0212*/ 199,
- /*0xab9c*/ 1132,
- /*0x0196*/ 135,
- /*0x2ca4*/ 945,
- -1,
- /*0x1feb*/ 806,
- /*0x2c1c*/ 893,
- -1,
- /*0x1ea6*/ 624,
- -1,
- /*0xa7a4*/ 1074,
- /*0x10ca4*/ 1318,
- /*0x004d*/ 11,
- -1, -1,
- /*0x2c6b*/ 918,
- /*0x0520*/ 437,
- /*0x2c9c*/ 941,
- /*0x1e0e*/ 546,
- -1,
- /*0x01d7*/ 168,
- /*0x04a0*/ 373,
- /*0x051e*/ 436,
- -1,
- /*0xa79c*/ 1070,
- /*0x10c9c*/ 1310,
- /*0x2ceb*/ 977,
- /*0x049e*/ 372,
- -1, -1,
- /*0x1e14*/ 549,
- -1,
- /*0x0186*/ 124,
- -1, -1,
- /*0x1e91c*/ 1393,
- /*0x038e*/ 239,
- -1,
- /*0x00cd*/ 39,
- -1,
- /*0x0220*/ 206,
- -1, -1,
- /*0x10bb*/ 511,
- /*0x0190*/ 131,
- -1,
- /*0x021e*/ 205,
- /*0x24bb*/ 844,
- /*0x0394*/ 245,
- -1,
- /*0x1f84*/ 725,
- /*0x0404*/ 301,
- /*0x0104*/ 59,
- /*0x10404*/ 1210,
- /*0x1ea4*/ 623,
- /*0xab84*/ 1108,
- /*0x01f7*/ 185,
- /*0x0051*/ 15,
- -1,
- /*0x24cb*/ 860,
- /*0x2c04*/ 869,
- -1,
- /*0x1e76*/ 598,
- /*0x050e*/ 428,
- /*0x1f82*/ 723,
- /*0x0402*/ 299,
- /*0x0102*/ 58,
- /*0x10402*/ 1208,
- /*0x048e*/ 364,
- /*0xab82*/ 1106,
- /*0x01db*/ 170,
- /*0x2c84*/ 929,
- -1,
- /*0x2169*/ 831,
- /*0x2c02*/ 867,
- /*0x0514*/ 431,
- /*0x01cb*/ 162,
- -1,
- /*0xa784*/ 1061,
- /*0x10c84*/ 1286,
- /*0x0494*/ 367,
- /*0x118bb*/ 1360,
- -1,
- /*0x00d1*/ 43,
- /*0x1e26*/ 558,
- /*0x2c82*/ 928,
- -1,
- /*0x020e*/ 197,
- -1,
- /*0x1e904*/ 1369,
- -1, -1,
- /*0xa782*/ 1060,
- /*0x10c82*/ 1284,
- -1,
- /*0x1fa7*/ 760,
- /*0x0427*/ 336,
- -1,
- /*0x10427*/ 1245,
- /*0x0214*/ 200,
- /*0xaba7*/ 1143,
- -1,
- /*0x03a6*/ 262,
- /*0x1e902*/ 1367,
- /*0x10a0*/ 484,
- /*0x2c27*/ 904,
- /*0x1f8c*/ 733,
- /*0x040c*/ 309,
- /*0x010c*/ 63,
- /*0x1040c*/ 1218,
- -1,
- /*0xab8c*/ 1116,
- /*0x04f6*/ 416,
- -1, -1, -1,
- /*0x2c0c*/ 877,
- /*0x047e*/ 360,
- /*0x1fa2*/ 755,
- /*0x0422*/ 331,
- /*0x0122*/ 74,
- /*0x10422*/ 1240,
- /*0x1e84*/ 605,
- /*0xaba2*/ 1138,
- /*0x10ca7*/ 1321,
- /*0x01a0*/ 141,
- /*0x2c7e*/ 925,
- /*0x2c8c*/ 933,
- /*0x2c22*/ 899,
- /*0x0526*/ 440,
- /*0x1e24*/ 557,
- /*0x1ff2*/ 808,
- /*0x0472*/ 354,
- /*0x0172*/ 113,
- /*0x04a6*/ 376,
- /*0x10c8c*/ 1294,
- /*0x1e82*/ 604,
- /*0x1f08*/ 669,
- -1,
- /*0x2ca2*/ 944,
- /*0x1f6f*/ 720,
- /*0x2c72*/ 923,
- /*0x118a0*/ 1333,
- /*0x1f09*/ 670,
- /*0x1e1c*/ 553,
- /*0x1e90c*/ 1377,
- /*0xa7a2*/ 1073,
- /*0x10ca2*/ 1316,
- /*0x03a4*/ 260,
- /*0xfb00*/ 1168,
- /*0x1f5f*/ 712,
- /*0x0372*/ 231,
- /*0x2cf2*/ 979,
- /*0x0226*/ 209,
- /*0x1f0a*/ 671,
- -1, -1,
- /*0xfb03*/ 1171,
- /*0x1faa*/ 763,
- /*0x042a*/ 339,
- /*0x012a*/ 78,
- -1,
- /*0x039c*/ 253,
- /*0xabaa*/ 1146,
- /*0x1fae*/ 767,
- /*0x042e*/ 343,
- /*0x012e*/ 80,
- -1,
- /*0x2c2a*/ 907,
- /*0xabae*/ 1150,
- -1,
- /*0x1f5d*/ 711,
- /*0x018e*/ 129,
- -1,
- /*0x2c2e*/ 911,
- /*0x0524*/ 439,
- -1, -1,
- /*0x1e8c*/ 609,
- /*0x2caa*/ 948,
- /*0x04a4*/ 375,
- -1, -1, -1,
- /*0x0194*/ 134,
- /*0x2cae*/ 950,
- /*0xa7aa*/ 1077,
- /*0x10caa*/ 1324,
- /*0x1efe*/ 668,
- /*0x051c*/ 435,
- /*0x1ea2*/ 622,
- -1,
- /*0xa7ae*/ 1081,
- /*0x10cae*/ 1328,
- /*0x049c*/ 371,
- -1, -1, -1,
- /*0x1e04*/ 541,
- /*0x0224*/ 208,
- /*0x1f18*/ 677,
- /*0xfb16*/ 1178,
- /*0x2126*/ 818,
- /*0x1ef2*/ 662,
- /*0x054d*/ 473,
- /*0x1fac*/ 765,
- /*0x042c*/ 341,
- /*0x012c*/ 79,
- -1,
- /*0x04cd*/ 396,
- /*0xabac*/ 1148,
- /*0x104cd*/ 1275,
- /*0x1e02*/ 540,
- /*0x021c*/ 204,
- -1,
- /*0x2c2c*/ 909,
- /*0x01f6*/ 184,
- /*0x10a6*/ 490,
- -1,
- /*0x1fa8*/ 761,
- /*0x0428*/ 337,
- /*0x0128*/ 77,
- /*0x03d1*/ 272,
- /*0x1fb2*/ 769,
- /*0xaba8*/ 1144,
- /*0x0132*/ 81,
- /*0x2cac*/ 949,
- /*0xa726*/ 1019,
- /*0xabb2*/ 1154,
- /*0x2c28*/ 905,
- /*0x1eaa*/ 626,
- /*0xfb06*/ 1174,
- /*0x10af*/ 499,
- /*0xa7ac*/ 1079,
- /*0x10cac*/ 1326,
- -1,
- /*0x1eae*/ 628,
- -1,
- /*0x01a6*/ 144,
- /*0x1f6d*/ 718,
- /*0x2ca8*/ 947,
- -1,
- /*0xa688*/ 1007,
- /*0x0504*/ 423,
- /*0x2cb2*/ 952,
- -1, -1,
- /*0xa7a8*/ 1076,
- /*0x10ca8*/ 1322,
- /*0x0551*/ 477,
- -1,
- /*0xa7b2*/ 1084,
- /*0x10cb2*/ 1332,
- /*0x01af*/ 149,
- /*0x1e0c*/ 545,
- /*0x118a6*/ 1339,
- /*0x104d1*/ 1279,
- /*0x0502*/ 422,
- -1,
- /*0xa68a*/ 1008,
- -1,
- /*0x03a7*/ 263,
- -1,
- /*0x10a4*/ 488,
- /*0x1e7e*/ 602,
- /*0xa680*/ 1003,
- /*0x1e22*/ 556,
- /*0x0204*/ 192,
- -1, -1,
- /*0x118af*/ 1348,
- /*0x216b*/ 833,
- /*0x038c*/ 238,
- /*0xa724*/ 1018,
- /*0x1f3b*/ 694,
- -1, -1,
- /*0x1eac*/ 627,
- -1,
- /*0x1e72*/ 596,
- /*0x1f5b*/ 710,
- /*0x0202*/ 191,
- /*0x03fe*/ 295,
- -1,
- /*0x01a4*/ 143,
- -1,
- /*0x1f4b*/ 702,
- -1, -1,
- /*0x1f1a*/ 679,
- -1,
- /*0x1ea8*/ 625,
- /*0x10cd*/ 523,
- /*0xff26*/ 1185,
- -1,
- /*0x1eb2*/ 630,
- /*0x24cd*/ 862,
- -1,
- /*0x019c*/ 138,
- /*0x050c*/ 427,
- -1,
- /*0x118a4*/ 1337,
- -1, -1,
- /*0x048c*/ 363,
- /*0xa698*/ 1015,
- /*0x1e2a*/ 560,
- /*0x0130*/ 1400,
- /*0xff2f*/ 1194,
- -1,
- /*0xabb0*/ 1152,
- /*0x0522*/ 438,
- /*0x1e2e*/ 562,
- /*0x01cd*/ 163,
- /*0x04fe*/ 420,
- -1,
- /*0x04a2*/ 374,
- /*0xa696*/ 1014,
- -1,
- /*0x1f69*/ 714,
- /*0x1fba*/ 776,
- -1, -1,
- /*0x020c*/ 196,
- /*0x03aa*/ 266,
- /*0xabba*/ 1162,
- /*0x2cb0*/ 951,
- -1, -1,
- /*0x04f2*/ 414,
- -1, -1, -1,
- /*0xa7b0*/ 1082,
- /*0x10cb0*/ 1330,
- /*0x0222*/ 207,
- -1, -1, -1,
- /*0xff24*/ 1183,
- -1,
- /*0x2cba*/ 956,
- -1,
- /*0x1fca*/ 787,
- /*0x004a*/ 8,
- /*0x014a*/ 93,
- -1,
- /*0xa686*/ 1006,
- -1, -1, -1,
- /*0x052a*/ 442,
- /*0x0184*/ 123,
- /*0x1e2c*/ 561,
- -1, -1,
- /*0x04aa*/ 378,
- /*0x052e*/ 444,
- /*0x01d1*/ 165,
- /*0xa690*/ 1011,
- -1, -1,
- /*0x04ae*/ 380,
- /*0x1fb8*/ 774,
- /*0x2cca*/ 964,
- -1,
- /*0x0182*/ 122,
- /*0x1e28*/ 559,
- /*0xabb8*/ 1160,
- -1,
- /*0x00ca*/ 36,
- /*0x1e32*/ 564,
- -1, -1, -1,
- /*0x022a*/ 211,
- /*0x10a7*/ 491,
- /*0x1eb0*/ 629,
- -1, -1, -1,
- /*0x022e*/ 213,
- /*0x1f0e*/ 675,
- /*0xfb14*/ 1176,
- /*0x2cb8*/ 955,
- /*0x03a8*/ 264,
- -1, -1, -1, -1, -1, -1,
- /*0x1eba*/ 634,
- -1,
- /*0xa69a*/ 1016,
- -1, -1,
- /*0x01a7*/ 145,
- /*0x052c*/ 443,
- /*0x10a2*/ 486,
- /*0xa692*/ 1012,
- /*0x1fd9*/ 795,
- /*0x0059*/ 23,
- /*0x04ac*/ 379,
- /*0x1ffa*/ 815,
- /*0x047a*/ 358,
- /*0x1fb6*/ 772,
- /*0xa77e*/ 1058,
- /*0x0136*/ 83,
- /*0xa722*/ 1017,
- -1,
- /*0xabb6*/ 1158,
- /*0x0528*/ 441,
- -1,
- /*0x118a7*/ 1340,
- /*0x1eca*/ 642,
- /*0x0532*/ 446,
- /*0x04a8*/ 377,
- /*0x01fe*/ 189,
- -1,
- /*0x01a2*/ 142,
- /*0x04b2*/ 382,
- /*0x022c*/ 212,
- /*0x104b2*/ 1248,
- /*0x212a*/ 819,
- -1, -1,
- /*0x2cb6*/ 954,
- /*0x00d9*/ 50,
- -1,
- /*0x1fcc*/ 789,
- /*0x004c*/ 10,
- /*0x014c*/ 94,
- /*0x01f2*/ 182,
- /*0xa7b6*/ 1087,
- /*0x1eb8*/ 633,
- /*0x0228*/ 210,
- /*0x118a2*/ 1335,
- -1,
- /*0x10aa*/ 494,
- /*0x0232*/ 215,
- -1, -1, -1, -1,
- /*0x10ae*/ 498,
- -1, -1, -1,
- /*0xa72a*/ 1021,
- -1,
- /*0x2ccc*/ 965,
- /*0xff27*/ 1186,
- /*0x1e30*/ 563,
- -1,
- /*0xa72e*/ 1023,
- -1,
- /*0x00cc*/ 38,
- -1,
- /*0x1fbc*/ 778,
- /*0x1fb4*/ 771,
- -1,
- /*0x0134*/ 82,
- /*0x1f2f*/ 690,
- /*0xabbc*/ 1164,
- /*0xabb4*/ 1156,
- /*0x01ae*/ 148,
- -1,
- /*0x1e3a*/ 568,
- -1, -1,
- /*0x03b0*/ 268,
- -1, -1, -1,
- /*0xff22*/ 1181,
- /*0x1efa*/ 666,
- /*0x118aa*/ 1343,
- /*0x1eb6*/ 632,
- -1,
- /*0x2cbc*/ 957,
- /*0x2cb4*/ 953,
- -1,
- /*0x118ae*/ 1347,
- -1,
- /*0x1fbe*/ 779,
- /*0x10ac*/ 496,
- /*0xa68e*/ 1010,
- /*0xa7b4*/ 1086,
- /*0x2132*/ 821,
- /*0xabbe*/ 1166,
- /*0x1e4a*/ 576,
- -1, -1, -1, -1,
- /*0xa72c*/ 1022,
- -1, -1,
- /*0xa694*/ 1013,
- /*0x10a8*/ 492,
- -1,
- /*0x1ecc*/ 643,
- /*0x04b0*/ 381,
- /*0x10b2*/ 502,
- /*0x104b0*/ 1246,
- /*0x2cbe*/ 958,
- /*0x01ac*/ 147,
- /*0x1f1c*/ 681,
- -1,
- /*0xa728*/ 1020,
- /*0x1e38*/ 567,
- -1,
- /*0x053a*/ 454,
- /*0xa732*/ 1024,
- /*0xff2a*/ 1189,
- /*0x13fa*/ 526,
- /*0x1f6b*/ 716,
- /*0x04ba*/ 386,
- -1,
- /*0x104ba*/ 1256,
- /*0xff2e*/ 1193,
- /*0x0230*/ 214,
- /*0x1f4d*/ 704,
- /*0x118ac*/ 1345,
- /*0x01b2*/ 151,
- -1, -1, -1, -1, -1,
- /*0x1ebc*/ 635,
- /*0x1eb4*/ 631,
- -1, -1, -1,
- /*0x054a*/ 470,
- /*0x023a*/ 216,
- /*0x118a8*/ 1341,
- -1, -1, -1,
- /*0x118b2*/ 1351,
- /*0x104ca*/ 1272,
- -1,
- /*0x1fc8*/ 785,
- /*0x0048*/ 7,
- -1,
- /*0x1fe2*/ 798,
- /*0x0462*/ 346,
- /*0x0162*/ 105,
- /*0xfb04*/ 1172,
- /*0x1e7a*/ 600,
- -1,
- /*0x1e36*/ 566,
- -1,
- /*0x0538*/ 452,
- /*0x1ebe*/ 636,
- /*0x2c62*/ 913,
- -1,
- /*0x024a*/ 226,
- /*0x04b8*/ 385,
- /*0xff2c*/ 1191,
- /*0x104b8*/ 1254,
- -1,
- /*0xfb02*/ 1170,
- /*0x2cc8*/ 963,
- -1, -1,
- /*0x2ce2*/ 976,
- /*0x03fa*/ 293,
- -1,
- /*0x00c8*/ 34,
- -1,
- /*0x1f85*/ 726,
- /*0x0405*/ 302,
- /*0xff28*/ 1187,
- /*0x10405*/ 1211,
- /*0x1e4c*/ 577,
- /*0xab85*/ 1109,
- /*0xff32*/ 1197,
- -1, -1, -1,
- /*0x2c05*/ 870,
- -1,
- /*0x10b0*/ 500,
- -1, -1,
- /*0x1fc4*/ 782,
- /*0x0044*/ 3,
- -1, -1, -1,
- /*0x1fd6*/ 792,
- /*0x0056*/ 20,
- /*0x0156*/ 99,
- -1, -1,
- /*0x0536*/ 450,
- -1,
- /*0x10ba*/ 510,
- /*0x04fa*/ 418,
- /*0x10c85*/ 1287,
- /*0x04b6*/ 384,
- /*0x24ba*/ 843,
- /*0x104b6*/ 1252,
- /*0x1e3c*/ 569,
- /*0x1e34*/ 565,
- -1,
- /*0x2cc4*/ 961,
- /*0xa73a*/ 1028,
- -1,
- /*0x1e905*/ 1370,
- -1,
- /*0x2cd6*/ 970,
- /*0x00c4*/ 30,
- /*0x1ec8*/ 641,
- /*0x1f0c*/ 673,
- -1,
- /*0x1ee2*/ 654,
- /*0x00d6*/ 48,
- -1,
- /*0x054c*/ 472,
- /*0x118b0*/ 1349,
- -1, -1, -1,
- /*0x24ca*/ 859,
- -1,
- /*0x104cc*/ 1274,
- -1,
- /*0xa64a*/ 985,
- /*0x1e3e*/ 570,
- /*0xa74a*/ 1036,
- -1, -1, -1, -1,
- /*0x118ba*/ 1359,
- -1, -1, -1, -1,
- /*0x10b8*/ 508,
- /*0x01ca*/ 161,
- -1,
- /*0x024c*/ 227,
- /*0x24b8*/ 841,
- -1, -1, -1,
- /*0x053c*/ 456,
- /*0x0534*/ 448,
- /*0xa738*/ 1027,
- -1, -1,
- /*0x04bc*/ 387,
- /*0x04b4*/ 383,
- /*0x104bc*/ 1258,
- /*0x104b4*/ 1250,
- /*0x1ec4*/ 639,
- /*0xff30*/ 1195,
- /*0x1fc2*/ 780,
- /*0x0042*/ 1,
- /*0x01b8*/ 155,
- /*0x1ed6*/ 648,
- /*0xa684*/ 1005,
- /*0x0050*/ 14,
- /*0x0150*/ 96,
- /*0x1f2a*/ 685,
- /*0x1fd2*/ 790,
- /*0x0052*/ 16,
- /*0x0152*/ 97,
- -1, -1,
- /*0x1f2e*/ 689,
- /*0xff3a*/ 1205,
- /*0x053e*/ 458,
- -1, -1,
- /*0xa682*/ 1004,
- /*0x118b8*/ 1357,
- /*0x04be*/ 388,
- /*0x2cc2*/ 960,
- /*0x104be*/ 1260,
- -1,
- /*0x10b6*/ 506,
- /*0x2cd0*/ 967,
- -1,
- /*0x00c2*/ 28,
- /*0x24b6*/ 839,
- /*0x2cd2*/ 968,
- /*0x1e48*/ 575,
- /*0x00d0*/ 42,
- -1,
- /*0x1e62*/ 588,
- /*0xa736*/ 1026,
- /*0x00d2*/ 44,
- -1, -1, -1,
- /*0x023e*/ 219,
- /*0x01d9*/ 169,
- /*0x004e*/ 12,
- /*0x014e*/ 95,
- /*0x01fa*/ 187,
- -1, -1, -1, -1, -1, -1,
- -1,
- /*0x03e2*/ 280,
- /*0x24cc*/ 861,
- -1,
- /*0x1f2c*/ 687,
- -1,
- /*0xa64c*/ 986,
- /*0xff38*/ 1203,
- /*0xa74c*/ 1037,
- -1,
- /*0xa68c*/ 1009,
- /*0x2cce*/ 966,
- /*0x118b6*/ 1355,
- -1, -1,
- /*0x1fc6*/ 783,
- /*0x0046*/ 5,
- /*0x00ce*/ 40,
- /*0x1f28*/ 683,
- -1,
- /*0x1ffc*/ 817,
- /*0x047c*/ 359,
- /*0x1ec2*/ 638,
- -1,
- /*0x1e44*/ 573,
- /*0x0548*/ 468,
- /*0x1ed0*/ 645,
- /*0x10bc*/ 512,
- /*0x10b4*/ 504,
- /*0x1e56*/ 582,
- /*0x1ed2*/ 646,
- /*0x24bc*/ 845,
- /*0x104c8*/ 1270,
- /*0x04e2*/ 406,
- -1, -1,
- /*0x2cc6*/ 962,
- /*0xa73c*/ 1029,
- /*0xa734*/ 1025,
- /*0x1fec*/ 807,
- /*0x046c*/ 351,
- /*0x016c*/ 110,
- /*0x00c6*/ 32,
- -1, -1, -1, -1,
- /*0x03d6*/ 274,
- /*0x01bc*/ 156,
- /*0x0248*/ 225,
- /*0xff36*/ 1201,
- -1, -1,
- /*0x10be*/ 514,
- -1, -1, -1,
- /*0x24be*/ 847,
- /*0x1fea*/ 805,
- /*0x046a*/ 350,
- /*0x016a*/ 109,
- -1,
- /*0x1ece*/ 644,
- /*0xa73e*/ 1030,
- -1,
- /*0x118bc*/ 1361,
- /*0x118b4*/ 1353,
- /*0x1fab*/ 764,
- /*0x042b*/ 340,
- /*0x0544*/ 464,
- /*0x1f81*/ 722,
- /*0x0401*/ 298,
- /*0xabab*/ 1147,
- /*0x10401*/ 1207,
- /*0x0556*/ 482,
- /*0xab81*/ 1105,
- /*0x104c4*/ 1266,
- /*0x2c2b*/ 908,
- -1,
- /*0x04d6*/ 400,
- /*0x2c01*/ 866,
- -1,
- /*0x013f*/ 87,
- -1, -1,
- /*0xabbf*/ 1167,
- -1,
- /*0x1ec6*/ 640,
- /*0x1fe8*/ 803,
- /*0x0468*/ 349,
- /*0x0168*/ 108,
- /*0x118be*/ 1363,
- /*0x1efc*/ 667,
- /*0x0244*/ 222,
- -1,
- /*0xa7ab*/ 1078,
- /*0x10cab*/ 1325,
- -1,
- /*0x1e42*/ 572,
- /*0x10c81*/ 1283,
- -1,
- /*0x2162*/ 824,
- /*0x1e50*/ 579,
- -1,
- /*0xff34*/ 1199,
- -1,
- /*0x1e52*/ 580,
- -1,
- /*0x1c88*/ 538,
- /*0x1e901*/ 1366,
- -1,
- /*0x1eec*/ 659,
- /*0x1fe4*/ 800,
- /*0x0464*/ 347,
- /*0x0164*/ 106,
- -1,
- /*0x03c2*/ 269,
- /*0x24c8*/ 857,
- -1,
- /*0x1f3a*/ 693,
- /*0x03d0*/ 271,
- /*0xa648*/ 984,
- /*0x2c64*/ 915,
- /*0xa748*/ 1035,
- /*0xa662*/ 997,
- -1,
- /*0xa762*/ 1048,
- -1, -1, -1,
- /*0x1eea*/ 658,
- /*0x1c80*/ 530,
- /*0x13fc*/ 528,
- -1,
- /*0x01c8*/ 160,
- /*0x1fa9*/ 762,
- /*0x0429*/ 338,
- /*0x01e2*/ 173,
- /*0x1e4e*/ 578,
- /*0x1c83*/ 533,
- /*0xaba9*/ 1145,
- -1,
- /*0x1f4a*/ 701,
- /*0x0542*/ 462,
- -1,
- /*0x2c29*/ 906,
- -1,
- /*0x0550*/ 476,
- -1, -1,
- /*0x104c2*/ 1264,
- /*0x0552*/ 478,
- /*0x04d0*/ 397,
- -1,
- /*0x104d0*/ 1278,
- -1,
- /*0x04d2*/ 398,
- /*0x10c4*/ 520,
- /*0x104d2*/ 1280,
- -1,
- /*0x1ee8*/ 657,
- /*0x24c4*/ 853,
- /*0x1f38*/ 691,
- /*0x1e46*/ 574,
- /*0x10ca9*/ 1323,
- /*0xa644*/ 982,
- -1,
- /*0xa744*/ 1033,
- /*0x1e7c*/ 601,
- -1,
- /*0xa656*/ 991,
- -1,
- /*0xa756*/ 1042,
- /*0x0460*/ 345,
- /*0x0160*/ 104,
- -1, -1, -1,
- /*0x01c4*/ 157,
- -1, -1, -1,
- /*0x2c60*/ 912,
- /*0x054e*/ 474,
- /*0x1ee4*/ 655,
- -1, -1,
- /*0x1e6c*/ 593,
- /*0x046e*/ 352,
- /*0x016e*/ 111,
- /*0x104ce*/ 1276,
- -1, -1,
- /*0x2ce0*/ 975,
- -1, -1, -1,
- /*0x2c6e*/ 920,
- -1, -1,
- /*0x1f59*/ 709,
- -1,
- /*0x1fe6*/ 801,
- /*0x0466*/ 348,
- /*0x0166*/ 107,
- /*0x03ec*/ 285,
- /*0x1e6a*/ 592,
- /*0x024e*/ 228,
- /*0x0546*/ 466,
- -1, -1,
- /*0x013d*/ 86,
- -1,
- /*0x1c86*/ 536,
- /*0xabbd*/ 1165,
- /*0x104c6*/ 1268,
- -1, -1,
- /*0x04fc*/ 419,
- -1, -1,
- /*0x1fda*/ 796,
- /*0x005a*/ 24,
- /*0x015a*/ 101,
- /*0x03ea*/ 284,
- /*0x1fd8*/ 794,
- /*0x0058*/ 22,
- /*0x0158*/ 100,
- -1,
- /*0x1f4c*/ 703,
- -1,
- /*0x10c2*/ 518,
- /*0x0246*/ 224,
- /*0x03ab*/ 267,
- -1,
- /*0x24c2*/ 851,
- /*0x1e68*/ 591,
- /*0x04ec*/ 411,
- -1,
- /*0xa642*/ 981,
- -1,
- /*0xa742*/ 1032,
- /*0x2cda*/ 972,
- /*0xa650*/ 988,
- /*0x1ee0*/ 653,
- /*0xa750*/ 1039,
- /*0x2cd8*/ 971,
- /*0xa652*/ 989,
- /*0x00da*/ 51,
- /*0xa752*/ 1040,
- /*0x0054*/ 18,
- /*0x0154*/ 98,
- /*0x00d8*/ 49,
- -1,
- /*0x03e8*/ 283,
- -1,
- /*0x04ea*/ 410,
- /*0x2cc0*/ 959,
- /*0x1f3c*/ 695,
- /*0x1eee*/ 660,
- /*0x1e64*/ 589,
- -1, -1,
- /*0x00c0*/ 26,
- -1, -1,
- /*0x015e*/ 103,
- /*0x1fc7*/ 784,
- /*0x0047*/ 6,
- /*0x0147*/ 91,
- /*0x2cd4*/ 969,
- -1, -1,
- /*0x053f*/ 459,
- /*0x1ee6*/ 656,
- /*0x24ce*/ 863,
- /*0x00d4*/ 46,
- -1,
- /*0x03e4*/ 281,
- /*0xa64e*/ 987,
- /*0x104bf*/ 1261,
- /*0xa74e*/ 1038,
- -1, -1,
- /*0x1f3e*/ 697,
- /*0x2cde*/ 974,
- /*0x04e8*/ 409,
- /*0x1fb9*/ 775,
- -1,
- /*0x0139*/ 84,
- -1,
- /*0x00de*/ 55,
- /*0xabb9*/ 1161,
- /*0x1eda*/ 650,
- /*0x00c7*/ 33,
- -1, -1,
- /*0x1ed8*/ 649,
- -1, -1,
- /*0x24c6*/ 855,
- /*0x03a9*/ 265,
- -1, -1,
- /*0xa646*/ 983,
- /*0x216c*/ 834,
- /*0xa746*/ 1034,
- -1,
- /*0x1ec0*/ 637,
- -1,
- /*0x04e4*/ 407,
- -1,
- /*0x015c*/ 102,
- /*0x1fa1*/ 754,
- /*0x0421*/ 330,
- -1,
- /*0x10421*/ 1239,
- -1,
- /*0xaba1*/ 1137,
- -1,
- /*0x1e60*/ 587,
- /*0x1ed4*/ 647,
- /*0x01fc*/ 188,
- /*0x2c21*/ 898,
- /*0x216a*/ 832,
- -1, -1, -1,
- /*0xa66c*/ 1002,
- -1,
- /*0xa76c*/ 1053,
- /*0x2cdc*/ 973,
- -1,
- /*0x212b*/ 820,
- -1,
- /*0x1e6e*/ 594,
- /*0x1ede*/ 652,
- /*0x00dc*/ 53,
- /*0x03e0*/ 279,
- -1, -1,
- /*0x01ec*/ 178,
- /*0x10ca1*/ 1315,
- -1,
- /*0x1f48*/ 699,
- /*0x0045*/ 4,
- /*0x0145*/ 90,
- /*0xa66a*/ 1001,
- /*0x10ab*/ 495,
- /*0xa76a*/ 1052,
- /*0x1e66*/ 590,
- -1,
- /*0x1e921*/ 1398,
- /*0x03ee*/ 286,
- /*0x2168*/ 830,
- -1,
- /*0x1fc3*/ 781,
- /*0x0043*/ 2,
- /*0x0143*/ 89,
- -1,
- /*0x01ea*/ 177,
- /*0x10bf*/ 515,
- /*0xfb05*/ 1173,
- -1,
- /*0x0345*/ 229,
- /*0x24bf*/ 848,
- -1, -1,
- /*0x03e6*/ 282,
- /*0x1e5a*/ 584,
- /*0x04e0*/ 405,
- /*0x00c5*/ 31,
- /*0x0181*/ 121,
- /*0x1e58*/ 583,
- -1, -1, -1,
- /*0xa668*/ 1000,
- /*0x2164*/ 826,
- /*0xa768*/ 1051,
- -1, -1,
- /*0x1edc*/ 651,
- /*0x00c3*/ 29,
- /*0x1e40*/ 571,
- /*0x04ee*/ 412,
- /*0x118ab*/ 1344,
- /*0x03da*/ 276,
- -1, -1,
- /*0x01e8*/ 176,
- /*0x03d8*/ 275,
- -1,
- /*0x1f56*/ 708,
- -1, -1, -1,
- /*0x1e54*/ 581,
- -1,
- /*0x118bf*/ 1364,
- /*0x04e6*/ 408,
- /*0xa664*/ 998,
- /*0x053d*/ 457,
- /*0xa764*/ 1049,
- /*0x1fc9*/ 786,
- /*0x0049*/ 1399,
- /*0x0149*/ 92,
- -1, -1,
- /*0x104bd*/ 1259,
- /*0x1f97*/ 744,
- /*0x0417*/ 320,
- /*0x1e5e*/ 586,
- /*0x10417*/ 1229,
- /*0x01e4*/ 174,
- /*0xab97*/ 1127,
- /*0x10a9*/ 493,
- /*0x1fad*/ 766,
- /*0x042d*/ 342,
- /*0x04da*/ 402,
- /*0x2c17*/ 888,
- -1,
- /*0xabad*/ 1149,
- /*0x04d8*/ 401,
- /*0xff2b*/ 1190,
- -1,
- /*0x023d*/ 218,
- /*0x2c2d*/ 910,
- -1,
- /*0x0540*/ 460,
- /*0x03de*/ 278,
- /*0x00c9*/ 35,
- -1, -1,
- /*0x04c0*/ 389,
- -1,
- /*0x104c0*/ 1262,
- /*0x01a9*/ 146,
- /*0x2160*/ 822,
- /*0x10c97*/ 1305,
- -1, -1,
- /*0x0554*/ 480,
- /*0x1fb3*/ 770,
- -1,
- /*0xa7ad*/ 1080,
- /*0x10cad*/ 1327,
- /*0x04d4*/ 399,
- /*0xabb3*/ 1155,
- /*0x1e917*/ 1388,
- -1, -1, -1,
- /*0x216e*/ 836,
- /*0x118a9*/ 1342,
- -1, -1, -1,
- /*0x1e5c*/ 585,
- -1,
- /*0x0547*/ 467,
- /*0xa660*/ 996,
- /*0x04de*/ 404,
- /*0xa760*/ 1047,
- -1,
- /*0x04c7*/ 393,
- /*0x1f50*/ 705,
- /*0x104c7*/ 1269,
- /*0x2166*/ 828,
- -1,
- /*0x1f52*/ 706,
- /*0xa7b3*/ 1085,
- -1, -1,
- /*0x01e0*/ 172,
- -1,
- /*0x03dc*/ 277,
- -1,
- /*0xa76e*/ 1054,
- /*0x03a1*/ 258,
- /*0x0539*/ 453,
- -1,
- /*0x1e97*/ 615,
- -1, -1, -1, -1,
- /*0x104b9*/ 1255,
- -1,
- /*0x01ee*/ 179,
- /*0x10bd*/ 513,
- /*0xa666*/ 999,
- /*0xff29*/ 1188,
- /*0xa766*/ 1050,
- /*0x24bd*/ 846,
- /*0x1fa5*/ 758,
- /*0x0425*/ 334,
- -1,
- /*0x10425*/ 1243,
- -1,
- /*0xaba5*/ 1141,
- /*0x1fb7*/ 773,
- -1, -1,
- /*0x01e6*/ 175,
- /*0x2c25*/ 902,
- /*0xabb7*/ 1159,
- -1,
- /*0x04dc*/ 403,
- -1,
- /*0xa65a*/ 993,
- -1,
- /*0xa75a*/ 1044,
- -1,
- /*0xa658*/ 992,
- -1,
- /*0xa758*/ 1043,
- /*0x10c0*/ 516,
- -1, -1, -1,
- /*0x24c0*/ 849,
- -1, -1,
- /*0x10ca5*/ 1319,
- /*0xa640*/ 980,
- -1,
- /*0xa740*/ 1031,
- /*0x118bd*/ 1362,
- /*0x1fa3*/ 756,
- /*0x0423*/ 332,
- -1,
- /*0x10423*/ 1241,
- /*0x1c84*/ 534,
- /*0xaba3*/ 1139,
- -1, -1,
- /*0x0545*/ 465,
- /*0xa654*/ 990,
- /*0x2c23*/ 900,
- /*0xa754*/ 1041,
- -1,
- /*0x04c5*/ 392,
- -1,
- /*0x104c5*/ 1267,
- -1, -1,
- /*0x1c82*/ 532,
- /*0x10c7*/ 522,
- /*0x0543*/ 463,
- -1,
- /*0x1f6c*/ 717,
- /*0x24c7*/ 856,
- /*0xa65e*/ 995,
- /*0x04c3*/ 391,
- /*0xa75e*/ 1046,
- /*0x104c3*/ 1265,
- -1,
- /*0x10ca3*/ 1317,
- -1, -1,
- /*0x0245*/ 223,
- /*0x1ff4*/ 810,
- /*0x0474*/ 355,
- /*0x0174*/ 114,
- -1,
- /*0x01de*/ 171,
- -1,
- /*0x10b9*/ 509,
- /*0x01c7*/ 159,
- /*0x1f6a*/ 715,
- /*0xfb01*/ 1169,
- /*0x24b9*/ 842,
- /*0x0243*/ 221,
- -1, -1,
- /*0x0397*/ 248,
- -1, -1,
- /*0x1f2b*/ 686,
- /*0x1f9d*/ 750,
- /*0x041d*/ 326,
- -1,
- /*0x1041d*/ 1235,
- /*0xabb1*/ 1153,
- /*0xab9d*/ 1133,
- /*0x0470*/ 353,
- /*0x0170*/ 112,
- /*0x1f9b*/ 748,
- /*0x041b*/ 324,
- /*0x2c1d*/ 894,
- /*0x1041b*/ 1233,
- /*0x1f3f*/ 698,
- /*0xab9b*/ 1131,
- /*0x10a1*/ 485,
- /*0x2c70*/ 922,
- /*0xabb5*/ 1157,
- /*0x0549*/ 469,
- /*0x2c1b*/ 892,
- /*0xa65c*/ 994,
- /*0x1f68*/ 713,
- /*0xa75c*/ 1045,
- /*0x04c9*/ 394,
- -1,
- /*0x104c9*/ 1271,
- /*0x0370*/ 230,
- /*0x118b9*/ 1358,
- /*0xa7b1*/ 1083,
- /*0x10cb1*/ 1331,
- /*0x10c9d*/ 1311,
- -1,
- /*0x1f99*/ 746,
- /*0x0419*/ 322,
- -1,
- /*0x10419*/ 1231,
- -1,
- /*0xab99*/ 1129,
- /*0x10c9b*/ 1309,
- /*0x00b5*/ 25,
- /*0x1e91d*/ 1394,
- -1,
- /*0x2c19*/ 890,
- -1,
- /*0x1f93*/ 740,
- /*0x0413*/ 316,
- /*0x10c5*/ 521,
- /*0x10413*/ 1225,
- /*0x1e91b*/ 1392,
- /*0xab93*/ 1123,
- /*0x24c5*/ 854,
- -1, -1,
- /*0x118a1*/ 1334,
- /*0x2c13*/ 884,
- /*0x1ef4*/ 663,
- -1,
- /*0x0533*/ 447,
- /*0x10c3*/ 519,
- /*0x0041*/ 0,
- /*0x0141*/ 88,
- /*0x10c99*/ 1307,
- /*0x24c3*/ 852,
- -1,
- /*0x104b3*/ 1249,
- /*0xff39*/ 1204,
- -1,
- /*0x01c5*/ 158,
- /*0x1f29*/ 684,
- -1, -1,
- /*0x1e919*/ 1390,
- -1,
- /*0x10c93*/ 1301,
- /*0x1f8f*/ 736,
- /*0x040f*/ 312,
- -1,
- /*0x1040f*/ 1221,
- /*0x1ef0*/ 661,
- /*0xab8f*/ 1119,
- -1,
- /*0x1e9b*/ 619,
- /*0x03a5*/ 261,
- /*0x1e913*/ 1384,
- /*0x2c0f*/ 880,
- /*0x00c1*/ 27,
- -1, -1, -1,
- /*0x1f8b*/ 732,
- /*0x040b*/ 308,
- /*0xff21*/ 1180,
- /*0x1040b*/ 1217,
- -1,
- /*0xab8b*/ 1115,
- /*0x1f87*/ 728,
- /*0x0407*/ 304,
- /*0xab7b*/ 1099,
- /*0x10407*/ 1213,
- /*0x2c0b*/ 876,
- /*0xab87*/ 1111,
- /*0x0587*/ 483,
- -1,
- /*0x10c8f*/ 1297,
- /*0x1e99*/ 617,
- /*0x2c07*/ 872,
- /*0x004f*/ 13,
- -1, -1, -1,
- /*0x24c9*/ 858,
- -1,
- /*0xab79*/ 1097,
- /*0x1e90f*/ 1380,
- -1,
- /*0x0537*/ 451,
- /*0x03a3*/ 259,
- /*0xa78b*/ 1063,
- /*0x10c8b*/ 1293,
- /*0x10ad*/ 497,
- /*0x1f6e*/ 719,
- -1,
- /*0x104b7*/ 1253,
- -1,
- /*0x10c87*/ 1289,
- -1, -1, -1,
- /*0x1e90b*/ 1376,
- -1, -1,
- /*0x1e74*/ 597,
- /*0x00cf*/ 41,
- /*0x0197*/ 136,
- /*0x1e907*/ 1372,
- -1,
- /*0xab71*/ 1089,
- /*0xab73*/ 1091,
- -1, -1, -1, -1,
- /*0x1f3d*/ 696,
- -1, -1,
- /*0x10b3*/ 503,
- -1,
- /*0xab7f*/ 1103,
- -1,
- /*0x03f4*/ 289,
- /*0x1f9f*/ 752,
- /*0x041f*/ 328,
- /*0xab7d*/ 1101,
- /*0x1041f*/ 1237,
- /*0x1e70*/ 595,
- /*0xab9f*/ 1135,
- -1,
- /*0x118ad*/ 1346,
- -1, -1,
- /*0x2c1f*/ 896,
- -1, -1, -1, -1, -1,
- /*0x01b3*/ 152,
- /*0x039d*/ 254,
- -1, -1, -1, -1,
- /*0x03f0*/ 287,
- /*0xab75*/ 1093,
- -1,
- /*0x039b*/ 252,
- -1, -1, -1,
- /*0x10c9f*/ 1313,
- -1,
- /*0x04f4*/ 415,
- /*0x1f54*/ 707,
- /*0x118b3*/ 1352,
- -1,
- /*0x1ff8*/ 813,
- /*0x0478*/ 357,
- /*0x0178*/ 116,
- -1,
- /*0x1e91f*/ 1396,
- -1, -1, -1,
- /*0x0531*/ 445,
- -1,
- /*0xff2d*/ 1192,
- -1,
- /*0x10a5*/ 489,
- /*0x0399*/ 250,
- -1,
- /*0x104b1*/ 1247,
- -1, -1,
- /*0x10b7*/ 507,
- /*0x04f0*/ 413,
- /*0x0535*/ 449,
- -1,
- /*0x24b7*/ 840,
- /*0x1f95*/ 742,
- /*0x0415*/ 318,
- /*0x0393*/ 244,
- /*0x10415*/ 1227,
- /*0x104b5*/ 1251,
- /*0xab95*/ 1125,
- -1, -1, -1, -1,
- /*0x2c15*/ 886,
- -1,
- /*0x1f39*/ 692,
- /*0xff33*/ 1198,
- -1, -1,
- /*0x01b7*/ 154,
- -1, -1, -1, -1,
- /*0xab77*/ 1095,
- -1,
- /*0x10a3*/ 487,
- -1,
- /*0x1f91*/ 738,
- /*0x0411*/ 314,
- /*0x118a5*/ 1338,
- /*0x10411*/ 1223,
- /*0x10c95*/ 1303,
- /*0xab91*/ 1121,
- -1,
- /*0x038f*/ 240,
- /*0x118b7*/ 1356,
- -1,
- /*0x2c11*/ 882,
- -1, -1, -1,
- /*0x1e915*/ 1386,
- -1,
- /*0x1f8d*/ 734,
- /*0x040d*/ 310,
- /*0x0541*/ 461,
- /*0x1040d*/ 1219,
- -1,
- /*0xab8d*/ 1117,
- -1,
- /*0x04c1*/ 390,
- /*0x1ef8*/ 665,
- /*0x104c1*/ 1263,
- /*0x2c0d*/ 878,
- -1, -1,
- /*0x10c91*/ 1299,
- -1, -1, -1, -1, -1, -1,
- /*0x118a3*/ 1336,
- -1,
- /*0x03cf*/ 270,
- /*0x1e911*/ 1382,
- /*0xff25*/ 1184,
- -1,
- /*0x0241*/ 220,
- -1,
- /*0xa78d*/ 1064,
- /*0x10c8d*/ 1295,
- /*0xff37*/ 1202,
- -1,
- /*0x10b1*/ 501,
- -1, -1, -1,
- /*0x01f4*/ 183,
- -1, -1,
- /*0x1e90d*/ 1378,
- -1, -1, -1, -1,
- /*0x10b5*/ 505,
- -1, -1, -1,
- /*0x13f8*/ 524,
- /*0x054f*/ 475,
- -1, -1, -1,
- /*0x01b1*/ 150,
- /*0x019d*/ 139,
- -1,
- /*0x104cf*/ 1277,
- -1,
- /*0xff23*/ 1182,
- /*0x01f0*/ 180,
- -1, -1, -1, -1, -1,
- /*0x01b5*/ 153,
- -1,
- /*0x039f*/ 256,
- -1, -1,
- /*0x118b1*/ 1350,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1,
- /*0x118b5*/ 1354,
- /*0xfb17*/ 1179,
- /*0x1e78*/ 599,
- -1, -1, -1,
- /*0x1f49*/ 700,
- -1,
- /*0x10c1*/ 517,
- -1, -1, -1,
- /*0x24c1*/ 850,
- -1, -1,
- /*0x0193*/ 133,
- -1, -1, -1,
- /*0x1f2d*/ 688,
- -1, -1, -1, -1, -1, -1,
- /*0xff31*/ 1196,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1,
- /*0xff35*/ 1200,
- -1, -1, -1, -1,
- /*0x0395*/ 246,
- -1,
- /*0x018f*/ 130,
- -1, -1, -1, -1, -1, -1,
- /*0x04f8*/ 417,
- -1, -1, -1, -1, -1,
- /*0xab76*/ 1094,
- /*0x24cf*/ 864,
- /*0x018b*/ 128,
- -1, -1, -1, -1, -1,
- /*0x0187*/ 125,
- -1,
- /*0x0391*/ 242,
- -1, -1, -1, -1, -1, -1,
- -1,
- /*0x01cf*/ 164,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1,
- /*0x019f*/ 140,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1,
- /*0x1c85*/ 535,
- -1, -1, -1, -1, -1, -1,
- -1, -1,
- /*0x01f8*/ 186,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1,
- /*0x1f1d*/ 682,
- -1, -1, -1, -1, -1, -1,
- -1,
- /*0x1f1b*/ 680,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1,
- /*0x0191*/ 132,
- -1, -1, -1, -1, -1, -1,
- -1, -1,
- /*0x1f19*/ 678,
- /*0xfb13*/ 1175,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1,
- /*0x1f0f*/ 676,
- -1, -1, -1, -1,
- /*0xab7e*/ 1102,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1,
- /*0x1f0b*/ 672,
- -1, -1, -1, -1,
- /*0xab72*/ 1090,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1,
- /*0x1c81*/ 531,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1,
- /*0xfb15*/ 1177,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1,
- /*0x1f0d*/ 674,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1,
- /*0xab7a*/ 1098,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1,
- /*0xab7c*/ 1100,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1,
- /*0x1c87*/ 537,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1,
- /*0xab74*/ 1092,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1,
- /*0xab70*/ 1088,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1,
- /*0xab78*/ 1096
- };
-
- if (code <= MAX_CODE_VALUE && code >= MIN_CODE_VALUE)
- {
- register int key = onigenc_unicode_CaseFold_11_hash(code);
-
- if (key <= MAX_HASH_VALUE && key >= 0)
- {
- register short s = wordlist[key];
-
- if (s >= 0 && code1_equal(code, CaseFold_11_Table[s].from))
- return &CaseFold_11_Table[s].to;
- }
- }
- return 0;
-}
-
-static const CaseUnfold_11_Type CaseUnfold_11_Table[] = {
-#define CaseUnfold_11 (*(CaseUnfold_11_Type (*)[1266])(CaseUnfold_11_Table+0))
- {0x0061, {1|U, {0x0041}}},
- {0x0062, {1|U, {0x0042}}},
- {0x0063, {1|U, {0x0043}}},
- {0x0064, {1|U, {0x0044}}},
- {0x0065, {1|U, {0x0045}}},
- {0x0066, {1|U, {0x0046}}},
- {0x0067, {1|U, {0x0047}}},
- {0x0068, {1|U, {0x0048}}},
- {0x006a, {1|U, {0x004a}}},
- {0x006b, {2|U, {0x004b, 0x212a}}},
- {0x006c, {1|U, {0x004c}}},
- {0x006d, {1|U, {0x004d}}},
- {0x006e, {1|U, {0x004e}}},
- {0x006f, {1|U, {0x004f}}},
- {0x0070, {1|U, {0x0050}}},
- {0x0071, {1|U, {0x0051}}},
- {0x0072, {1|U, {0x0052}}},
- {0x0073, {2|U, {0x0053, 0x017f}}},
- {0x0074, {1|U, {0x0054}}},
- {0x0075, {1|U, {0x0055}}},
- {0x0076, {1|U, {0x0056}}},
- {0x0077, {1|U, {0x0057}}},
- {0x0078, {1|U, {0x0058}}},
- {0x0079, {1|U, {0x0059}}},
- {0x007a, {1|U, {0x005a}}},
- {0x00e0, {1|U, {0x00c0}}},
- {0x00e1, {1|U, {0x00c1}}},
- {0x00e2, {1|U, {0x00c2}}},
- {0x00e3, {1|U, {0x00c3}}},
- {0x00e4, {1|U, {0x00c4}}},
- {0x00e5, {2|U, {0x00c5, 0x212b}}},
- {0x00e6, {1|U, {0x00c6}}},
- {0x00e7, {1|U, {0x00c7}}},
- {0x00e8, {1|U, {0x00c8}}},
- {0x00e9, {1|U, {0x00c9}}},
- {0x00ea, {1|U, {0x00ca}}},
- {0x00eb, {1|U, {0x00cb}}},
- {0x00ec, {1|U, {0x00cc}}},
- {0x00ed, {1|U, {0x00cd}}},
- {0x00ee, {1|U, {0x00ce}}},
- {0x00ef, {1|U, {0x00cf}}},
- {0x00f0, {1|U, {0x00d0}}},
- {0x00f1, {1|U, {0x00d1}}},
- {0x00f2, {1|U, {0x00d2}}},
- {0x00f3, {1|U, {0x00d3}}},
- {0x00f4, {1|U, {0x00d4}}},
- {0x00f5, {1|U, {0x00d5}}},
- {0x00f6, {1|U, {0x00d6}}},
- {0x00f8, {1|U, {0x00d8}}},
- {0x00f9, {1|U, {0x00d9}}},
- {0x00fa, {1|U, {0x00da}}},
- {0x00fb, {1|U, {0x00db}}},
- {0x00fc, {1|U, {0x00dc}}},
- {0x00fd, {1|U, {0x00dd}}},
- {0x00fe, {1|U, {0x00de}}},
- {0x00ff, {1|U, {0x0178}}},
- {0x0101, {1|U, {0x0100}}},
- {0x0103, {1|U, {0x0102}}},
- {0x0105, {1|U, {0x0104}}},
- {0x0107, {1|U, {0x0106}}},
- {0x0109, {1|U, {0x0108}}},
- {0x010b, {1|U, {0x010a}}},
- {0x010d, {1|U, {0x010c}}},
- {0x010f, {1|U, {0x010e}}},
- {0x0111, {1|U, {0x0110}}},
- {0x0113, {1|U, {0x0112}}},
- {0x0115, {1|U, {0x0114}}},
- {0x0117, {1|U, {0x0116}}},
- {0x0119, {1|U, {0x0118}}},
- {0x011b, {1|U, {0x011a}}},
- {0x011d, {1|U, {0x011c}}},
- {0x011f, {1|U, {0x011e}}},
- {0x0121, {1|U, {0x0120}}},
- {0x0123, {1|U, {0x0122}}},
- {0x0125, {1|U, {0x0124}}},
- {0x0127, {1|U, {0x0126}}},
- {0x0129, {1|U, {0x0128}}},
- {0x012b, {1|U, {0x012a}}},
- {0x012d, {1|U, {0x012c}}},
- {0x012f, {1|U, {0x012e}}},
- {0x0133, {1|U, {0x0132}}},
- {0x0135, {1|U, {0x0134}}},
- {0x0137, {1|U, {0x0136}}},
- {0x013a, {1|U, {0x0139}}},
- {0x013c, {1|U, {0x013b}}},
- {0x013e, {1|U, {0x013d}}},
- {0x0140, {1|U, {0x013f}}},
- {0x0142, {1|U, {0x0141}}},
- {0x0144, {1|U, {0x0143}}},
- {0x0146, {1|U, {0x0145}}},
- {0x0148, {1|U, {0x0147}}},
- {0x014b, {1|U, {0x014a}}},
- {0x014d, {1|U, {0x014c}}},
- {0x014f, {1|U, {0x014e}}},
- {0x0151, {1|U, {0x0150}}},
- {0x0153, {1|U, {0x0152}}},
- {0x0155, {1|U, {0x0154}}},
- {0x0157, {1|U, {0x0156}}},
- {0x0159, {1|U, {0x0158}}},
- {0x015b, {1|U, {0x015a}}},
- {0x015d, {1|U, {0x015c}}},
- {0x015f, {1|U, {0x015e}}},
- {0x0161, {1|U, {0x0160}}},
- {0x0163, {1|U, {0x0162}}},
- {0x0165, {1|U, {0x0164}}},
- {0x0167, {1|U, {0x0166}}},
- {0x0169, {1|U, {0x0168}}},
- {0x016b, {1|U, {0x016a}}},
- {0x016d, {1|U, {0x016c}}},
- {0x016f, {1|U, {0x016e}}},
- {0x0171, {1|U, {0x0170}}},
- {0x0173, {1|U, {0x0172}}},
- {0x0175, {1|U, {0x0174}}},
- {0x0177, {1|U, {0x0176}}},
- {0x017a, {1|U, {0x0179}}},
- {0x017c, {1|U, {0x017b}}},
- {0x017e, {1|U, {0x017d}}},
- {0x0180, {1|U, {0x0243}}},
- {0x0183, {1|U, {0x0182}}},
- {0x0185, {1|U, {0x0184}}},
- {0x0188, {1|U, {0x0187}}},
- {0x018c, {1|U, {0x018b}}},
- {0x0192, {1|U, {0x0191}}},
- {0x0195, {1|U, {0x01f6}}},
- {0x0199, {1|U, {0x0198}}},
- {0x019a, {1|U, {0x023d}}},
- {0x019e, {1|U, {0x0220}}},
- {0x01a1, {1|U, {0x01a0}}},
- {0x01a3, {1|U, {0x01a2}}},
- {0x01a5, {1|U, {0x01a4}}},
- {0x01a8, {1|U, {0x01a7}}},
- {0x01ad, {1|U, {0x01ac}}},
- {0x01b0, {1|U, {0x01af}}},
- {0x01b4, {1|U, {0x01b3}}},
- {0x01b6, {1|U, {0x01b5}}},
- {0x01b9, {1|U, {0x01b8}}},
- {0x01bd, {1|U, {0x01bc}}},
- {0x01bf, {1|U, {0x01f7}}},
- {0x01c6, {2|U|ST, {0x01c4, 0x01c5}}},
- {0x01c9, {2|U|ST, {0x01c7, 0x01c8}}},
- {0x01cc, {2|U|ST, {0x01ca, 0x01cb}}},
- {0x01ce, {1|U, {0x01cd}}},
- {0x01d0, {1|U, {0x01cf}}},
- {0x01d2, {1|U, {0x01d1}}},
- {0x01d4, {1|U, {0x01d3}}},
- {0x01d6, {1|U, {0x01d5}}},
- {0x01d8, {1|U, {0x01d7}}},
- {0x01da, {1|U, {0x01d9}}},
- {0x01dc, {1|U, {0x01db}}},
- {0x01dd, {1|U, {0x018e}}},
- {0x01df, {1|U, {0x01de}}},
- {0x01e1, {1|U, {0x01e0}}},
- {0x01e3, {1|U, {0x01e2}}},
- {0x01e5, {1|U, {0x01e4}}},
- {0x01e7, {1|U, {0x01e6}}},
- {0x01e9, {1|U, {0x01e8}}},
- {0x01eb, {1|U, {0x01ea}}},
- {0x01ed, {1|U, {0x01ec}}},
- {0x01ef, {1|U, {0x01ee}}},
- {0x01f3, {2|U|ST, {0x01f1, 0x01f2}}},
- {0x01f5, {1|U, {0x01f4}}},
- {0x01f9, {1|U, {0x01f8}}},
- {0x01fb, {1|U, {0x01fa}}},
- {0x01fd, {1|U, {0x01fc}}},
- {0x01ff, {1|U, {0x01fe}}},
- {0x0201, {1|U, {0x0200}}},
- {0x0203, {1|U, {0x0202}}},
- {0x0205, {1|U, {0x0204}}},
- {0x0207, {1|U, {0x0206}}},
- {0x0209, {1|U, {0x0208}}},
- {0x020b, {1|U, {0x020a}}},
- {0x020d, {1|U, {0x020c}}},
- {0x020f, {1|U, {0x020e}}},
- {0x0211, {1|U, {0x0210}}},
- {0x0213, {1|U, {0x0212}}},
- {0x0215, {1|U, {0x0214}}},
- {0x0217, {1|U, {0x0216}}},
- {0x0219, {1|U, {0x0218}}},
- {0x021b, {1|U, {0x021a}}},
- {0x021d, {1|U, {0x021c}}},
- {0x021f, {1|U, {0x021e}}},
- {0x0223, {1|U, {0x0222}}},
- {0x0225, {1|U, {0x0224}}},
- {0x0227, {1|U, {0x0226}}},
- {0x0229, {1|U, {0x0228}}},
- {0x022b, {1|U, {0x022a}}},
- {0x022d, {1|U, {0x022c}}},
- {0x022f, {1|U, {0x022e}}},
- {0x0231, {1|U, {0x0230}}},
- {0x0233, {1|U, {0x0232}}},
- {0x023c, {1|U, {0x023b}}},
- {0x023f, {1|U, {0x2c7e}}},
- {0x0240, {1|U, {0x2c7f}}},
- {0x0242, {1|U, {0x0241}}},
- {0x0247, {1|U, {0x0246}}},
- {0x0249, {1|U, {0x0248}}},
- {0x024b, {1|U, {0x024a}}},
- {0x024d, {1|U, {0x024c}}},
- {0x024f, {1|U, {0x024e}}},
- {0x0250, {1|U, {0x2c6f}}},
- {0x0251, {1|U, {0x2c6d}}},
- {0x0252, {1|U, {0x2c70}}},
- {0x0253, {1|U, {0x0181}}},
- {0x0254, {1|U, {0x0186}}},
- {0x0256, {1|U, {0x0189}}},
- {0x0257, {1|U, {0x018a}}},
- {0x0259, {1|U, {0x018f}}},
- {0x025b, {1|U, {0x0190}}},
- {0x025c, {1|U, {0xa7ab}}},
- {0x0260, {1|U, {0x0193}}},
- {0x0261, {1|U, {0xa7ac}}},
- {0x0263, {1|U, {0x0194}}},
- {0x0265, {1|U, {0xa78d}}},
- {0x0266, {1|U, {0xa7aa}}},
- {0x0268, {1|U, {0x0197}}},
- {0x0269, {1|U, {0x0196}}},
- {0x026a, {1|U, {0xa7ae}}},
- {0x026b, {1|U, {0x2c62}}},
- {0x026c, {1|U, {0xa7ad}}},
- {0x026f, {1|U, {0x019c}}},
- {0x0271, {1|U, {0x2c6e}}},
- {0x0272, {1|U, {0x019d}}},
- {0x0275, {1|U, {0x019f}}},
- {0x027d, {1|U, {0x2c64}}},
- {0x0280, {1|U, {0x01a6}}},
- {0x0283, {1|U, {0x01a9}}},
- {0x0287, {1|U, {0xa7b1}}},
- {0x0288, {1|U, {0x01ae}}},
- {0x0289, {1|U, {0x0244}}},
- {0x028a, {1|U, {0x01b1}}},
- {0x028b, {1|U, {0x01b2}}},
- {0x028c, {1|U, {0x0245}}},
- {0x0292, {1|U, {0x01b7}}},
- {0x029d, {1|U, {0xa7b2}}},
- {0x029e, {1|U, {0xa7b0}}},
- {0x0371, {1|U, {0x0370}}},
- {0x0373, {1|U, {0x0372}}},
- {0x0377, {1|U, {0x0376}}},
- {0x037b, {1|U, {0x03fd}}},
- {0x037c, {1|U, {0x03fe}}},
- {0x037d, {1|U, {0x03ff}}},
- {0x03ac, {1|U, {0x0386}}},
- {0x03ad, {1|U, {0x0388}}},
- {0x03ae, {1|U, {0x0389}}},
- {0x03af, {1|U, {0x038a}}},
- {0x03b1, {1|U, {0x0391}}},
- {0x03b2, {2|U, {0x0392, 0x03d0}}},
- {0x03b3, {1|U, {0x0393}}},
- {0x03b4, {1|U, {0x0394}}},
- {0x03b5, {2|U, {0x0395, 0x03f5}}},
- {0x03b6, {1|U, {0x0396}}},
- {0x03b7, {1|U, {0x0397}}},
- {0x03b8, {3|U, {0x0398, 0x03d1, 0x03f4}}},
- {0x03b9, {3, {0x0345, 0x0399, 0x1fbe}}},
- {0x03ba, {2|U, {0x039a, 0x03f0}}},
- {0x03bb, {1|U, {0x039b}}},
- {0x03bc, {2, {0x00b5, 0x039c}}},
- {0x03bd, {1|U, {0x039d}}},
- {0x03be, {1|U, {0x039e}}},
- {0x03bf, {1|U, {0x039f}}},
- {0x03c0, {2|U, {0x03a0, 0x03d6}}},
- {0x03c1, {2|U, {0x03a1, 0x03f1}}},
- {0x03c3, {2|U, {0x03a3, 0x03c2}}},
- {0x03c4, {1|U, {0x03a4}}},
- {0x03c5, {1|U, {0x03a5}}},
- {0x03c6, {2|U, {0x03a6, 0x03d5}}},
- {0x03c7, {1|U, {0x03a7}}},
- {0x03c8, {1|U, {0x03a8}}},
- {0x03c9, {2|U, {0x03a9, 0x2126}}},
- {0x03ca, {1|U, {0x03aa}}},
- {0x03cb, {1|U, {0x03ab}}},
- {0x03cc, {1|U, {0x038c}}},
- {0x03cd, {1|U, {0x038e}}},
- {0x03ce, {1|U, {0x038f}}},
- {0x03d7, {1|U, {0x03cf}}},
- {0x03d9, {1|U, {0x03d8}}},
- {0x03db, {1|U, {0x03da}}},
- {0x03dd, {1|U, {0x03dc}}},
- {0x03df, {1|U, {0x03de}}},
- {0x03e1, {1|U, {0x03e0}}},
- {0x03e3, {1|U, {0x03e2}}},
- {0x03e5, {1|U, {0x03e4}}},
- {0x03e7, {1|U, {0x03e6}}},
- {0x03e9, {1|U, {0x03e8}}},
- {0x03eb, {1|U, {0x03ea}}},
- {0x03ed, {1|U, {0x03ec}}},
- {0x03ef, {1|U, {0x03ee}}},
- {0x03f2, {1|U, {0x03f9}}},
- {0x03f3, {1|U, {0x037f}}},
- {0x03f8, {1|U, {0x03f7}}},
- {0x03fb, {1|U, {0x03fa}}},
- {0x0430, {1|U, {0x0410}}},
- {0x0431, {1|U, {0x0411}}},
- {0x0432, {2|U, {0x0412, 0x1c80}}},
- {0x0433, {1|U, {0x0413}}},
- {0x0434, {2|U, {0x0414, 0x1c81}}},
- {0x0435, {1|U, {0x0415}}},
- {0x0436, {1|U, {0x0416}}},
- {0x0437, {1|U, {0x0417}}},
- {0x0438, {1|U, {0x0418}}},
- {0x0439, {1|U, {0x0419}}},
- {0x043a, {1|U, {0x041a}}},
- {0x043b, {1|U, {0x041b}}},
- {0x043c, {1|U, {0x041c}}},
- {0x043d, {1|U, {0x041d}}},
- {0x043e, {2|U, {0x041e, 0x1c82}}},
- {0x043f, {1|U, {0x041f}}},
- {0x0440, {1|U, {0x0420}}},
- {0x0441, {2|U, {0x0421, 0x1c83}}},
- {0x0442, {3|U, {0x0422, 0x1c84, 0x1c85}}},
- {0x0443, {1|U, {0x0423}}},
- {0x0444, {1|U, {0x0424}}},
- {0x0445, {1|U, {0x0425}}},
- {0x0446, {1|U, {0x0426}}},
- {0x0447, {1|U, {0x0427}}},
- {0x0448, {1|U, {0x0428}}},
- {0x0449, {1|U, {0x0429}}},
- {0x044a, {2|U, {0x042a, 0x1c86}}},
- {0x044b, {1|U, {0x042b}}},
- {0x044c, {1|U, {0x042c}}},
- {0x044d, {1|U, {0x042d}}},
- {0x044e, {1|U, {0x042e}}},
- {0x044f, {1|U, {0x042f}}},
- {0x0450, {1|U, {0x0400}}},
- {0x0451, {1|U, {0x0401}}},
- {0x0452, {1|U, {0x0402}}},
- {0x0453, {1|U, {0x0403}}},
- {0x0454, {1|U, {0x0404}}},
- {0x0455, {1|U, {0x0405}}},
- {0x0456, {1|U, {0x0406}}},
- {0x0457, {1|U, {0x0407}}},
- {0x0458, {1|U, {0x0408}}},
- {0x0459, {1|U, {0x0409}}},
- {0x045a, {1|U, {0x040a}}},
- {0x045b, {1|U, {0x040b}}},
- {0x045c, {1|U, {0x040c}}},
- {0x045d, {1|U, {0x040d}}},
- {0x045e, {1|U, {0x040e}}},
- {0x045f, {1|U, {0x040f}}},
- {0x0461, {1|U, {0x0460}}},
- {0x0463, {2|U, {0x0462, 0x1c87}}},
- {0x0465, {1|U, {0x0464}}},
- {0x0467, {1|U, {0x0466}}},
- {0x0469, {1|U, {0x0468}}},
- {0x046b, {1|U, {0x046a}}},
- {0x046d, {1|U, {0x046c}}},
- {0x046f, {1|U, {0x046e}}},
- {0x0471, {1|U, {0x0470}}},
- {0x0473, {1|U, {0x0472}}},
- {0x0475, {1|U, {0x0474}}},
- {0x0477, {1|U, {0x0476}}},
- {0x0479, {1|U, {0x0478}}},
- {0x047b, {1|U, {0x047a}}},
- {0x047d, {1|U, {0x047c}}},
- {0x047f, {1|U, {0x047e}}},
- {0x0481, {1|U, {0x0480}}},
- {0x048b, {1|U, {0x048a}}},
- {0x048d, {1|U, {0x048c}}},
- {0x048f, {1|U, {0x048e}}},
- {0x0491, {1|U, {0x0490}}},
- {0x0493, {1|U, {0x0492}}},
- {0x0495, {1|U, {0x0494}}},
- {0x0497, {1|U, {0x0496}}},
- {0x0499, {1|U, {0x0498}}},
- {0x049b, {1|U, {0x049a}}},
- {0x049d, {1|U, {0x049c}}},
- {0x049f, {1|U, {0x049e}}},
- {0x04a1, {1|U, {0x04a0}}},
- {0x04a3, {1|U, {0x04a2}}},
- {0x04a5, {1|U, {0x04a4}}},
- {0x04a7, {1|U, {0x04a6}}},
- {0x04a9, {1|U, {0x04a8}}},
- {0x04ab, {1|U, {0x04aa}}},
- {0x04ad, {1|U, {0x04ac}}},
- {0x04af, {1|U, {0x04ae}}},
- {0x04b1, {1|U, {0x04b0}}},
- {0x04b3, {1|U, {0x04b2}}},
- {0x04b5, {1|U, {0x04b4}}},
- {0x04b7, {1|U, {0x04b6}}},
- {0x04b9, {1|U, {0x04b8}}},
- {0x04bb, {1|U, {0x04ba}}},
- {0x04bd, {1|U, {0x04bc}}},
- {0x04bf, {1|U, {0x04be}}},
- {0x04c2, {1|U, {0x04c1}}},
- {0x04c4, {1|U, {0x04c3}}},
- {0x04c6, {1|U, {0x04c5}}},
- {0x04c8, {1|U, {0x04c7}}},
- {0x04ca, {1|U, {0x04c9}}},
- {0x04cc, {1|U, {0x04cb}}},
- {0x04ce, {1|U, {0x04cd}}},
- {0x04cf, {1|U, {0x04c0}}},
- {0x04d1, {1|U, {0x04d0}}},
- {0x04d3, {1|U, {0x04d2}}},
- {0x04d5, {1|U, {0x04d4}}},
- {0x04d7, {1|U, {0x04d6}}},
- {0x04d9, {1|U, {0x04d8}}},
- {0x04db, {1|U, {0x04da}}},
- {0x04dd, {1|U, {0x04dc}}},
- {0x04df, {1|U, {0x04de}}},
- {0x04e1, {1|U, {0x04e0}}},
- {0x04e3, {1|U, {0x04e2}}},
- {0x04e5, {1|U, {0x04e4}}},
- {0x04e7, {1|U, {0x04e6}}},
- {0x04e9, {1|U, {0x04e8}}},
- {0x04eb, {1|U, {0x04ea}}},
- {0x04ed, {1|U, {0x04ec}}},
- {0x04ef, {1|U, {0x04ee}}},
- {0x04f1, {1|U, {0x04f0}}},
- {0x04f3, {1|U, {0x04f2}}},
- {0x04f5, {1|U, {0x04f4}}},
- {0x04f7, {1|U, {0x04f6}}},
- {0x04f9, {1|U, {0x04f8}}},
- {0x04fb, {1|U, {0x04fa}}},
- {0x04fd, {1|U, {0x04fc}}},
- {0x04ff, {1|U, {0x04fe}}},
- {0x0501, {1|U, {0x0500}}},
- {0x0503, {1|U, {0x0502}}},
- {0x0505, {1|U, {0x0504}}},
- {0x0507, {1|U, {0x0506}}},
- {0x0509, {1|U, {0x0508}}},
- {0x050b, {1|U, {0x050a}}},
- {0x050d, {1|U, {0x050c}}},
- {0x050f, {1|U, {0x050e}}},
- {0x0511, {1|U, {0x0510}}},
- {0x0513, {1|U, {0x0512}}},
- {0x0515, {1|U, {0x0514}}},
- {0x0517, {1|U, {0x0516}}},
- {0x0519, {1|U, {0x0518}}},
- {0x051b, {1|U, {0x051a}}},
- {0x051d, {1|U, {0x051c}}},
- {0x051f, {1|U, {0x051e}}},
- {0x0521, {1|U, {0x0520}}},
- {0x0523, {1|U, {0x0522}}},
- {0x0525, {1|U, {0x0524}}},
- {0x0527, {1|U, {0x0526}}},
- {0x0529, {1|U, {0x0528}}},
- {0x052b, {1|U, {0x052a}}},
- {0x052d, {1|U, {0x052c}}},
- {0x052f, {1|U, {0x052e}}},
- {0x0561, {1|U, {0x0531}}},
- {0x0562, {1|U, {0x0532}}},
- {0x0563, {1|U, {0x0533}}},
- {0x0564, {1|U, {0x0534}}},
- {0x0565, {1|U, {0x0535}}},
- {0x0566, {1|U, {0x0536}}},
- {0x0567, {1|U, {0x0537}}},
- {0x0568, {1|U, {0x0538}}},
- {0x0569, {1|U, {0x0539}}},
- {0x056a, {1|U, {0x053a}}},
- {0x056b, {1|U, {0x053b}}},
- {0x056c, {1|U, {0x053c}}},
- {0x056d, {1|U, {0x053d}}},
- {0x056e, {1|U, {0x053e}}},
- {0x056f, {1|U, {0x053f}}},
- {0x0570, {1|U, {0x0540}}},
- {0x0571, {1|U, {0x0541}}},
- {0x0572, {1|U, {0x0542}}},
- {0x0573, {1|U, {0x0543}}},
- {0x0574, {1|U, {0x0544}}},
- {0x0575, {1|U, {0x0545}}},
- {0x0576, {1|U, {0x0546}}},
- {0x0577, {1|U, {0x0547}}},
- {0x0578, {1|U, {0x0548}}},
- {0x0579, {1|U, {0x0549}}},
- {0x057a, {1|U, {0x054a}}},
- {0x057b, {1|U, {0x054b}}},
- {0x057c, {1|U, {0x054c}}},
- {0x057d, {1|U, {0x054d}}},
- {0x057e, {1|U, {0x054e}}},
- {0x057f, {1|U, {0x054f}}},
- {0x0580, {1|U, {0x0550}}},
- {0x0581, {1|U, {0x0551}}},
- {0x0582, {1|U, {0x0552}}},
- {0x0583, {1|U, {0x0553}}},
- {0x0584, {1|U, {0x0554}}},
- {0x0585, {1|U, {0x0555}}},
- {0x0586, {1|U, {0x0556}}},
- {0x13a0, {1|D, {0xab70}}},
- {0x13a1, {1|D, {0xab71}}},
- {0x13a2, {1|D, {0xab72}}},
- {0x13a3, {1|D, {0xab73}}},
- {0x13a4, {1|D, {0xab74}}},
- {0x13a5, {1|D, {0xab75}}},
- {0x13a6, {1|D, {0xab76}}},
- {0x13a7, {1|D, {0xab77}}},
- {0x13a8, {1|D, {0xab78}}},
- {0x13a9, {1|D, {0xab79}}},
- {0x13aa, {1|D, {0xab7a}}},
- {0x13ab, {1|D, {0xab7b}}},
- {0x13ac, {1|D, {0xab7c}}},
- {0x13ad, {1|D, {0xab7d}}},
- {0x13ae, {1|D, {0xab7e}}},
- {0x13af, {1|D, {0xab7f}}},
- {0x13b0, {1|D, {0xab80}}},
- {0x13b1, {1|D, {0xab81}}},
- {0x13b2, {1|D, {0xab82}}},
- {0x13b3, {1|D, {0xab83}}},
- {0x13b4, {1|D, {0xab84}}},
- {0x13b5, {1|D, {0xab85}}},
- {0x13b6, {1|D, {0xab86}}},
- {0x13b7, {1|D, {0xab87}}},
- {0x13b8, {1|D, {0xab88}}},
- {0x13b9, {1|D, {0xab89}}},
- {0x13ba, {1|D, {0xab8a}}},
- {0x13bb, {1|D, {0xab8b}}},
- {0x13bc, {1|D, {0xab8c}}},
- {0x13bd, {1|D, {0xab8d}}},
- {0x13be, {1|D, {0xab8e}}},
- {0x13bf, {1|D, {0xab8f}}},
- {0x13c0, {1|D, {0xab90}}},
- {0x13c1, {1|D, {0xab91}}},
- {0x13c2, {1|D, {0xab92}}},
- {0x13c3, {1|D, {0xab93}}},
- {0x13c4, {1|D, {0xab94}}},
- {0x13c5, {1|D, {0xab95}}},
- {0x13c6, {1|D, {0xab96}}},
- {0x13c7, {1|D, {0xab97}}},
- {0x13c8, {1|D, {0xab98}}},
- {0x13c9, {1|D, {0xab99}}},
- {0x13ca, {1|D, {0xab9a}}},
- {0x13cb, {1|D, {0xab9b}}},
- {0x13cc, {1|D, {0xab9c}}},
- {0x13cd, {1|D, {0xab9d}}},
- {0x13ce, {1|D, {0xab9e}}},
- {0x13cf, {1|D, {0xab9f}}},
- {0x13d0, {1|D, {0xaba0}}},
- {0x13d1, {1|D, {0xaba1}}},
- {0x13d2, {1|D, {0xaba2}}},
- {0x13d3, {1|D, {0xaba3}}},
- {0x13d4, {1|D, {0xaba4}}},
- {0x13d5, {1|D, {0xaba5}}},
- {0x13d6, {1|D, {0xaba6}}},
- {0x13d7, {1|D, {0xaba7}}},
- {0x13d8, {1|D, {0xaba8}}},
- {0x13d9, {1|D, {0xaba9}}},
- {0x13da, {1|D, {0xabaa}}},
- {0x13db, {1|D, {0xabab}}},
- {0x13dc, {1|D, {0xabac}}},
- {0x13dd, {1|D, {0xabad}}},
- {0x13de, {1|D, {0xabae}}},
- {0x13df, {1|D, {0xabaf}}},
- {0x13e0, {1|D, {0xabb0}}},
- {0x13e1, {1|D, {0xabb1}}},
- {0x13e2, {1|D, {0xabb2}}},
- {0x13e3, {1|D, {0xabb3}}},
- {0x13e4, {1|D, {0xabb4}}},
- {0x13e5, {1|D, {0xabb5}}},
- {0x13e6, {1|D, {0xabb6}}},
- {0x13e7, {1|D, {0xabb7}}},
- {0x13e8, {1|D, {0xabb8}}},
- {0x13e9, {1|D, {0xabb9}}},
- {0x13ea, {1|D, {0xabba}}},
- {0x13eb, {1|D, {0xabbb}}},
- {0x13ec, {1|D, {0xabbc}}},
- {0x13ed, {1|D, {0xabbd}}},
- {0x13ee, {1|D, {0xabbe}}},
- {0x13ef, {1|D, {0xabbf}}},
- {0x13f0, {1|D, {0x13f8}}},
- {0x13f1, {1|D, {0x13f9}}},
- {0x13f2, {1|D, {0x13fa}}},
- {0x13f3, {1|D, {0x13fb}}},
- {0x13f4, {1|D, {0x13fc}}},
- {0x13f5, {1|D, {0x13fd}}},
- {0x1d79, {1|U, {0xa77d}}},
- {0x1d7d, {1|U, {0x2c63}}},
- {0x1e01, {1|U, {0x1e00}}},
- {0x1e03, {1|U, {0x1e02}}},
- {0x1e05, {1|U, {0x1e04}}},
- {0x1e07, {1|U, {0x1e06}}},
- {0x1e09, {1|U, {0x1e08}}},
- {0x1e0b, {1|U, {0x1e0a}}},
- {0x1e0d, {1|U, {0x1e0c}}},
- {0x1e0f, {1|U, {0x1e0e}}},
- {0x1e11, {1|U, {0x1e10}}},
- {0x1e13, {1|U, {0x1e12}}},
- {0x1e15, {1|U, {0x1e14}}},
- {0x1e17, {1|U, {0x1e16}}},
- {0x1e19, {1|U, {0x1e18}}},
- {0x1e1b, {1|U, {0x1e1a}}},
- {0x1e1d, {1|U, {0x1e1c}}},
- {0x1e1f, {1|U, {0x1e1e}}},
- {0x1e21, {1|U, {0x1e20}}},
- {0x1e23, {1|U, {0x1e22}}},
- {0x1e25, {1|U, {0x1e24}}},
- {0x1e27, {1|U, {0x1e26}}},
- {0x1e29, {1|U, {0x1e28}}},
- {0x1e2b, {1|U, {0x1e2a}}},
- {0x1e2d, {1|U, {0x1e2c}}},
- {0x1e2f, {1|U, {0x1e2e}}},
- {0x1e31, {1|U, {0x1e30}}},
- {0x1e33, {1|U, {0x1e32}}},
- {0x1e35, {1|U, {0x1e34}}},
- {0x1e37, {1|U, {0x1e36}}},
- {0x1e39, {1|U, {0x1e38}}},
- {0x1e3b, {1|U, {0x1e3a}}},
- {0x1e3d, {1|U, {0x1e3c}}},
- {0x1e3f, {1|U, {0x1e3e}}},
- {0x1e41, {1|U, {0x1e40}}},
- {0x1e43, {1|U, {0x1e42}}},
- {0x1e45, {1|U, {0x1e44}}},
- {0x1e47, {1|U, {0x1e46}}},
- {0x1e49, {1|U, {0x1e48}}},
- {0x1e4b, {1|U, {0x1e4a}}},
- {0x1e4d, {1|U, {0x1e4c}}},
- {0x1e4f, {1|U, {0x1e4e}}},
- {0x1e51, {1|U, {0x1e50}}},
- {0x1e53, {1|U, {0x1e52}}},
- {0x1e55, {1|U, {0x1e54}}},
- {0x1e57, {1|U, {0x1e56}}},
- {0x1e59, {1|U, {0x1e58}}},
- {0x1e5b, {1|U, {0x1e5a}}},
- {0x1e5d, {1|U, {0x1e5c}}},
- {0x1e5f, {1|U, {0x1e5e}}},
- {0x1e61, {2|U, {0x1e60, 0x1e9b}}},
- {0x1e63, {1|U, {0x1e62}}},
- {0x1e65, {1|U, {0x1e64}}},
- {0x1e67, {1|U, {0x1e66}}},
- {0x1e69, {1|U, {0x1e68}}},
- {0x1e6b, {1|U, {0x1e6a}}},
- {0x1e6d, {1|U, {0x1e6c}}},
- {0x1e6f, {1|U, {0x1e6e}}},
- {0x1e71, {1|U, {0x1e70}}},
- {0x1e73, {1|U, {0x1e72}}},
- {0x1e75, {1|U, {0x1e74}}},
- {0x1e77, {1|U, {0x1e76}}},
- {0x1e79, {1|U, {0x1e78}}},
- {0x1e7b, {1|U, {0x1e7a}}},
- {0x1e7d, {1|U, {0x1e7c}}},
- {0x1e7f, {1|U, {0x1e7e}}},
- {0x1e81, {1|U, {0x1e80}}},
- {0x1e83, {1|U, {0x1e82}}},
- {0x1e85, {1|U, {0x1e84}}},
- {0x1e87, {1|U, {0x1e86}}},
- {0x1e89, {1|U, {0x1e88}}},
- {0x1e8b, {1|U, {0x1e8a}}},
- {0x1e8d, {1|U, {0x1e8c}}},
- {0x1e8f, {1|U, {0x1e8e}}},
- {0x1e91, {1|U, {0x1e90}}},
- {0x1e93, {1|U, {0x1e92}}},
- {0x1e95, {1|U, {0x1e94}}},
- {0x1ea1, {1|U, {0x1ea0}}},
- {0x1ea3, {1|U, {0x1ea2}}},
- {0x1ea5, {1|U, {0x1ea4}}},
- {0x1ea7, {1|U, {0x1ea6}}},
- {0x1ea9, {1|U, {0x1ea8}}},
- {0x1eab, {1|U, {0x1eaa}}},
- {0x1ead, {1|U, {0x1eac}}},
- {0x1eaf, {1|U, {0x1eae}}},
- {0x1eb1, {1|U, {0x1eb0}}},
- {0x1eb3, {1|U, {0x1eb2}}},
- {0x1eb5, {1|U, {0x1eb4}}},
- {0x1eb7, {1|U, {0x1eb6}}},
- {0x1eb9, {1|U, {0x1eb8}}},
- {0x1ebb, {1|U, {0x1eba}}},
- {0x1ebd, {1|U, {0x1ebc}}},
- {0x1ebf, {1|U, {0x1ebe}}},
- {0x1ec1, {1|U, {0x1ec0}}},
- {0x1ec3, {1|U, {0x1ec2}}},
- {0x1ec5, {1|U, {0x1ec4}}},
- {0x1ec7, {1|U, {0x1ec6}}},
- {0x1ec9, {1|U, {0x1ec8}}},
- {0x1ecb, {1|U, {0x1eca}}},
- {0x1ecd, {1|U, {0x1ecc}}},
- {0x1ecf, {1|U, {0x1ece}}},
- {0x1ed1, {1|U, {0x1ed0}}},
- {0x1ed3, {1|U, {0x1ed2}}},
- {0x1ed5, {1|U, {0x1ed4}}},
- {0x1ed7, {1|U, {0x1ed6}}},
- {0x1ed9, {1|U, {0x1ed8}}},
- {0x1edb, {1|U, {0x1eda}}},
- {0x1edd, {1|U, {0x1edc}}},
- {0x1edf, {1|U, {0x1ede}}},
- {0x1ee1, {1|U, {0x1ee0}}},
- {0x1ee3, {1|U, {0x1ee2}}},
- {0x1ee5, {1|U, {0x1ee4}}},
- {0x1ee7, {1|U, {0x1ee6}}},
- {0x1ee9, {1|U, {0x1ee8}}},
- {0x1eeb, {1|U, {0x1eea}}},
- {0x1eed, {1|U, {0x1eec}}},
- {0x1eef, {1|U, {0x1eee}}},
- {0x1ef1, {1|U, {0x1ef0}}},
- {0x1ef3, {1|U, {0x1ef2}}},
- {0x1ef5, {1|U, {0x1ef4}}},
- {0x1ef7, {1|U, {0x1ef6}}},
- {0x1ef9, {1|U, {0x1ef8}}},
- {0x1efb, {1|U, {0x1efa}}},
- {0x1efd, {1|U, {0x1efc}}},
- {0x1eff, {1|U, {0x1efe}}},
- {0x1f00, {1|U, {0x1f08}}},
- {0x1f01, {1|U, {0x1f09}}},
- {0x1f02, {1|U, {0x1f0a}}},
- {0x1f03, {1|U, {0x1f0b}}},
- {0x1f04, {1|U, {0x1f0c}}},
- {0x1f05, {1|U, {0x1f0d}}},
- {0x1f06, {1|U, {0x1f0e}}},
- {0x1f07, {1|U, {0x1f0f}}},
- {0x1f10, {1|U, {0x1f18}}},
- {0x1f11, {1|U, {0x1f19}}},
- {0x1f12, {1|U, {0x1f1a}}},
- {0x1f13, {1|U, {0x1f1b}}},
- {0x1f14, {1|U, {0x1f1c}}},
- {0x1f15, {1|U, {0x1f1d}}},
- {0x1f20, {1|U, {0x1f28}}},
- {0x1f21, {1|U, {0x1f29}}},
- {0x1f22, {1|U, {0x1f2a}}},
- {0x1f23, {1|U, {0x1f2b}}},
- {0x1f24, {1|U, {0x1f2c}}},
- {0x1f25, {1|U, {0x1f2d}}},
- {0x1f26, {1|U, {0x1f2e}}},
- {0x1f27, {1|U, {0x1f2f}}},
- {0x1f30, {1|U, {0x1f38}}},
- {0x1f31, {1|U, {0x1f39}}},
- {0x1f32, {1|U, {0x1f3a}}},
- {0x1f33, {1|U, {0x1f3b}}},
- {0x1f34, {1|U, {0x1f3c}}},
- {0x1f35, {1|U, {0x1f3d}}},
- {0x1f36, {1|U, {0x1f3e}}},
- {0x1f37, {1|U, {0x1f3f}}},
- {0x1f40, {1|U, {0x1f48}}},
- {0x1f41, {1|U, {0x1f49}}},
- {0x1f42, {1|U, {0x1f4a}}},
- {0x1f43, {1|U, {0x1f4b}}},
- {0x1f44, {1|U, {0x1f4c}}},
- {0x1f45, {1|U, {0x1f4d}}},
- {0x1f51, {1|U, {0x1f59}}},
- {0x1f53, {1|U, {0x1f5b}}},
- {0x1f55, {1|U, {0x1f5d}}},
- {0x1f57, {1|U, {0x1f5f}}},
- {0x1f60, {1|U, {0x1f68}}},
- {0x1f61, {1|U, {0x1f69}}},
- {0x1f62, {1|U, {0x1f6a}}},
- {0x1f63, {1|U, {0x1f6b}}},
- {0x1f64, {1|U, {0x1f6c}}},
- {0x1f65, {1|U, {0x1f6d}}},
- {0x1f66, {1|U, {0x1f6e}}},
- {0x1f67, {1|U, {0x1f6f}}},
- {0x1f70, {1|U, {0x1fba}}},
- {0x1f71, {1|U, {0x1fbb}}},
- {0x1f72, {1|U, {0x1fc8}}},
- {0x1f73, {1|U, {0x1fc9}}},
- {0x1f74, {1|U, {0x1fca}}},
- {0x1f75, {1|U, {0x1fcb}}},
- {0x1f76, {1|U, {0x1fda}}},
- {0x1f77, {1|U, {0x1fdb}}},
- {0x1f78, {1|U, {0x1ff8}}},
- {0x1f79, {1|U, {0x1ff9}}},
- {0x1f7a, {1|U, {0x1fea}}},
- {0x1f7b, {1|U, {0x1feb}}},
- {0x1f7c, {1|U, {0x1ffa}}},
- {0x1f7d, {1|U, {0x1ffb}}},
- {0x1fb0, {1|U, {0x1fb8}}},
- {0x1fb1, {1|U, {0x1fb9}}},
- {0x1fd0, {1|U, {0x1fd8}}},
- {0x1fd1, {1|U, {0x1fd9}}},
- {0x1fe0, {1|U, {0x1fe8}}},
- {0x1fe1, {1|U, {0x1fe9}}},
- {0x1fe5, {1|U, {0x1fec}}},
- {0x214e, {1|U, {0x2132}}},
- {0x2170, {1|U, {0x2160}}},
- {0x2171, {1|U, {0x2161}}},
- {0x2172, {1|U, {0x2162}}},
- {0x2173, {1|U, {0x2163}}},
- {0x2174, {1|U, {0x2164}}},
- {0x2175, {1|U, {0x2165}}},
- {0x2176, {1|U, {0x2166}}},
- {0x2177, {1|U, {0x2167}}},
- {0x2178, {1|U, {0x2168}}},
- {0x2179, {1|U, {0x2169}}},
- {0x217a, {1|U, {0x216a}}},
- {0x217b, {1|U, {0x216b}}},
- {0x217c, {1|U, {0x216c}}},
- {0x217d, {1|U, {0x216d}}},
- {0x217e, {1|U, {0x216e}}},
- {0x217f, {1|U, {0x216f}}},
- {0x2184, {1|U, {0x2183}}},
- {0x24d0, {1|U, {0x24b6}}},
- {0x24d1, {1|U, {0x24b7}}},
- {0x24d2, {1|U, {0x24b8}}},
- {0x24d3, {1|U, {0x24b9}}},
- {0x24d4, {1|U, {0x24ba}}},
- {0x24d5, {1|U, {0x24bb}}},
- {0x24d6, {1|U, {0x24bc}}},
- {0x24d7, {1|U, {0x24bd}}},
- {0x24d8, {1|U, {0x24be}}},
- {0x24d9, {1|U, {0x24bf}}},
- {0x24da, {1|U, {0x24c0}}},
- {0x24db, {1|U, {0x24c1}}},
- {0x24dc, {1|U, {0x24c2}}},
- {0x24dd, {1|U, {0x24c3}}},
- {0x24de, {1|U, {0x24c4}}},
- {0x24df, {1|U, {0x24c5}}},
- {0x24e0, {1|U, {0x24c6}}},
- {0x24e1, {1|U, {0x24c7}}},
- {0x24e2, {1|U, {0x24c8}}},
- {0x24e3, {1|U, {0x24c9}}},
- {0x24e4, {1|U, {0x24ca}}},
- {0x24e5, {1|U, {0x24cb}}},
- {0x24e6, {1|U, {0x24cc}}},
- {0x24e7, {1|U, {0x24cd}}},
- {0x24e8, {1|U, {0x24ce}}},
- {0x24e9, {1|U, {0x24cf}}},
- {0x2c30, {1|U, {0x2c00}}},
- {0x2c31, {1|U, {0x2c01}}},
- {0x2c32, {1|U, {0x2c02}}},
- {0x2c33, {1|U, {0x2c03}}},
- {0x2c34, {1|U, {0x2c04}}},
- {0x2c35, {1|U, {0x2c05}}},
- {0x2c36, {1|U, {0x2c06}}},
- {0x2c37, {1|U, {0x2c07}}},
- {0x2c38, {1|U, {0x2c08}}},
- {0x2c39, {1|U, {0x2c09}}},
- {0x2c3a, {1|U, {0x2c0a}}},
- {0x2c3b, {1|U, {0x2c0b}}},
- {0x2c3c, {1|U, {0x2c0c}}},
- {0x2c3d, {1|U, {0x2c0d}}},
- {0x2c3e, {1|U, {0x2c0e}}},
- {0x2c3f, {1|U, {0x2c0f}}},
- {0x2c40, {1|U, {0x2c10}}},
- {0x2c41, {1|U, {0x2c11}}},
- {0x2c42, {1|U, {0x2c12}}},
- {0x2c43, {1|U, {0x2c13}}},
- {0x2c44, {1|U, {0x2c14}}},
- {0x2c45, {1|U, {0x2c15}}},
- {0x2c46, {1|U, {0x2c16}}},
- {0x2c47, {1|U, {0x2c17}}},
- {0x2c48, {1|U, {0x2c18}}},
- {0x2c49, {1|U, {0x2c19}}},
- {0x2c4a, {1|U, {0x2c1a}}},
- {0x2c4b, {1|U, {0x2c1b}}},
- {0x2c4c, {1|U, {0x2c1c}}},
- {0x2c4d, {1|U, {0x2c1d}}},
- {0x2c4e, {1|U, {0x2c1e}}},
- {0x2c4f, {1|U, {0x2c1f}}},
- {0x2c50, {1|U, {0x2c20}}},
- {0x2c51, {1|U, {0x2c21}}},
- {0x2c52, {1|U, {0x2c22}}},
- {0x2c53, {1|U, {0x2c23}}},
- {0x2c54, {1|U, {0x2c24}}},
- {0x2c55, {1|U, {0x2c25}}},
- {0x2c56, {1|U, {0x2c26}}},
- {0x2c57, {1|U, {0x2c27}}},
- {0x2c58, {1|U, {0x2c28}}},
- {0x2c59, {1|U, {0x2c29}}},
- {0x2c5a, {1|U, {0x2c2a}}},
- {0x2c5b, {1|U, {0x2c2b}}},
- {0x2c5c, {1|U, {0x2c2c}}},
- {0x2c5d, {1|U, {0x2c2d}}},
- {0x2c5e, {1|U, {0x2c2e}}},
- {0x2c61, {1|U, {0x2c60}}},
- {0x2c65, {1|U, {0x023a}}},
- {0x2c66, {1|U, {0x023e}}},
- {0x2c68, {1|U, {0x2c67}}},
- {0x2c6a, {1|U, {0x2c69}}},
- {0x2c6c, {1|U, {0x2c6b}}},
- {0x2c73, {1|U, {0x2c72}}},
- {0x2c76, {1|U, {0x2c75}}},
- {0x2c81, {1|U, {0x2c80}}},
- {0x2c83, {1|U, {0x2c82}}},
- {0x2c85, {1|U, {0x2c84}}},
- {0x2c87, {1|U, {0x2c86}}},
- {0x2c89, {1|U, {0x2c88}}},
- {0x2c8b, {1|U, {0x2c8a}}},
- {0x2c8d, {1|U, {0x2c8c}}},
- {0x2c8f, {1|U, {0x2c8e}}},
- {0x2c91, {1|U, {0x2c90}}},
- {0x2c93, {1|U, {0x2c92}}},
- {0x2c95, {1|U, {0x2c94}}},
- {0x2c97, {1|U, {0x2c96}}},
- {0x2c99, {1|U, {0x2c98}}},
- {0x2c9b, {1|U, {0x2c9a}}},
- {0x2c9d, {1|U, {0x2c9c}}},
- {0x2c9f, {1|U, {0x2c9e}}},
- {0x2ca1, {1|U, {0x2ca0}}},
- {0x2ca3, {1|U, {0x2ca2}}},
- {0x2ca5, {1|U, {0x2ca4}}},
- {0x2ca7, {1|U, {0x2ca6}}},
- {0x2ca9, {1|U, {0x2ca8}}},
- {0x2cab, {1|U, {0x2caa}}},
- {0x2cad, {1|U, {0x2cac}}},
- {0x2caf, {1|U, {0x2cae}}},
- {0x2cb1, {1|U, {0x2cb0}}},
- {0x2cb3, {1|U, {0x2cb2}}},
- {0x2cb5, {1|U, {0x2cb4}}},
- {0x2cb7, {1|U, {0x2cb6}}},
- {0x2cb9, {1|U, {0x2cb8}}},
- {0x2cbb, {1|U, {0x2cba}}},
- {0x2cbd, {1|U, {0x2cbc}}},
- {0x2cbf, {1|U, {0x2cbe}}},
- {0x2cc1, {1|U, {0x2cc0}}},
- {0x2cc3, {1|U, {0x2cc2}}},
- {0x2cc5, {1|U, {0x2cc4}}},
- {0x2cc7, {1|U, {0x2cc6}}},
- {0x2cc9, {1|U, {0x2cc8}}},
- {0x2ccb, {1|U, {0x2cca}}},
- {0x2ccd, {1|U, {0x2ccc}}},
- {0x2ccf, {1|U, {0x2cce}}},
- {0x2cd1, {1|U, {0x2cd0}}},
- {0x2cd3, {1|U, {0x2cd2}}},
- {0x2cd5, {1|U, {0x2cd4}}},
- {0x2cd7, {1|U, {0x2cd6}}},
- {0x2cd9, {1|U, {0x2cd8}}},
- {0x2cdb, {1|U, {0x2cda}}},
- {0x2cdd, {1|U, {0x2cdc}}},
- {0x2cdf, {1|U, {0x2cde}}},
- {0x2ce1, {1|U, {0x2ce0}}},
- {0x2ce3, {1|U, {0x2ce2}}},
- {0x2cec, {1|U, {0x2ceb}}},
- {0x2cee, {1|U, {0x2ced}}},
- {0x2cf3, {1|U, {0x2cf2}}},
- {0x2d00, {1|U, {0x10a0}}},
- {0x2d01, {1|U, {0x10a1}}},
- {0x2d02, {1|U, {0x10a2}}},
- {0x2d03, {1|U, {0x10a3}}},
- {0x2d04, {1|U, {0x10a4}}},
- {0x2d05, {1|U, {0x10a5}}},
- {0x2d06, {1|U, {0x10a6}}},
- {0x2d07, {1|U, {0x10a7}}},
- {0x2d08, {1|U, {0x10a8}}},
- {0x2d09, {1|U, {0x10a9}}},
- {0x2d0a, {1|U, {0x10aa}}},
- {0x2d0b, {1|U, {0x10ab}}},
- {0x2d0c, {1|U, {0x10ac}}},
- {0x2d0d, {1|U, {0x10ad}}},
- {0x2d0e, {1|U, {0x10ae}}},
- {0x2d0f, {1|U, {0x10af}}},
- {0x2d10, {1|U, {0x10b0}}},
- {0x2d11, {1|U, {0x10b1}}},
- {0x2d12, {1|U, {0x10b2}}},
- {0x2d13, {1|U, {0x10b3}}},
- {0x2d14, {1|U, {0x10b4}}},
- {0x2d15, {1|U, {0x10b5}}},
- {0x2d16, {1|U, {0x10b6}}},
- {0x2d17, {1|U, {0x10b7}}},
- {0x2d18, {1|U, {0x10b8}}},
- {0x2d19, {1|U, {0x10b9}}},
- {0x2d1a, {1|U, {0x10ba}}},
- {0x2d1b, {1|U, {0x10bb}}},
- {0x2d1c, {1|U, {0x10bc}}},
- {0x2d1d, {1|U, {0x10bd}}},
- {0x2d1e, {1|U, {0x10be}}},
- {0x2d1f, {1|U, {0x10bf}}},
- {0x2d20, {1|U, {0x10c0}}},
- {0x2d21, {1|U, {0x10c1}}},
- {0x2d22, {1|U, {0x10c2}}},
- {0x2d23, {1|U, {0x10c3}}},
- {0x2d24, {1|U, {0x10c4}}},
- {0x2d25, {1|U, {0x10c5}}},
- {0x2d27, {1|U, {0x10c7}}},
- {0x2d2d, {1|U, {0x10cd}}},
- {0xa641, {1|U, {0xa640}}},
- {0xa643, {1|U, {0xa642}}},
- {0xa645, {1|U, {0xa644}}},
- {0xa647, {1|U, {0xa646}}},
- {0xa649, {1|U, {0xa648}}},
- {0xa64b, {2, {0x1c88, 0xa64a}}},
- {0xa64d, {1|U, {0xa64c}}},
- {0xa64f, {1|U, {0xa64e}}},
- {0xa651, {1|U, {0xa650}}},
- {0xa653, {1|U, {0xa652}}},
- {0xa655, {1|U, {0xa654}}},
- {0xa657, {1|U, {0xa656}}},
- {0xa659, {1|U, {0xa658}}},
- {0xa65b, {1|U, {0xa65a}}},
- {0xa65d, {1|U, {0xa65c}}},
- {0xa65f, {1|U, {0xa65e}}},
- {0xa661, {1|U, {0xa660}}},
- {0xa663, {1|U, {0xa662}}},
- {0xa665, {1|U, {0xa664}}},
- {0xa667, {1|U, {0xa666}}},
- {0xa669, {1|U, {0xa668}}},
- {0xa66b, {1|U, {0xa66a}}},
- {0xa66d, {1|U, {0xa66c}}},
- {0xa681, {1|U, {0xa680}}},
- {0xa683, {1|U, {0xa682}}},
- {0xa685, {1|U, {0xa684}}},
- {0xa687, {1|U, {0xa686}}},
- {0xa689, {1|U, {0xa688}}},
- {0xa68b, {1|U, {0xa68a}}},
- {0xa68d, {1|U, {0xa68c}}},
- {0xa68f, {1|U, {0xa68e}}},
- {0xa691, {1|U, {0xa690}}},
- {0xa693, {1|U, {0xa692}}},
- {0xa695, {1|U, {0xa694}}},
- {0xa697, {1|U, {0xa696}}},
- {0xa699, {1|U, {0xa698}}},
- {0xa69b, {1|U, {0xa69a}}},
- {0xa723, {1|U, {0xa722}}},
- {0xa725, {1|U, {0xa724}}},
- {0xa727, {1|U, {0xa726}}},
- {0xa729, {1|U, {0xa728}}},
- {0xa72b, {1|U, {0xa72a}}},
- {0xa72d, {1|U, {0xa72c}}},
- {0xa72f, {1|U, {0xa72e}}},
- {0xa733, {1|U, {0xa732}}},
- {0xa735, {1|U, {0xa734}}},
- {0xa737, {1|U, {0xa736}}},
- {0xa739, {1|U, {0xa738}}},
- {0xa73b, {1|U, {0xa73a}}},
- {0xa73d, {1|U, {0xa73c}}},
- {0xa73f, {1|U, {0xa73e}}},
- {0xa741, {1|U, {0xa740}}},
- {0xa743, {1|U, {0xa742}}},
- {0xa745, {1|U, {0xa744}}},
- {0xa747, {1|U, {0xa746}}},
- {0xa749, {1|U, {0xa748}}},
- {0xa74b, {1|U, {0xa74a}}},
- {0xa74d, {1|U, {0xa74c}}},
- {0xa74f, {1|U, {0xa74e}}},
- {0xa751, {1|U, {0xa750}}},
- {0xa753, {1|U, {0xa752}}},
- {0xa755, {1|U, {0xa754}}},
- {0xa757, {1|U, {0xa756}}},
- {0xa759, {1|U, {0xa758}}},
- {0xa75b, {1|U, {0xa75a}}},
- {0xa75d, {1|U, {0xa75c}}},
- {0xa75f, {1|U, {0xa75e}}},
- {0xa761, {1|U, {0xa760}}},
- {0xa763, {1|U, {0xa762}}},
- {0xa765, {1|U, {0xa764}}},
- {0xa767, {1|U, {0xa766}}},
- {0xa769, {1|U, {0xa768}}},
- {0xa76b, {1|U, {0xa76a}}},
- {0xa76d, {1|U, {0xa76c}}},
- {0xa76f, {1|U, {0xa76e}}},
- {0xa77a, {1|U, {0xa779}}},
- {0xa77c, {1|U, {0xa77b}}},
- {0xa77f, {1|U, {0xa77e}}},
- {0xa781, {1|U, {0xa780}}},
- {0xa783, {1|U, {0xa782}}},
- {0xa785, {1|U, {0xa784}}},
- {0xa787, {1|U, {0xa786}}},
- {0xa78c, {1|U, {0xa78b}}},
- {0xa791, {1|U, {0xa790}}},
- {0xa793, {1|U, {0xa792}}},
- {0xa797, {1|U, {0xa796}}},
- {0xa799, {1|U, {0xa798}}},
- {0xa79b, {1|U, {0xa79a}}},
- {0xa79d, {1|U, {0xa79c}}},
- {0xa79f, {1|U, {0xa79e}}},
- {0xa7a1, {1|U, {0xa7a0}}},
- {0xa7a3, {1|U, {0xa7a2}}},
- {0xa7a5, {1|U, {0xa7a4}}},
- {0xa7a7, {1|U, {0xa7a6}}},
- {0xa7a9, {1|U, {0xa7a8}}},
- {0xa7b5, {1|U, {0xa7b4}}},
- {0xa7b7, {1|U, {0xa7b6}}},
- {0xab53, {1|U, {0xa7b3}}},
- {0xff41, {1|U, {0xff21}}},
- {0xff42, {1|U, {0xff22}}},
- {0xff43, {1|U, {0xff23}}},
- {0xff44, {1|U, {0xff24}}},
- {0xff45, {1|U, {0xff25}}},
- {0xff46, {1|U, {0xff26}}},
- {0xff47, {1|U, {0xff27}}},
- {0xff48, {1|U, {0xff28}}},
- {0xff49, {1|U, {0xff29}}},
- {0xff4a, {1|U, {0xff2a}}},
- {0xff4b, {1|U, {0xff2b}}},
- {0xff4c, {1|U, {0xff2c}}},
- {0xff4d, {1|U, {0xff2d}}},
- {0xff4e, {1|U, {0xff2e}}},
- {0xff4f, {1|U, {0xff2f}}},
- {0xff50, {1|U, {0xff30}}},
- {0xff51, {1|U, {0xff31}}},
- {0xff52, {1|U, {0xff32}}},
- {0xff53, {1|U, {0xff33}}},
- {0xff54, {1|U, {0xff34}}},
- {0xff55, {1|U, {0xff35}}},
- {0xff56, {1|U, {0xff36}}},
- {0xff57, {1|U, {0xff37}}},
- {0xff58, {1|U, {0xff38}}},
- {0xff59, {1|U, {0xff39}}},
- {0xff5a, {1|U, {0xff3a}}},
- {0x10428, {1|U, {0x10400}}},
- {0x10429, {1|U, {0x10401}}},
- {0x1042a, {1|U, {0x10402}}},
- {0x1042b, {1|U, {0x10403}}},
- {0x1042c, {1|U, {0x10404}}},
- {0x1042d, {1|U, {0x10405}}},
- {0x1042e, {1|U, {0x10406}}},
- {0x1042f, {1|U, {0x10407}}},
- {0x10430, {1|U, {0x10408}}},
- {0x10431, {1|U, {0x10409}}},
- {0x10432, {1|U, {0x1040a}}},
- {0x10433, {1|U, {0x1040b}}},
- {0x10434, {1|U, {0x1040c}}},
- {0x10435, {1|U, {0x1040d}}},
- {0x10436, {1|U, {0x1040e}}},
- {0x10437, {1|U, {0x1040f}}},
- {0x10438, {1|U, {0x10410}}},
- {0x10439, {1|U, {0x10411}}},
- {0x1043a, {1|U, {0x10412}}},
- {0x1043b, {1|U, {0x10413}}},
- {0x1043c, {1|U, {0x10414}}},
- {0x1043d, {1|U, {0x10415}}},
- {0x1043e, {1|U, {0x10416}}},
- {0x1043f, {1|U, {0x10417}}},
- {0x10440, {1|U, {0x10418}}},
- {0x10441, {1|U, {0x10419}}},
- {0x10442, {1|U, {0x1041a}}},
- {0x10443, {1|U, {0x1041b}}},
- {0x10444, {1|U, {0x1041c}}},
- {0x10445, {1|U, {0x1041d}}},
- {0x10446, {1|U, {0x1041e}}},
- {0x10447, {1|U, {0x1041f}}},
- {0x10448, {1|U, {0x10420}}},
- {0x10449, {1|U, {0x10421}}},
- {0x1044a, {1|U, {0x10422}}},
- {0x1044b, {1|U, {0x10423}}},
- {0x1044c, {1|U, {0x10424}}},
- {0x1044d, {1|U, {0x10425}}},
- {0x1044e, {1|U, {0x10426}}},
- {0x1044f, {1|U, {0x10427}}},
- {0x104d8, {1|U, {0x104b0}}},
- {0x104d9, {1|U, {0x104b1}}},
- {0x104da, {1|U, {0x104b2}}},
- {0x104db, {1|U, {0x104b3}}},
- {0x104dc, {1|U, {0x104b4}}},
- {0x104dd, {1|U, {0x104b5}}},
- {0x104de, {1|U, {0x104b6}}},
- {0x104df, {1|U, {0x104b7}}},
- {0x104e0, {1|U, {0x104b8}}},
- {0x104e1, {1|U, {0x104b9}}},
- {0x104e2, {1|U, {0x104ba}}},
- {0x104e3, {1|U, {0x104bb}}},
- {0x104e4, {1|U, {0x104bc}}},
- {0x104e5, {1|U, {0x104bd}}},
- {0x104e6, {1|U, {0x104be}}},
- {0x104e7, {1|U, {0x104bf}}},
- {0x104e8, {1|U, {0x104c0}}},
- {0x104e9, {1|U, {0x104c1}}},
- {0x104ea, {1|U, {0x104c2}}},
- {0x104eb, {1|U, {0x104c3}}},
- {0x104ec, {1|U, {0x104c4}}},
- {0x104ed, {1|U, {0x104c5}}},
- {0x104ee, {1|U, {0x104c6}}},
- {0x104ef, {1|U, {0x104c7}}},
- {0x104f0, {1|U, {0x104c8}}},
- {0x104f1, {1|U, {0x104c9}}},
- {0x104f2, {1|U, {0x104ca}}},
- {0x104f3, {1|U, {0x104cb}}},
- {0x104f4, {1|U, {0x104cc}}},
- {0x104f5, {1|U, {0x104cd}}},
- {0x104f6, {1|U, {0x104ce}}},
- {0x104f7, {1|U, {0x104cf}}},
- {0x104f8, {1|U, {0x104d0}}},
- {0x104f9, {1|U, {0x104d1}}},
- {0x104fa, {1|U, {0x104d2}}},
- {0x104fb, {1|U, {0x104d3}}},
- {0x10cc0, {1|U, {0x10c80}}},
- {0x10cc1, {1|U, {0x10c81}}},
- {0x10cc2, {1|U, {0x10c82}}},
- {0x10cc3, {1|U, {0x10c83}}},
- {0x10cc4, {1|U, {0x10c84}}},
- {0x10cc5, {1|U, {0x10c85}}},
- {0x10cc6, {1|U, {0x10c86}}},
- {0x10cc7, {1|U, {0x10c87}}},
- {0x10cc8, {1|U, {0x10c88}}},
- {0x10cc9, {1|U, {0x10c89}}},
- {0x10cca, {1|U, {0x10c8a}}},
- {0x10ccb, {1|U, {0x10c8b}}},
- {0x10ccc, {1|U, {0x10c8c}}},
- {0x10ccd, {1|U, {0x10c8d}}},
- {0x10cce, {1|U, {0x10c8e}}},
- {0x10ccf, {1|U, {0x10c8f}}},
- {0x10cd0, {1|U, {0x10c90}}},
- {0x10cd1, {1|U, {0x10c91}}},
- {0x10cd2, {1|U, {0x10c92}}},
- {0x10cd3, {1|U, {0x10c93}}},
- {0x10cd4, {1|U, {0x10c94}}},
- {0x10cd5, {1|U, {0x10c95}}},
- {0x10cd6, {1|U, {0x10c96}}},
- {0x10cd7, {1|U, {0x10c97}}},
- {0x10cd8, {1|U, {0x10c98}}},
- {0x10cd9, {1|U, {0x10c99}}},
- {0x10cda, {1|U, {0x10c9a}}},
- {0x10cdb, {1|U, {0x10c9b}}},
- {0x10cdc, {1|U, {0x10c9c}}},
- {0x10cdd, {1|U, {0x10c9d}}},
- {0x10cde, {1|U, {0x10c9e}}},
- {0x10cdf, {1|U, {0x10c9f}}},
- {0x10ce0, {1|U, {0x10ca0}}},
- {0x10ce1, {1|U, {0x10ca1}}},
- {0x10ce2, {1|U, {0x10ca2}}},
- {0x10ce3, {1|U, {0x10ca3}}},
- {0x10ce4, {1|U, {0x10ca4}}},
- {0x10ce5, {1|U, {0x10ca5}}},
- {0x10ce6, {1|U, {0x10ca6}}},
- {0x10ce7, {1|U, {0x10ca7}}},
- {0x10ce8, {1|U, {0x10ca8}}},
- {0x10ce9, {1|U, {0x10ca9}}},
- {0x10cea, {1|U, {0x10caa}}},
- {0x10ceb, {1|U, {0x10cab}}},
- {0x10cec, {1|U, {0x10cac}}},
- {0x10ced, {1|U, {0x10cad}}},
- {0x10cee, {1|U, {0x10cae}}},
- {0x10cef, {1|U, {0x10caf}}},
- {0x10cf0, {1|U, {0x10cb0}}},
- {0x10cf1, {1|U, {0x10cb1}}},
- {0x10cf2, {1|U, {0x10cb2}}},
- {0x118c0, {1|U, {0x118a0}}},
- {0x118c1, {1|U, {0x118a1}}},
- {0x118c2, {1|U, {0x118a2}}},
- {0x118c3, {1|U, {0x118a3}}},
- {0x118c4, {1|U, {0x118a4}}},
- {0x118c5, {1|U, {0x118a5}}},
- {0x118c6, {1|U, {0x118a6}}},
- {0x118c7, {1|U, {0x118a7}}},
- {0x118c8, {1|U, {0x118a8}}},
- {0x118c9, {1|U, {0x118a9}}},
- {0x118ca, {1|U, {0x118aa}}},
- {0x118cb, {1|U, {0x118ab}}},
- {0x118cc, {1|U, {0x118ac}}},
- {0x118cd, {1|U, {0x118ad}}},
- {0x118ce, {1|U, {0x118ae}}},
- {0x118cf, {1|U, {0x118af}}},
- {0x118d0, {1|U, {0x118b0}}},
- {0x118d1, {1|U, {0x118b1}}},
- {0x118d2, {1|U, {0x118b2}}},
- {0x118d3, {1|U, {0x118b3}}},
- {0x118d4, {1|U, {0x118b4}}},
- {0x118d5, {1|U, {0x118b5}}},
- {0x118d6, {1|U, {0x118b6}}},
- {0x118d7, {1|U, {0x118b7}}},
- {0x118d8, {1|U, {0x118b8}}},
- {0x118d9, {1|U, {0x118b9}}},
- {0x118da, {1|U, {0x118ba}}},
- {0x118db, {1|U, {0x118bb}}},
- {0x118dc, {1|U, {0x118bc}}},
- {0x118dd, {1|U, {0x118bd}}},
- {0x118de, {1|U, {0x118be}}},
- {0x118df, {1|U, {0x118bf}}},
- {0x1e922, {1|U, {0x1e900}}},
- {0x1e923, {1|U, {0x1e901}}},
- {0x1e924, {1|U, {0x1e902}}},
- {0x1e925, {1|U, {0x1e903}}},
- {0x1e926, {1|U, {0x1e904}}},
- {0x1e927, {1|U, {0x1e905}}},
- {0x1e928, {1|U, {0x1e906}}},
- {0x1e929, {1|U, {0x1e907}}},
- {0x1e92a, {1|U, {0x1e908}}},
- {0x1e92b, {1|U, {0x1e909}}},
- {0x1e92c, {1|U, {0x1e90a}}},
- {0x1e92d, {1|U, {0x1e90b}}},
- {0x1e92e, {1|U, {0x1e90c}}},
- {0x1e92f, {1|U, {0x1e90d}}},
- {0x1e930, {1|U, {0x1e90e}}},
- {0x1e931, {1|U, {0x1e90f}}},
- {0x1e932, {1|U, {0x1e910}}},
- {0x1e933, {1|U, {0x1e911}}},
- {0x1e934, {1|U, {0x1e912}}},
- {0x1e935, {1|U, {0x1e913}}},
- {0x1e936, {1|U, {0x1e914}}},
- {0x1e937, {1|U, {0x1e915}}},
- {0x1e938, {1|U, {0x1e916}}},
- {0x1e939, {1|U, {0x1e917}}},
- {0x1e93a, {1|U, {0x1e918}}},
- {0x1e93b, {1|U, {0x1e919}}},
- {0x1e93c, {1|U, {0x1e91a}}},
- {0x1e93d, {1|U, {0x1e91b}}},
- {0x1e93e, {1|U, {0x1e91c}}},
- {0x1e93f, {1|U, {0x1e91d}}},
- {0x1e940, {1|U, {0x1e91e}}},
- {0x1e941, {1|U, {0x1e91f}}},
- {0x1e942, {1|U, {0x1e920}}},
- {0x1e943, {1|U, {0x1e921}}},
-#define CaseUnfold_11_Locale (*(CaseUnfold_11_Type (*)[1])(CaseUnfold_11_Table+1266))
- {0x0069, {1|U, {0x0049}}},
-};
-
-/* C code produced by gperf version 3.0.4 */
-/* Command-line: gperf -7 -k1,2,3 -F,-1 -c -j1 -i1 -t -T -E -C -H onigenc_unicode_CaseUnfold_11_hash -N onigenc_unicode_CaseUnfold_11_lookup -n */
-
-/* maximum key range = 2216, duplicates = 0 */
-
-#ifdef __GNUC__
-__inline
-#else
-#ifdef __cplusplus
-inline
-#endif
-#endif
-/*ARGSUSED*/
-static unsigned int
-onigenc_unicode_CaseUnfold_11_hash(const OnigCodePoint code)
-{
- static const unsigned short asso_values[] =
- {
- 1, 2219, 2, 14, 4, 807, 9, 379, 10, 179,
- 70, 161, 2, 3, 411, 4, 2219, 2219, 2219, 2219,
- 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 80,
- 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219,
- 2219, 2219, 2219, 1, 2219, 2219, 2219, 2219, 2219, 2219,
- 2219, 2219, 2219, 210, 2219, 2219, 2219, 2219, 2219, 2219,
- 2219, 2219, 53, 2219, 7, 8, 306, 607, 169, 844,
- 431, 722, 125, 1047, 489, 1394, 15, 16, 324, 1361,
- 140, 521, 47, 461, 221, 985, 70, 965, 9, 1085,
- 51, 1029, 223, 11, 430, 1168, 122, 1457, 344, 930,
- 91, 711, 31, 651, 157, 772, 224, 876, 262, 900,
- 254, 686, 221, 830, 1335, 755, 432, 623, 1382, 675,
- 1275, 587, 99, 821, 1530, 958, 195, 810, 1518, 739,
- 330, 361, 767, 313, 941, 400, 925, 384, 1142, 295,
- 1295, 242, 1103, 229, 1082, 206, 1066, 104, 1317, 137,
- 1249, 263, 1229, 115, 1154, 71, 135, 60, 1211, 158,
- 1472, 175, 1232, 1, 1345, 27, 1269, 38, 1111, 87,
- 1189, 49, 1256, 503, 1157, 574, 1410, 556, 1200, 787,
- 948, 486, 1316, 797, 1218, 1044, 1282, 1007, 1126, 996,
- 818, 1019, 218, 1072
- };
- return asso_values[bits_of(code, 2)+66] + asso_values[bits_of(code, 1)+4] + asso_values[bits_of(code, 0)];
-}
-
-#ifdef __GNUC__
-__inline
-#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
-__attribute__ ((__gnu_inline__))
-#endif
-#endif
-static const CodePointList3 *
-onigenc_unicode_CaseUnfold_11_lookup(const OnigCodePoint code)
-{
- enum
- {
- MIN_CODE_VALUE = 0x61,
- MAX_CODE_VALUE = 0x1e943,
- TOTAL_KEYWORDS = 1267,
- MIN_WORD_LENGTH = 3,
- MAX_WORD_LENGTH = 3,
- MIN_HASH_VALUE = 3,
- MAX_HASH_VALUE = 2218
- };
-
- static const short wordlist[] =
- {
- -1, -1, -1,
- /*0x13e1*/ 542,
- /*0x0461*/ 339,
- /*0x04e1*/ 399,
- /*0x0061*/ 0,
- -1,
- /*0x104e1*/ 1122,
- /*0x1e61*/ 613,
- /*0x1ee1*/ 672,
- /*0x0161*/ 102,
- /*0x0261*/ 210,
- /*0x2ce1*/ 904,
- -1,
- /*0x049b*/ 364,
- -1, -1,
- /*0x24e1*/ 792,
- /*0x1e1b*/ 578,
- /*0x048b*/ 356,
- /*0x011b*/ 69,
- /*0x021b*/ 178,
- /*0x2c9b*/ 869,
- /*0x1e0b*/ 570,
- /*0x1e8b*/ 634,
- /*0x010b*/ 61,
- /*0x020b*/ 170,
- /*0x2c8b*/ 861,
- /*0x13e3*/ 544,
- /*0x0463*/ 340,
- /*0x04e3*/ 400,
- /*0x0063*/ 2,
- /*0x13a4*/ 481,
- /*0x104e3*/ 1124,
- /*0x1e63*/ 614,
- /*0x1ee3*/ 673,
- /*0x0163*/ 103,
- /*0x0263*/ 211,
- /*0x2ce3*/ 905,
- /*0x13e5*/ 546,
- /*0x0465*/ 341,
- /*0x04e5*/ 401,
- /*0x0065*/ 4,
- /*0x24e3*/ 794,
- /*0x104e5*/ 1126,
- /*0x1e65*/ 615,
- /*0x1ee5*/ 674,
- /*0x0165*/ 104,
- /*0x0265*/ 212,
- /*0xa761*/ 1016,
- /*0x13e9*/ 550,
- /*0x0469*/ 343,
- /*0x04e9*/ 403,
- /*0x0069*/ 1266,
- /*0x24e5*/ 796,
- /*0x104e9*/ 1130,
- /*0x1e69*/ 617,
- /*0x1ee9*/ 676,
- /*0x0169*/ 106,
- /*0x0269*/ 215,
- -1,
- /*0x13db*/ 536,
- /*0x045b*/ 334,
- /*0x04db*/ 396,
- -1,
- /*0x24e9*/ 800,
- /*0x104db*/ 1116,
- /*0x1e5b*/ 610,
- /*0x1edb*/ 669,
- /*0x015b*/ 99,
- /*0x025b*/ 207,
- /*0x2cdb*/ 901,
- /*0x13d9*/ 534,
- /*0x0459*/ 332,
- /*0x04d9*/ 395,
- /*0xa763*/ 1017,
- /*0x24db*/ 786,
- /*0x104d9*/ 1114,
- /*0x1e59*/ 609,
- /*0x1ed9*/ 668,
- /*0x0159*/ 98,
- /*0x0259*/ 206,
- /*0x2cd9*/ 900,
- -1,
- /*0x10ce1*/ 1182,
- -1,
- /*0xa765*/ 1018,
- /*0x24d9*/ 784,
- /*0x13e7*/ 548,
- /*0x0467*/ 342,
- /*0x04e7*/ 402,
- /*0x0067*/ 6,
- /*0x13a2*/ 479,
- /*0x104e7*/ 1128,
- /*0x1e67*/ 616,
- /*0x1ee7*/ 675,
- /*0x0167*/ 105,
- /*0xa769*/ 1020,
- -1, -1,
- /*0x13b8*/ 501,
- /*0x0438*/ 299,
- -1,
- /*0x24e7*/ 798,
- /*0x10438*/ 1089,
- /*0x13d1*/ 526,
- /*0x0451*/ 324,
- /*0x04d1*/ 391,
- /*0xa75b*/ 1013,
- -1,
- /*0x10ce3*/ 1184,
- /*0x1e51*/ 605,
- /*0x1ed1*/ 664,
- /*0x0151*/ 94,
- /*0x0251*/ 200,
- /*0x2cd1*/ 896,
- /*0x13d7*/ 532,
- /*0x0457*/ 330,
- /*0x04d7*/ 394,
- /*0xa759*/ 1012,
- /*0x24d1*/ 776,
- /*0x10ce5*/ 1186,
- /*0x1e57*/ 608,
- /*0x1ed7*/ 667,
- /*0x0157*/ 97,
- /*0x0257*/ 205,
- /*0x2cd7*/ 899,
- -1, -1,
- /*0x0586*/ 476,
- -1,
- /*0x24d7*/ 782,
- /*0x10ce9*/ 1190,
- -1, -1,
- /*0xa767*/ 1019,
- /*0x13da*/ 535,
- /*0x045a*/ 333,
- /*0x13d3*/ 528,
- /*0x0453*/ 326,
- /*0x04d3*/ 392,
- /*0x104da*/ 1115,
- /*0xa661*/ 965,
- /*0x10cdb*/ 1176,
- /*0x1e53*/ 606,
- /*0x1ed3*/ 665,
- /*0x0153*/ 95,
- /*0x0253*/ 202,
- /*0x2cd3*/ 897,
- -1, -1,
- /*0x24da*/ 785,
- /*0xa751*/ 1008,
- /*0x24d3*/ 778,
- /*0x10cd9*/ 1174,
- -1, -1, -1,
- /*0x13a6*/ 483,
- /*0x13dd*/ 538,
- /*0x045d*/ 336,
- /*0x04dd*/ 397,
- /*0x03e1*/ 279,
- /*0xa757*/ 1011,
- /*0x104dd*/ 1118,
- /*0x1e5d*/ 611,
- /*0x1edd*/ 670,
- /*0x015d*/ 100,
- /*0xa663*/ 966,
- /*0x2cdd*/ 902,
- /*0x10ce7*/ 1188,
- -1, -1,
- /*0x0582*/ 472,
- /*0x24dd*/ 788,
- -1,
- /*0x13df*/ 540,
- /*0x045f*/ 338,
- /*0x04df*/ 398,
- /*0xa665*/ 967,
- -1,
- /*0x104df*/ 1120,
- /*0x1e5f*/ 612,
- /*0x1edf*/ 671,
- /*0x015f*/ 101,
- /*0xa753*/ 1009,
- /*0x2cdf*/ 903,
- /*0x10cd1*/ 1166,
- /*0x03e3*/ 280,
- /*0xab53*/ 1046,
- /*0xa669*/ 969,
- /*0x24df*/ 790,
- -1, -1,
- /*0x028a*/ 229,
- /*0x028b*/ 230,
- /*0x13bc*/ 505,
- /*0x043c*/ 303,
- /*0x10cd7*/ 1172,
- /*0x03e5*/ 281,
- /*0x1043c*/ 1093,
- /*0xa65b*/ 962,
- -1, -1,
- /*0x013c*/ 84,
- /*0x023c*/ 190,
- /*0xa75d*/ 1014,
- /*0x13cf*/ 524,
- /*0x044f*/ 322,
- /*0x04cf*/ 390,
- /*0x03e9*/ 283,
- /*0x1044f*/ 1112,
- /*0xa659*/ 961,
- /*0x1e4f*/ 604,
- /*0x1ecf*/ 663,
- /*0x014f*/ 93,
- /*0x024f*/ 198,
- /*0x2ccf*/ 895,
- /*0x10cda*/ 1175,
- -1,
- /*0x10cd3*/ 1168,
- /*0x03db*/ 276,
- /*0x13ae*/ 491,
- /*0xa75f*/ 1015,
- /*0x2c61*/ 848,
- /*0x13a8*/ 485,
- /*0x1042e*/ 1079,
- /*0x017e*/ 116,
- /*0xa667*/ 968,
- /*0x10428*/ 1073,
- /*0x13cd*/ 522,
- /*0x044d*/ 320,
- /*0x03d9*/ 275,
- -1,
- /*0x1044d*/ 1110,
- -1,
- /*0x1e4d*/ 603,
- /*0x1ecd*/ 662,
- /*0x014d*/ 92,
- /*0x024d*/ 197,
- /*0x2ccd*/ 894,
- /*0x10cdd*/ 1178,
- -1,
- /*0x13cb*/ 520,
- /*0x044b*/ 318,
- /*0xa651*/ 957,
- -1,
- /*0x1044b*/ 1108,
- /*0x03e7*/ 282,
- /*0x1e4b*/ 602,
- /*0x1ecb*/ 661,
- /*0x014b*/ 91,
- /*0x024b*/ 196,
- /*0x2ccb*/ 893,
- /*0xa74f*/ 1007,
- /*0x13ac*/ 489,
- /*0xa657*/ 960,
- -1,
- /*0x10cdf*/ 1180,
- /*0x1042c*/ 1077,
- /*0x03b8*/ 252,
- /*0x2c65*/ 849,
- -1,
- /*0x13aa*/ 487,
- /*0x13d5*/ 530,
- /*0x0455*/ 328,
- /*0x04d5*/ 393,
- /*0x1042a*/ 1075,
- -1, -1,
- /*0x1e55*/ 607,
- /*0x1ed5*/ 666,
- /*0x0155*/ 96,
- /*0x118db*/ 1227,
- /*0x2cd5*/ 898,
- -1,
- /*0x03d7*/ 274,
- /*0xa74d*/ 1006,
- /*0xa653*/ 958,
- /*0x24d5*/ 780,
- -1, -1, -1,
- /*0x2c5b*/ 844,
- /*0x118d9*/ 1225,
- -1, -1, -1, -1,
- /*0x10ccf*/ 1164,
- /*0xa74b*/ 1005,
- -1, -1, -1,
- /*0x2c59*/ 842,
- -1,
- /*0x13c9*/ 518,
- /*0x0449*/ 316,
- -1,
- /*0xa65d*/ 963,
- /*0x10449*/ 1106,
- /*0x029e*/ 234,
- /*0x1e49*/ 601,
- /*0x1ec9*/ 660,
- -1,
- /*0x0249*/ 195,
- /*0x2cc9*/ 892,
- /*0x1f61*/ 729,
- -1, -1,
- /*0x0580*/ 470,
- /*0xa755*/ 1010,
- /*0x10ccd*/ 1162,
- -1,
- /*0x13c3*/ 512,
- /*0x0443*/ 310,
- /*0xa65f*/ 964,
- /*0x118d1*/ 1217,
- /*0x10443*/ 1100,
- /*0x03dd*/ 277,
- /*0x1e43*/ 598,
- /*0x1ec3*/ 657,
- /*0x2c38*/ 809,
- -1,
- /*0x2cc3*/ 889,
- /*0x10ccb*/ 1160,
- -1,
- /*0x2c51*/ 834,
- /*0x118d7*/ 1223,
- -1, -1,
- /*0x13c0*/ 509,
- /*0x0440*/ 307,
- /*0x1f63*/ 731,
- -1,
- /*0x10440*/ 1097,
- /*0x03df*/ 278,
- /*0x1f24*/ 706,
- /*0x2c57*/ 840,
- /*0x0140*/ 86,
- /*0x0240*/ 192,
- -1, -1,
- /*0xa749*/ 1004,
- /*0x1f65*/ 733,
- /*0x13a0*/ 477,
- /*0x10cd5*/ 1170,
- /*0xa64f*/ 956,
- /*0x118da*/ 1226,
- -1,
- /*0x118d3*/ 1219,
- -1, -1,
- /*0x1f10*/ 696,
- -1, -1,
- /*0x03bc*/ 256,
- -1,
- /*0x2c5a*/ 843,
- -1,
- /*0x2c53*/ 836,
- /*0xa743*/ 1001,
- /*0x13c1*/ 510,
- /*0x0441*/ 308,
- -1, -1,
- /*0x10441*/ 1098,
- -1,
- /*0x1e41*/ 597,
- /*0x1ec1*/ 656,
- /*0xa64d*/ 955,
- /*0x118dd*/ 1229,
- /*0x2cc1*/ 888,
- -1, -1, -1,
- /*0x1f14*/ 700,
- -1,
- /*0x10cc9*/ 1158,
- -1,
- /*0x01e1*/ 151,
- /*0x2c5d*/ 846,
- /*0x03ae*/ 243,
- /*0xa64b*/ 954,
- -1,
- /*0x13c7*/ 516,
- /*0x0447*/ 314,
- -1,
- /*0x118df*/ 1231,
- /*0x10447*/ 1104,
- /*0x03cd*/ 272,
- /*0x1e47*/ 600,
- /*0x1ec7*/ 659,
- /*0x1f67*/ 735,
- /*0x0247*/ 194,
- /*0x2cc7*/ 891,
- /*0x10cc3*/ 1152,
- /*0x1f22*/ 704,
- -1, -1,
- /*0x0292*/ 232,
- /*0x13c5*/ 514,
- /*0x0445*/ 312,
- /*0x03cb*/ 270,
- /*0xa655*/ 959,
- /*0x10445*/ 1102,
- /*0x01e3*/ 152,
- /*0x1e45*/ 599,
- /*0x1ec5*/ 658,
- /*0xa741*/ 1000,
- /*0x1f51*/ 724,
- /*0x2cc5*/ 890,
- /*0x0561*/ 439,
- /*0x10cc0*/ 1149,
- /*0xff59*/ 1071,
- /*0x03ac*/ 241,
- -1,
- /*0x01e5*/ 153,
- /*0x2c3c*/ 813,
- /*0x118cf*/ 1215,
- -1,
- /*0x1f57*/ 727,
- /*0x051b*/ 428,
- -1, -1, -1, -1,
- /*0x050b*/ 420,
- /*0x01e9*/ 155,
- /*0x2c4f*/ 832,
- -1,
- /*0x1f06*/ 694,
- /*0xa747*/ 1003,
- /*0x13b2*/ 495,
- /*0x0432*/ 293,
- /*0x0584*/ 474,
- /*0xa649*/ 953,
- /*0x10432*/ 1083,
- /*0x0563*/ 441,
- /*0x2d16*/ 931,
- -1,
- /*0x2d1b*/ 936,
- /*0x118cd*/ 1213,
- /*0x1f53*/ 725,
- /*0x10cc1*/ 1150,
- /*0x2d0a*/ 919,
- /*0x2d0b*/ 920,
- /*0xff51*/ 1063,
- /*0xa745*/ 1002,
- /*0x0565*/ 443,
- -1, -1,
- /*0x2c4d*/ 830,
- -1,
- /*0xa643*/ 950,
- /*0x118cb*/ 1211,
- /*0x03c9*/ 268,
- -1,
- /*0xff57*/ 1069,
- -1,
- /*0x0569*/ 447,
- /*0x2d24*/ 945,
- -1,
- /*0x1f26*/ 708,
- /*0x0491*/ 359,
- /*0x2c4b*/ 828,
- /*0x01e7*/ 154,
- /*0x10cc7*/ 1156,
- /*0x1e11*/ 573,
- /*0x1e91*/ 637,
- /*0x0111*/ 64,
- /*0x0211*/ 173,
- /*0x2c91*/ 864,
- /*0xa79b*/ 1036,
- /*0x03c3*/ 262,
- /*0x1f02*/ 690,
- /*0x118d5*/ 1221,
- /*0x2d10*/ 925,
- /*0xff5a*/ 1072,
- /*0x1e924*/ 1234,
- /*0xff53*/ 1065,
- /*0x2d18*/ 933,
- -1,
- /*0x10cc5*/ 1154,
- -1,
- /*0x0280*/ 224,
- /*0x2c55*/ 838,
- /*0x13f3*/ 560,
- /*0x0473*/ 348,
- /*0x04f3*/ 408,
- /*0x0073*/ 17,
- /*0x03c0*/ 260,
- /*0x104f3*/ 1140,
- /*0x1e73*/ 622,
- /*0x1ef3*/ 681,
- /*0x0173*/ 111,
- -1,
- /*0x2cf3*/ 908,
- /*0x0567*/ 445,
- -1,
- /*0x2d14*/ 929,
- /*0x019e*/ 126,
- /*0xa641*/ 949,
- /*0x028c*/ 231,
- /*0x13eb*/ 552,
- /*0x046b*/ 344,
- /*0x04eb*/ 404,
- /*0x006b*/ 9,
- /*0x118c9*/ 1209,
- /*0x104eb*/ 1132,
- /*0x1e6b*/ 618,
- /*0x1eeb*/ 677,
- /*0x016b*/ 107,
- /*0x026b*/ 217,
- /*0x01da*/ 147,
- -1, -1, -1,
- /*0x2c49*/ 826,
- -1, -1,
- /*0x2d22*/ 943,
- /*0x03c1*/ 261,
- -1,
- /*0x048f*/ 358,
- /*0xa647*/ 952,
- /*0x118c3*/ 1203,
- /*0x1f12*/ 698,
- /*0x1e0f*/ 572,
- /*0x1e8f*/ 636,
- /*0x010f*/ 63,
- /*0x020f*/ 172,
- /*0x2c8f*/ 863,
- /*0xa69b*/ 985,
- -1, -1,
- /*0x2c43*/ 820,
- /*0x01dd*/ 149,
- /*0xa68b*/ 977,
- /*0x1e922*/ 1232,
- -1,
- /*0xa645*/ 951,
- -1,
- /*0x118c0*/ 1200,
- -1,
- /*0x03c7*/ 266,
- -1,
- /*0x1e938*/ 1254,
- -1,
- /*0xff4f*/ 1061,
- -1,
- /*0xa76b*/ 1021,
- /*0x2d1e*/ 939,
- /*0x2c40*/ 817,
- /*0x01df*/ 150,
- /*0x2d06*/ 915,
- /*0x0373*/ 236,
- /*0x13ef*/ 556,
- /*0x046f*/ 346,
- /*0x04ef*/ 406,
- /*0x006f*/ 13,
- /*0x03c5*/ 264,
- /*0x104ef*/ 1136,
- /*0x1e6f*/ 620,
- /*0x1eef*/ 679,
- /*0x016f*/ 109,
- /*0x026f*/ 219,
- -1, -1,
- /*0x1f55*/ 726,
- /*0x2d0e*/ 923,
- -1,
- /*0xff4d*/ 1059,
- -1,
- /*0x118c1*/ 1201,
- /*0x13ed*/ 554,
- /*0x046d*/ 345,
- /*0x04ed*/ 405,
- /*0x006d*/ 11,
- -1,
- /*0x104ed*/ 1134,
- /*0x1e6d*/ 619,
- /*0x1eed*/ 678,
- /*0x016d*/ 108,
- /*0x2c41*/ 818,
- /*0xff4b*/ 1057,
- /*0x10ceb*/ 1192,
- -1,
- /*0x13b7*/ 500,
- /*0x0437*/ 298,
- /*0x04b7*/ 378,
- -1,
- /*0x10437*/ 1088,
- /*0x03b2*/ 246,
- /*0x1e37*/ 592,
- /*0x1eb7*/ 651,
- /*0x0137*/ 82,
- /*0x118c7*/ 1207,
- /*0x2cb7*/ 883,
- /*0x2d02*/ 911,
- /*0x0192*/ 122,
- -1,
- /*0x019a*/ 125,
- /*0x01a8*/ 130,
- /*0xa76f*/ 1023,
- /*0x1e926*/ 1236,
- /*0xff55*/ 1067,
- /*0x2c47*/ 824,
- /*0x1fe1*/ 755,
- -1,
- /*0x0481*/ 355,
- /*0x0581*/ 471,
- /*0x1f00*/ 688,
- /*0x118c5*/ 1205,
- /*0x1e01*/ 565,
- /*0x1e81*/ 629,
- /*0x0101*/ 56,
- /*0x0201*/ 165,
- /*0x2c81*/ 856,
- /*0x1f43*/ 721,
- -1, -1,
- /*0xa76d*/ 1022,
- /*0x2c45*/ 822,
- /*0x13b3*/ 496,
- /*0x0433*/ 294,
- /*0x04b3*/ 376,
- -1,
- /*0x10433*/ 1084,
- /*0x057e*/ 468,
- /*0x1e33*/ 590,
- /*0x1eb3*/ 649,
- /*0x0133*/ 80,
- /*0x0233*/ 189,
- /*0x2cb3*/ 881,
- /*0xa737*/ 995,
- /*0x1f40*/ 718,
- -1,
- /*0xff49*/ 1055,
- /*0x10cef*/ 1196,
- -1, -1, -1,
- /*0x1e93c*/ 1258,
- /*0xa66b*/ 970,
- /*0x1fe5*/ 756,
- -1,
- /*0x03f3*/ 288,
- -1,
- /*0x217e*/ 772,
- /*0x1f20*/ 702,
- /*0x2d12*/ 927,
- /*0x13a5*/ 482,
- /*0x2d1a*/ 935,
- /*0x04a5*/ 369,
- /*0x2c32*/ 803,
- /*0xff43*/ 1049,
- /*0x10ced*/ 1194,
- /*0x1e25*/ 583,
- /*0x1ea5*/ 642,
- /*0x0125*/ 74,
- /*0x0225*/ 182,
- /*0x2ca5*/ 874,
- -1,
- /*0x03eb*/ 284,
- -1, -1,
- /*0x1f41*/ 719,
- /*0x0288*/ 227,
- /*0x1e92e*/ 1244,
- -1,
- /*0xa733*/ 993,
- /*0x1e928*/ 1238,
- -1,
- /*0x01c9*/ 139,
- -1,
- /*0x13b5*/ 498,
- /*0x0435*/ 296,
- /*0x04b5*/ 377,
- -1,
- /*0x10435*/ 1086,
- -1,
- /*0x1e35*/ 591,
- /*0x1eb5*/ 650,
- /*0x0135*/ 81,
- /*0x0180*/ 117,
- /*0x2cb5*/ 882,
- /*0x13ad*/ 490,
- -1,
- /*0x04ad*/ 373,
- -1,
- /*0x1042d*/ 1078,
- -1,
- /*0x1e2d*/ 587,
- /*0x1ead*/ 646,
- /*0x012d*/ 78,
- /*0x022d*/ 186,
- /*0x2cad*/ 878,
- -1,
- /*0xa725*/ 987,
- -1, -1,
- /*0x1e92c*/ 1242,
- /*0x018c*/ 121,
- /*0xff41*/ 1047,
- -1,
- /*0x1f45*/ 723,
- -1, -1,
- /*0x2c73*/ 854,
- /*0x1e92a*/ 1240,
- /*0x1fd1*/ 753,
- /*0x13a3*/ 480,
- -1,
- /*0x04a3*/ 368,
- /*0xa66d*/ 971,
- -1,
- /*0x03ef*/ 286,
- /*0x1e23*/ 582,
- /*0x1ea3*/ 641,
- /*0x0123*/ 73,
- /*0x0223*/ 181,
- /*0x2ca3*/ 873,
- /*0xa735*/ 994,
- -1, -1,
- /*0x0585*/ 475,
- /*0xff47*/ 1053,
- -1,
- /*0x1e05*/ 567,
- /*0x1e85*/ 631,
- /*0x0105*/ 58,
- /*0x0205*/ 167,
- /*0x2c85*/ 858,
- /*0xa72d*/ 991,
- /*0x03ed*/ 285,
- /*0x2d00*/ 909,
- /*0x1f04*/ 692,
- /*0x1f32*/ 712,
- -1,
- /*0x13bf*/ 508,
- /*0x043f*/ 306,
- /*0x04bf*/ 382,
- /*0xff45*/ 1051,
- /*0x1043f*/ 1096,
- -1,
- /*0x1e3f*/ 596,
- /*0x1ebf*/ 655,
- /*0x03b7*/ 251,
- /*0x023f*/ 191,
- /*0x2cbf*/ 887,
- -1, -1, -1,
- /*0x2d0c*/ 921,
- -1,
- /*0x13b1*/ 494,
- /*0x0431*/ 292,
- /*0x04b1*/ 375,
- /*0xa723*/ 986,
- /*0x10431*/ 1082,
- /*0x1e943*/ 1265,
- /*0x1e31*/ 589,
- /*0x1eb1*/ 648,
- -1,
- /*0x0231*/ 188,
- /*0x2cb1*/ 880,
- /*0x1f11*/ 697,
- /*0x13c2*/ 511,
- /*0x0442*/ 309,
- /*0x04c2*/ 383,
- -1,
- /*0x10442*/ 1099,
- /*0x13a7*/ 484,
- /*0x2d20*/ 941,
- /*0x04a7*/ 370,
- /*0x0142*/ 87,
- /*0x0242*/ 193,
- /*0x1e940*/ 1262,
- /*0x1e27*/ 584,
- /*0x1ea7*/ 643,
- /*0x0127*/ 75,
- /*0x0227*/ 183,
- /*0x2ca7*/ 875,
- /*0x03b3*/ 247,
- -1,
- /*0xa78c*/ 1031,
- /*0xa73f*/ 999,
- /*0x13f1*/ 558,
- /*0x0471*/ 347,
- /*0x04f1*/ 407,
- /*0x0071*/ 15,
- /*0x1f73*/ 739,
- /*0x104f1*/ 1138,
- /*0x1e71*/ 621,
- /*0x1ef1*/ 680,
- /*0x0171*/ 110,
- /*0x0271*/ 220,
- /*0x13f5*/ 562,
- /*0x0475*/ 349,
- /*0x04f5*/ 409,
- /*0x0075*/ 19,
- -1,
- /*0x104f5*/ 1142,
- /*0x1e75*/ 623,
- /*0x1ef5*/ 682,
- /*0x0175*/ 112,
- /*0x0275*/ 222,
- /*0x00e1*/ 26,
- /*0x1e941*/ 1263,
- /*0x2c37*/ 808,
- /*0x13bd*/ 506,
- /*0x043d*/ 304,
- /*0x04bd*/ 381,
- -1,
- /*0x1043d*/ 1094,
- -1,
- /*0x1e3d*/ 595,
- /*0x1ebd*/ 654,
- -1,
- /*0xa727*/ 988,
- /*0x2cbd*/ 886,
- /*0x13b9*/ 502,
- /*0x0439*/ 300,
- /*0x04b9*/ 379,
- -1,
- /*0x10439*/ 1090,
- /*0x017c*/ 115,
- /*0x1e39*/ 593,
- /*0x1eb9*/ 652,
- -1,
- /*0x13af*/ 492,
- /*0x2cb9*/ 884,
- /*0x04af*/ 374,
- /*0x00e3*/ 28,
- /*0x1042f*/ 1080,
- /*0x03b5*/ 249,
- /*0x1e2f*/ 588,
- /*0x1eaf*/ 647,
- /*0x012f*/ 79,
- /*0x022f*/ 187,
- /*0x2caf*/ 879,
- -1, -1, -1,
- /*0x00e5*/ 30,
- /*0x2c33*/ 804,
- /*0x03ad*/ 242,
- /*0x0583*/ 473,
- -1,
- /*0x10cc2*/ 1151,
- /*0x1e03*/ 566,
- /*0x1e83*/ 630,
- /*0x0103*/ 57,
- /*0x0203*/ 166,
- /*0x2c83*/ 857,
- /*0x00e9*/ 34,
- /*0x0371*/ 235,
- /*0xa73d*/ 998,
- -1,
- /*0x2d1c*/ 937,
- /*0x2d04*/ 913,
- -1, -1, -1,
- /*0x01f3*/ 159,
- /*0xa77c*/ 1025,
- -1,
- /*0x0188*/ 120,
- /*0xa739*/ 996,
- /*0x10cf1*/ 1198,
- -1,
- /*0x0511*/ 423,
- -1, -1, -1, -1,
- /*0x13a9*/ 486,
- /*0xa72f*/ 992,
- /*0x04a9*/ 371,
- /*0x1e932*/ 1248,
- /*0x10429*/ 1074,
- /*0x01eb*/ 156,
- /*0x1e29*/ 585,
- /*0x1ea9*/ 644,
- /*0x0129*/ 76,
- /*0x0229*/ 184,
- /*0x2ca9*/ 876,
- /*0x037c*/ 239,
- -1, -1,
- /*0x2d11*/ 926,
- -1,
- /*0x1f37*/ 717,
- /*0x00e7*/ 32,
- -1, -1,
- /*0x0573*/ 457,
- /*0x2c35*/ 806,
- -1,
- /*0x03bf*/ 259,
- /*0x13ab*/ 488,
- -1,
- /*0x04ab*/ 372,
- -1,
- /*0x1042b*/ 1076,
- -1,
- /*0x1e2b*/ 586,
- /*0x1eab*/ 645,
- /*0x012b*/ 77,
- /*0x022b*/ 185,
- /*0x2cab*/ 877,
- -1,
- /*0x1f01*/ 689,
- /*0x056b*/ 449,
- -1,
- /*0x03b1*/ 245,
- /*0x2173*/ 761,
- -1,
- /*0x2d08*/ 917,
- -1, -1, -1,
- /*0xa791*/ 1032,
- /*0xa729*/ 989,
- -1,
- /*0x13c6*/ 515,
- /*0x0446*/ 313,
- /*0x04c6*/ 385,
- /*0x1f33*/ 713,
- /*0x10446*/ 1103,
- /*0x13a1*/ 478,
- /*0x050f*/ 422,
- /*0x04a1*/ 367,
- /*0x0146*/ 89,
- /*0x01ef*/ 158,
- -1,
- /*0x1e21*/ 581,
- /*0x1ea1*/ 640,
- /*0x0121*/ 72,
- -1,
- /*0x2ca1*/ 872,
- /*0x13c4*/ 513,
- /*0x0444*/ 311,
- /*0x04c4*/ 384,
- -1,
- /*0x10444*/ 1101,
- -1,
- /*0xa72b*/ 990,
- /*0x13f2*/ 559,
- /*0x0144*/ 88,
- /*0x2d0f*/ 924,
- /*0x0072*/ 16,
- /*0x01ed*/ 157,
- /*0x104f2*/ 1139,
- -1, -1,
- /*0x1f25*/ 707,
- /*0x0272*/ 221,
- /*0x13bb*/ 504,
- /*0x043b*/ 302,
- /*0x04bb*/ 380,
- /*0x2c3f*/ 816,
- /*0x1043b*/ 1092,
- -1,
- /*0x1e3b*/ 594,
- /*0x1ebb*/ 653,
- /*0x056f*/ 453,
- /*0x0495*/ 361,
- /*0x2cbb*/ 885,
- -1,
- /*0x03bd*/ 257,
- /*0x1e15*/ 575,
- /*0x1e95*/ 639,
- /*0x0115*/ 66,
- /*0x0215*/ 175,
- /*0x2c95*/ 866,
- -1,
- /*0x2c31*/ 802,
- -1,
- /*0x118c2*/ 1202,
- /*0x1f35*/ 715,
- /*0x03b9*/ 253,
- /*0xa691*/ 980,
- -1,
- /*0x056d*/ 451,
- -1, -1,
- /*0x0493*/ 360,
- -1,
- /*0x2c42*/ 819,
- /*0x03af*/ 244,
- /*0x1e13*/ 574,
- /*0x1e93*/ 638,
- /*0x0113*/ 65,
- /*0x0213*/ 174,
- /*0x2c93*/ 865,
- -1,
- /*0x047b*/ 352,
- /*0x04fb*/ 412,
- -1, -1,
- /*0x104fb*/ 1148,
- /*0x1e7b*/ 626,
- /*0x1efb*/ 685,
- -1,
- /*0xa73b*/ 997,
- -1,
- /*0x10cc6*/ 1155,
- /*0x0479*/ 351,
- /*0x04f9*/ 411,
- /*0x0079*/ 23,
- -1,
- /*0x104f9*/ 1146,
- /*0x1e79*/ 625,
- /*0x1ef9*/ 684,
- -1,
- /*0x1f23*/ 705,
- /*0x0501*/ 415,
- -1, -1,
- /*0x047d*/ 353,
- /*0x04fd*/ 413,
- /*0x0283*/ 225,
- /*0x10cc4*/ 1153,
- /*0x00fe*/ 54,
- /*0x1e7d*/ 627,
- /*0x1efd*/ 686,
- /*0x1f05*/ 693,
- /*0x027d*/ 223,
- /*0x01a5*/ 129,
- /*0x10cf2*/ 1199,
- /*0x0499*/ 363,
- /*0x2c3d*/ 814,
- -1,
- /*0x1e937*/ 1253,
- /*0x1e19*/ 577,
- /*0x2d01*/ 910,
- /*0x0119*/ 68,
- /*0x0219*/ 177,
- /*0x2c99*/ 868,
- -1, -1,
- /*0xa68f*/ 979,
- /*0x2c39*/ 810,
- -1,
- /*0x0477*/ 350,
- /*0x04f7*/ 410,
- /*0x0077*/ 21,
- /*0xa7b7*/ 1045,
- /*0x104f7*/ 1144,
- /*0x1e77*/ 624,
- /*0x1ef7*/ 683,
- /*0x0177*/ 113,
- /*0x1e07*/ 568,
- /*0x1e87*/ 632,
- /*0x0107*/ 59,
- /*0x0207*/ 168,
- /*0x2c87*/ 859,
- -1,
- /*0x1d79*/ 563,
- /*0x1f31*/ 711,
- /*0x0525*/ 433,
- -1, -1,
- /*0x01ad*/ 131,
- /*0x037b*/ 238,
- /*0x13d0*/ 525,
- /*0x0450*/ 323,
- /*0xa781*/ 1027,
- -1,
- /*0x1e933*/ 1249,
- /*0x1d7d*/ 564,
- /*0x1f42*/ 720,
- /*0x047f*/ 354,
- /*0x04ff*/ 414,
- /*0x0250*/ 199,
- -1,
- /*0x1f27*/ 709,
- /*0x1e7f*/ 628,
- /*0x1eff*/ 687,
- /*0x2d25*/ 946,
- /*0x24d0*/ 775,
- /*0x13ce*/ 523,
- /*0x044e*/ 321,
- /*0x04ce*/ 389,
- /*0x03c6*/ 265,
- /*0x1044e*/ 1111,
- /*0x0497*/ 362,
- /*0x037d*/ 240,
- /*0x01a3*/ 128,
- -1,
- /*0x1e17*/ 576,
- /*0x1f71*/ 737,
- /*0x0117*/ 67,
- /*0x0217*/ 176,
- /*0x2c97*/ 867,
- /*0x052d*/ 437,
- -1,
- /*0x1e925*/ 1235,
- -1,
- /*0x0185*/ 119,
- /*0x03c4*/ 263,
- /*0x1f75*/ 741,
- /*0x13cc*/ 521,
- /*0x044c*/ 319,
- /*0x04cc*/ 388,
- -1,
- /*0x1044c*/ 1109,
- /*0x03f2*/ 287,
- /*0xff42*/ 1048,
- -1,
- /*0x13e6*/ 547,
- /*0xa7a5*/ 1041,
- /*0x0377*/ 237,
- /*0x0066*/ 5,
- /*0x2d2d*/ 948,
- /*0x104e6*/ 1127,
- /*0x01bf*/ 137,
- /*0x03bb*/ 255,
- /*0xa77f*/ 1026,
- /*0x0266*/ 213,
- /*0x0523*/ 432,
- /*0x1e935*/ 1251,
- /*0x1f7c*/ 748,
- -1, -1,
- /*0x24e6*/ 797,
- -1,
- /*0xa681*/ 972,
- /*0x007a*/ 24,
- -1,
- /*0x104fa*/ 1147,
- /*0x0505*/ 417,
- /*0x1e92d*/ 1243,
- /*0x017a*/ 114,
- -1,
- /*0xa7b5*/ 1044,
- /*0x118c6*/ 1206,
- -1, -1,
- /*0x2d23*/ 944,
- -1,
- /*0x13c8*/ 517,
- /*0x0448*/ 315,
- /*0x04c8*/ 386,
- -1,
- /*0x10448*/ 1105,
- /*0x2c46*/ 823,
- /*0x10cd0*/ 1165,
- /*0x1f03*/ 691,
- /*0x0148*/ 90,
- /*0x2d05*/ 914,
- /*0x2184*/ 774,
- /*0x118c4*/ 1204,
- /*0x13d8*/ 533,
- /*0x0458*/ 331,
- /*0x03fb*/ 290,
- /*0x13ec*/ 553,
- /*0x1e923*/ 1233,
- /*0x104d8*/ 1113,
- /*0x006c*/ 10,
- -1,
- /*0x104ec*/ 1133,
- /*0x2c44*/ 821,
- /*0x10cce*/ 1163,
- -1,
- /*0x026c*/ 218,
- /*0x2cec*/ 906,
- -1,
- /*0x24d8*/ 783,
- /*0x049d*/ 365,
- -1,
- /*0xa7a3*/ 1040,
- /*0xa77a*/ 1024,
- /*0x1e1d*/ 579,
- /*0x01f5*/ 160,
- /*0x011d*/ 70,
- /*0x021d*/ 179,
- /*0x2c9d*/ 870,
- -1,
- /*0x2c3b*/ 812,
- -1,
- /*0x0527*/ 434,
- /*0xa785*/ 1029,
- -1,
- /*0x10ccc*/ 1161,
- /*0x1e93f*/ 1261,
- -1,
- /*0x01bd*/ 136,
- /*0x13e8*/ 549,
- -1, -1,
- /*0x0068*/ 7,
- /*0x10ce6*/ 1187,
- /*0x104e8*/ 1129,
- -1, -1,
- /*0x0571*/ 455,
- /*0x0268*/ 214,
- /*0x01b9*/ 135,
- /*0x13f0*/ 557,
- /*0x2d27*/ 947,
- /*0x1e931*/ 1247,
- /*0x0070*/ 14,
- /*0x24e8*/ 799,
- /*0x104f0*/ 1137,
- -1,
- /*0x0575*/ 459,
- -1, -1, -1,
- /*0x13dc*/ 537,
- /*0x045c*/ 335,
- -1,
- /*0x1e942*/ 1264,
- -1,
- /*0x104dc*/ 1117,
- /*0x2171*/ 759,
- -1,
- /*0x1e927*/ 1237,
- /*0x025c*/ 208,
- /*0x0076*/ 20,
- /*0x0183*/ 118,
- /*0x104f6*/ 1143,
- /*0x10cc8*/ 1157,
- /*0x0287*/ 226,
- /*0x24dc*/ 787,
- /*0x2175*/ 763,
- /*0x057c*/ 466,
- /*0x13d6*/ 531,
- /*0x0456*/ 329,
- -1,
- /*0x13e0*/ 541,
- /*0xa7a7*/ 1042,
- -1,
- /*0x1f21*/ 703,
- /*0x10cd8*/ 1173,
- /*0x104e0*/ 1121,
- /*0x0256*/ 204,
- /*0x10cec*/ 1193,
- /*0x052f*/ 438,
- /*0x0260*/ 209,
- /*0x03ce*/ 273,
- /*0xa685*/ 974,
- /*0x24d6*/ 781,
- -1,
- /*0x1f44*/ 722,
- /*0x24e0*/ 791,
- /*0x217c*/ 770,
- /*0x13d4*/ 529,
- /*0x0454*/ 327,
- -1, -1,
- /*0x1f72*/ 738,
- /*0x0503*/ 416,
- -1,
- /*0x13ea*/ 551,
- /*0x1e93d*/ 1259,
- /*0x0254*/ 203,
- /*0x006a*/ 8,
- -1,
- /*0x104ea*/ 1131,
- -1,
- /*0x03cc*/ 271,
- /*0x24d4*/ 779,
- /*0x026a*/ 216,
- -1,
- /*0xff46*/ 1052,
- /*0x1e939*/ 1255,
- /*0x13e4*/ 545,
- /*0x1f15*/ 701,
- /*0x10ce8*/ 1189,
- /*0x0064*/ 3,
- /*0x2d03*/ 912,
- /*0x104e4*/ 1125,
- /*0x13b6*/ 499,
- /*0x0436*/ 297,
- /*0x1e92f*/ 1245,
- /*0x118d0*/ 1216,
- /*0x10436*/ 1087,
- -1, -1,
- /*0x10cf0*/ 1197,
- /*0xff44*/ 1050,
- /*0x24e4*/ 795,
- /*0x0078*/ 22,
- /*0x0529*/ 435,
- /*0x104f8*/ 1145,
- /*0x2c50*/ 833,
- -1,
- /*0x1f13*/ 699,
- -1,
- /*0x00f3*/ 44,
- /*0x10cdc*/ 1177,
- /*0x118ce*/ 1214,
- /*0x13ca*/ 519,
- /*0x044a*/ 317,
- /*0x04ca*/ 387,
- -1,
- /*0x1044a*/ 1107,
- -1,
- /*0x1f7b*/ 747,
- /*0x03c8*/ 267,
- /*0x01c6*/ 138,
- /*0x2c4e*/ 831,
- /*0xa783*/ 1028,
- -1, -1,
- /*0x01a1*/ 127,
- /*0x00eb*/ 36,
- /*0x052b*/ 436,
- /*0x10cd6*/ 1171,
- /*0x1f79*/ 745,
- -1,
- /*0x10ce0*/ 1181,
- /*0x118cc*/ 1212,
- /*0x13f4*/ 561,
- /*0x13d2*/ 527,
- /*0x0452*/ 325,
- /*0x0074*/ 18,
- -1,
- /*0x104f4*/ 1141,
- -1,
- /*0x1e929*/ 1239,
- /*0x1f7d*/ 749,
- /*0x2c4c*/ 829,
- /*0x0252*/ 201,
- -1, -1, -1, -1,
- /*0x10cd4*/ 1169,
- /*0x24d2*/ 777,
- /*0x2c66*/ 850,
- -1,
- /*0x13b0*/ 493,
- /*0x0430*/ 291,
- /*0xa7a9*/ 1043,
- /*0x10cea*/ 1191,
- /*0x10430*/ 1081,
- /*0x0521*/ 431,
- -1, -1,
- /*0x0195*/ 123,
- -1,
- /*0x13e2*/ 543,
- /*0x029d*/ 233,
- /*0x1e92b*/ 1241,
- /*0x0062*/ 1,
- /*0x1f77*/ 743,
- /*0x104e2*/ 1123,
- /*0x10ce4*/ 1185,
- /*0x1f07*/ 695,
- -1,
- /*0x118c8*/ 1208,
- -1, -1, -1,
- /*0x0572*/ 456,
- /*0x2d21*/ 942,
- /*0x24e2*/ 793,
- /*0x1fb1*/ 751,
- /*0x00ef*/ 40,
- /*0x048d*/ 357,
- /*0x2c48*/ 825,
- /*0xa683*/ 973,
- /*0x118d8*/ 1224,
- /*0x1e0d*/ 571,
- /*0x1e8d*/ 635,
- /*0x010d*/ 62,
- /*0x020d*/ 171,
- /*0x2c8d*/ 862,
- -1, -1,
- /*0x01fb*/ 162,
- /*0x0515*/ 425,
- /*0x2c58*/ 841,
- /*0x10cca*/ 1159,
- /*0x2172*/ 760,
- /*0x2c6c*/ 853,
- /*0x00ed*/ 38,
- -1,
- /*0x13b4*/ 497,
- /*0x0434*/ 295,
- -1,
- /*0x01f9*/ 161,
- /*0x10434*/ 1085,
- -1, -1, -1, -1,
- /*0xa7a1*/ 1039,
- -1, -1,
- /*0x2d15*/ 930,
- /*0x0513*/ 424,
- -1,
- /*0x01fd*/ 163,
- -1,
- /*0x10cd2*/ 1167,
- /*0x1e09*/ 569,
- /*0x1e89*/ 633,
- /*0x0109*/ 60,
- /*0x0209*/ 169,
- /*0x2c89*/ 860,
- /*0x1e93b*/ 1257,
- /*0x057b*/ 465,
- /*0x0199*/ 124,
- /*0xff50*/ 1062,
- -1,
- /*0x13ee*/ 555,
- /*0x2c68*/ 851,
- -1,
- /*0x006e*/ 12,
- /*0x2d13*/ 928,
- /*0x104ee*/ 1135,
- /*0x1f66*/ 734,
- /*0x0579*/ 463,
- -1, -1,
- /*0x2cee*/ 907,
- -1, -1,
- /*0x118dc*/ 1228,
- /*0xff4e*/ 1060,
- -1,
- /*0x217b*/ 769,
- /*0x10ce2*/ 1183,
- -1,
- /*0x057d*/ 467,
- -1,
- /*0x1f7a*/ 746,
- -1,
- /*0x2c5c*/ 845,
- -1,
- /*0x03b6*/ 250,
- -1,
- /*0x2179*/ 767,
- -1,
- /*0x0519*/ 427,
- /*0x2c76*/ 855,
- /*0x118d6*/ 1222,
- /*0x03f8*/ 289,
- -1,
- /*0x01d0*/ 142,
- /*0xff4c*/ 1058,
- /*0xa793*/ 1033,
- -1, -1,
- /*0x217d*/ 771,
- /*0x01ff*/ 164,
- /*0x2c56*/ 839,
- -1, -1,
- /*0x0577*/ 461,
- /*0x03ca*/ 269,
- -1,
- /*0x0507*/ 418,
- /*0x2d19*/ 934,
- /*0x049f*/ 366,
- /*0x01ce*/ 141,
- /*0x118d4*/ 1220,
- -1,
- /*0x1e1f*/ 580,
- -1,
- /*0x011f*/ 71,
- /*0x021f*/ 180,
- /*0x2c9f*/ 871,
- -1, -1, -1,
- /*0x2c54*/ 837,
- /*0x13de*/ 539,
- /*0x045e*/ 337,
- /*0x2177*/ 765,
- -1,
- /*0x2d07*/ 916,
- /*0x104de*/ 1119,
- /*0x2c6a*/ 852,
- -1, -1,
- /*0x01cc*/ 140,
- /*0x057f*/ 469,
- -1,
- /*0xff48*/ 1054,
- -1,
- /*0xa695*/ 982,
- /*0x24de*/ 789,
- -1, -1,
- /*0xa799*/ 1035,
- -1,
- /*0x10cee*/ 1195,
- -1, -1,
- /*0x0517*/ 426,
- /*0xff58*/ 1070,
- /*0x2c36*/ 807,
- -1, -1, -1, -1,
- /*0x217f*/ 773,
- -1, -1,
- /*0x1f70*/ 736,
- /*0xa693*/ 981,
- /*0x118ca*/ 1210,
- /*0xa787*/ 1030,
- -1, -1, -1,
- /*0x214e*/ 757,
- -1,
- /*0x2d17*/ 932,
- -1, -1,
- /*0x2c4a*/ 827,
- /*0x13be*/ 507,
- /*0x043e*/ 305,
- -1,
- /*0x0566*/ 444,
- /*0x1043e*/ 1095,
- /*0x1f76*/ 742,
- -1, -1,
- /*0x013e*/ 85,
- -1, -1,
- /*0x118d2*/ 1218,
- /*0x13ba*/ 503,
- /*0x043a*/ 301,
- /*0x01d8*/ 146,
- -1,
- /*0x1043a*/ 1091,
- -1,
- /*0x057a*/ 464,
- /*0x1f60*/ 728,
- /*0x013a*/ 83,
- /*0x2c52*/ 835,
- -1, -1,
- /*0x03b4*/ 248,
- -1, -1, -1,
- /*0xa797*/ 1034,
- -1, -1, -1,
- /*0xa699*/ 984,
- -1, -1, -1,
- /*0x10cde*/ 1179,
- -1,
- /*0x217a*/ 768,
- /*0x2c30*/ 801,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1,
- /*0x056c*/ 450,
- /*0xa687*/ 975,
- -1, -1,
- /*0xff56*/ 1068,
- /*0x0289*/ 228,
- -1,
- /*0x1f64*/ 732,
- -1, -1, -1,
- /*0x051d*/ 429,
- -1,
- /*0x1f36*/ 716,
- -1, -1, -1, -1, -1, -1,
- /*0x1f78*/ 744,
- -1,
- /*0x01dc*/ 148,
- -1,
- /*0xff54*/ 1066,
- -1,
- /*0x00f1*/ 42,
- -1, -1, -1,
- /*0x2d1d*/ 938,
- -1,
- /*0x0568*/ 446,
- -1, -1, -1,
- /*0x00f5*/ 46,
- /*0x2c34*/ 805,
- -1,
- /*0xa697*/ 983,
- /*0x01d6*/ 145,
- -1, -1,
- /*0x0570*/ 454,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1,
- /*0x1f74*/ 740,
- -1, -1,
- /*0x00fc*/ 52,
- -1, -1,
- /*0x01d4*/ 144,
- /*0x0576*/ 460,
- /*0xa79d*/ 1037,
- /*0x2170*/ 758,
- -1, -1, -1, -1, -1, -1,
- /*0xff4a*/ 1056,
- -1, -1,
- /*0x1f30*/ 710,
- -1, -1, -1, -1, -1, -1,
- -1,
- /*0x2176*/ 764,
- -1,
- /*0x1f62*/ 730,
- -1, -1,
- /*0x01b6*/ 134,
- -1, -1, -1, -1, -1,
- /*0xff52*/ 1064,
- -1, -1, -1, -1, -1, -1,
- /*0x056a*/ 448,
- -1, -1, -1, -1, -1,
- /*0x1fd0*/ 752,
- -1, -1, -1, -1, -1,
- /*0x03be*/ 258,
- /*0x0564*/ 442,
- -1, -1, -1, -1,
- /*0x118de*/ 1230,
- -1, -1,
- /*0x1f34*/ 714,
- -1, -1,
- /*0x03ba*/ 254,
- -1,
- /*0x0578*/ 462,
- -1,
- /*0x2c5e*/ 847,
- /*0x01d2*/ 143,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1,
- /*0x2178*/ 766,
- /*0x01b0*/ 132,
- -1, -1, -1, -1, -1, -1,
- -1, -1,
- /*0x1e936*/ 1252,
- -1, -1, -1,
- /*0x0574*/ 458,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1,
- /*0x2c3e*/ 815,
- -1, -1, -1, -1, -1,
- /*0x2174*/ 762,
- -1, -1, -1, -1, -1,
- /*0x2c3a*/ 811,
- -1,
- /*0x00f2*/ 43,
- /*0x0562*/ 440,
- -1, -1, -1, -1,
- /*0x01b4*/ 133,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1,
- /*0x050d*/ 421,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1,
- /*0x1e930*/ 1246,
- -1, -1, -1, -1, -1, -1,
- -1,
- /*0x2d0d*/ 922,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1,
- /*0x00fb*/ 51,
- -1,
- /*0x0509*/ 419,
- -1, -1, -1, -1, -1, -1,
- -1, -1,
- /*0x00f9*/ 49,
- -1, -1, -1, -1, -1, -1,
- /*0x056e*/ 452,
- -1, -1,
- /*0x2d09*/ 918,
- -1,
- /*0x00fd*/ 53,
- -1, -1, -1,
- /*0x1e934*/ 1250,
- -1, -1, -1, -1, -1, -1,
- -1, -1,
- /*0x1fe0*/ 754,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1,
- /*0x051f*/ 430,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1,
- /*0x00ff*/ 55,
- -1, -1, -1,
- /*0xa68d*/ 978,
- -1, -1, -1,
- /*0x2d1f*/ 940,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1,
- /*0xa689*/ 976,
- -1,
- /*0x00e6*/ 31,
- /*0xa79f*/ 1038,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1,
- /*0x00fa*/ 50,
- -1, -1, -1, -1, -1, -1,
- -1, -1,
- /*0x1fb0*/ 750,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1,
- /*0x00ec*/ 37,
- -1,
- /*0x1e93e*/ 1260,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1,
- /*0x1e93a*/ 1256,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1,
- /*0x00e8*/ 33,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1,
- /*0x00f0*/ 41,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1,
- /*0x00f6*/ 47,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1,
- /*0x00e0*/ 25,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1,
- /*0x00ea*/ 35,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- /*0x00e4*/ 29,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- /*0x00f8*/ 48,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1,
- /*0x00f4*/ 45,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1,
- /*0x00e2*/ 27,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1,
- /*0x00ee*/ 39
- };
-
- if (code <= MAX_CODE_VALUE && code >= MIN_CODE_VALUE)
- {
- register int key = onigenc_unicode_CaseUnfold_11_hash(code);
-
- if (key <= MAX_HASH_VALUE && key >= 0)
- {
- register short s = wordlist[key];
-
- if (s >= 0 && code1_equal(code, CaseUnfold_11_Table[s].from))
- return &CaseUnfold_11_Table[s].to;
- }
- }
- return 0;
-}
-
-static const CaseUnfold_12_Type CaseUnfold_12_Table[] = {
-#define CaseUnfold_12 (*(CaseUnfold_12_Type (*)[58])(CaseUnfold_12_Table+0))
- {{0x0061, 0x02be}, {1, {0x1e9a}}},
- {{0x0066, 0x0066}, {1, {0xfb00}}},
- {{0x0066, 0x0069}, {1, {0xfb01}}},
- {{0x0066, 0x006c}, {1, {0xfb02}}},
- {{0x0068, 0x0331}, {1, {0x1e96}}},
- {{0x006a, 0x030c}, {1, {0x01f0}}},
- {{0x0073, 0x0073}, {2, {0x00df, 0x1e9e}}},
- {{0x0073, 0x0074}, {2, {0xfb05, 0xfb06}}},
- {{0x0074, 0x0308}, {1, {0x1e97}}},
- {{0x0077, 0x030a}, {1, {0x1e98}}},
- {{0x0079, 0x030a}, {1, {0x1e99}}},
- {{0x02bc, 0x006e}, {1, {0x0149}}},
- {{0x03ac, 0x03b9}, {1, {0x1fb4}}},
- {{0x03ae, 0x03b9}, {1, {0x1fc4}}},
- {{0x03b1, 0x0342}, {1, {0x1fb6}}},
- {{0x03b1, 0x03b9}, {2, {0x1fb3, 0x1fbc}}},
- {{0x03b7, 0x0342}, {1, {0x1fc6}}},
- {{0x03b7, 0x03b9}, {2, {0x1fc3, 0x1fcc}}},
- {{0x03b9, 0x0342}, {1, {0x1fd6}}},
- {{0x03c1, 0x0313}, {1, {0x1fe4}}},
- {{0x03c5, 0x0313}, {1, {0x1f50}}},
- {{0x03c5, 0x0342}, {1, {0x1fe6}}},
- {{0x03c9, 0x0342}, {1, {0x1ff6}}},
- {{0x03c9, 0x03b9}, {2, {0x1ff3, 0x1ffc}}},
- {{0x03ce, 0x03b9}, {1, {0x1ff4}}},
- {{0x0565, 0x0582}, {1, {0x0587}}},
- {{0x0574, 0x0565}, {1, {0xfb14}}},
- {{0x0574, 0x056b}, {1, {0xfb15}}},
- {{0x0574, 0x056d}, {1, {0xfb17}}},
- {{0x0574, 0x0576}, {1, {0xfb13}}},
- {{0x057e, 0x0576}, {1, {0xfb16}}},
- {{0x1f00, 0x03b9}, {2, {0x1f80, 0x1f88}}},
- {{0x1f01, 0x03b9}, {2, {0x1f81, 0x1f89}}},
- {{0x1f02, 0x03b9}, {2, {0x1f82, 0x1f8a}}},
- {{0x1f03, 0x03b9}, {2, {0x1f83, 0x1f8b}}},
- {{0x1f04, 0x03b9}, {2, {0x1f84, 0x1f8c}}},
- {{0x1f05, 0x03b9}, {2, {0x1f85, 0x1f8d}}},
- {{0x1f06, 0x03b9}, {2, {0x1f86, 0x1f8e}}},
- {{0x1f07, 0x03b9}, {2, {0x1f87, 0x1f8f}}},
- {{0x1f20, 0x03b9}, {2, {0x1f90, 0x1f98}}},
- {{0x1f21, 0x03b9}, {2, {0x1f91, 0x1f99}}},
- {{0x1f22, 0x03b9}, {2, {0x1f92, 0x1f9a}}},
- {{0x1f23, 0x03b9}, {2, {0x1f93, 0x1f9b}}},
- {{0x1f24, 0x03b9}, {2, {0x1f94, 0x1f9c}}},
- {{0x1f25, 0x03b9}, {2, {0x1f95, 0x1f9d}}},
- {{0x1f26, 0x03b9}, {2, {0x1f96, 0x1f9e}}},
- {{0x1f27, 0x03b9}, {2, {0x1f97, 0x1f9f}}},
- {{0x1f60, 0x03b9}, {2, {0x1fa0, 0x1fa8}}},
- {{0x1f61, 0x03b9}, {2, {0x1fa1, 0x1fa9}}},
- {{0x1f62, 0x03b9}, {2, {0x1fa2, 0x1faa}}},
- {{0x1f63, 0x03b9}, {2, {0x1fa3, 0x1fab}}},
- {{0x1f64, 0x03b9}, {2, {0x1fa4, 0x1fac}}},
- {{0x1f65, 0x03b9}, {2, {0x1fa5, 0x1fad}}},
- {{0x1f66, 0x03b9}, {2, {0x1fa6, 0x1fae}}},
- {{0x1f67, 0x03b9}, {2, {0x1fa7, 0x1faf}}},
- {{0x1f70, 0x03b9}, {1, {0x1fb2}}},
- {{0x1f74, 0x03b9}, {1, {0x1fc2}}},
- {{0x1f7c, 0x03b9}, {1, {0x1ff2}}},
-#define CaseUnfold_12_Locale (*(CaseUnfold_12_Type (*)[1])(CaseUnfold_12_Table+58))
- {{0x0069, 0x0307}, {1, {0x0130}}},
-};
-
-/* C code produced by gperf version 3.0.4 */
-/* Command-line: gperf -7 -k1,2,3,4,5,6 -F,-1 -c -j1 -i1 -t -T -E -C -H onigenc_unicode_CaseUnfold_12_hash -N onigenc_unicode_CaseUnfold_12_lookup -n */
-
-/* maximum key range = 71, duplicates = 0 */
-
-#ifdef __GNUC__
-__inline
-#else
-#ifdef __cplusplus
-inline
-#endif
-#endif
-/*ARGSUSED*/
-static unsigned int
-onigenc_unicode_CaseUnfold_12_hash(const OnigCodePoint *codes)
-{
- static const unsigned char asso_values[] =
- {
- 3, 58, 54, 57, 56, 16, 8, 2, 43, 82,
- 3, 1, 23, 82, 82, 82, 82, 82, 82, 4,
- 82, 82, 82, 82, 82, 82, 82, 82, 82, 82,
- 82, 82, 52, 51, 50, 49, 48, 47, 46, 45,
- 82, 82, 82, 82, 43, 82, 42, 82, 82, 13,
- 82, 82, 82, 82, 82, 11, 82, 1, 82, 82,
- 14, 82, 1, 82, 82, 31, 3, 82, 82, 30,
- 82, 82, 82, 10, 82, 82, 82, 82, 37, 82,
- 82, 82, 82, 82, 82, 82, 82, 82, 82, 82,
- 82, 82, 82, 82, 82, 82, 37, 15, 36, 35,
- 34, 17, 1, 33, 12, 4, 23, 23, 26, 21,
- 13, 82, 27, 82, 82, 2, 5, 82, 11, 16,
- 82, 15, 82, 82, 23, 82, 8, 82
- };
- return asso_values[bits_at(codes, 5)] + asso_values[bits_at(codes, 4)] + asso_values[bits_at(codes, 3)] + asso_values[bits_at(codes, 2)] + asso_values[bits_at(codes, 1)] + asso_values[bits_at(codes, 0)];
-}
-
-#ifdef __GNUC__
-__inline
-#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
-__attribute__ ((__gnu_inline__))
-#endif
-#endif
-static const CodePointList2 *
-onigenc_unicode_CaseUnfold_12_lookup(const OnigCodePoint *codes)
-{
- enum
- {
- MIN_CODE_VALUE = 0x61,
- MAX_CODE_VALUE = 0x1f7c,
- TOTAL_KEYWORDS = 59,
- MIN_WORD_LENGTH = 6,
- MAX_WORD_LENGTH = 6,
- MIN_HASH_VALUE = 11,
- MAX_HASH_VALUE = 81
- };
-
- static const short wordlist[] =
- {
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1,
- /*0x1f66,0x03b9*/ 53,
- /*0x1f07,0x03b9*/ 38,
- /*0x1f00,0x03b9*/ 31,
- /*0x0066,0x0066*/ 1,
- /*0x1f74,0x03b9*/ 56,
- /*0x0073,0x0073*/ 6,
- /*0x0066,0x0069*/ 2,
- /*0x1f06,0x03b9*/ 37,
- /*0x0073,0x0074*/ 7,
- /*0x03b9,0x0342*/ 18,
- /*0x03c9,0x03b9*/ 23,
- /*0x03b7,0x03b9*/ 17,
- /*0x0069,0x0307*/ 58,
- /*0x03b1,0x03b9*/ 15,
- /*0x1f61,0x03b9*/ 48,
- /*0x1f05,0x03b9*/ 36,
- /*0x1f65,0x03b9*/ 52,
- /*0x0574,0x0576*/ 29,
- /*0x03c9,0x0342*/ 22,
- /*0x03b7,0x0342*/ 16,
- /*0x057e,0x0576*/ 30,
- /*0x03b1,0x0342*/ 14,
- /*0x1f7c,0x03b9*/ 57,
- /*0x0574,0x0565*/ 26,
- /*0x0079,0x030a*/ 10,
- /*0x0077,0x030a*/ 9,
- /*0x1f70,0x03b9*/ 55,
- /*0x0574,0x056d*/ 28,
- /*0x0066,0x006c*/ 3,
- /*0x0574,0x056b*/ 27,
- /*0x0061,0x02be*/ 0,
- /*0x0068,0x0331*/ 4,
- /*0x1f67,0x03b9*/ 54,
- /*0x1f64,0x03b9*/ 51,
- /*0x1f63,0x03b9*/ 50,
- /*0x1f62,0x03b9*/ 49,
- /*0x1f60,0x03b9*/ 47,
- /*0x03ce,0x03b9*/ 24,
- /*0x03c5,0x0342*/ 21,
- /*0x03c5,0x0313*/ 20,
- /*0x03c1,0x0313*/ 19,
- /*0x02bc,0x006e*/ 11,
- /*0x03ae,0x03b9*/ 13,
- /*0x03ac,0x03b9*/ 12,
- /*0x1f27,0x03b9*/ 46,
- /*0x1f26,0x03b9*/ 45,
- /*0x1f25,0x03b9*/ 44,
- /*0x1f24,0x03b9*/ 43,
- /*0x1f23,0x03b9*/ 42,
- /*0x1f22,0x03b9*/ 41,
- /*0x1f21,0x03b9*/ 40,
- /*0x1f20,0x03b9*/ 39,
- /*0x006a,0x030c*/ 5,
- /*0x1f02,0x03b9*/ 33,
- /*0x0074,0x0308*/ 8,
- /*0x1f04,0x03b9*/ 35,
- /*0x1f03,0x03b9*/ 34,
- /*0x1f01,0x03b9*/ 32,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- /*0x0565,0x0582*/ 25
- };
-
- if (codes[0] <= MAX_CODE_VALUE && codes[0] >= MIN_CODE_VALUE &&
- codes[1] <= MAX_CODE_VALUE && codes[1] >= MIN_CODE_VALUE)
- {
- register int key = onigenc_unicode_CaseUnfold_12_hash(codes);
-
- if (key <= MAX_HASH_VALUE && key >= 0)
- {
- register short s = wordlist[key];
-
- if (s >= 0 && code2_equal(codes, CaseUnfold_12_Table[s].from))
- return &CaseUnfold_12_Table[s].to;
- }
- }
- return 0;
-}
-
-static const CaseUnfold_13_Type CaseUnfold_13_Table[] = {
-#define CaseUnfold_13 (*(CaseUnfold_13_Type (*)[14])(CaseUnfold_13_Table+0))
- {{0x0066, 0x0066, 0x0069}, {1, {0xfb03}}},
- {{0x0066, 0x0066, 0x006c}, {1, {0xfb04}}},
- {{0x03b1, 0x0342, 0x03b9}, {1, {0x1fb7}}},
- {{0x03b7, 0x0342, 0x03b9}, {1, {0x1fc7}}},
- {{0x03b9, 0x0308, 0x0300}, {1, {0x1fd2}}},
- {{0x03b9, 0x0308, 0x0301}, {2, {0x0390, 0x1fd3}}},
- {{0x03b9, 0x0308, 0x0342}, {1, {0x1fd7}}},
- {{0x03c5, 0x0308, 0x0300}, {1, {0x1fe2}}},
- {{0x03c5, 0x0308, 0x0301}, {2, {0x03b0, 0x1fe3}}},
- {{0x03c5, 0x0308, 0x0342}, {1, {0x1fe7}}},
- {{0x03c5, 0x0313, 0x0300}, {1, {0x1f52}}},
- {{0x03c5, 0x0313, 0x0301}, {1, {0x1f54}}},
- {{0x03c5, 0x0313, 0x0342}, {1, {0x1f56}}},
- {{0x03c9, 0x0342, 0x03b9}, {1, {0x1ff7}}},
-};
-
-/* C code produced by gperf version 3.0.4 */
-/* Command-line: gperf -7 -k1,2,3,4,5,6,7,8,9 -F,-1 -c -j1 -i1 -t -T -E -C -H onigenc_unicode_CaseUnfold_13_hash -N onigenc_unicode_CaseUnfold_13_lookup -n */
-
-/* maximum key range = 20, duplicates = 0 */
-
-#ifdef __GNUC__
-__inline
-#else
-#ifdef __cplusplus
-inline
-#endif
-#endif
-/*ARGSUSED*/
-static unsigned int
-onigenc_unicode_CaseUnfold_13_hash(const OnigCodePoint *codes)
-{
- static const unsigned char asso_values[] =
- {
- 7, 4, 47, 47, 47, 47, 1, 1, 2, 47,
- 47, 47, 47, 47, 47, 47, 47, 47, 47, 1,
- 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
- 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
- 47, 47, 47, 47, 47, 47, 47, 47, 47, 11,
- 47, 47, 47, 47, 47, 10, 47, 2, 47, 47,
- 47, 47, 47, 47, 47, 47, 1, 47, 47, 1,
- 47, 47, 47, 9, 47, 47, 47, 47, 47, 47,
- 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
- 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
- 47, 47, 1, 47, 47, 2, 47, 47, 1, 47,
- 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
- 47, 47, 47, 47, 47, 47, 47, 47
- };
- return asso_values[bits_at(codes, 8)] + asso_values[bits_at(codes, 7)] + asso_values[bits_at(codes, 6)] + asso_values[bits_at(codes, 5)] + asso_values[bits_at(codes, 4)] + asso_values[bits_at(codes, 3)] + asso_values[bits_at(codes, 2)] + asso_values[bits_at(codes, 1)] + asso_values[bits_at(codes, 0)];
-}
-
-#ifdef __GNUC__
-__inline
-#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
-__attribute__ ((__gnu_inline__))
-#endif
-#endif
-static const CodePointList2 *
-onigenc_unicode_CaseUnfold_13_lookup(const OnigCodePoint *codes)
-{
- enum
- {
- MIN_CODE_VALUE = 0x66,
- MAX_CODE_VALUE = 0x3c9,
- TOTAL_KEYWORDS = 14,
- MIN_WORD_LENGTH = 9,
- MAX_WORD_LENGTH = 9,
- MIN_HASH_VALUE = 27,
- MAX_HASH_VALUE = 46
- };
-
- static const short wordlist[] =
- {
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1,
- -1, -1, -1,
- /*0x03c5,0x0313,0x0342*/ 12,
- /*0x03c5,0x0308,0x0342*/ 9,
- /*0x03b9,0x0308,0x0342*/ 6,
- /*0x03c5,0x0313,0x0301*/ 11,
- /*0x03c5,0x0308,0x0301*/ 8,
- /*0x03b9,0x0308,0x0301*/ 5,
- /*0x03c5,0x0313,0x0300*/ 10,
- /*0x03c5,0x0308,0x0300*/ 7,
- /*0x03b9,0x0308,0x0300*/ 4,
- /*0x03c9,0x0342,0x03b9*/ 13,
- /*0x03b7,0x0342,0x03b9*/ 3,
- /*0x03b1,0x0342,0x03b9*/ 2,
- -1, -1, -1, -1, -1, -1,
- /*0x0066,0x0066,0x006c*/ 1,
- /*0x0066,0x0066,0x0069*/ 0
- };
-
- if (codes[0] <= MAX_CODE_VALUE && codes[0] >= MIN_CODE_VALUE &&
- codes[1] <= MAX_CODE_VALUE && codes[1] >= MIN_CODE_VALUE &&
- codes[2] <= MAX_CODE_VALUE && codes[2] >= MIN_CODE_VALUE)
- {
- register int key = onigenc_unicode_CaseUnfold_13_hash(codes);
-
- if (key <= MAX_HASH_VALUE && key >= 0)
- {
- register short s = wordlist[key];
-
- if (s >= 0 && code3_equal(codes, CaseUnfold_13_Table[s].from))
- return &CaseUnfold_13_Table[s].to;
- }
- }
- return 0;
-}
-
-OnigCodePoint CaseMappingSpecials[] = {
- L(1)|0x039C,
- L(2)|0x0053, 0x0073, L(2)|0x0053, 0x0053,
- L(2)|0x02BC, 0x004E,
- L(1)|0x0053,
- L(1)|0x01C5,
- L(2)|0x0064, 0x017D, L(1)|0x01C4,
- L(1)|0x01C8,
- L(2)|0x006C, 0x004A, L(1)|0x01C7,
- L(1)|0x01CB,
- L(2)|0x006E, 0x004A, L(1)|0x01CA,
- L(2)|0x004A, 0x030C,
- L(1)|0x01F2,
- L(2)|0x0064, 0x005A, L(1)|0x01F1,
- L(1)|0x0399,
- L(3)|0x0399, 0x0308, 0x0301,
- L(3)|0x03A5, 0x0308, 0x0301,
- L(1)|0x03A3,
- L(1)|0x0392,
- L(1)|0x0398,
- L(1)|0x03A6,
- L(1)|0x03A0,
- L(1)|0x039A,
- L(1)|0x03A1,
- L(1)|0x0395,
- L(2)|0x0535, 0x0582, L(2)|0x0535, 0x0552,
- L(1)|0x0412,
- L(1)|0x0414,
- L(1)|0x041E,
- L(1)|0x0421,
- L(1)|0x0422,
- L(1)|0x0422,
- L(1)|0x042A,
- L(1)|0x0462,
- L(1)|0xA64A,
- L(2)|0x0048, 0x0331,
- L(2)|0x0054, 0x0308,
- L(2)|0x0057, 0x030A,
- L(2)|0x0059, 0x030A,
- L(2)|0x0041, 0x02BE,
- L(1)|0x1E60,
- L(1)|0x00DF,
- L(2)|0x03A5, 0x0313,
- L(3)|0x03A5, 0x0313, 0x0300,
- L(3)|0x03A5, 0x0313, 0x0301,
- L(3)|0x03A5, 0x0313, 0x0342,
- L(1)|0x1F88, L(2)|0x1F08, 0x0399,
- L(1)|0x1F89, L(2)|0x1F09, 0x0399,
- L(1)|0x1F8A, L(2)|0x1F0A, 0x0399,
- L(1)|0x1F8B, L(2)|0x1F0B, 0x0399,
- L(1)|0x1F8C, L(2)|0x1F0C, 0x0399,
- L(1)|0x1F8D, L(2)|0x1F0D, 0x0399,
- L(1)|0x1F8E, L(2)|0x1F0E, 0x0399,
- L(1)|0x1F8F, L(2)|0x1F0F, 0x0399,
- L(2)|0x1F00, 0x0399, L(1)|0x1F80, L(2)|0x1F08, 0x0399,
- L(2)|0x1F01, 0x0399, L(1)|0x1F81, L(2)|0x1F09, 0x0399,
- L(2)|0x1F02, 0x0399, L(1)|0x1F82, L(2)|0x1F0A, 0x0399,
- L(2)|0x1F03, 0x0399, L(1)|0x1F83, L(2)|0x1F0B, 0x0399,
- L(2)|0x1F04, 0x0399, L(1)|0x1F84, L(2)|0x1F0C, 0x0399,
- L(2)|0x1F05, 0x0399, L(1)|0x1F85, L(2)|0x1F0D, 0x0399,
- L(2)|0x1F06, 0x0399, L(1)|0x1F86, L(2)|0x1F0E, 0x0399,
- L(2)|0x1F07, 0x0399, L(1)|0x1F87, L(2)|0x1F0F, 0x0399,
- L(1)|0x1F98, L(2)|0x1F28, 0x0399,
- L(1)|0x1F99, L(2)|0x1F29, 0x0399,
- L(1)|0x1F9A, L(2)|0x1F2A, 0x0399,
- L(1)|0x1F9B, L(2)|0x1F2B, 0x0399,
- L(1)|0x1F9C, L(2)|0x1F2C, 0x0399,
- L(1)|0x1F9D, L(2)|0x1F2D, 0x0399,
- L(1)|0x1F9E, L(2)|0x1F2E, 0x0399,
- L(1)|0x1F9F, L(2)|0x1F2F, 0x0399,
- L(2)|0x1F20, 0x0399, L(1)|0x1F90, L(2)|0x1F28, 0x0399,
- L(2)|0x1F21, 0x0399, L(1)|0x1F91, L(2)|0x1F29, 0x0399,
- L(2)|0x1F22, 0x0399, L(1)|0x1F92, L(2)|0x1F2A, 0x0399,
- L(2)|0x1F23, 0x0399, L(1)|0x1F93, L(2)|0x1F2B, 0x0399,
- L(2)|0x1F24, 0x0399, L(1)|0x1F94, L(2)|0x1F2C, 0x0399,
- L(2)|0x1F25, 0x0399, L(1)|0x1F95, L(2)|0x1F2D, 0x0399,
- L(2)|0x1F26, 0x0399, L(1)|0x1F96, L(2)|0x1F2E, 0x0399,
- L(2)|0x1F27, 0x0399, L(1)|0x1F97, L(2)|0x1F2F, 0x0399,
- L(1)|0x1FA8, L(2)|0x1F68, 0x0399,
- L(1)|0x1FA9, L(2)|0x1F69, 0x0399,
- L(1)|0x1FAA, L(2)|0x1F6A, 0x0399,
- L(1)|0x1FAB, L(2)|0x1F6B, 0x0399,
- L(1)|0x1FAC, L(2)|0x1F6C, 0x0399,
- L(1)|0x1FAD, L(2)|0x1F6D, 0x0399,
- L(1)|0x1FAE, L(2)|0x1F6E, 0x0399,
- L(1)|0x1FAF, L(2)|0x1F6F, 0x0399,
- L(2)|0x1F60, 0x0399, L(1)|0x1FA0, L(2)|0x1F68, 0x0399,
- L(2)|0x1F61, 0x0399, L(1)|0x1FA1, L(2)|0x1F69, 0x0399,
- L(2)|0x1F62, 0x0399, L(1)|0x1FA2, L(2)|0x1F6A, 0x0399,
- L(2)|0x1F63, 0x0399, L(1)|0x1FA3, L(2)|0x1F6B, 0x0399,
- L(2)|0x1F64, 0x0399, L(1)|0x1FA4, L(2)|0x1F6C, 0x0399,
- L(2)|0x1F65, 0x0399, L(1)|0x1FA5, L(2)|0x1F6D, 0x0399,
- L(2)|0x1F66, 0x0399, L(1)|0x1FA6, L(2)|0x1F6E, 0x0399,
- L(2)|0x1F67, 0x0399, L(1)|0x1FA7, L(2)|0x1F6F, 0x0399,
- L(2)|0x1FBA, 0x0345, L(2)|0x1FBA, 0x0399,
- L(1)|0x1FBC, L(2)|0x0391, 0x0399,
- L(2)|0x0386, 0x0345, L(2)|0x0386, 0x0399,
- L(2)|0x0391, 0x0342,
- L(3)|0x0391, 0x0342, 0x0345, L(3)|0x0391, 0x0342, 0x0399,
- L(2)|0x03B1, 0x0399, L(1)|0x1FB3, L(2)|0x0391, 0x0399,
- L(1)|0x0399,
- L(2)|0x1FCA, 0x0345, L(2)|0x1FCA, 0x0399,
- L(1)|0x1FCC, L(2)|0x0397, 0x0399,
- L(2)|0x0389, 0x0345, L(2)|0x0389, 0x0399,
- L(2)|0x0397, 0x0342,
- L(3)|0x0397, 0x0342, 0x0345, L(3)|0x0397, 0x0342, 0x0399,
- L(2)|0x03B7, 0x0399, L(1)|0x1FC3, L(2)|0x0397, 0x0399,
- L(3)|0x0399, 0x0308, 0x0300,
- L(3)|0x0399, 0x0308, 0x0301,
- L(2)|0x0399, 0x0342,
- L(3)|0x0399, 0x0308, 0x0342,
- L(3)|0x03A5, 0x0308, 0x0300,
- L(3)|0x03A5, 0x0308, 0x0301,
- L(2)|0x03A1, 0x0313,
- L(2)|0x03A5, 0x0342,
- L(3)|0x03A5, 0x0308, 0x0342,
- L(2)|0x1FFA, 0x0345, L(2)|0x1FFA, 0x0399,
- L(1)|0x1FFC, L(2)|0x03A9, 0x0399,
- L(2)|0x038F, 0x0345, L(2)|0x038F, 0x0399,
- L(2)|0x03A9, 0x0342,
- L(3)|0x03A9, 0x0342, 0x0345, L(3)|0x03A9, 0x0342, 0x0399,
- L(2)|0x03C9, 0x0399, L(1)|0x1FF3, L(2)|0x03A9, 0x0399,
- L(2)|0x0046, 0x0066, L(2)|0x0046, 0x0046,
- L(2)|0x0046, 0x0069, L(2)|0x0046, 0x0049,
- L(2)|0x0046, 0x006C, L(2)|0x0046, 0x004C,
- L(3)|0x0046, 0x0066, 0x0069, L(3)|0x0046, 0x0046, 0x0049,
- L(3)|0x0046, 0x0066, 0x006C, L(3)|0x0046, 0x0046, 0x004C,
- L(2)|0x0053, 0x0074, L(2)|0x0053, 0x0054,
- L(2)|0x0053, 0x0074, L(2)|0x0053, 0x0054,
- L(2)|0x0544, 0x0576, L(2)|0x0544, 0x0546,
- L(2)|0x0544, 0x0565, L(2)|0x0544, 0x0535,
- L(2)|0x0544, 0x056B, L(2)|0x0544, 0x053B,
- L(2)|0x054E, 0x0576, L(2)|0x054E, 0x0546,
- L(2)|0x0544, 0x056D, L(2)|0x0544, 0x053D,
-};
diff --git a/enc/unicode/9.0.0/name2ctype.h b/enc/unicode/9.0.0/name2ctype.h
deleted file mode 100644
index 5f4dcfd034..0000000000
--- a/enc/unicode/9.0.0/name2ctype.h
+++ /dev/null
@@ -1,35389 +0,0 @@
-/* C code produced by gperf version 3.0.4 */
-/* Command-line: gperf -7 -c -j1 -i1 -t -C -P -T -H uniname2ctype_hash -Q uniname2ctype_pool -N uniname2ctype_p */
-#ifndef USE_UNICODE_PROPERTIES
-/* Computed positions: -k'1,3' */
-#else /* USE_UNICODE_PROPERTIES */
-/* Computed positions: -k'1-3,5-6,12,16,$' */
-#endif /* USE_UNICODE_PROPERTIES */
-
-#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
- && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
- && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
- && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
- && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
- && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
- && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
- && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
- && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
- && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
- && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
- && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
- && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
- && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
- && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
- && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
- && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
- && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
- && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
- && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
- && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
- && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
- && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
-/* The character set is not based on ISO-646. */
-error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
-#endif
-
-
-#define long size_t
-
-/* 'NEWLINE': [[:NEWLINE:]] */
-static const OnigCodePoint CR_NEWLINE[] = {
- 1,
- 0x000a, 0x000a,
-}; /* CR_NEWLINE */
-
-/* 'Alpha': [[:Alpha:]] */
-static const OnigCodePoint CR_Alpha[] = {
- 644,
- 0x0041, 0x005a,
- 0x0061, 0x007a,
- 0x00aa, 0x00aa,
- 0x00b5, 0x00b5,
- 0x00ba, 0x00ba,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00f6,
- 0x00f8, 0x02c1,
- 0x02c6, 0x02d1,
- 0x02e0, 0x02e4,
- 0x02ec, 0x02ec,
- 0x02ee, 0x02ee,
- 0x0345, 0x0345,
- 0x0370, 0x0374,
- 0x0376, 0x0377,
- 0x037a, 0x037d,
- 0x037f, 0x037f,
- 0x0386, 0x0386,
- 0x0388, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03f5,
- 0x03f7, 0x0481,
- 0x048a, 0x052f,
- 0x0531, 0x0556,
- 0x0559, 0x0559,
- 0x0561, 0x0587,
- 0x05b0, 0x05bd,
- 0x05bf, 0x05bf,
- 0x05c1, 0x05c2,
- 0x05c4, 0x05c5,
- 0x05c7, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f2,
- 0x0610, 0x061a,
- 0x0620, 0x0657,
- 0x0659, 0x065f,
- 0x066e, 0x06d3,
- 0x06d5, 0x06dc,
- 0x06e1, 0x06e8,
- 0x06ed, 0x06ef,
- 0x06fa, 0x06fc,
- 0x06ff, 0x06ff,
- 0x0710, 0x073f,
- 0x074d, 0x07b1,
- 0x07ca, 0x07ea,
- 0x07f4, 0x07f5,
- 0x07fa, 0x07fa,
- 0x0800, 0x0817,
- 0x081a, 0x082c,
- 0x0840, 0x0858,
- 0x08a0, 0x08b4,
- 0x08b6, 0x08bd,
- 0x08d4, 0x08df,
- 0x08e3, 0x08e9,
- 0x08f0, 0x093b,
- 0x093d, 0x094c,
- 0x094e, 0x0950,
- 0x0955, 0x0963,
- 0x0971, 0x0983,
- 0x0985, 0x098c,
- 0x098f, 0x0990,
- 0x0993, 0x09a8,
- 0x09aa, 0x09b0,
- 0x09b2, 0x09b2,
- 0x09b6, 0x09b9,
- 0x09bd, 0x09c4,
- 0x09c7, 0x09c8,
- 0x09cb, 0x09cc,
- 0x09ce, 0x09ce,
- 0x09d7, 0x09d7,
- 0x09dc, 0x09dd,
- 0x09df, 0x09e3,
- 0x09f0, 0x09f1,
- 0x0a01, 0x0a03,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a3e, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4c,
- 0x0a51, 0x0a51,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a70, 0x0a75,
- 0x0a81, 0x0a83,
- 0x0a85, 0x0a8d,
- 0x0a8f, 0x0a91,
- 0x0a93, 0x0aa8,
- 0x0aaa, 0x0ab0,
- 0x0ab2, 0x0ab3,
- 0x0ab5, 0x0ab9,
- 0x0abd, 0x0ac5,
- 0x0ac7, 0x0ac9,
- 0x0acb, 0x0acc,
- 0x0ad0, 0x0ad0,
- 0x0ae0, 0x0ae3,
- 0x0af9, 0x0af9,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3d, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4c,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b63,
- 0x0b71, 0x0b71,
- 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, 0x0bcc,
- 0x0bd0, 0x0bd0,
- 0x0bd7, 0x0bd7,
- 0x0c00, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4c,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c63,
- 0x0c80, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbd, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccc,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0cf1, 0x0cf2,
- 0x0d01, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4c,
- 0x0d4e, 0x0d4e,
- 0x0d54, 0x0d57,
- 0x0d5f, 0x0d63,
- 0x0d7a, 0x0d7f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df3,
- 0x0e01, 0x0e3a,
- 0x0e40, 0x0e46,
- 0x0e4d, 0x0e4d,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ecd, 0x0ecd,
- 0x0edc, 0x0edf,
- 0x0f00, 0x0f00,
- 0x0f40, 0x0f47,
- 0x0f49, 0x0f6c,
- 0x0f71, 0x0f81,
- 0x0f88, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x1000, 0x1036,
- 0x1038, 0x1038,
- 0x103b, 0x103f,
- 0x1050, 0x1062,
- 0x1065, 0x1068,
- 0x106e, 0x1086,
- 0x108e, 0x108e,
- 0x109c, 0x109d,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x10d0, 0x10fa,
- 0x10fc, 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,
- 0x135f, 0x135f,
- 0x1380, 0x138f,
- 0x13a0, 0x13f5,
- 0x13f8, 0x13fd,
- 0x1401, 0x166c,
- 0x166f, 0x167f,
- 0x1681, 0x169a,
- 0x16a0, 0x16ea,
- 0x16ee, 0x16f8,
- 0x1700, 0x170c,
- 0x170e, 0x1713,
- 0x1720, 0x1733,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17b3,
- 0x17b6, 0x17c8,
- 0x17d7, 0x17d7,
- 0x17dc, 0x17dc,
- 0x1820, 0x1877,
- 0x1880, 0x18aa,
- 0x18b0, 0x18f5,
- 0x1900, 0x191e,
- 0x1920, 0x192b,
- 0x1930, 0x1938,
- 0x1950, 0x196d,
- 0x1970, 0x1974,
- 0x1980, 0x19ab,
- 0x19b0, 0x19c9,
- 0x1a00, 0x1a1b,
- 0x1a20, 0x1a5e,
- 0x1a61, 0x1a74,
- 0x1aa7, 0x1aa7,
- 0x1b00, 0x1b33,
- 0x1b35, 0x1b43,
- 0x1b45, 0x1b4b,
- 0x1b80, 0x1ba9,
- 0x1bac, 0x1baf,
- 0x1bba, 0x1be5,
- 0x1be7, 0x1bf1,
- 0x1c00, 0x1c35,
- 0x1c4d, 0x1c4f,
- 0x1c5a, 0x1c7d,
- 0x1c80, 0x1c88,
- 0x1ce9, 0x1cec,
- 0x1cee, 0x1cf3,
- 0x1cf5, 0x1cf6,
- 0x1d00, 0x1dbf,
- 0x1de7, 0x1df4,
- 0x1e00, 0x1f15,
- 0x1f18, 0x1f1d,
- 0x1f20, 0x1f45,
- 0x1f48, 0x1f4d,
- 0x1f50, 0x1f57,
- 0x1f59, 0x1f59,
- 0x1f5b, 0x1f5b,
- 0x1f5d, 0x1f5d,
- 0x1f5f, 0x1f7d,
- 0x1f80, 0x1fb4,
- 0x1fb6, 0x1fbc,
- 0x1fbe, 0x1fbe,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fcc,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fdb,
- 0x1fe0, 0x1fec,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ffc,
- 0x2071, 0x2071,
- 0x207f, 0x207f,
- 0x2090, 0x209c,
- 0x2102, 0x2102,
- 0x2107, 0x2107,
- 0x210a, 0x2113,
- 0x2115, 0x2115,
- 0x2119, 0x211d,
- 0x2124, 0x2124,
- 0x2126, 0x2126,
- 0x2128, 0x2128,
- 0x212a, 0x212d,
- 0x212f, 0x2139,
- 0x213c, 0x213f,
- 0x2145, 0x2149,
- 0x214e, 0x214e,
- 0x2160, 0x2188,
- 0x24b6, 0x24e9,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2ce4,
- 0x2ceb, 0x2cee,
- 0x2cf2, 0x2cf3,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0x2d30, 0x2d67,
- 0x2d6f, 0x2d6f,
- 0x2d80, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0x2de0, 0x2dff,
- 0x2e2f, 0x2e2f,
- 0x3005, 0x3007,
- 0x3021, 0x3029,
- 0x3031, 0x3035,
- 0x3038, 0x303c,
- 0x3041, 0x3096,
- 0x309d, 0x309f,
- 0x30a1, 0x30fa,
- 0x30fc, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x31a0, 0x31ba,
- 0x31f0, 0x31ff,
- 0x3400, 0x4db5,
- 0x4e00, 0x9fd5,
- 0xa000, 0xa48c,
- 0xa4d0, 0xa4fd,
- 0xa500, 0xa60c,
- 0xa610, 0xa61f,
- 0xa62a, 0xa62b,
- 0xa640, 0xa66e,
- 0xa674, 0xa67b,
- 0xa67f, 0xa6ef,
- 0xa717, 0xa71f,
- 0xa722, 0xa788,
- 0xa78b, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7f7, 0xa801,
- 0xa803, 0xa805,
- 0xa807, 0xa80a,
- 0xa80c, 0xa827,
- 0xa840, 0xa873,
- 0xa880, 0xa8c3,
- 0xa8c5, 0xa8c5,
- 0xa8f2, 0xa8f7,
- 0xa8fb, 0xa8fb,
- 0xa8fd, 0xa8fd,
- 0xa90a, 0xa92a,
- 0xa930, 0xa952,
- 0xa960, 0xa97c,
- 0xa980, 0xa9b2,
- 0xa9b4, 0xa9bf,
- 0xa9cf, 0xa9cf,
- 0xa9e0, 0xa9e4,
- 0xa9e6, 0xa9ef,
- 0xa9fa, 0xa9fe,
- 0xaa00, 0xaa36,
- 0xaa40, 0xaa4d,
- 0xaa60, 0xaa76,
- 0xaa7a, 0xaa7a,
- 0xaa7e, 0xaabe,
- 0xaac0, 0xaac0,
- 0xaac2, 0xaac2,
- 0xaadb, 0xaadd,
- 0xaae0, 0xaaef,
- 0xaaf2, 0xaaf5,
- 0xab01, 0xab06,
- 0xab09, 0xab0e,
- 0xab11, 0xab16,
- 0xab20, 0xab26,
- 0xab28, 0xab2e,
- 0xab30, 0xab5a,
- 0xab5c, 0xab65,
- 0xab70, 0xabea,
- 0xac00, 0xd7a3,
- 0xd7b0, 0xd7c6,
- 0xd7cb, 0xd7fb,
- 0xf900, 0xfa6d,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb28,
- 0xfb2a, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3d,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfb,
- 0xfe70, 0xfe74,
- 0xfe76, 0xfefc,
- 0xff21, 0xff3a,
- 0xff41, 0xff5a,
- 0xff66, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
- 0x10000, 0x1000b,
- 0x1000d, 0x10026,
- 0x10028, 0x1003a,
- 0x1003c, 0x1003d,
- 0x1003f, 0x1004d,
- 0x10050, 0x1005d,
- 0x10080, 0x100fa,
- 0x10140, 0x10174,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x10300, 0x1031f,
- 0x10330, 0x1034a,
- 0x10350, 0x1037a,
- 0x10380, 0x1039d,
- 0x103a0, 0x103c3,
- 0x103c8, 0x103cf,
- 0x103d1, 0x103d5,
- 0x10400, 0x1049d,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x10855,
- 0x10860, 0x10876,
- 0x10880, 0x1089e,
- 0x108e0, 0x108f2,
- 0x108f4, 0x108f5,
- 0x10900, 0x10915,
- 0x10920, 0x10939,
- 0x10980, 0x109b7,
- 0x109be, 0x109bf,
- 0x10a00, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a60, 0x10a7c,
- 0x10a80, 0x10a9c,
- 0x10ac0, 0x10ac7,
- 0x10ac9, 0x10ae4,
- 0x10b00, 0x10b35,
- 0x10b40, 0x10b55,
- 0x10b60, 0x10b72,
- 0x10b80, 0x10b91,
- 0x10c00, 0x10c48,
- 0x10c80, 0x10cb2,
- 0x10cc0, 0x10cf2,
- 0x11000, 0x11045,
- 0x11082, 0x110b8,
- 0x110d0, 0x110e8,
- 0x11100, 0x11132,
- 0x11150, 0x11172,
- 0x11176, 0x11176,
- 0x11180, 0x111bf,
- 0x111c1, 0x111c4,
- 0x111da, 0x111da,
- 0x111dc, 0x111dc,
- 0x11200, 0x11211,
- 0x11213, 0x11234,
- 0x11237, 0x11237,
- 0x1123e, 0x1123e,
- 0x11280, 0x11286,
- 0x11288, 0x11288,
- 0x1128a, 0x1128d,
- 0x1128f, 0x1129d,
- 0x1129f, 0x112a8,
- 0x112b0, 0x112e8,
- 0x11300, 0x11303,
- 0x11305, 0x1130c,
- 0x1130f, 0x11310,
- 0x11313, 0x11328,
- 0x1132a, 0x11330,
- 0x11332, 0x11333,
- 0x11335, 0x11339,
- 0x1133d, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134c,
- 0x11350, 0x11350,
- 0x11357, 0x11357,
- 0x1135d, 0x11363,
- 0x11400, 0x11441,
- 0x11443, 0x11445,
- 0x11447, 0x1144a,
- 0x11480, 0x114c1,
- 0x114c4, 0x114c5,
- 0x114c7, 0x114c7,
- 0x11580, 0x115b5,
- 0x115b8, 0x115be,
- 0x115d8, 0x115dd,
- 0x11600, 0x1163e,
- 0x11640, 0x11640,
- 0x11644, 0x11644,
- 0x11680, 0x116b5,
- 0x11700, 0x11719,
- 0x1171d, 0x1172a,
- 0x118a0, 0x118df,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x11c00, 0x11c08,
- 0x11c0a, 0x11c36,
- 0x11c38, 0x11c3e,
- 0x11c40, 0x11c40,
- 0x11c72, 0x11c8f,
- 0x11c92, 0x11ca7,
- 0x11ca9, 0x11cb6,
- 0x12000, 0x12399,
- 0x12400, 0x1246e,
- 0x12480, 0x12543,
- 0x13000, 0x1342e,
- 0x14400, 0x14646,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16ad0, 0x16aed,
- 0x16b00, 0x16b36,
- 0x16b40, 0x16b43,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f93, 0x16f9f,
- 0x16fe0, 0x16fe0,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 0x1bc9e, 0x1bc9e,
- 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, 0x1d6c0,
- 0x1d6c2, 0x1d6da,
- 0x1d6dc, 0x1d6fa,
- 0x1d6fc, 0x1d714,
- 0x1d716, 0x1d734,
- 0x1d736, 0x1d74e,
- 0x1d750, 0x1d76e,
- 0x1d770, 0x1d788,
- 0x1d78a, 0x1d7a8,
- 0x1d7aa, 0x1d7c2,
- 0x1d7c4, 0x1d7cb,
- 0x1e000, 0x1e006,
- 0x1e008, 0x1e018,
- 0x1e01b, 0x1e021,
- 0x1e023, 0x1e024,
- 0x1e026, 0x1e02a,
- 0x1e800, 0x1e8c4,
- 0x1e900, 0x1e943,
- 0x1e947, 0x1e947,
- 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,
- 0x1f130, 0x1f149,
- 0x1f150, 0x1f169,
- 0x1f170, 0x1f189,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
-}; /* CR_Alpha */
-
-/* 'Blank': [[:Blank:]] */
-static const OnigCodePoint CR_Blank[] = {
- 8,
- 0x0009, 0x0009,
- 0x0020, 0x0020,
- 0x00a0, 0x00a0,
- 0x1680, 0x1680,
- 0x2000, 0x200a,
- 0x202f, 0x202f,
- 0x205f, 0x205f,
- 0x3000, 0x3000,
-}; /* CR_Blank */
-
-/* 'Cntrl': [[:Cntrl:]] */
-static const OnigCodePoint CR_Cntrl[] = {
- 2,
- 0x0000, 0x001f,
- 0x007f, 0x009f,
-}; /* CR_Cntrl */
-
-/* 'Digit': [[:Digit:]] */
-static const OnigCodePoint CR_Digit[] = {
- 54,
- 0x0030, 0x0039,
- 0x0660, 0x0669,
- 0x06f0, 0x06f9,
- 0x07c0, 0x07c9,
- 0x0966, 0x096f,
- 0x09e6, 0x09ef,
- 0x0a66, 0x0a6f,
- 0x0ae6, 0x0aef,
- 0x0b66, 0x0b6f,
- 0x0be6, 0x0bef,
- 0x0c66, 0x0c6f,
- 0x0ce6, 0x0cef,
- 0x0d66, 0x0d6f,
- 0x0de6, 0x0def,
- 0x0e50, 0x0e59,
- 0x0ed0, 0x0ed9,
- 0x0f20, 0x0f29,
- 0x1040, 0x1049,
- 0x1090, 0x1099,
- 0x17e0, 0x17e9,
- 0x1810, 0x1819,
- 0x1946, 0x194f,
- 0x19d0, 0x19d9,
- 0x1a80, 0x1a89,
- 0x1a90, 0x1a99,
- 0x1b50, 0x1b59,
- 0x1bb0, 0x1bb9,
- 0x1c40, 0x1c49,
- 0x1c50, 0x1c59,
- 0xa620, 0xa629,
- 0xa8d0, 0xa8d9,
- 0xa900, 0xa909,
- 0xa9d0, 0xa9d9,
- 0xa9f0, 0xa9f9,
- 0xaa50, 0xaa59,
- 0xabf0, 0xabf9,
- 0xff10, 0xff19,
- 0x104a0, 0x104a9,
- 0x11066, 0x1106f,
- 0x110f0, 0x110f9,
- 0x11136, 0x1113f,
- 0x111d0, 0x111d9,
- 0x112f0, 0x112f9,
- 0x11450, 0x11459,
- 0x114d0, 0x114d9,
- 0x11650, 0x11659,
- 0x116c0, 0x116c9,
- 0x11730, 0x11739,
- 0x118e0, 0x118e9,
- 0x11c50, 0x11c59,
- 0x16a60, 0x16a69,
- 0x16b50, 0x16b59,
- 0x1d7ce, 0x1d7ff,
- 0x1e950, 0x1e959,
-}; /* CR_Digit */
-
-/* 'Graph': [[:Graph:]] */
-static const OnigCodePoint CR_Graph[] = {
- 643,
- 0x0021, 0x007e,
- 0x00a1, 0x0377,
- 0x037a, 0x037f,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x052f,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x058d, 0x058f,
- 0x0591, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0600, 0x061c,
- 0x061e, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x07b1,
- 0x07c0, 0x07fa,
- 0x0800, 0x082d,
- 0x0830, 0x083e,
- 0x0840, 0x085b,
- 0x085e, 0x085e,
- 0x08a0, 0x08b4,
- 0x08b6, 0x08bd,
- 0x08d4, 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, 0x09fb,
- 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, 0x0a75,
- 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, 0x0af9,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 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, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c78, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d01, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4f,
- 0x0d54, 0x0d63,
- 0x0d66, 0x0d7f,
- 0x0d82, 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,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 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, 0x167f,
- 0x1681, 0x169c,
- 0x16a0, 0x16f8,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 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, 0x1abe,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b7c,
- 0x1b80, 0x1bf3,
- 0x1bfc, 0x1c37,
- 0x1c3b, 0x1c49,
- 0x1c4d, 0x1c88,
- 0x1cc0, 0x1cc7,
- 0x1cd0, 0x1cf6,
- 0x1cf8, 0x1cf9,
- 0x1d00, 0x1df5,
- 0x1dfb, 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,
- 0x200b, 0x2027,
- 0x202a, 0x202e,
- 0x2030, 0x205e,
- 0x2060, 0x2064,
- 0x2066, 0x2071,
- 0x2074, 0x208e,
- 0x2090, 0x209c,
- 0x20a0, 0x20be,
- 0x20d0, 0x20f0,
- 0x2100, 0x218b,
- 0x2190, 0x23fe,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x2b73,
- 0x2b76, 0x2b95,
- 0x2b98, 0x2bb9,
- 0x2bbd, 0x2bc8,
- 0x2bca, 0x2bd1,
- 0x2bec, 0x2bef,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 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, 0x2e44,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3001, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x3190, 0x31ba,
- 0x31c0, 0x31e3,
- 0x31f0, 0x321e,
- 0x3220, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fd5,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xa4d0, 0xa62b,
- 0xa640, 0xa6f7,
- 0xa700, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7f7, 0xa82b,
- 0xa830, 0xa839,
- 0xa840, 0xa877,
- 0xa880, 0xa8c5,
- 0xa8ce, 0xa8d9,
- 0xa8e0, 0xa8fd,
- 0xa900, 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, 0xab65,
- 0xab70, 0xabed,
- 0xabf0, 0xabf9,
- 0xac00, 0xd7a3,
- 0xd7b0, 0xd7c6,
- 0xd7cb, 0xd7fb,
- 0xe000, 0xfa6d,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbc1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfd,
- 0xfe00, 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, 0xfffd,
- 0x10000, 0x1000b,
- 0x1000d, 0x10026,
- 0x10028, 0x1003a,
- 0x1003c, 0x1003d,
- 0x1003f, 0x1004d,
- 0x10050, 0x1005d,
- 0x10080, 0x100fa,
- 0x10100, 0x10102,
- 0x10107, 0x10133,
- 0x10137, 0x1018e,
- 0x10190, 0x1019b,
- 0x101a0, 0x101a0,
- 0x101d0, 0x101fd,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x102e0, 0x102fb,
- 0x10300, 0x10323,
- 0x10330, 0x1034a,
- 0x10350, 0x1037a,
- 0x10380, 0x1039d,
- 0x1039f, 0x103c3,
- 0x103c8, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x1056f, 0x1056f,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 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, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a47,
- 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, 0x10cff,
- 0x10e60, 0x10e7e,
- 0x11000, 0x1104d,
- 0x11052, 0x1106f,
- 0x1107f, 0x110c1,
- 0x110d0, 0x110e8,
- 0x110f0, 0x110f9,
- 0x11100, 0x11134,
- 0x11136, 0x11143,
- 0x11150, 0x11176,
- 0x11180, 0x111cd,
- 0x111d0, 0x111df,
- 0x111e1, 0x111f4,
- 0x11200, 0x11211,
- 0x11213, 0x1123e,
- 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,
- 0x1133c, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134d,
- 0x11350, 0x11350,
- 0x11357, 0x11357,
- 0x1135d, 0x11363,
- 0x11366, 0x1136c,
- 0x11370, 0x11374,
- 0x11400, 0x11459,
- 0x1145b, 0x1145b,
- 0x1145d, 0x1145d,
- 0x11480, 0x114c7,
- 0x114d0, 0x114d9,
- 0x11580, 0x115b5,
- 0x115b8, 0x115dd,
- 0x11600, 0x11644,
- 0x11650, 0x11659,
- 0x11660, 0x1166c,
- 0x11680, 0x116b7,
- 0x116c0, 0x116c9,
- 0x11700, 0x11719,
- 0x1171d, 0x1172b,
- 0x11730, 0x1173f,
- 0x118a0, 0x118f2,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x11c00, 0x11c08,
- 0x11c0a, 0x11c36,
- 0x11c38, 0x11c45,
- 0x11c50, 0x11c6c,
- 0x11c70, 0x11c8f,
- 0x11c92, 0x11ca7,
- 0x11ca9, 0x11cb6,
- 0x12000, 0x12399,
- 0x12400, 0x1246e,
- 0x12470, 0x12474,
- 0x12480, 0x12543,
- 0x13000, 0x1342e,
- 0x14400, 0x14646,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16a60, 0x16a69,
- 0x16a6e, 0x16a6f,
- 0x16ad0, 0x16aed,
- 0x16af0, 0x16af5,
- 0x16b00, 0x16b45,
- 0x16b50, 0x16b59,
- 0x16b5b, 0x16b61,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f8f, 0x16f9f,
- 0x16fe0, 0x16fe0,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 0x1bc9c, 0x1bca3,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d1e8,
- 0x1d200, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d360, 0x1d371,
- 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,
- 0x1e000, 0x1e006,
- 0x1e008, 0x1e018,
- 0x1e01b, 0x1e021,
- 0x1e023, 0x1e024,
- 0x1e026, 0x1e02a,
- 0x1e800, 0x1e8c4,
- 0x1e8c7, 0x1e8d6,
- 0x1e900, 0x1e94a,
- 0x1e950, 0x1e959,
- 0x1e95e, 0x1e95f,
- 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, 0x1f10c,
- 0x1f110, 0x1f12e,
- 0x1f130, 0x1f16b,
- 0x1f170, 0x1f1ac,
- 0x1f1e6, 0x1f202,
- 0x1f210, 0x1f23b,
- 0x1f240, 0x1f248,
- 0x1f250, 0x1f251,
- 0x1f300, 0x1f6d2,
- 0x1f6e0, 0x1f6ec,
- 0x1f6f0, 0x1f6f6,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d4,
- 0x1f800, 0x1f80b,
- 0x1f810, 0x1f847,
- 0x1f850, 0x1f859,
- 0x1f860, 0x1f887,
- 0x1f890, 0x1f8ad,
- 0x1f910, 0x1f91e,
- 0x1f920, 0x1f927,
- 0x1f930, 0x1f930,
- 0x1f933, 0x1f93e,
- 0x1f940, 0x1f94b,
- 0x1f950, 0x1f95e,
- 0x1f980, 0x1f991,
- 0x1f9c0, 0x1f9c0,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
- 0xe0001, 0xe0001,
- 0xe0020, 0xe007f,
- 0xe0100, 0xe01ef,
- 0xf0000, 0xffffd,
- 0x100000, 0x10fffd,
-}; /* CR_Graph */
-
-/* 'Lower': [[:Lower:]] */
-static const OnigCodePoint CR_Lower[] = {
- 640,
- 0x0061, 0x007a,
- 0x00aa, 0x00aa,
- 0x00b5, 0x00b5,
- 0x00ba, 0x00ba,
- 0x00df, 0x00f6,
- 0x00f8, 0x00ff,
- 0x0101, 0x0101,
- 0x0103, 0x0103,
- 0x0105, 0x0105,
- 0x0107, 0x0107,
- 0x0109, 0x0109,
- 0x010b, 0x010b,
- 0x010d, 0x010d,
- 0x010f, 0x010f,
- 0x0111, 0x0111,
- 0x0113, 0x0113,
- 0x0115, 0x0115,
- 0x0117, 0x0117,
- 0x0119, 0x0119,
- 0x011b, 0x011b,
- 0x011d, 0x011d,
- 0x011f, 0x011f,
- 0x0121, 0x0121,
- 0x0123, 0x0123,
- 0x0125, 0x0125,
- 0x0127, 0x0127,
- 0x0129, 0x0129,
- 0x012b, 0x012b,
- 0x012d, 0x012d,
- 0x012f, 0x012f,
- 0x0131, 0x0131,
- 0x0133, 0x0133,
- 0x0135, 0x0135,
- 0x0137, 0x0138,
- 0x013a, 0x013a,
- 0x013c, 0x013c,
- 0x013e, 0x013e,
- 0x0140, 0x0140,
- 0x0142, 0x0142,
- 0x0144, 0x0144,
- 0x0146, 0x0146,
- 0x0148, 0x0149,
- 0x014b, 0x014b,
- 0x014d, 0x014d,
- 0x014f, 0x014f,
- 0x0151, 0x0151,
- 0x0153, 0x0153,
- 0x0155, 0x0155,
- 0x0157, 0x0157,
- 0x0159, 0x0159,
- 0x015b, 0x015b,
- 0x015d, 0x015d,
- 0x015f, 0x015f,
- 0x0161, 0x0161,
- 0x0163, 0x0163,
- 0x0165, 0x0165,
- 0x0167, 0x0167,
- 0x0169, 0x0169,
- 0x016b, 0x016b,
- 0x016d, 0x016d,
- 0x016f, 0x016f,
- 0x0171, 0x0171,
- 0x0173, 0x0173,
- 0x0175, 0x0175,
- 0x0177, 0x0177,
- 0x017a, 0x017a,
- 0x017c, 0x017c,
- 0x017e, 0x0180,
- 0x0183, 0x0183,
- 0x0185, 0x0185,
- 0x0188, 0x0188,
- 0x018c, 0x018d,
- 0x0192, 0x0192,
- 0x0195, 0x0195,
- 0x0199, 0x019b,
- 0x019e, 0x019e,
- 0x01a1, 0x01a1,
- 0x01a3, 0x01a3,
- 0x01a5, 0x01a5,
- 0x01a8, 0x01a8,
- 0x01aa, 0x01ab,
- 0x01ad, 0x01ad,
- 0x01b0, 0x01b0,
- 0x01b4, 0x01b4,
- 0x01b6, 0x01b6,
- 0x01b9, 0x01ba,
- 0x01bd, 0x01bf,
- 0x01c6, 0x01c6,
- 0x01c9, 0x01c9,
- 0x01cc, 0x01cc,
- 0x01ce, 0x01ce,
- 0x01d0, 0x01d0,
- 0x01d2, 0x01d2,
- 0x01d4, 0x01d4,
- 0x01d6, 0x01d6,
- 0x01d8, 0x01d8,
- 0x01da, 0x01da,
- 0x01dc, 0x01dd,
- 0x01df, 0x01df,
- 0x01e1, 0x01e1,
- 0x01e3, 0x01e3,
- 0x01e5, 0x01e5,
- 0x01e7, 0x01e7,
- 0x01e9, 0x01e9,
- 0x01eb, 0x01eb,
- 0x01ed, 0x01ed,
- 0x01ef, 0x01f0,
- 0x01f3, 0x01f3,
- 0x01f5, 0x01f5,
- 0x01f9, 0x01f9,
- 0x01fb, 0x01fb,
- 0x01fd, 0x01fd,
- 0x01ff, 0x01ff,
- 0x0201, 0x0201,
- 0x0203, 0x0203,
- 0x0205, 0x0205,
- 0x0207, 0x0207,
- 0x0209, 0x0209,
- 0x020b, 0x020b,
- 0x020d, 0x020d,
- 0x020f, 0x020f,
- 0x0211, 0x0211,
- 0x0213, 0x0213,
- 0x0215, 0x0215,
- 0x0217, 0x0217,
- 0x0219, 0x0219,
- 0x021b, 0x021b,
- 0x021d, 0x021d,
- 0x021f, 0x021f,
- 0x0221, 0x0221,
- 0x0223, 0x0223,
- 0x0225, 0x0225,
- 0x0227, 0x0227,
- 0x0229, 0x0229,
- 0x022b, 0x022b,
- 0x022d, 0x022d,
- 0x022f, 0x022f,
- 0x0231, 0x0231,
- 0x0233, 0x0239,
- 0x023c, 0x023c,
- 0x023f, 0x0240,
- 0x0242, 0x0242,
- 0x0247, 0x0247,
- 0x0249, 0x0249,
- 0x024b, 0x024b,
- 0x024d, 0x024d,
- 0x024f, 0x0293,
- 0x0295, 0x02b8,
- 0x02c0, 0x02c1,
- 0x02e0, 0x02e4,
- 0x0345, 0x0345,
- 0x0371, 0x0371,
- 0x0373, 0x0373,
- 0x0377, 0x0377,
- 0x037a, 0x037d,
- 0x0390, 0x0390,
- 0x03ac, 0x03ce,
- 0x03d0, 0x03d1,
- 0x03d5, 0x03d7,
- 0x03d9, 0x03d9,
- 0x03db, 0x03db,
- 0x03dd, 0x03dd,
- 0x03df, 0x03df,
- 0x03e1, 0x03e1,
- 0x03e3, 0x03e3,
- 0x03e5, 0x03e5,
- 0x03e7, 0x03e7,
- 0x03e9, 0x03e9,
- 0x03eb, 0x03eb,
- 0x03ed, 0x03ed,
- 0x03ef, 0x03f3,
- 0x03f5, 0x03f5,
- 0x03f8, 0x03f8,
- 0x03fb, 0x03fc,
- 0x0430, 0x045f,
- 0x0461, 0x0461,
- 0x0463, 0x0463,
- 0x0465, 0x0465,
- 0x0467, 0x0467,
- 0x0469, 0x0469,
- 0x046b, 0x046b,
- 0x046d, 0x046d,
- 0x046f, 0x046f,
- 0x0471, 0x0471,
- 0x0473, 0x0473,
- 0x0475, 0x0475,
- 0x0477, 0x0477,
- 0x0479, 0x0479,
- 0x047b, 0x047b,
- 0x047d, 0x047d,
- 0x047f, 0x047f,
- 0x0481, 0x0481,
- 0x048b, 0x048b,
- 0x048d, 0x048d,
- 0x048f, 0x048f,
- 0x0491, 0x0491,
- 0x0493, 0x0493,
- 0x0495, 0x0495,
- 0x0497, 0x0497,
- 0x0499, 0x0499,
- 0x049b, 0x049b,
- 0x049d, 0x049d,
- 0x049f, 0x049f,
- 0x04a1, 0x04a1,
- 0x04a3, 0x04a3,
- 0x04a5, 0x04a5,
- 0x04a7, 0x04a7,
- 0x04a9, 0x04a9,
- 0x04ab, 0x04ab,
- 0x04ad, 0x04ad,
- 0x04af, 0x04af,
- 0x04b1, 0x04b1,
- 0x04b3, 0x04b3,
- 0x04b5, 0x04b5,
- 0x04b7, 0x04b7,
- 0x04b9, 0x04b9,
- 0x04bb, 0x04bb,
- 0x04bd, 0x04bd,
- 0x04bf, 0x04bf,
- 0x04c2, 0x04c2,
- 0x04c4, 0x04c4,
- 0x04c6, 0x04c6,
- 0x04c8, 0x04c8,
- 0x04ca, 0x04ca,
- 0x04cc, 0x04cc,
- 0x04ce, 0x04cf,
- 0x04d1, 0x04d1,
- 0x04d3, 0x04d3,
- 0x04d5, 0x04d5,
- 0x04d7, 0x04d7,
- 0x04d9, 0x04d9,
- 0x04db, 0x04db,
- 0x04dd, 0x04dd,
- 0x04df, 0x04df,
- 0x04e1, 0x04e1,
- 0x04e3, 0x04e3,
- 0x04e5, 0x04e5,
- 0x04e7, 0x04e7,
- 0x04e9, 0x04e9,
- 0x04eb, 0x04eb,
- 0x04ed, 0x04ed,
- 0x04ef, 0x04ef,
- 0x04f1, 0x04f1,
- 0x04f3, 0x04f3,
- 0x04f5, 0x04f5,
- 0x04f7, 0x04f7,
- 0x04f9, 0x04f9,
- 0x04fb, 0x04fb,
- 0x04fd, 0x04fd,
- 0x04ff, 0x04ff,
- 0x0501, 0x0501,
- 0x0503, 0x0503,
- 0x0505, 0x0505,
- 0x0507, 0x0507,
- 0x0509, 0x0509,
- 0x050b, 0x050b,
- 0x050d, 0x050d,
- 0x050f, 0x050f,
- 0x0511, 0x0511,
- 0x0513, 0x0513,
- 0x0515, 0x0515,
- 0x0517, 0x0517,
- 0x0519, 0x0519,
- 0x051b, 0x051b,
- 0x051d, 0x051d,
- 0x051f, 0x051f,
- 0x0521, 0x0521,
- 0x0523, 0x0523,
- 0x0525, 0x0525,
- 0x0527, 0x0527,
- 0x0529, 0x0529,
- 0x052b, 0x052b,
- 0x052d, 0x052d,
- 0x052f, 0x052f,
- 0x0561, 0x0587,
- 0x13f8, 0x13fd,
- 0x1c80, 0x1c88,
- 0x1d00, 0x1dbf,
- 0x1e01, 0x1e01,
- 0x1e03, 0x1e03,
- 0x1e05, 0x1e05,
- 0x1e07, 0x1e07,
- 0x1e09, 0x1e09,
- 0x1e0b, 0x1e0b,
- 0x1e0d, 0x1e0d,
- 0x1e0f, 0x1e0f,
- 0x1e11, 0x1e11,
- 0x1e13, 0x1e13,
- 0x1e15, 0x1e15,
- 0x1e17, 0x1e17,
- 0x1e19, 0x1e19,
- 0x1e1b, 0x1e1b,
- 0x1e1d, 0x1e1d,
- 0x1e1f, 0x1e1f,
- 0x1e21, 0x1e21,
- 0x1e23, 0x1e23,
- 0x1e25, 0x1e25,
- 0x1e27, 0x1e27,
- 0x1e29, 0x1e29,
- 0x1e2b, 0x1e2b,
- 0x1e2d, 0x1e2d,
- 0x1e2f, 0x1e2f,
- 0x1e31, 0x1e31,
- 0x1e33, 0x1e33,
- 0x1e35, 0x1e35,
- 0x1e37, 0x1e37,
- 0x1e39, 0x1e39,
- 0x1e3b, 0x1e3b,
- 0x1e3d, 0x1e3d,
- 0x1e3f, 0x1e3f,
- 0x1e41, 0x1e41,
- 0x1e43, 0x1e43,
- 0x1e45, 0x1e45,
- 0x1e47, 0x1e47,
- 0x1e49, 0x1e49,
- 0x1e4b, 0x1e4b,
- 0x1e4d, 0x1e4d,
- 0x1e4f, 0x1e4f,
- 0x1e51, 0x1e51,
- 0x1e53, 0x1e53,
- 0x1e55, 0x1e55,
- 0x1e57, 0x1e57,
- 0x1e59, 0x1e59,
- 0x1e5b, 0x1e5b,
- 0x1e5d, 0x1e5d,
- 0x1e5f, 0x1e5f,
- 0x1e61, 0x1e61,
- 0x1e63, 0x1e63,
- 0x1e65, 0x1e65,
- 0x1e67, 0x1e67,
- 0x1e69, 0x1e69,
- 0x1e6b, 0x1e6b,
- 0x1e6d, 0x1e6d,
- 0x1e6f, 0x1e6f,
- 0x1e71, 0x1e71,
- 0x1e73, 0x1e73,
- 0x1e75, 0x1e75,
- 0x1e77, 0x1e77,
- 0x1e79, 0x1e79,
- 0x1e7b, 0x1e7b,
- 0x1e7d, 0x1e7d,
- 0x1e7f, 0x1e7f,
- 0x1e81, 0x1e81,
- 0x1e83, 0x1e83,
- 0x1e85, 0x1e85,
- 0x1e87, 0x1e87,
- 0x1e89, 0x1e89,
- 0x1e8b, 0x1e8b,
- 0x1e8d, 0x1e8d,
- 0x1e8f, 0x1e8f,
- 0x1e91, 0x1e91,
- 0x1e93, 0x1e93,
- 0x1e95, 0x1e9d,
- 0x1e9f, 0x1e9f,
- 0x1ea1, 0x1ea1,
- 0x1ea3, 0x1ea3,
- 0x1ea5, 0x1ea5,
- 0x1ea7, 0x1ea7,
- 0x1ea9, 0x1ea9,
- 0x1eab, 0x1eab,
- 0x1ead, 0x1ead,
- 0x1eaf, 0x1eaf,
- 0x1eb1, 0x1eb1,
- 0x1eb3, 0x1eb3,
- 0x1eb5, 0x1eb5,
- 0x1eb7, 0x1eb7,
- 0x1eb9, 0x1eb9,
- 0x1ebb, 0x1ebb,
- 0x1ebd, 0x1ebd,
- 0x1ebf, 0x1ebf,
- 0x1ec1, 0x1ec1,
- 0x1ec3, 0x1ec3,
- 0x1ec5, 0x1ec5,
- 0x1ec7, 0x1ec7,
- 0x1ec9, 0x1ec9,
- 0x1ecb, 0x1ecb,
- 0x1ecd, 0x1ecd,
- 0x1ecf, 0x1ecf,
- 0x1ed1, 0x1ed1,
- 0x1ed3, 0x1ed3,
- 0x1ed5, 0x1ed5,
- 0x1ed7, 0x1ed7,
- 0x1ed9, 0x1ed9,
- 0x1edb, 0x1edb,
- 0x1edd, 0x1edd,
- 0x1edf, 0x1edf,
- 0x1ee1, 0x1ee1,
- 0x1ee3, 0x1ee3,
- 0x1ee5, 0x1ee5,
- 0x1ee7, 0x1ee7,
- 0x1ee9, 0x1ee9,
- 0x1eeb, 0x1eeb,
- 0x1eed, 0x1eed,
- 0x1eef, 0x1eef,
- 0x1ef1, 0x1ef1,
- 0x1ef3, 0x1ef3,
- 0x1ef5, 0x1ef5,
- 0x1ef7, 0x1ef7,
- 0x1ef9, 0x1ef9,
- 0x1efb, 0x1efb,
- 0x1efd, 0x1efd,
- 0x1eff, 0x1f07,
- 0x1f10, 0x1f15,
- 0x1f20, 0x1f27,
- 0x1f30, 0x1f37,
- 0x1f40, 0x1f45,
- 0x1f50, 0x1f57,
- 0x1f60, 0x1f67,
- 0x1f70, 0x1f7d,
- 0x1f80, 0x1f87,
- 0x1f90, 0x1f97,
- 0x1fa0, 0x1fa7,
- 0x1fb0, 0x1fb4,
- 0x1fb6, 0x1fb7,
- 0x1fbe, 0x1fbe,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fc7,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fd7,
- 0x1fe0, 0x1fe7,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ff7,
- 0x2071, 0x2071,
- 0x207f, 0x207f,
- 0x2090, 0x209c,
- 0x210a, 0x210a,
- 0x210e, 0x210f,
- 0x2113, 0x2113,
- 0x212f, 0x212f,
- 0x2134, 0x2134,
- 0x2139, 0x2139,
- 0x213c, 0x213d,
- 0x2146, 0x2149,
- 0x214e, 0x214e,
- 0x2170, 0x217f,
- 0x2184, 0x2184,
- 0x24d0, 0x24e9,
- 0x2c30, 0x2c5e,
- 0x2c61, 0x2c61,
- 0x2c65, 0x2c66,
- 0x2c68, 0x2c68,
- 0x2c6a, 0x2c6a,
- 0x2c6c, 0x2c6c,
- 0x2c71, 0x2c71,
- 0x2c73, 0x2c74,
- 0x2c76, 0x2c7d,
- 0x2c81, 0x2c81,
- 0x2c83, 0x2c83,
- 0x2c85, 0x2c85,
- 0x2c87, 0x2c87,
- 0x2c89, 0x2c89,
- 0x2c8b, 0x2c8b,
- 0x2c8d, 0x2c8d,
- 0x2c8f, 0x2c8f,
- 0x2c91, 0x2c91,
- 0x2c93, 0x2c93,
- 0x2c95, 0x2c95,
- 0x2c97, 0x2c97,
- 0x2c99, 0x2c99,
- 0x2c9b, 0x2c9b,
- 0x2c9d, 0x2c9d,
- 0x2c9f, 0x2c9f,
- 0x2ca1, 0x2ca1,
- 0x2ca3, 0x2ca3,
- 0x2ca5, 0x2ca5,
- 0x2ca7, 0x2ca7,
- 0x2ca9, 0x2ca9,
- 0x2cab, 0x2cab,
- 0x2cad, 0x2cad,
- 0x2caf, 0x2caf,
- 0x2cb1, 0x2cb1,
- 0x2cb3, 0x2cb3,
- 0x2cb5, 0x2cb5,
- 0x2cb7, 0x2cb7,
- 0x2cb9, 0x2cb9,
- 0x2cbb, 0x2cbb,
- 0x2cbd, 0x2cbd,
- 0x2cbf, 0x2cbf,
- 0x2cc1, 0x2cc1,
- 0x2cc3, 0x2cc3,
- 0x2cc5, 0x2cc5,
- 0x2cc7, 0x2cc7,
- 0x2cc9, 0x2cc9,
- 0x2ccb, 0x2ccb,
- 0x2ccd, 0x2ccd,
- 0x2ccf, 0x2ccf,
- 0x2cd1, 0x2cd1,
- 0x2cd3, 0x2cd3,
- 0x2cd5, 0x2cd5,
- 0x2cd7, 0x2cd7,
- 0x2cd9, 0x2cd9,
- 0x2cdb, 0x2cdb,
- 0x2cdd, 0x2cdd,
- 0x2cdf, 0x2cdf,
- 0x2ce1, 0x2ce1,
- 0x2ce3, 0x2ce4,
- 0x2cec, 0x2cec,
- 0x2cee, 0x2cee,
- 0x2cf3, 0x2cf3,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0xa641, 0xa641,
- 0xa643, 0xa643,
- 0xa645, 0xa645,
- 0xa647, 0xa647,
- 0xa649, 0xa649,
- 0xa64b, 0xa64b,
- 0xa64d, 0xa64d,
- 0xa64f, 0xa64f,
- 0xa651, 0xa651,
- 0xa653, 0xa653,
- 0xa655, 0xa655,
- 0xa657, 0xa657,
- 0xa659, 0xa659,
- 0xa65b, 0xa65b,
- 0xa65d, 0xa65d,
- 0xa65f, 0xa65f,
- 0xa661, 0xa661,
- 0xa663, 0xa663,
- 0xa665, 0xa665,
- 0xa667, 0xa667,
- 0xa669, 0xa669,
- 0xa66b, 0xa66b,
- 0xa66d, 0xa66d,
- 0xa681, 0xa681,
- 0xa683, 0xa683,
- 0xa685, 0xa685,
- 0xa687, 0xa687,
- 0xa689, 0xa689,
- 0xa68b, 0xa68b,
- 0xa68d, 0xa68d,
- 0xa68f, 0xa68f,
- 0xa691, 0xa691,
- 0xa693, 0xa693,
- 0xa695, 0xa695,
- 0xa697, 0xa697,
- 0xa699, 0xa699,
- 0xa69b, 0xa69d,
- 0xa723, 0xa723,
- 0xa725, 0xa725,
- 0xa727, 0xa727,
- 0xa729, 0xa729,
- 0xa72b, 0xa72b,
- 0xa72d, 0xa72d,
- 0xa72f, 0xa731,
- 0xa733, 0xa733,
- 0xa735, 0xa735,
- 0xa737, 0xa737,
- 0xa739, 0xa739,
- 0xa73b, 0xa73b,
- 0xa73d, 0xa73d,
- 0xa73f, 0xa73f,
- 0xa741, 0xa741,
- 0xa743, 0xa743,
- 0xa745, 0xa745,
- 0xa747, 0xa747,
- 0xa749, 0xa749,
- 0xa74b, 0xa74b,
- 0xa74d, 0xa74d,
- 0xa74f, 0xa74f,
- 0xa751, 0xa751,
- 0xa753, 0xa753,
- 0xa755, 0xa755,
- 0xa757, 0xa757,
- 0xa759, 0xa759,
- 0xa75b, 0xa75b,
- 0xa75d, 0xa75d,
- 0xa75f, 0xa75f,
- 0xa761, 0xa761,
- 0xa763, 0xa763,
- 0xa765, 0xa765,
- 0xa767, 0xa767,
- 0xa769, 0xa769,
- 0xa76b, 0xa76b,
- 0xa76d, 0xa76d,
- 0xa76f, 0xa778,
- 0xa77a, 0xa77a,
- 0xa77c, 0xa77c,
- 0xa77f, 0xa77f,
- 0xa781, 0xa781,
- 0xa783, 0xa783,
- 0xa785, 0xa785,
- 0xa787, 0xa787,
- 0xa78c, 0xa78c,
- 0xa78e, 0xa78e,
- 0xa791, 0xa791,
- 0xa793, 0xa795,
- 0xa797, 0xa797,
- 0xa799, 0xa799,
- 0xa79b, 0xa79b,
- 0xa79d, 0xa79d,
- 0xa79f, 0xa79f,
- 0xa7a1, 0xa7a1,
- 0xa7a3, 0xa7a3,
- 0xa7a5, 0xa7a5,
- 0xa7a7, 0xa7a7,
- 0xa7a9, 0xa7a9,
- 0xa7b5, 0xa7b5,
- 0xa7b7, 0xa7b7,
- 0xa7f8, 0xa7fa,
- 0xab30, 0xab5a,
- 0xab5c, 0xab65,
- 0xab70, 0xabbf,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xff41, 0xff5a,
- 0x10428, 0x1044f,
- 0x104d8, 0x104fb,
- 0x10cc0, 0x10cf2,
- 0x118c0, 0x118df,
- 0x1d41a, 0x1d433,
- 0x1d44e, 0x1d454,
- 0x1d456, 0x1d467,
- 0x1d482, 0x1d49b,
- 0x1d4b6, 0x1d4b9,
- 0x1d4bb, 0x1d4bb,
- 0x1d4bd, 0x1d4c3,
- 0x1d4c5, 0x1d4cf,
- 0x1d4ea, 0x1d503,
- 0x1d51e, 0x1d537,
- 0x1d552, 0x1d56b,
- 0x1d586, 0x1d59f,
- 0x1d5ba, 0x1d5d3,
- 0x1d5ee, 0x1d607,
- 0x1d622, 0x1d63b,
- 0x1d656, 0x1d66f,
- 0x1d68a, 0x1d6a5,
- 0x1d6c2, 0x1d6da,
- 0x1d6dc, 0x1d6e1,
- 0x1d6fc, 0x1d714,
- 0x1d716, 0x1d71b,
- 0x1d736, 0x1d74e,
- 0x1d750, 0x1d755,
- 0x1d770, 0x1d788,
- 0x1d78a, 0x1d78f,
- 0x1d7aa, 0x1d7c2,
- 0x1d7c4, 0x1d7c9,
- 0x1d7cb, 0x1d7cb,
- 0x1e922, 0x1e943,
-}; /* CR_Lower */
-
-/* 'Print': [[:Print:]] */
-static const OnigCodePoint CR_Print[] = {
- 640,
- 0x0020, 0x007e,
- 0x00a0, 0x0377,
- 0x037a, 0x037f,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x052f,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x058d, 0x058f,
- 0x0591, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0600, 0x061c,
- 0x061e, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x07b1,
- 0x07c0, 0x07fa,
- 0x0800, 0x082d,
- 0x0830, 0x083e,
- 0x0840, 0x085b,
- 0x085e, 0x085e,
- 0x08a0, 0x08b4,
- 0x08b6, 0x08bd,
- 0x08d4, 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, 0x09fb,
- 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, 0x0a75,
- 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, 0x0af9,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 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, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c78, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d01, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4f,
- 0x0d54, 0x0d63,
- 0x0d66, 0x0d7f,
- 0x0d82, 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,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 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, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 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, 0x1abe,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b7c,
- 0x1b80, 0x1bf3,
- 0x1bfc, 0x1c37,
- 0x1c3b, 0x1c49,
- 0x1c4d, 0x1c88,
- 0x1cc0, 0x1cc7,
- 0x1cd0, 0x1cf6,
- 0x1cf8, 0x1cf9,
- 0x1d00, 0x1df5,
- 0x1dfb, 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, 0x2027,
- 0x202a, 0x2064,
- 0x2066, 0x2071,
- 0x2074, 0x208e,
- 0x2090, 0x209c,
- 0x20a0, 0x20be,
- 0x20d0, 0x20f0,
- 0x2100, 0x218b,
- 0x2190, 0x23fe,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x2b73,
- 0x2b76, 0x2b95,
- 0x2b98, 0x2bb9,
- 0x2bbd, 0x2bc8,
- 0x2bca, 0x2bd1,
- 0x2bec, 0x2bef,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 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, 0x2e44,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x3190, 0x31ba,
- 0x31c0, 0x31e3,
- 0x31f0, 0x321e,
- 0x3220, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fd5,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xa4d0, 0xa62b,
- 0xa640, 0xa6f7,
- 0xa700, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7f7, 0xa82b,
- 0xa830, 0xa839,
- 0xa840, 0xa877,
- 0xa880, 0xa8c5,
- 0xa8ce, 0xa8d9,
- 0xa8e0, 0xa8fd,
- 0xa900, 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, 0xab65,
- 0xab70, 0xabed,
- 0xabf0, 0xabf9,
- 0xac00, 0xd7a3,
- 0xd7b0, 0xd7c6,
- 0xd7cb, 0xd7fb,
- 0xe000, 0xfa6d,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbc1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfd,
- 0xfe00, 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, 0xfffd,
- 0x10000, 0x1000b,
- 0x1000d, 0x10026,
- 0x10028, 0x1003a,
- 0x1003c, 0x1003d,
- 0x1003f, 0x1004d,
- 0x10050, 0x1005d,
- 0x10080, 0x100fa,
- 0x10100, 0x10102,
- 0x10107, 0x10133,
- 0x10137, 0x1018e,
- 0x10190, 0x1019b,
- 0x101a0, 0x101a0,
- 0x101d0, 0x101fd,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x102e0, 0x102fb,
- 0x10300, 0x10323,
- 0x10330, 0x1034a,
- 0x10350, 0x1037a,
- 0x10380, 0x1039d,
- 0x1039f, 0x103c3,
- 0x103c8, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x1056f, 0x1056f,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 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, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a47,
- 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, 0x10cff,
- 0x10e60, 0x10e7e,
- 0x11000, 0x1104d,
- 0x11052, 0x1106f,
- 0x1107f, 0x110c1,
- 0x110d0, 0x110e8,
- 0x110f0, 0x110f9,
- 0x11100, 0x11134,
- 0x11136, 0x11143,
- 0x11150, 0x11176,
- 0x11180, 0x111cd,
- 0x111d0, 0x111df,
- 0x111e1, 0x111f4,
- 0x11200, 0x11211,
- 0x11213, 0x1123e,
- 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,
- 0x1133c, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134d,
- 0x11350, 0x11350,
- 0x11357, 0x11357,
- 0x1135d, 0x11363,
- 0x11366, 0x1136c,
- 0x11370, 0x11374,
- 0x11400, 0x11459,
- 0x1145b, 0x1145b,
- 0x1145d, 0x1145d,
- 0x11480, 0x114c7,
- 0x114d0, 0x114d9,
- 0x11580, 0x115b5,
- 0x115b8, 0x115dd,
- 0x11600, 0x11644,
- 0x11650, 0x11659,
- 0x11660, 0x1166c,
- 0x11680, 0x116b7,
- 0x116c0, 0x116c9,
- 0x11700, 0x11719,
- 0x1171d, 0x1172b,
- 0x11730, 0x1173f,
- 0x118a0, 0x118f2,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x11c00, 0x11c08,
- 0x11c0a, 0x11c36,
- 0x11c38, 0x11c45,
- 0x11c50, 0x11c6c,
- 0x11c70, 0x11c8f,
- 0x11c92, 0x11ca7,
- 0x11ca9, 0x11cb6,
- 0x12000, 0x12399,
- 0x12400, 0x1246e,
- 0x12470, 0x12474,
- 0x12480, 0x12543,
- 0x13000, 0x1342e,
- 0x14400, 0x14646,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16a60, 0x16a69,
- 0x16a6e, 0x16a6f,
- 0x16ad0, 0x16aed,
- 0x16af0, 0x16af5,
- 0x16b00, 0x16b45,
- 0x16b50, 0x16b59,
- 0x16b5b, 0x16b61,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f8f, 0x16f9f,
- 0x16fe0, 0x16fe0,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 0x1bc9c, 0x1bca3,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d1e8,
- 0x1d200, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d360, 0x1d371,
- 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,
- 0x1e000, 0x1e006,
- 0x1e008, 0x1e018,
- 0x1e01b, 0x1e021,
- 0x1e023, 0x1e024,
- 0x1e026, 0x1e02a,
- 0x1e800, 0x1e8c4,
- 0x1e8c7, 0x1e8d6,
- 0x1e900, 0x1e94a,
- 0x1e950, 0x1e959,
- 0x1e95e, 0x1e95f,
- 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, 0x1f10c,
- 0x1f110, 0x1f12e,
- 0x1f130, 0x1f16b,
- 0x1f170, 0x1f1ac,
- 0x1f1e6, 0x1f202,
- 0x1f210, 0x1f23b,
- 0x1f240, 0x1f248,
- 0x1f250, 0x1f251,
- 0x1f300, 0x1f6d2,
- 0x1f6e0, 0x1f6ec,
- 0x1f6f0, 0x1f6f6,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d4,
- 0x1f800, 0x1f80b,
- 0x1f810, 0x1f847,
- 0x1f850, 0x1f859,
- 0x1f860, 0x1f887,
- 0x1f890, 0x1f8ad,
- 0x1f910, 0x1f91e,
- 0x1f920, 0x1f927,
- 0x1f930, 0x1f930,
- 0x1f933, 0x1f93e,
- 0x1f940, 0x1f94b,
- 0x1f950, 0x1f95e,
- 0x1f980, 0x1f991,
- 0x1f9c0, 0x1f9c0,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
- 0xe0001, 0xe0001,
- 0xe0020, 0xe007f,
- 0xe0100, 0xe01ef,
- 0xf0000, 0xffffd,
- 0x100000, 0x10fffd,
-}; /* CR_Print */
-
-/* 'Punct': [[:Punct:]] */
-static const OnigCodePoint CR_Punct[] = {
- 168,
- 0x0021, 0x0023,
- 0x0025, 0x002a,
- 0x002c, 0x002f,
- 0x003a, 0x003b,
- 0x003f, 0x0040,
- 0x005b, 0x005d,
- 0x005f, 0x005f,
- 0x007b, 0x007b,
- 0x007d, 0x007d,
- 0x00a1, 0x00a1,
- 0x00a7, 0x00a7,
- 0x00ab, 0x00ab,
- 0x00b6, 0x00b7,
- 0x00bb, 0x00bb,
- 0x00bf, 0x00bf,
- 0x037e, 0x037e,
- 0x0387, 0x0387,
- 0x055a, 0x055f,
- 0x0589, 0x058a,
- 0x05be, 0x05be,
- 0x05c0, 0x05c0,
- 0x05c3, 0x05c3,
- 0x05c6, 0x05c6,
- 0x05f3, 0x05f4,
- 0x0609, 0x060a,
- 0x060c, 0x060d,
- 0x061b, 0x061b,
- 0x061e, 0x061f,
- 0x066a, 0x066d,
- 0x06d4, 0x06d4,
- 0x0700, 0x070d,
- 0x07f7, 0x07f9,
- 0x0830, 0x083e,
- 0x085e, 0x085e,
- 0x0964, 0x0965,
- 0x0970, 0x0970,
- 0x0af0, 0x0af0,
- 0x0df4, 0x0df4,
- 0x0e4f, 0x0e4f,
- 0x0e5a, 0x0e5b,
- 0x0f04, 0x0f12,
- 0x0f14, 0x0f14,
- 0x0f3a, 0x0f3d,
- 0x0f85, 0x0f85,
- 0x0fd0, 0x0fd4,
- 0x0fd9, 0x0fda,
- 0x104a, 0x104f,
- 0x10fb, 0x10fb,
- 0x1360, 0x1368,
- 0x1400, 0x1400,
- 0x166d, 0x166e,
- 0x169b, 0x169c,
- 0x16eb, 0x16ed,
- 0x1735, 0x1736,
- 0x17d4, 0x17d6,
- 0x17d8, 0x17da,
- 0x1800, 0x180a,
- 0x1944, 0x1945,
- 0x1a1e, 0x1a1f,
- 0x1aa0, 0x1aa6,
- 0x1aa8, 0x1aad,
- 0x1b5a, 0x1b60,
- 0x1bfc, 0x1bff,
- 0x1c3b, 0x1c3f,
- 0x1c7e, 0x1c7f,
- 0x1cc0, 0x1cc7,
- 0x1cd3, 0x1cd3,
- 0x2010, 0x2027,
- 0x2030, 0x2043,
- 0x2045, 0x2051,
- 0x2053, 0x205e,
- 0x207d, 0x207e,
- 0x208d, 0x208e,
- 0x2308, 0x230b,
- 0x2329, 0x232a,
- 0x2768, 0x2775,
- 0x27c5, 0x27c6,
- 0x27e6, 0x27ef,
- 0x2983, 0x2998,
- 0x29d8, 0x29db,
- 0x29fc, 0x29fd,
- 0x2cf9, 0x2cfc,
- 0x2cfe, 0x2cff,
- 0x2d70, 0x2d70,
- 0x2e00, 0x2e2e,
- 0x2e30, 0x2e44,
- 0x3001, 0x3003,
- 0x3008, 0x3011,
- 0x3014, 0x301f,
- 0x3030, 0x3030,
- 0x303d, 0x303d,
- 0x30a0, 0x30a0,
- 0x30fb, 0x30fb,
- 0xa4fe, 0xa4ff,
- 0xa60d, 0xa60f,
- 0xa673, 0xa673,
- 0xa67e, 0xa67e,
- 0xa6f2, 0xa6f7,
- 0xa874, 0xa877,
- 0xa8ce, 0xa8cf,
- 0xa8f8, 0xa8fa,
- 0xa8fc, 0xa8fc,
- 0xa92e, 0xa92f,
- 0xa95f, 0xa95f,
- 0xa9c1, 0xa9cd,
- 0xa9de, 0xa9df,
- 0xaa5c, 0xaa5f,
- 0xaade, 0xaadf,
- 0xaaf0, 0xaaf1,
- 0xabeb, 0xabeb,
- 0xfd3e, 0xfd3f,
- 0xfe10, 0xfe19,
- 0xfe30, 0xfe52,
- 0xfe54, 0xfe61,
- 0xfe63, 0xfe63,
- 0xfe68, 0xfe68,
- 0xfe6a, 0xfe6b,
- 0xff01, 0xff03,
- 0xff05, 0xff0a,
- 0xff0c, 0xff0f,
- 0xff1a, 0xff1b,
- 0xff1f, 0xff20,
- 0xff3b, 0xff3d,
- 0xff3f, 0xff3f,
- 0xff5b, 0xff5b,
- 0xff5d, 0xff5d,
- 0xff5f, 0xff65,
- 0x10100, 0x10102,
- 0x1039f, 0x1039f,
- 0x103d0, 0x103d0,
- 0x1056f, 0x1056f,
- 0x10857, 0x10857,
- 0x1091f, 0x1091f,
- 0x1093f, 0x1093f,
- 0x10a50, 0x10a58,
- 0x10a7f, 0x10a7f,
- 0x10af0, 0x10af6,
- 0x10b39, 0x10b3f,
- 0x10b99, 0x10b9c,
- 0x11047, 0x1104d,
- 0x110bb, 0x110bc,
- 0x110be, 0x110c1,
- 0x11140, 0x11143,
- 0x11174, 0x11175,
- 0x111c5, 0x111c9,
- 0x111cd, 0x111cd,
- 0x111db, 0x111db,
- 0x111dd, 0x111df,
- 0x11238, 0x1123d,
- 0x112a9, 0x112a9,
- 0x1144b, 0x1144f,
- 0x1145b, 0x1145b,
- 0x1145d, 0x1145d,
- 0x114c6, 0x114c6,
- 0x115c1, 0x115d7,
- 0x11641, 0x11643,
- 0x11660, 0x1166c,
- 0x1173c, 0x1173e,
- 0x11c41, 0x11c45,
- 0x11c70, 0x11c71,
- 0x12470, 0x12474,
- 0x16a6e, 0x16a6f,
- 0x16af5, 0x16af5,
- 0x16b37, 0x16b3b,
- 0x16b44, 0x16b44,
- 0x1bc9f, 0x1bc9f,
- 0x1da87, 0x1da8b,
- 0x1e95e, 0x1e95f,
-}; /* CR_Punct */
-
-/* 'Space': [[:Space:]] */
-static const OnigCodePoint CR_Space[] = {
- 10,
- 0x0009, 0x000d,
- 0x0020, 0x0020,
- 0x0085, 0x0085,
- 0x00a0, 0x00a0,
- 0x1680, 0x1680,
- 0x2000, 0x200a,
- 0x2028, 0x2029,
- 0x202f, 0x202f,
- 0x205f, 0x205f,
- 0x3000, 0x3000,
-}; /* CR_Space */
-
-/* 'Upper': [[:Upper:]] */
-static const OnigCodePoint CR_Upper[] = {
- 632,
- 0x0041, 0x005a,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00de,
- 0x0100, 0x0100,
- 0x0102, 0x0102,
- 0x0104, 0x0104,
- 0x0106, 0x0106,
- 0x0108, 0x0108,
- 0x010a, 0x010a,
- 0x010c, 0x010c,
- 0x010e, 0x010e,
- 0x0110, 0x0110,
- 0x0112, 0x0112,
- 0x0114, 0x0114,
- 0x0116, 0x0116,
- 0x0118, 0x0118,
- 0x011a, 0x011a,
- 0x011c, 0x011c,
- 0x011e, 0x011e,
- 0x0120, 0x0120,
- 0x0122, 0x0122,
- 0x0124, 0x0124,
- 0x0126, 0x0126,
- 0x0128, 0x0128,
- 0x012a, 0x012a,
- 0x012c, 0x012c,
- 0x012e, 0x012e,
- 0x0130, 0x0130,
- 0x0132, 0x0132,
- 0x0134, 0x0134,
- 0x0136, 0x0136,
- 0x0139, 0x0139,
- 0x013b, 0x013b,
- 0x013d, 0x013d,
- 0x013f, 0x013f,
- 0x0141, 0x0141,
- 0x0143, 0x0143,
- 0x0145, 0x0145,
- 0x0147, 0x0147,
- 0x014a, 0x014a,
- 0x014c, 0x014c,
- 0x014e, 0x014e,
- 0x0150, 0x0150,
- 0x0152, 0x0152,
- 0x0154, 0x0154,
- 0x0156, 0x0156,
- 0x0158, 0x0158,
- 0x015a, 0x015a,
- 0x015c, 0x015c,
- 0x015e, 0x015e,
- 0x0160, 0x0160,
- 0x0162, 0x0162,
- 0x0164, 0x0164,
- 0x0166, 0x0166,
- 0x0168, 0x0168,
- 0x016a, 0x016a,
- 0x016c, 0x016c,
- 0x016e, 0x016e,
- 0x0170, 0x0170,
- 0x0172, 0x0172,
- 0x0174, 0x0174,
- 0x0176, 0x0176,
- 0x0178, 0x0179,
- 0x017b, 0x017b,
- 0x017d, 0x017d,
- 0x0181, 0x0182,
- 0x0184, 0x0184,
- 0x0186, 0x0187,
- 0x0189, 0x018b,
- 0x018e, 0x0191,
- 0x0193, 0x0194,
- 0x0196, 0x0198,
- 0x019c, 0x019d,
- 0x019f, 0x01a0,
- 0x01a2, 0x01a2,
- 0x01a4, 0x01a4,
- 0x01a6, 0x01a7,
- 0x01a9, 0x01a9,
- 0x01ac, 0x01ac,
- 0x01ae, 0x01af,
- 0x01b1, 0x01b3,
- 0x01b5, 0x01b5,
- 0x01b7, 0x01b8,
- 0x01bc, 0x01bc,
- 0x01c4, 0x01c4,
- 0x01c7, 0x01c7,
- 0x01ca, 0x01ca,
- 0x01cd, 0x01cd,
- 0x01cf, 0x01cf,
- 0x01d1, 0x01d1,
- 0x01d3, 0x01d3,
- 0x01d5, 0x01d5,
- 0x01d7, 0x01d7,
- 0x01d9, 0x01d9,
- 0x01db, 0x01db,
- 0x01de, 0x01de,
- 0x01e0, 0x01e0,
- 0x01e2, 0x01e2,
- 0x01e4, 0x01e4,
- 0x01e6, 0x01e6,
- 0x01e8, 0x01e8,
- 0x01ea, 0x01ea,
- 0x01ec, 0x01ec,
- 0x01ee, 0x01ee,
- 0x01f1, 0x01f1,
- 0x01f4, 0x01f4,
- 0x01f6, 0x01f8,
- 0x01fa, 0x01fa,
- 0x01fc, 0x01fc,
- 0x01fe, 0x01fe,
- 0x0200, 0x0200,
- 0x0202, 0x0202,
- 0x0204, 0x0204,
- 0x0206, 0x0206,
- 0x0208, 0x0208,
- 0x020a, 0x020a,
- 0x020c, 0x020c,
- 0x020e, 0x020e,
- 0x0210, 0x0210,
- 0x0212, 0x0212,
- 0x0214, 0x0214,
- 0x0216, 0x0216,
- 0x0218, 0x0218,
- 0x021a, 0x021a,
- 0x021c, 0x021c,
- 0x021e, 0x021e,
- 0x0220, 0x0220,
- 0x0222, 0x0222,
- 0x0224, 0x0224,
- 0x0226, 0x0226,
- 0x0228, 0x0228,
- 0x022a, 0x022a,
- 0x022c, 0x022c,
- 0x022e, 0x022e,
- 0x0230, 0x0230,
- 0x0232, 0x0232,
- 0x023a, 0x023b,
- 0x023d, 0x023e,
- 0x0241, 0x0241,
- 0x0243, 0x0246,
- 0x0248, 0x0248,
- 0x024a, 0x024a,
- 0x024c, 0x024c,
- 0x024e, 0x024e,
- 0x0370, 0x0370,
- 0x0372, 0x0372,
- 0x0376, 0x0376,
- 0x037f, 0x037f,
- 0x0386, 0x0386,
- 0x0388, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x038f,
- 0x0391, 0x03a1,
- 0x03a3, 0x03ab,
- 0x03cf, 0x03cf,
- 0x03d2, 0x03d4,
- 0x03d8, 0x03d8,
- 0x03da, 0x03da,
- 0x03dc, 0x03dc,
- 0x03de, 0x03de,
- 0x03e0, 0x03e0,
- 0x03e2, 0x03e2,
- 0x03e4, 0x03e4,
- 0x03e6, 0x03e6,
- 0x03e8, 0x03e8,
- 0x03ea, 0x03ea,
- 0x03ec, 0x03ec,
- 0x03ee, 0x03ee,
- 0x03f4, 0x03f4,
- 0x03f7, 0x03f7,
- 0x03f9, 0x03fa,
- 0x03fd, 0x042f,
- 0x0460, 0x0460,
- 0x0462, 0x0462,
- 0x0464, 0x0464,
- 0x0466, 0x0466,
- 0x0468, 0x0468,
- 0x046a, 0x046a,
- 0x046c, 0x046c,
- 0x046e, 0x046e,
- 0x0470, 0x0470,
- 0x0472, 0x0472,
- 0x0474, 0x0474,
- 0x0476, 0x0476,
- 0x0478, 0x0478,
- 0x047a, 0x047a,
- 0x047c, 0x047c,
- 0x047e, 0x047e,
- 0x0480, 0x0480,
- 0x048a, 0x048a,
- 0x048c, 0x048c,
- 0x048e, 0x048e,
- 0x0490, 0x0490,
- 0x0492, 0x0492,
- 0x0494, 0x0494,
- 0x0496, 0x0496,
- 0x0498, 0x0498,
- 0x049a, 0x049a,
- 0x049c, 0x049c,
- 0x049e, 0x049e,
- 0x04a0, 0x04a0,
- 0x04a2, 0x04a2,
- 0x04a4, 0x04a4,
- 0x04a6, 0x04a6,
- 0x04a8, 0x04a8,
- 0x04aa, 0x04aa,
- 0x04ac, 0x04ac,
- 0x04ae, 0x04ae,
- 0x04b0, 0x04b0,
- 0x04b2, 0x04b2,
- 0x04b4, 0x04b4,
- 0x04b6, 0x04b6,
- 0x04b8, 0x04b8,
- 0x04ba, 0x04ba,
- 0x04bc, 0x04bc,
- 0x04be, 0x04be,
- 0x04c0, 0x04c1,
- 0x04c3, 0x04c3,
- 0x04c5, 0x04c5,
- 0x04c7, 0x04c7,
- 0x04c9, 0x04c9,
- 0x04cb, 0x04cb,
- 0x04cd, 0x04cd,
- 0x04d0, 0x04d0,
- 0x04d2, 0x04d2,
- 0x04d4, 0x04d4,
- 0x04d6, 0x04d6,
- 0x04d8, 0x04d8,
- 0x04da, 0x04da,
- 0x04dc, 0x04dc,
- 0x04de, 0x04de,
- 0x04e0, 0x04e0,
- 0x04e2, 0x04e2,
- 0x04e4, 0x04e4,
- 0x04e6, 0x04e6,
- 0x04e8, 0x04e8,
- 0x04ea, 0x04ea,
- 0x04ec, 0x04ec,
- 0x04ee, 0x04ee,
- 0x04f0, 0x04f0,
- 0x04f2, 0x04f2,
- 0x04f4, 0x04f4,
- 0x04f6, 0x04f6,
- 0x04f8, 0x04f8,
- 0x04fa, 0x04fa,
- 0x04fc, 0x04fc,
- 0x04fe, 0x04fe,
- 0x0500, 0x0500,
- 0x0502, 0x0502,
- 0x0504, 0x0504,
- 0x0506, 0x0506,
- 0x0508, 0x0508,
- 0x050a, 0x050a,
- 0x050c, 0x050c,
- 0x050e, 0x050e,
- 0x0510, 0x0510,
- 0x0512, 0x0512,
- 0x0514, 0x0514,
- 0x0516, 0x0516,
- 0x0518, 0x0518,
- 0x051a, 0x051a,
- 0x051c, 0x051c,
- 0x051e, 0x051e,
- 0x0520, 0x0520,
- 0x0522, 0x0522,
- 0x0524, 0x0524,
- 0x0526, 0x0526,
- 0x0528, 0x0528,
- 0x052a, 0x052a,
- 0x052c, 0x052c,
- 0x052e, 0x052e,
- 0x0531, 0x0556,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x13a0, 0x13f5,
- 0x1e00, 0x1e00,
- 0x1e02, 0x1e02,
- 0x1e04, 0x1e04,
- 0x1e06, 0x1e06,
- 0x1e08, 0x1e08,
- 0x1e0a, 0x1e0a,
- 0x1e0c, 0x1e0c,
- 0x1e0e, 0x1e0e,
- 0x1e10, 0x1e10,
- 0x1e12, 0x1e12,
- 0x1e14, 0x1e14,
- 0x1e16, 0x1e16,
- 0x1e18, 0x1e18,
- 0x1e1a, 0x1e1a,
- 0x1e1c, 0x1e1c,
- 0x1e1e, 0x1e1e,
- 0x1e20, 0x1e20,
- 0x1e22, 0x1e22,
- 0x1e24, 0x1e24,
- 0x1e26, 0x1e26,
- 0x1e28, 0x1e28,
- 0x1e2a, 0x1e2a,
- 0x1e2c, 0x1e2c,
- 0x1e2e, 0x1e2e,
- 0x1e30, 0x1e30,
- 0x1e32, 0x1e32,
- 0x1e34, 0x1e34,
- 0x1e36, 0x1e36,
- 0x1e38, 0x1e38,
- 0x1e3a, 0x1e3a,
- 0x1e3c, 0x1e3c,
- 0x1e3e, 0x1e3e,
- 0x1e40, 0x1e40,
- 0x1e42, 0x1e42,
- 0x1e44, 0x1e44,
- 0x1e46, 0x1e46,
- 0x1e48, 0x1e48,
- 0x1e4a, 0x1e4a,
- 0x1e4c, 0x1e4c,
- 0x1e4e, 0x1e4e,
- 0x1e50, 0x1e50,
- 0x1e52, 0x1e52,
- 0x1e54, 0x1e54,
- 0x1e56, 0x1e56,
- 0x1e58, 0x1e58,
- 0x1e5a, 0x1e5a,
- 0x1e5c, 0x1e5c,
- 0x1e5e, 0x1e5e,
- 0x1e60, 0x1e60,
- 0x1e62, 0x1e62,
- 0x1e64, 0x1e64,
- 0x1e66, 0x1e66,
- 0x1e68, 0x1e68,
- 0x1e6a, 0x1e6a,
- 0x1e6c, 0x1e6c,
- 0x1e6e, 0x1e6e,
- 0x1e70, 0x1e70,
- 0x1e72, 0x1e72,
- 0x1e74, 0x1e74,
- 0x1e76, 0x1e76,
- 0x1e78, 0x1e78,
- 0x1e7a, 0x1e7a,
- 0x1e7c, 0x1e7c,
- 0x1e7e, 0x1e7e,
- 0x1e80, 0x1e80,
- 0x1e82, 0x1e82,
- 0x1e84, 0x1e84,
- 0x1e86, 0x1e86,
- 0x1e88, 0x1e88,
- 0x1e8a, 0x1e8a,
- 0x1e8c, 0x1e8c,
- 0x1e8e, 0x1e8e,
- 0x1e90, 0x1e90,
- 0x1e92, 0x1e92,
- 0x1e94, 0x1e94,
- 0x1e9e, 0x1e9e,
- 0x1ea0, 0x1ea0,
- 0x1ea2, 0x1ea2,
- 0x1ea4, 0x1ea4,
- 0x1ea6, 0x1ea6,
- 0x1ea8, 0x1ea8,
- 0x1eaa, 0x1eaa,
- 0x1eac, 0x1eac,
- 0x1eae, 0x1eae,
- 0x1eb0, 0x1eb0,
- 0x1eb2, 0x1eb2,
- 0x1eb4, 0x1eb4,
- 0x1eb6, 0x1eb6,
- 0x1eb8, 0x1eb8,
- 0x1eba, 0x1eba,
- 0x1ebc, 0x1ebc,
- 0x1ebe, 0x1ebe,
- 0x1ec0, 0x1ec0,
- 0x1ec2, 0x1ec2,
- 0x1ec4, 0x1ec4,
- 0x1ec6, 0x1ec6,
- 0x1ec8, 0x1ec8,
- 0x1eca, 0x1eca,
- 0x1ecc, 0x1ecc,
- 0x1ece, 0x1ece,
- 0x1ed0, 0x1ed0,
- 0x1ed2, 0x1ed2,
- 0x1ed4, 0x1ed4,
- 0x1ed6, 0x1ed6,
- 0x1ed8, 0x1ed8,
- 0x1eda, 0x1eda,
- 0x1edc, 0x1edc,
- 0x1ede, 0x1ede,
- 0x1ee0, 0x1ee0,
- 0x1ee2, 0x1ee2,
- 0x1ee4, 0x1ee4,
- 0x1ee6, 0x1ee6,
- 0x1ee8, 0x1ee8,
- 0x1eea, 0x1eea,
- 0x1eec, 0x1eec,
- 0x1eee, 0x1eee,
- 0x1ef0, 0x1ef0,
- 0x1ef2, 0x1ef2,
- 0x1ef4, 0x1ef4,
- 0x1ef6, 0x1ef6,
- 0x1ef8, 0x1ef8,
- 0x1efa, 0x1efa,
- 0x1efc, 0x1efc,
- 0x1efe, 0x1efe,
- 0x1f08, 0x1f0f,
- 0x1f18, 0x1f1d,
- 0x1f28, 0x1f2f,
- 0x1f38, 0x1f3f,
- 0x1f48, 0x1f4d,
- 0x1f59, 0x1f59,
- 0x1f5b, 0x1f5b,
- 0x1f5d, 0x1f5d,
- 0x1f5f, 0x1f5f,
- 0x1f68, 0x1f6f,
- 0x1fb8, 0x1fbb,
- 0x1fc8, 0x1fcb,
- 0x1fd8, 0x1fdb,
- 0x1fe8, 0x1fec,
- 0x1ff8, 0x1ffb,
- 0x2102, 0x2102,
- 0x2107, 0x2107,
- 0x210b, 0x210d,
- 0x2110, 0x2112,
- 0x2115, 0x2115,
- 0x2119, 0x211d,
- 0x2124, 0x2124,
- 0x2126, 0x2126,
- 0x2128, 0x2128,
- 0x212a, 0x212d,
- 0x2130, 0x2133,
- 0x213e, 0x213f,
- 0x2145, 0x2145,
- 0x2160, 0x216f,
- 0x2183, 0x2183,
- 0x24b6, 0x24cf,
- 0x2c00, 0x2c2e,
- 0x2c60, 0x2c60,
- 0x2c62, 0x2c64,
- 0x2c67, 0x2c67,
- 0x2c69, 0x2c69,
- 0x2c6b, 0x2c6b,
- 0x2c6d, 0x2c70,
- 0x2c72, 0x2c72,
- 0x2c75, 0x2c75,
- 0x2c7e, 0x2c80,
- 0x2c82, 0x2c82,
- 0x2c84, 0x2c84,
- 0x2c86, 0x2c86,
- 0x2c88, 0x2c88,
- 0x2c8a, 0x2c8a,
- 0x2c8c, 0x2c8c,
- 0x2c8e, 0x2c8e,
- 0x2c90, 0x2c90,
- 0x2c92, 0x2c92,
- 0x2c94, 0x2c94,
- 0x2c96, 0x2c96,
- 0x2c98, 0x2c98,
- 0x2c9a, 0x2c9a,
- 0x2c9c, 0x2c9c,
- 0x2c9e, 0x2c9e,
- 0x2ca0, 0x2ca0,
- 0x2ca2, 0x2ca2,
- 0x2ca4, 0x2ca4,
- 0x2ca6, 0x2ca6,
- 0x2ca8, 0x2ca8,
- 0x2caa, 0x2caa,
- 0x2cac, 0x2cac,
- 0x2cae, 0x2cae,
- 0x2cb0, 0x2cb0,
- 0x2cb2, 0x2cb2,
- 0x2cb4, 0x2cb4,
- 0x2cb6, 0x2cb6,
- 0x2cb8, 0x2cb8,
- 0x2cba, 0x2cba,
- 0x2cbc, 0x2cbc,
- 0x2cbe, 0x2cbe,
- 0x2cc0, 0x2cc0,
- 0x2cc2, 0x2cc2,
- 0x2cc4, 0x2cc4,
- 0x2cc6, 0x2cc6,
- 0x2cc8, 0x2cc8,
- 0x2cca, 0x2cca,
- 0x2ccc, 0x2ccc,
- 0x2cce, 0x2cce,
- 0x2cd0, 0x2cd0,
- 0x2cd2, 0x2cd2,
- 0x2cd4, 0x2cd4,
- 0x2cd6, 0x2cd6,
- 0x2cd8, 0x2cd8,
- 0x2cda, 0x2cda,
- 0x2cdc, 0x2cdc,
- 0x2cde, 0x2cde,
- 0x2ce0, 0x2ce0,
- 0x2ce2, 0x2ce2,
- 0x2ceb, 0x2ceb,
- 0x2ced, 0x2ced,
- 0x2cf2, 0x2cf2,
- 0xa640, 0xa640,
- 0xa642, 0xa642,
- 0xa644, 0xa644,
- 0xa646, 0xa646,
- 0xa648, 0xa648,
- 0xa64a, 0xa64a,
- 0xa64c, 0xa64c,
- 0xa64e, 0xa64e,
- 0xa650, 0xa650,
- 0xa652, 0xa652,
- 0xa654, 0xa654,
- 0xa656, 0xa656,
- 0xa658, 0xa658,
- 0xa65a, 0xa65a,
- 0xa65c, 0xa65c,
- 0xa65e, 0xa65e,
- 0xa660, 0xa660,
- 0xa662, 0xa662,
- 0xa664, 0xa664,
- 0xa666, 0xa666,
- 0xa668, 0xa668,
- 0xa66a, 0xa66a,
- 0xa66c, 0xa66c,
- 0xa680, 0xa680,
- 0xa682, 0xa682,
- 0xa684, 0xa684,
- 0xa686, 0xa686,
- 0xa688, 0xa688,
- 0xa68a, 0xa68a,
- 0xa68c, 0xa68c,
- 0xa68e, 0xa68e,
- 0xa690, 0xa690,
- 0xa692, 0xa692,
- 0xa694, 0xa694,
- 0xa696, 0xa696,
- 0xa698, 0xa698,
- 0xa69a, 0xa69a,
- 0xa722, 0xa722,
- 0xa724, 0xa724,
- 0xa726, 0xa726,
- 0xa728, 0xa728,
- 0xa72a, 0xa72a,
- 0xa72c, 0xa72c,
- 0xa72e, 0xa72e,
- 0xa732, 0xa732,
- 0xa734, 0xa734,
- 0xa736, 0xa736,
- 0xa738, 0xa738,
- 0xa73a, 0xa73a,
- 0xa73c, 0xa73c,
- 0xa73e, 0xa73e,
- 0xa740, 0xa740,
- 0xa742, 0xa742,
- 0xa744, 0xa744,
- 0xa746, 0xa746,
- 0xa748, 0xa748,
- 0xa74a, 0xa74a,
- 0xa74c, 0xa74c,
- 0xa74e, 0xa74e,
- 0xa750, 0xa750,
- 0xa752, 0xa752,
- 0xa754, 0xa754,
- 0xa756, 0xa756,
- 0xa758, 0xa758,
- 0xa75a, 0xa75a,
- 0xa75c, 0xa75c,
- 0xa75e, 0xa75e,
- 0xa760, 0xa760,
- 0xa762, 0xa762,
- 0xa764, 0xa764,
- 0xa766, 0xa766,
- 0xa768, 0xa768,
- 0xa76a, 0xa76a,
- 0xa76c, 0xa76c,
- 0xa76e, 0xa76e,
- 0xa779, 0xa779,
- 0xa77b, 0xa77b,
- 0xa77d, 0xa77e,
- 0xa780, 0xa780,
- 0xa782, 0xa782,
- 0xa784, 0xa784,
- 0xa786, 0xa786,
- 0xa78b, 0xa78b,
- 0xa78d, 0xa78d,
- 0xa790, 0xa790,
- 0xa792, 0xa792,
- 0xa796, 0xa796,
- 0xa798, 0xa798,
- 0xa79a, 0xa79a,
- 0xa79c, 0xa79c,
- 0xa79e, 0xa79e,
- 0xa7a0, 0xa7a0,
- 0xa7a2, 0xa7a2,
- 0xa7a4, 0xa7a4,
- 0xa7a6, 0xa7a6,
- 0xa7a8, 0xa7a8,
- 0xa7aa, 0xa7ae,
- 0xa7b0, 0xa7b4,
- 0xa7b6, 0xa7b6,
- 0xff21, 0xff3a,
- 0x10400, 0x10427,
- 0x104b0, 0x104d3,
- 0x10c80, 0x10cb2,
- 0x118a0, 0x118bf,
- 0x1d400, 0x1d419,
- 0x1d434, 0x1d44d,
- 0x1d468, 0x1d481,
- 0x1d49c, 0x1d49c,
- 0x1d49e, 0x1d49f,
- 0x1d4a2, 0x1d4a2,
- 0x1d4a5, 0x1d4a6,
- 0x1d4a9, 0x1d4ac,
- 0x1d4ae, 0x1d4b5,
- 0x1d4d0, 0x1d4e9,
- 0x1d504, 0x1d505,
- 0x1d507, 0x1d50a,
- 0x1d50d, 0x1d514,
- 0x1d516, 0x1d51c,
- 0x1d538, 0x1d539,
- 0x1d53b, 0x1d53e,
- 0x1d540, 0x1d544,
- 0x1d546, 0x1d546,
- 0x1d54a, 0x1d550,
- 0x1d56c, 0x1d585,
- 0x1d5a0, 0x1d5b9,
- 0x1d5d4, 0x1d5ed,
- 0x1d608, 0x1d621,
- 0x1d63c, 0x1d655,
- 0x1d670, 0x1d689,
- 0x1d6a8, 0x1d6c0,
- 0x1d6e2, 0x1d6fa,
- 0x1d71c, 0x1d734,
- 0x1d756, 0x1d76e,
- 0x1d790, 0x1d7a8,
- 0x1d7ca, 0x1d7ca,
- 0x1e900, 0x1e921,
- 0x1f130, 0x1f149,
- 0x1f150, 0x1f169,
- 0x1f170, 0x1f189,
-}; /* CR_Upper */
-
-/* 'XDigit': [[:XDigit:]] */
-static const OnigCodePoint CR_XDigit[] = {
- 3,
- 0x0030, 0x0039,
- 0x0041, 0x0046,
- 0x0061, 0x0066,
-}; /* CR_XDigit */
-
-/* 'Word': [[:Word:]] */
-static const OnigCodePoint CR_Word[] = {
- 679,
- 0x0030, 0x0039,
- 0x0041, 0x005a,
- 0x005f, 0x005f,
- 0x0061, 0x007a,
- 0x00aa, 0x00aa,
- 0x00b5, 0x00b5,
- 0x00ba, 0x00ba,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00f6,
- 0x00f8, 0x02c1,
- 0x02c6, 0x02d1,
- 0x02e0, 0x02e4,
- 0x02ec, 0x02ec,
- 0x02ee, 0x02ee,
- 0x0300, 0x0374,
- 0x0376, 0x0377,
- 0x037a, 0x037d,
- 0x037f, 0x037f,
- 0x0386, 0x0386,
- 0x0388, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03f5,
- 0x03f7, 0x0481,
- 0x0483, 0x052f,
- 0x0531, 0x0556,
- 0x0559, 0x0559,
- 0x0561, 0x0587,
- 0x0591, 0x05bd,
- 0x05bf, 0x05bf,
- 0x05c1, 0x05c2,
- 0x05c4, 0x05c5,
- 0x05c7, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f2,
- 0x0610, 0x061a,
- 0x0620, 0x0669,
- 0x066e, 0x06d3,
- 0x06d5, 0x06dc,
- 0x06df, 0x06e8,
- 0x06ea, 0x06fc,
- 0x06ff, 0x06ff,
- 0x0710, 0x074a,
- 0x074d, 0x07b1,
- 0x07c0, 0x07f5,
- 0x07fa, 0x07fa,
- 0x0800, 0x082d,
- 0x0840, 0x085b,
- 0x08a0, 0x08b4,
- 0x08b6, 0x08bd,
- 0x08d4, 0x08e1,
- 0x08e3, 0x0963,
- 0x0966, 0x096f,
- 0x0971, 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, 0x09f1,
- 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, 0x0a75,
- 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, 0x0aef,
- 0x0af9, 0x0af9,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b63,
- 0x0b66, 0x0b6f,
- 0x0b71, 0x0b71,
- 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, 0x0bef,
- 0x0c00, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c80, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d01, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4e,
- 0x0d54, 0x0d57,
- 0x0d5f, 0x0d63,
- 0x0d66, 0x0d6f,
- 0x0d7a, 0x0d7f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0de6, 0x0def,
- 0x0df2, 0x0df3,
- 0x0e01, 0x0e3a,
- 0x0e40, 0x0e4e,
- 0x0e50, 0x0e59,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edf,
- 0x0f00, 0x0f00,
- 0x0f18, 0x0f19,
- 0x0f20, 0x0f29,
- 0x0f35, 0x0f35,
- 0x0f37, 0x0f37,
- 0x0f39, 0x0f39,
- 0x0f3e, 0x0f47,
- 0x0f49, 0x0f6c,
- 0x0f71, 0x0f84,
- 0x0f86, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fc6, 0x0fc6,
- 0x1000, 0x1049,
- 0x1050, 0x109d,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x10d0, 0x10fa,
- 0x10fc, 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, 0x135f,
- 0x1380, 0x138f,
- 0x13a0, 0x13f5,
- 0x13f8, 0x13fd,
- 0x1401, 0x166c,
- 0x166f, 0x167f,
- 0x1681, 0x169a,
- 0x16a0, 0x16ea,
- 0x16ee, 0x16f8,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1734,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17d3,
- 0x17d7, 0x17d7,
- 0x17dc, 0x17dd,
- 0x17e0, 0x17e9,
- 0x180b, 0x180d,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18aa,
- 0x18b0, 0x18f5,
- 0x1900, 0x191e,
- 0x1920, 0x192b,
- 0x1930, 0x193b,
- 0x1946, 0x196d,
- 0x1970, 0x1974,
- 0x1980, 0x19ab,
- 0x19b0, 0x19c9,
- 0x19d0, 0x19d9,
- 0x1a00, 0x1a1b,
- 0x1a20, 0x1a5e,
- 0x1a60, 0x1a7c,
- 0x1a7f, 0x1a89,
- 0x1a90, 0x1a99,
- 0x1aa7, 0x1aa7,
- 0x1ab0, 0x1abe,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b59,
- 0x1b6b, 0x1b73,
- 0x1b80, 0x1bf3,
- 0x1c00, 0x1c37,
- 0x1c40, 0x1c49,
- 0x1c4d, 0x1c7d,
- 0x1c80, 0x1c88,
- 0x1cd0, 0x1cd2,
- 0x1cd4, 0x1cf6,
- 0x1cf8, 0x1cf9,
- 0x1d00, 0x1df5,
- 0x1dfb, 0x1f15,
- 0x1f18, 0x1f1d,
- 0x1f20, 0x1f45,
- 0x1f48, 0x1f4d,
- 0x1f50, 0x1f57,
- 0x1f59, 0x1f59,
- 0x1f5b, 0x1f5b,
- 0x1f5d, 0x1f5d,
- 0x1f5f, 0x1f7d,
- 0x1f80, 0x1fb4,
- 0x1fb6, 0x1fbc,
- 0x1fbe, 0x1fbe,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fcc,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fdb,
- 0x1fe0, 0x1fec,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ffc,
- 0x203f, 0x2040,
- 0x2054, 0x2054,
- 0x2071, 0x2071,
- 0x207f, 0x207f,
- 0x2090, 0x209c,
- 0x20d0, 0x20f0,
- 0x2102, 0x2102,
- 0x2107, 0x2107,
- 0x210a, 0x2113,
- 0x2115, 0x2115,
- 0x2119, 0x211d,
- 0x2124, 0x2124,
- 0x2126, 0x2126,
- 0x2128, 0x2128,
- 0x212a, 0x212d,
- 0x212f, 0x2139,
- 0x213c, 0x213f,
- 0x2145, 0x2149,
- 0x214e, 0x214e,
- 0x2160, 0x2188,
- 0x24b6, 0x24e9,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2ce4,
- 0x2ceb, 0x2cf3,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0x2d30, 0x2d67,
- 0x2d6f, 0x2d6f,
- 0x2d7f, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0x2de0, 0x2dff,
- 0x2e2f, 0x2e2f,
- 0x3005, 0x3007,
- 0x3021, 0x302f,
- 0x3031, 0x3035,
- 0x3038, 0x303c,
- 0x3041, 0x3096,
- 0x3099, 0x309a,
- 0x309d, 0x309f,
- 0x30a1, 0x30fa,
- 0x30fc, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x31a0, 0x31ba,
- 0x31f0, 0x31ff,
- 0x3400, 0x4db5,
- 0x4e00, 0x9fd5,
- 0xa000, 0xa48c,
- 0xa4d0, 0xa4fd,
- 0xa500, 0xa60c,
- 0xa610, 0xa62b,
- 0xa640, 0xa672,
- 0xa674, 0xa67d,
- 0xa67f, 0xa6f1,
- 0xa717, 0xa71f,
- 0xa722, 0xa788,
- 0xa78b, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7f7, 0xa827,
- 0xa840, 0xa873,
- 0xa880, 0xa8c5,
- 0xa8d0, 0xa8d9,
- 0xa8e0, 0xa8f7,
- 0xa8fb, 0xa8fb,
- 0xa8fd, 0xa8fd,
- 0xa900, 0xa92d,
- 0xa930, 0xa953,
- 0xa960, 0xa97c,
- 0xa980, 0xa9c0,
- 0xa9cf, 0xa9d9,
- 0xa9e0, 0xa9fe,
- 0xaa00, 0xaa36,
- 0xaa40, 0xaa4d,
- 0xaa50, 0xaa59,
- 0xaa60, 0xaa76,
- 0xaa7a, 0xaac2,
- 0xaadb, 0xaadd,
- 0xaae0, 0xaaef,
- 0xaaf2, 0xaaf6,
- 0xab01, 0xab06,
- 0xab09, 0xab0e,
- 0xab11, 0xab16,
- 0xab20, 0xab26,
- 0xab28, 0xab2e,
- 0xab30, 0xab5a,
- 0xab5c, 0xab65,
- 0xab70, 0xabea,
- 0xabec, 0xabed,
- 0xabf0, 0xabf9,
- 0xac00, 0xd7a3,
- 0xd7b0, 0xd7c6,
- 0xd7cb, 0xd7fb,
- 0xf900, 0xfa6d,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb28,
- 0xfb2a, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3d,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfb,
- 0xfe00, 0xfe0f,
- 0xfe20, 0xfe2f,
- 0xfe33, 0xfe34,
- 0xfe4d, 0xfe4f,
- 0xfe70, 0xfe74,
- 0xfe76, 0xfefc,
- 0xff10, 0xff19,
- 0xff21, 0xff3a,
- 0xff3f, 0xff3f,
- 0xff41, 0xff5a,
- 0xff66, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
- 0x10000, 0x1000b,
- 0x1000d, 0x10026,
- 0x10028, 0x1003a,
- 0x1003c, 0x1003d,
- 0x1003f, 0x1004d,
- 0x10050, 0x1005d,
- 0x10080, 0x100fa,
- 0x10140, 0x10174,
- 0x101fd, 0x101fd,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x102e0, 0x102e0,
- 0x10300, 0x1031f,
- 0x10330, 0x1034a,
- 0x10350, 0x1037a,
- 0x10380, 0x1039d,
- 0x103a0, 0x103c3,
- 0x103c8, 0x103cf,
- 0x103d1, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x10855,
- 0x10860, 0x10876,
- 0x10880, 0x1089e,
- 0x108e0, 0x108f2,
- 0x108f4, 0x108f5,
- 0x10900, 0x10915,
- 0x10920, 0x10939,
- 0x10980, 0x109b7,
- 0x109be, 0x109bf,
- 0x10a00, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a3f,
- 0x10a60, 0x10a7c,
- 0x10a80, 0x10a9c,
- 0x10ac0, 0x10ac7,
- 0x10ac9, 0x10ae6,
- 0x10b00, 0x10b35,
- 0x10b40, 0x10b55,
- 0x10b60, 0x10b72,
- 0x10b80, 0x10b91,
- 0x10c00, 0x10c48,
- 0x10c80, 0x10cb2,
- 0x10cc0, 0x10cf2,
- 0x11000, 0x11046,
- 0x11066, 0x1106f,
- 0x1107f, 0x110ba,
- 0x110d0, 0x110e8,
- 0x110f0, 0x110f9,
- 0x11100, 0x11134,
- 0x11136, 0x1113f,
- 0x11150, 0x11173,
- 0x11176, 0x11176,
- 0x11180, 0x111c4,
- 0x111ca, 0x111cc,
- 0x111d0, 0x111da,
- 0x111dc, 0x111dc,
- 0x11200, 0x11211,
- 0x11213, 0x11237,
- 0x1123e, 0x1123e,
- 0x11280, 0x11286,
- 0x11288, 0x11288,
- 0x1128a, 0x1128d,
- 0x1128f, 0x1129d,
- 0x1129f, 0x112a8,
- 0x112b0, 0x112ea,
- 0x112f0, 0x112f9,
- 0x11300, 0x11303,
- 0x11305, 0x1130c,
- 0x1130f, 0x11310,
- 0x11313, 0x11328,
- 0x1132a, 0x11330,
- 0x11332, 0x11333,
- 0x11335, 0x11339,
- 0x1133c, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134d,
- 0x11350, 0x11350,
- 0x11357, 0x11357,
- 0x1135d, 0x11363,
- 0x11366, 0x1136c,
- 0x11370, 0x11374,
- 0x11400, 0x1144a,
- 0x11450, 0x11459,
- 0x11480, 0x114c5,
- 0x114c7, 0x114c7,
- 0x114d0, 0x114d9,
- 0x11580, 0x115b5,
- 0x115b8, 0x115c0,
- 0x115d8, 0x115dd,
- 0x11600, 0x11640,
- 0x11644, 0x11644,
- 0x11650, 0x11659,
- 0x11680, 0x116b7,
- 0x116c0, 0x116c9,
- 0x11700, 0x11719,
- 0x1171d, 0x1172b,
- 0x11730, 0x11739,
- 0x118a0, 0x118e9,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x11c00, 0x11c08,
- 0x11c0a, 0x11c36,
- 0x11c38, 0x11c40,
- 0x11c50, 0x11c59,
- 0x11c72, 0x11c8f,
- 0x11c92, 0x11ca7,
- 0x11ca9, 0x11cb6,
- 0x12000, 0x12399,
- 0x12400, 0x1246e,
- 0x12480, 0x12543,
- 0x13000, 0x1342e,
- 0x14400, 0x14646,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16a60, 0x16a69,
- 0x16ad0, 0x16aed,
- 0x16af0, 0x16af4,
- 0x16b00, 0x16b36,
- 0x16b40, 0x16b43,
- 0x16b50, 0x16b59,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f8f, 0x16f9f,
- 0x16fe0, 0x16fe0,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 0x1bc9d, 0x1bc9e,
- 0x1d165, 0x1d169,
- 0x1d16d, 0x1d172,
- 0x1d17b, 0x1d182,
- 0x1d185, 0x1d18b,
- 0x1d1aa, 0x1d1ad,
- 0x1d242, 0x1d244,
- 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, 0x1d6c0,
- 0x1d6c2, 0x1d6da,
- 0x1d6dc, 0x1d6fa,
- 0x1d6fc, 0x1d714,
- 0x1d716, 0x1d734,
- 0x1d736, 0x1d74e,
- 0x1d750, 0x1d76e,
- 0x1d770, 0x1d788,
- 0x1d78a, 0x1d7a8,
- 0x1d7aa, 0x1d7c2,
- 0x1d7c4, 0x1d7cb,
- 0x1d7ce, 0x1d7ff,
- 0x1da00, 0x1da36,
- 0x1da3b, 0x1da6c,
- 0x1da75, 0x1da75,
- 0x1da84, 0x1da84,
- 0x1da9b, 0x1da9f,
- 0x1daa1, 0x1daaf,
- 0x1e000, 0x1e006,
- 0x1e008, 0x1e018,
- 0x1e01b, 0x1e021,
- 0x1e023, 0x1e024,
- 0x1e026, 0x1e02a,
- 0x1e800, 0x1e8c4,
- 0x1e8d0, 0x1e8d6,
- 0x1e900, 0x1e94a,
- 0x1e950, 0x1e959,
- 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,
- 0x1f130, 0x1f149,
- 0x1f150, 0x1f169,
- 0x1f170, 0x1f189,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
- 0xe0100, 0xe01ef,
-}; /* CR_Word */
-
-/* 'Alnum': [[:Alnum:]] */
-static const OnigCodePoint CR_Alnum[] = {
- 678,
- 0x0030, 0x0039,
- 0x0041, 0x005a,
- 0x0061, 0x007a,
- 0x00aa, 0x00aa,
- 0x00b5, 0x00b5,
- 0x00ba, 0x00ba,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00f6,
- 0x00f8, 0x02c1,
- 0x02c6, 0x02d1,
- 0x02e0, 0x02e4,
- 0x02ec, 0x02ec,
- 0x02ee, 0x02ee,
- 0x0345, 0x0345,
- 0x0370, 0x0374,
- 0x0376, 0x0377,
- 0x037a, 0x037d,
- 0x037f, 0x037f,
- 0x0386, 0x0386,
- 0x0388, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03f5,
- 0x03f7, 0x0481,
- 0x048a, 0x052f,
- 0x0531, 0x0556,
- 0x0559, 0x0559,
- 0x0561, 0x0587,
- 0x05b0, 0x05bd,
- 0x05bf, 0x05bf,
- 0x05c1, 0x05c2,
- 0x05c4, 0x05c5,
- 0x05c7, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f2,
- 0x0610, 0x061a,
- 0x0620, 0x0657,
- 0x0659, 0x0669,
- 0x066e, 0x06d3,
- 0x06d5, 0x06dc,
- 0x06e1, 0x06e8,
- 0x06ed, 0x06fc,
- 0x06ff, 0x06ff,
- 0x0710, 0x073f,
- 0x074d, 0x07b1,
- 0x07c0, 0x07ea,
- 0x07f4, 0x07f5,
- 0x07fa, 0x07fa,
- 0x0800, 0x0817,
- 0x081a, 0x082c,
- 0x0840, 0x0858,
- 0x08a0, 0x08b4,
- 0x08b6, 0x08bd,
- 0x08d4, 0x08df,
- 0x08e3, 0x08e9,
- 0x08f0, 0x093b,
- 0x093d, 0x094c,
- 0x094e, 0x0950,
- 0x0955, 0x0963,
- 0x0966, 0x096f,
- 0x0971, 0x0983,
- 0x0985, 0x098c,
- 0x098f, 0x0990,
- 0x0993, 0x09a8,
- 0x09aa, 0x09b0,
- 0x09b2, 0x09b2,
- 0x09b6, 0x09b9,
- 0x09bd, 0x09c4,
- 0x09c7, 0x09c8,
- 0x09cb, 0x09cc,
- 0x09ce, 0x09ce,
- 0x09d7, 0x09d7,
- 0x09dc, 0x09dd,
- 0x09df, 0x09e3,
- 0x09e6, 0x09f1,
- 0x0a01, 0x0a03,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a3e, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4c,
- 0x0a51, 0x0a51,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a66, 0x0a75,
- 0x0a81, 0x0a83,
- 0x0a85, 0x0a8d,
- 0x0a8f, 0x0a91,
- 0x0a93, 0x0aa8,
- 0x0aaa, 0x0ab0,
- 0x0ab2, 0x0ab3,
- 0x0ab5, 0x0ab9,
- 0x0abd, 0x0ac5,
- 0x0ac7, 0x0ac9,
- 0x0acb, 0x0acc,
- 0x0ad0, 0x0ad0,
- 0x0ae0, 0x0ae3,
- 0x0ae6, 0x0aef,
- 0x0af9, 0x0af9,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3d, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4c,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b63,
- 0x0b66, 0x0b6f,
- 0x0b71, 0x0b71,
- 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, 0x0bcc,
- 0x0bd0, 0x0bd0,
- 0x0bd7, 0x0bd7,
- 0x0be6, 0x0bef,
- 0x0c00, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4c,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c80, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbd, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccc,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d01, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4c,
- 0x0d4e, 0x0d4e,
- 0x0d54, 0x0d57,
- 0x0d5f, 0x0d63,
- 0x0d66, 0x0d6f,
- 0x0d7a, 0x0d7f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0de6, 0x0def,
- 0x0df2, 0x0df3,
- 0x0e01, 0x0e3a,
- 0x0e40, 0x0e46,
- 0x0e4d, 0x0e4d,
- 0x0e50, 0x0e59,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ecd, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edf,
- 0x0f00, 0x0f00,
- 0x0f20, 0x0f29,
- 0x0f40, 0x0f47,
- 0x0f49, 0x0f6c,
- 0x0f71, 0x0f81,
- 0x0f88, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x1000, 0x1036,
- 0x1038, 0x1038,
- 0x103b, 0x1049,
- 0x1050, 0x1062,
- 0x1065, 0x1068,
- 0x106e, 0x1086,
- 0x108e, 0x108e,
- 0x1090, 0x1099,
- 0x109c, 0x109d,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x10d0, 0x10fa,
- 0x10fc, 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,
- 0x135f, 0x135f,
- 0x1380, 0x138f,
- 0x13a0, 0x13f5,
- 0x13f8, 0x13fd,
- 0x1401, 0x166c,
- 0x166f, 0x167f,
- 0x1681, 0x169a,
- 0x16a0, 0x16ea,
- 0x16ee, 0x16f8,
- 0x1700, 0x170c,
- 0x170e, 0x1713,
- 0x1720, 0x1733,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17b3,
- 0x17b6, 0x17c8,
- 0x17d7, 0x17d7,
- 0x17dc, 0x17dc,
- 0x17e0, 0x17e9,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18aa,
- 0x18b0, 0x18f5,
- 0x1900, 0x191e,
- 0x1920, 0x192b,
- 0x1930, 0x1938,
- 0x1946, 0x196d,
- 0x1970, 0x1974,
- 0x1980, 0x19ab,
- 0x19b0, 0x19c9,
- 0x19d0, 0x19d9,
- 0x1a00, 0x1a1b,
- 0x1a20, 0x1a5e,
- 0x1a61, 0x1a74,
- 0x1a80, 0x1a89,
- 0x1a90, 0x1a99,
- 0x1aa7, 0x1aa7,
- 0x1b00, 0x1b33,
- 0x1b35, 0x1b43,
- 0x1b45, 0x1b4b,
- 0x1b50, 0x1b59,
- 0x1b80, 0x1ba9,
- 0x1bac, 0x1be5,
- 0x1be7, 0x1bf1,
- 0x1c00, 0x1c35,
- 0x1c40, 0x1c49,
- 0x1c4d, 0x1c7d,
- 0x1c80, 0x1c88,
- 0x1ce9, 0x1cec,
- 0x1cee, 0x1cf3,
- 0x1cf5, 0x1cf6,
- 0x1d00, 0x1dbf,
- 0x1de7, 0x1df4,
- 0x1e00, 0x1f15,
- 0x1f18, 0x1f1d,
- 0x1f20, 0x1f45,
- 0x1f48, 0x1f4d,
- 0x1f50, 0x1f57,
- 0x1f59, 0x1f59,
- 0x1f5b, 0x1f5b,
- 0x1f5d, 0x1f5d,
- 0x1f5f, 0x1f7d,
- 0x1f80, 0x1fb4,
- 0x1fb6, 0x1fbc,
- 0x1fbe, 0x1fbe,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fcc,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fdb,
- 0x1fe0, 0x1fec,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ffc,
- 0x2071, 0x2071,
- 0x207f, 0x207f,
- 0x2090, 0x209c,
- 0x2102, 0x2102,
- 0x2107, 0x2107,
- 0x210a, 0x2113,
- 0x2115, 0x2115,
- 0x2119, 0x211d,
- 0x2124, 0x2124,
- 0x2126, 0x2126,
- 0x2128, 0x2128,
- 0x212a, 0x212d,
- 0x212f, 0x2139,
- 0x213c, 0x213f,
- 0x2145, 0x2149,
- 0x214e, 0x214e,
- 0x2160, 0x2188,
- 0x24b6, 0x24e9,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2ce4,
- 0x2ceb, 0x2cee,
- 0x2cf2, 0x2cf3,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0x2d30, 0x2d67,
- 0x2d6f, 0x2d6f,
- 0x2d80, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0x2de0, 0x2dff,
- 0x2e2f, 0x2e2f,
- 0x3005, 0x3007,
- 0x3021, 0x3029,
- 0x3031, 0x3035,
- 0x3038, 0x303c,
- 0x3041, 0x3096,
- 0x309d, 0x309f,
- 0x30a1, 0x30fa,
- 0x30fc, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x31a0, 0x31ba,
- 0x31f0, 0x31ff,
- 0x3400, 0x4db5,
- 0x4e00, 0x9fd5,
- 0xa000, 0xa48c,
- 0xa4d0, 0xa4fd,
- 0xa500, 0xa60c,
- 0xa610, 0xa62b,
- 0xa640, 0xa66e,
- 0xa674, 0xa67b,
- 0xa67f, 0xa6ef,
- 0xa717, 0xa71f,
- 0xa722, 0xa788,
- 0xa78b, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7f7, 0xa801,
- 0xa803, 0xa805,
- 0xa807, 0xa80a,
- 0xa80c, 0xa827,
- 0xa840, 0xa873,
- 0xa880, 0xa8c3,
- 0xa8c5, 0xa8c5,
- 0xa8d0, 0xa8d9,
- 0xa8f2, 0xa8f7,
- 0xa8fb, 0xa8fb,
- 0xa8fd, 0xa8fd,
- 0xa900, 0xa92a,
- 0xa930, 0xa952,
- 0xa960, 0xa97c,
- 0xa980, 0xa9b2,
- 0xa9b4, 0xa9bf,
- 0xa9cf, 0xa9d9,
- 0xa9e0, 0xa9e4,
- 0xa9e6, 0xa9fe,
- 0xaa00, 0xaa36,
- 0xaa40, 0xaa4d,
- 0xaa50, 0xaa59,
- 0xaa60, 0xaa76,
- 0xaa7a, 0xaa7a,
- 0xaa7e, 0xaabe,
- 0xaac0, 0xaac0,
- 0xaac2, 0xaac2,
- 0xaadb, 0xaadd,
- 0xaae0, 0xaaef,
- 0xaaf2, 0xaaf5,
- 0xab01, 0xab06,
- 0xab09, 0xab0e,
- 0xab11, 0xab16,
- 0xab20, 0xab26,
- 0xab28, 0xab2e,
- 0xab30, 0xab5a,
- 0xab5c, 0xab65,
- 0xab70, 0xabea,
- 0xabf0, 0xabf9,
- 0xac00, 0xd7a3,
- 0xd7b0, 0xd7c6,
- 0xd7cb, 0xd7fb,
- 0xf900, 0xfa6d,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb28,
- 0xfb2a, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3d,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfb,
- 0xfe70, 0xfe74,
- 0xfe76, 0xfefc,
- 0xff10, 0xff19,
- 0xff21, 0xff3a,
- 0xff41, 0xff5a,
- 0xff66, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
- 0x10000, 0x1000b,
- 0x1000d, 0x10026,
- 0x10028, 0x1003a,
- 0x1003c, 0x1003d,
- 0x1003f, 0x1004d,
- 0x10050, 0x1005d,
- 0x10080, 0x100fa,
- 0x10140, 0x10174,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x10300, 0x1031f,
- 0x10330, 0x1034a,
- 0x10350, 0x1037a,
- 0x10380, 0x1039d,
- 0x103a0, 0x103c3,
- 0x103c8, 0x103cf,
- 0x103d1, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x10855,
- 0x10860, 0x10876,
- 0x10880, 0x1089e,
- 0x108e0, 0x108f2,
- 0x108f4, 0x108f5,
- 0x10900, 0x10915,
- 0x10920, 0x10939,
- 0x10980, 0x109b7,
- 0x109be, 0x109bf,
- 0x10a00, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a60, 0x10a7c,
- 0x10a80, 0x10a9c,
- 0x10ac0, 0x10ac7,
- 0x10ac9, 0x10ae4,
- 0x10b00, 0x10b35,
- 0x10b40, 0x10b55,
- 0x10b60, 0x10b72,
- 0x10b80, 0x10b91,
- 0x10c00, 0x10c48,
- 0x10c80, 0x10cb2,
- 0x10cc0, 0x10cf2,
- 0x11000, 0x11045,
- 0x11066, 0x1106f,
- 0x11082, 0x110b8,
- 0x110d0, 0x110e8,
- 0x110f0, 0x110f9,
- 0x11100, 0x11132,
- 0x11136, 0x1113f,
- 0x11150, 0x11172,
- 0x11176, 0x11176,
- 0x11180, 0x111bf,
- 0x111c1, 0x111c4,
- 0x111d0, 0x111da,
- 0x111dc, 0x111dc,
- 0x11200, 0x11211,
- 0x11213, 0x11234,
- 0x11237, 0x11237,
- 0x1123e, 0x1123e,
- 0x11280, 0x11286,
- 0x11288, 0x11288,
- 0x1128a, 0x1128d,
- 0x1128f, 0x1129d,
- 0x1129f, 0x112a8,
- 0x112b0, 0x112e8,
- 0x112f0, 0x112f9,
- 0x11300, 0x11303,
- 0x11305, 0x1130c,
- 0x1130f, 0x11310,
- 0x11313, 0x11328,
- 0x1132a, 0x11330,
- 0x11332, 0x11333,
- 0x11335, 0x11339,
- 0x1133d, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134c,
- 0x11350, 0x11350,
- 0x11357, 0x11357,
- 0x1135d, 0x11363,
- 0x11400, 0x11441,
- 0x11443, 0x11445,
- 0x11447, 0x1144a,
- 0x11450, 0x11459,
- 0x11480, 0x114c1,
- 0x114c4, 0x114c5,
- 0x114c7, 0x114c7,
- 0x114d0, 0x114d9,
- 0x11580, 0x115b5,
- 0x115b8, 0x115be,
- 0x115d8, 0x115dd,
- 0x11600, 0x1163e,
- 0x11640, 0x11640,
- 0x11644, 0x11644,
- 0x11650, 0x11659,
- 0x11680, 0x116b5,
- 0x116c0, 0x116c9,
- 0x11700, 0x11719,
- 0x1171d, 0x1172a,
- 0x11730, 0x11739,
- 0x118a0, 0x118e9,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x11c00, 0x11c08,
- 0x11c0a, 0x11c36,
- 0x11c38, 0x11c3e,
- 0x11c40, 0x11c40,
- 0x11c50, 0x11c59,
- 0x11c72, 0x11c8f,
- 0x11c92, 0x11ca7,
- 0x11ca9, 0x11cb6,
- 0x12000, 0x12399,
- 0x12400, 0x1246e,
- 0x12480, 0x12543,
- 0x13000, 0x1342e,
- 0x14400, 0x14646,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16a60, 0x16a69,
- 0x16ad0, 0x16aed,
- 0x16b00, 0x16b36,
- 0x16b40, 0x16b43,
- 0x16b50, 0x16b59,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f93, 0x16f9f,
- 0x16fe0, 0x16fe0,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 0x1bc9e, 0x1bc9e,
- 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, 0x1d6c0,
- 0x1d6c2, 0x1d6da,
- 0x1d6dc, 0x1d6fa,
- 0x1d6fc, 0x1d714,
- 0x1d716, 0x1d734,
- 0x1d736, 0x1d74e,
- 0x1d750, 0x1d76e,
- 0x1d770, 0x1d788,
- 0x1d78a, 0x1d7a8,
- 0x1d7aa, 0x1d7c2,
- 0x1d7c4, 0x1d7cb,
- 0x1d7ce, 0x1d7ff,
- 0x1e000, 0x1e006,
- 0x1e008, 0x1e018,
- 0x1e01b, 0x1e021,
- 0x1e023, 0x1e024,
- 0x1e026, 0x1e02a,
- 0x1e800, 0x1e8c4,
- 0x1e900, 0x1e943,
- 0x1e947, 0x1e947,
- 0x1e950, 0x1e959,
- 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,
- 0x1f130, 0x1f149,
- 0x1f150, 0x1f169,
- 0x1f170, 0x1f189,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
-}; /* CR_Alnum */
-
-/* 'ASCII': [[:ASCII:]] */
-static const OnigCodePoint CR_ASCII[] = {
- 1,
- 0x0000, 0x007f,
-}; /* CR_ASCII */
-
-#ifdef USE_UNICODE_PROPERTIES
-/* 'Any': - */
-static const OnigCodePoint CR_Any[] = {
- 1,
- 0x0000, 0x10ffff,
-}; /* CR_Any */
-
-/* 'Assigned': - */
-static const OnigCodePoint CR_Assigned[] = {
- 638,
- 0x0000, 0x0377,
- 0x037a, 0x037f,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x052f,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x058d, 0x058f,
- 0x0591, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0600, 0x061c,
- 0x061e, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x07b1,
- 0x07c0, 0x07fa,
- 0x0800, 0x082d,
- 0x0830, 0x083e,
- 0x0840, 0x085b,
- 0x085e, 0x085e,
- 0x08a0, 0x08b4,
- 0x08b6, 0x08bd,
- 0x08d4, 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, 0x09fb,
- 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, 0x0a75,
- 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, 0x0af9,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 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, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c78, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d01, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4f,
- 0x0d54, 0x0d63,
- 0x0d66, 0x0d7f,
- 0x0d82, 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,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 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, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 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, 0x1abe,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b7c,
- 0x1b80, 0x1bf3,
- 0x1bfc, 0x1c37,
- 0x1c3b, 0x1c49,
- 0x1c4d, 0x1c88,
- 0x1cc0, 0x1cc7,
- 0x1cd0, 0x1cf6,
- 0x1cf8, 0x1cf9,
- 0x1d00, 0x1df5,
- 0x1dfb, 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, 0x20be,
- 0x20d0, 0x20f0,
- 0x2100, 0x218b,
- 0x2190, 0x23fe,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x2b73,
- 0x2b76, 0x2b95,
- 0x2b98, 0x2bb9,
- 0x2bbd, 0x2bc8,
- 0x2bca, 0x2bd1,
- 0x2bec, 0x2bef,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 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, 0x2e44,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x3190, 0x31ba,
- 0x31c0, 0x31e3,
- 0x31f0, 0x321e,
- 0x3220, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fd5,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xa4d0, 0xa62b,
- 0xa640, 0xa6f7,
- 0xa700, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7f7, 0xa82b,
- 0xa830, 0xa839,
- 0xa840, 0xa877,
- 0xa880, 0xa8c5,
- 0xa8ce, 0xa8d9,
- 0xa8e0, 0xa8fd,
- 0xa900, 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, 0xab65,
- 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, 0xfbc1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfd,
- 0xfe00, 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, 0xfffd,
- 0x10000, 0x1000b,
- 0x1000d, 0x10026,
- 0x10028, 0x1003a,
- 0x1003c, 0x1003d,
- 0x1003f, 0x1004d,
- 0x10050, 0x1005d,
- 0x10080, 0x100fa,
- 0x10100, 0x10102,
- 0x10107, 0x10133,
- 0x10137, 0x1018e,
- 0x10190, 0x1019b,
- 0x101a0, 0x101a0,
- 0x101d0, 0x101fd,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x102e0, 0x102fb,
- 0x10300, 0x10323,
- 0x10330, 0x1034a,
- 0x10350, 0x1037a,
- 0x10380, 0x1039d,
- 0x1039f, 0x103c3,
- 0x103c8, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x1056f, 0x1056f,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 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, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a47,
- 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, 0x10cff,
- 0x10e60, 0x10e7e,
- 0x11000, 0x1104d,
- 0x11052, 0x1106f,
- 0x1107f, 0x110c1,
- 0x110d0, 0x110e8,
- 0x110f0, 0x110f9,
- 0x11100, 0x11134,
- 0x11136, 0x11143,
- 0x11150, 0x11176,
- 0x11180, 0x111cd,
- 0x111d0, 0x111df,
- 0x111e1, 0x111f4,
- 0x11200, 0x11211,
- 0x11213, 0x1123e,
- 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,
- 0x1133c, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134d,
- 0x11350, 0x11350,
- 0x11357, 0x11357,
- 0x1135d, 0x11363,
- 0x11366, 0x1136c,
- 0x11370, 0x11374,
- 0x11400, 0x11459,
- 0x1145b, 0x1145b,
- 0x1145d, 0x1145d,
- 0x11480, 0x114c7,
- 0x114d0, 0x114d9,
- 0x11580, 0x115b5,
- 0x115b8, 0x115dd,
- 0x11600, 0x11644,
- 0x11650, 0x11659,
- 0x11660, 0x1166c,
- 0x11680, 0x116b7,
- 0x116c0, 0x116c9,
- 0x11700, 0x11719,
- 0x1171d, 0x1172b,
- 0x11730, 0x1173f,
- 0x118a0, 0x118f2,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x11c00, 0x11c08,
- 0x11c0a, 0x11c36,
- 0x11c38, 0x11c45,
- 0x11c50, 0x11c6c,
- 0x11c70, 0x11c8f,
- 0x11c92, 0x11ca7,
- 0x11ca9, 0x11cb6,
- 0x12000, 0x12399,
- 0x12400, 0x1246e,
- 0x12470, 0x12474,
- 0x12480, 0x12543,
- 0x13000, 0x1342e,
- 0x14400, 0x14646,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16a60, 0x16a69,
- 0x16a6e, 0x16a6f,
- 0x16ad0, 0x16aed,
- 0x16af0, 0x16af5,
- 0x16b00, 0x16b45,
- 0x16b50, 0x16b59,
- 0x16b5b, 0x16b61,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f8f, 0x16f9f,
- 0x16fe0, 0x16fe0,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 0x1bc9c, 0x1bca3,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d1e8,
- 0x1d200, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d360, 0x1d371,
- 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,
- 0x1e000, 0x1e006,
- 0x1e008, 0x1e018,
- 0x1e01b, 0x1e021,
- 0x1e023, 0x1e024,
- 0x1e026, 0x1e02a,
- 0x1e800, 0x1e8c4,
- 0x1e8c7, 0x1e8d6,
- 0x1e900, 0x1e94a,
- 0x1e950, 0x1e959,
- 0x1e95e, 0x1e95f,
- 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, 0x1f10c,
- 0x1f110, 0x1f12e,
- 0x1f130, 0x1f16b,
- 0x1f170, 0x1f1ac,
- 0x1f1e6, 0x1f202,
- 0x1f210, 0x1f23b,
- 0x1f240, 0x1f248,
- 0x1f250, 0x1f251,
- 0x1f300, 0x1f6d2,
- 0x1f6e0, 0x1f6ec,
- 0x1f6f0, 0x1f6f6,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d4,
- 0x1f800, 0x1f80b,
- 0x1f810, 0x1f847,
- 0x1f850, 0x1f859,
- 0x1f860, 0x1f887,
- 0x1f890, 0x1f8ad,
- 0x1f910, 0x1f91e,
- 0x1f920, 0x1f927,
- 0x1f930, 0x1f930,
- 0x1f933, 0x1f93e,
- 0x1f940, 0x1f94b,
- 0x1f950, 0x1f95e,
- 0x1f980, 0x1f991,
- 0x1f9c0, 0x1f9c0,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
- 0xe0001, 0xe0001,
- 0xe0020, 0xe007f,
- 0xe0100, 0xe01ef,
- 0xf0000, 0xffffd,
- 0x100000, 0x10fffd,
-}; /* CR_Assigned */
-
-/* 'C': Major Category */
-static const OnigCodePoint CR_C[] = {
- 642,
- 0x0000, 0x001f,
- 0x007f, 0x009f,
- 0x00ad, 0x00ad,
- 0x0378, 0x0379,
- 0x0380, 0x0383,
- 0x038b, 0x038b,
- 0x038d, 0x038d,
- 0x03a2, 0x03a2,
- 0x0530, 0x0530,
- 0x0557, 0x0558,
- 0x0560, 0x0560,
- 0x0588, 0x0588,
- 0x058b, 0x058c,
- 0x0590, 0x0590,
- 0x05c8, 0x05cf,
- 0x05eb, 0x05ef,
- 0x05f5, 0x0605,
- 0x061c, 0x061d,
- 0x06dd, 0x06dd,
- 0x070e, 0x070f,
- 0x074b, 0x074c,
- 0x07b2, 0x07bf,
- 0x07fb, 0x07ff,
- 0x082e, 0x082f,
- 0x083f, 0x083f,
- 0x085c, 0x085d,
- 0x085f, 0x089f,
- 0x08b5, 0x08b5,
- 0x08be, 0x08d3,
- 0x08e2, 0x08e2,
- 0x0984, 0x0984,
- 0x098d, 0x098e,
- 0x0991, 0x0992,
- 0x09a9, 0x09a9,
- 0x09b1, 0x09b1,
- 0x09b3, 0x09b5,
- 0x09ba, 0x09bb,
- 0x09c5, 0x09c6,
- 0x09c9, 0x09ca,
- 0x09cf, 0x09d6,
- 0x09d8, 0x09db,
- 0x09de, 0x09de,
- 0x09e4, 0x09e5,
- 0x09fc, 0x0a00,
- 0x0a04, 0x0a04,
- 0x0a0b, 0x0a0e,
- 0x0a11, 0x0a12,
- 0x0a29, 0x0a29,
- 0x0a31, 0x0a31,
- 0x0a34, 0x0a34,
- 0x0a37, 0x0a37,
- 0x0a3a, 0x0a3b,
- 0x0a3d, 0x0a3d,
- 0x0a43, 0x0a46,
- 0x0a49, 0x0a4a,
- 0x0a4e, 0x0a50,
- 0x0a52, 0x0a58,
- 0x0a5d, 0x0a5d,
- 0x0a5f, 0x0a65,
- 0x0a76, 0x0a80,
- 0x0a84, 0x0a84,
- 0x0a8e, 0x0a8e,
- 0x0a92, 0x0a92,
- 0x0aa9, 0x0aa9,
- 0x0ab1, 0x0ab1,
- 0x0ab4, 0x0ab4,
- 0x0aba, 0x0abb,
- 0x0ac6, 0x0ac6,
- 0x0aca, 0x0aca,
- 0x0ace, 0x0acf,
- 0x0ad1, 0x0adf,
- 0x0ae4, 0x0ae5,
- 0x0af2, 0x0af8,
- 0x0afa, 0x0b00,
- 0x0b04, 0x0b04,
- 0x0b0d, 0x0b0e,
- 0x0b11, 0x0b12,
- 0x0b29, 0x0b29,
- 0x0b31, 0x0b31,
- 0x0b34, 0x0b34,
- 0x0b3a, 0x0b3b,
- 0x0b45, 0x0b46,
- 0x0b49, 0x0b4a,
- 0x0b4e, 0x0b55,
- 0x0b58, 0x0b5b,
- 0x0b5e, 0x0b5e,
- 0x0b64, 0x0b65,
- 0x0b78, 0x0b81,
- 0x0b84, 0x0b84,
- 0x0b8b, 0x0b8d,
- 0x0b91, 0x0b91,
- 0x0b96, 0x0b98,
- 0x0b9b, 0x0b9b,
- 0x0b9d, 0x0b9d,
- 0x0ba0, 0x0ba2,
- 0x0ba5, 0x0ba7,
- 0x0bab, 0x0bad,
- 0x0bba, 0x0bbd,
- 0x0bc3, 0x0bc5,
- 0x0bc9, 0x0bc9,
- 0x0bce, 0x0bcf,
- 0x0bd1, 0x0bd6,
- 0x0bd8, 0x0be5,
- 0x0bfb, 0x0bff,
- 0x0c04, 0x0c04,
- 0x0c0d, 0x0c0d,
- 0x0c11, 0x0c11,
- 0x0c29, 0x0c29,
- 0x0c3a, 0x0c3c,
- 0x0c45, 0x0c45,
- 0x0c49, 0x0c49,
- 0x0c4e, 0x0c54,
- 0x0c57, 0x0c57,
- 0x0c5b, 0x0c5f,
- 0x0c64, 0x0c65,
- 0x0c70, 0x0c77,
- 0x0c84, 0x0c84,
- 0x0c8d, 0x0c8d,
- 0x0c91, 0x0c91,
- 0x0ca9, 0x0ca9,
- 0x0cb4, 0x0cb4,
- 0x0cba, 0x0cbb,
- 0x0cc5, 0x0cc5,
- 0x0cc9, 0x0cc9,
- 0x0cce, 0x0cd4,
- 0x0cd7, 0x0cdd,
- 0x0cdf, 0x0cdf,
- 0x0ce4, 0x0ce5,
- 0x0cf0, 0x0cf0,
- 0x0cf3, 0x0d00,
- 0x0d04, 0x0d04,
- 0x0d0d, 0x0d0d,
- 0x0d11, 0x0d11,
- 0x0d3b, 0x0d3c,
- 0x0d45, 0x0d45,
- 0x0d49, 0x0d49,
- 0x0d50, 0x0d53,
- 0x0d64, 0x0d65,
- 0x0d80, 0x0d81,
- 0x0d84, 0x0d84,
- 0x0d97, 0x0d99,
- 0x0db2, 0x0db2,
- 0x0dbc, 0x0dbc,
- 0x0dbe, 0x0dbf,
- 0x0dc7, 0x0dc9,
- 0x0dcb, 0x0dce,
- 0x0dd5, 0x0dd5,
- 0x0dd7, 0x0dd7,
- 0x0de0, 0x0de5,
- 0x0df0, 0x0df1,
- 0x0df5, 0x0e00,
- 0x0e3b, 0x0e3e,
- 0x0e5c, 0x0e80,
- 0x0e83, 0x0e83,
- 0x0e85, 0x0e86,
- 0x0e89, 0x0e89,
- 0x0e8b, 0x0e8c,
- 0x0e8e, 0x0e93,
- 0x0e98, 0x0e98,
- 0x0ea0, 0x0ea0,
- 0x0ea4, 0x0ea4,
- 0x0ea6, 0x0ea6,
- 0x0ea8, 0x0ea9,
- 0x0eac, 0x0eac,
- 0x0eba, 0x0eba,
- 0x0ebe, 0x0ebf,
- 0x0ec5, 0x0ec5,
- 0x0ec7, 0x0ec7,
- 0x0ece, 0x0ecf,
- 0x0eda, 0x0edb,
- 0x0ee0, 0x0eff,
- 0x0f48, 0x0f48,
- 0x0f6d, 0x0f70,
- 0x0f98, 0x0f98,
- 0x0fbd, 0x0fbd,
- 0x0fcd, 0x0fcd,
- 0x0fdb, 0x0fff,
- 0x10c6, 0x10c6,
- 0x10c8, 0x10cc,
- 0x10ce, 0x10cf,
- 0x1249, 0x1249,
- 0x124e, 0x124f,
- 0x1257, 0x1257,
- 0x1259, 0x1259,
- 0x125e, 0x125f,
- 0x1289, 0x1289,
- 0x128e, 0x128f,
- 0x12b1, 0x12b1,
- 0x12b6, 0x12b7,
- 0x12bf, 0x12bf,
- 0x12c1, 0x12c1,
- 0x12c6, 0x12c7,
- 0x12d7, 0x12d7,
- 0x1311, 0x1311,
- 0x1316, 0x1317,
- 0x135b, 0x135c,
- 0x137d, 0x137f,
- 0x139a, 0x139f,
- 0x13f6, 0x13f7,
- 0x13fe, 0x13ff,
- 0x169d, 0x169f,
- 0x16f9, 0x16ff,
- 0x170d, 0x170d,
- 0x1715, 0x171f,
- 0x1737, 0x173f,
- 0x1754, 0x175f,
- 0x176d, 0x176d,
- 0x1771, 0x1771,
- 0x1774, 0x177f,
- 0x17de, 0x17df,
- 0x17ea, 0x17ef,
- 0x17fa, 0x17ff,
- 0x180e, 0x180f,
- 0x181a, 0x181f,
- 0x1878, 0x187f,
- 0x18ab, 0x18af,
- 0x18f6, 0x18ff,
- 0x191f, 0x191f,
- 0x192c, 0x192f,
- 0x193c, 0x193f,
- 0x1941, 0x1943,
- 0x196e, 0x196f,
- 0x1975, 0x197f,
- 0x19ac, 0x19af,
- 0x19ca, 0x19cf,
- 0x19db, 0x19dd,
- 0x1a1c, 0x1a1d,
- 0x1a5f, 0x1a5f,
- 0x1a7d, 0x1a7e,
- 0x1a8a, 0x1a8f,
- 0x1a9a, 0x1a9f,
- 0x1aae, 0x1aaf,
- 0x1abf, 0x1aff,
- 0x1b4c, 0x1b4f,
- 0x1b7d, 0x1b7f,
- 0x1bf4, 0x1bfb,
- 0x1c38, 0x1c3a,
- 0x1c4a, 0x1c4c,
- 0x1c89, 0x1cbf,
- 0x1cc8, 0x1ccf,
- 0x1cf7, 0x1cf7,
- 0x1cfa, 0x1cff,
- 0x1df6, 0x1dfa,
- 0x1f16, 0x1f17,
- 0x1f1e, 0x1f1f,
- 0x1f46, 0x1f47,
- 0x1f4e, 0x1f4f,
- 0x1f58, 0x1f58,
- 0x1f5a, 0x1f5a,
- 0x1f5c, 0x1f5c,
- 0x1f5e, 0x1f5e,
- 0x1f7e, 0x1f7f,
- 0x1fb5, 0x1fb5,
- 0x1fc5, 0x1fc5,
- 0x1fd4, 0x1fd5,
- 0x1fdc, 0x1fdc,
- 0x1ff0, 0x1ff1,
- 0x1ff5, 0x1ff5,
- 0x1fff, 0x1fff,
- 0x200b, 0x200f,
- 0x202a, 0x202e,
- 0x2060, 0x206f,
- 0x2072, 0x2073,
- 0x208f, 0x208f,
- 0x209d, 0x209f,
- 0x20bf, 0x20cf,
- 0x20f1, 0x20ff,
- 0x218c, 0x218f,
- 0x23ff, 0x23ff,
- 0x2427, 0x243f,
- 0x244b, 0x245f,
- 0x2b74, 0x2b75,
- 0x2b96, 0x2b97,
- 0x2bba, 0x2bbc,
- 0x2bc9, 0x2bc9,
- 0x2bd2, 0x2beb,
- 0x2bf0, 0x2bff,
- 0x2c2f, 0x2c2f,
- 0x2c5f, 0x2c5f,
- 0x2cf4, 0x2cf8,
- 0x2d26, 0x2d26,
- 0x2d28, 0x2d2c,
- 0x2d2e, 0x2d2f,
- 0x2d68, 0x2d6e,
- 0x2d71, 0x2d7e,
- 0x2d97, 0x2d9f,
- 0x2da7, 0x2da7,
- 0x2daf, 0x2daf,
- 0x2db7, 0x2db7,
- 0x2dbf, 0x2dbf,
- 0x2dc7, 0x2dc7,
- 0x2dcf, 0x2dcf,
- 0x2dd7, 0x2dd7,
- 0x2ddf, 0x2ddf,
- 0x2e45, 0x2e7f,
- 0x2e9a, 0x2e9a,
- 0x2ef4, 0x2eff,
- 0x2fd6, 0x2fef,
- 0x2ffc, 0x2fff,
- 0x3040, 0x3040,
- 0x3097, 0x3098,
- 0x3100, 0x3104,
- 0x312e, 0x3130,
- 0x318f, 0x318f,
- 0x31bb, 0x31bf,
- 0x31e4, 0x31ef,
- 0x321f, 0x321f,
- 0x32ff, 0x32ff,
- 0x4db6, 0x4dbf,
- 0x9fd6, 0x9fff,
- 0xa48d, 0xa48f,
- 0xa4c7, 0xa4cf,
- 0xa62c, 0xa63f,
- 0xa6f8, 0xa6ff,
- 0xa7af, 0xa7af,
- 0xa7b8, 0xa7f6,
- 0xa82c, 0xa82f,
- 0xa83a, 0xa83f,
- 0xa878, 0xa87f,
- 0xa8c6, 0xa8cd,
- 0xa8da, 0xa8df,
- 0xa8fe, 0xa8ff,
- 0xa954, 0xa95e,
- 0xa97d, 0xa97f,
- 0xa9ce, 0xa9ce,
- 0xa9da, 0xa9dd,
- 0xa9ff, 0xa9ff,
- 0xaa37, 0xaa3f,
- 0xaa4e, 0xaa4f,
- 0xaa5a, 0xaa5b,
- 0xaac3, 0xaada,
- 0xaaf7, 0xab00,
- 0xab07, 0xab08,
- 0xab0f, 0xab10,
- 0xab17, 0xab1f,
- 0xab27, 0xab27,
- 0xab2f, 0xab2f,
- 0xab66, 0xab6f,
- 0xabee, 0xabef,
- 0xabfa, 0xabff,
- 0xd7a4, 0xd7af,
- 0xd7c7, 0xd7ca,
- 0xd7fc, 0xf8ff,
- 0xfa6e, 0xfa6f,
- 0xfada, 0xfaff,
- 0xfb07, 0xfb12,
- 0xfb18, 0xfb1c,
- 0xfb37, 0xfb37,
- 0xfb3d, 0xfb3d,
- 0xfb3f, 0xfb3f,
- 0xfb42, 0xfb42,
- 0xfb45, 0xfb45,
- 0xfbc2, 0xfbd2,
- 0xfd40, 0xfd4f,
- 0xfd90, 0xfd91,
- 0xfdc8, 0xfdef,
- 0xfdfe, 0xfdff,
- 0xfe1a, 0xfe1f,
- 0xfe53, 0xfe53,
- 0xfe67, 0xfe67,
- 0xfe6c, 0xfe6f,
- 0xfe75, 0xfe75,
- 0xfefd, 0xff00,
- 0xffbf, 0xffc1,
- 0xffc8, 0xffc9,
- 0xffd0, 0xffd1,
- 0xffd8, 0xffd9,
- 0xffdd, 0xffdf,
- 0xffe7, 0xffe7,
- 0xffef, 0xfffb,
- 0xfffe, 0xffff,
- 0x1000c, 0x1000c,
- 0x10027, 0x10027,
- 0x1003b, 0x1003b,
- 0x1003e, 0x1003e,
- 0x1004e, 0x1004f,
- 0x1005e, 0x1007f,
- 0x100fb, 0x100ff,
- 0x10103, 0x10106,
- 0x10134, 0x10136,
- 0x1018f, 0x1018f,
- 0x1019c, 0x1019f,
- 0x101a1, 0x101cf,
- 0x101fe, 0x1027f,
- 0x1029d, 0x1029f,
- 0x102d1, 0x102df,
- 0x102fc, 0x102ff,
- 0x10324, 0x1032f,
- 0x1034b, 0x1034f,
- 0x1037b, 0x1037f,
- 0x1039e, 0x1039e,
- 0x103c4, 0x103c7,
- 0x103d6, 0x103ff,
- 0x1049e, 0x1049f,
- 0x104aa, 0x104af,
- 0x104d4, 0x104d7,
- 0x104fc, 0x104ff,
- 0x10528, 0x1052f,
- 0x10564, 0x1056e,
- 0x10570, 0x105ff,
- 0x10737, 0x1073f,
- 0x10756, 0x1075f,
- 0x10768, 0x107ff,
- 0x10806, 0x10807,
- 0x10809, 0x10809,
- 0x10836, 0x10836,
- 0x10839, 0x1083b,
- 0x1083d, 0x1083e,
- 0x10856, 0x10856,
- 0x1089f, 0x108a6,
- 0x108b0, 0x108df,
- 0x108f3, 0x108f3,
- 0x108f6, 0x108fa,
- 0x1091c, 0x1091e,
- 0x1093a, 0x1093e,
- 0x10940, 0x1097f,
- 0x109b8, 0x109bb,
- 0x109d0, 0x109d1,
- 0x10a04, 0x10a04,
- 0x10a07, 0x10a0b,
- 0x10a14, 0x10a14,
- 0x10a18, 0x10a18,
- 0x10a34, 0x10a37,
- 0x10a3b, 0x10a3e,
- 0x10a48, 0x10a4f,
- 0x10a59, 0x10a5f,
- 0x10aa0, 0x10abf,
- 0x10ae7, 0x10aea,
- 0x10af7, 0x10aff,
- 0x10b36, 0x10b38,
- 0x10b56, 0x10b57,
- 0x10b73, 0x10b77,
- 0x10b92, 0x10b98,
- 0x10b9d, 0x10ba8,
- 0x10bb0, 0x10bff,
- 0x10c49, 0x10c7f,
- 0x10cb3, 0x10cbf,
- 0x10cf3, 0x10cf9,
- 0x10d00, 0x10e5f,
- 0x10e7f, 0x10fff,
- 0x1104e, 0x11051,
- 0x11070, 0x1107e,
- 0x110bd, 0x110bd,
- 0x110c2, 0x110cf,
- 0x110e9, 0x110ef,
- 0x110fa, 0x110ff,
- 0x11135, 0x11135,
- 0x11144, 0x1114f,
- 0x11177, 0x1117f,
- 0x111ce, 0x111cf,
- 0x111e0, 0x111e0,
- 0x111f5, 0x111ff,
- 0x11212, 0x11212,
- 0x1123f, 0x1127f,
- 0x11287, 0x11287,
- 0x11289, 0x11289,
- 0x1128e, 0x1128e,
- 0x1129e, 0x1129e,
- 0x112aa, 0x112af,
- 0x112eb, 0x112ef,
- 0x112fa, 0x112ff,
- 0x11304, 0x11304,
- 0x1130d, 0x1130e,
- 0x11311, 0x11312,
- 0x11329, 0x11329,
- 0x11331, 0x11331,
- 0x11334, 0x11334,
- 0x1133a, 0x1133b,
- 0x11345, 0x11346,
- 0x11349, 0x1134a,
- 0x1134e, 0x1134f,
- 0x11351, 0x11356,
- 0x11358, 0x1135c,
- 0x11364, 0x11365,
- 0x1136d, 0x1136f,
- 0x11375, 0x113ff,
- 0x1145a, 0x1145a,
- 0x1145c, 0x1145c,
- 0x1145e, 0x1147f,
- 0x114c8, 0x114cf,
- 0x114da, 0x1157f,
- 0x115b6, 0x115b7,
- 0x115de, 0x115ff,
- 0x11645, 0x1164f,
- 0x1165a, 0x1165f,
- 0x1166d, 0x1167f,
- 0x116b8, 0x116bf,
- 0x116ca, 0x116ff,
- 0x1171a, 0x1171c,
- 0x1172c, 0x1172f,
- 0x11740, 0x1189f,
- 0x118f3, 0x118fe,
- 0x11900, 0x11abf,
- 0x11af9, 0x11bff,
- 0x11c09, 0x11c09,
- 0x11c37, 0x11c37,
- 0x11c46, 0x11c4f,
- 0x11c6d, 0x11c6f,
- 0x11c90, 0x11c91,
- 0x11ca8, 0x11ca8,
- 0x11cb7, 0x11fff,
- 0x1239a, 0x123ff,
- 0x1246f, 0x1246f,
- 0x12475, 0x1247f,
- 0x12544, 0x12fff,
- 0x1342f, 0x143ff,
- 0x14647, 0x167ff,
- 0x16a39, 0x16a3f,
- 0x16a5f, 0x16a5f,
- 0x16a6a, 0x16a6d,
- 0x16a70, 0x16acf,
- 0x16aee, 0x16aef,
- 0x16af6, 0x16aff,
- 0x16b46, 0x16b4f,
- 0x16b5a, 0x16b5a,
- 0x16b62, 0x16b62,
- 0x16b78, 0x16b7c,
- 0x16b90, 0x16eff,
- 0x16f45, 0x16f4f,
- 0x16f7f, 0x16f8e,
- 0x16fa0, 0x16fdf,
- 0x16fe1, 0x16fff,
- 0x187ed, 0x187ff,
- 0x18af3, 0x1afff,
- 0x1b002, 0x1bbff,
- 0x1bc6b, 0x1bc6f,
- 0x1bc7d, 0x1bc7f,
- 0x1bc89, 0x1bc8f,
- 0x1bc9a, 0x1bc9b,
- 0x1bca0, 0x1cfff,
- 0x1d0f6, 0x1d0ff,
- 0x1d127, 0x1d128,
- 0x1d173, 0x1d17a,
- 0x1d1e9, 0x1d1ff,
- 0x1d246, 0x1d2ff,
- 0x1d357, 0x1d35f,
- 0x1d372, 0x1d3ff,
- 0x1d455, 0x1d455,
- 0x1d49d, 0x1d49d,
- 0x1d4a0, 0x1d4a1,
- 0x1d4a3, 0x1d4a4,
- 0x1d4a7, 0x1d4a8,
- 0x1d4ad, 0x1d4ad,
- 0x1d4ba, 0x1d4ba,
- 0x1d4bc, 0x1d4bc,
- 0x1d4c4, 0x1d4c4,
- 0x1d506, 0x1d506,
- 0x1d50b, 0x1d50c,
- 0x1d515, 0x1d515,
- 0x1d51d, 0x1d51d,
- 0x1d53a, 0x1d53a,
- 0x1d53f, 0x1d53f,
- 0x1d545, 0x1d545,
- 0x1d547, 0x1d549,
- 0x1d551, 0x1d551,
- 0x1d6a6, 0x1d6a7,
- 0x1d7cc, 0x1d7cd,
- 0x1da8c, 0x1da9a,
- 0x1daa0, 0x1daa0,
- 0x1dab0, 0x1dfff,
- 0x1e007, 0x1e007,
- 0x1e019, 0x1e01a,
- 0x1e022, 0x1e022,
- 0x1e025, 0x1e025,
- 0x1e02b, 0x1e7ff,
- 0x1e8c5, 0x1e8c6,
- 0x1e8d7, 0x1e8ff,
- 0x1e94b, 0x1e94f,
- 0x1e95a, 0x1e95d,
- 0x1e960, 0x1edff,
- 0x1ee04, 0x1ee04,
- 0x1ee20, 0x1ee20,
- 0x1ee23, 0x1ee23,
- 0x1ee25, 0x1ee26,
- 0x1ee28, 0x1ee28,
- 0x1ee33, 0x1ee33,
- 0x1ee38, 0x1ee38,
- 0x1ee3a, 0x1ee3a,
- 0x1ee3c, 0x1ee41,
- 0x1ee43, 0x1ee46,
- 0x1ee48, 0x1ee48,
- 0x1ee4a, 0x1ee4a,
- 0x1ee4c, 0x1ee4c,
- 0x1ee50, 0x1ee50,
- 0x1ee53, 0x1ee53,
- 0x1ee55, 0x1ee56,
- 0x1ee58, 0x1ee58,
- 0x1ee5a, 0x1ee5a,
- 0x1ee5c, 0x1ee5c,
- 0x1ee5e, 0x1ee5e,
- 0x1ee60, 0x1ee60,
- 0x1ee63, 0x1ee63,
- 0x1ee65, 0x1ee66,
- 0x1ee6b, 0x1ee6b,
- 0x1ee73, 0x1ee73,
- 0x1ee78, 0x1ee78,
- 0x1ee7d, 0x1ee7d,
- 0x1ee7f, 0x1ee7f,
- 0x1ee8a, 0x1ee8a,
- 0x1ee9c, 0x1eea0,
- 0x1eea4, 0x1eea4,
- 0x1eeaa, 0x1eeaa,
- 0x1eebc, 0x1eeef,
- 0x1eef2, 0x1efff,
- 0x1f02c, 0x1f02f,
- 0x1f094, 0x1f09f,
- 0x1f0af, 0x1f0b0,
- 0x1f0c0, 0x1f0c0,
- 0x1f0d0, 0x1f0d0,
- 0x1f0f6, 0x1f0ff,
- 0x1f10d, 0x1f10f,
- 0x1f12f, 0x1f12f,
- 0x1f16c, 0x1f16f,
- 0x1f1ad, 0x1f1e5,
- 0x1f203, 0x1f20f,
- 0x1f23c, 0x1f23f,
- 0x1f249, 0x1f24f,
- 0x1f252, 0x1f2ff,
- 0x1f6d3, 0x1f6df,
- 0x1f6ed, 0x1f6ef,
- 0x1f6f7, 0x1f6ff,
- 0x1f774, 0x1f77f,
- 0x1f7d5, 0x1f7ff,
- 0x1f80c, 0x1f80f,
- 0x1f848, 0x1f84f,
- 0x1f85a, 0x1f85f,
- 0x1f888, 0x1f88f,
- 0x1f8ae, 0x1f90f,
- 0x1f91f, 0x1f91f,
- 0x1f928, 0x1f92f,
- 0x1f931, 0x1f932,
- 0x1f93f, 0x1f93f,
- 0x1f94c, 0x1f94f,
- 0x1f95f, 0x1f97f,
- 0x1f992, 0x1f9bf,
- 0x1f9c1, 0x1ffff,
- 0x2a6d7, 0x2a6ff,
- 0x2b735, 0x2b73f,
- 0x2b81e, 0x2b81f,
- 0x2cea2, 0x2f7ff,
- 0x2fa1e, 0xe00ff,
- 0xe01f0, 0x10ffff,
-}; /* CR_C */
-
-/* 'Cc': General Category */
-#define CR_Cc CR_Cntrl
-
-/* 'Cf': General Category */
-static const OnigCodePoint CR_Cf[] = {
- 18,
- 0x00ad, 0x00ad,
- 0x0600, 0x0605,
- 0x061c, 0x061c,
- 0x06dd, 0x06dd,
- 0x070f, 0x070f,
- 0x08e2, 0x08e2,
- 0x180e, 0x180e,
- 0x200b, 0x200f,
- 0x202a, 0x202e,
- 0x2060, 0x2064,
- 0x2066, 0x206f,
- 0xfeff, 0xfeff,
- 0xfff9, 0xfffb,
- 0x110bd, 0x110bd,
- 0x1bca0, 0x1bca3,
- 0x1d173, 0x1d17a,
- 0xe0001, 0xe0001,
- 0xe0020, 0xe007f,
-}; /* CR_Cf */
-
-/* 'Cn': General Category */
-static const OnigCodePoint CR_Cn[] = {
- 638,
- 0x0378, 0x0379,
- 0x0380, 0x0383,
- 0x038b, 0x038b,
- 0x038d, 0x038d,
- 0x03a2, 0x03a2,
- 0x0530, 0x0530,
- 0x0557, 0x0558,
- 0x0560, 0x0560,
- 0x0588, 0x0588,
- 0x058b, 0x058c,
- 0x0590, 0x0590,
- 0x05c8, 0x05cf,
- 0x05eb, 0x05ef,
- 0x05f5, 0x05ff,
- 0x061d, 0x061d,
- 0x070e, 0x070e,
- 0x074b, 0x074c,
- 0x07b2, 0x07bf,
- 0x07fb, 0x07ff,
- 0x082e, 0x082f,
- 0x083f, 0x083f,
- 0x085c, 0x085d,
- 0x085f, 0x089f,
- 0x08b5, 0x08b5,
- 0x08be, 0x08d3,
- 0x0984, 0x0984,
- 0x098d, 0x098e,
- 0x0991, 0x0992,
- 0x09a9, 0x09a9,
- 0x09b1, 0x09b1,
- 0x09b3, 0x09b5,
- 0x09ba, 0x09bb,
- 0x09c5, 0x09c6,
- 0x09c9, 0x09ca,
- 0x09cf, 0x09d6,
- 0x09d8, 0x09db,
- 0x09de, 0x09de,
- 0x09e4, 0x09e5,
- 0x09fc, 0x0a00,
- 0x0a04, 0x0a04,
- 0x0a0b, 0x0a0e,
- 0x0a11, 0x0a12,
- 0x0a29, 0x0a29,
- 0x0a31, 0x0a31,
- 0x0a34, 0x0a34,
- 0x0a37, 0x0a37,
- 0x0a3a, 0x0a3b,
- 0x0a3d, 0x0a3d,
- 0x0a43, 0x0a46,
- 0x0a49, 0x0a4a,
- 0x0a4e, 0x0a50,
- 0x0a52, 0x0a58,
- 0x0a5d, 0x0a5d,
- 0x0a5f, 0x0a65,
- 0x0a76, 0x0a80,
- 0x0a84, 0x0a84,
- 0x0a8e, 0x0a8e,
- 0x0a92, 0x0a92,
- 0x0aa9, 0x0aa9,
- 0x0ab1, 0x0ab1,
- 0x0ab4, 0x0ab4,
- 0x0aba, 0x0abb,
- 0x0ac6, 0x0ac6,
- 0x0aca, 0x0aca,
- 0x0ace, 0x0acf,
- 0x0ad1, 0x0adf,
- 0x0ae4, 0x0ae5,
- 0x0af2, 0x0af8,
- 0x0afa, 0x0b00,
- 0x0b04, 0x0b04,
- 0x0b0d, 0x0b0e,
- 0x0b11, 0x0b12,
- 0x0b29, 0x0b29,
- 0x0b31, 0x0b31,
- 0x0b34, 0x0b34,
- 0x0b3a, 0x0b3b,
- 0x0b45, 0x0b46,
- 0x0b49, 0x0b4a,
- 0x0b4e, 0x0b55,
- 0x0b58, 0x0b5b,
- 0x0b5e, 0x0b5e,
- 0x0b64, 0x0b65,
- 0x0b78, 0x0b81,
- 0x0b84, 0x0b84,
- 0x0b8b, 0x0b8d,
- 0x0b91, 0x0b91,
- 0x0b96, 0x0b98,
- 0x0b9b, 0x0b9b,
- 0x0b9d, 0x0b9d,
- 0x0ba0, 0x0ba2,
- 0x0ba5, 0x0ba7,
- 0x0bab, 0x0bad,
- 0x0bba, 0x0bbd,
- 0x0bc3, 0x0bc5,
- 0x0bc9, 0x0bc9,
- 0x0bce, 0x0bcf,
- 0x0bd1, 0x0bd6,
- 0x0bd8, 0x0be5,
- 0x0bfb, 0x0bff,
- 0x0c04, 0x0c04,
- 0x0c0d, 0x0c0d,
- 0x0c11, 0x0c11,
- 0x0c29, 0x0c29,
- 0x0c3a, 0x0c3c,
- 0x0c45, 0x0c45,
- 0x0c49, 0x0c49,
- 0x0c4e, 0x0c54,
- 0x0c57, 0x0c57,
- 0x0c5b, 0x0c5f,
- 0x0c64, 0x0c65,
- 0x0c70, 0x0c77,
- 0x0c84, 0x0c84,
- 0x0c8d, 0x0c8d,
- 0x0c91, 0x0c91,
- 0x0ca9, 0x0ca9,
- 0x0cb4, 0x0cb4,
- 0x0cba, 0x0cbb,
- 0x0cc5, 0x0cc5,
- 0x0cc9, 0x0cc9,
- 0x0cce, 0x0cd4,
- 0x0cd7, 0x0cdd,
- 0x0cdf, 0x0cdf,
- 0x0ce4, 0x0ce5,
- 0x0cf0, 0x0cf0,
- 0x0cf3, 0x0d00,
- 0x0d04, 0x0d04,
- 0x0d0d, 0x0d0d,
- 0x0d11, 0x0d11,
- 0x0d3b, 0x0d3c,
- 0x0d45, 0x0d45,
- 0x0d49, 0x0d49,
- 0x0d50, 0x0d53,
- 0x0d64, 0x0d65,
- 0x0d80, 0x0d81,
- 0x0d84, 0x0d84,
- 0x0d97, 0x0d99,
- 0x0db2, 0x0db2,
- 0x0dbc, 0x0dbc,
- 0x0dbe, 0x0dbf,
- 0x0dc7, 0x0dc9,
- 0x0dcb, 0x0dce,
- 0x0dd5, 0x0dd5,
- 0x0dd7, 0x0dd7,
- 0x0de0, 0x0de5,
- 0x0df0, 0x0df1,
- 0x0df5, 0x0e00,
- 0x0e3b, 0x0e3e,
- 0x0e5c, 0x0e80,
- 0x0e83, 0x0e83,
- 0x0e85, 0x0e86,
- 0x0e89, 0x0e89,
- 0x0e8b, 0x0e8c,
- 0x0e8e, 0x0e93,
- 0x0e98, 0x0e98,
- 0x0ea0, 0x0ea0,
- 0x0ea4, 0x0ea4,
- 0x0ea6, 0x0ea6,
- 0x0ea8, 0x0ea9,
- 0x0eac, 0x0eac,
- 0x0eba, 0x0eba,
- 0x0ebe, 0x0ebf,
- 0x0ec5, 0x0ec5,
- 0x0ec7, 0x0ec7,
- 0x0ece, 0x0ecf,
- 0x0eda, 0x0edb,
- 0x0ee0, 0x0eff,
- 0x0f48, 0x0f48,
- 0x0f6d, 0x0f70,
- 0x0f98, 0x0f98,
- 0x0fbd, 0x0fbd,
- 0x0fcd, 0x0fcd,
- 0x0fdb, 0x0fff,
- 0x10c6, 0x10c6,
- 0x10c8, 0x10cc,
- 0x10ce, 0x10cf,
- 0x1249, 0x1249,
- 0x124e, 0x124f,
- 0x1257, 0x1257,
- 0x1259, 0x1259,
- 0x125e, 0x125f,
- 0x1289, 0x1289,
- 0x128e, 0x128f,
- 0x12b1, 0x12b1,
- 0x12b6, 0x12b7,
- 0x12bf, 0x12bf,
- 0x12c1, 0x12c1,
- 0x12c6, 0x12c7,
- 0x12d7, 0x12d7,
- 0x1311, 0x1311,
- 0x1316, 0x1317,
- 0x135b, 0x135c,
- 0x137d, 0x137f,
- 0x139a, 0x139f,
- 0x13f6, 0x13f7,
- 0x13fe, 0x13ff,
- 0x169d, 0x169f,
- 0x16f9, 0x16ff,
- 0x170d, 0x170d,
- 0x1715, 0x171f,
- 0x1737, 0x173f,
- 0x1754, 0x175f,
- 0x176d, 0x176d,
- 0x1771, 0x1771,
- 0x1774, 0x177f,
- 0x17de, 0x17df,
- 0x17ea, 0x17ef,
- 0x17fa, 0x17ff,
- 0x180f, 0x180f,
- 0x181a, 0x181f,
- 0x1878, 0x187f,
- 0x18ab, 0x18af,
- 0x18f6, 0x18ff,
- 0x191f, 0x191f,
- 0x192c, 0x192f,
- 0x193c, 0x193f,
- 0x1941, 0x1943,
- 0x196e, 0x196f,
- 0x1975, 0x197f,
- 0x19ac, 0x19af,
- 0x19ca, 0x19cf,
- 0x19db, 0x19dd,
- 0x1a1c, 0x1a1d,
- 0x1a5f, 0x1a5f,
- 0x1a7d, 0x1a7e,
- 0x1a8a, 0x1a8f,
- 0x1a9a, 0x1a9f,
- 0x1aae, 0x1aaf,
- 0x1abf, 0x1aff,
- 0x1b4c, 0x1b4f,
- 0x1b7d, 0x1b7f,
- 0x1bf4, 0x1bfb,
- 0x1c38, 0x1c3a,
- 0x1c4a, 0x1c4c,
- 0x1c89, 0x1cbf,
- 0x1cc8, 0x1ccf,
- 0x1cf7, 0x1cf7,
- 0x1cfa, 0x1cff,
- 0x1df6, 0x1dfa,
- 0x1f16, 0x1f17,
- 0x1f1e, 0x1f1f,
- 0x1f46, 0x1f47,
- 0x1f4e, 0x1f4f,
- 0x1f58, 0x1f58,
- 0x1f5a, 0x1f5a,
- 0x1f5c, 0x1f5c,
- 0x1f5e, 0x1f5e,
- 0x1f7e, 0x1f7f,
- 0x1fb5, 0x1fb5,
- 0x1fc5, 0x1fc5,
- 0x1fd4, 0x1fd5,
- 0x1fdc, 0x1fdc,
- 0x1ff0, 0x1ff1,
- 0x1ff5, 0x1ff5,
- 0x1fff, 0x1fff,
- 0x2065, 0x2065,
- 0x2072, 0x2073,
- 0x208f, 0x208f,
- 0x209d, 0x209f,
- 0x20bf, 0x20cf,
- 0x20f1, 0x20ff,
- 0x218c, 0x218f,
- 0x23ff, 0x23ff,
- 0x2427, 0x243f,
- 0x244b, 0x245f,
- 0x2b74, 0x2b75,
- 0x2b96, 0x2b97,
- 0x2bba, 0x2bbc,
- 0x2bc9, 0x2bc9,
- 0x2bd2, 0x2beb,
- 0x2bf0, 0x2bff,
- 0x2c2f, 0x2c2f,
- 0x2c5f, 0x2c5f,
- 0x2cf4, 0x2cf8,
- 0x2d26, 0x2d26,
- 0x2d28, 0x2d2c,
- 0x2d2e, 0x2d2f,
- 0x2d68, 0x2d6e,
- 0x2d71, 0x2d7e,
- 0x2d97, 0x2d9f,
- 0x2da7, 0x2da7,
- 0x2daf, 0x2daf,
- 0x2db7, 0x2db7,
- 0x2dbf, 0x2dbf,
- 0x2dc7, 0x2dc7,
- 0x2dcf, 0x2dcf,
- 0x2dd7, 0x2dd7,
- 0x2ddf, 0x2ddf,
- 0x2e45, 0x2e7f,
- 0x2e9a, 0x2e9a,
- 0x2ef4, 0x2eff,
- 0x2fd6, 0x2fef,
- 0x2ffc, 0x2fff,
- 0x3040, 0x3040,
- 0x3097, 0x3098,
- 0x3100, 0x3104,
- 0x312e, 0x3130,
- 0x318f, 0x318f,
- 0x31bb, 0x31bf,
- 0x31e4, 0x31ef,
- 0x321f, 0x321f,
- 0x32ff, 0x32ff,
- 0x4db6, 0x4dbf,
- 0x9fd6, 0x9fff,
- 0xa48d, 0xa48f,
- 0xa4c7, 0xa4cf,
- 0xa62c, 0xa63f,
- 0xa6f8, 0xa6ff,
- 0xa7af, 0xa7af,
- 0xa7b8, 0xa7f6,
- 0xa82c, 0xa82f,
- 0xa83a, 0xa83f,
- 0xa878, 0xa87f,
- 0xa8c6, 0xa8cd,
- 0xa8da, 0xa8df,
- 0xa8fe, 0xa8ff,
- 0xa954, 0xa95e,
- 0xa97d, 0xa97f,
- 0xa9ce, 0xa9ce,
- 0xa9da, 0xa9dd,
- 0xa9ff, 0xa9ff,
- 0xaa37, 0xaa3f,
- 0xaa4e, 0xaa4f,
- 0xaa5a, 0xaa5b,
- 0xaac3, 0xaada,
- 0xaaf7, 0xab00,
- 0xab07, 0xab08,
- 0xab0f, 0xab10,
- 0xab17, 0xab1f,
- 0xab27, 0xab27,
- 0xab2f, 0xab2f,
- 0xab66, 0xab6f,
- 0xabee, 0xabef,
- 0xabfa, 0xabff,
- 0xd7a4, 0xd7af,
- 0xd7c7, 0xd7ca,
- 0xd7fc, 0xd7ff,
- 0xfa6e, 0xfa6f,
- 0xfada, 0xfaff,
- 0xfb07, 0xfb12,
- 0xfb18, 0xfb1c,
- 0xfb37, 0xfb37,
- 0xfb3d, 0xfb3d,
- 0xfb3f, 0xfb3f,
- 0xfb42, 0xfb42,
- 0xfb45, 0xfb45,
- 0xfbc2, 0xfbd2,
- 0xfd40, 0xfd4f,
- 0xfd90, 0xfd91,
- 0xfdc8, 0xfdef,
- 0xfdfe, 0xfdff,
- 0xfe1a, 0xfe1f,
- 0xfe53, 0xfe53,
- 0xfe67, 0xfe67,
- 0xfe6c, 0xfe6f,
- 0xfe75, 0xfe75,
- 0xfefd, 0xfefe,
- 0xff00, 0xff00,
- 0xffbf, 0xffc1,
- 0xffc8, 0xffc9,
- 0xffd0, 0xffd1,
- 0xffd8, 0xffd9,
- 0xffdd, 0xffdf,
- 0xffe7, 0xffe7,
- 0xffef, 0xfff8,
- 0xfffe, 0xffff,
- 0x1000c, 0x1000c,
- 0x10027, 0x10027,
- 0x1003b, 0x1003b,
- 0x1003e, 0x1003e,
- 0x1004e, 0x1004f,
- 0x1005e, 0x1007f,
- 0x100fb, 0x100ff,
- 0x10103, 0x10106,
- 0x10134, 0x10136,
- 0x1018f, 0x1018f,
- 0x1019c, 0x1019f,
- 0x101a1, 0x101cf,
- 0x101fe, 0x1027f,
- 0x1029d, 0x1029f,
- 0x102d1, 0x102df,
- 0x102fc, 0x102ff,
- 0x10324, 0x1032f,
- 0x1034b, 0x1034f,
- 0x1037b, 0x1037f,
- 0x1039e, 0x1039e,
- 0x103c4, 0x103c7,
- 0x103d6, 0x103ff,
- 0x1049e, 0x1049f,
- 0x104aa, 0x104af,
- 0x104d4, 0x104d7,
- 0x104fc, 0x104ff,
- 0x10528, 0x1052f,
- 0x10564, 0x1056e,
- 0x10570, 0x105ff,
- 0x10737, 0x1073f,
- 0x10756, 0x1075f,
- 0x10768, 0x107ff,
- 0x10806, 0x10807,
- 0x10809, 0x10809,
- 0x10836, 0x10836,
- 0x10839, 0x1083b,
- 0x1083d, 0x1083e,
- 0x10856, 0x10856,
- 0x1089f, 0x108a6,
- 0x108b0, 0x108df,
- 0x108f3, 0x108f3,
- 0x108f6, 0x108fa,
- 0x1091c, 0x1091e,
- 0x1093a, 0x1093e,
- 0x10940, 0x1097f,
- 0x109b8, 0x109bb,
- 0x109d0, 0x109d1,
- 0x10a04, 0x10a04,
- 0x10a07, 0x10a0b,
- 0x10a14, 0x10a14,
- 0x10a18, 0x10a18,
- 0x10a34, 0x10a37,
- 0x10a3b, 0x10a3e,
- 0x10a48, 0x10a4f,
- 0x10a59, 0x10a5f,
- 0x10aa0, 0x10abf,
- 0x10ae7, 0x10aea,
- 0x10af7, 0x10aff,
- 0x10b36, 0x10b38,
- 0x10b56, 0x10b57,
- 0x10b73, 0x10b77,
- 0x10b92, 0x10b98,
- 0x10b9d, 0x10ba8,
- 0x10bb0, 0x10bff,
- 0x10c49, 0x10c7f,
- 0x10cb3, 0x10cbf,
- 0x10cf3, 0x10cf9,
- 0x10d00, 0x10e5f,
- 0x10e7f, 0x10fff,
- 0x1104e, 0x11051,
- 0x11070, 0x1107e,
- 0x110c2, 0x110cf,
- 0x110e9, 0x110ef,
- 0x110fa, 0x110ff,
- 0x11135, 0x11135,
- 0x11144, 0x1114f,
- 0x11177, 0x1117f,
- 0x111ce, 0x111cf,
- 0x111e0, 0x111e0,
- 0x111f5, 0x111ff,
- 0x11212, 0x11212,
- 0x1123f, 0x1127f,
- 0x11287, 0x11287,
- 0x11289, 0x11289,
- 0x1128e, 0x1128e,
- 0x1129e, 0x1129e,
- 0x112aa, 0x112af,
- 0x112eb, 0x112ef,
- 0x112fa, 0x112ff,
- 0x11304, 0x11304,
- 0x1130d, 0x1130e,
- 0x11311, 0x11312,
- 0x11329, 0x11329,
- 0x11331, 0x11331,
- 0x11334, 0x11334,
- 0x1133a, 0x1133b,
- 0x11345, 0x11346,
- 0x11349, 0x1134a,
- 0x1134e, 0x1134f,
- 0x11351, 0x11356,
- 0x11358, 0x1135c,
- 0x11364, 0x11365,
- 0x1136d, 0x1136f,
- 0x11375, 0x113ff,
- 0x1145a, 0x1145a,
- 0x1145c, 0x1145c,
- 0x1145e, 0x1147f,
- 0x114c8, 0x114cf,
- 0x114da, 0x1157f,
- 0x115b6, 0x115b7,
- 0x115de, 0x115ff,
- 0x11645, 0x1164f,
- 0x1165a, 0x1165f,
- 0x1166d, 0x1167f,
- 0x116b8, 0x116bf,
- 0x116ca, 0x116ff,
- 0x1171a, 0x1171c,
- 0x1172c, 0x1172f,
- 0x11740, 0x1189f,
- 0x118f3, 0x118fe,
- 0x11900, 0x11abf,
- 0x11af9, 0x11bff,
- 0x11c09, 0x11c09,
- 0x11c37, 0x11c37,
- 0x11c46, 0x11c4f,
- 0x11c6d, 0x11c6f,
- 0x11c90, 0x11c91,
- 0x11ca8, 0x11ca8,
- 0x11cb7, 0x11fff,
- 0x1239a, 0x123ff,
- 0x1246f, 0x1246f,
- 0x12475, 0x1247f,
- 0x12544, 0x12fff,
- 0x1342f, 0x143ff,
- 0x14647, 0x167ff,
- 0x16a39, 0x16a3f,
- 0x16a5f, 0x16a5f,
- 0x16a6a, 0x16a6d,
- 0x16a70, 0x16acf,
- 0x16aee, 0x16aef,
- 0x16af6, 0x16aff,
- 0x16b46, 0x16b4f,
- 0x16b5a, 0x16b5a,
- 0x16b62, 0x16b62,
- 0x16b78, 0x16b7c,
- 0x16b90, 0x16eff,
- 0x16f45, 0x16f4f,
- 0x16f7f, 0x16f8e,
- 0x16fa0, 0x16fdf,
- 0x16fe1, 0x16fff,
- 0x187ed, 0x187ff,
- 0x18af3, 0x1afff,
- 0x1b002, 0x1bbff,
- 0x1bc6b, 0x1bc6f,
- 0x1bc7d, 0x1bc7f,
- 0x1bc89, 0x1bc8f,
- 0x1bc9a, 0x1bc9b,
- 0x1bca4, 0x1cfff,
- 0x1d0f6, 0x1d0ff,
- 0x1d127, 0x1d128,
- 0x1d1e9, 0x1d1ff,
- 0x1d246, 0x1d2ff,
- 0x1d357, 0x1d35f,
- 0x1d372, 0x1d3ff,
- 0x1d455, 0x1d455,
- 0x1d49d, 0x1d49d,
- 0x1d4a0, 0x1d4a1,
- 0x1d4a3, 0x1d4a4,
- 0x1d4a7, 0x1d4a8,
- 0x1d4ad, 0x1d4ad,
- 0x1d4ba, 0x1d4ba,
- 0x1d4bc, 0x1d4bc,
- 0x1d4c4, 0x1d4c4,
- 0x1d506, 0x1d506,
- 0x1d50b, 0x1d50c,
- 0x1d515, 0x1d515,
- 0x1d51d, 0x1d51d,
- 0x1d53a, 0x1d53a,
- 0x1d53f, 0x1d53f,
- 0x1d545, 0x1d545,
- 0x1d547, 0x1d549,
- 0x1d551, 0x1d551,
- 0x1d6a6, 0x1d6a7,
- 0x1d7cc, 0x1d7cd,
- 0x1da8c, 0x1da9a,
- 0x1daa0, 0x1daa0,
- 0x1dab0, 0x1dfff,
- 0x1e007, 0x1e007,
- 0x1e019, 0x1e01a,
- 0x1e022, 0x1e022,
- 0x1e025, 0x1e025,
- 0x1e02b, 0x1e7ff,
- 0x1e8c5, 0x1e8c6,
- 0x1e8d7, 0x1e8ff,
- 0x1e94b, 0x1e94f,
- 0x1e95a, 0x1e95d,
- 0x1e960, 0x1edff,
- 0x1ee04, 0x1ee04,
- 0x1ee20, 0x1ee20,
- 0x1ee23, 0x1ee23,
- 0x1ee25, 0x1ee26,
- 0x1ee28, 0x1ee28,
- 0x1ee33, 0x1ee33,
- 0x1ee38, 0x1ee38,
- 0x1ee3a, 0x1ee3a,
- 0x1ee3c, 0x1ee41,
- 0x1ee43, 0x1ee46,
- 0x1ee48, 0x1ee48,
- 0x1ee4a, 0x1ee4a,
- 0x1ee4c, 0x1ee4c,
- 0x1ee50, 0x1ee50,
- 0x1ee53, 0x1ee53,
- 0x1ee55, 0x1ee56,
- 0x1ee58, 0x1ee58,
- 0x1ee5a, 0x1ee5a,
- 0x1ee5c, 0x1ee5c,
- 0x1ee5e, 0x1ee5e,
- 0x1ee60, 0x1ee60,
- 0x1ee63, 0x1ee63,
- 0x1ee65, 0x1ee66,
- 0x1ee6b, 0x1ee6b,
- 0x1ee73, 0x1ee73,
- 0x1ee78, 0x1ee78,
- 0x1ee7d, 0x1ee7d,
- 0x1ee7f, 0x1ee7f,
- 0x1ee8a, 0x1ee8a,
- 0x1ee9c, 0x1eea0,
- 0x1eea4, 0x1eea4,
- 0x1eeaa, 0x1eeaa,
- 0x1eebc, 0x1eeef,
- 0x1eef2, 0x1efff,
- 0x1f02c, 0x1f02f,
- 0x1f094, 0x1f09f,
- 0x1f0af, 0x1f0b0,
- 0x1f0c0, 0x1f0c0,
- 0x1f0d0, 0x1f0d0,
- 0x1f0f6, 0x1f0ff,
- 0x1f10d, 0x1f10f,
- 0x1f12f, 0x1f12f,
- 0x1f16c, 0x1f16f,
- 0x1f1ad, 0x1f1e5,
- 0x1f203, 0x1f20f,
- 0x1f23c, 0x1f23f,
- 0x1f249, 0x1f24f,
- 0x1f252, 0x1f2ff,
- 0x1f6d3, 0x1f6df,
- 0x1f6ed, 0x1f6ef,
- 0x1f6f7, 0x1f6ff,
- 0x1f774, 0x1f77f,
- 0x1f7d5, 0x1f7ff,
- 0x1f80c, 0x1f80f,
- 0x1f848, 0x1f84f,
- 0x1f85a, 0x1f85f,
- 0x1f888, 0x1f88f,
- 0x1f8ae, 0x1f90f,
- 0x1f91f, 0x1f91f,
- 0x1f928, 0x1f92f,
- 0x1f931, 0x1f932,
- 0x1f93f, 0x1f93f,
- 0x1f94c, 0x1f94f,
- 0x1f95f, 0x1f97f,
- 0x1f992, 0x1f9bf,
- 0x1f9c1, 0x1ffff,
- 0x2a6d7, 0x2a6ff,
- 0x2b735, 0x2b73f,
- 0x2b81e, 0x2b81f,
- 0x2cea2, 0x2f7ff,
- 0x2fa1e, 0xe0000,
- 0xe0002, 0xe001f,
- 0xe0080, 0xe00ff,
- 0xe01f0, 0xeffff,
- 0xffffe, 0xfffff,
- 0x10fffe, 0x10ffff,
-}; /* CR_Cn */
-
-/* 'Co': General Category */
-static const OnigCodePoint CR_Co[] = {
- 3,
- 0xe000, 0xf8ff,
- 0xf0000, 0xffffd,
- 0x100000, 0x10fffd,
-}; /* CR_Co */
-
-/* 'Cs': General Category */
-static const OnigCodePoint CR_Cs[] = {
- 1,
- 0xd800, 0xdfff,
-}; /* CR_Cs */
-
-/* 'L': Major Category */
-static const OnigCodePoint CR_L[] = {
- 571,
- 0x0041, 0x005a,
- 0x0061, 0x007a,
- 0x00aa, 0x00aa,
- 0x00b5, 0x00b5,
- 0x00ba, 0x00ba,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00f6,
- 0x00f8, 0x02c1,
- 0x02c6, 0x02d1,
- 0x02e0, 0x02e4,
- 0x02ec, 0x02ec,
- 0x02ee, 0x02ee,
- 0x0370, 0x0374,
- 0x0376, 0x0377,
- 0x037a, 0x037d,
- 0x037f, 0x037f,
- 0x0386, 0x0386,
- 0x0388, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03f5,
- 0x03f7, 0x0481,
- 0x048a, 0x052f,
- 0x0531, 0x0556,
- 0x0559, 0x0559,
- 0x0561, 0x0587,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f2,
- 0x0620, 0x064a,
- 0x066e, 0x066f,
- 0x0671, 0x06d3,
- 0x06d5, 0x06d5,
- 0x06e5, 0x06e6,
- 0x06ee, 0x06ef,
- 0x06fa, 0x06fc,
- 0x06ff, 0x06ff,
- 0x0710, 0x0710,
- 0x0712, 0x072f,
- 0x074d, 0x07a5,
- 0x07b1, 0x07b1,
- 0x07ca, 0x07ea,
- 0x07f4, 0x07f5,
- 0x07fa, 0x07fa,
- 0x0800, 0x0815,
- 0x081a, 0x081a,
- 0x0824, 0x0824,
- 0x0828, 0x0828,
- 0x0840, 0x0858,
- 0x08a0, 0x08b4,
- 0x08b6, 0x08bd,
- 0x0904, 0x0939,
- 0x093d, 0x093d,
- 0x0950, 0x0950,
- 0x0958, 0x0961,
- 0x0971, 0x0980,
- 0x0985, 0x098c,
- 0x098f, 0x0990,
- 0x0993, 0x09a8,
- 0x09aa, 0x09b0,
- 0x09b2, 0x09b2,
- 0x09b6, 0x09b9,
- 0x09bd, 0x09bd,
- 0x09ce, 0x09ce,
- 0x09dc, 0x09dd,
- 0x09df, 0x09e1,
- 0x09f0, 0x09f1,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a72, 0x0a74,
- 0x0a85, 0x0a8d,
- 0x0a8f, 0x0a91,
- 0x0a93, 0x0aa8,
- 0x0aaa, 0x0ab0,
- 0x0ab2, 0x0ab3,
- 0x0ab5, 0x0ab9,
- 0x0abd, 0x0abd,
- 0x0ad0, 0x0ad0,
- 0x0ae0, 0x0ae1,
- 0x0af9, 0x0af9,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3d, 0x0b3d,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b61,
- 0x0b71, 0x0b71,
- 0x0b83, 0x0b83,
- 0x0b85, 0x0b8a,
- 0x0b8e, 0x0b90,
- 0x0b92, 0x0b95,
- 0x0b99, 0x0b9a,
- 0x0b9c, 0x0b9c,
- 0x0b9e, 0x0b9f,
- 0x0ba3, 0x0ba4,
- 0x0ba8, 0x0baa,
- 0x0bae, 0x0bb9,
- 0x0bd0, 0x0bd0,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c3d,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c61,
- 0x0c80, 0x0c80,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbd, 0x0cbd,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce1,
- 0x0cf1, 0x0cf2,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d3d,
- 0x0d4e, 0x0d4e,
- 0x0d54, 0x0d56,
- 0x0d5f, 0x0d61,
- 0x0d7a, 0x0d7f,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0e01, 0x0e30,
- 0x0e32, 0x0e33,
- 0x0e40, 0x0e46,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb0,
- 0x0eb2, 0x0eb3,
- 0x0ebd, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0edc, 0x0edf,
- 0x0f00, 0x0f00,
- 0x0f40, 0x0f47,
- 0x0f49, 0x0f6c,
- 0x0f88, 0x0f8c,
- 0x1000, 0x102a,
- 0x103f, 0x103f,
- 0x1050, 0x1055,
- 0x105a, 0x105d,
- 0x1061, 0x1061,
- 0x1065, 0x1066,
- 0x106e, 0x1070,
- 0x1075, 0x1081,
- 0x108e, 0x108e,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x10d0, 0x10fa,
- 0x10fc, 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,
- 0x1380, 0x138f,
- 0x13a0, 0x13f5,
- 0x13f8, 0x13fd,
- 0x1401, 0x166c,
- 0x166f, 0x167f,
- 0x1681, 0x169a,
- 0x16a0, 0x16ea,
- 0x16f1, 0x16f8,
- 0x1700, 0x170c,
- 0x170e, 0x1711,
- 0x1720, 0x1731,
- 0x1740, 0x1751,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1780, 0x17b3,
- 0x17d7, 0x17d7,
- 0x17dc, 0x17dc,
- 0x1820, 0x1877,
- 0x1880, 0x1884,
- 0x1887, 0x18a8,
- 0x18aa, 0x18aa,
- 0x18b0, 0x18f5,
- 0x1900, 0x191e,
- 0x1950, 0x196d,
- 0x1970, 0x1974,
- 0x1980, 0x19ab,
- 0x19b0, 0x19c9,
- 0x1a00, 0x1a16,
- 0x1a20, 0x1a54,
- 0x1aa7, 0x1aa7,
- 0x1b05, 0x1b33,
- 0x1b45, 0x1b4b,
- 0x1b83, 0x1ba0,
- 0x1bae, 0x1baf,
- 0x1bba, 0x1be5,
- 0x1c00, 0x1c23,
- 0x1c4d, 0x1c4f,
- 0x1c5a, 0x1c7d,
- 0x1c80, 0x1c88,
- 0x1ce9, 0x1cec,
- 0x1cee, 0x1cf1,
- 0x1cf5, 0x1cf6,
- 0x1d00, 0x1dbf,
- 0x1e00, 0x1f15,
- 0x1f18, 0x1f1d,
- 0x1f20, 0x1f45,
- 0x1f48, 0x1f4d,
- 0x1f50, 0x1f57,
- 0x1f59, 0x1f59,
- 0x1f5b, 0x1f5b,
- 0x1f5d, 0x1f5d,
- 0x1f5f, 0x1f7d,
- 0x1f80, 0x1fb4,
- 0x1fb6, 0x1fbc,
- 0x1fbe, 0x1fbe,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fcc,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fdb,
- 0x1fe0, 0x1fec,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ffc,
- 0x2071, 0x2071,
- 0x207f, 0x207f,
- 0x2090, 0x209c,
- 0x2102, 0x2102,
- 0x2107, 0x2107,
- 0x210a, 0x2113,
- 0x2115, 0x2115,
- 0x2119, 0x211d,
- 0x2124, 0x2124,
- 0x2126, 0x2126,
- 0x2128, 0x2128,
- 0x212a, 0x212d,
- 0x212f, 0x2139,
- 0x213c, 0x213f,
- 0x2145, 0x2149,
- 0x214e, 0x214e,
- 0x2183, 0x2184,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2ce4,
- 0x2ceb, 0x2cee,
- 0x2cf2, 0x2cf3,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0x2d30, 0x2d67,
- 0x2d6f, 0x2d6f,
- 0x2d80, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0x2e2f, 0x2e2f,
- 0x3005, 0x3006,
- 0x3031, 0x3035,
- 0x303b, 0x303c,
- 0x3041, 0x3096,
- 0x309d, 0x309f,
- 0x30a1, 0x30fa,
- 0x30fc, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x31a0, 0x31ba,
- 0x31f0, 0x31ff,
- 0x3400, 0x4db5,
- 0x4e00, 0x9fd5,
- 0xa000, 0xa48c,
- 0xa4d0, 0xa4fd,
- 0xa500, 0xa60c,
- 0xa610, 0xa61f,
- 0xa62a, 0xa62b,
- 0xa640, 0xa66e,
- 0xa67f, 0xa69d,
- 0xa6a0, 0xa6e5,
- 0xa717, 0xa71f,
- 0xa722, 0xa788,
- 0xa78b, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7f7, 0xa801,
- 0xa803, 0xa805,
- 0xa807, 0xa80a,
- 0xa80c, 0xa822,
- 0xa840, 0xa873,
- 0xa882, 0xa8b3,
- 0xa8f2, 0xa8f7,
- 0xa8fb, 0xa8fb,
- 0xa8fd, 0xa8fd,
- 0xa90a, 0xa925,
- 0xa930, 0xa946,
- 0xa960, 0xa97c,
- 0xa984, 0xa9b2,
- 0xa9cf, 0xa9cf,
- 0xa9e0, 0xa9e4,
- 0xa9e6, 0xa9ef,
- 0xa9fa, 0xa9fe,
- 0xaa00, 0xaa28,
- 0xaa40, 0xaa42,
- 0xaa44, 0xaa4b,
- 0xaa60, 0xaa76,
- 0xaa7a, 0xaa7a,
- 0xaa7e, 0xaaaf,
- 0xaab1, 0xaab1,
- 0xaab5, 0xaab6,
- 0xaab9, 0xaabd,
- 0xaac0, 0xaac0,
- 0xaac2, 0xaac2,
- 0xaadb, 0xaadd,
- 0xaae0, 0xaaea,
- 0xaaf2, 0xaaf4,
- 0xab01, 0xab06,
- 0xab09, 0xab0e,
- 0xab11, 0xab16,
- 0xab20, 0xab26,
- 0xab28, 0xab2e,
- 0xab30, 0xab5a,
- 0xab5c, 0xab65,
- 0xab70, 0xabe2,
- 0xac00, 0xd7a3,
- 0xd7b0, 0xd7c6,
- 0xd7cb, 0xd7fb,
- 0xf900, 0xfa6d,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb1d,
- 0xfb1f, 0xfb28,
- 0xfb2a, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3d,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfb,
- 0xfe70, 0xfe74,
- 0xfe76, 0xfefc,
- 0xff21, 0xff3a,
- 0xff41, 0xff5a,
- 0xff66, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
- 0x10000, 0x1000b,
- 0x1000d, 0x10026,
- 0x10028, 0x1003a,
- 0x1003c, 0x1003d,
- 0x1003f, 0x1004d,
- 0x10050, 0x1005d,
- 0x10080, 0x100fa,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x10300, 0x1031f,
- 0x10330, 0x10340,
- 0x10342, 0x10349,
- 0x10350, 0x10375,
- 0x10380, 0x1039d,
- 0x103a0, 0x103c3,
- 0x103c8, 0x103cf,
- 0x10400, 0x1049d,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x10855,
- 0x10860, 0x10876,
- 0x10880, 0x1089e,
- 0x108e0, 0x108f2,
- 0x108f4, 0x108f5,
- 0x10900, 0x10915,
- 0x10920, 0x10939,
- 0x10980, 0x109b7,
- 0x109be, 0x109bf,
- 0x10a00, 0x10a00,
- 0x10a10, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a60, 0x10a7c,
- 0x10a80, 0x10a9c,
- 0x10ac0, 0x10ac7,
- 0x10ac9, 0x10ae4,
- 0x10b00, 0x10b35,
- 0x10b40, 0x10b55,
- 0x10b60, 0x10b72,
- 0x10b80, 0x10b91,
- 0x10c00, 0x10c48,
- 0x10c80, 0x10cb2,
- 0x10cc0, 0x10cf2,
- 0x11003, 0x11037,
- 0x11083, 0x110af,
- 0x110d0, 0x110e8,
- 0x11103, 0x11126,
- 0x11150, 0x11172,
- 0x11176, 0x11176,
- 0x11183, 0x111b2,
- 0x111c1, 0x111c4,
- 0x111da, 0x111da,
- 0x111dc, 0x111dc,
- 0x11200, 0x11211,
- 0x11213, 0x1122b,
- 0x11280, 0x11286,
- 0x11288, 0x11288,
- 0x1128a, 0x1128d,
- 0x1128f, 0x1129d,
- 0x1129f, 0x112a8,
- 0x112b0, 0x112de,
- 0x11305, 0x1130c,
- 0x1130f, 0x11310,
- 0x11313, 0x11328,
- 0x1132a, 0x11330,
- 0x11332, 0x11333,
- 0x11335, 0x11339,
- 0x1133d, 0x1133d,
- 0x11350, 0x11350,
- 0x1135d, 0x11361,
- 0x11400, 0x11434,
- 0x11447, 0x1144a,
- 0x11480, 0x114af,
- 0x114c4, 0x114c5,
- 0x114c7, 0x114c7,
- 0x11580, 0x115ae,
- 0x115d8, 0x115db,
- 0x11600, 0x1162f,
- 0x11644, 0x11644,
- 0x11680, 0x116aa,
- 0x11700, 0x11719,
- 0x118a0, 0x118df,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x11c00, 0x11c08,
- 0x11c0a, 0x11c2e,
- 0x11c40, 0x11c40,
- 0x11c72, 0x11c8f,
- 0x12000, 0x12399,
- 0x12480, 0x12543,
- 0x13000, 0x1342e,
- 0x14400, 0x14646,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16ad0, 0x16aed,
- 0x16b00, 0x16b2f,
- 0x16b40, 0x16b43,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f50,
- 0x16f93, 0x16f9f,
- 0x16fe0, 0x16fe0,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 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, 0x1d6c0,
- 0x1d6c2, 0x1d6da,
- 0x1d6dc, 0x1d6fa,
- 0x1d6fc, 0x1d714,
- 0x1d716, 0x1d734,
- 0x1d736, 0x1d74e,
- 0x1d750, 0x1d76e,
- 0x1d770, 0x1d788,
- 0x1d78a, 0x1d7a8,
- 0x1d7aa, 0x1d7c2,
- 0x1d7c4, 0x1d7cb,
- 0x1e800, 0x1e8c4,
- 0x1e900, 0x1e943,
- 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,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
-}; /* CR_L */
-
-/* 'LC': General Category */
-static const OnigCodePoint CR_LC[] = {
- 126,
- 0x0041, 0x005a,
- 0x0061, 0x007a,
- 0x00b5, 0x00b5,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00f6,
- 0x00f8, 0x01ba,
- 0x01bc, 0x01bf,
- 0x01c4, 0x0293,
- 0x0295, 0x02af,
- 0x0370, 0x0373,
- 0x0376, 0x0377,
- 0x037b, 0x037d,
- 0x037f, 0x037f,
- 0x0386, 0x0386,
- 0x0388, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03f5,
- 0x03f7, 0x0481,
- 0x048a, 0x052f,
- 0x0531, 0x0556,
- 0x0561, 0x0587,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x13a0, 0x13f5,
- 0x13f8, 0x13fd,
- 0x1c80, 0x1c88,
- 0x1d00, 0x1d2b,
- 0x1d6b, 0x1d77,
- 0x1d79, 0x1d9a,
- 0x1e00, 0x1f15,
- 0x1f18, 0x1f1d,
- 0x1f20, 0x1f45,
- 0x1f48, 0x1f4d,
- 0x1f50, 0x1f57,
- 0x1f59, 0x1f59,
- 0x1f5b, 0x1f5b,
- 0x1f5d, 0x1f5d,
- 0x1f5f, 0x1f7d,
- 0x1f80, 0x1fb4,
- 0x1fb6, 0x1fbc,
- 0x1fbe, 0x1fbe,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fcc,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fdb,
- 0x1fe0, 0x1fec,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ffc,
- 0x2102, 0x2102,
- 0x2107, 0x2107,
- 0x210a, 0x2113,
- 0x2115, 0x2115,
- 0x2119, 0x211d,
- 0x2124, 0x2124,
- 0x2126, 0x2126,
- 0x2128, 0x2128,
- 0x212a, 0x212d,
- 0x212f, 0x2134,
- 0x2139, 0x2139,
- 0x213c, 0x213f,
- 0x2145, 0x2149,
- 0x214e, 0x214e,
- 0x2183, 0x2184,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2c7b,
- 0x2c7e, 0x2ce4,
- 0x2ceb, 0x2cee,
- 0x2cf2, 0x2cf3,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0xa640, 0xa66d,
- 0xa680, 0xa69b,
- 0xa722, 0xa76f,
- 0xa771, 0xa787,
- 0xa78b, 0xa78e,
- 0xa790, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7fa, 0xa7fa,
- 0xab30, 0xab5a,
- 0xab60, 0xab65,
- 0xab70, 0xabbf,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xff21, 0xff3a,
- 0xff41, 0xff5a,
- 0x10400, 0x1044f,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10c80, 0x10cb2,
- 0x10cc0, 0x10cf2,
- 0x118a0, 0x118df,
- 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, 0x1d6c0,
- 0x1d6c2, 0x1d6da,
- 0x1d6dc, 0x1d6fa,
- 0x1d6fc, 0x1d714,
- 0x1d716, 0x1d734,
- 0x1d736, 0x1d74e,
- 0x1d750, 0x1d76e,
- 0x1d770, 0x1d788,
- 0x1d78a, 0x1d7a8,
- 0x1d7aa, 0x1d7c2,
- 0x1d7c4, 0x1d7cb,
- 0x1e900, 0x1e943,
-}; /* CR_LC */
-
-/* 'Ll': General Category */
-static const OnigCodePoint CR_Ll[] = {
- 633,
- 0x0061, 0x007a,
- 0x00b5, 0x00b5,
- 0x00df, 0x00f6,
- 0x00f8, 0x00ff,
- 0x0101, 0x0101,
- 0x0103, 0x0103,
- 0x0105, 0x0105,
- 0x0107, 0x0107,
- 0x0109, 0x0109,
- 0x010b, 0x010b,
- 0x010d, 0x010d,
- 0x010f, 0x010f,
- 0x0111, 0x0111,
- 0x0113, 0x0113,
- 0x0115, 0x0115,
- 0x0117, 0x0117,
- 0x0119, 0x0119,
- 0x011b, 0x011b,
- 0x011d, 0x011d,
- 0x011f, 0x011f,
- 0x0121, 0x0121,
- 0x0123, 0x0123,
- 0x0125, 0x0125,
- 0x0127, 0x0127,
- 0x0129, 0x0129,
- 0x012b, 0x012b,
- 0x012d, 0x012d,
- 0x012f, 0x012f,
- 0x0131, 0x0131,
- 0x0133, 0x0133,
- 0x0135, 0x0135,
- 0x0137, 0x0138,
- 0x013a, 0x013a,
- 0x013c, 0x013c,
- 0x013e, 0x013e,
- 0x0140, 0x0140,
- 0x0142, 0x0142,
- 0x0144, 0x0144,
- 0x0146, 0x0146,
- 0x0148, 0x0149,
- 0x014b, 0x014b,
- 0x014d, 0x014d,
- 0x014f, 0x014f,
- 0x0151, 0x0151,
- 0x0153, 0x0153,
- 0x0155, 0x0155,
- 0x0157, 0x0157,
- 0x0159, 0x0159,
- 0x015b, 0x015b,
- 0x015d, 0x015d,
- 0x015f, 0x015f,
- 0x0161, 0x0161,
- 0x0163, 0x0163,
- 0x0165, 0x0165,
- 0x0167, 0x0167,
- 0x0169, 0x0169,
- 0x016b, 0x016b,
- 0x016d, 0x016d,
- 0x016f, 0x016f,
- 0x0171, 0x0171,
- 0x0173, 0x0173,
- 0x0175, 0x0175,
- 0x0177, 0x0177,
- 0x017a, 0x017a,
- 0x017c, 0x017c,
- 0x017e, 0x0180,
- 0x0183, 0x0183,
- 0x0185, 0x0185,
- 0x0188, 0x0188,
- 0x018c, 0x018d,
- 0x0192, 0x0192,
- 0x0195, 0x0195,
- 0x0199, 0x019b,
- 0x019e, 0x019e,
- 0x01a1, 0x01a1,
- 0x01a3, 0x01a3,
- 0x01a5, 0x01a5,
- 0x01a8, 0x01a8,
- 0x01aa, 0x01ab,
- 0x01ad, 0x01ad,
- 0x01b0, 0x01b0,
- 0x01b4, 0x01b4,
- 0x01b6, 0x01b6,
- 0x01b9, 0x01ba,
- 0x01bd, 0x01bf,
- 0x01c6, 0x01c6,
- 0x01c9, 0x01c9,
- 0x01cc, 0x01cc,
- 0x01ce, 0x01ce,
- 0x01d0, 0x01d0,
- 0x01d2, 0x01d2,
- 0x01d4, 0x01d4,
- 0x01d6, 0x01d6,
- 0x01d8, 0x01d8,
- 0x01da, 0x01da,
- 0x01dc, 0x01dd,
- 0x01df, 0x01df,
- 0x01e1, 0x01e1,
- 0x01e3, 0x01e3,
- 0x01e5, 0x01e5,
- 0x01e7, 0x01e7,
- 0x01e9, 0x01e9,
- 0x01eb, 0x01eb,
- 0x01ed, 0x01ed,
- 0x01ef, 0x01f0,
- 0x01f3, 0x01f3,
- 0x01f5, 0x01f5,
- 0x01f9, 0x01f9,
- 0x01fb, 0x01fb,
- 0x01fd, 0x01fd,
- 0x01ff, 0x01ff,
- 0x0201, 0x0201,
- 0x0203, 0x0203,
- 0x0205, 0x0205,
- 0x0207, 0x0207,
- 0x0209, 0x0209,
- 0x020b, 0x020b,
- 0x020d, 0x020d,
- 0x020f, 0x020f,
- 0x0211, 0x0211,
- 0x0213, 0x0213,
- 0x0215, 0x0215,
- 0x0217, 0x0217,
- 0x0219, 0x0219,
- 0x021b, 0x021b,
- 0x021d, 0x021d,
- 0x021f, 0x021f,
- 0x0221, 0x0221,
- 0x0223, 0x0223,
- 0x0225, 0x0225,
- 0x0227, 0x0227,
- 0x0229, 0x0229,
- 0x022b, 0x022b,
- 0x022d, 0x022d,
- 0x022f, 0x022f,
- 0x0231, 0x0231,
- 0x0233, 0x0239,
- 0x023c, 0x023c,
- 0x023f, 0x0240,
- 0x0242, 0x0242,
- 0x0247, 0x0247,
- 0x0249, 0x0249,
- 0x024b, 0x024b,
- 0x024d, 0x024d,
- 0x024f, 0x0293,
- 0x0295, 0x02af,
- 0x0371, 0x0371,
- 0x0373, 0x0373,
- 0x0377, 0x0377,
- 0x037b, 0x037d,
- 0x0390, 0x0390,
- 0x03ac, 0x03ce,
- 0x03d0, 0x03d1,
- 0x03d5, 0x03d7,
- 0x03d9, 0x03d9,
- 0x03db, 0x03db,
- 0x03dd, 0x03dd,
- 0x03df, 0x03df,
- 0x03e1, 0x03e1,
- 0x03e3, 0x03e3,
- 0x03e5, 0x03e5,
- 0x03e7, 0x03e7,
- 0x03e9, 0x03e9,
- 0x03eb, 0x03eb,
- 0x03ed, 0x03ed,
- 0x03ef, 0x03f3,
- 0x03f5, 0x03f5,
- 0x03f8, 0x03f8,
- 0x03fb, 0x03fc,
- 0x0430, 0x045f,
- 0x0461, 0x0461,
- 0x0463, 0x0463,
- 0x0465, 0x0465,
- 0x0467, 0x0467,
- 0x0469, 0x0469,
- 0x046b, 0x046b,
- 0x046d, 0x046d,
- 0x046f, 0x046f,
- 0x0471, 0x0471,
- 0x0473, 0x0473,
- 0x0475, 0x0475,
- 0x0477, 0x0477,
- 0x0479, 0x0479,
- 0x047b, 0x047b,
- 0x047d, 0x047d,
- 0x047f, 0x047f,
- 0x0481, 0x0481,
- 0x048b, 0x048b,
- 0x048d, 0x048d,
- 0x048f, 0x048f,
- 0x0491, 0x0491,
- 0x0493, 0x0493,
- 0x0495, 0x0495,
- 0x0497, 0x0497,
- 0x0499, 0x0499,
- 0x049b, 0x049b,
- 0x049d, 0x049d,
- 0x049f, 0x049f,
- 0x04a1, 0x04a1,
- 0x04a3, 0x04a3,
- 0x04a5, 0x04a5,
- 0x04a7, 0x04a7,
- 0x04a9, 0x04a9,
- 0x04ab, 0x04ab,
- 0x04ad, 0x04ad,
- 0x04af, 0x04af,
- 0x04b1, 0x04b1,
- 0x04b3, 0x04b3,
- 0x04b5, 0x04b5,
- 0x04b7, 0x04b7,
- 0x04b9, 0x04b9,
- 0x04bb, 0x04bb,
- 0x04bd, 0x04bd,
- 0x04bf, 0x04bf,
- 0x04c2, 0x04c2,
- 0x04c4, 0x04c4,
- 0x04c6, 0x04c6,
- 0x04c8, 0x04c8,
- 0x04ca, 0x04ca,
- 0x04cc, 0x04cc,
- 0x04ce, 0x04cf,
- 0x04d1, 0x04d1,
- 0x04d3, 0x04d3,
- 0x04d5, 0x04d5,
- 0x04d7, 0x04d7,
- 0x04d9, 0x04d9,
- 0x04db, 0x04db,
- 0x04dd, 0x04dd,
- 0x04df, 0x04df,
- 0x04e1, 0x04e1,
- 0x04e3, 0x04e3,
- 0x04e5, 0x04e5,
- 0x04e7, 0x04e7,
- 0x04e9, 0x04e9,
- 0x04eb, 0x04eb,
- 0x04ed, 0x04ed,
- 0x04ef, 0x04ef,
- 0x04f1, 0x04f1,
- 0x04f3, 0x04f3,
- 0x04f5, 0x04f5,
- 0x04f7, 0x04f7,
- 0x04f9, 0x04f9,
- 0x04fb, 0x04fb,
- 0x04fd, 0x04fd,
- 0x04ff, 0x04ff,
- 0x0501, 0x0501,
- 0x0503, 0x0503,
- 0x0505, 0x0505,
- 0x0507, 0x0507,
- 0x0509, 0x0509,
- 0x050b, 0x050b,
- 0x050d, 0x050d,
- 0x050f, 0x050f,
- 0x0511, 0x0511,
- 0x0513, 0x0513,
- 0x0515, 0x0515,
- 0x0517, 0x0517,
- 0x0519, 0x0519,
- 0x051b, 0x051b,
- 0x051d, 0x051d,
- 0x051f, 0x051f,
- 0x0521, 0x0521,
- 0x0523, 0x0523,
- 0x0525, 0x0525,
- 0x0527, 0x0527,
- 0x0529, 0x0529,
- 0x052b, 0x052b,
- 0x052d, 0x052d,
- 0x052f, 0x052f,
- 0x0561, 0x0587,
- 0x13f8, 0x13fd,
- 0x1c80, 0x1c88,
- 0x1d00, 0x1d2b,
- 0x1d6b, 0x1d77,
- 0x1d79, 0x1d9a,
- 0x1e01, 0x1e01,
- 0x1e03, 0x1e03,
- 0x1e05, 0x1e05,
- 0x1e07, 0x1e07,
- 0x1e09, 0x1e09,
- 0x1e0b, 0x1e0b,
- 0x1e0d, 0x1e0d,
- 0x1e0f, 0x1e0f,
- 0x1e11, 0x1e11,
- 0x1e13, 0x1e13,
- 0x1e15, 0x1e15,
- 0x1e17, 0x1e17,
- 0x1e19, 0x1e19,
- 0x1e1b, 0x1e1b,
- 0x1e1d, 0x1e1d,
- 0x1e1f, 0x1e1f,
- 0x1e21, 0x1e21,
- 0x1e23, 0x1e23,
- 0x1e25, 0x1e25,
- 0x1e27, 0x1e27,
- 0x1e29, 0x1e29,
- 0x1e2b, 0x1e2b,
- 0x1e2d, 0x1e2d,
- 0x1e2f, 0x1e2f,
- 0x1e31, 0x1e31,
- 0x1e33, 0x1e33,
- 0x1e35, 0x1e35,
- 0x1e37, 0x1e37,
- 0x1e39, 0x1e39,
- 0x1e3b, 0x1e3b,
- 0x1e3d, 0x1e3d,
- 0x1e3f, 0x1e3f,
- 0x1e41, 0x1e41,
- 0x1e43, 0x1e43,
- 0x1e45, 0x1e45,
- 0x1e47, 0x1e47,
- 0x1e49, 0x1e49,
- 0x1e4b, 0x1e4b,
- 0x1e4d, 0x1e4d,
- 0x1e4f, 0x1e4f,
- 0x1e51, 0x1e51,
- 0x1e53, 0x1e53,
- 0x1e55, 0x1e55,
- 0x1e57, 0x1e57,
- 0x1e59, 0x1e59,
- 0x1e5b, 0x1e5b,
- 0x1e5d, 0x1e5d,
- 0x1e5f, 0x1e5f,
- 0x1e61, 0x1e61,
- 0x1e63, 0x1e63,
- 0x1e65, 0x1e65,
- 0x1e67, 0x1e67,
- 0x1e69, 0x1e69,
- 0x1e6b, 0x1e6b,
- 0x1e6d, 0x1e6d,
- 0x1e6f, 0x1e6f,
- 0x1e71, 0x1e71,
- 0x1e73, 0x1e73,
- 0x1e75, 0x1e75,
- 0x1e77, 0x1e77,
- 0x1e79, 0x1e79,
- 0x1e7b, 0x1e7b,
- 0x1e7d, 0x1e7d,
- 0x1e7f, 0x1e7f,
- 0x1e81, 0x1e81,
- 0x1e83, 0x1e83,
- 0x1e85, 0x1e85,
- 0x1e87, 0x1e87,
- 0x1e89, 0x1e89,
- 0x1e8b, 0x1e8b,
- 0x1e8d, 0x1e8d,
- 0x1e8f, 0x1e8f,
- 0x1e91, 0x1e91,
- 0x1e93, 0x1e93,
- 0x1e95, 0x1e9d,
- 0x1e9f, 0x1e9f,
- 0x1ea1, 0x1ea1,
- 0x1ea3, 0x1ea3,
- 0x1ea5, 0x1ea5,
- 0x1ea7, 0x1ea7,
- 0x1ea9, 0x1ea9,
- 0x1eab, 0x1eab,
- 0x1ead, 0x1ead,
- 0x1eaf, 0x1eaf,
- 0x1eb1, 0x1eb1,
- 0x1eb3, 0x1eb3,
- 0x1eb5, 0x1eb5,
- 0x1eb7, 0x1eb7,
- 0x1eb9, 0x1eb9,
- 0x1ebb, 0x1ebb,
- 0x1ebd, 0x1ebd,
- 0x1ebf, 0x1ebf,
- 0x1ec1, 0x1ec1,
- 0x1ec3, 0x1ec3,
- 0x1ec5, 0x1ec5,
- 0x1ec7, 0x1ec7,
- 0x1ec9, 0x1ec9,
- 0x1ecb, 0x1ecb,
- 0x1ecd, 0x1ecd,
- 0x1ecf, 0x1ecf,
- 0x1ed1, 0x1ed1,
- 0x1ed3, 0x1ed3,
- 0x1ed5, 0x1ed5,
- 0x1ed7, 0x1ed7,
- 0x1ed9, 0x1ed9,
- 0x1edb, 0x1edb,
- 0x1edd, 0x1edd,
- 0x1edf, 0x1edf,
- 0x1ee1, 0x1ee1,
- 0x1ee3, 0x1ee3,
- 0x1ee5, 0x1ee5,
- 0x1ee7, 0x1ee7,
- 0x1ee9, 0x1ee9,
- 0x1eeb, 0x1eeb,
- 0x1eed, 0x1eed,
- 0x1eef, 0x1eef,
- 0x1ef1, 0x1ef1,
- 0x1ef3, 0x1ef3,
- 0x1ef5, 0x1ef5,
- 0x1ef7, 0x1ef7,
- 0x1ef9, 0x1ef9,
- 0x1efb, 0x1efb,
- 0x1efd, 0x1efd,
- 0x1eff, 0x1f07,
- 0x1f10, 0x1f15,
- 0x1f20, 0x1f27,
- 0x1f30, 0x1f37,
- 0x1f40, 0x1f45,
- 0x1f50, 0x1f57,
- 0x1f60, 0x1f67,
- 0x1f70, 0x1f7d,
- 0x1f80, 0x1f87,
- 0x1f90, 0x1f97,
- 0x1fa0, 0x1fa7,
- 0x1fb0, 0x1fb4,
- 0x1fb6, 0x1fb7,
- 0x1fbe, 0x1fbe,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fc7,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fd7,
- 0x1fe0, 0x1fe7,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ff7,
- 0x210a, 0x210a,
- 0x210e, 0x210f,
- 0x2113, 0x2113,
- 0x212f, 0x212f,
- 0x2134, 0x2134,
- 0x2139, 0x2139,
- 0x213c, 0x213d,
- 0x2146, 0x2149,
- 0x214e, 0x214e,
- 0x2184, 0x2184,
- 0x2c30, 0x2c5e,
- 0x2c61, 0x2c61,
- 0x2c65, 0x2c66,
- 0x2c68, 0x2c68,
- 0x2c6a, 0x2c6a,
- 0x2c6c, 0x2c6c,
- 0x2c71, 0x2c71,
- 0x2c73, 0x2c74,
- 0x2c76, 0x2c7b,
- 0x2c81, 0x2c81,
- 0x2c83, 0x2c83,
- 0x2c85, 0x2c85,
- 0x2c87, 0x2c87,
- 0x2c89, 0x2c89,
- 0x2c8b, 0x2c8b,
- 0x2c8d, 0x2c8d,
- 0x2c8f, 0x2c8f,
- 0x2c91, 0x2c91,
- 0x2c93, 0x2c93,
- 0x2c95, 0x2c95,
- 0x2c97, 0x2c97,
- 0x2c99, 0x2c99,
- 0x2c9b, 0x2c9b,
- 0x2c9d, 0x2c9d,
- 0x2c9f, 0x2c9f,
- 0x2ca1, 0x2ca1,
- 0x2ca3, 0x2ca3,
- 0x2ca5, 0x2ca5,
- 0x2ca7, 0x2ca7,
- 0x2ca9, 0x2ca9,
- 0x2cab, 0x2cab,
- 0x2cad, 0x2cad,
- 0x2caf, 0x2caf,
- 0x2cb1, 0x2cb1,
- 0x2cb3, 0x2cb3,
- 0x2cb5, 0x2cb5,
- 0x2cb7, 0x2cb7,
- 0x2cb9, 0x2cb9,
- 0x2cbb, 0x2cbb,
- 0x2cbd, 0x2cbd,
- 0x2cbf, 0x2cbf,
- 0x2cc1, 0x2cc1,
- 0x2cc3, 0x2cc3,
- 0x2cc5, 0x2cc5,
- 0x2cc7, 0x2cc7,
- 0x2cc9, 0x2cc9,
- 0x2ccb, 0x2ccb,
- 0x2ccd, 0x2ccd,
- 0x2ccf, 0x2ccf,
- 0x2cd1, 0x2cd1,
- 0x2cd3, 0x2cd3,
- 0x2cd5, 0x2cd5,
- 0x2cd7, 0x2cd7,
- 0x2cd9, 0x2cd9,
- 0x2cdb, 0x2cdb,
- 0x2cdd, 0x2cdd,
- 0x2cdf, 0x2cdf,
- 0x2ce1, 0x2ce1,
- 0x2ce3, 0x2ce4,
- 0x2cec, 0x2cec,
- 0x2cee, 0x2cee,
- 0x2cf3, 0x2cf3,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0xa641, 0xa641,
- 0xa643, 0xa643,
- 0xa645, 0xa645,
- 0xa647, 0xa647,
- 0xa649, 0xa649,
- 0xa64b, 0xa64b,
- 0xa64d, 0xa64d,
- 0xa64f, 0xa64f,
- 0xa651, 0xa651,
- 0xa653, 0xa653,
- 0xa655, 0xa655,
- 0xa657, 0xa657,
- 0xa659, 0xa659,
- 0xa65b, 0xa65b,
- 0xa65d, 0xa65d,
- 0xa65f, 0xa65f,
- 0xa661, 0xa661,
- 0xa663, 0xa663,
- 0xa665, 0xa665,
- 0xa667, 0xa667,
- 0xa669, 0xa669,
- 0xa66b, 0xa66b,
- 0xa66d, 0xa66d,
- 0xa681, 0xa681,
- 0xa683, 0xa683,
- 0xa685, 0xa685,
- 0xa687, 0xa687,
- 0xa689, 0xa689,
- 0xa68b, 0xa68b,
- 0xa68d, 0xa68d,
- 0xa68f, 0xa68f,
- 0xa691, 0xa691,
- 0xa693, 0xa693,
- 0xa695, 0xa695,
- 0xa697, 0xa697,
- 0xa699, 0xa699,
- 0xa69b, 0xa69b,
- 0xa723, 0xa723,
- 0xa725, 0xa725,
- 0xa727, 0xa727,
- 0xa729, 0xa729,
- 0xa72b, 0xa72b,
- 0xa72d, 0xa72d,
- 0xa72f, 0xa731,
- 0xa733, 0xa733,
- 0xa735, 0xa735,
- 0xa737, 0xa737,
- 0xa739, 0xa739,
- 0xa73b, 0xa73b,
- 0xa73d, 0xa73d,
- 0xa73f, 0xa73f,
- 0xa741, 0xa741,
- 0xa743, 0xa743,
- 0xa745, 0xa745,
- 0xa747, 0xa747,
- 0xa749, 0xa749,
- 0xa74b, 0xa74b,
- 0xa74d, 0xa74d,
- 0xa74f, 0xa74f,
- 0xa751, 0xa751,
- 0xa753, 0xa753,
- 0xa755, 0xa755,
- 0xa757, 0xa757,
- 0xa759, 0xa759,
- 0xa75b, 0xa75b,
- 0xa75d, 0xa75d,
- 0xa75f, 0xa75f,
- 0xa761, 0xa761,
- 0xa763, 0xa763,
- 0xa765, 0xa765,
- 0xa767, 0xa767,
- 0xa769, 0xa769,
- 0xa76b, 0xa76b,
- 0xa76d, 0xa76d,
- 0xa76f, 0xa76f,
- 0xa771, 0xa778,
- 0xa77a, 0xa77a,
- 0xa77c, 0xa77c,
- 0xa77f, 0xa77f,
- 0xa781, 0xa781,
- 0xa783, 0xa783,
- 0xa785, 0xa785,
- 0xa787, 0xa787,
- 0xa78c, 0xa78c,
- 0xa78e, 0xa78e,
- 0xa791, 0xa791,
- 0xa793, 0xa795,
- 0xa797, 0xa797,
- 0xa799, 0xa799,
- 0xa79b, 0xa79b,
- 0xa79d, 0xa79d,
- 0xa79f, 0xa79f,
- 0xa7a1, 0xa7a1,
- 0xa7a3, 0xa7a3,
- 0xa7a5, 0xa7a5,
- 0xa7a7, 0xa7a7,
- 0xa7a9, 0xa7a9,
- 0xa7b5, 0xa7b5,
- 0xa7b7, 0xa7b7,
- 0xa7fa, 0xa7fa,
- 0xab30, 0xab5a,
- 0xab60, 0xab65,
- 0xab70, 0xabbf,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xff41, 0xff5a,
- 0x10428, 0x1044f,
- 0x104d8, 0x104fb,
- 0x10cc0, 0x10cf2,
- 0x118c0, 0x118df,
- 0x1d41a, 0x1d433,
- 0x1d44e, 0x1d454,
- 0x1d456, 0x1d467,
- 0x1d482, 0x1d49b,
- 0x1d4b6, 0x1d4b9,
- 0x1d4bb, 0x1d4bb,
- 0x1d4bd, 0x1d4c3,
- 0x1d4c5, 0x1d4cf,
- 0x1d4ea, 0x1d503,
- 0x1d51e, 0x1d537,
- 0x1d552, 0x1d56b,
- 0x1d586, 0x1d59f,
- 0x1d5ba, 0x1d5d3,
- 0x1d5ee, 0x1d607,
- 0x1d622, 0x1d63b,
- 0x1d656, 0x1d66f,
- 0x1d68a, 0x1d6a5,
- 0x1d6c2, 0x1d6da,
- 0x1d6dc, 0x1d6e1,
- 0x1d6fc, 0x1d714,
- 0x1d716, 0x1d71b,
- 0x1d736, 0x1d74e,
- 0x1d750, 0x1d755,
- 0x1d770, 0x1d788,
- 0x1d78a, 0x1d78f,
- 0x1d7aa, 0x1d7c2,
- 0x1d7c4, 0x1d7c9,
- 0x1d7cb, 0x1d7cb,
- 0x1e922, 0x1e943,
-}; /* CR_Ll */
-
-/* 'Lm': General Category */
-static const OnigCodePoint CR_Lm[] = {
- 57,
- 0x02b0, 0x02c1,
- 0x02c6, 0x02d1,
- 0x02e0, 0x02e4,
- 0x02ec, 0x02ec,
- 0x02ee, 0x02ee,
- 0x0374, 0x0374,
- 0x037a, 0x037a,
- 0x0559, 0x0559,
- 0x0640, 0x0640,
- 0x06e5, 0x06e6,
- 0x07f4, 0x07f5,
- 0x07fa, 0x07fa,
- 0x081a, 0x081a,
- 0x0824, 0x0824,
- 0x0828, 0x0828,
- 0x0971, 0x0971,
- 0x0e46, 0x0e46,
- 0x0ec6, 0x0ec6,
- 0x10fc, 0x10fc,
- 0x17d7, 0x17d7,
- 0x1843, 0x1843,
- 0x1aa7, 0x1aa7,
- 0x1c78, 0x1c7d,
- 0x1d2c, 0x1d6a,
- 0x1d78, 0x1d78,
- 0x1d9b, 0x1dbf,
- 0x2071, 0x2071,
- 0x207f, 0x207f,
- 0x2090, 0x209c,
- 0x2c7c, 0x2c7d,
- 0x2d6f, 0x2d6f,
- 0x2e2f, 0x2e2f,
- 0x3005, 0x3005,
- 0x3031, 0x3035,
- 0x303b, 0x303b,
- 0x309d, 0x309e,
- 0x30fc, 0x30fe,
- 0xa015, 0xa015,
- 0xa4f8, 0xa4fd,
- 0xa60c, 0xa60c,
- 0xa67f, 0xa67f,
- 0xa69c, 0xa69d,
- 0xa717, 0xa71f,
- 0xa770, 0xa770,
- 0xa788, 0xa788,
- 0xa7f8, 0xa7f9,
- 0xa9cf, 0xa9cf,
- 0xa9e6, 0xa9e6,
- 0xaa70, 0xaa70,
- 0xaadd, 0xaadd,
- 0xaaf3, 0xaaf4,
- 0xab5c, 0xab5f,
- 0xff70, 0xff70,
- 0xff9e, 0xff9f,
- 0x16b40, 0x16b43,
- 0x16f93, 0x16f9f,
- 0x16fe0, 0x16fe0,
-}; /* CR_Lm */
-
-/* 'Lo': General Category */
-static const OnigCodePoint CR_Lo[] = {
- 445,
- 0x00aa, 0x00aa,
- 0x00ba, 0x00ba,
- 0x01bb, 0x01bb,
- 0x01c0, 0x01c3,
- 0x0294, 0x0294,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f2,
- 0x0620, 0x063f,
- 0x0641, 0x064a,
- 0x066e, 0x066f,
- 0x0671, 0x06d3,
- 0x06d5, 0x06d5,
- 0x06ee, 0x06ef,
- 0x06fa, 0x06fc,
- 0x06ff, 0x06ff,
- 0x0710, 0x0710,
- 0x0712, 0x072f,
- 0x074d, 0x07a5,
- 0x07b1, 0x07b1,
- 0x07ca, 0x07ea,
- 0x0800, 0x0815,
- 0x0840, 0x0858,
- 0x08a0, 0x08b4,
- 0x08b6, 0x08bd,
- 0x0904, 0x0939,
- 0x093d, 0x093d,
- 0x0950, 0x0950,
- 0x0958, 0x0961,
- 0x0972, 0x0980,
- 0x0985, 0x098c,
- 0x098f, 0x0990,
- 0x0993, 0x09a8,
- 0x09aa, 0x09b0,
- 0x09b2, 0x09b2,
- 0x09b6, 0x09b9,
- 0x09bd, 0x09bd,
- 0x09ce, 0x09ce,
- 0x09dc, 0x09dd,
- 0x09df, 0x09e1,
- 0x09f0, 0x09f1,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a72, 0x0a74,
- 0x0a85, 0x0a8d,
- 0x0a8f, 0x0a91,
- 0x0a93, 0x0aa8,
- 0x0aaa, 0x0ab0,
- 0x0ab2, 0x0ab3,
- 0x0ab5, 0x0ab9,
- 0x0abd, 0x0abd,
- 0x0ad0, 0x0ad0,
- 0x0ae0, 0x0ae1,
- 0x0af9, 0x0af9,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3d, 0x0b3d,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b61,
- 0x0b71, 0x0b71,
- 0x0b83, 0x0b83,
- 0x0b85, 0x0b8a,
- 0x0b8e, 0x0b90,
- 0x0b92, 0x0b95,
- 0x0b99, 0x0b9a,
- 0x0b9c, 0x0b9c,
- 0x0b9e, 0x0b9f,
- 0x0ba3, 0x0ba4,
- 0x0ba8, 0x0baa,
- 0x0bae, 0x0bb9,
- 0x0bd0, 0x0bd0,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c3d,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c61,
- 0x0c80, 0x0c80,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbd, 0x0cbd,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce1,
- 0x0cf1, 0x0cf2,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d3d,
- 0x0d4e, 0x0d4e,
- 0x0d54, 0x0d56,
- 0x0d5f, 0x0d61,
- 0x0d7a, 0x0d7f,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0e01, 0x0e30,
- 0x0e32, 0x0e33,
- 0x0e40, 0x0e45,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb0,
- 0x0eb2, 0x0eb3,
- 0x0ebd, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0edc, 0x0edf,
- 0x0f00, 0x0f00,
- 0x0f40, 0x0f47,
- 0x0f49, 0x0f6c,
- 0x0f88, 0x0f8c,
- 0x1000, 0x102a,
- 0x103f, 0x103f,
- 0x1050, 0x1055,
- 0x105a, 0x105d,
- 0x1061, 0x1061,
- 0x1065, 0x1066,
- 0x106e, 0x1070,
- 0x1075, 0x1081,
- 0x108e, 0x108e,
- 0x10d0, 0x10fa,
- 0x10fd, 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,
- 0x1380, 0x138f,
- 0x1401, 0x166c,
- 0x166f, 0x167f,
- 0x1681, 0x169a,
- 0x16a0, 0x16ea,
- 0x16f1, 0x16f8,
- 0x1700, 0x170c,
- 0x170e, 0x1711,
- 0x1720, 0x1731,
- 0x1740, 0x1751,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1780, 0x17b3,
- 0x17dc, 0x17dc,
- 0x1820, 0x1842,
- 0x1844, 0x1877,
- 0x1880, 0x1884,
- 0x1887, 0x18a8,
- 0x18aa, 0x18aa,
- 0x18b0, 0x18f5,
- 0x1900, 0x191e,
- 0x1950, 0x196d,
- 0x1970, 0x1974,
- 0x1980, 0x19ab,
- 0x19b0, 0x19c9,
- 0x1a00, 0x1a16,
- 0x1a20, 0x1a54,
- 0x1b05, 0x1b33,
- 0x1b45, 0x1b4b,
- 0x1b83, 0x1ba0,
- 0x1bae, 0x1baf,
- 0x1bba, 0x1be5,
- 0x1c00, 0x1c23,
- 0x1c4d, 0x1c4f,
- 0x1c5a, 0x1c77,
- 0x1ce9, 0x1cec,
- 0x1cee, 0x1cf1,
- 0x1cf5, 0x1cf6,
- 0x2135, 0x2138,
- 0x2d30, 0x2d67,
- 0x2d80, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0x3006, 0x3006,
- 0x303c, 0x303c,
- 0x3041, 0x3096,
- 0x309f, 0x309f,
- 0x30a1, 0x30fa,
- 0x30ff, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x31a0, 0x31ba,
- 0x31f0, 0x31ff,
- 0x3400, 0x4db5,
- 0x4e00, 0x9fd5,
- 0xa000, 0xa014,
- 0xa016, 0xa48c,
- 0xa4d0, 0xa4f7,
- 0xa500, 0xa60b,
- 0xa610, 0xa61f,
- 0xa62a, 0xa62b,
- 0xa66e, 0xa66e,
- 0xa6a0, 0xa6e5,
- 0xa78f, 0xa78f,
- 0xa7f7, 0xa7f7,
- 0xa7fb, 0xa801,
- 0xa803, 0xa805,
- 0xa807, 0xa80a,
- 0xa80c, 0xa822,
- 0xa840, 0xa873,
- 0xa882, 0xa8b3,
- 0xa8f2, 0xa8f7,
- 0xa8fb, 0xa8fb,
- 0xa8fd, 0xa8fd,
- 0xa90a, 0xa925,
- 0xa930, 0xa946,
- 0xa960, 0xa97c,
- 0xa984, 0xa9b2,
- 0xa9e0, 0xa9e4,
- 0xa9e7, 0xa9ef,
- 0xa9fa, 0xa9fe,
- 0xaa00, 0xaa28,
- 0xaa40, 0xaa42,
- 0xaa44, 0xaa4b,
- 0xaa60, 0xaa6f,
- 0xaa71, 0xaa76,
- 0xaa7a, 0xaa7a,
- 0xaa7e, 0xaaaf,
- 0xaab1, 0xaab1,
- 0xaab5, 0xaab6,
- 0xaab9, 0xaabd,
- 0xaac0, 0xaac0,
- 0xaac2, 0xaac2,
- 0xaadb, 0xaadc,
- 0xaae0, 0xaaea,
- 0xaaf2, 0xaaf2,
- 0xab01, 0xab06,
- 0xab09, 0xab0e,
- 0xab11, 0xab16,
- 0xab20, 0xab26,
- 0xab28, 0xab2e,
- 0xabc0, 0xabe2,
- 0xac00, 0xd7a3,
- 0xd7b0, 0xd7c6,
- 0xd7cb, 0xd7fb,
- 0xf900, 0xfa6d,
- 0xfa70, 0xfad9,
- 0xfb1d, 0xfb1d,
- 0xfb1f, 0xfb28,
- 0xfb2a, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3d,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfb,
- 0xfe70, 0xfe74,
- 0xfe76, 0xfefc,
- 0xff66, 0xff6f,
- 0xff71, 0xff9d,
- 0xffa0, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
- 0x10000, 0x1000b,
- 0x1000d, 0x10026,
- 0x10028, 0x1003a,
- 0x1003c, 0x1003d,
- 0x1003f, 0x1004d,
- 0x10050, 0x1005d,
- 0x10080, 0x100fa,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x10300, 0x1031f,
- 0x10330, 0x10340,
- 0x10342, 0x10349,
- 0x10350, 0x10375,
- 0x10380, 0x1039d,
- 0x103a0, 0x103c3,
- 0x103c8, 0x103cf,
- 0x10450, 0x1049d,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x10855,
- 0x10860, 0x10876,
- 0x10880, 0x1089e,
- 0x108e0, 0x108f2,
- 0x108f4, 0x108f5,
- 0x10900, 0x10915,
- 0x10920, 0x10939,
- 0x10980, 0x109b7,
- 0x109be, 0x109bf,
- 0x10a00, 0x10a00,
- 0x10a10, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a60, 0x10a7c,
- 0x10a80, 0x10a9c,
- 0x10ac0, 0x10ac7,
- 0x10ac9, 0x10ae4,
- 0x10b00, 0x10b35,
- 0x10b40, 0x10b55,
- 0x10b60, 0x10b72,
- 0x10b80, 0x10b91,
- 0x10c00, 0x10c48,
- 0x11003, 0x11037,
- 0x11083, 0x110af,
- 0x110d0, 0x110e8,
- 0x11103, 0x11126,
- 0x11150, 0x11172,
- 0x11176, 0x11176,
- 0x11183, 0x111b2,
- 0x111c1, 0x111c4,
- 0x111da, 0x111da,
- 0x111dc, 0x111dc,
- 0x11200, 0x11211,
- 0x11213, 0x1122b,
- 0x11280, 0x11286,
- 0x11288, 0x11288,
- 0x1128a, 0x1128d,
- 0x1128f, 0x1129d,
- 0x1129f, 0x112a8,
- 0x112b0, 0x112de,
- 0x11305, 0x1130c,
- 0x1130f, 0x11310,
- 0x11313, 0x11328,
- 0x1132a, 0x11330,
- 0x11332, 0x11333,
- 0x11335, 0x11339,
- 0x1133d, 0x1133d,
- 0x11350, 0x11350,
- 0x1135d, 0x11361,
- 0x11400, 0x11434,
- 0x11447, 0x1144a,
- 0x11480, 0x114af,
- 0x114c4, 0x114c5,
- 0x114c7, 0x114c7,
- 0x11580, 0x115ae,
- 0x115d8, 0x115db,
- 0x11600, 0x1162f,
- 0x11644, 0x11644,
- 0x11680, 0x116aa,
- 0x11700, 0x11719,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x11c00, 0x11c08,
- 0x11c0a, 0x11c2e,
- 0x11c40, 0x11c40,
- 0x11c72, 0x11c8f,
- 0x12000, 0x12399,
- 0x12480, 0x12543,
- 0x13000, 0x1342e,
- 0x14400, 0x14646,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16ad0, 0x16aed,
- 0x16b00, 0x16b2f,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f50,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 0x1e800, 0x1e8c4,
- 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,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
-}; /* CR_Lo */
-
-/* 'Lt': General Category */
-static const OnigCodePoint CR_Lt[] = {
- 10,
- 0x01c5, 0x01c5,
- 0x01c8, 0x01c8,
- 0x01cb, 0x01cb,
- 0x01f2, 0x01f2,
- 0x1f88, 0x1f8f,
- 0x1f98, 0x1f9f,
- 0x1fa8, 0x1faf,
- 0x1fbc, 0x1fbc,
- 0x1fcc, 0x1fcc,
- 0x1ffc, 0x1ffc,
-}; /* CR_Lt */
-
-/* 'Lu': General Category */
-static const OnigCodePoint CR_Lu[] = {
- 627,
- 0x0041, 0x005a,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00de,
- 0x0100, 0x0100,
- 0x0102, 0x0102,
- 0x0104, 0x0104,
- 0x0106, 0x0106,
- 0x0108, 0x0108,
- 0x010a, 0x010a,
- 0x010c, 0x010c,
- 0x010e, 0x010e,
- 0x0110, 0x0110,
- 0x0112, 0x0112,
- 0x0114, 0x0114,
- 0x0116, 0x0116,
- 0x0118, 0x0118,
- 0x011a, 0x011a,
- 0x011c, 0x011c,
- 0x011e, 0x011e,
- 0x0120, 0x0120,
- 0x0122, 0x0122,
- 0x0124, 0x0124,
- 0x0126, 0x0126,
- 0x0128, 0x0128,
- 0x012a, 0x012a,
- 0x012c, 0x012c,
- 0x012e, 0x012e,
- 0x0130, 0x0130,
- 0x0132, 0x0132,
- 0x0134, 0x0134,
- 0x0136, 0x0136,
- 0x0139, 0x0139,
- 0x013b, 0x013b,
- 0x013d, 0x013d,
- 0x013f, 0x013f,
- 0x0141, 0x0141,
- 0x0143, 0x0143,
- 0x0145, 0x0145,
- 0x0147, 0x0147,
- 0x014a, 0x014a,
- 0x014c, 0x014c,
- 0x014e, 0x014e,
- 0x0150, 0x0150,
- 0x0152, 0x0152,
- 0x0154, 0x0154,
- 0x0156, 0x0156,
- 0x0158, 0x0158,
- 0x015a, 0x015a,
- 0x015c, 0x015c,
- 0x015e, 0x015e,
- 0x0160, 0x0160,
- 0x0162, 0x0162,
- 0x0164, 0x0164,
- 0x0166, 0x0166,
- 0x0168, 0x0168,
- 0x016a, 0x016a,
- 0x016c, 0x016c,
- 0x016e, 0x016e,
- 0x0170, 0x0170,
- 0x0172, 0x0172,
- 0x0174, 0x0174,
- 0x0176, 0x0176,
- 0x0178, 0x0179,
- 0x017b, 0x017b,
- 0x017d, 0x017d,
- 0x0181, 0x0182,
- 0x0184, 0x0184,
- 0x0186, 0x0187,
- 0x0189, 0x018b,
- 0x018e, 0x0191,
- 0x0193, 0x0194,
- 0x0196, 0x0198,
- 0x019c, 0x019d,
- 0x019f, 0x01a0,
- 0x01a2, 0x01a2,
- 0x01a4, 0x01a4,
- 0x01a6, 0x01a7,
- 0x01a9, 0x01a9,
- 0x01ac, 0x01ac,
- 0x01ae, 0x01af,
- 0x01b1, 0x01b3,
- 0x01b5, 0x01b5,
- 0x01b7, 0x01b8,
- 0x01bc, 0x01bc,
- 0x01c4, 0x01c4,
- 0x01c7, 0x01c7,
- 0x01ca, 0x01ca,
- 0x01cd, 0x01cd,
- 0x01cf, 0x01cf,
- 0x01d1, 0x01d1,
- 0x01d3, 0x01d3,
- 0x01d5, 0x01d5,
- 0x01d7, 0x01d7,
- 0x01d9, 0x01d9,
- 0x01db, 0x01db,
- 0x01de, 0x01de,
- 0x01e0, 0x01e0,
- 0x01e2, 0x01e2,
- 0x01e4, 0x01e4,
- 0x01e6, 0x01e6,
- 0x01e8, 0x01e8,
- 0x01ea, 0x01ea,
- 0x01ec, 0x01ec,
- 0x01ee, 0x01ee,
- 0x01f1, 0x01f1,
- 0x01f4, 0x01f4,
- 0x01f6, 0x01f8,
- 0x01fa, 0x01fa,
- 0x01fc, 0x01fc,
- 0x01fe, 0x01fe,
- 0x0200, 0x0200,
- 0x0202, 0x0202,
- 0x0204, 0x0204,
- 0x0206, 0x0206,
- 0x0208, 0x0208,
- 0x020a, 0x020a,
- 0x020c, 0x020c,
- 0x020e, 0x020e,
- 0x0210, 0x0210,
- 0x0212, 0x0212,
- 0x0214, 0x0214,
- 0x0216, 0x0216,
- 0x0218, 0x0218,
- 0x021a, 0x021a,
- 0x021c, 0x021c,
- 0x021e, 0x021e,
- 0x0220, 0x0220,
- 0x0222, 0x0222,
- 0x0224, 0x0224,
- 0x0226, 0x0226,
- 0x0228, 0x0228,
- 0x022a, 0x022a,
- 0x022c, 0x022c,
- 0x022e, 0x022e,
- 0x0230, 0x0230,
- 0x0232, 0x0232,
- 0x023a, 0x023b,
- 0x023d, 0x023e,
- 0x0241, 0x0241,
- 0x0243, 0x0246,
- 0x0248, 0x0248,
- 0x024a, 0x024a,
- 0x024c, 0x024c,
- 0x024e, 0x024e,
- 0x0370, 0x0370,
- 0x0372, 0x0372,
- 0x0376, 0x0376,
- 0x037f, 0x037f,
- 0x0386, 0x0386,
- 0x0388, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x038f,
- 0x0391, 0x03a1,
- 0x03a3, 0x03ab,
- 0x03cf, 0x03cf,
- 0x03d2, 0x03d4,
- 0x03d8, 0x03d8,
- 0x03da, 0x03da,
- 0x03dc, 0x03dc,
- 0x03de, 0x03de,
- 0x03e0, 0x03e0,
- 0x03e2, 0x03e2,
- 0x03e4, 0x03e4,
- 0x03e6, 0x03e6,
- 0x03e8, 0x03e8,
- 0x03ea, 0x03ea,
- 0x03ec, 0x03ec,
- 0x03ee, 0x03ee,
- 0x03f4, 0x03f4,
- 0x03f7, 0x03f7,
- 0x03f9, 0x03fa,
- 0x03fd, 0x042f,
- 0x0460, 0x0460,
- 0x0462, 0x0462,
- 0x0464, 0x0464,
- 0x0466, 0x0466,
- 0x0468, 0x0468,
- 0x046a, 0x046a,
- 0x046c, 0x046c,
- 0x046e, 0x046e,
- 0x0470, 0x0470,
- 0x0472, 0x0472,
- 0x0474, 0x0474,
- 0x0476, 0x0476,
- 0x0478, 0x0478,
- 0x047a, 0x047a,
- 0x047c, 0x047c,
- 0x047e, 0x047e,
- 0x0480, 0x0480,
- 0x048a, 0x048a,
- 0x048c, 0x048c,
- 0x048e, 0x048e,
- 0x0490, 0x0490,
- 0x0492, 0x0492,
- 0x0494, 0x0494,
- 0x0496, 0x0496,
- 0x0498, 0x0498,
- 0x049a, 0x049a,
- 0x049c, 0x049c,
- 0x049e, 0x049e,
- 0x04a0, 0x04a0,
- 0x04a2, 0x04a2,
- 0x04a4, 0x04a4,
- 0x04a6, 0x04a6,
- 0x04a8, 0x04a8,
- 0x04aa, 0x04aa,
- 0x04ac, 0x04ac,
- 0x04ae, 0x04ae,
- 0x04b0, 0x04b0,
- 0x04b2, 0x04b2,
- 0x04b4, 0x04b4,
- 0x04b6, 0x04b6,
- 0x04b8, 0x04b8,
- 0x04ba, 0x04ba,
- 0x04bc, 0x04bc,
- 0x04be, 0x04be,
- 0x04c0, 0x04c1,
- 0x04c3, 0x04c3,
- 0x04c5, 0x04c5,
- 0x04c7, 0x04c7,
- 0x04c9, 0x04c9,
- 0x04cb, 0x04cb,
- 0x04cd, 0x04cd,
- 0x04d0, 0x04d0,
- 0x04d2, 0x04d2,
- 0x04d4, 0x04d4,
- 0x04d6, 0x04d6,
- 0x04d8, 0x04d8,
- 0x04da, 0x04da,
- 0x04dc, 0x04dc,
- 0x04de, 0x04de,
- 0x04e0, 0x04e0,
- 0x04e2, 0x04e2,
- 0x04e4, 0x04e4,
- 0x04e6, 0x04e6,
- 0x04e8, 0x04e8,
- 0x04ea, 0x04ea,
- 0x04ec, 0x04ec,
- 0x04ee, 0x04ee,
- 0x04f0, 0x04f0,
- 0x04f2, 0x04f2,
- 0x04f4, 0x04f4,
- 0x04f6, 0x04f6,
- 0x04f8, 0x04f8,
- 0x04fa, 0x04fa,
- 0x04fc, 0x04fc,
- 0x04fe, 0x04fe,
- 0x0500, 0x0500,
- 0x0502, 0x0502,
- 0x0504, 0x0504,
- 0x0506, 0x0506,
- 0x0508, 0x0508,
- 0x050a, 0x050a,
- 0x050c, 0x050c,
- 0x050e, 0x050e,
- 0x0510, 0x0510,
- 0x0512, 0x0512,
- 0x0514, 0x0514,
- 0x0516, 0x0516,
- 0x0518, 0x0518,
- 0x051a, 0x051a,
- 0x051c, 0x051c,
- 0x051e, 0x051e,
- 0x0520, 0x0520,
- 0x0522, 0x0522,
- 0x0524, 0x0524,
- 0x0526, 0x0526,
- 0x0528, 0x0528,
- 0x052a, 0x052a,
- 0x052c, 0x052c,
- 0x052e, 0x052e,
- 0x0531, 0x0556,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x13a0, 0x13f5,
- 0x1e00, 0x1e00,
- 0x1e02, 0x1e02,
- 0x1e04, 0x1e04,
- 0x1e06, 0x1e06,
- 0x1e08, 0x1e08,
- 0x1e0a, 0x1e0a,
- 0x1e0c, 0x1e0c,
- 0x1e0e, 0x1e0e,
- 0x1e10, 0x1e10,
- 0x1e12, 0x1e12,
- 0x1e14, 0x1e14,
- 0x1e16, 0x1e16,
- 0x1e18, 0x1e18,
- 0x1e1a, 0x1e1a,
- 0x1e1c, 0x1e1c,
- 0x1e1e, 0x1e1e,
- 0x1e20, 0x1e20,
- 0x1e22, 0x1e22,
- 0x1e24, 0x1e24,
- 0x1e26, 0x1e26,
- 0x1e28, 0x1e28,
- 0x1e2a, 0x1e2a,
- 0x1e2c, 0x1e2c,
- 0x1e2e, 0x1e2e,
- 0x1e30, 0x1e30,
- 0x1e32, 0x1e32,
- 0x1e34, 0x1e34,
- 0x1e36, 0x1e36,
- 0x1e38, 0x1e38,
- 0x1e3a, 0x1e3a,
- 0x1e3c, 0x1e3c,
- 0x1e3e, 0x1e3e,
- 0x1e40, 0x1e40,
- 0x1e42, 0x1e42,
- 0x1e44, 0x1e44,
- 0x1e46, 0x1e46,
- 0x1e48, 0x1e48,
- 0x1e4a, 0x1e4a,
- 0x1e4c, 0x1e4c,
- 0x1e4e, 0x1e4e,
- 0x1e50, 0x1e50,
- 0x1e52, 0x1e52,
- 0x1e54, 0x1e54,
- 0x1e56, 0x1e56,
- 0x1e58, 0x1e58,
- 0x1e5a, 0x1e5a,
- 0x1e5c, 0x1e5c,
- 0x1e5e, 0x1e5e,
- 0x1e60, 0x1e60,
- 0x1e62, 0x1e62,
- 0x1e64, 0x1e64,
- 0x1e66, 0x1e66,
- 0x1e68, 0x1e68,
- 0x1e6a, 0x1e6a,
- 0x1e6c, 0x1e6c,
- 0x1e6e, 0x1e6e,
- 0x1e70, 0x1e70,
- 0x1e72, 0x1e72,
- 0x1e74, 0x1e74,
- 0x1e76, 0x1e76,
- 0x1e78, 0x1e78,
- 0x1e7a, 0x1e7a,
- 0x1e7c, 0x1e7c,
- 0x1e7e, 0x1e7e,
- 0x1e80, 0x1e80,
- 0x1e82, 0x1e82,
- 0x1e84, 0x1e84,
- 0x1e86, 0x1e86,
- 0x1e88, 0x1e88,
- 0x1e8a, 0x1e8a,
- 0x1e8c, 0x1e8c,
- 0x1e8e, 0x1e8e,
- 0x1e90, 0x1e90,
- 0x1e92, 0x1e92,
- 0x1e94, 0x1e94,
- 0x1e9e, 0x1e9e,
- 0x1ea0, 0x1ea0,
- 0x1ea2, 0x1ea2,
- 0x1ea4, 0x1ea4,
- 0x1ea6, 0x1ea6,
- 0x1ea8, 0x1ea8,
- 0x1eaa, 0x1eaa,
- 0x1eac, 0x1eac,
- 0x1eae, 0x1eae,
- 0x1eb0, 0x1eb0,
- 0x1eb2, 0x1eb2,
- 0x1eb4, 0x1eb4,
- 0x1eb6, 0x1eb6,
- 0x1eb8, 0x1eb8,
- 0x1eba, 0x1eba,
- 0x1ebc, 0x1ebc,
- 0x1ebe, 0x1ebe,
- 0x1ec0, 0x1ec0,
- 0x1ec2, 0x1ec2,
- 0x1ec4, 0x1ec4,
- 0x1ec6, 0x1ec6,
- 0x1ec8, 0x1ec8,
- 0x1eca, 0x1eca,
- 0x1ecc, 0x1ecc,
- 0x1ece, 0x1ece,
- 0x1ed0, 0x1ed0,
- 0x1ed2, 0x1ed2,
- 0x1ed4, 0x1ed4,
- 0x1ed6, 0x1ed6,
- 0x1ed8, 0x1ed8,
- 0x1eda, 0x1eda,
- 0x1edc, 0x1edc,
- 0x1ede, 0x1ede,
- 0x1ee0, 0x1ee0,
- 0x1ee2, 0x1ee2,
- 0x1ee4, 0x1ee4,
- 0x1ee6, 0x1ee6,
- 0x1ee8, 0x1ee8,
- 0x1eea, 0x1eea,
- 0x1eec, 0x1eec,
- 0x1eee, 0x1eee,
- 0x1ef0, 0x1ef0,
- 0x1ef2, 0x1ef2,
- 0x1ef4, 0x1ef4,
- 0x1ef6, 0x1ef6,
- 0x1ef8, 0x1ef8,
- 0x1efa, 0x1efa,
- 0x1efc, 0x1efc,
- 0x1efe, 0x1efe,
- 0x1f08, 0x1f0f,
- 0x1f18, 0x1f1d,
- 0x1f28, 0x1f2f,
- 0x1f38, 0x1f3f,
- 0x1f48, 0x1f4d,
- 0x1f59, 0x1f59,
- 0x1f5b, 0x1f5b,
- 0x1f5d, 0x1f5d,
- 0x1f5f, 0x1f5f,
- 0x1f68, 0x1f6f,
- 0x1fb8, 0x1fbb,
- 0x1fc8, 0x1fcb,
- 0x1fd8, 0x1fdb,
- 0x1fe8, 0x1fec,
- 0x1ff8, 0x1ffb,
- 0x2102, 0x2102,
- 0x2107, 0x2107,
- 0x210b, 0x210d,
- 0x2110, 0x2112,
- 0x2115, 0x2115,
- 0x2119, 0x211d,
- 0x2124, 0x2124,
- 0x2126, 0x2126,
- 0x2128, 0x2128,
- 0x212a, 0x212d,
- 0x2130, 0x2133,
- 0x213e, 0x213f,
- 0x2145, 0x2145,
- 0x2183, 0x2183,
- 0x2c00, 0x2c2e,
- 0x2c60, 0x2c60,
- 0x2c62, 0x2c64,
- 0x2c67, 0x2c67,
- 0x2c69, 0x2c69,
- 0x2c6b, 0x2c6b,
- 0x2c6d, 0x2c70,
- 0x2c72, 0x2c72,
- 0x2c75, 0x2c75,
- 0x2c7e, 0x2c80,
- 0x2c82, 0x2c82,
- 0x2c84, 0x2c84,
- 0x2c86, 0x2c86,
- 0x2c88, 0x2c88,
- 0x2c8a, 0x2c8a,
- 0x2c8c, 0x2c8c,
- 0x2c8e, 0x2c8e,
- 0x2c90, 0x2c90,
- 0x2c92, 0x2c92,
- 0x2c94, 0x2c94,
- 0x2c96, 0x2c96,
- 0x2c98, 0x2c98,
- 0x2c9a, 0x2c9a,
- 0x2c9c, 0x2c9c,
- 0x2c9e, 0x2c9e,
- 0x2ca0, 0x2ca0,
- 0x2ca2, 0x2ca2,
- 0x2ca4, 0x2ca4,
- 0x2ca6, 0x2ca6,
- 0x2ca8, 0x2ca8,
- 0x2caa, 0x2caa,
- 0x2cac, 0x2cac,
- 0x2cae, 0x2cae,
- 0x2cb0, 0x2cb0,
- 0x2cb2, 0x2cb2,
- 0x2cb4, 0x2cb4,
- 0x2cb6, 0x2cb6,
- 0x2cb8, 0x2cb8,
- 0x2cba, 0x2cba,
- 0x2cbc, 0x2cbc,
- 0x2cbe, 0x2cbe,
- 0x2cc0, 0x2cc0,
- 0x2cc2, 0x2cc2,
- 0x2cc4, 0x2cc4,
- 0x2cc6, 0x2cc6,
- 0x2cc8, 0x2cc8,
- 0x2cca, 0x2cca,
- 0x2ccc, 0x2ccc,
- 0x2cce, 0x2cce,
- 0x2cd0, 0x2cd0,
- 0x2cd2, 0x2cd2,
- 0x2cd4, 0x2cd4,
- 0x2cd6, 0x2cd6,
- 0x2cd8, 0x2cd8,
- 0x2cda, 0x2cda,
- 0x2cdc, 0x2cdc,
- 0x2cde, 0x2cde,
- 0x2ce0, 0x2ce0,
- 0x2ce2, 0x2ce2,
- 0x2ceb, 0x2ceb,
- 0x2ced, 0x2ced,
- 0x2cf2, 0x2cf2,
- 0xa640, 0xa640,
- 0xa642, 0xa642,
- 0xa644, 0xa644,
- 0xa646, 0xa646,
- 0xa648, 0xa648,
- 0xa64a, 0xa64a,
- 0xa64c, 0xa64c,
- 0xa64e, 0xa64e,
- 0xa650, 0xa650,
- 0xa652, 0xa652,
- 0xa654, 0xa654,
- 0xa656, 0xa656,
- 0xa658, 0xa658,
- 0xa65a, 0xa65a,
- 0xa65c, 0xa65c,
- 0xa65e, 0xa65e,
- 0xa660, 0xa660,
- 0xa662, 0xa662,
- 0xa664, 0xa664,
- 0xa666, 0xa666,
- 0xa668, 0xa668,
- 0xa66a, 0xa66a,
- 0xa66c, 0xa66c,
- 0xa680, 0xa680,
- 0xa682, 0xa682,
- 0xa684, 0xa684,
- 0xa686, 0xa686,
- 0xa688, 0xa688,
- 0xa68a, 0xa68a,
- 0xa68c, 0xa68c,
- 0xa68e, 0xa68e,
- 0xa690, 0xa690,
- 0xa692, 0xa692,
- 0xa694, 0xa694,
- 0xa696, 0xa696,
- 0xa698, 0xa698,
- 0xa69a, 0xa69a,
- 0xa722, 0xa722,
- 0xa724, 0xa724,
- 0xa726, 0xa726,
- 0xa728, 0xa728,
- 0xa72a, 0xa72a,
- 0xa72c, 0xa72c,
- 0xa72e, 0xa72e,
- 0xa732, 0xa732,
- 0xa734, 0xa734,
- 0xa736, 0xa736,
- 0xa738, 0xa738,
- 0xa73a, 0xa73a,
- 0xa73c, 0xa73c,
- 0xa73e, 0xa73e,
- 0xa740, 0xa740,
- 0xa742, 0xa742,
- 0xa744, 0xa744,
- 0xa746, 0xa746,
- 0xa748, 0xa748,
- 0xa74a, 0xa74a,
- 0xa74c, 0xa74c,
- 0xa74e, 0xa74e,
- 0xa750, 0xa750,
- 0xa752, 0xa752,
- 0xa754, 0xa754,
- 0xa756, 0xa756,
- 0xa758, 0xa758,
- 0xa75a, 0xa75a,
- 0xa75c, 0xa75c,
- 0xa75e, 0xa75e,
- 0xa760, 0xa760,
- 0xa762, 0xa762,
- 0xa764, 0xa764,
- 0xa766, 0xa766,
- 0xa768, 0xa768,
- 0xa76a, 0xa76a,
- 0xa76c, 0xa76c,
- 0xa76e, 0xa76e,
- 0xa779, 0xa779,
- 0xa77b, 0xa77b,
- 0xa77d, 0xa77e,
- 0xa780, 0xa780,
- 0xa782, 0xa782,
- 0xa784, 0xa784,
- 0xa786, 0xa786,
- 0xa78b, 0xa78b,
- 0xa78d, 0xa78d,
- 0xa790, 0xa790,
- 0xa792, 0xa792,
- 0xa796, 0xa796,
- 0xa798, 0xa798,
- 0xa79a, 0xa79a,
- 0xa79c, 0xa79c,
- 0xa79e, 0xa79e,
- 0xa7a0, 0xa7a0,
- 0xa7a2, 0xa7a2,
- 0xa7a4, 0xa7a4,
- 0xa7a6, 0xa7a6,
- 0xa7a8, 0xa7a8,
- 0xa7aa, 0xa7ae,
- 0xa7b0, 0xa7b4,
- 0xa7b6, 0xa7b6,
- 0xff21, 0xff3a,
- 0x10400, 0x10427,
- 0x104b0, 0x104d3,
- 0x10c80, 0x10cb2,
- 0x118a0, 0x118bf,
- 0x1d400, 0x1d419,
- 0x1d434, 0x1d44d,
- 0x1d468, 0x1d481,
- 0x1d49c, 0x1d49c,
- 0x1d49e, 0x1d49f,
- 0x1d4a2, 0x1d4a2,
- 0x1d4a5, 0x1d4a6,
- 0x1d4a9, 0x1d4ac,
- 0x1d4ae, 0x1d4b5,
- 0x1d4d0, 0x1d4e9,
- 0x1d504, 0x1d505,
- 0x1d507, 0x1d50a,
- 0x1d50d, 0x1d514,
- 0x1d516, 0x1d51c,
- 0x1d538, 0x1d539,
- 0x1d53b, 0x1d53e,
- 0x1d540, 0x1d544,
- 0x1d546, 0x1d546,
- 0x1d54a, 0x1d550,
- 0x1d56c, 0x1d585,
- 0x1d5a0, 0x1d5b9,
- 0x1d5d4, 0x1d5ed,
- 0x1d608, 0x1d621,
- 0x1d63c, 0x1d655,
- 0x1d670, 0x1d689,
- 0x1d6a8, 0x1d6c0,
- 0x1d6e2, 0x1d6fa,
- 0x1d71c, 0x1d734,
- 0x1d756, 0x1d76e,
- 0x1d790, 0x1d7a8,
- 0x1d7ca, 0x1d7ca,
- 0x1e900, 0x1e921,
-}; /* CR_Lu */
-
-/* 'M': Major Category */
-static const OnigCodePoint CR_M[] = {
- 250,
- 0x0300, 0x036f,
- 0x0483, 0x0489,
- 0x0591, 0x05bd,
- 0x05bf, 0x05bf,
- 0x05c1, 0x05c2,
- 0x05c4, 0x05c5,
- 0x05c7, 0x05c7,
- 0x0610, 0x061a,
- 0x064b, 0x065f,
- 0x0670, 0x0670,
- 0x06d6, 0x06dc,
- 0x06df, 0x06e4,
- 0x06e7, 0x06e8,
- 0x06ea, 0x06ed,
- 0x0711, 0x0711,
- 0x0730, 0x074a,
- 0x07a6, 0x07b0,
- 0x07eb, 0x07f3,
- 0x0816, 0x0819,
- 0x081b, 0x0823,
- 0x0825, 0x0827,
- 0x0829, 0x082d,
- 0x0859, 0x085b,
- 0x08d4, 0x08e1,
- 0x08e3, 0x0903,
- 0x093a, 0x093c,
- 0x093e, 0x094f,
- 0x0951, 0x0957,
- 0x0962, 0x0963,
- 0x0981, 0x0983,
- 0x09bc, 0x09bc,
- 0x09be, 0x09c4,
- 0x09c7, 0x09c8,
- 0x09cb, 0x09cd,
- 0x09d7, 0x09d7,
- 0x09e2, 0x09e3,
- 0x0a01, 0x0a03,
- 0x0a3c, 0x0a3c,
- 0x0a3e, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4d,
- 0x0a51, 0x0a51,
- 0x0a70, 0x0a71,
- 0x0a75, 0x0a75,
- 0x0a81, 0x0a83,
- 0x0abc, 0x0abc,
- 0x0abe, 0x0ac5,
- 0x0ac7, 0x0ac9,
- 0x0acb, 0x0acd,
- 0x0ae2, 0x0ae3,
- 0x0b01, 0x0b03,
- 0x0b3c, 0x0b3c,
- 0x0b3e, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b62, 0x0b63,
- 0x0b82, 0x0b82,
- 0x0bbe, 0x0bc2,
- 0x0bc6, 0x0bc8,
- 0x0bca, 0x0bcd,
- 0x0bd7, 0x0bd7,
- 0x0c00, 0x0c03,
- 0x0c3e, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c62, 0x0c63,
- 0x0c81, 0x0c83,
- 0x0cbc, 0x0cbc,
- 0x0cbe, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0ce2, 0x0ce3,
- 0x0d01, 0x0d03,
- 0x0d3e, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4d,
- 0x0d57, 0x0d57,
- 0x0d62, 0x0d63,
- 0x0d82, 0x0d83,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df3,
- 0x0e31, 0x0e31,
- 0x0e34, 0x0e3a,
- 0x0e47, 0x0e4e,
- 0x0eb1, 0x0eb1,
- 0x0eb4, 0x0eb9,
- 0x0ebb, 0x0ebc,
- 0x0ec8, 0x0ecd,
- 0x0f18, 0x0f19,
- 0x0f35, 0x0f35,
- 0x0f37, 0x0f37,
- 0x0f39, 0x0f39,
- 0x0f3e, 0x0f3f,
- 0x0f71, 0x0f84,
- 0x0f86, 0x0f87,
- 0x0f8d, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fc6, 0x0fc6,
- 0x102b, 0x103e,
- 0x1056, 0x1059,
- 0x105e, 0x1060,
- 0x1062, 0x1064,
- 0x1067, 0x106d,
- 0x1071, 0x1074,
- 0x1082, 0x108d,
- 0x108f, 0x108f,
- 0x109a, 0x109d,
- 0x135d, 0x135f,
- 0x1712, 0x1714,
- 0x1732, 0x1734,
- 0x1752, 0x1753,
- 0x1772, 0x1773,
- 0x17b4, 0x17d3,
- 0x17dd, 0x17dd,
- 0x180b, 0x180d,
- 0x1885, 0x1886,
- 0x18a9, 0x18a9,
- 0x1920, 0x192b,
- 0x1930, 0x193b,
- 0x1a17, 0x1a1b,
- 0x1a55, 0x1a5e,
- 0x1a60, 0x1a7c,
- 0x1a7f, 0x1a7f,
- 0x1ab0, 0x1abe,
- 0x1b00, 0x1b04,
- 0x1b34, 0x1b44,
- 0x1b6b, 0x1b73,
- 0x1b80, 0x1b82,
- 0x1ba1, 0x1bad,
- 0x1be6, 0x1bf3,
- 0x1c24, 0x1c37,
- 0x1cd0, 0x1cd2,
- 0x1cd4, 0x1ce8,
- 0x1ced, 0x1ced,
- 0x1cf2, 0x1cf4,
- 0x1cf8, 0x1cf9,
- 0x1dc0, 0x1df5,
- 0x1dfb, 0x1dff,
- 0x20d0, 0x20f0,
- 0x2cef, 0x2cf1,
- 0x2d7f, 0x2d7f,
- 0x2de0, 0x2dff,
- 0x302a, 0x302f,
- 0x3099, 0x309a,
- 0xa66f, 0xa672,
- 0xa674, 0xa67d,
- 0xa69e, 0xa69f,
- 0xa6f0, 0xa6f1,
- 0xa802, 0xa802,
- 0xa806, 0xa806,
- 0xa80b, 0xa80b,
- 0xa823, 0xa827,
- 0xa880, 0xa881,
- 0xa8b4, 0xa8c5,
- 0xa8e0, 0xa8f1,
- 0xa926, 0xa92d,
- 0xa947, 0xa953,
- 0xa980, 0xa983,
- 0xa9b3, 0xa9c0,
- 0xa9e5, 0xa9e5,
- 0xaa29, 0xaa36,
- 0xaa43, 0xaa43,
- 0xaa4c, 0xaa4d,
- 0xaa7b, 0xaa7d,
- 0xaab0, 0xaab0,
- 0xaab2, 0xaab4,
- 0xaab7, 0xaab8,
- 0xaabe, 0xaabf,
- 0xaac1, 0xaac1,
- 0xaaeb, 0xaaef,
- 0xaaf5, 0xaaf6,
- 0xabe3, 0xabea,
- 0xabec, 0xabed,
- 0xfb1e, 0xfb1e,
- 0xfe00, 0xfe0f,
- 0xfe20, 0xfe2f,
- 0x101fd, 0x101fd,
- 0x102e0, 0x102e0,
- 0x10376, 0x1037a,
- 0x10a01, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a0f,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a3f,
- 0x10ae5, 0x10ae6,
- 0x11000, 0x11002,
- 0x11038, 0x11046,
- 0x1107f, 0x11082,
- 0x110b0, 0x110ba,
- 0x11100, 0x11102,
- 0x11127, 0x11134,
- 0x11173, 0x11173,
- 0x11180, 0x11182,
- 0x111b3, 0x111c0,
- 0x111ca, 0x111cc,
- 0x1122c, 0x11237,
- 0x1123e, 0x1123e,
- 0x112df, 0x112ea,
- 0x11300, 0x11303,
- 0x1133c, 0x1133c,
- 0x1133e, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134d,
- 0x11357, 0x11357,
- 0x11362, 0x11363,
- 0x11366, 0x1136c,
- 0x11370, 0x11374,
- 0x11435, 0x11446,
- 0x114b0, 0x114c3,
- 0x115af, 0x115b5,
- 0x115b8, 0x115c0,
- 0x115dc, 0x115dd,
- 0x11630, 0x11640,
- 0x116ab, 0x116b7,
- 0x1171d, 0x1172b,
- 0x11c2f, 0x11c36,
- 0x11c38, 0x11c3f,
- 0x11c92, 0x11ca7,
- 0x11ca9, 0x11cb6,
- 0x16af0, 0x16af4,
- 0x16b30, 0x16b36,
- 0x16f51, 0x16f7e,
- 0x16f8f, 0x16f92,
- 0x1bc9d, 0x1bc9e,
- 0x1d165, 0x1d169,
- 0x1d16d, 0x1d172,
- 0x1d17b, 0x1d182,
- 0x1d185, 0x1d18b,
- 0x1d1aa, 0x1d1ad,
- 0x1d242, 0x1d244,
- 0x1da00, 0x1da36,
- 0x1da3b, 0x1da6c,
- 0x1da75, 0x1da75,
- 0x1da84, 0x1da84,
- 0x1da9b, 0x1da9f,
- 0x1daa1, 0x1daaf,
- 0x1e000, 0x1e006,
- 0x1e008, 0x1e018,
- 0x1e01b, 0x1e021,
- 0x1e023, 0x1e024,
- 0x1e026, 0x1e02a,
- 0x1e8d0, 0x1e8d6,
- 0x1e944, 0x1e94a,
- 0xe0100, 0xe01ef,
-}; /* CR_M */
-
-/* 'Mc': General Category */
-static const OnigCodePoint CR_Mc[] = {
- 155,
- 0x0903, 0x0903,
- 0x093b, 0x093b,
- 0x093e, 0x0940,
- 0x0949, 0x094c,
- 0x094e, 0x094f,
- 0x0982, 0x0983,
- 0x09be, 0x09c0,
- 0x09c7, 0x09c8,
- 0x09cb, 0x09cc,
- 0x09d7, 0x09d7,
- 0x0a03, 0x0a03,
- 0x0a3e, 0x0a40,
- 0x0a83, 0x0a83,
- 0x0abe, 0x0ac0,
- 0x0ac9, 0x0ac9,
- 0x0acb, 0x0acc,
- 0x0b02, 0x0b03,
- 0x0b3e, 0x0b3e,
- 0x0b40, 0x0b40,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4c,
- 0x0b57, 0x0b57,
- 0x0bbe, 0x0bbf,
- 0x0bc1, 0x0bc2,
- 0x0bc6, 0x0bc8,
- 0x0bca, 0x0bcc,
- 0x0bd7, 0x0bd7,
- 0x0c01, 0x0c03,
- 0x0c41, 0x0c44,
- 0x0c82, 0x0c83,
- 0x0cbe, 0x0cbe,
- 0x0cc0, 0x0cc4,
- 0x0cc7, 0x0cc8,
- 0x0cca, 0x0ccb,
- 0x0cd5, 0x0cd6,
- 0x0d02, 0x0d03,
- 0x0d3e, 0x0d40,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4c,
- 0x0d57, 0x0d57,
- 0x0d82, 0x0d83,
- 0x0dcf, 0x0dd1,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df3,
- 0x0f3e, 0x0f3f,
- 0x0f7f, 0x0f7f,
- 0x102b, 0x102c,
- 0x1031, 0x1031,
- 0x1038, 0x1038,
- 0x103b, 0x103c,
- 0x1056, 0x1057,
- 0x1062, 0x1064,
- 0x1067, 0x106d,
- 0x1083, 0x1084,
- 0x1087, 0x108c,
- 0x108f, 0x108f,
- 0x109a, 0x109c,
- 0x17b6, 0x17b6,
- 0x17be, 0x17c5,
- 0x17c7, 0x17c8,
- 0x1923, 0x1926,
- 0x1929, 0x192b,
- 0x1930, 0x1931,
- 0x1933, 0x1938,
- 0x1a19, 0x1a1a,
- 0x1a55, 0x1a55,
- 0x1a57, 0x1a57,
- 0x1a61, 0x1a61,
- 0x1a63, 0x1a64,
- 0x1a6d, 0x1a72,
- 0x1b04, 0x1b04,
- 0x1b35, 0x1b35,
- 0x1b3b, 0x1b3b,
- 0x1b3d, 0x1b41,
- 0x1b43, 0x1b44,
- 0x1b82, 0x1b82,
- 0x1ba1, 0x1ba1,
- 0x1ba6, 0x1ba7,
- 0x1baa, 0x1baa,
- 0x1be7, 0x1be7,
- 0x1bea, 0x1bec,
- 0x1bee, 0x1bee,
- 0x1bf2, 0x1bf3,
- 0x1c24, 0x1c2b,
- 0x1c34, 0x1c35,
- 0x1ce1, 0x1ce1,
- 0x1cf2, 0x1cf3,
- 0x302e, 0x302f,
- 0xa823, 0xa824,
- 0xa827, 0xa827,
- 0xa880, 0xa881,
- 0xa8b4, 0xa8c3,
- 0xa952, 0xa953,
- 0xa983, 0xa983,
- 0xa9b4, 0xa9b5,
- 0xa9ba, 0xa9bb,
- 0xa9bd, 0xa9c0,
- 0xaa2f, 0xaa30,
- 0xaa33, 0xaa34,
- 0xaa4d, 0xaa4d,
- 0xaa7b, 0xaa7b,
- 0xaa7d, 0xaa7d,
- 0xaaeb, 0xaaeb,
- 0xaaee, 0xaaef,
- 0xaaf5, 0xaaf5,
- 0xabe3, 0xabe4,
- 0xabe6, 0xabe7,
- 0xabe9, 0xabea,
- 0xabec, 0xabec,
- 0x11000, 0x11000,
- 0x11002, 0x11002,
- 0x11082, 0x11082,
- 0x110b0, 0x110b2,
- 0x110b7, 0x110b8,
- 0x1112c, 0x1112c,
- 0x11182, 0x11182,
- 0x111b3, 0x111b5,
- 0x111bf, 0x111c0,
- 0x1122c, 0x1122e,
- 0x11232, 0x11233,
- 0x11235, 0x11235,
- 0x112e0, 0x112e2,
- 0x11302, 0x11303,
- 0x1133e, 0x1133f,
- 0x11341, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134d,
- 0x11357, 0x11357,
- 0x11362, 0x11363,
- 0x11435, 0x11437,
- 0x11440, 0x11441,
- 0x11445, 0x11445,
- 0x114b0, 0x114b2,
- 0x114b9, 0x114b9,
- 0x114bb, 0x114be,
- 0x114c1, 0x114c1,
- 0x115af, 0x115b1,
- 0x115b8, 0x115bb,
- 0x115be, 0x115be,
- 0x11630, 0x11632,
- 0x1163b, 0x1163c,
- 0x1163e, 0x1163e,
- 0x116ac, 0x116ac,
- 0x116ae, 0x116af,
- 0x116b6, 0x116b6,
- 0x11720, 0x11721,
- 0x11726, 0x11726,
- 0x11c2f, 0x11c2f,
- 0x11c3e, 0x11c3e,
- 0x11ca9, 0x11ca9,
- 0x11cb1, 0x11cb1,
- 0x11cb4, 0x11cb4,
- 0x16f51, 0x16f7e,
- 0x1d165, 0x1d166,
- 0x1d16d, 0x1d172,
-}; /* CR_Mc */
-
-/* 'Me': General Category */
-static const OnigCodePoint CR_Me[] = {
- 5,
- 0x0488, 0x0489,
- 0x1abe, 0x1abe,
- 0x20dd, 0x20e0,
- 0x20e2, 0x20e4,
- 0xa670, 0xa672,
-}; /* CR_Me */
-
-/* 'Mn': General Category */
-static const OnigCodePoint CR_Mn[] = {
- 285,
- 0x0300, 0x036f,
- 0x0483, 0x0487,
- 0x0591, 0x05bd,
- 0x05bf, 0x05bf,
- 0x05c1, 0x05c2,
- 0x05c4, 0x05c5,
- 0x05c7, 0x05c7,
- 0x0610, 0x061a,
- 0x064b, 0x065f,
- 0x0670, 0x0670,
- 0x06d6, 0x06dc,
- 0x06df, 0x06e4,
- 0x06e7, 0x06e8,
- 0x06ea, 0x06ed,
- 0x0711, 0x0711,
- 0x0730, 0x074a,
- 0x07a6, 0x07b0,
- 0x07eb, 0x07f3,
- 0x0816, 0x0819,
- 0x081b, 0x0823,
- 0x0825, 0x0827,
- 0x0829, 0x082d,
- 0x0859, 0x085b,
- 0x08d4, 0x08e1,
- 0x08e3, 0x0902,
- 0x093a, 0x093a,
- 0x093c, 0x093c,
- 0x0941, 0x0948,
- 0x094d, 0x094d,
- 0x0951, 0x0957,
- 0x0962, 0x0963,
- 0x0981, 0x0981,
- 0x09bc, 0x09bc,
- 0x09c1, 0x09c4,
- 0x09cd, 0x09cd,
- 0x09e2, 0x09e3,
- 0x0a01, 0x0a02,
- 0x0a3c, 0x0a3c,
- 0x0a41, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4d,
- 0x0a51, 0x0a51,
- 0x0a70, 0x0a71,
- 0x0a75, 0x0a75,
- 0x0a81, 0x0a82,
- 0x0abc, 0x0abc,
- 0x0ac1, 0x0ac5,
- 0x0ac7, 0x0ac8,
- 0x0acd, 0x0acd,
- 0x0ae2, 0x0ae3,
- 0x0b01, 0x0b01,
- 0x0b3c, 0x0b3c,
- 0x0b3f, 0x0b3f,
- 0x0b41, 0x0b44,
- 0x0b4d, 0x0b4d,
- 0x0b56, 0x0b56,
- 0x0b62, 0x0b63,
- 0x0b82, 0x0b82,
- 0x0bc0, 0x0bc0,
- 0x0bcd, 0x0bcd,
- 0x0c00, 0x0c00,
- 0x0c3e, 0x0c40,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c62, 0x0c63,
- 0x0c81, 0x0c81,
- 0x0cbc, 0x0cbc,
- 0x0cbf, 0x0cbf,
- 0x0cc6, 0x0cc6,
- 0x0ccc, 0x0ccd,
- 0x0ce2, 0x0ce3,
- 0x0d01, 0x0d01,
- 0x0d41, 0x0d44,
- 0x0d4d, 0x0d4d,
- 0x0d62, 0x0d63,
- 0x0dca, 0x0dca,
- 0x0dd2, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0e31, 0x0e31,
- 0x0e34, 0x0e3a,
- 0x0e47, 0x0e4e,
- 0x0eb1, 0x0eb1,
- 0x0eb4, 0x0eb9,
- 0x0ebb, 0x0ebc,
- 0x0ec8, 0x0ecd,
- 0x0f18, 0x0f19,
- 0x0f35, 0x0f35,
- 0x0f37, 0x0f37,
- 0x0f39, 0x0f39,
- 0x0f71, 0x0f7e,
- 0x0f80, 0x0f84,
- 0x0f86, 0x0f87,
- 0x0f8d, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fc6, 0x0fc6,
- 0x102d, 0x1030,
- 0x1032, 0x1037,
- 0x1039, 0x103a,
- 0x103d, 0x103e,
- 0x1058, 0x1059,
- 0x105e, 0x1060,
- 0x1071, 0x1074,
- 0x1082, 0x1082,
- 0x1085, 0x1086,
- 0x108d, 0x108d,
- 0x109d, 0x109d,
- 0x135d, 0x135f,
- 0x1712, 0x1714,
- 0x1732, 0x1734,
- 0x1752, 0x1753,
- 0x1772, 0x1773,
- 0x17b4, 0x17b5,
- 0x17b7, 0x17bd,
- 0x17c6, 0x17c6,
- 0x17c9, 0x17d3,
- 0x17dd, 0x17dd,
- 0x180b, 0x180d,
- 0x1885, 0x1886,
- 0x18a9, 0x18a9,
- 0x1920, 0x1922,
- 0x1927, 0x1928,
- 0x1932, 0x1932,
- 0x1939, 0x193b,
- 0x1a17, 0x1a18,
- 0x1a1b, 0x1a1b,
- 0x1a56, 0x1a56,
- 0x1a58, 0x1a5e,
- 0x1a60, 0x1a60,
- 0x1a62, 0x1a62,
- 0x1a65, 0x1a6c,
- 0x1a73, 0x1a7c,
- 0x1a7f, 0x1a7f,
- 0x1ab0, 0x1abd,
- 0x1b00, 0x1b03,
- 0x1b34, 0x1b34,
- 0x1b36, 0x1b3a,
- 0x1b3c, 0x1b3c,
- 0x1b42, 0x1b42,
- 0x1b6b, 0x1b73,
- 0x1b80, 0x1b81,
- 0x1ba2, 0x1ba5,
- 0x1ba8, 0x1ba9,
- 0x1bab, 0x1bad,
- 0x1be6, 0x1be6,
- 0x1be8, 0x1be9,
- 0x1bed, 0x1bed,
- 0x1bef, 0x1bf1,
- 0x1c2c, 0x1c33,
- 0x1c36, 0x1c37,
- 0x1cd0, 0x1cd2,
- 0x1cd4, 0x1ce0,
- 0x1ce2, 0x1ce8,
- 0x1ced, 0x1ced,
- 0x1cf4, 0x1cf4,
- 0x1cf8, 0x1cf9,
- 0x1dc0, 0x1df5,
- 0x1dfb, 0x1dff,
- 0x20d0, 0x20dc,
- 0x20e1, 0x20e1,
- 0x20e5, 0x20f0,
- 0x2cef, 0x2cf1,
- 0x2d7f, 0x2d7f,
- 0x2de0, 0x2dff,
- 0x302a, 0x302d,
- 0x3099, 0x309a,
- 0xa66f, 0xa66f,
- 0xa674, 0xa67d,
- 0xa69e, 0xa69f,
- 0xa6f0, 0xa6f1,
- 0xa802, 0xa802,
- 0xa806, 0xa806,
- 0xa80b, 0xa80b,
- 0xa825, 0xa826,
- 0xa8c4, 0xa8c5,
- 0xa8e0, 0xa8f1,
- 0xa926, 0xa92d,
- 0xa947, 0xa951,
- 0xa980, 0xa982,
- 0xa9b3, 0xa9b3,
- 0xa9b6, 0xa9b9,
- 0xa9bc, 0xa9bc,
- 0xa9e5, 0xa9e5,
- 0xaa29, 0xaa2e,
- 0xaa31, 0xaa32,
- 0xaa35, 0xaa36,
- 0xaa43, 0xaa43,
- 0xaa4c, 0xaa4c,
- 0xaa7c, 0xaa7c,
- 0xaab0, 0xaab0,
- 0xaab2, 0xaab4,
- 0xaab7, 0xaab8,
- 0xaabe, 0xaabf,
- 0xaac1, 0xaac1,
- 0xaaec, 0xaaed,
- 0xaaf6, 0xaaf6,
- 0xabe5, 0xabe5,
- 0xabe8, 0xabe8,
- 0xabed, 0xabed,
- 0xfb1e, 0xfb1e,
- 0xfe00, 0xfe0f,
- 0xfe20, 0xfe2f,
- 0x101fd, 0x101fd,
- 0x102e0, 0x102e0,
- 0x10376, 0x1037a,
- 0x10a01, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a0f,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a3f,
- 0x10ae5, 0x10ae6,
- 0x11001, 0x11001,
- 0x11038, 0x11046,
- 0x1107f, 0x11081,
- 0x110b3, 0x110b6,
- 0x110b9, 0x110ba,
- 0x11100, 0x11102,
- 0x11127, 0x1112b,
- 0x1112d, 0x11134,
- 0x11173, 0x11173,
- 0x11180, 0x11181,
- 0x111b6, 0x111be,
- 0x111ca, 0x111cc,
- 0x1122f, 0x11231,
- 0x11234, 0x11234,
- 0x11236, 0x11237,
- 0x1123e, 0x1123e,
- 0x112df, 0x112df,
- 0x112e3, 0x112ea,
- 0x11300, 0x11301,
- 0x1133c, 0x1133c,
- 0x11340, 0x11340,
- 0x11366, 0x1136c,
- 0x11370, 0x11374,
- 0x11438, 0x1143f,
- 0x11442, 0x11444,
- 0x11446, 0x11446,
- 0x114b3, 0x114b8,
- 0x114ba, 0x114ba,
- 0x114bf, 0x114c0,
- 0x114c2, 0x114c3,
- 0x115b2, 0x115b5,
- 0x115bc, 0x115bd,
- 0x115bf, 0x115c0,
- 0x115dc, 0x115dd,
- 0x11633, 0x1163a,
- 0x1163d, 0x1163d,
- 0x1163f, 0x11640,
- 0x116ab, 0x116ab,
- 0x116ad, 0x116ad,
- 0x116b0, 0x116b5,
- 0x116b7, 0x116b7,
- 0x1171d, 0x1171f,
- 0x11722, 0x11725,
- 0x11727, 0x1172b,
- 0x11c30, 0x11c36,
- 0x11c38, 0x11c3d,
- 0x11c3f, 0x11c3f,
- 0x11c92, 0x11ca7,
- 0x11caa, 0x11cb0,
- 0x11cb2, 0x11cb3,
- 0x11cb5, 0x11cb6,
- 0x16af0, 0x16af4,
- 0x16b30, 0x16b36,
- 0x16f8f, 0x16f92,
- 0x1bc9d, 0x1bc9e,
- 0x1d167, 0x1d169,
- 0x1d17b, 0x1d182,
- 0x1d185, 0x1d18b,
- 0x1d1aa, 0x1d1ad,
- 0x1d242, 0x1d244,
- 0x1da00, 0x1da36,
- 0x1da3b, 0x1da6c,
- 0x1da75, 0x1da75,
- 0x1da84, 0x1da84,
- 0x1da9b, 0x1da9f,
- 0x1daa1, 0x1daaf,
- 0x1e000, 0x1e006,
- 0x1e008, 0x1e018,
- 0x1e01b, 0x1e021,
- 0x1e023, 0x1e024,
- 0x1e026, 0x1e02a,
- 0x1e8d0, 0x1e8d6,
- 0x1e944, 0x1e94a,
- 0xe0100, 0xe01ef,
-}; /* CR_Mn */
-
-/* 'N': Major Category */
-static const OnigCodePoint CR_N[] = {
- 115,
- 0x0030, 0x0039,
- 0x00b2, 0x00b3,
- 0x00b9, 0x00b9,
- 0x00bc, 0x00be,
- 0x0660, 0x0669,
- 0x06f0, 0x06f9,
- 0x07c0, 0x07c9,
- 0x0966, 0x096f,
- 0x09e6, 0x09ef,
- 0x09f4, 0x09f9,
- 0x0a66, 0x0a6f,
- 0x0ae6, 0x0aef,
- 0x0b66, 0x0b6f,
- 0x0b72, 0x0b77,
- 0x0be6, 0x0bf2,
- 0x0c66, 0x0c6f,
- 0x0c78, 0x0c7e,
- 0x0ce6, 0x0cef,
- 0x0d58, 0x0d5e,
- 0x0d66, 0x0d78,
- 0x0de6, 0x0def,
- 0x0e50, 0x0e59,
- 0x0ed0, 0x0ed9,
- 0x0f20, 0x0f33,
- 0x1040, 0x1049,
- 0x1090, 0x1099,
- 0x1369, 0x137c,
- 0x16ee, 0x16f0,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1810, 0x1819,
- 0x1946, 0x194f,
- 0x19d0, 0x19da,
- 0x1a80, 0x1a89,
- 0x1a90, 0x1a99,
- 0x1b50, 0x1b59,
- 0x1bb0, 0x1bb9,
- 0x1c40, 0x1c49,
- 0x1c50, 0x1c59,
- 0x2070, 0x2070,
- 0x2074, 0x2079,
- 0x2080, 0x2089,
- 0x2150, 0x2182,
- 0x2185, 0x2189,
- 0x2460, 0x249b,
- 0x24ea, 0x24ff,
- 0x2776, 0x2793,
- 0x2cfd, 0x2cfd,
- 0x3007, 0x3007,
- 0x3021, 0x3029,
- 0x3038, 0x303a,
- 0x3192, 0x3195,
- 0x3220, 0x3229,
- 0x3248, 0x324f,
- 0x3251, 0x325f,
- 0x3280, 0x3289,
- 0x32b1, 0x32bf,
- 0xa620, 0xa629,
- 0xa6e6, 0xa6ef,
- 0xa830, 0xa835,
- 0xa8d0, 0xa8d9,
- 0xa900, 0xa909,
- 0xa9d0, 0xa9d9,
- 0xa9f0, 0xa9f9,
- 0xaa50, 0xaa59,
- 0xabf0, 0xabf9,
- 0xff10, 0xff19,
- 0x10107, 0x10133,
- 0x10140, 0x10178,
- 0x1018a, 0x1018b,
- 0x102e1, 0x102fb,
- 0x10320, 0x10323,
- 0x10341, 0x10341,
- 0x1034a, 0x1034a,
- 0x103d1, 0x103d5,
- 0x104a0, 0x104a9,
- 0x10858, 0x1085f,
- 0x10879, 0x1087f,
- 0x108a7, 0x108af,
- 0x108fb, 0x108ff,
- 0x10916, 0x1091b,
- 0x109bc, 0x109bd,
- 0x109c0, 0x109cf,
- 0x109d2, 0x109ff,
- 0x10a40, 0x10a47,
- 0x10a7d, 0x10a7e,
- 0x10a9d, 0x10a9f,
- 0x10aeb, 0x10aef,
- 0x10b58, 0x10b5f,
- 0x10b78, 0x10b7f,
- 0x10ba9, 0x10baf,
- 0x10cfa, 0x10cff,
- 0x10e60, 0x10e7e,
- 0x11052, 0x1106f,
- 0x110f0, 0x110f9,
- 0x11136, 0x1113f,
- 0x111d0, 0x111d9,
- 0x111e1, 0x111f4,
- 0x112f0, 0x112f9,
- 0x11450, 0x11459,
- 0x114d0, 0x114d9,
- 0x11650, 0x11659,
- 0x116c0, 0x116c9,
- 0x11730, 0x1173b,
- 0x118e0, 0x118f2,
- 0x11c50, 0x11c6c,
- 0x12400, 0x1246e,
- 0x16a60, 0x16a69,
- 0x16b50, 0x16b59,
- 0x16b5b, 0x16b61,
- 0x1d360, 0x1d371,
- 0x1d7ce, 0x1d7ff,
- 0x1e8c7, 0x1e8cf,
- 0x1e950, 0x1e959,
- 0x1f100, 0x1f10c,
-}; /* CR_N */
-
-/* 'Nd': General Category */
-#define CR_Nd CR_Digit
-
-/* 'Nl': General Category */
-static const OnigCodePoint CR_Nl[] = {
- 12,
- 0x16ee, 0x16f0,
- 0x2160, 0x2182,
- 0x2185, 0x2188,
- 0x3007, 0x3007,
- 0x3021, 0x3029,
- 0x3038, 0x303a,
- 0xa6e6, 0xa6ef,
- 0x10140, 0x10174,
- 0x10341, 0x10341,
- 0x1034a, 0x1034a,
- 0x103d1, 0x103d5,
- 0x12400, 0x1246e,
-}; /* CR_Nl */
-
-/* 'No': General Category */
-static const OnigCodePoint CR_No[] = {
- 60,
- 0x00b2, 0x00b3,
- 0x00b9, 0x00b9,
- 0x00bc, 0x00be,
- 0x09f4, 0x09f9,
- 0x0b72, 0x0b77,
- 0x0bf0, 0x0bf2,
- 0x0c78, 0x0c7e,
- 0x0d58, 0x0d5e,
- 0x0d70, 0x0d78,
- 0x0f2a, 0x0f33,
- 0x1369, 0x137c,
- 0x17f0, 0x17f9,
- 0x19da, 0x19da,
- 0x2070, 0x2070,
- 0x2074, 0x2079,
- 0x2080, 0x2089,
- 0x2150, 0x215f,
- 0x2189, 0x2189,
- 0x2460, 0x249b,
- 0x24ea, 0x24ff,
- 0x2776, 0x2793,
- 0x2cfd, 0x2cfd,
- 0x3192, 0x3195,
- 0x3220, 0x3229,
- 0x3248, 0x324f,
- 0x3251, 0x325f,
- 0x3280, 0x3289,
- 0x32b1, 0x32bf,
- 0xa830, 0xa835,
- 0x10107, 0x10133,
- 0x10175, 0x10178,
- 0x1018a, 0x1018b,
- 0x102e1, 0x102fb,
- 0x10320, 0x10323,
- 0x10858, 0x1085f,
- 0x10879, 0x1087f,
- 0x108a7, 0x108af,
- 0x108fb, 0x108ff,
- 0x10916, 0x1091b,
- 0x109bc, 0x109bd,
- 0x109c0, 0x109cf,
- 0x109d2, 0x109ff,
- 0x10a40, 0x10a47,
- 0x10a7d, 0x10a7e,
- 0x10a9d, 0x10a9f,
- 0x10aeb, 0x10aef,
- 0x10b58, 0x10b5f,
- 0x10b78, 0x10b7f,
- 0x10ba9, 0x10baf,
- 0x10cfa, 0x10cff,
- 0x10e60, 0x10e7e,
- 0x11052, 0x11065,
- 0x111e1, 0x111f4,
- 0x1173a, 0x1173b,
- 0x118ea, 0x118f2,
- 0x11c5a, 0x11c6c,
- 0x16b5b, 0x16b61,
- 0x1d360, 0x1d371,
- 0x1e8c7, 0x1e8cf,
- 0x1f100, 0x1f10c,
-}; /* CR_No */
-
-/* 'P': Major Category */
-#define CR_P CR_Punct
-
-/* 'Pc': General Category */
-static const OnigCodePoint CR_Pc[] = {
- 6,
- 0x005f, 0x005f,
- 0x203f, 0x2040,
- 0x2054, 0x2054,
- 0xfe33, 0xfe34,
- 0xfe4d, 0xfe4f,
- 0xff3f, 0xff3f,
-}; /* CR_Pc */
-
-/* 'Pd': General Category */
-static const OnigCodePoint CR_Pd[] = {
- 17,
- 0x002d, 0x002d,
- 0x058a, 0x058a,
- 0x05be, 0x05be,
- 0x1400, 0x1400,
- 0x1806, 0x1806,
- 0x2010, 0x2015,
- 0x2e17, 0x2e17,
- 0x2e1a, 0x2e1a,
- 0x2e3a, 0x2e3b,
- 0x2e40, 0x2e40,
- 0x301c, 0x301c,
- 0x3030, 0x3030,
- 0x30a0, 0x30a0,
- 0xfe31, 0xfe32,
- 0xfe58, 0xfe58,
- 0xfe63, 0xfe63,
- 0xff0d, 0xff0d,
-}; /* CR_Pd */
-
-/* 'Pe': General Category */
-static const OnigCodePoint CR_Pe[] = {
- 72,
- 0x0029, 0x0029,
- 0x005d, 0x005d,
- 0x007d, 0x007d,
- 0x0f3b, 0x0f3b,
- 0x0f3d, 0x0f3d,
- 0x169c, 0x169c,
- 0x2046, 0x2046,
- 0x207e, 0x207e,
- 0x208e, 0x208e,
- 0x2309, 0x2309,
- 0x230b, 0x230b,
- 0x232a, 0x232a,
- 0x2769, 0x2769,
- 0x276b, 0x276b,
- 0x276d, 0x276d,
- 0x276f, 0x276f,
- 0x2771, 0x2771,
- 0x2773, 0x2773,
- 0x2775, 0x2775,
- 0x27c6, 0x27c6,
- 0x27e7, 0x27e7,
- 0x27e9, 0x27e9,
- 0x27eb, 0x27eb,
- 0x27ed, 0x27ed,
- 0x27ef, 0x27ef,
- 0x2984, 0x2984,
- 0x2986, 0x2986,
- 0x2988, 0x2988,
- 0x298a, 0x298a,
- 0x298c, 0x298c,
- 0x298e, 0x298e,
- 0x2990, 0x2990,
- 0x2992, 0x2992,
- 0x2994, 0x2994,
- 0x2996, 0x2996,
- 0x2998, 0x2998,
- 0x29d9, 0x29d9,
- 0x29db, 0x29db,
- 0x29fd, 0x29fd,
- 0x2e23, 0x2e23,
- 0x2e25, 0x2e25,
- 0x2e27, 0x2e27,
- 0x2e29, 0x2e29,
- 0x3009, 0x3009,
- 0x300b, 0x300b,
- 0x300d, 0x300d,
- 0x300f, 0x300f,
- 0x3011, 0x3011,
- 0x3015, 0x3015,
- 0x3017, 0x3017,
- 0x3019, 0x3019,
- 0x301b, 0x301b,
- 0x301e, 0x301f,
- 0xfd3e, 0xfd3e,
- 0xfe18, 0xfe18,
- 0xfe36, 0xfe36,
- 0xfe38, 0xfe38,
- 0xfe3a, 0xfe3a,
- 0xfe3c, 0xfe3c,
- 0xfe3e, 0xfe3e,
- 0xfe40, 0xfe40,
- 0xfe42, 0xfe42,
- 0xfe44, 0xfe44,
- 0xfe48, 0xfe48,
- 0xfe5a, 0xfe5a,
- 0xfe5c, 0xfe5c,
- 0xfe5e, 0xfe5e,
- 0xff09, 0xff09,
- 0xff3d, 0xff3d,
- 0xff5d, 0xff5d,
- 0xff60, 0xff60,
- 0xff63, 0xff63,
-}; /* CR_Pe */
-
-/* 'Pf': General Category */
-static const OnigCodePoint CR_Pf[] = {
- 10,
- 0x00bb, 0x00bb,
- 0x2019, 0x2019,
- 0x201d, 0x201d,
- 0x203a, 0x203a,
- 0x2e03, 0x2e03,
- 0x2e05, 0x2e05,
- 0x2e0a, 0x2e0a,
- 0x2e0d, 0x2e0d,
- 0x2e1d, 0x2e1d,
- 0x2e21, 0x2e21,
-}; /* CR_Pf */
-
-/* 'Pi': General Category */
-static const OnigCodePoint CR_Pi[] = {
- 11,
- 0x00ab, 0x00ab,
- 0x2018, 0x2018,
- 0x201b, 0x201c,
- 0x201f, 0x201f,
- 0x2039, 0x2039,
- 0x2e02, 0x2e02,
- 0x2e04, 0x2e04,
- 0x2e09, 0x2e09,
- 0x2e0c, 0x2e0c,
- 0x2e1c, 0x2e1c,
- 0x2e20, 0x2e20,
-}; /* CR_Pi */
-
-/* 'Po': General Category */
-static const OnigCodePoint CR_Po[] = {
- 165,
- 0x0021, 0x0023,
- 0x0025, 0x0027,
- 0x002a, 0x002a,
- 0x002c, 0x002c,
- 0x002e, 0x002f,
- 0x003a, 0x003b,
- 0x003f, 0x0040,
- 0x005c, 0x005c,
- 0x00a1, 0x00a1,
- 0x00a7, 0x00a7,
- 0x00b6, 0x00b7,
- 0x00bf, 0x00bf,
- 0x037e, 0x037e,
- 0x0387, 0x0387,
- 0x055a, 0x055f,
- 0x0589, 0x0589,
- 0x05c0, 0x05c0,
- 0x05c3, 0x05c3,
- 0x05c6, 0x05c6,
- 0x05f3, 0x05f4,
- 0x0609, 0x060a,
- 0x060c, 0x060d,
- 0x061b, 0x061b,
- 0x061e, 0x061f,
- 0x066a, 0x066d,
- 0x06d4, 0x06d4,
- 0x0700, 0x070d,
- 0x07f7, 0x07f9,
- 0x0830, 0x083e,
- 0x085e, 0x085e,
- 0x0964, 0x0965,
- 0x0970, 0x0970,
- 0x0af0, 0x0af0,
- 0x0df4, 0x0df4,
- 0x0e4f, 0x0e4f,
- 0x0e5a, 0x0e5b,
- 0x0f04, 0x0f12,
- 0x0f14, 0x0f14,
- 0x0f85, 0x0f85,
- 0x0fd0, 0x0fd4,
- 0x0fd9, 0x0fda,
- 0x104a, 0x104f,
- 0x10fb, 0x10fb,
- 0x1360, 0x1368,
- 0x166d, 0x166e,
- 0x16eb, 0x16ed,
- 0x1735, 0x1736,
- 0x17d4, 0x17d6,
- 0x17d8, 0x17da,
- 0x1800, 0x1805,
- 0x1807, 0x180a,
- 0x1944, 0x1945,
- 0x1a1e, 0x1a1f,
- 0x1aa0, 0x1aa6,
- 0x1aa8, 0x1aad,
- 0x1b5a, 0x1b60,
- 0x1bfc, 0x1bff,
- 0x1c3b, 0x1c3f,
- 0x1c7e, 0x1c7f,
- 0x1cc0, 0x1cc7,
- 0x1cd3, 0x1cd3,
- 0x2016, 0x2017,
- 0x2020, 0x2027,
- 0x2030, 0x2038,
- 0x203b, 0x203e,
- 0x2041, 0x2043,
- 0x2047, 0x2051,
- 0x2053, 0x2053,
- 0x2055, 0x205e,
- 0x2cf9, 0x2cfc,
- 0x2cfe, 0x2cff,
- 0x2d70, 0x2d70,
- 0x2e00, 0x2e01,
- 0x2e06, 0x2e08,
- 0x2e0b, 0x2e0b,
- 0x2e0e, 0x2e16,
- 0x2e18, 0x2e19,
- 0x2e1b, 0x2e1b,
- 0x2e1e, 0x2e1f,
- 0x2e2a, 0x2e2e,
- 0x2e30, 0x2e39,
- 0x2e3c, 0x2e3f,
- 0x2e41, 0x2e41,
- 0x2e43, 0x2e44,
- 0x3001, 0x3003,
- 0x303d, 0x303d,
- 0x30fb, 0x30fb,
- 0xa4fe, 0xa4ff,
- 0xa60d, 0xa60f,
- 0xa673, 0xa673,
- 0xa67e, 0xa67e,
- 0xa6f2, 0xa6f7,
- 0xa874, 0xa877,
- 0xa8ce, 0xa8cf,
- 0xa8f8, 0xa8fa,
- 0xa8fc, 0xa8fc,
- 0xa92e, 0xa92f,
- 0xa95f, 0xa95f,
- 0xa9c1, 0xa9cd,
- 0xa9de, 0xa9df,
- 0xaa5c, 0xaa5f,
- 0xaade, 0xaadf,
- 0xaaf0, 0xaaf1,
- 0xabeb, 0xabeb,
- 0xfe10, 0xfe16,
- 0xfe19, 0xfe19,
- 0xfe30, 0xfe30,
- 0xfe45, 0xfe46,
- 0xfe49, 0xfe4c,
- 0xfe50, 0xfe52,
- 0xfe54, 0xfe57,
- 0xfe5f, 0xfe61,
- 0xfe68, 0xfe68,
- 0xfe6a, 0xfe6b,
- 0xff01, 0xff03,
- 0xff05, 0xff07,
- 0xff0a, 0xff0a,
- 0xff0c, 0xff0c,
- 0xff0e, 0xff0f,
- 0xff1a, 0xff1b,
- 0xff1f, 0xff20,
- 0xff3c, 0xff3c,
- 0xff61, 0xff61,
- 0xff64, 0xff65,
- 0x10100, 0x10102,
- 0x1039f, 0x1039f,
- 0x103d0, 0x103d0,
- 0x1056f, 0x1056f,
- 0x10857, 0x10857,
- 0x1091f, 0x1091f,
- 0x1093f, 0x1093f,
- 0x10a50, 0x10a58,
- 0x10a7f, 0x10a7f,
- 0x10af0, 0x10af6,
- 0x10b39, 0x10b3f,
- 0x10b99, 0x10b9c,
- 0x11047, 0x1104d,
- 0x110bb, 0x110bc,
- 0x110be, 0x110c1,
- 0x11140, 0x11143,
- 0x11174, 0x11175,
- 0x111c5, 0x111c9,
- 0x111cd, 0x111cd,
- 0x111db, 0x111db,
- 0x111dd, 0x111df,
- 0x11238, 0x1123d,
- 0x112a9, 0x112a9,
- 0x1144b, 0x1144f,
- 0x1145b, 0x1145b,
- 0x1145d, 0x1145d,
- 0x114c6, 0x114c6,
- 0x115c1, 0x115d7,
- 0x11641, 0x11643,
- 0x11660, 0x1166c,
- 0x1173c, 0x1173e,
- 0x11c41, 0x11c45,
- 0x11c70, 0x11c71,
- 0x12470, 0x12474,
- 0x16a6e, 0x16a6f,
- 0x16af5, 0x16af5,
- 0x16b37, 0x16b3b,
- 0x16b44, 0x16b44,
- 0x1bc9f, 0x1bc9f,
- 0x1da87, 0x1da8b,
- 0x1e95e, 0x1e95f,
-}; /* CR_Po */
-
-/* 'Ps': General Category */
-static const OnigCodePoint CR_Ps[] = {
- 75,
- 0x0028, 0x0028,
- 0x005b, 0x005b,
- 0x007b, 0x007b,
- 0x0f3a, 0x0f3a,
- 0x0f3c, 0x0f3c,
- 0x169b, 0x169b,
- 0x201a, 0x201a,
- 0x201e, 0x201e,
- 0x2045, 0x2045,
- 0x207d, 0x207d,
- 0x208d, 0x208d,
- 0x2308, 0x2308,
- 0x230a, 0x230a,
- 0x2329, 0x2329,
- 0x2768, 0x2768,
- 0x276a, 0x276a,
- 0x276c, 0x276c,
- 0x276e, 0x276e,
- 0x2770, 0x2770,
- 0x2772, 0x2772,
- 0x2774, 0x2774,
- 0x27c5, 0x27c5,
- 0x27e6, 0x27e6,
- 0x27e8, 0x27e8,
- 0x27ea, 0x27ea,
- 0x27ec, 0x27ec,
- 0x27ee, 0x27ee,
- 0x2983, 0x2983,
- 0x2985, 0x2985,
- 0x2987, 0x2987,
- 0x2989, 0x2989,
- 0x298b, 0x298b,
- 0x298d, 0x298d,
- 0x298f, 0x298f,
- 0x2991, 0x2991,
- 0x2993, 0x2993,
- 0x2995, 0x2995,
- 0x2997, 0x2997,
- 0x29d8, 0x29d8,
- 0x29da, 0x29da,
- 0x29fc, 0x29fc,
- 0x2e22, 0x2e22,
- 0x2e24, 0x2e24,
- 0x2e26, 0x2e26,
- 0x2e28, 0x2e28,
- 0x2e42, 0x2e42,
- 0x3008, 0x3008,
- 0x300a, 0x300a,
- 0x300c, 0x300c,
- 0x300e, 0x300e,
- 0x3010, 0x3010,
- 0x3014, 0x3014,
- 0x3016, 0x3016,
- 0x3018, 0x3018,
- 0x301a, 0x301a,
- 0x301d, 0x301d,
- 0xfd3f, 0xfd3f,
- 0xfe17, 0xfe17,
- 0xfe35, 0xfe35,
- 0xfe37, 0xfe37,
- 0xfe39, 0xfe39,
- 0xfe3b, 0xfe3b,
- 0xfe3d, 0xfe3d,
- 0xfe3f, 0xfe3f,
- 0xfe41, 0xfe41,
- 0xfe43, 0xfe43,
- 0xfe47, 0xfe47,
- 0xfe59, 0xfe59,
- 0xfe5b, 0xfe5b,
- 0xfe5d, 0xfe5d,
- 0xff08, 0xff08,
- 0xff3b, 0xff3b,
- 0xff5b, 0xff5b,
- 0xff5f, 0xff5f,
- 0xff62, 0xff62,
-}; /* CR_Ps */
-
-/* 'S': Major Category */
-static const OnigCodePoint CR_S[] = {
- 218,
- 0x0024, 0x0024,
- 0x002b, 0x002b,
- 0x003c, 0x003e,
- 0x005e, 0x005e,
- 0x0060, 0x0060,
- 0x007c, 0x007c,
- 0x007e, 0x007e,
- 0x00a2, 0x00a6,
- 0x00a8, 0x00a9,
- 0x00ac, 0x00ac,
- 0x00ae, 0x00b1,
- 0x00b4, 0x00b4,
- 0x00b8, 0x00b8,
- 0x00d7, 0x00d7,
- 0x00f7, 0x00f7,
- 0x02c2, 0x02c5,
- 0x02d2, 0x02df,
- 0x02e5, 0x02eb,
- 0x02ed, 0x02ed,
- 0x02ef, 0x02ff,
- 0x0375, 0x0375,
- 0x0384, 0x0385,
- 0x03f6, 0x03f6,
- 0x0482, 0x0482,
- 0x058d, 0x058f,
- 0x0606, 0x0608,
- 0x060b, 0x060b,
- 0x060e, 0x060f,
- 0x06de, 0x06de,
- 0x06e9, 0x06e9,
- 0x06fd, 0x06fe,
- 0x07f6, 0x07f6,
- 0x09f2, 0x09f3,
- 0x09fa, 0x09fb,
- 0x0af1, 0x0af1,
- 0x0b70, 0x0b70,
- 0x0bf3, 0x0bfa,
- 0x0c7f, 0x0c7f,
- 0x0d4f, 0x0d4f,
- 0x0d79, 0x0d79,
- 0x0e3f, 0x0e3f,
- 0x0f01, 0x0f03,
- 0x0f13, 0x0f13,
- 0x0f15, 0x0f17,
- 0x0f1a, 0x0f1f,
- 0x0f34, 0x0f34,
- 0x0f36, 0x0f36,
- 0x0f38, 0x0f38,
- 0x0fbe, 0x0fc5,
- 0x0fc7, 0x0fcc,
- 0x0fce, 0x0fcf,
- 0x0fd5, 0x0fd8,
- 0x109e, 0x109f,
- 0x1390, 0x1399,
- 0x17db, 0x17db,
- 0x1940, 0x1940,
- 0x19de, 0x19ff,
- 0x1b61, 0x1b6a,
- 0x1b74, 0x1b7c,
- 0x1fbd, 0x1fbd,
- 0x1fbf, 0x1fc1,
- 0x1fcd, 0x1fcf,
- 0x1fdd, 0x1fdf,
- 0x1fed, 0x1fef,
- 0x1ffd, 0x1ffe,
- 0x2044, 0x2044,
- 0x2052, 0x2052,
- 0x207a, 0x207c,
- 0x208a, 0x208c,
- 0x20a0, 0x20be,
- 0x2100, 0x2101,
- 0x2103, 0x2106,
- 0x2108, 0x2109,
- 0x2114, 0x2114,
- 0x2116, 0x2118,
- 0x211e, 0x2123,
- 0x2125, 0x2125,
- 0x2127, 0x2127,
- 0x2129, 0x2129,
- 0x212e, 0x212e,
- 0x213a, 0x213b,
- 0x2140, 0x2144,
- 0x214a, 0x214d,
- 0x214f, 0x214f,
- 0x218a, 0x218b,
- 0x2190, 0x2307,
- 0x230c, 0x2328,
- 0x232b, 0x23fe,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x249c, 0x24e9,
- 0x2500, 0x2767,
- 0x2794, 0x27c4,
- 0x27c7, 0x27e5,
- 0x27f0, 0x2982,
- 0x2999, 0x29d7,
- 0x29dc, 0x29fb,
- 0x29fe, 0x2b73,
- 0x2b76, 0x2b95,
- 0x2b98, 0x2bb9,
- 0x2bbd, 0x2bc8,
- 0x2bca, 0x2bd1,
- 0x2bec, 0x2bef,
- 0x2ce5, 0x2cea,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3004, 0x3004,
- 0x3012, 0x3013,
- 0x3020, 0x3020,
- 0x3036, 0x3037,
- 0x303e, 0x303f,
- 0x309b, 0x309c,
- 0x3190, 0x3191,
- 0x3196, 0x319f,
- 0x31c0, 0x31e3,
- 0x3200, 0x321e,
- 0x322a, 0x3247,
- 0x3250, 0x3250,
- 0x3260, 0x327f,
- 0x328a, 0x32b0,
- 0x32c0, 0x32fe,
- 0x3300, 0x33ff,
- 0x4dc0, 0x4dff,
- 0xa490, 0xa4c6,
- 0xa700, 0xa716,
- 0xa720, 0xa721,
- 0xa789, 0xa78a,
- 0xa828, 0xa82b,
- 0xa836, 0xa839,
- 0xaa77, 0xaa79,
- 0xab5b, 0xab5b,
- 0xfb29, 0xfb29,
- 0xfbb2, 0xfbc1,
- 0xfdfc, 0xfdfd,
- 0xfe62, 0xfe62,
- 0xfe64, 0xfe66,
- 0xfe69, 0xfe69,
- 0xff04, 0xff04,
- 0xff0b, 0xff0b,
- 0xff1c, 0xff1e,
- 0xff3e, 0xff3e,
- 0xff40, 0xff40,
- 0xff5c, 0xff5c,
- 0xff5e, 0xff5e,
- 0xffe0, 0xffe6,
- 0xffe8, 0xffee,
- 0xfffc, 0xfffd,
- 0x10137, 0x1013f,
- 0x10179, 0x10189,
- 0x1018c, 0x1018e,
- 0x10190, 0x1019b,
- 0x101a0, 0x101a0,
- 0x101d0, 0x101fc,
- 0x10877, 0x10878,
- 0x10ac8, 0x10ac8,
- 0x1173f, 0x1173f,
- 0x16b3c, 0x16b3f,
- 0x16b45, 0x16b45,
- 0x1bc9c, 0x1bc9c,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d164,
- 0x1d16a, 0x1d16c,
- 0x1d183, 0x1d184,
- 0x1d18c, 0x1d1a9,
- 0x1d1ae, 0x1d1e8,
- 0x1d200, 0x1d241,
- 0x1d245, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d6c1, 0x1d6c1,
- 0x1d6db, 0x1d6db,
- 0x1d6fb, 0x1d6fb,
- 0x1d715, 0x1d715,
- 0x1d735, 0x1d735,
- 0x1d74f, 0x1d74f,
- 0x1d76f, 0x1d76f,
- 0x1d789, 0x1d789,
- 0x1d7a9, 0x1d7a9,
- 0x1d7c3, 0x1d7c3,
- 0x1d800, 0x1d9ff,
- 0x1da37, 0x1da3a,
- 0x1da6d, 0x1da74,
- 0x1da76, 0x1da83,
- 0x1da85, 0x1da86,
- 0x1eef0, 0x1eef1,
- 0x1f000, 0x1f02b,
- 0x1f030, 0x1f093,
- 0x1f0a0, 0x1f0ae,
- 0x1f0b1, 0x1f0bf,
- 0x1f0c1, 0x1f0cf,
- 0x1f0d1, 0x1f0f5,
- 0x1f110, 0x1f12e,
- 0x1f130, 0x1f16b,
- 0x1f170, 0x1f1ac,
- 0x1f1e6, 0x1f202,
- 0x1f210, 0x1f23b,
- 0x1f240, 0x1f248,
- 0x1f250, 0x1f251,
- 0x1f300, 0x1f6d2,
- 0x1f6e0, 0x1f6ec,
- 0x1f6f0, 0x1f6f6,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d4,
- 0x1f800, 0x1f80b,
- 0x1f810, 0x1f847,
- 0x1f850, 0x1f859,
- 0x1f860, 0x1f887,
- 0x1f890, 0x1f8ad,
- 0x1f910, 0x1f91e,
- 0x1f920, 0x1f927,
- 0x1f930, 0x1f930,
- 0x1f933, 0x1f93e,
- 0x1f940, 0x1f94b,
- 0x1f950, 0x1f95e,
- 0x1f980, 0x1f991,
- 0x1f9c0, 0x1f9c0,
-}; /* CR_S */
-
-/* 'Sc': General Category */
-static const OnigCodePoint CR_Sc[] = {
- 17,
- 0x0024, 0x0024,
- 0x00a2, 0x00a5,
- 0x058f, 0x058f,
- 0x060b, 0x060b,
- 0x09f2, 0x09f3,
- 0x09fb, 0x09fb,
- 0x0af1, 0x0af1,
- 0x0bf9, 0x0bf9,
- 0x0e3f, 0x0e3f,
- 0x17db, 0x17db,
- 0x20a0, 0x20be,
- 0xa838, 0xa838,
- 0xfdfc, 0xfdfc,
- 0xfe69, 0xfe69,
- 0xff04, 0xff04,
- 0xffe0, 0xffe1,
- 0xffe5, 0xffe6,
-}; /* CR_Sc */
-
-/* 'Sk': General Category */
-static const OnigCodePoint CR_Sk[] = {
- 29,
- 0x005e, 0x005e,
- 0x0060, 0x0060,
- 0x00a8, 0x00a8,
- 0x00af, 0x00af,
- 0x00b4, 0x00b4,
- 0x00b8, 0x00b8,
- 0x02c2, 0x02c5,
- 0x02d2, 0x02df,
- 0x02e5, 0x02eb,
- 0x02ed, 0x02ed,
- 0x02ef, 0x02ff,
- 0x0375, 0x0375,
- 0x0384, 0x0385,
- 0x1fbd, 0x1fbd,
- 0x1fbf, 0x1fc1,
- 0x1fcd, 0x1fcf,
- 0x1fdd, 0x1fdf,
- 0x1fed, 0x1fef,
- 0x1ffd, 0x1ffe,
- 0x309b, 0x309c,
- 0xa700, 0xa716,
- 0xa720, 0xa721,
- 0xa789, 0xa78a,
- 0xab5b, 0xab5b,
- 0xfbb2, 0xfbc1,
- 0xff3e, 0xff3e,
- 0xff40, 0xff40,
- 0xffe3, 0xffe3,
- 0x1f3fb, 0x1f3ff,
-}; /* CR_Sk */
-
-/* 'Sm': General Category */
-static const OnigCodePoint CR_Sm[] = {
- 64,
- 0x002b, 0x002b,
- 0x003c, 0x003e,
- 0x007c, 0x007c,
- 0x007e, 0x007e,
- 0x00ac, 0x00ac,
- 0x00b1, 0x00b1,
- 0x00d7, 0x00d7,
- 0x00f7, 0x00f7,
- 0x03f6, 0x03f6,
- 0x0606, 0x0608,
- 0x2044, 0x2044,
- 0x2052, 0x2052,
- 0x207a, 0x207c,
- 0x208a, 0x208c,
- 0x2118, 0x2118,
- 0x2140, 0x2144,
- 0x214b, 0x214b,
- 0x2190, 0x2194,
- 0x219a, 0x219b,
- 0x21a0, 0x21a0,
- 0x21a3, 0x21a3,
- 0x21a6, 0x21a6,
- 0x21ae, 0x21ae,
- 0x21ce, 0x21cf,
- 0x21d2, 0x21d2,
- 0x21d4, 0x21d4,
- 0x21f4, 0x22ff,
- 0x2320, 0x2321,
- 0x237c, 0x237c,
- 0x239b, 0x23b3,
- 0x23dc, 0x23e1,
- 0x25b7, 0x25b7,
- 0x25c1, 0x25c1,
- 0x25f8, 0x25ff,
- 0x266f, 0x266f,
- 0x27c0, 0x27c4,
- 0x27c7, 0x27e5,
- 0x27f0, 0x27ff,
- 0x2900, 0x2982,
- 0x2999, 0x29d7,
- 0x29dc, 0x29fb,
- 0x29fe, 0x2aff,
- 0x2b30, 0x2b44,
- 0x2b47, 0x2b4c,
- 0xfb29, 0xfb29,
- 0xfe62, 0xfe62,
- 0xfe64, 0xfe66,
- 0xff0b, 0xff0b,
- 0xff1c, 0xff1e,
- 0xff5c, 0xff5c,
- 0xff5e, 0xff5e,
- 0xffe2, 0xffe2,
- 0xffe9, 0xffec,
- 0x1d6c1, 0x1d6c1,
- 0x1d6db, 0x1d6db,
- 0x1d6fb, 0x1d6fb,
- 0x1d715, 0x1d715,
- 0x1d735, 0x1d735,
- 0x1d74f, 0x1d74f,
- 0x1d76f, 0x1d76f,
- 0x1d789, 0x1d789,
- 0x1d7a9, 0x1d7a9,
- 0x1d7c3, 0x1d7c3,
- 0x1eef0, 0x1eef1,
-}; /* CR_Sm */
-
-/* 'So': General Category */
-static const OnigCodePoint CR_So[] = {
- 174,
- 0x00a6, 0x00a6,
- 0x00a9, 0x00a9,
- 0x00ae, 0x00ae,
- 0x00b0, 0x00b0,
- 0x0482, 0x0482,
- 0x058d, 0x058e,
- 0x060e, 0x060f,
- 0x06de, 0x06de,
- 0x06e9, 0x06e9,
- 0x06fd, 0x06fe,
- 0x07f6, 0x07f6,
- 0x09fa, 0x09fa,
- 0x0b70, 0x0b70,
- 0x0bf3, 0x0bf8,
- 0x0bfa, 0x0bfa,
- 0x0c7f, 0x0c7f,
- 0x0d4f, 0x0d4f,
- 0x0d79, 0x0d79,
- 0x0f01, 0x0f03,
- 0x0f13, 0x0f13,
- 0x0f15, 0x0f17,
- 0x0f1a, 0x0f1f,
- 0x0f34, 0x0f34,
- 0x0f36, 0x0f36,
- 0x0f38, 0x0f38,
- 0x0fbe, 0x0fc5,
- 0x0fc7, 0x0fcc,
- 0x0fce, 0x0fcf,
- 0x0fd5, 0x0fd8,
- 0x109e, 0x109f,
- 0x1390, 0x1399,
- 0x1940, 0x1940,
- 0x19de, 0x19ff,
- 0x1b61, 0x1b6a,
- 0x1b74, 0x1b7c,
- 0x2100, 0x2101,
- 0x2103, 0x2106,
- 0x2108, 0x2109,
- 0x2114, 0x2114,
- 0x2116, 0x2117,
- 0x211e, 0x2123,
- 0x2125, 0x2125,
- 0x2127, 0x2127,
- 0x2129, 0x2129,
- 0x212e, 0x212e,
- 0x213a, 0x213b,
- 0x214a, 0x214a,
- 0x214c, 0x214d,
- 0x214f, 0x214f,
- 0x218a, 0x218b,
- 0x2195, 0x2199,
- 0x219c, 0x219f,
- 0x21a1, 0x21a2,
- 0x21a4, 0x21a5,
- 0x21a7, 0x21ad,
- 0x21af, 0x21cd,
- 0x21d0, 0x21d1,
- 0x21d3, 0x21d3,
- 0x21d5, 0x21f3,
- 0x2300, 0x2307,
- 0x230c, 0x231f,
- 0x2322, 0x2328,
- 0x232b, 0x237b,
- 0x237d, 0x239a,
- 0x23b4, 0x23db,
- 0x23e2, 0x23fe,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x249c, 0x24e9,
- 0x2500, 0x25b6,
- 0x25b8, 0x25c0,
- 0x25c2, 0x25f7,
- 0x2600, 0x266e,
- 0x2670, 0x2767,
- 0x2794, 0x27bf,
- 0x2800, 0x28ff,
- 0x2b00, 0x2b2f,
- 0x2b45, 0x2b46,
- 0x2b4d, 0x2b73,
- 0x2b76, 0x2b95,
- 0x2b98, 0x2bb9,
- 0x2bbd, 0x2bc8,
- 0x2bca, 0x2bd1,
- 0x2bec, 0x2bef,
- 0x2ce5, 0x2cea,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3004, 0x3004,
- 0x3012, 0x3013,
- 0x3020, 0x3020,
- 0x3036, 0x3037,
- 0x303e, 0x303f,
- 0x3190, 0x3191,
- 0x3196, 0x319f,
- 0x31c0, 0x31e3,
- 0x3200, 0x321e,
- 0x322a, 0x3247,
- 0x3250, 0x3250,
- 0x3260, 0x327f,
- 0x328a, 0x32b0,
- 0x32c0, 0x32fe,
- 0x3300, 0x33ff,
- 0x4dc0, 0x4dff,
- 0xa490, 0xa4c6,
- 0xa828, 0xa82b,
- 0xa836, 0xa837,
- 0xa839, 0xa839,
- 0xaa77, 0xaa79,
- 0xfdfd, 0xfdfd,
- 0xffe4, 0xffe4,
- 0xffe8, 0xffe8,
- 0xffed, 0xffee,
- 0xfffc, 0xfffd,
- 0x10137, 0x1013f,
- 0x10179, 0x10189,
- 0x1018c, 0x1018e,
- 0x10190, 0x1019b,
- 0x101a0, 0x101a0,
- 0x101d0, 0x101fc,
- 0x10877, 0x10878,
- 0x10ac8, 0x10ac8,
- 0x1173f, 0x1173f,
- 0x16b3c, 0x16b3f,
- 0x16b45, 0x16b45,
- 0x1bc9c, 0x1bc9c,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d164,
- 0x1d16a, 0x1d16c,
- 0x1d183, 0x1d184,
- 0x1d18c, 0x1d1a9,
- 0x1d1ae, 0x1d1e8,
- 0x1d200, 0x1d241,
- 0x1d245, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d800, 0x1d9ff,
- 0x1da37, 0x1da3a,
- 0x1da6d, 0x1da74,
- 0x1da76, 0x1da83,
- 0x1da85, 0x1da86,
- 0x1f000, 0x1f02b,
- 0x1f030, 0x1f093,
- 0x1f0a0, 0x1f0ae,
- 0x1f0b1, 0x1f0bf,
- 0x1f0c1, 0x1f0cf,
- 0x1f0d1, 0x1f0f5,
- 0x1f110, 0x1f12e,
- 0x1f130, 0x1f16b,
- 0x1f170, 0x1f1ac,
- 0x1f1e6, 0x1f202,
- 0x1f210, 0x1f23b,
- 0x1f240, 0x1f248,
- 0x1f250, 0x1f251,
- 0x1f300, 0x1f3fa,
- 0x1f400, 0x1f6d2,
- 0x1f6e0, 0x1f6ec,
- 0x1f6f0, 0x1f6f6,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d4,
- 0x1f800, 0x1f80b,
- 0x1f810, 0x1f847,
- 0x1f850, 0x1f859,
- 0x1f860, 0x1f887,
- 0x1f890, 0x1f8ad,
- 0x1f910, 0x1f91e,
- 0x1f920, 0x1f927,
- 0x1f930, 0x1f930,
- 0x1f933, 0x1f93e,
- 0x1f940, 0x1f94b,
- 0x1f950, 0x1f95e,
- 0x1f980, 0x1f991,
- 0x1f9c0, 0x1f9c0,
-}; /* CR_So */
-
-/* 'Z': Major Category */
-static const OnigCodePoint CR_Z[] = {
- 8,
- 0x0020, 0x0020,
- 0x00a0, 0x00a0,
- 0x1680, 0x1680,
- 0x2000, 0x200a,
- 0x2028, 0x2029,
- 0x202f, 0x202f,
- 0x205f, 0x205f,
- 0x3000, 0x3000,
-}; /* CR_Z */
-
-/* 'Zl': General Category */
-static const OnigCodePoint CR_Zl[] = {
- 1,
- 0x2028, 0x2028,
-}; /* CR_Zl */
-
-/* 'Zp': General Category */
-static const OnigCodePoint CR_Zp[] = {
- 1,
- 0x2029, 0x2029,
-}; /* CR_Zp */
-
-/* 'Zs': General Category */
-static const OnigCodePoint CR_Zs[] = {
- 7,
- 0x0020, 0x0020,
- 0x00a0, 0x00a0,
- 0x1680, 0x1680,
- 0x2000, 0x200a,
- 0x202f, 0x202f,
- 0x205f, 0x205f,
- 0x3000, 0x3000,
-}; /* CR_Zs */
-
-/* 'Math': Derived Property */
-static const OnigCodePoint CR_Math[] = {
- 138,
- 0x002b, 0x002b,
- 0x003c, 0x003e,
- 0x005e, 0x005e,
- 0x007c, 0x007c,
- 0x007e, 0x007e,
- 0x00ac, 0x00ac,
- 0x00b1, 0x00b1,
- 0x00d7, 0x00d7,
- 0x00f7, 0x00f7,
- 0x03d0, 0x03d2,
- 0x03d5, 0x03d5,
- 0x03f0, 0x03f1,
- 0x03f4, 0x03f6,
- 0x0606, 0x0608,
- 0x2016, 0x2016,
- 0x2032, 0x2034,
- 0x2040, 0x2040,
- 0x2044, 0x2044,
- 0x2052, 0x2052,
- 0x2061, 0x2064,
- 0x207a, 0x207e,
- 0x208a, 0x208e,
- 0x20d0, 0x20dc,
- 0x20e1, 0x20e1,
- 0x20e5, 0x20e6,
- 0x20eb, 0x20ef,
- 0x2102, 0x2102,
- 0x2107, 0x2107,
- 0x210a, 0x2113,
- 0x2115, 0x2115,
- 0x2118, 0x211d,
- 0x2124, 0x2124,
- 0x2128, 0x2129,
- 0x212c, 0x212d,
- 0x212f, 0x2131,
- 0x2133, 0x2138,
- 0x213c, 0x2149,
- 0x214b, 0x214b,
- 0x2190, 0x21a7,
- 0x21a9, 0x21ae,
- 0x21b0, 0x21b1,
- 0x21b6, 0x21b7,
- 0x21bc, 0x21db,
- 0x21dd, 0x21dd,
- 0x21e4, 0x21e5,
- 0x21f4, 0x22ff,
- 0x2308, 0x230b,
- 0x2320, 0x2321,
- 0x237c, 0x237c,
- 0x239b, 0x23b5,
- 0x23b7, 0x23b7,
- 0x23d0, 0x23d0,
- 0x23dc, 0x23e2,
- 0x25a0, 0x25a1,
- 0x25ae, 0x25b7,
- 0x25bc, 0x25c1,
- 0x25c6, 0x25c7,
- 0x25ca, 0x25cb,
- 0x25cf, 0x25d3,
- 0x25e2, 0x25e2,
- 0x25e4, 0x25e4,
- 0x25e7, 0x25ec,
- 0x25f8, 0x25ff,
- 0x2605, 0x2606,
- 0x2640, 0x2640,
- 0x2642, 0x2642,
- 0x2660, 0x2663,
- 0x266d, 0x266f,
- 0x27c0, 0x27ff,
- 0x2900, 0x2aff,
- 0x2b30, 0x2b44,
- 0x2b47, 0x2b4c,
- 0xfb29, 0xfb29,
- 0xfe61, 0xfe66,
- 0xfe68, 0xfe68,
- 0xff0b, 0xff0b,
- 0xff1c, 0xff1e,
- 0xff3c, 0xff3c,
- 0xff3e, 0xff3e,
- 0xff5c, 0xff5c,
- 0xff5e, 0xff5e,
- 0xffe2, 0xffe2,
- 0xffe9, 0xffec,
- 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, 0x1d7ff,
- 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,
-}; /* CR_Math */
-
-/* 'Alphabetic': Derived Property */
-#define CR_Alphabetic CR_Alpha
-
-/* 'Lowercase': Derived Property */
-#define CR_Lowercase CR_Lower
-
-/* 'Uppercase': Derived Property */
-#define CR_Uppercase CR_Upper
-
-/* 'Cased': Derived Property */
-static const OnigCodePoint CR_Cased[] = {
- 135,
- 0x0041, 0x005a,
- 0x0061, 0x007a,
- 0x00aa, 0x00aa,
- 0x00b5, 0x00b5,
- 0x00ba, 0x00ba,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00f6,
- 0x00f8, 0x01ba,
- 0x01bc, 0x01bf,
- 0x01c4, 0x0293,
- 0x0295, 0x02b8,
- 0x02c0, 0x02c1,
- 0x02e0, 0x02e4,
- 0x0345, 0x0345,
- 0x0370, 0x0373,
- 0x0376, 0x0377,
- 0x037a, 0x037d,
- 0x037f, 0x037f,
- 0x0386, 0x0386,
- 0x0388, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03f5,
- 0x03f7, 0x0481,
- 0x048a, 0x052f,
- 0x0531, 0x0556,
- 0x0561, 0x0587,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x13a0, 0x13f5,
- 0x13f8, 0x13fd,
- 0x1c80, 0x1c88,
- 0x1d00, 0x1dbf,
- 0x1e00, 0x1f15,
- 0x1f18, 0x1f1d,
- 0x1f20, 0x1f45,
- 0x1f48, 0x1f4d,
- 0x1f50, 0x1f57,
- 0x1f59, 0x1f59,
- 0x1f5b, 0x1f5b,
- 0x1f5d, 0x1f5d,
- 0x1f5f, 0x1f7d,
- 0x1f80, 0x1fb4,
- 0x1fb6, 0x1fbc,
- 0x1fbe, 0x1fbe,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fcc,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fdb,
- 0x1fe0, 0x1fec,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ffc,
- 0x2071, 0x2071,
- 0x207f, 0x207f,
- 0x2090, 0x209c,
- 0x2102, 0x2102,
- 0x2107, 0x2107,
- 0x210a, 0x2113,
- 0x2115, 0x2115,
- 0x2119, 0x211d,
- 0x2124, 0x2124,
- 0x2126, 0x2126,
- 0x2128, 0x2128,
- 0x212a, 0x212d,
- 0x212f, 0x2134,
- 0x2139, 0x2139,
- 0x213c, 0x213f,
- 0x2145, 0x2149,
- 0x214e, 0x214e,
- 0x2160, 0x217f,
- 0x2183, 0x2184,
- 0x24b6, 0x24e9,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2ce4,
- 0x2ceb, 0x2cee,
- 0x2cf2, 0x2cf3,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0xa640, 0xa66d,
- 0xa680, 0xa69d,
- 0xa722, 0xa787,
- 0xa78b, 0xa78e,
- 0xa790, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7f8, 0xa7fa,
- 0xab30, 0xab5a,
- 0xab5c, 0xab65,
- 0xab70, 0xabbf,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xff21, 0xff3a,
- 0xff41, 0xff5a,
- 0x10400, 0x1044f,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10c80, 0x10cb2,
- 0x10cc0, 0x10cf2,
- 0x118a0, 0x118df,
- 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, 0x1d6c0,
- 0x1d6c2, 0x1d6da,
- 0x1d6dc, 0x1d6fa,
- 0x1d6fc, 0x1d714,
- 0x1d716, 0x1d734,
- 0x1d736, 0x1d74e,
- 0x1d750, 0x1d76e,
- 0x1d770, 0x1d788,
- 0x1d78a, 0x1d7a8,
- 0x1d7aa, 0x1d7c2,
- 0x1d7c4, 0x1d7cb,
- 0x1e900, 0x1e943,
- 0x1f130, 0x1f149,
- 0x1f150, 0x1f169,
- 0x1f170, 0x1f189,
-}; /* CR_Cased */
-
-/* 'Case_Ignorable': Derived Property */
-static const OnigCodePoint CR_Case_Ignorable[] = {
- 365,
- 0x0027, 0x0027,
- 0x002e, 0x002e,
- 0x003a, 0x003a,
- 0x005e, 0x005e,
- 0x0060, 0x0060,
- 0x00a8, 0x00a8,
- 0x00ad, 0x00ad,
- 0x00af, 0x00af,
- 0x00b4, 0x00b4,
- 0x00b7, 0x00b8,
- 0x02b0, 0x036f,
- 0x0374, 0x0375,
- 0x037a, 0x037a,
- 0x0384, 0x0385,
- 0x0387, 0x0387,
- 0x0483, 0x0489,
- 0x0559, 0x0559,
- 0x0591, 0x05bd,
- 0x05bf, 0x05bf,
- 0x05c1, 0x05c2,
- 0x05c4, 0x05c5,
- 0x05c7, 0x05c7,
- 0x05f4, 0x05f4,
- 0x0600, 0x0605,
- 0x0610, 0x061a,
- 0x061c, 0x061c,
- 0x0640, 0x0640,
- 0x064b, 0x065f,
- 0x0670, 0x0670,
- 0x06d6, 0x06dd,
- 0x06df, 0x06e8,
- 0x06ea, 0x06ed,
- 0x070f, 0x070f,
- 0x0711, 0x0711,
- 0x0730, 0x074a,
- 0x07a6, 0x07b0,
- 0x07eb, 0x07f5,
- 0x07fa, 0x07fa,
- 0x0816, 0x082d,
- 0x0859, 0x085b,
- 0x08d4, 0x0902,
- 0x093a, 0x093a,
- 0x093c, 0x093c,
- 0x0941, 0x0948,
- 0x094d, 0x094d,
- 0x0951, 0x0957,
- 0x0962, 0x0963,
- 0x0971, 0x0971,
- 0x0981, 0x0981,
- 0x09bc, 0x09bc,
- 0x09c1, 0x09c4,
- 0x09cd, 0x09cd,
- 0x09e2, 0x09e3,
- 0x0a01, 0x0a02,
- 0x0a3c, 0x0a3c,
- 0x0a41, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4d,
- 0x0a51, 0x0a51,
- 0x0a70, 0x0a71,
- 0x0a75, 0x0a75,
- 0x0a81, 0x0a82,
- 0x0abc, 0x0abc,
- 0x0ac1, 0x0ac5,
- 0x0ac7, 0x0ac8,
- 0x0acd, 0x0acd,
- 0x0ae2, 0x0ae3,
- 0x0b01, 0x0b01,
- 0x0b3c, 0x0b3c,
- 0x0b3f, 0x0b3f,
- 0x0b41, 0x0b44,
- 0x0b4d, 0x0b4d,
- 0x0b56, 0x0b56,
- 0x0b62, 0x0b63,
- 0x0b82, 0x0b82,
- 0x0bc0, 0x0bc0,
- 0x0bcd, 0x0bcd,
- 0x0c00, 0x0c00,
- 0x0c3e, 0x0c40,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c62, 0x0c63,
- 0x0c81, 0x0c81,
- 0x0cbc, 0x0cbc,
- 0x0cbf, 0x0cbf,
- 0x0cc6, 0x0cc6,
- 0x0ccc, 0x0ccd,
- 0x0ce2, 0x0ce3,
- 0x0d01, 0x0d01,
- 0x0d41, 0x0d44,
- 0x0d4d, 0x0d4d,
- 0x0d62, 0x0d63,
- 0x0dca, 0x0dca,
- 0x0dd2, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0e31, 0x0e31,
- 0x0e34, 0x0e3a,
- 0x0e46, 0x0e4e,
- 0x0eb1, 0x0eb1,
- 0x0eb4, 0x0eb9,
- 0x0ebb, 0x0ebc,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0f18, 0x0f19,
- 0x0f35, 0x0f35,
- 0x0f37, 0x0f37,
- 0x0f39, 0x0f39,
- 0x0f71, 0x0f7e,
- 0x0f80, 0x0f84,
- 0x0f86, 0x0f87,
- 0x0f8d, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fc6, 0x0fc6,
- 0x102d, 0x1030,
- 0x1032, 0x1037,
- 0x1039, 0x103a,
- 0x103d, 0x103e,
- 0x1058, 0x1059,
- 0x105e, 0x1060,
- 0x1071, 0x1074,
- 0x1082, 0x1082,
- 0x1085, 0x1086,
- 0x108d, 0x108d,
- 0x109d, 0x109d,
- 0x10fc, 0x10fc,
- 0x135d, 0x135f,
- 0x1712, 0x1714,
- 0x1732, 0x1734,
- 0x1752, 0x1753,
- 0x1772, 0x1773,
- 0x17b4, 0x17b5,
- 0x17b7, 0x17bd,
- 0x17c6, 0x17c6,
- 0x17c9, 0x17d3,
- 0x17d7, 0x17d7,
- 0x17dd, 0x17dd,
- 0x180b, 0x180e,
- 0x1843, 0x1843,
- 0x1885, 0x1886,
- 0x18a9, 0x18a9,
- 0x1920, 0x1922,
- 0x1927, 0x1928,
- 0x1932, 0x1932,
- 0x1939, 0x193b,
- 0x1a17, 0x1a18,
- 0x1a1b, 0x1a1b,
- 0x1a56, 0x1a56,
- 0x1a58, 0x1a5e,
- 0x1a60, 0x1a60,
- 0x1a62, 0x1a62,
- 0x1a65, 0x1a6c,
- 0x1a73, 0x1a7c,
- 0x1a7f, 0x1a7f,
- 0x1aa7, 0x1aa7,
- 0x1ab0, 0x1abe,
- 0x1b00, 0x1b03,
- 0x1b34, 0x1b34,
- 0x1b36, 0x1b3a,
- 0x1b3c, 0x1b3c,
- 0x1b42, 0x1b42,
- 0x1b6b, 0x1b73,
- 0x1b80, 0x1b81,
- 0x1ba2, 0x1ba5,
- 0x1ba8, 0x1ba9,
- 0x1bab, 0x1bad,
- 0x1be6, 0x1be6,
- 0x1be8, 0x1be9,
- 0x1bed, 0x1bed,
- 0x1bef, 0x1bf1,
- 0x1c2c, 0x1c33,
- 0x1c36, 0x1c37,
- 0x1c78, 0x1c7d,
- 0x1cd0, 0x1cd2,
- 0x1cd4, 0x1ce0,
- 0x1ce2, 0x1ce8,
- 0x1ced, 0x1ced,
- 0x1cf4, 0x1cf4,
- 0x1cf8, 0x1cf9,
- 0x1d2c, 0x1d6a,
- 0x1d78, 0x1d78,
- 0x1d9b, 0x1df5,
- 0x1dfb, 0x1dff,
- 0x1fbd, 0x1fbd,
- 0x1fbf, 0x1fc1,
- 0x1fcd, 0x1fcf,
- 0x1fdd, 0x1fdf,
- 0x1fed, 0x1fef,
- 0x1ffd, 0x1ffe,
- 0x200b, 0x200f,
- 0x2018, 0x2019,
- 0x2024, 0x2024,
- 0x2027, 0x2027,
- 0x202a, 0x202e,
- 0x2060, 0x2064,
- 0x2066, 0x206f,
- 0x2071, 0x2071,
- 0x207f, 0x207f,
- 0x2090, 0x209c,
- 0x20d0, 0x20f0,
- 0x2c7c, 0x2c7d,
- 0x2cef, 0x2cf1,
- 0x2d6f, 0x2d6f,
- 0x2d7f, 0x2d7f,
- 0x2de0, 0x2dff,
- 0x2e2f, 0x2e2f,
- 0x3005, 0x3005,
- 0x302a, 0x302d,
- 0x3031, 0x3035,
- 0x303b, 0x303b,
- 0x3099, 0x309e,
- 0x30fc, 0x30fe,
- 0xa015, 0xa015,
- 0xa4f8, 0xa4fd,
- 0xa60c, 0xa60c,
- 0xa66f, 0xa672,
- 0xa674, 0xa67d,
- 0xa67f, 0xa67f,
- 0xa69c, 0xa69f,
- 0xa6f0, 0xa6f1,
- 0xa700, 0xa721,
- 0xa770, 0xa770,
- 0xa788, 0xa78a,
- 0xa7f8, 0xa7f9,
- 0xa802, 0xa802,
- 0xa806, 0xa806,
- 0xa80b, 0xa80b,
- 0xa825, 0xa826,
- 0xa8c4, 0xa8c5,
- 0xa8e0, 0xa8f1,
- 0xa926, 0xa92d,
- 0xa947, 0xa951,
- 0xa980, 0xa982,
- 0xa9b3, 0xa9b3,
- 0xa9b6, 0xa9b9,
- 0xa9bc, 0xa9bc,
- 0xa9cf, 0xa9cf,
- 0xa9e5, 0xa9e6,
- 0xaa29, 0xaa2e,
- 0xaa31, 0xaa32,
- 0xaa35, 0xaa36,
- 0xaa43, 0xaa43,
- 0xaa4c, 0xaa4c,
- 0xaa70, 0xaa70,
- 0xaa7c, 0xaa7c,
- 0xaab0, 0xaab0,
- 0xaab2, 0xaab4,
- 0xaab7, 0xaab8,
- 0xaabe, 0xaabf,
- 0xaac1, 0xaac1,
- 0xaadd, 0xaadd,
- 0xaaec, 0xaaed,
- 0xaaf3, 0xaaf4,
- 0xaaf6, 0xaaf6,
- 0xab5b, 0xab5f,
- 0xabe5, 0xabe5,
- 0xabe8, 0xabe8,
- 0xabed, 0xabed,
- 0xfb1e, 0xfb1e,
- 0xfbb2, 0xfbc1,
- 0xfe00, 0xfe0f,
- 0xfe13, 0xfe13,
- 0xfe20, 0xfe2f,
- 0xfe52, 0xfe52,
- 0xfe55, 0xfe55,
- 0xfeff, 0xfeff,
- 0xff07, 0xff07,
- 0xff0e, 0xff0e,
- 0xff1a, 0xff1a,
- 0xff3e, 0xff3e,
- 0xff40, 0xff40,
- 0xff70, 0xff70,
- 0xff9e, 0xff9f,
- 0xffe3, 0xffe3,
- 0xfff9, 0xfffb,
- 0x101fd, 0x101fd,
- 0x102e0, 0x102e0,
- 0x10376, 0x1037a,
- 0x10a01, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a0f,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a3f,
- 0x10ae5, 0x10ae6,
- 0x11001, 0x11001,
- 0x11038, 0x11046,
- 0x1107f, 0x11081,
- 0x110b3, 0x110b6,
- 0x110b9, 0x110ba,
- 0x110bd, 0x110bd,
- 0x11100, 0x11102,
- 0x11127, 0x1112b,
- 0x1112d, 0x11134,
- 0x11173, 0x11173,
- 0x11180, 0x11181,
- 0x111b6, 0x111be,
- 0x111ca, 0x111cc,
- 0x1122f, 0x11231,
- 0x11234, 0x11234,
- 0x11236, 0x11237,
- 0x1123e, 0x1123e,
- 0x112df, 0x112df,
- 0x112e3, 0x112ea,
- 0x11300, 0x11301,
- 0x1133c, 0x1133c,
- 0x11340, 0x11340,
- 0x11366, 0x1136c,
- 0x11370, 0x11374,
- 0x11438, 0x1143f,
- 0x11442, 0x11444,
- 0x11446, 0x11446,
- 0x114b3, 0x114b8,
- 0x114ba, 0x114ba,
- 0x114bf, 0x114c0,
- 0x114c2, 0x114c3,
- 0x115b2, 0x115b5,
- 0x115bc, 0x115bd,
- 0x115bf, 0x115c0,
- 0x115dc, 0x115dd,
- 0x11633, 0x1163a,
- 0x1163d, 0x1163d,
- 0x1163f, 0x11640,
- 0x116ab, 0x116ab,
- 0x116ad, 0x116ad,
- 0x116b0, 0x116b5,
- 0x116b7, 0x116b7,
- 0x1171d, 0x1171f,
- 0x11722, 0x11725,
- 0x11727, 0x1172b,
- 0x11c30, 0x11c36,
- 0x11c38, 0x11c3d,
- 0x11c3f, 0x11c3f,
- 0x11c92, 0x11ca7,
- 0x11caa, 0x11cb0,
- 0x11cb2, 0x11cb3,
- 0x11cb5, 0x11cb6,
- 0x16af0, 0x16af4,
- 0x16b30, 0x16b36,
- 0x16b40, 0x16b43,
- 0x16f8f, 0x16f9f,
- 0x16fe0, 0x16fe0,
- 0x1bc9d, 0x1bc9e,
- 0x1bca0, 0x1bca3,
- 0x1d167, 0x1d169,
- 0x1d173, 0x1d182,
- 0x1d185, 0x1d18b,
- 0x1d1aa, 0x1d1ad,
- 0x1d242, 0x1d244,
- 0x1da00, 0x1da36,
- 0x1da3b, 0x1da6c,
- 0x1da75, 0x1da75,
- 0x1da84, 0x1da84,
- 0x1da9b, 0x1da9f,
- 0x1daa1, 0x1daaf,
- 0x1e000, 0x1e006,
- 0x1e008, 0x1e018,
- 0x1e01b, 0x1e021,
- 0x1e023, 0x1e024,
- 0x1e026, 0x1e02a,
- 0x1e8d0, 0x1e8d6,
- 0x1e944, 0x1e94a,
- 0x1f3fb, 0x1f3ff,
- 0xe0001, 0xe0001,
- 0xe0020, 0xe007f,
- 0xe0100, 0xe01ef,
-}; /* CR_Case_Ignorable */
-
-/* 'Changes_When_Lowercased': Derived Property */
-static const OnigCodePoint CR_Changes_When_Lowercased[] = {
- 590,
- 0x0041, 0x005a,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00de,
- 0x0100, 0x0100,
- 0x0102, 0x0102,
- 0x0104, 0x0104,
- 0x0106, 0x0106,
- 0x0108, 0x0108,
- 0x010a, 0x010a,
- 0x010c, 0x010c,
- 0x010e, 0x010e,
- 0x0110, 0x0110,
- 0x0112, 0x0112,
- 0x0114, 0x0114,
- 0x0116, 0x0116,
- 0x0118, 0x0118,
- 0x011a, 0x011a,
- 0x011c, 0x011c,
- 0x011e, 0x011e,
- 0x0120, 0x0120,
- 0x0122, 0x0122,
- 0x0124, 0x0124,
- 0x0126, 0x0126,
- 0x0128, 0x0128,
- 0x012a, 0x012a,
- 0x012c, 0x012c,
- 0x012e, 0x012e,
- 0x0130, 0x0130,
- 0x0132, 0x0132,
- 0x0134, 0x0134,
- 0x0136, 0x0136,
- 0x0139, 0x0139,
- 0x013b, 0x013b,
- 0x013d, 0x013d,
- 0x013f, 0x013f,
- 0x0141, 0x0141,
- 0x0143, 0x0143,
- 0x0145, 0x0145,
- 0x0147, 0x0147,
- 0x014a, 0x014a,
- 0x014c, 0x014c,
- 0x014e, 0x014e,
- 0x0150, 0x0150,
- 0x0152, 0x0152,
- 0x0154, 0x0154,
- 0x0156, 0x0156,
- 0x0158, 0x0158,
- 0x015a, 0x015a,
- 0x015c, 0x015c,
- 0x015e, 0x015e,
- 0x0160, 0x0160,
- 0x0162, 0x0162,
- 0x0164, 0x0164,
- 0x0166, 0x0166,
- 0x0168, 0x0168,
- 0x016a, 0x016a,
- 0x016c, 0x016c,
- 0x016e, 0x016e,
- 0x0170, 0x0170,
- 0x0172, 0x0172,
- 0x0174, 0x0174,
- 0x0176, 0x0176,
- 0x0178, 0x0179,
- 0x017b, 0x017b,
- 0x017d, 0x017d,
- 0x0181, 0x0182,
- 0x0184, 0x0184,
- 0x0186, 0x0187,
- 0x0189, 0x018b,
- 0x018e, 0x0191,
- 0x0193, 0x0194,
- 0x0196, 0x0198,
- 0x019c, 0x019d,
- 0x019f, 0x01a0,
- 0x01a2, 0x01a2,
- 0x01a4, 0x01a4,
- 0x01a6, 0x01a7,
- 0x01a9, 0x01a9,
- 0x01ac, 0x01ac,
- 0x01ae, 0x01af,
- 0x01b1, 0x01b3,
- 0x01b5, 0x01b5,
- 0x01b7, 0x01b8,
- 0x01bc, 0x01bc,
- 0x01c4, 0x01c5,
- 0x01c7, 0x01c8,
- 0x01ca, 0x01cb,
- 0x01cd, 0x01cd,
- 0x01cf, 0x01cf,
- 0x01d1, 0x01d1,
- 0x01d3, 0x01d3,
- 0x01d5, 0x01d5,
- 0x01d7, 0x01d7,
- 0x01d9, 0x01d9,
- 0x01db, 0x01db,
- 0x01de, 0x01de,
- 0x01e0, 0x01e0,
- 0x01e2, 0x01e2,
- 0x01e4, 0x01e4,
- 0x01e6, 0x01e6,
- 0x01e8, 0x01e8,
- 0x01ea, 0x01ea,
- 0x01ec, 0x01ec,
- 0x01ee, 0x01ee,
- 0x01f1, 0x01f2,
- 0x01f4, 0x01f4,
- 0x01f6, 0x01f8,
- 0x01fa, 0x01fa,
- 0x01fc, 0x01fc,
- 0x01fe, 0x01fe,
- 0x0200, 0x0200,
- 0x0202, 0x0202,
- 0x0204, 0x0204,
- 0x0206, 0x0206,
- 0x0208, 0x0208,
- 0x020a, 0x020a,
- 0x020c, 0x020c,
- 0x020e, 0x020e,
- 0x0210, 0x0210,
- 0x0212, 0x0212,
- 0x0214, 0x0214,
- 0x0216, 0x0216,
- 0x0218, 0x0218,
- 0x021a, 0x021a,
- 0x021c, 0x021c,
- 0x021e, 0x021e,
- 0x0220, 0x0220,
- 0x0222, 0x0222,
- 0x0224, 0x0224,
- 0x0226, 0x0226,
- 0x0228, 0x0228,
- 0x022a, 0x022a,
- 0x022c, 0x022c,
- 0x022e, 0x022e,
- 0x0230, 0x0230,
- 0x0232, 0x0232,
- 0x023a, 0x023b,
- 0x023d, 0x023e,
- 0x0241, 0x0241,
- 0x0243, 0x0246,
- 0x0248, 0x0248,
- 0x024a, 0x024a,
- 0x024c, 0x024c,
- 0x024e, 0x024e,
- 0x0370, 0x0370,
- 0x0372, 0x0372,
- 0x0376, 0x0376,
- 0x037f, 0x037f,
- 0x0386, 0x0386,
- 0x0388, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x038f,
- 0x0391, 0x03a1,
- 0x03a3, 0x03ab,
- 0x03cf, 0x03cf,
- 0x03d8, 0x03d8,
- 0x03da, 0x03da,
- 0x03dc, 0x03dc,
- 0x03de, 0x03de,
- 0x03e0, 0x03e0,
- 0x03e2, 0x03e2,
- 0x03e4, 0x03e4,
- 0x03e6, 0x03e6,
- 0x03e8, 0x03e8,
- 0x03ea, 0x03ea,
- 0x03ec, 0x03ec,
- 0x03ee, 0x03ee,
- 0x03f4, 0x03f4,
- 0x03f7, 0x03f7,
- 0x03f9, 0x03fa,
- 0x03fd, 0x042f,
- 0x0460, 0x0460,
- 0x0462, 0x0462,
- 0x0464, 0x0464,
- 0x0466, 0x0466,
- 0x0468, 0x0468,
- 0x046a, 0x046a,
- 0x046c, 0x046c,
- 0x046e, 0x046e,
- 0x0470, 0x0470,
- 0x0472, 0x0472,
- 0x0474, 0x0474,
- 0x0476, 0x0476,
- 0x0478, 0x0478,
- 0x047a, 0x047a,
- 0x047c, 0x047c,
- 0x047e, 0x047e,
- 0x0480, 0x0480,
- 0x048a, 0x048a,
- 0x048c, 0x048c,
- 0x048e, 0x048e,
- 0x0490, 0x0490,
- 0x0492, 0x0492,
- 0x0494, 0x0494,
- 0x0496, 0x0496,
- 0x0498, 0x0498,
- 0x049a, 0x049a,
- 0x049c, 0x049c,
- 0x049e, 0x049e,
- 0x04a0, 0x04a0,
- 0x04a2, 0x04a2,
- 0x04a4, 0x04a4,
- 0x04a6, 0x04a6,
- 0x04a8, 0x04a8,
- 0x04aa, 0x04aa,
- 0x04ac, 0x04ac,
- 0x04ae, 0x04ae,
- 0x04b0, 0x04b0,
- 0x04b2, 0x04b2,
- 0x04b4, 0x04b4,
- 0x04b6, 0x04b6,
- 0x04b8, 0x04b8,
- 0x04ba, 0x04ba,
- 0x04bc, 0x04bc,
- 0x04be, 0x04be,
- 0x04c0, 0x04c1,
- 0x04c3, 0x04c3,
- 0x04c5, 0x04c5,
- 0x04c7, 0x04c7,
- 0x04c9, 0x04c9,
- 0x04cb, 0x04cb,
- 0x04cd, 0x04cd,
- 0x04d0, 0x04d0,
- 0x04d2, 0x04d2,
- 0x04d4, 0x04d4,
- 0x04d6, 0x04d6,
- 0x04d8, 0x04d8,
- 0x04da, 0x04da,
- 0x04dc, 0x04dc,
- 0x04de, 0x04de,
- 0x04e0, 0x04e0,
- 0x04e2, 0x04e2,
- 0x04e4, 0x04e4,
- 0x04e6, 0x04e6,
- 0x04e8, 0x04e8,
- 0x04ea, 0x04ea,
- 0x04ec, 0x04ec,
- 0x04ee, 0x04ee,
- 0x04f0, 0x04f0,
- 0x04f2, 0x04f2,
- 0x04f4, 0x04f4,
- 0x04f6, 0x04f6,
- 0x04f8, 0x04f8,
- 0x04fa, 0x04fa,
- 0x04fc, 0x04fc,
- 0x04fe, 0x04fe,
- 0x0500, 0x0500,
- 0x0502, 0x0502,
- 0x0504, 0x0504,
- 0x0506, 0x0506,
- 0x0508, 0x0508,
- 0x050a, 0x050a,
- 0x050c, 0x050c,
- 0x050e, 0x050e,
- 0x0510, 0x0510,
- 0x0512, 0x0512,
- 0x0514, 0x0514,
- 0x0516, 0x0516,
- 0x0518, 0x0518,
- 0x051a, 0x051a,
- 0x051c, 0x051c,
- 0x051e, 0x051e,
- 0x0520, 0x0520,
- 0x0522, 0x0522,
- 0x0524, 0x0524,
- 0x0526, 0x0526,
- 0x0528, 0x0528,
- 0x052a, 0x052a,
- 0x052c, 0x052c,
- 0x052e, 0x052e,
- 0x0531, 0x0556,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x13a0, 0x13f5,
- 0x1e00, 0x1e00,
- 0x1e02, 0x1e02,
- 0x1e04, 0x1e04,
- 0x1e06, 0x1e06,
- 0x1e08, 0x1e08,
- 0x1e0a, 0x1e0a,
- 0x1e0c, 0x1e0c,
- 0x1e0e, 0x1e0e,
- 0x1e10, 0x1e10,
- 0x1e12, 0x1e12,
- 0x1e14, 0x1e14,
- 0x1e16, 0x1e16,
- 0x1e18, 0x1e18,
- 0x1e1a, 0x1e1a,
- 0x1e1c, 0x1e1c,
- 0x1e1e, 0x1e1e,
- 0x1e20, 0x1e20,
- 0x1e22, 0x1e22,
- 0x1e24, 0x1e24,
- 0x1e26, 0x1e26,
- 0x1e28, 0x1e28,
- 0x1e2a, 0x1e2a,
- 0x1e2c, 0x1e2c,
- 0x1e2e, 0x1e2e,
- 0x1e30, 0x1e30,
- 0x1e32, 0x1e32,
- 0x1e34, 0x1e34,
- 0x1e36, 0x1e36,
- 0x1e38, 0x1e38,
- 0x1e3a, 0x1e3a,
- 0x1e3c, 0x1e3c,
- 0x1e3e, 0x1e3e,
- 0x1e40, 0x1e40,
- 0x1e42, 0x1e42,
- 0x1e44, 0x1e44,
- 0x1e46, 0x1e46,
- 0x1e48, 0x1e48,
- 0x1e4a, 0x1e4a,
- 0x1e4c, 0x1e4c,
- 0x1e4e, 0x1e4e,
- 0x1e50, 0x1e50,
- 0x1e52, 0x1e52,
- 0x1e54, 0x1e54,
- 0x1e56, 0x1e56,
- 0x1e58, 0x1e58,
- 0x1e5a, 0x1e5a,
- 0x1e5c, 0x1e5c,
- 0x1e5e, 0x1e5e,
- 0x1e60, 0x1e60,
- 0x1e62, 0x1e62,
- 0x1e64, 0x1e64,
- 0x1e66, 0x1e66,
- 0x1e68, 0x1e68,
- 0x1e6a, 0x1e6a,
- 0x1e6c, 0x1e6c,
- 0x1e6e, 0x1e6e,
- 0x1e70, 0x1e70,
- 0x1e72, 0x1e72,
- 0x1e74, 0x1e74,
- 0x1e76, 0x1e76,
- 0x1e78, 0x1e78,
- 0x1e7a, 0x1e7a,
- 0x1e7c, 0x1e7c,
- 0x1e7e, 0x1e7e,
- 0x1e80, 0x1e80,
- 0x1e82, 0x1e82,
- 0x1e84, 0x1e84,
- 0x1e86, 0x1e86,
- 0x1e88, 0x1e88,
- 0x1e8a, 0x1e8a,
- 0x1e8c, 0x1e8c,
- 0x1e8e, 0x1e8e,
- 0x1e90, 0x1e90,
- 0x1e92, 0x1e92,
- 0x1e94, 0x1e94,
- 0x1e9e, 0x1e9e,
- 0x1ea0, 0x1ea0,
- 0x1ea2, 0x1ea2,
- 0x1ea4, 0x1ea4,
- 0x1ea6, 0x1ea6,
- 0x1ea8, 0x1ea8,
- 0x1eaa, 0x1eaa,
- 0x1eac, 0x1eac,
- 0x1eae, 0x1eae,
- 0x1eb0, 0x1eb0,
- 0x1eb2, 0x1eb2,
- 0x1eb4, 0x1eb4,
- 0x1eb6, 0x1eb6,
- 0x1eb8, 0x1eb8,
- 0x1eba, 0x1eba,
- 0x1ebc, 0x1ebc,
- 0x1ebe, 0x1ebe,
- 0x1ec0, 0x1ec0,
- 0x1ec2, 0x1ec2,
- 0x1ec4, 0x1ec4,
- 0x1ec6, 0x1ec6,
- 0x1ec8, 0x1ec8,
- 0x1eca, 0x1eca,
- 0x1ecc, 0x1ecc,
- 0x1ece, 0x1ece,
- 0x1ed0, 0x1ed0,
- 0x1ed2, 0x1ed2,
- 0x1ed4, 0x1ed4,
- 0x1ed6, 0x1ed6,
- 0x1ed8, 0x1ed8,
- 0x1eda, 0x1eda,
- 0x1edc, 0x1edc,
- 0x1ede, 0x1ede,
- 0x1ee0, 0x1ee0,
- 0x1ee2, 0x1ee2,
- 0x1ee4, 0x1ee4,
- 0x1ee6, 0x1ee6,
- 0x1ee8, 0x1ee8,
- 0x1eea, 0x1eea,
- 0x1eec, 0x1eec,
- 0x1eee, 0x1eee,
- 0x1ef0, 0x1ef0,
- 0x1ef2, 0x1ef2,
- 0x1ef4, 0x1ef4,
- 0x1ef6, 0x1ef6,
- 0x1ef8, 0x1ef8,
- 0x1efa, 0x1efa,
- 0x1efc, 0x1efc,
- 0x1efe, 0x1efe,
- 0x1f08, 0x1f0f,
- 0x1f18, 0x1f1d,
- 0x1f28, 0x1f2f,
- 0x1f38, 0x1f3f,
- 0x1f48, 0x1f4d,
- 0x1f59, 0x1f59,
- 0x1f5b, 0x1f5b,
- 0x1f5d, 0x1f5d,
- 0x1f5f, 0x1f5f,
- 0x1f68, 0x1f6f,
- 0x1f88, 0x1f8f,
- 0x1f98, 0x1f9f,
- 0x1fa8, 0x1faf,
- 0x1fb8, 0x1fbc,
- 0x1fc8, 0x1fcc,
- 0x1fd8, 0x1fdb,
- 0x1fe8, 0x1fec,
- 0x1ff8, 0x1ffc,
- 0x2126, 0x2126,
- 0x212a, 0x212b,
- 0x2132, 0x2132,
- 0x2160, 0x216f,
- 0x2183, 0x2183,
- 0x24b6, 0x24cf,
- 0x2c00, 0x2c2e,
- 0x2c60, 0x2c60,
- 0x2c62, 0x2c64,
- 0x2c67, 0x2c67,
- 0x2c69, 0x2c69,
- 0x2c6b, 0x2c6b,
- 0x2c6d, 0x2c70,
- 0x2c72, 0x2c72,
- 0x2c75, 0x2c75,
- 0x2c7e, 0x2c80,
- 0x2c82, 0x2c82,
- 0x2c84, 0x2c84,
- 0x2c86, 0x2c86,
- 0x2c88, 0x2c88,
- 0x2c8a, 0x2c8a,
- 0x2c8c, 0x2c8c,
- 0x2c8e, 0x2c8e,
- 0x2c90, 0x2c90,
- 0x2c92, 0x2c92,
- 0x2c94, 0x2c94,
- 0x2c96, 0x2c96,
- 0x2c98, 0x2c98,
- 0x2c9a, 0x2c9a,
- 0x2c9c, 0x2c9c,
- 0x2c9e, 0x2c9e,
- 0x2ca0, 0x2ca0,
- 0x2ca2, 0x2ca2,
- 0x2ca4, 0x2ca4,
- 0x2ca6, 0x2ca6,
- 0x2ca8, 0x2ca8,
- 0x2caa, 0x2caa,
- 0x2cac, 0x2cac,
- 0x2cae, 0x2cae,
- 0x2cb0, 0x2cb0,
- 0x2cb2, 0x2cb2,
- 0x2cb4, 0x2cb4,
- 0x2cb6, 0x2cb6,
- 0x2cb8, 0x2cb8,
- 0x2cba, 0x2cba,
- 0x2cbc, 0x2cbc,
- 0x2cbe, 0x2cbe,
- 0x2cc0, 0x2cc0,
- 0x2cc2, 0x2cc2,
- 0x2cc4, 0x2cc4,
- 0x2cc6, 0x2cc6,
- 0x2cc8, 0x2cc8,
- 0x2cca, 0x2cca,
- 0x2ccc, 0x2ccc,
- 0x2cce, 0x2cce,
- 0x2cd0, 0x2cd0,
- 0x2cd2, 0x2cd2,
- 0x2cd4, 0x2cd4,
- 0x2cd6, 0x2cd6,
- 0x2cd8, 0x2cd8,
- 0x2cda, 0x2cda,
- 0x2cdc, 0x2cdc,
- 0x2cde, 0x2cde,
- 0x2ce0, 0x2ce0,
- 0x2ce2, 0x2ce2,
- 0x2ceb, 0x2ceb,
- 0x2ced, 0x2ced,
- 0x2cf2, 0x2cf2,
- 0xa640, 0xa640,
- 0xa642, 0xa642,
- 0xa644, 0xa644,
- 0xa646, 0xa646,
- 0xa648, 0xa648,
- 0xa64a, 0xa64a,
- 0xa64c, 0xa64c,
- 0xa64e, 0xa64e,
- 0xa650, 0xa650,
- 0xa652, 0xa652,
- 0xa654, 0xa654,
- 0xa656, 0xa656,
- 0xa658, 0xa658,
- 0xa65a, 0xa65a,
- 0xa65c, 0xa65c,
- 0xa65e, 0xa65e,
- 0xa660, 0xa660,
- 0xa662, 0xa662,
- 0xa664, 0xa664,
- 0xa666, 0xa666,
- 0xa668, 0xa668,
- 0xa66a, 0xa66a,
- 0xa66c, 0xa66c,
- 0xa680, 0xa680,
- 0xa682, 0xa682,
- 0xa684, 0xa684,
- 0xa686, 0xa686,
- 0xa688, 0xa688,
- 0xa68a, 0xa68a,
- 0xa68c, 0xa68c,
- 0xa68e, 0xa68e,
- 0xa690, 0xa690,
- 0xa692, 0xa692,
- 0xa694, 0xa694,
- 0xa696, 0xa696,
- 0xa698, 0xa698,
- 0xa69a, 0xa69a,
- 0xa722, 0xa722,
- 0xa724, 0xa724,
- 0xa726, 0xa726,
- 0xa728, 0xa728,
- 0xa72a, 0xa72a,
- 0xa72c, 0xa72c,
- 0xa72e, 0xa72e,
- 0xa732, 0xa732,
- 0xa734, 0xa734,
- 0xa736, 0xa736,
- 0xa738, 0xa738,
- 0xa73a, 0xa73a,
- 0xa73c, 0xa73c,
- 0xa73e, 0xa73e,
- 0xa740, 0xa740,
- 0xa742, 0xa742,
- 0xa744, 0xa744,
- 0xa746, 0xa746,
- 0xa748, 0xa748,
- 0xa74a, 0xa74a,
- 0xa74c, 0xa74c,
- 0xa74e, 0xa74e,
- 0xa750, 0xa750,
- 0xa752, 0xa752,
- 0xa754, 0xa754,
- 0xa756, 0xa756,
- 0xa758, 0xa758,
- 0xa75a, 0xa75a,
- 0xa75c, 0xa75c,
- 0xa75e, 0xa75e,
- 0xa760, 0xa760,
- 0xa762, 0xa762,
- 0xa764, 0xa764,
- 0xa766, 0xa766,
- 0xa768, 0xa768,
- 0xa76a, 0xa76a,
- 0xa76c, 0xa76c,
- 0xa76e, 0xa76e,
- 0xa779, 0xa779,
- 0xa77b, 0xa77b,
- 0xa77d, 0xa77e,
- 0xa780, 0xa780,
- 0xa782, 0xa782,
- 0xa784, 0xa784,
- 0xa786, 0xa786,
- 0xa78b, 0xa78b,
- 0xa78d, 0xa78d,
- 0xa790, 0xa790,
- 0xa792, 0xa792,
- 0xa796, 0xa796,
- 0xa798, 0xa798,
- 0xa79a, 0xa79a,
- 0xa79c, 0xa79c,
- 0xa79e, 0xa79e,
- 0xa7a0, 0xa7a0,
- 0xa7a2, 0xa7a2,
- 0xa7a4, 0xa7a4,
- 0xa7a6, 0xa7a6,
- 0xa7a8, 0xa7a8,
- 0xa7aa, 0xa7ae,
- 0xa7b0, 0xa7b4,
- 0xa7b6, 0xa7b6,
- 0xff21, 0xff3a,
- 0x10400, 0x10427,
- 0x104b0, 0x104d3,
- 0x10c80, 0x10cb2,
- 0x118a0, 0x118bf,
- 0x1e900, 0x1e921,
-}; /* CR_Changes_When_Lowercased */
-
-/* 'Changes_When_Uppercased': Derived Property */
-static const OnigCodePoint CR_Changes_When_Uppercased[] = {
- 607,
- 0x0061, 0x007a,
- 0x00b5, 0x00b5,
- 0x00df, 0x00f6,
- 0x00f8, 0x00ff,
- 0x0101, 0x0101,
- 0x0103, 0x0103,
- 0x0105, 0x0105,
- 0x0107, 0x0107,
- 0x0109, 0x0109,
- 0x010b, 0x010b,
- 0x010d, 0x010d,
- 0x010f, 0x010f,
- 0x0111, 0x0111,
- 0x0113, 0x0113,
- 0x0115, 0x0115,
- 0x0117, 0x0117,
- 0x0119, 0x0119,
- 0x011b, 0x011b,
- 0x011d, 0x011d,
- 0x011f, 0x011f,
- 0x0121, 0x0121,
- 0x0123, 0x0123,
- 0x0125, 0x0125,
- 0x0127, 0x0127,
- 0x0129, 0x0129,
- 0x012b, 0x012b,
- 0x012d, 0x012d,
- 0x012f, 0x012f,
- 0x0131, 0x0131,
- 0x0133, 0x0133,
- 0x0135, 0x0135,
- 0x0137, 0x0137,
- 0x013a, 0x013a,
- 0x013c, 0x013c,
- 0x013e, 0x013e,
- 0x0140, 0x0140,
- 0x0142, 0x0142,
- 0x0144, 0x0144,
- 0x0146, 0x0146,
- 0x0148, 0x0149,
- 0x014b, 0x014b,
- 0x014d, 0x014d,
- 0x014f, 0x014f,
- 0x0151, 0x0151,
- 0x0153, 0x0153,
- 0x0155, 0x0155,
- 0x0157, 0x0157,
- 0x0159, 0x0159,
- 0x015b, 0x015b,
- 0x015d, 0x015d,
- 0x015f, 0x015f,
- 0x0161, 0x0161,
- 0x0163, 0x0163,
- 0x0165, 0x0165,
- 0x0167, 0x0167,
- 0x0169, 0x0169,
- 0x016b, 0x016b,
- 0x016d, 0x016d,
- 0x016f, 0x016f,
- 0x0171, 0x0171,
- 0x0173, 0x0173,
- 0x0175, 0x0175,
- 0x0177, 0x0177,
- 0x017a, 0x017a,
- 0x017c, 0x017c,
- 0x017e, 0x0180,
- 0x0183, 0x0183,
- 0x0185, 0x0185,
- 0x0188, 0x0188,
- 0x018c, 0x018c,
- 0x0192, 0x0192,
- 0x0195, 0x0195,
- 0x0199, 0x019a,
- 0x019e, 0x019e,
- 0x01a1, 0x01a1,
- 0x01a3, 0x01a3,
- 0x01a5, 0x01a5,
- 0x01a8, 0x01a8,
- 0x01ad, 0x01ad,
- 0x01b0, 0x01b0,
- 0x01b4, 0x01b4,
- 0x01b6, 0x01b6,
- 0x01b9, 0x01b9,
- 0x01bd, 0x01bd,
- 0x01bf, 0x01bf,
- 0x01c5, 0x01c6,
- 0x01c8, 0x01c9,
- 0x01cb, 0x01cc,
- 0x01ce, 0x01ce,
- 0x01d0, 0x01d0,
- 0x01d2, 0x01d2,
- 0x01d4, 0x01d4,
- 0x01d6, 0x01d6,
- 0x01d8, 0x01d8,
- 0x01da, 0x01da,
- 0x01dc, 0x01dd,
- 0x01df, 0x01df,
- 0x01e1, 0x01e1,
- 0x01e3, 0x01e3,
- 0x01e5, 0x01e5,
- 0x01e7, 0x01e7,
- 0x01e9, 0x01e9,
- 0x01eb, 0x01eb,
- 0x01ed, 0x01ed,
- 0x01ef, 0x01f0,
- 0x01f2, 0x01f3,
- 0x01f5, 0x01f5,
- 0x01f9, 0x01f9,
- 0x01fb, 0x01fb,
- 0x01fd, 0x01fd,
- 0x01ff, 0x01ff,
- 0x0201, 0x0201,
- 0x0203, 0x0203,
- 0x0205, 0x0205,
- 0x0207, 0x0207,
- 0x0209, 0x0209,
- 0x020b, 0x020b,
- 0x020d, 0x020d,
- 0x020f, 0x020f,
- 0x0211, 0x0211,
- 0x0213, 0x0213,
- 0x0215, 0x0215,
- 0x0217, 0x0217,
- 0x0219, 0x0219,
- 0x021b, 0x021b,
- 0x021d, 0x021d,
- 0x021f, 0x021f,
- 0x0223, 0x0223,
- 0x0225, 0x0225,
- 0x0227, 0x0227,
- 0x0229, 0x0229,
- 0x022b, 0x022b,
- 0x022d, 0x022d,
- 0x022f, 0x022f,
- 0x0231, 0x0231,
- 0x0233, 0x0233,
- 0x023c, 0x023c,
- 0x023f, 0x0240,
- 0x0242, 0x0242,
- 0x0247, 0x0247,
- 0x0249, 0x0249,
- 0x024b, 0x024b,
- 0x024d, 0x024d,
- 0x024f, 0x0254,
- 0x0256, 0x0257,
- 0x0259, 0x0259,
- 0x025b, 0x025c,
- 0x0260, 0x0261,
- 0x0263, 0x0263,
- 0x0265, 0x0266,
- 0x0268, 0x026c,
- 0x026f, 0x026f,
- 0x0271, 0x0272,
- 0x0275, 0x0275,
- 0x027d, 0x027d,
- 0x0280, 0x0280,
- 0x0283, 0x0283,
- 0x0287, 0x028c,
- 0x0292, 0x0292,
- 0x029d, 0x029e,
- 0x0345, 0x0345,
- 0x0371, 0x0371,
- 0x0373, 0x0373,
- 0x0377, 0x0377,
- 0x037b, 0x037d,
- 0x0390, 0x0390,
- 0x03ac, 0x03ce,
- 0x03d0, 0x03d1,
- 0x03d5, 0x03d7,
- 0x03d9, 0x03d9,
- 0x03db, 0x03db,
- 0x03dd, 0x03dd,
- 0x03df, 0x03df,
- 0x03e1, 0x03e1,
- 0x03e3, 0x03e3,
- 0x03e5, 0x03e5,
- 0x03e7, 0x03e7,
- 0x03e9, 0x03e9,
- 0x03eb, 0x03eb,
- 0x03ed, 0x03ed,
- 0x03ef, 0x03f3,
- 0x03f5, 0x03f5,
- 0x03f8, 0x03f8,
- 0x03fb, 0x03fb,
- 0x0430, 0x045f,
- 0x0461, 0x0461,
- 0x0463, 0x0463,
- 0x0465, 0x0465,
- 0x0467, 0x0467,
- 0x0469, 0x0469,
- 0x046b, 0x046b,
- 0x046d, 0x046d,
- 0x046f, 0x046f,
- 0x0471, 0x0471,
- 0x0473, 0x0473,
- 0x0475, 0x0475,
- 0x0477, 0x0477,
- 0x0479, 0x0479,
- 0x047b, 0x047b,
- 0x047d, 0x047d,
- 0x047f, 0x047f,
- 0x0481, 0x0481,
- 0x048b, 0x048b,
- 0x048d, 0x048d,
- 0x048f, 0x048f,
- 0x0491, 0x0491,
- 0x0493, 0x0493,
- 0x0495, 0x0495,
- 0x0497, 0x0497,
- 0x0499, 0x0499,
- 0x049b, 0x049b,
- 0x049d, 0x049d,
- 0x049f, 0x049f,
- 0x04a1, 0x04a1,
- 0x04a3, 0x04a3,
- 0x04a5, 0x04a5,
- 0x04a7, 0x04a7,
- 0x04a9, 0x04a9,
- 0x04ab, 0x04ab,
- 0x04ad, 0x04ad,
- 0x04af, 0x04af,
- 0x04b1, 0x04b1,
- 0x04b3, 0x04b3,
- 0x04b5, 0x04b5,
- 0x04b7, 0x04b7,
- 0x04b9, 0x04b9,
- 0x04bb, 0x04bb,
- 0x04bd, 0x04bd,
- 0x04bf, 0x04bf,
- 0x04c2, 0x04c2,
- 0x04c4, 0x04c4,
- 0x04c6, 0x04c6,
- 0x04c8, 0x04c8,
- 0x04ca, 0x04ca,
- 0x04cc, 0x04cc,
- 0x04ce, 0x04cf,
- 0x04d1, 0x04d1,
- 0x04d3, 0x04d3,
- 0x04d5, 0x04d5,
- 0x04d7, 0x04d7,
- 0x04d9, 0x04d9,
- 0x04db, 0x04db,
- 0x04dd, 0x04dd,
- 0x04df, 0x04df,
- 0x04e1, 0x04e1,
- 0x04e3, 0x04e3,
- 0x04e5, 0x04e5,
- 0x04e7, 0x04e7,
- 0x04e9, 0x04e9,
- 0x04eb, 0x04eb,
- 0x04ed, 0x04ed,
- 0x04ef, 0x04ef,
- 0x04f1, 0x04f1,
- 0x04f3, 0x04f3,
- 0x04f5, 0x04f5,
- 0x04f7, 0x04f7,
- 0x04f9, 0x04f9,
- 0x04fb, 0x04fb,
- 0x04fd, 0x04fd,
- 0x04ff, 0x04ff,
- 0x0501, 0x0501,
- 0x0503, 0x0503,
- 0x0505, 0x0505,
- 0x0507, 0x0507,
- 0x0509, 0x0509,
- 0x050b, 0x050b,
- 0x050d, 0x050d,
- 0x050f, 0x050f,
- 0x0511, 0x0511,
- 0x0513, 0x0513,
- 0x0515, 0x0515,
- 0x0517, 0x0517,
- 0x0519, 0x0519,
- 0x051b, 0x051b,
- 0x051d, 0x051d,
- 0x051f, 0x051f,
- 0x0521, 0x0521,
- 0x0523, 0x0523,
- 0x0525, 0x0525,
- 0x0527, 0x0527,
- 0x0529, 0x0529,
- 0x052b, 0x052b,
- 0x052d, 0x052d,
- 0x052f, 0x052f,
- 0x0561, 0x0587,
- 0x13f8, 0x13fd,
- 0x1c80, 0x1c88,
- 0x1d79, 0x1d79,
- 0x1d7d, 0x1d7d,
- 0x1e01, 0x1e01,
- 0x1e03, 0x1e03,
- 0x1e05, 0x1e05,
- 0x1e07, 0x1e07,
- 0x1e09, 0x1e09,
- 0x1e0b, 0x1e0b,
- 0x1e0d, 0x1e0d,
- 0x1e0f, 0x1e0f,
- 0x1e11, 0x1e11,
- 0x1e13, 0x1e13,
- 0x1e15, 0x1e15,
- 0x1e17, 0x1e17,
- 0x1e19, 0x1e19,
- 0x1e1b, 0x1e1b,
- 0x1e1d, 0x1e1d,
- 0x1e1f, 0x1e1f,
- 0x1e21, 0x1e21,
- 0x1e23, 0x1e23,
- 0x1e25, 0x1e25,
- 0x1e27, 0x1e27,
- 0x1e29, 0x1e29,
- 0x1e2b, 0x1e2b,
- 0x1e2d, 0x1e2d,
- 0x1e2f, 0x1e2f,
- 0x1e31, 0x1e31,
- 0x1e33, 0x1e33,
- 0x1e35, 0x1e35,
- 0x1e37, 0x1e37,
- 0x1e39, 0x1e39,
- 0x1e3b, 0x1e3b,
- 0x1e3d, 0x1e3d,
- 0x1e3f, 0x1e3f,
- 0x1e41, 0x1e41,
- 0x1e43, 0x1e43,
- 0x1e45, 0x1e45,
- 0x1e47, 0x1e47,
- 0x1e49, 0x1e49,
- 0x1e4b, 0x1e4b,
- 0x1e4d, 0x1e4d,
- 0x1e4f, 0x1e4f,
- 0x1e51, 0x1e51,
- 0x1e53, 0x1e53,
- 0x1e55, 0x1e55,
- 0x1e57, 0x1e57,
- 0x1e59, 0x1e59,
- 0x1e5b, 0x1e5b,
- 0x1e5d, 0x1e5d,
- 0x1e5f, 0x1e5f,
- 0x1e61, 0x1e61,
- 0x1e63, 0x1e63,
- 0x1e65, 0x1e65,
- 0x1e67, 0x1e67,
- 0x1e69, 0x1e69,
- 0x1e6b, 0x1e6b,
- 0x1e6d, 0x1e6d,
- 0x1e6f, 0x1e6f,
- 0x1e71, 0x1e71,
- 0x1e73, 0x1e73,
- 0x1e75, 0x1e75,
- 0x1e77, 0x1e77,
- 0x1e79, 0x1e79,
- 0x1e7b, 0x1e7b,
- 0x1e7d, 0x1e7d,
- 0x1e7f, 0x1e7f,
- 0x1e81, 0x1e81,
- 0x1e83, 0x1e83,
- 0x1e85, 0x1e85,
- 0x1e87, 0x1e87,
- 0x1e89, 0x1e89,
- 0x1e8b, 0x1e8b,
- 0x1e8d, 0x1e8d,
- 0x1e8f, 0x1e8f,
- 0x1e91, 0x1e91,
- 0x1e93, 0x1e93,
- 0x1e95, 0x1e9b,
- 0x1ea1, 0x1ea1,
- 0x1ea3, 0x1ea3,
- 0x1ea5, 0x1ea5,
- 0x1ea7, 0x1ea7,
- 0x1ea9, 0x1ea9,
- 0x1eab, 0x1eab,
- 0x1ead, 0x1ead,
- 0x1eaf, 0x1eaf,
- 0x1eb1, 0x1eb1,
- 0x1eb3, 0x1eb3,
- 0x1eb5, 0x1eb5,
- 0x1eb7, 0x1eb7,
- 0x1eb9, 0x1eb9,
- 0x1ebb, 0x1ebb,
- 0x1ebd, 0x1ebd,
- 0x1ebf, 0x1ebf,
- 0x1ec1, 0x1ec1,
- 0x1ec3, 0x1ec3,
- 0x1ec5, 0x1ec5,
- 0x1ec7, 0x1ec7,
- 0x1ec9, 0x1ec9,
- 0x1ecb, 0x1ecb,
- 0x1ecd, 0x1ecd,
- 0x1ecf, 0x1ecf,
- 0x1ed1, 0x1ed1,
- 0x1ed3, 0x1ed3,
- 0x1ed5, 0x1ed5,
- 0x1ed7, 0x1ed7,
- 0x1ed9, 0x1ed9,
- 0x1edb, 0x1edb,
- 0x1edd, 0x1edd,
- 0x1edf, 0x1edf,
- 0x1ee1, 0x1ee1,
- 0x1ee3, 0x1ee3,
- 0x1ee5, 0x1ee5,
- 0x1ee7, 0x1ee7,
- 0x1ee9, 0x1ee9,
- 0x1eeb, 0x1eeb,
- 0x1eed, 0x1eed,
- 0x1eef, 0x1eef,
- 0x1ef1, 0x1ef1,
- 0x1ef3, 0x1ef3,
- 0x1ef5, 0x1ef5,
- 0x1ef7, 0x1ef7,
- 0x1ef9, 0x1ef9,
- 0x1efb, 0x1efb,
- 0x1efd, 0x1efd,
- 0x1eff, 0x1f07,
- 0x1f10, 0x1f15,
- 0x1f20, 0x1f27,
- 0x1f30, 0x1f37,
- 0x1f40, 0x1f45,
- 0x1f50, 0x1f57,
- 0x1f60, 0x1f67,
- 0x1f70, 0x1f7d,
- 0x1f80, 0x1fb4,
- 0x1fb6, 0x1fb7,
- 0x1fbc, 0x1fbc,
- 0x1fbe, 0x1fbe,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fc7,
- 0x1fcc, 0x1fcc,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fd7,
- 0x1fe0, 0x1fe7,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ff7,
- 0x1ffc, 0x1ffc,
- 0x214e, 0x214e,
- 0x2170, 0x217f,
- 0x2184, 0x2184,
- 0x24d0, 0x24e9,
- 0x2c30, 0x2c5e,
- 0x2c61, 0x2c61,
- 0x2c65, 0x2c66,
- 0x2c68, 0x2c68,
- 0x2c6a, 0x2c6a,
- 0x2c6c, 0x2c6c,
- 0x2c73, 0x2c73,
- 0x2c76, 0x2c76,
- 0x2c81, 0x2c81,
- 0x2c83, 0x2c83,
- 0x2c85, 0x2c85,
- 0x2c87, 0x2c87,
- 0x2c89, 0x2c89,
- 0x2c8b, 0x2c8b,
- 0x2c8d, 0x2c8d,
- 0x2c8f, 0x2c8f,
- 0x2c91, 0x2c91,
- 0x2c93, 0x2c93,
- 0x2c95, 0x2c95,
- 0x2c97, 0x2c97,
- 0x2c99, 0x2c99,
- 0x2c9b, 0x2c9b,
- 0x2c9d, 0x2c9d,
- 0x2c9f, 0x2c9f,
- 0x2ca1, 0x2ca1,
- 0x2ca3, 0x2ca3,
- 0x2ca5, 0x2ca5,
- 0x2ca7, 0x2ca7,
- 0x2ca9, 0x2ca9,
- 0x2cab, 0x2cab,
- 0x2cad, 0x2cad,
- 0x2caf, 0x2caf,
- 0x2cb1, 0x2cb1,
- 0x2cb3, 0x2cb3,
- 0x2cb5, 0x2cb5,
- 0x2cb7, 0x2cb7,
- 0x2cb9, 0x2cb9,
- 0x2cbb, 0x2cbb,
- 0x2cbd, 0x2cbd,
- 0x2cbf, 0x2cbf,
- 0x2cc1, 0x2cc1,
- 0x2cc3, 0x2cc3,
- 0x2cc5, 0x2cc5,
- 0x2cc7, 0x2cc7,
- 0x2cc9, 0x2cc9,
- 0x2ccb, 0x2ccb,
- 0x2ccd, 0x2ccd,
- 0x2ccf, 0x2ccf,
- 0x2cd1, 0x2cd1,
- 0x2cd3, 0x2cd3,
- 0x2cd5, 0x2cd5,
- 0x2cd7, 0x2cd7,
- 0x2cd9, 0x2cd9,
- 0x2cdb, 0x2cdb,
- 0x2cdd, 0x2cdd,
- 0x2cdf, 0x2cdf,
- 0x2ce1, 0x2ce1,
- 0x2ce3, 0x2ce3,
- 0x2cec, 0x2cec,
- 0x2cee, 0x2cee,
- 0x2cf3, 0x2cf3,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0xa641, 0xa641,
- 0xa643, 0xa643,
- 0xa645, 0xa645,
- 0xa647, 0xa647,
- 0xa649, 0xa649,
- 0xa64b, 0xa64b,
- 0xa64d, 0xa64d,
- 0xa64f, 0xa64f,
- 0xa651, 0xa651,
- 0xa653, 0xa653,
- 0xa655, 0xa655,
- 0xa657, 0xa657,
- 0xa659, 0xa659,
- 0xa65b, 0xa65b,
- 0xa65d, 0xa65d,
- 0xa65f, 0xa65f,
- 0xa661, 0xa661,
- 0xa663, 0xa663,
- 0xa665, 0xa665,
- 0xa667, 0xa667,
- 0xa669, 0xa669,
- 0xa66b, 0xa66b,
- 0xa66d, 0xa66d,
- 0xa681, 0xa681,
- 0xa683, 0xa683,
- 0xa685, 0xa685,
- 0xa687, 0xa687,
- 0xa689, 0xa689,
- 0xa68b, 0xa68b,
- 0xa68d, 0xa68d,
- 0xa68f, 0xa68f,
- 0xa691, 0xa691,
- 0xa693, 0xa693,
- 0xa695, 0xa695,
- 0xa697, 0xa697,
- 0xa699, 0xa699,
- 0xa69b, 0xa69b,
- 0xa723, 0xa723,
- 0xa725, 0xa725,
- 0xa727, 0xa727,
- 0xa729, 0xa729,
- 0xa72b, 0xa72b,
- 0xa72d, 0xa72d,
- 0xa72f, 0xa72f,
- 0xa733, 0xa733,
- 0xa735, 0xa735,
- 0xa737, 0xa737,
- 0xa739, 0xa739,
- 0xa73b, 0xa73b,
- 0xa73d, 0xa73d,
- 0xa73f, 0xa73f,
- 0xa741, 0xa741,
- 0xa743, 0xa743,
- 0xa745, 0xa745,
- 0xa747, 0xa747,
- 0xa749, 0xa749,
- 0xa74b, 0xa74b,
- 0xa74d, 0xa74d,
- 0xa74f, 0xa74f,
- 0xa751, 0xa751,
- 0xa753, 0xa753,
- 0xa755, 0xa755,
- 0xa757, 0xa757,
- 0xa759, 0xa759,
- 0xa75b, 0xa75b,
- 0xa75d, 0xa75d,
- 0xa75f, 0xa75f,
- 0xa761, 0xa761,
- 0xa763, 0xa763,
- 0xa765, 0xa765,
- 0xa767, 0xa767,
- 0xa769, 0xa769,
- 0xa76b, 0xa76b,
- 0xa76d, 0xa76d,
- 0xa76f, 0xa76f,
- 0xa77a, 0xa77a,
- 0xa77c, 0xa77c,
- 0xa77f, 0xa77f,
- 0xa781, 0xa781,
- 0xa783, 0xa783,
- 0xa785, 0xa785,
- 0xa787, 0xa787,
- 0xa78c, 0xa78c,
- 0xa791, 0xa791,
- 0xa793, 0xa793,
- 0xa797, 0xa797,
- 0xa799, 0xa799,
- 0xa79b, 0xa79b,
- 0xa79d, 0xa79d,
- 0xa79f, 0xa79f,
- 0xa7a1, 0xa7a1,
- 0xa7a3, 0xa7a3,
- 0xa7a5, 0xa7a5,
- 0xa7a7, 0xa7a7,
- 0xa7a9, 0xa7a9,
- 0xa7b5, 0xa7b5,
- 0xa7b7, 0xa7b7,
- 0xab53, 0xab53,
- 0xab70, 0xabbf,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xff41, 0xff5a,
- 0x10428, 0x1044f,
- 0x104d8, 0x104fb,
- 0x10cc0, 0x10cf2,
- 0x118c0, 0x118df,
- 0x1e922, 0x1e943,
-}; /* CR_Changes_When_Uppercased */
-
-/* 'Changes_When_Titlecased': Derived Property */
-static const OnigCodePoint CR_Changes_When_Titlecased[] = {
- 608,
- 0x0061, 0x007a,
- 0x00b5, 0x00b5,
- 0x00df, 0x00f6,
- 0x00f8, 0x00ff,
- 0x0101, 0x0101,
- 0x0103, 0x0103,
- 0x0105, 0x0105,
- 0x0107, 0x0107,
- 0x0109, 0x0109,
- 0x010b, 0x010b,
- 0x010d, 0x010d,
- 0x010f, 0x010f,
- 0x0111, 0x0111,
- 0x0113, 0x0113,
- 0x0115, 0x0115,
- 0x0117, 0x0117,
- 0x0119, 0x0119,
- 0x011b, 0x011b,
- 0x011d, 0x011d,
- 0x011f, 0x011f,
- 0x0121, 0x0121,
- 0x0123, 0x0123,
- 0x0125, 0x0125,
- 0x0127, 0x0127,
- 0x0129, 0x0129,
- 0x012b, 0x012b,
- 0x012d, 0x012d,
- 0x012f, 0x012f,
- 0x0131, 0x0131,
- 0x0133, 0x0133,
- 0x0135, 0x0135,
- 0x0137, 0x0137,
- 0x013a, 0x013a,
- 0x013c, 0x013c,
- 0x013e, 0x013e,
- 0x0140, 0x0140,
- 0x0142, 0x0142,
- 0x0144, 0x0144,
- 0x0146, 0x0146,
- 0x0148, 0x0149,
- 0x014b, 0x014b,
- 0x014d, 0x014d,
- 0x014f, 0x014f,
- 0x0151, 0x0151,
- 0x0153, 0x0153,
- 0x0155, 0x0155,
- 0x0157, 0x0157,
- 0x0159, 0x0159,
- 0x015b, 0x015b,
- 0x015d, 0x015d,
- 0x015f, 0x015f,
- 0x0161, 0x0161,
- 0x0163, 0x0163,
- 0x0165, 0x0165,
- 0x0167, 0x0167,
- 0x0169, 0x0169,
- 0x016b, 0x016b,
- 0x016d, 0x016d,
- 0x016f, 0x016f,
- 0x0171, 0x0171,
- 0x0173, 0x0173,
- 0x0175, 0x0175,
- 0x0177, 0x0177,
- 0x017a, 0x017a,
- 0x017c, 0x017c,
- 0x017e, 0x0180,
- 0x0183, 0x0183,
- 0x0185, 0x0185,
- 0x0188, 0x0188,
- 0x018c, 0x018c,
- 0x0192, 0x0192,
- 0x0195, 0x0195,
- 0x0199, 0x019a,
- 0x019e, 0x019e,
- 0x01a1, 0x01a1,
- 0x01a3, 0x01a3,
- 0x01a5, 0x01a5,
- 0x01a8, 0x01a8,
- 0x01ad, 0x01ad,
- 0x01b0, 0x01b0,
- 0x01b4, 0x01b4,
- 0x01b6, 0x01b6,
- 0x01b9, 0x01b9,
- 0x01bd, 0x01bd,
- 0x01bf, 0x01bf,
- 0x01c4, 0x01c4,
- 0x01c6, 0x01c7,
- 0x01c9, 0x01ca,
- 0x01cc, 0x01cc,
- 0x01ce, 0x01ce,
- 0x01d0, 0x01d0,
- 0x01d2, 0x01d2,
- 0x01d4, 0x01d4,
- 0x01d6, 0x01d6,
- 0x01d8, 0x01d8,
- 0x01da, 0x01da,
- 0x01dc, 0x01dd,
- 0x01df, 0x01df,
- 0x01e1, 0x01e1,
- 0x01e3, 0x01e3,
- 0x01e5, 0x01e5,
- 0x01e7, 0x01e7,
- 0x01e9, 0x01e9,
- 0x01eb, 0x01eb,
- 0x01ed, 0x01ed,
- 0x01ef, 0x01f1,
- 0x01f3, 0x01f3,
- 0x01f5, 0x01f5,
- 0x01f9, 0x01f9,
- 0x01fb, 0x01fb,
- 0x01fd, 0x01fd,
- 0x01ff, 0x01ff,
- 0x0201, 0x0201,
- 0x0203, 0x0203,
- 0x0205, 0x0205,
- 0x0207, 0x0207,
- 0x0209, 0x0209,
- 0x020b, 0x020b,
- 0x020d, 0x020d,
- 0x020f, 0x020f,
- 0x0211, 0x0211,
- 0x0213, 0x0213,
- 0x0215, 0x0215,
- 0x0217, 0x0217,
- 0x0219, 0x0219,
- 0x021b, 0x021b,
- 0x021d, 0x021d,
- 0x021f, 0x021f,
- 0x0223, 0x0223,
- 0x0225, 0x0225,
- 0x0227, 0x0227,
- 0x0229, 0x0229,
- 0x022b, 0x022b,
- 0x022d, 0x022d,
- 0x022f, 0x022f,
- 0x0231, 0x0231,
- 0x0233, 0x0233,
- 0x023c, 0x023c,
- 0x023f, 0x0240,
- 0x0242, 0x0242,
- 0x0247, 0x0247,
- 0x0249, 0x0249,
- 0x024b, 0x024b,
- 0x024d, 0x024d,
- 0x024f, 0x0254,
- 0x0256, 0x0257,
- 0x0259, 0x0259,
- 0x025b, 0x025c,
- 0x0260, 0x0261,
- 0x0263, 0x0263,
- 0x0265, 0x0266,
- 0x0268, 0x026c,
- 0x026f, 0x026f,
- 0x0271, 0x0272,
- 0x0275, 0x0275,
- 0x027d, 0x027d,
- 0x0280, 0x0280,
- 0x0283, 0x0283,
- 0x0287, 0x028c,
- 0x0292, 0x0292,
- 0x029d, 0x029e,
- 0x0345, 0x0345,
- 0x0371, 0x0371,
- 0x0373, 0x0373,
- 0x0377, 0x0377,
- 0x037b, 0x037d,
- 0x0390, 0x0390,
- 0x03ac, 0x03ce,
- 0x03d0, 0x03d1,
- 0x03d5, 0x03d7,
- 0x03d9, 0x03d9,
- 0x03db, 0x03db,
- 0x03dd, 0x03dd,
- 0x03df, 0x03df,
- 0x03e1, 0x03e1,
- 0x03e3, 0x03e3,
- 0x03e5, 0x03e5,
- 0x03e7, 0x03e7,
- 0x03e9, 0x03e9,
- 0x03eb, 0x03eb,
- 0x03ed, 0x03ed,
- 0x03ef, 0x03f3,
- 0x03f5, 0x03f5,
- 0x03f8, 0x03f8,
- 0x03fb, 0x03fb,
- 0x0430, 0x045f,
- 0x0461, 0x0461,
- 0x0463, 0x0463,
- 0x0465, 0x0465,
- 0x0467, 0x0467,
- 0x0469, 0x0469,
- 0x046b, 0x046b,
- 0x046d, 0x046d,
- 0x046f, 0x046f,
- 0x0471, 0x0471,
- 0x0473, 0x0473,
- 0x0475, 0x0475,
- 0x0477, 0x0477,
- 0x0479, 0x0479,
- 0x047b, 0x047b,
- 0x047d, 0x047d,
- 0x047f, 0x047f,
- 0x0481, 0x0481,
- 0x048b, 0x048b,
- 0x048d, 0x048d,
- 0x048f, 0x048f,
- 0x0491, 0x0491,
- 0x0493, 0x0493,
- 0x0495, 0x0495,
- 0x0497, 0x0497,
- 0x0499, 0x0499,
- 0x049b, 0x049b,
- 0x049d, 0x049d,
- 0x049f, 0x049f,
- 0x04a1, 0x04a1,
- 0x04a3, 0x04a3,
- 0x04a5, 0x04a5,
- 0x04a7, 0x04a7,
- 0x04a9, 0x04a9,
- 0x04ab, 0x04ab,
- 0x04ad, 0x04ad,
- 0x04af, 0x04af,
- 0x04b1, 0x04b1,
- 0x04b3, 0x04b3,
- 0x04b5, 0x04b5,
- 0x04b7, 0x04b7,
- 0x04b9, 0x04b9,
- 0x04bb, 0x04bb,
- 0x04bd, 0x04bd,
- 0x04bf, 0x04bf,
- 0x04c2, 0x04c2,
- 0x04c4, 0x04c4,
- 0x04c6, 0x04c6,
- 0x04c8, 0x04c8,
- 0x04ca, 0x04ca,
- 0x04cc, 0x04cc,
- 0x04ce, 0x04cf,
- 0x04d1, 0x04d1,
- 0x04d3, 0x04d3,
- 0x04d5, 0x04d5,
- 0x04d7, 0x04d7,
- 0x04d9, 0x04d9,
- 0x04db, 0x04db,
- 0x04dd, 0x04dd,
- 0x04df, 0x04df,
- 0x04e1, 0x04e1,
- 0x04e3, 0x04e3,
- 0x04e5, 0x04e5,
- 0x04e7, 0x04e7,
- 0x04e9, 0x04e9,
- 0x04eb, 0x04eb,
- 0x04ed, 0x04ed,
- 0x04ef, 0x04ef,
- 0x04f1, 0x04f1,
- 0x04f3, 0x04f3,
- 0x04f5, 0x04f5,
- 0x04f7, 0x04f7,
- 0x04f9, 0x04f9,
- 0x04fb, 0x04fb,
- 0x04fd, 0x04fd,
- 0x04ff, 0x04ff,
- 0x0501, 0x0501,
- 0x0503, 0x0503,
- 0x0505, 0x0505,
- 0x0507, 0x0507,
- 0x0509, 0x0509,
- 0x050b, 0x050b,
- 0x050d, 0x050d,
- 0x050f, 0x050f,
- 0x0511, 0x0511,
- 0x0513, 0x0513,
- 0x0515, 0x0515,
- 0x0517, 0x0517,
- 0x0519, 0x0519,
- 0x051b, 0x051b,
- 0x051d, 0x051d,
- 0x051f, 0x051f,
- 0x0521, 0x0521,
- 0x0523, 0x0523,
- 0x0525, 0x0525,
- 0x0527, 0x0527,
- 0x0529, 0x0529,
- 0x052b, 0x052b,
- 0x052d, 0x052d,
- 0x052f, 0x052f,
- 0x0561, 0x0587,
- 0x13f8, 0x13fd,
- 0x1c80, 0x1c88,
- 0x1d79, 0x1d79,
- 0x1d7d, 0x1d7d,
- 0x1e01, 0x1e01,
- 0x1e03, 0x1e03,
- 0x1e05, 0x1e05,
- 0x1e07, 0x1e07,
- 0x1e09, 0x1e09,
- 0x1e0b, 0x1e0b,
- 0x1e0d, 0x1e0d,
- 0x1e0f, 0x1e0f,
- 0x1e11, 0x1e11,
- 0x1e13, 0x1e13,
- 0x1e15, 0x1e15,
- 0x1e17, 0x1e17,
- 0x1e19, 0x1e19,
- 0x1e1b, 0x1e1b,
- 0x1e1d, 0x1e1d,
- 0x1e1f, 0x1e1f,
- 0x1e21, 0x1e21,
- 0x1e23, 0x1e23,
- 0x1e25, 0x1e25,
- 0x1e27, 0x1e27,
- 0x1e29, 0x1e29,
- 0x1e2b, 0x1e2b,
- 0x1e2d, 0x1e2d,
- 0x1e2f, 0x1e2f,
- 0x1e31, 0x1e31,
- 0x1e33, 0x1e33,
- 0x1e35, 0x1e35,
- 0x1e37, 0x1e37,
- 0x1e39, 0x1e39,
- 0x1e3b, 0x1e3b,
- 0x1e3d, 0x1e3d,
- 0x1e3f, 0x1e3f,
- 0x1e41, 0x1e41,
- 0x1e43, 0x1e43,
- 0x1e45, 0x1e45,
- 0x1e47, 0x1e47,
- 0x1e49, 0x1e49,
- 0x1e4b, 0x1e4b,
- 0x1e4d, 0x1e4d,
- 0x1e4f, 0x1e4f,
- 0x1e51, 0x1e51,
- 0x1e53, 0x1e53,
- 0x1e55, 0x1e55,
- 0x1e57, 0x1e57,
- 0x1e59, 0x1e59,
- 0x1e5b, 0x1e5b,
- 0x1e5d, 0x1e5d,
- 0x1e5f, 0x1e5f,
- 0x1e61, 0x1e61,
- 0x1e63, 0x1e63,
- 0x1e65, 0x1e65,
- 0x1e67, 0x1e67,
- 0x1e69, 0x1e69,
- 0x1e6b, 0x1e6b,
- 0x1e6d, 0x1e6d,
- 0x1e6f, 0x1e6f,
- 0x1e71, 0x1e71,
- 0x1e73, 0x1e73,
- 0x1e75, 0x1e75,
- 0x1e77, 0x1e77,
- 0x1e79, 0x1e79,
- 0x1e7b, 0x1e7b,
- 0x1e7d, 0x1e7d,
- 0x1e7f, 0x1e7f,
- 0x1e81, 0x1e81,
- 0x1e83, 0x1e83,
- 0x1e85, 0x1e85,
- 0x1e87, 0x1e87,
- 0x1e89, 0x1e89,
- 0x1e8b, 0x1e8b,
- 0x1e8d, 0x1e8d,
- 0x1e8f, 0x1e8f,
- 0x1e91, 0x1e91,
- 0x1e93, 0x1e93,
- 0x1e95, 0x1e9b,
- 0x1ea1, 0x1ea1,
- 0x1ea3, 0x1ea3,
- 0x1ea5, 0x1ea5,
- 0x1ea7, 0x1ea7,
- 0x1ea9, 0x1ea9,
- 0x1eab, 0x1eab,
- 0x1ead, 0x1ead,
- 0x1eaf, 0x1eaf,
- 0x1eb1, 0x1eb1,
- 0x1eb3, 0x1eb3,
- 0x1eb5, 0x1eb5,
- 0x1eb7, 0x1eb7,
- 0x1eb9, 0x1eb9,
- 0x1ebb, 0x1ebb,
- 0x1ebd, 0x1ebd,
- 0x1ebf, 0x1ebf,
- 0x1ec1, 0x1ec1,
- 0x1ec3, 0x1ec3,
- 0x1ec5, 0x1ec5,
- 0x1ec7, 0x1ec7,
- 0x1ec9, 0x1ec9,
- 0x1ecb, 0x1ecb,
- 0x1ecd, 0x1ecd,
- 0x1ecf, 0x1ecf,
- 0x1ed1, 0x1ed1,
- 0x1ed3, 0x1ed3,
- 0x1ed5, 0x1ed5,
- 0x1ed7, 0x1ed7,
- 0x1ed9, 0x1ed9,
- 0x1edb, 0x1edb,
- 0x1edd, 0x1edd,
- 0x1edf, 0x1edf,
- 0x1ee1, 0x1ee1,
- 0x1ee3, 0x1ee3,
- 0x1ee5, 0x1ee5,
- 0x1ee7, 0x1ee7,
- 0x1ee9, 0x1ee9,
- 0x1eeb, 0x1eeb,
- 0x1eed, 0x1eed,
- 0x1eef, 0x1eef,
- 0x1ef1, 0x1ef1,
- 0x1ef3, 0x1ef3,
- 0x1ef5, 0x1ef5,
- 0x1ef7, 0x1ef7,
- 0x1ef9, 0x1ef9,
- 0x1efb, 0x1efb,
- 0x1efd, 0x1efd,
- 0x1eff, 0x1f07,
- 0x1f10, 0x1f15,
- 0x1f20, 0x1f27,
- 0x1f30, 0x1f37,
- 0x1f40, 0x1f45,
- 0x1f50, 0x1f57,
- 0x1f60, 0x1f67,
- 0x1f70, 0x1f7d,
- 0x1f80, 0x1f87,
- 0x1f90, 0x1f97,
- 0x1fa0, 0x1fa7,
- 0x1fb0, 0x1fb4,
- 0x1fb6, 0x1fb7,
- 0x1fbe, 0x1fbe,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fc7,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fd7,
- 0x1fe0, 0x1fe7,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ff7,
- 0x214e, 0x214e,
- 0x2170, 0x217f,
- 0x2184, 0x2184,
- 0x24d0, 0x24e9,
- 0x2c30, 0x2c5e,
- 0x2c61, 0x2c61,
- 0x2c65, 0x2c66,
- 0x2c68, 0x2c68,
- 0x2c6a, 0x2c6a,
- 0x2c6c, 0x2c6c,
- 0x2c73, 0x2c73,
- 0x2c76, 0x2c76,
- 0x2c81, 0x2c81,
- 0x2c83, 0x2c83,
- 0x2c85, 0x2c85,
- 0x2c87, 0x2c87,
- 0x2c89, 0x2c89,
- 0x2c8b, 0x2c8b,
- 0x2c8d, 0x2c8d,
- 0x2c8f, 0x2c8f,
- 0x2c91, 0x2c91,
- 0x2c93, 0x2c93,
- 0x2c95, 0x2c95,
- 0x2c97, 0x2c97,
- 0x2c99, 0x2c99,
- 0x2c9b, 0x2c9b,
- 0x2c9d, 0x2c9d,
- 0x2c9f, 0x2c9f,
- 0x2ca1, 0x2ca1,
- 0x2ca3, 0x2ca3,
- 0x2ca5, 0x2ca5,
- 0x2ca7, 0x2ca7,
- 0x2ca9, 0x2ca9,
- 0x2cab, 0x2cab,
- 0x2cad, 0x2cad,
- 0x2caf, 0x2caf,
- 0x2cb1, 0x2cb1,
- 0x2cb3, 0x2cb3,
- 0x2cb5, 0x2cb5,
- 0x2cb7, 0x2cb7,
- 0x2cb9, 0x2cb9,
- 0x2cbb, 0x2cbb,
- 0x2cbd, 0x2cbd,
- 0x2cbf, 0x2cbf,
- 0x2cc1, 0x2cc1,
- 0x2cc3, 0x2cc3,
- 0x2cc5, 0x2cc5,
- 0x2cc7, 0x2cc7,
- 0x2cc9, 0x2cc9,
- 0x2ccb, 0x2ccb,
- 0x2ccd, 0x2ccd,
- 0x2ccf, 0x2ccf,
- 0x2cd1, 0x2cd1,
- 0x2cd3, 0x2cd3,
- 0x2cd5, 0x2cd5,
- 0x2cd7, 0x2cd7,
- 0x2cd9, 0x2cd9,
- 0x2cdb, 0x2cdb,
- 0x2cdd, 0x2cdd,
- 0x2cdf, 0x2cdf,
- 0x2ce1, 0x2ce1,
- 0x2ce3, 0x2ce3,
- 0x2cec, 0x2cec,
- 0x2cee, 0x2cee,
- 0x2cf3, 0x2cf3,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0xa641, 0xa641,
- 0xa643, 0xa643,
- 0xa645, 0xa645,
- 0xa647, 0xa647,
- 0xa649, 0xa649,
- 0xa64b, 0xa64b,
- 0xa64d, 0xa64d,
- 0xa64f, 0xa64f,
- 0xa651, 0xa651,
- 0xa653, 0xa653,
- 0xa655, 0xa655,
- 0xa657, 0xa657,
- 0xa659, 0xa659,
- 0xa65b, 0xa65b,
- 0xa65d, 0xa65d,
- 0xa65f, 0xa65f,
- 0xa661, 0xa661,
- 0xa663, 0xa663,
- 0xa665, 0xa665,
- 0xa667, 0xa667,
- 0xa669, 0xa669,
- 0xa66b, 0xa66b,
- 0xa66d, 0xa66d,
- 0xa681, 0xa681,
- 0xa683, 0xa683,
- 0xa685, 0xa685,
- 0xa687, 0xa687,
- 0xa689, 0xa689,
- 0xa68b, 0xa68b,
- 0xa68d, 0xa68d,
- 0xa68f, 0xa68f,
- 0xa691, 0xa691,
- 0xa693, 0xa693,
- 0xa695, 0xa695,
- 0xa697, 0xa697,
- 0xa699, 0xa699,
- 0xa69b, 0xa69b,
- 0xa723, 0xa723,
- 0xa725, 0xa725,
- 0xa727, 0xa727,
- 0xa729, 0xa729,
- 0xa72b, 0xa72b,
- 0xa72d, 0xa72d,
- 0xa72f, 0xa72f,
- 0xa733, 0xa733,
- 0xa735, 0xa735,
- 0xa737, 0xa737,
- 0xa739, 0xa739,
- 0xa73b, 0xa73b,
- 0xa73d, 0xa73d,
- 0xa73f, 0xa73f,
- 0xa741, 0xa741,
- 0xa743, 0xa743,
- 0xa745, 0xa745,
- 0xa747, 0xa747,
- 0xa749, 0xa749,
- 0xa74b, 0xa74b,
- 0xa74d, 0xa74d,
- 0xa74f, 0xa74f,
- 0xa751, 0xa751,
- 0xa753, 0xa753,
- 0xa755, 0xa755,
- 0xa757, 0xa757,
- 0xa759, 0xa759,
- 0xa75b, 0xa75b,
- 0xa75d, 0xa75d,
- 0xa75f, 0xa75f,
- 0xa761, 0xa761,
- 0xa763, 0xa763,
- 0xa765, 0xa765,
- 0xa767, 0xa767,
- 0xa769, 0xa769,
- 0xa76b, 0xa76b,
- 0xa76d, 0xa76d,
- 0xa76f, 0xa76f,
- 0xa77a, 0xa77a,
- 0xa77c, 0xa77c,
- 0xa77f, 0xa77f,
- 0xa781, 0xa781,
- 0xa783, 0xa783,
- 0xa785, 0xa785,
- 0xa787, 0xa787,
- 0xa78c, 0xa78c,
- 0xa791, 0xa791,
- 0xa793, 0xa793,
- 0xa797, 0xa797,
- 0xa799, 0xa799,
- 0xa79b, 0xa79b,
- 0xa79d, 0xa79d,
- 0xa79f, 0xa79f,
- 0xa7a1, 0xa7a1,
- 0xa7a3, 0xa7a3,
- 0xa7a5, 0xa7a5,
- 0xa7a7, 0xa7a7,
- 0xa7a9, 0xa7a9,
- 0xa7b5, 0xa7b5,
- 0xa7b7, 0xa7b7,
- 0xab53, 0xab53,
- 0xab70, 0xabbf,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xff41, 0xff5a,
- 0x10428, 0x1044f,
- 0x104d8, 0x104fb,
- 0x10cc0, 0x10cf2,
- 0x118c0, 0x118df,
- 0x1e922, 0x1e943,
-}; /* CR_Changes_When_Titlecased */
-
-/* 'Changes_When_Casefolded': Derived Property */
-static const OnigCodePoint CR_Changes_When_Casefolded[] = {
- 603,
- 0x0041, 0x005a,
- 0x00b5, 0x00b5,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00df,
- 0x0100, 0x0100,
- 0x0102, 0x0102,
- 0x0104, 0x0104,
- 0x0106, 0x0106,
- 0x0108, 0x0108,
- 0x010a, 0x010a,
- 0x010c, 0x010c,
- 0x010e, 0x010e,
- 0x0110, 0x0110,
- 0x0112, 0x0112,
- 0x0114, 0x0114,
- 0x0116, 0x0116,
- 0x0118, 0x0118,
- 0x011a, 0x011a,
- 0x011c, 0x011c,
- 0x011e, 0x011e,
- 0x0120, 0x0120,
- 0x0122, 0x0122,
- 0x0124, 0x0124,
- 0x0126, 0x0126,
- 0x0128, 0x0128,
- 0x012a, 0x012a,
- 0x012c, 0x012c,
- 0x012e, 0x012e,
- 0x0130, 0x0130,
- 0x0132, 0x0132,
- 0x0134, 0x0134,
- 0x0136, 0x0136,
- 0x0139, 0x0139,
- 0x013b, 0x013b,
- 0x013d, 0x013d,
- 0x013f, 0x013f,
- 0x0141, 0x0141,
- 0x0143, 0x0143,
- 0x0145, 0x0145,
- 0x0147, 0x0147,
- 0x0149, 0x014a,
- 0x014c, 0x014c,
- 0x014e, 0x014e,
- 0x0150, 0x0150,
- 0x0152, 0x0152,
- 0x0154, 0x0154,
- 0x0156, 0x0156,
- 0x0158, 0x0158,
- 0x015a, 0x015a,
- 0x015c, 0x015c,
- 0x015e, 0x015e,
- 0x0160, 0x0160,
- 0x0162, 0x0162,
- 0x0164, 0x0164,
- 0x0166, 0x0166,
- 0x0168, 0x0168,
- 0x016a, 0x016a,
- 0x016c, 0x016c,
- 0x016e, 0x016e,
- 0x0170, 0x0170,
- 0x0172, 0x0172,
- 0x0174, 0x0174,
- 0x0176, 0x0176,
- 0x0178, 0x0179,
- 0x017b, 0x017b,
- 0x017d, 0x017d,
- 0x017f, 0x017f,
- 0x0181, 0x0182,
- 0x0184, 0x0184,
- 0x0186, 0x0187,
- 0x0189, 0x018b,
- 0x018e, 0x0191,
- 0x0193, 0x0194,
- 0x0196, 0x0198,
- 0x019c, 0x019d,
- 0x019f, 0x01a0,
- 0x01a2, 0x01a2,
- 0x01a4, 0x01a4,
- 0x01a6, 0x01a7,
- 0x01a9, 0x01a9,
- 0x01ac, 0x01ac,
- 0x01ae, 0x01af,
- 0x01b1, 0x01b3,
- 0x01b5, 0x01b5,
- 0x01b7, 0x01b8,
- 0x01bc, 0x01bc,
- 0x01c4, 0x01c5,
- 0x01c7, 0x01c8,
- 0x01ca, 0x01cb,
- 0x01cd, 0x01cd,
- 0x01cf, 0x01cf,
- 0x01d1, 0x01d1,
- 0x01d3, 0x01d3,
- 0x01d5, 0x01d5,
- 0x01d7, 0x01d7,
- 0x01d9, 0x01d9,
- 0x01db, 0x01db,
- 0x01de, 0x01de,
- 0x01e0, 0x01e0,
- 0x01e2, 0x01e2,
- 0x01e4, 0x01e4,
- 0x01e6, 0x01e6,
- 0x01e8, 0x01e8,
- 0x01ea, 0x01ea,
- 0x01ec, 0x01ec,
- 0x01ee, 0x01ee,
- 0x01f1, 0x01f2,
- 0x01f4, 0x01f4,
- 0x01f6, 0x01f8,
- 0x01fa, 0x01fa,
- 0x01fc, 0x01fc,
- 0x01fe, 0x01fe,
- 0x0200, 0x0200,
- 0x0202, 0x0202,
- 0x0204, 0x0204,
- 0x0206, 0x0206,
- 0x0208, 0x0208,
- 0x020a, 0x020a,
- 0x020c, 0x020c,
- 0x020e, 0x020e,
- 0x0210, 0x0210,
- 0x0212, 0x0212,
- 0x0214, 0x0214,
- 0x0216, 0x0216,
- 0x0218, 0x0218,
- 0x021a, 0x021a,
- 0x021c, 0x021c,
- 0x021e, 0x021e,
- 0x0220, 0x0220,
- 0x0222, 0x0222,
- 0x0224, 0x0224,
- 0x0226, 0x0226,
- 0x0228, 0x0228,
- 0x022a, 0x022a,
- 0x022c, 0x022c,
- 0x022e, 0x022e,
- 0x0230, 0x0230,
- 0x0232, 0x0232,
- 0x023a, 0x023b,
- 0x023d, 0x023e,
- 0x0241, 0x0241,
- 0x0243, 0x0246,
- 0x0248, 0x0248,
- 0x024a, 0x024a,
- 0x024c, 0x024c,
- 0x024e, 0x024e,
- 0x0345, 0x0345,
- 0x0370, 0x0370,
- 0x0372, 0x0372,
- 0x0376, 0x0376,
- 0x037f, 0x037f,
- 0x0386, 0x0386,
- 0x0388, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x038f,
- 0x0391, 0x03a1,
- 0x03a3, 0x03ab,
- 0x03c2, 0x03c2,
- 0x03cf, 0x03d1,
- 0x03d5, 0x03d6,
- 0x03d8, 0x03d8,
- 0x03da, 0x03da,
- 0x03dc, 0x03dc,
- 0x03de, 0x03de,
- 0x03e0, 0x03e0,
- 0x03e2, 0x03e2,
- 0x03e4, 0x03e4,
- 0x03e6, 0x03e6,
- 0x03e8, 0x03e8,
- 0x03ea, 0x03ea,
- 0x03ec, 0x03ec,
- 0x03ee, 0x03ee,
- 0x03f0, 0x03f1,
- 0x03f4, 0x03f5,
- 0x03f7, 0x03f7,
- 0x03f9, 0x03fa,
- 0x03fd, 0x042f,
- 0x0460, 0x0460,
- 0x0462, 0x0462,
- 0x0464, 0x0464,
- 0x0466, 0x0466,
- 0x0468, 0x0468,
- 0x046a, 0x046a,
- 0x046c, 0x046c,
- 0x046e, 0x046e,
- 0x0470, 0x0470,
- 0x0472, 0x0472,
- 0x0474, 0x0474,
- 0x0476, 0x0476,
- 0x0478, 0x0478,
- 0x047a, 0x047a,
- 0x047c, 0x047c,
- 0x047e, 0x047e,
- 0x0480, 0x0480,
- 0x048a, 0x048a,
- 0x048c, 0x048c,
- 0x048e, 0x048e,
- 0x0490, 0x0490,
- 0x0492, 0x0492,
- 0x0494, 0x0494,
- 0x0496, 0x0496,
- 0x0498, 0x0498,
- 0x049a, 0x049a,
- 0x049c, 0x049c,
- 0x049e, 0x049e,
- 0x04a0, 0x04a0,
- 0x04a2, 0x04a2,
- 0x04a4, 0x04a4,
- 0x04a6, 0x04a6,
- 0x04a8, 0x04a8,
- 0x04aa, 0x04aa,
- 0x04ac, 0x04ac,
- 0x04ae, 0x04ae,
- 0x04b0, 0x04b0,
- 0x04b2, 0x04b2,
- 0x04b4, 0x04b4,
- 0x04b6, 0x04b6,
- 0x04b8, 0x04b8,
- 0x04ba, 0x04ba,
- 0x04bc, 0x04bc,
- 0x04be, 0x04be,
- 0x04c0, 0x04c1,
- 0x04c3, 0x04c3,
- 0x04c5, 0x04c5,
- 0x04c7, 0x04c7,
- 0x04c9, 0x04c9,
- 0x04cb, 0x04cb,
- 0x04cd, 0x04cd,
- 0x04d0, 0x04d0,
- 0x04d2, 0x04d2,
- 0x04d4, 0x04d4,
- 0x04d6, 0x04d6,
- 0x04d8, 0x04d8,
- 0x04da, 0x04da,
- 0x04dc, 0x04dc,
- 0x04de, 0x04de,
- 0x04e0, 0x04e0,
- 0x04e2, 0x04e2,
- 0x04e4, 0x04e4,
- 0x04e6, 0x04e6,
- 0x04e8, 0x04e8,
- 0x04ea, 0x04ea,
- 0x04ec, 0x04ec,
- 0x04ee, 0x04ee,
- 0x04f0, 0x04f0,
- 0x04f2, 0x04f2,
- 0x04f4, 0x04f4,
- 0x04f6, 0x04f6,
- 0x04f8, 0x04f8,
- 0x04fa, 0x04fa,
- 0x04fc, 0x04fc,
- 0x04fe, 0x04fe,
- 0x0500, 0x0500,
- 0x0502, 0x0502,
- 0x0504, 0x0504,
- 0x0506, 0x0506,
- 0x0508, 0x0508,
- 0x050a, 0x050a,
- 0x050c, 0x050c,
- 0x050e, 0x050e,
- 0x0510, 0x0510,
- 0x0512, 0x0512,
- 0x0514, 0x0514,
- 0x0516, 0x0516,
- 0x0518, 0x0518,
- 0x051a, 0x051a,
- 0x051c, 0x051c,
- 0x051e, 0x051e,
- 0x0520, 0x0520,
- 0x0522, 0x0522,
- 0x0524, 0x0524,
- 0x0526, 0x0526,
- 0x0528, 0x0528,
- 0x052a, 0x052a,
- 0x052c, 0x052c,
- 0x052e, 0x052e,
- 0x0531, 0x0556,
- 0x0587, 0x0587,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x13f8, 0x13fd,
- 0x1c80, 0x1c88,
- 0x1e00, 0x1e00,
- 0x1e02, 0x1e02,
- 0x1e04, 0x1e04,
- 0x1e06, 0x1e06,
- 0x1e08, 0x1e08,
- 0x1e0a, 0x1e0a,
- 0x1e0c, 0x1e0c,
- 0x1e0e, 0x1e0e,
- 0x1e10, 0x1e10,
- 0x1e12, 0x1e12,
- 0x1e14, 0x1e14,
- 0x1e16, 0x1e16,
- 0x1e18, 0x1e18,
- 0x1e1a, 0x1e1a,
- 0x1e1c, 0x1e1c,
- 0x1e1e, 0x1e1e,
- 0x1e20, 0x1e20,
- 0x1e22, 0x1e22,
- 0x1e24, 0x1e24,
- 0x1e26, 0x1e26,
- 0x1e28, 0x1e28,
- 0x1e2a, 0x1e2a,
- 0x1e2c, 0x1e2c,
- 0x1e2e, 0x1e2e,
- 0x1e30, 0x1e30,
- 0x1e32, 0x1e32,
- 0x1e34, 0x1e34,
- 0x1e36, 0x1e36,
- 0x1e38, 0x1e38,
- 0x1e3a, 0x1e3a,
- 0x1e3c, 0x1e3c,
- 0x1e3e, 0x1e3e,
- 0x1e40, 0x1e40,
- 0x1e42, 0x1e42,
- 0x1e44, 0x1e44,
- 0x1e46, 0x1e46,
- 0x1e48, 0x1e48,
- 0x1e4a, 0x1e4a,
- 0x1e4c, 0x1e4c,
- 0x1e4e, 0x1e4e,
- 0x1e50, 0x1e50,
- 0x1e52, 0x1e52,
- 0x1e54, 0x1e54,
- 0x1e56, 0x1e56,
- 0x1e58, 0x1e58,
- 0x1e5a, 0x1e5a,
- 0x1e5c, 0x1e5c,
- 0x1e5e, 0x1e5e,
- 0x1e60, 0x1e60,
- 0x1e62, 0x1e62,
- 0x1e64, 0x1e64,
- 0x1e66, 0x1e66,
- 0x1e68, 0x1e68,
- 0x1e6a, 0x1e6a,
- 0x1e6c, 0x1e6c,
- 0x1e6e, 0x1e6e,
- 0x1e70, 0x1e70,
- 0x1e72, 0x1e72,
- 0x1e74, 0x1e74,
- 0x1e76, 0x1e76,
- 0x1e78, 0x1e78,
- 0x1e7a, 0x1e7a,
- 0x1e7c, 0x1e7c,
- 0x1e7e, 0x1e7e,
- 0x1e80, 0x1e80,
- 0x1e82, 0x1e82,
- 0x1e84, 0x1e84,
- 0x1e86, 0x1e86,
- 0x1e88, 0x1e88,
- 0x1e8a, 0x1e8a,
- 0x1e8c, 0x1e8c,
- 0x1e8e, 0x1e8e,
- 0x1e90, 0x1e90,
- 0x1e92, 0x1e92,
- 0x1e94, 0x1e94,
- 0x1e9a, 0x1e9b,
- 0x1e9e, 0x1e9e,
- 0x1ea0, 0x1ea0,
- 0x1ea2, 0x1ea2,
- 0x1ea4, 0x1ea4,
- 0x1ea6, 0x1ea6,
- 0x1ea8, 0x1ea8,
- 0x1eaa, 0x1eaa,
- 0x1eac, 0x1eac,
- 0x1eae, 0x1eae,
- 0x1eb0, 0x1eb0,
- 0x1eb2, 0x1eb2,
- 0x1eb4, 0x1eb4,
- 0x1eb6, 0x1eb6,
- 0x1eb8, 0x1eb8,
- 0x1eba, 0x1eba,
- 0x1ebc, 0x1ebc,
- 0x1ebe, 0x1ebe,
- 0x1ec0, 0x1ec0,
- 0x1ec2, 0x1ec2,
- 0x1ec4, 0x1ec4,
- 0x1ec6, 0x1ec6,
- 0x1ec8, 0x1ec8,
- 0x1eca, 0x1eca,
- 0x1ecc, 0x1ecc,
- 0x1ece, 0x1ece,
- 0x1ed0, 0x1ed0,
- 0x1ed2, 0x1ed2,
- 0x1ed4, 0x1ed4,
- 0x1ed6, 0x1ed6,
- 0x1ed8, 0x1ed8,
- 0x1eda, 0x1eda,
- 0x1edc, 0x1edc,
- 0x1ede, 0x1ede,
- 0x1ee0, 0x1ee0,
- 0x1ee2, 0x1ee2,
- 0x1ee4, 0x1ee4,
- 0x1ee6, 0x1ee6,
- 0x1ee8, 0x1ee8,
- 0x1eea, 0x1eea,
- 0x1eec, 0x1eec,
- 0x1eee, 0x1eee,
- 0x1ef0, 0x1ef0,
- 0x1ef2, 0x1ef2,
- 0x1ef4, 0x1ef4,
- 0x1ef6, 0x1ef6,
- 0x1ef8, 0x1ef8,
- 0x1efa, 0x1efa,
- 0x1efc, 0x1efc,
- 0x1efe, 0x1efe,
- 0x1f08, 0x1f0f,
- 0x1f18, 0x1f1d,
- 0x1f28, 0x1f2f,
- 0x1f38, 0x1f3f,
- 0x1f48, 0x1f4d,
- 0x1f59, 0x1f59,
- 0x1f5b, 0x1f5b,
- 0x1f5d, 0x1f5d,
- 0x1f5f, 0x1f5f,
- 0x1f68, 0x1f6f,
- 0x1f80, 0x1faf,
- 0x1fb2, 0x1fb4,
- 0x1fb7, 0x1fbc,
- 0x1fc2, 0x1fc4,
- 0x1fc7, 0x1fcc,
- 0x1fd8, 0x1fdb,
- 0x1fe8, 0x1fec,
- 0x1ff2, 0x1ff4,
- 0x1ff7, 0x1ffc,
- 0x2126, 0x2126,
- 0x212a, 0x212b,
- 0x2132, 0x2132,
- 0x2160, 0x216f,
- 0x2183, 0x2183,
- 0x24b6, 0x24cf,
- 0x2c00, 0x2c2e,
- 0x2c60, 0x2c60,
- 0x2c62, 0x2c64,
- 0x2c67, 0x2c67,
- 0x2c69, 0x2c69,
- 0x2c6b, 0x2c6b,
- 0x2c6d, 0x2c70,
- 0x2c72, 0x2c72,
- 0x2c75, 0x2c75,
- 0x2c7e, 0x2c80,
- 0x2c82, 0x2c82,
- 0x2c84, 0x2c84,
- 0x2c86, 0x2c86,
- 0x2c88, 0x2c88,
- 0x2c8a, 0x2c8a,
- 0x2c8c, 0x2c8c,
- 0x2c8e, 0x2c8e,
- 0x2c90, 0x2c90,
- 0x2c92, 0x2c92,
- 0x2c94, 0x2c94,
- 0x2c96, 0x2c96,
- 0x2c98, 0x2c98,
- 0x2c9a, 0x2c9a,
- 0x2c9c, 0x2c9c,
- 0x2c9e, 0x2c9e,
- 0x2ca0, 0x2ca0,
- 0x2ca2, 0x2ca2,
- 0x2ca4, 0x2ca4,
- 0x2ca6, 0x2ca6,
- 0x2ca8, 0x2ca8,
- 0x2caa, 0x2caa,
- 0x2cac, 0x2cac,
- 0x2cae, 0x2cae,
- 0x2cb0, 0x2cb0,
- 0x2cb2, 0x2cb2,
- 0x2cb4, 0x2cb4,
- 0x2cb6, 0x2cb6,
- 0x2cb8, 0x2cb8,
- 0x2cba, 0x2cba,
- 0x2cbc, 0x2cbc,
- 0x2cbe, 0x2cbe,
- 0x2cc0, 0x2cc0,
- 0x2cc2, 0x2cc2,
- 0x2cc4, 0x2cc4,
- 0x2cc6, 0x2cc6,
- 0x2cc8, 0x2cc8,
- 0x2cca, 0x2cca,
- 0x2ccc, 0x2ccc,
- 0x2cce, 0x2cce,
- 0x2cd0, 0x2cd0,
- 0x2cd2, 0x2cd2,
- 0x2cd4, 0x2cd4,
- 0x2cd6, 0x2cd6,
- 0x2cd8, 0x2cd8,
- 0x2cda, 0x2cda,
- 0x2cdc, 0x2cdc,
- 0x2cde, 0x2cde,
- 0x2ce0, 0x2ce0,
- 0x2ce2, 0x2ce2,
- 0x2ceb, 0x2ceb,
- 0x2ced, 0x2ced,
- 0x2cf2, 0x2cf2,
- 0xa640, 0xa640,
- 0xa642, 0xa642,
- 0xa644, 0xa644,
- 0xa646, 0xa646,
- 0xa648, 0xa648,
- 0xa64a, 0xa64a,
- 0xa64c, 0xa64c,
- 0xa64e, 0xa64e,
- 0xa650, 0xa650,
- 0xa652, 0xa652,
- 0xa654, 0xa654,
- 0xa656, 0xa656,
- 0xa658, 0xa658,
- 0xa65a, 0xa65a,
- 0xa65c, 0xa65c,
- 0xa65e, 0xa65e,
- 0xa660, 0xa660,
- 0xa662, 0xa662,
- 0xa664, 0xa664,
- 0xa666, 0xa666,
- 0xa668, 0xa668,
- 0xa66a, 0xa66a,
- 0xa66c, 0xa66c,
- 0xa680, 0xa680,
- 0xa682, 0xa682,
- 0xa684, 0xa684,
- 0xa686, 0xa686,
- 0xa688, 0xa688,
- 0xa68a, 0xa68a,
- 0xa68c, 0xa68c,
- 0xa68e, 0xa68e,
- 0xa690, 0xa690,
- 0xa692, 0xa692,
- 0xa694, 0xa694,
- 0xa696, 0xa696,
- 0xa698, 0xa698,
- 0xa69a, 0xa69a,
- 0xa722, 0xa722,
- 0xa724, 0xa724,
- 0xa726, 0xa726,
- 0xa728, 0xa728,
- 0xa72a, 0xa72a,
- 0xa72c, 0xa72c,
- 0xa72e, 0xa72e,
- 0xa732, 0xa732,
- 0xa734, 0xa734,
- 0xa736, 0xa736,
- 0xa738, 0xa738,
- 0xa73a, 0xa73a,
- 0xa73c, 0xa73c,
- 0xa73e, 0xa73e,
- 0xa740, 0xa740,
- 0xa742, 0xa742,
- 0xa744, 0xa744,
- 0xa746, 0xa746,
- 0xa748, 0xa748,
- 0xa74a, 0xa74a,
- 0xa74c, 0xa74c,
- 0xa74e, 0xa74e,
- 0xa750, 0xa750,
- 0xa752, 0xa752,
- 0xa754, 0xa754,
- 0xa756, 0xa756,
- 0xa758, 0xa758,
- 0xa75a, 0xa75a,
- 0xa75c, 0xa75c,
- 0xa75e, 0xa75e,
- 0xa760, 0xa760,
- 0xa762, 0xa762,
- 0xa764, 0xa764,
- 0xa766, 0xa766,
- 0xa768, 0xa768,
- 0xa76a, 0xa76a,
- 0xa76c, 0xa76c,
- 0xa76e, 0xa76e,
- 0xa779, 0xa779,
- 0xa77b, 0xa77b,
- 0xa77d, 0xa77e,
- 0xa780, 0xa780,
- 0xa782, 0xa782,
- 0xa784, 0xa784,
- 0xa786, 0xa786,
- 0xa78b, 0xa78b,
- 0xa78d, 0xa78d,
- 0xa790, 0xa790,
- 0xa792, 0xa792,
- 0xa796, 0xa796,
- 0xa798, 0xa798,
- 0xa79a, 0xa79a,
- 0xa79c, 0xa79c,
- 0xa79e, 0xa79e,
- 0xa7a0, 0xa7a0,
- 0xa7a2, 0xa7a2,
- 0xa7a4, 0xa7a4,
- 0xa7a6, 0xa7a6,
- 0xa7a8, 0xa7a8,
- 0xa7aa, 0xa7ae,
- 0xa7b0, 0xa7b4,
- 0xa7b6, 0xa7b6,
- 0xab70, 0xabbf,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xff21, 0xff3a,
- 0x10400, 0x10427,
- 0x104b0, 0x104d3,
- 0x10c80, 0x10cb2,
- 0x118a0, 0x118bf,
- 0x1e900, 0x1e921,
-}; /* CR_Changes_When_Casefolded */
-
-/* 'Changes_When_Casemapped': Derived Property */
-static const OnigCodePoint CR_Changes_When_Casemapped[] = {
- 116,
- 0x0041, 0x005a,
- 0x0061, 0x007a,
- 0x00b5, 0x00b5,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00f6,
- 0x00f8, 0x0137,
- 0x0139, 0x018c,
- 0x018e, 0x019a,
- 0x019c, 0x01a9,
- 0x01ac, 0x01b9,
- 0x01bc, 0x01bd,
- 0x01bf, 0x01bf,
- 0x01c4, 0x0220,
- 0x0222, 0x0233,
- 0x023a, 0x0254,
- 0x0256, 0x0257,
- 0x0259, 0x0259,
- 0x025b, 0x025c,
- 0x0260, 0x0261,
- 0x0263, 0x0263,
- 0x0265, 0x0266,
- 0x0268, 0x026c,
- 0x026f, 0x026f,
- 0x0271, 0x0272,
- 0x0275, 0x0275,
- 0x027d, 0x027d,
- 0x0280, 0x0280,
- 0x0283, 0x0283,
- 0x0287, 0x028c,
- 0x0292, 0x0292,
- 0x029d, 0x029e,
- 0x0345, 0x0345,
- 0x0370, 0x0373,
- 0x0376, 0x0377,
- 0x037b, 0x037d,
- 0x037f, 0x037f,
- 0x0386, 0x0386,
- 0x0388, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03d1,
- 0x03d5, 0x03f5,
- 0x03f7, 0x03fb,
- 0x03fd, 0x0481,
- 0x048a, 0x052f,
- 0x0531, 0x0556,
- 0x0561, 0x0587,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x13a0, 0x13f5,
- 0x13f8, 0x13fd,
- 0x1c80, 0x1c88,
- 0x1d79, 0x1d79,
- 0x1d7d, 0x1d7d,
- 0x1e00, 0x1e9b,
- 0x1e9e, 0x1e9e,
- 0x1ea0, 0x1f15,
- 0x1f18, 0x1f1d,
- 0x1f20, 0x1f45,
- 0x1f48, 0x1f4d,
- 0x1f50, 0x1f57,
- 0x1f59, 0x1f59,
- 0x1f5b, 0x1f5b,
- 0x1f5d, 0x1f5d,
- 0x1f5f, 0x1f7d,
- 0x1f80, 0x1fb4,
- 0x1fb6, 0x1fbc,
- 0x1fbe, 0x1fbe,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fcc,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fdb,
- 0x1fe0, 0x1fec,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ffc,
- 0x2126, 0x2126,
- 0x212a, 0x212b,
- 0x2132, 0x2132,
- 0x214e, 0x214e,
- 0x2160, 0x217f,
- 0x2183, 0x2184,
- 0x24b6, 0x24e9,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2c70,
- 0x2c72, 0x2c73,
- 0x2c75, 0x2c76,
- 0x2c7e, 0x2ce3,
- 0x2ceb, 0x2cee,
- 0x2cf2, 0x2cf3,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0xa640, 0xa66d,
- 0xa680, 0xa69b,
- 0xa722, 0xa72f,
- 0xa732, 0xa76f,
- 0xa779, 0xa787,
- 0xa78b, 0xa78d,
- 0xa790, 0xa793,
- 0xa796, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xab53, 0xab53,
- 0xab70, 0xabbf,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xff21, 0xff3a,
- 0xff41, 0xff5a,
- 0x10400, 0x1044f,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10c80, 0x10cb2,
- 0x10cc0, 0x10cf2,
- 0x118a0, 0x118df,
- 0x1e900, 0x1e943,
-}; /* CR_Changes_When_Casemapped */
-
-/* 'ID_Start': Derived Property */
-static const OnigCodePoint CR_ID_Start[] = {
- 571,
- 0x0041, 0x005a,
- 0x0061, 0x007a,
- 0x00aa, 0x00aa,
- 0x00b5, 0x00b5,
- 0x00ba, 0x00ba,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00f6,
- 0x00f8, 0x02c1,
- 0x02c6, 0x02d1,
- 0x02e0, 0x02e4,
- 0x02ec, 0x02ec,
- 0x02ee, 0x02ee,
- 0x0370, 0x0374,
- 0x0376, 0x0377,
- 0x037a, 0x037d,
- 0x037f, 0x037f,
- 0x0386, 0x0386,
- 0x0388, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03f5,
- 0x03f7, 0x0481,
- 0x048a, 0x052f,
- 0x0531, 0x0556,
- 0x0559, 0x0559,
- 0x0561, 0x0587,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f2,
- 0x0620, 0x064a,
- 0x066e, 0x066f,
- 0x0671, 0x06d3,
- 0x06d5, 0x06d5,
- 0x06e5, 0x06e6,
- 0x06ee, 0x06ef,
- 0x06fa, 0x06fc,
- 0x06ff, 0x06ff,
- 0x0710, 0x0710,
- 0x0712, 0x072f,
- 0x074d, 0x07a5,
- 0x07b1, 0x07b1,
- 0x07ca, 0x07ea,
- 0x07f4, 0x07f5,
- 0x07fa, 0x07fa,
- 0x0800, 0x0815,
- 0x081a, 0x081a,
- 0x0824, 0x0824,
- 0x0828, 0x0828,
- 0x0840, 0x0858,
- 0x08a0, 0x08b4,
- 0x08b6, 0x08bd,
- 0x0904, 0x0939,
- 0x093d, 0x093d,
- 0x0950, 0x0950,
- 0x0958, 0x0961,
- 0x0971, 0x0980,
- 0x0985, 0x098c,
- 0x098f, 0x0990,
- 0x0993, 0x09a8,
- 0x09aa, 0x09b0,
- 0x09b2, 0x09b2,
- 0x09b6, 0x09b9,
- 0x09bd, 0x09bd,
- 0x09ce, 0x09ce,
- 0x09dc, 0x09dd,
- 0x09df, 0x09e1,
- 0x09f0, 0x09f1,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a72, 0x0a74,
- 0x0a85, 0x0a8d,
- 0x0a8f, 0x0a91,
- 0x0a93, 0x0aa8,
- 0x0aaa, 0x0ab0,
- 0x0ab2, 0x0ab3,
- 0x0ab5, 0x0ab9,
- 0x0abd, 0x0abd,
- 0x0ad0, 0x0ad0,
- 0x0ae0, 0x0ae1,
- 0x0af9, 0x0af9,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3d, 0x0b3d,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b61,
- 0x0b71, 0x0b71,
- 0x0b83, 0x0b83,
- 0x0b85, 0x0b8a,
- 0x0b8e, 0x0b90,
- 0x0b92, 0x0b95,
- 0x0b99, 0x0b9a,
- 0x0b9c, 0x0b9c,
- 0x0b9e, 0x0b9f,
- 0x0ba3, 0x0ba4,
- 0x0ba8, 0x0baa,
- 0x0bae, 0x0bb9,
- 0x0bd0, 0x0bd0,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c3d,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c61,
- 0x0c80, 0x0c80,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbd, 0x0cbd,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce1,
- 0x0cf1, 0x0cf2,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d3d,
- 0x0d4e, 0x0d4e,
- 0x0d54, 0x0d56,
- 0x0d5f, 0x0d61,
- 0x0d7a, 0x0d7f,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0e01, 0x0e30,
- 0x0e32, 0x0e33,
- 0x0e40, 0x0e46,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb0,
- 0x0eb2, 0x0eb3,
- 0x0ebd, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0edc, 0x0edf,
- 0x0f00, 0x0f00,
- 0x0f40, 0x0f47,
- 0x0f49, 0x0f6c,
- 0x0f88, 0x0f8c,
- 0x1000, 0x102a,
- 0x103f, 0x103f,
- 0x1050, 0x1055,
- 0x105a, 0x105d,
- 0x1061, 0x1061,
- 0x1065, 0x1066,
- 0x106e, 0x1070,
- 0x1075, 0x1081,
- 0x108e, 0x108e,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x10d0, 0x10fa,
- 0x10fc, 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,
- 0x1380, 0x138f,
- 0x13a0, 0x13f5,
- 0x13f8, 0x13fd,
- 0x1401, 0x166c,
- 0x166f, 0x167f,
- 0x1681, 0x169a,
- 0x16a0, 0x16ea,
- 0x16ee, 0x16f8,
- 0x1700, 0x170c,
- 0x170e, 0x1711,
- 0x1720, 0x1731,
- 0x1740, 0x1751,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1780, 0x17b3,
- 0x17d7, 0x17d7,
- 0x17dc, 0x17dc,
- 0x1820, 0x1877,
- 0x1880, 0x18a8,
- 0x18aa, 0x18aa,
- 0x18b0, 0x18f5,
- 0x1900, 0x191e,
- 0x1950, 0x196d,
- 0x1970, 0x1974,
- 0x1980, 0x19ab,
- 0x19b0, 0x19c9,
- 0x1a00, 0x1a16,
- 0x1a20, 0x1a54,
- 0x1aa7, 0x1aa7,
- 0x1b05, 0x1b33,
- 0x1b45, 0x1b4b,
- 0x1b83, 0x1ba0,
- 0x1bae, 0x1baf,
- 0x1bba, 0x1be5,
- 0x1c00, 0x1c23,
- 0x1c4d, 0x1c4f,
- 0x1c5a, 0x1c7d,
- 0x1c80, 0x1c88,
- 0x1ce9, 0x1cec,
- 0x1cee, 0x1cf1,
- 0x1cf5, 0x1cf6,
- 0x1d00, 0x1dbf,
- 0x1e00, 0x1f15,
- 0x1f18, 0x1f1d,
- 0x1f20, 0x1f45,
- 0x1f48, 0x1f4d,
- 0x1f50, 0x1f57,
- 0x1f59, 0x1f59,
- 0x1f5b, 0x1f5b,
- 0x1f5d, 0x1f5d,
- 0x1f5f, 0x1f7d,
- 0x1f80, 0x1fb4,
- 0x1fb6, 0x1fbc,
- 0x1fbe, 0x1fbe,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fcc,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fdb,
- 0x1fe0, 0x1fec,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ffc,
- 0x2071, 0x2071,
- 0x207f, 0x207f,
- 0x2090, 0x209c,
- 0x2102, 0x2102,
- 0x2107, 0x2107,
- 0x210a, 0x2113,
- 0x2115, 0x2115,
- 0x2118, 0x211d,
- 0x2124, 0x2124,
- 0x2126, 0x2126,
- 0x2128, 0x2128,
- 0x212a, 0x2139,
- 0x213c, 0x213f,
- 0x2145, 0x2149,
- 0x214e, 0x214e,
- 0x2160, 0x2188,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2ce4,
- 0x2ceb, 0x2cee,
- 0x2cf2, 0x2cf3,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0x2d30, 0x2d67,
- 0x2d6f, 0x2d6f,
- 0x2d80, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0x3005, 0x3007,
- 0x3021, 0x3029,
- 0x3031, 0x3035,
- 0x3038, 0x303c,
- 0x3041, 0x3096,
- 0x309b, 0x309f,
- 0x30a1, 0x30fa,
- 0x30fc, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x31a0, 0x31ba,
- 0x31f0, 0x31ff,
- 0x3400, 0x4db5,
- 0x4e00, 0x9fd5,
- 0xa000, 0xa48c,
- 0xa4d0, 0xa4fd,
- 0xa500, 0xa60c,
- 0xa610, 0xa61f,
- 0xa62a, 0xa62b,
- 0xa640, 0xa66e,
- 0xa67f, 0xa69d,
- 0xa6a0, 0xa6ef,
- 0xa717, 0xa71f,
- 0xa722, 0xa788,
- 0xa78b, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7f7, 0xa801,
- 0xa803, 0xa805,
- 0xa807, 0xa80a,
- 0xa80c, 0xa822,
- 0xa840, 0xa873,
- 0xa882, 0xa8b3,
- 0xa8f2, 0xa8f7,
- 0xa8fb, 0xa8fb,
- 0xa8fd, 0xa8fd,
- 0xa90a, 0xa925,
- 0xa930, 0xa946,
- 0xa960, 0xa97c,
- 0xa984, 0xa9b2,
- 0xa9cf, 0xa9cf,
- 0xa9e0, 0xa9e4,
- 0xa9e6, 0xa9ef,
- 0xa9fa, 0xa9fe,
- 0xaa00, 0xaa28,
- 0xaa40, 0xaa42,
- 0xaa44, 0xaa4b,
- 0xaa60, 0xaa76,
- 0xaa7a, 0xaa7a,
- 0xaa7e, 0xaaaf,
- 0xaab1, 0xaab1,
- 0xaab5, 0xaab6,
- 0xaab9, 0xaabd,
- 0xaac0, 0xaac0,
- 0xaac2, 0xaac2,
- 0xaadb, 0xaadd,
- 0xaae0, 0xaaea,
- 0xaaf2, 0xaaf4,
- 0xab01, 0xab06,
- 0xab09, 0xab0e,
- 0xab11, 0xab16,
- 0xab20, 0xab26,
- 0xab28, 0xab2e,
- 0xab30, 0xab5a,
- 0xab5c, 0xab65,
- 0xab70, 0xabe2,
- 0xac00, 0xd7a3,
- 0xd7b0, 0xd7c6,
- 0xd7cb, 0xd7fb,
- 0xf900, 0xfa6d,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb1d,
- 0xfb1f, 0xfb28,
- 0xfb2a, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3d,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfb,
- 0xfe70, 0xfe74,
- 0xfe76, 0xfefc,
- 0xff21, 0xff3a,
- 0xff41, 0xff5a,
- 0xff66, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
- 0x10000, 0x1000b,
- 0x1000d, 0x10026,
- 0x10028, 0x1003a,
- 0x1003c, 0x1003d,
- 0x1003f, 0x1004d,
- 0x10050, 0x1005d,
- 0x10080, 0x100fa,
- 0x10140, 0x10174,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x10300, 0x1031f,
- 0x10330, 0x1034a,
- 0x10350, 0x10375,
- 0x10380, 0x1039d,
- 0x103a0, 0x103c3,
- 0x103c8, 0x103cf,
- 0x103d1, 0x103d5,
- 0x10400, 0x1049d,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x10855,
- 0x10860, 0x10876,
- 0x10880, 0x1089e,
- 0x108e0, 0x108f2,
- 0x108f4, 0x108f5,
- 0x10900, 0x10915,
- 0x10920, 0x10939,
- 0x10980, 0x109b7,
- 0x109be, 0x109bf,
- 0x10a00, 0x10a00,
- 0x10a10, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a60, 0x10a7c,
- 0x10a80, 0x10a9c,
- 0x10ac0, 0x10ac7,
- 0x10ac9, 0x10ae4,
- 0x10b00, 0x10b35,
- 0x10b40, 0x10b55,
- 0x10b60, 0x10b72,
- 0x10b80, 0x10b91,
- 0x10c00, 0x10c48,
- 0x10c80, 0x10cb2,
- 0x10cc0, 0x10cf2,
- 0x11003, 0x11037,
- 0x11083, 0x110af,
- 0x110d0, 0x110e8,
- 0x11103, 0x11126,
- 0x11150, 0x11172,
- 0x11176, 0x11176,
- 0x11183, 0x111b2,
- 0x111c1, 0x111c4,
- 0x111da, 0x111da,
- 0x111dc, 0x111dc,
- 0x11200, 0x11211,
- 0x11213, 0x1122b,
- 0x11280, 0x11286,
- 0x11288, 0x11288,
- 0x1128a, 0x1128d,
- 0x1128f, 0x1129d,
- 0x1129f, 0x112a8,
- 0x112b0, 0x112de,
- 0x11305, 0x1130c,
- 0x1130f, 0x11310,
- 0x11313, 0x11328,
- 0x1132a, 0x11330,
- 0x11332, 0x11333,
- 0x11335, 0x11339,
- 0x1133d, 0x1133d,
- 0x11350, 0x11350,
- 0x1135d, 0x11361,
- 0x11400, 0x11434,
- 0x11447, 0x1144a,
- 0x11480, 0x114af,
- 0x114c4, 0x114c5,
- 0x114c7, 0x114c7,
- 0x11580, 0x115ae,
- 0x115d8, 0x115db,
- 0x11600, 0x1162f,
- 0x11644, 0x11644,
- 0x11680, 0x116aa,
- 0x11700, 0x11719,
- 0x118a0, 0x118df,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x11c00, 0x11c08,
- 0x11c0a, 0x11c2e,
- 0x11c40, 0x11c40,
- 0x11c72, 0x11c8f,
- 0x12000, 0x12399,
- 0x12400, 0x1246e,
- 0x12480, 0x12543,
- 0x13000, 0x1342e,
- 0x14400, 0x14646,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16ad0, 0x16aed,
- 0x16b00, 0x16b2f,
- 0x16b40, 0x16b43,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f50,
- 0x16f93, 0x16f9f,
- 0x16fe0, 0x16fe0,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 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, 0x1d6c0,
- 0x1d6c2, 0x1d6da,
- 0x1d6dc, 0x1d6fa,
- 0x1d6fc, 0x1d714,
- 0x1d716, 0x1d734,
- 0x1d736, 0x1d74e,
- 0x1d750, 0x1d76e,
- 0x1d770, 0x1d788,
- 0x1d78a, 0x1d7a8,
- 0x1d7aa, 0x1d7c2,
- 0x1d7c4, 0x1d7cb,
- 0x1e800, 0x1e8c4,
- 0x1e900, 0x1e943,
- 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,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
-}; /* CR_ID_Start */
-
-/* 'ID_Continue': Derived Property */
-static const OnigCodePoint CR_ID_Continue[] = {
- 676,
- 0x0030, 0x0039,
- 0x0041, 0x005a,
- 0x005f, 0x005f,
- 0x0061, 0x007a,
- 0x00aa, 0x00aa,
- 0x00b5, 0x00b5,
- 0x00b7, 0x00b7,
- 0x00ba, 0x00ba,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00f6,
- 0x00f8, 0x02c1,
- 0x02c6, 0x02d1,
- 0x02e0, 0x02e4,
- 0x02ec, 0x02ec,
- 0x02ee, 0x02ee,
- 0x0300, 0x0374,
- 0x0376, 0x0377,
- 0x037a, 0x037d,
- 0x037f, 0x037f,
- 0x0386, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03f5,
- 0x03f7, 0x0481,
- 0x0483, 0x0487,
- 0x048a, 0x052f,
- 0x0531, 0x0556,
- 0x0559, 0x0559,
- 0x0561, 0x0587,
- 0x0591, 0x05bd,
- 0x05bf, 0x05bf,
- 0x05c1, 0x05c2,
- 0x05c4, 0x05c5,
- 0x05c7, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f2,
- 0x0610, 0x061a,
- 0x0620, 0x0669,
- 0x066e, 0x06d3,
- 0x06d5, 0x06dc,
- 0x06df, 0x06e8,
- 0x06ea, 0x06fc,
- 0x06ff, 0x06ff,
- 0x0710, 0x074a,
- 0x074d, 0x07b1,
- 0x07c0, 0x07f5,
- 0x07fa, 0x07fa,
- 0x0800, 0x082d,
- 0x0840, 0x085b,
- 0x08a0, 0x08b4,
- 0x08b6, 0x08bd,
- 0x08d4, 0x08e1,
- 0x08e3, 0x0963,
- 0x0966, 0x096f,
- 0x0971, 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, 0x09f1,
- 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, 0x0a75,
- 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, 0x0aef,
- 0x0af9, 0x0af9,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b63,
- 0x0b66, 0x0b6f,
- 0x0b71, 0x0b71,
- 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, 0x0bef,
- 0x0c00, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c80, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d01, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4e,
- 0x0d54, 0x0d57,
- 0x0d5f, 0x0d63,
- 0x0d66, 0x0d6f,
- 0x0d7a, 0x0d7f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0de6, 0x0def,
- 0x0df2, 0x0df3,
- 0x0e01, 0x0e3a,
- 0x0e40, 0x0e4e,
- 0x0e50, 0x0e59,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edf,
- 0x0f00, 0x0f00,
- 0x0f18, 0x0f19,
- 0x0f20, 0x0f29,
- 0x0f35, 0x0f35,
- 0x0f37, 0x0f37,
- 0x0f39, 0x0f39,
- 0x0f3e, 0x0f47,
- 0x0f49, 0x0f6c,
- 0x0f71, 0x0f84,
- 0x0f86, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fc6, 0x0fc6,
- 0x1000, 0x1049,
- 0x1050, 0x109d,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x10d0, 0x10fa,
- 0x10fc, 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, 0x135f,
- 0x1369, 0x1371,
- 0x1380, 0x138f,
- 0x13a0, 0x13f5,
- 0x13f8, 0x13fd,
- 0x1401, 0x166c,
- 0x166f, 0x167f,
- 0x1681, 0x169a,
- 0x16a0, 0x16ea,
- 0x16ee, 0x16f8,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1734,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17d3,
- 0x17d7, 0x17d7,
- 0x17dc, 0x17dd,
- 0x17e0, 0x17e9,
- 0x180b, 0x180d,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18aa,
- 0x18b0, 0x18f5,
- 0x1900, 0x191e,
- 0x1920, 0x192b,
- 0x1930, 0x193b,
- 0x1946, 0x196d,
- 0x1970, 0x1974,
- 0x1980, 0x19ab,
- 0x19b0, 0x19c9,
- 0x19d0, 0x19da,
- 0x1a00, 0x1a1b,
- 0x1a20, 0x1a5e,
- 0x1a60, 0x1a7c,
- 0x1a7f, 0x1a89,
- 0x1a90, 0x1a99,
- 0x1aa7, 0x1aa7,
- 0x1ab0, 0x1abd,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b59,
- 0x1b6b, 0x1b73,
- 0x1b80, 0x1bf3,
- 0x1c00, 0x1c37,
- 0x1c40, 0x1c49,
- 0x1c4d, 0x1c7d,
- 0x1c80, 0x1c88,
- 0x1cd0, 0x1cd2,
- 0x1cd4, 0x1cf6,
- 0x1cf8, 0x1cf9,
- 0x1d00, 0x1df5,
- 0x1dfb, 0x1f15,
- 0x1f18, 0x1f1d,
- 0x1f20, 0x1f45,
- 0x1f48, 0x1f4d,
- 0x1f50, 0x1f57,
- 0x1f59, 0x1f59,
- 0x1f5b, 0x1f5b,
- 0x1f5d, 0x1f5d,
- 0x1f5f, 0x1f7d,
- 0x1f80, 0x1fb4,
- 0x1fb6, 0x1fbc,
- 0x1fbe, 0x1fbe,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fcc,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fdb,
- 0x1fe0, 0x1fec,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ffc,
- 0x203f, 0x2040,
- 0x2054, 0x2054,
- 0x2071, 0x2071,
- 0x207f, 0x207f,
- 0x2090, 0x209c,
- 0x20d0, 0x20dc,
- 0x20e1, 0x20e1,
- 0x20e5, 0x20f0,
- 0x2102, 0x2102,
- 0x2107, 0x2107,
- 0x210a, 0x2113,
- 0x2115, 0x2115,
- 0x2118, 0x211d,
- 0x2124, 0x2124,
- 0x2126, 0x2126,
- 0x2128, 0x2128,
- 0x212a, 0x2139,
- 0x213c, 0x213f,
- 0x2145, 0x2149,
- 0x214e, 0x214e,
- 0x2160, 0x2188,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2ce4,
- 0x2ceb, 0x2cf3,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0x2d30, 0x2d67,
- 0x2d6f, 0x2d6f,
- 0x2d7f, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0x2de0, 0x2dff,
- 0x3005, 0x3007,
- 0x3021, 0x302f,
- 0x3031, 0x3035,
- 0x3038, 0x303c,
- 0x3041, 0x3096,
- 0x3099, 0x309f,
- 0x30a1, 0x30fa,
- 0x30fc, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x31a0, 0x31ba,
- 0x31f0, 0x31ff,
- 0x3400, 0x4db5,
- 0x4e00, 0x9fd5,
- 0xa000, 0xa48c,
- 0xa4d0, 0xa4fd,
- 0xa500, 0xa60c,
- 0xa610, 0xa62b,
- 0xa640, 0xa66f,
- 0xa674, 0xa67d,
- 0xa67f, 0xa6f1,
- 0xa717, 0xa71f,
- 0xa722, 0xa788,
- 0xa78b, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7f7, 0xa827,
- 0xa840, 0xa873,
- 0xa880, 0xa8c5,
- 0xa8d0, 0xa8d9,
- 0xa8e0, 0xa8f7,
- 0xa8fb, 0xa8fb,
- 0xa8fd, 0xa8fd,
- 0xa900, 0xa92d,
- 0xa930, 0xa953,
- 0xa960, 0xa97c,
- 0xa980, 0xa9c0,
- 0xa9cf, 0xa9d9,
- 0xa9e0, 0xa9fe,
- 0xaa00, 0xaa36,
- 0xaa40, 0xaa4d,
- 0xaa50, 0xaa59,
- 0xaa60, 0xaa76,
- 0xaa7a, 0xaac2,
- 0xaadb, 0xaadd,
- 0xaae0, 0xaaef,
- 0xaaf2, 0xaaf6,
- 0xab01, 0xab06,
- 0xab09, 0xab0e,
- 0xab11, 0xab16,
- 0xab20, 0xab26,
- 0xab28, 0xab2e,
- 0xab30, 0xab5a,
- 0xab5c, 0xab65,
- 0xab70, 0xabea,
- 0xabec, 0xabed,
- 0xabf0, 0xabf9,
- 0xac00, 0xd7a3,
- 0xd7b0, 0xd7c6,
- 0xd7cb, 0xd7fb,
- 0xf900, 0xfa6d,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb28,
- 0xfb2a, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3d,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfb,
- 0xfe00, 0xfe0f,
- 0xfe20, 0xfe2f,
- 0xfe33, 0xfe34,
- 0xfe4d, 0xfe4f,
- 0xfe70, 0xfe74,
- 0xfe76, 0xfefc,
- 0xff10, 0xff19,
- 0xff21, 0xff3a,
- 0xff3f, 0xff3f,
- 0xff41, 0xff5a,
- 0xff66, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
- 0x10000, 0x1000b,
- 0x1000d, 0x10026,
- 0x10028, 0x1003a,
- 0x1003c, 0x1003d,
- 0x1003f, 0x1004d,
- 0x10050, 0x1005d,
- 0x10080, 0x100fa,
- 0x10140, 0x10174,
- 0x101fd, 0x101fd,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x102e0, 0x102e0,
- 0x10300, 0x1031f,
- 0x10330, 0x1034a,
- 0x10350, 0x1037a,
- 0x10380, 0x1039d,
- 0x103a0, 0x103c3,
- 0x103c8, 0x103cf,
- 0x103d1, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x10855,
- 0x10860, 0x10876,
- 0x10880, 0x1089e,
- 0x108e0, 0x108f2,
- 0x108f4, 0x108f5,
- 0x10900, 0x10915,
- 0x10920, 0x10939,
- 0x10980, 0x109b7,
- 0x109be, 0x109bf,
- 0x10a00, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a3f,
- 0x10a60, 0x10a7c,
- 0x10a80, 0x10a9c,
- 0x10ac0, 0x10ac7,
- 0x10ac9, 0x10ae6,
- 0x10b00, 0x10b35,
- 0x10b40, 0x10b55,
- 0x10b60, 0x10b72,
- 0x10b80, 0x10b91,
- 0x10c00, 0x10c48,
- 0x10c80, 0x10cb2,
- 0x10cc0, 0x10cf2,
- 0x11000, 0x11046,
- 0x11066, 0x1106f,
- 0x1107f, 0x110ba,
- 0x110d0, 0x110e8,
- 0x110f0, 0x110f9,
- 0x11100, 0x11134,
- 0x11136, 0x1113f,
- 0x11150, 0x11173,
- 0x11176, 0x11176,
- 0x11180, 0x111c4,
- 0x111ca, 0x111cc,
- 0x111d0, 0x111da,
- 0x111dc, 0x111dc,
- 0x11200, 0x11211,
- 0x11213, 0x11237,
- 0x1123e, 0x1123e,
- 0x11280, 0x11286,
- 0x11288, 0x11288,
- 0x1128a, 0x1128d,
- 0x1128f, 0x1129d,
- 0x1129f, 0x112a8,
- 0x112b0, 0x112ea,
- 0x112f0, 0x112f9,
- 0x11300, 0x11303,
- 0x11305, 0x1130c,
- 0x1130f, 0x11310,
- 0x11313, 0x11328,
- 0x1132a, 0x11330,
- 0x11332, 0x11333,
- 0x11335, 0x11339,
- 0x1133c, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134d,
- 0x11350, 0x11350,
- 0x11357, 0x11357,
- 0x1135d, 0x11363,
- 0x11366, 0x1136c,
- 0x11370, 0x11374,
- 0x11400, 0x1144a,
- 0x11450, 0x11459,
- 0x11480, 0x114c5,
- 0x114c7, 0x114c7,
- 0x114d0, 0x114d9,
- 0x11580, 0x115b5,
- 0x115b8, 0x115c0,
- 0x115d8, 0x115dd,
- 0x11600, 0x11640,
- 0x11644, 0x11644,
- 0x11650, 0x11659,
- 0x11680, 0x116b7,
- 0x116c0, 0x116c9,
- 0x11700, 0x11719,
- 0x1171d, 0x1172b,
- 0x11730, 0x11739,
- 0x118a0, 0x118e9,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x11c00, 0x11c08,
- 0x11c0a, 0x11c36,
- 0x11c38, 0x11c40,
- 0x11c50, 0x11c59,
- 0x11c72, 0x11c8f,
- 0x11c92, 0x11ca7,
- 0x11ca9, 0x11cb6,
- 0x12000, 0x12399,
- 0x12400, 0x1246e,
- 0x12480, 0x12543,
- 0x13000, 0x1342e,
- 0x14400, 0x14646,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16a60, 0x16a69,
- 0x16ad0, 0x16aed,
- 0x16af0, 0x16af4,
- 0x16b00, 0x16b36,
- 0x16b40, 0x16b43,
- 0x16b50, 0x16b59,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f8f, 0x16f9f,
- 0x16fe0, 0x16fe0,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 0x1bc9d, 0x1bc9e,
- 0x1d165, 0x1d169,
- 0x1d16d, 0x1d172,
- 0x1d17b, 0x1d182,
- 0x1d185, 0x1d18b,
- 0x1d1aa, 0x1d1ad,
- 0x1d242, 0x1d244,
- 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, 0x1d6c0,
- 0x1d6c2, 0x1d6da,
- 0x1d6dc, 0x1d6fa,
- 0x1d6fc, 0x1d714,
- 0x1d716, 0x1d734,
- 0x1d736, 0x1d74e,
- 0x1d750, 0x1d76e,
- 0x1d770, 0x1d788,
- 0x1d78a, 0x1d7a8,
- 0x1d7aa, 0x1d7c2,
- 0x1d7c4, 0x1d7cb,
- 0x1d7ce, 0x1d7ff,
- 0x1da00, 0x1da36,
- 0x1da3b, 0x1da6c,
- 0x1da75, 0x1da75,
- 0x1da84, 0x1da84,
- 0x1da9b, 0x1da9f,
- 0x1daa1, 0x1daaf,
- 0x1e000, 0x1e006,
- 0x1e008, 0x1e018,
- 0x1e01b, 0x1e021,
- 0x1e023, 0x1e024,
- 0x1e026, 0x1e02a,
- 0x1e800, 0x1e8c4,
- 0x1e8d0, 0x1e8d6,
- 0x1e900, 0x1e94a,
- 0x1e950, 0x1e959,
- 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,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
- 0xe0100, 0xe01ef,
-}; /* CR_ID_Continue */
-
-/* 'XID_Start': Derived Property */
-static const OnigCodePoint CR_XID_Start[] = {
- 578,
- 0x0041, 0x005a,
- 0x0061, 0x007a,
- 0x00aa, 0x00aa,
- 0x00b5, 0x00b5,
- 0x00ba, 0x00ba,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00f6,
- 0x00f8, 0x02c1,
- 0x02c6, 0x02d1,
- 0x02e0, 0x02e4,
- 0x02ec, 0x02ec,
- 0x02ee, 0x02ee,
- 0x0370, 0x0374,
- 0x0376, 0x0377,
- 0x037b, 0x037d,
- 0x037f, 0x037f,
- 0x0386, 0x0386,
- 0x0388, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03f5,
- 0x03f7, 0x0481,
- 0x048a, 0x052f,
- 0x0531, 0x0556,
- 0x0559, 0x0559,
- 0x0561, 0x0587,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f2,
- 0x0620, 0x064a,
- 0x066e, 0x066f,
- 0x0671, 0x06d3,
- 0x06d5, 0x06d5,
- 0x06e5, 0x06e6,
- 0x06ee, 0x06ef,
- 0x06fa, 0x06fc,
- 0x06ff, 0x06ff,
- 0x0710, 0x0710,
- 0x0712, 0x072f,
- 0x074d, 0x07a5,
- 0x07b1, 0x07b1,
- 0x07ca, 0x07ea,
- 0x07f4, 0x07f5,
- 0x07fa, 0x07fa,
- 0x0800, 0x0815,
- 0x081a, 0x081a,
- 0x0824, 0x0824,
- 0x0828, 0x0828,
- 0x0840, 0x0858,
- 0x08a0, 0x08b4,
- 0x08b6, 0x08bd,
- 0x0904, 0x0939,
- 0x093d, 0x093d,
- 0x0950, 0x0950,
- 0x0958, 0x0961,
- 0x0971, 0x0980,
- 0x0985, 0x098c,
- 0x098f, 0x0990,
- 0x0993, 0x09a8,
- 0x09aa, 0x09b0,
- 0x09b2, 0x09b2,
- 0x09b6, 0x09b9,
- 0x09bd, 0x09bd,
- 0x09ce, 0x09ce,
- 0x09dc, 0x09dd,
- 0x09df, 0x09e1,
- 0x09f0, 0x09f1,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a72, 0x0a74,
- 0x0a85, 0x0a8d,
- 0x0a8f, 0x0a91,
- 0x0a93, 0x0aa8,
- 0x0aaa, 0x0ab0,
- 0x0ab2, 0x0ab3,
- 0x0ab5, 0x0ab9,
- 0x0abd, 0x0abd,
- 0x0ad0, 0x0ad0,
- 0x0ae0, 0x0ae1,
- 0x0af9, 0x0af9,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3d, 0x0b3d,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b61,
- 0x0b71, 0x0b71,
- 0x0b83, 0x0b83,
- 0x0b85, 0x0b8a,
- 0x0b8e, 0x0b90,
- 0x0b92, 0x0b95,
- 0x0b99, 0x0b9a,
- 0x0b9c, 0x0b9c,
- 0x0b9e, 0x0b9f,
- 0x0ba3, 0x0ba4,
- 0x0ba8, 0x0baa,
- 0x0bae, 0x0bb9,
- 0x0bd0, 0x0bd0,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c3d,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c61,
- 0x0c80, 0x0c80,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbd, 0x0cbd,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce1,
- 0x0cf1, 0x0cf2,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d3d,
- 0x0d4e, 0x0d4e,
- 0x0d54, 0x0d56,
- 0x0d5f, 0x0d61,
- 0x0d7a, 0x0d7f,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0e01, 0x0e30,
- 0x0e32, 0x0e32,
- 0x0e40, 0x0e46,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb0,
- 0x0eb2, 0x0eb2,
- 0x0ebd, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0edc, 0x0edf,
- 0x0f00, 0x0f00,
- 0x0f40, 0x0f47,
- 0x0f49, 0x0f6c,
- 0x0f88, 0x0f8c,
- 0x1000, 0x102a,
- 0x103f, 0x103f,
- 0x1050, 0x1055,
- 0x105a, 0x105d,
- 0x1061, 0x1061,
- 0x1065, 0x1066,
- 0x106e, 0x1070,
- 0x1075, 0x1081,
- 0x108e, 0x108e,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x10d0, 0x10fa,
- 0x10fc, 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,
- 0x1380, 0x138f,
- 0x13a0, 0x13f5,
- 0x13f8, 0x13fd,
- 0x1401, 0x166c,
- 0x166f, 0x167f,
- 0x1681, 0x169a,
- 0x16a0, 0x16ea,
- 0x16ee, 0x16f8,
- 0x1700, 0x170c,
- 0x170e, 0x1711,
- 0x1720, 0x1731,
- 0x1740, 0x1751,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1780, 0x17b3,
- 0x17d7, 0x17d7,
- 0x17dc, 0x17dc,
- 0x1820, 0x1877,
- 0x1880, 0x18a8,
- 0x18aa, 0x18aa,
- 0x18b0, 0x18f5,
- 0x1900, 0x191e,
- 0x1950, 0x196d,
- 0x1970, 0x1974,
- 0x1980, 0x19ab,
- 0x19b0, 0x19c9,
- 0x1a00, 0x1a16,
- 0x1a20, 0x1a54,
- 0x1aa7, 0x1aa7,
- 0x1b05, 0x1b33,
- 0x1b45, 0x1b4b,
- 0x1b83, 0x1ba0,
- 0x1bae, 0x1baf,
- 0x1bba, 0x1be5,
- 0x1c00, 0x1c23,
- 0x1c4d, 0x1c4f,
- 0x1c5a, 0x1c7d,
- 0x1c80, 0x1c88,
- 0x1ce9, 0x1cec,
- 0x1cee, 0x1cf1,
- 0x1cf5, 0x1cf6,
- 0x1d00, 0x1dbf,
- 0x1e00, 0x1f15,
- 0x1f18, 0x1f1d,
- 0x1f20, 0x1f45,
- 0x1f48, 0x1f4d,
- 0x1f50, 0x1f57,
- 0x1f59, 0x1f59,
- 0x1f5b, 0x1f5b,
- 0x1f5d, 0x1f5d,
- 0x1f5f, 0x1f7d,
- 0x1f80, 0x1fb4,
- 0x1fb6, 0x1fbc,
- 0x1fbe, 0x1fbe,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fcc,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fdb,
- 0x1fe0, 0x1fec,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ffc,
- 0x2071, 0x2071,
- 0x207f, 0x207f,
- 0x2090, 0x209c,
- 0x2102, 0x2102,
- 0x2107, 0x2107,
- 0x210a, 0x2113,
- 0x2115, 0x2115,
- 0x2118, 0x211d,
- 0x2124, 0x2124,
- 0x2126, 0x2126,
- 0x2128, 0x2128,
- 0x212a, 0x2139,
- 0x213c, 0x213f,
- 0x2145, 0x2149,
- 0x214e, 0x214e,
- 0x2160, 0x2188,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2ce4,
- 0x2ceb, 0x2cee,
- 0x2cf2, 0x2cf3,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0x2d30, 0x2d67,
- 0x2d6f, 0x2d6f,
- 0x2d80, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0x3005, 0x3007,
- 0x3021, 0x3029,
- 0x3031, 0x3035,
- 0x3038, 0x303c,
- 0x3041, 0x3096,
- 0x309d, 0x309f,
- 0x30a1, 0x30fa,
- 0x30fc, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x31a0, 0x31ba,
- 0x31f0, 0x31ff,
- 0x3400, 0x4db5,
- 0x4e00, 0x9fd5,
- 0xa000, 0xa48c,
- 0xa4d0, 0xa4fd,
- 0xa500, 0xa60c,
- 0xa610, 0xa61f,
- 0xa62a, 0xa62b,
- 0xa640, 0xa66e,
- 0xa67f, 0xa69d,
- 0xa6a0, 0xa6ef,
- 0xa717, 0xa71f,
- 0xa722, 0xa788,
- 0xa78b, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7f7, 0xa801,
- 0xa803, 0xa805,
- 0xa807, 0xa80a,
- 0xa80c, 0xa822,
- 0xa840, 0xa873,
- 0xa882, 0xa8b3,
- 0xa8f2, 0xa8f7,
- 0xa8fb, 0xa8fb,
- 0xa8fd, 0xa8fd,
- 0xa90a, 0xa925,
- 0xa930, 0xa946,
- 0xa960, 0xa97c,
- 0xa984, 0xa9b2,
- 0xa9cf, 0xa9cf,
- 0xa9e0, 0xa9e4,
- 0xa9e6, 0xa9ef,
- 0xa9fa, 0xa9fe,
- 0xaa00, 0xaa28,
- 0xaa40, 0xaa42,
- 0xaa44, 0xaa4b,
- 0xaa60, 0xaa76,
- 0xaa7a, 0xaa7a,
- 0xaa7e, 0xaaaf,
- 0xaab1, 0xaab1,
- 0xaab5, 0xaab6,
- 0xaab9, 0xaabd,
- 0xaac0, 0xaac0,
- 0xaac2, 0xaac2,
- 0xaadb, 0xaadd,
- 0xaae0, 0xaaea,
- 0xaaf2, 0xaaf4,
- 0xab01, 0xab06,
- 0xab09, 0xab0e,
- 0xab11, 0xab16,
- 0xab20, 0xab26,
- 0xab28, 0xab2e,
- 0xab30, 0xab5a,
- 0xab5c, 0xab65,
- 0xab70, 0xabe2,
- 0xac00, 0xd7a3,
- 0xd7b0, 0xd7c6,
- 0xd7cb, 0xd7fb,
- 0xf900, 0xfa6d,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb1d,
- 0xfb1f, 0xfb28,
- 0xfb2a, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfc5d,
- 0xfc64, 0xfd3d,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdf9,
- 0xfe71, 0xfe71,
- 0xfe73, 0xfe73,
- 0xfe77, 0xfe77,
- 0xfe79, 0xfe79,
- 0xfe7b, 0xfe7b,
- 0xfe7d, 0xfe7d,
- 0xfe7f, 0xfefc,
- 0xff21, 0xff3a,
- 0xff41, 0xff5a,
- 0xff66, 0xff9d,
- 0xffa0, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
- 0x10000, 0x1000b,
- 0x1000d, 0x10026,
- 0x10028, 0x1003a,
- 0x1003c, 0x1003d,
- 0x1003f, 0x1004d,
- 0x10050, 0x1005d,
- 0x10080, 0x100fa,
- 0x10140, 0x10174,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x10300, 0x1031f,
- 0x10330, 0x1034a,
- 0x10350, 0x10375,
- 0x10380, 0x1039d,
- 0x103a0, 0x103c3,
- 0x103c8, 0x103cf,
- 0x103d1, 0x103d5,
- 0x10400, 0x1049d,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x10855,
- 0x10860, 0x10876,
- 0x10880, 0x1089e,
- 0x108e0, 0x108f2,
- 0x108f4, 0x108f5,
- 0x10900, 0x10915,
- 0x10920, 0x10939,
- 0x10980, 0x109b7,
- 0x109be, 0x109bf,
- 0x10a00, 0x10a00,
- 0x10a10, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a60, 0x10a7c,
- 0x10a80, 0x10a9c,
- 0x10ac0, 0x10ac7,
- 0x10ac9, 0x10ae4,
- 0x10b00, 0x10b35,
- 0x10b40, 0x10b55,
- 0x10b60, 0x10b72,
- 0x10b80, 0x10b91,
- 0x10c00, 0x10c48,
- 0x10c80, 0x10cb2,
- 0x10cc0, 0x10cf2,
- 0x11003, 0x11037,
- 0x11083, 0x110af,
- 0x110d0, 0x110e8,
- 0x11103, 0x11126,
- 0x11150, 0x11172,
- 0x11176, 0x11176,
- 0x11183, 0x111b2,
- 0x111c1, 0x111c4,
- 0x111da, 0x111da,
- 0x111dc, 0x111dc,
- 0x11200, 0x11211,
- 0x11213, 0x1122b,
- 0x11280, 0x11286,
- 0x11288, 0x11288,
- 0x1128a, 0x1128d,
- 0x1128f, 0x1129d,
- 0x1129f, 0x112a8,
- 0x112b0, 0x112de,
- 0x11305, 0x1130c,
- 0x1130f, 0x11310,
- 0x11313, 0x11328,
- 0x1132a, 0x11330,
- 0x11332, 0x11333,
- 0x11335, 0x11339,
- 0x1133d, 0x1133d,
- 0x11350, 0x11350,
- 0x1135d, 0x11361,
- 0x11400, 0x11434,
- 0x11447, 0x1144a,
- 0x11480, 0x114af,
- 0x114c4, 0x114c5,
- 0x114c7, 0x114c7,
- 0x11580, 0x115ae,
- 0x115d8, 0x115db,
- 0x11600, 0x1162f,
- 0x11644, 0x11644,
- 0x11680, 0x116aa,
- 0x11700, 0x11719,
- 0x118a0, 0x118df,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x11c00, 0x11c08,
- 0x11c0a, 0x11c2e,
- 0x11c40, 0x11c40,
- 0x11c72, 0x11c8f,
- 0x12000, 0x12399,
- 0x12400, 0x1246e,
- 0x12480, 0x12543,
- 0x13000, 0x1342e,
- 0x14400, 0x14646,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16ad0, 0x16aed,
- 0x16b00, 0x16b2f,
- 0x16b40, 0x16b43,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f50,
- 0x16f93, 0x16f9f,
- 0x16fe0, 0x16fe0,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 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, 0x1d6c0,
- 0x1d6c2, 0x1d6da,
- 0x1d6dc, 0x1d6fa,
- 0x1d6fc, 0x1d714,
- 0x1d716, 0x1d734,
- 0x1d736, 0x1d74e,
- 0x1d750, 0x1d76e,
- 0x1d770, 0x1d788,
- 0x1d78a, 0x1d7a8,
- 0x1d7aa, 0x1d7c2,
- 0x1d7c4, 0x1d7cb,
- 0x1e800, 0x1e8c4,
- 0x1e900, 0x1e943,
- 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,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
-}; /* CR_XID_Start */
-
-/* 'XID_Continue': Derived Property */
-static const OnigCodePoint CR_XID_Continue[] = {
- 683,
- 0x0030, 0x0039,
- 0x0041, 0x005a,
- 0x005f, 0x005f,
- 0x0061, 0x007a,
- 0x00aa, 0x00aa,
- 0x00b5, 0x00b5,
- 0x00b7, 0x00b7,
- 0x00ba, 0x00ba,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00f6,
- 0x00f8, 0x02c1,
- 0x02c6, 0x02d1,
- 0x02e0, 0x02e4,
- 0x02ec, 0x02ec,
- 0x02ee, 0x02ee,
- 0x0300, 0x0374,
- 0x0376, 0x0377,
- 0x037b, 0x037d,
- 0x037f, 0x037f,
- 0x0386, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03f5,
- 0x03f7, 0x0481,
- 0x0483, 0x0487,
- 0x048a, 0x052f,
- 0x0531, 0x0556,
- 0x0559, 0x0559,
- 0x0561, 0x0587,
- 0x0591, 0x05bd,
- 0x05bf, 0x05bf,
- 0x05c1, 0x05c2,
- 0x05c4, 0x05c5,
- 0x05c7, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f2,
- 0x0610, 0x061a,
- 0x0620, 0x0669,
- 0x066e, 0x06d3,
- 0x06d5, 0x06dc,
- 0x06df, 0x06e8,
- 0x06ea, 0x06fc,
- 0x06ff, 0x06ff,
- 0x0710, 0x074a,
- 0x074d, 0x07b1,
- 0x07c0, 0x07f5,
- 0x07fa, 0x07fa,
- 0x0800, 0x082d,
- 0x0840, 0x085b,
- 0x08a0, 0x08b4,
- 0x08b6, 0x08bd,
- 0x08d4, 0x08e1,
- 0x08e3, 0x0963,
- 0x0966, 0x096f,
- 0x0971, 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, 0x09f1,
- 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, 0x0a75,
- 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, 0x0aef,
- 0x0af9, 0x0af9,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b63,
- 0x0b66, 0x0b6f,
- 0x0b71, 0x0b71,
- 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, 0x0bef,
- 0x0c00, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c80, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d01, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4e,
- 0x0d54, 0x0d57,
- 0x0d5f, 0x0d63,
- 0x0d66, 0x0d6f,
- 0x0d7a, 0x0d7f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0de6, 0x0def,
- 0x0df2, 0x0df3,
- 0x0e01, 0x0e3a,
- 0x0e40, 0x0e4e,
- 0x0e50, 0x0e59,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edf,
- 0x0f00, 0x0f00,
- 0x0f18, 0x0f19,
- 0x0f20, 0x0f29,
- 0x0f35, 0x0f35,
- 0x0f37, 0x0f37,
- 0x0f39, 0x0f39,
- 0x0f3e, 0x0f47,
- 0x0f49, 0x0f6c,
- 0x0f71, 0x0f84,
- 0x0f86, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fc6, 0x0fc6,
- 0x1000, 0x1049,
- 0x1050, 0x109d,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x10d0, 0x10fa,
- 0x10fc, 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, 0x135f,
- 0x1369, 0x1371,
- 0x1380, 0x138f,
- 0x13a0, 0x13f5,
- 0x13f8, 0x13fd,
- 0x1401, 0x166c,
- 0x166f, 0x167f,
- 0x1681, 0x169a,
- 0x16a0, 0x16ea,
- 0x16ee, 0x16f8,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1734,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17d3,
- 0x17d7, 0x17d7,
- 0x17dc, 0x17dd,
- 0x17e0, 0x17e9,
- 0x180b, 0x180d,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18aa,
- 0x18b0, 0x18f5,
- 0x1900, 0x191e,
- 0x1920, 0x192b,
- 0x1930, 0x193b,
- 0x1946, 0x196d,
- 0x1970, 0x1974,
- 0x1980, 0x19ab,
- 0x19b0, 0x19c9,
- 0x19d0, 0x19da,
- 0x1a00, 0x1a1b,
- 0x1a20, 0x1a5e,
- 0x1a60, 0x1a7c,
- 0x1a7f, 0x1a89,
- 0x1a90, 0x1a99,
- 0x1aa7, 0x1aa7,
- 0x1ab0, 0x1abd,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b59,
- 0x1b6b, 0x1b73,
- 0x1b80, 0x1bf3,
- 0x1c00, 0x1c37,
- 0x1c40, 0x1c49,
- 0x1c4d, 0x1c7d,
- 0x1c80, 0x1c88,
- 0x1cd0, 0x1cd2,
- 0x1cd4, 0x1cf6,
- 0x1cf8, 0x1cf9,
- 0x1d00, 0x1df5,
- 0x1dfb, 0x1f15,
- 0x1f18, 0x1f1d,
- 0x1f20, 0x1f45,
- 0x1f48, 0x1f4d,
- 0x1f50, 0x1f57,
- 0x1f59, 0x1f59,
- 0x1f5b, 0x1f5b,
- 0x1f5d, 0x1f5d,
- 0x1f5f, 0x1f7d,
- 0x1f80, 0x1fb4,
- 0x1fb6, 0x1fbc,
- 0x1fbe, 0x1fbe,
- 0x1fc2, 0x1fc4,
- 0x1fc6, 0x1fcc,
- 0x1fd0, 0x1fd3,
- 0x1fd6, 0x1fdb,
- 0x1fe0, 0x1fec,
- 0x1ff2, 0x1ff4,
- 0x1ff6, 0x1ffc,
- 0x203f, 0x2040,
- 0x2054, 0x2054,
- 0x2071, 0x2071,
- 0x207f, 0x207f,
- 0x2090, 0x209c,
- 0x20d0, 0x20dc,
- 0x20e1, 0x20e1,
- 0x20e5, 0x20f0,
- 0x2102, 0x2102,
- 0x2107, 0x2107,
- 0x210a, 0x2113,
- 0x2115, 0x2115,
- 0x2118, 0x211d,
- 0x2124, 0x2124,
- 0x2126, 0x2126,
- 0x2128, 0x2128,
- 0x212a, 0x2139,
- 0x213c, 0x213f,
- 0x2145, 0x2149,
- 0x214e, 0x214e,
- 0x2160, 0x2188,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2ce4,
- 0x2ceb, 0x2cf3,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0x2d30, 0x2d67,
- 0x2d6f, 0x2d6f,
- 0x2d7f, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0x2de0, 0x2dff,
- 0x3005, 0x3007,
- 0x3021, 0x302f,
- 0x3031, 0x3035,
- 0x3038, 0x303c,
- 0x3041, 0x3096,
- 0x3099, 0x309a,
- 0x309d, 0x309f,
- 0x30a1, 0x30fa,
- 0x30fc, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x31a0, 0x31ba,
- 0x31f0, 0x31ff,
- 0x3400, 0x4db5,
- 0x4e00, 0x9fd5,
- 0xa000, 0xa48c,
- 0xa4d0, 0xa4fd,
- 0xa500, 0xa60c,
- 0xa610, 0xa62b,
- 0xa640, 0xa66f,
- 0xa674, 0xa67d,
- 0xa67f, 0xa6f1,
- 0xa717, 0xa71f,
- 0xa722, 0xa788,
- 0xa78b, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7f7, 0xa827,
- 0xa840, 0xa873,
- 0xa880, 0xa8c5,
- 0xa8d0, 0xa8d9,
- 0xa8e0, 0xa8f7,
- 0xa8fb, 0xa8fb,
- 0xa8fd, 0xa8fd,
- 0xa900, 0xa92d,
- 0xa930, 0xa953,
- 0xa960, 0xa97c,
- 0xa980, 0xa9c0,
- 0xa9cf, 0xa9d9,
- 0xa9e0, 0xa9fe,
- 0xaa00, 0xaa36,
- 0xaa40, 0xaa4d,
- 0xaa50, 0xaa59,
- 0xaa60, 0xaa76,
- 0xaa7a, 0xaac2,
- 0xaadb, 0xaadd,
- 0xaae0, 0xaaef,
- 0xaaf2, 0xaaf6,
- 0xab01, 0xab06,
- 0xab09, 0xab0e,
- 0xab11, 0xab16,
- 0xab20, 0xab26,
- 0xab28, 0xab2e,
- 0xab30, 0xab5a,
- 0xab5c, 0xab65,
- 0xab70, 0xabea,
- 0xabec, 0xabed,
- 0xabf0, 0xabf9,
- 0xac00, 0xd7a3,
- 0xd7b0, 0xd7c6,
- 0xd7cb, 0xd7fb,
- 0xf900, 0xfa6d,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb28,
- 0xfb2a, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfc5d,
- 0xfc64, 0xfd3d,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdf9,
- 0xfe00, 0xfe0f,
- 0xfe20, 0xfe2f,
- 0xfe33, 0xfe34,
- 0xfe4d, 0xfe4f,
- 0xfe71, 0xfe71,
- 0xfe73, 0xfe73,
- 0xfe77, 0xfe77,
- 0xfe79, 0xfe79,
- 0xfe7b, 0xfe7b,
- 0xfe7d, 0xfe7d,
- 0xfe7f, 0xfefc,
- 0xff10, 0xff19,
- 0xff21, 0xff3a,
- 0xff3f, 0xff3f,
- 0xff41, 0xff5a,
- 0xff66, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
- 0x10000, 0x1000b,
- 0x1000d, 0x10026,
- 0x10028, 0x1003a,
- 0x1003c, 0x1003d,
- 0x1003f, 0x1004d,
- 0x10050, 0x1005d,
- 0x10080, 0x100fa,
- 0x10140, 0x10174,
- 0x101fd, 0x101fd,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x102e0, 0x102e0,
- 0x10300, 0x1031f,
- 0x10330, 0x1034a,
- 0x10350, 0x1037a,
- 0x10380, 0x1039d,
- 0x103a0, 0x103c3,
- 0x103c8, 0x103cf,
- 0x103d1, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x10855,
- 0x10860, 0x10876,
- 0x10880, 0x1089e,
- 0x108e0, 0x108f2,
- 0x108f4, 0x108f5,
- 0x10900, 0x10915,
- 0x10920, 0x10939,
- 0x10980, 0x109b7,
- 0x109be, 0x109bf,
- 0x10a00, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a3f,
- 0x10a60, 0x10a7c,
- 0x10a80, 0x10a9c,
- 0x10ac0, 0x10ac7,
- 0x10ac9, 0x10ae6,
- 0x10b00, 0x10b35,
- 0x10b40, 0x10b55,
- 0x10b60, 0x10b72,
- 0x10b80, 0x10b91,
- 0x10c00, 0x10c48,
- 0x10c80, 0x10cb2,
- 0x10cc0, 0x10cf2,
- 0x11000, 0x11046,
- 0x11066, 0x1106f,
- 0x1107f, 0x110ba,
- 0x110d0, 0x110e8,
- 0x110f0, 0x110f9,
- 0x11100, 0x11134,
- 0x11136, 0x1113f,
- 0x11150, 0x11173,
- 0x11176, 0x11176,
- 0x11180, 0x111c4,
- 0x111ca, 0x111cc,
- 0x111d0, 0x111da,
- 0x111dc, 0x111dc,
- 0x11200, 0x11211,
- 0x11213, 0x11237,
- 0x1123e, 0x1123e,
- 0x11280, 0x11286,
- 0x11288, 0x11288,
- 0x1128a, 0x1128d,
- 0x1128f, 0x1129d,
- 0x1129f, 0x112a8,
- 0x112b0, 0x112ea,
- 0x112f0, 0x112f9,
- 0x11300, 0x11303,
- 0x11305, 0x1130c,
- 0x1130f, 0x11310,
- 0x11313, 0x11328,
- 0x1132a, 0x11330,
- 0x11332, 0x11333,
- 0x11335, 0x11339,
- 0x1133c, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134d,
- 0x11350, 0x11350,
- 0x11357, 0x11357,
- 0x1135d, 0x11363,
- 0x11366, 0x1136c,
- 0x11370, 0x11374,
- 0x11400, 0x1144a,
- 0x11450, 0x11459,
- 0x11480, 0x114c5,
- 0x114c7, 0x114c7,
- 0x114d0, 0x114d9,
- 0x11580, 0x115b5,
- 0x115b8, 0x115c0,
- 0x115d8, 0x115dd,
- 0x11600, 0x11640,
- 0x11644, 0x11644,
- 0x11650, 0x11659,
- 0x11680, 0x116b7,
- 0x116c0, 0x116c9,
- 0x11700, 0x11719,
- 0x1171d, 0x1172b,
- 0x11730, 0x11739,
- 0x118a0, 0x118e9,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x11c00, 0x11c08,
- 0x11c0a, 0x11c36,
- 0x11c38, 0x11c40,
- 0x11c50, 0x11c59,
- 0x11c72, 0x11c8f,
- 0x11c92, 0x11ca7,
- 0x11ca9, 0x11cb6,
- 0x12000, 0x12399,
- 0x12400, 0x1246e,
- 0x12480, 0x12543,
- 0x13000, 0x1342e,
- 0x14400, 0x14646,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16a60, 0x16a69,
- 0x16ad0, 0x16aed,
- 0x16af0, 0x16af4,
- 0x16b00, 0x16b36,
- 0x16b40, 0x16b43,
- 0x16b50, 0x16b59,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f8f, 0x16f9f,
- 0x16fe0, 0x16fe0,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 0x1bc9d, 0x1bc9e,
- 0x1d165, 0x1d169,
- 0x1d16d, 0x1d172,
- 0x1d17b, 0x1d182,
- 0x1d185, 0x1d18b,
- 0x1d1aa, 0x1d1ad,
- 0x1d242, 0x1d244,
- 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, 0x1d6c0,
- 0x1d6c2, 0x1d6da,
- 0x1d6dc, 0x1d6fa,
- 0x1d6fc, 0x1d714,
- 0x1d716, 0x1d734,
- 0x1d736, 0x1d74e,
- 0x1d750, 0x1d76e,
- 0x1d770, 0x1d788,
- 0x1d78a, 0x1d7a8,
- 0x1d7aa, 0x1d7c2,
- 0x1d7c4, 0x1d7cb,
- 0x1d7ce, 0x1d7ff,
- 0x1da00, 0x1da36,
- 0x1da3b, 0x1da6c,
- 0x1da75, 0x1da75,
- 0x1da84, 0x1da84,
- 0x1da9b, 0x1da9f,
- 0x1daa1, 0x1daaf,
- 0x1e000, 0x1e006,
- 0x1e008, 0x1e018,
- 0x1e01b, 0x1e021,
- 0x1e023, 0x1e024,
- 0x1e026, 0x1e02a,
- 0x1e800, 0x1e8c4,
- 0x1e8d0, 0x1e8d6,
- 0x1e900, 0x1e94a,
- 0x1e950, 0x1e959,
- 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,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
- 0xe0100, 0xe01ef,
-}; /* CR_XID_Continue */
-
-/* 'Default_Ignorable_Code_Point': Derived Property */
-static const OnigCodePoint CR_Default_Ignorable_Code_Point[] = {
- 17,
- 0x00ad, 0x00ad,
- 0x034f, 0x034f,
- 0x061c, 0x061c,
- 0x115f, 0x1160,
- 0x17b4, 0x17b5,
- 0x180b, 0x180e,
- 0x200b, 0x200f,
- 0x202a, 0x202e,
- 0x2060, 0x206f,
- 0x3164, 0x3164,
- 0xfe00, 0xfe0f,
- 0xfeff, 0xfeff,
- 0xffa0, 0xffa0,
- 0xfff0, 0xfff8,
- 0x1bca0, 0x1bca3,
- 0x1d173, 0x1d17a,
- 0xe0000, 0xe0fff,
-}; /* CR_Default_Ignorable_Code_Point */
-
-/* 'Grapheme_Extend': Derived Property */
-static const OnigCodePoint CR_Grapheme_Extend[] = {
- 303,
- 0x0300, 0x036f,
- 0x0483, 0x0489,
- 0x0591, 0x05bd,
- 0x05bf, 0x05bf,
- 0x05c1, 0x05c2,
- 0x05c4, 0x05c5,
- 0x05c7, 0x05c7,
- 0x0610, 0x061a,
- 0x064b, 0x065f,
- 0x0670, 0x0670,
- 0x06d6, 0x06dc,
- 0x06df, 0x06e4,
- 0x06e7, 0x06e8,
- 0x06ea, 0x06ed,
- 0x0711, 0x0711,
- 0x0730, 0x074a,
- 0x07a6, 0x07b0,
- 0x07eb, 0x07f3,
- 0x0816, 0x0819,
- 0x081b, 0x0823,
- 0x0825, 0x0827,
- 0x0829, 0x082d,
- 0x0859, 0x085b,
- 0x08d4, 0x08e1,
- 0x08e3, 0x0902,
- 0x093a, 0x093a,
- 0x093c, 0x093c,
- 0x0941, 0x0948,
- 0x094d, 0x094d,
- 0x0951, 0x0957,
- 0x0962, 0x0963,
- 0x0981, 0x0981,
- 0x09bc, 0x09bc,
- 0x09be, 0x09be,
- 0x09c1, 0x09c4,
- 0x09cd, 0x09cd,
- 0x09d7, 0x09d7,
- 0x09e2, 0x09e3,
- 0x0a01, 0x0a02,
- 0x0a3c, 0x0a3c,
- 0x0a41, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4d,
- 0x0a51, 0x0a51,
- 0x0a70, 0x0a71,
- 0x0a75, 0x0a75,
- 0x0a81, 0x0a82,
- 0x0abc, 0x0abc,
- 0x0ac1, 0x0ac5,
- 0x0ac7, 0x0ac8,
- 0x0acd, 0x0acd,
- 0x0ae2, 0x0ae3,
- 0x0b01, 0x0b01,
- 0x0b3c, 0x0b3c,
- 0x0b3e, 0x0b3f,
- 0x0b41, 0x0b44,
- 0x0b4d, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b62, 0x0b63,
- 0x0b82, 0x0b82,
- 0x0bbe, 0x0bbe,
- 0x0bc0, 0x0bc0,
- 0x0bcd, 0x0bcd,
- 0x0bd7, 0x0bd7,
- 0x0c00, 0x0c00,
- 0x0c3e, 0x0c40,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c62, 0x0c63,
- 0x0c81, 0x0c81,
- 0x0cbc, 0x0cbc,
- 0x0cbf, 0x0cbf,
- 0x0cc2, 0x0cc2,
- 0x0cc6, 0x0cc6,
- 0x0ccc, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0ce2, 0x0ce3,
- 0x0d01, 0x0d01,
- 0x0d3e, 0x0d3e,
- 0x0d41, 0x0d44,
- 0x0d4d, 0x0d4d,
- 0x0d57, 0x0d57,
- 0x0d62, 0x0d63,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dcf,
- 0x0dd2, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0ddf, 0x0ddf,
- 0x0e31, 0x0e31,
- 0x0e34, 0x0e3a,
- 0x0e47, 0x0e4e,
- 0x0eb1, 0x0eb1,
- 0x0eb4, 0x0eb9,
- 0x0ebb, 0x0ebc,
- 0x0ec8, 0x0ecd,
- 0x0f18, 0x0f19,
- 0x0f35, 0x0f35,
- 0x0f37, 0x0f37,
- 0x0f39, 0x0f39,
- 0x0f71, 0x0f7e,
- 0x0f80, 0x0f84,
- 0x0f86, 0x0f87,
- 0x0f8d, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fc6, 0x0fc6,
- 0x102d, 0x1030,
- 0x1032, 0x1037,
- 0x1039, 0x103a,
- 0x103d, 0x103e,
- 0x1058, 0x1059,
- 0x105e, 0x1060,
- 0x1071, 0x1074,
- 0x1082, 0x1082,
- 0x1085, 0x1086,
- 0x108d, 0x108d,
- 0x109d, 0x109d,
- 0x135d, 0x135f,
- 0x1712, 0x1714,
- 0x1732, 0x1734,
- 0x1752, 0x1753,
- 0x1772, 0x1773,
- 0x17b4, 0x17b5,
- 0x17b7, 0x17bd,
- 0x17c6, 0x17c6,
- 0x17c9, 0x17d3,
- 0x17dd, 0x17dd,
- 0x180b, 0x180d,
- 0x1885, 0x1886,
- 0x18a9, 0x18a9,
- 0x1920, 0x1922,
- 0x1927, 0x1928,
- 0x1932, 0x1932,
- 0x1939, 0x193b,
- 0x1a17, 0x1a18,
- 0x1a1b, 0x1a1b,
- 0x1a56, 0x1a56,
- 0x1a58, 0x1a5e,
- 0x1a60, 0x1a60,
- 0x1a62, 0x1a62,
- 0x1a65, 0x1a6c,
- 0x1a73, 0x1a7c,
- 0x1a7f, 0x1a7f,
- 0x1ab0, 0x1abe,
- 0x1b00, 0x1b03,
- 0x1b34, 0x1b34,
- 0x1b36, 0x1b3a,
- 0x1b3c, 0x1b3c,
- 0x1b42, 0x1b42,
- 0x1b6b, 0x1b73,
- 0x1b80, 0x1b81,
- 0x1ba2, 0x1ba5,
- 0x1ba8, 0x1ba9,
- 0x1bab, 0x1bad,
- 0x1be6, 0x1be6,
- 0x1be8, 0x1be9,
- 0x1bed, 0x1bed,
- 0x1bef, 0x1bf1,
- 0x1c2c, 0x1c33,
- 0x1c36, 0x1c37,
- 0x1cd0, 0x1cd2,
- 0x1cd4, 0x1ce0,
- 0x1ce2, 0x1ce8,
- 0x1ced, 0x1ced,
- 0x1cf4, 0x1cf4,
- 0x1cf8, 0x1cf9,
- 0x1dc0, 0x1df5,
- 0x1dfb, 0x1dff,
- 0x200c, 0x200c,
- 0x20d0, 0x20f0,
- 0x2cef, 0x2cf1,
- 0x2d7f, 0x2d7f,
- 0x2de0, 0x2dff,
- 0x302a, 0x302f,
- 0x3099, 0x309a,
- 0xa66f, 0xa672,
- 0xa674, 0xa67d,
- 0xa69e, 0xa69f,
- 0xa6f0, 0xa6f1,
- 0xa802, 0xa802,
- 0xa806, 0xa806,
- 0xa80b, 0xa80b,
- 0xa825, 0xa826,
- 0xa8c4, 0xa8c5,
- 0xa8e0, 0xa8f1,
- 0xa926, 0xa92d,
- 0xa947, 0xa951,
- 0xa980, 0xa982,
- 0xa9b3, 0xa9b3,
- 0xa9b6, 0xa9b9,
- 0xa9bc, 0xa9bc,
- 0xa9e5, 0xa9e5,
- 0xaa29, 0xaa2e,
- 0xaa31, 0xaa32,
- 0xaa35, 0xaa36,
- 0xaa43, 0xaa43,
- 0xaa4c, 0xaa4c,
- 0xaa7c, 0xaa7c,
- 0xaab0, 0xaab0,
- 0xaab2, 0xaab4,
- 0xaab7, 0xaab8,
- 0xaabe, 0xaabf,
- 0xaac1, 0xaac1,
- 0xaaec, 0xaaed,
- 0xaaf6, 0xaaf6,
- 0xabe5, 0xabe5,
- 0xabe8, 0xabe8,
- 0xabed, 0xabed,
- 0xfb1e, 0xfb1e,
- 0xfe00, 0xfe0f,
- 0xfe20, 0xfe2f,
- 0xff9e, 0xff9f,
- 0x101fd, 0x101fd,
- 0x102e0, 0x102e0,
- 0x10376, 0x1037a,
- 0x10a01, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a0f,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a3f,
- 0x10ae5, 0x10ae6,
- 0x11001, 0x11001,
- 0x11038, 0x11046,
- 0x1107f, 0x11081,
- 0x110b3, 0x110b6,
- 0x110b9, 0x110ba,
- 0x11100, 0x11102,
- 0x11127, 0x1112b,
- 0x1112d, 0x11134,
- 0x11173, 0x11173,
- 0x11180, 0x11181,
- 0x111b6, 0x111be,
- 0x111ca, 0x111cc,
- 0x1122f, 0x11231,
- 0x11234, 0x11234,
- 0x11236, 0x11237,
- 0x1123e, 0x1123e,
- 0x112df, 0x112df,
- 0x112e3, 0x112ea,
- 0x11300, 0x11301,
- 0x1133c, 0x1133c,
- 0x1133e, 0x1133e,
- 0x11340, 0x11340,
- 0x11357, 0x11357,
- 0x11366, 0x1136c,
- 0x11370, 0x11374,
- 0x11438, 0x1143f,
- 0x11442, 0x11444,
- 0x11446, 0x11446,
- 0x114b0, 0x114b0,
- 0x114b3, 0x114b8,
- 0x114ba, 0x114ba,
- 0x114bd, 0x114bd,
- 0x114bf, 0x114c0,
- 0x114c2, 0x114c3,
- 0x115af, 0x115af,
- 0x115b2, 0x115b5,
- 0x115bc, 0x115bd,
- 0x115bf, 0x115c0,
- 0x115dc, 0x115dd,
- 0x11633, 0x1163a,
- 0x1163d, 0x1163d,
- 0x1163f, 0x11640,
- 0x116ab, 0x116ab,
- 0x116ad, 0x116ad,
- 0x116b0, 0x116b5,
- 0x116b7, 0x116b7,
- 0x1171d, 0x1171f,
- 0x11722, 0x11725,
- 0x11727, 0x1172b,
- 0x11c30, 0x11c36,
- 0x11c38, 0x11c3d,
- 0x11c3f, 0x11c3f,
- 0x11c92, 0x11ca7,
- 0x11caa, 0x11cb0,
- 0x11cb2, 0x11cb3,
- 0x11cb5, 0x11cb6,
- 0x16af0, 0x16af4,
- 0x16b30, 0x16b36,
- 0x16f8f, 0x16f92,
- 0x1bc9d, 0x1bc9e,
- 0x1d165, 0x1d165,
- 0x1d167, 0x1d169,
- 0x1d16e, 0x1d172,
- 0x1d17b, 0x1d182,
- 0x1d185, 0x1d18b,
- 0x1d1aa, 0x1d1ad,
- 0x1d242, 0x1d244,
- 0x1da00, 0x1da36,
- 0x1da3b, 0x1da6c,
- 0x1da75, 0x1da75,
- 0x1da84, 0x1da84,
- 0x1da9b, 0x1da9f,
- 0x1daa1, 0x1daaf,
- 0x1e000, 0x1e006,
- 0x1e008, 0x1e018,
- 0x1e01b, 0x1e021,
- 0x1e023, 0x1e024,
- 0x1e026, 0x1e02a,
- 0x1e8d0, 0x1e8d6,
- 0x1e944, 0x1e94a,
- 0xe0020, 0xe007f,
- 0xe0100, 0xe01ef,
-}; /* CR_Grapheme_Extend */
-
-/* 'Grapheme_Base': Derived Property */
-static const OnigCodePoint CR_Grapheme_Base[] = {
- 772,
- 0x0020, 0x007e,
- 0x00a0, 0x00ac,
- 0x00ae, 0x02ff,
- 0x0370, 0x0377,
- 0x037a, 0x037f,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x0482,
- 0x048a, 0x052f,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x058d, 0x058f,
- 0x05be, 0x05be,
- 0x05c0, 0x05c0,
- 0x05c3, 0x05c3,
- 0x05c6, 0x05c6,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0606, 0x060f,
- 0x061b, 0x061b,
- 0x061e, 0x064a,
- 0x0660, 0x066f,
- 0x0671, 0x06d5,
- 0x06de, 0x06de,
- 0x06e5, 0x06e6,
- 0x06e9, 0x06e9,
- 0x06ee, 0x070d,
- 0x0710, 0x0710,
- 0x0712, 0x072f,
- 0x074d, 0x07a5,
- 0x07b1, 0x07b1,
- 0x07c0, 0x07ea,
- 0x07f4, 0x07fa,
- 0x0800, 0x0815,
- 0x081a, 0x081a,
- 0x0824, 0x0824,
- 0x0828, 0x0828,
- 0x0830, 0x083e,
- 0x0840, 0x0858,
- 0x085e, 0x085e,
- 0x08a0, 0x08b4,
- 0x08b6, 0x08bd,
- 0x0903, 0x0939,
- 0x093b, 0x093b,
- 0x093d, 0x0940,
- 0x0949, 0x094c,
- 0x094e, 0x0950,
- 0x0958, 0x0961,
- 0x0964, 0x0980,
- 0x0982, 0x0983,
- 0x0985, 0x098c,
- 0x098f, 0x0990,
- 0x0993, 0x09a8,
- 0x09aa, 0x09b0,
- 0x09b2, 0x09b2,
- 0x09b6, 0x09b9,
- 0x09bd, 0x09bd,
- 0x09bf, 0x09c0,
- 0x09c7, 0x09c8,
- 0x09cb, 0x09cc,
- 0x09ce, 0x09ce,
- 0x09dc, 0x09dd,
- 0x09df, 0x09e1,
- 0x09e6, 0x09fb,
- 0x0a03, 0x0a03,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a3e, 0x0a40,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a66, 0x0a6f,
- 0x0a72, 0x0a74,
- 0x0a83, 0x0a83,
- 0x0a85, 0x0a8d,
- 0x0a8f, 0x0a91,
- 0x0a93, 0x0aa8,
- 0x0aaa, 0x0ab0,
- 0x0ab2, 0x0ab3,
- 0x0ab5, 0x0ab9,
- 0x0abd, 0x0ac0,
- 0x0ac9, 0x0ac9,
- 0x0acb, 0x0acc,
- 0x0ad0, 0x0ad0,
- 0x0ae0, 0x0ae1,
- 0x0ae6, 0x0af1,
- 0x0af9, 0x0af9,
- 0x0b02, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3d, 0x0b3d,
- 0x0b40, 0x0b40,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4c,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b61,
- 0x0b66, 0x0b77,
- 0x0b83, 0x0b83,
- 0x0b85, 0x0b8a,
- 0x0b8e, 0x0b90,
- 0x0b92, 0x0b95,
- 0x0b99, 0x0b9a,
- 0x0b9c, 0x0b9c,
- 0x0b9e, 0x0b9f,
- 0x0ba3, 0x0ba4,
- 0x0ba8, 0x0baa,
- 0x0bae, 0x0bb9,
- 0x0bbf, 0x0bbf,
- 0x0bc1, 0x0bc2,
- 0x0bc6, 0x0bc8,
- 0x0bca, 0x0bcc,
- 0x0bd0, 0x0bd0,
- 0x0be6, 0x0bfa,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c3d,
- 0x0c41, 0x0c44,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c61,
- 0x0c66, 0x0c6f,
- 0x0c78, 0x0c80,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbd, 0x0cbe,
- 0x0cc0, 0x0cc1,
- 0x0cc3, 0x0cc4,
- 0x0cc7, 0x0cc8,
- 0x0cca, 0x0ccb,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce1,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d3d,
- 0x0d3f, 0x0d40,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4c,
- 0x0d4e, 0x0d4f,
- 0x0d54, 0x0d56,
- 0x0d58, 0x0d61,
- 0x0d66, 0x0d7f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dd0, 0x0dd1,
- 0x0dd8, 0x0dde,
- 0x0de6, 0x0def,
- 0x0df2, 0x0df4,
- 0x0e01, 0x0e30,
- 0x0e32, 0x0e33,
- 0x0e3f, 0x0e46,
- 0x0e4f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb0,
- 0x0eb2, 0x0eb3,
- 0x0ebd, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edf,
- 0x0f00, 0x0f17,
- 0x0f1a, 0x0f34,
- 0x0f36, 0x0f36,
- 0x0f38, 0x0f38,
- 0x0f3a, 0x0f47,
- 0x0f49, 0x0f6c,
- 0x0f7f, 0x0f7f,
- 0x0f85, 0x0f85,
- 0x0f88, 0x0f8c,
- 0x0fbe, 0x0fc5,
- 0x0fc7, 0x0fcc,
- 0x0fce, 0x0fda,
- 0x1000, 0x102c,
- 0x1031, 0x1031,
- 0x1038, 0x1038,
- 0x103b, 0x103c,
- 0x103f, 0x1057,
- 0x105a, 0x105d,
- 0x1061, 0x1070,
- 0x1075, 0x1081,
- 0x1083, 0x1084,
- 0x1087, 0x108c,
- 0x108e, 0x109c,
- 0x109e, 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,
- 0x1360, 0x137c,
- 0x1380, 0x1399,
- 0x13a0, 0x13f5,
- 0x13f8, 0x13fd,
- 0x1400, 0x169c,
- 0x16a0, 0x16f8,
- 0x1700, 0x170c,
- 0x170e, 0x1711,
- 0x1720, 0x1731,
- 0x1735, 0x1736,
- 0x1740, 0x1751,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1780, 0x17b3,
- 0x17b6, 0x17b6,
- 0x17be, 0x17c5,
- 0x17c7, 0x17c8,
- 0x17d4, 0x17dc,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180a,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x1884,
- 0x1887, 0x18a8,
- 0x18aa, 0x18aa,
- 0x18b0, 0x18f5,
- 0x1900, 0x191e,
- 0x1923, 0x1926,
- 0x1929, 0x192b,
- 0x1930, 0x1931,
- 0x1933, 0x1938,
- 0x1940, 0x1940,
- 0x1944, 0x196d,
- 0x1970, 0x1974,
- 0x1980, 0x19ab,
- 0x19b0, 0x19c9,
- 0x19d0, 0x19da,
- 0x19de, 0x1a16,
- 0x1a19, 0x1a1a,
- 0x1a1e, 0x1a55,
- 0x1a57, 0x1a57,
- 0x1a61, 0x1a61,
- 0x1a63, 0x1a64,
- 0x1a6d, 0x1a72,
- 0x1a80, 0x1a89,
- 0x1a90, 0x1a99,
- 0x1aa0, 0x1aad,
- 0x1b04, 0x1b33,
- 0x1b35, 0x1b35,
- 0x1b3b, 0x1b3b,
- 0x1b3d, 0x1b41,
- 0x1b43, 0x1b4b,
- 0x1b50, 0x1b6a,
- 0x1b74, 0x1b7c,
- 0x1b82, 0x1ba1,
- 0x1ba6, 0x1ba7,
- 0x1baa, 0x1baa,
- 0x1bae, 0x1be5,
- 0x1be7, 0x1be7,
- 0x1bea, 0x1bec,
- 0x1bee, 0x1bee,
- 0x1bf2, 0x1bf3,
- 0x1bfc, 0x1c2b,
- 0x1c34, 0x1c35,
- 0x1c3b, 0x1c49,
- 0x1c4d, 0x1c88,
- 0x1cc0, 0x1cc7,
- 0x1cd3, 0x1cd3,
- 0x1ce1, 0x1ce1,
- 0x1ce9, 0x1cec,
- 0x1cee, 0x1cf3,
- 0x1cf5, 0x1cf6,
- 0x1d00, 0x1dbf,
- 0x1e00, 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, 0x200a,
- 0x2010, 0x2027,
- 0x202f, 0x205f,
- 0x2070, 0x2071,
- 0x2074, 0x208e,
- 0x2090, 0x209c,
- 0x20a0, 0x20be,
- 0x2100, 0x218b,
- 0x2190, 0x23fe,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x2b73,
- 0x2b76, 0x2b95,
- 0x2b98, 0x2bb9,
- 0x2bbd, 0x2bc8,
- 0x2bca, 0x2bd1,
- 0x2bec, 0x2bef,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2cee,
- 0x2cf2, 0x2cf3,
- 0x2cf9, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
- 0x2d30, 0x2d67,
- 0x2d6f, 0x2d70,
- 0x2d80, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0x2e00, 0x2e44,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x3029,
- 0x3030, 0x303f,
- 0x3041, 0x3096,
- 0x309b, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x3190, 0x31ba,
- 0x31c0, 0x31e3,
- 0x31f0, 0x321e,
- 0x3220, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fd5,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xa4d0, 0xa62b,
- 0xa640, 0xa66e,
- 0xa673, 0xa673,
- 0xa67e, 0xa69d,
- 0xa6a0, 0xa6ef,
- 0xa6f2, 0xa6f7,
- 0xa700, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7f7, 0xa801,
- 0xa803, 0xa805,
- 0xa807, 0xa80a,
- 0xa80c, 0xa824,
- 0xa827, 0xa82b,
- 0xa830, 0xa839,
- 0xa840, 0xa877,
- 0xa880, 0xa8c3,
- 0xa8ce, 0xa8d9,
- 0xa8f2, 0xa8fd,
- 0xa900, 0xa925,
- 0xa92e, 0xa946,
- 0xa952, 0xa953,
- 0xa95f, 0xa97c,
- 0xa983, 0xa9b2,
- 0xa9b4, 0xa9b5,
- 0xa9ba, 0xa9bb,
- 0xa9bd, 0xa9cd,
- 0xa9cf, 0xa9d9,
- 0xa9de, 0xa9e4,
- 0xa9e6, 0xa9fe,
- 0xaa00, 0xaa28,
- 0xaa2f, 0xaa30,
- 0xaa33, 0xaa34,
- 0xaa40, 0xaa42,
- 0xaa44, 0xaa4b,
- 0xaa4d, 0xaa4d,
- 0xaa50, 0xaa59,
- 0xaa5c, 0xaa7b,
- 0xaa7d, 0xaaaf,
- 0xaab1, 0xaab1,
- 0xaab5, 0xaab6,
- 0xaab9, 0xaabd,
- 0xaac0, 0xaac0,
- 0xaac2, 0xaac2,
- 0xaadb, 0xaaeb,
- 0xaaee, 0xaaf5,
- 0xab01, 0xab06,
- 0xab09, 0xab0e,
- 0xab11, 0xab16,
- 0xab20, 0xab26,
- 0xab28, 0xab2e,
- 0xab30, 0xab65,
- 0xab70, 0xabe4,
- 0xabe6, 0xabe7,
- 0xabe9, 0xabec,
- 0xabf0, 0xabf9,
- 0xac00, 0xd7a3,
- 0xd7b0, 0xd7c6,
- 0xd7cb, 0xd7fb,
- 0xf900, 0xfa6d,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb1d,
- 0xfb1f, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbc1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfd,
- 0xfe10, 0xfe19,
- 0xfe30, 0xfe52,
- 0xfe54, 0xfe66,
- 0xfe68, 0xfe6b,
- 0xfe70, 0xfe74,
- 0xfe76, 0xfefc,
- 0xff01, 0xff9d,
- 0xffa0, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
- 0xffe0, 0xffe6,
- 0xffe8, 0xffee,
- 0xfffc, 0xfffd,
- 0x10000, 0x1000b,
- 0x1000d, 0x10026,
- 0x10028, 0x1003a,
- 0x1003c, 0x1003d,
- 0x1003f, 0x1004d,
- 0x10050, 0x1005d,
- 0x10080, 0x100fa,
- 0x10100, 0x10102,
- 0x10107, 0x10133,
- 0x10137, 0x1018e,
- 0x10190, 0x1019b,
- 0x101a0, 0x101a0,
- 0x101d0, 0x101fc,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x102e1, 0x102fb,
- 0x10300, 0x10323,
- 0x10330, 0x1034a,
- 0x10350, 0x10375,
- 0x10380, 0x1039d,
- 0x1039f, 0x103c3,
- 0x103c8, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x1056f, 0x1056f,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 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, 0x10a00,
- 0x10a10, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a40, 0x10a47,
- 0x10a50, 0x10a58,
- 0x10a60, 0x10a9f,
- 0x10ac0, 0x10ae4,
- 0x10aeb, 0x10af6,
- 0x10b00, 0x10b35,
- 0x10b39, 0x10b55,
- 0x10b58, 0x10b72,
- 0x10b78, 0x10b91,
- 0x10b99, 0x10b9c,
- 0x10ba9, 0x10baf,
- 0x10c00, 0x10c48,
- 0x10c80, 0x10cb2,
- 0x10cc0, 0x10cf2,
- 0x10cfa, 0x10cff,
- 0x10e60, 0x10e7e,
- 0x11000, 0x11000,
- 0x11002, 0x11037,
- 0x11047, 0x1104d,
- 0x11052, 0x1106f,
- 0x11082, 0x110b2,
- 0x110b7, 0x110b8,
- 0x110bb, 0x110bc,
- 0x110be, 0x110c1,
- 0x110d0, 0x110e8,
- 0x110f0, 0x110f9,
- 0x11103, 0x11126,
- 0x1112c, 0x1112c,
- 0x11136, 0x11143,
- 0x11150, 0x11172,
- 0x11174, 0x11176,
- 0x11182, 0x111b5,
- 0x111bf, 0x111c9,
- 0x111cd, 0x111cd,
- 0x111d0, 0x111df,
- 0x111e1, 0x111f4,
- 0x11200, 0x11211,
- 0x11213, 0x1122e,
- 0x11232, 0x11233,
- 0x11235, 0x11235,
- 0x11238, 0x1123d,
- 0x11280, 0x11286,
- 0x11288, 0x11288,
- 0x1128a, 0x1128d,
- 0x1128f, 0x1129d,
- 0x1129f, 0x112a9,
- 0x112b0, 0x112de,
- 0x112e0, 0x112e2,
- 0x112f0, 0x112f9,
- 0x11302, 0x11303,
- 0x11305, 0x1130c,
- 0x1130f, 0x11310,
- 0x11313, 0x11328,
- 0x1132a, 0x11330,
- 0x11332, 0x11333,
- 0x11335, 0x11339,
- 0x1133d, 0x1133d,
- 0x1133f, 0x1133f,
- 0x11341, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134d,
- 0x11350, 0x11350,
- 0x1135d, 0x11363,
- 0x11400, 0x11437,
- 0x11440, 0x11441,
- 0x11445, 0x11445,
- 0x11447, 0x11459,
- 0x1145b, 0x1145b,
- 0x1145d, 0x1145d,
- 0x11480, 0x114af,
- 0x114b1, 0x114b2,
- 0x114b9, 0x114b9,
- 0x114bb, 0x114bc,
- 0x114be, 0x114be,
- 0x114c1, 0x114c1,
- 0x114c4, 0x114c7,
- 0x114d0, 0x114d9,
- 0x11580, 0x115ae,
- 0x115b0, 0x115b1,
- 0x115b8, 0x115bb,
- 0x115be, 0x115be,
- 0x115c1, 0x115db,
- 0x11600, 0x11632,
- 0x1163b, 0x1163c,
- 0x1163e, 0x1163e,
- 0x11641, 0x11644,
- 0x11650, 0x11659,
- 0x11660, 0x1166c,
- 0x11680, 0x116aa,
- 0x116ac, 0x116ac,
- 0x116ae, 0x116af,
- 0x116b6, 0x116b6,
- 0x116c0, 0x116c9,
- 0x11700, 0x11719,
- 0x11720, 0x11721,
- 0x11726, 0x11726,
- 0x11730, 0x1173f,
- 0x118a0, 0x118f2,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x11c00, 0x11c08,
- 0x11c0a, 0x11c2f,
- 0x11c3e, 0x11c3e,
- 0x11c40, 0x11c45,
- 0x11c50, 0x11c6c,
- 0x11c70, 0x11c8f,
- 0x11ca9, 0x11ca9,
- 0x11cb1, 0x11cb1,
- 0x11cb4, 0x11cb4,
- 0x12000, 0x12399,
- 0x12400, 0x1246e,
- 0x12470, 0x12474,
- 0x12480, 0x12543,
- 0x13000, 0x1342e,
- 0x14400, 0x14646,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16a60, 0x16a69,
- 0x16a6e, 0x16a6f,
- 0x16ad0, 0x16aed,
- 0x16af5, 0x16af5,
- 0x16b00, 0x16b2f,
- 0x16b37, 0x16b45,
- 0x16b50, 0x16b59,
- 0x16b5b, 0x16b61,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f93, 0x16f9f,
- 0x16fe0, 0x16fe0,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 0x1bc9c, 0x1bc9c,
- 0x1bc9f, 0x1bc9f,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d164,
- 0x1d166, 0x1d166,
- 0x1d16a, 0x1d16d,
- 0x1d183, 0x1d184,
- 0x1d18c, 0x1d1a9,
- 0x1d1ae, 0x1d1e8,
- 0x1d200, 0x1d241,
- 0x1d245, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d360, 0x1d371,
- 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, 0x1d9ff,
- 0x1da37, 0x1da3a,
- 0x1da6d, 0x1da74,
- 0x1da76, 0x1da83,
- 0x1da85, 0x1da8b,
- 0x1e800, 0x1e8c4,
- 0x1e8c7, 0x1e8cf,
- 0x1e900, 0x1e943,
- 0x1e950, 0x1e959,
- 0x1e95e, 0x1e95f,
- 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, 0x1f10c,
- 0x1f110, 0x1f12e,
- 0x1f130, 0x1f16b,
- 0x1f170, 0x1f1ac,
- 0x1f1e6, 0x1f202,
- 0x1f210, 0x1f23b,
- 0x1f240, 0x1f248,
- 0x1f250, 0x1f251,
- 0x1f300, 0x1f6d2,
- 0x1f6e0, 0x1f6ec,
- 0x1f6f0, 0x1f6f6,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d4,
- 0x1f800, 0x1f80b,
- 0x1f810, 0x1f847,
- 0x1f850, 0x1f859,
- 0x1f860, 0x1f887,
- 0x1f890, 0x1f8ad,
- 0x1f910, 0x1f91e,
- 0x1f920, 0x1f927,
- 0x1f930, 0x1f930,
- 0x1f933, 0x1f93e,
- 0x1f940, 0x1f94b,
- 0x1f950, 0x1f95e,
- 0x1f980, 0x1f991,
- 0x1f9c0, 0x1f9c0,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
-}; /* CR_Grapheme_Base */
-
-/* 'Grapheme_Link': Derived Property */
-static const OnigCodePoint CR_Grapheme_Link[] = {
- 43,
- 0x094d, 0x094d,
- 0x09cd, 0x09cd,
- 0x0a4d, 0x0a4d,
- 0x0acd, 0x0acd,
- 0x0b4d, 0x0b4d,
- 0x0bcd, 0x0bcd,
- 0x0c4d, 0x0c4d,
- 0x0ccd, 0x0ccd,
- 0x0d4d, 0x0d4d,
- 0x0dca, 0x0dca,
- 0x0e3a, 0x0e3a,
- 0x0f84, 0x0f84,
- 0x1039, 0x103a,
- 0x1714, 0x1714,
- 0x1734, 0x1734,
- 0x17d2, 0x17d2,
- 0x1a60, 0x1a60,
- 0x1b44, 0x1b44,
- 0x1baa, 0x1bab,
- 0x1bf2, 0x1bf3,
- 0x2d7f, 0x2d7f,
- 0xa806, 0xa806,
- 0xa8c4, 0xa8c4,
- 0xa953, 0xa953,
- 0xa9c0, 0xa9c0,
- 0xaaf6, 0xaaf6,
- 0xabed, 0xabed,
- 0x10a3f, 0x10a3f,
- 0x11046, 0x11046,
- 0x1107f, 0x1107f,
- 0x110b9, 0x110b9,
- 0x11133, 0x11134,
- 0x111c0, 0x111c0,
- 0x11235, 0x11235,
- 0x112ea, 0x112ea,
- 0x1134d, 0x1134d,
- 0x11442, 0x11442,
- 0x114c2, 0x114c2,
- 0x115bf, 0x115bf,
- 0x1163f, 0x1163f,
- 0x116b6, 0x116b6,
- 0x1172b, 0x1172b,
- 0x11c3f, 0x11c3f,
-}; /* CR_Grapheme_Link */
-
-/* 'Common': Script */
-static const OnigCodePoint CR_Common[] = {
- 165,
- 0x0000, 0x0040,
- 0x005b, 0x0060,
- 0x007b, 0x00a9,
- 0x00ab, 0x00b9,
- 0x00bb, 0x00bf,
- 0x00d7, 0x00d7,
- 0x00f7, 0x00f7,
- 0x02b9, 0x02df,
- 0x02e5, 0x02e9,
- 0x02ec, 0x02ff,
- 0x0374, 0x0374,
- 0x037e, 0x037e,
- 0x0385, 0x0385,
- 0x0387, 0x0387,
- 0x0589, 0x0589,
- 0x0605, 0x0605,
- 0x060c, 0x060c,
- 0x061b, 0x061c,
- 0x061f, 0x061f,
- 0x0640, 0x0640,
- 0x06dd, 0x06dd,
- 0x08e2, 0x08e2,
- 0x0964, 0x0965,
- 0x0e3f, 0x0e3f,
- 0x0fd5, 0x0fd8,
- 0x10fb, 0x10fb,
- 0x16eb, 0x16ed,
- 0x1735, 0x1736,
- 0x1802, 0x1803,
- 0x1805, 0x1805,
- 0x1cd3, 0x1cd3,
- 0x1ce1, 0x1ce1,
- 0x1ce9, 0x1cec,
- 0x1cee, 0x1cf3,
- 0x1cf5, 0x1cf6,
- 0x2000, 0x200b,
- 0x200e, 0x2064,
- 0x2066, 0x2070,
- 0x2074, 0x207e,
- 0x2080, 0x208e,
- 0x20a0, 0x20be,
- 0x2100, 0x2125,
- 0x2127, 0x2129,
- 0x212c, 0x2131,
- 0x2133, 0x214d,
- 0x214f, 0x215f,
- 0x2189, 0x218b,
- 0x2190, 0x23fe,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x27ff,
- 0x2900, 0x2b73,
- 0x2b76, 0x2b95,
- 0x2b98, 0x2bb9,
- 0x2bbd, 0x2bc8,
- 0x2bca, 0x2bd1,
- 0x2bec, 0x2bef,
- 0x2e00, 0x2e44,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x3004,
- 0x3006, 0x3006,
- 0x3008, 0x3020,
- 0x3030, 0x3037,
- 0x303c, 0x303f,
- 0x309b, 0x309c,
- 0x30a0, 0x30a0,
- 0x30fb, 0x30fc,
- 0x3190, 0x319f,
- 0x31c0, 0x31e3,
- 0x3220, 0x325f,
- 0x327f, 0x32cf,
- 0x3358, 0x33ff,
- 0x4dc0, 0x4dff,
- 0xa700, 0xa721,
- 0xa788, 0xa78a,
- 0xa830, 0xa839,
- 0xa92e, 0xa92e,
- 0xa9cf, 0xa9cf,
- 0xab5b, 0xab5b,
- 0xfd3e, 0xfd3f,
- 0xfe10, 0xfe19,
- 0xfe30, 0xfe52,
- 0xfe54, 0xfe66,
- 0xfe68, 0xfe6b,
- 0xfeff, 0xfeff,
- 0xff01, 0xff20,
- 0xff3b, 0xff40,
- 0xff5b, 0xff65,
- 0xff70, 0xff70,
- 0xff9e, 0xff9f,
- 0xffe0, 0xffe6,
- 0xffe8, 0xffee,
- 0xfff9, 0xfffd,
- 0x10100, 0x10102,
- 0x10107, 0x10133,
- 0x10137, 0x1013f,
- 0x10190, 0x1019b,
- 0x101d0, 0x101fc,
- 0x102e1, 0x102fb,
- 0x1bca0, 0x1bca3,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d166,
- 0x1d16a, 0x1d17a,
- 0x1d183, 0x1d184,
- 0x1d18c, 0x1d1a9,
- 0x1d1ae, 0x1d1e8,
- 0x1d300, 0x1d356,
- 0x1d360, 0x1d371,
- 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, 0x1d7ff,
- 0x1f000, 0x1f02b,
- 0x1f030, 0x1f093,
- 0x1f0a0, 0x1f0ae,
- 0x1f0b1, 0x1f0bf,
- 0x1f0c1, 0x1f0cf,
- 0x1f0d1, 0x1f0f5,
- 0x1f100, 0x1f10c,
- 0x1f110, 0x1f12e,
- 0x1f130, 0x1f16b,
- 0x1f170, 0x1f1ac,
- 0x1f1e6, 0x1f1ff,
- 0x1f201, 0x1f202,
- 0x1f210, 0x1f23b,
- 0x1f240, 0x1f248,
- 0x1f250, 0x1f251,
- 0x1f300, 0x1f6d2,
- 0x1f6e0, 0x1f6ec,
- 0x1f6f0, 0x1f6f6,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d4,
- 0x1f800, 0x1f80b,
- 0x1f810, 0x1f847,
- 0x1f850, 0x1f859,
- 0x1f860, 0x1f887,
- 0x1f890, 0x1f8ad,
- 0x1f910, 0x1f91e,
- 0x1f920, 0x1f927,
- 0x1f930, 0x1f930,
- 0x1f933, 0x1f93e,
- 0x1f940, 0x1f94b,
- 0x1f950, 0x1f95e,
- 0x1f980, 0x1f991,
- 0x1f9c0, 0x1f9c0,
- 0xe0001, 0xe0001,
- 0xe0020, 0xe007f,
-}; /* CR_Common */
-
-/* 'Latin': Script */
-static const OnigCodePoint CR_Latin[] = {
- 31,
- 0x0041, 0x005a,
- 0x0061, 0x007a,
- 0x00aa, 0x00aa,
- 0x00ba, 0x00ba,
- 0x00c0, 0x00d6,
- 0x00d8, 0x00f6,
- 0x00f8, 0x02b8,
- 0x02e0, 0x02e4,
- 0x1d00, 0x1d25,
- 0x1d2c, 0x1d5c,
- 0x1d62, 0x1d65,
- 0x1d6b, 0x1d77,
- 0x1d79, 0x1dbe,
- 0x1e00, 0x1eff,
- 0x2071, 0x2071,
- 0x207f, 0x207f,
- 0x2090, 0x209c,
- 0x212a, 0x212b,
- 0x2132, 0x2132,
- 0x214e, 0x214e,
- 0x2160, 0x2188,
- 0x2c60, 0x2c7f,
- 0xa722, 0xa787,
- 0xa78b, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7f7, 0xa7ff,
- 0xab30, 0xab5a,
- 0xab5c, 0xab64,
- 0xfb00, 0xfb06,
- 0xff21, 0xff3a,
- 0xff41, 0xff5a,
-}; /* CR_Latin */
-
-/* 'Greek': Script */
-static const OnigCodePoint CR_Greek[] = {
- 36,
- 0x0370, 0x0373,
- 0x0375, 0x0377,
- 0x037a, 0x037d,
- 0x037f, 0x037f,
- 0x0384, 0x0384,
- 0x0386, 0x0386,
- 0x0388, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03e1,
- 0x03f0, 0x03ff,
- 0x1d26, 0x1d2a,
- 0x1d5d, 0x1d61,
- 0x1d66, 0x1d6a,
- 0x1dbf, 0x1dbf,
- 0x1f00, 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,
- 0x2126, 0x2126,
- 0xab65, 0xab65,
- 0x10140, 0x1018e,
- 0x101a0, 0x101a0,
- 0x1d200, 0x1d245,
-}; /* CR_Greek */
-
-/* 'Cyrillic': Script */
-static const OnigCodePoint CR_Cyrillic[] = {
- 8,
- 0x0400, 0x0484,
- 0x0487, 0x052f,
- 0x1c80, 0x1c88,
- 0x1d2b, 0x1d2b,
- 0x1d78, 0x1d78,
- 0x2de0, 0x2dff,
- 0xa640, 0xa69f,
- 0xfe2e, 0xfe2f,
-}; /* CR_Cyrillic */
-
-/* 'Armenian': Script */
-static const OnigCodePoint CR_Armenian[] = {
- 6,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x058a, 0x058a,
- 0x058d, 0x058f,
- 0xfb13, 0xfb17,
-}; /* CR_Armenian */
-
-/* 'Hebrew': Script */
-static const OnigCodePoint CR_Hebrew[] = {
- 9,
- 0x0591, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0xfb1d, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfb4f,
-}; /* CR_Hebrew */
-
-/* 'Arabic': Script */
-static const OnigCodePoint CR_Arabic[] = {
- 56,
- 0x0600, 0x0604,
- 0x0606, 0x060b,
- 0x060d, 0x061a,
- 0x061e, 0x061e,
- 0x0620, 0x063f,
- 0x0641, 0x064a,
- 0x0656, 0x066f,
- 0x0671, 0x06dc,
- 0x06de, 0x06ff,
- 0x0750, 0x077f,
- 0x08a0, 0x08b4,
- 0x08b6, 0x08bd,
- 0x08d4, 0x08e1,
- 0x08e3, 0x08ff,
- 0xfb50, 0xfbc1,
- 0xfbd3, 0xfd3d,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfd,
- 0xfe70, 0xfe74,
- 0xfe76, 0xfefc,
- 0x10e60, 0x10e7e,
- 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,
-}; /* CR_Arabic */
-
-/* 'Syriac': Script */
-static const OnigCodePoint CR_Syriac[] = {
- 3,
- 0x0700, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x074f,
-}; /* CR_Syriac */
-
-/* 'Thaana': Script */
-static const OnigCodePoint CR_Thaana[] = {
- 1,
- 0x0780, 0x07b1,
-}; /* CR_Thaana */
-
-/* 'Devanagari': Script */
-static const OnigCodePoint CR_Devanagari[] = {
- 4,
- 0x0900, 0x0950,
- 0x0953, 0x0963,
- 0x0966, 0x097f,
- 0xa8e0, 0xa8fd,
-}; /* CR_Devanagari */
-
-/* 'Bengali': Script */
-static const OnigCodePoint CR_Bengali[] = {
- 14,
- 0x0980, 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, 0x09fb,
-}; /* CR_Bengali */
-
-/* 'Gurmukhi': Script */
-static const OnigCodePoint CR_Gurmukhi[] = {
- 16,
- 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, 0x0a75,
-}; /* CR_Gurmukhi */
-
-/* 'Gujarati': Script */
-static const OnigCodePoint CR_Gujarati[] = {
- 14,
- 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, 0x0af9,
-}; /* CR_Gujarati */
-
-/* 'Oriya': Script */
-static const OnigCodePoint CR_Oriya[] = {
- 14,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b63,
- 0x0b66, 0x0b77,
-}; /* CR_Oriya */
-
-/* 'Tamil': Script */
-static const OnigCodePoint CR_Tamil[] = {
- 16,
- 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,
-}; /* CR_Tamil */
-
-/* 'Telugu': Script */
-static const OnigCodePoint CR_Telugu[] = {
- 13,
- 0x0c00, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c78, 0x0c7f,
-}; /* CR_Telugu */
-
-/* 'Kannada': Script */
-static const OnigCodePoint CR_Kannada[] = {
- 14,
- 0x0c80, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
-}; /* CR_Kannada */
-
-/* 'Malayalam': Script */
-static const OnigCodePoint CR_Malayalam[] = {
- 9,
- 0x0d01, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4f,
- 0x0d54, 0x0d63,
- 0x0d66, 0x0d7f,
-}; /* CR_Malayalam */
-
-/* 'Sinhala': Script */
-static const OnigCodePoint CR_Sinhala[] = {
- 13,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0de6, 0x0def,
- 0x0df2, 0x0df4,
- 0x111e1, 0x111f4,
-}; /* CR_Sinhala */
-
-/* 'Thai': Script */
-static const OnigCodePoint CR_Thai[] = {
- 2,
- 0x0e01, 0x0e3a,
- 0x0e40, 0x0e5b,
-}; /* CR_Thai */
-
-/* 'Lao': Script */
-static const OnigCodePoint CR_Lao[] = {
- 18,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edf,
-}; /* CR_Lao */
-
-/* 'Tibetan': Script */
-static const OnigCodePoint CR_Tibetan[] = {
- 7,
- 0x0f00, 0x0f47,
- 0x0f49, 0x0f6c,
- 0x0f71, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fbe, 0x0fcc,
- 0x0fce, 0x0fd4,
- 0x0fd9, 0x0fda,
-}; /* CR_Tibetan */
-
-/* 'Myanmar': Script */
-static const OnigCodePoint CR_Myanmar[] = {
- 3,
- 0x1000, 0x109f,
- 0xa9e0, 0xa9fe,
- 0xaa60, 0xaa7f,
-}; /* CR_Myanmar */
-
-/* 'Georgian': Script */
-static const OnigCodePoint CR_Georgian[] = {
- 8,
- 0x10a0, 0x10c5,
- 0x10c7, 0x10c7,
- 0x10cd, 0x10cd,
- 0x10d0, 0x10fa,
- 0x10fc, 0x10ff,
- 0x2d00, 0x2d25,
- 0x2d27, 0x2d27,
- 0x2d2d, 0x2d2d,
-}; /* CR_Georgian */
-
-/* 'Hangul': Script */
-static const OnigCodePoint CR_Hangul[] = {
- 14,
- 0x1100, 0x11ff,
- 0x302e, 0x302f,
- 0x3131, 0x318e,
- 0x3200, 0x321e,
- 0x3260, 0x327e,
- 0xa960, 0xa97c,
- 0xac00, 0xd7a3,
- 0xd7b0, 0xd7c6,
- 0xd7cb, 0xd7fb,
- 0xffa0, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
-}; /* CR_Hangul */
-
-/* 'Ethiopic': Script */
-static const OnigCodePoint CR_Ethiopic[] = {
- 32,
- 0x1200, 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,
- 0x2d80, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0xab01, 0xab06,
- 0xab09, 0xab0e,
- 0xab11, 0xab16,
- 0xab20, 0xab26,
- 0xab28, 0xab2e,
-}; /* CR_Ethiopic */
-
-/* 'Cherokee': Script */
-static const OnigCodePoint CR_Cherokee[] = {
- 3,
- 0x13a0, 0x13f5,
- 0x13f8, 0x13fd,
- 0xab70, 0xabbf,
-}; /* CR_Cherokee */
-
-/* 'Canadian_Aboriginal': Script */
-static const OnigCodePoint CR_Canadian_Aboriginal[] = {
- 2,
- 0x1400, 0x167f,
- 0x18b0, 0x18f5,
-}; /* CR_Canadian_Aboriginal */
-
-/* 'Ogham': Script */
-static const OnigCodePoint CR_Ogham[] = {
- 1,
- 0x1680, 0x169c,
-}; /* CR_Ogham */
-
-/* 'Runic': Script */
-static const OnigCodePoint CR_Runic[] = {
- 2,
- 0x16a0, 0x16ea,
- 0x16ee, 0x16f8,
-}; /* CR_Runic */
-
-/* 'Khmer': Script */
-static const OnigCodePoint CR_Khmer[] = {
- 4,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x19e0, 0x19ff,
-}; /* CR_Khmer */
-
-/* 'Mongolian': Script */
-static const OnigCodePoint CR_Mongolian[] = {
- 7,
- 0x1800, 0x1801,
- 0x1804, 0x1804,
- 0x1806, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18aa,
- 0x11660, 0x1166c,
-}; /* CR_Mongolian */
-
-/* 'Hiragana': Script */
-static const OnigCodePoint CR_Hiragana[] = {
- 4,
- 0x3041, 0x3096,
- 0x309d, 0x309f,
- 0x1b001, 0x1b001,
- 0x1f200, 0x1f200,
-}; /* CR_Hiragana */
-
-/* 'Katakana': Script */
-static const OnigCodePoint CR_Katakana[] = {
- 8,
- 0x30a1, 0x30fa,
- 0x30fd, 0x30ff,
- 0x31f0, 0x31ff,
- 0x32d0, 0x32fe,
- 0x3300, 0x3357,
- 0xff66, 0xff6f,
- 0xff71, 0xff9d,
- 0x1b000, 0x1b000,
-}; /* CR_Katakana */
-
-/* 'Bopomofo': Script */
-static const OnigCodePoint CR_Bopomofo[] = {
- 3,
- 0x02ea, 0x02eb,
- 0x3105, 0x312d,
- 0x31a0, 0x31ba,
-}; /* CR_Bopomofo */
-
-/* 'Han': Script */
-static const OnigCodePoint CR_Han[] = {
- 16,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x3005, 0x3005,
- 0x3007, 0x3007,
- 0x3021, 0x3029,
- 0x3038, 0x303b,
- 0x3400, 0x4db5,
- 0x4e00, 0x9fd5,
- 0xf900, 0xfa6d,
- 0xfa70, 0xfad9,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
-}; /* CR_Han */
-
-/* 'Yi': Script */
-static const OnigCodePoint CR_Yi[] = {
- 2,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
-}; /* CR_Yi */
-
-/* 'Old_Italic': Script */
-static const OnigCodePoint CR_Old_Italic[] = {
- 1,
- 0x10300, 0x10323,
-}; /* CR_Old_Italic */
-
-/* 'Gothic': Script */
-static const OnigCodePoint CR_Gothic[] = {
- 1,
- 0x10330, 0x1034a,
-}; /* CR_Gothic */
-
-/* 'Deseret': Script */
-static const OnigCodePoint CR_Deseret[] = {
- 1,
- 0x10400, 0x1044f,
-}; /* CR_Deseret */
-
-/* 'Inherited': Script */
-static const OnigCodePoint CR_Inherited[] = {
- 27,
- 0x0300, 0x036f,
- 0x0485, 0x0486,
- 0x064b, 0x0655,
- 0x0670, 0x0670,
- 0x0951, 0x0952,
- 0x1ab0, 0x1abe,
- 0x1cd0, 0x1cd2,
- 0x1cd4, 0x1ce0,
- 0x1ce2, 0x1ce8,
- 0x1ced, 0x1ced,
- 0x1cf4, 0x1cf4,
- 0x1cf8, 0x1cf9,
- 0x1dc0, 0x1df5,
- 0x1dfb, 0x1dff,
- 0x200c, 0x200d,
- 0x20d0, 0x20f0,
- 0x302a, 0x302d,
- 0x3099, 0x309a,
- 0xfe00, 0xfe0f,
- 0xfe20, 0xfe2d,
- 0x101fd, 0x101fd,
- 0x102e0, 0x102e0,
- 0x1d167, 0x1d169,
- 0x1d17b, 0x1d182,
- 0x1d185, 0x1d18b,
- 0x1d1aa, 0x1d1ad,
- 0xe0100, 0xe01ef,
-}; /* CR_Inherited */
-
-/* 'Tagalog': Script */
-static const OnigCodePoint CR_Tagalog[] = {
- 2,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
-}; /* CR_Tagalog */
-
-/* 'Hanunoo': Script */
-static const OnigCodePoint CR_Hanunoo[] = {
- 1,
- 0x1720, 0x1734,
-}; /* CR_Hanunoo */
-
-/* 'Buhid': Script */
-static const OnigCodePoint CR_Buhid[] = {
- 1,
- 0x1740, 0x1753,
-}; /* CR_Buhid */
-
-/* 'Tagbanwa': Script */
-static const OnigCodePoint CR_Tagbanwa[] = {
- 3,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
-}; /* CR_Tagbanwa */
-
-/* 'Limbu': Script */
-static const OnigCodePoint CR_Limbu[] = {
- 5,
- 0x1900, 0x191e,
- 0x1920, 0x192b,
- 0x1930, 0x193b,
- 0x1940, 0x1940,
- 0x1944, 0x194f,
-}; /* CR_Limbu */
-
-/* 'Tai_Le': Script */
-static const OnigCodePoint CR_Tai_Le[] = {
- 2,
- 0x1950, 0x196d,
- 0x1970, 0x1974,
-}; /* CR_Tai_Le */
-
-/* 'Linear_B': Script */
-static const OnigCodePoint CR_Linear_B[] = {
- 7,
- 0x10000, 0x1000b,
- 0x1000d, 0x10026,
- 0x10028, 0x1003a,
- 0x1003c, 0x1003d,
- 0x1003f, 0x1004d,
- 0x10050, 0x1005d,
- 0x10080, 0x100fa,
-}; /* CR_Linear_B */
-
-/* 'Ugaritic': Script */
-static const OnigCodePoint CR_Ugaritic[] = {
- 2,
- 0x10380, 0x1039d,
- 0x1039f, 0x1039f,
-}; /* CR_Ugaritic */
-
-/* 'Shavian': Script */
-static const OnigCodePoint CR_Shavian[] = {
- 1,
- 0x10450, 0x1047f,
-}; /* CR_Shavian */
-
-/* 'Osmanya': Script */
-static const OnigCodePoint CR_Osmanya[] = {
- 2,
- 0x10480, 0x1049d,
- 0x104a0, 0x104a9,
-}; /* CR_Osmanya */
-
-/* 'Cypriot': Script */
-static const OnigCodePoint CR_Cypriot[] = {
- 6,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x1083f,
-}; /* CR_Cypriot */
-
-/* 'Braille': Script */
-static const OnigCodePoint CR_Braille[] = {
- 1,
- 0x2800, 0x28ff,
-}; /* CR_Braille */
-
-/* 'Buginese': Script */
-static const OnigCodePoint CR_Buginese[] = {
- 2,
- 0x1a00, 0x1a1b,
- 0x1a1e, 0x1a1f,
-}; /* CR_Buginese */
-
-/* 'Coptic': Script */
-static const OnigCodePoint CR_Coptic[] = {
- 3,
- 0x03e2, 0x03ef,
- 0x2c80, 0x2cf3,
- 0x2cf9, 0x2cff,
-}; /* CR_Coptic */
-
-/* 'New_Tai_Lue': Script */
-static const OnigCodePoint CR_New_Tai_Lue[] = {
- 4,
- 0x1980, 0x19ab,
- 0x19b0, 0x19c9,
- 0x19d0, 0x19da,
- 0x19de, 0x19df,
-}; /* CR_New_Tai_Lue */
-
-/* 'Glagolitic': Script */
-static const OnigCodePoint CR_Glagolitic[] = {
- 7,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x1e000, 0x1e006,
- 0x1e008, 0x1e018,
- 0x1e01b, 0x1e021,
- 0x1e023, 0x1e024,
- 0x1e026, 0x1e02a,
-}; /* CR_Glagolitic */
-
-/* 'Tifinagh': Script */
-static const OnigCodePoint CR_Tifinagh[] = {
- 3,
- 0x2d30, 0x2d67,
- 0x2d6f, 0x2d70,
- 0x2d7f, 0x2d7f,
-}; /* CR_Tifinagh */
-
-/* 'Syloti_Nagri': Script */
-static const OnigCodePoint CR_Syloti_Nagri[] = {
- 1,
- 0xa800, 0xa82b,
-}; /* CR_Syloti_Nagri */
-
-/* 'Old_Persian': Script */
-static const OnigCodePoint CR_Old_Persian[] = {
- 2,
- 0x103a0, 0x103c3,
- 0x103c8, 0x103d5,
-}; /* CR_Old_Persian */
-
-/* 'Kharoshthi': Script */
-static const OnigCodePoint CR_Kharoshthi[] = {
- 8,
- 0x10a00, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a47,
- 0x10a50, 0x10a58,
-}; /* CR_Kharoshthi */
-
-/* 'Balinese': Script */
-static const OnigCodePoint CR_Balinese[] = {
- 2,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b7c,
-}; /* CR_Balinese */
-
-/* 'Cuneiform': Script */
-static const OnigCodePoint CR_Cuneiform[] = {
- 4,
- 0x12000, 0x12399,
- 0x12400, 0x1246e,
- 0x12470, 0x12474,
- 0x12480, 0x12543,
-}; /* CR_Cuneiform */
-
-/* 'Phoenician': Script */
-static const OnigCodePoint CR_Phoenician[] = {
- 2,
- 0x10900, 0x1091b,
- 0x1091f, 0x1091f,
-}; /* CR_Phoenician */
-
-/* 'Phags_Pa': Script */
-static const OnigCodePoint CR_Phags_Pa[] = {
- 1,
- 0xa840, 0xa877,
-}; /* CR_Phags_Pa */
-
-/* 'Nko': Script */
-static const OnigCodePoint CR_Nko[] = {
- 1,
- 0x07c0, 0x07fa,
-}; /* CR_Nko */
-
-/* 'Sundanese': Script */
-static const OnigCodePoint CR_Sundanese[] = {
- 2,
- 0x1b80, 0x1bbf,
- 0x1cc0, 0x1cc7,
-}; /* CR_Sundanese */
-
-/* 'Lepcha': Script */
-static const OnigCodePoint CR_Lepcha[] = {
- 3,
- 0x1c00, 0x1c37,
- 0x1c3b, 0x1c49,
- 0x1c4d, 0x1c4f,
-}; /* CR_Lepcha */
-
-/* 'Ol_Chiki': Script */
-static const OnigCodePoint CR_Ol_Chiki[] = {
- 1,
- 0x1c50, 0x1c7f,
-}; /* CR_Ol_Chiki */
-
-/* 'Vai': Script */
-static const OnigCodePoint CR_Vai[] = {
- 1,
- 0xa500, 0xa62b,
-}; /* CR_Vai */
-
-/* 'Saurashtra': Script */
-static const OnigCodePoint CR_Saurashtra[] = {
- 2,
- 0xa880, 0xa8c5,
- 0xa8ce, 0xa8d9,
-}; /* CR_Saurashtra */
-
-/* 'Kayah_Li': Script */
-static const OnigCodePoint CR_Kayah_Li[] = {
- 2,
- 0xa900, 0xa92d,
- 0xa92f, 0xa92f,
-}; /* CR_Kayah_Li */
-
-/* 'Rejang': Script */
-static const OnigCodePoint CR_Rejang[] = {
- 2,
- 0xa930, 0xa953,
- 0xa95f, 0xa95f,
-}; /* CR_Rejang */
-
-/* 'Lycian': Script */
-static const OnigCodePoint CR_Lycian[] = {
- 1,
- 0x10280, 0x1029c,
-}; /* CR_Lycian */
-
-/* 'Carian': Script */
-static const OnigCodePoint CR_Carian[] = {
- 1,
- 0x102a0, 0x102d0,
-}; /* CR_Carian */
-
-/* 'Lydian': Script */
-static const OnigCodePoint CR_Lydian[] = {
- 2,
- 0x10920, 0x10939,
- 0x1093f, 0x1093f,
-}; /* CR_Lydian */
-
-/* 'Cham': Script */
-static const OnigCodePoint CR_Cham[] = {
- 4,
- 0xaa00, 0xaa36,
- 0xaa40, 0xaa4d,
- 0xaa50, 0xaa59,
- 0xaa5c, 0xaa5f,
-}; /* CR_Cham */
-
-/* 'Tai_Tham': Script */
-static const OnigCodePoint CR_Tai_Tham[] = {
- 5,
- 0x1a20, 0x1a5e,
- 0x1a60, 0x1a7c,
- 0x1a7f, 0x1a89,
- 0x1a90, 0x1a99,
- 0x1aa0, 0x1aad,
-}; /* CR_Tai_Tham */
-
-/* 'Tai_Viet': Script */
-static const OnigCodePoint CR_Tai_Viet[] = {
- 2,
- 0xaa80, 0xaac2,
- 0xaadb, 0xaadf,
-}; /* CR_Tai_Viet */
-
-/* 'Avestan': Script */
-static const OnigCodePoint CR_Avestan[] = {
- 2,
- 0x10b00, 0x10b35,
- 0x10b39, 0x10b3f,
-}; /* CR_Avestan */
-
-/* 'Egyptian_Hieroglyphs': Script */
-static const OnigCodePoint CR_Egyptian_Hieroglyphs[] = {
- 1,
- 0x13000, 0x1342e,
-}; /* CR_Egyptian_Hieroglyphs */
-
-/* 'Samaritan': Script */
-static const OnigCodePoint CR_Samaritan[] = {
- 2,
- 0x0800, 0x082d,
- 0x0830, 0x083e,
-}; /* CR_Samaritan */
-
-/* 'Lisu': Script */
-static const OnigCodePoint CR_Lisu[] = {
- 1,
- 0xa4d0, 0xa4ff,
-}; /* CR_Lisu */
-
-/* 'Bamum': Script */
-static const OnigCodePoint CR_Bamum[] = {
- 2,
- 0xa6a0, 0xa6f7,
- 0x16800, 0x16a38,
-}; /* CR_Bamum */
-
-/* 'Javanese': Script */
-static const OnigCodePoint CR_Javanese[] = {
- 3,
- 0xa980, 0xa9cd,
- 0xa9d0, 0xa9d9,
- 0xa9de, 0xa9df,
-}; /* CR_Javanese */
-
-/* 'Meetei_Mayek': Script */
-static const OnigCodePoint CR_Meetei_Mayek[] = {
- 3,
- 0xaae0, 0xaaf6,
- 0xabc0, 0xabed,
- 0xabf0, 0xabf9,
-}; /* CR_Meetei_Mayek */
-
-/* 'Imperial_Aramaic': Script */
-static const OnigCodePoint CR_Imperial_Aramaic[] = {
- 2,
- 0x10840, 0x10855,
- 0x10857, 0x1085f,
-}; /* CR_Imperial_Aramaic */
-
-/* 'Old_South_Arabian': Script */
-static const OnigCodePoint CR_Old_South_Arabian[] = {
- 1,
- 0x10a60, 0x10a7f,
-}; /* CR_Old_South_Arabian */
-
-/* 'Inscriptional_Parthian': Script */
-static const OnigCodePoint CR_Inscriptional_Parthian[] = {
- 2,
- 0x10b40, 0x10b55,
- 0x10b58, 0x10b5f,
-}; /* CR_Inscriptional_Parthian */
-
-/* 'Inscriptional_Pahlavi': Script */
-static const OnigCodePoint CR_Inscriptional_Pahlavi[] = {
- 2,
- 0x10b60, 0x10b72,
- 0x10b78, 0x10b7f,
-}; /* CR_Inscriptional_Pahlavi */
-
-/* 'Old_Turkic': Script */
-static const OnigCodePoint CR_Old_Turkic[] = {
- 1,
- 0x10c00, 0x10c48,
-}; /* CR_Old_Turkic */
-
-/* 'Kaithi': Script */
-static const OnigCodePoint CR_Kaithi[] = {
- 1,
- 0x11080, 0x110c1,
-}; /* CR_Kaithi */
-
-/* 'Batak': Script */
-static const OnigCodePoint CR_Batak[] = {
- 2,
- 0x1bc0, 0x1bf3,
- 0x1bfc, 0x1bff,
-}; /* CR_Batak */
-
-/* 'Brahmi': Script */
-static const OnigCodePoint CR_Brahmi[] = {
- 3,
- 0x11000, 0x1104d,
- 0x11052, 0x1106f,
- 0x1107f, 0x1107f,
-}; /* CR_Brahmi */
-
-/* 'Mandaic': Script */
-static const OnigCodePoint CR_Mandaic[] = {
- 2,
- 0x0840, 0x085b,
- 0x085e, 0x085e,
-}; /* CR_Mandaic */
-
-/* 'Chakma': Script */
-static const OnigCodePoint CR_Chakma[] = {
- 2,
- 0x11100, 0x11134,
- 0x11136, 0x11143,
-}; /* CR_Chakma */
-
-/* 'Meroitic_Cursive': Script */
-static const OnigCodePoint CR_Meroitic_Cursive[] = {
- 3,
- 0x109a0, 0x109b7,
- 0x109bc, 0x109cf,
- 0x109d2, 0x109ff,
-}; /* CR_Meroitic_Cursive */
-
-/* 'Meroitic_Hieroglyphs': Script */
-static const OnigCodePoint CR_Meroitic_Hieroglyphs[] = {
- 1,
- 0x10980, 0x1099f,
-}; /* CR_Meroitic_Hieroglyphs */
-
-/* 'Miao': Script */
-static const OnigCodePoint CR_Miao[] = {
- 3,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f8f, 0x16f9f,
-}; /* CR_Miao */
-
-/* 'Sharada': Script */
-static const OnigCodePoint CR_Sharada[] = {
- 2,
- 0x11180, 0x111cd,
- 0x111d0, 0x111df,
-}; /* CR_Sharada */
-
-/* 'Sora_Sompeng': Script */
-static const OnigCodePoint CR_Sora_Sompeng[] = {
- 2,
- 0x110d0, 0x110e8,
- 0x110f0, 0x110f9,
-}; /* CR_Sora_Sompeng */
-
-/* 'Takri': Script */
-static const OnigCodePoint CR_Takri[] = {
- 2,
- 0x11680, 0x116b7,
- 0x116c0, 0x116c9,
-}; /* CR_Takri */
-
-/* 'Caucasian_Albanian': Script */
-static const OnigCodePoint CR_Caucasian_Albanian[] = {
- 2,
- 0x10530, 0x10563,
- 0x1056f, 0x1056f,
-}; /* CR_Caucasian_Albanian */
-
-/* 'Bassa_Vah': Script */
-static const OnigCodePoint CR_Bassa_Vah[] = {
- 2,
- 0x16ad0, 0x16aed,
- 0x16af0, 0x16af5,
-}; /* CR_Bassa_Vah */
-
-/* 'Duployan': Script */
-static const OnigCodePoint CR_Duployan[] = {
- 5,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 0x1bc9c, 0x1bc9f,
-}; /* CR_Duployan */
-
-/* 'Elbasan': Script */
-static const OnigCodePoint CR_Elbasan[] = {
- 1,
- 0x10500, 0x10527,
-}; /* CR_Elbasan */
-
-/* 'Grantha': Script */
-static const OnigCodePoint CR_Grantha[] = {
- 15,
- 0x11300, 0x11303,
- 0x11305, 0x1130c,
- 0x1130f, 0x11310,
- 0x11313, 0x11328,
- 0x1132a, 0x11330,
- 0x11332, 0x11333,
- 0x11335, 0x11339,
- 0x1133c, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134d,
- 0x11350, 0x11350,
- 0x11357, 0x11357,
- 0x1135d, 0x11363,
- 0x11366, 0x1136c,
- 0x11370, 0x11374,
-}; /* CR_Grantha */
-
-/* 'Pahawh_Hmong': Script */
-static const OnigCodePoint CR_Pahawh_Hmong[] = {
- 5,
- 0x16b00, 0x16b45,
- 0x16b50, 0x16b59,
- 0x16b5b, 0x16b61,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
-}; /* CR_Pahawh_Hmong */
-
-/* 'Khojki': Script */
-static const OnigCodePoint CR_Khojki[] = {
- 2,
- 0x11200, 0x11211,
- 0x11213, 0x1123e,
-}; /* CR_Khojki */
-
-/* 'Linear_A': Script */
-static const OnigCodePoint CR_Linear_A[] = {
- 3,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
-}; /* CR_Linear_A */
-
-/* 'Mahajani': Script */
-static const OnigCodePoint CR_Mahajani[] = {
- 1,
- 0x11150, 0x11176,
-}; /* CR_Mahajani */
-
-/* 'Manichaean': Script */
-static const OnigCodePoint CR_Manichaean[] = {
- 2,
- 0x10ac0, 0x10ae6,
- 0x10aeb, 0x10af6,
-}; /* CR_Manichaean */
-
-/* 'Mende_Kikakui': Script */
-static const OnigCodePoint CR_Mende_Kikakui[] = {
- 2,
- 0x1e800, 0x1e8c4,
- 0x1e8c7, 0x1e8d6,
-}; /* CR_Mende_Kikakui */
-
-/* 'Modi': Script */
-static const OnigCodePoint CR_Modi[] = {
- 2,
- 0x11600, 0x11644,
- 0x11650, 0x11659,
-}; /* CR_Modi */
-
-/* 'Mro': Script */
-static const OnigCodePoint CR_Mro[] = {
- 3,
- 0x16a40, 0x16a5e,
- 0x16a60, 0x16a69,
- 0x16a6e, 0x16a6f,
-}; /* CR_Mro */
-
-/* 'Old_North_Arabian': Script */
-static const OnigCodePoint CR_Old_North_Arabian[] = {
- 1,
- 0x10a80, 0x10a9f,
-}; /* CR_Old_North_Arabian */
-
-/* 'Nabataean': Script */
-static const OnigCodePoint CR_Nabataean[] = {
- 2,
- 0x10880, 0x1089e,
- 0x108a7, 0x108af,
-}; /* CR_Nabataean */
-
-/* 'Palmyrene': Script */
-static const OnigCodePoint CR_Palmyrene[] = {
- 1,
- 0x10860, 0x1087f,
-}; /* CR_Palmyrene */
-
-/* 'Pau_Cin_Hau': Script */
-static const OnigCodePoint CR_Pau_Cin_Hau[] = {
- 1,
- 0x11ac0, 0x11af8,
-}; /* CR_Pau_Cin_Hau */
-
-/* 'Old_Permic': Script */
-static const OnigCodePoint CR_Old_Permic[] = {
- 1,
- 0x10350, 0x1037a,
-}; /* CR_Old_Permic */
-
-/* 'Psalter_Pahlavi': Script */
-static const OnigCodePoint CR_Psalter_Pahlavi[] = {
- 3,
- 0x10b80, 0x10b91,
- 0x10b99, 0x10b9c,
- 0x10ba9, 0x10baf,
-}; /* CR_Psalter_Pahlavi */
-
-/* 'Siddham': Script */
-static const OnigCodePoint CR_Siddham[] = {
- 2,
- 0x11580, 0x115b5,
- 0x115b8, 0x115dd,
-}; /* CR_Siddham */
-
-/* 'Khudawadi': Script */
-static const OnigCodePoint CR_Khudawadi[] = {
- 2,
- 0x112b0, 0x112ea,
- 0x112f0, 0x112f9,
-}; /* CR_Khudawadi */
-
-/* 'Tirhuta': Script */
-static const OnigCodePoint CR_Tirhuta[] = {
- 2,
- 0x11480, 0x114c7,
- 0x114d0, 0x114d9,
-}; /* CR_Tirhuta */
-
-/* 'Warang_Citi': Script */
-static const OnigCodePoint CR_Warang_Citi[] = {
- 2,
- 0x118a0, 0x118f2,
- 0x118ff, 0x118ff,
-}; /* CR_Warang_Citi */
-
-/* 'Ahom': Script */
-static const OnigCodePoint CR_Ahom[] = {
- 3,
- 0x11700, 0x11719,
- 0x1171d, 0x1172b,
- 0x11730, 0x1173f,
-}; /* CR_Ahom */
-
-/* 'Anatolian_Hieroglyphs': Script */
-static const OnigCodePoint CR_Anatolian_Hieroglyphs[] = {
- 1,
- 0x14400, 0x14646,
-}; /* CR_Anatolian_Hieroglyphs */
-
-/* 'Hatran': Script */
-static const OnigCodePoint CR_Hatran[] = {
- 3,
- 0x108e0, 0x108f2,
- 0x108f4, 0x108f5,
- 0x108fb, 0x108ff,
-}; /* CR_Hatran */
-
-/* 'Multani': Script */
-static const OnigCodePoint CR_Multani[] = {
- 5,
- 0x11280, 0x11286,
- 0x11288, 0x11288,
- 0x1128a, 0x1128d,
- 0x1128f, 0x1129d,
- 0x1129f, 0x112a9,
-}; /* CR_Multani */
-
-/* 'Old_Hungarian': Script */
-static const OnigCodePoint CR_Old_Hungarian[] = {
- 3,
- 0x10c80, 0x10cb2,
- 0x10cc0, 0x10cf2,
- 0x10cfa, 0x10cff,
-}; /* CR_Old_Hungarian */
-
-/* 'SignWriting': Script */
-static const OnigCodePoint CR_SignWriting[] = {
- 3,
- 0x1d800, 0x1da8b,
- 0x1da9b, 0x1da9f,
- 0x1daa1, 0x1daaf,
-}; /* CR_SignWriting */
-
-/* 'Adlam': Script */
-static const OnigCodePoint CR_Adlam[] = {
- 3,
- 0x1e900, 0x1e94a,
- 0x1e950, 0x1e959,
- 0x1e95e, 0x1e95f,
-}; /* CR_Adlam */
-
-/* 'Bhaiksuki': Script */
-static const OnigCodePoint CR_Bhaiksuki[] = {
- 4,
- 0x11c00, 0x11c08,
- 0x11c0a, 0x11c36,
- 0x11c38, 0x11c45,
- 0x11c50, 0x11c6c,
-}; /* CR_Bhaiksuki */
-
-/* 'Marchen': Script */
-static const OnigCodePoint CR_Marchen[] = {
- 3,
- 0x11c70, 0x11c8f,
- 0x11c92, 0x11ca7,
- 0x11ca9, 0x11cb6,
-}; /* CR_Marchen */
-
-/* 'Newa': Script */
-static const OnigCodePoint CR_Newa[] = {
- 3,
- 0x11400, 0x11459,
- 0x1145b, 0x1145b,
- 0x1145d, 0x1145d,
-}; /* CR_Newa */
-
-/* 'Osage': Script */
-static const OnigCodePoint CR_Osage[] = {
- 2,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
-}; /* CR_Osage */
-
-/* 'Tangut': Script */
-static const OnigCodePoint CR_Tangut[] = {
- 3,
- 0x16fe0, 0x16fe0,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
-}; /* CR_Tangut */
-
-/* 'White_Space': Binary Property */
-#define CR_White_Space CR_Space
-
-/* 'Bidi_Control': Binary Property */
-static const OnigCodePoint CR_Bidi_Control[] = {
- 4,
- 0x061c, 0x061c,
- 0x200e, 0x200f,
- 0x202a, 0x202e,
- 0x2066, 0x2069,
-}; /* CR_Bidi_Control */
-
-/* 'Join_Control': Binary Property */
-static const OnigCodePoint CR_Join_Control[] = {
- 1,
- 0x200c, 0x200d,
-}; /* CR_Join_Control */
-
-/* 'Dash': Binary Property */
-static const OnigCodePoint CR_Dash[] = {
- 21,
- 0x002d, 0x002d,
- 0x058a, 0x058a,
- 0x05be, 0x05be,
- 0x1400, 0x1400,
- 0x1806, 0x1806,
- 0x2010, 0x2015,
- 0x2053, 0x2053,
- 0x207b, 0x207b,
- 0x208b, 0x208b,
- 0x2212, 0x2212,
- 0x2e17, 0x2e17,
- 0x2e1a, 0x2e1a,
- 0x2e3a, 0x2e3b,
- 0x2e40, 0x2e40,
- 0x301c, 0x301c,
- 0x3030, 0x3030,
- 0x30a0, 0x30a0,
- 0xfe31, 0xfe32,
- 0xfe58, 0xfe58,
- 0xfe63, 0xfe63,
- 0xff0d, 0xff0d,
-}; /* CR_Dash */
-
-/* 'Hyphen': Binary Property */
-static const OnigCodePoint CR_Hyphen[] = {
- 10,
- 0x002d, 0x002d,
- 0x00ad, 0x00ad,
- 0x058a, 0x058a,
- 0x1806, 0x1806,
- 0x2010, 0x2011,
- 0x2e17, 0x2e17,
- 0x30fb, 0x30fb,
- 0xfe63, 0xfe63,
- 0xff0d, 0xff0d,
- 0xff65, 0xff65,
-}; /* CR_Hyphen */
-
-/* 'Quotation_Mark': Binary Property */
-static const OnigCodePoint CR_Quotation_Mark[] = {
- 13,
- 0x0022, 0x0022,
- 0x0027, 0x0027,
- 0x00ab, 0x00ab,
- 0x00bb, 0x00bb,
- 0x2018, 0x201f,
- 0x2039, 0x203a,
- 0x2e42, 0x2e42,
- 0x300c, 0x300f,
- 0x301d, 0x301f,
- 0xfe41, 0xfe44,
- 0xff02, 0xff02,
- 0xff07, 0xff07,
- 0xff62, 0xff63,
-}; /* CR_Quotation_Mark */
-
-/* 'Terminal_Punctuation': Binary Property */
-static const OnigCodePoint CR_Terminal_Punctuation[] = {
- 94,
- 0x0021, 0x0021,
- 0x002c, 0x002c,
- 0x002e, 0x002e,
- 0x003a, 0x003b,
- 0x003f, 0x003f,
- 0x037e, 0x037e,
- 0x0387, 0x0387,
- 0x0589, 0x0589,
- 0x05c3, 0x05c3,
- 0x060c, 0x060c,
- 0x061b, 0x061b,
- 0x061f, 0x061f,
- 0x06d4, 0x06d4,
- 0x0700, 0x070a,
- 0x070c, 0x070c,
- 0x07f8, 0x07f9,
- 0x0830, 0x083e,
- 0x085e, 0x085e,
- 0x0964, 0x0965,
- 0x0e5a, 0x0e5b,
- 0x0f08, 0x0f08,
- 0x0f0d, 0x0f12,
- 0x104a, 0x104b,
- 0x1361, 0x1368,
- 0x166d, 0x166e,
- 0x16eb, 0x16ed,
- 0x1735, 0x1736,
- 0x17d4, 0x17d6,
- 0x17da, 0x17da,
- 0x1802, 0x1805,
- 0x1808, 0x1809,
- 0x1944, 0x1945,
- 0x1aa8, 0x1aab,
- 0x1b5a, 0x1b5b,
- 0x1b5d, 0x1b5f,
- 0x1c3b, 0x1c3f,
- 0x1c7e, 0x1c7f,
- 0x203c, 0x203d,
- 0x2047, 0x2049,
- 0x2e2e, 0x2e2e,
- 0x2e3c, 0x2e3c,
- 0x2e41, 0x2e41,
- 0x3001, 0x3002,
- 0xa4fe, 0xa4ff,
- 0xa60d, 0xa60f,
- 0xa6f3, 0xa6f7,
- 0xa876, 0xa877,
- 0xa8ce, 0xa8cf,
- 0xa92f, 0xa92f,
- 0xa9c7, 0xa9c9,
- 0xaa5d, 0xaa5f,
- 0xaadf, 0xaadf,
- 0xaaf0, 0xaaf1,
- 0xabeb, 0xabeb,
- 0xfe50, 0xfe52,
- 0xfe54, 0xfe57,
- 0xff01, 0xff01,
- 0xff0c, 0xff0c,
- 0xff0e, 0xff0e,
- 0xff1a, 0xff1b,
- 0xff1f, 0xff1f,
- 0xff61, 0xff61,
- 0xff64, 0xff64,
- 0x1039f, 0x1039f,
- 0x103d0, 0x103d0,
- 0x10857, 0x10857,
- 0x1091f, 0x1091f,
- 0x10a56, 0x10a57,
- 0x10af0, 0x10af5,
- 0x10b3a, 0x10b3f,
- 0x10b99, 0x10b9c,
- 0x11047, 0x1104d,
- 0x110be, 0x110c1,
- 0x11141, 0x11143,
- 0x111c5, 0x111c6,
- 0x111cd, 0x111cd,
- 0x111de, 0x111df,
- 0x11238, 0x1123c,
- 0x112a9, 0x112a9,
- 0x1144b, 0x1144d,
- 0x1145b, 0x1145b,
- 0x115c2, 0x115c5,
- 0x115c9, 0x115d7,
- 0x11641, 0x11642,
- 0x1173c, 0x1173e,
- 0x11c41, 0x11c43,
- 0x11c71, 0x11c71,
- 0x12470, 0x12474,
- 0x16a6e, 0x16a6f,
- 0x16af5, 0x16af5,
- 0x16b37, 0x16b39,
- 0x16b44, 0x16b44,
- 0x1bc9f, 0x1bc9f,
- 0x1da87, 0x1da8a,
-}; /* CR_Terminal_Punctuation */
-
-/* 'Other_Math': Binary Property */
-static const OnigCodePoint CR_Other_Math[] = {
- 134,
- 0x005e, 0x005e,
- 0x03d0, 0x03d2,
- 0x03d5, 0x03d5,
- 0x03f0, 0x03f1,
- 0x03f4, 0x03f5,
- 0x2016, 0x2016,
- 0x2032, 0x2034,
- 0x2040, 0x2040,
- 0x2061, 0x2064,
- 0x207d, 0x207e,
- 0x208d, 0x208e,
- 0x20d0, 0x20dc,
- 0x20e1, 0x20e1,
- 0x20e5, 0x20e6,
- 0x20eb, 0x20ef,
- 0x2102, 0x2102,
- 0x2107, 0x2107,
- 0x210a, 0x2113,
- 0x2115, 0x2115,
- 0x2119, 0x211d,
- 0x2124, 0x2124,
- 0x2128, 0x2129,
- 0x212c, 0x212d,
- 0x212f, 0x2131,
- 0x2133, 0x2138,
- 0x213c, 0x213f,
- 0x2145, 0x2149,
- 0x2195, 0x2199,
- 0x219c, 0x219f,
- 0x21a1, 0x21a2,
- 0x21a4, 0x21a5,
- 0x21a7, 0x21a7,
- 0x21a9, 0x21ad,
- 0x21b0, 0x21b1,
- 0x21b6, 0x21b7,
- 0x21bc, 0x21cd,
- 0x21d0, 0x21d1,
- 0x21d3, 0x21d3,
- 0x21d5, 0x21db,
- 0x21dd, 0x21dd,
- 0x21e4, 0x21e5,
- 0x2308, 0x230b,
- 0x23b4, 0x23b5,
- 0x23b7, 0x23b7,
- 0x23d0, 0x23d0,
- 0x23e2, 0x23e2,
- 0x25a0, 0x25a1,
- 0x25ae, 0x25b6,
- 0x25bc, 0x25c0,
- 0x25c6, 0x25c7,
- 0x25ca, 0x25cb,
- 0x25cf, 0x25d3,
- 0x25e2, 0x25e2,
- 0x25e4, 0x25e4,
- 0x25e7, 0x25ec,
- 0x2605, 0x2606,
- 0x2640, 0x2640,
- 0x2642, 0x2642,
- 0x2660, 0x2663,
- 0x266d, 0x266e,
- 0x27c5, 0x27c6,
- 0x27e6, 0x27ef,
- 0x2983, 0x2998,
- 0x29d8, 0x29db,
- 0x29fc, 0x29fd,
- 0xfe61, 0xfe61,
- 0xfe63, 0xfe63,
- 0xfe68, 0xfe68,
- 0xff3c, 0xff3c,
- 0xff3e, 0xff3e,
- 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, 0x1d6c0,
- 0x1d6c2, 0x1d6da,
- 0x1d6dc, 0x1d6fa,
- 0x1d6fc, 0x1d714,
- 0x1d716, 0x1d734,
- 0x1d736, 0x1d74e,
- 0x1d750, 0x1d76e,
- 0x1d770, 0x1d788,
- 0x1d78a, 0x1d7a8,
- 0x1d7aa, 0x1d7c2,
- 0x1d7c4, 0x1d7cb,
- 0x1d7ce, 0x1d7ff,
- 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,
-}; /* CR_Other_Math */
-
-/* 'Hex_Digit': Binary Property */
-static const OnigCodePoint CR_Hex_Digit[] = {
- 6,
- 0x0030, 0x0039,
- 0x0041, 0x0046,
- 0x0061, 0x0066,
- 0xff10, 0xff19,
- 0xff21, 0xff26,
- 0xff41, 0xff46,
-}; /* CR_Hex_Digit */
-
-/* 'ASCII_Hex_Digit': Binary Property */
-#define CR_ASCII_Hex_Digit CR_XDigit
-
-/* 'Other_Alphabetic': Binary Property */
-static const OnigCodePoint CR_Other_Alphabetic[] = {
- 194,
- 0x0345, 0x0345,
- 0x05b0, 0x05bd,
- 0x05bf, 0x05bf,
- 0x05c1, 0x05c2,
- 0x05c4, 0x05c5,
- 0x05c7, 0x05c7,
- 0x0610, 0x061a,
- 0x064b, 0x0657,
- 0x0659, 0x065f,
- 0x0670, 0x0670,
- 0x06d6, 0x06dc,
- 0x06e1, 0x06e4,
- 0x06e7, 0x06e8,
- 0x06ed, 0x06ed,
- 0x0711, 0x0711,
- 0x0730, 0x073f,
- 0x07a6, 0x07b0,
- 0x0816, 0x0817,
- 0x081b, 0x0823,
- 0x0825, 0x0827,
- 0x0829, 0x082c,
- 0x08d4, 0x08df,
- 0x08e3, 0x08e9,
- 0x08f0, 0x0903,
- 0x093a, 0x093b,
- 0x093e, 0x094c,
- 0x094e, 0x094f,
- 0x0955, 0x0957,
- 0x0962, 0x0963,
- 0x0981, 0x0983,
- 0x09be, 0x09c4,
- 0x09c7, 0x09c8,
- 0x09cb, 0x09cc,
- 0x09d7, 0x09d7,
- 0x09e2, 0x09e3,
- 0x0a01, 0x0a03,
- 0x0a3e, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4c,
- 0x0a51, 0x0a51,
- 0x0a70, 0x0a71,
- 0x0a75, 0x0a75,
- 0x0a81, 0x0a83,
- 0x0abe, 0x0ac5,
- 0x0ac7, 0x0ac9,
- 0x0acb, 0x0acc,
- 0x0ae2, 0x0ae3,
- 0x0b01, 0x0b03,
- 0x0b3e, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4c,
- 0x0b56, 0x0b57,
- 0x0b62, 0x0b63,
- 0x0b82, 0x0b82,
- 0x0bbe, 0x0bc2,
- 0x0bc6, 0x0bc8,
- 0x0bca, 0x0bcc,
- 0x0bd7, 0x0bd7,
- 0x0c00, 0x0c03,
- 0x0c3e, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4c,
- 0x0c55, 0x0c56,
- 0x0c62, 0x0c63,
- 0x0c81, 0x0c83,
- 0x0cbe, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccc,
- 0x0cd5, 0x0cd6,
- 0x0ce2, 0x0ce3,
- 0x0d01, 0x0d03,
- 0x0d3e, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4c,
- 0x0d57, 0x0d57,
- 0x0d62, 0x0d63,
- 0x0d82, 0x0d83,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df3,
- 0x0e31, 0x0e31,
- 0x0e34, 0x0e3a,
- 0x0e4d, 0x0e4d,
- 0x0eb1, 0x0eb1,
- 0x0eb4, 0x0eb9,
- 0x0ebb, 0x0ebc,
- 0x0ecd, 0x0ecd,
- 0x0f71, 0x0f81,
- 0x0f8d, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x102b, 0x1036,
- 0x1038, 0x1038,
- 0x103b, 0x103e,
- 0x1056, 0x1059,
- 0x105e, 0x1060,
- 0x1062, 0x1062,
- 0x1067, 0x1068,
- 0x1071, 0x1074,
- 0x1082, 0x1086,
- 0x109c, 0x109d,
- 0x135f, 0x135f,
- 0x1712, 0x1713,
- 0x1732, 0x1733,
- 0x1752, 0x1753,
- 0x1772, 0x1773,
- 0x17b6, 0x17c8,
- 0x1885, 0x1886,
- 0x18a9, 0x18a9,
- 0x1920, 0x192b,
- 0x1930, 0x1938,
- 0x1a17, 0x1a1b,
- 0x1a55, 0x1a5e,
- 0x1a61, 0x1a74,
- 0x1b00, 0x1b04,
- 0x1b35, 0x1b43,
- 0x1b80, 0x1b82,
- 0x1ba1, 0x1ba9,
- 0x1bac, 0x1bad,
- 0x1be7, 0x1bf1,
- 0x1c24, 0x1c35,
- 0x1cf2, 0x1cf3,
- 0x1de7, 0x1df4,
- 0x24b6, 0x24e9,
- 0x2de0, 0x2dff,
- 0xa674, 0xa67b,
- 0xa69e, 0xa69f,
- 0xa823, 0xa827,
- 0xa880, 0xa881,
- 0xa8b4, 0xa8c3,
- 0xa8c5, 0xa8c5,
- 0xa926, 0xa92a,
- 0xa947, 0xa952,
- 0xa980, 0xa983,
- 0xa9b4, 0xa9bf,
- 0xaa29, 0xaa36,
- 0xaa43, 0xaa43,
- 0xaa4c, 0xaa4d,
- 0xaab0, 0xaab0,
- 0xaab2, 0xaab4,
- 0xaab7, 0xaab8,
- 0xaabe, 0xaabe,
- 0xaaeb, 0xaaef,
- 0xaaf5, 0xaaf5,
- 0xabe3, 0xabea,
- 0xfb1e, 0xfb1e,
- 0x10376, 0x1037a,
- 0x10a01, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a0f,
- 0x11000, 0x11002,
- 0x11038, 0x11045,
- 0x11082, 0x11082,
- 0x110b0, 0x110b8,
- 0x11100, 0x11102,
- 0x11127, 0x11132,
- 0x11180, 0x11182,
- 0x111b3, 0x111bf,
- 0x1122c, 0x11234,
- 0x11237, 0x11237,
- 0x1123e, 0x1123e,
- 0x112df, 0x112e8,
- 0x11300, 0x11303,
- 0x1133e, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134c,
- 0x11357, 0x11357,
- 0x11362, 0x11363,
- 0x11435, 0x11441,
- 0x11443, 0x11445,
- 0x114b0, 0x114c1,
- 0x115af, 0x115b5,
- 0x115b8, 0x115be,
- 0x115dc, 0x115dd,
- 0x11630, 0x1163e,
- 0x11640, 0x11640,
- 0x116ab, 0x116b5,
- 0x1171d, 0x1172a,
- 0x11c2f, 0x11c36,
- 0x11c38, 0x11c3e,
- 0x11c92, 0x11ca7,
- 0x11ca9, 0x11cb6,
- 0x16b30, 0x16b36,
- 0x16f51, 0x16f7e,
- 0x1bc9e, 0x1bc9e,
- 0x1e000, 0x1e006,
- 0x1e008, 0x1e018,
- 0x1e01b, 0x1e021,
- 0x1e023, 0x1e024,
- 0x1e026, 0x1e02a,
- 0x1e947, 0x1e947,
- 0x1f130, 0x1f149,
- 0x1f150, 0x1f169,
- 0x1f170, 0x1f189,
-}; /* CR_Other_Alphabetic */
-
-/* 'Ideographic': Binary Property */
-static const OnigCodePoint CR_Ideographic[] = {
- 14,
- 0x3006, 0x3007,
- 0x3021, 0x3029,
- 0x3038, 0x303a,
- 0x3400, 0x4db5,
- 0x4e00, 0x9fd5,
- 0xf900, 0xfa6d,
- 0xfa70, 0xfad9,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
-}; /* CR_Ideographic */
-
-/* 'Diacritic': Binary Property */
-static const OnigCodePoint CR_Diacritic[] = {
- 152,
- 0x005e, 0x005e,
- 0x0060, 0x0060,
- 0x00a8, 0x00a8,
- 0x00af, 0x00af,
- 0x00b4, 0x00b4,
- 0x00b7, 0x00b8,
- 0x02b0, 0x034e,
- 0x0350, 0x0357,
- 0x035d, 0x0362,
- 0x0374, 0x0375,
- 0x037a, 0x037a,
- 0x0384, 0x0385,
- 0x0483, 0x0487,
- 0x0559, 0x0559,
- 0x0591, 0x05a1,
- 0x05a3, 0x05bd,
- 0x05bf, 0x05bf,
- 0x05c1, 0x05c2,
- 0x05c4, 0x05c4,
- 0x064b, 0x0652,
- 0x0657, 0x0658,
- 0x06df, 0x06e0,
- 0x06e5, 0x06e6,
- 0x06ea, 0x06ec,
- 0x0730, 0x074a,
- 0x07a6, 0x07b0,
- 0x07eb, 0x07f5,
- 0x0818, 0x0819,
- 0x08e3, 0x08fe,
- 0x093c, 0x093c,
- 0x094d, 0x094d,
- 0x0951, 0x0954,
- 0x0971, 0x0971,
- 0x09bc, 0x09bc,
- 0x09cd, 0x09cd,
- 0x0a3c, 0x0a3c,
- 0x0a4d, 0x0a4d,
- 0x0abc, 0x0abc,
- 0x0acd, 0x0acd,
- 0x0b3c, 0x0b3c,
- 0x0b4d, 0x0b4d,
- 0x0bcd, 0x0bcd,
- 0x0c4d, 0x0c4d,
- 0x0cbc, 0x0cbc,
- 0x0ccd, 0x0ccd,
- 0x0d4d, 0x0d4d,
- 0x0dca, 0x0dca,
- 0x0e47, 0x0e4c,
- 0x0e4e, 0x0e4e,
- 0x0ec8, 0x0ecc,
- 0x0f18, 0x0f19,
- 0x0f35, 0x0f35,
- 0x0f37, 0x0f37,
- 0x0f39, 0x0f39,
- 0x0f3e, 0x0f3f,
- 0x0f82, 0x0f84,
- 0x0f86, 0x0f87,
- 0x0fc6, 0x0fc6,
- 0x1037, 0x1037,
- 0x1039, 0x103a,
- 0x1087, 0x108d,
- 0x108f, 0x108f,
- 0x109a, 0x109b,
- 0x17c9, 0x17d3,
- 0x17dd, 0x17dd,
- 0x1939, 0x193b,
- 0x1a75, 0x1a7c,
- 0x1a7f, 0x1a7f,
- 0x1ab0, 0x1abd,
- 0x1b34, 0x1b34,
- 0x1b44, 0x1b44,
- 0x1b6b, 0x1b73,
- 0x1baa, 0x1bab,
- 0x1c36, 0x1c37,
- 0x1c78, 0x1c7d,
- 0x1cd0, 0x1ce8,
- 0x1ced, 0x1ced,
- 0x1cf4, 0x1cf4,
- 0x1cf8, 0x1cf9,
- 0x1d2c, 0x1d6a,
- 0x1dc4, 0x1dcf,
- 0x1df5, 0x1df5,
- 0x1dfd, 0x1dff,
- 0x1fbd, 0x1fbd,
- 0x1fbf, 0x1fc1,
- 0x1fcd, 0x1fcf,
- 0x1fdd, 0x1fdf,
- 0x1fed, 0x1fef,
- 0x1ffd, 0x1ffe,
- 0x2cef, 0x2cf1,
- 0x2e2f, 0x2e2f,
- 0x302a, 0x302f,
- 0x3099, 0x309c,
- 0x30fc, 0x30fc,
- 0xa66f, 0xa66f,
- 0xa67c, 0xa67d,
- 0xa67f, 0xa67f,
- 0xa69c, 0xa69d,
- 0xa6f0, 0xa6f1,
- 0xa717, 0xa721,
- 0xa788, 0xa788,
- 0xa7f8, 0xa7f9,
- 0xa8c4, 0xa8c4,
- 0xa8e0, 0xa8f1,
- 0xa92b, 0xa92e,
- 0xa953, 0xa953,
- 0xa9b3, 0xa9b3,
- 0xa9c0, 0xa9c0,
- 0xa9e5, 0xa9e5,
- 0xaa7b, 0xaa7d,
- 0xaabf, 0xaac2,
- 0xaaf6, 0xaaf6,
- 0xab5b, 0xab5f,
- 0xabec, 0xabed,
- 0xfb1e, 0xfb1e,
- 0xfe20, 0xfe2f,
- 0xff3e, 0xff3e,
- 0xff40, 0xff40,
- 0xff70, 0xff70,
- 0xff9e, 0xff9f,
- 0xffe3, 0xffe3,
- 0x102e0, 0x102e0,
- 0x10ae5, 0x10ae6,
- 0x110b9, 0x110ba,
- 0x11133, 0x11134,
- 0x11173, 0x11173,
- 0x111c0, 0x111c0,
- 0x111ca, 0x111cc,
- 0x11235, 0x11236,
- 0x112e9, 0x112ea,
- 0x1133c, 0x1133c,
- 0x1134d, 0x1134d,
- 0x11366, 0x1136c,
- 0x11370, 0x11374,
- 0x11442, 0x11442,
- 0x11446, 0x11446,
- 0x114c2, 0x114c3,
- 0x115bf, 0x115c0,
- 0x1163f, 0x1163f,
- 0x116b6, 0x116b7,
- 0x1172b, 0x1172b,
- 0x11c3f, 0x11c3f,
- 0x16af0, 0x16af4,
- 0x16f8f, 0x16f9f,
- 0x1d167, 0x1d169,
- 0x1d16d, 0x1d172,
- 0x1d17b, 0x1d182,
- 0x1d185, 0x1d18b,
- 0x1d1aa, 0x1d1ad,
- 0x1e8d0, 0x1e8d6,
- 0x1e944, 0x1e946,
- 0x1e948, 0x1e94a,
-}; /* CR_Diacritic */
-
-/* 'Extender': Binary Property */
-static const OnigCodePoint CR_Extender[] = {
- 28,
- 0x00b7, 0x00b7,
- 0x02d0, 0x02d1,
- 0x0640, 0x0640,
- 0x07fa, 0x07fa,
- 0x0e46, 0x0e46,
- 0x0ec6, 0x0ec6,
- 0x180a, 0x180a,
- 0x1843, 0x1843,
- 0x1aa7, 0x1aa7,
- 0x1c36, 0x1c36,
- 0x1c7b, 0x1c7b,
- 0x3005, 0x3005,
- 0x3031, 0x3035,
- 0x309d, 0x309e,
- 0x30fc, 0x30fe,
- 0xa015, 0xa015,
- 0xa60c, 0xa60c,
- 0xa9cf, 0xa9cf,
- 0xa9e6, 0xa9e6,
- 0xaa70, 0xaa70,
- 0xaadd, 0xaadd,
- 0xaaf3, 0xaaf4,
- 0xff70, 0xff70,
- 0x1135d, 0x1135d,
- 0x115c6, 0x115c8,
- 0x16b42, 0x16b43,
- 0x16fe0, 0x16fe0,
- 0x1e944, 0x1e946,
-}; /* CR_Extender */
-
-/* 'Other_Lowercase': Binary Property */
-static const OnigCodePoint CR_Other_Lowercase[] = {
- 20,
- 0x00aa, 0x00aa,
- 0x00ba, 0x00ba,
- 0x02b0, 0x02b8,
- 0x02c0, 0x02c1,
- 0x02e0, 0x02e4,
- 0x0345, 0x0345,
- 0x037a, 0x037a,
- 0x1d2c, 0x1d6a,
- 0x1d78, 0x1d78,
- 0x1d9b, 0x1dbf,
- 0x2071, 0x2071,
- 0x207f, 0x207f,
- 0x2090, 0x209c,
- 0x2170, 0x217f,
- 0x24d0, 0x24e9,
- 0x2c7c, 0x2c7d,
- 0xa69c, 0xa69d,
- 0xa770, 0xa770,
- 0xa7f8, 0xa7f9,
- 0xab5c, 0xab5f,
-}; /* CR_Other_Lowercase */
-
-/* 'Other_Uppercase': Binary Property */
-static const OnigCodePoint CR_Other_Uppercase[] = {
- 5,
- 0x2160, 0x216f,
- 0x24b6, 0x24cf,
- 0x1f130, 0x1f149,
- 0x1f150, 0x1f169,
- 0x1f170, 0x1f189,
-}; /* CR_Other_Uppercase */
-
-/* 'Noncharacter_Code_Point': Binary Property */
-static const OnigCodePoint CR_Noncharacter_Code_Point[] = {
- 18,
- 0xfdd0, 0xfdef,
- 0xfffe, 0xffff,
- 0x1fffe, 0x1ffff,
- 0x2fffe, 0x2ffff,
- 0x3fffe, 0x3ffff,
- 0x4fffe, 0x4ffff,
- 0x5fffe, 0x5ffff,
- 0x6fffe, 0x6ffff,
- 0x7fffe, 0x7ffff,
- 0x8fffe, 0x8ffff,
- 0x9fffe, 0x9ffff,
- 0xafffe, 0xaffff,
- 0xbfffe, 0xbffff,
- 0xcfffe, 0xcffff,
- 0xdfffe, 0xdffff,
- 0xefffe, 0xeffff,
- 0xffffe, 0xfffff,
- 0x10fffe, 0x10ffff,
-}; /* CR_Noncharacter_Code_Point */
-
-/* 'Other_Grapheme_Extend': Binary Property */
-static const OnigCodePoint CR_Other_Grapheme_Extend[] = {
- 23,
- 0x09be, 0x09be,
- 0x09d7, 0x09d7,
- 0x0b3e, 0x0b3e,
- 0x0b57, 0x0b57,
- 0x0bbe, 0x0bbe,
- 0x0bd7, 0x0bd7,
- 0x0cc2, 0x0cc2,
- 0x0cd5, 0x0cd6,
- 0x0d3e, 0x0d3e,
- 0x0d57, 0x0d57,
- 0x0dcf, 0x0dcf,
- 0x0ddf, 0x0ddf,
- 0x200c, 0x200c,
- 0x302e, 0x302f,
- 0xff9e, 0xff9f,
- 0x1133e, 0x1133e,
- 0x11357, 0x11357,
- 0x114b0, 0x114b0,
- 0x114bd, 0x114bd,
- 0x115af, 0x115af,
- 0x1d165, 0x1d165,
- 0x1d16e, 0x1d172,
- 0xe0020, 0xe007f,
-}; /* CR_Other_Grapheme_Extend */
-
-/* 'IDS_Binary_Operator': Binary Property */
-static const OnigCodePoint CR_IDS_Binary_Operator[] = {
- 2,
- 0x2ff0, 0x2ff1,
- 0x2ff4, 0x2ffb,
-}; /* CR_IDS_Binary_Operator */
-
-/* 'IDS_Trinary_Operator': Binary Property */
-static const OnigCodePoint CR_IDS_Trinary_Operator[] = {
- 1,
- 0x2ff2, 0x2ff3,
-}; /* CR_IDS_Trinary_Operator */
-
-/* 'Radical': Binary Property */
-static const OnigCodePoint CR_Radical[] = {
- 3,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
-}; /* CR_Radical */
-
-/* 'Unified_Ideograph': Binary Property */
-static const OnigCodePoint CR_Unified_Ideograph[] = {
- 13,
- 0x3400, 0x4db5,
- 0x4e00, 0x9fd5,
- 0xfa0e, 0xfa0f,
- 0xfa11, 0xfa11,
- 0xfa13, 0xfa14,
- 0xfa1f, 0xfa1f,
- 0xfa21, 0xfa21,
- 0xfa23, 0xfa24,
- 0xfa27, 0xfa29,
- 0x20000, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
-}; /* CR_Unified_Ideograph */
-
-/* 'Other_Default_Ignorable_Code_Point': Binary Property */
-static const OnigCodePoint CR_Other_Default_Ignorable_Code_Point[] = {
- 11,
- 0x034f, 0x034f,
- 0x115f, 0x1160,
- 0x17b4, 0x17b5,
- 0x2065, 0x2065,
- 0x3164, 0x3164,
- 0xffa0, 0xffa0,
- 0xfff0, 0xfff8,
- 0xe0000, 0xe0000,
- 0xe0002, 0xe001f,
- 0xe0080, 0xe00ff,
- 0xe01f0, 0xe0fff,
-}; /* CR_Other_Default_Ignorable_Code_Point */
-
-/* 'Deprecated': Binary Property */
-static const OnigCodePoint CR_Deprecated[] = {
- 8,
- 0x0149, 0x0149,
- 0x0673, 0x0673,
- 0x0f77, 0x0f77,
- 0x0f79, 0x0f79,
- 0x17a3, 0x17a4,
- 0x206a, 0x206f,
- 0x2329, 0x232a,
- 0xe0001, 0xe0001,
-}; /* CR_Deprecated */
-
-/* 'Soft_Dotted': Binary Property */
-static const OnigCodePoint CR_Soft_Dotted[] = {
- 31,
- 0x0069, 0x006a,
- 0x012f, 0x012f,
- 0x0249, 0x0249,
- 0x0268, 0x0268,
- 0x029d, 0x029d,
- 0x02b2, 0x02b2,
- 0x03f3, 0x03f3,
- 0x0456, 0x0456,
- 0x0458, 0x0458,
- 0x1d62, 0x1d62,
- 0x1d96, 0x1d96,
- 0x1da4, 0x1da4,
- 0x1da8, 0x1da8,
- 0x1e2d, 0x1e2d,
- 0x1ecb, 0x1ecb,
- 0x2071, 0x2071,
- 0x2148, 0x2149,
- 0x2c7c, 0x2c7c,
- 0x1d422, 0x1d423,
- 0x1d456, 0x1d457,
- 0x1d48a, 0x1d48b,
- 0x1d4be, 0x1d4bf,
- 0x1d4f2, 0x1d4f3,
- 0x1d526, 0x1d527,
- 0x1d55a, 0x1d55b,
- 0x1d58e, 0x1d58f,
- 0x1d5c2, 0x1d5c3,
- 0x1d5f6, 0x1d5f7,
- 0x1d62a, 0x1d62b,
- 0x1d65e, 0x1d65f,
- 0x1d692, 0x1d693,
-}; /* CR_Soft_Dotted */
-
-/* 'Logical_Order_Exception': Binary Property */
-static const OnigCodePoint CR_Logical_Order_Exception[] = {
- 7,
- 0x0e40, 0x0e44,
- 0x0ec0, 0x0ec4,
- 0x19b5, 0x19b7,
- 0x19ba, 0x19ba,
- 0xaab5, 0xaab6,
- 0xaab9, 0xaab9,
- 0xaabb, 0xaabc,
-}; /* CR_Logical_Order_Exception */
-
-/* 'Other_ID_Start': Binary Property */
-static const OnigCodePoint CR_Other_ID_Start[] = {
- 4,
- 0x1885, 0x1886,
- 0x2118, 0x2118,
- 0x212e, 0x212e,
- 0x309b, 0x309c,
-}; /* CR_Other_ID_Start */
-
-/* 'Other_ID_Continue': Binary Property */
-static const OnigCodePoint CR_Other_ID_Continue[] = {
- 4,
- 0x00b7, 0x00b7,
- 0x0387, 0x0387,
- 0x1369, 0x1371,
- 0x19da, 0x19da,
-}; /* CR_Other_ID_Continue */
-
-/* 'Sentence_Terminal': Binary Property */
-static const OnigCodePoint CR_Sentence_Terminal[] = {
- 66,
- 0x0021, 0x0021,
- 0x002e, 0x002e,
- 0x003f, 0x003f,
- 0x0589, 0x0589,
- 0x061f, 0x061f,
- 0x06d4, 0x06d4,
- 0x0700, 0x0702,
- 0x07f9, 0x07f9,
- 0x0964, 0x0965,
- 0x104a, 0x104b,
- 0x1362, 0x1362,
- 0x1367, 0x1368,
- 0x166e, 0x166e,
- 0x1735, 0x1736,
- 0x1803, 0x1803,
- 0x1809, 0x1809,
- 0x1944, 0x1945,
- 0x1aa8, 0x1aab,
- 0x1b5a, 0x1b5b,
- 0x1b5e, 0x1b5f,
- 0x1c3b, 0x1c3c,
- 0x1c7e, 0x1c7f,
- 0x203c, 0x203d,
- 0x2047, 0x2049,
- 0x2e2e, 0x2e2e,
- 0x2e3c, 0x2e3c,
- 0x3002, 0x3002,
- 0xa4ff, 0xa4ff,
- 0xa60e, 0xa60f,
- 0xa6f3, 0xa6f3,
- 0xa6f7, 0xa6f7,
- 0xa876, 0xa877,
- 0xa8ce, 0xa8cf,
- 0xa92f, 0xa92f,
- 0xa9c8, 0xa9c9,
- 0xaa5d, 0xaa5f,
- 0xaaf0, 0xaaf1,
- 0xabeb, 0xabeb,
- 0xfe52, 0xfe52,
- 0xfe56, 0xfe57,
- 0xff01, 0xff01,
- 0xff0e, 0xff0e,
- 0xff1f, 0xff1f,
- 0xff61, 0xff61,
- 0x10a56, 0x10a57,
- 0x11047, 0x11048,
- 0x110be, 0x110c1,
- 0x11141, 0x11143,
- 0x111c5, 0x111c6,
- 0x111cd, 0x111cd,
- 0x111de, 0x111df,
- 0x11238, 0x11239,
- 0x1123b, 0x1123c,
- 0x112a9, 0x112a9,
- 0x1144b, 0x1144c,
- 0x115c2, 0x115c3,
- 0x115c9, 0x115d7,
- 0x11641, 0x11642,
- 0x1173c, 0x1173e,
- 0x11c41, 0x11c42,
- 0x16a6e, 0x16a6f,
- 0x16af5, 0x16af5,
- 0x16b37, 0x16b38,
- 0x16b44, 0x16b44,
- 0x1bc9f, 0x1bc9f,
- 0x1da88, 0x1da88,
-}; /* CR_Sentence_Terminal */
-
-/* 'Variation_Selector': Binary Property */
-static const OnigCodePoint CR_Variation_Selector[] = {
- 3,
- 0x180b, 0x180d,
- 0xfe00, 0xfe0f,
- 0xe0100, 0xe01ef,
-}; /* CR_Variation_Selector */
-
-/* 'Pattern_White_Space': Binary Property */
-static const OnigCodePoint CR_Pattern_White_Space[] = {
- 5,
- 0x0009, 0x000d,
- 0x0020, 0x0020,
- 0x0085, 0x0085,
- 0x200e, 0x200f,
- 0x2028, 0x2029,
-}; /* CR_Pattern_White_Space */
-
-/* 'Pattern_Syntax': Binary Property */
-static const OnigCodePoint CR_Pattern_Syntax[] = {
- 28,
- 0x0021, 0x002f,
- 0x003a, 0x0040,
- 0x005b, 0x005e,
- 0x0060, 0x0060,
- 0x007b, 0x007e,
- 0x00a1, 0x00a7,
- 0x00a9, 0x00a9,
- 0x00ab, 0x00ac,
- 0x00ae, 0x00ae,
- 0x00b0, 0x00b1,
- 0x00b6, 0x00b6,
- 0x00bb, 0x00bb,
- 0x00bf, 0x00bf,
- 0x00d7, 0x00d7,
- 0x00f7, 0x00f7,
- 0x2010, 0x2027,
- 0x2030, 0x203e,
- 0x2041, 0x2053,
- 0x2055, 0x205e,
- 0x2190, 0x245f,
- 0x2500, 0x2775,
- 0x2794, 0x2bff,
- 0x2e00, 0x2e7f,
- 0x3001, 0x3003,
- 0x3008, 0x3020,
- 0x3030, 0x3030,
- 0xfd3e, 0xfd3f,
- 0xfe45, 0xfe46,
-}; /* CR_Pattern_Syntax */
-
-/* 'Prepended_Concatenation_Mark': Binary Property */
-static const OnigCodePoint CR_Prepended_Concatenation_Mark[] = {
- 5,
- 0x0600, 0x0605,
- 0x06dd, 0x06dd,
- 0x070f, 0x070f,
- 0x08e2, 0x08e2,
- 0x110bd, 0x110bd,
-}; /* CR_Prepended_Concatenation_Mark */
-
-/* 'Unknown': Script */
-static const OnigCodePoint CR_Unknown[] = {
- 636,
- 0x0378, 0x0379,
- 0x0380, 0x0383,
- 0x038b, 0x038b,
- 0x038d, 0x038d,
- 0x03a2, 0x03a2,
- 0x0530, 0x0530,
- 0x0557, 0x0558,
- 0x0560, 0x0560,
- 0x0588, 0x0588,
- 0x058b, 0x058c,
- 0x0590, 0x0590,
- 0x05c8, 0x05cf,
- 0x05eb, 0x05ef,
- 0x05f5, 0x05ff,
- 0x061d, 0x061d,
- 0x070e, 0x070e,
- 0x074b, 0x074c,
- 0x07b2, 0x07bf,
- 0x07fb, 0x07ff,
- 0x082e, 0x082f,
- 0x083f, 0x083f,
- 0x085c, 0x085d,
- 0x085f, 0x089f,
- 0x08b5, 0x08b5,
- 0x08be, 0x08d3,
- 0x0984, 0x0984,
- 0x098d, 0x098e,
- 0x0991, 0x0992,
- 0x09a9, 0x09a9,
- 0x09b1, 0x09b1,
- 0x09b3, 0x09b5,
- 0x09ba, 0x09bb,
- 0x09c5, 0x09c6,
- 0x09c9, 0x09ca,
- 0x09cf, 0x09d6,
- 0x09d8, 0x09db,
- 0x09de, 0x09de,
- 0x09e4, 0x09e5,
- 0x09fc, 0x0a00,
- 0x0a04, 0x0a04,
- 0x0a0b, 0x0a0e,
- 0x0a11, 0x0a12,
- 0x0a29, 0x0a29,
- 0x0a31, 0x0a31,
- 0x0a34, 0x0a34,
- 0x0a37, 0x0a37,
- 0x0a3a, 0x0a3b,
- 0x0a3d, 0x0a3d,
- 0x0a43, 0x0a46,
- 0x0a49, 0x0a4a,
- 0x0a4e, 0x0a50,
- 0x0a52, 0x0a58,
- 0x0a5d, 0x0a5d,
- 0x0a5f, 0x0a65,
- 0x0a76, 0x0a80,
- 0x0a84, 0x0a84,
- 0x0a8e, 0x0a8e,
- 0x0a92, 0x0a92,
- 0x0aa9, 0x0aa9,
- 0x0ab1, 0x0ab1,
- 0x0ab4, 0x0ab4,
- 0x0aba, 0x0abb,
- 0x0ac6, 0x0ac6,
- 0x0aca, 0x0aca,
- 0x0ace, 0x0acf,
- 0x0ad1, 0x0adf,
- 0x0ae4, 0x0ae5,
- 0x0af2, 0x0af8,
- 0x0afa, 0x0b00,
- 0x0b04, 0x0b04,
- 0x0b0d, 0x0b0e,
- 0x0b11, 0x0b12,
- 0x0b29, 0x0b29,
- 0x0b31, 0x0b31,
- 0x0b34, 0x0b34,
- 0x0b3a, 0x0b3b,
- 0x0b45, 0x0b46,
- 0x0b49, 0x0b4a,
- 0x0b4e, 0x0b55,
- 0x0b58, 0x0b5b,
- 0x0b5e, 0x0b5e,
- 0x0b64, 0x0b65,
- 0x0b78, 0x0b81,
- 0x0b84, 0x0b84,
- 0x0b8b, 0x0b8d,
- 0x0b91, 0x0b91,
- 0x0b96, 0x0b98,
- 0x0b9b, 0x0b9b,
- 0x0b9d, 0x0b9d,
- 0x0ba0, 0x0ba2,
- 0x0ba5, 0x0ba7,
- 0x0bab, 0x0bad,
- 0x0bba, 0x0bbd,
- 0x0bc3, 0x0bc5,
- 0x0bc9, 0x0bc9,
- 0x0bce, 0x0bcf,
- 0x0bd1, 0x0bd6,
- 0x0bd8, 0x0be5,
- 0x0bfb, 0x0bff,
- 0x0c04, 0x0c04,
- 0x0c0d, 0x0c0d,
- 0x0c11, 0x0c11,
- 0x0c29, 0x0c29,
- 0x0c3a, 0x0c3c,
- 0x0c45, 0x0c45,
- 0x0c49, 0x0c49,
- 0x0c4e, 0x0c54,
- 0x0c57, 0x0c57,
- 0x0c5b, 0x0c5f,
- 0x0c64, 0x0c65,
- 0x0c70, 0x0c77,
- 0x0c84, 0x0c84,
- 0x0c8d, 0x0c8d,
- 0x0c91, 0x0c91,
- 0x0ca9, 0x0ca9,
- 0x0cb4, 0x0cb4,
- 0x0cba, 0x0cbb,
- 0x0cc5, 0x0cc5,
- 0x0cc9, 0x0cc9,
- 0x0cce, 0x0cd4,
- 0x0cd7, 0x0cdd,
- 0x0cdf, 0x0cdf,
- 0x0ce4, 0x0ce5,
- 0x0cf0, 0x0cf0,
- 0x0cf3, 0x0d00,
- 0x0d04, 0x0d04,
- 0x0d0d, 0x0d0d,
- 0x0d11, 0x0d11,
- 0x0d3b, 0x0d3c,
- 0x0d45, 0x0d45,
- 0x0d49, 0x0d49,
- 0x0d50, 0x0d53,
- 0x0d64, 0x0d65,
- 0x0d80, 0x0d81,
- 0x0d84, 0x0d84,
- 0x0d97, 0x0d99,
- 0x0db2, 0x0db2,
- 0x0dbc, 0x0dbc,
- 0x0dbe, 0x0dbf,
- 0x0dc7, 0x0dc9,
- 0x0dcb, 0x0dce,
- 0x0dd5, 0x0dd5,
- 0x0dd7, 0x0dd7,
- 0x0de0, 0x0de5,
- 0x0df0, 0x0df1,
- 0x0df5, 0x0e00,
- 0x0e3b, 0x0e3e,
- 0x0e5c, 0x0e80,
- 0x0e83, 0x0e83,
- 0x0e85, 0x0e86,
- 0x0e89, 0x0e89,
- 0x0e8b, 0x0e8c,
- 0x0e8e, 0x0e93,
- 0x0e98, 0x0e98,
- 0x0ea0, 0x0ea0,
- 0x0ea4, 0x0ea4,
- 0x0ea6, 0x0ea6,
- 0x0ea8, 0x0ea9,
- 0x0eac, 0x0eac,
- 0x0eba, 0x0eba,
- 0x0ebe, 0x0ebf,
- 0x0ec5, 0x0ec5,
- 0x0ec7, 0x0ec7,
- 0x0ece, 0x0ecf,
- 0x0eda, 0x0edb,
- 0x0ee0, 0x0eff,
- 0x0f48, 0x0f48,
- 0x0f6d, 0x0f70,
- 0x0f98, 0x0f98,
- 0x0fbd, 0x0fbd,
- 0x0fcd, 0x0fcd,
- 0x0fdb, 0x0fff,
- 0x10c6, 0x10c6,
- 0x10c8, 0x10cc,
- 0x10ce, 0x10cf,
- 0x1249, 0x1249,
- 0x124e, 0x124f,
- 0x1257, 0x1257,
- 0x1259, 0x1259,
- 0x125e, 0x125f,
- 0x1289, 0x1289,
- 0x128e, 0x128f,
- 0x12b1, 0x12b1,
- 0x12b6, 0x12b7,
- 0x12bf, 0x12bf,
- 0x12c1, 0x12c1,
- 0x12c6, 0x12c7,
- 0x12d7, 0x12d7,
- 0x1311, 0x1311,
- 0x1316, 0x1317,
- 0x135b, 0x135c,
- 0x137d, 0x137f,
- 0x139a, 0x139f,
- 0x13f6, 0x13f7,
- 0x13fe, 0x13ff,
- 0x169d, 0x169f,
- 0x16f9, 0x16ff,
- 0x170d, 0x170d,
- 0x1715, 0x171f,
- 0x1737, 0x173f,
- 0x1754, 0x175f,
- 0x176d, 0x176d,
- 0x1771, 0x1771,
- 0x1774, 0x177f,
- 0x17de, 0x17df,
- 0x17ea, 0x17ef,
- 0x17fa, 0x17ff,
- 0x180f, 0x180f,
- 0x181a, 0x181f,
- 0x1878, 0x187f,
- 0x18ab, 0x18af,
- 0x18f6, 0x18ff,
- 0x191f, 0x191f,
- 0x192c, 0x192f,
- 0x193c, 0x193f,
- 0x1941, 0x1943,
- 0x196e, 0x196f,
- 0x1975, 0x197f,
- 0x19ac, 0x19af,
- 0x19ca, 0x19cf,
- 0x19db, 0x19dd,
- 0x1a1c, 0x1a1d,
- 0x1a5f, 0x1a5f,
- 0x1a7d, 0x1a7e,
- 0x1a8a, 0x1a8f,
- 0x1a9a, 0x1a9f,
- 0x1aae, 0x1aaf,
- 0x1abf, 0x1aff,
- 0x1b4c, 0x1b4f,
- 0x1b7d, 0x1b7f,
- 0x1bf4, 0x1bfb,
- 0x1c38, 0x1c3a,
- 0x1c4a, 0x1c4c,
- 0x1c89, 0x1cbf,
- 0x1cc8, 0x1ccf,
- 0x1cf7, 0x1cf7,
- 0x1cfa, 0x1cff,
- 0x1df6, 0x1dfa,
- 0x1f16, 0x1f17,
- 0x1f1e, 0x1f1f,
- 0x1f46, 0x1f47,
- 0x1f4e, 0x1f4f,
- 0x1f58, 0x1f58,
- 0x1f5a, 0x1f5a,
- 0x1f5c, 0x1f5c,
- 0x1f5e, 0x1f5e,
- 0x1f7e, 0x1f7f,
- 0x1fb5, 0x1fb5,
- 0x1fc5, 0x1fc5,
- 0x1fd4, 0x1fd5,
- 0x1fdc, 0x1fdc,
- 0x1ff0, 0x1ff1,
- 0x1ff5, 0x1ff5,
- 0x1fff, 0x1fff,
- 0x2065, 0x2065,
- 0x2072, 0x2073,
- 0x208f, 0x208f,
- 0x209d, 0x209f,
- 0x20bf, 0x20cf,
- 0x20f1, 0x20ff,
- 0x218c, 0x218f,
- 0x23ff, 0x23ff,
- 0x2427, 0x243f,
- 0x244b, 0x245f,
- 0x2b74, 0x2b75,
- 0x2b96, 0x2b97,
- 0x2bba, 0x2bbc,
- 0x2bc9, 0x2bc9,
- 0x2bd2, 0x2beb,
- 0x2bf0, 0x2bff,
- 0x2c2f, 0x2c2f,
- 0x2c5f, 0x2c5f,
- 0x2cf4, 0x2cf8,
- 0x2d26, 0x2d26,
- 0x2d28, 0x2d2c,
- 0x2d2e, 0x2d2f,
- 0x2d68, 0x2d6e,
- 0x2d71, 0x2d7e,
- 0x2d97, 0x2d9f,
- 0x2da7, 0x2da7,
- 0x2daf, 0x2daf,
- 0x2db7, 0x2db7,
- 0x2dbf, 0x2dbf,
- 0x2dc7, 0x2dc7,
- 0x2dcf, 0x2dcf,
- 0x2dd7, 0x2dd7,
- 0x2ddf, 0x2ddf,
- 0x2e45, 0x2e7f,
- 0x2e9a, 0x2e9a,
- 0x2ef4, 0x2eff,
- 0x2fd6, 0x2fef,
- 0x2ffc, 0x2fff,
- 0x3040, 0x3040,
- 0x3097, 0x3098,
- 0x3100, 0x3104,
- 0x312e, 0x3130,
- 0x318f, 0x318f,
- 0x31bb, 0x31bf,
- 0x31e4, 0x31ef,
- 0x321f, 0x321f,
- 0x32ff, 0x32ff,
- 0x4db6, 0x4dbf,
- 0x9fd6, 0x9fff,
- 0xa48d, 0xa48f,
- 0xa4c7, 0xa4cf,
- 0xa62c, 0xa63f,
- 0xa6f8, 0xa6ff,
- 0xa7af, 0xa7af,
- 0xa7b8, 0xa7f6,
- 0xa82c, 0xa82f,
- 0xa83a, 0xa83f,
- 0xa878, 0xa87f,
- 0xa8c6, 0xa8cd,
- 0xa8da, 0xa8df,
- 0xa8fe, 0xa8ff,
- 0xa954, 0xa95e,
- 0xa97d, 0xa97f,
- 0xa9ce, 0xa9ce,
- 0xa9da, 0xa9dd,
- 0xa9ff, 0xa9ff,
- 0xaa37, 0xaa3f,
- 0xaa4e, 0xaa4f,
- 0xaa5a, 0xaa5b,
- 0xaac3, 0xaada,
- 0xaaf7, 0xab00,
- 0xab07, 0xab08,
- 0xab0f, 0xab10,
- 0xab17, 0xab1f,
- 0xab27, 0xab27,
- 0xab2f, 0xab2f,
- 0xab66, 0xab6f,
- 0xabee, 0xabef,
- 0xabfa, 0xabff,
- 0xd7a4, 0xd7af,
- 0xd7c7, 0xd7ca,
- 0xd7fc, 0xf8ff,
- 0xfa6e, 0xfa6f,
- 0xfada, 0xfaff,
- 0xfb07, 0xfb12,
- 0xfb18, 0xfb1c,
- 0xfb37, 0xfb37,
- 0xfb3d, 0xfb3d,
- 0xfb3f, 0xfb3f,
- 0xfb42, 0xfb42,
- 0xfb45, 0xfb45,
- 0xfbc2, 0xfbd2,
- 0xfd40, 0xfd4f,
- 0xfd90, 0xfd91,
- 0xfdc8, 0xfdef,
- 0xfdfe, 0xfdff,
- 0xfe1a, 0xfe1f,
- 0xfe53, 0xfe53,
- 0xfe67, 0xfe67,
- 0xfe6c, 0xfe6f,
- 0xfe75, 0xfe75,
- 0xfefd, 0xfefe,
- 0xff00, 0xff00,
- 0xffbf, 0xffc1,
- 0xffc8, 0xffc9,
- 0xffd0, 0xffd1,
- 0xffd8, 0xffd9,
- 0xffdd, 0xffdf,
- 0xffe7, 0xffe7,
- 0xffef, 0xfff8,
- 0xfffe, 0xffff,
- 0x1000c, 0x1000c,
- 0x10027, 0x10027,
- 0x1003b, 0x1003b,
- 0x1003e, 0x1003e,
- 0x1004e, 0x1004f,
- 0x1005e, 0x1007f,
- 0x100fb, 0x100ff,
- 0x10103, 0x10106,
- 0x10134, 0x10136,
- 0x1018f, 0x1018f,
- 0x1019c, 0x1019f,
- 0x101a1, 0x101cf,
- 0x101fe, 0x1027f,
- 0x1029d, 0x1029f,
- 0x102d1, 0x102df,
- 0x102fc, 0x102ff,
- 0x10324, 0x1032f,
- 0x1034b, 0x1034f,
- 0x1037b, 0x1037f,
- 0x1039e, 0x1039e,
- 0x103c4, 0x103c7,
- 0x103d6, 0x103ff,
- 0x1049e, 0x1049f,
- 0x104aa, 0x104af,
- 0x104d4, 0x104d7,
- 0x104fc, 0x104ff,
- 0x10528, 0x1052f,
- 0x10564, 0x1056e,
- 0x10570, 0x105ff,
- 0x10737, 0x1073f,
- 0x10756, 0x1075f,
- 0x10768, 0x107ff,
- 0x10806, 0x10807,
- 0x10809, 0x10809,
- 0x10836, 0x10836,
- 0x10839, 0x1083b,
- 0x1083d, 0x1083e,
- 0x10856, 0x10856,
- 0x1089f, 0x108a6,
- 0x108b0, 0x108df,
- 0x108f3, 0x108f3,
- 0x108f6, 0x108fa,
- 0x1091c, 0x1091e,
- 0x1093a, 0x1093e,
- 0x10940, 0x1097f,
- 0x109b8, 0x109bb,
- 0x109d0, 0x109d1,
- 0x10a04, 0x10a04,
- 0x10a07, 0x10a0b,
- 0x10a14, 0x10a14,
- 0x10a18, 0x10a18,
- 0x10a34, 0x10a37,
- 0x10a3b, 0x10a3e,
- 0x10a48, 0x10a4f,
- 0x10a59, 0x10a5f,
- 0x10aa0, 0x10abf,
- 0x10ae7, 0x10aea,
- 0x10af7, 0x10aff,
- 0x10b36, 0x10b38,
- 0x10b56, 0x10b57,
- 0x10b73, 0x10b77,
- 0x10b92, 0x10b98,
- 0x10b9d, 0x10ba8,
- 0x10bb0, 0x10bff,
- 0x10c49, 0x10c7f,
- 0x10cb3, 0x10cbf,
- 0x10cf3, 0x10cf9,
- 0x10d00, 0x10e5f,
- 0x10e7f, 0x10fff,
- 0x1104e, 0x11051,
- 0x11070, 0x1107e,
- 0x110c2, 0x110cf,
- 0x110e9, 0x110ef,
- 0x110fa, 0x110ff,
- 0x11135, 0x11135,
- 0x11144, 0x1114f,
- 0x11177, 0x1117f,
- 0x111ce, 0x111cf,
- 0x111e0, 0x111e0,
- 0x111f5, 0x111ff,
- 0x11212, 0x11212,
- 0x1123f, 0x1127f,
- 0x11287, 0x11287,
- 0x11289, 0x11289,
- 0x1128e, 0x1128e,
- 0x1129e, 0x1129e,
- 0x112aa, 0x112af,
- 0x112eb, 0x112ef,
- 0x112fa, 0x112ff,
- 0x11304, 0x11304,
- 0x1130d, 0x1130e,
- 0x11311, 0x11312,
- 0x11329, 0x11329,
- 0x11331, 0x11331,
- 0x11334, 0x11334,
- 0x1133a, 0x1133b,
- 0x11345, 0x11346,
- 0x11349, 0x1134a,
- 0x1134e, 0x1134f,
- 0x11351, 0x11356,
- 0x11358, 0x1135c,
- 0x11364, 0x11365,
- 0x1136d, 0x1136f,
- 0x11375, 0x113ff,
- 0x1145a, 0x1145a,
- 0x1145c, 0x1145c,
- 0x1145e, 0x1147f,
- 0x114c8, 0x114cf,
- 0x114da, 0x1157f,
- 0x115b6, 0x115b7,
- 0x115de, 0x115ff,
- 0x11645, 0x1164f,
- 0x1165a, 0x1165f,
- 0x1166d, 0x1167f,
- 0x116b8, 0x116bf,
- 0x116ca, 0x116ff,
- 0x1171a, 0x1171c,
- 0x1172c, 0x1172f,
- 0x11740, 0x1189f,
- 0x118f3, 0x118fe,
- 0x11900, 0x11abf,
- 0x11af9, 0x11bff,
- 0x11c09, 0x11c09,
- 0x11c37, 0x11c37,
- 0x11c46, 0x11c4f,
- 0x11c6d, 0x11c6f,
- 0x11c90, 0x11c91,
- 0x11ca8, 0x11ca8,
- 0x11cb7, 0x11fff,
- 0x1239a, 0x123ff,
- 0x1246f, 0x1246f,
- 0x12475, 0x1247f,
- 0x12544, 0x12fff,
- 0x1342f, 0x143ff,
- 0x14647, 0x167ff,
- 0x16a39, 0x16a3f,
- 0x16a5f, 0x16a5f,
- 0x16a6a, 0x16a6d,
- 0x16a70, 0x16acf,
- 0x16aee, 0x16aef,
- 0x16af6, 0x16aff,
- 0x16b46, 0x16b4f,
- 0x16b5a, 0x16b5a,
- 0x16b62, 0x16b62,
- 0x16b78, 0x16b7c,
- 0x16b90, 0x16eff,
- 0x16f45, 0x16f4f,
- 0x16f7f, 0x16f8e,
- 0x16fa0, 0x16fdf,
- 0x16fe1, 0x16fff,
- 0x187ed, 0x187ff,
- 0x18af3, 0x1afff,
- 0x1b002, 0x1bbff,
- 0x1bc6b, 0x1bc6f,
- 0x1bc7d, 0x1bc7f,
- 0x1bc89, 0x1bc8f,
- 0x1bc9a, 0x1bc9b,
- 0x1bca4, 0x1cfff,
- 0x1d0f6, 0x1d0ff,
- 0x1d127, 0x1d128,
- 0x1d1e9, 0x1d1ff,
- 0x1d246, 0x1d2ff,
- 0x1d357, 0x1d35f,
- 0x1d372, 0x1d3ff,
- 0x1d455, 0x1d455,
- 0x1d49d, 0x1d49d,
- 0x1d4a0, 0x1d4a1,
- 0x1d4a3, 0x1d4a4,
- 0x1d4a7, 0x1d4a8,
- 0x1d4ad, 0x1d4ad,
- 0x1d4ba, 0x1d4ba,
- 0x1d4bc, 0x1d4bc,
- 0x1d4c4, 0x1d4c4,
- 0x1d506, 0x1d506,
- 0x1d50b, 0x1d50c,
- 0x1d515, 0x1d515,
- 0x1d51d, 0x1d51d,
- 0x1d53a, 0x1d53a,
- 0x1d53f, 0x1d53f,
- 0x1d545, 0x1d545,
- 0x1d547, 0x1d549,
- 0x1d551, 0x1d551,
- 0x1d6a6, 0x1d6a7,
- 0x1d7cc, 0x1d7cd,
- 0x1da8c, 0x1da9a,
- 0x1daa0, 0x1daa0,
- 0x1dab0, 0x1dfff,
- 0x1e007, 0x1e007,
- 0x1e019, 0x1e01a,
- 0x1e022, 0x1e022,
- 0x1e025, 0x1e025,
- 0x1e02b, 0x1e7ff,
- 0x1e8c5, 0x1e8c6,
- 0x1e8d7, 0x1e8ff,
- 0x1e94b, 0x1e94f,
- 0x1e95a, 0x1e95d,
- 0x1e960, 0x1edff,
- 0x1ee04, 0x1ee04,
- 0x1ee20, 0x1ee20,
- 0x1ee23, 0x1ee23,
- 0x1ee25, 0x1ee26,
- 0x1ee28, 0x1ee28,
- 0x1ee33, 0x1ee33,
- 0x1ee38, 0x1ee38,
- 0x1ee3a, 0x1ee3a,
- 0x1ee3c, 0x1ee41,
- 0x1ee43, 0x1ee46,
- 0x1ee48, 0x1ee48,
- 0x1ee4a, 0x1ee4a,
- 0x1ee4c, 0x1ee4c,
- 0x1ee50, 0x1ee50,
- 0x1ee53, 0x1ee53,
- 0x1ee55, 0x1ee56,
- 0x1ee58, 0x1ee58,
- 0x1ee5a, 0x1ee5a,
- 0x1ee5c, 0x1ee5c,
- 0x1ee5e, 0x1ee5e,
- 0x1ee60, 0x1ee60,
- 0x1ee63, 0x1ee63,
- 0x1ee65, 0x1ee66,
- 0x1ee6b, 0x1ee6b,
- 0x1ee73, 0x1ee73,
- 0x1ee78, 0x1ee78,
- 0x1ee7d, 0x1ee7d,
- 0x1ee7f, 0x1ee7f,
- 0x1ee8a, 0x1ee8a,
- 0x1ee9c, 0x1eea0,
- 0x1eea4, 0x1eea4,
- 0x1eeaa, 0x1eeaa,
- 0x1eebc, 0x1eeef,
- 0x1eef2, 0x1efff,
- 0x1f02c, 0x1f02f,
- 0x1f094, 0x1f09f,
- 0x1f0af, 0x1f0b0,
- 0x1f0c0, 0x1f0c0,
- 0x1f0d0, 0x1f0d0,
- 0x1f0f6, 0x1f0ff,
- 0x1f10d, 0x1f10f,
- 0x1f12f, 0x1f12f,
- 0x1f16c, 0x1f16f,
- 0x1f1ad, 0x1f1e5,
- 0x1f203, 0x1f20f,
- 0x1f23c, 0x1f23f,
- 0x1f249, 0x1f24f,
- 0x1f252, 0x1f2ff,
- 0x1f6d3, 0x1f6df,
- 0x1f6ed, 0x1f6ef,
- 0x1f6f7, 0x1f6ff,
- 0x1f774, 0x1f77f,
- 0x1f7d5, 0x1f7ff,
- 0x1f80c, 0x1f80f,
- 0x1f848, 0x1f84f,
- 0x1f85a, 0x1f85f,
- 0x1f888, 0x1f88f,
- 0x1f8ae, 0x1f90f,
- 0x1f91f, 0x1f91f,
- 0x1f928, 0x1f92f,
- 0x1f931, 0x1f932,
- 0x1f93f, 0x1f93f,
- 0x1f94c, 0x1f94f,
- 0x1f95f, 0x1f97f,
- 0x1f992, 0x1f9bf,
- 0x1f9c1, 0x1ffff,
- 0x2a6d7, 0x2a6ff,
- 0x2b735, 0x2b73f,
- 0x2b81e, 0x2b81f,
- 0x2cea2, 0x2f7ff,
- 0x2fa1e, 0xe0000,
- 0xe0002, 0xe001f,
- 0xe0080, 0xe00ff,
- 0xe01f0, 0x10ffff,
-}; /* CR_Unknown */
-
-#ifdef USE_UNICODE_AGE_PROPERTIES
-/* 'Age_1_1': Derived Age 1.1 */
-static const OnigCodePoint CR_Age_1_1[] = {
- 288,
- 0x0000, 0x01f5,
- 0x01fa, 0x0217,
- 0x0250, 0x02a8,
- 0x02b0, 0x02de,
- 0x02e0, 0x02e9,
- 0x0300, 0x0345,
- 0x0360, 0x0361,
- 0x0374, 0x0375,
- 0x037a, 0x037a,
- 0x037e, 0x037e,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03ce,
- 0x03d0, 0x03d6,
- 0x03da, 0x03da,
- 0x03dc, 0x03dc,
- 0x03de, 0x03de,
- 0x03e0, 0x03e0,
- 0x03e2, 0x03f3,
- 0x0401, 0x040c,
- 0x040e, 0x044f,
- 0x0451, 0x045c,
- 0x045e, 0x0486,
- 0x0490, 0x04c4,
- 0x04c7, 0x04c8,
- 0x04cb, 0x04cc,
- 0x04d0, 0x04eb,
- 0x04ee, 0x04f5,
- 0x04f8, 0x04f9,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x0589,
- 0x05b0, 0x05b9,
- 0x05bb, 0x05c3,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x060c, 0x060c,
- 0x061b, 0x061b,
- 0x061f, 0x061f,
- 0x0621, 0x063a,
- 0x0640, 0x0652,
- 0x0660, 0x066d,
- 0x0670, 0x06b7,
- 0x06ba, 0x06be,
- 0x06c0, 0x06ce,
- 0x06d0, 0x06ed,
- 0x06f0, 0x06f9,
- 0x0901, 0x0903,
- 0x0905, 0x0939,
- 0x093c, 0x094d,
- 0x0950, 0x0954,
- 0x0958, 0x0970,
- 0x0981, 0x0983,
- 0x0985, 0x098c,
- 0x098f, 0x0990,
- 0x0993, 0x09a8,
- 0x09aa, 0x09b0,
- 0x09b2, 0x09b2,
- 0x09b6, 0x09b9,
- 0x09bc, 0x09bc,
- 0x09be, 0x09c4,
- 0x09c7, 0x09c8,
- 0x09cb, 0x09cd,
- 0x09d7, 0x09d7,
- 0x09dc, 0x09dd,
- 0x09df, 0x09e3,
- 0x09e6, 0x09fa,
- 0x0a02, 0x0a02,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a3c, 0x0a3c,
- 0x0a3e, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4d,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a66, 0x0a74,
- 0x0a81, 0x0a83,
- 0x0a85, 0x0a8b,
- 0x0a8d, 0x0a8d,
- 0x0a8f, 0x0a91,
- 0x0a93, 0x0aa8,
- 0x0aaa, 0x0ab0,
- 0x0ab2, 0x0ab3,
- 0x0ab5, 0x0ab9,
- 0x0abc, 0x0ac5,
- 0x0ac7, 0x0ac9,
- 0x0acb, 0x0acd,
- 0x0ad0, 0x0ad0,
- 0x0ae0, 0x0ae0,
- 0x0ae6, 0x0aef,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b36, 0x0b39,
- 0x0b3c, 0x0b43,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b61,
- 0x0b66, 0x0b70,
- 0x0b82, 0x0b83,
- 0x0b85, 0x0b8a,
- 0x0b8e, 0x0b90,
- 0x0b92, 0x0b95,
- 0x0b99, 0x0b9a,
- 0x0b9c, 0x0b9c,
- 0x0b9e, 0x0b9f,
- 0x0ba3, 0x0ba4,
- 0x0ba8, 0x0baa,
- 0x0bae, 0x0bb5,
- 0x0bb7, 0x0bb9,
- 0x0bbe, 0x0bc2,
- 0x0bc6, 0x0bc8,
- 0x0bca, 0x0bcd,
- 0x0bd7, 0x0bd7,
- 0x0be7, 0x0bf2,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c3e, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c60, 0x0c61,
- 0x0c66, 0x0c6f,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbe, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce1,
- 0x0ce6, 0x0cef,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d28,
- 0x0d2a, 0x0d39,
- 0x0d3e, 0x0d43,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4d,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d61,
- 0x0d66, 0x0d6f,
- 0x0e01, 0x0e3a,
- 0x0e3f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edd,
- 0x10a0, 0x10c5,
- 0x10d0, 0x10f6,
- 0x10fb, 0x10fb,
- 0x1100, 0x1159,
- 0x115f, 0x11a2,
- 0x11a8, 0x11f9,
- 0x1e00, 0x1e9a,
- 0x1ea0, 0x1ef9,
- 0x1f00, 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, 0x202e,
- 0x2030, 0x2046,
- 0x206a, 0x2070,
- 0x2074, 0x208e,
- 0x20a0, 0x20aa,
- 0x20d0, 0x20e1,
- 0x2100, 0x2138,
- 0x2153, 0x2182,
- 0x2190, 0x21ea,
- 0x2200, 0x22f1,
- 0x2300, 0x2300,
- 0x2302, 0x237a,
- 0x2400, 0x2424,
- 0x2440, 0x244a,
- 0x2460, 0x24ea,
- 0x2500, 0x2595,
- 0x25a0, 0x25ef,
- 0x2600, 0x2613,
- 0x261a, 0x266f,
- 0x2701, 0x2704,
- 0x2706, 0x2709,
- 0x270c, 0x2727,
- 0x2729, 0x274b,
- 0x274d, 0x274d,
- 0x274f, 0x2752,
- 0x2756, 0x2756,
- 0x2758, 0x275e,
- 0x2761, 0x2767,
- 0x2776, 0x2794,
- 0x2798, 0x27af,
- 0x27b1, 0x27be,
- 0x3000, 0x3037,
- 0x303f, 0x303f,
- 0x3041, 0x3094,
- 0x3099, 0x309e,
- 0x30a1, 0x30fe,
- 0x3105, 0x312c,
- 0x3131, 0x318e,
- 0x3190, 0x319f,
- 0x3200, 0x321c,
- 0x3220, 0x3243,
- 0x3260, 0x327b,
- 0x327f, 0x32b0,
- 0x32c0, 0x32cb,
- 0x32d0, 0x32fe,
- 0x3300, 0x3376,
- 0x337b, 0x33dd,
- 0x33e0, 0x33fe,
- 0x4e00, 0x9fa5,
- 0xe000, 0xfa2d,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1e, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfb,
- 0xfe20, 0xfe23,
- 0xfe30, 0xfe44,
- 0xfe49, 0xfe52,
- 0xfe54, 0xfe66,
- 0xfe68, 0xfe6b,
- 0xfe70, 0xfe72,
- 0xfe74, 0xfe74,
- 0xfe76, 0xfefc,
- 0xfeff, 0xfeff,
- 0xff01, 0xff5e,
- 0xff61, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
- 0xffe0, 0xffe6,
- 0xffe8, 0xffee,
- 0xfffd, 0xffff,
-}; /* CR_Age_1_1 */
-
-/* 'Age_2_0': Derived Age 2.0 */
-static const OnigCodePoint CR_Age_2_0[] = {
- 312,
- 0x0000, 0x01f5,
- 0x01fa, 0x0217,
- 0x0250, 0x02a8,
- 0x02b0, 0x02de,
- 0x02e0, 0x02e9,
- 0x0300, 0x0345,
- 0x0360, 0x0361,
- 0x0374, 0x0375,
- 0x037a, 0x037a,
- 0x037e, 0x037e,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03ce,
- 0x03d0, 0x03d6,
- 0x03da, 0x03da,
- 0x03dc, 0x03dc,
- 0x03de, 0x03de,
- 0x03e0, 0x03e0,
- 0x03e2, 0x03f3,
- 0x0401, 0x040c,
- 0x040e, 0x044f,
- 0x0451, 0x045c,
- 0x045e, 0x0486,
- 0x0490, 0x04c4,
- 0x04c7, 0x04c8,
- 0x04cb, 0x04cc,
- 0x04d0, 0x04eb,
- 0x04ee, 0x04f5,
- 0x04f8, 0x04f9,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x0589,
- 0x0591, 0x05a1,
- 0x05a3, 0x05b9,
- 0x05bb, 0x05c4,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x060c, 0x060c,
- 0x061b, 0x061b,
- 0x061f, 0x061f,
- 0x0621, 0x063a,
- 0x0640, 0x0652,
- 0x0660, 0x066d,
- 0x0670, 0x06b7,
- 0x06ba, 0x06be,
- 0x06c0, 0x06ce,
- 0x06d0, 0x06ed,
- 0x06f0, 0x06f9,
- 0x0901, 0x0903,
- 0x0905, 0x0939,
- 0x093c, 0x094d,
- 0x0950, 0x0954,
- 0x0958, 0x0970,
- 0x0981, 0x0983,
- 0x0985, 0x098c,
- 0x098f, 0x0990,
- 0x0993, 0x09a8,
- 0x09aa, 0x09b0,
- 0x09b2, 0x09b2,
- 0x09b6, 0x09b9,
- 0x09bc, 0x09bc,
- 0x09be, 0x09c4,
- 0x09c7, 0x09c8,
- 0x09cb, 0x09cd,
- 0x09d7, 0x09d7,
- 0x09dc, 0x09dd,
- 0x09df, 0x09e3,
- 0x09e6, 0x09fa,
- 0x0a02, 0x0a02,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a3c, 0x0a3c,
- 0x0a3e, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4d,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a66, 0x0a74,
- 0x0a81, 0x0a83,
- 0x0a85, 0x0a8b,
- 0x0a8d, 0x0a8d,
- 0x0a8f, 0x0a91,
- 0x0a93, 0x0aa8,
- 0x0aaa, 0x0ab0,
- 0x0ab2, 0x0ab3,
- 0x0ab5, 0x0ab9,
- 0x0abc, 0x0ac5,
- 0x0ac7, 0x0ac9,
- 0x0acb, 0x0acd,
- 0x0ad0, 0x0ad0,
- 0x0ae0, 0x0ae0,
- 0x0ae6, 0x0aef,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b36, 0x0b39,
- 0x0b3c, 0x0b43,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b61,
- 0x0b66, 0x0b70,
- 0x0b82, 0x0b83,
- 0x0b85, 0x0b8a,
- 0x0b8e, 0x0b90,
- 0x0b92, 0x0b95,
- 0x0b99, 0x0b9a,
- 0x0b9c, 0x0b9c,
- 0x0b9e, 0x0b9f,
- 0x0ba3, 0x0ba4,
- 0x0ba8, 0x0baa,
- 0x0bae, 0x0bb5,
- 0x0bb7, 0x0bb9,
- 0x0bbe, 0x0bc2,
- 0x0bc6, 0x0bc8,
- 0x0bca, 0x0bcd,
- 0x0bd7, 0x0bd7,
- 0x0be7, 0x0bf2,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c3e, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c60, 0x0c61,
- 0x0c66, 0x0c6f,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbe, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce1,
- 0x0ce6, 0x0cef,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d28,
- 0x0d2a, 0x0d39,
- 0x0d3e, 0x0d43,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4d,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d61,
- 0x0d66, 0x0d6f,
- 0x0e01, 0x0e3a,
- 0x0e3f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edd,
- 0x0f00, 0x0f47,
- 0x0f49, 0x0f69,
- 0x0f71, 0x0f8b,
- 0x0f90, 0x0f95,
- 0x0f97, 0x0f97,
- 0x0f99, 0x0fad,
- 0x0fb1, 0x0fb7,
- 0x0fb9, 0x0fb9,
- 0x10a0, 0x10c5,
- 0x10d0, 0x10f6,
- 0x10fb, 0x10fb,
- 0x1100, 0x1159,
- 0x115f, 0x11a2,
- 0x11a8, 0x11f9,
- 0x1e00, 0x1e9b,
- 0x1ea0, 0x1ef9,
- 0x1f00, 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, 0x202e,
- 0x2030, 0x2046,
- 0x206a, 0x2070,
- 0x2074, 0x208e,
- 0x20a0, 0x20ab,
- 0x20d0, 0x20e1,
- 0x2100, 0x2138,
- 0x2153, 0x2182,
- 0x2190, 0x21ea,
- 0x2200, 0x22f1,
- 0x2300, 0x2300,
- 0x2302, 0x237a,
- 0x2400, 0x2424,
- 0x2440, 0x244a,
- 0x2460, 0x24ea,
- 0x2500, 0x2595,
- 0x25a0, 0x25ef,
- 0x2600, 0x2613,
- 0x261a, 0x266f,
- 0x2701, 0x2704,
- 0x2706, 0x2709,
- 0x270c, 0x2727,
- 0x2729, 0x274b,
- 0x274d, 0x274d,
- 0x274f, 0x2752,
- 0x2756, 0x2756,
- 0x2758, 0x275e,
- 0x2761, 0x2767,
- 0x2776, 0x2794,
- 0x2798, 0x27af,
- 0x27b1, 0x27be,
- 0x3000, 0x3037,
- 0x303f, 0x303f,
- 0x3041, 0x3094,
- 0x3099, 0x309e,
- 0x30a1, 0x30fe,
- 0x3105, 0x312c,
- 0x3131, 0x318e,
- 0x3190, 0x319f,
- 0x3200, 0x321c,
- 0x3220, 0x3243,
- 0x3260, 0x327b,
- 0x327f, 0x32b0,
- 0x32c0, 0x32cb,
- 0x32d0, 0x32fe,
- 0x3300, 0x3376,
- 0x337b, 0x33dd,
- 0x33e0, 0x33fe,
- 0x4e00, 0x9fa5,
- 0xac00, 0xd7a3,
- 0xd800, 0xfa2d,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1e, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfb,
- 0xfe20, 0xfe23,
- 0xfe30, 0xfe44,
- 0xfe49, 0xfe52,
- 0xfe54, 0xfe66,
- 0xfe68, 0xfe6b,
- 0xfe70, 0xfe72,
- 0xfe74, 0xfe74,
- 0xfe76, 0xfefc,
- 0xfeff, 0xfeff,
- 0xff01, 0xff5e,
- 0xff61, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
- 0xffe0, 0xffe6,
- 0xffe8, 0xffee,
- 0xfffd, 0xffff,
- 0x1fffe, 0x1ffff,
- 0x2fffe, 0x2ffff,
- 0x3fffe, 0x3ffff,
- 0x4fffe, 0x4ffff,
- 0x5fffe, 0x5ffff,
- 0x6fffe, 0x6ffff,
- 0x7fffe, 0x7ffff,
- 0x8fffe, 0x8ffff,
- 0x9fffe, 0x9ffff,
- 0xafffe, 0xaffff,
- 0xbfffe, 0xbffff,
- 0xcfffe, 0xcffff,
- 0xdfffe, 0xdffff,
- 0xefffe, 0x10ffff,
-}; /* CR_Age_2_0 */
-
-/* 'Age_2_1': Derived Age 2.1 */
-static const OnigCodePoint CR_Age_2_1[] = {
- 312,
- 0x0000, 0x01f5,
- 0x01fa, 0x0217,
- 0x0250, 0x02a8,
- 0x02b0, 0x02de,
- 0x02e0, 0x02e9,
- 0x0300, 0x0345,
- 0x0360, 0x0361,
- 0x0374, 0x0375,
- 0x037a, 0x037a,
- 0x037e, 0x037e,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03ce,
- 0x03d0, 0x03d6,
- 0x03da, 0x03da,
- 0x03dc, 0x03dc,
- 0x03de, 0x03de,
- 0x03e0, 0x03e0,
- 0x03e2, 0x03f3,
- 0x0401, 0x040c,
- 0x040e, 0x044f,
- 0x0451, 0x045c,
- 0x045e, 0x0486,
- 0x0490, 0x04c4,
- 0x04c7, 0x04c8,
- 0x04cb, 0x04cc,
- 0x04d0, 0x04eb,
- 0x04ee, 0x04f5,
- 0x04f8, 0x04f9,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x0589,
- 0x0591, 0x05a1,
- 0x05a3, 0x05b9,
- 0x05bb, 0x05c4,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x060c, 0x060c,
- 0x061b, 0x061b,
- 0x061f, 0x061f,
- 0x0621, 0x063a,
- 0x0640, 0x0652,
- 0x0660, 0x066d,
- 0x0670, 0x06b7,
- 0x06ba, 0x06be,
- 0x06c0, 0x06ce,
- 0x06d0, 0x06ed,
- 0x06f0, 0x06f9,
- 0x0901, 0x0903,
- 0x0905, 0x0939,
- 0x093c, 0x094d,
- 0x0950, 0x0954,
- 0x0958, 0x0970,
- 0x0981, 0x0983,
- 0x0985, 0x098c,
- 0x098f, 0x0990,
- 0x0993, 0x09a8,
- 0x09aa, 0x09b0,
- 0x09b2, 0x09b2,
- 0x09b6, 0x09b9,
- 0x09bc, 0x09bc,
- 0x09be, 0x09c4,
- 0x09c7, 0x09c8,
- 0x09cb, 0x09cd,
- 0x09d7, 0x09d7,
- 0x09dc, 0x09dd,
- 0x09df, 0x09e3,
- 0x09e6, 0x09fa,
- 0x0a02, 0x0a02,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a3c, 0x0a3c,
- 0x0a3e, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4d,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a66, 0x0a74,
- 0x0a81, 0x0a83,
- 0x0a85, 0x0a8b,
- 0x0a8d, 0x0a8d,
- 0x0a8f, 0x0a91,
- 0x0a93, 0x0aa8,
- 0x0aaa, 0x0ab0,
- 0x0ab2, 0x0ab3,
- 0x0ab5, 0x0ab9,
- 0x0abc, 0x0ac5,
- 0x0ac7, 0x0ac9,
- 0x0acb, 0x0acd,
- 0x0ad0, 0x0ad0,
- 0x0ae0, 0x0ae0,
- 0x0ae6, 0x0aef,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b36, 0x0b39,
- 0x0b3c, 0x0b43,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b61,
- 0x0b66, 0x0b70,
- 0x0b82, 0x0b83,
- 0x0b85, 0x0b8a,
- 0x0b8e, 0x0b90,
- 0x0b92, 0x0b95,
- 0x0b99, 0x0b9a,
- 0x0b9c, 0x0b9c,
- 0x0b9e, 0x0b9f,
- 0x0ba3, 0x0ba4,
- 0x0ba8, 0x0baa,
- 0x0bae, 0x0bb5,
- 0x0bb7, 0x0bb9,
- 0x0bbe, 0x0bc2,
- 0x0bc6, 0x0bc8,
- 0x0bca, 0x0bcd,
- 0x0bd7, 0x0bd7,
- 0x0be7, 0x0bf2,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c3e, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c60, 0x0c61,
- 0x0c66, 0x0c6f,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbe, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce1,
- 0x0ce6, 0x0cef,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d28,
- 0x0d2a, 0x0d39,
- 0x0d3e, 0x0d43,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4d,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d61,
- 0x0d66, 0x0d6f,
- 0x0e01, 0x0e3a,
- 0x0e3f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edd,
- 0x0f00, 0x0f47,
- 0x0f49, 0x0f69,
- 0x0f71, 0x0f8b,
- 0x0f90, 0x0f95,
- 0x0f97, 0x0f97,
- 0x0f99, 0x0fad,
- 0x0fb1, 0x0fb7,
- 0x0fb9, 0x0fb9,
- 0x10a0, 0x10c5,
- 0x10d0, 0x10f6,
- 0x10fb, 0x10fb,
- 0x1100, 0x1159,
- 0x115f, 0x11a2,
- 0x11a8, 0x11f9,
- 0x1e00, 0x1e9b,
- 0x1ea0, 0x1ef9,
- 0x1f00, 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, 0x202e,
- 0x2030, 0x2046,
- 0x206a, 0x2070,
- 0x2074, 0x208e,
- 0x20a0, 0x20ac,
- 0x20d0, 0x20e1,
- 0x2100, 0x2138,
- 0x2153, 0x2182,
- 0x2190, 0x21ea,
- 0x2200, 0x22f1,
- 0x2300, 0x2300,
- 0x2302, 0x237a,
- 0x2400, 0x2424,
- 0x2440, 0x244a,
- 0x2460, 0x24ea,
- 0x2500, 0x2595,
- 0x25a0, 0x25ef,
- 0x2600, 0x2613,
- 0x261a, 0x266f,
- 0x2701, 0x2704,
- 0x2706, 0x2709,
- 0x270c, 0x2727,
- 0x2729, 0x274b,
- 0x274d, 0x274d,
- 0x274f, 0x2752,
- 0x2756, 0x2756,
- 0x2758, 0x275e,
- 0x2761, 0x2767,
- 0x2776, 0x2794,
- 0x2798, 0x27af,
- 0x27b1, 0x27be,
- 0x3000, 0x3037,
- 0x303f, 0x303f,
- 0x3041, 0x3094,
- 0x3099, 0x309e,
- 0x30a1, 0x30fe,
- 0x3105, 0x312c,
- 0x3131, 0x318e,
- 0x3190, 0x319f,
- 0x3200, 0x321c,
- 0x3220, 0x3243,
- 0x3260, 0x327b,
- 0x327f, 0x32b0,
- 0x32c0, 0x32cb,
- 0x32d0, 0x32fe,
- 0x3300, 0x3376,
- 0x337b, 0x33dd,
- 0x33e0, 0x33fe,
- 0x4e00, 0x9fa5,
- 0xac00, 0xd7a3,
- 0xd800, 0xfa2d,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1e, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfb,
- 0xfe20, 0xfe23,
- 0xfe30, 0xfe44,
- 0xfe49, 0xfe52,
- 0xfe54, 0xfe66,
- 0xfe68, 0xfe6b,
- 0xfe70, 0xfe72,
- 0xfe74, 0xfe74,
- 0xfe76, 0xfefc,
- 0xfeff, 0xfeff,
- 0xff01, 0xff5e,
- 0xff61, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
- 0xffe0, 0xffe6,
- 0xffe8, 0xffee,
- 0xfffc, 0xffff,
- 0x1fffe, 0x1ffff,
- 0x2fffe, 0x2ffff,
- 0x3fffe, 0x3ffff,
- 0x4fffe, 0x4ffff,
- 0x5fffe, 0x5ffff,
- 0x6fffe, 0x6ffff,
- 0x7fffe, 0x7ffff,
- 0x8fffe, 0x8ffff,
- 0x9fffe, 0x9ffff,
- 0xafffe, 0xaffff,
- 0xbfffe, 0xbffff,
- 0xcfffe, 0xcffff,
- 0xdfffe, 0xdffff,
- 0xefffe, 0x10ffff,
-}; /* CR_Age_2_1 */
-
-/* 'Age_3_0': Derived Age 3.0 */
-static const OnigCodePoint CR_Age_3_0[] = {
- 369,
- 0x0000, 0x021f,
- 0x0222, 0x0233,
- 0x0250, 0x02ad,
- 0x02b0, 0x02ee,
- 0x0300, 0x034e,
- 0x0360, 0x0362,
- 0x0374, 0x0375,
- 0x037a, 0x037a,
- 0x037e, 0x037e,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03ce,
- 0x03d0, 0x03d7,
- 0x03da, 0x03f3,
- 0x0400, 0x0486,
- 0x0488, 0x0489,
- 0x048c, 0x04c4,
- 0x04c7, 0x04c8,
- 0x04cb, 0x04cc,
- 0x04d0, 0x04f5,
- 0x04f8, 0x04f9,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x0591, 0x05a1,
- 0x05a3, 0x05b9,
- 0x05bb, 0x05c4,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x060c, 0x060c,
- 0x061b, 0x061b,
- 0x061f, 0x061f,
- 0x0621, 0x063a,
- 0x0640, 0x0655,
- 0x0660, 0x066d,
- 0x0670, 0x06ed,
- 0x06f0, 0x06fe,
- 0x0700, 0x070d,
- 0x070f, 0x072c,
- 0x0730, 0x074a,
- 0x0780, 0x07b0,
- 0x0901, 0x0903,
- 0x0905, 0x0939,
- 0x093c, 0x094d,
- 0x0950, 0x0954,
- 0x0958, 0x0970,
- 0x0981, 0x0983,
- 0x0985, 0x098c,
- 0x098f, 0x0990,
- 0x0993, 0x09a8,
- 0x09aa, 0x09b0,
- 0x09b2, 0x09b2,
- 0x09b6, 0x09b9,
- 0x09bc, 0x09bc,
- 0x09be, 0x09c4,
- 0x09c7, 0x09c8,
- 0x09cb, 0x09cd,
- 0x09d7, 0x09d7,
- 0x09dc, 0x09dd,
- 0x09df, 0x09e3,
- 0x09e6, 0x09fa,
- 0x0a02, 0x0a02,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a3c, 0x0a3c,
- 0x0a3e, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4d,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a66, 0x0a74,
- 0x0a81, 0x0a83,
- 0x0a85, 0x0a8b,
- 0x0a8d, 0x0a8d,
- 0x0a8f, 0x0a91,
- 0x0a93, 0x0aa8,
- 0x0aaa, 0x0ab0,
- 0x0ab2, 0x0ab3,
- 0x0ab5, 0x0ab9,
- 0x0abc, 0x0ac5,
- 0x0ac7, 0x0ac9,
- 0x0acb, 0x0acd,
- 0x0ad0, 0x0ad0,
- 0x0ae0, 0x0ae0,
- 0x0ae6, 0x0aef,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b36, 0x0b39,
- 0x0b3c, 0x0b43,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b61,
- 0x0b66, 0x0b70,
- 0x0b82, 0x0b83,
- 0x0b85, 0x0b8a,
- 0x0b8e, 0x0b90,
- 0x0b92, 0x0b95,
- 0x0b99, 0x0b9a,
- 0x0b9c, 0x0b9c,
- 0x0b9e, 0x0b9f,
- 0x0ba3, 0x0ba4,
- 0x0ba8, 0x0baa,
- 0x0bae, 0x0bb5,
- 0x0bb7, 0x0bb9,
- 0x0bbe, 0x0bc2,
- 0x0bc6, 0x0bc8,
- 0x0bca, 0x0bcd,
- 0x0bd7, 0x0bd7,
- 0x0be7, 0x0bf2,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c3e, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c60, 0x0c61,
- 0x0c66, 0x0c6f,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbe, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce1,
- 0x0ce6, 0x0cef,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d28,
- 0x0d2a, 0x0d39,
- 0x0d3e, 0x0d43,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4d,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d61,
- 0x0d66, 0x0d6f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df4,
- 0x0e01, 0x0e3a,
- 0x0e3f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edd,
- 0x0f00, 0x0f47,
- 0x0f49, 0x0f6a,
- 0x0f71, 0x0f8b,
- 0x0f90, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fbe, 0x0fcc,
- 0x0fcf, 0x0fcf,
- 0x1000, 0x1021,
- 0x1023, 0x1027,
- 0x1029, 0x102a,
- 0x102c, 0x1032,
- 0x1036, 0x1039,
- 0x1040, 0x1059,
- 0x10a0, 0x10c5,
- 0x10d0, 0x10f6,
- 0x10fb, 0x10fb,
- 0x1100, 0x1159,
- 0x115f, 0x11a2,
- 0x11a8, 0x11f9,
- 0x1200, 0x1206,
- 0x1208, 0x1246,
- 0x1248, 0x1248,
- 0x124a, 0x124d,
- 0x1250, 0x1256,
- 0x1258, 0x1258,
- 0x125a, 0x125d,
- 0x1260, 0x1286,
- 0x1288, 0x1288,
- 0x128a, 0x128d,
- 0x1290, 0x12ae,
- 0x12b0, 0x12b0,
- 0x12b2, 0x12b5,
- 0x12b8, 0x12be,
- 0x12c0, 0x12c0,
- 0x12c2, 0x12c5,
- 0x12c8, 0x12ce,
- 0x12d0, 0x12d6,
- 0x12d8, 0x12ee,
- 0x12f0, 0x130e,
- 0x1310, 0x1310,
- 0x1312, 0x1315,
- 0x1318, 0x131e,
- 0x1320, 0x1346,
- 0x1348, 0x135a,
- 0x1361, 0x137c,
- 0x13a0, 0x13f4,
- 0x1401, 0x1676,
- 0x1680, 0x169c,
- 0x16a0, 0x16f0,
- 0x1780, 0x17dc,
- 0x17e0, 0x17e9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18a9,
- 0x1e00, 0x1e9b,
- 0x1ea0, 0x1ef9,
- 0x1f00, 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, 0x2046,
- 0x2048, 0x204d,
- 0x206a, 0x2070,
- 0x2074, 0x208e,
- 0x20a0, 0x20af,
- 0x20d0, 0x20e3,
- 0x2100, 0x213a,
- 0x2153, 0x2183,
- 0x2190, 0x21f3,
- 0x2200, 0x22f1,
- 0x2300, 0x237b,
- 0x237d, 0x239a,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x24ea,
- 0x2500, 0x2595,
- 0x25a0, 0x25f7,
- 0x2600, 0x2613,
- 0x2619, 0x2671,
- 0x2701, 0x2704,
- 0x2706, 0x2709,
- 0x270c, 0x2727,
- 0x2729, 0x274b,
- 0x274d, 0x274d,
- 0x274f, 0x2752,
- 0x2756, 0x2756,
- 0x2758, 0x275e,
- 0x2761, 0x2767,
- 0x2776, 0x2794,
- 0x2798, 0x27af,
- 0x27b1, 0x27be,
- 0x2800, 0x28ff,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303a,
- 0x303e, 0x303f,
- 0x3041, 0x3094,
- 0x3099, 0x309e,
- 0x30a1, 0x30fe,
- 0x3105, 0x312c,
- 0x3131, 0x318e,
- 0x3190, 0x31b7,
- 0x3200, 0x321c,
- 0x3220, 0x3243,
- 0x3260, 0x327b,
- 0x327f, 0x32b0,
- 0x32c0, 0x32cb,
- 0x32d0, 0x32fe,
- 0x3300, 0x3376,
- 0x337b, 0x33dd,
- 0x33e0, 0x33fe,
- 0x3400, 0x4db5,
- 0x4e00, 0x9fa5,
- 0xa000, 0xa48c,
- 0xa490, 0xa4a1,
- 0xa4a4, 0xa4b3,
- 0xa4b5, 0xa4c0,
- 0xa4c2, 0xa4c4,
- 0xa4c6, 0xa4c6,
- 0xac00, 0xd7a3,
- 0xd800, 0xfa2d,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdf0, 0xfdfb,
- 0xfe20, 0xfe23,
- 0xfe30, 0xfe44,
- 0xfe49, 0xfe52,
- 0xfe54, 0xfe66,
- 0xfe68, 0xfe6b,
- 0xfe70, 0xfe72,
- 0xfe74, 0xfe74,
- 0xfe76, 0xfefc,
- 0xfeff, 0xfeff,
- 0xff01, 0xff5e,
- 0xff61, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
- 0xffe0, 0xffe6,
- 0xffe8, 0xffee,
- 0xfff9, 0xffff,
- 0x1fffe, 0x1ffff,
- 0x2fffe, 0x2ffff,
- 0x3fffe, 0x3ffff,
- 0x4fffe, 0x4ffff,
- 0x5fffe, 0x5ffff,
- 0x6fffe, 0x6ffff,
- 0x7fffe, 0x7ffff,
- 0x8fffe, 0x8ffff,
- 0x9fffe, 0x9ffff,
- 0xafffe, 0xaffff,
- 0xbfffe, 0xbffff,
- 0xcfffe, 0xcffff,
- 0xdfffe, 0xdffff,
- 0xefffe, 0x10ffff,
-}; /* CR_Age_3_0 */
-
-/* 'Age_3_1': Derived Age 3.1 */
-static const OnigCodePoint CR_Age_3_1[] = {
- 402,
- 0x0000, 0x021f,
- 0x0222, 0x0233,
- 0x0250, 0x02ad,
- 0x02b0, 0x02ee,
- 0x0300, 0x034e,
- 0x0360, 0x0362,
- 0x0374, 0x0375,
- 0x037a, 0x037a,
- 0x037e, 0x037e,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03ce,
- 0x03d0, 0x03d7,
- 0x03da, 0x03f5,
- 0x0400, 0x0486,
- 0x0488, 0x0489,
- 0x048c, 0x04c4,
- 0x04c7, 0x04c8,
- 0x04cb, 0x04cc,
- 0x04d0, 0x04f5,
- 0x04f8, 0x04f9,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x0591, 0x05a1,
- 0x05a3, 0x05b9,
- 0x05bb, 0x05c4,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x060c, 0x060c,
- 0x061b, 0x061b,
- 0x061f, 0x061f,
- 0x0621, 0x063a,
- 0x0640, 0x0655,
- 0x0660, 0x066d,
- 0x0670, 0x06ed,
- 0x06f0, 0x06fe,
- 0x0700, 0x070d,
- 0x070f, 0x072c,
- 0x0730, 0x074a,
- 0x0780, 0x07b0,
- 0x0901, 0x0903,
- 0x0905, 0x0939,
- 0x093c, 0x094d,
- 0x0950, 0x0954,
- 0x0958, 0x0970,
- 0x0981, 0x0983,
- 0x0985, 0x098c,
- 0x098f, 0x0990,
- 0x0993, 0x09a8,
- 0x09aa, 0x09b0,
- 0x09b2, 0x09b2,
- 0x09b6, 0x09b9,
- 0x09bc, 0x09bc,
- 0x09be, 0x09c4,
- 0x09c7, 0x09c8,
- 0x09cb, 0x09cd,
- 0x09d7, 0x09d7,
- 0x09dc, 0x09dd,
- 0x09df, 0x09e3,
- 0x09e6, 0x09fa,
- 0x0a02, 0x0a02,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a3c, 0x0a3c,
- 0x0a3e, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4d,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a66, 0x0a74,
- 0x0a81, 0x0a83,
- 0x0a85, 0x0a8b,
- 0x0a8d, 0x0a8d,
- 0x0a8f, 0x0a91,
- 0x0a93, 0x0aa8,
- 0x0aaa, 0x0ab0,
- 0x0ab2, 0x0ab3,
- 0x0ab5, 0x0ab9,
- 0x0abc, 0x0ac5,
- 0x0ac7, 0x0ac9,
- 0x0acb, 0x0acd,
- 0x0ad0, 0x0ad0,
- 0x0ae0, 0x0ae0,
- 0x0ae6, 0x0aef,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b36, 0x0b39,
- 0x0b3c, 0x0b43,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b61,
- 0x0b66, 0x0b70,
- 0x0b82, 0x0b83,
- 0x0b85, 0x0b8a,
- 0x0b8e, 0x0b90,
- 0x0b92, 0x0b95,
- 0x0b99, 0x0b9a,
- 0x0b9c, 0x0b9c,
- 0x0b9e, 0x0b9f,
- 0x0ba3, 0x0ba4,
- 0x0ba8, 0x0baa,
- 0x0bae, 0x0bb5,
- 0x0bb7, 0x0bb9,
- 0x0bbe, 0x0bc2,
- 0x0bc6, 0x0bc8,
- 0x0bca, 0x0bcd,
- 0x0bd7, 0x0bd7,
- 0x0be7, 0x0bf2,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c3e, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c60, 0x0c61,
- 0x0c66, 0x0c6f,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbe, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce1,
- 0x0ce6, 0x0cef,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d28,
- 0x0d2a, 0x0d39,
- 0x0d3e, 0x0d43,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4d,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d61,
- 0x0d66, 0x0d6f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df4,
- 0x0e01, 0x0e3a,
- 0x0e3f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edd,
- 0x0f00, 0x0f47,
- 0x0f49, 0x0f6a,
- 0x0f71, 0x0f8b,
- 0x0f90, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fbe, 0x0fcc,
- 0x0fcf, 0x0fcf,
- 0x1000, 0x1021,
- 0x1023, 0x1027,
- 0x1029, 0x102a,
- 0x102c, 0x1032,
- 0x1036, 0x1039,
- 0x1040, 0x1059,
- 0x10a0, 0x10c5,
- 0x10d0, 0x10f6,
- 0x10fb, 0x10fb,
- 0x1100, 0x1159,
- 0x115f, 0x11a2,
- 0x11a8, 0x11f9,
- 0x1200, 0x1206,
- 0x1208, 0x1246,
- 0x1248, 0x1248,
- 0x124a, 0x124d,
- 0x1250, 0x1256,
- 0x1258, 0x1258,
- 0x125a, 0x125d,
- 0x1260, 0x1286,
- 0x1288, 0x1288,
- 0x128a, 0x128d,
- 0x1290, 0x12ae,
- 0x12b0, 0x12b0,
- 0x12b2, 0x12b5,
- 0x12b8, 0x12be,
- 0x12c0, 0x12c0,
- 0x12c2, 0x12c5,
- 0x12c8, 0x12ce,
- 0x12d0, 0x12d6,
- 0x12d8, 0x12ee,
- 0x12f0, 0x130e,
- 0x1310, 0x1310,
- 0x1312, 0x1315,
- 0x1318, 0x131e,
- 0x1320, 0x1346,
- 0x1348, 0x135a,
- 0x1361, 0x137c,
- 0x13a0, 0x13f4,
- 0x1401, 0x1676,
- 0x1680, 0x169c,
- 0x16a0, 0x16f0,
- 0x1780, 0x17dc,
- 0x17e0, 0x17e9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18a9,
- 0x1e00, 0x1e9b,
- 0x1ea0, 0x1ef9,
- 0x1f00, 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, 0x2046,
- 0x2048, 0x204d,
- 0x206a, 0x2070,
- 0x2074, 0x208e,
- 0x20a0, 0x20af,
- 0x20d0, 0x20e3,
- 0x2100, 0x213a,
- 0x2153, 0x2183,
- 0x2190, 0x21f3,
- 0x2200, 0x22f1,
- 0x2300, 0x237b,
- 0x237d, 0x239a,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x24ea,
- 0x2500, 0x2595,
- 0x25a0, 0x25f7,
- 0x2600, 0x2613,
- 0x2619, 0x2671,
- 0x2701, 0x2704,
- 0x2706, 0x2709,
- 0x270c, 0x2727,
- 0x2729, 0x274b,
- 0x274d, 0x274d,
- 0x274f, 0x2752,
- 0x2756, 0x2756,
- 0x2758, 0x275e,
- 0x2761, 0x2767,
- 0x2776, 0x2794,
- 0x2798, 0x27af,
- 0x27b1, 0x27be,
- 0x2800, 0x28ff,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303a,
- 0x303e, 0x303f,
- 0x3041, 0x3094,
- 0x3099, 0x309e,
- 0x30a1, 0x30fe,
- 0x3105, 0x312c,
- 0x3131, 0x318e,
- 0x3190, 0x31b7,
- 0x3200, 0x321c,
- 0x3220, 0x3243,
- 0x3260, 0x327b,
- 0x327f, 0x32b0,
- 0x32c0, 0x32cb,
- 0x32d0, 0x32fe,
- 0x3300, 0x3376,
- 0x337b, 0x33dd,
- 0x33e0, 0x33fe,
- 0x3400, 0x4db5,
- 0x4e00, 0x9fa5,
- 0xa000, 0xa48c,
- 0xa490, 0xa4a1,
- 0xa4a4, 0xa4b3,
- 0xa4b5, 0xa4c0,
- 0xa4c2, 0xa4c4,
- 0xa4c6, 0xa4c6,
- 0xac00, 0xd7a3,
- 0xd800, 0xfa2d,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdd0, 0xfdfb,
- 0xfe20, 0xfe23,
- 0xfe30, 0xfe44,
- 0xfe49, 0xfe52,
- 0xfe54, 0xfe66,
- 0xfe68, 0xfe6b,
- 0xfe70, 0xfe72,
- 0xfe74, 0xfe74,
- 0xfe76, 0xfefc,
- 0xfeff, 0xfeff,
- 0xff01, 0xff5e,
- 0xff61, 0xffbe,
- 0xffc2, 0xffc7,
- 0xffca, 0xffcf,
- 0xffd2, 0xffd7,
- 0xffda, 0xffdc,
- 0xffe0, 0xffe6,
- 0xffe8, 0xffee,
- 0xfff9, 0xffff,
- 0x10300, 0x1031e,
- 0x10320, 0x10323,
- 0x10330, 0x1034a,
- 0x10400, 0x10425,
- 0x10428, 0x1044d,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d12a, 0x1d1dd,
- 0x1d400, 0x1d454,
- 0x1d456, 0x1d49c,
- 0x1d49e, 0x1d49f,
- 0x1d4a2, 0x1d4a2,
- 0x1d4a5, 0x1d4a6,
- 0x1d4a9, 0x1d4ac,
- 0x1d4ae, 0x1d4b9,
- 0x1d4bb, 0x1d4bb,
- 0x1d4bd, 0x1d4c0,
- 0x1d4c2, 0x1d4c3,
- 0x1d4c5, 0x1d505,
- 0x1d507, 0x1d50a,
- 0x1d50d, 0x1d514,
- 0x1d516, 0x1d51c,
- 0x1d51e, 0x1d539,
- 0x1d53b, 0x1d53e,
- 0x1d540, 0x1d544,
- 0x1d546, 0x1d546,
- 0x1d54a, 0x1d550,
- 0x1d552, 0x1d6a3,
- 0x1d6a8, 0x1d7c9,
- 0x1d7ce, 0x1d7ff,
- 0x1fffe, 0x2a6d6,
- 0x2f800, 0x2fa1d,
- 0x2fffe, 0x2ffff,
- 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,
- 0xefffe, 0x10ffff,
-}; /* CR_Age_3_1 */
-
-/* 'Age_3_2': Derived Age 3.2 */
-static const OnigCodePoint CR_Age_3_2[] = {
- 397,
- 0x0000, 0x0220,
- 0x0222, 0x0233,
- 0x0250, 0x02ad,
- 0x02b0, 0x02ee,
- 0x0300, 0x034f,
- 0x0360, 0x036f,
- 0x0374, 0x0375,
- 0x037a, 0x037a,
- 0x037e, 0x037e,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03ce,
- 0x03d0, 0x03f6,
- 0x0400, 0x0486,
- 0x0488, 0x04ce,
- 0x04d0, 0x04f5,
- 0x04f8, 0x04f9,
- 0x0500, 0x050f,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x0591, 0x05a1,
- 0x05a3, 0x05b9,
- 0x05bb, 0x05c4,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x060c, 0x060c,
- 0x061b, 0x061b,
- 0x061f, 0x061f,
- 0x0621, 0x063a,
- 0x0640, 0x0655,
- 0x0660, 0x06ed,
- 0x06f0, 0x06fe,
- 0x0700, 0x070d,
- 0x070f, 0x072c,
- 0x0730, 0x074a,
- 0x0780, 0x07b1,
- 0x0901, 0x0903,
- 0x0905, 0x0939,
- 0x093c, 0x094d,
- 0x0950, 0x0954,
- 0x0958, 0x0970,
- 0x0981, 0x0983,
- 0x0985, 0x098c,
- 0x098f, 0x0990,
- 0x0993, 0x09a8,
- 0x09aa, 0x09b0,
- 0x09b2, 0x09b2,
- 0x09b6, 0x09b9,
- 0x09bc, 0x09bc,
- 0x09be, 0x09c4,
- 0x09c7, 0x09c8,
- 0x09cb, 0x09cd,
- 0x09d7, 0x09d7,
- 0x09dc, 0x09dd,
- 0x09df, 0x09e3,
- 0x09e6, 0x09fa,
- 0x0a02, 0x0a02,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a3c, 0x0a3c,
- 0x0a3e, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4d,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a66, 0x0a74,
- 0x0a81, 0x0a83,
- 0x0a85, 0x0a8b,
- 0x0a8d, 0x0a8d,
- 0x0a8f, 0x0a91,
- 0x0a93, 0x0aa8,
- 0x0aaa, 0x0ab0,
- 0x0ab2, 0x0ab3,
- 0x0ab5, 0x0ab9,
- 0x0abc, 0x0ac5,
- 0x0ac7, 0x0ac9,
- 0x0acb, 0x0acd,
- 0x0ad0, 0x0ad0,
- 0x0ae0, 0x0ae0,
- 0x0ae6, 0x0aef,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b36, 0x0b39,
- 0x0b3c, 0x0b43,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b61,
- 0x0b66, 0x0b70,
- 0x0b82, 0x0b83,
- 0x0b85, 0x0b8a,
- 0x0b8e, 0x0b90,
- 0x0b92, 0x0b95,
- 0x0b99, 0x0b9a,
- 0x0b9c, 0x0b9c,
- 0x0b9e, 0x0b9f,
- 0x0ba3, 0x0ba4,
- 0x0ba8, 0x0baa,
- 0x0bae, 0x0bb5,
- 0x0bb7, 0x0bb9,
- 0x0bbe, 0x0bc2,
- 0x0bc6, 0x0bc8,
- 0x0bca, 0x0bcd,
- 0x0bd7, 0x0bd7,
- 0x0be7, 0x0bf2,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c3e, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c60, 0x0c61,
- 0x0c66, 0x0c6f,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbe, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce1,
- 0x0ce6, 0x0cef,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d28,
- 0x0d2a, 0x0d39,
- 0x0d3e, 0x0d43,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4d,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d61,
- 0x0d66, 0x0d6f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df4,
- 0x0e01, 0x0e3a,
- 0x0e3f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edd,
- 0x0f00, 0x0f47,
- 0x0f49, 0x0f6a,
- 0x0f71, 0x0f8b,
- 0x0f90, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fbe, 0x0fcc,
- 0x0fcf, 0x0fcf,
- 0x1000, 0x1021,
- 0x1023, 0x1027,
- 0x1029, 0x102a,
- 0x102c, 0x1032,
- 0x1036, 0x1039,
- 0x1040, 0x1059,
- 0x10a0, 0x10c5,
- 0x10d0, 0x10f8,
- 0x10fb, 0x10fb,
- 0x1100, 0x1159,
- 0x115f, 0x11a2,
- 0x11a8, 0x11f9,
- 0x1200, 0x1206,
- 0x1208, 0x1246,
- 0x1248, 0x1248,
- 0x124a, 0x124d,
- 0x1250, 0x1256,
- 0x1258, 0x1258,
- 0x125a, 0x125d,
- 0x1260, 0x1286,
- 0x1288, 0x1288,
- 0x128a, 0x128d,
- 0x1290, 0x12ae,
- 0x12b0, 0x12b0,
- 0x12b2, 0x12b5,
- 0x12b8, 0x12be,
- 0x12c0, 0x12c0,
- 0x12c2, 0x12c5,
- 0x12c8, 0x12ce,
- 0x12d0, 0x12d6,
- 0x12d8, 0x12ee,
- 0x12f0, 0x130e,
- 0x1310, 0x1310,
- 0x1312, 0x1315,
- 0x1318, 0x131e,
- 0x1320, 0x1346,
- 0x1348, 0x135a,
- 0x1361, 0x137c,
- 0x13a0, 0x13f4,
- 0x1401, 0x1676,
- 0x1680, 0x169c,
- 0x16a0, 0x16f0,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dc,
- 0x17e0, 0x17e9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18a9,
- 0x1e00, 0x1e9b,
- 0x1ea0, 0x1ef9,
- 0x1f00, 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, 0x2052,
- 0x2057, 0x2057,
- 0x205f, 0x2063,
- 0x206a, 0x2071,
- 0x2074, 0x208e,
- 0x20a0, 0x20b1,
- 0x20d0, 0x20ea,
- 0x2100, 0x213a,
- 0x213d, 0x214b,
- 0x2153, 0x2183,
- 0x2190, 0x23ce,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x24fe,
- 0x2500, 0x2613,
- 0x2616, 0x2617,
- 0x2619, 0x267d,
- 0x2680, 0x2689,
- 0x2701, 0x2704,
- 0x2706, 0x2709,
- 0x270c, 0x2727,
- 0x2729, 0x274b,
- 0x274d, 0x274d,
- 0x274f, 0x2752,
- 0x2756, 0x2756,
- 0x2758, 0x275e,
- 0x2761, 0x2794,
- 0x2798, 0x27af,
- 0x27b1, 0x27be,
- 0x27d0, 0x27eb,
- 0x27f0, 0x2aff,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312c,
- 0x3131, 0x318e,
- 0x3190, 0x31b7,
- 0x31f0, 0x321c,
- 0x3220, 0x3243,
- 0x3251, 0x327b,
- 0x327f, 0x32cb,
- 0x32d0, 0x32fe,
- 0x3300, 0x3376,
- 0x337b, 0x33dd,
- 0x33e0, 0x33fe,
- 0x3400, 0x4db5,
- 0x4e00, 0x9fa5,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xac00, 0xd7a3,
- 0xd800, 0xfa2d,
- 0xfa30, 0xfa6a,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdd0, 0xfdfc,
- 0xfe00, 0xfe0f,
- 0xfe20, 0xfe23,
- 0xfe30, 0xfe46,
- 0xfe49, 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, 0xffff,
- 0x10300, 0x1031e,
- 0x10320, 0x10323,
- 0x10330, 0x1034a,
- 0x10400, 0x10425,
- 0x10428, 0x1044d,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d12a, 0x1d1dd,
- 0x1d400, 0x1d454,
- 0x1d456, 0x1d49c,
- 0x1d49e, 0x1d49f,
- 0x1d4a2, 0x1d4a2,
- 0x1d4a5, 0x1d4a6,
- 0x1d4a9, 0x1d4ac,
- 0x1d4ae, 0x1d4b9,
- 0x1d4bb, 0x1d4bb,
- 0x1d4bd, 0x1d4c0,
- 0x1d4c2, 0x1d4c3,
- 0x1d4c5, 0x1d505,
- 0x1d507, 0x1d50a,
- 0x1d50d, 0x1d514,
- 0x1d516, 0x1d51c,
- 0x1d51e, 0x1d539,
- 0x1d53b, 0x1d53e,
- 0x1d540, 0x1d544,
- 0x1d546, 0x1d546,
- 0x1d54a, 0x1d550,
- 0x1d552, 0x1d6a3,
- 0x1d6a8, 0x1d7c9,
- 0x1d7ce, 0x1d7ff,
- 0x1fffe, 0x2a6d6,
- 0x2f800, 0x2fa1d,
- 0x2fffe, 0x2ffff,
- 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,
- 0xefffe, 0x10ffff,
-}; /* CR_Age_3_2 */
-
-/* 'Age_4_0': Derived Age 4.0 */
-static const OnigCodePoint CR_Age_4_0[] = {
- 412,
- 0x0000, 0x0236,
- 0x0250, 0x0357,
- 0x035d, 0x036f,
- 0x0374, 0x0375,
- 0x037a, 0x037a,
- 0x037e, 0x037e,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03ce,
- 0x03d0, 0x03fb,
- 0x0400, 0x0486,
- 0x0488, 0x04ce,
- 0x04d0, 0x04f5,
- 0x04f8, 0x04f9,
- 0x0500, 0x050f,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x0591, 0x05a1,
- 0x05a3, 0x05b9,
- 0x05bb, 0x05c4,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0600, 0x0603,
- 0x060c, 0x0615,
- 0x061b, 0x061b,
- 0x061f, 0x061f,
- 0x0621, 0x063a,
- 0x0640, 0x0658,
- 0x0660, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x074f,
- 0x0780, 0x07b1,
- 0x0901, 0x0939,
- 0x093c, 0x094d,
- 0x0950, 0x0954,
- 0x0958, 0x0970,
- 0x0981, 0x0983,
- 0x0985, 0x098c,
- 0x098f, 0x0990,
- 0x0993, 0x09a8,
- 0x09aa, 0x09b0,
- 0x09b2, 0x09b2,
- 0x09b6, 0x09b9,
- 0x09bc, 0x09c4,
- 0x09c7, 0x09c8,
- 0x09cb, 0x09cd,
- 0x09d7, 0x09d7,
- 0x09dc, 0x09dd,
- 0x09df, 0x09e3,
- 0x09e6, 0x09fa,
- 0x0a01, 0x0a03,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a3c, 0x0a3c,
- 0x0a3e, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4d,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a66, 0x0a74,
- 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, 0x0aef,
- 0x0af1, 0x0af1,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b43,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b61,
- 0x0b66, 0x0b71,
- 0x0b82, 0x0b83,
- 0x0b85, 0x0b8a,
- 0x0b8e, 0x0b90,
- 0x0b92, 0x0b95,
- 0x0b99, 0x0b9a,
- 0x0b9c, 0x0b9c,
- 0x0b9e, 0x0b9f,
- 0x0ba3, 0x0ba4,
- 0x0ba8, 0x0baa,
- 0x0bae, 0x0bb5,
- 0x0bb7, 0x0bb9,
- 0x0bbe, 0x0bc2,
- 0x0bc6, 0x0bc8,
- 0x0bca, 0x0bcd,
- 0x0bd7, 0x0bd7,
- 0x0be7, 0x0bfa,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c3e, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c60, 0x0c61,
- 0x0c66, 0x0c6f,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce1,
- 0x0ce6, 0x0cef,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d28,
- 0x0d2a, 0x0d39,
- 0x0d3e, 0x0d43,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4d,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d61,
- 0x0d66, 0x0d6f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df4,
- 0x0e01, 0x0e3a,
- 0x0e3f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edd,
- 0x0f00, 0x0f47,
- 0x0f49, 0x0f6a,
- 0x0f71, 0x0f8b,
- 0x0f90, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fbe, 0x0fcc,
- 0x0fcf, 0x0fcf,
- 0x1000, 0x1021,
- 0x1023, 0x1027,
- 0x1029, 0x102a,
- 0x102c, 0x1032,
- 0x1036, 0x1039,
- 0x1040, 0x1059,
- 0x10a0, 0x10c5,
- 0x10d0, 0x10f8,
- 0x10fb, 0x10fb,
- 0x1100, 0x1159,
- 0x115f, 0x11a2,
- 0x11a8, 0x11f9,
- 0x1200, 0x1206,
- 0x1208, 0x1246,
- 0x1248, 0x1248,
- 0x124a, 0x124d,
- 0x1250, 0x1256,
- 0x1258, 0x1258,
- 0x125a, 0x125d,
- 0x1260, 0x1286,
- 0x1288, 0x1288,
- 0x128a, 0x128d,
- 0x1290, 0x12ae,
- 0x12b0, 0x12b0,
- 0x12b2, 0x12b5,
- 0x12b8, 0x12be,
- 0x12c0, 0x12c0,
- 0x12c2, 0x12c5,
- 0x12c8, 0x12ce,
- 0x12d0, 0x12d6,
- 0x12d8, 0x12ee,
- 0x12f0, 0x130e,
- 0x1310, 0x1310,
- 0x1312, 0x1315,
- 0x1318, 0x131e,
- 0x1320, 0x1346,
- 0x1348, 0x135a,
- 0x1361, 0x137c,
- 0x13a0, 0x13f4,
- 0x1401, 0x1676,
- 0x1680, 0x169c,
- 0x16a0, 0x16f0,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18a9,
- 0x1900, 0x191c,
- 0x1920, 0x192b,
- 0x1930, 0x193b,
- 0x1940, 0x1940,
- 0x1944, 0x196d,
- 0x1970, 0x1974,
- 0x19e0, 0x19ff,
- 0x1d00, 0x1d6b,
- 0x1e00, 0x1e9b,
- 0x1ea0, 0x1ef9,
- 0x1f00, 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, 0x2054,
- 0x2057, 0x2057,
- 0x205f, 0x2063,
- 0x206a, 0x2071,
- 0x2074, 0x208e,
- 0x20a0, 0x20b1,
- 0x20d0, 0x20ea,
- 0x2100, 0x213b,
- 0x213d, 0x214b,
- 0x2153, 0x2183,
- 0x2190, 0x23d0,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x2617,
- 0x2619, 0x267d,
- 0x2680, 0x2691,
- 0x26a0, 0x26a1,
- 0x2701, 0x2704,
- 0x2706, 0x2709,
- 0x270c, 0x2727,
- 0x2729, 0x274b,
- 0x274d, 0x274d,
- 0x274f, 0x2752,
- 0x2756, 0x2756,
- 0x2758, 0x275e,
- 0x2761, 0x2794,
- 0x2798, 0x27af,
- 0x27b1, 0x27be,
- 0x27d0, 0x27eb,
- 0x27f0, 0x2b0d,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312c,
- 0x3131, 0x318e,
- 0x3190, 0x31b7,
- 0x31f0, 0x321e,
- 0x3220, 0x3243,
- 0x3250, 0x327d,
- 0x327f, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fa5,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xac00, 0xd7a3,
- 0xd800, 0xfa2d,
- 0xfa30, 0xfa6a,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdd0, 0xfdfd,
- 0xfe00, 0xfe0f,
- 0xfe20, 0xfe23,
- 0xfe30, 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, 0x1013f,
- 0x10300, 0x1031e,
- 0x10320, 0x10323,
- 0x10330, 0x1034a,
- 0x10380, 0x1039d,
- 0x1039f, 0x1039f,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x1083f,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d12a, 0x1d1dd,
- 0x1d300, 0x1d356,
- 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, 0x1d6a3,
- 0x1d6a8, 0x1d7c9,
- 0x1d7ce, 0x1d7ff,
- 0x1fffe, 0x2a6d6,
- 0x2f800, 0x2fa1d,
- 0x2fffe, 0x2ffff,
- 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_4_0 */
-
-/* 'Age_4_1': Derived Age 4.1 */
-static const OnigCodePoint CR_Age_4_1[] = {
- 430,
- 0x0000, 0x0241,
- 0x0250, 0x036f,
- 0x0374, 0x0375,
- 0x037a, 0x037a,
- 0x037e, 0x037e,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03ce,
- 0x03d0, 0x0486,
- 0x0488, 0x04ce,
- 0x04d0, 0x04f9,
- 0x0500, 0x050f,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x0591, 0x05b9,
- 0x05bb, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0600, 0x0603,
- 0x060b, 0x0615,
- 0x061b, 0x061b,
- 0x061e, 0x061f,
- 0x0621, 0x063a,
- 0x0640, 0x065e,
- 0x0660, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x076d,
- 0x0780, 0x07b1,
- 0x0901, 0x0939,
- 0x093c, 0x094d,
- 0x0950, 0x0954,
- 0x0958, 0x0970,
- 0x097d, 0x097d,
- 0x0981, 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, 0x09fa,
- 0x0a01, 0x0a03,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a3c, 0x0a3c,
- 0x0a3e, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4d,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a66, 0x0a74,
- 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, 0x0aef,
- 0x0af1, 0x0af1,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b43,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b61,
- 0x0b66, 0x0b71,
- 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,
- 0x0bd7, 0x0bd7,
- 0x0be6, 0x0bfa,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c3e, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c60, 0x0c61,
- 0x0c66, 0x0c6f,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce1,
- 0x0ce6, 0x0cef,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d28,
- 0x0d2a, 0x0d39,
- 0x0d3e, 0x0d43,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4d,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d61,
- 0x0d66, 0x0d6f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df4,
- 0x0e01, 0x0e3a,
- 0x0e3f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edd,
- 0x0f00, 0x0f47,
- 0x0f49, 0x0f6a,
- 0x0f71, 0x0f8b,
- 0x0f90, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fbe, 0x0fcc,
- 0x0fcf, 0x0fd1,
- 0x1000, 0x1021,
- 0x1023, 0x1027,
- 0x1029, 0x102a,
- 0x102c, 0x1032,
- 0x1036, 0x1039,
- 0x1040, 0x1059,
- 0x10a0, 0x10c5,
- 0x10d0, 0x10fc,
- 0x1100, 0x1159,
- 0x115f, 0x11a2,
- 0x11a8, 0x11f9,
- 0x1200, 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,
- 0x135f, 0x137c,
- 0x1380, 0x1399,
- 0x13a0, 0x13f4,
- 0x1401, 0x1676,
- 0x1680, 0x169c,
- 0x16a0, 0x16f0,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18a9,
- 0x1900, 0x191c,
- 0x1920, 0x192b,
- 0x1930, 0x193b,
- 0x1940, 0x1940,
- 0x1944, 0x196d,
- 0x1970, 0x1974,
- 0x1980, 0x19a9,
- 0x19b0, 0x19c9,
- 0x19d0, 0x19d9,
- 0x19de, 0x1a1b,
- 0x1a1e, 0x1a1f,
- 0x1d00, 0x1dc3,
- 0x1e00, 0x1e9b,
- 0x1ea0, 0x1ef9,
- 0x1f00, 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, 0x2063,
- 0x206a, 0x2071,
- 0x2074, 0x208e,
- 0x2090, 0x2094,
- 0x20a0, 0x20b5,
- 0x20d0, 0x20eb,
- 0x2100, 0x214c,
- 0x2153, 0x2183,
- 0x2190, 0x23db,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x269c,
- 0x26a0, 0x26b1,
- 0x2701, 0x2704,
- 0x2706, 0x2709,
- 0x270c, 0x2727,
- 0x2729, 0x274b,
- 0x274d, 0x274d,
- 0x274f, 0x2752,
- 0x2756, 0x2756,
- 0x2758, 0x275e,
- 0x2761, 0x2794,
- 0x2798, 0x27af,
- 0x27b1, 0x27be,
- 0x27c0, 0x27c6,
- 0x27d0, 0x27eb,
- 0x27f0, 0x2b13,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c80, 0x2cea,
- 0x2cf9, 0x2d25,
- 0x2d30, 0x2d65,
- 0x2d6f, 0x2d6f,
- 0x2d80, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0x2e00, 0x2e17,
- 0x2e1c, 0x2e1d,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312c,
- 0x3131, 0x318e,
- 0x3190, 0x31b7,
- 0x31c0, 0x31cf,
- 0x31f0, 0x321e,
- 0x3220, 0x3243,
- 0x3250, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fbb,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xa700, 0xa716,
- 0xa800, 0xa82b,
- 0xac00, 0xd7a3,
- 0xd800, 0xfa2d,
- 0xfa30, 0xfa6a,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdd0, 0xfdfd,
- 0xfe00, 0xfe19,
- 0xfe20, 0xfe23,
- 0xfe30, 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, 0x1018a,
- 0x10300, 0x1031e,
- 0x10320, 0x10323,
- 0x10330, 0x1034a,
- 0x10380, 0x1039d,
- 0x1039f, 0x103c3,
- 0x103c8, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x1083f,
- 0x10a00, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a47,
- 0x10a50, 0x10a58,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d12a, 0x1d1dd,
- 0x1d200, 0x1d245,
- 0x1d300, 0x1d356,
- 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, 0x1d7c9,
- 0x1d7ce, 0x1d7ff,
- 0x1fffe, 0x2a6d6,
- 0x2f800, 0x2fa1d,
- 0x2fffe, 0x2ffff,
- 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_4_1 */
-
-/* 'Age_5_0': Derived Age 5.0 */
-static const OnigCodePoint CR_Age_5_0[] = {
- 440,
- 0x0000, 0x036f,
- 0x0374, 0x0375,
- 0x037a, 0x037e,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x03ce,
- 0x03d0, 0x0486,
- 0x0488, 0x0513,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x0591, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0600, 0x0603,
- 0x060b, 0x0615,
- 0x061b, 0x061b,
- 0x061e, 0x061f,
- 0x0621, 0x063a,
- 0x0640, 0x065e,
- 0x0660, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x076d,
- 0x0780, 0x07b1,
- 0x07c0, 0x07fa,
- 0x0901, 0x0939,
- 0x093c, 0x094d,
- 0x0950, 0x0954,
- 0x0958, 0x0970,
- 0x097b, 0x097f,
- 0x0981, 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, 0x09fa,
- 0x0a01, 0x0a03,
- 0x0a05, 0x0a0a,
- 0x0a0f, 0x0a10,
- 0x0a13, 0x0a28,
- 0x0a2a, 0x0a30,
- 0x0a32, 0x0a33,
- 0x0a35, 0x0a36,
- 0x0a38, 0x0a39,
- 0x0a3c, 0x0a3c,
- 0x0a3e, 0x0a42,
- 0x0a47, 0x0a48,
- 0x0a4b, 0x0a4d,
- 0x0a59, 0x0a5c,
- 0x0a5e, 0x0a5e,
- 0x0a66, 0x0a74,
- 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, 0x0aef,
- 0x0af1, 0x0af1,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b43,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b61,
- 0x0b66, 0x0b71,
- 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,
- 0x0bd7, 0x0bd7,
- 0x0be6, 0x0bfa,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c3e, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c60, 0x0c61,
- 0x0c66, 0x0c6f,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d28,
- 0x0d2a, 0x0d39,
- 0x0d3e, 0x0d43,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4d,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d61,
- 0x0d66, 0x0d6f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df4,
- 0x0e01, 0x0e3a,
- 0x0e3f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edd,
- 0x0f00, 0x0f47,
- 0x0f49, 0x0f6a,
- 0x0f71, 0x0f8b,
- 0x0f90, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fbe, 0x0fcc,
- 0x0fcf, 0x0fd1,
- 0x1000, 0x1021,
- 0x1023, 0x1027,
- 0x1029, 0x102a,
- 0x102c, 0x1032,
- 0x1036, 0x1039,
- 0x1040, 0x1059,
- 0x10a0, 0x10c5,
- 0x10d0, 0x10fc,
- 0x1100, 0x1159,
- 0x115f, 0x11a2,
- 0x11a8, 0x11f9,
- 0x1200, 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,
- 0x135f, 0x137c,
- 0x1380, 0x1399,
- 0x13a0, 0x13f4,
- 0x1401, 0x1676,
- 0x1680, 0x169c,
- 0x16a0, 0x16f0,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18a9,
- 0x1900, 0x191c,
- 0x1920, 0x192b,
- 0x1930, 0x193b,
- 0x1940, 0x1940,
- 0x1944, 0x196d,
- 0x1970, 0x1974,
- 0x1980, 0x19a9,
- 0x19b0, 0x19c9,
- 0x19d0, 0x19d9,
- 0x19de, 0x1a1b,
- 0x1a1e, 0x1a1f,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b7c,
- 0x1d00, 0x1dca,
- 0x1dfe, 0x1e9b,
- 0x1ea0, 0x1ef9,
- 0x1f00, 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, 0x2063,
- 0x206a, 0x2071,
- 0x2074, 0x208e,
- 0x2090, 0x2094,
- 0x20a0, 0x20b5,
- 0x20d0, 0x20ef,
- 0x2100, 0x214e,
- 0x2153, 0x2184,
- 0x2190, 0x23e7,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x269c,
- 0x26a0, 0x26b2,
- 0x2701, 0x2704,
- 0x2706, 0x2709,
- 0x270c, 0x2727,
- 0x2729, 0x274b,
- 0x274d, 0x274d,
- 0x274f, 0x2752,
- 0x2756, 0x2756,
- 0x2758, 0x275e,
- 0x2761, 0x2794,
- 0x2798, 0x27af,
- 0x27b1, 0x27be,
- 0x27c0, 0x27ca,
- 0x27d0, 0x27eb,
- 0x27f0, 0x2b1a,
- 0x2b20, 0x2b23,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2c6c,
- 0x2c74, 0x2c77,
- 0x2c80, 0x2cea,
- 0x2cf9, 0x2d25,
- 0x2d30, 0x2d65,
- 0x2d6f, 0x2d6f,
- 0x2d80, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0x2e00, 0x2e17,
- 0x2e1c, 0x2e1d,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312c,
- 0x3131, 0x318e,
- 0x3190, 0x31b7,
- 0x31c0, 0x31cf,
- 0x31f0, 0x321e,
- 0x3220, 0x3243,
- 0x3250, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fbb,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xa700, 0xa71a,
- 0xa720, 0xa721,
- 0xa800, 0xa82b,
- 0xa840, 0xa877,
- 0xac00, 0xd7a3,
- 0xd800, 0xfa2d,
- 0xfa30, 0xfa6a,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdd0, 0xfdfd,
- 0xfe00, 0xfe19,
- 0xfe20, 0xfe23,
- 0xfe30, 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, 0x1018a,
- 0x10300, 0x1031e,
- 0x10320, 0x10323,
- 0x10330, 0x1034a,
- 0x10380, 0x1039d,
- 0x1039f, 0x103c3,
- 0x103c8, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x1083f,
- 0x10900, 0x10919,
- 0x1091f, 0x1091f,
- 0x10a00, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a47,
- 0x10a50, 0x10a58,
- 0x12000, 0x1236e,
- 0x12400, 0x12462,
- 0x12470, 0x12473,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d12a, 0x1d1dd,
- 0x1d200, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d360, 0x1d371,
- 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, 0x1d7ff,
- 0x1fffe, 0x2a6d6,
- 0x2f800, 0x2fa1d,
- 0x2fffe, 0x2ffff,
- 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_5_0 */
-
-/* 'Age_5_1': Derived Age 5.1 */
-static const OnigCodePoint CR_Age_5_1[] = {
- 455,
- 0x0000, 0x0377,
- 0x037a, 0x037e,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x0523,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x0591, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0600, 0x0603,
- 0x0606, 0x061b,
- 0x061e, 0x061f,
- 0x0621, 0x065e,
- 0x0660, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x07b1,
- 0x07c0, 0x07fa,
- 0x0901, 0x0939,
- 0x093c, 0x094d,
- 0x0950, 0x0954,
- 0x0958, 0x0972,
- 0x097b, 0x097f,
- 0x0981, 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, 0x09fa,
- 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, 0x0a75,
- 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, 0x0aef,
- 0x0af1, 0x0af1,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b63,
- 0x0b66, 0x0b71,
- 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,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c59,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c78, 0x0c7f,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d28,
- 0x0d2a, 0x0d39,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4d,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d63,
- 0x0d66, 0x0d75,
- 0x0d79, 0x0d7f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df4,
- 0x0e01, 0x0e3a,
- 0x0e3f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edd,
- 0x0f00, 0x0f47,
- 0x0f49, 0x0f6c,
- 0x0f71, 0x0f8b,
- 0x0f90, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fbe, 0x0fcc,
- 0x0fce, 0x0fd4,
- 0x1000, 0x1099,
- 0x109e, 0x10c5,
- 0x10d0, 0x10fc,
- 0x1100, 0x1159,
- 0x115f, 0x11a2,
- 0x11a8, 0x11f9,
- 0x1200, 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,
- 0x135f, 0x137c,
- 0x1380, 0x1399,
- 0x13a0, 0x13f4,
- 0x1401, 0x1676,
- 0x1680, 0x169c,
- 0x16a0, 0x16f0,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18aa,
- 0x1900, 0x191c,
- 0x1920, 0x192b,
- 0x1930, 0x193b,
- 0x1940, 0x1940,
- 0x1944, 0x196d,
- 0x1970, 0x1974,
- 0x1980, 0x19a9,
- 0x19b0, 0x19c9,
- 0x19d0, 0x19d9,
- 0x19de, 0x1a1b,
- 0x1a1e, 0x1a1f,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b7c,
- 0x1b80, 0x1baa,
- 0x1bae, 0x1bb9,
- 0x1c00, 0x1c37,
- 0x1c3b, 0x1c49,
- 0x1c4d, 0x1c7f,
- 0x1d00, 0x1de6,
- 0x1dfe, 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,
- 0x206a, 0x2071,
- 0x2074, 0x208e,
- 0x2090, 0x2094,
- 0x20a0, 0x20b5,
- 0x20d0, 0x20f0,
- 0x2100, 0x214f,
- 0x2153, 0x2188,
- 0x2190, 0x23e7,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x269d,
- 0x26a0, 0x26bc,
- 0x26c0, 0x26c3,
- 0x2701, 0x2704,
- 0x2706, 0x2709,
- 0x270c, 0x2727,
- 0x2729, 0x274b,
- 0x274d, 0x274d,
- 0x274f, 0x2752,
- 0x2756, 0x2756,
- 0x2758, 0x275e,
- 0x2761, 0x2794,
- 0x2798, 0x27af,
- 0x27b1, 0x27be,
- 0x27c0, 0x27ca,
- 0x27cc, 0x27cc,
- 0x27d0, 0x2b4c,
- 0x2b50, 0x2b54,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2c6f,
- 0x2c71, 0x2c7d,
- 0x2c80, 0x2cea,
- 0x2cf9, 0x2d25,
- 0x2d30, 0x2d65,
- 0x2d6f, 0x2d6f,
- 0x2d80, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0x2de0, 0x2e30,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x3190, 0x31b7,
- 0x31c0, 0x31e3,
- 0x31f0, 0x321e,
- 0x3220, 0x3243,
- 0x3250, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fc3,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xa500, 0xa62b,
- 0xa640, 0xa65f,
- 0xa662, 0xa673,
- 0xa67c, 0xa697,
- 0xa700, 0xa78c,
- 0xa7fb, 0xa82b,
- 0xa840, 0xa877,
- 0xa880, 0xa8c4,
- 0xa8ce, 0xa8d9,
- 0xa900, 0xa953,
- 0xa95f, 0xa95f,
- 0xaa00, 0xaa36,
- 0xaa40, 0xaa4d,
- 0xaa50, 0xaa59,
- 0xaa5c, 0xaa5f,
- 0xac00, 0xd7a3,
- 0xd800, 0xfa2d,
- 0xfa30, 0xfa6a,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdd0, 0xfdfd,
- 0xfe00, 0xfe19,
- 0xfe20, 0xfe26,
- 0xfe30, 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, 0x1018a,
- 0x10190, 0x1019b,
- 0x101d0, 0x101fd,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x10300, 0x1031e,
- 0x10320, 0x10323,
- 0x10330, 0x1034a,
- 0x10380, 0x1039d,
- 0x1039f, 0x103c3,
- 0x103c8, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x1083f,
- 0x10900, 0x10919,
- 0x1091f, 0x10939,
- 0x1093f, 0x1093f,
- 0x10a00, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a47,
- 0x10a50, 0x10a58,
- 0x12000, 0x1236e,
- 0x12400, 0x12462,
- 0x12470, 0x12473,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d1dd,
- 0x1d200, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d360, 0x1d371,
- 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, 0x1d7ff,
- 0x1f000, 0x1f02b,
- 0x1f030, 0x1f093,
- 0x1fffe, 0x2a6d6,
- 0x2f800, 0x2fa1d,
- 0x2fffe, 0x2ffff,
- 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_5_1 */
-
-/* 'Age_5_2': Derived Age 5.2 */
-static const OnigCodePoint CR_Age_5_2[] = {
- 495,
- 0x0000, 0x0377,
- 0x037a, 0x037e,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x0525,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x0591, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0600, 0x0603,
- 0x0606, 0x061b,
- 0x061e, 0x061f,
- 0x0621, 0x065e,
- 0x0660, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x07b1,
- 0x07c0, 0x07fa,
- 0x0800, 0x082d,
- 0x0830, 0x083e,
- 0x0900, 0x0939,
- 0x093c, 0x094e,
- 0x0950, 0x0955,
- 0x0958, 0x0972,
- 0x0979, 0x097f,
- 0x0981, 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, 0x09fb,
- 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, 0x0a75,
- 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, 0x0aef,
- 0x0af1, 0x0af1,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 0x0b57,
- 0x0b5c, 0x0b5d,
- 0x0b5f, 0x0b63,
- 0x0b66, 0x0b71,
- 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,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c59,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c78, 0x0c7f,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d28,
- 0x0d2a, 0x0d39,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4d,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d63,
- 0x0d66, 0x0d75,
- 0x0d79, 0x0d7f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df4,
- 0x0e01, 0x0e3a,
- 0x0e3f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edd,
- 0x0f00, 0x0f47,
- 0x0f49, 0x0f6c,
- 0x0f71, 0x0f8b,
- 0x0f90, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fbe, 0x0fcc,
- 0x0fce, 0x0fd8,
- 0x1000, 0x10c5,
- 0x10d0, 0x10fc,
- 0x1100, 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,
- 0x135f, 0x137c,
- 0x1380, 0x1399,
- 0x13a0, 0x13f4,
- 0x1400, 0x169c,
- 0x16a0, 0x16f0,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18aa,
- 0x18b0, 0x18f5,
- 0x1900, 0x191c,
- 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,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b7c,
- 0x1b80, 0x1baa,
- 0x1bae, 0x1bb9,
- 0x1c00, 0x1c37,
- 0x1c3b, 0x1c49,
- 0x1c4d, 0x1c7f,
- 0x1cd0, 0x1cf2,
- 0x1d00, 0x1de6,
- 0x1dfd, 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,
- 0x206a, 0x2071,
- 0x2074, 0x208e,
- 0x2090, 0x2094,
- 0x20a0, 0x20b8,
- 0x20d0, 0x20f0,
- 0x2100, 0x2189,
- 0x2190, 0x23e8,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x26cd,
- 0x26cf, 0x26e1,
- 0x26e3, 0x26e3,
- 0x26e8, 0x26ff,
- 0x2701, 0x2704,
- 0x2706, 0x2709,
- 0x270c, 0x2727,
- 0x2729, 0x274b,
- 0x274d, 0x274d,
- 0x274f, 0x2752,
- 0x2756, 0x275e,
- 0x2761, 0x2794,
- 0x2798, 0x27af,
- 0x27b1, 0x27be,
- 0x27c0, 0x27ca,
- 0x27cc, 0x27cc,
- 0x27d0, 0x2b4c,
- 0x2b50, 0x2b59,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2cf1,
- 0x2cf9, 0x2d25,
- 0x2d30, 0x2d65,
- 0x2d6f, 0x2d6f,
- 0x2d80, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0x2de0, 0x2e31,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x3190, 0x31b7,
- 0x31c0, 0x31e3,
- 0x31f0, 0x321e,
- 0x3220, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fcb,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xa4d0, 0xa62b,
- 0xa640, 0xa65f,
- 0xa662, 0xa673,
- 0xa67c, 0xa697,
- 0xa6a0, 0xa6f7,
- 0xa700, 0xa78c,
- 0xa7fb, 0xa82b,
- 0xa830, 0xa839,
- 0xa840, 0xa877,
- 0xa880, 0xa8c4,
- 0xa8ce, 0xa8d9,
- 0xa8e0, 0xa8fb,
- 0xa900, 0xa953,
- 0xa95f, 0xa97c,
- 0xa980, 0xa9cd,
- 0xa9cf, 0xa9d9,
- 0xa9de, 0xa9df,
- 0xaa00, 0xaa36,
- 0xaa40, 0xaa4d,
- 0xaa50, 0xaa59,
- 0xaa5c, 0xaa7b,
- 0xaa80, 0xaac2,
- 0xaadb, 0xaadf,
- 0xabc0, 0xabed,
- 0xabf0, 0xabf9,
- 0xac00, 0xd7a3,
- 0xd7b0, 0xd7c6,
- 0xd7cb, 0xd7fb,
- 0xd800, 0xfa2d,
- 0xfa30, 0xfa6d,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbb1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdd0, 0xfdfd,
- 0xfe00, 0xfe19,
- 0xfe20, 0xfe26,
- 0xfe30, 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, 0x1018a,
- 0x10190, 0x1019b,
- 0x101d0, 0x101fd,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x10300, 0x1031e,
- 0x10320, 0x10323,
- 0x10330, 0x1034a,
- 0x10380, 0x1039d,
- 0x1039f, 0x103c3,
- 0x103c8, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x10855,
- 0x10857, 0x1085f,
- 0x10900, 0x1091b,
- 0x1091f, 0x10939,
- 0x1093f, 0x1093f,
- 0x10a00, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a47,
- 0x10a50, 0x10a58,
- 0x10a60, 0x10a7f,
- 0x10b00, 0x10b35,
- 0x10b39, 0x10b55,
- 0x10b58, 0x10b72,
- 0x10b78, 0x10b7f,
- 0x10c00, 0x10c48,
- 0x10e60, 0x10e7e,
- 0x11080, 0x110c1,
- 0x12000, 0x1236e,
- 0x12400, 0x12462,
- 0x12470, 0x12473,
- 0x13000, 0x1342e,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d1dd,
- 0x1d200, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d360, 0x1d371,
- 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, 0x1d7ff,
- 0x1f000, 0x1f02b,
- 0x1f030, 0x1f093,
- 0x1f100, 0x1f10a,
- 0x1f110, 0x1f12e,
- 0x1f131, 0x1f131,
- 0x1f13d, 0x1f13d,
- 0x1f13f, 0x1f13f,
- 0x1f142, 0x1f142,
- 0x1f146, 0x1f146,
- 0x1f14a, 0x1f14e,
- 0x1f157, 0x1f157,
- 0x1f15f, 0x1f15f,
- 0x1f179, 0x1f179,
- 0x1f17b, 0x1f17c,
- 0x1f17f, 0x1f17f,
- 0x1f18a, 0x1f18d,
- 0x1f190, 0x1f190,
- 0x1f200, 0x1f200,
- 0x1f210, 0x1f231,
- 0x1f240, 0x1f248,
- 0x1fffe, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2f800, 0x2fa1d,
- 0x2fffe, 0x2ffff,
- 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_5_2 */
-
-/* 'Age_6_0': Derived Age 6.0 */
-static const OnigCodePoint CR_Age_6_0[] = {
- 511,
- 0x0000, 0x0377,
- 0x037a, 0x037e,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x0527,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x0591, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0600, 0x0603,
- 0x0606, 0x061b,
- 0x061e, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x07b1,
- 0x07c0, 0x07fa,
- 0x0800, 0x082d,
- 0x0830, 0x083e,
- 0x0840, 0x085b,
- 0x085e, 0x085e,
- 0x0900, 0x0977,
- 0x0979, 0x097f,
- 0x0981, 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, 0x09fb,
- 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, 0x0a75,
- 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, 0x0aef,
- 0x0af1, 0x0af1,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 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,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c59,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c78, 0x0c7f,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4e,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d63,
- 0x0d66, 0x0d75,
- 0x0d79, 0x0d7f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df4,
- 0x0e01, 0x0e3a,
- 0x0e3f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 0x0ed0, 0x0ed9,
- 0x0edc, 0x0edd,
- 0x0f00, 0x0f47,
- 0x0f49, 0x0f6c,
- 0x0f71, 0x0f97,
- 0x0f99, 0x0fbc,
- 0x0fbe, 0x0fcc,
- 0x0fce, 0x0fda,
- 0x1000, 0x10c5,
- 0x10d0, 0x10fc,
- 0x1100, 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, 0x13f4,
- 0x1400, 0x169c,
- 0x16a0, 0x16f0,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18aa,
- 0x18b0, 0x18f5,
- 0x1900, 0x191c,
- 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,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b7c,
- 0x1b80, 0x1baa,
- 0x1bae, 0x1bb9,
- 0x1bc0, 0x1bf3,
- 0x1bfc, 0x1c37,
- 0x1c3b, 0x1c49,
- 0x1c4d, 0x1c7f,
- 0x1cd0, 0x1cf2,
- 0x1d00, 0x1de6,
- 0x1dfc, 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,
- 0x206a, 0x2071,
- 0x2074, 0x208e,
- 0x2090, 0x209c,
- 0x20a0, 0x20b9,
- 0x20d0, 0x20f0,
- 0x2100, 0x2189,
- 0x2190, 0x23f3,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x26ff,
- 0x2701, 0x27ca,
- 0x27cc, 0x27cc,
- 0x27ce, 0x2b4c,
- 0x2b50, 0x2b59,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 0x2cf1,
- 0x2cf9, 0x2d25,
- 0x2d30, 0x2d65,
- 0x2d6f, 0x2d70,
- 0x2d7f, 0x2d96,
- 0x2da0, 0x2da6,
- 0x2da8, 0x2dae,
- 0x2db0, 0x2db6,
- 0x2db8, 0x2dbe,
- 0x2dc0, 0x2dc6,
- 0x2dc8, 0x2dce,
- 0x2dd0, 0x2dd6,
- 0x2dd8, 0x2dde,
- 0x2de0, 0x2e31,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x3190, 0x31ba,
- 0x31c0, 0x31e3,
- 0x31f0, 0x321e,
- 0x3220, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fcb,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xa4d0, 0xa62b,
- 0xa640, 0xa673,
- 0xa67c, 0xa697,
- 0xa6a0, 0xa6f7,
- 0xa700, 0xa78e,
- 0xa790, 0xa791,
- 0xa7a0, 0xa7a9,
- 0xa7fa, 0xa82b,
- 0xa830, 0xa839,
- 0xa840, 0xa877,
- 0xa880, 0xa8c4,
- 0xa8ce, 0xa8d9,
- 0xa8e0, 0xa8fb,
- 0xa900, 0xa953,
- 0xa95f, 0xa97c,
- 0xa980, 0xa9cd,
- 0xa9cf, 0xa9d9,
- 0xa9de, 0xa9df,
- 0xaa00, 0xaa36,
- 0xaa40, 0xaa4d,
- 0xaa50, 0xaa59,
- 0xaa5c, 0xaa7b,
- 0xaa80, 0xaac2,
- 0xaadb, 0xaadf,
- 0xab01, 0xab06,
- 0xab09, 0xab0e,
- 0xab11, 0xab16,
- 0xab20, 0xab26,
- 0xab28, 0xab2e,
- 0xabc0, 0xabed,
- 0xabf0, 0xabf9,
- 0xac00, 0xd7a3,
- 0xd7b0, 0xd7c6,
- 0xd7cb, 0xd7fb,
- 0xd800, 0xfa2d,
- 0xfa30, 0xfa6d,
- 0xfa70, 0xfad9,
- 0xfb00, 0xfb06,
- 0xfb13, 0xfb17,
- 0xfb1d, 0xfb36,
- 0xfb38, 0xfb3c,
- 0xfb3e, 0xfb3e,
- 0xfb40, 0xfb41,
- 0xfb43, 0xfb44,
- 0xfb46, 0xfbc1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdd0, 0xfdfd,
- 0xfe00, 0xfe19,
- 0xfe20, 0xfe26,
- 0xfe30, 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, 0x1018a,
- 0x10190, 0x1019b,
- 0x101d0, 0x101fd,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x10300, 0x1031e,
- 0x10320, 0x10323,
- 0x10330, 0x1034a,
- 0x10380, 0x1039d,
- 0x1039f, 0x103c3,
- 0x103c8, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x10855,
- 0x10857, 0x1085f,
- 0x10900, 0x1091b,
- 0x1091f, 0x10939,
- 0x1093f, 0x1093f,
- 0x10a00, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a47,
- 0x10a50, 0x10a58,
- 0x10a60, 0x10a7f,
- 0x10b00, 0x10b35,
- 0x10b39, 0x10b55,
- 0x10b58, 0x10b72,
- 0x10b78, 0x10b7f,
- 0x10c00, 0x10c48,
- 0x10e60, 0x10e7e,
- 0x11000, 0x1104d,
- 0x11052, 0x1106f,
- 0x11080, 0x110c1,
- 0x12000, 0x1236e,
- 0x12400, 0x12462,
- 0x12470, 0x12473,
- 0x13000, 0x1342e,
- 0x16800, 0x16a38,
- 0x1b000, 0x1b001,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d1dd,
- 0x1d200, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d360, 0x1d371,
- 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, 0x1d7ff,
- 0x1f000, 0x1f02b,
- 0x1f030, 0x1f093,
- 0x1f0a0, 0x1f0ae,
- 0x1f0b1, 0x1f0be,
- 0x1f0c1, 0x1f0cf,
- 0x1f0d1, 0x1f0df,
- 0x1f100, 0x1f10a,
- 0x1f110, 0x1f12e,
- 0x1f130, 0x1f169,
- 0x1f170, 0x1f19a,
- 0x1f1e6, 0x1f202,
- 0x1f210, 0x1f23a,
- 0x1f240, 0x1f248,
- 0x1f250, 0x1f251,
- 0x1f300, 0x1f320,
- 0x1f330, 0x1f335,
- 0x1f337, 0x1f37c,
- 0x1f380, 0x1f393,
- 0x1f3a0, 0x1f3c4,
- 0x1f3c6, 0x1f3ca,
- 0x1f3e0, 0x1f3f0,
- 0x1f400, 0x1f43e,
- 0x1f440, 0x1f440,
- 0x1f442, 0x1f4f7,
- 0x1f4f9, 0x1f4fc,
- 0x1f500, 0x1f53d,
- 0x1f550, 0x1f567,
- 0x1f5fb, 0x1f5ff,
- 0x1f601, 0x1f610,
- 0x1f612, 0x1f614,
- 0x1f616, 0x1f616,
- 0x1f618, 0x1f618,
- 0x1f61a, 0x1f61a,
- 0x1f61c, 0x1f61e,
- 0x1f620, 0x1f625,
- 0x1f628, 0x1f62b,
- 0x1f62d, 0x1f62d,
- 0x1f630, 0x1f633,
- 0x1f635, 0x1f640,
- 0x1f645, 0x1f64f,
- 0x1f680, 0x1f6c5,
- 0x1f700, 0x1f773,
- 0x1fffe, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2f800, 0x2fa1d,
- 0x2fffe, 0x2ffff,
- 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_6_0 */
-
-/* 'Age_6_1': Derived Age 6.1 */
-static const OnigCodePoint CR_Age_6_1[] = {
- 549,
- 0x0000, 0x0377,
- 0x037a, 0x037e,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x0527,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x058f, 0x058f,
- 0x0591, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0600, 0x0604,
- 0x0606, 0x061b,
- 0x061e, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x07b1,
- 0x07c0, 0x07fa,
- 0x0800, 0x082d,
- 0x0830, 0x083e,
- 0x0840, 0x085b,
- 0x085e, 0x085e,
- 0x08a0, 0x08a0,
- 0x08a2, 0x08ac,
- 0x08e4, 0x08fe,
- 0x0900, 0x0977,
- 0x0979, 0x097f,
- 0x0981, 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, 0x09fb,
- 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, 0x0a75,
- 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,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 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,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c59,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c78, 0x0c7f,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4e,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d63,
- 0x0d66, 0x0d75,
- 0x0d79, 0x0d7f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df4,
- 0x0e01, 0x0e3a,
- 0x0e3f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 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, 0x13f4,
- 0x1400, 0x169c,
- 0x16a0, 0x16f0,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18aa,
- 0x18b0, 0x18f5,
- 0x1900, 0x191c,
- 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,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b7c,
- 0x1b80, 0x1bf3,
- 0x1bfc, 0x1c37,
- 0x1c3b, 0x1c49,
- 0x1c4d, 0x1c7f,
- 0x1cc0, 0x1cc7,
- 0x1cd0, 0x1cf6,
- 0x1d00, 0x1de6,
- 0x1dfc, 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,
- 0x206a, 0x2071,
- 0x2074, 0x208e,
- 0x2090, 0x209c,
- 0x20a0, 0x20b9,
- 0x20d0, 0x20f0,
- 0x2100, 0x2189,
- 0x2190, 0x23f3,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x26ff,
- 0x2701, 0x2b4c,
- 0x2b50, 0x2b59,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 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, 0x2e3b,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x3190, 0x31ba,
- 0x31c0, 0x31e3,
- 0x31f0, 0x321e,
- 0x3220, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fcc,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xa4d0, 0xa62b,
- 0xa640, 0xa697,
- 0xa69f, 0xa6f7,
- 0xa700, 0xa78e,
- 0xa790, 0xa793,
- 0xa7a0, 0xa7aa,
- 0xa7f8, 0xa82b,
- 0xa830, 0xa839,
- 0xa840, 0xa877,
- 0xa880, 0xa8c4,
- 0xa8ce, 0xa8d9,
- 0xa8e0, 0xa8fb,
- 0xa900, 0xa953,
- 0xa95f, 0xa97c,
- 0xa980, 0xa9cd,
- 0xa9cf, 0xa9d9,
- 0xa9de, 0xa9df,
- 0xaa00, 0xaa36,
- 0xaa40, 0xaa4d,
- 0xaa50, 0xaa59,
- 0xaa5c, 0xaa7b,
- 0xaa80, 0xaac2,
- 0xaadb, 0xaaf6,
- 0xab01, 0xab06,
- 0xab09, 0xab0e,
- 0xab11, 0xab16,
- 0xab20, 0xab26,
- 0xab28, 0xab2e,
- 0xabc0, 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, 0xfbc1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdd0, 0xfdfd,
- 0xfe00, 0xfe19,
- 0xfe20, 0xfe26,
- 0xfe30, 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, 0x1018a,
- 0x10190, 0x1019b,
- 0x101d0, 0x101fd,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x10300, 0x1031e,
- 0x10320, 0x10323,
- 0x10330, 0x1034a,
- 0x10380, 0x1039d,
- 0x1039f, 0x103c3,
- 0x103c8, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x10855,
- 0x10857, 0x1085f,
- 0x10900, 0x1091b,
- 0x1091f, 0x10939,
- 0x1093f, 0x1093f,
- 0x10980, 0x109b7,
- 0x109be, 0x109bf,
- 0x10a00, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a47,
- 0x10a50, 0x10a58,
- 0x10a60, 0x10a7f,
- 0x10b00, 0x10b35,
- 0x10b39, 0x10b55,
- 0x10b58, 0x10b72,
- 0x10b78, 0x10b7f,
- 0x10c00, 0x10c48,
- 0x10e60, 0x10e7e,
- 0x11000, 0x1104d,
- 0x11052, 0x1106f,
- 0x11080, 0x110c1,
- 0x110d0, 0x110e8,
- 0x110f0, 0x110f9,
- 0x11100, 0x11134,
- 0x11136, 0x11143,
- 0x11180, 0x111c8,
- 0x111d0, 0x111d9,
- 0x11680, 0x116b7,
- 0x116c0, 0x116c9,
- 0x12000, 0x1236e,
- 0x12400, 0x12462,
- 0x12470, 0x12473,
- 0x13000, 0x1342e,
- 0x16800, 0x16a38,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f8f, 0x16f9f,
- 0x1b000, 0x1b001,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d1dd,
- 0x1d200, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d360, 0x1d371,
- 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, 0x1d7ff,
- 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, 0x1f0be,
- 0x1f0c1, 0x1f0cf,
- 0x1f0d1, 0x1f0df,
- 0x1f100, 0x1f10a,
- 0x1f110, 0x1f12e,
- 0x1f130, 0x1f16b,
- 0x1f170, 0x1f19a,
- 0x1f1e6, 0x1f202,
- 0x1f210, 0x1f23a,
- 0x1f240, 0x1f248,
- 0x1f250, 0x1f251,
- 0x1f300, 0x1f320,
- 0x1f330, 0x1f335,
- 0x1f337, 0x1f37c,
- 0x1f380, 0x1f393,
- 0x1f3a0, 0x1f3c4,
- 0x1f3c6, 0x1f3ca,
- 0x1f3e0, 0x1f3f0,
- 0x1f400, 0x1f43e,
- 0x1f440, 0x1f440,
- 0x1f442, 0x1f4f7,
- 0x1f4f9, 0x1f4fc,
- 0x1f500, 0x1f53d,
- 0x1f540, 0x1f543,
- 0x1f550, 0x1f567,
- 0x1f5fb, 0x1f640,
- 0x1f645, 0x1f64f,
- 0x1f680, 0x1f6c5,
- 0x1f700, 0x1f773,
- 0x1fffe, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2f800, 0x2fa1d,
- 0x2fffe, 0x2ffff,
- 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_6_1 */
-
-/* 'Age_6_2': Derived Age 6.2 */
-static const OnigCodePoint CR_Age_6_2[] = {
- 549,
- 0x0000, 0x0377,
- 0x037a, 0x037e,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x0527,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x058f, 0x058f,
- 0x0591, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0600, 0x0604,
- 0x0606, 0x061b,
- 0x061e, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x07b1,
- 0x07c0, 0x07fa,
- 0x0800, 0x082d,
- 0x0830, 0x083e,
- 0x0840, 0x085b,
- 0x085e, 0x085e,
- 0x08a0, 0x08a0,
- 0x08a2, 0x08ac,
- 0x08e4, 0x08fe,
- 0x0900, 0x0977,
- 0x0979, 0x097f,
- 0x0981, 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, 0x09fb,
- 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, 0x0a75,
- 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,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 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,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c59,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c78, 0x0c7f,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4e,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d63,
- 0x0d66, 0x0d75,
- 0x0d79, 0x0d7f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df4,
- 0x0e01, 0x0e3a,
- 0x0e3f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 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, 0x13f4,
- 0x1400, 0x169c,
- 0x16a0, 0x16f0,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18aa,
- 0x18b0, 0x18f5,
- 0x1900, 0x191c,
- 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,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b7c,
- 0x1b80, 0x1bf3,
- 0x1bfc, 0x1c37,
- 0x1c3b, 0x1c49,
- 0x1c4d, 0x1c7f,
- 0x1cc0, 0x1cc7,
- 0x1cd0, 0x1cf6,
- 0x1d00, 0x1de6,
- 0x1dfc, 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,
- 0x206a, 0x2071,
- 0x2074, 0x208e,
- 0x2090, 0x209c,
- 0x20a0, 0x20ba,
- 0x20d0, 0x20f0,
- 0x2100, 0x2189,
- 0x2190, 0x23f3,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x26ff,
- 0x2701, 0x2b4c,
- 0x2b50, 0x2b59,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 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, 0x2e3b,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x3190, 0x31ba,
- 0x31c0, 0x31e3,
- 0x31f0, 0x321e,
- 0x3220, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fcc,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xa4d0, 0xa62b,
- 0xa640, 0xa697,
- 0xa69f, 0xa6f7,
- 0xa700, 0xa78e,
- 0xa790, 0xa793,
- 0xa7a0, 0xa7aa,
- 0xa7f8, 0xa82b,
- 0xa830, 0xa839,
- 0xa840, 0xa877,
- 0xa880, 0xa8c4,
- 0xa8ce, 0xa8d9,
- 0xa8e0, 0xa8fb,
- 0xa900, 0xa953,
- 0xa95f, 0xa97c,
- 0xa980, 0xa9cd,
- 0xa9cf, 0xa9d9,
- 0xa9de, 0xa9df,
- 0xaa00, 0xaa36,
- 0xaa40, 0xaa4d,
- 0xaa50, 0xaa59,
- 0xaa5c, 0xaa7b,
- 0xaa80, 0xaac2,
- 0xaadb, 0xaaf6,
- 0xab01, 0xab06,
- 0xab09, 0xab0e,
- 0xab11, 0xab16,
- 0xab20, 0xab26,
- 0xab28, 0xab2e,
- 0xabc0, 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, 0xfbc1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdd0, 0xfdfd,
- 0xfe00, 0xfe19,
- 0xfe20, 0xfe26,
- 0xfe30, 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, 0x1018a,
- 0x10190, 0x1019b,
- 0x101d0, 0x101fd,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x10300, 0x1031e,
- 0x10320, 0x10323,
- 0x10330, 0x1034a,
- 0x10380, 0x1039d,
- 0x1039f, 0x103c3,
- 0x103c8, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x10855,
- 0x10857, 0x1085f,
- 0x10900, 0x1091b,
- 0x1091f, 0x10939,
- 0x1093f, 0x1093f,
- 0x10980, 0x109b7,
- 0x109be, 0x109bf,
- 0x10a00, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a47,
- 0x10a50, 0x10a58,
- 0x10a60, 0x10a7f,
- 0x10b00, 0x10b35,
- 0x10b39, 0x10b55,
- 0x10b58, 0x10b72,
- 0x10b78, 0x10b7f,
- 0x10c00, 0x10c48,
- 0x10e60, 0x10e7e,
- 0x11000, 0x1104d,
- 0x11052, 0x1106f,
- 0x11080, 0x110c1,
- 0x110d0, 0x110e8,
- 0x110f0, 0x110f9,
- 0x11100, 0x11134,
- 0x11136, 0x11143,
- 0x11180, 0x111c8,
- 0x111d0, 0x111d9,
- 0x11680, 0x116b7,
- 0x116c0, 0x116c9,
- 0x12000, 0x1236e,
- 0x12400, 0x12462,
- 0x12470, 0x12473,
- 0x13000, 0x1342e,
- 0x16800, 0x16a38,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f8f, 0x16f9f,
- 0x1b000, 0x1b001,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d1dd,
- 0x1d200, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d360, 0x1d371,
- 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, 0x1d7ff,
- 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, 0x1f0be,
- 0x1f0c1, 0x1f0cf,
- 0x1f0d1, 0x1f0df,
- 0x1f100, 0x1f10a,
- 0x1f110, 0x1f12e,
- 0x1f130, 0x1f16b,
- 0x1f170, 0x1f19a,
- 0x1f1e6, 0x1f202,
- 0x1f210, 0x1f23a,
- 0x1f240, 0x1f248,
- 0x1f250, 0x1f251,
- 0x1f300, 0x1f320,
- 0x1f330, 0x1f335,
- 0x1f337, 0x1f37c,
- 0x1f380, 0x1f393,
- 0x1f3a0, 0x1f3c4,
- 0x1f3c6, 0x1f3ca,
- 0x1f3e0, 0x1f3f0,
- 0x1f400, 0x1f43e,
- 0x1f440, 0x1f440,
- 0x1f442, 0x1f4f7,
- 0x1f4f9, 0x1f4fc,
- 0x1f500, 0x1f53d,
- 0x1f540, 0x1f543,
- 0x1f550, 0x1f567,
- 0x1f5fb, 0x1f640,
- 0x1f645, 0x1f64f,
- 0x1f680, 0x1f6c5,
- 0x1f700, 0x1f773,
- 0x1fffe, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2f800, 0x2fa1d,
- 0x2fffe, 0x2ffff,
- 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_6_2 */
-
-/* 'Age_6_3': Derived Age 6.3 */
-static const OnigCodePoint CR_Age_6_3[] = {
- 549,
- 0x0000, 0x0377,
- 0x037a, 0x037e,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x0527,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x058f, 0x058f,
- 0x0591, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0600, 0x0604,
- 0x0606, 0x061c,
- 0x061e, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x07b1,
- 0x07c0, 0x07fa,
- 0x0800, 0x082d,
- 0x0830, 0x083e,
- 0x0840, 0x085b,
- 0x085e, 0x085e,
- 0x08a0, 0x08a0,
- 0x08a2, 0x08ac,
- 0x08e4, 0x08fe,
- 0x0900, 0x0977,
- 0x0979, 0x097f,
- 0x0981, 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, 0x09fb,
- 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, 0x0a75,
- 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,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 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,
- 0x0c01, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c33,
- 0x0c35, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c59,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c78, 0x0c7f,
- 0x0c82, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d02, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4e,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d63,
- 0x0d66, 0x0d75,
- 0x0d79, 0x0d7f,
- 0x0d82, 0x0d83,
- 0x0d85, 0x0d96,
- 0x0d9a, 0x0db1,
- 0x0db3, 0x0dbb,
- 0x0dbd, 0x0dbd,
- 0x0dc0, 0x0dc6,
- 0x0dca, 0x0dca,
- 0x0dcf, 0x0dd4,
- 0x0dd6, 0x0dd6,
- 0x0dd8, 0x0ddf,
- 0x0df2, 0x0df4,
- 0x0e01, 0x0e3a,
- 0x0e3f, 0x0e5b,
- 0x0e81, 0x0e82,
- 0x0e84, 0x0e84,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 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, 0x13f4,
- 0x1400, 0x169c,
- 0x16a0, 0x16f0,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 0x1880, 0x18aa,
- 0x18b0, 0x18f5,
- 0x1900, 0x191c,
- 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,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b7c,
- 0x1b80, 0x1bf3,
- 0x1bfc, 0x1c37,
- 0x1c3b, 0x1c49,
- 0x1c4d, 0x1c7f,
- 0x1cc0, 0x1cc7,
- 0x1cd0, 0x1cf6,
- 0x1d00, 0x1de6,
- 0x1dfc, 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, 0x20ba,
- 0x20d0, 0x20f0,
- 0x2100, 0x2189,
- 0x2190, 0x23f3,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x26ff,
- 0x2701, 0x2b4c,
- 0x2b50, 0x2b59,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 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, 0x2e3b,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x3190, 0x31ba,
- 0x31c0, 0x31e3,
- 0x31f0, 0x321e,
- 0x3220, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fcc,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xa4d0, 0xa62b,
- 0xa640, 0xa697,
- 0xa69f, 0xa6f7,
- 0xa700, 0xa78e,
- 0xa790, 0xa793,
- 0xa7a0, 0xa7aa,
- 0xa7f8, 0xa82b,
- 0xa830, 0xa839,
- 0xa840, 0xa877,
- 0xa880, 0xa8c4,
- 0xa8ce, 0xa8d9,
- 0xa8e0, 0xa8fb,
- 0xa900, 0xa953,
- 0xa95f, 0xa97c,
- 0xa980, 0xa9cd,
- 0xa9cf, 0xa9d9,
- 0xa9de, 0xa9df,
- 0xaa00, 0xaa36,
- 0xaa40, 0xaa4d,
- 0xaa50, 0xaa59,
- 0xaa5c, 0xaa7b,
- 0xaa80, 0xaac2,
- 0xaadb, 0xaaf6,
- 0xab01, 0xab06,
- 0xab09, 0xab0e,
- 0xab11, 0xab16,
- 0xab20, 0xab26,
- 0xab28, 0xab2e,
- 0xabc0, 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, 0xfbc1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdd0, 0xfdfd,
- 0xfe00, 0xfe19,
- 0xfe20, 0xfe26,
- 0xfe30, 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, 0x1018a,
- 0x10190, 0x1019b,
- 0x101d0, 0x101fd,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x10300, 0x1031e,
- 0x10320, 0x10323,
- 0x10330, 0x1034a,
- 0x10380, 0x1039d,
- 0x1039f, 0x103c3,
- 0x103c8, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x10855,
- 0x10857, 0x1085f,
- 0x10900, 0x1091b,
- 0x1091f, 0x10939,
- 0x1093f, 0x1093f,
- 0x10980, 0x109b7,
- 0x109be, 0x109bf,
- 0x10a00, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a47,
- 0x10a50, 0x10a58,
- 0x10a60, 0x10a7f,
- 0x10b00, 0x10b35,
- 0x10b39, 0x10b55,
- 0x10b58, 0x10b72,
- 0x10b78, 0x10b7f,
- 0x10c00, 0x10c48,
- 0x10e60, 0x10e7e,
- 0x11000, 0x1104d,
- 0x11052, 0x1106f,
- 0x11080, 0x110c1,
- 0x110d0, 0x110e8,
- 0x110f0, 0x110f9,
- 0x11100, 0x11134,
- 0x11136, 0x11143,
- 0x11180, 0x111c8,
- 0x111d0, 0x111d9,
- 0x11680, 0x116b7,
- 0x116c0, 0x116c9,
- 0x12000, 0x1236e,
- 0x12400, 0x12462,
- 0x12470, 0x12473,
- 0x13000, 0x1342e,
- 0x16800, 0x16a38,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f8f, 0x16f9f,
- 0x1b000, 0x1b001,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d1dd,
- 0x1d200, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d360, 0x1d371,
- 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, 0x1d7ff,
- 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, 0x1f0be,
- 0x1f0c1, 0x1f0cf,
- 0x1f0d1, 0x1f0df,
- 0x1f100, 0x1f10a,
- 0x1f110, 0x1f12e,
- 0x1f130, 0x1f16b,
- 0x1f170, 0x1f19a,
- 0x1f1e6, 0x1f202,
- 0x1f210, 0x1f23a,
- 0x1f240, 0x1f248,
- 0x1f250, 0x1f251,
- 0x1f300, 0x1f320,
- 0x1f330, 0x1f335,
- 0x1f337, 0x1f37c,
- 0x1f380, 0x1f393,
- 0x1f3a0, 0x1f3c4,
- 0x1f3c6, 0x1f3ca,
- 0x1f3e0, 0x1f3f0,
- 0x1f400, 0x1f43e,
- 0x1f440, 0x1f440,
- 0x1f442, 0x1f4f7,
- 0x1f4f9, 0x1f4fc,
- 0x1f500, 0x1f53d,
- 0x1f540, 0x1f543,
- 0x1f550, 0x1f567,
- 0x1f5fb, 0x1f640,
- 0x1f645, 0x1f64f,
- 0x1f680, 0x1f6c5,
- 0x1f700, 0x1f773,
- 0x1fffe, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2f800, 0x2fa1d,
- 0x2fffe, 0x2ffff,
- 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_6_3 */
-
-/* 'Age_7_0': Derived Age 7.0 */
-static const OnigCodePoint CR_Age_7_0[] = {
- 610,
- 0x0000, 0x0377,
- 0x037a, 0x037f,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x052f,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x058d, 0x058f,
- 0x0591, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0600, 0x061c,
- 0x061e, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x07b1,
- 0x07c0, 0x07fa,
- 0x0800, 0x082d,
- 0x0830, 0x083e,
- 0x0840, 0x085b,
- 0x085e, 0x085e,
- 0x08a0, 0x08b2,
- 0x08e4, 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, 0x09fb,
- 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, 0x0a75,
- 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,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 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, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c59,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c78, 0x0c7f,
- 0x0c81, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d01, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4e,
- 0x0d57, 0x0d57,
- 0x0d60, 0x0d63,
- 0x0d66, 0x0d75,
- 0x0d79, 0x0d7f,
- 0x0d82, 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,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 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, 0x13f4,
- 0x1400, 0x169c,
- 0x16a0, 0x16f8,
- 0x1700, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 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, 0x1abe,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b7c,
- 0x1b80, 0x1bf3,
- 0x1bfc, 0x1c37,
- 0x1c3b, 0x1c49,
- 0x1c4d, 0x1c7f,
- 0x1cc0, 0x1cc7,
- 0x1cd0, 0x1cf6,
- 0x1cf8, 0x1cf9,
- 0x1d00, 0x1df5,
- 0x1dfc, 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, 0x20bd,
- 0x20d0, 0x20f0,
- 0x2100, 0x2189,
- 0x2190, 0x23fa,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x2b73,
- 0x2b76, 0x2b95,
- 0x2b98, 0x2bb9,
- 0x2bbd, 0x2bc8,
- 0x2bca, 0x2bd1,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 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, 0x2e42,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x3190, 0x31ba,
- 0x31c0, 0x31e3,
- 0x31f0, 0x321e,
- 0x3220, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fcc,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xa4d0, 0xa62b,
- 0xa640, 0xa69d,
- 0xa69f, 0xa6f7,
- 0xa700, 0xa78e,
- 0xa790, 0xa7ad,
- 0xa7b0, 0xa7b1,
- 0xa7f7, 0xa82b,
- 0xa830, 0xa839,
- 0xa840, 0xa877,
- 0xa880, 0xa8c4,
- 0xa8ce, 0xa8d9,
- 0xa8e0, 0xa8fb,
- 0xa900, 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, 0xab5f,
- 0xab64, 0xab65,
- 0xabc0, 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, 0xfbc1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdd0, 0xfdfd,
- 0xfe00, 0xfe19,
- 0xfe20, 0xfe2d,
- 0xfe30, 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, 0x1018c,
- 0x10190, 0x1019b,
- 0x101a0, 0x101a0,
- 0x101d0, 0x101fd,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x102e0, 0x102fb,
- 0x10300, 0x10323,
- 0x10330, 0x1034a,
- 0x10350, 0x1037a,
- 0x10380, 0x1039d,
- 0x1039f, 0x103c3,
- 0x103c8, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x1056f, 0x1056f,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 0x10800, 0x10805,
- 0x10808, 0x10808,
- 0x1080a, 0x10835,
- 0x10837, 0x10838,
- 0x1083c, 0x1083c,
- 0x1083f, 0x10855,
- 0x10857, 0x1089e,
- 0x108a7, 0x108af,
- 0x10900, 0x1091b,
- 0x1091f, 0x10939,
- 0x1093f, 0x1093f,
- 0x10980, 0x109b7,
- 0x109be, 0x109bf,
- 0x10a00, 0x10a03,
- 0x10a05, 0x10a06,
- 0x10a0c, 0x10a13,
- 0x10a15, 0x10a17,
- 0x10a19, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a47,
- 0x10a50, 0x10a58,
- 0x10a60, 0x10a9f,
- 0x10ac0, 0x10ae6,
- 0x10aeb, 0x10af6,
- 0x10b00, 0x10b35,
- 0x10b39, 0x10b55,
- 0x10b58, 0x10b72,
- 0x10b78, 0x10b91,
- 0x10b99, 0x10b9c,
- 0x10ba9, 0x10baf,
- 0x10c00, 0x10c48,
- 0x10e60, 0x10e7e,
- 0x11000, 0x1104d,
- 0x11052, 0x1106f,
- 0x1107f, 0x110c1,
- 0x110d0, 0x110e8,
- 0x110f0, 0x110f9,
- 0x11100, 0x11134,
- 0x11136, 0x11143,
- 0x11150, 0x11176,
- 0x11180, 0x111c8,
- 0x111cd, 0x111cd,
- 0x111d0, 0x111da,
- 0x111e1, 0x111f4,
- 0x11200, 0x11211,
- 0x11213, 0x1123d,
- 0x112b0, 0x112ea,
- 0x112f0, 0x112f9,
- 0x11301, 0x11303,
- 0x11305, 0x1130c,
- 0x1130f, 0x11310,
- 0x11313, 0x11328,
- 0x1132a, 0x11330,
- 0x11332, 0x11333,
- 0x11335, 0x11339,
- 0x1133c, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134d,
- 0x11357, 0x11357,
- 0x1135d, 0x11363,
- 0x11366, 0x1136c,
- 0x11370, 0x11374,
- 0x11480, 0x114c7,
- 0x114d0, 0x114d9,
- 0x11580, 0x115b5,
- 0x115b8, 0x115c9,
- 0x11600, 0x11644,
- 0x11650, 0x11659,
- 0x11680, 0x116b7,
- 0x116c0, 0x116c9,
- 0x118a0, 0x118f2,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x12000, 0x12398,
- 0x12400, 0x1246e,
- 0x12470, 0x12474,
- 0x13000, 0x1342e,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16a60, 0x16a69,
- 0x16a6e, 0x16a6f,
- 0x16ad0, 0x16aed,
- 0x16af0, 0x16af5,
- 0x16b00, 0x16b45,
- 0x16b50, 0x16b59,
- 0x16b5b, 0x16b61,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f8f, 0x16f9f,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 0x1bc9c, 0x1bca3,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d1dd,
- 0x1d200, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d360, 0x1d371,
- 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, 0x1d7ff,
- 0x1e800, 0x1e8c4,
- 0x1e8c7, 0x1e8d6,
- 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, 0x1f10c,
- 0x1f110, 0x1f12e,
- 0x1f130, 0x1f16b,
- 0x1f170, 0x1f19a,
- 0x1f1e6, 0x1f202,
- 0x1f210, 0x1f23a,
- 0x1f240, 0x1f248,
- 0x1f250, 0x1f251,
- 0x1f300, 0x1f32c,
- 0x1f330, 0x1f37d,
- 0x1f380, 0x1f3ce,
- 0x1f3d4, 0x1f3f7,
- 0x1f400, 0x1f4fe,
- 0x1f500, 0x1f54a,
- 0x1f550, 0x1f579,
- 0x1f57b, 0x1f5a3,
- 0x1f5a5, 0x1f642,
- 0x1f645, 0x1f6cf,
- 0x1f6e0, 0x1f6ec,
- 0x1f6f0, 0x1f6f3,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d4,
- 0x1f800, 0x1f80b,
- 0x1f810, 0x1f847,
- 0x1f850, 0x1f859,
- 0x1f860, 0x1f887,
- 0x1f890, 0x1f8ad,
- 0x1fffe, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2f800, 0x2fa1d,
- 0x2fffe, 0x2ffff,
- 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_7_0 */
-
-/* 'Age_8_0': Derived Age 8.0 */
-static const OnigCodePoint CR_Age_8_0[] = {
- 623,
- 0x0000, 0x0377,
- 0x037a, 0x037f,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x052f,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x058d, 0x058f,
- 0x0591, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0600, 0x061c,
- 0x061e, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x07b1,
- 0x07c0, 0x07fa,
- 0x0800, 0x082d,
- 0x0830, 0x083e,
- 0x0840, 0x085b,
- 0x085e, 0x085e,
- 0x08a0, 0x08b4,
- 0x08e3, 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, 0x09fb,
- 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, 0x0a75,
- 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, 0x0af9,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 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, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c78, 0x0c7f,
- 0x0c81, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d01, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4e,
- 0x0d57, 0x0d57,
- 0x0d5f, 0x0d63,
- 0x0d66, 0x0d75,
- 0x0d79, 0x0d7f,
- 0x0d82, 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,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 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, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 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, 0x1abe,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b7c,
- 0x1b80, 0x1bf3,
- 0x1bfc, 0x1c37,
- 0x1c3b, 0x1c49,
- 0x1c4d, 0x1c7f,
- 0x1cc0, 0x1cc7,
- 0x1cd0, 0x1cf6,
- 0x1cf8, 0x1cf9,
- 0x1d00, 0x1df5,
- 0x1dfc, 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, 0x20be,
- 0x20d0, 0x20f0,
- 0x2100, 0x218b,
- 0x2190, 0x23fa,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x2b73,
- 0x2b76, 0x2b95,
- 0x2b98, 0x2bb9,
- 0x2bbd, 0x2bc8,
- 0x2bca, 0x2bd1,
- 0x2bec, 0x2bef,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 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, 0x2e42,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x3190, 0x31ba,
- 0x31c0, 0x31e3,
- 0x31f0, 0x321e,
- 0x3220, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fd5,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xa4d0, 0xa62b,
- 0xa640, 0xa6f7,
- 0xa700, 0xa7ad,
- 0xa7b0, 0xa7b7,
- 0xa7f7, 0xa82b,
- 0xa830, 0xa839,
- 0xa840, 0xa877,
- 0xa880, 0xa8c4,
- 0xa8ce, 0xa8d9,
- 0xa8e0, 0xa8fd,
- 0xa900, 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, 0xab65,
- 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, 0xfbc1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdd0, 0xfdfd,
- 0xfe00, 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, 0x1018c,
- 0x10190, 0x1019b,
- 0x101a0, 0x101a0,
- 0x101d0, 0x101fd,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x102e0, 0x102fb,
- 0x10300, 0x10323,
- 0x10330, 0x1034a,
- 0x10350, 0x1037a,
- 0x10380, 0x1039d,
- 0x1039f, 0x103c3,
- 0x103c8, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x1056f, 0x1056f,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 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, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a47,
- 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, 0x10cff,
- 0x10e60, 0x10e7e,
- 0x11000, 0x1104d,
- 0x11052, 0x1106f,
- 0x1107f, 0x110c1,
- 0x110d0, 0x110e8,
- 0x110f0, 0x110f9,
- 0x11100, 0x11134,
- 0x11136, 0x11143,
- 0x11150, 0x11176,
- 0x11180, 0x111cd,
- 0x111d0, 0x111df,
- 0x111e1, 0x111f4,
- 0x11200, 0x11211,
- 0x11213, 0x1123d,
- 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,
- 0x1133c, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134d,
- 0x11350, 0x11350,
- 0x11357, 0x11357,
- 0x1135d, 0x11363,
- 0x11366, 0x1136c,
- 0x11370, 0x11374,
- 0x11480, 0x114c7,
- 0x114d0, 0x114d9,
- 0x11580, 0x115b5,
- 0x115b8, 0x115dd,
- 0x11600, 0x11644,
- 0x11650, 0x11659,
- 0x11680, 0x116b7,
- 0x116c0, 0x116c9,
- 0x11700, 0x11719,
- 0x1171d, 0x1172b,
- 0x11730, 0x1173f,
- 0x118a0, 0x118f2,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x12000, 0x12399,
- 0x12400, 0x1246e,
- 0x12470, 0x12474,
- 0x12480, 0x12543,
- 0x13000, 0x1342e,
- 0x14400, 0x14646,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16a60, 0x16a69,
- 0x16a6e, 0x16a6f,
- 0x16ad0, 0x16aed,
- 0x16af0, 0x16af5,
- 0x16b00, 0x16b45,
- 0x16b50, 0x16b59,
- 0x16b5b, 0x16b61,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f8f, 0x16f9f,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 0x1bc9c, 0x1bca3,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d1e8,
- 0x1d200, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d360, 0x1d371,
- 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,
- 0x1e800, 0x1e8c4,
- 0x1e8c7, 0x1e8d6,
- 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, 0x1f10c,
- 0x1f110, 0x1f12e,
- 0x1f130, 0x1f16b,
- 0x1f170, 0x1f19a,
- 0x1f1e6, 0x1f202,
- 0x1f210, 0x1f23a,
- 0x1f240, 0x1f248,
- 0x1f250, 0x1f251,
- 0x1f300, 0x1f579,
- 0x1f57b, 0x1f5a3,
- 0x1f5a5, 0x1f6d0,
- 0x1f6e0, 0x1f6ec,
- 0x1f6f0, 0x1f6f3,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d4,
- 0x1f800, 0x1f80b,
- 0x1f810, 0x1f847,
- 0x1f850, 0x1f859,
- 0x1f860, 0x1f887,
- 0x1f890, 0x1f8ad,
- 0x1f910, 0x1f918,
- 0x1f980, 0x1f984,
- 0x1f9c0, 0x1f9c0,
- 0x1fffe, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
- 0x2fffe, 0x2ffff,
- 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_8_0 */
-
-/* 'Age_9_0': Derived Age 9.0 */
-static const OnigCodePoint CR_Age_9_0[] = {
- 648,
- 0x0000, 0x0377,
- 0x037a, 0x037f,
- 0x0384, 0x038a,
- 0x038c, 0x038c,
- 0x038e, 0x03a1,
- 0x03a3, 0x052f,
- 0x0531, 0x0556,
- 0x0559, 0x055f,
- 0x0561, 0x0587,
- 0x0589, 0x058a,
- 0x058d, 0x058f,
- 0x0591, 0x05c7,
- 0x05d0, 0x05ea,
- 0x05f0, 0x05f4,
- 0x0600, 0x061c,
- 0x061e, 0x070d,
- 0x070f, 0x074a,
- 0x074d, 0x07b1,
- 0x07c0, 0x07fa,
- 0x0800, 0x082d,
- 0x0830, 0x083e,
- 0x0840, 0x085b,
- 0x085e, 0x085e,
- 0x08a0, 0x08b4,
- 0x08b6, 0x08bd,
- 0x08d4, 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, 0x09fb,
- 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, 0x0a75,
- 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, 0x0af9,
- 0x0b01, 0x0b03,
- 0x0b05, 0x0b0c,
- 0x0b0f, 0x0b10,
- 0x0b13, 0x0b28,
- 0x0b2a, 0x0b30,
- 0x0b32, 0x0b33,
- 0x0b35, 0x0b39,
- 0x0b3c, 0x0b44,
- 0x0b47, 0x0b48,
- 0x0b4b, 0x0b4d,
- 0x0b56, 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, 0x0c03,
- 0x0c05, 0x0c0c,
- 0x0c0e, 0x0c10,
- 0x0c12, 0x0c28,
- 0x0c2a, 0x0c39,
- 0x0c3d, 0x0c44,
- 0x0c46, 0x0c48,
- 0x0c4a, 0x0c4d,
- 0x0c55, 0x0c56,
- 0x0c58, 0x0c5a,
- 0x0c60, 0x0c63,
- 0x0c66, 0x0c6f,
- 0x0c78, 0x0c83,
- 0x0c85, 0x0c8c,
- 0x0c8e, 0x0c90,
- 0x0c92, 0x0ca8,
- 0x0caa, 0x0cb3,
- 0x0cb5, 0x0cb9,
- 0x0cbc, 0x0cc4,
- 0x0cc6, 0x0cc8,
- 0x0cca, 0x0ccd,
- 0x0cd5, 0x0cd6,
- 0x0cde, 0x0cde,
- 0x0ce0, 0x0ce3,
- 0x0ce6, 0x0cef,
- 0x0cf1, 0x0cf2,
- 0x0d01, 0x0d03,
- 0x0d05, 0x0d0c,
- 0x0d0e, 0x0d10,
- 0x0d12, 0x0d3a,
- 0x0d3d, 0x0d44,
- 0x0d46, 0x0d48,
- 0x0d4a, 0x0d4f,
- 0x0d54, 0x0d63,
- 0x0d66, 0x0d7f,
- 0x0d82, 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,
- 0x0e87, 0x0e88,
- 0x0e8a, 0x0e8a,
- 0x0e8d, 0x0e8d,
- 0x0e94, 0x0e97,
- 0x0e99, 0x0e9f,
- 0x0ea1, 0x0ea3,
- 0x0ea5, 0x0ea5,
- 0x0ea7, 0x0ea7,
- 0x0eaa, 0x0eab,
- 0x0ead, 0x0eb9,
- 0x0ebb, 0x0ebd,
- 0x0ec0, 0x0ec4,
- 0x0ec6, 0x0ec6,
- 0x0ec8, 0x0ecd,
- 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, 0x170c,
- 0x170e, 0x1714,
- 0x1720, 0x1736,
- 0x1740, 0x1753,
- 0x1760, 0x176c,
- 0x176e, 0x1770,
- 0x1772, 0x1773,
- 0x1780, 0x17dd,
- 0x17e0, 0x17e9,
- 0x17f0, 0x17f9,
- 0x1800, 0x180e,
- 0x1810, 0x1819,
- 0x1820, 0x1877,
- 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, 0x1abe,
- 0x1b00, 0x1b4b,
- 0x1b50, 0x1b7c,
- 0x1b80, 0x1bf3,
- 0x1bfc, 0x1c37,
- 0x1c3b, 0x1c49,
- 0x1c4d, 0x1c88,
- 0x1cc0, 0x1cc7,
- 0x1cd0, 0x1cf6,
- 0x1cf8, 0x1cf9,
- 0x1d00, 0x1df5,
- 0x1dfb, 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, 0x20be,
- 0x20d0, 0x20f0,
- 0x2100, 0x218b,
- 0x2190, 0x23fe,
- 0x2400, 0x2426,
- 0x2440, 0x244a,
- 0x2460, 0x2b73,
- 0x2b76, 0x2b95,
- 0x2b98, 0x2bb9,
- 0x2bbd, 0x2bc8,
- 0x2bca, 0x2bd1,
- 0x2bec, 0x2bef,
- 0x2c00, 0x2c2e,
- 0x2c30, 0x2c5e,
- 0x2c60, 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, 0x2e44,
- 0x2e80, 0x2e99,
- 0x2e9b, 0x2ef3,
- 0x2f00, 0x2fd5,
- 0x2ff0, 0x2ffb,
- 0x3000, 0x303f,
- 0x3041, 0x3096,
- 0x3099, 0x30ff,
- 0x3105, 0x312d,
- 0x3131, 0x318e,
- 0x3190, 0x31ba,
- 0x31c0, 0x31e3,
- 0x31f0, 0x321e,
- 0x3220, 0x32fe,
- 0x3300, 0x4db5,
- 0x4dc0, 0x9fd5,
- 0xa000, 0xa48c,
- 0xa490, 0xa4c6,
- 0xa4d0, 0xa62b,
- 0xa640, 0xa6f7,
- 0xa700, 0xa7ae,
- 0xa7b0, 0xa7b7,
- 0xa7f7, 0xa82b,
- 0xa830, 0xa839,
- 0xa840, 0xa877,
- 0xa880, 0xa8c5,
- 0xa8ce, 0xa8d9,
- 0xa8e0, 0xa8fd,
- 0xa900, 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, 0xab65,
- 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, 0xfbc1,
- 0xfbd3, 0xfd3f,
- 0xfd50, 0xfd8f,
- 0xfd92, 0xfdc7,
- 0xfdd0, 0xfdfd,
- 0xfe00, 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, 0x1019b,
- 0x101a0, 0x101a0,
- 0x101d0, 0x101fd,
- 0x10280, 0x1029c,
- 0x102a0, 0x102d0,
- 0x102e0, 0x102fb,
- 0x10300, 0x10323,
- 0x10330, 0x1034a,
- 0x10350, 0x1037a,
- 0x10380, 0x1039d,
- 0x1039f, 0x103c3,
- 0x103c8, 0x103d5,
- 0x10400, 0x1049d,
- 0x104a0, 0x104a9,
- 0x104b0, 0x104d3,
- 0x104d8, 0x104fb,
- 0x10500, 0x10527,
- 0x10530, 0x10563,
- 0x1056f, 0x1056f,
- 0x10600, 0x10736,
- 0x10740, 0x10755,
- 0x10760, 0x10767,
- 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, 0x10a33,
- 0x10a38, 0x10a3a,
- 0x10a3f, 0x10a47,
- 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, 0x10cff,
- 0x10e60, 0x10e7e,
- 0x11000, 0x1104d,
- 0x11052, 0x1106f,
- 0x1107f, 0x110c1,
- 0x110d0, 0x110e8,
- 0x110f0, 0x110f9,
- 0x11100, 0x11134,
- 0x11136, 0x11143,
- 0x11150, 0x11176,
- 0x11180, 0x111cd,
- 0x111d0, 0x111df,
- 0x111e1, 0x111f4,
- 0x11200, 0x11211,
- 0x11213, 0x1123e,
- 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,
- 0x1133c, 0x11344,
- 0x11347, 0x11348,
- 0x1134b, 0x1134d,
- 0x11350, 0x11350,
- 0x11357, 0x11357,
- 0x1135d, 0x11363,
- 0x11366, 0x1136c,
- 0x11370, 0x11374,
- 0x11400, 0x11459,
- 0x1145b, 0x1145b,
- 0x1145d, 0x1145d,
- 0x11480, 0x114c7,
- 0x114d0, 0x114d9,
- 0x11580, 0x115b5,
- 0x115b8, 0x115dd,
- 0x11600, 0x11644,
- 0x11650, 0x11659,
- 0x11660, 0x1166c,
- 0x11680, 0x116b7,
- 0x116c0, 0x116c9,
- 0x11700, 0x11719,
- 0x1171d, 0x1172b,
- 0x11730, 0x1173f,
- 0x118a0, 0x118f2,
- 0x118ff, 0x118ff,
- 0x11ac0, 0x11af8,
- 0x11c00, 0x11c08,
- 0x11c0a, 0x11c36,
- 0x11c38, 0x11c45,
- 0x11c50, 0x11c6c,
- 0x11c70, 0x11c8f,
- 0x11c92, 0x11ca7,
- 0x11ca9, 0x11cb6,
- 0x12000, 0x12399,
- 0x12400, 0x1246e,
- 0x12470, 0x12474,
- 0x12480, 0x12543,
- 0x13000, 0x1342e,
- 0x14400, 0x14646,
- 0x16800, 0x16a38,
- 0x16a40, 0x16a5e,
- 0x16a60, 0x16a69,
- 0x16a6e, 0x16a6f,
- 0x16ad0, 0x16aed,
- 0x16af0, 0x16af5,
- 0x16b00, 0x16b45,
- 0x16b50, 0x16b59,
- 0x16b5b, 0x16b61,
- 0x16b63, 0x16b77,
- 0x16b7d, 0x16b8f,
- 0x16f00, 0x16f44,
- 0x16f50, 0x16f7e,
- 0x16f8f, 0x16f9f,
- 0x16fe0, 0x16fe0,
- 0x17000, 0x187ec,
- 0x18800, 0x18af2,
- 0x1b000, 0x1b001,
- 0x1bc00, 0x1bc6a,
- 0x1bc70, 0x1bc7c,
- 0x1bc80, 0x1bc88,
- 0x1bc90, 0x1bc99,
- 0x1bc9c, 0x1bca3,
- 0x1d000, 0x1d0f5,
- 0x1d100, 0x1d126,
- 0x1d129, 0x1d1e8,
- 0x1d200, 0x1d245,
- 0x1d300, 0x1d356,
- 0x1d360, 0x1d371,
- 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,
- 0x1e000, 0x1e006,
- 0x1e008, 0x1e018,
- 0x1e01b, 0x1e021,
- 0x1e023, 0x1e024,
- 0x1e026, 0x1e02a,
- 0x1e800, 0x1e8c4,
- 0x1e8c7, 0x1e8d6,
- 0x1e900, 0x1e94a,
- 0x1e950, 0x1e959,
- 0x1e95e, 0x1e95f,
- 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, 0x1f10c,
- 0x1f110, 0x1f12e,
- 0x1f130, 0x1f16b,
- 0x1f170, 0x1f1ac,
- 0x1f1e6, 0x1f202,
- 0x1f210, 0x1f23b,
- 0x1f240, 0x1f248,
- 0x1f250, 0x1f251,
- 0x1f300, 0x1f6d2,
- 0x1f6e0, 0x1f6ec,
- 0x1f6f0, 0x1f6f6,
- 0x1f700, 0x1f773,
- 0x1f780, 0x1f7d4,
- 0x1f800, 0x1f80b,
- 0x1f810, 0x1f847,
- 0x1f850, 0x1f859,
- 0x1f860, 0x1f887,
- 0x1f890, 0x1f8ad,
- 0x1f910, 0x1f91e,
- 0x1f920, 0x1f927,
- 0x1f930, 0x1f930,
- 0x1f933, 0x1f93e,
- 0x1f940, 0x1f94b,
- 0x1f950, 0x1f95e,
- 0x1f980, 0x1f991,
- 0x1f9c0, 0x1f9c0,
- 0x1fffe, 0x2a6d6,
- 0x2a700, 0x2b734,
- 0x2b740, 0x2b81d,
- 0x2b820, 0x2cea1,
- 0x2f800, 0x2fa1d,
- 0x2fffe, 0x2ffff,
- 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_9_0 */
-
-#endif /* USE_UNICODE_AGE_PROPERTIES */
-/* 'In_Basic_Latin': Block */
-#define CR_In_Basic_Latin CR_ASCII
-
-/* 'In_Latin_1_Supplement': Block */
-static const OnigCodePoint CR_In_Latin_1_Supplement[] = {
- 1,
- 0x0080, 0x00ff,
-}; /* CR_In_Latin_1_Supplement */
-
-/* 'In_Latin_Extended_A': Block */
-static const OnigCodePoint CR_In_Latin_Extended_A[] = {
- 1,
- 0x0100, 0x017f,
-}; /* CR_In_Latin_Extended_A */
-
-/* 'In_Latin_Extended_B': Block */
-static const OnigCodePoint CR_In_Latin_Extended_B[] = {
- 1,
- 0x0180, 0x024f,
-}; /* CR_In_Latin_Extended_B */
-
-/* 'In_IPA_Extensions': Block */
-static const OnigCodePoint CR_In_IPA_Extensions[] = {
- 1,
- 0x0250, 0x02af,
-}; /* CR_In_IPA_Extensions */
-
-/* 'In_Spacing_Modifier_Letters': Block */
-static const OnigCodePoint CR_In_Spacing_Modifier_Letters[] = {
- 1,
- 0x02b0, 0x02ff,
-}; /* CR_In_Spacing_Modifier_Letters */
-
-/* 'In_Combining_Diacritical_Marks': Block */
-static const OnigCodePoint CR_In_Combining_Diacritical_Marks[] = {
- 1,
- 0x0300, 0x036f,
-}; /* CR_In_Combining_Diacritical_Marks */
-
-/* 'In_Greek_and_Coptic': Block */
-static const OnigCodePoint CR_In_Greek_and_Coptic[] = {
- 1,
- 0x0370, 0x03ff,
-}; /* CR_In_Greek_and_Coptic */
-
-/* 'In_Cyrillic': Block */
-static const OnigCodePoint CR_In_Cyrillic[] = {
- 1,
- 0x0400, 0x04ff,
-}; /* CR_In_Cyrillic */
-
-/* 'In_Cyrillic_Supplement': Block */
-static const OnigCodePoint CR_In_Cyrillic_Supplement[] = {
- 1,
- 0x0500, 0x052f,
-}; /* CR_In_Cyrillic_Supplement */
-
-/* 'In_Armenian': Block */
-static const OnigCodePoint CR_In_Armenian[] = {
- 1,
- 0x0530, 0x058f,
-}; /* CR_In_Armenian */
-
-/* 'In_Hebrew': Block */
-static const OnigCodePoint CR_In_Hebrew[] = {
- 1,
- 0x0590, 0x05ff,
-}; /* CR_In_Hebrew */
-
-/* 'In_Arabic': Block */
-static const OnigCodePoint CR_In_Arabic[] = {
- 1,
- 0x0600, 0x06ff,
-}; /* CR_In_Arabic */
-
-/* 'In_Syriac': Block */
-static const OnigCodePoint CR_In_Syriac[] = {
- 1,
- 0x0700, 0x074f,
-}; /* CR_In_Syriac */
-
-/* 'In_Arabic_Supplement': Block */
-static const OnigCodePoint CR_In_Arabic_Supplement[] = {
- 1,
- 0x0750, 0x077f,
-}; /* CR_In_Arabic_Supplement */
-
-/* 'In_Thaana': Block */
-static const OnigCodePoint CR_In_Thaana[] = {
- 1,
- 0x0780, 0x07bf,
-}; /* CR_In_Thaana */
-
-/* 'In_NKo': Block */
-static const OnigCodePoint CR_In_NKo[] = {
- 1,
- 0x07c0, 0x07ff,
-}; /* CR_In_NKo */
-
-/* 'In_Samaritan': Block */
-static const OnigCodePoint CR_In_Samaritan[] = {
- 1,
- 0x0800, 0x083f,
-}; /* CR_In_Samaritan */
-
-/* 'In_Mandaic': Block */
-static const OnigCodePoint CR_In_Mandaic[] = {
- 1,
- 0x0840, 0x085f,
-}; /* CR_In_Mandaic */
-
-/* 'In_Arabic_Extended_A': Block */
-static const OnigCodePoint CR_In_Arabic_Extended_A[] = {
- 1,
- 0x08a0, 0x08ff,
-}; /* CR_In_Arabic_Extended_A */
-
-/* 'In_Devanagari': Block */
-static const OnigCodePoint CR_In_Devanagari[] = {
- 1,
- 0x0900, 0x097f,
-}; /* CR_In_Devanagari */
-
-/* 'In_Bengali': Block */
-static const OnigCodePoint CR_In_Bengali[] = {
- 1,
- 0x0980, 0x09ff,
-}; /* CR_In_Bengali */
-
-/* 'In_Gurmukhi': Block */
-static const OnigCodePoint CR_In_Gurmukhi[] = {
- 1,
- 0x0a00, 0x0a7f,
-}; /* CR_In_Gurmukhi */
-
-/* 'In_Gujarati': Block */
-static const OnigCodePoint CR_In_Gujarati[] = {
- 1,
- 0x0a80, 0x0aff,
-}; /* CR_In_Gujarati */
-
-/* 'In_Oriya': Block */
-static const OnigCodePoint CR_In_Oriya[] = {
- 1,
- 0x0b00, 0x0b7f,
-}; /* CR_In_Oriya */
-
-/* 'In_Tamil': Block */
-static const OnigCodePoint CR_In_Tamil[] = {
- 1,
- 0x0b80, 0x0bff,
-}; /* CR_In_Tamil */
-
-/* 'In_Telugu': Block */
-static const OnigCodePoint CR_In_Telugu[] = {
- 1,
- 0x0c00, 0x0c7f,
-}; /* CR_In_Telugu */
-
-/* 'In_Kannada': Block */
-static const OnigCodePoint CR_In_Kannada[] = {
- 1,
- 0x0c80, 0x0cff,
-}; /* CR_In_Kannada */
-
-/* 'In_Malayalam': Block */
-static const OnigCodePoint CR_In_Malayalam[] = {
- 1,
- 0x0d00, 0x0d7f,
-}; /* CR_In_Malayalam */
-
-/* 'In_Sinhala': Block */
-static const OnigCodePoint CR_In_Sinhala[] = {
- 1,
- 0x0d80, 0x0dff,
-}; /* CR_In_Sinhala */
-
-/* 'In_Thai': Block */
-static const OnigCodePoint CR_In_Thai[] = {
- 1,
- 0x0e00, 0x0e7f,
-}; /* CR_In_Thai */
-
-/* 'In_Lao': Block */
-static const OnigCodePoint CR_In_Lao[] = {
- 1,
- 0x0e80, 0x0eff,
-}; /* CR_In_Lao */
-
-/* 'In_Tibetan': Block */
-static const OnigCodePoint CR_In_Tibetan[] = {
- 1,
- 0x0f00, 0x0fff,
-}; /* CR_In_Tibetan */
-
-/* 'In_Myanmar': Block */
-static const OnigCodePoint CR_In_Myanmar[] = {
- 1,
- 0x1000, 0x109f,
-}; /* CR_In_Myanmar */
-
-/* 'In_Georgian': Block */
-static const OnigCodePoint CR_In_Georgian[] = {
- 1,
- 0x10a0, 0x10ff,
-}; /* CR_In_Georgian */
-
-/* 'In_Hangul_Jamo': Block */
-static const OnigCodePoint CR_In_Hangul_Jamo[] = {
- 1,
- 0x1100, 0x11ff,
-}; /* CR_In_Hangul_Jamo */
-
-/* 'In_Ethiopic': Block */
-static const OnigCodePoint CR_In_Ethiopic[] = {
- 1,
- 0x1200, 0x137f,
-}; /* CR_In_Ethiopic */
-
-/* 'In_Ethiopic_Supplement': Block */
-static const OnigCodePoint CR_In_Ethiopic_Supplement[] = {
- 1,
- 0x1380, 0x139f,
-}; /* CR_In_Ethiopic_Supplement */
-
-/* 'In_Cherokee': Block */
-static const OnigCodePoint CR_In_Cherokee[] = {
- 1,
- 0x13a0, 0x13ff,
-}; /* CR_In_Cherokee */
-
-/* 'In_Unified_Canadian_Aboriginal_Syllabics': Block */
-static const OnigCodePoint CR_In_Unified_Canadian_Aboriginal_Syllabics[] = {
- 1,
- 0x1400, 0x167f,
-}; /* CR_In_Unified_Canadian_Aboriginal_Syllabics */
-
-/* 'In_Ogham': Block */
-static const OnigCodePoint CR_In_Ogham[] = {
- 1,
- 0x1680, 0x169f,
-}; /* CR_In_Ogham */
-
-/* 'In_Runic': Block */
-static const OnigCodePoint CR_In_Runic[] = {
- 1,
- 0x16a0, 0x16ff,
-}; /* CR_In_Runic */
-
-/* 'In_Tagalog': Block */
-static const OnigCodePoint CR_In_Tagalog[] = {
- 1,
- 0x1700, 0x171f,
-}; /* CR_In_Tagalog */
-
-/* 'In_Hanunoo': Block */
-static const OnigCodePoint CR_In_Hanunoo[] = {
- 1,
- 0x1720, 0x173f,
-}; /* CR_In_Hanunoo */
-
-/* 'In_Buhid': Block */
-static const OnigCodePoint CR_In_Buhid[] = {
- 1,
- 0x1740, 0x175f,
-}; /* CR_In_Buhid */
-
-/* 'In_Tagbanwa': Block */
-static const OnigCodePoint CR_In_Tagbanwa[] = {
- 1,
- 0x1760, 0x177f,
-}; /* CR_In_Tagbanwa */
-
-/* 'In_Khmer': Block */
-static const OnigCodePoint CR_In_Khmer[] = {
- 1,
- 0x1780, 0x17ff,
-}; /* CR_In_Khmer */
-
-/* 'In_Mongolian': Block */
-static const OnigCodePoint CR_In_Mongolian[] = {
- 1,
- 0x1800, 0x18af,
-}; /* CR_In_Mongolian */
-
-/* 'In_Unified_Canadian_Aboriginal_Syllabics_Extended': Block */
-static const OnigCodePoint CR_In_Unified_Canadian_Aboriginal_Syllabics_Extended[] = {
- 1,
- 0x18b0, 0x18ff,
-}; /* CR_In_Unified_Canadian_Aboriginal_Syllabics_Extended */
-
-/* 'In_Limbu': Block */
-static const OnigCodePoint CR_In_Limbu[] = {
- 1,
- 0x1900, 0x194f,
-}; /* CR_In_Limbu */
-
-/* 'In_Tai_Le': Block */
-static const OnigCodePoint CR_In_Tai_Le[] = {
- 1,
- 0x1950, 0x197f,
-}; /* CR_In_Tai_Le */
-
-/* 'In_New_Tai_Lue': Block */
-static const OnigCodePoint CR_In_New_Tai_Lue[] = {
- 1,
- 0x1980, 0x19df,
-}; /* CR_In_New_Tai_Lue */
-
-/* 'In_Khmer_Symbols': Block */
-static const OnigCodePoint CR_In_Khmer_Symbols[] = {
- 1,
- 0x19e0, 0x19ff,
-}; /* CR_In_Khmer_Symbols */
-
-/* 'In_Buginese': Block */
-static const OnigCodePoint CR_In_Buginese[] = {
- 1,
- 0x1a00, 0x1a1f,
-}; /* CR_In_Buginese */
-
-/* 'In_Tai_Tham': Block */
-static const OnigCodePoint CR_In_Tai_Tham[] = {
- 1,
- 0x1a20, 0x1aaf,
-}; /* CR_In_Tai_Tham */
-
-/* 'In_Combining_Diacritical_Marks_Extended': Block */
-static const OnigCodePoint CR_In_Combining_Diacritical_Marks_Extended[] = {
- 1,
- 0x1ab0, 0x1aff,
-}; /* CR_In_Combining_Diacritical_Marks_Extended */
-
-/* 'In_Balinese': Block */
-static const OnigCodePoint CR_In_Balinese[] = {
- 1,
- 0x1b00, 0x1b7f,
-}; /* CR_In_Balinese */
-
-/* 'In_Sundanese': Block */
-static const OnigCodePoint CR_In_Sundanese[] = {
- 1,
- 0x1b80, 0x1bbf,
-}; /* CR_In_Sundanese */
-
-/* 'In_Batak': Block */
-static const OnigCodePoint CR_In_Batak[] = {
- 1,
- 0x1bc0, 0x1bff,
-}; /* CR_In_Batak */
-
-/* 'In_Lepcha': Block */
-static const OnigCodePoint CR_In_Lepcha[] = {
- 1,
- 0x1c00, 0x1c4f,
-}; /* CR_In_Lepcha */
-
-/* 'In_Ol_Chiki': Block */
-#define CR_In_Ol_Chiki CR_Ol_Chiki
-
-/* 'In_Cyrillic_Extended_C': Block */
-static const OnigCodePoint CR_In_Cyrillic_Extended_C[] = {
- 1,
- 0x1c80, 0x1c8f,
-}; /* CR_In_Cyrillic_Extended_C */
-
-/* 'In_Sundanese_Supplement': Block */
-static const OnigCodePoint CR_In_Sundanese_Supplement[] = {
- 1,
- 0x1cc0, 0x1ccf,
-}; /* CR_In_Sundanese_Supplement */
-
-/* 'In_Vedic_Extensions': Block */
-static const OnigCodePoint CR_In_Vedic_Extensions[] = {
- 1,
- 0x1cd0, 0x1cff,
-}; /* CR_In_Vedic_Extensions */
-
-/* 'In_Phonetic_Extensions': Block */
-static const OnigCodePoint CR_In_Phonetic_Extensions[] = {
- 1,
- 0x1d00, 0x1d7f,
-}; /* CR_In_Phonetic_Extensions */
-
-/* 'In_Phonetic_Extensions_Supplement': Block */
-static const OnigCodePoint CR_In_Phonetic_Extensions_Supplement[] = {
- 1,
- 0x1d80, 0x1dbf,
-}; /* CR_In_Phonetic_Extensions_Supplement */
-
-/* 'In_Combining_Diacritical_Marks_Supplement': Block */
-static const OnigCodePoint CR_In_Combining_Diacritical_Marks_Supplement[] = {
- 1,
- 0x1dc0, 0x1dff,
-}; /* CR_In_Combining_Diacritical_Marks_Supplement */
-
-/* 'In_Latin_Extended_Additional': Block */
-static const OnigCodePoint CR_In_Latin_Extended_Additional[] = {
- 1,
- 0x1e00, 0x1eff,
-}; /* CR_In_Latin_Extended_Additional */
-
-/* 'In_Greek_Extended': Block */
-static const OnigCodePoint CR_In_Greek_Extended[] = {
- 1,
- 0x1f00, 0x1fff,
-}; /* CR_In_Greek_Extended */
-
-/* 'In_General_Punctuation': Block */
-static const OnigCodePoint CR_In_General_Punctuation[] = {
- 1,
- 0x2000, 0x206f,
-}; /* CR_In_General_Punctuation */
-
-/* 'In_Superscripts_and_Subscripts': Block */
-static const OnigCodePoint CR_In_Superscripts_and_Subscripts[] = {
- 1,
- 0x2070, 0x209f,
-}; /* CR_In_Superscripts_and_Subscripts */
-
-/* 'In_Currency_Symbols': Block */
-static const OnigCodePoint CR_In_Currency_Symbols[] = {
- 1,
- 0x20a0, 0x20cf,
-}; /* CR_In_Currency_Symbols */
-
-/* 'In_Combining_Diacritical_Marks_for_Symbols': Block */
-static const OnigCodePoint CR_In_Combining_Diacritical_Marks_for_Symbols[] = {
- 1,
- 0x20d0, 0x20ff,
-}; /* CR_In_Combining_Diacritical_Marks_for_Symbols */
-
-/* 'In_Letterlike_Symbols': Block */
-static const OnigCodePoint CR_In_Letterlike_Symbols[] = {
- 1,
- 0x2100, 0x214f,
-}; /* CR_In_Letterlike_Symbols */
-
-/* 'In_Number_Forms': Block */
-static const OnigCodePoint CR_In_Number_Forms[] = {
- 1,
- 0x2150, 0x218f,
-}; /* CR_In_Number_Forms */
-
-/* 'In_Arrows': Block */
-static const OnigCodePoint CR_In_Arrows[] = {
- 1,
- 0x2190, 0x21ff,
-}; /* CR_In_Arrows */
-
-/* 'In_Mathematical_Operators': Block */
-static const OnigCodePoint CR_In_Mathematical_Operators[] = {
- 1,
- 0x2200, 0x22ff,
-}; /* CR_In_Mathematical_Operators */
-
-/* 'In_Miscellaneous_Technical': Block */
-static const OnigCodePoint CR_In_Miscellaneous_Technical[] = {
- 1,
- 0x2300, 0x23ff,
-}; /* CR_In_Miscellaneous_Technical */
-
-/* 'In_Control_Pictures': Block */
-static const OnigCodePoint CR_In_Control_Pictures[] = {
- 1,
- 0x2400, 0x243f,
-}; /* CR_In_Control_Pictures */
-
-/* 'In_Optical_Character_Recognition': Block */
-static const OnigCodePoint CR_In_Optical_Character_Recognition[] = {
- 1,
- 0x2440, 0x245f,
-}; /* CR_In_Optical_Character_Recognition */
-
-/* 'In_Enclosed_Alphanumerics': Block */
-static const OnigCodePoint CR_In_Enclosed_Alphanumerics[] = {
- 1,
- 0x2460, 0x24ff,
-}; /* CR_In_Enclosed_Alphanumerics */
-
-/* 'In_Box_Drawing': Block */
-static const OnigCodePoint CR_In_Box_Drawing[] = {
- 1,
- 0x2500, 0x257f,
-}; /* CR_In_Box_Drawing */
-
-/* 'In_Block_Elements': Block */
-static const OnigCodePoint CR_In_Block_Elements[] = {
- 1,
- 0x2580, 0x259f,
-}; /* CR_In_Block_Elements */
-
-/* 'In_Geometric_Shapes': Block */
-static const OnigCodePoint CR_In_Geometric_Shapes[] = {
- 1,
- 0x25a0, 0x25ff,
-}; /* CR_In_Geometric_Shapes */
-
-/* 'In_Miscellaneous_Symbols': Block */
-static const OnigCodePoint CR_In_Miscellaneous_Symbols[] = {
- 1,
- 0x2600, 0x26ff,
-}; /* CR_In_Miscellaneous_Symbols */
-
-/* 'In_Dingbats': Block */
-static const OnigCodePoint CR_In_Dingbats[] = {
- 1,
- 0x2700, 0x27bf,
-}; /* CR_In_Dingbats */
-
-/* 'In_Miscellaneous_Mathematical_Symbols_A': Block */
-static const OnigCodePoint CR_In_Miscellaneous_Mathematical_Symbols_A[] = {
- 1,
- 0x27c0, 0x27ef,
-}; /* CR_In_Miscellaneous_Mathematical_Symbols_A */
-
-/* 'In_Supplemental_Arrows_A': Block */
-static const OnigCodePoint CR_In_Supplemental_Arrows_A[] = {
- 1,
- 0x27f0, 0x27ff,
-}; /* CR_In_Supplemental_Arrows_A */
-
-/* 'In_Braille_Patterns': Block */
-#define CR_In_Braille_Patterns CR_Braille
-
-/* 'In_Supplemental_Arrows_B': Block */
-static const OnigCodePoint CR_In_Supplemental_Arrows_B[] = {
- 1,
- 0x2900, 0x297f,
-}; /* CR_In_Supplemental_Arrows_B */
-
-/* 'In_Miscellaneous_Mathematical_Symbols_B': Block */
-static const OnigCodePoint CR_In_Miscellaneous_Mathematical_Symbols_B[] = {
- 1,
- 0x2980, 0x29ff,
-}; /* CR_In_Miscellaneous_Mathematical_Symbols_B */
-
-/* 'In_Supplemental_Mathematical_Operators': Block */
-static const OnigCodePoint CR_In_Supplemental_Mathematical_Operators[] = {
- 1,
- 0x2a00, 0x2aff,
-}; /* CR_In_Supplemental_Mathematical_Operators */
-
-/* 'In_Miscellaneous_Symbols_and_Arrows': Block */
-static const OnigCodePoint CR_In_Miscellaneous_Symbols_and_Arrows[] = {
- 1,
- 0x2b00, 0x2bff,
-}; /* CR_In_Miscellaneous_Symbols_and_Arrows */
-
-/* 'In_Glagolitic': Block */
-static const OnigCodePoint CR_In_Glagolitic[] = {
- 1,
- 0x2c00, 0x2c5f,
-}; /* CR_In_Glagolitic */
-
-/* 'In_Latin_Extended_C': Block */
-static const OnigCodePoint CR_In_Latin_Extended_C[] = {
- 1,
- 0x2c60, 0x2c7f,
-}; /* CR_In_Latin_Extended_C */
-
-/* 'In_Coptic': Block */
-static const OnigCodePoint CR_In_Coptic[] = {
- 1,
- 0x2c80, 0x2cff,
-}; /* CR_In_Coptic */
-
-/* 'In_Georgian_Supplement': Block */
-static const OnigCodePoint CR_In_Georgian_Supplement[] = {
- 1,
- 0x2d00, 0x2d2f,
-}; /* CR_In_Georgian_Supplement */
-
-/* 'In_Tifinagh': Block */
-static const OnigCodePoint CR_In_Tifinagh[] = {
- 1,
- 0x2d30, 0x2d7f,
-}; /* CR_In_Tifinagh */
-
-/* 'In_Ethiopic_Extended': Block */
-static const OnigCodePoint CR_In_Ethiopic_Extended[] = {
- 1,
- 0x2d80, 0x2ddf,
-}; /* CR_In_Ethiopic_Extended */
-
-/* 'In_Cyrillic_Extended_A': Block */
-static const OnigCodePoint CR_In_Cyrillic_Extended_A[] = {
- 1,
- 0x2de0, 0x2dff,
-}; /* CR_In_Cyrillic_Extended_A */
-
-/* 'In_Supplemental_Punctuation': Block */
-static const OnigCodePoint CR_In_Supplemental_Punctuation[] = {
- 1,
- 0x2e00, 0x2e7f,
-}; /* CR_In_Supplemental_Punctuation */
-
-/* 'In_CJK_Radicals_Supplement': Block */
-static const OnigCodePoint CR_In_CJK_Radicals_Supplement[] = {
- 1,
- 0x2e80, 0x2eff,
-}; /* CR_In_CJK_Radicals_Supplement */
-
-/* 'In_Kangxi_Radicals': Block */
-static const OnigCodePoint CR_In_Kangxi_Radicals[] = {
- 1,
- 0x2f00, 0x2fdf,
-}; /* CR_In_Kangxi_Radicals */
-
-/* 'In_Ideographic_Description_Characters': Block */
-static const OnigCodePoint CR_In_Ideographic_Description_Characters[] = {
- 1,
- 0x2ff0, 0x2fff,
-}; /* CR_In_Ideographic_Description_Characters */
-
-/* 'In_CJK_Symbols_and_Punctuation': Block */
-static const OnigCodePoint CR_In_CJK_Symbols_and_Punctuation[] = {
- 1,
- 0x3000, 0x303f,
-}; /* CR_In_CJK_Symbols_and_Punctuation */
-
-/* 'In_Hiragana': Block */
-static const OnigCodePoint CR_In_Hiragana[] = {
- 1,
- 0x3040, 0x309f,
-}; /* CR_In_Hiragana */
-
-/* 'In_Katakana': Block */
-static const OnigCodePoint CR_In_Katakana[] = {
- 1,
- 0x30a0, 0x30ff,
-}; /* CR_In_Katakana */
-
-/* 'In_Bopomofo': Block */
-static const OnigCodePoint CR_In_Bopomofo[] = {
- 1,
- 0x3100, 0x312f,
-}; /* CR_In_Bopomofo */
-
-/* 'In_Hangul_Compatibility_Jamo': Block */
-static const OnigCodePoint CR_In_Hangul_Compatibility_Jamo[] = {
- 1,
- 0x3130, 0x318f,
-}; /* CR_In_Hangul_Compatibility_Jamo */
-
-/* 'In_Kanbun': Block */
-static const OnigCodePoint CR_In_Kanbun[] = {
- 1,
- 0x3190, 0x319f,
-}; /* CR_In_Kanbun */
-
-/* 'In_Bopomofo_Extended': Block */
-static const OnigCodePoint CR_In_Bopomofo_Extended[] = {
- 1,
- 0x31a0, 0x31bf,
-}; /* CR_In_Bopomofo_Extended */
-
-/* 'In_CJK_Strokes': Block */
-static const OnigCodePoint CR_In_CJK_Strokes[] = {
- 1,
- 0x31c0, 0x31ef,
-}; /* CR_In_CJK_Strokes */
-
-/* 'In_Katakana_Phonetic_Extensions': Block */
-static const OnigCodePoint CR_In_Katakana_Phonetic_Extensions[] = {
- 1,
- 0x31f0, 0x31ff,
-}; /* CR_In_Katakana_Phonetic_Extensions */
-
-/* 'In_Enclosed_CJK_Letters_and_Months': Block */
-static const OnigCodePoint CR_In_Enclosed_CJK_Letters_and_Months[] = {
- 1,
- 0x3200, 0x32ff,
-}; /* CR_In_Enclosed_CJK_Letters_and_Months */
-
-/* 'In_CJK_Compatibility': Block */
-static const OnigCodePoint CR_In_CJK_Compatibility[] = {
- 1,
- 0x3300, 0x33ff,
-}; /* CR_In_CJK_Compatibility */
-
-/* 'In_CJK_Unified_Ideographs_Extension_A': Block */
-static const OnigCodePoint CR_In_CJK_Unified_Ideographs_Extension_A[] = {
- 1,
- 0x3400, 0x4dbf,
-}; /* CR_In_CJK_Unified_Ideographs_Extension_A */
-
-/* 'In_Yijing_Hexagram_Symbols': Block */
-static const OnigCodePoint CR_In_Yijing_Hexagram_Symbols[] = {
- 1,
- 0x4dc0, 0x4dff,
-}; /* CR_In_Yijing_Hexagram_Symbols */
-
-/* 'In_CJK_Unified_Ideographs': Block */
-static const OnigCodePoint CR_In_CJK_Unified_Ideographs[] = {
- 1,
- 0x4e00, 0x9fff,
-}; /* CR_In_CJK_Unified_Ideographs */
-
-/* 'In_Yi_Syllables': Block */
-static const OnigCodePoint CR_In_Yi_Syllables[] = {
- 1,
- 0xa000, 0xa48f,
-}; /* CR_In_Yi_Syllables */
-
-/* 'In_Yi_Radicals': Block */
-static const OnigCodePoint CR_In_Yi_Radicals[] = {
- 1,
- 0xa490, 0xa4cf,
-}; /* CR_In_Yi_Radicals */
-
-/* 'In_Lisu': Block */
-#define CR_In_Lisu CR_Lisu
-
-/* 'In_Vai': Block */
-static const OnigCodePoint CR_In_Vai[] = {
- 1,
- 0xa500, 0xa63f,
-}; /* CR_In_Vai */
-
-/* 'In_Cyrillic_Extended_B': Block */
-static const OnigCodePoint CR_In_Cyrillic_Extended_B[] = {
- 1,
- 0xa640, 0xa69f,
-}; /* CR_In_Cyrillic_Extended_B */
-
-/* 'In_Bamum': Block */
-static const OnigCodePoint CR_In_Bamum[] = {
- 1,
- 0xa6a0, 0xa6ff,
-}; /* CR_In_Bamum */
-
-/* 'In_Modifier_Tone_Letters': Block */
-static const OnigCodePoint CR_In_Modifier_Tone_Letters[] = {
- 1,
- 0xa700, 0xa71f,
-}; /* CR_In_Modifier_Tone_Letters */
-
-/* 'In_Latin_Extended_D': Block */
-static const OnigCodePoint CR_In_Latin_Extended_D[] = {
- 1,
- 0xa720, 0xa7ff,
-}; /* CR_In_Latin_Extended_D */
-
-/* 'In_Syloti_Nagri': Block */
-static const OnigCodePoint CR_In_Syloti_Nagri[] = {
- 1,
- 0xa800, 0xa82f,
-}; /* CR_In_Syloti_Nagri */
-
-/* 'In_Common_Indic_Number_Forms': Block */
-static const OnigCodePoint CR_In_Common_Indic_Number_Forms[] = {
- 1,
- 0xa830, 0xa83f,
-}; /* CR_In_Common_Indic_Number_Forms */
-
-/* 'In_Phags_pa': Block */
-static const OnigCodePoint CR_In_Phags_pa[] = {
- 1,
- 0xa840, 0xa87f,
-}; /* CR_In_Phags_pa */
-
-/* 'In_Saurashtra': Block */
-static const OnigCodePoint CR_In_Saurashtra[] = {
- 1,
- 0xa880, 0xa8df,
-}; /* CR_In_Saurashtra */
-
-/* 'In_Devanagari_Extended': Block */
-static const OnigCodePoint CR_In_Devanagari_Extended[] = {
- 1,
- 0xa8e0, 0xa8ff,
-}; /* CR_In_Devanagari_Extended */
-
-/* 'In_Kayah_Li': Block */
-static const OnigCodePoint CR_In_Kayah_Li[] = {
- 1,
- 0xa900, 0xa92f,
-}; /* CR_In_Kayah_Li */
-
-/* 'In_Rejang': Block */
-static const OnigCodePoint CR_In_Rejang[] = {
- 1,
- 0xa930, 0xa95f,
-}; /* CR_In_Rejang */
-
-/* 'In_Hangul_Jamo_Extended_A': Block */
-static const OnigCodePoint CR_In_Hangul_Jamo_Extended_A[] = {
- 1,
- 0xa960, 0xa97f,
-}; /* CR_In_Hangul_Jamo_Extended_A */
-
-/* 'In_Javanese': Block */
-static const OnigCodePoint CR_In_Javanese[] = {
- 1,
- 0xa980, 0xa9df,
-}; /* CR_In_Javanese */
-
-/* 'In_Myanmar_Extended_B': Block */
-static const OnigCodePoint CR_In_Myanmar_Extended_B[] = {
- 1,
- 0xa9e0, 0xa9ff,
-}; /* CR_In_Myanmar_Extended_B */
-
-/* 'In_Cham': Block */
-static const OnigCodePoint CR_In_Cham[] = {
- 1,
- 0xaa00, 0xaa5f,
-}; /* CR_In_Cham */
-
-/* 'In_Myanmar_Extended_A': Block */
-static const OnigCodePoint CR_In_Myanmar_Extended_A[] = {
- 1,
- 0xaa60, 0xaa7f,
-}; /* CR_In_Myanmar_Extended_A */
-
-/* 'In_Tai_Viet': Block */
-static const OnigCodePoint CR_In_Tai_Viet[] = {
- 1,
- 0xaa80, 0xaadf,
-}; /* CR_In_Tai_Viet */
-
-/* 'In_Meetei_Mayek_Extensions': Block */
-static const OnigCodePoint CR_In_Meetei_Mayek_Extensions[] = {
- 1,
- 0xaae0, 0xaaff,
-}; /* CR_In_Meetei_Mayek_Extensions */
-
-/* 'In_Ethiopic_Extended_A': Block */
-static const OnigCodePoint CR_In_Ethiopic_Extended_A[] = {
- 1,
- 0xab00, 0xab2f,
-}; /* CR_In_Ethiopic_Extended_A */
-
-/* 'In_Latin_Extended_E': Block */
-static const OnigCodePoint CR_In_Latin_Extended_E[] = {
- 1,
- 0xab30, 0xab6f,
-}; /* CR_In_Latin_Extended_E */
-
-/* 'In_Cherokee_Supplement': Block */
-static const OnigCodePoint CR_In_Cherokee_Supplement[] = {
- 1,
- 0xab70, 0xabbf,
-}; /* CR_In_Cherokee_Supplement */
-
-/* 'In_Meetei_Mayek': Block */
-static const OnigCodePoint CR_In_Meetei_Mayek[] = {
- 1,
- 0xabc0, 0xabff,
-}; /* CR_In_Meetei_Mayek */
-
-/* 'In_Hangul_Syllables': Block */
-static const OnigCodePoint CR_In_Hangul_Syllables[] = {
- 1,
- 0xac00, 0xd7af,
-}; /* CR_In_Hangul_Syllables */
-
-/* 'In_Hangul_Jamo_Extended_B': Block */
-static const OnigCodePoint CR_In_Hangul_Jamo_Extended_B[] = {
- 1,
- 0xd7b0, 0xd7ff,
-}; /* CR_In_Hangul_Jamo_Extended_B */
-
-/* 'In_High_Surrogates': Block */
-static const OnigCodePoint CR_In_High_Surrogates[] = {
- 1,
- 0xd800, 0xdb7f,
-}; /* CR_In_High_Surrogates */
-
-/* 'In_High_Private_Use_Surrogates': Block */
-static const OnigCodePoint CR_In_High_Private_Use_Surrogates[] = {
- 1,
- 0xdb80, 0xdbff,
-}; /* CR_In_High_Private_Use_Surrogates */
-
-/* 'In_Low_Surrogates': Block */
-static const OnigCodePoint CR_In_Low_Surrogates[] = {
- 1,
- 0xdc00, 0xdfff,
-}; /* CR_In_Low_Surrogates */
-
-/* 'In_Private_Use_Area': Block */
-static const OnigCodePoint CR_In_Private_Use_Area[] = {
- 1,
- 0xe000, 0xf8ff,
-}; /* CR_In_Private_Use_Area */
-
-/* 'In_CJK_Compatibility_Ideographs': Block */
-static const OnigCodePoint CR_In_CJK_Compatibility_Ideographs[] = {
- 1,
- 0xf900, 0xfaff,
-}; /* CR_In_CJK_Compatibility_Ideographs */
-
-/* 'In_Alphabetic_Presentation_Forms': Block */
-static const OnigCodePoint CR_In_Alphabetic_Presentation_Forms[] = {
- 1,
- 0xfb00, 0xfb4f,
-}; /* CR_In_Alphabetic_Presentation_Forms */
-
-/* 'In_Arabic_Presentation_Forms_A': Block */
-static const OnigCodePoint CR_In_Arabic_Presentation_Forms_A[] = {
- 1,
- 0xfb50, 0xfdff,
-}; /* CR_In_Arabic_Presentation_Forms_A */
-
-/* 'In_Variation_Selectors': Block */
-static const OnigCodePoint CR_In_Variation_Selectors[] = {
- 1,
- 0xfe00, 0xfe0f,
-}; /* CR_In_Variation_Selectors */
-
-/* 'In_Vertical_Forms': Block */
-static const OnigCodePoint CR_In_Vertical_Forms[] = {
- 1,
- 0xfe10, 0xfe1f,
-}; /* CR_In_Vertical_Forms */
-
-/* 'In_Combining_Half_Marks': Block */
-static const OnigCodePoint CR_In_Combining_Half_Marks[] = {
- 1,
- 0xfe20, 0xfe2f,
-}; /* CR_In_Combining_Half_Marks */
-
-/* 'In_CJK_Compatibility_Forms': Block */
-static const OnigCodePoint CR_In_CJK_Compatibility_Forms[] = {
- 1,
- 0xfe30, 0xfe4f,
-}; /* CR_In_CJK_Compatibility_Forms */
-
-/* 'In_Small_Form_Variants': Block */
-static const OnigCodePoint CR_In_Small_Form_Variants[] = {
- 1,
- 0xfe50, 0xfe6f,
-}; /* CR_In_Small_Form_Variants */
-
-/* 'In_Arabic_Presentation_Forms_B': Block */
-static const OnigCodePoint CR_In_Arabic_Presentation_Forms_B[] = {
- 1,
- 0xfe70, 0xfeff,
-}; /* CR_In_Arabic_Presentation_Forms_B */
-
-/* 'In_Halfwidth_and_Fullwidth_Forms': Block */
-static const OnigCodePoint CR_In_Halfwidth_and_Fullwidth_Forms[] = {
- 1,
- 0xff00, 0xffef,
-}; /* CR_In_Halfwidth_and_Fullwidth_Forms */
-
-/* 'In_Specials': Block */
-static const OnigCodePoint CR_In_Specials[] = {
- 1,
- 0xfff0, 0xffff,
-}; /* CR_In_Specials */
-
-/* 'In_Linear_B_Syllabary': Block */
-static const OnigCodePoint CR_In_Linear_B_Syllabary[] = {
- 1,
- 0x10000, 0x1007f,
-}; /* CR_In_Linear_B_Syllabary */
-
-/* 'In_Linear_B_Ideograms': Block */
-static const OnigCodePoint CR_In_Linear_B_Ideograms[] = {
- 1,
- 0x10080, 0x100ff,
-}; /* CR_In_Linear_B_Ideograms */
-
-/* 'In_Aegean_Numbers': Block */
-static const OnigCodePoint CR_In_Aegean_Numbers[] = {
- 1,
- 0x10100, 0x1013f,
-}; /* CR_In_Aegean_Numbers */
-
-/* 'In_Ancient_Greek_Numbers': Block */
-static const OnigCodePoint CR_In_Ancient_Greek_Numbers[] = {
- 1,
- 0x10140, 0x1018f,
-}; /* CR_In_Ancient_Greek_Numbers */
-
-/* 'In_Ancient_Symbols': Block */
-static const OnigCodePoint CR_In_Ancient_Symbols[] = {
- 1,
- 0x10190, 0x101cf,
-}; /* CR_In_Ancient_Symbols */
-
-/* 'In_Phaistos_Disc': Block */
-static const OnigCodePoint CR_In_Phaistos_Disc[] = {
- 1,
- 0x101d0, 0x101ff,
-}; /* CR_In_Phaistos_Disc */
-
-/* 'In_Lycian': Block */
-static const OnigCodePoint CR_In_Lycian[] = {
- 1,
- 0x10280, 0x1029f,
-}; /* CR_In_Lycian */
-
-/* 'In_Carian': Block */
-static const OnigCodePoint CR_In_Carian[] = {
- 1,
- 0x102a0, 0x102df,
-}; /* CR_In_Carian */
-
-/* 'In_Coptic_Epact_Numbers': Block */
-static const OnigCodePoint CR_In_Coptic_Epact_Numbers[] = {
- 1,
- 0x102e0, 0x102ff,
-}; /* CR_In_Coptic_Epact_Numbers */
-
-/* 'In_Old_Italic': Block */
-static const OnigCodePoint CR_In_Old_Italic[] = {
- 1,
- 0x10300, 0x1032f,
-}; /* CR_In_Old_Italic */
-
-/* 'In_Gothic': Block */
-static const OnigCodePoint CR_In_Gothic[] = {
- 1,
- 0x10330, 0x1034f,
-}; /* CR_In_Gothic */
-
-/* 'In_Old_Permic': Block */
-static const OnigCodePoint CR_In_Old_Permic[] = {
- 1,
- 0x10350, 0x1037f,
-}; /* CR_In_Old_Permic */
-
-/* 'In_Ugaritic': Block */
-static const OnigCodePoint CR_In_Ugaritic[] = {
- 1,
- 0x10380, 0x1039f,
-}; /* CR_In_Ugaritic */
-
-/* 'In_Old_Persian': Block */
-static const OnigCodePoint CR_In_Old_Persian[] = {
- 1,
- 0x103a0, 0x103df,
-}; /* CR_In_Old_Persian */
-
-/* 'In_Deseret': Block */
-#define CR_In_Deseret CR_Deseret
-
-/* 'In_Shavian': Block */
-#define CR_In_Shavian CR_Shavian
-
-/* 'In_Osmanya': Block */
-static const OnigCodePoint CR_In_Osmanya[] = {
- 1,
- 0x10480, 0x104af,
-}; /* CR_In_Osmanya */
-
-/* 'In_Osage': Block */
-static const OnigCodePoint CR_In_Osage[] = {
- 1,
- 0x104b0, 0x104ff,
-}; /* CR_In_Osage */
-
-/* 'In_Elbasan': Block */
-static const OnigCodePoint CR_In_Elbasan[] = {
- 1,
- 0x10500, 0x1052f,
-}; /* CR_In_Elbasan */
-
-/* 'In_Caucasian_Albanian': Block */
-static const OnigCodePoint CR_In_Caucasian_Albanian[] = {
- 1,
- 0x10530, 0x1056f,
-}; /* CR_In_Caucasian_Albanian */
-
-/* 'In_Linear_A': Block */
-static const OnigCodePoint CR_In_Linear_A[] = {
- 1,
- 0x10600, 0x1077f,
-}; /* CR_In_Linear_A */
-
-/* 'In_Cypriot_Syllabary': Block */
-static const OnigCodePoint CR_In_Cypriot_Syllabary[] = {
- 1,
- 0x10800, 0x1083f,
-}; /* CR_In_Cypriot_Syllabary */
-
-/* 'In_Imperial_Aramaic': Block */
-static const OnigCodePoint CR_In_Imperial_Aramaic[] = {
- 1,
- 0x10840, 0x1085f,
-}; /* CR_In_Imperial_Aramaic */
-
-/* 'In_Palmyrene': Block */
-#define CR_In_Palmyrene CR_Palmyrene
-
-/* 'In_Nabataean': Block */
-static const OnigCodePoint CR_In_Nabataean[] = {
- 1,
- 0x10880, 0x108af,
-}; /* CR_In_Nabataean */
-
-/* 'In_Hatran': Block */
-static const OnigCodePoint CR_In_Hatran[] = {
- 1,
- 0x108e0, 0x108ff,
-}; /* CR_In_Hatran */
-
-/* 'In_Phoenician': Block */
-static const OnigCodePoint CR_In_Phoenician[] = {
- 1,
- 0x10900, 0x1091f,
-}; /* CR_In_Phoenician */
-
-/* 'In_Lydian': Block */
-static const OnigCodePoint CR_In_Lydian[] = {
- 1,
- 0x10920, 0x1093f,
-}; /* CR_In_Lydian */
-
-/* 'In_Meroitic_Hieroglyphs': Block */
-#define CR_In_Meroitic_Hieroglyphs CR_Meroitic_Hieroglyphs
-
-/* 'In_Meroitic_Cursive': Block */
-static const OnigCodePoint CR_In_Meroitic_Cursive[] = {
- 1,
- 0x109a0, 0x109ff,
-}; /* CR_In_Meroitic_Cursive */
-
-/* 'In_Kharoshthi': Block */
-static const OnigCodePoint CR_In_Kharoshthi[] = {
- 1,
- 0x10a00, 0x10a5f,
-}; /* CR_In_Kharoshthi */
-
-/* 'In_Old_South_Arabian': Block */
-#define CR_In_Old_South_Arabian CR_Old_South_Arabian
-
-/* 'In_Old_North_Arabian': Block */
-#define CR_In_Old_North_Arabian CR_Old_North_Arabian
-
-/* 'In_Manichaean': Block */
-static const OnigCodePoint CR_In_Manichaean[] = {
- 1,
- 0x10ac0, 0x10aff,
-}; /* CR_In_Manichaean */
-
-/* 'In_Avestan': Block */
-static const OnigCodePoint CR_In_Avestan[] = {
- 1,
- 0x10b00, 0x10b3f,
-}; /* CR_In_Avestan */
-
-/* 'In_Inscriptional_Parthian': Block */
-static const OnigCodePoint CR_In_Inscriptional_Parthian[] = {
- 1,
- 0x10b40, 0x10b5f,
-}; /* CR_In_Inscriptional_Parthian */
-
-/* 'In_Inscriptional_Pahlavi': Block */
-static const OnigCodePoint CR_In_Inscriptional_Pahlavi[] = {
- 1,
- 0x10b60, 0x10b7f,
-}; /* CR_In_Inscriptional_Pahlavi */
-
-/* 'In_Psalter_Pahlavi': Block */
-static const OnigCodePoint CR_In_Psalter_Pahlavi[] = {
- 1,
- 0x10b80, 0x10baf,
-}; /* CR_In_Psalter_Pahlavi */
-
-/* 'In_Old_Turkic': Block */
-static const OnigCodePoint CR_In_Old_Turkic[] = {
- 1,
- 0x10c00, 0x10c4f,
-}; /* CR_In_Old_Turkic */
-
-/* 'In_Old_Hungarian': Block */
-static const OnigCodePoint CR_In_Old_Hungarian[] = {
- 1,
- 0x10c80, 0x10cff,
-}; /* CR_In_Old_Hungarian */
-
-/* 'In_Rumi_Numeral_Symbols': Block */
-static const OnigCodePoint CR_In_Rumi_Numeral_Symbols[] = {
- 1,
- 0x10e60, 0x10e7f,
-}; /* CR_In_Rumi_Numeral_Symbols */
-
-/* 'In_Brahmi': Block */
-static const OnigCodePoint CR_In_Brahmi[] = {
- 1,
- 0x11000, 0x1107f,
-}; /* CR_In_Brahmi */
-
-/* 'In_Kaithi': Block */
-static const OnigCodePoint CR_In_Kaithi[] = {
- 1,
- 0x11080, 0x110cf,
-}; /* CR_In_Kaithi */
-
-/* 'In_Sora_Sompeng': Block */
-static const OnigCodePoint CR_In_Sora_Sompeng[] = {
- 1,
- 0x110d0, 0x110ff,
-}; /* CR_In_Sora_Sompeng */
-
-/* 'In_Chakma': Block */
-static const OnigCodePoint CR_In_Chakma[] = {
- 1,
- 0x11100, 0x1114f,
-}; /* CR_In_Chakma */
-
-/* 'In_Mahajani': Block */
-static const OnigCodePoint CR_In_Mahajani[] = {
- 1,
- 0x11150, 0x1117f,
-}; /* CR_In_Mahajani */
-
-/* 'In_Sharada': Block */
-static const OnigCodePoint CR_In_Sharada[] = {
- 1,
- 0x11180, 0x111df,
-}; /* CR_In_Sharada */
-
-/* 'In_Sinhala_Archaic_Numbers': Block */
-static const OnigCodePoint CR_In_Sinhala_Archaic_Numbers[] = {
- 1,
- 0x111e0, 0x111ff,
-}; /* CR_In_Sinhala_Archaic_Numbers */
-
-/* 'In_Khojki': Block */
-static const OnigCodePoint CR_In_Khojki[] = {
- 1,
- 0x11200, 0x1124f,
-}; /* CR_In_Khojki */
-
-/* 'In_Multani': Block */
-static const OnigCodePoint CR_In_Multani[] = {
- 1,
- 0x11280, 0x112af,
-}; /* CR_In_Multani */
-
-/* 'In_Khudawadi': Block */
-static const OnigCodePoint CR_In_Khudawadi[] = {
- 1,
- 0x112b0, 0x112ff,
-}; /* CR_In_Khudawadi */
-
-/* 'In_Grantha': Block */
-static const OnigCodePoint CR_In_Grantha[] = {
- 1,
- 0x11300, 0x1137f,
-}; /* CR_In_Grantha */
-
-/* 'In_Newa': Block */
-static const OnigCodePoint CR_In_Newa[] = {
- 1,
- 0x11400, 0x1147f,
-}; /* CR_In_Newa */
-
-/* 'In_Tirhuta': Block */
-static const OnigCodePoint CR_In_Tirhuta[] = {
- 1,
- 0x11480, 0x114df,
-}; /* CR_In_Tirhuta */
-
-/* 'In_Siddham': Block */
-static const OnigCodePoint CR_In_Siddham[] = {
- 1,
- 0x11580, 0x115ff,
-}; /* CR_In_Siddham */
-
-/* 'In_Modi': Block */
-static const OnigCodePoint CR_In_Modi[] = {
- 1,
- 0x11600, 0x1165f,
-}; /* CR_In_Modi */
-
-/* 'In_Mongolian_Supplement': Block */
-static const OnigCodePoint CR_In_Mongolian_Supplement[] = {
- 1,
- 0x11660, 0x1167f,
-}; /* CR_In_Mongolian_Supplement */
-
-/* 'In_Takri': Block */
-static const OnigCodePoint CR_In_Takri[] = {
- 1,
- 0x11680, 0x116cf,
-}; /* CR_In_Takri */
-
-/* 'In_Ahom': Block */
-static const OnigCodePoint CR_In_Ahom[] = {
- 1,
- 0x11700, 0x1173f,
-}; /* CR_In_Ahom */
-
-/* 'In_Warang_Citi': Block */
-static const OnigCodePoint CR_In_Warang_Citi[] = {
- 1,
- 0x118a0, 0x118ff,
-}; /* CR_In_Warang_Citi */
-
-/* 'In_Pau_Cin_Hau': Block */
-static const OnigCodePoint CR_In_Pau_Cin_Hau[] = {
- 1,
- 0x11ac0, 0x11aff,
-}; /* CR_In_Pau_Cin_Hau */
-
-/* 'In_Bhaiksuki': Block */
-static const OnigCodePoint CR_In_Bhaiksuki[] = {
- 1,
- 0x11c00, 0x11c6f,
-}; /* CR_In_Bhaiksuki */
-
-/* 'In_Marchen': Block */
-static const OnigCodePoint CR_In_Marchen[] = {
- 1,
- 0x11c70, 0x11cbf,
-}; /* CR_In_Marchen */
-
-/* 'In_Cuneiform': Block */
-static const OnigCodePoint CR_In_Cuneiform[] = {
- 1,
- 0x12000, 0x123ff,
-}; /* CR_In_Cuneiform */
-
-/* 'In_Cuneiform_Numbers_and_Punctuation': Block */
-static const OnigCodePoint CR_In_Cuneiform_Numbers_and_Punctuation[] = {
- 1,
- 0x12400, 0x1247f,
-}; /* CR_In_Cuneiform_Numbers_and_Punctuation */
-
-/* 'In_Early_Dynastic_Cuneiform': Block */
-static const OnigCodePoint CR_In_Early_Dynastic_Cuneiform[] = {
- 1,
- 0x12480, 0x1254f,
-}; /* CR_In_Early_Dynastic_Cuneiform */
-
-/* 'In_Egyptian_Hieroglyphs': Block */
-static const OnigCodePoint CR_In_Egyptian_Hieroglyphs[] = {
- 1,
- 0x13000, 0x1342f,
-}; /* CR_In_Egyptian_Hieroglyphs */
-
-/* 'In_Anatolian_Hieroglyphs': Block */
-static const OnigCodePoint CR_In_Anatolian_Hieroglyphs[] = {
- 1,
- 0x14400, 0x1467f,
-}; /* CR_In_Anatolian_Hieroglyphs */
-
-/* 'In_Bamum_Supplement': Block */
-static const OnigCodePoint CR_In_Bamum_Supplement[] = {
- 1,
- 0x16800, 0x16a3f,
-}; /* CR_In_Bamum_Supplement */
-
-/* 'In_Mro': Block */
-static const OnigCodePoint CR_In_Mro[] = {
- 1,
- 0x16a40, 0x16a6f,
-}; /* CR_In_Mro */
-
-/* 'In_Bassa_Vah': Block */
-static const OnigCodePoint CR_In_Bassa_Vah[] = {
- 1,
- 0x16ad0, 0x16aff,
-}; /* CR_In_Bassa_Vah */
-
-/* 'In_Pahawh_Hmong': Block */
-static const OnigCodePoint CR_In_Pahawh_Hmong[] = {
- 1,
- 0x16b00, 0x16b8f,
-}; /* CR_In_Pahawh_Hmong */
-
-/* 'In_Miao': Block */
-static const OnigCodePoint CR_In_Miao[] = {
- 1,
- 0x16f00, 0x16f9f,
-}; /* CR_In_Miao */
-
-/* 'In_Ideographic_Symbols_and_Punctuation': Block */
-static const OnigCodePoint CR_In_Ideographic_Symbols_and_Punctuation[] = {
- 1,
- 0x16fe0, 0x16fff,
-}; /* CR_In_Ideographic_Symbols_and_Punctuation */
-
-/* 'In_Tangut': Block */
-static const OnigCodePoint CR_In_Tangut[] = {
- 1,
- 0x17000, 0x187ff,
-}; /* CR_In_Tangut */
-
-/* 'In_Tangut_Components': Block */
-static const OnigCodePoint CR_In_Tangut_Components[] = {
- 1,
- 0x18800, 0x18aff,
-}; /* CR_In_Tangut_Components */
-
-/* 'In_Kana_Supplement': Block */
-static const OnigCodePoint CR_In_Kana_Supplement[] = {
- 1,
- 0x1b000, 0x1b0ff,
-}; /* CR_In_Kana_Supplement */
-
-/* 'In_Duployan': Block */
-static const OnigCodePoint CR_In_Duployan[] = {
- 1,
- 0x1bc00, 0x1bc9f,
-}; /* CR_In_Duployan */
-
-/* 'In_Shorthand_Format_Controls': Block */
-static const OnigCodePoint CR_In_Shorthand_Format_Controls[] = {
- 1,
- 0x1bca0, 0x1bcaf,
-}; /* CR_In_Shorthand_Format_Controls */
-
-/* 'In_Byzantine_Musical_Symbols': Block */
-static const OnigCodePoint CR_In_Byzantine_Musical_Symbols[] = {
- 1,
- 0x1d000, 0x1d0ff,
-}; /* CR_In_Byzantine_Musical_Symbols */
-
-/* 'In_Musical_Symbols': Block */
-static const OnigCodePoint CR_In_Musical_Symbols[] = {
- 1,
- 0x1d100, 0x1d1ff,
-}; /* CR_In_Musical_Symbols */
-
-/* 'In_Ancient_Greek_Musical_Notation': Block */
-static const OnigCodePoint CR_In_Ancient_Greek_Musical_Notation[] = {
- 1,
- 0x1d200, 0x1d24f,
-}; /* CR_In_Ancient_Greek_Musical_Notation */
-
-/* 'In_Tai_Xuan_Jing_Symbols': Block */
-static const OnigCodePoint CR_In_Tai_Xuan_Jing_Symbols[] = {
- 1,
- 0x1d300, 0x1d35f,
-}; /* CR_In_Tai_Xuan_Jing_Symbols */
-
-/* 'In_Counting_Rod_Numerals': Block */
-static const OnigCodePoint CR_In_Counting_Rod_Numerals[] = {
- 1,
- 0x1d360, 0x1d37f,
-}; /* CR_In_Counting_Rod_Numerals */
-
-/* 'In_Mathematical_Alphanumeric_Symbols': Block */
-static const OnigCodePoint CR_In_Mathematical_Alphanumeric_Symbols[] = {
- 1,
- 0x1d400, 0x1d7ff,
-}; /* CR_In_Mathematical_Alphanumeric_Symbols */
-
-/* 'In_Sutton_SignWriting': Block */
-static const OnigCodePoint CR_In_Sutton_SignWriting[] = {
- 1,
- 0x1d800, 0x1daaf,
-}; /* CR_In_Sutton_SignWriting */
-
-/* 'In_Glagolitic_Supplement': Block */
-static const OnigCodePoint CR_In_Glagolitic_Supplement[] = {
- 1,
- 0x1e000, 0x1e02f,
-}; /* CR_In_Glagolitic_Supplement */
-
-/* 'In_Mende_Kikakui': Block */
-static const OnigCodePoint CR_In_Mende_Kikakui[] = {
- 1,
- 0x1e800, 0x1e8df,
-}; /* CR_In_Mende_Kikakui */
-
-/* 'In_Adlam': Block */
-static const OnigCodePoint CR_In_Adlam[] = {
- 1,
- 0x1e900, 0x1e95f,
-}; /* CR_In_Adlam */
-
-/* 'In_Arabic_Mathematical_Alphabetic_Symbols': Block */
-static const OnigCodePoint CR_In_Arabic_Mathematical_Alphabetic_Symbols[] = {
- 1,
- 0x1ee00, 0x1eeff,
-}; /* CR_In_Arabic_Mathematical_Alphabetic_Symbols */
-
-/* 'In_Mahjong_Tiles': Block */
-static const OnigCodePoint CR_In_Mahjong_Tiles[] = {
- 1,
- 0x1f000, 0x1f02f,
-}; /* CR_In_Mahjong_Tiles */
-
-/* 'In_Domino_Tiles': Block */
-static const OnigCodePoint CR_In_Domino_Tiles[] = {
- 1,
- 0x1f030, 0x1f09f,
-}; /* CR_In_Domino_Tiles */
-
-/* 'In_Playing_Cards': Block */
-static const OnigCodePoint CR_In_Playing_Cards[] = {
- 1,
- 0x1f0a0, 0x1f0ff,
-}; /* CR_In_Playing_Cards */
-
-/* 'In_Enclosed_Alphanumeric_Supplement': Block */
-static const OnigCodePoint CR_In_Enclosed_Alphanumeric_Supplement[] = {
- 1,
- 0x1f100, 0x1f1ff,
-}; /* CR_In_Enclosed_Alphanumeric_Supplement */
-
-/* 'In_Enclosed_Ideographic_Supplement': Block */
-static const OnigCodePoint CR_In_Enclosed_Ideographic_Supplement[] = {
- 1,
- 0x1f200, 0x1f2ff,
-}; /* CR_In_Enclosed_Ideographic_Supplement */
-
-/* 'In_Miscellaneous_Symbols_and_Pictographs': Block */
-static const OnigCodePoint CR_In_Miscellaneous_Symbols_and_Pictographs[] = {
- 1,
- 0x1f300, 0x1f5ff,
-}; /* CR_In_Miscellaneous_Symbols_and_Pictographs */
-
-/* 'In_Emoticons': Block */
-static const OnigCodePoint CR_In_Emoticons[] = {
- 1,
- 0x1f600, 0x1f64f,
-}; /* CR_In_Emoticons */
-
-/* 'In_Ornamental_Dingbats': Block */
-static const OnigCodePoint CR_In_Ornamental_Dingbats[] = {
- 1,
- 0x1f650, 0x1f67f,
-}; /* CR_In_Ornamental_Dingbats */
-
-/* 'In_Transport_and_Map_Symbols': Block */
-static const OnigCodePoint CR_In_Transport_and_Map_Symbols[] = {
- 1,
- 0x1f680, 0x1f6ff,
-}; /* CR_In_Transport_and_Map_Symbols */
-
-/* 'In_Alchemical_Symbols': Block */
-static const OnigCodePoint CR_In_Alchemical_Symbols[] = {
- 1,
- 0x1f700, 0x1f77f,
-}; /* CR_In_Alchemical_Symbols */
-
-/* 'In_Geometric_Shapes_Extended': Block */
-static const OnigCodePoint CR_In_Geometric_Shapes_Extended[] = {
- 1,
- 0x1f780, 0x1f7ff,
-}; /* CR_In_Geometric_Shapes_Extended */
-
-/* 'In_Supplemental_Arrows_C': Block */
-static const OnigCodePoint CR_In_Supplemental_Arrows_C[] = {
- 1,
- 0x1f800, 0x1f8ff,
-}; /* CR_In_Supplemental_Arrows_C */
-
-/* 'In_Supplemental_Symbols_and_Pictographs': Block */
-static const OnigCodePoint CR_In_Supplemental_Symbols_and_Pictographs[] = {
- 1,
- 0x1f900, 0x1f9ff,
-}; /* CR_In_Supplemental_Symbols_and_Pictographs */
-
-/* 'In_CJK_Unified_Ideographs_Extension_B': Block */
-static const OnigCodePoint CR_In_CJK_Unified_Ideographs_Extension_B[] = {
- 1,
- 0x20000, 0x2a6df,
-}; /* CR_In_CJK_Unified_Ideographs_Extension_B */
-
-/* 'In_CJK_Unified_Ideographs_Extension_C': Block */
-static const OnigCodePoint CR_In_CJK_Unified_Ideographs_Extension_C[] = {
- 1,
- 0x2a700, 0x2b73f,
-}; /* CR_In_CJK_Unified_Ideographs_Extension_C */
-
-/* 'In_CJK_Unified_Ideographs_Extension_D': Block */
-static const OnigCodePoint CR_In_CJK_Unified_Ideographs_Extension_D[] = {
- 1,
- 0x2b740, 0x2b81f,
-}; /* CR_In_CJK_Unified_Ideographs_Extension_D */
-
-/* 'In_CJK_Unified_Ideographs_Extension_E': Block */
-static const OnigCodePoint CR_In_CJK_Unified_Ideographs_Extension_E[] = {
- 1,
- 0x2b820, 0x2ceaf,
-}; /* CR_In_CJK_Unified_Ideographs_Extension_E */
-
-/* 'In_CJK_Compatibility_Ideographs_Supplement': Block */
-static const OnigCodePoint CR_In_CJK_Compatibility_Ideographs_Supplement[] = {
- 1,
- 0x2f800, 0x2fa1f,
-}; /* CR_In_CJK_Compatibility_Ideographs_Supplement */
-
-/* 'In_Tags': Block */
-static const OnigCodePoint CR_In_Tags[] = {
- 1,
- 0xe0000, 0xe007f,
-}; /* CR_In_Tags */
-
-/* 'In_Variation_Selectors_Supplement': Block */
-static const OnigCodePoint CR_In_Variation_Selectors_Supplement[] = {
- 1,
- 0xe0100, 0xe01ef,
-}; /* CR_In_Variation_Selectors_Supplement */
-
-/* 'In_Supplementary_Private_Use_Area_A': Block */
-static const OnigCodePoint CR_In_Supplementary_Private_Use_Area_A[] = {
- 1,
- 0xf0000, 0xfffff,
-}; /* CR_In_Supplementary_Private_Use_Area_A */
-
-/* 'In_Supplementary_Private_Use_Area_B': Block */
-static const OnigCodePoint CR_In_Supplementary_Private_Use_Area_B[] = {
- 1,
- 0x100000, 0x10ffff,
-}; /* CR_In_Supplementary_Private_Use_Area_B */
-
-/* 'In_No_Block': Block */
-static const OnigCodePoint CR_In_No_Block[] = {
- 44,
- 0x0860, 0x089f,
- 0x1c90, 0x1cbf,
- 0x2fe0, 0x2fef,
- 0x10200, 0x1027f,
- 0x103e0, 0x103ff,
- 0x10570, 0x105ff,
- 0x10780, 0x107ff,
- 0x108b0, 0x108df,
- 0x10940, 0x1097f,
- 0x10aa0, 0x10abf,
- 0x10bb0, 0x10bff,
- 0x10c50, 0x10c7f,
- 0x10d00, 0x10e5f,
- 0x10e80, 0x10fff,
- 0x11250, 0x1127f,
- 0x11380, 0x113ff,
- 0x114e0, 0x1157f,
- 0x116d0, 0x116ff,
- 0x11740, 0x1189f,
- 0x11900, 0x11abf,
- 0x11b00, 0x11bff,
- 0x11cc0, 0x11fff,
- 0x12550, 0x12fff,
- 0x13430, 0x143ff,
- 0x14680, 0x167ff,
- 0x16a70, 0x16acf,
- 0x16b90, 0x16eff,
- 0x16fa0, 0x16fdf,
- 0x18b00, 0x1afff,
- 0x1b100, 0x1bbff,
- 0x1bcb0, 0x1cfff,
- 0x1d250, 0x1d2ff,
- 0x1d380, 0x1d3ff,
- 0x1dab0, 0x1dfff,
- 0x1e030, 0x1e7ff,
- 0x1e8e0, 0x1e8ff,
- 0x1e960, 0x1edff,
- 0x1ef00, 0x1efff,
- 0x1fa00, 0x1ffff,
- 0x2a6e0, 0x2a6ff,
- 0x2ceb0, 0x2f7ff,
- 0x2fa20, 0xdffff,
- 0xe0080, 0xe00ff,
- 0xe01f0, 0xeffff,
-}; /* CR_In_No_Block */
-
-#endif /* USE_UNICODE_PROPERTIES */
-static const OnigCodePoint* const CodeRanges[] = {
- CR_NEWLINE,
- CR_Alpha,
- CR_Blank,
- CR_Cntrl,
- CR_Digit,
- CR_Graph,
- CR_Lower,
- CR_Print,
- CR_Punct,
- CR_Space,
- CR_Upper,
- CR_XDigit,
- CR_Word,
- CR_Alnum,
- CR_ASCII,
-#ifdef USE_UNICODE_PROPERTIES
- CR_Any,
- CR_Assigned,
- CR_C,
- CR_Cc,
- CR_Cf,
- CR_Cn,
- CR_Co,
- CR_Cs,
- CR_L,
- CR_LC,
- CR_Ll,
- CR_Lm,
- CR_Lo,
- CR_Lt,
- CR_Lu,
- CR_M,
- CR_Mc,
- CR_Me,
- CR_Mn,
- CR_N,
- CR_Nd,
- CR_Nl,
- CR_No,
- CR_P,
- CR_Pc,
- CR_Pd,
- CR_Pe,
- CR_Pf,
- CR_Pi,
- CR_Po,
- CR_Ps,
- CR_S,
- CR_Sc,
- CR_Sk,
- CR_Sm,
- CR_So,
- CR_Z,
- CR_Zl,
- CR_Zp,
- CR_Zs,
- CR_Math,
- CR_Alphabetic,
- CR_Lowercase,
- CR_Uppercase,
- CR_Cased,
- CR_Case_Ignorable,
- CR_Changes_When_Lowercased,
- CR_Changes_When_Uppercased,
- CR_Changes_When_Titlecased,
- CR_Changes_When_Casefolded,
- CR_Changes_When_Casemapped,
- CR_ID_Start,
- CR_ID_Continue,
- CR_XID_Start,
- CR_XID_Continue,
- CR_Default_Ignorable_Code_Point,
- CR_Grapheme_Extend,
- CR_Grapheme_Base,
- CR_Grapheme_Link,
- CR_Common,
- CR_Latin,
- CR_Greek,
- CR_Cyrillic,
- CR_Armenian,
- CR_Hebrew,
- CR_Arabic,
- CR_Syriac,
- CR_Thaana,
- CR_Devanagari,
- CR_Bengali,
- CR_Gurmukhi,
- CR_Gujarati,
- CR_Oriya,
- CR_Tamil,
- CR_Telugu,
- CR_Kannada,
- CR_Malayalam,
- CR_Sinhala,
- CR_Thai,
- CR_Lao,
- CR_Tibetan,
- CR_Myanmar,
- CR_Georgian,
- CR_Hangul,
- CR_Ethiopic,
- CR_Cherokee,
- CR_Canadian_Aboriginal,
- CR_Ogham,
- CR_Runic,
- CR_Khmer,
- CR_Mongolian,
- CR_Hiragana,
- CR_Katakana,
- CR_Bopomofo,
- CR_Han,
- CR_Yi,
- CR_Old_Italic,
- CR_Gothic,
- CR_Deseret,
- CR_Inherited,
- CR_Tagalog,
- CR_Hanunoo,
- CR_Buhid,
- CR_Tagbanwa,
- CR_Limbu,
- CR_Tai_Le,
- CR_Linear_B,
- CR_Ugaritic,
- CR_Shavian,
- CR_Osmanya,
- CR_Cypriot,
- CR_Braille,
- CR_Buginese,
- CR_Coptic,
- CR_New_Tai_Lue,
- CR_Glagolitic,
- CR_Tifinagh,
- CR_Syloti_Nagri,
- CR_Old_Persian,
- CR_Kharoshthi,
- CR_Balinese,
- CR_Cuneiform,
- CR_Phoenician,
- CR_Phags_Pa,
- CR_Nko,
- CR_Sundanese,
- CR_Lepcha,
- CR_Ol_Chiki,
- CR_Vai,
- CR_Saurashtra,
- CR_Kayah_Li,
- CR_Rejang,
- CR_Lycian,
- CR_Carian,
- CR_Lydian,
- CR_Cham,
- CR_Tai_Tham,
- CR_Tai_Viet,
- CR_Avestan,
- CR_Egyptian_Hieroglyphs,
- CR_Samaritan,
- CR_Lisu,
- CR_Bamum,
- CR_Javanese,
- CR_Meetei_Mayek,
- CR_Imperial_Aramaic,
- CR_Old_South_Arabian,
- CR_Inscriptional_Parthian,
- CR_Inscriptional_Pahlavi,
- CR_Old_Turkic,
- CR_Kaithi,
- CR_Batak,
- CR_Brahmi,
- CR_Mandaic,
- CR_Chakma,
- CR_Meroitic_Cursive,
- CR_Meroitic_Hieroglyphs,
- CR_Miao,
- CR_Sharada,
- CR_Sora_Sompeng,
- CR_Takri,
- CR_Caucasian_Albanian,
- CR_Bassa_Vah,
- CR_Duployan,
- CR_Elbasan,
- CR_Grantha,
- CR_Pahawh_Hmong,
- CR_Khojki,
- CR_Linear_A,
- CR_Mahajani,
- CR_Manichaean,
- CR_Mende_Kikakui,
- CR_Modi,
- CR_Mro,
- CR_Old_North_Arabian,
- CR_Nabataean,
- CR_Palmyrene,
- CR_Pau_Cin_Hau,
- CR_Old_Permic,
- CR_Psalter_Pahlavi,
- CR_Siddham,
- CR_Khudawadi,
- CR_Tirhuta,
- CR_Warang_Citi,
- CR_Ahom,
- CR_Anatolian_Hieroglyphs,
- CR_Hatran,
- CR_Multani,
- CR_Old_Hungarian,
- CR_SignWriting,
- CR_Adlam,
- CR_Bhaiksuki,
- CR_Marchen,
- CR_Newa,
- CR_Osage,
- CR_Tangut,
- CR_White_Space,
- CR_Bidi_Control,
- CR_Join_Control,
- CR_Dash,
- CR_Hyphen,
- CR_Quotation_Mark,
- CR_Terminal_Punctuation,
- CR_Other_Math,
- CR_Hex_Digit,
- CR_ASCII_Hex_Digit,
- CR_Other_Alphabetic,
- CR_Ideographic,
- CR_Diacritic,
- CR_Extender,
- CR_Other_Lowercase,
- CR_Other_Uppercase,
- CR_Noncharacter_Code_Point,
- CR_Other_Grapheme_Extend,
- CR_IDS_Binary_Operator,
- CR_IDS_Trinary_Operator,
- CR_Radical,
- CR_Unified_Ideograph,
- CR_Other_Default_Ignorable_Code_Point,
- CR_Deprecated,
- CR_Soft_Dotted,
- CR_Logical_Order_Exception,
- CR_Other_ID_Start,
- CR_Other_ID_Continue,
- CR_Sentence_Terminal,
- CR_Variation_Selector,
- CR_Pattern_White_Space,
- CR_Pattern_Syntax,
- CR_Prepended_Concatenation_Mark,
- CR_Unknown,
-#ifdef USE_UNICODE_AGE_PROPERTIES
- CR_Age_1_1,
- CR_Age_2_0,
- CR_Age_2_1,
- CR_Age_3_0,
- CR_Age_3_1,
- CR_Age_3_2,
- CR_Age_4_0,
- CR_Age_4_1,
- CR_Age_5_0,
- CR_Age_5_1,
- CR_Age_5_2,
- CR_Age_6_0,
- CR_Age_6_1,
- CR_Age_6_2,
- CR_Age_6_3,
- CR_Age_7_0,
- CR_Age_8_0,
- CR_Age_9_0,
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- CR_In_Basic_Latin,
- CR_In_Latin_1_Supplement,
- CR_In_Latin_Extended_A,
- CR_In_Latin_Extended_B,
- CR_In_IPA_Extensions,
- CR_In_Spacing_Modifier_Letters,
- CR_In_Combining_Diacritical_Marks,
- CR_In_Greek_and_Coptic,
- CR_In_Cyrillic,
- CR_In_Cyrillic_Supplement,
- CR_In_Armenian,
- CR_In_Hebrew,
- CR_In_Arabic,
- CR_In_Syriac,
- CR_In_Arabic_Supplement,
- CR_In_Thaana,
- CR_In_NKo,
- CR_In_Samaritan,
- CR_In_Mandaic,
- CR_In_Arabic_Extended_A,
- CR_In_Devanagari,
- CR_In_Bengali,
- CR_In_Gurmukhi,
- CR_In_Gujarati,
- CR_In_Oriya,
- CR_In_Tamil,
- CR_In_Telugu,
- CR_In_Kannada,
- CR_In_Malayalam,
- CR_In_Sinhala,
- CR_In_Thai,
- CR_In_Lao,
- CR_In_Tibetan,
- CR_In_Myanmar,
- CR_In_Georgian,
- CR_In_Hangul_Jamo,
- CR_In_Ethiopic,
- CR_In_Ethiopic_Supplement,
- CR_In_Cherokee,
- CR_In_Unified_Canadian_Aboriginal_Syllabics,
- CR_In_Ogham,
- CR_In_Runic,
- CR_In_Tagalog,
- CR_In_Hanunoo,
- CR_In_Buhid,
- CR_In_Tagbanwa,
- CR_In_Khmer,
- CR_In_Mongolian,
- CR_In_Unified_Canadian_Aboriginal_Syllabics_Extended,
- CR_In_Limbu,
- CR_In_Tai_Le,
- CR_In_New_Tai_Lue,
- CR_In_Khmer_Symbols,
- CR_In_Buginese,
- CR_In_Tai_Tham,
- CR_In_Combining_Diacritical_Marks_Extended,
- CR_In_Balinese,
- CR_In_Sundanese,
- CR_In_Batak,
- CR_In_Lepcha,
- CR_In_Ol_Chiki,
- CR_In_Cyrillic_Extended_C,
- CR_In_Sundanese_Supplement,
- CR_In_Vedic_Extensions,
- CR_In_Phonetic_Extensions,
- CR_In_Phonetic_Extensions_Supplement,
- CR_In_Combining_Diacritical_Marks_Supplement,
- CR_In_Latin_Extended_Additional,
- CR_In_Greek_Extended,
- CR_In_General_Punctuation,
- CR_In_Superscripts_and_Subscripts,
- CR_In_Currency_Symbols,
- CR_In_Combining_Diacritical_Marks_for_Symbols,
- CR_In_Letterlike_Symbols,
- CR_In_Number_Forms,
- CR_In_Arrows,
- CR_In_Mathematical_Operators,
- CR_In_Miscellaneous_Technical,
- CR_In_Control_Pictures,
- CR_In_Optical_Character_Recognition,
- CR_In_Enclosed_Alphanumerics,
- CR_In_Box_Drawing,
- CR_In_Block_Elements,
- CR_In_Geometric_Shapes,
- CR_In_Miscellaneous_Symbols,
- CR_In_Dingbats,
- CR_In_Miscellaneous_Mathematical_Symbols_A,
- CR_In_Supplemental_Arrows_A,
- CR_In_Braille_Patterns,
- CR_In_Supplemental_Arrows_B,
- CR_In_Miscellaneous_Mathematical_Symbols_B,
- CR_In_Supplemental_Mathematical_Operators,
- CR_In_Miscellaneous_Symbols_and_Arrows,
- CR_In_Glagolitic,
- CR_In_Latin_Extended_C,
- CR_In_Coptic,
- CR_In_Georgian_Supplement,
- CR_In_Tifinagh,
- CR_In_Ethiopic_Extended,
- CR_In_Cyrillic_Extended_A,
- CR_In_Supplemental_Punctuation,
- CR_In_CJK_Radicals_Supplement,
- CR_In_Kangxi_Radicals,
- CR_In_Ideographic_Description_Characters,
- CR_In_CJK_Symbols_and_Punctuation,
- CR_In_Hiragana,
- CR_In_Katakana,
- CR_In_Bopomofo,
- CR_In_Hangul_Compatibility_Jamo,
- CR_In_Kanbun,
- CR_In_Bopomofo_Extended,
- CR_In_CJK_Strokes,
- CR_In_Katakana_Phonetic_Extensions,
- CR_In_Enclosed_CJK_Letters_and_Months,
- CR_In_CJK_Compatibility,
- CR_In_CJK_Unified_Ideographs_Extension_A,
- CR_In_Yijing_Hexagram_Symbols,
- CR_In_CJK_Unified_Ideographs,
- CR_In_Yi_Syllables,
- CR_In_Yi_Radicals,
- CR_In_Lisu,
- CR_In_Vai,
- CR_In_Cyrillic_Extended_B,
- CR_In_Bamum,
- CR_In_Modifier_Tone_Letters,
- CR_In_Latin_Extended_D,
- CR_In_Syloti_Nagri,
- CR_In_Common_Indic_Number_Forms,
- CR_In_Phags_pa,
- CR_In_Saurashtra,
- CR_In_Devanagari_Extended,
- CR_In_Kayah_Li,
- CR_In_Rejang,
- CR_In_Hangul_Jamo_Extended_A,
- CR_In_Javanese,
- CR_In_Myanmar_Extended_B,
- CR_In_Cham,
- CR_In_Myanmar_Extended_A,
- CR_In_Tai_Viet,
- CR_In_Meetei_Mayek_Extensions,
- CR_In_Ethiopic_Extended_A,
- CR_In_Latin_Extended_E,
- CR_In_Cherokee_Supplement,
- CR_In_Meetei_Mayek,
- CR_In_Hangul_Syllables,
- CR_In_Hangul_Jamo_Extended_B,
- CR_In_High_Surrogates,
- CR_In_High_Private_Use_Surrogates,
- CR_In_Low_Surrogates,
- CR_In_Private_Use_Area,
- CR_In_CJK_Compatibility_Ideographs,
- CR_In_Alphabetic_Presentation_Forms,
- CR_In_Arabic_Presentation_Forms_A,
- CR_In_Variation_Selectors,
- CR_In_Vertical_Forms,
- CR_In_Combining_Half_Marks,
- CR_In_CJK_Compatibility_Forms,
- CR_In_Small_Form_Variants,
- CR_In_Arabic_Presentation_Forms_B,
- CR_In_Halfwidth_and_Fullwidth_Forms,
- CR_In_Specials,
- CR_In_Linear_B_Syllabary,
- CR_In_Linear_B_Ideograms,
- CR_In_Aegean_Numbers,
- CR_In_Ancient_Greek_Numbers,
- CR_In_Ancient_Symbols,
- CR_In_Phaistos_Disc,
- CR_In_Lycian,
- CR_In_Carian,
- CR_In_Coptic_Epact_Numbers,
- CR_In_Old_Italic,
- CR_In_Gothic,
- CR_In_Old_Permic,
- CR_In_Ugaritic,
- CR_In_Old_Persian,
- CR_In_Deseret,
- CR_In_Shavian,
- CR_In_Osmanya,
- CR_In_Osage,
- CR_In_Elbasan,
- CR_In_Caucasian_Albanian,
- CR_In_Linear_A,
- CR_In_Cypriot_Syllabary,
- CR_In_Imperial_Aramaic,
- CR_In_Palmyrene,
- CR_In_Nabataean,
- CR_In_Hatran,
- CR_In_Phoenician,
- CR_In_Lydian,
- CR_In_Meroitic_Hieroglyphs,
- CR_In_Meroitic_Cursive,
- CR_In_Kharoshthi,
- CR_In_Old_South_Arabian,
- CR_In_Old_North_Arabian,
- CR_In_Manichaean,
- CR_In_Avestan,
- CR_In_Inscriptional_Parthian,
- CR_In_Inscriptional_Pahlavi,
- CR_In_Psalter_Pahlavi,
- CR_In_Old_Turkic,
- CR_In_Old_Hungarian,
- CR_In_Rumi_Numeral_Symbols,
- CR_In_Brahmi,
- CR_In_Kaithi,
- CR_In_Sora_Sompeng,
- CR_In_Chakma,
- CR_In_Mahajani,
- CR_In_Sharada,
- CR_In_Sinhala_Archaic_Numbers,
- CR_In_Khojki,
- CR_In_Multani,
- CR_In_Khudawadi,
- CR_In_Grantha,
- CR_In_Newa,
- CR_In_Tirhuta,
- CR_In_Siddham,
- CR_In_Modi,
- CR_In_Mongolian_Supplement,
- CR_In_Takri,
- CR_In_Ahom,
- CR_In_Warang_Citi,
- CR_In_Pau_Cin_Hau,
- CR_In_Bhaiksuki,
- CR_In_Marchen,
- CR_In_Cuneiform,
- CR_In_Cuneiform_Numbers_and_Punctuation,
- CR_In_Early_Dynastic_Cuneiform,
- CR_In_Egyptian_Hieroglyphs,
- CR_In_Anatolian_Hieroglyphs,
- CR_In_Bamum_Supplement,
- CR_In_Mro,
- CR_In_Bassa_Vah,
- CR_In_Pahawh_Hmong,
- CR_In_Miao,
- CR_In_Ideographic_Symbols_and_Punctuation,
- CR_In_Tangut,
- CR_In_Tangut_Components,
- CR_In_Kana_Supplement,
- CR_In_Duployan,
- CR_In_Shorthand_Format_Controls,
- CR_In_Byzantine_Musical_Symbols,
- CR_In_Musical_Symbols,
- CR_In_Ancient_Greek_Musical_Notation,
- CR_In_Tai_Xuan_Jing_Symbols,
- CR_In_Counting_Rod_Numerals,
- CR_In_Mathematical_Alphanumeric_Symbols,
- CR_In_Sutton_SignWriting,
- CR_In_Glagolitic_Supplement,
- CR_In_Mende_Kikakui,
- CR_In_Adlam,
- CR_In_Arabic_Mathematical_Alphabetic_Symbols,
- CR_In_Mahjong_Tiles,
- CR_In_Domino_Tiles,
- CR_In_Playing_Cards,
- CR_In_Enclosed_Alphanumeric_Supplement,
- CR_In_Enclosed_Ideographic_Supplement,
- CR_In_Miscellaneous_Symbols_and_Pictographs,
- CR_In_Emoticons,
- CR_In_Ornamental_Dingbats,
- CR_In_Transport_and_Map_Symbols,
- CR_In_Alchemical_Symbols,
- CR_In_Geometric_Shapes_Extended,
- CR_In_Supplemental_Arrows_C,
- CR_In_Supplemental_Symbols_and_Pictographs,
- CR_In_CJK_Unified_Ideographs_Extension_B,
- CR_In_CJK_Unified_Ideographs_Extension_C,
- CR_In_CJK_Unified_Ideographs_Extension_D,
- CR_In_CJK_Unified_Ideographs_Extension_E,
- CR_In_CJK_Compatibility_Ideographs_Supplement,
- CR_In_Tags,
- CR_In_Variation_Selectors_Supplement,
- CR_In_Supplementary_Private_Use_Area_A,
- CR_In_Supplementary_Private_Use_Area_B,
- CR_In_No_Block,
-#endif /* USE_UNICODE_PROPERTIES */
-};
-struct uniname2ctype_struct {
- int name, ctype;
-};
-
-static const struct uniname2ctype_struct *uniname2ctype_p(const char *, unsigned int);
-
-#ifndef USE_UNICODE_PROPERTIES
-#define TOTAL_KEYWORDS 14
-#define MIN_WORD_LENGTH 4
-#define MAX_WORD_LENGTH 6
-#define MIN_HASH_VALUE 6
-#define MAX_HASH_VALUE 19
-/* maximum key range = 14, duplicates = 0 */
-#else /* USE_UNICODE_PROPERTIES */
-#ifndef USE_UNICODE_AGE_PROPERTIES
-#define TOTAL_KEYWORDS 735
-#else /* USE_UNICODE_AGE_PROPERTIES */
-#define TOTAL_KEYWORDS 753
-#endif /* USE_UNICODE_AGE_PROPERTIES */
-#define MIN_WORD_LENGTH 1
-#define MAX_WORD_LENGTH 44
-#define MIN_HASH_VALUE 3
-#define MAX_HASH_VALUE 4563
-/* maximum key range = 4561, duplicates = 0 */
-#endif /* USE_UNICODE_PROPERTIES */
-
-#ifdef __GNUC__
-__inline
-#else
-#ifdef __cplusplus
-inline
-#endif
-#endif
-static unsigned int
-uniname2ctype_hash (str, len)
- register const char *str;
- register unsigned int len;
-{
-#ifndef USE_UNICODE_PROPERTIES
- static const unsigned char asso_values[] =
-#else /* USE_UNICODE_PROPERTIES */
- static const unsigned short asso_values[] =
-#endif /* USE_UNICODE_PROPERTIES */
- {
-#ifndef USE_UNICODE_PROPERTIES
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 3, 11, 5,
- 4, 20, 20, 9, 20, 1, 20, 20, 10, 20,
- 2, 20, 1, 20, 1, 7, 4, 6, 20, 1,
- 4, 20, 20, 20, 20, 20, 20, 20
-#else /* USE_UNICODE_PROPERTIES */
- 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564,
- 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564,
- 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564,
- 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564,
-#ifndef USE_UNICODE_AGE_PROPERTIES
- 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564,
- 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564,
-#else /* USE_UNICODE_AGE_PROPERTIES */
- 4564, 4564, 4564, 4564, 4564, 4564, 1, 4564, 13, 1,
- 2, 12, 8, 16, 10, 19, 17, 11, 4564, 4564,
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564,
- 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564,
- 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564, 4564,
- 4564, 4564, 4564, 4564, 4564, 4564, 4564, 13, 1303, 20,
- 497, 25, 58, 891, 322, 4, 30, 1489, 169, 7,
- 1, 267, 633, 1, 214, 40, 92, 950, 713, 277,
- 105, 1264, 18, 4564, 4564, 4564, 4564, 4564
-#endif /* USE_UNICODE_PROPERTIES */
- };
-#ifndef USE_UNICODE_PROPERTIES
- return len + asso_values[(unsigned char)str[2]] + asso_values[(unsigned char)str[0]];
-#else /* USE_UNICODE_PROPERTIES */
- register int hval = len;
-
- switch (hval)
- {
- default:
- hval += asso_values[(unsigned char)str[15]];
- /*FALLTHROUGH*/
- case 15:
- case 14:
- case 13:
- case 12:
- hval += asso_values[(unsigned char)str[11]];
- /*FALLTHROUGH*/
- case 11:
- case 10:
- case 9:
- case 8:
- case 7:
- case 6:
- hval += asso_values[(unsigned char)str[5]];
- /*FALLTHROUGH*/
- case 5:
- hval += asso_values[(unsigned char)str[4]];
- /*FALLTHROUGH*/
- case 4:
- case 3:
- hval += asso_values[(unsigned char)str[2]];
- /*FALLTHROUGH*/
- case 2:
- hval += asso_values[(unsigned char)str[1]];
- /*FALLTHROUGH*/
- case 1:
- hval += asso_values[(unsigned char)str[0]];
- break;
- }
- return hval + asso_values[(unsigned char)str[len - 1]];
-#endif /* USE_UNICODE_PROPERTIES */
-}
-
-struct uniname2ctype_pool_t
- {
-#ifndef USE_UNICODE_PROPERTIES
- char uniname2ctype_pool_str6[sizeof("word")];
- char uniname2ctype_pool_str7[sizeof("print")];
- char uniname2ctype_pool_str8[sizeof("punct")];
- char uniname2ctype_pool_str9[sizeof("alpha")];
- char uniname2ctype_pool_str10[sizeof("alnum")];
- char uniname2ctype_pool_str11[sizeof("xdigit")];
- char uniname2ctype_pool_str12[sizeof("upper")];
- char uniname2ctype_pool_str13[sizeof("ascii")];
- char uniname2ctype_pool_str14[sizeof("cntrl")];
- char uniname2ctype_pool_str15[sizeof("space")];
- char uniname2ctype_pool_str16[sizeof("lower")];
- char uniname2ctype_pool_str17[sizeof("graph")];
- char uniname2ctype_pool_str18[sizeof("digit")];
- char uniname2ctype_pool_str19[sizeof("blank")];
-#else /* USE_UNICODE_PROPERTIES */
- char uniname2ctype_pool_str3[sizeof("n")];
- char uniname2ctype_pool_str11[sizeof("mn")];
- char uniname2ctype_pool_str15[sizeof("m")];
- char uniname2ctype_pool_str24[sizeof("cn")];
- char uniname2ctype_pool_str29[sizeof("mani")];
- char uniname2ctype_pool_str30[sizeof("ci")];
- char uniname2ctype_pool_str31[sizeof("inmanichaean")];
- char uniname2ctype_pool_str35[sizeof("qaai")];
- char uniname2ctype_pool_str37[sizeof("z")];
- char uniname2ctype_pool_str41[sizeof("c")];
- char uniname2ctype_pool_str49[sizeof("mc")];
- char uniname2ctype_pool_str51[sizeof("qaac")];
- char uniname2ctype_pool_str56[sizeof("sm")];
- char uniname2ctype_pool_str58[sizeof("incham")];
- char uniname2ctype_pool_str59[sizeof("me")];
- char uniname2ctype_pool_str61[sizeof("inarmenian")];
- char uniname2ctype_pool_str62[sizeof("cc")];
- char uniname2ctype_pool_str65[sizeof("mandaic")];
- char uniname2ctype_pool_str69[sizeof("incuneiform")];
- char uniname2ctype_pool_str76[sizeof("zzzz")];
- char uniname2ctype_pool_str77[sizeof("insamaritan")];
- char uniname2ctype_pool_str78[sizeof("cans")];
- char uniname2ctype_pool_str81[sizeof("s")];
- char uniname2ctype_pool_str82[sizeof("sc")];
- char uniname2ctype_pool_str86[sizeof("ascii")];
- char uniname2ctype_pool_str93[sizeof("inavestan")];
- char uniname2ctype_pool_str100[sizeof("zs")];
- char uniname2ctype_pool_str102[sizeof("cs")];
- char uniname2ctype_pool_str106[sizeof("inipaextensions")];
- char uniname2ctype_pool_str110[sizeof("incuneiformnumbersandpunctuation")];
- char uniname2ctype_pool_str114[sizeof("incommonindicnumberforms")];
- char uniname2ctype_pool_str124[sizeof("inthai")];
- char uniname2ctype_pool_str132[sizeof("mtei")];
- char uniname2ctype_pool_str138[sizeof("cf")];
- char uniname2ctype_pool_str140[sizeof("inspecials")];
- char uniname2ctype_pool_str141[sizeof("initialpunctuation")];
- char uniname2ctype_pool_str144[sizeof("inthaana")];
- char uniname2ctype_pool_str145[sizeof("inancientsymbols")];
- char uniname2ctype_pool_str152[sizeof("inmiscellaneousmathematicalsymbolsa")];
- char uniname2ctype_pool_str159[sizeof("inmusicalsymbols")];
- char uniname2ctype_pool_str164[sizeof("taile")];
- char uniname2ctype_pool_str174[sizeof("inmyanmarextendeda")];
- char uniname2ctype_pool_str176[sizeof("sterm")];
- char uniname2ctype_pool_str185[sizeof("lm")];
- char uniname2ctype_pool_str191[sizeof("lina")];
- char uniname2ctype_pool_str199[sizeof("inmiscellaneoussymbols")];
- char uniname2ctype_pool_str200[sizeof("lana")];
- char uniname2ctype_pool_str201[sizeof("intransportandmapsymbols")];
- char uniname2ctype_pool_str202[sizeof("alnum")];
- char uniname2ctype_pool_str207[sizeof("inlycian")];
- char uniname2ctype_pool_str208[sizeof("inmiscellaneoussymbolsandarrows")];
- char uniname2ctype_pool_str209[sizeof("intaitham")];
- char uniname2ctype_pool_str211[sizeof("lc")];
- char uniname2ctype_pool_str212[sizeof("inmalayalam")];
- char uniname2ctype_pool_str213[sizeof("inmiscellaneoussymbolsandpictographs")];
- char uniname2ctype_pool_str214[sizeof("inadlam")];
- char uniname2ctype_pool_str220[sizeof("incontrolpictures")];
- char uniname2ctype_pool_str222[sizeof("inlineara")];
- char uniname2ctype_pool_str237[sizeof("taiviet")];
- char uniname2ctype_pool_str239[sizeof("armn")];
- char uniname2ctype_pool_str242[sizeof("armi")];
- char uniname2ctype_pool_str247[sizeof("sinhala")];
- char uniname2ctype_pool_str248[sizeof("armenian")];
- char uniname2ctype_pool_str249[sizeof("inmyanmar")];
- char uniname2ctype_pool_str251[sizeof("inrunic")];
- char uniname2ctype_pool_str252[sizeof("incarian")];
- char uniname2ctype_pool_str255[sizeof("cari")];
- char uniname2ctype_pool_str256[sizeof("inmarchen")];
- char uniname2ctype_pool_str258[sizeof("marc")];
- char uniname2ctype_pool_str268[sizeof("carian")];
- char uniname2ctype_pool_str270[sizeof("merc")];
- char uniname2ctype_pool_str273[sizeof("incyrillic")];
- char uniname2ctype_pool_str274[sizeof("intaixuanjingsymbols")];
- char uniname2ctype_pool_str278[sizeof("samr")];
- char uniname2ctype_pool_str279[sizeof("latn")];
- char uniname2ctype_pool_str281[sizeof("latin")];
- char uniname2ctype_pool_str282[sizeof("ital")];
- char uniname2ctype_pool_str284[sizeof("intamil")];
- char uniname2ctype_pool_str285[sizeof("taml")];
- char uniname2ctype_pool_str286[sizeof("inmultani")];
- char uniname2ctype_pool_str288[sizeof("samaritan")];
- char uniname2ctype_pool_str290[sizeof("arabic")];
- char uniname2ctype_pool_str291[sizeof("insyriac")];
- char uniname2ctype_pool_str294[sizeof("insharada")];
- char uniname2ctype_pool_str295[sizeof("miao")];
- char uniname2ctype_pool_str296[sizeof("inlinearbideograms")];
- char uniname2ctype_pool_str299[sizeof("incherokee")];
- char uniname2ctype_pool_str302[sizeof("intaile")];
- char uniname2ctype_pool_str303[sizeof("tale")];
- char uniname2ctype_pool_str305[sizeof("inahom")];
- char uniname2ctype_pool_str309[sizeof("inmeeteimayekextensions")];
- char uniname2ctype_pool_str310[sizeof("inruminumeralsymbols")];
- char uniname2ctype_pool_str313[sizeof("inlatinextendeda")];
- char uniname2ctype_pool_str314[sizeof("inosmanya")];
- char uniname2ctype_pool_str315[sizeof("innewa")];
- char uniname2ctype_pool_str317[sizeof("ext")];
- char uniname2ctype_pool_str320[sizeof("newa")];
- char uniname2ctype_pool_str327[sizeof("inlatinextendedc")];
- char uniname2ctype_pool_str328[sizeof("cwcm")];
- char uniname2ctype_pool_str331[sizeof("osma")];
- char uniname2ctype_pool_str337[sizeof("inlatinextendede")];
- char uniname2ctype_pool_str339[sizeof("l")];
- char uniname2ctype_pool_str340[sizeof("han")];
- char uniname2ctype_pool_str341[sizeof("nl")];
- char uniname2ctype_pool_str342[sizeof("term")];
- char uniname2ctype_pool_str344[sizeof("hani")];
- char uniname2ctype_pool_str346[sizeof("joinc")];
- char uniname2ctype_pool_str347[sizeof("inideographicsymbolsandpunctuation")];
- char uniname2ctype_pool_str349[sizeof("zinh")];
- char uniname2ctype_pool_str354[sizeof("newtailue")];
- char uniname2ctype_pool_str355[sizeof("lt")];
- char uniname2ctype_pool_str358[sizeof("zl")];
- char uniname2ctype_pool_str361[sizeof("inmahajani")];
- char uniname2ctype_pool_str366[sizeof("cham")];
- char uniname2ctype_pool_str368[sizeof("connectorpunctuation")];
- char uniname2ctype_pool_str371[sizeof("sinh")];
- char uniname2ctype_pool_str374[sizeof("manichaean")];
- char uniname2ctype_pool_str375[sizeof("osage")];
- char uniname2ctype_pool_str376[sizeof("mahj")];
- char uniname2ctype_pool_str379[sizeof("cwcf")];
- char uniname2ctype_pool_str382[sizeof("inmiscellaneoustechnical")];
- char uniname2ctype_pool_str386[sizeof("inethiopic")];
- char uniname2ctype_pool_str390[sizeof("insinhala")];
- char uniname2ctype_pool_str394[sizeof("chakma")];
- char uniname2ctype_pool_str397[sizeof("mahajani")];
- char uniname2ctype_pool_str400[sizeof("shavian")];
- char uniname2ctype_pool_str410[sizeof("inenclosedcjklettersandmonths")];
- char uniname2ctype_pool_str411[sizeof("innewtailue")];
- char uniname2ctype_pool_str419[sizeof("inideographicdescriptioncharacters")];
- char uniname2ctype_pool_str421[sizeof("lineara")];
- char uniname2ctype_pool_str422[sizeof("meroiticcursive")];
- char uniname2ctype_pool_str435[sizeof("thai")];
- char uniname2ctype_pool_str438[sizeof("math")];
- char uniname2ctype_pool_str440[sizeof("inemoticons")];
- char uniname2ctype_pool_str444[sizeof("thaa")];
- char uniname2ctype_pool_str447[sizeof("ethi")];
- char uniname2ctype_pool_str448[sizeof("hatran")];
- char uniname2ctype_pool_str452[sizeof("inenclosedalphanumerics")];
- char uniname2ctype_pool_str453[sizeof("sentenceterminal")];
- char uniname2ctype_pool_str455[sizeof("tamil")];
- char uniname2ctype_pool_str456[sizeof("cntrl")];
- char uniname2ctype_pool_str458[sizeof("taitham")];
- char uniname2ctype_pool_str460[sizeof("thaana")];
- char uniname2ctype_pool_str468[sizeof("terminalpunctuation")];
- char uniname2ctype_pool_str469[sizeof("ahex")];
- char uniname2ctype_pool_str471[sizeof("insinhalaarchaicnumbers")];
- char uniname2ctype_pool_str478[sizeof("inlatinextendedadditional")];
- char uniname2ctype_pool_str484[sizeof("cwt")];
- char uniname2ctype_pool_str489[sizeof("loe")];
- char uniname2ctype_pool_str491[sizeof("intifinagh")];
- char uniname2ctype_pool_str498[sizeof("tifinagh")];
- char uniname2ctype_pool_str503[sizeof("inopticalcharacterrecognition")];
- char uniname2ctype_pool_str504[sizeof("inearlydynasticcuneiform")];
- char uniname2ctype_pool_str507[sizeof("di")];
- char uniname2ctype_pool_str508[sizeof("asciihexdigit")];
- char uniname2ctype_pool_str509[sizeof("ll")];
- char uniname2ctype_pool_str512[sizeof("inscriptionalparthian")];
- char uniname2ctype_pool_str513[sizeof("inenclosedalphanumericsupplement")];
- char uniname2ctype_pool_str516[sizeof("oriya")];
- char uniname2ctype_pool_str517[sizeof("mero")];
- char uniname2ctype_pool_str521[sizeof("wara")];
- char uniname2ctype_pool_str522[sizeof("mand")];
- char uniname2ctype_pool_str523[sizeof("inmodi")];
- char uniname2ctype_pool_str529[sizeof("inwarangciti")];
- char uniname2ctype_pool_str530[sizeof("dia")];
- char uniname2ctype_pool_str534[sizeof("mend")];
- char uniname2ctype_pool_str537[sizeof("no")];
- char uniname2ctype_pool_str538[sizeof("sora")];
- char uniname2ctype_pool_str539[sizeof("inmandaic")];
- char uniname2ctype_pool_str544[sizeof("idc")];
- char uniname2ctype_pool_str545[sizeof("innko")];
- char uniname2ctype_pool_str546[sizeof("sind")];
- char uniname2ctype_pool_str547[sizeof("inarrows")];
- char uniname2ctype_pool_str551[sizeof("inmro")];
- char uniname2ctype_pool_str554[sizeof("titlecaseletter")];
- char uniname2ctype_pool_str556[sizeof("co")];
- char uniname2ctype_pool_str557[sizeof("hira")];
- char uniname2ctype_pool_str559[sizeof("inlowsurrogates")];
- char uniname2ctype_pool_str560[sizeof("hex")];
- char uniname2ctype_pool_str565[sizeof("inmiao")];
- char uniname2ctype_pool_str569[sizeof("common")];
- char uniname2ctype_pool_str576[sizeof("so")];
- char uniname2ctype_pool_str577[sizeof("inhiragana")];
- char uniname2ctype_pool_str579[sizeof("insundanese")];
- char uniname2ctype_pool_str584[sizeof("ids")];
- char uniname2ctype_pool_str585[sizeof("cher")];
- char uniname2ctype_pool_str587[sizeof("inmahjongtiles")];
- char uniname2ctype_pool_str589[sizeof("marchen")];
- char uniname2ctype_pool_str591[sizeof("indominotiles")];
- char uniname2ctype_pool_str607[sizeof("hano")];
- char uniname2ctype_pool_str613[sizeof("ahom")];
- char uniname2ctype_pool_str621[sizeof("inogham")];
- char uniname2ctype_pool_str622[sizeof("inscriptionalpahlavi")];
- char uniname2ctype_pool_str627[sizeof("inolchiki")];
- char uniname2ctype_pool_str630[sizeof("xidc")];
- char uniname2ctype_pool_str636[sizeof("tirh")];
- char uniname2ctype_pool_str637[sizeof("idst")];
- char uniname2ctype_pool_str638[sizeof("cwl")];
- char uniname2ctype_pool_str642[sizeof("inhatran")];
- char uniname2ctype_pool_str643[sizeof("pi")];
- char uniname2ctype_pool_str645[sizeof("hatr")];
- char uniname2ctype_pool_str649[sizeof("idcontinue")];
- char uniname2ctype_pool_str650[sizeof("xids")];
- char uniname2ctype_pool_str655[sizeof("intirhuta")];
- char uniname2ctype_pool_str656[sizeof("shaw")];
- char uniname2ctype_pool_str662[sizeof("inshorthandformatcontrols")];
- char uniname2ctype_pool_str664[sizeof("inspacingmodifierletters")];
- char uniname2ctype_pool_str668[sizeof("indeseret")];
- char uniname2ctype_pool_str670[sizeof("pcm")];
- char uniname2ctype_pool_str675[sizeof("pc")];
- char uniname2ctype_pool_str684[sizeof("inlydian")];
- char uniname2ctype_pool_str685[sizeof("pe")];
- char uniname2ctype_pool_str688[sizeof("inmathematicalalphanumericsymbols")];
- char uniname2ctype_pool_str690[sizeof("adlm")];
- char uniname2ctype_pool_str693[sizeof("inphaistosdisc")];
- char uniname2ctype_pool_str698[sizeof("adlam")];
- char uniname2ctype_pool_str703[sizeof("whitespace")];
- char uniname2ctype_pool_str705[sizeof("lo")];
- char uniname2ctype_pool_str712[sizeof("insylotinagri")];
- char uniname2ctype_pool_str713[sizeof("inlao")];
- char uniname2ctype_pool_str715[sizeof("ps")];
- char uniname2ctype_pool_str717[sizeof("anatolianhieroglyphs")];
- char uniname2ctype_pool_str719[sizeof("lao")];
- char uniname2ctype_pool_str720[sizeof("laoo")];
- char uniname2ctype_pool_str721[sizeof("mongolian")];
- char uniname2ctype_pool_str731[sizeof("invai")];
- char uniname2ctype_pool_str733[sizeof("lineseparator")];
- char uniname2ctype_pool_str737[sizeof("vai")];
- char uniname2ctype_pool_str738[sizeof("vaii")];
- char uniname2ctype_pool_str741[sizeof("space")];
- char uniname2ctype_pool_str742[sizeof("format")];
- char uniname2ctype_pool_str745[sizeof("letter")];
- char uniname2ctype_pool_str751[sizeof("pf")];
- char uniname2ctype_pool_str758[sizeof("mro")];
- char uniname2ctype_pool_str759[sizeof("mroo")];
- char uniname2ctype_pool_str761[sizeof("diacritic")];
- char uniname2ctype_pool_str768[sizeof("joincontrol")];
- char uniname2ctype_pool_str773[sizeof("java")];
- char uniname2ctype_pool_str774[sizeof("inanatolianhieroglyphs")];
- char uniname2ctype_pool_str775[sizeof("odi")];
- char uniname2ctype_pool_str776[sizeof("nchar")];
- char uniname2ctype_pool_str778[sizeof("incoptic")];
- char uniname2ctype_pool_str779[sizeof("modi")];
- char uniname2ctype_pool_str781[sizeof("inshavian")];
- char uniname2ctype_pool_str792[sizeof("oidc")];
- char uniname2ctype_pool_str795[sizeof("vs")];
- char uniname2ctype_pool_str796[sizeof("injavanese")];
- char uniname2ctype_pool_str797[sizeof("ideo")];
- char uniname2ctype_pool_str800[sizeof("xdigit")];
- char uniname2ctype_pool_str803[sizeof("oalpha")];
- char uniname2ctype_pool_str804[sizeof("inolditalic")];
- char uniname2ctype_pool_str811[sizeof("xidstart")];
- char uniname2ctype_pool_str812[sizeof("oids")];
- char uniname2ctype_pool_str814[sizeof("decimalnumber")];
- char uniname2ctype_pool_str815[sizeof("javanese")];
- char uniname2ctype_pool_str823[sizeof("patws")];
- char uniname2ctype_pool_str826[sizeof("palm")];
- char uniname2ctype_pool_str834[sizeof("psalterpahlavi")];
- char uniname2ctype_pool_str837[sizeof("incopticepactnumbers")];
- char uniname2ctype_pool_str846[sizeof("alpha")];
- char uniname2ctype_pool_str847[sizeof("dsrt")];
- char uniname2ctype_pool_str848[sizeof("inlepcha")];
- char uniname2ctype_pool_str850[sizeof("inpalmyrene")];
- char uniname2ctype_pool_str851[sizeof("lepc")];
- char uniname2ctype_pool_str862[sizeof("avst")];
- char uniname2ctype_pool_str864[sizeof("avestan")];
- char uniname2ctype_pool_str865[sizeof("insundanesesupplement")];
- char uniname2ctype_pool_str866[sizeof("inmodifiertoneletters")];
- char uniname2ctype_pool_str867[sizeof("idstart")];
- char uniname2ctype_pool_str876[sizeof("dash")];
- char uniname2ctype_pool_str877[sizeof("incyrillicextendeda")];
- char uniname2ctype_pool_str878[sizeof("hanunoo")];
- char uniname2ctype_pool_str883[sizeof("perm")];
- char uniname2ctype_pool_str884[sizeof("incyrillicextendedc")];
- char uniname2ctype_pool_str890[sizeof("siddham")];
- char uniname2ctype_pool_str892[sizeof("inoldturkic")];
- char uniname2ctype_pool_str896[sizeof("finalpunctuation")];
- char uniname2ctype_pool_str900[sizeof("deseret")];
- char uniname2ctype_pool_str904[sizeof("imperialaramaic")];
- char uniname2ctype_pool_str905[sizeof("sharada")];
- char uniname2ctype_pool_str910[sizeof("xidcontinue")];
- char uniname2ctype_pool_str914[sizeof("tavt")];
- char uniname2ctype_pool_str915[sizeof("intaiviet")];
- char uniname2ctype_pool_str916[sizeof("inmongolian")];
- char uniname2ctype_pool_str922[sizeof("inimperialaramaic")];
- char uniname2ctype_pool_str932[sizeof("ingrantha")];
- char uniname2ctype_pool_str933[sizeof("radical")];
- char uniname2ctype_pool_str936[sizeof("omath")];
- char uniname2ctype_pool_str937[sizeof("canadianaboriginal")];
-#ifdef USE_UNICODE_AGE_PROPERTIES
- char uniname2ctype_pool_str939[sizeof("age=1.1")];
- char uniname2ctype_pool_str940[sizeof("age=2.1")];
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- char uniname2ctype_pool_str942[sizeof("extender")];
- char uniname2ctype_pool_str943[sizeof("otheridcontinue")];
- char uniname2ctype_pool_str944[sizeof("inphoenician")];
- char uniname2ctype_pool_str945[sizeof("control")];
-#ifdef USE_UNICODE_AGE_PROPERTIES
- char uniname2ctype_pool_str946[sizeof("age=4.1")];
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- char uniname2ctype_pool_str947[sizeof("prti")];
-#ifdef USE_UNICODE_AGE_PROPERTIES
- char uniname2ctype_pool_str948[sizeof("age=6.1")];
- char uniname2ctype_pool_str949[sizeof("age=6.2")];
- char uniname2ctype_pool_str950[sizeof("age=3.1")];
- char uniname2ctype_pool_str951[sizeof("age=3.2")];
- char uniname2ctype_pool_str952[sizeof("age=2.0")];
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- char uniname2ctype_pool_str953[sizeof("ingujarati")];
-#ifdef USE_UNICODE_AGE_PROPERTIES
- char uniname2ctype_pool_str954[sizeof("age=5.1")];
- char uniname2ctype_pool_str955[sizeof("age=5.2")];
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- char uniname2ctype_pool_str956[sizeof("noncharactercodepoint")];
- char uniname2ctype_pool_str957[sizeof("ingeneralpunctuation")];
-#ifdef USE_UNICODE_AGE_PROPERTIES
- char uniname2ctype_pool_str958[sizeof("age=4.0")];
- char uniname2ctype_pool_str959[sizeof("age=6.3")];
- char uniname2ctype_pool_str960[sizeof("age=6.0")];
- char uniname2ctype_pool_str961[sizeof("age=9.0")];
- char uniname2ctype_pool_str962[sizeof("age=3.0")];
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- char uniname2ctype_pool_str963[sizeof("cprt")];
- char uniname2ctype_pool_str964[sizeof("casedletter")];
- char uniname2ctype_pool_str965[sizeof("letternumber")];
-#ifdef USE_UNICODE_AGE_PROPERTIES
- char uniname2ctype_pool_str966[sizeof("age=5.0")];
- char uniname2ctype_pool_str967[sizeof("age=8.0")];
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- char uniname2ctype_pool_str968[sizeof("otheralphabetic")];
-#ifdef USE_UNICODE_AGE_PROPERTIES
- char uniname2ctype_pool_str969[sizeof("age=7.0")];
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- char uniname2ctype_pool_str970[sizeof("coptic")];
- char uniname2ctype_pool_str981[sizeof("lowercase")];
- char uniname2ctype_pool_str990[sizeof("inethiopicextendeda")];
- char uniname2ctype_pool_str991[sizeof("privateuse")];
- char uniname2ctype_pool_str993[sizeof("ininscriptionalparthian")];
- char uniname2ctype_pool_str995[sizeof("ininscriptionalpahlavi")];
- char uniname2ctype_pool_str997[sizeof("nd")];
- char uniname2ctype_pool_str1001[sizeof("tang")];
- char uniname2ctype_pool_str1012[sizeof("insmallformvariants")];
- char uniname2ctype_pool_str1015[sizeof("inoldnortharabian")];
- char uniname2ctype_pool_str1016[sizeof("copt")];
- char uniname2ctype_pool_str1026[sizeof("wspace")];
- char uniname2ctype_pool_str1029[sizeof("incaucasianalbanian")];
- char uniname2ctype_pool_str1031[sizeof("tagbanwa")];
- char uniname2ctype_pool_str1034[sizeof("xpeo")];
- char uniname2ctype_pool_str1036[sizeof("sd")];
- char uniname2ctype_pool_str1038[sizeof("inlatin1supplement")];
- char uniname2ctype_pool_str1039[sizeof("sundanese")];
- char uniname2ctype_pool_str1040[sizeof("print")];
- char uniname2ctype_pool_str1042[sizeof("sidd")];
- char uniname2ctype_pool_str1046[sizeof("tfng")];
- char uniname2ctype_pool_str1047[sizeof("inancientgreekmusicalnotation")];
- char uniname2ctype_pool_str1049[sizeof("cuneiform")];
- char uniname2ctype_pool_str1051[sizeof("inherited")];
- char uniname2ctype_pool_str1053[sizeof("inenclosedideographicsupplement")];
- char uniname2ctype_pool_str1054[sizeof("inoldsoutharabian")];
- char uniname2ctype_pool_str1055[sizeof("insiddham")];
- char uniname2ctype_pool_str1056[sizeof("invariationselectors")];
- char uniname2ctype_pool_str1064[sizeof("patternwhitespace")];
- char uniname2ctype_pool_str1065[sizeof("phnx")];
- char uniname2ctype_pool_str1067[sizeof("olditalic")];
- char uniname2ctype_pool_str1069[sizeof("idsbinaryoperator")];
- char uniname2ctype_pool_str1071[sizeof("spaceseparator")];
- char uniname2ctype_pool_str1072[sizeof("cased")];
- char uniname2ctype_pool_str1074[sizeof("intags")];
- char uniname2ctype_pool_str1077[sizeof("shrd")];
- char uniname2ctype_pool_str1078[sizeof("inancientgreeknumbers")];
- char uniname2ctype_pool_str1079[sizeof("saurashtra")];
- char uniname2ctype_pool_str1089[sizeof("intangut")];
- char uniname2ctype_pool_str1095[sizeof("otheridstart")];
- char uniname2ctype_pool_str1102[sizeof("inalphabeticpresentationforms")];
- char uniname2ctype_pool_str1107[sizeof("inunifiedcanadianaboriginalsyllabics")];
- char uniname2ctype_pool_str1108[sizeof("patternsyntax")];
- char uniname2ctype_pool_str1110[sizeof("inoldhungarian")];
- char uniname2ctype_pool_str1111[sizeof("inphoneticextensions")];
- char uniname2ctype_pool_str1114[sizeof("other")];
- char uniname2ctype_pool_str1115[sizeof("othersymbol")];
- char uniname2ctype_pool_str1116[sizeof("otherlowercase")];
- char uniname2ctype_pool_str1118[sizeof("invariationselectorssupplement")];
- char uniname2ctype_pool_str1121[sizeof("othernumber")];
- char uniname2ctype_pool_str1123[sizeof("gran")];
- char uniname2ctype_pool_str1131[sizeof("ingurmukhi")];
- char uniname2ctype_pool_str1132[sizeof("phli")];
- char uniname2ctype_pool_str1140[sizeof("rjng")];
- char uniname2ctype_pool_str1142[sizeof("inmathematicaloperators")];
- char uniname2ctype_pool_str1145[sizeof("closepunctuation")];
- char uniname2ctype_pool_str1146[sizeof("lower")];
- char uniname2ctype_pool_str1148[sizeof("separator")];
- char uniname2ctype_pool_str1151[sizeof("multani")];
- char uniname2ctype_pool_str1153[sizeof("modifierletter")];
- char uniname2ctype_pool_str1161[sizeof("inrejang")];
- char uniname2ctype_pool_str1162[sizeof("olower")];
- char uniname2ctype_pool_str1166[sizeof("inpsalterpahlavi")];
- char uniname2ctype_pool_str1167[sizeof("lisu")];
- char uniname2ctype_pool_str1169[sizeof("po")];
- char uniname2ctype_pool_str1170[sizeof("mong")];
- char uniname2ctype_pool_str1173[sizeof("inphoneticextensionssupplement")];
- char uniname2ctype_pool_str1175[sizeof("caseignorable")];
- char uniname2ctype_pool_str1177[sizeof("insorasompeng")];
- char uniname2ctype_pool_str1181[sizeof("lepcha")];
- char uniname2ctype_pool_str1182[sizeof("ogam")];
- char uniname2ctype_pool_str1183[sizeof("oldpersian")];
- char uniname2ctype_pool_str1201[sizeof("oldpermic")];
- char uniname2ctype_pool_str1204[sizeof("xsux")];
- char uniname2ctype_pool_str1208[sizeof("inosage")];
- char uniname2ctype_pool_str1210[sizeof("runic")];
- char uniname2ctype_pool_str1212[sizeof("inugaritic")];
- char uniname2ctype_pool_str1213[sizeof("sgnw")];
- char uniname2ctype_pool_str1221[sizeof("saur")];
- char uniname2ctype_pool_str1222[sizeof("mult")];
- char uniname2ctype_pool_str1225[sizeof("hmng")];
- char uniname2ctype_pool_str1226[sizeof("inmongoliansupplement")];
- char uniname2ctype_pool_str1227[sizeof("osge")];
- char uniname2ctype_pool_str1228[sizeof("talu")];
- char uniname2ctype_pool_str1231[sizeof("hang")];
- char uniname2ctype_pool_str1232[sizeof("insuttonsignwriting")];
- char uniname2ctype_pool_str1233[sizeof("othermath")];
- char uniname2ctype_pool_str1238[sizeof("phoenician")];
- char uniname2ctype_pool_str1240[sizeof("telu")];
- char uniname2ctype_pool_str1246[sizeof("tangut")];
- char uniname2ctype_pool_str1247[sizeof("insaurashtra")];
- char uniname2ctype_pool_str1248[sizeof("indevanagari")];
- char uniname2ctype_pool_str1252[sizeof("deva")];
- char uniname2ctype_pool_str1259[sizeof("word")];
- char uniname2ctype_pool_str1263[sizeof("devanagari")];
- char uniname2ctype_pool_str1267[sizeof("p")];
- char uniname2ctype_pool_str1268[sizeof("lowercaseletter")];
- char uniname2ctype_pool_str1269[sizeof("ingreekandcoptic")];
- char uniname2ctype_pool_str1274[sizeof("yi")];
- char uniname2ctype_pool_str1278[sizeof("invedicextensions")];
- char uniname2ctype_pool_str1280[sizeof("yiii")];
- char uniname2ctype_pool_str1281[sizeof("inlatinextendedd")];
- char uniname2ctype_pool_str1286[sizeof("zp")];
- char uniname2ctype_pool_str1289[sizeof("otherletter")];
- char uniname2ctype_pool_str1292[sizeof("ingeometricshapes")];
- char uniname2ctype_pool_str1300[sizeof("gothic")];
- char uniname2ctype_pool_str1311[sizeof("incountingrodnumerals")];
- char uniname2ctype_pool_str1315[sizeof("induployan")];
- char uniname2ctype_pool_str1319[sizeof("grext")];
- char uniname2ctype_pool_str1330[sizeof("incyrillicsupplement")];
- char uniname2ctype_pool_str1334[sizeof("innabataean")];
- char uniname2ctype_pool_str1338[sizeof("ingothic")];
- char uniname2ctype_pool_str1340[sizeof("inbhaiksuki")];
- char uniname2ctype_pool_str1342[sizeof("bamum")];
- char uniname2ctype_pool_str1346[sizeof("inverticalforms")];
- char uniname2ctype_pool_str1351[sizeof("incherokeesupplement")];
- char uniname2ctype_pool_str1356[sizeof("inelbasan")];
- char uniname2ctype_pool_str1362[sizeof("inarabic")];
- char uniname2ctype_pool_str1366[sizeof("inbasiclatin")];
- char uniname2ctype_pool_str1367[sizeof("ethiopic")];
- char uniname2ctype_pool_str1372[sizeof("tirhuta")];
- char uniname2ctype_pool_str1376[sizeof("innumberforms")];
- char uniname2ctype_pool_str1383[sizeof("runr")];
- char uniname2ctype_pool_str1386[sizeof("inyijinghexagramsymbols")];
- char uniname2ctype_pool_str1388[sizeof("ingeorgian")];
- char uniname2ctype_pool_str1400[sizeof("bass")];
- char uniname2ctype_pool_str1401[sizeof("geor")];
- char uniname2ctype_pool_str1404[sizeof("insuperscriptsandsubscripts")];
- char uniname2ctype_pool_str1406[sizeof("inornamentaldingbats")];
- char uniname2ctype_pool_str1410[sizeof("warangciti")];
- char uniname2ctype_pool_str1413[sizeof("nbat")];
- char uniname2ctype_pool_str1416[sizeof("inoldpersian")];
- char uniname2ctype_pool_str1417[sizeof("number")];
- char uniname2ctype_pool_str1422[sizeof("inprivateusearea")];
- char uniname2ctype_pool_str1426[sizeof("inarabicpresentationformsa")];
- char uniname2ctype_pool_str1431[sizeof("inbyzantinemusicalsymbols")];
- char uniname2ctype_pool_str1432[sizeof("nabataean")];
- char uniname2ctype_pool_str1433[sizeof("inoldpermic")];
- char uniname2ctype_pool_str1435[sizeof("intibetan")];
- char uniname2ctype_pool_str1442[sizeof("inmiscellaneousmathematicalsymbolsb")];
- char uniname2ctype_pool_str1443[sizeof("inethiopicsupplement")];
- char uniname2ctype_pool_str1444[sizeof("indingbats")];
- char uniname2ctype_pool_str1447[sizeof("hexdigit")];
- char uniname2ctype_pool_str1449[sizeof("inmeroitichieroglyphs")];
- char uniname2ctype_pool_str1451[sizeof("mlym")];
- char uniname2ctype_pool_str1459[sizeof("ingreekextended")];
- char uniname2ctype_pool_str1461[sizeof("lyci")];
- char uniname2ctype_pool_str1464[sizeof("inmyanmarextendedb")];
- char uniname2ctype_pool_str1465[sizeof("hiragana")];
- char uniname2ctype_pool_str1470[sizeof("inhangulsyllables")];
- char uniname2ctype_pool_str1473[sizeof("inethiopicextended")];
- char uniname2ctype_pool_str1474[sizeof("lycian")];
- char uniname2ctype_pool_str1475[sizeof("inbraillepatterns")];
- char uniname2ctype_pool_str1481[sizeof("linb")];
- char uniname2ctype_pool_str1482[sizeof("malayalam")];
- char uniname2ctype_pool_str1487[sizeof("limb")];
- char uniname2ctype_pool_str1490[sizeof("assigned")];
- char uniname2ctype_pool_str1491[sizeof("insupplementalmathematicaloperators")];
- char uniname2ctype_pool_str1492[sizeof("sund")];
- char uniname2ctype_pool_str1493[sizeof("bali")];
- char uniname2ctype_pool_str1495[sizeof("tibt")];
- char uniname2ctype_pool_str1496[sizeof("mymr")];
- char uniname2ctype_pool_str1499[sizeof("ogham")];
- char uniname2ctype_pool_str1512[sizeof("tibetan")];
- char uniname2ctype_pool_str1514[sizeof("elba")];
- char uniname2ctype_pool_str1515[sizeof("unassigned")];
- char uniname2ctype_pool_str1516[sizeof("inbalinese")];
- char uniname2ctype_pool_str1517[sizeof("plrd")];
- char uniname2ctype_pool_str1518[sizeof("inkannada")];
- char uniname2ctype_pool_str1520[sizeof("kana")];
- char uniname2ctype_pool_str1522[sizeof("bengali")];
- char uniname2ctype_pool_str1525[sizeof("myanmar")];
- char uniname2ctype_pool_str1527[sizeof("graphemebase")];
- char uniname2ctype_pool_str1533[sizeof("cakm")];
- char uniname2ctype_pool_str1535[sizeof("narb")];
- char uniname2ctype_pool_str1538[sizeof("brai")];
- char uniname2ctype_pool_str1539[sizeof("glagolitic")];
- char uniname2ctype_pool_str1542[sizeof("syrc")];
- char uniname2ctype_pool_str1544[sizeof("balinese")];
- char uniname2ctype_pool_str1545[sizeof("inhanguljamoextendeda")];
- char uniname2ctype_pool_str1547[sizeof("arab")];
- char uniname2ctype_pool_str1548[sizeof("inchakma")];
- char uniname2ctype_pool_str1551[sizeof("brahmi")];
- char uniname2ctype_pool_str1552[sizeof("grantha")];
- char uniname2ctype_pool_str1554[sizeof("inhanunoo")];
- char uniname2ctype_pool_str1558[sizeof("elbasan")];
- char uniname2ctype_pool_str1559[sizeof("otherpunctuation")];
- char uniname2ctype_pool_str1560[sizeof("inoriya")];
- char uniname2ctype_pool_str1564[sizeof("inphagspa")];
- char uniname2ctype_pool_str1567[sizeof("mendekikakui")];
- char uniname2ctype_pool_str1572[sizeof("inunifiedcanadianaboriginalsyllabicsextended")];
- char uniname2ctype_pool_str1574[sizeof("sarb")];
- char uniname2ctype_pool_str1576[sizeof("goth")];
- char uniname2ctype_pool_str1577[sizeof("syriac")];
- char uniname2ctype_pool_str1581[sizeof("digit")];
- char uniname2ctype_pool_str1584[sizeof("sylotinagri")];
- char uniname2ctype_pool_str1585[sizeof("inhalfwidthandfullwidthforms")];
- char uniname2ctype_pool_str1586[sizeof("meeteimayek")];
- char uniname2ctype_pool_str1588[sizeof("inyiradicals")];
- char uniname2ctype_pool_str1595[sizeof("mathsymbol")];
- char uniname2ctype_pool_str1599[sizeof("osmanya")];
- char uniname2ctype_pool_str1602[sizeof("inkaithi")];
- char uniname2ctype_pool_str1605[sizeof("incjkcompatibilityforms")];
- char uniname2ctype_pool_str1607[sizeof("takri")];
- char uniname2ctype_pool_str1610[sizeof("incjkcompatibilityideographs")];
- char uniname2ctype_pool_str1620[sizeof("pauc")];
- char uniname2ctype_pool_str1621[sizeof("logicalorderexception")];
- char uniname2ctype_pool_str1622[sizeof("inkatakana")];
- char uniname2ctype_pool_str1629[sizeof("pd")];
- char uniname2ctype_pool_str1630[sizeof("hangul")];
- char uniname2ctype_pool_str1636[sizeof("softdotted")];
- char uniname2ctype_pool_str1646[sizeof("incjkstrokes")];
- char uniname2ctype_pool_str1651[sizeof("insupplementalarrowsa")];
- char uniname2ctype_pool_str1655[sizeof("inbrahmi")];
- char uniname2ctype_pool_str1656[sizeof("inmeeteimayek")];
- char uniname2ctype_pool_str1658[sizeof("insupplementalarrowsc")];
- char uniname2ctype_pool_str1661[sizeof("phagspa")];
- char uniname2ctype_pool_str1662[sizeof("ideographic")];
- char uniname2ctype_pool_str1664[sizeof("inlinearbsyllabary")];
- char uniname2ctype_pool_str1667[sizeof("ogrext")];
- char uniname2ctype_pool_str1671[sizeof("cyrl")];
- char uniname2ctype_pool_str1672[sizeof("incjkcompatibilityideographssupplement")];
- char uniname2ctype_pool_str1675[sizeof("inblockelements")];
- char uniname2ctype_pool_str1679[sizeof("kali")];
- char uniname2ctype_pool_str1681[sizeof("intangutcomponents")];
- char uniname2ctype_pool_str1707[sizeof("deprecated")];
- char uniname2ctype_pool_str1711[sizeof("linearb")];
- char uniname2ctype_pool_str1712[sizeof("variationselector")];
- char uniname2ctype_pool_str1716[sizeof("idstrinaryoperator")];
- char uniname2ctype_pool_str1720[sizeof("inbassavah")];
- char uniname2ctype_pool_str1722[sizeof("hluw")];
- char uniname2ctype_pool_str1723[sizeof("inhighprivateusesurrogates")];
- char uniname2ctype_pool_str1724[sizeof("incombininghalfmarks")];
- char uniname2ctype_pool_str1727[sizeof("mark")];
- char uniname2ctype_pool_str1730[sizeof("sorasompeng")];
- char uniname2ctype_pool_str1733[sizeof("inkanasupplement")];
- char uniname2ctype_pool_str1741[sizeof("inkharoshthi")];
- char uniname2ctype_pool_str1744[sizeof("sylo")];
- char uniname2ctype_pool_str1745[sizeof("inletterlikesymbols")];
- char uniname2ctype_pool_str1747[sizeof("inkhmer")];
- char uniname2ctype_pool_str1757[sizeof("ingeometricshapesextended")];
- char uniname2ctype_pool_str1761[sizeof("phlp")];
- char uniname2ctype_pool_str1762[sizeof("orya")];
- char uniname2ctype_pool_str1765[sizeof("inhanguljamo")];
- char uniname2ctype_pool_str1767[sizeof("graph")];
- char uniname2ctype_pool_str1773[sizeof("punct")];
- char uniname2ctype_pool_str1774[sizeof("indevanagariextended")];
- char uniname2ctype_pool_str1791[sizeof("dep")];
- char uniname2ctype_pool_str1803[sizeof("inkhojki")];
- char uniname2ctype_pool_str1811[sizeof("intakri")];
- char uniname2ctype_pool_str1812[sizeof("takr")];
- char uniname2ctype_pool_str1816[sizeof("changeswhencasemapped")];
- char uniname2ctype_pool_str1825[sizeof("inarabicmathematicalalphabeticsymbols")];
- char uniname2ctype_pool_str1842[sizeof("kaithi")];
- char uniname2ctype_pool_str1847[sizeof("inkhmersymbols")];
- char uniname2ctype_pool_str1848[sizeof("idsb")];
- char uniname2ctype_pool_str1849[sizeof("bidic")];
- char uniname2ctype_pool_str1852[sizeof("inglagolitic")];
- char uniname2ctype_pool_str1856[sizeof("brah")];
- char uniname2ctype_pool_str1863[sizeof("phag")];
- char uniname2ctype_pool_str1864[sizeof("cyrillic")];
- char uniname2ctype_pool_str1867[sizeof("changeswhencasefolded")];
- char uniname2ctype_pool_str1868[sizeof("hebr")];
- char uniname2ctype_pool_str1873[sizeof("otherdefaultignorablecodepoint")];
- char uniname2ctype_pool_str1878[sizeof("inpahawhhmong")];
- char uniname2ctype_pool_str1879[sizeof("meroitichieroglyphs")];
- char uniname2ctype_pool_str1886[sizeof("inarabicextendeda")];
- char uniname2ctype_pool_str1891[sizeof("inalchemicalsymbols")];
- char uniname2ctype_pool_str1897[sizeof("otheruppercase")];
- char uniname2ctype_pool_str1898[sizeof("oldhungarian")];
- char uniname2ctype_pool_str1900[sizeof("braille")];
- char uniname2ctype_pool_str1901[sizeof("intagalog")];
- char uniname2ctype_pool_str1906[sizeof("changeswhentitlecased")];
- char uniname2ctype_pool_str1911[sizeof("kthi")];
- char uniname2ctype_pool_str1922[sizeof("symbol")];
- char uniname2ctype_pool_str1938[sizeof("lydi")];
- char uniname2ctype_pool_str1943[sizeof("incurrencysymbols")];
- char uniname2ctype_pool_str1949[sizeof("olck")];
- char uniname2ctype_pool_str1951[sizeof("lydian")];
- char uniname2ctype_pool_str1960[sizeof("olchiki")];
- char uniname2ctype_pool_str1968[sizeof("glag")];
- char uniname2ctype_pool_str1978[sizeof("ugaritic")];
- char uniname2ctype_pool_str1990[sizeof("uideo")];
- char uniname2ctype_pool_str2001[sizeof("graphemeextend")];
- char uniname2ctype_pool_str2004[sizeof("knda")];
- char uniname2ctype_pool_str2010[sizeof("patsyn")];
- char uniname2ctype_pool_str2015[sizeof("combiningmark")];
- char uniname2ctype_pool_str2017[sizeof("inmendekikakui")];
- char uniname2ctype_pool_str2027[sizeof("nko")];
- char uniname2ctype_pool_str2028[sizeof("nkoo")];
- char uniname2ctype_pool_str2033[sizeof("kannada")];
- char uniname2ctype_pool_str2036[sizeof("khmr")];
- char uniname2ctype_pool_str2042[sizeof("khar")];
- char uniname2ctype_pool_str2047[sizeof("tglg")];
- char uniname2ctype_pool_str2055[sizeof("defaultignorablecodepoint")];
- char uniname2ctype_pool_str2058[sizeof("rejang")];
- char uniname2ctype_pool_str2069[sizeof("enclosingmark")];
- char uniname2ctype_pool_str2071[sizeof("lu")];
- char uniname2ctype_pool_str2072[sizeof("ugar")];
- char uniname2ctype_pool_str2079[sizeof("insupplementaryprivateuseareaa")];
- char uniname2ctype_pool_str2081[sizeof("inkatakanaphoneticextensions")];
- char uniname2ctype_pool_str2085[sizeof("limbu")];
- char uniname2ctype_pool_str2087[sizeof("georgian")];
- char uniname2ctype_pool_str2089[sizeof("gujr")];
- char uniname2ctype_pool_str2102[sizeof("inarabicsupplement")];
- char uniname2ctype_pool_str2110[sizeof("gujarati")];
- char uniname2ctype_pool_str2112[sizeof("khoj")];
- char uniname2ctype_pool_str2113[sizeof("incombiningdiacriticalmarks")];
- char uniname2ctype_pool_str2120[sizeof("inlisu")];
- char uniname2ctype_pool_str2123[sizeof("incombiningdiacriticalmarksforsymbols")];
- char uniname2ctype_pool_str2126[sizeof("oldturkic")];
- char uniname2ctype_pool_str2129[sizeof("inhebrew")];
- char uniname2ctype_pool_str2138[sizeof("inbuhid")];
- char uniname2ctype_pool_str2145[sizeof("kharoshthi")];
- char uniname2ctype_pool_str2156[sizeof("cherokee")];
- char uniname2ctype_pool_str2161[sizeof("alphabetic")];
- char uniname2ctype_pool_str2167[sizeof("incyrillicextendedb")];
- char uniname2ctype_pool_str2168[sizeof("hung")];
- char uniname2ctype_pool_str2172[sizeof("changeswhenlowercased")];
- char uniname2ctype_pool_str2174[sizeof("intelugu")];
- char uniname2ctype_pool_str2175[sizeof("incombiningdiacriticalmarkssupplement")];
- char uniname2ctype_pool_str2183[sizeof("inplayingcards")];
- char uniname2ctype_pool_str2191[sizeof("spacingmark")];
- char uniname2ctype_pool_str2198[sizeof("inmeroiticcursive")];
- char uniname2ctype_pool_str2200[sizeof("cwu")];
- char uniname2ctype_pool_str2213[sizeof("inbengali")];
- char uniname2ctype_pool_str2224[sizeof("beng")];
- char uniname2ctype_pool_str2235[sizeof("hebrew")];
- char uniname2ctype_pool_str2238[sizeof("inbuginese")];
- char uniname2ctype_pool_str2241[sizeof("dashpunctuation")];
- char uniname2ctype_pool_str2251[sizeof("khmer")];
- char uniname2ctype_pool_str2252[sizeof("hyphen")];
- char uniname2ctype_pool_str2253[sizeof("dupl")];
- char uniname2ctype_pool_str2254[sizeof("incjksymbolsandpunctuation")];
- char uniname2ctype_pool_str2271[sizeof("bidicontrol")];
- char uniname2ctype_pool_str2277[sizeof("bamu")];
- char uniname2ctype_pool_str2279[sizeof("inbamum")];
- char uniname2ctype_pool_str2287[sizeof("cypriot")];
- char uniname2ctype_pool_str2292[sizeof("inaegeannumbers")];
- char uniname2ctype_pool_str2296[sizeof("orkh")];
- char uniname2ctype_pool_str2303[sizeof("tagb")];
- char uniname2ctype_pool_str2309[sizeof("oupper")];
- char uniname2ctype_pool_str2314[sizeof("intagbanwa")];
- char uniname2ctype_pool_str2319[sizeof("modifiersymbol")];
- char uniname2ctype_pool_str2327[sizeof("palmyrene")];
- char uniname2ctype_pool_str2328[sizeof("signwriting")];
- char uniname2ctype_pool_str2330[sizeof("tagalog")];
- char uniname2ctype_pool_str2336[sizeof("incypriotsyllabary")];
- char uniname2ctype_pool_str2370[sizeof("caucasianalbanian")];
- char uniname2ctype_pool_str2379[sizeof("insupplementalpunctuation")];
- char uniname2ctype_pool_str2396[sizeof("surrogate")];
- char uniname2ctype_pool_str2401[sizeof("othergraphemeextend")];
- char uniname2ctype_pool_str2412[sizeof("bassavah")];
- char uniname2ctype_pool_str2431[sizeof("nonspacingmark")];
- char uniname2ctype_pool_str2439[sizeof("prependedconcatenationmark")];
- char uniname2ctype_pool_str2441[sizeof("inlimbu")];
- char uniname2ctype_pool_str2464[sizeof("ingeorgiansupplement")];
- char uniname2ctype_pool_str2469[sizeof("pahawhhmong")];
- char uniname2ctype_pool_str2474[sizeof("bopo")];
- char uniname2ctype_pool_str2484[sizeof("uppercase")];
- char uniname2ctype_pool_str2485[sizeof("inbopomofo")];
- char uniname2ctype_pool_str2486[sizeof("inkangxiradicals")];
- char uniname2ctype_pool_str2504[sizeof("grbase")];
- char uniname2ctype_pool_str2527[sizeof("inhighsurrogates")];
- char uniname2ctype_pool_str2533[sizeof("aghb")];
- char uniname2ctype_pool_str2535[sizeof("unifiedideograph")];
- char uniname2ctype_pool_str2544[sizeof("inbamumsupplement")];
- char uniname2ctype_pool_str2545[sizeof("any")];
- char uniname2ctype_pool_str2560[sizeof("paucinhau")];
- char uniname2ctype_pool_str2567[sizeof("inglagoliticsupplement")];
- char uniname2ctype_pool_str2569[sizeof("inpaucinhau")];
- char uniname2ctype_pool_str2578[sizeof("incombiningdiacriticalmarksextended")];
- char uniname2ctype_pool_str2616[sizeof("openpunctuation")];
- char uniname2ctype_pool_str2623[sizeof("grek")];
- char uniname2ctype_pool_str2638[sizeof("punctuation")];
- char uniname2ctype_pool_str2645[sizeof("incjkradicalssupplement")];
- char uniname2ctype_pool_str2649[sizeof("upper")];
- char uniname2ctype_pool_str2651[sizeof("inyisyllables")];
- char uniname2ctype_pool_str2696[sizeof("currencysymbol")];
- char uniname2ctype_pool_str2716[sizeof("inarabicpresentationformsb")];
- char uniname2ctype_pool_str2733[sizeof("oldnortharabian")];
- char uniname2ctype_pool_str2742[sizeof("insupplementalsymbolsandpictographs")];
- char uniname2ctype_pool_str2752[sizeof("bopomofo")];
- char uniname2ctype_pool_str2771[sizeof("uppercaseletter")];
- char uniname2ctype_pool_str2784[sizeof("inkayahli")];
- char uniname2ctype_pool_str2807[sizeof("inkanbun")];
- char uniname2ctype_pool_str2817[sizeof("egyp")];
- char uniname2ctype_pool_str2824[sizeof("incjkcompatibility")];
- char uniname2ctype_pool_str2835[sizeof("inhanguljamoextendedb")];
- char uniname2ctype_pool_str2883[sizeof("inegyptianhieroglyphs")];
- char uniname2ctype_pool_str2893[sizeof("inlatinextendedb")];
- char uniname2ctype_pool_str2901[sizeof("batk")];
- char uniname2ctype_pool_str2909[sizeof("inbatak")];
- char uniname2ctype_pool_str2922[sizeof("paragraphseparator")];
- char uniname2ctype_pool_str2941[sizeof("insupplementalarrowsb")];
- char uniname2ctype_pool_str2953[sizeof("changeswhenuppercased")];
- char uniname2ctype_pool_str2956[sizeof("inkhudawadi")];
- char uniname2ctype_pool_str2976[sizeof("innoblock")];
- char uniname2ctype_pool_str2992[sizeof("unknown")];
- char uniname2ctype_pool_str3004[sizeof("qmark")];
- char uniname2ctype_pool_str3009[sizeof("guru")];
- char uniname2ctype_pool_str3020[sizeof("sk")];
- char uniname2ctype_pool_str3039[sizeof("quotationmark")];
- char uniname2ctype_pool_str3064[sizeof("khudawadi")];
- char uniname2ctype_pool_str3076[sizeof("buhd")];
- char uniname2ctype_pool_str3083[sizeof("telugu")];
- char uniname2ctype_pool_str3117[sizeof("katakana")];
- char uniname2ctype_pool_str3152[sizeof("bugi")];
- char uniname2ctype_pool_str3158[sizeof("bhks")];
- char uniname2ctype_pool_str3180[sizeof("bhaiksuki")];
- char uniname2ctype_pool_str3203[sizeof("buginese")];
- char uniname2ctype_pool_str3268[sizeof("kayahli")];
- char uniname2ctype_pool_str3273[sizeof("incjkunifiedideographsextensiona")];
- char uniname2ctype_pool_str3280[sizeof("incjkunifiedideographsextensionc")];
- char uniname2ctype_pool_str3285[sizeof("incjkunifiedideographsextensione")];
- char uniname2ctype_pool_str3290[sizeof("incjkunifiedideographs")];
- char uniname2ctype_pool_str3325[sizeof("inbopomofoextended")];
- char uniname2ctype_pool_str3369[sizeof("insupplementaryprivateuseareab")];
- char uniname2ctype_pool_str3447[sizeof("inhangulcompatibilityjamo")];
- char uniname2ctype_pool_str3469[sizeof("oldsoutharabian")];
- char uniname2ctype_pool_str3574[sizeof("buhid")];
- char uniname2ctype_pool_str3581[sizeof("khojki")];
- char uniname2ctype_pool_str3620[sizeof("duployan")];
- char uniname2ctype_pool_str3704[sizeof("inboxdrawing")];
- char uniname2ctype_pool_str3757[sizeof("incjkunifiedideographsextensiond")];
- char uniname2ctype_pool_str3813[sizeof("egyptianhieroglyphs")];
- char uniname2ctype_pool_str3814[sizeof("zyyy")];
- char uniname2ctype_pool_str4113[sizeof("greek")];
- char uniname2ctype_pool_str4259[sizeof("grlink")];
- char uniname2ctype_pool_str4391[sizeof("batak")];
- char uniname2ctype_pool_str4455[sizeof("graphemelink")];
- char uniname2ctype_pool_str4468[sizeof("blank")];
- char uniname2ctype_pool_str4506[sizeof("gurmukhi")];
- char uniname2ctype_pool_str4563[sizeof("incjkunifiedideographsextensionb")];
-#endif /* USE_UNICODE_PROPERTIES */
- };
-static const struct uniname2ctype_pool_t uniname2ctype_pool_contents =
- {
-#ifndef USE_UNICODE_PROPERTIES
- "word",
- "print",
- "punct",
- "alpha",
- "alnum",
- "xdigit",
- "upper",
-#else /* USE_UNICODE_PROPERTIES */
- "n",
- "mn",
- "m",
- "cn",
- "mani",
- "ci",
- "inmanichaean",
- "qaai",
- "z",
- "c",
- "mc",
- "qaac",
- "sm",
- "incham",
- "me",
- "inarmenian",
- "cc",
- "mandaic",
- "incuneiform",
- "zzzz",
- "insamaritan",
- "cans",
- "s",
- "sc",
-#endif /* USE_UNICODE_PROPERTIES */
- "ascii",
-#ifdef USE_UNICODE_PROPERTIES
- "inavestan",
- "zs",
- "cs",
- "inipaextensions",
- "incuneiformnumbersandpunctuation",
- "incommonindicnumberforms",
- "inthai",
- "mtei",
- "cf",
- "inspecials",
- "initialpunctuation",
- "inthaana",
- "inancientsymbols",
- "inmiscellaneousmathematicalsymbolsa",
- "inmusicalsymbols",
- "taile",
- "inmyanmarextendeda",
- "sterm",
- "lm",
- "lina",
- "inmiscellaneoussymbols",
- "lana",
- "intransportandmapsymbols",
- "alnum",
- "inlycian",
- "inmiscellaneoussymbolsandarrows",
- "intaitham",
- "lc",
- "inmalayalam",
- "inmiscellaneoussymbolsandpictographs",
- "inadlam",
- "incontrolpictures",
- "inlineara",
- "taiviet",
- "armn",
- "armi",
- "sinhala",
- "armenian",
- "inmyanmar",
- "inrunic",
- "incarian",
- "cari",
- "inmarchen",
- "marc",
- "carian",
- "merc",
- "incyrillic",
- "intaixuanjingsymbols",
- "samr",
- "latn",
- "latin",
- "ital",
- "intamil",
- "taml",
- "inmultani",
- "samaritan",
- "arabic",
- "insyriac",
- "insharada",
- "miao",
- "inlinearbideograms",
- "incherokee",
- "intaile",
- "tale",
- "inahom",
- "inmeeteimayekextensions",
- "inruminumeralsymbols",
- "inlatinextendeda",
- "inosmanya",
- "innewa",
- "ext",
- "newa",
- "inlatinextendedc",
- "cwcm",
- "osma",
- "inlatinextendede",
- "l",
- "han",
- "nl",
- "term",
- "hani",
- "joinc",
- "inideographicsymbolsandpunctuation",
- "zinh",
- "newtailue",
- "lt",
- "zl",
- "inmahajani",
- "cham",
- "connectorpunctuation",
- "sinh",
- "manichaean",
- "osage",
- "mahj",
- "cwcf",
- "inmiscellaneoustechnical",
- "inethiopic",
- "insinhala",
- "chakma",
- "mahajani",
- "shavian",
- "inenclosedcjklettersandmonths",
- "innewtailue",
- "inideographicdescriptioncharacters",
- "lineara",
- "meroiticcursive",
- "thai",
- "math",
- "inemoticons",
- "thaa",
- "ethi",
- "hatran",
- "inenclosedalphanumerics",
- "sentenceterminal",
- "tamil",
-#endif /* USE_UNICODE_PROPERTIES */
- "cntrl",
-#ifdef USE_UNICODE_PROPERTIES
- "taitham",
- "thaana",
- "terminalpunctuation",
- "ahex",
- "insinhalaarchaicnumbers",
- "inlatinextendedadditional",
- "cwt",
- "loe",
- "intifinagh",
- "tifinagh",
- "inopticalcharacterrecognition",
- "inearlydynasticcuneiform",
- "di",
- "asciihexdigit",
- "ll",
- "inscriptionalparthian",
- "inenclosedalphanumericsupplement",
- "oriya",
- "mero",
- "wara",
- "mand",
- "inmodi",
- "inwarangciti",
- "dia",
- "mend",
- "no",
- "sora",
- "inmandaic",
- "idc",
- "innko",
- "sind",
- "inarrows",
- "inmro",
- "titlecaseletter",
- "co",
- "hira",
- "inlowsurrogates",
- "hex",
- "inmiao",
- "common",
- "so",
- "inhiragana",
- "insundanese",
- "ids",
- "cher",
- "inmahjongtiles",
- "marchen",
- "indominotiles",
- "hano",
- "ahom",
- "inogham",
- "inscriptionalpahlavi",
- "inolchiki",
- "xidc",
- "tirh",
- "idst",
- "cwl",
- "inhatran",
- "pi",
- "hatr",
- "idcontinue",
- "xids",
- "intirhuta",
- "shaw",
- "inshorthandformatcontrols",
- "inspacingmodifierletters",
- "indeseret",
- "pcm",
- "pc",
- "inlydian",
- "pe",
- "inmathematicalalphanumericsymbols",
- "adlm",
- "inphaistosdisc",
- "adlam",
- "whitespace",
- "lo",
- "insylotinagri",
- "inlao",
- "ps",
- "anatolianhieroglyphs",
- "lao",
- "laoo",
- "mongolian",
- "invai",
- "lineseparator",
- "vai",
- "vaii",
-#endif /* USE_UNICODE_PROPERTIES */
- "space",
-#ifdef USE_UNICODE_PROPERTIES
- "format",
- "letter",
- "pf",
- "mro",
- "mroo",
- "diacritic",
- "joincontrol",
- "java",
- "inanatolianhieroglyphs",
- "odi",
- "nchar",
- "incoptic",
- "modi",
- "inshavian",
- "oidc",
- "vs",
- "injavanese",
- "ideo",
- "xdigit",
- "oalpha",
- "inolditalic",
- "xidstart",
- "oids",
- "decimalnumber",
- "javanese",
- "patws",
- "palm",
- "psalterpahlavi",
- "incopticepactnumbers",
- "alpha",
- "dsrt",
- "inlepcha",
- "inpalmyrene",
- "lepc",
- "avst",
- "avestan",
- "insundanesesupplement",
- "inmodifiertoneletters",
- "idstart",
- "dash",
- "incyrillicextendeda",
- "hanunoo",
- "perm",
- "incyrillicextendedc",
- "siddham",
- "inoldturkic",
- "finalpunctuation",
- "deseret",
- "imperialaramaic",
- "sharada",
- "xidcontinue",
- "tavt",
- "intaiviet",
- "inmongolian",
- "inimperialaramaic",
- "ingrantha",
- "radical",
- "omath",
- "canadianaboriginal",
-#ifdef USE_UNICODE_AGE_PROPERTIES
- "age=1.1",
- "age=2.1",
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- "extender",
- "otheridcontinue",
- "inphoenician",
- "control",
-#ifdef USE_UNICODE_AGE_PROPERTIES
- "age=4.1",
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- "prti",
-#ifdef USE_UNICODE_AGE_PROPERTIES
- "age=6.1",
- "age=6.2",
- "age=3.1",
- "age=3.2",
- "age=2.0",
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- "ingujarati",
-#ifdef USE_UNICODE_AGE_PROPERTIES
- "age=5.1",
- "age=5.2",
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- "noncharactercodepoint",
- "ingeneralpunctuation",
-#ifdef USE_UNICODE_AGE_PROPERTIES
- "age=4.0",
- "age=6.3",
- "age=6.0",
- "age=9.0",
- "age=3.0",
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- "cprt",
- "casedletter",
- "letternumber",
-#ifdef USE_UNICODE_AGE_PROPERTIES
- "age=5.0",
- "age=8.0",
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- "otheralphabetic",
-#ifdef USE_UNICODE_AGE_PROPERTIES
- "age=7.0",
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- "coptic",
- "lowercase",
- "inethiopicextendeda",
- "privateuse",
- "ininscriptionalparthian",
- "ininscriptionalpahlavi",
- "nd",
- "tang",
- "insmallformvariants",
- "inoldnortharabian",
- "copt",
- "wspace",
- "incaucasianalbanian",
- "tagbanwa",
- "xpeo",
- "sd",
- "inlatin1supplement",
- "sundanese",
- "print",
- "sidd",
- "tfng",
- "inancientgreekmusicalnotation",
- "cuneiform",
- "inherited",
- "inenclosedideographicsupplement",
- "inoldsoutharabian",
- "insiddham",
- "invariationselectors",
- "patternwhitespace",
- "phnx",
- "olditalic",
- "idsbinaryoperator",
- "spaceseparator",
- "cased",
- "intags",
- "shrd",
- "inancientgreeknumbers",
- "saurashtra",
- "intangut",
- "otheridstart",
- "inalphabeticpresentationforms",
- "inunifiedcanadianaboriginalsyllabics",
- "patternsyntax",
- "inoldhungarian",
- "inphoneticextensions",
- "other",
- "othersymbol",
- "otherlowercase",
- "invariationselectorssupplement",
- "othernumber",
- "gran",
- "ingurmukhi",
- "phli",
- "rjng",
- "inmathematicaloperators",
- "closepunctuation",
-#endif /* USE_UNICODE_PROPERTIES */
- "lower",
-#ifndef USE_UNICODE_PROPERTIES
- "graph",
-#else /* USE_UNICODE_PROPERTIES */
- "separator",
- "multani",
- "modifierletter",
- "inrejang",
- "olower",
- "inpsalterpahlavi",
- "lisu",
- "po",
- "mong",
- "inphoneticextensionssupplement",
- "caseignorable",
- "insorasompeng",
- "lepcha",
- "ogam",
- "oldpersian",
- "oldpermic",
- "xsux",
- "inosage",
- "runic",
- "inugaritic",
- "sgnw",
- "saur",
- "mult",
- "hmng",
- "inmongoliansupplement",
- "osge",
- "talu",
- "hang",
- "insuttonsignwriting",
- "othermath",
- "phoenician",
- "telu",
- "tangut",
- "insaurashtra",
- "indevanagari",
- "deva",
- "word",
- "devanagari",
- "p",
- "lowercaseletter",
- "ingreekandcoptic",
- "yi",
- "invedicextensions",
- "yiii",
- "inlatinextendedd",
- "zp",
- "otherletter",
- "ingeometricshapes",
- "gothic",
- "incountingrodnumerals",
- "induployan",
- "grext",
- "incyrillicsupplement",
- "innabataean",
- "ingothic",
- "inbhaiksuki",
- "bamum",
- "inverticalforms",
- "incherokeesupplement",
- "inelbasan",
- "inarabic",
- "inbasiclatin",
- "ethiopic",
- "tirhuta",
- "innumberforms",
- "runr",
- "inyijinghexagramsymbols",
- "ingeorgian",
- "bass",
- "geor",
- "insuperscriptsandsubscripts",
- "inornamentaldingbats",
- "warangciti",
- "nbat",
- "inoldpersian",
- "number",
- "inprivateusearea",
- "inarabicpresentationformsa",
- "inbyzantinemusicalsymbols",
- "nabataean",
- "inoldpermic",
- "intibetan",
- "inmiscellaneousmathematicalsymbolsb",
- "inethiopicsupplement",
- "indingbats",
- "hexdigit",
- "inmeroitichieroglyphs",
- "mlym",
- "ingreekextended",
- "lyci",
- "inmyanmarextendedb",
- "hiragana",
- "inhangulsyllables",
- "inethiopicextended",
- "lycian",
- "inbraillepatterns",
- "linb",
- "malayalam",
- "limb",
- "assigned",
- "insupplementalmathematicaloperators",
- "sund",
- "bali",
- "tibt",
- "mymr",
- "ogham",
- "tibetan",
- "elba",
- "unassigned",
- "inbalinese",
- "plrd",
- "inkannada",
- "kana",
- "bengali",
- "myanmar",
- "graphemebase",
- "cakm",
- "narb",
- "brai",
- "glagolitic",
- "syrc",
- "balinese",
- "inhanguljamoextendeda",
- "arab",
- "inchakma",
- "brahmi",
- "grantha",
- "inhanunoo",
- "elbasan",
- "otherpunctuation",
- "inoriya",
- "inphagspa",
- "mendekikakui",
- "inunifiedcanadianaboriginalsyllabicsextended",
- "sarb",
- "goth",
- "syriac",
-#endif /* USE_UNICODE_PROPERTIES */
- "digit",
-#ifndef USE_UNICODE_PROPERTIES
- "blank"
-#else /* USE_UNICODE_PROPERTIES */
- "sylotinagri",
- "inhalfwidthandfullwidthforms",
- "meeteimayek",
- "inyiradicals",
- "mathsymbol",
- "osmanya",
- "inkaithi",
- "incjkcompatibilityforms",
- "takri",
- "incjkcompatibilityideographs",
- "pauc",
- "logicalorderexception",
- "inkatakana",
- "pd",
- "hangul",
- "softdotted",
- "incjkstrokes",
- "insupplementalarrowsa",
- "inbrahmi",
- "inmeeteimayek",
- "insupplementalarrowsc",
- "phagspa",
- "ideographic",
- "inlinearbsyllabary",
- "ogrext",
- "cyrl",
- "incjkcompatibilityideographssupplement",
- "inblockelements",
- "kali",
- "intangutcomponents",
- "deprecated",
- "linearb",
- "variationselector",
- "idstrinaryoperator",
- "inbassavah",
- "hluw",
- "inhighprivateusesurrogates",
- "incombininghalfmarks",
- "mark",
- "sorasompeng",
- "inkanasupplement",
- "inkharoshthi",
- "sylo",
- "inletterlikesymbols",
- "inkhmer",
- "ingeometricshapesextended",
- "phlp",
- "orya",
- "inhanguljamo",
- "graph",
- "punct",
- "indevanagariextended",
- "dep",
- "inkhojki",
- "intakri",
- "takr",
- "changeswhencasemapped",
- "inarabicmathematicalalphabeticsymbols",
- "kaithi",
- "inkhmersymbols",
- "idsb",
- "bidic",
- "inglagolitic",
- "brah",
- "phag",
- "cyrillic",
- "changeswhencasefolded",
- "hebr",
- "otherdefaultignorablecodepoint",
- "inpahawhhmong",
- "meroitichieroglyphs",
- "inarabicextendeda",
- "inalchemicalsymbols",
- "otheruppercase",
- "oldhungarian",
- "braille",
- "intagalog",
- "changeswhentitlecased",
- "kthi",
- "symbol",
- "lydi",
- "incurrencysymbols",
- "olck",
- "lydian",
- "olchiki",
- "glag",
- "ugaritic",
- "uideo",
- "graphemeextend",
- "knda",
- "patsyn",
- "combiningmark",
- "inmendekikakui",
- "nko",
- "nkoo",
- "kannada",
- "khmr",
- "khar",
- "tglg",
- "defaultignorablecodepoint",
- "rejang",
- "enclosingmark",
- "lu",
- "ugar",
- "insupplementaryprivateuseareaa",
- "inkatakanaphoneticextensions",
- "limbu",
- "georgian",
- "gujr",
- "inarabicsupplement",
- "gujarati",
- "khoj",
- "incombiningdiacriticalmarks",
- "inlisu",
- "incombiningdiacriticalmarksforsymbols",
- "oldturkic",
- "inhebrew",
- "inbuhid",
- "kharoshthi",
- "cherokee",
- "alphabetic",
- "incyrillicextendedb",
- "hung",
- "changeswhenlowercased",
- "intelugu",
- "incombiningdiacriticalmarkssupplement",
- "inplayingcards",
- "spacingmark",
- "inmeroiticcursive",
- "cwu",
- "inbengali",
- "beng",
- "hebrew",
- "inbuginese",
- "dashpunctuation",
- "khmer",
- "hyphen",
- "dupl",
- "incjksymbolsandpunctuation",
- "bidicontrol",
- "bamu",
- "inbamum",
- "cypriot",
- "inaegeannumbers",
- "orkh",
- "tagb",
- "oupper",
- "intagbanwa",
- "modifiersymbol",
- "palmyrene",
- "signwriting",
- "tagalog",
- "incypriotsyllabary",
- "caucasianalbanian",
- "insupplementalpunctuation",
- "surrogate",
- "othergraphemeextend",
- "bassavah",
- "nonspacingmark",
- "prependedconcatenationmark",
- "inlimbu",
- "ingeorgiansupplement",
- "pahawhhmong",
- "bopo",
- "uppercase",
- "inbopomofo",
- "inkangxiradicals",
- "grbase",
- "inhighsurrogates",
- "aghb",
- "unifiedideograph",
- "inbamumsupplement",
- "any",
- "paucinhau",
- "inglagoliticsupplement",
- "inpaucinhau",
- "incombiningdiacriticalmarksextended",
- "openpunctuation",
- "grek",
- "punctuation",
- "incjkradicalssupplement",
- "upper",
- "inyisyllables",
- "currencysymbol",
- "inarabicpresentationformsb",
- "oldnortharabian",
- "insupplementalsymbolsandpictographs",
- "bopomofo",
- "uppercaseletter",
- "inkayahli",
- "inkanbun",
- "egyp",
- "incjkcompatibility",
- "inhanguljamoextendedb",
- "inegyptianhieroglyphs",
- "inlatinextendedb",
- "batk",
- "inbatak",
- "paragraphseparator",
- "insupplementalarrowsb",
- "changeswhenuppercased",
- "inkhudawadi",
- "innoblock",
- "unknown",
- "qmark",
- "guru",
- "sk",
- "quotationmark",
- "khudawadi",
- "buhd",
- "telugu",
- "katakana",
- "bugi",
- "bhks",
- "bhaiksuki",
- "buginese",
- "kayahli",
- "incjkunifiedideographsextensiona",
- "incjkunifiedideographsextensionc",
- "incjkunifiedideographsextensione",
- "incjkunifiedideographs",
- "inbopomofoextended",
- "insupplementaryprivateuseareab",
- "inhangulcompatibilityjamo",
- "oldsoutharabian",
- "buhid",
- "khojki",
- "duployan",
- "inboxdrawing",
- "incjkunifiedideographsextensiond",
- "egyptianhieroglyphs",
- "zyyy",
- "greek",
- "grlink",
- "batak",
- "graphemelink",
- "blank",
- "gurmukhi",
- "incjkunifiedideographsextensionb"
-#endif /* USE_UNICODE_PROPERTIES */
- };
-#define uniname2ctype_pool ((const char *) &uniname2ctype_pool_contents)
-#ifdef __GNUC__
-__inline
-#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
-__attribute__ ((__gnu_inline__))
-#endif
-#endif
-const struct uniname2ctype_struct *
-uniname2ctype_p (str, len)
- register const char *str;
- register unsigned int len;
-{
- static const struct uniname2ctype_struct wordlist[] =
- {
-#ifdef USE_UNICODE_PROPERTIES
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3, 34},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str11, 33},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str15, 30},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str24, 20},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str29, 185},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str30, 60},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str31, 457},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str35, 114},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str37, 51},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str41, 17},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str49, 31},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str51, 128},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str56, 49},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str58, 399},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str59, 32},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str61, 273},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str62, 18},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str65, 168},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str69, 487},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str76, 244},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str77, 280},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str78, 101},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str81, 46},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str82, 47},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str86, 14},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str93, 458},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str100, 54},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str102, 22},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str106, 267},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str110, 488},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str114, 390},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str124, 293},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str132, 159},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str138, 19},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str140, 423},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str141, 43},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str144, 278},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str145, 428},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str152, 349},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str159, 504},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str164, 120},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str174, 400},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str176, 239},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str185, 26},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str191, 183},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str199, 347},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str200, 151},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str201, 522},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str202, 13},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str207, 430},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str208, 355},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str209, 317},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str211, 24},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str212, 291},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str213, 519},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str214, 512},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str220, 341},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str222, 444},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str237, 152},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str239, 78},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str242, 160},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str247, 92},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str248, 78},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str249, 296},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str251, 304},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str252, 431},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str255, 148},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str256, 486},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str258, 207},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str268, 148},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str270, 170},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str273, 271},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str274, 506},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str278, 155},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str279, 75},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str281, 75},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str282, 111},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str284, 288},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str285, 88},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str286, 473},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str288, 155},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str290, 80},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str291, 276},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str294, 470},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str295, 172},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str296, 425},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str299, 301},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str302, 313},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str303, 120},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str305, 482},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str309, 402},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str310, 464},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str313, 265},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str314, 440},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str315, 476},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str317, 224},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str320, 208},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str327, 357},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str328, 65},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str331, 124},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str337, 404},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str339, 23},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str340, 109},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str341, 36},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str342, 217},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str344, 109},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str346, 213},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str347, 497},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str349, 114},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str354, 129},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str355, 28},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str358, 52},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str361, 469},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str366, 150},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str368, 39},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str371, 92},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str374, 185},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str375, 209},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str376, 184},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str379, 64},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str382, 340},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str386, 299},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str390, 292},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str394, 169},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str397, 184},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str400, 123},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str410, 376},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str411, 314},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str419, 366},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str421, 183},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str422, 170},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str435, 93},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str438, 55},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str440, 520},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str444, 82},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str447, 99},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str448, 201},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str452, 343},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str453, 239},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str455, 88},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str456, 3},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str458, 151},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str460, 82},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str468, 217},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str469, 220},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str471, 471},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str478, 330},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str484, 63},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str489, 236},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str491, 360},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str498, 131},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str503, 342},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str504, 489},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str507, 70},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str508, 220},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str509, 25},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str512, 162},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str513, 517},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str516, 87},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str517, 171},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str521, 198},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str522, 168},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str523, 479},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str529, 483},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str530, 223},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str534, 186},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str537, 37},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str538, 174},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str539, 281},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str544, 67},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str545, 279},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str546, 196},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str547, 338},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str551, 493},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str554, 28},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str556, 21},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str557, 106},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str559, 411},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str560, 219},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str565, 496},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str569, 74},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str576, 50},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str577, 368},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str579, 320},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str584, 66},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str585, 100},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str587, 514},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str589, 207},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str591, 515},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str607, 116},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str613, 199},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str621, 303},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str622, 163},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str627, 323},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str630, 69},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str636, 197},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str637, 230},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str638, 61},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str642, 449},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str643, 43},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str645, 201},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str649, 67},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str650, 68},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str655, 477},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str656, 123},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str662, 502},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str664, 268},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str668, 438},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str670, 243},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str675, 39},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str684, 451},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str685, 41},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str688, 508},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str690, 205},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str693, 429},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str698, 205},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str703, 211},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str705, 27},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str712, 389},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str713, 294},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str715, 45},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str717, 200},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str719, 94},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str720, 94},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str721, 105},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str731, 384},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str733, 52},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str737, 143},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str738, 143},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str741, 9},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str742, 19},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str745, 23},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str751, 42},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str758, 188},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str759, 188},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str761, 223},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str768, 213},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str773, 158},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str774, 491},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str775, 233},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str776, 227},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str778, 358},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str779, 187},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str781, 439},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str792, 238},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str795, 240},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str796, 397},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str797, 222},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str800, 11},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str803, 221},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str804, 433},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str811, 68},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str812, 237},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str814, 35},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str815, 158},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str823, 241},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str826, 191},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str834, 194},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str837, 432},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str846, 1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str847, 113},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str848, 322},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str850, 447},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str851, 141},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str862, 153},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str864, 153},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str865, 325},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str866, 387},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str867, 66},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str876, 214},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str877, 362},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str878, 116},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str883, 193},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str884, 324},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str890, 195},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str892, 462},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str896, 42},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str900, 113},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str904, 160},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str905, 173},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str910, 69},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str914, 152},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str915, 401},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str916, 310},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str922, 446},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str932, 475},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str933, 231},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str936, 218},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str937, 101},
-#ifndef USE_UNICODE_AGE_PROPERTIES
- {-1}, {-1}, {-1}, {-1},
-#else /* USE_UNICODE_AGE_PROPERTIES */
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str939, 245},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str940, 247},
- {-1},
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str942, 224},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str943, 238},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str944, 450},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str945, 18},
-#ifndef USE_UNICODE_AGE_PROPERTIES
- {-1},
-#else /* USE_UNICODE_AGE_PROPERTIES */
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str946, 252},
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str947, 162},
-#ifndef USE_UNICODE_AGE_PROPERTIES
- {-1}, {-1}, {-1}, {-1}, {-1},
-#else /* USE_UNICODE_AGE_PROPERTIES */
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str948, 257},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str949, 258},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str950, 249},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str951, 250},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str952, 246},
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str953, 286},
-#ifndef USE_UNICODE_AGE_PROPERTIES
- {-1}, {-1},
-#else /* USE_UNICODE_AGE_PROPERTIES */
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str954, 254},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str955, 255},
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str956, 227},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str957, 332},
-#ifndef USE_UNICODE_AGE_PROPERTIES
- {-1}, {-1}, {-1}, {-1}, {-1},
-#else /* USE_UNICODE_AGE_PROPERTIES */
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str958, 251},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str959, 259},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str960, 256},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str961, 262},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str962, 248},
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str963, 125},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str964, 24},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str965, 36},
-#ifndef USE_UNICODE_AGE_PROPERTIES
- {-1}, {-1},
-#else /* USE_UNICODE_AGE_PROPERTIES */
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str966, 253},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str967, 261},
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str968, 221},
-#ifndef USE_UNICODE_AGE_PROPERTIES
- {-1},
-#else /* USE_UNICODE_AGE_PROPERTIES */
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str969, 260},
-#endif /* USE_UNICODE_AGE_PROPERTIES */
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str970, 128},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str981, 57},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str990, 403},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str991, 21},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str993, 459},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str995, 460},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str997, 35},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1001, 210},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1012, 420},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1015, 456},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1016, 128},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1026, 211},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1029, 443},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1031, 118},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1034, 133},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1036, 235},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1038, 264},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1039, 140},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1040, 7},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1042, 195},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1046, 131},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1047, 505},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1049, 136},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1051, 114},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1053, 518},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1054, 455},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1055, 478},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1056, 416},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1064, 241},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1065, 137},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1067, 111},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1069, 229},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1071, 54},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1072, 59},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1074, 532},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1077, 173},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1078, 427},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1079, 144},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1089, 498},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1095, 237},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1102, 414},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1107, 302},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1108, 242},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1110, 463},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1111, 327},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1114, 17},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1115, 50},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1116, 225},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1118, 533},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1121, 37},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1123, 180},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1131, 285},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1132, 163},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1140, 146},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1142, 339},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1145, 41},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1146, 6},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1148, 51},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1151, 202},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1153, 26},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1161, 395},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1162, 225},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1166, 461},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1167, 156},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1169, 44},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1170, 105},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1173, 328},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1175, 60},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1177, 467},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1181, 141},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1182, 102},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1183, 133},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1201, 193},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1204, 136},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1208, 441},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1210, 103},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1212, 436},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1213, 204},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1221, 144},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1222, 202},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1225, 181},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1226, 480},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1227, 209},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1228, 129},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1231, 98},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1232, 509},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1233, 218},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1238, 137},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1240, 89},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1246, 210},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1247, 392},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1248, 283},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1252, 83},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1259, 12},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1263, 83},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1267, 38},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1268, 25},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1269, 270},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1274, 110},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1278, 326},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1280, 110},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1281, 388},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1286, 53},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1289, 27},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1292, 346},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1300, 112},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1311, 507},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1315, 501},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1319, 71},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1330, 272},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1334, 448},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1338, 434},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1340, 485},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1342, 157},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1346, 417},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1351, 405},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1356, 442},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1362, 275},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1366, 263},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1367, 99},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1372, 197},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1376, 337},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1383, 103},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1386, 379},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1388, 297},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1400, 177},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1401, 97},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1404, 333},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1406, 521},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1410, 198},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1413, 190},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1416, 437},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1417, 34},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1422, 412},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1426, 415},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1431, 503},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1432, 190},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1433, 435},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1435, 295},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1442, 353},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1443, 300},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1444, 348},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1447, 219},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1449, 452},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1451, 91},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1459, 331},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1461, 147},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1464, 398},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1465, 106},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1470, 407},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1473, 361},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1474, 147},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1475, 351},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1481, 121},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1482, 91},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1487, 119},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1490, 16},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1491, 354},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1492, 140},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1493, 135},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1495, 95},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1496, 96},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1499, 102},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1512, 95},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1514, 179},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1515, 20},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1516, 319},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1517, 172},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1518, 290},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1520, 107},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1522, 84},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1525, 96},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1527, 72},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1533, 169},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1535, 189},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1538, 126},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1539, 130},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1542, 81},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1544, 135},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1545, 396},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1547, 80},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1548, 468},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1551, 167},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1552, 180},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1554, 306},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1558, 179},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1559, 44},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1560, 287},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1564, 391},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1567, 186},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1572, 311},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1574, 161},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1576, 112},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1577, 81},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1581, 4},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1584, 132},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1585, 422},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1586, 159},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1588, 382},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1595, 49},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1599, 124},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1602, 466},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1605, 419},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1607, 175},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1610, 413},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1620, 192},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1621, 236},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1622, 369},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1629, 40},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1630, 98},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1636, 235},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1646, 374},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1651, 350},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1655, 465},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1656, 406},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1658, 525},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1661, 138},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1662, 222},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1664, 424},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1667, 228},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1671, 77},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1672, 531},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1675, 345},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1679, 145},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1681, 499},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1707, 234},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1711, 121},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1712, 240},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1716, 230},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1720, 494},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1722, 200},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1723, 410},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1724, 418},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1727, 30},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1730, 174},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1733, 500},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1741, 454},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1744, 132},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1745, 336},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1747, 309},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1757, 524},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1761, 194},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1762, 87},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1765, 298},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1767, 5},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1773, 8},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1774, 393},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1791, 234},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1803, 472},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1811, 481},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1812, 175},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1816, 65},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1825, 513},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1842, 165},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1847, 315},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1848, 229},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1849, 212},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1852, 356},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1856, 167},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1863, 138},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1864, 77},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1867, 64},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1868, 79},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1873, 233},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1878, 495},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1879, 171},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1886, 282},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1891, 523},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1897, 226},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1898, 203},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1900, 126},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1901, 305},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1906, 63},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1911, 165},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1922, 46},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1938, 149},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1943, 334},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1949, 142},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1951, 149},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1960, 142},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1968, 130},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1978, 122},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str1990, 232},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2001, 71},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2004, 90},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2010, 242},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2015, 30},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2017, 511},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2027, 139},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2028, 139},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2033, 90},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2036, 104},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2042, 134},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2047, 115},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2055, 70},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2058, 146},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2069, 32},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2071, 29},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2072, 122},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2079, 534},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2081, 375},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2085, 119},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2087, 97},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2089, 86},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2102, 277},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2110, 86},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2112, 182},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2113, 269},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2120, 383},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2123, 335},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2126, 164},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2129, 274},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2138, 307},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2145, 134},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2156, 100},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2161, 56},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2167, 385},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2168, 203},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2172, 61},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2174, 289},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2175, 329},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2183, 516},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2191, 31},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2198, 453},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2200, 62},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2213, 284},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2224, 84},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2235, 79},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2238, 316},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2241, 40},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2251, 104},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2252, 215},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2253, 178},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2254, 367},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2271, 212},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2277, 157},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2279, 386},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2287, 125},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2292, 426},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2296, 164},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2303, 118},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2309, 226},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2314, 308},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2319, 48},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2327, 191},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2328, 204},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2330, 115},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2336, 445},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2370, 176},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2379, 363},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2396, 22},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2401, 228},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2412, 177},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2431, 33},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2439, 243},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2441, 312},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2464, 359},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2469, 181},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2474, 108},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2484, 58},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2485, 370},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2486, 365},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2504, 72},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2527, 409},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2533, 176},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2535, 232},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2544, 492},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2545, 15},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2560, 192},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2567, 510},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2569, 484},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2578, 318},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2616, 45},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2623, 76},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2638, 38},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2645, 364},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2649, 10},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2651, 381},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2696, 47},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2716, 421},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2733, 189},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2742, 526},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2752, 108},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2771, 29},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2784, 394},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2807, 372},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2817, 154},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2824, 377},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2835, 408},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2883, 490},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2893, 266},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2901, 166},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2909, 321},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2922, 53},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2941, 352},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2953, 62},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2956, 474},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2976, 536},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str2992, 244},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3004, 216},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3009, 85},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3020, 48},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3039, 216},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3064, 196},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3076, 117},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3083, 89},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3117, 107},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3152, 127},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3158, 206},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3180, 206},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3203, 127},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3268, 145},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3273, 378},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3280, 528},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3285, 530},
- {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3290, 380},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3325, 373},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3369, 535},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3447, 371},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3469, 161},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3574, 117},
-#endif /* USE_UNICODE_PROPERTIES */
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
-#ifndef USE_UNICODE_PROPERTIES
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str6, 12},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str7, 7},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str8, 8},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str9, 1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str10, 13},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str11, 11},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str12, 10},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str13, 14},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str14, 3},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str15, 9},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str16, 6},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str17, 5},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str18, 4},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str19, 2}
-#else /* USE_UNICODE_PROPERTIES */
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3581, 182},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3620, 178},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3704, 344},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3757, 529},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3813, 154},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str3814, 74},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str4113, 76},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str4259, 73},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str4391, 166},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str4455, 73},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str4468, 2},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str4506, 85},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
- {-1}, {-1},
- {(int)(long)&((struct uniname2ctype_pool_t *)0)->uniname2ctype_pool_str4563, 527}
-#endif /* USE_UNICODE_PROPERTIES */
- };
-
- if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
- {
- register int key = uniname2ctype_hash (str, len);
-
- if (key <= MAX_HASH_VALUE && key >= 0)
- {
- register int o = wordlist[key].name;
- if (o >= 0)
- {
- register const char *s = o + uniname2ctype_pool;
-
- if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0')
- return &wordlist[key];
- }
- }
- }
- return 0;
-}
-
-static int
-uniname2ctype(const UChar *name, unsigned int len)
-{
- const struct uniname2ctype_struct *p = uniname2ctype_p((const char *)name, len);
- if (p) return p->ctype;
- return -1;
-}
-#if defined ONIG_UNICODE_VERSION_STRING && !( \
- ONIG_UNICODE_VERSION_MAJOR == 9 && \
- ONIG_UNICODE_VERSION_MINOR == 0 && \
- ONIG_UNICODE_VERSION_TEENY == 0 && \
- 1)
-# error ONIG_UNICODE_VERSION_STRING mismatch
-#endif
-#define ONIG_UNICODE_VERSION_STRING "9.0.0"
-#define ONIG_UNICODE_VERSION_MAJOR 9
-#define ONIG_UNICODE_VERSION_MINOR 0
-#define ONIG_UNICODE_VERSION_TEENY 0
diff --git a/enc/unicode/case-folding.rb b/enc/unicode/case-folding.rb
index ef97baf737..362d6ebfd9 100755
--- a/enc/unicode/case-folding.rb
+++ b/enc/unicode/case-folding.rb
@@ -16,15 +16,20 @@ class CaseFolding
module_function
def hex_seq(v)
- v.map {|i| "0x%04x" % i}.join(", ")
+ v.map { |i| "0x%04x" % i }.join(", ")
end
def print_table_1(dest, type, mapping_data, data)
for k, v in data = data.sort
sk = (Array === k and k.length > 1) ? "{#{hex_seq(k)}}" : ("0x%04x" % k)
- ck = cv = ''
- ck = ' /* ' + Array(k).pack("U*") + ' */' if @debug
- cv = ' /* ' + Array(v).map{|c|[c].pack("U*")}.join(", ") + ' */' if @debug
+ if type=='CaseUnfold_11' and v.length>1
+ # reorder CaseUnfold_11 entries to avoid special treatment for U+03B9/U+03BC/U+A64B
+ item = mapping_data.map("%04X" % k[0])
+ upper = item.upper if item
+ v = v.sort_by { |i| ("%04X"%i) == upper ? 0 : 1 }
+ end
+ ck = @debug ? ' /* ' + Array(k).pack("U*") + ' */' : ''
+ cv = @debug ? ' /* ' + Array(v).map{|c|[c].pack("U*")}.join(", ") + ' */' : ''
dest.print(" {#{sk}#{ck}, {#{v.length}#{mapping_data.flags(k, type, v)}, {#{hex_seq(v)}#{cv}}}},\n")
end
data
@@ -249,13 +254,16 @@ class CaseMapping
end
end
+ def map (from)
+ @mappings[from]
+ end
+
def flags(from, type, to)
# types: CaseFold_11, CaseUnfold_11, CaseUnfold_12, CaseUnfold_13
flags = ""
from = Array(from).map {|i| "%04X" % i}.join(" ")
to = Array(to).map {|i| "%04X" % i}.join(" ")
- item = @mappings[from]
- specials_index = nil
+ item = map(from)
specials = []
case type
when 'CaseFold_11'
@@ -296,17 +304,15 @@ class CaseMapping
when item.upper then flags += '|U'
when item.lower then flags += '|D'
else
- unless from=='03B9' or from=='03BC'
- warn 'Unpredicted case 0; check data or adjust program (enc/unicode/case_folding.rb).'
- end
+ raise "Unpredicted case 0 in enc/unicode/case_folding.rb. Please contact https://bugs.ruby-lang.org/."
end
unless item.upper == item.title
if item.code == item.title
- warn 'Unpredicted case 1; check data or adjust program (enc/unicode/case_folding.rb).'
+ flags += '|IT' # was unpredicted case 1
elsif item.title==to[1]
flags += '|ST'
else
- warn 'Unpredicted case 2; check data or adjust program (enc/unicode/case_folding.rb).'
+ raise "Unpredicted case 2 in enc/unicode/case_folding.rb. Please contact https://bugs.ruby-lang.org/."
end
end
end
@@ -324,7 +330,7 @@ class CaseMapping
end
def specials_output
- "OnigCodePoint CaseMappingSpecials[] = {\n" +
+ "static const OnigCodePoint CaseMappingSpecials[] = {\n" +
@specials.map do |sps|
' ' + sps.map do |sp|
chars = sp.split(/ /)
@@ -403,8 +409,8 @@ if $0 == __FILE__
s = f.string
end
if dest
- open(dest, "wb") do |f|
- f.print(s)
+ open(dest, "wb") do |file|
+ file.print(s)
end
else
STDOUT.print(s)
diff --git a/enc/us_ascii.c b/enc/us_ascii.c
index cf835e6538..08f9072c43 100644
--- a/enc/us_ascii.c
+++ b/enc/us_ascii.c
@@ -1,7 +1,10 @@
#include "regenc.h"
-#include "encindex.h"
+#ifdef RUBY
+# include "encindex.h"
+#endif
+
#ifndef ENCINDEX_US_ASCII
-#define ENCINDEX_US_ASCII 0
+# define ENCINDEX_US_ASCII 0
#endif
static int
@@ -29,9 +32,9 @@ OnigEncodingDefine(us_ascii, US_ASCII) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ onigenc_single_byte_ascii_only_case_map,
ENCINDEX_US_ASCII,
ONIGENC_FLAG_NONE,
- onigenc_single_byte_ascii_only_case_map,
};
ENC_ALIAS("ASCII", "US-ASCII")
ENC_ALIAS("ANSI_X3.4-1968", "US-ASCII")
diff --git a/enc/utf_16be.c b/enc/utf_16be.c
index e8b97983bf..f9dd7119d6 100644
--- a/enc/utf_16be.c
+++ b/enc/utf_16be.c
@@ -249,8 +249,8 @@ OnigEncodingDefine(utf_16be, UTF_16BE) = {
onigenc_utf16_32_get_ctype_code_range,
utf16be_left_adjust_char_head,
onigenc_always_false_is_allowed_reverse_match,
+ onigenc_unicode_case_map,
0,
ONIGENC_FLAG_UNICODE,
- onigenc_unicode_case_map,
};
ENC_ALIAS("UCS-2BE", "UTF-16BE")
diff --git a/enc/utf_16le.c b/enc/utf_16le.c
index 67ec2ad178..2c8438d0be 100644
--- a/enc/utf_16le.c
+++ b/enc/utf_16le.c
@@ -242,7 +242,7 @@ OnigEncodingDefine(utf_16le, UTF_16LE) = {
onigenc_utf16_32_get_ctype_code_range,
utf16le_left_adjust_char_head,
onigenc_always_false_is_allowed_reverse_match,
+ onigenc_unicode_case_map,
0,
ONIGENC_FLAG_UNICODE,
- onigenc_unicode_case_map,
};
diff --git a/enc/utf_32be.c b/enc/utf_32be.c
index a57b854674..17841e52a4 100644
--- a/enc/utf_32be.c
+++ b/enc/utf_32be.c
@@ -30,11 +30,23 @@
#include "regenc.h"
#include "iso_8859.h"
+static OnigCodePoint utf32be_mbc_to_code(const UChar* p, const UChar* end, OnigEncoding enc);
static int
-utf32be_mbc_enc_len(const UChar* p ARG_UNUSED, const OnigUChar* e ARG_UNUSED,
- OnigEncoding enc ARG_UNUSED)
+utf32be_mbc_enc_len(const UChar* p ARG_UNUSED, const OnigUChar* e,
+ OnigEncoding enc)
{
- return 4;
+ if (e < p) {
+ return ONIGENC_CONSTRUCT_MBCLEN_INVALID();
+ }
+ else if (e-p < 4) {
+ return ONIGENC_CONSTRUCT_MBCLEN_NEEDMORE(4-(int)(e-p));
+ }
+ else {
+ OnigCodePoint c = utf32be_mbc_to_code(p, e, enc);
+ if (!UNICODE_VALID_CODEPOINT_P(c))
+ return ONIGENC_CONSTRUCT_MBCLEN_INVALID();
+ return ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(4);
+ }
}
static int
@@ -187,9 +199,8 @@ OnigEncodingDefine(utf_32be, UTF_32BE) = {
onigenc_utf16_32_get_ctype_code_range,
utf32be_left_adjust_char_head,
onigenc_always_false_is_allowed_reverse_match,
+ onigenc_unicode_case_map,
0,
ONIGENC_FLAG_UNICODE,
- onigenc_unicode_case_map,
};
ENC_ALIAS("UCS-4BE", "UTF-32BE")
-
diff --git a/enc/utf_32le.c b/enc/utf_32le.c
index c48089d6ed..18b798f102 100644
--- a/enc/utf_32le.c
+++ b/enc/utf_32le.c
@@ -30,11 +30,23 @@
#include "regenc.h"
#include "iso_8859.h"
+static OnigCodePoint utf32le_mbc_to_code(const UChar* p, const UChar* end, OnigEncoding enc);
static int
-utf32le_mbc_enc_len(const UChar* p ARG_UNUSED, const OnigUChar* e ARG_UNUSED,
- OnigEncoding enc ARG_UNUSED)
+utf32le_mbc_enc_len(const UChar* p ARG_UNUSED, const OnigUChar* e,
+ OnigEncoding enc)
{
- return 4;
+ if (e < p) {
+ return ONIGENC_CONSTRUCT_MBCLEN_INVALID();
+ }
+ else if (e-p < 4) {
+ return ONIGENC_CONSTRUCT_MBCLEN_NEEDMORE(4-(int)(e-p));
+ }
+ else {
+ OnigCodePoint c = utf32le_mbc_to_code(p, e, enc);
+ if (!UNICODE_VALID_CODEPOINT_P(c))
+ return ONIGENC_CONSTRUCT_MBCLEN_INVALID();
+ return ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(4);
+ }
}
static int
@@ -187,8 +199,8 @@ OnigEncodingDefine(utf_32le, UTF_32LE) = {
onigenc_utf16_32_get_ctype_code_range,
utf32le_left_adjust_char_head,
onigenc_always_false_is_allowed_reverse_match,
+ onigenc_unicode_case_map,
0,
ONIGENC_FLAG_UNICODE,
- onigenc_unicode_case_map,
};
ENC_ALIAS("UCS-4LE", "UTF-32LE")
diff --git a/enc/utf_8.c b/enc/utf_8.c
index 862b13fd9b..cdf2510d84 100644
--- a/enc/utf_8.c
+++ b/enc/utf_8.c
@@ -28,17 +28,20 @@
*/
#include "regenc.h"
-#include "encindex.h"
+#ifdef RUBY
+# include "encindex.h"
+#endif
+
#ifndef ENCINDEX_UTF_8
-#define ENCINDEX_UTF_8 0
+# define ENCINDEX_UTF_8 0
#endif
#define USE_INVALID_CODE_SCHEME
#ifdef USE_INVALID_CODE_SCHEME
/* virtual codepoint values for invalid encoding byte 0xfe and 0xff */
-#define INVALID_CODE_FE 0xfffffffe
-#define INVALID_CODE_FF 0xffffffff
+# define INVALID_CODE_FE 0xfffffffe
+# define INVALID_CODE_FF 0xffffffff
#endif
#define VALID_CODE_LIMIT 0x0010ffff
@@ -414,7 +417,7 @@ get_case_fold_codes_by_str(OnigCaseFoldType flag,
OnigEncodingDefine(utf_8, UTF_8) = {
mbc_enc_len,
"UTF-8", /* name */
- 6, /* max byte length */
+ 4, /* max byte length */
1, /* min byte length */
is_mbc_newline,
mbc_to_code,
@@ -428,9 +431,9 @@ OnigEncodingDefine(utf_8, UTF_8) = {
get_ctype_code_range,
left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ onigenc_unicode_case_map,
ENCINDEX_UTF_8,
ONIGENC_FLAG_UNICODE,
- onigenc_unicode_case_map,
};
ENC_ALIAS("CP65001", "UTF-8")
@@ -444,4 +447,3 @@ ENC_ALIAS("CP65001", "UTF-8")
ENC_REPLICATE("UTF8-MAC", "UTF-8")
ENC_ALIAS("UTF-8-MAC", "UTF8-MAC")
ENC_ALIAS("UTF-8-HFS", "UTF8-MAC") /* Emacs 23.2 */
-
diff --git a/enc/windows_1250.c b/enc/windows_1250.c
index 47317ddaf6..d2cf7b16bc 100644
--- a/enc/windows_1250.c
+++ b/enc/windows_1250.c
@@ -191,40 +191,41 @@ cp1250_get_case_fold_codes_by_str(OnigCaseFoldType flag,
}
static int
-case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==SHARP_s) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == SHARP_s) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 'S';
- code = (flags&ONIGENC_CASE_TITLECASE) ? 's' : 'S';
+ code = (flags & ONIGENC_CASE_TITLECASE) ? 's' : 'S';
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 's';
code = 's';
}
}
else if ((EncCP1250_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
code = ENC_CP1250_TO_LOWER_CASE(code);
}
- else if (code==0xB5) ;
+ else if (code == 0xB5)
+ ;
else if ((EncCP1250_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code==0xB9)
+ if (code == 0xB9)
code = 0xA5;
- else if (code==0xBE)
+ else if (code == 0xBE)
code = 0xBC;
else if (code >= 0x8A && code <= 0xBF && code!=0xB9)
code -= 0x10;
@@ -232,11 +233,11 @@ case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
code -= 0x20;
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(windows_1250, Windows_1250) = {
@@ -256,9 +257,9 @@ OnigEncodingDefine(windows_1250, Windows_1250) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
/*
* Name: windows-1250
diff --git a/enc/windows_1251.c b/enc/windows_1251.c
index 0f9b7fa69a..fcd0f1015d 100644
--- a/enc/windows_1251.c
+++ b/enc/windows_1251.c
@@ -181,49 +181,50 @@ cp1251_get_case_fold_codes_by_str(OnigCaseFoldType flag,
}
static int
-case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
if ((EncCP1251_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
code = ENC_CP1251_TO_LOWER_CASE(code);
}
- else if (code==0xB5) ;
+ else if (code == 0xB5)
+ ;
else if ((EncCP1251_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if ((0x61<=code && code<=0x7A) || (0xE0<=code && code<=0xFF))
+ if ((0x61 <= code && code <= 0x7A) || (0xE0 <= code && code <= 0xFF))
code -= 0x20;
- else if (code==0xA2 || code==0xB3 || code==0xBE)
+ else if (code == 0xA2 || code == 0xB3 || code == 0xBE)
code -= 0x01;
- else if (code==0x83)
+ else if (code == 0x83)
code = 0x81;
- else if (code==0xBC)
+ else if (code == 0xBC)
code = 0xA3;
- else if (code==0xB4)
+ else if (code == 0xB4)
code = 0xA5;
else
code -= 0x10;
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(windows_1251, Windows_1251) = {
onigenc_single_byte_mbc_enc_len,
- "Windows-1251", /* name */
+ "Windows-1251",/* name */
1, /* max enc length */
1, /* min enc length */
onigenc_is_mbc_newline_0x0a,
@@ -238,9 +239,9 @@ OnigEncodingDefine(windows_1251, Windows_1251) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
/*
* Name: windows-1251
diff --git a/enc/windows_1252.c b/enc/windows_1252.c
index 4427f8e31e..5f90c15601 100644
--- a/enc/windows_1252.c
+++ b/enc/windows_1252.c
@@ -190,42 +190,43 @@ case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==SHARP_s) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == SHARP_s) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 'S';
- code = (flags&ONIGENC_CASE_TITLECASE) ? 's' : 'S';
+ code = (flags & ONIGENC_CASE_TITLECASE) ? 's' : 'S';
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 's';
code = 's';
}
}
else if ((EncCP1252_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
code = ENC_CP1252_TO_LOWER_CASE(code);
}
- else if (code==0x83 || code==0xAA || code==0xBA || code==0xB5) ;
+ else if (code == 0x83 || code == 0xAA || code == 0xBA || code == 0xB5)
+ ;
else if ((EncCP1252_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code==0x9A || code==0x9C || code==0x9E)
+ if (code == 0x9A || code == 0x9C || code == 0x9E)
code -= 0x10;
- else if (code==0xFF)
+ else if (code == 0xFF)
code -= 0x60;
else
code -= 0x20;
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(windows_1252, Windows_1252) = {
@@ -245,9 +246,9 @@ OnigEncodingDefine(windows_1252, Windows_1252) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
/*
* Name: windows-1252
diff --git a/enc/windows_1253.c b/enc/windows_1253.c
index 2157b55c99..9e9c63a581 100644
--- a/enc/windows_1253.c
+++ b/enc/windows_1253.c
@@ -214,62 +214,63 @@ get_case_fold_codes_by_str(OnigCaseFoldType flag,
}
static int
-case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==0xF2) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == 0xF2) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
code = 0xD3;
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
code = 0xF3;
}
}
- else if (code==0xB5) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ else if (code == 0xB5) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
code = 0xCC;
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
code = 0xEC;
}
}
- else if (code==0xC0 || code==0xE0 || code==0xB6) ;
+ else if (code == 0xC0 || code == 0xE0 || code == 0xB6)
+ ;
else if ((EncCP1253_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
code = ENC_CP1253_TO_LOWER_CASE(code);
}
else if ((EncCP1253_CtypeTable[code] & BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code==0xDC)
+ if (code == 0xDC)
code = 0xA2;
- else if (code>=0xDD && code<=0xDF)
+ else if (code >= 0xDD && code <= 0xDF)
code -= 0x25;
- else if (code==0xFC)
+ else if (code == 0xFC)
code = 0xBC;
- else if (code==0xFD || code==0xFE)
+ else if (code == 0xFD || code == 0xFE)
code -= 0x3F;
else
code -= 0x20;
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(windows_1253, Windows_1253) = {
@@ -289,8 +290,8 @@ OnigEncodingDefine(windows_1253, Windows_1253) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
ENC_ALIAS("CP1253", "Windows-1253")
diff --git a/enc/windows_1254.c b/enc/windows_1254.c
index 2ccf966b8e..9ae66978a2 100644
--- a/enc/windows_1254.c
+++ b/enc/windows_1254.c
@@ -212,9 +212,9 @@ apply_all_case_fold(OnigCaseFoldType flag,
static int
get_case_fold_codes_by_str(OnigCaseFoldType flag,
- const OnigUChar* p, const OnigUChar* end,
- OnigCaseFoldCodeItem items[],
- OnigEncoding enc ARG_UNUSED)
+ const OnigUChar* p, const OnigUChar* end,
+ OnigCaseFoldCodeItem items[],
+ OnigEncoding enc ARG_UNUSED)
{
return onigenc_get_case_fold_codes_by_str_with_map(
numberof(CaseFoldMap), CaseFoldMap, 1,
@@ -232,49 +232,50 @@ case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==SHARP_s) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == SHARP_s) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 'S';
- code = (flags&ONIGENC_CASE_TITLECASE) ? 's' : 'S';
+ code = (flags & ONIGENC_CASE_TITLECASE) ? 's' : 'S';
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 's';
code = 's';
}
}
else if ((EncCP1254_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code=='I')
- code = flags&ONIGENC_CASE_FOLD_TURKISH_AZERI ? DOTLESS_i : 'i';
+ if (code == 'I')
+ code = flags & ONIGENC_CASE_FOLD_TURKISH_AZERI ? DOTLESS_i : 'i';
else
code = ENC_CP1254_TO_LOWER_CASE(code);
}
- else if (code==0x83 || code==0xAA || code==0xBA || code==0xB5) ;
- else if ((EncCP1254_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ else if (code == 0x83 || code == 0xAA || code == 0xBA || code == 0xB5)
+ ;
+ else if ((EncCP1254_CtypeTable[code] & BIT_CTYPE_LOWER)
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code=='i')
- code = flags&ONIGENC_CASE_FOLD_TURKISH_AZERI ? I_WITH_DOT_ABOVE : 'I';
- else if (code==DOTLESS_i)
+ if (code == 'i')
+ code = flags & ONIGENC_CASE_FOLD_TURKISH_AZERI ? I_WITH_DOT_ABOVE : 'I';
+ else if (code == DOTLESS_i)
code = 'I';
- else if (code==0x9A || code==0x9C || code==0x9E)
+ else if (code == 0x9A || code == 0x9C || code == 0x9E)
code -= 0x10;
- else if (code==0xFF)
+ else if (code == 0xFF)
code -= 0x60;
else
code -= 0x20;
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(windows_1254, Windows_1254) = {
@@ -294,8 +295,8 @@ OnigEncodingDefine(windows_1254, Windows_1254) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
ENC_ALIAS("CP1254", "Windows-1254")
diff --git a/enc/windows_1257.c b/enc/windows_1257.c
index 40cdb969aa..936a94ac76 100644
--- a/enc/windows_1257.c
+++ b/enc/windows_1257.c
@@ -216,9 +216,9 @@ apply_all_case_fold(OnigCaseFoldType flag,
static int
get_case_fold_codes_by_str(OnigCaseFoldType flag,
- const OnigUChar* p, const OnigUChar* end,
- OnigCaseFoldCodeItem items[],
- OnigEncoding enc ARG_UNUSED)
+ const OnigUChar* p, const OnigUChar* end,
+ OnigCaseFoldCodeItem items[],
+ OnigEncoding enc ARG_UNUSED)
{
return onigenc_get_case_fold_codes_by_str_with_map(
numberof(CaseFoldMap), CaseFoldMap, 1,
@@ -228,55 +228,56 @@ get_case_fold_codes_by_str(OnigCaseFoldType flag,
#define DOTLESS_i (0xB9)
#define I_WITH_DOT_ABOVE (0xA9)
static int
-case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code==SHARP_s) {
- if (flags&ONIGENC_CASE_UPCASE) {
+ if (code == SHARP_s) {
+ if (flags & ONIGENC_CASE_UPCASE) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 'S';
- code = (flags&ONIGENC_CASE_TITLECASE) ? 's' : 'S';
+ code = (flags & ONIGENC_CASE_TITLECASE) ? 's' : 'S';
}
- else if (flags&ONIGENC_CASE_FOLD) {
+ else if (flags & ONIGENC_CASE_FOLD) {
flags |= ONIGENC_CASE_MODIFIED;
*to++ = 's';
code = 's';
}
}
- else if (code==0xB5) ;
+ else if (code == 0xB5)
+ ;
else if ((EncCP1252_CtypeTable[code] & BIT_CTYPE_UPPER)
- && (flags & (ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD))) {
+ && (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code=='I')
- code = flags&ONIGENC_CASE_FOLD_TURKISH_AZERI ? DOTLESS_i : 'i';
+ if (code == 'I')
+ code = flags & ONIGENC_CASE_FOLD_TURKISH_AZERI ? DOTLESS_i : 'i';
else
code = ENC_CP1252_TO_LOWER_CASE(code);
}
else if ((EncCP1252_CtypeTable[code]&BIT_CTYPE_LOWER)
- && (flags&ONIGENC_CASE_UPCASE)) {
+ && (flags & ONIGENC_CASE_UPCASE)) {
flags |= ONIGENC_CASE_MODIFIED;
- if (code=='i')
- code = flags&ONIGENC_CASE_FOLD_TURKISH_AZERI ? I_WITH_DOT_ABOVE : 'I';
- else if (code==DOTLESS_i)
+ if (code == 'i')
+ code = flags & ONIGENC_CASE_FOLD_TURKISH_AZERI ? I_WITH_DOT_ABOVE : 'I';
+ else if (code == DOTLESS_i)
code = 'I';
- else if (code>=0xB0 && code<=0xBF )
+ else if (code >= 0xB0 && code <= 0xBF)
code -= 0x10;
else
code -= 0x20;
}
*to++ = code;
- if (flags&ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
OnigEncodingDefine(windows_1257, Windows_1257) = {
@@ -296,9 +297,8 @@ OnigEncodingDefine(windows_1257, Windows_1257) = {
onigenc_not_support_get_ctype_code_range,
onigenc_single_byte_left_adjust_char_head,
onigenc_always_true_is_allowed_reverse_match,
+ case_map,
0,
ONIGENC_FLAG_NONE,
- case_map,
};
-
ENC_ALIAS("CP1257", "Windows-1257")
diff --git a/enc/windows_31j.c b/enc/windows_31j.c
index 71836c1f13..e3a468f414 100644
--- a/enc/windows_31j.c
+++ b/enc/windows_31j.c
@@ -1,5 +1,5 @@
/**********************************************************************
- cp932.c - Onigmo (Oniguruma-mod) (regular expression library)
+ windows_31j.c - Onigmo (Oniguruma-mod) (regular expression library)
**********************************************************************/
/*-
* Copyright (c) 2002-2009 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
@@ -29,11 +29,11 @@
*/
#define ENC_CP932
-#include "shift_jis.c"
+#include "shift_jis.h"
OnigEncodingDefine(windows_31j, Windows_31J) = {
mbc_enc_len,
- "Windows-31J", /* name */
+ "Windows-31J", /* name */
2, /* max byte length */
1, /* min byte length */
onigenc_is_mbc_newline_0x0a,
@@ -48,9 +48,9 @@ OnigEncodingDefine(windows_31j, Windows_31J) = {
get_ctype_code_range,
left_adjust_char_head,
is_allowed_reverse_match,
+ onigenc_ascii_only_case_map,
0,
ONIGENC_FLAG_NONE,
- onigenc_ascii_only_case_map,
};
/*
* Name: Windows-31J
diff --git a/encindex.h b/encindex.h
index dcb1646f3a..658b60a9fd 100644
--- a/encindex.h
+++ b/encindex.h
@@ -57,6 +57,8 @@ enum ruby_preserved_encindex {
#define rb_utf8_encindex() RUBY_ENCINDEX_UTF_8
#define rb_usascii_encindex() RUBY_ENCINDEX_US_ASCII
+int rb_enc_find_index2(const char *name, long len);
+
#if defined(__cplusplus)
#if 0
{ /* satisfy cc-mode */
diff --git a/encoding.c b/encoding.c
index f5ec1e756d..10a0ab0371 100644
--- a/encoding.c
+++ b/encoding.c
@@ -9,6 +9,7 @@
**********************************************************************/
+#include "ruby/encoding.h"
#include "internal.h"
#include "encindex.h"
#include "regenc.h"
@@ -259,11 +260,6 @@ rb_find_encoding(VALUE enc)
return rb_enc_from_index(idx);
}
-void
-rb_gc_mark_encodings(void)
-{
-}
-
static int
enc_table_expand(int newsize)
{
@@ -660,7 +656,7 @@ load_encoding(const char *name)
++s;
}
FL_UNSET(enclib, FL_TAINT);
- OBJ_FREEZE(enclib);
+ enclib = rb_fstring(enclib);
ruby_verbose = Qfalse;
ruby_debug = Qfalse;
errinfo = rb_errinfo();
@@ -724,6 +720,17 @@ rb_enc_find_index(const char *name)
return i;
}
+int
+rb_enc_find_index2(const char *name, long len)
+{
+ char buf[ENCODING_NAMELEN_MAX+1];
+
+ if (len > ENCODING_NAMELEN_MAX) return -1;
+ memcpy(buf, name, len);
+ buf[len] = '\0';
+ return rb_enc_find_index(buf);
+}
+
rb_encoding *
rb_enc_find(const char *name)
{
@@ -749,6 +756,12 @@ enc_capable(VALUE obj)
}
}
+int
+rb_enc_capable(VALUE obj)
+{
+ return enc_capable(obj);
+}
+
ID
rb_id_encoding(void)
{
@@ -780,24 +793,26 @@ rb_enc_get_index(VALUE obj)
obj = rb_sym2str(obj);
}
switch (BUILTIN_TYPE(obj)) {
- as_default:
- default:
case T_STRING:
+ case T_SYMBOL:
case T_REGEXP:
i = enc_get_index_str(obj);
break;
case T_FILE:
tmp = rb_funcallv(obj, rb_intern("internal_encoding"), 0, 0);
- if (NIL_P(tmp)) obj = rb_funcallv(obj, rb_intern("external_encoding"), 0, 0);
- else obj = tmp;
- if (NIL_P(obj)) break;
+ 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);
}
- else {
- goto as_default;
- }
+ break;
+ default:
break;
}
return i;
@@ -806,6 +821,10 @@ rb_enc_get_index(VALUE obj)
static void
enc_set_index(VALUE obj, int idx)
{
+ if (!enc_capable(obj)) {
+ rb_raise(rb_eArgError, "cannot set encoding on non-encoding capable object");
+ }
+
if (idx < ENCODING_INLINE_MAX) {
ENCODING_SET_INLINED(obj, idx);
return;
@@ -1031,7 +1050,8 @@ rb_enc_precise_mbclen(const char *p, const char *e, rb_encoding *enc)
int
rb_enc_ascget(const char *p, const char *e, int *len, rb_encoding *enc)
{
- unsigned int c, l;
+ unsigned int c;
+ int l;
if (e <= p)
return -1;
if (rb_enc_asciicompat(enc)) {
@@ -1149,8 +1169,7 @@ 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_usascii_str_new2((char *)name);
- OBJ_FREEZE(str);
+ VALUE str = rb_fstring_cstr((char *)name);
rb_ary_push(arg[1], str);
}
return ST_CONTINUE;
@@ -1684,8 +1703,7 @@ rb_enc_aliases_enc_i(st_data_t name, st_data_t orig, st_data_t arg)
str = rb_fstring_cstr(rb_enc_name(enc));
rb_ary_store(ary, idx, str);
}
- key = rb_usascii_str_new2((char *)name);
- OBJ_FREEZE(key);
+ key = rb_fstring_cstr((char *)name);
rb_hash_aset(aliases, key, str);
return ST_CONTINUE;
}
diff --git a/enum.c b/enum.c
index b328003425..7b4d4b42a8 100644
--- a/enum.c
+++ b/enum.c
@@ -9,9 +9,12 @@
**********************************************************************/
+#include "ruby/encoding.h"
#include "internal.h"
#include "ruby/util.h"
#include "id.h"
+#include "symbol.h"
+#include "transient_heap.h"
#include <assert.h>
@@ -19,13 +22,13 @@ VALUE rb_mEnumerable;
static ID id_next;
static ID id_div;
-static ID id_call;
-static ID id_size;
#define id_each idEach
#define id_eqq idEqq
#define id_cmp idCmp
#define id_lshift idLTLT
+#define id_call idCall
+#define id_size idSize
VALUE
rb_enum_values_pack(int argc, const VALUE *argv)
@@ -39,7 +42,27 @@ rb_enum_values_pack(int argc, const VALUE *argv)
i = rb_enum_values_pack(argc, argv); \
} while (0)
-#define enum_yield rb_yield_values2
+static VALUE
+enum_yield(int argc, VALUE ary)
+{
+ if (argc > 1)
+ return rb_yield_force_blockarg(ary);
+ if (argc == 1)
+ return rb_yield(ary);
+ return rb_yield_values2(0, 0);
+}
+
+static VALUE
+enum_yield_array(VALUE ary)
+{
+ long len = RARRAY_LEN(ary);
+
+ if (len > 1)
+ return rb_yield_force_blockarg(ary);
+ if (len == 1)
+ return rb_yield(RARRAY_AREF(ary, 0));
+ return rb_yield_values2(0, 0);
+}
static VALUE
grep_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
@@ -47,7 +70,7 @@ grep_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
struct MEMO *memo = MEMO_CAST(args);
ENUM_WANT_SVALUE();
- if (RTEST(rb_funcall(memo->v1, id_eqq, 1, i)) == RTEST(memo->u3.value)) {
+ if (RTEST(rb_funcallv(memo->v1, id_eqq, 1, &i)) == RTEST(memo->u3.value)) {
rb_ary_push(memo->v2, i);
}
return Qnil;
@@ -59,8 +82,8 @@ grep_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
struct MEMO *memo = MEMO_CAST(args);
ENUM_WANT_SVALUE();
- if (RTEST(rb_funcall(memo->v1, id_eqq, 1, i)) == RTEST(memo->u3.value)) {
- rb_ary_push(memo->v2, rb_yield(i));
+ if (RTEST(rb_funcallv(memo->v1, id_eqq, 1, &i)) == RTEST(memo->u3.value)) {
+ rb_ary_push(memo->v2, enum_yield(argc, i));
}
return Qnil;
}
@@ -120,6 +143,34 @@ enum_grep_v(VALUE obj, VALUE pat)
return ary;
}
+#define COUNT_BIGNUM IMEMO_FL_USER0
+#define MEMO_V3_SET(m, v) RB_OBJ_WRITE((m), &(m)->u3.value, (v))
+
+static void
+imemo_count_up(struct MEMO *memo)
+{
+ if (memo->flags & COUNT_BIGNUM) {
+ 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;
+ }
+}
+
+static VALUE
+imemo_count_value(struct MEMO *memo)
+{
+ if (memo->flags & COUNT_BIGNUM) {
+ return memo->u3.value;
+ }
+ else {
+ return ULONG2NUM(memo->u3.cnt);
+ }
+}
+
static VALUE
count_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
{
@@ -128,7 +179,7 @@ count_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
ENUM_WANT_SVALUE();
if (rb_equal(i, memo->v1)) {
- memo->u3.cnt++;
+ imemo_count_up(memo);
}
return Qnil;
}
@@ -138,8 +189,8 @@ count_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
{
struct MEMO *memo = MEMO_CAST(memop);
- if (RTEST(enum_yield(argc, argv))) {
- memo->u3.cnt++;
+ if (RTEST(rb_yield_values2(argc, argv))) {
+ imemo_count_up(memo);
}
return Qnil;
}
@@ -149,7 +200,7 @@ count_all_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
{
struct MEMO *memo = MEMO_CAST(memop);
- memo->u3.cnt++;
+ imemo_count_up(memo);
return Qnil;
}
@@ -196,7 +247,7 @@ enum_count(int argc, VALUE *argv, VALUE obj)
memo = MEMO_NEW(item, 0, 0);
rb_block_call(obj, id_each, 0, 0, func, (VALUE)memo);
- return INT2NUM(memo->u3.cnt);
+ return imemo_count_value(memo);
}
static VALUE
@@ -204,7 +255,7 @@ find_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
{
ENUM_WANT_SVALUE();
- if (RTEST(rb_yield(i))) {
+ if (RTEST(enum_yield(argc, i))) {
struct MEMO *memo = MEMO_CAST(memop);
MEMO_V1_SET(memo, i);
memo->u3.cnt = 1;
@@ -227,13 +278,13 @@ find_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
*
* If no block is given, an enumerator is returned instead.
*
- * (1..100).detect => #<Enumerator: 1..100:detect>
- * (1..100).find => #<Enumerator: 1..100:find>
+ * (1..100).detect #=> #<Enumerator: 1..100:detect>
+ * (1..100).find #=> #<Enumerator: 1..100:find>
*
- * (1..10).detect { |i| i % 5 == 0 and i % 7 == 0 } #=> nil
- * (1..10).find { |i| i % 5 == 0 and i % 7 == 0 } #=> nil
- * (1..100).detect { |i| i % 5 == 0 and i % 7 == 0 } #=> 35
- * (1..100).find { |i| i % 5 == 0 and i % 7 == 0 } #=> 35
+ * (1..10).detect { |i| i % 5 == 0 and i % 7 == 0 } #=> nil
+ * (1..10).find { |i| i % 5 == 0 and i % 7 == 0 } #=> nil
+ * (1..100).detect { |i| i % 5 == 0 and i % 7 == 0 } #=> 35
+ * (1..100).find { |i| i % 5 == 0 and i % 7 == 0 } #=> 35
*
*/
@@ -243,7 +294,7 @@ enum_find(int argc, VALUE *argv, VALUE obj)
struct MEMO *memo;
VALUE if_none;
- rb_scan_args(argc, argv, "01", &if_none);
+ if_none = rb_check_arity(argc, 0, 1) ? argv[0] : Qnil;
RETURN_ENUMERATOR(obj, argc, argv);
memo = MEMO_NEW(Qundef, 0, 0);
rb_block_call(obj, id_each, 0, 0, find_i, (VALUE)memo);
@@ -264,10 +315,10 @@ find_index_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
ENUM_WANT_SVALUE();
if (rb_equal(i, memo->v2)) {
- MEMO_V1_SET(memo, UINT2NUM(memo->u3.cnt));
+ MEMO_V1_SET(memo, imemo_count_value(memo));
rb_iter_break();
}
- memo->u3.cnt++;
+ imemo_count_up(memo);
return Qnil;
}
@@ -276,11 +327,11 @@ find_index_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
{
struct MEMO *memo = MEMO_CAST(memop);
- if (RTEST(enum_yield(argc, argv))) {
- MEMO_V1_SET(memo, UINT2NUM(memo->u3.cnt));
+ if (RTEST(rb_yield_values2(argc, argv))) {
+ MEMO_V1_SET(memo, imemo_count_value(memo));
rb_iter_break();
}
- memo->u3.cnt++;
+ imemo_count_up(memo);
return Qnil;
}
@@ -332,7 +383,7 @@ find_all_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
{
ENUM_WANT_SVALUE();
- if (RTEST(rb_yield(i))) {
+ if (RTEST(enum_yield(argc, i))) {
rb_ary_push(ary, i);
}
return Qnil;
@@ -341,9 +392,7 @@ find_all_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
static VALUE
enum_size(VALUE self, VALUE args, VALUE eobj)
{
- VALUE r;
- r = rb_check_funcall(self, id_size, 0, 0);
- return (r == Qundef) ? Qnil : r;
+ return rb_check_funcall_default(self, id_size, 0, 0, Qnil);
}
static long
@@ -368,8 +417,10 @@ enum_size_over_p(VALUE obj, long n)
* call-seq:
* enum.find_all { |obj| block } -> array
* enum.select { |obj| block } -> array
+ * enum.filter { |obj| block } -> array
* enum.find_all -> an_enumerator
* enum.select -> an_enumerator
+ * enum.filter -> an_enumerator
*
* Returns an array containing all elements of +enum+
* for which the given +block+ returns a true value.
@@ -381,6 +432,8 @@ enum_size_over_p(VALUE obj, long n)
*
* [1,2,3,4,5].select { |num| num.even? } #=> [2, 4]
*
+ * [:foo, :bar].filter { |x| x == :foo } #=> [:foo]
+ *
* See also Enumerable#reject.
*/
@@ -402,7 +455,7 @@ reject_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
{
ENUM_WANT_SVALUE();
- if (!RTEST(rb_yield(i))) {
+ if (!RTEST(enum_yield(argc, i))) {
rb_ary_push(ary, i);
}
return Qnil;
@@ -441,7 +494,7 @@ enum_reject(VALUE obj)
static VALUE
collect_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
{
- rb_ary_push(ary, enum_yield(argc, argv));
+ rb_ary_push(ary, rb_yield_values2(argc, argv));
return Qnil;
}
@@ -476,11 +529,13 @@ static VALUE
enum_collect(VALUE obj)
{
VALUE ary;
+ int min_argc, max_argc;
RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
ary = rb_ary_new();
- rb_block_call(obj, id_each, 0, 0, collect_i, ary);
+ min_argc = rb_block_min_max_arity(&max_argc);
+ rb_lambda_call(obj, id_each, 0, 0, collect_i, min_argc, max_argc, ary);
return ary;
}
@@ -490,7 +545,7 @@ flat_map_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
{
VALUE tmp;
- i = enum_yield(argc, argv);
+ i = rb_yield_values2(argc, argv);
tmp = rb_check_array_type(i);
if (NIL_P(tmp)) {
@@ -559,38 +614,42 @@ enum_to_a(int argc, VALUE *argv, VALUE obj)
static VALUE
enum_to_h_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
{
- VALUE key_value_pair;
ENUM_WANT_SVALUE();
rb_thread_check_ints();
- key_value_pair = rb_check_array_type(i);
- if (NIL_P(key_value_pair)) {
- rb_raise(rb_eTypeError, "wrong element type %s (expected array)",
- rb_builtin_class_name(i));
- }
- if (RARRAY_LEN(key_value_pair) != 2) {
- rb_raise(rb_eArgError, "element has wrong array length (expected 2, was %ld)",
- RARRAY_LEN(key_value_pair));
- }
- rb_hash_aset(hash, RARRAY_AREF(key_value_pair, 0), RARRAY_AREF(key_value_pair, 1));
- return Qnil;
+ return rb_hash_set_pair(hash, i);
+}
+
+static VALUE
+enum_to_h_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
+{
+ rb_thread_check_ints();
+ return rb_hash_set_pair(hash, rb_yield_values2(argc, argv));
}
/*
* call-seq:
- * enum.to_h(*args) -> hash
+ * enum.to_h(*args) -> hash
+ * enum.to_h(*args) {...} -> hash
*
* Returns the result of interpreting <i>enum</i> as a list of
* <tt>[key, value]</tt> pairs.
*
* %i[hello world].each_with_index.to_h
* # => {:hello => 0, :world => 1}
+ *
+ * If a block is given, the results of the block on each element of
+ * the enum will be used as pairs.
+ *
+ * (1..5).to_h {|x| [x, x ** 2]}
+ * #=> {1=>1, 2=>4, 3=>9, 4=>16, 5=>25}
*/
static VALUE
enum_to_h(int argc, VALUE *argv, VALUE obj)
{
VALUE hash = rb_hash_new();
- rb_block_call(obj, id_each, argc, argv, enum_to_h_i, hash);
+ rb_block_call_func *iter = rb_block_given_p() ? enum_to_h_ii : enum_to_h_i;
+ rb_block_call(obj, id_each, argc, argv, iter, hash);
OBJ_INFECT(hash, obj);
return hash;
}
@@ -624,7 +683,7 @@ inject_op_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, p))
}
else if (SYMBOL_P(name = memo->u3.value)) {
const ID mid = SYM2ID(name);
- MEMO_V1_SET(memo, rb_funcall(memo->v1, mid, 1, i));
+ MEMO_V1_SET(memo, rb_funcallv(memo->v1, mid, 1, &i));
}
else {
VALUE args[2];
@@ -658,15 +717,16 @@ ary_inject_op(VALUE ary, VALUE init, VALUE op)
id = SYM2ID(op);
if (id == idPLUS) {
- if ((FIXNUM_P(v) || RB_TYPE_P(v, T_BIGNUM)) &&
- rb_method_basic_definition_p(rb_cInteger, idPLUS)) {
+ 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);
if (FIXNUM_P(e)) {
n += FIX2LONG(e); /* should not overflow long type */
if (!FIXABLE(n)) {
- v = rb_big_plus(LONG2NUM(n), v);
+ v = rb_big_plus(ULONG2NUM(n), v);
n = 0;
}
}
@@ -685,7 +745,8 @@ ary_inject_op(VALUE ary, VALUE init, VALUE op)
}
}
for (; i < RARRAY_LEN(ary); i++) {
- v = rb_funcall(v, id, 1, RARRAY_AREF(ary, i));
+ VALUE arg = RARRAY_AREF(ary, i);
+ v = rb_funcallv_public(v, id, 1, &arg);
}
return v;
}
@@ -787,7 +848,7 @@ partition_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arys))
VALUE ary;
ENUM_WANT_SVALUE();
- if (RTEST(rb_yield(i))) {
+ if (RTEST(enum_yield(argc, i))) {
ary = memo->v1;
}
else {
@@ -833,7 +894,7 @@ group_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
ENUM_WANT_SVALUE();
- group = rb_yield(i);
+ group = enum_yield(argc, i);
values = rb_hash_aref(hash, group);
if (!RB_TYPE_P(values, T_ARRAY)) {
values = rb_ary_new3(1, i);
@@ -883,7 +944,7 @@ first_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, params))
MEMO_V1_SET(memo, i);
rb_iter_break();
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
static VALUE enum_take(VALUE obj, VALUE n);
@@ -926,19 +987,23 @@ enum_first(int argc, VALUE *argv, VALUE obj)
* enum.sort -> array
* enum.sort { |a, b| block } -> array
*
- * Returns an array containing the items in <i>enum</i> sorted,
- * either according to their own <code><=></code> method, or by using
- * the results of the supplied block. The block should return -1, 0, or
- * +1 depending on the comparison between <i>a</i> and <i>b</i>. As of
- * Ruby 1.8, the method <code>Enumerable#sort_by</code> implements a
- * built-in Schwartzian Transform, useful when key computation or
- * comparison is expensive.
+ * Returns an array containing the items in <i>enum</i> sorted.
+ *
+ * Comparisons for the sort will be done using the items' own
+ * <code><=></code> operator or using an optional code block.
*
- * The result is not guaranteed as stable. When comparison of two
+ * The block must implement a comparison between +a+ and +b+ and return
+ * an integer less than 0 when +b+ follows +a+, +0+ when +a+ and +b+
+ * are equivalent, or an integer greater than 0 when +a+ follows +b+.
+ *
+ * The result is not guaranteed to be stable. When the comparison of two
* elements returns +0+, the order of the elements is unpredictable.
*
- * %w(rhea kea flea).sort #=> ["flea", "kea", "rhea"]
+ * %w(rhea kea flea).sort #=> ["flea", "kea", "rhea"]
* (1..10).sort { |a, b| b <=> a } #=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
+ *
+ * See also Enumerable#sort_by. It implements a Schwartzian transform
+ * which is useful when key computation or comparison is expensive.
*/
static VALUE
@@ -963,7 +1028,7 @@ sort_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _data))
ENUM_WANT_SVALUE();
- v = rb_yield(i);
+ v = enum_yield(argc, i);
if (RBASIC(ary)->klass) {
rb_raise(rb_eRuntimeError, "sort_by reentered");
@@ -985,6 +1050,7 @@ 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;
@@ -996,7 +1062,7 @@ sort_by_cmp(const void *ap, const void *bp, void *data)
a = *(VALUE *)ap;
b = *(VALUE *)bp;
- return rb_cmpint(rb_funcall(a, id_cmp, 1, b), a, b);
+ return OPTIMIZED_CMP(a, b, cmp_opt);
}
/*
@@ -1007,12 +1073,12 @@ sort_by_cmp(const void *ap, const void *bp, void *data)
* Sorts <i>enum</i> using a set of keys generated by mapping the
* values in <i>enum</i> through the given block.
*
- * The result is not guaranteed as stable. When two keys are equal,
+ * The result is not guaranteed to be stable. When two keys are equal,
* the order of the corresponding elements is unpredictable.
*
* If no block is given, an enumerator is returned instead.
*
- * %w{apple pear fig}.sort_by { |word| word.length}
+ * %w{apple pear fig}.sort_by { |word| word.length }
* #=> ["fig", "pear", "apple"]
*
* The current implementation of <code>sort_by</code> generates an
@@ -1057,7 +1123,7 @@ sort_by_cmp(const void *ap, const void *bp, void *data)
* This still generates many unnecessary <code>Time</code> objects. A
* more efficient technique is to cache the sort keys (modification
* times in this case) before the sort. Perl users often call this
- * approach a Schwartzian Transform, after Randal Schwartz. We
+ * approach a Schwartzian transform, after Randal Schwartz. We
* construct a temporary array, where each element is an array
* containing our sort key along with the filename. We sort this array,
* and then extract the filename from the result.
@@ -1106,9 +1172,9 @@ enum_sort_by(VALUE obj)
rb_ary_concat(ary, buf);
}
if (RARRAY_LEN(ary) > 2) {
- RARRAY_PTR_USE(ary, ptr,
- ruby_qsort(ptr, RARRAY_LEN(ary)/2, 2*sizeof(VALUE),
- sort_by_cmp, (void *)ary));
+ RARRAY_PTR_USE(ary, ptr,
+ ruby_qsort(ptr, RARRAY_LEN(ary)/2, 2*sizeof(VALUE),
+ sort_by_cmp, (void *)ary));
}
if (RBASIC(ary)->klass) {
rb_raise(rb_eRuntimeError, "sort_by reentered");
@@ -1123,7 +1189,9 @@ enum_sort_by(VALUE obj)
return ary;
}
-#define ENUMFUNC(name) rb_block_given_p() ? name##_iter_i : name##_i
+#define ENUMFUNC(name) argc ? name##_eqq : rb_block_given_p() ? name##_iter_i : name##_i
+
+#define MEMO_ENUM_NEW(v1) (rb_check_arity(argc, 0, 1), MEMO_NEW((v1), (argc ? *argv : 0), 0))
#define DEFINE_ENUMFUNCS(name) \
static VALUE enum_##name##_func(VALUE result, struct MEMO *memo); \
@@ -1137,12 +1205,25 @@ name##_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memo)) \
static VALUE \
name##_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memo)) \
{ \
- return enum_##name##_func(enum_yield(argc, argv), MEMO_CAST(memo)); \
+ return enum_##name##_func(rb_yield_values2(argc, argv), MEMO_CAST(memo)); \
+} \
+\
+static VALUE \
+name##_eqq(RB_BLOCK_CALL_FUNC_ARGLIST(i, memo)) \
+{ \
+ ENUM_WANT_SVALUE(); \
+ return enum_##name##_func(rb_funcallv(MEMO_CAST(memo)->v2, id_eqq, 1, &i), MEMO_CAST(memo)); \
} \
\
static VALUE \
enum_##name##_func(VALUE result, struct MEMO *memo)
+#define WARN_UNUSED_BLOCK(argc) do { \
+ if ((argc) > 0 && rb_block_given_p()) { \
+ rb_warn("given block not used"); \
+ } \
+} while (0)
+
DEFINE_ENUMFUNCS(all)
{
if (!RTEST(result)) {
@@ -1155,6 +1236,7 @@ DEFINE_ENUMFUNCS(all)
/*
* call-seq:
* enum.all? [{ |obj| block } ] -> true or false
+ * enum.all?(pattern) -> true or false
*
* Passes each element of the collection to the given block. The method
* returns <code>true</code> if the block never returns
@@ -1163,16 +1245,23 @@ DEFINE_ENUMFUNCS(all)
* cause #all? to return +true+ when none of the collection members are
* +false+ or +nil+.
*
+ * If instead a pattern is supplied, the method returns whether
+ * <code>pattern === element</code> for every collection member.
+ *
* %w[ant bear cat].all? { |word| word.length >= 3 } #=> true
* %w[ant bear cat].all? { |word| word.length >= 4 } #=> false
+ * %w[ant bear cat].all?(/t/) #=> false
+ * [1, 2i, 3.14].all?(Numeric) #=> true
* [nil, true, 99].all? #=> false
+ * [].all? #=> true
*
*/
static VALUE
-enum_all(VALUE obj)
+enum_all(int argc, VALUE *argv, VALUE obj)
{
- struct MEMO *memo = MEMO_NEW(Qtrue, 0, 0);
+ struct MEMO *memo = MEMO_ENUM_NEW(Qtrue);
+ WARN_UNUSED_BLOCK(argc);
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(all), (VALUE)memo);
return memo->v1;
}
@@ -1189,6 +1278,7 @@ DEFINE_ENUMFUNCS(any)
/*
* call-seq:
* enum.any? [{ |obj| block }] -> true or false
+ * enum.any?(pattern) -> true or false
*
* Passes each element of the collection to the given block. The method
* returns <code>true</code> if the block ever returns a value other
@@ -1197,16 +1287,23 @@ DEFINE_ENUMFUNCS(any)
* will cause #any? to return +true+ if at least one of the collection
* members is not +false+ or +nil+.
*
+ * If instead a pattern is supplied, the method returns whether
+ * <code>pattern === element</code> for any collection member.
+ *
* %w[ant bear cat].any? { |word| word.length >= 3 } #=> true
* %w[ant bear cat].any? { |word| word.length >= 4 } #=> true
+ * %w[ant bear cat].any?(/d/) #=> false
+ * [nil, true, 99].any?(Integer) #=> true
* [nil, true, 99].any? #=> true
+ * [].any? #=> false
*
*/
static VALUE
-enum_any(VALUE obj)
+enum_any(int argc, VALUE *argv, VALUE obj)
{
- struct MEMO *memo = MEMO_NEW(Qfalse, 0, 0);
+ struct MEMO *memo = MEMO_ENUM_NEW(Qfalse);
+ WARN_UNUSED_BLOCK(argc);
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(any), (VALUE)memo);
return memo->v1;
}
@@ -1226,27 +1323,36 @@ DEFINE_ENUMFUNCS(one)
}
struct nmin_data {
- long n;
- long bufmax;
- long curlen;
- VALUE buf;
- VALUE limit;
- int (*cmpfunc)(const void *, const void *, void *);
- int rev; /* max if 1 */
- int by; /* min_by if 1 */
- const char *method;
+ long n;
+ long bufmax;
+ long curlen;
+ VALUE buf;
+ VALUE limit;
+ int (*cmpfunc)(const void *, const void *, void *);
+ int rev: 1; /* max if 1 */
+ int by: 1; /* min_by if 1 */
};
+static VALUE
+cmpint_reenter_check(struct nmin_data *data, VALUE val)
+{
+ if (RBASIC(data->buf)->klass) {
+ rb_raise(rb_eRuntimeError, "%s%s reentered",
+ data->rev ? "max" : "min",
+ data->by ? "_by" : "");
+ }
+ return 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;
- VALUE cmp = rb_funcall(a, id_cmp, 1, b);
- if (RBASIC(data->buf)->klass) {
- rb_raise(rb_eRuntimeError, "%s reentered", data->method);
- }
- return rb_cmpint(cmp, a, b);
+#define rb_cmpint(cmp, a, b) rb_cmpint(cmpint_reenter_check(data, (cmp)), a, b)
+ return OPTIMIZED_CMP(a, b, cmp_opt);
+#undef rb_cmpint
}
static int
@@ -1255,13 +1361,10 @@ nmin_block_cmp(const void *ap, const void *bp, void *_data)
struct nmin_data *data = (struct nmin_data *)_data;
VALUE a = *(const VALUE *)ap, b = *(const VALUE *)bp;
VALUE cmp = rb_yield_values(2, a, b);
- if (RBASIC(data->buf)->klass) {
- rb_raise(rb_eRuntimeError, "%s reentered", data->method);
- }
+ cmpint_reenter_check(data, cmp);
return rb_cmpint(cmp, a, b);
}
-
static void
nmin_filter(struct nmin_data *data)
{
@@ -1340,7 +1443,7 @@ nmin_filter(struct nmin_data *data)
#undef GETPTR
#undef SWAP
- data->limit = RARRAY_PTR(data->buf)[store_index*eltsize]; /* the last pivot */
+ data->limit = RARRAY_AREF(data->buf, store_index*eltsize); /* the last pivot */
data->curlen = data->n;
rb_ary_resize(data->buf, data->n * eltsize);
}
@@ -1354,7 +1457,7 @@ nmin_i(VALUE i, VALUE *_data, int argc, VALUE *argv)
ENUM_WANT_SVALUE();
if (data->by)
- cmpv = rb_yield(i);
+ cmpv = enum_yield(argc, i);
else
cmpv = i;
@@ -1401,14 +1504,12 @@ rb_nmin_run(VALUE obj, VALUE num, int by, int rev, int ary)
nmin_cmp;
data.rev = rev;
data.by = by;
- data.method = rev ? (by ? "max_by" : "max")
- : (by ? "min_by" : "min");
if (ary) {
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);
+ VALUE args[1];
+ args[0] = RARRAY_AREF(obj, i);
+ nmin_i(obj, (VALUE*)&data, 1, args);
}
}
else {
@@ -1418,23 +1519,27 @@ rb_nmin_run(VALUE obj, VALUE num, int by, int rev, int ary)
result = data.buf;
if (by) {
long i;
- ruby_qsort(RARRAY_PTR(result),
- RARRAY_LEN(result)/2,
- sizeof(VALUE)*2,
- data.cmpfunc, (void *)&data);
- for (i=1; i<RARRAY_LEN(result); i+=2) {
- RARRAY_PTR(result)[i/2] = RARRAY_PTR(result)[i];
- }
+ RARRAY_PTR_USE(result, ptr, {
+ ruby_qsort(ptr,
+ RARRAY_LEN(result)/2,
+ sizeof(VALUE)*2,
+ data.cmpfunc, (void *)&data);
+ for (i=1; i<RARRAY_LEN(result); i+=2) {
+ ptr[i/2] = ptr[i];
+ }
+ });
rb_ary_resize(result, RARRAY_LEN(result)/2);
}
else {
- ruby_qsort(RARRAY_PTR(result), RARRAY_LEN(result), sizeof(VALUE),
- data.cmpfunc, (void *)&data);
+ RARRAY_PTR_USE(result, ptr, {
+ ruby_qsort(ptr, RARRAY_LEN(result), sizeof(VALUE),
+ data.cmpfunc, (void *)&data);
+ });
}
if (rev) {
rb_ary_reverse(result);
}
- *((VALUE *)&RBASIC(result)->klass) = rb_cArray;
+ RBASIC_SET_CLASS(result, rb_cArray);
return result;
}
@@ -1442,6 +1547,7 @@ rb_nmin_run(VALUE obj, VALUE num, int by, int rev, int ary)
/*
* call-seq:
* enum.one? [{ |obj| block }] -> true or false
+ * enum.one?(pattern) -> true or false
*
* Passes each element of the collection to the given block. The method
* returns <code>true</code> if the block returns <code>true</code>
@@ -1449,19 +1555,26 @@ rb_nmin_run(VALUE obj, VALUE num, int by, int rev, int ary)
* <code>true</code> only if exactly one of the collection members is
* true.
*
+ * If instead a pattern is supplied, the method returns whether
+ * <code>pattern === element</code> for exactly one collection member.
+ *
* %w{ant bear cat}.one? { |word| word.length == 4 } #=> true
* %w{ant bear cat}.one? { |word| word.length > 4 } #=> false
* %w{ant bear cat}.one? { |word| word.length < 4 } #=> false
+ * %w{ant bear cat}.one?(/t/) #=> false
* [ nil, true, 99 ].one? #=> false
* [ nil, true, false ].one? #=> true
+ * [ nil, true, 99 ].one?(Integer) #=> true
+ * [].one? #=> false
*
*/
static VALUE
-enum_one(VALUE obj)
+enum_one(int argc, VALUE *argv, VALUE obj)
{
- struct MEMO *memo = MEMO_NEW(Qundef, 0, 0);
+ struct MEMO *memo = MEMO_ENUM_NEW(Qundef);
VALUE result;
+ WARN_UNUSED_BLOCK(argc);
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(one), (VALUE)memo);
result = memo->v1;
if (result == Qundef) return Qfalse;
@@ -1480,23 +1593,31 @@ DEFINE_ENUMFUNCS(none)
/*
* call-seq:
* enum.none? [{ |obj| block }] -> true or false
+ * enum.none?(pattern) -> true or false
*
* Passes each element of the collection to the given block. The method
* returns <code>true</code> if the block never returns <code>true</code>
* for all elements. If the block is not given, <code>none?</code> will return
* <code>true</code> only if none of the collection members is true.
*
+ * If instead a pattern is supplied, the method returns whether
+ * <code>pattern === element</code> for none of the collection members.
+ *
* %w{ant bear cat}.none? { |word| word.length == 5 } #=> true
* %w{ant bear cat}.none? { |word| word.length >= 4 } #=> false
+ * %w{ant bear cat}.none?(/d/) #=> true
+ * [1, 3.14, 42].none?(Float) #=> false
* [].none? #=> true
* [nil].none? #=> true
* [nil, false].none? #=> true
* [nil, false, true].none? #=> false
*/
static VALUE
-enum_none(VALUE obj)
+enum_none(int argc, VALUE *argv, VALUE obj)
{
- struct MEMO *memo = MEMO_NEW(Qtrue, 0, 0);
+ struct MEMO *memo = MEMO_ENUM_NEW(Qtrue);
+
+ WARN_UNUSED_BLOCK(argc);
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(none), (VALUE)memo);
return memo->v1;
}
@@ -1561,11 +1682,12 @@ min_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
* a.min { |a, b| a.length <=> b.length } #=> "dog"
*
* If the +n+ argument is given, minimum +n+ elements are returned
- * as an array.
+ * as a sorted array.
*
* a = %w[albatross dog horse]
* a.min(2) #=> ["albatross", "dog"]
* a.min(2) {|a, b| a.length <=> b.length } #=> ["dog", "horse"]
+ * [5, 1, 3, 4, 2].min(3) #=> [1, 2, 3]
*/
static VALUE
@@ -1576,9 +1698,7 @@ enum_min(int argc, VALUE *argv, VALUE obj)
VALUE result;
VALUE num;
- rb_scan_args(argc, argv, "01", &num);
-
- if (!NIL_P(num))
+ if (rb_check_arity(argc, 0, 1) && !NIL_P(num = argv[0]))
return rb_nmin_run(obj, num, 0, 0, 0);
m->min = Qundef;
@@ -1654,11 +1774,12 @@ max_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
* a.max { |a, b| a.length <=> b.length } #=> "albatross"
*
* If the +n+ argument is given, maximum +n+ elements are returned
- * as an array.
+ * as an array, sorted in descending order.
*
* a = %w[albatross dog horse]
* a.max(2) #=> ["horse", "dog"]
* a.max(2) {|a, b| a.length <=> b.length } #=> ["albatross", "horse"]
+ * [5, 1, 3, 4, 2].max(3) #=> [5, 4, 3]
*/
static VALUE
@@ -1669,9 +1790,7 @@ enum_max(int argc, VALUE *argv, VALUE obj)
VALUE result;
VALUE num;
- rb_scan_args(argc, argv, "01", &num);
-
- if (!NIL_P(num))
+ if (rb_check_arity(argc, 0, 1) && !NIL_P(num = argv[0]))
return rb_nmin_run(obj, num, 0, 1, 0);
m->max = Qundef;
@@ -1843,17 +1962,18 @@ 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 = rb_yield(i);
+ v = enum_yield(argc, i);
if (memo->v1 == Qundef) {
MEMO_V1_SET(memo, v);
MEMO_V2_SET(memo, i);
}
- else if (rb_cmpint(rb_funcall(v, id_cmp, 1, memo->v1), v, memo->v1) < 0) {
+ else if (OPTIMIZED_CMP(v, memo->v1, cmp_opt) < 0) {
MEMO_V1_SET(memo, v);
MEMO_V2_SET(memo, i);
}
@@ -1876,7 +1996,8 @@ min_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
* a.min_by { |x| x.length } #=> "dog"
*
* If the +n+ argument is given, minimum +n+ elements are returned
- * as an array.
+ * as an array. These +n+ elements are sorted by the value from the
+ * given block.
*
* a = %w[albatross dog horse]
* p a.min_by(2) {|x| x.length } #=> ["dog", "horse"]
@@ -1888,11 +2009,11 @@ enum_min_by(int argc, VALUE *argv, VALUE obj)
struct MEMO *memo;
VALUE num;
- rb_scan_args(argc, argv, "01", &num);
+ rb_check_arity(argc, 0, 1);
RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);
- if (!NIL_P(num))
+ if (argc && !NIL_P(num = argv[0]))
return rb_nmin_run(obj, num, 1, 0, 0);
memo = MEMO_NEW(Qundef, Qnil, 0);
@@ -1903,17 +2024,18 @@ 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 = rb_yield(i);
+ v = enum_yield(argc, i);
if (memo->v1 == Qundef) {
MEMO_V1_SET(memo, v);
MEMO_V2_SET(memo, i);
}
- else if (rb_cmpint(rb_funcall(v, id_cmp, 1, memo->v1), v, memo->v1) > 0) {
+ else if (OPTIMIZED_CMP(v, memo->v1, cmp_opt) > 0) {
MEMO_V1_SET(memo, v);
MEMO_V2_SET(memo, i);
}
@@ -1935,8 +2057,9 @@ max_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
* a = %w(albatross dog horse)
* a.max_by { |x| x.length } #=> "albatross"
*
- * If the +n+ argument is given, minimum +n+ elements are returned
- * as an array.
+ * If the +n+ argument is given, maximum +n+ elements are returned
+ * as an array. These +n+ elements are sorted by the value from the
+ * given block, in descending order.
*
* a = %w[albatross dog horse]
* a.max_by(2) {|x| x.length } #=> ["albatross", "horse"]
@@ -1993,11 +2116,11 @@ enum_max_by(int argc, VALUE *argv, VALUE obj)
struct MEMO *memo;
VALUE num;
- rb_scan_args(argc, argv, "01", &num);
+ rb_check_arity(argc, 0, 1);
RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);
- if (!NIL_P(num))
+ if (argc && !NIL_P(num = argv[0]))
return rb_nmin_run(obj, num, 1, 1, 0);
memo = MEMO_NEW(Qundef, Qnil, 0);
@@ -2017,6 +2140,8 @@ 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;
@@ -2024,11 +2149,11 @@ minmax_by_i_update(VALUE v1, VALUE v2, VALUE i1, VALUE i2, struct minmax_by_t *m
memo->max = i2;
}
else {
- if (rb_cmpint(rb_funcall(v1, id_cmp, 1, memo->min_bv), v1, memo->min_bv) < 0) {
+ if (OPTIMIZED_CMP(v1, memo->min_bv, cmp_opt) < 0) {
memo->min_bv = v1;
memo->min = i1;
}
- if (rb_cmpint(rb_funcall(v2, id_cmp, 1, memo->max_bv), v2, memo->max_bv) > 0) {
+ if (OPTIMIZED_CMP(v2, memo->max_bv, cmp_opt) > 0) {
memo->max_bv = v2;
memo->max = i2;
}
@@ -2038,13 +2163,14 @@ minmax_by_i_update(VALUE v1, VALUE v2, VALUE i1, VALUE i2, struct minmax_by_t *m
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;
ENUM_WANT_SVALUE();
- vi = rb_yield(i);
+ vi = enum_yield(argc, i);
if (memo->last_bv == Qundef) {
memo->last_bv = vi;
@@ -2055,7 +2181,7 @@ minmax_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
j = memo->last;
memo->last_bv = Qundef;
- n = rb_cmpint(rb_funcall(vj, id_cmp, 1, vi), vj, vi);
+ n = OPTIMIZED_CMP(vj, vi, cmp_opt);
if (n == 0) {
i = j;
vi = vj;
@@ -2150,9 +2276,11 @@ enum_member(VALUE obj, VALUE val)
static VALUE
each_with_index_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memo))
{
- long n = MEMO_CAST(memo)->u3.cnt++;
+ struct MEMO *m = MEMO_CAST(memo);
+ VALUE n = imemo_count_value(m);
- return rb_yield_values(2, rb_enum_values_pack(argc, argv), INT2NUM(n));
+ imemo_count_up(m);
+ return rb_yield_values(2, rb_enum_values_pack(argc, argv), n);
}
/*
@@ -2196,27 +2324,33 @@ enum_each_with_index(int argc, VALUE *argv, VALUE obj)
*
* If no block is given, an enumerator is returned instead.
*
- * (1..3).reverse_each { |v| p v }
+ * (1..3).reverse_each { |v| p v }
*
- * produces:
+ * produces:
*
- * 3
- * 2
- * 1
+ * 3
+ * 2
+ * 1
*/
static VALUE
enum_reverse_each(int argc, VALUE *argv, VALUE obj)
{
VALUE ary;
- long i;
+ long len;
RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);
ary = enum_to_a(argc, argv, obj);
- for (i = RARRAY_LEN(ary); --i >= 0; ) {
- rb_yield(RARRAY_AREF(ary, i));
+ len = RARRAY_LEN(ary);
+ while (len--) {
+ long nlen;
+ rb_yield(RARRAY_AREF(ary, len));
+ nlen = RARRAY_LEN(ary);
+ if (nlen < len) {
+ len = nlen;
+ }
}
return obj;
@@ -2227,7 +2361,7 @@ static VALUE
each_val_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, p))
{
ENUM_WANT_SVALUE();
- rb_yield(i);
+ enum_yield(argc, i);
return Qnil;
}
@@ -2268,6 +2402,22 @@ enum_each_entry(int argc, VALUE *argv, VALUE obj)
return obj;
}
+static VALUE
+add_int(VALUE x, long n)
+{
+ const VALUE y = LONG2NUM(n);
+ if (RB_INTEGER_TYPE_P(x)) return rb_int_plus(x, y);
+ return rb_funcallv(x, '+', 1, &y);
+}
+
+static VALUE
+div_int(VALUE x, long n)
+{
+ const VALUE y = LONG2NUM(n);
+ if (RB_INTEGER_TYPE_P(x)) return rb_int_idiv(x, y);
+ return rb_funcallv(x, id_div, 1, &y);
+}
+
#define dont_recycle_block_arg(arity) ((arity) == 1 || (arity) < 0)
static VALUE
@@ -2305,8 +2455,8 @@ enum_each_slice_size(VALUE obj, VALUE args, VALUE eobj)
size = enum_size(obj, 0, 0);
if (size == Qnil) return Qnil;
- n = rb_funcall(size, '+', 1, LONG2NUM(slice_size-1));
- return rb_funcall(n, id_div, 1, LONG2FIX(slice_size));
+ n = add_int(size, slice_size-1);
+ return div_int(n, slice_size);
}
/*
@@ -2371,6 +2521,8 @@ 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));
if (cons_size <= 0) rb_raise(rb_eArgError, "invalid size");
@@ -2378,8 +2530,8 @@ enum_each_cons_size(VALUE obj, VALUE args, VALUE eobj)
size = enum_size(obj, 0, 0);
if (size == Qnil) return Qnil;
- n = rb_funcall(size, '+', 1, LONG2NUM(1 - cons_size));
- return (rb_cmpint(rb_funcall(n, id_cmp, 1, LONG2FIX(0)), n, LONG2FIX(0)) == -1) ? LONG2FIX(0) : n;
+ n = add_int(size, 1 - cons_size);
+ return (OPTIMIZED_CMP(n, zero, cmp_opt) == -1) ? zero : n;
}
/*
@@ -2474,7 +2626,7 @@ zip_ary(RB_BLOCK_CALL_FUNC_ARGLIST(val, memoval))
}
}
if (NIL_P(result)) {
- rb_yield(tmp);
+ enum_yield_array(tmp);
}
else {
rb_ary_push(result, tmp);
@@ -2525,7 +2677,7 @@ zip_i(RB_BLOCK_CALL_FUNC_ARGLIST(val, memoval))
}
}
if (NIL_P(result)) {
- rb_yield(tmp);
+ enum_yield_array(tmp);
}
else {
rb_ary_push(result, tmp);
@@ -2584,13 +2736,14 @@ enum_zip(int argc, VALUE *argv, VALUE obj)
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]));
}
- argv[i] = rb_funcall(argv[i], conv, 1, ID2SYM(id_each));
+ argv[i] = rb_funcallv(argv[i], conv, 1, &sym_each);
}
}
if (!rb_block_given_p()) {
@@ -2647,7 +2800,7 @@ enum_take(VALUE obj, VALUE n)
static VALUE
take_while_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
{
- if (!RTEST(enum_yield(argc, argv))) rb_iter_break();
+ if (!RTEST(rb_yield_values2(argc, argv))) rb_iter_break();
rb_ary_push(ary, rb_enum_values_pack(argc, argv));
return Qnil;
}
@@ -2727,7 +2880,7 @@ drop_while_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
struct MEMO *memo = MEMO_CAST(args);
ENUM_WANT_SVALUE();
- if (!memo->u3.state && !RTEST(rb_yield(i))) {
+ if (!memo->u3.state && !RTEST(enum_yield(argc, i))) {
memo->u3.state = TRUE;
}
if (memo->u3.state) {
@@ -2770,27 +2923,30 @@ cycle_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
{
ENUM_WANT_SVALUE();
- rb_ary_push(ary, i);
- rb_yield(i);
+ rb_ary_push(ary, argc > 1 ? i : rb_ary_new_from_values(argc, argv));
+ enum_yield(argc, i);
return Qnil;
}
static VALUE
enum_cycle_size(VALUE self, VALUE args, VALUE eobj)
{
- long mul;
+ long mul = 0;
VALUE n = Qnil;
- VALUE size = enum_size(self, args, 0);
-
- if (size == Qnil) return Qnil;
+ VALUE size;
if (args && (RARRAY_LEN(args) > 0)) {
n = RARRAY_AREF(args, 0);
+ if (!NIL_P(n)) mul = NUM2LONG(n);
}
- if (n == Qnil) return DBL2NUM(INFINITY);
- mul = NUM2LONG(n);
+
+ size = enum_size(self, args, 0);
+ if (NIL_P(size) || FIXNUM_ZERO_P(size)) return size;
+
+ if (NIL_P(n)) return DBL2NUM(HUGE_VAL);
if (mul <= 0) return INT2FIX(0);
- return rb_funcall(size, '*', 1, LONG2FIX(mul));
+ n = LONG2FIX(mul);
+ return rb_funcallv(size, '*', 1, &n);
}
/*
@@ -2821,10 +2977,10 @@ enum_cycle(int argc, VALUE *argv, VALUE obj)
VALUE nv = Qnil;
long n, i, len;
- rb_scan_args(argc, argv, "01", &nv);
+ rb_check_arity(argc, 0, 1);
RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_cycle_size);
- if (NIL_P(nv)) {
+ if (!argc || NIL_P(nv = argv[0])) {
n = -1;
}
else {
@@ -2838,7 +2994,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++) {
- rb_yield(RARRAY_AREF(ary, i));
+ enum_yield_array(RARRAY_AREF(ary, i));
}
}
return Qnil;
@@ -2861,18 +3017,21 @@ chunk_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _argp))
ENUM_WANT_SVALUE();
- v = rb_funcall(argp->categorize, id_call, 1, i);
+ v = rb_funcallv(argp->categorize, id_call, 1, &i);
if (v == alone) {
if (!NIL_P(argp->prev_value)) {
- rb_funcall(argp->yielder, id_lshift, 1, 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;
}
- rb_funcall(argp->yielder, id_lshift, 1, 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)) {
- rb_funcall(argp->yielder, id_lshift, 1, 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;
}
}
@@ -2889,7 +3048,8 @@ chunk_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _argp))
rb_ary_push(argp->prev_elts, i);
}
else {
- rb_funcall(argp->yielder, id_lshift, 1, 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);
}
@@ -2913,8 +3073,10 @@ 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))
- rb_funcall(memo->yielder, id_lshift, 1, rb_assoc_new(memo->prev_value, memo->prev_elts));
+ if (!NIL_P(memo->prev_elts)) {
+ arg = rb_assoc_new(memo->prev_value, memo->prev_elts);
+ rb_funcallv(memo->yielder, id_lshift, 1, &arg);
+ }
return Qnil;
}
@@ -3037,12 +3199,12 @@ slicebefore_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _argp))
ENUM_WANT_SVALUE();
if (!NIL_P(argp->sep_pat))
- header_p = rb_funcall(argp->sep_pat, id_eqq, 1, i);
+ header_p = rb_funcallv(argp->sep_pat, id_eqq, 1, &i);
else
- header_p = rb_funcall(argp->sep_pred, id_call, 1, i);
+ header_p = rb_funcallv(argp->sep_pred, id_call, 1, &i);
if (RTEST(header_p)) {
if (!NIL_P(argp->prev_elts))
- rb_funcall(argp->yielder, id_lshift, 1, argp->prev_elts);
+ rb_funcallv(argp->yielder, id_lshift, 1, &argp->prev_elts);
argp->prev_elts = rb_ary_new3(1, i);
}
else {
@@ -3071,7 +3233,7 @@ slicebefore_i(RB_BLOCK_CALL_FUNC_ARGLIST(yielder, enumerator))
rb_block_call(enumerable, id_each, 0, 0, slicebefore_ii, arg);
memo = MEMO_FOR(struct slicebefore_arg, arg);
if (!NIL_P(memo->prev_elts))
- rb_funcall(memo->yielder, id_lshift, 1, memo->prev_elts);
+ rb_funcallv(memo->yielder, id_lshift, 1, &memo->prev_elts);
return Qnil;
}
@@ -3207,9 +3369,10 @@ slicebefore_i(RB_BLOCK_CALL_FUNC_ARGLIST(yielder, enumerator))
*
* # split mails in mbox (slice before Unix From line after an empty line)
* open("mbox") { |f|
- * f.slice_before(emp: true) { |line, h|
- * prevemp = h[:emp]
- * h[:emp] = line == "\n"
+ * emp = true
+ * f.slice_before { |line|
+ * prevemp = emp
+ * emp = line == "\n"
* prevemp && line.start_with?("From ")
* }.each { |mail|
* mail.pop if mail.last == "\n"
@@ -3266,16 +3429,16 @@ sliceafter_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
}
if (NIL_P(memo->pred)) {
- split_p = RTEST(rb_funcall(memo->pat, id_eqq, 1, i));
+ split_p = RTEST(rb_funcallv(memo->pat, id_eqq, 1, &i));
UPDATE_MEMO;
}
else {
- split_p = RTEST(rb_funcall(memo->pred, id_call, 1, i));
+ split_p = RTEST(rb_funcallv(memo->pred, id_call, 1, &i));
UPDATE_MEMO;
}
if (split_p) {
- rb_funcall(memo->yielder, id_lshift, 1, memo->prev_elts);
+ rb_funcallv(memo->yielder, id_lshift, 1, &memo->prev_elts);
UPDATE_MEMO;
memo->prev_elts = Qnil;
}
@@ -3300,7 +3463,7 @@ sliceafter_i(RB_BLOCK_CALL_FUNC_ARGLIST(yielder, enumerator))
rb_block_call(enumerable, id_each, 0, 0, sliceafter_ii, arg);
memo = MEMO_FOR(struct sliceafter_arg, arg);
if (!NIL_P(memo->prev_elts))
- rb_funcall(memo->yielder, id_lshift, 1, memo->prev_elts);
+ rb_funcallv(memo->yielder, id_lshift, 1, &memo->prev_elts);
return Qnil;
}
@@ -3388,14 +3551,17 @@ slicewhen_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
memo->prev_elts = rb_ary_new3(1, i);
}
else {
- split_p = RTEST(rb_funcall(memo->pred, id_call, 2, memo->prev_elt, 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;
if (memo->inverted)
split_p = !split_p;
if (split_p) {
- rb_funcall(memo->yielder, id_lshift, 1, memo->prev_elts);
+ rb_funcallv(memo->yielder, id_lshift, 1, &memo->prev_elts);
UPDATE_MEMO;
memo->prev_elts = rb_ary_new3(1, i);
}
@@ -3428,7 +3594,7 @@ slicewhen_i(RB_BLOCK_CALL_FUNC_ARGLIST(yielder, enumerator))
rb_block_call(enumerable, id_each, 0, 0, slicewhen_ii, arg);
memo = MEMO_FOR(struct slicewhen_arg, arg);
if (!NIL_P(memo->prev_elts))
- rb_funcall(memo->yielder, id_lshift, 1, memo->prev_elts);
+ rb_funcallv(memo->yielder, id_lshift, 1, &memo->prev_elts);
return Qnil;
}
@@ -3644,8 +3810,11 @@ sum_iter(VALUE i, struct enum_sum_memo *memo)
}
}
else if (RB_FLOAT_TYPE_P(v)) {
- /* Kahan's compensated summation algorithm */
- double x, y, t;
+ /*
+ * Kahan-Babuska balancing compensated summation algorithm
+ * See http://link.springer.com/article/10.1007/s00607-005-0139-x
+ */
+ double x, t;
float_value:
if (RB_FLOAT_TYPE_P(i))
@@ -3662,14 +3831,35 @@ sum_iter(VALUE i, struct enum_sum_memo *memo)
goto some_value;
}
- y = x - c;
- t = f + y;
- c = (t - f) - y;
+ if (isnan(f)) return;
+ if (isnan(x)) {
+ memo->v = i;
+ memo->f = x;
+ return;
+ }
+ if (isinf(x)) {
+ if (isinf(f) && signbit(x) != signbit(f)) {
+ memo->f = NAN;
+ memo->v = DBL2NUM(f);
+ }
+ else {
+ memo->f = x;
+ memo->v = i;
+ }
+ return;
+ }
+ if (isinf(f)) return;
+
+ t = f + x;
+ if (fabs(f) >= fabs(x))
+ c += ((f - t) + x);
+ else
+ c += ((x - t) + f);
f = t;
}
else {
some_value:
- v = rb_funcall(v, idPLUS, 1, i);
+ v = rb_funcallv(v, idPLUS, 1, &i);
}
memo->v = v;
@@ -3760,11 +3950,8 @@ enum_sum(int argc, VALUE* argv, VALUE obj)
VALUE beg, end;
int excl;
- if (rb_scan_args(argc, argv, "01", &memo.v) == 0)
- memo.v = LONG2FIX(0);
-
+ memo.v = (rb_check_arity(argc, 0, 1) == 0) ? LONG2FIX(0) : argv[0];
memo.block_given = rb_block_given_p();
-
memo.n = 0;
memo.r = Qundef;
@@ -3788,7 +3975,7 @@ enum_sum(int argc, VALUE* argv, VALUE obj)
rb_block_call(obj, id_each, 0, 0, enum_sum_i, (VALUE)&memo);
if (memo.float_value) {
- return DBL2NUM(memo.f);
+ return DBL2NUM(memo.f + memo.c);
}
else {
if (memo.n != 0)
@@ -3809,6 +3996,7 @@ enum_sum(int argc, VALUE* argv, VALUE obj)
static VALUE
uniq_func(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
{
+ ENUM_WANT_SVALUE();
rb_hash_add_new_element(hash, i, i);
return Qnil;
}
@@ -3816,6 +4004,7 @@ uniq_func(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
static VALUE
uniq_iter(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
{
+ ENUM_WANT_SVALUE();
rb_hash_add_new_element(hash, rb_yield_values2(argc, argv), i);
return Qnil;
}
@@ -3877,6 +4066,7 @@ Init_Enumerable(void)
rb_define_method(rb_mEnumerable, "find_index", enum_find_index, -1);
rb_define_method(rb_mEnumerable, "find_all", enum_find_all, 0);
rb_define_method(rb_mEnumerable, "select", enum_find_all, 0);
+ rb_define_method(rb_mEnumerable, "filter", enum_find_all, 0);
rb_define_method(rb_mEnumerable, "reject", enum_reject, 0);
rb_define_method(rb_mEnumerable, "collect", enum_collect, 0);
rb_define_method(rb_mEnumerable, "map", enum_collect, 0);
@@ -3887,10 +4077,10 @@ Init_Enumerable(void)
rb_define_method(rb_mEnumerable, "partition", enum_partition, 0);
rb_define_method(rb_mEnumerable, "group_by", enum_group_by, 0);
rb_define_method(rb_mEnumerable, "first", enum_first, -1);
- rb_define_method(rb_mEnumerable, "all?", enum_all, 0);
- rb_define_method(rb_mEnumerable, "any?", enum_any, 0);
- rb_define_method(rb_mEnumerable, "one?", enum_one, 0);
- rb_define_method(rb_mEnumerable, "none?", enum_none, 0);
+ rb_define_method(rb_mEnumerable, "all?", enum_all, -1);
+ rb_define_method(rb_mEnumerable, "any?", enum_any, -1);
+ rb_define_method(rb_mEnumerable, "one?", enum_one, -1);
+ rb_define_method(rb_mEnumerable, "none?", enum_none, -1);
rb_define_method(rb_mEnumerable, "min", enum_min, -1);
rb_define_method(rb_mEnumerable, "max", enum_max, -1);
rb_define_method(rb_mEnumerable, "minmax", enum_minmax, 0);
@@ -3920,7 +4110,5 @@ Init_Enumerable(void)
rb_define_method(rb_mEnumerable, "uniq", enum_uniq, 0);
id_next = rb_intern("next");
- id_call = rb_intern("call");
- id_size = rb_intern("size");
id_div = rb_intern("div");
}
diff --git a/enumerator.c b/enumerator.c
index 9de08d7d2a..686dab0b20 100644
--- a/enumerator.c
+++ b/enumerator.c
@@ -12,7 +12,13 @@
************************************************/
+#include "ruby/ruby.h"
#include "internal.h"
+#include "id.h"
+
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
/*
* Document-class: Enumerator
@@ -101,11 +107,18 @@
*
*/
VALUE rb_cEnumerator;
-VALUE rb_cLazy;
-static ID id_rewind, id_each, id_new, id_initialize, id_yield, id_call, id_size, id_to_enum;
-static ID id_eqq, id_next, id_result, id_lazy, id_receiver, id_arguments, id_memo, id_method, id_force;
+static VALUE rb_cLazy;
+static ID id_rewind, id_new, id_to_enum;
+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;
+#define id_call idCall
+#define id_each idEach
+#define id_eqq idEqq
+#define id_initialize idInitialize
+#define id_size idSize
+
VALUE rb_eStopIteration;
struct enumerator {
@@ -149,6 +162,15 @@ struct proc_entry {
static VALUE generator_allocate(VALUE klass);
static VALUE generator_init(VALUE obj, VALUE proc);
+static VALUE rb_cEnumChain;
+
+struct enum_chain {
+ VALUE enums;
+ long pos;
+};
+
+VALUE rb_cArithSeq;
+
/*
* Enumerator
*/
@@ -390,7 +412,7 @@ enumerator_initialize(int argc, VALUE *argv, VALUE obj)
recv = generator_init(generator_allocate(rb_cGenerator), rb_block_proc());
if (argc) {
if (NIL_P(argv[0]) || rb_respond_to(argv[0], id_call) ||
- (RB_TYPE_P(argv[0], T_FLOAT) && RFLOAT_VALUE(argv[0]) == INFINITY)) {
+ (RB_TYPE_P(argv[0], T_FLOAT) && RFLOAT_VALUE(argv[0]) == HUGE_VAL)) {
size = argv[0];
}
else {
@@ -536,6 +558,8 @@ enumerator_each(int argc, VALUE *argv, VALUE obj)
args = rb_ary_new4(argc, argv);
}
e->args = args;
+ e->size = Qnil;
+ e->size_fn = 0;
}
if (!rb_block_given_p()) return obj;
return enumerator_block_call(obj, 0, obj);
@@ -580,12 +604,9 @@ enumerator_with_index(int argc, VALUE *argv, VALUE obj)
{
VALUE memo;
- rb_scan_args(argc, argv, "01", &memo);
+ rb_check_arity(argc, 0, 1);
RETURN_SIZED_ENUMERATOR(obj, argc, argv, enumerator_enum_size);
- if (NIL_P(memo))
- memo = INT2FIX(0);
- else
- memo = rb_to_int(memo);
+ memo = (!argc || NIL_P(memo = argv[0])) ? INT2FIX(0) : rb_to_int(memo);
return enumerator_block_call(obj, enumerator_with_index_i, (VALUE)MEMO_NEW(memo, 0, 0));
}
@@ -1037,6 +1058,22 @@ inspect_enumerator(VALUE obj, VALUE dummy, int recur)
return str;
}
+static int
+key_symbol_p(VALUE key, VALUE val, VALUE arg)
+{
+ if (SYMBOL_P(key)) return ST_CONTINUE;
+ *(int *)arg = FALSE;
+ return ST_STOP;
+}
+
+static int
+kwd_append(VALUE key, VALUE val, VALUE str)
+{
+ if (!SYMBOL_P(key)) rb_raise(rb_eRuntimeError, "non-symbol key inserted");
+ rb_str_catf(str, "% "PRIsVALUE": %"PRIsVALUE", ", key, val);
+ return ST_CONTINUE;
+}
+
static VALUE
append_method(VALUE obj, VALUE str, ID default_method, VALUE default_args)
{
@@ -1064,15 +1101,28 @@ append_method(VALUE obj, VALUE str, ID default_method, VALUE default_args)
const VALUE *argv = RARRAY_CONST_PTR(eargs); /* WB: no new reference */
if (argc > 0) {
+ VALUE kwds = Qnil;
+
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];
+ }
+
while (argc--) {
VALUE arg = *argv++;
rb_str_append(str, rb_inspect(arg));
- rb_str_buf_cat2(str, argc > 0 ? ", " : ")");
+ rb_str_buf_cat2(str, ", ");
OBJ_INFECT(str, arg);
}
+ 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, ")");
}
}
@@ -1119,11 +1169,11 @@ enumerator_size(VALUE obj)
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 = entry->fn->size;
- if (!size) {
+ lazyenum_size_func *size_fn = entry->fn->size;
+ if (!size_fn) {
return Qnil;
}
- receiver = (*size)(proc, receiver);
+ receiver = (*size_fn)(proc, receiver);
}
return receiver;
}
@@ -1229,9 +1279,12 @@ yielder_yield(VALUE obj, VALUE args)
/* :nodoc: */
static VALUE
-yielder_yield_push(VALUE obj, VALUE args)
+yielder_yield_push(VALUE obj, VALUE arg)
{
- yielder_yield(obj, args);
+ struct yielder *ptr = yielder_ptr(obj);
+
+ rb_proc_call_with_block(ptr->proc, 1, &arg, Qnil);
+
return obj;
}
@@ -1448,6 +1501,8 @@ lazy_init_block_i(RB_BLOCK_CALL_FUNC_ARGLIST(val, m))
#define LAZY_MEMO_PACKED_P(memo) ((memo)->memo_flags & LAZY_MEMO_PACKED)
#define LAZY_MEMO_SET_BREAK(memo) ((memo)->memo_flags |= LAZY_MEMO_BREAK)
#define LAZY_MEMO_SET_VALUE(memo, value) MEMO_V2_SET(memo, value)
+#define LAZY_MEMO_SET_PACKED(memo) ((memo)->memo_flags |= LAZY_MEMO_PACKED)
+#define LAZY_MEMO_RESET_PACKED(memo) ((memo)->memo_flags &= ~LAZY_MEMO_PACKED)
static VALUE
lazy_init_yielder(VALUE val, VALUE m, int argc, VALUE *argv)
@@ -1472,7 +1527,7 @@ lazy_init_yielder(VALUE val, VALUE m, int argc, VALUE *argv)
}
if (cont) {
- rb_funcall2(yielder, id_yield, 1, &(result->memo_value));
+ rb_funcall2(yielder, idLTLT, 1, &(result->memo_value));
}
if (LAZY_MEMO_BREAK_P(result)) {
rb_iter_break();
@@ -1568,6 +1623,20 @@ lazy_initialize(int argc, VALUE *argv, VALUE self)
return self;
}
+#if 0 /* for RDoc */
+/*
+ * call-seq:
+ * lazy.to_a -> array
+ * lazy.force -> array
+ *
+ * Expands +lazy+ enumerator to an array.
+ * See Enumerable#to_a.
+ */
+static VALUE lazy_to_a(VALUE self)
+{
+}
+#endif
+
static void
lazy_set_args(VALUE lazy, VALUE args)
{
@@ -1743,6 +1812,7 @@ lazy_map_proc(VALUE proc_entry, struct MEMO *result, VALUE memos, long memo_inde
{
VALUE value = lazyenum_yield_values(proc_entry, result);
LAZY_MEMO_SET_VALUE(result, value);
+ LAZY_MEMO_RESET_PACKED(result);
return result;
}
@@ -1769,7 +1839,9 @@ lazy_map(VALUE obj)
static VALUE
lazy_flat_map_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, yielder))
{
- return rb_funcallv(yielder, id_yield, argc, argv);
+ VALUE arg = rb_enum_values_pack(argc, argv);
+
+ return rb_funcallv(yielder, idLTLT, 1, &arg);
}
static VALUE
@@ -1784,12 +1856,12 @@ lazy_flat_map_to_ary(VALUE obj, VALUE yielder)
{
VALUE ary = rb_check_array_type(obj);
if (NIL_P(ary)) {
- rb_funcall(yielder, id_yield, 1, obj);
+ rb_funcall(yielder, idLTLT, 1, obj);
}
else {
long i;
for (i = 0; i < RARRAY_LEN(ary); i++) {
- rb_funcall(yielder, id_yield, 1, RARRAY_AREF(ary, i));
+ rb_funcall(yielder, idLTLT, 1, RARRAY_AREF(ary, i));
}
}
return Qnil;
@@ -1802,7 +1874,7 @@ lazy_flat_map_proc(RB_BLOCK_CALL_FUNC_ARGLIST(val, m))
if (RB_TYPE_P(result, T_ARRAY)) {
long i;
for (i = 0; i < RARRAY_LEN(result); i++) {
- rb_funcall(argv[0], id_yield, 1, RARRAY_AREF(result, i));
+ rb_funcall(argv[0], idLTLT, 1, RARRAY_AREF(result, i));
}
}
else {
@@ -1913,6 +1985,7 @@ lazy_grep_iter_proc(VALUE proc_entry, struct MEMO *result, VALUE memos, long mem
if (!RTEST(chain)) return 0;
value = rb_proc_call_with_block(entry->proc, 1, &(result->memo_value), Qnil);
LAZY_MEMO_SET_VALUE(result, value);
+ LAZY_MEMO_RESET_PACKED(result);
return result;
}
@@ -1933,38 +2006,43 @@ lazy_grep(VALUE obj, VALUE pattern)
return lazy_add_method(obj, 0, 0, pattern, rb_ary_new3(1, pattern), funcs);
}
-static VALUE
-lazy_grep_v_func(RB_BLOCK_CALL_FUNC_ARGLIST(val, m))
+static struct MEMO *
+lazy_grep_v_proc(VALUE proc_entry, struct MEMO *result, VALUE memos, long memo_index)
{
- VALUE i = rb_enum_values_pack(argc - 1, argv + 1);
- VALUE result = rb_funcall(m, id_eqq, 1, i);
-
- if (!RTEST(result)) {
- rb_funcall(argv[0], id_yield, 1, i);
- }
- return Qnil;
+ struct proc_entry *entry = proc_entry_ptr(proc_entry);
+ VALUE chain = rb_funcall(entry->memo, id_eqq, 1, result->memo_value);
+ if (RTEST(chain)) return 0;
+ return result;
}
-static VALUE
-lazy_grep_v_iter(RB_BLOCK_CALL_FUNC_ARGLIST(val, m))
+static struct MEMO *
+lazy_grep_v_iter_proc(VALUE proc_entry, struct MEMO *result, VALUE memos, long memo_index)
{
- VALUE i = rb_enum_values_pack(argc - 1, argv + 1);
- VALUE result = rb_funcall(m, id_eqq, 1, i);
+ struct proc_entry *entry = proc_entry_ptr(proc_entry);
+ VALUE value, chain = rb_funcall(entry->memo, id_eqq, 1, result->memo_value);
- if (!RTEST(result)) {
- rb_funcall(argv[0], id_yield, 1, rb_yield(i));
- }
- return Qnil;
+ if (RTEST(chain)) return 0;
+ value = rb_proc_call_with_block(entry->proc, 1, &(result->memo_value), Qnil);
+ LAZY_MEMO_SET_VALUE(result, value);
+ LAZY_MEMO_RESET_PACKED(result);
+
+ return result;
}
+static const lazyenum_funcs lazy_grep_v_iter_funcs = {
+ lazy_grep_v_iter_proc, 0,
+};
+
+static const lazyenum_funcs lazy_grep_v_funcs = {
+ lazy_grep_v_proc, 0,
+};
+
static VALUE
lazy_grep_v(VALUE obj, VALUE pattern)
{
- return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
- rb_block_given_p() ?
- lazy_grep_v_iter : lazy_grep_v_func,
- pattern),
- rb_ary_new3(1, pattern), 0);
+ const lazyenum_funcs *const funcs = rb_block_given_p() ?
+ &lazy_grep_v_iter_funcs : &lazy_grep_v_funcs;
+ return lazy_add_method(obj, 0, 0, pattern, rb_ary_new3(1, pattern), funcs);
}
static VALUE
@@ -1994,7 +2072,7 @@ lazy_zip_arrays_func(RB_BLOCK_CALL_FUNC_ARGLIST(val, arrays))
for (i = 0; i < RARRAY_LEN(arrays); i++) {
rb_ary_push(ary, rb_ary_entry(RARRAY_AREF(arrays, i), count));
}
- rb_funcall(yielder, id_yield, 1, ary);
+ rb_funcall(yielder, idLTLT, 1, ary);
rb_ivar_set(yielder, id_memo, LONG2NUM(++count));
return Qnil;
}
@@ -2027,7 +2105,7 @@ lazy_zip_func(RB_BLOCK_CALL_FUNC_ARGLIST(val, zip_args))
rb_eStopIteration, (VALUE)0);
rb_ary_push(ary, v);
}
- rb_funcall(yielder, id_yield, 1, ary);
+ rb_funcall(yielder, idLTLT, 1, ary);
return Qnil;
}
@@ -2228,39 +2306,49 @@ lazy_drop_while(VALUE obj)
return lazy_add_method(obj, 0, 0, Qfalse, Qnil, &lazy_drop_while_funcs);
}
-static VALUE
-lazy_uniq_i(VALUE i, VALUE hash, int argc, const VALUE *argv, VALUE yielder)
+static int
+lazy_uniq_check(VALUE chain, VALUE memos, long memo_index)
{
- if (rb_hash_add_new_element(hash, i, Qfalse))
- return Qnil;
- return rb_funcallv(yielder, id_yield, argc, argv);
+ VALUE hash = rb_ary_entry(memos, memo_index);
+
+ if (NIL_P(hash)) {
+ hash = rb_obj_hide(rb_hash_new());
+ rb_ary_store(memos, memo_index, hash);
+ }
+
+ return rb_hash_add_new_element(hash, chain, Qfalse);
}
-static VALUE
-lazy_uniq_func(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
+static struct MEMO *
+lazy_uniq_proc(VALUE proc_entry, struct MEMO *result, VALUE memos, long memo_index)
{
- VALUE yielder = (--argc, *argv++);
- i = rb_enum_values_pack(argc, argv);
- return lazy_uniq_i(i, hash, argc, argv, yielder);
+ if (lazy_uniq_check(result->memo_value, memos, memo_index)) return 0;
+ return result;
}
-static VALUE
-lazy_uniq_iter(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
+static struct MEMO *
+lazy_uniq_iter_proc(VALUE proc_entry, struct MEMO *result, VALUE memos, long memo_index)
{
- VALUE yielder = (--argc, *argv++);
- i = rb_yield_values2(argc, argv);
- return lazy_uniq_i(i, hash, argc, argv, yielder);
+ VALUE chain = lazyenum_yield(proc_entry, result);
+
+ if (lazy_uniq_check(chain, memos, memo_index)) return 0;
+ return result;
}
+static const lazyenum_funcs lazy_uniq_iter_funcs = {
+ lazy_uniq_iter_proc, 0,
+};
+
+static const lazyenum_funcs lazy_uniq_funcs = {
+ lazy_uniq_proc, 0,
+};
+
static VALUE
lazy_uniq(VALUE obj)
{
- rb_block_call_func *const func =
- rb_block_given_p() ? lazy_uniq_iter : lazy_uniq_func;
- VALUE hash = rb_obj_hide(rb_hash_new());
- return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
- func, hash),
- 0, 0);
+ const lazyenum_funcs *const funcs =
+ rb_block_given_p() ? &lazy_uniq_iter_funcs : &lazy_uniq_funcs;
+ return lazy_add_method(obj, 0, 0, Qnil, Qnil, funcs);
}
static VALUE
@@ -2328,6 +2416,973 @@ stop_result(VALUE self)
return rb_attr_get(self, id_result);
}
+/*
+ * Document-class: Enumerator::Chain
+ *
+ * Enumerator::Chain is a subclass of Enumerator, which represents a
+ * chain of enumerables that works as a single enumerator.
+ *
+ * This type of objects can be created by Enumerable#chain and
+ * Enumerator#+.
+ */
+
+static void
+enum_chain_mark(void *p)
+{
+ struct enum_chain *ptr = p;
+ rb_gc_mark(ptr->enums);
+}
+
+#define enum_chain_free RUBY_TYPED_DEFAULT_FREE
+
+static size_t
+enum_chain_memsize(const void *p)
+{
+ return sizeof(struct enum_chain);
+}
+
+static const rb_data_type_t enum_chain_data_type = {
+ "chain",
+ {
+ enum_chain_mark,
+ enum_chain_free,
+ enum_chain_memsize,
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+};
+
+static struct enum_chain *
+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) {
+ rb_raise(rb_eArgError, "uninitialized chain");
+ }
+ return ptr;
+}
+
+/* :nodoc: */
+static VALUE
+enum_chain_allocate(VALUE klass)
+{
+ struct enum_chain *ptr;
+ VALUE obj;
+
+ obj = TypedData_Make_Struct(klass, struct enum_chain, &enum_chain_data_type, ptr);
+ ptr->enums = Qundef;
+ ptr->pos = -1;
+
+ return obj;
+}
+
+/*
+ * call-seq:
+ * Enumerator::Chain.new(*enums) -> enum
+ *
+ * Generates a new enumerator object that iterates over the elements
+ * of given enumerable objects in sequence.
+ *
+ * e = Enumerator::Chain.new(1..3, [4, 5])
+ * e.to_a #=> [1, 2, 3, 4, 5]
+ * e.size #=> 5
+ */
+static VALUE
+enum_chain_initialize(VALUE obj, VALUE enums)
+{
+ struct enum_chain *ptr;
+
+ rb_check_frozen(obj);
+ TypedData_Get_Struct(obj, struct enum_chain, &enum_chain_data_type, ptr);
+
+ if (!ptr) rb_raise(rb_eArgError, "unallocated chain");
+
+ ptr->enums = rb_obj_freeze(enums);
+ ptr->pos = -1;
+
+ return obj;
+}
+
+/* :nodoc: */
+static VALUE
+enum_chain_init_copy(VALUE obj, VALUE orig)
+{
+ struct enum_chain *ptr0, *ptr1;
+
+ if (!OBJ_INIT_COPY(obj, orig)) return obj;
+ ptr0 = enum_chain_ptr(orig);
+
+ TypedData_Get_Struct(obj, struct enum_chain, &enum_chain_data_type, ptr1);
+
+ if (!ptr1) rb_raise(rb_eArgError, "unallocated chain");
+
+ ptr1->enums = ptr0->enums;
+ ptr1->pos = ptr0->pos;
+
+ return obj;
+}
+
+static VALUE
+enum_chain_total_size(VALUE enums)
+{
+ VALUE total = INT2FIX(0);
+ 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 chain calculated by
+ * summing up the size of each enumerable in the chain. If any of the
+ * enumerables reports its size as nil or Float::INFINITY, that value
+ * is returned as the total size.
+ */
+static VALUE
+enum_chain_size(VALUE obj)
+{
+ return enum_chain_total_size(enum_chain_ptr(obj)->enums);
+}
+
+static VALUE
+enum_chain_enum_size(VALUE obj, VALUE args, VALUE eobj)
+{
+ return enum_chain_size(obj);
+}
+
+static VALUE
+enum_chain_yield_block(VALUE arg, VALUE block, int argc, VALUE *argv)
+{
+ return rb_funcallv(block, id_call, argc, argv);
+}
+
+static VALUE
+enum_chain_enum_no_size(VALUE obj, VALUE args, VALUE eobj)
+{
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * obj.each(*args) { |...| ... } -> obj
+ * obj.each(*args) -> enumerator
+ *
+ * Iterates over the elements of the first enumerable by calling the
+ * "each" 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.
+ */
+static VALUE
+enum_chain_each(int argc, VALUE *argv, VALUE obj)
+{
+ VALUE enums, block;
+ struct enum_chain *objptr;
+ long i;
+
+ RETURN_SIZED_ENUMERATOR(obj, argc, argv, argc > 0 ? enum_chain_enum_no_size : enum_chain_enum_size);
+
+ objptr = enum_chain_ptr(obj);
+ enums = objptr->enums;
+ block = rb_block_proc();
+
+
+ for (i = 0; i < RARRAY_LEN(enums); i++) {
+ objptr->pos = i;
+ rb_block_call(RARRAY_AREF(enums, i), id_each, argc, argv, enum_chain_yield_block, block);
+ }
+
+ return obj;
+}
+
+/*
+ * call-seq:
+ * obj.rewind -> obj
+ *
+ * Rewinds the enumerator chain 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_chain_rewind(VALUE obj)
+{
+ struct enum_chain *objptr = enum_chain_ptr(obj);
+ VALUE enums = objptr->enums;
+ long i;
+
+ for (i = objptr->pos; 0 <= i && i < RARRAY_LEN(enums); objptr->pos = --i) {
+ rb_check_funcall(RARRAY_AREF(enums, i), id_rewind, 0, 0);
+ }
+
+ return obj;
+}
+
+static VALUE
+inspect_enum_chain(VALUE obj, VALUE dummy, int recur)
+{
+ VALUE klass = rb_obj_class(obj);
+ struct enum_chain *ptr;
+
+ TypedData_Get_Struct(obj, struct enum_chain, &enum_chain_data_type, ptr);
+
+ if (!ptr || ptr->enums == Qundef) {
+ 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 enumerator chain.
+ */
+static VALUE
+enum_chain_inspect(VALUE obj)
+{
+ return rb_exec_recursive(inspect_enum_chain, obj, 0);
+}
+
+/*
+ * call-seq:
+ * e.chain(*enums) -> enumerator
+ *
+ * Returns an enumerator object generated from this enumerator and
+ * given enumerables.
+ *
+ * e = (1..3).chain([4, 5])
+ * e.to_a #=> [1, 2, 3, 4, 5]
+ */
+static VALUE
+enum_chain(int argc, VALUE *argv, VALUE obj)
+{
+ VALUE enums = rb_ary_new_from_values(1, &obj);
+ rb_ary_cat(enums, argv, argc);
+
+ return enum_chain_initialize(enum_chain_allocate(rb_cEnumChain), enums);
+}
+
+/*
+ * call-seq:
+ * e + enum -> enumerator
+ *
+ * Returns an enumerator object generated from this enumerator and a
+ * given enumerable.
+ *
+ * e = (1..3).each + [4, 5]
+ * e.to_a #=> [1, 2, 3, 4, 5]
+ */
+static VALUE
+enumerator_plus(VALUE obj, VALUE eobj)
+{
+ VALUE enums = rb_ary_new_from_args(2, obj, eobj);
+
+ return enum_chain_initialize(enum_chain_allocate(rb_cEnumChain), enums);
+}
+
+/*
+ * Document-class: Enumerator::ArithmeticSequence
+ *
+ * Enumerator::ArithmeticSequence is a subclass of Enumerator,
+ * that is a representation of sequences of numbers with common difference.
+ * Instances of this class can be generated by the Range#step and Numeric#step
+ * methods.
+ */
+
+VALUE
+rb_arith_seq_new(VALUE obj, VALUE meth, int argc, VALUE const *argv,
+ rb_enumerator_size_func *size_fn,
+ VALUE beg, VALUE end, VALUE step, int excl)
+{
+ VALUE aseq = enumerator_init(enumerator_allocate(rb_cArithSeq),
+ obj, meth, argc, argv, size_fn, Qnil);
+ rb_ivar_set(aseq, id_begin, beg);
+ rb_ivar_set(aseq, id_end, end);
+ rb_ivar_set(aseq, id_step, step);
+ rb_ivar_set(aseq, id_exclude_end, excl ? Qtrue : Qfalse);
+ return aseq;
+}
+
+/*
+ * call-seq: aseq.begin -> num
+ *
+ * Returns the number that defines the first element of this arithmetic
+ * sequence.
+ */
+static inline VALUE
+arith_seq_begin(VALUE self)
+{
+ return rb_ivar_get(self, id_begin);
+}
+
+/*
+ * call-seq: aseq.end -> num or nil
+ *
+ * Returns the number that defines the end of this arithmetic sequence.
+ */
+static inline VALUE
+arith_seq_end(VALUE self)
+{
+ return rb_ivar_get(self, id_end);
+}
+
+/*
+ * call-seq: aseq.step -> num
+ *
+ * Returns the number that defines the common difference between
+ * two adjacent elements in this arithmetic sequence.
+ */
+static inline VALUE
+arith_seq_step(VALUE self)
+{
+ return rb_ivar_get(self, id_step);
+}
+
+/*
+ * call-seq: aseq.exclude_end? -> true or false
+ *
+ * Returns <code>true</code> if this arithmetic sequence excludes its end value.
+ */
+static inline VALUE
+arith_seq_exclude_end(VALUE self)
+{
+ return rb_ivar_get(self, id_exclude_end);
+}
+
+static inline int
+arith_seq_exclude_end_p(VALUE self)
+{
+ return RTEST(arith_seq_exclude_end(self));
+}
+
+int
+rb_arithmetic_sequence_extract(VALUE obj, rb_arithmetic_sequence_components_t *component)
+{
+ if (rb_obj_is_kind_of(obj, rb_cArithSeq)) {
+ component->begin = arith_seq_begin(obj);
+ component->end = arith_seq_end(obj);
+ component->step = arith_seq_step(obj);
+ component->exclude_end = arith_seq_exclude_end_p(obj);
+ return 1;
+ }
+ else if (rb_obj_is_kind_of(obj, rb_cRange)) {
+ component->begin = RANGE_BEG(obj);
+ component->end = RANGE_END(obj);
+ component->step = INT2FIX(1);
+ component->exclude_end = RTEST(RANGE_EXCL(obj));
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * call-seq:
+ * aseq.first -> num or nil
+ * aseq.first(n) -> an_array
+ *
+ * Returns the first number in this arithmetic sequence,
+ * or an array of the first +n+ elements.
+ */
+static VALUE
+arith_seq_first(int argc, VALUE *argv, VALUE self)
+{
+ VALUE b, e, s, ary;
+ long n;
+ int x;
+
+ rb_check_arity(argc, 0, 1);
+
+ b = arith_seq_begin(self);
+ e = arith_seq_end(self);
+ s = arith_seq_step(self);
+ if (argc == 0) {
+ if (!NIL_P(e)) {
+ VALUE zero = INT2FIX(0);
+ int r = rb_cmpint(rb_num_coerce_cmp(s, zero, idCmp), s, zero);
+ if (r > 0 && RTEST(rb_funcall(b, '>', 1, e))) {
+ return Qnil;
+ }
+ if (r < 0 && RTEST(rb_funcall(b, '<', 1, e))) {
+ return Qnil;
+ }
+ }
+ return b;
+ }
+
+ /* TODO: the following code should be extracted as arith_seq_take */
+
+ n = NUM2LONG(argv[0]);
+ if (n < 0) {
+ rb_raise(rb_eArgError, "attempt to take negative size");
+ }
+ if (n == 0) {
+ return rb_ary_new_capa(0);
+ }
+
+ x = arith_seq_exclude_end_p(self);
+
+ if (FIXNUM_P(b) && NIL_P(e) && FIXNUM_P(s)) {
+ long i = FIX2LONG(b), unit = FIX2LONG(s);
+ ary = rb_ary_new_capa(n);
+ while (n > 0 && FIXABLE(i)) {
+ rb_ary_push(ary, LONG2FIX(i));
+ i += unit; /* FIXABLE + FIXABLE never overflow; */
+ --n;
+ }
+ if (n > 0) {
+ b = LONG2NUM(i);
+ while (n > 0) {
+ rb_ary_push(ary, b);
+ b = rb_big_plus(b, s);
+ --n;
+ }
+ }
+ return ary;
+ }
+ else if (FIXNUM_P(b) && FIXNUM_P(e) && FIXNUM_P(s)) {
+ long i = FIX2LONG(b);
+ long end = FIX2LONG(e);
+ long unit = FIX2LONG(s);
+ long len;
+
+ if (unit >= 0) {
+ if (!x) end += 1;
+
+ len = end - i;
+ if (len < 0) len = 0;
+ ary = rb_ary_new_capa((n < len) ? n : len);
+ while (n > 0 && i < end) {
+ rb_ary_push(ary, LONG2FIX(i));
+ if (i + unit < i) break;
+ i += unit;
+ --n;
+ }
+ }
+ else {
+ if (!x) end -= 1;
+
+ len = i - end;
+ if (len < 0) len = 0;
+ ary = rb_ary_new_capa((n < len) ? n : len);
+ while (n > 0 && i > end) {
+ rb_ary_push(ary, LONG2FIX(i));
+ if (i + unit > i) break;
+ i += unit;
+ --n;
+ }
+ }
+ return ary;
+ }
+ else if (RB_FLOAT_TYPE_P(b) || RB_FLOAT_TYPE_P(e) || RB_FLOAT_TYPE_P(s)) {
+ /* generate values like ruby_float_step */
+
+ double unit = NUM2DBL(s);
+ double beg = NUM2DBL(b);
+ double end = NIL_P(e) ? (unit < 0 ? -1 : 1)*HUGE_VAL : NUM2DBL(e);
+ double len = ruby_float_step_size(beg, end, unit, x);
+ long i;
+
+ if (n > len)
+ n = (long)len;
+
+ if (isinf(unit)) {
+ if (len > 0) {
+ ary = rb_ary_new_capa(1);
+ rb_ary_push(ary, DBL2NUM(beg));
+ }
+ else {
+ ary = rb_ary_new_capa(0);
+ }
+ }
+ else if (unit == 0) {
+ VALUE val = DBL2NUM(beg);
+ ary = rb_ary_new_capa(n);
+ for (i = 0; i < len; ++i) {
+ rb_ary_push(ary, val);
+ }
+ }
+ else {
+ ary = rb_ary_new_capa(n);
+ for (i = 0; i < n; ++i) {
+ double d = i*unit+beg;
+ if (unit >= 0 ? end < d : d < end) d = end;
+ rb_ary_push(ary, DBL2NUM(d));
+ }
+ }
+
+ return ary;
+ }
+
+ return rb_call_super(argc, argv);
+}
+
+static inline VALUE
+num_plus(VALUE a, VALUE b)
+{
+ if (RB_INTEGER_TYPE_P(a)) {
+ return rb_int_plus(a, b);
+ }
+ else if (RB_FLOAT_TYPE_P(a)) {
+ return rb_float_plus(a, b);
+ }
+ else if (RB_TYPE_P(a, T_RATIONAL)) {
+ return rb_rational_plus(a, b);
+ }
+ else {
+ return rb_funcallv(a, '+', 1, &b);
+ }
+}
+
+static inline VALUE
+num_minus(VALUE a, VALUE b)
+{
+ if (RB_INTEGER_TYPE_P(a)) {
+ return rb_int_minus(a, b);
+ }
+ else if (RB_FLOAT_TYPE_P(a)) {
+ return rb_float_minus(a, b);
+ }
+ else if (RB_TYPE_P(a, T_RATIONAL)) {
+ return rb_rational_minus(a, b);
+ }
+ else {
+ return rb_funcallv(a, '-', 1, &b);
+ }
+}
+
+static inline VALUE
+num_mul(VALUE a, VALUE b)
+{
+ if (RB_INTEGER_TYPE_P(a)) {
+ return rb_int_mul(a, b);
+ }
+ else if (RB_FLOAT_TYPE_P(a)) {
+ return rb_float_mul(a, b);
+ }
+ else if (RB_TYPE_P(a, T_RATIONAL)) {
+ return rb_rational_mul(a, b);
+ }
+ else {
+ return rb_funcallv(a, '*', 1, &b);
+ }
+}
+
+static inline VALUE
+num_idiv(VALUE a, VALUE b)
+{
+ VALUE q;
+ if (RB_INTEGER_TYPE_P(a)) {
+ q = rb_int_idiv(a, b);
+ }
+ else if (RB_FLOAT_TYPE_P(a)) {
+ q = rb_float_div(a, b);
+ }
+ else if (RB_TYPE_P(a, T_RATIONAL)) {
+ q = rb_rational_div(a, b);
+ }
+ else {
+ q = rb_funcallv(a, '/', 1, &b);
+ }
+
+ if (RB_INTEGER_TYPE_P(q)) {
+ return q;
+ }
+ else if (RB_FLOAT_TYPE_P(q)) {
+ return rb_float_floor(q, 0);
+ }
+ else if (RB_TYPE_P(q, T_RATIONAL)) {
+ return rb_rational_floor(q, 0);
+ }
+ else {
+ return rb_funcall(q, rb_intern("floor"), 0);
+ }
+}
+
+/*
+ * call-seq:
+ * aseq.last -> num or nil
+ * aseq.last(n) -> an_array
+ *
+ * Returns the last number in this arithmetic sequence,
+ * or an array of the last +n+ elements.
+ */
+static VALUE
+arith_seq_last(int argc, VALUE *argv, VALUE self)
+{
+ VALUE b, e, s, len_1, len, last, nv, ary;
+ int last_is_adjusted;
+ long n;
+
+ e = arith_seq_end(self);
+ if (NIL_P(e)) {
+ rb_raise(rb_eRangeError,
+ "cannot get the last element of endless arithmetic sequence");
+ }
+
+ b = arith_seq_begin(self);
+ s = arith_seq_step(self);
+
+ len_1 = num_idiv(num_minus(e, b), s);
+ if (rb_num_negative_int_p(len_1)) {
+ if (argc == 0) {
+ return Qnil;
+ }
+ return rb_ary_new_capa(0);
+ }
+
+ last = num_plus(b, num_mul(s, len_1));
+ if ((last_is_adjusted = arith_seq_exclude_end_p(self) && rb_equal(last, e))) {
+ last = num_minus(last, s);
+ }
+
+ if (argc == 0) {
+ return last;
+ }
+
+ if (last_is_adjusted) {
+ len = len_1;
+ }
+ else {
+ len = rb_int_plus(len_1, INT2FIX(1));
+ }
+
+ rb_scan_args(argc, argv, "1", &nv);
+ if (!RB_INTEGER_TYPE_P(nv)) {
+ nv = rb_to_int(nv);
+ }
+ if (RTEST(rb_int_gt(nv, len))) {
+ nv = len;
+ }
+ n = NUM2LONG(nv);
+ if (n < 0) {
+ rb_raise(rb_eArgError, "negative array size");
+ }
+
+ ary = rb_ary_new_capa(n);
+ b = rb_int_minus(last, rb_int_mul(s, nv));
+ while (n) {
+ b = rb_int_plus(b, s);
+ rb_ary_push(ary, b);
+ --n;
+ }
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * aseq.inspect -> string
+ *
+ * Convert this arithmetic sequence to a printable form.
+ */
+static VALUE
+arith_seq_inspect(VALUE self)
+{
+ struct enumerator *e;
+ VALUE eobj, str, eargs;
+ int range_p;
+
+ TypedData_Get_Struct(self, struct enumerator, &enumerator_data_type, e);
+
+ eobj = rb_attr_get(self, id_receiver);
+ if (NIL_P(eobj)) {
+ eobj = e->obj;
+ }
+
+ range_p = RTEST(rb_obj_is_kind_of(eobj, rb_cRange));
+ str = rb_sprintf("(%s%"PRIsVALUE"%s.", range_p ? "(" : "", eobj, range_p ? ")" : "");
+
+ rb_str_buf_append(str, rb_id2str(e->meth));
+
+ eargs = rb_attr_get(eobj, id_arguments);
+ if (NIL_P(eargs)) {
+ eargs = e->args;
+ }
+ if (eargs != Qfalse) {
+ long argc = RARRAY_LEN(eargs);
+ const VALUE *argv = RARRAY_CONST_PTR(eargs); /* WB: no new reference */
+
+ if (argc > 0) {
+ VALUE kwds = Qnil;
+
+ rb_str_buf_cat2(str, "(");
+
+ if (RB_TYPE_P(argv[argc-1], T_HASH)) {
+ 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++;
+
+ rb_str_append(str, rb_inspect(arg));
+ rb_str_buf_cat2(str, ", ");
+ OBJ_INFECT(str, arg);
+ }
+ if (!NIL_P(kwds)) {
+ rb_hash_foreach(kwds, kwd_append, str);
+ }
+ rb_str_set_len(str, RSTRING_LEN(str)-2); /* drop the last ", " */
+ rb_str_buf_cat2(str, ")");
+ }
+ }
+
+ rb_str_buf_cat2(str, ")");
+
+ return str;
+}
+
+/*
+ * call-seq:
+ * aseq == obj -> true or false
+ *
+ * Returns <code>true</code> only if +obj+ is an Enumerator::ArithmeticSequence,
+ * has equivalent begin, end, step, and exclude_end? settings.
+ */
+static VALUE
+arith_seq_eq(VALUE self, VALUE other)
+{
+ if (!RTEST(rb_obj_is_kind_of(other, rb_cArithSeq))) {
+ return Qfalse;
+ }
+
+ if (!rb_equal(arith_seq_begin(self), arith_seq_begin(other))) {
+ return Qfalse;
+ }
+
+ if (!rb_equal(arith_seq_end(self), arith_seq_end(other))) {
+ return Qfalse;
+ }
+
+ if (!rb_equal(arith_seq_step(self), arith_seq_step(other))) {
+ return Qfalse;
+ }
+
+ if (arith_seq_exclude_end_p(self) != arith_seq_exclude_end_p(other)) {
+ return Qfalse;
+ }
+
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * aseq.hash -> integer
+ *
+ * Compute a hash-value for this arithmetic sequence.
+ * Two arithmetic sequences with same begin, end, step, and exclude_end?
+ * values will generate the same hash-value.
+ *
+ * See also Object#hash.
+ */
+static VALUE
+arith_seq_hash(VALUE self)
+{
+ st_index_t hash;
+ VALUE v;
+
+ hash = rb_hash_start(arith_seq_exclude_end_p(self));
+ v = rb_hash(arith_seq_begin(self));
+ hash = rb_hash_uint(hash, NUM2LONG(v));
+ v = rb_hash(arith_seq_end(self));
+ hash = rb_hash_uint(hash, NUM2LONG(v));
+ v = rb_hash(arith_seq_step(self));
+ hash = rb_hash_uint(hash, NUM2LONG(v));
+ hash = rb_hash_end(hash);
+
+ return ST2FIX(hash);
+}
+
+#define NUM_GE(x, y) RTEST(rb_num_coerce_relop((x), (y), idGE))
+
+struct arith_seq_gen {
+ VALUE current;
+ VALUE end;
+ VALUE step;
+ int excl;
+};
+
+/*
+ * call-seq:
+ * aseq.each {|i| block } -> aseq
+ * aseq.each -> aseq
+ */
+static VALUE
+arith_seq_each(VALUE self)
+{
+ VALUE c, e, s, len_1, last;
+ int x;
+
+ if (!rb_block_given_p()) return self;
+
+ c = arith_seq_begin(self);
+ e = arith_seq_end(self);
+ s = arith_seq_step(self);
+ x = arith_seq_exclude_end_p(self);
+
+ if (!RB_TYPE_P(s, T_COMPLEX) && ruby_float_step(c, e, s, x, TRUE)) {
+ return self;
+ }
+
+ if (NIL_P(e)) {
+ while (1) {
+ rb_yield(c);
+ c = rb_int_plus(c, s);
+ }
+
+ return self;
+ }
+
+ if (rb_equal(s, INT2FIX(0))) {
+ while (1) {
+ rb_yield(c);
+ }
+
+ return self;
+ }
+
+ len_1 = num_idiv(num_minus(e, c), s);
+ last = num_plus(c, num_mul(s, len_1));
+ if (x && rb_equal(last, e)) {
+ last = num_minus(last, s);
+ }
+
+ if (rb_num_negative_int_p(s)) {
+ while (NUM_GE(c, last)) {
+ rb_yield(c);
+ c = num_plus(c, s);
+ }
+ }
+ else {
+ while (NUM_GE(last, c)) {
+ rb_yield(c);
+ c = num_plus(c, s);
+ }
+ }
+
+ return self;
+}
+
+static double
+arith_seq_float_step_size(double beg, double end, double step, int excl)
+{
+ double const epsilon = DBL_EPSILON;
+ double n, err;
+
+ if (step == 0) {
+ return HUGE_VAL;
+ }
+ n = (end - beg) / step;
+ err = (fabs(beg) + fabs(end) + fabs(end - beg)) / fabs(step) * epsilon;
+ if (isinf(step)) {
+ return step > 0 ? beg <= end : beg >= end;
+ }
+ if (err > 0.5) err = 0.5;
+ if (excl) {
+ if (n <= 0) return 0;
+ if (n < 1)
+ n = 0;
+ else
+ n = floor(n - err);
+ }
+ else {
+ if (n < 0) return 0;
+ n = floor(n + err);
+ }
+ return n + 1;
+}
+
+/*
+ * call-seq:
+ * aseq.size -> num or nil
+ *
+ * Returns the number of elements in this arithmetic sequence if it is a finite
+ * sequence. Otherwise, returns <code>nil</code>.
+ */
+static VALUE
+arith_seq_size(VALUE self)
+{
+ VALUE b, e, s, len_1, len, last;
+ int x;
+
+ b = arith_seq_begin(self);
+ e = arith_seq_end(self);
+ s = arith_seq_step(self);
+ x = arith_seq_exclude_end_p(self);
+
+ if (RB_FLOAT_TYPE_P(b) || RB_FLOAT_TYPE_P(e) || RB_FLOAT_TYPE_P(s)) {
+ double ee, n;
+
+ if (NIL_P(e)) {
+ if (rb_num_negative_int_p(s)) {
+ ee = -HUGE_VAL;
+ }
+ else {
+ ee = HUGE_VAL;
+ }
+ }
+ else {
+ ee = NUM2DBL(e);
+ }
+
+ n = arith_seq_float_step_size(NUM2DBL(b), ee, NUM2DBL(s), x);
+ if (isinf(n)) return DBL2NUM(n);
+ if (POSFIXABLE(n)) return LONG2FIX(n);
+ return rb_dbl2big(n);
+ }
+
+ if (NIL_P(e)) {
+ return DBL2NUM(HUGE_VAL);
+ }
+
+ if (!rb_obj_is_kind_of(s, rb_cNumeric)) {
+ s = rb_to_int(s);
+ }
+
+ if (rb_equal(s, INT2FIX(0))) {
+ return DBL2NUM(HUGE_VAL);
+ }
+
+ len_1 = rb_int_idiv(rb_int_minus(e, b), s);
+ if (rb_num_negative_int_p(len_1)) {
+ return INT2FIX(0);
+ }
+
+ last = rb_int_plus(b, rb_int_mul(s, len_1));
+ if (x && rb_equal(last, e)) {
+ len = len_1;
+ }
+ else {
+ len = rb_int_plus(len_1, INT2FIX(1));
+ }
+
+ return len;
+}
+
void
InitVM_Enumerator(void)
{
@@ -2353,6 +3408,8 @@ InitVM_Enumerator(void)
rb_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0);
rb_define_method(rb_cEnumerator, "inspect", enumerator_inspect, 0);
rb_define_method(rb_cEnumerator, "size", enumerator_size, 0);
+ rb_define_method(rb_cEnumerator, "+", enumerator_plus, 1);
+ rb_define_method(rb_mEnumerable, "chain", enum_chain, -1);
/* Lazy */
rb_cLazy = rb_define_class_under(rb_cEnumerator, "Lazy", rb_cEnumerator);
@@ -2366,6 +3423,7 @@ InitVM_Enumerator(void)
rb_define_method(rb_cLazy, "collect_concat", lazy_flat_map, 0);
rb_define_method(rb_cLazy, "select", lazy_select, 0);
rb_define_method(rb_cLazy, "find_all", lazy_select, 0);
+ rb_define_method(rb_cLazy, "filter", lazy_select, 0);
rb_define_method(rb_cLazy, "reject", lazy_reject, 0);
rb_define_method(rb_cLazy, "grep", lazy_grep, 1);
rb_define_method(rb_cLazy, "grep_v", lazy_grep_v, 1);
@@ -2382,6 +3440,9 @@ InitVM_Enumerator(void)
rb_define_method(rb_cLazy, "chunk_while", lazy_super, -1);
rb_define_method(rb_cLazy, "uniq", lazy_uniq, 0);
+#if 0 /* for RDoc */
+ rb_define_method(rb_cLazy, "to_a", lazy_to_a, 0);
+#endif
rb_define_alias(rb_cLazy, "force", "to_a");
rb_eStopIteration = rb_define_class("StopIteration", rb_eIndexError);
@@ -2400,7 +3461,35 @@ InitVM_Enumerator(void)
rb_define_alloc_func(rb_cYielder, yielder_allocate);
rb_define_method(rb_cYielder, "initialize", yielder_initialize, 0);
rb_define_method(rb_cYielder, "yield", yielder_yield, -2);
- rb_define_method(rb_cYielder, "<<", yielder_yield_push, -2);
+ rb_define_method(rb_cYielder, "<<", yielder_yield_push, 1);
+
+ /* Chain */
+ rb_cEnumChain = rb_define_class_under(rb_cEnumerator, "Chain", rb_cEnumerator);
+ rb_define_alloc_func(rb_cEnumChain, enum_chain_allocate);
+ rb_define_method(rb_cEnumChain, "initialize", enum_chain_initialize, -2);
+ rb_define_method(rb_cEnumChain, "initialize_copy", enum_chain_init_copy, 1);
+ rb_define_method(rb_cEnumChain, "each", enum_chain_each, -1);
+ rb_define_method(rb_cEnumChain, "size", enum_chain_size, 0);
+ rb_define_method(rb_cEnumChain, "rewind", enum_chain_rewind, 0);
+ rb_define_method(rb_cEnumChain, "inspect", enum_chain_inspect, 0);
+
+ /* ArithmeticSequence */
+ rb_cArithSeq = rb_define_class_under(rb_cEnumerator, "ArithmeticSequence", rb_cEnumerator);
+ rb_undef_alloc_func(rb_cArithSeq);
+ rb_undef_method(CLASS_OF(rb_cArithSeq), "new");
+ rb_define_method(rb_cArithSeq, "begin", arith_seq_begin, 0);
+ rb_define_method(rb_cArithSeq, "end", arith_seq_end, 0);
+ rb_define_method(rb_cArithSeq, "exclude_end?", arith_seq_exclude_end, 0);
+ rb_define_method(rb_cArithSeq, "step", arith_seq_step, 0);
+ rb_define_method(rb_cArithSeq, "first", arith_seq_first, -1);
+ rb_define_method(rb_cArithSeq, "last", arith_seq_last, -1);
+ rb_define_method(rb_cArithSeq, "inspect", arith_seq_inspect, 0);
+ rb_define_method(rb_cArithSeq, "==", arith_seq_eq, 1);
+ rb_define_method(rb_cArithSeq, "===", arith_seq_eq, 1);
+ rb_define_method(rb_cArithSeq, "eql?", arith_seq_eq, 1);
+ rb_define_method(rb_cArithSeq, "hash", arith_seq_hash, 0);
+ rb_define_method(rb_cArithSeq, "each", arith_seq_each, 0);
+ rb_define_method(rb_cArithSeq, "size", arith_seq_size, 0);
rb_provide("enumerator.so"); /* for backward compatibility */
}
@@ -2410,22 +3499,19 @@ void
Init_Enumerator(void)
{
id_rewind = rb_intern("rewind");
- id_each = rb_intern("each");
- id_call = rb_intern("call");
- id_size = rb_intern("size");
- id_yield = rb_intern("yield");
id_new = rb_intern("new");
- id_initialize = rb_intern("initialize");
id_next = rb_intern("next");
id_result = rb_intern("result");
- id_lazy = rb_intern("lazy");
- id_eqq = rb_intern("===");
id_receiver = rb_intern("receiver");
id_arguments = rb_intern("arguments");
id_memo = rb_intern("memo");
id_method = rb_intern("method");
id_force = rb_intern("force");
id_to_enum = rb_intern("to_enum");
+ id_begin = rb_intern("begin");
+ id_end = rb_intern("end");
+ id_step = rb_intern("step");
+ id_exclude_end = rb_intern("exclude_end");
sym_each = ID2SYM(id_each);
sym_cycle = ID2SYM(rb_intern("cycle"));
diff --git a/error.c b/error.c
index 0856bc0e2a..a085afd800 100644
--- a/error.c
+++ b/error.c
@@ -9,8 +9,9 @@
**********************************************************************/
-#include "internal.h"
+#include "ruby/encoding.h"
#include "ruby/st.h"
+#include "internal.h"
#include "ruby_assert.h"
#include "vm_core.h"
@@ -24,6 +25,15 @@
#include <unistd.h>
#endif
+#if defined __APPLE__
+# include <AvailabilityMacros.h>
+#endif
+
+/*!
+ * \defgroup exception Exception handlings
+ * \{
+ */
+
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
@@ -38,11 +48,13 @@
VALUE rb_iseqw_local_variables(VALUE iseqval);
VALUE rb_iseqw_new(const rb_iseq_t *);
+int rb_str_end_with_asciichar(VALUE str, int c);
VALUE rb_eEAGAIN;
VALUE rb_eEWOULDBLOCK;
VALUE rb_eEINPROGRESS;
-VALUE rb_mWarning;
+static VALUE rb_mWarning;
+static VALUE rb_cWarningBuffer;
static ID id_warn;
@@ -54,7 +66,7 @@ static const char REPORTBUG_MSG[] =
" or extension libraries.\n" \
"Bug reports are welcome.\n" \
""
- "For details: http://www.ruby-lang.org/bugreport.html\n\n" \
+ "For details: https://www.ruby-lang.org/bugreport.html\n\n" \
;
static const char *
@@ -125,30 +137,20 @@ rb_syntax_error_append(VALUE exc, VALUE file, int line, int column,
}
void
-rb_compile_error_with_enc(const char *file, int line, void *enc, const char *fmt, ...)
-{
- ONLY_FOR_INTERNAL_USE("rb_compile_error_with_enc()");
-}
-
-void
-rb_compile_error(const char *file, int line, const char *fmt, ...)
-{
- ONLY_FOR_INTERNAL_USE("rb_compile_error()");
-}
-
-void
-rb_compile_error_append(const char *fmt, ...)
-{
- ONLY_FOR_INTERNAL_USE("rb_compile_error_append()");
-}
-
-void
-ruby_only_for_internal_use(const char *func)
+ruby_deprecated_internal_feature(const char *func)
{
rb_print_backtrace();
rb_fatal("%s is only for internal use and deprecated; do not use", func);
}
+/*
+ * call-seq:
+ * warn(msg) -> nil
+ *
+ * Writes warning message +msg+ to $stderr. This method is called by
+ * Ruby for all emitted warnings.
+ */
+
static VALUE
rb_warning_s_warn(VALUE mod, VALUE str)
{
@@ -158,10 +160,31 @@ rb_warning_s_warn(VALUE mod, VALUE str)
return Qnil;
}
+/*
+ * Document-module: Warning
+ *
+ * The Warning module contains a single method named #warn, and the
+ * module extends itself, making <code>Warning.warn</code> available.
+ * Warning.warn is called for all warnings issued by Ruby.
+ * By default, warnings are printed to $stderr.
+ *
+ * By overriding Warning.warn, you can change how warnings are
+ * handled by Ruby, either filtering some warnings, and/or outputting
+ * warnings somewhere other than $stderr. When Warning.warn is
+ * overridden, super can be called to get the default behavior of
+ * printing the warning to $stderr.
+ */
+
+VALUE
+rb_warning_warn(VALUE mod, VALUE str)
+{
+ return rb_funcallv(mod, id_warn, 1, &str);
+}
+
static void
rb_write_warning_str(VALUE str)
{
- rb_funcall(rb_mWarning, id_warn, 1, str);
+ rb_warning_warn(rb_mWarning, str);
}
static VALUE
@@ -206,79 +229,93 @@ static VALUE
warning_string(rb_encoding *enc, const char *fmt, va_list args)
{
int line;
- VALUE file = rb_source_location(&line);
-
- return warn_vsprintf(enc,
- NIL_P(file) ? NULL : RSTRING_PTR(file), line,
- fmt, args);
+ const char *file = rb_source_location_cstr(&line);
+ return warn_vsprintf(enc, file, line, fmt, args);
}
+#define with_warning_string(mesg, enc, fmt) \
+ VALUE mesg; \
+ va_list args; va_start(args, fmt); \
+ mesg = warning_string(enc, fmt, args); \
+ va_end(args);
+
void
rb_warn(const char *fmt, ...)
{
- VALUE mesg;
- va_list args;
-
- if (NIL_P(ruby_verbose)) return;
-
- va_start(args, fmt);
- mesg = warning_string(0, fmt, args);
- va_end(args);
- rb_write_warning_str(mesg);
+ if (!NIL_P(ruby_verbose)) {
+ with_warning_string(mesg, 0, fmt) {
+ rb_write_warning_str(mesg);
+ }
+ }
}
void
rb_enc_warn(rb_encoding *enc, const char *fmt, ...)
{
- VALUE mesg;
- va_list args;
-
- if (NIL_P(ruby_verbose)) return;
-
- va_start(args, fmt);
- mesg = warning_string(enc, fmt, args);
- va_end(args);
- rb_write_warning_str(mesg);
+ if (!NIL_P(ruby_verbose)) {
+ with_warning_string(mesg, enc, fmt) {
+ rb_write_warning_str(mesg);
+ }
+ }
}
/* rb_warning() reports only in verbose mode */
void
rb_warning(const char *fmt, ...)
{
- VALUE mesg;
- va_list args;
-
- if (!RTEST(ruby_verbose)) return;
+ if (RTEST(ruby_verbose)) {
+ with_warning_string(mesg, 0, fmt) {
+ rb_write_warning_str(mesg);
+ }
+ }
+}
- va_start(args, fmt);
- mesg = warning_string(0, fmt, args);
- va_end(args);
- rb_write_warning_str(mesg);
+VALUE
+rb_warning_string(const char *fmt, ...)
+{
+ with_warning_string(mesg, 0, fmt) {
+ }
+ return mesg;
}
#if 0
void
rb_enc_warning(rb_encoding *enc, const char *fmt, ...)
{
- VALUE mesg;
- va_list args;
+ if (RTEST(ruby_verbose)) {
+ with_warning_string(mesg, enc, fmt) {
+ rb_write_warning_str(mesg);
+ }
+ }
+}
+#endif
- if (!RTEST(ruby_verbose)) return;
+static inline int
+end_with_asciichar(VALUE str, int c)
+{
+ return RB_TYPE_P(str, T_STRING) &&
+ rb_str_end_with_asciichar(str, c);
+}
- va_start(args, fmt);
- mesg = warning_string(enc, fmt, args);
- va_end(args);
- rb_write_warning_str(mesg);
+/* :nodoc: */
+static VALUE
+warning_write(int argc, VALUE *argv, VALUE buf)
+{
+ while (argc-- > 0) {
+ rb_str_append(buf, *argv++);
+ }
+ return buf;
}
-#endif
/*
* call-seq:
* warn(msg, ...) -> nil
*
- * Displays each of the given messages followed by a record separator on
- * STDERR unless warnings have been disabled (for example with the
- * <code>-W0</code> flag).
+ * If warnings have been disabled (for example with the
+ * <code>-W0</code> flag), does nothing. Otherwise,
+ * converts each of the messages to strings, appends a newline
+ * character to the string if the string does not end in a newline,
+ * and calls <code>Warning.warn</code> with the string.
*
* warn("warning 1", "warning 2")
*
@@ -286,13 +323,83 @@ rb_enc_warning(rb_encoding *enc, const char *fmt, ...)
*
* warning 1
* warning 2
+ *
+ * If the <code>uplevel</code> keyword argument is given, the string will
+ * be prepended with information for the given caller frame in
+ * the same format used by the <code>rb_warn</code> C function.
+ *
+ * # In baz.rb
+ * def foo
+ * warn("invalid call to foo", uplevel: 1)
+ * end
+ *
+ * def bar
+ * foo
+ * end
+ *
+ * bar
+ *
+ * <em>produces:</em>
+ *
+ * baz.rb:6: warning: invalid call to foo
*/
static VALUE
rb_warn_m(int argc, VALUE *argv, VALUE exc)
{
- if (!NIL_P(ruby_verbose) && argc > 0) {
- rb_io_puts(argc, argv, rb_stderr);
+ VALUE opts, location = Qnil;
+
+ if (!NIL_P(ruby_verbose) && argc > 0 &&
+ (argc = rb_scan_args(argc, argv, "*:", NULL, &opts)) > 0) {
+ VALUE str = argv[0], uplevel = Qnil;
+ if (!NIL_P(opts)) {
+ static ID kwds[1];
+ if (!kwds[0]) {
+ CONST_ID(kwds[0], "uplevel");
+ }
+ rb_get_kwargs(opts, kwds, 0, 1, &uplevel);
+ if (uplevel == Qundef) {
+ uplevel = Qnil;
+ }
+ else if (!NIL_P(uplevel)) {
+ VALUE args[2];
+ long lev = NUM2LONG(uplevel);
+ if (lev < 0) {
+ rb_raise(rb_eArgError, "negative level (%ld)", lev);
+ }
+ args[0] = LONG2NUM(lev + 1);
+ args[1] = INT2FIX(1);
+ location = rb_vm_thread_backtrace_locations(2, args, GET_THREAD()->self);
+ 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 (exc == rb_mWarning) {
+ rb_must_asciicompat(str);
+ rb_write_error_str(str);
+ }
+ else {
+ rb_write_warning_str(str);
+ }
}
return Qnil;
}
@@ -336,6 +443,80 @@ bug_report_file(const char *file, int line)
return NULL;
}
+FUNC_MINIMIZED(static void bug_important_message(FILE *out, const char *const msg, size_t len));
+
+static void
+bug_important_message(FILE *out, const char *const msg, size_t len)
+{
+ const char *const endmsg = msg + len;
+ const char *p = msg;
+
+ 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);
+ }
+ fwrite(p, 1, endmsg - p, out);
+}
+
+static void
+preface_dump(FILE *out)
+{
+#if defined __APPLE__
+ static const char msg[] = ""
+ "-- Crash Report log information "
+ "--------------------------------------------\n"
+ " See Crash Report log file under the one of following:\n"
+# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6
+ " * ~/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";
+ const size_t msglen = sizeof(msg) - 1;
+#else
+ const char *msg = NULL;
+ const size_t msglen = 0;
+#endif
+ bug_important_message(out, msg, msglen);
+}
+
+static void
+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 "
+# endif
+ "DiagnosticReports directory in bug reports.\n"
+ /*"------------------------------------------------------------\n"*/
+ "\n";
+ const size_t msglen = sizeof(msg) - 1;
+#else
+ const char *msg = NULL;
+ const size_t msglen = 0;
+#endif
+ bug_important_message(out, msg, msglen);
+}
+
static void
bug_report_begin_valist(FILE *out, const char *fmt, va_list args)
{
@@ -346,6 +527,7 @@ bug_report_begin_valist(FILE *out, const char *fmt, va_list args)
fputs(buf, out);
snprintf(buf, sizeof(buf), "\n%s\n\n", ruby_description);
fputs(buf, out);
+ preface_dump(out);
}
#define bug_report_begin(out, fmt) do { \
@@ -366,7 +548,8 @@ bug_report_end(FILE *out)
(*reporter->func)(out, reporter->data);
}
}
- fprintf(out, REPORTBUG_MSG);
+ fputs(REPORTBUG_MSG, out);
+ postscript_dump(out);
}
#define report_bug(file, line, fmt, ctx) do { \
@@ -404,8 +587,8 @@ rb_bug(const char *fmt, ...)
const char *file = NULL;
int line = 0;
- if (GET_THREAD()) {
- file = rb_source_loc(&line);
+ if (GET_EC()) {
+ file = rb_source_location_cstr(&line);
}
report_bug(file, line, fmt, NULL);
@@ -419,8 +602,8 @@ rb_bug_context(const void *ctx, const char *fmt, ...)
const char *file = NULL;
int line = 0;
- if (GET_THREAD()) {
- file = rb_source_loc(&line);
+ if (GET_EC()) {
+ file = rb_source_location_cstr(&line);
}
report_bug(file, line, fmt, ctx);
@@ -480,13 +663,14 @@ rb_report_bug_valist(VALUE file, int line, const char *fmt, va_list args)
report_bug_valist(RSTRING_PTR(file), line, fmt, NULL, args);
}
-void
+MJIT_FUNC_EXPORTED void
rb_assert_failure(const char *file, int line, const char *name, const char *expr)
{
FILE *out = stderr;
fprintf(out, "Assertion Failed: %s:%d:", file, line);
if (name) fprintf(out, "%s:", name);
fprintf(out, "%s\n%s\n\n", expr, ruby_description);
+ preface_dump(out);
rb_vm_bugreport(NULL);
bug_report_end(out);
die();
@@ -645,6 +829,13 @@ rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
return 1;
}
+#undef rb_typeddata_is_instance_of
+int
+rb_typeddata_is_instance_of(VALUE obj, const rb_data_type_t *data_type)
+{
+ return rb_typeddata_is_instance_of_inline(obj, data_type);
+}
+
void *
rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type)
{
@@ -678,6 +869,7 @@ VALUE rb_eSignal;
VALUE rb_eFatal;
VALUE rb_eStandardError;
VALUE rb_eRuntimeError;
+VALUE rb_eFrozenError;
VALUE rb_eTypeError;
VALUE rb_eArgError;
VALUE rb_eIndexError;
@@ -700,22 +892,23 @@ VALUE rb_eSystemCallError;
VALUE rb_mErrno;
static VALUE rb_eNOERROR;
-static ID id_new, id_cause, id_message, id_backtrace;
-static ID id_name, id_args, id_Errno, id_errno, id_i_path;
-static ID id_receiver, id_iseq, id_local_variables;
-static ID id_private_call_p;
-extern ID ruby_static_id_status;
+ID ruby_static_id_cause;
+#define id_cause ruby_static_id_cause
+static ID id_message, id_backtrace;
+static ID id_name, id_key, id_args, id_Errno, id_errno, id_i_path;
+static ID id_receiver, id_recv, id_iseq, id_local_variables;
+static ID id_private_call_p, id_top, id_bottom;
#define id_bt idBt
#define id_bt_locations idBt_locations
#define id_mesg idMesg
-#define id_status ruby_static_id_status
#undef rb_exc_new_cstr
VALUE
rb_exc_new(VALUE etype, const char *ptr, long len)
{
- return rb_funcall(etype, id_new, 1, rb_str_new(ptr, len));
+ VALUE mesg = rb_str_new(ptr, len);
+ return rb_class_new_instance(1, &mesg, etype);
}
VALUE
@@ -728,7 +921,16 @@ VALUE
rb_exc_new_str(VALUE etype, VALUE str)
{
StringValue(str);
- return rb_funcall(etype, id_new, 1, str);
+ return rb_class_new_instance(1, &str, etype);
+}
+
+static VALUE
+exc_init(VALUE exc, VALUE mesg)
+{
+ rb_ivar_set(exc, id_mesg, mesg);
+ rb_ivar_set(exc, id_bt, Qnil);
+
+ return exc;
}
/*
@@ -744,11 +946,8 @@ exc_initialize(int argc, VALUE *argv, VALUE exc)
{
VALUE arg;
- rb_scan_args(argc, argv, "01", &arg);
- rb_ivar_set(exc, id_mesg, arg);
- rb_ivar_set(exc, id_bt, Qnil);
-
- return exc;
+ arg = (!rb_check_arity(argc, 0, 1) ? Qnil : argv[0]);
+ return exc_init(exc, arg);
}
/*
@@ -794,14 +993,99 @@ exc_to_s(VALUE exc)
return rb_String(mesg);
}
+/* FIXME: Include eval_error.c */
+void rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE highlight, VALUE reverse);
+
+VALUE
+rb_get_message(VALUE exc)
+{
+ VALUE e = rb_check_funcall(exc, id_message, 0, 0);
+ if (e == Qundef) return Qnil;
+ if (!RB_TYPE_P(e, T_STRING)) e = rb_check_string_type(e);
+ return e;
+}
+
+/*
+ * call-seq:
+ * Exception.to_tty? -> true or false
+ *
+ * Returns +true+ if exception messages will be sent to a tty.
+ */
+static VALUE
+exc_s_to_tty_p(VALUE self)
+{
+ return rb_stderr_tty_p() ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq:
+ * exception.full_message(highlight: bool, order: [:top or :bottom]) -> string
+ *
+ * Returns formatted string of _exception_.
+ * The returned string is formatted using the same format that Ruby uses
+ * when printing an uncaught exceptions to stderr.
+ *
+ * If _highlight_ is +true+ the default error handler will send the
+ * messages to a tty.
+ *
+ * _order_ must be either of +:top+ or +:bottom+, and places the error
+ * message and the innermost backtrace come at the top or the bottom.
+ *
+ * The default values of these options depend on <code>$stderr</code>
+ * and its +tty?+ at the timing of a call.
+ */
+
+static VALUE
+exc_full_message(int argc, VALUE *argv, VALUE exc)
+{
+ VALUE opt, str, emesg, errat;
+ enum {kw_highlight, kw_order, kw_max_};
+ static ID kw[kw_max_];
+ VALUE args[kw_max_] = {Qnil, Qnil};
+
+ rb_scan_args(argc, argv, "0:", &opt);
+ if (!NIL_P(opt)) {
+ if (!kw[0]) {
+#define INIT_KW(n) kw[kw_##n] = rb_intern_const(#n)
+ INIT_KW(highlight);
+ INIT_KW(order);
+#undef INIT_KW
+ }
+ rb_get_kwargs(opt, kw, 0, kw_max_, args);
+ switch (args[kw_highlight]) {
+ default:
+ rb_raise(rb_eArgError, "expected true or false as "
+ "highlight: %+"PRIsVALUE, args[kw_highlight]);
+ case Qundef: args[kw_highlight] = Qnil; break;
+ case Qtrue: case Qfalse: case Qnil: break;
+ }
+ if (args[kw_order] == Qundef) {
+ args[kw_order] = Qnil;
+ }
+ else {
+ ID id = rb_check_id(&args[kw_order]);
+ if (id == id_bottom) args[kw_order] = Qtrue;
+ else if (id == id_top) args[kw_order] = Qfalse;
+ else {
+ rb_raise(rb_eArgError, "expected :top or :bottom as "
+ "order: %+"PRIsVALUE, args[kw_order]);
+ }
+ }
+ }
+ str = rb_str_new2("");
+ errat = rb_get_backtrace(exc);
+ emesg = rb_get_message(exc);
+
+ rb_error_write(exc, emesg, errat, str, args[kw_highlight], args[kw_order]);
+ return str;
+}
+
/*
* call-seq:
* exception.message -> string
*
* Returns the result of invoking <code>exception.to_s</code>.
- * Normally this returns the exception's message or name. By
- * supplying a to_str method, exceptions are agreeing to
- * be used where Strings are expected.
+ * Normally this returns the exception's message or name.
*/
static VALUE
@@ -814,7 +1098,7 @@ exc_message(VALUE exc)
* call-seq:
* exception.inspect -> string
*
- * Return this exception's class name and message
+ * Return this exception's class name and message.
*/
static VALUE
@@ -882,6 +1166,27 @@ exc_backtrace(VALUE exc)
return obj;
}
+VALUE
+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);
+ }
+ else {
+ info = rb_funcallv(exc, mid, 0, 0);
+ }
+ if (NIL_P(info)) return Qnil;
+ return rb_check_backtrace(info);
+}
+
/*
* call-seq:
* exception.backtrace_locations -> array
@@ -942,7 +1247,7 @@ exc_set_backtrace(VALUE exc, VALUE bt)
return rb_ivar_set(exc, id_bt, rb_check_backtrace(bt));
}
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_exc_set_backtrace(VALUE exc, VALUE bt)
{
return exc_set_backtrace(exc, bt);
@@ -986,10 +1291,10 @@ exc_equal(VALUE exc, VALUE obj)
if (exc == obj) return Qtrue;
if (rb_obj_class(exc) != rb_obj_class(obj)) {
- int status = 0;
+ int state;
- obj = rb_protect(try_convert_to_exception, obj, &status);
- if (status || obj == Qundef) {
+ obj = rb_protect(try_convert_to_exception, obj, &state);
+ if (state || obj == Qundef) {
rb_set_errinfo(Qnil);
return Qfalse;
}
@@ -1132,34 +1437,57 @@ rb_name_error_str(VALUE str, const char *fmt, ...)
rb_exc_raise(exc);
}
+static VALUE
+name_err_init_attr(VALUE exc, VALUE recv, VALUE method)
+{
+ const rb_execution_context_t *ec = GET_EC();
+ rb_control_frame_t *cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(ec->cfp);
+ cfp = rb_vm_get_ruby_level_next_cfp(ec, cfp);
+ rb_ivar_set(exc, id_name, method);
+ if (recv != Qundef) rb_ivar_set(exc, id_recv, recv);
+ if (cfp) rb_ivar_set(exc, id_iseq, rb_iseqw_new(cfp->iseq));
+ return exc;
+}
+
/*
* call-seq:
- * NameError.new([msg, *, name]) -> name_error
+ * NameError.new(msg [, name]) -> name_error
+ * NameError.new(msg [, name], receiver:) -> name_error
*
* Construct a new NameError exception. If given the <i>name</i>
- * parameter may subsequently be examined using the <code>NameError.name</code>
+ * parameter may subsequently be examined using the <code>NameError#name</code>
* method.
*/
static VALUE
name_err_initialize(int argc, VALUE *argv, VALUE self)
{
- VALUE name;
- VALUE iseqw = Qnil;
+ ID keywords[1];
+ VALUE values[numberof(keywords)], name, options;
+ argc = rb_scan_args(argc, argv, "*:", NULL, &options);
+ keywords[0] = id_receiver;
+ rb_get_kwargs(options, keywords, 0, numberof(values), values);
name = (argc > 1) ? argv[--argc] : Qnil;
rb_call_super(argc, argv);
- rb_ivar_set(self, id_name, name);
- {
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp =
- rb_vm_get_ruby_level_next_cfp(th, RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp));
- if (cfp) iseqw = rb_iseqw_new(cfp->iseq);
- }
- rb_ivar_set(self, id_iseq, iseqw);
+ name_err_init_attr(self, values[0], name);
return self;
}
+static VALUE
+name_err_init(VALUE exc, VALUE mesg, VALUE recv, VALUE method)
+{
+ exc_init(exc, rb_name_err_mesg_new(mesg, recv, method));
+ return name_err_init_attr(exc, recv, method);
+}
+
+VALUE
+rb_name_err_new(VALUE mesg, VALUE recv, VALUE method)
+{
+ VALUE exc = rb_obj_alloc(rb_eNameError);
+ return name_err_init(exc, mesg, recv, method);
+}
+
/*
* call-seq:
* name_error.name -> string or nil
@@ -1197,9 +1525,17 @@ name_err_local_variables(VALUE self)
return vars;
}
+static VALUE
+nometh_err_init_attr(VALUE exc, VALUE args, int priv)
+{
+ rb_ivar_set(exc, id_args, args);
+ rb_ivar_set(exc, id_private_call_p, priv ? Qtrue : Qfalse);
+ return exc;
+}
+
/*
* call-seq:
- * NoMethodError.new([msg, *, name [, args]]) -> no_method_error
+ * NoMethodError.new([msg, *, name [, args [, priv]]]) -> no_method_error
*
* Construct a NoMethodError exception for a method of the given name
* called with the given arguments. The name may be accessed using
@@ -1210,12 +1546,22 @@ name_err_local_variables(VALUE self)
static VALUE
nometh_err_initialize(int argc, VALUE *argv, VALUE self)
{
- VALUE priv = (argc > 3) && (--argc, RTEST(argv[argc])) ? Qtrue : Qfalse;
- VALUE args = (argc > 2) ? argv[--argc] : Qnil;
- name_err_initialize(argc, argv, self);
- rb_ivar_set(self, id_args, args);
- rb_ivar_set(self, id_private_call_p, RTEST(priv) ? Qtrue : Qfalse);
- return self;
+ int priv;
+ VALUE args, options;
+ argc = rb_scan_args(argc, argv, "*:", NULL, &options);
+ priv = (argc > 3) && (--argc, RTEST(argv[argc]));
+ args = (argc > 2) ? argv[--argc] : Qnil;
+ if (!NIL_P(options)) argv[argc++] = options;
+ rb_call_super(argc, argv);
+ return nometh_err_init_attr(self, args, priv);
+}
+
+VALUE
+rb_nomethod_err_new(VALUE mesg, VALUE recv, VALUE method, VALUE args, int priv)
+{
+ VALUE exc = rb_obj_alloc(rb_eNoMethodError);
+ name_err_init(exc, mesg, recv, method);
+ return nometh_err_init_attr(exc, args, priv);
}
/* :nodoc: */
@@ -1265,17 +1611,6 @@ rb_name_err_mesg_new(VALUE mesg, VALUE recv, VALUE method)
return result;
}
-VALUE
-rb_name_err_new(VALUE mesg, VALUE recv, VALUE method)
-{
- VALUE exc = rb_obj_alloc(rb_eNameError);
- rb_ivar_set(exc, id_mesg, rb_name_err_mesg_new(mesg, recv, method));
- rb_ivar_set(exc, id_bt, Qnil);
- rb_ivar_set(exc, id_name, method);
- rb_ivar_set(exc, id_receiver, recv);
- return exc;
-}
-
/* :nodoc: */
static VALUE
name_err_mesg_equal(VALUE obj1, VALUE obj2)
@@ -1376,7 +1711,7 @@ name_err_receiver(VALUE self)
{
VALUE *ptr, recv, mesg;
- recv = rb_ivar_lookup(self, id_receiver, Qundef);
+ recv = rb_ivar_lookup(self, id_recv, Qundef);
if (recv != Qundef) return recv;
mesg = rb_attr_get(self, id_mesg);
@@ -1401,6 +1736,13 @@ nometh_err_args(VALUE self)
return rb_attr_get(self, id_args);
}
+/*
+ * call-seq:
+ * no_method_error.private_call? -> true or false
+ *
+ * Return true if the caused method was called as private.
+ */
+
static VALUE
nometh_err_private_call_p(VALUE self)
{
@@ -1417,6 +1759,83 @@ rb_invalid_str(const char *str, const char *type)
/*
* call-seq:
+ * key_error.receiver -> object
+ *
+ * Return the receiver associated with this KeyError exception.
+ */
+
+static VALUE
+key_err_receiver(VALUE self)
+{
+ VALUE recv;
+
+ recv = rb_ivar_lookup(self, id_receiver, Qundef);
+ if (recv != Qundef) return recv;
+ rb_raise(rb_eArgError, "no receiver is available");
+}
+
+/*
+ * call-seq:
+ * key_error.key -> object
+ *
+ * Return the key caused this KeyError exception.
+ */
+
+static VALUE
+key_err_key(VALUE self)
+{
+ VALUE key;
+
+ key = rb_ivar_lookup(self, id_key, Qundef);
+ if (key != Qundef) return key;
+ rb_raise(rb_eArgError, "no key is available");
+}
+
+VALUE
+rb_key_err_new(VALUE mesg, VALUE recv, VALUE key)
+{
+ VALUE exc = rb_obj_alloc(rb_eKeyError);
+ rb_ivar_set(exc, id_mesg, mesg);
+ rb_ivar_set(exc, id_bt, Qnil);
+ rb_ivar_set(exc, id_key, key);
+ rb_ivar_set(exc, id_receiver, recv);
+ return exc;
+}
+
+/*
+ * call-seq:
+ * KeyError.new(message=nil, receiver: nil, key: nil) -> key_error
+ *
+ * Construct a new +KeyError+ exception with the given message,
+ * receiver and key.
+ */
+
+static VALUE
+key_err_initialize(int argc, VALUE *argv, VALUE self)
+{
+ VALUE options;
+
+ 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]);
+ }
+ }
+ }
+
+ return self;
+}
+
+/*
+ * call-seq:
* SyntaxError.new([msg]) -> syntax_error
*
* Construct a SyntaxError exception.
@@ -1427,7 +1846,7 @@ syntax_error_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE mesg;
if (argc == 0) {
- mesg = rb_fstring_cstr("compile error");
+ mesg = rb_fstring_lit("compile error");
argc = 1;
argv = &mesg;
}
@@ -1663,8 +2082,8 @@ syserr_eqq(VALUE self, VALUE exc)
/*
* Document-class: Interrupt
*
- * Raised with the interrupt signal is received, typically because the
- * user pressed on Control-C (on most posix platforms). As such, it is a
+ * Raised when the interrupt signal is received, typically because the
+ * user has pressed Control-C (on most posix platforms). As such, it is a
* subclass of +SignalException+.
*
* begin
@@ -1837,17 +2256,22 @@ syserr_eqq(VALUE self, VALUE exc)
*/
/*
- * Document-class: RuntimeError
+ * Document-class: FrozenError
*
- * A generic error class raised when an invalid operation is attempted.
+ * Raised when there is an attempt to modify a frozen object.
*
* [1, 2, 3].freeze << 4
*
* <em>raises the exception:</em>
*
- * RuntimeError: can't modify frozen Array
+ * FrozenError: can't modify frozen Array
+ */
+
+/*
+ * Document-class: RuntimeError
*
- * Kernel.raise will raise a RuntimeError if no Exception class is
+ * A generic error class raised when an invalid operation is attempted.
+ * Kernel#raise will raise a RuntimeError if no Exception class is
* specified.
*
* raise "ouch"
@@ -1913,7 +2337,7 @@ syserr_eqq(VALUE self, VALUE exc)
/*
* Document-class: fatal
*
- * fatal is an Exception that is raised when ruby has encountered a fatal
+ * fatal is an Exception that is raised when Ruby has encountered a fatal
* error and must exit. You are not able to rescue fatal.
*/
@@ -1989,6 +2413,7 @@ syserr_eqq(VALUE self, VALUE exc)
* * FloatDomainError
* * RegexpError
* * RuntimeError -- default for +raise+
+ * * FrozenError
* * SystemCallError
* * Errno::*
* * ThreadError
@@ -2004,11 +2429,13 @@ Init_Exception(void)
{
rb_eException = rb_define_class("Exception", rb_cObject);
rb_define_singleton_method(rb_eException, "exception", rb_class_new_instance, -1);
+ rb_define_singleton_method(rb_eException, "to_tty?", exc_s_to_tty_p, 0);
rb_define_method(rb_eException, "exception", exc_exception, -1);
rb_define_method(rb_eException, "initialize", exc_initialize, -1);
rb_define_method(rb_eException, "==", exc_equal, 1);
rb_define_method(rb_eException, "to_s", exc_to_s, 0);
rb_define_method(rb_eException, "message", exc_message, 0);
+ rb_define_method(rb_eException, "full_message", exc_full_message, -1);
rb_define_method(rb_eException, "inspect", exc_inspect, 0);
rb_define_method(rb_eException, "backtrace", exc_backtrace, 0);
rb_define_method(rb_eException, "backtrace_locations", exc_backtrace_locations, 0);
@@ -2029,6 +2456,9 @@ Init_Exception(void)
rb_eArgError = rb_define_class("ArgumentError", rb_eStandardError);
rb_eIndexError = rb_define_class("IndexError", rb_eStandardError);
rb_eKeyError = rb_define_class("KeyError", rb_eIndexError);
+ rb_define_method(rb_eKeyError, "initialize", key_err_initialize, -1);
+ rb_define_method(rb_eKeyError, "receiver", key_err_receiver, 0);
+ rb_define_method(rb_eKeyError, "key", key_err_key, 0);
rb_eRangeError = rb_define_class("RangeError", rb_eStandardError);
rb_eScriptError = rb_define_class("ScriptError", rb_eException);
@@ -2057,6 +2487,7 @@ Init_Exception(void)
rb_define_method(rb_eNoMethodError, "private_call?", nometh_err_private_call_p, 0);
rb_eRuntimeError = rb_define_class("RuntimeError", rb_eStandardError);
+ rb_eFrozenError = rb_define_class("FrozenError", rb_eRuntimeError);
rb_eSecurityError = rb_define_class("SecurityError", rb_eException);
rb_eNoMemError = rb_define_class("NoMemoryError", rb_eException);
rb_eEncodingError = rb_define_class("EncodingError", rb_eStandardError);
@@ -2074,13 +2505,17 @@ Init_Exception(void)
rb_define_method(rb_mWarning, "warn", rb_warning_s_warn, 1);
rb_extend_object(rb_mWarning, rb_mWarning);
+ /* :nodoc: */
+ rb_cWarningBuffer = rb_define_class_under(rb_mWarning, "buffer", rb_cString);
+ rb_define_method(rb_cWarningBuffer, "write", warning_write, -1);
+
rb_define_global_function("warn", rb_warn_m, -1);
- id_new = rb_intern_const("new");
id_cause = rb_intern_const("cause");
id_message = rb_intern_const("message");
id_backtrace = rb_intern_const("backtrace");
id_name = rb_intern_const("name");
+ id_key = rb_intern_const("key");
id_args = rb_intern_const("args");
id_receiver = rb_intern_const("receiver");
id_private_call_p = rb_intern_const("private_call?");
@@ -2089,7 +2524,10 @@ Init_Exception(void)
id_errno = rb_intern_const("errno");
id_i_path = rb_intern_const("@path");
id_warn = rb_intern_const("warn");
+ id_top = rb_intern_const("top");
+ id_bottom = rb_intern_const("bottom");
id_iseq = rb_make_internal_id();
+ id_recv = rb_make_internal_id();
}
void
@@ -2296,44 +2734,104 @@ rb_mod_syserr_fail_str(VALUE mod, int e, VALUE mesg)
rb_exc_raise(exc);
}
+static void
+syserr_warning(VALUE mesg, int err)
+{
+ rb_str_set_len(mesg, RSTRING_LEN(mesg)-1);
+ rb_str_catf(mesg, ": %s\n", strerror(err));
+ rb_write_warning_str(mesg);
+}
+
+#if 0
void
-rb_sys_warning(const char *fmt, ...)
+rb_sys_warn(const char *fmt, ...)
{
- VALUE mesg;
- va_list args;
- int errno_save;
+ if (!NIL_P(ruby_verbose)) {
+ int errno_save = errno;
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, errno_save);
+ }
+ errno = errno_save;
+ }
+}
- errno_save = errno;
+void
+rb_syserr_warn(int err, const char *fmt, ...)
+{
+ if (!NIL_P(ruby_verbose)) {
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, err);
+ }
+ }
+}
- if (!RTEST(ruby_verbose)) return;
+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;
+ }
+}
- va_start(args, fmt);
- mesg = warning_string(0, fmt, args);
- va_end(args);
- rb_str_set_len(mesg, RSTRING_LEN(mesg)-1);
- rb_str_catf(mesg, ": %s\n", strerror(errno_save));
- rb_write_warning_str(mesg);
- errno = errno_save;
+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);
+ }
+ }
}
+#endif
void
-rb_sys_enc_warning(rb_encoding *enc, const char *fmt, ...)
+rb_sys_warning(const char *fmt, ...)
{
- VALUE mesg;
- va_list args;
- int errno_save;
+ if (RTEST(ruby_verbose)) {
+ int errno_save = errno;
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, errno_save);
+ }
+ errno = errno_save;
+ }
+}
- errno_save = errno;
+#if 0
+void
+rb_syserr_warning(int err, const char *fmt, ...)
+{
+ if (RTEST(ruby_verbose)) {
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, err);
+ }
+ }
+}
+#endif
- if (!RTEST(ruby_verbose)) return;
+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;
+ }
+}
- va_start(args, fmt);
- mesg = warning_string(enc, fmt, args);
- va_end(args);
- rb_str_set_len(mesg, RSTRING_LEN(mesg)-1);
- rb_str_catf(mesg, ": %s\n", strerror(errno_save));
- rb_write_warning_str(mesg);
- errno = errno_save;
+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);
+ }
+ }
}
void
@@ -2348,7 +2846,7 @@ rb_load_fail(VALUE path, const char *err)
void
rb_error_frozen(const char *what)
{
- rb_raise(rb_eRuntimeError, "can't modify frozen %s", what);
+ rb_raise(rb_eFrozenError, "can't modify frozen %s", what);
}
void
@@ -2361,11 +2859,11 @@ rb_error_frozen_object(VALUE frozen_obj)
VALUE path = rb_ary_entry(debug_info, 0);
VALUE line = rb_ary_entry(debug_info, 1);
- rb_raise(rb_eRuntimeError, "can't modify frozen %"PRIsVALUE", created at %"PRIsVALUE":%"PRIsVALUE,
+ rb_raise(rb_eFrozenError, "can't modify frozen %"PRIsVALUE", created at %"PRIsVALUE":%"PRIsVALUE,
CLASS_OF(frozen_obj), path, line);
}
else {
- rb_raise(rb_eRuntimeError, "can't modify frozen %"PRIsVALUE,
+ rb_raise(rb_eFrozenError, "can't modify frozen %"PRIsVALUE,
CLASS_OF(frozen_obj));
}
}
@@ -2412,3 +2910,7 @@ Init_syserr(void)
#undef defined_error
#undef undefined_error
}
+
+/*!
+ * \}
+ */
diff --git a/eval.c b/eval.c
index dcba1e16d0..47c9cac2b7 100644
--- a/eval.c
+++ b/eval.c
@@ -17,7 +17,12 @@
#include "gc.h"
#include "ruby/vm.h"
#include "vm_core.h"
+#include "mjit.h"
+#include "probes.h"
#include "probes_helper.h"
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
NORETURN(void rb_raise_jump(VALUE, VALUE));
@@ -25,9 +30,8 @@ VALUE rb_eLocalJumpError;
VALUE rb_eSysStackError;
ID ruby_static_id_signo, ruby_static_id_status;
-static ID id_cause;
-#define id_signo ruby_static_id_signo
-#define id_status ruby_static_id_status
+extern ID ruby_static_id_cause;
+#define id_cause ruby_static_id_cause
#define exception_error GET_VM()->special_exceptions[ruby_error_reenter]
@@ -38,35 +42,46 @@ static ID id_cause;
(!SPECIAL_CONST_P(obj) && \
(BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE))
-/* Initializes the Ruby VM and builtin libraries.
+/*!
+ * Initializes the Ruby VM and builtin libraries.
* @retval 0 if succeeded.
* @retval non-zero an error occurred.
*/
int
ruby_setup(void)
{
- int state;
+ enum ruby_tag_type state;
if (GET_VM())
return 0;
ruby_init_stack((void *)&state);
+
+ /*
+ * Disable THP early before mallocs happen because we want this to
+ * affect as many future pages as possible for CoW-friendliness
+ */
+#if defined(__linux__) && defined(PR_SET_THP_DISABLE)
+ prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0);
+#endif
Init_BareVM();
Init_heap();
+ rb_vm_encoded_insn_data_table_init();
Init_vm_objects();
- PUSH_TAG();
- if ((state = EXEC_TAG()) == 0) {
+ EC_PUSH_TAG(GET_EC());
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
rb_call_inits();
ruby_prog_init();
GET_VM()->running = 1;
}
- POP_TAG();
+ EC_POP_TAG();
return state;
}
-/* Calls ruby_setup() and check error.
+/*!
+ * Calls ruby_setup() and check error.
*
* Prints errors and calls exit(3) if an error occurred.
*/
@@ -76,7 +91,7 @@ ruby_init(void)
int state = ruby_setup();
if (state) {
if (RTEST(ruby_debug))
- error_print(GET_THREAD());
+ error_print(GET_EC());
exit(EXIT_FAILURE);
}
}
@@ -94,12 +109,12 @@ ruby_init(void)
void *
ruby_options(int argc, char **argv)
{
- int state;
+ enum ruby_tag_type state;
void *volatile iseq = 0;
ruby_init_stack((void *)&iseq);
- PUSH_TAG();
- if ((state = EXEC_TAG()) == 0) {
+ EC_PUSH_TAG(GET_EC());
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
SAVE_ROOT_JMPBUF(GET_THREAD(), iseq = ruby_process_options(argc, argv));
}
else {
@@ -107,18 +122,18 @@ ruby_options(int argc, char **argv)
state = error_handle(state);
iseq = (void *)INT2FIX(state);
}
- POP_TAG();
+ EC_POP_TAG();
return iseq;
}
static void
ruby_finalize_0(void)
{
- PUSH_TAG();
- if (EXEC_TAG() == 0) {
+ EC_PUSH_TAG(GET_EC());
+ if (EC_EXEC_TAG() == TAG_NONE) {
rb_trap_exit();
}
- POP_TAG();
+ EC_POP_TAG();
rb_exec_end_proc();
rb_clear_trace_func();
}
@@ -127,7 +142,7 @@ static void
ruby_finalize_1(void)
{
ruby_sig_finalize();
- GET_THREAD()->errinfo = Qnil;
+ GET_EC()->errinfo = Qnil;
rb_gc_call_finalizer_at_exit();
}
@@ -167,13 +182,14 @@ ruby_cleanup(volatile int ex)
rb_threadptr_interrupt(th);
rb_threadptr_check_signal(th);
- TH_PUSH_TAG(th);
- if ((state = EXEC_TAG()) == 0) {
- SAVE_ROOT_JMPBUF(th, { RUBY_VM_CHECK_INTS(th); });
+ EC_PUSH_TAG(th->ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ SAVE_ROOT_JMPBUF(th, { RUBY_VM_CHECK_INTS(th->ec); });
step_0: step++;
- errs[1] = th->errinfo;
- th->safe_level = 0;
+ errs[1] = th->ec->errinfo;
+ if (THROW_DATA_P(th->ec->errinfo)) th->ec->errinfo = Qnil;
+ rb_set_safe_level_force(0);
ruby_init_stack(&errs[STACK_UPPER(errs, 0, 1)]);
SAVE_ROOT_JMPBUF(th, ruby_finalize_0());
@@ -182,7 +198,7 @@ ruby_cleanup(volatile int ex)
/* protect from Thread#raise */
th->status = THREAD_KILLED;
- errs[0] = th->errinfo;
+ errs[0] = th->ec->errinfo;
SAVE_ROOT_JMPBUF(th, rb_thread_terminate_all());
}
else {
@@ -192,7 +208,7 @@ ruby_cleanup(volatile int ex)
}
if (ex == 0) ex = state;
}
- th->errinfo = errs[1];
+ th->ec->errinfo = errs[1];
sysex = error_handle(ex);
state = 0;
@@ -201,7 +217,7 @@ ruby_cleanup(volatile int ex)
if (!RTEST(err)) continue;
- /* th->errinfo contains a NODE while break'ing */
+ /* th->ec->errinfo contains a NODE while break'ing */
if (THROW_DATA_P(err)) continue;
if (rb_obj_is_kind_of(err, rb_eSystemExit)) {
@@ -218,11 +234,13 @@ ruby_cleanup(volatile int ex)
}
}
+ mjit_finish(TRUE); /* We still need ISeqs here. */
+
ruby_finalize_1();
/* unlock again if finalizer took mutexes. */
rb_threadptr_unlock_all_locking_mutexes(GET_THREAD());
- TH_POP_TAG();
+ EC_POP_TAG();
rb_thread_stop_timer_thread();
ruby_vm_destruct(GET_VM());
if (state) ruby_default_signal(state);
@@ -235,17 +253,17 @@ ruby_exec_internal(void *n)
{
volatile int state;
rb_iseq_t *iseq = (rb_iseq_t *)n;
- rb_thread_t *th = GET_THREAD();
+ rb_thread_t * volatile th = GET_THREAD();
if (!n) return 0;
- TH_PUSH_TAG(th);
- if ((state = EXEC_TAG()) == 0) {
+ EC_PUSH_TAG(th->ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
SAVE_ROOT_JMPBUF(th, {
rb_iseq_eval_main(iseq);
});
}
- TH_POP_TAG();
+ EC_POP_TAG();
return state;
}
@@ -392,8 +410,14 @@ rb_mod_s_constants(int argc, VALUE *argv, VALUE mod)
return rb_const_list(data);
}
+/*!
+ * Asserts that \a klass is not a frozen class.
+ * \param[in] klass a \c Module object
+ * \exception RuntimeError if \a klass is not a class or frozen.
+ * \ingroup class
+ */
void
-rb_frozen_class_p(VALUE klass)
+rb_class_modify_check(VALUE klass)
{
if (SPECIAL_CONST_P(klass)) {
noclass:
@@ -434,9 +458,9 @@ rb_frozen_class_p(VALUE klass)
}
}
-NORETURN(static void rb_longjmp(int, volatile VALUE, VALUE));
+NORETURN(static void rb_longjmp(rb_execution_context_t *, int, volatile VALUE, VALUE));
static VALUE get_errinfo(void);
-static VALUE get_thread_errinfo(rb_thread_t *th);
+static VALUE get_ec_errinfo(const rb_execution_context_t *ec);
static VALUE
exc_setup_cause(VALUE exc, VALUE cause)
@@ -457,80 +481,103 @@ exc_setup_cause(VALUE exc, VALUE 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);
+ }
}
return exc;
}
-static inline int
-sysstack_error_p(VALUE exc)
+static inline VALUE
+exc_setup_message(const rb_execution_context_t *ec, VALUE mesg, VALUE *cause)
{
- return exc == sysstack_error || (!SPECIAL_CONST_P(exc) && RBASIC_CLASS(exc) == rb_eSysStackError);
-}
-
-static void
-setup_exception(rb_thread_t *th, int tag, volatile VALUE mesg, VALUE cause)
-{
- VALUE e;
- const char *file = 0;
- int line;
int nocause = 0;
+ int nocircular = 0;
if (NIL_P(mesg)) {
- mesg = th->errinfo;
- if (INTERNAL_EXCEPTION_P(mesg)) TH_JUMP_TAG(th, TAG_FATAL);
+ 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;
+ nocircular = 1;
}
- if (cause != Qundef) {
- exc_setup_cause(mesg, cause);
+ if (*cause == Qundef) {
+ if (nocause) {
+ *cause = Qnil;
+ nocircular = 1;
+ }
+ else if (!rb_ivar_defined(mesg, id_cause)) {
+ *cause = get_ec_errinfo(ec);
+ }
+ else {
+ nocircular = 1;
+ }
}
- else if (nocause) {
- exc_setup_cause(mesg, Qnil);
+ else if (!NIL_P(*cause) && !rb_obj_is_kind_of(*cause, rb_eException)) {
+ rb_raise(rb_eTypeError, "exception object expected");
}
- else if (!rb_ivar_defined(mesg, id_cause)) {
- exc_setup_cause(mesg, get_thread_errinfo(th));
+
+ if (!nocircular && !NIL_P(*cause) && *cause != Qundef && *cause != mesg) {
+ VALUE c = *cause;
+ while (!NIL_P(c = rb_attr_get(c, id_cause))) {
+ if (c == mesg) {
+ rb_raise(rb_eArgError, "circular causes");
+ }
+ }
}
+ return mesg;
+}
- file = rb_source_loc(&line);
- if (file && !NIL_P(mesg)) {
- VALUE at;
- if (sysstack_error_p(mesg)) {
- if (NIL_P(rb_attr_get(mesg, idBt))) {
- at = rb_vm_backtrace_object();
- if (mesg == sysstack_error) {
- mesg = ruby_vm_sysstack_error_copy();
+static void
+setup_exception(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE cause)
+{
+ VALUE e;
+ const char *file = 0;
+ int line;
+
+ file = rb_source_location_cstr(&line);
+ 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);
}
- rb_ivar_set(mesg, idBt, at);
- rb_ivar_set(mesg, idBt_locations, at);
}
- }
- else if (NIL_P(get_backtrace(mesg))) {
- at = rb_vm_backtrace_object();
- if (OBJ_FROZEN(mesg)) {
- mesg = rb_obj_dup(mesg);
+ if (cause != Qundef && !THROW_DATA_P(cause)) {
+ exc_setup_cause(mesg, cause);
}
- rb_ivar_set(mesg, idBt_locations, at);
- set_backtrace(mesg, at);
+ 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 (state) goto fatal;
}
if (!NIL_P(mesg)) {
- th->errinfo = mesg;
+ ec->errinfo = mesg;
}
- if (RTEST(ruby_debug) && !NIL_P(e = th->errinfo) &&
+ if (RTEST(ruby_debug) && !NIL_P(e = ec->errinfo) &&
!rb_obj_is_kind_of(e, rb_eSystemExit)) {
- int status;
+ enum ruby_tag_type state;
mesg = e;
- TH_PUSH_TAG(th);
- if ((status = EXEC_TAG()) == 0) {
- th->errinfo = Qnil;
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ ec->errinfo = Qnil;
e = rb_obj_as_string(mesg);
- th->errinfo = mesg;
+ ec->errinfo = mesg;
if (file && line) {
e = rb_sprintf("Exception `%"PRIsVALUE"' at %s:%d - %"PRIsVALUE"\n",
rb_obj_class(mesg), file, line, e);
@@ -545,33 +592,35 @@ setup_exception(rb_thread_t *th, int tag, volatile VALUE mesg, VALUE cause)
}
warn_print_str(e);
}
- TH_POP_TAG();
- if (status == TAG_FATAL && th->errinfo == exception_error) {
- th->errinfo = mesg;
+ EC_POP_TAG();
+ if (state == TAG_FATAL && ec->errinfo == exception_error) {
+ ec->errinfo = mesg;
}
- else if (status) {
- rb_threadptr_reset_raised(th);
- TH_JUMP_TAG(th, status);
+ else if (state) {
+ rb_ec_reset_raised(ec);
+ EC_JUMP_TAG(ec, state);
}
}
- if (rb_threadptr_set_raised(th)) {
- th->errinfo = exception_error;
- rb_threadptr_reset_raised(th);
- TH_JUMP_TAG(th, TAG_FATAL);
+ if (rb_ec_set_raised(ec)) {
+ fatal:
+ ec->errinfo = exception_error;
+ rb_ec_reset_raised(ec);
+ EC_JUMP_TAG(ec, TAG_FATAL);
}
if (tag != TAG_FATAL) {
- RUBY_DTRACE_HOOK(RAISE, rb_obj_classname(th->errinfo));
- EXEC_EVENT_HOOK(th, RUBY_EVENT_RAISE, th->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);
}
}
+/*! \private */
void
-rb_threadptr_setup_exception(rb_thread_t *th, VALUE mesg, VALUE cause)
+rb_ec_setup_exception(const rb_execution_context_t *ec, VALUE mesg, VALUE cause)
{
if (cause == Qundef) {
- cause = get_thread_errinfo(th);
+ cause = get_ec_errinfo(ec);
}
if (cause != mesg) {
rb_ivar_set(mesg, id_cause, cause);
@@ -579,41 +628,59 @@ rb_threadptr_setup_exception(rb_thread_t *th, VALUE mesg, VALUE cause)
}
static void
-rb_longjmp(int tag, volatile VALUE mesg, VALUE cause)
+rb_longjmp(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE cause)
{
- rb_thread_t *th = GET_THREAD();
- setup_exception(th, tag, mesg, cause);
- rb_thread_raised_clear(th);
- TH_JUMP_TAG(th, tag);
+ mesg = exc_setup_message(ec, mesg, &cause);
+ setup_exception(ec, tag, mesg, cause);
+ rb_ec_raised_clear(ec);
+ EC_JUMP_TAG(ec, tag);
}
static VALUE make_exception(int argc, const VALUE *argv, int isstr);
+/*!
+ * Raises an exception in the current thread.
+ * \param[in] mesg an Exception class or an \c Exception object.
+ * \exception always raises an instance of the given exception class or
+ * the given \c Exception object.
+ * \ingroup exception
+ */
void
rb_exc_raise(VALUE mesg)
{
if (!NIL_P(mesg)) {
mesg = make_exception(1, &mesg, FALSE);
}
- rb_longjmp(TAG_RAISE, mesg, Qundef);
+ rb_longjmp(GET_EC(), TAG_RAISE, mesg, Qundef);
}
+/*!
+ * Raises a fatal error in the current thread.
+ *
+ * Same as rb_exc_raise() but raises a fatal error, which Ruby codes
+ * cannot rescue.
+ * \ingroup exception
+ */
void
rb_exc_fatal(VALUE mesg)
{
if (!NIL_P(mesg)) {
mesg = make_exception(1, &mesg, FALSE);
}
- rb_longjmp(TAG_FATAL, mesg, Qnil);
+ rb_longjmp(GET_EC(), TAG_FATAL, mesg, Qnil);
}
+/*!
+ * Raises an \c Interrupt exception.
+ * \ingroup exception
+ */
void
rb_interrupt(void)
{
- rb_raise(rb_eInterrupt, "%s", "");
+ rb_exc_raise(rb_exc_new(rb_eInterrupt, 0, 0));
}
-enum {raise_opt_cause, raise_max_opt};
+enum {raise_opt_cause, raise_max_opt}; /*< \private */
static int
extract_raise_opts(int argc, const VALUE *argv, VALUE *opts)
@@ -640,11 +707,11 @@ extract_raise_opts(int argc, const VALUE *argv, VALUE *opts)
/*
* call-seq:
* raise
- * raise(string)
- * raise(exception [, string [, array]])
+ * raise(string, cause: $!)
+ * raise(exception [, string [, array]], cause: $!)
* fail
- * fail(string)
- * fail(exception [, string [, array]])
+ * fail(string, cause: $!)
+ * fail(exception [, string [, array]], cause: $!)
*
* With no arguments, raises the exception in <code>$!</code> or raises
* a <code>RuntimeError</code> if <code>$!</code> is +nil+.
@@ -659,6 +726,11 @@ extract_raise_opts(int argc, const VALUE *argv, VALUE *opts)
*
* raise "Failed to create socket"
* raise ArgumentError, "No parameters", caller
+ *
+ * The +cause+ of the generated exception is automatically set to the
+ * "current" exception (<code>$!</code>) if any. An alternative
+ * value, either an +Exception+ object or +nil+, can be specified via
+ * the +:cause+ argument.
*/
static VALUE
@@ -680,7 +752,7 @@ rb_f_raise(int argc, VALUE *argv)
}
rb_raise_jump(rb_make_exception(argc, argv), *cause);
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
static VALUE
@@ -712,7 +784,6 @@ make_exception(int argc, const VALUE *argv, int isstr)
exc = argv[0];
n = 1;
exception_call:
- if (sysstack_error_p(exc)) return exc;
mesg = rb_check_funcall(exc, idException, n, argv+1);
if (mesg == Qundef) {
rb_raise(rb_eTypeError, "exception class/object expected");
@@ -732,45 +803,77 @@ make_exception(int argc, const VALUE *argv, int isstr)
return mesg;
}
+/*!
+ * Make an \c Exception object from the list of arguments in a manner
+ * similar to \c Kernel\#raise.
+ *
+ * \param[in] argc the number of arguments
+ * \param[in] argv a pointer to the array of arguments.
+ *
+ * The first form of this function takes a \c String argument. Then
+ * it returns a \c RuntimeError whose error message is the given value.
+ *
+ * The second from of this function takes an \c Exception object. Then
+ * it just returns the given value.
+ *
+ * The last form takes an exception class, an optional error message and
+ * an optional array of backtrace. Then it passes the optional arguments
+ * to \c #exception method of the exception class.
+ *
+ * \return the exception object, or \c Qnil if \c argc is 0.
+ * \ingroup exception
+ */
VALUE
rb_make_exception(int argc, const VALUE *argv)
{
return make_exception(argc, argv, TRUE);
}
+/*! \private
+ * \todo can be static?
+ */
void
rb_raise_jump(VALUE mesg, VALUE cause)
{
- rb_thread_t *th = GET_THREAD();
- const rb_control_frame_t *cfp = th->cfp;
+ rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = ec->cfp;
const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
VALUE klass = me->owner;
VALUE self = cfp->self;
ID mid = me->called_id;
- rb_vm_pop_frame(th);
- EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, self, me->def->original_id, mid, klass, Qnil);
-
- setup_exception(th, TAG_RAISE, mesg, cause);
+ rb_vm_pop_frame(ec);
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, self, me->def->original_id, mid, klass, Qnil);
- rb_thread_raised_clear(th);
- TH_JUMP_TAG(th, TAG_RAISE);
+ rb_longjmp(ec, TAG_RAISE, mesg, cause);
}
+/*!
+ * Continues the exception caught by rb_protect() and rb_eval_string_protect().
+ *
+ * This function never return to the caller.
+ * \param[in] the value of \c *state which the protect function has set to the
+ * their last parameter.
+ * \ingroup exception
+ */
void
rb_jump_tag(int tag)
{
if (UNLIKELY(tag < TAG_RETURN || tag > TAG_FATAL)) {
unknown_longjmp_status(tag);
}
- JUMP_TAG(tag);
+ EC_JUMP_TAG(GET_EC(), tag);
}
+/*! Determines if the current method is given a block.
+ * \retval zero if not given
+ * \retval non-zero if given
+ * \ingroup defmethod
+ */
int
rb_block_given_p(void)
{
- rb_thread_t *th = GET_THREAD();
- if (rb_vm_frame_block_handler(th->cfp) == VM_BLOCK_HANDLER_NONE) {
+ if (rb_vm_frame_block_handler(GET_EC()->cfp) == VM_BLOCK_HANDLER_NONE) {
return FALSE;
}
else {
@@ -778,14 +881,13 @@ rb_block_given_p(void)
}
}
-int
-rb_iterator_p(void)
-{
- return rb_block_given_p();
-}
-
VALUE rb_eThreadError;
+/*! Declares that the current method needs a block.
+ *
+ * Raises a \c LocalJumpError if not given a block.
+ * \ingroup defmethod
+ */
void
rb_need_block(void)
{
@@ -794,19 +896,23 @@ rb_need_block(void)
}
}
-VALUE
-rb_rescue2(VALUE (* b_proc) (ANYARGS), VALUE data1,
- VALUE (* r_proc) (ANYARGS), VALUE data2, ...)
+/*!
+ * \copydoc rb_rescue2
+ * \param[in] args exception classes, terminated by 0.
+ */
+static VALUE
+rb_vrescue2(VALUE (* b_proc) (VALUE), VALUE data1,
+ VALUE (* r_proc) (VALUE, VALUE), VALUE data2,
+ va_list args)
{
- int state;
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp;
+ enum ruby_tag_type state;
+ rb_execution_context_t * volatile ec = GET_EC();
+ rb_control_frame_t *volatile cfp = ec->cfp;
volatile VALUE result = Qfalse;
- volatile VALUE e_info = th->errinfo;
- va_list args;
+ volatile VALUE e_info = ec->errinfo;
- TH_PUSH_TAG(th);
- if ((state = TH_EXEC_TAG()) == 0) {
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
retry_entry:
result = (*b_proc) (data1);
}
@@ -814,44 +920,88 @@ rb_rescue2(VALUE (* b_proc) (ANYARGS), VALUE data1,
/* escape from r_proc */
if (state == TAG_RETRY) {
state = 0;
- th->errinfo = Qnil;
+ ec->errinfo = Qnil;
result = Qfalse;
goto retry_entry;
}
}
else {
- rb_vm_rewind_cfp(th, cfp);
+ rb_vm_rewind_cfp(ec, cfp);
if (state == TAG_RAISE) {
int handle = FALSE;
VALUE eclass;
- va_init_list(args, data2);
while ((eclass = va_arg(args, VALUE)) != 0) {
- if (rb_obj_is_kind_of(th->errinfo, eclass)) {
+ if (rb_obj_is_kind_of(ec->errinfo, eclass)) {
handle = TRUE;
break;
}
}
- va_end(args);
if (handle) {
result = Qnil;
state = 0;
if (r_proc) {
- result = (*r_proc) (data2, th->errinfo);
+ result = (*r_proc) (data2, ec->errinfo);
}
- th->errinfo = e_info;
+ ec->errinfo = e_info;
}
}
}
- TH_POP_TAG();
+ EC_POP_TAG();
if (state)
- TH_JUMP_TAG(th, state);
+ EC_JUMP_TAG(ec, state);
return result;
}
+/*! An equivalent of \c rescue clause.
+ *
+ * Equivalent to <code>begin .. rescue err_type .. end</code>
+ *
+ * \param[in] b_proc a function which potentially raises an exception.
+ * \param[in] data1 the argument of \a b_proc
+ * \param[in] r_proc a function which rescues an exception in \a b_proc.
+ * \param[in] data2 the first argument of \a r_proc
+ * \param[in] ... 1 or more exception classes. Must be terminated by \c (VALUE)0.
+ *
+ * First it calls the function \a b_proc, with \a data1 as the argument.
+ * When \a b_proc raises an exception, it calls \a r_proc with \a data2 and
+ * the exception object if the exception is a kind of one of the given
+ * exception classes.
+ *
+ * \return the return value of \a b_proc if no exception occurs,
+ * or the return value of \a r_proc if otherwise.
+ * \sa rb_rescue
+ * \sa rb_ensure
+ * \sa rb_protect
+ * \ingroup exception
+ */
+VALUE
+rb_rescue2(VALUE (* b_proc) (ANYARGS), VALUE data1,
+ VALUE (* r_proc) (ANYARGS), VALUE data2, ...)
+{
+ va_list ap;
+ va_start(ap, data2);
+ return rb_vrescue2((VALUE (*)(VALUE))b_proc, data1, (VALUE (*)(VALUE, VALUE))r_proc, data2, ap);
+ va_end(ap);
+}
+
+/*! An equivalent of \c rescue clause.
+ *
+ * Equivalent to <code>begin .. rescue .. end</code>.
+ *
+ * It is same as
+ * \code{cpp}
+ * rb_rescue2(b_proc, data1, r_proc, data2, rb_eStandardError, (VALUE)0);
+ * \endcode
+ *
+ * \sa rb_rescue2
+ * \sa rb_ensure
+ * \sa rb_protect
+ * \ingroup exception
+ */
VALUE
rb_rescue(VALUE (* b_proc)(ANYARGS), VALUE data1,
VALUE (* r_proc)(ANYARGS), VALUE data2)
@@ -860,67 +1010,98 @@ rb_rescue(VALUE (* b_proc)(ANYARGS), VALUE data1,
(VALUE)0);
}
+/*! Protects a function call from potential global escapes from the function.
+ *
+ * Such global escapes include exceptions, \c Kernel\#throw, \c break in
+ * an iterator, for example.
+ * It first calls the function func with arg as the argument.
+ * If no exception occurred during func, it returns the result of func and
+ * *state is zero.
+ * Otherwise, it returns Qnil and sets *state to nonzero.
+ * If state is NULL, it is not set in both cases.
+ *
+ * You have to clear the error info with rb_set_errinfo(Qnil) when
+ * ignoring the caught exception.
+ * \ingroup exception
+ * \sa rb_rescue
+ * \sa rb_rescue2
+ * \sa rb_ensure
+ */
VALUE
-rb_protect(VALUE (* proc) (VALUE), VALUE data, int * state)
+rb_protect(VALUE (* proc) (VALUE), VALUE data, int *pstate)
{
volatile VALUE result = Qnil;
- volatile int status;
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp;
+ volatile enum ruby_tag_type state;
+ rb_execution_context_t * volatile ec = GET_EC();
+ rb_control_frame_t *volatile cfp = ec->cfp;
struct rb_vm_protect_tag protect_tag;
rb_jmpbuf_t org_jmpbuf;
- protect_tag.prev = th->protect_tag;
+ protect_tag.prev = ec->protect_tag;
- TH_PUSH_TAG(th);
- th->protect_tag = &protect_tag;
- MEMCPY(&org_jmpbuf, &(th)->root_jmpbuf, rb_jmpbuf_t, 1);
- if ((status = TH_EXEC_TAG()) == 0) {
- SAVE_ROOT_JMPBUF(th, result = (*proc) (data));
+ EC_PUSH_TAG(ec);
+ ec->protect_tag = &protect_tag;
+ MEMCPY(&org_jmpbuf, &rb_ec_thread_ptr(ec)->root_jmpbuf, rb_jmpbuf_t, 1);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ SAVE_ROOT_JMPBUF(rb_ec_thread_ptr(ec), result = (*proc) (data));
}
else {
- rb_vm_rewind_cfp(th, cfp);
- }
- MEMCPY(&(th)->root_jmpbuf, &org_jmpbuf, rb_jmpbuf_t, 1);
- th->protect_tag = protect_tag.prev;
- TH_POP_TAG();
-
- if (state) {
- *state = status;
+ rb_vm_rewind_cfp(ec, cfp);
}
+ MEMCPY(&rb_ec_thread_ptr(ec)->root_jmpbuf, &org_jmpbuf, rb_jmpbuf_t, 1);
+ ec->protect_tag = protect_tag.prev;
+ EC_POP_TAG();
+ if (pstate != NULL) *pstate = state;
return result;
}
+/*!
+ * An equivalent to \c ensure clause.
+ *
+ * Equivalent to <code>begin .. ensure .. end</code>.
+ *
+ * Calls the function \a b_proc with \a data1 as the argument,
+ * then calls \a e_proc with \a data2 when execution terminated.
+ * \return The return value of \a b_proc if no exception occurred,
+ * or \c Qnil if otherwise.
+ * \sa rb_rescue
+ * \sa rb_rescue2
+ * \sa rb_protect
+ * \ingroup exception
+ */
VALUE
rb_ensure(VALUE (*b_proc)(ANYARGS), VALUE data1, VALUE (*e_proc)(ANYARGS), VALUE data2)
{
int state;
volatile VALUE result = Qnil;
- volatile VALUE errinfo;
- rb_thread_t *const th = GET_THREAD();
+ VALUE errinfo;
+ rb_execution_context_t * volatile ec = GET_EC();
rb_ensure_list_t ensure_list;
ensure_list.entry.marker = 0;
ensure_list.entry.e_proc = e_proc;
ensure_list.entry.data2 = data2;
- ensure_list.next = th->ensure_list;
- th->ensure_list = &ensure_list;
- TH_PUSH_TAG(th);
- if ((state = EXEC_TAG()) == 0) {
+ ensure_list.next = ec->ensure_list;
+ ec->ensure_list = &ensure_list;
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
result = (*b_proc) (data1);
}
- TH_POP_TAG();
- errinfo = th->errinfo;
- th->ensure_list=ensure_list.next;
+ EC_POP_TAG();
+ errinfo = ec->errinfo;
+ if (!NIL_P(errinfo) && !RB_TYPE_P(errinfo, T_OBJECT)) {
+ ec->errinfo = Qnil;
+ }
+ ec->ensure_list=ensure_list.next;
(*ensure_list.entry.e_proc)(ensure_list.entry.data2);
- th->errinfo = errinfo;
+ ec->errinfo = errinfo;
if (state)
- TH_JUMP_TAG(th, state);
+ EC_JUMP_TAG(ec, state);
return result;
}
static ID
-frame_func_id(rb_control_frame_t *cfp)
+frame_func_id(const rb_control_frame_t *cfp)
{
const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
@@ -945,24 +1126,47 @@ frame_called_id(rb_control_frame_t *cfp)
}
}
+/*!
+ * The original name of the current method.
+ *
+ * The function returns the original name of the method even if
+ * an alias of the method is called.
+ * The function can also return 0 if it is not in a method. This
+ * case can happen in a toplevel of a source file, for example.
+ *
+ * \returns the ID of the name or 0
+ * \sa rb_frame_callee
+ * \ingroup defmethod
+ */
ID
rb_frame_this_func(void)
{
- return frame_func_id(GET_THREAD()->cfp);
+ return frame_func_id(GET_EC()->cfp);
}
+/*!
+ * The name of the current method.
+ *
+ * The function returns the alias if an alias of the method is called.
+ * The function can also return 0 if it is not in a method. This
+ * case can happen in a toplevel of a source file, for example.
+ *
+ * \returns the ID of the name or 0.
+ * \sa rb_frame_this_func
+ * \ingroup defmethod
+ */
ID
rb_frame_callee(void)
{
- return frame_called_id(GET_THREAD()->cfp);
+ return frame_called_id(GET_EC()->cfp);
}
static rb_control_frame_t *
-previous_frame(rb_thread_t *th)
+previous_frame(const rb_execution_context_t *ec)
{
- rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
+ rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(ec->cfp);
/* check if prev_cfp can be accessible */
- if ((void *)(th->stack + th->stack_size) == (void *)(prev_cfp)) {
+ if ((void *)(ec->vm_stack + ec->vm_stack_size) == (void *)(prev_cfp)) {
return 0;
}
return prev_cfp;
@@ -971,7 +1175,7 @@ previous_frame(rb_thread_t *th)
static ID
prev_frame_callee(void)
{
- rb_control_frame_t *prev_cfp = previous_frame(GET_THREAD());
+ rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
if (!prev_cfp) return 0;
return frame_called_id(prev_cfp);
}
@@ -979,21 +1183,27 @@ prev_frame_callee(void)
static ID
prev_frame_func(void)
{
- rb_control_frame_t *prev_cfp = previous_frame(GET_THREAD());
+ rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
if (!prev_cfp) return 0;
return frame_func_id(prev_cfp);
}
+/*!
+ * \private
+ * Returns the ID of the last method in the call stack.
+ * \sa rb_frame_this_func
+ * \ingroup defmethod
+ */
ID
rb_frame_last_func(void)
{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp;
+ const rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = ec->cfp;
ID mid;
while (!(mid = frame_func_id(cfp)) &&
(cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp),
- !RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp)));
+ !RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)));
return mid;
}
@@ -1114,6 +1324,22 @@ hidden_identity_hash_new(void)
return hash;
}
+static VALUE
+refinement_superclass(VALUE superclass)
+{
+ if (RB_TYPE_P(superclass, T_MODULE)) {
+ /* FIXME: Should ancestors of superclass be used here? */
+ return rb_include_class_new(superclass, rb_cBasicObject);
+ }
+ else {
+ return superclass;
+ }
+}
+
+/*!
+ * \private
+ * \todo can be static?
+ */
void
rb_using_refinement(rb_cref_t *cref, VALUE klass, VALUE module)
{
@@ -1141,6 +1367,7 @@ rb_using_refinement(rb_cref_t *cref, VALUE klass, VALUE module)
}
}
FL_SET(module, RMODULE_IS_OVERLAID);
+ superclass = refinement_superclass(superclass);
c = iclass = rb_include_class_new(module, superclass);
RCLASS_REFINED_CLASS(c) = klass;
@@ -1196,6 +1423,10 @@ using_module_recursive(const rb_cref_t *cref, VALUE klass)
rb_hash_foreach(refinements, using_refinement, (VALUE) cref);
}
+/*!
+ * \private
+ * \todo can be static?
+ */
void
rb_using_module(const rb_cref_t *cref, VALUE module)
{
@@ -1204,6 +1435,7 @@ rb_using_module(const rb_cref_t *cref, VALUE module)
rb_clear_method_cache_by_class(rb_cObject);
}
+/*! \private */
VALUE
rb_refinement_module_get_refined_class(VALUE module)
{
@@ -1230,6 +1462,7 @@ add_activated_refinement(VALUE activated_refinements,
}
}
FL_SET(refinement, RMODULE_IS_OVERLAID);
+ superclass = refinement_superclass(superclass);
c = iclass = rb_include_class_new(refinement, superclass);
RCLASS_REFINED_CLASS(c) = klass;
refinement = RCLASS_SUPER(refinement);
@@ -1259,7 +1492,7 @@ rb_mod_refine(VALUE module, VALUE klass)
id_refined_class, id_defined_at;
VALUE refinements, activated_refinements;
rb_thread_t *th = GET_THREAD();
- VALUE block_handler = rb_vm_frame_block_handler(th->cfp);
+ 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");
@@ -1284,8 +1517,9 @@ rb_mod_refine(VALUE module, VALUE klass)
}
refinement = rb_hash_lookup(refinements, klass);
if (NIL_P(refinement)) {
+ VALUE superclass = refinement_superclass(klass);
refinement = rb_module_new();
- RCLASS_SET_SUPER(refinement, klass);
+ RCLASS_SET_SUPER(refinement, superclass);
FL_SET(refinement, RMODULE_IS_REFINEMENT);
CONST_ID(id_refined_class, "__refined_class__");
rb_ivar_set(refinement, id_refined_class, klass);
@@ -1302,6 +1536,7 @@ static void
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";
}
@@ -1319,7 +1554,7 @@ ignored_block(VALUE module, const char *klass)
static VALUE
mod_using(VALUE self, VALUE module)
{
- rb_control_frame_t *prev_cfp = previous_frame(GET_THREAD());
+ rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
if (prev_frame_func()) {
rb_raise(rb_eRuntimeError,
@@ -1388,6 +1623,16 @@ rb_mod_s_used_modules(void)
return rb_funcall(ary, rb_intern("uniq"), 0);
}
+/*!
+ * Calls \c #initialize method of \a obj with the given arguments.
+ *
+ * It also forwards the given block to \c #initialize if given.
+ *
+ * \param[in] obj the receiver object
+ * \param[in] argc the number of arguments
+ * \param[in] argv a pointer to the array of arguments
+ * \ingroup object
+ */
void
rb_obj_call_init(VALUE obj, int argc, const VALUE *argv)
{
@@ -1395,6 +1640,12 @@ rb_obj_call_init(VALUE obj, int argc, const VALUE *argv)
rb_funcallv(obj, idInitialize, argc, argv);
}
+/*!
+ * Extend the object with the module.
+ *
+ * Same as \c Module\#extend_object.
+ * \ingroup class
+ */
void
rb_extend_object(VALUE obj, VALUE module)
{
@@ -1512,7 +1763,7 @@ static VALUE
top_using(VALUE self, VALUE module)
{
const rb_cref_t *cref = rb_vm_cref();
- rb_control_frame_t *prev_cfp = previous_frame(GET_THREAD());
+ rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
if (CREF_NEXT(cref) || (prev_cfp && rb_vm_frame_method_entry(prev_cfp))) {
rb_raise(rb_eRuntimeError, "main.using is permitted only at toplevel");
@@ -1525,10 +1776,10 @@ top_using(VALUE self, VALUE module)
}
static const VALUE *
-errinfo_place(rb_thread_t *th)
+errinfo_place(const rb_execution_context_t *ec)
{
- rb_control_frame_t *cfp = th->cfp;
- rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(th);
+ const rb_control_frame_t *cfp = ec->cfp;
+ 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)) {
@@ -1547,21 +1798,21 @@ errinfo_place(rb_thread_t *th)
}
static VALUE
-get_thread_errinfo(rb_thread_t *th)
+get_ec_errinfo(const rb_execution_context_t *ec)
{
- const VALUE *ptr = errinfo_place(th);
+ const VALUE *ptr = errinfo_place(ec);
if (ptr) {
return *ptr;
}
else {
- return th->errinfo;
+ return ec->errinfo;
}
}
static VALUE
get_errinfo(void)
{
- return get_thread_errinfo(GET_THREAD());
+ return get_ec_errinfo(GET_EC());
}
static VALUE
@@ -1570,45 +1821,33 @@ errinfo_getter(ID id)
return get_errinfo();
}
-#if 0
-static void
-errinfo_setter(VALUE val, ID id, VALUE *var)
-{
- if (!NIL_P(val) && !rb_obj_is_kind_of(val, rb_eException)) {
- rb_raise(rb_eTypeError, "assigning non-exception to $!");
- }
- else {
- const VALUE *ptr = errinfo_place(GET_THREAD());
- if (ptr) {
- *ptr = val;
- }
- else {
- rb_raise(rb_eRuntimeError, "errinfo_setter: not in rescue clause.");
- }
- }
-}
-#endif
-
+/*! The current exception in the current thread.
+ *
+ * Same as \c $! in Ruby.
+ * \return the current exception or \c Qnil
+ * \ingroup exception
+ */
VALUE
rb_errinfo(void)
{
- rb_thread_t *th = GET_THREAD();
- return th->errinfo;
+ return GET_EC()->errinfo;
}
+/*! Sets the current exception (\c $!) to the given value
+ *
+ * \param[in] err an \c Exception object or \c Qnil.
+ * \exception TypeError if \a err is neither an exception nor \c nil.
+ * \note this function does not raise the exception.
+ * Use \c rb_raise() when you want to raise.
+ * \ingroup exception
+ */
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 $!");
}
- GET_THREAD()->errinfo = err;
-}
-
-VALUE
-rb_rubylevel_errinfo(void)
-{
- return get_errinfo();
+ GET_EC()->errinfo = err;
}
static VALUE
@@ -1616,7 +1855,7 @@ errat_getter(ID id)
{
VALUE err = get_errinfo();
if (!NIL_P(err)) {
- return get_backtrace(err);
+ return rb_get_backtrace(err);
}
else {
return Qnil;
@@ -1745,8 +1984,8 @@ Init_eval(void)
rb_define_global_function("untrace_var", rb_f_untrace_var, -1); /* in variable.c */
rb_vm_register_special_exception(ruby_error_reenter, rb_eFatal, "exception reentered");
+ rb_vm_register_special_exception(ruby_error_stackfatal, rb_eFatal, "machine stack overflow in critical region");
id_signo = rb_intern_const("signo");
id_status = rb_intern_const("status");
- id_cause = rb_intern_const("cause");
}
diff --git a/eval_error.c b/eval_error.c
index cfb82d3a24..c2d33ef498 100644
--- a/eval_error.c
+++ b/eval_error.c
@@ -3,6 +3,10 @@
* included by eval.c
*/
+#define write_warn(str, x) \
+ (NIL_P(str) ? warn_print(x) : (void)rb_str_cat_cstr(str, x))
+#define write_warn2(str, x, l) \
+ (NIL_P(str) ? warn_print2(x, l) : (void)rb_str_cat(str, x, l))
#ifdef HAVE_BUILTIN___BUILTIN_CONSTANT_P
#define warn_print(x) RB_GNUC_EXTENSION_BLOCK( \
(__builtin_constant_p(x)) ? \
@@ -12,17 +16,20 @@
#else
#define warn_print(x) rb_write_error(x)
#endif
+
#define warn_print2(x,l) rb_write_error2((x),(l))
+
+#define write_warn_str(str,x) NIL_P(str) ? rb_write_error_str(x) : (void)rb_str_concat((str), (x))
#define warn_print_str(x) rb_write_error_str(x)
static VALUE error_pos_str(void);
static void
-error_pos(void)
+error_pos(const VALUE str)
{
- VALUE str = error_pos_str();
- if (!NIL_P(str)) {
- warn_print_str(str);
+ VALUE pos = error_pos_str();
+ if (!NIL_P(pos)) {
+ write_warn_str(str, pos);
}
}
@@ -32,7 +39,7 @@ error_pos_str(void)
int sourceline;
VALUE sourcefile = rb_source_location(&sourceline);
- if (sourcefile) {
+ if (!NIL_P(sourcefile)) {
ID caller_name;
if (sourceline == 0) {
return rb_sprintf("%"PRIsVALUE": ", sourcefile);
@@ -49,23 +56,6 @@ error_pos_str(void)
return Qnil;
}
-static VALUE
-get_backtrace(VALUE info)
-{
- if (NIL_P(info))
- return Qnil;
- info = rb_funcall(info, rb_intern("backtrace"), 0);
- if (NIL_P(info))
- return Qnil;
- return rb_check_backtrace(info);
-}
-
-VALUE
-rb_get_backtrace(VALUE info)
-{
- return get_backtrace(info);
-}
-
static void
set_backtrace(VALUE info, VALUE bt)
{
@@ -80,131 +70,270 @@ set_backtrace(VALUE info, VALUE bt)
bt = rb_backtrace_to_str_ary(bt);
}
}
- rb_funcall(info, rb_intern("set_backtrace"), 1, bt);
+ rb_check_funcall(info, set_backtrace, 1, &bt);
}
static void
-error_print(rb_thread_t *th)
+error_print(rb_execution_context_t *ec)
{
- rb_threadptr_error_print(th, th->errinfo);
+ rb_ec_error_print(ec, ec->errinfo);
}
-void
-rb_threadptr_error_print(rb_thread_t *th, VALUE errinfo)
+#define CSI_BEGIN "\033["
+#define CSI_SGR "m"
+
+static const char underline[] = CSI_BEGIN"1;4"CSI_SGR;
+static const char bold[] = CSI_BEGIN"1"CSI_SGR;
+static const char reset[] = CSI_BEGIN""CSI_SGR;
+
+static void
+print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, const VALUE str, int highlight)
{
- volatile VALUE errat = Qundef;
- int raised_flag = th->raised_flag;
- volatile VALUE eclass = Qundef, e = Qundef;
- const char *volatile einfo;
- volatile long elen;
+ const char *einfo = "";
+ long elen = 0;
VALUE mesg;
- if (NIL_P(errinfo))
- return;
- rb_thread_raised_clear(th);
+ if (emesg != Qundef) {
+ if (NIL_P(errat) || RARRAY_LEN(errat) == 0 ||
+ NIL_P(mesg = RARRAY_AREF(errat, 0))) {
+ error_pos(str);
+ }
+ else {
+ write_warn_str(str, mesg);
+ write_warn(str, ": ");
+ }
- TH_PUSH_TAG(th);
- if (TH_EXEC_TAG() == 0) {
- errat = get_backtrace(errinfo);
- }
- else if (errat == Qundef) {
- errat = Qnil;
- }
- else if (eclass == Qundef || e != Qundef) {
- goto error;
- }
- else {
- goto no_message;
- }
- if (NIL_P(errat) || RARRAY_LEN(errat) == 0 ||
- NIL_P(mesg = RARRAY_AREF(errat, 0))) {
- error_pos();
- }
- else {
- warn_print_str(mesg);
- warn_print(": ");
- }
+ if (highlight) write_warn(str, bold);
- eclass = CLASS_OF(errinfo);
- if (eclass != Qundef &&
- (e = rb_check_funcall(errinfo, rb_intern("message"), 0, 0)) != Qundef &&
- (RB_TYPE_P(e, T_STRING) || !NIL_P(e = rb_check_string_type(e)))) {
- einfo = RSTRING_PTR(e);
- elen = RSTRING_LEN(e);
- }
- else {
- no_message:
- einfo = "";
- elen = 0;
+ if (!NIL_P(emesg)) {
+ einfo = RSTRING_PTR(emesg);
+ elen = RSTRING_LEN(emesg);
+ }
}
+
if (eclass == rb_eRuntimeError && elen == 0) {
- warn_print("unhandled exception\n");
+ 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) {
- warn_print_str(epath);
- warn_print("\n");
+ if (highlight) write_warn(str, underline);
+ write_warn_str(str, epath);
+ if (highlight) write_warn(str, reset);
+ write_warn(str, "\n");
}
else {
const char *tail = 0;
- long len = elen;
+ if (emesg == Qundef && highlight) write_warn(str, bold);
if (RSTRING_PTR(epath)[0] == '#')
epath = 0;
if ((tail = memchr(einfo, '\n', elen)) != 0) {
- len = tail - einfo;
+ write_warn2(str, einfo, tail - einfo);
tail++; /* skip newline */
}
- warn_print_str(tail ? rb_str_subseq(e, 0, len) : e);
+ else {
+ write_warn_str(str, emesg);
+ }
if (epath) {
- warn_print(" (");
- warn_print_str(epath);
- warn_print(")\n");
+ 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);
+ write_warn2(str, "\n", 1);
}
- if (tail) {
- warn_print_str(rb_str_subseq(e, tail - einfo, elen - len - 1));
+ if (tail && einfo+elen > tail) {
+ if (!highlight) {
+ write_warn2(str, tail, einfo+elen-tail);
+ if (einfo[elen-1] != '\n') write_warn2(str, "\n", 1);
+ }
+ else {
+ elen -= tail - einfo;
+ einfo = tail;
+ 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) {
+ write_warn2(str, "\n", 1);
+ 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;
+ }
+ }
+ }
+ else if (!epath) {
+ write_warn2(str, "\n", 1);
}
- if (tail ? einfo[elen-1] != '\n' : !epath) warn_print2("\n", 1);
}
}
+}
+static void
+print_backtrace(const VALUE eclass, const VALUE errat, const VALUE str, int reverse)
+{
if (!NIL_P(errat)) {
long i;
long len = RARRAY_LEN(errat);
int skip = eclass == rb_eSysStackError;
+ 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);
#define TRACE_MAX (TRACE_HEAD+TRACE_TAIL+5)
#define TRACE_HEAD 8
#define TRACE_TAIL 5
for (i = 1; i < len; i++) {
- VALUE line = RARRAY_AREF(errat, i);
+ VALUE line = RARRAY_AREF(errat, reverse ? len - i : i);
if (RB_TYPE_P(line, T_STRING)) {
- warn_print_str(rb_sprintf("\tfrom %"PRIsVALUE"\n", line));
+ 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));
}
if (skip && i == TRACE_HEAD && len > TRACE_MAX) {
- warn_print_str(rb_sprintf("\t ... %ld levels...\n",
+ write_warn_str(str, rb_sprintf("\t ... %ld levels...\n",
len - TRACE_HEAD - TRACE_TAIL));
i = len - TRACE_TAIL;
}
}
}
- error:
- TH_POP_TAG();
- th->errinfo = errinfo;
- rb_thread_raised_set(th, raised_flag);
+}
+
+VALUE rb_get_message(VALUE exc);
+
+static int
+shown_cause_p(VALUE cause, VALUE *shown_causes)
+{
+ VALUE shown = *shown_causes;
+ if (!shown) {
+ *shown_causes = shown = rb_obj_hide(rb_ident_hash_new());
+ }
+ if (rb_hash_has_key(shown, cause)) return TRUE;
+ rb_hash_aset(shown, cause, Qtrue);
+ return FALSE;
+}
+
+static void
+show_cause(VALUE errinfo, VALUE str, VALUE highlight, VALUE reverse, VALUE *shown_causes)
+{
+ VALUE cause = rb_attr_get(errinfo, id_cause);
+ if (!NIL_P(cause) && rb_obj_is_kind_of(cause, rb_eException) &&
+ !shown_cause_p(cause, shown_causes)) {
+ volatile VALUE eclass = CLASS_OF(cause);
+ VALUE errat = rb_get_backtrace(cause);
+ VALUE emesg = rb_get_message(cause);
+ if (reverse) {
+ show_cause(cause, str, highlight, reverse, shown_causes);
+ print_backtrace(eclass, errat, str, TRUE);
+ print_errinfo(eclass, errat, emesg, str, highlight!=0);
+ }
+ else {
+ print_errinfo(eclass, errat, emesg, str, highlight!=0);
+ print_backtrace(eclass, errat, str, FALSE);
+ show_cause(cause, str, highlight, reverse, shown_causes);
+ }
+ }
}
void
-ruby_error_print(void)
+rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE highlight, VALUE reverse)
{
- error_print(GET_THREAD());
+ volatile VALUE eclass;
+ VALUE shown_causes = 0;
+
+ if (NIL_P(errinfo))
+ return;
+
+ if (errat == Qundef) {
+ errat = Qnil;
+ }
+ eclass = CLASS_OF(errinfo);
+ if (NIL_P(reverse) || NIL_P(highlight)) {
+ VALUE tty = (VALUE)rb_stderr_tty_p();
+ if (NIL_P(reverse)) reverse = tty;
+ if (NIL_P(highlight)) highlight = tty;
+ }
+ 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 (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);
+#undef APPEND
+ len = p - (msg = buff);
+ }
+ write_warn2(str, msg, len);
+ show_cause(errinfo, str, highlight, reverse, &shown_causes);
+ print_backtrace(eclass, errat, str, TRUE);
+ print_errinfo(eclass, errat, emesg, str, highlight!=0);
+ }
+ else {
+ print_errinfo(eclass, errat, emesg, str, highlight!=0);
+ print_backtrace(eclass, errat, str, FALSE);
+ show_cause(errinfo, str, highlight, reverse, &shown_causes);
+ }
+}
+
+void
+rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo)
+{
+ volatile uint8_t raised_flag = ec->raised_flag;
+ volatile VALUE errat = Qundef;
+ volatile VALUE emesg = Qundef;
+ volatile bool written = false;
+
+ if (NIL_P(errinfo))
+ return;
+ rb_ec_raised_clear(ec);
+
+ EC_PUSH_TAG(ec);
+ if (EC_EXEC_TAG() == TAG_NONE) {
+ errat = rb_get_backtrace(errinfo);
+ }
+ if (emesg == Qundef) {
+ emesg = Qnil;
+ emesg = rb_get_message(errinfo);
+ }
+
+ if (!written) {
+ written = true;
+ rb_error_write(errinfo, emesg, errat, Qnil, Qnil, Qfalse);
+ }
+
+ EC_POP_TAG();
+ ec->errinfo = errinfo;
+ rb_ec_raised_set(ec, raised_flag);
}
-#define undef_mesg_for(v, k) rb_fstring_cstr("undefined"v" method `%1$s' for "k" `%2$s'")
+#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") : \
@@ -232,7 +361,7 @@ rb_print_undef_str(VALUE klass, VALUE name)
rb_name_err_raise_str(undef_mesg(""), klass, name);
}
-#define inaccessible_mesg_for(v, k) rb_fstring_cstr("method `%1$s' for "k" `%2$s' is "v)
+#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") : \
@@ -267,9 +396,9 @@ static int
error_handle(int ex)
{
int status = EXIT_FAILURE;
- rb_thread_t *th = GET_THREAD();
+ rb_execution_context_t *ec = GET_EC();
- if (rb_threadptr_set_raised(th))
+ if (rb_ec_set_raised(ec))
return EXIT_FAILURE;
switch (ex & TAG_MASK) {
case 0:
@@ -277,32 +406,32 @@ error_handle(int ex)
break;
case TAG_RETURN:
- error_pos();
+ error_pos(Qnil);
warn_print("unexpected return\n");
break;
case TAG_NEXT:
- error_pos();
+ error_pos(Qnil);
warn_print("unexpected next\n");
break;
case TAG_BREAK:
- error_pos();
+ error_pos(Qnil);
warn_print("unexpected break\n");
break;
case TAG_REDO:
- error_pos();
+ error_pos(Qnil);
warn_print("unexpected redo\n");
break;
case TAG_RETRY:
- error_pos();
+ error_pos(Qnil);
warn_print("retry outside of rescue clause\n");
break;
case TAG_THROW:
/* TODO: fix me */
- error_pos();
+ error_pos(Qnil);
warn_print("unexpected throw\n");
break;
case TAG_RAISE: {
- VALUE errinfo = th->errinfo;
+ VALUE errinfo = ec->errinfo;
if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
status = sysexit_status(errinfo);
}
@@ -311,17 +440,17 @@ error_handle(int ex)
/* no message when exiting by signal */
}
else {
- error_print(th);
+ rb_ec_error_print(ec, errinfo);
}
break;
}
case TAG_FATAL:
- error_print(th);
+ error_print(ec);
break;
default:
unknown_longjmp_status(ex);
break;
}
- rb_threadptr_reset_raised(th);
+ rb_ec_reset_raised(ec);
return status;
}
diff --git a/eval_intern.h b/eval_intern.h
index c5210b14de..f4a48d8171 100644
--- a/eval_intern.h
+++ b/eval_intern.h
@@ -5,23 +5,22 @@
#include "vm_core.h"
static inline void
-vm_passed_block_handler_set(rb_thread_t *th, VALUE block_handler)
+vm_passed_block_handler_set(rb_execution_context_t *ec, VALUE block_handler)
{
- VM_ASSERT(vm_block_handler_verify(block_handler));
- th->passed_block_handler = block_handler;
+ vm_block_handler_verify(block_handler);
+ ec->passed_block_handler = block_handler;
}
static inline void
-pass_passed_block_handler(rb_thread_t *th)
+pass_passed_block_handler(rb_execution_context_t *ec)
{
- VALUE block_handler = rb_vm_frame_block_handler(th->cfp);
- VM_ASSERT(vm_block_handler_verify(block_handler));
- vm_passed_block_handler_set(th, block_handler);
- VM_ENV_FLAGS_SET(th->cfp->ep, VM_FRAME_FLAG_PASSED);
+ VALUE block_handler = rb_vm_frame_block_handler(ec->cfp);
+ vm_passed_block_handler_set(ec, block_handler);
+ VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_PASSED);
}
-#define PASS_PASSED_BLOCK_HANDLER_TH(th) pass_passed_block_handler(th)
-#define PASS_PASSED_BLOCK_HANDLER() pass_passed_block_handler(GET_THREAD())
+#define PASS_PASSED_BLOCK_HANDLER_EC(ec) pass_passed_block_handler(ec)
+#define PASS_PASSED_BLOCK_HANDLER() pass_passed_block_handler(GET_EC())
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
@@ -98,7 +97,7 @@ extern int select_large_fdset(int, fd_set *, fd_set *, fd_set *, struct timeval
#define SAVE_ROOT_JMPBUF_AFTER_STMT \
} \
__except (GetExceptionCode() == EXCEPTION_STACK_OVERFLOW ? \
- (rb_thread_raised_set(GET_THREAD(), RAISED_STACKOVERFLOW), \
+ (rb_ec_raised_set(GET_EC(), RAISED_STACKOVERFLOW), \
raise(SIGSEGV), \
EXCEPTION_EXECUTE_HANDLER) : \
EXCEPTION_CONTINUE_SEARCH) { \
@@ -128,25 +127,27 @@ LONG WINAPI rb_w32_stack_overflow_handler(struct _EXCEPTION_POINTERS *);
rb_fiber_start(); \
} while (0)
-#define TH_PUSH_TAG(th) do { \
- rb_thread_t * const _th = (th); \
+#define EC_PUSH_TAG(ec) do { \
+ rb_execution_context_t * const _ec = (ec); \
struct rb_vm_tag _tag; \
+ _tag.state = TAG_NONE; \
_tag.tag = Qundef; \
- _tag.prev = _th->tag;
+ _tag.prev = _ec->tag;
-#define TH_POP_TAG() \
- _th->tag = _tag.prev; \
+#define EC_POP_TAG() \
+ _ec->tag = _tag.prev; \
} while (0)
-#define TH_TMPPOP_TAG() \
- _th->tag = _tag.prev
+#define EC_TMPPOP_TAG() \
+ _ec->tag = _tag.prev
-#define TH_REPUSH_TAG() (void)(_th->tag = &_tag)
+#define EC_REPUSH_TAG() (void)(_ec->tag = &_tag)
-#define PUSH_TAG() TH_PUSH_TAG(GET_THREAD())
-#define POP_TAG() TH_POP_TAG()
-
-#if defined __GNUC__ && __GNUC__ == 4 && (__GNUC_MINOR__ >= 6 && __GNUC_MINOR__ <= 8)
+#if defined __GNUC__ && __GNUC__ == 4 && (__GNUC_MINOR__ >= 6 && __GNUC_MINOR__ <= 8) || __clang__
+/* This macro prevents GCC 4.6--4.8 from emitting maybe-uninitialized warnings.
+ * This macro also prevents Clang from dumping core in EC_EXEC_TAG().
+ * (I confirmed Clang 4.0.1 and 5.0.0.)
+ */
# define VAR_FROM_MEMORY(var) __extension__(*(__typeof__(var) volatile *)&(var))
# define VAR_INITIALIZED(var) ((var) = VAR_FROM_MEMORY(var))
# define VAR_NOCLOBBERED(var) volatile var
@@ -156,36 +157,48 @@ LONG WINAPI rb_w32_stack_overflow_handler(struct _EXCEPTION_POINTERS *);
# define VAR_NOCLOBBERED(var) var
#endif
-/* clear th->state, and return the value */
+#if defined(USE_UNALIGNED_MEMBER_ACCESS) && USE_UNALIGNED_MEMBER_ACCESS && \
+ defined(__clang__)
+# define UNALIGNED_MEMBER_ACCESS(expr) __extension__({ \
+ COMPILER_WARNING_PUSH; \
+ COMPILER_WARNING_IGNORED(-Waddress-of-packed-member); \
+ typeof(expr) unaligned_member_access_result = (expr); \
+ COMPILER_WARNING_POP; \
+ unaligned_member_access_result; \
+})
+#else
+# define UNALIGNED_MEMBER_ACCESS(expr) expr
+#endif
+#define UNALIGNED_MEMBER_PTR(ptr, mem) UNALIGNED_MEMBER_ACCESS(&(ptr)->mem)
+
+#undef RB_OBJ_WRITE
+#define RB_OBJ_WRITE(a, slot, b) UNALIGNED_MEMBER_ACCESS(rb_obj_write((VALUE)(a), (VALUE *)(slot), (VALUE)(b), __FILE__, __LINE__))
+
+/* clear ec->tag->state, and return the value */
static inline int
-rb_threadptr_tag_state(rb_thread_t *th)
+rb_ec_tag_state(const rb_execution_context_t *ec)
{
- int state = th->state;
- th->state = 0;
+ enum ruby_tag_type state = ec->tag->state;
+ ec->tag->state = TAG_NONE;
return state;
}
-NORETURN(static inline void rb_threadptr_tag_jump(rb_thread_t *, int));
+NORETURN(static inline void rb_ec_tag_jump(const rb_execution_context_t *ec, enum ruby_tag_type st));
static inline void
-rb_threadptr_tag_jump(rb_thread_t *th, int st)
+rb_ec_tag_jump(const rb_execution_context_t *ec, enum ruby_tag_type st)
{
- th->state = st;
- ruby_longjmp(th->tag->buf, 1);
+ ec->tag->state = st;
+ ruby_longjmp(ec->tag->buf, 1);
}
/*
setjmp() in assignment expression rhs is undefined behavior
[ISO/IEC 9899:1999] 7.13.1.1
*/
-#define TH_EXEC_TAG() \
- (ruby_setjmp(_tag.buf) ? rb_threadptr_tag_state(VAR_FROM_MEMORY(_th)) : (TH_REPUSH_TAG(), 0))
+#define EC_EXEC_TAG() \
+ (ruby_setjmp(_tag.buf) ? rb_ec_tag_state(VAR_FROM_MEMORY(_ec)) : (EC_REPUSH_TAG(), 0))
-#define EXEC_TAG() \
- TH_EXEC_TAG()
-
-#define TH_JUMP_TAG(th, st) rb_threadptr_tag_jump(th, st)
-
-#define JUMP_TAG(st) TH_JUMP_TAG(GET_THREAD(), (st))
+#define EC_JUMP_TAG(ec, st) rb_ec_tag_jump(ec, st)
#define INTERNAL_EXCEPTION_P(exc) FIXNUM_P(exc)
@@ -262,12 +275,13 @@ enum {
RAISED_STACKOVERFLOW = 2,
RAISED_NOMEMORY = 4
};
-int rb_threadptr_set_raised(rb_thread_t *th);
-int rb_threadptr_reset_raised(rb_thread_t *th);
-#define rb_thread_raised_set(th, f) ((th)->raised_flag |= (f))
-#define rb_thread_raised_reset(th, f) ((th)->raised_flag &= ~(f))
-#define rb_thread_raised_p(th, f) (((th)->raised_flag & (f)) != 0)
-#define rb_thread_raised_clear(th) ((th)->raised_flag = 0)
+#define rb_ec_raised_set(ec, f) ((ec)->raised_flag |= (f))
+#define rb_ec_raised_reset(ec, f) ((ec)->raised_flag &= ~(f))
+#define rb_ec_raised_p(ec, f) (((ec)->raised_flag & (f)) != 0)
+#define rb_ec_raised_clear(ec) ((ec)->raised_flag = 0)
+int rb_ec_set_raised(rb_execution_context_t *ec);
+int rb_ec_reset_raised(rb_execution_context_t *ec);
+int rb_ec_stack_check(rb_execution_context_t *ec);
VALUE rb_f_eval(int argc, const VALUE *argv, VALUE self);
VALUE rb_make_exception(int argc, const VALUE *argv);
@@ -280,9 +294,9 @@ NORETURN(void rb_print_undef(VALUE, ID, rb_method_visibility_t));
NORETURN(void rb_print_undef_str(VALUE, VALUE));
NORETURN(void rb_print_inaccessible(VALUE, ID, rb_method_visibility_t));
NORETURN(void rb_vm_localjump_error(const char *,VALUE, int));
+#if 0
NORETURN(void rb_vm_jump_tag_but_local_jump(int));
-NORETURN(void rb_raise_method_missing(rb_thread_t *th, int argc, const VALUE *argv,
- VALUE obj, int call_status));
+#endif
VALUE rb_vm_make_jump_tag_but_local_jump(int state, VALUE val);
rb_cref_t *rb_vm_cref(void);
@@ -292,6 +306,10 @@ void rb_vm_set_progname(VALUE filename);
void rb_thread_terminate_all(void);
VALUE rb_vm_cbase(void);
+/* vm_backtrace.c */
+VALUE rb_ec_backtrace_object(const rb_execution_context_t *ec);
+VALUE rb_ec_backtrace_str_ary(const rb_execution_context_t *ec, long lev, long n);
+
#ifndef CharNext /* defined as CharNext[AW] on Windows. */
# ifdef HAVE_MBLEN
# define CharNext(p) ((p) + mblen((p), RUBY_MBCHAR_MAXSIZE))
diff --git a/eval_jump.c b/eval_jump.c
index 59dae109ce..a74aed959e 100644
--- a/eval_jump.c
+++ b/eval_jump.c
@@ -50,7 +50,6 @@ rb_f_at_exit(void)
struct end_proc_data {
void (*func) ();
VALUE data;
- int safe;
struct end_proc_data *next;
};
@@ -72,7 +71,6 @@ rb_set_end_proc(void (*func)(VALUE), VALUE data)
link->next = *list;
link->func = func;
link->data = data;
- link->safe = rb_safe_level();
*list = link;
}
@@ -104,7 +102,6 @@ exec_end_procs_chain(struct end_proc_data *volatile *procs, VALUE *errp)
*procs = link->next;
endproc = *link;
xfree(link);
- rb_set_safe_level_force(endproc.safe);
(*endproc.func) (endproc.data);
*errp = errinfo;
}
@@ -113,29 +110,26 @@ exec_end_procs_chain(struct end_proc_data *volatile *procs, VALUE *errp)
void
rb_exec_end_proc(void)
{
- int status;
- volatile int safe = rb_safe_level();
- rb_thread_t *th = GET_THREAD();
- volatile VALUE errinfo = th->errinfo;
+ enum ruby_tag_type state;
+ rb_execution_context_t * volatile ec = GET_EC();
+ volatile VALUE errinfo = ec->errinfo;
- TH_PUSH_TAG(th);
- if ((status = EXEC_TAG()) == 0) {
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
again:
- exec_end_procs_chain(&ephemeral_end_procs, &th->errinfo);
- exec_end_procs_chain(&end_procs, &th->errinfo);
+ exec_end_procs_chain(&ephemeral_end_procs, &ec->errinfo);
+ exec_end_procs_chain(&end_procs, &ec->errinfo);
}
else {
- VAR_INITIALIZED(th);
- TH_TMPPOP_TAG();
- error_handle(status);
- if (!NIL_P(th->errinfo)) errinfo = th->errinfo;
- TH_REPUSH_TAG();
+ EC_TMPPOP_TAG();
+ error_handle(state);
+ if (!NIL_P(ec->errinfo)) errinfo = ec->errinfo;
+ EC_REPUSH_TAG();
goto again;
}
- TH_POP_TAG();
+ EC_POP_TAG();
- rb_set_safe_level_force(safe);
- th->errinfo = errinfo;
+ ec->errinfo = errinfo;
}
void
diff --git a/ext/-test-/arith_seq/extract/extconf.rb b/ext/-test-/arith_seq/extract/extconf.rb
new file mode 100644
index 0000000000..9c368bf50e
--- /dev/null
+++ b/ext/-test-/arith_seq/extract/extconf.rb
@@ -0,0 +1,2 @@
+# frozen_string_literal: false
+create_makefile("-test-/arith_seq/extract")
diff --git a/ext/-test-/arith_seq/extract/extract.c b/ext/-test-/arith_seq/extract/extract.c
new file mode 100644
index 0000000000..93b89c239a
--- /dev/null
+++ b/ext/-test-/arith_seq/extract/extract.c
@@ -0,0 +1,27 @@
+#include "ruby/ruby.h"
+
+static VALUE
+arith_seq_s_extract(VALUE mod, VALUE obj)
+{
+ rb_arithmetic_sequence_components_t x;
+ VALUE ret;
+ int r;
+
+ r = rb_arithmetic_sequence_extract(obj, &x);
+
+ ret = rb_ary_new2(5);
+ rb_ary_store(ret, 0, r ? x.begin : Qnil);
+ rb_ary_store(ret, 1, r ? x.end : Qnil);
+ rb_ary_store(ret, 2, r ? x.step : Qnil);
+ rb_ary_store(ret, 3, r ? INT2FIX(x.exclude_end) : Qnil);
+ rb_ary_store(ret, 4, INT2FIX(r));
+
+ return ret;
+}
+
+void
+Init_extract(void)
+{
+ VALUE cArithSeq = rb_path2class("Enumerator::ArithmeticSequence");
+ rb_define_singleton_method(cArithSeq, "__extract__", arith_seq_s_extract, 1);
+}
diff --git a/ext/-test-/bignum/depend b/ext/-test-/bignum/depend
index 5310a3a4be..60e711489c 100644
--- a/ext/-test-/bignum/depend
+++ b/ext/-test-/bignum/depend
@@ -13,6 +13,7 @@ big2str.o: $(hdrdir)/ruby/encoding.h
big2str.o: $(hdrdir)/ruby/intern.h
big2str.o: $(hdrdir)/ruby/io.h
big2str.o: $(hdrdir)/ruby/missing.h
+big2str.o: $(hdrdir)/ruby/onigmo.h
big2str.o: $(hdrdir)/ruby/oniguruma.h
big2str.o: $(hdrdir)/ruby/ruby.h
big2str.o: $(hdrdir)/ruby/st.h
@@ -28,6 +29,7 @@ bigzero.o: $(hdrdir)/ruby/encoding.h
bigzero.o: $(hdrdir)/ruby/intern.h
bigzero.o: $(hdrdir)/ruby/io.h
bigzero.o: $(hdrdir)/ruby/missing.h
+bigzero.o: $(hdrdir)/ruby/onigmo.h
bigzero.o: $(hdrdir)/ruby/oniguruma.h
bigzero.o: $(hdrdir)/ruby/ruby.h
bigzero.o: $(hdrdir)/ruby/st.h
@@ -43,6 +45,7 @@ div.o: $(hdrdir)/ruby/encoding.h
div.o: $(hdrdir)/ruby/intern.h
div.o: $(hdrdir)/ruby/io.h
div.o: $(hdrdir)/ruby/missing.h
+div.o: $(hdrdir)/ruby/onigmo.h
div.o: $(hdrdir)/ruby/oniguruma.h
div.o: $(hdrdir)/ruby/ruby.h
div.o: $(hdrdir)/ruby/st.h
@@ -69,6 +72,7 @@ intpack.o: $(hdrdir)/ruby/encoding.h
intpack.o: $(hdrdir)/ruby/intern.h
intpack.o: $(hdrdir)/ruby/io.h
intpack.o: $(hdrdir)/ruby/missing.h
+intpack.o: $(hdrdir)/ruby/onigmo.h
intpack.o: $(hdrdir)/ruby/oniguruma.h
intpack.o: $(hdrdir)/ruby/ruby.h
intpack.o: $(hdrdir)/ruby/st.h
@@ -84,6 +88,7 @@ mul.o: $(hdrdir)/ruby/encoding.h
mul.o: $(hdrdir)/ruby/intern.h
mul.o: $(hdrdir)/ruby/io.h
mul.o: $(hdrdir)/ruby/missing.h
+mul.o: $(hdrdir)/ruby/onigmo.h
mul.o: $(hdrdir)/ruby/oniguruma.h
mul.o: $(hdrdir)/ruby/ruby.h
mul.o: $(hdrdir)/ruby/st.h
@@ -99,6 +104,7 @@ str2big.o: $(hdrdir)/ruby/encoding.h
str2big.o: $(hdrdir)/ruby/intern.h
str2big.o: $(hdrdir)/ruby/io.h
str2big.o: $(hdrdir)/ruby/missing.h
+str2big.o: $(hdrdir)/ruby/onigmo.h
str2big.o: $(hdrdir)/ruby/oniguruma.h
str2big.o: $(hdrdir)/ruby/ruby.h
str2big.o: $(hdrdir)/ruby/st.h
diff --git a/ext/-test-/bug-14834/bug-14384.c b/ext/-test-/bug-14834/bug-14384.c
new file mode 100644
index 0000000000..0d4103b5d4
--- /dev/null
+++ b/ext/-test-/bug-14834/bug-14384.c
@@ -0,0 +1,35 @@
+#include <ruby/ruby.h>
+#include <ruby/debug.h>
+
+static NOINLINE(VALUE f(VALUE));
+static NOINLINE(void g(VALUE, void*));
+extern NOINLINE(void Init_bug_14384(void));
+
+void
+Init_bug_14834(void)
+{
+ VALUE q = rb_define_module("Bug");
+ rb_define_module_function(q, "bug_14834", f, 0);
+}
+
+VALUE
+f(VALUE q)
+{
+ int w[] = { 0, 1024 };
+ VALUE e = rb_tracepoint_new(Qnil, RUBY_INTERNAL_EVENT_NEWOBJ, g, w);
+
+ rb_tracepoint_enable(e);
+ return rb_ensure(rb_yield, q, rb_tracepoint_disable, e);
+}
+
+void
+g(MAYBE_UNUSED(VALUE q), void* w)
+{
+ const int *e = (const int *)w;
+ const int r = *e++;
+ const int t = *e++;
+ VALUE *y = ALLOCA_N(VALUE, t);
+ int *u = ALLOCA_N(int, t);
+
+ rb_profile_frames(r, t, y, u);
+}
diff --git a/ext/-test-/bug-14834/depend b/ext/-test-/bug-14834/depend
new file mode 100644
index 0000000000..e25ed1aa1a
--- /dev/null
+++ b/ext/-test-/bug-14834/depend
@@ -0,0 +1,13 @@
+# AUTOGENERATED DEPENDENCIES START
+bug-14384.o: $(RUBY_EXTCONF_H)
+bug-14384.o: $(arch_hdrdir)/ruby/config.h
+bug-14384.o: $(hdrdir)/ruby/backward.h
+bug-14384.o: $(hdrdir)/ruby/debug.h
+bug-14384.o: $(hdrdir)/ruby/defines.h
+bug-14384.o: $(hdrdir)/ruby/intern.h
+bug-14384.o: $(hdrdir)/ruby/missing.h
+bug-14384.o: $(hdrdir)/ruby/ruby.h
+bug-14384.o: $(hdrdir)/ruby/st.h
+bug-14384.o: $(hdrdir)/ruby/subst.h
+bug-14384.o: bug-14384.c
+# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/-test-/bug-14834/extconf.rb b/ext/-test-/bug-14834/extconf.rb
new file mode 100644
index 0000000000..e8f3f1f437
--- /dev/null
+++ b/ext/-test-/bug-14834/extconf.rb
@@ -0,0 +1,2 @@
+# frozen_string_literal: true
+create_makefile("-test-/bug_14834")
diff --git a/ext/-test-/exception/depend b/ext/-test-/exception/depend
index 6d3f6e75f3..9e5ccff274 100644
--- a/ext/-test-/exception/depend
+++ b/ext/-test-/exception/depend
@@ -16,6 +16,7 @@ enc_raise.o: $(hdrdir)/ruby/defines.h
enc_raise.o: $(hdrdir)/ruby/encoding.h
enc_raise.o: $(hdrdir)/ruby/intern.h
enc_raise.o: $(hdrdir)/ruby/missing.h
+enc_raise.o: $(hdrdir)/ruby/onigmo.h
enc_raise.o: $(hdrdir)/ruby/oniguruma.h
enc_raise.o: $(hdrdir)/ruby/ruby.h
enc_raise.o: $(hdrdir)/ruby/st.h
diff --git a/ext/-test-/exception/enc_raise.c b/ext/-test-/exception/enc_raise.c
index dc8a42cf3f..68d7b4ebc1 100644
--- a/ext/-test-/exception/enc_raise.c
+++ b/ext/-test-/exception/enc_raise.c
@@ -5,7 +5,7 @@ static VALUE
enc_raise(VALUE exc, VALUE encoding, VALUE mesg)
{
rb_enc_raise(rb_to_encoding(encoding), exc, "%s", StringValueCStr(mesg));
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
void
diff --git a/ext/-test-/exception/ensured.c b/ext/-test-/exception/ensured.c
index 365e1f4f79..7dcc9b78ef 100644
--- a/ext/-test-/exception/ensured.c
+++ b/ext/-test-/exception/ensured.c
@@ -18,8 +18,22 @@ ensured(VALUE module, VALUE object)
return rb_ensure(begin, object, ensure, object);
}
+static VALUE
+exc_raise(VALUE exc)
+{
+ rb_exc_raise(exc);
+ return Qnil;
+}
+
+static VALUE
+ensure_raise(VALUE module, VALUE object, VALUE exc)
+{
+ return rb_ensure(rb_yield, object, exc_raise, exc);
+}
+
void
Init_ensured(VALUE klass)
{
rb_define_module_function(klass, "ensured", ensured, 1);
+ rb_define_module_function(klass, "ensure_raise", ensure_raise, 2);
}
diff --git a/ext/-test-/file/depend b/ext/-test-/file/depend
index fb22843b72..0eea6063dd 100644
--- a/ext/-test-/file/depend
+++ b/ext/-test-/file/depend
@@ -7,6 +7,7 @@ fs.o: $(hdrdir)/ruby/encoding.h
fs.o: $(hdrdir)/ruby/intern.h
fs.o: $(hdrdir)/ruby/io.h
fs.o: $(hdrdir)/ruby/missing.h
+fs.o: $(hdrdir)/ruby/onigmo.h
fs.o: $(hdrdir)/ruby/oniguruma.h
fs.o: $(hdrdir)/ruby/ruby.h
fs.o: $(hdrdir)/ruby/st.h
@@ -31,6 +32,7 @@ stat.o: $(hdrdir)/ruby/encoding.h
stat.o: $(hdrdir)/ruby/intern.h
stat.o: $(hdrdir)/ruby/io.h
stat.o: $(hdrdir)/ruby/missing.h
+stat.o: $(hdrdir)/ruby/onigmo.h
stat.o: $(hdrdir)/ruby/oniguruma.h
stat.o: $(hdrdir)/ruby/ruby.h
stat.o: $(hdrdir)/ruby/st.h
diff --git a/ext/-test-/file/fs.c b/ext/-test-/file/fs.c
index c9c3473257..63d2356d76 100644
--- a/ext/-test-/file/fs.c
+++ b/ext/-test-/file/fs.c
@@ -89,6 +89,9 @@ get_noatime_p(VALUE self, VALUE str)
rb_sys_fail_str(str);
}
# ifdef HAVE_STRUCT_STATFS_F_FLAGS
+# ifdef MNT_STRICTATIME
+ if (!(st.f_flags & MNT_STRICTATIME)) return Qtrue;
+# endif
# ifdef MNT_NOATIME
return st.f_flags & MNT_NOATIME ? Qtrue : Qfalse;
# elif defined(ST_NOATIME)
diff --git a/ext/-test-/funcall/funcall.c b/ext/-test-/funcall/funcall.c
new file mode 100644
index 0000000000..4e13c952e5
--- /dev/null
+++ b/ext/-test-/funcall/funcall.c
@@ -0,0 +1,44 @@
+#include "ruby.h"
+
+VALUE rb_funcall_passing_block(VALUE, ID, int, const VALUE*);
+
+static VALUE
+with_funcall2(int argc, VALUE *argv, VALUE self)
+{
+ return rb_funcallv(self, rb_intern("target"), argc, argv);
+}
+
+static VALUE
+with_funcall_passing_block(int argc, VALUE *argv, VALUE self)
+{
+ return rb_funcall_passing_block(self, rb_intern("target"), argc, argv);
+}
+
+static VALUE
+extra_args_name(VALUE self)
+{
+ /*
+ * at least clang 5.x gets tripped by the extra 0 arg
+ * [ruby-core:85266] [Bug #14425]
+ */
+ return rb_funcall(self, rb_intern("name"), 0, 0);
+}
+
+void
+Init_funcall(void)
+{
+ VALUE cTestFuncall = rb_path2class("TestFuncall");
+ VALUE cRelay = rb_define_module_under(cTestFuncall, "Relay");
+
+ rb_define_singleton_method(cRelay,
+ "with_funcall2",
+ with_funcall2,
+ -1);
+ rb_define_singleton_method(cRelay,
+ "with_funcall_passing_block",
+ with_funcall_passing_block,
+ -1);
+ rb_define_singleton_method(cTestFuncall, "extra_args_name",
+ extra_args_name,
+ 0);
+}
diff --git a/ext/-test-/funcall/passing_block.c b/ext/-test-/funcall/passing_block.c
deleted file mode 100644
index 70cb210831..0000000000
--- a/ext/-test-/funcall/passing_block.c
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "ruby.h"
-
-VALUE rb_funcall_passing_block(VALUE, ID, int, const VALUE*);
-
-static VALUE
-with_funcall2(int argc, VALUE *argv, VALUE self)
-{
- return rb_funcallv(self, rb_intern("target"), argc, argv);
-}
-
-static VALUE
-with_funcall_passing_block(int argc, VALUE *argv, VALUE self)
-{
- return rb_funcall_passing_block(self, rb_intern("target"), argc, argv);
-}
-
-void
-Init_funcall(void)
-{
- VALUE cRelay = rb_path2class("TestFuncall::Relay");
-
- rb_define_singleton_method(cRelay,
- "with_funcall2",
- with_funcall2,
- -1);
- rb_define_singleton_method(cRelay,
- "with_funcall_passing_block",
- with_funcall_passing_block,
- -1);
-}
diff --git a/ext/-test-/integer/core_ext.c b/ext/-test-/integer/core_ext.c
index 6d64cdb0c2..510ba4a1e6 100644
--- a/ext/-test-/integer/core_ext.c
+++ b/ext/-test-/integer/core_ext.c
@@ -20,10 +20,17 @@ rb_int_to_bignum(VALUE x)
return x;
}
+static VALUE
+positive_pow(VALUE x, VALUE y)
+{
+ return rb_int_positive_pow(NUM2LONG(x), NUM2ULONG(y));
+}
+
void
Init_core_ext(VALUE klass)
{
rb_define_method(rb_cInteger, "bignum?", int_bignum_p, 0);
rb_define_method(rb_cInteger, "fixnum?", int_fixnum_p, 0);
rb_define_method(rb_cInteger, "to_bignum", rb_int_to_bignum, 0);
+ rb_define_method(rb_cInteger, "positive_pow", positive_pow, 1);
}
diff --git a/ext/-test-/integer/depend b/ext/-test-/integer/depend
index 8ca1fe33c5..48f04d9ca0 100644
--- a/ext/-test-/integer/depend
+++ b/ext/-test-/integer/depend
@@ -7,6 +7,7 @@ core_ext.o: $(hdrdir)/ruby/encoding.h
core_ext.o: $(hdrdir)/ruby/intern.h
core_ext.o: $(hdrdir)/ruby/io.h
core_ext.o: $(hdrdir)/ruby/missing.h
+core_ext.o: $(hdrdir)/ruby/onigmo.h
core_ext.o: $(hdrdir)/ruby/oniguruma.h
core_ext.o: $(hdrdir)/ruby/ruby.h
core_ext.o: $(hdrdir)/ruby/st.h
diff --git a/ext/-test-/iter/break.c b/ext/-test-/iter/break.c
index 66ed26a9b8..4d43c5d0cf 100644
--- a/ext/-test-/iter/break.c
+++ b/ext/-test-/iter/break.c
@@ -5,7 +5,7 @@ iter_break(VALUE self)
{
rb_iter_break();
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
static VALUE
@@ -13,7 +13,7 @@ iter_break_value(VALUE self, VALUE val)
{
rb_iter_break_value(val);
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
void
diff --git a/ext/-test-/load/protect/extconf.rb b/ext/-test-/load/protect/extconf.rb
new file mode 100644
index 0000000000..83c080de9c
--- /dev/null
+++ b/ext/-test-/load/protect/extconf.rb
@@ -0,0 +1 @@
+create_makefile('-test-/load/protect')
diff --git a/ext/-test-/load/protect/protect.c b/ext/-test-/load/protect/protect.c
new file mode 100644
index 0000000000..ccfbbb9ea0
--- /dev/null
+++ b/ext/-test-/load/protect/protect.c
@@ -0,0 +1,19 @@
+#include <ruby.h>
+
+static VALUE
+load_protect(int argc, VALUE *argv, VALUE self)
+{
+ int state;
+ VALUE path, wrap;
+ rb_scan_args(argc, argv, "11", &path, &wrap);
+ rb_load_protect(path, RTEST(wrap), &state);
+ if (state) rb_jump_tag(state);
+ return Qnil;
+}
+
+void
+Init_protect(void)
+{
+ VALUE mod = rb_define_module("Bug");
+ rb_define_singleton_method(mod, "load_protect", load_protect, -1);
+}
diff --git a/ext/-test-/memory_status/memory_status.c b/ext/-test-/memory_status/memory_status.c
index 80e2648992..5775fa56f3 100644
--- a/ext/-test-/memory_status/memory_status.c
+++ b/ext/-test-/memory_status/memory_status.c
@@ -17,14 +17,25 @@ read_status(VALUE self)
#if defined __APPLE__
VALUE rss;
kern_return_t error;
- mach_msg_type_number_t out_count;
+# if defined MACH_TASK_BASIC_INFO
+ const task_flavor_t flavor = MACH_TASK_BASIC_INFO;
+ mach_msg_type_number_t out_count = MACH_TASK_BASIC_INFO_COUNT;
mach_task_basic_info_data_t taskinfo;
+# else
+ const task_flavor_t flavor = TASK_BASIC_INFO;
+ mach_msg_type_number_t out_count = TASK_BASIC_INFO_COUNT;
+ task_basic_info_data_t taskinfo;
+# endif
taskinfo.virtual_size = 0;
- out_count = MACH_TASK_BASIC_INFO_COUNT;
- error = task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
+ taskinfo.resident_size = 0;
+ error = task_info(mach_task_self(), flavor,
(task_info_t)&taskinfo, &out_count);
if (error != KERN_SUCCESS) return Qnil;
+#ifndef ULL2NUM
+/* "long long" does not exist here, use size_t instead. */
+#define ULL2NUM SIZET2NUM
+#endif
size = ULL2NUM(taskinfo.virtual_size);
rss = ULL2NUM(taskinfo.resident_size);
rb_struct_aset(self, INT2FIX(1), rss);
diff --git a/ext/-test-/notimplement/bug.c b/ext/-test-/notimplement/bug.c
index 8e9cae707a..82e243a81d 100644
--- a/ext/-test-/notimplement/bug.c
+++ b/ext/-test-/notimplement/bug.c
@@ -11,6 +11,8 @@ void
Init_notimplement(void)
{
VALUE mBug = rb_define_module("Bug");
+ VALUE klass = rb_define_class_under(mBug, "NotImplement", rb_cObject);
rb_define_module_function(mBug, "funcall", bug_funcall, -1);
rb_define_module_function(mBug, "notimplement", rb_f_notimplement, -1);
+ rb_define_method(klass, "notimplement", rb_f_notimplement, -1);
}
diff --git a/ext/-test-/printf/printf.c b/ext/-test-/printf/printf.c
index 666f5592e5..e793bb7a48 100644
--- a/ext/-test-/printf/printf.c
+++ b/ext/-test-/printf/printf.c
@@ -90,6 +90,13 @@ printf_test_call(int argc, VALUE *argv, VALUE self)
return rb_assoc_new(result, rb_usascii_str_new_cstr(format));
}
+static VALUE
+snprintf_count(VALUE self, VALUE str)
+{
+ int n = ruby_snprintf(NULL, 0, "%s", StringValueCStr(str));
+ return INT2FIX(n);
+}
+
void
Init_printf(void)
{
@@ -98,4 +105,5 @@ Init_printf(void)
rb_define_singleton_method(m, "v", printf_test_v, 1);
rb_define_singleton_method(m, "q", printf_test_q, 1);
rb_define_singleton_method(m, "call", printf_test_call, -1);
+ rb_define_singleton_method(m, "sncount", snprintf_count, 1);
}
diff --git a/ext/-test-/rational/depend b/ext/-test-/rational/depend
index a0ee797b31..4f7c4e4900 100644
--- a/ext/-test-/rational/depend
+++ b/ext/-test-/rational/depend
@@ -11,6 +11,7 @@ rat.o: $(hdrdir)/ruby/encoding.h
rat.o: $(hdrdir)/ruby/intern.h
rat.o: $(hdrdir)/ruby/io.h
rat.o: $(hdrdir)/ruby/missing.h
+rat.o: $(hdrdir)/ruby/onigmo.h
rat.o: $(hdrdir)/ruby/oniguruma.h
rat.o: $(hdrdir)/ruby/ruby.h
rat.o: $(hdrdir)/ruby/st.h
diff --git a/ext/-test-/regexp/extconf.rb b/ext/-test-/regexp/extconf.rb
new file mode 100644
index 0000000000..ca51178a18
--- /dev/null
+++ b/ext/-test-/regexp/extconf.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: false
+require_relative "../auto_ext.rb"
+auto_ext
diff --git a/ext/-test-/regexp/init.c b/ext/-test-/regexp/init.c
new file mode 100644
index 0000000000..906abe940f
--- /dev/null
+++ b/ext/-test-/regexp/init.c
@@ -0,0 +1,11 @@
+#include "ruby.h"
+
+#define init(n) {void Init_##n(VALUE klass); Init_##n(klass);}
+
+void
+Init_regexp(void)
+{
+ VALUE mBug = rb_define_module("Bug");
+ VALUE klass = rb_define_class_under(mBug, "Regexp", rb_cRegexp);
+ TEST_INIT_FUNCS(init);
+}
diff --git a/ext/-test-/regexp/parse_depth_limit.c b/ext/-test-/regexp/parse_depth_limit.c
new file mode 100644
index 0000000000..8e08a5c789
--- /dev/null
+++ b/ext/-test-/regexp/parse_depth_limit.c
@@ -0,0 +1,23 @@
+#include <ruby.h>
+#include <ruby/onigmo.h>
+
+static VALUE
+get_parse_depth_limit(VALUE self)
+{
+ unsigned int depth = onig_get_parse_depth_limit();
+ return UINT2NUM(depth);
+}
+
+static VALUE
+set_parse_depth_limit(VALUE self, VALUE depth)
+{
+ onig_set_parse_depth_limit(NUM2UINT(depth));
+ return depth;
+}
+
+void
+Init_parse_depth_limit(VALUE klass)
+{
+ rb_define_singleton_method(klass, "parse_depth_limit", get_parse_depth_limit, 0);
+ rb_define_singleton_method(klass, "parse_depth_limit=", set_parse_depth_limit, 1);
+}
diff --git a/ext/-test-/scan_args/extconf.rb b/ext/-test-/scan_args/extconf.rb
new file mode 100755
index 0000000000..6cae9c2779
--- /dev/null
+++ b/ext/-test-/scan_args/extconf.rb
@@ -0,0 +1 @@
+create_makefile("-test-/scan_args")
diff --git a/ext/-test-/scan_args/scan_args.c b/ext/-test-/scan_args/scan_args.c
new file mode 100644
index 0000000000..dca353f643
--- /dev/null
+++ b/ext/-test-/scan_args/scan_args.c
@@ -0,0 +1,286 @@
+#include <ruby.h>
+
+#ifndef numberof
+#define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
+#endif
+
+static VALUE
+scan_args_lead(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[2];
+ int n = rb_scan_args(argc, argv, "1", args+1);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_opt(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[2];
+ int n = rb_scan_args(argc, argv, "01", args+1);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_lead_opt(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[3];
+ int n = rb_scan_args(argc, argv, "11", args+1, args+2);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+/* var */
+static VALUE
+scan_args_var(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[2];
+ int n = rb_scan_args(argc, argv, "*", args+1);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_lead_var(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[3];
+ int n = rb_scan_args(argc, argv, "1*", args+1, args+2);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_opt_var(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[3];
+ int n = rb_scan_args(argc, argv, "01*", args+1, args+2);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_lead_opt_var(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[4];
+ int n = rb_scan_args(argc, argv, "11*", args+1, args+2, args+3);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+/* trail */
+static VALUE
+scan_args_opt_trail(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[3];
+ int n = rb_scan_args(argc, argv, "011", args+1, args+2);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_lead_opt_trail(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[4];
+ int n = rb_scan_args(argc, argv, "111", args+1, args+2, args+3);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_var_trail(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[3];
+ int n = rb_scan_args(argc, argv, "*1", args+1, args+2);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_lead_var_trail(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[4];
+ int n = rb_scan_args(argc, argv, "1*1", args+1, args+2, args+3);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_opt_var_trail(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[4];
+ int n = rb_scan_args(argc, argv, "01*1", args+1, args+2, args+3);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_lead_opt_var_trail(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[5];
+ int n = rb_scan_args(argc, argv, "11*1", args+1, args+2, args+3, args+4);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+/* hash */
+static VALUE
+scan_args_hash(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[2];
+ int n = rb_scan_args(argc, argv, ":", args+1);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_lead_hash(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[3];
+ int n = rb_scan_args(argc, argv, "1:", args+1, args+2);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_opt_hash(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[3];
+ int n = rb_scan_args(argc, argv, "01:", args+1, args+2);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_lead_opt_hash(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[4];
+ int n = rb_scan_args(argc, argv, "11:", args+1, args+2, args+3);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_var_hash(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[3];
+ int n = rb_scan_args(argc, argv, "*:", args+1, args+2);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_lead_var_hash(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[4];
+ int n = rb_scan_args(argc, argv, "1*:", args+1, args+2, args+3);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_opt_var_hash(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[4];
+ int n = rb_scan_args(argc, argv, "01*:", args+1, args+2, args+3);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_lead_opt_var_hash(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[5];
+ int n = rb_scan_args(argc, argv, "11*:", args+1, args+2, args+3, args+4);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_opt_trail_hash(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[4];
+ int n = rb_scan_args(argc, argv, "011:", args+1, args+2, args+3);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_lead_opt_trail_hash(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[5];
+ int n = rb_scan_args(argc, argv, "111:", args+1, args+2, args+3, args+4);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_var_trail_hash(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[4];
+ int n = rb_scan_args(argc, argv, "*1:", args+1, args+2, args+3);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_lead_var_trail_hash(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[5];
+ int n = rb_scan_args(argc, argv, "1*1:", args+1, args+2, args+3, args+4);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_opt_var_trail_hash(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[5];
+ int n = rb_scan_args(argc, argv, "01*1:", args+1, args+2, args+3, args+4);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+static VALUE
+scan_args_lead_opt_var_trail_hash(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[6];
+ int n = rb_scan_args(argc, argv, "11*1:", args+1, args+2, args+3, args+4, args+5);
+ args[0] = INT2NUM(n);
+ return rb_ary_new_from_values(numberof(args), args);
+}
+
+void
+Init_scan_args(void)
+{
+ VALUE module = rb_define_module("Bug");
+ module = rb_define_module_under(module, "ScanArgs");
+ rb_define_singleton_method(module, "lead", scan_args_lead, -1);
+ rb_define_singleton_method(module, "opt", scan_args_opt, -1);
+ rb_define_singleton_method(module, "lead_opt", scan_args_lead_opt, -1);
+ rb_define_singleton_method(module, "var", scan_args_var, -1);
+ rb_define_singleton_method(module, "lead_var", scan_args_lead_var, -1);
+ rb_define_singleton_method(module, "opt_var", scan_args_opt_var, -1);
+ rb_define_singleton_method(module, "lead_opt_var", scan_args_lead_opt_var, -1);
+ rb_define_singleton_method(module, "opt_trail", scan_args_opt_trail, -1);
+ rb_define_singleton_method(module, "lead_opt_trail", scan_args_lead_opt_trail, -1);
+ rb_define_singleton_method(module, "var_trail", scan_args_var_trail, -1);
+ rb_define_singleton_method(module, "lead_var_trail", scan_args_lead_var_trail, -1);
+ rb_define_singleton_method(module, "opt_var_trail", scan_args_opt_var_trail, -1);
+ rb_define_singleton_method(module, "lead_opt_var_trail", scan_args_lead_opt_var_trail, -1);
+ rb_define_singleton_method(module, "hash", scan_args_hash, -1);
+ rb_define_singleton_method(module, "lead_hash", scan_args_lead_hash, -1);
+ rb_define_singleton_method(module, "opt_hash", scan_args_opt_hash, -1);
+ rb_define_singleton_method(module, "lead_opt_hash", scan_args_lead_opt_hash, -1);
+ rb_define_singleton_method(module, "var_hash", scan_args_var_hash, -1);
+ rb_define_singleton_method(module, "lead_var_hash", scan_args_lead_var_hash, -1);
+ rb_define_singleton_method(module, "opt_var_hash", scan_args_opt_var_hash, -1);
+ rb_define_singleton_method(module, "lead_opt_var_hash", scan_args_lead_opt_var_hash, -1);
+ rb_define_singleton_method(module, "opt_trail_hash", scan_args_opt_trail_hash, -1);
+ rb_define_singleton_method(module, "lead_opt_trail_hash", scan_args_lead_opt_trail_hash, -1);
+ rb_define_singleton_method(module, "var_trail_hash", scan_args_var_trail_hash, -1);
+ rb_define_singleton_method(module, "lead_var_trail_hash", scan_args_lead_var_trail_hash, -1);
+ rb_define_singleton_method(module, "opt_var_trail_hash", scan_args_opt_var_trail_hash, -1);
+ rb_define_singleton_method(module, "lead_opt_var_trail_hash", scan_args_lead_opt_var_trail_hash, -1);
+}
+
diff --git a/ext/-test-/string/capacity.c b/ext/-test-/string/capacity.c
index ba7fb85e9e..f5277bf4e6 100644
--- a/ext/-test-/string/capacity.c
+++ b/ext/-test-/string/capacity.c
@@ -11,7 +11,7 @@ bug_str_capacity(VALUE klass, VALUE str)
}
void
-Init_capacity(VALUE klass)
+Init_string_capacity(VALUE klass)
{
rb_define_singleton_method(klass, "capacity", bug_str_capacity, 1);
}
diff --git a/ext/-test-/string/coderange.c b/ext/-test-/string/coderange.c
index b93172d72d..1342ce20da 100644
--- a/ext/-test-/string/coderange.c
+++ b/ext/-test-/string/coderange.c
@@ -17,7 +17,7 @@ coderange_int2sym(int coderange)
return sym_broken;
}
rb_bug("wrong condition of coderange");
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
/* return coderange without scan */
@@ -36,7 +36,7 @@ str_coderange_scan(VALUE str)
}
void
-Init_coderange(VALUE klass)
+Init_string_coderange(VALUE klass)
{
sym_7bit = ID2SYM(rb_intern("7bit"));
sym_valid = ID2SYM(rb_intern("valid"));
diff --git a/ext/-test-/string/cstr.c b/ext/-test-/string/cstr.c
index fc47d5206f..71eafdb703 100644
--- a/ext/-test-/string/cstr.c
+++ b/ext/-test-/string/cstr.c
@@ -1,3 +1,4 @@
+#include "ruby/encoding.h"
#include "internal.h"
static VALUE
@@ -131,7 +132,7 @@ bug_str_s_rb_str_new_frozen(VALUE self, VALUE str)
}
void
-Init_cstr(VALUE klass)
+Init_string_cstr(VALUE klass)
{
rb_define_method(klass, "cstr_term", bug_str_cstr_term, 0);
rb_define_method(klass, "cstr_unterm", bug_str_cstr_unterm, 1);
diff --git a/ext/-test-/string/depend b/ext/-test-/string/depend
index e3ce4b6e9c..71e995a523 100644
--- a/ext/-test-/string/depend
+++ b/ext/-test-/string/depend
@@ -7,6 +7,7 @@ capacity.o: $(hdrdir)/ruby/encoding.h
capacity.o: $(hdrdir)/ruby/intern.h
capacity.o: $(hdrdir)/ruby/io.h
capacity.o: $(hdrdir)/ruby/missing.h
+capacity.o: $(hdrdir)/ruby/onigmo.h
capacity.o: $(hdrdir)/ruby/oniguruma.h
capacity.o: $(hdrdir)/ruby/ruby.h
capacity.o: $(hdrdir)/ruby/st.h
@@ -21,6 +22,7 @@ coderange.o: $(hdrdir)/ruby/defines.h
coderange.o: $(hdrdir)/ruby/encoding.h
coderange.o: $(hdrdir)/ruby/intern.h
coderange.o: $(hdrdir)/ruby/missing.h
+coderange.o: $(hdrdir)/ruby/onigmo.h
coderange.o: $(hdrdir)/ruby/oniguruma.h
coderange.o: $(hdrdir)/ruby/ruby.h
coderange.o: $(hdrdir)/ruby/st.h
@@ -34,6 +36,7 @@ cstr.o: $(hdrdir)/ruby/encoding.h
cstr.o: $(hdrdir)/ruby/intern.h
cstr.o: $(hdrdir)/ruby/io.h
cstr.o: $(hdrdir)/ruby/missing.h
+cstr.o: $(hdrdir)/ruby/onigmo.h
cstr.o: $(hdrdir)/ruby/oniguruma.h
cstr.o: $(hdrdir)/ruby/ruby.h
cstr.o: $(hdrdir)/ruby/st.h
@@ -59,6 +62,7 @@ enc_associate.o: $(hdrdir)/ruby/defines.h
enc_associate.o: $(hdrdir)/ruby/encoding.h
enc_associate.o: $(hdrdir)/ruby/intern.h
enc_associate.o: $(hdrdir)/ruby/missing.h
+enc_associate.o: $(hdrdir)/ruby/onigmo.h
enc_associate.o: $(hdrdir)/ruby/oniguruma.h
enc_associate.o: $(hdrdir)/ruby/ruby.h
enc_associate.o: $(hdrdir)/ruby/st.h
@@ -72,6 +76,7 @@ enc_str_buf_cat.o: $(hdrdir)/ruby/defines.h
enc_str_buf_cat.o: $(hdrdir)/ruby/encoding.h
enc_str_buf_cat.o: $(hdrdir)/ruby/intern.h
enc_str_buf_cat.o: $(hdrdir)/ruby/missing.h
+enc_str_buf_cat.o: $(hdrdir)/ruby/onigmo.h
enc_str_buf_cat.o: $(hdrdir)/ruby/oniguruma.h
enc_str_buf_cat.o: $(hdrdir)/ruby/ruby.h
enc_str_buf_cat.o: $(hdrdir)/ruby/st.h
@@ -110,6 +115,22 @@ modify.o: $(hdrdir)/ruby/st.h
modify.o: $(hdrdir)/ruby/subst.h
modify.o: $(top_srcdir)/include/ruby.h
modify.o: modify.c
+new.o: $(RUBY_EXTCONF_H)
+new.o: $(arch_hdrdir)/ruby/config.h
+new.o: $(hdrdir)/ruby/backward.h
+new.o: $(hdrdir)/ruby/defines.h
+new.o: $(hdrdir)/ruby/encoding.h
+new.o: $(hdrdir)/ruby/intern.h
+new.o: $(hdrdir)/ruby/io.h
+new.o: $(hdrdir)/ruby/missing.h
+new.o: $(hdrdir)/ruby/onigmo.h
+new.o: $(hdrdir)/ruby/oniguruma.h
+new.o: $(hdrdir)/ruby/ruby.h
+new.o: $(hdrdir)/ruby/st.h
+new.o: $(hdrdir)/ruby/subst.h
+new.o: $(top_srcdir)/include/ruby.h
+new.o: $(top_srcdir)/internal.h
+new.o: new.c
nofree.o: $(RUBY_EXTCONF_H)
nofree.o: $(arch_hdrdir)/ruby/config.h
nofree.o: $(hdrdir)/ruby/backward.h
@@ -129,6 +150,7 @@ normalize.o: $(hdrdir)/ruby/encoding.h
normalize.o: $(hdrdir)/ruby/intern.h
normalize.o: $(hdrdir)/ruby/io.h
normalize.o: $(hdrdir)/ruby/missing.h
+normalize.o: $(hdrdir)/ruby/onigmo.h
normalize.o: $(hdrdir)/ruby/oniguruma.h
normalize.o: $(hdrdir)/ruby/ruby.h
normalize.o: $(hdrdir)/ruby/st.h
@@ -143,6 +165,7 @@ qsort.o: $(hdrdir)/ruby/defines.h
qsort.o: $(hdrdir)/ruby/encoding.h
qsort.o: $(hdrdir)/ruby/intern.h
qsort.o: $(hdrdir)/ruby/missing.h
+qsort.o: $(hdrdir)/ruby/onigmo.h
qsort.o: $(hdrdir)/ruby/oniguruma.h
qsort.o: $(hdrdir)/ruby/ruby.h
qsort.o: $(hdrdir)/ruby/st.h
@@ -150,6 +173,17 @@ qsort.o: $(hdrdir)/ruby/subst.h
qsort.o: $(hdrdir)/ruby/util.h
qsort.o: $(top_srcdir)/include/ruby.h
qsort.o: qsort.c
+rb_str_dup.o: $(RUBY_EXTCONF_H)
+rb_str_dup.o: $(arch_hdrdir)/ruby/config.h
+rb_str_dup.o: $(hdrdir)/ruby.h
+rb_str_dup.o: $(hdrdir)/ruby/backward.h
+rb_str_dup.o: $(hdrdir)/ruby/defines.h
+rb_str_dup.o: $(hdrdir)/ruby/intern.h
+rb_str_dup.o: $(hdrdir)/ruby/missing.h
+rb_str_dup.o: $(hdrdir)/ruby/ruby.h
+rb_str_dup.o: $(hdrdir)/ruby/st.h
+rb_str_dup.o: $(hdrdir)/ruby/subst.h
+rb_str_dup.o: rb_str_dup.c
set_len.o: $(RUBY_EXTCONF_H)
set_len.o: $(arch_hdrdir)/ruby/config.h
set_len.o: $(hdrdir)/ruby/backward.h
diff --git a/ext/-test-/string/ellipsize.c b/ext/-test-/string/ellipsize.c
index 0451519492..6034408724 100644
--- a/ext/-test-/string/ellipsize.c
+++ b/ext/-test-/string/ellipsize.c
@@ -7,7 +7,7 @@ bug_str_ellipsize(VALUE str, VALUE len)
}
void
-Init_ellipsize(VALUE klass)
+Init_string_ellipsize(VALUE klass)
{
rb_define_method(klass, "ellipsize", bug_str_ellipsize, 1);
}
diff --git a/ext/-test-/string/enc_associate.c b/ext/-test-/string/enc_associate.c
index 53811620a0..594d8a43a4 100644
--- a/ext/-test-/string/enc_associate.c
+++ b/ext/-test-/string/enc_associate.c
@@ -15,7 +15,7 @@ bug_str_encoding_index(VALUE self, VALUE str)
}
void
-Init_enc_associate(VALUE klass)
+Init_string_enc_associate(VALUE klass)
{
rb_define_method(klass, "associate_encoding!", bug_str_enc_associate, 1);
rb_define_singleton_method(klass, "encoding_index", bug_str_encoding_index, 1);
diff --git a/ext/-test-/string/enc_str_buf_cat.c b/ext/-test-/string/enc_str_buf_cat.c
index 5d583c65dc..9ac4a298be 100644
--- a/ext/-test-/string/enc_str_buf_cat.c
+++ b/ext/-test-/string/enc_str_buf_cat.c
@@ -8,7 +8,7 @@ enc_str_buf_cat(VALUE str, VALUE str2)
}
void
-Init_enc_str_buf_cat(VALUE klass)
+Init_string_enc_str_buf_cat(VALUE klass)
{
rb_define_method(klass, "enc_str_buf_cat", enc_str_buf_cat, 1);
}
diff --git a/ext/-test-/string/fstring.c b/ext/-test-/string/fstring.c
index b65c98ce6d..30120b42f6 100644
--- a/ext/-test-/string/fstring.c
+++ b/ext/-test-/string/fstring.c
@@ -9,7 +9,7 @@ bug_s_fstring(VALUE self, VALUE str)
}
void
-Init_fstring(VALUE klass)
+Init_string_fstring(VALUE klass)
{
rb_define_singleton_method(klass, "fstring", bug_s_fstring, 1);
}
diff --git a/ext/-test-/string/init.c b/ext/-test-/string/init.c
index 0b3e4a6ff2..a74245c8d7 100644
--- a/ext/-test-/string/init.c
+++ b/ext/-test-/string/init.c
@@ -1,6 +1,6 @@
#include "ruby.h"
-#define init(n) {void Init_##n(VALUE klass); Init_##n(klass);}
+#define init(n) {void Init_string_##n(VALUE klass); Init_string_##n(klass);}
void
Init_string(void)
diff --git a/ext/-test-/string/modify.c b/ext/-test-/string/modify.c
index ddd2efd8f0..945febc7e3 100644
--- a/ext/-test-/string/modify.c
+++ b/ext/-test-/string/modify.c
@@ -15,7 +15,7 @@ bug_str_modify_expand(VALUE str, VALUE expand)
}
void
-Init_modify(VALUE klass)
+Init_string_modify(VALUE klass)
{
rb_define_method(klass, "modify!", bug_str_modify, 0);
rb_define_method(klass, "modify_expand!", bug_str_modify_expand, 1);
diff --git a/ext/-test-/string/new.c b/ext/-test-/string/new.c
new file mode 100644
index 0000000000..60625b8300
--- /dev/null
+++ b/ext/-test-/string/new.c
@@ -0,0 +1,21 @@
+#include "ruby.h"
+#include "ruby/encoding.h"
+
+static VALUE
+bug_str_buf_new(VALUE self, VALUE len)
+{
+ return rb_str_buf_new(NUM2LONG(len));
+}
+
+static VALUE
+bug_external_str_new(VALUE self, VALUE len, VALUE enc)
+{
+ return rb_external_str_new_with_enc(NULL, NUM2LONG(len), rb_to_encoding(enc));
+}
+
+void
+Init_string_new(VALUE klass)
+{
+ rb_define_singleton_method(klass, "buf_new", bug_str_buf_new, 1);
+ rb_define_singleton_method(klass, "external_new", bug_external_str_new, 2);
+}
diff --git a/ext/-test-/string/nofree.c b/ext/-test-/string/nofree.c
index d3d8071ff9..fdf810c741 100644
--- a/ext/-test-/string/nofree.c
+++ b/ext/-test-/string/nofree.c
@@ -7,7 +7,7 @@ bug_str_nofree(VALUE self)
}
void
-Init_nofree(VALUE klass)
+Init_string_nofree(VALUE klass)
{
rb_define_singleton_method(klass, "nofree", bug_str_nofree, 0);
}
diff --git a/ext/-test-/string/normalize.c b/ext/-test-/string/normalize.c
index 2e16a4616f..0ba1797631 100644
--- a/ext/-test-/string/normalize.c
+++ b/ext/-test-/string/normalize.c
@@ -11,7 +11,7 @@ normalize_ospath(VALUE str)
#endif
void
-Init_normalize(VALUE klass)
+Init_string_normalize(VALUE klass)
{
rb_define_method(klass, "normalize_ospath", normalize_ospath, 0);
}
diff --git a/ext/-test-/string/qsort.c b/ext/-test-/string/qsort.c
index 0b34936d38..fb7ea3d8cb 100644
--- a/ext/-test-/string/qsort.c
+++ b/ext/-test-/string/qsort.c
@@ -55,7 +55,7 @@ bug_str_qsort_bang(int argc, VALUE *argv, VALUE str)
}
void
-Init_qsort(VALUE klass)
+Init_string_qsort(VALUE klass)
{
rb_define_method(klass, "qsort!", bug_str_qsort_bang, -1);
}
diff --git a/ext/-test-/string/rb_str_dup.c b/ext/-test-/string/rb_str_dup.c
new file mode 100644
index 0000000000..a0bd65820f
--- /dev/null
+++ b/ext/-test-/string/rb_str_dup.c
@@ -0,0 +1,35 @@
+#include "ruby.h"
+
+VALUE rb_str_dup(VALUE str);
+
+static VALUE
+bug_rb_str_dup(VALUE self, VALUE str)
+{
+ rb_check_type(str, T_STRING);
+ return rb_str_dup(str);
+}
+
+static VALUE
+bug_shared_string_p(VALUE self, VALUE str)
+{
+ rb_check_type(str, T_STRING);
+ return RB_FL_TEST(str, RUBY_ELTS_SHARED) && RB_FL_TEST(str, RSTRING_NOEMBED) ? Qtrue : Qfalse;
+}
+
+static VALUE
+bug_sharing_with_shared_p(VALUE self, VALUE str)
+{
+ rb_check_type(str, T_STRING);
+ if (bug_shared_string_p(self, str)) {
+ return bug_shared_string_p(self, RSTRING(str)->as.heap.aux.shared);
+ }
+ return Qfalse;
+}
+
+void
+Init_string_rb_str_dup(VALUE klass)
+{
+ rb_define_singleton_method(klass, "rb_str_dup", bug_rb_str_dup, 1);
+ rb_define_singleton_method(klass, "shared_string?", bug_shared_string_p, 1);
+ rb_define_singleton_method(klass, "sharing_with_shared?", bug_sharing_with_shared_p, 1);
+}
diff --git a/ext/-test-/string/set_len.c b/ext/-test-/string/set_len.c
index 3c7d19d778..219cea404c 100644
--- a/ext/-test-/string/set_len.c
+++ b/ext/-test-/string/set_len.c
@@ -8,7 +8,7 @@ bug_str_set_len(VALUE str, VALUE len)
}
void
-Init_set_len(VALUE klass)
+Init_string_set_len(VALUE klass)
{
rb_define_method(klass, "set_len", bug_str_set_len, 1);
}
diff --git a/ext/-test-/struct/depend b/ext/-test-/struct/depend
new file mode 100644
index 0000000000..58ededbff9
--- /dev/null
+++ b/ext/-test-/struct/depend
@@ -0,0 +1,46 @@
+# AUTOGENERATED DEPENDENCIES START
+duplicate.o: $(RUBY_EXTCONF_H)
+duplicate.o: $(arch_hdrdir)/ruby/config.h
+duplicate.o: $(hdrdir)/ruby/backward.h
+duplicate.o: $(hdrdir)/ruby/defines.h
+duplicate.o: $(hdrdir)/ruby/intern.h
+duplicate.o: $(hdrdir)/ruby/missing.h
+duplicate.o: $(hdrdir)/ruby/ruby.h
+duplicate.o: $(hdrdir)/ruby/st.h
+duplicate.o: $(hdrdir)/ruby/subst.h
+duplicate.o: $(top_srcdir)/include/ruby.h
+duplicate.o: duplicate.c
+init.o: $(RUBY_EXTCONF_H)
+init.o: $(arch_hdrdir)/ruby/config.h
+init.o: $(hdrdir)/ruby/backward.h
+init.o: $(hdrdir)/ruby/defines.h
+init.o: $(hdrdir)/ruby/intern.h
+init.o: $(hdrdir)/ruby/missing.h
+init.o: $(hdrdir)/ruby/ruby.h
+init.o: $(hdrdir)/ruby/st.h
+init.o: $(hdrdir)/ruby/subst.h
+init.o: $(top_srcdir)/include/ruby.h
+init.o: init.c
+len.o: $(RUBY_EXTCONF_H)
+len.o: $(arch_hdrdir)/ruby/config.h
+len.o: $(hdrdir)/ruby/backward.h
+len.o: $(hdrdir)/ruby/defines.h
+len.o: $(hdrdir)/ruby/intern.h
+len.o: $(hdrdir)/ruby/missing.h
+len.o: $(hdrdir)/ruby/ruby.h
+len.o: $(hdrdir)/ruby/st.h
+len.o: $(hdrdir)/ruby/subst.h
+len.o: $(top_srcdir)/include/ruby.h
+len.o: len.c
+member.o: $(RUBY_EXTCONF_H)
+member.o: $(arch_hdrdir)/ruby/config.h
+member.o: $(hdrdir)/ruby/backward.h
+member.o: $(hdrdir)/ruby/defines.h
+member.o: $(hdrdir)/ruby/intern.h
+member.o: $(hdrdir)/ruby/missing.h
+member.o: $(hdrdir)/ruby/ruby.h
+member.o: $(hdrdir)/ruby/st.h
+member.o: $(hdrdir)/ruby/subst.h
+member.o: $(top_srcdir)/include/ruby.h
+member.o: member.c
+# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/-test-/struct/len.c b/ext/-test-/struct/len.c
new file mode 100644
index 0000000000..6153c720b2
--- /dev/null
+++ b/ext/-test-/struct/len.c
@@ -0,0 +1,13 @@
+#include "ruby.h"
+
+static VALUE
+bug_struct_len(VALUE obj)
+{
+ return LONG2NUM(RSTRUCT_LEN(obj));
+}
+
+void
+Init_len(VALUE klass)
+{
+ rb_define_method(klass, "rstruct_len", bug_struct_len, 0);
+}
diff --git a/ext/-test-/symbol/init.c b/ext/-test-/symbol/init.c
index 9e42e1a38b..20cf2fa079 100644
--- a/ext/-test-/symbol/init.c
+++ b/ext/-test-/symbol/init.c
@@ -11,7 +11,13 @@ sym_find(VALUE dummy, VALUE sym)
static VALUE
sym_pinneddown_p(VALUE dummy, VALUE sym)
{
- return rb_check_id(&sym) ? Qtrue : Qfalse;
+ ID id = rb_check_id(&sym);
+ if (!id) return Qnil;
+#ifdef ULL2NUM
+ return ULL2NUM(id);
+#else
+ return ULONG2NUM(id);
+#endif
}
void
diff --git a/ext/-test-/thread_fd_close/depend b/ext/-test-/thread_fd_close/depend
new file mode 100644
index 0000000000..7bc04c3db3
--- /dev/null
+++ b/ext/-test-/thread_fd_close/depend
@@ -0,0 +1,16 @@
+# AUTOGENERATED DEPENDENCIES START
+thread_fd_close.o: $(RUBY_EXTCONF_H)
+thread_fd_close.o: $(arch_hdrdir)/ruby/config.h
+thread_fd_close.o: $(hdrdir)/ruby/backward.h
+thread_fd_close.o: $(hdrdir)/ruby/defines.h
+thread_fd_close.o: $(hdrdir)/ruby/encoding.h
+thread_fd_close.o: $(hdrdir)/ruby/intern.h
+thread_fd_close.o: $(hdrdir)/ruby/io.h
+thread_fd_close.o: $(hdrdir)/ruby/missing.h
+thread_fd_close.o: $(hdrdir)/ruby/onigmo.h
+thread_fd_close.o: $(hdrdir)/ruby/oniguruma.h
+thread_fd_close.o: $(hdrdir)/ruby/ruby.h
+thread_fd_close.o: $(hdrdir)/ruby/st.h
+thread_fd_close.o: $(hdrdir)/ruby/subst.h
+thread_fd_close.o: thread_fd_close.c
+# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/-test-/thread_fd_close/extconf.rb b/ext/-test-/thread_fd_close/extconf.rb
new file mode 100644
index 0000000000..0d9694539c
--- /dev/null
+++ b/ext/-test-/thread_fd_close/extconf.rb
@@ -0,0 +1,2 @@
+# frozen_string_literal: true
+create_makefile('-test-/thread_fd_close')
diff --git a/ext/-test-/thread_fd_close/thread_fd_close.c b/ext/-test-/thread_fd_close/thread_fd_close.c
new file mode 100644
index 0000000000..4fd967c5b3
--- /dev/null
+++ b/ext/-test-/thread_fd_close/thread_fd_close.c
@@ -0,0 +1,14 @@
+#include "ruby/ruby.h"
+
+static VALUE
+thread_fd_close(VALUE ign, VALUE fd)
+{
+ rb_thread_fd_close(NUM2INT(fd));
+ return Qnil;
+}
+
+void
+Init_thread_fd_close(void)
+{
+ rb_define_singleton_method(rb_cIO, "thread_fd_close", thread_fd_close, 1);
+}
diff --git a/ext/-test-/time/depend b/ext/-test-/time/depend
new file mode 100644
index 0000000000..a9a770ba1e
--- /dev/null
+++ b/ext/-test-/time/depend
@@ -0,0 +1,35 @@
+# AUTOGENERATED DEPENDENCIES START
+init.o: $(RUBY_EXTCONF_H)
+init.o: $(arch_hdrdir)/ruby/config.h
+init.o: $(hdrdir)/ruby/backward.h
+init.o: $(hdrdir)/ruby/defines.h
+init.o: $(hdrdir)/ruby/intern.h
+init.o: $(hdrdir)/ruby/missing.h
+init.o: $(hdrdir)/ruby/ruby.h
+init.o: $(hdrdir)/ruby/st.h
+init.o: $(hdrdir)/ruby/subst.h
+init.o: $(top_srcdir)/include/ruby.h
+init.o: init.c
+leap_second.o: $(RUBY_EXTCONF_H)
+leap_second.o: $(arch_hdrdir)/ruby/config.h
+leap_second.o: $(hdrdir)/ruby/backward.h
+leap_second.o: $(hdrdir)/ruby/defines.h
+leap_second.o: $(hdrdir)/ruby/intern.h
+leap_second.o: $(hdrdir)/ruby/missing.h
+leap_second.o: $(hdrdir)/ruby/ruby.h
+leap_second.o: $(hdrdir)/ruby/st.h
+leap_second.o: $(hdrdir)/ruby/subst.h
+leap_second.o: $(top_srcdir)/include/ruby.h
+leap_second.o: leap_second.c
+new.o: $(RUBY_EXTCONF_H)
+new.o: $(arch_hdrdir)/ruby/config.h
+new.o: $(hdrdir)/ruby/backward.h
+new.o: $(hdrdir)/ruby/defines.h
+new.o: $(hdrdir)/ruby/intern.h
+new.o: $(hdrdir)/ruby/missing.h
+new.o: $(hdrdir)/ruby/ruby.h
+new.o: $(hdrdir)/ruby/st.h
+new.o: $(hdrdir)/ruby/subst.h
+new.o: $(top_srcdir)/include/ruby.h
+new.o: new.c
+# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/-test-/time/init.c b/ext/-test-/time/init.c
index 01a20b8b3d..91f8bf825b 100644
--- a/ext/-test-/time/init.c
+++ b/ext/-test-/time/init.c
@@ -1,6 +1,6 @@
#include "ruby.h"
-#define init(n) {void Init_##n(VALUE klass); Init_##n(klass);}
+#define init(n) {void Init_time_##n(VALUE klass); Init_time_##n(klass);}
void
Init_time(void)
diff --git a/ext/-test-/time/leap_second.c b/ext/-test-/time/leap_second.c
new file mode 100644
index 0000000000..7eed421b73
--- /dev/null
+++ b/ext/-test-/time/leap_second.c
@@ -0,0 +1,15 @@
+#include "ruby.h"
+
+void ruby_reset_leap_second_info(void);
+static VALUE
+bug_time_s_reset_leap_second_info(VALUE klass)
+{
+ ruby_reset_leap_second_info();
+ return Qnil;
+}
+
+void
+Init_time_leap_second(VALUE klass)
+{
+ rb_define_singleton_method(klass, "reset_leap_second_info", bug_time_s_reset_leap_second_info, 0);
+}
diff --git a/ext/-test-/time/new.c b/ext/-test-/time/new.c
index 0f71a5542b..6f894417b6 100644
--- a/ext/-test-/time/new.c
+++ b/ext/-test-/time/new.c
@@ -26,7 +26,7 @@ bug_time_s_timespec_now(VALUE klass)
}
void
-Init_new(VALUE klass)
+Init_time_new(VALUE klass)
{
rb_define_singleton_method(klass, "nano_new", bug_time_s_nano_new, 2);
rb_define_singleton_method(klass, "timespec_new", bug_time_s_timespec_new, 3);
diff --git a/ext/-test-/typeddata/typeddata.c b/ext/-test-/typeddata/typeddata.c
index 1c5d677713..ae060960cd 100644
--- a/ext/-test-/typeddata/typeddata.c
+++ b/ext/-test-/typeddata/typeddata.c
@@ -2,19 +2,43 @@
static const rb_data_type_t test_data = {
"typed_data",
+ {NULL, ruby_xfree, NULL},
+ NULL, NULL,
+ 0/* deferred free */,
};
static VALUE
+test_alloc(VALUE klass)
+{
+ char *p;
+ return TypedData_Make_Struct(klass, char, &test_data, p);
+}
+
+static VALUE
test_check(VALUE self, VALUE obj)
{
rb_check_typeddata(obj, &test_data);
return obj;
}
+static VALUE
+test_make(VALUE klass, VALUE num)
+{
+ unsigned long i, n = NUM2UINT(num);
+
+ for (i = 0; i < n; i++) {
+ test_alloc(klass);
+ }
+
+ return Qnil;
+}
+
void
Init_typeddata(void)
{
VALUE mBug = rb_define_module("Bug");
VALUE klass = rb_define_class_under(mBug, "TypedData", rb_cData);
+ rb_define_alloc_func(klass, test_alloc);
rb_define_singleton_method(klass, "check", test_check, 1);
+ rb_define_singleton_method(klass, "make", test_make, 1);
}
diff --git a/ext/-test-/wait_for_single_fd/depend b/ext/-test-/wait_for_single_fd/depend
index 949c427685..edd2f88dcf 100644
--- a/ext/-test-/wait_for_single_fd/depend
+++ b/ext/-test-/wait_for_single_fd/depend
@@ -7,6 +7,7 @@ wait_for_single_fd.o: $(hdrdir)/ruby/encoding.h
wait_for_single_fd.o: $(hdrdir)/ruby/intern.h
wait_for_single_fd.o: $(hdrdir)/ruby/io.h
wait_for_single_fd.o: $(hdrdir)/ruby/missing.h
+wait_for_single_fd.o: $(hdrdir)/ruby/onigmo.h
wait_for_single_fd.o: $(hdrdir)/ruby/oniguruma.h
wait_for_single_fd.o: $(hdrdir)/ruby/ruby.h
wait_for_single_fd.o: $(hdrdir)/ruby/st.h
diff --git a/ext/-test-/wait_for_single_fd/extconf.rb b/ext/-test-/wait_for_single_fd/extconf.rb
index 931662c040..2a976c8f4b 100644
--- a/ext/-test-/wait_for_single_fd/extconf.rb
+++ b/ext/-test-/wait_for_single_fd/extconf.rb
@@ -1,2 +1,4 @@
# frozen_string_literal: false
+headers = %w(sys/types.h sys/time.h sys/event.h).select { |h| have_header(h) }
+have_func('kqueue', headers)
create_makefile("-test-/wait_for_single_fd")
diff --git a/ext/-test-/wait_for_single_fd/wait_for_single_fd.c b/ext/-test-/wait_for_single_fd/wait_for_single_fd.c
index d406724a3f..b8a33979bc 100644
--- a/ext/-test-/wait_for_single_fd/wait_for_single_fd.c
+++ b/ext/-test-/wait_for_single_fd/wait_for_single_fd.c
@@ -19,6 +19,67 @@ wait_for_single_fd(VALUE ign, VALUE fd, VALUE events, VALUE timeout)
return INT2NUM(rc);
}
+#ifdef HAVE_KQUEUE
+/* ensure rb_wait_for_single_fd works on kqueue descriptors */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/event.h>
+static VALUE
+kqueue_test_wait(VALUE klass)
+{
+ int kqfd = -1;
+ int p[2] = { -1, -1 };
+ struct timeval tv = { 0, 0 };
+ const struct timespec ts = { 1, 0 };
+ struct kevent kev;
+ const char *msg;
+ VALUE ret = Qfalse;
+ int e = 0;
+ int n;
+
+ msg = "pipe";
+ if (rb_cloexec_pipe(p) < 0) goto err;
+
+ msg = "kqueue";
+ kqfd = kqueue();
+ if (kqfd < 0) goto err;
+
+ n = rb_wait_for_single_fd(kqfd, RB_WAITFD_IN, &tv);
+ if (n != 0) {
+ msg = "spurious wakeup";
+ errno = 0;
+ goto err;
+ }
+
+ msg = "write";
+ if (write(p[1], "", 1) < 0) goto err;
+
+ EV_SET(&kev, p[0], EVFILT_READ, EV_ADD, 0, 0, 0);
+
+ msg = "kevent";
+ n = kevent(kqfd, &kev, 1, &kev, 1, &ts);
+ if (n < 0) goto err;
+ msg = NULL;
+ if (n == 1) {
+ n = rb_wait_for_single_fd(kqfd, RB_WAITFD_IN, &tv);
+ ret = INT2NUM(n);
+ }
+ else {
+ rb_warn("kevent did not return readiness");
+ }
+err:
+ if (msg) e = errno;
+ if (p[0] >= 0) close(p[0]);
+ if (p[1] >= 0) close(p[1]);
+ if (kqfd >= 0) close(kqfd);
+ if (msg) {
+ if (e) rb_syserr_fail(e, msg);
+ rb_raise(rb_eRuntimeError, "%s", msg);
+ }
+ return ret;
+}
+#endif /* HAVE_KQUEUE */
+
void
Init_wait_for_single_fd(void)
{
@@ -27,4 +88,7 @@ Init_wait_for_single_fd(void)
rb_define_const(rb_cObject, "RB_WAITFD_PRI", INT2NUM(RB_WAITFD_PRI));
rb_define_singleton_method(rb_cIO, "wait_for_single_fd",
wait_for_single_fd, 3);
+#ifdef HAVE_KQUEUE
+ rb_define_singleton_method(rb_cIO, "kqueue_test_wait", kqueue_test_wait, 0);
+#endif
}
diff --git a/ext/-test-/win32/console/attribute.c b/ext/-test-/win32/console/attribute.c
index 6d706fbfe7..a5f80fcaff 100644
--- a/ext/-test-/win32/console/attribute.c
+++ b/ext/-test-/win32/console/attribute.c
@@ -61,4 +61,9 @@ Init_attribute(VALUE m)
rb_define_const(m, "BACKGROUND_GREEN", INT2FIX(BACKGROUND_GREEN));
rb_define_const(m, "BACKGROUND_RED", INT2FIX(BACKGROUND_RED));
rb_define_const(m, "BACKGROUND_INTENSITY", INT2FIX(BACKGROUND_INTENSITY));
+
+#ifndef COMMON_LVB_REVERSE_VIDEO
+#define COMMON_LVB_REVERSE_VIDEO 0x4000
+#endif
+ rb_define_const(m, "REVERSE_VIDEO", INT2FIX(COMMON_LVB_REVERSE_VIDEO));
}
diff --git a/ext/.document b/ext/.document
index 2e6fc78879..6a491576a1 100644
--- a/ext/.document
+++ b/ext/.document
@@ -13,12 +13,6 @@ digest/md5/md5init.c
digest/rmd160/rmd160init.c
digest/sha1/sha1init.c
digest/sha2/sha2init.c
-dl/cfunc.c
-dl/cptr.c
-dl/dl.c
-dl/handle.c
-dl/lib
-dl/win32/lib
etc/etc.c
fcntl/fcntl.c
fiber/fiber.c
@@ -29,17 +23,13 @@ fiddle/function.c
fiddle/pointer.c
fiddle/handle.c
fiddle/lib
-fiddle/win32/lib
gdbm/gdbm.c
io/console/console.c
io/nonblock/nonblock.c
-io/wait/lib
io/wait/wait.c
-json/ext/generator/generator.c
-json/ext/parser/parser.c
+json/generator/generator.c
json/lib
-mathn/complex/complex.c
-mathn/rational/rational.c
+json/parser/parser.c
nkf/lib
nkf/nkf.c
objspace/objspace.c
@@ -55,10 +45,10 @@ openssl/ossl_config.c
openssl/ossl_digest.c
openssl/ossl_engine.c
openssl/ossl_hmac.c
+openssl/ossl_kdf.c
openssl/ossl_ns_spki.c
openssl/ossl_ocsp.c
openssl/ossl_pkcs12.c
-openssl/ossl_pkcs5.c
openssl/ossl_pkcs7.c
openssl/ossl_pkey.c
openssl/ossl_pkey_dh.c
@@ -79,25 +69,23 @@ openssl/ossl_x509revoked.c
openssl/ossl_x509store.c
pathname/lib
pathname/pathname.c
-psych/emitter.c
psych/lib
-psych/parser.c
psych/psych.c
-psych/to_ruby.c
-psych/yaml_tree.c
+psych/psych_emitter.c
+psych/psych_parser.c
+psych/psych_to_ruby.c
+psych/psych_yaml_tree.c
pty/lib
pty/pty.c
racc/cparse/cparse.c
readline/readline.c
ripper/lib
-ripper/ripper.c
sdbm/init.c
socket
stringio/stringio.c
strscan/strscan.c
syslog/syslog.c
syslog/lib
-thread/thread.c
win32ole/lib
win32ole/*.c
zlib/zlib.c
diff --git a/ext/Setup b/ext/Setup
index 0929d647ae..ac79c86f84 100644
--- a/ext/Setup
+++ b/ext/Setup
@@ -23,8 +23,6 @@
#json
#json/generator
#json/parser
-#mathn/complex
-#mathn/rational
#nkf
#objspace
#openssl
diff --git a/ext/Setup.nacl b/ext/Setup.nacl
deleted file mode 100644
index eb8a1a70cd..0000000000
--- a/ext/Setup.nacl
+++ /dev/null
@@ -1,44 +0,0 @@
-# #option nodynamic
-#
-# #Win32API
-# bigdecimal
-# cgi/escape
-# continuation
-# coverage
-# date
-# #dbm
-# digest/bubblebabble
-# digest
-# digest/md5
-# digest/rmd160
-# digest/sha1
-# digest/sha2
-# etc
-# fcntl
-# fiber
-# #fiddle
-# #gdbm
-# #iconv
-# io/console
-# io/nonblock
-# io/wait
-# #json
-# json/generator
-# json/parser
-# mathn/complex
-# mathn/rational
-# nkf
-# objspace
-# #openssl
-# pathname
-# #psych
-# #pty
-# racc/cparse
-# #readline
-# ripper
-# #sdbm
-# #socket
-# stringio
-# strscan
-# #syslog
-# #zlib
diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c
index 7df56e3aa1..da1b24a631 100644
--- a/ext/bigdecimal/bigdecimal.c
+++ b/ext/bigdecimal/bigdecimal.c
@@ -62,6 +62,7 @@ static ID id_ceil;
static ID id_floor;
static ID id_to_r;
static ID id_eq;
+static ID id_half;
/* MACRO's to guard objects from GC by keeping them in stack */
#define ENTER(n) volatile VALUE RB_UNUSED_VAR(vStack[n]);int iStack=0
@@ -109,7 +110,7 @@ rb_rational_num(VALUE rat)
#ifdef HAVE_TYPE_STRUCT_RRATIONAL
return RRATIONAL(rat)->num;
#else
- return rb_funcall(rat, rb_intern("numerator"));
+ return rb_funcall(rat, rb_intern("numerator"), 0);
#endif
}
#endif
@@ -121,31 +122,20 @@ rb_rational_den(VALUE rat)
#ifdef HAVE_TYPE_STRUCT_RRATIONAL
return RRATIONAL(rat)->den;
#else
- return rb_funcall(rat, rb_intern("denominator"));
+ return rb_funcall(rat, rb_intern("denominator"), 0);
#endif
}
#endif
+#define BIGDECIMAL_POSITIVE_P(bd) ((bd)->sign > 0)
+#define BIGDECIMAL_NEGATIVE_P(bd) ((bd)->sign < 0)
+
/*
* ================== Ruby Interface part ==========================
*/
#define DoSomeOne(x,y,f) rb_num_coerce_bin(x,y,f)
/*
- * Returns the BigDecimal version number.
- */
-static VALUE
-BigDecimal_version(VALUE self)
-{
- /*
- * 1.0.0: Ruby 1.8.0
- * 1.0.1: Ruby 1.8.1
- * 1.1.0: Ruby 1.9.3
- */
- return rb_str_new2("1.1.0");
-}
-
-/*
* VP routines used in BigDecimal part
*/
static unsigned short VpGetException(void);
@@ -227,6 +217,7 @@ static inline VALUE BigDecimal_div2(VALUE, VALUE, VALUE);
static Real*
GetVpValueWithPrec(VALUE v, long prec, int must)
{
+ ENTER(1);
Real *pv;
VALUE num, bg;
char szD[128];
@@ -240,9 +231,8 @@ again:
if (prec > DBL_DIG+1) goto SomeOneMayDoIt;
d = RFLOAT_VALUE(v);
if (!isfinite(d)) {
- pv = VpCreateRbObject(prec, NULL);
- pv->sign = isnan(d) ? VP_SIGN_NaN :
- d > 0 ? VP_SIGN_POSITIVE_INFINITE : VP_SIGN_NEGATIVE_FINITE;
+ pv = VpCreateRbObject(1, NULL);
+ VpDtoV(pv, d);
return pv;
}
if (d != 0.0) {
@@ -285,13 +275,15 @@ again:
#ifdef ENABLE_NUMERIC_STRING
case T_STRING:
- SafeStringValue(v);
- return VpCreateRbObject(strlen(RSTRING_PTR(v)) + VpBaseFig() + 1,
+ StringValueCStr(v);
+ rb_check_safe_obj(v);
+ return VpCreateRbObject(RSTRING_LEN(v) + VpBaseFig() + 1,
RSTRING_PTR(v));
#endif /* ENABLE_NUMERIC_STRING */
case T_BIGNUM:
bg = rb_big2str(v, 10);
+ PUSH(bg);
return VpCreateRbObject(strlen(RSTRING_PTR(bg)) + VpBaseFig() + 1,
RSTRING_PTR(bg));
default:
@@ -332,15 +324,18 @@ BigDecimal_double_fig(VALUE self)
return INT2FIX(VpDblFig());
}
-/* call-seq:
- * precs
+/* call-seq:
+ * big_decimal.precs -> array
+ *
+ * Returns an Array of two Integer values.
*
- * Returns an Array of two Integer values.
+ * The first value is the current number of significant digits in the
+ * BigDecimal. The second value is the maximum number of significant digits
+ * for the BigDecimal.
*
- * The first value is the current number of significant digits in the
- * BigDecimal. The second value is the maximum number of significant digits
- * for the BigDecimal.
+ * BigDecimal('5').precs #=> [9, 18]
*/
+
static VALUE
BigDecimal_prec(VALUE self)
{
@@ -376,7 +371,7 @@ BigDecimal_hash(VALUE self)
hash ^= rb_memhash(p->frac, sizeof(BDIGIT)*p->Prec);
hash += p->exponent;
}
- return INT2FIX(hash);
+ return ST2FIX(hash);
}
/*
@@ -384,10 +379,10 @@ BigDecimal_hash(VALUE self)
*
* Method used to provide marshalling support.
*
- * inf = BigDecimal.new('Infinity')
- * #=> #<BigDecimal:1e16fa8,'Infinity',9(9)>
+ * inf = BigDecimal('Infinity')
+ * #=> Infinity
* BigDecimal._load(inf._dump)
- * #=> #<BigDecimal:1df8dc8,'Infinity',9(9)>
+ * #=> Infinity
*
* See the Marshal module.
*/
@@ -422,8 +417,8 @@ BigDecimal_load(VALUE self, VALUE str)
unsigned char ch;
unsigned long m=0;
- SafeStringValue(str);
- pch = (unsigned char *)RSTRING_PTR(str);
+ pch = (unsigned char *)StringValueCStr(str);
+ rb_check_safe_obj(str);
/* First get max prec */
while((*pch) != (unsigned char)'\0' && (ch = *pch++) != (unsigned char)':') {
if(!ISDIGIT(ch)) {
@@ -441,6 +436,55 @@ BigDecimal_load(VALUE self, VALUE str)
}
static unsigned short
+check_rounding_mode_option(VALUE const opts)
+{
+ VALUE mode;
+ char const *s;
+ long l;
+
+ assert(RB_TYPE_P(opts, T_HASH));
+
+ if (NIL_P(opts))
+ goto noopt;
+
+ mode = rb_hash_lookup2(opts, ID2SYM(id_half), Qundef);
+ if (mode == Qundef || NIL_P(mode))
+ goto noopt;
+
+ 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;
+ }
+ s = RSTRING_PTR(mode);
+ l = RSTRING_LEN(mode);
+ switch (l) {
+ case 2:
+ if (strncasecmp(s, "up", 2) == 0)
+ return VP_ROUND_HALF_UP;
+ break;
+ case 4:
+ if (strncasecmp(s, "even", 4) == 0)
+ return VP_ROUND_HALF_EVEN;
+ else if (strncasecmp(s, "down", 4) == 0)
+ return VP_ROUND_HALF_DOWN;
+ break;
+ 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);
+
+ noopt:
+ return VpGetRoundMode();
+}
+
+static unsigned short
check_rounding_mode(VALUE const v)
{
unsigned short sw;
@@ -562,7 +606,7 @@ BigDecimal_mode(int argc, VALUE *argv, VALUE self)
fo = VpSetRoundMode(sw);
return INT2FIX(fo);
}
- rb_raise(rb_eTypeError, "first argument for BigDecimal#mode invalid");
+ rb_raise(rb_eTypeError, "first argument for BigDecimal.mode invalid");
return Qnil;
}
@@ -588,12 +632,12 @@ GetAddSubPrec(Real *a, Real *b)
}
static SIGNED_VALUE
-GetPositiveInt(VALUE v)
+GetPrecisionInt(VALUE v)
{
SIGNED_VALUE n;
n = NUM2INT(v);
if (n < 0) {
- rb_raise(rb_eArgError, "argument must be positive");
+ rb_raise(rb_eArgError, "negative precision");
}
return n;
}
@@ -602,9 +646,10 @@ VP_EXPORT Real *
VpNewRbClass(size_t mx, const char *str, VALUE klass)
{
VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, 0);
- Real *pv = VpAlloc(mx,str);
+ Real *pv = VpAlloc(mx, str, 1, 1);
RTYPEDDATA_DATA(obj) = pv;
pv->obj = obj;
+ RB_OBJ_FREEZE(obj);
return pv;
}
@@ -682,7 +727,7 @@ static VALUE BigDecimal_split(VALUE self);
/* Returns the value as an Integer.
*
- * If the BigNumber is infinity or NaN, raises FloatDomainError.
+ * If the BigDecimal is infinity or NaN, raises FloatDomainError.
*/
static VALUE
BigDecimal_to_i(VALUE self)
@@ -707,7 +752,7 @@ BigDecimal_to_i(VALUE self)
VALUE ret;
ssize_t dpower = e - (ssize_t)RSTRING_LEN(digits);
- if (VpGetSign(p) < 0) {
+ if (BIGDECIMAL_NEGATIVE_P(p)) {
numerator = rb_funcall(numerator, '*', 1, INT2FIX(-1));
}
if (dpower < 0) {
@@ -762,17 +807,17 @@ BigDecimal_to_f(VALUE self)
overflow:
VpException(VP_EXCEPTION_OVERFLOW, "BigDecimal to Float conversion", 0);
- if (p->sign >= 0)
- return rb_float_new(VpGetDoublePosInf());
- else
+ if (BIGDECIMAL_NEGATIVE_P(p))
return rb_float_new(VpGetDoubleNegInf());
+ else
+ return rb_float_new(VpGetDoublePosInf());
underflow:
VpException(VP_EXCEPTION_UNDERFLOW, "BigDecimal to Float conversion", 0);
- if (p->sign >= 0)
- return rb_float_new(0.0);
- else
+ if (BIGDECIMAL_NEGATIVE_P(p))
return rb_float_new(-0.0);
+ else
+ return rb_float_new(0.0);
}
@@ -818,7 +863,7 @@ BigDecimal_to_r(VALUE self)
* be coerced into a BigDecimal value.
*
* e.g.
- * a = BigDecimal.new("1.0")
+ * a = BigDecimal("1.0")
* b = a / 2.0 #=> 0.5
*
* Note that coercing a String to a BigDecimal is not supported by default;
@@ -850,13 +895,14 @@ BigDecimal_coerce(VALUE self, VALUE other)
}
/*
- * call-seq: +@
+ * call-seq:
+ * +big_decimal -> big_decimal
*
* Return self.
*
- * e.g.
- * b = +a # b == a
+ * +BigDecimal('5') #=> 0.5e1
*/
+
static VALUE
BigDecimal_uplus(VALUE self)
{
@@ -1106,7 +1152,7 @@ BigDecimal_comp(VALUE self, VALUE r)
*
* Values may be coerced to perform the comparison:
*
- * BigDecimal.new('1.0') == 1.0 #=> true
+ * BigDecimal('1.0') == 1.0 #=> true
*/
static VALUE
BigDecimal_eq(VALUE self, VALUE r)
@@ -1167,14 +1213,14 @@ BigDecimal_ge(VALUE self, VALUE r)
}
/*
- * call-seq: -@
+ * call-seq:
+ * -big_decimal -> big_decimal
*
- * Return the negation of self.
+ * Return the negation of self.
*
- * e.g.
- * b = -a
- * b == a * -1
+ * -BigDecimal('5') #=> -0.5e1
*/
+
static VALUE
BigDecimal_neg(VALUE self)
{
@@ -1262,25 +1308,14 @@ BigDecimal_divide(Real **c, Real **res, Real **div, VALUE self, VALUE r)
return Qnil;
}
- /* call-seq:
- * div(value, digits)
- * quo(value)
- *
- * Divide by the specified value.
- *
- * e.g.
- * c = a.div(b,n)
- *
- * digits:: If specified and less than the number of significant digits of the
- * result, the result is rounded to that number of digits, according
- * to BigDecimal.mode.
- *
- * If digits is 0, the result is the same as the / operator. If not, the
- * result is an integer BigDecimal, by analogy with Float#div.
- *
- * The alias quo is provided since <code>div(value, 0)</code> is the same as
- * computing the quotient; see BigDecimal#divmod.
- */
+/* call-seq:
+ * a / b -> bigdecimal
+ * quo(value) -> bigdecimal
+ *
+ * Divide by the specified value.
+ *
+ * See BigDecimal#div.
+ */
static VALUE
BigDecimal_div(VALUE self, VALUE r)
/* For c = self/r: with round operation */
@@ -1479,8 +1514,8 @@ BigDecimal_remainder(VALUE self, VALUE r) /* remainder */
*
* require 'bigdecimal'
*
- * a = BigDecimal.new("42")
- * b = BigDecimal.new("9")
+ * a = BigDecimal("42")
+ * b = BigDecimal("9")
*
* q, m = a.divmod(b)
*
@@ -1523,7 +1558,7 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n)
}
/* div in BigDecimal sense */
- ix = GetPositiveInt(n);
+ ix = GetPrecisionInt(n);
if (ix == 0) {
return BigDecimal_div(self, b);
}
@@ -1546,6 +1581,37 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n)
}
}
+ /*
+ * Document-method: BigDecimal#div
+ *
+ * call-seq:
+ * div(value, digits) -> bigdecimal or integer
+ *
+ * Divide by the specified value.
+ *
+ * digits:: If specified and less than the number of significant digits of the
+ * result, the result is rounded to that number of digits, according
+ * to BigDecimal.mode.
+ *
+ * If digits is 0, the result is the same as for the / operator
+ * or #quo.
+ *
+ * If digits is not specified, the result is an integer,
+ * by analogy with Float#div; see also BigDecimal#divmod.
+ *
+ * Examples:
+ *
+ * a = BigDecimal("4")
+ * b = BigDecimal("3")
+ *
+ * a.div(b, 3) # => 0.133e1
+ *
+ * a.div(b, 0) # => 0.1333333333333333333e1
+ * a / b # => 0.1333333333333333333e1
+ * a.quo(b) # => 0.1333333333333333333e1
+ *
+ * a.div(b) # => 1
+ */
static VALUE
BigDecimal_div3(int argc, VALUE *argv, VALUE self)
{
@@ -1561,7 +1627,7 @@ BigDecimal_add2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
- SIGNED_VALUE mx = GetPositiveInt(n);
+ SIGNED_VALUE mx = GetPrecisionInt(n);
if (mx == 0) return BigDecimal_add(self, b);
else {
size_t pl = VpSetPrecLimit(0);
@@ -1591,7 +1657,7 @@ BigDecimal_sub2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
- SIGNED_VALUE mx = GetPositiveInt(n);
+ SIGNED_VALUE mx = GetPrecisionInt(n);
if (mx == 0) return BigDecimal_sub(self, b);
else {
size_t pl = VpSetPrecLimit(0);
@@ -1609,7 +1675,7 @@ BigDecimal_mult2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
- SIGNED_VALUE mx = GetPositiveInt(n);
+ SIGNED_VALUE mx = GetPrecisionInt(n);
if (mx == 0) return BigDecimal_mult(self, b);
else {
size_t pl = VpSetPrecLimit(0);
@@ -1621,11 +1687,16 @@ BigDecimal_mult2(VALUE self, VALUE b, VALUE n)
}
}
-/* Returns the absolute value, as a BigDecimal.
+/*
+ * call-seq:
+ * big_decimal.abs -> big_decimal
+ *
+ * Returns the absolute value, as a BigDecimal.
*
- * BigDecimal('5').abs #=> 5
- * BigDecimal('-3').abs #=> 3
+ * BigDecimal('5').abs #=> 0.5e1
+ * BigDecimal('-3').abs #=> 0.3e1
*/
+
static VALUE
BigDecimal_abs(VALUE self)
{
@@ -1658,7 +1729,7 @@ BigDecimal_sqrt(VALUE self, VALUE nFig)
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
- n = GetPositiveInt(nFig) + VpDblFig() + BASE_FIG;
+ n = GetPrecisionInt(nFig) + VpDblFig() + BASE_FIG;
if (mx <= n) mx = n;
GUARD_OBJ(c, VpCreateRbObject(mx, "0"));
VpSqrt(c, a);
@@ -1720,11 +1791,21 @@ BigDecimal_round(int argc, VALUE *argv, VALUE self)
iLoc = 0;
break;
case 1:
- iLoc = NUM2INT(vLoc);
+ if (RB_TYPE_P(vLoc, T_HASH)) {
+ sw = check_rounding_mode_option(vLoc);
+ }
+ else {
+ iLoc = NUM2INT(vLoc);
+ }
break;
case 2:
iLoc = NUM2INT(vLoc);
- sw = check_rounding_mode(vRound);
+ if (RB_TYPE_P(vRound, T_HASH)) {
+ sw = check_rounding_mode_option(vRound);
+ }
+ else {
+ sw = check_rounding_mode(vRound);
+ }
break;
default:
break;
@@ -1918,34 +1999,35 @@ BigDecimal_ceil(int argc, VALUE *argv, VALUE self)
*
* Examples:
*
- * BigDecimal.new('-123.45678901234567890').to_s('5F')
+ * BigDecimal('-123.45678901234567890').to_s('5F')
* #=> '-123.45678 90123 45678 9'
*
- * BigDecimal.new('123.45678901234567890').to_s('+8F')
+ * BigDecimal('123.45678901234567890').to_s('+8F')
* #=> '+123.45678901 23456789'
*
- * BigDecimal.new('123.45678901234567890').to_s(' F')
+ * BigDecimal('123.45678901234567890').to_s(' F')
* #=> ' 123.4567890123456789'
*/
static VALUE
BigDecimal_to_s(int argc, VALUE *argv, VALUE self)
{
ENTER(5);
- int fmt = 0; /* 0:E format */
- int fPlus = 0; /* =0:default,=1: set ' ' before digits ,set '+' before digits. */
+ int fmt = 0; /* 0: E format, 1: F format */
+ int fPlus = 0; /* 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
Real *vp;
volatile VALUE str;
char *psz;
char ch;
size_t nc, mc = 0;
+ SIGNED_VALUE m;
VALUE f;
GUARD_OBJ(vp, GetVpValue(self, 1));
if (rb_scan_args(argc, argv, "01", &f) == 1) {
if (RB_TYPE_P(f, T_STRING)) {
- SafeStringValue(f);
- psz = RSTRING_PTR(f);
+ psz = StringValueCStr(f);
+ rb_check_safe_obj(f);
if (*psz == ' ') {
fPlus = 1;
psz++;
@@ -1968,7 +2050,11 @@ BigDecimal_to_s(int argc, VALUE *argv, VALUE self)
}
}
else {
- mc = (size_t)GetPositiveInt(f);
+ m = NUM2INT(f);
+ if (m <= 0) {
+ rb_raise(rb_eArgError, "argument must be positive");
+ }
+ mc = (size_t)m;
}
}
if (fmt) {
@@ -2062,38 +2148,26 @@ BigDecimal_exponent(VALUE self)
return INT2NUM(e);
}
-/* Returns debugging information about the value as a string of comma-separated
- * values in angle brackets with a leading #:
- *
- * BigDecimal.new("1234.5678").inspect
- * #=> "#<BigDecimal:b7ea1130,'0.12345678E4',8(12)>"
+/* Returns a string representation of self.
*
- * The first part is the address, the second is the value as a string, and
- * the final part ss(mm) is the current number of significant digits and the
- * maximum number of significant digits, respectively.
+ * BigDecimal("1234.5678").inspect
+ * #=> "0.12345678e4"
*/
static VALUE
BigDecimal_inspect(VALUE self)
{
ENTER(5);
Real *vp;
- volatile VALUE obj;
+ volatile VALUE str;
size_t nc;
- char *psz, *tmp;
GUARD_OBJ(vp, GetVpValue(self, 1));
nc = VpNumOfChars(vp, "E");
- nc += (nc + 9) / 10;
-
- obj = rb_str_new(0, nc+256);
- psz = RSTRING_PTR(obj);
- sprintf(psz, "#<BigDecimal:%"PRIxVALUE",'", self);
- tmp = psz + strlen(psz);
- VpToString(vp, tmp, 10, 0);
- tmp += strlen(tmp);
- sprintf(tmp, "',%"PRIuSIZE"(%"PRIuSIZE")>", VpPrec(vp)*VpBaseFig(), VpMaxPrec(vp)*VpBaseFig());
- rb_str_resize(obj, strlen(psz));
- return obj;
+
+ str = rb_str_new(0, nc);
+ VpToString(vp, RSTRING_PTR(str), 0, 0);
+ rb_str_resize(str, strlen(RSTRING_PTR(str)));
+ return str;
}
static VALUE BigMath_s_exp(VALUE, VALUE, VALUE);
@@ -2239,7 +2313,7 @@ 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#");
+ y = VpCreateRbObject(n, "0");
RB_GC_GUARD(y->obj);
VpSetNaN(y);
return ToValue(y);
@@ -2303,7 +2377,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
if (is_negative(vexp)) {
y = VpCreateRbObject(n, "#0");
RB_GC_GUARD(y->obj);
- if (VpGetSign(x) < 0) {
+ if (BIGDECIMAL_NEGATIVE_P(x)) {
if (is_integer(vexp)) {
if (is_even(vexp)) {
/* (-0) ** (-even_integer) -> Infinity */
@@ -2342,7 +2416,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
if (VpIsInf(x)) {
if (is_negative(vexp)) {
- if (VpGetSign(x) < 0) {
+ if (BIGDECIMAL_NEGATIVE_P(x)) {
if (is_integer(vexp)) {
if (is_even(vexp)) {
/* (-Infinity) ** (-even_integer) -> +0 */
@@ -2363,8 +2437,8 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
}
}
else {
- y = VpCreateRbObject(n, "0#");
- if (VpGetSign(x) < 0) {
+ y = VpCreateRbObject(n, "0");
+ if (BIGDECIMAL_NEGATIVE_P(x)) {
if (is_integer(vexp)) {
if (is_even(vexp)) {
VpSetPosInf(y);
@@ -2396,7 +2470,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
}
else if (RTEST(rb_funcall(abs_value, '<', 1, INT2FIX(1)))) {
if (is_negative(vexp)) {
- y = VpCreateRbObject(n, "0#");
+ y = VpCreateRbObject(n, "0");
if (is_even(vexp)) {
VpSetInf(y, VpGetSign(x));
}
@@ -2405,7 +2479,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
}
return ToValue(y);
}
- else if (VpGetSign(x) < 0 && is_even(vexp)) {
+ else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
return ToValue(VpCreateRbObject(n, "-0"));
}
else {
@@ -2414,7 +2488,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
}
else {
if (is_positive(vexp)) {
- y = VpCreateRbObject(n, "0#");
+ y = VpCreateRbObject(n, "0");
if (is_even(vexp)) {
VpSetInf(y, VpGetSign(x));
}
@@ -2423,7 +2497,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
}
return ToValue(y);
}
- else if (VpGetSign(x) < 0 && is_even(vexp)) {
+ else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
return ToValue(VpCreateRbObject(n, "-0"));
}
else {
@@ -2464,65 +2538,6 @@ BigDecimal_power_op(VALUE self, VALUE exp)
return BigDecimal_power(1, &exp, self);
}
-static VALUE
-BigDecimal_s_allocate(VALUE klass)
-{
- return VpNewRbClass(0, NULL, klass)->obj;
-}
-
-static Real *BigDecimal_new(int argc, VALUE *argv);
-
-/* call-seq:
- * new(initial, digits)
- *
- * Create a new BigDecimal object.
- *
- * initial:: The initial value, as an Integer, a Float, a Rational,
- * a BigDecimal, or a String.
- *
- * If it is a String, spaces are ignored and unrecognized characters
- * terminate the value.
- *
- * digits:: The number of significant digits, as an Integer. If omitted or 0,
- * the number of significant digits is determined from the initial
- * value.
- *
- * The actual number of significant digits used in computation is usually
- * larger than the specified number.
- *
- * ==== Exceptions
- *
- * TypeError:: If the +initial+ type is neither Integer, Float,
- * Rational, nor BigDecimal, this exception is raised.
- *
- * TypeError:: If the +digits+ is not an Integer, this exception is raised.
- *
- * ArgumentError:: If +initial+ is a Float, and the +digits+ is larger than
- * Float::DIG + 1, this exception is raised.
- *
- * ArgumentError:: If the +initial+ is a Float or Rational, and the +digits+
- * value is omitted, this exception is raised.
- */
-static VALUE
-BigDecimal_initialize(int argc, VALUE *argv, VALUE self)
-{
- ENTER(1);
- Real *pv = rb_check_typeddata(self, &BigDecimal_data_type);
- Real *x;
-
- GUARD_OBJ(x, BigDecimal_new(argc, argv));
- if (ToValue(x)) {
- pv = VpCopy(pv, x);
- }
- else {
- VpFree(pv);
- pv = x;
- }
- DATA_PTR(self) = pv;
- pv->obj = self;
- return self;
-}
-
/* :nodoc:
*
* private method for dup and clone the provided BigDecimal +other+
@@ -2539,18 +2554,66 @@ BigDecimal_initialize_copy(VALUE self, VALUE other)
return self;
}
+static VALUE
+BigDecimal_clone(VALUE self)
+{
+ return self;
+}
+
+static int
+opts_exception_p(VALUE opts)
+{
+ static ID kwds[1];
+ VALUE exception;
+ if (!kwds[0]) {
+ kwds[0] = rb_intern_const("exception");
+ }
+ rb_get_kwargs(opts, kwds, 0, 1, &exception);
+ return exception != Qfalse;
+}
+
static Real *
BigDecimal_new(int argc, VALUE *argv)
{
size_t mf;
+ VALUE opts = Qnil;
VALUE nFig;
VALUE iniValue;
+ double d;
+ int exc;
+
+ argc = rb_scan_args(argc, argv, "11:", &iniValue, &nFig, &opts);
+ exc = opts_exception_p(opts);
- if (rb_scan_args(argc, argv, "11", &iniValue, &nFig) == 1) {
+ if (argc == 1) {
mf = 0;
}
else {
- mf = GetPositiveInt(nFig);
+ /* expand GetPrecisionInt for exception suppression */
+ ssize_t n = NUM2INT(nFig);
+ if (n < 0) {
+ if (!exc) {
+ return NULL;
+ }
+ rb_raise(rb_eArgError, "negative precision");
+ }
+ mf = (size_t)n;
+ }
+
+ if (SPECIAL_CONST_P(iniValue)) {
+ switch (iniValue) {
+ case Qnil:
+ if (!exc) return NULL;
+ rb_raise(rb_eTypeError, "can't convert nil into BigDecimal");
+ case Qtrue:
+ if (!exc) return NULL;
+ rb_raise(rb_eTypeError, "can't convert true into BigDecimal");
+ case Qfalse:
+ if (!exc) return NULL;
+ rb_raise(rb_eTypeError, "can't convert false into BigDecimal");
+ default:
+ break;
+ }
}
switch (TYPE(iniValue)) {
@@ -2566,12 +2629,24 @@ BigDecimal_new(int argc, VALUE *argv)
return GetVpValue(iniValue, 1);
case T_FLOAT:
+ d = RFLOAT_VALUE(iniValue);
+ if (!isfinite(d)) {
+ Real *pv = VpCreateRbObject(1, NULL);
+ VpDtoV(pv, d);
+ return pv;
+ }
if (mf > DBL_DIG+1) {
+ if (!exc) {
+ return NULL;
+ }
rb_raise(rb_eArgError, "precision too large.");
}
/* fall through */
case T_RATIONAL:
if (NIL_P(nFig)) {
+ if (!exc) {
+ return NULL;
+ }
rb_raise(rb_eArgError,
"can't omit precision for a %"PRIsVALUE".",
RB_OBJ_CLASSNAME(iniValue));
@@ -2583,22 +2658,65 @@ BigDecimal_new(int argc, VALUE *argv)
default:
break;
}
+ /* TODO: support to_d */
+ if (!exc) {
+ iniValue = rb_check_convert_type(iniValue, T_STRING, "String", "to_str");
+ if (NIL_P(iniValue)) return NULL;
+ }
StringValueCStr(iniValue);
- return VpAlloc(mf, RSTRING_PTR(iniValue));
+ return VpAlloc(mf, RSTRING_PTR(iniValue), 1, exc);
}
-/* See also BigDecimal.new */
+/* call-seq:
+ * BigDecimal(initial, digits, exception: true)
+ *
+ * Create a new BigDecimal object.
+ *
+ * initial:: The initial value, as an Integer, a Float, a Rational,
+ * a BigDecimal, or a String.
+ *
+ * If it is a String, spaces are ignored and unrecognized characters
+ * terminate the value.
+ *
+ * digits:: The number of significant digits, as an Integer. If omitted or 0,
+ * the number of significant digits is determined from the initial
+ * value.
+ *
+ * The actual number of significant digits used in computation is
+ * usually larger than the specified number.
+ *
+ * exception:: Whether an exception should be raised on invalid arguments.
+ * +true+ by default, if passed +false+, just returns +nil+
+ * for invalid.
+ *
+ *
+ * ==== Exceptions
+ *
+ * TypeError:: If the +initial+ type is neither Integer, Float,
+ * Rational, nor BigDecimal, this exception is raised.
+ *
+ * TypeError:: If the +digits+ is not an Integer, this exception is raised.
+ *
+ * ArgumentError:: If +initial+ is a Float, and the +digits+ is larger than
+ * Float::DIG + 1, this exception is raised.
+ *
+ * ArgumentError:: If the +initial+ is a Float or Rational, and the +digits+
+ * value is omitted, this exception is raised.
+ */
static VALUE
-BigDecimal_global_new(int argc, VALUE *argv, VALUE self)
+f_BigDecimal(int argc, VALUE *argv, VALUE self)
{
ENTER(1);
Real *pv;
VALUE obj;
obj = TypedData_Wrap_Struct(rb_cBigDecimal, &BigDecimal_data_type, 0);
- GUARD_OBJ(pv, BigDecimal_new(argc, argv));
+ pv = BigDecimal_new(argc, argv);
+ if (pv == NULL) return Qnil;
+ SAVE(pv);
if (ToValue(pv)) pv = VpCopy(NULL, pv);
RTYPEDDATA_DATA(obj) = pv;
+ RB_OBJ_FREEZE(obj);
return pv->obj = obj;
}
@@ -2664,9 +2782,9 @@ BigDecimal_sign(VALUE self)
* BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
* BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
*
- * BigDecimal.new(BigDecimal('Infinity'))
- * BigDecimal.new(BigDecimal('-Infinity'))
- * BigDecimal(BigDecimal.new('NaN'))
+ * BigDecimal(BigDecimal('Infinity'))
+ * BigDecimal(BigDecimal('-Infinity'))
+ * BigDecimal(BigDecimal('NaN'))
* end
*
* For use with the BigDecimal::EXCEPTION_*
@@ -2766,7 +2884,7 @@ BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec)
case T_DATA:
if (!is_kind_of_BigDecimal(x)) break;
vx = DATA_PTR(x);
- negative = VpGetSign(vx) < 0;
+ negative = BIGDECIMAL_NEGATIVE_P(vx);
infinite = VpIsPosInf(vx) || VpIsNegInf(vx);
nan = VpIsNaN(vx);
break;
@@ -2819,7 +2937,7 @@ BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec)
x = vx->obj;
n = prec + rmpd_double_figures();
- negative = VpGetSign(vx) < 0;
+ negative = BIGDECIMAL_NEGATIVE_P(vx);
if (negative) {
VpSetSign(vx, 1);
}
@@ -2905,7 +3023,7 @@ BigMath_s_log(VALUE klass, VALUE x, VALUE vprec)
if (!is_kind_of_BigDecimal(x)) break;
vx = DATA_PTR(x);
zero = VpIsZero(vx);
- negative = VpGetSign(vx) < 0;
+ negative = BIGDECIMAL_NEGATIVE_P(vx);
infinite = VpIsPosInf(vx) || VpIsNegInf(vx);
nan = VpIsNaN(vx);
break;
@@ -3022,6 +3140,20 @@ get_vp_value:
return y;
}
+VALUE
+rmpd_util_str_to_d(VALUE str)
+{
+ ENTER(1);
+ char const *c_str;
+ Real *pv;
+
+ c_str = StringValueCStr(str);
+ GUARD_OBJ(pv, VpAlloc(0, c_str, 0, 1));
+ pv->obj = TypedData_Wrap_Struct(rb_cBigDecimal, &BigDecimal_data_type, pv);
+ RB_OBJ_FREEZE(pv->obj);
+ return pv->obj;
+}
+
/* Document-class: BigDecimal
* BigDecimal provides arbitrary-precision floating point decimal arithmetic.
*
@@ -3053,15 +3185,15 @@ get_vp_value:
*
* require 'bigdecimal'
*
- * sum = BigDecimal.new("0")
+ * sum = BigDecimal("0")
* 10_000.times do
- * sum = sum + BigDecimal.new("0.0001")
+ * sum = sum + BigDecimal("0.0001")
* end
* print sum #=> 0.1E1
*
* Similarly:
*
- * (BigDecimal.new("1.2") - BigDecimal("1.0")) == BigDecimal("0.2") #=> true
+ * (BigDecimal("1.2") - BigDecimal("1.0")) == BigDecimal("0.2") #=> true
*
* (1.2 - 1.0) == 0.2 #=> false
*
@@ -3075,8 +3207,8 @@ get_vp_value:
* BigDecimal sometimes needs to return infinity, for example if you divide
* a value by zero.
*
- * BigDecimal.new("1.0") / BigDecimal.new("0.0") #=> Infinity
- * BigDecimal.new("-1.0") / BigDecimal.new("0.0") #=> -Infinity
+ * BigDecimal("1.0") / BigDecimal("0.0") #=> Infinity
+ * BigDecimal("-1.0") / BigDecimal("0.0") #=> -Infinity
*
* You can represent infinite numbers to BigDecimal using the strings
* <code>'Infinity'</code>, <code>'+Infinity'</code> and
@@ -3089,13 +3221,13 @@ get_vp_value:
*
* Example:
*
- * BigDecimal.new("0.0") / BigDecimal.new("0.0") #=> NaN
+ * BigDecimal("0.0") / BigDecimal("0.0") #=> NaN
*
* You can also create undefined values.
*
* NaN is never considered to be the same as any other value, even NaN itself:
*
- * n = BigDecimal.new('NaN')
+ * n = BigDecimal('NaN')
* n == 0.0 #=> false
* n == n #=> false
*
@@ -3108,11 +3240,11 @@ get_vp_value:
* If the value which is too small to be represented is negative, a BigDecimal
* value of negative zero is returned.
*
- * BigDecimal.new("1.0") / BigDecimal.new("-Infinity") #=> -0.0
+ * BigDecimal("1.0") / BigDecimal("-Infinity") #=> -0.0
*
* If the value is positive, a value of positive zero is returned.
*
- * BigDecimal.new("1.0") / BigDecimal.new("Infinity") #=> 0.0
+ * BigDecimal("1.0") / BigDecimal("Infinity") #=> 0.0
*
* (See BigDecimal.mode for how to specify limits of precision.)
*
@@ -3122,6 +3254,19 @@ get_vp_value:
* Note also that in mathematics, there is no particular concept of negative
* or positive zero; true mathematical zero has no sign.
*
+ * == bigdecimal/util
+ *
+ * When you require +bigdecimal/util+, the #to_d method will be
+ * available on BigDecimal and the native Integer, Float, Rational,
+ * and String classes:
+ *
+ * require 'bigdecimal/util'
+ *
+ * 42.to_d # => 0.42e2
+ * 0.5.to_d # => 0.5e0
+ * (2/3r).to_d(3) # => 0.667e0
+ * "0.5".to_d # => 0.5e0
+ *
* == License
*
* Copyright (C) 2002 by Shigeo Kobayashi <shigeo@tinyforest.gr.jp>.
@@ -3148,17 +3293,17 @@ Init_bigdecimal(void)
/* Class and method registration */
rb_cBigDecimal = rb_define_class("BigDecimal", rb_cNumeric);
- rb_define_alloc_func(rb_cBigDecimal, BigDecimal_s_allocate);
/* Global function */
- rb_define_global_function("BigDecimal", BigDecimal_global_new, -1);
+ rb_define_global_function("BigDecimal", f_BigDecimal, -1);
/* Class methods */
+ rb_undef_method(CLASS_OF(rb_cBigDecimal), "allocate");
+ rb_undef_method(CLASS_OF(rb_cBigDecimal), "new");
rb_define_singleton_method(rb_cBigDecimal, "mode", BigDecimal_mode, -1);
rb_define_singleton_method(rb_cBigDecimal, "limit", BigDecimal_limit, -1);
rb_define_singleton_method(rb_cBigDecimal, "double_fig", BigDecimal_double_fig, 0);
rb_define_singleton_method(rb_cBigDecimal, "_load", BigDecimal_load, 1);
- rb_define_singleton_method(rb_cBigDecimal, "ver", BigDecimal_version, 0);
rb_define_singleton_method(rb_cBigDecimal, "save_exception_mode", BigDecimal_save_exception_mode, 0);
rb_define_singleton_method(rb_cBigDecimal, "save_rounding_mode", BigDecimal_save_rounding_mode, 0);
@@ -3166,6 +3311,14 @@ Init_bigdecimal(void)
/* Constants definition */
+#ifndef RUBY_BIGDECIMAL_VERSION
+# error RUBY_BIGDECIMAL_VERSION is not defined
+#endif
+ /*
+ * The version of bigdecimal library
+ */
+ rb_define_const(rb_cBigDecimal, "VERSION", rb_str_new2(RUBY_BIGDECIMAL_VERSION));
+
/*
* Base value used in internal calculations. On a 32 bit system, BASE
* is 10000, indicating that calculation is done in groups of 4 digits.
@@ -3208,7 +3361,7 @@ Init_bigdecimal(void)
rb_define_const(rb_cBigDecimal, "EXCEPTION_OVERFLOW", INT2FIX(VP_EXCEPTION_OVERFLOW));
/*
- * 0x01: Determines what happens when a division by zero is performed.
+ * 0x10: Determines what happens when a division by zero is performed.
* See BigDecimal.mode.
*/
rb_define_const(rb_cBigDecimal, "EXCEPTION_ZERODIVIDE", INT2FIX(VP_EXCEPTION_ZERODIVIDE));
@@ -3270,14 +3423,13 @@ Init_bigdecimal(void)
arg = rb_str_new2("+Infinity");
/* Positive infinity value. */
- rb_define_const(rb_cBigDecimal, "INFINITY", BigDecimal_global_new(1, &arg, rb_cBigDecimal));
+ rb_define_const(rb_cBigDecimal, "INFINITY", f_BigDecimal(1, &arg, rb_cBigDecimal));
arg = rb_str_new2("NaN");
/* 'Not a Number' value. */
- rb_define_const(rb_cBigDecimal, "NAN", BigDecimal_global_new(1, &arg, rb_cBigDecimal));
+ rb_define_const(rb_cBigDecimal, "NAN", f_BigDecimal(1, &arg, rb_cBigDecimal));
/* instance methods */
- rb_define_method(rb_cBigDecimal, "initialize", BigDecimal_initialize, -1);
rb_define_method(rb_cBigDecimal, "initialize_copy", BigDecimal_initialize_copy, 1);
rb_define_method(rb_cBigDecimal, "precs", BigDecimal_prec, 0);
@@ -3302,7 +3454,8 @@ Init_bigdecimal(void)
rb_define_method(rb_cBigDecimal, "modulo", BigDecimal_mod, 1);
rb_define_method(rb_cBigDecimal, "remainder", BigDecimal_remainder, 1);
rb_define_method(rb_cBigDecimal, "divmod", BigDecimal_divmod, 1);
- /* rb_define_method(rb_cBigDecimal, "dup", BigDecimal_dup, 0); */
+ rb_define_method(rb_cBigDecimal, "clone", BigDecimal_clone, 0);
+ rb_define_method(rb_cBigDecimal, "dup", BigDecimal_clone, 0);
rb_define_method(rb_cBigDecimal, "to_f", BigDecimal_to_f, 0);
rb_define_method(rb_cBigDecimal, "abs", BigDecimal_abs, 0);
rb_define_method(rb_cBigDecimal, "sqrt", BigDecimal_sqrt, 1);
@@ -3350,6 +3503,7 @@ Init_bigdecimal(void)
id_floor = rb_intern_const("floor");
id_to_r = rb_intern_const("to_r");
id_eq = rb_intern_const("==");
+ id_half = rb_intern_const("half");
}
/*
@@ -3377,7 +3531,14 @@ static Real *VpPt5; /* constant 0.5 */
#define MemCmp(x,y,z) memcmp(x,y,z)
#define StrCmp(x,y) strcmp(x,y)
-static int VpIsDefOP(Real *c,Real *a,Real *b,int sw);
+enum op_sw {
+ OP_SW_ADD = 1, /* + */
+ OP_SW_SUB, /* - */
+ OP_SW_MULT, /* * */
+ OP_SW_DIV /* / */
+};
+
+static int VpIsDefOP(Real *c, Real *a, Real *b, enum op_sw sw);
static int AddExponent(Real *a, SIGNED_VALUE n);
static BDIGIT VpAddAbs(Real *a,Real *b,Real *c);
static BDIGIT VpSubAbs(Real *a,Real *b,Real *c);
@@ -3404,7 +3565,7 @@ VpMemAlloc(size_t mb)
return p;
}
- VP_EXPORT void *
+VP_EXPORT void *
VpMemRealloc(void *ptr, size_t mb)
{
void *p = xrealloc(ptr, mb);
@@ -3567,13 +3728,7 @@ VpSetRoundMode(unsigned short n)
* (to let the compiler know they may be changed in outside
* (... but not actually..)).
*/
-volatile const double gZero_ABCED9B1_CE73__00400511F31D = 0.0;
volatile const double gOne_ABCED9B4_CE73__00400511F31D = 1.0;
-static double
-Zero(void)
-{
- return gZero_ABCED9B1_CE73__00400511F31D;
-}
static double
One(void)
@@ -3598,25 +3753,19 @@ One(void)
VP_EXPORT double
VpGetDoubleNaN(void) /* Returns the value of NaN */
{
- static double fNaN = 0.0;
- if (fNaN == 0.0) fNaN = Zero()/Zero();
- return fNaN;
+ return nan("");
}
VP_EXPORT double
VpGetDoublePosInf(void) /* Returns the value of +Infinity */
{
- static double fInf = 0.0;
- if (fInf == 0.0) fInf = One()/Zero();
- return fInf;
+ return HUGE_VAL;
}
VP_EXPORT double
VpGetDoubleNegInf(void) /* Returns the value of -Infinity */
{
- static double fInf = 0.0;
- if (fInf == 0.0) fInf = -(One()/Zero());
- return fInf;
+ return -HUGE_VAL;
}
VP_EXPORT double
@@ -3664,7 +3813,7 @@ VpException(unsigned short f, const char *str,int always)
/* Throw exception or returns 0,when resulting c is Inf or NaN */
/* sw=1:+ 2:- 3:* 4:/ */
static int
-VpIsDefOP(Real *c,Real *a,Real *b,int sw)
+VpIsDefOP(Real *c, Real *a, Real *b, enum op_sw sw)
{
if (VpIsNaN(a) || VpIsNaN(b)) {
/* at least a or b is NaN */
@@ -3675,7 +3824,7 @@ VpIsDefOP(Real *c,Real *a,Real *b,int sw)
if (VpIsInf(a)) {
if (VpIsInf(b)) {
switch(sw) {
- case 1: /* + */
+ case OP_SW_ADD: /* + */
if (VpGetSign(a) == VpGetSign(b)) {
VpSetInf(c, VpGetSign(a));
goto Inf;
@@ -3684,7 +3833,7 @@ VpIsDefOP(Real *c,Real *a,Real *b,int sw)
VpSetNaN(c);
goto NaN;
}
- case 2: /* - */
+ case OP_SW_SUB: /* - */
if (VpGetSign(a) != VpGetSign(b)) {
VpSetInf(c, VpGetSign(a));
goto Inf;
@@ -3693,12 +3842,10 @@ VpIsDefOP(Real *c,Real *a,Real *b,int sw)
VpSetNaN(c);
goto NaN;
}
- break;
- case 3: /* * */
+ case OP_SW_MULT: /* * */
VpSetInf(c, VpGetSign(a)*VpGetSign(b));
goto Inf;
- break;
- case 4: /* / */
+ case OP_SW_DIV: /* / */
VpSetNaN(c);
goto NaN;
}
@@ -3707,18 +3854,18 @@ VpIsDefOP(Real *c,Real *a,Real *b,int sw)
}
/* Inf op Finite */
switch(sw) {
- case 1: /* + */
- case 2: /* - */
+ case OP_SW_ADD: /* + */
+ case OP_SW_SUB: /* - */
VpSetInf(c, VpGetSign(a));
break;
- case 3: /* * */
+ case OP_SW_MULT: /* * */
if (VpIsZero(b)) {
VpSetNaN(c);
goto NaN;
}
VpSetInf(c, VpGetSign(a)*VpGetSign(b));
break;
- case 4: /* / */
+ case OP_SW_DIV: /* / */
VpSetInf(c, VpGetSign(a)*VpGetSign(b));
}
goto Inf;
@@ -3726,20 +3873,20 @@ VpIsDefOP(Real *c,Real *a,Real *b,int sw)
if (VpIsInf(b)) {
switch(sw) {
- case 1: /* + */
+ case OP_SW_ADD: /* + */
VpSetInf(c, VpGetSign(b));
break;
- case 2: /* - */
+ case OP_SW_SUB: /* - */
VpSetInf(c, -VpGetSign(b));
break;
- case 3: /* * */
+ case OP_SW_MULT: /* * */
if (VpIsZero(a)) {
VpSetNaN(c);
goto NaN;
}
VpSetInf(c, VpGetSign(a)*VpGetSign(b));
break;
- case 4: /* / */
+ case OP_SW_DIV: /* / */
VpSetZero(c, VpGetSign(a)*VpGetSign(b));
}
goto Inf;
@@ -3747,7 +3894,13 @@ VpIsDefOP(Real *c,Real *a,Real *b,int sw)
return 1; /* Results OK */
Inf:
- return VpException(VP_EXCEPTION_INFINITY, "Computation results to 'Infinity'", 0);
+ if (VpIsPosInf(c)) {
+ return VpException(VP_EXCEPTION_INFINITY, "Computation results to 'Infinity'", 0);
+ }
+ else {
+ return VpException(VP_EXCEPTION_INFINITY, "Computation results to '-Infinity'", 0);
+ }
+
NaN:
return VpException(VP_EXCEPTION_NaN, "Computation results to 'NaN'", 0);
}
@@ -3807,14 +3960,11 @@ VP_EXPORT size_t
VpInit(BDIGIT BaseVal)
{
/* Setup +/- Inf NaN -0 */
- VpGetDoubleNaN();
- VpGetDoublePosInf();
- VpGetDoubleNegInf();
VpGetDoubleNegZero();
/* Allocates Vp constants. */
- VpConstOne = VpAlloc(1UL, "1");
- VpPt5 = VpAlloc(1UL, ".5");
+ VpConstOne = VpAlloc(1UL, "1", 1, 1);
+ VpPt5 = VpAlloc(1UL, ".5", 1, 1);
#ifdef BIGDECIMAL_DEBUG
gnAlloc = 0;
@@ -3878,6 +4028,52 @@ overflow:
return VpException(VP_EXCEPTION_OVERFLOW, "Exponent overflow", 0);
}
+Real *
+rmpd_parse_special_string(const char *str)
+{
+ static const struct {
+ const char *str;
+ size_t len;
+ int sign;
+ } table[] = {
+ { SZ_INF, sizeof(SZ_INF) - 1, VP_SIGN_POSITIVE_INFINITE },
+ { SZ_PINF, sizeof(SZ_PINF) - 1, VP_SIGN_POSITIVE_INFINITE },
+ { SZ_NINF, sizeof(SZ_NINF) - 1, VP_SIGN_NEGATIVE_INFINITE },
+ { SZ_NaN, sizeof(SZ_NaN) - 1, VP_SIGN_NaN }
+ };
+ static const size_t table_length = sizeof(table) / sizeof(table[0]);
+ size_t i;
+
+ for (i = 0; i < table_length; ++i) {
+ const char *p;
+ if (strncmp(str, table[i].str, table[i].len) != 0) {
+ continue;
+ }
+
+ p = str + table[i].len;
+ while (*p && ISSPACE(*p)) ++p;
+ if (*p == '\0') {
+ Real *vp = VpAllocReal(1);
+ vp->MaxPrec = 1;
+ switch (table[i].sign) {
+ default:
+ UNREACHABLE; break;
+ case VP_SIGN_POSITIVE_INFINITE:
+ VpSetPosInf(vp);
+ return vp;
+ case VP_SIGN_NEGATIVE_INFINITE:
+ VpSetNegInf(vp);
+ return vp;
+ case VP_SIGN_NaN:
+ VpSetNaN(vp);
+ return vp;
+ }
+ }
+ }
+
+ return NULL;
+}
+
/*
* Allocates variable.
* [Input]
@@ -3892,9 +4088,10 @@ overflow:
* NULL be returned if memory allocation is failed,or any error.
*/
VP_EXPORT Real *
-VpAlloc(size_t mx, const char *szVal)
+VpAlloc(size_t mx, const char *szVal, int strict_p, int exc)
{
- size_t i, ni, ipn, ipf, nf, ipe, ne, nalloc;
+ const char *orig_szVal = szVal;
+ size_t i, j, ni, ipf, nf, ipe, ne, dot_seen, exp_seen, nalloc;
char v, *psz;
int sign=1;
Real *vp = NULL;
@@ -3905,7 +4102,10 @@ VpAlloc(size_t mx, const char *szVal)
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 */
@@ -3919,6 +4119,7 @@ VpAlloc(size_t mx, const char *szVal)
}
}
else {
+ return_zero:
/* necessary to be able to store */
/* at least mx digits. */
/* szVal==NULL ==> allocate zero value. */
@@ -3929,98 +4130,170 @@ VpAlloc(size_t mx, const char *szVal)
return vp;
}
- /* Skip all '_' after digit: 2006-6-30 */
- ni = 0;
+ /* Check on Inf & NaN */
+ if ((vp = rmpd_parse_special_string(szVal)) != NULL) {
+ return vp;
+ }
+
+ /* Scanning digits */
+
+ /* A buffer for keeping scanned digits */
buf = rb_str_tmp_new(strlen(szVal) + 1);
psz = RSTRING_PTR(buf);
- i = 0;
- ipn = 0;
- while ((psz[i] = szVal[ipn]) != 0) {
- if (ISSPACE(psz[i])) {
- psz[i] = 0;
+
+ /* cursor: i for psz, and j for szVal */
+ i = j = 0;
+
+ /* Scanning: sign part */
+ v = psz[i] = szVal[j];
+ if ((v == '-') || (v == '+')) {
+ sign = -(v == '-');
+ ++i;
+ ++j;
+ }
+
+ /* Scanning: integer part */
+ ni = 0; /* number of digits in the integer part */
+ while ((v = psz[i] = szVal[j]) != '\0') {
+ if (!strict_p && ISSPACE(v)) {
+ v = psz[i] = '\0';
break;
}
- if (ISDIGIT(psz[i])) ++ni;
- if (psz[i] == '_') {
+ if (v == '_') {
if (ni > 0) {
- ipn++;
- continue;
+ v = szVal[j+1];
+ if (v == '\0' || ISSPACE(v) || ISDIGIT(v)) {
+ ++j;
+ continue;
+ }
+ if (!strict_p) {
+ v = psz[i] = '\0';
+ break;
+ }
}
- psz[i] = 0;
+ goto invalid_value;
+ }
+ if (!ISDIGIT(v)) {
break;
}
+ ++ni;
++i;
- ++ipn;
+ ++j;
}
- szVal = psz;
- /* Check on Inf & NaN */
- if (StrCmp(szVal, SZ_PINF) == 0 || StrCmp(szVal, SZ_INF) == 0 ) {
- vp = VpAllocReal(1);
- vp->MaxPrec = 1; /* set max precision */
- VpSetPosInf(vp);
- return vp;
- }
- if (StrCmp(szVal, SZ_NINF) == 0) {
- vp = VpAllocReal(1);
- vp->MaxPrec = 1; /* set max precision */
- VpSetNegInf(vp);
- return vp;
- }
- if (StrCmp(szVal, SZ_NaN) == 0) {
- vp = VpAllocReal(1);
- vp->MaxPrec = 1; /* set max precision */
- VpSetNaN(vp);
- return vp;
- }
+ /* Scanning: fractional part */
+ nf = 0; /* number of digits in the fractional part */
+ ne = 0; /* number of digits in the exponential part */
+ ipf = 0; /* index of the beginning of the fractional part */
+ ipe = 0; /* index of the beginning of the exponential part */
+ dot_seen = 0;
+ exp_seen = 0;
- /* check on number szVal[] */
- ipn = i = 0;
- if (szVal[i] == '-') { sign=-1; ++i; }
- else if (szVal[i] == '+') ++i;
- /* Skip digits */
- ni = 0; /* digits in mantissa */
- while ((v = szVal[i]) != 0) {
- if (!ISDIGIT(v)) break;
- ++i;
- ++ni;
- }
- nf = 0;
- ipf = 0;
- ipe = 0;
- ne = 0;
- if (v) {
- /* other than digit nor \0 */
- if (szVal[i] == '.') { /* xxx. */
+ if (v != '\0') {
+ /* Scanning fractional part */
+ if ((psz[i] = szVal[j]) == '.') {
+ dot_seen = 1;
++i;
+ ++j;
ipf = i;
- while ((v = szVal[i]) != 0) { /* get fraction part. */
+ while ((v = psz[i] = szVal[j]) != '\0') {
+ if (!strict_p && ISSPACE(v)) {
+ v = psz[i] = '\0';
+ break;
+ }
+ if (v == '_') {
+ if (nf > 0 && ISDIGIT(szVal[j+1])) {
+ ++j;
+ continue;
+ }
+ if (!strict_p) {
+ v = psz[i] = '\0';
+ if (nf == 0) {
+ dot_seen = 0;
+ }
+ break;
+ }
+ goto invalid_value;
+ }
if (!ISDIGIT(v)) break;
++i;
+ ++j;
++nf;
}
}
- ipe = 0; /* Exponent */
- switch (szVal[i]) {
- case '\0':
- break;
- case 'e': case 'E':
- case 'd': case 'D':
- ++i;
- ipe = i;
- v = szVal[i];
- if ((v == '-') || (v == '+')) ++i;
- while ((v=szVal[i]) != 0) {
- if (!ISDIGIT(v)) break;
+ /* Scanning exponential part */
+ if (v != '\0') {
+ switch ((psz[i] = szVal[j])) {
+ case '\0':
+ break;
+ case 'e': case 'E':
+ case 'd': case 'D':
+ exp_seen = 1;
++i;
- ++ne;
- }
- break;
- default:
- break;
+ ++j;
+ ipe = i;
+ v = psz[i] = szVal[j];
+ if ((v == '-') || (v == '+')) {
+ ++i;
+ ++j;
+ }
+ while ((v = psz[i] = szVal[j]) != '\0') {
+ if (!strict_p && ISSPACE(v)) {
+ v = psz[i] = '\0';
+ break;
+ }
+ if (v == '_') {
+ if (ne > 0 && ISDIGIT(szVal[j+1])) {
+ ++j;
+ continue;
+ }
+ if (!strict_p) {
+ v = psz[i] = '\0';
+ if (ne == 0) {
+ exp_seen = 0;
+ }
+ break;
+ }
+ goto invalid_value;
+ }
+ if (!ISDIGIT(v)) break;
+ ++i;
+ ++j;
+ ++ne;
+ }
+ break;
+ default:
+ break;
+ }
}
+
+ if (v != '\0') {
+ /* Scanning trailing spaces */
+ while (ISSPACE(szVal[j])) ++j;
+
+ /* Invalid character */
+ if (szVal[j] && strict_p) {
+ goto invalid_value;
+ }
+ }
+ }
+
+ psz[i] = '\0';
+
+ if (((ni == 0 || dot_seen) && nf == 0) || (exp_seen && ne == 0)) {
+ VALUE str;
+ invalid_value:
+ if (!strict_p) {
+ goto return_zero;
+ }
+ if (!exc) {
+ return NULL;
+ }
+ str = rb_str_new2(orig_szVal);
+ rb_raise(rb_eArgError, "invalid value for BigDecimal(): \"%"PRIsVALUE"\"", str);
}
+
nalloc = (ni + nf + BASE_FIG - 1) / BASE_FIG + 1; /* set effective allocation */
/* units for szVal[] */
if (mx == 0) mx = 1;
@@ -4030,7 +4303,7 @@ VpAlloc(size_t mx, const char *szVal)
/* xmalloc() alway returns(or throw interruption) */
vp->MaxPrec = mx; /* set max precision */
VpSetZero(vp, sign);
- VpCtoV(vp, &szVal[ipn], ni, &szVal[ipf], nf, &szVal[ipe], ne);
+ VpCtoV(vp, psz, ni, psz + ipf, nf, psz + ipe, ne);
rb_str_resize(buf, 0);
return vp;
}
@@ -4107,7 +4380,7 @@ VpAddSub(Real *c, Real *a, Real *b, int operation)
}
#endif /* BIGDECIMAL_DEBUG */
- if (!VpIsDefOP(c, a, b, (operation > 0) ? 1 : 2)) return 0; /* No significant digits */
+ if (!VpIsDefOP(c, a, b, (operation > 0) ? OP_SW_ADD : OP_SW_SUB)) return 0; /* No significant digits */
/* check if a or b is zero */
if (VpIsZero(a)) {
@@ -4563,7 +4836,7 @@ VpMult(Real *c, Real *a, Real *b)
}
#endif /* BIGDECIMAL_DEBUG */
- if (!VpIsDefOP(c, a, b, 3)) return 0; /* No significant digit */
+ if (!VpIsDefOP(c, a, b, OP_SW_MULT)) return 0; /* No significant digit */
if (VpIsZero(a) || VpIsZero(b)) {
/* at least a or b is zero */
@@ -4593,7 +4866,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");
+ c = VpAlloc((size_t)((MxIndAB + 1) * BASE_FIG), "#0", 1, 1);
MxIndC = MxIndAB;
}
@@ -4675,7 +4948,7 @@ Exit:
/*
* c = a / b, remainder = r
*/
- VP_EXPORT size_t
+VP_EXPORT size_t
VpDivd(Real *c, Real *r, Real *a, Real *b)
{
size_t word_a, word_b, word_c, word_r;
@@ -4693,14 +4966,14 @@ VpDivd(Real *c, Real *r, Real *a, Real *b)
#endif /*BIGDECIMAL_DEBUG */
VpSetNaN(r);
- if (!VpIsDefOP(c, a, b, 4)) goto Exit;
+ if (!VpIsDefOP(c, a, b, OP_SW_DIV)) goto Exit;
if (VpIsZero(a) && VpIsZero(b)) {
VpSetNaN(c);
- return VpException(VP_EXCEPTION_NaN, "(VpDivd) 0/0 not defined(NaN)", 0);
+ return VpException(VP_EXCEPTION_NaN, "Computation results to 'NaN'", 0);
}
if (VpIsZero(b)) {
VpSetInf(c, VpGetSign(a) * VpGetSign(b));
- return VpException(VP_EXCEPTION_ZERODIVIDE, "(VpDivd) Divide by zero", 0);
+ return VpException(VP_EXCEPTION_ZERODIVIDE, "Divide by zero", 0);
}
if (VpIsZero(a)) {
/* numerator a is zero */
@@ -5053,7 +5326,7 @@ VPrint(FILE *fp, const char *cntl_chr, Real *a)
nc += 9;
}
else if (!VpIsZero(a)) {
- if (VpGetSign(a) < 0) {
+ if (BIGDECIMAL_NEGATIVE_P(a)) {
fprintf(fp, "-");
++nc;
}
@@ -5142,7 +5415,7 @@ VpFormatSt(char *psz, size_t fFmt)
if (!ch) break;
if (ISSPACE(ch) || ch=='-' || ch=='+') continue;
if (ch == '.') { nf = 0; continue; }
- if (ch == 'E') break;
+ if (ch == 'E' || ch == 'e') break;
if (++nf > fFmt) {
memmove(psz + i + 1, psz + i, ie - i + 1);
@@ -5191,7 +5464,7 @@ VpSzMantissa(Real *a,char *psz)
ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
if (!VpIsZero(a)) {
- if (VpGetSign(a) < 0) *psz++ = '-';
+ if (BIGDECIMAL_NEGATIVE_P(a)) *psz++ = '-';
n = a->Prec;
for (i = 0; i < n; ++i) {
m = BASE1;
@@ -5219,7 +5492,7 @@ VpSzMantissa(Real *a,char *psz)
VP_EXPORT int
VpToSpecialString(Real *a,char *psz,int fPlus)
- /* fPlus =0:default, =1: set ' ' before digits , =2: set '+' before digits. */
+/* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
{
if (VpIsNaN(a)) {
sprintf(psz,SZ_NaN);
@@ -5254,7 +5527,7 @@ VpToSpecialString(Real *a,char *psz,int fPlus)
VP_EXPORT void
VpToString(Real *a, char *psz, size_t fFmt, int fPlus)
-/* fPlus =0:default, =1: set ' ' before digits , =2:set '+' before digits. */
+/* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
{
size_t i, n, ZeroSup;
BDIGIT shift, m, e, nn;
@@ -5265,7 +5538,7 @@ VpToString(Real *a, char *psz, size_t fFmt, int fPlus)
ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
- if (VpGetSign(a) < 0) *psz++ = '-';
+ if (BIGDECIMAL_NEGATIVE_P(a)) *psz++ = '-';
else if (fPlus == 1) *psz++ = ' ';
else if (fPlus == 2) *psz++ = '+';
@@ -5296,13 +5569,13 @@ VpToString(Real *a, char *psz, size_t fFmt, int fPlus)
while (psz[-1] == '0') {
*(--psz) = 0;
}
- sprintf(psz, "E%"PRIdSIZE, ex);
+ sprintf(psz, "e%"PRIdSIZE, ex);
if (fFmt) VpFormatSt(pszSav, fFmt);
}
VP_EXPORT void
VpToFString(Real *a, char *psz, size_t fFmt, int fPlus)
-/* fPlus =0:default,=1: set ' ' before digits ,set '+' before digits. */
+/* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
{
size_t i, n;
BDIGIT m, e, nn;
@@ -5311,7 +5584,7 @@ VpToFString(Real *a, char *psz, size_t fFmt, int fPlus)
if (VpToSpecialString(a, psz, fPlus)) return;
- if (VpGetSign(a) < 0) *psz++ = '-';
+ if (BIGDECIMAL_NEGATIVE_P(a)) *psz++ = '-';
else if (fPlus == 1) *psz++ = ' ';
else if (fPlus == 2) *psz++ = '+';
@@ -5733,21 +6006,22 @@ VpSqrt(Real *y, Real *x)
ssize_t nr;
double val;
- /* Zero, NaN or Infinity ? */
- if (!VpHasVal(x)) {
- if (VpIsZero(x) || VpGetSign(x) > 0) {
- VpAsgn(y,x,1);
- goto Exit;
- }
- VpSetNaN(y);
- return VpException(VP_EXCEPTION_OP, "(VpSqrt) SQRT(NaN or negative value)", 0);
+ /* Zero or +Infinity ? */
+ if (VpIsZero(x) || VpIsPosInf(x)) {
+ VpAsgn(y,x,1);
goto Exit;
}
/* Negative ? */
- if (VpGetSign(x) < 0) {
+ if (BIGDECIMAL_NEGATIVE_P(x)) {
+ VpSetNaN(y);
+ return VpException(VP_EXCEPTION_OP, "sqrt of negative value", 0);
+ }
+
+ /* NaN ? */
+ if (VpIsNaN(x)) {
VpSetNaN(y);
- return VpException(VP_EXCEPTION_OP, "(VpSqrt) SQRT(negative value)", 0);
+ return VpException(VP_EXCEPTION_OP, "sqrt of 'NaN'(Not a Number)", 0);
}
/* One ? */
@@ -5760,8 +6034,8 @@ 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");
- r = VpAlloc((n + n) * (BASE_FIG + 2), "#1");
+ f = VpAlloc(y->MaxPrec * (BASE_FIG + 2), "#1", 1, 1);
+ r = VpAlloc((n + n) * (BASE_FIG + 2), "#1", 1, 1);
nr = 0;
y_prec = y->MaxPrec;
@@ -5826,17 +6100,12 @@ Exit:
}
/*
- *
- * nf: digit position for operation.
- *
- */
-VP_EXPORT int
-VpMidRound(Real *y, unsigned short f, ssize_t nf)
-/*
* Round relatively from the decimal point.
* f: rounding mode
* nf: digit location to round from the decimal point.
*/
+VP_EXPORT int
+VpMidRound(Real *y, unsigned short f, ssize_t nf)
{
/* fracf: any positive digit under rounding position? */
/* fracf_1further: any positive digits under one further than the rounding position? */
@@ -5938,10 +6207,10 @@ VpMidRound(Real *y, unsigned short f, ssize_t nf)
if (v > 5 || (v == 5 && fracf_1further)) ++div;
break;
case VP_ROUND_CEIL:
- if (fracf && (VpGetSign(y) > 0)) ++div;
+ if (fracf && BIGDECIMAL_POSITIVE_P(y)) ++div;
break;
case VP_ROUND_FLOOR:
- if (fracf && (VpGetSign(y) < 0)) ++div;
+ if (fracf && BIGDECIMAL_NEGATIVE_P(y)) ++div;
break;
case VP_ROUND_HALF_EVEN: /* Banker's rounding */
if (v > 5) ++div;
@@ -6060,10 +6329,10 @@ VpInternalRound(Real *c, size_t ixDigit, BDIGIT vPrev, BDIGIT v)
if (v >= 6) f = 1;
break;
case VP_ROUND_CEIL:
- if (v && (VpGetSign(c) > 0)) f = 1;
+ if (v && BIGDECIMAL_POSITIVE_P(c)) f = 1;
break;
case VP_ROUND_FLOOR:
- if (v && (VpGetSign(c) < 0)) f = 1;
+ if (v && BIGDECIMAL_NEGATIVE_P(c)) f = 1;
break;
case VP_ROUND_HALF_EVEN: /* Banker's rounding */
/* as per VP_ROUND_HALF_DOWN, because this is the last digit of precision,
@@ -6200,7 +6469,7 @@ VpPower(Real *y, Real *x, SIGNED_VALUE n)
if (x->exponent == 1 && x->Prec == 1 && x->frac[0] == 1) {
/* abs(x) = 1 */
VpSetOne(y);
- if (VpGetSign(x) > 0) goto Exit;
+ if (BIGDECIMAL_POSITIVE_P(x)) goto Exit;
if ((n % 2) == 0) goto Exit;
VpSetSign(y, -1);
goto Exit;
@@ -6218,8 +6487,8 @@ VpPower(Real *y, Real *x, SIGNED_VALUE n)
/* Allocate working variables */
- w1 = VpAlloc((y->MaxPrec + 2) * BASE_FIG, "#0");
- w2 = VpAlloc((w1->MaxPrec * 2 + 1) * BASE_FIG, "#0");
+ 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);
diff --git a/ext/bigdecimal/bigdecimal.def b/ext/bigdecimal/bigdecimal.def
new file mode 100644
index 0000000000..615bf72e20
--- /dev/null
+++ b/ext/bigdecimal/bigdecimal.def
@@ -0,0 +1,3 @@
+EXPORTS
+rmpd_util_str_to_d
+Init_bigdecimal
diff --git a/ext/bigdecimal/bigdecimal.gemspec b/ext/bigdecimal/bigdecimal.gemspec
index e7acfe2d11..68d8f3c00f 100644
--- a/ext/bigdecimal/bigdecimal.gemspec
+++ b/ext/bigdecimal/bigdecimal.gemspec
@@ -1,23 +1,30 @@
-# -*- ruby -*-
-_VERSION = "1.2.8"
-date = %w$Date:: $[1]
+# coding: utf-8
+
+bigdecimal_version = '1.4.1'
Gem::Specification.new do |s|
- s.name = "bigdecimal"
- s.version = _VERSION
- s.date = date
- s.license = 'ruby'
- s.summary = "Arbitrary-precision decimal floating-point number library."
- s.homepage = "https://www.ruby-lang.org"
- s.email = "mrkn@mrkn.jp"
- s.description = "This library provides arbitrary-precision decimal floating-point number class."
- s.authors = ["Kenta Murata", "Zachary Scott", "Shigeo Kobayashi"]
- s.require_path = %[lib]
- s.files = %w[
+ s.name = "bigdecimal"
+ s.version = bigdecimal_version
+ 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.require_paths = %w[lib]
+ s.extensions = %w[ext/bigdecimal/extconf.rb ext/bigdecimal/util/extconf.rb]
+ s.files = %w[
bigdecimal.gemspec
- bigdecimal.c
- bigdecimal.h
- depend extconf.rb
+ ext/bigdecimal/bigdecimal.c
+ ext/bigdecimal/bigdecimal.def
+ ext/bigdecimal/bigdecimal.h
+ ext/bigdecimal/depend
+ ext/bigdecimal/extconf.rb
+ ext/bigdecimal/util/extconf.rb
+ ext/bigdecimal/util/util.c
+ lib/bigdecimal.rb
lib/bigdecimal/jacobian.rb
lib/bigdecimal/ludcmp.rb
lib/bigdecimal/math.rb
@@ -27,5 +34,12 @@ Gem::Specification.new do |s|
sample/nlsolve.rb
sample/pi.rb
]
- s.extensions = %w[extconf.rb]
+
+ s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze)
+
+ s.add_development_dependency "rake", "~> 10.0"
+ s.add_development_dependency "rake-compiler", ">= 0.9"
+ s.add_development_dependency "rake-compiler-dock", ">= 0.6.1"
+ s.add_development_dependency "minitest", "< 5.0.0"
+ s.add_development_dependency "pry"
end
diff --git a/ext/bigdecimal/bigdecimal.h b/ext/bigdecimal/bigdecimal.h
index f85c3e55ad..e3eae06e67 100644
--- a/ext/bigdecimal/bigdecimal.h
+++ b/ext/bigdecimal/bigdecimal.h
@@ -9,6 +9,8 @@
#ifndef RUBY_BIG_DECIMAL_H
#define RUBY_BIG_DECIMAL_H 1
+#define RUBY_NO_OLD_COMPATIBILITY
+
#include "ruby/ruby.h"
#include <float.h>
@@ -90,6 +92,62 @@ llabs(LONG_LONG const x)
}
#endif
+#ifndef HAVE_FINITE
+static int
+finite(double)
+{
+ return !isnan(n) && !isinf(n);
+}
+#endif
+
+#ifndef isfinite
+# ifndef HAVE_ISFINITE
+# define HAVE_ISFINITE 1
+# define isfinite(x) finite(x)
+# endif
+#endif
+
+#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
+
+#ifndef HAVE_RB_SYM2STR
+static inline VALUE
+rb_sym2str(VALUE sym)
+{
+ return rb_id2str(SYM2ID(sym));
+}
+#endif
+
+#ifndef ST2FIX
+# undef RB_ST2FIX
+# define RB_ST2FIX(h) LONG2FIX((long)(h))
+# define ST2FIX(h) RB_ST2FIX(h)
+#endif
+
#ifdef vabs
# undef vabs
#endif
@@ -169,7 +227,9 @@ extern VALUE rb_cBigDecimal;
#define VP_SIGN_POSITIVE_INFINITE 3 /* Positive infinite number */
#define VP_SIGN_NEGATIVE_INFINITE -3 /* Negative infinite number */
-#ifdef __GNUC__
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+#define FLEXIBLE_ARRAY_SIZE /* */
+#elif defined(__GNUC__) && !defined(__STRICT_ANSI__)
#define FLEXIBLE_ARRAY_SIZE 0
#else
#define FLEXIBLE_ARRAY_SIZE 1
@@ -182,11 +242,11 @@ extern VALUE rb_cBigDecimal;
typedef struct {
VALUE obj; /* Back pointer(VALUE) for Ruby object. */
size_t MaxPrec; /* Maximum precision size */
- /* This is the actual size of pfrac[] */
+ /* This is the actual size of frac[] */
/*(frac[0] to frac[MaxPrec] are available). */
size_t Prec; /* Current precision size. */
- /* This indicates how much the. */
- /* the array frac[] is actually used. */
+ /* This indicates how much the */
+ /* array frac[] is actually used. */
SIGNED_VALUE exponent; /* Exponent part. */
short sign; /* Attributes of the value. */
/*
@@ -248,7 +308,7 @@ VP_EXPORT size_t VpInit(BDIGIT 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);
+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);
VP_EXPORT size_t VpMult(Real *c,Real *a,Real *b);
diff --git a/ext/bigdecimal/depend b/ext/bigdecimal/depend
index 3ab229f2b6..943bd6c38c 100644
--- a/ext/bigdecimal/depend
+++ b/ext/bigdecimal/depend
@@ -1,7 +1,9 @@
+extconf.h: $(srcdir)/$(GEMSPEC)
+Makefile: $(BIGDECIMAL_RB)
+
# AUTOGENERATED DEPENDENCIES START
bigdecimal.o: $(RUBY_EXTCONF_H)
bigdecimal.o: $(arch_hdrdir)/ruby/config.h
-bigdecimal.o: $(hdrdir)/ruby/backward.h
bigdecimal.o: $(hdrdir)/ruby/defines.h
bigdecimal.o: $(hdrdir)/ruby/intern.h
bigdecimal.o: $(hdrdir)/ruby/missing.h
diff --git a/ext/bigdecimal/extconf.rb b/ext/bigdecimal/extconf.rb
index 3799e685bc..a6a36304cc 100644
--- a/ext/bigdecimal/extconf.rb
+++ b/ext/bigdecimal/extconf.rb
@@ -1,11 +1,40 @@
# frozen_string_literal: false
require 'mkmf'
+gemspec_name = gemspec_path = nil
+unless ['', '../../'].any? {|dir|
+ gemspec_name = "#{dir}bigdecimal.gemspec"
+ gemspec_path = File.expand_path("../#{gemspec_name}", __FILE__)
+ File.file?(gemspec_path)
+ }
+ $stderr.puts "Unable to find bigdecimal.gemspec"
+ abort
+end
+
+bigdecimal_version =
+ IO.readlines(gemspec_path)
+ .grep(/\Abigdecimal_version\s+=\s+/)[0][/\'([\d\.]+)\'/, 1]
+
+$defs << %Q[-DRUBY_BIGDECIMAL_VERSION=\\"#{bigdecimal_version}\\"]
+
have_func("labs", "stdlib.h")
have_func("llabs", "stdlib.h")
+have_func("finite", "math.h")
+have_func("isfinite", "math.h")
have_type("struct RRational", "ruby.h")
have_func("rb_rational_num", "ruby.h")
have_func("rb_rational_den", "ruby.h")
+have_func("rb_array_const_ptr", "ruby.h")
+have_func("rb_sym2str", "ruby.h")
+
+if File.file?(File.expand_path('../lib/bigdecimal.rb', __FILE__))
+ bigdecimal_rb = "$(srcdir)/lib/bigdecimal.rb"
+else
+ bigdecimal_rb = "$(srcdir)/../../lib/bigdecimal.rb"
+end
-create_makefile('bigdecimal')
+create_makefile('bigdecimal') {|mf|
+ mf << "GEMSPEC = #{gemspec_name}\n"
+ mf << "BIGDECIMAL_RB = #{bigdecimal_rb}\n"
+}
diff --git a/ext/bigdecimal/lib/bigdecimal.rb b/ext/bigdecimal/lib/bigdecimal.rb
new file mode 100644
index 0000000000..96995a32b3
--- /dev/null
+++ b/ext/bigdecimal/lib/bigdecimal.rb
@@ -0,0 +1,6 @@
+require 'bigdecimal.so'
+
+def BigDecimal.new(*args, **kwargs)
+ warn "BigDecimal.new is deprecated; use BigDecimal() method instead.", uplevel: 1
+ BigDecimal(*args, **kwargs)
+end
diff --git a/ext/bigdecimal/lib/bigdecimal/jacobian.rb b/ext/bigdecimal/lib/bigdecimal/jacobian.rb
index 9cad06c09b..84c50248b7 100644
--- a/ext/bigdecimal/lib/bigdecimal/jacobian.rb
+++ b/ext/bigdecimal/lib/bigdecimal/jacobian.rb
@@ -21,6 +21,9 @@
#
# fx is f.values(x).
#
+
+require 'bigdecimal'
+
module Jacobian
module_function
diff --git a/ext/bigdecimal/lib/bigdecimal/math.rb b/ext/bigdecimal/lib/bigdecimal/math.rb
index 3ddde6a9a0..0b9d0648bb 100644
--- a/ext/bigdecimal/lib/bigdecimal/math.rb
+++ b/ext/bigdecimal/lib/bigdecimal/math.rb
@@ -26,7 +26,7 @@ require 'bigdecimal'
# include BigMath
#
# a = BigDecimal((PI(100)/2).to_s)
-# puts sin(a,100) # => 0.10000000000000000000......E1
+# puts sin(a,100) # => 0.99999999999999999999......e0
#
module BigMath
module_function
@@ -37,8 +37,8 @@ module BigMath
# Computes the square root of +decimal+ to the specified number of digits of
# precision, +numeric+.
#
- # BigMath.sqrt(BigDecimal.new('2'), 16).to_s
- # #=> "0.1414213562373095048801688724E1"
+ # BigMath.sqrt(BigDecimal('2'), 16).to_s
+ # #=> "0.1414213562373095048801688724e1"
#
def sqrt(x, prec)
x.sqrt(prec)
@@ -53,7 +53,7 @@ module BigMath
# If +decimal+ is Infinity or NaN, returns NaN.
#
# BigMath.sin(BigMath.PI(5)/4, 5).to_s
- # #=> "0.70710678118654752440082036563292800375E0"
+ # #=> "0.70710678118654752440082036563292800375e0"
#
def sin(x, prec)
raise ArgumentError, "Zero or negative precision for sin" if prec <= 0
@@ -97,7 +97,7 @@ module BigMath
# If +decimal+ is Infinity or NaN, returns NaN.
#
# BigMath.cos(BigMath.PI(4), 16).to_s
- # #=> "-0.999999999999999999999999999999856613163740061349E0"
+ # #=> "-0.999999999999999999999999999999856613163740061349e0"
#
def cos(x, prec)
raise ArgumentError, "Zero or negative precision for cos" if prec <= 0
@@ -140,8 +140,8 @@ module BigMath
#
# If +decimal+ is NaN, returns NaN.
#
- # BigMath.atan(BigDecimal.new('-1'), 16).to_s
- # #=> "-0.785398163397448309615660845819878471907514682065E0"
+ # BigMath.atan(BigDecimal('-1'), 16).to_s
+ # #=> "-0.785398163397448309615660845819878471907514682065e0"
#
def atan(x, prec)
raise ArgumentError, "Zero or negative precision for atan" if prec <= 0
@@ -178,7 +178,7 @@ module BigMath
# +numeric+.
#
# BigMath.PI(10).to_s
- # #=> "0.3141592653589793238462643388813853786957412E1"
+ # #=> "0.3141592653589793238462643388813853786957412e1"
#
def PI(prec)
raise ArgumentError, "Zero or negative precision for PI" if prec <= 0
@@ -223,7 +223,7 @@ module BigMath
# digits of precision, +numeric+.
#
# BigMath.E(10).to_s
- # #=> "0.271828182845904523536028752390026306410273E1"
+ # #=> "0.271828182845904523536028752390026306410273e1"
#
def E(prec)
raise ArgumentError, "Zero or negative precision for E" if prec <= 0
diff --git a/ext/bigdecimal/lib/bigdecimal/util.rb b/ext/bigdecimal/lib/bigdecimal/util.rb
index 0c4e486c00..88f490cb45 100644
--- a/ext/bigdecimal/lib/bigdecimal/util.rb
+++ b/ext/bigdecimal/lib/bigdecimal/util.rb
@@ -1,72 +1,74 @@
# frozen_string_literal: false
-# BigDecimal extends the native Integer class to provide the #to_d method.
#
-# When you require the BigDecimal library in your application, this methodwill
-# be available on Integer objects.
+#--
+# bigdecimal/util extends various native classes to provide the #to_d method,
+# and provides BigDecimal#to_d and BigDecimal#to_digits.
+#++
+
+require 'bigdecimal'
+require 'bigdecimal/util.so'
+
class Integer < Numeric
# call-seq:
# int.to_d -> bigdecimal
#
- # Convert +int+ to a BigDecimal and return it.
+ # Returns the value of +int+ as a BigDecimal.
#
# require 'bigdecimal'
# require 'bigdecimal/util'
#
- # 42.to_d
- # # => #<BigDecimal:1008ef070,'0.42E2',9(36)>
+ # 42.to_d # => 0.42e2
+ #
+ # See also BigDecimal::new.
#
def to_d
BigDecimal(self)
end
end
-# BigDecimal extends the native Float class to provide the #to_d method.
-#
-# When you require BigDecimal in your application, this method will be
-# available on Float objects.
+
class Float < Numeric
# call-seq:
- # flt.to_d -> bigdecimal
+ # float.to_d -> bigdecimal
+ # float.to_d(precision) -> bigdecimal
#
- # Convert +flt+ to a BigDecimal and return it.
+ # 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).
#
# require 'bigdecimal'
# require 'bigdecimal/util'
#
- # 0.5.to_d
- # # => #<BigDecimal:1dc69e0,'0.5E0',9(18)>
+ # 0.5.to_d # => 0.5e0
+ # 1.234.to_d(2) # => 0.12e1
#
- def to_d(precision=nil)
- BigDecimal(self, precision || Float::DIG)
+ # See also BigDecimal::new.
+ #
+ def to_d(precision=Float::DIG)
+ BigDecimal(self, precision)
end
end
-# BigDecimal extends the native String class to provide the #to_d method.
-#
-# When you require BigDecimal in your application, this method will be
-# available on String objects.
+
class String
# call-seq:
- # string.to_d -> bigdecimal
+ # str.to_d -> bigdecimal
#
- # Convert +string+ to a BigDecimal and return it.
+ # Returns the result of interpreting leading characters in +str+
+ # as a BigDecimal.
#
# require 'bigdecimal'
# require 'bigdecimal/util'
#
- # "0.5".to_d
- # # => #<BigDecimal:1dc69e0,'0.5E0',9(18)>
+ # "0.5".to_d # => 0.5e0
+ # "123.45e1".to_d # => 0.12345e4
+ # "45.67 degrees".to_d # => 0.4567e2
+ #
+ # See also BigDecimal::new.
#
- def to_d
- BigDecimal(self)
- end
end
-# BigDecimal extends the native Numeric class to provide the #to_digits and
-# #to_d methods.
-#
-# When you require BigDecimal in your application, this method will be
-# available on BigDecimal objects.
+
class BigDecimal < Numeric
# call-seq:
# a.to_digits -> string
@@ -74,12 +76,11 @@ class BigDecimal < Numeric
# Converts a BigDecimal to a String of the form "nnnnnn.mmm".
# This method is deprecated; use BigDecimal#to_s("F") instead.
#
- # require 'bigdecimal'
# require 'bigdecimal/util'
#
- # d = BigDecimal.new("3.14")
- # d.to_digits
- # # => "3.14"
+ # d = BigDecimal("3.14")
+ # d.to_digits # => "3.14"
+ #
def to_digits
if self.nan? || self.infinite? || self.zero?
self.to_s
@@ -94,35 +95,52 @@ class BigDecimal < Numeric
# a.to_d -> bigdecimal
#
# Returns self.
+ #
+ # require 'bigdecimal/util'
+ #
+ # d = BigDecimal("3.14")
+ # d.to_d # => 0.314e1
+ #
def to_d
self
end
end
-# BigDecimal extends the native Rational class to provide the #to_d method.
-#
-# When you require BigDecimal in your application, this method will be
-# available on Rational objects.
+
class Rational < Numeric
# call-seq:
- # r.to_d(precision) -> bigdecimal
+ # rat.to_d(precision) -> bigdecimal
+ #
+ # Returns the value as a BigDecimal.
+ #
+ # The required +precision+ parameter is used to determine the number of
+ # significant digits for the result.
+ #
+ # require 'bigdecimal'
+ # require 'bigdecimal/util'
#
- # Converts a Rational to a BigDecimal.
+ # Rational(22, 7).to_d(3) # => 0.314e1
#
- # The required +precision+ parameter is used to determine the amount of
- # significant digits for the result. See BigDecimal#div for more information,
- # as it is used along with the #denominator and the +precision+ for
- # parameters.
+ # See also BigDecimal::new.
#
- # r = (22/7.0).to_r
- # # => (7077085128725065/2251799813685248)
- # r.to_d(3)
- # # => #<BigDecimal:1a44d08,'0.314E1',18(36)>
def to_d(precision)
- if precision <= 0
- raise ArgumentError, "negative precision"
- end
- num = self.numerator
- BigDecimal(num).div(self.denominator, precision)
+ BigDecimal(self, precision)
+ end
+end
+
+
+class NilClass
+ # call-seq:
+ # nil.to_d -> bigdecimal
+ #
+ # Returns nil represented as a BigDecimal.
+ #
+ # require 'bigdecimal'
+ # require 'bigdecimal/util'
+ #
+ # nil.to_d # => 0.0
+ #
+ def to_d
+ BigDecimal(0)
end
end
diff --git a/ext/bigdecimal/sample/linear.rb b/ext/bigdecimal/sample/linear.rb
index 3b23269f8a..516c2473be 100644
--- a/ext/bigdecimal/sample/linear.rb
+++ b/ext/bigdecimal/sample/linear.rb
@@ -28,8 +28,8 @@ def rd_order(na)
end
na = ARGV.size
-zero = BigDecimal.new("0.0")
-one = BigDecimal.new("1.0")
+zero = BigDecimal("0.0")
+one = BigDecimal("1.0")
while (n=rd_order(na))>0
a = []
@@ -37,27 +37,28 @@ while (n=rd_order(na))>0
b = []
if na <= 0
# Read data from console.
- printf("\nEnter coefficient matrix element A[i,j]\n");
+ printf("\nEnter coefficient matrix element A[i,j]\n")
for i in 0...n do
for j in 0...n do
printf("A[%d,%d]? ",i,j); s = ARGF.gets
- a << BigDecimal.new(s);
- as << BigDecimal.new(s);
+ a << BigDecimal(s)
+ as << BigDecimal(s)
end
- printf("Contatant vector element b[%d] ? ",i); b << BigDecimal.new(ARGF.gets);
+ printf("Contatant vector element b[%d] ? ",i)
+ b << BigDecimal(ARGF.gets)
end
else
# Read data from specified file.
- printf("Coefficient matrix and constant vector.\n");
+ printf("Coefficient matrix and constant vector.\n")
for i in 0...n do
s = ARGF.gets
printf("%d) %s",i,s)
s = s.split
for j in 0...n do
- a << BigDecimal.new(s[j]);
- as << BigDecimal.new(s[j]);
+ a << BigDecimal(s[j])
+ as << BigDecimal(s[j])
end
- b << BigDecimal.new(s[n]);
+ b << BigDecimal(s[n])
end
end
x = lusolve(a,b,ludecomp(a,n,zero,one),zero)
diff --git a/ext/bigdecimal/sample/nlsolve.rb b/ext/bigdecimal/sample/nlsolve.rb
index b1dd08e0a3..c2227dac73 100644
--- a/ext/bigdecimal/sample/nlsolve.rb
+++ b/ext/bigdecimal/sample/nlsolve.rb
@@ -12,11 +12,11 @@ include Newton
class Function # :nodoc: all
def initialize()
- @zero = BigDecimal.new("0.0")
- @one = BigDecimal.new("1.0")
- @two = BigDecimal.new("2.0")
- @ten = BigDecimal.new("10.0")
- @eps = BigDecimal.new("1.0e-16")
+ @zero = BigDecimal("0.0")
+ @one = BigDecimal("1.0")
+ @two = BigDecimal("2.0")
+ @ten = BigDecimal("10.0")
+ @eps = BigDecimal("1.0e-16")
end
def zero;@zero;end
def one ;@one ;end
diff --git a/ext/bigdecimal/util/extconf.rb b/ext/bigdecimal/util/extconf.rb
new file mode 100644
index 0000000000..8750db1c52
--- /dev/null
+++ b/ext/bigdecimal/util/extconf.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: false
+require 'mkmf'
+
+checking_for(checking_message("Windows")) do
+ case RUBY_PLATFORM
+ when /cygwin|mingw/
+ if defined?($extlist)
+ build_dir = "$(TARGET_SO_DIR)../"
+ else
+ base_dir = File.expand_path('../../../..', __FILE__)
+ build_dir = File.join(base_dir, "tmp", RUBY_PLATFORM, "bigdecimal", RUBY_VERSION, "")
+ end
+ $libs << " #{build_dir}bigdecimal.so"
+ true
+ when /mswin/
+ $DLDFLAGS << " -libpath:.."
+ $libs << " bigdecimal-$(arch).lib"
+ true
+ else
+ false
+ end
+end
+
+create_makefile('bigdecimal/util')
diff --git a/ext/bigdecimal/util/util.c b/ext/bigdecimal/util/util.c
new file mode 100644
index 0000000000..8d38d87852
--- /dev/null
+++ b/ext/bigdecimal/util/util.c
@@ -0,0 +1,9 @@
+#include "ruby.h"
+
+RUBY_EXTERN VALUE rmpd_util_str_to_d(VALUE str);
+
+void
+Init_util(void)
+{
+ rb_define_method(rb_cString, "to_d", rmpd_util_str_to_d, 0);
+}
diff --git a/ext/cgi/escape/depend b/ext/cgi/escape/depend
index c93a487a56..099bb3f14b 100644
--- a/ext/cgi/escape/depend
+++ b/ext/cgi/escape/depend
@@ -6,6 +6,7 @@ escape.o: $(hdrdir)/ruby/defines.h
escape.o: $(hdrdir)/ruby/encoding.h
escape.o: $(hdrdir)/ruby/intern.h
escape.o: $(hdrdir)/ruby/missing.h
+escape.o: $(hdrdir)/ruby/onigmo.h
escape.o: $(hdrdir)/ruby/oniguruma.h
escape.o: $(hdrdir)/ruby/ruby.h
escape.o: $(hdrdir)/ruby/st.h
diff --git a/ext/cgi/escape/escape.c b/ext/cgi/escape/escape.c
index 7289f43b8d..ced1b182eb 100644
--- a/ext/cgi/escape/escape.c
+++ b/ext/cgi/escape/escape.c
@@ -196,7 +196,7 @@ url_unreserved_char(unsigned char c)
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J':
case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':
case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
- case '-': case '.': case '_':
+ case '-': case '.': case '_': case '~':
return 1;
default:
break;
@@ -252,7 +252,8 @@ optimized_unescape(VALUE str, VALUE encoding)
long i, len, beg = 0;
VALUE dest = 0;
const char *cstr;
- int cr, origenc, encidx = rb_to_encoding_index(encoding);
+ rb_encoding *enc = rb_to_encoding(encoding);
+ int cr, origenc, encidx = rb_enc_to_index(enc);
len = RSTRING_LEN(str);
cstr = RSTRING_PTR(str);
diff --git a/ext/coverage/coverage.c b/ext/coverage/coverage.c
index 9a9011a7da..c7f5a5e34b 100644
--- a/ext/coverage/coverage.c
+++ b/ext/coverage/coverage.c
@@ -10,6 +10,10 @@
#include "ruby.h"
#include "vm_core.h"
+#include "gc.h"
+
+static int current_mode;
+static VALUE me2counter = Qnil;
/*
* call-seq:
@@ -18,25 +22,184 @@
* Enables coverage measurement.
*/
static VALUE
-rb_coverage_start(VALUE klass)
+rb_coverage_start(int argc, VALUE *argv, VALUE klass)
{
- VALUE coverages = rb_get_coverages();
+ VALUE coverages, opt;
+ int mode;
+
+ rb_scan_args(argc, argv, "01", &opt);
+
+ if (argc == 0) {
+ mode = 0; /* compatible mode */
+ }
+ else if (opt == ID2SYM(rb_intern("all"))) {
+ mode = COVERAGE_TARGET_LINES | COVERAGE_TARGET_BRANCHES | COVERAGE_TARGET_METHODS;
+ }
+ 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;
+ 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 (mode & COVERAGE_TARGET_METHODS) {
+ me2counter = rb_hash_new_compare_by_id();
+ }
+ else {
+ me2counter = Qnil;
+ }
+
+ coverages = rb_get_coverages();
if (!RTEST(coverages)) {
coverages = rb_hash_new();
rb_obj_hide(coverages);
- rb_set_coverages(coverages);
+ current_mode = mode;
+ if (mode == 0) mode = COVERAGE_TARGET_LINES;
+ rb_set_coverages(coverages, mode, me2counter);
+ }
+ else if (current_mode != mode) {
+ rb_raise(rb_eRuntimeError, "cannot change the measuring target during coverage measurement");
}
return Qnil;
}
+static VALUE
+branch_coverage(VALUE branches)
+{
+ VALUE ret = rb_hash_new();
+ VALUE structure = rb_ary_dup(RARRAY_AREF(branches, 0));
+ VALUE counters = rb_ary_dup(RARRAY_AREF(branches, 1));
+ int i, j;
+ long id = 0;
+
+ for (i = 0; i < RARRAY_LEN(structure); i++) {
+ VALUE branches = RARRAY_AREF(structure, i);
+ VALUE base_type = RARRAY_AREF(branches, 0);
+ VALUE base_first_lineno = RARRAY_AREF(branches, 1);
+ VALUE base_first_column = RARRAY_AREF(branches, 2);
+ VALUE base_last_lineno = RARRAY_AREF(branches, 3);
+ VALUE base_last_column = RARRAY_AREF(branches, 4);
+ VALUE children = rb_hash_new();
+ rb_hash_aset(ret, rb_ary_new_from_args(6, base_type, LONG2FIX(id++), base_first_lineno, base_first_column, base_last_lineno, base_last_column), children);
+ for (j = 5; j < RARRAY_LEN(branches); j += 6) {
+ VALUE target_label = RARRAY_AREF(branches, j);
+ VALUE target_first_lineno = RARRAY_AREF(branches, j + 1);
+ VALUE target_first_column = RARRAY_AREF(branches, j + 2);
+ VALUE target_last_lineno = RARRAY_AREF(branches, j + 3);
+ VALUE target_last_column = RARRAY_AREF(branches, j + 4);
+ int idx = FIX2INT(RARRAY_AREF(branches, j + 5));
+ rb_hash_aset(children, rb_ary_new_from_args(6, target_label, LONG2FIX(id++), target_first_lineno, target_first_column, target_last_lineno, target_last_column), RARRAY_AREF(counters, idx));
+ }
+ }
+
+ return ret;
+}
+
+static int
+method_coverage_i(void *vstart, void *vend, size_t stride, void *data)
+{
+ /*
+ * ObjectSpace.each_object(Module){|mod|
+ * mod.instance_methods.each{|mid|
+ * m = mod.instance_method(mid)
+ * if loc = m.source_location
+ * p [m.name, loc, $g_method_cov_counts[m]]
+ * end
+ * }
+ * }
+ */
+ VALUE ncoverages = *(VALUE*)data, v;
+
+ for (v = (VALUE)vstart; v != (VALUE)vend; v += stride) {
+ 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);
+ }
+ }
+ }
+ return 0;
+}
+
static int
coverage_peek_result_i(st_data_t key, st_data_t val, st_data_t h)
{
VALUE path = (VALUE)key;
VALUE coverage = (VALUE)val;
VALUE coverages = (VALUE)h;
- coverage = rb_ary_dup(coverage);
- rb_ary_freeze(coverage);
+ if (current_mode == 0) {
+ /* 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();
+
+ 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);
+ 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_METHODS) {
+ rb_hash_aset(h, ID2SYM(rb_intern("methods")), rb_hash_new());
+ }
+
+ coverage = h;
+ }
+
rb_hash_aset(coverages, path, coverage);
return ST_CONTINUE;
}
@@ -46,6 +209,12 @@ coverage_peek_result_i(st_data_t key, st_data_t val, st_data_t h)
* Coverage.peek_result => hash
*
* Returns a hash that contains filename as key and coverage array as value.
+ * This is the same as `Coverage.result(stop: false, clear: false)`.
+ *
+ * {
+ * "file.rb" => [1, 2, nil],
+ * ...
+ * }
*/
static VALUE
rb_coverage_peek_result(VALUE klass)
@@ -56,25 +225,77 @@ rb_coverage_peek_result(VALUE klass)
rb_raise(rb_eRuntimeError, "coverage measurement is not enabled");
}
st_foreach(RHASH_TBL(coverages), coverage_peek_result_i, ncoverages);
+
+ if (current_mode & COVERAGE_TARGET_METHODS) {
+ rb_objspace_each_objects(method_coverage_i, &ncoverages);
+ }
+
rb_hash_freeze(ncoverages);
return ncoverages;
}
+
+static int
+clear_me2counter_i(VALUE key, VALUE value, VALUE unused)
+{
+ rb_hash_aset(me2counter, key, INT2FIX(0));
+ return ST_CONTINUE;
+}
+
/*
* call-seq:
- * Coverage.result => hash
+ * Coverage.result(stop: true, clear: true) => hash
*
- * Returns a hash that contains filename as key and coverage array as value
- * and disables coverage measurement.
+ * Returns a hash that contains filename as key and coverage array as value.
+ * If +clear+ is true, it clears the counters to zero.
+ * If +stop+ is true, it disables coverage measurement.
*/
static VALUE
-rb_coverage_result(VALUE klass)
+rb_coverage_result(int argc, VALUE *argv, VALUE klass)
{
- VALUE ncoverages = rb_coverage_peek_result(klass);
- rb_reset_coverages();
+ VALUE ncoverages;
+ VALUE opt;
+ int stop = 1, clear = 1;
+
+ rb_scan_args(argc, argv, "01", &opt);
+
+ if (argc == 1) {
+ opt = rb_convert_type(opt, T_HASH, "Hash", "to_hash");
+ stop = RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("stop"))));
+ clear = RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("clear"))));
+ }
+
+ ncoverages = rb_coverage_peek_result(klass);
+ if (stop && !clear) {
+ rb_warn("stop implies clear");
+ clear = 1;
+ }
+ if (clear) {
+ rb_clear_coverages();
+ if (!NIL_P(me2counter)) rb_hash_foreach(me2counter, clear_me2counter_i, Qnil);
+ }
+ if (stop) {
+ rb_reset_coverages();
+ me2counter = Qnil;
+ }
return ncoverages;
}
+
+/*
+ * call-seq:
+ * Coverage.running? => bool
+ *
+ * Returns true if coverage stats are currently being collected (after
+ * Coverage.start call, but before Coverage.result call)
+ */
+static VALUE
+rb_coverage_running(VALUE klass)
+{
+ VALUE coverages = rb_get_coverages();
+ return RTEST(coverages) ? Qtrue : Qfalse;
+}
+
/* Coverage provides coverage measurement feature for Ruby.
* This feature is experimental, so these APIs may be changed in future.
*
@@ -112,7 +333,9 @@ void
Init_coverage(void)
{
VALUE rb_mCoverage = rb_define_module("Coverage");
- rb_define_module_function(rb_mCoverage, "start", rb_coverage_start, 0);
- rb_define_module_function(rb_mCoverage, "result", rb_coverage_result, 0);
+ rb_define_module_function(rb_mCoverage, "start", rb_coverage_start, -1);
+ rb_define_module_function(rb_mCoverage, "result", rb_coverage_result, -1);
rb_define_module_function(rb_mCoverage, "peek_result", rb_coverage_peek_result, 0);
+ rb_define_module_function(rb_mCoverage, "running?", rb_coverage_running, 0);
+ rb_global_variable(&me2counter);
}
diff --git a/ext/coverage/depend b/ext/coverage/depend
index 860893b06f..146f694cd5 100644
--- a/ext/coverage/depend
+++ b/ext/coverage/depend
@@ -19,6 +19,7 @@ coverage.o: $(hdrdir)/ruby/encoding.h
coverage.o: $(hdrdir)/ruby/intern.h
coverage.o: $(hdrdir)/ruby/io.h
coverage.o: $(hdrdir)/ruby/missing.h
+coverage.o: $(hdrdir)/ruby/onigmo.h
coverage.o: $(hdrdir)/ruby/oniguruma.h
coverage.o: $(hdrdir)/ruby/ruby.h
coverage.o: $(hdrdir)/ruby/st.h
@@ -28,6 +29,7 @@ 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)/gc.h
coverage.o: $(top_srcdir)/include/ruby.h
coverage.o: $(top_srcdir)/internal.h
coverage.o: $(top_srcdir)/method.h
diff --git a/ext/coverage/lib/coverage.rb b/ext/coverage/lib/coverage.rb
new file mode 100644
index 0000000000..f1923ef366
--- /dev/null
+++ b/ext/coverage/lib/coverage.rb
@@ -0,0 +1,14 @@
+require "coverage.so"
+
+module Coverage
+ def self.line_stub(file)
+ lines = File.foreach(file).map { nil }
+ iseqs = [RubyVM::InstructionSequence.compile_file(file)]
+ until iseqs.empty?
+ iseq = iseqs.pop
+ iseq.trace_points.each {|n, type| lines[n - 1] = 0 if type == :line }
+ iseq.each_child {|child| iseqs << child }
+ end
+ lines
+ end
+end
diff --git a/ext/date/date.gemspec b/ext/date/date.gemspec
new file mode 100644
index 0000000000..b30a055746
--- /dev/null
+++ b/ext/date/date.gemspec
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+version = File.foreach(File.expand_path("../lib/date.rb", __FILE__)).find do |line|
+ /^\s*VERSION\s*=\s*["'](.*)["']/ =~ line and break $1
+end
+
+Gem::Specification.new do |s|
+ s.name = "date"
+ s.version = version
+ s.summary = "A subclass of Object includes Comparable module for handling dates."
+ s.description = "A subclass of Object includes Comparable module for handling dates."
+
+ s.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"
+
+ s.authors = ["Tadayoshi Funaba"]
+ s.email = [nil]
+ s.homepage = "https://github.com/ruby/date"
+ s.license = "BSD-2-Clause"
+
+ s.add_development_dependency "rake-compiler"
+end
diff --git a/ext/date/date_core.c b/ext/date/date_core.c
index 12240d7457..1734ec0349 100644
--- a/ext/date/date_core.c
+++ b/ext/date/date_core.c
@@ -51,18 +51,21 @@ static double positive_inf, negative_inf;
#define f_add3(x,y,z) f_add(f_add(x, y), z)
#define f_sub3(x,y,z) f_sub(f_sub(x, y), z)
-inline static VALUE
+static VALUE date_initialize(int argc, VALUE *argv, VALUE self);
+static VALUE datetime_initialize(int argc, VALUE *argv, VALUE self);
+
+inline static int
f_cmp(VALUE x, VALUE y)
{
if (FIXNUM_P(x) && FIXNUM_P(y)) {
long c = FIX2LONG(x) - FIX2LONG(y);
if (c > 0)
- c = 1;
+ return 1;
else if (c < 0)
- c = -1;
- return INT2FIX(c);
+ return -1;
+ return 0;
}
- return rb_funcall(x, id_cmp, 1, y);
+ return rb_cmpint(rb_funcallv(x, id_cmp, 1, &y), x, y);
}
inline static VALUE
@@ -94,7 +97,7 @@ f_ge_p(VALUE x, VALUE y)
{
if (FIXNUM_P(x) && FIXNUM_P(y))
return f_boolcast(FIX2LONG(x) >= FIX2LONG(y));
- return rb_funcall(x, rb_intern(">="), 1, y);
+ return rb_funcall(x, id_ge_p, 1, y);
}
inline static VALUE
@@ -102,7 +105,7 @@ f_eqeq_p(VALUE x, VALUE y)
{
if (FIXNUM_P(x) && FIXNUM_P(y))
return f_boolcast(FIX2LONG(x) == FIX2LONG(y));
- return rb_funcall(x, rb_intern("=="), 1, y);
+ return rb_funcall(x, id_eqeq_p, 1, y);
}
inline static VALUE
@@ -236,11 +239,8 @@ f_negative_p(VALUE x)
struct SimpleDateData
{
unsigned flags;
- VALUE nth; /* not always canonicalized */
int jd; /* as utc */
- /* df is zero */
- /* sf is zero */
- /* of is zero */
+ VALUE nth; /* not always canonicalized */
date_sg_t sg; /* 2298874..2426355 or -/+oo -- at most 22 bits */
/* decoded as utc=local */
int year; /* truncated */
@@ -259,11 +259,8 @@ struct SimpleDateData
struct ComplexDateData
{
unsigned flags;
- VALUE nth; /* not always canonicalized */
int jd; /* as utc */
- int df; /* as utc, in secs */
- VALUE sf; /* in nano secs */
- int of; /* in secs */
+ VALUE nth; /* not always canonicalized */
date_sg_t sg; /* 2298874..2426355 or -/+oo -- at most 22 bits */
/* decoded as local */
int year; /* truncated */
@@ -277,6 +274,9 @@ struct ComplexDateData
/* packed civil */
unsigned pc;
#endif
+ int df; /* as utc, in secs */
+ int of; /* in secs */
+ VALUE sf; /* in nano secs */
};
union DateData {
@@ -315,31 +315,31 @@ canon(VALUE x)
#ifndef USE_PACK
#define set_to_simple(obj, x, _nth, _jd ,_sg, _year, _mon, _mday, _flags) \
-{\
+do {\
RB_OBJ_WRITE((obj), &(x)->nth, canon(_nth)); \
(x)->jd = _jd;\
(x)->sg = (date_sg_t)(_sg);\
(x)->year = _year;\
(x)->mon = _mon;\
(x)->mday = _mday;\
- (x)->flags = _flags;\
-}
+ (x)->flags = (_flags) & ~COMPLEX_DAT;\
+} while (0)
#else
#define set_to_simple(obj, x, _nth, _jd ,_sg, _year, _mon, _mday, _flags) \
-{\
+do {\
RB_OBJ_WRITE((obj), &(x)->nth, canon(_nth)); \
(x)->jd = _jd;\
(x)->sg = (date_sg_t)(_sg);\
(x)->year = _year;\
(x)->pc = PACK2(_mon, _mday);\
- (x)->flags = _flags;\
-}
+ (x)->flags = (_flags) & ~COMPLEX_DAT;\
+} while (0)
#endif
#ifndef USE_PACK
#define set_to_complex(obj, x, _nth, _jd ,_df, _sf, _of, _sg,\
_year, _mon, _mday, _hour, _min, _sec, _flags) \
-{\
+do {\
RB_OBJ_WRITE((obj), &(x)->nth, canon(_nth));\
(x)->jd = _jd;\
(x)->df = _df;\
@@ -352,12 +352,12 @@ _year, _mon, _mday, _hour, _min, _sec, _flags) \
(x)->hour = _hour;\
(x)->min = _min;\
(x)->sec = _sec;\
- (x)->flags = _flags;\
-}
+ (x)->flags = (_flags) | COMPLEX_DAT;\
+} while (0)
#else
#define set_to_complex(obj, x, _nth, _jd ,_df, _sf, _of, _sg,\
_year, _mon, _mday, _hour, _min, _sec, _flags) \
-{\
+do {\
RB_OBJ_WRITE((obj), &(x)->nth, canon(_nth));\
(x)->jd = _jd;\
(x)->df = _df;\
@@ -366,13 +366,13 @@ _year, _mon, _mday, _hour, _min, _sec, _flags) \
(x)->sg = (date_sg_t)(_sg);\
(x)->year = _year;\
(x)->pc = PACK5(_mon, _mday, _hour, _min, _sec);\
- (x)->flags = _flags;\
-}
+ (x)->flags = (_flags) | COMPLEX_DAT;\
+} while (0)
#endif
#ifndef USE_PACK
#define copy_simple_to_complex(obj, x, y) \
-{\
+do {\
RB_OBJ_WRITE((obj), &(x)->nth, (y)->nth);\
(x)->jd = (y)->jd;\
(x)->df = 0;\
@@ -386,10 +386,10 @@ _year, _mon, _mday, _hour, _min, _sec, _flags) \
(x)->min = 0;\
(x)->sec = 0;\
(x)->flags = (y)->flags;\
-}
+} while (0)
#else
#define copy_simple_to_complex(obj, x, y) \
-{\
+do {\
RB_OBJ_WRITE((obj), &(x)->nth, (y)->nth);\
(x)->jd = (y)->jd;\
(x)->df = 0;\
@@ -399,12 +399,12 @@ _year, _mon, _mday, _hour, _min, _sec, _flags) \
(x)->year = (y)->year;\
(x)->pc = PACK5(EX_MON((y)->pc), EX_MDAY((y)->pc), 0, 0, 0);\
(x)->flags = (y)->flags;\
-}
+} while (0)
#endif
#ifndef USE_PACK
#define copy_complex_to_simple(obj, x, y) \
-{\
+do {\
RB_OBJ_WRITE((obj), &(x)->nth, (y)->nth);\
(x)->jd = (y)->jd;\
(x)->sg = (date_sg_t)((y)->sg);\
@@ -412,17 +412,17 @@ _year, _mon, _mday, _hour, _min, _sec, _flags) \
(x)->mon = (y)->mon;\
(x)->mday = (y)->mday;\
(x)->flags = (y)->flags;\
-}
+} while (0)
#else
#define copy_complex_to_simple(obj, x, y) \
-{\
+do {\
RB_OBJ_WRITE((obj), &(x)->nth, (y)->nth);\
(x)->jd = (y)->jd;\
(x)->sg = (date_sg_t)((y)->sg);\
(x)->year = (y)->year;\
(x)->pc = PACK2(EX_MON((y)->pc), EX_MDAY((y)->pc));\
(x)->flags = (y)->flags;\
-}
+} while (0)
#endif
/* base */
@@ -1109,7 +1109,7 @@ m_virtual_sg(union DateData *x)
}
#define canonicalize_jd(_nth, _jd) \
-{\
+do {\
if (_jd < 0) {\
_nth = f_sub(_nth, INT2FIX(1));\
_jd += CM_PERIOD;\
@@ -1118,7 +1118,7 @@ m_virtual_sg(union DateData *x)
_nth = f_add(_nth, INT2FIX(1));\
_jd -= CM_PERIOD;\
}\
-}
+} while (0)
inline static void
canonicalize_s_jd(VALUE obj, union DateData *x)
@@ -1928,13 +1928,13 @@ m_sec(union DateData *x)
}
#define decode_offset(of,s,h,m)\
-{\
+do {\
int a;\
s = (of < 0) ? '-' : '+';\
a = (of < 0) ? -of : of;\
h = a / HOUR_IN_SECONDS;\
m = a % HOUR_IN_SECONDS / MINUTE_IN_SECONDS;\
-}
+} while (0)
static VALUE
of2str(int of)
@@ -2333,6 +2333,9 @@ VALUE date_zone_to_diff(VALUE);
static int
offset_to_sec(VALUE vof, int *rof)
{
+ int try_rational = 1;
+
+ again:
switch (TYPE(vof)) {
case T_FIXNUM:
{
@@ -2359,10 +2362,11 @@ offset_to_sec(VALUE vof, int *rof)
default:
expect_numeric(vof);
vof = f_to_r(vof);
-#ifdef CANONICALIZATION_FOR_MATHN
- if (!k_rational_p(vof))
- return offset_to_sec(vof, rof);
-#endif
+ if (!k_rational_p(vof)) {
+ if (!try_rational) Check_Type(vof, T_RATIONAL);
+ try_rational = 0;
+ goto again;
+ }
/* fall through */
case T_RATIONAL:
{
@@ -2371,17 +2375,10 @@ offset_to_sec(VALUE vof, int *rof)
vs = day_to_sec(vof);
-#ifdef CANONICALIZATION_FOR_MATHN
if (!k_rational_p(vs)) {
- if (!FIXNUM_P(vs))
- return 0;
- n = FIX2LONG(vs);
- if (n < -DAY_IN_SECONDS || n > DAY_IN_SECONDS)
- return 0;
- *rof = (int)n;
- return 1;
+ vn = vs;
+ goto rounded;
}
-#endif
vn = rb_rational_num(vs);
vd = rb_rational_den(vs);
@@ -2391,6 +2388,7 @@ offset_to_sec(VALUE vof, int *rof)
vn = f_round(vs);
if (!f_eqeq_p(vn, vs))
rb_warning("fraction of offset is ignored");
+ rounded:
if (!FIXNUM_P(vn))
return 0;
n = FIX2LONG(vn);
@@ -2420,12 +2418,12 @@ offset_to_sec(VALUE vof, int *rof)
/* date */
#define valid_sg(sg) \
-{\
+do {\
if (!c_valid_start_p(sg)) {\
sg = 0;\
rb_warning("invalid start is ignored");\
}\
-}
+} while (0)
static VALUE
valid_jd_sub(int argc, VALUE *argv, VALUE klass, int need_jd)
@@ -2462,7 +2460,7 @@ date_s__valid_jd_p(int argc, VALUE *argv, VALUE klass)
*
* Date.valid_jd?(2451944) #=> true
*
- * See also jd.
+ * See also ::jd.
*/
static VALUE
date_s_valid_jd_p(int argc, VALUE *argv, VALUE klass)
@@ -2551,7 +2549,7 @@ date_s__valid_civil_p(int argc, VALUE *argv, VALUE klass)
* Date.valid_date?(2001,2,3) #=> true
* Date.valid_date?(2001,2,29) #=> false
*
- * See also jd and civil.
+ * See also ::jd and ::civil.
*/
static VALUE
date_s_valid_civil_p(int argc, VALUE *argv, VALUE klass)
@@ -2632,7 +2630,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 also jd and ordinal.
+ * See also ::jd and ::ordinal.
*/
static VALUE
date_s_valid_ordinal_p(int argc, VALUE *argv, VALUE klass)
@@ -2714,7 +2712,7 @@ date_s__valid_commercial_p(int argc, VALUE *argv, VALUE klass)
* Date.valid_commercial?(2001,5,6) #=> true
* Date.valid_commercial?(2001,5,8) #=> false
*
- * See also jd and commercial.
+ * See also ::jd and ::commercial.
*/
static VALUE
date_s_valid_commercial_p(int argc, VALUE *argv, VALUE klass)
@@ -2968,7 +2966,7 @@ d_simple_new_internal(VALUE klass,
obj = TypedData_Make_Struct(klass, struct SimpleDateData,
&d_lite_type, dat);
- set_to_simple(obj, dat, nth, jd, sg, y, m, d, flags & ~COMPLEX_DAT);
+ set_to_simple(obj, dat, nth, jd, sg, y, m, d, flags);
assert(have_jd_p(dat) || have_civil_p(dat));
@@ -2990,7 +2988,7 @@ d_complex_new_internal(VALUE klass,
obj = TypedData_Make_Struct(klass, struct ComplexDateData,
&d_lite_type, dat);
set_to_complex(obj, dat, nth, jd, df, sf, of, sg,
- y, m, d, h, min, s, flags | COMPLEX_DAT);
+ y, m, d, h, min, s, flags);
assert(have_jd_p(dat) || have_civil_p(dat));
assert(have_df_p(dat) || have_time_p(dat));
@@ -3207,47 +3205,47 @@ s_trunc(VALUE s, VALUE *fr)
}
#define num2num_with_frac(s,n) \
-{\
+do {\
s = s##_trunc(v##s, &fr);\
if (f_nonzero_p(fr)) {\
if (argc > n)\
rb_raise(rb_eArgError, "invalid fraction");\
fr2 = fr;\
}\
-}
+} while (0)
#define num2int_with_frac(s,n) \
-{\
+do {\
s = NUM2INT(s##_trunc(v##s, &fr));\
if (f_nonzero_p(fr)) {\
if (argc > n)\
rb_raise(rb_eArgError, "invalid fraction");\
fr2 = fr;\
}\
-}
+} while (0)
#define canon24oc() \
-{\
+do {\
if (rh == 24) {\
rh = 0;\
fr2 = f_add(fr2, INT2FIX(1));\
}\
-}
+} while (0)
#define add_frac() \
-{\
+do {\
if (f_nonzero_p(fr2))\
ret = d_lite_plus(ret, fr2);\
-}
+} while (0)
#define val2sg(vsg,dsg) \
-{\
+do {\
dsg = NUM2DBL(vsg);\
if (!c_valid_start_p(dsg)) {\
dsg = DEFAULT_SG;\
rb_warning("invalid start is ignored");\
}\
-}
+} while (0)
static VALUE d_lite_plus(VALUE, VALUE);
@@ -3262,7 +3260,7 @@ static VALUE d_lite_plus(VALUE, VALUE);
* Date.jd(2451945) #=> #<Date: 2001-02-04 ...>
* Date.jd(0) #=> #<Date: -4712-01-01 ...>
*
- * See also new.
+ * See also ::new.
*/
static VALUE
date_s_jd(int argc, VALUE *argv, VALUE klass)
@@ -3312,7 +3310,7 @@ date_s_jd(int argc, VALUE *argv, VALUE klass)
* Date.ordinal(2001,34) #=> #<Date: 2001-02-03 ...>
* Date.ordinal(2001,-1) #=> #<Date: 2001-12-31 ...>
*
- * See also jd and new.
+ * See also ::jd and ::new.
*/
static VALUE
date_s_ordinal(int argc, VALUE *argv, VALUE klass)
@@ -3380,14 +3378,25 @@ date_s_ordinal(int argc, VALUE *argv, VALUE klass)
* Date.new(2001,2,3) #=> #<Date: 2001-02-03 ...>
* Date.new(2001,2,-1) #=> #<Date: 2001-02-28 ...>
*
- * See also jd.
+ * See also ::jd.
*/
static VALUE
date_s_civil(int argc, VALUE *argv, VALUE klass)
{
+ return date_initialize(argc, argv, d_lite_s_alloc_simple(klass));
+}
+
+static VALUE
+date_initialize(int argc, VALUE *argv, VALUE self)
+{
VALUE vy, vm, vd, vsg, y, fr, fr2, ret;
int m, d;
double sg;
+ struct SimpleDateData *dat = rb_check_typeddata(self, &d_lite_type);
+
+ if (!simple_dat_p(dat)) {
+ rb_raise(rb_eTypeError, "Date expected");
+ }
rb_scan_args(argc, argv, "04", &vy, &vm, &vd, &vsg);
@@ -3417,11 +3426,7 @@ date_s_civil(int argc, VALUE *argv, VALUE klass)
&rm, &rd))
rb_raise(rb_eArgError, "invalid date");
- ret = d_simple_new_internal(klass,
- nth, 0,
- sg,
- ry, rm, rd,
- HAVE_CIVIL);
+ set_to_simple(self, dat, nth, 0, sg, ry, rm, rd, HAVE_CIVIL);
}
else {
VALUE nth;
@@ -3433,12 +3438,9 @@ date_s_civil(int argc, VALUE *argv, VALUE klass)
&ns))
rb_raise(rb_eArgError, "invalid date");
- ret = d_simple_new_internal(klass,
- nth, rjd,
- sg,
- ry, rm, rd,
- HAVE_JD | HAVE_CIVIL);
+ set_to_simple(self, dat, nth, rjd, sg, ry, rm, rd, HAVE_JD | HAVE_CIVIL);
}
+ ret = self;
add_frac();
return ret;
}
@@ -3457,7 +3459,7 @@ date_s_civil(int argc, VALUE *argv, VALUE klass)
* Date.commercial(2002) #=> #<Date: 2001-12-31 ...>
* Date.commercial(2001,5,6) #=> #<Date: 2001-02-03 ...>
*
- * See also jd and new.
+ * See also ::jd and ::new.
*/
static VALUE
date_s_commercial(int argc, VALUE *argv, VALUE klass)
@@ -3631,9 +3633,9 @@ static void set_sg(union DateData *, double);
* call-seq:
* Date.today([start=Date::ITALY]) -> date
*
- * Date.today #=> #<Date: 2011-06-11 ..>
- *
* Creates a date object denoting the present day.
+ *
+ * Date.today #=> #<Date: 2011-06-11 ...>
*/
static VALUE
date_s_today(int argc, VALUE *argv, VALUE klass)
@@ -3679,9 +3681,11 @@ date_s_today(int argc, VALUE *argv, VALUE klass)
#define ref_hash0(k) rb_hash_aref(hash, k)
#define del_hash0(k) rb_hash_delete(hash, k)
-#define set_hash(k,v) rb_hash_aset(hash, ID2SYM(rb_intern(k)), v)
-#define ref_hash(k) rb_hash_aref(hash, ID2SYM(rb_intern(k)))
-#define del_hash(k) rb_hash_delete(hash, ID2SYM(rb_intern(k)))
+#define sym(x) ID2SYM(rb_intern(x""))
+
+#define set_hash(k,v) set_hash0(sym(k), v)
+#define ref_hash(k) ref_hash0(sym(k))
+#define del_hash(k) del_hash0(sym(k))
static VALUE
rt_rewrite_frags(VALUE hash)
@@ -3718,8 +3722,6 @@ rt_rewrite_frags(VALUE hash)
return hash;
}
-#define sym(x) ID2SYM(rb_intern(x))
-
static VALUE d_lite_year(VALUE);
static VALUE d_lite_wday(VALUE);
static VALUE d_lite_jd(VALUE);
@@ -4236,7 +4238,7 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass,
* Date._strptime('2001-02-03', '%Y-%m-%d')
* #=> {:year=>2001, :mon=>2, :mday=>3}
*
- * See also strptime(3) and strftime.
+ * See also strptime(3) and #strftime.
*/
static VALUE
date_s__strptime(int argc, VALUE *argv, VALUE klass)
@@ -4246,7 +4248,7 @@ date_s__strptime(int argc, VALUE *argv, VALUE klass)
/*
* call-seq:
- * Date.strptime([string='-4712-01-01'[, format='%F'[, start=ITALY]]]) -> date
+ * 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
@@ -4260,7 +4262,7 @@ date_s__strptime(int argc, VALUE *argv, VALUE klass)
* Date.strptime('2001 05 6', '%Y %W %u') #=> #<Date: 2001-02-03 ...>
* Date.strptime('sat3feb01', '%a%d%b%y') #=> #<Date: 2001-02-03 ...>
*
- * See also strptime(3) and strftime.
+ * See also strptime(3) and #strftime.
*/
static VALUE
date_s_strptime(int argc, VALUE *argv, VALUE klass)
@@ -4290,12 +4292,40 @@ date_s_strptime(int argc, VALUE *argv, VALUE klass)
VALUE date__parse(VALUE str, VALUE comp);
+static size_t
+get_limit(VALUE opt)
+{
+ if (!NIL_P(opt)) {
+ VALUE limit = rb_hash_aref(opt, ID2SYM(rb_intern("limit")));
+ if (NIL_P(limit)) return SIZE_MAX;
+ return NUM2SIZET(limit);
+ }
+ return 128;
+}
+
+static void
+check_limit(VALUE str, VALUE opt)
+{
+ if (NIL_P(str)) return;
+ if (SYMBOL_P(str)) str = rb_sym2str(str);
+
+ StringValue(str);
+ size_t slen = RSTRING_LEN(str);
+ size_t limit = get_limit(opt);
+ if (slen > limit) {
+ rb_raise(rb_eArgError,
+ "string length (%"PRI_SIZE_PREFIX"u) exceeds the limit %"PRI_SIZE_PREFIX"u", slen, limit);
+ }
+}
+
static VALUE
date_s__parse_internal(int argc, VALUE *argv, VALUE klass)
{
- VALUE vstr, vcomp, hash;
+ VALUE vstr, vcomp, hash, opt;
- rb_scan_args(argc, argv, "11", &vstr, &vcomp);
+ rb_scan_args(argc, argv, "11:", &vstr, &vcomp, &opt);
+ if (!NIL_P(opt)) argc--;
+ check_limit(vstr, opt);
StringValue(vstr);
if (!rb_enc_str_asciicompat_p(vstr))
rb_raise(rb_eArgError,
@@ -4320,7 +4350,7 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass)
/*
* call-seq:
- * Date._parse(string[, comp=true]) -> hash
+ * Date._parse(string[, comp=true], limit: 128) -> hash
*
* Parses the given representation of date and time, and returns a
* hash of parsed elements. This method does not function as a
@@ -4331,6 +4361,10 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass)
* it full.
*
* 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.
*/
static VALUE
date_s__parse(int argc, VALUE *argv, VALUE klass)
@@ -4340,7 +4374,7 @@ date_s__parse(int argc, VALUE *argv, VALUE klass)
/*
* call-seq:
- * Date.parse(string='-4712-01-01'[, comp=true[, start=ITALY]]) -> date
+ * 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. This method does not function as a validator.
@@ -4352,13 +4386,18 @@ date_s__parse(int argc, VALUE *argv, VALUE klass)
* 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 ...>
+ *
+ * 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.
*/
static VALUE
date_s_parse(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, comp, sg;
+ VALUE str, comp, sg, opt;
- rb_scan_args(argc, argv, "03", &str, &comp, &sg);
+ rb_scan_args(argc, argv, "03:", &str, &comp, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -4370,11 +4409,12 @@ date_s_parse(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE argv2[2], hash;
-
- argv2[0] = str;
- argv2[1] = comp;
- hash = date_s__parse(2, argv2, klass);
+ int argc2 = 2;
+ VALUE argv2[3];
+ argv2[0] = str;
+ argv2[1] = comp;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__parse(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
@@ -4388,19 +4428,28 @@ VALUE date__jisx0301(VALUE);
/*
* call-seq:
- * Date._iso8601(string) -> hash
+ * Date._iso8601(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * 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.
*/
static VALUE
-date_s__iso8601(VALUE klass, VALUE str)
+date_s__iso8601(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__iso8601(str);
}
/*
* call-seq:
- * Date.iso8601(string='-4712-01-01'[, start=ITALY]) -> date
+ * 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.
@@ -4408,13 +4457,18 @@ date_s__iso8601(VALUE klass, VALUE str)
* 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 ...>
+ *
+ * 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.
*/
static VALUE
date_s_iso8601(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -4424,38 +4478,56 @@ date_s_iso8601(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__iso8601(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__iso8601(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * Date._rfc3339(string) -> hash
+ * Date._rfc3339(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * 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.
*/
static VALUE
-date_s__rfc3339(VALUE klass, VALUE str)
+date_s__rfc3339(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__rfc3339(str);
}
/*
* call-seq:
- * Date.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=ITALY]) -> date
+ * 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.
*
* Date.rfc3339('2001-02-03T04:05:06+07:00') #=> #<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.
*/
static VALUE
date_s_rfc3339(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -4465,38 +4537,56 @@ date_s_rfc3339(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__rfc3339(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__rfc3339(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * Date._xmlschema(string) -> hash
+ * Date._xmlschema(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * 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.
*/
static VALUE
-date_s__xmlschema(VALUE klass, VALUE str)
+date_s__xmlschema(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__xmlschema(str);
}
/*
* call-seq:
- * Date.xmlschema(string='-4712-01-01'[, start=ITALY]) -> date
+ * 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.
*
* Date.xmlschema('2001-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.
*/
static VALUE
date_s_xmlschema(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -4506,41 +4596,58 @@ date_s_xmlschema(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__xmlschema(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__xmlschema(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * Date._rfc2822(string) -> hash
- * Date._rfc822(string) -> hash
+ * Date._rfc2822(string, limit: 128) -> hash
+ * Date._rfc822(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * 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.
*/
static VALUE
-date_s__rfc2822(VALUE klass, VALUE str)
+date_s__rfc2822(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__rfc2822(str);
}
/*
* call-seq:
- * Date.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=ITALY]) -> date
- * Date.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=ITALY]) -> date
+ * Date.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> date
+ * Date.rfc822(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.
*
* Date.rfc2822('Sat, 3 Feb 2001 00:00:00 +0000')
* #=> #<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.
*/
static VALUE
date_s_rfc2822(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
switch (argc) {
case 0:
@@ -4550,39 +4657,56 @@ date_s_rfc2822(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__rfc2822(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__rfc2822(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * Date._httpdate(string) -> hash
+ * Date._httpdate(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * 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.
*/
static VALUE
-date_s__httpdate(VALUE klass, VALUE str)
+date_s__httpdate(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__httpdate(str);
}
/*
* call-seq:
- * Date.httpdate(string='Mon, 01 Jan -4712 00:00:00 GMT'[, start=ITALY]) -> date
+ * 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.
*
* Date.httpdate('Sat, 03 Feb 2001 00:00:00 GMT')
* #=> #<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.
*/
static VALUE
date_s_httpdate(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
switch (argc) {
case 0:
@@ -4592,38 +4716,60 @@ date_s_httpdate(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__httpdate(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__httpdate(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * Date._jisx0301(string) -> hash
+ * Date._jisx0301(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * 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.
*/
static VALUE
-date_s__jisx0301(VALUE klass, VALUE str)
+date_s__jisx0301(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__jisx0301(str);
}
/*
* call-seq:
- * Date.jisx0301(string='-4712-01-01'[, start=ITALY]) -> date
+ * 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.
*
* Date.jisx0301('H13.02.03') #=> #<Date: 2001-02-03 ...>
+ *
+ * For no-era year, legacy format, Heisei is assumed.
+ *
+ * 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.
*/
static VALUE
date_s_jisx0301(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -4633,7 +4779,11 @@ date_s_jisx0301(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__jisx0301(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__jisx0301(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
@@ -4691,14 +4841,14 @@ dup_obj_as_complex(VALUE self)
}
#define val2off(vof,iof) \
-{\
+do {\
if (!offset_to_sec(vof, &iof)) {\
iof = 0;\
rb_warning("invalid offset is ignored");\
}\
-}
+} while (0)
-#ifndef NDEBUG
+#if 0
static VALUE
d_lite_initialize(int argc, VALUE *argv, VALUE self)
{
@@ -4751,7 +4901,7 @@ d_lite_initialize(int argc, VALUE *argv, VALUE self)
"cannot load complex into simple");
set_to_complex(self, &dat->c, nth, rjd, df, sf, of, sg,
- 0, 0, 0, 0, 0, 0, HAVE_JD | HAVE_DF | COMPLEX_DAT);
+ 0, 0, 0, 0, 0, 0, HAVE_JD | HAVE_DF);
}
}
return self;
@@ -4770,8 +4920,28 @@ d_lite_initialize_copy(VALUE copy, VALUE date)
{
get_d2(copy, date);
if (simple_dat_p(bdat)) {
- adat->s = bdat->s;
- adat->s.flags &= ~COMPLEX_DAT;
+ if (simple_dat_p(adat)) {
+ adat->s = bdat->s;
+ }
+ else {
+ adat->c.flags = bdat->s.flags | COMPLEX_DAT;
+ adat->c.nth = bdat->s.nth;
+ adat->c.jd = bdat->s.jd;
+ adat->c.df = 0;
+ adat->c.sf = INT2FIX(0);
+ adat->c.of = 0;
+ adat->c.sg = bdat->s.sg;
+ adat->c.year = bdat->s.year;
+#ifndef USE_PACK
+ adat->c.mon = bdat->s.mon;
+ adat->c.mday = bdat->s.mday;
+ adat->c.hour = bdat->s.hour;
+ adat->c.min = bdat->s.min;
+ adat->c.sec = bdat->s.sec;
+#else
+ adat->c.pc = bdat->s.pc;
+#endif
+ }
}
else {
if (!complex_dat_p(adat))
@@ -4779,7 +4949,6 @@ d_lite_initialize_copy(VALUE copy, VALUE date)
"cannot load complex into simple");
adat->c = bdat->c;
- adat->c.flags |= COMPLEX_DAT;
}
}
return copy;
@@ -5380,7 +5549,7 @@ dup_obj_with_new_start(VALUE obj, double sg)
* call-seq:
* d.new_start([start=Date::ITALY]) -> date
*
- * Duplicates self and resets its the day of calendar reform.
+ * Duplicates self and resets its day of calendar reform.
*
* d = Date.new(1582,10,15)
* d.new_start(Date::JULIAN) #=> #<Date: 1582-10-05 ...>
@@ -5498,9 +5667,9 @@ d_lite_new_offset(int argc, VALUE *argv, VALUE self)
* call-seq:
* d + other -> date
*
- * Returns a date object pointing other days after self. The other
- * should be a numeric value. If the other is flonum, assumes its
- * precision is at most nanosecond.
+ * Returns a date object pointing +other+ days after self. The other
+ * should be a numeric value. If the other is a fractional number,
+ * assumes its precision is at most nanosecond.
*
* Date.new(2001,2,3) + 1 #=> #<Date: 2001-02-04 ...>
* DateTime.new(2001,2,3) + Rational(1,2)
@@ -5513,8 +5682,10 @@ d_lite_new_offset(int argc, VALUE *argv, VALUE self)
static VALUE
d_lite_plus(VALUE self, VALUE other)
{
+ int try_rational = 1;
get_d1(self);
+ again:
switch (TYPE(other)) {
case T_FIXNUM:
{
@@ -5724,18 +5895,21 @@ d_lite_plus(VALUE self, VALUE other)
default:
expect_numeric(other);
other = f_to_r(other);
-#ifdef CANONICALIZATION_FOR_MATHN
- if (!k_rational_p(other))
- return d_lite_plus(self, other);
-#endif
+ if (!k_rational_p(other)) {
+ if (!try_rational) Check_Type(other, T_RATIONAL);
+ try_rational = 0;
+ goto again;
+ }
/* fall through */
case T_RATIONAL:
{
VALUE nth, sf, t;
int jd, df, s;
- if (wholenum_p(other))
- return d_lite_plus(self, rb_rational_num(other));
+ if (wholenum_p(other)) {
+ other = rb_rational_num(other);
+ goto again;
+ }
if (f_positive_p(other))
s = +1;
@@ -5883,8 +6057,8 @@ minus_dd(VALUE self, VALUE other)
*
* Returns the difference between the two dates if the other is a date
* object. If the other is a numeric value, returns a date object
- * pointing other days before self. If the other is flonum, assumes
- * its precision is at most nanosecond.
+ * pointing +other+ days before self. If the other is a fractional number,
+ * assumes its precision is at most nanosecond.
*
* Date.new(2001,2,3) - 1 #=> #<Date: 2001-02-02 ...>
* DateTime.new(2001,2,3) - Rational(1,2)
@@ -5965,12 +6139,24 @@ d_lite_next(VALUE self)
* call-seq:
* d >> n -> date
*
- * Returns a date object pointing n months after self. The n should
- * be a numeric value.
+ * Returns a date object pointing +n+ months after self.
+ * The argument +n+ should be a numeric value.
+ *
+ * Date.new(2001,2,3) >> 1 #=> #<Date: 2001-03-03 ...>
+ * Date.new(2001,2,3) >> -2 #=> #<Date: 2000-12-03 ...>
+ *
+ * When the same day does not exist for the corresponding month,
+ * the last day of the month is used instead:
+ *
+ * Date.new(2001,1,28) >> 1 #=> #<Date: 2001-02-28 ...>
+ * Date.new(2001,1,31) >> 1 #=> #<Date: 2001-02-28 ...>
+ *
+ * This also results in the following, possibly unexpected, behavior:
*
- * Date.new(2001,2,3) >> 1 #=> #<Date: 2001-03-03 ...>
- * Date.new(2001,1,31) >> 1 #=> #<Date: 2001-02-28 ...>
- * Date.new(2001,2,3) >> -2 #=> #<Date: 2000-12-03 ...>
+ * Date.new(2001,1,31) >> 2 #=> #<Date: 2001-03-31 ...>
+ * Date.new(2001,1,31) >> 1 >> 1 #=> #<Date: 2001-03-28 ...>
+ *
+ * Date.new(2001,1,31) >> 1 >> -1 #=> #<Date: 2001-01-28 ...>
*/
static VALUE
d_lite_rshift(VALUE self, VALUE other)
@@ -6015,12 +6201,24 @@ d_lite_rshift(VALUE self, VALUE other)
* call-seq:
* d << n -> date
*
- * Returns a date object pointing n months before self. The n should
- * be a numeric value.
+ * Returns a date object pointing +n+ months before self.
+ * The argument +n+ should be a numeric value.
+ *
+ * Date.new(2001,2,3) << 1 #=> #<Date: 2001-01-03 ...>
+ * Date.new(2001,2,3) << -2 #=> #<Date: 2001-04-03 ...>
+ *
+ * When the same day does not exist for the corresponding month,
+ * the last day of the month is used instead:
+ *
+ * Date.new(2001,3,28) << 1 #=> #<Date: 2001-02-28 ...>
+ * Date.new(2001,3,31) << 1 #=> #<Date: 2001-02-28 ...>
+ *
+ * This also results in the following, possibly unexpected, behavior:
+ *
+ * Date.new(2001,3,31) << 2 #=> #<Date: 2001-01-31 ...>
+ * Date.new(2001,3,31) << 1 << 1 #=> #<Date: 2001-01-28 ...>
*
- * Date.new(2001,2,3) << 1 #=> #<Date: 2001-01-03 ...>
- * Date.new(2001,1,31) << 11 #=> #<Date: 2000-02-29 ...>
- * Date.new(2001,2,3) << -1 #=> #<Date: 2001-03-03 ...>
+ * Date.new(2001,3,31) << 1 << -1 #=> #<Date: 2001-03-28 ...>
*/
static VALUE
d_lite_lshift(VALUE self, VALUE other)
@@ -6033,7 +6231,9 @@ d_lite_lshift(VALUE self, VALUE other)
* call-seq:
* d.next_month([n=1]) -> date
*
- * This method is equivalent to d >> n
+ * This method is equivalent to d >> n.
+ *
+ * See Date#>> for examples.
*/
static VALUE
d_lite_next_month(int argc, VALUE *argv, VALUE self)
@@ -6050,7 +6250,9 @@ d_lite_next_month(int argc, VALUE *argv, VALUE self)
* call-seq:
* d.prev_month([n=1]) -> date
*
- * This method is equivalent to d << n
+ * This method is equivalent to d << n.
+ *
+ * See Date#<< for examples.
*/
static VALUE
d_lite_prev_month(int argc, VALUE *argv, VALUE self)
@@ -6067,7 +6269,13 @@ d_lite_prev_month(int argc, VALUE *argv, VALUE self)
* call-seq:
* d.next_year([n=1]) -> date
*
- * This method is equivalent to d >> (n * 12)
+ * This method is equivalent to d >> (n * 12).
+ *
+ * Date.new(2001,2,3).next_year #=> #<Date: 2002-02-03 ...>
+ * Date.new(2008,2,29).next_year #=> #<Date: 2009-02-28 ...>
+ * Date.new(2008,2,29).next_year(4) #=> #<Date: 2012-02-29 ...>
+ *
+ * See also Date#>>.
*/
static VALUE
d_lite_next_year(int argc, VALUE *argv, VALUE self)
@@ -6084,7 +6292,13 @@ d_lite_next_year(int argc, VALUE *argv, VALUE self)
* call-seq:
* d.prev_year([n=1]) -> date
*
- * This method is equivalent to d << (n * 12)
+ * This method is equivalent to d << (n * 12).
+ *
+ * Date.new(2001,2,3).prev_year #=> #<Date: 2000-02-03 ...>
+ * Date.new(2008,2,29).prev_year #=> #<Date: 2007-02-28 ...>
+ * Date.new(2008,2,29).prev_year(4) #=> #<Date: 2004-02-29 ...>
+ *
+ * See also Date#<<.
*/
static VALUE
d_lite_prev_year(int argc, VALUE *argv, VALUE self)
@@ -6114,6 +6328,7 @@ static VALUE
d_lite_step(int argc, VALUE *argv, VALUE self)
{
VALUE limit, step, date;
+ int c;
rb_scan_args(argc, argv, "11", &limit, &step);
@@ -6128,25 +6343,22 @@ d_lite_step(int argc, VALUE *argv, VALUE self)
RETURN_ENUMERATOR(self, argc, argv);
date = self;
- switch (FIX2INT(f_cmp(step, INT2FIX(0)))) {
- case -1:
+ c = f_cmp(step, INT2FIX(0));
+ if (c < 0) {
while (FIX2INT(d_lite_cmp(date, limit)) >= 0) {
rb_yield(date);
date = d_lite_plus(date, step);
}
- break;
- case 0:
+ }
+ else if (c == 0) {
while (1)
rb_yield(date);
- break;
- case 1:
+ }
+ else /* if (c > 0) */ {
while (FIX2INT(d_lite_cmp(date, limit)) <= 0) {
rb_yield(date);
date = d_lite_plus(date, step);
}
- break;
- default:
- abort();
}
return self;
}
@@ -6201,10 +6413,10 @@ cmp_gen(VALUE self, VALUE other)
get_d1(self);
if (k_numeric_p(other))
- return f_cmp(m_ajd(dat), other);
+ return INT2FIX(f_cmp(m_ajd(dat), other));
else if (k_date_p(other))
- return f_cmp(m_ajd(dat), f_ajd(other));
- return rb_num_coerce_cmp(self, other, rb_intern("<=>"));
+ return INT2FIX(f_cmp(m_ajd(dat), f_ajd(other)));
+ return rb_num_coerce_cmp(self, other, id_cmp);
}
static VALUE
@@ -6272,11 +6484,11 @@ cmp_dd(VALUE self, VALUE other)
* should be a date object or a numeric value as an astronomical
* Julian day number.
*
- * 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
+ * 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
*
* See also Comparable.
*/
@@ -6333,7 +6545,7 @@ equal_gen(VALUE self, VALUE other)
return f_eqeq_p(m_real_local_jd(dat), other);
else if (k_date_p(other))
return f_eqeq_p(m_real_local_jd(dat), f_jd(other));
- return rb_num_coerce_cmp(self, other, rb_intern("=="));
+ return rb_num_coerce_cmp(self, other, id_eqeq_p);
}
/*
@@ -6404,7 +6616,7 @@ d_lite_hash(VALUE self)
h[2] = m_df(dat);
h[3] = m_sf(dat);
v = rb_memhash(h, sizeof(h));
- return LONG2FIX(v);
+ return ST2FIX(v);
}
#include "date_tmx.h"
@@ -6416,8 +6628,8 @@ static VALUE strftimev(const char *, VALUE,
* call-seq:
* d.to_s -> string
*
- * Returns a string in an ISO 8601 format (This method doesn't use the
- * expanded representations).
+ * Returns a string in an ISO 8601 format. (This method doesn't use the
+ * expanded representations.)
*
* Date.new(2001,2,3).to_s #=> "2001-02-03"
*/
@@ -6431,7 +6643,7 @@ d_lite_to_s(VALUE self)
static VALUE
mk_inspect_raw(union DateData *x, VALUE klass)
{
- char flags[5];
+ char flags[6];
flags[0] = (x->flags & COMPLEX_DAT) ? 'C' : 'S';
flags[1] = (x->flags & HAVE_JD) ? 'j' : '-';
@@ -6597,7 +6809,9 @@ tmx_m_of(union DateData *x)
static char *
tmx_m_zone(union DateData *x)
{
- return RSTRING_PTR(m_zone(x));
+ VALUE zone = m_zone(x);
+ /* TODO: fix potential dangling pointer */
+ return RSTRING_PTR(zone);
}
static const struct tmx_funcs tmx_funcs = {
@@ -6691,13 +6905,13 @@ date_strftime_internal(int argc, VALUE *argv, VALUE self,
*
* Formats date according to the directives in the given format
* string.
- * The directives begins with a percent (%) character.
+ * The directives begin with a percent (%) character.
* Any text not listed as a directive will be passed through to the
* output string.
*
- * The directive consists of a percent (%) character,
- * zero or more flags, optional minimum field width,
- * optional modifier and a conversion specifier
+ * A directive consists of a percent (%) character,
+ * zero or more flags, an optional minimum field width,
+ * an optional modifier, and a conversion specifier
* as follows.
*
* %<flags><width><modifier><conversion>
@@ -6747,7 +6961,7 @@ date_strftime_internal(int argc, VALUE *argv, VALUE self,
*
* %M - Minute of the hour (00..59)
*
- * %S - Second of the minute (00..59)
+ * %S - Second of the minute (00..60)
*
* %L - Millisecond of the second (000..999)
* %N - Fractional seconds digits, default is 9 digits (nanosecond)
@@ -6762,7 +6976,7 @@ date_strftime_internal(int argc, VALUE *argv, VALUE self,
* %::z - hour, minute and second offset from UTC (e.g. +09:00:00)
* %:::z - hour, minute and second offset from UTC
* (e.g. +09, +09:30, +09:30:30)
- * %Z - Time zone abbreviation name or something similar information.
+ * %Z - Equivalent to %:z (e.g. +09:00)
*
* Weekday:
* %A - The full weekday name (``Sunday'')
@@ -6807,11 +7021,12 @@ date_strftime_internal(int argc, VALUE *argv, VALUE self,
* %T - 24-hour time (%H:%M:%S)
* %+ - date(1) (%a %b %e %H:%M:%S %Z %Y)
*
- * This method is similar to strftime() function defined in ISO C and POSIX.
+ * This method is similar to the strftime() function defined in ISO C
+ * and POSIX.
* Several directives (%a, %A, %b, %B, %c, %p, %r, %x, %X, %E*, %O* and %Z)
* are locale dependent in the function.
- * However this method is locale independent.
- * So, the result may differ even if a same format string is used in other
+ * However, this method is locale independent.
+ * So, the result may differ even if the same format string is used in other
* systems such as C.
* It is good practice to avoid %x and %X because there are corresponding
* locale independent representations, %D and %T.
@@ -6859,7 +7074,7 @@ date_strftime_internal(int argc, VALUE *argv, VALUE self,
* %GW%V%uT%H%M%z => 2007W471T0837-0600 Week date and local time and difference from UTC (basic)
* %G-W%V-%uT%R%:z => 2007-W47-1T08:37-06:00 Week date and local time and difference from UTC (extended)
*
- * See also strftime(3) and strptime.
+ * See also strftime(3) and ::strptime.
*/
static VALUE
d_lite_strftime(int argc, VALUE *argv, VALUE self)
@@ -6979,10 +7194,14 @@ jisx0301_date_format(char *fmt, size_t size, VALUE jd, VALUE y)
c = 'S';
s = 1925;
}
- else {
+ else if (d < 2458605) {
c = 'H';
s = 1988;
}
+ else {
+ c = 'R';
+ s = 2018;
+ }
snprintf(fmt, size, "%c%02ld" ".%%m.%%d", c, FIX2INT(y) - s);
return fmt;
}
@@ -7060,6 +7279,10 @@ d_lite_marshal_dump(VALUE self)
static VALUE
d_lite_marshal_load(VALUE self, VALUE a)
{
+ VALUE nth, sf;
+ int jd, df, of;
+ double sg;
+
get_d1(self);
rb_check_frozen(self);
@@ -7072,63 +7295,33 @@ d_lite_marshal_load(VALUE self, VALUE a)
case 2: /* 1.6.x */
case 3: /* 1.8.x, 1.9.2 */
{
- VALUE ajd, of, sg, nth, sf;
- int jd, df, rof;
- double rsg;
-
+ VALUE ajd, vof, vsg;
if (RARRAY_LEN(a) == 2) {
ajd = f_sub(RARRAY_AREF(a, 0), half_days_in_day);
- of = INT2FIX(0);
- sg = RARRAY_AREF(a, 1);
- if (!k_numeric_p(sg))
- sg = DBL2NUM(RTEST(sg) ? GREGORIAN : JULIAN);
+ vof = INT2FIX(0);
+ vsg = RARRAY_AREF(a, 1);
+ if (!k_numeric_p(vsg))
+ vsg = DBL2NUM(RTEST(vsg) ? GREGORIAN : JULIAN);
}
else {
ajd = RARRAY_AREF(a, 0);
- of = RARRAY_AREF(a, 1);
- sg = RARRAY_AREF(a, 2);
+ vof = RARRAY_AREF(a, 1);
+ vsg = RARRAY_AREF(a, 2);
}
- old_to_new(ajd, of, sg,
- &nth, &jd, &df, &sf, &rof, &rsg);
-
- if (!df && f_zero_p(sf) && !rof) {
- set_to_simple(self, &dat->s, nth, jd, rsg, 0, 0, 0, HAVE_JD);
- } else {
- if (!complex_dat_p(dat))
- rb_raise(rb_eArgError,
- "cannot load complex into simple");
-
- set_to_complex(self, &dat->c, nth, jd, df, sf, rof, rsg,
- 0, 0, 0, 0, 0, 0,
- HAVE_JD | HAVE_DF | COMPLEX_DAT);
- }
+ old_to_new(ajd, vof, vsg,
+ &nth, &jd, &df, &sf, &of, &sg);
}
break;
case 6:
{
- VALUE nth, sf;
- int jd, df, of;
- double sg;
-
nth = RARRAY_AREF(a, 0);
jd = NUM2INT(RARRAY_AREF(a, 1));
df = NUM2INT(RARRAY_AREF(a, 2));
sf = RARRAY_AREF(a, 3);
of = NUM2INT(RARRAY_AREF(a, 4));
sg = NUM2DBL(RARRAY_AREF(a, 5));
- if (!df && f_zero_p(sf) && !of) {
- set_to_simple(self, &dat->s, nth, jd, sg, 0, 0, 0, HAVE_JD);
- } else {
- if (!complex_dat_p(dat))
- rb_raise(rb_eArgError,
- "cannot load complex into simple");
-
- set_to_complex(self, &dat->c, nth, jd, df, sf, of, sg,
- 0, 0, 0, 0, 0, 0,
- HAVE_JD | HAVE_DF | COMPLEX_DAT);
- }
}
break;
default:
@@ -7136,6 +7329,18 @@ d_lite_marshal_load(VALUE self, VALUE a)
break;
}
+ if (simple_dat_p(dat)) {
+ if (df || !f_zero_p(sf) || of) {
+ rb_raise(rb_eArgError,
+ "cannot load complex into simple");
+ }
+ set_to_simple(self, &dat->s, nth, jd, sg, 0, 0, 0, HAVE_JD);
+ } else {
+ set_to_complex(self, &dat->c, nth, jd, df, sf, of, sg,
+ 0, 0, 0, 0, 0, 0,
+ HAVE_JD | HAVE_DF);
+ }
+
if (FL_TEST(a, FL_EXIVAR)) {
rb_copy_generic_ivar(self, a);
FL_SET(self, FL_EXIVAR);
@@ -7161,7 +7366,7 @@ date_s__load(VALUE klass, VALUE s)
* call-seq:
* DateTime.jd([jd=0[, hour=0[, minute=0[, second=0[, offset=0[, start=Date::ITALY]]]]]]) -> datetime
*
- * Creates a datetime object denoting the given chronological Julian
+ * Creates a DateTime object denoting the given chronological Julian
* day number.
*
* DateTime.jd(2451944) #=> #<DateTime: 2001-02-03T00:00:00+00:00 ...>
@@ -7229,7 +7434,7 @@ datetime_s_jd(int argc, VALUE *argv, VALUE klass)
* call-seq:
* DateTime.ordinal([year=-4712[, yday=1[, hour=0[, minute=0[, second=0[, offset=0[, start=Date::ITALY]]]]]]]) -> datetime
*
- * Creates a date-time object denoting the given ordinal date.
+ * Creates a DateTime object denoting the given ordinal date.
*
* DateTime.ordinal(2001,34) #=> #<DateTime: 2001-02-03T00:00:00+00:00 ...>
* DateTime.ordinal(2001,34,4,5,6,'+7')
@@ -7305,7 +7510,7 @@ datetime_s_ordinal(int argc, VALUE *argv, VALUE klass)
* DateTime.civil([year=-4712[, month=1[, mday=1[, hour=0[, minute=0[, second=0[, offset=0[, start=Date::ITALY]]]]]]]]) -> datetime
* DateTime.new([year=-4712[, month=1[, mday=1[, hour=0[, minute=0[, second=0[, offset=0[, start=Date::ITALY]]]]]]]]) -> datetime
*
- * Creates a date-time object denoting the given calendar date.
+ * Creates a DateTime object denoting the given calendar date.
*
* DateTime.new(2001,2,3) #=> #<DateTime: 2001-02-03T00:00:00+00:00 ...>
* DateTime.new(2001,2,3,4,5,6,'+7')
@@ -7316,9 +7521,20 @@ datetime_s_ordinal(int argc, VALUE *argv, VALUE klass)
static VALUE
datetime_s_civil(int argc, VALUE *argv, VALUE klass)
{
+ return datetime_initialize(argc, argv, d_lite_s_alloc_complex(klass));
+}
+
+static VALUE
+datetime_initialize(int argc, VALUE *argv, VALUE self)
+{
VALUE vy, vm, vd, vh, vmin, vs, vof, vsg, y, fr, fr2, ret;
int m, d, h, min, s, rof;
double sg;
+ struct ComplexDateData *dat = rb_check_typeddata(self, &d_lite_type);
+
+ if (!complex_dat_p(dat)) {
+ rb_raise(rb_eTypeError, "DateTime expected");
+ }
rb_scan_args(argc, argv, "08", &vy, &vm, &vd, &vh, &vmin, &vs, &vof, &vsg);
@@ -7362,13 +7578,13 @@ datetime_s_civil(int argc, VALUE *argv, VALUE klass)
rb_raise(rb_eArgError, "invalid date");
canon24oc();
- ret = d_complex_new_internal(klass,
- nth, 0,
- 0, INT2FIX(0),
- rof, sg,
- ry, rm, rd,
- rh, rmin, rs,
- HAVE_CIVIL | HAVE_TIME);
+ set_to_complex(self, dat,
+ nth, 0,
+ 0, INT2FIX(0),
+ rof, sg,
+ ry, rm, rd,
+ rh, rmin, rs,
+ HAVE_CIVIL | HAVE_TIME);
}
else {
VALUE nth;
@@ -7387,14 +7603,15 @@ datetime_s_civil(int argc, VALUE *argv, VALUE klass)
time_to_df(rh, rmin, rs),
rof);
- ret = d_complex_new_internal(klass,
- nth, rjd2,
- 0, INT2FIX(0),
- rof, sg,
- ry, rm, rd,
- rh, rmin, rs,
- HAVE_JD | HAVE_CIVIL | HAVE_TIME);
+ set_to_complex(self, dat,
+ nth, rjd2,
+ 0, INT2FIX(0),
+ rof, sg,
+ ry, rm, rd,
+ rh, rmin, rs,
+ HAVE_JD | HAVE_CIVIL | HAVE_TIME);
}
+ ret = self;
add_frac();
return ret;
}
@@ -7403,7 +7620,7 @@ datetime_s_civil(int argc, VALUE *argv, VALUE klass)
* call-seq:
* DateTime.commercial([cwyear=-4712[, cweek=1[, cwday=1[, hour=0[, minute=0[, second=0[, offset=0[, start=Date::ITALY]]]]]]]]) -> datetime
*
- * Creates a date-time object denoting the given week date.
+ * Creates a DateTime object denoting the given week date.
*
* DateTime.commercial(2001) #=> #<DateTime: 2001-01-01T00:00:00+00:00 ...>
* DateTime.commercial(2002) #=> #<DateTime: 2001-12-31T00:00:00+00:00 ...>
@@ -7620,7 +7837,7 @@ datetime_s_nth_kday(int argc, VALUE *argv, VALUE klass)
* call-seq:
* DateTime.now([start=Date::ITALY]) -> datetime
*
- * Creates a date-time object denoting the present time.
+ * Creates a DateTime object denoting the present time.
*
* DateTime.now #=> #<DateTime: 2011-06-11T21:20:44+09:00 ...>
*/
@@ -7669,8 +7886,8 @@ datetime_s_now(int argc, VALUE *argv, VALUE klass)
s = 59;
#ifdef HAVE_STRUCT_TM_TM_GMTOFF
of = tm.tm_gmtoff;
-#elif defined(HAVE_VAR_TIMEZONE)
-#ifdef HAVE_VAR_ALTZONE
+#elif defined(HAVE_TIMEZONE)
+#ifdef HAVE_ALTZONE
of = (long)-((tm.tm_isdst > 0) ? altzone : timezone);
#else
of = (long)-timezone;
@@ -7823,7 +8040,7 @@ dt_new_by_frags(VALUE klass, VALUE hash, VALUE sg)
* template, and returns a hash of parsed elements. _strptime does
* not support specification of flags and width unlike strftime.
*
- * See also strptime(3) and strftime.
+ * See also strptime(3) and #strftime.
*/
static VALUE
datetime_s__strptime(int argc, VALUE *argv, VALUE klass)
@@ -7833,10 +8050,10 @@ datetime_s__strptime(int argc, VALUE *argv, VALUE klass)
/*
* call-seq:
- * DateTime.strptime([string='-4712-01-01T00:00:00+00:00'[, format='%FT%T%z'[ ,start=ITALY]]]) -> datetime
+ * DateTime.strptime([string='-4712-01-01T00:00:00+00:00'[, format='%FT%T%z'[ ,start=Date::ITALY]]]) -> datetime
*
* Parses the given representation of date and time with the given
- * template, and creates a date object. strptime does not support
+ * template, and creates a DateTime object. strptime does not support
* specification of flags and width unlike strftime.
*
* DateTime.strptime('2001-02-03T04:05:06+07:00', '%Y-%m-%dT%H:%M:%S%z')
@@ -7856,7 +8073,7 @@ datetime_s__strptime(int argc, VALUE *argv, VALUE klass)
* DateTime.strptime('sat3feb014pm+7', '%a%d%b%y%H%p%z')
* #=> #<DateTime: 2001-02-03T16:00:00+07:00 ...>
*
- * See also strptime(3) and strftime.
+ * See also strptime(3) and #strftime.
*/
static VALUE
datetime_s_strptime(int argc, VALUE *argv, VALUE klass)
@@ -7886,10 +8103,10 @@ datetime_s_strptime(int argc, VALUE *argv, VALUE klass)
/*
* call-seq:
- * DateTime.parse(string='-4712-01-01T00:00:00+00:00'[, comp=true[, start=ITALY]]) -> datetime
+ * DateTime.parse(string='-4712-01-01T00:00:00+00:00'[, comp=true[, start=Date::ITALY]], limit: 128) -> datetime
*
* Parses the given representation of date and time, and creates a
- * date object. This method does not function as a validator.
+ * DateTime object. This method does not function as a validator.
*
* If the optional second argument is true and the detected year is in
* the range "00" to "99", makes it full.
@@ -7900,13 +8117,18 @@ datetime_s_strptime(int argc, VALUE *argv, VALUE klass)
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
* DateTime.parse('3rd Feb 2001 04:05:06 PM')
* #=> #<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.
*/
static VALUE
datetime_s_parse(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, comp, sg;
+ VALUE str, comp, sg, opt;
- rb_scan_args(argc, argv, "03", &str, &comp, &sg);
+ rb_scan_args(argc, argv, "03:", &str, &comp, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -7918,20 +8140,22 @@ datetime_s_parse(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE argv2[2], hash;
-
- argv2[0] = str;
- argv2[1] = comp;
- hash = date_s__parse(2, argv2, klass);
+ int argc2 = 2;
+ VALUE argv2[3];
+ argv2[0] = str;
+ argv2[1] = comp;
+ argv2[2] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__parse(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.iso8601(string='-4712-01-01T00:00:00+00:00'[, start=ITALY]) -> datetime
+ * DateTime.iso8601(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime
*
- * Creates a new Date object by parsing from a string according to
+ * Creates a new DateTime object by parsing from a string according to
* some typical ISO 8601 formats.
*
* DateTime.iso8601('2001-02-03T04:05:06+07:00')
@@ -7940,13 +8164,18 @@ datetime_s_parse(int argc, VALUE *argv, VALUE klass)
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
* DateTime.iso8601('2001-W05-6T04:05:06+07:00')
* #=> #<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.
*/
static VALUE
datetime_s_iso8601(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -7956,27 +8185,37 @@ datetime_s_iso8601(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__iso8601(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2--;
+ VALUE hash = date_s__iso8601(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=ITALY]) -> datetime
+ * DateTime.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime
*
- * Creates a new Date object by parsing from a string according to
+ * Creates a new DateTime object by parsing from a string according to
* some typical RFC 3339 formats.
*
* DateTime.rfc3339('2001-02-03T04:05:06+07:00')
* #=> #<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.
*/
static VALUE
datetime_s_rfc3339(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -7986,27 +8225,37 @@ datetime_s_rfc3339(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__rfc3339(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__rfc3339(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.xmlschema(string='-4712-01-01T00:00:00+00:00'[, start=ITALY]) -> datetime
+ * DateTime.xmlschema(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime
*
- * Creates a new Date object by parsing from a string according to
+ * Creates a new DateTime object by parsing from a string according to
* some typical XML Schema formats.
*
* DateTime.xmlschema('2001-02-03T04:05:06+07:00')
* #=> #<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.
*/
static VALUE
datetime_s_xmlschema(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8016,28 +8265,38 @@ datetime_s_xmlschema(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__xmlschema(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__xmlschema(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=ITALY]) -> datetime
- * DateTime.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=ITALY]) -> datetime
+ * DateTime.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> datetime
+ * DateTime.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> datetime
*
- * Creates a new Date object by parsing from a string according to
+ * Creates a new DateTime object by parsing from a string according to
* some typical RFC 2822 formats.
*
* DateTime.rfc2822('Sat, 3 Feb 2001 04:05:06 +0700')
* #=> #<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.
*/
static VALUE
datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8047,27 +8306,37 @@ datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__rfc2822(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__rfc2822(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.httpdate(string='Mon, 01 Jan -4712 00:00:00 GMT'[, start=ITALY]) -> datetime
+ * DateTime.httpdate(string='Mon, 01 Jan -4712 00:00:00 GMT'[, start=Date::ITALY]) -> datetime
*
- * Creates a new Date object by parsing from a string according to
+ * Creates a new DateTime object by parsing from a string according to
* some RFC 2616 format.
*
* DateTime.httpdate('Sat, 03 Feb 2001 04:05:06 GMT')
* #=> #<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.
*/
static VALUE
datetime_s_httpdate(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8077,27 +8346,42 @@ datetime_s_httpdate(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__httpdate(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__httpdate(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.jisx0301(string='-4712-01-01T00:00:00+00:00'[, start=ITALY]) -> datetime
+ * DateTime.jisx0301(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime
*
- * Creates a new Date object by parsing from a string according to
+ * Creates a new DateTime object by parsing from a string according to
* some typical JIS X 0301 formats.
*
* DateTime.jisx0301('H13.02.03T04:05:06+07:00')
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
+ *
+ * For no-era year, legacy format, Heisei is assumed.
+ *
+ * DateTime.jisx0301('13.02.03T04:05:06+07:00')
+ * #=> #<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.
*/
static VALUE
datetime_s_jisx0301(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8107,7 +8391,12 @@ datetime_s_jisx0301(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__jisx0301(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__jisx0301(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
@@ -8116,8 +8405,8 @@ datetime_s_jisx0301(int argc, VALUE *argv, VALUE klass)
* call-seq:
* dt.to_s -> string
*
- * Returns a string in an ISO 8601 format (This method doesn't use the
- * expanded representations).
+ * Returns a string in an ISO 8601 format. (This method doesn't use the
+ * expanded representations.)
*
* DateTime.new(2001,2,3,4,5,6,'-7').to_s
* #=> "2001-02-03T04:05:06-07:00"
@@ -8134,13 +8423,13 @@ dt_lite_to_s(VALUE self)
*
* Formats date according to the directives in the given format
* string.
- * The directives begins with a percent (%) character.
+ * The directives begin with a percent (%) character.
* Any text not listed as a directive will be passed through to the
* output string.
*
- * The directive consists of a percent (%) character,
- * zero or more flags, optional minimum field width,
- * optional modifier and a conversion specifier
+ * A directive consists of a percent (%) character,
+ * zero or more flags, an optional minimum field width,
+ * an optional modifier, and a conversion specifier
* as follows.
*
* %<flags><width><modifier><conversion>
@@ -8155,7 +8444,7 @@ dt_lite_to_s(VALUE self)
*
* The minimum field width specifies the minimum width.
*
- * The modifier is "E" and "O".
+ * The modifiers are "E" and "O".
* They are ignored.
*
* Format directives:
@@ -8191,7 +8480,7 @@ dt_lite_to_s(VALUE self)
*
* %M - Minute of the hour (00..59)
*
- * %S - Second of the minute (00..59)
+ * %S - Second of the minute (00..60)
*
* %L - Millisecond of the second (000..999)
* %N - Fractional seconds digits, default is 9 digits (nanosecond)
@@ -8206,7 +8495,7 @@ dt_lite_to_s(VALUE self)
* %::z - hour, minute and second offset from UTC (e.g. +09:00:00)
* %:::z - hour, minute and second offset from UTC
* (e.g. +09, +09:30, +09:30:30)
- * %Z - Time zone abbreviation name or something similar information.
+ * %Z - Equivalent to %:z (e.g. +09:00)
*
* Weekday:
* %A - The full weekday name (``Sunday'')
@@ -8251,11 +8540,12 @@ dt_lite_to_s(VALUE self)
* %T - 24-hour time (%H:%M:%S)
* %+ - date(1) (%a %b %e %H:%M:%S %Z %Y)
*
- * This method is similar to strftime() function defined in ISO C and POSIX.
+ * This method is similar to the strftime() function defined in ISO C
+ * and POSIX.
* Several directives (%a, %A, %b, %B, %c, %p, %r, %x, %X, %E*, %O* and %Z)
* are locale dependent in the function.
- * However this method is locale independent.
- * So, the result may differ even if a same format string is used in other
+ * However, this method is locale independent.
+ * So, the result may differ even if the same format string is used in other
* systems such as C.
* It is good practice to avoid %x and %X because there are corresponding
* locale independent representations, %D and %T.
@@ -8303,7 +8593,7 @@ dt_lite_to_s(VALUE self)
* %GW%V%uT%H%M%z => 2007W471T0837-0600 Week date and local time and difference from UTC (basic)
* %G-W%V-%uT%R%:z => 2007-W47-1T08:37-06:00 Week date and local time and difference from UTC (extended)
*
- * See also strftime(3) and strptime.
+ * See also strftime(3) and ::strptime.
*/
static VALUE
dt_lite_strftime(int argc, VALUE *argv, VALUE self)
@@ -8333,8 +8623,8 @@ iso8601_timediv(VALUE self, long n)
* dt.iso8601([n=0]) -> string
* dt.xmlschema([n=0]) -> string
*
- * This method is equivalent to strftime('%FT%T'). The optional
- * argument n is length of fractional seconds.
+ * This method is equivalent to strftime('%FT%T%:z').
+ * The optional argument +n+ is the number of digits for fractional seconds.
*
* DateTime.parse('2001-02-03T04:05:06.123456789+07:00').iso8601(9)
* #=> "2001-02-03T04:05:06.123456789+07:00"
@@ -8356,8 +8646,8 @@ dt_lite_iso8601(int argc, VALUE *argv, VALUE self)
* call-seq:
* dt.rfc3339([n=0]) -> string
*
- * This method is equivalent to strftime('%FT%T'). The optional
- * argument n is length of fractional seconds.
+ * This method is equivalent to strftime('%FT%T%:z').
+ * The optional argument +n+ is the number of digits for fractional seconds.
*
* DateTime.parse('2001-02-03T04:05:06.123456789+07:00').rfc3339(9)
* #=> "2001-02-03T04:05:06.123456789+07:00"
@@ -8372,8 +8662,8 @@ dt_lite_rfc3339(int argc, VALUE *argv, VALUE self)
* call-seq:
* dt.jisx0301([n=0]) -> string
*
- * Returns a string in a JIS X 0301 format. The optional argument n
- * is length of fractional seconds.
+ * Returns a string in a JIS X 0301 format.
+ * The optional argument +n+ is the number of digits for fractional seconds.
*
* DateTime.parse('2001-02-03T04:05:06.123456789+07:00').jisx0301(9)
* #=> "H13.02.03T04:05:06.123456789+07:00"
@@ -8603,7 +8893,7 @@ datetime_to_date(VALUE self)
VALUE new = d_lite_s_alloc_simple(cDate);
{
get_d1b(new);
- copy_complex_to_simple(new, &bdat->s, &adat->c)
+ copy_complex_to_simple(new, &bdat->s, &adat->c);
bdat->s.jd = m_local_jd(adat);
bdat->s.flags &= ~(HAVE_DF | HAVE_TIME | COMPLEX_DAT);
return new;
@@ -8968,14 +9258,18 @@ mk_ary_of_str(long len, const char *a[])
return o;
}
+static VALUE
+d_lite_zero(VALUE x)
+{
+ return INT2FIX(0);
+}
+
void
Init_date_core(void)
{
#undef rb_intern
#define rb_intern(str) rb_intern_const(str)
- assert(fprintf(stderr, "assert() is now active\n"));
-
id_cmp = rb_intern("<=>");
id_le_p = rb_intern("<=");
id_ge_p = rb_intern(">=");
@@ -9003,13 +9297,13 @@ Init_date_core(void)
/*
* date and datetime class - Tadayoshi Funaba 1998-2011
*
- * 'date' provides two classes Date and DateTime.
+ * 'date' provides two classes: Date and DateTime.
*
- * == Terms and definitions
+ * == Terms and Definitions
*
* Some terms and definitions are based on ISO 8601 and JIS X 0301.
*
- * === calendar date
+ * === Calendar Date
*
* The calendar date is a particular day of a calendar year,
* identified by its ordinal number within a calendar month within
@@ -9017,14 +9311,14 @@ Init_date_core(void)
*
* In those classes, this is so-called "civil".
*
- * === ordinal date
+ * === Ordinal Date
*
* The ordinal date is a particular day of a calendar year identified
* by its ordinal number within the year.
*
* In those classes, this is so-called "ordinal".
*
- * === week date
+ * === Week Date
*
* The week date is a date identified by calendar week and day numbers.
*
@@ -9034,15 +9328,15 @@ Init_date_core(void)
* includes the first Thursday of that year. In the Gregorian
* calendar, this is equivalent to the week which includes January 4.
*
- * In those classes, this so-called "commercial".
+ * In those classes, this is so-called "commercial".
*
- * === julian day number
+ * === Julian Day Number
*
- * The Julian day number is in elapsed days since noon (Greenwich mean
- * time) on January 1, 4713 BCE (in the Julian calendar).
+ * The Julian day number is in elapsed days since noon (Greenwich Mean
+ * Time) on January 1, 4713 BCE (in the Julian calendar).
*
- * In this document, the astronomical Julian day number is same as the
- * original Julian day number. And the chronological Julian day
+ * 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.
*
@@ -9052,14 +9346,14 @@ Init_date_core(void)
*
* In those classes, those are so-called "ajd" and "jd".
*
- * === modified julian day number
+ * === Modified Julian Day Number
*
* The modified Julian day number is in elapsed days since midnight
- * (Coordinated universal time) on November 17, 1858 CE (in the
+ * (Coordinated Universal Time) on November 17, 1858 CE (in the
* Gregorian calendar).
*
* In this document, the astronomical modified Julian day number is
- * same as the original modified Julian day number. And the
+ * 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.
@@ -9068,16 +9362,16 @@ Init_date_core(void)
* appears, it just refers to "chronological modified Julian day
* number", not the original.
*
- * In those classes, this is so-called "mjd".
+ * In those classes, those are so-called "amjd" and "mjd".
*
* == Date
*
- * A subclass of Object that includes Comparable module and easily handles
- * date.
+ * A subclass of Object that includes the Comparable module and
+ * easily handles date.
*
- * Date object is created with Date::new, Date::jd, Date::ordinal,
+ * A Date object is created with Date::new, Date::jd, Date::ordinal,
* Date::commercial, Date::parse, Date::strptime, Date::today,
- * Time#to_date or etc.
+ * Time#to_date, etc.
*
* require 'date'
*
@@ -9098,7 +9392,7 @@ Init_date_core(void)
*
* All date objects are immutable; hence cannot modify themselves.
*
- * The concept of this date object can be represented as a tuple
+ * 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
@@ -9113,10 +9407,11 @@ Init_date_core(void)
* The offset in this class is usually zero, and cannot be
* specified directly.
*
- * An optional argument the day of calendar reform (start) as a
- * Julian day number, which should be 2298874 to 2426355 or -/+oo.
- * The default value is Date::ITALY (2299161=1582-10-15). See
- * also sample/cal.rb.
+ * 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
@@ -9133,7 +9428,7 @@ Init_date_core(void)
* 17 18 19 20 21 22 23
* 24 25 26 27 28 29 30
*
- * Date object has various methods. See each reference.
+ * A Date object has various methods. See each reference.
*
* d = Date.parse('3rd Feb 2001')
* #=> #<Date: 2001-02-03 ...>
@@ -9181,32 +9476,31 @@ Init_date_core(void)
rb_define_const(cDate, "ENGLAND", INT2FIX(ENGLAND));
/* The Julian day number of the day of calendar reform for the
- * proleptic Julian calendar
+ * proleptic Julian calendar.
*/
rb_define_const(cDate, "JULIAN", DBL2NUM(JULIAN));
/* The Julian day number of the day of calendar reform for the
- * proleptic Gregorian calendar
+ * proleptic Gregorian calendar.
*/
rb_define_const(cDate, "GREGORIAN", DBL2NUM(GREGORIAN));
- rb_define_alloc_func(cDate, d_lite_s_alloc);
+ rb_define_alloc_func(cDate, d_lite_s_alloc_simple);
#ifndef NDEBUG
-#define de_define_private_method rb_define_private_method
- de_define_private_method(CLASS_OF(cDate), "_valid_jd?",
+ rb_define_private_method(CLASS_OF(cDate), "_valid_jd?",
date_s__valid_jd_p, -1);
- de_define_private_method(CLASS_OF(cDate), "_valid_ordinal?",
+ rb_define_private_method(CLASS_OF(cDate), "_valid_ordinal?",
date_s__valid_ordinal_p, -1);
- de_define_private_method(CLASS_OF(cDate), "_valid_civil?",
+ rb_define_private_method(CLASS_OF(cDate), "_valid_civil?",
date_s__valid_civil_p, -1);
- de_define_private_method(CLASS_OF(cDate), "_valid_date?",
+ rb_define_private_method(CLASS_OF(cDate), "_valid_date?",
date_s__valid_civil_p, -1);
- de_define_private_method(CLASS_OF(cDate), "_valid_commercial?",
+ rb_define_private_method(CLASS_OF(cDate), "_valid_commercial?",
date_s__valid_commercial_p, -1);
- de_define_private_method(CLASS_OF(cDate), "_valid_weeknum?",
+ rb_define_private_method(CLASS_OF(cDate), "_valid_weeknum?",
date_s__valid_weeknum_p, -1);
- de_define_private_method(CLASS_OF(cDate), "_valid_nth_kday?",
+ rb_define_private_method(CLASS_OF(cDate), "_valid_nth_kday?",
date_s__valid_nth_kday_p, -1);
#endif
@@ -9219,11 +9513,11 @@ Init_date_core(void)
date_s_valid_commercial_p, -1);
#ifndef NDEBUG
- de_define_private_method(CLASS_OF(cDate), "valid_weeknum?",
+ rb_define_private_method(CLASS_OF(cDate), "valid_weeknum?",
date_s_valid_weeknum_p, -1);
- de_define_private_method(CLASS_OF(cDate), "valid_nth_kday?",
+ rb_define_private_method(CLASS_OF(cDate), "valid_nth_kday?",
date_s_valid_nth_kday_p, -1);
- de_define_private_method(CLASS_OF(cDate), "zone_to_diff",
+ rb_define_private_method(CLASS_OF(cDate), "zone_to_diff",
date_s_zone_to_diff, 1);
#endif
@@ -9234,21 +9528,18 @@ Init_date_core(void)
date_s_gregorian_leap_p, 1);
#ifndef NDEBUG
-#define de_define_singleton_method rb_define_singleton_method
-#define de_define_alias rb_define_alias
- de_define_singleton_method(cDate, "new!", date_s_new_bang, -1);
- de_define_alias(rb_singleton_class(cDate), "new_l!", "new");
+ rb_define_singleton_method(cDate, "new!", date_s_new_bang, -1);
+ rb_define_alias(rb_singleton_class(cDate), "new_l!", "new");
#endif
rb_define_singleton_method(cDate, "jd", date_s_jd, -1);
rb_define_singleton_method(cDate, "ordinal", date_s_ordinal, -1);
rb_define_singleton_method(cDate, "civil", date_s_civil, -1);
- rb_define_singleton_method(cDate, "new", date_s_civil, -1);
rb_define_singleton_method(cDate, "commercial", date_s_commercial, -1);
#ifndef NDEBUG
- de_define_singleton_method(cDate, "weeknum", date_s_weeknum, -1);
- de_define_singleton_method(cDate, "nth_kday", date_s_nth_kday, -1);
+ rb_define_singleton_method(cDate, "weeknum", date_s_weeknum, -1);
+ rb_define_singleton_method(cDate, "nth_kday", date_s_nth_kday, -1);
#endif
rb_define_singleton_method(cDate, "today", date_s_today, -1);
@@ -9256,29 +9547,26 @@ Init_date_core(void)
rb_define_singleton_method(cDate, "strptime", date_s_strptime, -1);
rb_define_singleton_method(cDate, "_parse", date_s__parse, -1);
rb_define_singleton_method(cDate, "parse", date_s_parse, -1);
- rb_define_singleton_method(cDate, "_iso8601", date_s__iso8601, 1);
+ rb_define_singleton_method(cDate, "_iso8601", date_s__iso8601, -1);
rb_define_singleton_method(cDate, "iso8601", date_s_iso8601, -1);
- rb_define_singleton_method(cDate, "_rfc3339", date_s__rfc3339, 1);
+ rb_define_singleton_method(cDate, "_rfc3339", date_s__rfc3339, -1);
rb_define_singleton_method(cDate, "rfc3339", date_s_rfc3339, -1);
- rb_define_singleton_method(cDate, "_xmlschema", date_s__xmlschema, 1);
+ rb_define_singleton_method(cDate, "_xmlschema", date_s__xmlschema, -1);
rb_define_singleton_method(cDate, "xmlschema", date_s_xmlschema, -1);
- rb_define_singleton_method(cDate, "_rfc2822", date_s__rfc2822, 1);
- rb_define_singleton_method(cDate, "_rfc822", date_s__rfc2822, 1);
+ rb_define_singleton_method(cDate, "_rfc2822", date_s__rfc2822, -1);
+ rb_define_singleton_method(cDate, "_rfc822", date_s__rfc2822, -1);
rb_define_singleton_method(cDate, "rfc2822", date_s_rfc2822, -1);
rb_define_singleton_method(cDate, "rfc822", date_s_rfc2822, -1);
- rb_define_singleton_method(cDate, "_httpdate", date_s__httpdate, 1);
+ rb_define_singleton_method(cDate, "_httpdate", date_s__httpdate, -1);
rb_define_singleton_method(cDate, "httpdate", date_s_httpdate, -1);
- rb_define_singleton_method(cDate, "_jisx0301", date_s__jisx0301, 1);
+ rb_define_singleton_method(cDate, "_jisx0301", date_s__jisx0301, -1);
rb_define_singleton_method(cDate, "jisx0301", date_s_jisx0301, -1);
-#ifndef NDEBUG
-#define de_define_method rb_define_method
- de_define_method(cDate, "initialize", d_lite_initialize, -1);
-#endif
+ rb_define_method(cDate, "initialize", date_initialize, -1);
rb_define_method(cDate, "initialize_copy", d_lite_initialize_copy, 1);
#ifndef NDEBUG
- de_define_method(cDate, "fill", d_lite_fill, 0);
+ rb_define_method(cDate, "fill", d_lite_fill, 0);
#endif
rb_define_method(cDate, "ajd", d_lite_ajd, 0);
@@ -9300,8 +9588,8 @@ Init_date_core(void)
rb_define_method(cDate, "cwday", d_lite_cwday, 0);
#ifndef NDEBUG
- de_define_private_method(cDate, "wnum0", d_lite_wnum0, 0);
- de_define_private_method(cDate, "wnum1", d_lite_wnum1, 0);
+ rb_define_private_method(cDate, "wnum0", d_lite_wnum0, 0);
+ rb_define_private_method(cDate, "wnum1", d_lite_wnum1, 0);
#endif
rb_define_method(cDate, "wday", d_lite_wday, 0);
@@ -9315,18 +9603,14 @@ Init_date_core(void)
rb_define_method(cDate, "saturday?", d_lite_saturday_p, 0);
#ifndef NDEBUG
- de_define_method(cDate, "nth_kday?", d_lite_nth_kday_p, 2);
+ rb_define_method(cDate, "nth_kday?", d_lite_nth_kday_p, 2);
#endif
- rb_define_private_method(cDate, "hour", d_lite_hour, 0);
- rb_define_private_method(cDate, "min", d_lite_min, 0);
- rb_define_private_method(cDate, "minute", d_lite_min, 0);
- rb_define_private_method(cDate, "sec", d_lite_sec, 0);
- rb_define_private_method(cDate, "second", d_lite_sec, 0);
- rb_define_private_method(cDate, "sec_fraction", d_lite_sec_fraction, 0);
- rb_define_private_method(cDate, "second_fraction", d_lite_sec_fraction, 0);
- rb_define_private_method(cDate, "offset", d_lite_offset, 0);
- rb_define_private_method(cDate, "zone", d_lite_zone, 0);
+ rb_define_private_method(cDate, "hour", d_lite_zero, 0);
+ rb_define_private_method(cDate, "min", d_lite_zero, 0);
+ rb_define_private_method(cDate, "minute", d_lite_zero, 0);
+ rb_define_private_method(cDate, "sec", d_lite_zero, 0);
+ rb_define_private_method(cDate, "second", d_lite_zero, 0);
rb_define_method(cDate, "julian?", d_lite_julian_p, 0);
rb_define_method(cDate, "gregorian?", d_lite_gregorian_p, 0);
@@ -9339,8 +9623,6 @@ Init_date_core(void)
rb_define_method(cDate, "julian", d_lite_julian, 0);
rb_define_method(cDate, "gregorian", d_lite_gregorian, 0);
- rb_define_private_method(cDate, "new_offset", d_lite_new_offset, -1);
-
rb_define_method(cDate, "+", d_lite_plus, 1);
rb_define_method(cDate, "-", d_lite_minus, 1);
@@ -9368,7 +9650,7 @@ Init_date_core(void)
rb_define_method(cDate, "to_s", d_lite_to_s, 0);
#ifndef NDEBUG
- de_define_method(cDate, "inspect_raw", d_lite_inspect_raw, 0);
+ rb_define_method(cDate, "inspect_raw", d_lite_inspect_raw, 0);
#endif
rb_define_method(cDate, "inspect", d_lite_inspect, 0);
@@ -9385,7 +9667,7 @@ Init_date_core(void)
rb_define_method(cDate, "jisx0301", d_lite_jisx0301, 0);
#ifndef NDEBUG
- de_define_method(cDate, "marshal_dump_old", d_lite_marshal_dump_old, 0);
+ rb_define_method(cDate, "marshal_dump_old", d_lite_marshal_dump_old, 0);
#endif
rb_define_method(cDate, "marshal_dump", d_lite_marshal_dump, 0);
rb_define_method(cDate, "marshal_load", d_lite_marshal_load, 1);
@@ -9394,48 +9676,49 @@ Init_date_core(void)
/*
* == DateTime
*
- * A subclass of Date that easily handles date, hour, minute, second and
- * offset.
+ * A subclass of Date that easily handles date, hour, minute, second,
+ * and offset.
*
* DateTime does not consider any leap seconds, does not track
* any summer time rules.
*
- * DateTime object is created with DateTime::new, DateTime::jd,
+ * A DateTime object is created with DateTime::new, DateTime::jd,
* DateTime::ordinal, DateTime::commercial, DateTime::parse,
- * DateTime::strptime, DateTime::now, Time#to_datetime or etc.
+ * DateTime::strptime, DateTime::now, Time#to_datetime, etc.
*
* require 'date'
*
* DateTime.new(2001,2,3,4,5,6)
* #=> #<DateTime: 2001-02-03T04:05:06+00:00 ...>
*
- * The last element of day, hour, minute or second can be
+ * The last element of day, hour, minute, or second can be a
* fractional number. The fractional number's precision is assumed
* at most nanosecond.
*
* DateTime.new(2001,2,3.5)
* #=> #<DateTime: 2001-02-03T12:00:00+00:00 ...>
*
- * An optional argument the offset indicates the difference
+ * An optional argument, the offset, indicates the difference
* between the local time and UTC. For example, <tt>Rational(3,24)</tt>
* represents ahead of 3 hours of UTC, <tt>Rational(-5,24)</tt> represents
* behind of 5 hours of UTC. The offset should be -1 to +1, and
* its precision is assumed at most second. The default value is
- * zero(equals to UTC).
+ * zero (equals to UTC).
*
* DateTime.new(2001,2,3,4,5,6,Rational(3,24))
* #=> #<DateTime: 2001-02-03T04:05:06+03:00 ...>
*
- * also accepts string form.
+ * The offset also accepts string form:
*
* DateTime.new(2001,2,3,4,5,6,'+03:00')
* #=> #<DateTime: 2001-02-03T04:05:06+03:00 ...>
*
- * An optional argument the day of calendar reform (start) denotes
+ * An optional argument, the day of calendar reform (+start+), denotes
* a Julian day number, which should be 2298874 to 2426355 or
- * -/+oo. The default value is +Date::ITALY+ (2299161=1582-10-15).
+ * negative/positive infinity.
+ * The default value is +Date::ITALY+ (2299161=1582-10-15).
*
- * DateTime object has various methods. See each reference.
+ * A DateTime object has various methods. See each reference.
*
* d = DateTime.parse('3rd Feb 2001 04:05:06+03:30')
* #=> #<DateTime: 2001-02-03T04:05:06+03:30 ...>
@@ -9462,22 +9745,22 @@ Init_date_core(void)
* died on the same day in history -
* so much so that UNESCO named April 23 as
* {World Book Day because of this fact}[http://en.wikipedia.org/wiki/World_Book_Day].
- * However because England hadn't yet adopted
+ * However, because England hadn't yet adopted the
* {Gregorian Calendar Reform}[http://en.wikipedia.org/wiki/Gregorian_calendar#Gregorian_reform]
* (and wouldn't until {1752}[http://en.wikipedia.org/wiki/Calendar_(New_Style)_Act_1750])
* their deaths are actually 10 days apart.
* Since Ruby's Time class implements a
* {proleptic Gregorian calendar}[http://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar]
- * and has no concept of calendar reform then there's no way
- * to express this. This is where DateTime steps in:
+ * and has no concept of calendar reform there's no way
+ * to express this with Time objects. This is where DateTime steps in:
*
* shakespeare = DateTime.iso8601('1616-04-23', Date::ENGLAND)
* #=> Tue, 23 Apr 1616 00:00:00 +0000
* cervantes = DateTime.iso8601('1616-04-23', Date::ITALY)
* #=> Sat, 23 Apr 1616 00:00:00 +0000
*
- * Already you can see something's weird - the days of the week
- * are different, taking this further:
+ * Already you can see something is weird - the days of the week
+ * are different. Taking this further:
*
* cervantes == shakespeare
* #=> false
@@ -9531,6 +9814,7 @@ Init_date_core(void)
*/
cDateTime = rb_define_class("DateTime", cDate);
+ rb_define_alloc_func(cDateTime, d_lite_s_alloc_complex);
rb_define_singleton_method(cDateTime, "jd", datetime_s_jd, -1);
rb_define_singleton_method(cDateTime, "ordinal", datetime_s_ordinal, -1);
@@ -9540,9 +9824,9 @@ Init_date_core(void)
datetime_s_commercial, -1);
#ifndef NDEBUG
- de_define_singleton_method(cDateTime, "weeknum",
+ rb_define_singleton_method(cDateTime, "weeknum",
datetime_s_weeknum, -1);
- de_define_singleton_method(cDateTime, "nth_kday",
+ rb_define_singleton_method(cDateTime, "nth_kday",
datetime_s_nth_kday, -1);
#endif
@@ -9570,19 +9854,16 @@ Init_date_core(void)
rb_define_singleton_method(cDateTime, "jisx0301",
datetime_s_jisx0301, -1);
-#define f_public(m,s) rb_funcall(m, rb_intern("public"), 1,\
- ID2SYM(rb_intern(s)))
-
- f_public(cDateTime, "hour");
- f_public(cDateTime, "min");
- f_public(cDateTime, "minute");
- f_public(cDateTime, "sec");
- f_public(cDateTime, "second");
- f_public(cDateTime, "sec_fraction");
- f_public(cDateTime, "second_fraction");
- f_public(cDateTime, "offset");
- f_public(cDateTime, "zone");
- f_public(cDateTime, "new_offset");
+ rb_define_method(cDateTime, "hour", d_lite_hour, 0);
+ rb_define_method(cDateTime, "min", d_lite_min, 0);
+ rb_define_method(cDateTime, "minute", d_lite_min, 0);
+ rb_define_method(cDateTime, "sec", d_lite_sec, 0);
+ rb_define_method(cDateTime, "second", d_lite_sec, 0);
+ rb_define_method(cDateTime, "sec_fraction", d_lite_sec_fraction, 0);
+ rb_define_method(cDateTime, "second_fraction", d_lite_sec_fraction, 0);
+ rb_define_method(cDateTime, "offset", d_lite_offset, 0);
+ rb_define_method(cDateTime, "zone", d_lite_zone, 0);
+ rb_define_method(cDateTime, "new_offset", d_lite_new_offset, -1);
rb_define_method(cDateTime, "to_s", dt_lite_to_s, 0);
@@ -9610,15 +9891,15 @@ Init_date_core(void)
#ifndef NDEBUG
/* tests */
- de_define_singleton_method(cDate, "test_civil", date_s_test_civil, 0);
- de_define_singleton_method(cDate, "test_ordinal", date_s_test_ordinal, 0);
- de_define_singleton_method(cDate, "test_commercial",
+ rb_define_singleton_method(cDate, "test_civil", date_s_test_civil, 0);
+ rb_define_singleton_method(cDate, "test_ordinal", date_s_test_ordinal, 0);
+ rb_define_singleton_method(cDate, "test_commercial",
date_s_test_commercial, 0);
- de_define_singleton_method(cDate, "test_weeknum", date_s_test_weeknum, 0);
- de_define_singleton_method(cDate, "test_nth_kday", date_s_test_nth_kday, 0);
- de_define_singleton_method(cDate, "test_unit_conv",
+ rb_define_singleton_method(cDate, "test_weeknum", date_s_test_weeknum, 0);
+ rb_define_singleton_method(cDate, "test_nth_kday", date_s_test_nth_kday, 0);
+ rb_define_singleton_method(cDate, "test_unit_conv",
date_s_test_unit_conv, 0);
- de_define_singleton_method(cDate, "test_all", date_s_test_all, 0);
+ rb_define_singleton_method(cDate, "test_all", date_s_test_all, 0);
#endif
}
diff --git a/ext/date/date_parse.c b/ext/date/date_parse.c
index b74230d291..f06c07bae4 100644
--- a/ext/date/date_parse.c
+++ b/ext/date/date_parse.c
@@ -40,9 +40,9 @@ RUBY_EXTERN unsigned long ruby_scan_digits(const char *str, ssize_t len, int bas
#define f_sub_bang(s,r,x) rb_funcall(s, rb_intern("sub!"), 2, r, x)
#define f_gsub_bang(s,r,x) rb_funcall(s, rb_intern("gsub!"), 2, r, x)
-#define set_hash(k,v) rb_hash_aset(hash, ID2SYM(rb_intern(k)), v)
-#define ref_hash(k) rb_hash_aref(hash, ID2SYM(rb_intern(k)))
-#define del_hash(k) rb_hash_delete(hash, ID2SYM(rb_intern(k)))
+#define set_hash(k,v) rb_hash_aset(hash, ID2SYM(rb_intern(k"")), v)
+#define ref_hash(k) rb_hash_aref(hash, ID2SYM(rb_intern(k"")))
+#define del_hash(k) rb_hash_delete(hash, ID2SYM(rb_intern(k"")))
#define cstr2num(s) rb_cstr_to_inum(s, 10, 0)
#define str2num(s) rb_str_to_inum(s, 10, 0)
@@ -66,7 +66,13 @@ static const char abbr_months[][4] = {
#define asubt_string() rb_str_new("\024", 1)
#endif
-#define DECDIGIT "0123456789"
+static size_t
+digit_span(const char *s, const char *e)
+{
+ size_t i = 0;
+ while (s + i < e && isdigit(s[i])) i++;
+ return i;
+}
static void
s3e(VALUE hash, VALUE y, VALUE m, VALUE d, int bc)
@@ -92,7 +98,7 @@ s3e(VALUE hash, VALUE y, VALUE m, VALUE d, int bc)
y = d;
d = Qnil;
}
- if (!NIL_P(d) && *RSTRING_PTR(d) == '\'') {
+ if (!NIL_P(d) && RSTRING_LEN(d) > 0 && *RSTRING_PTR(d) == '\'') {
y = d;
d = Qnil;
}
@@ -103,17 +109,20 @@ s3e(VALUE hash, VALUE y, VALUE m, VALUE d, int bc)
size_t l;
s = RSTRING_PTR(y);
- while (!issign((unsigned char)*s) && !isdigit((unsigned char)*s))
+ ep = RSTRING_END(y);
+ while (s < ep && !issign(*s) && !isdigit(*s))
s++;
+ if (s >= ep) goto no_date;
bp = s;
if (issign((unsigned char)*s))
s++;
- l = strspn(s, DECDIGIT);
+ l = digit_span(s, ep);
ep = s + l;
if (*ep) {
y = d;
d = rb_str_new(bp, ep - bp);
}
+ no_date:;
}
if (!NIL_P(m)) {
@@ -152,8 +161,10 @@ s3e(VALUE hash, VALUE y, VALUE m, VALUE d, int bc)
VALUE iy;
s = RSTRING_PTR(y);
- while (!issign((unsigned char)*s) && !isdigit((unsigned char)*s))
+ ep = RSTRING_END(y);
+ while (s < ep && !issign(*s) && !isdigit(*s))
s++;
+ if (s >= ep) goto no_year;
bp = s;
if (issign(*s)) {
s++;
@@ -161,7 +172,7 @@ s3e(VALUE hash, VALUE y, VALUE m, VALUE d, int bc)
}
if (sign)
c = Qfalse;
- l = strspn(s, DECDIGIT);
+ l = digit_span(s, ep);
ep = s + l;
if (l > 2)
c = Qfalse;
@@ -175,6 +186,7 @@ s3e(VALUE hash, VALUE y, VALUE m, VALUE d, int bc)
ALLOCV_END(vbuf);
}
set_hash("year", iy);
+ no_year:;
}
if (bc)
@@ -186,10 +198,12 @@ s3e(VALUE hash, VALUE y, VALUE m, VALUE d, int bc)
VALUE im;
s = RSTRING_PTR(m);
- while (!isdigit((unsigned char)*s))
+ ep = RSTRING_END(m);
+ while (s < ep && !isdigit(*s))
s++;
+ if (s >= ep) goto no_month;
bp = s;
- l = strspn(s, DECDIGIT);
+ l = digit_span(s, ep);
ep = s + l;
{
char *buf;
@@ -201,6 +215,7 @@ s3e(VALUE hash, VALUE y, VALUE m, VALUE d, int bc)
ALLOCV_END(vbuf);
}
set_hash("mon", im);
+ no_month:;
}
if (!NIL_P(d)) {
@@ -209,10 +224,12 @@ s3e(VALUE hash, VALUE y, VALUE m, VALUE d, int bc)
VALUE id;
s = RSTRING_PTR(d);
- while (!isdigit((unsigned char)*s))
+ ep = RSTRING_END(d);
+ while (s < ep && !isdigit(*s))
s++;
+ if (s >= ep) goto no_mday;
bp = s;
- l = strspn(s, DECDIGIT);
+ l = digit_span(s, ep);
ep = s + l;
{
char *buf;
@@ -224,6 +241,7 @@ s3e(VALUE hash, VALUE y, VALUE m, VALUE d, int bc)
ALLOCV_END(vbuf);
}
set_hash("mday", id);
+ no_mday:;
}
if (!NIL_P(c))
@@ -263,18 +281,18 @@ regcomp(const char *source, long len, int opt)
}
#define REGCOMP(pat,opt) \
-{ \
+do { \
if (NIL_P(pat)) \
pat = regcomp(pat##_source, sizeof pat##_source - 1, opt); \
-}
+} while (0)
#define REGCOMP_0(pat) REGCOMP(pat, 0)
#define REGCOMP_I(pat) REGCOMP(pat, ONIG_OPTION_IGNORECASE)
#define MATCH(s,p,c) \
-{ \
+do { \
return match(s, p, hash, c); \
-}
+} while (0)
static int
match(VALUE str, VALUE pat, VALUE hash, int (*cb)(VALUE, VALUE))
@@ -314,30 +332,30 @@ subx(VALUE str, VALUE rep, VALUE pat, VALUE hash, int (*cb)(VALUE, VALUE))
}
#define SUBS(s,p,c) \
-{ \
+do { \
return subx(s, asp_string(), p, hash, c); \
-}
+} while (0)
#ifdef TIGHT_PARSER
#define SUBA(s,p,c) \
-{ \
+do { \
return subx(s, asuba_string(), p, hash, c); \
-}
+} while (0)
#define SUBB(s,p,c) \
-{ \
+do { \
return subx(s, asubb_string(), p, hash, c); \
-}
+} while (0)
#define SUBW(s,p,c) \
-{ \
+do { \
return subx(s, asubw_string(), p, hash, c); \
-}
+} while (0)
#define SUBT(s,p,c) \
-{ \
+do { \
return subx(s, asubt_string(), p, hash, c); \
-}
+} while (0)
#endif
#include "zonetab.h"
@@ -706,16 +724,14 @@ parse_era(VALUE str, VALUE hash)
static int
check_year_width(VALUE y)
{
- char *s;
- size_t l;
+ const char *s;
+ long l;
+ l = RSTRING_LEN(y);
+ if (l < 2) return 0;
s = RSTRING_PTR(y);
- l = strcspn(s, DECDIGIT);
- s += l;
- l = strspn(s, DECDIGIT);
- if (l != 2)
- return 0;
- return 1;
+ if (!isdigit(s[1])) return 0;
+ return (l == 2 || !isdigit(s[2]));
}
static int
@@ -1196,6 +1212,9 @@ parse_iso2(VALUE str, VALUE hash)
return 1;
}
+#define JISX0301_ERA_INITIALS "mtshr"
+#define JISX0301_DEFAULT_ERA 'H' /* obsolete */
+
static int
gengo(int c)
{
@@ -1206,6 +1225,7 @@ gengo(int c)
case 'T': case 't': e = 1911; break;
case 'S': case 's': e = 1925; break;
case 'H': case 'h': e = 1988; break;
+ case 'R': case 'r': e = 2018; break;
default: e = 0; break;
}
return e;
@@ -1236,11 +1256,11 @@ parse_jis(VALUE str, VALUE hash)
{
static const char pat_source[] =
#ifndef TIGHT_PARSER
- "\\b([mtsh])(\\d+)\\.(\\d+)\\.(\\d+)"
+ "\\b([" JISX0301_ERA_INITIALS "])(\\d+)\\.(\\d+)\\.(\\d+)"
#else
BOS
FPW_COM FPT_COM
- "([mtsh])(\\d+)\\.(\\d+)\\.(\\d+)"
+ "([" JISX0301_ERA_INITIALS "])(\\d+)\\.(\\d+)\\.(\\d+)"
TEE_FPT COM_FPW
EOS
#endif
@@ -2938,7 +2958,7 @@ jisx0301_cb(VALUE m, VALUE hash)
s[i] = rb_reg_nth_match(i, m);
}
- ep = gengo(NIL_P(s[1]) ? 'h' : *RSTRING_PTR(s[1]));
+ ep = gengo(NIL_P(s[1]) ? JISX0301_DEFAULT_ERA : *RSTRING_PTR(s[1]));
set_hash("year", f_add(str2num(s[2]), INT2FIX(ep)));
set_hash("mon", str2num(s[3]));
set_hash("mday", str2num(s[4]));
@@ -2963,7 +2983,7 @@ static int
jisx0301(VALUE str, VALUE hash)
{
static const char pat_source[] =
- "\\A\\s*([mtsh])?(\\d{2})\\.(\\d{2})\\.(\\d{2})"
+ "\\A\\s*([" JISX0301_ERA_INITIALS "])?(\\d{2})\\.(\\d{2})\\.(\\d{2})"
"(?:t"
"(?:(\\d{2}):(\\d{2})(?::(\\d{2})(?:[,.](\\d*))?)?"
"(z|[-+]\\d{2}(?::?\\d{2})?)?)?)?\\s*\\z";
diff --git a/ext/date/date_strptime.c b/ext/date/date_strptime.c
index 4f93219317..4383eb6fa1 100644
--- a/ext/date/date_strptime.c
+++ b/ext/date/date_strptime.c
@@ -79,14 +79,17 @@ read_digits(const char *s, VALUE *n, size_t width)
{
size_t l;
- l = strspn(s, "0123456789");
+ if (!width)
+ return 0;
+
+ l = 0;
+ while (ISDIGIT(s[l])) {
+ if (++l == width) break;
+ }
if (l == 0)
return 0;
- if (width < l)
- l = width;
-
if ((4 * l * sizeof(char)) <= (sizeof(long)*CHAR_BIT)) {
const char *os = s;
long v;
@@ -113,26 +116,26 @@ read_digits(const char *s, VALUE *n, size_t width)
}
}
-#define set_hash(k,v) rb_hash_aset(hash, ID2SYM(rb_intern(k)), v)
-#define ref_hash(k) rb_hash_aref(hash, ID2SYM(rb_intern(k)))
-#define del_hash(k) rb_hash_delete(hash, ID2SYM(rb_intern(k)))
+#define set_hash(k,v) rb_hash_aset(hash, ID2SYM(rb_intern(k"")), v)
+#define ref_hash(k) rb_hash_aref(hash, ID2SYM(rb_intern(k"")))
+#define del_hash(k) rb_hash_delete(hash, ID2SYM(rb_intern(k"")))
#define fail() \
-{ \
+do { \
set_hash("_fail", Qtrue); \
return 0; \
-}
+} while (0)
#define fail_p() (!NIL_P(ref_hash("_fail")))
#define READ_DIGITS(n,w) \
-{ \
+do { \
size_t l; \
l = read_digits(&str[si], &n, w); \
if (l == 0) \
fail(); \
si += l; \
-}
+} while (0)
#define READ_DIGITS_MAX(n) READ_DIGITS(n, LONG_MAX)
@@ -147,14 +150,14 @@ valid_range_p(VALUE v, int a, int b)
}
#define recur(fmt) \
-{ \
+do { \
size_t l; \
l = date__strptime_internal(&str[si], slen - si, \
fmt, sizeof fmt - 1, hash); \
if (fail_p()) \
return 0; \
si += l; \
-}
+} while (0)
VALUE date_zone_to_diff(VALUE);
@@ -237,9 +240,9 @@ date__strptime_internal(const char *str, size_t slen,
VALUE n;
if (NUM_PATTERN_P())
- READ_DIGITS(n, 2)
+ READ_DIGITS(n, 2);
else
- READ_DIGITS_MAX(n)
+ READ_DIGITS_MAX(n);
set_hash("_cent", n);
goto matched;
}
@@ -278,9 +281,9 @@ date__strptime_internal(const char *str, size_t slen,
VALUE n;
if (NUM_PATTERN_P())
- READ_DIGITS(n, 4)
+ READ_DIGITS(n, 4);
else
- READ_DIGITS_MAX(n)
+ READ_DIGITS_MAX(n);
set_hash("cwyear", n);
goto matched;
}
@@ -358,9 +361,9 @@ date__strptime_internal(const char *str, size_t slen,
}
osi = si;
if (NUM_PATTERN_P())
- READ_DIGITS(n, c == 'L' ? 3 : 9)
+ READ_DIGITS(n, c == 'L' ? 3 : 9);
else
- READ_DIGITS_MAX(n)
+ READ_DIGITS_MAX(n);
if (sign == -1)
n = f_negate(n);
set_hash("sec_fraction",
@@ -426,9 +429,7 @@ date__strptime_internal(const char *str, size_t slen,
if (sign == -1)
n = f_negate(n);
set_hash("seconds",
- rb_rational_new2(n,
- f_expt(INT2FIX(10),
- INT2FIX(3))));
+ rb_rational_new2(n, INT2FIX(1000)));
goto matched;
}
@@ -529,24 +530,24 @@ date__strptime_internal(const char *str, size_t slen,
goto matched;
case 'Y':
- {
- VALUE n;
- int sign = 1;
-
- if (issign(str[si])) {
- if (str[si] == '-')
- sign = -1;
- si++;
- }
- if (NUM_PATTERN_P())
- READ_DIGITS(n, 4)
- else
- READ_DIGITS_MAX(n)
+ {
+ VALUE n;
+ int sign = 1;
+
+ if (issign(str[si])) {
+ if (str[si] == '-')
+ sign = -1;
+ si++;
+ }
+ if (NUM_PATTERN_P())
+ READ_DIGITS(n, 4);
+ else
+ READ_DIGITS_MAX(n);
if (sign == -1)
n = f_negate(n);
- set_hash("year", n);
- goto matched;
- }
+ set_hash("year", n);
+ goto matched;
+ }
case 'y':
{
diff --git a/ext/date/depend b/ext/date/depend
index 2c24e91432..864fcc5302 100644
--- a/ext/date/depend
+++ b/ext/date/depend
@@ -6,6 +6,7 @@ date_core.o: $(hdrdir)/ruby/defines.h
date_core.o: $(hdrdir)/ruby/encoding.h
date_core.o: $(hdrdir)/ruby/intern.h
date_core.o: $(hdrdir)/ruby/missing.h
+date_core.o: $(hdrdir)/ruby/onigmo.h
date_core.o: $(hdrdir)/ruby/oniguruma.h
date_core.o: $(hdrdir)/ruby/ruby.h
date_core.o: $(hdrdir)/ruby/st.h
@@ -21,6 +22,7 @@ date_parse.o: $(hdrdir)/ruby/defines.h
date_parse.o: $(hdrdir)/ruby/encoding.h
date_parse.o: $(hdrdir)/ruby/intern.h
date_parse.o: $(hdrdir)/ruby/missing.h
+date_parse.o: $(hdrdir)/ruby/onigmo.h
date_parse.o: $(hdrdir)/ruby/oniguruma.h
date_parse.o: $(hdrdir)/ruby/re.h
date_parse.o: $(hdrdir)/ruby/regex.h
@@ -49,6 +51,7 @@ date_strptime.o: $(hdrdir)/ruby/defines.h
date_strptime.o: $(hdrdir)/ruby/encoding.h
date_strptime.o: $(hdrdir)/ruby/intern.h
date_strptime.o: $(hdrdir)/ruby/missing.h
+date_strptime.o: $(hdrdir)/ruby/onigmo.h
date_strptime.o: $(hdrdir)/ruby/oniguruma.h
date_strptime.o: $(hdrdir)/ruby/re.h
date_strptime.o: $(hdrdir)/ruby/regex.h
diff --git a/ext/date/extconf.rb b/ext/date/extconf.rb
index e8596952de..8938df13b3 100644
--- a/ext/date/extconf.rb
+++ b/ext/date/extconf.rb
@@ -1,4 +1,9 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'mkmf'
+
config_string("strict_warnflags") {|w| $warnflags += " #{w}"}
+
+have_var("timezone", "time.h")
+have_var("altzone", "time.h")
+
create_makefile('date_core')
diff --git a/ext/date/lib/date.rb b/ext/date/lib/date.rb
index 48ce6316bd..cea63b1259 100644
--- a/ext/date/lib/date.rb
+++ b/ext/date/lib/date.rb
@@ -1,9 +1,10 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# date.rb: Written by Tadayoshi Funaba 1998-2011
require 'date_core'
class Date
+ VERSION = '2.0.3' # :nodoc:
class Infinity < Numeric # :nodoc:
@@ -15,17 +16,17 @@ class Date
protected :d
- def zero? () false end
- def finite? () false end
- def infinite? () d.nonzero? end
- def nan? () d.zero? end
+ def zero?() false end
+ def finite?() false end
+ def infinite?() d.nonzero? end
+ def nan?() d.zero? end
def abs() self.class.new end
- def -@ () self.class.new(-d) end
- def +@ () self.class.new(+d) end
+ def -@() self.class.new(-d) end
+ def +@() self.class.new(+d) end
- def <=> (other)
+ def <=>(other)
case other
when Infinity; return d <=> other.d
when Numeric; return d
diff --git a/ext/date/prereq.mk b/ext/date/prereq.mk
index a0a5a2bff3..c0c55d2c27 100644
--- a/ext/date/prereq.mk
+++ b/ext/date/prereq.mk
@@ -2,7 +2,7 @@
.list.h:
gperf -E -C -c -P -p -j1 -i 1 -g -o -t -N $(*F) $< \
- | sed 's/(int)(long)&((\(struct stringpool_t\) *\*)0)->\(stringpool_[a-z0-9]*\)/offsetof(\1, \2)/g' \
+ | sed -f $(top_srcdir)/tool/gperf.sed \
> $(@F)
zonetab.h: zonetab.list
diff --git a/ext/date/zonetab.h b/ext/date/zonetab.h
index fa86e7991a..2dfa9b988a 100644
--- a/ext/date/zonetab.h
+++ b/ext/date/zonetab.h
@@ -1,4 +1,4 @@
-/* C code produced by gperf version 3.0.4 */
+/* ANSI-C code produced by gperf version 3.1 */
/* Command-line: gperf -E -C -c -P -p -j1 -i 1 -g -o -t -N zonetab zonetab.list */
/* Computed positions: -k'1-4,$' */
@@ -26,9 +26,10 @@
&& ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
&& ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
/* The character set is not based on ISO-646. */
-error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>."
#endif
+#define gperf_offsetof(s, n) (short)offsetof(struct s##_t, s##_str##n)
#line 1 "zonetab.list"
struct zone {
@@ -40,15 +41,15 @@ static const struct zone *zonetab();
struct zone;
/* maximum key range = 434, duplicates = 0 */
-#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) || defined(__GNUC_STDC_INLINE__)
-inline
-#elif defined(__GNUC__)
+#ifdef __GNUC__
__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
#endif
static unsigned int
-hash (str, len)
- register const char *str;
- register unsigned int len;
+hash (register const char *str, register size_t len)
{
static const unsigned short asso_values[] =
{
@@ -79,7 +80,7 @@ hash (str, len)
439, 439, 439, 439, 439, 439, 439, 439, 439, 439,
439, 439, 439, 439, 439, 439, 439
};
- register int hval = len;
+ register unsigned int hval = (unsigned int)len;
switch (hval)
{
@@ -96,7 +97,7 @@ hash (str, len)
hval += asso_values[(unsigned char)str[0]+1];
break;
}
- return hval + asso_values[(unsigned char)str[len - 1]];
+ return (unsigned int)hval + asso_values[(unsigned char)str[len - 1]];
}
struct stringpool_t
@@ -446,16 +447,8 @@ static const struct stringpool_t stringpool_contents =
"fiji"
};
#define stringpool ((const char *) &stringpool_contents)
-#ifdef __GNUC__
-__inline
-#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
-__attribute__ ((__gnu_inline__))
-#endif
-#endif
const struct zone *
-zonetab (str, len)
- register const char *str;
- register unsigned int len;
+zonetab (register const char *str, register size_t len)
{
enum
{
@@ -470,398 +463,398 @@ zonetab (str, len)
{
{-1}, {-1}, {-1}, {-1}, {-1},
#line 37 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str5), -5*3600},
+ {gperf_offsetof(stringpool, 5), -5*3600},
#line 38 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str6), -6*3600},
+ {gperf_offsetof(stringpool, 6), -6*3600},
#line 24 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str7), 4*3600},
+ {gperf_offsetof(stringpool, 7), 4*3600},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#line 15 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str14), -6*3600},
+ {gperf_offsetof(stringpool, 14), -6*3600},
#line 16 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str15), -5*3600},
+ {gperf_offsetof(stringpool, 15), -5*3600},
#line 85 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str16), 2*3600},
+ {gperf_offsetof(stringpool, 16), 2*3600},
#line 71 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str17), 1*3600},
+ {gperf_offsetof(stringpool, 17), 1*3600},
#line 90 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str18), 4*3600},
+ {gperf_offsetof(stringpool, 18), 4*3600},
#line 79 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str19), 2*3600},
+ {gperf_offsetof(stringpool, 19), 2*3600},
#line 65 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str20),-10*3600},
+ {gperf_offsetof(stringpool, 20),-10*3600},
{-1},
#line 17 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str22), -7*3600},
+ {gperf_offsetof(stringpool, 22), -7*3600},
#line 18 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str23), -6*3600},
+ {gperf_offsetof(stringpool, 23), -6*3600},
#line 84 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str24), 2*3600},
+ {gperf_offsetof(stringpool, 24), 2*3600},
#line 73 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str25), 1*3600},
+ {gperf_offsetof(stringpool, 25), 1*3600},
{-1},
#line 82 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str27), 2*3600},
+ {gperf_offsetof(stringpool, 27), 2*3600},
{-1}, {-1},
#line 47 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str30), 0*3600},
+ {gperf_offsetof(stringpool, 30), 0*3600},
#line 128 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str31), -43200},
+ {gperf_offsetof(stringpool, 31), -43200},
#line 78 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str32), 1*3600},
+ {gperf_offsetof(stringpool, 32), 1*3600},
#line 77 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str33), 1*3600},
+ {gperf_offsetof(stringpool, 33), 1*3600},
{-1},
#line 95 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str35), 7*3600},
+ {gperf_offsetof(stringpool, 35), 7*3600},
#line 98 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str36), 8*3600},
+ {gperf_offsetof(stringpool, 36), 8*3600},
#line 25 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str37), 5*3600},
+ {gperf_offsetof(stringpool, 37), 5*3600},
#line 123 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str38), 3600},
+ {gperf_offsetof(stringpool, 38), 3600},
#line 122 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str39), 21600},
+ {gperf_offsetof(stringpool, 39), 21600},
#line 178 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str40), 18000},
+ {gperf_offsetof(stringpool, 40), 18000},
#line 120 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str41), 34200},
+ {gperf_offsetof(stringpool, 41), 34200},
#line 121 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str42), -21600},
+ {gperf_offsetof(stringpool, 42), -21600},
{-1},
#line 13 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str44), -5*3600},
+ {gperf_offsetof(stringpool, 44), -5*3600},
#line 14 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str45), -4*3600},
+ {gperf_offsetof(stringpool, 45), -4*3600},
#line 124 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str46), 3600},
+ {gperf_offsetof(stringpool, 46), 3600},
#line 80 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str47), 2*3600},
+ {gperf_offsetof(stringpool, 47), 2*3600},
#line 164 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str48), 25200},
+ {gperf_offsetof(stringpool, 48), 25200},
#line 88 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str49), 3*3600},
+ {gperf_offsetof(stringpool, 49), 3*3600},
#line 87 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str50), 3*3600},
+ {gperf_offsetof(stringpool, 50), 3*3600},
#line 45 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str51), 0*3600},
+ {gperf_offsetof(stringpool, 51), 0*3600},
#line 101 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str52),10*3600},
+ {gperf_offsetof(stringpool, 52),10*3600},
#line 103 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str53),11*3600},
+ {gperf_offsetof(stringpool, 53),11*3600},
#line 160 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str54), -10800},
+ {gperf_offsetof(stringpool, 54), -10800},
#line 177 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str55), 3600},
+ {gperf_offsetof(stringpool, 55), 3600},
#line 23 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str56), 3*3600},
+ {gperf_offsetof(stringpool, 56), 3*3600},
#line 63 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str57), -9*3600},
+ {gperf_offsetof(stringpool, 57), -9*3600},
#line 59 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str58), -8*3600},
+ {gperf_offsetof(stringpool, 58), -8*3600},
#line 100 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str59), 9*3600},
+ {gperf_offsetof(stringpool, 59), 9*3600},
#line 57 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str60), -4*3600},
+ {gperf_offsetof(stringpool, 60), -4*3600},
#line 133 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str61), -18000},
+ {gperf_offsetof(stringpool, 61), -18000},
#line 54 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str62),-3*3600},
+ {gperf_offsetof(stringpool, 62),-3*3600},
#line 86 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str63), 3*3600},
+ {gperf_offsetof(stringpool, 63), 3*3600},
#line 175 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str64), 28800},
+ {gperf_offsetof(stringpool, 64), 28800},
#line 70 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str65), 1*3600},
+ {gperf_offsetof(stringpool, 65), 1*3600},
#line 96 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str66), 8*3600},
+ {gperf_offsetof(stringpool, 66), 8*3600},
#line 53 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str67), -3*3600},
+ {gperf_offsetof(stringpool, 67), -3*3600},
{-1},
#line 49 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str69),-2*3600},
+ {gperf_offsetof(stringpool, 69),-2*3600},
{-1},
#line 21 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str71), 1*3600},
+ {gperf_offsetof(stringpool, 71), 1*3600},
#line 131 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str72), 7200},
+ {gperf_offsetof(stringpool, 72), 7200},
#line 48 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str73), -2*3600},
+ {gperf_offsetof(stringpool, 73), -2*3600},
#line 126 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str74), -21600},
+ {gperf_offsetof(stringpool, 74), -21600},
#line 56 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str75), -4*3600},
+ {gperf_offsetof(stringpool, 75), -4*3600},
#line 52 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str76), -3*3600},
+ {gperf_offsetof(stringpool, 76), -3*3600},
#line 51 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str77), -3*3600},
+ {gperf_offsetof(stringpool, 77), -3*3600},
#line 129 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str78), 10800},
+ {gperf_offsetof(stringpool, 78), 10800},
#line 132 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str79), -10800},
+ {gperf_offsetof(stringpool, 79), -10800},
#line 99 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str80), 9*3600},
+ {gperf_offsetof(stringpool, 80), 9*3600},
#line 130 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str81), 36000},
+ {gperf_offsetof(stringpool, 81), 36000},
#line 39 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str82), -7*3600},
+ {gperf_offsetof(stringpool, 82), -7*3600},
#line 68 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str83), -11*3600},
+ {gperf_offsetof(stringpool, 83), -11*3600},
#line 33 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str84), -1*3600},
+ {gperf_offsetof(stringpool, 84), -1*3600},
#line 55 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str85), -(3*3600+1800)},
+ {gperf_offsetof(stringpool, 85), -(3*3600+1800)},
#line 50 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str86), -(2*3600+1800)},
+ {gperf_offsetof(stringpool, 86), -(2*3600+1800)},
#line 117 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str87), -21600},
+ {gperf_offsetof(stringpool, 87), -21600},
#line 125 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str88), 39600},
+ {gperf_offsetof(stringpool, 88), 39600},
#line 179 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str89), 36000},
+ {gperf_offsetof(stringpool, 89), 36000},
#line 67 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str90),-10*3600},
+ {gperf_offsetof(stringpool, 90),-10*3600},
#line 62 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str91), -9*3600},
+ {gperf_offsetof(stringpool, 91), -9*3600},
{-1},
#line 165 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str93), 28800},
+ {gperf_offsetof(stringpool, 93), 28800},
{-1},
#line 94 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str95), 6*3600},
+ {gperf_offsetof(stringpool, 95), 6*3600},
{-1},
#line 159 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str97), 10800},
+ {gperf_offsetof(stringpool, 97), 10800},
#line 66 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str98),-10*3600},
+ {gperf_offsetof(stringpool, 98),-10*3600},
#line 61 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str99),-9*3600},
+ {gperf_offsetof(stringpool, 99),-9*3600},
#line 102 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str100), 10*3600},
+ {gperf_offsetof(stringpool, 100), 10*3600},
#line 92 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str101), 5*3600},
+ {gperf_offsetof(stringpool, 101), 5*3600},
#line 93 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str102), (5*3600+1800)},
+ {gperf_offsetof(stringpool, 102), (5*3600+1800)},
#line 76 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str103), 1*3600},
+ {gperf_offsetof(stringpool, 103), 1*3600},
#line 42 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str104), -10*3600},
+ {gperf_offsetof(stringpool, 104), -10*3600},
#line 91 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str105), 4*3600},
+ {gperf_offsetof(stringpool, 105), 4*3600},
{-1},
#line 75 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str107), 1*3600},
+ {gperf_offsetof(stringpool, 107), 1*3600},
#line 118 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str108), -3600},
+ {gperf_offsetof(stringpool, 108), -3600},
#line 83 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str109), 2*3600},
+ {gperf_offsetof(stringpool, 109), 2*3600},
#line 138 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str110), -10800},
+ {gperf_offsetof(stringpool, 110), -10800},
{-1},
#line 43 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str112), -11*3600},
+ {gperf_offsetof(stringpool, 112), -11*3600},
{-1},
#line 74 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str114), 1*3600},
+ {gperf_offsetof(stringpool, 114), 1*3600},
#line 176 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str115), 3600},
+ {gperf_offsetof(stringpool, 115), 3600},
#line 30 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str116), 10*3600},
+ {gperf_offsetof(stringpool, 116), 10*3600},
#line 22 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str117), 2*3600},
+ {gperf_offsetof(stringpool, 117), 2*3600},
{-1},
#line 32 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str119), 12*3600},
+ {gperf_offsetof(stringpool, 119), 12*3600},
#line 167 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str120), 21600},
+ {gperf_offsetof(stringpool, 120), 21600},
{-1},
#line 81 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str122), 2*3600},
+ {gperf_offsetof(stringpool, 122), 2*3600},
{-1},
#line 143 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str124), 12600},
+ {gperf_offsetof(stringpool, 124), 12600},
#line 97 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str125), 8*3600},
+ {gperf_offsetof(stringpool, 125), 8*3600},
#line 11 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str126), 0*3600},
+ {gperf_offsetof(stringpool, 126), 0*3600},
{-1},
#line 36 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str128), -4*3600},
+ {gperf_offsetof(stringpool, 128), -4*3600},
#line 106 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str129), 12*3600},
+ {gperf_offsetof(stringpool, 129), 12*3600},
{-1},
#line 105 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str131),12*3600},
+ {gperf_offsetof(stringpool, 131),12*3600},
#line 107 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str132),13*3600},
+ {gperf_offsetof(stringpool, 132),13*3600},
#line 149 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str133), 23400},
+ {gperf_offsetof(stringpool, 133), 23400},
{-1},
#line 109 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str135), -32400},
+ {gperf_offsetof(stringpool, 135), -32400},
#line 19 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str136), -8*3600},
+ {gperf_offsetof(stringpool, 136), -8*3600},
#line 20 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str137), -7*3600},
+ {gperf_offsetof(stringpool, 137), -7*3600},
#line 162 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str138), -14400},
+ {gperf_offsetof(stringpool, 138), -14400},
#line 145 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str139), 32400},
+ {gperf_offsetof(stringpool, 139), 32400},
{-1}, {-1},
#line 44 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str142), -12*3600},
+ {gperf_offsetof(stringpool, 142), -12*3600},
#line 26 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str143), 6*3600},
+ {gperf_offsetof(stringpool, 143), 6*3600},
#line 60 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str144),-9*3600},
+ {gperf_offsetof(stringpool, 144),-9*3600},
#line 58 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str145),-8*3600},
+ {gperf_offsetof(stringpool, 145),-8*3600},
{-1}, {-1},
#line 119 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str148), 14400},
+ {gperf_offsetof(stringpool, 148), 14400},
{-1},
#line 89 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str150), 3*3600},
+ {gperf_offsetof(stringpool, 150), 3*3600},
#line 104 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str151),12*3600},
+ {gperf_offsetof(stringpool, 151),12*3600},
{-1},
#line 111 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str153), 14400},
+ {gperf_offsetof(stringpool, 153), 14400},
{-1},
#line 34 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str155), -2*3600},
+ {gperf_offsetof(stringpool, 155), -2*3600},
#line 31 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str156), 11*3600},
+ {gperf_offsetof(stringpool, 156), 11*3600},
#line 147 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str157), -7200},
+ {gperf_offsetof(stringpool, 157), -7200},
{-1}, {-1},
#line 172 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str160), -18000},
+ {gperf_offsetof(stringpool, 160), -18000},
{-1}, {-1}, {-1},
#line 64 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str164),-10*3600},
+ {gperf_offsetof(stringpool, 164),-10*3600},
{-1}, {-1},
#line 28 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str167), 8*3600},
+ {gperf_offsetof(stringpool, 167), 8*3600},
#line 137 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str168), 7200},
+ {gperf_offsetof(stringpool, 168), 7200},
#line 29 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str169), 9*3600},
+ {gperf_offsetof(stringpool, 169), 9*3600},
#line 155 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str170), 25200},
+ {gperf_offsetof(stringpool, 170), 25200},
#line 150 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str171), 21600},
+ {gperf_offsetof(stringpool, 171), 21600},
#line 154 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str172), 28800},
+ {gperf_offsetof(stringpool, 172), 28800},
{-1},
#line 161 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str174), -18000},
+ {gperf_offsetof(stringpool, 174), -18000},
{-1}, {-1},
#line 166 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str177), 7200},
+ {gperf_offsetof(stringpool, 177), 7200},
{-1}, {-1}, {-1},
#line 115 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str181), 36000},
+ {gperf_offsetof(stringpool, 181), 36000},
#line 113 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str182), -14400},
+ {gperf_offsetof(stringpool, 182), -14400},
{-1}, {-1}, {-1},
#line 146 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str186), -21600},
+ {gperf_offsetof(stringpool, 186), -21600},
{-1},
#line 148 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str188), -25200},
+ {gperf_offsetof(stringpool, 188), -25200},
{-1},
#line 127 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str190), 28800},
+ {gperf_offsetof(stringpool, 190), 28800},
#line 116 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str191), -3600},
+ {gperf_offsetof(stringpool, 191), -3600},
#line 142 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str192), 19800},
+ {gperf_offsetof(stringpool, 192), 19800},
{-1},
#line 40 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str194), -8*3600},
+ {gperf_offsetof(stringpool, 194), -8*3600},
#line 112 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str195), 10800},
+ {gperf_offsetof(stringpool, 195), 10800},
#line 139 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str196), 0},
+ {gperf_offsetof(stringpool, 196), 0},
#line 152 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str197), 43200},
+ {gperf_offsetof(stringpool, 197), 43200},
#line 141 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str198), -36000},
+ {gperf_offsetof(stringpool, 198), -36000},
#line 27 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str199), 7*3600},
+ {gperf_offsetof(stringpool, 199), 7*3600},
#line 158 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str200), 3600},
+ {gperf_offsetof(stringpool, 200), 3600},
{-1}, {-1},
#line 110 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str203), 10800},
+ {gperf_offsetof(stringpool, 203), 10800},
#line 163 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str204), -39600},
+ {gperf_offsetof(stringpool, 204), -39600},
#line 41 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str205), -9*3600},
+ {gperf_offsetof(stringpool, 205), -9*3600},
#line 35 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str206), -3*3600},
+ {gperf_offsetof(stringpool, 206), -3*3600},
#line 12 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str207), 0*3600},
+ {gperf_offsetof(stringpool, 207), 0*3600},
#line 169 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str208), 36000},
+ {gperf_offsetof(stringpool, 208), 36000},
#line 72 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str209), 1*3600},
+ {gperf_offsetof(stringpool, 209), 1*3600},
{-1},
#line 153 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str211), -12600},
+ {gperf_offsetof(stringpool, 211), -12600},
{-1}, {-1}, {-1}, {-1}, {-1},
#line 151 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str217), 20700},
+ {gperf_offsetof(stringpool, 217), 20700},
#line 114 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str218), 34200},
+ {gperf_offsetof(stringpool, 218), 34200},
{-1}, {-1},
#line 140 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str221), 7200},
+ {gperf_offsetof(stringpool, 221), 7200},
{-1},
#line 174 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str223), 36000},
+ {gperf_offsetof(stringpool, 223), 36000},
{-1}, {-1}, {-1}, {-1}, {-1},
#line 46 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str229), 0*3600},
+ {gperf_offsetof(stringpool, 229), 0*3600},
{-1}, {-1}, {-1},
#line 135 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str233), 18000},
+ {gperf_offsetof(stringpool, 233), 18000},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
#line 173 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str265), -25200},
+ {gperf_offsetof(stringpool, 265), -25200},
{-1}, {-1}, {-1},
#line 144 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str269), 7200},
+ {gperf_offsetof(stringpool, 269), 7200},
{-1}, {-1},
#line 180 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str272), 32400},
+ {gperf_offsetof(stringpool, 272), 32400},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#line 156 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str279), -14400},
+ {gperf_offsetof(stringpool, 279), -14400},
{-1}, {-1},
#line 171 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str282), 46800},
+ {gperf_offsetof(stringpool, 282), 46800},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
#line 108 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str314), 16200},
+ {gperf_offsetof(stringpool, 314), 16200},
{-1}, {-1}, {-1}, {-1},
#line 69 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str319),-12*3600},
+ {gperf_offsetof(stringpool, 319),-12*3600},
{-1}, {-1},
#line 157 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str322), -28800},
+ {gperf_offsetof(stringpool, 322), -28800},
{-1}, {-1}, {-1}, {-1},
#line 168 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str327), 28800},
+ {gperf_offsetof(stringpool, 327), 28800},
#line 134 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str328), 7200},
+ {gperf_offsetof(stringpool, 328), 7200},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
@@ -870,21 +863,21 @@ zonetab (str, len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#line 170 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str392), 32400},
+ {gperf_offsetof(stringpool, 392), 32400},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#line 136 "zonetab.list"
- {offsetof(struct stringpool_t, stringpool_str438), 43200}
+ {gperf_offsetof(stringpool, 438), 43200}
};
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
{
- register int key = hash (str, len);
+ register unsigned int key = hash (str, len);
- if (key <= MAX_HASH_VALUE && key >= 0)
+ if (key <= MAX_HASH_VALUE)
{
register int o = wordlist[key].name;
if (o >= 0)
diff --git a/ext/dbm/dbm.c b/ext/dbm/dbm.c
index 07044d6f9f..df69b9fb50 100644
--- a/ext/dbm/dbm.c
+++ b/ext/dbm/dbm.c
@@ -24,7 +24,7 @@
#define DSIZE_TYPE TYPEOF_DATUM_DSIZE
#if SIZEOF_DATUM_DSIZE > SIZEOF_INT
# define RSTRING_DSIZE(s) RSTRING_LEN(s)
-# define TOO_LONG(n) 0
+# define TOO_LONG(n) ((void)(n),0)
#else
# define RSTRING_DSIZE(s) RSTRING_LENINT(s)
# define TOO_LONG(n) ((long)(+(DSIZE_TYPE)(n)) != (n))
@@ -39,6 +39,8 @@ struct dbmdata {
DBM *di_dbm;
};
+NORETURN(static void closed_dbm(void));
+
static void
closed_dbm(void)
{
@@ -47,7 +49,6 @@ closed_dbm(void)
#define GetDBM(obj, dbmp) do {\
TypedData_Get_Struct((obj), struct dbmdata, &dbm_type, (dbmp));\
- if ((dbmp) == 0) closed_dbm();\
if ((dbmp)->di_dbm == 0) closed_dbm();\
} while (0)
@@ -60,21 +61,18 @@ static void
free_dbm(void *ptr)
{
struct dbmdata *dbmp = ptr;
- if (dbmp) {
- if (dbmp->di_dbm) dbm_close(dbmp->di_dbm);
- xfree(dbmp);
- }
+ if (dbmp->di_dbm)
+ dbm_close(dbmp->di_dbm);
+ xfree(dbmp);
}
static size_t
memsize_dbm(const void *ptr)
{
- size_t size = 0;
const struct dbmdata *dbmp = ptr;
- if (dbmp) {
- size += sizeof(*dbmp);
- if (dbmp->di_dbm) size += DBM_SIZEOF_DBM;
- }
+ size_t size = sizeof(*dbmp);
+ if (dbmp->di_dbm)
+ size += DBM_SIZEOF_DBM;
return size;
}
@@ -115,8 +113,6 @@ fdbm_closed(VALUE obj)
struct dbmdata *dbmp;
TypedData_Get_Struct(obj, struct dbmdata, &dbm_type, dbmp);
- if (dbmp == 0)
- return Qtrue;
if (dbmp->di_dbm == 0)
return Qtrue;
@@ -126,7 +122,9 @@ fdbm_closed(VALUE obj)
static VALUE
fdbm_alloc(VALUE klass)
{
- return TypedData_Wrap_Struct(klass, &dbm_type, 0);
+ struct dbmdata *dbmp;
+
+ return TypedData_Make_Struct(klass, struct dbmdata, &dbm_type, dbmp);
}
/*
@@ -150,6 +148,7 @@ fdbm_initialize(int argc, VALUE *argv, VALUE obj)
struct dbmdata *dbmp;
int mode, flags = 0;
+ TypedData_Get_Struct(obj, struct dbmdata, &dbm_type, dbmp);
if (rb_scan_args(argc, argv, "12", &file, &vmode, &vflags) == 1) {
mode = 0666; /* default value */
}
@@ -191,24 +190,24 @@ fdbm_initialize(int argc, VALUE *argv, VALUE obj)
}
if (dbm) {
- /*
- * History of dbm_pagfno() and dbm_dirfno() in ndbm and its compatibles.
- * (dbm_pagfno() and dbm_dirfno() is not standardized.)
- *
- * 1986: 4.3BSD provides ndbm.
- * It provides dbm_pagfno() and dbm_dirfno() as macros.
- * 1991: gdbm-1.5 provides them as functions.
- * They returns a same descriptor.
- * (Earlier releases may have the functions too.)
- * 1991: Net/2 provides Berkeley DB.
- * It doesn't provide dbm_pagfno() and dbm_dirfno().
- * 1992: 4.4BSD Alpha provides Berkeley DB with dbm_dirfno() as a function.
- * dbm_pagfno() is a macro as DBM_PAGFNO_NOT_AVAILABLE.
- * 1997: Berkeley DB 2.0 is released by Sleepycat Software, Inc.
- * It defines dbm_pagfno() and dbm_dirfno() as macros.
- * 2011: gdbm-1.9 creates a separate dir file.
- * dbm_pagfno() and dbm_dirfno() returns different descriptors.
- */
+ /*
+ * History of dbm_pagfno() and dbm_dirfno() in ndbm and its compatibles.
+ * (dbm_pagfno() and dbm_dirfno() is not standardized.)
+ *
+ * 1986: 4.3BSD provides ndbm.
+ * It provides dbm_pagfno() and dbm_dirfno() as macros.
+ * 1991: gdbm-1.5 provides them as functions.
+ * They returns a same descriptor.
+ * (Earlier releases may have the functions too.)
+ * 1991: Net/2 provides Berkeley DB.
+ * It doesn't provide dbm_pagfno() and dbm_dirfno().
+ * 1992: 4.4BSD Alpha provides Berkeley DB with dbm_dirfno() as a function.
+ * dbm_pagfno() is a macro as DBM_PAGFNO_NOT_AVAILABLE.
+ * 1997: Berkeley DB 2.0 is released by Sleepycat Software, Inc.
+ * It defines dbm_pagfno() and dbm_dirfno() as macros.
+ * 2011: gdbm-1.9 creates a separate dir file.
+ * dbm_pagfno() and dbm_dirfno() returns different descriptors.
+ */
#if defined(HAVE_DBM_PAGFNO)
rb_fd_fix_cloexec(dbm_pagfno(dbm));
#endif
@@ -217,8 +216,8 @@ fdbm_initialize(int argc, VALUE *argv, VALUE obj)
#endif
#if defined(RUBYDBM_DB_HEADER) && defined(HAVE_TYPE_DBC)
- /* Disable Berkeley DB error messages such as:
- * DB->put: attempt to modify a read-only database */
+ /* Disable Berkeley DB error messages such as:
+ * DB->put: attempt to modify a read-only database */
((DBC*)dbm)->dbp->set_errfile(((DBC*)dbm)->dbp, NULL);
#endif
}
@@ -228,8 +227,8 @@ fdbm_initialize(int argc, VALUE *argv, VALUE obj)
rb_sys_fail_str(file);
}
- dbmp = ALLOC(struct dbmdata);
- DATA_PTR(obj) = dbmp;
+ if (dbmp->di_dbm)
+ dbm_close(dbmp->di_dbm);
dbmp->di_dbm = dbm;
dbmp->di_size = -1;
@@ -339,8 +338,6 @@ fdbm_key(VALUE obj, VALUE valstr)
ExportStringValue(valstr);
len = RSTRING_LEN(valstr);
if (TOO_LONG(len)) return Qnil;
- val.dptr = RSTRING_PTR(valstr);
- val.dsize = (DSIZE_TYPE)len;
GetDBM2(obj, dbmp, dbm);
for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
@@ -997,7 +994,7 @@ fdbm_reject(VALUE obj)
* It is based on dbm library in Unix Version 7 but has different API to
* support multiple databases in a process.
* - {Berkeley DB}[http://en.wikipedia.org/wiki/Berkeley_DB] versions
- * 1 thru 5, also known as BDB and Sleepycat DB, now owned by Oracle
+ * 1 thru 6, also known as BDB and Sleepycat DB, now owned by Oracle
* Corporation.
* - Berkeley DB 1.x, still found in 4.4BSD derivatives (FreeBSD, OpenBSD, etc).
* - {gdbm}[http://www.gnu.org/software/gdbm/], the GNU implementation of dbm.
diff --git a/ext/dbm/dbm.gemspec b/ext/dbm/dbm.gemspec
new file mode 100644
index 0000000000..9ab444b9c7
--- /dev/null
+++ b/ext/dbm/dbm.gemspec
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+Gem::Specification.new do |s|
+ s.name = "dbm"
+ s.version = '1.0.0'
+ s.summary = "Provides a wrapper for the UNIX-style Database Manager Library"
+ s.description = "Provides a wrapper for the UNIX-style Database Manager Library"
+
+ s.require_path = %w{lib}
+ s.files = %w{ext/dbm/extconf.rb ext/dbm/dbm.c}
+ s.extensions = %w{ext/dbm/extconf.rb}
+ s.required_ruby_version = ">= 2.3.0"
+
+ s.authors = ["Yukihiro Matsumoto"]
+ s.email = ["matz@ruby-lang.org"]
+ s.homepage = "https://github.com/ruby/dbm"
+ s.license = "BSD-2-Clause"
+
+ s.add_development_dependency "rake-compiler"
+ s.add_development_dependency "test-unit"
+end
diff --git a/ext/dbm/extconf.rb b/ext/dbm/extconf.rb
index 04f751d776..c9a5518bf6 100644
--- a/ext/dbm/extconf.rb
+++ b/ext/dbm/extconf.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# configure option:
# --with-dbm-type=COMMA-SEPARATED-NDBM-TYPES
#
@@ -7,6 +7,7 @@
# db Berkeley DB (libdb)
# db2 Berkeley DB (libdb2)
# db1 Berkeley DB (libdb1)
+# db6 Berkeley DB (libdb6)
# db5 Berkeley DB (libdb5)
# db4 Berkeley DB (libdb4)
# db3 Berkeley DB (libdb3)
@@ -23,7 +24,7 @@ dir_config("dbm")
if dblib = with_config("dbm-type", nil)
dblib = dblib.split(/[ ,]+/)
else
- dblib = %w(libc db db2 db1 db5 db4 db3 gdbm_compat gdbm qdbm)
+ dblib = %w(libc db db2 db1 db6 db5 db4 db3 gdbm_compat gdbm qdbm)
end
headers = {
@@ -34,6 +35,7 @@ headers = {
"db3" => ["db3/db.h", "db3.h", "db.h"],
"db4" => ["db4/db.h", "db4.h", "db.h"],
"db5" => ["db5/db.h", "db5.h", "db.h"],
+ "db6" => ["db6/db.h", "db6.h", "db.h"],
"gdbm_compat" => ["gdbm-ndbm.h", "gdbm/ndbm.h", "ndbm.h"], # GDBM since 1.8.1
"gdbm" => ["gdbm-ndbm.h", "gdbm/ndbm.h", "ndbm.h"], # GDBM until 1.8.0
"qdbm" => ["qdbm/relic.h", "relic.h"],
@@ -131,7 +133,7 @@ def headers.db_check2(db, hdr)
hsearch = nil
case db
- when /^db[2-5]?$/
+ when /^db[2-6]?$/
hsearch = "-DDB_DBM_HSEARCH"
when "gdbm_compat"
have_library("gdbm") or return false
diff --git a/ext/digest/bubblebabble/bubblebabble.c b/ext/digest/bubblebabble/bubblebabble.c
index 8f436908d3..6557e43c9d 100644
--- a/ext/digest/bubblebabble/bubblebabble.c
+++ b/ext/digest/bubblebabble/bubblebabble.c
@@ -124,6 +124,7 @@ rb_digest_instance_bubblebabble(VALUE self)
void
Init_bubblebabble(void)
{
+#undef rb_intern
VALUE rb_mDigest, rb_mDigest_Instance, rb_cDigest_Class;
rb_require("digest");
diff --git a/ext/digest/digest.c b/ext/digest/digest.c
index 9838ed33de..7d285cfe74 100644
--- a/ext/digest/digest.c
+++ b/ext/digest/digest.c
@@ -728,6 +728,7 @@ rb_digest_base_block_length(VALUE self)
void
Init_digest(void)
{
+#undef rb_intern
id_reset = rb_intern("reset");
id_update = rb_intern("update");
id_finish = rb_intern("finish");
diff --git a/ext/digest/digest_conf.rb b/ext/digest/digest_conf.rb
index 8e36ce5d41..915b61877f 100644
--- a/ext/digest/digest_conf.rb
+++ b/ext/digest/digest_conf.rb
@@ -1,4 +1,56 @@
# frozen_string_literal: false
+
+# Copy from ext/openssl/extconf.rb
+def find_openssl_library
+ if $mswin || $mingw
+ # required for static OpenSSL libraries
+ have_library("gdi32") # OpenSSL <= 1.0.2 (for RAND_screen())
+ have_library("crypt32")
+ end
+
+ return false unless have_header("openssl/ssl.h")
+
+ ret = have_library("crypto", "CRYPTO_malloc") &&
+ have_library("ssl", "SSL_new")
+ return ret if ret
+
+ if $mswin
+ # OpenSSL >= 1.1.0: libcrypto.lib and libssl.lib.
+ if have_library("libcrypto", "CRYPTO_malloc") &&
+ have_library("libssl", "SSL_new")
+ return true
+ end
+
+ # OpenSSL <= 1.0.2: libeay32.lib and ssleay32.lib.
+ if have_library("libeay32", "CRYPTO_malloc") &&
+ have_library("ssleay32", "SSL_new")
+ return true
+ end
+
+ # LibreSSL: libcrypto-##.lib and libssl-##.lib, where ## is the ABI version
+ # number. We have to find the version number out by scanning libpath.
+ libpath = $LIBPATH.dup
+ libpath |= ENV["LIB"].split(File::PATH_SEPARATOR)
+ libpath.map! { |d| d.tr(File::ALT_SEPARATOR, File::SEPARATOR) }
+
+ ret = [
+ ["crypto", "CRYPTO_malloc"],
+ ["ssl", "SSL_new"]
+ ].all? do |base, func|
+ result = false
+ libs = ["lib#{base}-[0-9][0-9]", "lib#{base}-[0-9][0-9][0-9]"]
+ libs = Dir.glob(libs.map{|l| libpath.map{|d| File.join(d, l + ".*")}}.flatten).map{|path| File.basename(path, ".*")}.uniq
+ libs.each do |lib|
+ result = have_library(lib, func)
+ break if result
+ end
+ result
+ end
+ return ret if ret
+ end
+ return false
+end
+
def digest_conf(name, hdr = name, funcs = nil, types = nil)
unless with_config("bundled-#{name}")
cc = with_config("common-digest")
@@ -14,7 +66,7 @@ def digest_conf(name, hdr = name, funcs = nil, types = nil)
dir_config("openssl")
pkg_config("openssl")
require File.expand_path('../../openssl/deprecation', __FILE__)
- if have_library("crypto")
+ if find_openssl_library
funcs ||= name.upcase
funcs = Array(funcs)
types ||= funcs
diff --git a/ext/etc/depend b/ext/etc/depend
index 137ec83849..2d986c5913 100644
--- a/ext/etc/depend
+++ b/ext/etc/depend
@@ -11,6 +11,7 @@ etc.o: $(hdrdir)/ruby/encoding.h
etc.o: $(hdrdir)/ruby/intern.h
etc.o: $(hdrdir)/ruby/io.h
etc.o: $(hdrdir)/ruby/missing.h
+etc.o: $(hdrdir)/ruby/onigmo.h
etc.o: $(hdrdir)/ruby/oniguruma.h
etc.o: $(hdrdir)/ruby/ruby.h
etc.o: $(hdrdir)/ruby/st.h
diff --git a/ext/etc/etc.c b/ext/etc/etc.c
index 0d2d9af7cb..2dd4ed673e 100644
--- a/ext/etc/etc.c
+++ b/ext/etc/etc.c
@@ -215,9 +215,10 @@ etc_getpwnam(VALUE obj, VALUE nam)
{
#ifdef HAVE_GETPWENT
struct passwd *pwd;
+ const char *p = StringValueCStr(nam);
- SafeStringValue(nam);
- pwd = getpwnam(RSTRING_PTR(nam));
+ rb_check_safe_obj(nam);
+ pwd = getpwnam(p);
if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %"PRIsVALUE, nam);
return setup_passwd(pwd);
#else
@@ -458,9 +459,10 @@ etc_getgrnam(VALUE obj, VALUE nam)
{
#ifdef HAVE_GETGRENT
struct group *grp;
+ const char *p = StringValueCStr(nam);
- SafeStringValue(nam);
- grp = getgrnam(RSTRING_PTR(nam));
+ rb_check_safe_obj(nam);
+ grp = getgrnam(p);
if (grp == 0) rb_raise(rb_eArgError, "can't find group for %"PRIsVALUE, nam);
return setup_group(grp);
#else
@@ -625,8 +627,9 @@ VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
* Returns system configuration directory.
*
* This is typically "/etc", but is modified by the prefix used when Ruby was
- * compiled. For example, if Ruby is built and installed in /usr/local, returns
- * "/usr/local/etc".
+ * compiled. For example, if Ruby is built and installed in /usr/local,
+ * returns "/usr/local/etc" on other platforms than Windows.
+ * On Windows, this always returns the directory provided by the system.
*/
static VALUE
etc_sysconfdir(VALUE obj)
@@ -1012,7 +1015,7 @@ etc_nprocessors(VALUE obj)
ncpus = etc_nprocessors_affin();
if (ncpus != -1) {
- return INT2NUM(ncpus);
+ return INT2NUM(ncpus);
}
/* fallback to _SC_NPROCESSORS_ONLN */
#endif
diff --git a/ext/etc/etc.gemspec b/ext/etc/etc.gemspec
new file mode 100644
index 0000000000..322fd41e8b
--- /dev/null
+++ b/ext/etc/etc.gemspec
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+Gem::Specification.new do |spec|
+ spec.name = "etc"
+ spec.version = "1.0.1"
+ spec.authors = ["Yukihiro Matsumoto"]
+ spec.email = ["matz@ruby-lang.org"]
+
+ spec.summary = %q{Provides access to information typically stored in UNIX /etc directory.}
+ spec.description = %q{Provides access to information typically stored in UNIX /etc directory.}
+ spec.homepage = "https://github.com/ruby/etc"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = %w[
+ .gitignore
+ .travis.yml
+ Gemfile
+ LICENSE.txt
+ README.md
+ Rakefile
+ bin/console
+ bin/setup
+ etc.gemspec
+ ext/etc/etc.c
+ ext/etc/extconf.rb
+ ext/etc/mkconstants.rb
+ test/etc/test_etc.rb
+ ]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+ spec.extensions = %w{ext/etc/extconf.rb}
+
+ spec.required_ruby_version = ">= 2.3.0"
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+ spec.add_development_dependency "rake-compiler"
+ spec.add_development_dependency "test-unit"
+end
diff --git a/ext/etc/extconf.rb b/ext/etc/extconf.rb
index 11bc9458c0..435fbe7f3d 100644
--- a/ext/etc/extconf.rb
+++ b/ext/etc/extconf.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'mkmf'
headers = []
@@ -12,8 +12,10 @@ have_func("uname((struct utsname *)NULL)", headers)
have_func("getlogin")
have_func("getpwent")
have_func("getgrent")
-sysconfdir = RbConfig.expand(RbConfig::CONFIG["sysconfdir"].dup, "prefix"=>"", "DESTDIR"=>"")
-$defs.push("-DSYSCONFDIR=#{Shellwords.escape(sysconfdir.dump)}")
+if (sysconfdir = RbConfig::CONFIG["sysconfdir"] and
+ !RbConfig.expand(sysconfdir.dup, "prefix"=>"", "DESTDIR"=>"").empty?)
+ $defs.push("-DSYSCONFDIR=#{Shellwords.escape(sysconfdir.dump)}")
+end
have_func("sysconf")
have_func("confstr")
@@ -39,6 +41,12 @@ have_struct_member('struct passwd', 'pw_expire', 'pwd.h')
have_struct_member('struct passwd', 'pw_passwd', 'pwd.h')
have_struct_member('struct group', 'gr_passwd', 'grp.h')
+# for https://github.com/ruby/etc
+srcdir = File.expand_path("..", __FILE__)
+if !File.exist?("#{srcdir}/depend")
+ %x[#{RbConfig.ruby} #{srcdir}/mkconstants.rb -o #{srcdir}/constdefs.h]
+end
+
$distcleanfiles << "constdefs.h"
create_makefile("etc")
diff --git a/ext/etc/mkconstants.rb b/ext/etc/mkconstants.rb
index 0c4d17e6f9..a752d64519 100644
--- a/ext/etc/mkconstants.rb
+++ b/ext/etc/mkconstants.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'optparse'
require 'erb'
@@ -66,7 +66,15 @@ def each_name(pat)
}
end
-ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_const_decls")
+erb_new = lambda do |src, safe, trim|
+ if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
+ ERB.new(src, trim_mode: trim)
+ else
+ ERB.new(src, safe, trim)
+ end
+end
+
+erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_decls")
% each_const {|name, default_value|
#if !defined(<%=name%>)
# if defined(HAVE_CONST_<%=name.upcase%>)
@@ -80,7 +88,7 @@ ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_const_decls")
% }
EOS
-ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_const_defs")
+erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_defs")
% each_const {|name, default_value|
#if defined(<%=name%>)
% if comment = COMMENTS[name]
@@ -91,13 +99,13 @@ ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_const_defs")
% }
EOS
-header_result = ERB.new(<<'EOS', nil, '%').result(binding)
+header_result = erb_new.call(<<'EOS', nil, '%').result(binding)
/* autogenerated file */
<%= gen_const_decls %>
EOS
-result = ERB.new(<<'EOS', nil, '%').result(binding)
+result = erb_new.call(<<'EOS', nil, '%').result(binding)
/* autogenerated file */
#ifdef HAVE_LONG_LONG
diff --git a/ext/extmk.rb b/ext/extmk.rb
index 86174830b3..1389dc4117 100755
--- a/ext/extmk.rb
+++ b/ext/extmk.rb
@@ -6,27 +6,22 @@
$extension = nil
$extstatic = nil
$force_static = nil
-$install = nil
$destdir = nil
$dryrun = false
-$clean = nil
$nodynamic = nil
-$extinit = nil
$extobjs = []
$extflags = ""
$extlibs = nil
$extpath = nil
-$ignore = nil
$message = nil
$command_output = nil
-$configure_only = false
+$subconfigure = false
$progname = $0
alias $PROGRAM_NAME $0
alias $0 $progname
$extlist = []
-$compiled = {}
DUMMY_SIGNATURE = "***DUMMY MAKEFILE***"
@@ -107,7 +102,7 @@ def extract_makefile(makefile, keep = true)
srcs = Dir[File.join($srcdir, "*.{#{SRC_EXT.join(%q{,})}}")].map {|fn| File.basename(fn)}.sort
if !srcs.empty?
old_srcs = m[/^ORIG_SRCS[ \t]*=[ \t](.*)/, 1] or return false
- old_srcs.split.sort == srcs or return false
+ (old_srcs.split - srcs).empty? or return false
end
$target = target
$extconf_h = m[/^RUBY_EXTCONF_H[ \t]*=[ \t]*(\S+)/, 1]
@@ -132,12 +127,7 @@ def extract_makefile(makefile, keep = true)
true
end
-def extmake(target, basedir = (maybestatic = 'ext'))
- unless $configure_only || verbose?
- print "#{$message} #{target}\n"
- $stdout.flush
- end
-
+def extmake(target, basedir = 'ext', maybestatic = true)
FileUtils.mkpath target unless File.directory?(target)
begin
# don't build if parent library isn't build
@@ -167,12 +157,11 @@ def extmake(target, basedir = (maybestatic = 'ext'))
$objs = []
$srcs = []
$extso = []
- $compiled[target] = false
makefile = "./Makefile"
static = $static
$static = nil if noinstall = File.fnmatch?("-*", target)
ok = parent && File.exist?(makefile)
- if parent && !$ignore
+ if parent
rbconfig0 = RbConfig::CONFIG
mkconfig0 = CONFIG
rbconfig = {
@@ -208,14 +197,12 @@ def extmake(target, basedir = (maybestatic = 'ext'))
[conf, "#{$srcdir}/depend"].any? {|f| modified?(f, [t])})
then
ok = false
- if $configure_only
- if verbose?
- print "#{conf}\n" if conf
- else
- print "#{$message} #{target}\n"
- end
- $stdout.flush
+ if verbose?
+ print "#{conf}\n" if conf
+ else
+ print "#{$message} #{target}\n"
end
+ $stdout.flush
init_mkmf
Logging::logfile 'mkmf.log'
rm_f makefile
@@ -235,7 +222,6 @@ def extmake(target, basedir = (maybestatic = 'ext'))
rescue SystemExit
# ignore
rescue => error
- lineno = error.backtrace_locations[0].lineno
ok = false
ensure
rm_f "conftest*"
@@ -251,39 +237,27 @@ def extmake(target, basedir = (maybestatic = 'ext'))
return true if !error and target.start_with?("-")
- if parent
- message = "Failed to configure #{target}. It will not be installed."
- else
- message = "Skipped to configure #{target}. Its parent is not configured."
- end
- if Logging.log_opened?
- Logging::message(error.to_s) if error
- Logging::message(message)
+ message = nil
+ if error
+ loc = error.backtrace_locations[0]
+ message = "#{loc.absolute_path}:#{loc.lineno}: #{error.message}"
+ if Logging.log_opened?
+ Logging::message("#{message}\n\t#{error.backtrace.join("\n\t")}\n")
+ end
end
- message = error.message if error
- return parent ? [conf, lineno||0, message] : true
+ return [parent, message]
end
args = $mflags
unless $destdir.to_s.empty? or $mflags.defined?("DESTDIR")
args += ["DESTDIR=" + relative_from($destdir, "../"+prefix)]
end
if $static and ok and !$objs.empty? and !noinstall
- args += ["static"] unless $clean
+ args += ["static"]
$extlist.push [(maybestatic ? $static : false), target, $target, $preload]
end
FileUtils.rm_f(old_cleanfiles - $distcleanfiles - $cleanfiles)
FileUtils.rm_f(old_objs - $objs)
- unless $configure_only or system($make, *args)
- $ignore or $continue or return false
- end
- $compiled[target] = true
- if $clean
- FileUtils.rm_f("mkmf.log")
- if $clean != true
- FileUtils.rm_f([makefile, $extconf_h || "extconf.h"])
- end
- end
if $static
$extflags ||= ""
$extlibs ||= []
@@ -325,10 +299,6 @@ def extmake(target, basedir = (maybestatic = 'ext'))
true
end
-def compiled?(target)
- $compiled[target]
-end
-
def parse_args()
$mflags = []
$makeflags = [] # for make command to build ruby, so quoted
@@ -383,6 +353,7 @@ def parse_args()
$optparser.warn(e)
abort $optparser.to_s
end
+ $command_output or abort "--command-output option is mandatory"
$destdir ||= ''
@@ -418,18 +389,10 @@ parse_args()
if target = ARGV.shift and /^[a-z-]+$/ =~ target
$mflags.push(target)
case target
- when /^(dist|real)?(clean)$/
- target = $2
- $ignore ||= true
- $clean = $1 ? $1[0] : true
- when /^install\b/
- $install = true
- $ignore ||= true
- $mflags.unshift("INSTALL_PROG=install -c -p -m 0755",
- "INSTALL_DATA=install -c -p -m 0644",
- "MAKEDIRS=mkdir -p") if $dryrun
+ when /^(dist|real)?(clean)$/, /^install\b/
+ abort "#{target} is obsolete"
when /configure/
- $configure_only = true
+ $subconfigure = !ARGV.empty?
end
end
unless $message
@@ -448,12 +411,15 @@ elsif sep = config_string('BUILD_FILE_SEPARATOR')
else
$ruby = '$(topdir)/miniruby' + EXEEXT
end
-$ruby << " -I'$(topdir)'"
+$ruby = [$ruby]
+$ruby << "-I'$(topdir)'"
unless CROSS_COMPILING
- $ruby << " -I'$(top_srcdir)/lib'"
- $ruby << " -I'$(extout)/$(arch)' -I'$(extout)/common'" if $extout
+ $ruby << "-I'$(top_srcdir)/lib'"
+ $ruby << "-I'$(extout)/$(arch)'" << "-I'$(extout)/common'" if $extout
ENV["RUBYLIB"] = "-"
end
+topruby = $ruby
+$ruby = topruby.join(' ')
$mflags << "ruby=#$ruby"
MTIMES = [__FILE__, 'rbconfig.rb', srcdir+'/lib/mkmf.rb'].collect {|f| File.mtime(f)}
@@ -493,7 +459,13 @@ for dir in ["ext", File::join($top_srcdir, "ext")]
end
end unless $extstatic
-ext_prefix = "#{$top_srcdir}/ext"
+@gemname = nil
+if ARGV[0]
+ ext_prefix, exts = ARGV.shift.split('/', 2)
+ $extension = [exts] if exts
+ @gemname = exts if ext_prefix == 'gems'
+end
+ext_prefix = "#{$top_srcdir}/#{ext_prefix || 'ext'}"
exts = $static_ext.sort_by {|t, i| i}.collect {|t, i| t}
default_exclude_exts =
case
@@ -504,6 +476,7 @@ default_exclude_exts =
else
%w'*win32*'
end
+mandatory_exts = {}
withes, withouts = [["--with", nil], ["--without", default_exclude_exts]].collect {|w, d|
if !(w = %w[-extensions -ext].collect {|o|arg_config(w+o)}).any?
d ? proc {|c1| d.any?(&c1)} : proc {true}
@@ -511,84 +484,49 @@ withes, withouts = [["--with", nil], ["--without", default_exclude_exts]].collec
proc {true}
else
w = w.collect {|o| o.split(/,/)}.flatten
- w.collect! {|o| o == '+' ? d : o}.flatten! if d
+ w.collect! {|o| o == '+' ? d : o}.flatten!
proc {|c1| w.any?(&c1)}
end
}
cond = proc {|ext, *|
- cond1 = proc {|n| File.fnmatch(n, ext)}
- withes.call(cond1) and !withouts.call(cond1)
+ withes.call(proc {|n|
+ !n or (mandatory_exts[ext] = true if File.fnmatch(n, ext))
+ }) and
+ !withouts.call(proc {|n| File.fnmatch(n, ext)})
}
($extension || %w[*]).each do |e|
e = e.sub(/\A(?:\.\/)+/, '')
- exts |= Dir.glob("#{ext_prefix}/#{e}/**/extconf.rb").collect {|d|
+ incl, excl = Dir.glob("#{ext_prefix}/#{e}/**/extconf.rb").collect {|d|
d = File.dirname(d)
d.slice!(0, ext_prefix.length + 1)
d
- }.find_all {|ext|
+ }.partition {|ext|
with_config(ext, &cond)
- }.sort
+ }
+ incl.sort!
+ excl.sort!.collect! {|d| d+"/"}
+ nil while incl.reject! {|d| excl << d+"/" if excl.any? {|x| d.start_with?(x)}}
+ exts |= incl
if $LIBRUBYARG_SHARED.empty? and CONFIG["EXTSTATIC"] == "static"
exts.delete_if {|d| File.fnmatch?("-*", d)}
end
end
-
-if $extout
- extout = RbConfig.expand("#{$extout}", RbConfig::CONFIG.merge("topdir"=>$topdir))
- unless $ignore
- FileUtils.mkpath("#{extout}/gems")
- end
-end
-
-FileUtils.makedirs('gems')
-ext_prefix = "#$top_srcdir/gems"
-gems = Dir.glob(File.join(ext_prefix, ($extension || ''), '**/extconf.rb')).collect {|d|
- d = File.dirname(d)
- d.slice!(0, ext_prefix.length + 1)
- d
-}.find_all {|ext|
- with_config(ext, &cond)
-}.sort
+ext_prefix = File.basename(ext_prefix)
extend Module.new {
def timestamp_file(name, target_prefix = nil)
+ if @gemname and name == '$(TARGET_SO_DIR)'
+ name = "$(arch)/gems/#{@gemname}#{target_prefix}"
+ end
super.sub(%r[/\.extout\.(?:-\.)?], '/.')
end
def configuration(srcdir)
super << "EXTSO #{['=', $extso].join(' ')}\n"
end
-}
-
-dir = Dir.pwd
-FileUtils::makedirs('ext')
-Dir::chdir('ext')
-
-hdrdir = $hdrdir
-$hdrdir = ($top_srcdir = relative_from(srcdir, $topdir = "..")) + "/include"
-extso = []
-fails = []
-exts.each do |d|
- $static = $force_static ? true : $static_ext[d]
-
- if $ignore or !$nodynamic or $static
- result = extmake(d) or abort
- extso |= $extso
- fails << result unless result == true
- end
-end
-
-Dir.chdir('..')
-FileUtils::makedirs('gems')
-Dir.chdir('gems')
-extout = $extout
-unless gems.empty?
- def self.timestamp_file(name, target_prefix = nil)
- name = "$(arch)/gems/#{@gemname}#{target_prefix}" if name == '$(TARGET_SO_DIR)'
- super
- end
- def self.create_makefile(*args, &block)
+ def create_makefile(*args, &block)
+ return super unless @gemname
super(*args) do |conf|
conf.find do |s|
s.sub!(/^(TARGET_SO_DIR *= *)\$\(RUBYARCHDIR\)/) {
@@ -607,19 +545,31 @@ build_complete: $(build_complete)
$(build_complete): $(TARGET_SO)
$(Q) $(TOUCH) $@
+clean-so::
+ -$(Q)$(RM) $(build_complete)
}
conf
end
end
+}
+
+dir = Dir.pwd
+FileUtils::makedirs(ext_prefix)
+Dir::chdir(ext_prefix)
+
+hdrdir = $hdrdir
+$hdrdir = ($top_srcdir = relative_from(srcdir, $topdir = "..")) + "/include"
+extso = []
+fails = []
+exts.each do |d|
+ $static = $force_static ? true : $static_ext[d]
+
+ if !$nodynamic or $static
+ result = extmake(d, ext_prefix, !@gemname) or abort
+ extso |= $extso
+ fails << [d, result] unless result == true
+ end
end
-gems.each do |d|
- $extout = extout.dup
- @gemname = d[%r{\A[^/]+}]
- extmake(d, 'gems')
- extso |= $extso
-end
-$extout = extout
-Dir.chdir('../ext')
$top_srcdir = srcdir
$topdir = "."
@@ -630,32 +580,13 @@ extinit = Struct.new(:c, :o) {
super("#{src}.c", "#{src}.#{$OBJEXT}")
end
}.new("extinit")
-if $ignore
- FileUtils.rm_f(extinit.to_a) if $clean
- Dir.chdir ".."
- if $clean
- Dir.rmdir('ext') rescue nil
- if $extout
- FileUtils.rm_rf([extout+"/common", extout+"/include/ruby", extout+"/rdoc"])
- FileUtils.rm_rf(extout+"/"+CONFIG["arch"])
- if $clean != true
- FileUtils.rm_rf(extout+"/include/"+CONFIG["arch"])
- FileUtils.rm_f($mflags.defined?("INSTALLED_LIST")||ENV["INSTALLED_LIST"]||".installed.list")
- Dir.rmdir(extout+"/include") rescue nil
- Dir.rmdir(extout) rescue nil
- end
- end
- end
- exit
-end
-$extinit ||= ""
$extobjs ||= []
$extpath ||= []
$extflags ||= ""
$extlibs ||= []
+extinits = []
unless $extlist.empty?
- $extinit << "\n" unless $extinit.empty?
list = $extlist.dup
built = []
while e = list.shift
@@ -669,27 +600,11 @@ unless $extlist.empty?
next
end
base = File.basename(feature)
- $extinit << " init(Init_#{base}, \"#{feature}.so\");\n"
+ extinits << feature
$extobjs << format("ext/%s/%s.%s", target, base, $LIBEXT)
built << target
end
- src = %{\
-#include "ruby/ruby.h"
-
-#define init(func, name) { \\
- extern void func(void); \\
- ruby_init_ext(name, func); \\
-}
-
-void ruby_init_ext(const char *name, void (*init)(void));
-
-void Init_ext(void)\n{\n#$extinit}
-}
- if !modified?(extinit.c, MTIMES) || IO.read(extinit.c) != src
- open(extinit.c, "w") {|fe| fe.print src}
- end
-
$extpath.delete("$(topdir)")
$extflags = libpathflag($extpath) << " " << $extflags.strip
conf = [
@@ -699,7 +614,7 @@ void Init_ext(void)\n{\n#$extinit}
].map {|n, v|
"#{n}=#{v}" if v &&= v[/\S(?:.*\S)?/]
}.compact
- puts(*conf)
+ puts(*conf) unless $subconfigure
$stdout.flush
$mflags.concat(conf)
$makeflags.concat(conf)
@@ -723,9 +638,9 @@ $makeflags.uniq!
$mflags.unshift("topdir=#$topdir")
ENV.delete("RUBYOPT")
-if $configure_only and $command_output
- exts.map! {|d| "ext/#{d}/."}
- gems.map! {|d| "gems/#{d}/."}
+exts.map! {|d| "#{ext_prefix}/#{d}/."}
+FileUtils.makedirs(File.dirname($command_output))
+begin
atomic_write_open($command_output) do |mf|
mf.puts "V = 0"
mf.puts "Q1 = $(V:1=)"
@@ -750,12 +665,14 @@ if $configure_only and $command_output
puts
end
+ mf.macro "ruby", topruby
+ mf.macro "RUBY", ["$(ruby)"]
mf.macro "extensions", exts
- mf.macro "gems", gems
mf.macro "EXTOBJS", $extlist.empty? ? ["dmyext.#{$OBJEXT}"] : ["ext/extinit.#{$OBJEXT}", *$extobjs]
mf.macro "EXTLIBS", $extlibs
mf.macro "EXTSO", extso
mf.macro "EXTLDFLAGS", $extflags.split
+ mf.macro "EXTINITS", extinits
submakeopts = []
if enable_config("shared", $enable_shared)
submakeopts << 'DLDOBJS="$(EXTOBJS) $(EXTENCS)"'
@@ -767,21 +684,23 @@ if $configure_only and $command_output
submakeopts << 'EXTLIBS="$(EXTLIBS)"'
end
submakeopts << 'EXTLDFLAGS="$(EXTLDFLAGS)"'
+ submakeopts << 'EXTINITS="$(EXTINITS)"'
submakeopts << 'UPDATE_LIBRARIES="$(UPDATE_LIBRARIES)"'
submakeopts << 'SHOWFLAGS='
mf.macro "SUBMAKEOPTS", submakeopts
+ mf.macro "NOTE_MESG", %w[$(RUBY) $(top_srcdir)/tool/colorize.rb skip]
+ mf.macro "NOTE_NAME", %w[$(RUBY) $(top_srcdir)/tool/colorize.rb fail]
mf.puts
targets = %w[all install static install-so install-rb clean distclean realclean]
targets.each do |tgt|
mf.puts "#{tgt}: $(extensions:/.=/#{tgt})"
- mf.puts "#{tgt}: $(gems:/.=/#{tgt})" unless tgt == 'static'
mf.puts "#{tgt}: note" unless /clean\z/ =~ tgt
end
mf.puts
mf.puts "clean:\n\t-$(Q)$(RM) ext/extinit.#{$OBJEXT}"
mf.puts "distclean:\n\t-$(Q)$(RM) ext/extinit.c"
mf.puts
- mf.puts "#{rubies.join(' ')}: $(extensions:/.=/#{$force_static ? 'static' : 'all'}) $(gems:/.=/all)"
+ mf.puts "#{rubies.join(' ')}: $(extensions:/.=/#{$force_static ? 'static' : 'all'})"
submake = "$(Q)$(MAKE) $(MFLAGS) $(SUBMAKEOPTS)"
mf.puts "all static: #{rubies.join(' ')}\n"
$extobjs.each do |tgt|
@@ -792,7 +711,7 @@ if $configure_only and $command_output
mf.puts "#{tgt}:\n\t#{submake} $@"
end
mf.puts "libencs:\n\t$(Q)$(MAKE) -f enc.mk V=$(V) $@"
- mf.puts "ext/extinit.#{$OBJEXT}:\n\t$(Q)$(MAKE) $(MFLAGS) V=$(V) $@" if $static
+ mf.puts "ext/extinit.#{$OBJEXT}:\n\t$(Q)$(MAKE) $(MFLAGS) V=$(V) EXTINITS=\"$(EXTINITS)\" $@" if $static
mf.puts
if $gnumake == "yes"
submake = "$(MAKE) -C $(@D)"
@@ -801,10 +720,18 @@ if $configure_only and $command_output
config_string("exec") {|str| submake << str << " "}
submake << "$(MAKE)"
end
- gems = exts + gems
targets.each do |tgt|
- (tgt == 'static' ? exts : gems).each do |d|
- mf.puts "#{d[0..-2]}#{tgt}:\n\t$(Q)#{submake} $(MFLAGS) V=$(V) $(@F)"
+ exts.each do |d|
+ d = d[0..-2]
+ t = "#{d}#{tgt}"
+ if /^(dist|real)?clean$/ =~ tgt
+ deps = exts.select {|e|e.start_with?(d)}.map {|e|"#{e[0..-2]}#{tgt}"} - [t]
+ pd = ' ' + deps.join(' ') unless deps.empty?
+ 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)"
end
end
mf.puts "\n""extso:\n"
@@ -812,43 +739,32 @@ if $configure_only and $command_output
mf.puts "\n""note:\n"
unless fails.empty?
- mf.puts %Q<\t@echo "*** Following extensions failed to configure:">
- fails.each do |d, n, err|
- d = "#{d}:#{n}:"
- if err
- d << " " << err
+ abandon = false
+ mf.puts "note: note-body\n"
+ mf.puts "note-body:: note-header\n"
+ mf.puts "note-header:\n"
+ mf.puts %Q<\t@$(NOTE_MESG) "*** Following extensions are not compiled:">
+ mf.puts "note-body:: note-header\n"
+ fails.each do |ext, (parent, err)|
+ abandon ||= mandatory_exts[ext]
+ mf.puts %Q<\t@$(NOTE_NAME) "#{ext}:">
+ if parent
+ mf.puts %Q<\t@echo "\tCould not be configured. It will not be installed.">
+ err and err.scan(/.+/) do |ee|
+ mf.puts %Q<\t@echo "\t#{ee.gsub(/["`$^]/, '\\\\\\&')}">
+ end
+ mf.puts %Q<\t@echo "\tCheck #{ext_prefix}/#{ext}/mkmf.log for more details.">
+ else
+ mf.puts %Q<\t@echo "\tSkipped because its parent was not configured.">
end
- mf.puts %Q<\t@echo "#{d}">
end
- mf.puts %Q<\t@echo "*** Fix the problems, then remove these directories and try again if you want.">
- end
-
- end
-elsif $command_output
- message = "making #{rubies.join(', ')}"
- message = "echo #{message}"
- $mflags.concat(rubies)
- $makeflags.concat(rubies)
- cmd = $makeflags.map {|ss|ss.sub(/.*[$(){};\s].*/, %q['\&'])}.join(' ')
- open($command_output, 'wb') do |ff|
- case $command_output
- when /\.sh\z/
- ff.puts message, "rm -f \"$0\"; exec \"$@\" #{cmd}"
- when /\.bat\z/
- ["@echo off", message, "%* #{cmd}", "del %0 & exit %ERRORLEVEL%"].each do |ss|
- ff.print ss, "\r\n"
+ mf.puts "note:\n"
+ mf.puts %Q<\t@$(NOTE_MESG) "*** Fix the problems, then remove these directories and try again if you want.">
+ if abandon
+ mf.puts "\t""@exit 1"
end
- else
- ff.puts cmd
end
- ff.chmod(0755)
end
-elsif !$configure_only
- message = "making #{rubies.join(', ')}"
- puts message
- $stdout.flush
- $mflags.concat(rubies)
- system($make, *$mflags) or exit($?.exitstatus)
end
# :startdoc:
diff --git a/ext/fcntl/extconf.rb b/ext/fcntl/extconf.rb
index 35371ebe18..6998f77483 100644
--- a/ext/fcntl/extconf.rb
+++ b/ext/fcntl/extconf.rb
@@ -1,3 +1,3 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'mkmf'
create_makefile('fcntl')
diff --git a/ext/fcntl/fcntl.c b/ext/fcntl/fcntl.c
index 62780b78c0..c93694c431 100644
--- a/ext/fcntl/fcntl.c
+++ b/ext/fcntl/fcntl.c
@@ -68,7 +68,7 @@ Init_fcntl(void)
#ifdef F_DUPFD
/* Document-const: F_DUPFD
*
- * Duplicate a file descriptor to the mimimum unused file descriptor
+ * Duplicate a file descriptor to the minimum unused file descriptor
* greater than or equal to the argument.
*
* The close-on-exec flag of the duplicated file descriptor is set.
diff --git a/ext/fcntl/fcntl.gemspec b/ext/fcntl/fcntl.gemspec
new file mode 100644
index 0000000000..0e3194fbdc
--- /dev/null
+++ b/ext/fcntl/fcntl.gemspec
@@ -0,0 +1,25 @@
+# coding: utf-8
+# frozen_string_literal: true
+
+Gem::Specification.new do |spec|
+ spec.name = "fcntl"
+ spec.version = "1.0.0"
+ spec.authors = ["Yukihiro Matsumoto"]
+ spec.email = ["matz@ruby-lang.org"]
+
+ spec.summary = "Loads constants defined in the OS fcntl.h C header file"
+ spec.description = "Loads constants defined in the OS fcntl.h C header file"
+ spec.homepage = "https://github.com/ruby/fcntl"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = ["ext/fcntl/extconf.rb", "ext/fcntl/fcntl.c"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+ spec.extensions = "ext/fcntl/extconf.rb"
+ spec.required_ruby_version = ">= 2.3.0"
+
+ spec.add_development_dependency "bundler", "~> 1.14"
+ spec.add_development_dependency "rake", "~> 12"
+ spec.add_development_dependency "rake-compiler"
+end
diff --git a/ext/fiddle/closure.c b/ext/fiddle/closure.c
index f19091b29b..2044c836ea 100644
--- a/ext/fiddle/closure.c
+++ b/ext/fiddle/closure.c
@@ -1,6 +1,7 @@
#include <fiddle.h>
#include <ruby/thread.h>
-#include "internal.h" /* rb_thread_has_gvl_p */
+
+int ruby_thread_has_gvl_p(void); /* from internal.h */
VALUE cFiddleClosure;
@@ -13,10 +14,7 @@ typedef struct {
} fiddle_closure;
#if defined(USE_FFI_CLOSURE_ALLOC)
-#elif defined(__OpenBSD__) || defined(__APPLE__) || defined(__linux__)
-# define USE_FFI_CLOSURE_ALLOC 0
-#elif defined(RUBY_LIBFFI_MODVERSION) && RUBY_LIBFFI_MODVERSION < 3000005 && \
- (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_AMD64))
+#elif !defined(HAVE_FFI_CLOSURE_ALLOC)
# define USE_FFI_CLOSURE_ALLOC 0
#else
# define USE_FFI_CLOSURE_ALLOC 1
diff --git a/ext/fiddle/depend b/ext/fiddle/depend
index 470e15efef..7209469260 100644
--- a/ext/fiddle/depend
+++ b/ext/fiddle/depend
@@ -25,18 +25,20 @@ $(LIBFFI_DIR)/Makefile:
$(Q) $(MAKEDIRS) $(LIBFFI_DIR)
$(Q) $(CONFIGURE_LIBFFI)
-build-libffi: $(LIBFFI_A)
build-libffi $(LIBFFI_A):
- $(Q) $(SUBMAKE_LIBFFI)
+ $(Q) $(SUBMAKE_PRE) $(MAKE) $(SUBMAKE_ARG)
+clean-none:
clean-libffi:
- $(Q) $(SUBMAKE_LIBFFI) clean
+ $(Q) $(SUBMAKE_PRE) $(MAKE) $(SUBMAKE_ARG) clean
+distclean-none:
distclean-libffi:
- $(Q) $(SUBMAKE_LIBFFI) distclean
+ $(Q) $(SUBMAKE_PRE) $(MAKE) $(SUBMAKE_ARG) distclean
$(Q) $(RM) $(LIBFFI_DIR)/local.exp
$(Q) $(RUBY) -rfileutils -e "FileUtils.rmdir(Dir.glob(ARGV[0]+'/**/{,.*/}'), :parents=>true)" $(LIBFFI_DIR)
+realclean-none:
realclean-libffi:
$(Q) $(RMALL) $(LIBFFI_DIR)
@@ -58,6 +60,7 @@ closure.o: $(hdrdir)/ruby/encoding.h
closure.o: $(hdrdir)/ruby/intern.h
closure.o: $(hdrdir)/ruby/io.h
closure.o: $(hdrdir)/ruby/missing.h
+closure.o: $(hdrdir)/ruby/onigmo.h
closure.o: $(hdrdir)/ruby/oniguruma.h
closure.o: $(hdrdir)/ruby/ruby.h
closure.o: $(hdrdir)/ruby/st.h
@@ -139,6 +142,7 @@ pointer.o: $(hdrdir)/ruby/encoding.h
pointer.o: $(hdrdir)/ruby/intern.h
pointer.o: $(hdrdir)/ruby/io.h
pointer.o: $(hdrdir)/ruby/missing.h
+pointer.o: $(hdrdir)/ruby/onigmo.h
pointer.o: $(hdrdir)/ruby/oniguruma.h
pointer.o: $(hdrdir)/ruby/ruby.h
pointer.o: $(hdrdir)/ruby/st.h
diff --git a/ext/fiddle/extconf.rb b/ext/fiddle/extconf.rb
index 0359355193..f8a94d41e7 100644
--- a/ext/fiddle/extconf.rb
+++ b/ext/fiddle/extconf.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'mkmf'
# :stopdoc:
@@ -13,11 +13,21 @@ if ! bundle
if have_header(ffi_header = 'ffi.h')
true
elsif have_header(ffi_header = 'ffi/ffi.h')
- $defs.push(format('-DUSE_HEADER_HACKS'))
+ $defs.push('-DUSE_HEADER_HACKS')
true
end and (have_library('ffi') || have_library('libffi'))
end or
begin
+ # for https://github.com/ruby/fiddle
+ if bundle && File.exist?("../../bin/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
ver = bundle != false &&
Dir.glob("#{$srcdir}/libffi-*/")
.map {|n| File.basename(n)}
@@ -41,12 +51,12 @@ begin
libffi.lib = "#{libffi.builddir}/.libs"
libffi.a = "#{libffi.lib}/libffi_convenience.#{$LIBEXT}"
nowarn = CONFIG.merge("warnflags"=>"")
- libffi.cflags = RbConfig.expand("$(CFLAGS)", nowarn)
+ libffi.cflags = RbConfig.expand("$(CFLAGS)".dup, nowarn)
ver = ver[/libffi-(.*)/, 1]
FileUtils.mkdir_p(libffi.dir)
libffi.opt = CONFIG['configure_args'][/'(-C)'/, 1]
- libffi.ldflags = RbConfig.expand("$(LDFLAGS) #{libpathflag([relative_from($topdir, "..")])} #{$LIBRUBYARG}")
+ libffi.ldflags = RbConfig.expand("$(LDFLAGS) #{libpathflag([relative_from($topdir, "..")])} #{$LIBRUBYARG}".dup)
libffi.arch = RbConfig::CONFIG['host']
if $mswin
unless find_executable(as = /x64/ =~ libffi.arch ? "ml64" : "ml")
@@ -77,7 +87,7 @@ begin
args << libffi.opt if libffi.opt
args.concat %W[
CC=#{cc} CFLAGS=#{libffi.cflags}
- CXX=#{cxx} CXXFLAGS=#{RbConfig.expand("$(CXXFLAGS)", nowarn)}
+ CXX=#{cxx} CXXFLAGS=#{RbConfig.expand("$(CXXFLAGS)".dup, nowarn)}
LD=#{ld} LDFLAGS=#{libffi.ldflags}
]
@@ -104,8 +114,17 @@ end
if ver
ver = ver.gsub(/-rc\d+/, '') # If ver contains rc version, just ignored.
- ver = (ver.split('.') + [0,0])[0,3]
+ ver = (ver.split('.').map(&:to_i) + [0,0])[0,3]
$defs.push(%{-DRUBY_LIBFFI_MODVERSION=#{ '%d%03d%03d' % ver }})
+ warn "libffi_version: #{ver.join('.')}"
+end
+
+case
+when $mswin, $mingw, (ver && (ver <=> [3, 2]) >= 0)
+ $defs << "-DUSE_FFI_CLOSURE_ALLOC=1"
+when (ver && (ver <=> [3, 2]) < 0)
+else
+ have_func('ffi_closure_alloc', ffi_header)
end
have_header 'sys/mman.h'
@@ -132,7 +151,7 @@ types.each do |type, signed|
if /^\#define\s+SIZEOF_#{type}\s+(SIZEOF_(.+)|\d+)/ =~ config
if size = $2 and size != 'VOIDP'
size = types.fetch(size) {size}
- $defs << format("-DTYPE_%s=TYPE_%s", signed||type, size)
+ $defs << "-DTYPE_#{signed||type}=TYPE_#{size}"
end
if signed
check_signedness(type.downcase, "stddef.h")
@@ -149,9 +168,9 @@ create_makefile 'fiddle' do |conf|
if !libffi
next conf << "LIBFFI_CLEAN = none\n"
elsif $gnumake && !$nmake
- submake = "$(MAKE) -C $(LIBFFI_DIR)\n"
+ submake_arg = "-C $(LIBFFI_DIR)\n"
else
- submake = "cd $(LIBFFI_DIR) && \\\n\t\t" << "#{config_string("exec")} $(MAKE)".strip
+ submake_pre = "cd $(LIBFFI_DIR) && #{config_string("exec")}".strip
end
if $nmake
cmd = "$(RUBY) -C $(LIBFFI_DIR) #{libffi_config} --srcdir=$(LIBFFI_SRCDIR)"
@@ -170,7 +189,8 @@ create_makefile 'fiddle' do |conf|
LIBFFI_CFLAGS = #{libffi.cflags}
LIBFFI_LDFLAGS = #{libffi.ldflags}
FFI_H = $(LIBFFI_DIR)/include/ffi.h
- SUBMAKE_LIBFFI = #{submake}
+ SUBMAKE_PRE = #{submake_pre}
+ SUBMAKE_ARG = #{submake_arg}
LIBFFI_CLEAN = libffi
MK
end
diff --git a/ext/fiddle/extlibs b/ext/fiddle/extlibs
index 7d5fda5247..1f0c9348e6 100644
--- a/ext/fiddle/extlibs
+++ b/ext/fiddle/extlibs
@@ -1,2 +1,5 @@
-ftp://sourceware.org/pub/libffi/libffi-3.2.1.tar.gz md5:83b89587607e3eb65c70d361f13bab43
+https://ftp.osuosl.org/pub/blfs/conglomeration/libffi/libffi-3.2.1.tar.gz \
+ md5:83b89587607e3eb65c70d361f13bab43 \
+ sha512:980ca30a8d76f963fca722432b1fe5af77d7a4e4d2eac5144fbc5374d4c596609a293440573f4294207e1bdd9fda80ad1e1cafb2ffb543df5a275bc3bd546483 \
+ #
win32/libffi-3.2.1-mswin.patch -p0
diff --git a/ext/fiddle/fiddle.gemspec b/ext/fiddle/fiddle.gemspec
new file mode 100644
index 0000000000..b29f4ec788
--- /dev/null
+++ b/ext/fiddle/fiddle.gemspec
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+Gem::Specification.new do |spec|
+ spec.name = "fiddle"
+ spec.version = '1.0.0'
+ spec.authors = ["Aaron Patterson", "SHIBATA Hiroshi"]
+ spec.email = ["aaron@tenderlovemaking.com", "hsbt@ruby-lang.org"]
+
+ spec.summary = %q{A libffi wrapper for Ruby.}
+ spec.description = %q{A libffi wrapper for Ruby.}
+ spec.homepage = "https://github.com/ruby/fiddle"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "ext/fiddle/closure.c", "ext/fiddle/closure.h", "ext/fiddle/conversions.c", "ext/fiddle/conversions.h", "ext/fiddle/extconf.rb", "ext/fiddle/extlibs", "ext/fiddle/fiddle.c", "ext/fiddle/fiddle.h", "ext/fiddle/function.c", "ext/fiddle/function.h", "ext/fiddle/handle.c", "ext/fiddle/pointer.c", "ext/fiddle/win32/fficonfig.h", "ext/fiddle/win32/libffi-3.2.1-mswin.patch", "ext/fiddle/win32/libffi-config.rb", "ext/fiddle/win32/libffi.mk.tmpl", "fiddle.gemspec", "lib/fiddle.rb", "lib/fiddle/closure.rb", "lib/fiddle/cparser.rb", "lib/fiddle/function.rb", "lib/fiddle/import.rb", "lib/fiddle/pack.rb", "lib/fiddle/struct.rb", "lib/fiddle/types.rb", "lib/fiddle/value.rb"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.required_ruby_version = ">= 2.3.0"
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+ spec.add_development_dependency "rake-compiler"
+end
diff --git a/ext/fiddle/function.c b/ext/fiddle/function.c
index 8e280567db..bbd73e0f0a 100644
--- a/ext/fiddle/function.c
+++ b/ext/fiddle/function.c
@@ -113,7 +113,7 @@ initialize(int argc, VALUE argv[], VALUE self)
Check_Max_Args("args", len);
ary = rb_ary_subseq(args, 0, len);
for (i = 0; i < RARRAY_LEN(args); i++) {
- VALUE a = RARRAY_PTR(args)[i];
+ VALUE a = RARRAY_AREF(args, i);
int type = NUM2INT(a);
(void)INT2FFI_TYPE(type); /* raise */
if (INT2FIX(type) != a) rb_ary_store(ary, i, INT2FIX(type));
@@ -214,7 +214,7 @@ function_call(int argc, VALUE argv[], VALUE self)
args.values[i] = (void *)&generic_args[i];
}
args.values[argc] = NULL;
- args.fn = NUM2PTR(cfunc);
+ args.fn = (void(*)(void))NUM2PTR(cfunc);
(void)rb_thread_call_without_gvl(nogvl_ffi_call, &args, 0, 0);
diff --git a/ext/fiddle/lib/fiddle.rb b/ext/fiddle/lib/fiddle.rb
index 5208eb9328..e9aa7e50ae 100644
--- a/ext/fiddle/lib/fiddle.rb
+++ b/ext/fiddle/lib/fiddle.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'fiddle.so'
require 'fiddle/function'
require 'fiddle/closure'
diff --git a/ext/fiddle/lib/fiddle/closure.rb b/ext/fiddle/lib/fiddle/closure.rb
index 0b9adbb60a..c865a63c20 100644
--- a/ext/fiddle/lib/fiddle/closure.rb
+++ b/ext/fiddle/lib/fiddle/closure.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Fiddle
class Closure
diff --git a/ext/fiddle/lib/fiddle/cparser.rb b/ext/fiddle/lib/fiddle/cparser.rb
index 6b9da9fa7c..cd0a64fef5 100644
--- a/ext/fiddle/lib/fiddle/cparser.rb
+++ b/ext/fiddle/lib/fiddle/cparser.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Fiddle
# A mixin that provides methods for parsing C struct and prototype signatures.
#
@@ -21,6 +21,7 @@ module Fiddle
# Parses a C struct's members
#
# Example:
+ # require 'fiddle/import'
#
# include Fiddle::CParser
# #=> Object
@@ -66,6 +67,7 @@ module Fiddle
# be looked up.
#
# Example:
+ # require 'fiddle/import'
#
# include Fiddle::CParser
# #=> Object
@@ -102,6 +104,7 @@ module Fiddle
# value will be the C type to be looked up.
#
# Example:
+ # require 'fiddle/import'
#
# include Fiddle::CParser
# #=> Object
diff --git a/ext/fiddle/lib/fiddle/function.rb b/ext/fiddle/lib/fiddle/function.rb
index fcd90dfd26..dd5e04e417 100644
--- a/ext/fiddle/lib/fiddle/function.rb
+++ b/ext/fiddle/lib/fiddle/function.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Fiddle
class Function
# The ABI of the Function.
diff --git a/ext/fiddle/lib/fiddle/import.rb b/ext/fiddle/lib/fiddle/import.rb
index 09429f6631..178ebb8c76 100644
--- a/ext/fiddle/lib/fiddle/import.rb
+++ b/ext/fiddle/lib/fiddle/import.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'fiddle'
require 'fiddle/struct'
require 'fiddle/cparser'
@@ -115,8 +115,6 @@ module Fiddle
return SIZEOF_INT
when TYPE_LONG
return SIZEOF_LONG
- when TYPE_LONG_LONG
- return SIZEOF_LONG_LONG
when TYPE_FLOAT
return SIZEOF_FLOAT
when TYPE_DOUBLE
@@ -124,7 +122,12 @@ module Fiddle
when TYPE_VOIDP
return SIZEOF_VOIDP
else
- raise(DLError, "unknown type: #{ty}")
+ if defined?(TYPE_LONG_LONG) and
+ ty == TYPE_LONG_LONG
+ return SIZEOF_LONG_LONG
+ else
+ raise(DLError, "unknown type: #{ty}")
+ end
end
when Class
if( ty.instance_methods().include?(:to_ptr) )
@@ -154,7 +157,8 @@ module Fiddle
# :stopdoc:
CALL_TYPE_TO_ABI = Hash.new { |h, k|
raise RuntimeError, "unsupported call type: #{k}"
- }.merge({ :stdcall => (Function::STDCALL rescue Function::DEFAULT),
+ }.merge({ :stdcall => Function.const_defined?(:STDCALL) ? Function::STDCALL :
+ Function::DEFAULT,
:cdecl => Function::DEFAULT,
nil => Function::DEFAULT
}).freeze
diff --git a/ext/fiddle/lib/fiddle/pack.rb b/ext/fiddle/lib/fiddle/pack.rb
index 6301068450..22eccedb76 100644
--- a/ext/fiddle/lib/fiddle/pack.rb
+++ b/ext/fiddle/lib/fiddle/pack.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'fiddle'
module Fiddle
@@ -18,7 +18,7 @@ module Fiddle
}
PACK_MAP = {
- TYPE_VOIDP => ((SIZEOF_VOIDP == SIZEOF_LONG_LONG) ? "q" : "l!"),
+ TYPE_VOIDP => "l!",
TYPE_CHAR => "c",
TYPE_SHORT => "s!",
TYPE_INT => "i!",
@@ -48,6 +48,7 @@ module Fiddle
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
end
def align(addr, align)
@@ -80,10 +81,13 @@ module Fiddle
case SIZEOF_VOIDP
when SIZEOF_LONG
ary.pack(@template)
- when SIZEOF_LONG_LONG
- ary.pack(@template)
else
- raise(RuntimeError, "sizeof(void*)?")
+ if defined?(TYPE_LONG_LONG) and
+ SIZEOF_VOIDP == SIZEOF_LONG_LONG
+ ary.pack(@template)
+ else
+ raise(RuntimeError, "sizeof(void*)?")
+ end
end
end
@@ -91,17 +95,20 @@ module Fiddle
case SIZEOF_VOIDP
when SIZEOF_LONG
ary.join().unpack(@template)
- when SIZEOF_LONG_LONG
- ary.join().unpack(@template)
else
- raise(RuntimeError, "sizeof(void*)?")
+ if defined?(TYPE_LONG_LONG) and
+ SIZEOF_VOIDP == SIZEOF_LONG_LONG
+ ary.join().unpack(@template)
+ else
+ raise(RuntimeError, "sizeof(void*)?")
+ end
end
end
private
def parse_types(types)
- @template = ""
+ @template = "".dup
addr = 0
types.each{|t|
orig_addr = addr
diff --git a/ext/fiddle/lib/fiddle/struct.rb b/ext/fiddle/lib/fiddle/struct.rb
index 233a987269..7c0dedb39f 100644
--- a/ext/fiddle/lib/fiddle/struct.rb
+++ b/ext/fiddle/lib/fiddle/struct.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'fiddle'
require 'fiddle/value'
require 'fiddle/pack'
diff --git a/ext/fiddle/lib/fiddle/types.rb b/ext/fiddle/lib/fiddle/types.rb
index 8a72635a69..8dc811d3e4 100644
--- a/ext/fiddle/lib/fiddle/types.rb
+++ b/ext/fiddle/lib/fiddle/types.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Fiddle
# Adds Windows type aliases to the including class for use with
# Fiddle::Importer.
diff --git a/ext/fiddle/lib/fiddle/value.rb b/ext/fiddle/lib/fiddle/value.rb
index ac318cf2c4..01fec1c206 100644
--- a/ext/fiddle/lib/fiddle/value.rb
+++ b/ext/fiddle/lib/fiddle/value.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'fiddle'
module Fiddle
@@ -13,10 +13,13 @@ module Fiddle
[val].pack("i!").unpack("I!")[0]
when TYPE_LONG
[val].pack("l!").unpack("L!")[0]
- when TYPE_LONG_LONG
- [val].pack("q").unpack("Q")[0]
else
- val
+ if defined?(TYPE_LONG_LONG) and
+ ty.abs == TYPE_LONG_LONG
+ [val].pack("q").unpack("Q")[0]
+ else
+ val
+ end
end
end
@@ -30,10 +33,13 @@ module Fiddle
[val].pack("I!").unpack("i!")[0]
when TYPE_LONG
[val].pack("L!").unpack("l!")[0]
- when TYPE_LONG_LONG
- [val].pack("Q").unpack("q")[0]
else
- val
+ if defined?(TYPE_LONG_LONG) and
+ ty.abs == TYPE_LONG_LONG
+ [val].pack("Q").unpack("q")[0]
+ else
+ val
+ end
end
end
@@ -75,10 +81,13 @@ module Fiddle
case SIZEOF_VOIDP
when SIZEOF_LONG
return [arg].pack("p").unpack("l!")[0]
- when SIZEOF_LONG_LONG
- return [arg].pack("p").unpack("q")[0]
else
- raise(RuntimeError, "sizeof(void*)?")
+ if defined?(SIZEOF_LONG_LONG) and
+ SIZEOF_VOIDP == SIZEOF_LONG_LONG
+ return [arg].pack("p").unpack("q")[0]
+ else
+ raise(RuntimeError, "sizeof(void*)?")
+ end
end
end
when Float, Integer
diff --git a/ext/fiddle/pointer.c b/ext/fiddle/pointer.c
index 932bc576c5..2fb21f832e 100644
--- a/ext/fiddle/pointer.c
+++ b/ext/fiddle/pointer.c
@@ -440,7 +440,7 @@ rb_fiddle_ptr_inspect(VALUE self)
TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
return rb_sprintf("#<%"PRIsVALUE":%p ptr=%p size=%ld free=%p>",
- RB_OBJ_CLASSNAME(self), data, data->ptr, data->size, data->free);
+ RB_OBJ_CLASSNAME(self), (void *)data, data->ptr, data->size, (void *)data->free);
}
/*
@@ -677,6 +677,7 @@ rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val)
void
Init_fiddle_pointer(void)
{
+#undef rb_intern
id_to_ptr = rb_intern("to_ptr");
/* Document-class: Fiddle::Pointer
diff --git a/ext/fiddle/win32/libffi-config.rb b/ext/fiddle/win32/libffi-config.rb
index 7a32a91517..6abc9b2c02 100755
--- a/ext/fiddle/win32/libffi-config.rb
+++ b/ext/fiddle/win32/libffi-config.rb
@@ -1,5 +1,5 @@
#!/usr/bin/ruby
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'fileutils'
basedir = File.dirname(__FILE__)
diff --git a/ext/gdbm/gdbm.c b/ext/gdbm/gdbm.c
index 709f466cd8..05eb450381 100644
--- a/ext/gdbm/gdbm.c
+++ b/ext/gdbm/gdbm.c
@@ -83,6 +83,10 @@ static VALUE rb_cGDBM, rb_eGDBMError, rb_eGDBMFatalError;
#define MY_BLOCK_SIZE (2048)
#define MY_FATAL_FUNC rb_gdbm_fatal
+
+NORETURN(static void rb_gdbm_fatal(const char *msg));
+NORETURN(static void closed_dbm(void));
+
static void
rb_gdbm_fatal(const char *msg)
{
@@ -102,7 +106,6 @@ closed_dbm(void)
#define GetDBM(obj, dbmp) do {\
TypedData_Get_Struct((obj), struct dbmdata, &dbm_type, (dbmp));\
- if ((dbmp) == 0) closed_dbm();\
if ((dbmp)->di_dbm == 0) closed_dbm();\
} while (0)
@@ -115,21 +118,18 @@ static void
free_dbm(void *ptr)
{
struct dbmdata *dbmp = ptr;
- if (dbmp) {
- if (dbmp->di_dbm) gdbm_close(dbmp->di_dbm);
- xfree(dbmp);
- }
+ if (dbmp->di_dbm)
+ gdbm_close(dbmp->di_dbm);
+ xfree(dbmp);
}
static size_t
memsize_dbm(const void *ptr)
{
- size_t size = 0;
const struct dbmdata *dbmp = ptr;
- if (dbmp) {
- size += sizeof(*dbmp);
- if (dbmp->di_dbm) size += DBM_SIZEOF_DBM;
- }
+ size_t size = sizeof(*dbmp);
+ if (dbmp->di_dbm)
+ size += DBM_SIZEOF_DBM;
return size;
}
@@ -170,8 +170,6 @@ fgdbm_closed(VALUE obj)
struct dbmdata *dbmp;
TypedData_Get_Struct(obj, struct dbmdata, &dbm_type, dbmp);
- if (dbmp == 0)
- return Qtrue;
if (dbmp->di_dbm == 0)
return Qtrue;
@@ -181,7 +179,9 @@ fgdbm_closed(VALUE obj)
static VALUE
fgdbm_s_alloc(VALUE klass)
{
- return TypedData_Wrap_Struct(klass, &dbm_type, 0);
+ struct dbmdata *dbmp;
+
+ return TypedData_Make_Struct(klass, struct dbmdata, &dbm_type, dbmp);
}
/*
@@ -215,6 +215,7 @@ fgdbm_initialize(int argc, VALUE *argv, VALUE obj)
struct dbmdata *dbmp;
int mode, flags = 0;
+ TypedData_Get_Struct(obj, struct dbmdata, &dbm_type, dbmp);
if (rb_scan_args(argc, argv, "12", &file, &vmode, &vflags) == 1) {
mode = 0666; /* default value */
}
@@ -228,7 +229,7 @@ fgdbm_initialize(int argc, VALUE *argv, VALUE obj)
if (!NIL_P(vflags))
flags = NUM2INT(vflags);
- SafeStringValue(file);
+ FilePathValue(file);
#ifdef GDBM_CLOEXEC
/* GDBM_CLOEXEC is available since gdbm 1.10. */
@@ -268,9 +269,8 @@ fgdbm_initialize(int argc, VALUE *argv, VALUE obj)
rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
}
- dbmp = ALLOC(struct dbmdata);
- free_dbm(DATA_PTR(obj));
- DATA_PTR(obj) = dbmp;
+ if (dbmp->di_dbm)
+ gdbm_close(dbmp->di_dbm);
dbmp->di_dbm = dbm;
dbmp->di_size = -1;
@@ -334,7 +334,7 @@ rb_gdbm_fetch2(GDBM_FILE dbm, VALUE keystr)
datum key;
long len;
- StringValue(keystr);
+ ExportStringValue(keystr);
len = RSTRING_LEN(keystr);
if (TOO_LONG(len)) return Qnil;
key.dptr = RSTRING_PTR(keystr);
@@ -450,7 +450,7 @@ fgdbm_key(VALUE obj, VALUE valstr)
GDBM_FILE dbm;
VALUE keystr, valstr2;
- StringValue(valstr);
+ ExportStringValue(valstr);
GetDBM2(obj, dbmp, dbm);
for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
keystr = rb_gdbm_nextkey(dbm, keystr)) {
@@ -538,7 +538,7 @@ rb_gdbm_delete(VALUE obj, VALUE keystr)
long len;
rb_gdbm_modify(obj);
- StringValue(keystr);
+ ExportStringValue(keystr);
len = RSTRING_LEN(keystr);
if (TOO_LONG(len)) return Qnil;
key.dptr = RSTRING_PTR(keystr);
@@ -725,8 +725,8 @@ fgdbm_store(VALUE obj, VALUE keystr, VALUE valstr)
GDBM_FILE dbm;
rb_gdbm_modify(obj);
- StringValue(keystr);
- StringValue(valstr);
+ ExportStringValue(keystr);
+ ExportStringValue(valstr);
key.dptr = RSTRING_PTR(keystr);
key.dsize = RSTRING_LENINT(keystr);
@@ -991,7 +991,7 @@ fgdbm_has_key(VALUE obj, VALUE keystr)
GDBM_FILE dbm;
long len;
- StringValue(keystr);
+ ExportStringValue(keystr);
len = RSTRING_LENINT(keystr);
if (TOO_LONG(len)) return Qfalse;
key.dptr = RSTRING_PTR(keystr);
@@ -1018,7 +1018,7 @@ fgdbm_has_value(VALUE obj, VALUE valstr)
GDBM_FILE dbm;
VALUE keystr, valstr2;
- StringValue(valstr);
+ ExportStringValue(valstr);
GetDBM2(obj, dbmp, dbm);
for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
keystr = rb_gdbm_nextkey(dbm, keystr)) {
diff --git a/ext/gdbm/gdbm.gemspec b/ext/gdbm/gdbm.gemspec
new file mode 100644
index 0000000000..849e289f9d
--- /dev/null
+++ b/ext/gdbm/gdbm.gemspec
@@ -0,0 +1,26 @@
+# coding: utf-8
+# frozen_string_literal: true
+
+Gem::Specification.new do |spec|
+ spec.name = "gdbm"
+ spec.version = "2.0.0"
+ spec.authors = ["Yukihiro Matsumoto"]
+ spec.email = ["matz@ruby-lang.org"]
+
+ spec.summary = "Ruby extension for GNU dbm."
+ spec.description = "Ruby extension for GNU dbm."
+ spec.homepage = "https://github.com/ruby/gdbm"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = ["ext/gdbm/extconf.rb", "ext/gdbm/gdbm.c"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+ spec.extensions = ["ext/gdbm/extconf.rb"]
+ spec.required_ruby_version = ">= 2.3.0"
+
+ spec.add_development_dependency "bundler", "~> 1.14"
+ spec.add_development_dependency "rake", "~> 10.0"
+ spec.add_development_dependency "rake-compiler"
+ spec.add_development_dependency "test-unit"
+end
diff --git a/ext/io/console/console.c b/ext/io/console/console.c
index dbbfbb7463..54ff34492e 100644
--- a/ext/io/console/console.c
+++ b/ext/io/console/console.c
@@ -14,12 +14,6 @@
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
-#ifndef RARRAY_CONST_PTR
-# define RARRAY_CONST_PTR(ary) RARRAY_PTR(ary)
-#endif
-#ifndef HAVE_RB_FUNCALLV
-# define rb_funcallv rb_funcall2
-#endif
#if defined HAVE_TERMIOS_H
# include <termios.h>
@@ -103,10 +97,6 @@ rb_f_send(int argc, VALUE *argv, VALUE recv)
}
#endif
-#ifndef HAVE_RB_SYM2STR
-# define rb_sym2str(sym) rb_id2str(SYM2ID(sym))
-#endif
-
typedef struct {
int vmin;
int vtime;
@@ -307,6 +297,14 @@ ttymode(VALUE io, VALUE (*func)(VALUE), void (*setter)(conmode *, void *), void
*
* will read and return a line without echo back and line editing.
*
+ * The parameter +min+ specifies the minimum number of bytes that
+ * should be received when a read operation is performed. (default: 1)
+ *
+ * The parameter +time+ specifies the timeout in _seconds_ with a
+ * precision of 1/10 of a second. (default: 0)
+ *
+ * Refer to the manual page of termios for further details.
+ *
* You must require 'io/console' to use this method.
*/
static VALUE
@@ -324,6 +322,8 @@ console_raw(int argc, VALUE *argv, VALUE io)
*
* If the terminal mode needs to be back, use io.raw { ... }.
*
+ * See IO#raw for details on the parameters.
+ *
* You must require 'io/console' to use this method.
*/
static VALUE
@@ -397,6 +397,8 @@ getc_call(VALUE io)
*
* Reads and returns a character in raw mode.
*
+ * See IO#raw for details on the parameters.
+ *
* You must require 'io/console' to use this method.
*/
static VALUE
@@ -531,16 +533,23 @@ console_set_winsize(VALUE io, VALUE size)
#if defined _WIN32
HANDLE wh;
int newrow, newcol;
+ BOOL ret;
#endif
VALUE row, col, xpixel, ypixel;
const VALUE *sz;
int fd;
+ long sizelen;
GetOpenFile(io, fptr);
size = rb_Array(size);
- rb_check_arity(RARRAY_LENINT(size), 2, 4);
+ if ((sizelen = RARRAY_LEN(size)) != 2 && sizelen != 4) {
+ rb_raise(rb_eArgError,
+ "wrong number of arguments (given %ld, expected 2 or 4)",
+ sizelen);
+ }
sz = RARRAY_CONST_PTR(size);
- row = sz[0], col = sz[1], xpixel = sz[2], ypixel = sz[3];
+ row = sz[0], col = sz[1], xpixel = ypixel = Qnil;
+ if (sizelen == 4) xpixel = sz[2], ypixel = sz[3];
fd = GetWriteFD(fptr);
#if defined TIOCSWINSZ
ws.ws_row = ws.ws_col = ws.ws_xpixel = ws.ws_ypixel = 0;
@@ -562,17 +571,21 @@ console_set_winsize(VALUE io, VALUE size)
if (!GetConsoleScreenBufferInfo(wh, &ws)) {
rb_syserr_fail(LAST_ERROR, "GetConsoleScreenBufferInfo");
}
- if ((ws.dwSize.X < newcol && (ws.dwSize.X = newcol, 1)) ||
- (ws.dwSize.Y < newrow && (ws.dwSize.Y = newrow, 1))) {
- if (!SetConsoleScreenBufferSize(wh, ws.dwSize)) {
- rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo");
- }
- }
+ ws.dwSize.X = newcol;
+ ret = SetConsoleScreenBufferSize(wh, ws.dwSize);
ws.srWindow.Left = 0;
ws.srWindow.Top = 0;
- ws.srWindow.Right = newcol;
- ws.srWindow.Bottom = newrow;
- if (!SetConsoleWindowInfo(wh, FALSE, &ws.srWindow)) {
+ ws.srWindow.Right = newcol-1;
+ ws.srWindow.Bottom = newrow-1;
+ if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) {
+ rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo");
+ }
+ /* retry when shrinking buffer after shrunk window */
+ if (!ret && !SetConsoleScreenBufferSize(wh, ws.dwSize)) {
+ rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo");
+ }
+ /* remove scrollbar if possible */
+ if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) {
rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo");
}
#endif
diff --git a/ext/io/console/depend b/ext/io/console/depend
index 400ae81a96..821b28d3fc 100644
--- a/ext/io/console/depend
+++ b/ext/io/console/depend
@@ -7,6 +7,7 @@ console.o: $(hdrdir)/ruby/encoding.h
console.o: $(hdrdir)/ruby/intern.h
console.o: $(hdrdir)/ruby/io.h
console.o: $(hdrdir)/ruby/missing.h
+console.o: $(hdrdir)/ruby/onigmo.h
console.o: $(hdrdir)/ruby/oniguruma.h
console.o: $(hdrdir)/ruby/ruby.h
console.o: $(hdrdir)/ruby/st.h
@@ -25,7 +26,7 @@ win32_vk.inc: win32_vk.list
-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* $< \
- | sed 's/(int)(long)&((\(struct stringpool_t\) *\*)0)->\(stringpool_[a-z0-9]*\)/offsetof(\1, \2)/g' \
+ | sed -f $(top_srcdir)/tool/gperf.sed \
) > $(@F)
.SUFFIXES: .chksum .list .inc
diff --git a/ext/io/console/extconf.rb b/ext/io/console/extconf.rb
index be536dff9f..a6049da667 100644
--- a/ext/io/console/extconf.rb
+++ b/ext/io/console/extconf.rb
@@ -17,11 +17,12 @@ else
end
if ok
have_header("sys/ioctl.h") if hdr
- have_func("rb_funcallv")
- have_func("rb_sym2str")
# rb_check_hash_type: 1.9.3
# rb_io_get_write_io: 1.9.1
# rb_cloexec_open: 2.0.0
+ # rb_funcallv: 2.1.0
+ # RARRAY_CONST_PTR: 2.1.0
+ # rb_sym2str: 2.2.0
$defs << "-D""ENABLE_IO_GETPASS=1"
create_makefile("io/console") {|conf|
conf << "\n""VK_HEADER = #{vk_header}\n"
diff --git a/ext/io/console/io-console.gemspec b/ext/io/console/io-console.gemspec
index 27e1a11e3a..ea50214712 100644
--- a/ext/io/console/io-console.gemspec
+++ b/ext/io/console/io-console.gemspec
@@ -1,5 +1,5 @@
# -*- ruby -*-
-_VERSION = "0.4.6"
+_VERSION = "0.4.7"
date = %w$Date:: $[1]
Gem::Specification.new do |s|
@@ -9,13 +9,16 @@ Gem::Specification.new do |s|
s.summary = "Console interface"
s.email = "nobu@ruby-lang.org"
s.description = "add console capabilities to IO instances."
- s.required_ruby_version = ">= 2.0.0"
- s.homepage = "https://www.ruby-lang.org"
+ s.required_ruby_version = ">= 2.2.0"
+ s.homepage = "https://github.com/ruby/io-console"
s.authors = ["Nobu Nakada"]
s.require_path = %[lib]
- s.files = %w[console.c depend extconf.rb lib/console/size.rb win32_vk.inc]
- s.extensions = %w[extconf.rb]
+ s.files = %w[ext/io/console/console.c ext/io/console/extconf.rb lib/console/size.rb ext/io/console/win32_vk.inc]
+ s.extensions = %w[ext/io/console/extconf.rb]
s.license = "BSD-2-Clause"
s.cert_chain = %w[certs/nobu.pem]
s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/
+
+ s.add_development_dependency 'rake-compiler'
+ s.add_development_dependency 'rake-compiler-dock', ">= 0.6.1"
end
diff --git a/ext/io/console/lib/console/size.rb b/ext/io/console/lib/console/size.rb
index f17206dfcf..14b9a74b22 100644
--- a/ext/io/console/lib/console/size.rb
+++ b/ext/io/console/lib/console/size.rb
@@ -10,7 +10,7 @@ end
begin
require 'io/console'
rescue LoadError
- class IO
+ class << IO
alias console_size default_console_size
end
else
diff --git a/ext/io/console/win32_vk.inc b/ext/io/console/win32_vk.inc
index a098158e27..cbec7bef15 100644
--- a/ext/io/console/win32_vk.inc
+++ b/ext/io/console/win32_vk.inc
@@ -479,7 +479,7 @@
#ifndef VK_OEM_CLEAR
# define VK_OEM_CLEAR UNDEFINED_VK
#endif
-/* C code produced by gperf version 3.0.4 */
+/* 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 */
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
@@ -506,13 +506,14 @@
&& ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
&& ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
/* The character set is not based on ISO-646. */
-error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#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 *, unsigned int*/);
#line 5 "win32_vk.list"
struct vktable;
/* maximum key range = 245, duplicates = 0 */
@@ -545,9 +546,7 @@ static unsigned char gperf_downcase[256] =
#ifndef GPERF_CASE_STRCMP
#define GPERF_CASE_STRCMP 1
static int
-gperf_case_strcmp (s1, s2)
- register const char *s1;
- register const char *s2;
+gperf_case_strcmp (register const char *s1, register const char *s2)
{
for (;;)
{
@@ -560,15 +559,15 @@ gperf_case_strcmp (s1, s2)
}
#endif
-#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) || defined(__GNUC_STDC_INLINE__)
-inline
-#elif defined(__GNUC__)
+#ifdef __GNUC__
__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
#endif
static unsigned int
-hash (str, len)
- register const char *str;
- register unsigned int len;
+hash (register const char *str, register size_t len)
{
static const unsigned short asso_values[] =
{
@@ -599,7 +598,7 @@ hash (str, len)
257, 257, 257, 257, 257, 257, 257, 257, 257, 257,
257, 257, 257, 257, 257, 257, 257, 257
};
- register int hval = len;
+ register unsigned int hval = (unsigned int)len;
switch (hval)
{
@@ -661,7 +660,7 @@ hash (str, len)
hval += asso_values[(unsigned char)str[0]];
break;
}
- return hval;
+ return (unsigned int)hval;
}
struct stringpool_t
@@ -991,16 +990,8 @@ static const struct stringpool_t stringpool_contents =
"DIVIDE"
};
#define stringpool ((const char *) &stringpool_contents)
-#ifdef __GNUC__
-__inline
-#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
-__attribute__ ((__gnu_inline__))
-#endif
-#endif
const struct vktable *
-console_win32_vk (str, len)
- register const char *str;
- register unsigned int len;
+console_win32_vk (register const char *str, register size_t len)
{
enum
{
@@ -1016,375 +1007,375 @@ console_win32_vk (str, len)
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1},
#line 40 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str12), VK_UP},
+ {gperf_offsetof(stringpool, 12), VK_UP},
#line 52 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str13), VK_APPS},
+ {gperf_offsetof(stringpool, 13), VK_APPS},
#line 159 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str14), VK_CRSEL},
+ {gperf_offsetof(stringpool, 14), VK_CRSEL},
#line 34 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str15), VK_SPACE},
+ {gperf_offsetof(stringpool, 15), VK_SPACE},
#line 95 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str16), VK_SCROLL},
+ {gperf_offsetof(stringpool, 16), VK_SCROLL},
#line 29 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str17), VK_ESCAPE},
+ {gperf_offsetof(stringpool, 17), VK_ESCAPE},
#line 9 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str18), VK_CANCEL},
+ {gperf_offsetof(stringpool, 18), VK_CANCEL},
#line 32 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str19), VK_ACCEPT},
+ {gperf_offsetof(stringpool, 19), VK_ACCEPT},
#line 66 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str20), VK_SEPARATOR},
+ {gperf_offsetof(stringpool, 20), VK_SEPARATOR},
#line 43 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str21), VK_SELECT},
+ {gperf_offsetof(stringpool, 21), VK_SELECT},
#line 18 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str22), VK_CONTROL},
+ {gperf_offsetof(stringpool, 22), VK_CONTROL},
#line 166 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str23), VK_OEM_CLEAR},
+ {gperf_offsetof(stringpool, 23), VK_OEM_CLEAR},
#line 145 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str24), VK_OEM_RESET},
+ {gperf_offsetof(stringpool, 24), VK_OEM_RESET},
#line 155 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str25), VK_OEM_AUTO},
+ {gperf_offsetof(stringpool, 25), VK_OEM_AUTO},
#line 151 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str26), VK_OEM_CUSEL},
+ {gperf_offsetof(stringpool, 26), VK_OEM_CUSEL},
{-1},
#line 22 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str28), VK_KANA},
+ {gperf_offsetof(stringpool, 28), VK_KANA},
#line 127 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str29), VK_OEM_PLUS},
+ {gperf_offsetof(stringpool, 29), VK_OEM_PLUS},
#line 35 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str30), VK_PRIOR},
+ {gperf_offsetof(stringpool, 30), VK_PRIOR},
#line 152 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str31), VK_OEM_ATTN},
+ {gperf_offsetof(stringpool, 31), VK_OEM_ATTN},
#line 20 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str32), VK_PAUSE},
+ {gperf_offsetof(stringpool, 32), VK_PAUSE},
#line 13 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str33), VK_BACK},
+ {gperf_offsetof(stringpool, 33), VK_BACK},
#line 144 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str34), VK_PACKET},
+ {gperf_offsetof(stringpool, 34), VK_PACKET},
#line 105 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str35), VK_RCONTROL},
+ {gperf_offsetof(stringpool, 35), VK_RCONTROL},
#line 104 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str36), VK_LCONTROL},
+ {gperf_offsetof(stringpool, 36), VK_LCONTROL},
#line 37 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str37), VK_END},
+ {gperf_offsetof(stringpool, 37), VK_END},
#line 38 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str38), VK_HOME},
+ {gperf_offsetof(stringpool, 38), VK_HOME},
#line 44 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str39), VK_PRINT},
+ {gperf_offsetof(stringpool, 39), VK_PRINT},
#line 94 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str40), VK_NUMLOCK},
+ {gperf_offsetof(stringpool, 40), VK_NUMLOCK},
#line 39 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str41), VK_LEFT},
+ {gperf_offsetof(stringpool, 41), VK_LEFT},
#line 25 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str42), VK_JUNJA},
+ {gperf_offsetof(stringpool, 42), VK_JUNJA},
#line 19 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str43), VK_MENU},
+ {gperf_offsetof(stringpool, 43), VK_MENU},
#line 150 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str44), VK_OEM_WSCTRL},
+ {gperf_offsetof(stringpool, 44), VK_OEM_WSCTRL},
#line 156 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str45), VK_OEM_ENLW},
+ {gperf_offsetof(stringpool, 45), VK_OEM_ENLW},
#line 36 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str46), VK_NEXT},
+ {gperf_offsetof(stringpool, 46), VK_NEXT},
#line 51 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str47), VK_RWIN},
+ {gperf_offsetof(stringpool, 47), VK_RWIN},
#line 50 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str48), VK_LWIN},
+ {gperf_offsetof(stringpool, 48), VK_LWIN},
#line 21 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str49), VK_CAPITAL},
+ {gperf_offsetof(stringpool, 49), VK_CAPITAL},
#line 49 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str50), VK_HELP},
+ {gperf_offsetof(stringpool, 50), VK_HELP},
#line 164 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str51), VK_NONAME},
+ {gperf_offsetof(stringpool, 51), VK_NONAME},
#line 8 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str52), VK_RBUTTON},
+ {gperf_offsetof(stringpool, 52), VK_RBUTTON},
#line 7 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str53), VK_LBUTTON},
+ {gperf_offsetof(stringpool, 53), VK_LBUTTON},
#line 96 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str54), VK_OEM_NEC_EQUAL},
+ {gperf_offsetof(stringpool, 54), VK_OEM_NEC_EQUAL},
{-1},
#line 47 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str56), VK_INSERT},
+ {gperf_offsetof(stringpool, 56), VK_INSERT},
#line 27 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str57), VK_HANJA},
+ {gperf_offsetof(stringpool, 57), VK_HANJA},
{-1}, {-1},
#line 46 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str60), VK_SNAPSHOT},
+ {gperf_offsetof(stringpool, 60), VK_SNAPSHOT},
#line 158 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str61), VK_ATTN},
+ {gperf_offsetof(stringpool, 61), VK_ATTN},
#line 14 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str62), VK_TAB},
+ {gperf_offsetof(stringpool, 62), VK_TAB},
#line 157 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str63), VK_OEM_BACKTAB},
+ {gperf_offsetof(stringpool, 63), VK_OEM_BACKTAB},
#line 143 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str64), VK_ICO_CLEAR},
+ {gperf_offsetof(stringpool, 64), VK_ICO_CLEAR},
#line 30 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str65), VK_CONVERT},
+ {gperf_offsetof(stringpool, 65), VK_CONVERT},
#line 16 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str66), VK_RETURN},
+ {gperf_offsetof(stringpool, 66), VK_RETURN},
#line 146 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str67), VK_OEM_JUMP},
+ {gperf_offsetof(stringpool, 67), VK_OEM_JUMP},
{-1}, {-1}, {-1},
#line 111 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str71), VK_BROWSER_STOP},
+ {gperf_offsetof(stringpool, 71), VK_BROWSER_STOP},
#line 26 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str72), VK_FINAL},
+ {gperf_offsetof(stringpool, 72), VK_FINAL},
#line 163 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str73), VK_ZOOM},
+ {gperf_offsetof(stringpool, 73), VK_ZOOM},
#line 28 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str74), VK_KANJI},
+ {gperf_offsetof(stringpool, 74), VK_KANJI},
#line 48 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str75), VK_DELETE},
+ {gperf_offsetof(stringpool, 75), VK_DELETE},
#line 128 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str76), VK_OEM_COMMA},
+ {gperf_offsetof(stringpool, 76), VK_OEM_COMMA},
#line 67 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str77), VK_SUBTRACT},
+ {gperf_offsetof(stringpool, 77), VK_SUBTRACT},
{-1},
#line 10 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str79), VK_MBUTTON},
+ {gperf_offsetof(stringpool, 79), VK_MBUTTON},
#line 78 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str80), VK_F9},
+ {gperf_offsetof(stringpool, 80), VK_F9},
#line 17 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str81), VK_SHIFT},
+ {gperf_offsetof(stringpool, 81), VK_SHIFT},
#line 103 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str82), VK_RSHIFT},
+ {gperf_offsetof(stringpool, 82), VK_RSHIFT},
#line 102 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str83), VK_LSHIFT},
+ {gperf_offsetof(stringpool, 83), VK_LSHIFT},
#line 65 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str84), VK_ADD},
+ {gperf_offsetof(stringpool, 84), VK_ADD},
#line 31 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str85), VK_NONCONVERT},
+ {gperf_offsetof(stringpool, 85), VK_NONCONVERT},
#line 160 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str86), VK_EXSEL},
+ {gperf_offsetof(stringpool, 86), VK_EXSEL},
#line 126 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str87), VK_OEM_1},
+ {gperf_offsetof(stringpool, 87), VK_OEM_1},
#line 138 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str88), VK_OEM_AX},
+ {gperf_offsetof(stringpool, 88), VK_OEM_AX},
#line 108 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str89), VK_BROWSER_BACK},
+ {gperf_offsetof(stringpool, 89), VK_BROWSER_BACK},
#line 137 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str90), VK_OEM_8},
+ {gperf_offsetof(stringpool, 90), VK_OEM_8},
#line 129 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str91), VK_OEM_MINUS},
+ {gperf_offsetof(stringpool, 91), VK_OEM_MINUS},
#line 162 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str92), VK_PLAY},
+ {gperf_offsetof(stringpool, 92), VK_PLAY},
#line 131 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str93), VK_OEM_2},
+ {gperf_offsetof(stringpool, 93), VK_OEM_2},
#line 15 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str94), VK_CLEAR},
+ {gperf_offsetof(stringpool, 94), VK_CLEAR},
#line 99 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str95), VK_OEM_FJ_TOUROKU},
+ {gperf_offsetof(stringpool, 95), VK_OEM_FJ_TOUROKU},
#line 147 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str96), VK_OEM_PA1},
+ {gperf_offsetof(stringpool, 96), VK_OEM_PA1},
#line 140 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str97), VK_ICO_HELP},
+ {gperf_offsetof(stringpool, 97), VK_ICO_HELP},
#line 112 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str98), VK_BROWSER_SEARCH},
+ {gperf_offsetof(stringpool, 98), VK_BROWSER_SEARCH},
#line 53 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str99), VK_SLEEP},
+ {gperf_offsetof(stringpool, 99), VK_SLEEP},
{-1},
#line 70 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str101), VK_F1},
+ {gperf_offsetof(stringpool, 101), VK_F1},
#line 148 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str102), VK_OEM_PA2},
+ {gperf_offsetof(stringpool, 102), VK_OEM_PA2},
#line 154 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str103), VK_OEM_COPY},
+ {gperf_offsetof(stringpool, 103), VK_OEM_COPY},
#line 77 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str104), VK_F8},
+ {gperf_offsetof(stringpool, 104), VK_F8},
#line 88 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str105), VK_F19},
+ {gperf_offsetof(stringpool, 105), VK_F19},
#line 41 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str106), VK_RIGHT},
+ {gperf_offsetof(stringpool, 106), VK_RIGHT},
#line 71 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str107), VK_F2},
+ {gperf_offsetof(stringpool, 107), VK_F2},
#line 135 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str108), VK_OEM_6},
+ {gperf_offsetof(stringpool, 108), VK_OEM_6},
#line 87 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str109), VK_F18},
+ {gperf_offsetof(stringpool, 109), VK_F18},
{-1},
#line 117 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str111), VK_VOLUME_UP},
+ {gperf_offsetof(stringpool, 111), VK_VOLUME_UP},
{-1}, {-1},
#line 120 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str114), VK_MEDIA_STOP},
+ {gperf_offsetof(stringpool, 114), VK_MEDIA_STOP},
#line 130 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str115), VK_OEM_PERIOD},
+ {gperf_offsetof(stringpool, 115), VK_OEM_PERIOD},
{-1},
#line 161 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str117), VK_EREOF},
+ {gperf_offsetof(stringpool, 117), VK_EREOF},
{-1}, {-1}, {-1},
#line 114 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str121), VK_BROWSER_HOME},
+ {gperf_offsetof(stringpool, 121), VK_BROWSER_HOME},
#line 75 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str122), VK_F6},
+ {gperf_offsetof(stringpool, 122), VK_F6},
{-1},
#line 110 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str124), VK_BROWSER_REFRESH},
+ {gperf_offsetof(stringpool, 124), VK_BROWSER_REFRESH},
{-1},
#line 165 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str126), VK_PA1},
+ {gperf_offsetof(stringpool, 126), VK_PA1},
#line 142 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str127), VK_PROCESSKEY},
+ {gperf_offsetof(stringpool, 127), VK_PROCESSKEY},
#line 68 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str128), VK_DECIMAL},
+ {gperf_offsetof(stringpool, 128), VK_DECIMAL},
#line 132 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str129), VK_OEM_3},
+ {gperf_offsetof(stringpool, 129), VK_OEM_3},
#line 107 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str130), VK_RMENU},
+ {gperf_offsetof(stringpool, 130), VK_RMENU},
#line 106 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str131), VK_LMENU},
+ {gperf_offsetof(stringpool, 131), VK_LMENU},
#line 98 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str132), VK_OEM_FJ_MASSHOU},
+ {gperf_offsetof(stringpool, 132), VK_OEM_FJ_MASSHOU},
#line 54 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str133), VK_NUMPAD0},
+ {gperf_offsetof(stringpool, 133), VK_NUMPAD0},
#line 24 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str134), VK_HANGUL},
+ {gperf_offsetof(stringpool, 134), VK_HANGUL},
#line 63 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str135), VK_NUMPAD9},
+ {gperf_offsetof(stringpool, 135), VK_NUMPAD9},
#line 23 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str136), VK_HANGEUL},
+ {gperf_offsetof(stringpool, 136), VK_HANGEUL},
#line 134 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str137), VK_OEM_5},
+ {gperf_offsetof(stringpool, 137), VK_OEM_5},
#line 149 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str138), VK_OEM_PA3},
+ {gperf_offsetof(stringpool, 138), VK_OEM_PA3},
#line 115 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str139), VK_VOLUME_MUTE},
+ {gperf_offsetof(stringpool, 139), VK_VOLUME_MUTE},
#line 133 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str140), VK_OEM_4},
+ {gperf_offsetof(stringpool, 140), VK_OEM_4},
#line 122 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str141), VK_LAUNCH_MAIL},
+ {gperf_offsetof(stringpool, 141), VK_LAUNCH_MAIL},
#line 97 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str142), VK_OEM_FJ_JISHO},
+ {gperf_offsetof(stringpool, 142), VK_OEM_FJ_JISHO},
#line 72 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str143), VK_F3},
+ {gperf_offsetof(stringpool, 143), VK_F3},
#line 101 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str144), VK_OEM_FJ_ROYA},
+ {gperf_offsetof(stringpool, 144), VK_OEM_FJ_ROYA},
#line 100 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str145), VK_OEM_FJ_LOYA},
+ {gperf_offsetof(stringpool, 145), VK_OEM_FJ_LOYA},
{-1},
#line 42 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str147), VK_DOWN},
+ {gperf_offsetof(stringpool, 147), VK_DOWN},
{-1},
#line 153 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str149), VK_OEM_FINISH},
+ {gperf_offsetof(stringpool, 149), VK_OEM_FINISH},
{-1},
#line 74 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str151), VK_F5},
+ {gperf_offsetof(stringpool, 151), VK_F5},
{-1},
#line 136 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str153), VK_OEM_7},
+ {gperf_offsetof(stringpool, 153), VK_OEM_7},
#line 73 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str154), VK_F4},
+ {gperf_offsetof(stringpool, 154), VK_F4},
#line 86 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str155), VK_F17},
+ {gperf_offsetof(stringpool, 155), VK_F17},
#line 55 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str156), VK_NUMPAD1},
+ {gperf_offsetof(stringpool, 156), VK_NUMPAD1},
#line 141 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str157), VK_ICO_00},
+ {gperf_offsetof(stringpool, 157), VK_ICO_00},
{-1},
#line 62 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str159), VK_NUMPAD8},
+ {gperf_offsetof(stringpool, 159), VK_NUMPAD8},
{-1}, {-1},
#line 56 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str162), VK_NUMPAD2},
+ {gperf_offsetof(stringpool, 162), VK_NUMPAD2},
{-1},
#line 124 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str164), VK_LAUNCH_APP1},
+ {gperf_offsetof(stringpool, 164), VK_LAUNCH_APP1},
#line 109 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str165), VK_BROWSER_FORWARD},
+ {gperf_offsetof(stringpool, 165), VK_BROWSER_FORWARD},
{-1},
#line 76 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str167), VK_F7},
+ {gperf_offsetof(stringpool, 167), VK_F7},
{-1}, {-1},
#line 125 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str170), VK_LAUNCH_APP2},
+ {gperf_offsetof(stringpool, 170), VK_LAUNCH_APP2},
#line 64 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str171), VK_MULTIPLY},
+ {gperf_offsetof(stringpool, 171), VK_MULTIPLY},
{-1}, {-1},
#line 45 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str174), VK_EXECUTE},
+ {gperf_offsetof(stringpool, 174), VK_EXECUTE},
{-1},
#line 113 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str176), VK_BROWSER_FAVORITES},
+ {gperf_offsetof(stringpool, 176), VK_BROWSER_FAVORITES},
#line 60 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str177), VK_NUMPAD6},
+ {gperf_offsetof(stringpool, 177), VK_NUMPAD6},
{-1},
#line 85 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str179), VK_F16},
+ {gperf_offsetof(stringpool, 179), VK_F16},
{-1}, {-1},
#line 79 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str182), VK_F10},
+ {gperf_offsetof(stringpool, 182), VK_F10},
{-1}, {-1},
#line 116 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str185), VK_VOLUME_DOWN},
+ {gperf_offsetof(stringpool, 185), VK_VOLUME_DOWN},
{-1}, {-1},
#line 89 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str188), VK_F20},
+ {gperf_offsetof(stringpool, 188), VK_F20},
#line 119 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str189), VK_MEDIA_PREV_TRACK},
+ {gperf_offsetof(stringpool, 189), VK_MEDIA_PREV_TRACK},
{-1},
#line 33 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str191), VK_MODECHANGE},
+ {gperf_offsetof(stringpool, 191), VK_MODECHANGE},
{-1}, {-1}, {-1}, {-1}, {-1},
#line 83 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str197), VK_F14},
+ {gperf_offsetof(stringpool, 197), VK_F14},
#line 57 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str198), VK_NUMPAD3},
+ {gperf_offsetof(stringpool, 198), VK_NUMPAD3},
#line 11 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str199), VK_XBUTTON1},
+ {gperf_offsetof(stringpool, 199), VK_XBUTTON1},
{-1}, {-1}, {-1},
#line 93 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str203), VK_F24},
+ {gperf_offsetof(stringpool, 203), VK_F24},
{-1},
#line 12 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str205), VK_XBUTTON2},
+ {gperf_offsetof(stringpool, 205), VK_XBUTTON2},
#line 59 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str206), VK_NUMPAD5},
+ {gperf_offsetof(stringpool, 206), VK_NUMPAD5},
{-1}, {-1},
#line 58 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str209), VK_NUMPAD4},
+ {gperf_offsetof(stringpool, 209), VK_NUMPAD4},
{-1}, {-1}, {-1}, {-1}, {-1},
#line 121 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str215), VK_MEDIA_PLAY_PAUSE},
+ {gperf_offsetof(stringpool, 215), VK_MEDIA_PLAY_PAUSE},
{-1},
#line 123 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str217), VK_LAUNCH_MEDIA_SELECT},
+ {gperf_offsetof(stringpool, 217), VK_LAUNCH_MEDIA_SELECT},
#line 80 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str218), VK_F11},
+ {gperf_offsetof(stringpool, 218), VK_F11},
{-1},
#line 139 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str220), VK_OEM_102},
+ {gperf_offsetof(stringpool, 220), VK_OEM_102},
#line 118 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str221), VK_MEDIA_NEXT_TRACK},
+ {gperf_offsetof(stringpool, 221), VK_MEDIA_NEXT_TRACK},
#line 61 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str222), VK_NUMPAD7},
+ {gperf_offsetof(stringpool, 222), VK_NUMPAD7},
{-1},
#line 90 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str224), VK_F21},
+ {gperf_offsetof(stringpool, 224), VK_F21},
{-1},
#line 82 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str226), VK_F13},
+ {gperf_offsetof(stringpool, 226), VK_F13},
{-1}, {-1},
#line 81 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str229), VK_F12},
+ {gperf_offsetof(stringpool, 229), VK_F12},
{-1}, {-1},
#line 92 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str232), VK_F23},
+ {gperf_offsetof(stringpool, 232), VK_F23},
{-1}, {-1},
#line 91 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str235), VK_F22},
+ {gperf_offsetof(stringpool, 235), VK_F22},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#line 84 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str242), VK_F15},
+ {gperf_offsetof(stringpool, 242), VK_F15},
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
{-1}, {-1}, {-1}, {-1},
#line 69 "win32_vk.list"
- {offsetof(struct stringpool_t, stringpool_str256), VK_DIVIDE}
+ {gperf_offsetof(stringpool, 256), VK_DIVIDE}
};
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
{
- register int key = hash (str, len);
+ register unsigned int key = hash (str, len);
- if (key <= MAX_HASH_VALUE && key >= 0)
+ if (key <= MAX_HASH_VALUE)
{
register int o = wordlist[key].ofs;
if (o >= 0)
diff --git a/ext/io/console/win32_vk.list b/ext/io/console/win32_vk.list
index 28bc9545ec..7909a4d1f0 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(const char *, unsigned int);
+static const struct vktable *console_win32_vk(/*!ANSI{*/const char *, unsigned int/*}!ANSI*/);
%}
struct vktable
%%
diff --git a/ext/io/nonblock/depend b/ext/io/nonblock/depend
index 13f445abf5..4402898de6 100644
--- a/ext/io/nonblock/depend
+++ b/ext/io/nonblock/depend
@@ -7,6 +7,7 @@ nonblock.o: $(hdrdir)/ruby/encoding.h
nonblock.o: $(hdrdir)/ruby/intern.h
nonblock.o: $(hdrdir)/ruby/io.h
nonblock.o: $(hdrdir)/ruby/missing.h
+nonblock.o: $(hdrdir)/ruby/onigmo.h
nonblock.o: $(hdrdir)/ruby/oniguruma.h
nonblock.o: $(hdrdir)/ruby/ruby.h
nonblock.o: $(hdrdir)/ruby/st.h
diff --git a/ext/io/nonblock/nonblock.c b/ext/io/nonblock/nonblock.c
index 2509329f6c..1c0bdc68e7 100644
--- a/ext/io/nonblock/nonblock.c
+++ b/ext/io/nonblock/nonblock.c
@@ -1,6 +1,6 @@
/**********************************************************************
- io/wait.c -
+ io/nonblock.c -
$Author$
created at: Tue Jul 14 21:53:18 2009
diff --git a/ext/io/wait/depend b/ext/io/wait/depend
index 8440965df0..f509dcd8a4 100644
--- a/ext/io/wait/depend
+++ b/ext/io/wait/depend
@@ -7,6 +7,7 @@ wait.o: $(hdrdir)/ruby/encoding.h
wait.o: $(hdrdir)/ruby/intern.h
wait.o: $(hdrdir)/ruby/io.h
wait.o: $(hdrdir)/ruby/missing.h
+wait.o: $(hdrdir)/ruby/onigmo.h
wait.o: $(hdrdir)/ruby/oniguruma.h
wait.o: $(hdrdir)/ruby/ruby.h
wait.o: $(hdrdir)/ruby/st.h
diff --git a/ext/json/fbuffer/fbuffer.h b/ext/json/fbuffer/fbuffer.h
index 5a0a27cda5..dc8f406b5b 100644
--- a/ext/json/fbuffer/fbuffer.h
+++ b/ext/json/fbuffer/fbuffer.h
@@ -12,9 +12,6 @@
#define RFLOAT_VALUE(val) (RFLOAT(val)->value)
#endif
-#ifndef RARRAY_PTR
-#define RARRAY_PTR(ARRAY) RARRAY(ARRAY)->ptr
-#endif
#ifndef RARRAY_LEN
#define RARRAY_LEN(ARRAY) RARRAY(ARRAY)->len
#endif
diff --git a/ext/json/generator/depend b/ext/json/generator/depend
index 556259c1c8..54e8ae3eb4 100644
--- a/ext/json/generator/depend
+++ b/ext/json/generator/depend
@@ -9,6 +9,7 @@ generator.o: $(hdrdir)/ruby/defines.h
generator.o: $(hdrdir)/ruby/encoding.h
generator.o: $(hdrdir)/ruby/intern.h
generator.o: $(hdrdir)/ruby/missing.h
+generator.o: $(hdrdir)/ruby/onigmo.h
generator.o: $(hdrdir)/ruby/oniguruma.h
generator.o: $(hdrdir)/ruby/re.h
generator.o: $(hdrdir)/ruby/regex.h
diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c
index ef85bb7337..f061267f0d 100644
--- a/ext/json/generator/generator.c
+++ b/ext/json/generator/generator.c
@@ -308,7 +308,7 @@ static char *fstrndup(const char *ptr, unsigned long len) {
char *result;
if (len <= 0) return NULL;
result = ALLOC_N(char, len);
- memccpy(result, ptr, 0, len);
+ memcpy(result, ptr, len);
return result;
}
@@ -1062,7 +1062,7 @@ static VALUE cState_indent_set(VALUE self, VALUE indent)
}
} else {
if (state->indent) ruby_xfree(state->indent);
- state->indent = strdup(RSTRING_PTR(indent));
+ state->indent = fstrndup(RSTRING_PTR(indent), len);
state->indent_len = len;
}
return Qnil;
@@ -1100,7 +1100,7 @@ static VALUE cState_space_set(VALUE self, VALUE space)
}
} else {
if (state->space) ruby_xfree(state->space);
- state->space = strdup(RSTRING_PTR(space));
+ state->space = fstrndup(RSTRING_PTR(space), len);
state->space_len = len;
}
return Qnil;
@@ -1136,7 +1136,7 @@ static VALUE cState_space_before_set(VALUE self, VALUE space_before)
}
} else {
if (state->space_before) ruby_xfree(state->space_before);
- state->space_before = strdup(RSTRING_PTR(space_before));
+ state->space_before = fstrndup(RSTRING_PTR(space_before), len);
state->space_before_len = len;
}
return Qnil;
@@ -1173,7 +1173,7 @@ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
}
} else {
if (state->object_nl) ruby_xfree(state->object_nl);
- state->object_nl = strdup(RSTRING_PTR(object_nl));
+ state->object_nl = fstrndup(RSTRING_PTR(object_nl), len);
state->object_nl_len = len;
}
return Qnil;
@@ -1208,7 +1208,7 @@ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
}
} else {
if (state->array_nl) ruby_xfree(state->array_nl);
- state->array_nl = strdup(RSTRING_PTR(array_nl));
+ state->array_nl = fstrndup(RSTRING_PTR(array_nl), len);
state->array_nl_len = len;
}
return Qnil;
@@ -1335,6 +1335,7 @@ static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_l
*/
void Init_generator(void)
{
+#undef rb_intern
rb_require("json/common");
mJSON = rb_define_module("JSON");
diff --git a/ext/json/generator/generator.h b/ext/json/generator/generator.h
index 900b4d58f3..c367a6209a 100644
--- a/ext/json/generator/generator.h
+++ b/ext/json/generator/generator.h
@@ -1,7 +1,6 @@
#ifndef _GENERATOR_H_
#define _GENERATOR_H_
-#include <string.h>
#include <math.h>
#include <ctype.h>
diff --git a/ext/json/json.gemspec b/ext/json/json.gemspec
index 2c304ef918..1c18efbedc 100644
--- a/ext/json/json.gemspec
+++ b/ext/json/json.gemspec
Binary files differ
diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb
index 8997def28b..b65ed87f98 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.0.2'
+ VERSION = '2.1.0'
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/depend b/ext/json/parser/depend
index 927cd892bf..2ffd904475 100644
--- a/ext/json/parser/depend
+++ b/ext/json/parser/depend
@@ -9,6 +9,7 @@ parser.o: $(hdrdir)/ruby/defines.h
parser.o: $(hdrdir)/ruby/encoding.h
parser.o: $(hdrdir)/ruby/intern.h
parser.o: $(hdrdir)/ruby/missing.h
+parser.o: $(hdrdir)/ruby/onigmo.h
parser.o: $(hdrdir)/ruby/oniguruma.h
parser.o: $(hdrdir)/ruby/ruby.h
parser.o: $(hdrdir)/ruby/st.h
diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c
index 0dae674c88..027fbcc26f 100644
--- a/ext/json/parser/parser.c
+++ b/ext/json/parser/parser.c
@@ -91,18 +91,20 @@ static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
static VALUE CNaN, CInfinity, CMinusInfinity;
+static VALUE cBigDecimal = Qundef;
static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
i_chr, i_max_nesting, i_allow_nan, i_symbolize_names,
- i_object_class, i_array_class, i_key_p, i_deep_const_get, i_match,
- i_match_string, i_aset, i_aref, i_leftshift;
+ i_object_class, i_array_class, i_decimal_class, i_key_p,
+ i_deep_const_get, i_match, i_match_string, i_aset, i_aref,
+ i_leftshift, i_new, i_BigDecimal;
-#line 124 "parser.rl"
+#line 125 "parser.rl"
-#line 106 "parser.c"
+#line 107 "parser.c"
enum {JSON_object_start = 1};
enum {JSON_object_first_final = 27};
enum {JSON_object_error = 0};
@@ -110,7 +112,7 @@ enum {JSON_object_error = 0};
enum {JSON_object_en_main = 1};
-#line 165 "parser.rl"
+#line 166 "parser.rl"
static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
@@ -126,14 +128,14 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
*result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class);
-#line 130 "parser.c"
+#line 131 "parser.c"
{
cs = JSON_object_start;
}
-#line 180 "parser.rl"
+#line 181 "parser.rl"
-#line 137 "parser.c"
+#line 138 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -161,7 +163,7 @@ case 2:
goto st2;
goto st0;
tr2:
-#line 147 "parser.rl"
+#line 148 "parser.rl"
{
char *np;
json->parsing_name = 1;
@@ -174,7 +176,7 @@ st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
-#line 178 "parser.c"
+#line 179 "parser.c"
switch( (*p) ) {
case 13: goto st3;
case 32: goto st3;
@@ -241,7 +243,7 @@ case 8:
goto st8;
goto st0;
tr11:
-#line 132 "parser.rl"
+#line 133 "parser.rl"
{
VALUE v = Qnil;
char *np = JSON_parse_value(json, p, pe, &v, current_nesting);
@@ -261,7 +263,7 @@ st9:
if ( ++p == pe )
goto _test_eof9;
case 9:
-#line 265 "parser.c"
+#line 266 "parser.c"
switch( (*p) ) {
case 13: goto st9;
case 32: goto st9;
@@ -350,14 +352,14 @@ case 18:
goto st9;
goto st18;
tr4:
-#line 155 "parser.rl"
+#line 156 "parser.rl"
{ p--; {p++; cs = 27; goto _out;} }
goto st27;
st27:
if ( ++p == pe )
goto _test_eof27;
case 27:
-#line 361 "parser.c"
+#line 362 "parser.c"
goto st0;
st19:
if ( ++p == pe )
@@ -455,7 +457,7 @@ case 26:
_out: {}
}
-#line 181 "parser.rl"
+#line 182 "parser.rl"
if (cs >= JSON_object_first_final) {
if (json->create_additions) {
@@ -480,7 +482,7 @@ case 26:
-#line 484 "parser.c"
+#line 485 "parser.c"
enum {JSON_value_start = 1};
enum {JSON_value_first_final = 29};
enum {JSON_value_error = 0};
@@ -488,7 +490,7 @@ enum {JSON_value_error = 0};
enum {JSON_value_en_main = 1};
-#line 281 "parser.rl"
+#line 282 "parser.rl"
static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
@@ -496,14 +498,14 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul
int cs = EVIL;
-#line 500 "parser.c"
+#line 501 "parser.c"
{
cs = JSON_value_start;
}
-#line 288 "parser.rl"
+#line 289 "parser.rl"
-#line 507 "parser.c"
+#line 508 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -537,14 +539,14 @@ st0:
cs = 0;
goto _out;
tr2:
-#line 233 "parser.rl"
+#line 234 "parser.rl"
{
char *np = JSON_parse_string(json, p, pe, result);
if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;}
}
goto st29;
tr3:
-#line 238 "parser.rl"
+#line 239 "parser.rl"
{
char *np;
if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) {
@@ -564,7 +566,7 @@ tr3:
}
goto st29;
tr7:
-#line 256 "parser.rl"
+#line 257 "parser.rl"
{
char *np;
np = JSON_parse_array(json, p, pe, result, current_nesting + 1);
@@ -572,7 +574,7 @@ tr7:
}
goto st29;
tr11:
-#line 262 "parser.rl"
+#line 263 "parser.rl"
{
char *np;
np = JSON_parse_object(json, p, pe, result, current_nesting + 1);
@@ -580,7 +582,7 @@ tr11:
}
goto st29;
tr25:
-#line 226 "parser.rl"
+#line 227 "parser.rl"
{
if (json->allow_nan) {
*result = CInfinity;
@@ -590,7 +592,7 @@ tr25:
}
goto st29;
tr27:
-#line 219 "parser.rl"
+#line 220 "parser.rl"
{
if (json->allow_nan) {
*result = CNaN;
@@ -600,19 +602,19 @@ tr27:
}
goto st29;
tr31:
-#line 213 "parser.rl"
+#line 214 "parser.rl"
{
*result = Qfalse;
}
goto st29;
tr34:
-#line 210 "parser.rl"
+#line 211 "parser.rl"
{
*result = Qnil;
}
goto st29;
tr37:
-#line 216 "parser.rl"
+#line 217 "parser.rl"
{
*result = Qtrue;
}
@@ -621,9 +623,9 @@ st29:
if ( ++p == pe )
goto _test_eof29;
case 29:
-#line 268 "parser.rl"
+#line 269 "parser.rl"
{ p--; {p++; cs = 29; goto _out;} }
-#line 627 "parser.c"
+#line 628 "parser.c"
switch( (*p) ) {
case 13: goto st29;
case 32: goto st29;
@@ -864,7 +866,7 @@ case 28:
_out: {}
}
-#line 289 "parser.rl"
+#line 290 "parser.rl"
if (cs >= JSON_value_first_final) {
return p;
@@ -874,7 +876,7 @@ case 28:
}
-#line 878 "parser.c"
+#line 879 "parser.c"
enum {JSON_integer_start = 1};
enum {JSON_integer_first_final = 3};
enum {JSON_integer_error = 0};
@@ -882,7 +884,7 @@ enum {JSON_integer_error = 0};
enum {JSON_integer_en_main = 1};
-#line 305 "parser.rl"
+#line 306 "parser.rl"
static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -890,15 +892,15 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
int cs = EVIL;
-#line 894 "parser.c"
+#line 895 "parser.c"
{
cs = JSON_integer_start;
}
-#line 312 "parser.rl"
+#line 313 "parser.rl"
json->memo = p;
-#line 902 "parser.c"
+#line 903 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -932,14 +934,14 @@ case 3:
goto st0;
goto tr4;
tr4:
-#line 302 "parser.rl"
+#line 303 "parser.rl"
{ p--; {p++; cs = 4; goto _out;} }
goto st4;
st4:
if ( ++p == pe )
goto _test_eof4;
case 4:
-#line 943 "parser.c"
+#line 944 "parser.c"
goto st0;
st5:
if ( ++p == pe )
@@ -958,7 +960,7 @@ case 5:
_out: {}
}
-#line 314 "parser.rl"
+#line 315 "parser.rl"
if (cs >= JSON_integer_first_final) {
long len = p - json->memo;
@@ -973,7 +975,7 @@ case 5:
}
-#line 977 "parser.c"
+#line 978 "parser.c"
enum {JSON_float_start = 1};
enum {JSON_float_first_final = 8};
enum {JSON_float_error = 0};
@@ -981,23 +983,36 @@ enum {JSON_float_error = 0};
enum {JSON_float_en_main = 1};
-#line 339 "parser.rl"
+#line 340 "parser.rl"
+static int is_bigdecimal_class(VALUE obj)
+{
+ if (cBigDecimal == Qundef) {
+ if (rb_const_defined(rb_cObject, i_BigDecimal)) {
+ cBigDecimal = rb_const_get_at(rb_cObject, i_BigDecimal);
+ }
+ else {
+ return 0;
+ }
+ }
+ return obj == cBigDecimal;
+}
+
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
{
int cs = EVIL;
-#line 993 "parser.c"
+#line 994 "parser.c"
{
cs = JSON_float_start;
}
-#line 346 "parser.rl"
+#line 347 "parser.rl"
json->memo = p;
-#line 1001 "parser.c"
+#line 1002 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1055,14 +1070,14 @@ case 8:
goto st0;
goto tr9;
tr9:
-#line 333 "parser.rl"
+#line 334 "parser.rl"
{ p--; {p++; cs = 9; goto _out;} }
goto st9;
st9:
if ( ++p == pe )
goto _test_eof9;
case 9:
-#line 1066 "parser.c"
+#line 1067 "parser.c"
goto st0;
st5:
if ( ++p == pe )
@@ -1123,14 +1138,24 @@ case 7:
_out: {}
}
-#line 348 "parser.rl"
+#line 349 "parser.rl"
if (cs >= JSON_float_first_final) {
long len = p - json->memo;
fbuffer_clear(json->fbuffer);
fbuffer_append(json->fbuffer, json->memo, len);
fbuffer_append_char(json->fbuffer, '\0');
- *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
+ if (NIL_P(json->decimal_class)) {
+ *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
+ } else {
+ VALUE text;
+ text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
+ if (is_bigdecimal_class(json->decimal_class)) {
+ *result = rb_funcall(Qnil, i_BigDecimal, 1, text);
+ } else {
+ *result = rb_funcall(json->decimal_class, i_new, 1, text);
+ }
+ }
return p + 1;
} else {
return NULL;
@@ -1139,7 +1164,7 @@ case 7:
-#line 1143 "parser.c"
+#line 1150 "parser.c"
enum {JSON_array_start = 1};
enum {JSON_array_first_final = 17};
enum {JSON_array_error = 0};
@@ -1147,7 +1172,7 @@ enum {JSON_array_error = 0};
enum {JSON_array_en_main = 1};
-#line 391 "parser.rl"
+#line 398 "parser.rl"
static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
@@ -1161,14 +1186,14 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul
*result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class);
-#line 1165 "parser.c"
+#line 1172 "parser.c"
{
cs = JSON_array_start;
}
-#line 404 "parser.rl"
+#line 411 "parser.rl"
-#line 1172 "parser.c"
+#line 1179 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1207,7 +1232,7 @@ case 2:
goto st2;
goto st0;
tr2:
-#line 368 "parser.rl"
+#line 375 "parser.rl"
{
VALUE v = Qnil;
char *np = JSON_parse_value(json, p, pe, &v, current_nesting);
@@ -1227,7 +1252,7 @@ st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
-#line 1231 "parser.c"
+#line 1238 "parser.c"
switch( (*p) ) {
case 13: goto st3;
case 32: goto st3;
@@ -1327,14 +1352,14 @@ case 12:
goto st3;
goto st12;
tr4:
-#line 383 "parser.rl"
+#line 390 "parser.rl"
{ p--; {p++; cs = 17; goto _out;} }
goto st17;
st17:
if ( ++p == pe )
goto _test_eof17;
case 17:
-#line 1338 "parser.c"
+#line 1345 "parser.c"
goto st0;
st13:
if ( ++p == pe )
@@ -1390,7 +1415,7 @@ case 16:
_out: {}
}
-#line 405 "parser.rl"
+#line 412 "parser.rl"
if(cs >= JSON_array_first_final) {
return p + 1;
@@ -1435,13 +1460,21 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd)
break;
case 'u':
if (pe > stringEnd - 4) {
- return Qnil;
+ rb_enc_raise(
+ EXC_ENCODING eParserError,
+ "%u: incomplete unicode character escape sequence at '%s'", __LINE__, p
+ );
} else {
UTF32 ch = unescape_unicode((unsigned char *) ++pe);
pe += 3;
if (UNI_SUR_HIGH_START == (ch & 0xFC00)) {
pe++;
- if (pe > stringEnd - 6) return Qnil;
+ if (pe > stringEnd - 6) {
+ rb_enc_raise(
+ EXC_ENCODING eParserError,
+ "%u: incomplete surrogate pair at '%s'", __LINE__, p
+ );
+ }
if (pe[0] == '\\' && pe[1] == 'u') {
UTF32 sur = unescape_unicode((unsigned char *) pe + 2);
ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
@@ -1471,7 +1504,7 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd)
}
-#line 1475 "parser.c"
+#line 1490 "parser.c"
enum {JSON_string_start = 1};
enum {JSON_string_first_final = 8};
enum {JSON_string_error = 0};
@@ -1479,7 +1512,7 @@ enum {JSON_string_error = 0};
enum {JSON_string_en_main = 1};
-#line 504 "parser.rl"
+#line 519 "parser.rl"
static int
@@ -1501,15 +1534,15 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
*result = rb_str_buf_new(0);
-#line 1505 "parser.c"
+#line 1520 "parser.c"
{
cs = JSON_string_start;
}
-#line 525 "parser.rl"
+#line 540 "parser.rl"
json->memo = p;
-#line 1513 "parser.c"
+#line 1528 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1534,7 +1567,7 @@ case 2:
goto st0;
goto st2;
tr2:
-#line 490 "parser.rl"
+#line 505 "parser.rl"
{
*result = json_string_unescape(*result, json->memo + 1, p);
if (NIL_P(*result)) {
@@ -1545,14 +1578,14 @@ tr2:
{p = (( p + 1))-1;}
}
}
-#line 501 "parser.rl"
+#line 516 "parser.rl"
{ p--; {p++; cs = 8; goto _out;} }
goto st8;
st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
-#line 1556 "parser.c"
+#line 1571 "parser.c"
goto st0;
st3:
if ( ++p == pe )
@@ -1628,7 +1661,7 @@ case 7:
_out: {}
}
-#line 527 "parser.rl"
+#line 542 "parser.rl"
if (json->create_additions && RTEST(match_string = json->match_string)) {
VALUE klass;
@@ -1644,7 +1677,9 @@ case 7:
if (json->symbolize_names && json->parsing_name) {
*result = rb_str_intern(*result);
} else {
- rb_str_resize(*result, RSTRING_LEN(*result));
+ if (RB_TYPE_P(*result, T_STRING)) {
+ rb_str_resize(*result, RSTRING_LEN(*result));
+ }
}
if (cs >= JSON_string_first_final) {
return p + 1;
@@ -1675,7 +1710,7 @@ static VALUE convert_encoding(VALUE source)
}
FORCE_UTF8(source);
} else {
- source = rb_str_conv_enc(source, NULL, rb_utf8_encoding());
+ source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding());
}
#endif
return source;
@@ -1781,6 +1816,12 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
} else {
json->array_class = Qnil;
}
+ tmp = ID2SYM(i_decimal_class);
+ if (option_given_p(opts, tmp)) {
+ json->decimal_class = rb_hash_aref(opts, tmp);
+ } else {
+ json->decimal_class = Qnil;
+ }
tmp = ID2SYM(i_match_string);
if (option_given_p(opts, tmp)) {
VALUE match_string = rb_hash_aref(opts, tmp);
@@ -1794,10 +1835,11 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
} else {
json->max_nesting = 100;
json->allow_nan = 0;
- json->create_additions = 1;
+ json->create_additions = 0;
json->create_id = rb_funcall(mJSON, i_create_id, 0);
json->object_class = Qnil;
json->array_class = Qnil;
+ json->decimal_class = Qnil;
}
source = convert_encoding(StringValue(source));
StringValue(source);
@@ -1808,7 +1850,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
}
-#line 1812 "parser.c"
+#line 1836 "parser.c"
enum {JSON_start = 1};
enum {JSON_first_final = 10};
enum {JSON_error = 0};
@@ -1816,7 +1858,7 @@ enum {JSON_error = 0};
enum {JSON_en_main = 1};
-#line 720 "parser.rl"
+#line 744 "parser.rl"
/*
@@ -1833,16 +1875,16 @@ static VALUE cParser_parse(VALUE self)
GET_PARSER;
-#line 1837 "parser.c"
+#line 1861 "parser.c"
{
cs = JSON_start;
}
-#line 736 "parser.rl"
+#line 760 "parser.rl"
p = json->source;
pe = p + json->len;
-#line 1846 "parser.c"
+#line 1870 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1876,7 +1918,7 @@ st0:
cs = 0;
goto _out;
tr2:
-#line 712 "parser.rl"
+#line 736 "parser.rl"
{
char *np = JSON_parse_value(json, p, pe, &result, 0);
if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;}
@@ -1886,7 +1928,7 @@ st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
-#line 1890 "parser.c"
+#line 1914 "parser.c"
switch( (*p) ) {
case 13: goto st10;
case 32: goto st10;
@@ -1975,7 +2017,7 @@ case 9:
_out: {}
}
-#line 739 "parser.rl"
+#line 763 "parser.rl"
if (cs >= JSON_first_final && p == pe) {
return result;
@@ -1992,6 +2034,7 @@ static void JSON_mark(void *ptr)
rb_gc_mark_maybe(json->create_id);
rb_gc_mark_maybe(json->object_class);
rb_gc_mark_maybe(json->array_class);
+ rb_gc_mark_maybe(json->decimal_class);
rb_gc_mark_maybe(json->match_string);
}
@@ -2041,6 +2084,7 @@ static VALUE cParser_source(VALUE self)
void Init_parser(void)
{
+#undef rb_intern
rb_require("json/common");
mJSON = rb_define_module("JSON");
mExt = rb_define_module_under(mJSON, "Ext");
@@ -2066,6 +2110,7 @@ void Init_parser(void)
i_symbolize_names = rb_intern("symbolize_names");
i_object_class = rb_intern("object_class");
i_array_class = rb_intern("array_class");
+ i_decimal_class = rb_intern("decimal_class");
i_match = rb_intern("match");
i_match_string = rb_intern("match_string");
i_key_p = rb_intern("key?");
@@ -2073,6 +2118,8 @@ void Init_parser(void)
i_aset = rb_intern("[]=");
i_aref = rb_intern("[]");
i_leftshift = rb_intern("<<");
+ i_new = rb_intern("new");
+ i_BigDecimal = rb_intern("BigDecimal");
}
/*
diff --git a/ext/json/parser/parser.h b/ext/json/parser/parser.h
index 1d46831965..e6cf779024 100644
--- a/ext/json/parser/parser.h
+++ b/ext/json/parser/parser.h
@@ -39,6 +39,7 @@ typedef struct JSON_ParserStruct {
int symbolize_names;
VALUE object_class;
VALUE array_class;
+ VALUE decimal_class;
int create_additions;
VALUE match_string;
FBuffer *fbuffer;
diff --git a/ext/json/parser/parser.rl b/ext/json/parser/parser.rl
index dd24cf94cc..fb0bb515de 100644
--- a/ext/json/parser/parser.rl
+++ b/ext/json/parser/parser.rl
@@ -89,11 +89,13 @@ static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
static VALUE CNaN, CInfinity, CMinusInfinity;
+static VALUE cBigDecimal = Qundef;
static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
i_chr, i_max_nesting, i_allow_nan, i_symbolize_names,
- i_object_class, i_array_class, i_key_p, i_deep_const_get, i_match,
- i_match_string, i_aset, i_aref, i_leftshift;
+ i_object_class, i_array_class, i_decimal_class, i_key_p,
+ i_deep_const_get, i_match, i_match_string, i_aset, i_aref,
+ i_leftshift, i_new, i_BigDecimal;
%%{
machine JSON_common;
@@ -338,6 +340,19 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
) (^[0-9Ee.\-]? @exit );
}%%
+static int is_bigdecimal_class(VALUE obj)
+{
+ if (cBigDecimal == Qundef) {
+ if (rb_const_defined(rb_cObject, i_BigDecimal)) {
+ cBigDecimal = rb_const_get_at(rb_cObject, i_BigDecimal);
+ }
+ else {
+ return 0;
+ }
+ }
+ return obj == cBigDecimal;
+}
+
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
{
int cs = EVIL;
@@ -351,7 +366,17 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
fbuffer_clear(json->fbuffer);
fbuffer_append(json->fbuffer, json->memo, len);
fbuffer_append_char(json->fbuffer, '\0');
- *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
+ if (NIL_P(json->decimal_class)) {
+ *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
+ } else {
+ VALUE text;
+ text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
+ if (is_bigdecimal_class(json->decimal_class)) {
+ *result = rb_funcall(Qnil, i_BigDecimal, 1, text);
+ } else {
+ *result = rb_funcall(json->decimal_class, i_new, 1, text);
+ }
+ }
return p + 1;
} else {
return NULL;
@@ -446,13 +471,21 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd)
break;
case 'u':
if (pe > stringEnd - 4) {
- return Qnil;
+ rb_enc_raise(
+ EXC_ENCODING eParserError,
+ "%u: incomplete unicode character escape sequence at '%s'", __LINE__, p
+ );
} else {
UTF32 ch = unescape_unicode((unsigned char *) ++pe);
pe += 3;
if (UNI_SUR_HIGH_START == (ch & 0xFC00)) {
pe++;
- if (pe > stringEnd - 6) return Qnil;
+ if (pe > stringEnd - 6) {
+ rb_enc_raise(
+ EXC_ENCODING eParserError,
+ "%u: incomplete surrogate pair at '%s'", __LINE__, p
+ );
+ }
if (pe[0] == '\\' && pe[1] == 'u') {
UTF32 sur = unescape_unicode((unsigned char *) pe + 2);
ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
@@ -539,7 +572,9 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
if (json->symbolize_names && json->parsing_name) {
*result = rb_str_intern(*result);
} else {
- rb_str_resize(*result, RSTRING_LEN(*result));
+ if (RB_TYPE_P(*result, T_STRING)) {
+ rb_str_resize(*result, RSTRING_LEN(*result));
+ }
}
if (cs >= JSON_string_first_final) {
return p + 1;
@@ -570,7 +605,7 @@ static VALUE convert_encoding(VALUE source)
}
FORCE_UTF8(source);
} else {
- source = rb_str_conv_enc(source, NULL, rb_utf8_encoding());
+ source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding());
}
#endif
return source;
@@ -676,6 +711,12 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
} else {
json->array_class = Qnil;
}
+ tmp = ID2SYM(i_decimal_class);
+ if (option_given_p(opts, tmp)) {
+ json->decimal_class = rb_hash_aref(opts, tmp);
+ } else {
+ json->decimal_class = Qnil;
+ }
tmp = ID2SYM(i_match_string);
if (option_given_p(opts, tmp)) {
VALUE match_string = rb_hash_aref(opts, tmp);
@@ -689,10 +730,11 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
} else {
json->max_nesting = 100;
json->allow_nan = 0;
- json->create_additions = 1;
+ json->create_additions = 0;
json->create_id = rb_funcall(mJSON, i_create_id, 0);
json->object_class = Qnil;
json->array_class = Qnil;
+ json->decimal_class = Qnil;
}
source = convert_encoding(StringValue(source));
StringValue(source);
@@ -752,6 +794,7 @@ static void JSON_mark(void *ptr)
rb_gc_mark_maybe(json->create_id);
rb_gc_mark_maybe(json->object_class);
rb_gc_mark_maybe(json->array_class);
+ rb_gc_mark_maybe(json->decimal_class);
rb_gc_mark_maybe(json->match_string);
}
@@ -801,6 +844,7 @@ static VALUE cParser_source(VALUE self)
void Init_parser(void)
{
+#undef rb_intern
rb_require("json/common");
mJSON = rb_define_module("JSON");
mExt = rb_define_module_under(mJSON, "Ext");
@@ -826,6 +870,7 @@ void Init_parser(void)
i_symbolize_names = rb_intern("symbolize_names");
i_object_class = rb_intern("object_class");
i_array_class = rb_intern("array_class");
+ i_decimal_class = rb_intern("decimal_class");
i_match = rb_intern("match");
i_match_string = rb_intern("match_string");
i_key_p = rb_intern("key?");
@@ -833,6 +878,8 @@ void Init_parser(void)
i_aset = rb_intern("[]=");
i_aref = rb_intern("[]");
i_leftshift = rb_intern("<<");
+ i_new = rb_intern("new");
+ i_BigDecimal = rb_intern("BigDecimal");
}
/*
diff --git a/ext/mathn/complex/complex.c b/ext/mathn/complex/complex.c
deleted file mode 100644
index dce494959f..0000000000
--- a/ext/mathn/complex/complex.c
+++ /dev/null
@@ -1,7 +0,0 @@
-extern void nucomp_canonicalization(int);
-
-void
-Init_complex(void)
-{
- nucomp_canonicalization(1);
-}
diff --git a/ext/mathn/complex/extconf.rb b/ext/mathn/complex/extconf.rb
deleted file mode 100644
index a3f45ac4fc..0000000000
--- a/ext/mathn/complex/extconf.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# frozen_string_literal: false
-require "mkmf"
-
-create_makefile "mathn/complex"
diff --git a/ext/mathn/rational/extconf.rb b/ext/mathn/rational/extconf.rb
deleted file mode 100644
index 4e4cc5f621..0000000000
--- a/ext/mathn/rational/extconf.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# frozen_string_literal: false
-require "mkmf"
-
-create_makefile "mathn/rational"
diff --git a/ext/mathn/rational/rational.c b/ext/mathn/rational/rational.c
deleted file mode 100644
index 2ac5999946..0000000000
--- a/ext/mathn/rational/rational.c
+++ /dev/null
@@ -1,7 +0,0 @@
-extern void nurat_canonicalization(int);
-
-void
-Init_rational(void)
-{
- nurat_canonicalization(1);
-}
diff --git a/ext/nkf/depend b/ext/nkf/depend
index dfda9c0597..4ea8544a95 100644
--- a/ext/nkf/depend
+++ b/ext/nkf/depend
@@ -10,6 +10,7 @@ nkf.o: $(hdrdir)/ruby/defines.h
nkf.o: $(hdrdir)/ruby/encoding.h
nkf.o: $(hdrdir)/ruby/intern.h
nkf.o: $(hdrdir)/ruby/missing.h
+nkf.o: $(hdrdir)/ruby/onigmo.h
nkf.o: $(hdrdir)/ruby/oniguruma.h
nkf.o: $(hdrdir)/ruby/ruby.h
nkf.o: $(hdrdir)/ruby/st.h
diff --git a/ext/nkf/nkf-utf8/nkf.c b/ext/nkf/nkf-utf8/nkf.c
index b58c437d3c..cc438a50d6 100644
--- a/ext/nkf/nkf-utf8/nkf.c
+++ b/ext/nkf/nkf-utf8/nkf.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 1987, Fujitsu LTD. (Itaru ICHIKAWA).
- * Copyright (c) 1996-2013, The nkf Project.
+ * Copyright (c) 1996-2018, The nkf Project.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
@@ -20,11 +20,11 @@
*
* 3. This notice may not be removed or altered from any source distribution.
*/
-#define NKF_VERSION "2.1.4"
-#define NKF_RELEASE_DATE "2015-12-12"
+#define NKF_VERSION "2.1.5"
+#define NKF_RELEASE_DATE "2018-12-15"
#define COPY_RIGHT \
"Copyright (C) 1987, FUJITSU LTD. (I.Ichikawa).\n" \
- "Copyright (C) 1996-2015, The nkf Project."
+ "Copyright (C) 1996-2018, The nkf Project."
#include "config.h"
#include "nkf.h"
@@ -1111,18 +1111,26 @@ encode_fallback_java(nkf_char c)
(*oconv)(0, '\\');
c &= VALUE_MASK;
if(!nkf_char_unicode_bmp_p(c)){
- (*oconv)(0, 'U');
- (*oconv)(0, '0');
- (*oconv)(0, '0');
- (*oconv)(0, bin2hex(c>>20));
- (*oconv)(0, bin2hex(c>>16));
+ int high = (c >> 10) + NKF_INT32_C(0xD7C0); /* high surrogate */
+ int low = (c & 0x3FF) + NKF_INT32_C(0xDC00); /* low surrogate */
+ (*oconv)(0, 'u');
+ (*oconv)(0, bin2hex(high>>12));
+ (*oconv)(0, bin2hex(high>> 8));
+ (*oconv)(0, bin2hex(high>> 4));
+ (*oconv)(0, bin2hex(high ));
+ (*oconv)(0, '\\');
+ (*oconv)(0, 'u');
+ (*oconv)(0, bin2hex(low>>12));
+ (*oconv)(0, bin2hex(low>> 8));
+ (*oconv)(0, bin2hex(low>> 4));
+ (*oconv)(0, bin2hex(low ));
}else{
(*oconv)(0, 'u');
+ (*oconv)(0, bin2hex(c>>12));
+ (*oconv)(0, bin2hex(c>> 8));
+ (*oconv)(0, bin2hex(c>> 4));
+ (*oconv)(0, bin2hex(c ));
}
- (*oconv)(0, bin2hex(c>>12));
- (*oconv)(0, bin2hex(c>> 8));
- (*oconv)(0, bin2hex(c>> 4));
- (*oconv)(0, bin2hex(c ));
return;
}
@@ -1947,12 +1955,17 @@ unicode_to_jis_common(nkf_char c2, nkf_char c1, nkf_char c0, nkf_char *p2, nkf_c
ret = unicode_to_jis_common2(c1, c0, ppp[c2 - 0xE0], sizeof_utf8_to_euc_C2, p2, p1);
}else return -1;
#ifdef SHIFTJIS_CP932
- if (!ret && !cp932inv_f && is_eucg3(*p2)) {
- nkf_char s2, s1;
- if (e2s_conv(*p2, *p1, &s2, &s1) == 0) {
- s2e_conv(s2, s1, p2, p1);
- }else{
- ret = 1;
+ if (!ret&& is_eucg3(*p2)) {
+ if (cp932inv_f) {
+ if (encode_fallback) ret = 1;
+ }
+ else {
+ nkf_char s2, s1;
+ if (e2s_conv(*p2, *p1, &s2, &s1) == 0) {
+ s2e_conv(s2, s1, p2, p1);
+ }else{
+ ret = 1;
+ }
}
}
#endif
diff --git a/ext/nkf/nkf-utf8/utf8tbl.c b/ext/nkf/nkf-utf8/utf8tbl.c
index 3821c59468..a31e4e7805 100644
--- a/ext/nkf/nkf-utf8/utf8tbl.c
+++ b/ext/nkf/nkf-utf8/utf8tbl.c
@@ -5445,7 +5445,7 @@ static const unsigned short utf8_to_euc_E4BB_x0213[] = {
0xB047, 0x2E28, 0xB049, 0x4265, 0x4E61, 0x304A, 0, 0,
0xB04A, 0, 0, 0xA13B, 0, 0x5041, 0x323E, 0xB04B,
0x3644, 0xA13D, 0x4367, 0xB04D, 0, 0xA13E, 0x376F, 0x5043,
- 0, 0, 0, 0x4724, 0xF42F, 0x2E29, 0xB050, 0x2E2A,
+ 0, 0, 0, 0x4724, 0, 0x2E29, 0xB050, 0x2E2A,
};
static const unsigned short utf8_to_euc_E4BC[] = {
0xB052, 0x346B, 0xB053, 0xB054, 0, 0, 0, 0,
@@ -5465,7 +5465,7 @@ static const unsigned short utf8_to_euc_E4BC_x0213[] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0xB05D, 0x476C,
0x5046, 0xB05E, 0, 0xB060, 0x483C, 0xB061, 0x4E62, 0xA142,
- 0x3F2D, 0xB063, 0x3B47, 0xB064, 0x3B77, 0x3240, 0xA143, 0,
+ 0x3F2D, 0, 0x3B47, 0xB064, 0x3B77, 0x3240, 0xA143, 0,
};
static const unsigned short utf8_to_euc_E4BD[] = {
0xB066, 0, 0xB067, 0x4451, 0, 0, 0x4322, 0x504A,
@@ -5519,13 +5519,13 @@ static const unsigned short utf8_to_euc_E4BF[] = {
};
static const unsigned short utf8_to_euc_E4BF_x0213[] = {
0xB136, 0xB137, 0x3738, 0x4225, 0x3264, 0xA152, 0xB139, 0,
- 0xB13A, 0x2E39, 0x3D53, 0xA153, 0xB13D, 0xB13E, 0x5059, 0xA154,
+ 0xB13A, 0x2E39, 0x3D53, 0xA153, 0xB13D, 0, 0x5059, 0xA154,
0x505E, 0x505C, 0xA155, 0, 0x5057, 0, 0, 0x422F,
0x505A, 0, 0x505D, 0x505B, 0xB141, 0x4A5D, 0, 0x5058,
0x2E3A, 0x3F2E, 0xB143, 0x4B73, 0x505F, 0x5060, 0xA14F, 0,
0, 0, 0, 0, 0, 0, 0x3D24, 0x506D,
0xB144, 0x2E21, 0xA157, 0x4750, 0, 0x4936, 0x5068, 0,
- 0x4A70, 0, 0x3236, 0, 0xB146, 0xB147, 0x506C, 0xB148,
+ 0x4A70, 0, 0x3236, 0, 0xB146, 0xB147, 0x506C, 0,
};
static const unsigned short utf8_to_euc_E580[] = {
0xB149, 0xB14A, 0, 0, 0xB14B, 0x5066, 0x506F, 0xB14C,
@@ -5601,10 +5601,10 @@ static const unsigned short utf8_to_euc_E583_x0213[] = {
0xB232, 0, 0x5124, 0xB233, 0xA174, 0x364F, 0, 0xA175,
0, 0x5121, 0x5122, 0, 0x2E45, 0x462F, 0xA178, 0x417C,
0x2E47, 0x3623, 0, 0xB239, 0xA17A, 0x4B4D, 0x5125, 0,
- 0xB23B, 0xA17B, 0x4E3D, 0, 0xB23C, 0xB23D, 0x5126, 0xB23E,
+ 0, 0xA17B, 0x4E3D, 0, 0xB23C, 0xB23D, 0x5126, 0xB23E,
0, 0xA17C, 0xB23F, 0x5129, 0xB240, 0x5127, 0x2E48, 0x414E,
0xB242, 0xA17D, 0, 0, 0, 0x5128, 0x512A, 0xB244,
- 0, 0xB245, 0x2E46, 0xA176, 0xF430, 0x512C, 0xB246, 0,
+ 0, 0xB245, 0x2E46, 0xA176, 0, 0x512C, 0xB246, 0,
0, 0x512B, 0xB247, 0x4A48, 0, 0, 0xB248, 0,
};
static const unsigned short utf8_to_euc_E584[] = {
@@ -5642,7 +5642,7 @@ static const unsigned short utf8_to_euc_E585_x0213[] = {
0x4068, 0x3877, 0x2E4F, 0x396E, 0x513C, 0x4C48, 0x4546, 0xB267,
0x3B79, 0, 0x513B, 0xB268, 0x513D, 0x2E51, 0, 0x2E52,
0xB26B, 0, 0x455E, 0, 0x3375, 0, 0, 0xB26C,
- 0xA326, 0, 0x513E, 0, 0xB26D, 0x467E, 0xB26E, 0,
+ 0xA326, 0, 0x513E, 0, 0, 0x467E, 0xB26E, 0,
0x4134, 0x5140, 0x5141, 0x482C, 0x3878, 0x4F3B, 0x5142, 0,
0, 0x3626, 0, 0xA328, 0, 0x4A3C, 0x4236, 0x3671,
0x4535, 0, 0, 0xF474, 0x3773, 0, 0xB26F, 0,
@@ -5665,7 +5665,7 @@ static const unsigned short utf8_to_euc_E586_x0213[] = {
0x3427, 0xB276, 0x514F, 0xA32D, 0x514D, 0x4C3D, 0x514E, 0,
0x495A, 0x5150, 0x5151, 0x5152, 0x455F, 0xA32E, 0, 0,
0x5156, 0x5154, 0x5155, 0x5153, 0x3A63, 0x5157, 0x4C6A, 0x4E64,
- 0xB279, 0, 0xB27A, 0, 0xA330, 0x5158, 0xB27C, 0xB27D,
+ 0xB279, 0, 0xB27A, 0, 0xA330, 0x5158, 0, 0xB27D,
};
static const unsigned short utf8_to_euc_E587[] = {
0, 0, 0xB27E, 0, 0x4028, 0x5159, 0x3D5A, 0,
@@ -5683,7 +5683,7 @@ static const unsigned short utf8_to_euc_E587_x0213[] = {
0, 0xB323, 0xB324, 0xB325, 0, 0xB326, 0x5245, 0,
0xB327, 0, 0, 0x515B, 0x7425, 0x3645, 0x2E57, 0,
0x515C, 0x4B5E, 0x2E58, 0, 0, 0xB32A, 0x3D68, 0x427C,
- 0, 0x515E, 0x4664, 0, 0xF431, 0x515F, 0x2E59, 0,
+ 0, 0x515E, 0x4664, 0, 0, 0x515F, 0x2E59, 0,
0x5160, 0x332E, 0xB32C, 0xA333, 0xA334, 0x5161, 0x3627, 0xB32F,
0x464C, 0x317A, 0x3D50, 0, 0, 0x4821, 0x5162, 0,
};
@@ -5741,7 +5741,7 @@ static const unsigned short utf8_to_euc_E58A_x0213[] = {
0xB34D, 0, 0xA33E, 0x3344, 0xA33D, 0xB34F, 0, 0x3760,
0x517C, 0x4E2D, 0xB350, 0, 0xB351, 0x5178, 0, 0,
0, 0x517D, 0x517A, 0x2E61, 0x5179, 0xB353, 0xB354, 0xB355,
- 0xA340, 0, 0xB357, 0x4E4F, 0xB358, 0, 0, 0x3879,
+ 0xA340, 0, 0xB357, 0x4E4F, 0, 0, 0, 0x3879,
0x3243, 0, 0, 0x4E74, 0xA342, 0xB35A, 0xA343, 0xB35C,
0, 0x3D75, 0x4558, 0x3965, 0x5222, 0x5223, 0, 0xA344,
0xB35E, 0x4E65, 0, 0, 0x4F2B, 0x5225, 0xB35F, 0xB360,
@@ -5758,7 +5758,7 @@ static const unsigned short utf8_to_euc_E58B[] = {
0x5230, 0x5231, 0x3C5B, 0, 0, 0, 0x387B, 0x4C5E,
};
static const unsigned short utf8_to_euc_E58B_x0213[] = {
- 0xB365, 0x5226, 0, 0x4B56, 0xB366, 0x443C, 0xB367, 0x4D26,
+ 0, 0x5226, 0, 0x4B56, 0xB366, 0x443C, 0xB367, 0x4D26,
0x2E62, 0x4A59, 0xA347, 0, 0x2E64, 0x5227, 0, 0xB36A,
0x2E65, 0xA349, 0x7055, 0, 0xB36C, 0x4630, 0x2E66, 0x5228,
0x342A, 0x4C33, 0, 0x2E67, 0xB36F, 0x3E21, 0x5229, 0x4A67,
@@ -5804,7 +5804,7 @@ static const unsigned short utf8_to_euc_E58D_x0213[] = {
0x4331, 0xB439, 0x476E, 0xB43A, 0x4B4E, 0, 0x5246, 0,
0x406A, 0x2E6F, 0, 0x2E70, 0, 0xB43D, 0x3735, 0xA354,
0, 0x5247, 0, 0, 0xA355, 0xB43F, 0x5248, 0x312C,
- 0x3075, 0x346D, 0xB440, 0x4228, 0x3551, 0x4D71, 0, 0x524B,
+ 0x3075, 0x346D, 0, 0x4228, 0x3551, 0x4D71, 0, 0x524B,
0x3237, 0xB441, 0xA356, 0x524A, 0, 0x2E71, 0xB442, 0x362A,
};
static const unsigned short utf8_to_euc_E58E[] = {
@@ -5841,7 +5841,7 @@ static const unsigned short utf8_to_euc_E58F_x0213[] = {
0xA35B, 0, 0x3B32, 0x5254, 0, 0xB458, 0, 0,
0x4B74, 0x3A35, 0x355A, 0x4D27, 0x4150, 0x483F, 0x3C7D, 0xB459,
0, 0, 0xB45A, 0xB45B, 0x3D47, 0xA35F, 0x3C68, 0x3C75,
- 0, 0x3D76, 0xA360, 0x4840, 0, 0xB45E, 0xB45F, 0x5257,
+ 0, 0x3D76, 0xA360, 0x4840, 0, 0, 0xB45F, 0x5257,
0xB460, 0x3143, 0x4151, 0x387D, 0x3845, 0x3667, 0xB461, 0xB462,
0x525B, 0x4321, 0x427E, 0x362B, 0x3E24, 0x525C, 0x525A, 0x3244,
0x4266, 0x3C38, 0x3B4B, 0x3126, 0xA362, 0xA363, 0x3370, 0x3966,
@@ -6121,8 +6121,8 @@ static const unsigned short utf8_to_euc_E59D_x0213[] = {
0x542E, 0, 0x3A64, 0, 0, 0xA45F, 0xA460, 0x3651,
0, 0, 0x4B37, 0, 0xA461, 0xA462, 0x542C, 0x542F,
0x3A41, 0x3923, 0xB740, 0, 0, 0, 0, 0,
- 0, 0xF436, 0, 0, 0, 0, 0, 0,
- 0, 0x5433, 0xB741, 0, 0x3A25, 0xB742, 0x4333, 0xB743,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x5433, 0xB741, 0, 0x3A25, 0, 0x4333, 0xB743,
0xA464, 0x5430, 0x445A, 0xB745, 0, 0xB746, 0xB747, 0xA465,
0x2F47, 0xB74A, 0, 0xA466, 0xA467, 0xA468, 0, 0x2F48,
0, 0xB74F, 0xB750, 0xA469, 0x2F49, 0, 0xB753, 0x5434,
@@ -6224,7 +6224,7 @@ static const unsigned short utf8_to_euc_E5A2_x0213[] = {
0, 0, 0, 0, 0x4446, 0xA52F, 0x2F5D, 0x5452,
0xB848, 0xB849, 0xB84A, 0, 0, 0, 0xB84B, 0,
0x4B4F, 0x2F5F, 0xA530, 0x5453, 0, 0, 0x5458, 0,
- 0, 0xA531, 0xB84E, 0x4A2F, 0, 0, 0, 0,
+ 0, 0xA531, 0, 0x4A2F, 0, 0, 0, 0,
0x5457, 0x5451, 0x5454, 0x5456, 0xB850, 0, 0x3A26, 0,
};
static const unsigned short utf8_to_euc_E5A3[] = {
@@ -6280,9 +6280,9 @@ static const unsigned short utf8_to_euc_E5A5[] = {
static const unsigned short utf8_to_euc_E5A5_x0213[] = {
0, 0, 0, 0xB872, 0x3162, 0, 0xA542, 0x3471,
0x4660, 0x4A74, 0, 0, 0, 0, 0x5477, 0x4155,
- 0x5476, 0x3740, 0xB874, 0xB875, 0x4B5B, 0x5475, 0, 0x4565,
+ 0x5476, 0x3740, 0xB874, 0, 0x4B5B, 0x5475, 0, 0x4565,
0x5479, 0xB876, 0x5478, 0xA545, 0, 0x2F69, 0xB879, 0xA546,
- 0x547B, 0xB87B, 0x547A, 0xB87C, 0, 0x317C, 0, 0x547C,
+ 0x547B, 0xB87B, 0x547A, 0, 0, 0x317C, 0, 0x547C,
0x3E29, 0x547E, 0x4325, 0xB87D, 0x547D, 0x2F6A, 0x4A33, 0xB921,
0, 0, 0xB922, 0x3D77, 0x455B, 0xA548, 0xA549, 0,
0x5521, 0xB925, 0, 0xB926, 0xA54A, 0x3925, 0, 0,
@@ -6305,7 +6305,7 @@ static const unsigned short utf8_to_euc_E5A6_x0213[] = {
0, 0, 0, 0x5526, 0x2F6D, 0x4245, 0, 0xB930,
0x4B38, 0, 0, 0, 0x454A, 0xB931, 0xA54C, 0xB933,
0xB934, 0, 0x5527, 0xB935, 0, 0, 0, 0xB936,
- 0, 0x4B65, 0xB937, 0x3A4A, 0xA54D, 0, 0x3E2A, 0,
+ 0, 0x4B65, 0, 0x3A4A, 0xA54D, 0, 0x3E2A, 0,
};
static const unsigned short utf8_to_euc_E5A7[] = {
0, 0xB939, 0, 0xB93A, 0xB93B, 0, 0x5528, 0,
@@ -6564,7 +6564,7 @@ static const unsigned short utf8_to_euc_E5B3_x0213[] = {
0, 0x5635, 0, 0, 0, 0xBB3C, 0, 0,
0x463D, 0x362E, 0, 0, 0, 0, 0, 0,
0x3265, 0x5636, 0x563B, 0, 0, 0x5639, 0xBB3E, 0x4A77,
- 0x4A76, 0xBB3F, 0xBB40, 0, 0x4F6D, 0xF43B, 0x4567, 0,
+ 0x4A76, 0xBB3F, 0xBB40, 0, 0x4F6D, 0, 0x4567, 0,
0, 0, 0x5638, 0x3D54, 0, 0x5637, 0, 0,
};
static const unsigned short utf8_to_euc_E5B4[] = {
@@ -6640,7 +6640,7 @@ static const unsigned short utf8_to_euc_E5B7[] = {
static const unsigned short utf8_to_euc_E5B7_x0213[] = {
0, 0, 0, 0xBB76, 0, 0, 0, 0xBB77,
0, 0x565A, 0, 0x4F7D, 0x3460, 0x565B, 0xBB7A, 0,
- 0xBB79, 0xA868, 0x565D, 0x565C, 0, 0, 0x565E, 0xA869,
+ 0, 0xA868, 0x565D, 0x565C, 0, 0, 0x565E, 0xA869,
0xA86A, 0xBB7C, 0, 0x565F, 0, 0x406E, 0x3D23, 0,
0xA86B, 0x3D64, 0x7428, 0x4163, 0xA86D, 0x3929, 0x3A38, 0x392A,
0x3570, 0xA86E, 0, 0x5660, 0, 0, 0x3A39, 0,
@@ -6742,7 +6742,7 @@ static const unsigned short utf8_to_euc_E5BC_x0213[] = {
0xAC2D, 0x5732, 0x4A40, 0x5735, 0x5021, 0x5031, 0xAC2E, 0x3C30,
0x4675, 0x5736, 0, 0x355D, 0x4424, 0x307A, 0x5737, 0x4A26,
0x3930, 0xBC61, 0, 0x4350, 0xAC2F, 0x7434, 0xAC31, 0x446F,
- 0, 0xBC64, 0xBC65, 0x7435, 0xBC67, 0x4C6F, 0x3839, 0x384C,
+ 0, 0, 0xBC65, 0x7435, 0xBC67, 0x4C6F, 0x3839, 0x384C,
0xBC68, 0x5738, 0, 0xBC69, 0xBC6A, 0x5739, 0xBC6B, 0x573F,
0xBC6C, 0x3C65, 0, 0, 0x7436, 0x4425, 0x7437, 0x362F,
0x573A, 0, 0, 0xBC6F, 0x492B, 0x7438, 0x4346, 0xBC71,
@@ -6841,7 +6841,7 @@ static const unsigned short utf8_to_euc_E681_x0213[] = {
0xBD5E, 0x576C, 0x5776, 0x5774, 0, 0, 0x5771, 0x744F,
0xBD60, 0xBD61, 0x5770, 0x4E78, 0xAC4B, 0x5772, 0, 0,
0x3632, 0xBD63, 0x3931, 0, 0xBD64, 0x3D7A, 0xBD65, 0xBD66,
- 0, 0x5779, 0x576B, 0, 0, 0xBD67, 0, 0x576F,
+ 0, 0x5779, 0x576B, 0, 0, 0, 0, 0x576F,
0x575F, 0xBD68, 0x327A, 0x5773, 0x5775, 0x4351, 0, 0xBD69,
0x3A28, 0x3238, 0x576D, 0x5778, 0x5777, 0x3633, 0, 0x4229,
0x3366, 0xBD6A, 0, 0, 0, 0x3743, 0, 0x576E,
@@ -6858,7 +6858,7 @@ static const unsigned short utf8_to_euc_E682[] = {
0x5829, 0, 0, 0xBE21, 0x4569, 0x582E, 0xBE22, 0,
};
static const unsigned short utf8_to_euc_E682_x0213[] = {
- 0, 0x577A, 0xBD6D, 0x577D, 0x5821, 0xF43F, 0xBD6E, 0,
+ 0, 0x577A, 0xBD6D, 0x577D, 0x5821, 0, 0xBD6E, 0,
0xBD6F, 0x3C3D, 0xAC4D, 0x5827, 0x4470, 0x577B, 0xBD71, 0,
0, 0xBD72, 0x5825, 0xBD73, 0x3279, 0xAC4E, 0x5823, 0x5824,
0xBD75, 0, 0x577E, 0x5822, 0, 0x7451, 0x7452, 0x3867,
@@ -6881,7 +6881,7 @@ static const unsigned short utf8_to_euc_E683_x0213[] = {
0, 0, 0xBE23, 0, 0xBE24, 0x3E70, 0x582F, 0x4657,
0xAC54, 0xBE26, 0xBE27, 0x7453, 0, 0, 0xBE29, 0xBE2A,
0, 0x4F47, 0, 0x582B, 0x7454, 0x7455, 0, 0,
- 0x5831, 0xAC55, 0x397B, 0xAC56, 0x404B, 0x7456, 0xBE30, 0x3054,
+ 0x5831, 0xAC55, 0x397B, 0xAC56, 0x404B, 0x7456, 0, 0x3054,
0x582A, 0x5828, 0xBE31, 0x415A, 0, 0xBE32, 0, 0x577C,
0x3B34, 0, 0, 0, 0, 0, 0xAC57, 0,
0x4246, 0x583D, 0xAC58, 0x415B, 0x5838, 0xAC59, 0x5835, 0x5836,
@@ -6900,9 +6900,9 @@ static const unsigned short utf8_to_euc_E684[] = {
static const unsigned short utf8_to_euc_E684_x0213[] = {
0x5837, 0x3D25, 0xBE38, 0x583A, 0, 0, 0x5834, 0xBE39,
0x4C7C, 0x4C7B, 0xBE3A, 0, 0xBE3B, 0x583E, 0x583F, 0x3055,
- 0xAC5A, 0xBE3D, 0xAC5B, 0xAC5C, 0xBE40, 0x5833, 0xBE41, 0xBE42,
+ 0xAC5A, 0, 0xAC5B, 0xAC5C, 0xBE40, 0x5833, 0xBE41, 0xBE42,
0, 0xAC5D, 0x3672, 0x3026, 0x7458, 0, 0xAC5E, 0x3436,
- 0xF440, 0x583B, 0xBE46, 0, 0, 0, 0, 0x5843,
+ 0, 0x583B, 0xBE46, 0, 0, 0, 0, 0x5843,
0x5842, 0, 0xBE47, 0x7459, 0x5847, 0, 0, 0,
0x745A, 0xBE4A, 0, 0, 0x5848, 0xBE4B, 0xBE4C, 0x745B,
0, 0xBE4E, 0xAC5F, 0, 0x5846, 0x5849, 0x5841, 0x5845,
@@ -6980,7 +6980,7 @@ static const unsigned short utf8_to_euc_E688[] = {
static const unsigned short utf8_to_euc_E688_x0213[] = {
0x5878, 0xBF24, 0, 0xBF25, 0xBF26, 0, 0, 0xBF27,
0x5879, 0x587A, 0x4A6A, 0, 0x587C, 0x587B, 0x3D3F, 0,
- 0x402E, 0x3266, 0x327C, 0xBF28, 0x587D, 0xAC73, 0x303F, 0,
+ 0x402E, 0x3266, 0x327C, 0, 0x587D, 0xAC73, 0x303F, 0,
0, 0, 0x404C, 0x587E, 0xBF2A, 0x6C43, 0x5921, 0x3761,
0xBF2B, 0x5922, 0x7462, 0xAC74, 0, 0, 0x406F, 0xBF2E,
0, 0xAC75, 0x5923, 0xBF30, 0, 0, 0x5924, 0x353A,
@@ -7239,7 +7239,7 @@ static const unsigned short utf8_to_euc_E695[] = {
};
static const unsigned short utf8_to_euc_E695_x0213[] = {
0, 0, 0, 0xC14A, 0xAD62, 0x384E, 0, 0xC14B,
- 0x5A43, 0xC14C, 0, 0, 0, 0x5A46, 0xF441, 0x4952,
+ 0x5A43, 0xC14C, 0, 0, 0, 0x5A46, 0, 0x4952,
0xC14D, 0x355F, 0xC14E, 0, 0xAD63, 0x5A45, 0x5A44, 0x4754,
0x5A47, 0x3635, 0, 0, 0, 0x5A49, 0x5A48, 0xC150,
0xC151, 0, 0x343A, 0x3B36, 0, 0, 0x4658, 0x7529,
@@ -7303,9 +7303,9 @@ static const unsigned short utf8_to_euc_E698_x0213[] = {
0, 0xC17A, 0xC17B, 0x3057, 0x404E, 0x752E, 0xC17D, 0,
0, 0, 0, 0, 0x5A66, 0xC17E, 0x752F, 0x4031,
0x3147, 0xAD77, 0x7531, 0xC224, 0x7532, 0x3D55, 0xC226, 0x4B66,
- 0x3A72, 0xC227, 0xAD78, 0x7533, 0xC22A, 0x3E3C, 0xC22B, 0x4027,
+ 0x3A72, 0xC227, 0xAD78, 0x7533, 0xC22A, 0x3E3C, 0, 0x4027,
0x7534, 0x7535, 0, 0x7536, 0x5A65, 0x5A63, 0x5A64, 0xC230,
- 0, 0xC22F, 0x7530, 0xF442, 0x436B, 0, 0, 0x5B26,
+ 0, 0xC22F, 0x7530, 0, 0x436B, 0, 0, 0x5B26,
};
static const unsigned short utf8_to_euc_E699[] = {
0xC231, 0x5A6A, 0x3B7E, 0x3938, 0x5A68, 0xC232, 0xC233, 0,
@@ -7379,7 +7379,7 @@ static const unsigned short utf8_to_euc_E69C[] = {
};
static const unsigned short utf8_to_euc_E69C_x0213[] = {
0x3A47, 0xAE37, 0, 0x5072, 0, 0xAE38, 0, 0xC26F,
- 0x376E, 0x4D2D, 0, 0x4A7E, 0, 0x497E, 0xC270, 0x5B2C,
+ 0x376E, 0x4D2D, 0, 0x4A7E, 0, 0x497E, 0, 0x5B2C,
0, 0, 0xAE39, 0x754D, 0x3A73, 0x443F, 0x5B2D, 0x4F2F,
0, 0xAE3B, 0, 0x4B3E, 0xC273, 0x442B, 0x5B2E, 0x347C,
0xC274, 0, 0xC275, 0, 0, 0, 0x5B2F, 0x5B30,
@@ -7682,7 +7682,7 @@ static const unsigned short utf8_to_euc_E6AB_x0213[] = {
0xC54D, 0xC54C, 0, 0, 0xC54E, 0, 0, 0,
0xAF66, 0x5D2A, 0, 0x4F26, 0xAF65, 0xC551, 0xC552, 0,
0, 0, 0x5D2D, 0x367B, 0xAF67, 0xAF68, 0x5D29, 0x5D2B,
- 0, 0, 0xF44A, 0, 0x7638, 0, 0, 0x7639,
+ 0, 0, 0, 0, 0x7638, 0, 0, 0x7639,
0x4827, 0, 0x5D2E, 0, 0xAF6B, 0, 0, 0,
0xC558, 0xAF6C, 0xAF6D, 0xAF6E, 0, 0, 0, 0,
0, 0, 0x5D32, 0x5D2F, 0xC55B, 0xAF6F, 0, 0,
@@ -7803,7 +7803,7 @@ static const unsigned short utf8_to_euc_E6B1_x0213[] = {
0x3C2E, 0, 0xC65C, 0, 0xC65D, 0x5D68, 0, 0x3440,
0, 0x7651, 0x3178, 0xEE37, 0x7652, 0x4672, 0x5D67, 0x393E,
0x4353, 0, 0x5D69, 0, 0, 0, 0, 0xEE4F,
- 0x5D71, 0, 0x5D6A, 0xC661, 0, 0xEE38, 0, 0xC663,
+ 0x5D71, 0, 0x5D6A, 0xC661, 0, 0xEE38, 0, 0,
0x4241, 0, 0x3562, 0x5D72, 0x7654, 0, 0x7655, 0,
0xC666, 0xC667, 0x3768, 0xC668, 0, 0x3525, 0x5D70, 0,
};
@@ -7883,7 +7883,7 @@ static const unsigned short utf8_to_euc_E6B5_x0213[] = {
0, 0, 0, 0, 0, 0, 0, 0xC73D,
0x7667, 0x5E36, 0x5E34, 0xEE52, 0x494D, 0, 0xEE53, 0xC73F,
0xEE54, 0xC740, 0, 0x5E31, 0x5E33, 0x7668, 0x313A, 0xC742,
- 0, 0x3940, 0x4F32, 0, 0x333D, 0, 0x4962, 0xC743,
+ 0, 0x3940, 0x4F32, 0, 0x333D, 0, 0x4962, 0,
0xEE55, 0, 0, 0, 0x4D61, 0, 0, 0x3324,
0x3F3B, 0x5E35, 0, 0, 0xC745, 0, 0, 0,
};
@@ -7900,7 +7900,7 @@ static const unsigned short utf8_to_euc_E6B6[] = {
static const unsigned short utf8_to_euc_E6B6_x0213[] = {
0xEE56, 0xEE57, 0x766A, 0, 0, 0x5E3A, 0, 0x766B,
0x3E43, 0x766C, 0xEE58, 0, 0x4D30, 0xEE59, 0x5E37, 0,
- 0, 0xEE5A, 0xC749, 0x5E32, 0x766D, 0x5E38, 0xC74B, 0xC74C,
+ 0, 0xEE5A, 0xC749, 0x5E32, 0x766D, 0x5E38, 0, 0xC74C,
0xEE5B, 0x4E5E, 0, 0x4573, 0x4642, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0x766E, 0xEE61, 0x766F, 0, 0xEE62, 0x3336,
@@ -7924,8 +7924,8 @@ static const unsigned short utf8_to_euc_E6B7_x0213[] = {
0x4571, 0x5E4A, 0x7673, 0x7674, 0, 0x7675, 0x5E44, 0xEE6A,
0xC75E, 0x4338, 0xC75F, 0, 0x5E4B, 0xC760, 0x5E40, 0,
0x5E46, 0xEE6B, 0x5E4D, 0x307C, 0x5E43, 0, 0x5E4E, 0xC762,
- 0xC763, 0x3F3C, 0xF44C, 0x3D5F, 0xC764, 0x4A25, 0xEE6C, 0x3A2E,
- 0xF44B, 0x5E3B, 0x5E49, 0x453A, 0x7676, 0, 0, 0,
+ 0xC763, 0x3F3C, 0, 0x3D5F, 0xC764, 0x4A25, 0xEE6C, 0x3A2E,
+ 0, 0x5E3B, 0x5E49, 0x453A, 0x7676, 0, 0, 0,
};
static const unsigned short utf8_to_euc_E6B8[] = {
0xC767, 0, 0, 0, 0xC768, 0x4036, 0, 0x3369,
@@ -7945,7 +7945,7 @@ static const unsigned short utf8_to_euc_E6B8_x0213[] = {
0x3574, 0x454F, 0xEE6F, 0x5E56, 0x5E5F, 0x302F, 0x3132, 0xEE70,
0, 0x3239, 0, 0x5E58, 0x422C, 0x5E4F, 0x5E51, 0x3941,
0, 0, 0xEE72, 0, 0x7678, 0, 0xEE6D, 0,
- 0x5E62, 0xC76E, 0x5E5D, 0xC76F, 0xEE73, 0, 0x5E55, 0,
+ 0x5E62, 0, 0x5E5D, 0xC76F, 0xEE73, 0, 0x5E55, 0,
};
static const unsigned short utf8_to_euc_E6B9[] = {
0, 0, 0, 0x5E5C, 0xC771, 0xC772, 0, 0,
@@ -8342,7 +8342,7 @@ static const unsigned short utf8_to_euc_E78C_x0213[] = {
0, 0, 0x6049, 0xCB26, 0, 0xCB27, 0, 0,
0, 0, 0xF046, 0xCB29, 0, 0, 0x604B, 0x6048,
0xF047, 0xF048, 0, 0x4C54, 0x604A, 0x604C, 0xCB2C, 0x4E44,
- 0, 0, 0xCB2D, 0, 0xCB2E, 0x6050, 0, 0x776D,
+ 0, 0, 0xCB2D, 0, 0, 0x6050, 0, 0x776D,
0x776E, 0x604F, 0x4376, 0x472D, 0xF04B, 0, 0x3825, 0x604E,
0, 0xF04C, 0xCB33, 0xF04D, 0x604D, 0xCB34, 0x4D31, 0x4D32,
0, 0xF04A, 0xCB35, 0xCB36, 0, 0xF04E, 0x6051, 0x316E,
@@ -8385,7 +8385,7 @@ static const unsigned short utf8_to_euc_E78E_x0213[] = {
0x7775, 0, 0x7776, 0, 0, 0xF05F, 0x7777, 0,
0xF060, 0x3461, 0xCB5F, 0x7778, 0, 0xCB61, 0, 0,
0, 0, 0x4E68, 0x605E, 0, 0xF061, 0, 0xF062,
- 0, 0xF063, 0, 0x6060, 0xF064, 0xCB66, 0, 0xF065,
+ 0, 0xF063, 0, 0x6060, 0xF064, 0, 0, 0xF065,
};
static const unsigned short utf8_to_euc_E78F[] = {
0x6061, 0, 0x3251, 0, 0, 0xCB68, 0xCB69, 0,
@@ -8400,11 +8400,11 @@ static const unsigned short utf8_to_euc_E78F[] = {
static const unsigned short utf8_to_euc_E78F_x0213[] = {
0x6061, 0, 0x3251, 0, 0, 0xF066, 0xCB69, 0,
0x605D, 0x7779, 0x3B39, 0xF067, 0xCB6C, 0x4441, 0x605F, 0x777A,
- 0, 0, 0xCB6E, 0xCB6F, 0, 0, 0x777B, 0,
+ 0, 0, 0, 0xCB6F, 0, 0, 0x777B, 0,
0, 0x777C, 0, 0, 0, 0xCB72, 0x6064, 0,
0x3C6E, 0xF068, 0, 0x777D, 0, 0x6062, 0xCB75, 0xF069,
0, 0x777E, 0x373E, 0, 0, 0x4849, 0x6063, 0,
- 0, 0x607E, 0, 0, 0xCB78, 0xCB79, 0, 0xCB7A,
+ 0, 0x607E, 0, 0, 0xCB78, 0, 0, 0xCB7A,
0x6069, 0xF06A, 0xF06C, 0xCB7D, 0, 0xCB7E, 0x383D, 0xCC21,
};
static const unsigned short utf8_to_euc_E790[] = {
@@ -8423,7 +8423,7 @@ static const unsigned short utf8_to_euc_E790_x0213[] = {
0, 0xCC27, 0, 0xF06B, 0, 0, 0, 0,
0, 0, 0x7823, 0x7824, 0, 0, 0, 0,
0, 0, 0x4276, 0, 0xF06E, 0x6068, 0x7826, 0,
- 0x7827, 0xCC2D, 0x7828, 0x7829, 0x782A, 0xCC31, 0x782B, 0x782C,
+ 0x7827, 0, 0x7828, 0x7829, 0x782A, 0xCC31, 0x782B, 0x782C,
0x782D, 0xF06F, 0x606A, 0x4E56, 0x3657, 0x487C, 0x474A, 0,
0, 0xF070, 0x606B, 0, 0, 0, 0, 0x606D,
};
@@ -8618,7 +8618,7 @@ static const unsigned short utf8_to_euc_E79A[] = {
0x6230, 0x6231, 0x6232, 0, 0, 0xCE48, 0, 0x3B2E,
};
static const unsigned short utf8_to_euc_E79A_x0213[] = {
- 0x6225, 0x7860, 0xF451, 0x6226, 0x452A, 0xCE36, 0x3327, 0x3944,
+ 0x6225, 0x7860, 0, 0x6226, 0x452A, 0xCE36, 0x3327, 0x3944,
0x6227, 0, 0, 0x6228, 0xCE37, 0xCE38, 0x6229, 0,
0x3B29, 0, 0, 0x622B, 0, 0xF16E, 0x622A, 0,
0, 0x622C, 0x622D, 0x7861, 0xF16F, 0x7862, 0x7863, 0xCE3D,
@@ -8765,7 +8765,7 @@ static const unsigned short utf8_to_euc_E7A1_x0213[] = {
0xCF66, 0xCF67, 0, 0xCF68, 0xF246, 0, 0, 0,
0x7925, 0, 0xF247, 0x4E32, 0x3945, 0, 0x7926, 0x3827,
0, 0, 0x4823, 0, 0x626D, 0, 0, 0,
- 0, 0, 0xCF6D, 0, 0x626F, 0, 0xCF6E, 0,
+ 0, 0, 0, 0, 0x626F, 0, 0xCF6E, 0,
};
static const unsigned short utf8_to_euc_E7A2[] = {
0, 0x386B, 0, 0, 0, 0, 0x626E, 0x4476,
@@ -9322,7 +9322,7 @@ static const unsigned short utf8_to_euc_E7BE_x0213[] = {
0x6633, 0, 0x4D53, 0xD526, 0x6635, 0xD527, 0x487E, 0xD528,
0xF473, 0x7A3B, 0, 0, 0x6636, 0, 0xF476, 0x7A3C,
0, 0, 0x6639, 0, 0xF477, 0x6638, 0x6637, 0,
- 0, 0xD52E, 0xD52F, 0x663A, 0x3732, 0, 0xD530, 0,
+ 0, 0, 0xD52F, 0x663A, 0x3732, 0, 0xD530, 0,
0x4122, 0x3541, 0xD531, 0, 0, 0xF478, 0x663E, 0x663B,
0, 0, 0x663C, 0, 0xD533, 0, 0x663F, 0,
0x6640, 0x663D, 0, 0, 0xD534, 0x3129, 0, 0x7A3D,
@@ -9664,7 +9664,7 @@ static const unsigned short utf8_to_euc_E88F_x0213[] = {
0x6845, 0, 0, 0, 0x3A5A, 0xF63E, 0, 0x4551,
0x684A, 0x7B22, 0, 0, 0, 0xF63F, 0, 0,
0xD83F, 0x4A6E, 0x7B23, 0x6841, 0, 0, 0, 0x325A,
- 0x3856, 0x4929, 0x684B, 0, 0x683F, 0, 0xD841, 0x6848,
+ 0x3856, 0x4929, 0x684B, 0, 0x683F, 0, 0, 0x6848,
0xD842, 0xF640, 0, 0x6852, 0xD844, 0x6843, 0, 0,
};
static const unsigned short utf8_to_euc_E890[] = {
@@ -9783,7 +9783,7 @@ static const unsigned short utf8_to_euc_E895_x0213[] = {
0xD943, 0xF670, 0xD945, 0xF671, 0, 0x6924, 0xD947, 0x4979,
0x687D, 0x7B38, 0x6856, 0, 0xD949, 0xD94A, 0xF672, 0xD94C,
0xD94D, 0xF673, 0xF674, 0x687C, 0x7B39, 0, 0, 0,
- 0x4F4F, 0x4622, 0x4973, 0xD951, 0, 0x692B, 0, 0xF66C,
+ 0x4F4F, 0x4622, 0x4973, 0, 0, 0x692B, 0, 0xF66C,
0, 0, 0, 0, 0, 0, 0, 0x6931,
0, 0xD953, 0x7B3C, 0xF676, 0, 0xF677, 0x6932, 0xF678,
};
@@ -9998,7 +9998,7 @@ static const unsigned short utf8_to_euc_E8A0[] = {
0, 0x6A45, 0xDC21, 0x6A47, 0xDC22, 0, 0, 0,
};
static const unsigned short utf8_to_euc_E8A0_x0213[] = {
- 0, 0xF77A, 0, 0xF77B, 0, 0x6A24, 0x7B63, 0xF464,
+ 0, 0xF77A, 0, 0xF77B, 0, 0x6A24, 0x7B63, 0,
0, 0xDB6B, 0x7B64, 0xF77C, 0, 0x6A38, 0x6A3C, 0x6A37,
0x7B65, 0x6A3E, 0xDB70, 0xF77D, 0x7B66, 0x6A40, 0x6A3F, 0,
0xDB73, 0xDB6F, 0xDB74, 0xDB75, 0xDB76, 0, 0xDB77, 0x7B67,
@@ -10260,7 +10260,7 @@ static const unsigned short utf8_to_euc_E8AD[] = {
static const unsigned short utf8_to_euc_E8AD_x0213[] = {
0, 0x6B76, 0xDE44, 0xF86A, 0xDE46, 0xDE47, 0x7C31, 0,
0xDE49, 0x6B7A, 0, 0, 0x6B77, 0xDE4E, 0x6B79, 0x6B78,
- 0, 0xF86C, 0xDE4A, 0xDE4B, 0x7C32, 0, 0x6B7B, 0,
+ 0, 0xF86C, 0xDE4A, 0, 0x7C32, 0, 0x6B7B, 0,
0x3C31, 0x7C33, 0x6B7D, 0x6B7C, 0x4968, 0, 0xF86D, 0x6C21,
0, 0, 0, 0xDE50, 0, 0, 0x3759, 0,
0, 0x7C34, 0, 0x6B7E, 0x6C22, 0xDE51, 0, 0x6C23,
@@ -10404,7 +10404,7 @@ static const unsigned short utf8_to_euc_E8B5_x0213[] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0x4056, 0xDF46, 0x3C4F, 0x6C5F,
0, 0xDF47, 0, 0x3352, 0xF935, 0x6C60, 0xDF49, 0,
- 0x4176, 0x6C61, 0, 0x6C62, 0x496B, 0, 0xF468, 0x352F,
+ 0x4176, 0x6C61, 0, 0x6C62, 0x496B, 0, 0, 0x352F,
0, 0, 0, 0, 0, 0, 0, 0xDF4A,
};
static const unsigned short utf8_to_euc_E8B6[] = {
@@ -10519,7 +10519,7 @@ static const unsigned short utf8_to_euc_E8BB[] = {
};
static const unsigned short utf8_to_euc_E8BB_x0213[] = {
0x7C4A, 0xE055, 0, 0xE056, 0xE057, 0x6D40, 0x6D3D, 0xE058,
- 0x6D41, 0, 0x3C56, 0x6D42, 0x3530, 0x3733, 0, 0xE059,
+ 0x6D41, 0, 0x3C56, 0x6D42, 0x3530, 0x3733, 0, 0,
0, 0xF95A, 0x382E, 0, 0xF95B, 0, 0, 0,
0, 0, 0, 0x6D43, 0xE05C, 0, 0, 0x4670,
0, 0, 0x453E, 0x6D44, 0, 0, 0, 0,
@@ -10642,7 +10642,7 @@ static const unsigned short utf8_to_euc_E981_x0213[] = {
0, 0x6E26, 0x4D37, 0x313F, 0xE15D, 0x4A57, 0x3261, 0x6E21,
0x6E22, 0x6E23, 0x6E24, 0x463B, 0x4323, 0x3063, 0x6E28, 0,
0x6E29, 0x7423, 0, 0xE15E, 0x423D, 0xF97D, 0x6E2A, 0,
- 0x3173, 0x414C, 0xE160, 0x382F, 0, 0x4D5A, 0xE161, 0xE162,
+ 0x3173, 0x414C, 0xE160, 0x382F, 0, 0x4D5A, 0xE161, 0,
0x6E2B, 0x452C, 0, 0, 0xE163, 0x4178, 0x3C57, 0x6E2C,
0xE164, 0, 0x6E2F, 0, 0xE165, 0x3D65, 0x6E2D, 0x412B,
0x412A, 0xE166, 0x3064, 0, 0x4E4B, 0x6E31, 0, 0x4872,
@@ -10761,7 +10761,7 @@ static const unsigned short utf8_to_euc_E987_x0213[] = {
0x6E56, 0x6E57, 0xE321, 0xFA4C, 0xFA4D, 0xE323, 0x4850, 0x3A53,
0x3C61, 0x6E58, 0, 0x6E59, 0x4E24, 0x3D45, 0x4C6E, 0x4E4C,
0x6E5A, 0x3662, 0, 0xE324, 0xE325, 0, 0x6E5B, 0x7C7C,
- 0x4523, 0xE327, 0xFA4E, 0x6E5E, 0x3378, 0x3F4B, 0xE329, 0x6E5C,
+ 0x4523, 0xE327, 0xFA4E, 0x6E5E, 0x3378, 0x3F4B, 0, 0x6E5C,
0, 0x6E5D, 0, 0x4460, 0x7C7E, 0x7D21, 0x4B55, 0x367C,
0, 0xE32C, 0xE32D, 0, 0xFA51, 0x7D22, 0xFA52, 0xE331,
0xE332, 0x7D23, 0, 0, 0, 0x6E60, 0x6E61, 0xE334,
@@ -10778,7 +10778,7 @@ static const unsigned short utf8_to_euc_E988[] = {
0xE353, 0xE354, 0xE355, 0, 0xE356, 0, 0xE357, 0x6E6F,
};
static const unsigned short utf8_to_euc_E988_x0213[] = {
- 0xE338, 0xFA53, 0, 0, 0xE33A, 0xE33B, 0xE33C, 0x7D24,
+ 0xE338, 0xFA53, 0, 0, 0xE33A, 0xE33B, 0, 0x7D24,
0, 0xE33E, 0xFA54, 0, 0xE340, 0x465F, 0x3343, 0,
0x7D25, 0x6E67, 0xE342, 0xE343, 0x6E64, 0x6E66, 0xFA55, 0xFA56,
0xE345, 0, 0, 0, 0xE346, 0xE347, 0x6E62, 0,
@@ -10804,7 +10804,7 @@ static const unsigned short utf8_to_euc_E989_x0213[] = {
0xE362, 0xFA5F, 0x6E76, 0x3174, 0xE364, 0xE365, 0x6E68, 0,
0xFA60, 0xFA61, 0x482D, 0, 0x6E6C, 0xFA62, 0x3E60, 0xFA63,
0xFA64, 0xE36B, 0, 0, 0, 0, 0xE36C, 0xE36D,
- 0xE36E, 0x395B, 0, 0, 0, 0xE36F, 0xE370, 0xE371,
+ 0xE36E, 0x395B, 0, 0, 0, 0xE36F, 0xE370, 0,
0x7D2D, 0xE373, 0, 0xE374, 0xFA67, 0xFA68, 0x4B48, 0xFA69,
};
static const unsigned short utf8_to_euc_E98A[] = {
@@ -10840,7 +10840,7 @@ static const unsigned short utf8_to_euc_E98B[] = {
static const unsigned short utf8_to_euc_E98B_x0213[] = {
0xFA75, 0xE433, 0x7D2F, 0xE435, 0, 0xE436, 0xFA76, 0xE438,
0xE439, 0, 0, 0x7D30, 0x7D31, 0xE43C, 0xFA77, 0x6E77,
- 0xFA78, 0, 0x4B2F, 0x7D32, 0, 0xE440, 0, 0xFA79,
+ 0xFA78, 0, 0x4B2F, 0x7D32, 0, 0, 0, 0xFA79,
0xE442, 0xFA7A, 0, 0, 0xE444, 0xE445, 0, 0xE446,
0x7D33, 0xE448, 0, 0xE449, 0x3D7B, 0xFA7B, 0, 0xFA7C,
0xE44C, 0x6E7A, 0x4A5F, 0, 0xE44D, 0x3154, 0xE44E, 0,
@@ -10902,7 +10902,7 @@ static const unsigned short utf8_to_euc_E98E_x0213[] = {
0xFB38, 0, 0xE528, 0xFB39, 0x3379, 0xE52A, 0, 0xFB3A,
0, 0, 0xE52C, 0, 0x6F30, 0xE52D, 0x3A3F, 0x4179,
0xE52E, 0, 0x444A, 0x7D40, 0, 0, 0xFB3B, 0,
- 0, 0xFB35, 0, 0x7D41, 0xE533, 0, 0xE534, 0x333B,
+ 0, 0xFB35, 0, 0x7D41, 0, 0, 0xE534, 0x333B,
0xE535, 0xE53B, 0, 0xE536, 0x6F2E, 0x6F2F, 0x4443, 0,
0x6F2D, 0, 0, 0, 0xE537, 0xE538, 0xE539, 0,
0, 0x6F31, 0x7D42, 0, 0, 0, 0, 0,
@@ -10925,7 +10925,7 @@ static const unsigned short utf8_to_euc_E98F_x0213[] = {
0, 0x3640, 0xFB43, 0, 0x6F3B, 0x6F35, 0xE54C, 0xFB44,
0x6F34, 0, 0, 0, 0, 0, 0, 0,
0, 0xFB3F, 0, 0, 0, 0xFB3C, 0, 0xE54F,
- 0xE550, 0xE54E, 0xE551, 0xFB49, 0, 0x7D47, 0, 0,
+ 0, 0xE54E, 0xE551, 0xFB49, 0, 0x7D47, 0, 0,
};
static const unsigned short utf8_to_euc_E990[] = {
0, 0xE554, 0xE555, 0x6F3F, 0xE556, 0, 0, 0x6F40,
@@ -10944,7 +10944,7 @@ static const unsigned short utf8_to_euc_E990_x0213[] = {
0x3E62, 0x462A, 0x6F3C, 0, 0, 0, 0, 0xE55F,
0, 0x6F45, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0x6F43, 0, 0, 0xE560, 0xE561,
- 0, 0xE562, 0xFB4A, 0x7D48, 0xFB4B, 0x6F44, 0x6F42, 0,
+ 0, 0, 0xFB4A, 0x7D48, 0xFB4B, 0x6F44, 0x6F42, 0,
0x4278, 0, 0x6F46, 0xFB4C, 0, 0xE568, 0, 0xE567,
};
static const unsigned short utf8_to_euc_E991[] = {
@@ -10959,7 +10959,7 @@ static const unsigned short utf8_to_euc_E991[] = {
};
static const unsigned short utf8_to_euc_E991_x0213[] = {
0, 0x6F47, 0, 0xE569, 0x6F49, 0xFB4D, 0, 0,
- 0xE56B, 0, 0x7D49, 0, 0xE56D, 0, 0, 0,
+ 0, 0, 0x7D49, 0, 0xE56D, 0, 0, 0,
0, 0x3455, 0x6F48, 0x4C7A, 0, 0xE56E, 0, 0,
0, 0xE56F, 0x6F54, 0x6F4A, 0xE570, 0, 0x6F4D, 0xE571,
0x6F4B, 0xE572, 0x6F4C, 0x7D4A, 0, 0, 0, 0,
@@ -11010,7 +11010,7 @@ static const unsigned short utf8_to_euc_E996[] = {
static const unsigned short utf8_to_euc_E996_x0213[] = {
0x4C67, 0, 0x6F59, 0x412E, 0xE622, 0, 0xFB54, 0x6F5A,
0xE623, 0x4A44, 0x6F5B, 0x332B, 0xFB55, 0xFB56, 0x7D4E, 0x313C,
- 0, 0x3457, 0xF471, 0x3456, 0x6F5C, 0, 0x6F5D, 0,
+ 0, 0x3457, 0, 0x3456, 0x6F5C, 0, 0x6F5D, 0,
0x6F5E, 0x6F5F, 0, 0, 0, 0xE627, 0xE628, 0x7D4F,
0x6F60, 0xE62A, 0x3458, 0x3355, 0x395E, 0x4836, 0x7D50, 0x6F62,
0x6F61, 0x7D51, 0, 0xFB58, 0x7D52, 0x6F63, 0, 0,
@@ -11148,9 +11148,9 @@ static const unsigned short utf8_to_euc_E99D[] = {
0xE73E, 0x7058, 0x705C, 0xE73F, 0x705A, 0xE740, 0, 0xE741,
};
static const unsigned short utf8_to_euc_E99D_x0213[] = {
- 0, 0xFB7A, 0x704E, 0xE72E, 0x704B, 0, 0x704C, 0xFB7B,
+ 0, 0xFB7A, 0x704E, 0, 0x704B, 0, 0x704C, 0xFB7B,
0x704D, 0x704F, 0xE72F, 0, 0, 0x7D68, 0x7D69, 0x7D6A,
- 0, 0xF476, 0x4044, 0, 0, 0xFB7C, 0x4C77, 0xFB7D,
+ 0, 0, 0x4044, 0, 0, 0xFB7C, 0x4C77, 0xFB7D,
0xE734, 0x4045, 0x7D6B, 0xFB7E, 0x7050, 0, 0x4873, 0,
0x7051, 0x7353, 0x4C4C, 0xE737, 0x7052, 0, 0x7053, 0xE738,
0x7054, 0x3357, 0xFC21, 0x7056, 0, 0x3F59, 0x7D6C, 0,
@@ -11292,7 +11292,7 @@ static const unsigned short utf8_to_euc_E9A4_x0213[] = {
0xFC54, 0x712E, 0x4D5C, 0, 0x3142, 0, 0, 0,
0x3B41, 0xE853, 0x712F, 0x326E, 0x7130, 0xE854, 0xFC57, 0xFC58,
0x7131, 0, 0xFC5A, 0xFC5B, 0xFC5C, 0x7133, 0x7134, 0xE85A,
- 0x7136, 0x7132, 0xE85B, 0, 0x7135, 0, 0xE85C, 0xE85D,
+ 0x7136, 0x7132, 0xE85B, 0, 0x7135, 0, 0xE85C, 0,
0x345B, 0, 0, 0xE85E, 0x7137, 0, 0x7138, 0,
0, 0xFC5E, 0xFC5F, 0xFC60, 0xE862, 0xE863, 0, 0,
0, 0xE864, 0xFC61, 0xFC62, 0xFC63, 0x7139, 0x713A, 0,
@@ -11431,7 +11431,7 @@ static const unsigned short utf8_to_euc_E9AB_x0213[] = {
0x716F, 0x7E36, 0, 0x7E37, 0x3F71, 0, 0xFD2D, 0,
0xE965, 0, 0, 0, 0, 0, 0x7E38, 0x7170,
0xFD2E, 0x7171, 0xFD2F, 0x7172, 0x7173, 0xFD30, 0x7E39, 0xE96B,
- 0x3962, 0xF47B, 0, 0xE96C, 0xFD32, 0, 0x7174, 0x7175,
+ 0x3962, 0, 0, 0xE96C, 0xFD32, 0, 0x7174, 0x7175,
0xFD33, 0, 0x7176, 0x7177, 0xE96F, 0xFD34, 0x7178, 0xE971,
0, 0xFD35, 0x4831, 0x717A, 0xE973, 0x4926, 0x717B, 0x7179,
0, 0x717D, 0xE974, 0xE975, 0x717C, 0xE976, 0, 0x717E,
@@ -11495,7 +11495,7 @@ static const unsigned short utf8_to_euc_E9AE_x0213[] = {
0x723E, 0, 0, 0, 0, 0, 0xFD48, 0x7E49,
0x723F, 0xEA63, 0x4B6E, 0x3B2D, 0xFD49, 0x3A7A, 0x412F, 0,
0xEA65, 0xFD4A, 0xFD4D, 0, 0x7240, 0, 0, 0xEA68,
- 0xFD4E, 0x7243, 0, 0xEA6A, 0xEA6B, 0, 0xFD4F, 0xEA6D,
+ 0xFD4E, 0x7243, 0, 0, 0xEA6B, 0, 0xFD4F, 0xEA6D,
};
static const unsigned short utf8_to_euc_E9AF[] = {
0x7241, 0xEA6E, 0, 0, 0, 0, 0x7244, 0xEA6F,
@@ -11859,11 +11859,11 @@ static const unsigned short utf8_to_euc_EFA8[] = {
};
static const unsigned short utf8_to_euc_EFA8_x0213[] = {
0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0xF434, 0x2F4B,
- 0x2F57, 0x4F72, 0xF444, 0xAE79, 0x757A, 0x775A, 0x776F, 0xF453,
- 0xF455, 0x793C, 0x793D, 0x7941, 0xF45A, 0xF45B, 0xF45E, 0x7B3A,
- 0xF738, 0xF745, 0x7C2E, 0xF469, 0xF96E, 0xF46B, 0x7C6A, 0xF46F,
- 0xF470, 0xF473, 0xF477, 0xF478, 0xF479, 0xF47D, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0x2F4B,
+ 0x2F57, 0x4F72, 0, 0xAE79, 0x757A, 0x775A, 0x776F, 0,
+ 0, 0x793C, 0x793D, 0x7941, 0, 0, 0, 0x7B3A,
+ 0xF738, 0xF745, 0x7C2E, 0, 0xF96E, 0, 0x7C6A, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
0x2E38, 0x2E49, 0x2E50, 0x2E63, 0x2E68, 0x2E6E, 0x2F2C, 0x2F2F,
0x2F36, 0x2F5A, 0x2F5E, 0x4F61, 0x4F62, 0x7450, 0x745C, 0x745E,
};
@@ -11957,6 +11957,16 @@ static const unsigned short utf8_to_euc_EFBF[] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
+static const unsigned short utf8_to_euc_EFBF_x0213[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0x2131, 0, 0x216F, 0, 0,
+ 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 unsigned short *const utf8_to_euc_E1_x0213[] = {
0, 0, 0, 0,
0, 0, 0, 0,
@@ -12405,7 +12415,7 @@ static const unsigned short *const utf8_to_euc_EF_x0213[] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, utf8_to_euc_EFB9_x0213, 0, 0,
- utf8_to_euc_EFBC_x0213, utf8_to_euc_EFBD_x0213, utf8_to_euc_EFBE, utf8_to_euc_EFBF,
+ utf8_to_euc_EFBC_x0213, utf8_to_euc_EFBD_x0213, utf8_to_euc_EFBE, utf8_to_euc_EFBF_x0213,
};
const unsigned short *const utf8_to_euc_2bytes[] = {
0, 0, 0, 0,
diff --git a/ext/nkf/nkf.c b/ext/nkf/nkf.c
index 9613a925ce..c958c91753 100644
--- a/ext/nkf/nkf.c
+++ b/ext/nkf/nkf.c
@@ -137,8 +137,7 @@ rb_nkf_convert(VALUE obj, VALUE opt, VALUE src)
{
VALUE tmp;
reinit();
- StringValue(opt);
- nkf_split_options(RSTRING_PTR(opt));
+ nkf_split_options(StringValueCStr(opt));
if (!output_encoding) rb_raise(rb_eArgError, "no output encoding given");
switch (nkf_enc_to_index(output_encoding)) {
@@ -153,8 +152,7 @@ rb_nkf_convert(VALUE obj, VALUE opt, VALUE src)
incsize = INCSIZE;
input_ctr = 0;
- StringValue(src);
- input = (unsigned char *)RSTRING_PTR(src);
+ input = (unsigned char *)StringValuePtr(src);
i_len = RSTRING_LENINT(src);
tmp = rb_str_new(0, i_len*3 + 10);
@@ -195,8 +193,7 @@ rb_nkf_guess(VALUE obj, VALUE src)
reinit();
input_ctr = 0;
- StringValue(src);
- input = (unsigned char *)RSTRING_PTR(src);
+ input = (unsigned char *)StringValuePtr(src);
i_len = RSTRING_LENINT(src);
guess_f = TRUE;
diff --git a/ext/objspace/depend b/ext/objspace/depend
index dc3009fc50..642265618c 100644
--- a/ext/objspace/depend
+++ b/ext/objspace/depend
@@ -8,6 +8,7 @@ object_tracing.o: $(hdrdir)/ruby/encoding.h
object_tracing.o: $(hdrdir)/ruby/intern.h
object_tracing.o: $(hdrdir)/ruby/io.h
object_tracing.o: $(hdrdir)/ruby/missing.h
+object_tracing.o: $(hdrdir)/ruby/onigmo.h
object_tracing.o: $(hdrdir)/ruby/oniguruma.h
object_tracing.o: $(hdrdir)/ruby/ruby.h
object_tracing.o: $(hdrdir)/ruby/st.h
@@ -24,6 +25,7 @@ objspace.o: $(hdrdir)/ruby/encoding.h
objspace.o: $(hdrdir)/ruby/intern.h
objspace.o: $(hdrdir)/ruby/io.h
objspace.o: $(hdrdir)/ruby/missing.h
+objspace.o: $(hdrdir)/ruby/onigmo.h
objspace.o: $(hdrdir)/ruby/oniguruma.h
objspace.o: $(hdrdir)/ruby/re.h
objspace.o: $(hdrdir)/ruby/regex.h
@@ -46,6 +48,7 @@ objspace_dump.o: $(hdrdir)/ruby/encoding.h
objspace_dump.o: $(hdrdir)/ruby/intern.h
objspace_dump.o: $(hdrdir)/ruby/io.h
objspace_dump.o: $(hdrdir)/ruby/missing.h
+objspace_dump.o: $(hdrdir)/ruby/onigmo.h
objspace_dump.o: $(hdrdir)/ruby/oniguruma.h
objspace_dump.o: $(hdrdir)/ruby/ruby.h
objspace_dump.o: $(hdrdir)/ruby/st.h
diff --git a/ext/objspace/extconf.rb b/ext/objspace/extconf.rb
index adb8ef9169..d0c2dbd02b 100644
--- a/ext/objspace/extconf.rb
+++ b/ext/objspace/extconf.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
$INCFLAGS << " -I$(topdir) -I$(top_srcdir)"
$VPATH << '$(topdir)' << '$(top_srcdir)' # for id.h.
create_makefile('objspace')
diff --git a/ext/objspace/object_tracing.c b/ext/objspace/object_tracing.c
index d3169c6c48..7c354498ab 100644
--- a/ext/objspace/object_tracing.c
+++ b/ext/objspace/object_tracing.c
@@ -39,7 +39,8 @@ make_unique_str(st_table *tbl, const char *str, long len)
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, (st_data_t *)&result);
+ st_get_key(tbl, (st_data_t)str, &n);
+ result = (char *)n;
}
else {
result = (char *)ruby_xmalloc(len+1);
@@ -59,8 +60,9 @@ delete_unique_str(st_table *tbl, const char *str)
st_lookup(tbl, (st_data_t)str, &n);
if (n == 1) {
- st_delete(tbl, (st_data_t *)&str, 0);
- ruby_xfree((char *)str);
+ n = (st_data_t)str;
+ st_delete(tbl, &n, 0);
+ ruby_xfree((char *)n);
}
else {
st_insert(tbl, (st_data_t)str, n-1);
@@ -82,8 +84,10 @@ newobj_i(VALUE tpval, void *data)
const char *path_cstr = RTEST(path) ? make_unique_str(arg->str_table, RSTRING_PTR(path), RSTRING_LEN(path)) : 0;
VALUE class_path = (RTEST(klass) && !OBJ_FROZEN(klass)) ? rb_class_path_cached(klass) : Qnil;
const char *class_path_cstr = RTEST(class_path) ? make_unique_str(arg->str_table, RSTRING_PTR(class_path), RSTRING_LEN(class_path)) : 0;
+ st_data_t v;
- if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
+ 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 */
@@ -113,15 +117,19 @@ freeobj_i(VALUE tpval, void *data)
{
struct traceobj_arg *arg = (struct traceobj_arg *)data;
rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
- VALUE obj = rb_tracearg_object(tparg);
+ st_data_t obj = (st_data_t)rb_tracearg_object(tparg);
+ st_data_t v;
struct allocation_info *info;
- if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
- if (arg->keep_remains) {
+ if (arg->keep_remains) {
+ if (st_lookup(arg->object_table, obj, &v)) {
+ info = (struct allocation_info *)v;
info->living = 0;
}
- else {
- st_delete(arg->object_table, (st_data_t *)&obj, (st_data_t *)&info);
+ }
+ 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);
@@ -178,7 +186,9 @@ trace_object_allocations_start(VALUE self)
else {
if (arg->newobj_trace == 0) {
arg->newobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, arg);
+ rb_gc_register_mark_object(arg->newobj_trace);
arg->freeobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, arg);
+ rb_gc_register_mark_object(arg->freeobj_trace);
}
rb_tracepoint_enable(arg->newobj_trace);
rb_tracepoint_enable(arg->freeobj_trace);
@@ -208,8 +218,6 @@ trace_object_allocations_stop(VALUE self)
if (arg->running == 0) {
rb_tracepoint_disable(arg->newobj_trace);
rb_tracepoint_disable(arg->freeobj_trace);
- arg->newobj_trace = 0;
- arg->freeobj_trace = 0;
}
return Qnil;
@@ -321,9 +329,9 @@ static struct allocation_info *
lookup_allocation_info(VALUE obj)
{
if (tmp_trace_arg) {
- struct allocation_info *info;
- if (st_lookup(tmp_trace_arg->object_table, obj, (st_data_t *)&info)) {
- return info;
+ st_data_t info;
+ if (st_lookup(tmp_trace_arg->object_table, obj, &info)) {
+ return (struct allocation_info *)info;
}
}
return NULL;
@@ -356,7 +364,7 @@ allocation_sourcefile(VALUE self, VALUE obj)
}
/*
- * call-seq: allocation_sourceline(object) -> string
+ * call-seq: allocation_sourceline(object) -> integer
*
* Returns the original line from source for from the given +object+.
*
diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c
index 958ef71db2..a811441320 100644
--- a/ext/objspace/objspace.c
+++ b/ext/objspace/objspace.c
@@ -12,9 +12,9 @@
**********************************************************************/
+#include <ruby/io.h>
#include "internal.h"
#include <ruby/st.h>
-#include <ruby/io.h>
#include <ruby/re.h>
#include "node.h"
#include "gc.h"
@@ -33,7 +33,7 @@
* This method is only expected to work with C Ruby.
*
* From Ruby 2.2, memsize_of(obj) returns a memory size includes
- * sizeof(RVALUE).
+ * sizeof(RVALUE).
*/
static VALUE
@@ -203,7 +203,7 @@ type2sym(enum ruby_value_type i)
*
* Note that this information is incomplete. You need to deal with
* this information as only a *HINT*. Especially, total size of
- * T_DATA may not right size.
+ * T_DATA may be wrong.
*
* It returns a hash as:
* {:TOTAL=>1461154, :T_CLASS=>158280, :T_MODULE=>20672, :T_STRING=>527249, ...}
@@ -232,13 +232,6 @@ count_objects_size(int argc, VALUE *argv, VALUE os)
rb_objspace_each_objects(cos_i, &counts[0]);
- if (hash == Qnil) {
- hash = rb_hash_new();
- }
- else if (!RHASH_EMPTY_P(hash)) {
- st_foreach(RHASH_TBL(hash), set_zero_i, hash);
- }
-
for (i = 0; i <= T_MASK; i++) {
if (counts[i]) {
VALUE type = type2sym(i);
@@ -313,13 +306,6 @@ count_symbols(int argc, VALUE *argv, VALUE os)
size_t immortal_symbols = rb_sym_immortal_count();
rb_objspace_each_objects(cs_i, &dynamic_counts);
- if (hash == Qnil) {
- hash = rb_hash_new();
- }
- else if (!RHASH_EMPTY_P(hash)) {
- st_foreach(RHASH_TBL(hash), set_zero_i, hash);
- }
-
rb_hash_aset(hash, ID2SYM(rb_intern("mortal_dynamic_symbol")), SIZET2NUM(dynamic_counts.mortal));
rb_hash_aset(hash, ID2SYM(rb_intern("immortal_dynamic_symbol")), SIZET2NUM(dynamic_counts.immortal));
rb_hash_aset(hash, ID2SYM(rb_intern("immortal_static_symbol")), SIZET2NUM(immortal_symbols - dynamic_counts.immortal));
@@ -371,7 +357,7 @@ static VALUE
count_nodes(int argc, VALUE *argv, VALUE os)
{
size_t nodes[NODE_LAST+1];
- size_t i;
+ enum node_type i;
VALUE hash = setup_hash(argc, argv);
for (i = 0; i <= NODE_LAST; i++) {
@@ -380,28 +366,23 @@ count_nodes(int argc, VALUE *argv, VALUE os)
rb_objspace_each_objects(cn_i, &nodes[0]);
- if (hash == Qnil) {
- hash = rb_hash_new();
- }
- else if (!RHASH_EMPTY_P(hash)) {
- st_foreach(RHASH_TBL(hash), set_zero_i, hash);
- }
-
for (i=0; i<NODE_LAST; i++) {
if (nodes[i] != 0) {
VALUE node;
switch (i) {
-#define COUNT_NODE(n) case n: node = ID2SYM(rb_intern(#n)); break;
+#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_WHEN);
- COUNT_NODE(NODE_OPT_N);
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);
@@ -418,18 +399,18 @@ count_nodes(int argc, VALUE *argv, VALUE os)
COUNT_NODE(NODE_DASGN_CURR);
COUNT_NODE(NODE_GASGN);
COUNT_NODE(NODE_IASGN);
- COUNT_NODE(NODE_IASGN2);
COUNT_NODE(NODE_CDECL);
COUNT_NODE(NODE_CVASGN);
- COUNT_NODE(NODE_CVDECL);
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_ARRAY);
@@ -456,7 +437,7 @@ count_nodes(int argc, VALUE *argv, VALUE os)
COUNT_NODE(NODE_DXSTR);
COUNT_NODE(NODE_EVSTR);
COUNT_NODE(NODE_DREGX);
- COUNT_NODE(NODE_DREGX_ONCE);
+ COUNT_NODE(NODE_ONCE);
COUNT_NODE(NODE_ARGS);
COUNT_NODE(NODE_ARGS_AUX);
COUNT_NODE(NODE_OPT_ARG);
@@ -465,8 +446,6 @@ count_nodes(int argc, VALUE *argv, VALUE os)
COUNT_NODE(NODE_ARGSCAT);
COUNT_NODE(NODE_ARGSPUSH);
COUNT_NODE(NODE_SPLAT);
- COUNT_NODE(NODE_TO_ARY);
- COUNT_NODE(NODE_BLOCK_ARG);
COUNT_NODE(NODE_BLOCK_PASS);
COUNT_NODE(NODE_DEFN);
COUNT_NODE(NODE_DEFS);
@@ -489,15 +468,14 @@ count_nodes(int argc, VALUE *argv, VALUE os)
COUNT_NODE(NODE_ERRINFO);
COUNT_NODE(NODE_DEFINED);
COUNT_NODE(NODE_POSTEXE);
- COUNT_NODE(NODE_ALLOCA);
- COUNT_NODE(NODE_BMETHOD);
COUNT_NODE(NODE_DSYM);
COUNT_NODE(NODE_ATTRASGN);
- COUNT_NODE(NODE_PRELUDE);
COUNT_NODE(NODE_LAMBDA);
#undef COUNT_NODE
- default: node = INT2FIX(i);
+ case NODE_LAST: break;
}
+ UNREACHABLE;
+ set:
rb_hash_aset(hash, node, SIZET2NUM(nodes[i]));
}
}
@@ -576,7 +554,7 @@ count_tdata_objects(int argc, VALUE *argv, VALUE self)
return hash;
}
-static ID imemo_type_ids[imemo_mask+1];
+static ID imemo_type_ids[IMEMO_MASK+1];
static int
count_imemo_objects_i(void *vstart, void *vend, size_t stride, void *data)
@@ -639,7 +617,7 @@ count_imemo_objects(int argc, VALUE *argv, VALUE self)
VALUE hash = setup_hash(argc, argv);
if (imemo_type_ids[0] == 0) {
- imemo_type_ids[0] = rb_intern("imemo_none");
+ imemo_type_ids[0] = rb_intern("imemo_env");
imemo_type_ids[1] = rb_intern("imemo_cref");
imemo_type_ids[2] = rb_intern("imemo_svar");
imemo_type_ids[3] = rb_intern("imemo_throw_data");
@@ -647,6 +625,9 @@ count_imemo_objects(int argc, VALUE *argv, VALUE self)
imemo_type_ids[5] = rb_intern("imemo_memo");
imemo_type_ids[6] = rb_intern("imemo_ment");
imemo_type_ids[7] = rb_intern("imemo_iseq");
+ imemo_type_ids[8] = rb_intern("imemo_tmpbuf");
+ imemo_type_ids[9] = rb_intern("imemo_ast");
+ imemo_type_ids[10] = rb_intern("imemo_parser_strterm");
}
rb_objspace_each_objects(count_imemo_objects_i, (void *)hash);
@@ -844,7 +825,7 @@ static int
collect_values_of_values(VALUE category, VALUE category_objects, VALUE categories)
{
VALUE ary = rb_ary_new();
- st_foreach(rb_hash_tbl(category_objects), collect_values, ary);
+ rb_hash_foreach(category_objects, collect_values, ary);
rb_hash_aset(categories, category, ary);
return ST_CONTINUE;
}
@@ -956,6 +937,7 @@ void Init_objspace_dump(VALUE rb_mObjSpace);
void
Init_objspace(void)
{
+#undef rb_intern
VALUE rb_mObjSpace;
#if 0
rb_mObjSpace = rb_define_module("ObjectSpace"); /* let rdoc know */
diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c
index 127d33283a..602cbadef5 100644
--- a/ext/objspace/objspace_dump.c
+++ b/ext/objspace/objspace_dump.c
@@ -12,15 +12,16 @@
**********************************************************************/
+#include "ruby/io.h"
#include "internal.h"
#include "ruby/debug.h"
-#include "ruby/io.h"
#include "gc.h"
#include "node.h"
#include "vm_core.h"
#include "objspace.h"
static VALUE sym_output, sym_stdout, sym_string, sym_file;
+static VALUE sym_full;
struct dump_config {
VALUE type;
@@ -31,6 +32,7 @@ struct dump_config {
VALUE cur_obj;
VALUE cur_obj_klass;
size_t cur_obj_references;
+ int full_heap;
};
PRINTF_ARGS(static void dump_append(struct dump_config *, const char *, ...), 2, 3);
@@ -103,7 +105,7 @@ static inline const char *
obj_type(VALUE obj)
{
switch (BUILTIN_TYPE(obj)) {
-#define CASE_TYPE(type) case T_##type: return #type; break
+#define CASE_TYPE(type) case T_##type: return #type
CASE_TYPE(NONE);
CASE_TYPE(NIL);
CASE_TYPE(OBJECT);
@@ -170,9 +172,9 @@ reachable_object_i(VALUE ref, void *data)
return;
if (dc->cur_obj_references == 0)
- dump_append(dc, ", \"references\":[\"%p\"", (void *)ref);
+ dump_append(dc, ", \"references\":[\"%#"PRIxVALUE"\"", ref);
else
- dump_append(dc, ", \"%p\"", (void *)ref);
+ dump_append(dc, ", \"%#"PRIxVALUE"\"", ref);
dc->cur_obj_references++;
}
@@ -190,6 +192,28 @@ dump_append_string_content(struct dump_config *dc, VALUE obj)
}
}
+static const char *
+imemo_name(int imemo)
+{
+ switch(imemo) {
+#define TYPE_STR(t) case(imemo_##t): return #t
+ TYPE_STR(env);
+ TYPE_STR(cref);
+ TYPE_STR(svar);
+ TYPE_STR(throw_data);
+ TYPE_STR(ifunc);
+ TYPE_STR(memo);
+ TYPE_STR(ment);
+ TYPE_STR(iseq);
+ TYPE_STR(tmpbuf);
+ TYPE_STR(ast);
+ TYPE_STR(parser_strterm);
+ default:
+ return "unknown";
+#undef TYPE_STR
+ }
+}
+
static void
dump_object(VALUE obj, struct dump_config *dc)
{
@@ -211,16 +235,20 @@ dump_object(VALUE obj, struct dump_config *dc)
if (dc->cur_obj == dc->string)
return;
- dump_append(dc, "{\"address\":\"%p\", \"type\":\"%s\"", (void *)obj, obj_type(obj));
+ dump_append(dc, "{\"address\":\"%#"PRIxVALUE"\", \"type\":\"%s\"", obj, obj_type(obj));
if (dc->cur_obj_klass)
- dump_append(dc, ", \"class\":\"%p\"", (void *)dc->cur_obj_klass);
+ dump_append(dc, ", \"class\":\"%#"PRIxVALUE"\"", dc->cur_obj_klass);
if (rb_obj_frozen_p(obj))
dump_append(dc, ", \"frozen\":true");
switch (BUILTIN_TYPE(obj)) {
- case T_NODE:
- dump_append(dc, ", \"node_type\":\"%s\"", ruby_node_name(nd_type(obj)));
+ case T_NONE:
+ dump_append(dc, "}\n");
+ return;
+
+ case T_IMEMO:
+ dump_append(dc, ", \"imemo_type\":\"%s\"", imemo_name(imemo_type(obj)));
break;
case T_SYMBOL:
@@ -246,7 +274,7 @@ dump_object(VALUE obj, struct dump_config *dc)
case T_HASH:
dump_append(dc, ", \"size\":%"PRIuSIZE, (size_t)RHASH_SIZE(obj));
if (FL_TEST(obj, HASH_PROC_DEFAULT))
- dump_append(dc, ", \"default\":\"%p\"", (void *)RHASH_IFNONE(obj));
+ dump_append(dc, ", \"default\":\"%#"PRIxVALUE"\"", RHASH_IFNONE(obj));
break;
case T_ARRAY:
@@ -318,10 +346,11 @@ dump_object(VALUE obj, struct dump_config *dc)
static int
heap_i(void *vstart, void *vend, size_t stride, void *data)
{
+ struct dump_config *dc = (struct dump_config *)data;
VALUE v = (VALUE)vstart;
for (; v != (VALUE)vend; v += stride) {
- if (RBASIC(v)->flags)
- dump_object(v, data);
+ if (dc->full_heap || RBASIC(v)->flags)
+ dump_object(v, dc);
}
return 0;
}
@@ -334,9 +363,9 @@ root_obj_i(const char *category, VALUE obj, void *data)
if (dc->root_category != NULL && category != dc->root_category)
dump_append(dc, "]}\n");
if (dc->root_category == NULL || category != dc->root_category)
- dump_append(dc, "{\"type\":\"ROOT\", \"root\":\"%s\", \"references\":[\"%p\"", category, (void *)obj);
+ dump_append(dc, "{\"type\":\"ROOT\", \"root\":\"%s\", \"references\":[\"%#"PRIxVALUE"\"", category, obj);
else
- dump_append(dc, ", \"%p\"", (void *)obj);
+ dump_append(dc, ", \"%#"PRIxVALUE"\"", obj);
dc->root_category = category;
dc->roots++;
@@ -347,9 +376,15 @@ dump_output(struct dump_config *dc, VALUE opts, VALUE output, const char *filena
{
VALUE tmp;
- if (RTEST(opts))
+ dc->full_heap = 0;
+
+ if (RTEST(opts)) {
output = rb_hash_aref(opts, sym_output);
+ if (Qtrue == rb_hash_lookup2(opts, sym_full, Qfalse))
+ dc->full_heap = 1;
+ }
+
if (output == sym_stdout) {
dc->stream = stdout;
dc->string = Qnil;
@@ -463,6 +498,7 @@ objspace_dump_all(int argc, VALUE *argv, VALUE os)
void
Init_objspace_dump(VALUE rb_mObjSpace)
{
+#undef rb_intern
#if 0
rb_mObjSpace = rb_define_module("ObjectSpace"); /* let rdoc know */
#endif
@@ -474,6 +510,7 @@ Init_objspace_dump(VALUE rb_mObjSpace)
sym_stdout = ID2SYM(rb_intern("stdout"));
sym_string = ID2SYM(rb_intern("string"));
sym_file = ID2SYM(rb_intern("file"));
+ sym_full = ID2SYM(rb_intern("full"));
/* force create static IDs */
rb_obj_gc_flags(rb_mObjSpace, 0, 0);
diff --git a/ext/openssl/History.md b/ext/openssl/History.md
new file mode 100644
index 0000000000..db5050014e
--- /dev/null
+++ b/ext/openssl/History.md
@@ -0,0 +1,339 @@
+Version 2.1.2
+=============
+
+Merged changes in 2.0.9.
+
+
+Version 2.1.1
+=============
+
+Merged changes in 2.0.8.
+
+
+Version 2.1.0
+=============
+
+Notable changes
+---------------
+
+* Support for OpenSSL versions before 1.0.1 and LibreSSL versions before 2.5
+ is removed.
+ [[GitHub #86]](https://github.com/ruby/openssl/pull/86)
+* OpenSSL::BN#negative?, #+@, and #-@ are added.
+* OpenSSL::SSL::SSLSocket#connect raises a more informative exception when
+ certificate verification fails.
+ [[GitHub #99]](https://github.com/ruby/openssl/pull/99)
+* OpenSSL::KDF module is newly added. In addition to PBKDF2-HMAC that has moved
+ from OpenSSL::PKCS5, scrypt and HKDF are supported.
+ [[GitHub #109]](https://github.com/ruby/openssl/pull/109)
+ [[GitHub #173]](https://github.com/ruby/openssl/pull/173)
+* OpenSSL.fips_mode is added. We had the setter, but not the getter.
+ [[GitHub #125]](https://github.com/ruby/openssl/pull/125)
+* OpenSSL::OCSP::Request#signed? is added.
+* OpenSSL::ASN1 handles the indefinite length form better. OpenSSL::ASN1.decode
+ no longer wrongly treats the end-of-contents octets as part of the content.
+ OpenSSL::ASN1::ASN1Data#infinite_length is renamed to #indefinite_length.
+ [[GitHub #98]](https://github.com/ruby/openssl/pull/98)
+* OpenSSL::X509::Name#add_entry now accepts two additional keyword arguments
+ 'loc' and 'set'.
+ [[GitHub #94]](https://github.com/ruby/openssl/issues/94)
+* OpenSSL::SSL::SSLContext#min_version= and #max_version= are added to replace
+ #ssl_version= that was built on top of the deprecated OpenSSL C API. Use of
+ that method and the constant OpenSSL::SSL::SSLContext::METHODS is now
+ deprecated.
+ [[GitHub #142]](https://github.com/ruby/openssl/pull/142)
+* OpenSSL::X509::Name#to_utf8 is added.
+ [[GitHub #26]](https://github.com/ruby/openssl/issues/26)
+ [[GitHub #143]](https://github.com/ruby/openssl/pull/143)
+* OpenSSL::X509::{Extension,Attribute,Certificate,CRL,Revoked,Request} can be
+ compared with == operator.
+ [[GitHub #161]](https://github.com/ruby/openssl/pull/161)
+* TLS Fallback Signaling Cipher Suite Value (SCSV) support is added.
+ [[GitHub #165]](https://github.com/ruby/openssl/pull/165)
+* Build failure with OpenSSL 1.1 built with no-deprecated is fixed.
+ [[GitHub #160]](https://github.com/ruby/openssl/pull/160)
+* OpenSSL::Buffering#write accepts an arbitrary number of arguments.
+ [[Feature #9323]](https://bugs.ruby-lang.org/issues/9323)
+ [[GitHub #162]](https://github.com/ruby/openssl/pull/162)
+* OpenSSL::PKey::RSA#sign_pss and #verify_pss are added. They perform RSA-PSS
+ signature and verification.
+ [[GitHub #75]](https://github.com/ruby/openssl/issues/75)
+ [[GitHub #76]](https://github.com/ruby/openssl/pull/76)
+ [[GitHub #169]](https://github.com/ruby/openssl/pull/169)
+* OpenSSL::SSL::SSLContext#add_certificate is added.
+ [[GitHub #167]](https://github.com/ruby/openssl/pull/167)
+* OpenSSL::PKey::EC::Point#to_octet_string is added.
+ OpenSSL::PKey::EC::Point.new can now take String as the second argument.
+ [[GitHub #177]](https://github.com/ruby/openssl/pull/177)
+
+
+Version 2.0.9
+=============
+
+Security fixes
+--------------
+
+* OpenSSL::X509::Name#<=> could incorrectly return 0 (= equal) for non-equal
+ objects. CVE-2018-16395 is assigned for this issue.
+ https://hackerone.com/reports/387250
+
+Bug fixes
+---------
+
+* Fixed OpenSSL::PKey::*.{new,generate} immediately aborting if the thread is
+ interrupted.
+ [[Bug #14882]](https://bugs.ruby-lang.org/issues/14882)
+ [[GitHub #205]](https://github.com/ruby/openssl/pull/205)
+* Fixed OpenSSL::X509::Name#to_s failing with OpenSSL::X509::NameError if
+ called against an empty instance.
+ [[GitHub #200]](https://github.com/ruby/openssl/issues/200)
+ [[GitHub #211]](https://github.com/ruby/openssl/pull/211)
+
+
+Version 2.0.8
+=============
+
+Bug fixes
+---------
+
+* OpenSSL::Cipher#pkcs5_keyivgen raises an error when a negative iteration
+ count is given.
+ [[GitHub #184]](https://github.com/ruby/openssl/pull/184)
+* Fixed build with LibreSSL 2.7.
+ [[GitHub #192]](https://github.com/ruby/openssl/issues/192)
+ [[GitHub #193]](https://github.com/ruby/openssl/pull/193)
+
+
+Version 2.0.7
+=============
+
+Bug fixes
+---------
+
+* OpenSSL::Cipher#auth_data= could segfault if called against a non-AEAD cipher.
+ [[Bug #14024]](https://bugs.ruby-lang.org/issues/14024)
+* OpenSSL::X509::Certificate#public_key= (and similar methods) could segfault
+ when an instance of OpenSSL::PKey::PKey with no public key components is
+ passed.
+ [[Bug #14087]](https://bugs.ruby-lang.org/issues/14087)
+ [[GitHub #168]](https://github.com/ruby/openssl/pull/168)
+
+
+Version 2.0.6
+=============
+
+Bug fixes
+---------
+
+* The session_remove_cb set to an OpenSSL::SSL::SSLContext is no longer called
+ during GC.
+* A possible deadlock in OpenSSL::SSL::SSLSocket#sysread is fixed.
+ [[GitHub #139]](https://github.com/ruby/openssl/pull/139)
+* OpenSSL::BN#hash could return an unnormalized fixnum value on Windows.
+ [[Bug #13877]](https://bugs.ruby-lang.org/issues/13877)
+* OpenSSL::SSL::SSLSocket#sysread and #sysread_nonblock set the length of the
+ destination buffer String to 0 on error.
+ [[GitHub #153]](https://github.com/ruby/openssl/pull/153)
+* Possible deadlock is fixed. This happened only when built with older versions
+ of OpenSSL (before 1.1.0) or LibreSSL.
+ [[GitHub #155]](https://github.com/ruby/openssl/pull/155)
+
+
+Version 2.0.5
+=============
+
+Bug fixes
+---------
+
+* Reading a PEM/DER-encoded private key or certificate from an IO object did
+ not work properly on mswin platforms.
+ [[ruby/openssl#128]](https://github.com/ruby/openssl/issues/128)
+* Broken length check in the PEM passphrase callback is fixed.
+* It failed to compile when OpenSSL is configured without TLS 1.0 support.
+
+
+Version 2.0.4
+=============
+
+Bug fixes
+---------
+
+* It now compiles with LibreSSL without renaming on Windows (mswin).
+* A workaround for the error queue leak of X509_load_cert_crl_file() that
+ causes random errors is added.
+ [[Bug #11033]](https://bugs.ruby-lang.org/issues/11033)
+
+
+Version 2.0.3
+=============
+
+Bug fixes
+---------
+
+* OpenSSL::ASN1::Constructive#each which was broken by 2.0.0 is fixed.
+ [[ruby/openssl#96]](https://github.com/ruby/openssl/pull/96)
+* Fixed build with static OpenSSL libraries on Windows.
+ [[Bug #13080]](https://bugs.ruby-lang.org/issues/13080)
+* OpenSSL::X509::Name#eql? which was broken by 2.0.0 is fixed.
+
+
+Version 2.0.2
+=============
+
+Bug fixes
+---------
+
+* Fix build with early 0.9.8 series which did not have SSL_CTX_clear_options().
+ [ruby-core:78693]
+
+
+Version 2.0.1
+=============
+
+Bug fixes
+---------
+
+* A GC issue around OpenSSL::BN is fixed.
+ [[ruby/openssl#87]](https://github.com/ruby/openssl/issues/87)
+* OpenSSL::ASN1 now parses BER encoding of GeneralizedTime without seconds.
+ [[ruby/openssl#88]](https://github.com/ruby/openssl/pull/88)
+
+
+Version 2.0.0
+=============
+
+This is the first release of openssl gem, formerly a standard library of Ruby,
+ext/openssl. This is the successor of the version included in Ruby 2.3.
+
+Compatibility notes
+-------------------
+
+* Support for OpenSSL version 0.9.6 and 0.9.7 is completely removed. openssl gem
+ still works with OpenSSL 0.9.8, but users are strongly encouraged to upgrade
+ to at least 1.0.1, as OpenSSL < 1.0.1 will not receive any security fixes from
+ the OpenSSL development team.
+
+Supported platforms
+-------------------
+
+* OpenSSL 1.0.0, 1.0.1, 1.0.2, 1.1.0
+* OpenSSL < 0.9.8 is no longer supported.
+* LibreSSL 2.3, 2.4, 2.5
+* Ruby 2.3, 2.4
+
+Notable changes
+---------------
+
+* Add support for OpenSSL 1.1.0.
+ [[Feature #12324]](https://bugs.ruby-lang.org/issues/12324)
+* Add support for LibreSSL
+
+* OpenSSL::Cipher
+
+ - OpenSSL::Cipher#key= and #iv= reject too long inputs. They used to truncate
+ silently. [[Bug #12561]](https://bugs.ruby-lang.org/issues/12561)
+
+ - OpenSSL::Cipher#iv_len= is added. It allows changing IV (nonce) length if
+ using AEAD ciphers.
+ [[Bug #8667]](https://bugs.ruby-lang.org/issues/8667),
+ [[Bug #10420]](https://bugs.ruby-lang.org/issues/10420),
+ [[GH ruby/ruby#569]](https://github.com/ruby/ruby/pull/569),
+ [[GH ruby/openssl#58]](https://github.com/ruby/openssl/pull/58)
+
+ - OpenSSL::Cipher#auth_tag_len= is added. This sets the authentication tag
+ length to be generated by an AEAD cipher.
+
+* OpenSSL::OCSP
+
+ - Accessor methods are added to OpenSSL::OCSP::CertificateId.
+ [[Feature #7181]](https://bugs.ruby-lang.org/issues/7181)
+
+ - OpenSSL::OCSP::Request and BasicResponse can be signed with non-SHA-1 hash
+ algorithm. [[Feature #11552]](https://bugs.ruby-lang.org/issues/11552)
+
+ - OpenSSL::OCSP::CertificateId and BasicResponse can be encoded into DER.
+
+ - A new class OpenSSL::OCSP::SingleResponse is added for convenience.
+
+ - OpenSSL::OCSP::BasicResponse#add_status accepts absolute times. They used to
+ accept only relative seconds from the current time.
+
+* OpenSSL::PKey
+
+ - OpenSSL::PKey::EC follows the general PKey interface.
+ [[Bug #6567]](https://bugs.ruby-lang.org/issues/6567)
+
+ - OpenSSL::PKey.read raises OpenSSL::PKey::PKeyError instead of ArgumentError
+ for consistency with OpenSSL::PKey::{DH,DSA,RSA,EC}#new.
+ [[Bug #11774]](https://bugs.ruby-lang.org/issues/11774),
+ [[GH ruby/openssl#55]](https://github.com/ruby/openssl/pull/55)
+
+ - OpenSSL::PKey::EC::Group retrieved by OpenSSL::PKey::EC#group is no longer
+ linked with the EC key. Modifications to the EC::Group have no effect on the
+ key. [[GH ruby/openssl#71]](https://github.com/ruby/openssl/pull/71)
+
+ - OpenSSL::PKey::EC::Point#to_bn allows specifying the point conversion form
+ by the optional argument.
+
+* OpenSSL::SSL
+
+ - OpenSSL::SSL::SSLSocket#tmp_key is added. A client can call it after the
+ connection is established to retrieve the ephemeral key.
+ [[GH ruby/ruby#1318]](https://github.com/ruby/ruby/pull/1318)
+
+ - The automatic ephemeral ECDH curve selection is enabled by default when
+ built with OpenSSL >= 1.0.2 or LibreSSL.
+
+ - OpenSSL::SSL::SSLContext#security_level= is added. You can set the "security
+ level" of the SSL context. This is effective only when built with OpenSSL
+ 1.1.0.
+
+ - A new option 'verify_hostname' is added to OpenSSL::SSL::SSLContext. When it
+ is enabled, and the SNI hostname is also set, the hostname verification on
+ the server certificate is automatically performed. It is now enabled by
+ OpenSSL::SSL::SSLContext#set_params.
+ [[GH ruby/openssl#60]](https://github.com/ruby/openssl/pull/60)
+
+Removals
+--------
+
+* OpenSSL::Engine
+
+ - OpenSSL::Engine.cleanup does nothing when built with OpenSSL 1.1.0.
+
+* OpenSSL::SSL
+
+ - OpenSSL::PKey::DH::DEFAULT_512 is removed. Hence servers no longer use
+ 512-bit DH group by default. It is considered too weak nowadays.
+ [[Bug #11968]](https://bugs.ruby-lang.org/issues/11968),
+ [[GH ruby/ruby#1196]](https://github.com/ruby/ruby/pull/1196)
+
+ - RC4 cipher suites are removed from OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.
+ RC4 is now considered to be weak.
+ [[GH ruby/openssl#50]](https://github.com/ruby/openssl/pull/50)
+
+Deprecations
+------------
+
+* OpenSSL::PKey
+
+ - OpenSSL::PKey::RSA#n=, #e=, #d=, #p=, #q=, #dmp1=, #dmq1=, #iqmp=,
+ OpenSSL::PKey::DSA#p=, #q=, #g=, #priv_key=, #pub_key=,
+ OpenSSL::PKey::DH#p=, #g=, #priv_key= and #pub_key= are deprecated. They are
+ disabled when built with OpenSSL 1.1.0, due to its API change. Instead,
+ OpenSSL::PKey::RSA#set_key, #set_factors, #set_crt_params,
+ OpenSSL::PKey::DSA#set_pqg, #set_key, OpenSSL::PKey::DH#set_pqg and #set_key
+ are added.
+
+* OpenSSL::Random
+
+ - OpenSSL::Random.pseudo_bytes is deprecated, and not defined when built with
+ OpenSSL 1.1.0. Use OpenSSL::Random.random_bytes instead.
+
+* OpenSSL::SSL
+
+ - OpenSSL::SSL::SSLContext#tmp_ecdh_callback is deprecated, as the underlying
+ API SSL_CTX_set_tmp_ecdh_callback() is removed in OpenSSL 1.1.0. It was
+ first added in Ruby 2.3.0. To specify the curve to be used in ephemeral
+ ECDH, use OpenSSL::SSL::SSLContext#ecdh_curves=. The automatic curve
+ selection is also now enabled by default when built with a capable OpenSSL.
diff --git a/ext/openssl/depend b/ext/openssl/depend
index 8426765281..021c6d99a8 100644
--- a/ext/openssl/depend
+++ b/ext/openssl/depend
@@ -11,6 +11,7 @@ ossl.o: $(hdrdir)/ruby/encoding.h
ossl.o: $(hdrdir)/ruby/intern.h
ossl.o: $(hdrdir)/ruby/io.h
ossl.o: $(hdrdir)/ruby/missing.h
+ossl.o: $(hdrdir)/ruby/onigmo.h
ossl.o: $(hdrdir)/ruby/oniguruma.h
ossl.o: $(hdrdir)/ruby/ruby.h
ossl.o: $(hdrdir)/ruby/st.h
@@ -29,10 +30,10 @@ ossl.o: ossl_config.h
ossl.o: ossl_digest.h
ossl.o: ossl_engine.h
ossl.o: ossl_hmac.h
+ossl.o: ossl_kdf.h
ossl.o: ossl_ns_spki.h
ossl.o: ossl_ocsp.h
ossl.o: ossl_pkcs12.h
-ossl.o: ossl_pkcs5.h
ossl.o: ossl_pkcs7.h
ossl.o: ossl_pkey.h
ossl.o: ossl_rand.h
@@ -48,6 +49,7 @@ ossl_asn1.o: $(hdrdir)/ruby/encoding.h
ossl_asn1.o: $(hdrdir)/ruby/intern.h
ossl_asn1.o: $(hdrdir)/ruby/io.h
ossl_asn1.o: $(hdrdir)/ruby/missing.h
+ossl_asn1.o: $(hdrdir)/ruby/onigmo.h
ossl_asn1.o: $(hdrdir)/ruby/oniguruma.h
ossl_asn1.o: $(hdrdir)/ruby/ruby.h
ossl_asn1.o: $(hdrdir)/ruby/st.h
@@ -65,10 +67,10 @@ ossl_asn1.o: ossl_config.h
ossl_asn1.o: ossl_digest.h
ossl_asn1.o: ossl_engine.h
ossl_asn1.o: ossl_hmac.h
+ossl_asn1.o: ossl_kdf.h
ossl_asn1.o: ossl_ns_spki.h
ossl_asn1.o: ossl_ocsp.h
ossl_asn1.o: ossl_pkcs12.h
-ossl_asn1.o: ossl_pkcs5.h
ossl_asn1.o: ossl_pkcs7.h
ossl_asn1.o: ossl_pkey.h
ossl_asn1.o: ossl_rand.h
@@ -84,6 +86,7 @@ ossl_bio.o: $(hdrdir)/ruby/encoding.h
ossl_bio.o: $(hdrdir)/ruby/intern.h
ossl_bio.o: $(hdrdir)/ruby/io.h
ossl_bio.o: $(hdrdir)/ruby/missing.h
+ossl_bio.o: $(hdrdir)/ruby/onigmo.h
ossl_bio.o: $(hdrdir)/ruby/oniguruma.h
ossl_bio.o: $(hdrdir)/ruby/ruby.h
ossl_bio.o: $(hdrdir)/ruby/st.h
@@ -101,10 +104,10 @@ ossl_bio.o: ossl_config.h
ossl_bio.o: ossl_digest.h
ossl_bio.o: ossl_engine.h
ossl_bio.o: ossl_hmac.h
+ossl_bio.o: ossl_kdf.h
ossl_bio.o: ossl_ns_spki.h
ossl_bio.o: ossl_ocsp.h
ossl_bio.o: ossl_pkcs12.h
-ossl_bio.o: ossl_pkcs5.h
ossl_bio.o: ossl_pkcs7.h
ossl_bio.o: ossl_pkey.h
ossl_bio.o: ossl_rand.h
@@ -120,6 +123,7 @@ ossl_bn.o: $(hdrdir)/ruby/encoding.h
ossl_bn.o: $(hdrdir)/ruby/intern.h
ossl_bn.o: $(hdrdir)/ruby/io.h
ossl_bn.o: $(hdrdir)/ruby/missing.h
+ossl_bn.o: $(hdrdir)/ruby/onigmo.h
ossl_bn.o: $(hdrdir)/ruby/oniguruma.h
ossl_bn.o: $(hdrdir)/ruby/ruby.h
ossl_bn.o: $(hdrdir)/ruby/st.h
@@ -137,10 +141,10 @@ ossl_bn.o: ossl_config.h
ossl_bn.o: ossl_digest.h
ossl_bn.o: ossl_engine.h
ossl_bn.o: ossl_hmac.h
+ossl_bn.o: ossl_kdf.h
ossl_bn.o: ossl_ns_spki.h
ossl_bn.o: ossl_ocsp.h
ossl_bn.o: ossl_pkcs12.h
-ossl_bn.o: ossl_pkcs5.h
ossl_bn.o: ossl_pkcs7.h
ossl_bn.o: ossl_pkey.h
ossl_bn.o: ossl_rand.h
@@ -156,6 +160,7 @@ ossl_cipher.o: $(hdrdir)/ruby/encoding.h
ossl_cipher.o: $(hdrdir)/ruby/intern.h
ossl_cipher.o: $(hdrdir)/ruby/io.h
ossl_cipher.o: $(hdrdir)/ruby/missing.h
+ossl_cipher.o: $(hdrdir)/ruby/onigmo.h
ossl_cipher.o: $(hdrdir)/ruby/oniguruma.h
ossl_cipher.o: $(hdrdir)/ruby/ruby.h
ossl_cipher.o: $(hdrdir)/ruby/st.h
@@ -173,10 +178,10 @@ ossl_cipher.o: ossl_config.h
ossl_cipher.o: ossl_digest.h
ossl_cipher.o: ossl_engine.h
ossl_cipher.o: ossl_hmac.h
+ossl_cipher.o: ossl_kdf.h
ossl_cipher.o: ossl_ns_spki.h
ossl_cipher.o: ossl_ocsp.h
ossl_cipher.o: ossl_pkcs12.h
-ossl_cipher.o: ossl_pkcs5.h
ossl_cipher.o: ossl_pkcs7.h
ossl_cipher.o: ossl_pkey.h
ossl_cipher.o: ossl_rand.h
@@ -192,6 +197,7 @@ ossl_config.o: $(hdrdir)/ruby/encoding.h
ossl_config.o: $(hdrdir)/ruby/intern.h
ossl_config.o: $(hdrdir)/ruby/io.h
ossl_config.o: $(hdrdir)/ruby/missing.h
+ossl_config.o: $(hdrdir)/ruby/onigmo.h
ossl_config.o: $(hdrdir)/ruby/oniguruma.h
ossl_config.o: $(hdrdir)/ruby/ruby.h
ossl_config.o: $(hdrdir)/ruby/st.h
@@ -209,10 +215,10 @@ ossl_config.o: ossl_config.h
ossl_config.o: ossl_digest.h
ossl_config.o: ossl_engine.h
ossl_config.o: ossl_hmac.h
+ossl_config.o: ossl_kdf.h
ossl_config.o: ossl_ns_spki.h
ossl_config.o: ossl_ocsp.h
ossl_config.o: ossl_pkcs12.h
-ossl_config.o: ossl_pkcs5.h
ossl_config.o: ossl_pkcs7.h
ossl_config.o: ossl_pkey.h
ossl_config.o: ossl_rand.h
@@ -228,6 +234,7 @@ ossl_digest.o: $(hdrdir)/ruby/encoding.h
ossl_digest.o: $(hdrdir)/ruby/intern.h
ossl_digest.o: $(hdrdir)/ruby/io.h
ossl_digest.o: $(hdrdir)/ruby/missing.h
+ossl_digest.o: $(hdrdir)/ruby/onigmo.h
ossl_digest.o: $(hdrdir)/ruby/oniguruma.h
ossl_digest.o: $(hdrdir)/ruby/ruby.h
ossl_digest.o: $(hdrdir)/ruby/st.h
@@ -245,10 +252,10 @@ ossl_digest.o: ossl_digest.c
ossl_digest.o: ossl_digest.h
ossl_digest.o: ossl_engine.h
ossl_digest.o: ossl_hmac.h
+ossl_digest.o: ossl_kdf.h
ossl_digest.o: ossl_ns_spki.h
ossl_digest.o: ossl_ocsp.h
ossl_digest.o: ossl_pkcs12.h
-ossl_digest.o: ossl_pkcs5.h
ossl_digest.o: ossl_pkcs7.h
ossl_digest.o: ossl_pkey.h
ossl_digest.o: ossl_rand.h
@@ -264,6 +271,7 @@ ossl_engine.o: $(hdrdir)/ruby/encoding.h
ossl_engine.o: $(hdrdir)/ruby/intern.h
ossl_engine.o: $(hdrdir)/ruby/io.h
ossl_engine.o: $(hdrdir)/ruby/missing.h
+ossl_engine.o: $(hdrdir)/ruby/onigmo.h
ossl_engine.o: $(hdrdir)/ruby/oniguruma.h
ossl_engine.o: $(hdrdir)/ruby/ruby.h
ossl_engine.o: $(hdrdir)/ruby/st.h
@@ -281,10 +289,10 @@ ossl_engine.o: ossl_digest.h
ossl_engine.o: ossl_engine.c
ossl_engine.o: ossl_engine.h
ossl_engine.o: ossl_hmac.h
+ossl_engine.o: ossl_kdf.h
ossl_engine.o: ossl_ns_spki.h
ossl_engine.o: ossl_ocsp.h
ossl_engine.o: ossl_pkcs12.h
-ossl_engine.o: ossl_pkcs5.h
ossl_engine.o: ossl_pkcs7.h
ossl_engine.o: ossl_pkey.h
ossl_engine.o: ossl_rand.h
@@ -300,6 +308,7 @@ ossl_hmac.o: $(hdrdir)/ruby/encoding.h
ossl_hmac.o: $(hdrdir)/ruby/intern.h
ossl_hmac.o: $(hdrdir)/ruby/io.h
ossl_hmac.o: $(hdrdir)/ruby/missing.h
+ossl_hmac.o: $(hdrdir)/ruby/onigmo.h
ossl_hmac.o: $(hdrdir)/ruby/oniguruma.h
ossl_hmac.o: $(hdrdir)/ruby/ruby.h
ossl_hmac.o: $(hdrdir)/ruby/st.h
@@ -317,10 +326,10 @@ ossl_hmac.o: ossl_digest.h
ossl_hmac.o: ossl_engine.h
ossl_hmac.o: ossl_hmac.c
ossl_hmac.o: ossl_hmac.h
+ossl_hmac.o: ossl_kdf.h
ossl_hmac.o: ossl_ns_spki.h
ossl_hmac.o: ossl_ocsp.h
ossl_hmac.o: ossl_pkcs12.h
-ossl_hmac.o: ossl_pkcs5.h
ossl_hmac.o: ossl_pkcs7.h
ossl_hmac.o: ossl_pkey.h
ossl_hmac.o: ossl_rand.h
@@ -328,6 +337,43 @@ ossl_hmac.o: ossl_ssl.h
ossl_hmac.o: ossl_version.h
ossl_hmac.o: ossl_x509.h
ossl_hmac.o: ruby_missing.h
+ossl_kdf.o: $(RUBY_EXTCONF_H)
+ossl_kdf.o: $(arch_hdrdir)/ruby/config.h
+ossl_kdf.o: $(hdrdir)/ruby/backward.h
+ossl_kdf.o: $(hdrdir)/ruby/defines.h
+ossl_kdf.o: $(hdrdir)/ruby/encoding.h
+ossl_kdf.o: $(hdrdir)/ruby/intern.h
+ossl_kdf.o: $(hdrdir)/ruby/io.h
+ossl_kdf.o: $(hdrdir)/ruby/missing.h
+ossl_kdf.o: $(hdrdir)/ruby/onigmo.h
+ossl_kdf.o: $(hdrdir)/ruby/oniguruma.h
+ossl_kdf.o: $(hdrdir)/ruby/ruby.h
+ossl_kdf.o: $(hdrdir)/ruby/st.h
+ossl_kdf.o: $(hdrdir)/ruby/subst.h
+ossl_kdf.o: $(hdrdir)/ruby/thread.h
+ossl_kdf.o: $(top_srcdir)/include/ruby.h
+ossl_kdf.o: openssl_missing.h
+ossl_kdf.o: ossl.h
+ossl_kdf.o: ossl_asn1.h
+ossl_kdf.o: ossl_bio.h
+ossl_kdf.o: ossl_bn.h
+ossl_kdf.o: ossl_cipher.h
+ossl_kdf.o: ossl_config.h
+ossl_kdf.o: ossl_digest.h
+ossl_kdf.o: ossl_engine.h
+ossl_kdf.o: ossl_hmac.h
+ossl_kdf.o: ossl_kdf.c
+ossl_kdf.o: ossl_kdf.h
+ossl_kdf.o: ossl_ns_spki.h
+ossl_kdf.o: ossl_ocsp.h
+ossl_kdf.o: ossl_pkcs12.h
+ossl_kdf.o: ossl_pkcs7.h
+ossl_kdf.o: ossl_pkey.h
+ossl_kdf.o: ossl_rand.h
+ossl_kdf.o: ossl_ssl.h
+ossl_kdf.o: ossl_version.h
+ossl_kdf.o: ossl_x509.h
+ossl_kdf.o: ruby_missing.h
ossl_ns_spki.o: $(RUBY_EXTCONF_H)
ossl_ns_spki.o: $(arch_hdrdir)/ruby/config.h
ossl_ns_spki.o: $(hdrdir)/ruby/backward.h
@@ -336,6 +382,7 @@ ossl_ns_spki.o: $(hdrdir)/ruby/encoding.h
ossl_ns_spki.o: $(hdrdir)/ruby/intern.h
ossl_ns_spki.o: $(hdrdir)/ruby/io.h
ossl_ns_spki.o: $(hdrdir)/ruby/missing.h
+ossl_ns_spki.o: $(hdrdir)/ruby/onigmo.h
ossl_ns_spki.o: $(hdrdir)/ruby/oniguruma.h
ossl_ns_spki.o: $(hdrdir)/ruby/ruby.h
ossl_ns_spki.o: $(hdrdir)/ruby/st.h
@@ -352,11 +399,11 @@ ossl_ns_spki.o: ossl_config.h
ossl_ns_spki.o: ossl_digest.h
ossl_ns_spki.o: ossl_engine.h
ossl_ns_spki.o: ossl_hmac.h
+ossl_ns_spki.o: ossl_kdf.h
ossl_ns_spki.o: ossl_ns_spki.c
ossl_ns_spki.o: ossl_ns_spki.h
ossl_ns_spki.o: ossl_ocsp.h
ossl_ns_spki.o: ossl_pkcs12.h
-ossl_ns_spki.o: ossl_pkcs5.h
ossl_ns_spki.o: ossl_pkcs7.h
ossl_ns_spki.o: ossl_pkey.h
ossl_ns_spki.o: ossl_rand.h
@@ -372,6 +419,7 @@ ossl_ocsp.o: $(hdrdir)/ruby/encoding.h
ossl_ocsp.o: $(hdrdir)/ruby/intern.h
ossl_ocsp.o: $(hdrdir)/ruby/io.h
ossl_ocsp.o: $(hdrdir)/ruby/missing.h
+ossl_ocsp.o: $(hdrdir)/ruby/onigmo.h
ossl_ocsp.o: $(hdrdir)/ruby/oniguruma.h
ossl_ocsp.o: $(hdrdir)/ruby/ruby.h
ossl_ocsp.o: $(hdrdir)/ruby/st.h
@@ -388,11 +436,11 @@ ossl_ocsp.o: ossl_config.h
ossl_ocsp.o: ossl_digest.h
ossl_ocsp.o: ossl_engine.h
ossl_ocsp.o: ossl_hmac.h
+ossl_ocsp.o: ossl_kdf.h
ossl_ocsp.o: ossl_ns_spki.h
ossl_ocsp.o: ossl_ocsp.c
ossl_ocsp.o: ossl_ocsp.h
ossl_ocsp.o: ossl_pkcs12.h
-ossl_ocsp.o: ossl_pkcs5.h
ossl_ocsp.o: ossl_pkcs7.h
ossl_ocsp.o: ossl_pkey.h
ossl_ocsp.o: ossl_rand.h
@@ -408,6 +456,7 @@ ossl_pkcs12.o: $(hdrdir)/ruby/encoding.h
ossl_pkcs12.o: $(hdrdir)/ruby/intern.h
ossl_pkcs12.o: $(hdrdir)/ruby/io.h
ossl_pkcs12.o: $(hdrdir)/ruby/missing.h
+ossl_pkcs12.o: $(hdrdir)/ruby/onigmo.h
ossl_pkcs12.o: $(hdrdir)/ruby/oniguruma.h
ossl_pkcs12.o: $(hdrdir)/ruby/ruby.h
ossl_pkcs12.o: $(hdrdir)/ruby/st.h
@@ -424,11 +473,11 @@ ossl_pkcs12.o: ossl_config.h
ossl_pkcs12.o: ossl_digest.h
ossl_pkcs12.o: ossl_engine.h
ossl_pkcs12.o: ossl_hmac.h
+ossl_pkcs12.o: ossl_kdf.h
ossl_pkcs12.o: ossl_ns_spki.h
ossl_pkcs12.o: ossl_ocsp.h
ossl_pkcs12.o: ossl_pkcs12.c
ossl_pkcs12.o: ossl_pkcs12.h
-ossl_pkcs12.o: ossl_pkcs5.h
ossl_pkcs12.o: ossl_pkcs7.h
ossl_pkcs12.o: ossl_pkey.h
ossl_pkcs12.o: ossl_rand.h
@@ -436,42 +485,6 @@ ossl_pkcs12.o: ossl_ssl.h
ossl_pkcs12.o: ossl_version.h
ossl_pkcs12.o: ossl_x509.h
ossl_pkcs12.o: ruby_missing.h
-ossl_pkcs5.o: $(RUBY_EXTCONF_H)
-ossl_pkcs5.o: $(arch_hdrdir)/ruby/config.h
-ossl_pkcs5.o: $(hdrdir)/ruby/backward.h
-ossl_pkcs5.o: $(hdrdir)/ruby/defines.h
-ossl_pkcs5.o: $(hdrdir)/ruby/encoding.h
-ossl_pkcs5.o: $(hdrdir)/ruby/intern.h
-ossl_pkcs5.o: $(hdrdir)/ruby/io.h
-ossl_pkcs5.o: $(hdrdir)/ruby/missing.h
-ossl_pkcs5.o: $(hdrdir)/ruby/oniguruma.h
-ossl_pkcs5.o: $(hdrdir)/ruby/ruby.h
-ossl_pkcs5.o: $(hdrdir)/ruby/st.h
-ossl_pkcs5.o: $(hdrdir)/ruby/subst.h
-ossl_pkcs5.o: $(hdrdir)/ruby/thread.h
-ossl_pkcs5.o: $(top_srcdir)/include/ruby.h
-ossl_pkcs5.o: openssl_missing.h
-ossl_pkcs5.o: ossl.h
-ossl_pkcs5.o: ossl_asn1.h
-ossl_pkcs5.o: ossl_bio.h
-ossl_pkcs5.o: ossl_bn.h
-ossl_pkcs5.o: ossl_cipher.h
-ossl_pkcs5.o: ossl_config.h
-ossl_pkcs5.o: ossl_digest.h
-ossl_pkcs5.o: ossl_engine.h
-ossl_pkcs5.o: ossl_hmac.h
-ossl_pkcs5.o: ossl_ns_spki.h
-ossl_pkcs5.o: ossl_ocsp.h
-ossl_pkcs5.o: ossl_pkcs12.h
-ossl_pkcs5.o: ossl_pkcs5.c
-ossl_pkcs5.o: ossl_pkcs5.h
-ossl_pkcs5.o: ossl_pkcs7.h
-ossl_pkcs5.o: ossl_pkey.h
-ossl_pkcs5.o: ossl_rand.h
-ossl_pkcs5.o: ossl_ssl.h
-ossl_pkcs5.o: ossl_version.h
-ossl_pkcs5.o: ossl_x509.h
-ossl_pkcs5.o: ruby_missing.h
ossl_pkcs7.o: $(RUBY_EXTCONF_H)
ossl_pkcs7.o: $(arch_hdrdir)/ruby/config.h
ossl_pkcs7.o: $(hdrdir)/ruby/backward.h
@@ -480,6 +493,7 @@ ossl_pkcs7.o: $(hdrdir)/ruby/encoding.h
ossl_pkcs7.o: $(hdrdir)/ruby/intern.h
ossl_pkcs7.o: $(hdrdir)/ruby/io.h
ossl_pkcs7.o: $(hdrdir)/ruby/missing.h
+ossl_pkcs7.o: $(hdrdir)/ruby/onigmo.h
ossl_pkcs7.o: $(hdrdir)/ruby/oniguruma.h
ossl_pkcs7.o: $(hdrdir)/ruby/ruby.h
ossl_pkcs7.o: $(hdrdir)/ruby/st.h
@@ -496,10 +510,10 @@ ossl_pkcs7.o: ossl_config.h
ossl_pkcs7.o: ossl_digest.h
ossl_pkcs7.o: ossl_engine.h
ossl_pkcs7.o: ossl_hmac.h
+ossl_pkcs7.o: ossl_kdf.h
ossl_pkcs7.o: ossl_ns_spki.h
ossl_pkcs7.o: ossl_ocsp.h
ossl_pkcs7.o: ossl_pkcs12.h
-ossl_pkcs7.o: ossl_pkcs5.h
ossl_pkcs7.o: ossl_pkcs7.c
ossl_pkcs7.o: ossl_pkcs7.h
ossl_pkcs7.o: ossl_pkey.h
@@ -516,6 +530,7 @@ ossl_pkey.o: $(hdrdir)/ruby/encoding.h
ossl_pkey.o: $(hdrdir)/ruby/intern.h
ossl_pkey.o: $(hdrdir)/ruby/io.h
ossl_pkey.o: $(hdrdir)/ruby/missing.h
+ossl_pkey.o: $(hdrdir)/ruby/onigmo.h
ossl_pkey.o: $(hdrdir)/ruby/oniguruma.h
ossl_pkey.o: $(hdrdir)/ruby/ruby.h
ossl_pkey.o: $(hdrdir)/ruby/st.h
@@ -532,10 +547,10 @@ ossl_pkey.o: ossl_config.h
ossl_pkey.o: ossl_digest.h
ossl_pkey.o: ossl_engine.h
ossl_pkey.o: ossl_hmac.h
+ossl_pkey.o: ossl_kdf.h
ossl_pkey.o: ossl_ns_spki.h
ossl_pkey.o: ossl_ocsp.h
ossl_pkey.o: ossl_pkcs12.h
-ossl_pkey.o: ossl_pkcs5.h
ossl_pkey.o: ossl_pkcs7.h
ossl_pkey.o: ossl_pkey.c
ossl_pkey.o: ossl_pkey.h
@@ -552,6 +567,7 @@ ossl_pkey_dh.o: $(hdrdir)/ruby/encoding.h
ossl_pkey_dh.o: $(hdrdir)/ruby/intern.h
ossl_pkey_dh.o: $(hdrdir)/ruby/io.h
ossl_pkey_dh.o: $(hdrdir)/ruby/missing.h
+ossl_pkey_dh.o: $(hdrdir)/ruby/onigmo.h
ossl_pkey_dh.o: $(hdrdir)/ruby/oniguruma.h
ossl_pkey_dh.o: $(hdrdir)/ruby/ruby.h
ossl_pkey_dh.o: $(hdrdir)/ruby/st.h
@@ -568,10 +584,10 @@ ossl_pkey_dh.o: ossl_config.h
ossl_pkey_dh.o: ossl_digest.h
ossl_pkey_dh.o: ossl_engine.h
ossl_pkey_dh.o: ossl_hmac.h
+ossl_pkey_dh.o: ossl_kdf.h
ossl_pkey_dh.o: ossl_ns_spki.h
ossl_pkey_dh.o: ossl_ocsp.h
ossl_pkey_dh.o: ossl_pkcs12.h
-ossl_pkey_dh.o: ossl_pkcs5.h
ossl_pkey_dh.o: ossl_pkcs7.h
ossl_pkey_dh.o: ossl_pkey.h
ossl_pkey_dh.o: ossl_pkey_dh.c
@@ -588,6 +604,7 @@ ossl_pkey_dsa.o: $(hdrdir)/ruby/encoding.h
ossl_pkey_dsa.o: $(hdrdir)/ruby/intern.h
ossl_pkey_dsa.o: $(hdrdir)/ruby/io.h
ossl_pkey_dsa.o: $(hdrdir)/ruby/missing.h
+ossl_pkey_dsa.o: $(hdrdir)/ruby/onigmo.h
ossl_pkey_dsa.o: $(hdrdir)/ruby/oniguruma.h
ossl_pkey_dsa.o: $(hdrdir)/ruby/ruby.h
ossl_pkey_dsa.o: $(hdrdir)/ruby/st.h
@@ -604,10 +621,10 @@ ossl_pkey_dsa.o: ossl_config.h
ossl_pkey_dsa.o: ossl_digest.h
ossl_pkey_dsa.o: ossl_engine.h
ossl_pkey_dsa.o: ossl_hmac.h
+ossl_pkey_dsa.o: ossl_kdf.h
ossl_pkey_dsa.o: ossl_ns_spki.h
ossl_pkey_dsa.o: ossl_ocsp.h
ossl_pkey_dsa.o: ossl_pkcs12.h
-ossl_pkey_dsa.o: ossl_pkcs5.h
ossl_pkey_dsa.o: ossl_pkcs7.h
ossl_pkey_dsa.o: ossl_pkey.h
ossl_pkey_dsa.o: ossl_pkey_dsa.c
@@ -624,6 +641,7 @@ ossl_pkey_ec.o: $(hdrdir)/ruby/encoding.h
ossl_pkey_ec.o: $(hdrdir)/ruby/intern.h
ossl_pkey_ec.o: $(hdrdir)/ruby/io.h
ossl_pkey_ec.o: $(hdrdir)/ruby/missing.h
+ossl_pkey_ec.o: $(hdrdir)/ruby/onigmo.h
ossl_pkey_ec.o: $(hdrdir)/ruby/oniguruma.h
ossl_pkey_ec.o: $(hdrdir)/ruby/ruby.h
ossl_pkey_ec.o: $(hdrdir)/ruby/st.h
@@ -640,10 +658,10 @@ ossl_pkey_ec.o: ossl_config.h
ossl_pkey_ec.o: ossl_digest.h
ossl_pkey_ec.o: ossl_engine.h
ossl_pkey_ec.o: ossl_hmac.h
+ossl_pkey_ec.o: ossl_kdf.h
ossl_pkey_ec.o: ossl_ns_spki.h
ossl_pkey_ec.o: ossl_ocsp.h
ossl_pkey_ec.o: ossl_pkcs12.h
-ossl_pkey_ec.o: ossl_pkcs5.h
ossl_pkey_ec.o: ossl_pkcs7.h
ossl_pkey_ec.o: ossl_pkey.h
ossl_pkey_ec.o: ossl_pkey_ec.c
@@ -660,6 +678,7 @@ ossl_pkey_rsa.o: $(hdrdir)/ruby/encoding.h
ossl_pkey_rsa.o: $(hdrdir)/ruby/intern.h
ossl_pkey_rsa.o: $(hdrdir)/ruby/io.h
ossl_pkey_rsa.o: $(hdrdir)/ruby/missing.h
+ossl_pkey_rsa.o: $(hdrdir)/ruby/onigmo.h
ossl_pkey_rsa.o: $(hdrdir)/ruby/oniguruma.h
ossl_pkey_rsa.o: $(hdrdir)/ruby/ruby.h
ossl_pkey_rsa.o: $(hdrdir)/ruby/st.h
@@ -676,10 +695,10 @@ ossl_pkey_rsa.o: ossl_config.h
ossl_pkey_rsa.o: ossl_digest.h
ossl_pkey_rsa.o: ossl_engine.h
ossl_pkey_rsa.o: ossl_hmac.h
+ossl_pkey_rsa.o: ossl_kdf.h
ossl_pkey_rsa.o: ossl_ns_spki.h
ossl_pkey_rsa.o: ossl_ocsp.h
ossl_pkey_rsa.o: ossl_pkcs12.h
-ossl_pkey_rsa.o: ossl_pkcs5.h
ossl_pkey_rsa.o: ossl_pkcs7.h
ossl_pkey_rsa.o: ossl_pkey.h
ossl_pkey_rsa.o: ossl_pkey_rsa.c
@@ -696,6 +715,7 @@ ossl_rand.o: $(hdrdir)/ruby/encoding.h
ossl_rand.o: $(hdrdir)/ruby/intern.h
ossl_rand.o: $(hdrdir)/ruby/io.h
ossl_rand.o: $(hdrdir)/ruby/missing.h
+ossl_rand.o: $(hdrdir)/ruby/onigmo.h
ossl_rand.o: $(hdrdir)/ruby/oniguruma.h
ossl_rand.o: $(hdrdir)/ruby/ruby.h
ossl_rand.o: $(hdrdir)/ruby/st.h
@@ -712,10 +732,10 @@ ossl_rand.o: ossl_config.h
ossl_rand.o: ossl_digest.h
ossl_rand.o: ossl_engine.h
ossl_rand.o: ossl_hmac.h
+ossl_rand.o: ossl_kdf.h
ossl_rand.o: ossl_ns_spki.h
ossl_rand.o: ossl_ocsp.h
ossl_rand.o: ossl_pkcs12.h
-ossl_rand.o: ossl_pkcs5.h
ossl_rand.o: ossl_pkcs7.h
ossl_rand.o: ossl_pkey.h
ossl_rand.o: ossl_rand.c
@@ -732,6 +752,7 @@ ossl_ssl.o: $(hdrdir)/ruby/encoding.h
ossl_ssl.o: $(hdrdir)/ruby/intern.h
ossl_ssl.o: $(hdrdir)/ruby/io.h
ossl_ssl.o: $(hdrdir)/ruby/missing.h
+ossl_ssl.o: $(hdrdir)/ruby/onigmo.h
ossl_ssl.o: $(hdrdir)/ruby/oniguruma.h
ossl_ssl.o: $(hdrdir)/ruby/ruby.h
ossl_ssl.o: $(hdrdir)/ruby/st.h
@@ -748,10 +769,10 @@ ossl_ssl.o: ossl_config.h
ossl_ssl.o: ossl_digest.h
ossl_ssl.o: ossl_engine.h
ossl_ssl.o: ossl_hmac.h
+ossl_ssl.o: ossl_kdf.h
ossl_ssl.o: ossl_ns_spki.h
ossl_ssl.o: ossl_ocsp.h
ossl_ssl.o: ossl_pkcs12.h
-ossl_ssl.o: ossl_pkcs5.h
ossl_ssl.o: ossl_pkcs7.h
ossl_ssl.o: ossl_pkey.h
ossl_ssl.o: ossl_rand.h
@@ -768,6 +789,7 @@ ossl_ssl_session.o: $(hdrdir)/ruby/encoding.h
ossl_ssl_session.o: $(hdrdir)/ruby/intern.h
ossl_ssl_session.o: $(hdrdir)/ruby/io.h
ossl_ssl_session.o: $(hdrdir)/ruby/missing.h
+ossl_ssl_session.o: $(hdrdir)/ruby/onigmo.h
ossl_ssl_session.o: $(hdrdir)/ruby/oniguruma.h
ossl_ssl_session.o: $(hdrdir)/ruby/ruby.h
ossl_ssl_session.o: $(hdrdir)/ruby/st.h
@@ -784,10 +806,10 @@ ossl_ssl_session.o: ossl_config.h
ossl_ssl_session.o: ossl_digest.h
ossl_ssl_session.o: ossl_engine.h
ossl_ssl_session.o: ossl_hmac.h
+ossl_ssl_session.o: ossl_kdf.h
ossl_ssl_session.o: ossl_ns_spki.h
ossl_ssl_session.o: ossl_ocsp.h
ossl_ssl_session.o: ossl_pkcs12.h
-ossl_ssl_session.o: ossl_pkcs5.h
ossl_ssl_session.o: ossl_pkcs7.h
ossl_ssl_session.o: ossl_pkey.h
ossl_ssl_session.o: ossl_rand.h
@@ -804,6 +826,7 @@ ossl_x509.o: $(hdrdir)/ruby/encoding.h
ossl_x509.o: $(hdrdir)/ruby/intern.h
ossl_x509.o: $(hdrdir)/ruby/io.h
ossl_x509.o: $(hdrdir)/ruby/missing.h
+ossl_x509.o: $(hdrdir)/ruby/onigmo.h
ossl_x509.o: $(hdrdir)/ruby/oniguruma.h
ossl_x509.o: $(hdrdir)/ruby/ruby.h
ossl_x509.o: $(hdrdir)/ruby/st.h
@@ -820,10 +843,10 @@ ossl_x509.o: ossl_config.h
ossl_x509.o: ossl_digest.h
ossl_x509.o: ossl_engine.h
ossl_x509.o: ossl_hmac.h
+ossl_x509.o: ossl_kdf.h
ossl_x509.o: ossl_ns_spki.h
ossl_x509.o: ossl_ocsp.h
ossl_x509.o: ossl_pkcs12.h
-ossl_x509.o: ossl_pkcs5.h
ossl_x509.o: ossl_pkcs7.h
ossl_x509.o: ossl_pkey.h
ossl_x509.o: ossl_rand.h
@@ -840,6 +863,7 @@ ossl_x509attr.o: $(hdrdir)/ruby/encoding.h
ossl_x509attr.o: $(hdrdir)/ruby/intern.h
ossl_x509attr.o: $(hdrdir)/ruby/io.h
ossl_x509attr.o: $(hdrdir)/ruby/missing.h
+ossl_x509attr.o: $(hdrdir)/ruby/onigmo.h
ossl_x509attr.o: $(hdrdir)/ruby/oniguruma.h
ossl_x509attr.o: $(hdrdir)/ruby/ruby.h
ossl_x509attr.o: $(hdrdir)/ruby/st.h
@@ -856,10 +880,10 @@ ossl_x509attr.o: ossl_config.h
ossl_x509attr.o: ossl_digest.h
ossl_x509attr.o: ossl_engine.h
ossl_x509attr.o: ossl_hmac.h
+ossl_x509attr.o: ossl_kdf.h
ossl_x509attr.o: ossl_ns_spki.h
ossl_x509attr.o: ossl_ocsp.h
ossl_x509attr.o: ossl_pkcs12.h
-ossl_x509attr.o: ossl_pkcs5.h
ossl_x509attr.o: ossl_pkcs7.h
ossl_x509attr.o: ossl_pkey.h
ossl_x509attr.o: ossl_rand.h
@@ -876,6 +900,7 @@ ossl_x509cert.o: $(hdrdir)/ruby/encoding.h
ossl_x509cert.o: $(hdrdir)/ruby/intern.h
ossl_x509cert.o: $(hdrdir)/ruby/io.h
ossl_x509cert.o: $(hdrdir)/ruby/missing.h
+ossl_x509cert.o: $(hdrdir)/ruby/onigmo.h
ossl_x509cert.o: $(hdrdir)/ruby/oniguruma.h
ossl_x509cert.o: $(hdrdir)/ruby/ruby.h
ossl_x509cert.o: $(hdrdir)/ruby/st.h
@@ -892,10 +917,10 @@ ossl_x509cert.o: ossl_config.h
ossl_x509cert.o: ossl_digest.h
ossl_x509cert.o: ossl_engine.h
ossl_x509cert.o: ossl_hmac.h
+ossl_x509cert.o: ossl_kdf.h
ossl_x509cert.o: ossl_ns_spki.h
ossl_x509cert.o: ossl_ocsp.h
ossl_x509cert.o: ossl_pkcs12.h
-ossl_x509cert.o: ossl_pkcs5.h
ossl_x509cert.o: ossl_pkcs7.h
ossl_x509cert.o: ossl_pkey.h
ossl_x509cert.o: ossl_rand.h
@@ -912,6 +937,7 @@ ossl_x509crl.o: $(hdrdir)/ruby/encoding.h
ossl_x509crl.o: $(hdrdir)/ruby/intern.h
ossl_x509crl.o: $(hdrdir)/ruby/io.h
ossl_x509crl.o: $(hdrdir)/ruby/missing.h
+ossl_x509crl.o: $(hdrdir)/ruby/onigmo.h
ossl_x509crl.o: $(hdrdir)/ruby/oniguruma.h
ossl_x509crl.o: $(hdrdir)/ruby/ruby.h
ossl_x509crl.o: $(hdrdir)/ruby/st.h
@@ -928,10 +954,10 @@ ossl_x509crl.o: ossl_config.h
ossl_x509crl.o: ossl_digest.h
ossl_x509crl.o: ossl_engine.h
ossl_x509crl.o: ossl_hmac.h
+ossl_x509crl.o: ossl_kdf.h
ossl_x509crl.o: ossl_ns_spki.h
ossl_x509crl.o: ossl_ocsp.h
ossl_x509crl.o: ossl_pkcs12.h
-ossl_x509crl.o: ossl_pkcs5.h
ossl_x509crl.o: ossl_pkcs7.h
ossl_x509crl.o: ossl_pkey.h
ossl_x509crl.o: ossl_rand.h
@@ -948,6 +974,7 @@ ossl_x509ext.o: $(hdrdir)/ruby/encoding.h
ossl_x509ext.o: $(hdrdir)/ruby/intern.h
ossl_x509ext.o: $(hdrdir)/ruby/io.h
ossl_x509ext.o: $(hdrdir)/ruby/missing.h
+ossl_x509ext.o: $(hdrdir)/ruby/onigmo.h
ossl_x509ext.o: $(hdrdir)/ruby/oniguruma.h
ossl_x509ext.o: $(hdrdir)/ruby/ruby.h
ossl_x509ext.o: $(hdrdir)/ruby/st.h
@@ -964,10 +991,10 @@ ossl_x509ext.o: ossl_config.h
ossl_x509ext.o: ossl_digest.h
ossl_x509ext.o: ossl_engine.h
ossl_x509ext.o: ossl_hmac.h
+ossl_x509ext.o: ossl_kdf.h
ossl_x509ext.o: ossl_ns_spki.h
ossl_x509ext.o: ossl_ocsp.h
ossl_x509ext.o: ossl_pkcs12.h
-ossl_x509ext.o: ossl_pkcs5.h
ossl_x509ext.o: ossl_pkcs7.h
ossl_x509ext.o: ossl_pkey.h
ossl_x509ext.o: ossl_rand.h
@@ -984,6 +1011,7 @@ ossl_x509name.o: $(hdrdir)/ruby/encoding.h
ossl_x509name.o: $(hdrdir)/ruby/intern.h
ossl_x509name.o: $(hdrdir)/ruby/io.h
ossl_x509name.o: $(hdrdir)/ruby/missing.h
+ossl_x509name.o: $(hdrdir)/ruby/onigmo.h
ossl_x509name.o: $(hdrdir)/ruby/oniguruma.h
ossl_x509name.o: $(hdrdir)/ruby/ruby.h
ossl_x509name.o: $(hdrdir)/ruby/st.h
@@ -1000,10 +1028,10 @@ ossl_x509name.o: ossl_config.h
ossl_x509name.o: ossl_digest.h
ossl_x509name.o: ossl_engine.h
ossl_x509name.o: ossl_hmac.h
+ossl_x509name.o: ossl_kdf.h
ossl_x509name.o: ossl_ns_spki.h
ossl_x509name.o: ossl_ocsp.h
ossl_x509name.o: ossl_pkcs12.h
-ossl_x509name.o: ossl_pkcs5.h
ossl_x509name.o: ossl_pkcs7.h
ossl_x509name.o: ossl_pkey.h
ossl_x509name.o: ossl_rand.h
@@ -1020,6 +1048,7 @@ ossl_x509req.o: $(hdrdir)/ruby/encoding.h
ossl_x509req.o: $(hdrdir)/ruby/intern.h
ossl_x509req.o: $(hdrdir)/ruby/io.h
ossl_x509req.o: $(hdrdir)/ruby/missing.h
+ossl_x509req.o: $(hdrdir)/ruby/onigmo.h
ossl_x509req.o: $(hdrdir)/ruby/oniguruma.h
ossl_x509req.o: $(hdrdir)/ruby/ruby.h
ossl_x509req.o: $(hdrdir)/ruby/st.h
@@ -1036,10 +1065,10 @@ ossl_x509req.o: ossl_config.h
ossl_x509req.o: ossl_digest.h
ossl_x509req.o: ossl_engine.h
ossl_x509req.o: ossl_hmac.h
+ossl_x509req.o: ossl_kdf.h
ossl_x509req.o: ossl_ns_spki.h
ossl_x509req.o: ossl_ocsp.h
ossl_x509req.o: ossl_pkcs12.h
-ossl_x509req.o: ossl_pkcs5.h
ossl_x509req.o: ossl_pkcs7.h
ossl_x509req.o: ossl_pkey.h
ossl_x509req.o: ossl_rand.h
@@ -1056,6 +1085,7 @@ ossl_x509revoked.o: $(hdrdir)/ruby/encoding.h
ossl_x509revoked.o: $(hdrdir)/ruby/intern.h
ossl_x509revoked.o: $(hdrdir)/ruby/io.h
ossl_x509revoked.o: $(hdrdir)/ruby/missing.h
+ossl_x509revoked.o: $(hdrdir)/ruby/onigmo.h
ossl_x509revoked.o: $(hdrdir)/ruby/oniguruma.h
ossl_x509revoked.o: $(hdrdir)/ruby/ruby.h
ossl_x509revoked.o: $(hdrdir)/ruby/st.h
@@ -1072,10 +1102,10 @@ ossl_x509revoked.o: ossl_config.h
ossl_x509revoked.o: ossl_digest.h
ossl_x509revoked.o: ossl_engine.h
ossl_x509revoked.o: ossl_hmac.h
+ossl_x509revoked.o: ossl_kdf.h
ossl_x509revoked.o: ossl_ns_spki.h
ossl_x509revoked.o: ossl_ocsp.h
ossl_x509revoked.o: ossl_pkcs12.h
-ossl_x509revoked.o: ossl_pkcs5.h
ossl_x509revoked.o: ossl_pkcs7.h
ossl_x509revoked.o: ossl_pkey.h
ossl_x509revoked.o: ossl_rand.h
@@ -1092,6 +1122,7 @@ ossl_x509store.o: $(hdrdir)/ruby/encoding.h
ossl_x509store.o: $(hdrdir)/ruby/intern.h
ossl_x509store.o: $(hdrdir)/ruby/io.h
ossl_x509store.o: $(hdrdir)/ruby/missing.h
+ossl_x509store.o: $(hdrdir)/ruby/onigmo.h
ossl_x509store.o: $(hdrdir)/ruby/oniguruma.h
ossl_x509store.o: $(hdrdir)/ruby/ruby.h
ossl_x509store.o: $(hdrdir)/ruby/st.h
@@ -1108,10 +1139,10 @@ ossl_x509store.o: ossl_config.h
ossl_x509store.o: ossl_digest.h
ossl_x509store.o: ossl_engine.h
ossl_x509store.o: ossl_hmac.h
+ossl_x509store.o: ossl_kdf.h
ossl_x509store.o: ossl_ns_spki.h
ossl_x509store.o: ossl_ocsp.h
ossl_x509store.o: ossl_pkcs12.h
-ossl_x509store.o: ossl_pkcs5.h
ossl_x509store.o: ossl_pkcs7.h
ossl_x509store.o: ossl_pkey.h
ossl_x509store.o: ossl_rand.h
diff --git a/ext/openssl/deprecation.rb b/ext/openssl/deprecation.rb
index 7dfc87c1c6..1d51d065a9 100644
--- a/ext/openssl/deprecation.rb
+++ b/ext/openssl/deprecation.rb
@@ -3,9 +3,6 @@ module OpenSSL
def self.deprecated_warning_flag
unless flag = (@deprecated_warning_flag ||= nil)
if try_compile("", flag = "-Werror=deprecated-declarations")
- if with_config("broken-apple-openssl")
- flag = "-Wno-deprecated-declarations"
- end
$warnflags << " #{flag}"
else
flag = ""
diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb
index 20c67c6b50..4f218562b1 100644
--- a/ext/openssl/extconf.rb
+++ b/ext/openssl/extconf.rb
@@ -33,50 +33,80 @@ end
Logging::message "=== Checking for system dependent stuff... ===\n"
have_library("nsl", "t_open")
have_library("socket", "socket")
-have_header("assert.h")
+if $mswin || $mingw
+ have_library("ws2_32")
+end
Logging::message "=== Checking for required stuff... ===\n"
-if $mingw
- have_library("wsock32")
- have_library("gdi32")
+result = pkg_config("openssl") && have_header("openssl/ssl.h")
+
+def find_openssl_library
+ if $mswin || $mingw
+ # required for static OpenSSL libraries
+ have_library("gdi32") # OpenSSL <= 1.0.2 (for RAND_screen())
+ have_library("crypt32")
+ end
+
+ return false unless have_header("openssl/ssl.h")
+
+ ret = have_library("crypto", "CRYPTO_malloc") &&
+ have_library("ssl", "SSL_new")
+ return ret if ret
+
+ if $mswin
+ # OpenSSL >= 1.1.0: libcrypto.lib and libssl.lib.
+ if have_library("libcrypto", "CRYPTO_malloc") &&
+ have_library("libssl", "SSL_new")
+ return true
+ end
+
+ # OpenSSL <= 1.0.2: libeay32.lib and ssleay32.lib.
+ if have_library("libeay32", "CRYPTO_malloc") &&
+ have_library("ssleay32", "SSL_new")
+ return true
+ end
+
+ # LibreSSL: libcrypto-##.lib and libssl-##.lib, where ## is the ABI version
+ # number. We have to find the version number out by scanning libpath.
+ libpath = $LIBPATH.dup
+ libpath |= ENV["LIB"].split(File::PATH_SEPARATOR)
+ libpath.map! { |d| d.tr(File::ALT_SEPARATOR, File::SEPARATOR) }
+
+ ret = [
+ ["crypto", "CRYPTO_malloc"],
+ ["ssl", "SSL_new"]
+ ].all? do |base, func|
+ result = false
+ libs = ["lib#{base}-[0-9][0-9]", "lib#{base}-[0-9][0-9][0-9]"]
+ libs = Dir.glob(libs.map{|l| libpath.map{|d| File.join(d, l + ".*")}}.flatten).map{|path| File.basename(path, ".*")}.uniq
+ libs.each do |lib|
+ result = have_library(lib, func)
+ break if result
+ end
+ result
+ end
+ return ret if ret
+ end
+ return false
end
-result = pkg_config("openssl") && have_header("openssl/ssl.h")
unless result
- result = have_header("openssl/ssl.h")
- result &&= %w[crypto libeay32].any? {|lib| have_library(lib, "CRYPTO_malloc")}
- result &&= %w[ssl ssleay32].any? {|lib| have_library(lib, "SSL_new")}
- unless result
+ unless find_openssl_library
Logging::message "=== Checking for required stuff failed. ===\n"
Logging::message "Makefile wasn't created. Fix the errors above.\n"
- exit 1
+ raise "OpenSSL library could not be found. You might want to use " \
+ "--with-openssl-dir=<dir> option to specify the prefix where OpenSSL " \
+ "is installed."
end
end
-result = checking_for("OpenSSL version is 0.9.8 or later") {
- try_static_assert("OPENSSL_VERSION_NUMBER >= 0x00908000L", "openssl/opensslv.h")
-}
-unless result
- raise "OpenSSL 0.9.8 or later required."
-end
-
-unless OpenSSL.check_func("SSL_library_init()", "openssl/ssl.h")
- raise "Ignore OpenSSL broken by Apple.\nPlease use another openssl. (e.g. using `configure --with-openssl-dir=/path/to/openssl')"
+unless checking_for("OpenSSL version is 1.0.1 or later") {
+ try_static_assert("OPENSSL_VERSION_NUMBER >= 0x10001000L", "openssl/opensslv.h") }
+ raise "OpenSSL >= 1.0.1 or LibreSSL is required"
end
Logging::message "=== Checking for OpenSSL features... ===\n"
# compile options
-
-# check OPENSSL_NO_{SSL2,SSL3_METHOD} macro: on some environment, these symbols
-# exist even if compiled with no-ssl2 or no-ssl3-method.
-unless have_macro("OPENSSL_NO_SSL2", "openssl/opensslconf.h")
- have_func("SSLv2_method")
-end
-unless have_macro("OPENSSL_NO_SSL3_METHOD", "openssl/opensslconf.h")
- have_func("SSLv3_method")
-end
-have_func("TLSv1_1_method")
-have_func("TLSv1_2_method")
have_func("RAND_egd")
engines = %w{builtin_engines openbsd_dev_crypto dynamic 4758cca aep atalla chil
cswift nuron sureware ubsec padlock capi gmp gost cryptodev aesni}
@@ -84,27 +114,9 @@ engines.each { |name|
OpenSSL.check_func_or_macro("ENGINE_load_#{name}", "openssl/engine.h")
}
-# added in 0.9.8X
-have_func("EVP_CIPHER_CTX_new")
-have_func("EVP_CIPHER_CTX_free")
-
-# added in 1.0.0
-have_func("ASN1_TIME_adj")
-have_func("EVP_CIPHER_CTX_copy")
-have_func("EVP_PKEY_base_id")
-have_func("HMAC_CTX_copy")
-have_func("PKCS5_PBKDF2_HMAC")
-have_func("X509_NAME_hash_old")
-have_func("X509_STORE_CTX_get0_current_crl")
-have_func("X509_STORE_set_verify_cb")
-have_func("i2d_ASN1_SET_ANY")
-have_func("SSL_SESSION_cmp") # removed
-OpenSSL.check_func_or_macro("SSL_set_tlsext_host_name", "openssl/ssl.h")
-have_struct_member("CRYPTO_THREADID", "ptr", "openssl/crypto.h")
-
-# added in 1.0.1
-have_func("SSL_CTX_set_next_proto_select_cb")
-have_macro("EVP_CTRL_GCM_GET_TAG", ['openssl/evp.h']) && $defs.push("-DHAVE_AUTHENTICATED_ENCRYPTION")
+if ($mswin || $mingw) && have_macro("LIBRESSL_VERSION_NUMBER", "openssl/opensslv.h")
+ $defs.push("-DNOCRYPT")
+end
# added in 1.0.2
have_func("EC_curve_nist2nid")
@@ -117,8 +129,11 @@ OpenSSL.check_func_or_macro("SSL_get_server_tmp_key", "openssl/ssl.h")
have_func("SSL_is_server")
# added in 1.1.0
+if !have_struct_member("SSL", "ctx", "openssl/ssl.h") ||
+ try_static_assert("LIBRESSL_VERSION_NUMBER >= 0x2070000fL", "openssl/opensslv.h")
+ $defs.push("-DHAVE_OPAQUE_OPENSSL")
+end
have_func("CRYPTO_lock") || $defs.push("-DHAVE_OPENSSL_110_THREADING_API")
-have_struct_member("SSL", "ctx", "openssl/ssl.h") || $defs.push("-DHAVE_OPAQUE_OPENSSL")
have_func("BN_GENCB_new")
have_func("BN_GENCB_free")
have_func("BN_GENCB_get_arg")
@@ -148,6 +163,8 @@ OpenSSL.check_func_or_macro("SSL_CTX_set_tmp_ecdh_callback", "openssl/ssl.h") #
OpenSSL.check_func_or_macro("SSL_CTX_set_min_proto_version", "openssl/ssl.h")
have_func("SSL_CTX_get_security_level")
have_func("X509_get0_notBefore")
+have_func("SSL_SESSION_get_protocol_version")
+have_func("EVP_PBE_scrypt")
Logging::message "=== Checking done. ===\n"
diff --git a/ext/openssl/lib/openssl.rb b/ext/openssl/lib/openssl.rb
index 26d167a9b4..0914282920 100644
--- a/ext/openssl/lib/openssl.rb
+++ b/ext/openssl/lib/openssl.rb
@@ -19,3 +19,4 @@ require 'openssl/config'
require 'openssl/digest'
require 'openssl/x509'
require 'openssl/ssl'
+require 'openssl/pkcs5'
diff --git a/ext/openssl/lib/openssl/bn.rb b/ext/openssl/lib/openssl/bn.rb
index 6d6c96e42d..8d1ebefb6e 100644
--- a/ext/openssl/lib/openssl/bn.rb
+++ b/ext/openssl/lib/openssl/bn.rb
@@ -27,8 +27,9 @@ module OpenSSL
end # OpenSSL
##
+#--
# Add double dispatch to Integer
-#
+#++
class Integer
# Casts an Integer as an OpenSSL::BN
#
diff --git a/ext/openssl/lib/openssl/buffering.rb b/ext/openssl/lib/openssl/buffering.rb
index 61e1f43e00..5d1586e594 100644
--- a/ext/openssl/lib/openssl/buffering.rb
+++ b/ext/openssl/lib/openssl/buffering.rb
@@ -63,7 +63,7 @@ module OpenSSL::Buffering
end
##
- # Consumes +size+ bytes from the buffer
+ # Consumes _size_ bytes from the buffer
def consume_rbuff(size=nil)
if @rbuffer.empty?
@@ -79,7 +79,7 @@ module OpenSSL::Buffering
public
##
- # Reads +size+ bytes from the stream. If +buf+ is provided it must
+ # Reads _size_ bytes from the stream. If _buf_ is provided it must
# reference a string which will receive the data.
#
# See IO#read for full details.
@@ -106,7 +106,7 @@ module OpenSSL::Buffering
end
##
- # Reads at most +maxlen+ bytes from the stream. If +buf+ is provided it
+ # Reads at most _maxlen_ bytes from the stream. If _buf_ is provided it
# must reference a string which will receive the data.
#
# See IO#readpartial for full details.
@@ -136,7 +136,7 @@ module OpenSSL::Buffering
end
##
- # Reads at most +maxlen+ bytes in the non-blocking manner.
+ # Reads at most _maxlen_ bytes in the non-blocking manner.
#
# When no data can be read without blocking it raises
# OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
@@ -163,6 +163,11 @@ module OpenSSL::Buffering
# Note that one reason that read_nonblock writes to the underlying IO is
# when the peer requests a new TLS/SSL handshake. See openssl the FAQ for
# more details. http://www.openssl.org/support/faq.html
+ #
+ # By specifying a keyword argument _exception_ to +false+, you can indicate
+ # that read_nonblock should not raise an IO::Wait*able exception, but
+ # return the symbol +:wait_writable+ or +:wait_readable+ instead. At EOF,
+ # it will return +nil+ instead of raising EOFError.
def read_nonblock(maxlen, buf=nil, exception: true)
if maxlen == 0
@@ -185,11 +190,11 @@ module OpenSSL::Buffering
end
##
- # Reads the next "line+ from the stream. Lines are separated by +eol+. If
- # +limit+ is provided the result will not be longer than the given number of
+ # Reads the next "line" from the stream. Lines are separated by _eol_. If
+ # _limit_ is provided the result will not be longer than the given number of
# bytes.
#
- # +eol+ may be a String or Regexp.
+ # _eol_ may be a String or Regexp.
#
# Unlike IO#gets the line read will not be assigned to +$_+.
#
@@ -215,7 +220,7 @@ module OpenSSL::Buffering
##
# Executes the block for every line in the stream where lines are separated
- # by +eol+.
+ # by _eol_.
#
# See also #gets
@@ -227,7 +232,7 @@ module OpenSSL::Buffering
alias each_line each
##
- # Reads lines from the stream which are separated by +eol+.
+ # Reads lines from the stream which are separated by _eol_.
#
# See also #gets
@@ -240,7 +245,7 @@ module OpenSSL::Buffering
end
##
- # Reads a line from the stream which is separated by +eol+.
+ # Reads a line from the stream which is separated by _eol_.
#
# Raises EOFError if at end of file.
@@ -276,7 +281,7 @@ module OpenSSL::Buffering
end
##
- # Pushes character +c+ back onto the stream such that a subsequent buffered
+ # Pushes character _c_ back onto the stream such that a subsequent buffered
# character read will return it.
#
# Unlike IO#getc multiple bytes may be pushed back onto the stream.
@@ -303,7 +308,7 @@ module OpenSSL::Buffering
private
##
- # Writes +s+ to the buffer. When the buffer is full or #sync is true the
+ # Writes _s_ to the buffer. When the buffer is full or #sync is true the
# buffer is flushed to the underlying socket.
def do_write(s)
@@ -311,36 +316,33 @@ module OpenSSL::Buffering
@wbuffer << s
@wbuffer.force_encoding(Encoding::BINARY)
@sync ||= false
- if @sync or @wbuffer.size > BLOCK_SIZE or idx = @wbuffer.rindex($/)
- remain = idx ? idx + $/.size : @wbuffer.length
- nwritten = 0
- while remain > 0
- str = @wbuffer[nwritten,remain]
+ if @sync or @wbuffer.size > BLOCK_SIZE
+ until @wbuffer.empty?
begin
- nwrote = syswrite(str)
+ nwrote = syswrite(@wbuffer)
rescue Errno::EAGAIN
retry
end
- remain -= nwrote
- nwritten += nwrote
+ @wbuffer[0, nwrote] = ""
end
- @wbuffer[0,nwritten] = ""
end
end
public
##
- # Writes +s+ to the stream. If the argument is not a string it will be
- # converted using String#to_s. Returns the number of bytes written.
+ # Writes _s_ to the stream. If the argument is not a String it will be
+ # converted using +.to_s+ method. Returns the number of bytes written.
- def write(s)
- do_write(s)
- s.bytesize
+ def write(*s)
+ s.inject(0) do |written, str|
+ do_write(str)
+ written + str.bytesize
+ end
end
##
- # Writes +str+ in the non-blocking manner.
+ # Writes _s_ in the non-blocking manner.
#
# If there is buffered data, it is flushed first. This may block.
#
@@ -371,6 +373,10 @@ module OpenSSL::Buffering
# Note that one reason that write_nonblock reads from the underlying IO
# is when the peer requests a new TLS/SSL handshake. See the openssl FAQ
# for more details. http://www.openssl.org/support/faq.html
+ #
+ # By specifying a keyword argument _exception_ to +false+, you can indicate
+ # that write_nonblock should not raise an IO::Wait*able exception, but
+ # return the symbol +:wait_writable+ or +:wait_readable+ instead.
def write_nonblock(s, exception: true)
flush
@@ -378,16 +384,16 @@ module OpenSSL::Buffering
end
##
- # Writes +s+ to the stream. +s+ will be converted to a String using
- # String#to_s.
+ # Writes _s_ to the stream. _s_ will be converted to a String using
+ # +.to_s+ method.
- def << (s)
+ def <<(s)
do_write(s)
self
end
##
- # Writes +args+ to the stream along with a record separator.
+ # Writes _args_ to the stream along with a record separator.
#
# See IO#puts for full details.
@@ -398,16 +404,14 @@ module OpenSSL::Buffering
end
args.each{|arg|
s << arg.to_s
- if $/ && /\n\z/ !~ s
- s << "\n"
- end
+ s.sub!(/(?<!\n)\z/, "\n")
}
do_write(s)
nil
end
##
- # Writes +args+ to the stream.
+ # Writes _args_ to the stream.
#
# See IO#print for full details.
diff --git a/ext/openssl/lib/openssl/config.rb b/ext/openssl/lib/openssl/config.rb
index 8822545192..48d8be0069 100644
--- a/ext/openssl/lib/openssl/config.rb
+++ b/ext/openssl/lib/openssl/config.rb
@@ -30,7 +30,8 @@ module OpenSSL
class << self
##
- # Parses a given +string+ as a blob that contains configuration for openssl.
+ # Parses a given _string_ as a blob that contains configuration for
+ # OpenSSL.
#
# If the source of the IO is a file, then consider using #parse_config.
def parse(string)
@@ -46,7 +47,7 @@ module OpenSSL
alias load new
##
- # Parses the configuration data read from +io+, see also #parse.
+ # Parses the configuration data read from _io_, see also #parse.
#
# Raises a ConfigError on invalid configuration data.
def parse_config(io)
@@ -236,7 +237,7 @@ module OpenSSL
#
# This can be used in contexts like OpenSSL::X509::ExtensionFactory.config=
#
- # If the optional +filename+ parameter is provided, then it is read in and
+ # If the optional _filename_ parameter is provided, then it is read in and
# parsed via #parse_config.
#
# This can raise IO exceptions based on the access, or availability of the
@@ -255,7 +256,7 @@ module OpenSSL
end
##
- # Gets the value of +key+ from the given +section+
+ # Gets the value of _key_ from the given _section_
#
# Given the following configurating file being loaded:
#
@@ -265,8 +266,8 @@ module OpenSSL
# #=> [ default ]
# # foo=bar
#
- # You can get a specific value from the config if you know the +section+
- # and +key+ like so:
+ # You can get a specific value from the config if you know the _section_
+ # and _key_ like so:
#
# config.get_value('default','foo')
# #=> "bar"
@@ -297,7 +298,7 @@ module OpenSSL
end
##
- # Set the target +key+ with a given +value+ under a specific +section+.
+ # Set the target _key_ with a given _value_ under a specific _section_.
#
# Given the following configurating file being loaded:
#
@@ -307,7 +308,7 @@ module OpenSSL
# #=> [ default ]
# # foo=bar
#
- # You can set the value of +foo+ under the +default+ section to a new
+ # You can set the value of _foo_ under the _default_ section to a new
# value:
#
# config.add_value('default', 'foo', 'buzz')
@@ -322,7 +323,7 @@ module OpenSSL
end
##
- # Get a specific +section+ from the current configuration
+ # Get a specific _section_ from the current configuration
#
# Given the following configurating file being loaded:
#
@@ -351,7 +352,7 @@ module OpenSSL
end
##
- # Sets a specific +section+ name with a Hash +pairs+
+ # Sets a specific _section_ name with a Hash _pairs_.
#
# Given the following configuration being created:
#
@@ -365,7 +366,7 @@ module OpenSSL
# # baz=buz
#
# It's important to note that this will essentially merge any of the keys
- # in +pairs+ with the existing +section+. For example:
+ # in _pairs_ with the existing _section_. For example:
#
# config['default']
# #=> {"foo"=>"bar", "baz"=>"buz"}
diff --git a/ext/openssl/lib/openssl/digest.rb b/ext/openssl/lib/openssl/digest.rb
index 97ccbc9569..b6744de6bd 100644
--- a/ext/openssl/lib/openssl/digest.rb
+++ b/ext/openssl/lib/openssl/digest.rb
@@ -15,15 +15,12 @@
module OpenSSL
class Digest
- alg = %w(MD2 MD4 MD5 MDC2 RIPEMD160 SHA1)
+ alg = %w(MD2 MD4 MD5 MDC2 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512)
if OPENSSL_VERSION_NUMBER < 0x10100000
alg += %w(DSS DSS1 SHA)
end
- if OPENSSL_VERSION_NUMBER > 0x00908000
- alg += %w(SHA224 SHA256 SHA384 SHA512)
- end
- # Return the +data+ hash computed with +name+ Digest. +name+ is either the
+ # Return the hash value computed with _name_ Digest. _name_ is either the
# long name or short name of a supported digest algorithm.
#
# === Examples
@@ -59,7 +56,7 @@ module OpenSSL
end # Digest
- # Returns a Digest subclass by +name+.
+ # Returns a Digest subclass by _name_
#
# require 'openssl'
#
diff --git a/ext/openssl/lib/openssl/pkcs5.rb b/ext/openssl/lib/openssl/pkcs5.rb
new file mode 100644
index 0000000000..959447df5e
--- /dev/null
+++ b/ext/openssl/lib/openssl/pkcs5.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: false
+#--
+# Ruby/OpenSSL Project
+# Copyright (C) 2017 Ruby/OpenSSL Project Authors
+#++
+
+module OpenSSL
+ module PKCS5
+ module_function
+
+ # OpenSSL::PKCS5.pbkdf2_hmac has been renamed to OpenSSL::KDF.pbkdf2_hmac.
+ # This method is provided for backwards compatibility.
+ def pbkdf2_hmac(pass, salt, iter, keylen, digest)
+ OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter,
+ length: keylen, hash: digest)
+ end
+
+ def pbkdf2_hmac_sha1(pass, salt, iter, keylen)
+ pbkdf2_hmac(pass, salt, iter, keylen, "sha1")
+ end
+ end
+end
diff --git a/ext/openssl/lib/openssl/pkey.rb b/ext/openssl/lib/openssl/pkey.rb
index 9af5f781f9..8a547c340d 100644
--- a/ext/openssl/lib/openssl/pkey.rb
+++ b/ext/openssl/lib/openssl/pkey.rb
@@ -1,44 +1,25 @@
# frozen_string_literal: false
-module OpenSSL
- module PKey
- if defined?(OpenSSL::PKey::DH)
+#--
+# Ruby/OpenSSL Project
+# Copyright (C) 2017 Ruby/OpenSSL Project Authors
+#++
- class DH
- # :nodoc:
- DEFAULT_1024 = new <<-_end_of_pem_
------BEGIN DH PARAMETERS-----
-MIGHAoGBAJ0lOVy0VIr/JebWn0zDwY2h+rqITFOpdNr6ugsgvkDXuucdcChhYExJ
-AV/ZD2AWPbrTqV76mGRgJg4EddgT1zG0jq3rnFdMj2XzkBYx3BVvfR0Arnby0RHR
-T4h7KZ/2zmjvV+eF8kBUHBJAojUlzxKj4QeO2x20FP9X5xmNUXeDAgEC
------END DH PARAMETERS-----
- _end_of_pem_
-
- # :nodoc:
- DEFAULT_2048 = new <<-_end_of_pem_
------BEGIN DH PARAMETERS-----
-MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY
-JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab
-VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6
-YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
-1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD
-7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg==
------END DH PARAMETERS-----
- _end_of_pem_
- end
-
- # :nodoc:
- DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen|
- warn "using default DH parameters." if $VERBOSE
- case keylen
- when 1024 then OpenSSL::PKey::DH::DEFAULT_1024
- when 2048 then OpenSSL::PKey::DH::DEFAULT_2048
- else
- nil
- end
- }
-
- else
- DEFAULT_TMP_DH_CALLBACK = nil
+module OpenSSL::PKey
+ if defined?(EC)
+ class EC::Point
+ # :call-seq:
+ # point.to_bn([conversion_form]) -> OpenSSL::BN
+ #
+ # Returns the octet string representation of the EC point as an instance of
+ # OpenSSL::BN.
+ #
+ # If _conversion_form_ is not given, the _point_conversion_form_ attribute
+ # set to the group is used.
+ #
+ # See #to_octet_string for more information.
+ def to_bn(conversion_form = group.point_conversion_form)
+ OpenSSL::BN.new(to_octet_string(conversion_form), 2)
end
end
+ end
end
diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb
index 190f504276..355eb2ebbb 100644
--- a/ext/openssl/lib/openssl/ssl.rb
+++ b/ext/openssl/lib/openssl/ssl.rb
@@ -12,24 +12,42 @@
require "openssl/buffering"
require "io/nonblock"
+require "ipaddr"
module OpenSSL
module SSL
class SSLContext
- # :nodoc:
- DEFAULT_PARAMS = {
- :ssl_version => "SSLv23",
+ DEFAULT_PARAMS = { # :nodoc:
+ :min_version => OpenSSL::SSL::TLS1_VERSION,
:verify_mode => OpenSSL::SSL::VERIFY_PEER,
:verify_hostname => true,
:options => -> {
opts = OpenSSL::SSL::OP_ALL
opts &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS
- opts |= OpenSSL::SSL::OP_NO_COMPRESSION if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
- opts |= OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3
+ opts |= OpenSSL::SSL::OP_NO_COMPRESSION
opts
}.call
}
+ if defined?(OpenSSL::PKey::DH)
+ DEFAULT_2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY
+JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab
+VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6
+YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
+1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD
+7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg==
+-----END DH PARAMETERS-----
+ _end_of_pem_
+ private_constant :DEFAULT_2048
+
+ DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen| # :nodoc:
+ warn "using default DH parameters." if $VERBOSE
+ DEFAULT_2048
+ }
+ end
+
if !(OpenSSL::OPENSSL_VERSION.start_with?("OpenSSL") &&
OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10100000)
DEFAULT_PARAMS.merge!(
@@ -68,8 +86,7 @@ module OpenSSL
)
end
- # :nodoc:
- DEFAULT_CERT_STORE = OpenSSL::X509::Store.new
+ DEFAULT_CERT_STORE = OpenSSL::X509::Store.new # :nodoc:
DEFAULT_CERT_STORE.set_default_paths
DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
@@ -84,21 +101,23 @@ module OpenSSL
attr_accessor :tmp_dh_callback
- if ExtConfig::HAVE_TLSEXT_HOST_NAME
- # A callback invoked at connect time to distinguish between multiple
- # server names.
- #
- # The callback is invoked with an SSLSocket and a server name. The
- # callback must return an SSLContext for the server name or nil.
- attr_accessor :servername_cb
- end
+ # A callback invoked at connect time to distinguish between multiple
+ # server names.
+ #
+ # The callback is invoked with an SSLSocket and a server name. The
+ # callback must return an SSLContext for the server name or nil.
+ attr_accessor :servername_cb
# call-seq:
- # SSLContext.new => ctx
- # SSLContext.new(:TLSv1) => ctx
- # SSLContext.new("SSLv23_client") => ctx
+ # SSLContext.new -> ctx
+ # SSLContext.new(:TLSv1) -> ctx
+ # SSLContext.new("SSLv23") -> ctx
+ #
+ # Creates a new SSL context.
#
- # You can get a list of valid methods with OpenSSL::SSL::SSLContext::METHODS
+ # If an argument is given, #ssl_version= is called with the value. Note
+ # that this form is deprecated. New applications should use #min_version=
+ # and #max_version= as necessary.
def initialize(version = nil)
self.options |= OpenSSL::SSL::OP_ALL
self.ssl_version = version if version
@@ -110,14 +129,15 @@ module OpenSSL
#
# Sets saner defaults optimized for the use with HTTP-like protocols.
#
- # If a Hash +params+ is given, the parameters are overridden with it.
- # The keys in +params+ must be assignment methods on SSLContext.
+ # If a Hash _params_ is given, the parameters are overridden with it.
+ # The keys in _params_ must be assignment methods on SSLContext.
#
# If the verify_mode is not VERIFY_NONE and ca_file, ca_path and
# cert_store are not set then the system default certificate store is
# used.
def set_params(params={})
params = DEFAULT_PARAMS.merge(params)
+ self.options = params.delete(:options) # set before min_version/max_version
params.each{|name, value| self.__send__("#{name}=", value) }
if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
unless self.ca_file or self.ca_path or self.cert_store
@@ -126,6 +146,88 @@ module OpenSSL
end
return params
end
+
+ # call-seq:
+ # ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
+ # ctx.min_version = :TLS1_2
+ # ctx.min_version = nil
+ #
+ # Sets the lower bound on the supported SSL/TLS protocol version. The
+ # version may be specified by an integer constant named
+ # OpenSSL::SSL::*_VERSION, a Symbol, or +nil+ which means "any version".
+ #
+ # Be careful that you don't overwrite OpenSSL::SSL::OP_NO_{SSL,TLS}v*
+ # options by #options= once you have called #min_version= or
+ # #max_version=.
+ #
+ # === Example
+ # ctx = OpenSSL::SSL::SSLContext.new
+ # ctx.min_version = OpenSSL::SSL::TLS1_1_VERSION
+ # ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
+ #
+ # sock = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx)
+ # sock.connect # Initiates a connection using either TLS 1.1 or TLS 1.2
+ def min_version=(version)
+ set_minmax_proto_version(version, @max_proto_version ||= nil)
+ @min_proto_version = version
+ end
+
+ # call-seq:
+ # ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
+ # ctx.max_version = :TLS1_2
+ # ctx.max_version = nil
+ #
+ # Sets the upper bound of the supported SSL/TLS protocol version. See
+ # #min_version= for the possible values.
+ def max_version=(version)
+ set_minmax_proto_version(@min_proto_version ||= nil, version)
+ @max_proto_version = version
+ end
+
+ # call-seq:
+ # ctx.ssl_version = :TLSv1
+ # ctx.ssl_version = "SSLv23"
+ #
+ # Sets the SSL/TLS protocol version for the context. This forces
+ # connections to use only the specified protocol version. This is
+ # deprecated and only provided for backwards compatibility. Use
+ # #min_version= and #max_version= instead.
+ #
+ # === History
+ # As the name hints, this used to call the SSL_CTX_set_ssl_version()
+ # function which sets the SSL method used for connections created from
+ # the context. As of Ruby/OpenSSL 2.1, this accessor method is
+ # implemented to call #min_version= and #max_version= instead.
+ def ssl_version=(meth)
+ meth = meth.to_s if meth.is_a?(Symbol)
+ if /(?<type>_client|_server)\z/ =~ meth
+ meth = $`
+ if $VERBOSE
+ warn "#{caller(1, 1)[0]}: method type #{type.inspect} is ignored"
+ end
+ end
+ version = METHODS_MAP[meth.intern] or
+ raise ArgumentError, "unknown SSL method `%s'" % meth
+ set_minmax_proto_version(version, version)
+ @min_proto_version = @max_proto_version = version
+ end
+
+ METHODS_MAP = {
+ SSLv23: 0,
+ SSLv2: OpenSSL::SSL::SSL2_VERSION,
+ SSLv3: OpenSSL::SSL::SSL3_VERSION,
+ TLSv1: OpenSSL::SSL::TLS1_VERSION,
+ TLSv1_1: OpenSSL::SSL::TLS1_1_VERSION,
+ TLSv1_2: OpenSSL::SSL::TLS1_2_VERSION,
+ }.freeze
+ private_constant :METHODS_MAP
+
+ # The list of available SSL/TLS methods. This constant is only provided
+ # for backwards compatibility.
+ METHODS = METHODS_MAP.flat_map { |name,|
+ [name, :"#{name}_client", :"#{name}_server"]
+ }.freeze
+ deprecate_constant :METHODS
end
module SocketForwarder
@@ -171,11 +273,11 @@ module OpenSSL
return true if verify_hostname(hostname, san.value)
when 7 # iPAddress in GeneralName (RFC5280)
should_verify_common_name = false
- # follows GENERAL_NAME_print() in x509v3/v3_alt.c
- if san.value.size == 4
- return true if san.value.unpack('C*').join('.') == hostname
- elsif san.value.size == 16
- return true if san.value.unpack('n*').map { |e| sprintf("%X", e) }.join(':') == hostname
+ if san.value.size == 4 || san.value.size == 16
+ begin
+ return true if san.value == IPAddr.new(hostname).hton
+ rescue IPAddr::InvalidAddressError
+ end
end
end
}
@@ -246,9 +348,7 @@ module OpenSSL
include Buffering
include SocketForwarder
- if ExtConfig::HAVE_TLSEXT_HOST_NAME
- attr_reader :hostname
- end
+ attr_reader :hostname
# The underlying IO object.
attr_reader :io
@@ -321,7 +421,7 @@ module OpenSSL
end
def tmp_dh_callback
- @context.tmp_dh_callback || OpenSSL::PKey::DEFAULT_TMP_DH_CALLBACK
+ @context.tmp_dh_callback || OpenSSL::SSL::SSLContext::DEFAULT_TMP_DH_CALLBACK
end
def tmp_ecdh_callback
@@ -345,8 +445,8 @@ module OpenSSL
attr_accessor :start_immediately
# Creates a new instance of SSLServer.
- # * +srv+ is an instance of TCPServer.
- # * +ctx+ is an instance of OpenSSL::SSL::SSLContext.
+ # * _srv_ is an instance of TCPServer.
+ # * _ctx_ is an instance of OpenSSL::SSL::SSLContext.
def initialize(svr, ctx)
@svr = svr
@ctx = ctx
diff --git a/ext/openssl/lib/openssl/x509.rb b/ext/openssl/lib/openssl/x509.rb
index aef3456e0f..98358f90da 100644
--- a/ext/openssl/lib/openssl/x509.rb
+++ b/ext/openssl/lib/openssl/x509.rb
@@ -41,6 +41,11 @@ module OpenSSL
end
class Extension
+ def ==(other)
+ return false unless Extension === other
+ to_der == other.to_der
+ end
+
def to_s # "oid = critical, value"
str = self.oid
str << " = "
@@ -139,7 +144,13 @@ module OpenSSL
end
def parse_openssl(str, template=OBJECT_TYPE_TEMPLATE)
- ary = str.scan(/\s*([^\/,]+)\s*/).collect{|i| i[0].split("=", 2) }
+ if str.start_with?("/")
+ # /A=B/C=D format
+ ary = str[1..-1].split("/").map { |i| i.split("=", 2) }
+ else
+ # Comma-separated
+ ary = str.split(",").map { |i| i.strip.split("=", 2) }
+ end
self.new(ary, template)
end
@@ -154,6 +165,13 @@ module OpenSSL
end
end
+ class Attribute
+ def ==(other)
+ return false unless Attribute === other
+ to_der == other.to_der
+ end
+ end
+
class StoreContext
def cleanup
warn "(#{caller.first}) OpenSSL::X509::StoreContext#cleanup is deprecated with no replacement" if $VERBOSE
@@ -172,5 +190,26 @@ module OpenSSL
}
end
end
+
+ class CRL
+ def ==(other)
+ return false unless CRL === other
+ to_der == other.to_der
+ end
+ end
+
+ class Revoked
+ def ==(other)
+ return false unless Revoked === other
+ to_der == other.to_der
+ end
+ end
+
+ class Request
+ def ==(other)
+ return false unless Request === other
+ to_der == other.to_der
+ end
+ end
end
end
diff --git a/ext/openssl/openssl.gemspec b/ext/openssl/openssl.gemspec
index 48191fa0e9..e29cfcd709 100644
--- a/ext/openssl/openssl.gemspec
+++ b/ext/openssl/openssl.gemspec
@@ -1,45 +1,49 @@
# -*- encoding: utf-8 -*-
-# stub: openssl 2.0.0.beta.2 ruby lib
+# stub: openssl 2.1.2 ruby lib
# stub: ext/openssl/extconf.rb
Gem::Specification.new do |s|
s.name = "openssl".freeze
- s.version = "2.0.0.beta.2"
+ s.version = "2.1.2"
- s.required_rubygems_version = Gem::Requirement.new("> 1.3.1".freeze) if s.respond_to? :required_rubygems_version=
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
+ s.metadata = { "msys2_mingw_dependencies" => "openssl" } if s.respond_to? :metadata=
s.require_paths = ["lib".freeze]
s.authors = ["Martin Bosslet".freeze, "SHIBATA Hiroshi".freeze, "Zachary Scott".freeze, "Kazuki Yamaguchi".freeze]
- s.date = "2016-09-08"
+ s.date = "2018-10-17"
s.description = "It wraps the OpenSSL library.".freeze
s.email = ["ruby-core@ruby-lang.org".freeze]
s.extensions = ["ext/openssl/extconf.rb".freeze]
- s.extra_rdoc_files = ["CONTRIBUTING.md".freeze, "README.md".freeze, "History.md".freeze]
- s.files = ["BSDL".freeze, "CONTRIBUTING.md".freeze, "History.md".freeze, "LICENSE.txt".freeze, "README.md".freeze, "ext/openssl/deprecation.rb".freeze, "ext/openssl/extconf.rb".freeze, "ext/openssl/openssl_missing.c".freeze, "ext/openssl/openssl_missing.h".freeze, "ext/openssl/ossl.c".freeze, "ext/openssl/ossl.h".freeze, "ext/openssl/ossl_asn1.c".freeze, "ext/openssl/ossl_asn1.h".freeze, "ext/openssl/ossl_bio.c".freeze, "ext/openssl/ossl_bio.h".freeze, "ext/openssl/ossl_bn.c".freeze, "ext/openssl/ossl_bn.h".freeze, "ext/openssl/ossl_cipher.c".freeze, "ext/openssl/ossl_cipher.h".freeze, "ext/openssl/ossl_config.c".freeze, "ext/openssl/ossl_config.h".freeze, "ext/openssl/ossl_digest.c".freeze, "ext/openssl/ossl_digest.h".freeze, "ext/openssl/ossl_engine.c".freeze, "ext/openssl/ossl_engine.h".freeze, "ext/openssl/ossl_hmac.c".freeze, "ext/openssl/ossl_hmac.h".freeze, "ext/openssl/ossl_ns_spki.c".freeze, "ext/openssl/ossl_ns_spki.h".freeze, "ext/openssl/ossl_ocsp.c".freeze, "ext/openssl/ossl_ocsp.h".freeze, "ext/openssl/ossl_pkcs12.c".freeze, "ext/openssl/ossl_pkcs12.h".freeze, "ext/openssl/ossl_pkcs5.c".freeze, "ext/openssl/ossl_pkcs5.h".freeze, "ext/openssl/ossl_pkcs7.c".freeze, "ext/openssl/ossl_pkcs7.h".freeze, "ext/openssl/ossl_pkey.c".freeze, "ext/openssl/ossl_pkey.h".freeze, "ext/openssl/ossl_pkey_dh.c".freeze, "ext/openssl/ossl_pkey_dsa.c".freeze, "ext/openssl/ossl_pkey_ec.c".freeze, "ext/openssl/ossl_pkey_rsa.c".freeze, "ext/openssl/ossl_rand.c".freeze, "ext/openssl/ossl_rand.h".freeze, "ext/openssl/ossl_ssl.c".freeze, "ext/openssl/ossl_ssl.h".freeze, "ext/openssl/ossl_ssl_session.c".freeze, "ext/openssl/ossl_version.h".freeze, "ext/openssl/ossl_x509.c".freeze, "ext/openssl/ossl_x509.h".freeze, "ext/openssl/ossl_x509attr.c".freeze, "ext/openssl/ossl_x509cert.c".freeze, "ext/openssl/ossl_x509crl.c".freeze, "ext/openssl/ossl_x509ext.c".freeze, "ext/openssl/ossl_x509name.c".freeze, "ext/openssl/ossl_x509req.c".freeze, "ext/openssl/ossl_x509revoked.c".freeze, "ext/openssl/ossl_x509store.c".freeze, "ext/openssl/ruby_missing.h".freeze, "lib/openssl.rb".freeze, "lib/openssl/bn.rb".freeze, "lib/openssl/buffering.rb".freeze, "lib/openssl/cipher.rb".freeze, "lib/openssl/config.rb".freeze, "lib/openssl/digest.rb".freeze, "lib/openssl/pkey.rb".freeze, "lib/openssl/ssl.rb".freeze, "lib/openssl/x509.rb".freeze]
- s.homepage = "https://www.ruby-lang.org/".freeze
+ s.extra_rdoc_files = ["README.md".freeze, "CONTRIBUTING.md".freeze, "History.md".freeze]
+ s.files = ["BSDL".freeze, "CONTRIBUTING.md".freeze, "History.md".freeze, "LICENSE.txt".freeze, "README.md".freeze, "ext/openssl/deprecation.rb".freeze, "ext/openssl/extconf.rb".freeze, "ext/openssl/openssl_missing.c".freeze, "ext/openssl/openssl_missing.h".freeze, "ext/openssl/ossl.c".freeze, "ext/openssl/ossl.h".freeze, "ext/openssl/ossl_asn1.c".freeze, "ext/openssl/ossl_asn1.h".freeze, "ext/openssl/ossl_bio.c".freeze, "ext/openssl/ossl_bio.h".freeze, "ext/openssl/ossl_bn.c".freeze, "ext/openssl/ossl_bn.h".freeze, "ext/openssl/ossl_cipher.c".freeze, "ext/openssl/ossl_cipher.h".freeze, "ext/openssl/ossl_config.c".freeze, "ext/openssl/ossl_config.h".freeze, "ext/openssl/ossl_digest.c".freeze, "ext/openssl/ossl_digest.h".freeze, "ext/openssl/ossl_engine.c".freeze, "ext/openssl/ossl_engine.h".freeze, "ext/openssl/ossl_hmac.c".freeze, "ext/openssl/ossl_hmac.h".freeze, "ext/openssl/ossl_kdf.c".freeze, "ext/openssl/ossl_kdf.h".freeze, "ext/openssl/ossl_ns_spki.c".freeze, "ext/openssl/ossl_ns_spki.h".freeze, "ext/openssl/ossl_ocsp.c".freeze, "ext/openssl/ossl_ocsp.h".freeze, "ext/openssl/ossl_pkcs12.c".freeze, "ext/openssl/ossl_pkcs12.h".freeze, "ext/openssl/ossl_pkcs7.c".freeze, "ext/openssl/ossl_pkcs7.h".freeze, "ext/openssl/ossl_pkey.c".freeze, "ext/openssl/ossl_pkey.h".freeze, "ext/openssl/ossl_pkey_dh.c".freeze, "ext/openssl/ossl_pkey_dsa.c".freeze, "ext/openssl/ossl_pkey_ec.c".freeze, "ext/openssl/ossl_pkey_rsa.c".freeze, "ext/openssl/ossl_rand.c".freeze, "ext/openssl/ossl_rand.h".freeze, "ext/openssl/ossl_ssl.c".freeze, "ext/openssl/ossl_ssl.h".freeze, "ext/openssl/ossl_ssl_session.c".freeze, "ext/openssl/ossl_version.h".freeze, "ext/openssl/ossl_x509.c".freeze, "ext/openssl/ossl_x509.h".freeze, "ext/openssl/ossl_x509attr.c".freeze, "ext/openssl/ossl_x509cert.c".freeze, "ext/openssl/ossl_x509crl.c".freeze, "ext/openssl/ossl_x509ext.c".freeze, "ext/openssl/ossl_x509name.c".freeze, "ext/openssl/ossl_x509req.c".freeze, "ext/openssl/ossl_x509revoked.c".freeze, "ext/openssl/ossl_x509store.c".freeze, "ext/openssl/ruby_missing.h".freeze, "lib/openssl.rb".freeze, "lib/openssl/bn.rb".freeze, "lib/openssl/buffering.rb".freeze, "lib/openssl/cipher.rb".freeze, "lib/openssl/config.rb".freeze, "lib/openssl/digest.rb".freeze, "lib/openssl/pkcs5.rb".freeze, "lib/openssl/pkey.rb".freeze, "lib/openssl/ssl.rb".freeze, "lib/openssl/x509.rb".freeze]
+ s.homepage = "https://github.com/ruby/openssl".freeze
s.licenses = ["Ruby".freeze]
s.rdoc_options = ["--main".freeze, "README.md".freeze]
s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze)
- s.rubygems_version = "2.6.6".freeze
+ s.rubygems_version = "3.0.0.beta1".freeze
s.summary = "OpenSSL provides SSL, TLS and general purpose cryptography.".freeze
if s.respond_to? :specification_version then
s.specification_version = 4
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
- s.add_development_dependency(%q<rake>.freeze, ["~> 10.3"])
- s.add_development_dependency(%q<rake-compiler>.freeze, ["~> 0.9"])
+ s.add_runtime_dependency(%q<ipaddr>.freeze, [">= 0"])
+ s.add_development_dependency(%q<rake>.freeze, [">= 0"])
+ s.add_development_dependency(%q<rake-compiler>.freeze, [">= 0"])
s.add_development_dependency(%q<test-unit>.freeze, ["~> 3.0"])
- s.add_development_dependency(%q<rdoc>.freeze, ["~> 4.2"])
+ s.add_development_dependency(%q<rdoc>.freeze, [">= 0"])
else
- s.add_dependency(%q<rake>.freeze, ["~> 10.3"])
- s.add_dependency(%q<rake-compiler>.freeze, ["~> 0.9"])
+ s.add_dependency(%q<ipaddr>.freeze, [">= 0"])
+ s.add_dependency(%q<rake>.freeze, [">= 0"])
+ s.add_dependency(%q<rake-compiler>.freeze, [">= 0"])
s.add_dependency(%q<test-unit>.freeze, ["~> 3.0"])
- s.add_dependency(%q<rdoc>.freeze, ["~> 4.2"])
+ s.add_dependency(%q<rdoc>.freeze, [">= 0"])
end
else
- s.add_dependency(%q<rake>.freeze, ["~> 10.3"])
- s.add_dependency(%q<rake-compiler>.freeze, ["~> 0.9"])
+ s.add_dependency(%q<ipaddr>.freeze, [">= 0"])
+ s.add_dependency(%q<rake>.freeze, [">= 0"])
+ s.add_dependency(%q<rake-compiler>.freeze, [">= 0"])
s.add_dependency(%q<test-unit>.freeze, ["~> 3.0"])
- s.add_dependency(%q<rdoc>.freeze, ["~> 4.2"])
+ s.add_dependency(%q<rdoc>.freeze, [">= 0"])
end
end
diff --git a/ext/openssl/openssl_missing.c b/ext/openssl/openssl_missing.c
index cc13b7a3a5..b36ef0288e 100644
--- a/ext/openssl/openssl_missing.c
+++ b/ext/openssl/openssl_missing.c
@@ -20,73 +20,6 @@
#include "openssl_missing.h"
-/* added in 0.9.8X */
-#if !defined(HAVE_EVP_CIPHER_CTX_NEW)
-EVP_CIPHER_CTX *
-EVP_CIPHER_CTX_new(void)
-{
- EVP_CIPHER_CTX *ctx = OPENSSL_malloc(sizeof(EVP_CIPHER_CTX));
- if (!ctx)
- return NULL;
- EVP_CIPHER_CTX_init(ctx);
- return ctx;
-}
-#endif
-
-#if !defined(HAVE_EVP_CIPHER_CTX_FREE)
-void
-EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx)
-{
- if (ctx) {
- EVP_CIPHER_CTX_cleanup(ctx);
- OPENSSL_free(ctx);
- }
-}
-#endif
-
-/* added in 1.0.0 */
-#if !defined(HAVE_EVP_CIPHER_CTX_COPY)
-/*
- * this function does not exist in OpenSSL yet... or ever?.
- * a future version may break this function.
- * tested on 0.9.7d.
- */
-int
-EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in)
-{
- memcpy(out, in, sizeof(EVP_CIPHER_CTX));
-
-#if !defined(OPENSSL_NO_ENGINE)
- if (in->engine) ENGINE_add(out->engine);
- if (in->cipher_data) {
- out->cipher_data = OPENSSL_malloc(in->cipher->ctx_size);
- memcpy(out->cipher_data, in->cipher_data, in->cipher->ctx_size);
- }
-#endif
-
- return 1;
-}
-#endif
-
-#if !defined(OPENSSL_NO_HMAC)
-#if !defined(HAVE_HMAC_CTX_COPY)
-int
-HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in)
-{
- if (!out || !in)
- return 0;
-
- memcpy(out, in, sizeof(HMAC_CTX));
-
- EVP_MD_CTX_copy(&out->md_ctx, &in->md_ctx);
- EVP_MD_CTX_copy(&out->i_ctx, &in->i_ctx);
- EVP_MD_CTX_copy(&out->o_ctx, &in->o_ctx);
-
- return 1;
-}
-#endif /* HAVE_HMAC_CTX_COPY */
-#endif /* NO_HMAC */
-
/* added in 1.0.2 */
#if !defined(OPENSSL_NO_EC)
#if !defined(HAVE_EC_CURVE_NIST2NID)
@@ -112,7 +45,7 @@ static struct {
};
int
-EC_curve_nist2nid(const char *name)
+ossl_EC_curve_nist2nid(const char *name)
{
size_t i;
for (i = 0; i < (sizeof(nist_curves) / sizeof(nist_curves[0])); i++) {
@@ -127,7 +60,7 @@ EC_curve_nist2nid(const char *name)
/*** added in 1.1.0 ***/
#if !defined(HAVE_HMAC_CTX_NEW)
HMAC_CTX *
-HMAC_CTX_new(void)
+ossl_HMAC_CTX_new(void)
{
HMAC_CTX *ctx = OPENSSL_malloc(sizeof(HMAC_CTX));
if (!ctx)
@@ -139,7 +72,7 @@ HMAC_CTX_new(void)
#if !defined(HAVE_HMAC_CTX_FREE)
void
-HMAC_CTX_free(HMAC_CTX *ctx)
+ossl_HMAC_CTX_free(HMAC_CTX *ctx)
{
if (ctx) {
HMAC_CTX_cleanup(ctx);
@@ -150,8 +83,8 @@ HMAC_CTX_free(HMAC_CTX *ctx)
#if !defined(HAVE_X509_CRL_GET0_SIGNATURE)
void
-X509_CRL_get0_signature(const X509_CRL *crl, const ASN1_BIT_STRING **psig,
- const X509_ALGOR **palg)
+ossl_X509_CRL_get0_signature(const X509_CRL *crl, const ASN1_BIT_STRING **psig,
+ const X509_ALGOR **palg)
{
if (psig != NULL)
*psig = crl->signature;
@@ -162,8 +95,8 @@ X509_CRL_get0_signature(const X509_CRL *crl, const ASN1_BIT_STRING **psig,
#if !defined(HAVE_X509_REQ_GET0_SIGNATURE)
void
-X509_REQ_get0_signature(const X509_REQ *req, const ASN1_BIT_STRING **psig,
- const X509_ALGOR **palg)
+ossl_X509_REQ_get0_signature(const X509_REQ *req, const ASN1_BIT_STRING **psig,
+ const X509_ALGOR **palg)
{
if (psig != NULL)
*psig = req->signature;
diff --git a/ext/openssl/openssl_missing.h b/ext/openssl/openssl_missing.h
index 897d6235b0..09998214e1 100644
--- a/ext/openssl/openssl_missing.h
+++ b/ext/openssl/openssl_missing.h
@@ -12,45 +12,11 @@
#include "ruby/config.h"
-/* added in 0.9.8X */
-#if !defined(HAVE_EVP_CIPHER_CTX_NEW)
-EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void);
-#endif
-
-#if !defined(HAVE_EVP_CIPHER_CTX_FREE)
-void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx);
-#endif
-
-/* added in 1.0.0 */
-#if !defined(HAVE_EVP_PKEY_BASE_ID)
-# define EVP_PKEY_base_id(pkey) EVP_PKEY_type((pkey)->type)
-#endif
-
-#if !defined(HAVE_EVP_CIPHER_CTX_COPY)
-int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in);
-#endif
-
-#if !defined(HAVE_HMAC_CTX_COPY)
-int HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in);
-#endif
-
-#if !defined(HAVE_X509_STORE_CTX_GET0_CURRENT_CRL)
-# define X509_STORE_CTX_get0_current_crl(x) ((x)->current_crl)
-#endif
-
-#if !defined(HAVE_X509_STORE_SET_VERIFY_CB)
-# define X509_STORE_set_verify_cb X509_STORE_set_verify_cb_func
-#endif
-
-#if !defined(HAVE_I2D_ASN1_SET_ANY)
-# define i2d_ASN1_SET_ANY(sk, x) i2d_ASN1_SET_OF_ASN1_TYPE((sk), (x), \
- i2d_ASN1_TYPE, V_ASN1_SET, V_ASN1_UNIVERSAL, 0)
-#endif
-
/* added in 1.0.2 */
#if !defined(OPENSSL_NO_EC)
#if !defined(HAVE_EC_CURVE_NIST2NID)
-int EC_curve_nist2nid(const char *);
+int ossl_EC_curve_nist2nid(const char *);
+# define EC_curve_nist2nid ossl_EC_curve_nist2nid
#endif
#endif
@@ -89,11 +55,13 @@ int EC_curve_nist2nid(const char *);
#endif
#if !defined(HAVE_HMAC_CTX_NEW)
-HMAC_CTX *HMAC_CTX_new(void);
+HMAC_CTX *ossl_HMAC_CTX_new(void);
+# define HMAC_CTX_new ossl_HMAC_CTX_new
#endif
#if !defined(HAVE_HMAC_CTX_FREE)
-void HMAC_CTX_free(HMAC_CTX *ctx);
+void ossl_HMAC_CTX_free(HMAC_CTX *);
+# define HMAC_CTX_free ossl_HMAC_CTX_free
#endif
#if !defined(HAVE_X509_STORE_GET_EX_DATA)
@@ -110,11 +78,13 @@ void HMAC_CTX_free(HMAC_CTX *ctx);
#endif
#if !defined(HAVE_X509_CRL_GET0_SIGNATURE)
-void X509_CRL_get0_signature(const X509_CRL *, const ASN1_BIT_STRING **, const X509_ALGOR **);
+void ossl_X509_CRL_get0_signature(const X509_CRL *, const ASN1_BIT_STRING **, const X509_ALGOR **);
+# define X509_CRL_get0_signature ossl_X509_CRL_get0_signature
#endif
#if !defined(HAVE_X509_REQ_GET0_SIGNATURE)
-void X509_REQ_get0_signature(const X509_REQ *, const ASN1_BIT_STRING **, const X509_ALGOR **);
+void ossl_X509_REQ_get0_signature(const X509_REQ *, const ASN1_BIT_STRING **, const X509_ALGOR **);
+# define X509_REQ_get0_signature ossl_X509_REQ_get0_signature
#endif
#if !defined(HAVE_X509_REVOKED_GET0_SERIALNUMBER)
@@ -179,7 +149,7 @@ void X509_REQ_get0_signature(const X509_REQ *, const ASN1_BIT_STRING **, const X
static inline _type *EVP_PKEY_get0_##_type(EVP_PKEY *pkey) { \
return pkey->pkey._name; }
#define IMPL_KEY_ACCESSOR2(_type, _group, a1, a2, _fail_cond) \
-static inline void _type##_get0_##_group(_type *obj, const BIGNUM **a1, const BIGNUM **a2) { \
+static inline void _type##_get0_##_group(const _type *obj, const BIGNUM **a1, const BIGNUM **a2) { \
if (a1) *a1 = obj->a1; \
if (a2) *a2 = obj->a2; } \
static inline int _type##_set0_##_group(_type *obj, BIGNUM *a1, BIGNUM *a2) { \
@@ -188,7 +158,7 @@ static inline int _type##_set0_##_group(_type *obj, BIGNUM *a1, BIGNUM *a2) { \
BN_clear_free(obj->a2); obj->a2 = a2; \
return 1; }
#define IMPL_KEY_ACCESSOR3(_type, _group, a1, a2, a3, _fail_cond) \
-static inline void _type##_get0_##_group(_type *obj, const BIGNUM **a1, const BIGNUM **a2, const BIGNUM **a3) { \
+static inline void _type##_get0_##_group(const _type *obj, const BIGNUM **a1, const BIGNUM **a2, const BIGNUM **a3) { \
if (a1) *a1 = obj->a1; \
if (a2) *a2 = obj->a2; \
if (a3) *a3 = obj->a3; } \
@@ -215,7 +185,7 @@ IMPL_KEY_ACCESSOR3(DSA, pqg, p, q, g, (p == obj->p || q == obj->q || g == obj->g
#if !defined(OPENSSL_NO_DH)
IMPL_PKEY_GETTER(DH, dh)
IMPL_KEY_ACCESSOR2(DH, key, pub_key, priv_key, (pub_key == obj->pub_key || (obj->priv_key && priv_key == obj->priv_key)))
-IMPL_KEY_ACCESSOR3(DH, pqg, p, q, g, (p == obj->p || obj->q && q == obj->q || g == obj->g))
+IMPL_KEY_ACCESSOR3(DH, pqg, p, q, g, (p == obj->p || (obj->q && q == obj->q) || g == obj->g))
static inline ENGINE *DH_get0_engine(DH *dh) { return dh->engine; }
#endif
@@ -228,7 +198,7 @@ IMPL_PKEY_GETTER(EC_KEY, ec)
#undef IMPL_KEY_ACCESSOR3
#endif /* HAVE_OPAQUE_OPENSSL */
-#if defined(HAVE_AUTHENTICATED_ENCRYPTION) && !defined(EVP_CTRL_AEAD_GET_TAG)
+#if !defined(EVP_CTRL_AEAD_GET_TAG)
# define EVP_CTRL_AEAD_GET_TAG EVP_CTRL_GCM_GET_TAG
# define EVP_CTRL_AEAD_SET_TAG EVP_CTRL_GCM_SET_TAG
# define EVP_CTRL_AEAD_SET_IVLEN EVP_CTRL_GCM_SET_IVLEN
@@ -239,6 +209,14 @@ IMPL_PKEY_GETTER(EC_KEY, ec)
# define X509_get0_notAfter(x) X509_get_notAfter(x)
# define X509_CRL_get0_lastUpdate(x) X509_CRL_get_lastUpdate(x)
# define X509_CRL_get0_nextUpdate(x) X509_CRL_get_nextUpdate(x)
+# define X509_set1_notBefore(x, t) X509_set_notBefore(x, t)
+# define X509_set1_notAfter(x, t) X509_set_notAfter(x, t)
+# define X509_CRL_set1_lastUpdate(x, t) X509_CRL_set_lastUpdate(x, t)
+# define X509_CRL_set1_nextUpdate(x, t) X509_CRL_set_nextUpdate(x, t)
+#endif
+
+#if !defined(HAVE_SSL_SESSION_GET_PROTOCOL_VERSION)
+# define SSL_SESSION_get_protocol_version(s) ((s)->ssl_version)
#endif
#endif /* _OSSL_OPENSSL_MISSING_H_ */
diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c
index a9000f25a3..69758aed7a 100644
--- a/ext/openssl/ossl.c
+++ b/ext/openssl/ossl.c
@@ -92,22 +92,40 @@ OSSL_IMPL_SK2ARY(x509crl, X509_CRL)
OSSL_IMPL_SK2ARY(x509name, X509_NAME)
static VALUE
-ossl_str_new(int size)
+ossl_str_new_i(VALUE size)
{
- return rb_str_new(0, size);
+ return rb_str_new(NULL, (long)size);
+}
+
+VALUE
+ossl_str_new(const char *ptr, long len, int *pstate)
+{
+ VALUE str;
+ int state;
+
+ str = rb_protect(ossl_str_new_i, len, &state);
+ if (pstate)
+ *pstate = state;
+ if (state) {
+ if (!pstate)
+ rb_set_errinfo(Qnil);
+ return Qnil;
+ }
+ if (ptr)
+ memcpy(RSTRING_PTR(str), ptr, len);
+ return str;
}
VALUE
ossl_buf2str(char *buf, int len)
{
VALUE str;
- int status = 0;
+ int state;
- str = rb_protect((VALUE (*)(VALUE))ossl_str_new, len, &status);
- if(!NIL_P(str)) memcpy(RSTRING_PTR(str), buf, len);
+ str = ossl_str_new(buf, len, &state);
OPENSSL_free(buf);
- if(status) rb_jump_tag(status);
-
+ if (state)
+ rb_jump_tag(state);
return str;
}
@@ -129,13 +147,6 @@ ossl_bin2hex(unsigned char *in, char *out, size_t inlen)
/*
* our default PEM callback
*/
-
-/*
- * OpenSSL requires passwords for PEM-encoded files to be at least four
- * characters long. See crypto/pem/pem_lib.c (as of 1.0.2h)
- */
-#define OSSL_MIN_PWD_LEN 4
-
VALUE
ossl_pem_passwd_value(VALUE pass)
{
@@ -144,12 +155,10 @@ ossl_pem_passwd_value(VALUE pass)
StringValue(pass);
- if (RSTRING_LEN(pass) < OSSL_MIN_PWD_LEN)
- ossl_raise(eOSSLError, "password must be at least %d bytes", OSSL_MIN_PWD_LEN);
/* PEM_BUFSIZE is currently used as the second argument of pem_password_cb,
* that is +max_len+ of ossl_pem_passwd_cb() */
if (RSTRING_LEN(pass) > PEM_BUFSIZE)
- ossl_raise(eOSSLError, "password must be shorter than %d bytes", PEM_BUFSIZE);
+ ossl_raise(eOSSLError, "password must not be longer than %d bytes", PEM_BUFSIZE);
return pass;
}
@@ -157,18 +166,18 @@ ossl_pem_passwd_value(VALUE pass)
static VALUE
ossl_pem_passwd_cb0(VALUE flag)
{
- VALUE pass;
-
- pass = rb_yield(flag);
- SafeStringValue(pass);
-
+ VALUE pass = rb_yield(flag);
+ if (NIL_P(pass))
+ return Qnil;
+ StringValue(pass);
return pass;
}
int
ossl_pem_passwd_cb(char *buf, int max_len, int flag, void *pwd_)
{
- int len, status;
+ long len;
+ int status;
VALUE rflag, pass = (VALUE)pwd_;
if (RTEST(pass)) {
@@ -176,10 +185,10 @@ ossl_pem_passwd_cb(char *buf, int max_len, int flag, void *pwd_)
* work because it does not allow NUL characters and truncates to 1024
* bytes silently if the input is over 1024 bytes */
if (RB_TYPE_P(pass, T_STRING)) {
- len = RSTRING_LENINT(pass);
- if (len >= OSSL_MIN_PWD_LEN && len <= max_len) {
+ len = RSTRING_LEN(pass);
+ if (len <= max_len) {
memcpy(buf, RSTRING_PTR(pass), len);
- return len;
+ return (int)len;
}
}
OSSL_Debug("passed data is not valid String???");
@@ -203,78 +212,17 @@ ossl_pem_passwd_cb(char *buf, int max_len, int flag, void *pwd_)
rb_set_errinfo(Qnil);
return -1;
}
- len = RSTRING_LENINT(pass);
- if (len < OSSL_MIN_PWD_LEN) {
- rb_warning("password must be at least %d bytes", OSSL_MIN_PWD_LEN);
- continue;
- }
+ if (NIL_P(pass))
+ return -1;
+ len = RSTRING_LEN(pass);
if (len > max_len) {
- rb_warning("password must be shorter than %d bytes", max_len);
+ rb_warning("password must not be longer than %d bytes", max_len);
continue;
}
memcpy(buf, RSTRING_PTR(pass), len);
break;
}
- return len;
-}
-
-/*
- * Verify callback
- */
-int ossl_store_ctx_ex_verify_cb_idx;
-int ossl_store_ex_verify_cb_idx;
-
-struct ossl_verify_cb_args {
- VALUE proc;
- VALUE preverify_ok;
- VALUE store_ctx;
-};
-
-static VALUE
-ossl_call_verify_cb_proc(struct ossl_verify_cb_args *args)
-{
- return rb_funcall(args->proc, rb_intern("call"), 2,
- args->preverify_ok, args->store_ctx);
-}
-
-int
-ossl_verify_cb_call(VALUE proc, int ok, X509_STORE_CTX *ctx)
-{
- VALUE rctx, ret;
- struct ossl_verify_cb_args args;
- int state;
-
- if (NIL_P(proc))
- return ok;
-
- ret = Qfalse;
- rctx = rb_protect((VALUE(*)(VALUE))ossl_x509stctx_new, (VALUE)ctx, &state);
- if (state) {
- rb_set_errinfo(Qnil);
- rb_warn("StoreContext initialization failure");
- }
- else {
- args.proc = proc;
- args.preverify_ok = ok ? Qtrue : Qfalse;
- args.store_ctx = rctx;
- ret = rb_protect((VALUE(*)(VALUE))ossl_call_verify_cb_proc, (VALUE)&args, &state);
- if (state) {
- rb_set_errinfo(Qnil);
- rb_warn("exception in verify_callback is ignored");
- }
- ossl_x509stctx_clear_ptr(rctx);
- }
- if (ret == Qtrue) {
- X509_STORE_CTX_set_error(ctx, X509_V_OK);
- ok = 1;
- }
- else {
- if (X509_STORE_CTX_get_error(ctx) == X509_V_OK)
- X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED);
- ok = 0;
- }
-
- return ok;
+ return (int)len;
}
/*
@@ -290,7 +238,7 @@ VALUE eOSSLError;
/*
* Convert to DER string
*/
-ID ossl_s_to_der;
+static ID ossl_s_to_der;
VALUE
ossl_to_der(VALUE obj)
@@ -318,18 +266,15 @@ static VALUE
ossl_make_error(VALUE exc, const char *fmt, va_list args)
{
VALUE str = Qnil;
- const char *msg;
- long e;
+ unsigned long e;
- e = ERR_peek_last_error();
if (fmt) {
str = rb_vsprintf(fmt, args);
}
+ e = ERR_peek_last_error();
if (e) {
- if (dOSSL == Qtrue) /* FULL INFO */
- msg = ERR_error_string(e, NULL);
- else
- msg = ERR_reason_error_string(e);
+ const char *msg = ERR_reason_error_string(e);
+
if (NIL_P(str)) {
if (msg) str = rb_str_new_cstr(msg);
}
@@ -337,8 +282,8 @@ ossl_make_error(VALUE exc, const char *fmt, va_list args)
if (RSTRING_LEN(str)) rb_str_cat2(str, ": ");
rb_str_cat2(str, msg ? msg : "(null)");
}
+ ossl_clear_error();
}
- ossl_clear_error();
if (NIL_P(str)) str = rb_str_new(0, 0);
return rb_exc_new3(exc, str);
@@ -355,27 +300,32 @@ ossl_raise(VALUE exc, const char *fmt, ...)
rb_exc_raise(err);
}
-VALUE
-ossl_exc_new(VALUE exc, const char *fmt, ...)
-{
- va_list args;
- VALUE err;
- va_start(args, fmt);
- err = ossl_make_error(exc, fmt, args);
- va_end(args);
- return err;
-}
-
void
ossl_clear_error(void)
{
if (dOSSL == Qtrue) {
- long e;
- while ((e = ERR_get_error())) {
- rb_warn("error on stack: %s", ERR_error_string(e, NULL));
+ unsigned long e;
+ const char *file, *data, *errstr;
+ int line, flags;
+
+ while ((e = ERR_get_error_line_data(&file, &line, &data, &flags))) {
+ errstr = ERR_error_string(e, NULL);
+ if (!errstr)
+ errstr = "(null)";
+
+ if (flags & ERR_TXT_STRING) {
+ if (!data)
+ data = "(null)";
+ rb_warn("error on stack: %s (%s)", errstr, data);
+ }
+ else {
+ rb_warn("error on stack: %s", errstr);
+ }
}
}
- ERR_clear_error();
+ else {
+ ERR_clear_error();
+ }
}
/*
@@ -384,7 +334,8 @@ ossl_clear_error(void)
*
* See any remaining errors held in queue.
*
- * Any errors you see here are probably due to a bug in ruby's OpenSSL implementation.
+ * Any errors you see here are probably due to a bug in Ruby's OpenSSL
+ * implementation.
*/
VALUE
ossl_get_errors(void)
@@ -448,6 +399,23 @@ ossl_debug_set(VALUE self, VALUE val)
/*
* call-seq:
+ * OpenSSL.fips_mode -> true | false
+ */
+static VALUE
+ossl_fips_mode_get(VALUE self)
+{
+
+#ifdef OPENSSL_FIPS
+ VALUE enabled;
+ enabled = FIPS_mode() ? Qtrue : Qfalse;
+ return enabled;
+#else
+ return Qfalse;
+#endif
+}
+
+/*
+ * call-seq:
* OpenSSL.fips_mode = boolean -> boolean
*
* Turns FIPS mode on or off. Turning on FIPS mode will obviously only have an
@@ -479,44 +447,123 @@ ossl_fips_mode_set(VALUE self, VALUE enabled)
#endif
}
+#if defined(OSSL_DEBUG)
+#if !defined(LIBRESSL_VERSION_NUMBER) && \
+ (OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(OPENSSL_NO_CRYPTO_MDEBUG) || \
+ defined(CRYPTO_malloc_debug_init))
+/*
+ * call-seq:
+ * OpenSSL.mem_check_start -> nil
+ *
+ * Calls CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON). Starts tracking memory
+ * allocations. See also OpenSSL.print_mem_leaks.
+ *
+ * This is available only when built with a capable OpenSSL and --enable-debug
+ * configure option.
+ */
+static VALUE
+mem_check_start(VALUE self)
+{
+ CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * OpenSSL.print_mem_leaks -> true | false
+ *
+ * For debugging the Ruby/OpenSSL library. Calls CRYPTO_mem_leaks_fp(stderr).
+ * Prints detected memory leaks to standard error. This cleans the global state
+ * up thus you cannot use any methods of the library after calling this.
+ *
+ * Returns +true+ if leaks detected, +false+ otherwise.
+ *
+ * This is available only when built with a capable OpenSSL and --enable-debug
+ * configure option.
+ *
+ * === Example
+ * OpenSSL.mem_check_start
+ * NOT_GCED = OpenSSL::PKey::RSA.new(256)
+ *
+ * END {
+ * GC.start
+ * OpenSSL.print_mem_leaks # will print the leakage
+ * }
+ */
+static VALUE
+print_mem_leaks(VALUE self)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+ int ret;
+#endif
+
+ BN_CTX_free(ossl_bn_ctx);
+ ossl_bn_ctx = NULL;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+ ret = CRYPTO_mem_leaks_fp(stderr);
+ if (ret < 0)
+ ossl_raise(eOSSLError, "CRYPTO_mem_leaks_fp");
+ return ret ? Qfalse : Qtrue;
+#else
+ CRYPTO_mem_leaks_fp(stderr);
+ return Qnil;
+#endif
+}
+#endif
+#endif
+
#if !defined(HAVE_OPENSSL_110_THREADING_API)
/**
* Stores locks needed for OpenSSL thread safety
*/
-static rb_nativethread_lock_t *ossl_locks;
+struct CRYPTO_dynlock_value {
+ rb_nativethread_lock_t lock;
+ rb_nativethread_id_t owner;
+ size_t count;
+};
static void
-ossl_lock_unlock(int mode, rb_nativethread_lock_t *lock)
+ossl_lock_init(struct CRYPTO_dynlock_value *l)
{
- if (mode & CRYPTO_LOCK) {
- rb_nativethread_lock_lock(lock);
- } else {
- rb_nativethread_lock_unlock(lock);
- }
+ rb_nativethread_lock_initialize(&l->lock);
+ l->count = 0;
}
static void
-ossl_lock_callback(int mode, int type, const char *file, int line)
+ossl_lock_unlock(int mode, struct CRYPTO_dynlock_value *l)
{
- ossl_lock_unlock(mode, &ossl_locks[type]);
+ if (mode & CRYPTO_LOCK) {
+ /* TODO: rb_nativethread_id_t is not necessarily compared with ==. */
+ rb_nativethread_id_t tid = rb_nativethread_self();
+ if (l->count && l->owner == tid) {
+ l->count++;
+ return;
+ }
+ rb_nativethread_lock_lock(&l->lock);
+ l->owner = tid;
+ l->count = 1;
+ } else {
+ if (!--l->count)
+ rb_nativethread_lock_unlock(&l->lock);
+ }
}
-struct CRYPTO_dynlock_value {
- rb_nativethread_lock_t lock;
-};
-
static struct CRYPTO_dynlock_value *
ossl_dyn_create_callback(const char *file, int line)
{
- struct CRYPTO_dynlock_value *dynlock = (struct CRYPTO_dynlock_value *)OPENSSL_malloc((int)sizeof(struct CRYPTO_dynlock_value));
- rb_nativethread_lock_initialize(&dynlock->lock);
+ /* Do not use xmalloc() here, since it may raise NoMemoryError */
+ struct CRYPTO_dynlock_value *dynlock =
+ OPENSSL_malloc(sizeof(struct CRYPTO_dynlock_value));
+ if (dynlock)
+ ossl_lock_init(dynlock);
return dynlock;
}
static void
ossl_dyn_lock_callback(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line)
{
- ossl_lock_unlock(mode, &l->lock);
+ ossl_lock_unlock(mode, l);
}
static void
@@ -526,41 +573,30 @@ ossl_dyn_destroy_callback(struct CRYPTO_dynlock_value *l, const char *file, int
OPENSSL_free(l);
}
-#ifdef HAVE_CRYPTO_THREADID_PTR
static void ossl_threadid_func(CRYPTO_THREADID *id)
{
/* register native thread id */
CRYPTO_THREADID_set_pointer(id, (void *)rb_nativethread_self());
}
-#else
-static unsigned long ossl_thread_id(void)
+
+static struct CRYPTO_dynlock_value *ossl_locks;
+
+static void
+ossl_lock_callback(int mode, int type, const char *file, int line)
{
- /* before OpenSSL 1.0, this is 'unsigned long' */
- return (unsigned long)rb_nativethread_self();
+ ossl_lock_unlock(mode, &ossl_locks[type]);
}
-#endif
static void Init_ossl_locks(void)
{
int i;
int num_locks = CRYPTO_num_locks();
- if ((unsigned)num_locks >= INT_MAX / (int)sizeof(VALUE)) {
- rb_raise(rb_eRuntimeError, "CRYPTO_num_locks() is too big: %d", num_locks);
- }
- ossl_locks = (rb_nativethread_lock_t *) OPENSSL_malloc(num_locks * (int)sizeof(rb_nativethread_lock_t));
- if (!ossl_locks) {
- rb_raise(rb_eNoMemError, "CRYPTO_num_locks() is too big: %d", num_locks);
- }
- for (i = 0; i < num_locks; i++) {
- rb_nativethread_lock_initialize(&ossl_locks[i]);
- }
+ ossl_locks = ALLOC_N(struct CRYPTO_dynlock_value, num_locks);
+ for (i = 0; i < num_locks; i++)
+ ossl_lock_init(&ossl_locks[i]);
-#ifdef HAVE_CRYPTO_THREADID_PTR
CRYPTO_THREADID_set_callback(ossl_threadid_func);
-#else
- CRYPTO_set_id_callback(ossl_thread_id);
-#endif
CRYPTO_set_locking_callback(ossl_lock_callback);
CRYPTO_set_dynlock_create_callback(ossl_dyn_create_callback);
CRYPTO_set_dynlock_lock_callback(ossl_dyn_lock_callback);
@@ -570,7 +606,7 @@ static void Init_ossl_locks(void)
/*
* OpenSSL provides SSL, TLS and general purpose cryptography. It wraps the
- * OpenSSL[http://www.openssl.org/] library.
+ * OpenSSL[https://www.openssl.org/] library.
*
* = Examples
*
@@ -1063,6 +1099,7 @@ static void Init_ossl_locks(void)
void
Init_openssl(void)
{
+#undef rb_intern
/*
* Init timezone info
*/
@@ -1073,25 +1110,14 @@ Init_openssl(void)
/*
* Init all digests, ciphers
*/
- /* CRYPTO_malloc_init(); */
- /* ENGINE_load_builtin_engines(); */
+#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000
+ if (!OPENSSL_init_ssl(0, NULL))
+ rb_raise(rb_eRuntimeError, "OPENSSL_init_ssl");
+#else
OpenSSL_add_ssl_algorithms();
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
SSL_load_error_strings();
-
- /*
- * FIXME:
- * On unload do:
- */
-#if 0
- CONF_modules_unload(1);
- destroy_ui_method();
- EVP_cleanup();
- ENGINE_cleanup();
- CRYPTO_cleanup_all_ex_data();
- ERR_remove_state(0);
- ERR_free_strings();
#endif
/*
@@ -1113,7 +1139,11 @@ Init_openssl(void)
/*
* Version of OpenSSL the ruby OpenSSL extension is running with
*/
+#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000
+ rb_define_const(mOSSL, "OPENSSL_LIBRARY_VERSION", rb_str_new2(OpenSSL_version(OPENSSL_VERSION)));
+#else
rb_define_const(mOSSL, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION)));
+#endif
/*
* Version number of OpenSSL the ruby OpenSSL extension was built with
@@ -1122,7 +1152,7 @@ Init_openssl(void)
rb_define_const(mOSSL, "OPENSSL_VERSION_NUMBER", INT2NUM(OPENSSL_VERSION_NUMBER));
/*
- * Boolean indicating whether OpenSSL is FIPS-enabled or not
+ * Boolean indicating whether OpenSSL is FIPS-capable or not
*/
rb_define_const(mOSSL, "OPENSSL_FIPS",
#ifdef OPENSSL_FIPS
@@ -1132,6 +1162,7 @@ Init_openssl(void)
#endif
);
+ rb_define_module_function(mOSSL, "fips_mode", ossl_fips_mode_get, 0);
rb_define_module_function(mOSSL, "fips_mode=", ossl_fips_mode_set, 1);
/*
@@ -1152,14 +1183,6 @@ Init_openssl(void)
rb_define_module_function(mOSSL, "errors", ossl_get_errors, 0);
/*
- * Verify callback Proc index for ext-data
- */
- if ((ossl_store_ctx_ex_verify_cb_idx = X509_STORE_CTX_get_ex_new_index(0, (void *)"ossl_store_ctx_ex_verify_cb_idx", 0, 0, 0)) < 0)
- ossl_raise(eOSSLError, "X509_STORE_CTX_get_ex_new_index");
- if ((ossl_store_ex_verify_cb_idx = X509_STORE_get_ex_new_index(0, (void *)"ossl_store_ex_verify_cb_idx", 0, 0, 0)) < 0)
- ossl_raise(eOSSLError, "X509_STORE_get_ex_new_index");
-
- /*
* Get ID of to_der
*/
ossl_s_to_der = rb_intern("to_der");
@@ -1179,7 +1202,6 @@ Init_openssl(void)
Init_ossl_ns_spki();
Init_ossl_pkcs12();
Init_ossl_pkcs7();
- Init_ossl_pkcs5();
Init_ossl_pkey();
Init_ossl_rand();
Init_ossl_ssl();
@@ -1187,15 +1209,41 @@ Init_openssl(void)
Init_ossl_ocsp();
Init_ossl_engine();
Init_ossl_asn1();
-}
+ Init_ossl_kdf();
#if defined(OSSL_DEBUG)
-/*
- * Check if all symbols are OK with 'make LDSHARED=gcc all'
- */
-int
-main(int argc, char *argv[])
-{
- return 0;
+ /*
+ * For debugging Ruby/OpenSSL. Enable only when built with --enable-debug
+ */
+#if !defined(LIBRESSL_VERSION_NUMBER) && \
+ (OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(OPENSSL_NO_CRYPTO_MDEBUG) || \
+ defined(CRYPTO_malloc_debug_init))
+ rb_define_module_function(mOSSL, "mem_check_start", mem_check_start, 0);
+ rb_define_module_function(mOSSL, "print_mem_leaks", print_mem_leaks, 0);
+
+#if defined(CRYPTO_malloc_debug_init) /* <= 1.0.2 */
+ CRYPTO_malloc_debug_init();
+#endif
+
+#if defined(V_CRYPTO_MDEBUG_ALL) /* <= 1.0.2 */
+ CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL);
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000 /* <= 1.0.2 */
+ {
+ int i;
+ /*
+ * See crypto/ex_data.c; call def_get_class() immediately to avoid
+ * allocations. 15 is the maximum number that is used as the class index
+ * in OpenSSL 1.0.2.
+ */
+ for (i = 0; i <= 15; i++) {
+ if (CRYPTO_get_ex_new_index(i, 0, (void *)"ossl-mdebug-dummy", 0, 0, 0) < 0)
+ rb_raise(rb_eRuntimeError, "CRYPTO_get_ex_new_index for "
+ "class index %d failed", i);
+ }
+ }
+#endif
+#endif
+#endif
}
-#endif /* OSSL_DEBUG */
diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h
index 864d068342..39699bd5e6 100644
--- a/ext/openssl/ossl.h
+++ b/ext/openssl/ossl.h
@@ -12,37 +12,12 @@
#include RUBY_EXTCONF_H
-#if 0
- mOSSL = rb_define_module("OpenSSL");
- mX509 = rb_define_module_under(mOSSL, "X509");
-#endif
-
-/*
-* OpenSSL has defined RFILE and Ruby has defined RFILE - so undef it!
-*/
-#if defined(RFILE) /*&& !defined(OSSL_DEBUG)*/
-# undef RFILE
-#endif
+#include <assert.h>
#include <ruby.h>
+#include <errno.h>
#include <ruby/io.h>
#include <ruby/thread.h>
-
#include <openssl/opensslv.h>
-
-#ifdef HAVE_ASSERT_H
-# include <assert.h>
-#else
-# define assert(condition)
-#endif
-
-#if defined(_WIN32) && !defined(LIBRESSL_VERSION_NUMBER)
-# include <openssl/e_os2.h>
-# if !defined(OPENSSL_SYS_WIN32)
-# define OPENSSL_SYS_WIN32 1
-# endif
-# include <winsock2.h>
-#endif
-#include <errno.h>
#include <openssl/err.h>
#include <openssl/asn1.h>
#include <openssl/x509v3.h>
@@ -53,15 +28,18 @@
#include <openssl/rand.h>
#include <openssl/conf.h>
#include <openssl/conf_api.h>
-#if !defined(_WIN32)
-# include <openssl/crypto.h>
-#endif
+#include <openssl/crypto.h>
#if !defined(OPENSSL_NO_ENGINE)
# include <openssl/engine.h>
#endif
#if !defined(OPENSSL_NO_OCSP)
# include <openssl/ocsp.h>
#endif
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
+#include <openssl/evp.h>
+#include <openssl/dh.h>
/*
* Common Module
@@ -83,29 +61,29 @@ extern VALUE eOSSLError;
}\
} while (0)
-#define OSSL_Check_Instance(obj, klass) do {\
- if (!rb_obj_is_instance_of((obj), (klass))) {\
- ossl_raise(rb_eTypeError, "wrong argument (%"PRIsVALUE")! (Expected instance of %"PRIsVALUE")",\
- rb_obj_class(obj), (klass));\
- }\
-} while (0)
-
-#define OSSL_Check_Same_Class(obj1, obj2) do {\
- if (!rb_obj_is_instance_of((obj1), rb_obj_class(obj2))) {\
- ossl_raise(rb_eTypeError, "wrong argument type");\
- }\
-} while (0)
+/*
+ * Type conversions
+ */
+#if !defined(NUM2UINT64T) /* in case Ruby starts to provide */
+# if SIZEOF_LONG == 8
+# define NUM2UINT64T(x) ((uint64_t)NUM2ULONG(x))
+# elif defined(HAVE_LONG_LONG) && SIZEOF_LONG_LONG == 8
+# define NUM2UINT64T(x) ((uint64_t)NUM2ULL(x))
+# else
+# error "unknown platform; no 64-bit width integer"
+# endif
+#endif
/*
* Data Conversion
*/
-STACK_OF(X509) *ossl_x509_ary2sk0(VALUE);
STACK_OF(X509) *ossl_x509_ary2sk(VALUE);
STACK_OF(X509) *ossl_protect_x509_ary2sk(VALUE,int*);
VALUE ossl_x509_sk2ary(const STACK_OF(X509) *certs);
VALUE ossl_x509crl_sk2ary(const STACK_OF(X509_CRL) *crl);
VALUE ossl_x509name_sk2ary(const STACK_OF(X509_NAME) *names);
VALUE ossl_buf2str(char *buf, int len);
+VALUE ossl_str_new(const char *, long, int *);
#define ossl_str_adjust(str, p) \
do{\
long len = RSTRING_LEN(str);\
@@ -142,24 +120,13 @@ int ossl_pem_passwd_cb(char *, int, int, void *);
/*
* ERRor messages
*/
-#define OSSL_ErrMsg() ERR_reason_error_string(ERR_get_error())
NORETURN(void ossl_raise(VALUE, const char *, ...));
-VALUE ossl_exc_new(VALUE, const char *, ...);
/* Clear OpenSSL error queue. If dOSSL is set, rb_warn() them. */
void ossl_clear_error(void);
/*
- * Verify callback
- */
-extern int ossl_store_ctx_ex_verify_cb_idx;
-extern int ossl_store_ex_verify_cb_idx;
-
-int ossl_verify_cb_call(VALUE, int, X509_STORE_CTX *);
-
-/*
* String to DER String
*/
-extern ID ossl_s_to_der;
VALUE ossl_to_der(VALUE);
VALUE ossl_to_der_if_possible(VALUE);
@@ -177,20 +144,9 @@ extern VALUE dOSSL;
} \
} while (0)
-#define OSSL_Warning(fmt, ...) do { \
- OSSL_Debug((fmt), ##__VA_ARGS__); \
- rb_warning((fmt), ##__VA_ARGS__); \
-} while (0)
-
-#define OSSL_Warn(fmt, ...) do { \
- OSSL_Debug((fmt), ##__VA_ARGS__); \
- rb_warn((fmt), ##__VA_ARGS__); \
-} while (0)
#else
void ossl_debug(const char *, ...);
#define OSSL_Debug ossl_debug
-#define OSSL_Warning rb_warning
-#define OSSL_Warn rb_warn
#endif
/*
@@ -209,13 +165,13 @@ void ossl_debug(const char *, ...);
#include "ossl_ocsp.h"
#include "ossl_pkcs12.h"
#include "ossl_pkcs7.h"
-#include "ossl_pkcs5.h"
#include "ossl_pkey.h"
#include "ossl_rand.h"
#include "ossl_ssl.h"
#include "ossl_version.h"
#include "ossl_x509.h"
#include "ossl_engine.h"
+#include "ossl_kdf.h"
void Init_openssl(void);
diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c
index 85b1f02e62..0085d4beab 100644
--- a/ext/openssl/ossl_asn1.c
+++ b/ext/openssl/ossl_asn1.c
@@ -9,20 +9,9 @@
*/
#include "ossl.h"
-#if defined(HAVE_SYS_TIME_H)
-# include <sys/time.h>
-#elif !defined(NT) && !defined(_WIN32)
-struct timeval {
- long tv_sec; /* seconds */
- long tv_usec; /* and microseconds */
-};
-#endif
-
-static VALUE join_der(VALUE enumerable);
static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset,
int depth, int yield, long *num_read);
static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self);
-static VALUE ossl_asn1eoc_initialize(VALUE self);
/*
* DATE conversion
@@ -34,7 +23,6 @@ asn1time_to_time(const ASN1_TIME *time)
VALUE argv[6];
int count;
- if (!time || !time->data) return Qnil;
memset(&tm, 0, sizeof(struct tm));
switch (time->type) {
@@ -56,9 +44,15 @@ asn1time_to_time(const ASN1_TIME *time)
}
break;
case V_ASN1_GENERALIZEDTIME:
- if (sscanf((const char *)time->data, "%4d%2d%2d%2d%2d%2dZ", &tm.tm_year, &tm.tm_mon,
- &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
- ossl_raise(rb_eTypeError, "bad GENERALIZEDTIME format" );
+ count = sscanf((const char *)time->data, "%4d%2d%2d%2d%2d%2dZ",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min,
+ &tm.tm_sec);
+ if (count == 5) {
+ tm.tm_sec = 0;
+ }
+ else if (count != 6) {
+ ossl_raise(rb_eTypeError, "bad GENERALIZEDTIME format: \"%s\"",
+ time->data);
}
break;
default:
@@ -75,7 +69,6 @@ asn1time_to_time(const ASN1_TIME *time)
return rb_funcall2(rb_cTime, rb_intern("utc"), 6, argv);
}
-#if defined(HAVE_ASN1_TIME_ADJ)
void
ossl_time_split(VALUE time, time_t *sec, int *days)
{
@@ -91,13 +84,6 @@ ossl_time_split(VALUE time, time_t *sec, int *days)
*sec = NUM2TIMET(rb_funcall(num, rb_intern("%"), 1, INT2FIX(86400)));
}
}
-#else
-time_t
-time_to_time_t(VALUE time)
-{
- return (time_t)NUM2TIMET(rb_Integer(time));
-}
-#endif
/*
* STRING conversion
@@ -110,16 +96,11 @@ asn1str_to_str(const ASN1_STRING *str)
/*
* ASN1_INTEGER conversions
- * TODO: Make a decision what's the right way to do this.
*/
-#define DO_IT_VIA_RUBY 0
VALUE
asn1integer_to_num(const ASN1_INTEGER *ai)
{
BIGNUM *bn;
-#if DO_IT_VIA_RUBY
- char *txt;
-#endif
VALUE num;
if (!ai) {
@@ -133,43 +114,12 @@ asn1integer_to_num(const ASN1_INTEGER *ai)
if (!bn)
ossl_raise(eOSSLError, NULL);
-#if DO_IT_VIA_RUBY
- if (!(txt = BN_bn2dec(bn))) {
- BN_free(bn);
- ossl_raise(eOSSLError, NULL);
- }
- num = rb_cstr_to_inum(txt, 10, Qtrue);
- OPENSSL_free(txt);
-#else
num = ossl_bn_new(bn);
-#endif
BN_free(bn);
return num;
}
-#if DO_IT_VIA_RUBY
-ASN1_INTEGER *
-num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai)
-{
- BIGNUM *bn = NULL;
-
- if (RTEST(rb_obj_is_kind_of(obj, cBN))) {
- bn = GetBNPtr(obj);
- } else {
- obj = rb_String(obj);
- if (!BN_dec2bn(&bn, StringValueCStr(obj))) {
- ossl_raise(eOSSLError, NULL);
- }
- }
- if (!(ai = BN_to_ASN1_INTEGER(bn, ai))) {
- BN_free(bn);
- ossl_raise(eOSSLError, NULL);
- }
- BN_free(bn);
- return ai;
-}
-#else
ASN1_INTEGER *
num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai)
{
@@ -185,7 +135,6 @@ num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai)
return ai;
}
-#endif
/********/
/*
@@ -195,13 +144,13 @@ num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai)
#define ossl_asn1_get_tag(o) rb_attr_get((o),sivTAG)
#define ossl_asn1_get_tagging(o) rb_attr_get((o),sivTAGGING)
#define ossl_asn1_get_tag_class(o) rb_attr_get((o),sivTAG_CLASS)
-#define ossl_asn1_get_infinite_length(o) rb_attr_get((o),sivINFINITE_LENGTH)
+#define ossl_asn1_get_indefinite_length(o) rb_attr_get((o),sivINDEFINITE_LENGTH)
#define ossl_asn1_set_value(o,v) rb_ivar_set((o),sivVALUE,(v))
#define ossl_asn1_set_tag(o,v) rb_ivar_set((o),sivTAG,(v))
#define ossl_asn1_set_tagging(o,v) rb_ivar_set((o),sivTAGGING,(v))
#define ossl_asn1_set_tag_class(o,v) rb_ivar_set((o),sivTAG_CLASS,(v))
-#define ossl_asn1_set_infinite_length(o,v) rb_ivar_set((o),sivINFINITE_LENGTH,(v))
+#define ossl_asn1_set_indefinite_length(o,v) rb_ivar_set((o),sivINDEFINITE_LENGTH,(v))
VALUE mASN1;
VALUE eASN1Error;
@@ -225,9 +174,10 @@ VALUE cASN1ObjectId; /* OBJECT IDENTIFIER */
VALUE cASN1UTCTime, cASN1GeneralizedTime; /* TIME */
VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */
-static ID sIMPLICIT, sEXPLICIT;
-static ID sUNIVERSAL, sAPPLICATION, sCONTEXT_SPECIFIC, sPRIVATE;
-static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINFINITE_LENGTH, sivUNUSED_BITS;
+static VALUE sym_IMPLICIT, sym_EXPLICIT;
+static VALUE sym_UNIVERSAL, sym_APPLICATION, sym_CONTEXT_SPECIFIC, sym_PRIVATE;
+static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINDEFINITE_LENGTH, sivUNUSED_BITS;
+static ID id_each;
/*
* Ruby to ASN1 converters
@@ -252,13 +202,15 @@ obj_to_asn1bstr(VALUE obj, long unused_bits)
{
ASN1_BIT_STRING *bstr;
- if(unused_bits < 0) unused_bits = 0;
+ if (unused_bits < 0 || unused_bits > 7)
+ ossl_raise(eASN1Error, "unused_bits for a bitstring value must be in "\
+ "the range 0 to 7");
StringValue(obj);
if(!(bstr = ASN1_BIT_STRING_new()))
ossl_raise(eASN1Error, NULL);
ASN1_BIT_STRING_set(bstr, (unsigned char *)RSTRING_PTR(obj), RSTRING_LENINT(obj));
bstr->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07); /* clear */
- bstr->flags |= ASN1_STRING_FLAG_BITS_LEFT|(unused_bits&0x07);
+ bstr->flags |= ASN1_STRING_FLAG_BITS_LEFT | unused_bits;
return bstr;
}
@@ -308,15 +260,10 @@ obj_to_asn1utime(VALUE time)
time_t sec;
ASN1_UTCTIME *t;
-#if defined(HAVE_ASN1_TIME_ADJ)
int off_days;
ossl_time_split(time, &sec, &off_days);
if (!(t = ASN1_UTCTIME_adj(NULL, sec, off_days, 0)))
-#else
- sec = time_to_time_t(time);
- if (!(t = ASN1_UTCTIME_set(NULL, sec)))
-#endif
ossl_raise(eASN1Error, NULL);
return t;
@@ -328,15 +275,10 @@ obj_to_asn1gtime(VALUE time)
time_t sec;
ASN1_GENERALIZEDTIME *t;
-#if defined(HAVE_ASN1_TIME_ADJ)
int off_days;
ossl_time_split(time, &sec, &off_days);
if (!(t = ASN1_GENERALIZEDTIME_adj(NULL, sec, off_days, 0)))
-#else
- sec = time_to_time_t(time);
- if (!(t = ASN1_GENERALIZEDTIME_set(NULL, sec)))
-#endif
ossl_raise(eASN1Error, NULL);
return t;
@@ -364,13 +306,12 @@ decode_bool(unsigned char* der, long length)
{
const unsigned char *p = der;
- assert(length == 3);
- if (*p++ != 1)
- ossl_raise(eASN1Error, "not a boolean");
- if (*p++ != 1)
- ossl_raise(eASN1Error, "length is not 1");
+ if (length != 3)
+ ossl_raise(eASN1Error, "invalid length for BOOLEAN");
+ if (p[0] != 1 || p[1] != 1)
+ ossl_raise(eASN1Error, "invalid BOOLEAN");
- return *p ? Qtrue : Qfalse;
+ return p[2] ? Qtrue : Qfalse;
}
static VALUE
@@ -557,7 +498,7 @@ ossl_asn1_get_asn1type(VALUE obj)
VALUE value, rflag;
void *ptr;
void (*free_func)();
- int tag, flag;
+ int tag;
tag = ossl_asn1_default_tag(obj);
value = ossl_asn1_get_value(obj);
@@ -573,8 +514,7 @@ ossl_asn1_get_asn1type(VALUE obj)
break;
case V_ASN1_BIT_STRING:
rflag = rb_attr_get(obj, sivUNUSED_BITS);
- flag = NIL_P(rflag) ? -1 : NUM2INT(rflag);
- ptr = obj_to_asn1bstr(value, flag);
+ ptr = obj_to_asn1bstr(value, NUM2INT(rflag));
free_func = ASN1_BIT_STRING_free;
break;
case V_ASN1_NULL:
@@ -632,17 +572,14 @@ ossl_asn1_default_tag(VALUE obj)
VALUE tmp_class, tag;
tmp_class = CLASS_OF(obj);
- while (tmp_class) {
+ while (!NIL_P(tmp_class)) {
tag = rb_hash_lookup(class_tag_map, tmp_class);
- if (tag != Qnil) {
- return NUM2INT(tag);
- }
- tmp_class = rb_class_superclass(tmp_class);
+ if (tag != Qnil)
+ return NUM2INT(tag);
+ tmp_class = rb_class_superclass(tmp_class);
}
- ossl_raise(eASN1Error, "universal tag for %"PRIsVALUE" not found",
- rb_obj_class(obj));
- return -1; /* dummy */
+ return -1;
}
static int
@@ -658,74 +595,46 @@ ossl_asn1_tag(VALUE obj)
}
static int
-ossl_asn1_is_explicit(VALUE obj)
-{
- VALUE s;
- int ret = -1;
-
- s = ossl_asn1_get_tagging(obj);
- if(NIL_P(s)) return 0;
- else if(SYMBOL_P(s)){
- if (SYM2ID(s) == sIMPLICIT)
- ret = 0;
- else if (SYM2ID(s) == sEXPLICIT)
- ret = 1;
- }
- if(ret < 0){
- ossl_raise(eASN1Error, "invalid tag default");
- }
-
- return ret;
-}
-
-static int
ossl_asn1_tag_class(VALUE obj)
{
VALUE s;
- int ret = -1;
s = ossl_asn1_get_tag_class(obj);
- if(NIL_P(s)) ret = V_ASN1_UNIVERSAL;
- else if(SYMBOL_P(s)){
- if (SYM2ID(s) == sUNIVERSAL)
- ret = V_ASN1_UNIVERSAL;
- else if (SYM2ID(s) == sAPPLICATION)
- ret = V_ASN1_APPLICATION;
- else if (SYM2ID(s) == sCONTEXT_SPECIFIC)
- ret = V_ASN1_CONTEXT_SPECIFIC;
- else if (SYM2ID(s) == sPRIVATE)
- ret = V_ASN1_PRIVATE;
- }
- if(ret < 0){
+ if (NIL_P(s) || s == sym_UNIVERSAL)
+ return V_ASN1_UNIVERSAL;
+ else if (s == sym_APPLICATION)
+ return V_ASN1_APPLICATION;
+ else if (s == sym_CONTEXT_SPECIFIC)
+ return V_ASN1_CONTEXT_SPECIFIC;
+ else if (s == sym_PRIVATE)
+ return V_ASN1_PRIVATE;
+ else
ossl_raise(eASN1Error, "invalid tag class");
- }
-
- return ret;
}
static VALUE
ossl_asn1_class2sym(int tc)
{
if((tc & V_ASN1_PRIVATE) == V_ASN1_PRIVATE)
- return ID2SYM(sPRIVATE);
+ return sym_PRIVATE;
else if((tc & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC)
- return ID2SYM(sCONTEXT_SPECIFIC);
+ return sym_CONTEXT_SPECIFIC;
else if((tc & V_ASN1_APPLICATION) == V_ASN1_APPLICATION)
- return ID2SYM(sAPPLICATION);
+ return sym_APPLICATION;
else
- return ID2SYM(sUNIVERSAL);
+ return sym_UNIVERSAL;
}
/*
* call-seq:
* OpenSSL::ASN1::ASN1Data.new(value, tag, tag_class) => ASN1Data
*
- * +value+: Please have a look at Constructive and Primitive to see how Ruby
+ * _value_: Please have a look at Constructive and Primitive to see how Ruby
* types are mapped to ASN.1 types and vice versa.
*
- * +tag+: A +Number+ indicating the tag number.
+ * _tag_: An Integer indicating the tag number.
*
- * +tag_class+: A +Symbol+ indicating the tag class. Please cf. ASN1 for
+ * _tag_class_: A Symbol indicating the tag class. Please cf. ASN1 for
* possible values.
*
* == Example
@@ -737,73 +646,85 @@ ossl_asn1data_initialize(VALUE self, VALUE value, VALUE tag, VALUE tag_class)
{
if(!SYMBOL_P(tag_class))
ossl_raise(eASN1Error, "invalid tag class");
- if((SYM2ID(tag_class) == sUNIVERSAL) && NUM2INT(tag) > 31)
- ossl_raise(eASN1Error, "tag number for Universal too large");
ossl_asn1_set_tag(self, tag);
ossl_asn1_set_value(self, value);
ossl_asn1_set_tag_class(self, tag_class);
- ossl_asn1_set_infinite_length(self, Qfalse);
+ ossl_asn1_set_indefinite_length(self, Qfalse);
return self;
}
static VALUE
-join_der_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, str))
+to_der_internal(VALUE self, int constructed, int indef_len, VALUE body)
{
- i = ossl_to_der_if_possible(i);
- StringValue(i);
- rb_str_append(str, i);
- return Qnil;
-}
+ int encoding = constructed ? indef_len ? 2 : 1 : 0;
+ int tag_class = ossl_asn1_tag_class(self);
+ int tag_number = ossl_asn1_tag(self);
+ int default_tag_number = ossl_asn1_default_tag(self);
+ int body_length, total_length;
+ VALUE str;
+ unsigned char *p;
-static VALUE
-join_der(VALUE enumerable)
-{
- VALUE str = rb_str_new(0, 0);
- rb_block_call(enumerable, rb_intern("each"), 0, 0, join_der_i, str);
+ body_length = RSTRING_LENINT(body);
+ if (ossl_asn1_get_tagging(self) == sym_EXPLICIT) {
+ int inner_length, e_encoding = indef_len ? 2 : 1;
+
+ if (default_tag_number == -1)
+ ossl_raise(eASN1Error, "explicit tagging of unknown tag");
+
+ inner_length = ASN1_object_size(encoding, body_length, default_tag_number);
+ total_length = ASN1_object_size(e_encoding, inner_length, tag_number);
+ str = rb_str_new(NULL, total_length);
+ p = (unsigned char *)RSTRING_PTR(str);
+ /* Put explicit tag */
+ ASN1_put_object(&p, e_encoding, inner_length, tag_number, tag_class);
+ /* Append inner object */
+ ASN1_put_object(&p, encoding, body_length, default_tag_number, V_ASN1_UNIVERSAL);
+ memcpy(p, RSTRING_PTR(body), body_length);
+ p += body_length;
+ if (indef_len) {
+ ASN1_put_eoc(&p); /* For inner object */
+ ASN1_put_eoc(&p); /* For wrapper object */
+ }
+ }
+ else {
+ total_length = ASN1_object_size(encoding, body_length, tag_number);
+ str = rb_str_new(NULL, total_length);
+ p = (unsigned char *)RSTRING_PTR(str);
+ ASN1_put_object(&p, encoding, body_length, tag_number, tag_class);
+ memcpy(p, RSTRING_PTR(body), body_length);
+ p += body_length;
+ if (indef_len)
+ ASN1_put_eoc(&p);
+ }
+ assert(p - (unsigned char *)RSTRING_PTR(str) == total_length);
return str;
}
+static VALUE ossl_asn1prim_to_der(VALUE);
+static VALUE ossl_asn1cons_to_der(VALUE);
/*
* call-seq:
* asn1.to_der => DER-encoded String
*
* Encodes this ASN1Data into a DER-encoded String value. The result is
- * DER-encoded except for the possibility of infinite length encodings.
- * Infinite length encodings are not allowed in strict DER, so strictly
- * speaking the result of such an encoding would be a BER-encoding.
+ * DER-encoded except for the possibility of indefinite length forms.
+ * Indefinite length forms are not allowed in strict DER, so strictly speaking
+ * the result of such an encoding would be a BER-encoding.
*/
static VALUE
ossl_asn1data_to_der(VALUE self)
{
- VALUE value, der, inf_length;
- int tag, tag_class, is_cons = 0;
- long length;
- unsigned char *p;
+ VALUE value = ossl_asn1_get_value(self);
- value = ossl_asn1_get_value(self);
- if(rb_obj_is_kind_of(value, rb_cArray)){
- is_cons = 1;
- value = join_der(value);
- }
- StringValue(value);
-
- tag = ossl_asn1_tag(self);
- tag_class = ossl_asn1_tag_class(self);
- inf_length = ossl_asn1_get_infinite_length(self);
- if (inf_length == Qtrue) {
- is_cons = 2;
+ if (rb_obj_is_kind_of(value, rb_cArray))
+ return ossl_asn1cons_to_der(self);
+ else {
+ if (RTEST(ossl_asn1_get_indefinite_length(self)))
+ ossl_raise(eASN1Error, "indefinite length form cannot be used " \
+ "with primitive encoding");
+ return ossl_asn1prim_to_der(self);
}
- if((length = ASN1_object_size(is_cons, RSTRING_LENINT(value), tag)) <= 0)
- ossl_raise(eASN1Error, NULL);
- der = rb_str_new(0, length);
- p = (unsigned char *)RSTRING_PTR(der);
- ASN1_put_object(&p, is_cons, RSTRING_LENINT(value), tag, tag_class);
- memcpy(p, RSTRING_PTR(value), RSTRING_LEN(value));
- p += RSTRING_LEN(value);
- ossl_str_adjust(der, p);
-
- return der;
}
static VALUE
@@ -816,7 +737,7 @@ int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag,
p = *pp;
- if(tc == sUNIVERSAL && tag < ossl_asn1_info_size) {
+ if(tc == sym_UNIVERSAL && tag < ossl_asn1_info_size) {
switch(tag){
case V_ASN1_EOC:
value = decode_eoc(p, hlen+length);
@@ -858,13 +779,14 @@ int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag,
*pp += hlen + length;
*num_read = hlen + length;
- if (tc == sUNIVERSAL && tag < ossl_asn1_info_size && ossl_asn1_info[tag].klass) {
+ if (tc == sym_UNIVERSAL &&
+ tag < ossl_asn1_info_size && ossl_asn1_info[tag].klass) {
VALUE klass = *ossl_asn1_info[tag].klass;
VALUE args[4];
args[0] = value;
args[1] = INT2NUM(tag);
args[2] = Qnil;
- args[3] = ID2SYM(tc);
+ args[3] = tc;
asn1data = rb_obj_alloc(klass);
ossl_asn1_initialize(4, args, asn1data);
if(tag == V_ASN1_BIT_STRING){
@@ -873,7 +795,7 @@ int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag,
}
else {
asn1data = rb_obj_alloc(cASN1Data);
- ossl_asn1data_initialize(asn1data, value, INT2NUM(tag), ID2SYM(tc));
+ ossl_asn1data_initialize(asn1data, value, INT2NUM(tag), tc);
}
return asn1data;
@@ -885,62 +807,48 @@ int_ossl_asn1_decode0_cons(unsigned char **pp, long max_len, long length,
int tag, VALUE tc, long *num_read)
{
VALUE value, asn1data, ary;
- int infinite;
- long off = *offset;
+ int indefinite;
+ long available_len, off = *offset;
- infinite = (j == 0x21);
+ indefinite = (j == 0x21);
ary = rb_ary_new();
- while (length > 0 || infinite) {
+ available_len = indefinite ? max_len : length;
+ while (available_len > 0) {
long inner_read = 0;
- value = ossl_asn1_decode0(pp, max_len, &off, depth + 1, yield, &inner_read);
+ value = ossl_asn1_decode0(pp, available_len, &off, depth + 1, yield, &inner_read);
*num_read += inner_read;
- max_len -= inner_read;
- rb_ary_push(ary, value);
- if (length > 0)
- length -= inner_read;
+ available_len -= inner_read;
- if (infinite &&
- NUM2INT(ossl_asn1_get_tag(value)) == V_ASN1_EOC &&
- SYM2ID(ossl_asn1_get_tag_class(value)) == sUNIVERSAL) {
+ if (indefinite &&
+ ossl_asn1_tag(value) == V_ASN1_EOC &&
+ ossl_asn1_get_tag_class(value) == sym_UNIVERSAL) {
break;
}
+ rb_ary_push(ary, value);
}
- if (tc == sUNIVERSAL) {
+ if (tc == sym_UNIVERSAL) {
VALUE args[4];
- int not_sequence_or_set;
-
- not_sequence_or_set = tag != V_ASN1_SEQUENCE && tag != V_ASN1_SET;
-
- if (not_sequence_or_set) {
- if (infinite) {
- asn1data = rb_obj_alloc(cASN1Constructive);
- }
- else {
- ossl_raise(eASN1Error, "invalid non-infinite tag");
- return Qnil;
- }
- }
- else {
- VALUE klass = *ossl_asn1_info[tag].klass;
- asn1data = rb_obj_alloc(klass);
- }
+ if (tag == V_ASN1_SEQUENCE || tag == V_ASN1_SET)
+ asn1data = rb_obj_alloc(*ossl_asn1_info[tag].klass);
+ else
+ asn1data = rb_obj_alloc(cASN1Constructive);
args[0] = ary;
args[1] = INT2NUM(tag);
args[2] = Qnil;
- args[3] = ID2SYM(tc);
+ args[3] = tc;
ossl_asn1_initialize(4, args, asn1data);
}
else {
asn1data = rb_obj_alloc(cASN1Data);
- ossl_asn1data_initialize(asn1data, ary, INT2NUM(tag), ID2SYM(tc));
+ ossl_asn1data_initialize(asn1data, ary, INT2NUM(tag), tc);
}
- if (infinite)
- ossl_asn1_set_infinite_length(asn1data, Qtrue);
+ if (indefinite)
+ ossl_asn1_set_indefinite_length(asn1data, Qtrue);
else
- ossl_asn1_set_infinite_length(asn1data, Qfalse);
+ ossl_asn1_set_indefinite_length(asn1data, Qfalse);
*offset = off;
return asn1data;
@@ -964,13 +872,13 @@ ossl_asn1_decode0(unsigned char **pp, long length, long *offset, int depth,
if(j & 0x80) ossl_raise(eASN1Error, NULL);
if(len > length) ossl_raise(eASN1Error, "value is too short");
if((tc & V_ASN1_PRIVATE) == V_ASN1_PRIVATE)
- tag_class = sPRIVATE;
+ tag_class = sym_PRIVATE;
else if((tc & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC)
- tag_class = sCONTEXT_SPECIFIC;
+ tag_class = sym_CONTEXT_SPECIFIC;
else if((tc & V_ASN1_APPLICATION) == V_ASN1_APPLICATION)
- tag_class = sAPPLICATION;
+ tag_class = sym_APPLICATION;
else
- tag_class = sUNIVERSAL;
+ tag_class = sym_UNIVERSAL;
hlen = p - start;
@@ -989,11 +897,12 @@ ossl_asn1_decode0(unsigned char **pp, long length, long *offset, int depth,
if(j & V_ASN1_CONSTRUCTED) {
*pp += hlen;
off += hlen;
- asn1data = int_ossl_asn1_decode0_cons(pp, length, len, &off, depth, yield, j, tag, tag_class, &inner_read);
+ asn1data = int_ossl_asn1_decode0_cons(pp, length - hlen, len, &off, depth, yield, j, tag, tag_class, &inner_read);
inner_read += hlen;
}
else {
- if ((j & 0x01) && (len == 0)) ossl_raise(eASN1Error, "Infinite length for primitive value");
+ if ((j & 0x01) && (len == 0))
+ ossl_raise(eASN1Error, "indefinite length for primitive value");
asn1data = int_ossl_asn1_decode0_prim(pp, len, hlen, tag, tag_class, &inner_read);
off += hlen + len;
}
@@ -1025,13 +934,13 @@ int_ossl_decode_sanity_check(long len, long read, long offset)
*
* If a block is given, it prints out each of the elements encountered.
* Block parameters are (in that order):
- * * depth: The recursion depth, plus one with each constructed value being encountered (Number)
- * * offset: Current byte offset (Number)
- * * header length: Combined length in bytes of the Tag and Length headers. (Number)
- * * length: The overall remaining length of the entire data (Number)
+ * * depth: The recursion depth, plus one with each constructed value being encountered (Integer)
+ * * offset: Current byte offset (Integer)
+ * * header length: Combined length in bytes of the Tag and Length headers. (Integer)
+ * * length: The overall remaining length of the entire data (Integer)
* * constructed: Whether this value is constructed or not (Boolean)
* * tag_class: Current tag class (Symbol)
- * * tag: The current tag (Number)
+ * * tag: The current tag number (Integer)
*
* == Example
* der = File.binread('asn1data.der')
@@ -1061,9 +970,9 @@ ossl_asn1_traverse(VALUE self, VALUE obj)
* call-seq:
* OpenSSL::ASN1.decode(der) -> ASN1Data
*
- * Decodes a BER- or DER-encoded value and creates an ASN1Data instance. +der+
- * may be a +String+ or any object that features a +#to_der+ method transforming
- * it into a BER-/DER-encoded +String+.
+ * Decodes a BER- or DER-encoded value and creates an ASN1Data instance. _der_
+ * may be a String or any object that features a +.to_der+ method transforming
+ * it into a BER-/DER-encoded String+
*
* == Example
* der = File.binread('asn1data')
@@ -1091,9 +1000,9 @@ ossl_asn1_decode(VALUE self, VALUE obj)
* call-seq:
* OpenSSL::ASN1.decode_all(der) -> Array of ASN1Data
*
- * Similar to +decode+ with the difference that +decode+ expects one
- * distinct value represented in +der+. +decode_all+ on the contrary
- * decodes a sequence of sequential BER/DER values lined up in +der+
+ * Similar to #decode with the difference that #decode expects one
+ * distinct value represented in _der_. #decode_all on the contrary
+ * decodes a sequence of sequential BER/DER values lined up in _der_
* and returns them as an array.
*
* == Example
@@ -1128,19 +1037,19 @@ ossl_asn1_decode_all(VALUE self, VALUE obj)
/*
* call-seq:
- * OpenSSL::ASN1::Primitive.new( value [, tag, tagging, tag_class ]) => Primitive
+ * OpenSSL::ASN1::Primitive.new(value [, tag, tagging, tag_class ]) => Primitive
*
- * +value+: is mandatory.
+ * _value_: is mandatory.
*
- * +tag+: optional, may be specified for tagged values. If no +tag+ is
+ * _tag_: optional, may be specified for tagged values. If no _tag_ is
* specified, the UNIVERSAL tag corresponding to the Primitive sub-class
* is used by default.
*
- * +tagging+: may be used as an encoding hint to encode a value either
+ * _tagging_: may be used as an encoding hint to encode a value either
* explicitly or implicitly, see ASN1 for possible values.
*
- * +tag_class+: if +tag+ and +tagging+ are +nil+ then this is set to
- * +:UNIVERSAL+ by default. If either +tag+ or +tagging+ are set then
+ * _tag_class_: if _tag_ and _tagging_ are +nil+ then this is set to
+ * +:UNIVERSAL+ by default. If either _tag_ or _tagging_ are set then
* +:CONTEXT_SPECIFIC+ is used as the default. For possible values please
* cf. ASN1.
*
@@ -1153,34 +1062,37 @@ static VALUE
ossl_asn1_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE value, tag, tagging, tag_class;
+ int default_tag;
rb_scan_args(argc, argv, "13", &value, &tag, &tagging, &tag_class);
- if(argc > 1){
+ default_tag = ossl_asn1_default_tag(self);
+
+ if (default_tag == -1 || argc > 1) {
if(NIL_P(tag))
ossl_raise(eASN1Error, "must specify tag number");
if(!NIL_P(tagging) && !SYMBOL_P(tagging))
ossl_raise(eASN1Error, "invalid tagging method");
if(NIL_P(tag_class)) {
if (NIL_P(tagging))
- tag_class = ID2SYM(sUNIVERSAL);
+ tag_class = sym_UNIVERSAL;
else
- tag_class = ID2SYM(sCONTEXT_SPECIFIC);
+ tag_class = sym_CONTEXT_SPECIFIC;
}
if(!SYMBOL_P(tag_class))
ossl_raise(eASN1Error, "invalid tag class");
- if(!NIL_P(tagging) && SYM2ID(tagging) == sIMPLICIT && NUM2INT(tag) > 31)
- ossl_raise(eASN1Error, "tag number for Universal too large");
}
else{
- tag = INT2NUM(ossl_asn1_default_tag(self));
+ tag = INT2NUM(default_tag);
tagging = Qnil;
- tag_class = ID2SYM(sUNIVERSAL);
+ tag_class = sym_UNIVERSAL;
}
ossl_asn1_set_tag(self, tag);
ossl_asn1_set_value(self, value);
ossl_asn1_set_tagging(self, tagging);
ossl_asn1_set_tag_class(self, tag_class);
- ossl_asn1_set_infinite_length(self, Qfalse);
+ ossl_asn1_set_indefinite_length(self, Qfalse);
+ if (default_tag == V_ASN1_BIT_STRING)
+ rb_ivar_set(self, sivUNUSED_BITS, INT2FIX(0));
return self;
}
@@ -1188,59 +1100,66 @@ ossl_asn1_initialize(int argc, VALUE *argv, VALUE self)
static VALUE
ossl_asn1eoc_initialize(VALUE self) {
VALUE tag, tagging, tag_class, value;
- tag = INT2NUM(ossl_asn1_default_tag(self));
+ tag = INT2FIX(0);
tagging = Qnil;
- tag_class = ID2SYM(sUNIVERSAL);
+ tag_class = sym_UNIVERSAL;
value = rb_str_new("", 0);
ossl_asn1_set_tag(self, tag);
ossl_asn1_set_value(self, value);
ossl_asn1_set_tagging(self, tagging);
ossl_asn1_set_tag_class(self, tag_class);
- ossl_asn1_set_infinite_length(self, Qfalse);
+ ossl_asn1_set_indefinite_length(self, Qfalse);
return self;
}
+static VALUE
+ossl_asn1eoc_to_der(VALUE self)
+{
+ return rb_str_new("\0\0", 2);
+}
+
/*
* call-seq:
* asn1.to_der => DER-encoded String
*
- * See ASN1Data#to_der for details. *
+ * See ASN1Data#to_der for details.
*/
static VALUE
ossl_asn1prim_to_der(VALUE self)
{
ASN1_TYPE *asn1;
- int tn, tc, explicit;
- long len, reallen;
- unsigned char *buf, *p;
+ long alllen, bodylen;
+ unsigned char *p0, *p1;
+ int j, tag, tc, state;
VALUE str;
- tn = NUM2INT(ossl_asn1_get_tag(self));
- tc = ossl_asn1_tag_class(self);
- explicit = ossl_asn1_is_explicit(self);
- asn1 = ossl_asn1_get_asn1type(self);
+ if (ossl_asn1_default_tag(self) == -1) {
+ str = ossl_asn1_get_value(self);
+ return to_der_internal(self, 0, 0, StringValue(str));
+ }
- len = ASN1_object_size(1, i2d_ASN1_TYPE(asn1, NULL), tn);
- if(!(buf = OPENSSL_malloc(len))){
+ asn1 = ossl_asn1_get_asn1type(self);
+ alllen = i2d_ASN1_TYPE(asn1, NULL);
+ if (alllen < 0) {
ASN1_TYPE_free(asn1);
- ossl_raise(eASN1Error, "cannot alloc buffer");
+ ossl_raise(eASN1Error, "i2d_ASN1_TYPE");
}
- p = buf;
- if (tc == V_ASN1_UNIVERSAL) {
- i2d_ASN1_TYPE(asn1, &p);
- } else if (explicit) {
- ASN1_put_object(&p, 1, i2d_ASN1_TYPE(asn1, NULL), tn, tc);
- i2d_ASN1_TYPE(asn1, &p);
- } else {
- i2d_ASN1_TYPE(asn1, &p);
- *buf = tc | tn | (*buf & V_ASN1_CONSTRUCTED);
+ str = ossl_str_new(NULL, alllen, &state);
+ if (state) {
+ ASN1_TYPE_free(asn1);
+ rb_jump_tag(state);
}
+ p0 = p1 = (unsigned char *)RSTRING_PTR(str);
+ i2d_ASN1_TYPE(asn1, &p0);
ASN1_TYPE_free(asn1);
- reallen = p - buf;
- assert(reallen <= len);
- str = ossl_buf2str((char *)buf, rb_long2int(reallen)); /* buf will be free in ossl_buf2str */
+ assert(p0 - p1 == alllen);
- return str;
+ /* Strip header since to_der_internal() wants only the payload */
+ j = ASN1_get_object((const unsigned char **)&p1, &bodylen, &tag, &tc, alllen);
+ if (j & 0x80)
+ ossl_raise(eASN1Error, "ASN1_get_object"); /* should not happen */
+
+ return to_der_internal(self, 0, 0, rb_str_drop_bytes(str, alllen - bodylen));
}
/*
@@ -1252,92 +1171,41 @@ ossl_asn1prim_to_der(VALUE self)
static VALUE
ossl_asn1cons_to_der(VALUE self)
{
- int tag, tn, tc, explicit, constructed = 1;
- int found_prim = 0, seq_len;
- long length;
- unsigned char *p;
- VALUE value, str, inf_length;
-
- tn = NUM2INT(ossl_asn1_get_tag(self));
- tc = ossl_asn1_tag_class(self);
- inf_length = ossl_asn1_get_infinite_length(self);
- if (inf_length == Qtrue) {
- VALUE ary, example;
- constructed = 2;
- if (CLASS_OF(self) == cASN1Sequence ||
- CLASS_OF(self) == cASN1Set) {
- tag = ossl_asn1_default_tag(self);
- }
- else { /* must be a constructive encoding of a primitive value */
- ary = ossl_asn1_get_value(self);
- if (!rb_obj_is_kind_of(ary, rb_cArray))
- ossl_raise(eASN1Error, "Constructive value must be an Array");
- /* Recursively descend until a primitive value is found.
- The overall value of the entire constructed encoding
- is of the type of the first primitive encoding to be
- found. */
- while (!found_prim){
- example = rb_ary_entry(ary, 0);
- if (rb_obj_is_kind_of(example, cASN1Primitive)){
- found_prim = 1;
- }
- else {
- /* example is another ASN1Constructive */
- if (!rb_obj_is_kind_of(example, cASN1Constructive)){
- ossl_raise(eASN1Error, "invalid constructed encoding");
- return Qnil; /* dummy */
- }
- ary = ossl_asn1_get_value(example);
- }
- }
- tag = ossl_asn1_default_tag(example);
- }
- }
- else {
- if (CLASS_OF(self) == cASN1Constructive)
- ossl_raise(eASN1Error, "Constructive shall only be used with infinite length");
- tag = ossl_asn1_default_tag(self);
- }
- explicit = ossl_asn1_is_explicit(self);
- value = join_der(ossl_asn1_get_value(self));
-
- seq_len = ASN1_object_size(constructed, RSTRING_LENINT(value), tag);
- length = ASN1_object_size(constructed, seq_len, tn);
- str = rb_str_new(0, length);
- p = (unsigned char *)RSTRING_PTR(str);
- if(tc == V_ASN1_UNIVERSAL)
- ASN1_put_object(&p, constructed, RSTRING_LENINT(value), tn, tc);
- else{
- if(explicit){
- ASN1_put_object(&p, constructed, seq_len, tn, tc);
- ASN1_put_object(&p, constructed, RSTRING_LENINT(value), tag, V_ASN1_UNIVERSAL);
- }
- else{
- ASN1_put_object(&p, constructed, RSTRING_LENINT(value), tn, tc);
+ VALUE ary, str;
+ long i;
+ int indef_len;
+
+ indef_len = RTEST(ossl_asn1_get_indefinite_length(self));
+ ary = rb_convert_type(ossl_asn1_get_value(self), T_ARRAY, "Array", "to_a");
+ str = rb_str_new(NULL, 0);
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ VALUE item = RARRAY_AREF(ary, i);
+
+ if (indef_len && rb_obj_is_kind_of(item, cASN1EndOfContent)) {
+ if (i != RARRAY_LEN(ary) - 1)
+ ossl_raise(eASN1Error, "illegal EOC octets in value");
+
+ /*
+ * EOC is not really part of the content, but we required to add one
+ * at the end in the past.
+ */
+ break;
}
- }
- memcpy(p, RSTRING_PTR(value), RSTRING_LEN(value));
- p += RSTRING_LEN(value);
- /* In this case we need an additional EOC (one for the explicit part and
- * one for the Constructive itself. The EOC for the Constructive is
- * supplied by the user, but that for the "explicit wrapper" must be
- * added here.
- */
- if (explicit && inf_length == Qtrue) {
- ASN1_put_eoc(&p);
+ item = ossl_to_der_if_possible(item);
+ StringValue(item);
+ rb_str_append(str, item);
}
- ossl_str_adjust(str, p);
- return str;
+ return to_der_internal(self, 1, indef_len, str);
}
/*
* call-seq:
* asn1_ary.each { |asn1| block } => asn1_ary
*
- * Calls <i>block</i> once for each element in +self+, passing that element
- * as parameter +asn1+. If no block is given, an enumerator is returned
+ * Calls the given block once for each element in self, passing that element
+ * as parameter _asn1_. If no block is given, an enumerator is returned
* instead.
*
* == Example
@@ -1348,7 +1216,8 @@ ossl_asn1cons_to_der(VALUE self)
static VALUE
ossl_asn1cons_each(VALUE self)
{
- rb_ary_each(ossl_asn1_get_value(self));
+ rb_block_call(ossl_asn1_get_value(self), id_each, 0, 0, 0, 0);
+
return self;
}
@@ -1356,8 +1225,8 @@ ossl_asn1cons_each(VALUE self)
* call-seq:
* OpenSSL::ASN1::ObjectId.register(object_id, short_name, long_name)
*
- * This adds a new ObjectId to the internal tables. Where +object_id+ is the
- * numerical form, +short_name+ is the short name, and +long_name+ is the long
+ * This adds a new ObjectId to the internal tables. Where _object_id_ is the
+ * numerical form, _short_name_ is the short name, and _long_name_ is the long
* name.
*
* Returns +true+ if successful. Raises an OpenSSL::ASN1::ASN1Error if it fails.
@@ -1376,14 +1245,13 @@ ossl_asn1obj_s_register(VALUE self, VALUE oid, VALUE sn, VALUE ln)
return Qtrue;
}
-/* Document-method: OpenSSL::ASN1::ObjectId#sn
+/*
+ * call-seq:
+ * oid.sn -> string
+ * oid.short_name -> string
*
* The short name of the ObjectId, as defined in <openssl/objects.h>.
*/
-/* Document-method: OpenSSL::ASN1::ObjectId#short_name
- *
- * +short_name+ is an alias to +sn+
- */
static VALUE
ossl_asn1obj_get_sn(VALUE self)
{
@@ -1397,14 +1265,13 @@ ossl_asn1obj_get_sn(VALUE self)
return ret;
}
-/* Document-method: OpenSSL::ASN1::ObjectId#ln
+/*
+ * call-seq:
+ * oid.ln -> string
+ * oid.long_name -> string
*
* The long name of the ObjectId, as defined in <openssl/objects.h>.
*/
-/* Document-method: OpenSSL::ASN1::ObjectId#long_name
- *
- * +long_name+ is an alias to +ln+
- */
static VALUE
ossl_asn1obj_get_ln(VALUE self)
{
@@ -1418,23 +1285,48 @@ ossl_asn1obj_get_ln(VALUE self)
return ret;
}
-/* Document-method: OpenSSL::ASN1::ObjectId#oid
+static VALUE
+asn1obj_get_oid_i(VALUE vobj)
+{
+ ASN1_OBJECT *a1obj = (void *)vobj;
+ VALUE str;
+ int len;
+
+ str = rb_usascii_str_new(NULL, 127);
+ len = OBJ_obj2txt(RSTRING_PTR(str), RSTRING_LENINT(str), a1obj, 1);
+ if (len <= 0 || len == INT_MAX)
+ ossl_raise(eASN1Error, "OBJ_obj2txt");
+ if (len > RSTRING_LEN(str)) {
+ /* +1 is for the \0 terminator added by OBJ_obj2txt() */
+ rb_str_resize(str, len + 1);
+ len = OBJ_obj2txt(RSTRING_PTR(str), len + 1, a1obj, 1);
+ if (len <= 0)
+ ossl_raise(eASN1Error, "OBJ_obj2txt");
+ }
+ rb_str_set_len(str, len);
+ return str;
+}
+
+/*
+ * call-seq:
+ * oid.oid -> string
*
- * The object identifier as a +String+, e.g. "1.2.3.4.5"
+ * Returns a String representing the Object Identifier in the dot notation,
+ * e.g. "1.2.3.4.5"
*/
static VALUE
ossl_asn1obj_get_oid(VALUE self)
{
- VALUE val;
+ VALUE str;
ASN1_OBJECT *a1obj;
- char buf[128];
+ int state;
- val = ossl_asn1_get_value(self);
- a1obj = obj_to_asn1obj(val);
- OBJ_obj2txt(buf, sizeof(buf), a1obj, 1);
+ a1obj = obj_to_asn1obj(ossl_asn1_get_value(self));
+ str = rb_protect(asn1obj_get_oid_i, (VALUE)a1obj, &state);
ASN1_OBJECT_free(a1obj);
-
- return rb_str_new2(buf);
+ if (state)
+ rb_jump_tag(state);
+ return str;
}
#define OSSL_ASN1_IMPL_FACTORY_METHOD(klass) \
@@ -1468,6 +1360,7 @@ OSSL_ASN1_IMPL_FACTORY_METHOD(EndOfContent)
void
Init_ossl_asn1(void)
{
+#undef rb_intern
VALUE ary;
int i;
@@ -1476,18 +1369,18 @@ Init_ossl_asn1(void)
eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
#endif
- sUNIVERSAL = rb_intern("UNIVERSAL");
- sCONTEXT_SPECIFIC = rb_intern("CONTEXT_SPECIFIC");
- sAPPLICATION = rb_intern("APPLICATION");
- sPRIVATE = rb_intern("PRIVATE");
- sEXPLICIT = rb_intern("EXPLICIT");
- sIMPLICIT = rb_intern("IMPLICIT");
+ sym_UNIVERSAL = ID2SYM(rb_intern_const("UNIVERSAL"));
+ sym_CONTEXT_SPECIFIC = ID2SYM(rb_intern_const("CONTEXT_SPECIFIC"));
+ sym_APPLICATION = ID2SYM(rb_intern_const("APPLICATION"));
+ sym_PRIVATE = ID2SYM(rb_intern_const("PRIVATE"));
+ sym_EXPLICIT = ID2SYM(rb_intern_const("EXPLICIT"));
+ sym_IMPLICIT = ID2SYM(rb_intern_const("IMPLICIT"));
sivVALUE = rb_intern("@value");
sivTAG = rb_intern("@tag");
sivTAGGING = rb_intern("@tagging");
sivTAG_CLASS = rb_intern("@tag_class");
- sivINFINITE_LENGTH = rb_intern("@infinite_length");
+ sivINDEFINITE_LENGTH = rb_intern("@indefinite_length");
sivUNUSED_BITS = rb_intern("@unused_bits");
/*
@@ -1513,24 +1406,21 @@ Init_ossl_asn1(void)
* == ASN.1 class hierarchy
*
* The base class representing ASN.1 structures is ASN1Data. ASN1Data offers
- * attributes to read and set the +tag+, the +tag_class+ and finally the
- * +value+ of a particular ASN.1 item. Upon parsing, any tagged values
+ * attributes to read and set the _tag_, the _tag_class_ and finally the
+ * _value_ of a particular ASN.1 item. Upon parsing, any tagged values
* (implicit or explicit) will be represented by ASN1Data instances because
* their "real type" can only be determined using out-of-band information
* from the ASN.1 type declaration. Since this information is normally
* known when encoding a type, all sub-classes of ASN1Data offer an
- * additional attribute +tagging+ that allows to encode a value implicitly
+ * additional attribute _tagging_ that allows to encode a value implicitly
* (+:IMPLICIT+) or explicitly (+:EXPLICIT+).
*
* === Constructive
*
* Constructive is, as its name implies, the base class for all
* constructed encodings, i.e. those that consist of several values,
- * opposed to "primitive" encodings with just one single value.
- * Primitive values that are encoded with "infinite length" are typically
- * constructed (their values come in multiple chunks) and are therefore
- * represented by instances of Constructive. The value of an Constructive
- * is always an Array.
+ * opposed to "primitive" encodings with just one single value. The value of
+ * an Constructive is always an Array.
*
* ==== ASN1::Set and ASN1::Sequence
*
@@ -1547,18 +1437,18 @@ Init_ossl_asn1(void)
* Please cf. Primitive documentation for details on sub-classes and
* their respective mappings of ASN.1 data types to Ruby objects.
*
- * == Possible values for +tagging+
+ * == Possible values for _tagging_
*
* When constructing an ASN1Data object the ASN.1 type definition may
* require certain elements to be either implicitly or explicitly tagged.
- * This can be achieved by setting the +tagging+ attribute manually for
+ * This can be achieved by setting the _tagging_ attribute manually for
* sub-classes of ASN1Data. Use the symbol +:IMPLICIT+ for implicit
* tagging and +:EXPLICIT+ if the element requires explicit tagging.
*
- * == Possible values for +tag_class+
+ * == Possible values for _tag_class_
*
* It is possible to create arbitrary ASN1Data objects that also support
- * a PRIVATE or APPLICATION tag class. Possible values for the +tag_class+
+ * a PRIVATE or APPLICATION tag class. Possible values for the _tag_class_
* attribute are:
* * +:UNIVERSAL+ (the default for untagged values)
* * +:CONTEXT_SPECIFIC+ (the default for tagged values)
@@ -1660,9 +1550,9 @@ Init_ossl_asn1(void)
*
* An implicitly 1-tagged INTEGER value will be parsed as an
* ASN1Data with
- * * +tag+ equal to 1
- * * +tag_class+ equal to +:CONTEXT_SPECIFIC+
- * * +value+ equal to a +String+ that carries the raw encoding
+ * * _tag_ equal to 1
+ * * _tag_class_ equal to +:CONTEXT_SPECIFIC+
+ * * _value_ equal to a String that carries the raw encoding
* of the INTEGER.
* This implies that a subsequent decoding step is required to
* completely decode implicitly tagged values.
@@ -1671,9 +1561,9 @@ Init_ossl_asn1(void)
*
* An explicitly 1-tagged INTEGER value will be parsed as an
* ASN1Data with
- * * +tag+ equal to 1
- * * +tag_class+ equal to +:CONTEXT_SPECIFIC+
- * * +value+ equal to an +Array+ with one single element, an
+ * * _tag_ equal to 1
+ * * _tag_class_ equal to +:CONTEXT_SPECIFIC+
+ * * _value_ equal to an Array with one single element, an
* instance of OpenSSL::ASN1::Integer, i.e. the inner element
* is the non-tagged primitive value, and the tagging is represented
* in the outer ASN1Data
@@ -1684,13 +1574,13 @@ Init_ossl_asn1(void)
* der = seq.to_der
* asn1 = OpenSSL::ASN1.decode(der)
* # pp asn1 => #<OpenSSL::ASN1::Sequence:0x87326e0
- * # @infinite_length=false,
+ * # @indefinite_length=false,
* # @tag=16,
* # @tag_class=:UNIVERSAL,
* # @tagging=nil,
* # @value=
* # [#<OpenSSL::ASN1::ASN1Data:0x87326f4
- * # @infinite_length=false,
+ * # @indefinite_length=false,
* # @tag=0,
* # @tag_class=:CONTEXT_SPECIFIC,
* # @value="\x01">]>
@@ -1707,18 +1597,18 @@ Init_ossl_asn1(void)
* der = seq.to_der
* asn1 = OpenSSL::ASN1.decode(der)
* # pp asn1 => #<OpenSSL::ASN1::Sequence:0x87326e0
- * # @infinite_length=false,
+ * # @indefinite_length=false,
* # @tag=16,
* # @tag_class=:UNIVERSAL,
* # @tagging=nil,
* # @value=
* # [#<OpenSSL::ASN1::ASN1Data:0x87326f4
- * # @infinite_length=false,
+ * # @indefinite_length=false,
* # @tag=0,
* # @tag_class=:CONTEXT_SPECIFIC,
* # @value=
* # [#<OpenSSL::ASN1::Integer:0x85bf308
- * # @infinite_length=false,
+ * # @indefinite_length=false,
* # @tag=2,
* # @tag_class=:UNIVERSAL
* # @tagging=nil,
@@ -1734,73 +1624,75 @@ Init_ossl_asn1(void)
*/
rb_attr(cASN1Data, rb_intern("value"), 1, 1, 0);
/*
- * A +Number+ representing the tag number of this ASN1Data. Never +nil+.
+ * An Integer representing the tag number of this ASN1Data. Never +nil+.
*/
rb_attr(cASN1Data, rb_intern("tag"), 1, 1, 0);
/*
- * A +Symbol+ representing the tag class of this ASN1Data. Never +nil+.
+ * A Symbol representing the tag class of this ASN1Data. Never +nil+.
* See ASN1Data for possible values.
*/
rb_attr(cASN1Data, rb_intern("tag_class"), 1, 1, 0);
/*
- * Never +nil+. A +Boolean+ indicating whether the encoding was infinite
- * length (in the case of parsing) or whether an infinite length encoding
- * shall be used (in the encoding case).
- * In DER, every value has a finite length associated with it. But in
- * scenarios where large amounts of data need to be transferred it
- * might be desirable to have some kind of streaming support available.
+ * Never +nil+. A boolean value indicating whether the encoding uses
+ * indefinite length (in the case of parsing) or whether an indefinite
+ * length form shall be used (in the encoding case).
+ * In DER, every value uses definite length form. But in scenarios where
+ * large amounts of data need to be transferred it might be desirable to
+ * have some kind of streaming support available.
* For example, huge OCTET STRINGs are preferably sent in smaller-sized
* chunks, each at a time.
* This is possible in BER by setting the length bytes of an encoding
* to zero and by this indicating that the following value will be
- * sent in chunks. Infinite length encodings are always constructed.
+ * sent in chunks. Indefinite length encodings are always constructed.
* The end of such a stream of chunks is indicated by sending a EOC
- * (End of Content) tag. SETs and SEQUENCEs may use an infinite length
+ * (End of Content) tag. SETs and SEQUENCEs may use an indefinite length
* encoding, but also primitive types such as e.g. OCTET STRINGS or
* BIT STRINGS may leverage this functionality (cf. ITU-T X.690).
*/
- rb_attr(cASN1Data, rb_intern("infinite_length"), 1, 1, 0);
+ rb_attr(cASN1Data, rb_intern("indefinite_length"), 1, 1, 0);
+ rb_define_alias(cASN1Data, "infinite_length", "indefinite_length");
+ rb_define_alias(cASN1Data, "infinite_length=", "indefinite_length=");
rb_define_method(cASN1Data, "initialize", ossl_asn1data_initialize, 3);
rb_define_method(cASN1Data, "to_der", ossl_asn1data_to_der, 0);
/* Document-class: OpenSSL::ASN1::Primitive
*
* The parent class for all primitive encodings. Attributes are the same as
- * for ASN1Data, with the addition of +tagging+.
- * Primitive values can never be infinite length encodings, thus it is not
- * possible to set the +infinite_length+ attribute for Primitive and its
- * sub-classes.
+ * for ASN1Data, with the addition of _tagging_.
+ * Primitive values can never be encoded with indefinite length form, thus
+ * it is not possible to set the _indefinite_length_ attribute for Primitive
+ * and its sub-classes.
*
* == Primitive sub-classes and their mapping to Ruby classes
- * * OpenSSL::ASN1::EndOfContent <=> +value+ is always +nil+
- * * OpenSSL::ASN1::Boolean <=> +value+ is a +Boolean+
- * * OpenSSL::ASN1::Integer <=> +value+ is a +Number+
- * * OpenSSL::ASN1::BitString <=> +value+ is a +String+
- * * OpenSSL::ASN1::OctetString <=> +value+ is a +String+
- * * OpenSSL::ASN1::Null <=> +value+ is always +nil+
- * * OpenSSL::ASN1::Object <=> +value+ is a +String+
- * * OpenSSL::ASN1::Enumerated <=> +value+ is a +Number+
- * * OpenSSL::ASN1::UTF8String <=> +value+ is a +String+
- * * OpenSSL::ASN1::NumericString <=> +value+ is a +String+
- * * OpenSSL::ASN1::PrintableString <=> +value+ is a +String+
- * * OpenSSL::ASN1::T61String <=> +value+ is a +String+
- * * OpenSSL::ASN1::VideotexString <=> +value+ is a +String+
- * * OpenSSL::ASN1::IA5String <=> +value+ is a +String+
- * * OpenSSL::ASN1::UTCTime <=> +value+ is a +Time+
- * * OpenSSL::ASN1::GeneralizedTime <=> +value+ is a +Time+
- * * OpenSSL::ASN1::GraphicString <=> +value+ is a +String+
- * * OpenSSL::ASN1::ISO64String <=> +value+ is a +String+
- * * OpenSSL::ASN1::GeneralString <=> +value+ is a +String+
- * * OpenSSL::ASN1::UniversalString <=> +value+ is a +String+
- * * OpenSSL::ASN1::BMPString <=> +value+ is a +String+
+ * * OpenSSL::ASN1::EndOfContent <=> _value_ is always +nil+
+ * * OpenSSL::ASN1::Boolean <=> _value_ is +true+ or +false+
+ * * OpenSSL::ASN1::Integer <=> _value_ is an OpenSSL::BN
+ * * OpenSSL::ASN1::BitString <=> _value_ is a String
+ * * OpenSSL::ASN1::OctetString <=> _value_ is a String
+ * * OpenSSL::ASN1::Null <=> _value_ is always +nil+
+ * * OpenSSL::ASN1::Object <=> _value_ is a String
+ * * OpenSSL::ASN1::Enumerated <=> _value_ is an OpenSSL::BN
+ * * OpenSSL::ASN1::UTF8String <=> _value_ is a String
+ * * OpenSSL::ASN1::NumericString <=> _value_ is a String
+ * * OpenSSL::ASN1::PrintableString <=> _value_ is a String
+ * * OpenSSL::ASN1::T61String <=> _value_ is a String
+ * * OpenSSL::ASN1::VideotexString <=> _value_ is a String
+ * * OpenSSL::ASN1::IA5String <=> _value_ is a String
+ * * OpenSSL::ASN1::UTCTime <=> _value_ is a Time
+ * * OpenSSL::ASN1::GeneralizedTime <=> _value_ is a Time
+ * * OpenSSL::ASN1::GraphicString <=> _value_ is a String
+ * * OpenSSL::ASN1::ISO64String <=> _value_ is a String
+ * * OpenSSL::ASN1::GeneralString <=> _value_ is a String
+ * * OpenSSL::ASN1::UniversalString <=> _value_ is a String
+ * * OpenSSL::ASN1::BMPString <=> _value_ is a String
*
* == OpenSSL::ASN1::BitString
*
* === Additional attributes
- * +unused_bits+: if the underlying BIT STRING's
- * length is a multiple of 8 then +unused_bits+ is 0. Otherwise
- * +unused_bits+ indicates the number of bits that are to be ignored in
- * the final octet of the +BitString+'s +value+.
+ * _unused_bits_: if the underlying BIT STRING's
+ * length is a multiple of 8 then _unused_bits_ is 0. Otherwise
+ * _unused_bits_ indicates the number of bits that are to be ignored in
+ * the final octet of the BitString's _value_.
*
* == OpenSSL::ASN1::ObjectId
*
@@ -1809,15 +1701,15 @@ Init_ossl_asn1(void)
* parsed ASN1 encodings.
*
* === Additional attributes
- * * +sn+: the short name as defined in <openssl/objects.h>.
- * * +ln+: the long name as defined in <openssl/objects.h>.
- * * +oid+: the object identifier as a +String+, e.g. "1.2.3.4.5"
- * * +short_name+: alias for +sn+.
- * * +long_name+: alias for +ln+.
+ * * _sn_: the short name as defined in <openssl/objects.h>.
+ * * _ln_: the long name as defined in <openssl/objects.h>.
+ * * _oid_: the object identifier as a String, e.g. "1.2.3.4.5"
+ * * _short_name_: alias for _sn_.
+ * * _long_name_: alias for _ln_.
*
* == Examples
* With the Exception of OpenSSL::ASN1::EndOfContent, each Primitive class
- * constructor takes at least one parameter, the +value+.
+ * constructor takes at least one parameter, the _value_.
*
* === Creating EndOfContent
* eoc = OpenSSL::ASN1::EndOfContent.new
@@ -1831,19 +1723,20 @@ Init_ossl_asn1(void)
/*
* May be used as a hint for encoding a value either implicitly or
* explicitly by setting it either to +:IMPLICIT+ or to +:EXPLICIT+.
- * +tagging+ is not set when a ASN.1 structure is parsed using
+ * _tagging_ is not set when a ASN.1 structure is parsed using
* OpenSSL::ASN1.decode.
*/
rb_attr(cASN1Primitive, rb_intern("tagging"), 1, 1, Qtrue);
+ rb_undef_method(cASN1Primitive, "indefinite_length=");
rb_undef_method(cASN1Primitive, "infinite_length=");
rb_define_method(cASN1Primitive, "initialize", ossl_asn1_initialize, -1);
rb_define_method(cASN1Primitive, "to_der", ossl_asn1prim_to_der, 0);
/* Document-class: OpenSSL::ASN1::Constructive
*
- * The parent class for all constructed encodings. The +value+ attribute
- * of a Constructive is always an +Array+. Attributes are the same as
- * for ASN1Data, with the addition of +tagging+.
+ * The parent class for all constructed encodings. The _value_ attribute
+ * of a Constructive is always an Array. Attributes are the same as
+ * for ASN1Data, with the addition of _tagging_.
*
* == SET and SEQUENCE
*
@@ -1865,48 +1758,13 @@ Init_ossl_asn1(void)
* int = OpenSSL::ASN1::Integer.new(1)
* str = OpenSSL::ASN1::PrintableString.new('abc')
* set = OpenSSL::ASN1::Set.new( [ int, str ] )
- *
- * == Infinite length primitive values
- *
- * The only case where Constructive is used directly is for infinite
- * length encodings of primitive values. These encodings are always
- * constructed, with the contents of the +value+ +Array+ being either
- * UNIVERSAL non-infinite length partial encodings of the actual value
- * or again constructive encodings with infinite length (i.e. infinite
- * length primitive encodings may be constructed recursively with another
- * infinite length value within an already infinite length value). Each
- * partial encoding must be of the same UNIVERSAL type as the overall
- * encoding. The value of the overall encoding consists of the
- * concatenation of each partial encoding taken in sequence. The +value+
- * array of the outer infinite length value must end with a
- * OpenSSL::ASN1::EndOfContent instance.
- *
- * Please note that it is not possible to encode Constructive without
- * the +infinite_length+ attribute being set to +true+, use
- * OpenSSL::ASN1::Sequence or OpenSSL::ASN1::Set in these cases instead.
- *
- * === Example - Infinite length OCTET STRING
- * partial1 = OpenSSL::ASN1::OctetString.new("\x01")
- * partial2 = OpenSSL::ASN1::OctetString.new("\x02")
- * inf_octets = OpenSSL::ASN1::Constructive.new( [ partial1,
- * partial2,
- * OpenSSL::ASN1::EndOfContent.new ],
- * OpenSSL::ASN1::OCTET_STRING,
- * nil,
- * :UNIVERSAL )
- * # The real value of inf_octets is "\x01\x02", i.e. the concatenation
- * # of partial1 and partial2
- * inf_octets.infinite_length = true
- * der = inf_octets.to_der
- * asn1 = OpenSSL::ASN1.decode(der)
- * puts asn1.infinite_length # => true
*/
cASN1Constructive = rb_define_class_under(mASN1,"Constructive", cASN1Data);
rb_include_module(cASN1Constructive, rb_mEnumerable);
/*
* May be used as a hint for encoding a value either implicitly or
* explicitly by setting it either to +:IMPLICIT+ or to +:EXPLICIT+.
- * +tagging+ is not set when a ASN.1 structure is parsed using
+ * _tagging_ is not set when a ASN.1 structure is parsed using
* OpenSSL::ASN1.decode.
*/
rb_attr(cASN1Constructive, rb_intern("tagging"), 1, 1, Qtrue);
@@ -1963,8 +1821,10 @@ do{\
rb_attr(cASN1BitString, rb_intern("unused_bits"), 1, 1, 0);
rb_define_method(cASN1EndOfContent, "initialize", ossl_asn1eoc_initialize, 0);
+ rb_define_method(cASN1EndOfContent, "to_der", ossl_asn1eoc_to_der, 0);
class_tag_map = rb_hash_new();
+ rb_gc_register_mark_object(class_tag_map);
rb_hash_aset(class_tag_map, cASN1EndOfContent, INT2NUM(V_ASN1_EOC));
rb_hash_aset(class_tag_map, cASN1Boolean, INT2NUM(V_ASN1_BOOLEAN));
rb_hash_aset(class_tag_map, cASN1Integer, INT2NUM(V_ASN1_INTEGER));
@@ -1988,5 +1848,6 @@ do{\
rb_hash_aset(class_tag_map, cASN1GeneralString, INT2NUM(V_ASN1_GENERALSTRING));
rb_hash_aset(class_tag_map, cASN1UniversalString, INT2NUM(V_ASN1_UNIVERSALSTRING));
rb_hash_aset(class_tag_map, cASN1BMPString, INT2NUM(V_ASN1_BMPSTRING));
- rb_global_variable(&class_tag_map);
+
+ id_each = rb_intern_const("each");
}
diff --git a/ext/openssl/ossl_asn1.h b/ext/openssl/ossl_asn1.h
index d6a170c86c..939a96ce74 100644
--- a/ext/openssl/ossl_asn1.h
+++ b/ext/openssl/ossl_asn1.h
@@ -14,15 +14,11 @@
* ASN1_DATE conversions
*/
VALUE asn1time_to_time(const ASN1_TIME *);
-#if defined(HAVE_ASN1_TIME_ADJ)
/* Splits VALUE to seconds and offset days. VALUE is typically a Time or an
* Integer. This is used when updating ASN1_*TIME with ASN1_TIME_adj() or
* X509_time_adj_ex(). We can't use ASN1_TIME_set() and X509_time_adj() because
* they have the Year 2038 issue on sizeof(time_t) == 4 environment */
void ossl_time_split(VALUE, time_t *, int *);
-#else
-time_t time_to_time_t(VALUE);
-#endif
/*
* ASN1_STRING conversions
diff --git a/ext/openssl/ossl_bio.c b/ext/openssl/ossl_bio.c
index feaf229604..42833d901a 100644
--- a/ext/openssl/ossl_bio.c
+++ b/ext/openssl/ossl_bio.c
@@ -8,80 +8,35 @@
* (See the file 'LICENCE'.)
*/
#include "ossl.h"
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
BIO *
-ossl_obj2bio(VALUE obj)
+ossl_obj2bio(volatile VALUE *pobj)
{
+ VALUE obj = *pobj;
BIO *bio;
- if (RB_TYPE_P(obj, T_FILE)) {
- rb_io_t *fptr;
- FILE *fp;
- int fd;
-
- GetOpenFile(obj, fptr);
- rb_io_check_readable(fptr);
- if ((fd = rb_cloexec_dup(FPTR_TO_FD(fptr))) < 0){
- rb_sys_fail(0);
- }
- rb_update_max_fd(fd);
- if (!(fp = fdopen(fd, "r"))){
- int e = errno;
- close(fd);
- rb_syserr_fail(e, 0);
- }
- if (!(bio = BIO_new_fp(fp, BIO_CLOSE))){
- fclose(fp);
- ossl_raise(eOSSLError, NULL);
- }
- }
- else {
- StringValue(obj);
- bio = BIO_new_mem_buf(RSTRING_PTR(obj), RSTRING_LENINT(obj));
- if (!bio) ossl_raise(eOSSLError, NULL);
- }
-
+ if (RB_TYPE_P(obj, T_FILE))
+ obj = rb_funcallv(obj, rb_intern("read"), 0, NULL);
+ StringValue(obj);
+ bio = BIO_new_mem_buf(RSTRING_PTR(obj), RSTRING_LENINT(obj));
+ if (!bio)
+ ossl_raise(eOSSLError, "BIO_new_mem_buf");
+ *pobj = obj;
return bio;
}
-BIO *
-ossl_protect_obj2bio(VALUE obj, int *status)
-{
- BIO *ret = NULL;
- ret = (BIO*)rb_protect((VALUE (*)(VALUE))ossl_obj2bio, obj, status);
- return ret;
-}
-
VALUE
-ossl_membio2str0(BIO *bio)
+ossl_membio2str(BIO *bio)
{
VALUE ret;
+ int state;
BUF_MEM *buf;
BIO_get_mem_ptr(bio, &buf);
- ret = rb_str_new(buf->data, buf->length);
-
- return ret;
-}
-
-VALUE
-ossl_protect_membio2str(BIO *bio, int *status)
-{
- return rb_protect((VALUE (*)(VALUE))ossl_membio2str0, (VALUE)bio, status);
-}
-
-VALUE
-ossl_membio2str(BIO *bio)
-{
- VALUE ret;
- int status = 0;
-
- ret = ossl_protect_membio2str(bio, &status);
+ ret = ossl_str_new(buf->data, buf->length, &state);
BIO_free(bio);
- if(status) rb_jump_tag(status);
+ if (state)
+ rb_jump_tag(state);
return ret;
}
diff --git a/ext/openssl/ossl_bio.h b/ext/openssl/ossl_bio.h
index 1705d0ac89..da68c5e5a2 100644
--- a/ext/openssl/ossl_bio.h
+++ b/ext/openssl/ossl_bio.h
@@ -10,10 +10,7 @@
#if !defined(_OSSL_BIO_H_)
#define _OSSL_BIO_H_
-BIO *ossl_obj2bio(VALUE);
-BIO *ossl_protect_obj2bio(VALUE,int*);
-VALUE ossl_membio2str0(BIO*);
+BIO *ossl_obj2bio(volatile VALUE *);
VALUE ossl_membio2str(BIO*);
-VALUE ossl_protect_membio2str(BIO*,int*);
#endif
diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c
index 19a868c334..6f0064e966 100644
--- a/ext/openssl/ossl_bn.c
+++ b/ext/openssl/ossl_bn.c
@@ -26,11 +26,6 @@
} \
} while (0)
-#define SafeGetBN(obj, bn) do { \
- OSSL_Check_Kind((obj), cBN); \
- GetBN((obj), (bn)); \
-} while (0)
-
static void
ossl_bn_free(void *ptr)
{
@@ -120,30 +115,34 @@ integer_to_bnptr(VALUE obj, BIGNUM *orig)
return bn;
}
-static BIGNUM *
-try_convert_to_bnptr(VALUE obj)
+static VALUE
+try_convert_to_bn(VALUE obj)
{
- BIGNUM *bn = NULL;
- VALUE newobj;
+ BIGNUM *bn;
+ VALUE newobj = Qnil;
- if (rb_obj_is_kind_of(obj, cBN)) {
- GetBN(obj, bn);
- }
- else if (RB_INTEGER_TYPE_P(obj)) {
- newobj = NewBN(cBN); /* Handle potencial mem leaks */
+ if (rb_obj_is_kind_of(obj, cBN))
+ return obj;
+ if (RB_INTEGER_TYPE_P(obj)) {
+ newobj = NewBN(cBN); /* Handle potential mem leaks */
bn = integer_to_bnptr(obj, NULL);
SetBN(newobj, bn);
}
- return bn;
+ return newobj;
}
BIGNUM *
-GetBNPtr(VALUE obj)
+ossl_bn_value_ptr(volatile VALUE *ptr)
{
- BIGNUM *bn = try_convert_to_bnptr(obj);
- if (!bn)
+ VALUE tmp;
+ BIGNUM *bn;
+
+ tmp = try_convert_to_bn(*ptr);
+ if (NIL_P(tmp))
ossl_raise(rb_eTypeError, "Cannot convert into OpenSSL::BN");
+ GetBN(tmp, bn);
+ *ptr = tmp;
return bn;
}
@@ -172,8 +171,7 @@ ossl_bn_alloc(VALUE klass)
return obj;
}
-/* Document-method: OpenSSL::BN.new
- *
+/*
* call-seq:
* OpenSSL::BN.new => aBN
* OpenSSL::BN.new(bn) => aBN
@@ -181,7 +179,7 @@ ossl_bn_alloc(VALUE klass)
* OpenSSL::BN.new(string) => aBN
* OpenSSL::BN.new(string, 0 | 2 | 10 | 16) => aBN
*
- * Construct a new OpenSSL BigNum object.
+ * Construct a new OpenSSL BIGNUM object.
*/
static VALUE
ossl_bn_initialize(int argc, VALUE *argv, VALUE self)
@@ -189,6 +187,7 @@ ossl_bn_initialize(int argc, VALUE *argv, VALUE self)
BIGNUM *bn;
VALUE str, bs;
int base = 10;
+ char *ptr;
if (rb_scan_args(argc, argv, "11", &str, &bs) == 2) {
base = NUM2INT(bs);
@@ -215,12 +214,14 @@ ossl_bn_initialize(int argc, VALUE *argv, VALUE self)
GetBN(self, bn);
switch (base) {
case 0:
- if (!BN_mpi2bn((unsigned char *)StringValuePtr(str), RSTRING_LENINT(str), bn)) {
+ ptr = StringValuePtr(str);
+ if (!BN_mpi2bn((unsigned char *)ptr, RSTRING_LENINT(str), bn)) {
ossl_raise(eBNError, NULL);
}
break;
case 2:
- if (!BN_bin2bn((unsigned char *)StringValuePtr(str), RSTRING_LENINT(str), bn)) {
+ ptr = StringValuePtr(str);
+ if (!BN_bin2bn((unsigned char *)ptr, RSTRING_LENINT(str), bn)) {
ossl_raise(eBNError, NULL);
}
break;
@@ -246,7 +247,7 @@ ossl_bn_initialize(int argc, VALUE *argv, VALUE self)
* bn.to_s(base) => string
*
* === Parameters
- * * +base+ - integer
+ * * _base_ - Integer
* Valid values:
* * 0 - MPI
* * 2 - binary
@@ -373,6 +374,21 @@ BIGNUM_BOOL1(is_one)
*/
BIGNUM_BOOL1(is_odd)
+/*
+ * call-seq:
+ * bn.negative? => true | false
+ */
+static VALUE
+ossl_bn_is_negative(VALUE self)
+{
+ BIGNUM *bn;
+
+ GetBN(self, bn);
+ if (BN_is_zero(bn))
+ return Qfalse;
+ return BN_is_negative(bn) ? Qtrue : Qfalse;
+}
+
#define BIGNUM_1c(func) \
static VALUE \
ossl_bn_##func(VALUE self) \
@@ -380,7 +396,7 @@ BIGNUM_BOOL1(is_odd)
BIGNUM *bn, *result; \
VALUE obj; \
GetBN(self, bn); \
- obj = NewBN(CLASS_OF(self)); \
+ obj = NewBN(rb_obj_class(self)); \
if (!(result = BN_new())) { \
ossl_raise(eBNError, NULL); \
} \
@@ -406,7 +422,7 @@ BIGNUM_1c(sqr)
BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \
VALUE obj; \
GetBN(self, bn1); \
- obj = NewBN(CLASS_OF(self)); \
+ obj = NewBN(rb_obj_class(self)); \
if (!(result = BN_new())) { \
ossl_raise(eBNError, NULL); \
} \
@@ -439,7 +455,7 @@ BIGNUM_2(sub)
BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \
VALUE obj; \
GetBN(self, bn1); \
- obj = NewBN(CLASS_OF(self)); \
+ obj = NewBN(rb_obj_class(self)); \
if (!(result = BN_new())) { \
ossl_raise(eBNError, NULL); \
} \
@@ -494,7 +510,6 @@ BIGNUM_2c(mod_sqr)
BIGNUM_2c(mod_inverse)
/*
- * Document-method: OpenSSL::BN#/
* call-seq:
* bn1 / bn2 => [result, remainder]
*
@@ -504,12 +519,13 @@ static VALUE
ossl_bn_div(VALUE self, VALUE other)
{
BIGNUM *bn1, *bn2 = GetBNPtr(other), *r1, *r2;
- VALUE obj1, obj2;
+ VALUE klass, obj1, obj2;
GetBN(self, bn1);
- obj1 = NewBN(CLASS_OF(self));
- obj2 = NewBN(CLASS_OF(self));
+ klass = rb_obj_class(self);
+ obj1 = NewBN(klass);
+ obj2 = NewBN(klass);
if (!(r1 = BN_new())) {
ossl_raise(eBNError, NULL);
}
@@ -536,7 +552,7 @@ ossl_bn_div(VALUE self, VALUE other)
BIGNUM *bn3 = GetBNPtr(other2), *result; \
VALUE obj; \
GetBN(self, bn1); \
- obj = NewBN(CLASS_OF(self)); \
+ obj = NewBN(rb_obj_class(self)); \
if (!(result = BN_new())) { \
ossl_raise(eBNError, NULL); \
} \
@@ -609,12 +625,11 @@ BIGNUM_BIT(clear_bit)
*/
BIGNUM_BIT(mask_bits)
-/* Document-method: OpenSSL::BN#bit_set?
+/*
* call-seq:
* bn.bit_set?(bit) => true | false
*
- * Returns boolean of whether +bit+ is set.
- * Bitwise operations for openssl BIGNUMs.
+ * Tests bit _bit_ in _bn_ and returns +true+ if set, +false+ if not set.
*/
static VALUE
ossl_bn_is_bit_set(VALUE self, VALUE bit)
@@ -639,7 +654,7 @@ ossl_bn_is_bit_set(VALUE self, VALUE bit)
VALUE obj; \
b = NUM2INT(bits); \
GetBN(self, bn); \
- obj = NewBN(CLASS_OF(self)); \
+ obj = NewBN(rb_obj_class(self)); \
if (!(result = BN_new())) { \
ossl_raise(eBNError, NULL); \
} \
@@ -769,15 +784,15 @@ BIGNUM_RAND_RANGE(pseudo_rand)
* call-seq:
* BN.generate_prime(bits, [, safe [, add [, rem]]]) => bn
*
- * Generates a random prime number of bit length +bits+. If +safe+ is true,
- * generates a safe prime. If +add+ is specified, generates a prime that
+ * Generates a random prime number of bit length _bits_. If _safe_ is set to
+ * +true+, generates a safe prime. If _add_ is specified, generates a prime that
* fulfills condition <tt>p % add = rem</tt>.
*
* === Parameters
- * * +bits+ - integer
- * * +safe+ - boolean
- * * +add+ - BN
- * * +rem+ - BN
+ * * _bits_ - integer
+ * * _safe_ - boolean
+ * * _add_ - BN
+ * * _rem_ - BN
*/
static VALUE
ossl_bn_s_generate_prime(int argc, VALUE *argv, VALUE klass)
@@ -851,6 +866,37 @@ ossl_bn_copy(VALUE self, VALUE other)
return self;
}
+/*
+ * call-seq:
+ * +bn -> aBN
+ */
+static VALUE
+ossl_bn_uplus(VALUE self)
+{
+ return self;
+}
+
+/*
+ * call-seq:
+ * -bn -> aBN
+ */
+static VALUE
+ossl_bn_uminus(VALUE self)
+{
+ VALUE obj;
+ BIGNUM *bn1, *bn2;
+
+ GetBN(self, bn1);
+ obj = NewBN(cBN);
+ bn2 = BN_dup(bn1);
+ if (!bn2)
+ ossl_raise(eBNError, "BN_dup");
+ SetBN(obj, bn2);
+ BN_set_negative(bn2, !BN_is_negative(bn2));
+
+ return obj;
+}
+
#define BIGNUM_CMP(func) \
static VALUE \
ossl_bn_##func(VALUE self, VALUE other) \
@@ -883,7 +929,7 @@ BIGNUM_CMP(ucmp)
* call-seq:
* bn == obj => true or false
*
- * Returns +true+ only if +obj+ has the same value as +bn+. Contrast this
+ * Returns +true+ only if _obj_ has the same value as _bn_. Contrast this
* with OpenSSL::BN#eql?, which requires obj to be OpenSSL::BN.
*/
static VALUE
@@ -892,10 +938,12 @@ ossl_bn_eq(VALUE self, VALUE other)
BIGNUM *bn1, *bn2;
GetBN(self, bn1);
- /* BNPtr may raise, so we can't use here */
- bn2 = try_convert_to_bnptr(other);
+ other = try_convert_to_bn(other);
+ if (NIL_P(other))
+ return Qfalse;
+ GetBN(other, bn2);
- if (bn2 && !BN_cmp(bn1, bn2)) {
+ if (!BN_cmp(bn1, bn2)) {
return Qtrue;
}
return Qfalse;
@@ -906,7 +954,7 @@ ossl_bn_eq(VALUE self, VALUE other)
* bn.eql?(obj) => true or false
*
* Returns <code>true</code> only if <i>obj</i> is a
- * <code>OpenSSL::BN</code> with the same value as <i>big</i>. Contrast this
+ * <code>OpenSSL::BN</code> with the same value as <i>bn</i>. Contrast this
* with OpenSSL::BN#==, which performs type conversions.
*/
static VALUE
@@ -934,20 +982,20 @@ static VALUE
ossl_bn_hash(VALUE self)
{
BIGNUM *bn;
- VALUE hash;
+ VALUE tmp, hash;
unsigned char *buf;
int len;
GetBN(self, bn);
len = BN_num_bytes(bn);
- buf = xmalloc(len);
+ buf = ALLOCV(tmp, len);
if (BN_bn2bin(bn, buf) != len) {
- xfree(buf);
- ossl_raise(eBNError, NULL);
+ ALLOCV_END(tmp);
+ ossl_raise(eBNError, "BN_bn2bin");
}
- hash = INT2FIX(rb_memhash(buf, len));
- xfree(buf);
+ hash = ST2FIX(rb_memhash(buf, len));
+ ALLOCV_END(tmp);
return hash;
}
@@ -957,12 +1005,12 @@ ossl_bn_hash(VALUE self)
* bn.prime? => true | false
* bn.prime?(checks) => true | false
*
- * Performs a Miller-Rabin probabilistic primality test with +checks+
- * iterations. If +nchecks+ is not specified, a number of iterations is used
+ * Performs a Miller-Rabin probabilistic primality test with _checks_
+ * iterations. If _checks_ is not specified, a number of iterations is used
* that yields a false positive rate of at most 2^-80 for random input.
*
* === Parameters
- * * +checks+ - integer
+ * * _checks_ - integer
*/
static VALUE
ossl_bn_is_prime(int argc, VALUE *argv, VALUE self)
@@ -997,8 +1045,8 @@ ossl_bn_is_prime(int argc, VALUE *argv, VALUE self)
* first attempts trial divisions with some small primes.
*
* === Parameters
- * * +checks+ - integer
- * * +trial_div+ - boolean
+ * * _checks_ - integer
+ * * _trial_div_ - boolean
*/
static VALUE
ossl_bn_is_prime_fasttest(int argc, VALUE *argv, VALUE self)
@@ -1052,7 +1100,7 @@ Init_ossl_bn(void)
rb_define_alloc_func(cBN, ossl_bn_alloc);
rb_define_method(cBN, "initialize", ossl_bn_initialize, -1);
- rb_define_copy_func(cBN, ossl_bn_copy);
+ rb_define_method(cBN, "initialize_copy", ossl_bn_copy, 1);
rb_define_method(cBN, "copy", ossl_bn_copy, 1);
/* swap (=coerce?) */
@@ -1061,6 +1109,9 @@ Init_ossl_bn(void)
rb_define_method(cBN, "num_bits", ossl_bn_num_bits, 0);
/* num_bits_word */
+ rb_define_method(cBN, "+@", ossl_bn_uplus, 0);
+ rb_define_method(cBN, "-@", ossl_bn_uminus, 0);
+
rb_define_method(cBN, "+", ossl_bn_add, 1);
rb_define_method(cBN, "-", ossl_bn_sub, 1);
rb_define_method(cBN, "*", ossl_bn_mul, 1);
@@ -1094,6 +1145,7 @@ Init_ossl_bn(void)
rb_define_method(cBN, "one?", ossl_bn_is_one, 0);
/* is_word */
rb_define_method(cBN, "odd?", ossl_bn_is_odd, 0);
+ rb_define_method(cBN, "negative?", ossl_bn_is_negative, 0);
/* zero
* one
diff --git a/ext/openssl/ossl_bn.h b/ext/openssl/ossl_bn.h
index 4cd9d0600a..a19ba19487 100644
--- a/ext/openssl/ossl_bn.h
+++ b/ext/openssl/ossl_bn.h
@@ -15,8 +15,10 @@ extern VALUE eBNError;
extern BN_CTX *ossl_bn_ctx;
+#define GetBNPtr(obj) ossl_bn_value_ptr(&(obj))
+
VALUE ossl_bn_new(const BIGNUM *);
-BIGNUM *GetBNPtr(VALUE);
+BIGNUM *ossl_bn_value_ptr(volatile VALUE *);
void Init_ossl_bn(void);
diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c
index 57bc3cfa08..0840c84a71 100644
--- a/ext/openssl/ossl_cipher.c
+++ b/ext/openssl/ossl_cipher.c
@@ -23,20 +23,16 @@
#define GetCipher(obj, ctx) do { \
GetCipherInit((obj), (ctx)); \
if (!(ctx)) { \
- ossl_raise(rb_eRuntimeError, "Cipher not inititalized!"); \
+ ossl_raise(rb_eRuntimeError, "Cipher not initialized!"); \
} \
} while (0)
-#define SafeGetCipher(obj, ctx) do { \
- OSSL_Check_Kind((obj), cCipher); \
- GetCipher((obj), (ctx)); \
-} while (0)
/*
* Classes
*/
VALUE cCipher;
VALUE eCipherError;
-static ID id_auth_tag_len;
+static ID id_auth_tag_len, id_key_set;
static VALUE ossl_cipher_alloc(VALUE klass);
static void ossl_cipher_free(void *ptr);
@@ -53,7 +49,7 @@ static const rb_data_type_t ossl_cipher_type = {
* PUBLIC
*/
const EVP_CIPHER *
-GetCipherPtr(VALUE obj)
+ossl_evp_get_cipherbyname(VALUE obj)
{
if (rb_obj_is_kind_of(obj, cCipher)) {
EVP_CIPHER_CTX *ctx;
@@ -108,7 +104,7 @@ ossl_cipher_alloc(VALUE klass)
* call-seq:
* Cipher.new(string) -> cipher
*
- * The string must contain a valid cipher name like "AES-128-CBC" or "3DES".
+ * The string must be a valid cipher name like "AES-128-CBC" or "3DES".
*
* A list of cipher names is available by calling OpenSSL::Cipher.ciphers.
*/
@@ -118,27 +114,17 @@ ossl_cipher_initialize(VALUE self, VALUE str)
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *cipher;
char *name;
- unsigned char dummy_key[EVP_MAX_KEY_LENGTH] = { 0 };
name = StringValueCStr(str);
GetCipherInit(self, ctx);
if (ctx) {
- ossl_raise(rb_eRuntimeError, "Cipher already inititalized!");
+ ossl_raise(rb_eRuntimeError, "Cipher already initialized!");
}
AllocCipher(self, ctx);
if (!(cipher = EVP_get_cipherbyname(name))) {
ossl_raise(rb_eRuntimeError, "unsupported cipher algorithm (%"PRIsVALUE")", str);
}
- /*
- * EVP_CipherInit_ex() allows to specify NULL to key and IV, however some
- * ciphers don't handle well (OpenSSL's bug). [Bug #2768]
- *
- * The EVP which has EVP_CIPH_RAND_KEY flag (such as DES3) allows
- * uninitialized key, but other EVPs (such as AES) does not allow it.
- * Calling EVP_CipherUpdate() without initializing key causes SEGV so we
- * set the data filled with "\0" as the key by default.
- */
- if (EVP_CipherInit_ex(ctx, cipher, NULL, dummy_key, NULL, -1) != 1)
+ if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1)
ossl_raise(eCipherError, NULL);
return self;
@@ -156,7 +142,7 @@ ossl_cipher_copy(VALUE self, VALUE other)
if (!ctx1) {
AllocCipher(self, ctx1);
}
- SafeGetCipher(other, ctx2);
+ GetCipher(other, ctx2);
if (EVP_CIPHER_CTX_copy(ctx1, ctx2) != 1)
ossl_raise(eCipherError, NULL);
@@ -251,6 +237,9 @@ ossl_cipher_init(int argc, VALUE *argv, VALUE self, int mode)
ossl_raise(eCipherError, NULL);
}
+ if (p_key)
+ rb_ivar_set(self, id_key_set, Qtrue);
+
return self;
}
@@ -303,9 +292,9 @@ ossl_cipher_decrypt(int argc, VALUE *argv, VALUE self)
* OpenSSL::PKCS5 instead.
*
* === Parameters
- * * +salt+ must be an 8 byte string if provided.
- * * +iterations+ is an integer with a default of 2048.
- * * +digest+ is a Digest object that defaults to 'MD5'
+ * * _salt_ must be an 8 byte string if provided.
+ * * _iterations_ is an integer with a default of 2048.
+ * * _digest_ is a Digest object that defaults to 'MD5'
*
* A minimum of 1000 iterations is recommended.
*
@@ -328,7 +317,9 @@ ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self)
salt = (unsigned char *)RSTRING_PTR(vsalt);
}
iter = NIL_P(viter) ? 2048 : NUM2INT(viter);
- digest = NIL_P(vdigest) ? EVP_md5() : GetDigestPtr(vdigest);
+ if (iter <= 0)
+ rb_raise(rb_eArgError, "iterations must be a positive integer");
+ digest = NIL_P(vdigest) ? EVP_md5() : ossl_evp_get_digestbyname(vdigest);
GetCipher(self, ctx);
EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), digest, salt,
(unsigned char *)RSTRING_PTR(vpass), RSTRING_LENINT(vpass), iter, key, iv);
@@ -337,6 +328,8 @@ ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self)
OPENSSL_cleanse(key, sizeof key);
OPENSSL_cleanse(iv, sizeof iv);
+ rb_ivar_set(self, id_key_set, Qtrue);
+
return Qnil;
}
@@ -370,12 +363,12 @@ ossl_cipher_update_long(EVP_CIPHER_CTX *ctx, unsigned char *out, long *out_len_p
* cipher.update(data [, buffer]) -> string or buffer
*
* Encrypts data in a streaming fashion. Hand consecutive blocks of data
- * to the +update+ method in order to encrypt it. Returns the encrypted
+ * to the #update method in order to encrypt it. Returns the encrypted
* data chunk. When done, the output of Cipher#final should be additionally
* added to the result.
*
- * If +buffer+ is given, the encryption/decryption result will be written to
- * it. +buffer+ will be resized automatically.
+ * If _buffer_ is given, the encryption/decryption result will be written to
+ * it. _buffer_ will be resized automatically.
*/
static VALUE
ossl_cipher_update(int argc, VALUE *argv, VALUE self)
@@ -387,6 +380,9 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self)
rb_scan_args(argc, argv, "11", &data, &str);
+ if (!RTEST(rb_attr_get(self, id_key_set)))
+ ossl_raise(eCipherError, "key not set");
+
StringValue(data);
in = (unsigned char *)RSTRING_PTR(data);
if ((in_len = RSTRING_LEN(data)) == 0)
@@ -420,7 +416,7 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self)
* Returns the remaining data held in the cipher object. Further calls to
* Cipher#update or Cipher#final will return garbage. This call should always
* be made as the last call of an encryption or decryption operation, after
- * after having fed the entire plaintext or ciphertext to the Cipher instance.
+ * having fed the entire plaintext or ciphertext to the Cipher instance.
*
* If an authenticated cipher was used, a CipherError is raised if the tag
* could not be authenticated successfully. Only call this method after
@@ -488,6 +484,8 @@ ossl_cipher_set_key(VALUE self, VALUE key)
if (EVP_CipherInit_ex(ctx, NULL, NULL, (unsigned char *)RSTRING_PTR(key), NULL, -1) != 1)
ossl_raise(eCipherError, NULL);
+ rb_ivar_set(self, id_key_set, Qtrue);
+
return key;
}
@@ -502,9 +500,6 @@ ossl_cipher_set_key(VALUE self, VALUE key)
* Cipher#random_iv to create a secure random IV.
*
* Only call this method after calling Cipher#encrypt or Cipher#decrypt.
- *
- * If not explicitly set, the OpenSSL default of an all-zeroes ("\\0") IV is
- * used.
*/
static VALUE
ossl_cipher_set_iv(VALUE self, VALUE iv)
@@ -515,10 +510,8 @@ ossl_cipher_set_iv(VALUE self, VALUE iv)
StringValue(iv);
GetCipher(self, ctx);
-#if defined(HAVE_AUTHENTICATED_ENCRYPTION)
- if (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)
+ if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)
iv_len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx);
-#endif
if (!iv_len)
iv_len = EVP_CIPHER_CTX_iv_length(ctx);
if (RSTRING_LEN(iv) != iv_len)
@@ -530,7 +523,23 @@ ossl_cipher_set_iv(VALUE self, VALUE iv)
return iv;
}
-#ifdef HAVE_AUTHENTICATED_ENCRYPTION
+/*
+ * call-seq:
+ * cipher.authenticated? -> true | false
+ *
+ * Indicated whether this Cipher instance uses an Authenticated Encryption
+ * mode.
+ */
+static VALUE
+ossl_cipher_is_authenticated(VALUE self)
+{
+ EVP_CIPHER_CTX *ctx;
+
+ GetCipher(self, ctx);
+
+ return (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) ? Qtrue : Qfalse;
+}
+
/*
* call-seq:
* cipher.auth_data = string -> string
@@ -562,6 +571,8 @@ ossl_cipher_set_auth_data(VALUE self, VALUE data)
in_len = RSTRING_LEN(data);
GetCipher(self, ctx);
+ if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER))
+ ossl_raise(eCipherError, "AEAD not supported by this cipher");
if (!ossl_cipher_update_long(ctx, NULL, &out_len, in, in_len))
ossl_raise(eCipherError, "couldn't set additional authenticated data");
@@ -576,8 +587,8 @@ ossl_cipher_set_auth_data(VALUE self, VALUE data)
* Gets the authentication tag generated by Authenticated Encryption Cipher
* modes (GCM for example). This tag may be stored along with the ciphertext,
* then set on the decryption cipher to authenticate the contents of the
- * ciphertext against changes. If the optional integer parameter +tag_len+ is
- * given, the returned tag will be +tag_len+ bytes long. If the parameter is
+ * ciphertext against changes. If the optional integer parameter _tag_len_ is
+ * given, the returned tag will be _tag_len_ bytes long. If the parameter is
* omitted, the default length of 16 bytes or the length previously set by
* #auth_tag_len= will be used. For maximum security, the longest possible
* should be chosen.
@@ -599,7 +610,7 @@ ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self)
GetCipher(self, ctx);
- if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER))
+ if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER))
ossl_raise(eCipherError, "authentication tag not supported by this cipher");
ret = rb_str_new(NULL, tag_len);
@@ -613,13 +624,11 @@ ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self)
* call-seq:
* cipher.auth_tag = string -> string
*
- * Sets the authentication tag to verify the contents of the
- * ciphertext. The tag must be set after calling Cipher#decrypt,
- * Cipher#key= and Cipher#iv=, but before assigning the associated
- * authenticated data using Cipher#auth_data= and of course, before
- * decrypting any of the ciphertext. After all decryption is
- * performed, the tag is verified automatically in the call to
- * Cipher#final.
+ * Sets the authentication tag to verify the integrity of the ciphertext.
+ * This can be called only when the cipher supports AE. The tag must be set
+ * after calling Cipher#decrypt, Cipher#key= and Cipher#iv=, but before
+ * calling Cipher#final. After all decryption is performed, the tag is
+ * verified automatically in the call to Cipher#final.
*
* For OCB mode, the tag length must be supplied with #auth_tag_len=
* beforehand.
@@ -636,7 +645,7 @@ ossl_cipher_set_auth_tag(VALUE self, VALUE vtag)
tag_len = RSTRING_LENINT(vtag);
GetCipher(self, ctx);
- if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER))
+ if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER))
ossl_raise(eCipherError, "authentication tag not supported by this cipher");
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag))
@@ -663,7 +672,7 @@ ossl_cipher_set_auth_tag_len(VALUE self, VALUE vlen)
EVP_CIPHER_CTX *ctx;
GetCipher(self, ctx);
- if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER))
+ if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER))
ossl_raise(eCipherError, "AEAD not supported by this cipher");
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, NULL))
@@ -676,23 +685,6 @@ ossl_cipher_set_auth_tag_len(VALUE self, VALUE vlen)
}
/*
- * call-seq:
- * cipher.authenticated? -> boolean
- *
- * Indicated whether this Cipher instance uses an Authenticated Encryption
- * mode.
- */
-static VALUE
-ossl_cipher_is_authenticated(VALUE self)
-{
- EVP_CIPHER_CTX *ctx;
-
- GetCipher(self, ctx);
-
- return (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) ? Qtrue : Qfalse;
-}
-
-/*
* call-seq:
* cipher.iv_len = integer -> integer
*
@@ -707,7 +699,7 @@ ossl_cipher_set_iv_length(VALUE self, VALUE iv_length)
EVP_CIPHER_CTX *ctx;
GetCipher(self, ctx);
- if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER))
+ if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER))
ossl_raise(eCipherError, "cipher does not support AEAD");
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, len, NULL))
@@ -721,14 +713,6 @@ ossl_cipher_set_iv_length(VALUE self, VALUE iv_length)
return iv_length;
}
-#else
-#define ossl_cipher_set_auth_data rb_f_notimplement
-#define ossl_cipher_get_auth_tag rb_f_notimplement
-#define ossl_cipher_set_auth_tag rb_f_notimplement
-#define ossl_cipher_set_auth_tag_len rb_f_notimplement
-#define ossl_cipher_is_authenticated rb_f_notimplement
-#define ossl_cipher_set_iv_length rb_f_notimplement
-#endif
/*
* call-seq:
@@ -806,10 +790,8 @@ ossl_cipher_iv_length(VALUE self)
int len = 0;
GetCipher(self, ctx);
-#if defined(HAVE_AUTHENTICATED_ENCRYPTION)
- if (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)
+ if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)
len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx);
-#endif
if (!len)
len = EVP_CIPHER_CTX_iv_length(ctx);
@@ -939,12 +921,10 @@ Init_ossl_cipher(void)
* you absolutely need it</b>
*
* Because of this, you will end up with a mode that explicitly requires
- * an IV in any case. Note that for backwards compatibility reasons,
- * setting an IV is not explicitly mandated by the Cipher API. If not
- * set, OpenSSL itself defaults to an all-zeroes IV ("\\0", not the
- * character). Although the IV can be seen as public information, i.e.
- * it may be transmitted in public once generated, it should still stay
- * unpredictable to prevent certain kinds of attacks. Therefore, ideally
+ * an IV in any case. Although the IV can be seen as public information,
+ * i.e. it may be transmitted in public once generated, it should still
+ * stay unpredictable to prevent certain kinds of attacks. Therefore,
+ * ideally
*
* <b>Always create a secure random IV for every encryption of your
* Cipher</b>
@@ -1022,10 +1002,10 @@ Init_ossl_cipher(void)
* encryption and later decryption, the OpenSSL library still requires a
* value to be set - "" may be used in case none is available.
*
- * An example using the GCM (Galois/Counter Mode). You have 16 bytes +key+,
- * 12 bytes (96 bits) +nonce+ and the associated data +auth_data+. Be sure
- * not to reuse the +key+ and +nonce+ pair. Reusing an nonce ruins the
- * security gurantees of GCM mode.
+ * An example using the GCM (Galois/Counter Mode). You have 16 bytes _key_,
+ * 12 bytes (96 bits) _nonce_ and the associated data _auth_data_. Be sure
+ * not to reuse the _key_ and _nonce_ pair. Reusing an nonce ruins the
+ * security guarantees of GCM mode.
*
* cipher = OpenSSL::Cipher::AES.new(128, :GCM).encrypt
* cipher.key = key
@@ -1035,8 +1015,8 @@ Init_ossl_cipher(void)
* encrypted = cipher.update(data) + cipher.final
* tag = cipher.auth_tag # produces 16 bytes tag by default
*
- * Now you are the receiver. You know the +key+ and have received +nonce+,
- * +auth_data+, +encrypted+ and +tag+ through an untrusted network. Note
+ * Now you are the receiver. You know the _key_ and have received _nonce_,
+ * _auth_data_, _encrypted_ and _tag_ through an untrusted network. Note
* that GCM accepts an arbitrary length tag between 1 and 16 bytes. You may
* additionally need to check that the received tag has the correct length,
* or you allow attackers to forge a valid single byte tag for the tampered
@@ -1057,7 +1037,7 @@ Init_ossl_cipher(void)
eCipherError = rb_define_class_under(cCipher, "CipherError", eOSSLError);
rb_define_alloc_func(cCipher, ossl_cipher_alloc);
- rb_define_copy_func(cCipher, ossl_cipher_copy);
+ rb_define_method(cCipher, "initialize_copy", ossl_cipher_copy, 1);
rb_define_module_function(cCipher, "ciphers", ossl_s_ciphers, 0);
rb_define_method(cCipher, "initialize", ossl_cipher_initialize, 1);
rb_define_method(cCipher, "reset", ossl_cipher_reset, 0);
@@ -1082,4 +1062,5 @@ Init_ossl_cipher(void)
rb_define_method(cCipher, "padding=", ossl_cipher_set_padding, 1);
id_auth_tag_len = rb_intern_const("auth_tag_len");
+ id_key_set = rb_intern_const("key_set");
}
diff --git a/ext/openssl/ossl_cipher.h b/ext/openssl/ossl_cipher.h
index c444089fc2..2392d41c6a 100644
--- a/ext/openssl/ossl_cipher.h
+++ b/ext/openssl/ossl_cipher.h
@@ -13,7 +13,7 @@
extern VALUE cCipher;
extern VALUE eCipherError;
-const EVP_CIPHER *GetCipherPtr(VALUE);
+const EVP_CIPHER *ossl_evp_get_cipherbyname(VALUE);
VALUE ossl_cipher_new(const EVP_CIPHER *);
void Init_ossl_cipher(void);
diff --git a/ext/openssl/ossl_config.c b/ext/openssl/ossl_config.c
index ebf6ae2a3d..28392e208c 100644
--- a/ext/openssl/ossl_config.c
+++ b/ext/openssl/ossl_config.c
@@ -41,7 +41,7 @@ DupConfigPtr(VALUE obj)
OSSL_Check_Kind(obj, cConfig);
str = rb_funcall(obj, rb_intern("to_s"), 0);
- bio = ossl_obj2bio(str);
+ bio = ossl_obj2bio(&str);
conf = NCONF_new(NULL);
if(!conf){
BIO_free(bio);
diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c
index 44d961833d..112ce33647 100644
--- a/ext/openssl/ossl_digest.c
+++ b/ext/openssl/ossl_digest.c
@@ -15,10 +15,6 @@
ossl_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \
} \
} while (0)
-#define SafeGetDigest(obj, ctx) do { \
- OSSL_Check_Kind((obj), cDigest); \
- GetDigest((obj), (ctx)); \
-} while (0)
/*
* Classes
@@ -46,7 +42,7 @@ static const rb_data_type_t ossl_digest_type = {
* Public
*/
const EVP_MD *
-GetDigestPtr(VALUE obj)
+ossl_evp_get_digestbyname(VALUE obj)
{
const EVP_MD *md;
ASN1_OBJECT *oid = NULL;
@@ -65,7 +61,7 @@ GetDigestPtr(VALUE obj)
} else {
EVP_MD_CTX *ctx;
- SafeGetDigest(obj, ctx);
+ GetDigest(obj, ctx);
md = EVP_MD_CTX_md(ctx);
}
@@ -80,10 +76,13 @@ ossl_digest_new(const EVP_MD *md)
EVP_MD_CTX *ctx;
ret = ossl_digest_alloc(cDigest);
- GetDigest(ret, ctx);
- if (EVP_DigestInit_ex(ctx, md, NULL) != 1) {
- ossl_raise(eDigestError, "Digest initialization failed.");
- }
+ ctx = EVP_MD_CTX_new();
+ if (!ctx)
+ ossl_raise(eDigestError, "EVP_MD_CTX_new");
+ RTYPEDDATA_DATA(ret) = ctx;
+
+ if (!EVP_DigestInit_ex(ctx, md, NULL))
+ ossl_raise(eDigestError, "Digest initialization failed");
return ret;
}
@@ -94,13 +93,7 @@ ossl_digest_new(const EVP_MD *md)
static VALUE
ossl_digest_alloc(VALUE klass)
{
- VALUE obj = TypedData_Wrap_Struct(klass, &ossl_digest_type, 0);
- EVP_MD_CTX *ctx = EVP_MD_CTX_create();
- if (ctx == NULL)
- ossl_raise(rb_eRuntimeError, "EVP_MD_CTX_create() failed");
- RTYPEDDATA_DATA(obj) = ctx;
-
- return obj;
+ return TypedData_Wrap_Struct(klass, &ossl_digest_type, 0);
}
VALUE ossl_digest_update(VALUE, VALUE);
@@ -109,15 +102,15 @@ VALUE ossl_digest_update(VALUE, VALUE);
* call-seq:
* Digest.new(string [, data]) -> Digest
*
- * Creates a Digest instance based on +string+, which is either the ln
+ * Creates a Digest instance based on _string_, which is either the ln
* (long name) or sn (short name) of a supported digest algorithm.
*
- * If +data+ (a +String+) is given, it is used as the initial input to the
+ * If _data_ (a String) is given, it is used as the initial input to the
* Digest instance, i.e.
*
* digest = OpenSSL::Digest.new('sha256', 'digestdata')
*
- * is equal to
+ * is equivalent to
*
* digest = OpenSSL::Digest.new('sha256')
* digest.update('digestdata')
@@ -130,14 +123,19 @@ ossl_digest_initialize(int argc, VALUE *argv, VALUE self)
VALUE type, data;
rb_scan_args(argc, argv, "11", &type, &data);
- md = GetDigestPtr(type);
+ md = ossl_evp_get_digestbyname(type);
if (!NIL_P(data)) StringValue(data);
- GetDigest(self, ctx);
- if (EVP_DigestInit_ex(ctx, md, NULL) != 1) {
- ossl_raise(eDigestError, "Digest initialization failed.");
+ TypedData_Get_Struct(self, EVP_MD_CTX, &ossl_digest_type, ctx);
+ if (!ctx) {
+ RTYPEDDATA_DATA(self) = ctx = EVP_MD_CTX_new();
+ if (!ctx)
+ ossl_raise(eDigestError, "EVP_MD_CTX_new");
}
+ if (!EVP_DigestInit_ex(ctx, md, NULL))
+ ossl_raise(eDigestError, "Digest initialization failed");
+
if (!NIL_P(data)) return ossl_digest_update(self, data);
return self;
}
@@ -150,8 +148,13 @@ ossl_digest_copy(VALUE self, VALUE other)
rb_check_frozen(self);
if (self == other) return self;
- GetDigest(self, ctx1);
- SafeGetDigest(other, ctx2);
+ TypedData_Get_Struct(self, EVP_MD_CTX, &ossl_digest_type, ctx1);
+ if (!ctx1) {
+ RTYPEDDATA_DATA(self) = ctx1 = EVP_MD_CTX_new();
+ if (!ctx1)
+ ossl_raise(eDigestError, "EVP_MD_CTX_new");
+ }
+ GetDigest(other, ctx2);
if (!EVP_MD_CTX_copy(ctx1, ctx2)) {
ossl_raise(eDigestError, NULL);
@@ -441,7 +444,7 @@ Init_ossl_digest(void)
rb_define_alloc_func(cDigest, ossl_digest_alloc);
rb_define_method(cDigest, "initialize", ossl_digest_initialize, -1);
- rb_define_copy_func(cDigest, ossl_digest_copy);
+ rb_define_method(cDigest, "initialize_copy", ossl_digest_copy, 1);
rb_define_method(cDigest, "reset", ossl_digest_reset, 0);
rb_define_method(cDigest, "update", ossl_digest_update, 1);
rb_define_alias(cDigest, "<<", "update");
diff --git a/ext/openssl/ossl_digest.h b/ext/openssl/ossl_digest.h
index 512f7d3a39..50bf5666a3 100644
--- a/ext/openssl/ossl_digest.h
+++ b/ext/openssl/ossl_digest.h
@@ -13,7 +13,7 @@
extern VALUE cDigest;
extern VALUE eDigestError;
-const EVP_MD *GetDigestPtr(VALUE);
+const EVP_MD *ossl_evp_get_digestbyname(VALUE);
VALUE ossl_digest_new(const EVP_MD *);
void Init_ossl_digest(void);
diff --git a/ext/openssl/ossl_engine.c b/ext/openssl/ossl_engine.c
index f4863b36a4..5ca0d4ca3f 100644
--- a/ext/openssl/ossl_engine.c
+++ b/ext/openssl/ossl_engine.c
@@ -25,10 +25,6 @@
ossl_raise(rb_eRuntimeError, "ENGINE wasn't initialized."); \
} \
} while (0)
-#define SafeGetEngine(obj, engine) do { \
- OSSL_Check_Kind((obj), cEngine); \
- GetPKCS7((obj), (engine)); \
-} while (0)
/*
* Classes
@@ -50,13 +46,25 @@ VALUE eEngineError;
/*
* Private
*/
-#define OSSL_ENGINE_LOAD_IF_MATCH(x) \
+#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000
+#define OSSL_ENGINE_LOAD_IF_MATCH(engine_name, x) \
+do{\
+ if(!strcmp(#engine_name, RSTRING_PTR(name))){\
+ if (OPENSSL_init_crypto(OPENSSL_INIT_ENGINE_##x, NULL))\
+ return Qtrue;\
+ else\
+ ossl_raise(eEngineError, "OPENSSL_init_crypto"); \
+ }\
+}while(0)
+#else
+#define OSSL_ENGINE_LOAD_IF_MATCH(engine_name, x) \
do{\
- if(!strcmp(#x, RSTRING_PTR(name))){\
- ENGINE_load_##x();\
+ if(!strcmp(#engine_name, RSTRING_PTR(name))){\
+ ENGINE_load_##engine_name();\
return Qtrue;\
}\
}while(0)
+#endif
static void
ossl_engine_free(void *engine)
@@ -72,14 +80,13 @@ static const rb_data_type_t ossl_engine_type = {
0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
};
-/* Document-method: OpenSSL::Engine.load
- *
+/*
* call-seq:
- * load(enginename = nil)
+ * OpenSSL::Engine.load(name = nil)
*
- * This method loads engines. If +name+ is nil, then all builtin engines are
- * loaded. Otherwise, the given +name+, as a string, is loaded if available to
- * your runtime, and returns true. If +name+ is not found, then nil is
+ * This method loads engines. If _name_ is nil, then all builtin engines are
+ * loaded. Otherwise, the given _name_, as a String, is loaded if available to
+ * your runtime, and returns true. If _name_ is not found, then nil is
* returned.
*
*/
@@ -99,63 +106,63 @@ ossl_engine_s_load(int argc, VALUE *argv, VALUE klass)
StringValueCStr(name);
#ifndef OPENSSL_NO_STATIC_ENGINE
#if HAVE_ENGINE_LOAD_DYNAMIC
- OSSL_ENGINE_LOAD_IF_MATCH(dynamic);
+ OSSL_ENGINE_LOAD_IF_MATCH(dynamic, DYNAMIC);
#endif
#if HAVE_ENGINE_LOAD_4758CCA
- OSSL_ENGINE_LOAD_IF_MATCH(4758cca);
+ OSSL_ENGINE_LOAD_IF_MATCH(4758cca, 4758CCA);
#endif
#if HAVE_ENGINE_LOAD_AEP
- OSSL_ENGINE_LOAD_IF_MATCH(aep);
+ OSSL_ENGINE_LOAD_IF_MATCH(aep, AEP);
#endif
#if HAVE_ENGINE_LOAD_ATALLA
- OSSL_ENGINE_LOAD_IF_MATCH(atalla);
+ OSSL_ENGINE_LOAD_IF_MATCH(atalla, ATALLA);
#endif
#if HAVE_ENGINE_LOAD_CHIL
- OSSL_ENGINE_LOAD_IF_MATCH(chil);
+ OSSL_ENGINE_LOAD_IF_MATCH(chil, CHIL);
#endif
#if HAVE_ENGINE_LOAD_CSWIFT
- OSSL_ENGINE_LOAD_IF_MATCH(cswift);
+ OSSL_ENGINE_LOAD_IF_MATCH(cswift, CSWIFT);
#endif
#if HAVE_ENGINE_LOAD_NURON
- OSSL_ENGINE_LOAD_IF_MATCH(nuron);
+ OSSL_ENGINE_LOAD_IF_MATCH(nuron, NURON);
#endif
#if HAVE_ENGINE_LOAD_SUREWARE
- OSSL_ENGINE_LOAD_IF_MATCH(sureware);
+ OSSL_ENGINE_LOAD_IF_MATCH(sureware, SUREWARE);
#endif
#if HAVE_ENGINE_LOAD_UBSEC
- OSSL_ENGINE_LOAD_IF_MATCH(ubsec);
+ OSSL_ENGINE_LOAD_IF_MATCH(ubsec, UBSEC);
#endif
#if HAVE_ENGINE_LOAD_PADLOCK
- OSSL_ENGINE_LOAD_IF_MATCH(padlock);
+ OSSL_ENGINE_LOAD_IF_MATCH(padlock, PADLOCK);
#endif
#if HAVE_ENGINE_LOAD_CAPI
- OSSL_ENGINE_LOAD_IF_MATCH(capi);
+ OSSL_ENGINE_LOAD_IF_MATCH(capi, CAPI);
#endif
#if HAVE_ENGINE_LOAD_GMP
- OSSL_ENGINE_LOAD_IF_MATCH(gmp);
+ OSSL_ENGINE_LOAD_IF_MATCH(gmp, GMP);
#endif
#if HAVE_ENGINE_LOAD_GOST
- OSSL_ENGINE_LOAD_IF_MATCH(gost);
+ OSSL_ENGINE_LOAD_IF_MATCH(gost, GOST);
#endif
#if HAVE_ENGINE_LOAD_CRYPTODEV
- OSSL_ENGINE_LOAD_IF_MATCH(cryptodev);
+ OSSL_ENGINE_LOAD_IF_MATCH(cryptodev, CRYPTODEV);
#endif
#if HAVE_ENGINE_LOAD_AESNI
- OSSL_ENGINE_LOAD_IF_MATCH(aesni);
+ OSSL_ENGINE_LOAD_IF_MATCH(aesni, AESNI);
#endif
#endif
#ifdef HAVE_ENGINE_LOAD_OPENBSD_DEV_CRYPTO
- OSSL_ENGINE_LOAD_IF_MATCH(openbsd_dev_crypto);
+ OSSL_ENGINE_LOAD_IF_MATCH(openbsd_dev_crypto, OPENBSD_DEV_CRYPTO);
#endif
- OSSL_ENGINE_LOAD_IF_MATCH(openssl);
+ OSSL_ENGINE_LOAD_IF_MATCH(openssl, OPENSSL);
rb_warning("no such builtin loader for `%"PRIsVALUE"'", name);
return Qnil;
#endif /* HAVE_ENGINE_LOAD_BUILTIN_ENGINES */
}
-/* Document-method: OpenSSL::Engine.cleanup
+/*
* call-seq:
- * OpenSSL::Engine.cleanup
+ * OpenSSL::Engine.cleanup
*
* It is only necessary to run cleanup when engines are loaded via
* OpenSSL::Engine.load. However, running cleanup before exit is recommended.
@@ -165,11 +172,15 @@ ossl_engine_s_load(int argc, VALUE *argv, VALUE klass)
static VALUE
ossl_engine_s_cleanup(VALUE self)
{
+#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000
ENGINE_cleanup();
+#endif
return Qnil;
}
-/* Document-method: OpenSSL::Engine.engines
+/*
+ * call-seq:
+ * OpenSSL::Engine.engines -> [engine, ...]
*
* Returns an array of currently loaded engines.
*/
@@ -193,17 +204,16 @@ ossl_engine_s_engines(VALUE klass)
return ary;
}
-/* Document-method: OpenSSL::Engine.by_id
- *
+/*
* call-seq:
- * by_id(name) -> engine
+ * OpenSSL::Engine.by_id(name) -> engine
*
- * Fetch the engine as specified by the +id+ String
+ * Fetches the engine as specified by the _id_ String.
*
* OpenSSL::Engine.by_id("openssl")
* => #<OpenSSL::Engine id="openssl" name="Software engine support">
*
- * See OpenSSL::Engine.engines for the currently loaded engines
+ * See OpenSSL::Engine.engines for the currently loaded engines.
*/
static VALUE
ossl_engine_s_by_id(VALUE klass, VALUE id)
@@ -227,24 +237,11 @@ ossl_engine_s_by_id(VALUE klass, VALUE id)
return obj;
}
-static VALUE
-ossl_engine_s_alloc(VALUE klass)
-{
- ENGINE *e;
- VALUE obj;
-
- obj = NewEngine(klass);
- if (!(e = ENGINE_new())) {
- ossl_raise(eEngineError, NULL);
- }
- SetEngine(obj, e);
-
- return obj;
-}
-
-/* Document-method: OpenSSL::Engine#id
+/*
+ * call-seq:
+ * engine.id -> string
*
- * Get the id for this engine
+ * Gets the id for this engine.
*
* OpenSSL::Engine.load
* OpenSSL::Engine.engines #=> [#<OpenSSL::Engine#>, ...]
@@ -259,9 +256,11 @@ ossl_engine_get_id(VALUE self)
return rb_str_new2(ENGINE_get_id(e));
}
-/* Document-method: OpenSSL::Engine#name
+/*
+ * call-seq:
+ * engine.name -> string
*
- * Get the descriptive name for this engine
+ * Get the descriptive name for this engine.
*
* OpenSSL::Engine.load
* OpenSSL::Engine.engines #=> [#<OpenSSL::Engine#>, ...]
@@ -277,7 +276,9 @@ ossl_engine_get_name(VALUE self)
return rb_str_new2(ENGINE_get_name(e));
}
-/* Document-method: OpenSSL::Engine#finish
+/*
+ * call-seq:
+ * engine.finish -> nil
*
* Releases all internal structural references for this engine.
*
@@ -294,15 +295,14 @@ ossl_engine_finish(VALUE self)
return Qnil;
}
-/* Document-method: OpenSSL::Engine#cipher
- *
+/*
* call-seq:
* engine.cipher(name) -> OpenSSL::Cipher
*
- * This returns an OpenSSL::Cipher by +name+, if it is available in this
- * engine.
+ * Returns a new instance of OpenSSL::Cipher by _name_, if it is available in
+ * this engine.
*
- * A EngineError will be raised if the cipher is unavailable.
+ * An EngineError will be raised if the cipher is unavailable.
*
* e = OpenSSL::Engine.by_id("openssl")
* => #<OpenSSL::Engine id="openssl" name="Software engine support">
@@ -327,12 +327,11 @@ ossl_engine_get_cipher(VALUE self, VALUE name)
return ossl_cipher_new(ciph);
}
-/* Document-method: OpenSSL::Engine#digest
- *
+/*
* call-seq:
* engine.digest(name) -> OpenSSL::Digest
*
- * This returns an OpenSSL::Digest by +name+.
+ * Returns a new instance of OpenSSL::Digest by _name_.
*
* Will raise an EngineError if the digest is unavailable.
*
@@ -360,12 +359,11 @@ ossl_engine_get_digest(VALUE self, VALUE name)
return ossl_digest_new(md);
}
-/* Document-method: OpenSSL::Engine#load_private_key
- *
+/*
* call-seq:
* engine.load_private_key(id = nil, data = nil) -> OpenSSL::PKey
*
- * Loads the given private key by +id+ and +data+.
+ * Loads the given private key identified by _id_ and _data_.
*
* An EngineError is raised of the OpenSSL::PKey is unavailable.
*
@@ -390,12 +388,11 @@ ossl_engine_load_privkey(int argc, VALUE *argv, VALUE self)
return obj;
}
-/* Document-method: OpenSSL::Engine#load_public_key
- *
+/*
* call-seq:
* engine.load_public_key(id = nil, data = nil) -> OpenSSL::PKey
*
- * Loads the given private key by +id+ and +data+.
+ * Loads the given public key identified by _id_ and _data_.
*
* An EngineError is raised of the OpenSSL::PKey is unavailable.
*
@@ -418,16 +415,15 @@ ossl_engine_load_pubkey(int argc, VALUE *argv, VALUE self)
return ossl_pkey_new(pkey);
}
-/* Document-method: OpenSSL::Engine#set_default
- *
+/*
* call-seq:
* engine.set_default(flag)
*
- * Set the defaults for this engine with the given +flag+.
+ * Set the defaults for this engine with the given _flag_.
*
* These flags are used to control combinations of algorithm methods.
*
- * +flag+ can be one of the following, other flags are available depending on
+ * _flag_ can be one of the following, other flags are available depending on
* your OS.
*
* [All flags] 0xFFFF
@@ -447,14 +443,13 @@ ossl_engine_set_default(VALUE self, VALUE flag)
return Qtrue;
}
-/* Document-method: OpenSSL::Engine#ctrl_cmd
- *
+/*
* call-seq:
* engine.ctrl_cmd(command, value = nil) -> engine
*
- * Send the given +command+ to this engine.
+ * Sends the given _command_ to this engine.
*
- * Raises an EngineError if the +command+ fails.
+ * Raises an EngineError if the command fails.
*/
static VALUE
ossl_engine_ctrl_cmd(int argc, VALUE *argv, VALUE self)
@@ -484,7 +479,9 @@ ossl_engine_cmd_flag_to_name(int flag)
}
}
-/* Document-method: OpenSSL::Engine#cmds
+/*
+ * call-seq:
+ * engine.cmds -> [["name", "description", "flags"], ...]
*
* Returns an array of command definitions for the current engine
*/
@@ -510,9 +507,11 @@ ossl_engine_get_cmds(VALUE self)
return ary;
}
-/* Document-method: OpenSSL::Engine#inspect
+/*
+ * call-seq:
+ * engine.inspect -> string
*
- * Pretty print this engine
+ * Pretty prints this engine.
*/
static VALUE
ossl_engine_inspect(VALUE self)
@@ -537,13 +536,11 @@ Init_ossl_engine(void)
cEngine = rb_define_class_under(mOSSL, "Engine", rb_cObject);
eEngineError = rb_define_class_under(cEngine, "EngineError", eOSSLError);
- rb_define_alloc_func(cEngine, ossl_engine_s_alloc);
+ rb_undef_alloc_func(cEngine);
rb_define_singleton_method(cEngine, "load", ossl_engine_s_load, -1);
rb_define_singleton_method(cEngine, "cleanup", ossl_engine_s_cleanup, 0);
rb_define_singleton_method(cEngine, "engines", ossl_engine_s_engines, 0);
rb_define_singleton_method(cEngine, "by_id", ossl_engine_s_by_id, 1);
- rb_undef_method(CLASS_OF(cEngine), "new");
- rb_undef_method(cEngine, "initialize_copy");
rb_define_method(cEngine, "id", ossl_engine_get_id, 0);
rb_define_method(cEngine, "name", ossl_engine_get_name, 0);
diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c
index 270979ed92..564dcab522 100644
--- a/ext/openssl/ossl_hmac.c
+++ b/ext/openssl/ossl_hmac.c
@@ -19,10 +19,6 @@
ossl_raise(rb_eRuntimeError, "HMAC wasn't initialized"); \
} \
} while (0)
-#define SafeGetHMAC(obj, ctx) do { \
- OSSL_Check_Kind((obj), cHMAC); \
- GetHMAC((obj), (ctx)); \
-} while (0)
/*
* Classes
@@ -110,7 +106,7 @@ ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest)
StringValue(key);
GetHMAC(self, ctx);
HMAC_Init_ex(ctx, RSTRING_PTR(key), RSTRING_LENINT(key),
- GetDigestPtr(digest), NULL);
+ ossl_evp_get_digestbyname(digest), NULL);
return self;
}
@@ -124,7 +120,7 @@ ossl_hmac_copy(VALUE self, VALUE other)
if (self == other) return self;
GetHMAC(self, ctx1);
- SafeGetHMAC(other, ctx2);
+ GetHMAC(other, ctx2);
if (!HMAC_CTX_copy(ctx1, ctx2))
ossl_raise(eHMACError, "HMAC_CTX_copy");
@@ -135,7 +131,7 @@ ossl_hmac_copy(VALUE self, VALUE other)
* call-seq:
* hmac.update(string) -> self
*
- * Returns +self+ updated with the message to be authenticated.
+ * Returns _hmac_ updated with the message to be authenticated.
* Can be called repeatedly with chunks of the message.
*
* === Example
@@ -234,7 +230,7 @@ ossl_hmac_hexdigest(VALUE self)
* call-seq:
* hmac.reset -> self
*
- * Returns +self+ as it was when it was first initialized, with all processed
+ * Returns _hmac_ as it was when it was first initialized, with all processed
* data cleared from it.
*
* === Example
@@ -264,16 +260,16 @@ ossl_hmac_reset(VALUE self)
* call-seq:
* HMAC.digest(digest, key, data) -> aString
*
- * Returns the authentication code as a binary string. The +digest+ parameter
- * must be an instance of OpenSSL::Digest.
+ * Returns the authentication code as a binary string. The _digest_ parameter
+ * specifies the digest algorithm to use. This may be a String representing
+ * the algorithm name or an instance of OpenSSL::Digest.
*
* === Example
*
* key = 'key'
* data = 'The quick brown fox jumps over the lazy dog'
- * digest = OpenSSL::Digest.new('sha1')
*
- * hmac = OpenSSL::HMAC.digest(digest, key, data)
+ * hmac = OpenSSL::HMAC.digest('sha1', key, data)
* #=> "\xDE|\x9B\x85\xB8\xB7\x8A\xA6\xBC\x8Az6\xF7\n\x90p\x1C\x9D\xB4\xD9"
*
*/
@@ -285,8 +281,9 @@ ossl_hmac_s_digest(VALUE klass, VALUE digest, VALUE key, VALUE data)
StringValue(key);
StringValue(data);
- buf = HMAC(GetDigestPtr(digest), RSTRING_PTR(key), RSTRING_LENINT(key),
- (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data), NULL, &buf_len);
+ buf = HMAC(ossl_evp_get_digestbyname(digest), RSTRING_PTR(key),
+ RSTRING_LENINT(key), (unsigned char *)RSTRING_PTR(data),
+ RSTRING_LEN(data), NULL, &buf_len);
return rb_str_new((const char *)buf, buf_len);
}
@@ -295,16 +292,16 @@ ossl_hmac_s_digest(VALUE klass, VALUE digest, VALUE key, VALUE data)
* call-seq:
* HMAC.hexdigest(digest, key, data) -> aString
*
- * Returns the authentication code as a hex-encoded string. The +digest+
- * parameter must be an instance of OpenSSL::Digest.
+ * Returns the authentication code as a hex-encoded string. The _digest_
+ * parameter specifies the digest algorithm to use. This may be a String
+ * representing the algorithm name or an instance of OpenSSL::Digest.
*
* === Example
*
* key = 'key'
* data = 'The quick brown fox jumps over the lazy dog'
- * digest = OpenSSL::Digest.new('sha1')
*
- * hmac = OpenSSL::HMAC.hexdigest(digest, key, data)
+ * hmac = OpenSSL::HMAC.hexdigest('sha1', key, data)
* #=> "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"
*
*/
@@ -318,9 +315,9 @@ ossl_hmac_s_hexdigest(VALUE klass, VALUE digest, VALUE key, VALUE data)
StringValue(key);
StringValue(data);
- if (!HMAC(GetDigestPtr(digest), RSTRING_PTR(key), RSTRING_LENINT(key),
- (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data),
- buf, &buf_len))
+ if (!HMAC(ossl_evp_get_digestbyname(digest), RSTRING_PTR(key),
+ RSTRING_LENINT(key), (unsigned char *)RSTRING_PTR(data),
+ RSTRING_LEN(data), buf, &buf_len))
ossl_raise(eHMACError, "HMAC");
ret = rb_str_new(NULL, buf_len * 2);
@@ -377,7 +374,7 @@ Init_ossl_hmac(void)
rb_define_singleton_method(cHMAC, "hexdigest", ossl_hmac_s_hexdigest, 3);
rb_define_method(cHMAC, "initialize", ossl_hmac_initialize, 2);
- rb_define_copy_func(cHMAC, ossl_hmac_copy);
+ rb_define_method(cHMAC, "initialize_copy", ossl_hmac_copy, 1);
rb_define_method(cHMAC, "reset", ossl_hmac_reset, 0);
rb_define_method(cHMAC, "update", ossl_hmac_update, 1);
diff --git a/ext/openssl/ossl_kdf.c b/ext/openssl/ossl_kdf.c
new file mode 100644
index 0000000000..ee124718b5
--- /dev/null
+++ b/ext/openssl/ossl_kdf.c
@@ -0,0 +1,319 @@
+/*
+ * Ruby/OpenSSL Project
+ * Copyright (C) 2007, 2017 Ruby/OpenSSL Project Authors
+ */
+#include "ossl.h"
+#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+# include <openssl/kdf.h>
+#endif
+
+static VALUE mKDF, eKDF;
+
+/*
+ * call-seq:
+ * KDF.pbkdf2_hmac(pass, salt:, iterations:, length:, hash:) -> aString
+ *
+ * PKCS #5 PBKDF2 (Password-Based Key Derivation Function 2) in combination
+ * with HMAC. Takes _pass_, _salt_ and _iterations_, and then derives a key
+ * of _length_ bytes.
+ *
+ * For more information about PBKDF2, see RFC 2898 Section 5.2
+ * (https://tools.ietf.org/html/rfc2898#section-5.2).
+ *
+ * === Parameters
+ * pass :: The passphrase.
+ * salt :: The salt. Salts prevent attacks based on dictionaries of common
+ * passwords and attacks based on rainbow tables. It is a public
+ * value that can be safely stored along with the password (e.g.
+ * if the derived value is used for password storage).
+ * iterations :: The iteration count. This provides the ability to tune the
+ * algorithm. It is better to use the highest count possible for
+ * the maximum resistance to brute-force attacks.
+ * length :: The desired length of the derived key in octets.
+ * hash :: The hash algorithm used with HMAC for the PRF. May be a String
+ * representing the algorithm name, or an instance of
+ * OpenSSL::Digest.
+ */
+static VALUE
+kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self)
+{
+ VALUE pass, salt, opts, kwargs[4], str;
+ static ID kwargs_ids[4];
+ int iters, len;
+ const EVP_MD *md;
+
+ if (!kwargs_ids[0]) {
+ kwargs_ids[0] = rb_intern_const("salt");
+ kwargs_ids[1] = rb_intern_const("iterations");
+ kwargs_ids[2] = rb_intern_const("length");
+ kwargs_ids[3] = rb_intern_const("hash");
+ }
+ rb_scan_args(argc, argv, "1:", &pass, &opts);
+ rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs);
+
+ StringValue(pass);
+ salt = StringValue(kwargs[0]);
+ iters = NUM2INT(kwargs[1]);
+ len = NUM2INT(kwargs[2]);
+ md = ossl_evp_get_digestbyname(kwargs[3]);
+
+ str = rb_str_new(0, len);
+ if (!PKCS5_PBKDF2_HMAC(RSTRING_PTR(pass), RSTRING_LENINT(pass),
+ (unsigned char *)RSTRING_PTR(salt),
+ RSTRING_LENINT(salt), iters, md, len,
+ (unsigned char *)RSTRING_PTR(str)))
+ ossl_raise(eKDF, "PKCS5_PBKDF2_HMAC");
+
+ return str;
+}
+
+#if defined(HAVE_EVP_PBE_SCRYPT)
+/*
+ * call-seq:
+ * KDF.scrypt(pass, salt:, N:, r:, p:, length:) -> aString
+ *
+ * Derives a key from _pass_ using given parameters with the scrypt
+ * password-based key derivation function. The result can be used for password
+ * storage.
+ *
+ * scrypt is designed to be memory-hard and more secure against brute-force
+ * attacks using custom hardwares than alternative KDFs such as PBKDF2 or
+ * bcrypt.
+ *
+ * The keyword arguments _N_, _r_ and _p_ can be used to tune scrypt. RFC 7914
+ * (published on 2016-08, https://tools.ietf.org/html/rfc7914#section-2) states
+ * that using values r=8 and p=1 appears to yield good results.
+ *
+ * See RFC 7914 (https://tools.ietf.org/html/rfc7914) for more information.
+ *
+ * === Parameters
+ * pass :: Passphrase.
+ * salt :: Salt.
+ * N :: CPU/memory cost parameter. This must be a power of 2.
+ * r :: Block size parameter.
+ * p :: Parallelization parameter.
+ * length :: Length in octets of the derived key.
+ *
+ * === Example
+ * pass = "password"
+ * salt = SecureRandom.random_bytes(16)
+ * dk = OpenSSL::KDF.scrypt(pass, salt: salt, N: 2**14, r: 8, p: 1, length: 32)
+ * p dk #=> "\xDA\xE4\xE2...\x7F\xA1\x01T"
+ */
+static VALUE
+kdf_scrypt(int argc, VALUE *argv, VALUE self)
+{
+ VALUE pass, salt, opts, kwargs[5], str;
+ static ID kwargs_ids[5];
+ size_t len;
+ uint64_t N, r, p, maxmem;
+
+ if (!kwargs_ids[0]) {
+ kwargs_ids[0] = rb_intern_const("salt");
+ kwargs_ids[1] = rb_intern_const("N");
+ kwargs_ids[2] = rb_intern_const("r");
+ kwargs_ids[3] = rb_intern_const("p");
+ kwargs_ids[4] = rb_intern_const("length");
+ }
+ rb_scan_args(argc, argv, "1:", &pass, &opts);
+ rb_get_kwargs(opts, kwargs_ids, 5, 0, kwargs);
+
+ StringValue(pass);
+ salt = StringValue(kwargs[0]);
+ N = NUM2UINT64T(kwargs[1]);
+ r = NUM2UINT64T(kwargs[2]);
+ p = NUM2UINT64T(kwargs[3]);
+ len = NUM2LONG(kwargs[4]);
+ /*
+ * OpenSSL uses 32MB by default (if zero is specified), which is too small.
+ * Let's not limit memory consumption but just let malloc() fail inside
+ * OpenSSL. The amount is controllable by other parameters.
+ */
+ maxmem = SIZE_MAX;
+
+ str = rb_str_new(0, len);
+ if (!EVP_PBE_scrypt(RSTRING_PTR(pass), RSTRING_LEN(pass),
+ (unsigned char *)RSTRING_PTR(salt), RSTRING_LEN(salt),
+ N, r, p, maxmem, (unsigned char *)RSTRING_PTR(str), len))
+ ossl_raise(eKDF, "EVP_PBE_scrypt");
+
+ return str;
+}
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+/*
+ * call-seq:
+ * KDF.hkdf(ikm, salt:, info:, length:, hash:) -> String
+ *
+ * HMAC-based Extract-and-Expand Key Derivation Function (HKDF) as specified in
+ * {RFC 5869}[https://tools.ietf.org/html/rfc5869].
+ *
+ * New in OpenSSL 1.1.0.
+ *
+ * === Parameters
+ * _ikm_::
+ * The input keying material.
+ * _salt_::
+ * The salt.
+ * _info_::
+ * The context and application specific information.
+ * _length_::
+ * The output length in octets. Must be <= <tt>255 * HashLen</tt>, where
+ * HashLen is the length of the hash function output in octets.
+ * _hash_::
+ * The hash function.
+ */
+static VALUE
+kdf_hkdf(int argc, VALUE *argv, VALUE self)
+{
+ VALUE ikm, salt, info, opts, kwargs[4], str;
+ static ID kwargs_ids[4];
+ int saltlen, ikmlen, infolen;
+ size_t len;
+ const EVP_MD *md;
+ EVP_PKEY_CTX *pctx;
+
+ if (!kwargs_ids[0]) {
+ kwargs_ids[0] = rb_intern_const("salt");
+ kwargs_ids[1] = rb_intern_const("info");
+ kwargs_ids[2] = rb_intern_const("length");
+ kwargs_ids[3] = rb_intern_const("hash");
+ }
+ rb_scan_args(argc, argv, "1:", &ikm, &opts);
+ rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs);
+
+ StringValue(ikm);
+ ikmlen = RSTRING_LENINT(ikm);
+ salt = StringValue(kwargs[0]);
+ saltlen = RSTRING_LENINT(salt);
+ info = StringValue(kwargs[1]);
+ infolen = RSTRING_LENINT(info);
+ len = (size_t)NUM2LONG(kwargs[2]);
+ if (len > LONG_MAX)
+ rb_raise(rb_eArgError, "length must be non-negative");
+ md = ossl_evp_get_digestbyname(kwargs[3]);
+
+ str = rb_str_new(NULL, (long)len);
+ pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ if (!pctx)
+ ossl_raise(eKDF, "EVP_PKEY_CTX_new_id");
+ if (EVP_PKEY_derive_init(pctx) <= 0) {
+ EVP_PKEY_CTX_free(pctx);
+ ossl_raise(eKDF, "EVP_PKEY_derive_init");
+ }
+ if (EVP_PKEY_CTX_set_hkdf_md(pctx, md) <= 0) {
+ EVP_PKEY_CTX_free(pctx);
+ ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_md");
+ }
+ if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (unsigned char *)RSTRING_PTR(salt),
+ saltlen) <= 0) {
+ EVP_PKEY_CTX_free(pctx);
+ ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_salt");
+ }
+ if (EVP_PKEY_CTX_set1_hkdf_key(pctx, (unsigned char *)RSTRING_PTR(ikm),
+ ikmlen) <= 0) {
+ EVP_PKEY_CTX_free(pctx);
+ ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_key");
+ }
+ if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (unsigned char *)RSTRING_PTR(info),
+ infolen) <= 0) {
+ EVP_PKEY_CTX_free(pctx);
+ ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_info");
+ }
+ if (EVP_PKEY_derive(pctx, (unsigned char *)RSTRING_PTR(str), &len) <= 0) {
+ EVP_PKEY_CTX_free(pctx);
+ ossl_raise(eKDF, "EVP_PKEY_derive");
+ }
+ rb_str_set_len(str, (long)len);
+ EVP_PKEY_CTX_free(pctx);
+
+ return str;
+}
+#endif
+
+void
+Init_ossl_kdf(void)
+{
+#if 0
+ mOSSL = rb_define_module("OpenSSL");
+ eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
+#endif
+
+ /*
+ * Document-module: OpenSSL::KDF
+ *
+ * Provides functionality of various KDFs (key derivation function).
+ *
+ * KDF is typically used for securely deriving arbitrary length symmetric
+ * keys to be used with an OpenSSL::Cipher from passwords. Another use case
+ * is for storing passwords: Due to the ability to tweak the effort of
+ * computation by increasing the iteration count, computation can be slowed
+ * down artificially in order to render possible attacks infeasible.
+ *
+ * Currently, OpenSSL::KDF provides implementations for the following KDF:
+ *
+ * * PKCS #5 PBKDF2 (Password-Based Key Derivation Function 2) in
+ * combination with HMAC
+ * * scrypt
+ * * HKDF
+ *
+ * == Examples
+ * === Generating a 128 bit key for a Cipher (e.g. AES)
+ * pass = "secret"
+ * salt = OpenSSL::Random.random_bytes(16)
+ * iter = 20_000
+ * key_len = 16
+ * key = OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter,
+ * length: key_len, hash: "sha1")
+ *
+ * === Storing Passwords
+ * pass = "secret"
+ * # store this with the generated value
+ * salt = OpenSSL::Random.random_bytes(16)
+ * iter = 20_000
+ * hash = OpenSSL::Digest::SHA256.new
+ * len = hash.digest_length
+ * # the final value to be stored
+ * value = OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter,
+ * length: len, hash: hash)
+ *
+ * == Important Note on Checking Passwords
+ * When comparing passwords provided by the user with previously stored
+ * values, a common mistake made is comparing the two values using "==".
+ * Typically, "==" short-circuits on evaluation, and is therefore
+ * vulnerable to timing attacks. The proper way is to use a method that
+ * always takes the same amount of time when comparing two values, thus
+ * not leaking any information to potential attackers. To compare two
+ * values, the following could be used:
+ *
+ * def eql_time_cmp(a, b)
+ * unless a.length == b.length
+ * return false
+ * end
+ * cmp = b.bytes
+ * result = 0
+ * a.bytes.each_with_index {|c,i|
+ * result |= c ^ cmp[i]
+ * }
+ * result == 0
+ * end
+ *
+ * Please note that the premature return in case of differing lengths
+ * typically does not leak valuable information - when using PBKDF2, the
+ * length of the values to be compared is of fixed size.
+ */
+ mKDF = rb_define_module_under(mOSSL, "KDF");
+ /*
+ * Generic exception class raised if an error occurs in OpenSSL::KDF module.
+ */
+ eKDF = rb_define_class_under(mKDF, "KDFError", eOSSLError);
+
+ rb_define_module_function(mKDF, "pbkdf2_hmac", kdf_pbkdf2_hmac, -1);
+#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)
+ rb_define_module_function(mKDF, "hkdf", kdf_hkdf, -1);
+#endif
+}
diff --git a/ext/openssl/ossl_kdf.h b/ext/openssl/ossl_kdf.h
new file mode 100644
index 0000000000..b6503f8d9d
--- /dev/null
+++ b/ext/openssl/ossl_kdf.h
@@ -0,0 +1,6 @@
+#if !defined(OSSL_KDF_H)
+#define OSSL_KDF_H
+
+void Init_ossl_kdf(void);
+
+#endif
diff --git a/ext/openssl/ossl_ns_spki.c b/ext/openssl/ossl_ns_spki.c
index 2f7845b685..6f61e61bf5 100644
--- a/ext/openssl/ossl_ns_spki.c
+++ b/ext/openssl/ossl_ns_spki.c
@@ -73,7 +73,7 @@ ossl_spki_alloc(VALUE klass)
* SPKI.new([request]) => spki
*
* === Parameters
- * * +request+ - optional raw request, either in PEM or DER format.
+ * * _request_ - optional raw request, either in PEM or DER format.
*/
static VALUE
ossl_spki_initialize(int argc, VALUE *argv, VALUE self)
@@ -159,8 +159,6 @@ ossl_spki_print(VALUE self)
{
NETSCAPE_SPKI *spki;
BIO *out;
- BUF_MEM *buf;
- VALUE str;
GetSPKI(self, spki);
if (!(out = BIO_new(BIO_s_mem()))) {
@@ -170,11 +168,8 @@ ossl_spki_print(VALUE self)
BIO_free(out);
ossl_raise(eSPKIError, NULL);
}
- BIO_get_mem_ptr(out, &buf);
- str = rb_str_new(buf->data, buf->length);
- BIO_free(out);
- return str;
+ return ossl_membio2str(out);
}
/*
@@ -203,7 +198,7 @@ ossl_spki_get_public_key(VALUE self)
* spki.public_key = pub => pkey
*
* === Parameters
- * * +pub+ - the public key to be set for this instance
+ * * _pub_ - the public key to be set for this instance
*
* Sets the public key to be associated with the SPKI, an instance of
* OpenSSL::PKey. This should be the public key corresponding to the
@@ -213,12 +208,13 @@ static VALUE
ossl_spki_set_public_key(VALUE self, VALUE key)
{
NETSCAPE_SPKI *spki;
+ EVP_PKEY *pkey;
GetSPKI(self, spki);
- if (!NETSCAPE_SPKI_set_pubkey(spki, GetPKeyPtr(key))) { /* NO NEED TO DUP */
- ossl_raise(eSPKIError, NULL);
- }
-
+ pkey = GetPKeyPtr(key);
+ ossl_pkey_check_public_key(pkey);
+ if (!NETSCAPE_SPKI_set_pubkey(spki, pkey))
+ ossl_raise(eSPKIError, "NETSCAPE_SPKI_set_pubkey");
return key;
}
@@ -248,7 +244,7 @@ ossl_spki_get_challenge(VALUE self)
* spki.challenge = str => string
*
* === Parameters
- * * +str+ - the challenge string to be set for this instance
+ * * _str_ - the challenge string to be set for this instance
*
* Sets the challenge to be associated with the SPKI. May be used by the
* server, e.g. to prevent replay.
@@ -273,8 +269,8 @@ ossl_spki_set_challenge(VALUE self, VALUE str)
* spki.sign(key, digest) => spki
*
* === Parameters
- * * +key+ - the private key to be used for signing this instance
- * * +digest+ - the digest to be used for signing this instance
+ * * _key_ - the private key to be used for signing this instance
+ * * _digest_ - the digest to be used for signing this instance
*
* To sign an SPKI, the private key corresponding to the public key set
* for this instance should be used, in addition to a digest algorithm in
@@ -289,7 +285,7 @@ ossl_spki_sign(VALUE self, VALUE key, VALUE digest)
const EVP_MD *md;
pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
- md = GetDigestPtr(digest);
+ md = ossl_evp_get_digestbyname(digest);
GetSPKI(self, spki);
if (!NETSCAPE_SPKI_sign(spki, pkey, md)) {
ossl_raise(eSPKIError, NULL);
@@ -303,7 +299,7 @@ ossl_spki_sign(VALUE self, VALUE key, VALUE digest)
* spki.verify(key) => boolean
*
* === Parameters
- * * +key+ - the public key to be used for verifying the SPKI signature
+ * * _key_ - the public key to be used for verifying the SPKI signature
*
* Returns +true+ if the signature is valid, +false+ otherwise. To verify an
* SPKI, the public key contained within the SPKI should be used.
@@ -312,22 +308,25 @@ static VALUE
ossl_spki_verify(VALUE self, VALUE key)
{
NETSCAPE_SPKI *spki;
+ EVP_PKEY *pkey;
GetSPKI(self, spki);
- switch (NETSCAPE_SPKI_verify(spki, GetPKeyPtr(key))) { /* NO NEED TO DUP */
- case 0:
+ pkey = GetPKeyPtr(key);
+ ossl_pkey_check_public_key(pkey);
+ switch (NETSCAPE_SPKI_verify(spki, pkey)) {
+ case 0:
+ ossl_clear_error();
return Qfalse;
- case 1:
+ case 1:
return Qtrue;
- default:
- ossl_raise(eSPKIError, NULL);
+ default:
+ ossl_raise(eSPKIError, "NETSCAPE_SPKI_verify");
}
- return Qnil; /* dummy */
}
/* Document-class: OpenSSL::Netscape::SPKI
*
- * A Simple Public Key Infrastructure implementation (pronounced "spookey").
+ * A Simple Public Key Infrastructure implementation (pronounced "spooky").
* The structure is defined as
* PublicKeyAndChallenge ::= SEQUENCE {
* spki SubjectPublicKeyInfo,
@@ -353,7 +352,7 @@ ossl_spki_verify(VALUE self, VALUE key)
* spki.public_key = key.public_key
* spki.sign(key, OpenSSL::Digest::SHA256.new)
* #send a request containing this to a server generating a certificate
- * === Verifiying an SPKI request
+ * === Verifying an SPKI request
* request = #...
* spki = OpenSSL::Netscape::SPKI.new request
* unless spki.verify(spki.public_key)
diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c
index a8b3503d2a..c0237791da 100644
--- a/ext/openssl/ossl_ocsp.c
+++ b/ext/openssl/ossl_ocsp.c
@@ -22,10 +22,6 @@
TypedData_Get_Struct((obj), OCSP_REQUEST, &ossl_ocsp_request_type, (req)); \
if(!(req)) ossl_raise(rb_eRuntimeError, "Request wasn't initialized!"); \
} while (0)
-#define SafeGetOCSPReq(obj, req) do { \
- OSSL_Check_Kind((obj), cOCSPReq); \
- GetOCSPReq((obj), (req)); \
-} while (0)
#define NewOCSPRes(klass) \
TypedData_Wrap_Struct((klass), &ossl_ocsp_response_type, 0)
@@ -37,10 +33,6 @@
TypedData_Get_Struct((obj), OCSP_RESPONSE, &ossl_ocsp_response_type, (res)); \
if(!(res)) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \
} while (0)
-#define SafeGetOCSPRes(obj, res) do { \
- OSSL_Check_Kind((obj), cOCSPRes); \
- GetOCSPRes((obj), (res)); \
-} while (0)
#define NewOCSPBasicRes(klass) \
TypedData_Wrap_Struct((klass), &ossl_ocsp_basicresp_type, 0)
@@ -52,10 +44,6 @@
TypedData_Get_Struct((obj), OCSP_BASICRESP, &ossl_ocsp_basicresp_type, (res)); \
if(!(res)) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \
} while (0)
-#define SafeGetOCSPBasicRes(obj, res) do { \
- OSSL_Check_Kind((obj), cOCSPBasicRes); \
- GetOCSPBasicRes((obj), (res)); \
-} while (0)
#define NewOCSPSingleRes(klass) \
TypedData_Wrap_Struct((klass), &ossl_ocsp_singleresp_type, 0)
@@ -67,10 +55,6 @@
TypedData_Get_Struct((obj), OCSP_SINGLERESP, &ossl_ocsp_singleresp_type, (res)); \
if(!(res)) ossl_raise(rb_eRuntimeError, "SingleResponse wasn't initialized!"); \
} while (0)
-#define SafeGetOCSPSingleRes(obj, res) do { \
- OSSL_Check_Kind((obj), cOCSPSingleRes); \
- GetOCSPSingleRes((obj), (res)); \
-} while (0)
#define NewOCSPCertId(klass) \
TypedData_Wrap_Struct((klass), &ossl_ocsp_certid_type, 0)
@@ -82,10 +66,6 @@
TypedData_Get_Struct((obj), OCSP_CERTID, &ossl_ocsp_certid_type, (cid)); \
if(!(cid)) ossl_raise(rb_eRuntimeError, "Cert ID wasn't initialized!"); \
} while (0)
-#define SafeGetOCSPCertId(obj, cid) do { \
- OSSL_Check_Kind((obj), cOCSPCertId); \
- GetOCSPCertId((obj), (cid)); \
-} while (0)
VALUE mOCSP;
VALUE eOCSPError;
@@ -200,7 +180,7 @@ ossl_ocspreq_initialize_copy(VALUE self, VALUE other)
rb_check_frozen(self);
GetOCSPReq(self, req_old);
- SafeGetOCSPReq(other, req);
+ GetOCSPReq(other, req);
req_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_REQUEST), req);
if (!req_new)
@@ -218,7 +198,7 @@ ossl_ocspreq_initialize_copy(VALUE self, VALUE other)
* OpenSSL::OCSP::Request.new(request_der) -> request
*
* Creates a new OpenSSL::OCSP::Request. The request may be created empty or
- * from a +request_der+ string.
+ * from a _request_der_ string.
*/
static VALUE
@@ -248,7 +228,7 @@ ossl_ocspreq_initialize(int argc, VALUE *argv, VALUE self)
* call-seq:
* request.add_nonce(nonce = nil) -> request
*
- * Adds a +nonce+ to the OCSP request. If no nonce is given a random one will
+ * Adds a _nonce_ to the OCSP request. If no nonce is given a random one will
* be generated.
*
* The nonce is used to prevent replay attacks but some servers do not support
@@ -281,7 +261,7 @@ ossl_ocspreq_add_nonce(int argc, VALUE *argv, VALUE self)
* call-seq:
* request.check_nonce(response) -> result
*
- * Checks the nonce validity for this request and +response+.
+ * Checks the nonce validity for this request and _response_.
*
* The return value is one of the following:
*
@@ -291,7 +271,7 @@ ossl_ocspreq_add_nonce(int argc, VALUE *argv, VALUE self)
* 2 :: nonces both absent.
* 3 :: nonce present in response only.
*
- * For most responses, clients can check +result+ > 0. If a responder doesn't
+ * For most responses, clients can check _result_ > 0. If a responder doesn't
* handle nonces <code>result.nonzero?</code> may be necessary. A result of
* <code>0</code> is always an error.
*/
@@ -304,7 +284,7 @@ ossl_ocspreq_check_nonce(VALUE self, VALUE basic_resp)
int res;
GetOCSPReq(self, req);
- SafeGetOCSPBasicRes(basic_resp, bs);
+ GetOCSPBasicRes(basic_resp, bs);
res = OCSP_check_nonce(req, bs);
return INT2NUM(res);
@@ -314,7 +294,7 @@ ossl_ocspreq_check_nonce(VALUE self, VALUE basic_resp)
* call-seq:
* request.add_certid(certificate_id) -> request
*
- * Adds +certificate_id+ to the request.
+ * Adds _certificate_id_ to the request.
*/
static VALUE
@@ -371,17 +351,17 @@ ossl_ocspreq_get_certid(VALUE self)
* call-seq:
* request.sign(cert, key, certs = nil, flags = 0, digest = nil) -> self
*
- * Signs this OCSP request using +cert+, +key+ and optional +digest+. If
- * +digest+ is not specified, SHA-1 is used. +certs+ is an optional Array of
+ * Signs this OCSP request using _cert_, _key_ and optional _digest_. If
+ * _digest_ is not specified, SHA-1 is used. _certs_ is an optional Array of
* additional certificates which are included in the request in addition to
- * the signer certificate. Note that if +certs+ is nil or not given, flag
+ * the signer certificate. Note that if _certs_ is +nil+ or not given, flag
* OpenSSL::OCSP::NOCERTS is enabled. Pass an empty array to include only the
* signer certificate.
*
- * +flags+ can be a bitwise OR of the following constants:
+ * _flags_ is a bitwise OR of the following constants:
*
* OpenSSL::OCSP::NOCERTS::
- * Don't include any certificates in the request. +certs+ will be ignored.
+ * Don't include any certificates in the request. _certs_ will be ignored.
*/
static VALUE
ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self)
@@ -404,7 +384,7 @@ ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self)
if (NIL_P(digest))
md = EVP_sha1();
else
- md = GetDigestPtr(digest);
+ md = ossl_evp_get_digestbyname(digest);
if (NIL_P(certs))
flg |= OCSP_NOCERTS;
else
@@ -421,9 +401,12 @@ ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self)
* call-seq:
* request.verify(certificates, store, flags = 0) -> true or false
*
- * Verifies this request using the given +certificates+ and +store+.
- * +certificates+ is an array of OpenSSL::X509::Certificate, +store+ is an
+ * Verifies this request using the given _certificates_ and _store_.
+ * _certificates_ is an array of OpenSSL::X509::Certificate, _store_ is an
* OpenSSL::X509::Store.
+ *
+ * Note that +false+ is returned if the request does not have a signature.
+ * Use #signed? to check whether the request is signed or not.
*/
static VALUE
@@ -473,13 +456,29 @@ ossl_ocspreq_to_der(VALUE self)
}
/*
+ * call-seq:
+ * request.signed? -> true or false
+ *
+ * Returns +true+ if the request is signed, +false+ otherwise. Note that the
+ * validity of the signature is *not* checked. Use #verify to verify that.
+ */
+static VALUE
+ossl_ocspreq_signed_p(VALUE self)
+{
+ OCSP_REQUEST *req;
+
+ GetOCSPReq(self, req);
+ return OCSP_request_is_signed(req) ? Qtrue : Qfalse;
+}
+
+/*
* OCSP::Response
*/
/* call-seq:
* OpenSSL::OCSP::Response.create(status, basic_response = nil) -> response
*
- * Creates an OpenSSL::OCSP::Response from +status+ and +basic_response+.
+ * Creates an OpenSSL::OCSP::Response from _status_ and _basic_response_.
*/
static VALUE
@@ -521,7 +520,7 @@ ossl_ocspres_initialize_copy(VALUE self, VALUE other)
rb_check_frozen(self);
GetOCSPRes(self, res_old);
- SafeGetOCSPRes(other, res);
+ GetOCSPRes(other, res);
res_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_RESPONSE), res);
if (!res_new)
@@ -539,7 +538,7 @@ ossl_ocspres_initialize_copy(VALUE self, VALUE other)
* OpenSSL::OCSP::Response.new(response_der) -> response
*
* Creates a new OpenSSL::OCSP::Response. The response may be created empty or
- * from a +response_der+ string.
+ * from a _response_der_ string.
*/
static VALUE
@@ -677,7 +676,7 @@ ossl_ocspbres_initialize_copy(VALUE self, VALUE other)
rb_check_frozen(self);
GetOCSPBasicRes(self, bs_old);
- SafeGetOCSPBasicRes(other, bs);
+ GetOCSPBasicRes(other, bs);
bs_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_BASICRESP), bs);
if (!bs_new)
@@ -693,7 +692,7 @@ ossl_ocspbres_initialize_copy(VALUE self, VALUE other)
* call-seq:
* OpenSSL::OCSP::BasicResponse.new(der_string = nil) -> basic_response
*
- * Creates a new BasicResponse. If +der_string+ is given, decodes +der_string+
+ * Creates a new BasicResponse. If _der_string_ is given, decodes _der_string_
* as DER.
*/
@@ -724,7 +723,7 @@ ossl_ocspbres_initialize(int argc, VALUE *argv, VALUE self)
* call-seq:
* basic_response.copy_nonce(request) -> Integer
*
- * Copies the nonce from +request+ into this response. Returns 1 on success
+ * Copies the nonce from _request_ into this response. Returns 1 on success
* and 0 on failure.
*/
@@ -736,7 +735,7 @@ ossl_ocspbres_copy_nonce(VALUE self, VALUE request)
int ret;
GetOCSPBasicRes(self, bs);
- SafeGetOCSPReq(request, req);
+ GetOCSPReq(request, req);
ret = OCSP_copy_nonce(bs, req);
return INT2NUM(ret);
@@ -746,7 +745,7 @@ ossl_ocspbres_copy_nonce(VALUE self, VALUE request)
* call-seq:
* basic_response.add_nonce(nonce = nil)
*
- * Adds +nonce+ to this response. If no nonce was provided a random nonce
+ * Adds _nonce_ to this response. If no nonce was provided a random nonce
* will be added.
*/
@@ -792,26 +791,26 @@ add_status_convert_time(VALUE obj)
* call-seq:
* basic_response.add_status(certificate_id, status, reason, revocation_time, this_update, next_update, extensions) -> basic_response
*
- * Adds a certificate status for +certificate_id+. +status+ is the status, and
+ * Adds a certificate status for _certificate_id_. _status_ is the status, and
* must be one of these:
*
* - OpenSSL::OCSP::V_CERTSTATUS_GOOD
* - OpenSSL::OCSP::V_CERTSTATUS_REVOKED
* - OpenSSL::OCSP::V_CERTSTATUS_UNKNOWN
*
- * +reason+ and +revocation_time+ can be given only when +status+ is
- * OpenSSL::OCSP::V_CERTSTATUS_REVOKED. +reason+ describes the reason for the
+ * _reason_ and _revocation_time_ can be given only when _status_ is
+ * OpenSSL::OCSP::V_CERTSTATUS_REVOKED. _reason_ describes the reason for the
* revocation, and must be one of OpenSSL::OCSP::REVOKED_STATUS_* constants.
- * +revocation_time+ is the time when the certificate is revoked.
+ * _revocation_time_ is the time when the certificate is revoked.
*
- * +this_update+ and +next_update+ indicate the time at which ths status is
+ * _this_update_ and _next_update_ indicate the time at which ths status is
* verified to be correct and the time at or before which newer information
- * will be available, respectively. +next_update+ is optional.
+ * will be available, respectively. _next_update_ is optional.
*
- * +extensions+ is an Array of OpenSSL::X509::Extension to be included in the
+ * _extensions_ is an Array of OpenSSL::X509::Extension to be included in the
* SingleResponse. This is also optional.
*
- * Note that the times, +revocation_time+, +this_update+ and +next_update+
+ * Note that the times, _revocation_time_, _this_update_ and _next_update_
* can be specified in either of Integer or Time object. If they are Integer, it
* is treated as the relative seconds from the current time.
*/
@@ -829,7 +828,7 @@ ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status,
VALUE tmp;
GetOCSPBasicRes(self, bs);
- SafeGetOCSPCertId(cid, id);
+ GetOCSPCertId(cid, id);
st = NUM2INT(status);
if (!NIL_P(ext)) { /* All ext's members must be X509::Extension */
ext = rb_check_array_type(ext);
@@ -888,7 +887,7 @@ ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status,
* Returns an Array of statuses for this response. Each status contains a
* CertificateId, the status (0 for good, 1 for revoked, 2 for unknown), the
* reason for the status, the revocation time, the time of this update, the time
- * for the next update and a list of OpenSSL::X509::Extensions.
+ * for the next update and a list of OpenSSL::X509::Extension.
*
* This should be superseded by BasicResponse#responses and #find_response that
* return SingleResponse.
@@ -977,7 +976,7 @@ ossl_ocspbres_get_responses(VALUE self)
* call-seq:
* basic_response.find_response(certificate_id) -> SingleResponse | nil
*
- * Returns a SingleResponse whose CertId matches with +certificate_id+, or nil
+ * Returns a SingleResponse whose CertId matches with _certificate_id_, or +nil+
* if this BasicResponse does not contain it.
*/
static VALUE
@@ -988,7 +987,7 @@ ossl_ocspbres_find_response(VALUE self, VALUE target)
OCSP_CERTID *id;
int n;
- SafeGetOCSPCertId(target, id);
+ GetOCSPCertId(target, id);
GetOCSPBasicRes(self, bs);
if ((n = OCSP_resp_find(bs, id, -1)) == -1)
@@ -1006,10 +1005,10 @@ ossl_ocspbres_find_response(VALUE self, VALUE target)
* call-seq:
* basic_response.sign(cert, key, certs = nil, flags = 0, digest = nil) -> self
*
- * Signs this OCSP response using the +cert+, +key+ and optional +digest+. This
+ * Signs this OCSP response using the _cert_, _key_ and optional _digest_. This
* behaves in the similar way as OpenSSL::OCSP::Request#sign.
*
- * +flags+ can include:
+ * _flags_ can include:
* OpenSSL::OCSP::NOCERTS:: don't include certificates
* OpenSSL::OCSP::NOTIME:: don't set producedAt
* OpenSSL::OCSP::RESPID_KEY:: use signer's public key hash as responderID
@@ -1036,7 +1035,7 @@ ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self)
if (NIL_P(digest))
md = EVP_sha1();
else
- md = GetDigestPtr(digest);
+ md = ossl_evp_get_digestbyname(digest);
if (NIL_P(certs))
flg |= OCSP_NOCERTS;
else
@@ -1053,8 +1052,8 @@ ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self)
* call-seq:
* basic_response.verify(certificates, store, flags = 0) -> true or false
*
- * Verifies the signature of the response using the given +certificates+ and
- * +store+. This works in the similar way as OpenSSL::OCSP::Request#verify.
+ * Verifies the signature of the response using the given _certificates_ and
+ * _store_. This works in the similar way as OpenSSL::OCSP::Request#verify.
*/
static VALUE
ossl_ocspbres_verify(int argc, VALUE *argv, VALUE self)
@@ -1184,7 +1183,7 @@ ossl_ocspsres_alloc(VALUE klass)
* call-seq:
* OpenSSL::OCSP::SingleResponse.new(der_string) -> SingleResponse
*
- * Creates a new SingleResponse from +der_string+.
+ * Creates a new SingleResponse from _der_string_.
*/
static VALUE
ossl_ocspsres_initialize(VALUE self, VALUE arg)
@@ -1213,7 +1212,7 @@ ossl_ocspsres_initialize_copy(VALUE self, VALUE other)
rb_check_frozen(self);
GetOCSPSingleRes(self, sres_old);
- SafeGetOCSPSingleRes(other, sres);
+ GetOCSPSingleRes(other, sres);
sres_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_SINGLERESP), sres);
if (!sres_new)
@@ -1235,10 +1234,10 @@ ossl_ocspsres_initialize_copy(VALUE self, VALUE other)
*
* It is possible that the OCSP request takes a few seconds or the time is not
* accurate. To avoid rejecting a valid response, this method allows the times
- * to be within +nsec+ of the current time.
+ * to be within _nsec_ seconds of the current time.
*
* Some responders don't set the nextUpdate field. This may cause a very old
- * response to be considered valid. The +maxsec+ parameter can be used to limit
+ * response to be considered valid. The _maxsec_ parameter can be used to limit
* the age of responses.
*/
static VALUE
@@ -1329,8 +1328,10 @@ ossl_ocspsres_get_this_update(VALUE self)
status = OCSP_single_get0_status(sres, NULL, NULL, &time, NULL);
if (status < 0)
ossl_raise(eOCSPError, "OCSP_single_get0_status");
+ if (!time)
+ return Qnil;
- return asn1time_to_time(time); /* will handle NULL */
+ return asn1time_to_time(time);
}
/*
@@ -1348,6 +1349,8 @@ ossl_ocspsres_get_next_update(VALUE self)
status = OCSP_single_get0_status(sres, NULL, NULL, NULL, &time);
if (status < 0)
ossl_raise(eOCSPError, "OCSP_single_get0_status");
+ if (!time)
+ return Qnil;
return asn1time_to_time(time);
}
@@ -1369,6 +1372,8 @@ ossl_ocspsres_get_revocation_time(VALUE self)
ossl_raise(eOCSPError, "OCSP_single_get0_status");
if (status != V_OCSP_CERTSTATUS_REVOKED)
ossl_raise(eOCSPError, "certificate is not revoked");
+ if (!time)
+ return Qnil;
return asn1time_to_time(time);
}
@@ -1468,7 +1473,7 @@ ossl_ocspcid_initialize_copy(VALUE self, VALUE other)
rb_check_frozen(self);
GetOCSPCertId(self, cid_old);
- SafeGetOCSPCertId(other, cid);
+ GetOCSPCertId(other, cid);
cid_new = OCSP_CERTID_dup(cid);
if (!cid_new)
@@ -1485,14 +1490,13 @@ ossl_ocspcid_initialize_copy(VALUE self, VALUE other)
* OpenSSL::OCSP::CertificateId.new(subject, issuer, digest = nil) -> certificate_id
* OpenSSL::OCSP::CertificateId.new(der_string) -> certificate_id
*
- * Creates a new OpenSSL::OCSP::CertificateId for the given +subject+ and
- * +issuer+ X509 certificates. The +digest+ is used to compute the
- * certificate ID and must be an OpenSSL::Digest instance.
+ * Creates a new OpenSSL::OCSP::CertificateId for the given _subject_ and
+ * _issuer_ X509 certificates. The _digest_ is a digest algorithm that is used
+ * to compute the hash values. This defaults to SHA-1.
*
* If only one argument is given, decodes it as DER representation of a
* certificate ID.
*/
-
static VALUE
ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self)
{
@@ -1517,7 +1521,7 @@ ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self)
x509s = GetX509CertPtr(subject); /* NO NEED TO DUP */
x509i = GetX509CertPtr(issuer); /* NO NEED TO DUP */
- md = !NIL_P(digest) ? GetDigestPtr(digest) : NULL;
+ md = !NIL_P(digest) ? ossl_evp_get_digestbyname(digest) : NULL;
newid = OCSP_cert_to_id(md, x509s, x509i);
if (!newid)
@@ -1534,7 +1538,7 @@ ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self)
* call-seq:
* certificate_id.cmp(other) -> true or false
*
- * Compares this certificate id with +other+ and returns true if they are the
+ * Compares this certificate id with _other_ and returns +true+ if they are the
* same.
*/
static VALUE
@@ -1544,7 +1548,7 @@ ossl_ocspcid_cmp(VALUE self, VALUE other)
int result;
GetOCSPCertId(self, id);
- SafeGetOCSPCertId(other, id2);
+ GetOCSPCertId(other, id2);
result = OCSP_id_cmp(id, id2);
return (result == 0) ? Qtrue : Qfalse;
@@ -1554,7 +1558,7 @@ ossl_ocspcid_cmp(VALUE self, VALUE other)
* call-seq:
* certificate_id.cmp_issuer(other) -> true or false
*
- * Compares this certificate id's issuer with +other+ and returns true if
+ * Compares this certificate id's issuer with _other_ and returns +true+ if
* they are the same.
*/
@@ -1565,7 +1569,7 @@ ossl_ocspcid_cmp_issuer(VALUE self, VALUE other)
int result;
GetOCSPCertId(self, id);
- SafeGetOCSPCertId(other, id2);
+ GetOCSPCertId(other, id2);
result = OCSP_id_issuer_cmp(id, id2);
return (result == 0) ? Qtrue : Qfalse;
@@ -1824,12 +1828,13 @@ Init_ossl_ocsp(void)
cOCSPReq = rb_define_class_under(mOCSP, "Request", rb_cObject);
rb_define_alloc_func(cOCSPReq, ossl_ocspreq_alloc);
- rb_define_copy_func(cOCSPReq, ossl_ocspreq_initialize_copy);
+ rb_define_method(cOCSPReq, "initialize_copy", ossl_ocspreq_initialize_copy, 1);
rb_define_method(cOCSPReq, "initialize", ossl_ocspreq_initialize, -1);
rb_define_method(cOCSPReq, "add_nonce", ossl_ocspreq_add_nonce, -1);
rb_define_method(cOCSPReq, "check_nonce", ossl_ocspreq_check_nonce, 1);
rb_define_method(cOCSPReq, "add_certid", ossl_ocspreq_add_certid, 1);
rb_define_method(cOCSPReq, "certid", ossl_ocspreq_get_certid, 0);
+ rb_define_method(cOCSPReq, "signed?", ossl_ocspreq_signed_p, 0);
rb_define_method(cOCSPReq, "sign", ossl_ocspreq_sign, -1);
rb_define_method(cOCSPReq, "verify", ossl_ocspreq_verify, -1);
rb_define_method(cOCSPReq, "to_der", ossl_ocspreq_to_der, 0);
@@ -1842,7 +1847,7 @@ Init_ossl_ocsp(void)
cOCSPRes = rb_define_class_under(mOCSP, "Response", rb_cObject);
rb_define_singleton_method(cOCSPRes, "create", ossl_ocspres_s_create, 2);
rb_define_alloc_func(cOCSPRes, ossl_ocspres_alloc);
- rb_define_copy_func(cOCSPRes, ossl_ocspres_initialize_copy);
+ rb_define_method(cOCSPRes, "initialize_copy", ossl_ocspres_initialize_copy, 1);
rb_define_method(cOCSPRes, "initialize", ossl_ocspres_initialize, -1);
rb_define_method(cOCSPRes, "status", ossl_ocspres_status, 0);
rb_define_method(cOCSPRes, "status_string", ossl_ocspres_status_string, 0);
@@ -1857,7 +1862,7 @@ Init_ossl_ocsp(void)
cOCSPBasicRes = rb_define_class_under(mOCSP, "BasicResponse", rb_cObject);
rb_define_alloc_func(cOCSPBasicRes, ossl_ocspbres_alloc);
- rb_define_copy_func(cOCSPBasicRes, ossl_ocspbres_initialize_copy);
+ rb_define_method(cOCSPBasicRes, "initialize_copy", ossl_ocspbres_initialize_copy, 1);
rb_define_method(cOCSPBasicRes, "initialize", ossl_ocspbres_initialize, -1);
rb_define_method(cOCSPBasicRes, "copy_nonce", ossl_ocspbres_copy_nonce, 1);
rb_define_method(cOCSPBasicRes, "add_nonce", ossl_ocspbres_add_nonce, -1);
@@ -1876,7 +1881,7 @@ Init_ossl_ocsp(void)
*/
cOCSPSingleRes = rb_define_class_under(mOCSP, "SingleResponse", rb_cObject);
rb_define_alloc_func(cOCSPSingleRes, ossl_ocspsres_alloc);
- rb_define_copy_func(cOCSPSingleRes, ossl_ocspsres_initialize_copy);
+ rb_define_method(cOCSPSingleRes, "initialize_copy", ossl_ocspsres_initialize_copy, 1);
rb_define_method(cOCSPSingleRes, "initialize", ossl_ocspsres_initialize, 1);
rb_define_method(cOCSPSingleRes, "check_validity", ossl_ocspsres_check_validity, -1);
rb_define_method(cOCSPSingleRes, "certid", ossl_ocspsres_get_certid, 0);
@@ -1895,7 +1900,7 @@ Init_ossl_ocsp(void)
cOCSPCertId = rb_define_class_under(mOCSP, "CertificateId", rb_cObject);
rb_define_alloc_func(cOCSPCertId, ossl_ocspcid_alloc);
- rb_define_copy_func(cOCSPCertId, ossl_ocspcid_initialize_copy);
+ rb_define_method(cOCSPCertId, "initialize_copy", ossl_ocspcid_initialize_copy, 1);
rb_define_method(cOCSPCertId, "initialize", ossl_ocspcid_initialize, -1);
rb_define_method(cOCSPCertId, "cmp", ossl_ocspcid_cmp, 1);
rb_define_method(cOCSPCertId, "cmp_issuer", ossl_ocspcid_cmp_issuer, 1);
diff --git a/ext/openssl/ossl_pkcs12.c b/ext/openssl/ossl_pkcs12.c
index 0b9c7816b5..4566334481 100644
--- a/ext/openssl/ossl_pkcs12.c
+++ b/ext/openssl/ossl_pkcs12.c
@@ -17,11 +17,6 @@
if(!(p12)) ossl_raise(rb_eRuntimeError, "PKCS12 wasn't initialized."); \
} while (0)
-#define SafeGetPKCS12(obj, p12) do { \
- OSSL_Check_Kind((obj), cPKCS12); \
- GetPKCS12((obj), (p12)); \
-} while (0)
-
#define ossl_pkcs12_set_key(o,v) rb_iv_set((o), "@key", (v))
#define ossl_pkcs12_set_cert(o,v) rb_iv_set((o), "@certificate", (v))
#define ossl_pkcs12_set_ca_certs(o,v) rb_iv_set((o), "@ca_certs", (v))
@@ -72,7 +67,7 @@ ossl_pkcs12_initialize_copy(VALUE self, VALUE other)
rb_check_frozen(self);
GetPKCS12(self, p12_old);
- SafeGetPKCS12(other, p12);
+ GetPKCS12(other, p12);
p12_new = ASN1_dup((i2d_of_void *)i2d_PKCS12, (d2i_of_void *)d2i_PKCS12, (char *)p12);
if (!p12_new)
@@ -89,20 +84,20 @@ ossl_pkcs12_initialize_copy(VALUE self, VALUE other)
* PKCS12.create(pass, name, key, cert [, ca, [, key_pbe [, cert_pbe [, key_iter [, mac_iter [, keytype]]]]]])
*
* === Parameters
- * * +pass+ - string
- * * +name+ - A string describing the key.
- * * +key+ - Any PKey.
- * * +cert+ - A X509::Certificate.
+ * * _pass_ - string
+ * * _name_ - A string describing the key.
+ * * _key_ - Any PKey.
+ * * _cert_ - A X509::Certificate.
* * The public_key portion of the certificate must contain a valid public key.
* * The not_before and not_after fields must be filled in.
- * * +ca+ - An optional array of X509::Certificate's.
- * * +key_pbe+ - string
- * * +cert_pbe+ - string
- * * +key_iter+ - integer
- * * +mac_iter+ - integer
- * * +keytype+ - An integer representing an MSIE specific extension.
+ * * _ca_ - An optional array of X509::Certificate's.
+ * * _key_pbe_ - string
+ * * _cert_pbe_ - string
+ * * _key_iter_ - integer
+ * * _mac_iter_ - integer
+ * * _keytype_ - An integer representing an MSIE specific extension.
*
- * Any optional arguments may be supplied as nil to preserve the OpenSSL defaults.
+ * Any optional arguments may be supplied as +nil+ to preserve the OpenSSL defaults.
*
* See the OpenSSL documentation for PKCS12_create().
*/
@@ -161,8 +156,8 @@ ossl_pkcs12_s_create(int argc, VALUE *argv, VALUE self)
* PKCS12.new(str, pass) -> pkcs12
*
* === Parameters
- * * +str+ - Must be a DER encoded PKCS12 string.
- * * +pass+ - string
+ * * _str_ - Must be a DER encoded PKCS12 string.
+ * * _pass_ - string
*/
static VALUE
ossl_pkcs12_initialize(int argc, VALUE *argv, VALUE self)
@@ -178,7 +173,7 @@ ossl_pkcs12_initialize(int argc, VALUE *argv, VALUE self)
if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) return self;
passphrase = NIL_P(pass) ? NULL : StringValueCStr(pass);
- in = ossl_obj2bio(arg);
+ in = ossl_obj2bio(&arg);
d2i_PKCS12_bio(in, &pkcs);
DATA_PTR(self) = pkcs;
BIO_free(in);
@@ -237,6 +232,7 @@ ossl_pkcs12_to_der(VALUE self)
void
Init_ossl_pkcs12(void)
{
+#undef rb_intern
#if 0
mOSSL = rb_define_module("OpenSSL");
eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
@@ -252,7 +248,7 @@ Init_ossl_pkcs12(void)
rb_define_singleton_method(cPKCS12, "create", ossl_pkcs12_s_create, -1);
rb_define_alloc_func(cPKCS12, ossl_pkcs12_s_allocate);
- rb_define_copy_func(cPKCS12, ossl_pkcs12_initialize_copy);
+ rb_define_method(cPKCS12, "initialize_copy", ossl_pkcs12_initialize_copy, 1);
rb_attr(cPKCS12, rb_intern("key"), 1, 0, Qfalse);
rb_attr(cPKCS12, rb_intern("certificate"), 1, 0, Qfalse);
rb_attr(cPKCS12, rb_intern("ca_certs"), 1, 0, Qfalse);
diff --git a/ext/openssl/ossl_pkcs5.c b/ext/openssl/ossl_pkcs5.c
deleted file mode 100644
index 47c5bfa3b8..0000000000
--- a/ext/openssl/ossl_pkcs5.c
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2007 Technorama Ltd. <oss-ruby@technorama.net>
- */
-#include "ossl.h"
-
-VALUE mPKCS5;
-VALUE ePKCS5;
-
-#ifdef HAVE_PKCS5_PBKDF2_HMAC
-/*
- * call-seq:
- * PKCS5.pbkdf2_hmac(pass, salt, iter, keylen, digest) => string
- *
- * === Parameters
- * * +pass+ - string
- * * +salt+ - string - should be at least 8 bytes long.
- * * +iter+ - integer - should be greater than 1000. 20000 is better.
- * * +keylen+ - integer
- * * +digest+ - a string or OpenSSL::Digest object.
- *
- * Available in OpenSSL >= 1.0.0.
- *
- * Digests other than SHA1 may not be supported by other cryptography libraries.
- */
-static VALUE
-ossl_pkcs5_pbkdf2_hmac(VALUE self, VALUE pass, VALUE salt, VALUE iter, VALUE keylen, VALUE digest)
-{
- VALUE str;
- const EVP_MD *md;
- int len = NUM2INT(keylen);
-
- StringValue(pass);
- StringValue(salt);
- md = GetDigestPtr(digest);
-
- str = rb_str_new(0, len);
-
- if (PKCS5_PBKDF2_HMAC(RSTRING_PTR(pass), RSTRING_LENINT(pass),
- (unsigned char *)RSTRING_PTR(salt), RSTRING_LENINT(salt),
- NUM2INT(iter), md, len,
- (unsigned char *)RSTRING_PTR(str)) != 1)
- ossl_raise(ePKCS5, "PKCS5_PBKDF2_HMAC");
-
- return str;
-}
-#else
-#define ossl_pkcs5_pbkdf2_hmac rb_f_notimplement
-#endif
-
-
-/*
- * call-seq:
- * PKCS5.pbkdf2_hmac_sha1(pass, salt, iter, keylen) => string
- *
- * === Parameters
- * * +pass+ - string
- * * +salt+ - string - should be at least 8 bytes long.
- * * +iter+ - integer - should be greater than 1000. 20000 is better.
- * * +keylen+ - integer
- *
- * This method is available in almost any version of OpenSSL.
- *
- * Conforms to RFC 2898.
- */
-static VALUE
-ossl_pkcs5_pbkdf2_hmac_sha1(VALUE self, VALUE pass, VALUE salt, VALUE iter, VALUE keylen)
-{
- VALUE str;
- int len = NUM2INT(keylen);
-
- StringValue(pass);
- StringValue(salt);
-
- str = rb_str_new(0, len);
-
- if (PKCS5_PBKDF2_HMAC_SHA1(RSTRING_PTR(pass), RSTRING_LENINT(pass),
- (const unsigned char *)RSTRING_PTR(salt), RSTRING_LENINT(salt), NUM2INT(iter),
- len, (unsigned char *)RSTRING_PTR(str)) != 1)
- ossl_raise(ePKCS5, "PKCS5_PBKDF2_HMAC_SHA1");
-
- return str;
-}
-
-void
-Init_ossl_pkcs5(void)
-{
-#if 0
- mOSSL = rb_define_module("OpenSSL");
- eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
-#endif
-
- /* Document-class: OpenSSL::PKCS5
- *
- * Provides password-based encryption functionality based on PKCS#5.
- * Typically used for securely deriving arbitrary length symmetric keys
- * to be used with an OpenSSL::Cipher from passwords. Another use case
- * is for storing passwords: Due to the ability to tweak the effort of
- * computation by increasing the iteration count, computation can be
- * slowed down artificially in order to render possible attacks infeasible.
- *
- * PKCS5 offers support for PBKDF2 with an OpenSSL::Digest::SHA1-based
- * HMAC, or an arbitrary Digest if the underlying version of OpenSSL
- * already supports it (>= 1.0.0).
- *
- * === Parameters
- * ==== Password
- * Typically an arbitrary String that represents the password to be used
- * for deriving a key.
- * ==== Salt
- * Prevents attacks based on dictionaries of common passwords. It is a
- * public value that can be safely stored along with the password (e.g.
- * if PBKDF2 is used for password storage). For maximum security, a fresh,
- * random salt should be generated for each stored password. According
- * to PKCS#5, a salt should be at least 8 bytes long.
- * ==== Iteration Count
- * Allows to tweak the length that the actual computation will take. The
- * larger the iteration count, the longer it will take.
- * ==== Key Length
- * Specifies the length in bytes of the output that will be generated.
- * Typically, the key length should be larger than or equal to the output
- * length of the underlying digest function, otherwise an attacker could
- * simply try to brute-force the key. According to PKCS#5, security is
- * limited by the output length of the underlying digest function, i.e.
- * security is not improved if a key length strictly larger than the
- * digest output length is chosen. Therefore, when using PKCS5 for
- * password storage, it suffices to store values equal to the digest
- * output length, nothing is gained by storing larger values.
- *
- * == Examples
- * === Generating a 128 bit key for a Cipher (e.g. AES)
- * pass = "secret"
- * salt = OpenSSL::Random.random_bytes(16)
- * iter = 20000
- * key_len = 16
- * key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(pass, salt, iter, key_len)
- *
- * === Storing Passwords
- * pass = "secret"
- * salt = OpenSSL::Random.random_bytes(16) #store this with the generated value
- * iter = 20000
- * digest = OpenSSL::Digest::SHA256.new
- * len = digest.digest_length
- * #the final value to be stored
- * value = OpenSSL::PKCS5.pbkdf2_hmac(pass, salt, iter, len, digest)
- *
- * === Important Note on Checking Passwords
- * When comparing passwords provided by the user with previously stored
- * values, a common mistake made is comparing the two values using "==".
- * Typically, "==" short-circuits on evaluation, and is therefore
- * vulnerable to timing attacks. The proper way is to use a method that
- * always takes the same amount of time when comparing two values, thus
- * not leaking any information to potential attackers. To compare two
- * values, the following could be used:
- * def eql_time_cmp(a, b)
- * unless a.length == b.length
- * return false
- * end
- * cmp = b.bytes.to_a
- * result = 0
- * a.bytes.each_with_index {|c,i|
- * result |= c ^ cmp[i]
- * }
- * result == 0
- * end
- * Please note that the premature return in case of differing lengths
- * typically does not leak valuable information - when using PKCS#5, the
- * length of the values to be compared is of fixed size.
- */
-
- mPKCS5 = rb_define_module_under(mOSSL, "PKCS5");
- /* Document-class: OpenSSL::PKCS5::PKCS5Error
- *
- * Generic Exception class that is raised if an error occurs during a
- * computation.
- */
- ePKCS5 = rb_define_class_under(mPKCS5, "PKCS5Error", eOSSLError);
-
- rb_define_module_function(mPKCS5, "pbkdf2_hmac", ossl_pkcs5_pbkdf2_hmac, 5);
- rb_define_module_function(mPKCS5, "pbkdf2_hmac_sha1", ossl_pkcs5_pbkdf2_hmac_sha1, 4);
-}
diff --git a/ext/openssl/ossl_pkcs5.h b/ext/openssl/ossl_pkcs5.h
deleted file mode 100644
index a3b132bc50..0000000000
--- a/ext/openssl/ossl_pkcs5.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#if !defined(_OSSL_PKCS5_H_)
-#define _OSSL_PKCS5_H_
-
-void Init_ossl_pkcs5(void);
-
-#endif /* _OSSL_PKCS5_H_ */
diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c
index fd58b48be8..28010c81fb 100644
--- a/ext/openssl/ossl_pkcs7.c
+++ b/ext/openssl/ossl_pkcs7.c
@@ -23,10 +23,6 @@
ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \
} \
} while (0)
-#define SafeGetPKCS7(obj, pkcs7) do { \
- OSSL_Check_Kind((obj), cPKCS7); \
- GetPKCS7((obj), (pkcs7)); \
-} while (0)
#define NewPKCS7si(klass) \
TypedData_Wrap_Struct((klass), &ossl_pkcs7_signer_info_type, 0)
@@ -42,10 +38,6 @@
ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \
} \
} while (0)
-#define SafeGetPKCS7si(obj, p7si) do { \
- OSSL_Check_Kind((obj), cPKCS7Signer); \
- GetPKCS7si((obj), (p7si)); \
-} while (0)
#define NewPKCS7ri(klass) \
TypedData_Wrap_Struct((klass), &ossl_pkcs7_recip_info_type, 0)
@@ -61,10 +53,6 @@
ossl_raise(rb_eRuntimeError, "PKCS7ri wasn't initialized."); \
} \
} while (0)
-#define SafeGetPKCS7ri(obj, p7ri) do { \
- OSSL_Check_Kind((obj), cPKCS7Recipient); \
- GetPKCS7ri((obj), (p7ri)); \
-} while (0)
#define numberof(ary) (int)(sizeof(ary)/sizeof((ary)[0]))
@@ -162,7 +150,7 @@ DupPKCS7SignerPtr(VALUE obj)
{
PKCS7_SIGNER_INFO *p7si, *pkcs7;
- SafeGetPKCS7si(obj, p7si);
+ GetPKCS7si(obj, p7si);
if (!(pkcs7 = ossl_PKCS7_SIGNER_INFO_dup(p7si))) {
ossl_raise(ePKCS7Error, NULL);
}
@@ -189,7 +177,7 @@ DupPKCS7RecipientPtr(VALUE obj)
{
PKCS7_RECIP_INFO *p7ri, *pkcs7;
- SafeGetPKCS7ri(obj, p7ri);
+ GetPKCS7ri(obj, p7ri);
if (!(pkcs7 = ossl_PKCS7_RECIP_INFO_dup(p7ri))) {
ossl_raise(ePKCS7Error, NULL);
}
@@ -209,7 +197,7 @@ ossl_pkcs7_s_read_smime(VALUE klass, VALUE arg)
VALUE ret, data;
ret = NewPKCS7(cPKCS7);
- in = ossl_obj2bio(arg);
+ in = ossl_obj2bio(&arg);
out = NULL;
pkcs7 = SMIME_read_PKCS7(in, &out);
BIO_free(in);
@@ -238,10 +226,10 @@ ossl_pkcs7_s_write_smime(int argc, VALUE *argv, VALUE klass)
rb_scan_args(argc, argv, "12", &pkcs7, &data, &flags);
flg = NIL_P(flags) ? 0 : NUM2INT(flags);
if(NIL_P(data)) data = ossl_pkcs7_get_data(pkcs7);
- SafeGetPKCS7(pkcs7, p7);
+ GetPKCS7(pkcs7, p7);
if(!NIL_P(data) && PKCS7_is_detached(p7))
flg |= PKCS7_DETACHED;
- in = NIL_P(data) ? NULL : ossl_obj2bio(data);
+ in = NIL_P(data) ? NULL : ossl_obj2bio(&data);
if(!(out = BIO_new(BIO_s_mem()))){
BIO_free(in);
ossl_raise(ePKCS7Error, NULL);
@@ -278,7 +266,7 @@ ossl_pkcs7_s_sign(int argc, VALUE *argv, VALUE klass)
pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
flg = NIL_P(flags) ? 0 : NUM2INT(flags);
ret = NewPKCS7(cPKCS7);
- in = ossl_obj2bio(data);
+ in = ossl_obj2bio(&data);
if(NIL_P(certs)) x509s = NULL;
else{
x509s = ossl_protect_x509_ary2sk(certs, &status);
@@ -331,10 +319,10 @@ ossl_pkcs7_s_encrypt(int argc, VALUE *argv, VALUE klass)
#endif
}
- else ciph = GetCipherPtr(cipher); /* NO NEED TO DUP */
+ else ciph = ossl_evp_get_cipherbyname(cipher);
flg = NIL_P(flags) ? 0 : NUM2INT(flags);
ret = NewPKCS7(cPKCS7);
- in = ossl_obj2bio(data);
+ in = ossl_obj2bio(&data);
x509s = ossl_protect_x509_ary2sk(certs, &status);
if(status){
BIO_free(in);
@@ -385,7 +373,7 @@ ossl_pkcs7_initialize(int argc, VALUE *argv, VALUE self)
if(rb_scan_args(argc, argv, "01", &arg) == 0)
return self;
arg = ossl_to_der_if_possible(arg);
- in = ossl_obj2bio(arg);
+ in = ossl_obj2bio(&arg);
p7 = PEM_read_bio_PKCS7(in, &pkcs, NULL, NULL);
if (!p7) {
OSSL_BIO_reset(in);
@@ -414,7 +402,7 @@ ossl_pkcs7_copy(VALUE self, VALUE other)
if (self == other) return self;
GetPKCS7(self, a);
- SafeGetPKCS7(other, b);
+ GetPKCS7(other, b);
pkcs7 = PKCS7_dup(b);
if (!pkcs7) {
@@ -537,7 +525,7 @@ ossl_pkcs7_set_cipher(VALUE self, VALUE cipher)
PKCS7 *pkcs7;
GetPKCS7(self, pkcs7);
- if (!PKCS7_set_cipher(pkcs7, GetCipherPtr(cipher))) {
+ if (!PKCS7_set_cipher(pkcs7, ossl_evp_get_cipherbyname(cipher))) {
ossl_raise(ePKCS7Error, NULL);
}
@@ -777,7 +765,7 @@ ossl_pkcs7_verify(int argc, VALUE *argv, VALUE self)
x509st = GetX509StorePtr(store);
flg = NIL_P(flags) ? 0 : NUM2INT(flags);
if(NIL_P(indata)) indata = ossl_pkcs7_get_data(self);
- in = NIL_P(indata) ? NULL : ossl_obj2bio(indata);
+ in = NIL_P(indata) ? NULL : ossl_obj2bio(&indata);
if(NIL_P(certs)) x509s = NULL;
else{
x509s = ossl_protect_x509_ary2sk(certs, &status);
@@ -795,7 +783,7 @@ ossl_pkcs7_verify(int argc, VALUE *argv, VALUE self)
BIO_free(in);
sk_X509_pop_free(x509s, X509_free);
if (ok < 0) ossl_raise(ePKCS7Error, "PKCS7_verify");
- msg = ERR_reason_error_string(ERR_get_error());
+ msg = ERR_reason_error_string(ERR_peek_error());
ossl_pkcs7_set_err_string(self, msg ? rb_str_new2(msg) : Qnil);
ossl_clear_error();
data = ossl_membio2str(out);
@@ -815,9 +803,9 @@ ossl_pkcs7_decrypt(int argc, VALUE *argv, VALUE self)
BIO *out;
VALUE str;
- rb_scan_args(argc, argv, "21", &pkey, &cert, &flags);
+ rb_scan_args(argc, argv, "12", &pkey, &cert, &flags);
key = GetPrivPKeyPtr(pkey); /* NO NEED TO DUP */
- x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */
+ x509 = NIL_P(cert) ? NULL : GetX509CertPtr(cert); /* NO NEED TO DUP */
flg = NIL_P(flags) ? 0 : NUM2INT(flags);
GetPKCS7(self, p7);
if(!(out = BIO_new(BIO_s_mem())))
@@ -844,7 +832,7 @@ ossl_pkcs7_add_data(VALUE self, VALUE data)
if(!PKCS7_content_new(pkcs7, NID_pkcs7_data))
ossl_raise(ePKCS7Error, NULL);
}
- in = ossl_obj2bio(data);
+ in = ossl_obj2bio(&data);
if(!(out = PKCS7_dataInit(pkcs7, NULL))) goto err;
for(;;){
if((len = BIO_read(in, buf, sizeof(buf))) <= 0)
@@ -933,7 +921,7 @@ ossl_pkcs7si_initialize(VALUE self, VALUE cert, VALUE key, VALUE digest)
pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */
- md = GetDigestPtr(digest);
+ md = ossl_evp_get_digestbyname(digest);
GetPKCS7si(self, p7si);
if (!(PKCS7_SIGNER_INFO_set(p7si, x509, pkey, (EVP_MD*)md))) {
ossl_raise(ePKCS7Error, NULL);
@@ -1054,6 +1042,7 @@ ossl_pkcs7ri_get_enc_key(VALUE self)
void
Init_ossl_pkcs7(void)
{
+#undef rb_intern
#if 0
mOSSL = rb_define_module("OpenSSL");
eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
@@ -1068,7 +1057,7 @@ Init_ossl_pkcs7(void)
rb_attr(cPKCS7, rb_intern("data"), 1, 0, Qfalse);
rb_attr(cPKCS7, rb_intern("error_string"), 1, 1, Qfalse);
rb_define_alloc_func(cPKCS7, ossl_pkcs7_alloc);
- rb_define_copy_func(cPKCS7, ossl_pkcs7_copy);
+ rb_define_method(cPKCS7, "initialize_copy", ossl_pkcs7_copy, 1);
rb_define_method(cPKCS7, "initialize", ossl_pkcs7_initialize, -1);
rb_define_method(cPKCS7, "type=", ossl_pkcs7_set_type, 1);
rb_define_method(cPKCS7, "type", ossl_pkcs7_get_type, 0);
diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c
index 3c7c5e1781..e1fffb2446 100644
--- a/ext/openssl/ossl_pkey.c
+++ b/ext/openssl/ossl_pkey.c
@@ -20,6 +20,21 @@ static ID id_private_q;
/*
* callback for generating keys
*/
+static VALUE
+call_check_ints0(VALUE arg)
+{
+ rb_thread_check_ints();
+ return Qnil;
+}
+
+static void *
+call_check_ints(void *arg)
+{
+ int state;
+ rb_protect(call_check_ints0, Qnil, &state);
+ return (void *)(VALUE)state;
+}
+
int
ossl_generate_cb_2(int p, int n, BN_GENCB *cb)
{
@@ -38,11 +53,18 @@ ossl_generate_cb_2(int p, int n, BN_GENCB *cb)
*/
rb_protect(rb_yield, ary, &state);
if (state) {
- arg->stop = 1;
arg->state = state;
+ return 0;
+ }
+ }
+ if (arg->interrupted) {
+ arg->interrupted = 0;
+ state = (int)(VALUE)rb_thread_call_with_gvl(call_check_ints, NULL);
+ if (state) {
+ arg->state = state;
+ return 0;
}
}
- if (arg->stop) return 0;
return 1;
}
@@ -50,7 +72,7 @@ void
ossl_generate_cb_stop(void *ptr)
{
struct ossl_generate_cb_arg *arg = (struct ossl_generate_cb_arg *)ptr;
- arg->stop = 1;
+ arg->interrupted = 1;
}
static void
@@ -70,13 +92,16 @@ const rb_data_type_t ossl_evp_pkey_type = {
0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
};
-VALUE
-ossl_pkey_new(EVP_PKEY *pkey)
+static VALUE
+pkey_new0(EVP_PKEY *pkey)
{
- if (!pkey) {
- ossl_raise(ePKeyError, "Cannot make new key from NULL.");
- }
- switch (EVP_PKEY_base_id(pkey)) {
+ VALUE obj;
+ int type;
+
+ if (!pkey || (type = EVP_PKEY_base_id(pkey)) == EVP_PKEY_NONE)
+ ossl_raise(rb_eRuntimeError, "pkey is empty");
+
+ switch (type) {
#if !defined(OPENSSL_NO_RSA)
case EVP_PKEY_RSA:
return ossl_rsa_new(pkey);
@@ -89,36 +114,30 @@ ossl_pkey_new(EVP_PKEY *pkey)
case EVP_PKEY_DH:
return ossl_dh_new(pkey);
#endif
-#if !defined(OPENSSL_NO_EC) && (OPENSSL_VERSION_NUMBER >= 0x0090802fL)
+#if !defined(OPENSSL_NO_EC)
case EVP_PKEY_EC:
return ossl_ec_new(pkey);
#endif
default:
- ossl_raise(ePKeyError, "unsupported key type");
+ obj = NewPKey(cPKey);
+ SetPKey(obj, pkey);
+ return obj;
}
-
- UNREACHABLE;
}
VALUE
-ossl_pkey_new_from_file(VALUE filename)
+ossl_pkey_new(EVP_PKEY *pkey)
{
- FILE *fp;
- EVP_PKEY *pkey;
-
- rb_check_safe_obj(filename);
- if (!(fp = fopen(StringValueCStr(filename), "r"))) {
- ossl_raise(ePKeyError, "%s", strerror(errno));
- }
- rb_fd_fix_cloexec(fileno(fp));
+ VALUE obj;
+ int status;
- pkey = PEM_read_PrivateKey(fp, NULL, ossl_pem_passwd_cb, NULL);
- fclose(fp);
- if (!pkey) {
- ossl_raise(ePKeyError, NULL);
+ obj = rb_protect((VALUE (*)(VALUE))pkey_new0, (VALUE)pkey, &status);
+ if (status) {
+ EVP_PKEY_free(pkey);
+ rb_jump_tag(status);
}
- return ossl_pkey_new(pkey);
+ return obj;
}
/*
@@ -126,15 +145,15 @@ ossl_pkey_new_from_file(VALUE filename)
* OpenSSL::PKey.read(string [, pwd ]) -> PKey
* OpenSSL::PKey.read(io [, pwd ]) -> PKey
*
- * Reads a DER or PEM encoded string from +string+ or +io+ and returns an
+ * Reads a DER or PEM encoded string from _string_ or _io_ and returns an
* instance of the appropriate PKey class.
*
* === Parameters
- * * +string+ is a DER- or PEM-encoded string containing an arbitrary private
+ * * _string+ is a DER- or PEM-encoded string containing an arbitrary private
* or public key.
- * * +io+ is an instance of +IO+ containing a DER- or PEM-encoded
+ * * _io_ is an instance of IO containing a DER- or PEM-encoded
* arbitrary private or public key.
- * * +pwd+ is an optional password in case +string+ or +file+ is an encrypted
+ * * _pwd_ is an optional password in case _string_ or _io_ is an encrypted
* PEM resource.
*/
static VALUE
@@ -147,7 +166,7 @@ ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self)
rb_scan_args(argc, argv, "11", &data, &pass);
pass = ossl_pem_passwd_value(pass);
- bio = ossl_obj2bio(data);
+ bio = ossl_obj2bio(&data);
if (!(pkey = d2i_PrivateKey_bio(bio, NULL))) {
OSSL_BIO_reset(bio);
if (!(pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, (void *)pass))) {
@@ -166,12 +185,52 @@ ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self)
return ossl_pkey_new(pkey);
}
+void
+ossl_pkey_check_public_key(const EVP_PKEY *pkey)
+{
+ void *ptr;
+ const BIGNUM *n, *e, *pubkey;
+
+ if (EVP_PKEY_missing_parameters(pkey))
+ ossl_raise(ePKeyError, "parameters missing");
+
+ /* OpenSSL < 1.1.0 takes non-const pointer */
+ ptr = EVP_PKEY_get0((EVP_PKEY *)pkey);
+ switch (EVP_PKEY_base_id(pkey)) {
+ case EVP_PKEY_RSA:
+ RSA_get0_key(ptr, &n, &e, NULL);
+ if (n && e)
+ return;
+ break;
+ case EVP_PKEY_DSA:
+ DSA_get0_key(ptr, &pubkey, NULL);
+ if (pubkey)
+ return;
+ break;
+ case EVP_PKEY_DH:
+ DH_get0_key(ptr, &pubkey, NULL);
+ if (pubkey)
+ return;
+ break;
+#if !defined(OPENSSL_NO_EC)
+ case EVP_PKEY_EC:
+ if (EC_KEY_get0_public_key(ptr))
+ return;
+ break;
+#endif
+ default:
+ /* unsupported type; assuming ok */
+ return;
+ }
+ ossl_raise(ePKeyError, "public key missing");
+}
+
EVP_PKEY *
GetPKeyPtr(VALUE obj)
{
EVP_PKEY *pkey;
- SafeGetPKey(obj, pkey);
+ GetPKey(obj, pkey);
return pkey;
}
@@ -184,7 +243,7 @@ GetPrivPKeyPtr(VALUE obj)
if (rb_funcallv(obj, id_private_q, 0, NULL) != Qtrue) {
ossl_raise(rb_eArgError, "Private key is needed.");
}
- SafeGetPKey(obj, pkey);
+ GetPKey(obj, pkey);
return pkey;
}
@@ -194,7 +253,7 @@ DupPKeyPtr(VALUE obj)
{
EVP_PKEY *pkey;
- SafeGetPKey(obj, pkey);
+ GetPKey(obj, pkey);
EVP_PKEY_up_ref(pkey);
return pkey;
@@ -223,13 +282,13 @@ ossl_pkey_alloc(VALUE klass)
* PKeyClass.new -> self
*
* Because PKey is an abstract class, actually calling this method explicitly
- * will raise a +NotImplementedError+.
+ * will raise a NotImplementedError.
*/
static VALUE
ossl_pkey_initialize(VALUE self)
{
if (rb_obj_is_instance_of(self, cPKey)) {
- ossl_raise(rb_eNotImpError, "OpenSSL::PKey::PKey is an abstract class.");
+ ossl_raise(rb_eTypeError, "OpenSSL::PKey::PKey can't be instantiated directly");
}
return self;
}
@@ -238,10 +297,10 @@ ossl_pkey_initialize(VALUE self)
* call-seq:
* pkey.sign(digest, data) -> String
*
- * To sign the +String+ +data+, +digest+, an instance of OpenSSL::Digest, must
- * be provided. The return value is again a +String+ containing the signature.
+ * To sign the String _data_, _digest_, an instance of OpenSSL::Digest, must
+ * be provided. The return value is again a String containing the signature.
* A PKeyError is raised should errors occur.
- * Any previous state of the +Digest+ instance is irrelevant to the signature
+ * Any previous state of the Digest instance is irrelevant to the signature
* outcome, the digest instance is reset to its initial state during the
* operation.
*
@@ -262,20 +321,25 @@ ossl_pkey_sign(VALUE self, VALUE digest, VALUE data)
int result;
pkey = GetPrivPKeyPtr(self);
- md = GetDigestPtr(digest);
+ md = ossl_evp_get_digestbyname(digest);
StringValue(data);
- str = rb_str_new(0, EVP_PKEY_size(pkey)+16);
+ str = rb_str_new(0, EVP_PKEY_size(pkey));
ctx = EVP_MD_CTX_new();
if (!ctx)
ossl_raise(ePKeyError, "EVP_MD_CTX_new");
- EVP_SignInit(ctx, md);
- EVP_SignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data));
+ if (!EVP_SignInit_ex(ctx, md, NULL)) {
+ EVP_MD_CTX_free(ctx);
+ ossl_raise(ePKeyError, "EVP_SignInit_ex");
+ }
+ if (!EVP_SignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) {
+ EVP_MD_CTX_free(ctx);
+ ossl_raise(ePKeyError, "EVP_SignUpdate");
+ }
result = EVP_SignFinal(ctx, (unsigned char *)RSTRING_PTR(str), &buf_len, pkey);
EVP_MD_CTX_free(ctx);
if (!result)
- ossl_raise(ePKeyError, NULL);
- assert((long)buf_len <= RSTRING_LEN(str));
+ ossl_raise(ePKeyError, "EVP_SignFinal");
rb_str_set_len(str, buf_len);
return str;
@@ -285,12 +349,12 @@ ossl_pkey_sign(VALUE self, VALUE digest, VALUE data)
* call-seq:
* pkey.verify(digest, signature, data) -> String
*
- * To verify the +String+ +signature+, +digest+, an instance of
+ * To verify the String _signature_, _digest_, an instance of
* OpenSSL::Digest, must be provided to re-compute the message digest of the
- * original +data+, also a +String+. The return value is +true+ if the
+ * original _data_, also a String. The return value is +true+ if the
* signature is valid, +false+ otherwise. A PKeyError is raised should errors
* occur.
- * Any previous state of the +Digest+ instance is irrelevant to the validation
+ * Any previous state of the Digest instance is irrelevant to the validation
* outcome, the digest instance is reset to its initial state during the
* operation.
*
@@ -308,19 +372,27 @@ ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data)
EVP_PKEY *pkey;
const EVP_MD *md;
EVP_MD_CTX *ctx;
- int result;
+ int siglen, result;
GetPKey(self, pkey);
- md = GetDigestPtr(digest);
+ ossl_pkey_check_public_key(pkey);
+ md = ossl_evp_get_digestbyname(digest);
StringValue(sig);
+ siglen = RSTRING_LENINT(sig);
StringValue(data);
ctx = EVP_MD_CTX_new();
if (!ctx)
ossl_raise(ePKeyError, "EVP_MD_CTX_new");
- EVP_VerifyInit(ctx, md);
- EVP_VerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data));
- result = EVP_VerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig), RSTRING_LENINT(sig), pkey);
+ if (!EVP_VerifyInit_ex(ctx, md, NULL)) {
+ EVP_MD_CTX_free(ctx);
+ ossl_raise(ePKeyError, "EVP_VerifyInit_ex");
+ }
+ if (!EVP_VerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) {
+ EVP_MD_CTX_free(ctx);
+ ossl_raise(ePKeyError, "EVP_VerifyUpdate");
+ }
+ result = EVP_VerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig), siglen, pkey);
EVP_MD_CTX_free(ctx);
switch (result) {
case 0:
@@ -329,9 +401,8 @@ ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data)
case 1:
return Qtrue;
default:
- ossl_raise(ePKeyError, NULL);
+ ossl_raise(ePKeyError, "EVP_VerifyFinal");
}
- return Qnil; /* dummy */
}
/*
@@ -340,6 +411,7 @@ ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data)
void
Init_ossl_pkey(void)
{
+#undef rb_intern
#if 0
mOSSL = rb_define_module("OpenSSL");
eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h
index 218f2ebbae..0db59305f7 100644
--- a/ext/openssl/ossl_pkey.h
+++ b/ext/openssl/ossl_pkey.h
@@ -34,21 +34,17 @@ extern const rb_data_type_t ossl_evp_pkey_type;
rb_raise(rb_eRuntimeError, "PKEY wasn't initialized!");\
} \
} while (0)
-#define SafeGetPKey(obj, pkey) do { \
- OSSL_Check_Kind((obj), cPKey); \
- GetPKey((obj), (pkey)); \
-} while (0)
struct ossl_generate_cb_arg {
int yield;
- int stop;
+ int interrupted;
int state;
};
int ossl_generate_cb_2(int p, int n, BN_GENCB *cb);
void ossl_generate_cb_stop(void *ptr);
VALUE ossl_pkey_new(EVP_PKEY *);
-VALUE ossl_pkey_new_from_file(VALUE);
+void ossl_pkey_check_public_key(const EVP_PKEY *);
EVP_PKEY *GetPKeyPtr(VALUE);
EVP_PKEY *DupPKeyPtr(VALUE);
EVP_PKEY *GetPrivPKeyPtr(VALUE);
@@ -137,9 +133,9 @@ static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2, VALU
BIGNUM *bn3 = NULL, *orig_bn3 = NIL_P(v3) ? NULL : GetBNPtr(v3);\
\
Get##_type(self, obj); \
- if (orig_bn1 && !(bn1 = BN_dup(orig_bn1)) || \
- orig_bn2 && !(bn2 = BN_dup(orig_bn2)) || \
- orig_bn3 && !(bn3 = BN_dup(orig_bn3))) { \
+ if ((orig_bn1 && !(bn1 = BN_dup(orig_bn1))) || \
+ (orig_bn2 && !(bn2 = BN_dup(orig_bn2))) || \
+ (orig_bn3 && !(bn3 = BN_dup(orig_bn3)))) { \
BN_clear_free(bn1); \
BN_clear_free(bn2); \
BN_clear_free(bn3); \
@@ -167,8 +163,8 @@ static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2) \
BIGNUM *bn2 = NULL, *orig_bn2 = NIL_P(v2) ? NULL : GetBNPtr(v2);\
\
Get##_type(self, obj); \
- if (orig_bn1 && !(bn1 = BN_dup(orig_bn1)) || \
- orig_bn2 && !(bn2 = BN_dup(orig_bn2))) { \
+ if ((orig_bn1 && !(bn1 = BN_dup(orig_bn1))) || \
+ (orig_bn2 && !(bn2 = BN_dup(orig_bn2)))) { \
BN_clear_free(bn1); \
BN_clear_free(bn2); \
ossl_raise(eBNError, NULL); \
diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c
index 938efe1abc..bf4e3f9322 100644
--- a/ext/openssl/ossl_pkey_dh.c
+++ b/ext/openssl/ossl_pkey_dh.c
@@ -150,8 +150,8 @@ dh_generate(int size, int gen)
* components alike.
*
* === Parameters
- * * +size+ is an integer representing the desired key size. Keys smaller than 1024 bits should be considered insecure.
- * * +generator+ is a small number > 1, typically 2 or 5.
+ * * _size_ is an integer representing the desired key size. Keys smaller than 1024 bits should be considered insecure.
+ * * _generator_ is a small number > 1, typically 2 or 5.
*
*/
static VALUE
@@ -181,15 +181,15 @@ ossl_dh_s_generate(int argc, VALUE *argv, VALUE klass)
* DH.new(size [, generator]) -> dh
*
* Either generates a DH instance from scratch or by reading already existing
- * DH parameters from +string+. Note that when reading a DH instance from
+ * DH parameters from _string_. Note that when reading a DH instance from
* data that was encoded from a DH instance by using DH#to_pem or DH#to_der
* the result will *not* contain a public/private key pair yet. This needs to
* be generated using DH#generate_key! first.
*
* === Parameters
- * * +size+ is an integer representing the desired key size. Keys smaller than 1024 bits should be considered insecure.
- * * +generator+ is a small number > 1, typically 2 or 5.
- * * +string+ contains the DER or PEM encoded key.
+ * * _size_ is an integer representing the desired key size. Keys smaller than 1024 bits should be considered insecure.
+ * * _generator_ is a small number > 1, typically 2 or 5.
+ * * _string_ contains the DER or PEM encoded key.
*
* === Examples
* DH.new # -> dh
@@ -222,7 +222,7 @@ ossl_dh_initialize(int argc, VALUE *argv, VALUE self)
}
else {
arg = ossl_to_der_if_possible(arg);
- in = ossl_obj2bio(arg);
+ in = ossl_obj2bio(&arg);
dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL);
if (!dh){
OSSL_BIO_reset(in);
@@ -262,7 +262,7 @@ ossl_dh_initialize_copy(VALUE self, VALUE other)
BIGNUM *pub2 = BN_dup(pub);
BIGNUM *priv2 = BN_dup(priv);
- if (!pub2 || priv && !priv2) {
+ if (!pub2 || (priv && !priv2)) {
BN_clear_free(pub2);
BN_clear_free(priv2);
ossl_raise(eDHError, "BN_dup");
@@ -436,7 +436,7 @@ ossl_dh_to_text(VALUE self)
* dh.public_key -> aDH
*
* Returns a new DH instance that carries just the public information, i.e.
- * the prime +p+ and the generator +g+, but no public/private key yet. Such
+ * the prime _p_ and the generator _g_, but no public/private key yet. Such
* a pair may be generated using DH#generate_key!. The "public key" needed
* for a key exchange with DH#compute_key is considered as per-session
* information and may be retrieved with DH#pub_key once a key pair has
@@ -460,7 +460,7 @@ ossl_dh_to_public_key(VALUE self)
GetDH(self, orig_dh);
dh = DHparams_dup(orig_dh); /* err check perfomed by dh_instance */
- obj = dh_instance(CLASS_OF(self), dh);
+ obj = dh_instance(rb_obj_class(self), dh);
if (obj == Qfalse) {
DH_free(dh);
ossl_raise(eDHError, NULL);
@@ -526,7 +526,7 @@ ossl_dh_generate_key(VALUE self)
* See DH_compute_key() for further information.
*
* === Parameters
- * * +pub_bn+ is a OpenSSL::BN, *not* the DH instance returned by
+ * * _pub_bn_ is a OpenSSL::BN, *not* the DH instance returned by
* DH#public_key as that contains the DH parameters only.
*/
static VALUE
@@ -557,7 +557,7 @@ ossl_dh_compute_key(VALUE self, VALUE pub)
* call-seq:
* dh.set_pqg(p, q, g) -> self
*
- * Sets +p+, +q+, +g+ for the DH instance.
+ * Sets _p_, _q_, _g_ to the DH instance.
*/
OSSL_PKEY_BN_DEF3(dh, DH, pqg, p, q, g)
/*
@@ -565,7 +565,7 @@ OSSL_PKEY_BN_DEF3(dh, DH, pqg, p, q, g)
* call-seq:
* dh.set_key(pub_key, priv_key) -> self
*
- * Sets +pub_key+ and +priv_key+ for the DH instance. +priv_key+ may be nil.
+ * Sets _pub_key_ and _priv_key_ for the DH instance. _priv_key_ may be +nil+.
*/
OSSL_PKEY_BN_DEF2(dh, DH, key, pub_key, priv_key)
@@ -618,7 +618,7 @@ Init_ossl_dh(void)
cDH = rb_define_class_under(mPKey, "DH", cPKey);
rb_define_singleton_method(cDH, "generate", ossl_dh_s_generate, -1);
rb_define_method(cDH, "initialize", ossl_dh_initialize, -1);
- rb_define_copy_func(cDH, ossl_dh_initialize_copy);
+ rb_define_method(cDH, "initialize_copy", ossl_dh_initialize_copy, 1);
rb_define_method(cDH, "public?", ossl_dh_is_public, 0);
rb_define_method(cDH, "private?", ossl_dh_is_private, 0);
rb_define_method(cDH, "to_text", ossl_dh_to_text, 0);
diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c
index 3821cd813c..56cc9dd4f1 100644
--- a/ext/openssl/ossl_pkey_dsa.c
+++ b/ext/openssl/ossl_pkey_dsa.c
@@ -172,7 +172,7 @@ dsa_generate(int size)
* from scratch.
*
* === Parameters
- * * +size+ is an integer representing the desired key size.
+ * * _size_ is an integer representing the desired key size.
*
*/
static VALUE
@@ -195,12 +195,12 @@ ossl_dsa_s_generate(VALUE klass, VALUE size)
* DSA.new(size) -> dsa
* DSA.new(string [, pass]) -> dsa
*
- * Creates a new DSA instance by reading an existing key from +string+.
+ * Creates a new DSA instance by reading an existing key from _string_.
*
* === Parameters
- * * +size+ is an integer representing the desired key size.
- * * +string+ contains a DER or PEM encoded key.
- * * +pass+ is a string that contains an optional password.
+ * * _size_ is an integer representing the desired key size.
+ * * _string_ contains a DER or PEM encoded key.
+ * * _pass_ is a string that contains an optional password.
*
* === Examples
* DSA.new -> dsa
@@ -229,7 +229,7 @@ ossl_dsa_initialize(int argc, VALUE *argv, VALUE self)
else {
pass = ossl_pem_passwd_value(pass);
arg = ossl_to_der_if_possible(arg);
- in = ossl_obj2bio(arg);
+ in = ossl_obj2bio(&arg);
dsa = PEM_read_bio_DSAPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass);
if (!dsa) {
OSSL_BIO_reset(in);
@@ -329,8 +329,8 @@ ossl_dsa_is_private(VALUE self)
* Encodes this DSA to its PEM encoding.
*
* === Parameters
- * * +cipher+ is an OpenSSL::Cipher.
- * * +password+ is a string containing your password.
+ * * _cipher_ is an OpenSSL::Cipher.
+ * * _password_ is a string containing your password.
*
* === Examples
* DSA.to_pem -> aString
@@ -348,7 +348,7 @@ ossl_dsa_export(int argc, VALUE *argv, VALUE self)
GetDSA(self, dsa);
rb_scan_args(argc, argv, "02", &cipher, &pass);
if (!NIL_P(cipher)) {
- ciph = GetCipherPtr(cipher);
+ ciph = ossl_evp_get_cipherbyname(cipher);
pass = ossl_pem_passwd_value(pass);
}
if (!(out = BIO_new(BIO_s_mem()))) {
@@ -491,7 +491,7 @@ ossl_dsa_to_public_key(VALUE self)
(i2d_of_void *)i2d_DSAPublicKey, (d2i_of_void *)d2i_DSAPublicKey, (char *)(dsa))
dsa = DSAPublicKey_dup(EVP_PKEY_get0_DSA(pkey));
#undef DSAPublicKey_dup
- obj = dsa_instance(CLASS_OF(self), dsa);
+ obj = dsa_instance(rb_obj_class(self), dsa);
if (obj == Qfalse) {
DSA_free(dsa);
ossl_raise(eDSAError, NULL);
@@ -499,18 +499,16 @@ ossl_dsa_to_public_key(VALUE self)
return obj;
}
-#define ossl_dsa_buf_size(dsa) (DSA_size(dsa) + 16)
-
/*
* call-seq:
* dsa.syssign(string) -> aString
*
- * Computes and returns the DSA signature of +string+, where +string+ is
+ * Computes and returns the DSA signature of _string_, where _string_ is
* expected to be an already-computed message digest of the original input
* data. The signature is issued using the private key of this DSA instance.
*
* === Parameters
- * * +string+ is a message digest of the original input data to be signed
+ * * _string_ is a message digest of the original input data to be signed.
*
* === Example
* dsa = OpenSSL::PKey::DSA.new(2048)
@@ -535,7 +533,7 @@ ossl_dsa_sign(VALUE self, VALUE data)
if (!DSA_PRIVATE(self, dsa))
ossl_raise(eDSAError, "Private DSA key needed!");
StringValue(data);
- str = rb_str_new(0, ossl_dsa_buf_size(dsa));
+ str = rb_str_new(0, DSA_size(dsa));
if (!DSA_sign(0, (unsigned char *)RSTRING_PTR(data), RSTRING_LENINT(data),
(unsigned char *)RSTRING_PTR(str),
&buf_len, dsa)) { /* type is ignored (0) */
@@ -551,11 +549,11 @@ ossl_dsa_sign(VALUE self, VALUE data)
* dsa.sysverify(digest, sig) -> true | false
*
* Verifies whether the signature is valid given the message digest input. It
- * does so by validating +sig+ using the public key of this DSA instance.
+ * does so by validating _sig_ using the public key of this DSA instance.
*
* === Parameters
- * * +digest+ is a message digest of the original input data to be signed
- * * +sig+ is a DSA signature value
+ * * _digest_ is a message digest of the original input data to be signed
+ * * _sig_ is a DSA signature value
*
* === Example
* dsa = OpenSSL::PKey::DSA.new(2048)
@@ -592,7 +590,7 @@ ossl_dsa_verify(VALUE self, VALUE digest, VALUE sig)
* call-seq:
* dsa.set_pqg(p, q, g) -> self
*
- * Sets +p+, +q+, +g+ for the DSA instance.
+ * Sets _p_, _q_, _g_ to the DSA instance.
*/
OSSL_PKEY_BN_DEF3(dsa, DSA, pqg, p, q, g)
/*
@@ -600,7 +598,7 @@ OSSL_PKEY_BN_DEF3(dsa, DSA, pqg, p, q, g)
* call-seq:
* dsa.set_key(pub_key, priv_key) -> self
*
- * Sets +pub_key+ and +priv_key+ for the DSA instance. +priv_key+ may be nil.
+ * Sets _pub_key_ and _priv_key_ for the DSA instance. _priv_key_ may be +nil+.
*/
OSSL_PKEY_BN_DEF2(dsa, DSA, key, pub_key, priv_key)
@@ -629,18 +627,12 @@ Init_ossl_dsa(void)
* DSA, the Digital Signature Algorithm, is specified in NIST's
* FIPS 186-3. It is an asymmetric public key algorithm that may be used
* similar to e.g. RSA.
- * Please note that for OpenSSL versions prior to 1.0.0 the digest
- * algorithms OpenSSL::Digest::DSS (equivalent to SHA) or
- * OpenSSL::Digest::DSS1 (equivalent to SHA-1) must be used for issuing
- * signatures with a DSA key using OpenSSL::PKey#sign.
- * Starting with OpenSSL 1.0.0, digest algorithms are no longer restricted,
- * any Digest may be used for signing.
*/
cDSA = rb_define_class_under(mPKey, "DSA", cPKey);
rb_define_singleton_method(cDSA, "generate", ossl_dsa_s_generate, 1);
rb_define_method(cDSA, "initialize", ossl_dsa_initialize, -1);
- rb_define_copy_func(cDSA, ossl_dsa_initialize_copy);
+ rb_define_method(cDSA, "initialize_copy", ossl_dsa_initialize_copy, 1);
rb_define_method(cDSA, "public?", ossl_dsa_is_public, 0);
rb_define_method(cDSA, "private?", ossl_dsa_is_private, 0);
diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c
index 20a7222384..8bb611248b 100644
--- a/ext/openssl/ossl_pkey_ec.c
+++ b/ext/openssl/ossl_pkey_ec.c
@@ -4,7 +4,7 @@
#include "ossl.h"
-#if !defined(OPENSSL_NO_EC) && (OPENSSL_VERSION_NUMBER >= 0x0090802fL)
+#if !defined(OPENSSL_NO_EC)
#define EXPORT_PEM 0
#define EXPORT_DER 1
@@ -23,33 +23,21 @@ static const rb_data_type_t ossl_ec_point_type;
GetPKeyEC(obj, _pkey); \
(key) = EVP_PKEY_get0_EC_KEY(_pkey); \
} while (0)
-#define SafeGetEC(obj, key) do { \
- OSSL_Check_Kind(obj, cEC); \
- GetEC(obj, key); \
-} while (0)
#define GetECGroup(obj, group) do { \
TypedData_Get_Struct(obj, EC_GROUP, &ossl_ec_group_type, group); \
if ((group) == NULL) \
ossl_raise(eEC_GROUP, "EC_GROUP is not initialized"); \
} while (0)
-#define SafeGetECGroup(obj, group) do { \
- OSSL_Check_Kind((obj), cEC_GROUP); \
- GetECGroup(obj, group); \
-} while (0)
#define GetECPoint(obj, point) do { \
TypedData_Get_Struct(obj, EC_POINT, &ossl_ec_point_type, point); \
if ((point) == NULL) \
ossl_raise(eEC_POINT, "EC_POINT is not initialized"); \
} while (0)
-#define SafeGetECPoint(obj, point) do { \
- OSSL_Check_Kind((obj), cEC_POINT); \
- GetECPoint(obj, point); \
-} while(0)
#define GetECPointGroup(obj, group) do { \
VALUE _group = rb_attr_get(obj, id_i_group); \
- SafeGetECGroup(_group, group); \
+ GetECGroup(_group, group); \
} while (0)
VALUE cEC;
@@ -128,7 +116,7 @@ ec_key_new_from_group(VALUE arg)
if (rb_obj_is_kind_of(arg, cEC_GROUP)) {
EC_GROUP *group;
- SafeGetECGroup(arg, group);
+ GetECGroup(arg, group);
if (!(ec = EC_KEY_new()))
ossl_raise(eECError, NULL);
@@ -208,7 +196,7 @@ static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self)
} else if (rb_obj_is_kind_of(arg, cEC)) {
EC_KEY *other_ec = NULL;
- SafeGetEC(arg, other_ec);
+ GetEC(arg, other_ec);
if (!(ec = EC_KEY_dup(other_ec)))
ossl_raise(eECError, NULL);
} else if (rb_obj_is_kind_of(arg, cEC_GROUP)) {
@@ -217,7 +205,7 @@ static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self)
BIO *in;
pass = ossl_pem_passwd_value(pass);
- in = ossl_obj2bio(arg);
+ in = ossl_obj2bio(&arg);
ec = PEM_read_bio_ECPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass);
if (!ec) {
@@ -257,7 +245,7 @@ ossl_ec_key_initialize_copy(VALUE self, VALUE other)
GetPKey(self, pkey);
if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE)
ossl_raise(eECError, "EC already initialized");
- SafeGetEC(other, ec);
+ GetEC(other, ec);
ec_new = EC_KEY_dup(ec);
if (!ec_new)
@@ -275,7 +263,7 @@ ossl_ec_key_initialize_copy(VALUE self, VALUE other)
* key.group => group
*
* Returns the EC::Group that the key is associated with. Modifying the returned
- * group does not affect +key+.
+ * group does not affect _key_.
*/
static VALUE
ossl_ec_key_get_group(VALUE self)
@@ -296,7 +284,7 @@ ossl_ec_key_get_group(VALUE self)
* key.group = group
*
* Sets the EC::Group for the key. The group structure is internally copied so
- * modifition to +group+ after assigning to a key has no effect on the key.
+ * modification to _group_ after assigning to a key has no effect on the key.
*/
static VALUE
ossl_ec_key_set_group(VALUE self, VALUE group_v)
@@ -305,7 +293,7 @@ ossl_ec_key_set_group(VALUE self, VALUE group_v)
EC_GROUP *group;
GetEC(self, ec);
- SafeGetECGroup(group_v, group);
+ GetECGroup(group_v, group);
if (EC_KEY_set_group(ec, group) != 1)
ossl_raise(eECError, "EC_KEY_set_group");
@@ -390,7 +378,7 @@ static VALUE ossl_ec_key_set_public_key(VALUE self, VALUE public_key)
GetEC(self, ec);
if (!NIL_P(public_key))
- SafeGetECPoint(public_key, point);
+ GetECPoint(public_key, point);
switch (EC_KEY_set_public_key(ec, point)) {
case 1:
@@ -458,7 +446,7 @@ static VALUE ossl_ec_key_to_string(VALUE self, VALUE ciph, VALUE pass, int forma
private = 1;
if (!NIL_P(ciph)) {
- cipher = GetCipherPtr(ciph);
+ cipher = ossl_evp_get_cipherbyname(ciph);
pass = ossl_pem_passwd_value(pass);
}
@@ -502,8 +490,8 @@ static VALUE ossl_ec_key_to_string(VALUE self, VALUE ciph, VALUE pass, int forma
* key.export([cipher, pass_phrase]) => String
* key.to_pem([cipher, pass_phrase]) => String
*
- * Outputs the EC key in PEM encoding. If +cipher+ and +pass_phrase+ are given
- * they will be used to encrypt the key. +cipher+ must be an OpenSSL::Cipher
+ * Outputs the EC key in PEM encoding. If _cipher_ and _pass_phrase_ are given
+ * they will be used to encrypt the key. _cipher_ must be an OpenSSL::Cipher
* instance. Note that encryption will only be effective for a private key,
* public keys will always be encoded in plain text.
*/
@@ -608,7 +596,7 @@ static VALUE ossl_ec_key_dh_compute_key(VALUE self, VALUE pubkey)
VALUE str;
GetEC(self, ec);
- SafeGetECPoint(pubkey, point);
+ GetECPoint(pubkey, point);
/* BUG: need a way to figure out the maximum string size */
buf_len = 1024;
@@ -643,11 +631,10 @@ static VALUE ossl_ec_key_dsa_sign_asn1(VALUE self, VALUE data)
if (EC_KEY_get0_private_key(ec) == NULL)
ossl_raise(eECError, "Private EC key needed!");
- str = rb_str_new(0, ECDSA_size(ec) + 16);
+ str = rb_str_new(0, ECDSA_size(ec));
if (ECDSA_sign(0, (unsigned char *) RSTRING_PTR(data), RSTRING_LENINT(data), (unsigned char *) RSTRING_PTR(str), &buf_len, ec) != 1)
- ossl_raise(eECError, "ECDSA_sign");
-
- rb_str_resize(str, buf_len);
+ ossl_raise(eECError, "ECDSA_sign");
+ rb_str_set_len(str, buf_len);
return str;
}
@@ -725,7 +712,7 @@ ec_group_new(const EC_GROUP *group)
*
* Creates a new EC::Group object.
*
- * +ec_method+ is a symbol that represents an EC_METHOD. Currently the following
+ * _ec_method_ is a symbol that represents an EC_METHOD. Currently the following
* are supported:
*
* * :GFp_simple
@@ -772,11 +759,11 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self)
} else if (rb_obj_is_kind_of(arg1, cEC_GROUP)) {
const EC_GROUP *arg1_group;
- SafeGetECGroup(arg1, arg1_group);
+ GetECGroup(arg1, arg1_group);
if ((group = EC_GROUP_dup(arg1_group)) == NULL)
ossl_raise(eEC_GROUP, "EC_GROUP_dup");
} else {
- BIO *in = ossl_obj2bio(arg1);
+ BIO *in = ossl_obj2bio(&arg1);
group = PEM_read_bio_ECPKParameters(in, NULL, NULL, NULL);
if (!group) {
@@ -848,7 +835,7 @@ ossl_ec_group_initialize_copy(VALUE self, VALUE other)
TypedData_Get_Struct(self, EC_GROUP, &ossl_ec_group_type, group_new);
if (group_new)
ossl_raise(eEC_GROUP, "EC::Group already initialized");
- SafeGetECGroup(other, group);
+ GetECGroup(other, group);
group_new = EC_GROUP_dup(group);
if (!group_new)
@@ -863,15 +850,15 @@ ossl_ec_group_initialize_copy(VALUE self, VALUE other)
* group1.eql?(group2) => true | false
* group1 == group2 => true | false
*
- * Returns true if the two groups use the same curve and have the same
- * parameters, false otherwise.
+ * Returns +true+ if the two groups use the same curve and have the same
+ * parameters, +false+ otherwise.
*/
static VALUE ossl_ec_group_eql(VALUE a, VALUE b)
{
EC_GROUP *group1 = NULL, *group2 = NULL;
GetECGroup(a, group1);
- SafeGetECGroup(b, group2);
+ GetECGroup(b, group2);
if (EC_GROUP_cmp(group1, group2, ossl_bn_ctx) == 1)
return Qfalse;
@@ -904,8 +891,8 @@ static VALUE ossl_ec_group_get_generator(VALUE self)
* call-seq:
* group.set_generator(generator, order, cofactor) => self
*
- * Sets the curve parameters. +generator+ must be an instance of EC::Point that
- * is on the curve. +order+ and +cofactor+ are integers.
+ * Sets the curve parameters. _generator_ must be an instance of EC::Point that
+ * is on the curve. _order_ and _cofactor_ are integers.
*
* See the OpenSSL documentation for EC_GROUP_set_generator()
*/
@@ -916,7 +903,7 @@ static VALUE ossl_ec_group_set_generator(VALUE self, VALUE generator, VALUE orde
const BIGNUM *o, *co;
GetECGroup(self, group);
- SafeGetECPoint(generator, point);
+ GetECPoint(generator, point);
o = GetBNPtr(order);
co = GetBNPtr(cofactor);
@@ -1106,42 +1093,49 @@ static VALUE ossl_ec_group_get_point_conversion_form(VALUE self)
return ID2SYM(ret);
}
+static point_conversion_form_t
+parse_point_conversion_form_symbol(VALUE sym)
+{
+ ID id = SYM2ID(sym);
+
+ if (id == ID_uncompressed)
+ return POINT_CONVERSION_UNCOMPRESSED;
+ else if (id == ID_compressed)
+ return POINT_CONVERSION_COMPRESSED;
+ else if (id == ID_hybrid)
+ return POINT_CONVERSION_HYBRID;
+ else
+ ossl_raise(rb_eArgError, "unsupported point conversion form %+"PRIsVALUE
+ " (expected :compressed, :uncompressed, or :hybrid)", sym);
+}
+
/*
* call-seq:
* group.point_conversion_form = form
*
* Sets the form how EC::Point data is encoded as ASN.1 as defined in X9.62.
*
- * +format+ can be one of these:
+ * _format_ can be one of these:
*
- * :compressed::
+ * +:compressed+::
* Encoded as z||x, where z is an octet indicating which solution of the
* equation y is. z will be 0x02 or 0x03.
- * :uncompressed::
+ * +:uncompressed+::
* Encoded as z||x||y, where z is an octet 0x04.
- * :hybrid::
+ * +:hybrid+::
* Encodes as z||x||y, where z is an octet indicating which solution of the
* equation y is. z will be 0x06 or 0x07.
*
* See the OpenSSL documentation for EC_GROUP_set_point_conversion_form()
*/
-static VALUE ossl_ec_group_set_point_conversion_form(VALUE self, VALUE form_v)
+static VALUE
+ossl_ec_group_set_point_conversion_form(VALUE self, VALUE form_v)
{
- EC_GROUP *group = NULL;
+ EC_GROUP *group;
point_conversion_form_t form;
- ID form_id = SYM2ID(form_v);
GetECGroup(self, group);
-
- if (form_id == ID_uncompressed) {
- form = POINT_CONVERSION_UNCOMPRESSED;
- } else if (form_id == ID_compressed) {
- form = POINT_CONVERSION_COMPRESSED;
- } else if (form_id == ID_hybrid) {
- form = POINT_CONVERSION_HYBRID;
- } else {
- ossl_raise(rb_eArgError, "form must be :compressed, :uncompressed, or :hybrid");
- }
+ form = parse_point_conversion_form_symbol(form_v);
EC_GROUP_set_point_conversion_form(group, form);
@@ -1325,76 +1319,61 @@ ec_point_new(const EC_POINT *point, const EC_GROUP *group)
return obj;
}
+static VALUE ossl_ec_point_initialize_copy(VALUE, VALUE);
/*
* call-seq:
* OpenSSL::PKey::EC::Point.new(point)
- * OpenSSL::PKey::EC::Point.new(group)
- * OpenSSL::PKey::EC::Point.new(group, bn)
+ * OpenSSL::PKey::EC::Point.new(group [, encoded_point])
+ *
+ * Creates a new instance of OpenSSL::PKey::EC::Point. If the only argument is
+ * an instance of EC::Point, a copy is returned. Otherwise, creates a point
+ * that belongs to _group_.
*
- * See the OpenSSL documentation for EC_POINT_*
+ * _encoded_point_ is the octet string representation of the point. This
+ * must be either a String or an OpenSSL::BN.
*/
static VALUE ossl_ec_point_initialize(int argc, VALUE *argv, VALUE self)
{
EC_POINT *point;
- VALUE arg1, arg2;
- VALUE group_v = Qnil;
- const EC_GROUP *group = NULL;
+ VALUE group_v, arg2;
+ const EC_GROUP *group;
TypedData_Get_Struct(self, EC_POINT, &ossl_ec_point_type, point);
if (point)
- ossl_raise(eEC_POINT, "EC_POINT already initialized");
-
- switch (rb_scan_args(argc, argv, "11", &arg1, &arg2)) {
- case 1:
- if (rb_obj_is_kind_of(arg1, cEC_POINT)) {
- const EC_POINT *arg_point;
-
- group_v = rb_attr_get(arg1, id_i_group);
- SafeGetECGroup(group_v, group);
- SafeGetECPoint(arg1, arg_point);
-
- point = EC_POINT_dup(arg_point, group);
- } else if (rb_obj_is_kind_of(arg1, cEC_GROUP)) {
- group_v = arg1;
- SafeGetECGroup(group_v, group);
-
- point = EC_POINT_new(group);
- } else {
- ossl_raise(eEC_POINT, "wrong argument type: must be OpenSSL::PKey::EC::Point or OpenSSL::Pkey::EC::Group");
- }
-
- break;
- case 2:
- if (!rb_obj_is_kind_of(arg1, cEC_GROUP))
- ossl_raise(rb_eArgError, "1st argument must be OpenSSL::PKey::EC::Group");
- group_v = arg1;
- SafeGetECGroup(group_v, group);
-
- if (rb_obj_is_kind_of(arg2, cBN)) {
- const BIGNUM *bn = GetBNPtr(arg2);
-
- point = EC_POINT_bn2point(group, bn, NULL, ossl_bn_ctx);
- } else {
- BIO *in = ossl_obj2bio(arg1);
+ rb_raise(eEC_POINT, "EC_POINT already initialized");
-/* BUG: finish me */
-
- BIO_free(in);
-
- if (point == NULL) {
- ossl_raise(eEC_POINT, "unknown type for 2nd arg");
- }
- }
- break;
- default:
- ossl_raise(rb_eArgError, "wrong number of arguments");
+ rb_scan_args(argc, argv, "11", &group_v, &arg2);
+ if (rb_obj_is_kind_of(group_v, cEC_POINT)) {
+ if (argc != 1)
+ rb_raise(rb_eArgError, "invalid second argument");
+ return ossl_ec_point_initialize_copy(self, group_v);
}
- if (point == NULL)
- ossl_raise(eEC_POINT, NULL);
-
- if (NIL_P(group_v))
- ossl_raise(rb_eRuntimeError, "missing group (internal error)");
+ GetECGroup(group_v, group);
+ if (argc == 1) {
+ point = EC_POINT_new(group);
+ if (!point)
+ ossl_raise(eEC_POINT, "EC_POINT_new");
+ }
+ else {
+ if (rb_obj_is_kind_of(arg2, cBN)) {
+ point = EC_POINT_bn2point(group, GetBNPtr(arg2), NULL, ossl_bn_ctx);
+ if (!point)
+ ossl_raise(eEC_POINT, "EC_POINT_bn2point");
+ }
+ else {
+ StringValue(arg2);
+ point = EC_POINT_new(group);
+ if (!point)
+ ossl_raise(eEC_POINT, "EC_POINT_new");
+ if (!EC_POINT_oct2point(group, point,
+ (unsigned char *)RSTRING_PTR(arg2),
+ RSTRING_LEN(arg2), ossl_bn_ctx)) {
+ EC_POINT_free(point);
+ ossl_raise(eEC_POINT, "EC_POINT_oct2point");
+ }
+ }
+ }
RTYPEDDATA_DATA(self) = point;
rb_ivar_set(self, id_i_group, group_v);
@@ -1412,10 +1391,10 @@ ossl_ec_point_initialize_copy(VALUE self, VALUE other)
TypedData_Get_Struct(self, EC_POINT, &ossl_ec_point_type, point_new);
if (point_new)
ossl_raise(eEC_POINT, "EC::Point already initialized");
- SafeGetECPoint(other, point);
+ GetECPoint(other, point);
group_v = rb_obj_dup(rb_attr_get(other, id_i_group));
- SafeGetECGroup(group_v, group);
+ GetECGroup(group_v, group);
point_new = EC_POINT_dup(point, group);
if (!point_new)
@@ -1442,8 +1421,8 @@ static VALUE ossl_ec_point_eql(VALUE a, VALUE b)
return Qfalse;
GetECPoint(a, point1);
- SafeGetECPoint(b, point2);
- SafeGetECGroup(group_v1, group);
+ GetECPoint(b, point2);
+ GetECGroup(group_v1, group);
if (EC_POINT_cmp(group, point1, point2, ossl_bn_ctx) == 1)
return Qfalse;
@@ -1549,30 +1528,38 @@ static VALUE ossl_ec_point_set_to_infinity(VALUE self)
/*
* call-seq:
- * point.to_bn => OpenSSL::BN
+ * point.to_octet_string(conversion_form) -> String
+ *
+ * Returns the octet string representation of the elliptic curve point.
*
- * See the OpenSSL documentation for EC_POINT_point2bn()
+ * _conversion_form_ specifies how the point is converted. Possible values are:
+ *
+ * - +:compressed+
+ * - +:uncompressed+
+ * - +:hybrid+
*/
-static VALUE ossl_ec_point_to_bn(VALUE self)
+static VALUE
+ossl_ec_point_to_octet_string(VALUE self, VALUE conversion_form)
{
EC_POINT *point;
- VALUE bn_obj;
const EC_GROUP *group;
point_conversion_form_t form;
- BIGNUM *bn;
+ VALUE str;
+ size_t len;
GetECPoint(self, point);
GetECPointGroup(self, group);
-
- form = EC_GROUP_get_point_conversion_form(group);
-
- bn_obj = rb_obj_alloc(cBN);
- bn = GetBNPtr(bn_obj);
-
- if (EC_POINT_point2bn(group, point, form, bn, ossl_bn_ctx) == NULL)
- ossl_raise(eEC_POINT, "EC_POINT_point2bn");
-
- return bn_obj;
+ form = parse_point_conversion_form_symbol(conversion_form);
+
+ len = EC_POINT_point2oct(group, point, form, NULL, 0, ossl_bn_ctx);
+ if (!len)
+ ossl_raise(eEC_POINT, "EC_POINT_point2oct");
+ str = rb_str_new(NULL, (long)len);
+ if (!EC_POINT_point2oct(group, point, form,
+ (unsigned char *)RSTRING_PTR(str), len,
+ ossl_bn_ctx))
+ ossl_raise(eEC_POINT, "EC_POINT_point2oct");
+ return str;
}
/*
@@ -1583,12 +1570,12 @@ static VALUE ossl_ec_point_to_bn(VALUE self)
* Performs elliptic curve point multiplication.
*
* The first form calculates <tt>bn1 * point + bn2 * G</tt>, where +G+ is the
- * generator of the group of +point+. +bn2+ may be ommitted, and in that case,
+ * generator of the group of _point_. _bn2_ may be omitted, and in that case,
* the result is just <tt>bn1 * point</tt>.
*
* The second form calculates <tt>bns[0] * point + bns[1] * points[0] + ...
- * + bns[-1] * points[-1] + bn2 * G</tt>. +bn2+ may be ommitted. +bns+ must be
- * an array of OpenSSL::BN. +points+ must be an array of
+ * + bns[-1] * points[-1] + bn2 * G</tt>. _bn2_ may be omitted. _bns_ must be
+ * an array of OpenSSL::BN. _points_ must be an array of
* OpenSSL::PKey::EC::Point. Please note that <tt>points[0]</tt> is not
* multiplied by <tt>bns[0]</tt>, but <tt>bns[1]</tt>.
*/
@@ -1601,7 +1588,7 @@ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self)
const BIGNUM *bn_g = NULL;
GetECPoint(self, point_self);
- SafeGetECGroup(group_v, group);
+ GetECGroup(group_v, group);
result = rb_obj_alloc(cEC_POINT);
ossl_ec_point_initialize(1, &group_v, result);
@@ -1621,7 +1608,7 @@ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self)
* points | self | arg2[0] | arg2[1] | ...
*/
long i, num;
- VALUE tmp_p, tmp_b;
+ VALUE bns_tmp, tmp_p, tmp_b;
const EC_POINT **points;
const BIGNUM **bignums;
@@ -1631,14 +1618,18 @@ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self)
ossl_raise(rb_eArgError, "bns must be 1 longer than points; see the documentation");
num = RARRAY_LEN(arg1);
+ bns_tmp = rb_ary_tmp_new(num);
bignums = ALLOCV_N(const BIGNUM *, tmp_b, num);
- for (i = 0; i < num; i++)
- bignums[i] = GetBNPtr(RARRAY_AREF(arg1, i));
+ for (i = 0; i < num; i++) {
+ VALUE item = RARRAY_AREF(arg1, i);
+ bignums[i] = GetBNPtr(item);
+ rb_ary_push(bns_tmp, item);
+ }
points = ALLOCV_N(const EC_POINT *, tmp_p, num);
points[0] = point_self; /* self */
for (i = 0; i < num - 1; i++)
- SafeGetECPoint(RARRAY_AREF(arg2, i), points[i + 1]);
+ GetECPoint(RARRAY_AREF(arg2, i), points[i + 1]);
if (!NIL_P(arg3))
bn_g = GetBNPtr(arg3);
@@ -1658,6 +1649,7 @@ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self)
void Init_ossl_ec(void)
{
+#undef rb_intern
#if 0
mPKey = rb_define_module_under(mOSSL, "PKey");
cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject);
@@ -1708,7 +1700,7 @@ void Init_ossl_ec(void)
rb_define_singleton_method(cEC, "generate", ossl_ec_key_s_generate, 1);
rb_define_method(cEC, "initialize", ossl_ec_key_initialize, -1);
- rb_define_copy_func(cEC, ossl_ec_key_initialize_copy);
+ rb_define_method(cEC, "initialize_copy", ossl_ec_key_initialize_copy, 1);
/* copy/dup/cmp */
rb_define_method(cEC, "group", ossl_ec_key_get_group, 0);
@@ -1745,7 +1737,7 @@ void Init_ossl_ec(void)
rb_define_alloc_func(cEC_GROUP, ossl_ec_group_alloc);
rb_define_method(cEC_GROUP, "initialize", ossl_ec_group_initialize, -1);
- rb_define_copy_func(cEC_GROUP, ossl_ec_group_initialize_copy);
+ rb_define_method(cEC_GROUP, "initialize_copy", ossl_ec_group_initialize_copy, 1);
rb_define_method(cEC_GROUP, "eql?", ossl_ec_group_eql, 1);
rb_define_alias(cEC_GROUP, "==", "eql?");
/* copy/dup/cmp */
@@ -1781,7 +1773,7 @@ void Init_ossl_ec(void)
rb_define_alloc_func(cEC_POINT, ossl_ec_point_alloc);
rb_define_method(cEC_POINT, "initialize", ossl_ec_point_initialize, -1);
- rb_define_copy_func(cEC_POINT, ossl_ec_point_initialize_copy);
+ rb_define_method(cEC_POINT, "initialize_copy", ossl_ec_point_initialize_copy, 1);
rb_attr(cEC_POINT, rb_intern("group"), 1, 0, 0);
rb_define_method(cEC_POINT, "eql?", ossl_ec_point_eql, 1);
rb_define_alias(cEC_POINT, "==", "eql?");
@@ -1793,7 +1785,7 @@ void Init_ossl_ec(void)
rb_define_method(cEC_POINT, "set_to_infinity!", ossl_ec_point_set_to_infinity, 0);
/* all the other methods */
- rb_define_method(cEC_POINT, "to_bn", ossl_ec_point_to_bn, 0);
+ rb_define_method(cEC_POINT, "to_octet_string", ossl_ec_point_to_octet_string, 1);
rb_define_method(cEC_POINT, "mul", ossl_ec_point_mul, -1);
id_i_group = rb_intern("@group");
diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c
index 17a7494992..4800fb2710 100644
--- a/ext/openssl/ossl_pkey_rsa.c
+++ b/ext/openssl/ossl_pkey_rsa.c
@@ -172,8 +172,8 @@ rsa_generate(int size, unsigned long exp)
* RSA.generate(size) => RSA instance
* RSA.generate(size, exponent) => RSA instance
*
- * Generates an RSA keypair. +size+ is an integer representing the desired key
- * size. Keys smaller than 1024 should be considered insecure. +exponent+ is
+ * Generates an RSA keypair. _size_ is an integer representing the desired key
+ * size. Keys smaller than 1024 should be considered insecure. _exponent_ is
* an odd number normally 3, 17, or 65537.
*/
static VALUE
@@ -203,12 +203,12 @@ ossl_rsa_s_generate(int argc, VALUE *argv, VALUE klass)
* RSA.new(encoded_key) => RSA instance
* RSA.new(encoded_key, pass_phrase) => RSA instance
*
- * Generates or loads an RSA keypair. If an integer +key_size+ is given it
+ * Generates or loads an RSA keypair. If an integer _key_size_ is given it
* represents the desired key size. Keys less than 1024 bits should be
* considered insecure.
*
- * A key can instead be loaded from an +encoded_key+ which must be PEM or DER
- * encoded. A +pass_phrase+ can be used to decrypt the key. If none is given
+ * A key can instead be loaded from an _encoded_key_ which must be PEM or DER
+ * encoded. A _pass_phrase_ can be used to decrypt the key. If none is given
* OpenSSL will prompt for the pass phrase.
*
* = Examples
@@ -236,7 +236,7 @@ ossl_rsa_initialize(int argc, VALUE *argv, VALUE self)
else {
pass = ossl_pem_passwd_value(pass);
arg = ossl_to_der_if_possible(arg);
- in = ossl_obj2bio(arg);
+ in = ossl_obj2bio(&arg);
rsa = PEM_read_bio_RSAPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass);
if (!rsa) {
OSSL_BIO_reset(in);
@@ -295,7 +295,7 @@ ossl_rsa_initialize_copy(VALUE self, VALUE other)
* call-seq:
* rsa.public? => true
*
- * The return value is always true since every private key is also a public
+ * The return value is always +true+ since every private key is also a public
* key.
*/
static VALUE
@@ -333,8 +333,8 @@ ossl_rsa_is_private(VALUE self)
* rsa.to_pem([cipher, pass_phrase]) => PEM-format String
* rsa.to_s([cipher, pass_phrase]) => PEM-format String
*
- * Outputs this keypair in PEM encoding. If +cipher+ and +pass_phrase+ are
- * given they will be used to encrypt the key. +cipher+ must be an
+ * Outputs this keypair in PEM encoding. If _cipher_ and _pass_phrase_ are
+ * given they will be used to encrypt the key. _cipher_ must be an
* OpenSSL::Cipher instance.
*/
static VALUE
@@ -350,7 +350,7 @@ ossl_rsa_export(int argc, VALUE *argv, VALUE self)
rb_scan_args(argc, argv, "02", &cipher, &pass);
if (!NIL_P(cipher)) {
- ciph = GetCipherPtr(cipher);
+ ciph = ossl_evp_get_cipherbyname(cipher);
pass = ossl_pem_passwd_value(pass);
}
if (!(out = BIO_new(BIO_s_mem()))) {
@@ -404,14 +404,12 @@ ossl_rsa_to_der(VALUE self)
return str;
}
-#define ossl_rsa_buf_size(rsa) (RSA_size(rsa)+16)
-
/*
* call-seq:
* rsa.public_encrypt(string) => String
* rsa.public_encrypt(string, padding) => String
*
- * Encrypt +string+ with the public key. +padding+ defaults to PKCS1_PADDING.
+ * Encrypt _string_ with the public key. _padding_ defaults to PKCS1_PADDING.
* The encrypted string output can be decrypted using #private_decrypt.
*/
static VALUE
@@ -429,7 +427,7 @@ ossl_rsa_public_encrypt(int argc, VALUE *argv, VALUE self)
rb_scan_args(argc, argv, "11", &buffer, &padding);
pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
StringValue(buffer);
- str = rb_str_new(0, ossl_rsa_buf_size(rsa));
+ str = rb_str_new(0, RSA_size(rsa));
buf_len = RSA_public_encrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
(unsigned char *)RSTRING_PTR(str), rsa, pad);
if (buf_len < 0) ossl_raise(eRSAError, NULL);
@@ -443,8 +441,8 @@ ossl_rsa_public_encrypt(int argc, VALUE *argv, VALUE self)
* rsa.public_decrypt(string) => String
* 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.
+ * Decrypt _string_, which has been encrypted with the private key, with the
+ * public key. _padding_ defaults to PKCS1_PADDING.
*/
static VALUE
ossl_rsa_public_decrypt(int argc, VALUE *argv, VALUE self)
@@ -461,7 +459,7 @@ ossl_rsa_public_decrypt(int argc, VALUE *argv, VALUE self)
rb_scan_args(argc, argv, "11", &buffer, &padding);
pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
StringValue(buffer);
- str = rb_str_new(0, ossl_rsa_buf_size(rsa));
+ str = rb_str_new(0, RSA_size(rsa));
buf_len = RSA_public_decrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
(unsigned char *)RSTRING_PTR(str), rsa, pad);
if (buf_len < 0) ossl_raise(eRSAError, NULL);
@@ -475,7 +473,7 @@ ossl_rsa_public_decrypt(int argc, VALUE *argv, VALUE self)
* rsa.private_encrypt(string) => String
* rsa.private_encrypt(string, padding) => String
*
- * Encrypt +string+ with the private key. +padding+ defaults to PKCS1_PADDING.
+ * Encrypt _string_ with the private key. _padding_ defaults to PKCS1_PADDING.
* The encrypted string output can be decrypted using #public_decrypt.
*/
static VALUE
@@ -495,7 +493,7 @@ ossl_rsa_private_encrypt(int argc, VALUE *argv, VALUE self)
rb_scan_args(argc, argv, "11", &buffer, &padding);
pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
StringValue(buffer);
- str = rb_str_new(0, ossl_rsa_buf_size(rsa));
+ str = rb_str_new(0, RSA_size(rsa));
buf_len = RSA_private_encrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
(unsigned char *)RSTRING_PTR(str), rsa, pad);
if (buf_len < 0) ossl_raise(eRSAError, NULL);
@@ -509,8 +507,8 @@ ossl_rsa_private_encrypt(int argc, VALUE *argv, VALUE self)
* rsa.private_decrypt(string) => String
* 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.
+ * Decrypt _string_, which has been encrypted with the public key, with the
+ * private key. _padding_ defaults to PKCS1_PADDING.
*/
static VALUE
ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self)
@@ -529,7 +527,7 @@ ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self)
rb_scan_args(argc, argv, "11", &buffer, &padding);
pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
StringValue(buffer);
- str = rb_str_new(0, ossl_rsa_buf_size(rsa));
+ str = rb_str_new(0, RSA_size(rsa));
buf_len = RSA_private_decrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
(unsigned char *)RSTRING_PTR(str), rsa, pad);
if (buf_len < 0) ossl_raise(eRSAError, NULL);
@@ -540,6 +538,196 @@ ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
+ * rsa.sign_pss(digest, data, salt_length:, mgf1_hash:) -> String
+ *
+ * Signs _data_ using the Probabilistic Signature Scheme (RSA-PSS) and returns
+ * the calculated signature.
+ *
+ * RSAError will be raised if an error occurs.
+ *
+ * See #verify_pss for the verification operation.
+ *
+ * === Parameters
+ * _digest_::
+ * A String containing the message digest algorithm name.
+ * _data_::
+ * A String. The data to be signed.
+ * _salt_length_::
+ * The length in octets of the salt. Two special values are reserved:
+ * +:digest+ means the digest length, and +:max+ means the maximum possible
+ * length for the combination of the private key and the selected message
+ * digest algorithm.
+ * _mgf1_hash_::
+ * The hash algorithm used in MGF1 (the currently supported mask generation
+ * function (MGF)).
+ *
+ * === Example
+ * data = "Sign me!"
+ * pkey = OpenSSL::PKey::RSA.new(2048)
+ * signature = pkey.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA256")
+ * pub_key = pkey.public_key
+ * puts pub_key.verify_pss("SHA256", signature, data,
+ * salt_length: :auto, mgf1_hash: "SHA256") # => true
+ */
+static VALUE
+ossl_rsa_sign_pss(int argc, VALUE *argv, VALUE self)
+{
+ VALUE digest, data, options, kwargs[2], signature;
+ static ID kwargs_ids[2];
+ EVP_PKEY *pkey;
+ EVP_PKEY_CTX *pkey_ctx;
+ const EVP_MD *md, *mgf1md;
+ EVP_MD_CTX *md_ctx;
+ size_t buf_len;
+ int salt_len;
+
+ if (!kwargs_ids[0]) {
+ kwargs_ids[0] = rb_intern_const("salt_length");
+ kwargs_ids[1] = rb_intern_const("mgf1_hash");
+ }
+ rb_scan_args(argc, argv, "2:", &digest, &data, &options);
+ rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs);
+ if (kwargs[0] == ID2SYM(rb_intern("max")))
+ salt_len = -2; /* RSA_PSS_SALTLEN_MAX_SIGN */
+ else if (kwargs[0] == ID2SYM(rb_intern("digest")))
+ salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */
+ else
+ salt_len = NUM2INT(kwargs[0]);
+ mgf1md = ossl_evp_get_digestbyname(kwargs[1]);
+
+ pkey = GetPrivPKeyPtr(self);
+ buf_len = EVP_PKEY_size(pkey);
+ md = ossl_evp_get_digestbyname(digest);
+ StringValue(data);
+ signature = rb_str_new(NULL, (long)buf_len);
+
+ md_ctx = EVP_MD_CTX_new();
+ if (!md_ctx)
+ goto err;
+
+ if (EVP_DigestSignInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1)
+ goto err;
+
+ if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1)
+ goto err;
+
+ if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1)
+ goto err;
+
+ if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1)
+ goto err;
+
+ if (EVP_DigestSignUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1)
+ goto err;
+
+ if (EVP_DigestSignFinal(md_ctx, (unsigned char *)RSTRING_PTR(signature), &buf_len) != 1)
+ goto err;
+
+ rb_str_set_len(signature, (long)buf_len);
+
+ EVP_MD_CTX_free(md_ctx);
+ return signature;
+
+ err:
+ EVP_MD_CTX_free(md_ctx);
+ ossl_raise(eRSAError, NULL);
+}
+
+/*
+ * call-seq:
+ * rsa.verify_pss(digest, signature, data, salt_length:, mgf1_hash:) -> true | false
+ *
+ * Verifies _data_ using the Probabilistic Signature Scheme (RSA-PSS).
+ *
+ * The return value is +true+ if the signature is valid, +false+ otherwise.
+ * RSAError will be raised if an error occurs.
+ *
+ * See #sign_pss for the signing operation and an example code.
+ *
+ * === Parameters
+ * _digest_::
+ * A String containing the message digest algorithm name.
+ * _data_::
+ * A String. The data to be signed.
+ * _salt_length_::
+ * The length in octets of the salt. Two special values are reserved:
+ * +:digest+ means the digest length, and +:auto+ means automatically
+ * determining the length based on the signature.
+ * _mgf1_hash_::
+ * The hash algorithm used in MGF1.
+ */
+static VALUE
+ossl_rsa_verify_pss(int argc, VALUE *argv, VALUE self)
+{
+ VALUE digest, signature, data, options, kwargs[2];
+ static ID kwargs_ids[2];
+ EVP_PKEY *pkey;
+ EVP_PKEY_CTX *pkey_ctx;
+ const EVP_MD *md, *mgf1md;
+ EVP_MD_CTX *md_ctx;
+ int result, salt_len;
+
+ if (!kwargs_ids[0]) {
+ kwargs_ids[0] = rb_intern_const("salt_length");
+ kwargs_ids[1] = rb_intern_const("mgf1_hash");
+ }
+ rb_scan_args(argc, argv, "3:", &digest, &signature, &data, &options);
+ rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs);
+ if (kwargs[0] == ID2SYM(rb_intern("auto")))
+ salt_len = -2; /* RSA_PSS_SALTLEN_AUTO */
+ else if (kwargs[0] == ID2SYM(rb_intern("digest")))
+ salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */
+ else
+ salt_len = NUM2INT(kwargs[0]);
+ mgf1md = ossl_evp_get_digestbyname(kwargs[1]);
+
+ GetPKey(self, pkey);
+ md = ossl_evp_get_digestbyname(digest);
+ StringValue(signature);
+ StringValue(data);
+
+ md_ctx = EVP_MD_CTX_new();
+ if (!md_ctx)
+ goto err;
+
+ if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1)
+ goto err;
+
+ if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1)
+ goto err;
+
+ if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1)
+ goto err;
+
+ if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1)
+ goto err;
+
+ if (EVP_DigestVerifyUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1)
+ goto err;
+
+ result = EVP_DigestVerifyFinal(md_ctx,
+ (unsigned char *)RSTRING_PTR(signature),
+ RSTRING_LEN(signature));
+
+ switch (result) {
+ case 0:
+ ossl_clear_error();
+ EVP_MD_CTX_free(md_ctx);
+ return Qfalse;
+ case 1:
+ EVP_MD_CTX_free(md_ctx);
+ return Qtrue;
+ default:
+ goto err;
+ }
+
+ err:
+ EVP_MD_CTX_free(md_ctx);
+ ossl_raise(eRSAError, NULL);
+}
+
+/*
+ * call-seq:
* rsa.params => hash
*
* THIS METHOD IS INSECURE, PRIVATE INFORMATION CAN LEAK OUT!!!
@@ -620,7 +808,7 @@ ossl_rsa_to_public_key(VALUE self)
GetPKeyRSA(self, pkey);
/* err check performed by rsa_instance */
rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(pkey));
- obj = rsa_instance(CLASS_OF(self), rsa);
+ obj = rsa_instance(rb_obj_class(self), rsa);
if (obj == Qfalse) {
RSA_free(rsa);
ossl_raise(eRSAError, NULL);
@@ -661,7 +849,7 @@ ossl_rsa_blinding_off(VALUE self)
* call-seq:
* rsa.set_key(n, e, d) -> self
*
- * Sets +n+, +e+, +d+ for the RSA instance.
+ * Sets _n_, _e_, _d_ for the RSA instance.
*/
OSSL_PKEY_BN_DEF3(rsa, RSA, key, n, e, d)
/*
@@ -669,7 +857,7 @@ OSSL_PKEY_BN_DEF3(rsa, RSA, key, n, e, d)
* call-seq:
* rsa.set_factors(p, q) -> self
*
- * Sets +p+, +q+ for the RSA instance.
+ * Sets _p_, _q_ for the RSA instance.
*/
OSSL_PKEY_BN_DEF2(rsa, RSA, factors, p, q)
/*
@@ -677,7 +865,7 @@ OSSL_PKEY_BN_DEF2(rsa, RSA, factors, p, q)
* call-seq:
* rsa.set_crt_params(dmp1, dmq1, iqmp) -> self
*
- * Sets +dmp1+, +dmq1+, +iqmp+ for the RSA instance. They are calculated by
+ * Sets _dmp1_, _dmq1_, _iqmp_ for the RSA instance. They are calculated by
* <tt>d mod (p - 1)</tt>, <tt>d mod (q - 1)</tt> and <tt>q^(-1) mod p</tt>
* respectively.
*/
@@ -708,7 +896,7 @@ Init_ossl_rsa(void)
/* Document-class: OpenSSL::PKey::RSA
*
* RSA is an asymmetric public key algorithm that has been formalized in
- * RFC 3447. It is in widespread use in public key infrastuctures (PKI)
+ * RFC 3447. It is in widespread use in public key infrastructures (PKI)
* where certificates (cf. OpenSSL::X509::Certificate) often are issued
* on the basis of a public/private RSA key pair. RSA is used in a wide
* field of applications such as secure (symmetric) key exchange, e.g.
@@ -719,7 +907,7 @@ Init_ossl_rsa(void)
rb_define_singleton_method(cRSA, "generate", ossl_rsa_s_generate, -1);
rb_define_method(cRSA, "initialize", ossl_rsa_initialize, -1);
- rb_define_copy_func(cRSA, ossl_rsa_initialize_copy);
+ rb_define_method(cRSA, "initialize_copy", ossl_rsa_initialize_copy, 1);
rb_define_method(cRSA, "public?", ossl_rsa_is_public, 0);
rb_define_method(cRSA, "private?", ossl_rsa_is_private, 0);
@@ -733,6 +921,8 @@ Init_ossl_rsa(void)
rb_define_method(cRSA, "public_decrypt", ossl_rsa_public_decrypt, -1);
rb_define_method(cRSA, "private_encrypt", ossl_rsa_private_encrypt, -1);
rb_define_method(cRSA, "private_decrypt", ossl_rsa_private_decrypt, -1);
+ rb_define_method(cRSA, "sign_pss", ossl_rsa_sign_pss, -1);
+ rb_define_method(cRSA, "verify_pss", ossl_rsa_verify_pss, -1);
DEF_OSSL_PKEY_BN(cRSA, rsa, n);
DEF_OSSL_PKEY_BN(cRSA, rsa, e);
diff --git a/ext/openssl/ossl_rand.c b/ext/openssl/ossl_rand.c
index 688c525afa..c95857060a 100644
--- a/ext/openssl/ossl_rand.c
+++ b/ext/openssl/ossl_rand.c
@@ -16,7 +16,7 @@ VALUE eRandomError;
* call-seq:
* seed(str) -> str
*
- * ::seed is equivalent to ::add where +entropy+ is length of +str+.
+ * ::seed is equivalent to ::add where _entropy_ is length of _str_.
*/
static VALUE
ossl_rand_seed(VALUE self, VALUE str)
@@ -31,15 +31,15 @@ ossl_rand_seed(VALUE self, VALUE str)
* call-seq:
* add(str, entropy) -> self
*
- * Mixes the bytes from +str+ into the Pseudo Random Number Generator(PRNG)
+ * Mixes the bytes from _str_ into the Pseudo Random Number Generator(PRNG)
* state.
*
- * Thus, if the data from +str+ are unpredictable to an adversary, this
+ * Thus, if the data from _str_ are unpredictable to an adversary, this
* increases the uncertainty about the state and makes the PRNG output less
* predictable.
*
- * The +entropy+ argument is (the lower bound of) an estimate of how much
- * randomness is contained in +str+, measured in bytes.
+ * The _entropy_ argument is (the lower bound of) an estimate of how much
+ * randomness is contained in _str_, measured in bytes.
*
* === Example
*
@@ -62,7 +62,7 @@ ossl_rand_add(VALUE self, VALUE str, VALUE entropy)
* call-seq:
* load_random_file(filename) -> true
*
- * Reads bytes from +filename+ and adds them to the PRNG.
+ * Reads bytes from _filename_ and adds them to the PRNG.
*/
static VALUE
ossl_rand_load_file(VALUE self, VALUE filename)
@@ -79,7 +79,7 @@ ossl_rand_load_file(VALUE self, VALUE filename)
* call-seq:
* write_random_file(filename) -> true
*
- * Writes a number of random generated bytes (currently 1024) to +filename+
+ * Writes a number of random generated bytes (currently 1024) to _filename_
* which can be used to initialize the PRNG by calling ::load_random_file in a
* later session.
*/
@@ -98,7 +98,7 @@ ossl_rand_write_file(VALUE self, VALUE filename)
* call-seq:
* random_bytes(length) -> string
*
- * Generates +string+ with +length+ number of cryptographically strong
+ * Generates a String with _length_ number of cryptographically strong
* pseudo-random bytes.
*
* === Example
@@ -129,7 +129,7 @@ ossl_rand_bytes(VALUE self, VALUE len)
* call-seq:
* pseudo_bytes(length) -> string
*
- * Generates +string+ with +length+ number of pseudo-random bytes.
+ * Generates a String with _length_ number of pseudo-random bytes.
*
* Pseudo-random byte sequences generated by ::pseudo_bytes will be unique if
* they are of sufficient length, but are not necessarily unpredictable.
@@ -176,9 +176,9 @@ ossl_rand_egd(VALUE self, VALUE filename)
* call-seq:
* egd_bytes(filename, length) -> true
*
- * Queries the entropy gathering daemon EGD on socket path given by +filename+.
+ * Queries the entropy gathering daemon EGD on socket path given by _filename_.
*
- * Fetches +length+ number of bytes and uses ::add to seed the OpenSSL built-in
+ * Fetches _length_ number of bytes and uses ::add to seed the OpenSSL built-in
* PRNG.
*/
static VALUE
@@ -199,7 +199,7 @@ ossl_rand_egd_bytes(VALUE self, VALUE filename, VALUE len)
* call-seq:
* status? => true | false
*
- * Return true if the PRNG has been seeded with enough data, false otherwise.
+ * Return +true+ if the PRNG has been seeded with enough data, +false+ otherwise.
*/
static VALUE
ossl_rand_status(VALUE self)
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index 861f820dbd..7996f227b6 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -11,10 +11,6 @@
*/
#include "ossl.h"
-#if defined(HAVE_UNISTD_H)
-# include <unistd.h> /* for read(), and write() */
-#endif
-
#define numberof(ary) (int)(sizeof(ary)/sizeof((ary)[0]))
#ifdef _WIN32
@@ -36,7 +32,8 @@ VALUE cSSLSocket;
static VALUE eSSLErrorWaitReadable;
static VALUE eSSLErrorWaitWritable;
-static ID ID_callback_state;
+static ID id_call, ID_callback_state, id_tmp_dh_callback, id_tmp_ecdh_callback,
+ id_npn_protocols_encoded;
static VALUE sym_exception, sym_wait_readable, sym_wait_writable;
static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode,
@@ -49,52 +46,19 @@ static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode,
id_i_verify_hostname;
static ID id_i_io, id_i_context, id_i_hostname;
-/*
- * SSLContext class
- */
-static const struct {
- const char *name;
- SSL_METHOD *(*func)(void); /* FIXME: constify when dropping 0.9.8 */
- int version;
-} ossl_ssl_method_tab[] = {
-#if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION)
-#define OSSL_SSL_METHOD_ENTRY(name, version) \
- { #name, (SSL_METHOD *(*)(void))TLS_method, version }, \
- { #name"_server", (SSL_METHOD *(*)(void))TLS_server_method, version }, \
- { #name"_client", (SSL_METHOD *(*)(void))TLS_client_method, version }
-#else
-#define OSSL_SSL_METHOD_ENTRY(name, version) \
- { #name, (SSL_METHOD *(*)(void))name##_method, version }, \
- { #name"_server", (SSL_METHOD *(*)(void))name##_server_method, version }, \
- { #name"_client", (SSL_METHOD *(*)(void))name##_client_method, version }
-#endif
-#if defined(HAVE_SSLV2_METHOD)
- OSSL_SSL_METHOD_ENTRY(SSLv2, SSL2_VERSION),
-#endif
-#if defined(HAVE_SSLV3_METHOD)
- OSSL_SSL_METHOD_ENTRY(SSLv3, SSL3_VERSION),
-#endif
- OSSL_SSL_METHOD_ENTRY(TLSv1, TLS1_VERSION),
-#if defined(HAVE_TLSV1_1_METHOD)
- OSSL_SSL_METHOD_ENTRY(TLSv1_1, TLS1_1_VERSION),
-#endif
-#if defined(HAVE_TLSV1_2_METHOD)
- OSSL_SSL_METHOD_ENTRY(TLSv1_2, TLS1_2_VERSION),
-#endif
- OSSL_SSL_METHOD_ENTRY(SSLv23, 0),
-#undef OSSL_SSL_METHOD_ENTRY
-};
-
static int ossl_ssl_ex_vcb_idx;
-static int ossl_ssl_ex_store_p;
static int ossl_ssl_ex_ptr_idx;
+static int ossl_sslctx_ex_ptr_idx;
+#if !defined(HAVE_X509_STORE_UP_REF)
+static int ossl_sslctx_ex_store_p;
+#endif
static void
ossl_sslctx_free(void *ptr)
{
SSL_CTX *ctx = ptr;
#if !defined(HAVE_X509_STORE_UP_REF)
- if(ctx && SSL_CTX_get_ex_data(ctx, ossl_ssl_ex_store_p)== (void*)1)
+ if (ctx && SSL_CTX_get_ex_data(ctx, ossl_sslctx_ex_store_p))
ctx->cert_store = NULL;
#endif
SSL_CTX_free(ctx);
@@ -112,22 +76,24 @@ static VALUE
ossl_sslctx_s_alloc(VALUE klass)
{
SSL_CTX *ctx;
- long mode = SSL_MODE_ENABLE_PARTIAL_WRITE |
- SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER;
+ long mode = 0 |
+ SSL_MODE_ENABLE_PARTIAL_WRITE |
+ SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |
+ SSL_MODE_RELEASE_BUFFERS;
VALUE obj;
-#ifdef SSL_MODE_RELEASE_BUFFERS
- mode |= SSL_MODE_RELEASE_BUFFERS;
-#endif
-
obj = TypedData_Wrap_Struct(klass, &ossl_sslctx_type, 0);
+#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+ ctx = SSL_CTX_new(TLS_method());
+#else
ctx = SSL_CTX_new(SSLv23_method());
+#endif
if (!ctx) {
ossl_raise(eSSLError, "SSL_CTX_new");
}
SSL_CTX_set_mode(ctx, mode);
RTYPEDDATA_DATA(obj) = ctx;
- SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_ptr_idx, (void*)obj);
+ SSL_CTX_set_ex_data(ctx, ossl_sslctx_ex_ptr_idx, (void *)obj);
#if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_ECDH_AUTO)
/* We use SSL_CTX_set1_curves_list() to specify the curve used in ECDH. It
@@ -144,49 +110,91 @@ ossl_sslctx_s_alloc(VALUE klass)
return obj;
}
+static int
+parse_proto_version(VALUE str)
+{
+ int i;
+ static const struct {
+ const char *name;
+ int version;
+ } map[] = {
+ { "SSL2", SSL2_VERSION },
+ { "SSL3", SSL3_VERSION },
+ { "TLS1", TLS1_VERSION },
+ { "TLS1_1", TLS1_1_VERSION },
+ { "TLS1_2", TLS1_2_VERSION },
+#ifdef TLS1_3_VERSION
+ { "TLS1_3", TLS1_3_VERSION },
+#endif
+ };
+
+ if (NIL_P(str))
+ return 0;
+ if (RB_INTEGER_TYPE_P(str))
+ return NUM2INT(str);
+
+ if (SYMBOL_P(str))
+ str = rb_sym2str(str);
+ StringValue(str);
+ for (i = 0; i < numberof(map); i++)
+ if (!strncmp(map[i].name, RSTRING_PTR(str), RSTRING_LEN(str)))
+ return map[i].version;
+ rb_raise(rb_eArgError, "unrecognized version %+"PRIsVALUE, str);
+}
+
/*
* call-seq:
- * ctx.ssl_version = :TLSv1
- * ctx.ssl_version = "SSLv23_client"
+ * ctx.set_minmax_proto_version(min, max) -> nil
*
- * Sets the SSL/TLS protocol version for the context. This forces connections to
- * use only the specified protocol version.
- *
- * You can get a list of valid versions with OpenSSL::SSL::SSLContext::METHODS
+ * Sets the minimum and maximum supported protocol versions. See #min_version=
+ * and #max_version=.
*/
static VALUE
-ossl_sslctx_set_ssl_version(VALUE self, VALUE ssl_method)
+ossl_sslctx_set_minmax_proto_version(VALUE self, VALUE min_v, VALUE max_v)
{
SSL_CTX *ctx;
- const char *s;
- VALUE m = ssl_method;
- int i;
+ int min, max;
GetSSLCTX(self, ctx);
- if (RB_TYPE_P(ssl_method, T_SYMBOL))
- m = rb_sym2str(ssl_method);
- s = StringValueCStr(m);
- for (i = 0; i < numberof(ossl_ssl_method_tab); i++) {
- if (strcmp(ossl_ssl_method_tab[i].name, s) == 0) {
-#if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION)
- int version = ossl_ssl_method_tab[i].version;
-#endif
- SSL_METHOD *method = ossl_ssl_method_tab[i].func();
-
- if (SSL_CTX_set_ssl_version(ctx, method) != 1)
- ossl_raise(eSSLError, "SSL_CTX_set_ssl_version");
+ min = parse_proto_version(min_v);
+ max = parse_proto_version(max_v);
+
+#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
+ if (!SSL_CTX_set_min_proto_version(ctx, min))
+ ossl_raise(eSSLError, "SSL_CTX_set_min_proto_version");
+ if (!SSL_CTX_set_max_proto_version(ctx, max))
+ ossl_raise(eSSLError, "SSL_CTX_set_max_proto_version");
+#else
+ {
+ unsigned long sum = 0, opts = 0;
+ int i;
+ static const struct {
+ int ver;
+ unsigned long opts;
+ } options_map[] = {
+ { SSL2_VERSION, SSL_OP_NO_SSLv2 },
+ { SSL3_VERSION, SSL_OP_NO_SSLv3 },
+ { TLS1_VERSION, SSL_OP_NO_TLSv1 },
+ { TLS1_1_VERSION, SSL_OP_NO_TLSv1_1 },
+ { TLS1_2_VERSION, SSL_OP_NO_TLSv1_2 },
+# if defined(TLS1_3_VERSION)
+ { TLS1_3_VERSION, SSL_OP_NO_TLSv1_3 },
+# endif
+ };
-#if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION)
- if (!SSL_CTX_set_min_proto_version(ctx, version))
- ossl_raise(eSSLError, "SSL_CTX_set_min_proto_version");
- if (!SSL_CTX_set_max_proto_version(ctx, version))
- ossl_raise(eSSLError, "SSL_CTX_set_max_proto_version");
-#endif
- return ssl_method;
- }
+ for (i = 0; i < numberof(options_map); i++) {
+ sum |= options_map[i].opts;
+ if ((min && min > options_map[i].ver) ||
+ (max && max < options_map[i].ver)) {
+ opts |= options_map[i].opts;
+ }
+ }
+ SSL_CTX_clear_options(ctx, sum);
+ SSL_CTX_set_options(ctx, opts);
}
+#endif
- ossl_raise(rb_eArgError, "unknown SSL method `%"PRIsVALUE"'.", m);
+ return Qnil;
}
static VALUE
@@ -199,7 +207,7 @@ ossl_call_client_cert_cb(VALUE obj)
if (NIL_P(cb))
return Qnil;
- ary = rb_funcall(cb, rb_intern("call"), 1, obj);
+ ary = rb_funcallv(cb, id_call, 1, &obj);
Check_Type(ary, T_ARRAY);
GetX509CertPtr(cert = rb_ary_entry(ary, 0));
GetPrivPKeyPtr(key = rb_ary_entry(ary, 1));
@@ -223,69 +231,90 @@ ossl_client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
return 1;
}
-#if !defined(OPENSSL_NO_DH)
-static VALUE
-ossl_call_tmp_dh_callback(VALUE args)
+#if !defined(OPENSSL_NO_DH) || \
+ !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK)
+struct tmp_dh_callback_args {
+ VALUE ssl_obj;
+ ID id;
+ int type;
+ int is_export;
+ int keylength;
+};
+
+static EVP_PKEY *
+ossl_call_tmp_dh_callback(struct tmp_dh_callback_args *args)
{
VALUE cb, dh;
EVP_PKEY *pkey;
- cb = rb_funcall(rb_ary_entry(args, 0), rb_intern("tmp_dh_callback"), 0);
-
- if (NIL_P(cb)) return Qfalse;
- dh = rb_apply(cb, rb_intern("call"), args);
+ cb = rb_funcall(args->ssl_obj, args->id, 0);
+ if (NIL_P(cb))
+ return NULL;
+ dh = rb_funcall(cb, id_call, 3, args->ssl_obj, INT2NUM(args->is_export),
+ INT2NUM(args->keylength));
pkey = GetPKeyPtr(dh);
- if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) return Qfalse;
+ if (EVP_PKEY_base_id(pkey) != args->type)
+ return NULL;
- return dh;
+ return pkey;
}
+#endif
-static DH*
+#if !defined(OPENSSL_NO_DH)
+static DH *
ossl_tmp_dh_callback(SSL *ssl, int is_export, int keylength)
{
- VALUE args, dh, rb_ssl;
+ VALUE rb_ssl;
+ EVP_PKEY *pkey;
+ struct tmp_dh_callback_args args;
+ int state;
rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
+ args.ssl_obj = rb_ssl;
+ args.id = id_tmp_dh_callback;
+ args.is_export = is_export;
+ args.keylength = keylength;
+ args.type = EVP_PKEY_DH;
+
+ pkey = (EVP_PKEY *)rb_protect((VALUE (*)(VALUE))ossl_call_tmp_dh_callback,
+ (VALUE)&args, &state);
+ if (state) {
+ rb_ivar_set(rb_ssl, ID_callback_state, INT2NUM(state));
+ return NULL;
+ }
+ if (!pkey)
+ return NULL;
- args = rb_ary_new_from_args(3, rb_ssl, INT2NUM(is_export), INT2NUM(keylength));
-
- dh = rb_protect(ossl_call_tmp_dh_callback, args, NULL);
- if (!RTEST(dh)) return NULL;
-
- return EVP_PKEY_get0_DH(GetPKeyPtr(dh));
+ return EVP_PKEY_get0_DH(pkey);
}
#endif /* OPENSSL_NO_DH */
#if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK)
-static VALUE
-ossl_call_tmp_ecdh_callback(VALUE args)
-{
- VALUE cb, ecdh;
- EVP_PKEY *pkey;
-
- cb = rb_funcall(rb_ary_entry(args, 0), rb_intern("tmp_ecdh_callback"), 0);
-
- if (NIL_P(cb)) return Qfalse;
- ecdh = rb_apply(cb, rb_intern("call"), args);
- pkey = GetPKeyPtr(ecdh);
- if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) return Qfalse;
-
- return ecdh;
-}
-
-static EC_KEY*
+static EC_KEY *
ossl_tmp_ecdh_callback(SSL *ssl, int is_export, int keylength)
{
- VALUE args, ecdh, rb_ssl;
+ VALUE rb_ssl;
+ EVP_PKEY *pkey;
+ struct tmp_dh_callback_args args;
+ int state;
rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
+ args.ssl_obj = rb_ssl;
+ args.id = id_tmp_ecdh_callback;
+ args.is_export = is_export;
+ args.keylength = keylength;
+ args.type = EVP_PKEY_EC;
+
+ pkey = (EVP_PKEY *)rb_protect((VALUE (*)(VALUE))ossl_call_tmp_dh_callback,
+ (VALUE)&args, &state);
+ if (state) {
+ rb_ivar_set(rb_ssl, ID_callback_state, INT2NUM(state));
+ return NULL;
+ }
+ if (!pkey)
+ return NULL;
- args = rb_ary_new_from_args(3, rb_ssl, INT2NUM(is_export), INT2NUM(keylength));
-
- ecdh = rb_protect(ossl_call_tmp_ecdh_callback, args, NULL);
- if (!RTEST(ecdh)) return NULL;
-
- return EVP_PKEY_get0_EC_KEY(GetPKeyPtr(ecdh));
+ return EVP_PKEY_get0_EC_KEY(pkey);
}
#endif
@@ -347,12 +376,11 @@ ossl_call_session_get_cb(VALUE ary)
cb = rb_funcall(ssl_obj, rb_intern("session_get_cb"), 0);
if (NIL_P(cb)) return Qnil;
- return rb_funcall(cb, rb_intern("call"), 1, ary);
+ return rb_funcallv(cb, id_call, 1, &ary);
}
-/* this method is currently only called for servers (in OpenSSL <= 0.9.8e) */
static SSL_SESSION *
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
ossl_sslctx_session_get_cb(SSL *ssl, const unsigned char *buf, int len, int *copy)
#else
ossl_sslctx_session_get_cb(SSL *ssl, unsigned char *buf, int len, int *copy)
@@ -360,13 +388,10 @@ ossl_sslctx_session_get_cb(SSL *ssl, unsigned char *buf, int len, int *copy)
{
VALUE ary, ssl_obj, ret_obj;
SSL_SESSION *sess;
- void *ptr;
int state = 0;
OSSL_Debug("SSL SESSION get callback entered");
- if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL)
- return NULL;
- ssl_obj = (VALUE)ptr;
+ ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
ary = rb_ary_new2(2);
rb_ary_push(ary, ssl_obj);
rb_ary_push(ary, rb_str_new((const char *)buf, len));
@@ -379,7 +404,7 @@ ossl_sslctx_session_get_cb(SSL *ssl, unsigned char *buf, int len, int *copy)
if (!rb_obj_is_instance_of(ret_obj, cSSLSession))
return NULL;
- SafeGetSSLSession(ret_obj, sess);
+ GetSSLSession(ret_obj, sess);
*copy = 1;
return sess;
@@ -396,7 +421,7 @@ ossl_call_session_new_cb(VALUE ary)
cb = rb_funcall(ssl_obj, rb_intern("session_new_cb"), 0);
if (NIL_P(cb)) return Qnil;
- return rb_funcall(cb, rb_intern("call"), 1, ary);
+ return rb_funcallv(cb, id_call, 1, &ary);
}
/* return 1 normal. return 0 removes the session */
@@ -404,14 +429,11 @@ static int
ossl_sslctx_session_new_cb(SSL *ssl, SSL_SESSION *sess)
{
VALUE ary, ssl_obj, sess_obj;
- void *ptr;
int state = 0;
OSSL_Debug("SSL SESSION new callback entered");
- if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL)
- return 1;
- ssl_obj = (VALUE)ptr;
+ ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
sess_obj = rb_obj_alloc(cSSLSession);
SSL_SESSION_up_ref(sess);
DATA_PTR(sess_obj) = sess;
@@ -446,21 +468,25 @@ ossl_call_session_remove_cb(VALUE ary)
cb = rb_attr_get(sslctx_obj, id_i_session_remove_cb);
if (NIL_P(cb)) return Qnil;
- return rb_funcall(cb, rb_intern("call"), 1, ary);
+ return rb_funcallv(cb, id_call, 1, &ary);
}
static void
ossl_sslctx_session_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess)
{
VALUE ary, sslctx_obj, sess_obj;
- void *ptr;
int state = 0;
+ /*
+ * This callback is also called for all sessions in the internal store
+ * when SSL_CTX_free() is called.
+ */
+ if (rb_during_gc())
+ return;
+
OSSL_Debug("SSL SESSION remove callback entered");
- if ((ptr = SSL_CTX_get_ex_data(ctx, ossl_ssl_ex_ptr_idx)) == NULL)
- return;
- sslctx_obj = (VALUE)ptr;
+ sslctx_obj = (VALUE)SSL_CTX_get_ex_data(ctx, ossl_sslctx_ex_ptr_idx);
sess_obj = rb_obj_alloc(cSSLSession);
SSL_SESSION_up_ref(sess);
DATA_PTR(sess_obj) = sess;
@@ -496,7 +522,6 @@ ossl_sslctx_add_extra_chain_cert_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arg))
static VALUE ossl_sslctx_setup(VALUE self);
-#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
static VALUE
ossl_call_servername_cb(VALUE ary)
{
@@ -509,7 +534,7 @@ ossl_call_servername_cb(VALUE ary)
cb = rb_attr_get(sslctx_obj, id_i_servername_cb);
if (NIL_P(cb)) return Qnil;
- ret_obj = rb_funcall(cb, rb_intern("call"), 1, ary);
+ ret_obj = rb_funcallv(cb, id_call, 1, &ary);
if (rb_obj_is_kind_of(ret_obj, cSSLContext)) {
SSL *ssl;
SSL_CTX *ctx2;
@@ -531,16 +556,13 @@ static int
ssl_servername_cb(SSL *ssl, int *ad, void *arg)
{
VALUE ary, ssl_obj;
- void *ptr;
int state = 0;
const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (!servername)
return SSL_TLSEXT_ERR_OK;
- if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL)
- return SSL_TLSEXT_ERR_ALERT_FATAL;
- ssl_obj = (VALUE)ptr;
+ ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
ary = rb_ary_new2(2);
rb_ary_push(ary, ssl_obj);
rb_ary_push(ary, rb_str_new2(servername));
@@ -553,26 +575,21 @@ ssl_servername_cb(SSL *ssl, int *ad, void *arg)
return SSL_TLSEXT_ERR_OK;
}
-#endif
static void
ssl_renegotiation_cb(const SSL *ssl)
{
VALUE ssl_obj, sslctx_obj, cb;
- void *ptr;
-
- if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL)
- ossl_raise(eSSLError, "SSL object could not be retrieved");
- ssl_obj = (VALUE)ptr;
+ ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
sslctx_obj = rb_attr_get(ssl_obj, id_i_context);
cb = rb_attr_get(sslctx_obj, id_i_renegotiation_cb);
if (NIL_P(cb)) return;
- (void) rb_funcall(cb, rb_intern("call"), 1, ssl_obj);
+ rb_funcallv(cb, id_call, 1, &ssl_obj);
}
-#if defined(HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB) || \
+#if !defined(OPENSSL_NO_NEXTPROTONEG) || \
defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB)
static VALUE
ssl_npn_encode_protocol_i(VALUE cur, VALUE encoded)
@@ -619,7 +636,7 @@ npn_select_cb_common_i(VALUE tmp)
in += l;
}
- selected = rb_funcall(args->cb, rb_intern("call"), 1, protocols);
+ selected = rb_funcallv(args->cb, id_call, 1, &protocols);
StringValue(selected);
len = RSTRING_LEN(selected);
if (len < 1 || len >= 256) {
@@ -657,7 +674,7 @@ ssl_npn_select_cb_common(SSL *ssl, VALUE cb, const unsigned char **out,
}
#endif
-#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
+#ifndef OPENSSL_NO_NEXTPROTONEG
static int
ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen,
void *arg)
@@ -717,7 +734,11 @@ ossl_sslctx_get_options(VALUE self)
{
SSL_CTX *ctx;
GetSSLCTX(self, ctx);
- return LONG2NUM(SSL_CTX_get_options(ctx));
+ /*
+ * Do explicit cast because SSL_CTX_get_options() returned (signed) long in
+ * OpenSSL before 1.1.0.
+ */
+ return ULONG2NUM((unsigned long)SSL_CTX_get_options(ctx));
}
/*
@@ -736,7 +757,7 @@ ossl_sslctx_set_options(VALUE self, VALUE options)
if (NIL_P(options)) {
SSL_CTX_set_options(ctx, SSL_OP_ALL);
} else {
- SSL_CTX_set_options(ctx, NUM2LONG(options));
+ SSL_CTX_set_options(ctx, NUM2ULONG(options));
}
return self;
@@ -800,7 +821,7 @@ ossl_sslctx_setup(VALUE self)
* X509_STORE_free() doesn't care it.
* So we won't increment it but mark it by ex_data.
*/
- SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_store_p, (void *)1);
+ SSL_CTX_set_ex_data(ctx, ossl_sslctx_ex_store_p, ctx);
#else /* Fixed in OpenSSL 1.0.2; bff9ce4db38b (master), 5b4b9ce976fc (1.0.2) */
X509_STORE_up_ref(store);
#endif
@@ -871,10 +892,11 @@ 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));
-#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
+#ifndef OPENSSL_NO_NEXTPROTONEG
val = rb_attr_get(self, id_i_npn_protocols);
if (!NIL_P(val)) {
VALUE encoded = ssl_encode_npn_protocols(val);
+ rb_ivar_set(self, id_npn_protocols_encoded, encoded);
SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)encoded);
OSSL_Debug("SSL NPN advertise callback added");
}
@@ -925,13 +947,11 @@ ossl_sslctx_setup(VALUE self)
OSSL_Debug("SSL SESSION remove callback added");
}
-#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
val = rb_attr_get(self, id_i_servername_cb);
if (!NIL_P(val)) {
SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb);
OSSL_Debug("SSL TLSEXT servername callback added");
}
-#endif
return Qtrue;
}
@@ -968,12 +988,7 @@ ossl_sslctx_get_ciphers(VALUE self)
int i, num;
GetSSLCTX(self, ctx);
- if(!ctx){
- rb_warning("SSL_CTX is not initialized.");
- return Qnil;
- }
ciphers = SSL_CTX_get_ciphers(ctx);
-
if (!ciphers)
return rb_ary_new();
@@ -1021,10 +1036,6 @@ ossl_sslctx_set_ciphers(VALUE self, VALUE v)
}
GetSSLCTX(self, ctx);
- if(!ctx){
- ossl_raise(eSSLError, "SSL_CTX is not initialized.");
- return Qnil;
- }
if (!SSL_CTX_set_cipher_list(ctx, StringValueCStr(str))) {
ossl_raise(eSSLError, "SSL_CTX_set_cipher_list");
}
@@ -1179,11 +1190,139 @@ ossl_sslctx_set_security_level(VALUE self, VALUE value)
return value;
}
+#ifdef SSL_MODE_SEND_FALLBACK_SCSV
+/*
+ * call-seq:
+ * ctx.enable_fallback_scsv() => nil
+ *
+ * Activate TLS_FALLBACK_SCSV for this context.
+ * See RFC 7507.
+ */
+static VALUE
+ossl_sslctx_enable_fallback_scsv(VALUE self)
+{
+ SSL_CTX *ctx;
+
+ GetSSLCTX(self, ctx);
+ SSL_CTX_set_mode(ctx, SSL_MODE_SEND_FALLBACK_SCSV);
+
+ return Qnil;
+}
+#endif
+
+/*
+ * call-seq:
+ * ctx.add_certificate(certiticate, pkey [, extra_certs]) -> self
+ *
+ * Adds a certificate to the context. _pkey_ must be a corresponding private
+ * key with _certificate_.
+ *
+ * Multiple certificates with different public key type can be added by
+ * repeated calls of this method, and OpenSSL will choose the most appropriate
+ * certificate during the handshake.
+ *
+ * #cert=, #key=, and #extra_chain_cert= are old accessor methods for setting
+ * certificate and internally call this method.
+ *
+ * === Parameters
+ * _certificate_::
+ * A certificate. An instance of OpenSSL::X509::Certificate.
+ * _pkey_::
+ * The private key for _certificate_. An instance of OpenSSL::PKey::PKey.
+ * _extra_certs_::
+ * Optional. An array of OpenSSL::X509::Certificate. When sending a
+ * certificate chain, the certificates specified by this are sent following
+ * _certificate_, in the order in the array.
+ *
+ * === Example
+ * rsa_cert = OpenSSL::X509::Certificate.new(...)
+ * rsa_pkey = OpenSSL::PKey.read(...)
+ * ca_intermediate_cert = OpenSSL::X509::Certificate.new(...)
+ * ctx.add_certificate(rsa_cert, rsa_pkey, [ca_intermediate_cert])
+ *
+ * ecdsa_cert = ...
+ * ecdsa_pkey = ...
+ * another_ca_cert = ...
+ * ctx.add_certificate(ecdsa_cert, ecdsa_pkey, [another_ca_cert])
+ *
+ * === Note
+ * OpenSSL before the version 1.0.2 could handle only one extra chain across
+ * all key types. Calling this method discards the chain set previously.
+ */
+static VALUE
+ossl_sslctx_add_certificate(int argc, VALUE *argv, VALUE self)
+{
+ VALUE cert, key, extra_chain_ary;
+ SSL_CTX *ctx;
+ X509 *x509;
+ STACK_OF(X509) *extra_chain = NULL;
+ EVP_PKEY *pkey, *pub_pkey;
+
+ GetSSLCTX(self, ctx);
+ rb_scan_args(argc, argv, "21", &cert, &key, &extra_chain_ary);
+ rb_check_frozen(self);
+ x509 = GetX509CertPtr(cert);
+ pkey = GetPrivPKeyPtr(key);
+
+ /*
+ * The reference counter is bumped, and decremented immediately.
+ * X509_get0_pubkey() is only available in OpenSSL >= 1.1.0.
+ */
+ pub_pkey = X509_get_pubkey(x509);
+ EVP_PKEY_free(pub_pkey);
+ if (!pub_pkey)
+ rb_raise(rb_eArgError, "certificate does not contain public key");
+ if (EVP_PKEY_cmp(pub_pkey, pkey) != 1)
+ rb_raise(rb_eArgError, "public key mismatch");
+
+ if (argc >= 3)
+ extra_chain = ossl_x509_ary2sk(extra_chain_ary);
+
+ if (!SSL_CTX_use_certificate(ctx, x509)) {
+ sk_X509_pop_free(extra_chain, X509_free);
+ ossl_raise(eSSLError, "SSL_CTX_use_certificate");
+ }
+ if (!SSL_CTX_use_PrivateKey(ctx, pkey)) {
+ sk_X509_pop_free(extra_chain, X509_free);
+ ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey");
+ }
+
+ if (extra_chain) {
+#if OPENSSL_VERSION_NUMBER >= 0x10002000 && !defined(LIBRESSL_VERSION_NUMBER)
+ if (!SSL_CTX_set0_chain(ctx, extra_chain)) {
+ sk_X509_pop_free(extra_chain, X509_free);
+ ossl_raise(eSSLError, "SSL_CTX_set0_chain");
+ }
+#else
+ STACK_OF(X509) *orig_extra_chain;
+ X509 *x509_tmp;
+
+ /* First, clear the existing chain */
+ SSL_CTX_get_extra_chain_certs(ctx, &orig_extra_chain);
+ if (orig_extra_chain && sk_X509_num(orig_extra_chain)) {
+ rb_warning("SSL_CTX_set0_chain() is not available; " \
+ "clearing previously set certificate chain");
+ SSL_CTX_clear_extra_chain_certs(ctx);
+ }
+ while ((x509_tmp = sk_X509_shift(extra_chain))) {
+ /* Transfers ownership */
+ if (!SSL_CTX_add_extra_chain_cert(ctx, x509_tmp)) {
+ X509_free(x509_tmp);
+ sk_X509_pop_free(extra_chain, X509_free);
+ ossl_raise(eSSLError, "SSL_CTX_add_extra_chain_cert");
+ }
+ }
+ sk_X509_free(extra_chain);
+#endif
+ }
+ return self;
+}
+
/*
* call-seq:
* ctx.session_add(session) -> true | false
*
- * Adds +session+ to the session cache.
+ * Adds _session_ to the session cache.
*/
static VALUE
ossl_sslctx_session_add(VALUE self, VALUE arg)
@@ -1192,7 +1331,7 @@ ossl_sslctx_session_add(VALUE self, VALUE arg)
SSL_SESSION *sess;
GetSSLCTX(self, ctx);
- SafeGetSSLSession(arg, sess);
+ GetSSLSession(arg, sess);
return SSL_CTX_add_session(ctx, sess) == 1 ? Qtrue : Qfalse;
}
@@ -1201,7 +1340,7 @@ ossl_sslctx_session_add(VALUE self, VALUE arg)
* call-seq:
* ctx.session_remove(session) -> true | false
*
- * Removes +session+ from the session cache.
+ * Removes _session_ from the session cache.
*/
static VALUE
ossl_sslctx_session_remove(VALUE self, VALUE arg)
@@ -1210,7 +1349,7 @@ ossl_sslctx_session_remove(VALUE self, VALUE arg)
SSL_SESSION *sess;
GetSSLCTX(self, ctx);
- SafeGetSSLSession(arg, sess);
+ GetSSLSession(arg, sess);
return SSL_CTX_remove_session(ctx, sess) == 1 ? Qtrue : Qfalse;
}
@@ -1337,9 +1476,9 @@ ossl_sslctx_get_session_cache_stats(VALUE self)
/*
* call-seq:
- * ctx.flush_sessions(time | nil) -> self
+ * ctx.flush_sessions(time) -> self
*
- * Removes sessions in the internal cache that have expired at +time+.
+ * Removes sessions in the internal cache that have expired at _time_.
*/
static VALUE
ossl_sslctx_flush_sessions(int argc, VALUE *argv, VALUE self)
@@ -1377,24 +1516,6 @@ ssl_started(SSL *ssl)
}
static void
-ossl_ssl_shutdown(SSL *ssl)
-{
- int i;
-
- /* 4 is from SSL_smart_shutdown() of mod_ssl.c (v2.2.19) */
- /* It says max 2x pending + 2x data = 4 */
- for (i = 0; i < 4; ++i) {
- /*
- * Ignore the case SSL_shutdown returns -1. Empty handshake_func
- * must not happen.
- */
- if (SSL_shutdown(ssl) != 0)
- break;
- }
- ossl_clear_error();
-}
-
-static void
ossl_ssl_free(void *ssl)
{
SSL_free(ssl);
@@ -1419,10 +1540,10 @@ ossl_ssl_s_alloc(VALUE klass)
* SSLSocket.new(io) => aSSLSocket
* SSLSocket.new(io, ctx) => aSSLSocket
*
- * Creates a new SSL socket from +io+ which must be a real IO object (not an
+ * Creates a new SSL socket from _io_ which must be a real IO object (not an
* IO-like object that responds to read/write).
*
- * If +ctx+ is provided the SSL Sockets initial params will be taken from
+ * If _ctx_ is provided the SSL Sockets initial params will be taken from
* the context.
*
* The OpenSSL::Buffering module provides additional IO methods.
@@ -1482,7 +1603,8 @@ ossl_ssl_setup(VALUE self)
GetOpenFile(io, fptr);
rb_io_check_readable(fptr);
rb_io_check_writable(fptr);
- SSL_set_fd(ssl, TO_SOCKET(FPTR_TO_FD(fptr)));
+ if (!SSL_set_fd(ssl, TO_SOCKET(fptr->fd)))
+ ossl_raise(eSSLError, "SSL_set_fd");
return Qtrue;
}
@@ -1496,19 +1618,15 @@ ossl_ssl_setup(VALUE self)
static void
write_would_block(int nonblock)
{
- if (nonblock) {
- VALUE exc = ossl_exc_new(eSSLErrorWaitWritable, "write would block");
- rb_exc_raise(exc);
- }
+ if (nonblock)
+ ossl_raise(eSSLErrorWaitWritable, "write would block");
}
static void
read_would_block(int nonblock)
{
- if (nonblock) {
- VALUE exc = ossl_exc_new(eSSLErrorWaitReadable, "read would block");
- rb_exc_raise(exc);
- }
+ if (nonblock)
+ ossl_raise(eSSLErrorWaitReadable, "read would block");
}
static int
@@ -1528,6 +1646,9 @@ ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts)
int ret, ret2;
VALUE cb_state;
int nonblock = opts != Qfalse;
+#if defined(SSL_R_CERTIFICATE_VERIFY_FAILED)
+ unsigned long err;
+#endif
rb_ivar_set(self, ID_callback_state, Qnil);
@@ -1551,16 +1672,33 @@ ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts)
case SSL_ERROR_WANT_WRITE:
if (no_exception_p(opts)) { return sym_wait_writable; }
write_would_block(nonblock);
- rb_io_wait_writable(FPTR_TO_FD(fptr));
+ rb_io_wait_writable(fptr->fd);
continue;
case SSL_ERROR_WANT_READ:
if (no_exception_p(opts)) { return sym_wait_readable; }
read_would_block(nonblock);
- rb_io_wait_readable(FPTR_TO_FD(fptr));
+ rb_io_wait_readable(fptr->fd);
continue;
case SSL_ERROR_SYSCALL:
if (errno) rb_sys_fail(funcname);
ossl_raise(eSSLError, "%s SYSCALL returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl));
+#if defined(SSL_R_CERTIFICATE_VERIFY_FAILED)
+ case SSL_ERROR_SSL:
+ err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) == ERR_LIB_SSL &&
+ ERR_GET_REASON(err) == SSL_R_CERTIFICATE_VERIFY_FAILED) {
+ const char *err_msg = ERR_reason_error_string(err),
+ *verify_msg = X509_verify_cert_error_string(SSL_get_verify_result(ssl));
+ if (!err_msg)
+ err_msg = "(null)";
+ if (!verify_msg)
+ verify_msg = "(null)";
+ ossl_clear_error(); /* let ossl_raise() not append message */
+ ossl_raise(eSSLError, "%s returned=%d errno=%d state=%s: %s (%s)",
+ funcname, ret2, errno, SSL_state_string_long(ssl),
+ err_msg, verify_msg);
+ }
+#endif
default:
ossl_raise(eSSLError, "%s returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl));
}
@@ -1601,10 +1739,10 @@ ossl_ssl_connect(VALUE self)
* retry
* end
*
- * By specifying `exception: false`, the options hash allows you to indicate
+ * By specifying a keyword argument _exception_ to +false+, you can indicate
* that connect_nonblock should not raise an IO::WaitReadable or
- * IO::WaitWritable exception, but return the symbol :wait_readable or
- * :wait_writable instead.
+ * IO::WaitWritable exception, but return the symbol +:wait_readable+ or
+ * +:wait_writable+ instead.
*/
static VALUE
ossl_ssl_connect_nonblock(int argc, VALUE *argv, VALUE self)
@@ -1649,10 +1787,10 @@ ossl_ssl_accept(VALUE self)
* retry
* end
*
- * By specifying `exception: false`, the options hash allows you to indicate
+ * By specifying a keyword argument _exception_ to +false+, you can indicate
* that accept_nonblock should not raise an IO::WaitReadable or
- * IO::WaitWritable exception, but return the symbol :wait_readable or
- * :wait_writable instead.
+ * IO::WaitWritable exception, but return the symbol +:wait_readable+ or
+ * +:wait_writable+ instead.
*/
static VALUE
ossl_ssl_accept_nonblock(int argc, VALUE *argv, VALUE self)
@@ -1681,22 +1819,26 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
}
ilen = NUM2INT(len);
- if(NIL_P(str)) str = rb_str_new(0, ilen);
- else{
- StringValue(str);
- rb_str_modify(str);
- rb_str_resize(str, ilen);
+ if (NIL_P(str))
+ str = rb_str_new(0, ilen);
+ else {
+ StringValue(str);
+ if (RSTRING_LEN(str) >= ilen)
+ rb_str_modify(str);
+ else
+ rb_str_modify_expand(str, ilen - RSTRING_LEN(str));
}
- if(ilen == 0) return str;
+ OBJ_TAINT(str);
+ rb_str_set_len(str, 0);
+ if (ilen == 0)
+ return str;
GetSSL(self, ssl);
io = rb_attr_get(self, id_i_io);
GetOpenFile(io, fptr);
if (ssl_started(ssl)) {
- if(!nonblock && SSL_pending(ssl) <= 0)
- rb_thread_wait_fd(FPTR_TO_FD(fptr));
for (;;){
- nread = SSL_read(ssl, RSTRING_PTR(str), RSTRING_LENINT(str));
+ nread = SSL_read(ssl, RSTRING_PTR(str), ilen);
switch(ssl_get_error(ssl, nread)){
case SSL_ERROR_NONE:
goto end;
@@ -1706,19 +1848,29 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
case SSL_ERROR_WANT_WRITE:
if (no_exception_p(opts)) { return sym_wait_writable; }
write_would_block(nonblock);
- rb_io_wait_writable(FPTR_TO_FD(fptr));
+ rb_io_wait_writable(fptr->fd);
continue;
case SSL_ERROR_WANT_READ:
if (no_exception_p(opts)) { return sym_wait_readable; }
read_would_block(nonblock);
- rb_io_wait_readable(FPTR_TO_FD(fptr));
+ rb_io_wait_readable(fptr->fd);
continue;
case SSL_ERROR_SYSCALL:
- if(ERR_peek_error() == 0 && nread == 0) {
- if (no_exception_p(opts)) { return Qnil; }
- rb_eof_error();
+ if (!ERR_peek_error()) {
+ if (errno)
+ rb_sys_fail(0);
+ else {
+ /*
+ * The underlying BIO returned 0. This is actually a
+ * protocol error. But unfortunately, not all
+ * implementations cleanly shutdown the TLS connection
+ * but just shutdown/close the TCP connection. So report
+ * EOF for now...
+ */
+ if (no_exception_p(opts)) { return Qnil; }
+ rb_eof_error();
+ }
}
- rb_sys_fail(0);
default:
ossl_raise(eSSLError, "SSL_read");
}
@@ -1736,8 +1888,6 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
end:
rb_str_set_len(str, nread);
- OBJ_TAINT(str);
-
return str;
}
@@ -1746,7 +1896,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
* ssl.sysread(length) => string
* ssl.sysread(length, buffer) => buffer
*
- * Reads +length+ bytes from the SSL connection. If a pre-allocated +buffer+
+ * Reads _length_ bytes from the SSL connection. If a pre-allocated _buffer_
* is provided the data will be written into it.
*/
static VALUE
@@ -1765,7 +1915,7 @@ ossl_ssl_read(int argc, VALUE *argv, VALUE self)
* block. If "exception: false" is passed, this method returns a symbol of
* :wait_readable, :wait_writable, or nil, rather than raising an exception.
*
- * Reads +length+ bytes from the SSL connection. If a pre-allocated +buffer+
+ * Reads _length_ bytes from the SSL connection. If a pre-allocated _buffer_
* is provided the data will be written into it.
*/
static VALUE
@@ -1802,12 +1952,12 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
case SSL_ERROR_WANT_WRITE:
if (no_exception_p(opts)) { return sym_wait_writable; }
write_would_block(nonblock);
- rb_io_wait_writable(FPTR_TO_FD(fptr));
+ rb_io_wait_writable(fptr->fd);
continue;
case SSL_ERROR_WANT_READ:
if (no_exception_p(opts)) { return sym_wait_readable; }
read_would_block(nonblock);
- rb_io_wait_readable(FPTR_TO_FD(fptr));
+ rb_io_wait_readable(fptr->fd);
continue;
case SSL_ERROR_SYSCALL:
if (errno) rb_sys_fail(0);
@@ -1835,7 +1985,7 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
* call-seq:
* ssl.syswrite(string) => Integer
*
- * Writes +string+ to the SSL connection.
+ * Writes _string_ to the SSL connection.
*/
static VALUE
ossl_ssl_write(VALUE self, VALUE str)
@@ -1847,7 +1997,7 @@ ossl_ssl_write(VALUE self, VALUE str)
* call-seq:
* ssl.syswrite_nonblock(string) => Integer
*
- * Writes +string+ to the SSL connection in a non-blocking manner. Raises an
+ * Writes _string_ to the SSL connection in a non-blocking manner. Raises an
* SSLError if writing would block.
*/
static VALUE
@@ -1871,11 +2021,24 @@ static VALUE
ossl_ssl_stop(VALUE self)
{
SSL *ssl;
+ int ret;
GetSSL(self, ssl);
+ if (!ssl_started(ssl))
+ return Qnil;
+ ret = SSL_shutdown(ssl);
+ if (ret == 1) /* Have already received close_notify */
+ return Qnil;
+ if (ret == 0) /* Sent close_notify, but we don't wait for reply */
+ return Qnil;
- ossl_ssl_shutdown(ssl);
-
+ /*
+ * XXX: Something happened. Possibly it failed because the underlying socket
+ * is not writable/readable, since it is in non-blocking mode. We should do
+ * some proper error handling using SSL_get_error() and maybe retry, but we
+ * can't block here. Give up for now.
+ */
+ ossl_clear_error();
return Qnil;
}
@@ -1978,22 +2141,21 @@ ossl_ssl_get_version(VALUE self)
}
/*
-* call-seq:
-* ssl.cipher => [name, version, bits, alg_bits]
-*
-* The cipher being used for the current connection
-*/
+ * call-seq:
+ * ssl.cipher -> nil or [name, version, bits, alg_bits]
+ *
+ * Returns the cipher suite actually used in the current session, or nil if
+ * no session has been established.
+ */
static VALUE
ossl_ssl_get_cipher(VALUE self)
{
SSL *ssl;
- SSL_CIPHER *cipher;
+ const SSL_CIPHER *cipher;
GetSSL(self, ssl);
-
- cipher = (SSL_CIPHER *)SSL_get_current_cipher(ssl);
-
- return ossl_ssl_cipher_to_ary(cipher);
+ cipher = SSL_get_current_cipher(ssl);
+ return cipher ? ossl_ssl_cipher_to_ary(cipher) : Qnil;
}
/*
@@ -2039,7 +2201,7 @@ ossl_ssl_pending(VALUE self)
* call-seq:
* ssl.session_reused? -> true | false
*
- * Returns true if a reused session was negotiated during the handshake.
+ * Returns +true+ if a reused session was negotiated during the handshake.
*/
static VALUE
ossl_ssl_session_reused(VALUE self)
@@ -2064,7 +2226,7 @@ ossl_ssl_set_session(VALUE self, VALUE arg1)
SSL_SESSION *sess;
GetSSL(self, ssl);
- SafeGetSSLSession(arg1, sess);
+ GetSSLSession(arg1, sess);
if (SSL_set_session(ssl, sess) != 1)
ossl_raise(eSSLError, "SSL_set_session");
@@ -2072,7 +2234,6 @@ ossl_ssl_set_session(VALUE self, VALUE arg1)
return arg1;
}
-#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
/*
* call-seq:
* ssl.hostname = hostname -> hostname
@@ -2099,7 +2260,6 @@ ossl_ssl_set_hostname(VALUE self, VALUE arg)
return arg;
}
-#endif
/*
* call-seq:
@@ -2143,7 +2303,7 @@ ossl_ssl_get_client_ca_list(VALUE self)
return ossl_x509name_sk2ary(ca);
}
-# ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
+# ifndef OPENSSL_NO_NEXTPROTONEG
/*
* call-seq:
* ssl.npn_protocol => String | nil
@@ -2219,9 +2379,6 @@ ossl_ssl_tmp_key(VALUE self)
void
Init_ossl_ssl(void)
{
- int i;
- VALUE ary;
-
#if 0
mOSSL = rb_define_module("OpenSSL");
eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
@@ -2229,11 +2386,23 @@ Init_ossl_ssl(void)
rb_mWaitWritable = rb_define_module_under(rb_cIO, "WaitWritable");
#endif
+ id_call = rb_intern("call");
ID_callback_state = rb_intern("callback_state");
- ossl_ssl_ex_vcb_idx = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_vcb_idx",0,0,0);
- ossl_ssl_ex_store_p = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_store_p",0,0,0);
- ossl_ssl_ex_ptr_idx = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_ptr_idx",0,0,0);
+ ossl_ssl_ex_vcb_idx = SSL_get_ex_new_index(0, (void *)"ossl_ssl_ex_vcb_idx", 0, 0, 0);
+ if (ossl_ssl_ex_vcb_idx < 0)
+ ossl_raise(rb_eRuntimeError, "SSL_get_ex_new_index");
+ ossl_ssl_ex_ptr_idx = SSL_get_ex_new_index(0, (void *)"ossl_ssl_ex_ptr_idx", 0, 0, 0);
+ if (ossl_ssl_ex_ptr_idx < 0)
+ ossl_raise(rb_eRuntimeError, "SSL_get_ex_new_index");
+ ossl_sslctx_ex_ptr_idx = SSL_CTX_get_ex_new_index(0, (void *)"ossl_sslctx_ex_ptr_idx", 0, 0, 0);
+ if (ossl_sslctx_ex_ptr_idx < 0)
+ ossl_raise(rb_eRuntimeError, "SSL_CTX_get_ex_new_index");
+#if !defined(HAVE_X509_STORE_UP_REF)
+ ossl_sslctx_ex_store_p = SSL_CTX_get_ex_new_index(0, (void *)"ossl_sslctx_ex_store_p", 0, 0, 0);
+ if (ossl_sslctx_ex_store_p < 0)
+ ossl_raise(rb_eRuntimeError, "SSL_CTX_get_ex_new_index");
+#endif
/* Document-module: OpenSSL::SSL
*
@@ -2249,7 +2418,7 @@ Init_ossl_ssl(void)
* 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
+ * +true+ or +false+ values depending on the configuration of your OpenSSL
* installation.
*/
mSSLExtConfig = rb_define_module_under(mOSSL, "ExtConfig");
@@ -2281,11 +2450,17 @@ Init_ossl_ssl(void)
/*
* Context certificate
+ *
+ * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
+ * It is recommended to use #add_certificate instead.
*/
rb_attr(cSSLContext, rb_intern("cert"), 1, 1, Qfalse);
/*
* Context private key
+ *
+ * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
+ * It is recommended to use #add_certificate instead.
*/
rb_attr(cSSLContext, rb_intern("key"), 1, 1, Qfalse);
@@ -2333,12 +2508,12 @@ Init_ossl_ssl(void)
* A callback for additional certificate verification. The callback is
* invoked for each certificate in the chain.
*
- * The callback is invoked with two values. +preverify_ok+ indicates
- * indicates if the verification was passed (true) or not (false).
- * +store_context+ is an OpenSSL::X509::StoreContext containing the
+ * The callback is invoked with two values. _preverify_ok_ indicates
+ * indicates if the verification was passed (+true+) or not (+false+).
+ * _store_context_ is an OpenSSL::X509::StoreContext containing the
* context used for certificate verification.
*
- * If the callback returns false, the chain verification is immediately
+ * If the callback returns +false+, the chain verification is immediately
* stopped and a bad_certificate alert is then sent.
*/
rb_attr(cSSLContext, rb_intern("verify_callback"), 1, 1, Qfalse);
@@ -2359,6 +2534,9 @@ Init_ossl_ssl(void)
/*
* An Array of extra X509 certificates to be added to the certificate
* chain.
+ *
+ * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
+ * It is recommended to use #add_certificate instead.
*/
rb_attr(cSSLContext, rb_intern("extra_chain_cert"), 1, 1, Qfalse);
@@ -2405,7 +2583,7 @@ Init_ossl_ssl(void)
/*
* A callback invoked when a new session was negotiated.
*
- * The callback is invoked with an SSLSocket. If false is returned the
+ * The callback is invoked with an SSLSocket. If +false+ is returned the
* session will be removed from the internal cache.
*/
rb_attr(cSSLContext, rb_intern("session_new_cb"), 1, 1, Qfalse);
@@ -2414,20 +2592,14 @@ Init_ossl_ssl(void)
* A callback invoked when a session is removed from the internal cache.
*
* The callback is invoked with an SSLContext and a Session.
+ *
+ * IMPORTANT NOTE: It is currently not possible to use this safely in a
+ * multi-threaded application. The callback is called inside a global lock
+ * and it can randomly cause deadlock on Ruby thread switching.
*/
rb_attr(cSSLContext, rb_intern("session_remove_cb"), 1, 1, Qfalse);
-#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
rb_define_const(mSSLExtConfig, "HAVE_TLSEXT_HOST_NAME", Qtrue);
-#else
- rb_define_const(mSSLExtConfig, "HAVE_TLSEXT_HOST_NAME", Qfalse);
-#endif
-
-#ifdef TLS_DH_anon_WITH_AES_256_GCM_SHA384
- rb_define_const(mSSLExtConfig, "TLS_DH_anon_WITH_AES_256_GCM_SHA384", Qtrue);
-#else
- rb_define_const(mSSLExtConfig, "TLS_DH_anon_WITH_AES_256_GCM_SHA384", Qfalse);
-#endif
/*
* A callback invoked whenever a new handshake is initiated. May be used
@@ -2451,7 +2623,7 @@ Init_ossl_ssl(void)
* end
*/
rb_attr(cSSLContext, rb_intern("renegotiation_cb"), 1, 1, Qfalse);
-#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
+#ifndef OPENSSL_NO_NEXTPROTONEG
/*
* An Enumerable of Strings. Each String represents a protocol to be
* advertised as the list of supported protocols for Next Protocol
@@ -2517,14 +2689,20 @@ Init_ossl_ssl(void)
rb_define_alias(cSSLContext, "ssl_timeout", "timeout");
rb_define_alias(cSSLContext, "ssl_timeout=", "timeout=");
- rb_define_method(cSSLContext, "ssl_version=", ossl_sslctx_set_ssl_version, 1);
+ rb_define_private_method(cSSLContext, "set_minmax_proto_version",
+ ossl_sslctx_set_minmax_proto_version, 2);
rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0);
rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1);
rb_define_method(cSSLContext, "ecdh_curves=", ossl_sslctx_set_ecdh_curves, 1);
rb_define_method(cSSLContext, "security_level", ossl_sslctx_get_security_level, 0);
rb_define_method(cSSLContext, "security_level=", ossl_sslctx_set_security_level, 1);
+#ifdef SSL_MODE_SEND_FALLBACK_SCSV
+ rb_define_method(cSSLContext, "enable_fallback_scsv", ossl_sslctx_enable_fallback_scsv, 0);
+#endif
+ rb_define_method(cSSLContext, "add_certificate", ossl_sslctx_add_certificate, -1);
rb_define_method(cSSLContext, "setup", ossl_sslctx_setup, 0);
+ rb_define_alias(cSSLContext, "freeze", "setup");
/*
* No session caching for client or server
@@ -2584,14 +2762,6 @@ Init_ossl_ssl(void)
rb_define_method(cSSLContext, "options", ossl_sslctx_get_options, 0);
rb_define_method(cSSLContext, "options=", ossl_sslctx_set_options, 1);
- ary = rb_ary_new2(numberof(ossl_ssl_method_tab));
- for (i = 0; i < numberof(ossl_ssl_method_tab); i++) {
- rb_ary_push(ary, ID2SYM(rb_intern(ossl_ssl_method_tab[i].name)));
- }
- rb_obj_freeze(ary);
- /* The list of available SSL/TLS methods */
- rb_define_const(cSSLContext, "METHODS", ary);
-
/*
* Document-class: OpenSSL::SSL::SSLSocket
*/
@@ -2625,72 +2795,129 @@ Init_ossl_ssl(void)
rb_define_method(cSSLSocket, "session=", ossl_ssl_set_session, 1);
rb_define_method(cSSLSocket, "verify_result", ossl_ssl_get_verify_result, 0);
rb_define_method(cSSLSocket, "client_ca", ossl_ssl_get_client_ca_list, 0);
-#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
/* #hostname is defined in lib/openssl/ssl.rb */
rb_define_method(cSSLSocket, "hostname=", ossl_ssl_set_hostname, 1);
-#endif
# ifdef HAVE_SSL_GET_SERVER_TMP_KEY
rb_define_method(cSSLSocket, "tmp_key", ossl_ssl_tmp_key, 0);
# endif
# ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
rb_define_method(cSSLSocket, "alpn_protocol", ossl_ssl_alpn_protocol, 0);
# endif
-# ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
+# ifndef OPENSSL_NO_NEXTPROTONEG
rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0);
# endif
#endif
-#define ossl_ssl_def_const(x) rb_define_const(mSSL, #x, LONG2NUM(SSL_##x))
+ rb_define_const(mSSL, "VERIFY_NONE", INT2NUM(SSL_VERIFY_NONE));
+ rb_define_const(mSSL, "VERIFY_PEER", INT2NUM(SSL_VERIFY_PEER));
+ rb_define_const(mSSL, "VERIFY_FAIL_IF_NO_PEER_CERT", INT2NUM(SSL_VERIFY_FAIL_IF_NO_PEER_CERT));
+ rb_define_const(mSSL, "VERIFY_CLIENT_ONCE", INT2NUM(SSL_VERIFY_CLIENT_ONCE));
- ossl_ssl_def_const(VERIFY_NONE);
- ossl_ssl_def_const(VERIFY_PEER);
- ossl_ssl_def_const(VERIFY_FAIL_IF_NO_PEER_CERT);
- ossl_ssl_def_const(VERIFY_CLIENT_ONCE);
- /* Introduce constants included in OP_ALL. These constants are mostly for
- * unset some bits in OP_ALL such as;
- * ctx.options = OP_ALL & ~OP_DONT_INSERT_EMPTY_FRAGMENTS
- */
- ossl_ssl_def_const(OP_MICROSOFT_SESS_ID_BUG);
- ossl_ssl_def_const(OP_NETSCAPE_CHALLENGE_BUG);
- ossl_ssl_def_const(OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG);
- ossl_ssl_def_const(OP_SSLREF2_REUSE_CERT_TYPE_BUG);
- ossl_ssl_def_const(OP_MICROSOFT_BIG_SSLV3_BUFFER);
- ossl_ssl_def_const(OP_MSIE_SSLV2_RSA_PADDING);
- ossl_ssl_def_const(OP_SSLEAY_080_CLIENT_DH_BUG);
- ossl_ssl_def_const(OP_TLS_D5_BUG);
- ossl_ssl_def_const(OP_TLS_BLOCK_PADDING_BUG);
- ossl_ssl_def_const(OP_DONT_INSERT_EMPTY_FRAGMENTS);
- ossl_ssl_def_const(OP_ALL);
- ossl_ssl_def_const(OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
- ossl_ssl_def_const(OP_SINGLE_ECDH_USE);
- ossl_ssl_def_const(OP_SINGLE_DH_USE);
- ossl_ssl_def_const(OP_EPHEMERAL_RSA);
- ossl_ssl_def_const(OP_CIPHER_SERVER_PREFERENCE);
- ossl_ssl_def_const(OP_TLS_ROLLBACK_BUG);
- ossl_ssl_def_const(OP_NO_SSLv2);
- ossl_ssl_def_const(OP_NO_SSLv3);
- ossl_ssl_def_const(OP_NO_TLSv1);
-#if defined(SSL_OP_NO_TLSv1_1)
- ossl_ssl_def_const(OP_NO_TLSv1_1);
+ rb_define_const(mSSL, "OP_ALL", ULONG2NUM(SSL_OP_ALL));
+ rb_define_const(mSSL, "OP_LEGACY_SERVER_CONNECT", ULONG2NUM(SSL_OP_LEGACY_SERVER_CONNECT));
+#ifdef SSL_OP_TLSEXT_PADDING /* OpenSSL 1.0.1h and OpenSSL 1.0.2 */
+ rb_define_const(mSSL, "OP_TLSEXT_PADDING", ULONG2NUM(SSL_OP_TLSEXT_PADDING));
+#endif
+#ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG /* OpenSSL 1.0.1f and OpenSSL 1.0.2 */
+ rb_define_const(mSSL, "OP_SAFARI_ECDHE_ECDSA_BUG", ULONG2NUM(SSL_OP_SAFARI_ECDHE_ECDSA_BUG));
#endif
-#if defined(SSL_OP_NO_TLSv1_2)
- ossl_ssl_def_const(OP_NO_TLSv1_2);
+#ifdef SSL_OP_ALLOW_NO_DHE_KEX /* OpenSSL 1.1.1 */
+ rb_define_const(mSSL, "OP_ALLOW_NO_DHE_KEX", ULONG2NUM(SSL_OP_ALLOW_NO_DHE_KEX));
#endif
-#if defined(SSL_OP_NO_TICKET)
- ossl_ssl_def_const(OP_NO_TICKET);
+ rb_define_const(mSSL, "OP_DONT_INSERT_EMPTY_FRAGMENTS", ULONG2NUM(SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS));
+ rb_define_const(mSSL, "OP_NO_TICKET", ULONG2NUM(SSL_OP_NO_TICKET));
+ rb_define_const(mSSL, "OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION", ULONG2NUM(SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION));
+ rb_define_const(mSSL, "OP_NO_COMPRESSION", ULONG2NUM(SSL_OP_NO_COMPRESSION));
+ rb_define_const(mSSL, "OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION", ULONG2NUM(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
+#ifdef SSL_OP_NO_ENCRYPT_THEN_MAC /* OpenSSL 1.1.1 */
+ rb_define_const(mSSL, "OP_NO_ENCRYPT_THEN_MAC", ULONG2NUM(SSL_OP_NO_ENCRYPT_THEN_MAC));
#endif
-#if defined(SSL_OP_NO_COMPRESSION)
- ossl_ssl_def_const(OP_NO_COMPRESSION);
+ rb_define_const(mSSL, "OP_CIPHER_SERVER_PREFERENCE", ULONG2NUM(SSL_OP_CIPHER_SERVER_PREFERENCE));
+ rb_define_const(mSSL, "OP_TLS_ROLLBACK_BUG", ULONG2NUM(SSL_OP_TLS_ROLLBACK_BUG));
+#ifdef SSL_OP_NO_RENEGOTIATION /* OpenSSL 1.1.1 */
+ rb_define_const(mSSL, "OP_NO_RENEGOTIATION", ULONG2NUM(SSL_OP_NO_RENEGOTIATION));
#endif
- ossl_ssl_def_const(OP_PKCS1_CHECK_1);
- ossl_ssl_def_const(OP_PKCS1_CHECK_2);
- ossl_ssl_def_const(OP_NETSCAPE_CA_DN_BUG);
- ossl_ssl_def_const(OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG);
+ rb_define_const(mSSL, "OP_CRYPTOPRO_TLSEXT_BUG", ULONG2NUM(SSL_OP_CRYPTOPRO_TLSEXT_BUG));
+
+ rb_define_const(mSSL, "OP_NO_SSLv3", ULONG2NUM(SSL_OP_NO_SSLv3));
+ rb_define_const(mSSL, "OP_NO_TLSv1", ULONG2NUM(SSL_OP_NO_TLSv1));
+ rb_define_const(mSSL, "OP_NO_TLSv1_1", ULONG2NUM(SSL_OP_NO_TLSv1_1));
+ rb_define_const(mSSL, "OP_NO_TLSv1_2", ULONG2NUM(SSL_OP_NO_TLSv1_2));
+#ifdef SSL_OP_NO_TLSv1_3 /* OpenSSL 1.1.1 */
+ rb_define_const(mSSL, "OP_NO_TLSv1_3", ULONG2NUM(SSL_OP_NO_TLSv1_3));
+#endif
+
+ /* SSL_OP_* flags for DTLS */
+#if 0
+ rb_define_const(mSSL, "OP_NO_QUERY_MTU", ULONG2NUM(SSL_OP_NO_QUERY_MTU));
+ rb_define_const(mSSL, "OP_COOKIE_EXCHANGE", ULONG2NUM(SSL_OP_COOKIE_EXCHANGE));
+ rb_define_const(mSSL, "OP_CISCO_ANYCONNECT", ULONG2NUM(SSL_OP_CISCO_ANYCONNECT));
+#endif
+
+ /* Deprecated in OpenSSL 1.1.0. */
+ rb_define_const(mSSL, "OP_MICROSOFT_SESS_ID_BUG", ULONG2NUM(SSL_OP_MICROSOFT_SESS_ID_BUG));
+ /* Deprecated in OpenSSL 1.1.0. */
+ rb_define_const(mSSL, "OP_NETSCAPE_CHALLENGE_BUG", ULONG2NUM(SSL_OP_NETSCAPE_CHALLENGE_BUG));
+ /* Deprecated in OpenSSL 0.9.8q and 1.0.0c. */
+ rb_define_const(mSSL, "OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG", ULONG2NUM(SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG));
+ /* Deprecated in OpenSSL 1.0.1h and 1.0.2. */
+ rb_define_const(mSSL, "OP_SSLREF2_REUSE_CERT_TYPE_BUG", ULONG2NUM(SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG));
+ /* Deprecated in OpenSSL 1.1.0. */
+ rb_define_const(mSSL, "OP_MICROSOFT_BIG_SSLV3_BUFFER", ULONG2NUM(SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER));
+ /* Deprecated in OpenSSL 0.9.7h and 0.9.8b. */
+ rb_define_const(mSSL, "OP_MSIE_SSLV2_RSA_PADDING", ULONG2NUM(SSL_OP_MSIE_SSLV2_RSA_PADDING));
+ /* Deprecated in OpenSSL 1.1.0. */
+ rb_define_const(mSSL, "OP_SSLEAY_080_CLIENT_DH_BUG", ULONG2NUM(SSL_OP_SSLEAY_080_CLIENT_DH_BUG));
+ /* Deprecated in OpenSSL 1.1.0. */
+ rb_define_const(mSSL, "OP_TLS_D5_BUG", ULONG2NUM(SSL_OP_TLS_D5_BUG));
+ /* Deprecated in OpenSSL 1.1.0. */
+ rb_define_const(mSSL, "OP_TLS_BLOCK_PADDING_BUG", ULONG2NUM(SSL_OP_TLS_BLOCK_PADDING_BUG));
+ /* Deprecated in OpenSSL 1.1.0. */
+ rb_define_const(mSSL, "OP_SINGLE_ECDH_USE", ULONG2NUM(SSL_OP_SINGLE_ECDH_USE));
+ /* Deprecated in OpenSSL 1.1.0. */
+ rb_define_const(mSSL, "OP_SINGLE_DH_USE", ULONG2NUM(SSL_OP_SINGLE_DH_USE));
+ /* Deprecated in OpenSSL 1.0.1k and 1.0.2. */
+ rb_define_const(mSSL, "OP_EPHEMERAL_RSA", ULONG2NUM(SSL_OP_EPHEMERAL_RSA));
+ /* Deprecated in OpenSSL 1.1.0. */
+ rb_define_const(mSSL, "OP_NO_SSLv2", ULONG2NUM(SSL_OP_NO_SSLv2));
+ /* Deprecated in OpenSSL 1.0.1. */
+ rb_define_const(mSSL, "OP_PKCS1_CHECK_1", ULONG2NUM(SSL_OP_PKCS1_CHECK_1));
+ /* Deprecated in OpenSSL 1.0.1. */
+ rb_define_const(mSSL, "OP_PKCS1_CHECK_2", ULONG2NUM(SSL_OP_PKCS1_CHECK_2));
+ /* Deprecated in OpenSSL 1.1.0. */
+ rb_define_const(mSSL, "OP_NETSCAPE_CA_DN_BUG", ULONG2NUM(SSL_OP_NETSCAPE_CA_DN_BUG));
+ /* Deprecated in OpenSSL 1.1.0. */
+ rb_define_const(mSSL, "OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG", ULONG2NUM(SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG));
+
+
+ /*
+ * SSL/TLS version constants. Used by SSLContext#min_version= and
+ * #max_version=
+ */
+ /* SSL 2.0 */
+ rb_define_const(mSSL, "SSL2_VERSION", INT2NUM(SSL2_VERSION));
+ /* SSL 3.0 */
+ rb_define_const(mSSL, "SSL3_VERSION", INT2NUM(SSL3_VERSION));
+ /* TLS 1.0 */
+ rb_define_const(mSSL, "TLS1_VERSION", INT2NUM(TLS1_VERSION));
+ /* TLS 1.1 */
+ rb_define_const(mSSL, "TLS1_1_VERSION", INT2NUM(TLS1_1_VERSION));
+ /* TLS 1.2 */
+ rb_define_const(mSSL, "TLS1_2_VERSION", INT2NUM(TLS1_2_VERSION));
+#ifdef TLS1_3_VERSION /* OpenSSL 1.1.1 */
+ /* TLS 1.3 */
+ rb_define_const(mSSL, "TLS1_3_VERSION", INT2NUM(TLS1_3_VERSION));
+#endif
+
sym_exception = ID2SYM(rb_intern("exception"));
sym_wait_readable = ID2SYM(rb_intern("wait_readable"));
sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
+ id_tmp_dh_callback = rb_intern("tmp_dh_callback");
+ id_tmp_ecdh_callback = rb_intern("tmp_ecdh_callback");
+ id_npn_protocols_encoded = rb_intern("npn_protocols_encoded");
+
#define DefIVarID(name) do \
id_i_##name = rb_intern("@"#name); while (0)
diff --git a/ext/openssl/ossl_ssl.h b/ext/openssl/ossl_ssl.h
index c1a3cd6c1d..535c56097c 100644
--- a/ext/openssl/ossl_ssl.h
+++ b/ext/openssl/ossl_ssl.h
@@ -24,11 +24,6 @@
} \
} while (0)
-#define SafeGetSSLSession(obj, sess) do { \
- OSSL_Check_Kind((obj), cSSLSession); \
- GetSSLSession((obj), (sess)); \
-} while (0)
-
extern const rb_data_type_t ossl_ssl_type;
extern const rb_data_type_t ossl_ssl_session_type;
extern VALUE mSSL;
diff --git a/ext/openssl/ossl_ssl_session.c b/ext/openssl/ossl_ssl_session.c
index fb7c0fb611..5514087387 100644
--- a/ext/openssl/ossl_ssl_session.c
+++ b/ext/openssl/ossl_ssl_session.c
@@ -49,7 +49,7 @@ static VALUE ossl_ssl_session_initialize(VALUE self, VALUE arg1)
if ((ctx = SSL_get1_session(ssl)) == NULL)
ossl_raise(eSSLSession, "no session available");
} else {
- BIO *in = ossl_obj2bio(arg1);
+ BIO *in = ossl_obj2bio(&arg1);
ctx = PEM_read_bio_SSL_SESSION(in, NULL, NULL, NULL);
@@ -80,7 +80,7 @@ ossl_ssl_session_initialize_copy(VALUE self, VALUE other)
rb_check_frozen(self);
sess = RTYPEDDATA_DATA(self); /* XXX */
- SafeGetSSLSession(other, sess_other);
+ GetSSLSession(other, sess_other);
sess_new = ASN1_dup((i2d_of_void *)i2d_SSL_SESSION, (d2i_of_void *)d2i_SSL_SESSION,
(char *)sess_other);
@@ -93,43 +93,36 @@ ossl_ssl_session_initialize_copy(VALUE self, VALUE other)
return self;
}
-#if HAVE_SSL_SESSION_CMP == 0
-int SSL_SESSION_cmp(const SSL_SESSION *a,const SSL_SESSION *b)
+static int
+ossl_SSL_SESSION_cmp(const SSL_SESSION *a, const SSL_SESSION *b)
{
unsigned int a_len;
const unsigned char *a_sid = SSL_SESSION_get_id(a, &a_len);
unsigned int b_len;
const unsigned char *b_sid = SSL_SESSION_get_id(b, &b_len);
-#if !defined(HAVE_OPAQUE_OPENSSL) /* missing SSL_SESSION_get_ssl_version() ? */
- if (a->ssl_version != b->ssl_version)
+ if (SSL_SESSION_get_protocol_version(a) != SSL_SESSION_get_protocol_version(b))
return 1;
-#endif
if (a_len != b_len)
return 1;
-#if defined(_WIN32)
- return memcmp(a_sid, b_sid, a_len);
-#else
return CRYPTO_memcmp(a_sid, b_sid, a_len);
-#endif
}
-#endif
/*
* call-seq:
* session1 == session2 -> boolean
*
- * Returns true if the two Session is the same, false if not.
+ * Returns +true+ if the two Session is the same, +false+ if not.
*/
static VALUE ossl_ssl_session_eq(VALUE val1, VALUE val2)
{
SSL_SESSION *ctx1, *ctx2;
GetSSLSession(val1, ctx1);
- SafeGetSSLSession(val2, ctx2);
+ GetSSLSession(val2, ctx2);
- switch (SSL_SESSION_cmp(ctx1, ctx2)) {
+ switch (ossl_SSL_SESSION_cmp(ctx1, ctx2)) {
case 0: return Qtrue;
default: return Qfalse;
}
@@ -141,19 +134,18 @@ static VALUE ossl_ssl_session_eq(VALUE val1, VALUE val2)
*
* Returns the time at which the session was established.
*/
-static VALUE ossl_ssl_session_get_time(VALUE self)
+static VALUE
+ossl_ssl_session_get_time(VALUE self)
{
- SSL_SESSION *ctx;
- time_t t;
+ SSL_SESSION *ctx;
+ long t;
- GetSSLSession(self, ctx);
-
- t = SSL_SESSION_get_time(ctx);
+ GetSSLSession(self, ctx);
+ t = SSL_SESSION_get_time(ctx);
+ if (t == 0)
+ return Qnil;
- if (t == 0)
- return Qnil;
-
- return rb_funcall(rb_cTime, rb_intern("at"), 1, TIMET2NUM(t));
+ return rb_funcall(rb_cTime, rb_intern("at"), 1, LONG2NUM(t));
}
/*
@@ -164,16 +156,16 @@ static VALUE ossl_ssl_session_get_time(VALUE self)
* established time.
*
*/
-static VALUE ossl_ssl_session_get_timeout(VALUE self)
+static VALUE
+ossl_ssl_session_get_timeout(VALUE self)
{
- SSL_SESSION *ctx;
- time_t t;
-
- GetSSLSession(self, ctx);
+ SSL_SESSION *ctx;
+ long t;
- t = SSL_SESSION_get_timeout(ctx);
+ GetSSLSession(self, ctx);
+ t = SSL_SESSION_get_timeout(ctx);
- return TIMET2NUM(t);
+ return LONG2NUM(t);
}
/*
@@ -270,9 +262,6 @@ static VALUE ossl_ssl_session_to_pem(VALUE self)
{
SSL_SESSION *ctx;
BIO *out;
- BUF_MEM *buf;
- VALUE str;
- int i;
GetSSLSession(self, ctx);
@@ -280,16 +269,13 @@ static VALUE ossl_ssl_session_to_pem(VALUE self)
ossl_raise(eSSLSession, "BIO_s_mem()");
}
- if (!(i=PEM_write_bio_SSL_SESSION(out, ctx))) {
+ if (!PEM_write_bio_SSL_SESSION(out, ctx)) {
BIO_free(out);
ossl_raise(eSSLSession, "SSL_SESSION_print()");
}
- BIO_get_mem_ptr(out, &buf);
- str = rb_str_new(buf->data, buf->length);
- BIO_free(out);
- return str;
+ return ossl_membio2str(out);
}
@@ -303,8 +289,6 @@ static VALUE ossl_ssl_session_to_text(VALUE self)
{
SSL_SESSION *ctx;
BIO *out;
- BUF_MEM *buf;
- VALUE str;
GetSSLSession(self, ctx);
@@ -317,11 +301,7 @@ static VALUE ossl_ssl_session_to_text(VALUE self)
ossl_raise(eSSLSession, "SSL_SESSION_print()");
}
- BIO_get_mem_ptr(out, &buf);
- str = rb_str_new(buf->data, buf->length);
- BIO_free(out);
-
- return str;
+ return ossl_membio2str(out);
}
@@ -337,7 +317,7 @@ void Init_ossl_ssl_session(void)
rb_define_alloc_func(cSSLSession, ossl_ssl_session_alloc);
rb_define_method(cSSLSession, "initialize", ossl_ssl_session_initialize, 1);
- rb_define_copy_func(cSSLSession, ossl_ssl_session_initialize_copy);
+ rb_define_method(cSSLSession, "initialize_copy", ossl_ssl_session_initialize_copy, 1);
rb_define_method(cSSLSession, "==", ossl_ssl_session_eq, 1);
diff --git a/ext/openssl/ossl_version.h b/ext/openssl/ossl_version.h
index 1744c1cac4..c162f8c2a8 100644
--- a/ext/openssl/ossl_version.h
+++ b/ext/openssl/ossl_version.h
@@ -10,6 +10,6 @@
#if !defined(_OSSL_VERSION_H_)
#define _OSSL_VERSION_H_
-#define OSSL_VERSION "2.0.0"
+#define OSSL_VERSION "2.1.2"
#endif /* _OSSL_VERSION_H_ */
diff --git a/ext/openssl/ossl_x509.c b/ext/openssl/ossl_x509.c
index 19ec274ae1..8a061b0687 100644
--- a/ext/openssl/ossl_x509.c
+++ b/ext/openssl/ossl_x509.c
@@ -20,15 +20,10 @@ ossl_x509_time_adjust(ASN1_TIME *s, VALUE time)
{
time_t sec;
-#if defined(HAVE_ASN1_TIME_ADJ)
int off_days;
ossl_time_split(time, &sec, &off_days);
return X509_time_adj_ex(s, off_days, 0, &sec);
-#else
- sec = time_to_time_t(time);
- return X509_time_adj(s, 0, &sec);
-#endif
}
void
@@ -112,21 +107,15 @@ Init_ossl_x509(void)
DefX509Const(V_FLAG_INHIBIT_MAP);
/* Set by Store#flags= and StoreContext#flags=. */
DefX509Const(V_FLAG_NOTIFY_POLICY);
-#if defined(X509_V_FLAG_EXTENDED_CRL_SUPPORT)
/* Set by Store#flags= and StoreContext#flags=. Enables some additional
* features including support for indirect signed CRLs. */
DefX509Const(V_FLAG_EXTENDED_CRL_SUPPORT);
-#endif
-#if defined(X509_V_FLAG_USE_DELTAS)
/* Set by Store#flags= and StoreContext#flags=. Uses delta CRLs. If not
* specified, deltas are ignored. */
DefX509Const(V_FLAG_USE_DELTAS);
-#endif
-#if defined(X509_V_FLAG_CHECK_SS_SIGNATURE)
/* Set by Store#flags= and StoreContext#flags=. Enables checking of the
* signature of the root self-signed CA. */
DefX509Const(V_FLAG_CHECK_SS_SIGNATURE);
-#endif
#if defined(X509_V_FLAG_TRUSTED_FIRST)
/* Set by Store#flags= and StoreContext#flags=. When constructing a
* certificate chain, search the Store first for the issuer certificate.
@@ -161,10 +150,8 @@ Init_ossl_x509(void)
DefX509Const(PURPOSE_ANY);
/* Set by Store#purpose=. OCSP helper. */
DefX509Const(PURPOSE_OCSP_HELPER);
-#if defined(X509_PURPOSE_TIMESTAMP_SIGN)
/* Set by Store#purpose=. Time stamps signer. */
DefX509Const(PURPOSE_TIMESTAMP_SIGN);
-#endif
DefX509Const(TRUST_COMPAT);
DefX509Const(TRUST_SSL_CLIENT);
@@ -173,9 +160,7 @@ Init_ossl_x509(void)
DefX509Const(TRUST_OBJECT_SIGN);
DefX509Const(TRUST_OCSP_SIGN);
DefX509Const(TRUST_OCSP_REQUEST);
-#if defined(X509_TRUST_TSA)
DefX509Const(TRUST_TSA);
-#endif
DefX509Default(CERT_AREA, cert_area);
DefX509Default(CERT_DIR, cert_dir);
diff --git a/ext/openssl/ossl_x509.h b/ext/openssl/ossl_x509.h
index c26da73897..4fadfa6b82 100644
--- a/ext/openssl/ossl_x509.h
+++ b/ext/openssl/ossl_x509.h
@@ -41,7 +41,6 @@ extern VALUE cX509Cert;
extern VALUE eX509CertError;
VALUE ossl_x509_new(X509 *);
-VALUE ossl_x509_new_from_file(VALUE);
X509 *GetX509CertPtr(VALUE);
X509 *DupX509CertPtr(VALUE);
void Init_ossl_x509cert(void);
@@ -54,7 +53,6 @@ extern VALUE eX509CRLError;
VALUE ossl_x509crl_new(X509_CRL *);
X509_CRL *GetX509CRLPtr(VALUE);
-X509_CRL *DupX509CRLPtr(VALUE);
void Init_ossl_x509crl(void);
/*
@@ -84,9 +82,7 @@ void Init_ossl_x509name(void);
extern VALUE cX509Req;
extern VALUE eX509ReqError;
-VALUE ossl_x509req_new(X509_REQ *);
X509_REQ *GetX509ReqPtr(VALUE);
-X509_REQ *DupX509ReqPtr(VALUE);
void Init_ossl_x509req(void);
/*
@@ -106,14 +102,14 @@ extern VALUE cX509Store;
extern VALUE cX509StoreContext;
extern VALUE eX509StoreError;
-VALUE ossl_x509store_new(X509_STORE *);
X509_STORE *GetX509StorePtr(VALUE);
-X509_STORE *DupX509StorePtr(VALUE);
-
-VALUE ossl_x509stctx_new(X509_STORE_CTX *);
-VALUE ossl_x509stctx_clear_ptr(VALUE);
-X509_STORE_CTX *GetX509StCtxtPtr(VALUE);
void Init_ossl_x509store(void);
+/*
+ * Calls the verify callback Proc (the first parameter) with given pre-verify
+ * result and the X509_STORE_CTX.
+ */
+int ossl_verify_cb_call(VALUE, int, X509_STORE_CTX *);
+
#endif /* _OSSL_X509_H_ */
diff --git a/ext/openssl/ossl_x509attr.c b/ext/openssl/ossl_x509attr.c
index ae0b347b5f..60846cfe9d 100644
--- a/ext/openssl/ossl_x509attr.c
+++ b/ext/openssl/ossl_x509attr.c
@@ -23,10 +23,6 @@
ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \
} \
} while (0)
-#define SafeGetX509Attr(obj, attr) do { \
- OSSL_Check_Kind((obj), cX509Attr); \
- GetX509Attr((obj), (attr)); \
-} while (0)
/*
* Classes
@@ -76,7 +72,7 @@ GetX509AttrPtr(VALUE obj)
{
X509_ATTRIBUTE *attr;
- SafeGetX509Attr(obj, attr);
+ GetX509Attr(obj, attr);
return attr;
}
@@ -134,7 +130,7 @@ ossl_x509attr_initialize_copy(VALUE self, VALUE other)
rb_check_frozen(self);
GetX509Attr(self, attr);
- SafeGetX509Attr(other, attr_other);
+ GetX509Attr(other, attr_other);
attr_new = X509_ATTRIBUTE_dup(attr_other);
if (!attr_new)
@@ -319,7 +315,7 @@ Init_ossl_x509attr(void)
cX509Attr = rb_define_class_under(mX509, "Attribute", rb_cObject);
rb_define_alloc_func(cX509Attr, ossl_x509attr_alloc);
rb_define_method(cX509Attr, "initialize", ossl_x509attr_initialize, -1);
- rb_define_copy_func(cX509Attr, ossl_x509attr_initialize_copy);
+ rb_define_method(cX509Attr, "initialize_copy", ossl_x509attr_initialize_copy, 1);
rb_define_method(cX509Attr, "oid=", ossl_x509attr_set_oid, 1);
rb_define_method(cX509Attr, "oid", ossl_x509attr_get_oid, 0);
rb_define_method(cX509Attr, "value=", ossl_x509attr_set_value, 1);
diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c
index ad1126d465..40542c4a78 100644
--- a/ext/openssl/ossl_x509cert.c
+++ b/ext/openssl/ossl_x509cert.c
@@ -23,10 +23,6 @@
ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \
} \
} while (0)
-#define SafeGetX509(obj, x509) do { \
- OSSL_Check_Kind((obj), cX509Cert); \
- GetX509((obj), (x509)); \
-} while (0)
/*
* Classes
@@ -71,46 +67,12 @@ ossl_x509_new(X509 *x509)
return obj;
}
-VALUE
-ossl_x509_new_from_file(VALUE filename)
-{
- X509 *x509;
- FILE *fp;
- VALUE obj;
-
- rb_check_safe_obj(filename);
- obj = NewX509(cX509Cert);
- if (!(fp = fopen(StringValueCStr(filename), "r"))) {
- ossl_raise(eX509CertError, "%s", strerror(errno));
- }
- rb_fd_fix_cloexec(fileno(fp));
- x509 = PEM_read_X509(fp, NULL, NULL, NULL);
- /*
- * prepare for DER...
-#if !defined(OPENSSL_NO_FP_API)
- if (!x509) {
- (void)ERR_get_error();
- rewind(fp);
-
- x509 = d2i_X509_fp(fp, NULL);
- }
-#endif
- */
- fclose(fp);
- if (!x509) {
- ossl_raise(eX509CertError, NULL);
- }
- SetX509(obj, x509);
-
- return obj;
-}
-
X509 *
GetX509CertPtr(VALUE obj)
{
X509 *x509;
- SafeGetX509(obj, x509);
+ GetX509(obj, x509);
return x509;
}
@@ -120,7 +82,7 @@ DupX509CertPtr(VALUE obj)
{
X509 *x509;
- SafeGetX509(obj, x509);
+ GetX509(obj, x509);
X509_up_ref(x509);
@@ -161,7 +123,7 @@ ossl_x509_initialize(int argc, VALUE *argv, VALUE self)
return self;
}
arg = ossl_to_der_if_possible(arg);
- in = ossl_obj2bio(arg);
+ in = ossl_obj2bio(&arg);
x509 = PEM_read_bio_X509(in, &x, NULL, NULL);
DATA_PTR(self) = x;
if (!x509) {
@@ -184,7 +146,7 @@ ossl_x509_copy(VALUE self, VALUE other)
if (self == other) return self;
GetX509(self, a);
- SafeGetX509(other, b);
+ GetX509(other, b);
x509 = X509_dup(b);
if (!x509) ossl_raise(eX509CertError, NULL);
@@ -478,7 +440,7 @@ ossl_x509_set_not_before(VALUE self, VALUE time)
GetX509(self, x509);
asn1time = ossl_x509_time_adjust(NULL, time);
- if (!X509_set_notBefore(x509, asn1time)) {
+ if (!X509_set1_notBefore(x509, asn1time)) {
ASN1_TIME_free(asn1time);
ossl_raise(eX509CertError, "X509_set_notBefore");
}
@@ -517,7 +479,7 @@ ossl_x509_set_not_after(VALUE self, VALUE time)
GetX509(self, x509);
asn1time = ossl_x509_time_adjust(NULL, time);
- if (!X509_set_notAfter(x509, asn1time)) {
+ if (!X509_set1_notAfter(x509, asn1time)) {
ASN1_TIME_free(asn1time);
ossl_raise(eX509CertError, "X509_set_notAfter");
}
@@ -546,18 +508,19 @@ ossl_x509_get_public_key(VALUE self)
/*
* call-seq:
- * cert.public_key = key => key
+ * cert.public_key = key
*/
static VALUE
ossl_x509_set_public_key(VALUE self, VALUE key)
{
X509 *x509;
+ EVP_PKEY *pkey;
GetX509(self, x509);
- if (!X509_set_pubkey(x509, GetPKeyPtr(key))) { /* DUPs pkey */
- ossl_raise(eX509CertError, NULL);
- }
-
+ pkey = GetPKeyPtr(key);
+ ossl_pkey_check_public_key(pkey);
+ if (!X509_set_pubkey(x509, pkey))
+ ossl_raise(eX509CertError, "X509_set_pubkey");
return key;
}
@@ -573,7 +536,7 @@ ossl_x509_sign(VALUE self, VALUE key, VALUE digest)
const EVP_MD *md;
pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
- md = GetDigestPtr(digest);
+ md = ossl_evp_get_digestbyname(digest);
GetX509(self, x509);
if (!X509_sign(x509, pkey, md)) {
ossl_raise(eX509CertError, NULL);
@@ -586,7 +549,8 @@ ossl_x509_sign(VALUE self, VALUE key, VALUE digest)
* call-seq:
* cert.verify(key) => true | false
*
- * Checks that cert signature is made with PRIVversion of this PUBLIC 'key'
+ * Verifies the signature of the certificate, with the public key _key_. _key_
+ * must be an instance of OpenSSL::PKey.
*/
static VALUE
ossl_x509_verify(VALUE self, VALUE key)
@@ -594,9 +558,9 @@ ossl_x509_verify(VALUE self, VALUE key)
X509 *x509;
EVP_PKEY *pkey;
- pkey = GetPKeyPtr(key); /* NO NEED TO DUP */
GetX509(self, x509);
-
+ pkey = GetPKeyPtr(key);
+ ossl_pkey_check_public_key(pkey);
switch (X509_verify(x509, pkey)) {
case 1:
return Qtrue;
@@ -610,9 +574,10 @@ ossl_x509_verify(VALUE self, VALUE key)
/*
* call-seq:
- * cert.check_private_key(key)
+ * cert.check_private_key(key) -> true | false
*
- * Checks if 'key' is PRIV key for this cert
+ * Returns +true+ if _key_ is the corresponding private key to the Subject
+ * Public Key Information, +false+ otherwise.
*/
static VALUE
ossl_x509_check_private_key(VALUE self, VALUE key)
@@ -624,7 +589,7 @@ ossl_x509_check_private_key(VALUE self, VALUE key)
pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
GetX509(self, x509);
if (!X509_check_private_key(x509, pkey)) {
- OSSL_Warning("Check private key:%s", OSSL_ErrMsg());
+ ossl_clear_error();
return Qfalse;
}
@@ -720,6 +685,26 @@ ossl_x509_inspect(VALUE self)
}
/*
+ * call-seq:
+ * cert1 == cert2 -> true | false
+ *
+ * Compares the two certificates. Note that this takes into account all fields,
+ * not just the issuer name and the serial number.
+ */
+static VALUE
+ossl_x509_eq(VALUE self, VALUE other)
+{
+ X509 *a, *b;
+
+ GetX509(self, a);
+ if (!rb_obj_is_kind_of(other, cX509Cert))
+ return Qfalse;
+ GetX509(other, b);
+
+ return !X509_cmp(a, b) ? Qtrue : Qfalse;
+}
+
+/*
* INIT
*/
void
@@ -829,7 +814,7 @@ Init_ossl_x509cert(void)
rb_define_alloc_func(cX509Cert, ossl_x509_alloc);
rb_define_method(cX509Cert, "initialize", ossl_x509_initialize, -1);
- rb_define_copy_func(cX509Cert, ossl_x509_copy);
+ rb_define_method(cX509Cert, "initialize_copy", ossl_x509_copy, 1);
rb_define_method(cX509Cert, "to_der", ossl_x509_to_der, 0);
rb_define_method(cX509Cert, "to_pem", ossl_x509_to_pem, 0);
@@ -857,4 +842,5 @@ Init_ossl_x509cert(void)
rb_define_method(cX509Cert, "extensions=", ossl_x509_set_extensions, 1);
rb_define_method(cX509Cert, "add_extension", ossl_x509_add_extension, 1);
rb_define_method(cX509Cert, "inspect", ossl_x509_inspect, 0);
+ rb_define_method(cX509Cert, "==", ossl_x509_eq, 1);
}
diff --git a/ext/openssl/ossl_x509crl.c b/ext/openssl/ossl_x509crl.c
index 0ff5d2f8ea..b0badf45c4 100644
--- a/ext/openssl/ossl_x509crl.c
+++ b/ext/openssl/ossl_x509crl.c
@@ -23,10 +23,6 @@
ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \
} \
} while (0)
-#define SafeGetX509CRL(obj, crl) do { \
- OSSL_Check_Kind((obj), cX509CRL); \
- GetX509CRL((obj), (crl)); \
-} while (0)
/*
* Classes
@@ -56,18 +52,7 @@ GetX509CRLPtr(VALUE obj)
{
X509_CRL *crl;
- SafeGetX509CRL(obj, crl);
-
- return crl;
-}
-
-X509_CRL *
-DupX509CRLPtr(VALUE obj)
-{
- X509_CRL *crl;
-
- SafeGetX509CRL(obj, crl);
- X509_CRL_up_ref(crl);
+ GetX509CRL(obj, crl);
return crl;
}
@@ -115,7 +100,7 @@ ossl_x509crl_initialize(int argc, VALUE *argv, VALUE self)
return self;
}
arg = ossl_to_der_if_possible(arg);
- in = ossl_obj2bio(arg);
+ in = ossl_obj2bio(&arg);
crl = PEM_read_bio_X509_CRL(in, &x, NULL, NULL);
DATA_PTR(self) = x;
if (!crl) {
@@ -137,7 +122,7 @@ ossl_x509crl_copy(VALUE self, VALUE other)
rb_check_frozen(self);
if (self == other) return self;
GetX509CRL(self, a);
- SafeGetX509CRL(other, b);
+ GetX509CRL(other, b);
if (!(crl = X509_CRL_dup(b))) {
ossl_raise(eX509CRLError, NULL);
}
@@ -182,8 +167,6 @@ ossl_x509crl_get_signature_algorithm(VALUE self)
X509_CRL *crl;
const X509_ALGOR *alg;
BIO *out;
- BUF_MEM *buf;
- VALUE str;
GetX509CRL(self, crl);
if (!(out = BIO_new(BIO_s_mem()))) {
@@ -194,10 +177,8 @@ ossl_x509crl_get_signature_algorithm(VALUE self)
BIO_free(out);
ossl_raise(eX509CRLError, NULL);
}
- BIO_get_mem_ptr(out, &buf);
- str = rb_str_new(buf->data, buf->length);
- BIO_free(out);
- return str;
+
+ return ossl_membio2str(out);
}
static VALUE
@@ -227,10 +208,14 @@ static VALUE
ossl_x509crl_get_last_update(VALUE self)
{
X509_CRL *crl;
+ const ASN1_TIME *time;
GetX509CRL(self, crl);
+ time = X509_CRL_get0_lastUpdate(crl);
+ if (!time)
+ return Qnil;
- return asn1time_to_time(X509_CRL_get0_lastUpdate(crl));
+ return asn1time_to_time(time);
}
static VALUE
@@ -241,7 +226,7 @@ ossl_x509crl_set_last_update(VALUE self, VALUE time)
GetX509CRL(self, crl);
asn1time = ossl_x509_time_adjust(NULL, time);
- if (!X509_CRL_set_lastUpdate(crl, asn1time)) {
+ if (!X509_CRL_set1_lastUpdate(crl, asn1time)) {
ASN1_TIME_free(asn1time);
ossl_raise(eX509CRLError, "X509_CRL_set_lastUpdate");
}
@@ -254,10 +239,14 @@ static VALUE
ossl_x509crl_get_next_update(VALUE self)
{
X509_CRL *crl;
+ const ASN1_TIME *time;
GetX509CRL(self, crl);
+ time = X509_CRL_get0_nextUpdate(crl);
+ if (!time)
+ return Qnil;
- return asn1time_to_time(X509_CRL_get0_nextUpdate(crl));
+ return asn1time_to_time(time);
}
static VALUE
@@ -268,7 +257,7 @@ ossl_x509crl_set_next_update(VALUE self, VALUE time)
GetX509CRL(self, crl);
asn1time = ossl_x509_time_adjust(NULL, time);
- if (!X509_CRL_set_nextUpdate(crl, asn1time)) {
+ if (!X509_CRL_set1_nextUpdate(crl, asn1time)) {
ASN1_TIME_free(asn1time);
ossl_raise(eX509CRLError, "X509_CRL_set_nextUpdate");
}
@@ -358,7 +347,7 @@ ossl_x509crl_sign(VALUE self, VALUE key, VALUE digest)
GetX509CRL(self, crl);
pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
- md = GetDigestPtr(digest);
+ md = ossl_evp_get_digestbyname(digest);
if (!X509_CRL_sign(crl, pkey, md)) {
ossl_raise(eX509CRLError, NULL);
}
@@ -370,9 +359,12 @@ static VALUE
ossl_x509crl_verify(VALUE self, VALUE key)
{
X509_CRL *crl;
+ EVP_PKEY *pkey;
GetX509CRL(self, crl);
- switch (X509_CRL_verify(crl, GetPKeyPtr(key))) {
+ pkey = GetPKeyPtr(key);
+ ossl_pkey_check_public_key(pkey);
+ switch (X509_CRL_verify(crl, pkey)) {
case 1:
return Qtrue;
case 0:
@@ -388,8 +380,6 @@ ossl_x509crl_to_der(VALUE self)
{
X509_CRL *crl;
BIO *out;
- BUF_MEM *buf;
- VALUE str;
GetX509CRL(self, crl);
if (!(out = BIO_new(BIO_s_mem()))) {
@@ -399,11 +389,8 @@ ossl_x509crl_to_der(VALUE self)
BIO_free(out);
ossl_raise(eX509CRLError, NULL);
}
- BIO_get_mem_ptr(out, &buf);
- str = rb_str_new(buf->data, buf->length);
- BIO_free(out);
- return str;
+ return ossl_membio2str(out);
}
static VALUE
@@ -411,8 +398,6 @@ ossl_x509crl_to_pem(VALUE self)
{
X509_CRL *crl;
BIO *out;
- BUF_MEM *buf;
- VALUE str;
GetX509CRL(self, crl);
if (!(out = BIO_new(BIO_s_mem()))) {
@@ -422,11 +407,8 @@ ossl_x509crl_to_pem(VALUE self)
BIO_free(out);
ossl_raise(eX509CRLError, NULL);
}
- BIO_get_mem_ptr(out, &buf);
- str = rb_str_new(buf->data, buf->length);
- BIO_free(out);
- return str;
+ return ossl_membio2str(out);
}
static VALUE
@@ -434,8 +416,6 @@ ossl_x509crl_to_text(VALUE self)
{
X509_CRL *crl;
BIO *out;
- BUF_MEM *buf;
- VALUE str;
GetX509CRL(self, crl);
if (!(out = BIO_new(BIO_s_mem()))) {
@@ -445,11 +425,8 @@ ossl_x509crl_to_text(VALUE self)
BIO_free(out);
ossl_raise(eX509CRLError, NULL);
}
- BIO_get_mem_ptr(out, &buf);
- str = rb_str_new(buf->data, buf->length);
- BIO_free(out);
- return str;
+ return ossl_membio2str(out);
}
/*
@@ -539,7 +516,7 @@ Init_ossl_x509crl(void)
rb_define_alloc_func(cX509CRL, ossl_x509crl_alloc);
rb_define_method(cX509CRL, "initialize", ossl_x509crl_initialize, -1);
- rb_define_copy_func(cX509CRL, ossl_x509crl_copy);
+ rb_define_method(cX509CRL, "initialize_copy", ossl_x509crl_copy, 1);
rb_define_method(cX509CRL, "version", ossl_x509crl_get_version, 0);
rb_define_method(cX509CRL, "version=", ossl_x509crl_set_version, 1);
diff --git a/ext/openssl/ossl_x509ext.c b/ext/openssl/ossl_x509ext.c
index b92b0786b2..30ec09d7a3 100644
--- a/ext/openssl/ossl_x509ext.c
+++ b/ext/openssl/ossl_x509ext.c
@@ -23,10 +23,6 @@
ossl_raise(rb_eRuntimeError, "EXT wasn't initialized!"); \
} \
} while (0)
-#define SafeGetX509Ext(obj, ext) do { \
- OSSL_Check_Kind((obj), cX509Ext); \
- GetX509Ext((obj), (ext)); \
-} while (0)
#define MakeX509ExtFactory(klass, obj, ctx) do { \
(obj) = TypedData_Wrap_Struct((klass), &ossl_x509extfactory_type, 0); \
if (!((ctx) = OPENSSL_malloc(sizeof(X509V3_CTX)))) \
@@ -90,7 +86,7 @@ GetX509ExtPtr(VALUE obj)
{
X509_EXTENSION *ext;
- SafeGetX509Ext(obj, ext);
+ GetX509Ext(obj, ext);
return ext;
}
@@ -263,15 +259,15 @@ ossl_x509ext_alloc(VALUE klass)
/*
* call-seq:
- * OpenSSL::X509::Extension.new asn1
- * OpenSSL::X509::Extension.new name, value
- * OpenSSL::X509::Extension.new name, value, critical
+ * OpenSSL::X509::Extension.new(der)
+ * OpenSSL::X509::Extension.new(oid, value)
+ * OpenSSL::X509::Extension.new(oid, value, critical)
*
* Creates an X509 extension.
*
- * The extension may be created from +asn1+ data or from an extension +name+
- * and +value+. The +name+ may be either an OID or an extension name. If
- * +critical+ is true the extension is marked critical.
+ * The extension may be created from _der_ data or from an extension _oid_
+ * and _value_. The _oid_ may be either an OID or an extension name. If
+ * _critical_ is +true+ the extension is marked critical.
*/
static VALUE
ossl_x509ext_initialize(int argc, VALUE *argv, VALUE self)
@@ -305,7 +301,7 @@ ossl_x509ext_initialize_copy(VALUE self, VALUE other)
rb_check_frozen(self);
GetX509Ext(self, ext);
- SafeGetX509Ext(other, ext_other);
+ GetX509Ext(other, ext_other);
ext_new = X509_EXTENSION_dup(ext_other);
if (!ext_new)
@@ -441,6 +437,7 @@ ossl_x509ext_to_der(VALUE obj)
void
Init_ossl_x509ext(void)
{
+#undef rb_intern
#if 0
mOSSL = rb_define_module("OpenSSL");
eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
@@ -469,7 +466,7 @@ Init_ossl_x509ext(void)
cX509Ext = rb_define_class_under(mX509, "Extension", rb_cObject);
rb_define_alloc_func(cX509Ext, ossl_x509ext_alloc);
rb_define_method(cX509Ext, "initialize", ossl_x509ext_initialize, -1);
- rb_define_copy_func(cX509Ext, ossl_x509ext_initialize_copy);
+ rb_define_method(cX509Ext, "initialize_copy", ossl_x509ext_initialize_copy, 1);
rb_define_method(cX509Ext, "oid=", ossl_x509ext_set_oid, 1);
rb_define_method(cX509Ext, "value=", ossl_x509ext_set_value, 1);
rb_define_method(cX509Ext, "critical=", ossl_x509ext_set_critical, 1);
diff --git a/ext/openssl/ossl_x509name.c b/ext/openssl/ossl_x509name.c
index abbdc3b11e..1ea8400dbb 100644
--- a/ext/openssl/ossl_x509name.c
+++ b/ext/openssl/ossl_x509name.c
@@ -23,10 +23,6 @@
ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \
} \
} while (0)
-#define SafeGetX509Name(obj, name) do { \
- OSSL_Check_Kind((obj), cX509Name); \
- GetX509Name((obj), (name)); \
-} while (0)
#define OBJECT_TYPE_TEMPLATE \
rb_const_get(cX509Name, rb_intern("OBJECT_TYPE_TEMPLATE"))
@@ -81,7 +77,7 @@ GetX509NamePtr(VALUE obj)
{
X509_NAME *name;
- SafeGetX509Name(obj, name);
+ GetX509Name(obj, name);
return name;
}
@@ -135,15 +131,15 @@ ossl_x509name_init_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
*
* Creates a new Name.
*
- * A name may be created from a DER encoded string +der+, an Array
- * representing a +distinguished_name+ or a +distinguished_name+ along with a
- * +template+.
+ * A name may be created from a DER encoded string _der_, an Array
+ * representing a _distinguished_name_ or a _distinguished_name_ along with a
+ * _template_.
*
* name = OpenSSL::X509::Name.new [['CN', 'nobody'], ['DC', 'example']]
*
* name = OpenSSL::X509::Name.new name.to_der
*
- * See add_entry for a description of the +distinguished_name+ Array's
+ * See add_entry for a description of the _distinguished_name_ Array's
* contents
*/
static VALUE
@@ -188,7 +184,7 @@ ossl_x509name_initialize_copy(VALUE self, VALUE other)
rb_check_frozen(self);
GetX509Name(self, name);
- SafeGetX509Name(other, name_other);
+ GetX509Name(other, name_other);
name_new = X509_NAME_dup(name_other);
if (!name_new)
@@ -202,9 +198,9 @@ ossl_x509name_initialize_copy(VALUE self, VALUE other)
/*
* call-seq:
- * name.add_entry(oid, value [, type]) => self
+ * name.add_entry(oid, value [, type], loc: -1, set: 0) => self
*
- * Adds a new entry with the given +oid+ and +value+ to this name. The +oid+
+ * Adds a new entry with the given _oid_ and _value_ to this name. The _oid_
* is an object identifier defined in ASN.1. Some common OIDs are:
*
* C:: Country Name
@@ -213,24 +209,39 @@ ossl_x509name_initialize_copy(VALUE self, VALUE other)
* O:: Organization Name
* OU:: Organizational Unit Name
* ST:: State or Province Name
+ *
+ * The optional keyword parameters _loc_ and _set_ specify where to insert the
+ * new attribute. Refer to the manpage of X509_NAME_add_entry(3) for details.
+ * _loc_ defaults to -1 and _set_ defaults to 0. This appends a single-valued
+ * RDN to the end.
*/
static
VALUE ossl_x509name_add_entry(int argc, VALUE *argv, VALUE self)
{
X509_NAME *name;
- VALUE oid, value, type;
+ VALUE oid, value, type, opts, kwargs[2];
+ static ID kwargs_ids[2];
const char *oid_name;
+ int loc = -1, set = 0;
- rb_scan_args(argc, argv, "21", &oid, &value, &type);
+ if (!kwargs_ids[0]) {
+ kwargs_ids[0] = rb_intern_const("loc");
+ kwargs_ids[1] = rb_intern_const("set");
+ }
+ rb_scan_args(argc, argv, "21:", &oid, &value, &type, &opts);
+ rb_get_kwargs(opts, kwargs_ids, 0, 2, kwargs);
oid_name = StringValueCStr(oid);
StringValue(value);
if(NIL_P(type)) type = rb_aref(OBJECT_TYPE_TEMPLATE, oid);
+ if (kwargs[0] != Qundef)
+ loc = NUM2INT(kwargs[0]);
+ if (kwargs[1] != Qundef)
+ set = NUM2INT(kwargs[1]);
GetX509Name(self, name);
if (!X509_NAME_add_entry_by_txt(name, oid_name, NUM2INT(type),
- (const unsigned char *)RSTRING_PTR(value), RSTRING_LENINT(value), -1, 0)) {
- ossl_raise(eX509NameError, NULL);
- }
-
+ (unsigned char *)RSTRING_PTR(value),
+ RSTRING_LENINT(value), loc, set))
+ ossl_raise(eX509NameError, "X509_NAME_add_entry_by_txt");
return self;
}
@@ -239,52 +250,83 @@ ossl_x509name_to_s_old(VALUE self)
{
X509_NAME *name;
char *buf;
- VALUE str;
GetX509Name(self, name);
buf = X509_NAME_oneline(name, NULL, 0);
- str = rb_str_new2(buf);
- OPENSSL_free(buf);
+ if (!buf)
+ ossl_raise(eX509NameError, "X509_NAME_oneline");
+ return ossl_buf2str(buf, rb_long2int(strlen(buf)));
+}
- return str;
+static VALUE
+x509name_print(VALUE self, unsigned long iflag)
+{
+ X509_NAME *name;
+ BIO *out;
+ int ret;
+
+ GetX509Name(self, name);
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ ossl_raise(eX509NameError, NULL);
+ ret = X509_NAME_print_ex(out, name, 0, iflag);
+ if (ret < 0 || (iflag == XN_FLAG_COMPAT && ret == 0)) {
+ BIO_free(out);
+ ossl_raise(eX509NameError, "X509_NAME_print_ex");
+ }
+ return ossl_membio2str(out);
}
/*
* call-seq:
- * name.to_s => string
- * name.to_s(flags) => string
+ * name.to_s -> string
+ * name.to_s(format) -> string
*
- * Returns this name as a Distinguished Name string. +flags+ may be one of:
+ * Returns a String representation of the Distinguished Name. _format_ is
+ * one of:
*
* * OpenSSL::X509::Name::COMPAT
* * OpenSSL::X509::Name::RFC2253
* * OpenSSL::X509::Name::ONELINE
* * OpenSSL::X509::Name::MULTILINE
+ *
+ * If _format_ is omitted, the largely broken and traditional OpenSSL format
+ * is used.
*/
static VALUE
ossl_x509name_to_s(int argc, VALUE *argv, VALUE self)
{
- X509_NAME *name;
- VALUE flag, str;
- BIO *out;
- unsigned long iflag;
-
- rb_scan_args(argc, argv, "01", &flag);
- if (NIL_P(flag))
+ rb_check_arity(argc, 0, 1);
+ /* name.to_s(nil) was allowed */
+ if (!argc || NIL_P(argv[0]))
return ossl_x509name_to_s_old(self);
- else iflag = NUM2ULONG(flag);
- if (!(out = BIO_new(BIO_s_mem())))
- ossl_raise(eX509NameError, NULL);
- GetX509Name(self, name);
- if (!X509_NAME_print_ex(out, name, 0, iflag)){
- BIO_free(out);
- ossl_raise(eX509NameError, NULL);
- }
- str = ossl_membio2str(out);
+ else
+ return x509name_print(self, NUM2ULONG(argv[0]));
+}
+/*
+ * call-seq:
+ * name.to_utf8 -> string
+ *
+ * Returns an UTF-8 representation of the distinguished name, as specified
+ * in {RFC 2253}[https://www.ietf.org/rfc/rfc2253.txt].
+ */
+static VALUE
+ossl_x509name_to_utf8(VALUE self)
+{
+ VALUE str = x509name_print(self, XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
+ rb_enc_associate_index(str, rb_utf8_encindex());
return str;
}
+/* :nodoc: */
+static VALUE
+ossl_x509name_inspect(VALUE self)
+{
+ return rb_enc_sprintf(rb_utf8_encoding(), "#<%"PRIsVALUE" %"PRIsVALUE">",
+ rb_obj_class(self), ossl_x509name_to_utf8(self));
+}
+
/*
* call-seq:
* name.to_a => [[name, data, type], ...]
@@ -338,18 +380,18 @@ ossl_x509name_cmp0(VALUE self, VALUE other)
X509_NAME *name1, *name2;
GetX509Name(self, name1);
- SafeGetX509Name(other, name2);
+ GetX509Name(other, name2);
return X509_NAME_cmp(name1, name2);
}
/*
* call-seq:
- * name.cmp other => integer
- * name.<=> other => integer
+ * name.cmp(other) -> -1 | 0 | 1
+ * name <=> other -> -1 | 0 | 1
*
- * Compares this Name with +other+ and returns 0 if they are the same and -1 or
- * +1 if they are greater or less than each other respectively.
+ * Compares this Name with _other_ and returns +0+ if they are the same and +-1+
+ * or ++1+ if they are greater or less than each other respectively.
*/
static VALUE
ossl_x509name_cmp(VALUE self, VALUE other)
@@ -358,26 +400,24 @@ ossl_x509name_cmp(VALUE self, VALUE other)
result = ossl_x509name_cmp0(self, other);
if (result < 0) return INT2FIX(-1);
- if (result > 1) return INT2FIX(1);
+ if (result > 0) return INT2FIX(1);
return INT2FIX(0);
}
/*
* call-seq:
- * name.eql? other => boolean
+ * name.eql?(other) -> true | false
*
- * Returns true if +name+ and +other+ refer to the same hash key.
+ * Returns true if _name_ and _other_ refer to the same hash key.
*/
static VALUE
ossl_x509name_eql(VALUE self, VALUE other)
{
- int result;
-
- if(CLASS_OF(other) != cX509Name) return Qfalse;
- result = ossl_x509name_cmp0(self, other);
+ if (!rb_obj_is_kind_of(other, cX509Name))
+ return Qfalse;
- return (result == 0) ? Qtrue : Qfalse;
+ return ossl_x509name_cmp0(self, other) == 0 ? Qtrue : Qfalse;
}
/*
@@ -400,7 +440,6 @@ ossl_x509name_hash(VALUE self)
return ULONG2NUM(hash);
}
-#ifdef HAVE_X509_NAME_HASH_OLD
/*
* call-seq:
* name.hash_old => integer
@@ -419,7 +458,6 @@ ossl_x509name_hash_old(VALUE self)
return ULONG2NUM(hash);
}
-#endif
/*
* call-seq:
@@ -464,6 +502,7 @@ ossl_x509name_to_der(VALUE self)
void
Init_ossl_x509name(void)
{
+#undef rb_intern
VALUE utf8str, ptrstr, ia5str, hash;
#if 0
@@ -480,17 +519,17 @@ Init_ossl_x509name(void)
rb_define_alloc_func(cX509Name, ossl_x509name_alloc);
rb_define_method(cX509Name, "initialize", ossl_x509name_initialize, -1);
- rb_define_copy_func(cX509Name, ossl_x509name_initialize_copy);
+ rb_define_method(cX509Name, "initialize_copy", ossl_x509name_initialize_copy, 1);
rb_define_method(cX509Name, "add_entry", ossl_x509name_add_entry, -1);
rb_define_method(cX509Name, "to_s", ossl_x509name_to_s, -1);
+ rb_define_method(cX509Name, "to_utf8", ossl_x509name_to_utf8, 0);
+ rb_define_method(cX509Name, "inspect", ossl_x509name_inspect, 0);
rb_define_method(cX509Name, "to_a", ossl_x509name_to_a, 0);
rb_define_method(cX509Name, "cmp", ossl_x509name_cmp, 1);
rb_define_alias(cX509Name, "<=>", "cmp");
rb_define_method(cX509Name, "eql?", ossl_x509name_eql, 1);
rb_define_method(cX509Name, "hash", ossl_x509name_hash, 0);
-#ifdef HAVE_X509_NAME_HASH_OLD
rb_define_method(cX509Name, "hash_old", ossl_x509name_hash_old, 0);
-#endif
rb_define_method(cX509Name, "to_der", ossl_x509name_to_der, 0);
utf8str = INT2NUM(V_ASN1_UTF8STRING);
diff --git a/ext/openssl/ossl_x509req.c b/ext/openssl/ossl_x509req.c
index d2619971d1..2c20042a92 100644
--- a/ext/openssl/ossl_x509req.c
+++ b/ext/openssl/ossl_x509req.c
@@ -23,10 +23,6 @@
ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \
} \
} while (0)
-#define SafeGetX509Req(obj, req) do { \
- OSSL_Check_Kind((obj), cX509Req); \
- GetX509Req((obj), (req)); \
-} while (0)
/*
* Classes
@@ -51,49 +47,16 @@ static const rb_data_type_t ossl_x509req_type = {
/*
* Public functions
*/
-VALUE
-ossl_x509req_new(X509_REQ *req)
-{
- X509_REQ *new;
- VALUE obj;
-
- obj = NewX509Req(cX509Req);
- if (!req) {
- new = X509_REQ_new();
- } else {
- new = X509_REQ_dup(req);
- }
- if (!new) {
- ossl_raise(eX509ReqError, NULL);
- }
- SetX509Req(obj, new);
-
- return obj;
-}
-
X509_REQ *
GetX509ReqPtr(VALUE obj)
{
X509_REQ *req;
- SafeGetX509Req(obj, req);
+ GetX509Req(obj, req);
return req;
}
-X509_REQ *
-DupX509ReqPtr(VALUE obj)
-{
- X509_REQ *req, *new;
-
- SafeGetX509Req(obj, req);
- if (!(new = X509_REQ_dup(req))) {
- ossl_raise(eX509ReqError, NULL);
- }
-
- return new;
-}
-
/*
* Private functions
*/
@@ -123,7 +86,7 @@ ossl_x509req_initialize(int argc, VALUE *argv, VALUE self)
return self;
}
arg = ossl_to_der_if_possible(arg);
- in = ossl_obj2bio(arg);
+ in = ossl_obj2bio(&arg);
req = PEM_read_bio_X509_REQ(in, &x, NULL, NULL);
DATA_PTR(self) = x;
if (!req) {
@@ -145,7 +108,7 @@ ossl_x509req_copy(VALUE self, VALUE other)
rb_check_frozen(self);
if (self == other) return self;
GetX509Req(self, a);
- SafeGetX509Req(other, b);
+ GetX509Req(other, b);
if (!(req = X509_REQ_dup(b))) {
ossl_raise(eX509ReqError, NULL);
}
@@ -160,8 +123,6 @@ ossl_x509req_to_pem(VALUE self)
{
X509_REQ *req;
BIO *out;
- BUF_MEM *buf;
- VALUE str;
GetX509Req(self, req);
if (!(out = BIO_new(BIO_s_mem()))) {
@@ -171,11 +132,8 @@ ossl_x509req_to_pem(VALUE self)
BIO_free(out);
ossl_raise(eX509ReqError, NULL);
}
- BIO_get_mem_ptr(out, &buf);
- str = rb_str_new(buf->data, buf->length);
- BIO_free(out);
- return str;
+ return ossl_membio2str(out);
}
static VALUE
@@ -203,8 +161,6 @@ ossl_x509req_to_text(VALUE self)
{
X509_REQ *req;
BIO *out;
- BUF_MEM *buf;
- VALUE str;
GetX509Req(self, req);
if (!(out = BIO_new(BIO_s_mem()))) {
@@ -214,11 +170,8 @@ ossl_x509req_to_text(VALUE self)
BIO_free(out);
ossl_raise(eX509ReqError, NULL);
}
- BIO_get_mem_ptr(out, &buf);
- str = rb_str_new(buf->data, buf->length);
- BIO_free(out);
- return str;
+ return ossl_membio2str(out);
}
#if 0
@@ -304,8 +257,6 @@ ossl_x509req_get_signature_algorithm(VALUE self)
X509_REQ *req;
const X509_ALGOR *alg;
BIO *out;
- BUF_MEM *buf;
- VALUE str;
GetX509Req(self, req);
@@ -317,10 +268,8 @@ ossl_x509req_get_signature_algorithm(VALUE self)
BIO_free(out);
ossl_raise(eX509ReqError, NULL);
}
- BIO_get_mem_ptr(out, &buf);
- str = rb_str_new(buf->data, buf->length);
- BIO_free(out);
- return str;
+
+ return ossl_membio2str(out);
}
static VALUE
@@ -344,11 +293,10 @@ ossl_x509req_set_public_key(VALUE self, VALUE key)
EVP_PKEY *pkey;
GetX509Req(self, req);
- pkey = GetPKeyPtr(key); /* NO NEED TO DUP */
- if (!X509_REQ_set_pubkey(req, pkey)) {
- ossl_raise(eX509ReqError, NULL);
- }
-
+ pkey = GetPKeyPtr(key);
+ ossl_pkey_check_public_key(pkey);
+ if (!X509_REQ_set_pubkey(req, pkey))
+ ossl_raise(eX509ReqError, "X509_REQ_set_pubkey");
return key;
}
@@ -361,7 +309,7 @@ ossl_x509req_sign(VALUE self, VALUE key, VALUE digest)
GetX509Req(self, req);
pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
- md = GetDigestPtr(digest);
+ md = ossl_evp_get_digestbyname(digest);
if (!X509_REQ_sign(req, pkey, md)) {
ossl_raise(eX509ReqError, NULL);
}
@@ -379,7 +327,8 @@ ossl_x509req_verify(VALUE self, VALUE key)
EVP_PKEY *pkey;
GetX509Req(self, req);
- pkey = GetPKeyPtr(key); /* NO NEED TO DUP */
+ pkey = GetPKeyPtr(key);
+ ossl_pkey_check_public_key(pkey);
switch (X509_REQ_verify(req, pkey)) {
case 1:
return Qtrue;
@@ -471,7 +420,7 @@ Init_ossl_x509req(void)
rb_define_alloc_func(cX509Req, ossl_x509req_alloc);
rb_define_method(cX509Req, "initialize", ossl_x509req_initialize, -1);
- rb_define_copy_func(cX509Req, ossl_x509req_copy);
+ rb_define_method(cX509Req, "initialize_copy", ossl_x509req_copy, 1);
rb_define_method(cX509Req, "to_pem", ossl_x509req_to_pem, 0);
rb_define_method(cX509Req, "to_der", ossl_x509req_to_der, 0);
diff --git a/ext/openssl/ossl_x509revoked.c b/ext/openssl/ossl_x509revoked.c
index 7960ea349e..5fe6853430 100644
--- a/ext/openssl/ossl_x509revoked.c
+++ b/ext/openssl/ossl_x509revoked.c
@@ -23,10 +23,6 @@
ossl_raise(rb_eRuntimeError, "REV wasn't initialized!"); \
} \
} while (0)
-#define SafeGetX509Rev(obj, rev) do { \
- OSSL_Check_Kind((obj), cX509Rev); \
- GetX509Rev((obj), (rev)); \
-} while (0)
/*
* Classes
@@ -76,7 +72,7 @@ DupX509RevokedPtr(VALUE obj)
{
X509_REVOKED *rev, *new;
- SafeGetX509Rev(obj, rev);
+ GetX509Rev(obj, rev);
if (!(new = X509_REVOKED_dup(rev))) {
ossl_raise(eX509RevError, NULL);
}
@@ -116,7 +112,7 @@ ossl_x509revoked_initialize_copy(VALUE self, VALUE other)
rb_check_frozen(self);
GetX509Rev(self, rev);
- SafeGetX509Rev(other, rev_other);
+ GetX509Rev(other, rev_other);
rev_new = X509_REVOKED_dup(rev_other);
if (!rev_new)
@@ -159,10 +155,14 @@ static VALUE
ossl_x509revoked_get_time(VALUE self)
{
X509_REVOKED *rev;
+ const ASN1_TIME *time;
GetX509Rev(self, rev);
+ time = X509_REVOKED_get0_revocationDate(rev);
+ if (!time)
+ return Qnil;
- return asn1time_to_time(X509_REVOKED_get0_revocationDate(rev));
+ return asn1time_to_time(time);
}
static VALUE
@@ -249,6 +249,26 @@ ossl_x509revoked_add_extension(VALUE self, VALUE ext)
return ext;
}
+static VALUE
+ossl_x509revoked_to_der(VALUE self)
+{
+ X509_REVOKED *rev;
+ VALUE str;
+ int len;
+ unsigned char *p;
+
+ GetX509Rev(self, rev);
+ len = i2d_X509_REVOKED(rev, NULL);
+ if (len <= 0)
+ ossl_raise(eX509RevError, "i2d_X509_REVOKED");
+ str = rb_str_new(NULL, len);
+ p = (unsigned char *)RSTRING_PTR(str);
+ if (i2d_X509_REVOKED(rev, &p) <= 0)
+ ossl_raise(eX509RevError, "i2d_X509_REVOKED");
+ ossl_str_adjust(str, p);
+ return str;
+}
+
/*
* INIT
*/
@@ -267,7 +287,7 @@ Init_ossl_x509revoked(void)
rb_define_alloc_func(cX509Rev, ossl_x509revoked_alloc);
rb_define_method(cX509Rev, "initialize", ossl_x509revoked_initialize, -1);
- rb_define_copy_func(cX509Rev, ossl_x509revoked_initialize_copy);
+ rb_define_method(cX509Rev, "initialize_copy", ossl_x509revoked_initialize_copy, 1);
rb_define_method(cX509Rev, "serial", ossl_x509revoked_get_serial, 0);
rb_define_method(cX509Rev, "serial=", ossl_x509revoked_set_serial, 1);
@@ -276,4 +296,5 @@ Init_ossl_x509revoked(void)
rb_define_method(cX509Rev, "extensions", ossl_x509revoked_get_extensions, 0);
rb_define_method(cX509Rev, "extensions=", ossl_x509revoked_set_extensions, 1);
rb_define_method(cX509Rev, "add_extension", ossl_x509revoked_add_extension, 1);
+ rb_define_method(cX509Rev, "to_der", ossl_x509revoked_to_der, 0);
}
diff --git a/ext/openssl/ossl_x509store.c b/ext/openssl/ossl_x509store.c
index 75f8238b01..2909eeda17 100644
--- a/ext/openssl/ossl_x509store.c
+++ b/ext/openssl/ossl_x509store.c
@@ -23,10 +23,6 @@
ossl_raise(rb_eRuntimeError, "STORE wasn't initialized!"); \
} \
} while (0)
-#define SafeGetX509Store(obj, st) do { \
- OSSL_Check_Kind((obj), cX509Store); \
- GetX509Store((obj), (st)); \
-} while (0)
#define NewX509StCtx(klass) \
TypedData_Wrap_Struct((klass), &ossl_x509stctx_type, 0)
@@ -42,10 +38,65 @@
ossl_raise(rb_eRuntimeError, "STORE_CTX is out of scope!"); \
} \
} while (0)
-#define SafeGetX509StCtx(obj, storep) do { \
- OSSL_Check_Kind((obj), cX509StoreContext); \
- GetX509Store((obj), (ctx)); \
-} while (0)
+
+/*
+ * Verify callback stuff
+ */
+static int stctx_ex_verify_cb_idx, store_ex_verify_cb_idx;
+static VALUE ossl_x509stctx_new(X509_STORE_CTX *);
+
+struct ossl_verify_cb_args {
+ VALUE proc;
+ VALUE preverify_ok;
+ VALUE store_ctx;
+};
+
+static VALUE
+call_verify_cb_proc(struct ossl_verify_cb_args *args)
+{
+ return rb_funcall(args->proc, rb_intern("call"), 2,
+ args->preverify_ok, args->store_ctx);
+}
+
+int
+ossl_verify_cb_call(VALUE proc, int ok, X509_STORE_CTX *ctx)
+{
+ VALUE rctx, ret;
+ struct ossl_verify_cb_args args;
+ int state;
+
+ if (NIL_P(proc))
+ return ok;
+
+ ret = Qfalse;
+ rctx = rb_protect((VALUE(*)(VALUE))ossl_x509stctx_new, (VALUE)ctx, &state);
+ if (state) {
+ rb_set_errinfo(Qnil);
+ rb_warn("StoreContext initialization failure");
+ }
+ else {
+ args.proc = proc;
+ args.preverify_ok = ok ? Qtrue : Qfalse;
+ args.store_ctx = rctx;
+ ret = rb_protect((VALUE(*)(VALUE))call_verify_cb_proc, (VALUE)&args, &state);
+ if (state) {
+ rb_set_errinfo(Qnil);
+ rb_warn("exception in verify_callback is ignored");
+ }
+ RTYPEDDATA_DATA(rctx) = NULL;
+ }
+ if (ret == Qtrue) {
+ X509_STORE_CTX_set_error(ctx, X509_V_OK);
+ ok = 1;
+ }
+ else {
+ if (X509_STORE_CTX_get_error(ctx) == X509_V_OK)
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED);
+ ok = 0;
+ }
+
+ return ok;
+}
/*
* Classes
@@ -71,34 +122,12 @@ static const rb_data_type_t ossl_x509store_type = {
/*
* Public functions
*/
-VALUE
-ossl_x509store_new(X509_STORE *store)
-{
- VALUE obj;
-
- obj = NewX509Store(cX509Store);
- SetX509Store(obj, store);
-
- return obj;
-}
-
X509_STORE *
GetX509StorePtr(VALUE obj)
{
X509_STORE *store;
- SafeGetX509Store(obj, store);
-
- return store;
-}
-
-X509_STORE *
-DupX509StorePtr(VALUE obj)
-{
- X509_STORE *store;
-
- SafeGetX509Store(obj, store);
- X509_STORE_up_ref(store);
+ GetX509Store(obj, store);
return store;
}
@@ -111,9 +140,10 @@ x509store_verify_cb(int ok, X509_STORE_CTX *ctx)
{
VALUE proc;
- proc = (VALUE)X509_STORE_CTX_get_ex_data(ctx, ossl_store_ctx_ex_verify_cb_idx);
+ proc = (VALUE)X509_STORE_CTX_get_ex_data(ctx, stctx_ex_verify_cb_idx);
if (!proc)
- proc = (VALUE)X509_STORE_get_ex_data(X509_STORE_CTX_get0_store(ctx), ossl_store_ex_verify_cb_idx);
+ proc = (VALUE)X509_STORE_get_ex_data(X509_STORE_CTX_get0_store(ctx),
+ store_ex_verify_cb_idx);
if (!proc)
return ok;
@@ -144,7 +174,7 @@ ossl_x509store_set_vfy_cb(VALUE self, VALUE cb)
X509_STORE *store;
GetX509Store(self, store);
- X509_STORE_set_ex_data(store, ossl_store_ex_verify_cb_idx, (void *)cb);
+ X509_STORE_set_ex_data(store, store_ex_verify_cb_idx, (void *)cb);
rb_iv_set(self, "@verify_callback", cb);
return cb;
@@ -182,9 +212,9 @@ ossl_x509store_initialize(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * store.flags = flag
+ * store.flags = flags
*
- * Sets +flag+ to the Store. +flag+ consists of zero or more of the constants
+ * Sets _flags_ to the Store. _flags_ consists of zero or more of the constants
* defined in with name V_FLAG_* or'ed together.
*/
static VALUE
@@ -203,7 +233,7 @@ ossl_x509store_set_flags(VALUE self, VALUE flags)
* call-seq:
* store.purpose = purpose
*
- * Sets the store's purpose to +purpose+. If specified, the verifications on
+ * Sets the store's purpose to _purpose_. If specified, the verifications on
* the store will check every untrusted certificate's extensions are consistent
* with the purpose. The purpose is specified by constants:
*
@@ -262,8 +292,9 @@ ossl_x509store_set_time(VALUE self, VALUE time)
* call-seq:
* store.add_file(file) -> self
*
- * Adds the certificates in +file+ to the certificate store. The +file+ can
- * contain multiple PEM-encoded certificates.
+ * Adds the certificates in _file_ to the certificate store. _file_ is the path
+ * to the file, and the file contains one or more certificates in PEM format
+ * concatenated together.
*/
static VALUE
ossl_x509store_add_file(VALUE self, VALUE file)
@@ -282,6 +313,15 @@ ossl_x509store_add_file(VALUE self, VALUE file)
if(X509_LOOKUP_load_file(lookup, path, X509_FILETYPE_PEM) != 1){
ossl_raise(eX509StoreError, NULL);
}
+#if OPENSSL_VERSION_NUMBER < 0x10101000 || defined(LIBRESSL_VERSION_NUMBER)
+ /*
+ * X509_load_cert_crl_file() which is called from X509_LOOKUP_load_file()
+ * did not check the return value of X509_STORE_add_{cert,crl}(), leaking
+ * "cert already in hash table" errors on the error queue, if duplicate
+ * certificates are found. This will be fixed by OpenSSL 1.1.1.
+ */
+ ossl_clear_error();
+#endif
return self;
}
@@ -290,7 +330,7 @@ ossl_x509store_add_file(VALUE self, VALUE file)
* call-seq:
* store.add_path(path) -> self
*
- * Adds +path+ as the hash dir to be looked up by the store.
+ * Adds _path_ as the hash dir to be looked up by the store.
*/
static VALUE
ossl_x509store_add_path(VALUE self, VALUE dir)
@@ -317,7 +357,7 @@ ossl_x509store_add_path(VALUE self, VALUE dir)
* call-seq:
* store.set_default_paths
*
- * Configures +store+ to look up CA certificates from the system default
+ * Configures _store_ to look up CA certificates from the system default
* certificate store as needed basis. The location of the store can usually be
* determined by:
*
@@ -341,7 +381,7 @@ ossl_x509store_set_default_paths(VALUE self)
* call-seq:
* store.add_cert(cert)
*
- * Adds the OpenSSL::X509::Certificate +cert+ to the certificate store.
+ * Adds the OpenSSL::X509::Certificate _cert_ to the certificate store.
*/
static VALUE
ossl_x509store_add_cert(VALUE self, VALUE arg)
@@ -362,7 +402,7 @@ ossl_x509store_add_cert(VALUE self, VALUE arg)
* call-seq:
* store.add_crl(crl) -> self
*
- * Adds the OpenSSL::X509::CRL +crl+ to the store.
+ * Adds the OpenSSL::X509::CRL _crl_ to the store.
*/
static VALUE
ossl_x509store_add_crl(VALUE self, VALUE arg)
@@ -387,15 +427,15 @@ static VALUE ossl_x509stctx_get_chain(VALUE);
* call-seq:
* store.verify(cert, chain = nil) -> true | false
*
- * Performs a certificate verification on the OpenSSL::X509::Certificate +cert+.
+ * Performs a certificate verification on the OpenSSL::X509::Certificate _cert_.
*
- * +chain+ can be an array of OpenSSL::X509::Certificate that is used to
+ * _chain_ can be an array of OpenSSL::X509::Certificate that is used to
* construct the certificate chain.
*
* If a block is given, it overrides the callback set by #verify_callback=.
*
* After finishing the verification, the error information can be retrieved by
- * #error, #error_string, and the resuting complete certificate chain can be
+ * #error, #error_string, and the resulting complete certificate chain can be
* retrieved by #chain.
*/
static VALUE
@@ -432,27 +472,6 @@ static const rb_data_type_t ossl_x509stctx_type = {
0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
};
-
-VALUE
-ossl_x509stctx_new(X509_STORE_CTX *ctx)
-{
- VALUE obj;
-
- obj = NewX509StCtx(cX509StoreContext);
- SetX509StCtx(obj, ctx);
-
- return obj;
-}
-
-VALUE
-ossl_x509stctx_clear_ptr(VALUE obj)
-{
- OSSL_Check_Kind(obj, cX509StoreContext);
- RDATA(obj)->data = NULL;
-
- return obj;
-}
-
/*
* Private functions
*/
@@ -482,6 +501,17 @@ ossl_x509stctx_alloc(VALUE klass)
return obj;
}
+static VALUE
+ossl_x509stctx_new(X509_STORE_CTX *ctx)
+{
+ VALUE obj;
+
+ obj = NewX509StCtx(cX509StoreContext);
+ SetX509StCtx(obj, ctx);
+
+ return obj;
+}
+
static VALUE ossl_x509stctx_set_flags(VALUE, VALUE);
static VALUE ossl_x509stctx_set_purpose(VALUE, VALUE);
static VALUE ossl_x509stctx_set_trust(VALUE, VALUE);
@@ -502,7 +532,7 @@ ossl_x509stctx_initialize(int argc, VALUE *argv, VALUE self)
rb_scan_args(argc, argv, "12", &store, &cert, &chain);
GetX509StCtx(self, ctx);
- SafeGetX509Store(store, x509st);
+ GetX509Store(store, x509st);
if(!NIL_P(cert)) x509 = DupX509CertPtr(cert); /* NEED TO DUP */
if(!NIL_P(chain)) x509s = ossl_x509_ary2sk(chain);
if(X509_STORE_CTX_init(ctx, x509st, x509, x509s) != 1){
@@ -527,7 +557,7 @@ ossl_x509stctx_verify(VALUE self)
X509_STORE_CTX *ctx;
GetX509StCtx(self, ctx);
- X509_STORE_CTX_set_ex_data(ctx, ossl_store_ctx_ex_verify_cb_idx,
+ X509_STORE_CTX_set_ex_data(ctx, stctx_ex_verify_cb_idx,
(void *)rb_iv_get(self, "@verify_callback"));
switch (X509_verify_cert(ctx)) {
@@ -741,12 +771,21 @@ ossl_x509stctx_set_time(VALUE self, VALUE time)
void
Init_ossl_x509store(void)
{
+#undef rb_intern
#if 0
mOSSL = rb_define_module("OpenSSL");
eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
mX509 = rb_define_module_under(mOSSL, "X509");
#endif
+ /* Register ext_data slot for verify callback Proc */
+ stctx_ex_verify_cb_idx = X509_STORE_CTX_get_ex_new_index(0, (void *)"stctx_ex_verify_cb_idx", 0, 0, 0);
+ if (stctx_ex_verify_cb_idx < 0)
+ ossl_raise(eOSSLError, "X509_STORE_CTX_get_ex_new_index");
+ store_ex_verify_cb_idx = X509_STORE_get_ex_new_index(0, (void *)"store_ex_verify_cb_idx", 0, 0, 0);
+ if (store_ex_verify_cb_idx < 0)
+ ossl_raise(eOSSLError, "X509_STORE_get_ex_new_index");
+
eX509StoreError = rb_define_class_under(mX509, "StoreError", eOSSLError);
/* Document-class: OpenSSL::X509::Store
diff --git a/ext/openssl/ruby_missing.h b/ext/openssl/ruby_missing.h
index f076b1757d..069acc8b14 100644
--- a/ext/openssl/ruby_missing.h
+++ b/ext/openssl/ruby_missing.h
@@ -10,23 +10,15 @@
#if !defined(_OSSL_RUBY_MISSING_H_)
#define _OSSL_RUBY_MISSING_H_
-#define rb_define_copy_func(klass, func) \
- rb_define_method((klass), "initialize_copy", (func), 1)
-
-
-#ifndef GetReadFile
-#define FPTR_TO_FD(fptr) ((fptr)->fd)
-#else
-#define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
-#endif
-
-#ifndef HAVE_RB_IO_T
-#define rb_io_t OpenFile
+/* Ruby 2.4 */
+#ifndef RB_INTEGER_TYPE_P
+# define RB_INTEGER_TYPE_P(obj) (RB_FIXNUM_P(obj) || RB_TYPE_P(obj, T_BIGNUM))
#endif
-#ifndef RB_INTEGER_TYPE_P
-/* for Ruby 2.3 compatibility */
-#define RB_INTEGER_TYPE_P(obj) (RB_FIXNUM_P(obj) || RB_TYPE_P(obj, T_BIGNUM))
+/* Ruby 2.5 */
+#ifndef ST2FIX
+# define RB_ST2FIX(h) LONG2FIX((long)(h))
+# define ST2FIX(h) RB_ST2FIX(h)
#endif
#endif /* _OSSL_RUBY_MISSING_H_ */
diff --git a/ext/pathname/depend b/ext/pathname/depend
index 5b6ea7484d..42abd32130 100644
--- a/ext/pathname/depend
+++ b/ext/pathname/depend
@@ -6,6 +6,7 @@ pathname.o: $(hdrdir)/ruby/defines.h
pathname.o: $(hdrdir)/ruby/encoding.h
pathname.o: $(hdrdir)/ruby/intern.h
pathname.o: $(hdrdir)/ruby/missing.h
+pathname.o: $(hdrdir)/ruby/onigmo.h
pathname.o: $(hdrdir)/ruby/oniguruma.h
pathname.o: $(hdrdir)/ruby/ruby.h
pathname.o: $(hdrdir)/ruby/st.h
diff --git a/ext/pathname/lib/pathname.rb b/ext/pathname/lib/pathname.rb
index a6621565d0..2fd0642e14 100644
--- a/ext/pathname/lib/pathname.rb
+++ b/ext/pathname/lib/pathname.rb
@@ -14,6 +14,8 @@ require 'pathname.so'
class Pathname
+ # :stopdoc:
+
# to_path is implemented so Pathname objects are usable with File.open, etc.
TO_PATH = :to_path
@@ -38,7 +40,7 @@ class Pathname
# chop_basename(path) -> [pre-basename, basename] or nil
def chop_basename(path) # :nodoc:
base = File.basename(path)
- if /\A#{SEPARATOR_PAT}?\z/o =~ base
+ if /\A#{SEPARATOR_PAT}?\z/o.match?(base)
return nil
else
return path[0, path.rindex(base)], base
@@ -60,7 +62,7 @@ class Pathname
def prepend_prefix(prefix, relpath) # :nodoc:
if relpath.empty?
File.dirname(prefix)
- elsif /#{SEPARATOR_PAT}/o =~ prefix
+ elsif /#{SEPARATOR_PAT}/o.match?(prefix)
prefix = File.dirname(prefix)
prefix = File.join(prefix, "") if File.basename(prefix + 'a') != 'a'
prefix + relpath
@@ -111,7 +113,7 @@ class Pathname
end
end
pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
- if /#{SEPARATOR_PAT}/o =~ File.basename(pre)
+ if /#{SEPARATOR_PAT}/o.match?(File.basename(pre))
names.shift while names[0] == '..'
end
self.class.new(prepend_prefix(pre, File.join(*names)))
@@ -160,7 +162,7 @@ class Pathname
names.unshift base if base != '.'
end
pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
- if /#{SEPARATOR_PAT}/o =~ File.basename(pre)
+ if /#{SEPARATOR_PAT}/o.match?(File.basename(pre))
names.shift while names[0] == '..'
end
if names.empty?
@@ -191,8 +193,7 @@ class Pathname
begin
stat1 = self.lstat
stat2 = self.parent.lstat
- stat1.dev == stat2.dev && stat1.ino == stat2.ino ||
- stat1.dev != stat2.dev
+ stat1.dev != stat2.dev || stat1.ino == stat2.ino
rescue Errno::ENOENT
false
end
@@ -206,7 +207,7 @@ class Pathname
# pathnames which points to roots such as <tt>/usr/..</tt>.
#
def root?
- !!(chop_basename(@path) == nil && /#{SEPARATOR_PAT}/o =~ @path)
+ !!(chop_basename(@path) == nil && /#{SEPARATOR_PAT}/o.match?(@path))
end
# Predicate method for testing whether a path is absolute.
@@ -378,7 +379,7 @@ class Pathname
basename_list2.shift
end
r1 = chop_basename(prefix1)
- if !r1 && /#{SEPARATOR_PAT}/o =~ File.basename(prefix1)
+ if !r1 && (r1 = /#{SEPARATOR_PAT}/o.match?(File.basename(prefix1)))
while !basename_list2.empty? && basename_list2.first == '..'
index_list2.shift
basename_list2.shift
@@ -502,6 +503,7 @@ class Pathname
# ArgumentError is raised when it cannot find a relative path.
#
def relative_path_from(base_directory)
+ base_directory = Pathname.new(base_directory) unless base_directory.is_a? Pathname
dest_directory = self.cleanpath.to_s
base_directory = base_directory.cleanpath.to_s
dest_prefix = dest_directory
diff --git a/ext/pathname/pathname.c b/ext/pathname/pathname.c
index e617e82176..d5e58c52d8 100644
--- a/ext/pathname/pathname.c
+++ b/ext/pathname/pathname.c
@@ -2,7 +2,75 @@
#include "ruby/encoding.h"
static VALUE rb_cPathname;
-static ID id_at_path, id_to_path;
+static ID id_ENOTDIR;
+static ID id_at_path;
+static ID id_atime;
+static ID id_base;
+static ID id_basename;
+static ID id_binread;
+static ID id_binwrite;
+static ID id_birthtime;
+static ID id_blockdev_p;
+static ID id_chardev_p;
+static ID id_chmod;
+static ID id_chown;
+static ID id_ctime;
+static ID id_directory_p;
+static ID id_dirname;
+static ID id_empty_p;
+static ID id_entries;
+static ID id_executable_p;
+static ID id_executable_real_p;
+static ID id_exist_p;
+static ID id_expand_path;
+static ID id_extname;
+static ID id_file_p;
+static ID id_fnmatch;
+static ID id_foreach;
+static ID id_ftype;
+static ID id_getwd;
+static ID id_glob;
+static ID id_grpowned_p;
+static ID id_lchmod;
+static ID id_lchown;
+static ID id_link;
+static ID id_lstat;
+static ID id_mkdir;
+static ID id_mtime;
+static ID id_open;
+static ID id_owned_p;
+static ID id_pipe_p;
+static ID id_read;
+static ID id_readable_p;
+static ID id_readable_real_p;
+static ID id_readlines;
+static ID id_readlink;
+static ID id_realdirpath;
+static ID id_realpath;
+static ID id_rename;
+static ID id_rmdir;
+static ID id_setgid_p;
+static ID id_setuid_p;
+static ID id_size;
+static ID id_size_p;
+static ID id_socket_p;
+static ID id_split;
+static ID id_stat;
+static ID id_sticky_p;
+static ID id_sub;
+static ID id_symlink;
+static ID id_symlink_p;
+static ID id_sysopen;
+static ID id_to_path;
+static ID id_truncate;
+static ID id_unlink;
+static ID id_utime;
+static ID id_world_readable_p;
+static ID id_world_writable_p;
+static ID id_writable_p;
+static ID id_writable_real_p;
+static ID id_write;
+static ID id_zero_p;
static VALUE
get_strpath(VALUE obj)
@@ -203,10 +271,10 @@ path_sub(int argc, VALUE *argv, VALUE self)
VALUE str = get_strpath(self);
if (rb_block_given_p()) {
- str = rb_block_call(str, rb_intern("sub"), argc, argv, 0, 0);
+ str = rb_block_call(str, id_sub, argc, argv, 0, 0);
}
else {
- str = rb_funcallv(str, rb_intern("sub"), argc, argv);
+ str = rb_funcallv(str, id_sub, argc, argv);
}
return rb_class_new_instance(1, &str, rb_obj_class(self));
}
@@ -261,7 +329,7 @@ path_realpath(int argc, VALUE *argv, VALUE self)
{
VALUE basedir, str;
rb_scan_args(argc, argv, "01", &basedir);
- str = rb_funcall(rb_cFile, rb_intern("realpath"), 2, get_strpath(self), basedir);
+ str = rb_funcall(rb_cFile, id_realpath, 2, get_strpath(self), basedir);
return rb_class_new_instance(1, &str, rb_obj_class(self));
}
@@ -277,7 +345,7 @@ path_realdirpath(int argc, VALUE *argv, VALUE self)
{
VALUE basedir, str;
rb_scan_args(argc, argv, "01", &basedir);
- str = rb_funcall(rb_cFile, rb_intern("realdirpath"), 2, get_strpath(self), basedir);
+ str = rb_funcall(rb_cFile, id_realdirpath, 2, get_strpath(self), basedir);
return rb_class_new_instance(1, &str, rb_obj_class(self));
}
@@ -300,10 +368,10 @@ path_each_line(int argc, VALUE *argv, VALUE self)
args[0] = get_strpath(self);
n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]);
if (rb_block_given_p()) {
- return rb_block_call(rb_cIO, rb_intern("foreach"), 1+n, args, 0, 0);
+ return rb_block_call(rb_cFile, id_foreach, 1+n, args, 0, 0);
}
else {
- return rb_funcallv(rb_cIO, rb_intern("foreach"), 1+n, args);
+ return rb_funcallv(rb_cFile, id_foreach, 1+n, args);
}
}
@@ -314,7 +382,7 @@ path_each_line(int argc, VALUE *argv, VALUE self)
*
* Returns all data from the file, or the first +N+ bytes if specified.
*
- * See IO.read.
+ * See File.read.
*
*/
static VALUE
@@ -325,7 +393,7 @@ path_read(int argc, VALUE *argv, VALUE self)
args[0] = get_strpath(self);
n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]);
- return rb_funcallv(rb_cIO, rb_intern("read"), 1+n, args);
+ return rb_funcallv(rb_cFile, id_read, 1+n, args);
}
/*
@@ -334,7 +402,7 @@ path_read(int argc, VALUE *argv, VALUE self)
*
* Returns all the bytes from the file, or the first +N+ if specified.
*
- * See IO.binread.
+ * See File.binread.
*
*/
static VALUE
@@ -345,7 +413,7 @@ path_binread(int argc, VALUE *argv, VALUE self)
args[0] = get_strpath(self);
n = rb_scan_args(argc, argv, "02", &args[1], &args[2]);
- return rb_funcallv(rb_cIO, rb_intern("binread"), 1+n, args);
+ return rb_funcallv(rb_cFile, id_binread, 1+n, args);
}
/*
@@ -355,7 +423,7 @@ path_binread(int argc, VALUE *argv, VALUE self)
*
* Writes +contents+ to the file.
*
- * See IO.write.
+ * See File.write.
*
*/
static VALUE
@@ -366,7 +434,7 @@ path_write(int argc, VALUE *argv, VALUE self)
args[0] = get_strpath(self);
n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]);
- return rb_funcallv(rb_cIO, rb_intern("write"), 1+n, args);
+ return rb_funcallv(rb_cFile, id_write, 1+n, args);
}
/*
@@ -376,7 +444,7 @@ path_write(int argc, VALUE *argv, VALUE self)
*
* Writes +contents+ to the file, opening it in binary mode.
*
- * See IO.binwrite.
+ * See File.binwrite.
*
*/
static VALUE
@@ -387,7 +455,7 @@ path_binwrite(int argc, VALUE *argv, VALUE self)
args[0] = get_strpath(self);
n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]);
- return rb_funcallv(rb_cIO, rb_intern("binwrite"), 1+n, args);
+ return rb_funcallv(rb_cFile, id_binwrite, 1+n, args);
}
/*
@@ -398,7 +466,7 @@ path_binwrite(int argc, VALUE *argv, VALUE self)
*
* Returns all the lines from the file.
*
- * See IO.readlines.
+ * See File.readlines.
*
*/
static VALUE
@@ -409,7 +477,7 @@ path_readlines(int argc, VALUE *argv, VALUE self)
args[0] = get_strpath(self);
n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]);
- return rb_funcallv(rb_cIO, rb_intern("readlines"), 1+n, args);
+ return rb_funcallv(rb_cFile, id_readlines, 1+n, args);
}
/*
@@ -427,7 +495,7 @@ path_sysopen(int argc, VALUE *argv, VALUE self)
args[0] = get_strpath(self);
n = rb_scan_args(argc, argv, "02", &args[1], &args[2]);
- return rb_funcallv(rb_cIO, rb_intern("sysopen"), 1+n, args);
+ return rb_funcallv(rb_cIO, id_sysopen, 1+n, args);
}
/*
@@ -441,7 +509,7 @@ path_sysopen(int argc, VALUE *argv, VALUE self)
static VALUE
path_atime(VALUE self)
{
- return rb_funcall(rb_cFile, rb_intern("atime"), 1, get_strpath(self));
+ return rb_funcall(rb_cFile, id_atime, 1, get_strpath(self));
}
#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC) || defined(_WIN32)
@@ -457,7 +525,7 @@ path_atime(VALUE self)
static VALUE
path_birthtime(VALUE self)
{
- return rb_funcall(rb_cFile, rb_intern("birthtime"), 1, get_strpath(self));
+ return rb_funcall(rb_cFile, id_birthtime, 1, get_strpath(self));
}
#else
# define path_birthtime rb_f_notimplement
@@ -474,7 +542,7 @@ path_birthtime(VALUE self)
static VALUE
path_ctime(VALUE self)
{
- return rb_funcall(rb_cFile, rb_intern("ctime"), 1, get_strpath(self));
+ return rb_funcall(rb_cFile, id_ctime, 1, get_strpath(self));
}
/*
@@ -488,7 +556,7 @@ path_ctime(VALUE self)
static VALUE
path_mtime(VALUE self)
{
- return rb_funcall(rb_cFile, rb_intern("mtime"), 1, get_strpath(self));
+ return rb_funcall(rb_cFile, id_mtime, 1, get_strpath(self));
}
/*
@@ -502,7 +570,7 @@ path_mtime(VALUE self)
static VALUE
path_chmod(VALUE self, VALUE mode)
{
- return rb_funcall(rb_cFile, rb_intern("chmod"), 2, mode, get_strpath(self));
+ return rb_funcall(rb_cFile, id_chmod, 2, mode, get_strpath(self));
}
/*
@@ -516,7 +584,7 @@ path_chmod(VALUE self, VALUE mode)
static VALUE
path_lchmod(VALUE self, VALUE mode)
{
- return rb_funcall(rb_cFile, rb_intern("lchmod"), 2, mode, get_strpath(self));
+ return rb_funcall(rb_cFile, id_lchmod, 2, mode, get_strpath(self));
}
/*
@@ -530,7 +598,7 @@ path_lchmod(VALUE self, VALUE mode)
static VALUE
path_chown(VALUE self, VALUE owner, VALUE group)
{
- return rb_funcall(rb_cFile, rb_intern("chown"), 3, owner, group, get_strpath(self));
+ return rb_funcall(rb_cFile, id_chown, 3, owner, group, get_strpath(self));
}
/*
@@ -544,7 +612,7 @@ path_chown(VALUE self, VALUE owner, VALUE group)
static VALUE
path_lchown(VALUE self, VALUE owner, VALUE group)
{
- return rb_funcall(rb_cFile, rb_intern("lchown"), 3, owner, group, get_strpath(self));
+ return rb_funcall(rb_cFile, id_lchown, 3, owner, group, get_strpath(self));
}
/*
@@ -562,9 +630,9 @@ path_fnmatch(int argc, VALUE *argv, VALUE self)
VALUE str = get_strpath(self);
VALUE pattern, flags;
if (rb_scan_args(argc, argv, "11", &pattern, &flags) == 1)
- return rb_funcall(rb_cFile, rb_intern("fnmatch"), 2, pattern, str);
+ return rb_funcall(rb_cFile, id_fnmatch, 2, pattern, str);
else
- return rb_funcall(rb_cFile, rb_intern("fnmatch"), 3, pattern, str, flags);
+ return rb_funcall(rb_cFile, id_fnmatch, 3, pattern, str, flags);
}
/*
@@ -578,7 +646,7 @@ path_fnmatch(int argc, VALUE *argv, VALUE self)
static VALUE
path_ftype(VALUE self)
{
- return rb_funcall(rb_cFile, rb_intern("ftype"), 1, get_strpath(self));
+ return rb_funcall(rb_cFile, id_ftype, 1, get_strpath(self));
}
/*
@@ -592,7 +660,7 @@ path_ftype(VALUE self)
static VALUE
path_make_link(VALUE self, VALUE old)
{
- return rb_funcall(rb_cFile, rb_intern("link"), 2, old, get_strpath(self));
+ return rb_funcall(rb_cFile, id_link, 2, old, get_strpath(self));
}
/*
@@ -609,10 +677,10 @@ path_open(int argc, VALUE *argv, VALUE self)
args[0] = get_strpath(self);
n = rb_scan_args(argc, argv, "03", &args[1], &args[2], &args[3]);
if (rb_block_given_p()) {
- return rb_block_call(rb_cFile, rb_intern("open"), 1+n, args, 0, 0);
+ return rb_block_call(rb_cFile, id_open, 1+n, args, 0, 0);
}
else {
- return rb_funcallv(rb_cFile, rb_intern("open"), 1+n, args);
+ return rb_funcallv(rb_cFile, id_open, 1+n, args);
}
}
@@ -625,7 +693,7 @@ static VALUE
path_readlink(VALUE self)
{
VALUE str;
- str = rb_funcall(rb_cFile, rb_intern("readlink"), 1, get_strpath(self));
+ str = rb_funcall(rb_cFile, id_readlink, 1, get_strpath(self));
return rb_class_new_instance(1, &str, rb_obj_class(self));
}
@@ -637,7 +705,7 @@ path_readlink(VALUE self)
static VALUE
path_rename(VALUE self, VALUE to)
{
- return rb_funcall(rb_cFile, rb_intern("rename"), 2, get_strpath(self), to);
+ return rb_funcall(rb_cFile, id_rename, 2, get_strpath(self), to);
}
/*
@@ -648,7 +716,7 @@ path_rename(VALUE self, VALUE to)
static VALUE
path_stat(VALUE self)
{
- return rb_funcall(rb_cFile, rb_intern("stat"), 1, get_strpath(self));
+ return rb_funcall(rb_cFile, id_stat, 1, get_strpath(self));
}
/*
@@ -657,7 +725,7 @@ path_stat(VALUE self)
static VALUE
path_lstat(VALUE self)
{
- return rb_funcall(rb_cFile, rb_intern("lstat"), 1, get_strpath(self));
+ return rb_funcall(rb_cFile, id_lstat, 1, get_strpath(self));
}
/*
@@ -671,7 +739,7 @@ path_lstat(VALUE self)
static VALUE
path_make_symlink(VALUE self, VALUE old)
{
- return rb_funcall(rb_cFile, rb_intern("symlink"), 2, old, get_strpath(self));
+ return rb_funcall(rb_cFile, id_symlink, 2, old, get_strpath(self));
}
/*
@@ -682,7 +750,7 @@ path_make_symlink(VALUE self, VALUE old)
static VALUE
path_truncate(VALUE self, VALUE length)
{
- return rb_funcall(rb_cFile, rb_intern("truncate"), 2, get_strpath(self), length);
+ return rb_funcall(rb_cFile, id_truncate, 2, get_strpath(self), length);
}
/*
@@ -693,7 +761,7 @@ path_truncate(VALUE self, VALUE length)
static VALUE
path_utime(VALUE self, VALUE atime, VALUE mtime)
{
- return rb_funcall(rb_cFile, rb_intern("utime"), 3, atime, mtime, get_strpath(self));
+ return rb_funcall(rb_cFile, id_utime, 3, atime, mtime, get_strpath(self));
}
/*
@@ -707,9 +775,9 @@ path_basename(int argc, VALUE *argv, VALUE self)
VALUE str = get_strpath(self);
VALUE fext;
if (rb_scan_args(argc, argv, "01", &fext) == 0)
- str = rb_funcall(rb_cFile, rb_intern("basename"), 1, str);
+ str = rb_funcall(rb_cFile, id_basename, 1, str);
else
- str = rb_funcall(rb_cFile, rb_intern("basename"), 2, str, fext);
+ str = rb_funcall(rb_cFile, id_basename, 2, str, fext);
return rb_class_new_instance(1, &str, rb_obj_class(self));
}
@@ -722,7 +790,7 @@ static VALUE
path_dirname(VALUE self)
{
VALUE str = get_strpath(self);
- str = rb_funcall(rb_cFile, rb_intern("dirname"), 1, str);
+ str = rb_funcall(rb_cFile, id_dirname, 1, str);
return rb_class_new_instance(1, &str, rb_obj_class(self));
}
@@ -735,7 +803,7 @@ static VALUE
path_extname(VALUE self)
{
VALUE str = get_strpath(self);
- return rb_funcall(rb_cFile, rb_intern("extname"), 1, str);
+ return rb_funcall(rb_cFile, id_extname, 1, str);
}
/*
@@ -749,9 +817,9 @@ path_expand_path(int argc, VALUE *argv, VALUE self)
VALUE str = get_strpath(self);
VALUE dname;
if (rb_scan_args(argc, argv, "01", &dname) == 0)
- str = rb_funcall(rb_cFile, rb_intern("expand_path"), 1, str);
+ str = rb_funcall(rb_cFile, id_expand_path, 1, str);
else
- str = rb_funcall(rb_cFile, rb_intern("expand_path"), 2, str, dname);
+ str = rb_funcall(rb_cFile, id_expand_path, 2, str, dname);
return rb_class_new_instance(1, &str, rb_obj_class(self));
}
@@ -765,7 +833,7 @@ path_split(VALUE self)
{
VALUE str = get_strpath(self);
VALUE ary, dirname, basename;
- ary = rb_funcall(rb_cFile, rb_intern("split"), 1, str);
+ ary = rb_funcall(rb_cFile, id_split, 1, str);
ary = rb_check_array_type(ary);
dirname = rb_ary_entry(ary, 0);
basename = rb_ary_entry(ary, 1);
@@ -780,7 +848,7 @@ path_split(VALUE self)
static VALUE
path_blockdev_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("blockdev?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_blockdev_p, 1, get_strpath(self));
}
/*
@@ -789,7 +857,7 @@ path_blockdev_p(VALUE self)
static VALUE
path_chardev_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("chardev?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_chardev_p, 1, get_strpath(self));
}
/*
@@ -798,7 +866,7 @@ path_chardev_p(VALUE self)
static VALUE
path_executable_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("executable?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_executable_p, 1, get_strpath(self));
}
/*
@@ -807,7 +875,7 @@ path_executable_p(VALUE self)
static VALUE
path_executable_real_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("executable_real?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_executable_real_p, 1, get_strpath(self));
}
/*
@@ -816,7 +884,7 @@ path_executable_real_p(VALUE self)
static VALUE
path_exist_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("exist?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_exist_p, 1, get_strpath(self));
}
/*
@@ -825,7 +893,7 @@ path_exist_p(VALUE self)
static VALUE
path_grpowned_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("grpowned?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_grpowned_p, 1, get_strpath(self));
}
/*
@@ -834,7 +902,7 @@ path_grpowned_p(VALUE self)
static VALUE
path_directory_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("directory?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_directory_p, 1, get_strpath(self));
}
/*
@@ -843,7 +911,7 @@ path_directory_p(VALUE self)
static VALUE
path_file_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("file?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_file_p, 1, get_strpath(self));
}
/*
@@ -852,7 +920,7 @@ path_file_p(VALUE self)
static VALUE
path_pipe_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("pipe?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_pipe_p, 1, get_strpath(self));
}
/*
@@ -861,7 +929,7 @@ path_pipe_p(VALUE self)
static VALUE
path_socket_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("socket?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_socket_p, 1, get_strpath(self));
}
/*
@@ -870,7 +938,7 @@ path_socket_p(VALUE self)
static VALUE
path_owned_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("owned?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_owned_p, 1, get_strpath(self));
}
/*
@@ -879,7 +947,7 @@ path_owned_p(VALUE self)
static VALUE
path_readable_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("readable?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_readable_p, 1, get_strpath(self));
}
/*
@@ -888,7 +956,7 @@ path_readable_p(VALUE self)
static VALUE
path_world_readable_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("world_readable?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_world_readable_p, 1, get_strpath(self));
}
/*
@@ -897,7 +965,7 @@ path_world_readable_p(VALUE self)
static VALUE
path_readable_real_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("readable_real?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_readable_real_p, 1, get_strpath(self));
}
/*
@@ -906,7 +974,7 @@ path_readable_real_p(VALUE self)
static VALUE
path_setuid_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("setuid?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_setuid_p, 1, get_strpath(self));
}
/*
@@ -915,7 +983,7 @@ path_setuid_p(VALUE self)
static VALUE
path_setgid_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("setgid?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_setgid_p, 1, get_strpath(self));
}
/*
@@ -924,7 +992,7 @@ path_setgid_p(VALUE self)
static VALUE
path_size(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("size"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_size, 1, get_strpath(self));
}
/*
@@ -933,7 +1001,7 @@ path_size(VALUE self)
static VALUE
path_size_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("size?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_size_p, 1, get_strpath(self));
}
/*
@@ -942,7 +1010,7 @@ path_size_p(VALUE self)
static VALUE
path_sticky_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("sticky?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_sticky_p, 1, get_strpath(self));
}
/*
@@ -951,7 +1019,7 @@ path_sticky_p(VALUE self)
static VALUE
path_symlink_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("symlink?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_symlink_p, 1, get_strpath(self));
}
/*
@@ -960,7 +1028,7 @@ path_symlink_p(VALUE self)
static VALUE
path_writable_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("writable?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_writable_p, 1, get_strpath(self));
}
/*
@@ -969,7 +1037,7 @@ path_writable_p(VALUE self)
static VALUE
path_world_writable_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("world_writable?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_world_writable_p, 1, get_strpath(self));
}
/*
@@ -978,7 +1046,7 @@ path_world_writable_p(VALUE self)
static VALUE
path_writable_real_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("writable_real?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_writable_real_p, 1, get_strpath(self));
}
/*
@@ -987,7 +1055,7 @@ path_writable_real_p(VALUE self)
static VALUE
path_zero_p(VALUE self)
{
- return rb_funcall(rb_mFileTest, rb_intern("zero?"), 1, get_strpath(self));
+ return rb_funcall(rb_mFileTest, id_zero_p, 1, get_strpath(self));
}
/*
@@ -1000,14 +1068,14 @@ path_empty_p(VALUE self)
{
VALUE path = get_strpath(self);
- if (RTEST(rb_funcall(rb_mFileTest, rb_intern("directory?"), 1, path)))
- return rb_funcall(rb_cDir, rb_intern("empty?"), 1, path);
+ if (RTEST(rb_funcall(rb_mFileTest, id_directory_p, 1, path)))
+ return rb_funcall(rb_cDir, id_empty_p, 1, path);
else
- return rb_funcall(rb_mFileTest, rb_intern("empty?"), 1, path);
+ return rb_funcall(rb_mFileTest, id_empty_p, 1, path);
}
static VALUE
-glob_i(RB_BLOCK_CALL_FUNC_ARGLIST(elt, klass))
+s_glob_i(RB_BLOCK_CALL_FUNC_ARGLIST(elt, klass))
{
return rb_yield(rb_class_new_instance(1, &elt, klass));
}
@@ -1015,8 +1083,8 @@ glob_i(RB_BLOCK_CALL_FUNC_ARGLIST(elt, klass))
/*
* Returns or yields Pathname objects.
*
- * Pathname.glob("config/" "*.rb")
- * #=> [#<Pathname:config/environment.rb>, #<Pathname:config/routes.rb>, ..]
+ * Pathname.glob("lib/i*.rb")
+ * #=> [#<Pathname:lib/ipaddr.rb>, #<Pathname:lib/irb.rb>]
*
* See Dir.glob.
*/
@@ -1028,12 +1096,12 @@ path_s_glob(int argc, VALUE *argv, VALUE klass)
n = rb_scan_args(argc, argv, "11", &args[0], &args[1]);
if (rb_block_given_p()) {
- return rb_block_call(rb_cDir, rb_intern("glob"), n, args, glob_i, klass);
+ return rb_block_call(rb_cDir, id_glob, n, args, s_glob_i, klass);
}
else {
VALUE ary;
long i;
- ary = rb_funcallv(rb_cDir, rb_intern("glob"), n, args);
+ ary = rb_funcallv(rb_cDir, id_glob, n, args);
ary = rb_convert_type(ary, T_ARRAY, "Array", "to_ary");
for (i = 0; i < RARRAY_LEN(ary); i++) {
VALUE elt = RARRAY_AREF(ary, i);
@@ -1044,6 +1112,54 @@ path_s_glob(int argc, VALUE *argv, VALUE klass)
}
}
+static VALUE
+glob_i(RB_BLOCK_CALL_FUNC_ARGLIST(elt, self))
+{
+ elt = rb_funcall(self, '+', 1, elt);
+ return rb_yield(elt);
+}
+
+/*
+ * Returns or yields Pathname objects.
+ *
+ * Pathname("ruby-2.4.2").glob("R*.md")
+ * #=> [#<Pathname:ruby-2.4.2/README.md>, #<Pathname:ruby-2.4.2/README.ja.md>]
+ *
+ * See Dir.glob.
+ * This method uses the +base+ keyword argument of Dir.glob.
+ */
+static VALUE
+path_glob(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[3];
+ int n;
+
+ n = rb_scan_args(argc, argv, "11", &args[0], &args[1]);
+ if (n == 1)
+ args[1] = INT2FIX(0);
+
+ args[2] = rb_hash_new();
+ rb_hash_aset(args[2], ID2SYM(id_base), get_strpath(self));
+
+ n = 3;
+
+ if (rb_block_given_p()) {
+ return rb_block_call(rb_cDir, id_glob, n, args, glob_i, self);
+ }
+ else {
+ VALUE ary;
+ long i;
+ ary = rb_funcallv(rb_cDir, id_glob, n, args);
+ ary = rb_convert_type(ary, T_ARRAY, "Array", "to_ary");
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ VALUE elt = RARRAY_AREF(ary, i);
+ elt = rb_funcall(self, '+', 1, elt);
+ rb_ary_store(ary, i, elt);
+ }
+ return ary;
+ }
+}
+
/*
* Returns the current working directory as a Pathname.
*
@@ -1056,7 +1172,7 @@ static VALUE
path_s_getwd(VALUE klass)
{
VALUE str;
- str = rb_funcall(rb_cDir, rb_intern("getwd"), 0);
+ str = rb_funcall(rb_cDir, id_getwd, 0);
return rb_class_new_instance(1, &str, klass);
}
@@ -1093,7 +1209,7 @@ path_entries(VALUE self)
long i;
klass = rb_obj_class(self);
str = get_strpath(self);
- ary = rb_funcall(rb_cDir, rb_intern("entries"), 1, str);
+ 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);
@@ -1114,9 +1230,9 @@ path_mkdir(int argc, VALUE *argv, VALUE self)
VALUE str = get_strpath(self);
VALUE vmode;
if (rb_scan_args(argc, argv, "01", &vmode) == 0)
- return rb_funcall(rb_cDir, rb_intern("mkdir"), 1, str);
+ return rb_funcall(rb_cDir, id_mkdir, 1, str);
else
- return rb_funcall(rb_cDir, rb_intern("mkdir"), 2, str, vmode);
+ return rb_funcall(rb_cDir, id_mkdir, 2, str, vmode);
}
/*
@@ -1127,7 +1243,7 @@ path_mkdir(int argc, VALUE *argv, VALUE self)
static VALUE
path_rmdir(VALUE self)
{
- return rb_funcall(rb_cDir, rb_intern("rmdir"), 1, get_strpath(self));
+ return rb_funcall(rb_cDir, id_rmdir, 1, get_strpath(self));
}
/*
@@ -1141,7 +1257,7 @@ path_opendir(VALUE self)
VALUE args[1];
args[0] = get_strpath(self);
- return rb_block_call(rb_cDir, rb_intern("open"), 1, args, 0, 0);
+ return rb_block_call(rb_cDir, id_open, 1, args, 0, 0);
}
static VALUE
@@ -1160,19 +1276,19 @@ path_each_entry(VALUE self)
VALUE args[1];
args[0] = get_strpath(self);
- return rb_block_call(rb_cDir, rb_intern("foreach"), 1, args, each_entry_i, rb_obj_class(self));
+ return rb_block_call(rb_cDir, id_foreach, 1, args, each_entry_i, rb_obj_class(self));
}
static VALUE
unlink_body(VALUE str)
{
- return rb_funcall(rb_cDir, rb_intern("unlink"), 1, str);
+ return rb_funcall(rb_cDir, id_unlink, 1, str);
}
static VALUE
unlink_rescue(VALUE str, VALUE errinfo)
{
- return rb_funcall(rb_cFile, rb_intern("unlink"), 1, str);
+ return rb_funcall(rb_cFile, id_unlink, 1, str);
}
/*
@@ -1182,7 +1298,7 @@ unlink_rescue(VALUE str, VALUE errinfo)
static VALUE
path_unlink(VALUE self)
{
- VALUE eENOTDIR = rb_const_get_at(rb_mErrno, rb_intern("ENOTDIR"));
+ VALUE eENOTDIR = rb_const_get_at(rb_mErrno, id_ENOTDIR);
VALUE str = get_strpath(self);
return rb_rescue2(unlink_body, str, unlink_rescue, str, eENOTDIR, (VALUE)0);
}
@@ -1392,8 +1508,7 @@ path_f_pathname(VALUE self, VALUE str)
void
Init_pathname(void)
{
- id_at_path = rb_intern("@path");
- id_to_path = rb_intern("to_path");
+ InitVM(pathname);
rb_cPathname = rb_define_class("Pathname", rb_cObject);
rb_define_method(rb_cPathname, "initialize", path_initialize, 1);
@@ -1472,6 +1587,7 @@ Init_pathname(void)
rb_define_singleton_method(rb_cPathname, "glob", path_s_glob, -1);
rb_define_singleton_method(rb_cPathname, "getwd", path_s_getwd, 0);
rb_define_singleton_method(rb_cPathname, "pwd", path_s_getwd, 0);
+ rb_define_method(rb_cPathname, "glob", path_glob, -1);
rb_define_method(rb_cPathname, "entries", path_entries, 0);
rb_define_method(rb_cPathname, "mkdir", path_mkdir, -1);
rb_define_method(rb_cPathname, "rmdir", path_rmdir, 0);
@@ -1482,3 +1598,78 @@ Init_pathname(void)
rb_undef_method(rb_cPathname, "=~");
rb_define_global_function("Pathname", path_f_pathname, 1);
}
+
+void
+InitVM_pathname(void)
+{
+#undef rb_intern
+ id_at_path = rb_intern("@path");
+ id_to_path = rb_intern("to_path");
+ id_ENOTDIR = rb_intern("ENOTDIR");
+ id_atime = rb_intern("atime");
+ id_basename = rb_intern("basename");
+ id_base = rb_intern("base");
+ id_binread = rb_intern("binread");
+ id_binwrite = rb_intern("binwrite");
+ id_birthtime = rb_intern("birthtime");
+ id_blockdev_p = rb_intern("blockdev?");
+ id_chardev_p = rb_intern("chardev?");
+ id_chmod = rb_intern("chmod");
+ id_chown = rb_intern("chown");
+ id_ctime = rb_intern("ctime");
+ id_directory_p = rb_intern("directory?");
+ id_dirname = rb_intern("dirname");
+ id_empty_p = rb_intern("empty?");
+ id_entries = rb_intern("entries");
+ id_executable_p = rb_intern("executable?");
+ id_executable_real_p = rb_intern("executable_real?");
+ id_exist_p = rb_intern("exist?");
+ id_expand_path = rb_intern("expand_path");
+ id_extname = rb_intern("extname");
+ id_file_p = rb_intern("file?");
+ id_fnmatch = rb_intern("fnmatch");
+ id_foreach = rb_intern("foreach");
+ id_ftype = rb_intern("ftype");
+ id_getwd = rb_intern("getwd");
+ id_glob = rb_intern("glob");
+ id_grpowned_p = rb_intern("grpowned?");
+ id_lchmod = rb_intern("lchmod");
+ id_lchown = rb_intern("lchown");
+ id_link = rb_intern("link");
+ id_lstat = rb_intern("lstat");
+ id_mkdir = rb_intern("mkdir");
+ id_mtime = rb_intern("mtime");
+ id_open = rb_intern("open");
+ id_owned_p = rb_intern("owned?");
+ id_pipe_p = rb_intern("pipe?");
+ id_read = rb_intern("read");
+ id_readable_p = rb_intern("readable?");
+ id_readable_real_p = rb_intern("readable_real?");
+ id_readlines = rb_intern("readlines");
+ id_readlink = rb_intern("readlink");
+ id_realdirpath = rb_intern("realdirpath");
+ id_realpath = rb_intern("realpath");
+ id_rename = rb_intern("rename");
+ id_rmdir = rb_intern("rmdir");
+ id_setgid_p = rb_intern("setgid?");
+ id_setuid_p = rb_intern("setuid?");
+ id_size = rb_intern("size");
+ id_size_p = rb_intern("size?");
+ id_socket_p = rb_intern("socket?");
+ id_split = rb_intern("split");
+ id_stat = rb_intern("stat");
+ id_sticky_p = rb_intern("sticky?");
+ id_sub = rb_intern("sub");
+ id_symlink = rb_intern("symlink");
+ id_symlink_p = rb_intern("symlink?");
+ id_sysopen = rb_intern("sysopen");
+ id_truncate = rb_intern("truncate");
+ id_unlink = rb_intern("unlink");
+ id_utime = rb_intern("utime");
+ id_world_readable_p = rb_intern("world_readable?");
+ id_world_writable_p = rb_intern("world_writable?");
+ id_writable_p = rb_intern("writable?");
+ id_writable_real_p = rb_intern("writable_real?");
+ id_write = rb_intern("write");
+ id_zero_p = rb_intern("zero?");
+}
diff --git a/ext/psych/.gitignore b/ext/psych/.gitignore
deleted file mode 100644
index 836058c169..0000000000
--- a/ext/psych/.gitignore
+++ /dev/null
@@ -1,11 +0,0 @@
-/api.c
-/config.h
-/dumper.c
-/emitter.c
-/loader.c
-/parser.c
-/reader.c
-/scanner.c
-/writer.c
-/yaml.h
-/yaml_private.h
diff --git a/ext/psych/depend b/ext/psych/depend
index 7505e950bc..c5ca1f6b13 100644
--- a/ext/psych/depend
+++ b/ext/psych/depend
@@ -6,6 +6,7 @@ psych.o: $(hdrdir)/ruby/defines.h
psych.o: $(hdrdir)/ruby/encoding.h
psych.o: $(hdrdir)/ruby/intern.h
psych.o: $(hdrdir)/ruby/missing.h
+psych.o: $(hdrdir)/ruby/onigmo.h
psych.o: $(hdrdir)/ruby/oniguruma.h
psych.o: $(hdrdir)/ruby/ruby.h
psych.o: $(hdrdir)/ruby/st.h
@@ -24,6 +25,7 @@ psych_emitter.o: $(hdrdir)/ruby/defines.h
psych_emitter.o: $(hdrdir)/ruby/encoding.h
psych_emitter.o: $(hdrdir)/ruby/intern.h
psych_emitter.o: $(hdrdir)/ruby/missing.h
+psych_emitter.o: $(hdrdir)/ruby/onigmo.h
psych_emitter.o: $(hdrdir)/ruby/oniguruma.h
psych_emitter.o: $(hdrdir)/ruby/ruby.h
psych_emitter.o: $(hdrdir)/ruby/st.h
@@ -42,6 +44,7 @@ psych_parser.o: $(hdrdir)/ruby/defines.h
psych_parser.o: $(hdrdir)/ruby/encoding.h
psych_parser.o: $(hdrdir)/ruby/intern.h
psych_parser.o: $(hdrdir)/ruby/missing.h
+psych_parser.o: $(hdrdir)/ruby/onigmo.h
psych_parser.o: $(hdrdir)/ruby/oniguruma.h
psych_parser.o: $(hdrdir)/ruby/ruby.h
psych_parser.o: $(hdrdir)/ruby/st.h
@@ -60,6 +63,7 @@ psych_to_ruby.o: $(hdrdir)/ruby/defines.h
psych_to_ruby.o: $(hdrdir)/ruby/encoding.h
psych_to_ruby.o: $(hdrdir)/ruby/intern.h
psych_to_ruby.o: $(hdrdir)/ruby/missing.h
+psych_to_ruby.o: $(hdrdir)/ruby/onigmo.h
psych_to_ruby.o: $(hdrdir)/ruby/oniguruma.h
psych_to_ruby.o: $(hdrdir)/ruby/ruby.h
psych_to_ruby.o: $(hdrdir)/ruby/st.h
@@ -78,6 +82,7 @@ psych_yaml_tree.o: $(hdrdir)/ruby/defines.h
psych_yaml_tree.o: $(hdrdir)/ruby/encoding.h
psych_yaml_tree.o: $(hdrdir)/ruby/intern.h
psych_yaml_tree.o: $(hdrdir)/ruby/missing.h
+psych_yaml_tree.o: $(hdrdir)/ruby/onigmo.h
psych_yaml_tree.o: $(hdrdir)/ruby/oniguruma.h
psych_yaml_tree.o: $(hdrdir)/ruby/ruby.h
psych_yaml_tree.o: $(hdrdir)/ruby/st.h
diff --git a/ext/psych/extconf.rb b/ext/psych/extconf.rb
index be33d35a5e..6d8390ebe5 100644
--- a/ext/psych/extconf.rb
+++ b/ext/psych/extconf.rb
@@ -1,5 +1,5 @@
# -*- coding: us-ascii -*-
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'mkmf'
require 'fileutils'
diff --git a/ext/psych/lib/psych.rb b/ext/psych/lib/psych.rb
index ecef46c024..2a2ec2af43 100644
--- a/ext/psych/lib/psych.rb
+++ b/ext/psych/lib/psych.rb
@@ -1,10 +1,20 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
+require 'psych/versions'
case RUBY_ENGINE
when 'jruby'
require 'psych_jars'
- org.jruby.ext.psych.PsychLibrary.new.load(JRuby.runtime, false)
+ if JRuby::Util.respond_to?(:load_ext)
+ JRuby::Util.load_ext('org.jruby.ext.psych.PsychLibrary')
+ else
+ require 'java'; require 'jruby'
+ org.jruby.ext.psych.PsychLibrary.new.load(JRuby.runtime, false)
+ end
else
- require 'psych.so'
+ begin
+ require "#{RUBY_VERSION[/\d+\.\d+/]}/psych.so"
+ rescue LoadError
+ require 'psych.so'
+ end
end
require 'psych/nodes'
require 'psych/streaming'
@@ -16,7 +26,6 @@ require 'psych/omap'
require 'psych/set'
require 'psych/coder'
require 'psych/core_ext'
-require 'psych/deprecated'
require 'psych/stream'
require 'psych/json/tree_builder'
require 'psych/json/stream'
@@ -27,7 +36,7 @@ require 'psych/class_loader'
# = Overview
#
# Psych is a YAML parser and emitter.
-# Psych leverages libyaml [Home page: http://pyyaml.org/wiki/LibYAML]
+# Psych leverages libyaml [Home page: https://pyyaml.org/wiki/LibYAML]
# or [HG repo: https://bitbucket.org/xi/libyaml] for its YAML parsing
# and emitting capabilities. In addition to wrapping libyaml, Psych also
# knows how to serialize and de-serialize most Ruby objects to and from
@@ -194,12 +203,13 @@ require 'psych/class_loader'
#
# ==== Receiving an events stream
#
-# parser = Psych::Parser.new(Psych::Handlers::Recorder.new)
+# recorder = Psych::Handlers::Recorder.new
+# parser = Psych::Parser.new(recorder)
#
# parser.parse("---\n - a\n - b")
-# parser.events # => [list of [event, args] lists]
-# # event is one of: Psych::Handler::EVENTS
-# # args are the arguments passed to the event
+# recorder.events # => [list of [event, args] lists]
+# # event is one of: Psych::Handler::EVENTS
+# # args are the arguments passed to the event
#
# === Emitting
#
@@ -223,19 +233,18 @@ require 'psych/class_loader'
# # => "a"
module Psych
- # The version is Psych you're using
- VERSION = '2.1.1'
-
# The version of libyaml Psych is using
LIBYAML_VERSION = Psych.libyaml_version.join '.'
-
- FALLBACK = Struct.new :to_ruby # :nodoc:
+ # Deprecation guard
+ NOT_GIVEN = Object.new
+ private_constant :NOT_GIVEN
###
# Load +yaml+ in to a Ruby data structure. If multiple documents are
# provided, the object contained in the first document will be returned.
- # +filename+ will be used in the exception message if any exception is raised
- # while parsing.
+ # +filename+ will be used in the exception message if any exception
+ # is raised while parsing. If +yaml+ is empty, it returns
+ # the specified +fallback+ return value, which defaults to +false+.
#
# Raises a Psych::SyntaxError when a YAML syntax error is detected.
#
@@ -245,14 +254,31 @@ module Psych
# Psych.load("---\n - a\n - b") # => ['a', 'b']
#
# begin
- # Psych.load("--- `", "file.txt")
+ # Psych.load("--- `", filename: "file.txt")
# rescue Psych::SyntaxError => ex
# ex.file # => 'file.txt'
# ex.message # => "(file.txt): found character that cannot start any token"
# end
- def self.load yaml, filename = nil, fallback = false
- result = parse(yaml, filename, fallback)
- result ? result.to_ruby : result
+ #
+ # When the optional +symbolize_names+ keyword argument is set to a
+ # true value, returns symbols for keys in Hash objects (default: strings).
+ #
+ # Psych.load("---\n foo: bar") # => {"foo"=>"bar"}
+ # Psych.load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
+ #
+ # Raises a TypeError when `yaml` parameter is NilClass
+ #
+ def self.load yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: false, symbolize_names: false
+ if legacy_filename != NOT_GIVEN
+ warn_with_uplevel 'Passing filename with the 2nd argument of Psych.load is deprecated. Use keyword argument like Psych.load(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
+ filename = legacy_filename
+ end
+
+ result = parse(yaml, filename: filename)
+ return fallback unless result
+ result = result.to_ruby if result
+ symbolize_names!(result) if symbolize_names
+ result
end
###
@@ -268,40 +294,72 @@ module Psych
# * Hash
#
# Recursive data structures are not allowed by default. Arbitrary classes
- # can be allowed by adding those classes to the +whitelist+. They are
+ # can be allowed by adding those classes to the +permitted_classes+ keyword argument. They are
# additive. For example, to allow Date deserialization:
#
- # Psych.safe_load(yaml, [Date])
+ # Psych.safe_load(yaml, permitted_classes: [Date])
#
# Now the Date class can be loaded in addition to the classes listed above.
#
- # Aliases can be explicitly allowed by changing the +aliases+ parameter.
+ # Aliases can be explicitly allowed by changing the +aliases+ keyword argument.
# For example:
#
# x = []
# x << x
# yaml = Psych.dump x
# Psych.safe_load yaml # => raises an exception
- # Psych.safe_load yaml, [], [], true # => loads the aliases
+ # Psych.safe_load yaml, aliases: true # => loads the aliases
#
# A Psych::DisallowedClass exception will be raised if the yaml contains a
- # class that isn't in the whitelist.
+ # class that isn't in the +permitted_classes+ list.
#
# A Psych::BadAlias exception will be raised if the yaml contains aliases
- # but the +aliases+ parameter is set to false.
- def self.safe_load yaml, whitelist_classes = [], whitelist_symbols = [], aliases = false, filename = nil
- result = parse(yaml, filename)
- return unless result
+ # but the +aliases+ keyword argument is set to false.
+ #
+ # +filename+ will be used in the exception message if any exception is raised
+ # while parsing.
+ #
+ # When the optional +symbolize_names+ keyword argument is set to a
+ # true value, returns symbols for keys in Hash objects (default: strings).
+ #
+ # Psych.safe_load("---\n foo: bar") # => {"foo"=>"bar"}
+ # Psych.safe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
+ #
+ def self.safe_load yaml, legacy_permitted_classes = NOT_GIVEN, legacy_permitted_symbols = NOT_GIVEN, legacy_aliases = NOT_GIVEN, legacy_filename = NOT_GIVEN, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false
+ if legacy_permitted_classes != NOT_GIVEN
+ warn_with_uplevel 'Passing permitted_classes with the 2nd argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, permitted_classes: ...) instead.', uplevel: 1 if $VERBOSE
+ permitted_classes = legacy_permitted_classes
+ end
- class_loader = ClassLoader::Restricted.new(whitelist_classes.map(&:to_s),
- whitelist_symbols.map(&:to_s))
- scanner = ScalarScanner.new class_loader
- if aliases
- visitor = Visitors::ToRuby.new scanner, class_loader
- else
- visitor = Visitors::NoAliasRuby.new scanner, class_loader
+ if legacy_permitted_symbols != NOT_GIVEN
+ warn_with_uplevel 'Passing permitted_symbols with the 3rd argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, permitted_symbols: ...) instead.', uplevel: 1 if $VERBOSE
+ permitted_symbols = legacy_permitted_symbols
end
- visitor.accept result
+
+ if legacy_aliases != NOT_GIVEN
+ warn_with_uplevel 'Passing aliases with the 4th argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, aliases: ...) instead.', uplevel: 1 if $VERBOSE
+ aliases = legacy_aliases
+ end
+
+ if legacy_filename != NOT_GIVEN
+ warn_with_uplevel 'Passing filename with the 5th argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
+ filename = legacy_filename
+ end
+
+ result = parse(yaml, filename: filename)
+ return fallback unless result
+
+ class_loader = ClassLoader::Restricted.new(permitted_classes.map(&:to_s),
+ permitted_symbols.map(&:to_s))
+ scanner = ScalarScanner.new class_loader
+ visitor = if aliases
+ Visitors::ToRuby.new scanner, class_loader
+ else
+ Visitors::NoAliasRuby.new scanner, class_loader
+ end
+ result = visitor.accept result
+ symbolize_names!(result) if symbolize_names
+ result
end
###
@@ -316,28 +374,40 @@ module Psych
# Psych.parse("---\n - a\n - b") # => #<Psych::Nodes::Document:0x00>
#
# begin
- # Psych.parse("--- `", "file.txt")
+ # Psych.parse("--- `", filename: "file.txt")
# rescue Psych::SyntaxError => ex
# ex.file # => 'file.txt'
# ex.message # => "(file.txt): found character that cannot start any token"
# end
#
# See Psych::Nodes for more information about YAML AST.
- def self.parse yaml, filename = nil, fallback = false
- parse_stream(yaml, filename) do |node|
+ def self.parse yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: NOT_GIVEN
+ if legacy_filename != NOT_GIVEN
+ warn_with_uplevel 'Passing filename with the 2nd argument of Psych.parse is deprecated. Use keyword argument like Psych.parse(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
+ filename = legacy_filename
+ end
+
+ parse_stream(yaml, filename: filename) do |node|
return node
end
- fallback
+
+ if fallback != NOT_GIVEN
+ warn_with_uplevel 'Passing the `fallback` keyword argument of Psych.parse is deprecated.', uplevel: 1 if $VERBOSE
+ fallback
+ else
+ false
+ end
end
###
# Parse a file at +filename+. Returns the Psych::Nodes::Document.
#
# Raises a Psych::SyntaxError when a YAML syntax error is detected.
- def self.parse_file filename
- File.open filename, 'r:bom|utf-8' do |f|
- parse f, filename
+ def self.parse_file filename, fallback: false
+ result = File.open filename, 'r:bom|utf-8' do |f|
+ parse f, filename: filename
end
+ result || fallback
end
###
@@ -366,14 +436,21 @@ module Psych
# end
#
# begin
- # Psych.parse_stream("--- `", "file.txt")
+ # Psych.parse_stream("--- `", filename: "file.txt")
# rescue Psych::SyntaxError => ex
# ex.file # => 'file.txt'
# ex.message # => "(file.txt): found character that cannot start any token"
# end
#
+ # Raises a TypeError when NilClass is passed.
+ #
# See Psych::Nodes for more information about YAML AST.
- def self.parse_stream yaml, filename = nil, &block
+ def self.parse_stream yaml, legacy_filename = NOT_GIVEN, filename: nil, &block
+ if legacy_filename != NOT_GIVEN
+ warn_with_uplevel 'Passing filename with the 2nd argument of Psych.parse_stream is deprecated. Use keyword argument like Psych.parse_stream(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
+ filename = legacy_filename
+ end
+
if block_given?
parser = Psych::Parser.new(Handlers::DocumentStream.new(&block))
parser.parse yaml, filename
@@ -395,6 +472,24 @@ module Psych
# to control the output format. If an IO object is passed in, the YAML will
# be dumped to that IO object.
#
+ # Currently supported options are:
+ #
+ # [<tt>:indentation</tt>] Number of space characters used to indent.
+ # Acceptable value should be in <tt>0..9</tt> range,
+ # otherwise option is ignored.
+ #
+ # Default: <tt>2</tt>.
+ # [<tt>:line_width</tt>] Max character to wrap line at.
+ #
+ # Default: <tt>0</tt> (meaning "wrap at 81").
+ # [<tt>:canonical</tt>] Write "canonical" YAML form (very verbose, yet
+ # strictly formal).
+ #
+ # Default: <tt>false</tt>.
+ # [<tt>:header</tt>] Write <tt>%YAML [version]</tt> at the beginning of document.
+ #
+ # Default: <tt>false</tt>.
+ #
# Example:
#
# # Dump an array, get back a YAML string
@@ -404,10 +499,10 @@ module Psych
# Psych.dump(['a', 'b'], StringIO.new) # => #<StringIO:0x000001009d0890>
#
# # Dump an array with indentation set
- # Psych.dump(['a', ['b']], :indentation => 3) # => "---\n- a\n- - b\n"
+ # Psych.dump(['a', ['b']], indentation: 3) # => "---\n- a\n- - b\n"
#
# # Dump an array to an IO with indentation set
- # Psych.dump(['a', ['b']], StringIO.new, :indentation => 3)
+ # Psych.dump(['a', ['b']], StringIO.new, indentation: 3)
def self.dump o, io = nil, options = {}
if Hash === io
options = io
@@ -456,23 +551,31 @@ module Psych
# end
# list # => ['foo', 'bar']
#
- def self.load_stream yaml, filename = nil
- if block_given?
- parse_stream(yaml, filename) do |node|
- yield node.to_ruby
- end
- else
- parse_stream(yaml, filename).children.map { |child| child.to_ruby }
+ def self.load_stream yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: []
+ if legacy_filename != NOT_GIVEN
+ warn_with_uplevel 'Passing filename with the 2nd argument of Psych.load_stream is deprecated. Use keyword argument like Psych.load_stream(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
+ filename = legacy_filename
end
+
+ result = if block_given?
+ parse_stream(yaml, filename: filename) do |node|
+ yield node.to_ruby
+ end
+ else
+ parse_stream(yaml, filename: filename).children.map(&:to_ruby)
+ end
+
+ return fallback if result.is_a?(Array) && result.empty?
+ result
end
###
# Load the document contained in +filename+. Returns the yaml contained in
# +filename+ as a Ruby object, or if the file is empty, it returns
- # the specified default return value, which defaults to an empty Hash
- def self.load_file filename, fallback = false
+ # the specified +fallback+ return value, which defaults to +false+.
+ def self.load_file filename, fallback: false
File.open(filename, 'r:bom|utf-8') { |f|
- self.load f, filename, FALLBACK.new(fallback)
+ self.load f, filename: filename, fallback: fallback
}
end
@@ -501,6 +604,34 @@ module Psych
@dump_tags[klass] = tag
end
+ def self.symbolize_names!(result)
+ case result
+ when Hash
+ result.keys.each do |key|
+ result[key.to_sym] = symbolize_names!(result.delete(key))
+ end
+ when Array
+ result.map! { |r| symbolize_names!(r) }
+ end
+ result
+ end
+ private_class_method :symbolize_names!
+
+ # Workaround for emulating `warn '...', uplevel: 1` in Ruby 2.4 or lower.
+ def self.warn_with_uplevel(message, uplevel: 1)
+ at = parse_caller(caller[uplevel]).join(':')
+ warn "#{at}: #{message}"
+ end
+
+ def self.parse_caller(at)
+ if /^(.+?):(\d+)(?::in `.*')?/ =~ at
+ file = $1
+ line = $2.to_i
+ [file, line]
+ end
+ end
+ private_class_method :warn_with_uplevel, :parse_caller
+
class << self
attr_accessor :load_tags
attr_accessor :dump_tags
diff --git a/ext/psych/lib/psych/class_loader.rb b/ext/psych/lib/psych/class_loader.rb
index ba756f7ea7..cfca86845a 100644
--- a/ext/psych/lib/psych/class_loader.rb
+++ b/ext/psych/lib/psych/class_loader.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/omap'
require 'psych/set'
diff --git a/ext/psych/lib/psych/coder.rb b/ext/psych/lib/psych/coder.rb
index 26005f57b4..96a9c3fbad 100644
--- a/ext/psych/lib/psych/coder.rb
+++ b/ext/psych/lib/psych/coder.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
###
# If an object defines +encode_with+, then an instance of Psych::Coder will
diff --git a/ext/psych/lib/psych/core_ext.rb b/ext/psych/lib/psych/core_ext.rb
index 1a98279afd..81055cc501 100644
--- a/ext/psych/lib/psych/core_ext.rb
+++ b/ext/psych/lib/psych/core_ext.rb
@@ -1,34 +1,17 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
class Object
def self.yaml_tag url
Psych.add_tag(url, self)
end
- # FIXME: rename this to "to_yaml" when syck is removed
-
###
# call-seq: to_yaml(options = {})
#
# Convert an object to YAML. See Psych.dump for more information on the
# available +options+.
- def psych_to_yaml options = {}
+ def to_yaml options = {}
Psych.dump self, options
end
- remove_method :to_yaml rescue nil
- alias :to_yaml :psych_to_yaml
-end
-
-class Module
- def psych_yaml_as url
- return if caller[0].end_with?('rubytypes.rb')
- if $VERBOSE
- warn "#{caller[0]}: yaml_as is deprecated, please use yaml_tag"
- end
- Psych.add_tag(url, self)
- end
-
- remove_method :yaml_as rescue nil
- alias :yaml_as :psych_yaml_as
end
if defined?(::IRB)
diff --git a/ext/psych/lib/psych/deprecated.rb b/ext/psych/lib/psych/deprecated.rb
deleted file mode 100644
index 165d2102b4..0000000000
--- a/ext/psych/lib/psych/deprecated.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-# frozen_string_literal: false
-require 'date'
-
-module Psych
- DEPRECATED = __FILE__ # :nodoc:
-
- module DeprecatedMethods # :nodoc:
- attr_accessor :taguri
- attr_accessor :to_yaml_style
- end
-
- def self.quick_emit thing, opts = {}, &block # :nodoc:
- warn "#{caller[0]}: YAML.quick_emit is deprecated" if $VERBOSE && !caller[0].start_with?(File.dirname(__FILE__))
- target = eval 'self', block.binding
- target.extend DeprecatedMethods
- metaclass = class << target; self; end
- metaclass.send(:define_method, :encode_with) do |coder|
- target.taguri = coder.tag
- target.to_yaml_style = coder.style
- block.call coder
- end
- target.psych_to_yaml unless opts[:nodump]
- end
-
- # This method is deprecated, use Psych.load_stream instead.
- def self.load_documents yaml, &block
- if $VERBOSE
- warn "#{caller[0]}: load_documents is deprecated, use load_stream"
- end
- list = load_stream yaml
- return list unless block_given?
- list.each(&block)
- end
-
- def self.detect_implicit thing
- warn "#{caller[0]}: detect_implicit is deprecated" if $VERBOSE
- return '' unless String === thing
- return 'null' if '' == thing
- ss = ScalarScanner.new(ClassLoader.new)
- ss.tokenize(thing).class.name.downcase
- end
-
- def self.add_ruby_type type_tag, &block
- warn "#{caller[0]}: add_ruby_type is deprecated, use add_domain_type" if $VERBOSE
- domain = 'ruby.yaml.org,2002'
- key = ['tag', domain, type_tag].join ':'
- @domain_types[key] = [key, block]
- end
-
- def self.add_private_type type_tag, &block
- warn "#{caller[0]}: add_private_type is deprecated, use add_domain_type" if $VERBOSE
- domain = 'x-private'
- key = [domain, type_tag].join ':'
- @domain_types[key] = [key, block]
- end
-
- def self.tagurize thing
- warn "#{caller[0]}: add_private_type is deprecated, use add_domain_type" if $VERBOSE
- return thing unless String === thing
- "tag:yaml.org,2002:#{thing}"
- end
-
- def self.read_type_class type, reference
- warn "#{caller[0]}: read_type_class is deprecated" if $VERBOSE
- _, _, type, name = type.split ':', 4
-
- reference = name.split('::').inject(reference) do |k,n|
- k.const_get(n.to_sym)
- end if name
- [type, reference]
- end
-
- def self.object_maker klass, hash
- warn "#{caller[0]}: object_maker is deprecated" if $VERBOSE
- klass.allocate.tap do |obj|
- hash.each { |k,v| obj.instance_variable_set(:"@#{k}", v) }
- end
- end
-end
-
-class Object
- undef :to_yaml_properties rescue nil
- def to_yaml_properties # :nodoc:
- instance_variables
- end
-end
diff --git a/ext/psych/lib/psych/exception.rb b/ext/psych/lib/psych/exception.rb
index 83c3d7fa82..fac0c42b9f 100644
--- a/ext/psych/lib/psych/exception.rb
+++ b/ext/psych/lib/psych/exception.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
class Exception < RuntimeError
end
diff --git a/ext/psych/lib/psych/handler.rb b/ext/psych/lib/psych/handler.rb
index 1ab5f73e95..8f23e366fa 100644
--- a/ext/psych/lib/psych/handler.rb
+++ b/ext/psych/lib/psych/handler.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
###
# Psych::Handler is an abstract base class that defines the events used
@@ -105,7 +105,7 @@ module Psych
# - first element
# - *ponies
#
- # &ponies is the achor, *ponies is the alias. In this case, alias is
+ # &ponies is the anchor, *ponies is the alias. In this case, alias is
# called with "ponies".
def alias anchor
end
@@ -242,6 +242,11 @@ module Psych
end
###
+ # Called before each event with line/column information.
+ def event_location(start_line, start_column, end_line, end_column)
+ end
+
+ ###
# Is this handler a streaming handler?
def streaming?
false
diff --git a/ext/psych/lib/psych/handlers/document_stream.rb b/ext/psych/lib/psych/handlers/document_stream.rb
index c43b39ebc5..67da794093 100644
--- a/ext/psych/lib/psych/handlers/document_stream.rb
+++ b/ext/psych/lib/psych/handlers/document_stream.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/tree_builder'
module Psych
diff --git a/ext/psych/lib/psych/handlers/recorder.rb b/ext/psych/lib/psych/handlers/recorder.rb
index 341b81dec4..a8fc7b1144 100644
--- a/ext/psych/lib/psych/handlers/recorder.rb
+++ b/ext/psych/lib/psych/handlers/recorder.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/handler'
module Psych
diff --git a/ext/psych/lib/psych/json/ruby_events.rb b/ext/psych/lib/psych/json/ruby_events.rb
index 478eb667c7..17b7ddc386 100644
--- a/ext/psych/lib/psych/json/ruby_events.rb
+++ b/ext/psych/lib/psych/json/ruby_events.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
module JSON
module RubyEvents # :nodoc:
diff --git a/ext/psych/lib/psych/json/stream.rb b/ext/psych/lib/psych/json/stream.rb
index 83b7e13655..2ebd3d7a66 100644
--- a/ext/psych/lib/psych/json/stream.rb
+++ b/ext/psych/lib/psych/json/stream.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/json/ruby_events'
require 'psych/json/yaml_events'
diff --git a/ext/psych/lib/psych/json/tree_builder.rb b/ext/psych/lib/psych/json/tree_builder.rb
index 2f94b8c252..5c2ee8ca25 100644
--- a/ext/psych/lib/psych/json/tree_builder.rb
+++ b/ext/psych/lib/psych/json/tree_builder.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/json/yaml_events'
module Psych
diff --git a/ext/psych/lib/psych/json/yaml_events.rb b/ext/psych/lib/psych/json/yaml_events.rb
index 07f64737c5..eb973f5361 100644
--- a/ext/psych/lib/psych/json/yaml_events.rb
+++ b/ext/psych/lib/psych/json/yaml_events.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
module JSON
module YAMLEvents # :nodoc:
diff --git a/ext/psych/lib/psych/nodes.rb b/ext/psych/lib/psych/nodes.rb
index 01573b509b..5842c2e3e5 100644
--- a/ext/psych/lib/psych/nodes.rb
+++ b/ext/psych/lib/psych/nodes.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/nodes/node'
require 'psych/nodes/stream'
require 'psych/nodes/document'
diff --git a/ext/psych/lib/psych/nodes/alias.rb b/ext/psych/lib/psych/nodes/alias.rb
index 716a00d62f..6da655f0fd 100644
--- a/ext/psych/lib/psych/nodes/alias.rb
+++ b/ext/psych/lib/psych/nodes/alias.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
module Nodes
###
@@ -14,6 +14,8 @@ module Psych
def initialize anchor
@anchor = anchor
end
+
+ def alias?; true; end
end
end
end
diff --git a/ext/psych/lib/psych/nodes/document.rb b/ext/psych/lib/psych/nodes/document.rb
index 7234fef1d8..f57410d636 100644
--- a/ext/psych/lib/psych/nodes/document.rb
+++ b/ext/psych/lib/psych/nodes/document.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
module Nodes
###
@@ -56,6 +56,8 @@ module Psych
def root
children.first
end
+
+ def document?; true; end
end
end
end
diff --git a/ext/psych/lib/psych/nodes/mapping.rb b/ext/psych/lib/psych/nodes/mapping.rb
index 4c11df8cd6..d49678cb0e 100644
--- a/ext/psych/lib/psych/nodes/mapping.rb
+++ b/ext/psych/lib/psych/nodes/mapping.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
module Nodes
###
@@ -52,6 +52,8 @@ module Psych
@implicit = implicit
@style = style
end
+
+ def mapping?; true; end
end
end
end
diff --git a/ext/psych/lib/psych/nodes/node.rb b/ext/psych/lib/psych/nodes/node.rb
index e3621dc451..f59fb8916b 100644
--- a/ext/psych/lib/psych/nodes/node.rb
+++ b/ext/psych/lib/psych/nodes/node.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'stringio'
require 'psych/class_loader'
require 'psych/scalar_scanner'
@@ -17,6 +17,18 @@ module Psych
# An associated tag
attr_reader :tag
+ # The line number where this node start
+ attr_accessor :start_line
+
+ # The column number where this node start
+ attr_accessor :start_column
+
+ # The line number where this node ends
+ attr_accessor :end_line
+
+ # The column number where this node ends
+ attr_accessor :end_column
+
# Create a new Psych::Nodes::Node
def initialize
@children = []
@@ -51,6 +63,13 @@ module Psych
io
end
alias :to_yaml :yaml
+
+ def alias?; false; end
+ def document?; false; end
+ def mapping?; false; end
+ def scalar?; false; end
+ def sequence?; false; end
+ def stream?; false; end
end
end
end
diff --git a/ext/psych/lib/psych/nodes/scalar.rb b/ext/psych/lib/psych/nodes/scalar.rb
index ee5570518e..e2616b6a84 100644
--- a/ext/psych/lib/psych/nodes/scalar.rb
+++ b/ext/psych/lib/psych/nodes/scalar.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
module Nodes
###
@@ -63,6 +63,8 @@ module Psych
@quoted = quoted
@style = style
end
+
+ def scalar?; true; end
end
end
end
diff --git a/ext/psych/lib/psych/nodes/sequence.rb b/ext/psych/lib/psych/nodes/sequence.rb
index 1096469567..740f1938a4 100644
--- a/ext/psych/lib/psych/nodes/sequence.rb
+++ b/ext/psych/lib/psych/nodes/sequence.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
module Nodes
###
@@ -77,6 +77,8 @@ module Psych
@implicit = implicit
@style = style
end
+
+ def sequence?; true; end
end
end
end
diff --git a/ext/psych/lib/psych/nodes/stream.rb b/ext/psych/lib/psych/nodes/stream.rb
index 559b0846e7..b525217821 100644
--- a/ext/psych/lib/psych/nodes/stream.rb
+++ b/ext/psych/lib/psych/nodes/stream.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
module Nodes
###
@@ -33,6 +33,8 @@ module Psych
super()
@encoding = encoding
end
+
+ def stream?; true; end
end
end
end
diff --git a/ext/psych/lib/psych/omap.rb b/ext/psych/lib/psych/omap.rb
index 233b945c4a..29cde0be50 100644
--- a/ext/psych/lib/psych/omap.rb
+++ b/ext/psych/lib/psych/omap.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
class Omap < ::Hash
end
diff --git a/ext/psych/lib/psych/parser.rb b/ext/psych/lib/psych/parser.rb
index 242512f89f..39bc8289be 100644
--- a/ext/psych/lib/psych/parser.rb
+++ b/ext/psych/lib/psych/parser.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
###
# YAML event parser class. This class parses a YAML document and calls
diff --git a/ext/psych/lib/psych/scalar_scanner.rb b/ext/psych/lib/psych/scalar_scanner.rb
index a849359d18..29c156c212 100644
--- a/ext/psych/lib/psych/scalar_scanner.rb
+++ b/ext/psych/lib/psych/scalar_scanner.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'strscan'
module Psych
@@ -10,7 +10,6 @@ module Psych
# Taken from http://yaml.org/type/float.html
FLOAT = /^(?:[-+]?([0-9][0-9_,]*)?\.[0-9]*([eE][-+][0-9]+)?(?# base 10)
- |[-+]?[0-9][0-9_,]*(:[0-5]?[0-9])+\.[0-9_]*(?# base 60)
|[-+]?\.(inf|Inf|INF)(?# infinity)
|\.(nan|NaN|NAN)(?# not a number))$/x
@@ -83,13 +82,13 @@ module Psych
else
@symbol_cache[string] = class_loader.symbolize(string.sub(/^:/, ''))
end
- when /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+$/
+ when /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}$/
i = 0
string.split(':').each_with_index do |n,e|
i += (n.to_i * 60 ** (e - 2).abs)
end
i
- when /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+\.[0-9_]*$/
+ when /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}\.[0-9_]*$/
i = 0
string.split(':').each_with_index do |n,e|
i += (n.to_f * 60 ** (e - 2).abs)
@@ -144,7 +143,7 @@ module Psych
offset += ((tz[1] || 0) * 60)
end
- klass.at((time - offset).to_i, us)
+ klass.new(yy, m, dd, hh, mm, ss+us/(1_000_000r), offset)
end
end
end
diff --git a/ext/psych/lib/psych/set.rb b/ext/psych/lib/psych/set.rb
index f35be15e6f..760d217098 100644
--- a/ext/psych/lib/psych/set.rb
+++ b/ext/psych/lib/psych/set.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
class Set < ::Hash
end
diff --git a/ext/psych/lib/psych/stream.rb b/ext/psych/lib/psych/stream.rb
index 2f63d7d552..24e45afc3b 100644
--- a/ext/psych/lib/psych/stream.rb
+++ b/ext/psych/lib/psych/stream.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
###
# Psych::Stream is a streaming YAML emitter. It will not buffer your YAML,
diff --git a/ext/psych/lib/psych/streaming.rb b/ext/psych/lib/psych/streaming.rb
index 260f8a8008..eb19792ad0 100644
--- a/ext/psych/lib/psych/streaming.rb
+++ b/ext/psych/lib/psych/streaming.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
module Streaming
module ClassMethods
diff --git a/ext/psych/lib/psych/syntax_error.rb b/ext/psych/lib/psych/syntax_error.rb
index db293b9fb2..1598e6ff36 100644
--- a/ext/psych/lib/psych/syntax_error.rb
+++ b/ext/psych/lib/psych/syntax_error.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/exception'
module Psych
diff --git a/ext/psych/lib/psych/tree_builder.rb b/ext/psych/lib/psych/tree_builder.rb
index d359c933af..47a1695643 100644
--- a/ext/psych/lib/psych/tree_builder.rb
+++ b/ext/psych/lib/psych/tree_builder.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/handler'
module Psych
@@ -23,6 +23,18 @@ module Psych
@stack = []
@last = nil
@root = nil
+
+ @start_line = nil
+ @start_column = nil
+ @end_line = nil
+ @end_column = nil
+ end
+
+ def event_location(start_line, start_column, end_line, end_column)
+ @start_line = start_line
+ @start_column = start_column
+ @end_line = end_line
+ @end_column = end_column
end
%w{
@@ -32,12 +44,15 @@ module Psych
class_eval %{
def start_#{node.downcase}(anchor, tag, implicit, style)
n = Nodes::#{node}.new(anchor, tag, implicit, style)
+ set_start_location(n)
@last.children << n
push n
end
def end_#{node.downcase}
- pop
+ n = pop
+ set_end_location(n)
+ n
end
}
end
@@ -49,6 +64,7 @@ module Psych
# See Psych::Handler#start_document
def start_document version, tag_directives, implicit
n = Nodes::Document.new version, tag_directives, implicit
+ set_start_location(n)
@last.children << n
push n
end
@@ -60,26 +76,35 @@ module Psych
# See Psych::Handler#start_document
def end_document implicit_end = !streaming?
@last.implicit_end = implicit_end
- pop
+ n = pop
+ set_end_location(n)
+ n
end
def start_stream encoding
@root = Nodes::Stream.new(encoding)
+ set_start_location(@root)
push @root
end
def end_stream
- pop
+ n = pop
+ set_end_location(n)
+ n
end
def scalar value, anchor, tag, plain, quoted, style
s = Nodes::Scalar.new(value,anchor,tag,plain,quoted,style)
+ set_location(s)
@last.children << s
s
end
def alias anchor
- @last.children << Nodes::Alias.new(anchor)
+ a = Nodes::Alias.new(anchor)
+ set_location(a)
+ @last.children << a
+ a
end
private
@@ -93,5 +118,20 @@ module Psych
@last = @stack.last
x
end
+
+ def set_location(node)
+ set_start_location(node)
+ set_end_location(node)
+ end
+
+ def set_start_location(node)
+ node.start_line = @start_line
+ node.start_column = @start_column
+ end
+
+ def set_end_location(node)
+ node.end_line = @end_line
+ node.end_column = @end_column
+ end
end
end
diff --git a/ext/psych/lib/psych/versions.rb b/ext/psych/lib/psych/versions.rb
index 5f7652b097..731ba9545e 100644
--- a/ext/psych/lib/psych/versions.rb
+++ b/ext/psych/lib/psych/versions.rb
@@ -1,4 +1,10 @@
-# frozen_string_literal: false
+
+# frozen_string_literal: true
module Psych
- DEFAULT_SNAKEYAML_VERSION = '1.14'.freeze
+ # The version of Psych you are using
+ VERSION = '3.1.0' unless defined?(::Psych::VERSION)
+
+ if RUBY_ENGINE == 'jruby'
+ DEFAULT_SNAKEYAML_VERSION = '1.23'.freeze
+ end
end
diff --git a/ext/psych/lib/psych/visitors.rb b/ext/psych/lib/psych/visitors.rb
index 5dee4ebd7a..e2b084daee 100644
--- a/ext/psych/lib/psych/visitors.rb
+++ b/ext/psych/lib/psych/visitors.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/visitors/visitor'
require 'psych/visitors/to_ruby'
require 'psych/visitors/emitter'
diff --git a/ext/psych/lib/psych/visitors/depth_first.rb b/ext/psych/lib/psych/visitors/depth_first.rb
index 2d74a212d6..b4ff9e40e7 100644
--- a/ext/psych/lib/psych/visitors/depth_first.rb
+++ b/ext/psych/lib/psych/visitors/depth_first.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
module Visitors
class DepthFirst < Psych::Visitors::Visitor
diff --git a/ext/psych/lib/psych/visitors/emitter.rb b/ext/psych/lib/psych/visitors/emitter.rb
index f2ff9fdb28..e3b92b7d03 100644
--- a/ext/psych/lib/psych/visitors/emitter.rb
+++ b/ext/psych/lib/psych/visitors/emitter.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
module Visitors
class Emitter < Psych::Visitors::Visitor
diff --git a/ext/psych/lib/psych/visitors/json_tree.rb b/ext/psych/lib/psych/visitors/json_tree.rb
index f2f0215cd2..9912cb1362 100644
--- a/ext/psych/lib/psych/visitors/json_tree.rb
+++ b/ext/psych/lib/psych/visitors/json_tree.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/json/ruby_events'
module Psych
diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb
index fd1c8e6caf..74a52df866 100644
--- a/ext/psych/lib/psych/visitors/to_ruby.rb
+++ b/ext/psych/lib/psych/visitors/to_ruby.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/scalar_scanner'
require 'psych/class_loader'
require 'psych/exception'
@@ -380,11 +380,6 @@ module Psych
if o.respond_to?(:init_with)
o.init_with c
- elsif o.respond_to?(:yaml_initialize)
- if $VERBOSE
- warn "Implementing #{o.class}#yaml_initialize is deprecated, please implement \"init_with(coder)\""
- end
- o.yaml_initialize c.tag, c.map
else
h.each { |k,v| o.instance_variable_set(:"@#{k}", v) }
end
diff --git a/ext/psych/lib/psych/visitors/visitor.rb b/ext/psych/lib/psych/visitors/visitor.rb
index d97bf550f6..3f4ba64e57 100644
--- a/ext/psych/lib/psych/visitors/visitor.rb
+++ b/ext/psych/lib/psych/visitors/visitor.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Psych
module Visitors
class Visitor
diff --git a/ext/psych/lib/psych/visitors/yaml_tree.rb b/ext/psych/lib/psych/visitors/yaml_tree.rb
index 11214ecbf4..cfed8f1814 100644
--- a/ext/psych/lib/psych/visitors/yaml_tree.rb
+++ b/ext/psych/lib/psych/visitors/yaml_tree.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/tree_builder'
require 'psych/scalar_scanner'
require 'psych/class_loader'
@@ -53,15 +53,6 @@ module Psych
new(emitter, ss, options)
end
- def self.new emitter = nil, ss = nil, options = nil
- return super if emitter && ss && options
-
- if $VERBOSE
- warn "This API is deprecated, please pass an emitter, scalar scanner, and options or call #{self}.create() (#{caller.first})"
- end
- create emitter, ss
- end
-
def initialize emitter, ss, options
super()
@started = false
@@ -139,24 +130,6 @@ module Psych
return @emitter.alias anchor
end
- if target.respond_to?(:to_yaml)
- begin
- loc = target.method(:to_yaml).source_location.first
- if loc !~ /(syck\/rubytypes.rb|psych\/core_ext.rb)/
- unless target.respond_to?(:encode_with)
- if $VERBOSE
- warn "implementing to_yaml is deprecated, please implement \"encode_with\""
- end
-
- target.to_yaml(:nodump => true)
- end
- end
- rescue
- # public_method or source_location might be overridden,
- # and it's OK to skip it since it's only to emit a warning
- end
- end
-
if target.respond_to?(:encode_with)
dump_coder target
else
@@ -191,6 +164,8 @@ module Psych
@emitter.end_mapping
end
+ alias :visit_Delegator :visit_Object
+
def visit_Struct o
tag = ['!ruby/struct', o.class.name].compact.join(':')
@@ -336,7 +311,7 @@ module Psych
end
is_primitive = o.class == ::String
- ivars = find_ivars o, is_primitive
+ ivars = is_primitive ? [] : o.instance_variables
if ivars.empty?
unless is_primitive
@@ -346,7 +321,7 @@ module Psych
end
@emitter.scalar o, nil, tag, plain, quote, style
else
- maptag = '!ruby/string'
+ maptag = '!ruby/string'.dup
maptag << ":#{o.class}" unless o.class == ::String
register o, @emitter.start_mapping(nil, maptag, false, Nodes::Mapping::BLOCK)
@@ -403,14 +378,18 @@ module Psych
def visit_Array o
if o.class == ::Array
- register o, @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK)
- o.each { |c| accept c }
- @emitter.end_sequence
+ visit_Enumerator o
else
visit_array_subclass o
end
end
+ def visit_Enumerator o
+ register o, @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK)
+ o.each { |c| accept c }
+ @emitter.end_sequence
+ end
+
def visit_NilClass o
@emitter.scalar('', nil, 'tag:yaml.org,2002:null', true, false, Nodes::Scalar::ANY)
end
@@ -436,15 +415,9 @@ module Psych
end
private
- # FIXME: Remove the index and count checks in Psych 3.0
- NULL = "\x00"
- BINARY_RANGE = "\x00-\x7F"
- WS_RANGE = "^ -~\t\r\n"
def binary? string
- (string.encoding == Encoding::ASCII_8BIT && !string.ascii_only?) ||
- string.index(NULL) ||
- string.count(BINARY_RANGE, WS_RANGE).fdiv(string.length) > 0.3
+ string.encoding == Encoding::ASCII_8BIT && !string.ascii_only?
end
def visit_array_subclass o
@@ -527,24 +500,6 @@ module Psych
end
end
- # FIXME: remove this method once "to_yaml_properties" is removed
- def find_ivars target, is_primitive=false
- begin
- loc = target.method(:to_yaml_properties).source_location.first
- unless loc.start_with?(Psych::DEPRECATED) || loc.end_with?('rubytypes.rb')
- if $VERBOSE
- warn "#{loc}: to_yaml_properties is deprecated, please implement \"encode_with(coder)\""
- end
- return target.to_yaml_properties
- end
- rescue
- # public_method or source_location might be overridden,
- # and it's OK to skip it since it's only to emit a warning.
- end
-
- is_primitive ? [] : target.instance_variables
- end
-
def register target, yaml_obj
@st.register target, yaml_obj
yaml_obj
@@ -586,9 +541,7 @@ module Psych
end
def dump_ivars target
- ivars = find_ivars target
-
- ivars.each do |iv|
+ target.instance_variables.each do |iv|
@emitter.scalar("#{iv.to_s.sub(/^@/, '')}", nil, nil, true, false, Nodes::Scalar::ANY)
accept target.instance_variable_get(iv)
end
diff --git a/ext/psych/lib/psych/y.rb b/ext/psych/lib/psych/y.rb
index 82e05a783c..e857953c04 100644
--- a/ext/psych/lib/psych/y.rb
+++ b/ext/psych/lib/psych/y.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module Kernel
###
# An alias for Psych.dump_stream meant to be used with IRB.
diff --git a/ext/psych/psych.gemspec b/ext/psych/psych.gemspec
index 4c0cb65214..44491c801e 100644
--- a/ext/psych/psych.gemspec
+++ b/ext/psych/psych.gemspec
@@ -1,41 +1,75 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: true
+
+begin
+ require_relative 'lib/psych/versions'
+rescue LoadError
+ # for Ruby core repository
+ require_relative 'versions'
+end
Gem::Specification.new do |s|
s.name = "psych"
- s.version = "2.1.1"
- s.authors = ["Aaron Patterson", "SHIBATA Hiroshi"]
- s.email = ["aaron@tenderlovemaking.com", "hsbt@ruby-lang.org"]
- s.date = "2016-09-07"
+ s.version = Psych::VERSION
+ s.authors = ["Aaron Patterson", "SHIBATA Hiroshi", "Charles Oliver Nutter"]
+ s.email = ["aaron@tenderlovemaking.com", "hsbt@ruby-lang.org", "headius@headius.com"]
s.summary = "Psych is a YAML parser and emitter"
s.description = <<-DESCRIPTION
-Psych is a YAML parser and emitter. Psych leverages libyaml[http://pyyaml.org/wiki/LibYAML]
+Psych is a YAML parser and emitter. Psych leverages libyaml[https://pyyaml.org/wiki/LibYAML]
for its YAML parsing and emitting capabilities. In addition to wrapping libyaml,
Psych also knows how to serialize and de-serialize most Ruby objects to and from the YAML format.
DESCRIPTION
- s.homepage = "http://github.com/tenderlove/psych"
+ s.homepage = "https://github.com/ruby/psych"
s.licenses = ["MIT"]
s.require_paths = ["lib"]
# for ruby core repository. It was generated by `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
- s.files = [".gitignore", ".travis.yml", "CHANGELOG.rdoc", "Gemfile", "Mavenfile", "README.rdoc", "Rakefile", "bin/console", "bin/setup", "ext/java/PsychEmitter.java", "ext/java/PsychLibrary.java", "ext/java/PsychParser.java", "ext/java/PsychToRuby.java", "ext/java/PsychYamlTree.java", "ext/psych/.gitignore", "ext/psych/depend", "ext/psych/extconf.rb", "ext/psych/psych.c", "ext/psych/psych.h", "ext/psych/psych_emitter.c", "ext/psych/psych_emitter.h", "ext/psych/psych_parser.c", "ext/psych/psych_parser.h", "ext/psych/psych_to_ruby.c", "ext/psych/psych_to_ruby.h", "ext/psych/psych_yaml_tree.c", "ext/psych/psych_yaml_tree.h", "ext/psych/yaml/LICENSE", "ext/psych/yaml/api.c", "ext/psych/yaml/config.h", "ext/psych/yaml/dumper.c", "ext/psych/yaml/emitter.c", "ext/psych/yaml/loader.c", "ext/psych/yaml/parser.c", "ext/psych/yaml/reader.c", "ext/psych/yaml/scanner.c", "ext/psych/yaml/writer.c", "ext/psych/yaml/yaml.h", "ext/psych/yaml/yaml_private.h", "lib/psych.rb", "lib/psych/class_loader.rb", "lib/psych/coder.rb", "lib/psych/core_ext.rb", "lib/psych/deprecated.rb", "lib/psych/exception.rb", "lib/psych/handler.rb", "lib/psych/handlers/document_stream.rb", "lib/psych/handlers/recorder.rb", "lib/psych/json/ruby_events.rb", "lib/psych/json/stream.rb", "lib/psych/json/tree_builder.rb", "lib/psych/json/yaml_events.rb", "lib/psych/nodes.rb", "lib/psych/nodes/alias.rb", "lib/psych/nodes/document.rb", "lib/psych/nodes/mapping.rb", "lib/psych/nodes/node.rb", "lib/psych/nodes/scalar.rb", "lib/psych/nodes/sequence.rb", "lib/psych/nodes/stream.rb", "lib/psych/omap.rb", "lib/psych/parser.rb", "lib/psych/scalar_scanner.rb", "lib/psych/set.rb", "lib/psych/stream.rb", "lib/psych/streaming.rb", "lib/psych/syntax_error.rb", "lib/psych/tree_builder.rb", "lib/psych/versions.rb", "lib/psych/visitors.rb","lib/psych/visitors/depth_first.rb", "lib/psych/visitors/emitter.rb", "lib/psych/visitors/json_tree.rb", "lib/psych/visitors/to_ruby.rb", "lib/psych/visitors/visitor.rb", "lib/psych/visitors/yaml_tree.rb", "lib/psych/y.rb", "lib/psych_jars.rb", "psych.gemspec"]
+ s.files = [
+ ".gitignore", ".travis.yml", "CHANGELOG.rdoc", "Gemfile", "Mavenfile", "README.md", "Rakefile", "bin/console",
+ "bin/setup", "ext/psych/depend", "ext/psych/extconf.rb", "ext/psych/psych.c", "ext/psych/psych.h",
+ "ext/psych/psych_emitter.c", "ext/psych/psych_emitter.h", "ext/psych/psych_parser.c", "ext/psych/psych_parser.h",
+ "ext/psych/psych_to_ruby.c", "ext/psych/psych_to_ruby.h", "ext/psych/psych_yaml_tree.c", "ext/psych/psych_yaml_tree.h",
+ "ext/psych/yaml/LICENSE", "ext/psych/yaml/api.c", "ext/psych/yaml/config.h", "ext/psych/yaml/dumper.c",
+ "ext/psych/yaml/emitter.c", "ext/psych/yaml/loader.c", "ext/psych/yaml/parser.c", "ext/psych/yaml/reader.c",
+ "ext/psych/yaml/scanner.c", "ext/psych/yaml/writer.c", "ext/psych/yaml/yaml.h", "ext/psych/yaml/yaml_private.h",
+ "lib/psych.rb", "lib/psych/class_loader.rb", "lib/psych/coder.rb", "lib/psych/core_ext.rb", "lib/psych/exception.rb",
+ "lib/psych/handler.rb", "lib/psych/handlers/document_stream.rb", "lib/psych/handlers/recorder.rb",
+ "lib/psych/json/ruby_events.rb", "lib/psych/json/stream.rb", "lib/psych/json/tree_builder.rb",
+ "lib/psych/json/yaml_events.rb", "lib/psych/nodes.rb", "lib/psych/nodes/alias.rb", "lib/psych/nodes/document.rb",
+ "lib/psych/nodes/mapping.rb", "lib/psych/nodes/node.rb", "lib/psych/nodes/scalar.rb", "lib/psych/nodes/sequence.rb",
+ "lib/psych/nodes/stream.rb", "lib/psych/omap.rb", "lib/psych/parser.rb", "lib/psych/scalar_scanner.rb",
+ "lib/psych/set.rb", "lib/psych/stream.rb", "lib/psych/streaming.rb", "lib/psych/syntax_error.rb",
+ "lib/psych/tree_builder.rb", "lib/psych/versions.rb", "lib/psych/visitors.rb","lib/psych/visitors/depth_first.rb",
+ "lib/psych/visitors/emitter.rb", "lib/psych/visitors/json_tree.rb", "lib/psych/visitors/to_ruby.rb",
+ "lib/psych/visitors/visitor.rb", "lib/psych/visitors/yaml_tree.rb", "lib/psych/y.rb", "psych.gemspec"
+ ]
- s.rdoc_options = ["--main", "README.rdoc"]
- s.extra_rdoc_files = ["CHANGELOG.rdoc", "README.rdoc", "CHANGELOG.rdoc", "README.rdoc"]
+ s.rdoc_options = ["--main", "README.md"]
+ s.extra_rdoc_files = ["CHANGELOG.rdoc", "README.md"]
- s.required_ruby_version = Gem::Requirement.new(">= 1.9.2")
+ s.required_ruby_version = Gem::Requirement.new(">= 2.2.2")
s.rubygems_version = "2.5.1"
s.required_rubygems_version = Gem::Requirement.new(">= 0")
- s.add_development_dependency(%q<rake-compiler>, [">= 0.4.1"])
- s.add_development_dependency(%q<minitest>, ["~> 5.0"])
+ s.add_development_dependency 'rake-compiler', ">= 0.4.1"
+ s.add_development_dependency 'minitest', "~> 5.0"
- if RUBY_PLATFORM =~ /java/
- require 'psych/versions'
+ if RUBY_ENGINE == 'jruby'
s.platform = 'java'
+ s.files.concat [
+ "ext/java/org/jruby/ext/psych/PsychEmitter.java",
+ "ext/java/org/jruby/ext/psych/PsychLibrary.java",
+ "ext/java/org/jruby/ext/psych/PsychParser.java",
+ "ext/java/org/jruby/ext/psych/PsychToRuby.java",
+ "ext/java/org/jruby/ext/psych/PsychYamlTree.java",
+ "lib/psych_jars.rb",
+ "lib/psych.jar"
+ ]
s.requirements = "jar org.yaml:snakeyaml, #{Psych::DEFAULT_SNAKEYAML_VERSION}"
s.add_dependency 'jar-dependencies', '>= 0.1.7'
s.add_development_dependency 'ruby-maven'
else
s.extensions = ["ext/psych/extconf.rb"]
+ s.add_development_dependency 'rake-compiler-dock', ">= 0.6.3"
end
end
diff --git a/ext/psych/psych.h b/ext/psych/psych.h
index 1830ca4b19..6b3d63f246 100644
--- a/ext/psych/psych.h
+++ b/ext/psych/psych.h
@@ -2,10 +2,7 @@
#define PSYCH_H
#include <ruby.h>
-
-#ifdef HAVE_RUBY_ENCODING_H
#include <ruby/encoding.h>
-#endif
#include <yaml.h>
diff --git a/ext/psych/psych_emitter.c b/ext/psych/psych_emitter.c
index 371c285183..022ffa0946 100644
--- a/ext/psych/psych_emitter.c
+++ b/ext/psych/psych_emitter.c
@@ -8,6 +8,7 @@
#endif
VALUE cPsychEmitter;
+static ID id_io;
static ID id_write;
static ID id_line_width;
static ID id_indentation;
@@ -21,12 +22,8 @@ static void emit(yaml_emitter_t * emitter, yaml_event_t * event)
static int writer(void *ctx, unsigned char *buffer, size_t size)
{
- VALUE io = (VALUE)ctx;
-#ifdef HAVE_RUBY_ENCODING_H
+ VALUE self = (VALUE)ctx, io = rb_attr_get(self, id_io);
VALUE str = rb_enc_str_new((const char *)buffer, (long)size, rb_utf8_encoding());
-#else
- VALUE str = rb_str_new((const char *)buffer, (long)size);
-#endif
VALUE wrote = rb_funcall(io, id_write, 1, str);
return (int)NUM2INT(wrote);
}
@@ -94,7 +91,8 @@ static VALUE initialize(int argc, VALUE *argv, VALUE self)
yaml_emitter_set_canonical(emitter, Qtrue == canonical ? 1 : 0);
}
- yaml_emitter_set_output(emitter, writer, (void *)io);
+ rb_ivar_set(self, id_io, io);
+ yaml_emitter_set_output(emitter, writer, (void *)self);
return self;
}
@@ -168,9 +166,7 @@ static VALUE start_document(VALUE self, VALUE version, VALUE tags, VALUE imp)
if(RTEST(tags)) {
long i = 0;
long len;
-#ifdef HAVE_RUBY_ENCODING_H
rb_encoding * encoding = rb_utf8_encoding();
-#endif
Check_Type(tags, T_ARRAY);
@@ -193,13 +189,11 @@ static VALUE start_document(VALUE self, VALUE version, VALUE tags, VALUE imp)
value = RARRAY_AREF(tuple, 1);
StringValue(name);
StringValue(value);
-#ifdef HAVE_RUBY_ENCODING_H
name = rb_str_export_to_enc(name, encoding);
value = rb_str_export_to_enc(value, encoding);
-#endif
- tail->handle = (yaml_char_t *)RSTRING_PTR(name);
- tail->prefix = (yaml_char_t *)RSTRING_PTR(value);
+ tail->handle = (yaml_char_t *)StringValueCStr(name);
+ tail->prefix = (yaml_char_t *)StringValueCStr(value);
tail++;
}
@@ -257,14 +251,11 @@ static VALUE scalar(
) {
yaml_emitter_t * emitter;
yaml_event_t event;
-#ifdef HAVE_RUBY_ENCODING_H
rb_encoding *encoding;
-#endif
TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
Check_Type(value, T_STRING);
-#ifdef HAVE_RUBY_ENCODING_H
encoding = rb_utf8_encoding();
value = rb_str_export_to_enc(value, encoding);
@@ -278,12 +269,11 @@ static VALUE scalar(
Check_Type(tag, T_STRING);
tag = rb_str_export_to_enc(tag, encoding);
}
-#endif
yaml_scalar_event_initialize(
&event,
- (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)),
- (yaml_char_t *)(NIL_P(tag) ? NULL : StringValuePtr(tag)),
+ (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)),
+ (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)),
(yaml_char_t*)StringValuePtr(value),
(int)RSTRING_LEN(value),
plain ? 1 : 0,
@@ -313,7 +303,6 @@ static VALUE start_sequence(
yaml_emitter_t * emitter;
yaml_event_t event;
-#ifdef HAVE_RUBY_ENCODING_H
rb_encoding * encoding = rb_utf8_encoding();
if(!NIL_P(anchor)) {
@@ -325,14 +314,13 @@ static VALUE start_sequence(
Check_Type(tag, T_STRING);
tag = rb_str_export_to_enc(tag, encoding);
}
-#endif
TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
yaml_sequence_start_event_initialize(
&event,
- (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)),
- (yaml_char_t *)(NIL_P(tag) ? NULL : StringValuePtr(tag)),
+ (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)),
+ (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)),
implicit ? 1 : 0,
(yaml_sequence_style_t)NUM2INT(style)
);
@@ -377,12 +365,10 @@ static VALUE start_mapping(
) {
yaml_emitter_t * emitter;
yaml_event_t event;
-#ifdef HAVE_RUBY_ENCODING_H
rb_encoding *encoding;
-#endif
+
TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
-#ifdef HAVE_RUBY_ENCODING_H
encoding = rb_utf8_encoding();
if(!NIL_P(anchor)) {
@@ -394,12 +380,11 @@ static VALUE start_mapping(
Check_Type(tag, T_STRING);
tag = rb_str_export_to_enc(tag, encoding);
}
-#endif
yaml_mapping_start_event_initialize(
&event,
- (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)),
- (yaml_char_t *)(NIL_P(tag) ? NULL : StringValuePtr(tag)),
+ (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)),
+ (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)),
implicit ? 1 : 0,
(yaml_mapping_style_t)NUM2INT(style)
);
@@ -440,16 +425,14 @@ static VALUE alias(VALUE self, VALUE anchor)
yaml_event_t event;
TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
-#ifdef HAVE_RUBY_ENCODING_H
if(!NIL_P(anchor)) {
Check_Type(anchor, T_STRING);
anchor = rb_str_export_to_enc(anchor, rb_utf8_encoding());
}
-#endif
yaml_alias_event_initialize(
&event,
- (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor))
+ (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor))
);
emit(emitter, &event);
@@ -538,6 +521,7 @@ static VALUE set_line_width(VALUE self, VALUE width)
void Init_psych_emitter(void)
{
+#undef rb_intern
VALUE psych = rb_define_module("Psych");
VALUE handler = rb_define_class_under(psych, "Handler", rb_cObject);
cPsychEmitter = rb_define_class_under(psych, "Emitter", handler);
@@ -562,6 +546,7 @@ void Init_psych_emitter(void)
rb_define_method(cPsychEmitter, "line_width", line_width, 0);
rb_define_method(cPsychEmitter, "line_width=", set_line_width, 1);
+ id_io = rb_intern("io");
id_write = rb_intern("write");
id_line_width = rb_intern("line_width");
id_indentation = rb_intern("indentation");
diff --git a/ext/psych/psych_parser.c b/ext/psych/psych_parser.c
index 2caa8a09c3..8eb2205848 100644
--- a/ext/psych/psych_parser.c
+++ b/ext/psych/psych_parser.c
@@ -16,6 +16,7 @@ static ID id_start_sequence;
static ID id_end_sequence;
static ID id_start_mapping;
static ID id_end_mapping;
+static ID id_event_location;
#define PSYCH_TRANSCODE(_str, _yaml_enc, _internal_enc) \
do { \
@@ -93,7 +94,6 @@ static VALUE make_exception(yaml_parser_t * parser, VALUE path)
parser->context ? rb_usascii_str_new2(parser->context) : Qnil);
}
-#ifdef HAVE_RUBY_ENCODING_H
static VALUE transcode_string(VALUE src, int * parser_encoding)
{
int utf8 = rb_utf8_encindex();
@@ -171,8 +171,6 @@ static VALUE transcode_io(VALUE src, int * parser_encoding)
return src;
}
-#endif
-
static VALUE protected_start_stream(VALUE pointer)
{
VALUE *args = (VALUE *)pointer;
@@ -235,6 +233,12 @@ static VALUE protected_end_stream(VALUE handler)
return rb_funcall(handler, id_end_stream, 0);
}
+static VALUE protected_event_location(VALUE pointer)
+{
+ VALUE *args = (VALUE *)pointer;
+ return rb_funcall3(args[0], id_event_location, 4, args + 1);
+}
+
/*
* call-seq:
* parser.parse(yaml)
@@ -253,10 +257,8 @@ static VALUE parse(int argc, VALUE *argv, VALUE self)
int tainted = 0;
int state = 0;
int parser_encoding = YAML_ANY_ENCODING;
-#ifdef HAVE_RUBY_ENCODING_H
int encoding = rb_utf8_encindex();
rb_encoding * internal_enc = rb_default_internal_encoding();
-#endif
VALUE handler = rb_iv_get(self, "@handler");
if (rb_scan_args(argc, argv, "11", &yaml, &path) == 1) {
@@ -274,18 +276,14 @@ static VALUE parse(int argc, VALUE *argv, VALUE self)
if (OBJ_TAINTED(yaml)) tainted = 1;
if (rb_respond_to(yaml, id_read)) {
-#ifdef HAVE_RUBY_ENCODING_H
yaml = transcode_io(yaml, &parser_encoding);
yaml_parser_set_encoding(parser, parser_encoding);
-#endif
yaml_parser_set_input(parser, io_reader, (void *)yaml);
if (RTEST(rb_obj_is_kind_of(yaml, rb_cIO))) tainted = 1;
} else {
StringValue(yaml);
-#ifdef HAVE_RUBY_ENCODING_H
yaml = transcode_string(yaml, &parser_encoding);
yaml_parser_set_encoding(parser, parser_encoding);
-#endif
yaml_parser_set_input_string(
parser,
(const unsigned char *)RSTRING_PTR(yaml),
@@ -294,6 +292,9 @@ static VALUE parse(int argc, VALUE *argv, VALUE self)
}
while(!done) {
+ VALUE event_args[5];
+ VALUE start_line, start_column, end_line, end_column;
+
if(!yaml_parser_parse(parser, &event)) {
VALUE exception;
@@ -304,6 +305,18 @@ static VALUE parse(int argc, VALUE *argv, VALUE self)
rb_exc_raise(exception);
}
+ start_line = INT2NUM((long)event.start_mark.line);
+ start_column = INT2NUM((long)event.start_mark.column);
+ end_line = INT2NUM((long)event.end_mark.line);
+ end_column = INT2NUM((long)event.end_mark.column);
+
+ event_args[0] = handler;
+ event_args[1] = start_line;
+ event_args[2] = start_column;
+ event_args[3] = end_line;
+ event_args[4] = end_column;
+ rb_protect(protected_event_location, (VALUE)event_args, &state);
+
switch(event.type) {
case YAML_STREAM_START_EVENT:
{
@@ -338,17 +351,13 @@ static VALUE parse(int argc, VALUE *argv, VALUE self)
if(start->handle) {
handle = rb_str_new2((const char *)start->handle);
if (tainted) OBJ_TAINT(handle);
-#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(handle, encoding, internal_enc);
-#endif
}
if(start->prefix) {
prefix = rb_str_new2((const char *)start->prefix);
if (tainted) OBJ_TAINT(prefix);
-#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(prefix, encoding, internal_enc);
-#endif
}
rb_ary_push(tag_directives, rb_ary_new3((long)2, handle, prefix));
@@ -377,9 +386,7 @@ static VALUE parse(int argc, VALUE *argv, VALUE self)
if(event.data.alias.anchor) {
alias = rb_str_new2((const char *)event.data.alias.anchor);
if (tainted) OBJ_TAINT(alias);
-#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(alias, encoding, internal_enc);
-#endif
}
args[0] = handler;
@@ -399,24 +406,18 @@ static VALUE parse(int argc, VALUE *argv, VALUE self)
);
if (tainted) OBJ_TAINT(val);
-#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(val, encoding, internal_enc);
-#endif
if(event.data.scalar.anchor) {
anchor = rb_str_new2((const char *)event.data.scalar.anchor);
if (tainted) OBJ_TAINT(anchor);
-#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(anchor, encoding, internal_enc);
-#endif
}
if(event.data.scalar.tag) {
tag = rb_str_new2((const char *)event.data.scalar.tag);
if (tainted) OBJ_TAINT(tag);
-#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(tag, encoding, internal_enc);
-#endif
}
plain_implicit =
@@ -446,18 +447,14 @@ static VALUE parse(int argc, VALUE *argv, VALUE self)
if(event.data.sequence_start.anchor) {
anchor = rb_str_new2((const char *)event.data.sequence_start.anchor);
if (tainted) OBJ_TAINT(anchor);
-#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(anchor, encoding, internal_enc);
-#endif
}
tag = Qnil;
if(event.data.sequence_start.tag) {
tag = rb_str_new2((const char *)event.data.sequence_start.tag);
if (tainted) OBJ_TAINT(tag);
-#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(tag, encoding, internal_enc);
-#endif
}
implicit =
@@ -486,17 +483,13 @@ static VALUE parse(int argc, VALUE *argv, VALUE self)
if(event.data.mapping_start.anchor) {
anchor = rb_str_new2((const char *)event.data.mapping_start.anchor);
if (tainted) OBJ_TAINT(anchor);
-#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(anchor, encoding, internal_enc);
-#endif
}
if(event.data.mapping_start.tag) {
tag = rb_str_new2((const char *)event.data.mapping_start.tag);
if (tainted) OBJ_TAINT(tag);
-#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(tag, encoding, internal_enc);
-#endif
}
implicit =
@@ -555,6 +548,7 @@ static VALUE mark(VALUE self)
void Init_psych_parser(void)
{
+#undef rb_intern
#if 0
mPsych = rb_define_module("Psych");
#endif
@@ -580,18 +574,19 @@ void Init_psych_parser(void)
rb_define_method(cPsychParser, "parse", parse, -1);
rb_define_method(cPsychParser, "mark", mark, 0);
- id_read = rb_intern("read");
- id_path = rb_intern("path");
- id_empty = rb_intern("empty");
- id_start_stream = rb_intern("start_stream");
- id_end_stream = rb_intern("end_stream");
- id_start_document = rb_intern("start_document");
- id_end_document = rb_intern("end_document");
- id_alias = rb_intern("alias");
- id_scalar = rb_intern("scalar");
- id_start_sequence = rb_intern("start_sequence");
- id_end_sequence = rb_intern("end_sequence");
- id_start_mapping = rb_intern("start_mapping");
- id_end_mapping = rb_intern("end_mapping");
+ id_read = rb_intern("read");
+ id_path = rb_intern("path");
+ id_empty = rb_intern("empty");
+ id_start_stream = rb_intern("start_stream");
+ id_end_stream = rb_intern("end_stream");
+ id_start_document = rb_intern("start_document");
+ id_end_document = rb_intern("end_document");
+ id_alias = rb_intern("alias");
+ id_scalar = rb_intern("scalar");
+ id_start_sequence = rb_intern("start_sequence");
+ id_end_sequence = rb_intern("end_sequence");
+ id_start_mapping = rb_intern("start_mapping");
+ id_end_mapping = rb_intern("end_mapping");
+ id_event_location = rb_intern("event_location");
}
/* vim: set noet sws=4 sw=4: */
diff --git a/ext/psych/psych_to_ruby.c b/ext/psych/psych_to_ruby.c
index 3cc87a965e..b388ff7754 100644
--- a/ext/psych/psych_to_ruby.c
+++ b/ext/psych/psych_to_ruby.c
@@ -21,11 +21,7 @@ static VALUE build_exception(VALUE self, VALUE klass, VALUE mesg)
*/
static VALUE path2class(VALUE self, VALUE path)
{
-#ifdef HAVE_RUBY_ENCODING_H
return rb_path_to_class(path);
-#else
- return rb_path2class(StringValuePtr(path));
-#endif
}
void Init_psych_to_ruby(void)
diff --git a/ext/psych/psych_yaml_tree.c b/ext/psych/psych_yaml_tree.c
index bcf24d2070..7aca9114c9 100644
--- a/ext/psych/psych_yaml_tree.c
+++ b/ext/psych/psych_yaml_tree.c
@@ -9,7 +9,7 @@ VALUE cPsychVisitorsYamlTree;
*/
static VALUE private_iv_get(VALUE self, VALUE target, VALUE prop)
{
- return rb_attr_get(target, rb_intern(StringValuePtr(prop)));
+ return rb_attr_get(target, rb_intern(StringValueCStr(prop)));
}
void Init_psych_yaml_tree(void)
diff --git a/ext/psych/yaml/LICENSE b/ext/psych/yaml/LICENSE
deleted file mode 100644
index 050ced23f6..0000000000
--- a/ext/psych/yaml/LICENSE
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (c) 2006 Kirill Simonov
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/ext/psych/yaml/api.c b/ext/psych/yaml/api.c
index e0b9d979cc..ee170d87de 100644
--- a/ext/psych/yaml/api.c
+++ b/ext/psych/yaml/api.c
@@ -74,7 +74,7 @@ YAML_DECLARE(int)
yaml_string_extend(yaml_char_t **start,
yaml_char_t **pointer, yaml_char_t **end)
{
- yaml_char_t *new_start = yaml_realloc(*start, (*end - *start)*2);
+ yaml_char_t *new_start = (yaml_char_t *)yaml_realloc((void*)*start, (*end - *start)*2);
if (!new_start) return 0;
@@ -94,8 +94,9 @@ yaml_string_extend(yaml_char_t **start,
YAML_DECLARE(int)
yaml_string_join(
yaml_char_t **a_start, yaml_char_t **a_pointer, yaml_char_t **a_end,
- yaml_char_t **b_start, yaml_char_t **b_pointer, yaml_char_t **b_end)
+ yaml_char_t **b_start, yaml_char_t **b_pointer, SHIM(yaml_char_t **b_end))
{
+ UNUSED_PARAM(b_end)
if (*b_start == *b_pointer)
return 1;
@@ -177,17 +178,17 @@ yaml_parser_initialize(yaml_parser_t *parser)
goto error;
if (!BUFFER_INIT(parser, parser->buffer, INPUT_BUFFER_SIZE))
goto error;
- if (!QUEUE_INIT(parser, parser->tokens, INITIAL_QUEUE_SIZE))
+ if (!QUEUE_INIT(parser, parser->tokens, INITIAL_QUEUE_SIZE, yaml_token_t*))
goto error;
- if (!STACK_INIT(parser, parser->indents, INITIAL_STACK_SIZE))
+ if (!STACK_INIT(parser, parser->indents, int*))
goto error;
- if (!STACK_INIT(parser, parser->simple_keys, INITIAL_STACK_SIZE))
+ if (!STACK_INIT(parser, parser->simple_keys, yaml_simple_key_t*))
goto error;
- if (!STACK_INIT(parser, parser->states, INITIAL_STACK_SIZE))
+ if (!STACK_INIT(parser, parser->states, yaml_parser_state_t*))
goto error;
- if (!STACK_INIT(parser, parser->marks, INITIAL_STACK_SIZE))
+ if (!STACK_INIT(parser, parser->marks, yaml_mark_t*))
goto error;
- if (!STACK_INIT(parser, parser->tag_directives, INITIAL_STACK_SIZE))
+ if (!STACK_INIT(parser, parser->tag_directives, yaml_tag_directive_t*))
goto error;
return 1;
@@ -243,7 +244,7 @@ static int
yaml_string_read_handler(void *data, unsigned char *buffer, size_t size,
size_t *size_read)
{
- yaml_parser_t *parser = data;
+ yaml_parser_t *parser = (yaml_parser_t *)data;
if (parser->input.string.current == parser->input.string.end) {
*size_read = 0;
@@ -269,7 +270,7 @@ static int
yaml_file_read_handler(void *data, unsigned char *buffer, size_t size,
size_t *size_read)
{
- yaml_parser_t *parser = data;
+ yaml_parser_t *parser = (yaml_parser_t *)data;
*size_read = fread(buffer, 1, size, parser->input.file);
return !ferror(parser->input.file);
@@ -355,13 +356,13 @@ yaml_emitter_initialize(yaml_emitter_t *emitter)
goto error;
if (!BUFFER_INIT(emitter, emitter->raw_buffer, OUTPUT_RAW_BUFFER_SIZE))
goto error;
- if (!STACK_INIT(emitter, emitter->states, INITIAL_STACK_SIZE))
+ if (!STACK_INIT(emitter, emitter->states, yaml_emitter_state_t*))
goto error;
- if (!QUEUE_INIT(emitter, emitter->events, INITIAL_QUEUE_SIZE))
+ if (!QUEUE_INIT(emitter, emitter->events, INITIAL_QUEUE_SIZE, yaml_event_t*))
goto error;
- if (!STACK_INIT(emitter, emitter->indents, INITIAL_STACK_SIZE))
+ if (!STACK_INIT(emitter, emitter->indents, int*))
goto error;
- if (!STACK_INIT(emitter, emitter->tag_directives, INITIAL_STACK_SIZE))
+ if (!STACK_INIT(emitter, emitter->tag_directives, yaml_tag_directive_t*))
goto error;
return 1;
@@ -395,7 +396,7 @@ yaml_emitter_delete(yaml_emitter_t *emitter)
}
QUEUE_DEL(emitter, emitter->events);
STACK_DEL(emitter, emitter->indents);
- while (!STACK_EMPTY(emitter, emitter->tag_directives)) {
+ while (!STACK_EMPTY(empty, emitter->tag_directives)) {
yaml_tag_directive_t tag_directive = POP(emitter, emitter->tag_directives);
yaml_free(tag_directive.handle);
yaml_free(tag_directive.prefix);
@@ -413,9 +414,9 @@ yaml_emitter_delete(yaml_emitter_t *emitter)
static int
yaml_string_write_handler(void *data, unsigned char *buffer, size_t size)
{
- yaml_emitter_t *emitter = data;
+ yaml_emitter_t *emitter = (yaml_emitter_t *)data;
- if (emitter->output.string.size + *emitter->output.string.size_written
+ if (emitter->output.string.size - *emitter->output.string.size_written
< size) {
memcpy(emitter->output.string.buffer
+ *emitter->output.string.size_written,
@@ -439,7 +440,7 @@ yaml_string_write_handler(void *data, unsigned char *buffer, size_t size)
static int
yaml_file_write_handler(void *data, unsigned char *buffer, size_t size)
{
- yaml_emitter_t *emitter = data;
+ yaml_emitter_t *emitter = (yaml_emitter_t *)data;
return (fwrite(buffer, 1, size, emitter->output.file) == size);
}
@@ -717,7 +718,7 @@ yaml_document_start_event_initialize(yaml_event_t *event,
/* Valid tag directives are expected. */
if (version_directive) {
- version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t));
+ version_directive_copy = YAML_MALLOC_STATIC(yaml_version_directive_t);
if (!version_directive_copy) goto error;
version_directive_copy->major = version_directive->major;
version_directive_copy->minor = version_directive->minor;
@@ -725,7 +726,7 @@ yaml_document_start_event_initialize(yaml_event_t *event,
if (tag_directives_start != tag_directives_end) {
yaml_tag_directive_t *tag_directive;
- if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE))
+ if (!STACK_INIT(&context, tag_directives_copy, yaml_tag_directive_t*))
goto error;
for (tag_directive = tag_directives_start;
tag_directive != tag_directives_end; tag_directive ++) {
@@ -822,7 +823,6 @@ yaml_scalar_event_initialize(yaml_event_t *event,
yaml_char_t *anchor_copy = NULL;
yaml_char_t *tag_copy = NULL;
yaml_char_t *value_copy = NULL;
- size_t value_length;
assert(event); /* Non-NULL event object is expected. */
assert(value); /* Non-NULL anchor is expected. */
@@ -840,19 +840,16 @@ yaml_scalar_event_initialize(yaml_event_t *event,
}
if (length < 0) {
- value_length = strlen((char *)value);
- }
- else {
- value_length = (size_t)length;
+ length = strlen((char *)value);
}
- if (!yaml_check_utf8(value, value_length)) goto error;
- value_copy = yaml_malloc(value_length+1);
+ if (!yaml_check_utf8(value, length)) goto error;
+ value_copy = YAML_MALLOC(length+1);
if (!value_copy) goto error;
- memcpy(value_copy, value, value_length);
- value_copy[value_length] = '\0';
+ memcpy(value_copy, value, length);
+ value_copy[length] = '\0';
- SCALAR_EVENT_INIT(*event, anchor_copy, tag_copy, value_copy, value_length,
+ SCALAR_EVENT_INIT(*event, anchor_copy, tag_copy, value_copy, length,
plain_implicit, quoted_implicit, style, mark, mark);
return 1;
@@ -1059,10 +1056,10 @@ yaml_document_initialize(yaml_document_t *document,
(tag_directives_start == tag_directives_end));
/* Valid tag directives are expected. */
- if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error;
+ if (!STACK_INIT(&context, nodes, yaml_node_t*)) goto error;
if (version_directive) {
- version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t));
+ version_directive_copy = YAML_MALLOC_STATIC(yaml_version_directive_t);
if (!version_directive_copy) goto error;
version_directive_copy->major = version_directive->major;
version_directive_copy->minor = version_directive->minor;
@@ -1070,7 +1067,7 @@ yaml_document_initialize(yaml_document_t *document,
if (tag_directives_start != tag_directives_end) {
yaml_tag_directive_t *tag_directive;
- if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE))
+ if (!STACK_INIT(&context, tag_directives_copy, yaml_tag_directive_t*))
goto error;
for (tag_directive = tag_directives_start;
tag_directive != tag_directives_end; tag_directive ++) {
@@ -1206,8 +1203,6 @@ yaml_document_add_scalar(yaml_document_t *document,
yaml_char_t *tag_copy = NULL;
yaml_char_t *value_copy = NULL;
yaml_node_t node;
- size_t value_length;
- ptrdiff_t ret;
assert(document); /* Non-NULL document object is expected. */
assert(value); /* Non-NULL value is expected. */
@@ -1221,26 +1216,19 @@ yaml_document_add_scalar(yaml_document_t *document,
if (!tag_copy) goto error;
if (length < 0) {
- value_length = strlen((char *)value);
- }
- else {
- value_length = (size_t)length;
+ length = strlen((char *)value);
}
- if (!yaml_check_utf8(value, value_length)) goto error;
- value_copy = yaml_malloc(value_length+1);
+ if (!yaml_check_utf8(value, length)) goto error;
+ value_copy = YAML_MALLOC(length+1);
if (!value_copy) goto error;
- memcpy(value_copy, value, value_length);
- value_copy[value_length] = '\0';
+ memcpy(value_copy, value, length);
+ value_copy[length] = '\0';
- SCALAR_NODE_INIT(node, tag_copy, value_copy, value_length, style, mark, mark);
+ SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark);
if (!PUSH(&context, document->nodes, node)) goto error;
- ret = document->nodes.top - document->nodes.start;
-#if PTRDIFF_MAX > INT_MAX
- if (ret > INT_MAX) goto error;
-#endif
- return (int)ret;
+ return document->nodes.top - document->nodes.start;
error:
yaml_free(tag_copy);
@@ -1268,7 +1256,6 @@ yaml_document_add_sequence(yaml_document_t *document,
yaml_node_item_t *top;
} items = { NULL, NULL, NULL };
yaml_node_t node;
- ptrdiff_t ret;
assert(document); /* Non-NULL document object is expected. */
@@ -1280,17 +1267,13 @@ yaml_document_add_sequence(yaml_document_t *document,
tag_copy = yaml_strdup(tag);
if (!tag_copy) goto error;
- if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error;
+ if (!STACK_INIT(&context, items, yaml_node_item_t*)) goto error;
SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end,
style, mark, mark);
if (!PUSH(&context, document->nodes, node)) goto error;
- ret = document->nodes.top - document->nodes.start;
-#if PTRDIFF_MAX > INT_MAX
- if (ret > INT_MAX) goto error;
-#endif
- return (int)ret;
+ return document->nodes.top - document->nodes.start;
error:
STACK_DEL(&context, items);
@@ -1318,7 +1301,6 @@ yaml_document_add_mapping(yaml_document_t *document,
yaml_node_pair_t *top;
} pairs = { NULL, NULL, NULL };
yaml_node_t node;
- ptrdiff_t ret;
assert(document); /* Non-NULL document object is expected. */
@@ -1330,17 +1312,13 @@ yaml_document_add_mapping(yaml_document_t *document,
tag_copy = yaml_strdup(tag);
if (!tag_copy) goto error;
- if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error;
+ if (!STACK_INIT(&context, pairs, yaml_node_pair_t*)) goto error;
MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end,
style, mark, mark);
if (!PUSH(&context, document->nodes, node)) goto error;
- ret = document->nodes.top - document->nodes.start;
-#if PTRDIFF_MAX > INT_MAX
- if (ret > INT_MAX) goto error;
-#endif
- return (int)ret;
+ return document->nodes.top - document->nodes.start;
error:
STACK_DEL(&context, pairs);
diff --git a/ext/psych/yaml/config.h b/ext/psych/yaml/config.h
index f54c27d339..da905133ff 100644
--- a/ext/psych/yaml/config.h
+++ b/ext/psych/yaml/config.h
@@ -1,10 +1,10 @@
#define PACKAGE_NAME "yaml"
#define PACKAGE_TARNAME "yaml"
-#define PACKAGE_VERSION "0.1.6"
-#define PACKAGE_STRING "yaml 0.1.6"
-#define PACKAGE_BUGREPORT "http://pyyaml.org/newticket?component libyaml"
-#define PACKAGE_URL ""
+#define PACKAGE_VERSION "0.2.1"
+#define PACKAGE_STRING "yaml 0.2.1"
+#define PACKAGE_BUGREPORT "https://github.com/yaml/libyaml/issues"
+#define PACKAGE_URL "https://github.com/yaml/libyaml"
#define YAML_VERSION_MAJOR 0
-#define YAML_VERSION_MINOR 1
-#define YAML_VERSION_PATCH 6
-#define YAML_VERSION_STRING "0.1.6"
+#define YAML_VERSION_MINOR 2
+#define YAML_VERSION_PATCH 1
+#define YAML_VERSION_STRING "0.2.1"
diff --git a/ext/psych/yaml/dumper.c b/ext/psych/yaml/dumper.c
index 203c6a709c..29fb9c0784 100644
--- a/ext/psych/yaml/dumper.c
+++ b/ext/psych/yaml/dumper.c
@@ -245,9 +245,9 @@ yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index)
#define ANCHOR_TEMPLATE_LENGTH 16
static yaml_char_t *
-yaml_emitter_generate_anchor(yaml_emitter_t *emitter, int anchor_id)
+yaml_emitter_generate_anchor(SHIM(yaml_emitter_t *emitter), int anchor_id)
{
- yaml_char_t *anchor = yaml_malloc(ANCHOR_TEMPLATE_LENGTH);
+ yaml_char_t *anchor = YAML_MALLOC(ANCHOR_TEMPLATE_LENGTH);
if (!anchor) return NULL;
diff --git a/ext/psych/yaml/emitter.c b/ext/psych/yaml/emitter.c
index bf84fafc51..92e21cdb73 100644
--- a/ext/psych/yaml/emitter.c
+++ b/ext/psych/yaml/emitter.c
@@ -24,8 +24,8 @@
*/
#define PUT_BREAK(emitter) \
- (FLUSH(emitter) \
- && ((emitter->line_break == YAML_CR_BREAK ? \
+ (FLUSH(emitter) ? \
+ ((emitter->line_break == YAML_CR_BREAK ? \
(*(emitter->buffer.pointer++) = (yaml_char_t) '\r') : \
emitter->line_break == YAML_LN_BREAK ? \
(*(emitter->buffer.pointer++) = (yaml_char_t) '\n') : \
@@ -34,7 +34,7 @@
*(emitter->buffer.pointer++) = (yaml_char_t) '\n') : 0), \
emitter->column = 0, \
emitter->line ++, \
- 1))
+ 1) : 0)
/*
* Copy a character from a string into buffer.
@@ -53,7 +53,7 @@
#define WRITE_BREAK(emitter,string) \
(FLUSH(emitter) \
&& (CHECK(string,'\n') ? \
- ((void)PUT_BREAK(emitter), \
+ (PUT_BREAK(emitter), \
string.pointer ++, \
1) : \
(COPY(emitter->buffer,string), \
@@ -1002,7 +1002,7 @@ yaml_emitter_emit_node(yaml_emitter_t *emitter, yaml_event_t *event,
*/
static int
-yaml_emitter_emit_alias(yaml_emitter_t *emitter, yaml_event_t *event)
+yaml_emitter_emit_alias(yaml_emitter_t *emitter, SHIM(yaml_event_t *event))
{
if (!yaml_emitter_process_anchor(emitter))
return 0;
@@ -1087,7 +1087,7 @@ yaml_emitter_emit_mapping_start(yaml_emitter_t *emitter, yaml_event_t *event)
*/
static int
-yaml_emitter_check_empty_document(yaml_emitter_t *emitter)
+yaml_emitter_check_empty_document(SHIM(yaml_emitter_t *emitter))
{
return 0;
}
@@ -1234,7 +1234,7 @@ yaml_emitter_select_scalar_style(yaml_emitter_t *emitter, yaml_event_t *event)
}
/*
- * Write an achor.
+ * Write an anchor.
*/
static int
@@ -1493,7 +1493,7 @@ yaml_emitter_analyze_scalar(yaml_emitter_t *emitter,
int break_space = 0;
int space_break = 0;
- int preceeded_by_whitespace = 0;
+ int preceded_by_whitespace = 0;
int followed_by_whitespace = 0;
int previous_space = 0;
int previous_break = 0;
@@ -1524,7 +1524,7 @@ yaml_emitter_analyze_scalar(yaml_emitter_t *emitter,
flow_indicators = 1;
}
- preceeded_by_whitespace = 1;
+ preceded_by_whitespace = 1;
followed_by_whitespace = IS_BLANKZ_AT(string, WIDTH(string));
while (string.pointer != string.end)
@@ -1570,7 +1570,7 @@ yaml_emitter_analyze_scalar(yaml_emitter_t *emitter,
}
}
- if (CHECK(string, '#') && preceeded_by_whitespace) {
+ if (CHECK(string, '#') && preceded_by_whitespace) {
flow_indicators = 1;
block_indicators = 1;
}
@@ -1619,7 +1619,7 @@ yaml_emitter_analyze_scalar(yaml_emitter_t *emitter,
previous_break = 0;
}
- preceeded_by_whitespace = IS_BLANKZ(string);
+ preceded_by_whitespace = IS_BLANKZ(string);
MOVE(string);
if (string.pointer != string.end) {
followed_by_whitespace = IS_BLANKZ_AT(string, WIDTH(string));
@@ -1946,10 +1946,6 @@ yaml_emitter_write_plain_scalar(yaml_emitter_t *emitter,
emitter->whitespace = 0;
emitter->indention = 0;
- if (emitter->root_context)
- {
- emitter->open_ended = 1;
- }
return 1;
}
@@ -2326,4 +2322,3 @@ yaml_emitter_write_folded_scalar(yaml_emitter_t *emitter,
return 1;
}
-
diff --git a/ext/psych/yaml/loader.c b/ext/psych/yaml/loader.c
index def67933e7..db8501ac74 100644
--- a/ext/psych/yaml/loader.c
+++ b/ext/psych/yaml/loader.c
@@ -72,7 +72,7 @@ yaml_parser_load(yaml_parser_t *parser, yaml_document_t *document)
assert(document); /* Non-NULL document object is expected. */
memset(document, 0, sizeof(yaml_document_t));
- if (!STACK_INIT(parser, document->nodes, INITIAL_STACK_SIZE))
+ if (!STACK_INIT(parser, document->nodes, yaml_node_t*))
goto error;
if (!parser->stream_start_produced) {
@@ -90,7 +90,7 @@ yaml_parser_load(yaml_parser_t *parser, yaml_document_t *document)
return 1;
}
- if (!STACK_INIT(parser, parser->aliases, INITIAL_STACK_SIZE))
+ if (!STACK_INIT(parser, parser->aliases, yaml_alias_data_t*))
goto error;
parser->document = document;
@@ -283,7 +283,6 @@ static int
yaml_parser_load_scalar(yaml_parser_t *parser, yaml_event_t *first_event)
{
yaml_node_t node;
- ptrdiff_t node_index;
int index;
yaml_char_t *tag = first_event->data.scalar.tag;
@@ -301,11 +300,7 @@ yaml_parser_load_scalar(yaml_parser_t *parser, yaml_event_t *first_event)
if (!PUSH(parser, parser->document->nodes, node)) goto error;
- node_index = parser->document->nodes.top - parser->document->nodes.start;
-#if PTRDIFF_MAX > INT_MAX
- if (node_index > INT_MAX) goto error;
-#endif
- index = (int)node_index;
+ index = parser->document->nodes.top - parser->document->nodes.start;
if (!yaml_parser_register_anchor(parser, index,
first_event->data.scalar.anchor)) return 0;
@@ -334,7 +329,6 @@ yaml_parser_load_sequence(yaml_parser_t *parser, yaml_event_t *first_event)
yaml_node_item_t *top;
} items = { NULL, NULL, NULL };
int index, item_index;
- ptrdiff_t node_index;
yaml_char_t *tag = first_event->data.sequence_start.tag;
if (!STACK_LIMIT(parser, parser->document->nodes, INT_MAX-1)) goto error;
@@ -345,7 +339,7 @@ yaml_parser_load_sequence(yaml_parser_t *parser, yaml_event_t *first_event)
if (!tag) goto error;
}
- if (!STACK_INIT(parser, items, INITIAL_STACK_SIZE)) goto error;
+ if (!STACK_INIT(parser, items, yaml_node_item_t*)) goto error;
SEQUENCE_NODE_INIT(node, tag, items.start, items.end,
first_event->data.sequence_start.style,
@@ -353,11 +347,7 @@ yaml_parser_load_sequence(yaml_parser_t *parser, yaml_event_t *first_event)
if (!PUSH(parser, parser->document->nodes, node)) goto error;
- node_index = parser->document->nodes.top - parser->document->nodes.start;
-#if PTRDIFF_MAX > INT_MAX
- if (node_index > INT_MAX) goto error;
-#endif
- index = (int)node_index;
+ index = parser->document->nodes.top - parser->document->nodes.start;
if (!yaml_parser_register_anchor(parser, index,
first_event->data.sequence_start.anchor)) return 0;
@@ -401,7 +391,6 @@ yaml_parser_load_mapping(yaml_parser_t *parser, yaml_event_t *first_event)
yaml_node_pair_t *top;
} pairs = { NULL, NULL, NULL };
int index;
- ptrdiff_t node_index;
yaml_node_pair_t pair;
yaml_char_t *tag = first_event->data.mapping_start.tag;
@@ -413,7 +402,7 @@ yaml_parser_load_mapping(yaml_parser_t *parser, yaml_event_t *first_event)
if (!tag) goto error;
}
- if (!STACK_INIT(parser, pairs, INITIAL_STACK_SIZE)) goto error;
+ if (!STACK_INIT(parser, pairs, yaml_node_pair_t*)) goto error;
MAPPING_NODE_INIT(node, tag, pairs.start, pairs.end,
first_event->data.mapping_start.style,
@@ -421,11 +410,7 @@ yaml_parser_load_mapping(yaml_parser_t *parser, yaml_event_t *first_event)
if (!PUSH(parser, parser->document->nodes, node)) goto error;
- node_index = parser->document->nodes.top - parser->document->nodes.start;
-#if PTRDIFF_MAX > INT_MAX
- if (node_index > INT_MAX) goto error;
-#endif
- index = (int)node_index;
+ index = parser->document->nodes.top - parser->document->nodes.start;
if (!yaml_parser_register_anchor(parser, index,
first_event->data.mapping_start.anchor)) return 0;
diff --git a/ext/psych/yaml/parser.c b/ext/psych/yaml/parser.c
index 32671b252c..621f676bf2 100644
--- a/ext/psych/yaml/parser.c
+++ b/ext/psych/yaml/parser.c
@@ -605,7 +605,7 @@ yaml_parser_parse_node(yaml_parser_t *parser, yaml_event_t *event,
if (strcmp((char *)tag_directive->handle, (char *)tag_handle) == 0) {
size_t prefix_len = strlen((char *)tag_directive->prefix);
size_t suffix_len = strlen((char *)tag_suffix);
- tag = yaml_malloc(prefix_len+suffix_len+1);
+ tag = YAML_MALLOC(prefix_len+suffix_len+1);
if (!tag) {
parser->error = YAML_MEMORY_ERROR;
goto error;
@@ -685,7 +685,7 @@ yaml_parser_parse_node(yaml_parser_t *parser, yaml_event_t *event,
return 1;
}
else if (anchor || tag) {
- yaml_char_t *value = yaml_malloc(1);
+ yaml_char_t *value = YAML_MALLOC(1);
if (!value) {
parser->error = YAML_MEMORY_ERROR;
goto error;
@@ -1208,7 +1208,7 @@ yaml_parser_process_empty_scalar(yaml_parser_t *parser, yaml_event_t *event,
{
yaml_char_t *value;
- value = yaml_malloc(1);
+ value = YAML_MALLOC(1);
if (!value) {
parser->error = YAML_MEMORY_ERROR;
return 0;
@@ -1245,7 +1245,7 @@ yaml_parser_process_directives(yaml_parser_t *parser,
} tag_directives = { NULL, NULL, NULL };
yaml_token_t *token;
- if (!STACK_INIT(parser, tag_directives, INITIAL_STACK_SIZE))
+ if (!STACK_INIT(parser, tag_directives, yaml_tag_directive_t*))
goto error;
token = PEEK_TOKEN(parser);
@@ -1266,7 +1266,7 @@ yaml_parser_process_directives(yaml_parser_t *parser,
"found incompatible YAML document", token->start_mark);
goto error;
}
- version_directive = yaml_malloc(sizeof(yaml_version_directive_t));
+ version_directive = YAML_MALLOC_STATIC(yaml_version_directive_t);
if (!version_directive) {
parser->error = YAML_MEMORY_ERROR;
goto error;
diff --git a/ext/psych/yaml/reader.c b/ext/psych/yaml/reader.c
index f1a06deb9d..f3ac54c251 100644
--- a/ext/psych/yaml/reader.c
+++ b/ext/psych/yaml/reader.c
@@ -460,10 +460,10 @@ yaml_parser_update_buffer(yaml_parser_t *parser, size_t length)
}
- if (parser->offset >= PTRDIFF_MAX)
+ if (parser->offset >= MAX_FILE_SIZE) {
return yaml_parser_set_reader_error(parser, "input is too long",
- PTRDIFF_MAX, -1);
+ parser->offset, -1);
+ }
return 1;
}
-
diff --git a/ext/psych/yaml/scanner.c b/ext/psych/yaml/scanner.c
index 5e4875d7f9..359f1072f1 100644
--- a/ext/psych/yaml/scanner.c
+++ b/ext/psych/yaml/scanner.c
@@ -1222,14 +1222,12 @@ yaml_parser_roll_indent(yaml_parser_t *parser, ptrdiff_t column,
if (!PUSH(parser, parser->indents, parser->indent))
return 0;
-#if PTRDIFF_MAX > INT_MAX
if (column > INT_MAX) {
parser->error = YAML_MEMORY_ERROR;
return 0;
}
-#endif
- parser->indent = (int)column;
+ parser->indent = column;
/* Create a token and insert it into the queue. */
@@ -1638,7 +1636,7 @@ yaml_parser_fetch_key(yaml_parser_t *parser)
if (!parser->flow_level)
{
- /* Check if we are allowed to start a new key (not nessesary simple). */
+ /* Check if we are allowed to start a new key (not necessary simple). */
if (!parser->simple_key_allowed) {
return yaml_parser_set_scanner_error(parser, NULL, parser->mark,
@@ -2401,7 +2399,7 @@ yaml_parser_scan_tag(yaml_parser_t *parser, yaml_token_t *token)
{
/* Set the handle to '' */
- handle = yaml_malloc(1);
+ handle = YAML_MALLOC(1);
if (!handle) goto error;
handle[0] = '\0';
@@ -2453,7 +2451,7 @@ yaml_parser_scan_tag(yaml_parser_t *parser, yaml_token_t *token)
/* Set the handle to '!'. */
yaml_free(handle);
- handle = yaml_malloc(2);
+ handle = YAML_MALLOC(2);
if (!handle) goto error;
handle[0] = '!';
handle[1] = '\0';
@@ -3162,8 +3160,8 @@ yaml_parser_scan_flow_scalar(yaml_parser_t *parser, yaml_token_t *token,
*(string.pointer++) = '"';
break;
- case '\'':
- *(string.pointer++) = '\'';
+ case '/':
+ *(string.pointer++) = '/';
break;
case '\\':
@@ -3280,6 +3278,11 @@ yaml_parser_scan_flow_scalar(yaml_parser_t *parser, yaml_token_t *token,
/* Check if we are at the end of the scalar. */
+ /* Fix for crash unitialized value crash
+ * Credit for the bug and input is to OSS Fuzz
+ * Credit for the fix to Alex Gaynor
+ */
+ if (!CACHE(parser, 1)) goto error;
if (CHECK(parser->buffer, single ? '\'' : '"'))
break;
@@ -3504,7 +3507,7 @@ yaml_parser_scan_plain_scalar(yaml_parser_t *parser, yaml_token_t *token)
{
if (IS_BLANK(parser->buffer))
{
- /* Check for tab character that abuse indentation. */
+ /* Check for tab characters that abuse indentation. */
if (leading_blanks && (int)parser->mark.column < indent
&& IS_TAB(parser->buffer)) {
@@ -3573,4 +3576,3 @@ error:
return 0;
}
-
diff --git a/ext/psych/yaml/yaml_private.h b/ext/psych/yaml/yaml_private.h
index 944499c94e..f4f244cbc8 100644
--- a/ext/psych/yaml/yaml_private.h
+++ b/ext/psych/yaml/yaml_private.h
@@ -12,16 +12,6 @@
#include <limits.h>
#include <stddef.h>
-#ifndef _MSC_VER
-#include <stdint.h>
-#else
-#ifdef _WIN64
-#define PTRDIFF_MAX _I64_MAX
-#else
-#define PTRDIFF_MAX INT_MAX
-#endif
-#endif
-
/*
* Memory management.
*/
@@ -81,6 +71,17 @@ yaml_parser_fetch_more_tokens(yaml_parser_t *parser);
#define OUTPUT_RAW_BUFFER_SIZE (OUTPUT_BUFFER_SIZE*2+2)
/*
+ * The maximum size of a YAML input file.
+ * This used to be PTRDIFF_MAX, but that's not entirely portable
+ * because stdint.h isn't available on all platforms.
+ * It is not entirely clear why this isn't the maximum value
+ * that can fit into the parser->offset field.
+ */
+
+#define MAX_FILE_SIZE (~(size_t)0 / 2)
+
+
+/*
* The size of other stacks and queues.
*/
@@ -93,7 +94,7 @@ yaml_parser_fetch_more_tokens(yaml_parser_t *parser);
*/
#define BUFFER_INIT(context,buffer,size) \
- (((buffer).start = yaml_malloc(size)) ? \
+ (((buffer).start = (yaml_char_t *)yaml_malloc(size)) ? \
((buffer).last = (buffer).pointer = (buffer).start, \
(buffer).end = (buffer).start+(size), \
1) : \
@@ -133,7 +134,7 @@ yaml_string_join(
(value).pointer = (string))
#define STRING_INIT(context,string,size) \
- (((string).start = yaml_malloc(size)) ? \
+ (((string).start = YAML_MALLOC(size)) ? \
((string).pointer = (string).start, \
(string).end = (string).start+(size), \
memset((string).start, 0, (size)), \
@@ -423,10 +424,10 @@ yaml_stack_extend(void **start, void **top, void **end);
YAML_DECLARE(int)
yaml_queue_extend(void **start, void **head, void **tail, void **end);
-#define STACK_INIT(context,stack,size) \
- (((stack).start = yaml_malloc((size)*sizeof(*(stack).start))) ? \
+#define STACK_INIT(context,stack,type) \
+ (((stack).start = (type)yaml_malloc(INITIAL_STACK_SIZE*sizeof(*(stack).start))) ? \
((stack).top = (stack).start, \
- (stack).end = (stack).start+(size), \
+ (stack).end = (stack).start+INITIAL_STACK_SIZE, \
1) : \
((context)->error = YAML_MEMORY_ERROR, \
0))
@@ -436,8 +437,7 @@ yaml_queue_extend(void **start, void **head, void **tail, void **end);
(stack).start = (stack).top = (stack).end = 0)
#define STACK_EMPTY(context,stack) \
- ((void)(context), \
- ((stack).start == (stack).top))
+ ((stack).start == (stack).top)
#define STACK_LIMIT(context,stack,size) \
((stack).top - (stack).start < (size) ? \
@@ -457,8 +457,8 @@ yaml_queue_extend(void **start, void **head, void **tail, void **end);
#define POP(context,stack) \
(*(--(stack).top))
-#define QUEUE_INIT(context,queue,size) \
- (((queue).start = yaml_malloc((size)*sizeof(*(queue).start))) ? \
+#define QUEUE_INIT(context,queue,size,type) \
+ (((queue).start = (type)yaml_malloc((size)*sizeof(*(queue).start))) ? \
((queue).head = (queue).tail = (queue).start, \
(queue).end = (queue).start+(size), \
1) : \
@@ -662,3 +662,27 @@ yaml_queue_extend(void **start, void **head, void **tail, void **end);
(node).data.mapping.pairs.top = (node_pairs_start), \
(node).data.mapping.style = (node_style))
+/* Strict C compiler warning helpers */
+
+#if defined(__clang__) || defined(__GNUC__)
+# define HASATTRIBUTE_UNUSED
+#endif
+#ifdef HASATTRIBUTE_UNUSED
+# define __attribute__unused__ __attribute__((__unused__))
+#else
+# define __attribute__unused__
+#endif
+
+/* Shim arguments are arguments that must be included in your function,
+ * but serve no purpose inside. Silence compiler warnings. */
+#define SHIM(a) /*@unused@*/ a __attribute__unused__
+
+/* UNUSED_PARAM() marks a shim argument in the body to silence compiler warnings */
+#ifdef __clang__
+# define UNUSED_PARAM(a) (void)(a);
+#else
+# define UNUSED_PARAM(a) /*@-noeffect*/if (0) (void)(a)/*@=noeffect*/;
+#endif
+
+#define YAML_MALLOC_STATIC(type) (type*)yaml_malloc(sizeof(type))
+#define YAML_MALLOC(size) (yaml_char_t *)yaml_malloc(size)
diff --git a/ext/pty/depend b/ext/pty/depend
index 432ad0647c..4f0595c99d 100644
--- a/ext/pty/depend
+++ b/ext/pty/depend
@@ -7,6 +7,7 @@ pty.o: $(hdrdir)/ruby/encoding.h
pty.o: $(hdrdir)/ruby/intern.h
pty.o: $(hdrdir)/ruby/io.h
pty.o: $(hdrdir)/ruby/missing.h
+pty.o: $(hdrdir)/ruby/onigmo.h
pty.o: $(hdrdir)/ruby/oniguruma.h
pty.o: $(hdrdir)/ruby/ruby.h
pty.o: $(hdrdir)/ruby/st.h
diff --git a/ext/pty/extconf.rb b/ext/pty/extconf.rb
index 844902b1f7..7721a744c8 100644
--- a/ext/pty/extconf.rb
+++ b/ext/pty/extconf.rb
@@ -1,9 +1,9 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'mkmf'
$INCFLAGS << " -I$(topdir) -I$(top_srcdir)"
-if /mswin|mingw|bccwin|nacl/ !~ RUBY_PLATFORM
+if /mswin|mingw|bccwin/ !~ RUBY_PLATFORM
have_header("sys/stropts.h")
have_func("setresuid")
have_header("libutil.h")
diff --git a/ext/pty/lib/expect.rb b/ext/pty/lib/expect.rb
index 122562127d..5dbfa09ae9 100644
--- a/ext/pty/lib/expect.rb
+++ b/ext/pty/lib/expect.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
$expect_verbose = false
# Expect library adds the IO instance method #expect, which does similar act to
@@ -31,7 +31,7 @@ class IO
# or yielded. However, the buffer in a timeout session is kept for the next
# expect call. The default timeout is 9999999 seconds.
def expect(pat,timeout=9999999)
- buf = ''
+ buf = ''.dup
case pat
when String
e_pat = Regexp.new(Regexp.quote(pat))
@@ -43,13 +43,13 @@ class IO
@unusedBuf ||= ''
while true
if not @unusedBuf.empty?
- c = @unusedBuf.slice!(0).chr
+ c = @unusedBuf.slice!(0)
elsif !IO.select([self],nil,nil,timeout) or eof? then
result = nil
@unusedBuf = buf
break
else
- c = getc.chr
+ c = getc
end
buf << c
if $expect_verbose
@@ -57,7 +57,7 @@ class IO
STDOUT.flush
end
if mat=e_pat.match(buf) then
- result = [buf,*mat.to_a[1..-1]]
+ result = [buf,*mat.captures]
break
end
end
@@ -69,4 +69,3 @@ class IO
nil
end
end
-
diff --git a/ext/pty/pty.c b/ext/pty/pty.c
index 2ac412cfbc..7b9df4b5b9 100644
--- a/ext/pty/pty.c
+++ b/ext/pty/pty.c
@@ -35,8 +35,8 @@
#endif
#include <ctype.h>
-#include "internal.h"
#include "ruby/io.h"
+#include "internal.h"
#include "ruby/util.h"
#include <signal.h>
@@ -143,7 +143,7 @@ chfunc(void *data, char *errbuf, size_t errbuf_len)
dup2(slave,0);
dup2(slave,1);
dup2(slave,2);
- close(slave);
+ if (slave < 0 || slave > 2) (void)!close(slave);
#if defined(HAVE_SETEUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRESUID)
if (seteuid(getuid())) ERROR_EXIT("seteuid()");
#endif
@@ -182,7 +182,7 @@ establishShell(int argc, VALUE *argv, struct pty_info *info,
argv = &v;
}
- carg.execarg_obj = rb_execarg_new(argc, argv, 1);
+ carg.execarg_obj = rb_execarg_new(argc, argv, 1, 0);
carg.eargp = rb_execarg_get(carg.execarg_obj);
rb_execarg_parent_start(carg.execarg_obj);
@@ -224,6 +224,21 @@ no_mesg(char *slavedevice, int nomesg)
}
#endif
+#if defined(I_PUSH) && !defined(__linux__) && !defined(_AIX)
+static inline int
+ioctl_I_PUSH(int fd, const char *const name)
+{
+ int ret = 0;
+# if defined(I_FIND)
+ ret = ioctl(fd, I_FIND, name);
+# endif
+ if (ret == 0) {
+ ret = ioctl(fd, I_PUSH, name);
+ }
+ return ret;
+}
+#endif
+
static int
get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, int fail)
{
@@ -231,19 +246,13 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
/* Unix98 PTY */
int masterfd = -1, slavefd = -1;
char *slavedevice;
- struct sigaction dfl, old;
-
- dfl.sa_handler = SIG_DFL;
- dfl.sa_flags = 0;
- sigemptyset(&dfl.sa_mask);
-#if defined(__sun) || (defined(__FreeBSD__) && __FreeBSD_version < 902000)
+#if defined(__sun) || defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD_version < 902000)
/* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */
/* FreeBSD 9.2 or later supports O_CLOEXEC
* http://www.freebsd.org/cgi/query-pr.cgi?pr=162374 */
if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error;
- if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
- if (grantpt(masterfd) == -1) goto grantpt_error;
+ if (rb_grantpt(masterfd) == -1) goto error;
rb_fd_fix_cloexec(masterfd);
#else
{
@@ -257,10 +266,8 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
if ((masterfd = posix_openpt(flags)) == -1) goto error;
}
rb_fd_fix_cloexec(masterfd);
- if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
- if (grantpt(masterfd) == -1) goto grantpt_error;
+ if (rb_grantpt(masterfd) == -1) goto error;
#endif
- if (sigaction(SIGCHLD, &old, NULL) == -1) goto error;
if (unlockpt(masterfd) == -1) goto error;
if ((slavedevice = ptsname(masterfd)) == NULL) goto error;
if (no_mesg(slavedevice, nomesg) == -1) goto error;
@@ -268,9 +275,9 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
rb_update_max_fd(slavefd);
#if defined(I_PUSH) && !defined(__linux__) && !defined(_AIX)
- if (ioctl(slavefd, I_PUSH, "ptem") == -1) goto error;
- if (ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error;
- if (ioctl(slavefd, I_PUSH, "ttcompat") == -1) goto error;
+ if (ioctl_I_PUSH(slavefd, "ptem") == -1) goto error;
+ if (ioctl_I_PUSH(slavefd, "ldterm") == -1) goto error;
+ if (ioctl_I_PUSH(slavefd, "ttcompat") == -1) goto error;
#endif
*master = masterfd;
@@ -278,8 +285,6 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
strlcpy(SlaveName, slavedevice, DEVICELEN);
return 0;
- grantpt_error:
- sigaction(SIGCHLD, &old, NULL);
error:
if (slavefd != -1) close(slavefd);
if (masterfd != -1) close(masterfd);
@@ -331,30 +336,26 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
extern char *ptsname(int);
extern int unlockpt(int);
- extern int grantpt(int);
#if defined(__sun)
/* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */
if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
- s = signal(SIGCHLD, SIG_DFL);
- if(grantpt(masterfd) == -1) goto error;
+ if(rb_grantpt(masterfd) == -1) goto error;
rb_fd_fix_cloexec(masterfd);
#else
if((masterfd = rb_cloexec_open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
rb_update_max_fd(masterfd);
- s = signal(SIGCHLD, SIG_DFL);
- if(grantpt(masterfd) == -1) goto error;
+ if(rb_grantpt(masterfd) == -1) goto error;
#endif
- signal(SIGCHLD, s);
if(unlockpt(masterfd) == -1) goto error;
if((slavedevice = ptsname(masterfd)) == NULL) goto error;
if (no_mesg(slavedevice, nomesg) == -1) goto error;
if((slavefd = rb_cloexec_open(slavedevice, O_RDWR, 0)) == -1) goto error;
rb_update_max_fd(slavefd);
#if defined(I_PUSH) && !defined(__linux__) && !defined(_AIX)
- if(ioctl(slavefd, I_PUSH, "ptem") == -1) goto error;
- if(ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error;
- ioctl(slavefd, I_PUSH, "ttcompat");
+ if(ioctl_I_PUSH(slavefd, "ptem") == -1) goto error;
+ if(ioctl_I_PUSH(slavefd, "ldterm") == -1) goto error;
+ ioctl_I_PUSH(slavefd, "ttcompat");
#endif
*master = masterfd;
*slave = slavefd;
@@ -372,62 +373,34 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
int i;
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"
+
#if defined(__hpux)
static const char MasterDevice[] = "/dev/ptym/pty%s";
static const char SlaveDevice[] = "/dev/pty/tty%s";
static const char deviceNo[][3] = {
- "p0","p1","p2","p3","p4","p5","p6","p7",
- "p8","p9","pa","pb","pc","pd","pe","pf",
- "q0","q1","q2","q3","q4","q5","q6","q7",
- "q8","q9","qa","qb","qc","qd","qe","qf",
- "r0","r1","r2","r3","r4","r5","r6","r7",
- "r8","r9","ra","rb","rc","rd","re","rf",
- "s0","s1","s2","s3","s4","s5","s6","s7",
- "s8","s9","sa","sb","sc","sd","se","sf",
- "t0","t1","t2","t3","t4","t5","t6","t7",
- "t8","t9","ta","tb","tc","td","te","tf",
- "u0","u1","u2","u3","u4","u5","u6","u7",
- "u8","u9","ua","ub","uc","ud","ue","uf",
- "v0","v1","v2","v3","v4","v5","v6","v7",
- "v8","v9","va","vb","vc","vd","ve","vf",
- "w0","w1","w2","w3","w4","w5","w6","w7",
- "w8","w9","wa","wb","wc","wd","we","wf",
+ HEX1("p"), HEX1("q"), HEX1("r"), HEX1("s"),
+ HEX1("t"), HEX1("u"), HEX1("v"), HEX1("w"),
};
#elif defined(_IBMESA) /* AIX/ESA */
static const char MasterDevice[] = "/dev/ptyp%s";
static const char SlaveDevice[] = "/dev/ttyp%s";
static const char deviceNo[][3] = {
- "00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f",
- "10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f",
- "20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f",
- "30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f",
- "40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f",
- "50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f",
- "60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f",
- "70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f",
- "80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f",
- "90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f",
- "a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af",
- "b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf",
- "c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf",
- "d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df",
- "e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef",
- "f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff",
+ 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] = {
- "p0","p1","p2","p3","p4","p5","p6","p7",
- "p8","p9","pa","pb","pc","pd","pe","pf",
- "q0","q1","q2","q3","q4","q5","q6","q7",
- "q8","q9","qa","qb","qc","qd","qe","qf",
- "r0","r1","r2","r3","r4","r5","r6","r7",
- "r8","r9","ra","rb","rc","rd","re","rf",
- "s0","s1","s2","s3","s4","s5","s6","s7",
- "s8","s9","sa","sb","sc","sd","se","sf",
+ 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);
@@ -692,7 +665,7 @@ pty_check(int argc, VALUE *argv, VALUE self)
if (!RTEST(exc)) return rb_last_status_get();
raise_from_check(cpid, status);
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
static VALUE cPTY;
@@ -707,7 +680,7 @@ static VALUE cPTY;
/*
* Document-class: PTY
*
- * Creates and managed pseudo terminals (PTYs). See also
+ * Creates and manages pseudo terminals (PTYs). See also
* http://en.wikipedia.org/wiki/Pseudo_terminal
*
* PTY allows you to allocate new terminals using ::open or ::spawn a new
diff --git a/ext/rbconfig/sizeof/depend b/ext/rbconfig/sizeof/depend
index 589837bee7..143cbb0ee5 100644
--- a/ext/rbconfig/sizeof/depend
+++ b/ext/rbconfig/sizeof/depend
@@ -1,13 +1,29 @@
+# sources
+
+limits.c: $(top_srcdir)/tool/generic_erb.rb $(top_srcdir)/template/limits.c.tmpl
+ $(Q) $(RUBY) $(top_srcdir)/tool/generic_erb.rb --output=$@ \
+ $(top_srcdir)/template/limits.c.tmpl \
+
sizes.c: $(top_srcdir)/tool/generic_erb.rb \
$(top_srcdir)/template/sizes.c.tmpl \
- $(top_srcdir)/configure.in \
+ $(top_srcdir)/configure.ac \
$(top_srcdir)/ext/rbconfig/sizeof/extconf.rb
$(Q) $(RUBY) $(top_srcdir)/tool/generic_erb.rb --output=$@ \
$(top_srcdir)/template/sizes.c.tmpl \
- $(top_srcdir)/configure.in \
- $(top_srcdir)/ext/rbconfig/sizeof/extconf.rb
+ $(top_srcdir)/configure.ac \
+ $(top_srcdir)/ext/rbconfig/sizeof/extconf.rb
# AUTOGENERATED DEPENDENCIES START
+limits.o: $(RUBY_EXTCONF_H)
+limits.o: $(arch_hdrdir)/ruby/config.h
+limits.o: $(hdrdir)/ruby/backward.h
+limits.o: $(hdrdir)/ruby/defines.h
+limits.o: $(hdrdir)/ruby/intern.h
+limits.o: $(hdrdir)/ruby/missing.h
+limits.o: $(hdrdir)/ruby/ruby.h
+limits.o: $(hdrdir)/ruby/st.h
+limits.o: $(hdrdir)/ruby/subst.h
+limits.o: limits.c
sizes.o: $(RUBY_EXTCONF_H)
sizes.o: $(arch_hdrdir)/ruby/config.h
sizes.o: $(hdrdir)/ruby/backward.h
diff --git a/ext/rbconfig/sizeof/extconf.rb b/ext/rbconfig/sizeof/extconf.rb
index 8cb16ca866..f7cd58f2d9 100644
--- a/ext/rbconfig/sizeof/extconf.rb
+++ b/ext/rbconfig/sizeof/extconf.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-$srcs = %w[sizes.c]
+$srcs = %w[sizes.c limits.c]
$distcleanfiles.concat($srcs)
have_type('int_least8_t')
diff --git a/ext/readline/.gitignore b/ext/readline/.gitignore
new file mode 100644
index 0000000000..3d372989ae
--- /dev/null
+++ b/ext/readline/.gitignore
@@ -0,0 +1 @@
+/readline-[1-9]*.*
diff --git a/ext/readline/depend b/ext/readline/depend
index 819328288e..eb7a047473 100644
--- a/ext/readline/depend
+++ b/ext/readline/depend
@@ -7,6 +7,7 @@ readline.o: $(hdrdir)/ruby/encoding.h
readline.o: $(hdrdir)/ruby/intern.h
readline.o: $(hdrdir)/ruby/io.h
readline.o: $(hdrdir)/ruby/missing.h
+readline.o: $(hdrdir)/ruby/onigmo.h
readline.o: $(hdrdir)/ruby/oniguruma.h
readline.o: $(hdrdir)/ruby/ruby.h
readline.o: $(hdrdir)/ruby/st.h
diff --git a/ext/readline/extconf.rb b/ext/readline/extconf.rb
index 7bba386540..fcc62921ae 100644
--- a/ext/readline/extconf.rb
+++ b/ext/readline/extconf.rb
@@ -71,6 +71,7 @@ readline.have_func("rl_completion_matches")
readline.have_func("rl_refresh_line")
readline.have_var("rl_deprep_term_function")
readline.have_var("rl_completion_append_character")
+readline.have_var("rl_completion_quote_character")
readline.have_var("rl_basic_word_break_characters")
readline.have_var("rl_completer_word_break_characters")
readline.have_var("rl_basic_quote_characters")
@@ -83,9 +84,9 @@ readline.have_var("rl_line_buffer")
readline.have_var("rl_point")
readline.have_var("rl_char_is_quoted_p")
# workaround for native windows.
-/mswin|bccwin|mingw/ !~ RUBY_PLATFORM && readline.have_var("rl_event_hook")
-/mswin|bccwin|mingw/ !~ RUBY_PLATFORM && readline.have_var("rl_catch_sigwinch")
-/mswin|bccwin|mingw/ !~ RUBY_PLATFORM && readline.have_var("rl_catch_signals")
+/mswin|bccwin/ !~ RUBY_PLATFORM && readline.have_var("rl_event_hook")
+/mswin|bccwin/ !~ RUBY_PLATFORM && readline.have_var("rl_catch_sigwinch")
+/mswin|bccwin/ !~ RUBY_PLATFORM && readline.have_var("rl_catch_signals")
readline.have_var("rl_pre_input_hook")
readline.have_var("rl_special_prefixes")
readline.have_func("rl_cleanup_after_signal")
diff --git a/ext/readline/readline.c b/ext/readline/readline.c
index 6ed558e334..3380720f47 100644
--- a/ext/readline/readline.c
+++ b/ext/readline/readline.c
@@ -77,6 +77,8 @@ static ID id_special_prefixes;
#endif
#ifndef HAVE_RL_USERNAME_COMPLETION_FUNCTION
# define rl_username_completion_function username_completion_function
+#else
+char *rl_username_completion_function(const char *, int);
#endif
#ifndef HAVE_RL_COMPLETION_MATCHES
# define rl_completion_matches completion_matches
@@ -92,7 +94,8 @@ static char **readline_attempted_completion_function(const char *text,
int start, int end);
#define OutputStringValue(str) do {\
- SafeStringValue(str);\
+ StringValueCStr(str);\
+ rb_check_safe_obj(str);\
(str) = rb_str_conv_enc((str), rb_enc_get(str), rb_locale_encoding());\
} while (0)\
@@ -164,24 +167,25 @@ getc_body(struct getc_struct *p)
#if defined(_WIN32)
{
INPUT_RECORD ir;
- int n;
+ DWORD n;
static int prior_key = '0';
for (;;) {
+ HANDLE h;
if (prior_key > 0xff) {
prior_key = rl_getc(p->input);
return prior_key;
}
- if (PeekConsoleInput((HANDLE)_get_osfhandle(p->fd), &ir, 1, &n)) {
+ h = (HANDLE)_get_osfhandle(p->fd);
+ if (PeekConsoleInput(h, &ir, 1, &n)) {
if (n == 1) {
if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) {
prior_key = rl_getc(p->input);
return prior_key;
} else {
- ReadConsoleInput((HANDLE)_get_osfhandle(p->fd), &ir, 1, &n);
+ ReadConsoleInput(h, &ir, 1, &n);
}
} else {
- HANDLE h = (HANDLE)_get_osfhandle(p->fd);
- rb_w32_wait_events(&h, 1, INFINITE);
+ rb_w32_wait_events_blocking(&h, 1, INFINITE);
}
} else {
break;
@@ -686,6 +690,17 @@ readline_s_insert_text(VALUE self, VALUE str)
#endif
#if defined(HAVE_RL_DELETE_TEXT)
+int rl_delete_text(int, int);
+static const char *
+str_subpos(const char *ptr, const char *end, long beg, long *sublen, rb_encoding *enc)
+{
+ VALUE str = rb_enc_str_new_static(ptr, end-ptr, enc);
+ OBJ_FREEZE(str);
+ ptr = rb_str_subpos(str, beg, sublen);
+ rb_gc_force_recycle(str);
+ return ptr;
+}
+
/*
* call-seq:
* Readline.delete_text([start[, length]]) -> self
@@ -703,20 +718,20 @@ readline_s_delete_text(int argc, VALUE *argv, VALUE self)
{
rb_check_arity(argc, 0, 2);
if (rl_line_buffer) {
- char *p, *ptr = rl_line_buffer;
- long beg = 0, len = strlen(rl_line_buffer);
- VALUE str = rb_enc_str_new_static(ptr, len, rb_locale_encoding());
- OBJ_FREEZE(str);
+ const char *p, *ptr = rl_line_buffer;
+ long beg = 0, len = strlen(ptr);
+ const char *end = ptr + len;
+ rb_encoding *enc = rb_locale_encoding();
if (argc == 2) {
beg = NUM2LONG(argv[0]);
len = NUM2LONG(argv[1]);
num_pos:
- p = rb_str_subpos(str, beg, &len);
+ p = str_subpos(ptr, end, beg, &len, enc);
if (!p) rb_raise(rb_eArgError, "invalid index");
beg = p - ptr;
}
else if (argc == 1) {
- len = rb_str_strlen(str);
+ len = rb_enc_strlen(ptr, ptr + len, enc);
if (!rb_range_beg_len(argv[0], &beg, &len, len, 1)) {
beg = NUM2LONG(argv[0]);
goto num_pos;
@@ -808,7 +823,7 @@ readline_s_redisplay(VALUE self)
*
* When working with auto-complete there are some strategies that work well.
* To get some ideas you can take a look at the
- * completion.rb[http://svn.ruby-lang.org/repos/ruby/trunk/lib/irb/completion.rb]
+ * completion.rb[https://svn.ruby-lang.org/repos/ruby/trunk/lib/irb/completion.rb]
* file for irb.
*
* The common strategy is to take a list of possible completions and filter it
@@ -1134,6 +1149,7 @@ readline_s_get_screen_size(VALUE self)
#endif
#ifdef HAVE_RL_VI_EDITING_MODE
+int rl_vi_editing_mode(int, int);
/*
* call-seq:
* Readline.vi_editing_mode -> nil
@@ -1172,6 +1188,7 @@ readline_s_vi_editing_mode_p(VALUE self)
#endif
#ifdef HAVE_RL_EMACS_EDITING_MODE
+int rl_emacs_editing_mode(int, int);
/*
* call-seq:
* Readline.emacs_editing_mode -> nil
@@ -1291,6 +1308,35 @@ readline_s_get_completion_append_character(VALUE self)
#define readline_s_get_completion_append_character rb_f_notimplement
#endif
+#ifdef HAVE_RL_COMPLETION_QUOTE_CHARACTER
+/*
+ * call-seq:
+ * Readline.completion_quote_character -> char
+ *
+ * When called during a completion (e.g. from within your completion_proc),
+ * it will return a string containing the character used to quote the
+ * argument being completed, or nil if the argument is unquoted.
+ *
+ * When called at other times, it will always return nil.
+ *
+ * Note that Readline.completer_quote_characters must be set,
+ * or this method will always return nil.
+ */
+static VALUE
+readline_s_get_completion_quote_character(VALUE self)
+{
+ char buf[1];
+
+ if (rl_completion_quote_character == '\0')
+ return Qnil;
+
+ buf[0] = (char) rl_completion_quote_character;
+ return rb_locale_str_new(buf, 1);
+}
+#else
+#define readline_s_get_completion_quote_character rb_f_notimplement
+#endif
+
#ifdef HAVE_RL_BASIC_WORD_BREAK_CHARACTERS
/*
* call-seq:
@@ -1627,6 +1673,7 @@ readline_s_get_filename_quote_characters(VALUE self, VALUE str)
#endif
#ifdef HAVE_RL_REFRESH_LINE
+int rl_refresh_line(int, int);
/*
* call-seq:
* Readline.refresh_line -> nil
@@ -1744,7 +1791,7 @@ rb_remove_history(int index)
#else
rb_notimplement();
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
#endif
}
@@ -1872,6 +1919,10 @@ username_completion_proc_call(VALUE self, VALUE str)
return result;
}
+#ifdef HAVE_RL_CLEAR_SIGNALS
+int rl_clear_signals(void);
+#endif
+
#undef rb_intern
void
Init_readline(void)
@@ -1946,6 +1997,8 @@ Init_readline(void)
readline_s_set_completion_append_character, 1);
rb_define_singleton_method(mReadline, "completion_append_character",
readline_s_get_completion_append_character, 0);
+ rb_define_singleton_method(mReadline, "completion_quote_character",
+ readline_s_get_completion_quote_character, 0);
rb_define_singleton_method(mReadline, "basic_word_break_characters=",
readline_s_set_basic_word_break_characters, 1);
rb_define_singleton_method(mReadline, "basic_word_break_characters",
@@ -1984,8 +2037,8 @@ Init_readline(void)
readline_s_get_special_prefixes, 0);
#if USE_INSERT_IGNORE_ESCAPE
- CONST_ID(id_orig_prompt, "orig_prompt");
- CONST_ID(id_last_prompt, "last_prompt");
+ id_orig_prompt = rb_intern("orig_prompt");
+ id_last_prompt = rb_intern("last_prompt");
#endif
history = rb_obj_alloc(rb_cObject);
@@ -2066,7 +2119,7 @@ Init_readline(void)
rl_attempted_completion_function = readline_attempted_completion_function;
#if defined(HAVE_RL_PRE_INPUT_HOOK)
- rl_pre_input_hook = readline_pre_input_hook;
+ rl_pre_input_hook = (rl_hook_func_t *)readline_pre_input_hook;
#endif
#if defined HAVE_RL_CHAR_IS_QUOTED_P
rl_char_is_quoted_p = &readline_char_is_quoted;
@@ -2081,3 +2134,9 @@ Init_readline(void)
rb_gc_register_address(&readline_instream);
rb_gc_register_address(&readline_outstream);
}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: nil
+ * end:
+ */
diff --git a/ext/ripper/depend b/ext/ripper/depend
index 7ea823c427..ed07a32e99 100644
--- a/ext/ripper/depend
+++ b/ext/ripper/depend
@@ -12,15 +12,16 @@ ripper.o: ripper.c
.y.c:
$(ECHO) compiling compiler $<
$(Q) $(BISON) -t -v -oy.tab.c $<
- $(Q) sed -f $(top_srcdir)/tool/ytab.sed -e "/^#/s!y\.tab\.c!$@!" y.tab.c > $@
+ $(Q) sed -e "/^#/s!y\.tab\.c!$@!" -f $(top_srcdir)/tool/ytab.sed y.tab.c > $@
@$(RM) y.tab.c
all: check
static: check
-ripper.y: $(srcdir)/tools/preproc.rb $(top_srcdir)/parse.y
+ripper.y: $(srcdir)/tools/preproc.rb $(srcdir)/tools/dsl.rb $(top_srcdir)/parse.y {$(VPATH)}id.h
$(ECHO) extracting $@ from $(top_srcdir)/parse.y
- $(Q) $(RUBY) $(top_srcdir)/tool/id2token.rb --path-separator=.$(PATH_SEPARATOR)./ --vpath=$(VPATH) id.h $(top_srcdir)/parse.y > ripper.tmp.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) $(srcdir)/tools/preproc.rb ripper.tmp.y --output=$@
$(Q) $(RM) ripper.tmp.y
@@ -31,11 +32,11 @@ check: .eventids2-check
$(Q) $(RUBY) $(GEN) --mode=check --ids1src=$(SRC1) --ids2src=$(SRC2)
@exit > $@
-eventids1.c: $(srcdir)/tools/generate.rb $(SRC1)
+eventids1.c: $(GEN) $(srcdir)/tools/dsl.rb $(SRC1)
$(ECHO) generating $@ from $(SRC1)
$(Q) $(RUBY) $(GEN) --mode=eventids1 --ids1src=$(SRC1) --output=$@
-eventids2table.c: $(srcdir)/tools/generate.rb $(SRC2)
+eventids2table.c: $(GEN) $(srcdir)/tools/dsl.rb $(SRC2)
$(ECHO) generating $@ from $(SRC2)
$(Q) $(RUBY) $(GEN) --mode=eventids2table --ids2src=$(SRC2) --output=$@
@@ -55,6 +56,7 @@ ripper.o: $(hdrdir)/ruby/encoding.h
ripper.o: $(hdrdir)/ruby/intern.h
ripper.o: $(hdrdir)/ruby/io.h
ripper.o: $(hdrdir)/ruby/missing.h
+ripper.o: $(hdrdir)/ruby/onigmo.h
ripper.o: $(hdrdir)/ruby/oniguruma.h
ripper.o: $(hdrdir)/ruby/regex.h
ripper.o: $(hdrdir)/ruby/ruby.h
diff --git a/ext/ripper/eventids2.c b/ext/ripper/eventids2.c
index 04a40e0da7..e876e95ab3 100644
--- a/ext/ripper/eventids2.c
+++ b/ext/ripper/eventids2.c
@@ -1,12 +1,13 @@
-#define tIGNORED_NL (tLAST_TOKEN + 1)
-#define tCOMMENT (tLAST_TOKEN + 2)
-#define tEMBDOC_BEG (tLAST_TOKEN + 3)
-#define tEMBDOC (tLAST_TOKEN + 4)
-#define tEMBDOC_END (tLAST_TOKEN + 5)
-#define tSP (tLAST_TOKEN + 6)
-#define tHEREDOC_BEG (tLAST_TOKEN + 7)
-#define tHEREDOC_END (tLAST_TOKEN + 8)
-#define k__END__ (tLAST_TOKEN + 9)
+enum {
+ tIGNORED_NL = tLAST_TOKEN + 1,
+ tCOMMENT,
+ tEMBDOC_BEG,
+ tEMBDOC,
+ tEMBDOC_END,
+ tHEREDOC_BEG,
+ tHEREDOC_END,
+ k__END__
+};
typedef struct {
ID ripper_id_backref;
@@ -302,5 +303,5 @@ ripper_token2eventid(int tok)
}
rb_raise(rb_eRuntimeError, "[Ripper FATAL] unknown token %d", tok);
- UNREACHABLE;
+ UNREACHABLE_RETURN(0);
}
diff --git a/ext/ripper/extconf.rb b/ext/ripper/extconf.rb
index 91d4b320e4..89b46abcfd 100644
--- a/ext/ripper/extconf.rb
+++ b/ext/ripper/extconf.rb
@@ -1,5 +1,5 @@
#!ruby -s
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'mkmf'
require 'rbconfig'
diff --git a/ext/ripper/lib/ripper.rb b/ext/ripper/lib/ripper.rb
index c5c3a8091e..e937d65217 100644
--- a/ext/ripper/lib/ripper.rb
+++ b/ext/ripper/lib/ripper.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'ripper/core'
require 'ripper/lexer'
require 'ripper/filter'
@@ -66,9 +66,9 @@ require 'ripper/sexp'
#
# == License
#
-# Ruby License.
+# Ruby License.
#
-# Minero Aoki
-# aamine@loveruby.net
-# http://i.loveruby.net
+# - Minero Aoki
+# - aamine@loveruby.net
+# - http://i.loveruby.net
class Ripper; end
diff --git a/ext/ripper/lib/ripper/core.rb b/ext/ripper/lib/ripper/core.rb
index 53ed14d5e1..cdbaf7dd34 100644
--- a/ext/ripper/lib/ripper/core.rb
+++ b/ext/ripper/lib/ripper/core.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# $Id$
#
diff --git a/ext/ripper/lib/ripper/filter.rb b/ext/ripper/lib/ripper/filter.rb
index a50a2c6c4e..9955d30550 100644
--- a/ext/ripper/lib/ripper/filter.rb
+++ b/ext/ripper/lib/ripper/filter.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# $Id$
#
@@ -25,6 +25,7 @@ class Ripper
@__lexer = Lexer.new(src, filename, lineno)
@__line = nil
@__col = nil
+ @__state = nil
end
# The file name of the input.
@@ -46,13 +47,20 @@ class Ripper
@__col
end
+ # The scanner's state of the current token.
+ # This value is the bitwise OR of zero or more of the +Ripper::EXPR_*+ constants.
+ def state
+ @__state
+ end
+
# Starts the parser.
# +init+ is a data accumulator and is passed to the next event handler (as
# of Enumerable#inject).
def parse(init = nil)
data = init
- @__lexer.lex.each do |pos, event, tok|
+ @__lexer.lex.each do |pos, event, tok, state|
@__line, @__col = *pos
+ @__state = state
data = if respond_to?(event, true)
then __send__(event, tok, data)
else on_default(event, tok, data)
diff --git a/ext/ripper/lib/ripper/lexer.rb b/ext/ripper/lib/ripper/lexer.rb
index 69526340a7..95e1ebcdfe 100644
--- a/ext/ripper/lib/ripper/lexer.rb
+++ b/ext/ripper/lib/ripper/lexer.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# $Id$
#
@@ -23,29 +23,47 @@ class Ripper
end
# Tokenizes the Ruby program and returns an array of an array,
- # which is formatted like <code>[[lineno, column], type, token]</code>.
+ # which is formatted like
+ # <code>[[lineno, column], type, token, state]</code>.
#
# require 'ripper'
# require 'pp'
#
# pp Ripper.lex("def m(a) nil end")
- # #=> [[[1, 0], :on_kw, "def"],
- # [[1, 3], :on_sp, " " ],
- # [[1, 4], :on_ident, "m" ],
- # [[1, 5], :on_lparen, "(" ],
- # [[1, 6], :on_ident, "a" ],
- # [[1, 7], :on_rparen, ")" ],
- # [[1, 8], :on_sp, " " ],
- # [[1, 9], :on_kw, "nil"],
- # [[1, 12], :on_sp, " " ],
- # [[1, 13], :on_kw, "end"]]
+ # #=> [[[1, 0], :on_kw, "def", Ripper::EXPR_FNAME ],
+ # [[1, 3], :on_sp, " ", Ripper::EXPR_FNAME ],
+ # [[1, 4], :on_ident, "m", Ripper::EXPR_ENDFN ],
+ # [[1, 5], :on_lparen, "(", Ripper::EXPR_LABEL | Ripper::EXPR_BEG],
+ # [[1, 6], :on_ident, "a", Ripper::EXPR_ARG ],
+ # [[1, 7], :on_rparen, ")", Ripper::EXPR_ENDFN ],
+ # [[1, 8], :on_sp, " ", Ripper::EXPR_BEG ],
+ # [[1, 9], :on_kw, "nil", Ripper::EXPR_END ],
+ # [[1, 12], :on_sp, " ", Ripper::EXPR_END ],
+ # [[1, 13], :on_kw, "end", Ripper::EXPR_END ]]
#
def Ripper.lex(src, filename = '-', lineno = 1)
Lexer.new(src, filename, lineno).lex
end
class Lexer < ::Ripper #:nodoc: internal use only
- Elem = Struct.new(:pos, :event, :tok)
+ State = Struct.new(:to_int, :to_s) do
+ alias to_i to_int
+ def initialize(i) super(i, Ripper.lex_state_name(i)).freeze end
+ def inspect; "#<#{self.class}: #{self}>" end
+ def pretty_print(q) q.text(to_s) end
+ def ==(i) super or to_int == i end
+ def &(i) self.class.new(to_int & i) end
+ def |(i) self.class.new(to_int & i) end
+ def allbits?(i) to_int.allbits?(i) end
+ def anybits?(i) to_int.anybits?(i) end
+ def nobits?(i) to_int.nobits?(i) end
+ end
+
+ Elem = Struct.new(:pos, :event, :tok, :state) do
+ def initialize(pos, event, tok, state)
+ super(pos, event, tok, State.new(state))
+ end
+ end
def tokenize
parse().sort_by(&:pos).map(&:tok)
@@ -65,14 +83,32 @@ class Ripper
private
+ unless SCANNER_EVENT_TABLE.key?(:ignored_sp)
+ SCANNER_EVENT_TABLE[:ignored_sp] = 1
+ SCANNER_EVENTS << :ignored_sp
+ EVENTS << :ignored_sp
+ end
+
def on_heredoc_dedent(v, w)
- @buf.last.each do |e|
- if e.event == :on_tstring_content
+ ignored_sp = []
+ heredoc = @buf.last
+ heredoc.each_with_index do |e, i|
+ if Elem === e and e.event == :on_tstring_content and e.pos[1].zero?
+ tok = e.tok.dup if w > 0 and /\A\s/ =~ e.tok
if (n = dedent_string(e.tok, w)) > 0
+ if e.tok.empty?
+ e.tok = tok[0, n]
+ e.event = :on_ignored_sp
+ next
+ end
+ ignored_sp << [i, Elem.new(e.pos.dup, :on_ignored_sp, tok[0, n], e.state)]
e.pos[1] += n
end
end
end
+ ignored_sp.reverse_each do |i, e|
+ heredoc[i, 0] = [e]
+ end
v
end
@@ -81,16 +117,16 @@ class Ripper
buf = []
@buf << buf
@buf = buf
- @buf.push Elem.new([lineno(), column()], __callee__, tok)
+ @buf.push Elem.new([lineno(), column()], __callee__, tok, state())
end
def on_heredoc_end(tok)
- @buf.push Elem.new([lineno(), column()], __callee__, tok)
+ @buf.push Elem.new([lineno(), column()], __callee__, tok, state())
@buf = @stack.pop
end
def _push_token(tok)
- @buf.push Elem.new([lineno(), column()], __callee__, tok)
+ @buf.push Elem.new([lineno(), column()], __callee__, tok, state())
end
(SCANNER_EVENTS.map {|event|:"on_#{event}"} - private_instance_methods(false)).each do |event|
@@ -156,7 +192,7 @@ class Ripper
if m = /[^\w\s$()\[\]{}?*+\.]/.match(pattern)
raise CompileError, "invalid char in pattern: #{m[0].inspect}"
end
- buf = ''
+ buf = +''
pattern.scan(/(?:\w+|\$\(|[()\[\]\{\}?*+\.]+)/) do |tok|
case tok
when /\w/
@@ -177,14 +213,14 @@ class Ripper
end
def map_tokens(tokens)
- tokens.map {|pos,type,str| map_token(type.to_s.sub(/\Aon_/,'')) }.join
+ tokens.map {|pos,type,str| map_token(type.to_s.delete_prefix('on_')) }.join
end
MAP = {}
seed = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
SCANNER_EVENT_TABLE.each do |ev, |
raise CompileError, "[RIPPER FATAL] too many system token" if seed.empty?
- MAP[ev.to_s.sub(/\Aon_/,'')] = seed.shift
+ MAP[ev.to_s.delete_prefix('on_')] = seed.shift
end
def map_token(tok)
diff --git a/ext/ripper/lib/ripper/sexp.rb b/ext/ripper/lib/ripper/sexp.rb
index aa1f86e38c..b52dd30ddc 100644
--- a/ext/ripper/lib/ripper/sexp.rb
+++ b/ext/ripper/lib/ripper/sexp.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# $Id$
#
@@ -134,6 +134,18 @@ class Ripper
list
end
+ def on_mlhs_paren(list)
+ [:mlhs, *list]
+ end
+
+ def on_mlhs_add_star(list, star)
+ list.push([:rest_param, star])
+ end
+
+ def on_mlhs_add_post(list, post)
+ list.concat(post)
+ end
+
PARSER_EVENT_TABLE.each do |event, arity|
if /_new\z/ =~ event and arity == 0
alias_method "on_#{event}", :_dispatch_event_new
diff --git a/ext/ripper/tools/dsl.rb b/ext/ripper/tools/dsl.rb
new file mode 100644
index 0000000000..aafaa6f407
--- /dev/null
+++ b/ext/ripper/tools/dsl.rb
@@ -0,0 +1,86 @@
+# Simple DSL implementation for Ripper code generation
+#
+# input: /*% ripper: stmts_add(stmts_new, void_stmt) %*/
+# output:
+# VALUE v1, v2;
+# v1 = dispatch0(stmts_new);
+# v2 = dispatch0(void_stmt);
+# $$ = dispatch2(stmts_add, v1, v2);
+
+class DSL
+ def initialize(code, options)
+ @events = {}
+ @error = options.include?("error")
+ @brace = options.include?("brace")
+ @final = options.include?("final")
+ @vars = 0
+
+ # create $1 == "$1", $2 == "$2", ...
+ re, s = "", ""
+ 1.upto(9) do |n|
+ re << "(..)"
+ s << "$#{ n }"
+ end
+ /#{ re }/ =~ s
+
+ # struct parser_params *p
+ p = "p"
+
+ @code = ""
+ @last_value = eval(code)
+ end
+
+ attr_reader :events
+
+ undef lambda
+ undef hash
+ undef class
+
+ def generate
+ s = "$$"
+ s = "p->result" if @final
+ s = "#@code#{ s }=#@last_value;"
+ s = "{VALUE #{ (1..@vars).map {|v| "v#{ v }" }.join(",") };#{ s }}" if @vars > 0
+ s << "ripper_error(p);" if @error
+ s = "{#{ s }}" if @brace
+ "\t\t\t#{s}"
+ end
+
+ def new_var
+ "v#{ @vars += 1 }"
+ end
+
+ def opt_event(event, default, addend)
+ add_event(event, [default, addend], true)
+ end
+
+ def add_event(event, args, qundef_check = false)
+ event = event.to_s.sub(/!\z/, "")
+ @events[event] = args.size
+ vars = []
+ args.each do |arg|
+ vars << v = new_var
+ @code << "#{ v }=#{ arg };"
+ end
+ v = new_var
+ d = "dispatch#{ args.size }(#{ [event, *vars].join(",") })"
+ d = "#{ vars.last }==Qundef ? #{ vars.first } : #{ d }" if qundef_check
+ @code << "#{ v }=#{ d };"
+ v
+ end
+
+ def method_missing(event, *args)
+ if event.to_s =~ /!\z/
+ add_event(event, args)
+ elsif args.empty? and /\Aid[A-Z]/ =~ event.to_s
+ event
+ else
+ "#{ event }(#{ args.join(", ") })"
+ end
+ end
+
+ def self.const_missing(name)
+ name
+ end
+end
+
diff --git a/ext/ripper/tools/generate-param-macros.rb b/ext/ripper/tools/generate-param-macros.rb
index c1f0c5bc31..f0de55a5f2 100755
--- a/ext/ripper/tools/generate-param-macros.rb
+++ b/ext/ripper/tools/generate-param-macros.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
off = true
ARGF.each do |line|
case line
diff --git a/ext/ripper/tools/generate.rb b/ext/ripper/tools/generate.rb
index cb02de9b4b..883e6ef2df 100755
--- a/ext/ripper/tools/generate.rb
+++ b/ext/ripper/tools/generate.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# $Id$
require 'optparse'
@@ -68,7 +68,7 @@ def usage(msg)
end
def generate_eventids1(ids)
- buf = ""
+ buf = "".dup
buf << %Q[static struct {\n]
ids.each do |id, arity|
buf << %Q[ ID id_#{id};\n]
@@ -101,7 +101,7 @@ def generate_eventids1(ids)
end
def generate_eventids2_table(ids)
- buf = ""
+ buf = "".dup
buf << %Q[static void\n]
buf << %Q[ripper_init_eventids2_table(VALUE self)\n]
buf << %Q[{\n]
@@ -135,6 +135,8 @@ def check_arity(h)
abort if invalid
end
+require_relative "dsl"
+
def read_ids1_with_locations(path)
h = {}
File.open(path) {|f|
@@ -144,6 +146,13 @@ def read_ids1_with_locations(path)
line.scan(/\bdispatch(\d)\((\w+)/) do |arity, event|
(h[event] ||= []).push [f.lineno, arity.to_i]
end
+ if line =~ %r</\*% *ripper(?:\[(.*?)\])?: *(.*?) *%\*/>
+ gen = DSL.new($2, ($1 || "").split(","))
+ gen.generate
+ gen.events.each do |event, arity|
+ (h[event] ||= []).push [f.lineno, arity.to_i]
+ end
+ end
end
}
h
diff --git a/ext/ripper/tools/preproc.rb b/ext/ripper/tools/preproc.rb
index 2377506cd5..7639a901df 100755
--- a/ext/ripper/tools/preproc.rb
+++ b/ext/ripper/tools/preproc.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# $Id$
require 'optparse'
@@ -24,7 +24,7 @@ def main
unless ARGV.size == 1
abort "wrong number of arguments (#{ARGV.size} for 1)"
end
- out = ""
+ out = "".dup
File.open(ARGV[0]) {|f|
prelude f, out
grammar f, out
@@ -40,14 +40,10 @@ def main
end
def prelude(f, out)
+ @exprs = {}
+ lex_state_def = false
while line = f.gets
case line
- when %r</\*%%%\*/>
- out << '/*' << $/
- when %r</\*%>
- out << '*/' << $/
- when %r<%\*/>
- out << $/
when /\A%%/
out << '%%' << $/
return
@@ -55,21 +51,37 @@ def prelude(f, out)
out << line.sub(/<\w+>/, '<val>')
when /\A%type/
out << line.sub(/<\w+>/, '<val>')
+ when /^enum lex_state_(?:bits|e) \{/
+ lex_state_def = true
+ out << line
+ when /^\}/
+ lex_state_def = false
+ out << line
else
out << line
end
+ if lex_state_def
+ case line
+ when /^\s*(EXPR_\w+),\s+\/\*(.+)\*\//
+ @exprs[$1.chomp("_bit")] = $2.strip
+ when /^\s*(EXPR_\w+)\s+=\s+(.+)$/
+ name = $1
+ val = $2.chomp(",")
+ @exprs[name] = "equals to " + (val.start_with?("(") ? "<tt>#{val}</tt>" : "+#{val}+")
+ end
+ end
end
end
+require_relative "dsl"
+
def grammar(f, out)
while line = f.gets
case line
+ when %r</\*% *ripper(?:\[(.*?)\])?: *(.*?) *%\*/>
+ out << DSL.new($2, ($1 || "").split(",")).generate << $/
when %r</\*%%%\*/>
out << '#if 0' << $/
- when %r</\*%c%\*/>
- out << '/*' << $/
- when %r</\*%c>
- out << '*/' << $/
when %r</\*%>
out << '#endif' << $/
when %r<%\*/>
@@ -84,9 +96,12 @@ def grammar(f, out)
end
def usercode(f, out)
- while line = f.gets
- out << line
- end
+ require 'erb'
+ compiler = ERB::Compiler.new('%-')
+ compiler.put_cmd = compiler.insert_cmd = "out.<<"
+ lineno = f.lineno
+ src, = compiler.compile(f.read)
+ eval(src, binding, f.path, lineno)
end
main
diff --git a/ext/ripper/tools/strip.rb b/ext/ripper/tools/strip.rb
index 0e409eb63b..23102f797a 100755
--- a/ext/ripper/tools/strip.rb
+++ b/ext/ripper/tools/strip.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
last_is_void = false
ARGF.each do |line|
case line
diff --git a/ext/rubyvm/extconf.rb b/ext/rubyvm/extconf.rb
new file mode 100644
index 0000000000..8c40f58e2b
--- /dev/null
+++ b/ext/rubyvm/extconf.rb
@@ -0,0 +1 @@
+create_makefile("rubyvm")
diff --git a/ext/rubyvm/lib/forwardable/impl.rb b/ext/rubyvm/lib/forwardable/impl.rb
new file mode 100644
index 0000000000..e9bde2f299
--- /dev/null
+++ b/ext/rubyvm/lib/forwardable/impl.rb
@@ -0,0 +1,16 @@
+# :stopdoc:
+module Forwardable
+ def self._valid_method?(method)
+ iseq = RubyVM::InstructionSequence.compile("().#{method}", nil, nil, 0, false)
+ rescue SyntaxError
+ false
+ else
+ iseq.to_a.dig(-1, 1, 1, :mid) == method.to_sym
+ end
+
+ def self._compile_method(src, file, line)
+ RubyVM::InstructionSequence.compile(src, file, file, line,
+ trace_instruction: false)
+ .eval
+ end
+end
diff --git a/ext/sdbm/_sdbm.c b/ext/sdbm/_sdbm.c
index 9ff0e7959a..3a42b0e569 100644
--- a/ext/sdbm/_sdbm.c
+++ b/ext/sdbm/_sdbm.c
@@ -176,24 +176,24 @@ sdbm_open(register char *file, register int flags, register int mode)
static int
fd_set_cloexec(int fd)
{
- /* MinGW don't have F_GETFD and FD_CLOEXEC. [ruby-core:40281] */
+ /* MinGW don't have F_GETFD and FD_CLOEXEC. [ruby-core:40281] */
#ifdef F_GETFD
- int flags, ret;
- flags = fcntl(fd, F_GETFD); /* should not fail except EBADF. */
- if (flags == -1) {
- return -1;
- }
- if (2 < fd) {
- if (!(flags & FD_CLOEXEC)) {
- flags |= FD_CLOEXEC;
- ret = fcntl(fd, F_SETFD, flags);
- if (ret == -1) {
- return -1;
- }
- }
- }
+ int flags, ret;
+ flags = fcntl(fd, F_GETFD); /* should not fail except EBADF. */
+ if (flags == -1) {
+ return -1;
+ }
+ if (2 < fd) {
+ if (!(flags & FD_CLOEXEC)) {
+ flags |= FD_CLOEXEC;
+ ret = fcntl(fd, F_SETFD, flags);
+ if (ret == -1) {
+ return -1;
+ }
+ }
+ }
#endif
- return 0;
+ return 0;
}
DBM *
@@ -400,20 +400,20 @@ makroom(register DBM *db, long int hash, int need)
*/
#if defined _WIN32
- /*
- * Fill hole with 0 if made it.
- * (hole is NOT read as 0)
- */
- oldtail = lseek(db->pagf, 0L, SEEK_END);
- memset(zer, 0, PBLKSIZ);
- while (OFF_PAG(newp) > oldtail) {
- if (lseek(db->pagf, 0L, SEEK_END) < 0 ||
- write(db->pagf, zer, PBLKSIZ) < 0) {
+ /*
+ * Fill hole with 0 if made it.
+ * (hole is NOT read as 0)
+ */
+ oldtail = lseek(db->pagf, 0L, SEEK_END);
+ memset(zer, 0, PBLKSIZ);
+ while (OFF_PAG(newp) > oldtail) {
+ if (lseek(db->pagf, 0L, SEEK_END) < 0 ||
+ write(db->pagf, zer, PBLKSIZ) < 0) {
- return 0;
+ return 0;
+ }
+ oldtail += PBLKSIZ;
}
- oldtail += PBLKSIZ;
- }
#endif
if (hash & (db->hmask + 1)) {
diff --git a/ext/sdbm/init.c b/ext/sdbm/init.c
index 684f31b98f..42292b99cd 100644
--- a/ext/sdbm/init.c
+++ b/ext/sdbm/init.c
@@ -71,6 +71,8 @@ struct dbmdata {
DBM *di_dbm;
};
+NORETURN(static void closed_sdbm(void));
+
static void
closed_sdbm(void)
{
@@ -79,7 +81,6 @@ closed_sdbm(void)
#define GetDBM(obj, dbmp) do {\
TypedData_Get_Struct((obj), struct dbmdata, &sdbm_type, (dbmp));\
- if ((dbmp) == 0) closed_sdbm();\
if ((dbmp)->di_dbm == 0) closed_sdbm();\
} while (0)
@@ -100,12 +101,10 @@ free_sdbm(void *ptr)
static size_t
memsize_dbm(const void *ptr)
{
- size_t size = 0;
const struct dbmdata *dbmp = ptr;
- if (dbmp) {
- size += sizeof(*dbmp);
- if (dbmp->di_dbm) size += sizeof(DBM);
- }
+ size_t size = sizeof(*dbmp);
+ if (dbmp->di_dbm)
+ size += sizeof(DBM);
return size;
}
@@ -148,8 +147,6 @@ fsdbm_closed(VALUE obj)
struct dbmdata *dbmp;
TypedData_Get_Struct(obj, struct dbmdata, &sdbm_type, dbmp);
- if (dbmp == 0)
- return Qtrue;
if (dbmp->di_dbm == 0)
return Qtrue;
@@ -159,7 +156,9 @@ fsdbm_closed(VALUE obj)
static VALUE
fsdbm_alloc(VALUE klass)
{
- return TypedData_Wrap_Struct(klass, &sdbm_type, 0);
+ struct dbmdata *dbmp;
+
+ return TypedData_Make_Struct(klass, struct dbmdata, &sdbm_type, dbmp);
}
/*
* call-seq:
@@ -184,6 +183,7 @@ fsdbm_initialize(int argc, VALUE *argv, VALUE obj)
struct dbmdata *dbmp;
int mode;
+ TypedData_Get_Struct(obj, struct dbmdata, &sdbm_type, dbmp);
if (rb_scan_args(argc, argv, "11", &file, &vmode) == 1) {
mode = 0666; /* default value */
}
@@ -208,8 +208,8 @@ fsdbm_initialize(int argc, VALUE *argv, VALUE obj)
rb_sys_fail_str(file);
}
- dbmp = ALLOC(struct dbmdata);
- DATA_PTR(obj) = dbmp;
+ if (dbmp->di_dbm)
+ sdbm_close(dbmp->di_dbm);
dbmp->di_dbm = dbm;
dbmp->di_size = -1;
diff --git a/ext/sdbm/sdbm.gemspec b/ext/sdbm/sdbm.gemspec
new file mode 100644
index 0000000000..6cf000b453
--- /dev/null
+++ b/ext/sdbm/sdbm.gemspec
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+Gem::Specification.new do |s|
+ s.name = "sdbm"
+ s.version = '1.0.0'
+ s.summary = "Provides a simple file-based key-value store with String keys and values."
+ s.description = "Provides a simple file-based key-value store with String keys and values."
+
+ s.require_path = %w{lib}
+ s.files = %w{ext/sdbm/_sdbm.c ext/sdbm/extconf.rb ext/sdbm/init.c ext/sdbm/sdbm.h}
+ s.extensions = ["ext/sdbm/extconf.rb"]
+ s.required_ruby_version = ">= 2.3.0"
+
+ s.authors = ["Yukihiro Matsumoto"]
+ s.email = ["matz@ruby-lang.org"]
+ s.homepage = "https://github.com/ruby/sdbm"
+ s.license = "BSD-2-Clause"
+
+ s.add_development_dependency "test-unit"
+ s.add_development_dependency "rake-compiler"
+end
diff --git a/ext/socket/basicsocket.c b/ext/socket/basicsocket.c
index 264b882500..1371672850 100644
--- a/ext/socket/basicsocket.c
+++ b/ext/socket/basicsocket.c
@@ -314,6 +314,18 @@ bsock_getsockopt(VALUE sock, VALUE lev, VALUE optname)
level = rsock_level_arg(family, lev);
option = rsock_optname_arg(family, level, optname);
len = 256;
+#ifdef _AIX
+ switch (option) {
+ case SO_DEBUG:
+ case SO_REUSEADDR:
+ case SO_KEEPALIVE:
+ case SO_DONTROUTE:
+ case SO_BROADCAST:
+ case SO_OOBINLINE:
+ /* AIX doesn't set len for boolean options */
+ len = sizeof(int);
+ }
+#endif
buf = ALLOCA_N(char,len);
rb_io_check_closed(fptr);
@@ -530,8 +542,9 @@ rsock_bsock_send(int argc, VALUE *argv, VALUE sock)
struct rsock_send_arg arg;
VALUE flags, to;
rb_io_t *fptr;
- int n;
+ ssize_t n;
rb_blocking_function_t *func;
+ const char *funcname;
rb_scan_args(argc, argv, "21", &arg.mesg, &flags, &to);
@@ -542,21 +555,23 @@ rsock_bsock_send(int argc, VALUE *argv, VALUE sock)
arg.to = (struct sockaddr *)RSTRING_PTR(to);
arg.tolen = RSTRING_SOCKLEN(to);
func = rsock_sendto_blocking;
+ funcname = "sendto(2)";
}
else {
func = rsock_send_blocking;
+ funcname = "send(2)";
}
GetOpenFile(sock, fptr);
arg.fd = fptr->fd;
arg.flags = NUM2INT(flags);
while (rsock_maybe_fd_writable(arg.fd),
- (n = (int)BLOCKING_REGION_FD(func, &arg)) < 0) {
+ (n = (ssize_t)BLOCKING_REGION_FD(func, &arg)) < 0) {
if (rb_io_wait_writable(arg.fd)) {
continue;
}
- rb_sys_fail("send(2)");
+ rb_sys_fail(funcname);
}
- return INT2FIX(n);
+ return SSIZET2NUM(n);
}
/*
@@ -722,6 +737,13 @@ rsock_init_basicsocket(void)
rb_define_private_method(rb_cBasicSocket,
"__recv_nonblock", bsock_recv_nonblock, 4);
+#if MSG_DONTWAIT_RELIABLE
+ rb_define_private_method(rb_cBasicSocket,
+ "__read_nonblock", rsock_read_nonblock, 3);
+ rb_define_private_method(rb_cBasicSocket,
+ "__write_nonblock", rsock_write_nonblock, 2);
+#endif
+
/* in ancdata.c */
rb_define_private_method(rb_cBasicSocket, "__sendmsg",
rsock_bsock_sendmsg, 4);
diff --git a/ext/socket/depend b/ext/socket/depend
index 4b93dbeb1f..41b00406a4 100644
--- a/ext/socket/depend
+++ b/ext/socket/depend
@@ -3,10 +3,12 @@ srcs: constdefs.h constdefs.c
getnameinfo.o: getnameinfo.c $(arch_hdrdir)/ruby/config.h addrinfo.h sockport.h rubysocket.h
getaddrinfo.o: getaddrinfo.c $(arch_hdrdir)/ruby/config.h addrinfo.h sockport.h
-constdefs.h constdefs.c : $(srcdir)/mkconstants.rb
+constdefs.h: $(srcdir)/mkconstants.rb
@echo "generating constant definitions"
@$(RUBY) $(srcdir)/mkconstants.rb -H constdefs.h -o constdefs.c
+constdefs.c: constdefs.h
+
# AUTOGENERATED DEPENDENCIES START
ancdata.o: $(RUBY_EXTCONF_H)
ancdata.o: $(arch_hdrdir)/ruby/config.h
@@ -16,6 +18,7 @@ ancdata.o: $(hdrdir)/ruby/encoding.h
ancdata.o: $(hdrdir)/ruby/intern.h
ancdata.o: $(hdrdir)/ruby/io.h
ancdata.o: $(hdrdir)/ruby/missing.h
+ancdata.o: $(hdrdir)/ruby/onigmo.h
ancdata.o: $(hdrdir)/ruby/oniguruma.h
ancdata.o: $(hdrdir)/ruby/ruby.h
ancdata.o: $(hdrdir)/ruby/st.h
@@ -36,6 +39,7 @@ basicsocket.o: $(hdrdir)/ruby/encoding.h
basicsocket.o: $(hdrdir)/ruby/intern.h
basicsocket.o: $(hdrdir)/ruby/io.h
basicsocket.o: $(hdrdir)/ruby/missing.h
+basicsocket.o: $(hdrdir)/ruby/onigmo.h
basicsocket.o: $(hdrdir)/ruby/oniguruma.h
basicsocket.o: $(hdrdir)/ruby/ruby.h
basicsocket.o: $(hdrdir)/ruby/st.h
@@ -56,6 +60,7 @@ constants.o: $(hdrdir)/ruby/encoding.h
constants.o: $(hdrdir)/ruby/intern.h
constants.o: $(hdrdir)/ruby/io.h
constants.o: $(hdrdir)/ruby/missing.h
+constants.o: $(hdrdir)/ruby/onigmo.h
constants.o: $(hdrdir)/ruby/oniguruma.h
constants.o: $(hdrdir)/ruby/ruby.h
constants.o: $(hdrdir)/ruby/st.h
@@ -77,6 +82,7 @@ ifaddr.o: $(hdrdir)/ruby/encoding.h
ifaddr.o: $(hdrdir)/ruby/intern.h
ifaddr.o: $(hdrdir)/ruby/io.h
ifaddr.o: $(hdrdir)/ruby/missing.h
+ifaddr.o: $(hdrdir)/ruby/onigmo.h
ifaddr.o: $(hdrdir)/ruby/oniguruma.h
ifaddr.o: $(hdrdir)/ruby/ruby.h
ifaddr.o: $(hdrdir)/ruby/st.h
@@ -97,6 +103,7 @@ init.o: $(hdrdir)/ruby/encoding.h
init.o: $(hdrdir)/ruby/intern.h
init.o: $(hdrdir)/ruby/io.h
init.o: $(hdrdir)/ruby/missing.h
+init.o: $(hdrdir)/ruby/onigmo.h
init.o: $(hdrdir)/ruby/oniguruma.h
init.o: $(hdrdir)/ruby/ruby.h
init.o: $(hdrdir)/ruby/st.h
@@ -117,6 +124,7 @@ ipsocket.o: $(hdrdir)/ruby/encoding.h
ipsocket.o: $(hdrdir)/ruby/intern.h
ipsocket.o: $(hdrdir)/ruby/io.h
ipsocket.o: $(hdrdir)/ruby/missing.h
+ipsocket.o: $(hdrdir)/ruby/onigmo.h
ipsocket.o: $(hdrdir)/ruby/oniguruma.h
ipsocket.o: $(hdrdir)/ruby/ruby.h
ipsocket.o: $(hdrdir)/ruby/st.h
@@ -137,6 +145,7 @@ option.o: $(hdrdir)/ruby/encoding.h
option.o: $(hdrdir)/ruby/intern.h
option.o: $(hdrdir)/ruby/io.h
option.o: $(hdrdir)/ruby/missing.h
+option.o: $(hdrdir)/ruby/onigmo.h
option.o: $(hdrdir)/ruby/oniguruma.h
option.o: $(hdrdir)/ruby/ruby.h
option.o: $(hdrdir)/ruby/st.h
@@ -157,6 +166,7 @@ raddrinfo.o: $(hdrdir)/ruby/encoding.h
raddrinfo.o: $(hdrdir)/ruby/intern.h
raddrinfo.o: $(hdrdir)/ruby/io.h
raddrinfo.o: $(hdrdir)/ruby/missing.h
+raddrinfo.o: $(hdrdir)/ruby/onigmo.h
raddrinfo.o: $(hdrdir)/ruby/oniguruma.h
raddrinfo.o: $(hdrdir)/ruby/ruby.h
raddrinfo.o: $(hdrdir)/ruby/st.h
@@ -177,6 +187,7 @@ socket.o: $(hdrdir)/ruby/encoding.h
socket.o: $(hdrdir)/ruby/intern.h
socket.o: $(hdrdir)/ruby/io.h
socket.o: $(hdrdir)/ruby/missing.h
+socket.o: $(hdrdir)/ruby/onigmo.h
socket.o: $(hdrdir)/ruby/oniguruma.h
socket.o: $(hdrdir)/ruby/ruby.h
socket.o: $(hdrdir)/ruby/st.h
@@ -197,6 +208,7 @@ sockssocket.o: $(hdrdir)/ruby/encoding.h
sockssocket.o: $(hdrdir)/ruby/intern.h
sockssocket.o: $(hdrdir)/ruby/io.h
sockssocket.o: $(hdrdir)/ruby/missing.h
+sockssocket.o: $(hdrdir)/ruby/onigmo.h
sockssocket.o: $(hdrdir)/ruby/oniguruma.h
sockssocket.o: $(hdrdir)/ruby/ruby.h
sockssocket.o: $(hdrdir)/ruby/st.h
@@ -217,6 +229,7 @@ tcpserver.o: $(hdrdir)/ruby/encoding.h
tcpserver.o: $(hdrdir)/ruby/intern.h
tcpserver.o: $(hdrdir)/ruby/io.h
tcpserver.o: $(hdrdir)/ruby/missing.h
+tcpserver.o: $(hdrdir)/ruby/onigmo.h
tcpserver.o: $(hdrdir)/ruby/oniguruma.h
tcpserver.o: $(hdrdir)/ruby/ruby.h
tcpserver.o: $(hdrdir)/ruby/st.h
@@ -237,6 +250,7 @@ tcpsocket.o: $(hdrdir)/ruby/encoding.h
tcpsocket.o: $(hdrdir)/ruby/intern.h
tcpsocket.o: $(hdrdir)/ruby/io.h
tcpsocket.o: $(hdrdir)/ruby/missing.h
+tcpsocket.o: $(hdrdir)/ruby/onigmo.h
tcpsocket.o: $(hdrdir)/ruby/oniguruma.h
tcpsocket.o: $(hdrdir)/ruby/ruby.h
tcpsocket.o: $(hdrdir)/ruby/st.h
@@ -257,6 +271,7 @@ udpsocket.o: $(hdrdir)/ruby/encoding.h
udpsocket.o: $(hdrdir)/ruby/intern.h
udpsocket.o: $(hdrdir)/ruby/io.h
udpsocket.o: $(hdrdir)/ruby/missing.h
+udpsocket.o: $(hdrdir)/ruby/onigmo.h
udpsocket.o: $(hdrdir)/ruby/oniguruma.h
udpsocket.o: $(hdrdir)/ruby/ruby.h
udpsocket.o: $(hdrdir)/ruby/st.h
@@ -277,6 +292,7 @@ unixserver.o: $(hdrdir)/ruby/encoding.h
unixserver.o: $(hdrdir)/ruby/intern.h
unixserver.o: $(hdrdir)/ruby/io.h
unixserver.o: $(hdrdir)/ruby/missing.h
+unixserver.o: $(hdrdir)/ruby/onigmo.h
unixserver.o: $(hdrdir)/ruby/oniguruma.h
unixserver.o: $(hdrdir)/ruby/ruby.h
unixserver.o: $(hdrdir)/ruby/st.h
@@ -297,6 +313,7 @@ unixsocket.o: $(hdrdir)/ruby/encoding.h
unixsocket.o: $(hdrdir)/ruby/intern.h
unixsocket.o: $(hdrdir)/ruby/io.h
unixsocket.o: $(hdrdir)/ruby/missing.h
+unixsocket.o: $(hdrdir)/ruby/onigmo.h
unixsocket.o: $(hdrdir)/ruby/oniguruma.h
unixsocket.o: $(hdrdir)/ruby/ruby.h
unixsocket.o: $(hdrdir)/ruby/st.h
diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb
index fd70be45da..0cc8a88d5c 100644
--- a/ext/socket/extconf.rb
+++ b/ext/socket/extconf.rb
@@ -433,6 +433,7 @@ end
case RUBY_PLATFORM
when /mswin(32|64)|mingw/
test_func = "WSACleanup"
+ have_library("iphlpapi")
have_library("ws2_32", "WSACleanup", headers)
when /cygwin/
test_func = "socket(0,0,0)"
@@ -476,6 +477,7 @@ EOF
have_func('inet_aton("", (struct in_addr *)0)', headers)
have_func('getservbyport(0, "")', headers)
have_func("getifaddrs((struct ifaddrs **)NULL)", headers)
+ have_struct_member("struct if_data", "ifi_vhid", headers) # FreeBSD
have_func("getpeereid", headers)
@@ -566,6 +568,7 @@ EOS
getaddr_info_ok = (:wide if getaddr_info_ok.nil?)
if have_func("getnameinfo", headers) and have_func("getaddrinfo", headers)
if CROSS_COMPILING ||
+ $mingw || $mswin ||
checking_for("system getaddrinfo working") {
try_run(cpp_include(headers) + GETADDRINFO_GETNAMEINFO_TEST)
}
@@ -653,7 +656,7 @@ EOS
#include <netinet/in.h>
int t(struct in6_addr *addr) {return IN6_IS_ADDR_UNSPECIFIED(addr);}
SRC
- print "fixing apple's netinet6/in6.rb ..."; $stdout.flush
+ print "fixing apple's netinet6/in6.h ..."; $stdout.flush
in6 = File.read("/usr/include/#{hdr}")
if in6.gsub!(/\*\(const\s+__uint32_t\s+\*\)\(const\s+void\s+\*\)\(&(\(\w+\))->s6_addr\[(\d+)\]\)/) do
i, r = $2.to_i.divmod(4)
diff --git a/ext/socket/getaddrinfo.c b/ext/socket/getaddrinfo.c
index b01f1cb82e..ce6dc40478 100644
--- a/ext/socket/getaddrinfo.c
+++ b/ext/socket/getaddrinfo.c
@@ -149,6 +149,7 @@ static int get_addr __P((const char *, int, struct addrinfo **,
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 */
@@ -166,6 +167,7 @@ static const char *const ai_errlist[] = {
"resolved protocol is unknown.", /* EAI_PROTOCOL */
"unknown error.", /* EAI_MAX */
};
+#endif
#define GET_CANONNAME(ai, str) \
if (pai->ai_flags & AI_CANONNAME) {\
diff --git a/ext/socket/ifaddr.c b/ext/socket/ifaddr.c
index a954163369..26aa0c8082 100644
--- a/ext/socket/ifaddr.c
+++ b/ext/socket/ifaddr.c
@@ -24,7 +24,6 @@ typedef struct rb_ifaddr_root_tag rb_ifaddr_root_t;
struct rb_ifaddr_tag {
int ord;
struct ifaddrs *ifaddr;
- rb_ifaddr_root_t *root;
};
struct rb_ifaddr_root_tag {
@@ -55,11 +54,12 @@ ifaddr_free(void *ptr)
static size_t
ifaddr_memsize(const void *ptr)
{
+ size_t size = offsetof(rb_ifaddr_root_t, ary);
const rb_ifaddr_t *ifaddr;
- const rb_ifaddr_root_t *root;
ifaddr = ptr;
- root = get_root(ifaddr);
- return sizeof(rb_ifaddr_root_t) + (root->numifaddrs - 1) * sizeof(rb_ifaddr_t);
+ if (ifaddr->ord == 0) size = sizeof(rb_ifaddr_root_t);
+ size += sizeof(struct ifaddrs);
+ return size;
}
static const rb_data_type_t ifaddr_type = {
@@ -84,6 +84,12 @@ get_ifaddr(VALUE self)
return rifaddr;
}
+static struct ifaddrs *
+get_ifaddrs(VALUE self)
+{
+ return get_ifaddr(self)->ifaddr;
+}
+
static VALUE
rsock_getifaddrs(void)
{
@@ -106,7 +112,7 @@ rsock_getifaddrs(void)
numifaddrs++;
addr = TypedData_Wrap_Struct(rb_cSockIfaddr, &ifaddr_type, 0);
- root = xmalloc(sizeof(rb_ifaddr_root_t) + (numifaddrs-1) * sizeof(rb_ifaddr_t));
+ root = xmalloc(offsetof(rb_ifaddr_root_t, ary) + numifaddrs * sizeof(rb_ifaddr_t));
root->refcount = 0;
root->numifaddrs = numifaddrs;
@@ -114,7 +120,6 @@ rsock_getifaddrs(void)
for (i = 0; i < numifaddrs; i++) {
root->ary[i].ord = i;
root->ary[i].ifaddr = ifa;
- root->ary[i].root = root;
ifa = ifa->ifa_next;
}
RTYPEDDATA_DATA(addr) = &root->ary[0];
@@ -141,8 +146,7 @@ rsock_getifaddrs(void)
static VALUE
ifaddr_name(VALUE self)
{
- rb_ifaddr_t *rifaddr = get_ifaddr(self);
- struct ifaddrs *ifa = rifaddr->ifaddr;
+ struct ifaddrs *ifa = get_ifaddrs(self);
return rb_str_new_cstr(ifa->ifa_name);
}
@@ -157,8 +161,7 @@ ifaddr_name(VALUE self)
static VALUE
ifaddr_ifindex(VALUE self)
{
- rb_ifaddr_t *rifaddr = get_ifaddr(self);
- struct ifaddrs *ifa = rifaddr->ifaddr;
+ struct ifaddrs *ifa = get_ifaddrs(self);
unsigned int ifindex = if_nametoindex(ifa->ifa_name);
if (ifindex == 0) {
rb_raise(rb_eArgError, "invalid interface name: %s", ifa->ifa_name);
@@ -179,8 +182,7 @@ ifaddr_ifindex(VALUE self)
static VALUE
ifaddr_flags(VALUE self)
{
- rb_ifaddr_t *rifaddr = get_ifaddr(self);
- struct ifaddrs *ifa = rifaddr->ifaddr;
+ struct ifaddrs *ifa = get_ifaddrs(self);
return IFAFLAGS2NUM(ifa->ifa_flags);
}
@@ -195,8 +197,7 @@ ifaddr_flags(VALUE self)
static VALUE
ifaddr_addr(VALUE self)
{
- rb_ifaddr_t *rifaddr = get_ifaddr(self);
- struct ifaddrs *ifa = rifaddr->ifaddr;
+ struct ifaddrs *ifa = get_ifaddrs(self);
if (ifa->ifa_addr)
return rsock_sockaddr_obj(ifa->ifa_addr, rsock_sockaddr_len(ifa->ifa_addr));
return Qnil;
@@ -213,8 +214,7 @@ ifaddr_addr(VALUE self)
static VALUE
ifaddr_netmask(VALUE self)
{
- rb_ifaddr_t *rifaddr = get_ifaddr(self);
- struct ifaddrs *ifa = rifaddr->ifaddr;
+ struct ifaddrs *ifa = get_ifaddrs(self);
if (ifa->ifa_netmask)
return rsock_sockaddr_obj(ifa->ifa_netmask, rsock_sockaddr_len(ifa->ifa_netmask));
return Qnil;
@@ -231,8 +231,7 @@ ifaddr_netmask(VALUE self)
static VALUE
ifaddr_broadaddr(VALUE self)
{
- rb_ifaddr_t *rifaddr = get_ifaddr(self);
- struct ifaddrs *ifa = rifaddr->ifaddr;
+ struct ifaddrs *ifa = get_ifaddrs(self);
if ((ifa->ifa_flags & IFF_BROADCAST) && ifa->ifa_broadaddr)
return rsock_sockaddr_obj(ifa->ifa_broadaddr, rsock_sockaddr_len(ifa->ifa_broadaddr));
return Qnil;
@@ -249,13 +248,32 @@ ifaddr_broadaddr(VALUE self)
static VALUE
ifaddr_dstaddr(VALUE self)
{
- rb_ifaddr_t *rifaddr = get_ifaddr(self);
- struct ifaddrs *ifa = rifaddr->ifaddr;
+ struct ifaddrs *ifa = get_ifaddrs(self);
if ((ifa->ifa_flags & IFF_POINTOPOINT) && ifa->ifa_dstaddr)
return rsock_sockaddr_obj(ifa->ifa_dstaddr, rsock_sockaddr_len(ifa->ifa_dstaddr));
return Qnil;
}
+#ifdef HAVE_STRUCT_IF_DATA_IFI_VHID
+/*
+ * call-seq:
+ * ifaddr.vhid => Integer
+ *
+ * Returns the vhid address of _ifaddr_.
+ * nil is returned if there is no vhid.
+ */
+
+static VALUE
+ifaddr_vhid(VALUE self)
+{
+ struct ifaddrs *ifa = get_ifaddrs(self);
+ if (ifa->ifa_data)
+ return (INT2FIX(((struct if_data*)ifa->ifa_data)->ifi_vhid));
+ else
+ return Qnil;
+}
+#endif
+
static void
ifaddr_inspect_flags(ifa_flags_t flags, VALUE result)
{
@@ -338,12 +356,9 @@ ifaddr_inspect_flags(ifa_flags_t flags, VALUE result)
static VALUE
ifaddr_inspect(VALUE self)
{
- rb_ifaddr_t *rifaddr = get_ifaddr(self);
- struct ifaddrs *ifa;
+ struct ifaddrs *ifa = get_ifaddrs(self);
VALUE result;
- ifa = rifaddr->ifaddr;
-
result = rb_str_new_cstr("#<");
rb_str_append(result, rb_class_name(CLASS_OF(self)));
@@ -453,6 +468,9 @@ rsock_init_sockifaddr(void)
rb_define_method(rb_cSockIfaddr, "netmask", ifaddr_netmask, 0);
rb_define_method(rb_cSockIfaddr, "broadaddr", ifaddr_broadaddr, 0);
rb_define_method(rb_cSockIfaddr, "dstaddr", ifaddr_dstaddr, 0);
+#ifdef HAVE_STRUCT_IF_DATA_IFI_VHID
+ rb_define_method(rb_cSockIfaddr, "vhid", ifaddr_vhid, 0);
+#endif
#endif
rb_define_singleton_method(rb_cSocket, "getifaddrs", socket_s_getifaddrs, 0);
diff --git a/ext/socket/init.c b/ext/socket/init.c
index 1ecd4fe352..259d3b5996 100644
--- a/ext/socket/init.c
+++ b/ext/socket/init.c
@@ -10,6 +10,10 @@
#include "rubysocket.h"
+#ifdef _WIN32
+VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
+#endif
+
VALUE rb_cBasicSocket;
VALUE rb_cIPSocket;
VALUE rb_cTCPSocket;
@@ -39,7 +43,15 @@ rsock_raise_socket_error(const char *reason, int error)
if (error == EAI_SYSTEM && (e = errno) != 0)
rb_syserr_fail(e, reason);
#endif
+#ifdef _WIN32
+ rb_encoding *enc = rb_default_internal_encoding();
+ VALUE msg = rb_sprintf("%s: ", reason);
+ if (!enc) enc = rb_default_internal_encoding();
+ rb_str_concat(msg, rb_w32_conv_from_wchar(gai_strerrorW(error), enc));
+ rb_exc_raise(rb_exc_new_str(rb_eSocket, msg));
+#else
rb_raise(rb_eSocket, "%s: %s", reason, gai_strerror(error));
+#endif
}
#ifdef _WIN32
@@ -56,6 +68,12 @@ is_socket(int fd)
}
#endif
+#if defined __APPLE__
+# define do_write_retry(code) do {ret = code;} while (ret == -1 && errno == EPROTOTYPE)
+#else
+# define do_write_retry(code) ret = code
+#endif
+
VALUE
rsock_init_sock(VALUE sock, int fd)
{
@@ -83,8 +101,10 @@ rsock_sendto_blocking(void *data)
{
struct rsock_send_arg *arg = data;
VALUE mesg = arg->mesg;
- return (VALUE)sendto(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg),
- arg->flags, arg->to, arg->tolen);
+ ssize_t ret;
+ do_write_retry(sendto(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg),
+ arg->flags, arg->to, arg->tolen));
+ return (VALUE)ret;
}
VALUE
@@ -92,13 +112,16 @@ rsock_send_blocking(void *data)
{
struct rsock_send_arg *arg = data;
VALUE mesg = arg->mesg;
- return (VALUE)send(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg),
- arg->flags);
+ ssize_t ret;
+ do_write_retry(send(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg),
+ arg->flags));
+ return (VALUE)ret;
}
struct recvfrom_arg {
int fd, flags;
VALUE str;
+ size_t length;
socklen_t alen;
union_sockaddr buf;
};
@@ -109,10 +132,11 @@ recvfrom_blocking(void *data)
struct recvfrom_arg *arg = data;
socklen_t len0 = arg->alen;
ssize_t ret;
- ret = recvfrom(arg->fd, RSTRING_PTR(arg->str), RSTRING_LEN(arg->str),
+ ret = recvfrom(arg->fd, RSTRING_PTR(arg->str), arg->length,
arg->flags, &arg->buf.addr, &arg->alen);
if (ret != -1 && len0 < arg->alen)
arg->alen = len0;
+
return (VALUE)ret;
}
@@ -130,7 +154,6 @@ rsock_strbuf(VALUE str, long buflen)
} else {
rb_str_modify_expand(str, buflen - len);
}
- rb_str_set_len(str, buflen);
return str;
}
@@ -166,6 +189,7 @@ rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from)
arg.fd = fptr->fd;
arg.alen = (socklen_t)sizeof(arg.buf);
arg.str = str;
+ arg.length = buflen;
while (rb_io_check_closed(fptr),
rsock_maybe_wait_fd(arg.fd),
@@ -176,9 +200,8 @@ rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from)
}
}
- if (slen != RSTRING_LEN(str)) {
- rb_str_set_len(str, slen);
- }
+ /* Resize the string to the amount of data received */
+ rb_str_set_len(str, slen);
rb_obj_taint(str);
switch (from) {
case RECV_RECV:
@@ -280,6 +303,114 @@ rsock_s_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str,
return rb_assoc_new(str, addr);
}
+#if MSG_DONTWAIT_RELIABLE
+static VALUE sym_wait_writable;
+
+/* copied from io.c :< */
+static long
+read_buffered_data(char *ptr, long len, rb_io_t *fptr)
+{
+ int n = fptr->rbuf.len;
+
+ if (n <= 0) return 0;
+ if (n > len) n = (int)len;
+ MEMMOVE(ptr, fptr->rbuf.ptr+fptr->rbuf.off, char, n);
+ fptr->rbuf.off += n;
+ fptr->rbuf.len -= n;
+ return n;
+}
+
+/* :nodoc: */
+VALUE
+rsock_read_nonblock(VALUE sock, VALUE length, VALUE buf, VALUE ex)
+{
+ rb_io_t *fptr;
+ long n;
+ long len = NUM2LONG(length);
+ VALUE str = rsock_strbuf(buf, len);
+ char *ptr;
+
+ OBJ_TAINT(str);
+ GetOpenFile(sock, fptr);
+
+ if (len == 0) {
+ 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);
+ }
+ }
+ if (n != RSTRING_LEN(str)) {
+ rb_str_modify(str);
+ rb_str_set_len(str, n);
+ }
+ if (n == 0) {
+ if (ex == Qfalse) return Qnil;
+ rb_eof_error();
+ }
+
+ return str;
+}
+
+/* :nodoc: */
+VALUE
+rsock_write_nonblock(VALUE sock, VALUE str, VALUE ex)
+{
+ rb_io_t *fptr;
+ long n;
+
+ if (!RB_TYPE_P(str, T_STRING))
+ str = rb_obj_as_string(str);
+
+ sock = rb_io_get_write_io(sock);
+ GetOpenFile(sock, fptr);
+ rb_io_check_writable(fptr);
+
+ /*
+ * As with IO#write_nonblock, we may block if somebody is relying on
+ * buffered I/O; but nobody actually hits this because pipes and sockets
+ * are not userspace-buffered in Ruby by default.
+ */
+ if (fptr->wbuf.len > 0) {
+ rb_io_flush(sock);
+ }
+
+#ifdef __APPLE__
+ again:
+#endif
+ n = (long)send(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str), MSG_DONTWAIT);
+ if (n < 0) {
+ 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);
+ }
+
+ return LONG2FIX(n);
+}
+#endif /* MSG_DONTWAIT_RELIABLE */
+
/* returns true if SOCK_CLOEXEC is supported */
int rsock_detect_cloexec(int fd)
{
@@ -303,7 +434,7 @@ rsock_socket0(int domain, int type, int proto)
static int cloexec_state = -1; /* <0: unknown, 0: ignored, >0: working */
if (cloexec_state > 0) { /* common path, if SOCK_CLOEXEC is defined */
- ret = socket(domain, type|SOCK_CLOEXEC, proto);
+ ret = socket(domain, type|SOCK_CLOEXEC|RSOCK_NONBLOCK_DEFAULT, proto);
if (ret >= 0) {
if (ret <= 2)
goto fix_cloexec;
@@ -311,7 +442,7 @@ rsock_socket0(int domain, int type, int proto)
}
}
else if (cloexec_state < 0) { /* usually runs once only for detection */
- ret = socket(domain, type|SOCK_CLOEXEC, proto);
+ ret = socket(domain, type|SOCK_CLOEXEC|RSOCK_NONBLOCK_DEFAULT, proto);
if (ret >= 0) {
cloexec_state = rsock_detect_cloexec(ret);
if (cloexec_state == 0 || ret <= 2)
@@ -334,6 +465,9 @@ rsock_socket0(int domain, int type, int proto)
return -1;
fix_cloexec:
rb_maygvl_fd_fix_cloexec(ret);
+ if (RSOCK_NONBLOCK_DEFAULT) {
+ rsock_make_fd_nonblock(ret);
+ }
update_max_fd:
rb_update_max_fd(ret);
@@ -348,6 +482,9 @@ rsock_socket0(int domain, int type, int proto)
if (ret == -1)
return -1;
rb_fd_fix_cloexec(ret);
+ if (RSOCK_NONBLOCK_DEFAULT) {
+ rsock_make_fd_nonblock(ret);
+ }
return ret;
}
@@ -376,11 +513,30 @@ wait_connectable(int fd)
int sockerr, revents;
socklen_t sockerrlen;
- /* only to clear pending error */
sockerrlen = (socklen_t)sizeof(sockerr);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen) < 0)
return -1;
+ /* necessary for non-blocking sockets (at least ECONNREFUSED) */
+ switch (sockerr) {
+ case 0:
+ break;
+#ifdef EALREADY
+ case EALREADY:
+#endif
+#ifdef EISCONN
+ case EISCONN:
+#endif
+#ifdef ECONNREFUSED
+ case ECONNREFUSED:
+#endif
+#ifdef EHOSTUNREACH
+ case EHOSTUNREACH:
+#endif
+ errno = sockerr;
+ return -1;
+ }
+
/*
* Stevens book says, successful finish turn on RB_WAITFD_OUT and
* failure finish turn on both RB_WAITFD_IN and RB_WAITFD_OUT.
@@ -481,8 +637,8 @@ rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks)
return status;
}
-static void
-make_fd_nonblock(int fd)
+void
+rsock_make_fd_nonblock(int fd)
{
int flags;
#ifdef F_GETFL
@@ -508,6 +664,9 @@ cloexec_accept(int socket, struct sockaddr *address, socklen_t *address_len,
#ifdef HAVE_ACCEPT4
static int try_accept4 = 1;
#endif
+ if (RSOCK_NONBLOCK_DEFAULT) {
+ nonblock = 1;
+ }
if (address_len) len0 = *address_len;
#ifdef HAVE_ACCEPT4
if (try_accept4) {
@@ -527,7 +686,7 @@ cloexec_accept(int socket, struct sockaddr *address, socklen_t *address_len,
rb_maygvl_fd_fix_cloexec(ret);
#ifndef SOCK_NONBLOCK
if (nonblock) {
- make_fd_nonblock(ret);
+ rsock_make_fd_nonblock(ret);
}
#endif
if (address_len && len0 < *address_len) *address_len = len0;
@@ -544,7 +703,7 @@ cloexec_accept(int socket, struct sockaddr *address, socklen_t *address_len,
if (address_len && len0 < *address_len) *address_len = len0;
rb_maygvl_fd_fix_cloexec(ret);
if (nonblock) {
- make_fd_nonblock(ret);
+ rsock_make_fd_nonblock(ret);
}
return ret;
}
@@ -680,4 +839,8 @@ rsock_init_socket_init(void)
#undef rb_intern
sym_wait_readable = ID2SYM(rb_intern("wait_readable"));
+
+#if MSG_DONTWAIT_RELIABLE
+ sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
+#endif
}
diff --git a/ext/socket/ipsocket.c b/ext/socket/ipsocket.c
index dbf403b39a..dadf10f6a5 100644
--- a/ext/socket/ipsocket.c
+++ b/ext/socket/ipsocket.c
@@ -191,6 +191,43 @@ rsock_revlookup_flag(VALUE revlookup, int *norevlookup)
/*
* call-seq:
+ * ipsocket.inspect -> string
+ *
+ * Return a string describing this IPSocket object.
+ */
+static VALUE
+ip_inspect(VALUE sock)
+{
+ VALUE str = rb_call_super(0, 0);
+ rb_io_t *fptr = RFILE(sock)->fptr;
+ union_sockaddr addr;
+ 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);
+ }
+ return str;
+}
+
+/*
+ * call-seq:
* ipsocket.addr([reverse_lookup]) => [address_family, port, hostname, numeric_address]
*
* Returns the local address as an array which contains
@@ -332,6 +369,7 @@ rsock_init_ipsocket(void)
* IPSocket is the super class of TCPSocket and UDPSocket.
*/
rb_cIPSocket = rb_define_class("IPSocket", rb_cBasicSocket);
+ rb_define_method(rb_cIPSocket, "inspect", ip_inspect, 0);
rb_define_method(rb_cIPSocket, "addr", ip_addr, -1);
rb_define_method(rb_cIPSocket, "peeraddr", ip_peeraddr, -1);
rb_define_method(rb_cIPSocket, "recvfrom", ip_recvfrom, -1);
diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb
index 46361c6c15..4ed2df23e6 100644
--- a/ext/socket/lib/socket.rb
+++ b/ext/socket/lib/socket.rb
@@ -71,13 +71,13 @@ class Addrinfo
begin
yield sock
ensure
- sock.close if !sock.closed?
+ sock.close
end
else
sock
end
end
- private :connect_internal
+ protected :connect_internal
# :call-seq:
# addrinfo.connect_from([local_addr_args], [opts]) {|socket| ... }
@@ -110,10 +110,8 @@ class Addrinfo
# puts s.read
# }
#
- def connect_from(*args, &block)
- opts = Hash === args.last ? args.pop : {}
- local_addr_args = args
- connect_internal(family_addrinfo(*local_addr_args), opts[:timeout], &block)
+ def connect_from(*args, timeout: nil, &block)
+ connect_internal(family_addrinfo(*args), timeout, &block)
end
# :call-seq:
@@ -135,8 +133,8 @@ class Addrinfo
# puts s.read
# }
#
- def connect(opts={}, &block)
- connect_internal(nil, opts[:timeout], &block)
+ def connect(timeout: nil, &block)
+ connect_internal(nil, timeout, &block)
end
# :call-seq:
@@ -158,11 +156,9 @@ class Addrinfo
# puts s.read
# }
#
- def connect_to(*args, &block)
- opts = Hash === args.last ? args.pop : {}
- remote_addr_args = args
- remote_addrinfo = family_addrinfo(*remote_addr_args)
- remote_addrinfo.send(:connect_internal, self, opts[:timeout], &block)
+ def connect_to(*args, timeout: nil, &block)
+ remote_addrinfo = family_addrinfo(*args)
+ remote_addrinfo.connect_internal(self, timeout, &block)
end
# creates a socket bound to self.
@@ -189,7 +185,7 @@ class Addrinfo
begin
yield sock
ensure
- sock.close if !sock.closed?
+ sock.close
end
else
sock
@@ -212,7 +208,7 @@ class Addrinfo
begin
yield sock
ensure
- sock.close if !sock.closed?
+ sock.close
end
else
sock
@@ -317,9 +313,9 @@ class BasicSocket < IO
# but the non-blocking flag is set before the system call
# and it doesn't retry the system call.
#
- # By specifying `exception: false`, the _opts_ hash allows you to indicate
+ # By specifying a keyword argument _exception_ to +false+, you can indicate
# that sendmsg_nonblock should not raise an IO::WaitWritable exception, but
- # return the symbol :wait_writable instead.
+ # return the symbol +:wait_writable+ instead.
def sendmsg_nonblock(mesg, flags = 0, dest_sockaddr = nil, *controls,
exception: true)
__sendmsg_nonblock(mesg, flags, dest_sockaddr, controls, exception)
@@ -340,6 +336,7 @@ class BasicSocket < IO
# === Parameters
# * +maxlen+ - the number of bytes to receive from the socket
# * +flags+ - zero or more of the +MSG_+ options
+ # * +buf+ - destination String buffer
# * +options+ - keyword hash, supporting `exception: false`
#
# === Example
@@ -365,9 +362,9 @@ class BasicSocket < IO
# it is extended by IO::WaitReadable.
# So IO::WaitReadable can be used to rescue the exceptions for retrying recv_nonblock.
#
- # By specifying `exception: false`, the options hash allows you to indicate
- # that recv_nonblock should not raise an IO::WaitWritable exception, but
- # return the symbol :wait_writable instead.
+ # By specifying a keyword argument _exception_ to +false+, you can indicate
+ # that recv_nonblock should not raise an IO::WaitReadable exception, but
+ # return the symbol +:wait_readable+ instead.
#
# === See
# * Socket#recvfrom
@@ -439,13 +436,26 @@ class BasicSocket < IO
# but non-blocking flag is set before the system call
# and it doesn't retry the system call.
#
- # By specifying `exception: false`, the _opts_ hash allows you to indicate
- # that recvmsg_nonblock should not raise an IO::WaitWritable exception, but
- # return the symbol :wait_writable instead.
+ # By specifying a keyword argument _exception_ to +false+, you can indicate
+ # that recvmsg_nonblock should not raise an IO::WaitReadable exception, but
+ # return the symbol +:wait_readable+ instead.
def recvmsg_nonblock(dlen = nil, flags = 0, clen = nil,
scm_rights: false, exception: true)
__recvmsg_nonblock(dlen, flags, clen, scm_rights, exception)
end
+
+ # Linux-specific optimizations to avoid fcntl for IO#read_nonblock
+ # and IO#write_nonblock using MSG_DONTWAIT
+ # Do other platforms support MSG_DONTWAIT reliably?
+ if RUBY_PLATFORM =~ /linux/ && Socket.const_defined?(:MSG_DONTWAIT)
+ def read_nonblock(len, str = nil, exception: true) # :nodoc:
+ __read_nonblock(len, str, exception)
+ end
+
+ def write_nonblock(buf, exception: true) # :nodoc:
+ __write_nonblock(buf, exception)
+ end
+ end
end
class Socket < BasicSocket
@@ -516,9 +526,9 @@ class Socket < BasicSocket
# So IO::WaitReadable can be used to rescue the exceptions for retrying
# recvfrom_nonblock.
#
- # By specifying `exception: false`, the options hash allows you to indicate
- # that accept_nonblock should not raise an IO::WaitReadable exception, but
- # return the symbol :wait_readable instead.
+ # By specifying a keyword argument _exception_ to +false+, you can indicate
+ # that recvfrom_nonblock should not raise an IO::WaitReadable exception, but
+ # return the symbol +:wait_readable+ instead.
#
# === See
# * Socket#recvfrom
@@ -573,9 +583,9 @@ class Socket < BasicSocket
# it is extended by IO::WaitReadable.
# So IO::WaitReadable can be used to rescue the exceptions for retrying accept_nonblock.
#
- # By specifying `exception: false`, the options hash allows you to indicate
+ # By specifying a keyword argument _exception_ to +false+, you can indicate
# that accept_nonblock should not raise an IO::WaitReadable exception, but
- # return the symbol :wait_readable instead.
+ # return the symbol +:wait_readable+ instead.
#
# === See
# * Socket#accept
@@ -609,15 +619,10 @@ class Socket < BasicSocket
# puts sock.read
# }
#
- def self.tcp(host, port, *rest) # :yield: socket
- opts = Hash === rest.last ? rest.pop : {}
- raise ArgumentError, "wrong number of arguments (#{rest.length} for 2)" if 2 < rest.length
- local_host, local_port = rest
+ def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil) # :yield: socket
last_error = nil
ret = nil
- connect_timeout = opts[:connect_timeout]
-
local_addr_list = nil
if local_host != nil || local_port != nil
local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil)
@@ -626,14 +631,14 @@ class Socket < BasicSocket
Addrinfo.foreach(host, port, nil, :STREAM) {|ai|
if local_addr_list
local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily }
- next if !local_addr
+ next unless local_addr
else
local_addr = nil
end
begin
sock = local_addr ?
- ai.connect_from(local_addr, :timeout => connect_timeout) :
- ai.connect(:timeout => connect_timeout)
+ ai.connect_from(local_addr, timeout: connect_timeout) :
+ ai.connect(timeout: connect_timeout)
rescue SystemCallError
last_error = $!
next
@@ -641,7 +646,7 @@ class Socket < BasicSocket
ret = sock
break
}
- if !ret
+ unless ret
if last_error
raise last_error
else
@@ -652,7 +657,7 @@ class Socket < BasicSocket
begin
yield ret
ensure
- ret.close if !ret.closed?
+ ret.close
end
else
ret
@@ -676,7 +681,7 @@ class Socket < BasicSocket
if reuseaddr
s.setsockopt(:SOCKET, :REUSEADDR, 1)
end
- if !port
+ unless port
s.bind(ai)
port = s.local_address.ip_port
else
@@ -684,10 +689,10 @@ class Socket < BasicSocket
end
}
rescue Errno::EADDRINUSE
- sockets.each {|s| s.close }
+ sockets.each(&:close)
retry
rescue Exception
- sockets.each {|s| s.close }
+ sockets.each(&:close)
raise
end
sockets
@@ -704,7 +709,7 @@ class Socket < BasicSocket
s.listen(Socket::SOMAXCONN)
}
rescue Exception
- sockets.each {|s| s.close }
+ sockets.each(&:close)
raise
end
sockets
@@ -767,7 +772,7 @@ class Socket < BasicSocket
raise last_error
end
rescue Exception
- sockets.each {|s| s.close }
+ sockets.each(&:close)
raise
end
end
@@ -775,7 +780,7 @@ class Socket < BasicSocket
begin
yield sockets
ensure
- sockets.each {|s| s.close if !s.closed? }
+ sockets.each(&:close)
end
else
sockets
@@ -894,18 +899,19 @@ class Socket < BasicSocket
Addrinfo.foreach(host, port, nil, :DGRAM, nil, Socket::AI_PASSIVE) {|ai|
if ai.ipv4? && ai.ip_address == "0.0.0.0"
local_addrs.each {|a|
- next if !a.ipv4?
+ next unless a.ipv4?
ip_list << Addrinfo.new(a.to_sockaddr, :INET, :DGRAM, 0);
}
elsif ai.ipv6? && ai.ip_address == "::" && !ipv6_recvpktinfo
local_addrs.each {|a|
- next if !a.ipv6?
+ next unless a.ipv6?
ip_list << Addrinfo.new(a.to_sockaddr, :INET6, :DGRAM, 0);
}
else
ip_list << ai
end
}
+ ip_list.uniq!(&:to_sockaddr)
if port == 0
sockets = ip_sockets_port0(ip_list, false)
@@ -936,7 +942,7 @@ class Socket < BasicSocket
begin
yield sockets
ensure
- sockets.each {|s| s.close if !s.closed? } if sockets
+ sockets.each(&:close) if sockets
end
else
sockets
@@ -1073,7 +1079,7 @@ class Socket < BasicSocket
begin
yield sock
ensure
- sock.close if !sock.closed?
+ sock.close
end
else
sock
@@ -1097,7 +1103,7 @@ class Socket < BasicSocket
# }
#
def self.unix_server_socket(path)
- if !unix_socket_abstract_name?(path)
+ unless unix_socket_abstract_name?(path)
begin
st = File.lstat(path)
rescue Errno::ENOENT
@@ -1111,8 +1117,8 @@ class Socket < BasicSocket
begin
yield s
ensure
- s.close if !s.closed?
- if !unix_socket_abstract_name?(path)
+ s.close
+ unless unix_socket_abstract_name?(path)
File.unlink path
end
end
@@ -1197,9 +1203,9 @@ class Socket < BasicSocket
# it is extended by IO::WaitWritable.
# So IO::WaitWritable can be used to rescue the exceptions for retrying connect_nonblock.
#
- # By specifying `exception: false`, the options hash allows you to indicate
+ # By specifying a keyword argument _exception_ to +false+, you can indicate
# that connect_nonblock should not raise an IO::WaitWritable exception, but
- # return the symbol :wait_writable instead.
+ # return the symbol +:wait_writable+ instead.
#
# === See
# # Socket#connect
@@ -1255,9 +1261,9 @@ class UDPSocket < IPSocket
# it is extended by IO::WaitReadable.
# So IO::WaitReadable can be used to rescue the exceptions for retrying recvfrom_nonblock.
#
- # By specifying `exception: false`, the options hash allows you to indicate
- # that recvmsg_nonblock should not raise an IO::WaitWritable exception, but
- # return the symbol :wait_writable instead.
+ # By specifying a keyword argument _exception_ to +false+, you can indicate
+ # that recvfrom_nonblock should not raise an IO::WaitReadable exception, but
+ # return the symbol +:wait_readable+ instead.
#
# === See
# * Socket#recvfrom
@@ -1296,9 +1302,9 @@ class TCPServer < TCPSocket
# it is extended by IO::WaitReadable.
# So IO::WaitReadable can be used to rescue the exceptions for retrying accept_nonblock.
#
- # By specifying `exception: false`, the options hash allows you to indicate
+ # By specifying a keyword argument _exception_ to +false+, you can indicate
# that accept_nonblock should not raise an IO::WaitReadable exception, but
- # return the symbol :wait_readable instead.
+ # return the symbol +:wait_readable+ instead.
#
# === See
# * TCPServer#accept
@@ -1337,9 +1343,9 @@ class UNIXServer < UNIXSocket
# it is extended by IO::WaitReadable.
# So IO::WaitReadable can be used to rescue the exceptions for retrying accept_nonblock.
#
- # By specifying `exception: false`, the options hash allows you to indicate
+ # By specifying a keyword argument _exception_ to +false+, you can indicate
# that accept_nonblock should not raise an IO::WaitReadable exception, but
- # return the symbol :wait_readable instead.
+ # return the symbol +:wait_readable+ instead.
#
# === See
# * UNIXServer#accept
diff --git a/ext/socket/mkconstants.rb b/ext/socket/mkconstants.rb
index 0ebf628b46..81b8224077 100644
--- a/ext/socket/mkconstants.rb
+++ b/ext/socket/mkconstants.rb
@@ -73,7 +73,15 @@ def each_name(pat)
}
end
-ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_const_decls")
+erb_new = lambda do |src, safe, trim|
+ if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
+ ERB.new(src, trim_mode: trim)
+ else
+ ERB.new(src, safe, trim)
+ end
+end
+
+erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_decls")
% each_const {|guard, name, default_value|
#if !defined(<%=name%>)
# if defined(HAVE_CONST_<%=name.upcase%>)
@@ -87,7 +95,7 @@ ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_const_decls")
% }
EOS
-ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_const_defs_in_guard(name, default_value)")
+erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_defs_in_guard(name, default_value)")
#if defined(<%=name%>)
/* <%= COMMENTS[name] %> */
rb_define_const(rb_cSocket, <%=c_str name%>, INTEGER2NUM(<%=name%>));
@@ -96,7 +104,7 @@ ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_const_defs_in_guard(name, def
#endif
EOS
-ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_const_defs")
+erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_const_defs")
% each_const {|guard, name, default_value|
% if guard
#if <%=guard%>
@@ -146,7 +154,7 @@ def each_names_with_len(pat, prefix_optional=nil)
}
end
-ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_name_to_int_decl(funcname, pat, prefix_optional, guard=nil)")
+erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_name_to_int_decl(funcname, pat, prefix_optional, guard=nil)")
%if guard
#ifdef <%=guard%>
int <%=funcname%>(const char *str, long len, int *valp);
@@ -156,7 +164,7 @@ int <%=funcname%>(const char *str, long len, int *valp);
%end
EOS
-ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_name_to_int_func_in_guard(funcname, pat, prefix_optional, guard=nil)")
+erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_name_to_int_func_in_guard(funcname, pat, prefix_optional, guard=nil)")
int
<%=funcname%>(const char *str, long len, int *valp)
{
@@ -177,7 +185,7 @@ int
}
EOS
-ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_name_to_int_func(funcname, pat, prefix_optional, guard=nil)")
+erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_name_to_int_func(funcname, pat, prefix_optional, guard=nil)")
%if guard
#ifdef <%=guard%>
<%=gen_name_to_int_func_in_guard(funcname, pat, prefix_optional, guard)%>
@@ -206,7 +214,7 @@ def reverse_each_name_with_prefix_optional(pat, prefix_pat)
end
end
-ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_int_to_name_hash(hash_var, pat, prefix_pat)")
+erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_int_to_name_hash(hash_var, pat, prefix_pat)")
<%=hash_var%> = st_init_numtable();
% reverse_each_name_with_prefix_optional(pat, prefix_pat) {|n,s|
#ifdef <%=n%>
@@ -215,7 +223,7 @@ ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_int_to_name_hash(hash_var, pa
% }
EOS
-ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_int_to_name_func(func_name, hash_var)")
+erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_int_to_name_func(func_name, hash_var)")
ID
<%=func_name%>(int val)
{
@@ -226,7 +234,7 @@ ID
}
EOS
-ERB.new(<<'EOS', nil, '%').def_method(Object, "gen_int_to_name_decl(func_name, hash_var)")
+erb_new.call(<<'EOS', nil, '%').def_method(Object, "gen_int_to_name_decl(func_name, hash_var)")
ID <%=func_name%>(int val);
EOS
@@ -275,7 +283,7 @@ def_intern('rsock_intern_udp_optname', /\AUDP_/, "UDP_")
def_intern('rsock_intern_scm_optname', /\ASCM_/, "SCM_")
def_intern('rsock_intern_local_optname', /\ALOCAL_/, "LOCAL_")
-result = ERB.new(<<'EOS', nil, '%').result(binding)
+result = erb_new.call(<<'EOS', nil, '%').result(binding)
/* autogenerated file */
<%= INTERN_DEFS.map {|vardef, gen_hash, decl, func| vardef }.join("\n") %>
@@ -318,7 +326,7 @@ init_constants(void)
EOS
-header_result = ERB.new(<<'EOS', nil, '%').result(binding)
+header_result = erb_new.call(<<'EOS', nil, '%').result(binding)
/* autogenerated file */
<%= gen_const_decls %>
<%= NAME_TO_INT_DEFS.map {|decl, func| decl }.join("\n") %>
diff --git a/ext/socket/option.c b/ext/socket/option.c
index bf3af171a2..5ad44cdcd8 100644
--- a/ext/socket/option.c
+++ b/ext/socket/option.c
@@ -424,7 +424,7 @@ sockopt_ipv4_multicast_loop(VALUE self)
}
#endif
rb_raise(rb_eTypeError, "ipv4_multicast_loop socket option expected");
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
#define inspect_ipv4_multicast_loop(a,b,c,d) \
@@ -475,7 +475,7 @@ sockopt_ipv4_multicast_ttl(VALUE self)
}
#endif
rb_raise(rb_eTypeError, "ipv4_multicast_ttl socket option expected");
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
#define inspect_ipv4_multicast_ttl(a,b,c,d) \
diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c
index 67bc9c2a97..4c6a9b511c 100644
--- a/ext/socket/raddrinfo.c
+++ b/ext/socket/raddrinfo.c
@@ -145,15 +145,6 @@ ruby_getaddrinfo__darwin(const char *nodename, const char *servname,
#define getaddrinfo(node,serv,hints,res) ruby_getaddrinfo__darwin((node),(serv),(hints),(res))
#endif
-#ifndef GETADDRINFO_EMU
-struct getaddrinfo_arg
-{
- const char *node;
- const char *service;
- const struct addrinfo *hints;
- struct addrinfo **res;
-};
-
#ifdef HAVE_INET_PTON
static int
parse_numeric_port(const char *service, int *portp)
@@ -182,6 +173,15 @@ parse_numeric_port(const char *service, int *portp)
}
#endif
+#ifndef GETADDRINFO_EMU
+struct getaddrinfo_arg
+{
+ const char *node;
+ const char *service;
+ const struct addrinfo *hints;
+ struct addrinfo **res;
+};
+
static void *
nogvl_getaddrinfo(void *arg)
{
@@ -726,10 +726,8 @@ static void
addrinfo_mark(void *ptr)
{
rb_addrinfo_t *rai = ptr;
- if (rai) {
- rb_gc_mark(rai->inspectname);
- rb_gc_mark(rai->canonname);
- }
+ rb_gc_mark(rai->inspectname);
+ rb_gc_mark(rai->canonname);
}
#define addrinfo_free RUBY_TYPED_DEFAULT_FREE
@@ -2552,7 +2550,7 @@ rsock_io_socket_addrinfo(VALUE io, struct sockaddr *addr, socklen_t len)
rb_raise(rb_eTypeError, "neither IO nor file descriptor");
}
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
/*
diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h
index 352da8c56e..0ce77a5f6e 100644
--- a/ext/socket/rubysocket.h
+++ b/ext/socket/rubysocket.h
@@ -26,7 +26,13 @@
# if defined(_MSC_VER)
# undef HAVE_TYPE_STRUCT_SOCKADDR_DL
# endif
+/*
+ * FIXME: failures if we make nonblocking the default
+ * [ruby-core:89973] [ruby-core:89976] [ruby-core:89977] [Bug #14968]
+ */
+# define RSOCK_NONBLOCK_DEFAULT (0)
#else
+# define RSOCK_NONBLOCK_DEFAULT (0)
# include <sys/socket.h>
# include <netinet/in.h>
# ifdef HAVE_NETINET_IN_SYSTM_H
@@ -408,7 +414,7 @@ NORETURN(void rsock_sys_fail_raddrinfo_or_sockaddr(const char *, VALUE addr, VAL
* all cases. For some syscalls (e.g. accept/accept4), blocking on the
* syscall instead of relying on select/poll allows the kernel to use
* "wake-one" behavior and avoid the thundering herd problem.
- * This is likely safe on all other *nix-like systems, so this whitelist
+ * This is likely safe on all other *nix-like systems, so this safe list
* can be expanded by interested parties.
*/
#if defined(__linux__)
@@ -430,6 +436,11 @@ static inline void rsock_maybe_wait_fd(int fd) { }
# define MSG_DONTWAIT_RELIABLE 0
#endif
+VALUE rsock_read_nonblock(VALUE sock, VALUE length, VALUE buf, VALUE ex);
+VALUE rsock_write_nonblock(VALUE sock, VALUE buf, VALUE ex);
+
+void rsock_make_fd_nonblock(int fd);
+
#if !defined HAVE_INET_NTOP && ! defined _WIN32
const char *inet_ntop(int, const void *, char *, size_t);
#elif defined __MINGW32__
diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index 14e069bb8d..0059595e1b 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -175,16 +175,17 @@ rsock_socketpair0(int domain, int type, int protocol, int sv[2])
{
int ret;
static int cloexec_state = -1; /* <0: unknown, 0: ignored, >0: working */
+ static const int default_flags = SOCK_CLOEXEC|RSOCK_NONBLOCK_DEFAULT;
if (cloexec_state > 0) { /* common path, if SOCK_CLOEXEC is defined */
- ret = socketpair(domain, type|SOCK_CLOEXEC, protocol, sv);
+ ret = socketpair(domain, type|default_flags, protocol, sv);
if (ret == 0 && (sv[0] <= 2 || sv[1] <= 2)) {
goto fix_cloexec; /* highly unlikely */
}
goto update_max_fd;
}
else if (cloexec_state < 0) { /* usually runs once only for detection */
- ret = socketpair(domain, type|SOCK_CLOEXEC, protocol, sv);
+ ret = socketpair(domain, type|default_flags, protocol, sv);
if (ret == 0) {
cloexec_state = rsock_detect_cloexec(sv[0]);
if ((cloexec_state == 0) || (sv[0] <= 2 || sv[1] <= 2))
@@ -213,6 +214,10 @@ rsock_socketpair0(int domain, int type, int protocol, int sv[2])
fix_cloexec:
rb_maygvl_fd_fix_cloexec(sv[0]);
rb_maygvl_fd_fix_cloexec(sv[1]);
+ if (RSOCK_NONBLOCK_DEFAULT) {
+ rsock_make_fd_nonblock(sv[0]);
+ rsock_make_fd_nonblock(sv[1]);
+ }
update_max_fd:
rb_update_max_fd(sv[0]);
@@ -231,6 +236,10 @@ rsock_socketpair0(int domain, int type, int protocol, int sv[2])
rb_fd_fix_cloexec(sv[0]);
rb_fd_fix_cloexec(sv[1]);
+ if (RSOCK_NONBLOCK_DEFAULT) {
+ rsock_make_fd_nonblock(sv[0]);
+ rsock_make_fd_nonblock(sv[1]);
+ }
return ret;
}
#endif /* !SOCK_CLOEXEC */
@@ -983,7 +992,18 @@ sock_sockaddr(struct sockaddr *addr, socklen_t len)
* call-seq:
* Socket.gethostbyname(hostname) => [official_hostname, alias_hostnames, address_family, *address_list]
*
- * Obtains the host information for _hostname_.
+ * Use Addrinfo.getaddrinfo instead.
+ * This method is deprecated for the following reasons:
+ *
+ * - The 3rd element of the result is the address family of the first address.
+ * The address families of the rest of the addresses are not returned.
+ * - Uncommon address representation:
+ * 4/16-bytes binary string to represent IPv4/IPv6 address.
+ * - gethostbyname() may take a long time and it may block other threads.
+ * (GVL cannot be released since gethostbyname() is not thread safe.)
+ * - This method uses gethostbyname() function already removed from POSIX.
+ *
+ * This method obtains the host information for _hostname_.
*
* p Socket.gethostbyname("hal") #=> ["localhost", ["hal"], 2, "\x7F\x00\x00\x01"]
*
@@ -1000,10 +1020,26 @@ sock_s_gethostbyname(VALUE obj, VALUE host)
* call-seq:
* Socket.gethostbyaddr(address_string [, address_family]) => hostent
*
- * Obtains the host information for _address_.
+ * Use Addrinfo#getnameinfo instead.
+ * This method is deprecated for the following reasons:
+ *
+ * - Uncommon address representation:
+ * 4/16-bytes binary string to represent IPv4/IPv6 address.
+ * - gethostbyaddr() may take a long time and it may block other threads.
+ * (GVL cannot be released since gethostbyname() is not thread safe.)
+ * - This method uses gethostbyname() function already removed from POSIX.
+ *
+ * This method obtains the host information for _address_.
*
* p Socket.gethostbyaddr([221,186,184,68].pack("CCCC"))
* #=> ["carbon.ruby-lang.org", [], 2, "\xDD\xBA\xB8D"]
+ *
+ * p Socket.gethostbyaddr([127,0,0,1].pack("CCCC"))
+ * ["localhost", [], 2, "\x7F\x00\x00\x01"]
+ * p Socket.gethostbyaddr(([0]*15+[1]).pack("C"*16))
+ * #=> ["localhost", ["ip6-localhost", "ip6-loopback"], 10,
+ * "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"]
+ *
*/
static VALUE
sock_s_gethostbyaddr(int argc, VALUE *argv)
@@ -1137,6 +1173,9 @@ sock_s_getservbyport(int argc, VALUE *argv)
*
* Obtains address information for _nodename_:_servname_.
*
+ * Note that Addrinfo.getaddrinfo provides the same functionality in
+ * an object oriented style.
+ *
* _family_ should be an address family such as: :INET, :INET6, etc.
*
* _socktype_ should be a socket type such as: :STREAM, :DGRAM, :RAW, etc.
@@ -1287,7 +1326,7 @@ sock_s_getnameinfo(int argc, VALUE *argv)
hptr = NULL;
}
else {
- strncpy(hbuf, StringValuePtr(host), sizeof(hbuf));
+ strncpy(hbuf, StringValueCStr(host), sizeof(hbuf));
hbuf[sizeof(hbuf) - 1] = '\0';
hptr = hbuf;
}
@@ -1301,7 +1340,7 @@ sock_s_getnameinfo(int argc, VALUE *argv)
pptr = pbuf;
}
else {
- strncpy(pbuf, StringValuePtr(port), sizeof(pbuf));
+ strncpy(pbuf, StringValueCStr(port), sizeof(pbuf));
pbuf[sizeof(pbuf) - 1] = '\0';
pptr = pbuf;
}
@@ -1351,7 +1390,7 @@ sock_s_getnameinfo(int argc, VALUE *argv)
errno = saved_errno;
rsock_raise_socket_error("getnameinfo", error);
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
/*
diff --git a/ext/socket/tcpsocket.c b/ext/socket/tcpsocket.c
index a7a82fd880..29a3eda45f 100644
--- a/ext/socket/tcpsocket.c
+++ b/ext/socket/tcpsocket.c
@@ -41,7 +41,16 @@ tcp_sockaddr(struct sockaddr *addr, socklen_t len)
* call-seq:
* TCPSocket.gethostbyname(hostname) => [official_hostname, alias_hostnames, address_family, *address_list]
*
- * Lookups host information by _hostname_.
+ * Use Addrinfo.getaddrinfo instead.
+ * This method is deprecated for the following reasons:
+ *
+ * - The 3rd element of the result is the address family of the first address.
+ * The address families of the rest of the addresses are not returned.
+ * - gethostbyname() may take a long time and it may block other threads.
+ * (GVL cannot be released since gethostbyname() is not thread safe.)
+ * - This method uses gethostbyname() function already removed from POSIX.
+ *
+ * This method lookups host information by _hostname_.
*
* TCPSocket.gethostbyname("localhost")
* #=> ["localhost", ["hal"], 2, "127.0.0.1"]
diff --git a/ext/socket/unixsocket.c b/ext/socket/unixsocket.c
index 5a44b552f8..8bdfc84575 100644
--- a/ext/socket/unixsocket.c
+++ b/ext/socket/unixsocket.c
@@ -25,6 +25,28 @@ unixsock_connect_internal(VALUE a)
arg->sockaddrlen, 0);
}
+static VALUE
+unixsock_path_value(VALUE path)
+{
+#ifdef __linux__
+#define TO_STR_FOR_LINUX_ABSTRACT_NAMESPACE 0
+
+ VALUE name = path;
+#if TO_STR_FOR_LINUX_ABSTRACT_NAMESPACE
+ const int isstr = !NIL_P(name = rb_check_string_type(name));
+#else
+ const int isstr = RB_TYPE_P(name, T_STRING);
+#endif
+ if (isstr) {
+ if (RSTRING_LEN(name) == 0 || RSTRING_PTR(name)[0] == '\0') {
+ rb_check_safe_obj(name);
+ return name; /* ignore encoding */
+ }
+ }
+#endif
+ return rb_get_path(path);
+}
+
VALUE
rsock_init_unixsock(VALUE sock, VALUE path, int server)
{
@@ -33,7 +55,7 @@ rsock_init_unixsock(VALUE sock, VALUE path, int server)
int fd, status;
rb_io_t *fptr;
- SafeStringValue(path);
+ path = unixsock_path_value(path);
INIT_SOCKADDR_UN(&sockaddr, sizeof(struct sockaddr_un));
if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) {
@@ -317,6 +339,12 @@ unix_recv_io(int argc, VALUE *argv, VALUE sock)
struct iomsg_arg arg;
struct iovec vec[2];
char buf[1];
+ unsigned int gc_reason = 0;
+ enum {
+ GC_REASON_EMSGSIZE = 0x1,
+ GC_REASON_TRUNCATE = 0x2,
+ GC_REASON_ENOMEM = 0x4
+ };
int fd;
#if FD_PASSING_BY_MSG_CONTROL
@@ -332,6 +360,7 @@ unix_recv_io(int argc, VALUE *argv, VALUE sock)
if (argc <= 1)
mode = Qnil;
+retry:
GetOpenFile(sock, fptr);
arg.msg.msg_name = NULL;
@@ -359,12 +388,31 @@ unix_recv_io(int argc, VALUE *argv, VALUE sock)
arg.fd = fptr->fd;
while ((int)BLOCKING_REGION_FD(recvmsg_blocking, &arg) == -1) {
+ int e = errno;
+ if (e == EMSGSIZE && !(gc_reason & GC_REASON_EMSGSIZE)) {
+ /* FreeBSD gets here when we're out of FDs */
+ gc_reason |= GC_REASON_EMSGSIZE;
+ rb_gc_for_fd(EMFILE);
+ goto retry;
+ }
+ else if (e == ENOMEM && !(gc_reason & GC_REASON_ENOMEM)) {
+ /* ENOMEM is documented in recvmsg manpages */
+ gc_reason |= GC_REASON_ENOMEM;
+ rb_gc_for_fd(e);
+ goto retry;
+ }
if (!rb_io_wait_readable(arg.fd))
- rsock_sys_fail_path("recvmsg(2)", fptr->pathv);
+ rsock_syserr_fail_path(e, "recvmsg(2)", fptr->pathv);
}
#if FD_PASSING_BY_MSG_CONTROL
if (arg.msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr)) {
+ /* FreeBSD and Linux both get here when we're out of FDs */
+ if (!(gc_reason & GC_REASON_TRUNCATE)) {
+ gc_reason |= GC_REASON_TRUNCATE;
+ 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));
diff --git a/ext/stringio/depend b/ext/stringio/depend
index cbd17f5df2..852146f503 100644
--- a/ext/stringio/depend
+++ b/ext/stringio/depend
@@ -7,6 +7,7 @@ stringio.o: $(hdrdir)/ruby/encoding.h
stringio.o: $(hdrdir)/ruby/intern.h
stringio.o: $(hdrdir)/ruby/io.h
stringio.o: $(hdrdir)/ruby/missing.h
+stringio.o: $(hdrdir)/ruby/onigmo.h
stringio.o: $(hdrdir)/ruby/oniguruma.h
stringio.o: $(hdrdir)/ruby/ruby.h
stringio.o: $(hdrdir)/ruby/st.h
diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c
index 9273a8effb..47c2c50b95 100644
--- a/ext/stringio/stringio.c
+++ b/ext/stringio/stringio.c
@@ -20,6 +20,10 @@
#include <sys/fcntl.h>
#endif
+#ifndef RB_INTEGER_TYPE_P
+# define RB_INTEGER_TYPE_P(c) (FIXNUM_P(c) || RB_TYPE_P(c, T_BIGNUM))
+#endif
+
struct StringIO {
VALUE string;
rb_encoding *enc;
@@ -31,6 +35,7 @@ struct StringIO {
static VALUE strio_init(int, VALUE *, struct StringIO *, VALUE);
static VALUE strio_unget_bytes(struct StringIO *, const char *, long);
+static long strio_write(VALUE self, VALUE str);
#define IS_STRIO(obj) (rb_typeddata_is_kind_of((obj), &strio_data_type))
#define error_inval(msg) (rb_syserr_fail(EINVAL, msg))
@@ -52,9 +57,8 @@ static void
strio_mark(void *p)
{
struct StringIO *ptr = p;
- if (ptr) {
- rb_gc_mark(ptr->string);
- }
+
+ rb_gc_mark(ptr->string);
}
static void
@@ -104,15 +108,14 @@ enc_subseq(VALUE str, long pos, long len, rb_encoding *enc)
}
static VALUE
-strio_substr(struct StringIO *ptr, long pos, long len)
+strio_substr(struct StringIO *ptr, long pos, long len, rb_encoding *enc)
{
VALUE str = ptr->string;
- rb_encoding *enc = get_enc(ptr);
long rlen = RSTRING_LEN(str) - pos;
if (len > rlen) len = rlen;
if (len < 0) len = 0;
- if (len == 0) return rb_str_new(0,0);
+ if (len == 0) return rb_enc_str_new(0, 0, enc);
return enc_subseq(str, pos, len, enc);
}
@@ -767,13 +770,15 @@ strio_ungetc(VALUE self, VALUE c)
check_modifiable(ptr);
if (NIL_P(c)) return Qnil;
- if (FIXNUM_P(c)) {
- int cc = FIX2INT(c);
+ if (RB_INTEGER_TYPE_P(c)) {
+ int len, cc = NUM2INT(c);
char buf[16];
enc = rb_enc_get(ptr->string);
+ len = rb_enc_codelen(cc, enc);
+ if (len <= 0) rb_enc_uint_chr(cc, enc);
rb_enc_mbcput(cc, buf, enc);
- return strio_unget_bytes(ptr, buf, rb_enc_codelen(cc, enc));
+ return strio_unget_bytes(ptr, buf, len);
}
else {
SafeStringValue(c);
@@ -798,24 +803,28 @@ static VALUE
strio_ungetbyte(VALUE self, VALUE c)
{
struct StringIO *ptr = readable(self);
- char buf[1], *cp = buf;
- long cl = 1;
check_modifiable(ptr);
- if (NIL_P(c)) return Qnil;
- if (FIXNUM_P(c)) {
- buf[0] = (char)FIX2INT(c);
- return strio_unget_bytes(ptr, buf, 1);
- }
- else {
- SafeStringValue(c);
- cp = RSTRING_PTR(c);
- cl = RSTRING_LEN(c);
- if (cl == 0) return Qnil;
- strio_unget_bytes(ptr, cp, cl);
- RB_GC_GUARD(c);
- return Qnil;
+ switch (TYPE(c)) {
+ case T_NIL:
+ return Qnil;
+ case T_FIXNUM:
+ case T_BIGNUM: ;
+ /* rb_int_modulo() not visible from exts */
+ VALUE v = rb_funcall(c, rb_intern("modulo"), 1, INT2FIX(256));
+ unsigned char cc = NUM2INT(v) & 0xFF;
+ c = rb_str_new((const char *)&cc, 1);
+ break;
+ default:
+ SafeStringValue(c);
}
+
+ const char *cp = RSTRING_PTR(c);
+ long cl = RSTRING_LEN(c);
+ if (cl == 0) return Qnil;
+ strio_unget_bytes(ptr, cp, cl);
+ RB_GC_GUARD(c);
+ return Qnil;
}
static VALUE
@@ -989,15 +998,16 @@ bm_search(const char *little, long llen, const char *big, long blen, const long
struct getline_arg {
VALUE rs;
long limit;
+ unsigned int chomp: 1;
};
static struct getline_arg *
prepare_getline_args(struct getline_arg *arg, int argc, VALUE *argv)
{
- VALUE str, lim;
+ VALUE str, lim, opts;
long limit = -1;
- rb_scan_args(argc, argv, "02", &str, &lim);
+ argc = rb_scan_args(argc, argv, "02:", &str, &lim, &opts);
switch (argc) {
case 0:
str = rb_rs;
@@ -1023,15 +1033,37 @@ prepare_getline_args(struct getline_arg *arg, int argc, VALUE *argv)
}
arg->rs = str;
arg->limit = limit;
+ arg->chomp = 0;
+ if (!NIL_P(opts)) {
+ static ID keywords[1];
+ VALUE vchomp;
+ if (!keywords[0]) {
+ keywords[0] = rb_intern_const("chomp");
+ }
+ rb_get_kwargs(opts, keywords, 0, 1, &vchomp);
+ arg->chomp = (vchomp != Qundef) && RTEST(vchomp);
+ }
return arg;
}
+static inline int
+chomp_newline_width(const char *s, const char *e)
+{
+ if (e > s && *--e == '\n') {
+ if (e > s && *--e == '\r') return 2;
+ return 1;
+ }
+ return 0;
+}
+
static VALUE
strio_getline(struct getline_arg *arg, struct StringIO *ptr)
{
const char *s, *e, *p;
long n, limit = arg->limit;
VALUE str = arg->rs;
+ int w = 0;
+ rb_encoding *enc = get_enc(ptr);
if (ptr->pos >= (n = RSTRING_LEN(ptr->string))) {
return Qnil;
@@ -1043,11 +1075,15 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr)
e = rb_enc_right_char_head(s, s + limit, e, get_enc(ptr));
}
if (NIL_P(str)) {
- str = strio_substr(ptr, ptr->pos, e - s);
+ if (arg->chomp) {
+ w = chomp_newline_width(s, e);
+ }
+ str = strio_substr(ptr, ptr->pos, e - s - w, enc);
}
else if ((n = RSTRING_LEN(str)) == 0) {
p = s;
- while (*p == '\n') {
+ while (p[(p + 1 < e) && (*p == '\r') && 0] == '\n') {
+ p += *p == '\r';
if (++p == e) {
return Qnil;
}
@@ -1056,23 +1092,33 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr)
while ((p = memchr(p, '\n', e - p)) && (p != e)) {
if (*++p == '\n') {
e = p + 1;
+ w = (arg->chomp ? 1 : 0);
+ break;
+ }
+ else if (*p == '\r' && p < e && p[1] == '\n') {
+ e = p + 2;
+ w = (arg->chomp ? 2 : 0);
break;
}
}
- str = strio_substr(ptr, s - RSTRING_PTR(ptr->string), e - s);
+ if (!w && arg->chomp) {
+ w = chomp_newline_width(s, e);
+ }
+ str = strio_substr(ptr, s - RSTRING_PTR(ptr->string), e - s - w, enc);
}
else if (n == 1) {
if ((p = memchr(s, RSTRING_PTR(str)[0], e - s)) != 0) {
e = p + 1;
+ w = (arg->chomp ? (p > s && *(p-1) == '\r') + 1 : 0);
}
- str = strio_substr(ptr, ptr->pos, e - s);
+ str = strio_substr(ptr, ptr->pos, e - s - w, enc);
}
else {
if (n < e - s) {
if (e - s < 1024) {
for (p = s; p + n <= e; ++p) {
if (MEMCMP(p, RSTRING_PTR(str), char, n) == 0) {
- e = p + n;
+ e = p + (arg->chomp ? 0 : n);
break;
}
}
@@ -1082,11 +1128,11 @@ strio_getline(struct getline_arg *arg, struct StringIO *ptr)
p = RSTRING_PTR(str);
bm_init_skip(skip, p, n);
if ((pos = bm_search(p, n, s, e - s, skip)) >= 0) {
- e = s + pos + n;
+ e = s + pos + (arg->chomp ? 0 : n);
}
}
}
- str = strio_substr(ptr, ptr->pos, e - s);
+ str = strio_substr(ptr, ptr->pos, e - s - w, enc);
}
ptr->pos = e - RSTRING_PTR(ptr->string);
ptr->lineno++;
@@ -1108,7 +1154,8 @@ strio_gets(int argc, VALUE *argv, VALUE self)
VALUE str;
if (prepare_getline_args(&arg, argc, argv)->limit == 0) {
- return rb_str_new(0, 0);
+ struct StringIO *ptr = readable(self);
+ return rb_enc_str_new(0, 0, get_enc(ptr));
}
str = strio_getline(&arg, readable(self));
@@ -1205,8 +1252,8 @@ strio_readlines(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * strio.write(string) -> integer
- * strio.syswrite(string) -> integer
+ * strio.write(string, ...) -> integer
+ * strio.syswrite(string) -> integer
*
* Appends the given string to the underlying buffer string of *strio*.
* The stream must be opened for writing. If the argument is not a
@@ -1214,6 +1261,17 @@ strio_readlines(int argc, VALUE *argv, VALUE self)
* Returns the number of bytes written. See IO#write.
*/
static VALUE
+strio_write_m(int argc, VALUE *argv, VALUE self)
+{
+ long len = 0;
+ while (argc-- > 0) {
+ /* StringIO can't exceed long limit */
+ len += strio_write(self, *argv++);
+ }
+ return LONG2NUM(len);
+}
+
+static long
strio_write(VALUE self, VALUE str)
{
struct StringIO *ptr = writable(self);
@@ -1229,7 +1287,7 @@ strio_write(VALUE self, VALUE str)
str = rb_str_conv_enc(str, enc2, enc);
}
len = RSTRING_LEN(str);
- if (len == 0) return INT2FIX(0);
+ if (len == 0) return 0;
check_modifiable(ptr);
olen = RSTRING_LEN(ptr->string);
if (ptr->flags & FMODE_APPEND) {
@@ -1252,7 +1310,7 @@ strio_write(VALUE self, VALUE str)
OBJ_INFECT(ptr->string, self);
RB_GC_GUARD(str);
ptr->pos += len;
- return LONG2NUM(len);
+ return len;
}
/*
@@ -1334,6 +1392,7 @@ strio_read(int argc, VALUE *argv, VALUE self)
StringValue(str);
rb_str_modify(str);
}
+ /* fall through */
case 1:
if (!NIL_P(argv[0])) {
len = NUM2LONG(argv[0]);
@@ -1351,12 +1410,14 @@ strio_read(int argc, VALUE *argv, VALUE self)
case 0:
len = RSTRING_LEN(ptr->string);
if (len <= ptr->pos) {
+ rb_encoding *enc = binary ? rb_ascii8bit_encoding() : get_enc(ptr);
if (NIL_P(str)) {
str = rb_str_new(0, 0);
}
else {
rb_str_resize(str, 0);
}
+ rb_enc_associate(str, enc);
return str;
}
else {
@@ -1365,8 +1426,8 @@ strio_read(int argc, VALUE *argv, VALUE self)
break;
}
if (NIL_P(str)) {
- str = strio_substr(ptr, ptr->pos, len);
- if (binary) rb_enc_associate(str, rb_ascii8bit_encoding());
+ rb_encoding *enc = binary ? rb_ascii8bit_encoding() : get_enc(ptr);
+ str = strio_substr(ptr, ptr->pos, len, enc);
}
else {
long rest = RSTRING_LEN(ptr->string) - ptr->pos;
@@ -1513,7 +1574,7 @@ strio_external_encoding(VALUE self)
static VALUE
strio_internal_encoding(VALUE self)
{
- return Qnil;
+ return Qnil;
}
/*
@@ -1565,6 +1626,7 @@ strio_set_encoding(int argc, VALUE *argv, VALUE self)
void
Init_stringio(void)
{
+#undef rb_intern
VALUE StringIO = rb_define_class("StringIO", rb_cData);
rb_include_module(StringIO, rb_mEnumerable);
@@ -1623,7 +1685,7 @@ Init_stringio(void)
rb_define_method(StringIO, "readlines", strio_readlines, -1);
rb_define_method(StringIO, "read", strio_read, -1);
- rb_define_method(StringIO, "write", strio_write, 1);
+ rb_define_method(StringIO, "write", strio_write_m, -1);
rb_define_method(StringIO, "putc", strio_putc, 1);
/*
diff --git a/ext/stringio/stringio.gemspec b/ext/stringio/stringio.gemspec
new file mode 100644
index 0000000000..df1d468f93
--- /dev/null
+++ b/ext/stringio/stringio.gemspec
@@ -0,0 +1,27 @@
+# -*- encoding: utf-8 -*-
+# frozen_string_literal: true
+# stub: stringio 0.0.0 ruby lib
+# stub: extconf.rb
+
+Gem::Specification.new do |s|
+ s.name = "stringio".freeze
+ s.version = "0.0.2"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 2.6".freeze)
+ s.require_paths = ["lib".freeze]
+ s.authors = ["Nobu Nakada".freeze]
+ s.description = "Pseudo `IO` class from/to `String`.".freeze
+ s.email = "nobu@ruby-lang.org".freeze
+ s.extensions = ["ext/stringio/extconf.rb".freeze]
+ s.files = ["README.md".freeze, "ext/stringio/extconf.rb".freeze, "ext/stringio/stringio.c".freeze]
+ s.homepage = "https://github.com/ruby/stringio".freeze
+ s.licenses = ["BSD-2-Clause".freeze]
+ s.required_ruby_version = Gem::Requirement.new(">= 2.2".freeze)
+ s.rubygems_version = "2.6.11".freeze
+ s.summary = "Pseudo IO on String".freeze
+
+ # s.cert_chain = %w[certs/nobu.pem]
+ # s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/
+
+ s.add_development_dependency 'rake-compiler'
+end
diff --git a/ext/strscan/depend b/ext/strscan/depend
index 318da4f7e7..1c396b00cf 100644
--- a/ext/strscan/depend
+++ b/ext/strscan/depend
@@ -6,6 +6,7 @@ strscan.o: $(hdrdir)/ruby/defines.h
strscan.o: $(hdrdir)/ruby/encoding.h
strscan.o: $(hdrdir)/ruby/intern.h
strscan.o: $(hdrdir)/ruby/missing.h
+strscan.o: $(hdrdir)/ruby/onigmo.h
strscan.o: $(hdrdir)/ruby/oniguruma.h
strscan.o: $(hdrdir)/ruby/re.h
strscan.o: $(hdrdir)/ruby/regex.h
diff --git a/ext/strscan/extconf.rb b/ext/strscan/extconf.rb
index c968f81c95..714fa99fae 100644
--- a/ext/strscan/extconf.rb
+++ b/ext/strscan/extconf.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'mkmf'
$INCFLAGS << " -I$(top_srcdir)"
create_makefile 'strscan'
diff --git a/ext/strscan/strscan.c b/ext/strscan/strscan.c
index 838f0e7f86..77a36fe323 100644
--- a/ext/strscan/strscan.c
+++ b/ext/strscan/strscan.c
@@ -996,7 +996,7 @@ name_to_backref_number(struct re_registers *regs, VALUE regexp, const char* name
/*
* call-seq: [](n)
*
- * Return the n-th subgroup in the most recent match.
+ * Returns the n-th subgroup in the most recent match.
*
* s = StringScanner.new("Fri Dec 12 1975 14:39")
* s.scan(/(\w+) (\w+) (\d+) /) # -> "Fri Dec 12 "
@@ -1034,6 +1034,7 @@ strscan_aref(VALUE self, VALUE idx)
idx = rb_sym2str(idx);
/* fall through */
case T_STRING:
+ if (!p->regex) return Qnil;
RSTRING_GETMEM(idx, name, i);
i = name_to_backref_number(&(p->regs), p->regex, name, name + i, rb_enc_get(idx));
break;
@@ -1052,7 +1053,93 @@ strscan_aref(VALUE self, VALUE idx)
}
/*
- * Return the <i><b>pre</b>-match</i> (in the regular expression sense) of the last scan.
+ * call-seq: size
+ *
+ * Returns the amount of subgroups in the most recent match.
+ * The full match counts as a subgroup.
+ *
+ * s = StringScanner.new("Fri Dec 12 1975 14:39")
+ * s.scan(/(\w+) (\w+) (\d+) /) # -> "Fri Dec 12 "
+ * s.size # -> 4
+ */
+static VALUE
+strscan_size(VALUE self)
+{
+ struct strscanner *p;
+
+ GET_SCANNER(self, p);
+ if (! MATCHED_P(p)) return Qnil;
+ return INT2FIX(p->regs.num_regs);
+}
+
+/*
+ * call-seq: captures
+ *
+ * Returns the subgroups in the most recent match (not including the full match).
+ * If nothing was priorly matched, it returns nil.
+ *
+ * s = StringScanner.new("Fri Dec 12 1975 14:39")
+ * s.scan(/(\w+) (\w+) (\d+) /) # -> "Fri Dec 12 "
+ * s.captures # -> ["Fri", "Dec", "12"]
+ * s.scan(/(\w+) (\w+) (\d+) /) # -> nil
+ * s.captures # -> nil
+ */
+static VALUE
+strscan_captures(VALUE self)
+{
+ struct strscanner *p;
+ int i, num_regs;
+ VALUE new_ary;
+
+ GET_SCANNER(self, p);
+ if (! MATCHED_P(p)) return Qnil;
+
+ num_regs = p->regs.num_regs;
+ new_ary = rb_ary_new2(num_regs);
+
+ for (i = 1; i < num_regs; i++) {
+ VALUE str = extract_range(p, p->prev + p->regs.beg[i],
+ p->prev + p->regs.end[i]);
+ rb_ary_push(new_ary, str);
+ }
+
+ return new_ary;
+}
+
+/*
+ * call-seq:
+ * scanner.values_at( i1, i2, ... iN ) -> an_array
+ *
+ * Returns the subgroups in the most recent match at the given indices.
+ * If nothing was priorly matched, it returns nil.
+ *
+ * s = StringScanner.new("Fri Dec 12 1975 14:39")
+ * s.scan(/(\w+) (\w+) (\d+) /) # -> "Fri Dec 12 "
+ * s.values_at 0, -1, 5, 2 # -> ["Fri Dec 12 ", "12", nil, "Dec"]
+ * s.scan(/(\w+) (\w+) (\d+) /) # -> nil
+ * s.values_at 0, -1, 5, 2 # -> nil
+ */
+
+static VALUE
+strscan_values_at(int argc, VALUE *argv, VALUE self)
+{
+ struct strscanner *p;
+ long i;
+ VALUE new_ary;
+
+ GET_SCANNER(self, p);
+ if (! MATCHED_P(p)) return Qnil;
+
+ new_ary = rb_ary_new2(argc);
+ for (i = 0; i<argc; i++) {
+ rb_ary_push(new_ary, strscan_aref(self, argv[i]));
+ }
+
+ return new_ary;
+}
+
+/*
+ * Returns the <i><b>pre</b>-match</i> (in the regular expression sense) of the last scan.
*
* s = StringScanner.new('test string')
* s.scan(/\w+/) # -> "test"
@@ -1071,7 +1158,7 @@ strscan_pre_match(VALUE self)
}
/*
- * Return the <i><b>post</b>-match</i> (in the regular expression sense) of the last scan.
+ * Returns the <i><b>post</b>-match</i> (in the regular expression sense) of the last scan.
*
* s = StringScanner.new('test string')
* s.scan(/\w+/) # -> "test"
@@ -1325,6 +1412,7 @@ inspect2(struct strscanner *p)
void
Init_strscan(void)
{
+#undef rb_intern
ID id_scanerr = rb_intern("ScanError");
VALUE tmp;
@@ -1391,6 +1479,9 @@ Init_strscan(void)
rb_define_method(StringScanner, "[]", strscan_aref, 1);
rb_define_method(StringScanner, "pre_match", strscan_pre_match, 0);
rb_define_method(StringScanner, "post_match", strscan_post_match, 0);
+ rb_define_method(StringScanner, "size", strscan_size, 0);
+ rb_define_method(StringScanner, "captures", strscan_captures, 0);
+ rb_define_method(StringScanner, "values_at", strscan_values_at, -1);
rb_define_method(StringScanner, "rest", strscan_rest, 0);
rb_define_method(StringScanner, "rest_size", strscan_rest_size, 0);
diff --git a/ext/strscan/strscan.gemspec b/ext/strscan/strscan.gemspec
new file mode 100644
index 0000000000..eefe8fbf2c
--- /dev/null
+++ b/ext/strscan/strscan.gemspec
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+Gem::Specification.new do |s|
+ s.name = "strscan"
+ s.version = '1.0.0'
+ 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 ext/strscan/regenc.h ext/strscan/regint.h}
+ s.extensions = %w{ext/strscan/extconf.rb}
+ s.required_ruby_version = ">= 2.4.0"
+
+ s.authors = ["Minero Aoki"]
+ s.email = [nil]
+ s.homepage = "https://github.com/ruby/strscan"
+ s.license = "BSD-2-Clause"
+
+ s.add_development_dependency "rake-compiler"
+end
diff --git a/ext/syslog/syslog.c b/ext/syslog/syslog.c
index 9c41795f5b..754efd0317 100644
--- a/ext/syslog/syslog.c
+++ b/ext/syslog/syslog.c
@@ -150,6 +150,7 @@ static VALUE mSyslog_close(VALUE self)
static VALUE mSyslog_open(int argc, VALUE *argv, VALUE self)
{
VALUE ident, opt, fac;
+ const char *ident_ptr;
if (syslog_opened) {
rb_raise(rb_eRuntimeError, "syslog already open");
@@ -160,8 +161,9 @@ static VALUE mSyslog_open(int argc, VALUE *argv, VALUE self)
if (NIL_P(ident)) {
ident = rb_gv_get("$0");
}
- SafeStringValue(ident);
- syslog_ident = strdup(RSTRING_PTR(ident));
+ ident_ptr = StringValueCStr(ident);
+ rb_check_safe_obj(ident);
+ syslog_ident = strdup(ident_ptr);
if (NIL_P(opt)) {
syslog_options = LOG_PID | LOG_CONS;
@@ -418,6 +420,7 @@ static VALUE mSyslogMacros_included(VALUE mod, VALUE target)
*/
void Init_syslog(void)
{
+#undef rb_intern
mSyslog = rb_define_module("Syslog");
mSyslogConstants = rb_define_module_under(mSyslog, "Constants");
@@ -504,7 +507,7 @@ void Init_syslog(void)
rb_define_syslog_facility(LOG_NEWS);
#endif
#ifdef LOG_NTP
- rb_define_syslog_facility(LOG_NTP);
+ rb_define_syslog_facility(LOG_NTP);
#endif
#ifdef LOG_SECURITY
rb_define_syslog_facility(LOG_SECURITY);
diff --git a/ext/win32/extconf.rb b/ext/win32/extconf.rb
index 9952274e29..ceab4ef4f4 100644
--- a/ext/win32/extconf.rb
+++ b/ext/win32/extconf.rb
@@ -1,4 +1,4 @@
# frozen_string_literal: false
-if compiled?('fiddle') and $mswin||$mingw||$cygwin
+if $mswin||$mingw||$cygwin
create_makefile('win32')
end
diff --git a/ext/win32/lib/Win32API.rb b/ext/win32/lib/Win32API.rb
index d03ecc1c46..97b29fbf74 100644
--- a/ext/win32/lib/Win32API.rb
+++ b/ext/win32/lib/Win32API.rb
@@ -2,7 +2,7 @@
# frozen_string_literal: true
# for backward compatibility
-warn "Warning:#{caller[0].sub(/:in `.*'\z/, '')}: Win32API is deprecated after Ruby 1.9.1; use fiddle directly instead" if $VERBOSE
+warn "Win32API is deprecated after Ruby 1.9.1; use fiddle directly instead", uplevel: 2
require 'fiddle/import'
diff --git a/ext/win32/lib/win32/registry.rb b/ext/win32/lib/win32/registry.rb
index f3655b78d3..ea04bb34bf 100644
--- a/ext/win32/lib/win32/registry.rb
+++ b/ext/win32/lib/win32/registry.rb
@@ -635,7 +635,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
# Array of String
# :REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_QWORD
# Integer
- # :REG_BINARY
+ # :REG_BINARY, REG_NONE
# String (contains binary data)
#
# When rtype is specified, the value type must be included by
@@ -643,14 +643,16 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
def read(name, *rtype)
type, data = API.QueryValue(@hkey, name)
unless rtype.empty? or rtype.include?(type)
- raise TypeError, "Type mismatch (expect #{rtype.inspect} but #{type} present)"
+ raise TypeError, "Type mismatch (expect [#{
+ rtype.map{|t|Registry.type2name(t)}.join(', ')}] but #{
+ Registry.type2name(type)} present)"
end
case type
when REG_SZ, REG_EXPAND_SZ
[ type, data.encode(name.encoding, WCHAR).chop ]
when REG_MULTI_SZ
[ type, data.encode(name.encoding, WCHAR).split(/\0/) ]
- when REG_BINARY
+ when REG_BINARY, REG_NONE
[ type, data ]
when REG_DWORD
[ type, API.unpackdw(data) ]
@@ -659,7 +661,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
when REG_QWORD
[ type, API.unpackqw(data) ]
else
- raise TypeError, "Type #{type} is not supported."
+ raise TypeError, "Type #{Registry.type2name(type)} is not supported."
end
end
@@ -682,7 +684,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
when REG_EXPAND_SZ
Registry.expand_environ(data)
else
- raise TypeError, "Type #{type} is not supported."
+ raise TypeError, "Type #{Registry.type2name(type)} is not supported."
end
end
@@ -746,7 +748,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
when REG_MULTI_SZ
data = data.to_a.map {|s| s.encode(WCHAR)}.join(WCHAR_NUL) << WCHAR_NUL
termsize = WCHAR_SIZE
- when REG_BINARY
+ when REG_BINARY, REG_NONE
data = data.to_s
when REG_DWORD
data = API.packdw(data.to_i)
@@ -755,7 +757,7 @@ For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/pr
when REG_QWORD
data = API.packqw(data.to_i)
else
- raise TypeError, "Unsupported type #{type}"
+ raise TypeError, "Unsupported type #{Registry.type2name(type)}"
end
API.SetValue(@hkey, name, type, data, data.bytesize + termsize)
end
diff --git a/ext/win32/lib/win32/resolv.rb b/ext/win32/lib/win32/resolv.rb
index 647f1446fc..1eb70d5dc6 100644
--- a/ext/win32/lib/win32/resolv.rb
+++ b/ext/win32/lib/win32/resolv.rb
@@ -42,24 +42,39 @@ begin
rescue LoadError
end
-nt = Module.new do
- break true if [nil].pack("p").size > 4
- extend Importer
- dlload "kernel32.dll"
- getv = extern "int GetVersionExA(void *)", :stdcall
- info = [ 148, 0, 0, 0, 0 ].pack('V5') + "\0" * 128
- getv.call(info)
- break info.unpack('V5')[4] == 2 # VER_PLATFORM_WIN32_NT
+if [nil].pack("p").size <= 4 # 32bit env
+ begin
+ f = Fiddle
+ osid = f::Handle.new["rb_w32_osid"]
+ rescue f::DLError # not ix86, cannot be Windows 9x
+ else
+ if f::Function.new(osid, [], f::TYPE_INT).call < 2 # VER_PLATFORM_WIN32_NT
+ require_relative 'resolv9x'
+ return
+ end
+ end
end
-if not nt
- require_relative 'resolv9x'
- # return # does not work yet
-else
+
module Win32
#====================================================================
# Windows NT
#====================================================================
module Resolv
+ module SZ
+ refine Registry do
+ # ad hoc workaround for broken registry
+ def read_s(key)
+ type, str = read(key)
+ unless type == Registry::REG_SZ
+ warn "Broken registry, #{name}\\#{key} was #{Registry.type2name(type)}, ignored"
+ return String.new
+ end
+ str
+ end
+ end
+ end
+ using SZ
+
TCPIP_NT = 'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters'
class << self
@@ -131,4 +146,3 @@ module Win32
end
end
end
-end
diff --git a/ext/win32/lib/win32/resolv9x.rb b/ext/win32/lib/win32/resolv9x.rb
index 1045bb2e09..a409d59c0c 100644
--- a/ext/win32/lib/win32/resolv9x.rb
+++ b/ext/win32/lib/win32/resolv9x.rb
@@ -141,7 +141,7 @@ module Win32
extend Importer
dlload "wsock32.dll"
end
- WsControl = WSock32.extern "int WsControl(int, int, void *, void *, void *, void *", :stdcall
+ WsControl = WSock32.extern "int WsControl(int, int, void *, void *, void *, void *)", :stdcall
WSAGetLastError = WSock32.extern "int WSAGetLastError(void)", :stdcall
MAX_TDI_ENTITIES = 512
diff --git a/ext/win32/lib/win32/sspi.rb b/ext/win32/lib/win32/sspi.rb
index 20441b389c..8103893d7f 100644
--- a/ext/win32/lib/win32/sspi.rb
+++ b/ext/win32/lib/win32/sspi.rb
@@ -73,7 +73,7 @@ module Win32
end
end
- # Creates binary representaiton of a SecBufferDesc structure,
+ # Creates binary representations of a SecBufferDesc structure,
# including the SecBuffer contained inside.
class SecurityBuffer
diff --git a/ext/win32ole/lib/win32ole.rb b/ext/win32ole/lib/win32ole.rb
new file mode 100644
index 0000000000..d7034f7845
--- /dev/null
+++ b/ext/win32ole/lib/win32ole.rb
@@ -0,0 +1,33 @@
+begin
+ require 'win32ole.so'
+rescue LoadError
+ # do nothing
+end
+
+if defined?(WIN32OLE)
+ # WIN32OLE
+ class WIN32OLE
+
+ #
+ # By overriding Object#methods, WIN32OLE might
+ # work well with did_you_mean gem.
+ # This is experimental.
+ #
+ # require 'win32ole'
+ # dict = WIN32OLE.new('Scripting.Dictionary')
+ # dict.Ade('a', 1)
+ # #=> Did you mean? Add
+ #
+ def methods(*args)
+ super + ole_methods_safely.map(&:name).map(&:to_sym)
+ end
+
+ private
+
+ def ole_methods_safely
+ ole_methods
+ rescue WIN32OLEQueryInterfaceError
+ []
+ end
+ end
+end
diff --git a/ext/win32ole/win32ole.c b/ext/win32ole/win32ole.c
index 73aae71395..8c647e8922 100644
--- a/ext/win32ole/win32ole.c
+++ b/ext/win32ole/win32ole.c
@@ -1,6 +1,7 @@
/*
* (c) 1995 Microsoft Corporation. All rights reserved.
- * Developed by ActiveWare Internet Corp., http://www.ActiveWare.com
+ * Developed by ActiveWare Internet Corp., now known as
+ * ActiveState Tool Corp., http://www.ActiveState.com
*
* Other modifications Copyright (c) 1997, 1998 by Gurusamy Sarathy
* <gsar@umich.edu> and Jan Dubois <jan.dubois@ibm.net>
@@ -26,7 +27,7 @@
const IID IID_IMultiLanguage2 = {0xDCCFC164, 0x2B38, 0x11d2, {0xB7, 0xEC, 0x00, 0xC0, 0x4F, 0x8F, 0x5D, 0x9A}};
#endif
-#define WIN32OLE_VERSION "1.8.5"
+#define WIN32OLE_VERSION "1.8.8"
typedef HRESULT (STDAPICALLTYPE FNCOCREATEINSTANCEEX)
(REFCLSID, IUnknown*, DWORD, COSERVERINFO*, DWORD, MULTI_QI*);
@@ -36,13 +37,13 @@ typedef HWND (WINAPI FNHTMLHELP)(HWND hwndCaller, LPCSTR pszFile,
typedef BOOL (FNENUMSYSEMCODEPAGES) (CODEPAGE_ENUMPROC, DWORD);
VALUE cWIN32OLE;
-#if defined(RB_THREAD_SPECIFIC) && (defined(__CYGWIN__) || defined(__MINGW32__))
+#if defined(RB_THREAD_SPECIFIC) && (defined(__CYGWIN__))
static RB_THREAD_SPECIFIC BOOL g_ole_initialized;
# define g_ole_initialized_init() ((void)0)
# define g_ole_initialized_set(val) (g_ole_initialized = (val))
#else
static volatile DWORD g_ole_initialized_key = TLS_OUT_OF_INDEXES;
-# define g_ole_initialized (BOOL)TlsGetValue(g_ole_initialized_key)
+# define g_ole_initialized (TlsGetValue(g_ole_initialized_key)!=0)
# define g_ole_initialized_init() (g_ole_initialized_key = TlsAlloc())
# define g_ole_initialized_set(val) TlsSetValue(g_ole_initialized_key, (void*)(val))
#endif
@@ -50,6 +51,7 @@ static volatile DWORD g_ole_initialized_key = TLS_OUT_OF_INDEXES;
static BOOL g_uninitialize_hooked = FALSE;
static BOOL g_cp_installed = FALSE;
static BOOL g_lcid_installed = FALSE;
+static BOOL g_running_nano = FALSE;
static HINSTANCE ghhctrl = NULL;
static HINSTANCE gole32 = NULL;
static FNCOCREATEINSTANCEEX *gCoCreateInstanceEx = NULL;
@@ -169,6 +171,7 @@ static VALUE fole_activex_initialize(VALUE self);
static void com_hash_free(void *ptr);
static void com_hash_mark(void *ptr);
static size_t com_hash_size(const void *ptr);
+static void check_nano_server(void);
static const rb_data_type_t ole_datatype = {
"win32ole",
@@ -817,16 +820,22 @@ ole_initialize(void)
}
if(g_ole_initialized == FALSE) {
- hr = OleInitialize(NULL);
+ if(g_running_nano) {
+ hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+ } else {
+ hr = OleInitialize(NULL);
+ }
if(FAILED(hr)) {
ole_raise(hr, rb_eRuntimeError, "fail: OLE initialize");
}
g_ole_initialized_set(TRUE);
- hr = CoRegisterMessageFilter(&imessage_filter, &previous_filter);
- if(FAILED(hr)) {
- previous_filter = NULL;
- ole_raise(hr, rb_eRuntimeError, "fail: install OLE MessageFilter");
+ if (g_running_nano == FALSE) {
+ hr = CoRegisterMessageFilter(&imessage_filter, &previous_filter);
+ if(FAILED(hr)) {
+ previous_filter = NULL;
+ ole_raise(hr, rb_eRuntimeError, "fail: install OLE MessageFilter");
+ }
}
}
}
@@ -1801,7 +1810,9 @@ ole_const_load(ITypeLib *pTypeLib, VALUE klass, VALUE self)
*pName = toupper((int)*pName);
id = rb_intern(pName);
if (rb_is_const_id(id)) {
- rb_define_const(klass, pName, val);
+ if(!rb_const_defined_at(klass, id)) {
+ rb_define_const(klass, pName, val);
+ }
}
else {
rb_hash_aset(constant, rb_str_new2(pName), val);
@@ -2063,12 +2074,12 @@ fole_s_const_load(int argc, VALUE *argv, VALUE self)
hr = pole->pDispatch->lpVtbl->GetTypeInfo(pole->pDispatch,
0, lcid, &pTypeInfo);
if(FAILED(hr)) {
- ole_raise(hr, rb_eRuntimeError, "failed to GetTypeInfo");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeInfo");
}
hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, &pTypeLib, &index);
if(FAILED(hr)) {
OLE_RELEASE(pTypeInfo);
- ole_raise(hr, rb_eRuntimeError, "failed to GetContainingTypeLib");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetContainingTypeLib");
}
OLE_RELEASE(pTypeInfo);
if(!RB_TYPE_P(klass, T_NIL)) {
@@ -2432,12 +2443,16 @@ fole_s_ole_uninitialize(VALUE self)
/*
* call-seq:
* WIN32OLE.new(server, [host]) -> WIN32OLE object
+ * WIN32OLE.new(server, license: 'key') -> WIN32OLE object
*
* Returns a new WIN32OLE object(OLE Automation object).
* The first argument server specifies OLE Automation server.
* The first argument should be CLSID or PROGID.
* If second argument host specified, then returns OLE Automation
* object on host.
+ * If :license keyword argument is provided,
+ * IClassFactory2::CreateInstanceLic is used to create instance of
+ * licensed server.
*
* WIN32OLE.new('Excel.Application') # => Excel OLE Automation WIN32OLE object.
* WIN32OLE.new('{00024500-0000-0000-C000-000000000046}') # => Excel OLE Automation WIN32OLE object.
@@ -2448,13 +2463,19 @@ fole_initialize(int argc, VALUE *argv, VALUE self)
VALUE svr_name;
VALUE host;
VALUE others;
+ VALUE opts;
HRESULT hr;
CLSID clsid;
OLECHAR *pBuf;
+ OLECHAR *key_buf;
IDispatch *pDispatch;
+ IClassFactory2 * pIClassFactory2;
void *p;
+ static ID keyword_ids[1];
+ VALUE kwargs[1];
+
rb_call_super(0, 0);
- rb_scan_args(argc, argv, "11*", &svr_name, &host, &others);
+ rb_scan_args(argc, argv, "11*:", &svr_name, &host, &others, &opts);
StringValue(svr_name);
if (rb_safe_level() > 0 && OBJ_TAINTED(svr_name)) {
@@ -2483,9 +2504,35 @@ fole_initialize(int argc, VALUE *argv, VALUE self)
StringValuePtr(svr_name));
}
- /* get IDispatch interface */
- hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
- &IID_IDispatch, &p);
+ if (!keyword_ids[0]) {
+ keyword_ids[0] = rb_intern_const("license");
+ }
+ rb_get_kwargs(opts, keyword_ids, 0, 1, kwargs);
+
+ if (kwargs[0] == Qundef) {
+ /* get IDispatch interface */
+ hr = CoCreateInstance(
+ &clsid,
+ NULL,
+ CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
+ &IID_IDispatch,
+ &p
+ );
+ } else {
+ hr = CoGetClassObject(
+ &clsid,
+ CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
+ NULL,
+ &IID_IClassFactory2,
+ (LPVOID)&pIClassFactory2
+ );
+ if (hr == S_OK) {
+ key_buf = ole_vstr2wc(kwargs[0]);
+ hr = pIClassFactory2->lpVtbl->CreateInstanceLic(pIClassFactory2, NULL, NULL, &IID_IDispatch, key_buf, &p);
+ SysFreeString(key_buf);
+ OLE_RELEASE(pIClassFactory2);
+ }
+ }
pDispatch = p;
if(FAILED(hr)) {
ole_raise(hr, eWIN32OLERuntimeError,
@@ -2602,9 +2649,7 @@ ole_invoke(int argc, VALUE *argv, VALUE self, USHORT wFlags, BOOL is_bracket)
&wcmdname, 1, lcid, &DispID);
SysFreeString(wcmdname);
if(FAILED(hr)) {
- ole_raise(hr, rb_eNoMethodError,
- "unknown property or method: `%s'",
- StringValuePtr(cmd));
+ return rb_eNoMethodError;
}
}
@@ -2618,7 +2663,7 @@ ole_invoke(int argc, VALUE *argv, VALUE self, USHORT wFlags, BOOL is_bracket)
/*------------------------------------------
hash object ==> named dispatch parameters
--------------------------------------------*/
- cNamedArgs = rb_long2int(RHASH_SIZE(param));
+ cNamedArgs = rb_long2int((long)RHASH_SIZE(param));
op.dp.cArgs = cNamedArgs + argc - 2;
op.pNamedArgs = ALLOCA_N(OLECHAR*, cNamedArgs + 1);
op.dp.rgvarg = ALLOCA_N(VARIANTARG, op.dp.cArgs);
@@ -2806,7 +2851,11 @@ ole_invoke(int argc, VALUE *argv, VALUE self, USHORT wFlags, BOOL is_bracket)
static VALUE
fole_invoke(int argc, VALUE *argv, VALUE self)
{
- return ole_invoke(argc, argv, self, DISPATCH_METHOD|DISPATCH_PROPERTYGET, FALSE);
+ VALUE v = ole_invoke(argc, argv, self, DISPATCH_METHOD|DISPATCH_PROPERTYGET, FALSE);
+ if (v == rb_eNoMethodError) {
+ return rb_call_super(argc, argv);
+ }
+ return v;
}
static VALUE
@@ -2819,8 +2868,7 @@ ole_invoke2(VALUE self, VALUE dispid, VALUE args, VALUE types, USHORT dispkind)
VARIANT result;
DISPPARAMS dispParams;
VARIANTARG* realargs = NULL;
- int i, j;
- VALUE obj = Qnil;
+ int i, j; VALUE obj = Qnil;
VALUE tp, param;
VALUE v;
VARTYPE vt;
@@ -3074,7 +3122,11 @@ fole_setproperty2(VALUE self, VALUE dispid, VALUE args, VALUE types)
static VALUE
fole_setproperty_with_bracket(int argc, VALUE *argv, VALUE self)
{
- return ole_invoke(argc, argv, self, DISPATCH_PROPERTYPUT, TRUE);
+ VALUE v = ole_invoke(argc, argv, self, DISPATCH_PROPERTYPUT, TRUE);
+ if (v == rb_eNoMethodError) {
+ return rb_call_super(argc, argv);
+ }
+ return v;
}
/*
@@ -3093,7 +3145,11 @@ fole_setproperty_with_bracket(int argc, VALUE *argv, VALUE self)
static VALUE
fole_setproperty(int argc, VALUE *argv, VALUE self)
{
- return ole_invoke(argc, argv, self, DISPATCH_PROPERTYPUT, FALSE);
+ VALUE v = ole_invoke(argc, argv, self, DISPATCH_PROPERTYPUT, FALSE);
+ if (v == rb_eNoMethodError) {
+ return rb_call_super(argc, argv);
+ }
+ return v;
}
/*
@@ -3115,7 +3171,11 @@ fole_setproperty(int argc, VALUE *argv, VALUE self)
static VALUE
fole_getproperty_with_bracket(int argc, VALUE *argv, VALUE self)
{
- return ole_invoke(argc, argv, self, DISPATCH_PROPERTYGET, TRUE);
+ VALUE v = ole_invoke(argc, argv, self, DISPATCH_PROPERTYGET, TRUE);
+ if (v == rb_eNoMethodError) {
+ return rb_call_super(argc, argv);
+ }
+ return v;
}
static VALUE
@@ -3265,7 +3325,7 @@ fole_each(VALUE self)
if (FAILED(hr)) {
VariantClear(&result);
- ole_raise(hr, eWIN32OLERuntimeError, "failed to get IEnum Interface");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to get IEnum Interface");
}
if (V_VT(&result) == VT_UNKNOWN) {
@@ -3281,7 +3341,7 @@ fole_each(VALUE self)
}
if (FAILED(hr) || !pEnum) {
VariantClear(&result);
- ole_raise(hr, rb_eRuntimeError, "failed to get IEnum Interface");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to get IEnum Interface");
}
VariantClear(&result);
@@ -3298,11 +3358,11 @@ fole_each(VALUE self)
static VALUE
fole_missing(int argc, VALUE *argv, VALUE self)
{
- VALUE mid, sym;
+ VALUE mid, org_mid, sym, v;
const char* mname;
long n;
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
- mid = argv[0];
+ mid = org_mid = argv[0];
sym = rb_check_symbol(&mid);
if (!NIL_P(sym)) mid = rb_sym2str(sym);
mname = StringValueCStr(mid);
@@ -3318,7 +3378,12 @@ fole_missing(int argc, VALUE *argv, VALUE self)
}
else {
argv[0] = rb_enc_associate(rb_str_dup(mid), cWIN32OLE_enc);
- return ole_invoke(argc, argv, self, DISPATCH_METHOD|DISPATCH_PROPERTYGET, FALSE);
+ v = ole_invoke(argc, argv, self, DISPATCH_METHOD|DISPATCH_PROPERTYGET, FALSE);
+ if (v == rb_eNoMethodError) {
+ argv[0] = org_mid;
+ return rb_call_super(argc, argv);
+ }
+ return v;
}
}
@@ -3335,7 +3400,7 @@ typeinfo_from_ole(struct oledata *pole, ITypeInfo **ppti)
HRESULT hr = pole->pDispatch->lpVtbl->GetTypeInfo(pole->pDispatch,
0, lcid, &pTypeInfo);
if(FAILED(hr)) {
- ole_raise(hr, rb_eRuntimeError, "failed to GetTypeInfo");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeInfo");
}
hr = pTypeInfo->lpVtbl->GetDocumentation(pTypeInfo,
-1,
@@ -3345,7 +3410,7 @@ typeinfo_from_ole(struct oledata *pole, ITypeInfo **ppti)
hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, &pTypeLib, &i);
OLE_RELEASE(pTypeInfo);
if (FAILED(hr)) {
- ole_raise(hr, rb_eRuntimeError, "failed to GetContainingTypeLib");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetContainingTypeLib");
}
count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib);
for (i = 0; i < count; i++) {
@@ -3470,7 +3535,7 @@ fole_type(VALUE self)
hr = pole->pDispatch->lpVtbl->GetTypeInfo( pole->pDispatch, 0, lcid, &pTypeInfo );
if(FAILED(hr)) {
- ole_raise(hr, rb_eRuntimeError, "failed to GetTypeInfo");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeInfo");
}
type = ole_type_from_itypeinfo(pTypeInfo);
OLE_RELEASE(pTypeInfo);
@@ -3504,7 +3569,7 @@ fole_typelib(VALUE self)
hr = pole->pDispatch->lpVtbl->GetTypeInfo(pole->pDispatch,
0, lcid, &pTypeInfo);
if(FAILED(hr)) {
- ole_raise(hr, rb_eRuntimeError, "failed to GetTypeInfo");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeInfo");
}
vtlib = ole_typelib_from_itypeinfo(pTypeInfo);
OLE_RELEASE(pTypeInfo);
@@ -3551,7 +3616,7 @@ fole_query_interface(VALUE self, VALUE str_iid)
hr = pole->pDispatch->lpVtbl->QueryInterface(pole->pDispatch, &iid,
&p);
if(FAILED(hr)) {
- ole_raise(hr, eWIN32OLERuntimeError,
+ ole_raise(hr, eWIN32OLEQueryInterfaceError,
"failed to get interface `%s'",
StringValuePtr(str_iid));
}
@@ -3792,7 +3857,7 @@ fole_method_help(VALUE self, VALUE cmdname)
pole = oledata_get_struct(self);
hr = typeinfo_from_ole(pole, &pTypeInfo);
if(FAILED(hr))
- ole_raise(hr, rb_eRuntimeError, "failed to get ITypeInfo");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to get ITypeInfo");
obj = create_win32ole_method(pTypeInfo, cmdname);
@@ -3891,11 +3956,32 @@ com_hash_size(const void *ptr)
return st_memsize(tbl);
}
+static void
+check_nano_server(void)
+{
+ HKEY hsubkey;
+ LONG err;
+ const char * subkey = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Server\\ServerLevels";
+ const char * regval = "NanoServer";
+
+ err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subkey, 0, KEY_READ, &hsubkey);
+ if (err == ERROR_SUCCESS) {
+ err = RegQueryValueEx(hsubkey, regval, NULL, NULL, NULL, NULL);
+ if (err == ERROR_SUCCESS) {
+ g_running_nano = TRUE;
+ }
+ RegCloseKey(hsubkey);
+ }
+}
+
+LCID cWIN32OLE_lcid;
+
void
Init_win32ole(void)
{
cWIN32OLE_lcid = LOCALE_SYSTEM_DEFAULT;
g_ole_initialized_init();
+ check_nano_server();
com_vtbl.QueryInterface = QueryInterface;
com_vtbl.AddRef = AddRef;
@@ -3912,11 +3998,11 @@ Init_win32ole(void)
message_filter.RetryRejectedCall = mf_RetryRejectedCall;
message_filter.MessagePending = mf_MessagePending;
- enc2cp_hash = TypedData_Wrap_Struct(rb_cData, &win32ole_hash_datatype, 0);
+ enc2cp_hash = TypedData_Wrap_Struct(0, &win32ole_hash_datatype, 0);
RTYPEDDATA_DATA(enc2cp_hash) = st_init_numtable();
rb_gc_register_mark_object(enc2cp_hash);
- com_hash = TypedData_Wrap_Struct(rb_cData, &win32ole_hash_datatype, 0);
+ com_hash = TypedData_Wrap_Struct(0, &win32ole_hash_datatype, 0);
RTYPEDDATA_DATA(com_hash) = st_init_numtable();
rb_gc_register_mark_object(com_hash);
diff --git a/ext/win32ole/win32ole.h b/ext/win32ole/win32ole.h
index c019930397..cd627ef765 100644
--- a/ext/win32ole/win32ole.h
+++ b/ext/win32ole/win32ole.h
@@ -112,8 +112,8 @@ struct oledata {
IDispatch *pDispatch;
};
-VALUE cWIN32OLE;
-LCID cWIN32OLE_lcid;
+extern VALUE cWIN32OLE;
+extern LCID cWIN32OLE_lcid;
struct oledata *oledata_get_struct(VALUE obj);
LPWSTR ole_vstr2wc(VALUE vstr);
diff --git a/ext/win32ole/win32ole_error.c b/ext/win32ole/win32ole_error.c
index 62e69b186c..2bb5156263 100644
--- a/ext/win32ole/win32ole_error.c
+++ b/ext/win32ole/win32ole_error.c
@@ -60,6 +60,9 @@ ole_raise(HRESULT hr, VALUE ecs, const char *fmt, ...)
rb_exc_raise(rb_exc_new_str(ecs, msg));
}
+VALUE eWIN32OLERuntimeError;
+VALUE eWIN32OLEQueryInterfaceError;
+
void
Init_win32ole_error(void)
{
@@ -80,4 +83,5 @@ Init_win32ole_error(void)
*
*/
eWIN32OLERuntimeError = rb_define_class("WIN32OLERuntimeError", rb_eRuntimeError);
+ eWIN32OLEQueryInterfaceError = rb_define_class("WIN32OLEQueryInterfaceError", eWIN32OLERuntimeError);
}
diff --git a/ext/win32ole/win32ole_error.h b/ext/win32ole/win32ole_error.h
index e0eee0ecf9..a2f329856f 100644
--- a/ext/win32ole/win32ole_error.h
+++ b/ext/win32ole/win32ole_error.h
@@ -1,7 +1,8 @@
#ifndef WIN32OLE_ERROR_H
#define WIN32OLE_ERROR_H 1
-VALUE eWIN32OLERuntimeError;
+extern VALUE eWIN32OLERuntimeError;
+extern VALUE eWIN32OLEQueryInterfaceError;
NORETURN(PRINTF_ARGS(void ole_raise(HRESULT hr, VALUE ecs, const char *fmt, ...), 3, 4));
void Init_win32ole_error(void);
diff --git a/ext/win32ole/win32ole_event.c b/ext/win32ole/win32ole_event.c
index 6de31cb137..ddb5200b9a 100644
--- a/ext/win32ole/win32ole_event.c
+++ b/ext/win32ole/win32ole_event.c
@@ -942,7 +942,7 @@ ev_advise(int argc, VALUE *argv, VALUE self)
&p);
if (FAILED(hr)) {
OLE_RELEASE(pTypeInfo);
- ole_raise(hr, rb_eRuntimeError,
+ ole_raise(hr, eWIN32OLEQueryInterfaceError,
"failed to query IConnectionPointContainer");
}
pContainer = p;
@@ -953,7 +953,7 @@ ev_advise(int argc, VALUE *argv, VALUE self)
OLE_RELEASE(pContainer);
if (FAILED(hr)) {
OLE_RELEASE(pTypeInfo);
- ole_raise(hr, rb_eRuntimeError, "failed to query IConnectionPoint");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to query IConnectionPoint");
}
pIEV = EVENTSINK_Constructor();
pIEV->m_iid = iid;
@@ -961,7 +961,7 @@ ev_advise(int argc, VALUE *argv, VALUE self)
(IUnknown*)pIEV,
&dwCookie);
if (FAILED(hr)) {
- ole_raise(hr, rb_eRuntimeError, "Advise Error");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "Advise Error");
}
TypedData_Get_Struct(self, struct oleeventdata, &oleevent_datatype, poleev);
@@ -1264,6 +1264,7 @@ fev_get_handler(VALUE self)
void
Init_win32ole_event(void)
{
+#undef rb_intern
ary_ole_event = rb_ary_new();
rb_gc_register_mark_object(ary_ole_event);
id_events = rb_intern("events");
diff --git a/ext/win32ole/win32ole_method.c b/ext/win32ole/win32ole_method.c
index 381c1cbbd3..bf668300c2 100644
--- a/ext/win32ole/win32ole_method.c
+++ b/ext/win32ole/win32ole_method.c
@@ -83,7 +83,7 @@ ole_method_sub(VALUE self, ITypeInfo *pOwnerTypeInfo, ITypeInfo *pTypeInfo, VALU
VALUE method = Qnil;
hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr);
if (FAILED(hr)) {
- ole_raise(hr, eWIN32OLERuntimeError, "failed to GetTypeAttr");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeAttr");
}
for(i = 0; i < pTypeAttr->cFuncs && method == Qnil; i++) {
hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, i, &pFuncDesc);
@@ -119,7 +119,7 @@ ole_methods_from_typeinfo(ITypeInfo *pTypeInfo, int mask)
VALUE methods = rb_ary_new();
hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr);
if (FAILED(hr)) {
- ole_raise(hr, eWIN32OLERuntimeError, "failed to GetTypeAttr");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeAttr");
}
ole_methods_sub(0, pTypeInfo, methods, mask);
@@ -148,7 +148,7 @@ olemethod_from_typeinfo(VALUE self, ITypeInfo *pTypeInfo, VALUE name)
VALUE method = Qnil;
hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr);
if (FAILED(hr)) {
- ole_raise(hr, eWIN32OLERuntimeError, "failed to GetTypeAttr");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeAttr");
}
method = ole_method_sub(self, 0, pTypeInfo, name);
if (method != Qnil) {
@@ -179,7 +179,7 @@ ole_methods_sub(ITypeInfo *pOwnerTypeInfo, ITypeInfo *pTypeInfo, VALUE methods,
WORD i;
hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr);
if (FAILED(hr)) {
- ole_raise(hr, eWIN32OLERuntimeError, "failed to GetTypeAttr");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeAttr");
}
for(i = 0; i < pTypeAttr->cFuncs; i++) {
hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, i, &pFuncDesc);
@@ -283,7 +283,7 @@ folemethod_initialize(VALUE self, VALUE oletype, VALUE method)
}
/*
- * call-seq
+ * call-seq:
* WIN32OLE_METHOD#name
*
* Returns the name of the method.
@@ -308,7 +308,7 @@ ole_method_return_type(ITypeInfo *pTypeInfo, UINT method_index)
hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, method_index, &pFuncDesc);
if (FAILED(hr))
- ole_raise(hr, eWIN32OLERuntimeError, "failed to GetFuncDesc");
+ ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetFuncDesc");
type = ole_typedesc2val(pTypeInfo, &(pFuncDesc->elemdescFunc.tdesc), Qnil);
pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc);
@@ -923,6 +923,8 @@ folemethod_inspect(VALUE self)
return default_inspect(self, "WIN32OLE_METHOD");
}
+VALUE cWIN32OLE_METHOD;
+
void Init_win32ole_method(void)
{
cWIN32OLE_METHOD = rb_define_class("WIN32OLE_METHOD", rb_cObject);
diff --git a/ext/win32ole/win32ole_method.h b/ext/win32ole/win32ole_method.h
index ff2898ebeb..ef907d2fac 100644
--- a/ext/win32ole/win32ole_method.h
+++ b/ext/win32ole/win32ole_method.h
@@ -7,7 +7,7 @@ struct olemethoddata {
UINT index;
};
-VALUE cWIN32OLE_METHOD;
+extern VALUE cWIN32OLE_METHOD;
VALUE folemethod_s_allocate(VALUE klass);
VALUE ole_methods_from_typeinfo(ITypeInfo *pTypeInfo, int mask);
VALUE create_win32ole_method(ITypeInfo *pTypeInfo, VALUE name);
diff --git a/ext/win32ole/win32ole_record.c b/ext/win32ole/win32ole_record.c
index e8838832a7..03523bc47d 100644
--- a/ext/win32ole/win32ole_record.c
+++ b/ext/win32ole/win32ole_record.c
@@ -589,6 +589,8 @@ folerecord_inspect(VALUE self)
field);
}
+VALUE cWIN32OLE_RECORD;
+
void
Init_win32ole_record(void)
{
diff --git a/ext/win32ole/win32ole_record.h b/ext/win32ole/win32ole_record.h
index ea431e91f7..ab1df0ee7f 100644
--- a/ext/win32ole/win32ole_record.h
+++ b/ext/win32ole/win32ole_record.h
@@ -1,7 +1,7 @@
#ifndef WIN32OLE_RECORD_H
#define WIN32OLE_RECORD_H 1
-VALUE cWIN32OLE_RECORD;
+extern VALUE cWIN32OLE_RECORD;
void ole_rec2variant(VALUE rec, VARIANT *var);
void olerecord_set_ivar(VALUE obj, IRecordInfo *pri, void *prec);
VALUE create_win32ole_record(IRecordInfo *pri, void *prec);
diff --git a/ext/win32ole/win32ole_type.c b/ext/win32ole/win32ole_type.c
index e6ac402ecf..fa39bf3696 100644
--- a/ext/win32ole/win32ole_type.c
+++ b/ext/win32ole/win32ole_type.c
@@ -883,6 +883,8 @@ foletype_inspect(VALUE self)
return default_inspect(self, "WIN32OLE_TYPE");
}
+VALUE cWIN32OLE_TYPE;
+
void Init_win32ole_type(void)
{
cWIN32OLE_TYPE = rb_define_class("WIN32OLE_TYPE", rb_cObject);
diff --git a/ext/win32ole/win32ole_type.h b/ext/win32ole/win32ole_type.h
index a26bf3e043..87b551e502 100644
--- a/ext/win32ole/win32ole_type.h
+++ b/ext/win32ole/win32ole_type.h
@@ -1,6 +1,6 @@
#ifndef WIN32OLE_TYPE_H
#define WIN32OLE_TYPE_H 1
-VALUE cWIN32OLE_TYPE;
+extern VALUE cWIN32OLE_TYPE;
VALUE create_win32ole_type(ITypeInfo *pTypeInfo, VALUE name);
ITypeInfo *itypeinfo(VALUE self);
VALUE ole_type_from_itypeinfo(ITypeInfo *pTypeInfo);
diff --git a/ext/win32ole/win32ole_typelib.c b/ext/win32ole/win32ole_typelib.c
index 35376c644b..d89f181e07 100644
--- a/ext/win32ole/win32ole_typelib.c
+++ b/ext/win32ole/win32ole_typelib.c
@@ -822,6 +822,8 @@ foletypelib_inspect(VALUE self)
return default_inspect(self, "WIN32OLE_TYPELIB");
}
+VALUE cWIN32OLE_TYPELIB;
+
void
Init_win32ole_typelib(void)
{
diff --git a/ext/win32ole/win32ole_typelib.h b/ext/win32ole/win32ole_typelib.h
index 9fc117fcb4..2c2730bb58 100644
--- a/ext/win32ole/win32ole_typelib.h
+++ b/ext/win32ole/win32ole_typelib.h
@@ -1,7 +1,7 @@
#ifndef WIN32OLE_TYPELIB_H
#define WIN32OLE_TYPELIB_H 1
-VALUE cWIN32OLE_TYPELIB;
+extern VALUE cWIN32OLE_TYPELIB;
void Init_win32ole_typelib(void);
ITypeLib * itypelib(VALUE self);
diff --git a/ext/win32ole/win32ole_variable.c b/ext/win32ole/win32ole_variable.c
index 3dc9972ee7..803083156c 100644
--- a/ext/win32ole/win32ole_variable.c
+++ b/ext/win32ole/win32ole_variable.c
@@ -365,6 +365,8 @@ folevariable_inspect(VALUE self)
return make_inspect("WIN32OLE_VARIABLE", detail);
}
+VALUE cWIN32OLE_VARIABLE;
+
void Init_win32ole_variable(void)
{
cWIN32OLE_VARIABLE = rb_define_class("WIN32OLE_VARIABLE", rb_cObject);
diff --git a/ext/win32ole/win32ole_variable.h b/ext/win32ole/win32ole_variable.h
index 704dc13508..209613fd44 100644
--- a/ext/win32ole/win32ole_variable.h
+++ b/ext/win32ole/win32ole_variable.h
@@ -1,7 +1,7 @@
#ifndef WIN32OLE_VARIABLE_H
#define WIN32OLE_VARIABLE_H 1
-VALUE cWIN32OLE_VARIABLE;
+extern VALUE cWIN32OLE_VARIABLE;
VALUE create_win32ole_variable(ITypeInfo *pTypeInfo, UINT index, VALUE name);
void Init_win32ole_variable(void);
diff --git a/ext/win32ole/win32ole_variant.c b/ext/win32ole/win32ole_variant.c
index 3bbf86df52..93f0636593 100644
--- a/ext/win32ole/win32ole_variant.c
+++ b/ext/win32ole/win32ole_variant.c
@@ -155,7 +155,7 @@ ole_val2variant_err(VALUE val, VARIANT *var)
if (rb_obj_is_kind_of(v, cWIN32OLE_VARIANT)) {
v = folevariant_value(v);
}
- if (TYPE(v) != T_FIXNUM && TYPE(v) != T_BIGNUM && v != Qnil) {
+ if (!(FIXNUM_P(v) || RB_TYPE_P(v, T_BIGNUM) || v == Qnil)) {
rb_raise(eWIN32OLERuntimeError, "failed to convert VT_ERROR VARIANT:`%"PRIsVALUE"'", rb_inspect(v));
}
V_VT(var) = VT_ERROR;
@@ -689,9 +689,12 @@ ole_variant2variant(VALUE val, VARIANT *var)
VariantCopy(var, &(pvar->var));
}
+VALUE cWIN32OLE_VARIANT;
+
void
Init_win32ole_variant(void)
{
+#undef rb_intern
cWIN32OLE_VARIANT = rb_define_class("WIN32OLE_VARIANT", rb_cObject);
rb_define_alloc_func(cWIN32OLE_VARIANT, folevariant_s_allocate);
rb_define_singleton_method(cWIN32OLE_VARIANT, "array", folevariant_s_array, 2);
diff --git a/ext/win32ole/win32ole_variant.h b/ext/win32ole/win32ole_variant.h
index efe7ea8bef..4bd3b0aeea 100644
--- a/ext/win32ole/win32ole_variant.h
+++ b/ext/win32ole/win32ole_variant.h
@@ -1,7 +1,7 @@
#ifndef WIN32OLE_VARIANT_H
#define WIN32OLE_VARIANT_H 1
-VALUE cWIN32OLE_VARIANT;
+extern VALUE cWIN32OLE_VARIANT;
void ole_variant2variant(VALUE val, VARIANT *var);
void Init_win32ole_variant(void);
diff --git a/ext/win32ole/win32ole_variant_m.c b/ext/win32ole/win32ole_variant_m.c
index 4d76fdc790..145c08a16e 100644
--- a/ext/win32ole/win32ole_variant_m.c
+++ b/ext/win32ole/win32ole_variant_m.c
@@ -1,5 +1,7 @@
#include "win32ole.h"
+VALUE mWIN32OLE_VARIANT;
+
void Init_win32ole_variant_m(void)
{
/*
diff --git a/ext/win32ole/win32ole_variant_m.h b/ext/win32ole/win32ole_variant_m.h
index afbef30218..6272a6578f 100644
--- a/ext/win32ole/win32ole_variant_m.h
+++ b/ext/win32ole/win32ole_variant_m.h
@@ -1,7 +1,7 @@
#ifndef WIN32OLE_VARIANT_M_H
#define WIN32OLE_VARIANT_M_H 1
-VALUE mWIN32OLE_VARIANT;
+extern VALUE mWIN32OLE_VARIANT;
void Init_win32ole_variant_m(void);
#endif
diff --git a/ext/zlib/.gitignore b/ext/zlib/.gitignore
new file mode 100644
index 0000000000..069491b4b4
--- /dev/null
+++ b/ext/zlib/.gitignore
@@ -0,0 +1 @@
+/zlib-[1-9]*.*.*
diff --git a/ext/zlib/depend b/ext/zlib/depend
index f094d343dc..bfba309dae 100644
--- a/ext/zlib/depend
+++ b/ext/zlib/depend
@@ -7,6 +7,7 @@ zlib.o: $(hdrdir)/ruby/encoding.h
zlib.o: $(hdrdir)/ruby/intern.h
zlib.o: $(hdrdir)/ruby/io.h
zlib.o: $(hdrdir)/ruby/missing.h
+zlib.o: $(hdrdir)/ruby/onigmo.h
zlib.o: $(hdrdir)/ruby/oniguruma.h
zlib.o: $(hdrdir)/ruby/ruby.h
zlib.o: $(hdrdir)/ruby/st.h
diff --git a/ext/zlib/extconf.rb b/ext/zlib/extconf.rb
index c59ef7fd44..fd2f168522 100644
--- a/ext/zlib/extconf.rb
+++ b/ext/zlib/extconf.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# extconf.rb
#
@@ -27,17 +27,43 @@ else
]
$INCFLAGS << " -I$(ZSRC)"
if $mswin or $mingw
- $libs = append_library($libs, "zdll")
dll = "zlib1.dll"
$extso << dll
+ $cleanfiles << "$(topdir)/#{dll}" << "$(ZIMPLIB)"
+ zmk = "\t$(MAKE) -f $(ZMKFILE) TOP=$(ZSRC)"
+ if $nmake
+ zmkfile = "$(ZSRC)/win32/Makefile.msc"
+ m = "#{zsrc}/win32/Makefile.msc"
+ else
+ zmkfile = "$(ZSRC)/win32/Makefile.gcc"
+ m = "#{zsrc}/win32/Makefile.gcc"
+ zmk += " PREFIX="
+ zmk << CONFIG['CC'][/(.*-)gcc([^\/]*)\z/, 1]
+ zmk << " CC=$(CC)" if $2
+ end
+ m = File.read(m)
+ zimplib = m[/^IMPLIB[ \t]*=[ \t]*(\S+)/, 1]
+ $LOCAL_LIBS << " " << zimplib
+ unless $nmake or /^TOP[ \t]/ =~ m
+ m.gsub!(/win32\/zlib\.def/, '$(TOP)/\&')
+ m.gsub!(/^(\t.*[ \t])(\S+\.rc)/, '\1-I$(<D) $<')
+ m = "TOP = .\n""VPATH=$(TOP)\n" + m
+ zmkfile = File.basename(zmkfile)
+ File.rename(zmkfile, zmkfile+".orig") if File.exist?(zmkfile)
+ File.write(zmkfile, m)
+ end
addconf.push(
- "ZIMPLIB = zdll.lib\n",
+ "ZMKFILE = #{zmkfile}\n",
+ "ZIMPLIB = #{zimplib}\n",
"$(TARGET_SO): $(ZIMPLIB)\n",
"$(ZIMPLIB):\n",
- "\t$(MAKE) -f $(ZSRC)/win32/Makefile.#{$nmake ? 'msc' : 'gcc'} TOP=$(ZSRC) $@\n",
+ "#{zmk} $@\n",
"install-so: $(topdir)/#{dll}",
"$(topdir)/#{dll}: $(ZIMPLIB)\n",
"\t$(Q) $(COPY) #{dll} $(@D)\n",
+ "clean: clean-zsrc\n",
+ "clean-zsrc:\n",
+ "#{zmk} clean\n",
)
end
Logging.message "using zlib in #{zsrc}\n"
diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
index deb3b04617..1b48bb2677 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 "0.6.0"
+#define RUBY_ZLIB_VERSION "1.0.0"
#ifndef GZIP_SUPPORT
#define GZIP_SUPPORT 1
@@ -72,6 +72,7 @@ struct zstream_run_args;
static void zstream_init(struct zstream*, const struct zstream_funcs*);
static void zstream_expand_buffer(struct zstream*);
static void zstream_expand_buffer_into(struct zstream*, unsigned long);
+static int zstream_expand_buffer_non_stream(struct zstream *z);
static void zstream_append_buffer(struct zstream*, const Bytef*, long);
static VALUE zstream_detach_buffer(struct zstream*);
static VALUE zstream_shift_buffer(struct zstream*, long);
@@ -140,18 +141,18 @@ static void gzfile_close(struct gzfile*, int);
static void gzfile_write_raw(struct gzfile*);
static VALUE gzfile_read_raw_partial(VALUE);
static VALUE gzfile_read_raw_rescue(VALUE);
-static VALUE gzfile_read_raw(struct gzfile*);
-static int gzfile_read_raw_ensure(struct gzfile*, long);
+static VALUE gzfile_read_raw(struct gzfile*, VALUE outbuf);
+static int gzfile_read_raw_ensure(struct gzfile*, long, VALUE outbuf);
static char *gzfile_read_raw_until_zero(struct gzfile*, long);
static unsigned int gzfile_get16(const unsigned char*);
static unsigned long gzfile_get32(const unsigned char*);
static void gzfile_set32(unsigned long n, unsigned char*);
static void gzfile_make_header(struct gzfile*);
static void gzfile_make_footer(struct gzfile*);
-static void gzfile_read_header(struct gzfile*);
-static void gzfile_check_footer(struct gzfile*);
+static void gzfile_read_header(struct gzfile*, VALUE outbuf);
+static void gzfile_check_footer(struct gzfile*, VALUE outbuf);
static void gzfile_write(struct gzfile*, Bytef*, long);
-static long gzfile_read_more(struct gzfile*);
+static long gzfile_read_more(struct gzfile*, VALUE outbuf);
static void gzfile_calc_crc(struct gzfile*, VALUE);
static VALUE gzfile_read(struct gzfile*, long);
static VALUE gzfile_read_all(struct gzfile*);
@@ -196,7 +197,7 @@ static VALUE rb_gzwriter_s_allocate(VALUE);
static VALUE rb_gzwriter_s_open(int, VALUE*, VALUE);
static VALUE rb_gzwriter_initialize(int, VALUE*, VALUE);
static VALUE rb_gzwriter_flush(int, VALUE*, VALUE);
-static VALUE rb_gzwriter_write(VALUE, VALUE);
+static VALUE rb_gzwriter_write(int, VALUE*, VALUE);
static VALUE rb_gzwriter_putc(VALUE, VALUE);
static VALUE rb_gzreader_s_allocate(VALUE);
@@ -450,7 +451,7 @@ rb_zlib_adler32(int argc, VALUE *argv, VALUE klass)
static VALUE
rb_zlib_adler32_combine(VALUE klass, VALUE adler1, VALUE adler2, VALUE len2)
{
- return ULONG2NUM(
+ return ULONG2NUM(
adler32_combine(NUM2ULONG(adler1), NUM2ULONG(adler2), NUM2LONG(len2)));
}
#else
@@ -488,7 +489,7 @@ rb_zlib_crc32(int argc, VALUE *argv, VALUE klass)
static VALUE
rb_zlib_crc32_combine(VALUE klass, VALUE crc1, VALUE crc2, VALUE len2)
{
- return ULONG2NUM(
+ return ULONG2NUM(
crc32_combine(NUM2ULONG(crc1), NUM2ULONG(crc2), NUM2LONG(len2)));
}
#else
@@ -527,7 +528,6 @@ rb_zlib_crc_table(VALUE obj)
struct zstream {
unsigned long flags;
VALUE buf;
- long buf_filled;
VALUE input;
z_stream stream;
const struct zstream_funcs {
@@ -550,6 +550,7 @@ struct zstream {
#define ZSTREAM_IS_FINISHED(z) ((z)->flags & ZSTREAM_FLAG_FINISHED)
#define ZSTREAM_IS_CLOSING(z) ((z)->flags & ZSTREAM_FLAG_CLOSING)
#define ZSTREAM_IS_GZFILE(z) ((z)->flags & ZSTREAM_FLAG_GZFILE)
+#define ZSTREAM_BUF_FILLED(z) (NIL_P((z)->buf) ? 0 : RSTRING_LEN((z)->buf))
#define ZSTREAM_EXPAND_BUFFER_OK 0
@@ -599,7 +600,6 @@ zstream_init(struct zstream *z, const struct zstream_funcs *func)
{
z->flags = 0;
z->buf = Qnil;
- z->buf_filled = 0;
z->input = Qnil;
z->stream.zalloc = zlib_mem_alloc;
z->stream.zfree = zlib_mem_free;
@@ -624,11 +624,11 @@ zstream_expand_buffer(struct zstream *z)
}
if (!ZSTREAM_IS_GZFILE(z) && rb_block_given_p()) {
- if (z->buf_filled >= ZSTREAM_AVAIL_OUT_STEP_MAX) {
+ long buf_filled = ZSTREAM_BUF_FILLED(z);
+ if (buf_filled >= ZSTREAM_AVAIL_OUT_STEP_MAX) {
int state = 0;
VALUE self = (VALUE)z->stream.opaque;
- rb_str_resize(z->buf, z->buf_filled);
rb_obj_reveal(z->buf, rb_cString);
OBJ_INFECT(z->buf, self);
@@ -644,23 +644,11 @@ zstream_expand_buffer(struct zstream *z)
}
else {
zstream_expand_buffer_into(z,
- ZSTREAM_AVAIL_OUT_STEP_MAX - z->buf_filled);
+ ZSTREAM_AVAIL_OUT_STEP_MAX - buf_filled);
}
}
else {
- if (RSTRING_LEN(z->buf) - z->buf_filled >= ZSTREAM_AVAIL_OUT_STEP_MAX) {
- z->stream.avail_out = ZSTREAM_AVAIL_OUT_STEP_MAX;
- }
- else {
- long inc = z->buf_filled / 2;
- if (inc < ZSTREAM_AVAIL_OUT_STEP_MIN) {
- inc = ZSTREAM_AVAIL_OUT_STEP_MIN;
- }
- rb_str_resize(z->buf, z->buf_filled + inc);
- z->stream.avail_out = (inc < ZSTREAM_AVAIL_OUT_STEP_MAX) ?
- (int)inc : ZSTREAM_AVAIL_OUT_STEP_MAX;
- }
- z->stream.next_out = (Bytef*)RSTRING_PTR(z->buf) + z->buf_filled;
+ zstream_expand_buffer_non_stream(z);
}
}
@@ -670,15 +658,14 @@ zstream_expand_buffer_into(struct zstream *z, unsigned long size)
if (NIL_P(z->buf)) {
/* I uses rb_str_new here not rb_str_buf_new because
rb_str_buf_new makes a zero-length string. */
- z->buf = rb_str_new(0, size);
- z->buf_filled = 0;
+ z->buf = rb_str_buf_new(size);
z->stream.next_out = (Bytef*)RSTRING_PTR(z->buf);
z->stream.avail_out = MAX_UINT(size);
rb_obj_hide(z->buf);
}
else if (z->stream.avail_out != size) {
- rb_str_resize(z->buf, z->buf_filled + size);
- z->stream.next_out = (Bytef*)RSTRING_PTR(z->buf) + z->buf_filled;
+ rb_str_modify_expand(z->buf, size);
+ z->stream.next_out = (Bytef*)RSTRING_END(z->buf);
z->stream.avail_out = MAX_UINT(size);
}
}
@@ -695,34 +682,24 @@ zstream_expand_buffer_protect(void *ptr)
}
static int
-zstream_expand_buffer_without_gvl(struct zstream *z)
+zstream_expand_buffer_non_stream(struct zstream *z)
{
- char * new_str;
- long inc, len;
+ long inc, len = ZSTREAM_BUF_FILLED(z);
- if (RSTRING_LEN(z->buf) - z->buf_filled >= ZSTREAM_AVAIL_OUT_STEP_MAX) {
+ if (rb_str_capacity(z->buf) - len >= ZSTREAM_AVAIL_OUT_STEP_MAX) {
z->stream.avail_out = ZSTREAM_AVAIL_OUT_STEP_MAX;
}
else {
- inc = z->buf_filled / 2;
+ inc = len / 2;
if (inc < ZSTREAM_AVAIL_OUT_STEP_MIN) {
inc = ZSTREAM_AVAIL_OUT_STEP_MIN;
}
- len = z->buf_filled + inc;
-
- new_str = ruby_xrealloc(RSTRING(z->buf)->as.heap.ptr, len + 1);
-
- /* from rb_str_resize */
- RSTRING(z->buf)->as.heap.ptr = new_str;
- RSTRING(z->buf)->as.heap.ptr[len] = '\0'; /* sentinel */
- RSTRING(z->buf)->as.heap.len =
- RSTRING(z->buf)->as.heap.aux.capa = len;
-
+ rb_str_modify_expand(z->buf, inc);
z->stream.avail_out = (inc < ZSTREAM_AVAIL_OUT_STEP_MAX) ?
(int)inc : ZSTREAM_AVAIL_OUT_STEP_MAX;
}
- z->stream.next_out = (Bytef*)RSTRING_PTR(z->buf) + z->buf_filled;
+ z->stream.next_out = (Bytef*)RSTRING_END(z->buf);
return ZSTREAM_EXPAND_BUFFER_OK;
}
@@ -733,15 +710,14 @@ zstream_append_buffer(struct zstream *z, const Bytef *src, long len)
if (NIL_P(z->buf)) {
z->buf = rb_str_buf_new(len);
rb_str_buf_cat(z->buf, (const char*)src, len);
- z->buf_filled = len;
z->stream.next_out = (Bytef*)RSTRING_PTR(z->buf);
z->stream.avail_out = 0;
rb_obj_hide(z->buf);
return;
}
- if (RSTRING_LEN(z->buf) < z->buf_filled + len) {
- rb_str_resize(z->buf, z->buf_filled + len);
+ if ((long)rb_str_capacity(z->buf) < ZSTREAM_BUF_FILLED(z) + len) {
+ rb_str_modify_expand(z->buf, len);
z->stream.avail_out = 0;
}
else {
@@ -752,9 +728,8 @@ zstream_append_buffer(struct zstream *z, const Bytef *src, long len)
z->stream.avail_out = 0;
}
}
- memcpy(RSTRING_PTR(z->buf) + z->buf_filled, src, len);
- z->buf_filled += len;
- z->stream.next_out = (Bytef*)RSTRING_PTR(z->buf) + z->buf_filled;
+ rb_str_cat(z->buf, (const char *)src, len);
+ z->stream.next_out = (Bytef*)RSTRING_END(z->buf);
}
#define zstream_append_buffer2(z,v) \
@@ -777,14 +752,12 @@ zstream_detach_buffer(struct zstream *z)
}
else {
dst = z->buf;
- rb_str_resize(dst, z->buf_filled);
rb_obj_reveal(dst, rb_cString);
}
OBJ_INFECT(dst, self);
z->buf = Qnil;
- z->buf_filled = 0;
z->stream.next_out = 0;
z->stream.avail_out = 0;
@@ -800,18 +773,20 @@ static VALUE
zstream_shift_buffer(struct zstream *z, long len)
{
VALUE dst;
- long buflen;
+ char *bufptr;
+ long buflen = ZSTREAM_BUF_FILLED(z);
- if (z->buf_filled <= len) {
+ if (buflen <= len) {
return zstream_detach_buffer(z);
}
- dst = rb_str_new(RSTRING_PTR(z->buf), len);
- z->buf_filled -= len;
- memmove(RSTRING_PTR(z->buf), RSTRING_PTR(z->buf) + len,
- z->buf_filled);
- z->stream.next_out = (Bytef*)RSTRING_PTR(z->buf) + z->buf_filled;
- buflen = RSTRING_LEN(z->buf) - z->buf_filled;
+ bufptr = RSTRING_PTR(z->buf);
+ dst = rb_str_new(bufptr, len);
+ buflen -= len;
+ memmove(bufptr, bufptr + len, buflen);
+ rb_str_set_len(z->buf, buflen);
+ z->stream.next_out = (Bytef*)RSTRING_END(z->buf);
+ buflen = (long)rb_str_capacity(z->buf) - ZSTREAM_BUF_FILLED(z);
if (buflen > ZSTREAM_AVAIL_OUT_STEP_MAX) {
buflen = ZSTREAM_AVAIL_OUT_STEP_MAX;
}
@@ -823,13 +798,17 @@ zstream_shift_buffer(struct zstream *z, long len)
static void
zstream_buffer_ungets(struct zstream *z, const Bytef *b, unsigned long len)
{
- if (NIL_P(z->buf) || RSTRING_LEN(z->buf) - z->buf_filled == 0) {
+ char *bufptr;
+ long filled;
+
+ if (NIL_P(z->buf) || (long)rb_str_capacity(z->buf) <= ZSTREAM_BUF_FILLED(z)) {
zstream_expand_buffer_into(z, len);
}
- memmove(RSTRING_PTR(z->buf) + len, RSTRING_PTR(z->buf), z->buf_filled);
- memmove(RSTRING_PTR(z->buf), b, len);
- z->buf_filled+=len;
+ RSTRING_GETMEM(z->buf, bufptr, filled);
+ memmove(bufptr + len, bufptr, filled);
+ memmove(bufptr, b, len);
+ rb_str_set_len(z->buf, filled + len);
if (z->stream.avail_out > 0) {
if (len > z->stream.avail_out) len = z->stream.avail_out;
z->stream.next_out+=len;
@@ -840,17 +819,8 @@ zstream_buffer_ungets(struct zstream *z, const Bytef *b, unsigned long len)
static void
zstream_buffer_ungetbyte(struct zstream *z, int c)
{
- if (NIL_P(z->buf) || RSTRING_LEN(z->buf) - z->buf_filled == 0) {
- zstream_expand_buffer(z);
- }
-
- memmove(RSTRING_PTR(z->buf) + 1, RSTRING_PTR(z->buf), z->buf_filled);
- RSTRING_PTR(z->buf)[0] = (char)c;
- z->buf_filled++;
- if (z->stream.avail_out > 0) {
- z->stream.next_out++;
- z->stream.avail_out--;
- }
+ Bytef cc = (Bytef)c;
+ zstream_buffer_ungets(z, &cc, 1);
}
static void
@@ -875,20 +845,50 @@ zstream_append_input(struct zstream *z, const Bytef *src, long len)
static void
zstream_discard_input(struct zstream *z, long len)
{
- if (NIL_P(z->input) || RSTRING_LEN(z->input) <= len) {
- z->input = Qnil;
+ if (NIL_P(z->input)) {
}
- else {
- memmove(RSTRING_PTR(z->input), RSTRING_PTR(z->input) + len,
- RSTRING_LEN(z->input) - len);
- rb_str_resize(z->input, RSTRING_LEN(z->input) - len);
+ else if (RBASIC_CLASS(z->input) == 0) {
+ /* hidden, we created z->input and have complete control */
+ char *ptr;
+ long oldlen, newlen;
+
+ RSTRING_GETMEM(z->input, ptr, oldlen);
+ newlen = oldlen - len;
+ if (newlen > 0) {
+ memmove(ptr, ptr + len, newlen);
+ }
+ if (newlen < 0) {
+ newlen = 0;
+ }
+ rb_str_resize(z->input, newlen);
+ if (newlen == 0) {
+ rb_gc_force_recycle(z->input);
+ z->input = Qnil;
+ }
+ else {
+ rb_str_set_len(z->input, newlen);
+ }
+ }
+ else { /* do not mangle user-provided data */
+ if (RSTRING_LEN(z->input) <= len) {
+ z->input = Qnil;
+ }
+ else {
+ z->input = rb_str_substr(z->input, len,
+ RSTRING_LEN(z->input) - len);
+ }
}
}
static void
zstream_reset_input(struct zstream *z)
{
- z->input = Qnil;
+ if (!NIL_P(z->input) && RBASIC_CLASS(z->input) == 0) {
+ rb_str_resize(z->input, 0);
+ }
+ else {
+ z->input = Qnil;
+ }
}
static void
@@ -913,7 +913,6 @@ zstream_detach_input(struct zstream *z)
rb_obj_reveal(dst, rb_cString);
}
z->input = Qnil;
- rb_obj_reveal(dst, rb_cString);
return dst;
}
@@ -928,7 +927,6 @@ zstream_reset(struct zstream *z)
}
z->flags = ZSTREAM_FLAG_READY;
z->buf = Qnil;
- z->buf_filled = 0;
z->stream.next_out = 0;
z->stream.avail_out = 0;
zstream_reset_input(z);
@@ -969,7 +967,7 @@ zstream_run_func(void *ptr)
while (!args->interrupt) {
n = z->stream.avail_out;
err = z->func->run(&z->stream, flush);
- z->buf_filled += n - z->stream.avail_out;
+ rb_str_set_len(z->buf, ZSTREAM_BUF_FILLED(z) + (n - z->stream.avail_out));
if (err == Z_STREAM_END) {
z->flags &= ~ZSTREAM_FLAG_IN_STREAM;
@@ -998,7 +996,7 @@ zstream_run_func(void *ptr)
(void *)z);
}
else {
- state = zstream_expand_buffer_without_gvl(z);
+ state = zstream_expand_buffer_non_stream(z);
}
if (state) {
@@ -1027,7 +1025,7 @@ zstream_run(struct zstream *z, Bytef *src, long len, int flush)
{
struct zstream_run_args args;
int err;
- VALUE guard = Qnil;
+ VALUE old_input = Qnil;
args.z = z;
args.flush = flush;
@@ -1041,12 +1039,13 @@ zstream_run(struct zstream *z, Bytef *src, long len, int flush)
}
else {
zstream_append_input(z, src, len);
- z->stream.next_in = (Bytef*)RSTRING_PTR(z->input);
- z->stream.avail_in = MAX_UINT(RSTRING_LEN(z->input));
/* keep reference to `z->input' so as not to be garbage collected
after zstream_reset_input() and prevent `z->stream.next_in'
from dangling. */
- guard = z->input;
+ old_input = zstream_detach_input(z);
+ rb_obj_hide(old_input); /* for GVL release and later recycle */
+ z->stream.next_in = (Bytef*)RSTRING_PTR(old_input);
+ z->stream.avail_in = MAX_UINT(RSTRING_LEN(old_input));
}
if (z->stream.avail_out == 0) {
@@ -1084,7 +1083,10 @@ loop:
if (z->stream.avail_in > 0) {
zstream_append_input(z, z->stream.next_in, z->stream.avail_in);
- RB_GC_GUARD(guard); /* prevent tail call to make guard effective */
+ }
+ if (!NIL_P(old_input)) {
+ rb_str_resize(old_input, 0);
+ rb_gc_force_recycle(old_input);
}
if (args.jump_state)
@@ -1413,7 +1415,7 @@ rb_zstream_data_type(VALUE obj)
static VALUE
rb_zstream_adler(VALUE obj)
{
- return rb_uint2inum(get_zstream(obj)->stream.adler);
+ return rb_uint2inum(get_zstream(obj)->stream.adler);
}
/*
@@ -1578,7 +1580,6 @@ rb_deflate_init_copy(VALUE self, VALUE orig)
}
z1->input = NIL_P(z2->input) ? Qnil : rb_str_dup(z2->input);
z1->buf = NIL_P(z2->buf) ? Qnil : rb_str_dup(z2->buf);
- z1->buf_filled = z2->buf_filled;
z1->flags = z2->flags;
return self;
@@ -1762,23 +1763,26 @@ rb_deflate_params(VALUE obj, VALUE v_level, VALUE v_strategy)
int level, strategy;
int err;
uInt n;
+ long filled;
level = ARG_LEVEL(v_level);
strategy = ARG_STRATEGY(v_strategy);
n = z->stream.avail_out;
err = deflateParams(&z->stream, level, strategy);
- z->buf_filled += n - z->stream.avail_out;
+ filled = n - z->stream.avail_out;
while (err == Z_BUF_ERROR) {
rb_warning("deflateParams() returned Z_BUF_ERROR");
zstream_expand_buffer(z);
+ rb_str_set_len(z->buf, RSTRING_LEN(z->buf) + filled);
n = z->stream.avail_out;
err = deflateParams(&z->stream, level, strategy);
- z->buf_filled += n - z->stream.avail_out;
+ filled = n - z->stream.avail_out;
}
if (err != Z_OK) {
raise_zlib_error(err, z->stream.msg);
}
+ rb_str_set_len(z->buf, RSTRING_LEN(z->buf) + filled);
return Qnil;
}
@@ -2221,7 +2225,6 @@ struct gzfile {
rb_encoding *enc2;
rb_econv_t *ec;
VALUE ecopts;
- char *cbuf;
VALUE path;
};
#define GZFILE_CBUF_CAPA 10
@@ -2231,10 +2234,20 @@ struct gzfile {
#define GZFILE_FLAG_FOOTER_FINISHED (ZSTREAM_FLAG_UNUSED << 2)
#define GZFILE_IS_FINISHED(gz) \
- (ZSTREAM_IS_FINISHED(&(gz)->z) && (gz)->z.buf_filled == 0)
+ (ZSTREAM_IS_FINISHED(&(gz)->z) && ZSTREAM_BUF_FILLED(&(gz)->z) == 0)
#define GZFILE_READ_SIZE 2048
+struct read_raw_arg {
+ VALUE io;
+ union {
+ const VALUE argv[2]; /* for rb_funcallv */
+ struct {
+ VALUE len;
+ VALUE buf;
+ } in;
+ } as;
+};
static void
gzfile_mark(void *p)
@@ -2261,22 +2274,13 @@ gzfile_free(void *p)
}
zstream_finalize(z);
}
- if (gz->cbuf) {
- xfree(gz->cbuf);
- }
xfree(gz);
}
static size_t
gzfile_memsize(const void *p)
{
- const struct gzfile *gz = p;
- size_t size = sizeof(struct gzfile);
-
- if (gz->cbuf)
- size += GZFILE_CBUF_CAPA;
-
- return size;
+ return sizeof(struct gzfile);
}
static const rb_data_type_t gzfile_data_type = {
@@ -2285,16 +2289,9 @@ static const rb_data_type_t gzfile_data_type = {
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
-static VALUE
-gzfile_new(klass, funcs, endfunc)
- VALUE klass;
- const struct zstream_funcs *funcs;
- void (*endfunc)(struct gzfile *);
+static void
+gzfile_init(struct gzfile *gz, const struct zstream_funcs *funcs, void (*endfunc)(struct gzfile *))
{
- VALUE obj;
- struct gzfile *gz;
-
- obj = TypedData_Make_Struct(klass, struct gzfile, &gzfile_data_type, gz);
zstream_init(&gz->z, funcs);
gz->z.flags |= ZSTREAM_FLAG_GZFILE;
gz->io = Qnil;
@@ -2312,9 +2309,17 @@ gzfile_new(klass, funcs, endfunc)
gz->ec = NULL;
gz->ecflags = 0;
gz->ecopts = Qnil;
- gz->cbuf = 0;
gz->path = Qnil;
+}
+
+static VALUE
+gzfile_new(VALUE klass, const struct zstream_funcs *funcs, void (*endfunc)(struct gzfile *))
+{
+ VALUE obj;
+ struct gzfile *gz;
+ obj = TypedData_Make_Struct(klass, struct gzfile, &gzfile_data_type, gz);
+ gzfile_init(gz, funcs, endfunc);
return obj;
}
@@ -2355,7 +2360,7 @@ gzfile_write_raw(struct gzfile *gz)
{
VALUE str;
- if (gz->z.buf_filled > 0) {
+ if (ZSTREAM_BUF_FILLED(&gz->z) > 0) {
str = zstream_detach_buffer(&gz->z);
OBJ_TAINT(str); /* for safe */
rb_funcall(gz->io, id_write, 1, str);
@@ -2368,10 +2373,11 @@ gzfile_write_raw(struct gzfile *gz)
static VALUE
gzfile_read_raw_partial(VALUE arg)
{
- struct gzfile *gz = (struct gzfile*)arg;
+ struct read_raw_arg *ra = (struct read_raw_arg *)arg;
VALUE str;
+ int argc = NIL_P(ra->as.argv[1]) ? 1 : 2;
- str = rb_funcall(gz->io, id_readpartial, 1, INT2FIX(GZFILE_READ_SIZE));
+ str = rb_funcallv(ra->io, id_readpartial, argc, ra->as.argv);
Check_Type(str, T_STRING);
return str;
}
@@ -2379,10 +2385,11 @@ gzfile_read_raw_partial(VALUE arg)
static VALUE
gzfile_read_raw_rescue(VALUE arg)
{
- struct gzfile *gz = (struct gzfile*)arg;
+ struct read_raw_arg *ra = (struct read_raw_arg *)arg;
VALUE str = Qnil;
if (rb_obj_is_kind_of(rb_errinfo(), rb_eNoMethodError)) {
- str = rb_funcall(gz->io, id_read, 1, INT2FIX(GZFILE_READ_SIZE));
+ int argc = NIL_P(ra->as.argv[1]) ? 1 : 2;
+ str = rb_funcallv(ra->io, id_read, argc, ra->as.argv);
if (!NIL_P(str)) {
Check_Type(str, T_STRING);
}
@@ -2391,20 +2398,30 @@ gzfile_read_raw_rescue(VALUE arg)
}
static VALUE
-gzfile_read_raw(struct gzfile *gz)
+gzfile_read_raw(struct gzfile *gz, VALUE outbuf)
{
- return rb_rescue2(gzfile_read_raw_partial, (VALUE)gz,
- gzfile_read_raw_rescue, (VALUE)gz,
+ struct read_raw_arg ra;
+
+ ra.io = gz->io;
+ ra.as.in.len = INT2FIX(GZFILE_READ_SIZE);
+ ra.as.in.buf = outbuf;
+
+ return rb_rescue2(gzfile_read_raw_partial, (VALUE)&ra,
+ gzfile_read_raw_rescue, (VALUE)&ra,
rb_eEOFError, rb_eNoMethodError, (VALUE)0);
}
static int
-gzfile_read_raw_ensure(struct gzfile *gz, long size)
+gzfile_read_raw_ensure(struct gzfile *gz, long size, VALUE outbuf)
{
VALUE str;
+ if (gz->io == Qundef) { /* Zlib.gunzip */
+ if (NIL_P(gz->z.input) || RSTRING_LEN(gz->z.input) < size)
+ rb_raise(cGzError, "unexpected end of string");
+ }
while (NIL_P(gz->z.input) || RSTRING_LEN(gz->z.input) < size) {
- str = gzfile_read_raw(gz);
+ str = gzfile_read_raw(gz, outbuf);
if (NIL_P(str)) return 0;
zstream_append_input2(&gz->z, str);
}
@@ -2421,7 +2438,7 @@ gzfile_read_raw_until_zero(struct gzfile *gz, long offset)
p = memchr(RSTRING_PTR(gz->z.input) + offset, '\0',
RSTRING_LEN(gz->z.input) - offset);
if (p) break;
- str = gzfile_read_raw(gz);
+ str = gzfile_read_raw(gz, Qnil);
if (NIL_P(str)) {
rb_raise(cGzError, "unexpected end of file");
}
@@ -2546,13 +2563,14 @@ gzfile_make_footer(struct gzfile *gz)
}
static void
-gzfile_read_header(struct gzfile *gz)
+gzfile_read_header(struct gzfile *gz, VALUE outbuf)
{
const unsigned char *head;
long len;
char flags, *p;
- if (!gzfile_read_raw_ensure(gz, 10)) { /* 10 is the size of gzip header */
+ /* 10 is the size of gzip header */
+ if (!gzfile_read_raw_ensure(gz, 10, outbuf)) {
gzfile_raise(gz, cGzError, "not in gzip format");
}
@@ -2591,17 +2609,17 @@ gzfile_read_header(struct gzfile *gz)
zstream_discard_input(&gz->z, 10);
if (flags & GZ_FLAG_EXTRA) {
- if (!gzfile_read_raw_ensure(gz, 2)) {
+ if (!gzfile_read_raw_ensure(gz, 2, outbuf)) {
rb_raise(cGzError, "unexpected end of file");
}
len = gzfile_get16((Bytef*)RSTRING_PTR(gz->z.input));
- if (!gzfile_read_raw_ensure(gz, 2 + len)) {
+ if (!gzfile_read_raw_ensure(gz, 2 + len, outbuf)) {
rb_raise(cGzError, "unexpected end of file");
}
zstream_discard_input(&gz->z, 2 + len);
}
if (flags & GZ_FLAG_ORIG_NAME) {
- if (!gzfile_read_raw_ensure(gz, 1)) {
+ if (!gzfile_read_raw_ensure(gz, 1, outbuf)) {
rb_raise(cGzError, "unexpected end of file");
}
p = gzfile_read_raw_until_zero(gz, 0);
@@ -2611,7 +2629,7 @@ gzfile_read_header(struct gzfile *gz)
zstream_discard_input(&gz->z, len + 1);
}
if (flags & GZ_FLAG_COMMENT) {
- if (!gzfile_read_raw_ensure(gz, 1)) {
+ if (!gzfile_read_raw_ensure(gz, 1, outbuf)) {
rb_raise(cGzError, "unexpected end of file");
}
p = gzfile_read_raw_until_zero(gz, 0);
@@ -2627,13 +2645,14 @@ gzfile_read_header(struct gzfile *gz)
}
static void
-gzfile_check_footer(struct gzfile *gz)
+gzfile_check_footer(struct gzfile *gz, VALUE outbuf)
{
unsigned long crc, length;
gz->z.flags |= GZFILE_FLAG_FOOTER_FINISHED;
- if (!gzfile_read_raw_ensure(gz, 8)) { /* 8 is the size of gzip footer */
+ /* 8 is the size of gzip footer */
+ if (!gzfile_read_raw_ensure(gz, 8, outbuf)) {
gzfile_raise(gz, cNoFooter, "footer is not found");
}
@@ -2667,12 +2686,12 @@ gzfile_write(struct gzfile *gz, Bytef *str, long len)
}
static long
-gzfile_read_more(struct gzfile *gz)
+gzfile_read_more(struct gzfile *gz, VALUE outbuf)
{
VALUE str;
while (!ZSTREAM_IS_FINISHED(&gz->z)) {
- str = gzfile_read_raw(gz);
+ str = gzfile_read_raw(gz, outbuf);
if (NIL_P(str)) {
if (!ZSTREAM_IS_FINISHED(&gz->z)) {
rb_raise(cGzError, "unexpected end of file");
@@ -2684,9 +2703,9 @@ gzfile_read_more(struct gzfile *gz)
Z_SYNC_FLUSH);
RB_GC_GUARD(str);
}
- if (gz->z.buf_filled > 0) break;
+ if (ZSTREAM_BUF_FILLED(&gz->z) > 0) break;
}
- return gz->z.buf_filled;
+ return ZSTREAM_BUF_FILLED(&gz->z);
}
static void
@@ -2697,7 +2716,7 @@ gzfile_calc_crc(struct gzfile *gz, VALUE str)
}
else {
gz->crc = checksum_long(crc32, gz->crc, (Bytef*)RSTRING_PTR(str) + gz->ungetc,
- RSTRING_LEN(str) - gz->ungetc);
+ RSTRING_LEN(str) - gz->ungetc);
gz->ungetc = 0;
}
}
@@ -2727,16 +2746,16 @@ gzfile_fill(struct gzfile *gz, long len)
rb_raise(rb_eArgError, "negative length %ld given", len);
if (len == 0)
return 0;
- while (!ZSTREAM_IS_FINISHED(&gz->z) && gz->z.buf_filled < len) {
- gzfile_read_more(gz);
+ while (!ZSTREAM_IS_FINISHED(&gz->z) && ZSTREAM_BUF_FILLED(&gz->z) < len) {
+ gzfile_read_more(gz, Qnil);
}
if (GZFILE_IS_FINISHED(gz)) {
if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) {
- gzfile_check_footer(gz);
+ gzfile_check_footer(gz, Qnil);
}
return -1;
}
- return len < gz->z.buf_filled ? len : gz->z.buf_filled;
+ return len < ZSTREAM_BUF_FILLED(&gz->z) ? len : ZSTREAM_BUF_FILLED(&gz->z);
}
static VALUE
@@ -2771,12 +2790,12 @@ gzfile_readpartial(struct gzfile *gz, long len, VALUE outbuf)
return outbuf;
}
}
- while (!ZSTREAM_IS_FINISHED(&gz->z) && gz->z.buf_filled == 0) {
- gzfile_read_more(gz);
+ while (!ZSTREAM_IS_FINISHED(&gz->z) && ZSTREAM_BUF_FILLED(&gz->z) == 0) {
+ gzfile_read_more(gz, outbuf);
}
if (GZFILE_IS_FINISHED(gz)) {
if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) {
- gzfile_check_footer(gz);
+ gzfile_check_footer(gz, outbuf);
}
if (!NIL_P(outbuf))
rb_str_resize(outbuf, 0);
@@ -2789,7 +2808,8 @@ gzfile_readpartial(struct gzfile *gz, long len, VALUE outbuf)
if (!NIL_P(outbuf)) {
rb_str_resize(outbuf, RSTRING_LEN(dst));
memcpy(RSTRING_PTR(outbuf), RSTRING_PTR(dst), RSTRING_LEN(dst));
- RB_GC_GUARD(dst);
+ rb_str_resize(dst, 0);
+ rb_gc_force_recycle(dst);
dst = outbuf;
}
OBJ_TAINT(dst); /* for safe */
@@ -2802,11 +2822,11 @@ gzfile_read_all(struct gzfile *gz)
VALUE dst;
while (!ZSTREAM_IS_FINISHED(&gz->z)) {
- gzfile_read_more(gz);
+ gzfile_read_more(gz, Qnil);
}
if (GZFILE_IS_FINISHED(gz)) {
if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) {
- gzfile_check_footer(gz);
+ gzfile_check_footer(gz, Qnil);
}
return rb_str_new(0, 0);
}
@@ -2825,12 +2845,12 @@ gzfile_getc(struct gzfile *gz)
int len;
len = rb_enc_mbmaxlen(gz->enc);
- while (!ZSTREAM_IS_FINISHED(&gz->z) && gz->z.buf_filled < len) {
- gzfile_read_more(gz);
+ while (!ZSTREAM_IS_FINISHED(&gz->z) && ZSTREAM_BUF_FILLED(&gz->z) < len) {
+ gzfile_read_more(gz, Qnil);
}
if (GZFILE_IS_FINISHED(gz)) {
if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) {
- gzfile_check_footer(gz);
+ gzfile_check_footer(gz, Qnil);
}
return Qnil;
}
@@ -2838,22 +2858,19 @@ gzfile_getc(struct gzfile *gz)
if (gz->ec && rb_enc_dummy_p(gz->enc2)) {
const unsigned char *ss, *sp, *se;
unsigned char *ds, *dp, *de;
+ VALUE cbuf = rb_enc_str_new(0, GZFILE_CBUF_CAPA, gz->enc);
- if (!gz->cbuf) {
- gz->cbuf = ALLOC_N(char, GZFILE_CBUF_CAPA);
- }
ss = sp = (const unsigned char*)RSTRING_PTR(gz->z.buf);
- se = sp + gz->z.buf_filled;
- ds = dp = (unsigned char *)gz->cbuf;
+ se = sp + ZSTREAM_BUF_FILLED(&gz->z);
+ ds = dp = (unsigned char *)RSTRING_PTR(cbuf);
de = (unsigned char *)ds + GZFILE_CBUF_CAPA;
(void)rb_econv_convert(gz->ec, &sp, se, &dp, de, ECONV_PARTIAL_INPUT|ECONV_AFTER_OUTPUT);
rb_econv_check_error(gz->ec);
dst = zstream_shift_buffer(&gz->z, sp - ss);
gzfile_calc_crc(gz, dst);
- dst = rb_str_new(gz->cbuf, dp - ds);
- rb_enc_associate(dst, gz->enc);
- OBJ_TAINT(dst);
- return dst;
+ rb_str_resize(cbuf, dp - ds);
+ OBJ_TAINT(cbuf);
+ return cbuf;
}
else {
buf = gz->z.buf;
@@ -2910,7 +2927,7 @@ gzfile_reader_end_run(VALUE arg)
if (GZFILE_IS_FINISHED(gz)
&& !(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) {
- gzfile_check_footer(gz);
+ gzfile_check_footer(gz, Qnil);
}
return Qnil;
@@ -2947,7 +2964,7 @@ gzfile_reader_get_unused(struct gzfile *gz)
if (!ZSTREAM_IS_READY(&gz->z)) return Qnil;
if (!GZFILE_IS_FINISHED(gz)) return Qnil;
if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) {
- gzfile_check_footer(gz);
+ gzfile_check_footer(gz, Qnil);
}
if (NIL_P(gz->z.input)) return Qnil;
@@ -3417,7 +3434,14 @@ static VALUE
rb_gzfile_total_out(VALUE obj)
{
struct gzfile *gz = get_gzfile(obj);
- return rb_uint2inum(gz->z.stream.total_out - gz->z.buf_filled);
+ uLong total_out = gz->z.stream.total_out;
+ long buf_filled = ZSTREAM_BUF_FILLED(&gz->z);
+
+ if (total_out >= (uLong)buf_filled) {
+ return rb_uint2inum(total_out - buf_filled);
+ } else {
+ return LONG2FIX(-(buf_filled - (long)total_out));
+ }
}
/*
@@ -3583,18 +3607,23 @@ rb_gzwriter_flush(int argc, VALUE *argv, VALUE obj)
* Same as IO.
*/
static VALUE
-rb_gzwriter_write(VALUE obj, VALUE str)
+rb_gzwriter_write(int argc, VALUE *argv, VALUE obj)
{
struct gzfile *gz = get_gzfile(obj);
-
- if (!RB_TYPE_P(str, T_STRING))
- str = rb_obj_as_string(str);
- if (gz->enc2 && gz->enc2 != rb_ascii8bit_encoding()) {
- str = rb_str_conv_enc(str, rb_enc_get(str), gz->enc2);
+ size_t total = 0;
+
+ while (argc-- > 0) {
+ VALUE str = *argv++;
+ if (!RB_TYPE_P(str, T_STRING))
+ str = rb_obj_as_string(str);
+ if (gz->enc2 && gz->enc2 != rb_ascii8bit_encoding()) {
+ str = rb_str_conv_enc(str, rb_enc_get(str), gz->enc2);
+ }
+ gzfile_write(gz, (Bytef*)RSTRING_PTR(str), RSTRING_LEN(str));
+ total += RSTRING_LEN(str);
+ RB_GC_GUARD(str);
}
- gzfile_write(gz, (Bytef*)RSTRING_PTR(str), RSTRING_LEN(str));
- RB_GC_GUARD(str);
- return INT2FIX(RSTRING_LEN(str));
+ return SIZET2NUM(total);
}
/*
@@ -3743,7 +3772,7 @@ rb_gzreader_initialize(int argc, VALUE *argv, VALUE obj)
}
gz->io = io;
ZSTREAM_READY(&gz->z);
- gzfile_read_header(gz);
+ gzfile_read_header(gz, Qnil);
rb_gzfile_ecopts(gz, opt);
if (rb_respond_to(io, id_path)) {
@@ -3991,20 +4020,20 @@ gzreader_skip_linebreaks(struct gzfile *gz)
char *p;
int n;
- while (gz->z.buf_filled == 0) {
+ while (ZSTREAM_BUF_FILLED(&gz->z) == 0) {
if (GZFILE_IS_FINISHED(gz)) return;
- gzfile_read_more(gz);
+ gzfile_read_more(gz, Qnil);
}
n = 0;
p = RSTRING_PTR(gz->z.buf);
while (n++, *(p++) == '\n') {
- if (n >= gz->z.buf_filled) {
+ if (n >= ZSTREAM_BUF_FILLED(&gz->z)) {
str = zstream_detach_buffer(&gz->z);
gzfile_calc_crc(gz, str);
- while (gz->z.buf_filled == 0) {
+ while (ZSTREAM_BUF_FILLED(&gz->z) == 0) {
if (GZFILE_IS_FINISHED(gz)) return;
- gzfile_read_more(gz);
+ gzfile_read_more(gz, Qnil);
}
n = 0;
p = RSTRING_PTR(gz->z.buf);
@@ -4026,7 +4055,7 @@ static long
gzreader_charboundary(struct gzfile *gz, long n)
{
char *s = RSTRING_PTR(gz->z.buf);
- char *e = s + gz->z.buf_filled;
+ char *e = s + ZSTREAM_BUF_FILLED(&gz->z);
char *p = rb_enc_left_char_head(s, s + n, e, gz->enc);
long l = p - s;
if (l < n) {
@@ -4121,25 +4150,25 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj)
gzreader_skip_linebreaks(gz);
}
- while (gz->z.buf_filled < rslen) {
+ while (ZSTREAM_BUF_FILLED(&gz->z) < rslen) {
if (ZSTREAM_IS_FINISHED(&gz->z)) {
- if (gz->z.buf_filled > 0) gz->lineno++;
+ if (ZSTREAM_BUF_FILLED(&gz->z) > 0) gz->lineno++;
return gzfile_read(gz, rslen);
}
- gzfile_read_more(gz);
+ gzfile_read_more(gz, Qnil);
}
p = RSTRING_PTR(gz->z.buf);
n = rslen;
for (;;) {
long filled;
- if (n > gz->z.buf_filled) {
+ if (n > ZSTREAM_BUF_FILLED(&gz->z)) {
if (ZSTREAM_IS_FINISHED(&gz->z)) break;
- gzfile_read_more(gz);
+ gzfile_read_more(gz, Qnil);
p = RSTRING_PTR(gz->z.buf) + n - rslen;
}
if (!rspara) rscheck(rsptr, rslen, rs);
- filled = gz->z.buf_filled;
+ filled = ZSTREAM_BUF_FILLED(&gz->z);
if (limit > 0 && filled >= limit) {
filled = limit;
}
@@ -4156,7 +4185,7 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj)
p++, n++;
}
}
- if (maxlen > 1 && n == limit && (gz->z.buf_filled > n || !ZSTREAM_IS_FINISHED(&gz->z))) {
+ if (maxlen > 1 && n == limit && (ZSTREAM_BUF_FILLED(&gz->z) > n || !ZSTREAM_IS_FINISHED(&gz->z))) {
n = gzreader_charboundary(gz, n);
}
@@ -4262,11 +4291,178 @@ rb_gzreader_external_encoding(VALUE self)
return rb_enc_from_encoding(get_gzfile(self)->enc);
}
+static VALUE
+zlib_gzip_ensure(VALUE arg)
+{
+ struct gzfile *gz = (struct gzfile *)arg;
+ rb_rescue((VALUE(*)())gz->end, arg, NULL, Qnil);
+ return Qnil;
+}
+
+static void
+zlib_gzip_end(struct gzfile *gz)
+{
+ gz->z.flags |= ZSTREAM_FLAG_CLOSING;
+ zstream_run(&gz->z, (Bytef*)"", 0, Z_FINISH);
+ gzfile_make_footer(gz);
+ zstream_end(&gz->z);
+}
+
+#define OPTHASH_GIVEN_P(opts) \
+ (argc > 0 && !NIL_P((opts) = rb_check_hash_type(argv[argc-1])) && (--argc, 1))
+static ID id_level, id_strategy;
+static VALUE zlib_gzip_run(VALUE arg);
+
+/*
+ * call-seq:
+ * Zlib.gzip(src, level: nil, strategy: nil) -> String
+ *
+ * Gzip the given +string+. Valid values of level are
+ * Zlib::NO_COMPRESSION, Zlib::BEST_SPEED, Zlib::BEST_COMPRESSION,
+ * Zlib::DEFAULT_COMPRESSION (default), or an integer from 0 to 9.
+ *
+ * This method is almost equivalent to the following code:
+ *
+ * def gzip(string, level: nil, strategy: nil)
+ * sio = StringIO.new
+ * sio.binmode
+ * gz = Zlib::GzipWriter.new(sio, level, strategy)
+ * gz.write(string)
+ * gz.close
+ * sio.string
+ * end
+ *
+ * See also Zlib.gunzip
+ *
+ */
+static VALUE
+zlib_s_gzip(int argc, VALUE *argv, VALUE klass)
+{
+ struct gzfile gz0;
+ struct gzfile *gz = &gz0;
+ int err;
+ VALUE src, opts, level=Qnil, strategy=Qnil, args[2];
+
+ if (OPTHASH_GIVEN_P(opts)) {
+ ID keyword_ids[2];
+ VALUE kwargs[2];
+ keyword_ids[0] = id_level;
+ keyword_ids[1] = id_strategy;
+ rb_get_kwargs(opts, keyword_ids, 0, 2, kwargs);
+ if (kwargs[0] != Qundef) {
+ level = kwargs[0];
+ }
+ if (kwargs[1] != Qundef) {
+ strategy = kwargs[1];
+ }
+ }
+ rb_scan_args(argc, argv, "10", &src);
+ StringValue(src);
+ gzfile_init(gz, &deflate_funcs, zlib_gzip_end);
+ gz->level = ARG_LEVEL(level);
+ err = deflateInit2(&gz->z.stream, gz->level, Z_DEFLATED,
+ -MAX_WBITS, DEF_MEM_LEVEL, ARG_STRATEGY(strategy));
+ if (err != Z_OK) {
+ zlib_gzip_end(gz);
+ raise_zlib_error(err, gz->z.stream.msg);
+ }
+ ZSTREAM_READY(&gz->z);
+ args[0] = (VALUE)gz;
+ args[1] = src;
+ return rb_ensure(zlib_gzip_run, (VALUE)args, zlib_gzip_ensure, (VALUE)gz);
+}
+
+static VALUE
+zlib_gzip_run(VALUE arg)
+{
+ VALUE *args = (VALUE *)arg;
+ struct gzfile *gz = (struct gzfile *)args[0];
+ VALUE src = args[1];
+ long len;
+
+ gzfile_make_header(gz);
+ len = RSTRING_LEN(src);
+ if (len > 0) {
+ Bytef *ptr = (Bytef *)RSTRING_PTR(src);
+ gz->crc = checksum_long(crc32, gz->crc, ptr, len);
+ zstream_run(&gz->z, ptr, len, Z_NO_FLUSH);
+ }
+ gzfile_close(gz, 0);
+ return zstream_detach_buffer(&gz->z);
+}
+
+static void
+zlib_gunzip_end(struct gzfile *gz)
+{
+ gz->z.flags |= ZSTREAM_FLAG_CLOSING;
+ zstream_end(&gz->z);
+}
+
+static VALUE zlib_gunzip_run(VALUE arg);
+
+/*
+ * call-seq:
+ * Zlib.gunzip(src) -> String
+ *
+ * Decode the given gzipped +string+.
+ *
+ * This method is almost equivalent to the following code:
+ *
+ * def gunzip(string)
+ * sio = StringIO.new(string)
+ * gz = Zlib::GzipReader.new(sio, encoding: Encoding::ASCII_8BIT)
+ * gz.read
+ * ensure
+ * gz&.close
+ * end
+ *
+ * See also Zlib.gzip
+ */
+static VALUE
+zlib_gunzip(VALUE klass, VALUE src)
+{
+ struct gzfile gz0;
+ struct gzfile *gz = &gz0;
+ int err;
+
+ StringValue(src);
+
+ gzfile_init(gz, &inflate_funcs, zlib_gunzip_end);
+ err = inflateInit2(&gz->z.stream, -MAX_WBITS);
+ if (err != Z_OK) {
+ raise_zlib_error(err, gz->z.stream.msg);
+ }
+ gz->io = Qundef;
+ gz->z.input = src;
+ ZSTREAM_READY(&gz->z);
+ return rb_ensure(zlib_gunzip_run, (VALUE)gz, zlib_gzip_ensure, (VALUE)gz);
+}
+
+static VALUE
+zlib_gunzip_run(VALUE arg)
+{
+ struct gzfile *gz = (struct gzfile *)arg;
+ VALUE dst;
+
+ gzfile_read_header(gz, Qnil);
+ dst = zstream_detach_buffer(&gz->z);
+ gzfile_calc_crc(gz, dst);
+ if (!ZSTREAM_IS_FINISHED(&gz->z)) {
+ rb_raise(cGzError, "unexpected end of file");
+ }
+ if (NIL_P(gz->z.input)) {
+ rb_raise(cNoFooter, "footer is not found");
+ }
+ gzfile_check_footer(gz, Qnil);
+ return dst;
+}
+
#endif /* GZIP_SUPPORT */
void
Init_zlib(void)
{
+#undef rb_intern
VALUE mZlib, cZStream, cDeflate, cInflate;
#if GZIP_SUPPORT
VALUE cGzipFile, cGzipWriter, cGzipReader;
@@ -4501,7 +4697,7 @@ Init_zlib(void)
rb_define_alloc_func(cGzipWriter, rb_gzwriter_s_allocate);
rb_define_method(cGzipWriter, "initialize", rb_gzwriter_initialize,-1);
rb_define_method(cGzipWriter, "flush", rb_gzwriter_flush, -1);
- rb_define_method(cGzipWriter, "write", rb_gzwriter_write, 1);
+ rb_define_method(cGzipWriter, "write", rb_gzwriter_write, -1);
rb_define_method(cGzipWriter, "putc", rb_gzwriter_putc, 1);
rb_define_method(cGzipWriter, "<<", rb_gzwriter_addstr, 1);
rb_define_method(cGzipWriter, "printf", rb_gzwriter_printf, -1);
@@ -4532,6 +4728,9 @@ Init_zlib(void)
rb_define_method(cGzipReader, "readlines", rb_gzreader_readlines, -1);
rb_define_method(cGzipReader, "external_encoding", rb_gzreader_external_encoding, 0);
+ rb_define_singleton_method(mZlib, "gzip", zlib_s_gzip, -1);
+ rb_define_singleton_method(mZlib, "gunzip", zlib_gunzip, 1);
+
/* The OS code of current host */
rb_define_const(mZlib, "OS_CODE", INT2FIX(OS_CODE));
/* OS code for MSDOS hosts */
@@ -4565,6 +4764,8 @@ Init_zlib(void)
/* OS code for unknown hosts */
rb_define_const(mZlib, "OS_UNKNOWN", INT2FIX(OS_UNKNOWN));
+ id_level = rb_intern("level");
+ id_strategy = rb_intern("strategy");
#endif /* GZIP_SUPPORT */
}
diff --git a/ext/zlib/zlib.gemspec b/ext/zlib/zlib.gemspec
new file mode 100644
index 0000000000..f5b6443258
--- /dev/null
+++ b/ext/zlib/zlib.gemspec
@@ -0,0 +1,28 @@
+# coding: utf-8
+# frozen_string_literal: true
+source_version = File.open(File.join(__dir__, "zlib.c")) {|f|
+ f.gets("\n#define RUBY_ZLIB_VERSION ")
+ f.gets[/\s*(".+")/, 1].undump
+}
+Gem::Specification.new do |spec|
+ spec.name = "zlib"
+ spec.version = source_version
+ spec.authors = ["Yukihiro Matsumoto", "UENO Katsuhiro"]
+ spec.email = ["matz@ruby-lang.org", nil]
+
+ spec.summary = %q{Ruby interface for the zlib compression/decompression library}
+ spec.description = %q{Ruby interface for the zlib compression/decompression library}
+ spec.homepage = "https://github.com/ruby/zlib"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "ext/zlib/extconf.rb", "ext/zlib/zlib.c", "zlib.gemspec"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+ spec.extensions = "ext/zlib/extconf.rb"
+ spec.required_ruby_version = ">= 2.3.0"
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+ spec.add_development_dependency "rake-compiler"
+end
diff --git a/file.c b/file.c
index 170c251519..94ac327e74 100644
--- a/file.c
+++ b/file.c
@@ -23,9 +23,12 @@
#include <CoreFoundation/CFString.h>
#endif
-#include "internal.h"
+#include "id.h"
+#include "ruby/encoding.h"
#include "ruby/io.h"
#include "ruby/util.h"
+#include "ruby/thread.h"
+#include "internal.h"
#include "dln.h"
#include "encindex.h"
@@ -70,16 +73,6 @@ int flock(int, int);
#include <sys/types.h>
#include <sys/stat.h>
-#if defined(__native_client__)
-# if defined(NACL_NEWLIB)
-# include "nacl/utime.h"
-# include "nacl/stat.h"
-# include "nacl/unistd.h"
-# else
-# undef HAVE_UTIMENSAT
-# endif
-#endif
-
#ifdef HAVE_SYS_MKDEV_H
#include <sys/mkdev.h>
#endif
@@ -99,9 +92,9 @@ int flock(int, int);
/* define system APIs */
#ifdef _WIN32
#include "win32/file.h"
-#define STAT(p, s) rb_w32_ustati64((p), (s))
+#define STAT(p, s) rb_w32_ustati128((p), (s))
#undef lstat
-#define lstat(p, s) rb_w32_ulstati64((p), (s))
+#define lstat(p, s) rb_w32_ulstati128((p), (s))
#undef access
#define access(p, m) rb_w32_uaccess((p), (m))
#undef truncate
@@ -112,8 +105,8 @@ int flock(int, int);
#define chown(p, o, g) rb_w32_uchown((p), (o), (g))
#undef lchown
#define lchown(p, o, g) rb_w32_ulchown((p), (o), (g))
-#undef utime
-#define utime(p, t) rb_w32_uutime((p), (t))
+#undef utimensat
+#define utimensat(s, p, t, f) rb_w32_uutimensat((s), (p), (t), (f))
#undef link
#define link(f, t) rb_w32_ulink((f), (t))
#undef unlink
@@ -126,6 +119,19 @@ int flock(int, int);
#define STAT(p, s) stat((p), (s))
#endif
+#if defined _WIN32 || defined __APPLE__
+# define USE_OSPATH 1
+# define TO_OSPATH(str) rb_str_encode_ospath(str)
+#else
+# define USE_OSPATH 0
+# define TO_OSPATH(str) (str)
+#endif
+
+/* utime may fail if time is out-of-range for the FS [ruby-dev:38277] */
+#if defined DOSISH || defined __CYGWIN__
+# define UTIME_EINVAL
+#endif
+
VALUE rb_cFile;
VALUE rb_mFileTest;
VALUE rb_cStat;
@@ -178,10 +184,7 @@ rb_get_path_check_to_string(VALUE obj, int level)
return obj;
}
CONST_ID(to_path, "to_path");
- tmp = rb_check_funcall(obj, to_path, 0, 0);
- if (tmp == Qundef) {
- tmp = obj;
- }
+ tmp = rb_check_funcall_default(obj, to_path, 0, 0, obj);
StringValue(tmp);
return tmp;
}
@@ -195,12 +198,14 @@ rb_get_path_check_convert(VALUE obj, VALUE tmp, int level)
}
check_path_encoding(tmp);
- StringValueCStr(tmp);
+ if (!rb_str_to_cstr(tmp)) {
+ rb_raise(rb_eArgError, "path name contains null byte");
+ }
return rb_str_new4(tmp);
}
-static VALUE
+VALUE
rb_get_path_check(VALUE obj, int level)
{
VALUE tmp = rb_get_path_check_to_string(obj, level);
@@ -222,7 +227,7 @@ rb_get_path(VALUE obj)
VALUE
rb_str_encode_ospath(VALUE path)
{
-#if defined _WIN32 || defined __APPLE__
+#if USE_OSPATH
int encidx = ENCODING_GET(path);
#ifdef _WIN32
if (encidx == ENCINDEX_ASCII) {
@@ -345,20 +350,72 @@ ignored_char_p(const char *p, const char *e, rb_encoding *enc)
#define apply2args(n) (rb_check_arity(argc, n, UNLIMITED_ARGUMENTS), argc-=n)
+struct apply_filename {
+ const char *ptr;
+ VALUE path;
+};
+
+struct apply_arg {
+ int i;
+ int argc;
+ int errnum;
+ int (*func)(const char *, void *);
+ void *arg;
+ struct apply_filename fn[FLEX_ARY_LEN];
+};
+
+static void *
+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;
+ }
+ }
+ return 0;
+}
+
+#ifdef UTIME_EINVAL
+NORETURN(static void utime_failed(struct apply_arg *));
+static int utime_internal(const char *, void *);
+#endif
+
static VALUE
-apply2files(void (*func)(const char *, VALUE, void *), int argc, VALUE *argv, void *arg)
+apply2files(int (*func)(const char *, void *), int argc, VALUE *argv, void *arg)
{
- long i;
- volatile VALUE path;
+ VALUE v;
+ const size_t size = sizeof(struct apply_filename);
+ const long len = (long)(offsetof(struct apply_arg, fn) + (size * argc));
+ struct apply_arg *aa = ALLOCV(v, len);
+
+ aa->errnum = 0;
+ aa->argc = argc;
+ aa->arg = arg;
+ aa->func = func;
+
+ for (aa->i = 0; aa->i < argc; aa->i++) {
+ VALUE path = rb_get_path(argv[aa->i]);
- for (i=0; i<argc; i++) {
- const char *s;
- path = rb_get_path(argv[i]);
path = rb_str_encode_ospath(path);
- s = RSTRING_PTR(path);
- (*func)(s, path, arg);
+ 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);
+ }
+#endif
+ rb_syserr_fail_path(aa->errnum, aa->fn[aa->i].path);
+ }
+ if (v) {
+ ALLOCV_END(v);
+ }
return LONG2FIX(argc);
}
@@ -370,6 +427,13 @@ apply2files(void (*func)(const char *, VALUE, void *), int argc, VALUE *argv, vo
* Returns the pathname used to create <i>file</i> as a string. Does
* not normalize the name.
*
+ * The pathname may not point to the file corresponding to <i>file</i>.
+ * For instance, the pathname becomes void when the file has been
+ * moved or deleted.
+ *
+ * This method raises <code>IOError</code> for a <i>file</i> created using
+ * <code>File::Constants::TMPFILE</code> because they don't have a pathname.
+ *
* File.new("testfile").path #=> "testfile"
* File.new("/tmp/../tmp/xxx", "w").path #=> "/tmp/../tmp/xxx"
*
@@ -382,7 +446,11 @@ rb_file_path(VALUE obj)
fptr = RFILE(rb_io_taint_check(obj))->fptr;
rb_io_check_initialized(fptr);
- if (NIL_P(fptr->pathv)) return Qnil;
+
+ if (NIL_P(fptr->pathv)) {
+ rb_raise(rb_eIOError, "File is unnamed (TMPFILE?)");
+ }
+
return rb_obj_taint(rb_str_dup(fptr->pathv));
}
@@ -504,7 +572,7 @@ static VALUE
rb_stat_dev_major(VALUE self)
{
#if defined(major)
- return DEVT2NUM(major(get_stat(self)->st_dev));
+ return UINT2NUM(major(get_stat(self)->st_dev));
#else
return Qnil;
#endif
@@ -525,7 +593,7 @@ static VALUE
rb_stat_dev_minor(VALUE self)
{
#if defined(minor)
- return DEVT2NUM(minor(get_stat(self)->st_dev));
+ return UINT2NUM(minor(get_stat(self)->st_dev));
#else
return Qnil;
#endif
@@ -544,17 +612,12 @@ rb_stat_dev_minor(VALUE self)
static VALUE
rb_stat_ino(VALUE self)
{
-#ifdef _WIN32
- struct stat *st = get_stat(self);
- unsigned short *p2 = (unsigned short *)st;
- unsigned int *p4 = (unsigned int *)st;
- uint64_t r;
- r = p2[2];
- r <<= 16;
- r |= p2[7];
- r <<= 32;
- r |= p4[5];
- return ULL2NUM(r);
+#ifdef HAVE_STRUCT_STAT_ST_INOHIGH
+ /* assume INTEGER_PACK_LSWORD_FIRST and st_inohigh is just next of st_ino */
+ return rb_integer_unpack(&get_stat(self)->st_ino, 2,
+ SIZEOF_STRUCT_STAT_ST_INO, 0,
+ INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER|
+ INTEGER_PACK_2COMP);
#elif SIZEOF_STRUCT_STAT_ST_INO > SIZEOF_LONG
return ULL2NUM(get_stat(self)->st_ino);
#else
@@ -668,7 +731,7 @@ static VALUE
rb_stat_rdev_major(VALUE self)
{
#if defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(major)
- return DEVT2NUM(major(get_stat(self)->st_rdev));
+ return UINT2NUM(major(get_stat(self)->st_rdev));
#else
return Qnil;
#endif
@@ -689,7 +752,7 @@ static VALUE
rb_stat_rdev_minor(VALUE self)
{
#if defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(minor)
- return DEVT2NUM(minor(get_stat(self)->st_rdev));
+ return UINT2NUM(minor(get_stat(self)->st_rdev));
#else
return Qnil;
#endif
@@ -1004,83 +1067,73 @@ rb_stat_inspect(VALUE self)
return str;
}
+typedef struct no_gvl_stat_data {
+ struct stat *st;
+ union {
+ const char *path;
+ int fd;
+ } file;
+} no_gvl_stat_data;
+
+static VALUE
+no_gvl_fstat(void *data)
+{
+ no_gvl_stat_data *arg = data;
+ return (VALUE)fstat(arg->file.fd, arg->st);
+}
+
static int
-rb_stat(VALUE file, struct stat *st)
+fstat_without_gvl(int fd, struct stat *st)
{
- VALUE tmp;
+ no_gvl_stat_data data;
- tmp = rb_check_convert_type(file, T_FILE, "IO", "to_io");
- if (!NIL_P(tmp)) {
- rb_io_t *fptr;
+ data.file.fd = fd;
+ data.st = st;
- GetOpenFile(tmp, fptr);
- return fstat(fptr->fd, st);
- }
- FilePathValue(file);
- file = rb_str_encode_ospath(file);
- return STAT(StringValueCStr(file), st);
+ return (int)(VALUE)rb_thread_io_blocking_region(no_gvl_fstat, &data, fd);
}
-#ifdef _WIN32
-static HANDLE
-w32_io_info(VALUE *file, BY_HANDLE_FILE_INFORMATION *st)
+static void *
+no_gvl_stat(void * data)
+{
+ no_gvl_stat_data *arg = data;
+ return (void *)(VALUE)STAT(arg->file.path, arg->st);
+}
+
+static int
+stat_without_gvl(const char *path, struct stat *st)
+{
+ no_gvl_stat_data data;
+
+ data.file.path = path;
+ data.st = st;
+
+ return (int)(VALUE)rb_thread_call_without_gvl(no_gvl_stat, &data,
+ RUBY_UBF_IO, NULL);
+}
+
+static int
+rb_stat(VALUE file, struct stat *st)
{
VALUE tmp;
- HANDLE f, ret = 0;
+ int result;
- tmp = rb_check_convert_type(*file, T_FILE, "IO", "to_io");
+ tmp = rb_check_convert_type_with_id(file, T_FILE, "IO", idTo_io);
if (!NIL_P(tmp)) {
rb_io_t *fptr;
GetOpenFile(tmp, fptr);
- f = (HANDLE)rb_w32_get_osfhandle(fptr->fd);
- if (f == (HANDLE)-1) return INVALID_HANDLE_VALUE;
+ result = fstat_without_gvl(fptr->fd, st);
+ file = tmp;
}
else {
- VALUE tmp;
- WCHAR *ptr;
- int len;
- VALUE v;
-
- FilePathValue(*file);
- tmp = rb_str_encode_ospath(*file);
- len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
- ptr = ALLOCV_N(WCHAR, v, len);
- MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, ptr, len);
- f = CreateFileW(ptr, 0,
- FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS, NULL);
- ALLOCV_END(v);
- if (f == INVALID_HANDLE_VALUE) return f;
- ret = f;
- }
- if (GetFileType(f) == FILE_TYPE_DISK) {
- ZeroMemory(st, sizeof(*st));
- if (GetFileInformationByHandle(f, st)) return ret;
+ FilePathValue(file);
+ file = rb_str_encode_ospath(file);
+ result = stat_without_gvl(RSTRING_PTR(file), st);
}
- if (ret) CloseHandle(ret);
- return INVALID_HANDLE_VALUE;
-}
-
-static VALUE
-close_handle(VALUE h)
-{
- CloseHandle((HANDLE)h);
- return Qfalse;
-}
-
-struct w32_io_info_args {
- VALUE *fname;
- BY_HANDLE_FILE_INFORMATION *st;
-};
-
-static VALUE
-call_w32_io_info(VALUE arg)
-{
- struct w32_io_info_args *p = (void *)arg;
- return (VALUE)w32_io_info(p->fname, p->st);
+ RB_GC_GUARD(file);
+ return result;
}
-#endif
/*
* call-seq:
@@ -1099,7 +1152,8 @@ rb_file_s_stat(VALUE klass, VALUE fname)
struct stat st;
FilePathValue(fname);
- if (rb_stat(fname, &st) < 0) {
+ fname = rb_str_encode_ospath(fname);
+ if (stat_without_gvl(RSTRING_PTR(fname), &st) < 0) {
rb_sys_fail_path(fname);
}
return rb_stat_new(&st);
@@ -1133,6 +1187,27 @@ rb_io_stat(VALUE obj)
return rb_stat_new(&st);
}
+#ifdef HAVE_LSTAT
+static void *
+no_gvl_lstat(void *ptr)
+{
+ no_gvl_stat_data *arg = ptr;
+ return (void *)(VALUE)lstat(arg->file.path, arg->st);
+}
+
+static int
+lstat_without_gvl(const char *path, struct stat *st)
+{
+ no_gvl_stat_data data;
+
+ data.file.path = path;
+ data.st = st;
+
+ return (int)(VALUE)rb_thread_call_without_gvl(no_gvl_lstat, &data,
+ RUBY_UBF_IO, NULL);
+}
+#endif /* HAVE_LSTAT */
+
/*
* call-seq:
* File.lstat(file_name) -> stat
@@ -1155,7 +1230,7 @@ rb_file_s_lstat(VALUE klass, VALUE fname)
FilePathValue(fname);
fname = rb_str_encode_ospath(fname);
- if (lstat(StringValueCStr(fname), &st) == -1) {
+ if (lstat_without_gvl(StringValueCStr(fname), &st) == -1) {
rb_sys_fail_path(fname);
}
return rb_stat_new(&st);
@@ -1189,7 +1264,7 @@ rb_file_lstat(VALUE obj)
GetOpenFile(obj, fptr);
if (NIL_P(fptr->pathv)) return Qnil;
path = rb_str_encode_ospath(fptr->pathv);
- if (lstat(RSTRING_PTR(path), &st) == -1) {
+ if (lstat_without_gvl(RSTRING_PTR(path), &st) == -1) {
rb_sys_fail_path(fptr->pathv);
}
return rb_stat_new(&st);
@@ -1253,15 +1328,6 @@ rb_group_member(GETGROUPS_T gid)
#define USE_GETEUID 1
#endif
-#ifdef __native_client__
-// Although the NaCl toolchain contain eaccess() is it not yet
-// overridden by nacl_io.
-// TODO(sbc): Remove this once eaccess() is wired up correctly
-// in NaCl.
-# undef HAVE_EACCESS
-# undef USE_GETEUID
-#endif
-
#ifndef HAVE_EACCESS
int
eaccess(const char *path, int mode)
@@ -1306,6 +1372,54 @@ eaccess(const char *path, int mode)
}
#endif
+struct access_arg {
+ const char *path;
+ int mode;
+};
+
+static void *
+nogvl_eaccess(void *ptr)
+{
+ struct access_arg *aa = ptr;
+
+ return (void *)(VALUE)eaccess(aa->path, aa->mode);
+}
+
+static int
+rb_eaccess(VALUE fname, int mode)
+{
+ struct access_arg aa;
+
+ FilePathValue(fname);
+ fname = rb_str_encode_ospath(fname);
+ aa.path = StringValueCStr(fname);
+ aa.mode = mode;
+
+ return (int)(VALUE)rb_thread_call_without_gvl(nogvl_eaccess, &aa,
+ RUBY_UBF_IO, 0);
+}
+
+static void *
+nogvl_access(void *ptr)
+{
+ struct access_arg *aa = ptr;
+
+ return (void *)(VALUE)access(aa->path, aa->mode);
+}
+
+static int
+rb_access(VALUE fname, int mode)
+{
+ struct access_arg aa;
+
+ FilePathValue(fname);
+ fname = rb_str_encode_ospath(fname);
+ aa.path = StringValueCStr(fname);
+ aa.mode = mode;
+
+ return (int)(VALUE)rb_thread_call_without_gvl(nogvl_access, &aa,
+ RUBY_UBF_IO, 0);
+}
/*
* Document-class: FileTest
@@ -1401,7 +1515,7 @@ rb_file_symlink_p(VALUE obj, VALUE fname)
FilePathValue(fname);
fname = rb_str_encode_ospath(fname);
- if (lstat(StringValueCStr(fname), &st) < 0) return Qfalse;
+ if (lstat_without_gvl(StringValueCStr(fname), &st) < 0) return Qfalse;
if (S_ISLNK(st.st_mode)) return Qtrue;
#endif
@@ -1550,9 +1664,7 @@ rb_file_exists_p(VALUE obj, VALUE fname)
static VALUE
rb_file_readable_p(VALUE obj, VALUE fname)
{
- FilePathValue(fname);
- fname = rb_str_encode_ospath(fname);
- if (eaccess(StringValueCStr(fname), R_OK) < 0) return Qfalse;
+ if (rb_eaccess(fname, R_OK) < 0) return Qfalse;
return Qtrue;
}
@@ -1567,9 +1679,7 @@ rb_file_readable_p(VALUE obj, VALUE fname)
static VALUE
rb_file_readable_real_p(VALUE obj, VALUE fname)
{
- FilePathValue(fname);
- fname = rb_str_encode_ospath(fname);
- if (access(StringValueCStr(fname), R_OK) < 0) return Qfalse;
+ if (rb_access(fname, R_OK) < 0) return Qfalse;
return Qtrue;
}
@@ -1622,9 +1732,7 @@ rb_file_world_readable_p(VALUE obj, VALUE fname)
static VALUE
rb_file_writable_p(VALUE obj, VALUE fname)
{
- FilePathValue(fname);
- fname = rb_str_encode_ospath(fname);
- if (eaccess(StringValueCStr(fname), W_OK) < 0) return Qfalse;
+ if (rb_eaccess(fname, W_OK) < 0) return Qfalse;
return Qtrue;
}
@@ -1639,9 +1747,7 @@ rb_file_writable_p(VALUE obj, VALUE fname)
static VALUE
rb_file_writable_real_p(VALUE obj, VALUE fname)
{
- FilePathValue(fname);
- fname = rb_str_encode_ospath(fname);
- if (access(StringValueCStr(fname), W_OK) < 0) return Qfalse;
+ if (rb_access(fname, W_OK) < 0) return Qfalse;
return Qtrue;
}
@@ -1686,9 +1792,7 @@ rb_file_world_writable_p(VALUE obj, VALUE fname)
static VALUE
rb_file_executable_p(VALUE obj, VALUE fname)
{
- FilePathValue(fname);
- fname = rb_str_encode_ospath(fname);
- if (eaccess(StringValueCStr(fname), X_OK) < 0) return Qfalse;
+ if (rb_eaccess(fname, X_OK) < 0) return Qfalse;
return Qtrue;
}
@@ -1703,9 +1807,7 @@ rb_file_executable_p(VALUE obj, VALUE fname)
static VALUE
rb_file_executable_real_p(VALUE obj, VALUE fname)
{
- FilePathValue(fname);
- fname = rb_str_encode_ospath(fname);
- if (access(StringValueCStr(fname), X_OK) < 0) return Qfalse;
+ if (rb_access(fname, X_OK) < 0) return Qfalse;
return Qtrue;
}
@@ -1835,9 +1937,7 @@ check3rdbyte(VALUE fname, int mode)
{
struct stat st;
- FilePathValue(fname);
- fname = rb_str_encode_ospath(fname);
- if (STAT(StringValueCStr(fname), &st) < 0) return Qfalse;
+ if (rb_stat(fname, &st) < 0) return Qfalse;
if (st.st_mode & mode) return Qtrue;
return Qfalse;
}
@@ -1848,6 +1948,8 @@ check3rdbyte(VALUE fname, int mode)
* File.setuid?(file_name) -> true or false
*
* Returns <code>true</code> if the named file has the setuid bit set.
+ *
+ * _file_name_ can be an IO object.
*/
static VALUE
@@ -1865,6 +1967,8 @@ rb_file_suid_p(VALUE obj, VALUE fname)
* File.setgid?(file_name) -> true or false
*
* Returns <code>true</code> if the named file has the setgid bit set.
+ *
+ * _file_name_ can be an IO object.
*/
static VALUE
@@ -1882,6 +1986,8 @@ rb_file_sgid_p(VALUE obj, VALUE fname)
* File.sticky?(file_name) -> true or false
*
* Returns <code>true</code> if the named file has the sticky bit set.
+ *
+ * _file_name_ can be an IO object.
*/
static VALUE
@@ -1925,28 +2031,8 @@ rb_file_identical_p(VALUE obj, VALUE fname1, VALUE fname2)
if (st1.st_ino != st2.st_ino) return Qfalse;
return Qtrue;
#else
- BY_HANDLE_FILE_INFORMATION st1, st2;
- HANDLE f1 = 0, f2 = 0;
-
- f1 = w32_io_info(&fname1, &st1);
- if (f1 == INVALID_HANDLE_VALUE) return Qfalse;
- if (f1) {
- struct w32_io_info_args arg;
- arg.fname = &fname2;
- arg.st = &st2;
- f2 = (HANDLE)rb_ensure(call_w32_io_info, (VALUE)&arg, close_handle, (VALUE)f1);
- }
- else {
- f2 = w32_io_info(&fname2, &st2);
- }
- if (f2 == INVALID_HANDLE_VALUE) return Qfalse;
- if (f2) CloseHandle(f2);
-
- if (st1.dwVolumeSerialNumber == st2.dwVolumeSerialNumber &&
- st1.nFileIndexHigh == st2.nFileIndexHigh &&
- st1.nFileIndexLow == st2.nFileIndexLow)
- return Qtrue;
- return Qfalse;
+ extern VALUE rb_w32_file_identical_p(VALUE, VALUE);
+ return rb_w32_file_identical_p(fname1, fname2);
#endif
}
@@ -2035,7 +2121,7 @@ rb_file_s_ftype(VALUE klass, VALUE fname)
FilePathValue(fname);
fname = rb_str_encode_ospath(fname);
- if (lstat(StringValueCStr(fname), &st) == -1) {
+ if (lstat_without_gvl(StringValueCStr(fname), &st) == -1) {
rb_sys_fail_path(fname);
}
@@ -2046,7 +2132,7 @@ rb_file_s_ftype(VALUE klass, VALUE fname)
* call-seq:
* File.atime(file_name) -> time
*
- * Returns the last access time for the named file as a Time object).
+ * Returns the last access time for the named file as a Time object.
*
* _file_name_ can be an IO object.
*
@@ -2072,7 +2158,7 @@ rb_file_s_atime(VALUE klass, VALUE fname)
* file.atime -> time
*
* Returns the last access time (a <code>Time</code> object)
- * for <i>file</i>, or epoch if <i>file</i> has not been accessed.
+ * for <i>file</i>, or epoch if <i>file</i> has not been accessed.
*
* File.new("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
*
@@ -2280,11 +2366,10 @@ rb_file_size(VALUE obj)
return OFFT2NUM(st.st_size);
}
-static void
-chmod_internal(const char *path, VALUE pathv, void *mode)
+static int
+chmod_internal(const char *path, void *mode)
{
- if (chmod(path, *(int *)mode) < 0)
- rb_sys_fail_path(pathv);
+ return chmod(path, *(mode_t *)mode);
}
/*
@@ -2303,10 +2388,10 @@ chmod_internal(const char *path, VALUE pathv, void *mode)
static VALUE
rb_file_s_chmod(int argc, VALUE *argv)
{
- int mode;
+ mode_t mode;
apply2args(1);
- mode = NUM2INT(*argv++);
+ mode = NUM2MODET(*argv++);
return apply2files(chmod_internal, argc, argv, &mode);
}
@@ -2328,12 +2413,12 @@ static VALUE
rb_file_chmod(VALUE obj, VALUE vmode)
{
rb_io_t *fptr;
- int mode;
+ mode_t mode;
#if !defined HAVE_FCHMOD || !HAVE_FCHMOD
VALUE path;
#endif
- mode = NUM2INT(vmode);
+ mode = NUM2MODET(vmode);
GetOpenFile(obj, fptr);
#ifdef HAVE_FCHMOD
@@ -2356,11 +2441,10 @@ rb_file_chmod(VALUE obj, VALUE vmode)
}
#if defined(HAVE_LCHMOD)
-static void
-lchmod_internal(const char *path, VALUE pathv, void *mode)
+static int
+lchmod_internal(const char *path, void *mode)
{
- if (lchmod(path, (int)(VALUE)mode) < 0)
- rb_sys_fail_path(pathv);
+ return lchmod(path, *(mode_t *)mode);
}
/*
@@ -2376,12 +2460,12 @@ lchmod_internal(const char *path, VALUE pathv, void *mode)
static VALUE
rb_file_s_lchmod(int argc, VALUE *argv)
{
- long mode;
+ mode_t mode;
apply2args(1);
- mode = NUM2INT(*argv++);
+ mode = NUM2MODET(*argv++);
- return apply2files(lchmod_internal, argc, argv, (void *)(long)mode);
+ return apply2files(lchmod_internal, argc, argv, &mode);
}
#else
#define rb_file_s_lchmod rb_f_notimplement
@@ -2410,17 +2494,16 @@ struct chown_args {
rb_gid_t group;
};
-static void
-chown_internal(const char *path, VALUE pathv, void *arg)
+static int
+chown_internal(const char *path, void *arg)
{
struct chown_args *args = arg;
- if (chown(path, args->owner, args->group) < 0)
- rb_sys_fail_path(pathv);
+ return chown(path, args->owner, args->group);
}
/*
* call-seq:
- * File.chown(owner_int, group_int, file_name,... ) -> integer
+ * File.chown(owner_int, group_int, file_name, ...) -> integer
*
* Changes the owner and group of the named file(s) to the given
* numeric owner and group id's. Only a process with superuser
@@ -2487,12 +2570,11 @@ rb_file_chown(VALUE obj, VALUE owner, VALUE group)
}
#if defined(HAVE_LCHOWN)
-static void
-lchown_internal(const char *path, VALUE pathv, void *arg)
+static int
+lchown_internal(const char *path, void *arg)
{
struct chown_args *args = arg;
- if (lchown(path, args->owner, args->group) < 0)
- rb_sys_fail_path(pathv);
+ return lchown(path, args->owner, args->group);
}
/*
@@ -2524,18 +2606,25 @@ rb_file_s_lchown(int argc, VALUE *argv)
struct utime_args {
const struct timespec* tsp;
VALUE atime, mtime;
+ int follow; /* Whether to act on symlinks (1) or their referent (0) */
};
-#if defined DOSISH || defined __CYGWIN__
-NORETURN(static void utime_failed(VALUE, const struct timespec *, VALUE, VALUE));
+#ifdef UTIME_EINVAL
+NORETURN(static void utime_failed(struct apply_arg *));
static void
-utime_failed(VALUE path, const struct timespec *tsp, VALUE atime, VALUE mtime)
+utime_failed(struct apply_arg *aa)
{
- int e = errno;
- if (tsp && e == EINVAL) {
+ int e = aa->errnum;
+ VALUE path = aa->fn[aa->i].path;
+ 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);
}
@@ -2560,14 +2649,12 @@ utime_failed(VALUE path, const struct timespec *tsp, VALUE atime, VALUE mtime)
}
rb_syserr_fail_path(e, path);
}
-#else
-#define utime_failed(path, tsp, atime, mtime) rb_sys_fail_path(path)
#endif
#if defined(HAVE_UTIMES)
-static void
-utime_internal(const char *path, VALUE pathv, void *arg)
+static int
+utime_internal(const char *path, void *arg)
{
struct utime_args *v = arg;
const struct timespec *tsp = v->tsp;
@@ -2575,16 +2662,32 @@ utime_internal(const char *path, VALUE pathv, void *arg)
#if defined(HAVE_UTIMENSAT)
static int try_utimensat = 1;
+# ifdef AT_SYMLINK_NOFOLLOW
+ static int try_utimensat_follow = 1;
+# else
+ const int try_utimensat_follow = 0;
+# endif
+ int flags = 0;
+
+ if (v->follow ? try_utimensat_follow : try_utimensat) {
+# ifdef AT_SYMLINK_NOFOLLOW
+ if (v->follow) {
+ flags = AT_SYMLINK_NOFOLLOW;
+ }
+# endif
- if (try_utimensat) {
- if (utimensat(AT_FDCWD, path, tsp, 0) < 0) {
+ if (utimensat(AT_FDCWD, path, tsp, flags) < 0) {
if (errno == ENOSYS) {
- try_utimensat = 0;
+# ifdef AT_SYMLINK_NOFOLLOW
+ try_utimensat_follow = 0;
+# endif
+ if (!v->follow)
+ try_utimensat = 0;
goto no_utimensat;
}
- utime_failed(pathv, tsp, v->atime, v->mtime);
+ return -1; /* calls utime_failed */
}
- return;
+ return 0;
}
no_utimensat:
#endif
@@ -2596,8 +2699,10 @@ no_utimensat:
tvbuf[1].tv_usec = (int)(tsp[1].tv_nsec / 1000);
tvp = tvbuf;
}
- if (utimes(path, tvp) < 0)
- utime_failed(pathv, tsp, v->atime, v->mtime);
+#ifdef HAVE_LUTIMES
+ if (v->follow) return lutimes(path, tvp);
+#endif
+ return utimes(path, tvp);
}
#else
@@ -2609,8 +2714,8 @@ struct utimbuf {
};
#endif
-static void
-utime_internal(const char *path, VALUE pathv, void *arg)
+static int
+utime_internal(const char *path, void *arg)
{
struct utime_args *v = arg;
const struct timespec *tsp = v->tsp;
@@ -2620,23 +2725,13 @@ utime_internal(const char *path, VALUE pathv, void *arg)
utbuf.modtime = tsp[1].tv_sec;
utp = &utbuf;
}
- if (utime(path, utp) < 0)
- utime_failed(pathv, tsp, v->atime, v->mtime);
+ return utime(path, utp);
}
#endif
-/*
- * call-seq:
- * File.utime(atime, mtime, file_name,...) -> integer
- *
- * Sets the access and modification times of each
- * named file to the first two arguments. Returns
- * the number of file names in the argument list.
- */
-
static VALUE
-rb_file_s_utime(int argc, VALUE *argv)
+utime_internal_i(int argc, VALUE *argv, int follow)
{
struct utime_args args;
struct timespec tss[2], *tsp = NULL;
@@ -2645,6 +2740,8 @@ rb_file_s_utime(int argc, VALUE *argv)
args.atime = *argv++;
args.mtime = *argv++;
+ args.follow = follow;
+
if (!NIL_P(args.atime) || !NIL_P(args.mtime)) {
tsp = tss;
tsp[0] = rb_time_timespec(args.atime);
@@ -2658,6 +2755,45 @@ rb_file_s_utime(int argc, VALUE *argv)
return apply2files(utime_internal, argc, argv, &args);
}
+/*
+ * call-seq:
+ * File.utime(atime, mtime, file_name, ...) -> integer
+ *
+ * Sets the access and modification times of each named file to the
+ * first two arguments. If a file is a symlink, this method acts upon
+ * its referent rather than the link itself; for the inverse
+ * behavior see File.lutime. Returns the number of file
+ * names in the argument list.
+ */
+
+static VALUE
+rb_file_s_utime(int argc, VALUE *argv)
+{
+ return utime_internal_i(argc, argv, FALSE);
+}
+
+#if defined(HAVE_UTIMES) && (defined(HAVE_LUTIMES) || (defined(HAVE_UTIMENSAT) && defined(AT_SYMLINK_NOFOLLOW)))
+
+/*
+ * call-seq:
+ * File.lutime(atime, mtime, file_name, ...) -> integer
+ *
+ * Sets the access and modification times of each named file to the
+ * first two arguments. If a file is a symlink, this method acts upon
+ * the link itself as opposed to its referent; for the inverse
+ * behavior, see File.utime. Returns the number of file
+ * names in the argument list.
+ */
+
+static VALUE
+rb_file_s_lutime(int argc, VALUE *argv)
+{
+ return utime_internal_i(argc, argv, TRUE);
+}
+#else
+#define rb_file_s_lutime rb_f_notimplement
+#endif
+
#ifdef RUBY_FUNCTION_NAME_STRING
# define syserr_fail2(e, s1, s2) syserr_fail2_in(RUBY_FUNCTION_NAME_STRING, e, s1, s2)
#else
@@ -2769,6 +2905,33 @@ rb_file_s_readlink(VALUE klass, VALUE path)
}
#ifndef _WIN32
+struct readlink_arg {
+ const char *path;
+ char *buf;
+ size_t size;
+};
+
+static void *
+nogvl_readlink(void *ptr)
+{
+ struct readlink_arg *ra = ptr;
+
+ return (void *)(VALUE)readlink(ra->path, ra->buf, ra->size);
+}
+
+static ssize_t
+readlink_without_gvl(VALUE path, VALUE buf, size_t size)
+{
+ struct readlink_arg ra;
+
+ ra.path = RSTRING_PTR(path);
+ ra.buf = RSTRING_PTR(buf);
+ ra.size = size;
+
+ return (ssize_t)rb_thread_call_without_gvl(nogvl_readlink, &ra,
+ RUBY_UBF_IO, 0);
+}
+
VALUE
rb_readlink(VALUE path, rb_encoding *enc)
{
@@ -2779,7 +2942,7 @@ rb_readlink(VALUE path, rb_encoding *enc)
FilePathValue(path);
path = rb_str_encode_ospath(path);
v = rb_enc_str_new(0, size, enc);
- while ((rv = readlink(RSTRING_PTR(path), RSTRING_PTR(v), size)) == size
+ while ((rv = readlink_without_gvl(path, v, size)) == size
#ifdef _AIX
|| (rv < 0 && errno == ERANGE) /* quirky behavior of GPFS */
#endif
@@ -2802,11 +2965,10 @@ rb_readlink(VALUE path, rb_encoding *enc)
#define rb_file_s_readlink rb_f_notimplement
#endif
-static void
-unlink_internal(const char *path, VALUE pathv, void *arg)
+static int
+unlink_internal(const char *path, void *arg)
{
- if (unlink(path) < 0)
- rb_sys_fail_path(pathv);
+ return unlink(path);
}
/*
@@ -2816,6 +2978,12 @@ unlink_internal(const char *path, VALUE pathv, void *arg)
*
* Deletes the named files, returning the number of names
* passed as arguments. Raises an exception on any error.
+ * Since the underlying implementation relies on the
+ * <code>unlink(2)</code> system call, the type of
+ * exception raised depends on its error type (see
+ * https://linux.die.net/man/2/unlink) and has the form of
+ * e.g. <code>Errno::ENOENT</code>.
+ *
* See also <code>Dir::rmdir</code>.
*/
@@ -2825,6 +2993,19 @@ rb_file_s_unlink(int argc, VALUE *argv, VALUE klass)
return apply2files(unlink_internal, argc, argv, 0);
}
+struct rename_args {
+ const char *src;
+ const char *dst;
+};
+
+static void *
+no_gvl_rename(void *ptr)
+{
+ struct rename_args *ra = ptr;
+
+ return (void *)(VALUE)rename(ra->src, ra->dst);
+}
+
/*
* call-seq:
* File.rename(old_name, new_name) -> 0
@@ -2838,26 +3019,27 @@ rb_file_s_unlink(int argc, VALUE *argv, VALUE klass)
static VALUE
rb_file_s_rename(VALUE klass, VALUE from, VALUE to)
{
- const char *src, *dst;
+ struct rename_args ra;
VALUE f, t;
FilePathValue(from);
FilePathValue(to);
f = rb_str_encode_ospath(from);
t = rb_str_encode_ospath(to);
- src = StringValueCStr(f);
- dst = StringValueCStr(t);
+ ra.src = StringValueCStr(f);
+ ra.dst = StringValueCStr(t);
#if defined __CYGWIN__
errno = 0;
#endif
- if (rename(src, dst) < 0) {
+ if ((int)(VALUE)rb_thread_call_without_gvl(no_gvl_rename, &ra,
+ RUBY_UBF_IO, 0) < 0) {
int e = errno;
#if defined DOSISH
switch (e) {
case EEXIST:
- if (chmod(dst, 0666) == 0 &&
- unlink(dst) == 0 &&
- rename(src, dst) == 0)
+ if (chmod(ra.dst, 0666) == 0 &&
+ unlink(ra.dst) == 0 &&
+ rename(ra.src, ra.dst) == 0)
return INT2FIX(0);
}
#endif
@@ -2885,19 +3067,19 @@ rb_file_s_rename(VALUE klass, VALUE from, VALUE to)
static VALUE
rb_file_s_umask(int argc, VALUE *argv)
{
- int omask = 0;
+ mode_t omask = 0;
if (argc == 0) {
omask = umask(0);
umask(omask);
}
else if (argc == 1) {
- omask = umask(NUM2INT(argv[0]));
+ omask = umask(NUM2MODET(argv[0]));
}
else {
rb_check_arity(argc, 0, 1);
}
- return INT2FIX(omask);
+ return MODET2NUM(omask);
}
#ifdef __CYGWIN__
@@ -2910,24 +3092,38 @@ rb_file_s_umask(int argc, VALUE *argv)
#endif
#ifdef FILE_ALT_SEPARATOR
#define isdirsep(x) ((x) == '/' || (x) == FILE_ALT_SEPARATOR)
+# ifdef DOSISH
static const char file_alt_separator[] = {FILE_ALT_SEPARATOR, '\0'};
+# endif
#else
#define isdirsep(x) ((x) == '/')
#endif
#ifndef USE_NTFS
-#if defined _WIN32 || defined __CYGWIN__
+#if defined _WIN32
#define USE_NTFS 1
#else
#define USE_NTFS 0
#endif
#endif
+#ifndef USE_NTFS_ADS
+# if USE_NTFS
+# define USE_NTFS_ADS 1
+# else
+# define USE_NTFS_ADS 0
+# endif
+#endif
#if USE_NTFS
#define istrailinggarbage(x) ((x) == '.' || (x) == ' ')
#else
#define istrailinggarbage(x) 0
#endif
+#if USE_NTFS_ADS
+# define isADS(x) ((x) == ':')
+#else
+# define isADS(x) 0
+#endif
#define Next(p, e, enc) ((p) + rb_enc_mbclen((p), (e), (enc)))
#define Inc(p, e, enc) ((p) = Next((p), (e), (enc)))
@@ -2965,9 +3161,9 @@ getcwdofdrv(int drv)
of a particular drive is to change chdir() to that drive,
so save the old cwd before chdir()
*/
- oldcwd = my_getcwd();
+ oldcwd = ruby_getcwd();
if (chdir(drive) == 0) {
- drvcwd = my_getcwd();
+ drvcwd = ruby_getcwd();
chdir(oldcwd);
xfree(oldcwd);
}
@@ -3098,17 +3294,17 @@ static char *
ntfs_tail(const char *path, const char *end, rb_encoding *enc)
{
while (path < end && *path == '.') path++;
- while (path < end && *path != ':') {
+ while (path < end && !isADS(*path)) {
if (istrailinggarbage(*path)) {
const char *last = path++;
while (path < end && istrailinggarbage(*path)) path++;
- if (path >= end || *path == ':') return (char *)last;
+ 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 (*path == ':') path++;
+ if (isADS(*path)) path++;
}
else {
Inc(path, end, enc);
@@ -3221,17 +3417,58 @@ rb_home_dir_of(VALUE user, VALUE result)
return result;
}
+#ifndef _WIN32
VALUE
rb_default_home_dir(VALUE result)
{
const char *dir = getenv("HOME");
+
+#if defined HAVE_PWD_H
+ if (!dir) {
+ /* We'll look up the user's default home dir in the password db by
+ * login name, if possible, and failing that will fall back to looking
+ * the information up by uid (as would be needed for processes that
+ * are not a descendant of login(1) or a work-alike).
+ *
+ * While the lookup by uid is more likely to succeed (since we always
+ * have a uid, but may or may not have a login name), we prefer first
+ * looking up by name to accommodate the possibility of multiple login
+ * names (each with its own record in the password database, so each
+ * with a potentially different home directory) being mapped to the
+ * same uid (as explicitly allowed for by POSIX; see getlogin(3posix)).
+ */
+ VALUE login_name = rb_getlogin();
+
+# if !defined(HAVE_GETPWUID_R) && !defined(HAVE_GETPWUID)
+ /* This is a corner case, but for backward compatibility reasons we
+ * want to emit this error if neither the lookup by login name nor
+ * lookup by getuid() has a chance of succeeding.
+ */
+ if (NIL_P(login_name)) {
+ rb_raise(rb_eArgError, "couldn't find login name -- expanding `~'");
+ }
+# endif
+
+ VALUE pw_dir = rb_getpwdirnam_for_login(login_name);
+ if (NIL_P(pw_dir)) {
+ pw_dir = rb_getpwdiruid();
+ if (NIL_P(pw_dir)) {
+ rb_raise(rb_eArgError, "couldn't find home for uid `%ld'", (long)getuid());
+ }
+ }
+
+ /* found it */
+ copy_home_path(result, RSTRING_PTR(pw_dir));
+ rb_str_resize(pw_dir, 0);
+ return result;
+ }
+#endif
if (!dir) {
rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
}
return copy_home_path(result, dir);
}
-#ifndef _WIN32
static VALUE
ospath_new(const char *ptr, long len, rb_encoding *fsenc)
{
@@ -3368,7 +3605,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
p = pend;
}
else {
- char *e = append_fspath(result, fname, my_getcwd(), &enc, fsenc);
+ char *e = append_fspath(result, fname, ruby_getcwd(), &enc, fsenc);
tainted = 1;
BUFINIT();
p = e;
@@ -3452,7 +3689,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
case ' ': {
const char *e = s;
while (s < fend && istrailinggarbage(*s)) s++;
- if (!*s) {
+ if (s >= fend) {
s = e;
goto endpath;
}
@@ -3491,19 +3728,23 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
if (s > b) {
#if USE_NTFS
+# if USE_NTFS_ADS
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 (*(s - (prime_len+1)) == ':') {
+ 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);
@@ -3532,7 +3773,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
struct stat st;
p = (char *)s;
len = strlen(p);
- if (lstat(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
+ 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;
@@ -3721,8 +3962,16 @@ rb_file_s_absolute_path(int argc, const VALUE *argv)
return rb_file_absolute_path(argv[0], argc > 1 ? argv[1] : Qnil);
}
-static void
-realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE loopcheck, int strict, int last)
+enum rb_realpath_mode {
+ RB_REALPATH_CHECK,
+ RB_REALPATH_DIR,
+ RB_REALPATH_STRICT,
+ RB_REALPATH_MODE_MAX
+};
+
+static int
+realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE fallback,
+ VALUE loopcheck, enum rb_realpath_mode mode, int last)
{
const char *pend = unresolved + strlen(unresolved);
rb_encoding *enc = rb_enc_get(*resolvedp);
@@ -3763,6 +4012,10 @@ realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE l
checkval = rb_hash_aref(loopcheck, testpath);
if (!NIL_P(checkval)) {
if (checkval == ID2SYM(resolving)) {
+ if (mode == RB_REALPATH_CHECK) {
+ errno = ELOOP;
+ return -1;
+ }
rb_syserr_fail_path(ELOOP, testpath);
}
else {
@@ -3772,16 +4025,18 @@ realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE l
else {
struct stat sbuf;
int ret;
- VALUE testpath2 = rb_str_encode_ospath(testpath);
-#ifdef __native_client__
- ret = stat(RSTRING_PTR(testpath2), &sbuf);
-#else
- ret = lstat(RSTRING_PTR(testpath2), &sbuf);
-#endif
+ 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 (strict || !last || *unresolved_firstsep)
+ if (mode == RB_REALPATH_STRICT || !last || *unresolved_firstsep)
rb_syserr_fail_path(e, testpath);
*resolvedp = testpath;
break;
@@ -3793,7 +4048,7 @@ realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE l
#ifdef HAVE_READLINK
if (S_ISLNK(sbuf.st_mode)) {
VALUE link;
- volatile VALUE link_orig = Qnil;
+ VALUE link_orig = Qnil;
const char *link_prefix, *link_names;
long link_prefixlen;
rb_hash_aset(loopcheck, testpath, ID2SYM(resolving));
@@ -3802,15 +4057,17 @@ realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE l
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 *enc, *linkenc = rb_enc_get(link);
+ rb_encoding *tmpenc, *linkenc = rb_enc_get(link);
link_orig = link;
link = rb_str_subseq(link, 0, link_prefixlen);
- enc = rb_enc_check(*resolvedp, link);
- if (enc != linkenc) link = rb_str_conv_enc(link, linkenc, enc);
+ tmpenc = rb_enc_check(*resolvedp, link);
+ if (tmpenc != linkenc) link = rb_str_conv_enc(link, linkenc, tmpenc);
*resolvedp = link;
*prefixlenp = link_prefixlen;
}
- realpath_rec(prefixlenp, resolvedp, link_names, loopcheck, strict, *unresolved_firstsep == '\0');
+ 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));
}
@@ -3824,23 +4081,17 @@ realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE l
}
}
}
+ return 0;
}
-#ifdef __native_client__
-VALUE
-rb_realpath_internal(VALUE basedir, VALUE path, int strict)
-{
- return path;
-}
-#else
-VALUE
-rb_realpath_internal(VALUE basedir, VALUE path, int strict)
+static VALUE
+rb_check_realpath_internal(VALUE basedir, VALUE path, enum rb_realpath_mode mode)
{
long prefixlen;
VALUE resolved;
- volatile VALUE unresolved_path;
+ VALUE unresolved_path;
VALUE loopcheck;
- volatile VALUE curdir = Qnil;
+ VALUE curdir = Qnil;
rb_encoding *enc, *origenc;
char *path_names = NULL, *basedir_names = NULL, *curdir_names = NULL;
@@ -3851,9 +4102,12 @@ rb_realpath_internal(VALUE basedir, VALUE path, int strict)
if (!NIL_P(basedir)) {
FilePathValue(basedir);
- basedir = rb_str_dup_frozen(basedir);
+ basedir = TO_OSPATH(rb_str_dup_frozen(basedir));
}
+ enc = rb_enc_get(unresolved_path);
+ origenc = enc;
+ unresolved_path = TO_OSPATH(unresolved_path);
RSTRING_GETMEM(unresolved_path, ptr, len);
path_names = skipprefixroot(ptr, ptr + len, rb_enc_get(unresolved_path));
if (ptr != path_names) {
@@ -3870,7 +4124,7 @@ rb_realpath_internal(VALUE basedir, VALUE path, int strict)
}
}
- curdir = rb_dir_getwd();
+ curdir = rb_dir_getwd_ospath();
RSTRING_GETMEM(curdir, ptr, len);
curdir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(curdir));
resolved = rb_str_subseq(curdir, 0, curdir_names - ptr);
@@ -3878,7 +4132,6 @@ rb_realpath_internal(VALUE basedir, VALUE path, int strict)
root_found:
RSTRING_GETMEM(resolved, prefixptr, prefixlen);
pend = prefixptr + prefixlen;
- enc = rb_enc_get(resolved);
ptr = chompdirsep(prefixptr, pend, enc);
if (ptr < pend) {
prefixlen = ++ptr - prefixptr;
@@ -3893,7 +4146,6 @@ rb_realpath_internal(VALUE basedir, VALUE path, int strict)
}
#endif
- origenc = enc;
switch (rb_enc_to_index(enc)) {
case ENCINDEX_ASCII:
case ENCINDEX_US_ASCII:
@@ -3901,19 +4153,45 @@ rb_realpath_internal(VALUE basedir, VALUE path, int strict)
}
loopcheck = rb_hash_new();
- if (curdir_names)
- realpath_rec(&prefixlen, &resolved, curdir_names, loopcheck, 1, 0);
- if (basedir_names)
- realpath_rec(&prefixlen, &resolved, basedir_names, loopcheck, 1, 0);
- realpath_rec(&prefixlen, &resolved, path_names, loopcheck, strict, 1);
+ if (curdir_names) {
+ 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, path_names, Qnil, loopcheck, mode, 1))
+ return Qnil;
- if (origenc != enc && rb_enc_str_asciionly_p(resolved))
- rb_enc_associate(resolved, origenc);
+ if (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);
+ }
+ }
- OBJ_TAINT(resolved);
+ rb_obj_taint(resolved);
+ RB_GC_GUARD(unresolved_path);
+ RB_GC_GUARD(curdir);
return resolved;
}
-#endif
+
+VALUE
+rb_realpath_internal(VALUE basedir, VALUE path, int strict)
+{
+ const enum rb_realpath_mode mode =
+ strict ? RB_REALPATH_STRICT : RB_REALPATH_DIR;
+ return rb_check_realpath_internal(basedir, path, mode);
+}
+
+VALUE
+rb_check_realpath(VALUE basedir, VALUE path)
+{
+ return rb_check_realpath_internal(basedir, path, RB_REALPATH_CHECK);
+}
/*
* call-seq:
@@ -4061,7 +4339,8 @@ ruby_enc_find_basename(const char *name, long *baselen, long *alllen, rb_encodin
* call-seq:
* File.basename(file_name [, suffix] ) -> base_name
*
- * Returns the last component of the filename given in <i>file_name</i>,
+ * Returns the last component of the filename given in
+ * <i>file_name</i> (after first stripping trailing separators),
* which can be formed using both <code>File::SEPARATOR</code> and
* <code>File::ALT_SEPARATOR</code> as the separator when
* <code>File::ALT_SEPARATOR</code> is not <code>nil</code>. If
@@ -4124,9 +4403,10 @@ rb_file_s_basename(int argc, VALUE *argv)
* File.dirname(file_name) -> dir_name
*
* Returns all components of the filename given in <i>file_name</i>
- * except the last one. The filename can be formed using both
- * <code>File::SEPARATOR</code> and <code>File::ALT_SEPARATOR</code> as the
- * separator when <code>File::ALT_SEPARATOR</code> is not <code>nil</code>.
+ * except the last one (after first stripping trailing separators).
+ * The filename can be formed using both <code>File::SEPARATOR</code>
+ * and <code>File::ALT_SEPARATOR</code> as the separator when
+ * <code>File::ALT_SEPARATOR</code> is not <code>nil</code>.
*
* File.dirname("/home/gumby/work/ruby.rb") #=> "/home/gumby/work"
*/
@@ -4213,7 +4493,7 @@ ruby_enc_find_extname(const char *name, long *len, rb_encoding *enc)
if (*p == '.') dot = p;
p++;
}
- if (!*p || *p == ':') {
+ if (!*p || isADS(*p)) {
p = last;
break;
}
@@ -4224,7 +4504,7 @@ ruby_enc_find_extname(const char *name, long *len, rb_encoding *enc)
#endif
}
#if USE_NTFS
- else if (*p == ':') {
+ else if (isADS(*p)) {
break;
}
#endif
@@ -4321,20 +4601,17 @@ rb_file_s_split(VALUE klass, VALUE path)
return rb_assoc_new(rb_file_dirname(path), rb_file_s_basename(1,&path));
}
-static VALUE separator;
-
-static VALUE rb_file_join(VALUE ary, VALUE sep);
+static VALUE rb_file_join(VALUE ary);
static VALUE
-file_inspect_join(VALUE ary, VALUE argp, int recur)
+file_inspect_join(VALUE ary, VALUE arg, int recur)
{
- VALUE *arg = (VALUE *)argp;
- if (recur || ary == arg[0]) rb_raise(rb_eArgError, "recursive array");
- return rb_file_join(arg[0], arg[1]);
+ if (recur || ary == arg) rb_raise(rb_eArgError, "recursive array");
+ return rb_file_join(arg);
}
static VALUE
-rb_file_join(VALUE ary, VALUE sep)
+rb_file_join(VALUE ary)
{
long len, i;
VALUE result, tmp;
@@ -4355,10 +4632,7 @@ rb_file_join(VALUE ary, VALUE sep)
len += 10;
}
}
- if (!NIL_P(sep)) {
- StringValue(sep);
- len += RSTRING_LEN(sep) * (RARRAY_LEN(ary) - 1);
- }
+ len += RARRAY_LEN(ary) - 1;
result = rb_str_buf_new(len);
RBASIC_CLEAR_CLASS(result);
OBJ_INFECT(result, ary);
@@ -4374,11 +4648,7 @@ rb_file_join(VALUE ary, VALUE sep)
rb_raise(rb_eArgError, "recursive array");
}
else {
- VALUE args[2];
-
- args[0] = tmp;
- args[1] = sep;
- tmp = rb_exec_recursive(file_inspect_join, ary, (VALUE)args);
+ tmp = rb_exec_recursive(file_inspect_join, ary, tmp);
}
break;
default:
@@ -4389,15 +4659,13 @@ rb_file_join(VALUE ary, VALUE sep)
if (i == 0) {
rb_enc_copy(result, tmp);
}
- else if (!NIL_P(sep)) {
+ 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) {
- enc = rb_enc_check(result, sep);
- rb_str_buf_append(result, sep);
- rb_enc_associate(result, enc);
+ rb_str_cat(result, "/", 1);
}
}
enc = rb_enc_check(result, tmp);
@@ -4414,7 +4682,7 @@ rb_file_join(VALUE ary, VALUE sep)
* File.join(string, ...) -> string
*
* Returns a new string formed by joining the strings using
- * <code>File::SEPARATOR</code>.
+ * <code>"/"</code>.
*
* File.join("usr", "mail", "gumby") #=> "usr/mail/gumby"
*
@@ -4423,10 +4691,46 @@ rb_file_join(VALUE ary, VALUE sep)
static VALUE
rb_file_s_join(VALUE klass, VALUE args)
{
- return rb_file_join(args, separator);
+ return rb_file_join(args);
}
#if defined(HAVE_TRUNCATE) || defined(HAVE_CHSIZE)
+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
+};
+
+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
+}
+
/*
* call-seq:
* File.truncate(file_name, integer) -> 0
@@ -4445,36 +4749,18 @@ rb_file_s_join(VALUE klass, VALUE args)
static VALUE
rb_file_s_truncate(VALUE klass, VALUE path, VALUE len)
{
-#ifdef HAVE_TRUNCATE
-#define NUM2POS(n) NUM2OFFT(n)
- off_t pos;
-#else
-#define NUM2POS(n) NUM2LONG(n)
- long pos;
-#endif
+ struct truncate_arg ta;
+ int r;
- pos = NUM2POS(len);
+ ta.pos = NUM2POS(len);
FilePathValue(path);
path = rb_str_encode_ospath(path);
-#ifdef HAVE_TRUNCATE
- if (truncate(StringValueCStr(path), pos) < 0)
- rb_sys_fail_path(path);
-#else /* defined(HAVE_CHSIZE) */
- {
- int tmpfd;
+ ta.path = StringValueCStr(path);
- if ((tmpfd = rb_cloexec_open(StringValueCStr(path), 0, 0)) < 0) {
- rb_sys_fail_path(path);
- }
- rb_update_max_fd(tmpfd);
- if (chsize(tmpfd, pos) < 0) {
- int e = errno;
- close(tmpfd);
- rb_syserr_fail_path(e, path);
- }
- close(tmpfd);
- }
-#endif
+ r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_truncate, &ta,
+ RUBY_UBF_IO, NULL);
+ if (r < 0)
+ rb_sys_fail_path(path);
return INT2FIX(0);
#undef NUM2POS
}
@@ -4483,6 +4769,29 @@ rb_file_s_truncate(VALUE klass, VALUE path, VALUE len)
#endif
#if defined(HAVE_FTRUNCATE) || defined(HAVE_CHSIZE)
+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
+};
+
+static VALUE
+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
+}
+
/*
* call-seq:
* file.truncate(integer) -> 0
@@ -4501,27 +4810,18 @@ static VALUE
rb_file_truncate(VALUE obj, VALUE len)
{
rb_io_t *fptr;
-#if defined(HAVE_FTRUNCATE)
-#define NUM2POS(n) NUM2OFFT(n)
- off_t pos;
-#else
-#define NUM2POS(n) NUM2LONG(n)
- long pos;
-#endif
+ struct ftruncate_arg fa;
- pos = NUM2POS(len);
+ fa.pos = NUM2POS(len);
GetOpenFile(obj, fptr);
if (!(fptr->mode & FMODE_WRITABLE)) {
rb_raise(rb_eIOError, "not opened for writing");
}
rb_io_flush_raw(obj, 0);
-#ifdef HAVE_FTRUNCATE
- if (ftruncate(fptr->fd, pos) < 0)
- rb_sys_fail_path(fptr->pathv);
-#else /* defined(HAVE_CHSIZE) */
- if (chsize(fptr->fd, pos) < 0)
+ fa.fd = fptr->fd;
+ if ((int)rb_thread_io_blocking_region(nogvl_ftruncate, &fa, fa.fd) < 0) {
rb_sys_fail_path(fptr->pathv);
-#endif
+ }
return INT2FIX(0);
#undef NUM2POS
}
@@ -5510,6 +5810,20 @@ rb_stat_sticky(VALUE obj)
#define HAVE_MKFIFO
#endif
+#ifdef HAVE_MKFIFO
+struct mkfifo_arg {
+ const char *path;
+ mode_t mode;
+};
+
+static void *
+nogvl_mkfifo(void *ptr)
+{
+ struct mkfifo_arg *ma = ptr;
+
+ return (void *)(VALUE)mkfifo(ma->path, ma->mode);
+}
+
/*
* call-seq:
* File.mkfifo(file_name, mode=0666) => 0
@@ -5520,21 +5834,22 @@ rb_stat_sticky(VALUE obj)
* (mode & ~umask).
*/
-#ifdef HAVE_MKFIFO
static VALUE
rb_file_s_mkfifo(int argc, VALUE *argv)
{
VALUE path;
- int mode = 0666;
+ struct mkfifo_arg ma;
+ ma.mode = 0666;
rb_check_arity(argc, 1, 2);
if (argc > 1) {
- mode = NUM2INT(argv[1]);
+ ma.mode = NUM2MODET(argv[1]);
}
path = argv[0];
FilePathValue(path);
path = rb_str_encode_ospath(path);
- if (mkfifo(RSTRING_PTR(path), mode)) {
+ ma.path = RSTRING_PTR(path);
+ if (rb_thread_call_without_gvl(nogvl_mkfifo, &ma, RUBY_UBF_IO, 0)) {
rb_sys_fail_path(path);
}
return INT2FIX(0);
@@ -5543,7 +5858,7 @@ rb_file_s_mkfifo(int argc, VALUE *argv)
#define rb_file_s_mkfifo rb_f_notimplement
#endif
-VALUE rb_mFConst;
+static VALUE rb_mFConst;
void
rb_file_const(const char *name, VALUE value)
@@ -5585,7 +5900,7 @@ path_check_0(VALUE path, int execpath)
char *p = 0, *s;
if (!rb_is_absolute_path(p0)) {
- char *buf = my_getcwd();
+ char *buf = ruby_getcwd();
VALUE newpath;
newpath = rb_str_new2(buf);
@@ -5607,9 +5922,9 @@ path_check_0(VALUE path, int execpath)
&& !(p && execpath && (st.st_mode & S_ISVTX))
#endif
&& !access(p0, W_OK)) {
- rb_warn("Insecure world writable dir %s in %sPATH, mode 0%"
- PRI_MODET_PREFIX"o",
- p0, (execpath ? "" : "LOAD_"), st.st_mode);
+ rb_enc_warn(enc, "Insecure world writable dir %s in %sPATH, mode 0%"
+ PRI_MODET_PREFIX"o",
+ p0, (execpath ? "" : "LOAD_"), st.st_mode);
if (p) *p = '/';
RB_GC_GUARD(path);
return 0;
@@ -5671,7 +5986,7 @@ ruby_is_fd_loadable(int fd)
if (S_ISREG(st.st_mode))
return 1;
- if (S_ISFIFO(st.st_mode))
+ if (S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode))
return -1;
if (S_ISDIR(st.st_mode))
@@ -5813,7 +6128,7 @@ rb_find_file_safe(VALUE path, int safe_level)
if (f[0] == '~') {
tmp = file_expand_path_1(path);
if (safe_level >= 1 && OBJ_TAINTED(tmp)) {
- rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
+ rb_raise(rb_eSecurityError, "loading from unsafe file %"PRIsVALUE, tmp);
}
path = copy_path_class(tmp, path);
f = RSTRING_PTR(path);
@@ -5822,7 +6137,7 @@ rb_find_file_safe(VALUE path, int safe_level)
if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
if (safe_level >= 1 && !fpath_check(path)) {
- rb_raise(rb_eSecurityError, "loading from unsafe path %s", f);
+ rb_raise(rb_eSecurityError, "loading from unsafe path %"PRIsVALUE, path);
}
if (!rb_file_load_ok(f)) return 0;
if (!expanded)
@@ -5854,7 +6169,7 @@ rb_find_file_safe(VALUE path, int safe_level)
found:
if (safe_level >= 1 && !fpath_check(tmp)) {
- rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
+ rb_raise(rb_eSecurityError, "loading from unsafe file %"PRIsVALUE, tmp);
}
return copy_path_class(tmp, path);
@@ -5867,7 +6182,7 @@ define_filetest_function(const char *name, VALUE (*func)(ANYARGS), int argc)
rb_define_singleton_method(rb_cFile, name, func, argc);
}
-static const char null_device[] =
+const char ruby_null_device[] =
#if defined DOSISH
"NUL"
#elif defined AMIGA || defined __amigaos__
@@ -5916,6 +6231,8 @@ static const char null_device[] =
void
Init_File(void)
{
+ VALUE separator;
+
rb_mFileTest = rb_define_module("FileTest");
rb_cFile = rb_define_class("File", rb_cIO);
@@ -5965,6 +6282,7 @@ Init_File(void)
rb_define_singleton_method(rb_cFile, "chown", rb_file_s_chown, -1);
rb_define_singleton_method(rb_cFile, "lchmod", rb_file_s_lchmod, -1);
rb_define_singleton_method(rb_cFile, "lchown", rb_file_s_lchown, -1);
+ rb_define_singleton_method(rb_cFile, "lutime", rb_file_s_lutime, -1);
rb_define_singleton_method(rb_cFile, "link", rb_file_s_link, 2);
rb_define_singleton_method(rb_cFile, "symlink", rb_file_s_symlink, 2);
@@ -5985,9 +6303,10 @@ Init_File(void)
rb_define_singleton_method(rb_cFile, "extname", rb_file_s_extname, 1);
rb_define_singleton_method(rb_cFile, "path", rb_file_s_path, 1);
- separator = rb_obj_freeze(rb_usascii_str_new2("/"));
+ separator = rb_fstring_lit("/");
/* separates directory parts in path */
rb_define_const(rb_cFile, "Separator", separator);
+ /* separates directory parts in path */
rb_define_const(rb_cFile, "SEPARATOR", separator);
rb_define_singleton_method(rb_cFile, "split", rb_file_s_split, 1);
rb_define_singleton_method(rb_cFile, "join", rb_file_s_join, -2);
@@ -5999,7 +6318,7 @@ Init_File(void)
rb_define_const(rb_cFile, "ALT_SEPARATOR", Qnil);
#endif
/* path list separator */
- rb_define_const(rb_cFile, "PATH_SEPARATOR", rb_obj_freeze(rb_str_new2(PATH_SEP)));
+ rb_define_const(rb_cFile, "PATH_SEPARATOR", rb_fstring_cstr(PATH_SEP));
rb_define_method(rb_cIO, "stat", rb_io_stat, 0); /* this is IO's method */
rb_define_method(rb_cFile, "lstat", rb_file_lstat, 0);
@@ -6106,7 +6425,7 @@ Init_File(void)
rb_define_const(rb_mFConst, "LOCK_NB", INT2FIX(LOCK_NB));
/* Name of the null device */
- rb_define_const(rb_mFConst, "NULL", rb_obj_freeze(rb_usascii_str_new2(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);
diff --git a/gc.c b/gc.c
index 847062557f..0df7189191 100644
--- a/gc.c
+++ b/gc.c
@@ -14,13 +14,14 @@
#define rb_data_object_alloc rb_data_object_alloc
#define rb_data_typed_object_alloc rb_data_typed_object_alloc
-#include "internal.h"
+#include "ruby/encoding.h"
+#include "ruby/io.h"
#include "ruby/st.h"
#include "ruby/re.h"
-#include "ruby/io.h"
#include "ruby/thread.h"
#include "ruby/util.h"
#include "ruby/debug.h"
+#include "internal.h"
#include "eval_intern.h"
#include "vm_core.h"
#include "gc.h"
@@ -33,6 +34,9 @@
#include <setjmp.h>
#include <sys/types.h>
#include "ruby_assert.h"
+#include "debug_counter.h"
+#include "transient_heap.h"
+#include "mjit.h"
#undef rb_data_object_wrap
@@ -46,7 +50,9 @@
# endif
#endif
#ifdef HAVE_MALLOC_USABLE_SIZE
-# ifdef HAVE_MALLOC_H
+# ifdef RUBY_ALTERNATIVE_MALLOC_HEADER
+# include RUBY_ALTERNATIVE_MALLOC_HEADER
+# elif HAVE_MALLOC_H
# include <malloc.h>
# elif defined(HAVE_MALLOC_NP_H)
# include <malloc_np.h>
@@ -55,16 +61,6 @@
# endif
#endif
-#if /* is ASAN enabled? */ \
- __has_feature(address_sanitizer) /* Clang */ || \
- defined(__SANITIZE_ADDRESS__) /* GCC 4.8.x */
- #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \
- __attribute__((no_address_safety_analysis)) \
- __attribute__((noinline))
-#else
- #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
-#endif
-
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
@@ -72,12 +68,6 @@
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
-#if defined(__native_client__) && defined(NACL_NEWLIB)
-# include "nacl/resource.h"
-# undef HAVE_POSIX_MEMALIGN
-# undef HAVE_MEMALIGN
-
-#endif
#if defined _WIN32 || defined __CYGWIN__
#include <windows.h>
@@ -220,8 +210,18 @@ static ruby_gc_params_t gc_params = {
* 5: sweep
*/
#ifndef RGENGC_DEBUG
+#ifdef RUBY_DEVEL
+#define RGENGC_DEBUG -1
+#else
#define RGENGC_DEBUG 0
#endif
+#endif
+#if RGENGC_DEBUG < 0 && !defined(_MSC_VER)
+# define RGENGC_DEBUG_ENABLED(level) (-(RGENGC_DEBUG) >= (level) && ruby_rgengc_debug >= (level))
+#else
+# define RGENGC_DEBUG_ENABLED(level) ((RGENGC_DEBUG) >= (level))
+#endif
+int ruby_rgengc_debug;
/* RGENGC_CHECK_MODE
* 0: disable all assertions
@@ -235,6 +235,12 @@ static ruby_gc_params_t gc_params = {
#define RGENGC_CHECK_MODE 0
#endif
+#if RGENGC_CHECK_MODE > 0
+#define GC_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(RGENGC_CHECK_MODE > 0, expr, #expr)
+#else
+#define GC_ASSERT(expr) ((void)0)
+#endif
+
/* RGENGC_OLD_NEWOBJ_CHECK
* 0: disable all assertions
* >0: make a OLD object when new object creation.
@@ -300,7 +306,7 @@ static ruby_gc_params_t gc_params = {
#define GC_ENABLE_LAZY_SWEEP 1
#endif
#ifndef CALC_EXACT_MALLOC_SIZE
-#define CALC_EXACT_MALLOC_SIZE 0
+#define CALC_EXACT_MALLOC_SIZE USE_GC_MALLOC_OBJ_INFO_DETAILS
#endif
#if defined(HAVE_MALLOC_USABLE_SIZE) || CALC_EXACT_MALLOC_SIZE > 0
#ifndef MALLOC_ALLOCATED_SIZE
@@ -342,7 +348,9 @@ typedef enum {
/* others */
GPR_FLAG_IMMEDIATE_SWEEP = 0x2000,
- GPR_FLAG_HAVE_FINALIZE = 0x4000
+ GPR_FLAG_HAVE_FINALIZE = 0x4000,
+ GPR_FLAG_IMMEDIATE_MARK = 0x8000,
+ GPR_FLAG_FULL_MARK = 0x10000
} gc_profile_record_flag;
typedef struct gc_profile_record {
@@ -409,7 +417,6 @@ typedef struct RVALUE {
struct RStruct rstruct;
struct RBignum bignum;
struct RFile file;
- struct RNode node;
struct RMatch match;
struct RRational rational;
struct RComplex complex;
@@ -422,6 +429,8 @@ typedef struct RVALUE {
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;
@@ -482,8 +491,8 @@ typedef struct rb_heap_struct {
struct heap_page *free_pages;
struct heap_page *using_page;
- struct heap_page *pages;
- struct heap_page *sweep_pages;
+ struct list_head pages;
+ struct heap_page *sweeping_page; /* iterator for .pages */
#if GC_ENABLE_INCREMENTAL_MARK
struct heap_page *pooled_pages;
#endif
@@ -640,10 +649,8 @@ typedef struct rb_objspace {
} rb_objspace_t;
-#ifndef HEAP_PAGE_ALIGN_LOG
/* default tiny heap size: 16KB */
#define HEAP_PAGE_ALIGN_LOG 14
-#endif
#define CEILDIV(i, mod) (((i) + (mod) - 1)/(mod))
enum {
HEAP_PAGE_ALIGN = (1UL << HEAP_PAGE_ALIGN_LOG),
@@ -657,7 +664,6 @@ enum {
};
struct heap_page {
- struct heap_page *prev;
short total_slots;
short free_slots;
short final_slots;
@@ -671,7 +677,7 @@ struct heap_page {
struct heap_page *free_next;
RVALUE *start;
RVALUE *freelist;
- struct heap_page *next;
+ struct list_node page_node;
#if USE_RGENGC
bits_t wb_unprotected_bits[HEAP_PAGE_BITMAP_LIMIT];
@@ -706,6 +712,10 @@ struct heap_page {
#define GET_HEAP_MARKING_BITS(x) (&GET_HEAP_PAGE(x)->marking_bits[0])
#endif
+#ifndef ENABLE_VM_OBJSPACE
+# define ENABLE_VM_OBJSPACE 1
+#endif
+
/* Aliases */
#if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
#define rb_objspace (*rb_objspace_of(GET_VM()))
@@ -782,7 +792,7 @@ gc_mode_verify(enum gc_mode mode)
#else
#define will_be_incremental_marking(objspace) FALSE
#endif
-#define has_sweeping_pages(heap) ((heap)->sweep_pages != 0)
+#define has_sweeping_pages(heap) ((heap)->sweeping_page != 0)
#define is_lazy_sweeping(heap) (GC_ENABLE_LAZY_SWEEP && has_sweeping_pages(heap))
#if SIZEOF_LONG == SIZEOF_VOIDP
@@ -809,7 +819,9 @@ struct RZombie {
#define nomem_error GET_VM()->special_exceptions[ruby_error_nomemory]
+#if RUBY_MARK_FREE_DEBUG
int ruby_gc_debug_indent = 0;
+#endif
VALUE rb_mGC;
int ruby_disable_gc = 0;
@@ -822,16 +834,14 @@ static void rb_objspace_call_finalizer(rb_objspace_t *objspace);
static VALUE define_final0(VALUE obj, VALUE block);
static void negative_size_allocation_error(const char *);
-static void *aligned_malloc(size_t, size_t);
-static void aligned_free(void *);
static void init_mark_stack(mark_stack_t *stack);
static int ready_to_gc(rb_objspace_t *objspace);
-static int garbage_collect(rb_objspace_t *, int full_mark, int immediate_mark, int immediate_sweep, int reason);
+static int garbage_collect(rb_objspace_t *, int reason);
-static int gc_start(rb_objspace_t *objspace, const int full_mark, const int immediate_mark, const unsigned int immediate_sweep, int reason);
+static int gc_start(rb_objspace_t *objspace, int reason);
static void gc_rest(rb_objspace_t *objspace);
static inline void gc_enter(rb_objspace_t *objspace, const char *event);
static inline void gc_exit(rb_objspace_t *objspace, const char *event);
@@ -840,23 +850,19 @@ static void gc_marks(rb_objspace_t *objspace, int full_mark);
static void gc_marks_start(rb_objspace_t *objspace, int full);
static int gc_marks_finish(rb_objspace_t *objspace);
static void gc_marks_rest(rb_objspace_t *objspace);
-#if GC_ENABLE_INCREMENTAL_MARK
static void gc_marks_step(rb_objspace_t *objspace, int slots);
static void gc_marks_continue(rb_objspace_t *objspace, rb_heap_t *heap);
-#endif
static void gc_sweep(rb_objspace_t *objspace);
static void gc_sweep_start(rb_objspace_t *objspace);
static void gc_sweep_finish(rb_objspace_t *objspace);
static int gc_sweep_step(rb_objspace_t *objspace, rb_heap_t *heap);
static void gc_sweep_rest(rb_objspace_t *objspace);
-#if GC_ENABLE_LAZY_SWEEP
static void gc_sweep_continue(rb_objspace_t *objspace, rb_heap_t *heap);
-#endif
static inline void gc_mark(rb_objspace_t *objspace, VALUE ptr);
static void gc_mark_ptr(rb_objspace_t *objspace, VALUE ptr);
-static void gc_mark_maybe(rb_objspace_t *objspace, VALUE ptr);
+NO_SANITIZE("memory", static void gc_mark_maybe(rb_objspace_t *objspace, VALUE ptr));
static void gc_mark_children(rb_objspace_t *objspace, VALUE ptr);
static int gc_mark_stacked_objects_incremental(rb_objspace_t *, size_t count);
@@ -864,7 +870,7 @@ static int gc_mark_stacked_objects_all(rb_objspace_t *);
static void gc_grey(rb_objspace_t *objspace, VALUE ptr);
static inline int gc_mark_set(rb_objspace_t *objspace, VALUE obj);
-static inline int is_pointer_to_heap(rb_objspace_t *objspace, void *ptr);
+NO_SANITIZE("memory", static inline int is_pointer_to_heap(rb_objspace_t *objspace, void *ptr));
static void push_mark_stack(mark_stack_t *, VALUE);
static int pop_mark_stack(mark_stack_t *, VALUE *);
@@ -893,10 +899,10 @@ static inline void gc_prof_set_heap_info(rb_objspace_t *);
#define gc_prof_enabled(objspace) ((objspace)->profile.run && (objspace)->profile.current_record)
#ifdef HAVE_VA_ARGS_MACRO
-# define gc_report(level, objspace, fmt, ...) \
- if ((level) > RGENGC_DEBUG) {} else gc_report_body(level, objspace, fmt, ##__VA_ARGS__)
+# define gc_report(level, objspace, ...) \
+ if (!RGENGC_DEBUG_ENABLED(level)) {} else gc_report_body(level, objspace, __VA_ARGS__)
#else
-# define gc_report if (!(RGENGC_DEBUG)) {} else gc_report_body
+# define gc_report if (!RGENGC_DEBUG_ENABLED(0)) {} else gc_report_body
#endif
PRINTF_ARGS(static void gc_report_body(int level, rb_objspace_t *objspace, const char *fmt, ...), 3, 4);
static const char *obj_info(VALUE obj);
@@ -1006,9 +1012,12 @@ tick(void)
#define MEASURE_LINE(expr) expr
#endif /* USE_TICK_T */
-#define FL_TEST2(x,f) ((RGENGC_CHECK_MODE && SPECIAL_CONST_P(x)) ? (rb_bug("FL_TEST2: SPECIAL_CONST (%p)", (void *)(x)), 0) : FL_TEST_RAW((x),(f)) != 0)
-#define FL_SET2(x,f) do {if (RGENGC_CHECK_MODE && SPECIAL_CONST_P(x)) rb_bug("FL_SET2: SPECIAL_CONST"); RBASIC(x)->flags |= (f);} while (0)
-#define FL_UNSET2(x,f) do {if (RGENGC_CHECK_MODE && SPECIAL_CONST_P(x)) rb_bug("FL_UNSET2: SPECIAL_CONST"); RBASIC(x)->flags &= ~(f);} while (0)
+#define FL_CHECK2(name, x, pred) \
+ ((RGENGC_CHECK_MODE && SPECIAL_CONST_P(x)) ? \
+ (rb_bug(name": SPECIAL_CONST (%p)", (void *)(x)), 0) : (pred))
+#define FL_TEST2(x,f) FL_CHECK2("FL_TEST2", x, FL_TEST_RAW((x),(f)) != 0)
+#define FL_SET2(x,f) FL_CHECK2("FL_SET2", x, RBASIC(x)->flags |= (f))
+#define FL_UNSET2(x,f) FL_CHECK2("FL_UNSET2", x, RBASIC(x)->flags &= ~(f))
#define RVALUE_MARK_BITMAP(obj) MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(obj), (obj))
#define RVALUE_PAGE_MARKED(page, obj) MARKED_IN_BITMAP((page)->mark_bits, (obj))
@@ -1085,7 +1094,7 @@ check_rvalue_consistency(const VALUE obj)
rb_bug("check_rvalue_consistency: %s is uncollectible, but not old (age: %d) and not WB unprotected.", obj_info(obj), age);
}
if (remembered_bit && age != RVALUE_OLD_AGE) {
- rb_bug("check_rvalue_consistency: %s is rememberd, but not old (age: %d).", obj_info(obj), age);
+ rb_bug("check_rvalue_consistency: %s is remembered, but not old (age: %d).", obj_info(obj), age);
}
}
@@ -1168,6 +1177,7 @@ RVALUE_PAGE_OLD_UNCOLLECTIBLE_SET(rb_objspace_t *objspace, struct heap_page *pag
{
MARK_IN_BITMAP(&page->uncollectible_bits[0], obj);
objspace->rgengc.old_objects++;
+ rb_transient_heap_promote(obj);
#if RGENGC_PROFILE >= 2
objspace->profile.total_promoted_count++;
@@ -1178,6 +1188,7 @@ RVALUE_PAGE_OLD_UNCOLLECTIBLE_SET(rb_objspace_t *objspace, struct heap_page *pag
static inline void
RVALUE_OLD_UNCOLLECTIBLE_SET(rb_objspace_t *objspace, VALUE obj)
{
+ RB_DEBUG_COUNTER_INC(obj_promote);
RVALUE_PAGE_OLD_UNCOLLECTIBLE_SET(objspace, GET_HEAP_PAGE(obj), obj);
}
@@ -1214,7 +1225,7 @@ static inline void
RVALUE_AGE_SET_OLD(rb_objspace_t *objspace, VALUE obj)
{
check_rvalue_consistency(obj);
- if (RGENGC_CHECK_MODE) assert(!RVALUE_OLD_P(obj));
+ GC_ASSERT(!RVALUE_OLD_P(obj));
RBASIC(obj)->flags = RVALUE_FLAGS_AGE_SET(RBASIC(obj)->flags, RVALUE_OLD_AGE);
RVALUE_OLD_UNCOLLECTIBLE_SET(objspace, obj);
@@ -1227,7 +1238,7 @@ static inline void
RVALUE_AGE_SET_CANDIDATE(rb_objspace_t *objspace, VALUE obj)
{
check_rvalue_consistency(obj);
- if (RGENGC_CHECK_MODE) assert(!RVALUE_OLD_P(obj));
+ GC_ASSERT(!RVALUE_OLD_P(obj));
RBASIC(obj)->flags = RVALUE_FLAGS_AGE_SET(RBASIC(obj)->flags, RVALUE_OLD_AGE - 1);
@@ -1245,7 +1256,7 @@ static inline void
RVALUE_DEMOTE(rb_objspace_t *objspace, VALUE obj)
{
check_rvalue_consistency(obj);
- if (RGENGC_CHECK_MODE) assert(RVALUE_OLD_P(obj));
+ GC_ASSERT(RVALUE_OLD_P(obj));
if (!is_incremental_marking(objspace) && RVALUE_REMEMBERED(obj)) {
CLEAR_IN_BITMAP(GET_HEAP_MARKING_BITS(obj), obj);
@@ -1270,7 +1281,8 @@ static inline void
RVALUE_AGE_RESET(VALUE obj)
{
check_rvalue_consistency(obj);
- if (RGENGC_CHECK_MODE) assert(!RVALUE_OLD_P(obj));
+ GC_ASSERT(!RVALUE_OLD_P(obj));
+
RVALUE_AGE_RESET_RAW(obj);
check_rvalue_consistency(obj);
}
@@ -1310,6 +1322,8 @@ rb_objspace_alloc(void)
rb_objspace_t *objspace = &rb_objspace;
#endif
malloc_limit = gc_params.malloc_limit_min;
+ list_head_init(&objspace->eden_heap.pages);
+ list_head_init(&objspace->tomb_heap.pages);
return objspace;
}
@@ -1348,7 +1362,6 @@ rb_objspace_free(rb_objspace_t *objspace)
objspace->eden_heap.total_pages = 0;
objspace->eden_heap.total_slots = 0;
- objspace->eden_heap.pages = NULL;
}
free_stack_chunks(&objspace->mark_stack);
#if !(defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE)
@@ -1358,34 +1371,56 @@ rb_objspace_free(rb_objspace_t *objspace)
}
static void
+heap_pages_expand_sorted_to(rb_objspace_t *objspace, size_t next_length)
+{
+ struct heap_page **sorted;
+ size_t size = next_length * sizeof(struct heap_page *);
+
+ gc_report(3, objspace, "heap_pages_expand_sorted: next_length: %d, size: %d\n", (int)next_length, (int)size);
+
+ if (heap_pages_sorted_length > 0) {
+ 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);
+ }
+
+ if (sorted == 0) {
+ rb_memerror();
+ }
+
+ heap_pages_sorted_length = next_length;
+}
+
+static void
heap_pages_expand_sorted(rb_objspace_t *objspace)
{
+ /* usually heap_allocatable_pages + heap_eden->total_pages == heap_pages_sorted_length
+ * because heap_allocatable_pages contains heap_tomb->total_pages (recycle heap_tomb pages).
+ * however, if there are pages which do not have empty slots, then try to create new pages
+ * so that the additional allocatable_pages counts (heap_tomb->total_pages) are added.
+ */
size_t next_length = heap_allocatable_pages;
next_length += heap_eden->total_pages;
next_length += heap_tomb->total_pages;
if (next_length > heap_pages_sorted_length) {
- struct heap_page **sorted;
- size_t size = next_length * sizeof(struct heap_page *);
-
- gc_report(3, objspace, "heap_pages_expand_sorted: next_length: %d, size: %d\n", (int)next_length, (int)size);
-
- if (heap_pages_sorted_length > 0) {
- 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);
- }
+ heap_pages_expand_sorted_to(objspace, next_length);
+ }
- if (sorted == 0) {
- rb_memerror();
- }
+ GC_ASSERT(heap_allocatable_pages + heap_eden->total_pages <= heap_pages_sorted_length);
+ GC_ASSERT(heap_allocated_pages <= heap_pages_sorted_length);
+}
- heap_pages_sorted_length = next_length;
- }
+static void
+heap_allocatable_pages_set(rb_objspace_t *objspace, size_t s)
+{
+ heap_allocatable_pages = s;
+ heap_pages_expand_sorted(objspace);
}
+
static inline void
heap_page_add_freeobj(rb_objspace_t *objspace, struct heap_page *page, VALUE obj)
{
@@ -1395,8 +1430,9 @@ heap_page_add_freeobj(rb_objspace_t *objspace, struct heap_page *page, VALUE obj
page->freelist = p;
if (RGENGC_CHECK_MODE && !is_pointer_to_heap(objspace, p)) {
- rb_bug("heap_page_add_freeobj: %p is not rvalue.", p);
+ rb_bug("heap_page_add_freeobj: %p is not rvalue.", (void *)p);
}
+ poison_object(obj);
gc_report(3, objspace, "heap_page_add_freeobj: add %p to freelist\n", (void *)obj);
}
@@ -1429,11 +1465,7 @@ heap_add_poolpage(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *pa
static void
heap_unlink_page(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *page)
{
- if (page->prev) page->prev->next = page->next;
- if (page->next) page->next->prev = page->prev;
- if (heap->pages == page) heap->pages = page->next;
- page->prev = NULL;
- page->next = NULL;
+ list_del(&page->page_node);
heap->total_pages--;
heap->total_slots -= page->total_slots;
}
@@ -1443,7 +1475,7 @@ heap_page_free(rb_objspace_t *objspace, struct heap_page *page)
{
heap_allocated_pages--;
objspace->profile.total_freed_pages++;
- aligned_free(GET_PAGE_BODY(page->start));
+ rb_aligned_free(GET_PAGE_BODY(page->start));
free(page);
}
@@ -1452,7 +1484,7 @@ heap_pages_free_unused_pages(rb_objspace_t *objspace)
{
size_t i, j;
- if (heap_tomb->pages) {
+ if (!list_empty(&heap_tomb->pages)) {
for (i = j = 1; j < heap_allocated_pages; i++) {
struct heap_page *page = heap_pages_sorted[i];
@@ -1467,7 +1499,7 @@ heap_pages_free_unused_pages(rb_objspace_t *objspace)
j++;
}
}
- if (RGENGC_CHECK_MODE) assert(j == heap_allocated_pages);
+ GC_ASSERT(j == heap_allocated_pages);
}
}
@@ -1481,7 +1513,7 @@ heap_page_allocate(rb_objspace_t *objspace)
int limit = HEAP_PAGE_OBJ_LIMIT;
/* assign heap_page body (contains heap_page_header and RVALUEs) */
- page_body = (struct heap_page_body *)aligned_malloc(HEAP_PAGE_ALIGN, HEAP_PAGE_SIZE);
+ page_body = (struct heap_page_body *)rb_aligned_malloc(HEAP_PAGE_ALIGN, HEAP_PAGE_SIZE);
if (page_body == 0) {
rb_memerror();
}
@@ -1489,7 +1521,7 @@ heap_page_allocate(rb_objspace_t *objspace)
/* assign heap_page entry */
page = (struct heap_page *)calloc(1, sizeof(struct heap_page));
if (page == 0) {
- aligned_free(page_body);
+ rb_aligned_free(page_body);
rb_memerror();
}
@@ -1520,6 +1552,7 @@ heap_page_allocate(rb_objspace_t *objspace)
rb_bug("same heap page is allocated: %p at %"PRIuVALUE, (void *)page_body, (VALUE)mid);
}
}
+
if (hi < heap_allocated_pages) {
MEMMOVE(&heap_pages_sorted[hi+1], &heap_pages_sorted[hi], struct heap_page_header*, heap_allocated_pages - hi);
}
@@ -1527,9 +1560,17 @@ heap_page_allocate(rb_objspace_t *objspace)
heap_pages_sorted[hi] = page;
heap_allocated_pages++;
+
+ GC_ASSERT(heap_eden->total_pages + heap_allocatable_pages <= heap_pages_sorted_length);
+ GC_ASSERT(heap_eden->total_pages + heap_tomb->total_pages == heap_allocated_pages - 1);
+ GC_ASSERT(heap_allocated_pages <= heap_pages_sorted_length);
+
objspace->profile.total_allocated_pages++;
- if (RGENGC_CHECK_MODE) assert(heap_allocated_pages <= heap_pages_sorted_length);
+ if (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;
if (heap_pages_himem < end) heap_pages_himem = end;
@@ -1539,7 +1580,7 @@ heap_page_allocate(rb_objspace_t *objspace)
page_body->header.page = page;
for (p = start; p != end; p++) {
- gc_report(3, objspace, "assign_heap_page: %p is added to freelist\n", 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;
@@ -1550,14 +1591,13 @@ heap_page_allocate(rb_objspace_t *objspace)
static struct heap_page *
heap_page_resurrect(rb_objspace_t *objspace)
{
- struct heap_page *page = heap_tomb->pages;
+ struct heap_page *page = 0, *next;
- while (page) {
+ list_for_each_safe(&heap_tomb->pages, page, next, page_node) {
if (page->freelist != NULL) {
heap_unlink_page(objspace, heap_tomb, page);
return page;
}
- page = page->next;
}
return NULL;
@@ -1566,14 +1606,19 @@ heap_page_resurrect(rb_objspace_t *objspace)
static struct heap_page *
heap_page_create(rb_objspace_t *objspace)
{
- struct heap_page *page = heap_page_resurrect(objspace);
+ struct heap_page *page;
const char *method = "recycle";
+
+ heap_allocatable_pages--;
+
+ page = heap_page_resurrect(objspace);
+
if (page == NULL) {
page = heap_page_allocate(objspace);
method = "allocate";
}
if (0) fprintf(stderr, "heap_page_create: %s - %p, heap_allocated_pages: %d, heap_allocated_pages: %d, tomb->total_pages: %d\n",
- method, page, (int)heap_pages_sorted_length, (int)heap_allocated_pages, (int)heap_tomb->total_pages);
+ method, (void *)page, (int)heap_pages_sorted_length, (int)heap_allocated_pages, (int)heap_tomb->total_pages);
return page;
}
@@ -1581,9 +1626,7 @@ static void
heap_add_page(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *page)
{
page->flags.in_tomb = (heap == heap_tomb);
- page->next = heap->pages;
- if (heap->pages) heap->pages->prev = page;
- heap->pages = page;
+ list_add(&heap->pages, &page->page_node);
heap->total_pages++;
heap->total_slots += page->total_slots;
}
@@ -1601,12 +1644,13 @@ heap_add_pages(rb_objspace_t *objspace, rb_heap_t *heap, size_t add)
{
size_t i;
- heap_allocatable_pages = add;
- heap_pages_expand_sorted(objspace);
+ heap_allocatable_pages_set(objspace, add);
+
for (i = 0; i < add; i++) {
heap_assign_page(objspace, heap);
}
- heap_allocatable_pages = 0;
+
+ GC_ASSERT(heap_allocatable_pages == 0);
}
static size_t
@@ -1656,8 +1700,7 @@ heap_set_increment(rb_objspace_t *objspace, size_t additional_pages)
if (next_used_limit == heap_allocated_pages) next_used_limit++;
- heap_allocatable_pages = next_used_limit - used;
- heap_pages_expand_sorted(objspace);
+ heap_allocatable_pages_set(objspace, next_used_limit - used);
gc_report(1, objspace, "heap_set_increment: heap_allocatable_pages is %d\n", (int)heap_allocatable_pages);
}
@@ -1668,7 +1711,10 @@ heap_increment(rb_objspace_t *objspace, rb_heap_t *heap)
if (heap_allocatable_pages > 0) {
gc_report(1, objspace, "heap_increment: heap_pages_sorted_length: %d, heap_pages_inc: %d, heap->total_pages: %d\n",
(int)heap_pages_sorted_length, (int)heap_allocatable_pages, (int)heap->total_pages);
- heap_allocatable_pages--;
+
+ GC_ASSERT(heap_allocatable_pages + heap_eden->total_pages <= heap_pages_sorted_length);
+ GC_ASSERT(heap_allocated_pages <= heap_pages_sorted_length);
+
heap_assign_page(objspace, heap);
return TRUE;
}
@@ -1678,22 +1724,18 @@ heap_increment(rb_objspace_t *objspace, rb_heap_t *heap)
static void
heap_prepare(rb_objspace_t *objspace, rb_heap_t *heap)
{
- if (RGENGC_CHECK_MODE) assert(heap->free_pages == NULL);
+ GC_ASSERT(heap->free_pages == NULL);
-#if GC_ENABLE_LAZY_SWEEP
if (is_lazy_sweeping(heap)) {
gc_sweep_continue(objspace, heap);
}
-#endif
-#if GC_ENABLE_INCREMENTAL_MARK
else if (is_incremental_marking(objspace)) {
gc_marks_continue(objspace, heap);
}
-#endif
if (heap->free_pages == NULL &&
(will_be_incremental_marking(objspace) || heap_increment(objspace, heap) == FALSE) &&
- gc_start(objspace, FALSE, FALSE, FALSE, GPR_FLAG_NEWOBJ) == FALSE) {
+ gc_start(objspace, GPR_FLAG_NEWOBJ) == FALSE) {
rb_memerror();
}
}
@@ -1711,10 +1753,11 @@ heap_get_freeobj_from_next_freepage(rb_objspace_t *objspace, rb_heap_t *heap)
heap->free_pages = page->free_next;
heap->using_page = page;
- if (RGENGC_CHECK_MODE) assert(page->free_slots != 0);
+ GC_ASSERT(page->free_slots != 0);
p = page->freelist;
page->freelist = NULL;
page->free_slots = 0;
+ unpoison_object((VALUE)p, true);
return p;
}
@@ -1725,6 +1768,7 @@ heap_get_freeobj_head(rb_objspace_t *objspace, rb_heap_t *heap)
if (LIKELY(p != NULL)) {
heap->freelist = p->as.free.next;
}
+ unpoison_object((VALUE)p, true);
return (VALUE)p;
}
@@ -1735,6 +1779,7 @@ heap_get_freeobj(rb_objspace_t *objspace, rb_heap_t *heap)
while (1) {
if (LIKELY(p != NULL)) {
+ unpoison_object((VALUE)p, true);
heap->freelist = p->as.free.next;
return (VALUE)p;
}
@@ -1753,9 +1798,15 @@ rb_objspace_set_event_hook(const rb_event_flag_t event)
}
static void
-gc_event_hook_body(rb_thread_t *th, rb_objspace_t *objspace, const rb_event_flag_t event, VALUE data)
+gc_event_hook_body(rb_execution_context_t *ec, rb_objspace_t *objspace, const rb_event_flag_t event, VALUE data)
{
- EXEC_EVENT_HOOK(th, event, th->cfp->self, 0, 0, 0, data);
+ const VALUE *pc = ec->cfp->pc;
+ if (pc && VM_FRAME_RUBYFRAME_P(ec->cfp)) {
+ /* increment PC because source line is calculated with PC-1 */
+ ec->cfp->pc++;
+ }
+ EXEC_EVENT_HOOK(ec, event, ec->cfp->self, 0, 0, 0, data);
+ ec->cfp->pc = pc;
}
#define gc_event_hook_available_p(objspace) ((objspace)->flags.has_hook)
@@ -1763,17 +1814,15 @@ gc_event_hook_body(rb_thread_t *th, rb_objspace_t *objspace, const rb_event_flag
#define gc_event_hook(objspace, event, data) do { \
if (UNLIKELY(gc_event_hook_needed_p(objspace, event))) { \
- gc_event_hook_body(GET_THREAD(), (objspace), (event), (data)); \
+ gc_event_hook_body(GET_EC(), (objspace), (event), (data)); \
} \
} while (0)
static inline VALUE
newobj_init(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, int wb_protected, rb_objspace_t *objspace, VALUE obj)
{
- if (RGENGC_CHECK_MODE > 0) {
- assert(BUILTIN_TYPE(obj) == T_NONE);
- assert((flags & FL_WB_PROTECTED) == 0);
- }
+ GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE);
+ GC_ASSERT((flags & FL_WB_PROTECTED) == 0);
/* OBJSETUP */
RBASIC(obj)->flags = flags;
@@ -1783,10 +1832,10 @@ newobj_init(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, int wb_prote
RANY(obj)->as.values.v3 = v3;
#if RGENGC_CHECK_MODE
- assert(RVALUE_MARKED(obj) == FALSE);
- assert(RVALUE_MARKING(obj) == FALSE);
- assert(RVALUE_OLD_P(obj) == FALSE);
- assert(RVALUE_WB_UNPROTECTED(obj) == FALSE);
+ GC_ASSERT(RVALUE_MARKED(obj) == FALSE);
+ GC_ASSERT(RVALUE_MARKING(obj) == FALSE);
+ GC_ASSERT(RVALUE_OLD_P(obj) == FALSE);
+ GC_ASSERT(RVALUE_WB_UNPROTECTED(obj) == FALSE);
if (flags & FL_PROMOTED1) {
if (RVALUE_AGE(obj) != 2) rb_bug("newobj: %s of age (%d) != 2.", obj_info(obj), RVALUE_AGE(obj));
@@ -1819,8 +1868,8 @@ newobj_init(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, int wb_prote
#endif
#if GC_DEBUG
- RANY(obj)->file = rb_source_loc(&RANY(obj)->line);
- assert(!SPECIAL_CONST_P(obj)); /* check alignment */
+ RANY(obj)->file = rb_source_location_cstr(&RANY(obj)->line);
+ GC_ASSERT(!SPECIAL_CONST_P(obj)); /* check alignment */
#endif
objspace->total_allocated_objects++;
@@ -1862,7 +1911,7 @@ newobj_slowpath(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, rb_objsp
}
if (ruby_gc_stressful) {
- if (!garbage_collect(objspace, FALSE, FALSE, FALSE, GPR_FLAG_NEWOBJ)) {
+ if (!garbage_collect(objspace, GPR_FLAG_NEWOBJ)) {
rb_memerror();
}
}
@@ -1895,13 +1944,15 @@ newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, int wb_protect
rb_objspace_t *objspace = &rb_objspace;
VALUE obj;
+ RB_DEBUG_COUNTER_INC(obj_newobj);
+ (void)RB_DEBUG_COUNTER_INC_IF(obj_newobj_wb_unprotected, !wb_protected);
+
#if GC_DEBUG_STRESS_TO_CLASS
if (UNLIKELY(stress_to_class)) {
- long i, cnt = RARRAY_LEN(stress_to_class);
- const VALUE *ptr = RARRAY_CONST_PTR(stress_to_class);
- for (i = 0; i < cnt; ++i) {
- if (klass == ptr[i]) rb_memerror();
- }
+ long i, cnt = RARRAY_LEN(stress_to_class);
+ for (i = 0; i < cnt; ++i) {
+ if (klass == RARRAY_AREF(stress_to_class, i)) rb_memerror();
+ }
}
#endif
if (!(during_gc ||
@@ -1911,6 +1962,8 @@ newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, int wb_protect
return newobj_init(klass, flags, v1, v2, v3, wb_protected, objspace, obj);
}
else {
+ RB_DEBUG_COUNTER_INC(obj_newobj_slowpath);
+
return wb_protected ?
newobj_slowpath_wb_protected(klass, flags, v1, v2, v3, objspace) :
newobj_slowpath_wb_unprotected(klass, flags, v1, v2, v3, objspace);
@@ -1920,14 +1973,14 @@ newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, int wb_protect
VALUE
rb_wb_unprotected_newobj_of(VALUE klass, VALUE flags)
{
- if (RGENGC_CHECK_MODE > 0) assert((flags & FL_WB_PROTECTED) == 0);
+ GC_ASSERT((flags & FL_WB_PROTECTED) == 0);
return newobj_of(klass, flags, 0, 0, 0, FALSE);
}
VALUE
rb_wb_protected_newobj_of(VALUE klass, VALUE flags)
{
- if (RGENGC_CHECK_MODE > 0) assert((flags & FL_WB_PROTECTED) == 0);
+ GC_ASSERT((flags & FL_WB_PROTECTED) == 0);
return newobj_of(klass, flags, 0, 0, 0, TRUE);
}
@@ -1945,13 +1998,9 @@ rb_newobj_of(VALUE klass, VALUE flags)
return newobj_of(klass, flags & ~FL_WB_PROTECTED, 0, 0, 0, flags & FL_WB_PROTECTED);
}
-NODE*
-rb_node_newnode(enum node_type type, VALUE a0, VALUE a1, VALUE a2)
-{
- NODE *n = (NODE *)newobj_of(0, T_NODE, a0, a1, a2, FALSE); /* TODO: node also should be wb protected */
- nd_set_type(n, type);
- return n;
-}
+#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)
#undef rb_imemo_new
@@ -1962,12 +2011,37 @@ rb_imemo_new(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0)
return newobj_of(v0, flags, v1, v2, v3, TRUE);
}
+static VALUE
+rb_imemo_tmpbuf_new(VALUE v1, VALUE v2, VALUE v3, VALUE v0)
+{
+ VALUE flags = T_IMEMO | (imemo_tmpbuf << FL_USHIFT);
+ return newobj_of(v0, flags, v1, v2, v3, FALSE);
+}
+
+VALUE
+rb_imemo_tmpbuf_auto_free_pointer(void *buf)
+{
+ return rb_imemo_new(imemo_tmpbuf, (VALUE)buf, 0, 0, 0);
+}
+
+VALUE
+rb_imemo_tmpbuf_auto_free_maybe_mark_buffer(void *buf, size_t cnt)
+{
+ return rb_imemo_tmpbuf_new((VALUE)buf, 0, (VALUE)cnt, 0);
+}
+
+rb_imemo_tmpbuf_t *
+rb_imemo_tmpbuf_parser_heap(void *buf, rb_imemo_tmpbuf_t *old_heap, size_t cnt)
+{
+ return (rb_imemo_tmpbuf_t *)rb_imemo_tmpbuf_new((VALUE)buf, (VALUE)old_heap, (VALUE)cnt, 0);
+}
+
#if IMEMO_DEBUG
VALUE
rb_imemo_new_debug(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0, const char *file, int line)
{
VALUE memo = rb_imemo_new(type, v1, v2, v3, v0);
- fprintf(stderr, "memo %p (type: %d) @ %s:%d\n", memo, imemo_type(memo), file, line);
+ fprintf(stderr, "memo %p (type: %d) @ %s:%d\n", (void *)memo, imemo_type(memo), file, line);
return memo;
}
#endif
@@ -2103,6 +2177,8 @@ make_io_zombie(rb_objspace_t *objspace, VALUE obj)
static int
obj_free(rb_objspace_t *objspace, VALUE obj)
{
+ RB_DEBUG_COUNTER_INC(obj_free);
+
gc_event_hook(objspace, RUBY_INTERNAL_EVENT_FREEOBJ, obj);
switch (BUILTIN_TYPE(obj)) {
@@ -2134,13 +2210,21 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
switch (BUILTIN_TYPE(obj)) {
case T_OBJECT:
- if (!(RANY(obj)->as.basic.flags & ROBJECT_EMBED) &&
- RANY(obj)->as.object.as.heap.ivptr) {
- xfree(RANY(obj)->as.object.as.heap.ivptr);
- }
- break;
+ if ((RANY(obj)->as.basic.flags & ROBJECT_EMBED) ||
+ RANY(obj)->as.object.as.heap.ivptr == NULL) {
+ RB_DEBUG_COUNTER_INC(obj_obj_embed);
+ }
+ else if (ROBJ_TRANSIENT_P(obj)) {
+ RB_DEBUG_COUNTER_INC(obj_obj_transient);
+ }
+ else {
+ xfree(RANY(obj)->as.object.as.heap.ivptr);
+ RB_DEBUG_COUNTER_INC(obj_obj_ptr);
+ }
+ break;
case T_MODULE:
case T_CLASS:
+ mjit_remove_class_serial(RCLASS_SERIAL(obj));
rb_id_table_free(RCLASS_M_TBL(obj));
if (RCLASS_IV_TBL(obj)) {
st_free_table(RCLASS_IV_TBL(obj));
@@ -2165,21 +2249,59 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
if (RANY(obj)->as.klass.ptr)
xfree(RANY(obj)->as.klass.ptr);
RANY(obj)->as.klass.ptr = NULL;
+
+ (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;
case T_STRING:
rb_str_free(obj);
break;
case T_ARRAY:
- rb_ary_free(obj);
+ rb_ary_free(obj);
break;
case T_HASH:
- if (RANY(obj)->as.hash.ntbl) {
- st_free_table(RANY(obj)->as.hash.ntbl);
- }
+#if USE_DEBUG_COUNTER
+ if (RHASH_SIZE(obj) >= 8) {
+ RB_DEBUG_COUNTER_INC(obj_hash_ge8);
+ }
+ else if (RHASH_SIZE(obj) >= 4) {
+ RB_DEBUG_COUNTER_INC(obj_hash_ge4);
+ }
+ else if (RHASH_SIZE(obj) >= 1) {
+ RB_DEBUG_COUNTER_INC(obj_hash_under4);
+ }
+ else {
+ RB_DEBUG_COUNTER_INC(obj_hash_empty);
+ }
+
+ if (RHASH_AR_TABLE_P(obj)) {
+ RB_DEBUG_COUNTER_INC(obj_hash_ar);
+ }
+ else {
+ RB_DEBUG_COUNTER_INC(obj_hash_st);
+ }
+#endif
+ if (/* RHASH_AR_TABLE_P(obj) */ !FL_TEST_RAW(obj, RHASH_ST_TABLE_FLAG)) {
+ ar_table *tab = RHASH(obj)->as.ar;
+
+ if (tab) {
+ if (RHASH_TRANSIENT_P(obj)) {
+ RB_DEBUG_COUNTER_INC(obj_hash_transient);
+ }
+ else {
+ ruby_xfree(tab);
+ }
+ }
+ }
+ else {
+ GC_ASSERT(RHASH_ST_TABLE_P(obj));
+ st_free_table(RHASH(obj)->as.st);
+ }
break;
case T_REGEXP:
if (RANY(obj)->as.regexp.ptr) {
onig_free(RANY(obj)->as.regexp.ptr);
+ RB_DEBUG_COUNTER_INC(obj_regexp_ptr);
}
break;
case T_DATA:
@@ -2203,15 +2325,21 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
if (dfree) {
if (dfree == RUBY_DEFAULT_FREE) {
xfree(data);
+ RB_DEBUG_COUNTER_INC(obj_data_xfree);
}
else if (free_immediately) {
(*dfree)(data);
+ RB_DEBUG_COUNTER_INC(obj_data_imm_free);
}
else {
make_zombie(objspace, obj, dfree, data);
+ RB_DEBUG_COUNTER_INC(obj_data_zombie);
return 1;
}
}
+ else {
+ RB_DEBUG_COUNTER_INC(obj_data_empty);
+ }
}
break;
case T_MATCH:
@@ -2221,11 +2349,14 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
if (rm->char_offset)
xfree(rm->char_offset);
xfree(rm);
+
+ RB_DEBUG_COUNTER_INC(obj_match_ptr);
}
break;
case T_FILE:
if (RANY(obj)->as.file.fptr) {
make_io_zombie(objspace, obj);
+ RB_DEBUG_COUNTER_INC(obj_file_ptr);
return 1;
}
break;
@@ -2248,6 +2379,8 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
rb_class_remove_from_super_subclasses(obj);
xfree(RANY(obj)->as.klass.ptr);
RANY(obj)->as.klass.ptr = NULL;
+
+ RB_DEBUG_COUNTER_INC(obj_iclass_ptr);
break;
case T_FLOAT:
@@ -2256,23 +2389,32 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
case T_BIGNUM:
if (!(RBASIC(obj)->flags & BIGNUM_EMBED_FLAG) && BIGNUM_DIGITS(obj)) {
xfree(BIGNUM_DIGITS(obj));
+ RB_DEBUG_COUNTER_INC(obj_bignum_ptr);
}
break;
case T_NODE:
- rb_gc_free_node(obj);
- break; /* no need to free iv_tbl */
+ UNEXPECTED_NODE(obj_free);
+ break;
case T_STRUCT:
- if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) == 0 &&
- RANY(obj)->as.rstruct.as.heap.ptr) {
- xfree((void *)RANY(obj)->as.rstruct.as.heap.ptr);
+ if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) ||
+ RANY(obj)->as.rstruct.as.heap.ptr == NULL) {
+ RB_DEBUG_COUNTER_INC(obj_struct_embed);
+ }
+ else if (RSTRUCT_TRANSIENT_P(obj)) {
+ RB_DEBUG_COUNTER_INC(obj_struct_transient);
+ }
+ else {
+ xfree((void *)RANY(obj)->as.rstruct.as.heap.ptr);
+ RB_DEBUG_COUNTER_INC(obj_struct_ptr);
}
break;
case T_SYMBOL:
{
rb_gc_free_dsymbol(obj);
+ RB_DEBUG_COUNTER_INC(obj_symbol);
}
break;
@@ -2280,15 +2422,45 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
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);
+ RB_DEBUG_COUNTER_INC(obj_imemo_iseq);
break;
case imemo_env:
- VM_ASSERT(VM_ENV_ESCAPED_P(RANY(obj)->as.imemo.env.ep));
+ 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);
+ RB_DEBUG_COUNTER_INC(obj_imemo_tmpbuf);
+ break;
+ case imemo_ast:
+ rb_ast_free(&RANY(obj)->as.imemo.ast);
+ RB_DEBUG_COUNTER_INC(obj_imemo_ast);
break;
+ case imemo_cref:
+ RB_DEBUG_COUNTER_INC(obj_imemo_cref);
+ break;
+ case imemo_svar:
+ RB_DEBUG_COUNTER_INC(obj_imemo_svar);
+ break;
+ case imemo_throw_data:
+ RB_DEBUG_COUNTER_INC(obj_imemo_throw_data);
+ break;
+ case imemo_ifunc:
+ RB_DEBUG_COUNTER_INC(obj_imemo_ifunc);
+ break;
+ case imemo_memo:
+ RB_DEBUG_COUNTER_INC(obj_imemo_memo);
+ break;
+ case imemo_parser_strterm:
+ RB_DEBUG_COUNTER_INC(obj_imemo_parser_strterm);
+ break;
default:
+ /* unreachable */
break;
}
return 0;
@@ -2321,16 +2493,6 @@ Init_heap(void)
heap_add_pages(objspace, heap_eden, gc_params.heap_init_slots / HEAP_PAGE_OBJ_LIMIT);
init_mark_stack(&objspace->mark_stack);
-#ifdef USE_SIGALTSTACK
- {
- /* altstack of another threads are allocated in another place */
- rb_thread_t *th = GET_THREAD();
- void *tmp = th->altstack;
- th->altstack = malloc(rb_sigaltstack_size());
- free(tmp); /* free previously allocated area */
- }
-#endif
-
objspace->profile.invoke_time = getrusage_time();
finalizer_table = st_init_numtable();
}
@@ -2455,13 +2617,18 @@ static int
internal_object_p(VALUE obj)
{
RVALUE *p = (RVALUE *)obj;
+ void *ptr = __asan_region_is_poisoned(p, SIZEOF_VALUE);
+ bool used_p = p->as.basic.flags;
+ unpoison_object(obj, false);
- if (p->as.basic.flags) {
+ if (used_p) {
switch (BUILTIN_TYPE(p)) {
+ case T_NODE:
+ UNEXPECTED_NODE(internal_object_p);
+ break;
case T_NONE:
case T_IMEMO:
case T_ICLASS:
- case T_NODE:
case T_ZOMBIE:
break;
case T_CLASS:
@@ -2475,6 +2642,9 @@ internal_object_p(VALUE obj)
return 0;
}
}
+ if (ptr || ! used_p) {
+ poison_object(obj);
+ }
return 1;
}
@@ -2555,12 +2725,7 @@ os_each_obj(int argc, VALUE *argv, VALUE os)
{
VALUE of;
- if (argc == 0) {
- of = 0;
- }
- else {
- rb_scan_args(argc, argv, "01", &of);
- }
+ of = (!rb_check_arity(argc, 0, 1) ? 0 : argv[0]);
RETURN_ENUMERATOR(os, 1, &of);
return os_obj_of(of);
}
@@ -2593,7 +2758,7 @@ rb_undefine_finalizer(VALUE obj)
static void
should_be_callable(VALUE block)
{
- if (!rb_obj_respond_to(block, rb_intern("call"), TRUE)) {
+ if (!rb_obj_respond_to(block, idCall, TRUE)) {
rb_raise(rb_eArgError, "wrong type argument %"PRIsVALUE" (should be callable)",
rb_obj_class(block));
}
@@ -2653,13 +2818,13 @@ define_final0(VALUE obj, VALUE block)
/* avoid duplicate block, table is usually small */
{
- const VALUE *ptr = RARRAY_CONST_PTR(table);
long len = RARRAY_LEN(table);
long i;
- for (i = 0; i < len; i++, ptr++) {
- if (rb_funcall(*ptr, idEq, 1, block)) {
- return *ptr;
+ for (i = 0; i < len; i++) {
+ VALUE recv = RARRAY_AREF(table, i);
+ if (rb_funcall(recv, idEq, 1, block)) {
+ return recv;
}
}
}
@@ -2712,26 +2877,29 @@ static void
run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE table)
{
long i;
- int status;
+ enum ruby_tag_type state;
volatile struct {
VALUE errinfo;
VALUE objid;
+ rb_control_frame_t *cfp;
long finished;
int safe;
} saved;
- rb_thread_t *const th = GET_THREAD();
+ rb_execution_context_t * volatile ec = GET_EC();
#define RESTORE_FINALIZER() (\
+ ec->cfp = saved.cfp, \
rb_set_safe_level_force(saved.safe), \
rb_set_errinfo(saved.errinfo))
saved.safe = rb_safe_level();
saved.errinfo = rb_errinfo();
saved.objid = nonspecial_obj_id(obj);
+ saved.cfp = ec->cfp;
saved.finished = 0;
- TH_PUSH_TAG(th);
- status = TH_EXEC_TAG();
- if (status) {
+ EC_PUSH_TAG(ec);
+ state = EC_EXEC_TAG();
+ if (state != TAG_NONE) {
++saved.finished; /* skip failed finalizer */
}
for (i = saved.finished;
@@ -2739,7 +2907,7 @@ run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE table)
saved.finished = ++i) {
run_single_final(RARRAY_AREF(table, i), saved.objid);
}
- TH_POP_TAG();
+ EC_POP_TAG();
#undef RESTORE_FINALIZER
}
@@ -2762,13 +2930,16 @@ static void
finalize_list(rb_objspace_t *objspace, VALUE zombie)
{
while (zombie) {
- VALUE next_zombie = RZOMBIE(zombie)->next;
- struct heap_page *page = GET_HEAP_PAGE(zombie);
+ VALUE next_zombie;
+ struct heap_page *page;
+ unpoison_object(zombie, false);
+ next_zombie = RZOMBIE(zombie)->next;
+ page = GET_HEAP_PAGE(zombie);
run_final(objspace, zombie);
RZOMBIE(zombie)->basic.flags = 0;
- heap_pages_final_slots--;
+ if (LIKELY(heap_pages_final_slots)) heap_pages_final_slots--;
page->final_slots--;
page->free_slots++;
heap_page_add_freeobj(objspace, GET_HEAP_PAGE(zombie), zombie);
@@ -2852,7 +3023,7 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace)
/* run finalizers */
finalize_deferred(objspace);
- assert(heap_pages_deferred_final == 0);
+ GC_ASSERT(heap_pages_deferred_final == 0);
gc_rest(objspace);
/* prohibit incremental GC */
@@ -2882,6 +3053,7 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace)
for (i = 0; i < heap_allocated_pages; i++) {
p = heap_pages_sorted[i]->start; pend = p + heap_pages_sorted[i]->total_slots;
while (p < pend) {
+ unpoison_object((VALUE)p, false);
switch (BUILTIN_TYPE(p)) {
case T_DATA:
if (!DATA_PTR(p) || !RANY(p)->as.data.dfree) break;
@@ -2892,7 +3064,7 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace)
if (RTYPEDDATA_P(p)) {
RDATA(p)->dfree = RANY(p)->as.typeddata.type->function.dfree;
}
- if (RANY(p)->as.data.dfree == (RUBY_DATA_FUNC)-1) {
+ if (RANY(p)->as.data.dfree == RUBY_DEFAULT_FREE) {
xfree(DATA_PTR(p));
}
else if (RANY(p)->as.data.dfree) {
@@ -2905,6 +3077,7 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace)
}
break;
}
+ poison_object((VALUE)p);
p++;
}
}
@@ -3185,9 +3358,13 @@ obj_memsize_of(VALUE obj, int use_all_types)
size += rb_ary_memsize(obj);
break;
case T_HASH:
- if (RHASH(obj)->ntbl) {
- size += st_memsize(RHASH(obj)->ntbl);
+ if (RHASH_AR_TABLE_P(obj)) {
+ size += sizeof(ar_table);
}
+ else {
+ VM_ASSERT(RHASH_ST_TABLE(obj) != NULL);
+ size += st_memsize(RHASH_ST_TABLE(obj));
+ }
break;
case T_REGEXP:
if (RREGEXP_PTR(obj)) {
@@ -3213,6 +3390,9 @@ obj_memsize_of(VALUE obj, int use_all_types)
case T_RATIONAL:
case T_COMPLEX:
case T_IMEMO:
+ if (imemo_type_p(obj, imemo_tmpbuf)) {
+ size += RANY(obj)->as.imemo.alloc.cnt * sizeof(VALUE);
+ }
break;
case T_FLOAT:
@@ -3226,7 +3406,7 @@ obj_memsize_of(VALUE obj, int use_all_types)
break;
case T_NODE:
- if (use_all_types) size += rb_node_memsize(obj);
+ UNEXPECTED_NODE(obj_memsize_of);
break;
case T_STRUCT:
@@ -3305,9 +3485,10 @@ count_objects(int argc, VALUE *argv, VALUE os)
size_t freed = 0;
size_t total = 0;
size_t i;
- VALUE hash;
+ VALUE hash = Qnil;
- if (rb_scan_args(argc, argv, "01", &hash) == 1) {
+ if (rb_check_arity(argc, 0, 1) == 1) {
+ hash = argv[0];
if (!RB_TYPE_P(hash, T_HASH))
rb_raise(rb_eTypeError, "non-hash given");
}
@@ -3336,7 +3517,7 @@ count_objects(int argc, VALUE *argv, VALUE os)
hash = rb_hash_new();
}
else if (!RHASH_EMPTY_P(hash)) {
- st_foreach(RHASH_TBL_RAW(hash), set_zero, hash);
+ rb_hash_stlike_foreach(hash, set_zero, hash);
}
rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total));
rb_hash_aset(hash, ID2SYM(rb_intern("FREE")), SIZET2NUM(freed));
@@ -3368,7 +3549,6 @@ count_objects(int argc, VALUE *argv, VALUE os)
COUNT_TYPE(T_FIXNUM);
COUNT_TYPE(T_IMEMO);
COUNT_TYPE(T_UNDEF);
- COUNT_TYPE(T_NODE);
COUNT_TYPE(T_ICLASS);
COUNT_TYPE(T_ZOMBIE);
#undef COUNT_TYPE
@@ -3442,14 +3622,15 @@ gc_page_sweep(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *sweep_
if (bitset) {
p = offset + i * BITS_BITLENGTH;
do {
+ unpoison_object((VALUE)p, false);
if (bitset & 1) {
switch (BUILTIN_TYPE(p)) {
default: { /* majority case */
- gc_report(2, objspace, "page_sweep: free %s\n", obj_info((VALUE)p));
+ gc_report(2, objspace, "page_sweep: free %p\n", (void *)p);
#if USE_RGENGC && RGENGC_CHECK_MODE
if (!is_full_marking(objspace)) {
- if (RVALUE_OLD_P((VALUE)p)) rb_bug("page_sweep: %s - old while minor GC.", obj_info((VALUE)p));
- if (rgengc_remembered(objspace, (VALUE)p)) rb_bug("page_sweep: %s - remembered.", obj_info((VALUE)p));
+ if (RVALUE_OLD_P((VALUE)p)) rb_bug("page_sweep: %p - old while minor GC.", (void *)p);
+ if (rgengc_remembered(objspace, (VALUE)p)) rb_bug("page_sweep: %p - remembered.", (void *)p);
}
#endif
if (obj_free(objspace, (VALUE)p)) {
@@ -3460,6 +3641,7 @@ gc_page_sweep(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *sweep_
heap_page_add_freeobj(objspace, sweep_page, (VALUE)p);
gc_report(3, objspace, "page_sweep: %s is added to freelist\n", obj_info((VALUE)p));
freed_slots++;
+ poison_object((VALUE)p);
}
break;
}
@@ -3540,9 +3722,9 @@ gc_mode_transition(rb_objspace_t *objspace, enum gc_mode mode)
#if RGENGC_CHECK_MODE
enum gc_mode prev_mode = gc_mode(objspace);
switch (prev_mode) {
- case gc_mode_none: assert(mode == gc_mode_marking); break;
- case gc_mode_marking: assert(mode == gc_mode_sweeping); break;
- case gc_mode_sweeping: assert(mode == gc_mode_none); break;
+ case gc_mode_none: GC_ASSERT(mode == gc_mode_marking); break;
+ case gc_mode_marking: GC_ASSERT(mode == gc_mode_sweeping); break;
+ case gc_mode_sweeping: GC_ASSERT(mode == gc_mode_none); break;
}
#endif
if (0) fprintf(stderr, "gc_mode_transition: %s->%s\n", gc_mode_name(gc_mode(objspace)), gc_mode_name(mode));
@@ -3552,7 +3734,7 @@ gc_mode_transition(rb_objspace_t *objspace, enum gc_mode mode)
static void
gc_sweep_start_heap(rb_objspace_t *objspace, rb_heap_t *heap)
{
- heap->sweep_pages = heap->pages;
+ heap->sweeping_page = list_top(&heap->pages, struct heap_page, page_node);
heap->free_pages = NULL;
#if GC_ENABLE_INCREMENTAL_MARK
heap->pooled_pages = NULL;
@@ -3582,14 +3764,14 @@ gc_sweep_start(rb_objspace_t *objspace)
static void
gc_sweep_finish(rb_objspace_t *objspace)
{
- gc_report(1, objspace, "gc_sweep_finish");
+ gc_report(1, objspace, "gc_sweep_finish\n");
gc_prof_set_heap_info(objspace);
heap_pages_free_unused_pages(objspace);
/* if heap_pages has unused pages, then assign them to increment */
if (heap_allocatable_pages < heap_tomb->total_pages) {
- heap_allocatable_pages = heap_tomb->total_pages;
+ heap_allocatable_pages_set(objspace, heap_tomb->total_pages);
}
gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_END_SWEEP, 0);
@@ -3603,7 +3785,7 @@ gc_sweep_finish(rb_objspace_t *objspace)
static int
gc_sweep_step(rb_objspace_t *objspace, rb_heap_t *heap)
{
- struct heap_page *sweep_page = heap->sweep_pages;
+ struct heap_page *sweep_page = heap->sweeping_page;
int unlink_limit = 3;
#if GC_ENABLE_INCREMENTAL_MARK
int need_pool = will_be_incremental_marking(objspace) ? TRUE : FALSE;
@@ -3619,9 +3801,9 @@ gc_sweep_step(rb_objspace_t *objspace, rb_heap_t *heap)
gc_prof_sweep_timer_start(objspace);
#endif
- while (sweep_page) {
- struct heap_page *next_sweep_page = heap->sweep_pages = sweep_page->next;
+ do {
int free_slots = gc_page_sweep(objspace, heap, sweep_page);
+ heap->sweeping_page = list_next(&heap->pages, sweep_page, page_node);
if (sweep_page->final_slots + free_slots == sweep_page->total_slots &&
heap_pages_freeable_pages > 0 &&
@@ -3651,11 +3833,9 @@ gc_sweep_step(rb_objspace_t *objspace, rb_heap_t *heap)
else {
sweep_page->free_next = NULL;
}
+ } while ((sweep_page = heap->sweeping_page));
- sweep_page = next_sweep_page;
- }
-
- if (heap->sweep_pages == NULL) {
+ if (!heap->sweeping_page) {
gc_sweep_finish(objspace);
}
@@ -3676,11 +3856,11 @@ gc_sweep_rest(rb_objspace_t *objspace)
}
}
-#if GC_ENABLE_LAZY_SWEEP
static void
gc_sweep_continue(rb_objspace_t *objspace, rb_heap_t *heap)
{
- if (RGENGC_CHECK_MODE) assert(dont_gc == FALSE);
+ GC_ASSERT(dont_gc == FALSE);
+ if (!GC_ENABLE_LAZY_SWEEP) return;
gc_enter(objspace, "sweep_continue");
#if USE_RGENGC
@@ -3691,7 +3871,6 @@ gc_sweep_continue(rb_objspace_t *objspace, rb_heap_t *heap)
gc_sweep_step(objspace, heap);
gc_exit(objspace, "sweep_continue");
}
-#endif
static void
gc_sweep(rb_objspace_t *objspace)
@@ -3711,13 +3890,12 @@ gc_sweep(rb_objspace_t *objspace)
#endif
}
else {
- struct heap_page *page;
+ struct heap_page *page = NULL;
gc_sweep_start(objspace);
- page = heap_eden->sweep_pages;
- while (page) {
- page->flags.before_sweep = TRUE;
- page = page->next;
- }
+
+ list_for_each(&heap_eden->pages, page, page_node) {
+ page->flags.before_sweep = TRUE;
+ }
gc_sweep_step(objspace, heap_eden);
}
@@ -3784,7 +3962,7 @@ push_mark_stack_chunk(mark_stack_t *stack)
{
stack_chunk_t *next;
- if (RGENGC_CHECK_MODE) assert(stack->index == stack->limit);
+ GC_ASSERT(stack->index == stack->limit);
if (stack->cache_size > 0) {
next = stack->cache;
@@ -3807,7 +3985,7 @@ pop_mark_stack_chunk(mark_stack_t *stack)
stack_chunk_t *prev;
prev = stack->chunk->next;
- if (RGENGC_CHECK_MODE) assert(stack->index == 0);
+ GC_ASSERT(stack->index == 0);
add_stack_chunk_cache(stack, stack->chunk);
stack->chunk = prev;
stack->index = stack->limit;
@@ -3898,14 +4076,19 @@ init_mark_stack(mark_stack_t *stack)
/* Marking */
#ifdef __ia64
-#define SET_STACK_END (SET_MACHINE_STACK_END(&th->machine.stack_end), th->machine.register_stack_end = rb_ia64_bsp())
+#define SET_STACK_END (SET_MACHINE_STACK_END(&ec->machine.stack_end), ec->machine.register_stack_end = rb_ia64_bsp())
#else
-#define SET_STACK_END SET_MACHINE_STACK_END(&th->machine.stack_end)
+#define SET_STACK_END SET_MACHINE_STACK_END(&ec->machine.stack_end)
#endif
-#define STACK_START (th->machine.stack_start)
-#define STACK_END (th->machine.stack_end)
-#define STACK_LEVEL_MAX (th->machine.stack_maxsize/sizeof(VALUE))
+#define STACK_START (ec->machine.stack_start)
+#define STACK_END (ec->machine.stack_end)
+#define STACK_LEVEL_MAX (ec->machine.stack_maxsize/sizeof(VALUE))
+
+#ifdef __EMSCRIPTEN__
+#undef STACK_GROW_DIRECTION
+#define STACK_GROW_DIRECTION 1
+#endif
#if STACK_GROW_DIRECTION < 0
# define STACK_LENGTH (size_t)(STACK_START - STACK_END)
@@ -3931,43 +4114,54 @@ ruby_get_stack_grow_direction(volatile VALUE *addr)
size_t
ruby_stack_length(VALUE **p)
{
- rb_thread_t *th = GET_THREAD();
+ rb_execution_context_t *ec = GET_EC();
SET_STACK_END;
if (p) *p = STACK_UPPER(STACK_END, STACK_START, STACK_END);
return STACK_LENGTH;
}
+#define PREVENT_STACK_OVERFLOW 1
+#ifndef PREVENT_STACK_OVERFLOW
#if !(defined(POSIX_SIGNAL) && defined(SIGSEGV) && defined(HAVE_SIGALTSTACK))
+# define PREVENT_STACK_OVERFLOW 1
+#else
+# define PREVENT_STACK_OVERFLOW 0
+#endif
+#endif
+#if PREVENT_STACK_OVERFLOW
static int
-stack_check(int water_mark)
+stack_check(rb_execution_context_t *ec, int water_mark)
{
int ret;
- rb_thread_t *th = GET_THREAD();
SET_STACK_END;
ret = STACK_LENGTH > STACK_LEVEL_MAX - water_mark;
#ifdef __ia64
if (!ret) {
- ret = (VALUE*)rb_ia64_bsp() - th->machine.register_stack_start >
- th->machine.register_stack_maxsize/sizeof(VALUE) - water_mark;
+ ret = (VALUE*)rb_ia64_bsp() - ec->machine.register_stack_start >
+ ec->machine.register_stack_maxsize/sizeof(VALUE) - water_mark;
}
#endif
return ret;
}
+#else
+#define stack_check(ec, water_mark) FALSE
#endif
-#define STACKFRAME_FOR_CALL_CFUNC 512
+#define STACKFRAME_FOR_CALL_CFUNC 838
+
+MJIT_FUNC_EXPORTED int
+rb_ec_stack_check(rb_execution_context_t *ec)
+{
+ return stack_check(ec, STACKFRAME_FOR_CALL_CFUNC);
+}
int
ruby_stack_check(void)
{
-#if defined(POSIX_SIGNAL) && defined(SIGSEGV) && defined(HAVE_SIGALTSTACK)
- return 0;
-#else
- return stack_check(STACKFRAME_FOR_CALL_CFUNC);
-#endif
+ return stack_check(GET_EC(), STACKFRAME_FOR_CALL_CFUNC);
}
-ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
+ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(static void mark_locations_array(rb_objspace_t *objspace, register const VALUE *x, register long n));
static void
mark_locations_array(rb_objspace_t *objspace, register const VALUE *x, register long n)
{
@@ -4059,7 +4253,23 @@ mark_keyvalue(st_data_t key, st_data_t value, st_data_t data)
}
static void
-mark_hash(rb_objspace_t *objspace, st_table *tbl)
+mark_hash(rb_objspace_t *objspace, VALUE hash)
+{
+ rb_hash_stlike_foreach(hash, mark_keyvalue, (st_data_t)objspace);
+
+ if (RHASH_AR_TABLE_P(hash)) {
+ if (objspace->mark_func_data == NULL && RHASH_TRANSIENT_P(hash)) {
+ rb_transient_heap_mark(hash, RHASH_AR_TABLE(hash));
+ }
+ }
+ else {
+ VM_ASSERT(!RHASH_TRANSIENT_P(hash));
+ }
+ gc_mark(objspace, RHASH(hash)->ifnone);
+}
+
+static void
+mark_st(rb_objspace_t *objspace, st_table *tbl)
{
if (!tbl) return;
st_foreach(tbl, mark_keyvalue, (st_data_t)objspace);
@@ -4068,7 +4278,7 @@ mark_hash(rb_objspace_t *objspace, st_table *tbl)
void
rb_mark_hash(st_table *tbl)
{
- mark_hash(&rb_objspace, tbl);
+ mark_st(&rb_objspace, tbl);
}
static void
@@ -4090,7 +4300,8 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me)
gc_mark(objspace, def->body.attr.location);
break;
case VM_METHOD_TYPE_BMETHOD:
- gc_mark(objspace, def->body.proc);
+ 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);
@@ -4155,11 +4366,11 @@ mark_const_tbl(rb_objspace_t *objspace, struct rb_id_table *tbl)
((start) = STACK_END, (end) = STACK_START) : ((start) = STACK_START, (end) = STACK_END+(appendix)))
#endif
-static void mark_stack_locations(rb_objspace_t *objspace, rb_thread_t *th,
+static void mark_stack_locations(rb_objspace_t *objspace, const rb_execution_context_t *ec,
const VALUE *stack_start, const VALUE *stack_end);
static void
-mark_current_machine_context(rb_objspace_t *objspace, rb_thread_t *th)
+mark_current_machine_context(rb_objspace_t *objspace, rb_execution_context_t *ec)
{
union {
rb_jmp_buf j;
@@ -4179,29 +4390,29 @@ mark_current_machine_context(rb_objspace_t *objspace, rb_thread_t *th)
mark_locations_array(objspace, save_regs_gc_mark.v, numberof(save_regs_gc_mark.v));
- mark_stack_locations(objspace, th, stack_start, stack_end);
+ mark_stack_locations(objspace, ec, stack_start, stack_end);
}
void
-rb_gc_mark_machine_stack(rb_thread_t *th)
+rb_gc_mark_machine_stack(const rb_execution_context_t *ec)
{
- rb_objspace_t *objspace = rb_objspace_of(th->vm);
+ rb_objspace_t *objspace = &rb_objspace;
VALUE *stack_start, *stack_end;
GET_STACK_BOUNDS(stack_start, stack_end, 0);
- mark_stack_locations(objspace, th, stack_start, stack_end);
+ mark_stack_locations(objspace, ec, stack_start, stack_end);
}
static void
-mark_stack_locations(rb_objspace_t *objspace, rb_thread_t *th,
+mark_stack_locations(rb_objspace_t *objspace, const rb_execution_context_t *ec,
const VALUE *stack_start, const VALUE *stack_end)
{
gc_mark_locations(objspace, stack_start, stack_end);
#ifdef __ia64
gc_mark_locations(objspace,
- th->machine.register_stack_start,
- th->machine.register_stack_end);
+ ec->machine.register_stack_start,
+ ec->machine.register_stack_end);
#endif
#if defined(__mc68000__)
gc_mark_locations(objspace,
@@ -4221,10 +4432,17 @@ gc_mark_maybe(rb_objspace_t *objspace, VALUE obj)
{
(void)VALGRIND_MAKE_MEM_DEFINED(&obj, sizeof(obj));
if (is_pointer_to_heap(objspace, (void *)obj)) {
- int type = BUILTIN_TYPE(obj);
+ int type;
+ void *ptr = __asan_region_is_poisoned((void *)obj, SIZEOF_VALUE);
+
+ unpoison_object(obj, false);
+ type = BUILTIN_TYPE(obj);
if (type != T_ZOMBIE && type != T_NONE) {
gc_mark_ptr(objspace, obj);
}
+ if (ptr) {
+ poison_object(obj);
+ }
}
}
@@ -4303,7 +4521,7 @@ rgengc_check_relation(rb_objspace_t *objspace, VALUE obj)
}
}
- if (RGENGC_CHECK_MODE) assert(old_parent == objspace->rgengc.parent_object);
+ GC_ASSERT(old_parent == objspace->rgengc.parent_object);
#endif
}
@@ -4330,10 +4548,7 @@ gc_aging(rb_objspace_t *objspace, VALUE obj)
#if USE_RGENGC
struct heap_page *page = GET_HEAP_PAGE(obj);
-#if RGENGC_CHECK_MODE
- assert(RVALUE_MARKING(obj) == FALSE);
-#endif
-
+ GC_ASSERT(RVALUE_MARKING(obj) == FALSE);
check_rvalue_consistency(obj);
if (!RVALUE_PAGE_WB_UNPROTECTED(page, obj)) {
@@ -4342,7 +4557,7 @@ gc_aging(rb_objspace_t *objspace, VALUE obj)
RVALUE_AGE_INC(objspace, obj);
}
else if (is_full_marking(objspace)) {
- if (RGENGC_CHECK_MODE) assert(RVALUE_PAGE_UNCOLLECTIBLE(page, obj) == FALSE);
+ GC_ASSERT(RVALUE_PAGE_UNCOLLECTIBLE(page, obj) == FALSE);
RVALUE_PAGE_OLD_UNCOLLECTIBLE_SET(objspace, page, obj);
}
}
@@ -4360,6 +4575,7 @@ gc_mark_ptr(rb_objspace_t *objspace, VALUE obj)
if (LIKELY(objspace->mark_func_data == NULL)) {
rgengc_check_relation(objspace, obj);
if (!gc_mark_set(objspace, obj)) return; /* already marked */
+ if (RB_TYPE_P(obj, T_NONE)) rb_bug("try to mark T_NONE object"); /* check here will help debugging */
gc_aging(objspace, obj);
gc_grey(objspace, obj);
}
@@ -4411,7 +4627,7 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj)
case imemo_env:
{
const rb_env_t *env = (const rb_env_t *)obj;
- VM_ASSERT(VM_ENV_ESCAPED_P(env->ep));
+ GC_ASSERT(VM_ENV_ESCAPED_P(env->ep));
gc_mark_values(objspace, (long)env->env_size, env->env);
VM_ENV_FLAGS_SET(env->ep, VM_ENV_FLAG_WB_REQUIRED);
gc_mark(objspace, (VALUE)rb_vm_env_prev_env(env));
@@ -4446,6 +4662,20 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj)
case imemo_iseq:
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;
+ case imemo_ast:
+ rb_ast_mark(&RANY(obj)->as.imemo.ast);
+ return;
+ case imemo_parser_strterm:
+ rb_strterm_mark(obj);
+ return;
#if VM_CHECK_MODE > 0
default:
VM_UNREACHABLE(gc_mark_imemo);
@@ -4470,9 +4700,8 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
break;
case T_NODE:
- obj = rb_gc_mark_node(&any->as.node);
- if (obj) gc_mark(objspace, obj);
- return; /* no need to mark class. */
+ UNEXPECTED_NODE(rb_gc_mark);
+ break;
case T_IMEMO:
gc_mark_imemo(objspace, obj);
@@ -4501,21 +4730,28 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
break;
case T_ARRAY:
- if (FL_TEST(obj, ELTS_SHARED)) {
- gc_mark(objspace, any->as.array.as.heap.aux.shared);
+ if (FL_TEST(obj, ELTS_SHARED)) {
+ VALUE root = any->as.array.as.heap.aux.shared;
+ gc_mark(objspace, root);
}
else {
long i, len = RARRAY_LEN(obj);
- const VALUE *ptr = RARRAY_CONST_PTR(obj);
+ const VALUE *ptr = RARRAY_CONST_PTR_TRANSIENT(obj);
for (i=0; i < len; i++) {
- gc_mark(objspace, *ptr++);
+ gc_mark(objspace, ptr[i]);
}
- }
+
+ if (objspace->mark_func_data == NULL) {
+ if (!FL_TEST_RAW(obj, RARRAY_EMBED_FLAG) &&
+ RARRAY_TRANSIENT_P(obj)) {
+ rb_transient_heap_mark(obj, ptr);
+ }
+ }
+ }
break;
case T_HASH:
- mark_hash(objspace, any->as.hash.ntbl);
- gc_mark(objspace, any->as.hash.ifnone);
+ mark_hash(objspace, obj);
break;
case T_STRING:
@@ -4538,10 +4774,18 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
case T_OBJECT:
{
- uint32_t i, len = ROBJECT_NUMIV(obj);
- VALUE *ptr = ROBJECT_IVPTR(obj);
- for (i = 0; i < len; i++) {
- gc_mark(objspace, *ptr++);
+ const VALUE * const ptr = ROBJECT_IVPTR(obj);
+
+ if (ptr) {
+ uint32_t i, len = ROBJECT_NUMIV(obj);
+ for (i = 0; i < len; i++) {
+ gc_mark(objspace, ptr[i]);
+ }
+
+ if (objspace->mark_func_data == NULL &&
+ ROBJ_TRANSIENT_P(obj)) {
+ rb_transient_heap_mark(obj, ptr);
+ }
}
}
break;
@@ -4585,12 +4829,18 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
case T_STRUCT:
{
- long len = RSTRUCT_LEN(obj);
- const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
+ long i;
+ const long len = RSTRUCT_LEN(obj);
+ const VALUE * const ptr = RSTRUCT_CONST_PTR(obj);
- while (len--) {
- gc_mark(objspace, *ptr++);
- }
+ for (i=0; i<len; i++) {
+ gc_mark(objspace, ptr[i]);
+ }
+
+ if (objspace->mark_func_data == NULL &&
+ RSTRUCT_TRANSIENT_P(obj)) {
+ rb_transient_heap_mark(obj, ptr);
+ }
}
break;
@@ -4601,7 +4851,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE 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), any,
+ BUILTIN_TYPE(obj), (void *)any,
is_pointer_to_heap(objspace, any) ? "corrupted object" : "non object");
}
}
@@ -4696,7 +4946,8 @@ static void
gc_mark_roots(rb_objspace_t *objspace, const char **categoryp)
{
struct gc_list *list;
- rb_thread_t *th = GET_THREAD();
+ rb_execution_context_t *ec = GET_EC();
+ rb_vm_t *vm = rb_ec_vm_ptr(ec);
#if PRINT_ROOT_TICKS
tick_t start_tick = tick();
@@ -4736,17 +4987,14 @@ gc_mark_roots(rb_objspace_t *objspace, const char **categoryp)
MARK_CHECKPOINT("vm");
SET_STACK_END;
- rb_vm_mark(th->vm);
- if (th->vm->self) gc_mark(objspace, th->vm->self);
+ rb_vm_mark(vm);
+ if (vm->self) gc_mark(objspace, vm->self);
MARK_CHECKPOINT("finalizers");
mark_tbl(objspace, finalizer_table);
MARK_CHECKPOINT("machine_context");
- mark_current_machine_context(objspace, th);
-
- MARK_CHECKPOINT("encodings");
- rb_gc_mark_encodings();
+ mark_current_machine_context(objspace, ec);
/* mark protected global variables */
MARK_CHECKPOINT("global_list");
@@ -4824,7 +5072,7 @@ reflist_dump(struct reflist *refs)
}
static int
-reflist_refered_from_machine_context(struct reflist *refs)
+reflist_referred_from_machine_context(struct reflist *refs)
{
int i;
for (i=0; i<refs->pos; i++) {
@@ -4967,7 +5215,7 @@ gc_check_after_marks_i(st_data_t k, st_data_t v, void *ptr)
fprintf(stderr, "gc_check_after_marks_i: %p is referred from ", (void *)obj);
reflist_dump(refs);
- if (reflist_refered_from_machine_context(refs)) {
+ if (reflist_referred_from_machine_context(refs)) {
fprintf(stderr, " (marked from machine stack).\n");
/* marked from machine context can be false positive */
}
@@ -5032,7 +5280,7 @@ check_generation_i(const VALUE child, void *ptr)
struct verify_internal_consistency_struct *data = (struct verify_internal_consistency_struct *)ptr;
const VALUE parent = data->parent;
- if (RGENGC_CHECK_MODE) assert(RVALUE_OLD_P(parent));
+ if (RGENGC_CHECK_MODE) GC_ASSERT(RVALUE_OLD_P(parent));
if (!RVALUE_OLD_P(child)) {
if (!RVALUE_REMEMBERED(parent) &&
@@ -5102,7 +5350,7 @@ verify_internal_consistency_i(void *page_start, void *page_end, size_t stride, v
}
else {
if (BUILTIN_TYPE(obj) == T_ZOMBIE) {
- if (RGENGC_CHECK_MODE) assert(RBASIC(obj)->flags == T_ZOMBIE);
+ GC_ASSERT(RBASIC(obj)->flags == T_ZOMBIE);
data->zombie_object_count++;
}
}
@@ -5118,18 +5366,20 @@ gc_verify_heap_page(rb_objspace_t *objspace, struct heap_page *page, VALUE obj)
int i;
unsigned int has_remembered_shady = FALSE;
unsigned int has_remembered_old = FALSE;
- int rememberd_old_objects = 0;
+ int remembered_old_objects = 0;
int free_objects = 0;
int zombie_objects = 0;
for (i=0; i<page->total_slots; i++) {
- VALUE obj = (VALUE)&page->start[i];
- if (RBASIC(obj) == 0) free_objects++;
- if (BUILTIN_TYPE(obj) == T_ZOMBIE) zombie_objects++;
- if (RVALUE_PAGE_UNCOLLECTIBLE(page, obj) && RVALUE_PAGE_WB_UNPROTECTED(page, obj)) has_remembered_shady = TRUE;
- if (RVALUE_PAGE_MARKING(page, obj)) {
+ VALUE val = (VALUE)&page->start[i];
+ if (RBASIC(val) == 0) free_objects++;
+ if (BUILTIN_TYPE(val) == 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;
- rememberd_old_objects++;
+ remembered_old_objects++;
}
}
@@ -5137,58 +5387,58 @@ gc_verify_heap_page(rb_objspace_t *objspace, struct heap_page *page, VALUE obj)
page->flags.has_remembered_objects == FALSE && has_remembered_old == TRUE) {
for (i=0; i<page->total_slots; i++) {
- VALUE obj = (VALUE)&page->start[i];
- if (RVALUE_PAGE_MARKING(page, obj)) {
- fprintf(stderr, "marking -> %s\n", obj_info(obj));
+ VALUE val = (VALUE)&page->start[i];
+ 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",
- page, rememberd_old_objects, obj ? obj_info(obj) : "");
+ (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",
- page, obj ? obj_info(obj) : "");
+ (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", page, (int)page->free_slots, free_objects);
+ rb_bug("page %p's free_slots should be %d, but %d\n", (void *)page, (int)page->free_slots, free_objects);
}
}
if (page->final_slots != zombie_objects) {
- rb_bug("page %p's final_slots should be %d, but %d\n", page, (int)page->final_slots, zombie_objects);
+ rb_bug("page %p's final_slots should be %d, but %d\n", (void *)page, (int)page->final_slots, zombie_objects);
}
- return rememberd_old_objects;
+ return remembered_old_objects;
#else
return 0;
#endif
}
static int
-gc_verify_heap_pages_(rb_objspace_t *objspace, struct heap_page *page)
+gc_verify_heap_pages_(rb_objspace_t *objspace, struct list_head *head)
{
- int rememberd_old_objects = 0;
+ int remembered_old_objects = 0;
+ struct heap_page *page = 0;
- while (page) {
+ list_for_each(head, page, page_node) {
if (page->flags.has_remembered_objects == FALSE) {
- rememberd_old_objects += gc_verify_heap_page(objspace, page, Qfalse);
+ remembered_old_objects += gc_verify_heap_page(objspace, page, Qfalse);
}
- page = page->next;
}
- return rememberd_old_objects;
+ return remembered_old_objects;
}
static int
gc_verify_heap_pages(rb_objspace_t *objspace)
{
- int rememberd_old_objects = 0;
- rememberd_old_objects = gc_verify_heap_pages_(objspace, heap_eden->pages);
- rememberd_old_objects = gc_verify_heap_pages_(objspace, heap_tomb->pages);
- return rememberd_old_objects;
+ int remembered_old_objects = 0;
+ remembered_old_objects += gc_verify_heap_pages_(objspace, &heap_eden->pages);
+ remembered_old_objects += gc_verify_heap_pages_(objspace, &heap_tomb->pages);
+ return remembered_old_objects;
}
/*
@@ -5235,17 +5485,17 @@ gc_verify_internal_consistency(VALUE dummy)
if (objspace_live_slots(objspace) != data.live_object_count) {
fprintf(stderr, "heap_pages_final_slots: %d, objspace->profile.total_freed_objects: %d\n",
(int)heap_pages_final_slots, (int)objspace->profile.total_freed_objects);
- rb_bug("inconsistent live slot nubmer: expect %"PRIuSIZE", but %"PRIuSIZE".", objspace_live_slots(objspace), data.live_object_count);
+ rb_bug("inconsistent live slot number: expect %"PRIuSIZE", but %"PRIuSIZE".", objspace_live_slots(objspace), data.live_object_count);
}
}
#if USE_RGENGC
if (!is_marking(objspace)) {
if (objspace->rgengc.old_objects != data.old_object_count) {
- rb_bug("inconsistent old slot nubmer: expect %"PRIuSIZE", but %"PRIuSIZE".", 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) {
- rb_bug("inconsistent old slot nubmer: expect %"PRIuSIZE", but %"PRIuSIZE".", objspace->rgengc.uncollectible_wb_unprotected_objects, data.remembered_shady_count);
+ rb_bug("inconsistent old slot number: expect %"PRIuSIZE", but %"PRIuSIZE".", objspace->rgengc.uncollectible_wb_unprotected_objects, data.remembered_shady_count);
}
}
#endif
@@ -5285,6 +5535,13 @@ rb_gc_verify_internal_consistency(void)
gc_verify_internal_consistency(Qnil);
}
+static VALUE
+gc_verify_transient_heap_internal_consistency(VALUE dmy)
+{
+ rb_transient_heap_verify();
+ return Qnil;
+}
+
/* marks */
static void
@@ -5328,9 +5585,9 @@ gc_marks_start(rb_objspace_t *objspace, int full_mark)
static void
gc_marks_wb_unprotected_objects(rb_objspace_t *objspace)
{
- struct heap_page *page = heap_eden->pages;
+ struct heap_page *page = 0;
- while (page) {
+ list_for_each(&heap_eden->pages, page, page_node) {
bits_t *mark_bits = page->mark_bits;
bits_t *wbun_bits = page->wb_unprotected_bits;
RVALUE *p = page->start;
@@ -5346,10 +5603,8 @@ gc_marks_wb_unprotected_objects(rb_objspace_t *objspace)
do {
if (bits & 1) {
gc_report(2, objspace, "gc_marks_wb_unprotected_objects: marked shady: %s\n", obj_info((VALUE)p));
- if (RGENGC_CHECK_MODE > 0) {
- assert(RVALUE_WB_UNPROTECTED((VALUE)p));
- assert(RVALUE_MARKED((VALUE)p));
- }
+ GC_ASSERT(RVALUE_WB_UNPROTECTED((VALUE)p));
+ GC_ASSERT(RVALUE_MARKED((VALUE)p));
gc_mark_children(objspace, (VALUE)p);
}
p++;
@@ -5357,8 +5612,6 @@ gc_marks_wb_unprotected_objects(rb_objspace_t *objspace)
} while (bits);
}
}
-
- page = page->next;
}
gc_mark_stacked_objects_all(objspace);
@@ -5440,9 +5693,7 @@ gc_marks_finish(rb_objspace_t *objspace)
size_t min_free_slots = (size_t)(total_slots * gc_params.heap_free_slots_min_ratio);
int full_marking = is_full_marking(objspace);
-#if RGENGC_CHECK_MODE
- assert(heap->total_slots >= objspace->marked_slots);
-#endif
+ GC_ASSERT(heap->total_slots >= objspace->marked_slots);
/* setup free-able page counts */
if (max_free_slots < gc_params.heap_init_slots) max_free_slots = gc_params.heap_init_slots;
@@ -5507,16 +5758,18 @@ gc_marks_finish(rb_objspace_t *objspace)
#endif
}
+ rb_transient_heap_finish_marking();
+
gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_END_MARK, 0);
return TRUE;
}
-#if GC_ENABLE_INCREMENTAL_MARK
static void
gc_marks_step(rb_objspace_t *objspace, int slots)
{
- if (RGENGC_CHECK_MODE) assert(is_marking(objspace));
+#if GC_ENABLE_INCREMENTAL_MARK
+ GC_ASSERT(is_marking(objspace));
if (gc_mark_stacked_objects_incremental(objspace, slots)) {
if (gc_marks_finish(objspace)) {
@@ -5525,8 +5778,8 @@ gc_marks_step(rb_objspace_t *objspace, int slots)
}
}
if (0) fprintf(stderr, "objspace->marked_slots: %d\n", (int)objspace->marked_slots);
-}
#endif
+}
static void
gc_marks_rest(rb_objspace_t *objspace)
@@ -5551,19 +5804,19 @@ gc_marks_rest(rb_objspace_t *objspace)
gc_sweep(objspace);
}
-#if GC_ENABLE_INCREMENTAL_MARK
static void
gc_marks_continue(rb_objspace_t *objspace, rb_heap_t *heap)
{
- int slots = 0;
- const char *from;
-
- if (RGENGC_CHECK_MODE) assert(dont_gc == FALSE);
+ GC_ASSERT(dont_gc == FALSE);
+#if GC_ENABLE_INCREMENTAL_MARK
gc_enter(objspace, "marks_continue");
PUSH_MARK_FUNC_DATA(NULL);
{
+ int slots = 0;
+ const char *from;
+
if (heap->pooled_pages) {
while (heap->pooled_pages && slots < HEAP_PAGE_OBJ_LIMIT) {
struct heap_page *page = heap_move_pooled_pages_to_free_pages(heap);
@@ -5588,8 +5841,8 @@ gc_marks_continue(rb_objspace_t *objspace, rb_heap_t *heap)
POP_MARK_FUNC_DATA();
gc_exit(objspace, "marks_continue");
-}
#endif
+}
static void
gc_marks(rb_objspace_t *objspace, int full_mark)
@@ -5672,7 +5925,7 @@ rgengc_remembersetbits_set(rb_objspace_t *objspace, VALUE obj)
struct heap_page *page = GET_HEAP_PAGE(obj);
bits_t *bits = &page->marking_bits[0];
- if (RGENGC_CHECK_MODE) assert(!is_incremental_marking(objspace));
+ GC_ASSERT(!is_incremental_marking(objspace));
if (MARKED_IN_BITMAP(bits, obj)) {
return FALSE;
@@ -5730,13 +5983,13 @@ static void
rgengc_rememberset_mark(rb_objspace_t *objspace, rb_heap_t *heap)
{
size_t j;
- struct heap_page *page = heap->pages;
+ struct heap_page *page = 0;
#if PROFILE_REMEMBERSET_MARK
int has_old = 0, has_shady = 0, has_both = 0, skip = 0;
#endif
gc_report(1, objspace, "rgengc_rememberset_mark: start\n");
- while (page) {
+ list_for_each(&heap->pages, page, page_node) {
if (page->flags.has_remembered_objects | page->flags.has_uncollectible_shady_objects) {
RVALUE *p = page->start;
RVALUE *offset = p - NUM_IN_PAGE(p);
@@ -5765,11 +6018,8 @@ rgengc_rememberset_mark(rb_objspace_t *objspace, rb_heap_t *heap)
if (bitset & 1) {
VALUE obj = (VALUE)p;
gc_report(2, objspace, "rgengc_rememberset_mark: mark %s\n", obj_info(obj));
-
- if (RGENGC_CHECK_MODE) {
- assert(RVALUE_UNCOLLECTIBLE(obj));
- assert(RVALUE_OLD_P(obj) || RVALUE_WB_UNPROTECTED(obj));
- }
+ GC_ASSERT(RVALUE_UNCOLLECTIBLE(obj));
+ GC_ASSERT(RVALUE_OLD_P(obj) || RVALUE_WB_UNPROTECTED(obj));
gc_mark_children(objspace, obj);
}
@@ -5784,8 +6034,6 @@ rgengc_rememberset_mark(rb_objspace_t *objspace, rb_heap_t *heap)
skip++;
}
#endif
-
- page = page->next;
}
#if PROFILE_REMEMBERSET_MARK
@@ -5797,15 +6045,14 @@ rgengc_rememberset_mark(rb_objspace_t *objspace, rb_heap_t *heap)
static void
rgengc_mark_and_rememberset_clear(rb_objspace_t *objspace, rb_heap_t *heap)
{
- struct heap_page *page = heap->pages;
+ struct heap_page *page = 0;
- while (page) {
+ list_for_each(&heap->pages, page, page_node) {
memset(&page->mark_bits[0], 0, HEAP_PAGE_BITMAP_SIZE);
memset(&page->marking_bits[0], 0, HEAP_PAGE_BITMAP_SIZE);
memset(&page->uncollectible_bits[0], 0, HEAP_PAGE_BITMAP_SIZE);
page->flags.has_uncollectible_shady_objects = FALSE;
page->flags.has_remembered_objects = FALSE;
- page = page->next;
}
}
@@ -5862,18 +6109,18 @@ NOINLINE(static void gc_writebarrier_incremental(VALUE a, VALUE b, rb_objspace_t
static void
gc_writebarrier_incremental(VALUE a, VALUE b, rb_objspace_t *objspace)
{
- gc_report(2, objspace, "gc_writebarrier_incremental: [LG] %s -> %s\n", obj_info(a), obj_info(b));
+ 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] %s -> %s\n", obj_info(a), obj_info(b));
+ 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] %s -> %s\n", obj_info(a), obj_info(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)) {
@@ -5881,7 +6128,7 @@ gc_writebarrier_incremental(VALUE a, VALUE b, rb_objspace_t *objspace)
}
}
else {
- gc_report(1, objspace, "gc_writebarrier_incremental: [LL] %s -> %s\n", obj_info(a), obj_info(b));
+ gc_report(1, objspace, "gc_writebarrier_incremental: [LL] %p -> %s\n", (void *)a, obj_info(b));
gc_remember_unprotected(objspace, b);
}
}
@@ -5941,6 +6188,7 @@ rb_gc_writebarrier_unprotect(VALUE obj)
RVALUE_AGE_RESET(obj);
}
+ RB_DEBUG_COUNTER_INC(obj_wb_unprotect);
MARK_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(obj), obj);
}
}
@@ -5948,7 +6196,7 @@ rb_gc_writebarrier_unprotect(VALUE obj)
/*
* remember `obj' if needed.
*/
-void
+MJIT_FUNC_EXPORTED void
rb_gc_writebarrier_remember(VALUE obj)
{
rb_objspace_t *objspace = &rb_objspace;
@@ -6003,9 +6251,8 @@ rb_gc_unprotect_logging(void *objptr, const char *filename, int line)
cnt++;
}
else {
- ptr = (char *)malloc(strlen(buff) + 1);
+ ptr = (strdup)(buff);
if (!ptr) rb_memerror();
- strcpy(ptr, buff);
}
st_insert(rgengc_unprotect_logging_table, (st_data_t)ptr, cnt);
}
@@ -6138,7 +6385,7 @@ rb_gc_force_recycle(VALUE obj)
void
rb_gc_register_mark_object(VALUE obj)
{
- VALUE ary_ary = GET_THREAD()->vm->mark_object_ary;
+ VALUE ary_ary = GET_VM()->mark_object_ary;
VALUE ary = rb_ary_last(0, 0, ary_ary);
if (ary == Qnil || RARRAY_LEN(ary) >= MARK_OBJECT_ARY_BUCKET_SIZE) {
@@ -6235,8 +6482,7 @@ gc_reset_malloc_info(rb_objspace_t *objspace)
if (inc > malloc_limit) {
malloc_limit = (size_t)(inc * gc_params.malloc_limit_growth_factor);
- if (gc_params.malloc_limit_max > 0 && /* ignore max-check if 0 */
- malloc_limit > gc_params.malloc_limit_max) {
+ if (malloc_limit > gc_params.malloc_limit_max) {
malloc_limit = gc_params.malloc_limit_max;
}
}
@@ -6295,7 +6541,7 @@ gc_reset_malloc_info(rb_objspace_t *objspace)
}
static int
-garbage_collect(rb_objspace_t *objspace, int full_mark, int immediate_mark, int immediate_sweep, int reason)
+garbage_collect(rb_objspace_t *objspace, int reason)
{
#if GC_PROFILE_MORE_DETAIL
objspace->profile.prepare_time = getrusage_time();
@@ -6307,26 +6553,27 @@ garbage_collect(rb_objspace_t *objspace, int full_mark, int immediate_mark, int
objspace->profile.prepare_time = getrusage_time() - objspace->profile.prepare_time;
#endif
- return gc_start(objspace, full_mark, immediate_mark, immediate_sweep, reason);
+ return gc_start(objspace, reason);
}
static int
-gc_start(rb_objspace_t *objspace, const int full_mark, const int immediate_mark, const unsigned int immediate_sweep, int reason)
+gc_start(rb_objspace_t *objspace, int reason)
{
- int do_full_mark = full_mark;
- objspace->flags.immediate_sweep = immediate_sweep;
+ unsigned int do_full_mark = !!((unsigned)reason & GPR_FLAG_FULL_MARK);
+ unsigned int immediate_mark = (unsigned)reason & GPR_FLAG_IMMEDIATE_MARK;
+
+ /* reason may be clobbered, later, so keep set immediate_sweep here */
+ objspace->flags.immediate_sweep = !!((unsigned)reason & GPR_FLAG_IMMEDIATE_SWEEP);
if (!heap_allocated_pages) return FALSE; /* heap is not ready */
- if (reason != GPR_FLAG_METHOD && !ready_to_gc(objspace)) return TRUE; /* GC is not allowed */
+ if (!(reason & GPR_FLAG_METHOD) && !ready_to_gc(objspace)) return TRUE; /* GC is not allowed */
- if (RGENGC_CHECK_MODE) {
- assert(gc_mode(objspace) == gc_mode_none);
- assert(!is_lazy_sweeping(heap_eden));
- assert(!is_incremental_marking(objspace));
+ GC_ASSERT(gc_mode(objspace) == gc_mode_none);
+ GC_ASSERT(!is_lazy_sweeping(heap_eden));
+ GC_ASSERT(!is_incremental_marking(objspace));
#if RGENGC_CHECK_MODE >= 2
- gc_verify_internal_consistency(Qnil);
+ gc_verify_internal_consistency(Qnil);
#endif
- }
gc_enter(objspace, "gc_start");
@@ -6373,19 +6620,41 @@ gc_start(rb_objspace_t *objspace, const int full_mark, const int immediate_mark,
if (objspace->flags.immediate_sweep) reason |= GPR_FLAG_IMMEDIATE_SWEEP;
- gc_report(1, objspace, "gc_start(%d, %d, %d, reason: %d) => %d, %d, %d\n",
- full_mark, immediate_mark, immediate_sweep, reason,
+ gc_report(1, objspace, "gc_start(reason: %d) => %u, %d, %d\n",
+ reason,
do_full_mark, !is_incremental_marking(objspace), objspace->flags.immediate_sweep);
+#if USE_DEBUG_COUNTER
+ RB_DEBUG_COUNTER_INC(gc_count);
+
+ if (reason & GPR_FLAG_MAJOR_MASK) {
+ (void)RB_DEBUG_COUNTER_INC_IF(gc_major_nofree, reason & GPR_FLAG_MAJOR_BY_NOFREE);
+ (void)RB_DEBUG_COUNTER_INC_IF(gc_major_oldgen, reason & GPR_FLAG_MAJOR_BY_OLDGEN);
+ (void)RB_DEBUG_COUNTER_INC_IF(gc_major_shady, reason & GPR_FLAG_MAJOR_BY_SHADY);
+ (void)RB_DEBUG_COUNTER_INC_IF(gc_major_force, reason & GPR_FLAG_MAJOR_BY_FORCE);
+#if RGENGC_ESTIMATE_OLDMALLOC
+ (void)RB_DEBUG_COUNTER_INC_IF(gc_major_oldmalloc, reason & GPR_FLAG_MAJOR_BY_OLDMALLOC);
+#endif
+ }
+ else {
+ (void)RB_DEBUG_COUNTER_INC_IF(gc_minor_newobj, reason & GPR_FLAG_NEWOBJ);
+ (void)RB_DEBUG_COUNTER_INC_IF(gc_minor_malloc, reason & GPR_FLAG_MALLOC);
+ (void)RB_DEBUG_COUNTER_INC_IF(gc_minor_method, reason & GPR_FLAG_METHOD);
+ (void)RB_DEBUG_COUNTER_INC_IF(gc_minor_capi, reason & GPR_FLAG_CAPI);
+ (void)RB_DEBUG_COUNTER_INC_IF(gc_minor_stress, reason & GPR_FLAG_STRESS);
+ }
+#endif
+
objspace->profile.count++;
objspace->profile.latest_gc_info = reason;
objspace->profile.total_allocated_objects_at_gc_start = objspace->total_allocated_objects;
objspace->profile.heap_used_at_gc_start = heap_allocated_pages;
gc_prof_setup_new_record(objspace, reason);
gc_reset_malloc_info(objspace);
+ rb_transient_heap_start_marking(do_full_mark);
gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_START, 0 /* TODO: pass minor/immediate flag? */);
- if (RGENGC_CHECK_MODE) assert(during_gc);
+ GC_ASSERT(during_gc);
gc_prof_timer_start(objspace);
{
@@ -6423,9 +6692,6 @@ gc_rest(rb_objspace_t *objspace)
struct objspace_and_reason {
rb_objspace_t *objspace;
int reason;
- int full_mark;
- int immediate_mark;
- int immediate_sweep;
};
static void
@@ -6509,11 +6775,13 @@ gc_record(rb_objspace_t *objspace, int direction, const char *event)
static inline void
gc_enter(rb_objspace_t *objspace, const char *event)
{
- if (RGENGC_CHECK_MODE) assert(during_gc == 0);
+ GC_ASSERT(during_gc == 0);
if (RGENGC_CHECK_MODE >= 3) gc_verify_internal_consistency(Qnil);
+ mjit_gc_start_hook();
+
during_gc = TRUE;
- gc_report(1, objspace, "gc_entr: %s [%s]\n", event, gc_current_status(objspace));
+ gc_report(1, objspace, "gc_enter: %s [%s]\n", event, gc_current_status(objspace));
gc_record(objspace, 0, event);
gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_ENTER, 0); /* TODO: which parameter should be passed? */
}
@@ -6521,36 +6789,35 @@ gc_enter(rb_objspace_t *objspace, const char *event)
static inline void
gc_exit(rb_objspace_t *objspace, const char *event)
{
- if (RGENGC_CHECK_MODE) assert(during_gc != 0);
+ GC_ASSERT(during_gc != 0);
gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_EXIT, 0); /* TODO: which parameter should be passsed? */
gc_record(objspace, 1, event);
gc_report(1, objspace, "gc_exit: %s [%s]\n", event, gc_current_status(objspace));
during_gc = FALSE;
+
+ mjit_gc_finish_hook();
}
static void *
gc_with_gvl(void *ptr)
{
struct objspace_and_reason *oar = (struct objspace_and_reason *)ptr;
- return (void *)(VALUE)garbage_collect(oar->objspace, oar->full_mark, oar->immediate_mark, oar->immediate_sweep, oar->reason);
+ return (void *)(VALUE)garbage_collect(oar->objspace, oar->reason);
}
static int
-garbage_collect_with_gvl(rb_objspace_t *objspace, int full_mark, int immediate_mark, int immediate_sweep, int reason)
+garbage_collect_with_gvl(rb_objspace_t *objspace, int reason)
{
if (dont_gc) return TRUE;
if (ruby_thread_has_gvl_p()) {
- return garbage_collect(objspace, full_mark, immediate_mark, immediate_sweep, reason);
+ return garbage_collect(objspace, reason);
}
else {
if (ruby_native_thread_p()) {
struct objspace_and_reason oar;
oar.objspace = objspace;
oar.reason = reason;
- oar.full_mark = full_mark;
- oar.immediate_mark = immediate_mark;
- oar.immediate_sweep = immediate_sweep;
return (int)(VALUE)rb_thread_call_with_gvl(gc_with_gvl, (void *)&oar);
}
else {
@@ -6561,12 +6828,6 @@ garbage_collect_with_gvl(rb_objspace_t *objspace, int full_mark, int immediate_m
}
}
-int
-rb_garbage_collect(void)
-{
- return garbage_collect(&rb_objspace, TRUE, TRUE, TRUE, GPR_FLAG_CAPI);
-}
-
#undef Init_stack
void
@@ -6602,7 +6863,8 @@ static VALUE
gc_start_internal(int argc, VALUE *argv, VALUE self)
{
rb_objspace_t *objspace = &rb_objspace;
- int full_mark = TRUE, immediate_mark = TRUE, immediate_sweep = TRUE;
+ int reason = GPR_FLAG_FULL_MARK | GPR_FLAG_IMMEDIATE_MARK |
+ GPR_FLAG_IMMEDIATE_SWEEP | GPR_FLAG_METHOD;
VALUE opt = Qnil;
static ID keyword_ids[3];
@@ -6619,12 +6881,18 @@ gc_start_internal(int argc, VALUE *argv, VALUE self)
rb_get_kwargs(opt, keyword_ids, 0, 3, kwvals);
- if (kwvals[0] != Qundef) full_mark = RTEST(kwvals[0]);
- if (kwvals[1] != Qundef) immediate_mark = RTEST(kwvals[1]);
- if (kwvals[2] != Qundef) immediate_sweep = RTEST(kwvals[2]);
+ if (kwvals[0] != Qundef && !RTEST(kwvals[0])) {
+ reason &= ~GPR_FLAG_FULL_MARK;
+ }
+ if (kwvals[1] != Qundef && !RTEST(kwvals[1])) {
+ reason &= ~GPR_FLAG_IMMEDIATE_MARK;
+ }
+ if (kwvals[2] != Qundef && !RTEST(kwvals[2])) {
+ reason &= ~GPR_FLAG_IMMEDIATE_SWEEP;
+ }
}
- garbage_collect(objspace, full_mark, immediate_mark, immediate_sweep, GPR_FLAG_METHOD);
+ garbage_collect(objspace, reason);
gc_finalize_deferred(objspace);
return Qnil;
@@ -6641,7 +6909,9 @@ void
rb_gc(void)
{
rb_objspace_t *objspace = &rb_objspace;
- garbage_collect(objspace, TRUE, TRUE, TRUE, GPR_FLAG_CAPI);
+ int reason = GPR_FLAG_FULL_MARK | GPR_FLAG_IMMEDIATE_MARK |
+ GPR_FLAG_IMMEDIATE_SWEEP | GPR_FLAG_CAPI;
+ garbage_collect(objspace, reason);
gc_finalize_deferred(objspace);
}
@@ -6659,7 +6929,7 @@ static const char *type_name(int type, VALUE obj);
static void
gc_count_add_each_types(VALUE hash, const char *name, const size_t *types)
{
- VALUE result = rb_hash_new();
+ VALUE result = rb_hash_new_with_size(T_MASK);
int i;
for (i=0; i<T_MASK; i++) {
const char *type = type_name(i, 0);
@@ -6806,13 +7076,13 @@ gc_latest_gc_info(int argc, VALUE *argv, VALUE self)
rb_objspace_t *objspace = &rb_objspace;
VALUE arg = Qnil;
- if (rb_scan_args(argc, argv, "01", &arg) == 1) {
+ if (rb_check_arity(argc, 0, 1) == 1) {
+ arg = argv[0];
if (!SYMBOL_P(arg) && !RB_TYPE_P(arg, T_HASH)) {
rb_raise(rb_eTypeError, "non-hash or symbol given");
}
}
-
- if (arg == Qnil) {
+ else {
arg = rb_hash_new();
}
@@ -7181,7 +7451,8 @@ gc_stat(int argc, VALUE *argv, VALUE self)
{
VALUE arg = Qnil;
- if (rb_scan_args(argc, argv, "01", &arg) == 1) {
+ if (rb_check_arity(argc, 0, 1) == 1) {
+ arg = argv[0];
if (SYMBOL_P(arg)) {
size_t value = gc_stat_internal(arg);
return SIZET2NUM(value);
@@ -7190,8 +7461,7 @@ gc_stat(int argc, VALUE *argv, VALUE self)
rb_raise(rb_eTypeError, "non-hash or symbol given");
}
}
-
- if (arg == Qnil) {
+ else {
arg = rb_hash_new();
}
gc_stat_internal(arg);
@@ -7490,6 +7760,9 @@ ruby_gc_set_params(int safe_level)
get_envparam_size ("RUBY_GC_MALLOC_LIMIT", &gc_params.malloc_limit_min, 0);
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 */
+ gc_params.malloc_limit_max = SIZE_MAX;
+ }
get_envparam_double("RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR", &gc_params.malloc_limit_growth_factor, 1.0, 0.0, FALSE);
#if RGENGC_ESTIMATE_OLDMALLOC
@@ -7606,27 +7879,31 @@ ruby_memerror(void)
void
rb_memerror(void)
{
- rb_thread_t *th = GET_THREAD();
- rb_objspace_t *objspace = rb_objspace_of(th->vm);
+ rb_execution_context_t *ec = GET_EC();
+ rb_objspace_t *objspace = rb_objspace_of(rb_ec_vm_ptr(ec));
+ VALUE exc;
if (during_gc) gc_exit(objspace, "rb_memerror");
- if (!nomem_error ||
- rb_thread_raised_p(th, RAISED_NOMEMORY)) {
+ exc = nomem_error;
+ if (!exc ||
+ rb_ec_raised_p(ec, RAISED_NOMEMORY)) {
fprintf(stderr, "[FATAL] failed to allocate memory\n");
exit(EXIT_FAILURE);
}
- if (rb_thread_raised_p(th, RAISED_NOMEMORY)) {
- rb_thread_raised_clear(th);
- GET_THREAD()->errinfo = nomem_error;
- TH_JUMP_TAG(th, TAG_RAISE);
+ if (rb_ec_raised_p(ec, RAISED_NOMEMORY)) {
+ rb_ec_raised_clear(ec);
}
- rb_thread_raised_set(th, RAISED_NOMEMORY);
- rb_exc_raise(nomem_error);
+ else {
+ rb_ec_raised_set(ec, RAISED_NOMEMORY);
+ exc = ruby_vm_special_exception_copy(exc);
+ }
+ ec->errinfo = exc;
+ EC_JUMP_TAG(ec, TAG_RAISE);
}
-static void *
-aligned_malloc(size_t alignment, size_t size)
+void *
+rb_aligned_malloc(size_t alignment, size_t size)
{
void *res;
@@ -7653,16 +7930,14 @@ aligned_malloc(size_t alignment, size_t size)
res = (void*)aligned;
#endif
-#if defined(_DEBUG) || GC_DEBUG
/* alignment must be a power of 2 */
- assert(((alignment - 1) & alignment) == 0);
- assert(alignment % sizeof(void*) == 0);
-#endif
+ GC_ASSERT(((alignment - 1) & alignment) == 0);
+ GC_ASSERT(alignment % sizeof(void*) == 0);
return res;
}
-static void
-aligned_free(void *ptr)
+void
+rb_aligned_free(void *ptr)
{
#if defined __MINGW32__
__mingw_aligned_free(ptr);
@@ -7686,9 +7961,9 @@ objspace_malloc_size(rb_objspace_t *objspace, void *ptr, size_t hint)
}
enum memop_type {
- MEMOP_TYPE_MALLOC = 1,
- MEMOP_TYPE_FREE = 2,
- MEMOP_TYPE_REALLOC = 3
+ MEMOP_TYPE_MALLOC = 0,
+ MEMOP_TYPE_FREE,
+ MEMOP_TYPE_REALLOC
};
static inline void
@@ -7707,7 +7982,13 @@ static void
objspace_malloc_gc_stress(rb_objspace_t *objspace)
{
if (ruby_gc_stressful && ruby_native_thread_p()) {
- garbage_collect_with_gvl(objspace, gc_stress_full_mark_after_malloc_p(), TRUE, TRUE, GPR_FLAG_STRESS | GPR_FLAG_MALLOC);
+ int reason = GPR_FLAG_IMMEDIATE_MARK | GPR_FLAG_IMMEDIATE_SWEEP |
+ GPR_FLAG_STRESS | GPR_FLAG_MALLOC;
+
+ if (gc_stress_full_mark_after_malloc_p()) {
+ reason |= GPR_FLAG_FULL_MARK;
+ }
+ garbage_collect_with_gvl(objspace, reason);
}
}
@@ -7734,7 +8015,7 @@ objspace_malloc_increase(rb_objspace_t *objspace, void *mem, size_t new_size, si
gc_rest(objspace); /* gc_rest can reduce malloc_increase */
goto retry;
}
- garbage_collect_with_gvl(objspace, FALSE, FALSE, FALSE, GPR_FLAG_MALLOC);
+ garbage_collect_with_gvl(objspace, GPR_FLAG_MALLOC);
}
}
@@ -7773,7 +8054,7 @@ objspace_malloc_increase(rb_objspace_t *objspace, void *mem, size_t new_size, si
}
#if MALLOC_ALLOCATED_SIZE_CHECK
else {
- if (RGENGC_CHECK_MODE) assert(objspace->malloc_params.allocations > 0);
+ GC_ASSERT(objspace->malloc_params.allocations > 0);
}
#endif
}
@@ -7783,13 +8064,27 @@ objspace_malloc_increase(rb_objspace_t *objspace, void *mem, size_t new_size, si
#endif
}
+struct malloc_obj_info { /* 4 words */
+ size_t size;
+#if USE_GC_MALLOC_OBJ_INFO_DETAILS
+ size_t gen;
+ const char *file;
+ size_t line;
+#endif
+};
+
+#if USE_GC_MALLOC_OBJ_INFO_DETAILS
+const char *ruby_malloc_info_file;
+int ruby_malloc_info_line;
+#endif
+
static inline size_t
objspace_malloc_prepare(rb_objspace_t *objspace, size_t size)
{
if (size == 0) size = 1;
#if CALC_EXACT_MALLOC_SIZE
- size += sizeof(size_t);
+ size += sizeof(struct malloc_obj_info);
#endif
return size;
@@ -7798,9 +8093,22 @@ objspace_malloc_prepare(rb_objspace_t *objspace, size_t size)
static inline void *
objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
{
+ size = objspace_malloc_size(objspace, mem, size);
+ objspace_malloc_increase(objspace, mem, size, 0, MEMOP_TYPE_MALLOC);
+
#if CALC_EXACT_MALLOC_SIZE
- ((size_t *)mem)[0] = size;
- mem = (size_t *)mem + 1;
+ {
+ struct malloc_obj_info *info = mem;
+ info->size = size;
+#if USE_GC_MALLOC_OBJ_INFO_DETAILS
+ info->gen = objspace->profile.count;
+ info->file = ruby_malloc_info_file;
+ info->line = info->file ? ruby_malloc_info_line : 0;
+#else
+ info->file = NULL;
+#endif
+ mem = info + 1;
+ }
#endif
return mem;
@@ -7809,14 +8117,16 @@ objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
#define TRY_WITH_GC(alloc) do { \
objspace_malloc_gc_stress(objspace); \
if (!(alloc) && \
- (!garbage_collect_with_gvl(objspace, TRUE, TRUE, TRUE, GPR_FLAG_MALLOC) || /* full/immediate mark && immediate sweep */ \
+ (!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)
-/* this shouldn't be called directly.
- * objspace_xmalloc and objspace_xmalloc2 checks allocation size.
+/* these shouldn't be called directly.
+ * objspace_* functinos do not check allocation size.
*/
static void *
objspace_xmalloc0(rb_objspace_t *objspace, size_t size)
@@ -7825,20 +8135,10 @@ objspace_xmalloc0(rb_objspace_t *objspace, size_t size)
size = objspace_malloc_prepare(objspace, size);
TRY_WITH_GC(mem = malloc(size));
- size = objspace_malloc_size(objspace, mem, size);
- objspace_malloc_increase(objspace, mem, size, 0, MEMOP_TYPE_MALLOC);
+ RB_DEBUG_COUNTER_INC(heap_xmalloc);
return objspace_malloc_fixup(objspace, mem, size);
}
-static void *
-objspace_xmalloc(rb_objspace_t *objspace, size_t size)
-{
- if ((ssize_t)size < 0) {
- negative_size_allocation_error("too large allocation size");
- }
- return objspace_xmalloc0(objspace, size);
-}
-
static inline size_t
xmalloc2_size(const size_t count, const size_t elsize)
{
@@ -7850,17 +8150,11 @@ xmalloc2_size(const size_t count, const size_t elsize)
}
static void *
-objspace_xmalloc2(rb_objspace_t *objspace, size_t n, size_t size)
-{
- return objspace_xmalloc0(&rb_objspace, xmalloc2_size(n, size));
-}
-
-static void *
objspace_xrealloc(rb_objspace_t *objspace, void *ptr, size_t new_size, size_t old_size)
{
void *mem;
- if (!ptr) return objspace_xmalloc(objspace, new_size);
+ if (!ptr) return objspace_xmalloc0(objspace, new_size);
/*
* The behavior of realloc(ptr, 0) is implementation defined.
@@ -7873,9 +8167,12 @@ objspace_xrealloc(rb_objspace_t *objspace, void *ptr, size_t new_size, size_t ol
}
#if CALC_EXACT_MALLOC_SIZE
- new_size += sizeof(size_t);
- ptr = (size_t *)ptr - 1;
- old_size = ((size_t *)ptr)[0];
+ {
+ struct malloc_obj_info *info = (struct malloc_obj_info *)ptr - 1;
+ new_size += sizeof(struct malloc_obj_info);
+ ptr = info;
+ old_size = info->size;
+ }
#endif
old_size = objspace_malloc_size(objspace, ptr, old_size);
@@ -7883,25 +8180,137 @@ objspace_xrealloc(rb_objspace_t *objspace, void *ptr, size_t new_size, size_t ol
new_size = objspace_malloc_size(objspace, mem, new_size);
#if CALC_EXACT_MALLOC_SIZE
- ((size_t *)mem)[0] = new_size;
- mem = (size_t *)mem + 1;
+ {
+ struct malloc_obj_info *info = mem;
+ info->size = new_size;
+ mem = info + 1;
+ }
#endif
objspace_malloc_increase(objspace, mem, new_size, old_size, MEMOP_TYPE_REALLOC);
+ RB_DEBUG_COUNTER_INC(heap_xrealloc);
return mem;
}
+#if CALC_EXACT_MALLOC_SIZE && USE_GC_MALLOC_OBJ_INFO_DETAILS
+
+#define MALLOC_INFO_GEN_SIZE 100
+#define MALLOC_INFO_SIZE_SIZE 10
+static size_t malloc_info_gen_cnt[MALLOC_INFO_GEN_SIZE];
+static size_t malloc_info_gen_size[MALLOC_INFO_GEN_SIZE];
+static size_t malloc_info_size[MALLOC_INFO_SIZE_SIZE+1];
+static st_table *malloc_info_file_table;
+
+static int
+mmalloc_info_file_i(st_data_t key, st_data_t val, st_data_t dmy)
+{
+ const char *file = (void *)key;
+ const size_t *data = (void *)val;
+
+ fprintf(stderr, "%s\t%d\t%d\n", file, (int)data[0], (int)data[1]);
+
+ return ST_CONTINUE;
+}
+
+__attribute__((destructor))
+void
+rb_malloc_info_show_results(void)
+{
+ int i;
+
+ fprintf(stderr, "* malloc_info gen statistics\n");
+ for (i=0; i<MALLOC_INFO_GEN_SIZE; i++) {
+ if (i == MALLOC_INFO_GEN_SIZE-1) {
+ fprintf(stderr, "more\t%d\t%d\n", (int)malloc_info_gen_cnt[i], (int)malloc_info_gen_size[i]);
+ }
+ else {
+ fprintf(stderr, "%d\t%d\t%d\n", i, (int)malloc_info_gen_cnt[i], (int)malloc_info_gen_size[i]);
+ }
+ }
+
+ fprintf(stderr, "* malloc_info size statistics\n");
+ for (i=0; i<MALLOC_INFO_SIZE_SIZE; i++) {
+ int s = 16 << i;
+ fprintf(stderr, "%d\t%d\n", (int)s, (int)malloc_info_size[i]);
+ }
+ fprintf(stderr, "more\t%d\n", (int)malloc_info_size[i]);
+
+ if (malloc_info_file_table) {
+ fprintf(stderr, "* malloc_info file statistics\n");
+ st_foreach(malloc_info_file_table, mmalloc_info_file_i, 0);
+ }
+}
+#else
+void
+rb_malloc_info_show_results(void)
+{
+}
+#endif
+
static void
objspace_xfree(rb_objspace_t *objspace, void *ptr, size_t old_size)
{
#if CALC_EXACT_MALLOC_SIZE
- ptr = ((size_t *)ptr) - 1;
- old_size = ((size_t*)ptr)[0];
+ struct malloc_obj_info *info = (struct malloc_obj_info *)ptr - 1;
+ ptr = info;
+ old_size = info->size;
+
+#if USE_GC_MALLOC_OBJ_INFO_DETAILS
+ {
+ int gen = (int)(objspace->profile.count - info->gen);
+ int gen_index = gen >= MALLOC_INFO_GEN_SIZE ? MALLOC_INFO_GEN_SIZE-1 : gen;
+ int i;
+
+ malloc_info_gen_cnt[gen_index]++;
+ malloc_info_gen_size[gen_index] += info->size;
+
+ for (i=0; i<MALLOC_INFO_SIZE_SIZE; i++) {
+ size_t s = 16 << i;
+ if (info->size <= s) {
+ malloc_info_size[i]++;
+ goto found;
+ }
+ }
+ malloc_info_size[i]++;
+ found:;
+
+ {
+ st_data_t key = (st_data_t)info->file;
+ size_t *data;
+
+ if (malloc_info_file_table == NULL) {
+ malloc_info_file_table = st_init_numtable_with_size(1024);
+ }
+ if (st_lookup(malloc_info_file_table, key, (st_data_t *)&data)) {
+ /* hit */
+ }
+ else {
+ data = malloc(sizeof(size_t) * 2);
+ if (data == NULL) rb_bug("objspace_xfree: can not allocate memory");
+ data[0] = data[1] = 0;
+ st_insert(malloc_info_file_table, key, (st_data_t)data);
+ }
+ data[0] ++;
+ data[1] += info->size;
+ };
+#if 0 /* verbose output */
+ if (gen >= 2) {
+ if (info->file) {
+ fprintf(stderr, "free - size:%d, gen:%d, pos: %s:%d\n", (int)info->size, gen, info->file, (int)info->line);
+ }
+ else {
+ fprintf(stderr, "free - size:%d, gen:%d\n", (int)info->size, gen);
+ }
+ }
+#endif
+ }
+#endif
#endif
old_size = objspace_malloc_size(objspace, ptr, old_size);
free(ptr);
+ RB_DEBUG_COUNTER_INC(heap_xfree);
objspace_malloc_increase(objspace, ptr, 0, old_size, MEMOP_TYPE_FREE);
}
@@ -7913,9 +8322,12 @@ ruby_xmalloc0(size_t size)
}
void *
-ruby_xmalloc(size_t size)
+ruby_xmalloc_body(size_t size)
{
- return objspace_xmalloc(&rb_objspace, size);
+ if ((ssize_t)size < 0) {
+ negative_size_allocation_error("too large allocation size");
+ }
+ return ruby_xmalloc0(size);
}
void
@@ -7927,30 +8339,25 @@ ruby_malloc_size_overflow(size_t count, size_t elsize)
}
void *
-ruby_xmalloc2(size_t n, size_t size)
+ruby_xmalloc2_body(size_t n, size_t size)
{
- return objspace_xmalloc2(&rb_objspace, n, size);
+ return objspace_xmalloc0(&rb_objspace, xmalloc2_size(n, size));
}
static void *
-objspace_xcalloc(rb_objspace_t *objspace, size_t count, size_t elsize)
+objspace_xcalloc(rb_objspace_t *objspace, size_t size)
{
void *mem;
- size_t size;
- size = xmalloc2_size(count, elsize);
size = objspace_malloc_prepare(objspace, size);
-
TRY_WITH_GC(mem = calloc(1, size));
- size = objspace_malloc_size(objspace, mem, size);
- objspace_malloc_increase(objspace, mem, size, 0, MEMOP_TYPE_MALLOC);
return objspace_malloc_fixup(objspace, mem, size);
}
void *
-ruby_xcalloc(size_t n, size_t size)
+ruby_xcalloc_body(size_t n, size_t size)
{
- return objspace_xcalloc(&rb_objspace, n, size);
+ return objspace_xcalloc(&rb_objspace, xmalloc2_size(n, size));
}
#ifdef ruby_sized_xrealloc
@@ -7959,11 +8366,15 @@ ruby_xcalloc(size_t n, size_t size)
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");
+ }
+
return objspace_xrealloc(&rb_objspace, ptr, new_size, old_size);
}
void *
-ruby_xrealloc(void *ptr, size_t new_size)
+ruby_xrealloc_body(void *ptr, size_t new_size)
{
return ruby_sized_xrealloc(ptr, new_size, 0);
}
@@ -7982,7 +8393,7 @@ ruby_sized_xrealloc2(void *ptr, size_t n, size_t size, size_t old_n)
}
void *
-ruby_xrealloc2(void *ptr, size_t n, size_t size)
+ruby_xrealloc2_body(void *ptr, size_t n, size_t size)
{
return ruby_sized_xrealloc2(ptr, n, size, 0);
}
@@ -8012,13 +8423,23 @@ ruby_mimmalloc(size_t size)
{
void *mem;
#if CALC_EXACT_MALLOC_SIZE
- size += sizeof(size_t);
+ size += sizeof(struct malloc_obj_info);
#endif
mem = malloc(size);
#if CALC_EXACT_MALLOC_SIZE
/* set 0 for consistency of allocated_size/allocations */
- ((size_t *)mem)[0] = 0;
- mem = (size_t *)mem + 1;
+ {
+ struct malloc_obj_info *info = mem;
+ info->size = 0;
+#if USE_GC_MALLOC_OBJ_INFO_DETAILS
+ info->gen = 0;
+ info->file = NULL;
+ info->line = 0;
+#else
+ info->file = NULL;
+#endif
+ mem = info + 1;
+ }
#endif
return mem;
}
@@ -8026,24 +8447,28 @@ ruby_mimmalloc(size_t size)
void
ruby_mimfree(void *ptr)
{
- size_t *mem = (size_t *)ptr;
#if CALC_EXACT_MALLOC_SIZE
- mem = mem - 1;
+ struct malloc_obj_info *info = (struct malloc_obj_info *)ptr - 1;
+ ptr = info;
#endif
- free(mem);
+ free(ptr);
}
void *
rb_alloc_tmp_buffer_with_count(volatile VALUE *store, size_t size, size_t cnt)
{
- NODE *s;
void *ptr;
+ VALUE imemo;
+ rb_imemo_tmpbuf_t *tmpbuf;
- s = rb_node_newnode(NODE_ALLOCA, 0, 0, 0);
+ /* Keep the order; allocate an empty imemo first then xmalloc, to
+ * get rid of potential memory leak */
+ imemo = rb_imemo_tmpbuf_auto_free_maybe_mark_buffer(NULL, 0);
+ *store = imemo;
ptr = ruby_xmalloc0(size);
- s->u1.value = (VALUE)ptr;
- s->u3.cnt = cnt;
- *store = (VALUE)s;
+ tmpbuf = (rb_imemo_tmpbuf_t *)imemo;
+ tmpbuf->ptr = ptr;
+ tmpbuf->cnt = cnt;
return ptr;
}
@@ -8062,10 +8487,10 @@ rb_alloc_tmp_buffer(volatile VALUE *store, long len)
void
rb_free_tmp_buffer(volatile VALUE *store)
{
- VALUE s = ATOMIC_VALUE_EXCHANGE(*store, 0);
+ rb_imemo_tmpbuf_t *s = (rb_imemo_tmpbuf_t*)ATOMIC_VALUE_EXCHANGE(*store, 0);
if (s) {
- void *ptr = ATOMIC_PTR_EXCHANGE(RNODE(s)->u1.node, 0);
- RNODE(s)->u3.cnt = 0;
+ void *ptr = ATOMIC_PTR_EXCHANGE(s->ptr, 0);
+ s->cnt = 0;
ruby_xfree(ptr);
}
}
@@ -8228,6 +8653,7 @@ wmap_final_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
return ST_CONTINUE;
}
+/* :nodoc: */
static VALUE
wmap_finalize(VALUE self, VALUE objid)
{
@@ -8440,7 +8866,7 @@ wmap_aset_update(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
else {
optr = 0;
size = 1;
- ptr = ruby_xmalloc2(2, sizeof(VALUE));
+ ptr = ruby_xmalloc0(2 * sizeof(VALUE));
}
ptr[0] = size;
ptr[size] = (VALUE)arg;
@@ -8489,6 +8915,7 @@ wmap_has_key(VALUE self, VALUE key)
return NIL_P(wmap_aref(self, key)) ? Qfalse : Qtrue;
}
+/* Returns the number of referenced objects */
static VALUE
wmap_size(VALUE self)
{
@@ -9027,12 +9454,7 @@ gc_profile_report(int argc, VALUE *argv, VALUE self)
{
VALUE out;
- if (argc == 0) {
- out = rb_stdout;
- }
- else {
- rb_scan_args(argc, argv, "01", &out);
- }
+ out = (!rb_check_arity(argc, 0, 1) ? rb_stdout : argv[0]);
gc_profile_dump_on(out, rb_io_write);
return Qnil;
@@ -9142,7 +9564,6 @@ type_name(int type, VALUE obj)
TYPE_NAME(T_FIXNUM);
TYPE_NAME(T_UNDEF);
TYPE_NAME(T_IMEMO);
- TYPE_NAME(T_NODE);
TYPE_NAME(T_ICLASS);
TYPE_NAME(T_ZOMBIE);
case T_DATA:
@@ -9183,20 +9604,22 @@ method_type_name(rb_method_type_t type)
/* from array.c */
# define ARY_SHARED_P(ary) \
- (assert(!FL_TEST((ary), ELTS_SHARED) || !FL_TEST((ary), RARRAY_EMBED_FLAG)), \
+ (GC_ASSERT(!FL_TEST((ary), ELTS_SHARED) || !FL_TEST((ary), RARRAY_EMBED_FLAG)), \
FL_TEST((ary),ELTS_SHARED)!=0)
# define ARY_EMBED_P(ary) \
- (assert(!FL_TEST((ary), ELTS_SHARED) || !FL_TEST((ary), RARRAY_EMBED_FLAG)), \
+ (GC_ASSERT(!FL_TEST((ary), ELTS_SHARED) || !FL_TEST((ary), RARRAY_EMBED_FLAG)), \
FL_TEST((ary), RARRAY_EMBED_FLAG)!=0)
static void
rb_raw_iseq_info(char *buff, const int buff_size, const rb_iseq_t *iseq)
{
- if (iseq->body->location.label) {
+ if (iseq->body && iseq->body->location.label) {
+ VALUE path = rb_iseq_path(iseq);
+ VALUE n = iseq->body->location.first_lineno;
snprintf(buff, buff_size, "%s %s@%s:%d", buff,
RSTRING_PTR(iseq->body->location.label),
- RSTRING_PTR(iseq->body->location.path),
- FIX2INT(iseq->body->location.first_lineno));
+ RSTRING_PTR(path),
+ n ? FIX2INT(n) : 0 );
}
}
@@ -9205,6 +9628,13 @@ rb_raw_obj_info(char *buff, const int buff_size, VALUE obj)
{
if (SPECIAL_CONST_P(obj)) {
snprintf(buff, buff_size, "%s", obj_type_name(obj));
+
+ if (FIXNUM_P(obj)) {
+ snprintf(buff, buff_size, "%s %ld", buff, FIX2LONG(obj));
+ }
+ else if (SYMBOL_P(obj)) {
+ snprintf(buff, buff_size, "%s %s", buff, rb_id2name(SYM2ID(obj)));
+ }
}
else {
#define TF(c) ((c) != 0 ? "true" : "false")
@@ -9213,13 +9643,21 @@ rb_raw_obj_info(char *buff, const int buff_size, VALUE obj)
#if USE_RGENGC
const int age = RVALUE_FLAGS_AGE(RBASIC(obj)->flags);
- snprintf(buff, buff_size, "%p [%d%s%s%s%s] %s",
- (void *)obj, age,
- C(RVALUE_UNCOLLECTIBLE_BITMAP(obj), "L"),
- C(RVALUE_MARK_BITMAP(obj), "M"),
- C(RVALUE_MARKING_BITMAP(obj), "R"),
- C(RVALUE_WB_UNPROTECTED_BITMAP(obj), "U"),
- obj_type_name(obj));
+ if (is_pointer_to_heap(&rb_objspace, (void *)obj)) {
+ snprintf(buff, buff_size, "%p [%d%s%s%s%s] %s",
+ (void *)obj, age,
+ C(RVALUE_UNCOLLECTIBLE_BITMAP(obj), "L"),
+ C(RVALUE_MARK_BITMAP(obj), "M"),
+ C(RVALUE_MARKING_BITMAP(obj), "R"),
+ C(RVALUE_WB_UNPROTECTED_BITMAP(obj), "U"),
+ obj_type_name(obj));
+ }
+ else {
+ /* fake */
+ snprintf(buff, buff_size, "%p [%dXXXX] %s",
+ (void *)obj, age,
+ obj_type_name(obj));
+ }
#else
snprintf(buff, buff_size, "%p [%s] %s",
(void *)obj,
@@ -9246,74 +9684,131 @@ rb_raw_obj_info(char *buff, const int buff_size, VALUE obj)
switch (type) {
case T_NODE:
- snprintf(buff, buff_size, "%s (%s)", buff,
- ruby_node_name(nd_type(obj)));
+ UNEXPECTED_NODE(rb_raw_obj_info);
break;
case T_ARRAY:
- snprintf(buff, buff_size, "%s [%s%s] len: %d", buff,
- C(ARY_EMBED_P(obj), "E"),
- C(ARY_SHARED_P(obj), "S"),
- (int)RARRAY_LEN(obj));
+ if (FL_TEST(obj, ELTS_SHARED)) {
+ snprintf(buff, buff_size, "%s shared -> %s", buff,
+ rb_obj_info(RARRAY(obj)->as.heap.aux.shared));
+ }
+ else if (FL_TEST(obj, RARRAY_EMBED_FLAG)) {
+ snprintf(buff, buff_size, "%s [%s%s] len: %d (embed)", buff,
+ C(ARY_EMBED_P(obj), "E"),
+ C(ARY_SHARED_P(obj), "S"),
+ (int)RARRAY_LEN(obj));
+ }
+ else {
+ snprintf(buff, buff_size, "%s [%s%s%s] len: %d, capa:%d ptr:%p", buff,
+ C(ARY_EMBED_P(obj), "E"),
+ C(ARY_SHARED_P(obj), "S"),
+ C(RARRAY_TRANSIENT_P(obj), "T"),
+ (int)RARRAY_LEN(obj),
+ ARY_EMBED_P(obj) ? -1 : (int)RARRAY(obj)->as.heap.aux.capa,
+ (void *)RARRAY_CONST_PTR_TRANSIENT(obj));
+ }
break;
case T_STRING: {
- snprintf(buff, buff_size, "%s %s", buff, RSTRING_PTR(obj));
- break;
- }
- case T_CLASS: {
- VALUE class_path = rb_class_path_cached(obj);
- if (!NIL_P(class_path)) {
- snprintf(buff, buff_size, "%s %s", buff, RSTRING_PTR(class_path));
- }
- break;
+ snprintf(buff, buff_size, "%s %s", buff, RSTRING_PTR(obj));
+ break;
}
+ case T_HASH: {
+ snprintf(buff, buff_size, "%s [%c%c] %d", buff,
+ RHASH_AR_TABLE_P(obj) ? 'A' : 'S',
+ RHASH_TRANSIENT_P(obj) ? 'T' : ' ',
+ (int)RHASH_SIZE(obj));
+ break;
+ }
+ case T_CLASS:
+ case T_MODULE:
+ {
+ VALUE class_path = rb_class_path_cached(obj);
+ if (!NIL_P(class_path)) {
+ snprintf(buff, buff_size, "%s %s", buff, RSTRING_PTR(class_path));
+ }
+ break;
+ }
+ case T_ICLASS:
+ {
+ VALUE class_path = rb_class_path_cached(RBASIC_CLASS(obj));
+ if (!NIL_P(class_path)) {
+ snprintf(buff, buff_size, "%s src:%s", buff, RSTRING_PTR(class_path));
+ }
+ break;
+ }
+ case T_OBJECT:
+ {
+ uint32_t len = ROBJECT_NUMIV(obj);
+
+ if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) {
+ snprintf(buff, buff_size, "%s (embed) len:%d", buff, len);
+ }
+ else {
+ VALUE *ptr = ROBJECT_IVPTR(obj);
+ snprintf(buff, buff_size, "%s len:%d ptr:%p", buff, len, (void *)ptr);
+ }
+ }
+ break;
case T_DATA: {
- const rb_iseq_t *iseq;
- if (rb_obj_is_proc(obj) && (iseq = vm_proc_iseq(obj)) != NULL) {
- rb_raw_iseq_info(buff, buff_size, iseq);
- }
- else {
- const char * const type_name = rb_objspace_data_type_name(obj);
- if (type_name) {
- snprintf(buff, buff_size, "%s %s", buff, type_name);
- }
- }
- break;
+ 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, buff_size, iseq);
+ }
+ else {
+ const char * const type_name = rb_objspace_data_type_name(obj);
+ if (type_name) {
+ snprintf(buff, buff_size, "%s %s", buff, type_name);
+ }
+ }
+ break;
}
case T_IMEMO: {
- const char *imemo_name;
- switch (imemo_type(obj)) {
+ const char *imemo_name = "\0";
+ switch (imemo_type(obj)) {
#define IMEMO_NAME(x) case imemo_##x: imemo_name = #x; break;
- IMEMO_NAME(env);
- IMEMO_NAME(cref);
- IMEMO_NAME(svar);
- IMEMO_NAME(throw_data);
- IMEMO_NAME(ifunc);
- IMEMO_NAME(memo);
- IMEMO_NAME(ment);
- IMEMO_NAME(iseq);
+ IMEMO_NAME(env);
+ IMEMO_NAME(cref);
+ IMEMO_NAME(svar);
+ IMEMO_NAME(throw_data);
+ IMEMO_NAME(ifunc);
+ IMEMO_NAME(memo);
+ IMEMO_NAME(ment);
+ IMEMO_NAME(iseq);
+ IMEMO_NAME(tmpbuf);
+ IMEMO_NAME(ast);
+ IMEMO_NAME(parser_strterm);
#undef IMEMO_NAME
- }
- snprintf(buff, buff_size, "%s %s", buff, imemo_name);
+ default: UNREACHABLE;
+ }
+ snprintf(buff, buff_size, "%s %s", buff, imemo_name);
- switch (imemo_type(obj)) {
- case imemo_ment: {
- const rb_method_entry_t *me = &RANY(obj)->as.imemo.ment;
+ switch (imemo_type(obj)) {
+ case imemo_ment: {
+ const rb_method_entry_t *me = &RANY(obj)->as.imemo.ment;
+ if (me->def) {
snprintf(buff, buff_size, "%s (called_id: %s, type: %s, alias: %d, owner: %s, defined_class: %s)", buff,
rb_id2name(me->called_id),
method_type_name(me->def->type),
me->def->alias_count,
obj_info(me->owner),
obj_info(me->defined_class));
- break;
}
- case imemo_iseq: {
- const rb_iseq_t *iseq = (const rb_iseq_t *)obj;
- rb_raw_iseq_info(buff, buff_size, iseq);
- break;
+ else {
+ snprintf(buff, buff_size, "%s", rb_id2name(me->called_id));
}
- default:
- break;
+ break;
}
+ case imemo_iseq: {
+ const rb_iseq_t *iseq = (const rb_iseq_t *)obj;
+ rb_raw_iseq_info(buff, buff_size, iseq);
+ break;
+ }
+ default:
+ break;
+ }
}
default:
break;
@@ -9350,7 +9845,7 @@ obj_info(VALUE obj)
}
#endif
-const char *
+MJIT_FUNC_EXPORTED const char *
rb_obj_info(VALUE obj)
{
if (!rb_special_const_p(obj)) {
@@ -9418,6 +9913,13 @@ rb_gcdebug_sentinel(VALUE obj, const char *name)
#endif /* GC_DEBUG */
#if GC_DEBUG_STRESS_TO_CLASS
+/*
+ * call-seq:
+ * GC.add_stress_to_class(class[, ...])
+ *
+ * Raises NoMemoryError when allocating an instance of the given classes.
+ *
+ */
static VALUE
rb_gcdebug_add_stress_to_class(int argc, VALUE *argv, VALUE self)
{
@@ -9430,6 +9932,14 @@ rb_gcdebug_add_stress_to_class(int argc, VALUE *argv, VALUE self)
return self;
}
+/*
+ * call-seq:
+ * GC.remove_stress_to_class(class[, ...])
+ *
+ * No longer raises NoMemoryError when allocating an instance of the
+ * given classes.
+ *
+ */
static VALUE
rb_gcdebug_remove_stress_to_class(int argc, VALUE *argv, VALUE self)
{
@@ -9537,6 +10047,7 @@ Init_GC(void)
rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_PAGE_BITMAP_SIZE")), SIZET2NUM(HEAP_PAGE_BITMAP_SIZE));
rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_PAGE_BITMAP_PLANES")), SIZET2NUM(HEAP_PAGE_BITMAP_PLANES));
OBJ_FREEZE(gc_constants);
+ /* internal constants */
rb_define_const(rb_mGC, "INTERNAL_CONSTANTS", gc_constants);
rb_mProfiler = rb_define_module_under(rb_mGC, "Profiler");
@@ -9588,6 +10099,7 @@ Init_GC(void)
/* internal methods */
rb_define_singleton_method(rb_mGC, "verify_internal_consistency", gc_verify_internal_consistency, 0);
+ rb_define_singleton_method(rb_mGC, "verify_transient_heap_internal_consistency", gc_verify_transient_heap_internal_consistency, 0);
#if MALLOC_ALLOCATED_SIZE
rb_define_singleton_method(rb_mGC, "malloc_allocated_size", gc_malloc_allocated_size, 0);
rb_define_singleton_method(rb_mGC, "malloc_allocations", gc_malloc_allocations, 0);
@@ -9598,9 +10110,9 @@ Init_GC(void)
rb_define_singleton_method(rb_mGC, "remove_stress_to_class", rb_gcdebug_remove_stress_to_class, -1);
#endif
- /* ::GC::OPTS, which shows GC build options */
{
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);
@@ -9619,3 +10131,69 @@ Init_GC(void)
OBJ_FREEZE(opts);
}
}
+
+#ifdef ruby_xmalloc
+#undef ruby_xmalloc
+#endif
+#ifdef ruby_xmalloc2
+#undef ruby_xmalloc2
+#endif
+#ifdef ruby_xcalloc
+#undef ruby_xcalloc
+#endif
+#ifdef ruby_xrealloc
+#undef ruby_xrealloc
+#endif
+#ifdef ruby_xrealloc2
+#undef ruby_xrealloc2
+#endif
+
+void *
+ruby_xmalloc(size_t size)
+{
+#if USE_GC_MALLOC_OBJ_INFO_DETAILS
+ ruby_malloc_info_file = __FILE__;
+ ruby_malloc_info_line = __LINE__;
+#endif
+ return ruby_xmalloc_body(size);
+}
+
+void *
+ruby_xmalloc2(size_t n, size_t size)
+{
+#if USE_GC_MALLOC_OBJ_INFO_DETAILS
+ ruby_malloc_info_file = __FILE__;
+ ruby_malloc_info_line = __LINE__;
+#endif
+ return ruby_xmalloc2_body(n, size);
+}
+
+void *
+ruby_xcalloc(size_t n, size_t size)
+{
+#if USE_GC_MALLOC_OBJ_INFO_DETAILS
+ ruby_malloc_info_file = __FILE__;
+ ruby_malloc_info_line = __LINE__;
+#endif
+ return ruby_xcalloc_body(n, size);
+}
+
+void *
+ruby_xrealloc(void *ptr, size_t new_size)
+{
+#if USE_GC_MALLOC_OBJ_INFO_DETAILS
+ ruby_malloc_info_file = __FILE__;
+ ruby_malloc_info_line = __LINE__;
+#endif
+ return ruby_xrealloc_body(ptr, new_size);
+}
+
+void *
+ruby_xrealloc2(void *ptr, size_t n, size_t new_size)
+{
+#if USE_GC_MALLOC_OBJ_INFO_DETAILS
+ ruby_malloc_info_file = __FILE__;
+ ruby_malloc_info_line = __LINE__;
+#endif
+ return ruby_xrealloc2_body(ptr, n, new_size);
+}
diff --git a/gc.h b/gc.h
index c723106137..2c91e06620 100644
--- a/gc.h
+++ b/gc.h
@@ -2,9 +2,9 @@
#ifndef RUBY_GC_H
#define RUBY_GC_H 1
-#if defined(__x86_64__) && !defined(_ILP32) && defined(__GNUC__) && !defined(__native_client__)
+#if defined(__x86_64__) && !defined(_ILP32) && defined(__GNUC__)
#define SET_MACHINE_STACK_END(p) __asm__ __volatile__ ("movq\t%%rsp, %0" : "=r" (*(p)))
-#elif defined(__i386) && defined(__GNUC__) && !defined(__native_client__)
+#elif defined(__i386) && defined(__GNUC__)
#define SET_MACHINE_STACK_END(p) __asm__ __volatile__ ("movl\t%%esp, %0" : "=r" (*(p)))
#else
NOINLINE(void rb_gc_set_stack_end(VALUE **stack_end_p));
@@ -90,6 +90,8 @@ const char *rb_obj_info(VALUE obj);
const char *rb_raw_obj_info(char *buff, const int buff_size, VALUE obj);
void rb_obj_info_dump(VALUE obj);
+struct rb_thread_struct;
+
RUBY_SYMBOL_EXPORT_BEGIN
/* exports for objspace module */
diff --git a/gems/bundled_gems b/gems/bundled_gems
index eba107724d..7c896b73ce 100644
--- a/gems/bundled_gems
+++ b/gems/bundled_gems
@@ -1,7 +1,7 @@
-did_you_mean 1.0.2
-minitest 5.9.1
-net-telnet 0.1.1
-power_assert 0.3.1
-rake 11.3.0
-test-unit 3.2.1
-xmlrpc 0.1.1
+did_you_mean 1.3.0 https://github.com/yuki24/did_you_mean
+minitest 5.11.3 https://github.com/seattlerb/minitest
+net-telnet 0.2.0 https://github.com/ruby/net-telnet
+power_assert 1.1.3 https://github.com/k-tsj/power_assert
+rake 12.3.3 https://github.com/ruby/rake
+test-unit 3.2.9 https://github.com/test-unit/test-unit
+xmlrpc 0.3.0 https://github.com/ruby/xmlrpc
diff --git a/golf_prelude.rb b/golf_prelude.rb
index 8034601287..204f659b0e 100644
--- a/golf_prelude.rb
+++ b/golf_prelude.rb
@@ -50,6 +50,11 @@ class Object
puts "#{a}ello, #{b}orld#{c}"
end
+ def f(m = 100)
+ 1.upto(m){|n|puts'FizzBuzz
+'[i=n**4%-15,i+13]||n}
+ end
+
alias say puts
def do_while
@@ -85,7 +90,7 @@ class String
split('')
end
- (Array.instance_methods - instance_methods - [:to_ary, :transpose, :flatten, :flatten!, :compact, :compact!, :assoc, :rassoc]).each{|meth|
+ (Array.instance_methods - instance_methods - %i[to_ary transpose flatten flatten! compact compact! assoc rassoc]).each{|meth|
eval "
def #{meth}(*args, &block)
a = to_a
diff --git a/goruby.c b/goruby.c
index 5ac4c96d12..ddb62562d4 100644
--- a/goruby.c
+++ b/goruby.c
@@ -35,7 +35,11 @@ goruby_options(int argc, char **argv)
if ((isatty(0) && isatty(1) && isatty(2)) && (pipe(rw) == 0)) {
ssize_t n;
infd = dup(0);
- if (infd < 0) return NULL;
+ 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);
@@ -46,6 +50,7 @@ goruby_options(int argc, char **argv)
return ret;
}
else {
+ no_irb:
return ruby_options(argc, argv);
}
}
diff --git a/hash.c b/hash.c
index 0ac95730b3..65a0419af3 100644
--- a/hash.c
+++ b/hash.c
@@ -11,14 +11,18 @@
**********************************************************************/
-#include "internal.h"
+#include "ruby/encoding.h"
#include "ruby/st.h"
#include "ruby/util.h"
+#include "internal.h"
#include <errno.h>
#include "probes.h"
#include "id.h"
#include "symbol.h"
-
+#include "gc.h"
+#include "debug_counter.h"
+#include "transient_heap.h"
+#include "ruby_assert.h"
#ifdef __APPLE__
# ifdef HAVE_CRT_EXTERNS_H
# include <crt_externs.h>
@@ -27,6 +31,10 @@
# endif
#endif
+#ifndef HASH_DEBUG
+#define HASH_DEBUG 0
+#endif
+
#define HAS_EXTRA_STATES(hash, klass) ( \
((klass = has_extra_methods(rb_obj_class(hash))) != 0) || \
FL_TEST((hash), FL_EXIVAR|FL_TAINT|HASH_PROC_DEFAULT) || \
@@ -84,12 +92,6 @@ static VALUE envtbl;
static ID id_hash, id_yield, id_default, id_flatten_bang;
VALUE
-rb_hash_ifnone(VALUE h)
-{
- return RHASH_IFNONE(h);
-}
-
-VALUE
rb_hash_set_ifnone(VALUE hash, VALUE ifnone)
{
RB_OBJ_WRITE(hash, (&RHASH(hash)->ifnone), ifnone);
@@ -157,19 +159,13 @@ rb_dbl_long_hash(double d)
union {double d; uint64_t i;} u;
u.d = d;
- return rb_objid_hash(u.i);
+ return rb_objid_hash(rb_hash_start(u.i));
}
#endif
}
-#if SIZEOF_INT == SIZEOF_VOIDP
-static const st_index_t str_seed = 0xfa835867;
-#else
-static const st_index_t str_seed = 0xc42b5e2e6480b23bULL;
-#endif
-
-static inline st_index_t
-any_hash_general(VALUE a, int strong_p, st_index_t (*other_func)(VALUE))
+static inline long
+any_hash(VALUE a, st_index_t (*other_func)(VALUE))
{
VALUE hval;
st_index_t hnum;
@@ -177,6 +173,7 @@ any_hash_general(VALUE a, int strong_p, st_index_t (*other_func)(VALUE))
if (SPECIAL_CONST_P(a)) {
if (STATIC_SYM_P(a)) {
hnum = a >> (RUBY_SPECIAL_SHIFT + ID_SCOPE_SHIFT);
+ hnum = rb_hash_start(hnum);
goto out;
}
else if (FLONUM_P(a)) {
@@ -186,9 +183,7 @@ any_hash_general(VALUE a, int strong_p, st_index_t (*other_func)(VALUE))
hnum = rb_objid_hash((st_index_t)a);
}
else if (BUILTIN_TYPE(a) == T_STRING) {
- hnum = (strong_p
- ? rb_str_hash(a)
- : st_hash(RSTRING_PTR(a), RSTRING_LEN(a), str_seed));
+ hnum = rb_str_hash(a);
}
else if (BUILTIN_TYPE(a) == T_SYMBOL) {
hnum = RSYMBOL(a)->hashval;
@@ -205,8 +200,16 @@ any_hash_general(VALUE a, int strong_p, st_index_t (*other_func)(VALUE))
hnum = other_func(a);
}
out:
+#if SIZEOF_LONG < SIZEOF_ST_INDEX_T
+ if (hnum > 0)
+ hnum &= (unsigned long)-1 >> 2;
+ else
+ hnum |= ~((unsigned long)-1 >> 2);
+#else
hnum <<= 1;
- return (st_index_t)RSHIFT(hnum, 1);
+ hnum = RSHIFT(hnum, 1);
+#endif
+ return (long)hnum;
}
static st_index_t
@@ -216,23 +219,9 @@ obj_any_hash(VALUE obj)
return FIX2LONG(obj);
}
-static inline st_index_t
-any_hash_weak(VALUE a, st_index_t (*other_func)(VALUE)) {
- return any_hash_general(a, FALSE, other_func);
-}
-
-static st_index_t
-rb_any_hash_weak(VALUE a) {
- return any_hash_weak(a, obj_any_hash);
-}
-
-static inline st_index_t
-any_hash(VALUE a, st_index_t (*other_func)(VALUE)) {
- return any_hash_general(a, TRUE, other_func);
-}
-
static st_index_t
-rb_any_hash(VALUE a) {
+rb_any_hash(VALUE a)
+{
return any_hash(a, obj_any_hash);
}
@@ -241,15 +230,15 @@ rb_any_hash(VALUE a) {
tailored Spooky or City hash function can be. */
/* Here we two primes with random bit generation. */
-static const uint64_t prime1 = 0x2e0bb864e9ea7df5ULL;
-static const uint64_t prime2 = 0xcdb32970830fcaa1ULL;
+static const uint64_t prime1 = ((uint64_t)0x2e0bb864 << 32) | 0xe9ea7df5;
+static const uint32_t prime2 = 0x830fcab9;
static inline uint64_t
-mult_and_mix (uint64_t m1, uint64_t m2)
+mult_and_mix(uint64_t m1, uint64_t m2)
{
-#if defined(__GNUC__) && UINT_MAX != ULONG_MAX
- __uint128_t r = (__uint128_t) m1 * (__uint128_t) m2;
+#if defined HAVE_UINT128_T
+ uint128_t r = (uint128_t) m1 * (uint128_t) m2;
return (uint64_t) (r >> 64) ^ (uint64_t) r;
#else
uint64_t hm1 = m1 >> 32, hm2 = m2 >> 32;
@@ -263,7 +252,7 @@ mult_and_mix (uint64_t m1, uint64_t m2)
}
static inline uint64_t
-key64_hash (uint64_t key, uint32_t seed)
+key64_hash(uint64_t key, uint32_t seed)
{
return mult_and_mix(key + seed, prime1);
}
@@ -271,7 +260,7 @@ key64_hash (uint64_t key, uint32_t seed)
long
rb_objid_hash(st_index_t index)
{
- return (long)key64_hash(index, (uint32_t)prime2);
+ return (long)key64_hash(rb_hash_start(index), prime2);
}
static st_index_t
@@ -283,19 +272,12 @@ objid_hash(VALUE obj)
VALUE
rb_obj_hash(VALUE obj)
{
- st_index_t hnum = any_hash(obj, objid_hash);
+ long hnum = any_hash(obj, objid_hash);
return ST2FIX(hnum);
}
-int
-rb_hash_iter_lev(VALUE h)
-{
- return RHASH_ITER_LEV(h);
-}
-
static const struct st_hash_type objhash = {
rb_any_cmp,
- rb_any_hash_weak,
rb_any_hash,
};
@@ -311,11 +293,13 @@ rb_ident_hash(st_data_t n)
* many integers get interpreted as 2.0 or -2.0 [Bug #10761]
*/
if (FLONUM_P(n)) {
- n ^= (st_data_t)rb_float_value(n);
+ union { double d; st_data_t i; } u;
+ u.d = rb_float_value(n);
+ n ^= u.i;
}
#endif
- return (st_index_t) key64_hash((st_index_t)n, (uint32_t) prime2);
+ return (st_index_t)key64_hash(rb_hash_start((st_index_t)n), prime2);
}
static const struct st_hash_type identhash = {
@@ -323,6 +307,805 @@ static const struct st_hash_type identhash = {
rb_ident_hash,
};
+#define EQUAL(x,y) ((x) == (y) || (*objhash.compare)((x),(y)) == 0)
+#define PTR_EQUAL(ptr, hash_val, key_) \
+ ((ptr)->hash == (hash_val) && EQUAL((key_), (ptr)->key))
+
+#define RESERVED_HASH_VAL (~(st_hash_t) 0)
+#define RESERVED_HASH_SUBSTITUTION_VAL ((st_hash_t) 0)
+
+#define SET_KEY(entry, _key) (entry)->key = (_key)
+#define SET_HASH(entry, _hash) (entry)->hash = (_hash)
+#define SET_RECORD(entry, _value) (entry)->record = (_value)
+
+typedef st_data_t st_hash_t;
+extern const st_hash_t st_reserved_hash_val;
+extern const st_hash_t st_reserved_hash_substitution_val;
+
+static inline st_hash_t
+do_hash(st_data_t key)
+{
+ st_hash_t hash = (st_hash_t)(*objhash.hash)(key);
+ return (RESERVED_HASH_VAL == hash) ? RESERVED_HASH_SUBSTITUTION_VAL : hash;
+}
+
+static inline void
+set_entry(ar_table_entry *entry, st_data_t key, st_data_t val, st_hash_t hash)
+{
+ SET_HASH(entry, hash);
+ SET_KEY(entry, key);
+ SET_RECORD(entry, val);
+}
+
+static inline void
+clear_entry(ar_table_entry* entry)
+{
+ SET_KEY(entry, Qundef);
+ SET_RECORD(entry, Qundef);
+ SET_HASH(entry, RESERVED_HASH_VAL);
+}
+
+static inline int
+empty_entry(ar_table_entry *entry)
+{
+ return entry->hash == RESERVED_HASH_VAL;
+}
+
+#define RHASH_AR_TABLE_SIZE(h) (HASH_ASSERT(RHASH_AR_TABLE_P(h)), \
+ RHASH_AR_TABLE_SIZE_RAW(h))
+
+#define RHASH_AR_TABLE_BOUND_RAW(h) \
+ ((unsigned int)((RBASIC(h)->flags >> RHASH_AR_TABLE_BOUND_SHIFT) & \
+ (RHASH_AR_TABLE_BOUND_MASK >> RHASH_AR_TABLE_BOUND_SHIFT)))
+
+#define RHASH_AR_TABLE_BOUND(h) (HASH_ASSERT(RHASH_AR_TABLE_P(h)), \
+ RHASH_AR_TABLE_BOUND_RAW(h))
+
+#define RHASH_ST_TABLE_SET(h, s) rb_hash_st_table_set(h, s)
+#define RHASH_TYPE(hash) (RHASH_AR_TABLE_P(hash) ? &objhash : RHASH_ST_TABLE(hash)->type)
+#define RHASH_AR_TABLE_REF(hash, n) (&RHASH_AR_TABLE(hash)->entries[n])
+
+#if HASH_DEBUG
+#define hash_verify(hash) hash_verify_(hash, __FILE__, __LINE__)
+#define HASH_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(1, expr, #expr)
+
+void
+rb_hash_dump(VALUE hash)
+{
+ rb_obj_info_dump(hash);
+
+ if (RHASH_AR_TABLE_P(hash)) {
+ unsigned i, n = 0, bound = RHASH_AR_TABLE_BOUND(hash);
+
+ fprintf(stderr, " size:%u bound:%u\n",
+ RHASH_AR_TABLE_SIZE(hash), RHASH_AR_TABLE_BOUND(hash));
+
+ for (i=0; i<bound; i++) {
+ ar_table_entry *cur_entry = RHASH_AR_TABLE_REF(hash, i);
+ st_data_t k, v;
+
+ if (!empty_entry(cur_entry)) {
+ char b1[0x100], b2[0x100];
+ /* h = cur_entry->hash; */
+ k = cur_entry->key;
+ v = cur_entry->record;
+ fprintf(stderr, " %d key:%s val:%s\n", i,
+ rb_raw_obj_info(b1, 0x100, k),
+ rb_raw_obj_info(b2, 0x100, v));
+ n++;
+ }
+ else {
+ fprintf(stderr, " %d empty\n", i);
+ }
+ }
+ }
+}
+
+static VALUE
+hash_verify_(VALUE hash, const char *file, int line)
+{
+ HASH_ASSERT(RB_TYPE_P(hash, T_HASH));
+
+ if (RHASH_AR_TABLE_P(hash)) {
+ unsigned i, n = 0, bound = RHASH_AR_TABLE_BOUND(hash);
+
+ for (i=0; i<bound; i++) {
+ ar_table_entry *cur_entry = RHASH_AR_TABLE_REF(hash, i);
+ st_data_t h, k, v;
+ if (!empty_entry(cur_entry)) {
+ h = cur_entry->hash;
+ k = cur_entry->key;
+ v = cur_entry->record;
+ HASH_ASSERT(h != RESERVED_HASH_VAL);
+ HASH_ASSERT(k != Qundef);
+ HASH_ASSERT(v != Qundef);
+ n++;
+ }
+ }
+ if (n != RHASH_AR_TABLE_SIZE(hash)) {
+ rb_bug("n:%u, RHASH_AR_TABLE_SIZE:%u", n, RHASH_AR_TABLE_SIZE(hash));
+ }
+ }
+ else {
+ HASH_ASSERT(RHASH_ST_TABLE(hash) != NULL);
+ HASH_ASSERT(RHASH_AR_TABLE_SIZE_RAW(hash) == 0);
+ HASH_ASSERT(RHASH_AR_TABLE_BOUND_RAW(hash) == 0);
+ }
+
+ if (RHASH_TRANSIENT_P(hash)) {
+ volatile st_data_t MAYBE_UNUSED(key) = RHASH_AR_TABLE_REF(hash, 0)->key; /* read */
+ HASH_ASSERT(RHASH_AR_TABLE(hash) != NULL);
+ HASH_ASSERT(rb_transient_heap_managed_ptr_p(RHASH_AR_TABLE(hash)));
+ }
+ return hash;
+}
+
+#else
+#define hash_verify(h) ((void)0)
+#define HASH_ASSERT(e) ((void)0)
+#endif
+
+static inline int
+RHASH_TABLE_NULL_P(VALUE hash)
+{
+ if (RHASH(hash)->as.ar == NULL) {
+ HASH_ASSERT(RHASH_AR_TABLE_P(hash));
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+static inline int
+RHASH_TABLE_EMPTY_P(VALUE hash)
+{
+ return RHASH_SIZE(hash) == 0;
+}
+
+MJIT_FUNC_EXPORTED int
+rb_hash_ar_table_p(VALUE hash)
+{
+ if (FL_TEST_RAW((hash), RHASH_ST_TABLE_FLAG)) {
+ HASH_ASSERT(RHASH(hash)->as.st != NULL);
+ return FALSE;
+ }
+ else {
+ return TRUE;
+ }
+}
+
+ar_table *
+rb_hash_ar_table(VALUE hash)
+{
+ HASH_ASSERT(RHASH_AR_TABLE_P(hash));
+ return RHASH(hash)->as.ar;
+}
+
+MJIT_FUNC_EXPORTED st_table *
+rb_hash_st_table(VALUE hash)
+{
+ HASH_ASSERT(!RHASH_AR_TABLE_P(hash));
+ return RHASH(hash)->as.st;
+}
+
+void
+rb_hash_st_table_set(VALUE hash, st_table *st)
+{
+ HASH_ASSERT(st != NULL);
+ FL_SET_RAW((hash), RHASH_ST_TABLE_FLAG);
+ RHASH(hash)->as.st = st;
+}
+
+static void
+hash_ar_table_set(VALUE hash, ar_table *ar)
+{
+ HASH_ASSERT(RHASH_AR_TABLE_P(hash));
+ HASH_ASSERT((RHASH_TRANSIENT_P(hash) && ar == NULL) ? FALSE : TRUE);
+ RHASH(hash)->as.ar = ar;
+ hash_verify(hash);
+}
+
+#define RHASH_AR_TABLE_SET(h, a) hash_ar_table_set(h, a)
+
+#define RHASH_SET_ST_FLAG(h) FL_SET_RAW(h, RHASH_ST_TABLE_FLAG)
+#define RHASH_UNSET_ST_FLAG(h) FL_UNSET_RAW(h, RHASH_ST_TABLE_FLAG)
+
+#define RHASH_AR_TABLE_BOUND_SET(h, n) do { \
+ st_index_t tmp_n = (n); \
+ HASH_ASSERT(RHASH_AR_TABLE_P(h)); \
+ HASH_ASSERT(tmp_n <= RHASH_AR_TABLE_MAX_BOUND); \
+ RBASIC(h)->flags &= ~RHASH_AR_TABLE_BOUND_MASK; \
+ RBASIC(h)->flags |= (tmp_n) << RHASH_AR_TABLE_BOUND_SHIFT; \
+} while (0)
+
+#define RHASH_AR_TABLE_SIZE_SET(h, n) do { \
+ st_index_t tmp_n = n; \
+ HASH_ASSERT(RHASH_AR_TABLE_P(h)); \
+ RBASIC(h)->flags &= ~RHASH_AR_TABLE_SIZE_MASK; \
+ RBASIC(h)->flags |= (tmp_n) << RHASH_AR_TABLE_SIZE_SHIFT; \
+} while (0)
+
+#define HASH_AR_TABLE_SIZE_ADD(h, n) do { \
+ HASH_ASSERT(RHASH_AR_TABLE_P(h)); \
+ RHASH_AR_TABLE_SIZE_SET((h), RHASH_AR_TABLE_SIZE(h)+(n)); \
+ hash_verify(h); \
+} while (0)
+
+#define RHASH_AR_TABLE_SIZE_INC(h) HASH_AR_TABLE_SIZE_ADD(h, 1)
+#define RHASH_AR_TABLE_SIZE_DEC(h) do { \
+ HASH_ASSERT(RHASH_AR_TABLE_P(h)); \
+ RHASH_AR_TABLE_SIZE_SET((h), RHASH_AR_TABLE_SIZE(h) - 1); \
+ hash_verify(h); \
+} while (0)
+
+#define RHASH_AR_TABLE_CLEAR(h) do { \
+ RBASIC(h)->flags &= ~RHASH_AR_TABLE_SIZE_MASK; \
+ RBASIC(h)->flags &= ~RHASH_AR_TABLE_BOUND_MASK; \
+ RHASH_AR_TABLE_SET(hash, NULL); \
+} while (0)
+
+
+static ar_table*
+ar_alloc_table(VALUE hash)
+{
+ ar_table *tab = (ar_table*)rb_transient_heap_alloc(hash, sizeof(ar_table));
+
+ if (tab != NULL) {
+ RHASH_SET_TRANSIENT_FLAG(hash);
+ }
+ else {
+ RHASH_UNSET_TRANSIENT_FLAG(hash);
+ tab = (ar_table*)ruby_xmalloc(sizeof(ar_table));
+ }
+
+ RHASH_AR_TABLE_SIZE_SET(hash, 0);
+ RHASH_AR_TABLE_BOUND_SET(hash, 0);
+ RHASH_AR_TABLE_SET(hash, tab);
+
+ return tab;
+}
+
+static unsigned
+find_entry(VALUE hash, st_hash_t hash_value, st_data_t key)
+{
+ unsigned i, bound = RHASH_AR_TABLE_BOUND(hash);
+
+ /* if table is NULL, then bound also should be 0 */
+
+ for (i = 0; i < bound; i++) {
+ if (PTR_EQUAL(RHASH_AR_TABLE_REF(hash, i), hash_value, key)) {
+ return i;
+ }
+ }
+ return RHASH_AR_TABLE_MAX_BOUND;
+}
+
+static inline void
+ar_free_and_clear_table(VALUE hash)
+{
+ ar_table *tab = RHASH_AR_TABLE(hash);
+
+ if (tab) {
+ if (RHASH_TRANSIENT_P(hash)) {
+ RHASH_UNSET_TRANSIENT_FLAG(hash);
+ }
+ else {
+ ruby_xfree(RHASH_AR_TABLE(hash));
+ }
+ RHASH_AR_TABLE_CLEAR(hash);
+ }
+ HASH_ASSERT(RHASH_AR_TABLE_SIZE(hash) == 0);
+ HASH_ASSERT(RHASH_AR_TABLE_BOUND(hash) == 0);
+ HASH_ASSERT(RHASH_TRANSIENT_P(hash) == 0);
+}
+
+void st_add_direct_with_hash(st_table *tab, st_data_t key, st_data_t value, st_hash_t hash); /* st.c */
+
+static void
+ar_try_convert_table(VALUE hash)
+{
+ st_table *new_tab;
+ ar_table_entry *entry;
+ unsigned size;
+ st_index_t i;
+
+ if (!RHASH_AR_TABLE_P(hash)) return;
+
+ size = RHASH_AR_TABLE_SIZE(hash);
+
+ if (size < RHASH_AR_TABLE_MAX_SIZE) {
+ return;
+ }
+
+ new_tab = st_init_table_with_size(&objhash, size * 2);
+
+ for (i = 0; i < RHASH_AR_TABLE_MAX_BOUND; i++) {
+ entry = RHASH_AR_TABLE_REF(hash, i);
+ HASH_ASSERT(entry->hash != RESERVED_HASH_VAL);
+
+ st_add_direct_with_hash(new_tab, entry->key, entry->record, entry->hash);
+ }
+ ar_free_and_clear_table(hash);
+ RHASH_ST_TABLE_SET(hash, new_tab);
+ return;
+}
+
+static st_table *
+ar_force_convert_table(VALUE hash, const char *file, int line)
+{
+ st_table *new_tab;
+
+ if (RHASH_ST_TABLE_P(hash)) {
+ return RHASH_ST_TABLE(hash);
+ }
+
+ if (RHASH_AR_TABLE(hash)) {
+ ar_table_entry *entry;
+ unsigned i, bound = RHASH_AR_TABLE_BOUND(hash);
+
+#if 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
+
+ new_tab = st_init_table_with_size(&objhash, RHASH_AR_TABLE_SIZE(hash));
+
+ for (i = 0; i < bound; i++) {
+ entry = RHASH_AR_TABLE_REF(hash, i);
+ if (empty_entry(entry)) continue;
+
+ st_add_direct_with_hash(new_tab, entry->key, entry->record, entry->hash);
+ }
+ ar_free_and_clear_table(hash);
+ }
+ else {
+ new_tab = st_init_table(&objhash);
+ }
+ RHASH_ST_TABLE_SET(hash, new_tab);
+
+ return new_tab;
+}
+
+static ar_table *
+hash_ar_table(VALUE hash)
+{
+ if (RHASH_TABLE_NULL_P(hash)) {
+ ar_alloc_table(hash);
+ }
+ return RHASH_AR_TABLE(hash);
+}
+
+static int
+ar_compact_table(VALUE hash)
+{
+ const unsigned bound = RHASH_AR_TABLE_BOUND(hash);
+ const unsigned size = RHASH_AR_TABLE_SIZE(hash);
+
+ if (size == bound) {
+ return size;
+ }
+ else {
+ unsigned i, j=0;
+ ar_table_entry *entries = RHASH_AR_TABLE_REF(hash, 0);
+
+ for (i=0; i<bound; i++) {
+ if (empty_entry(&entries[i])) {
+ if (j <= i) j = i+1;
+ for (; j<bound; j++) {
+ if (!empty_entry(&entries[j])) {
+ entries[i] = entries[j];
+ clear_entry(&entries[j]);
+ j++;
+ goto found;
+ }
+ }
+ /* non-empty is not found */
+ goto done;
+ found:;
+ }
+ }
+ done:
+ HASH_ASSERT(i<=bound);
+
+ RHASH_AR_TABLE_BOUND_SET(hash, size);
+ hash_verify(hash);
+ return size;
+ }
+}
+
+static int
+ar_add_direct_with_hash(VALUE hash, st_data_t key, st_data_t val, st_hash_t hash_value)
+{
+ unsigned bin = RHASH_AR_TABLE_BOUND(hash);
+ ar_table *tab = RHASH_AR_TABLE(hash);
+ ar_table_entry *entry;
+
+ if (RHASH_AR_TABLE_SIZE(hash) >= RHASH_AR_TABLE_MAX_SIZE) {
+ return 1;
+ }
+ else {
+ if (UNLIKELY(bin >= RHASH_AR_TABLE_MAX_BOUND)) {
+ bin = ar_compact_table(hash);
+ hash_ar_table(hash);
+ }
+ HASH_ASSERT(bin < RHASH_AR_TABLE_MAX_BOUND);
+
+ entry = &tab->entries[bin];
+ set_entry(entry, key, val, hash_value);
+ RHASH_AR_TABLE_BOUND_SET(hash, bin+1);
+ RHASH_AR_TABLE_SIZE_INC(hash);
+ return 0;
+ }
+}
+
+static int
+ar_foreach(VALUE hash, int (*func)(ANYARGS), st_data_t arg)
+{
+ if (RHASH_AR_TABLE_SIZE(hash) > 0) {
+ unsigned i, bound = RHASH_AR_TABLE_BOUND(hash);
+
+ for (i = 0; i < bound; i++) {
+ enum st_retval retval;
+ ar_table_entry *cur_entry = RHASH_AR_TABLE_REF(hash, i);
+ if (empty_entry(cur_entry)) continue;
+ retval = (*func)(cur_entry->key, cur_entry->record, arg, 0);
+ /* cur_entry is not valid after that */
+
+ switch (retval) {
+ case ST_CONTINUE:
+ break;
+ case ST_CHECK:
+ case ST_STOP:
+ return 0;
+ case ST_DELETE:
+ clear_entry(RHASH_AR_TABLE_REF(hash, i));
+ RHASH_AR_TABLE_SIZE_DEC(hash);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+ar_foreach_check(VALUE hash, int (*func)(ANYARGS), st_data_t arg,
+ st_data_t never)
+{
+ if (RHASH_AR_TABLE_SIZE(hash) > 0) {
+ unsigned i, ret = 0, bound = RHASH_AR_TABLE_BOUND(hash);
+ enum st_retval retval;
+ ar_table_entry *cur_entry;
+ st_data_t key;
+ st_hash_t hash_value;
+
+ for (i = 0; i < bound; i++) {
+ cur_entry = RHASH_AR_TABLE_REF(hash, i);
+ if (empty_entry(cur_entry))
+ continue;
+ key = cur_entry->key;
+ hash_value = cur_entry->hash;
+
+ retval = (*func)(key, cur_entry->record, arg, 0);
+ hash_verify(hash);
+
+ cur_entry = RHASH_AR_TABLE_REF(hash, i);
+
+ switch (retval) {
+ case ST_CHECK: {
+ if (cur_entry->key == never && cur_entry->hash == RESERVED_HASH_VAL)
+ break;
+ ret = find_entry(hash, hash_value, key);
+ if (ret == RHASH_AR_TABLE_MAX_BOUND) {
+ retval = (*func)(0, 0, arg, 1);
+ return 2;
+ }
+ }
+ case ST_CONTINUE:
+ break;
+ case ST_STOP:
+ return 0;
+ case ST_DELETE: {
+ if (!empty_entry(cur_entry)) {
+ clear_entry(cur_entry);
+ RHASH_AR_TABLE_SIZE_DEC(hash);
+ }
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+ar_update(VALUE hash, st_data_t key,
+ st_update_callback_func *func, st_data_t arg)
+{
+ int retval, existing;
+ unsigned bin = RHASH_AR_TABLE_MAX_BOUND;
+ st_data_t value = 0, old_key;
+ st_hash_t hash_value = do_hash(key);
+
+ if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) {
+ /* `#hash` changes ar_table -> st_table */
+ return -1;
+ }
+
+ if (RHASH_AR_TABLE_SIZE(hash) > 0) {
+ bin = find_entry(hash, hash_value, key);
+ existing = (bin != RHASH_AR_TABLE_MAX_BOUND) ? TRUE : FALSE;
+ }
+ else {
+ hash_ar_table(hash); /* allocate ltbl if needed */
+ existing = FALSE;
+ }
+
+ if (existing) {
+ ar_table_entry *entry = RHASH_AR_TABLE_REF(hash, bin);
+ key = entry->key;
+ value = entry->record;
+ }
+ old_key = key;
+ retval = (*func)(&key, &value, arg, existing);
+
+ switch (retval) {
+ case ST_CONTINUE:
+ if (!existing) {
+ if (ar_add_direct_with_hash(hash, key, value, hash_value)) {
+ return -1;
+ }
+ }
+ else {
+ ar_table_entry *entry = RHASH_AR_TABLE_REF(hash, bin);
+ if (old_key != key) {
+ entry->key = key;
+ }
+ entry->record = value;
+ }
+ break;
+ case ST_DELETE:
+ if (existing) {
+ clear_entry(RHASH_AR_TABLE_REF(hash, bin));
+ RHASH_AR_TABLE_SIZE_DEC(hash);
+ }
+ break;
+ }
+ return existing;
+}
+
+static int
+ar_insert(VALUE hash, st_data_t key, st_data_t value)
+{
+ unsigned bin = RHASH_AR_TABLE_BOUND(hash);
+ st_hash_t hash_value = do_hash(key);
+
+ if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) {
+ /* `#hash` changes ar_table -> st_table */
+ return -1;
+ }
+
+ hash_ar_table(hash); /* prepare ltbl */
+
+ bin = find_entry(hash, hash_value, key);
+ if (bin == RHASH_AR_TABLE_MAX_BOUND) {
+ if (RHASH_AR_TABLE_SIZE(hash) >= RHASH_AR_TABLE_MAX_SIZE) {
+ return -1;
+ }
+ else if (bin >= RHASH_AR_TABLE_MAX_BOUND) {
+ bin = ar_compact_table(hash);
+ hash_ar_table(hash);
+ }
+ HASH_ASSERT(bin < RHASH_AR_TABLE_MAX_BOUND);
+
+ set_entry(RHASH_AR_TABLE_REF(hash, bin), key, value, hash_value);
+ RHASH_AR_TABLE_BOUND_SET(hash, bin+1);
+ RHASH_AR_TABLE_SIZE_INC(hash);
+ return 0;
+ }
+ else {
+ RHASH_AR_TABLE_REF(hash, bin)->record = value;
+ return 1;
+ }
+}
+
+static int
+ar_lookup(VALUE hash, st_data_t key, st_data_t *value)
+{
+ st_hash_t hash_value = do_hash(key);
+ unsigned bin;
+ if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) {
+ /* `#hash` changes ar_table -> st_table */
+ return st_lookup(RHASH_ST_TABLE(hash), key, value);
+ }
+ bin = find_entry(hash, hash_value, key);
+
+ if (bin == RHASH_AR_TABLE_MAX_BOUND) {
+ return 0;
+ }
+ else {
+ HASH_ASSERT(bin < RHASH_AR_TABLE_MAX_BOUND);
+ if (value != NULL) {
+ *value = RHASH_AR_TABLE_REF(hash, bin)->record;
+ }
+ return 1;
+ }
+}
+
+static int
+ar_delete(VALUE hash, st_data_t *key, st_data_t *value)
+{
+ unsigned bin;
+ st_hash_t hash_value = do_hash(*key);
+
+ if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) {
+ /* `#hash` changes ar_table -> st_table */
+ return st_delete(RHASH_ST_TABLE(hash), key, value);
+ }
+
+ bin = find_entry(hash, hash_value, *key);
+
+ if (bin == RHASH_AR_TABLE_MAX_BOUND) {
+ if (value != 0) *value = 0;
+ return 0;
+ }
+ else {
+ ar_table_entry *entry = RHASH_AR_TABLE_REF(hash, bin);
+ if (value != 0) *value = entry->record;
+ clear_entry(entry);
+ RHASH_AR_TABLE_SIZE_DEC(hash);
+ return 1;
+ }
+}
+
+static int
+ar_shift(VALUE hash, st_data_t *key, st_data_t *value)
+{
+ if (RHASH_AR_TABLE_SIZE(hash) > 0) {
+ unsigned i, bound = RHASH_AR_TABLE_BOUND(hash);
+ ar_table_entry *entry, *entries = RHASH_AR_TABLE(hash)->entries;
+
+ for (i = 0; i < bound; i++) {
+ entry = &entries[i];
+ if (!empty_entry(entry)) {
+ if (value != 0) *value = entry->record;
+ *key = entry->key;
+ clear_entry(entry);
+ RHASH_AR_TABLE_SIZE_DEC(hash);
+ return 1;
+ }
+ }
+ }
+ if (value != 0) *value = 0;
+ return 0;
+}
+
+static long
+ar_keys(VALUE hash, st_data_t *keys, st_index_t size)
+{
+ unsigned i, bound = RHASH_AR_TABLE_BOUND(hash);
+ st_data_t *keys_start = keys, *keys_end = keys + size;
+
+ for (i = 0; i < bound; i++) {
+ if (keys == keys_end) {
+ break;
+ }
+ else {
+ ar_table_entry *cur_entry = RHASH_AR_TABLE_REF(hash, i);
+ if (!empty_entry(cur_entry))
+ *keys++ = cur_entry->key;
+ }
+ }
+
+ return keys - keys_start;
+}
+
+static long
+ar_values(VALUE hash, st_data_t *values, st_index_t size)
+{
+ unsigned i, bound = RHASH_AR_TABLE_BOUND(hash);
+ st_data_t *values_start = values, *values_end = values + size;
+
+ for (i = 0; i < bound; i++) {
+ if (values == values_end) {
+ break;
+ }
+ else {
+ ar_table_entry *cur_entry = RHASH_AR_TABLE_REF(hash, i);
+ if (!empty_entry(cur_entry))
+ *values++ = cur_entry->record;
+ }
+ }
+
+ return values - values_start;
+}
+
+static ar_table*
+ar_copy(VALUE hash1, VALUE hash2)
+{
+ ar_table *old_tab = RHASH_AR_TABLE(hash2);
+
+ if (old_tab != NULL) {
+ ar_table *new_tab = RHASH_AR_TABLE(hash1);
+ if (new_tab == NULL) {
+ new_tab = (ar_table*) rb_transient_heap_alloc(hash1, sizeof(ar_table));
+ if (new_tab != NULL) {
+ RHASH_SET_TRANSIENT_FLAG(hash1);
+ }
+ else {
+ RHASH_UNSET_TRANSIENT_FLAG(hash1);
+ new_tab = (ar_table*)ruby_xmalloc(sizeof(ar_table));
+ }
+ }
+ *new_tab = *old_tab;
+ RHASH_AR_TABLE_BOUND_SET(hash1, RHASH_AR_TABLE_BOUND(hash2));
+ RHASH_AR_TABLE_SIZE_SET(hash1, RHASH_AR_TABLE_SIZE(hash2));
+ RHASH_AR_TABLE_SET(hash1, new_tab);
+
+ rb_gc_writebarrier_remember(hash1);
+ return new_tab;
+ }
+ else {
+ RHASH_AR_TABLE_BOUND_SET(hash1, RHASH_AR_TABLE_BOUND(hash2));
+ RHASH_AR_TABLE_SIZE_SET(hash1, RHASH_AR_TABLE_SIZE(hash2));
+
+ if (RHASH_TRANSIENT_P(hash1)) {
+ RHASH_UNSET_TRANSIENT_FLAG(hash1);
+ }
+ else if (RHASH_AR_TABLE(hash1)) {
+ ruby_xfree(RHASH_AR_TABLE(hash1));
+ }
+
+ RHASH_AR_TABLE_SET(hash1, NULL);
+
+ rb_gc_writebarrier_remember(hash1);
+ return old_tab;
+ }
+}
+
+static void
+ar_clear(VALUE hash)
+{
+ if (RHASH_AR_TABLE(hash) != NULL) {
+ RHASH_AR_TABLE_SIZE_SET(hash, 0);
+ RHASH_AR_TABLE_BOUND_SET(hash, 0);
+ }
+ else {
+ HASH_ASSERT(RHASH_AR_TABLE_SIZE(hash) == 0);
+ HASH_ASSERT(RHASH_AR_TABLE_BOUND(hash) == 0);
+ }
+}
+
+#if USE_TRANSIENT_HEAP
+void
+rb_hash_transient_heap_evacuate(VALUE hash, int promote)
+{
+ if (RHASH_TRANSIENT_P(hash)) {
+ ar_table *new_tab;
+ ar_table *old_tab = RHASH_AR_TABLE(hash);
+
+ if (UNLIKELY(old_tab == NULL)) {
+ rb_gc_force_recycle(hash);
+ return;
+ }
+ HASH_ASSERT(old_tab != NULL);
+ if (promote) {
+ promote:
+ new_tab = ruby_xmalloc(sizeof(ar_table));
+ RHASH_UNSET_TRANSIENT_FLAG(hash);
+ }
+ else {
+ new_tab = rb_transient_heap_alloc(hash, sizeof(ar_table));
+ if (new_tab == NULL) goto promote;
+ }
+ *new_tab = *old_tab;
+ RHASH_AR_TABLE_SET(hash, new_tab);
+ }
+ hash_verify(hash);
+}
+#endif
+
typedef int st_foreach_func(st_data_t, st_data_t, st_data_t);
struct foreach_safe_arg {
@@ -367,6 +1150,27 @@ struct hash_foreach_arg {
};
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;
+ 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;
+ case ST_CONTINUE:
+ break;
+ case ST_STOP:
+ return ST_STOP;
+ }
+ return ST_CHECK;
+}
+
+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;
@@ -374,14 +1178,13 @@ hash_foreach_iter(st_data_t key, st_data_t value, st_data_t argp, int error)
st_table *tbl;
if (error) return ST_STOP;
- tbl = RHASH(arg->hash)->ntbl;
+ tbl = RHASH_ST_TABLE(arg->hash);
status = (*arg->func)((VALUE)key, (VALUE)value, arg->arg);
- if (RHASH(arg->hash)->ntbl != tbl) {
- rb_raise(rb_eRuntimeError, "rehash occurred during iteration");
+ if (RHASH_ST_TABLE(arg->hash) != tbl) {
+ rb_raise(rb_eRuntimeError, "rehash occurred during iteration");
}
switch (status) {
case ST_DELETE:
- FL_SET(arg->hash, HASH_DELETED);
return ST_DELETE;
case ST_CONTINUE:
break;
@@ -401,21 +1204,36 @@ hash_foreach_ensure_rollback(VALUE hash)
static VALUE
hash_foreach_ensure(VALUE hash)
{
- if (--RHASH_ITER_LEV(hash) == 0) {
- if (FL_TEST(hash, HASH_DELETED)) {
- st_cleanup_safe(RHASH(hash)->ntbl, (st_data_t)Qundef);
- FL_UNSET(hash, HASH_DELETED);
- }
- }
+ RHASH_ITER_LEV(hash)--;
return 0;
}
+int
+rb_hash_stlike_foreach(VALUE hash, int (*func)(ANYARGS), st_data_t arg)
+{
+ if (RHASH_AR_TABLE_P(hash)) {
+ return ar_foreach(hash, func, arg);
+ }
+ else {
+ return st_foreach(RHASH_ST_TABLE(hash), func, arg);
+ }
+}
+
static VALUE
hash_foreach_call(VALUE arg)
{
VALUE hash = ((struct hash_foreach_arg *)arg)->hash;
- if (st_foreach_check(RHASH(hash)->ntbl, hash_foreach_iter, (st_data_t)arg, (st_data_t)Qundef)) {
- rb_raise(rb_eRuntimeError, "hash modified during iteration");
+ int ret = 0;
+ if (RHASH_AR_TABLE_P(hash)) {
+ ret = ar_foreach_check(hash, hash_ar_foreach_iter,
+ (st_data_t)arg, (st_data_t)Qundef);
+ }
+ else if (RHASH_ST_TABLE_P(hash)) {
+ ret = st_foreach_check(RHASH_ST_TABLE(hash), hash_foreach_iter,
+ (st_data_t)arg, (st_data_t)Qundef);
+ }
+ if (ret) {
+ rb_raise(rb_eRuntimeError, "ret: %d, hash modified during iteration", ret);
}
return Qnil;
}
@@ -425,13 +1243,14 @@ rb_hash_foreach(VALUE hash, int (*func)(ANYARGS), VALUE farg)
{
struct hash_foreach_arg arg;
- if (!RHASH(hash)->ntbl)
+ if (RHASH_TABLE_EMPTY_P(hash))
return;
RHASH_ITER_LEV(hash)++;
arg.hash = hash;
arg.func = (rb_foreach_func *)func;
arg.arg = farg;
rb_ensure(hash_foreach_call, (VALUE)&arg, hash_foreach_ensure, hash);
+ hash_verify(hash);
}
static VALUE
@@ -465,13 +1284,41 @@ rb_hash_new(void)
return hash_alloc(rb_cHash);
}
+VALUE
+rb_hash_new_compare_by_id(void)
+{
+ VALUE hash = rb_hash_new();
+ RHASH_ST_TABLE_SET(hash, rb_init_identtable());
+ return hash;
+}
+
+MJIT_FUNC_EXPORTED VALUE
+rb_hash_new_with_size(st_index_t size)
+{
+ VALUE ret = rb_hash_new();
+ if (size == 0) {
+ /* do nothing */
+ }
+ else if (size <= RHASH_AR_TABLE_MAX_SIZE) {
+ ar_alloc_table(ret);
+ }
+ else {
+ RHASH_ST_TABLE_SET(ret, st_init_table_with_size(&objhash, size));
+ }
+ return ret;
+}
+
static VALUE
hash_dup(VALUE hash, VALUE klass, VALUE flags)
{
VALUE ret = hash_alloc_flags(klass, flags,
RHASH_IFNONE(hash));
- if (!RHASH_EMPTY_P(hash))
- RHASH(ret)->ntbl = st_copy(RHASH(hash)->ntbl);
+ if (!RHASH_EMPTY_P(hash)) {
+ if (RHASH_AR_TABLE_P(hash))
+ ar_copy(ret, hash);
+ else if (RHASH_ST_TABLE_P(hash))
+ RHASH_ST_TABLE_SET(ret, st_copy(RHASH_ST_TABLE(hash)));
+ }
return ret;
}
@@ -486,39 +1333,43 @@ rb_hash_dup(VALUE hash)
return ret;
}
+MJIT_FUNC_EXPORTED VALUE
+rb_hash_resurrect(VALUE hash)
+{
+ VALUE ret = hash_dup(hash, rb_cHash, 0);
+ return ret;
+}
+
static void
rb_hash_modify_check(VALUE hash)
{
rb_check_frozen(hash);
}
-static struct st_table *
-hash_tbl(VALUE hash)
+MJIT_FUNC_EXPORTED struct st_table *
+#if RHASH_CONVERT_TABLE_DEBUG
+rb_hash_tbl_raw(VALUE hash, const char *file, int line)
{
- if (!RHASH(hash)->ntbl) {
- RHASH(hash)->ntbl = st_init_table(&objhash);
- }
- return RHASH(hash)->ntbl;
+ return ar_force_convert_table(hash, file, line);
}
-
-struct st_table *
-rb_hash_tbl(VALUE hash)
+#else
+rb_hash_tbl_raw(VALUE hash)
{
- OBJ_WB_UNPROTECT(hash);
- return hash_tbl(hash);
+ return ar_force_convert_table(hash, NULL, 0);
}
+#endif
struct st_table *
-rb_hash_tbl_raw(VALUE hash)
+rb_hash_tbl(VALUE hash, const char *file, int line)
{
- return hash_tbl(hash);
+ OBJ_WB_UNPROTECT(hash);
+ return RHASH_TBL_RAW(hash);
}
static void
rb_hash_modify(VALUE hash)
{
rb_hash_modify_check(hash);
- hash_tbl(hash);
}
NORETURN(static void no_new_key(void));
@@ -558,6 +1409,22 @@ struct update_arg {
typedef int (*tbl_update_func)(st_data_t *, st_data_t *, st_data_t, int);
+int
+rb_hash_stlike_update(VALUE hash, st_data_t key, st_update_callback_func func, st_data_t arg)
+{
+ if (RHASH_AR_TABLE_P(hash)) {
+ int result = ar_update(hash, (st_data_t)key, func, arg);
+ if (result == -1) {
+ ar_try_convert_table(hash);
+ }
+ else {
+ return result;
+ }
+ }
+
+ return st_update(RHASH_ST_TABLE(hash), (st_data_t)key, func, arg);
+}
+
static int
tbl_update(VALUE hash, VALUE key, tbl_update_func func, st_data_t optional_arg)
{
@@ -571,7 +1438,7 @@ tbl_update(VALUE hash, VALUE key, tbl_update_func func, st_data_t optional_arg)
arg.new_value = 0;
arg.old_value = Qundef;
- result = st_update(RHASH(hash)->ntbl, (st_data_t)key, func, (st_data_t)&arg);
+ result = rb_hash_stlike_update(hash, key, func, (st_data_t)&arg);
/* write barrier */
if (arg.new_key) RB_OBJ_WRITTEN(hash, arg.old_key, arg.new_key);
@@ -684,15 +1551,17 @@ static VALUE
rb_hash_s_create(int argc, VALUE *argv, VALUE klass)
{
VALUE hash, tmp;
- int i;
if (argc == 1) {
- tmp = rb_hash_s_try_convert(Qnil, argv[0]);
+ tmp = rb_hash_s_try_convert(Qnil, argv[0]);
if (!NIL_P(tmp)) {
hash = hash_alloc(klass);
- if (RHASH(tmp)->ntbl) {
- RHASH(hash)->ntbl = st_copy(RHASH(tmp)->ntbl);
+ if (RHASH_AR_TABLE_P(tmp)) {
+ ar_copy(hash, tmp);
}
+ else {
+ RHASH_ST_TABLE_SET(hash, st_copy(RHASH_ST_TABLE(tmp)));
+ }
return hash;
}
@@ -738,26 +1607,22 @@ rb_hash_s_create(int argc, VALUE *argv, VALUE klass)
}
hash = hash_alloc(klass);
- if (argc > 0) {
- RHASH(hash)->ntbl = st_init_table_with_size(&objhash, argc / 2);
- }
- for (i=0; i<argc; i+=2) {
- rb_hash_aset(hash, argv[i], argv[i + 1]);
- }
-
+ rb_hash_bulk_insert(argc, argv, hash);
+ hash_verify(hash);
return hash;
}
-static VALUE
-to_hash(VALUE hash)
+VALUE
+rb_to_hash_type(VALUE hash)
{
- return rb_convert_type(hash, T_HASH, "Hash", "to_hash");
+ return rb_convert_type_with_id(hash, T_HASH, "Hash", idTo_hash);
}
+#define to_hash rb_to_hash_type
VALUE
rb_check_hash_type(VALUE hash)
{
- return rb_check_convert_type(hash, T_HASH, "Hash", "to_hash");
+ return rb_check_convert_type_with_id(hash, T_HASH, "Hash", idTo_hash);
}
/*
@@ -785,9 +1650,12 @@ struct rehash_arg {
static int
rb_hash_rehash_i(VALUE key, VALUE value, VALUE arg)
{
- st_table *tbl = (st_table *)arg;
-
- st_insert(tbl, (st_data_t)key, (st_data_t)value);
+ if (RHASH_AR_TABLE_P(arg)) {
+ ar_insert(arg, (st_data_t)key, (st_data_t)value);
+ }
+ else {
+ st_insert(RHASH_ST_TABLE(arg), (st_data_t)key, (st_data_t)value);
+ }
return ST_CONTINUE;
}
@@ -821,17 +1689,25 @@ rb_hash_rehash(VALUE hash)
rb_raise(rb_eRuntimeError, "rehash during iteration");
}
rb_hash_modify_check(hash);
- if (!RHASH(hash)->ntbl)
- return hash;
- tmp = hash_alloc(0);
- tbl = st_init_table_with_size(RHASH(hash)->ntbl->type, RHASH(hash)->ntbl->num_entries);
- RHASH(tmp)->ntbl = tbl;
-
- rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tbl);
- st_free_table(RHASH(hash)->ntbl);
- RHASH(hash)->ntbl = tbl;
- RHASH(tmp)->ntbl = 0;
-
+ if (RHASH_AR_TABLE_P(hash)) {
+ tmp = hash_alloc(0);
+ ar_alloc_table(tmp);
+ rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp);
+ ar_free_and_clear_table(hash);
+ ar_copy(hash, tmp);
+ ar_free_and_clear_table(tmp);
+ }
+ else if (RHASH_ST_TABLE_P(hash)) {
+ st_table *old_tab = RHASH_ST_TABLE(hash);
+ tmp = hash_alloc(0);
+ tbl = st_init_table_with_size(old_tab->type, old_tab->num_entries);
+ RHASH_ST_TABLE_SET(tmp, tbl);
+ rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp);
+ st_free_table(old_tab);
+ RHASH_ST_TABLE_SET(hash, tbl);
+ RHASH_ST_CLEAR(tmp);
+ }
+ hash_verify(hash);
return hash;
}
@@ -868,10 +1744,27 @@ rb_hash_aref(VALUE hash, VALUE key)
{
st_data_t val;
- if (!RHASH(hash)->ntbl || !st_lookup(RHASH(hash)->ntbl, key, &val)) {
- return rb_hash_default_value(hash, key);
+ if (RHASH_AR_TABLE_P(hash) && ar_lookup(hash, key, &val)) {
+ return (VALUE)val;
+ }
+ else if (RHASH_ST_TABLE_P(hash) && st_lookup(RHASH_ST_TABLE(hash), key, &val)) {
+ return (VALUE)val;
+ }
+ hash_verify(hash);
+ return rb_hash_default_value(hash, key);
+}
+
+MJIT_FUNC_EXPORTED int
+rb_hash_stlike_lookup(VALUE hash, st_data_t key, st_data_t *pval)
+{
+ hash_verify(hash);
+
+ if (RHASH_AR_TABLE_P(hash)) {
+ return ar_lookup(hash, key, pval);
+ }
+ else {
+ return st_lookup(RHASH_ST_TABLE(hash), key, pval);
}
- return (VALUE)val;
}
VALUE
@@ -879,10 +1772,12 @@ rb_hash_lookup2(VALUE hash, VALUE key, VALUE def)
{
st_data_t val;
- if (!RHASH(hash)->ntbl || !st_lookup(RHASH(hash)->ntbl, key, &val)) {
- return def; /* without Hash#default */
+ if (rb_hash_stlike_lookup(hash, key, &val)) {
+ return (VALUE)val;
+ }
+ else {
+ return def; /* without Hash#default */
}
- return (VALUE)val;
}
VALUE
@@ -898,9 +1793,9 @@ rb_hash_lookup(VALUE hash, VALUE key)
*
* Returns a value from the hash for the given key. If the key can't be
* found, there are several options: With no other arguments, it will
- * raise an <code>KeyError</code> exception; if <i>default</i> is
- * given, then that will be returned; if the optional code block is
- * specified, then that will be run and its result returned.
+ * raise a <code>KeyError</code> exception; if <i>default</i> is given,
+ * then that will be returned; if the optional code block is specified,
+ * then that will be run and its result returned.
*
* h = { "a" => 100, "b" => 200 }
* h.fetch("a") #=> 100
@@ -934,19 +1829,23 @@ rb_hash_fetch_m(int argc, VALUE *argv, VALUE hash)
if (block_given && argc == 2) {
rb_warn("block supersedes default value argument");
}
- if (!RHASH(hash)->ntbl || !st_lookup(RHASH(hash)->ntbl, key, &val)) {
- if (block_given) return rb_yield(key);
- if (argc == 1) {
- VALUE desc = rb_protect(rb_inspect, key, 0);
- if (NIL_P(desc)) {
- desc = rb_any_to_s(key);
- }
- desc = rb_str_ellipsize(desc, 65);
- rb_raise(rb_eKeyError, "key not found: %"PRIsVALUE, desc);
+ if (RHASH_AR_TABLE_P(hash) && ar_lookup(hash, key, &val)) {
+ return (VALUE)val;
+ }
+ else if (RHASH_ST_TABLE_P(hash) && st_lookup(RHASH_ST_TABLE(hash), key, &val)) {
+ return (VALUE)val;
+ }
+ if (block_given) return rb_yield(key);
+ if (argc == 1) {
+ VALUE desc = rb_protect(rb_inspect, key, 0);
+ if (NIL_P(desc)) {
+ desc = rb_any_to_s(key);
}
- return argv[1];
+ desc = rb_str_ellipsize(desc, 65);
+ rb_key_err_raise(rb_sprintf("key not found: %"PRIsVALUE, desc), hash, key);
}
- return (VALUE)val;
+ hash_verify(hash);
+ return argv[1];
}
VALUE
@@ -1067,7 +1966,7 @@ rb_hash_set_default_proc(VALUE hash, VALUE proc)
SET_DEFAULT(hash, proc);
return proc;
}
- b = rb_check_convert_type(proc, T_DATA, "Proc", "to_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)",
@@ -1125,6 +2024,17 @@ rb_hash_index(VALUE hash, VALUE value)
return rb_hash_key(hash, value);
}
+int
+rb_hash_stlike_delete(VALUE hash, st_data_t *pkey, st_data_t *pval)
+{
+ if (RHASH_AR_TABLE_P(hash)) {
+ return ar_delete(hash, pkey, pval);
+ }
+ else {
+ return st_delete(RHASH_ST_TABLE(hash), pkey, pval);
+ }
+}
+
/*
* delete a specified entry a given key.
* if there is the corresponding entry, return a value of the entry.
@@ -1135,19 +2045,11 @@ rb_hash_delete_entry(VALUE hash, VALUE key)
{
st_data_t ktmp = (st_data_t)key, val;
- if (!RHASH(hash)->ntbl) {
- return Qundef;
- }
- else if (RHASH_ITER_LEV(hash) > 0 &&
- (st_delete_safe(RHASH(hash)->ntbl, &ktmp, &val, (st_data_t)Qundef))) {
- FL_SET(hash, HASH_DELETED);
- return (VALUE)val;
- }
- else if (st_delete(RHASH(hash)->ntbl, &ktmp, &val)) {
- return (VALUE)val;
+ if (rb_hash_stlike_delete(hash, &ktmp, &val)) {
+ return (VALUE)val;
}
else {
- return Qundef;
+ return Qundef;
}
}
@@ -1242,14 +2144,29 @@ rb_hash_shift(VALUE hash)
struct shift_var var;
rb_hash_modify_check(hash);
- if (RHASH(hash)->ntbl) {
+ if (RHASH_AR_TABLE_P(hash)) {
var.key = Qundef;
if (RHASH_ITER_LEV(hash) == 0) {
- if (st_shift(RHASH(hash)->ntbl, &var.key, &var.val)) {
+ if (ar_shift(hash, &var.key, &var.val)) {
return rb_assoc_new(var.key, var.val);
}
}
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);
+ }
+ }
+ }
+ if (RHASH_ST_TABLE_P(hash)) {
+ var.key = Qundef;
+ if (RHASH_ITER_LEV(hash) == 0) {
+ if (st_shift(RHASH_ST_TABLE(hash), &var.key, &var.val)) {
+ return rb_assoc_new(var.key, var.val);
+ }
+ }
+ else {
rb_hash_foreach(hash, shift_i_safe, (VALUE)&var);
if (var.key != Qundef) {
rb_hash_delete_entry(hash, var.key);
@@ -1295,8 +2212,9 @@ rb_hash_delete_if(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
rb_hash_modify_check(hash);
- if (RHASH(hash)->ntbl)
- rb_hash_foreach(hash, delete_if_i, hash);
+ if (!RHASH_TABLE_EMPTY_P(hash)) {
+ rb_hash_foreach(hash, delete_if_i, hash);
+ }
return hash;
}
@@ -1319,7 +2237,7 @@ rb_hash_reject_bang(VALUE hash)
n = RHASH_SIZE(hash);
if (!n) return Qnil;
rb_hash_foreach(hash, delete_if_i, hash);
- if (n == RHASH(hash)->ntbl->num_entries) return Qnil;
+ if (n == RHASH_SIZE(hash)) return Qnil;
return hash;
}
@@ -1366,6 +2284,38 @@ rb_hash_reject(VALUE hash)
}
/*
+ * call-seq:
+ * hsh.slice(*keys) -> a_hash
+ *
+ * Returns a hash containing only the given keys and their values.
+ *
+ * h = { a: 100, b: 200, c: 300 }
+ * h.slice(:a) #=> {:a=>100}
+ * h.slice(:b, :c, :d) #=> {:b=>200, :c=>300}
+ */
+
+static VALUE
+rb_hash_slice(int argc, VALUE *argv, VALUE hash)
+{
+ int i;
+ VALUE key, value, result;
+
+ if (argc == 0 || RHASH_EMPTY_P(hash)) {
+ return rb_hash_new();
+ }
+ result = rb_hash_new_with_size(argc);
+
+ 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);
+ }
+
+ return result;
+}
+
+/*
* call-seq:
* hsh.values_at(key, ...) -> array
*
@@ -1429,6 +2379,8 @@ select_i(VALUE key, VALUE value, VALUE result)
* call-seq:
* hsh.select {|key, value| block} -> a_hash
* hsh.select -> an_enumerator
+ * hsh.filter {|key, value| block} -> a_hash
+ * hsh.filter -> an_enumerator
*
* Returns a new hash consisting of entries for which the block returns true.
*
@@ -1437,6 +2389,8 @@ select_i(VALUE key, VALUE value, VALUE result)
* h = { "a" => 100, "b" => 200, "c" => 300 }
* h.select {|k,v| k > "a"} #=> {"b" => 200, "c" => 300}
* h.select {|k,v| v < 200} #=> {"a" => 100}
+ *
+ * Hash#filter is an alias for Hash#select.
*/
VALUE
@@ -1465,9 +2419,13 @@ keep_if_i(VALUE key, VALUE value, VALUE hash)
* call-seq:
* hsh.select! {| key, value | block } -> hsh or nil
* hsh.select! -> an_enumerator
+ * hsh.filter! {| key, value | block } -> hsh or nil
+ * hsh.filter! -> an_enumerator
*
- * Equivalent to <code>Hash#keep_if</code>, but returns
- * <code>nil</code> if no changes were made.
+ * Equivalent to Hash#keep_if, but returns
+ * +nil+ if no changes were made.
+ *
+ * Hash#filter! is an alias for Hash#select!.
*/
VALUE
@@ -1477,11 +2435,10 @@ rb_hash_select_bang(VALUE hash)
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
rb_hash_modify_check(hash);
- if (!RHASH(hash)->ntbl)
- return Qnil;
- n = RHASH(hash)->ntbl->num_entries;
+ n = RHASH_SIZE(hash);
+ if (!n) return Qnil;
rb_hash_foreach(hash, keep_if_i, hash);
- if (n == RHASH(hash)->ntbl->num_entries) return Qnil;
+ if (n == RHASH_SIZE(hash)) return Qnil;
return hash;
}
@@ -1491,10 +2448,11 @@ rb_hash_select_bang(VALUE hash)
* hsh.keep_if -> an_enumerator
*
* Deletes every key-value pair from <i>hsh</i> for which <i>block</i>
- * evaluates to false.
+ * evaluates to +false+.
*
* If no block is given, an enumerator is returned instead.
*
+ * See also Hash#select!.
*/
VALUE
@@ -1502,8 +2460,9 @@ rb_hash_keep_if(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
rb_hash_modify_check(hash);
- if (RHASH(hash)->ntbl)
- rb_hash_foreach(hash, keep_if_i, hash);
+ if (!RHASH_TABLE_EMPTY_P(hash)) {
+ rb_hash_foreach(hash, keep_if_i, hash);
+ }
return hash;
}
@@ -1528,13 +2487,15 @@ VALUE
rb_hash_clear(VALUE hash)
{
rb_hash_modify_check(hash);
- if (!RHASH(hash)->ntbl)
- return hash;
- if (RHASH(hash)->ntbl->num_entries > 0) {
- if (RHASH_ITER_LEV(hash) > 0)
- rb_hash_foreach(hash, clear_i, 0);
- else
- st_clear(RHASH(hash)->ntbl);
+
+ if (RHASH_ITER_LEV(hash) > 0) {
+ rb_hash_foreach(hash, clear_i, 0);
+ }
+ else if (RHASH_AR_TABLE_P(hash)) {
+ ar_clear(hash);
+ }
+ else {
+ st_clear(RHASH_ST_TABLE(hash));
}
return hash;
@@ -1555,11 +2516,22 @@ hash_aset(st_data_t *key, st_data_t *val, struct update_arg *arg, int existing)
return ST_CONTINUE;
}
+VALUE
+rb_hash_key_str(VALUE key)
+{
+ if (!RB_FL_ANY_RAW(key, FL_TAINT|FL_EXIVAR) && RBASIC_CLASS(key) == rb_cString) {
+ return rb_fstring(key);
+ }
+ else {
+ return rb_str_new_frozen(key);
+ }
+}
+
static int
hash_aset_str(st_data_t *key, st_data_t *val, struct update_arg *arg, int existing)
{
- if (!existing) {
- *key = rb_str_new_frozen(*key);
+ if (!existing && !RB_OBJ_FROZEN(*key)) {
+ *key = rb_hash_key_str(*key);
}
return hash_aset(key, val, arg, existing);
}
@@ -1598,14 +2570,15 @@ VALUE
rb_hash_aset(VALUE hash, VALUE key, VALUE val)
{
int iter_lev = RHASH_ITER_LEV(hash);
- st_table *tbl = RHASH(hash)->ntbl;
rb_hash_modify(hash);
- if (!tbl) {
+
+ if (RHASH_TABLE_NULL_P(hash)) {
if (iter_lev > 0) no_new_key();
- tbl = hash_tbl(hash);
+ ar_alloc_table(hash);
}
- if (tbl->type == &identhash || rb_obj_class(key) != rb_cString) {
+
+ if (RHASH_TYPE(hash) == &identhash || rb_obj_class(key) != rb_cString) {
RHASH_UPDATE_ITER(hash, iter_lev, key, hash_aset, val);
}
else {
@@ -1626,8 +2599,6 @@ replace_i(VALUE key, VALUE val, VALUE hash)
static VALUE
rb_hash_initialize_copy(VALUE hash, VALUE hash2)
{
- st_table *ntbl;
-
rb_hash_modify_check(hash);
hash2 = to_hash(hash2);
@@ -1635,15 +2606,23 @@ rb_hash_initialize_copy(VALUE hash, VALUE hash2)
if (hash == hash2) return hash;
- ntbl = RHASH(hash)->ntbl;
- if (RHASH(hash2)->ntbl) {
- if (ntbl) st_free_table(ntbl);
- RHASH(hash)->ntbl = st_copy(RHASH(hash2)->ntbl);
- if (RHASH(hash)->ntbl->num_entries)
+ if (RHASH_AR_TABLE_P(hash2)) {
+ if (RHASH_AR_TABLE_P(hash)) ar_free_and_clear_table(hash);
+ ar_copy(hash, hash2);
+ if (RHASH_AR_TABLE_SIZE(hash))
rb_hash_rehash(hash);
}
- else if (ntbl) {
- st_clear(ntbl);
+ else if (RHASH_ST_TABLE_P(hash2)) {
+ if (RHASH_ST_TABLE_P(hash)) st_free_table(RHASH_ST_TABLE(hash));
+ RHASH_ST_TABLE_SET(hash, st_copy(RHASH_ST_TABLE(hash2)));
+ if (RHASH_ST_TABLE(hash)->num_entries)
+ rb_hash_rehash(hash);
+ }
+ else if (RHASH_AR_TABLE_P(hash)) {
+ ar_clear(hash);
+ }
+ else if (RHASH_ST_TABLE_P(hash)) {
+ st_clear(RHASH_ST_TABLE(hash));
}
COPY_DEFAULT(hash, hash2);
@@ -1666,19 +2645,28 @@ rb_hash_initialize_copy(VALUE hash, VALUE hash2)
static VALUE
rb_hash_replace(VALUE hash, VALUE hash2)
{
- st_table *table2;
-
rb_hash_modify_check(hash);
if (hash == hash2) return hash;
hash2 = to_hash(hash2);
COPY_DEFAULT(hash, hash2);
- table2 = RHASH(hash2)->ntbl;
-
rb_hash_clear(hash);
- if (table2) hash_tbl(hash)->type = table2->type;
- rb_hash_foreach(hash2, replace_i, hash);
+
+ if (RHASH_AR_TABLE_P(hash)) {
+ if (RHASH_AR_TABLE_P(hash2)) {
+ ar_copy(hash, hash2);
+ }
+ else {
+ goto st_to_st;
+ }
+ }
+ else {
+ if (RHASH_AR_TABLE_P(hash2)) ar_force_convert_table(hash2, __FILE__, __LINE__);
+ st_to_st:
+ RHASH_TBL_RAW(hash)->type = RHASH_ST_TABLE(hash2)->type;
+ rb_hash_foreach(hash2, replace_i, hash);
+ }
return hash;
}
@@ -1691,9 +2679,12 @@ rb_hash_replace(VALUE hash, VALUE hash2)
* Returns the number of key-value pairs in the hash.
*
* h = { "d" => 100, "a" => 200, "v" => 300, "e" => 400 }
- * h.length #=> 4
+ * h.size #=> 4
* h.delete("a") #=> 200
+ * h.size #=> 3
* h.length #=> 3
+ *
+ * Hash#length is an alias for Hash#size.
*/
VALUE
@@ -1702,6 +2693,11 @@ rb_hash_size(VALUE hash)
return INT2FIX(RHASH_SIZE(hash));
}
+size_t
+rb_hash_size_num(VALUE hash)
+{
+ return (long)RHASH_SIZE(hash);
+}
/*
* call-seq:
@@ -1837,6 +2833,82 @@ rb_hash_each_pair(VALUE hash)
}
static int
+transform_keys_i(VALUE key, VALUE value, VALUE result)
+{
+ VALUE new_key = rb_yield(key);
+ rb_hash_aset(result, new_key, value);
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * hsh.transform_keys {|key| block } -> new_hash
+ * hsh.transform_keys -> an_enumerator
+ *
+ * Returns a new hash with the results of running the block once for
+ * every key.
+ * This method does not change the values.
+ *
+ * h = { a: 1, b: 2, c: 3 }
+ * h.transform_keys {|k| k.to_s } #=> { "a" => 1, "b" => 2, "c" => 3 }
+ * h.transform_keys(&:to_s) #=> { "a" => 1, "b" => 2, "c" => 3 }
+ * h.transform_keys.with_index {|k, i| "#{k}.#{i}" }
+ * #=> { "a.0" => 1, "b.1" => 2, "c.2" => 3 }
+ *
+ * If no block is given, an enumerator is returned instead.
+ */
+static VALUE
+rb_hash_transform_keys(VALUE hash)
+{
+ VALUE result;
+
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
+ result = rb_hash_new();
+ if (!RHASH_EMPTY_P(hash)) {
+ rb_hash_foreach(hash, transform_keys_i, result);
+ }
+
+ return result;
+}
+
+static VALUE rb_hash_flatten(int argc, VALUE *argv, VALUE hash);
+
+/*
+ * call-seq:
+ * hsh.transform_keys! {|key| block } -> hsh
+ * hsh.transform_keys! -> an_enumerator
+ *
+ * Invokes the given block once for each key in <i>hsh</i>, replacing it
+ * with the new key returned by the block, and then returns <i>hsh</i>.
+ * This method does not change the values.
+ *
+ * h = { a: 1, b: 2, c: 3 }
+ * h.transform_keys! {|k| k.to_s } #=> { "a" => 1, "b" => 2, "c" => 3 }
+ * h.transform_keys!(&:to_sym) #=> { a: 1, b: 2, c: 3 }
+ * h.transform_keys!.with_index {|k, i| "#{k}.#{i}" }
+ * #=> { "a.0" => 1, "b.1" => 2, "c.2" => 3 }
+ *
+ * If no block is given, an enumerator is returned instead.
+ */
+static VALUE
+rb_hash_transform_keys_bang(VALUE hash)
+{
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
+ rb_hash_modify_check(hash);
+ if (!RHASH_TABLE_EMPTY_P(hash)) {
+ long i;
+ VALUE pairs = rb_hash_flatten(0, NULL, hash);
+ rb_hash_clear(hash);
+ for (i = 0; i < RARRAY_LEN(pairs); i += 2) {
+ VALUE key = RARRAY_AREF(pairs, i), new_key = rb_yield(key),
+ val = RARRAY_AREF(pairs, i+1);
+ rb_hash_aset(hash, new_key, val);
+ }
+ }
+ return hash;
+}
+
+static int
transform_values_i(VALUE key, VALUE value, VALUE result)
{
VALUE new_value = rb_yield(value);
@@ -1846,10 +2918,11 @@ transform_values_i(VALUE key, VALUE value, VALUE result)
/*
* call-seq:
- * hsh.transform_values {|value| block } -> hsh
+ * hsh.transform_values {|value| block } -> new_hash
* hsh.transform_values -> an_enumerator
*
- * Return a new with the results of running block once for every value.
+ * Returns a new hash with the results of running the block once for
+ * every value.
* This method does not change the keys.
*
* h = { a: 1, b: 2, c: 3 }
@@ -1866,7 +2939,7 @@ rb_hash_transform_values(VALUE hash)
VALUE result;
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
- result = rb_hash_new();
+ result = rb_hash_new_with_size(RHASH_SIZE(hash));
if (!RHASH_EMPTY_P(hash)) {
rb_hash_foreach(hash, transform_values_i, result);
}
@@ -1879,14 +2952,15 @@ rb_hash_transform_values(VALUE hash)
* hsh.transform_values! {|value| block } -> hsh
* hsh.transform_values! -> an_enumerator
*
- * Return a new with the results of running block once for every value.
+ * Invokes the given block once for each value in <i>hsh</i>, replacing it
+ * with the new value returned by the block, and then returns <i>hsh</i>.
* This method does not change the keys.
*
* h = { a: 1, b: 2, c: 3 }
* h.transform_values! {|v| v * v + 1 } #=> { a: 2, b: 5, c: 10 }
- * h.transform_values!(&:to_s) #=> { a: "1", b: "2", c: "3" }
+ * h.transform_values!(&:to_s) #=> { a: "2", b: "5", c: "10" }
* h.transform_values!.with_index {|v, i| "#{v}.#{i}" }
- * #=> { a: "1.0", b: "2.1", c: "3.2" }
+ * #=> { a: "2.0", b: "5.1", c: "10.2" }
*
* If no block is given, an enumerator is returned instead.
*/
@@ -1895,7 +2969,7 @@ rb_hash_transform_values_bang(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
rb_hash_modify_check(hash);
- if (RHASH(hash)->ntbl)
+ if (!RHASH_TABLE_EMPTY_P(hash))
rb_hash_foreach(hash, transform_values_i, hash);
return hash;
}
@@ -1998,17 +3072,58 @@ rb_hash_to_hash(VALUE hash)
return hash;
}
+VALUE
+rb_hash_set_pair(VALUE hash, VALUE arg)
+{
+ VALUE pair;
+
+ pair = rb_check_array_type(arg);
+ if (NIL_P(pair)) {
+ rb_raise(rb_eTypeError, "wrong element type %s (expected array)",
+ rb_builtin_class_name(arg));
+ }
+ if (RARRAY_LEN(pair) != 2) {
+ rb_raise(rb_eArgError, "element has wrong array length (expected 2, was %ld)",
+ RARRAY_LEN(pair));
+ }
+ rb_hash_aset(hash, RARRAY_AREF(pair, 0), RARRAY_AREF(pair, 1));
+ return hash;
+}
+
+static int
+to_h_i(VALUE key, VALUE value, VALUE hash)
+{
+ rb_hash_set_pair(hash, rb_yield_values(2, key, value));
+ return ST_CONTINUE;
+}
+
+static VALUE
+rb_hash_to_h_block(VALUE hash)
+{
+ VALUE h = rb_hash_new_with_size(RHASH_SIZE(hash));
+ rb_hash_foreach(hash, to_h_i, h);
+ OBJ_INFECT(h, hash);
+ return h;
+}
+
/*
* call-seq:
- * hsh.to_h -> hsh or new_hash
+ * hsh.to_h -> hsh or new_hash
+ * hsh.to_h {|key, value| block } -> new_hash
*
* Returns +self+. If called on a subclass of Hash, converts
* the receiver to a Hash object.
+ *
+ * If a block is given, the results of the block on each pair of
+ * the receiver will be used as pairs.
*/
static VALUE
rb_hash_to_h(VALUE hash)
{
+ if (rb_block_given_p()) {
+ return rb_hash_to_h_block(hash);
+ }
if (rb_obj_class(hash) != rb_cHash) {
const VALUE flags = RBASIC(hash)->flags;
hash = hash_dup(hash, rb_cHash, flags & HASH_PROC_DEFAULT);
@@ -2035,22 +3150,25 @@ keys_i(VALUE key, VALUE value, VALUE ary)
*
*/
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_hash_keys(VALUE hash)
{
- VALUE keys;
st_index_t size = RHASH_SIZE(hash);
+ VALUE keys = rb_ary_new_capa(size);
- keys = rb_ary_new_capa(size);
if (size == 0) return keys;
if (ST_DATA_COMPATIBLE_P(VALUE)) {
- st_table *table = RHASH(hash)->ntbl;
-
- rb_gc_writebarrier_remember(keys);
- RARRAY_PTR_USE(keys, ptr, {
- size = st_keys_check(table, ptr, size, Qundef);
- });
+ RARRAY_PTR_USE_TRANSIENT(keys, ptr, {
+ if (RHASH_AR_TABLE_P(hash)) {
+ size = ar_keys(hash, ptr, size);
+ }
+ else {
+ st_table *table = RHASH_ST_TABLE(hash);
+ size = st_keys(table, ptr, size);
+ }
+ });
+ rb_gc_writebarrier_remember(keys);
rb_ary_set_len(keys, size);
}
else {
@@ -2089,12 +3207,19 @@ rb_hash_values(VALUE hash)
if (size == 0) return values;
if (ST_DATA_COMPATIBLE_P(VALUE)) {
- st_table *table = RHASH(hash)->ntbl;
-
- rb_gc_writebarrier_remember(values);
- RARRAY_PTR_USE(values, ptr, {
- size = st_values_check(table, ptr, size, Qundef);
- });
+ if (RHASH_AR_TABLE_P(hash)) {
+ rb_gc_writebarrier_remember(values);
+ RARRAY_PTR_USE_TRANSIENT(values, ptr, {
+ size = ar_values(hash, ptr, size);
+ });
+ }
+ else if (RHASH_ST_TABLE_P(hash)) {
+ st_table *table = RHASH_ST_TABLE(hash);
+ rb_gc_writebarrier_remember(values);
+ RARRAY_PTR_USE_TRANSIENT(values, ptr, {
+ size = st_values(table, ptr, size);
+ });
+ }
rb_ary_set_len(values, size);
}
else {
@@ -2123,12 +3248,13 @@ rb_hash_values(VALUE hash)
* See also Enumerable#include?
*/
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_hash_has_key(VALUE hash, VALUE key)
{
- if (!RHASH(hash)->ntbl)
- return Qfalse;
- if (st_lookup(RHASH(hash)->ntbl, key, 0)) {
+ if (RHASH_AR_TABLE_P(hash) && ar_lookup(hash, key, 0)) {
+ return Qtrue;
+ }
+ else if (RHASH_ST_TABLE_P(hash) && st_lookup(RHASH_ST_TABLE(hash), key, 0)) {
return Qtrue;
}
return Qfalse;
@@ -2155,8 +3281,8 @@ rb_hash_search_value(VALUE key, VALUE value, VALUE arg)
* in <i>hsh</i>.
*
* h = { "a" => 100, "b" => 200 }
- * h.has_value?(100) #=> true
- * h.has_value?(999) #=> false
+ * h.value?(100) #=> true
+ * h.value?(999) #=> false
*/
static VALUE
@@ -2172,7 +3298,7 @@ rb_hash_has_value(VALUE hash, VALUE val)
struct equal_data {
VALUE result;
- st_table *tbl;
+ VALUE hash;
int eql;
};
@@ -2182,10 +3308,15 @@ eql_i(VALUE key, VALUE val1, VALUE arg)
struct equal_data *data = (struct equal_data *)arg;
st_data_t val2;
- if (!st_lookup(data->tbl, key, &val2)) {
+ if (RHASH_AR_TABLE_P(data->hash) && !ar_lookup(data->hash, key, &val2)) {
data->result = Qfalse;
return ST_STOP;
}
+ else if (RHASH_ST_TABLE_P(data->hash) && !st_lookup(RHASH_ST_TABLE(data->hash), key, &val2)) {
+ data->result = Qfalse;
+ return ST_STOP;
+ }
+
if (!(data->eql ? rb_eql(val1, (VALUE)val2) : (int)rb_equal(val1, (VALUE)val2))) {
data->result = Qfalse;
return ST_STOP;
@@ -2230,19 +3361,23 @@ hash_equal(VALUE hash1, VALUE hash2, int eql)
}
if (RHASH_SIZE(hash1) != RHASH_SIZE(hash2))
return Qfalse;
- if (!RHASH(hash1)->ntbl || !RHASH(hash2)->ntbl)
- return Qtrue;
- if (RHASH(hash1)->ntbl->type != RHASH(hash2)->ntbl->type)
- return Qfalse;
+ if (!RHASH_TABLE_EMPTY_P(hash1) && !RHASH_TABLE_EMPTY_P(hash2)) {
+ if (RHASH_TYPE(hash1) != RHASH_TYPE(hash2)) {
+ return Qfalse;
+ }
+ else {
+ data.hash = hash2;
+ data.eql = eql;
+ return rb_exec_recursive_paired(recursive_eql, hash1, hash2, (VALUE)&data);
+ }
+ }
+
#if 0
if (!(rb_equal(RHASH_IFNONE(hash1), RHASH_IFNONE(hash2)) &&
FL_TEST(hash1, HASH_PROC_DEFAULT) == FL_TEST(hash2, HASH_PROC_DEFAULT)))
return Qfalse;
#endif
-
- data.tbl = RHASH(hash2)->ntbl;
- data.eql = eql;
- return rb_exec_recursive_paired(recursive_eql, hash1, hash2, (VALUE)&data);
+ return Qtrue;
}
/*
@@ -2366,7 +3501,7 @@ rb_hash_invert_i(VALUE key, VALUE value, VALUE hash)
static VALUE
rb_hash_invert(VALUE hash)
{
- VALUE h = rb_hash_new();
+ VALUE h = rb_hash_new_with_size(RHASH_SIZE(hash));
rb_hash_foreach(hash, rb_hash_invert_i, h);
return h;
@@ -2424,39 +3559,64 @@ rb_hash_update_block_i(VALUE key, VALUE value, VALUE hash)
/*
* call-seq:
- * hsh.merge!(other_hash) -> hsh
- * hsh.update(other_hash) -> hsh
- * hsh.merge!(other_hash){|key, oldval, newval| block} -> hsh
- * hsh.update(other_hash){|key, oldval, newval| block} -> hsh
+ * hsh.merge!(other_hash1, other_hash2, ...) -> hsh
+ * hsh.update(other_hash1, other_hash2, ...) -> hsh
+ * hsh.merge!(other_hash1, other_hash2, ...) {|key, oldval, newval| block}
+ * -> hsh
+ * hsh.update(other_hash1, other_hash2, ...) {|key, oldval, newval| block}
+ * -> hsh
+ *
+ * Adds the contents of the given hashes to the receiver.
*
- * Adds the contents of _other_hash_ to _hsh_. If no block is specified,
- * entries with duplicate keys are overwritten with the values from
- * _other_hash_, otherwise the value of each duplicate key is determined by
- * calling the block with the key, its value in _hsh_ and its value in
- * _other_hash_.
+ * If no block is given, entries with duplicate keys are overwritten
+ * with the values from each +other_hash+ successively,
+ * otherwise the value for each duplicate key is determined by
+ * calling the block with the key, its value in the receiver and
+ * its value in each +other_hash+.
*
* h1 = { "a" => 100, "b" => 200 }
- * h2 = { "b" => 254, "c" => 300 }
- * h1.merge!(h2) #=> {"a"=>100, "b"=>254, "c"=>300}
+ * h1.merge! #=> {"a"=>100, "b"=>200}
+ * h1 #=> {"a"=>100, "b"=>200}
*
* h1 = { "a" => 100, "b" => 200 }
- * h2 = { "b" => 254, "c" => 300 }
- * h1.merge!(h2) { |key, v1, v2| v1 }
- * #=> {"a"=>100, "b"=>200, "c"=>300}
+ * h2 = { "b" => 246, "c" => 300 }
+ * h1.merge!(h2) #=> {"a"=>100, "b"=>246, "c"=>300}
+ * h1 #=> {"a"=>100, "b"=>246, "c"=>300}
+ *
+ * h1 = { "a" => 100, "b" => 200 }
+ * h2 = { "b" => 246, "c" => 300 }
+ * h3 = { "b" => 357, "d" => 400 }
+ * h1.merge!(h2, h3)
+ * #=> {"a"=>100, "b"=>357, "c"=>300, "d"=>400}
+ * h1 #=> {"a"=>100, "b"=>357, "c"=>300, "d"=>400}
+ *
+ * h1 = { "a" => 100, "b" => 200 }
+ * h2 = { "b" => 246, "c" => 300 }
+ * h3 = { "b" => 357, "d" => 400 }
+ * h1.merge!(h2, h3) {|key, v1, v2| v1 }
+ * #=> {"a"=>100, "b"=>200, "c"=>300, "d"=>400}
+ * h1 #=> {"a"=>100, "b"=>200, "c"=>300, "d"=>400}
+ *
+ * Hash#update is an alias for Hash#merge!.
*/
static VALUE
-rb_hash_update(VALUE hash1, VALUE hash2)
+rb_hash_update(int argc, VALUE *argv, VALUE self)
{
- rb_hash_modify(hash1);
- hash2 = to_hash(hash2);
- if (rb_block_given_p()) {
- rb_hash_foreach(hash2, rb_hash_update_block_i, hash1);
- }
- else {
- rb_hash_foreach(hash2, rb_hash_update_i, hash1);
+ int i;
+ bool block_given = rb_block_given_p();
+
+ rb_hash_modify(self);
+ for (i = 0; i < argc; i++){
+ VALUE hash = to_hash(argv[i]);
+ if (block_given) {
+ rb_hash_foreach(hash, rb_hash_update_block_i, self);
+ }
+ else {
+ rb_hash_foreach(hash, rb_hash_update_i, self);
+ }
}
- return hash1;
+ return self;
}
struct update_func_arg {
@@ -2515,28 +3675,39 @@ rb_hash_update_by(VALUE hash1, VALUE hash2, rb_hash_update_func *func)
/*
* call-seq:
- * hsh.merge(other_hash) -> new_hash
- * hsh.merge(other_hash){|key, oldval, newval| block} -> new_hash
+ * hsh.merge(other_hash1, other_hash2, ...) -> new_hash
+ * hsh.merge(other_hash1, other_hash2, ...) {|key, oldval, newval| block}
+ * -> new_hash
+ *
+ * Returns a new hash that combines the contents of the receiver and
+ * the contents of the given hashes.
+ *
+ * If no block is given, entries with duplicate keys are overwritten
+ * with the values from each +other_hash+ successively,
+ * otherwise the value for each duplicate key is determined by
+ * calling the block with the key, its value in the receiver and
+ * its value in each +other_hash+.
*
- * Returns a new hash containing the contents of <i>other_hash</i> and
- * the contents of <i>hsh</i>. If no block is specified, the value for
- * entries with duplicate keys will be that of <i>other_hash</i>. Otherwise
- * the value for each duplicate key is determined by calling the block
- * with the key, its value in <i>hsh</i> and its value in <i>other_hash</i>.
+ * When called without any argument, returns a copy of the receiver.
*
* h1 = { "a" => 100, "b" => 200 }
- * h2 = { "b" => 254, "c" => 300 }
- * h1.merge(h2) #=> {"a"=>100, "b"=>254, "c"=>300}
- * h1.merge(h2){|key, oldval, newval| newval - oldval}
- * #=> {"a"=>100, "b"=>54, "c"=>300}
- * h1 #=> {"a"=>100, "b"=>200}
+ * h2 = { "b" => 246, "c" => 300 }
+ * h3 = { "b" => 357, "d" => 400 }
+ * h1.merge #=> {"a"=>100, "b"=>200}
+ * h1.merge(h2) #=> {"a"=>100, "b"=>246, "c"=>300}
+ * h1.merge(h2, h3) #=> {"a"=>100, "b"=>357, "c"=>300, "d"=>400}
+ * h1.merge(h2) {|key, oldval, newval| newval - oldval}
+ * #=> {"a"=>100, "b"=>46, "c"=>300}
+ * h1.merge(h2, h3) {|key, oldval, newval| newval - oldval}
+ * #=> {"a"=>100, "b"=>311, "c"=>300, "d"=>400}
+ * h1 #=> {"a"=>100, "b"=>200}
*
*/
static VALUE
-rb_hash_merge(VALUE hash1, VALUE hash2)
+rb_hash_merge(int argc, VALUE *argv, VALUE self)
{
- return rb_hash_update(rb_obj_dup(hash1), hash2);
+ return rb_hash_update(argc, argv, rb_hash_dup(self));
}
static int
@@ -2561,7 +3732,8 @@ static VALUE
reset_hash_type(VALUE arg)
{
struct reset_hash_type_arg *p = (struct reset_hash_type_arg *)arg;
- RHASH(p->hash)->ntbl->type = p->orighash;
+ HASH_ASSERT(RHASH_ST_TABLE_P(p->hash));
+ RHASH_ST_TABLE(p->hash)->type = p->orighash;
return Qundef;
}
@@ -2599,7 +3771,10 @@ rb_hash_assoc(VALUE hash, VALUE key)
VALUE args[2];
if (RHASH_EMPTY_P(hash)) return Qnil;
- table = RHASH(hash)->ntbl;
+
+ ar_force_convert_table(hash, __FILE__, __LINE__);
+ HASH_ASSERT(RHASH_ST_TABLE_P(hash));
+ table = RHASH_ST_TABLE(hash);
orighash = table->type;
if (orighash != &identhash) {
@@ -2609,7 +3784,7 @@ rb_hash_assoc(VALUE hash, VALUE key)
assochash.compare = assoc_cmp;
assochash.hash = orighash->hash;
- table->type = &assochash;
+ table->type = &assochash;
args[0] = hash;
args[1] = key;
ensure_arg.hash = hash;
@@ -2693,17 +3868,23 @@ rb_hash_flatten(int argc, VALUE *argv, VALUE hash)
{
VALUE ary;
+ rb_check_arity(argc, 0, 1);
+
if (argc) {
- int level = NUM2INT(*argv);
+ int level = NUM2INT(argv[0]);
+
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);
- if (level - 1 > 0) {
- *argv = INT2FIX(level - 1);
- rb_funcallv(ary, id_flatten_bang, argc, argv);
+ 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);
}
}
@@ -2757,10 +3938,10 @@ rb_hash_compact(VALUE hash)
/*
* call-seq:
- * hsh.compact! -> hsh
+ * hsh.compact! -> hsh or nil
*
* Removes all nil values from the hash.
- * Returns the hash.
+ * Returns nil if no changes were made, otherwise returns the hash.
*
* h = { a: 1, b: false, c: nil }
* h.compact! #=> { a: 1, b: false }
@@ -2770,18 +3951,17 @@ rb_hash_compact(VALUE hash)
static VALUE
rb_hash_compact_bang(VALUE hash)
{
+ st_index_t n;
rb_hash_modify_check(hash);
- if (RHASH(hash)->ntbl) {
- st_index_t n = RHASH(hash)->ntbl->num_entries;
+ n = RHASH_SIZE(hash);
+ if (n) {
rb_hash_foreach(hash, delete_if_nil, hash);
- if (n != RHASH(hash)->ntbl->num_entries)
+ if (n != RHASH_SIZE(hash))
return hash;
}
return Qnil;
}
-static VALUE rb_hash_compare_by_id_p(VALUE hash);
-
/*
* call-seq:
* hsh.compare_by_identity -> hsh
@@ -2801,10 +3981,24 @@ static VALUE rb_hash_compare_by_id_p(VALUE hash);
static VALUE
rb_hash_compare_by_id(VALUE hash)
{
+ VALUE tmp;
+ st_table *identtable;
+
if (rb_hash_compare_by_id_p(hash)) return hash;
- rb_hash_modify(hash);
- RHASH(hash)->ntbl->type = &identhash;
- rb_hash_rehash(hash);
+
+ rb_hash_modify_check(hash);
+ ar_force_convert_table(hash, __FILE__, __LINE__);
+ HASH_ASSERT(RHASH_ST_TABLE_P(hash));
+
+ tmp = hash_alloc(0);
+ identtable = rb_init_identtable_with_size(RHASH_SIZE(hash));
+ RHASH_ST_TABLE_SET(tmp, identtable);
+ rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp);
+ st_free_table(RHASH_ST_TABLE(hash));
+ RHASH_ST_TABLE_SET(hash, identtable);
+ RHASH_ST_CLEAR(tmp);
+ rb_gc_force_recycle(tmp);
+
return hash;
}
@@ -2817,22 +4011,22 @@ rb_hash_compare_by_id(VALUE hash)
*
*/
-static VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_hash_compare_by_id_p(VALUE hash)
{
- if (!RHASH(hash)->ntbl)
- return Qfalse;
- if (RHASH(hash)->ntbl->type == &identhash) {
+ if (RHASH_ST_TABLE_P(hash) && RHASH_ST_TABLE(hash)->type == &identhash) {
return Qtrue;
}
- return Qfalse;
+ else {
+ return Qfalse;
+ }
}
VALUE
rb_ident_hash_new(void)
{
VALUE hash = rb_hash_new();
- RHASH(hash)->ntbl = st_init_table(&identhash);
+ RHASH_ST_TABLE_SET(hash, st_init_table(&identhash));
return hash;
}
@@ -2870,50 +4064,74 @@ any_p_i_fast(VALUE key, VALUE value, VALUE arg)
return ST_CONTINUE;
}
+static int
+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;
+ }
+ return ST_CONTINUE;
+}
+
/*
* call-seq:
* hsh.any? [{ |(key, value)| block }] -> true or false
+ * hsh.any?(pattern) -> true or false
*
* See also Enumerable#any?
*/
static VALUE
-rb_hash_any_p(VALUE hash)
+rb_hash_any_p(int argc, VALUE *argv, VALUE hash)
{
- VALUE ret = Qfalse;
+ VALUE args[2];
+ args[0] = Qfalse;
+ rb_check_arity(argc, 0, 1);
if (RHASH_EMPTY_P(hash)) return Qfalse;
- if (!rb_block_given_p()) {
- /* yields pairs, never false */
- return Qtrue;
+ if (argc) {
+ if (rb_block_given_p()) {
+ rb_warn("given block not used");
+ }
+ args[1] = argv[0];
+
+ rb_hash_foreach(hash, any_p_i_pattern, (VALUE)args);
}
- if (rb_block_arity() > 1)
- rb_hash_foreach(hash, any_p_i_fast, (VALUE)&ret);
- else
- rb_hash_foreach(hash, any_p_i, (VALUE)&ret);
- return ret;
+ else {
+ if (!rb_block_given_p()) {
+ /* yields pairs, never false */
+ return Qtrue;
+ }
+ if (rb_block_arity() > 1)
+ rb_hash_foreach(hash, any_p_i_fast, (VALUE)args);
+ else
+ rb_hash_foreach(hash, any_p_i, (VALUE)args);
+ }
+ return args[0];
}
/*
* call-seq:
* hsh.dig(key, ...) -> object
*
- * Extracts the nested value specified by the sequence of <i>idx</i>
+ * Extracts the nested value specified by the sequence of <i>key</i>
* objects by calling +dig+ at each step, returning +nil+ if any
* intermediate step is +nil+.
*
* h = { foo: {bar: {baz: 1}}}
*
- * h.dig(:foo, :bar, :baz) #=> 1
- * h.dig(:foo, :zot, :xyz) #=> nil
+ * h.dig(:foo, :bar, :baz) #=> 1
+ * h.dig(:foo, :zot, :xyz) #=> nil
*
* g = { foo: [10, 11, 12] }
- * g.dig(:foo, 1) #=> 11
- * g.dig(:foo, 1, 0) #=> TypeError: Integer does not have #dig method
- * g.dig(:foo, :bar) #=> TypeError: no implicit conversion of Symbol into Integer
+ * g.dig(:foo, 1) #=> 11
+ * g.dig(:foo, 1, 0) #=> TypeError: Integer does not have #dig method
+ * g.dig(:foo, :bar) #=> TypeError: no implicit conversion of Symbol into Integer
*/
-VALUE
+static VALUE
rb_hash_dig(int argc, VALUE *argv, VALUE self)
{
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
@@ -3034,6 +4252,19 @@ hash_proc_call(VALUE key, VALUE hash, int argc, const VALUE *argv, VALUE passed_
return rb_hash_aref(hash, *argv);
}
+/*
+ * call-seq:
+ * hash.to_proc -> proc
+ *
+ * Returns a Proc which maps keys to values.
+ *
+ * h = {a:1, b:2}
+ * hp = h.to_proc
+ * hp.call(:a) #=> 1
+ * hp.call(:b) #=> 2
+ * hp.call(:c) #=> nil
+ * [:a, :b, :c].map(&h) #=> [1, 2, nil]
+ */
static VALUE
rb_hash_to_proc(VALUE hash)
{
@@ -3057,11 +4288,70 @@ add_new_i(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
int
rb_hash_add_new_element(VALUE hash, VALUE key, VALUE val)
{
- st_table *tbl = rb_hash_tbl_raw(hash);
+ st_table *tbl;
+ int ret = 0;
VALUE args[2];
args[0] = hash;
args[1] = val;
+
+ if (RHASH_AR_TABLE_P(hash)) {
+ hash_ar_table(hash);
+
+ ret = ar_update(hash, (st_data_t)key, add_new_i, (st_data_t)args);
+ if (ret != -1) {
+ return ret;
+ }
+ ar_try_convert_table(hash);
+ }
+ tbl = RHASH_TBL_RAW(hash);
return st_update(tbl, (st_data_t)key, add_new_i, (st_data_t)args);
+
+}
+
+static st_data_t
+key_stringify(VALUE key)
+{
+ return (rb_obj_class(key) == rb_cString && !RB_OBJ_FROZEN(key)) ?
+ rb_hash_key_str(key) : key;
+}
+
+static void
+ar_bulk_insert(VALUE hash, long argc, const VALUE *argv)
+{
+ long i;
+ for (i = 0; i < argc; ) {
+ st_data_t k = key_stringify(argv[i++]);
+ st_data_t v = argv[i++];
+ ar_insert(hash, k, v);
+ RB_OBJ_WRITTEN(hash, Qundef, k);
+ RB_OBJ_WRITTEN(hash, Qundef, v);
+ }
+}
+
+MJIT_FUNC_EXPORTED void
+rb_hash_bulk_insert(long argc, const VALUE *argv, VALUE hash)
+{
+ HASH_ASSERT(argc % 2 == 0);
+ if (argc > 0) {
+ st_index_t size = argc / 2;
+
+ if (RHASH_TABLE_NULL_P(hash)) {
+ if (size <= RHASH_AR_TABLE_MAX_SIZE) {
+ hash_ar_table(hash);
+ }
+ else {
+ RHASH_TBL_RAW(hash);
+ }
+ }
+
+ if (RHASH_AR_TABLE_P(hash) &&
+ (RHASH_AR_TABLE_SIZE(hash) + size <= RHASH_AR_TABLE_MAX_SIZE)) {
+ ar_bulk_insert(hash, argc, argv);
+ }
+ else {
+ rb_hash_bulk_insert_into_st_table(argc, argv, hash);
+ }
+ }
}
static int path_tainted = -1;
@@ -3108,20 +4398,17 @@ extern char **environ;
#define ENVNMATCH(s1, s2, n) (memcmp((s1), (s2), (n)) == 0)
#endif
-#ifdef _WIN32
-static VALUE
-env_str_transcode(VALUE str, rb_encoding *enc)
-{
- return rb_str_conv_enc_opts(str, NULL, enc,
- ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
-}
-#endif
-
static VALUE
env_enc_str_new(const char *ptr, long len, rb_encoding *enc)
{
#ifdef _WIN32
- VALUE str = env_str_transcode(rb_utf8_str_new(ptr, len), enc);
+ rb_encoding *internal = rb_default_internal_encoding();
+ const int ecflags = ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE;
+ rb_encoding *utf8 = rb_utf8_encoding();
+ VALUE str = rb_enc_str_new(NULL, 0, (internal ? internal : enc));
+ if (NIL_P(rb_str_cat_conv_enc_opts(str, 0, ptr, len, utf8, ecflags, Qnil))) {
+ rb_str_initialize(str, ptr, len, NULL);
+ }
#else
VALUE str = rb_external_str_new_with_enc(ptr, len, enc);
#endif
@@ -3152,6 +4439,9 @@ env_str_new2(const char *ptr)
static int env_path_tainted(const char *);
+static const char TZ_ENV[] = "TZ";
+extern bool ruby_tz_uptodate_p;
+
static rb_encoding *
env_encoding_for(const char *name, const char *ptr)
{
@@ -3233,6 +4523,9 @@ env_delete(VALUE obj, VALUE name)
RB_GC_GUARD(name);
path_tainted = 0;
}
+ else if (ENVMATCH(nam, TZ_ENV)) {
+ ruby_tz_uptodate_p = FALSE;
+ }
return value;
}
return Qnil;
@@ -3240,8 +4533,8 @@ env_delete(VALUE obj, VALUE name)
/*
* call-seq:
- * ENV.delete(name) -> value
- * ENV.delete(name) { |name| } -> value
+ * ENV.delete(name) -> value
+ * ENV.delete(name) { |name| block } -> value
*
* Deletes the environment variable with +name+ and returns the value of the
* variable. If a block is given it will be called when the named environment
@@ -3280,14 +4573,14 @@ rb_f_getenv(VALUE obj, VALUE name)
/*
* :yield: missing_name
* call-seq:
- * ENV.fetch(name) -> value
- * ENV.fetch(name, default) -> value
- * ENV.fetch(name) { |missing_name| ... } -> value
+ * ENV.fetch(name) -> value
+ * ENV.fetch(name, default) -> value
+ * ENV.fetch(name) { |missing_name| block } -> value
*
* Retrieves the environment variable +name+.
*
* If the given name does not exist and neither +default+ nor a block a
- * provided an IndexError is raised. If a block is given it is called with
+ * provided an KeyError is raised. If a block is given it is called with
* the missing name to provide a value. If a default value is given it will
* be returned when no block is given.
*/
@@ -3309,7 +4602,7 @@ env_fetch(int argc, VALUE *argv)
if (!env) {
if (block_given) return rb_yield(key);
if (argc == 1) {
- rb_raise(rb_eKeyError, "key not found: \"%"PRIsVALUE"\"", key);
+ rb_key_err_raise(rb_sprintf("key not found: \"%"PRIsVALUE"\"", key), envtbl, key);
}
return argv[1];
}
@@ -3376,10 +4669,33 @@ getenvsize(const WCHAR* p)
while (*p++) p += lstrlenW(p) + 1;
return p - porg + 1;
}
+
static size_t
getenvblocksize(void)
{
+#ifdef _MAX_ENV
+ return _MAX_ENV;
+#else
return 32767;
+#endif
+}
+
+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;
+ }
+ }
+ return 0;
}
#endif
@@ -3419,16 +4735,11 @@ ruby_setenv(const char *name, const char *value)
check_envname(name);
len = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0);
if (value) {
- WCHAR* p = GetEnvironmentStringsW();
- size_t n;
int len2;
- if (!p) goto fail; /* never happen */
- n = lstrlen(name) + 2 + strlen(value) + getenvsize(p);
- FreeEnvironmentStringsW(p);
- if (n >= getenvblocksize()) {
+ 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' */
}
- len2 = MultiByteToWideChar(CP_UTF8, 0, value, -1, NULL, 0);
wname = ALLOCV_N(WCHAR, buf, len + len2);
wvalue = wname + len;
MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, len);
@@ -3592,6 +4903,9 @@ env_aset(VALUE obj, VALUE nm, VALUE val)
path_tainted_p(value);
}
}
+ else if (ENVMATCH(name, TZ_ENV)) {
+ ruby_tz_uptodate_p = FALSE;
+ }
return val;
}
@@ -3638,8 +4952,8 @@ rb_env_size(VALUE ehash, VALUE args, VALUE eobj)
/*
* call-seq:
- * ENV.each_key { |name| } -> Hash
- * ENV.each_key -> Enumerator
+ * ENV.each_key { |name| block } -> Hash
+ * ENV.each_key -> Enumerator
*
* Yields each environment variable name.
*
@@ -3686,8 +5000,8 @@ env_values(void)
/*
* call-seq:
- * ENV.each_value { |value| } -> Hash
- * ENV.each_value -> Enumerator
+ * ENV.each_value { |value| block } -> Hash
+ * ENV.each_value -> Enumerator
*
* Yields each environment variable +value+.
*
@@ -3709,10 +5023,10 @@ env_each_value(VALUE ehash)
/*
* call-seq:
- * ENV.each { |name, value| } -> Hash
- * ENV.each -> Enumerator
- * ENV.each_pair { |name, value| } -> Hash
- * ENV.each_pair -> Enumerator
+ * ENV.each { |name, value| block } -> Hash
+ * ENV.each -> Enumerator
+ * ENV.each_pair { |name, value| block } -> Hash
+ * ENV.each_pair -> Enumerator
*
* Yields each environment variable +name+ and +value+.
*
@@ -3754,10 +5068,10 @@ env_each_pair(VALUE ehash)
/*
* call-seq:
- * ENV.reject! { |name, value| } -> ENV or nil
- * ENV.reject! -> Enumerator
+ * ENV.reject! { |name, value| block } -> ENV or nil
+ * ENV.reject! -> Enumerator
*
- * Equivalent to ENV#delete_if but returns +nil+ if no changes were made.
+ * Equivalent to ENV.delete_if but returns +nil+ if no changes were made.
*
* Returns an Enumerator if no block was given.
*/
@@ -3788,8 +5102,8 @@ env_reject_bang(VALUE ehash)
/*
* call-seq:
- * ENV.delete_if { |name, value| } -> Hash
- * ENV.delete_if -> Enumerator
+ * ENV.delete_if { |name, value| block } -> Hash
+ * ENV.delete_if -> Enumerator
*
* Deletes every environment variable for which the block evaluates to +true+.
*
@@ -3825,12 +5139,16 @@ env_values_at(int argc, VALUE *argv)
/*
* call-seq:
- * ENV.select { |name, value| } -> Hash
- * ENV.select -> Enumerator
+ * ENV.select { |name, value| block } -> Hash
+ * ENV.select -> Enumerator
+ * ENV.filter { |name, value| block } -> Hash
+ * ENV.filter -> Enumerator
*
* Returns a copy of the environment for entries where the block returns true.
*
* Returns an Enumerator if no block was given.
+ *
+ * ENV.filter is an alias for ENV.select.
*/
static VALUE
env_select(VALUE ehash)
@@ -3858,10 +5176,14 @@ env_select(VALUE ehash)
/*
* call-seq:
- * ENV.select! { |name, value| } -> ENV or nil
- * ENV.select! -> Enumerator
+ * ENV.select! { |name, value| block } -> ENV or nil
+ * ENV.select! -> Enumerator
+ * ENV.filter! { |name, value| block } -> ENV or nil
+ * ENV.filter! -> Enumerator
+ *
+ * Equivalent to ENV.keep_if but returns +nil+ if no changes were made.
*
- * Equivalent to ENV#keep_if but returns +nil+ if no changes were made.
+ * ENV.filter! is an alias for ENV.select!.
*/
static VALUE
env_select_bang(VALUE ehash)
@@ -3890,8 +5212,8 @@ env_select_bang(VALUE ehash)
/*
* call-seq:
- * ENV.keep_if { |name, value| } -> Hash
- * ENV.keep_if -> Enumerator
+ * ENV.keep_if { |name, value| block } -> Hash
+ * ENV.keep_if -> Enumerator
*
* Deletes every environment variable where the block evaluates to +false+.
*
@@ -3906,6 +5228,35 @@ env_keep_if(VALUE ehash)
}
/*
+ * call-seq:
+ * ENV.slice(*keys) -> a_hash
+ *
+ * Returns a hash containing only the given keys from ENV and their values.
+ *
+ * ENV.slice("TERM","HOME") #=> {"TERM"=>"xterm-256color", "HOME"=>"/Users/rhc"}
+ */
+static VALUE
+env_slice(int argc, VALUE *argv)
+{
+ int i;
+ VALUE key, value, result;
+
+ if (argc == 0) {
+ return rb_hash_new();
+ }
+ result = rb_hash_new_with_size(argc);
+
+ for (i = 0; i < argc; i++) {
+ key = argv[i];
+ value = rb_f_getenv(Qnil, key);
+ if (value != Qnil)
+ rb_hash_aset(result, key, value);
+ }
+
+ return result;
+}
+
+/*
* call-seq:
* ENV.clear
*
@@ -4205,7 +5556,6 @@ env_index(VALUE dmy, VALUE value)
/*
* call-seq:
* ENV.to_hash -> hash
- * ENV.to_h -> hash
*
* Creates a hash with a copy of the environment variables.
*
@@ -4232,10 +5582,28 @@ env_to_hash(void)
/*
* call-seq:
- * ENV.reject { |name, value| } -> Hash
- * ENV.reject -> Enumerator
+ * ENV.to_h -> hash
+ * ENV.to_h {|name, value| block } -> hash
+ *
+ * Creates a hash with a copy of the environment variables.
*
- * Same as ENV#delete_if, but works on (and returns) a copy of the
+ */
+static VALUE
+env_to_h(void)
+{
+ VALUE hash = env_to_hash();
+ if (rb_block_given_p()) {
+ hash = rb_hash_to_h_block(hash);
+ }
+ return hash;
+}
+
+/*
+ * call-seq:
+ * ENV.reject { |name, value| block } -> Hash
+ * ENV.reject -> Enumerator
+ *
+ * Same as ENV.delete_if, but works on (and returns) a copy of the
* environment.
*/
static VALUE
@@ -4284,13 +5652,29 @@ env_invert(void)
return rb_hash_invert(env_to_hash());
}
+static void
+keylist_delete(VALUE keys, VALUE key)
+{
+ long keylen, elen, i;
+ const char *keyptr, *eptr;
+ RSTRING_GETMEM(key, keyptr, keylen);
+ for (i=0; i<RARRAY_LEN(keys); i++) {
+ VALUE e = RARRAY_AREF(keys, i);
+ RSTRING_GETMEM(e, eptr, elen);
+ if (elen != keylen) continue;
+ if (!ENVNMATCH(keyptr, eptr, elen)) continue;
+ rb_ary_delete_at(keys, i);
+ return;
+ }
+}
+
static int
env_replace_i(VALUE key, VALUE val, VALUE keys)
{
+ env_name(key);
env_aset(Qnil, key, val);
- if (rb_ary_includes(keys, key)) {
- rb_ary_delete(keys, key);
- }
+
+ keylist_delete(keys, key);
return ST_CONTINUE;
}
@@ -4331,8 +5715,8 @@ env_update_i(VALUE key, VALUE val)
/*
* call-seq:
- * ENV.update(hash) -> Hash
- * ENV.update(hash) { |name, old_value, new_value| } -> Hash
+ * ENV.update(hash) -> Hash
+ * ENV.update(hash) { |name, old_value, new_value| block } -> Hash
*
* Adds the contents of +hash+ to the environment variables. If no block is
* specified entries with duplicate keys are overwritten, otherwise the value
@@ -4399,7 +5783,7 @@ env_update(VALUE env, VALUE hash)
* Hashes are an easy way to represent data structures, such as
*
* books = {}
- * books[:matz] = "The Ruby Language"
+ * books[:matz] = "The Ruby Programming Language"
* books[:black] = "The Well-Grounded Rubyist"
*
* Hashes are also commonly used as a way to have named parameters in
@@ -4467,6 +5851,9 @@ Init_Hash(void)
#undef rb_intern
#define rb_intern(str) rb_intern_const(str)
+ RUBY_ASSERT(RESERVED_HASH_VAL == st_reserved_hash_val);
+ RUBY_ASSERT(RESERVED_HASH_SUBSTITUTION_VAL == st_reserved_hash_substitution_val);
+
id_hash = rb_intern("hash");
id_yield = rb_intern("yield");
id_default = rb_intern("default");
@@ -4479,78 +5866,83 @@ Init_Hash(void)
rb_define_alloc_func(rb_cHash, empty_hash_alloc);
rb_define_singleton_method(rb_cHash, "[]", rb_hash_s_create, -1);
rb_define_singleton_method(rb_cHash, "try_convert", rb_hash_s_try_convert, 1);
- rb_define_method(rb_cHash,"initialize", rb_hash_initialize, -1);
- rb_define_method(rb_cHash,"initialize_copy", rb_hash_initialize_copy, 1);
- rb_define_method(rb_cHash,"rehash", rb_hash_rehash, 0);
-
- rb_define_method(rb_cHash,"to_hash", rb_hash_to_hash, 0);
- rb_define_method(rb_cHash,"to_h", rb_hash_to_h, 0);
- rb_define_method(rb_cHash,"to_a", rb_hash_to_a, 0);
- rb_define_method(rb_cHash,"inspect", rb_hash_inspect, 0);
+ rb_define_method(rb_cHash, "initialize", rb_hash_initialize, -1);
+ rb_define_method(rb_cHash, "initialize_copy", rb_hash_initialize_copy, 1);
+ rb_define_method(rb_cHash, "rehash", rb_hash_rehash, 0);
+
+ rb_define_method(rb_cHash, "to_hash", rb_hash_to_hash, 0);
+ rb_define_method(rb_cHash, "to_h", rb_hash_to_h, 0);
+ rb_define_method(rb_cHash, "to_a", rb_hash_to_a, 0);
+ rb_define_method(rb_cHash, "inspect", rb_hash_inspect, 0);
rb_define_alias(rb_cHash, "to_s", "inspect");
- rb_define_method(rb_cHash,"to_proc", rb_hash_to_proc, 0);
-
- rb_define_method(rb_cHash,"==", rb_hash_equal, 1);
- rb_define_method(rb_cHash,"[]", rb_hash_aref, 1);
- rb_define_method(rb_cHash,"hash", rb_hash_hash, 0);
- rb_define_method(rb_cHash,"eql?", rb_hash_eql, 1);
- rb_define_method(rb_cHash,"fetch", rb_hash_fetch_m, -1);
- rb_define_method(rb_cHash,"[]=", rb_hash_aset, 2);
- rb_define_method(rb_cHash,"store", rb_hash_aset, 2);
- rb_define_method(rb_cHash,"default", rb_hash_default, -1);
- rb_define_method(rb_cHash,"default=", rb_hash_set_default, 1);
- rb_define_method(rb_cHash,"default_proc", rb_hash_default_proc, 0);
- rb_define_method(rb_cHash,"default_proc=", rb_hash_set_default_proc, 1);
- rb_define_method(rb_cHash,"key", rb_hash_key, 1);
- rb_define_method(rb_cHash,"index", rb_hash_index, 1);
- rb_define_method(rb_cHash,"size", rb_hash_size, 0);
- rb_define_method(rb_cHash,"length", rb_hash_size, 0);
- rb_define_method(rb_cHash,"empty?", rb_hash_empty_p, 0);
-
- rb_define_method(rb_cHash,"each_value", rb_hash_each_value, 0);
- rb_define_method(rb_cHash,"each_key", rb_hash_each_key, 0);
- rb_define_method(rb_cHash,"each_pair", rb_hash_each_pair, 0);
- rb_define_method(rb_cHash,"each", rb_hash_each_pair, 0);
-
+ rb_define_method(rb_cHash, "to_proc", rb_hash_to_proc, 0);
+
+ rb_define_method(rb_cHash, "==", rb_hash_equal, 1);
+ rb_define_method(rb_cHash, "[]", rb_hash_aref, 1);
+ rb_define_method(rb_cHash, "hash", rb_hash_hash, 0);
+ rb_define_method(rb_cHash, "eql?", rb_hash_eql, 1);
+ rb_define_method(rb_cHash, "fetch", rb_hash_fetch_m, -1);
+ rb_define_method(rb_cHash, "[]=", rb_hash_aset, 2);
+ rb_define_method(rb_cHash, "store", rb_hash_aset, 2);
+ rb_define_method(rb_cHash, "default", rb_hash_default, -1);
+ rb_define_method(rb_cHash, "default=", rb_hash_set_default, 1);
+ rb_define_method(rb_cHash, "default_proc", rb_hash_default_proc, 0);
+ rb_define_method(rb_cHash, "default_proc=", rb_hash_set_default_proc, 1);
+ rb_define_method(rb_cHash, "key", rb_hash_key, 1);
+ rb_define_method(rb_cHash, "index", rb_hash_index, 1);
+ rb_define_method(rb_cHash, "size", rb_hash_size, 0);
+ rb_define_method(rb_cHash, "length", rb_hash_size, 0);
+ rb_define_method(rb_cHash, "empty?", rb_hash_empty_p, 0);
+
+ rb_define_method(rb_cHash, "each_value", rb_hash_each_value, 0);
+ rb_define_method(rb_cHash, "each_key", rb_hash_each_key, 0);
+ rb_define_method(rb_cHash, "each_pair", rb_hash_each_pair, 0);
+ rb_define_method(rb_cHash, "each", rb_hash_each_pair, 0);
+
+ rb_define_method(rb_cHash, "transform_keys", rb_hash_transform_keys, 0);
+ rb_define_method(rb_cHash, "transform_keys!", rb_hash_transform_keys_bang, 0);
rb_define_method(rb_cHash, "transform_values", rb_hash_transform_values, 0);
rb_define_method(rb_cHash, "transform_values!", rb_hash_transform_values_bang, 0);
- rb_define_method(rb_cHash,"keys", rb_hash_keys, 0);
- rb_define_method(rb_cHash,"values", rb_hash_values, 0);
- rb_define_method(rb_cHash,"values_at", rb_hash_values_at, -1);
- rb_define_method(rb_cHash,"fetch_values", rb_hash_fetch_values, -1);
-
- rb_define_method(rb_cHash,"shift", rb_hash_shift, 0);
- rb_define_method(rb_cHash,"delete", rb_hash_delete_m, 1);
- rb_define_method(rb_cHash,"delete_if", rb_hash_delete_if, 0);
- rb_define_method(rb_cHash,"keep_if", rb_hash_keep_if, 0);
- rb_define_method(rb_cHash,"select", rb_hash_select, 0);
- rb_define_method(rb_cHash,"select!", rb_hash_select_bang, 0);
- rb_define_method(rb_cHash,"reject", rb_hash_reject, 0);
- rb_define_method(rb_cHash,"reject!", rb_hash_reject_bang, 0);
- rb_define_method(rb_cHash,"clear", rb_hash_clear, 0);
- rb_define_method(rb_cHash,"invert", rb_hash_invert, 0);
- rb_define_method(rb_cHash,"update", rb_hash_update, 1);
- rb_define_method(rb_cHash,"replace", rb_hash_replace, 1);
- rb_define_method(rb_cHash,"merge!", rb_hash_update, 1);
- rb_define_method(rb_cHash,"merge", rb_hash_merge, 1);
+ rb_define_method(rb_cHash, "keys", rb_hash_keys, 0);
+ rb_define_method(rb_cHash, "values", rb_hash_values, 0);
+ rb_define_method(rb_cHash, "values_at", rb_hash_values_at, -1);
+ rb_define_method(rb_cHash, "fetch_values", rb_hash_fetch_values, -1);
+
+ rb_define_method(rb_cHash, "shift", rb_hash_shift, 0);
+ rb_define_method(rb_cHash, "delete", rb_hash_delete_m, 1);
+ rb_define_method(rb_cHash, "delete_if", rb_hash_delete_if, 0);
+ rb_define_method(rb_cHash, "keep_if", rb_hash_keep_if, 0);
+ rb_define_method(rb_cHash, "select", rb_hash_select, 0);
+ rb_define_method(rb_cHash, "select!", rb_hash_select_bang, 0);
+ rb_define_method(rb_cHash, "filter", rb_hash_select, 0);
+ rb_define_method(rb_cHash, "filter!", rb_hash_select_bang, 0);
+ rb_define_method(rb_cHash, "reject", rb_hash_reject, 0);
+ rb_define_method(rb_cHash, "reject!", rb_hash_reject_bang, 0);
+ rb_define_method(rb_cHash, "slice", rb_hash_slice, -1);
+ rb_define_method(rb_cHash, "clear", rb_hash_clear, 0);
+ rb_define_method(rb_cHash, "invert", rb_hash_invert, 0);
+ rb_define_method(rb_cHash, "update", rb_hash_update, -1);
+ rb_define_method(rb_cHash, "replace", rb_hash_replace, 1);
+ rb_define_method(rb_cHash, "merge!", rb_hash_update, -1);
+ rb_define_method(rb_cHash, "merge", rb_hash_merge, -1);
rb_define_method(rb_cHash, "assoc", rb_hash_assoc, 1);
rb_define_method(rb_cHash, "rassoc", rb_hash_rassoc, 1);
rb_define_method(rb_cHash, "flatten", rb_hash_flatten, -1);
- rb_define_method(rb_cHash,"compact", rb_hash_compact, 0);
- rb_define_method(rb_cHash,"compact!", rb_hash_compact_bang, 0);
+ rb_define_method(rb_cHash, "compact", rb_hash_compact, 0);
+ rb_define_method(rb_cHash, "compact!", rb_hash_compact_bang, 0);
- rb_define_method(rb_cHash,"include?", rb_hash_has_key, 1);
- rb_define_method(rb_cHash,"member?", rb_hash_has_key, 1);
- rb_define_method(rb_cHash,"has_key?", rb_hash_has_key, 1);
- rb_define_method(rb_cHash,"has_value?", rb_hash_has_value, 1);
- rb_define_method(rb_cHash,"key?", rb_hash_has_key, 1);
- rb_define_method(rb_cHash,"value?", rb_hash_has_value, 1);
+ rb_define_method(rb_cHash, "include?", rb_hash_has_key, 1);
+ rb_define_method(rb_cHash, "member?", rb_hash_has_key, 1);
+ rb_define_method(rb_cHash, "has_key?", rb_hash_has_key, 1);
+ rb_define_method(rb_cHash, "has_value?", rb_hash_has_value, 1);
+ rb_define_method(rb_cHash, "key?", rb_hash_has_key, 1);
+ rb_define_method(rb_cHash, "value?", rb_hash_has_value, 1);
- rb_define_method(rb_cHash,"compare_by_identity", rb_hash_compare_by_id, 0);
- rb_define_method(rb_cHash,"compare_by_identity?", rb_hash_compare_by_id_p, 0);
+ rb_define_method(rb_cHash, "compare_by_identity", rb_hash_compare_by_id, 0);
+ rb_define_method(rb_cHash, "compare_by_identity?", rb_hash_compare_by_id_p, 0);
- rb_define_method(rb_cHash, "any?", rb_hash_any_p, 0);
+ rb_define_method(rb_cHash, "any?", rb_hash_any_p, -1);
rb_define_method(rb_cHash, "dig", rb_hash_dig, -1);
rb_define_method(rb_cHash, "<=", rb_hash_le, 1);
@@ -4571,48 +5963,51 @@ Init_Hash(void)
envtbl = rb_obj_alloc(rb_cObject);
rb_extend_object(envtbl, rb_mEnumerable);
- rb_define_singleton_method(envtbl,"[]", rb_f_getenv, 1);
- rb_define_singleton_method(envtbl,"fetch", env_fetch, -1);
- rb_define_singleton_method(envtbl,"[]=", env_aset, 2);
- rb_define_singleton_method(envtbl,"store", env_aset, 2);
- rb_define_singleton_method(envtbl,"each", env_each_pair, 0);
- rb_define_singleton_method(envtbl,"each_pair", env_each_pair, 0);
- rb_define_singleton_method(envtbl,"each_key", env_each_key, 0);
- rb_define_singleton_method(envtbl,"each_value", env_each_value, 0);
- rb_define_singleton_method(envtbl,"delete", env_delete_m, 1);
- rb_define_singleton_method(envtbl,"delete_if", env_delete_if, 0);
- rb_define_singleton_method(envtbl,"keep_if", env_keep_if, 0);
- rb_define_singleton_method(envtbl,"clear", rb_env_clear, 0);
- rb_define_singleton_method(envtbl,"reject", env_reject, 0);
- rb_define_singleton_method(envtbl,"reject!", env_reject_bang, 0);
- rb_define_singleton_method(envtbl,"select", env_select, 0);
- rb_define_singleton_method(envtbl,"select!", env_select_bang, 0);
- rb_define_singleton_method(envtbl,"shift", env_shift, 0);
- rb_define_singleton_method(envtbl,"invert", env_invert, 0);
- rb_define_singleton_method(envtbl,"replace", env_replace, 1);
- rb_define_singleton_method(envtbl,"update", env_update, 1);
- rb_define_singleton_method(envtbl,"inspect", env_inspect, 0);
- rb_define_singleton_method(envtbl,"rehash", env_none, 0);
- rb_define_singleton_method(envtbl,"to_a", env_to_a, 0);
- rb_define_singleton_method(envtbl,"to_s", env_to_s, 0);
- rb_define_singleton_method(envtbl,"key", env_key, 1);
- rb_define_singleton_method(envtbl,"index", env_index, 1);
- rb_define_singleton_method(envtbl,"size", env_size, 0);
- rb_define_singleton_method(envtbl,"length", env_size, 0);
- rb_define_singleton_method(envtbl,"empty?", env_empty_p, 0);
- rb_define_singleton_method(envtbl,"keys", env_keys, 0);
- rb_define_singleton_method(envtbl,"values", env_values, 0);
- rb_define_singleton_method(envtbl,"values_at", env_values_at, -1);
- rb_define_singleton_method(envtbl,"include?", env_has_key, 1);
- rb_define_singleton_method(envtbl,"member?", env_has_key, 1);
- rb_define_singleton_method(envtbl,"has_key?", env_has_key, 1);
- rb_define_singleton_method(envtbl,"has_value?", env_has_value, 1);
- rb_define_singleton_method(envtbl,"key?", env_has_key, 1);
- rb_define_singleton_method(envtbl,"value?", env_has_value, 1);
- rb_define_singleton_method(envtbl,"to_hash", env_to_hash, 0);
- rb_define_singleton_method(envtbl,"to_h", env_to_hash, 0);
- rb_define_singleton_method(envtbl,"assoc", env_assoc, 1);
- rb_define_singleton_method(envtbl,"rassoc", env_rassoc, 1);
+ rb_define_singleton_method(envtbl, "[]", rb_f_getenv, 1);
+ rb_define_singleton_method(envtbl, "fetch", env_fetch, -1);
+ rb_define_singleton_method(envtbl, "[]=", env_aset, 2);
+ rb_define_singleton_method(envtbl, "store", env_aset, 2);
+ rb_define_singleton_method(envtbl, "each", env_each_pair, 0);
+ rb_define_singleton_method(envtbl, "each_pair", env_each_pair, 0);
+ rb_define_singleton_method(envtbl, "each_key", env_each_key, 0);
+ rb_define_singleton_method(envtbl, "each_value", env_each_value, 0);
+ rb_define_singleton_method(envtbl, "delete", env_delete_m, 1);
+ rb_define_singleton_method(envtbl, "delete_if", env_delete_if, 0);
+ rb_define_singleton_method(envtbl, "keep_if", env_keep_if, 0);
+ rb_define_singleton_method(envtbl, "slice", env_slice, -1);
+ rb_define_singleton_method(envtbl, "clear", rb_env_clear, 0);
+ rb_define_singleton_method(envtbl, "reject", env_reject, 0);
+ rb_define_singleton_method(envtbl, "reject!", env_reject_bang, 0);
+ rb_define_singleton_method(envtbl, "select", env_select, 0);
+ rb_define_singleton_method(envtbl, "select!", env_select_bang, 0);
+ rb_define_singleton_method(envtbl, "filter", env_select, 0);
+ rb_define_singleton_method(envtbl, "filter!", env_select_bang, 0);
+ rb_define_singleton_method(envtbl, "shift", env_shift, 0);
+ rb_define_singleton_method(envtbl, "invert", env_invert, 0);
+ rb_define_singleton_method(envtbl, "replace", env_replace, 1);
+ rb_define_singleton_method(envtbl, "update", env_update, 1);
+ rb_define_singleton_method(envtbl, "inspect", env_inspect, 0);
+ rb_define_singleton_method(envtbl, "rehash", env_none, 0);
+ rb_define_singleton_method(envtbl, "to_a", env_to_a, 0);
+ rb_define_singleton_method(envtbl, "to_s", env_to_s, 0);
+ rb_define_singleton_method(envtbl, "key", env_key, 1);
+ rb_define_singleton_method(envtbl, "index", env_index, 1);
+ rb_define_singleton_method(envtbl, "size", env_size, 0);
+ rb_define_singleton_method(envtbl, "length", env_size, 0);
+ rb_define_singleton_method(envtbl, "empty?", env_empty_p, 0);
+ rb_define_singleton_method(envtbl, "keys", env_keys, 0);
+ rb_define_singleton_method(envtbl, "values", env_values, 0);
+ rb_define_singleton_method(envtbl, "values_at", env_values_at, -1);
+ rb_define_singleton_method(envtbl, "include?", env_has_key, 1);
+ rb_define_singleton_method(envtbl, "member?", env_has_key, 1);
+ rb_define_singleton_method(envtbl, "has_key?", env_has_key, 1);
+ rb_define_singleton_method(envtbl, "has_value?", env_has_value, 1);
+ rb_define_singleton_method(envtbl, "key?", env_has_key, 1);
+ rb_define_singleton_method(envtbl, "value?", env_has_value, 1);
+ rb_define_singleton_method(envtbl, "to_hash", env_to_hash, 0);
+ rb_define_singleton_method(envtbl, "to_h", env_to_h, 0);
+ rb_define_singleton_method(envtbl, "assoc", env_assoc, 1);
+ rb_define_singleton_method(envtbl, "rassoc", env_rassoc, 1);
/*
* ENV is a Hash-like accessor for environment variables.
diff --git a/hrtime.h b/hrtime.h
new file mode 100644
index 0000000000..f133bdb1ac
--- /dev/null
+++ b/hrtime.h
@@ -0,0 +1,168 @@
+#ifndef RB_HRTIME_H
+#define RB_HRTIME_H
+#include "ruby/ruby.h"
+#include <time.h>
+#if defined(HAVE_SYS_TIME_H)
+# include <sys/time.h>
+#endif
+
+/*
+ * Hi-res monotonic clock. It is currently nsec resolution, which has over
+ * 500 years of range (with an unsigned 64-bit integer). Developers
+ * targeting small systems may try 32-bit and low-resolution (milliseconds).
+ *
+ * TBD: Is nsec even necessary? usec resolution seems enough for userspace
+ * and it'll be suitable for use with devices lasting over 500,000 years
+ * (maybe some devices designed for long-term space travel)
+ *
+ * Current API:
+ *
+ * * rb_hrtime_now - current clock value (monotonic if available)
+ * * rb_hrtime_mul - multiply with overflow check
+ * * rb_hrtime_add - add with overflow check
+ * * rb_timeval2hrtime - convert from timeval
+ * * rb_timespec2hrtime - convert from timespec
+ * * rb_msec2hrtime - convert from millisecond
+ * * rb_sec2hrtime - convert from time_t (seconds)
+ * * rb_hrtime2timeval - convert to timeval
+ * * rb_hrtime2timespec - convert to timespec
+ *
+ * Note: no conversion to milliseconds is provided here because different
+ * functions have different limits (e.g. epoll_wait vs w32_wait_events).
+ * So we provide RB_HRTIME_PER_MSEC and similar macros for implementing
+ * this for each use case.
+ */
+#define RB_HRTIME_PER_USEC ((rb_hrtime_t)1000)
+#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
+
+/*
+ * Lets try to support time travelers. Lets assume anybody with a time machine
+ * also has access to a modern gcc or clang with 128-bit int support
+ */
+#ifdef MY_RUBY_BUILD_MAY_TIME_TRAVEL
+typedef int128_t rb_hrtime_t;
+#else
+typedef uint64_t rb_hrtime_t;
+#endif
+
+/* thread.c */
+/* returns the value of the monotonic clock (if available) */
+rb_hrtime_t rb_hrtime_now(void);
+
+/*
+ * multiply @a and @b with overflow check and return the
+ * (clamped to RB_HRTIME_MAX) result.
+ */
+static inline rb_hrtime_t
+rb_hrtime_mul(rb_hrtime_t a, rb_hrtime_t b)
+{
+ rb_hrtime_t c;
+
+#ifdef HAVE_BUILTIN___BUILTIN_MUL_OVERFLOW
+ if (__builtin_mul_overflow(a, b, &c))
+ return RB_HRTIME_MAX;
+#else
+ if (b != 0 && a > RB_HRTIME_MAX / b) /* overflow */
+ return RB_HRTIME_MAX;
+ c = a * b;
+#endif
+ return c;
+}
+
+/*
+ * add @a and @b with overflow check and return the
+ * (clamped to RB_HRTIME_MAX) result.
+ */
+static inline rb_hrtime_t
+rb_hrtime_add(rb_hrtime_t a, rb_hrtime_t b)
+{
+ rb_hrtime_t c;
+
+#ifdef HAVE_BUILTIN___BUILTIN_ADD_OVERFLOW
+ if (__builtin_add_overflow(a, b, &c))
+ return RB_HRTIME_MAX;
+#else
+ c = a + b;
+ if (c < a) /* overflow */
+ return RB_HRTIME_MAX;
+#endif
+ return c;
+}
+
+/*
+ * convert a timeval struct to rb_hrtime_t, clamping at RB_HRTIME_MAX
+ */
+static inline rb_hrtime_t
+rb_timeval2hrtime(const struct timeval *tv)
+{
+ rb_hrtime_t s = rb_hrtime_mul((rb_hrtime_t)tv->tv_sec, RB_HRTIME_PER_SEC);
+ rb_hrtime_t u = rb_hrtime_mul((rb_hrtime_t)tv->tv_usec, RB_HRTIME_PER_USEC);
+
+ return rb_hrtime_add(s, u);
+}
+
+/*
+ * convert a timespec struct to rb_hrtime_t, clamping at RB_HRTIME_MAX
+ */
+static inline rb_hrtime_t
+rb_timespec2hrtime(const struct timespec *ts)
+{
+ rb_hrtime_t s = rb_hrtime_mul((rb_hrtime_t)ts->tv_sec, RB_HRTIME_PER_SEC);
+
+ return rb_hrtime_add(s, (rb_hrtime_t)ts->tv_nsec);
+}
+
+/*
+ * convert a millisecond value to rb_hrtime_t, clamping at RB_HRTIME_MAX
+ */
+static inline rb_hrtime_t
+rb_msec2hrtime(unsigned long msec)
+{
+ return rb_hrtime_mul((rb_hrtime_t)msec, RB_HRTIME_PER_MSEC);
+}
+
+/*
+ * convert a time_t value to rb_hrtime_t, clamping at RB_HRTIME_MAX
+ * Negative values will be clamped at 0.
+ */
+static inline rb_hrtime_t
+rb_sec2hrtime(time_t sec)
+{
+ if (sec <= 0) return 0;
+
+ return rb_hrtime_mul((rb_hrtime_t)sec, RB_HRTIME_PER_SEC);
+}
+
+/*
+ * convert a rb_hrtime_t value to a timespec, suitable for calling
+ * functions like ppoll(2) or kevent(2)
+ */
+static inline struct timespec *
+rb_hrtime2timespec(struct timespec *ts, const rb_hrtime_t *hrt)
+{
+ if (hrt) {
+ ts->tv_sec = (time_t)(*hrt / RB_HRTIME_PER_SEC);
+ ts->tv_nsec = (int32_t)(*hrt % RB_HRTIME_PER_SEC);
+ return ts;
+ }
+ return 0;
+}
+
+/*
+ * convert a rb_hrtime_t value to a timeval, suitable for calling
+ * functions like select(2)
+ */
+static inline struct timeval *
+rb_hrtime2timeval(struct timeval *tv, const rb_hrtime_t *hrt)
+{
+ if (hrt) {
+ tv->tv_sec = (time_t)(*hrt / RB_HRTIME_PER_SEC);
+ tv->tv_usec = (int32_t)((*hrt % RB_HRTIME_PER_SEC)/RB_HRTIME_PER_USEC);
+
+ return tv;
+ }
+ return 0;
+}
+#endif /* RB_HRTIME_H */
diff --git a/ia64.s b/ia64.S
index 1087105585..1087105585 100644
--- a/ia64.s
+++ b/ia64.S
diff --git a/id_table.c b/id_table.c
index f0f37cc913..74c9e756a0 100644
--- a/id_table.c
+++ b/id_table.c
@@ -11,205 +11,8 @@
#endif
#include "ruby_assert.h"
-/*
- * st
- * 0: using st with debug information.
- * 1: using st.
- * array
- * 11: simple array. ids = [ID1, ID2, ...], values = [val1, val2, ...]
- * 12: simple array, and use rb_id_serial_t instead of ID.
- * 13: simple array, and use rb_id_serial_t instead of ID. Swap recent access.
- * 14: sorted array, and use rb_id_serial_t instead of ID.
- * 15: sorted array, and use rb_id_serial_t instead of ID, linear small part.
- * hash
- * 21: funny falcon's Coalesced Hashing implementation [Feature #6962]
- * 22: simple open addressing with quadratic probing.
- * mix (array + hash)
- * 31: array(12) (capa <= 32) + hash(22)
- * 32: array(14) (capa <= 32) + hash(22)
- * 33: array(12) (capa <= 64) + hash(22)
- * 34: array(14) (capa <= 64) + hash(22)
- * 34: array(15) (capa <= 64) + hash(22)
- */
-
-#ifndef ID_TABLE_IMPL
-#define ID_TABLE_IMPL 34
-#endif
-
-#if ID_TABLE_IMPL == 0
-#define ID_TABLE_NAME st
-#define ID_TABLE_IMPL_TYPE struct st_id_table
-
-#define ID_TABLE_USE_ST 1
-#define ID_TABLE_USE_ST_DEBUG 1
-
-#elif ID_TABLE_IMPL == 1
-#define ID_TABLE_NAME st
-#define ID_TABLE_IMPL_TYPE struct st_id_table
-
-#define ID_TABLE_USE_ST 1
-#define ID_TABLE_USE_ST_DEBUG 0
-
-#elif ID_TABLE_IMPL == 11
-#define ID_TABLE_NAME list
-#define ID_TABLE_IMPL_TYPE struct list_id_table
-
-#define ID_TABLE_USE_LIST 1
-#define ID_TABLE_USE_CALC_VALUES 1
-
-#elif ID_TABLE_IMPL == 12
-#define ID_TABLE_NAME list
-#define ID_TABLE_IMPL_TYPE struct list_id_table
-
-#define ID_TABLE_USE_LIST 1
-#define ID_TABLE_USE_CALC_VALUES 1
-#define ID_TABLE_USE_ID_SERIAL 1
-
-#elif ID_TABLE_IMPL == 13
-#define ID_TABLE_NAME list
-#define ID_TABLE_IMPL_TYPE struct list_id_table
-
-#define ID_TABLE_USE_LIST 1
-#define ID_TABLE_USE_CALC_VALUES 1
-#define ID_TABLE_USE_ID_SERIAL 1
-#define ID_TABLE_SWAP_RECENT_ACCESS 1
-
-#elif ID_TABLE_IMPL == 14
-#define ID_TABLE_NAME list
-#define ID_TABLE_IMPL_TYPE struct list_id_table
-
-#define ID_TABLE_USE_LIST 1
-#define ID_TABLE_USE_CALC_VALUES 1
-#define ID_TABLE_USE_ID_SERIAL 1
-#define ID_TABLE_USE_LIST_SORTED 1
-
-#elif ID_TABLE_IMPL == 15
-#define ID_TABLE_NAME list
-#define ID_TABLE_IMPL_TYPE struct list_id_table
-
-#define ID_TABLE_USE_LIST 1
-#define ID_TABLE_USE_CALC_VALUES 1
-#define ID_TABLE_USE_ID_SERIAL 1
-#define ID_TABLE_USE_LIST_SORTED 1
-#define ID_TABLE_USE_LIST_SORTED_LINEAR_SMALL_RANGE 1
-
-#elif ID_TABLE_IMPL == 21
-#define ID_TABLE_NAME hash
-#define ID_TABLE_IMPL_TYPE sa_table
-
-#define ID_TABLE_USE_COALESCED_HASHING 1
-#define ID_TABLE_USE_ID_SERIAL 1
-
-#elif ID_TABLE_IMPL == 22
-#define ID_TABLE_NAME hash
-#define ID_TABLE_IMPL_TYPE struct hash_id_table
-
-#define ID_TABLE_USE_SMALL_HASH 1
-#define ID_TABLE_USE_ID_SERIAL 1
-
-#elif ID_TABLE_IMPL == 31
-#define ID_TABLE_NAME mix
-#define ID_TABLE_IMPL_TYPE struct mix_id_table
-
-#define ID_TABLE_USE_MIX 1
-#define ID_TABLE_USE_MIX_LIST_MAX_CAPA 32
-
-#define ID_TABLE_USE_ID_SERIAL 1
-
-#define ID_TABLE_USE_LIST 1
-#define ID_TABLE_USE_CALC_VALUES 1
-#define ID_TABLE_USE_SMALL_HASH 1
-
-#elif ID_TABLE_IMPL == 32
-#define ID_TABLE_NAME mix
-#define ID_TABLE_IMPL_TYPE struct mix_id_table
-
-#define ID_TABLE_USE_MIX 1
-#define ID_TABLE_USE_MIX_LIST_MAX_CAPA 32
-
-#define ID_TABLE_USE_ID_SERIAL 1
-
-#define ID_TABLE_USE_LIST 1
-#define ID_TABLE_USE_CALC_VALUES 1
-#define ID_TABLE_USE_LIST_SORTED 1
-
-#define ID_TABLE_USE_SMALL_HASH 1
-
-#elif ID_TABLE_IMPL == 33
-#define ID_TABLE_NAME mix
-#define ID_TABLE_IMPL_TYPE struct mix_id_table
-
-#define ID_TABLE_USE_MIX 1
-#define ID_TABLE_USE_MIX_LIST_MAX_CAPA 64
-
-#define ID_TABLE_USE_ID_SERIAL 1
-
-#define ID_TABLE_USE_LIST 1
-#define ID_TABLE_USE_CALC_VALUES 1
-#define ID_TABLE_USE_SMALL_HASH 1
-
-#elif ID_TABLE_IMPL == 34
-#define ID_TABLE_NAME mix
-#define ID_TABLE_IMPL_TYPE struct mix_id_table
-
-#define ID_TABLE_USE_MIX 1
-#define ID_TABLE_USE_MIX_LIST_MAX_CAPA 64
-
-#define ID_TABLE_USE_ID_SERIAL 1
-
-#define ID_TABLE_USE_LIST 1
-#define ID_TABLE_USE_CALC_VALUES 1
-#define ID_TABLE_USE_LIST_SORTED 1
-
-#define ID_TABLE_USE_SMALL_HASH 1
-
-#elif ID_TABLE_IMPL == 35
-#define ID_TABLE_NAME mix
-#define ID_TABLE_IMPL_TYPE struct mix_id_table
-
-#define ID_TABLE_USE_MIX 1
-#define ID_TABLE_USE_MIX_LIST_MAX_CAPA 64
-
-#define ID_TABLE_USE_ID_SERIAL 1
-
-#define ID_TABLE_USE_LIST 1
-#define ID_TABLE_USE_CALC_VALUES 1
-#define ID_TABLE_USE_LIST_SORTED 1
-#define ID_TABLE_USE_LIST_SORTED_LINEAR_SMALL_RANGE 1
-
-#define ID_TABLE_USE_SMALL_HASH 1
-
-#else
-#error
-#endif
-
-#if ID_TABLE_SWAP_RECENT_ACCESS && ID_TABLE_USE_LIST_SORTED
-#error
-#endif
-
-/* IMPL(create) will be "hash_id_table_create" and so on */
-#define IMPL1(name, op) TOKEN_PASTE(name, _id##op) /* expand `name' */
-#define IMPL(op) IMPL1(ID_TABLE_NAME, _table##op) /* but prevent `op' */
-
-#ifdef __GNUC__
-# define UNUSED(func) static func __attribute__((unused))
-#else
-# define UNUSED(func) static func
-#endif
-
-UNUSED(ID_TABLE_IMPL_TYPE *IMPL(_create)(size_t));
-UNUSED(void IMPL(_free)(ID_TABLE_IMPL_TYPE *));
-UNUSED(void IMPL(_clear)(ID_TABLE_IMPL_TYPE *));
-UNUSED(size_t IMPL(_size)(const ID_TABLE_IMPL_TYPE *));
-UNUSED(size_t IMPL(_memsize)(const ID_TABLE_IMPL_TYPE *));
-UNUSED(int IMPL(_insert)(ID_TABLE_IMPL_TYPE *, ID, VALUE));
-UNUSED(int IMPL(_lookup)(ID_TABLE_IMPL_TYPE *, ID, VALUE *));
-UNUSED(int IMPL(_delete)(ID_TABLE_IMPL_TYPE *, ID));
-UNUSED(void IMPL(_foreach)(ID_TABLE_IMPL_TYPE *, rb_id_table_foreach_func_t *, void *));
-UNUSED(void IMPL(_foreach_values)(ID_TABLE_IMPL_TYPE *, rb_id_table_foreach_values_func_t *, void *));
-
-#if ID_TABLE_USE_ID_SERIAL
typedef rb_id_serial_t id_key_t;
+
static inline ID
key2id(id_key_t key)
{
@@ -221,932 +24,7 @@ id2key(ID id)
{
return rb_id_to_serial(id);
}
-#else /* ID_TABLE_USE_ID_SERIAL */
-
-typedef ID id_key_t;
-#define key2id(key) key
-#define id2key(id) id
-
-#endif /* ID_TABLE_USE_ID_SERIAL */
-
-/***************************************************************
- * 0: using st with debug information.
- * 1: using st.
- ***************************************************************/
-#if ID_TABLE_USE_ST
-#if ID_TABLE_USE_ST_DEBUG
-#define ID_TABLE_MARK 0x12345678
-
-struct st_id_table {
- struct st_table *st;
- unsigned int check;
-};
-
-static struct st_table *
-tbl2st(struct st_id_table *tbl)
-{
- if (tbl->check != ID_TABLE_MARK) rb_bug("tbl2st: check error %x", tbl->check);
- return tbl->st;
-}
-
-static struct st_id_table *
-st_id_table_create(size_t size)
-{
- struct st_id_table *tbl = ALLOC(struct st_id_table);
- tbl->st = st_init_numtable_with_size(size);
- tbl->check = ID_TABLE_MARK;
- return tbl;
-}
-
-static void
-st_id_table_free(struct st_id_table *tbl)
-{
- st_free_table(tbl->st);
- xfree(tbl);
-}
-
-#else /* ID_TABLE_USE_ST_DEBUG */
-
-struct st_id_table {
- struct st_table st;
-};
-
-static struct st_table *
-tbl2st(struct st_id_table *tbl)
-{
- return (struct st_table *)tbl;
-}
-
-static struct st_id_table *
-st_id_table_create(size_t size)
-{
- return (struct st_id_table *)st_init_numtable_with_size(size);
-}
-
-static void
-st_id_table_free(struct st_id_table *tbl)
-{
- st_free_table((struct st_table*)tbl);
-}
-
-#endif /* ID_TABLE_USE_ST_DEBUG */
-
-static void
-st_id_table_clear(struct st_id_table *tbl)
-{
- st_clear(tbl2st(tbl));
-}
-
-static size_t
-st_id_table_size(const struct st_id_table *tbl)
-{
- return tbl2st(tbl)->num_entries;
-}
-
-static size_t
-st_id_table_memsize(const struct st_id_table *tbl)
-{
- size_t header_size = ID_TABLE_USE_ST_DEBUG ? sizeof(struct st_id_table) : 0;
- return header_size + st_memsize(tbl2st(tbl));
-}
-
-static int
-st_id_table_lookup(struct st_id_table *tbl, ID id, VALUE *val)
-{
- return st_lookup(tbl2st(tbl), (st_data_t)id, (st_data_t *)val);
-}
-
-static int
-st_id_table_insert(struct st_id_table *tbl, ID id, VALUE val)
-{
- return st_insert(tbl2st(tbl), id, val);
-}
-
-static int
-st_id_table_delete(struct st_id_table *tbl, ID id)
-{
- return st_delete(tbl2st(tbl), (st_data_t *)&id, NULL);
-}
-
-static void
-st_id_table_foreach(struct st_id_table *tbl, rb_id_table_foreach_func_t *func, void *data)
-{
- st_foreach(tbl2st(tbl), (int (*)(ANYARGS))func, (st_data_t)data);
-}
-
-struct values_iter_data {
- rb_id_table_foreach_values_func_t *values_i;
- void *data;
-};
-
-static int
-each_values(st_data_t key, st_data_t val, st_data_t ptr)
-{
- struct values_iter_data *values_iter_data = (struct values_iter_data *)ptr;
- return values_iter_data->values_i(val, values_iter_data->data);
-}
-
-static void
-st_id_table_foreach_values(struct st_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data)
-{
- struct values_iter_data values_iter_data;
- values_iter_data.values_i = func;
- values_iter_data.data = data;
- st_foreach(tbl2st(tbl), each_values, (st_data_t)&values_iter_data);
-}
-#endif /* ID_TABLE_USE_ST */
-
-#if ID_TABLE_USE_LIST
-
-#define LIST_MIN_CAPA 4
-
-struct list_id_table {
- int capa;
- int num;
- id_key_t *keys;
-#if ID_TABLE_USE_CALC_VALUES == 0
- VALUE *values_;
-#endif
-};
-
-#if ID_TABLE_USE_CALC_VALUES
-#define TABLE_VALUES(tbl) ((VALUE *)((tbl)->keys + (tbl)->capa))
-#else
-#define TABLE_VALUES(tbl) (tbl)->values_
-#endif
-
-static struct list_id_table *
-list_id_table_init(struct list_id_table *tbl, size_t capa)
-{
- if (capa > 0) {
-#if ID_TABLE_USE_CALC_VALUES && \
- (UNALIGNED_WORD_ACCESS == 0) && (SIZEOF_VALUE == 8)
- /* Workaround for 8-byte word alignment on 64-bit SPARC.
- * This code assumes that sizeof(ID) == 4, sizeof(VALUE) == 8, and
- * xmalloc() returns 8-byte aligned memory block.
- */
- if (capa & (size_t)1) capa += 1;
-#endif
- tbl->capa = (int)capa;
-#if ID_TABLE_USE_CALC_VALUES
- tbl->keys = (id_key_t *)xmalloc(sizeof(id_key_t) * capa + sizeof(VALUE) * capa);
-#else
- tbl->keys = ALLOC_N(id_key_t, capa);
- tbl->values_ = ALLOC_N(VALUE, capa);
-#endif
- }
- return tbl;
-}
-
-#ifndef ID_TABLE_USE_MIX
-static struct list_id_table *
-list_id_table_create(size_t capa)
-{
- struct list_id_table *tbl = ZALLOC(struct list_id_table);
- return list_id_table_init(tbl, capa);
-}
-#endif
-
-static void
-list_id_table_free(struct list_id_table *tbl)
-{
- xfree(tbl->keys);
-#if ID_TABLE_USE_CALC_VALUES == 0
- xfree(tbl->values_);
-#endif
- xfree(tbl);
-}
-
-static void
-list_id_table_clear(struct list_id_table *tbl)
-{
- tbl->num = 0;
-}
-
-static size_t
-list_id_table_size(const struct list_id_table *tbl)
-{
- return (size_t)tbl->num;
-}
-
-static size_t
-list_id_table_memsize(const struct list_id_table *tbl)
-{
- return (sizeof(id_key_t) + sizeof(VALUE)) * tbl->capa + sizeof(struct list_id_table);
-}
-
-static void
-list_table_extend(struct list_id_table *tbl)
-{
- if (tbl->capa == tbl->num) {
- const int capa = tbl->capa == 0 ? LIST_MIN_CAPA : (tbl->capa * 2);
-
-#if ID_TABLE_USE_CALC_VALUES
- {
- VALUE *old_values, *new_values;
- VALUE *debug_values = NULL;
- const int num = tbl->num;
- const int size = sizeof(id_key_t) * capa + sizeof(VALUE) * capa;
- int i;
-
- if (num > 0) {
- VALUE *orig_values = (VALUE *)(tbl->keys + num);
- debug_values = ALLOC_N(VALUE, num);
-
- for (i=0; i<num; i++) {
- debug_values[i] = orig_values[i];
- }
-
- if (0)
- for (i=0; i< 2 * num; i++) {
- unsigned char *cs = (unsigned char *)&tbl->keys[i];
- size_t j;
- fprintf(stderr, ">> %3d | %p - ", i, cs);
- for (j=0; j<sizeof(VALUE); j++) {
- fprintf(stderr, "%x ", cs[j]);
- }
- fprintf(stderr, "\n");
- }
- }
-
- tbl->keys = (id_key_t *)xrealloc(tbl->keys, size);
- old_values = (VALUE *)(tbl->keys + num);
- new_values = (VALUE *)(tbl->keys + capa);
-
- /* [ keys (num) ] [ values (num) ]
- * ^ old_values
- * realloc =>
- * [ keys (capa = num * 2) ] [ values (capa = num * 2) ]
- * ^ new_values
- */
-
- /* memmove */
- if (0) {
- fprintf(stderr, "memmove: %p -> %p (%d, capa: %d)\n",
- old_values, new_values, num, capa);
- }
- assert(num < capa);
- assert(num == 0 || old_values < new_values);
-
- for (i=num-1; i>=0; i--) {
- new_values[i] = old_values[i];
- }
-
- if (num > 0) {
- for (i=0; i<num; i++) {
- assert(debug_values[i] == new_values[i]);
- }
- xfree(debug_values);
- }
- }
-
- tbl->capa = capa;
-#else
- tbl->capa = capa;
- tbl->keys = (id_key_t *)xrealloc(tbl->keys, sizeof(id_key_t) * capa);
- tbl->values_ = (VALUE *)xrealloc(tbl->values_, sizeof(VALUE) * capa);
-#endif
- }
-}
-
-#if ID_TABLE_DEBUG
-static void
-list_table_show(struct list_id_table *tbl)
-{
- const id_key_t *keys = tbl->keys;
- const int num = tbl->num;
- int i;
-
- fprintf(stderr, "tbl: %p (num: %d)\n", tbl, num);
- for (i=0; i<num; i++) {
- fprintf(stderr, " -> [%d] %s %d\n", i, rb_id2name(key2id(keys[i])), (int)keys[i]);
- }
-}
-#endif
-
-static void
-tbl_assert(struct list_id_table *tbl)
-{
-#if ID_TABLE_DEBUG
-#if ID_TABLE_USE_LIST_SORTED
- const id_key_t *keys = tbl->keys;
- const int num = tbl->num;
- int i;
-
- for (i=0; i<num-1; i++) {
- if (keys[i] >= keys[i+1]) {
- list_table_show(tbl);
- rb_bug(": not sorted.");
- }
- }
-#endif
-#endif
-}
-
-#if ID_TABLE_USE_LIST_SORTED
-static int
-list_ids_bsearch(const id_key_t *keys, id_key_t key, int num)
-{
- int p, min = 0, max = num;
-
-#if ID_TABLE_USE_LIST_SORTED_LINEAR_SMALL_RANGE
- if (num <= 64) {
- if (num > 32) {
- if (keys[num/2] <= key) {
- min = num/2;
- } else {
- max = num/2;
- }
- }
- for (p = min; p<num && keys[p] < key; p++) {
- assert(keys[p] != 0);
- }
- return (p<num && keys[p] == key) ? p : -p-1;
- }
-#endif /* ID_TABLE_USE_LIST_SORTED_LINEAR_SMALL_RANGE */
-
- while (1) {
- p = min + (max - min) / 2;
-
- if (min >= max) {
- break;
- }
- else {
- id_key_t kp = keys[p];
- assert(p < max);
- assert(p >= min);
-
- if (kp > key) max = p;
- else if (kp < key) min = p+1;
- else {
- assert(kp == key);
- assert(p >= 0);
- assert(p < num);
- return p;
- }
- }
- }
-
- assert(min == max);
- assert(min == p);
- return -p-1;
-}
-#endif /* ID_TABLE_USE_LIST_SORTED */
-
-static int
-list_table_index(struct list_id_table *tbl, id_key_t key)
-{
- const int num = tbl->num;
- const id_key_t *keys = tbl->keys;
-
-#if ID_TABLE_USE_LIST_SORTED
- return list_ids_bsearch(keys, key, num);
-#else /* ID_TABLE_USE_LIST_SORTED */
- int i;
-
- for (i=0; i<num; i++) {
- assert(keys[i] != 0);
-
- if (keys[i] == key) {
- return (int)i;
- }
- }
- return -1;
-#endif
-}
-
-static int
-list_id_table_lookup(struct list_id_table *tbl, ID id, VALUE *valp)
-{
- id_key_t key = id2key(id);
- int index = list_table_index(tbl, key);
-
- if (index >= 0) {
- *valp = TABLE_VALUES(tbl)[index];
-
-#if ID_TABLE_SWAP_RECENT_ACCESS
- if (index > 0) {
- VALUE *values = TABLE_VALUES(tbl);
- id_key_t tk = tbl->keys[index-1];
- VALUE tv = values[index-1];
- tbl->keys[index-1] = tbl->keys[index];
- tbl->keys[index] = tk;
- values[index-1] = values[index];
- values[index] = tv;
- }
-#endif /* ID_TABLE_SWAP_RECENT_ACCESS */
- return TRUE;
- }
- else {
- return FALSE;
- }
-}
-
-static int
-list_id_table_insert(struct list_id_table *tbl, ID id, VALUE val)
-{
- const id_key_t key = id2key(id);
- const int index = list_table_index(tbl, key);
-
- if (index >= 0) {
- TABLE_VALUES(tbl)[index] = val;
- }
- else {
- list_table_extend(tbl);
- {
- const int num = tbl->num++;
-#if ID_TABLE_USE_LIST_SORTED
- const int insert_index = -(index + 1);
- id_key_t *keys = tbl->keys;
- VALUE *values = TABLE_VALUES(tbl);
- int i;
-
- if (0) fprintf(stderr, "insert: %d into %d on\n", (int)key, insert_index);
-
- for (i=num; i>insert_index; i--) {
- keys[i] = keys[i-1];
- values[i] = values[i-1];
- }
- keys[i] = key;
- values[i] = val;
-
- tbl_assert(tbl);
-#else
- tbl->keys[num] = key;
- TABLE_VALUES(tbl)[num] = val;
-#endif
- }
- }
-
- return TRUE;
-}
-
-static int
-list_delete_index(struct list_id_table *tbl, id_key_t key, int index)
-{
- if (index >= 0) {
- VALUE *values = TABLE_VALUES(tbl);
-
-#if ID_TABLE_USE_LIST_SORTED
- int i;
- const int num = tbl->num;
- id_key_t *keys = tbl->keys;
-
- for (i=index+1; i<num; i++) { /* compaction */
- keys[i-1] = keys[i];
- values[i-1] = values[i];
- }
-#else
- tbl->keys[index] = tbl->keys[tbl->num-1];
- values[index] = values[tbl->num-1];
-#endif
- tbl->num--;
- tbl_assert(tbl);
-
- return TRUE;
- }
- else {
- return FALSE;
- }
-}
-
-static int
-list_id_table_delete(struct list_id_table *tbl, ID id)
-{
- const id_key_t key = id2key(id);
- int index = list_table_index(tbl, key);
- return list_delete_index(tbl, key, index);
-}
-
-#define FOREACH_LAST() do { \
- switch (ret) { \
- case ID_TABLE_ITERATOR_RESULT_END: \
- case ID_TABLE_CONTINUE: \
- case ID_TABLE_STOP: \
- break; \
- case ID_TABLE_DELETE: \
- list_delete_index(tbl, key, i); \
- values = TABLE_VALUES(tbl); \
- num = tbl->num; \
- i--; /* redo same index */ \
- break; \
- } \
-} while (0)
-
-static void
-list_id_table_foreach(struct list_id_table *tbl, rb_id_table_foreach_func_t *func, void *data)
-{
- int num = tbl->num;
- int i;
- const id_key_t *keys = tbl->keys;
- const VALUE *values = TABLE_VALUES(tbl);
-
- for (i=0; i<num; i++) {
- const id_key_t key = keys[i];
- enum rb_id_table_iterator_result ret = (*func)(key2id(key), values[i], data);
- assert(key != 0);
-
- FOREACH_LAST();
- if (ret == ID_TABLE_STOP) return;
- }
-}
-
-static void
-list_id_table_foreach_values(struct list_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data)
-{
- int num = tbl->num;
- int i;
- const id_key_t *keys = tbl->keys;
- VALUE *values = TABLE_VALUES(tbl);
-
- for (i=0; i<num; i++) {
- const id_key_t key = keys[i];
- enum rb_id_table_iterator_result ret = (*func)(values[i], data);
- assert(key != 0);
-
- FOREACH_LAST();
- if (ret == ID_TABLE_STOP) return;
- }
-}
-#endif /* ID_TABLE_USE_LIST */
-
-
-#if ID_TABLE_USE_COALESCED_HASHING
-/* implementation is based on
- * https://bugs.ruby-lang.org/issues/6962 by funny_falcon
- */
-
-typedef unsigned int sa_index_t;
-
-#define SA_EMPTY 0
-#define SA_LAST 1
-#define SA_OFFSET 2
-#define SA_MIN_SIZE 4
-
-typedef struct sa_entry {
- sa_index_t next;
- id_key_t key;
- VALUE value;
-} sa_entry;
-
-typedef struct {
- sa_index_t num_bins;
- sa_index_t num_entries;
- sa_index_t free_pos;
- sa_entry *entries;
-} sa_table;
-
-static void
-sa_init_table(register sa_table *table, sa_index_t num_bins)
-{
- if (num_bins) {
- table->num_entries = 0;
- table->entries = ZALLOC_N(sa_entry, num_bins);
- table->num_bins = num_bins;
- table->free_pos = num_bins;
- }
-}
-
-static sa_table*
-hash_id_table_create(size_t size)
-{
- sa_table* table = ZALLOC(sa_table);
- sa_init_table(table, (sa_index_t)size);
- return table;
-}
-
-static void
-hash_id_table_clear(sa_table *table)
-{
- xfree(table->entries);
- memset(table, 0, sizeof(sa_table));
-}
-
-static void
-hash_id_table_free(sa_table *table)
-{
- xfree(table->entries);
- xfree(table);
-}
-
-static size_t
-hash_id_table_memsize(const sa_table *table)
-{
- return sizeof(sa_table) + table->num_bins * sizeof (sa_entry);
-}
-
-static inline sa_index_t
-calc_pos(register sa_table* table, id_key_t key)
-{
- return key & (table->num_bins - 1);
-}
-
-static void
-fix_empty(register sa_table* table)
-{
- while (--table->free_pos &&
- table->entries[table->free_pos-1].next != SA_EMPTY);
-}
-
-#define FLOOR_TO_4 ((~((sa_index_t)0)) << 2)
-static sa_index_t
-find_empty(register sa_table* table, register sa_index_t pos)
-{
- sa_index_t new_pos = table->free_pos-1;
- sa_entry *entry;
- static const unsigned offsets[][3] = {
- {1, 2, 3},
- {2, 3, 0},
- {3, 1, 0},
- {2, 1, 0}
- };
- const unsigned *const check = offsets[pos&3];
- pos &= FLOOR_TO_4;
- entry = table->entries+pos;
-
- if (entry[check[0]].next == SA_EMPTY) { new_pos = pos + check[0]; goto check; }
- if (entry[check[1]].next == SA_EMPTY) { new_pos = pos + check[1]; goto check; }
- if (entry[check[2]].next == SA_EMPTY) { new_pos = pos + check[2]; goto check; }
-
- check:
- if (new_pos+1 == table->free_pos) fix_empty(table);
- return new_pos;
-}
-
-static void resize(register sa_table* table);
-static int insert_into_chain(register sa_table*, register id_key_t, st_data_t, sa_index_t pos);
-static int insert_into_main(register sa_table*, id_key_t, st_data_t, sa_index_t pos, sa_index_t prev_pos);
-
-static int
-sa_insert(register sa_table* table, id_key_t key, VALUE value)
-{
- register sa_entry *entry;
- sa_index_t pos, main_pos;
-
- if (table->num_bins == 0) {
- sa_init_table(table, SA_MIN_SIZE);
- }
-
- pos = calc_pos(table, key);
- entry = table->entries + pos;
-
- if (entry->next == SA_EMPTY) {
- entry->next = SA_LAST;
- entry->key = key;
- entry->value = value;
- table->num_entries++;
- if (pos+1 == table->free_pos) fix_empty(table);
- return 0;
- }
-
- if (entry->key == key) {
- entry->value = value;
- return 1;
- }
-
- if (table->num_entries + (table->num_entries >> 2) > table->num_bins) {
- resize(table);
- return sa_insert(table, key, value);
- }
-
- main_pos = calc_pos(table, entry->key);
- if (main_pos == pos) {
- return insert_into_chain(table, key, value, pos);
- }
- else {
- if (!table->free_pos) {
- resize(table);
- return sa_insert(table, key, value);
- }
- return insert_into_main(table, key, value, pos, main_pos);
- }
-}
-
-static int
-hash_id_table_insert(register sa_table* table, ID id, VALUE value)
-{
- return sa_insert(table, id2key(id), value);
-}
-
-static int
-insert_into_chain(register sa_table* table, id_key_t key, st_data_t value, sa_index_t pos)
-{
- sa_entry *entry = table->entries + pos, *new_entry;
- sa_index_t new_pos;
-
- while (entry->next != SA_LAST) {
- pos = entry->next - SA_OFFSET;
- entry = table->entries + pos;
- if (entry->key == key) {
- entry->value = value;
- return 1;
- }
- }
-
- if (!table->free_pos) {
- resize(table);
- return sa_insert(table, key, value);
- }
-
- new_pos = find_empty(table, pos);
- new_entry = table->entries + new_pos;
- entry->next = new_pos + SA_OFFSET;
-
- new_entry->next = SA_LAST;
- new_entry->key = key;
- new_entry->value = value;
- table->num_entries++;
- return 0;
-}
-
-static int
-insert_into_main(register sa_table* table, id_key_t key, st_data_t value, sa_index_t pos, sa_index_t prev_pos)
-{
- sa_entry *entry = table->entries + pos;
- sa_index_t new_pos = find_empty(table, pos);
- sa_entry *new_entry = table->entries + new_pos;
- sa_index_t npos;
-
- *new_entry = *entry;
-
- while((npos = table->entries[prev_pos].next - SA_OFFSET) != pos) {
- prev_pos = npos;
- }
- table->entries[prev_pos].next = new_pos + SA_OFFSET;
-
- entry->next = SA_LAST;
- entry->key = key;
- entry->value = value;
- table->num_entries++;
- return 0;
-}
-static sa_index_t
-new_size(sa_index_t num_entries)
-{
- sa_index_t size = num_entries >> 3;
- size |= size >> 1;
- size |= size >> 2;
- size |= size >> 4;
- size |= size >> 8;
- size |= size >> 16;
- return (size + 1) << 3;
-}
-
-static void
-resize(register sa_table *table)
-{
- sa_table tmp_table;
- sa_entry *entry;
- sa_index_t i;
-
- if (table->num_entries == 0) {
- xfree(table->entries);
- memset(table, 0, sizeof(sa_table));
- return;
- }
-
- sa_init_table(&tmp_table, new_size(table->num_entries + (table->num_entries >> 2)));
- entry = table->entries;
-
- for(i = 0; i < table->num_bins; i++, entry++) {
- if (entry->next != SA_EMPTY) {
- sa_insert(&tmp_table, entry->key, entry->value);
- }
- }
- xfree(table->entries);
- *table = tmp_table;
-}
-
-static int
-hash_id_table_lookup(register sa_table *table, ID id, VALUE *valuep)
-{
- register sa_entry *entry;
- id_key_t key = id2key(id);
-
- if (table->num_entries == 0) return 0;
-
- entry = table->entries + calc_pos(table, key);
- if (entry->next == SA_EMPTY) return 0;
-
- if (entry->key == key) goto found;
- if (entry->next == SA_LAST) return 0;
-
- entry = table->entries + (entry->next - SA_OFFSET);
- if (entry->key == key) goto found;
-
- while(entry->next != SA_LAST) {
- entry = table->entries + (entry->next - SA_OFFSET);
- if (entry->key == key) goto found;
- }
- return 0;
- found:
- if (valuep) *valuep = entry->value;
- return 1;
-}
-
-static size_t
-hash_id_table_size(const sa_table *table)
-{
- return table->num_entries;
-}
-
-static int
-hash_id_table_delete(sa_table *table, ID id)
-{
- sa_index_t pos, prev_pos = ~0;
- sa_entry *entry;
- id_key_t key = id2key(id);
-
- if (table->num_entries == 0) goto not_found;
-
- pos = calc_pos(table, key);
- entry = table->entries + pos;
-
- if (entry->next == SA_EMPTY) goto not_found;
-
- do {
- if (entry->key == key) {
- if (entry->next != SA_LAST) {
- sa_index_t npos = entry->next - SA_OFFSET;
- *entry = table->entries[npos];
- memset(table->entries + npos, 0, sizeof(sa_entry));
- }
- else {
- memset(table->entries + pos, 0, sizeof(sa_entry));
- if (~prev_pos) {
- table->entries[prev_pos].next = SA_LAST;
- }
- }
- table->num_entries--;
- if (table->num_entries < table->num_bins / 4) {
- resize(table);
- }
- return 1;
- }
- if (entry->next == SA_LAST) break;
- prev_pos = pos;
- pos = entry->next - SA_OFFSET;
- entry = table->entries + pos;
- } while(1);
-
- not_found:
- return 0;
-}
-
-enum foreach_type {
- foreach_key_values,
- foreach_values
-};
-
-static void
-hash_foreach(sa_table *table, enum rb_id_table_iterator_result (*func)(ANYARGS), void *arg, enum foreach_type type)
-{
- sa_index_t i;
-
- if (table->num_bins > 0) {
- for(i = 0; i < table->num_bins ; i++) {
- if (table->entries[i].next != SA_EMPTY) {
- id_key_t key = table->entries[i].key;
- st_data_t val = table->entries[i].value;
- enum rb_id_table_iterator_result ret;
-
- switch (type) {
- case foreach_key_values:
- ret = (*func)(key2id(key), val, arg);
- break;
- case foreach_values:
- ret = (*func)(val, arg);
- break;
- }
-
- switch (ret) {
- case ID_TABLE_DELETE:
- rb_warn("unsupported yet");
- break;
- default:
- break;
- }
- if (ret == ID_TABLE_STOP) break;
- }
- }
- }
-}
-
-static void
-hash_id_table_foreach(sa_table *table, enum rb_id_table_iterator_result (*func)(ID, VALUE, void *), void *arg)
-{
- hash_foreach(table, func, arg, foreach_key_values);
-}
-
-static void
-hash_id_table_foreach_values(sa_table *table, enum rb_id_table_iterator_result (*func)(VALUE, void *), void *arg)
-{
- hash_foreach(table, func, arg, foreach_values);
-}
-#endif /* ID_TABLE_USE_COALESCED_HASHING */
-
-#ifdef ID_TABLE_USE_SMALL_HASH
/* simple open addressing with quadratic probing.
uses mark-bit on collisions - need extra 1 bit,
ID is strictly 3 bits larger than rb_id_serial_t */
@@ -1159,7 +37,7 @@ typedef struct rb_id_item {
VALUE val;
} item_t;
-struct hash_id_table {
+struct rb_id_table {
int capa;
int num;
int used;
@@ -1172,7 +50,7 @@ struct hash_id_table {
#define ITEM_COLLIDED(tbl, i) ((tbl)->items[i].collision)
#define ITEM_SET_COLLIDED(tbl, i) ((tbl)->items[i].collision = 1)
static inline void
-ITEM_SET_KEY(struct hash_id_table *tbl, int i, id_key_t key)
+ITEM_SET_KEY(struct rb_id_table *tbl, int i, id_key_t key)
{
tbl->items[i].key = key;
}
@@ -1182,7 +60,7 @@ ITEM_SET_KEY(struct hash_id_table *tbl, int i, id_key_t key)
#define ITEM_COLLIDED(tbl, i) ((tbl)->items[i].key & 1)
#define ITEM_SET_COLLIDED(tbl, i) ((tbl)->items[i].key |= 1)
static inline void
-ITEM_SET_KEY(struct hash_id_table *tbl, int i, id_key_t key)
+ITEM_SET_KEY(struct rb_id_table *tbl, int i, id_key_t key)
{
tbl->items[i].key = (key << 1) | ITEM_COLLIDED(tbl, i);
}
@@ -1201,10 +79,10 @@ round_capa(int capa)
return (capa + 1) << 2;
}
-static struct hash_id_table *
-hash_id_table_init(struct hash_id_table *tbl, int capa)
+static struct rb_id_table *
+rb_id_table_init(struct rb_id_table *tbl, int capa)
{
- MEMZERO(tbl, struct hash_id_table, 1);
+ MEMZERO(tbl, struct rb_id_table, 1);
if (capa > 0) {
capa = round_capa(capa);
tbl->capa = (int)capa;
@@ -1213,44 +91,42 @@ hash_id_table_init(struct hash_id_table *tbl, int capa)
return tbl;
}
-#ifndef ID_TABLE_USE_MIX
-static struct hash_id_table *
-hash_id_table_create(size_t capa)
+struct rb_id_table *
+rb_id_table_create(size_t capa)
{
- struct hash_id_table *tbl = ALLOC(struct hash_id_table);
- return hash_id_table_init(tbl, (int)capa);
+ struct rb_id_table *tbl = ALLOC(struct rb_id_table);
+ return rb_id_table_init(tbl, (int)capa);
}
-#endif
-static void
-hash_id_table_free(struct hash_id_table *tbl)
+void
+rb_id_table_free(struct rb_id_table *tbl)
{
xfree(tbl->items);
xfree(tbl);
}
-static void
-hash_id_table_clear(struct hash_id_table *tbl)
+void
+rb_id_table_clear(struct rb_id_table *tbl)
{
tbl->num = 0;
tbl->used = 0;
MEMZERO(tbl->items, item_t, tbl->capa);
}
-static size_t
-hash_id_table_size(const struct hash_id_table *tbl)
+size_t
+rb_id_table_size(const struct rb_id_table *tbl)
{
return (size_t)tbl->num;
}
-static size_t
-hash_id_table_memsize(const struct hash_id_table *tbl)
+size_t
+rb_id_table_memsize(const struct rb_id_table *tbl)
{
- return sizeof(item_t) * tbl->capa + sizeof(struct hash_id_table);
+ return sizeof(item_t) * tbl->capa + sizeof(struct rb_id_table);
}
static int
-hash_table_index(struct hash_id_table* tbl, id_key_t key)
+hash_table_index(struct rb_id_table* tbl, id_key_t key)
{
if (tbl->capa > 0) {
int mask = tbl->capa - 1;
@@ -1268,7 +144,7 @@ hash_table_index(struct hash_id_table* tbl, id_key_t key)
}
static void
-hash_table_raw_insert(struct hash_id_table *tbl, id_key_t key, VALUE val)
+hash_table_raw_insert(struct rb_id_table *tbl, id_key_t key, VALUE val)
{
int mask = tbl->capa - 1;
int ix = key & mask;
@@ -1288,7 +164,7 @@ hash_table_raw_insert(struct hash_id_table *tbl, id_key_t key, VALUE val)
}
static int
-hash_delete_index(struct hash_id_table *tbl, int ix)
+hash_delete_index(struct rb_id_table *tbl, int ix)
{
if (ix >= 0) {
if (!ITEM_COLLIDED(tbl, ix)) {
@@ -1298,19 +174,20 @@ hash_delete_index(struct hash_id_table *tbl, int ix)
ITEM_SET_KEY(tbl, ix, 0);
tbl->items[ix].val = 0;
return TRUE;
- } else {
+ }
+ else {
return FALSE;
}
}
static void
-hash_table_extend(struct hash_id_table* tbl)
+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 hash_id_table tmp_tbl = {0, 0, 0};
+ struct rb_id_table tmp_tbl = {0, 0, 0};
if (new_cap < tbl->capa) {
new_cap = round_capa(tbl->used + (tbl->used >> 1));
}
@@ -1330,7 +207,7 @@ hash_table_extend(struct hash_id_table* tbl)
#if ID_TABLE_DEBUG && 0
static void
-hash_table_show(struct hash_id_table *tbl)
+hash_table_show(struct rb_id_table *tbl)
{
const id_key_t *keys = tbl->keys;
const int capa = tbl->capa;
@@ -1345,8 +222,8 @@ hash_table_show(struct hash_id_table *tbl)
}
#endif
-static int
-hash_id_table_lookup(struct hash_id_table *tbl, ID id, VALUE *valp)
+int
+rb_id_table_lookup(struct rb_id_table *tbl, ID id, VALUE *valp)
{
id_key_t key = id2key(id);
int index = hash_table_index(tbl, key);
@@ -1361,7 +238,7 @@ hash_id_table_lookup(struct hash_id_table *tbl, ID id, VALUE *valp)
}
static int
-hash_id_table_insert_key(struct hash_id_table *tbl, const id_key_t key, const VALUE val)
+rb_id_table_insert_key(struct rb_id_table *tbl, const id_key_t key, const VALUE val)
{
const int index = hash_table_index(tbl, key);
@@ -1375,22 +252,22 @@ hash_id_table_insert_key(struct hash_id_table *tbl, const id_key_t key, const VA
return TRUE;
}
-static int
-hash_id_table_insert(struct hash_id_table *tbl, ID id, VALUE val)
+int
+rb_id_table_insert(struct rb_id_table *tbl, ID id, VALUE val)
{
- return hash_id_table_insert_key(tbl, id2key(id), val);
+ return rb_id_table_insert_key(tbl, id2key(id), val);
}
-static int
-hash_id_table_delete(struct hash_id_table *tbl, ID id)
+int
+rb_id_table_delete(struct rb_id_table *tbl, ID id)
{
const id_key_t key = id2key(id);
int index = hash_table_index(tbl, key);
return hash_delete_index(tbl, index);
}
-static void
-hash_id_table_foreach(struct hash_id_table *tbl, rb_id_table_foreach_func_t *func, void *data)
+void
+rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, void *data)
{
int i, capa = tbl->capa;
@@ -1408,8 +285,8 @@ hash_id_table_foreach(struct hash_id_table *tbl, rb_id_table_foreach_func_t *fun
}
}
-static void
-hash_id_table_foreach_values(struct hash_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data)
+void
+rb_id_table_foreach_values(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data)
{
int i, capa = tbl->capa;
@@ -1424,169 +301,3 @@ hash_id_table_foreach_values(struct hash_id_table *tbl, rb_id_table_foreach_valu
}
}
}
-#endif /* ID_TABLE_USE_SMALL_HASH */
-
-#if ID_TABLE_USE_MIX
-
-struct mix_id_table {
- union {
- struct {
- int capa;
- int num;
- } size;
- struct list_id_table list;
- struct hash_id_table hash;
- } aux;
-};
-
-#define LIST_LIMIT_P(mix) ((mix)->aux.size.num == ID_TABLE_USE_MIX_LIST_MAX_CAPA)
-#define LIST_P(mix) ((mix)->aux.size.capa <= ID_TABLE_USE_MIX_LIST_MAX_CAPA)
-
-static struct mix_id_table *
-mix_id_table_create(size_t size)
-{
- struct mix_id_table *mix = ZALLOC(struct mix_id_table);
- list_id_table_init((struct list_id_table *)mix, size);
- return mix;
-}
-
-static void
-mix_id_table_free(struct mix_id_table *tbl)
-{
- if (LIST_P(tbl)) list_id_table_free(&tbl->aux.list);
- else hash_id_table_free(&tbl->aux.hash);
-}
-
-static void
-mix_id_table_clear(struct mix_id_table *tbl)
-{
- if (LIST_P(tbl)) list_id_table_clear(&tbl->aux.list);
- else hash_id_table_clear(&tbl->aux.hash);
-}
-
-static size_t
-mix_id_table_size(const struct mix_id_table *tbl)
-{
- if (LIST_P(tbl)) return list_id_table_size(&tbl->aux.list);
- else return hash_id_table_size(&tbl->aux.hash);
-}
-
-static size_t
-mix_id_table_memsize(const struct mix_id_table *tbl)
-{
- if (LIST_P(tbl)) return list_id_table_memsize(&tbl->aux.list) - sizeof(struct list_id_table) + sizeof(struct mix_id_table);
- else return hash_id_table_memsize(&tbl->aux.hash);
-}
-
-static int
-mix_id_table_insert(struct mix_id_table *tbl, ID id, VALUE val)
-{
- int r;
-
- if (LIST_P(tbl)) {
- if (!LIST_LIMIT_P(tbl)) {
- r = list_id_table_insert(&tbl->aux.list, id, val);
- }
- else {
- /* convert to hash */
- /* overflow. TODO: this promotion should be done in list_extend_table */
- struct list_id_table *list = &tbl->aux.list;
- struct hash_id_table hash_body;
- id_key_t *keys = list->keys;
- VALUE *values = TABLE_VALUES(list);
- const int num = list->num;
- int i;
-
- hash_id_table_init(&hash_body, 0);
-
- for (i=0; i<num; i++) {
- /* note that GC can run */
- hash_id_table_insert_key(&hash_body, keys[i], values[i]);
- }
-
- tbl->aux.hash = hash_body;
-
- /* free list keys/values */
- xfree(keys);
-#if ID_TABLE_USE_CALC_VALUES == 0
- xfree(values);
-#endif
- goto hash_insert;
- }
- }
- else {
- hash_insert:
- r = hash_id_table_insert(&tbl->aux.hash, id, val);
- assert(!LIST_P(tbl));
- }
- return r;
-}
-
-static int
-mix_id_table_lookup(struct mix_id_table *tbl, ID id, VALUE *valp)
-{
- if (LIST_P(tbl)) return list_id_table_lookup(&tbl->aux.list, id, valp);
- else return hash_id_table_lookup(&tbl->aux.hash, id, valp);
-}
-
-static int
-mix_id_table_delete(struct mix_id_table *tbl, ID id)
-{
- if (LIST_P(tbl)) return list_id_table_delete(&tbl->aux.list, id);
- else return hash_id_table_delete(&tbl->aux.hash, id);
-}
-
-static void
-mix_id_table_foreach(struct mix_id_table *tbl, rb_id_table_foreach_func_t *func, void *data)
-{
- if (LIST_P(tbl)) list_id_table_foreach(&tbl->aux.list, func, data);
- else hash_id_table_foreach(&tbl->aux.hash, func, data);
-}
-
-static void
-mix_id_table_foreach_values(struct mix_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data)
-{
- if (LIST_P(tbl)) list_id_table_foreach_values(&tbl->aux.list, func, data);
- else hash_id_table_foreach_values(&tbl->aux.hash, func, data);
-}
-
-#endif /* ID_TABLE_USE_MIX */
-
-#define IMPL_TYPE1(type, prot, name, args) \
- RUBY_ALIAS_FUNCTION_TYPE(type, prot, name, args)
-#define IMPL_TYPE(type, name, prot, args) \
- IMPL_TYPE1(type, rb_id_table_##name prot, IMPL(_##name), args)
-#define IMPL_VOID1(prot, name, args) \
- RUBY_ALIAS_FUNCTION_VOID(prot, name, args)
-#define IMPL_VOID(name, prot, args) \
- IMPL_VOID1(rb_id_table_##name prot, IMPL(_##name), args)
-#define id_tbl (ID_TABLE_IMPL_TYPE *)tbl
-
-IMPL_TYPE(struct rb_id_table *, create, (size_t size), (size))
-IMPL_VOID(free, (struct rb_id_table *tbl), (id_tbl))
-IMPL_VOID(clear, (struct rb_id_table *tbl), (id_tbl))
-IMPL_TYPE(size_t, size, (const struct rb_id_table *tbl), (id_tbl))
-IMPL_TYPE(size_t, memsize, (const struct rb_id_table *tbl), (id_tbl))
-
-IMPL_TYPE(int , insert, (struct rb_id_table *tbl, ID id, VALUE val),
- (id_tbl, id, val))
-IMPL_TYPE(int, lookup, (struct rb_id_table *tbl, ID id, VALUE *valp),
- (id_tbl, id, valp))
-IMPL_TYPE(int, delete, (struct rb_id_table *tbl, ID id),
- (id_tbl, id))
-
-IMPL_VOID(foreach,
- (struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, void *data),
- (id_tbl, func, data))
-IMPL_VOID(foreach_values,
- (struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data),
- (id_tbl, func, data))
-
-#if ID_TABLE_STARTUP_SIG
-__attribute__((constructor))
-static void
-show_impl(void)
-{
- fprintf(stderr, "impl: %d\n", ID_TABLE_IMPL);
-}
-#endif
diff --git a/include/ruby/backward.h b/include/ruby/backward.h
index 4574cb98da..262ed2cef1 100644
--- a/include/ruby/backward.h
+++ b/include/ruby/backward.h
@@ -1,6 +1,55 @@
#ifndef RUBY_RUBY_BACKWARD_H
#define RUBY_RUBY_BACKWARD_H 1
+#define RClass RClassDeprecated
+#ifndef __cplusplus
+DEPRECATED_TYPE(("RClass is internal use only"),
+struct RClass {
+ struct RBasic basic;
+});
+#endif
+
+#define DECLARE_DEPRECATED_FEATURE(ver, func) \
+ NORETURN(ERRORFUNC(("deprecated since "#ver), DEPRECATED(void func(void))))
+
+/* eval.c */
+DECLARE_DEPRECATED_FEATURE(2.2, rb_disable_super);
+DECLARE_DEPRECATED_FEATURE(2.2, rb_enable_super);
+
+/* hash.c */
+DECLARE_DEPRECATED_FEATURE(2.2, rb_hash_iter_lev);
+DECLARE_DEPRECATED_FEATURE(2.2, rb_hash_ifnone);
+
+/* string.c */
+DECLARE_DEPRECATED_FEATURE(2.2, rb_str_associate);
+DECLARE_DEPRECATED_FEATURE(2.2, rb_str_associated);
+
+/* variable.c */
+DEPRECATED(void rb_autoload(VALUE, ID, const char*));
+
+/* vm.c */
+DECLARE_DEPRECATED_FEATURE(2.2, rb_clear_cache);
+DECLARE_DEPRECATED_FEATURE(2.2, rb_frame_pop);
+
+#define DECLARE_DEPRECATED_INTERNAL_FEATURE(func) \
+ NORETURN(ERRORFUNC(("deprecated internal function"), DEPRECATED(void func(void))))
+
+/* eval.c */
+NORETURN(ERRORFUNC(("internal function"), void rb_frozen_class_p(VALUE)));
+
+/* error.c */
+DECLARE_DEPRECATED_INTERNAL_FEATURE(rb_compile_error);
+DECLARE_DEPRECATED_INTERNAL_FEATURE(rb_compile_error_with_enc);
+DECLARE_DEPRECATED_INTERNAL_FEATURE(rb_compile_error_append);
+
+/* struct.c */
+DECLARE_DEPRECATED_INTERNAL_FEATURE(rb_struct_ptr);
+
+/* variable.c */
+DECLARE_DEPRECATED_INTERNAL_FEATURE(rb_generic_ivar_table);
+NORETURN(ERRORFUNC(("internal function"), VALUE rb_mod_const_missing(VALUE, VALUE)));
+
+/* from version.c */
#ifndef RUBY_SHOW_COPYRIGHT_TO_DIE
# define RUBY_SHOW_COPYRIGHT_TO_DIE 1
#endif
diff --git a/include/ruby/debug.h b/include/ruby/debug.h
index 9bfc9b9a83..8a831e61ab 100644
--- a/include/ruby/debug.h
+++ b/include/ruby/debug.h
@@ -75,6 +75,7 @@ VALUE rb_tracearg_event(rb_trace_arg_t *trace_arg);
VALUE rb_tracearg_lineno(rb_trace_arg_t *trace_arg);
VALUE rb_tracearg_path(rb_trace_arg_t *trace_arg);
VALUE rb_tracearg_method_id(rb_trace_arg_t *trace_arg);
+VALUE rb_tracearg_callee_id(rb_trace_arg_t *trace_arg);
VALUE rb_tracearg_defined_class(rb_trace_arg_t *trace_arg);
VALUE rb_tracearg_binding(rb_trace_arg_t *trace_arg);
VALUE rb_tracearg_self(rb_trace_arg_t *trace_arg);
@@ -82,7 +83,11 @@ VALUE rb_tracearg_return_value(rb_trace_arg_t *trace_arg);
VALUE rb_tracearg_raised_exception(rb_trace_arg_t *trace_arg);
VALUE rb_tracearg_object(rb_trace_arg_t *trace_arg);
-/* Postponed Job API */
+/*
+ * Postponed Job API
+ * rb_postponed_job_register and rb_postponed_job_register_one are
+ * async-signal-safe and used via SIGPROF by the "stackprof" RubyGem
+ */
typedef void (*rb_postponed_job_func_t)(void *arg);
int rb_postponed_job_register(unsigned int flags, rb_postponed_job_func_t func, void *data);
int rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func, void *data);
diff --git a/include/ruby/defines.h b/include/ruby/defines.h
index 89e549ec19..cbf5537790 100644
--- a/include/ruby/defines.h
+++ b/include/ruby/defines.h
@@ -29,10 +29,6 @@ extern "C" {
#ifndef PUREFUNC
# define PUREFUNC(x) x
#endif
-#define NORETURN_STYLE_NEW 1
-#ifndef NORETURN
-# define NORETURN(x) x
-#endif
#ifndef DEPRECATED
# define DEPRECATED(x) x
#endif
@@ -62,12 +58,26 @@ extern "C" {
#endif
#ifndef GCC_VERSION_SINCE
-#define GCC_VERSION_SINCE(major, minor, patchlevel) \
- (defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__) && \
- ((__GNUC__ > (major)) || \
- ((__GNUC__ == (major) && \
- ((__GNUC_MINOR__ > (minor)) || \
- (__GNUC_MINOR__ == (minor) && __GNUC_PATCHLEVEL__ >= (patchlevel)))))))
+# if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
+# define GCC_VERSION_SINCE(major, minor, patchlevel) \
+ ((__GNUC__ > (major)) || \
+ ((__GNUC__ == (major) && \
+ ((__GNUC_MINOR__ > (minor)) || \
+ (__GNUC_MINOR__ == (minor) && __GNUC_PATCHLEVEL__ >= (patchlevel))))))
+# else
+# define GCC_VERSION_SINCE(major, minor, patchlevel) 0
+# endif
+#endif
+#ifndef GCC_VERSION_BEFORE
+# if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
+# define GCC_VERSION_BEFORE(major, minor, patchlevel) \
+ ((__GNUC__ < (major)) || \
+ ((__GNUC__ == (major) && \
+ ((__GNUC_MINOR__ < (minor)) || \
+ (__GNUC_MINOR__ == (minor) && __GNUC_PATCHLEVEL__ <= (patchlevel))))))
+# else
+# define GCC_VERSION_BEFORE(major, minor, patchlevel) 0
+# endif
#endif
/* likely */
@@ -79,9 +89,24 @@ extern "C" {
#define RB_UNLIKELY(x) (x)
#endif /* __GNUC__ >= 3 */
+/*
+ cold attribute for code layout improvements
+ RUBY_FUNC_ATTRIBUTE not used because MSVC does not like nested func macros
+ */
+#if defined(__clang__) || GCC_VERSION_SINCE(4, 3, 0)
+#define COLDFUNC __attribute__((cold))
+#else
+#define COLDFUNC
+#endif
+
#ifdef __GNUC__
+#if defined __MINGW_PRINTF_FORMAT
+#define PRINTF_ARGS(decl, string_index, first_to_check) \
+ decl __attribute__((format(__MINGW_PRINTF_FORMAT, string_index, first_to_check)))
+#else
#define PRINTF_ARGS(decl, string_index, first_to_check) \
decl __attribute__((format(printf, string_index, first_to_check)))
+#endif
#else
#define PRINTF_ARGS(decl, string_index, first_to_check) decl
#endif
@@ -125,6 +150,9 @@ extern "C" {
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
+#ifdef HAVE_STDALIGN_H
+# include <stdalign.h>
+#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
@@ -190,12 +218,89 @@ RUBY_SYMBOL_EXPORT_BEGIN
# define RUBY_ATTR_ALLOC_SIZE(params)
#endif
-void *xmalloc(size_t) RUBY_ATTR_ALLOC_SIZE((1));
-void *xmalloc2(size_t,size_t) RUBY_ATTR_ALLOC_SIZE((1,2));
-void *xcalloc(size_t,size_t) RUBY_ATTR_ALLOC_SIZE((1,2));
-void *xrealloc(void*,size_t) RUBY_ATTR_ALLOC_SIZE((2));
-void *xrealloc2(void*,size_t,size_t) RUBY_ATTR_ALLOC_SIZE((2,3));
-void xfree(void*);
+void *ruby_xmalloc(size_t) RUBY_ATTR_ALLOC_SIZE((1));
+void *ruby_xmalloc2(size_t,size_t) RUBY_ATTR_ALLOC_SIZE((1,2));
+void *ruby_xcalloc(size_t,size_t) RUBY_ATTR_ALLOC_SIZE((1,2));
+void *ruby_xrealloc(void*,size_t) RUBY_ATTR_ALLOC_SIZE((2));
+void *ruby_xrealloc2(void*,size_t,size_t) RUBY_ATTR_ALLOC_SIZE((2,3));
+void ruby_xfree(void*);
+
+#ifndef USE_GC_MALLOC_OBJ_INFO_DETAILS
+#define USE_GC_MALLOC_OBJ_INFO_DETAILS 0
+#endif
+
+#if USE_GC_MALLOC_OBJ_INFO_DETAILS
+
+void *ruby_xmalloc_body(size_t) RUBY_ATTR_ALLOC_SIZE((1));
+void *ruby_xmalloc2_body(size_t,size_t) RUBY_ATTR_ALLOC_SIZE((1,2));
+void *ruby_xcalloc_body(size_t,size_t) RUBY_ATTR_ALLOC_SIZE((1,2));
+void *ruby_xrealloc_body(void*,size_t) RUBY_ATTR_ALLOC_SIZE((2));
+void *ruby_xrealloc2_body(void*,size_t,size_t) RUBY_ATTR_ALLOC_SIZE((2,3));
+
+#define ruby_xmalloc(s1) ruby_xmalloc_with_location(s1, __FILE__, __LINE__)
+#define ruby_xmalloc2(s1, s2) ruby_xmalloc2_with_location(s1, s2, __FILE__, __LINE__)
+#define ruby_xcalloc(s1, s2) ruby_xcalloc_with_location(s1, s2, __FILE__, __LINE__)
+#define ruby_xrealloc(ptr, s1) ruby_xrealloc_with_location(ptr, s1, __FILE__, __LINE__)
+#define ruby_xrealloc2(ptr, s1, s2) ruby_xrealloc2_with_location(ptr, s1, s2, __FILE__, __LINE__)
+
+extern const char *ruby_malloc_info_file;
+extern int ruby_malloc_info_line;
+
+static inline void *
+ruby_xmalloc_with_location(size_t s, const char *file, int line)
+{
+ void *ptr;
+ ruby_malloc_info_file = file;
+ ruby_malloc_info_line = line;
+ ptr = ruby_xmalloc_body(s);
+ ruby_malloc_info_file = NULL;
+ return ptr;
+}
+
+static inline void *
+ruby_xmalloc2_with_location(size_t s1, size_t s2, const char *file, int line)
+{
+ void *ptr;
+ ruby_malloc_info_file = file;
+ ruby_malloc_info_line = line;
+ ptr = ruby_xmalloc2_body(s1, s2);
+ ruby_malloc_info_file = NULL;
+ return ptr;
+}
+
+static inline void *
+ruby_xcalloc_with_location(size_t s1, size_t s2, const char *file, int line)
+{
+ void *ptr;
+ ruby_malloc_info_file = file;
+ ruby_malloc_info_line = line;
+ ptr = ruby_xcalloc_body(s1, s2);
+ ruby_malloc_info_file = NULL;
+ return ptr;
+}
+
+static inline void *
+ruby_xrealloc_with_location(void *ptr, size_t s, const char *file, int line)
+{
+ void *rptr;
+ ruby_malloc_info_file = file;
+ ruby_malloc_info_line = line;
+ rptr = ruby_xrealloc_body(ptr, s);
+ ruby_malloc_info_file = NULL;
+ return rptr;
+}
+
+static inline void *
+ruby_xrealloc2_with_location(void *ptr, size_t s1, size_t s2, const char *file, int line)
+{
+ void *rptr;
+ ruby_malloc_info_file = file;
+ ruby_malloc_info_line = line;
+ rptr = ruby_xrealloc2_body(ptr, s1, s2);
+ ruby_malloc_info_file = NULL;
+ return rptr;
+}
+#endif
#define STRINGIZE(expr) STRINGIZE0(expr)
#ifndef STRINGIZE0
@@ -260,12 +365,32 @@ void xfree(void*);
#define RUBY_FUNC_EXPORTED
#endif
+/* These macros are used for functions which are exported only for MJIT
+ and NOT ensured to be exported in future versions. */
+#define MJIT_FUNC_EXPORTED RUBY_FUNC_EXPORTED
+#define MJIT_SYMBOL_EXPORT_BEGIN RUBY_SYMBOL_EXPORT_BEGIN
+#define MJIT_SYMBOL_EXPORT_END RUBY_SYMBOL_EXPORT_END
+
+#if defined(MJIT_HEADER) && defined(_MSC_VER)
+# undef MJIT_FUNC_EXPORTED
+# define MJIT_FUNC_EXPORTED static
+#endif
+
#ifndef RUBY_EXTERN
#define RUBY_EXTERN extern
#endif
#ifndef EXTERN
-#define EXTERN RUBY_EXTERN /* deprecated */
+# if defined __GNUC__
+# define EXTERN _Pragma("message \"EXTERN is deprecated, use RUBY_EXTERN instead\""); \
+ RUBY_EXTERN
+# elif defined _MSC_VER
+# define EXTERN __pragma(message(__FILE__"("STRINGIZE(__LINE__)"): warning: "\
+ "EXTERN is deprecated, use RUBY_EXTERN instead")); \
+ RUBY_EXTERN
+# else
+# define EXTERN <-<-"EXTERN is deprecated, use RUBY_EXTERN instead"->->
+# endif
#endif
#ifndef RUBY_MBCHAR_MAXSIZE
@@ -353,6 +478,31 @@ void rb_ia64_flushrs(void);
# endif
#endif
+#ifndef RUBY_ALIGNAS
+#define RUBY_ALIGNAS(x) /* x */
+#endif
+
+#ifdef RUBY_ALIGNOF
+/* OK, take that definition */
+#elif defined(__cplusplus) && (__cplusplus >= 201103L)
+#define RUBY_ALIGNOF alignof
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
+#define RUBY_ALIGNOF _Alignof
+#else
+#define RUBY_ALIGNOF(type) ((size_t)offsetof(struct { char f1; type f2; }, f2))
+#endif
+
+#define NORETURN_STYLE_NEW 1
+#ifdef NORETURN
+/* OK, take that definition */
+#elif defined(__cplusplus) && (__cplusplus >= 201103L)
+#define NORETURN(x) [[ noreturn ]] x
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
+#define NORETURN(x) _Noreturn x
+#else
+#define NORETURN(x) x
+#endif
+
RUBY_SYMBOL_EXPORT_END
#if defined(__cplusplus)
diff --git a/include/ruby/encoding.h b/include/ruby/encoding.h
index e6ceb19cdf..93939ee7db 100644
--- a/include/ruby/encoding.h
+++ b/include/ruby/encoding.h
@@ -12,6 +12,10 @@
#ifndef RUBY_ENCODING_H
#define RUBY_ENCODING_H 1
+#ifdef RUBY_INTERNAL_H
+#error "Include this file before internal.h"
+#endif
+
#if defined(__cplusplus)
extern "C" {
#if 0
@@ -118,7 +122,9 @@ PUREFUNC(int rb_enc_dummy_p(rb_encoding *enc));
PUREFUNC(int rb_enc_to_index(rb_encoding *enc));
int rb_enc_get_index(VALUE obj);
void rb_enc_set_index(VALUE obj, int encindex);
+int rb_enc_capable(VALUE obj);
int rb_enc_find_index(const char *name);
+int rb_enc_alias(const char *alias, const char *orig);
int rb_to_encoding_index(VALUE);
rb_encoding *rb_to_encoding(VALUE);
rb_encoding *rb_find_encoding(VALUE);
diff --git a/include/ruby/intern.h b/include/ruby/intern.h
index 8776a596f0..17aafd7f8e 100644
--- a/include/ruby/intern.h
+++ b/include/ruby/intern.h
@@ -34,6 +34,15 @@ extern "C" {
#include "ruby/st.h"
+/* On mswin, MJIT header transformation can't be used since cl.exe can't output
+ preprocessed output preserving macros. So this `MJIT_STATIC` is needed
+ to force non-static function to static on MJIT header to avoid symbol conflict. */
+#ifdef MJIT_HEADER
+# define MJIT_STATIC static
+#else
+# define MJIT_STATIC
+#endif
+
RUBY_SYMBOL_EXPORT_BEGIN
/*
@@ -179,12 +188,27 @@ VALUE rb_complex_raw(VALUE, VALUE);
VALUE rb_complex_new(VALUE, VALUE);
#define rb_complex_new1(x) rb_complex_new((x), INT2FIX(0))
#define rb_complex_new2(x,y) rb_complex_new((x), (y))
-VALUE rb_complex_polar(VALUE, VALUE);
+VALUE rb_complex_new_polar(VALUE abs, VALUE arg);
+DEPRECATED_BY(rb_complex_new_polar, VALUE rb_complex_polar(VALUE abs, VALUE arg));
+VALUE rb_complex_real(VALUE z);
+VALUE rb_complex_imag(VALUE z);
+VALUE rb_complex_plus(VALUE x, VALUE y);
+VALUE rb_complex_minus(VALUE x, VALUE y);
+VALUE rb_complex_mul(VALUE x, VALUE y);
+VALUE rb_complex_div(VALUE x, VALUE y);
+VALUE rb_complex_uminus(VALUE z);
+VALUE rb_complex_conjugate(VALUE z);
+VALUE rb_complex_abs(VALUE z);
+VALUE rb_complex_arg(VALUE z);
+VALUE rb_complex_pow(VALUE base, VALUE exp);
+VALUE rb_dbl_complex_new(double real, double imag);
+#define rb_complex_add rb_complex_plus
+#define rb_complex_sub rb_complex_minus
+#define rb_complex_nagate rb_complex_uminus
+
VALUE rb_Complex(VALUE, VALUE);
#define rb_Complex1(x) rb_Complex((x), INT2FIX(0))
#define rb_Complex2(x,y) rb_Complex((x), (y))
-DEPRECATED(VALUE rb_complex_set_real(VALUE, VALUE));
-DEPRECATED(VALUE rb_complex_set_imag(VALUE, VALUE));
/* class.c */
VALUE rb_class_new(VALUE);
VALUE rb_mod_init_copy(VALUE, VALUE);
@@ -205,7 +229,6 @@ VALUE rb_class_protected_instance_methods(int, const VALUE*, VALUE);
VALUE rb_class_private_instance_methods(int, const VALUE*, VALUE);
VALUE rb_obj_singleton_methods(int, const VALUE*, VALUE);
void rb_define_method_id(VALUE, ID, VALUE (*)(ANYARGS), int);
-void rb_frozen_class_p(VALUE);
void rb_undef(VALUE, ID);
void rb_define_protected_method(VALUE, const char*, VALUE (*)(ANYARGS), int);
void rb_define_private_method(VALUE, const char*, VALUE (*)(ANYARGS), int);
@@ -238,6 +261,13 @@ VALUE rb_enumeratorize_with_size(VALUE, VALUE, int, const VALUE *, rb_enumerator
return SIZED_ENUMERATOR(obj, argc, argv, size_fn); \
} while (0)
#define RETURN_ENUMERATOR(obj, argc, argv) RETURN_SIZED_ENUMERATOR(obj, argc, argv, 0)
+typedef struct {
+ VALUE begin;
+ VALUE end;
+ VALUE step;
+ int exclude_end;
+} rb_arithmetic_sequence_components_t;
+int rb_arithmetic_sequence_extract(VALUE, rb_arithmetic_sequence_components_t *);
/* error.c */
VALUE rb_exc_new(VALUE, const char*, long);
VALUE rb_exc_new_cstr(VALUE, const char*);
@@ -249,17 +279,14 @@ PRINTF_ARGS(NORETURN(void rb_loaderror_with_path(VALUE path, const char*, ...)),
PRINTF_ARGS(NORETURN(void rb_name_error(ID, const char*, ...)), 2, 3);
PRINTF_ARGS(NORETURN(void rb_name_error_str(VALUE, const char*, ...)), 2, 3);
NORETURN(void rb_invalid_str(const char*, const char*));
-NORETURN(DEPRECATED(PRINTF_ARGS(void rb_compile_error(const char*, int, const char*, ...), 3, 4)));
-NORETURN(DEPRECATED(PRINTF_ARGS(void rb_compile_error_with_enc(const char*, int, void *, const char*, ...), 4, 5)));
-NORETURN(DEPRECATED(PRINTF_ARGS(void rb_compile_error_append(const char*, ...), 1, 2)));
NORETURN(void rb_error_frozen(const char*));
NORETURN(void rb_error_frozen_object(VALUE));
-CONSTFUNC(void rb_error_untrusted(VALUE));
+void rb_error_untrusted(VALUE);
void rb_check_frozen(VALUE);
-CONSTFUNC(void rb_check_trusted(VALUE));
+void rb_check_trusted(VALUE);
#define rb_check_frozen_internal(obj) do { \
VALUE frozen_obj = (obj); \
- if (OBJ_FROZEN(frozen_obj)) { \
+ if (RB_UNLIKELY(RB_OBJ_FROZEN(frozen_obj))) { \
rb_error_frozen_object(frozen_obj); \
} \
} while (0)
@@ -292,7 +319,7 @@ int rb_sourceline(void);
const char *rb_sourcefile(void);
VALUE rb_check_funcall(VALUE, ID, int, const VALUE*);
-NORETURN(void rb_error_arity(int, int, int));
+NORETURN(MJIT_STATIC void rb_error_arity(int, int, int));
static inline int
rb_check_arity(int argc, int min, int max)
{
@@ -340,7 +367,15 @@ void rb_fd_set(int, rb_fdset_t *);
void rb_w32_fd_copy(rb_fdset_t *, const fd_set *, int);
#define rb_fd_dup(d, s) rb_w32_fd_dup((d), (s))
void rb_w32_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src);
-#define rb_fd_select(n, rfds, wfds, efds, timeout) rb_w32_select((n), (rfds) ? ((rb_fdset_t*)(rfds))->fdset : NULL, (wfds) ? ((rb_fdset_t*)(wfds))->fdset : NULL, (efds) ? ((rb_fdset_t*)(efds))->fdset: NULL, (timeout))
+static inline int
+rb_fd_select(int n, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct timeval *timeout)
+{
+ return rb_w32_select(n,
+ rfds ? rfds->fdset : NULL,
+ wfds ? wfds->fdset : NULL,
+ efds ? efds->fdset : NULL,
+ timeout);
+}
#define rb_fd_resize(n, f) ((void)(f))
#define rb_fd_ptr(f) ((f)->fdset)
@@ -371,24 +406,11 @@ NORETURN(VALUE rb_f_exit(int, const VALUE*));
NORETURN(VALUE rb_f_abort(int, const VALUE*));
void rb_remove_method(VALUE, const char*);
void rb_remove_method_id(VALUE, ID);
-DEPRECATED(static inline void rb_disable_super(void));
-DEPRECATED(static inline void rb_enable_super(void));
-static inline void rb_disable_super(void)
-{
- /* obsolete - no use */
-}
-static inline void rb_enable_super(void)
-{
- rb_warning("rb_enable_super() is obsolete");
-}
-#define rb_disable_super(klass, name) rb_disable_super()
-#define rb_enable_super(klass, name) rb_enable_super()
#define HAVE_RB_DEFINE_ALLOC_FUNC 1
typedef VALUE (*rb_alloc_func_t)(VALUE);
void rb_define_alloc_func(VALUE, rb_alloc_func_t);
void rb_undef_alloc_func(VALUE);
rb_alloc_func_t rb_get_alloc_func(VALUE);
-NORETURN(DEPRECATED(void rb_clear_cache(void)));
void rb_clear_constant_cache(void);
void rb_clear_method_cache_by_class(VALUE);
void rb_alias(VALUE, ID, ID);
@@ -481,7 +503,7 @@ VALUE rb_file_directory_p(VALUE,VALUE);
VALUE rb_str_encode_ospath(VALUE);
int rb_is_absolute_path(const char *);
/* gc.c */
-NORETURN(void rb_memerror(void));
+COLDFUNC NORETURN(void rb_memerror(void));
PUREFUNC(int rb_during_gc(void));
void rb_gc_mark_locations(const VALUE*, const VALUE*);
void rb_mark_tbl(struct st_table*);
@@ -522,13 +544,12 @@ VALUE rb_hash_delete(VALUE,VALUE);
VALUE rb_hash_set_ifnone(VALUE hash, VALUE ifnone);
typedef VALUE rb_hash_update_func(VALUE newkey, VALUE oldkey, VALUE value);
VALUE rb_hash_update_by(VALUE hash1, VALUE hash2, rb_hash_update_func *func);
-struct st_table *rb_hash_tbl(VALUE);
+struct st_table *rb_hash_tbl(VALUE, const char *file, int line);
int rb_path_check(const char*);
int rb_env_path_tainted(void);
VALUE rb_env_clear(void);
VALUE rb_hash_size(VALUE);
-DEPRECATED(int rb_hash_iter_lev(VALUE));
-DEPRECATED(VALUE rb_hash_ifnone(VALUE));
+void rb_hash_free(VALUE);
/* io.c */
#define rb_defout rb_stdout
RUBY_EXTERN VALUE rb_fs;
@@ -777,8 +798,6 @@ VALUE rb_str_replace(VALUE, VALUE);
VALUE rb_str_inspect(VALUE);
VALUE rb_str_dump(VALUE);
VALUE rb_str_split(VALUE, const char*);
-NORETURN(DEPRECATED(void rb_str_associate(VALUE, VALUE)));
-NORETURN(DEPRECATED(VALUE rb_str_associated(VALUE)));
void rb_str_setter(VALUE, ID, VALUE*);
VALUE rb_str_intern(VALUE);
VALUE rb_sym_to_s(VALUE);
@@ -886,7 +905,6 @@ VALUE rb_struct_getmember(VALUE, ID);
VALUE rb_struct_s_members(VALUE);
VALUE rb_struct_members(VALUE);
VALUE rb_struct_size(VALUE s);
-DEPRECATED(const VALUE *rb_struct_ptr(VALUE s));
VALUE rb_struct_alloc_noinit(VALUE);
VALUE rb_struct_define_without_accessor(const char *, VALUE, rb_alloc_func_t, ...);
VALUE rb_struct_define_without_accessor_under(VALUE outer, const char *class_name, VALUE super, rb_alloc_func_t alloc, ...);
@@ -915,6 +933,7 @@ VALUE rb_time_num_new(VALUE, VALUE);
struct timeval rb_time_interval(VALUE num);
struct timeval rb_time_timeval(VALUE time);
struct timespec rb_time_timespec(VALUE time);
+VALUE rb_time_utc_offset(VALUE time);
/* variable.c */
VALUE rb_mod_name(VALUE);
VALUE rb_class_path(VALUE);
@@ -925,14 +944,12 @@ VALUE rb_path_to_class(VALUE);
VALUE rb_path2class(const char*);
void rb_name_class(VALUE, ID);
VALUE rb_class_name(VALUE);
-DEPRECATED(void rb_autoload(VALUE, ID, const char*));
VALUE rb_autoload_load(VALUE, ID);
VALUE rb_autoload_p(VALUE, ID);
VALUE rb_f_trace_var(int, const VALUE*);
VALUE rb_f_untrace_var(int, const VALUE*);
VALUE rb_f_global_variables(void);
void rb_alias_variable(ID, ID);
-DEPRECATED(struct st_table* rb_generic_ivar_table(VALUE));
void rb_copy_generic_ivar(VALUE,VALUE);
void rb_free_generic_ivar(VALUE);
VALUE rb_ivar_get(VALUE, ID);
@@ -956,7 +973,9 @@ VALUE rb_const_get_at(VALUE, ID);
VALUE rb_const_get_from(VALUE, ID);
void rb_const_set(VALUE, ID, VALUE);
VALUE rb_const_remove(VALUE, ID);
+#if 0 /* EXPERIMENTAL: remove if no problem */
NORETURN(VALUE rb_mod_const_missing(VALUE,VALUE));
+#endif
VALUE rb_cvar_defined(VALUE, ID);
void rb_cvar_set(VALUE, ID, VALUE);
VALUE rb_cvar_get(VALUE, ID);
@@ -967,16 +986,12 @@ VALUE rb_mod_class_variables(int, const VALUE*, VALUE);
VALUE rb_mod_remove_cvar(VALUE, VALUE);
ID rb_frame_callee(void);
+int rb_frame_method_id_and_class(ID *idp, VALUE *klassp);
VALUE rb_str_succ(VALUE);
VALUE rb_time_succ(VALUE);
-int rb_frame_method_id_and_class(ID *idp, ID *called_idp, VALUE *klassp);
VALUE rb_make_backtrace(void);
VALUE rb_make_exception(int, const VALUE*);
-/* deprecated */
-NORETURN(DEPRECATED(void rb_frame_pop(void)));
-
-
RUBY_SYMBOL_EXPORT_END
#if defined(__cplusplus)
diff --git a/include/ruby/io.h b/include/ruby/io.h
index 60d6f6d32e..7caca17e3b 100644
--- a/include/ruby/io.h
+++ b/include/ruby/io.h
@@ -12,6 +12,10 @@
#ifndef RUBY_IO_H
#define RUBY_IO_H 1
+#ifdef RUBY_INTERNAL_H
+#error "Include this file before internal.h"
+#endif
+
#if defined(__cplusplus)
extern "C" {
#if 0
@@ -20,7 +24,6 @@ extern "C" {
#endif
#include <stdio.h>
-#include <errno.h>
#include "ruby/encoding.h"
#if defined(HAVE_STDIO_EXT_H)
@@ -28,6 +31,7 @@ extern "C" {
#endif
#include "ruby/config.h"
+#include <errno.h>
#if defined(HAVE_POLL)
# ifdef _AIX
# define reqevents events
@@ -109,6 +113,7 @@ typedef struct rb_io_t {
#define FMODE_APPEND 0x00000040
#define FMODE_CREATE 0x00000080
/* #define FMODE_NOREVLOOKUP 0x00000100 */
+#define FMODE_EXCL 0x00000400
#define FMODE_TRUNC 0x00000800
#define FMODE_TEXTMODE 0x00001000
/* #define FMODE_PREP 0x00010000 */
diff --git a/include/ruby/missing.h b/include/ruby/missing.h
index 3474ca256f..03657042ce 100644
--- a/include/ruby/missing.h
+++ b/include/ruby/missing.h
@@ -136,7 +136,7 @@ RUBY_EXTERN double lgamma_r(double, int *);
RUBY_EXTERN double cbrt(double);
#endif
-#if !defined(HAVE_INFINITY) || !defined(HAVE_NAN)
+#if !defined(INFINITY) || !defined(NAN)
union bytesequence4_or_float {
unsigned char bytesequence[4];
float float_value;
@@ -147,12 +147,18 @@ union bytesequence4_or_float {
/** @internal */
RUBY_EXTERN const union bytesequence4_or_float rb_infinity;
# define INFINITY (rb_infinity.float_value)
+# define USE_RB_INFINITY 1
#endif
#ifndef NAN
/** @internal */
RUBY_EXTERN const union bytesequence4_or_float rb_nan;
# define NAN (rb_nan.float_value)
+# define USE_RB_NAN 1
+#endif
+
+#ifndef HUGE_VAL
+# define HUGE_VAL ((double)INFINITY)
#endif
#ifndef isinf
@@ -162,6 +168,8 @@ RUBY_EXTERN const union bytesequence4_or_float rb_nan;
# include <ieeefp.h>
# endif
# define isinf(x) (!finite(x) && !isnan(x))
+# elif defined(__cplusplus) && __cplusplus >= 201103L
+# include <cmath> // it must include constexpr bool isinf(double);
# else
RUBY_EXTERN int isinf(double);
# endif
@@ -170,7 +178,11 @@ RUBY_EXTERN int isinf(double);
#ifndef isnan
# ifndef HAVE_ISNAN
+# if defined(__cplusplus) && __cplusplus >= 201103L
+# include <cmath> // it must include constexpr bool isnan(double);
+# else
RUBY_EXTERN int isnan(double);
+# endif
# endif
#endif
@@ -181,6 +193,10 @@ RUBY_EXTERN int isnan(double);
# endif
#endif
+#ifndef HAVE_NAN
+RUBY_EXTERN double nan(const char *);
+#endif
+
#ifndef HAVE_NEXTAFTER
RUBY_EXTERN double nextafter(double x, double y);
#endif
@@ -214,12 +230,6 @@ RUBY_EXTERN char *strerror(int);
RUBY_EXTERN char *strstr(const char *, const char *);
#endif
-/*
-#ifndef HAVE_STRTOL
-RUBY_EXTERN long strtol(const char *, char **, int);
-#endif
-*/
-
#ifndef HAVE_STRLCPY
RUBY_EXTERN size_t strlcpy(char *, const char*, size_t);
#endif
diff --git a/include/ruby/onigmo.h b/include/ruby/onigmo.h
new file mode 100644
index 0000000000..385f2d6a8b
--- /dev/null
+++ b/include/ruby/onigmo.h
@@ -0,0 +1,935 @@
+#ifndef ONIGMO_H
+#define ONIGMO_H
+/**********************************************************************
+ onigmo.h - Onigmo (Oniguruma-mod) (regular expression library)
+**********************************************************************/
+/*-
+ * Copyright (c) 2002-2009 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
+ * Copyright (c) 2011-2017 K.Takata <kentkt AT csc DOT jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+# if 0
+} /* satisfy cc-mode */
+# endif
+#endif
+
+#define ONIGMO_VERSION_MAJOR 6
+#define ONIGMO_VERSION_MINOR 1
+#define ONIGMO_VERSION_TEENY 3
+
+#ifndef ONIG_EXTERN
+# ifdef RUBY_EXTERN
+# define ONIG_EXTERN RUBY_EXTERN
+# else
+# if defined(_WIN32) && !defined(__GNUC__)
+# if defined(EXPORT) || defined(RUBY_EXPORT)
+# define ONIG_EXTERN extern __declspec(dllexport)
+# else
+# define ONIG_EXTERN extern __declspec(dllimport)
+# endif
+# endif
+# endif
+#endif
+
+#ifndef ONIG_EXTERN
+# define ONIG_EXTERN extern
+#endif
+
+#ifndef RUBY
+# ifndef RUBY_SYMBOL_EXPORT_BEGIN
+# define RUBY_SYMBOL_EXPORT_BEGIN
+# define RUBY_SYMBOL_EXPORT_END
+# endif
+#endif
+
+RUBY_SYMBOL_EXPORT_BEGIN
+
+#include <stddef.h> /* for size_t */
+
+/* PART: character encoding */
+
+#ifndef ONIG_ESCAPE_UCHAR_COLLISION
+# define UChar OnigUChar
+#endif
+
+typedef unsigned char OnigUChar;
+typedef unsigned int OnigCodePoint;
+typedef unsigned int OnigCtype;
+typedef size_t OnigDistance;
+typedef ptrdiff_t OnigPosition;
+
+#define ONIG_INFINITE_DISTANCE ~((OnigDistance )0)
+
+/*
+ * Onig casefold/case mapping flags and related definitions
+ *
+ * Subfields (starting with 0 at LSB):
+ * 0-2: Code point count in casefold.h
+ * 3-12: Index into SpecialCaseMapping array in casefold.h
+ * 13-22: Case folding/mapping flags
+ */
+typedef unsigned int OnigCaseFoldType; /* case fold flag */
+
+ONIG_EXTERN OnigCaseFoldType OnigDefaultCaseFoldFlag;
+
+/* bits for actual code point count; 3 bits is more than enough, currently only 2 used */
+#define OnigCodePointMaskWidth 3
+#define OnigCodePointMask ((1<<OnigCodePointMaskWidth)-1)
+#define OnigCodePointCount(n) ((n)&OnigCodePointMask)
+#define OnigCaseFoldFlags(n) ((n)&~OnigCodePointMask)
+
+/* #define ONIGENC_CASE_FOLD_HIRAGANA_KATAKANA (1<<1) */ /* no longer usable with these values! */
+/* #define ONIGENC_CASE_FOLD_KATAKANA_WIDTH (1<<2) */ /* no longer usable with these values! */
+
+/* bits for index into table with separate titlecase mappings */
+/* 10 bits provide 1024 values */
+#define OnigSpecialIndexShift 3
+#define OnigSpecialIndexWidth 10
+
+#define ONIGENC_CASE_UPCASE (1<<13) /* has/needs uppercase mapping */
+#define ONIGENC_CASE_DOWNCASE (1<<14) /* has/needs lowercase mapping */
+#define ONIGENC_CASE_TITLECASE (1<<15) /* has/needs (special) titlecase mapping */
+#define ONIGENC_CASE_SPECIAL_OFFSET 3 /* offset in bits from ONIGENC_CASE to ONIGENC_CASE_SPECIAL */
+#define ONIGENC_CASE_UP_SPECIAL (1<<16) /* has special upcase mapping */
+#define ONIGENC_CASE_DOWN_SPECIAL (1<<17) /* has special downcase mapping */
+#define ONIGENC_CASE_MODIFIED (1<<18) /* data has been modified */
+#define ONIGENC_CASE_FOLD (1<<19) /* has/needs case folding */
+
+#define ONIGENC_CASE_FOLD_TURKISH_AZERI (1<<20) /* needs mapping specific to Turkic languages; better not change original value! */
+
+#define ONIGENC_CASE_FOLD_LITHUANIAN (1<<21) /* needs Lithuanian-specific mapping */
+#define ONIGENC_CASE_ASCII_ONLY (1<<22) /* only modify ASCII range */
+#define ONIGENC_CASE_IS_TITLECASE (1<<23) /* character itself is already titlecase */
+
+#define INTERNAL_ONIGENC_CASE_FOLD_MULTI_CHAR (1<<30) /* better not change original value! */
+
+#define ONIGENC_CASE_FOLD_MIN INTERNAL_ONIGENC_CASE_FOLD_MULTI_CHAR
+#define ONIGENC_CASE_FOLD_DEFAULT OnigDefaultCaseFoldFlag
+
+
+#define ONIGENC_MAX_COMP_CASE_FOLD_CODE_LEN 3
+#define ONIGENC_GET_CASE_FOLD_CODES_MAX_NUM 13
+/* 13 => Unicode:0x1ffc */
+
+/* code range */
+#define ONIGENC_CODE_RANGE_NUM(range) ((int )range[0])
+#define ONIGENC_CODE_RANGE_FROM(range,i) range[((i)*2) + 1]
+#define ONIGENC_CODE_RANGE_TO(range,i) range[((i)*2) + 2]
+
+typedef struct {
+ int byte_len; /* argument(original) character(s) byte length */
+ int code_len; /* number of code */
+ OnigCodePoint code[ONIGENC_MAX_COMP_CASE_FOLD_CODE_LEN];
+} OnigCaseFoldCodeItem;
+
+typedef struct {
+ OnigCodePoint esc;
+ OnigCodePoint anychar;
+ OnigCodePoint anytime;
+ OnigCodePoint zero_or_one_time;
+ OnigCodePoint one_or_more_time;
+ OnigCodePoint anychar_anytime;
+} OnigMetaCharTableType;
+
+typedef int (*OnigApplyAllCaseFoldFunc)(OnigCodePoint from, OnigCodePoint* to, int to_len, void* arg);
+
+typedef struct OnigEncodingTypeST {
+ int (*precise_mbc_enc_len)(const OnigUChar* p,const OnigUChar* e, const struct OnigEncodingTypeST* enc);
+ const char* name;
+ int max_enc_len;
+ int min_enc_len;
+ int (*is_mbc_newline)(const OnigUChar* p, const OnigUChar* end, const struct OnigEncodingTypeST* enc);
+ OnigCodePoint (*mbc_to_code)(const OnigUChar* p, const OnigUChar* end, const struct OnigEncodingTypeST* enc);
+ int (*code_to_mbclen)(OnigCodePoint code, const struct OnigEncodingTypeST* enc);
+ int (*code_to_mbc)(OnigCodePoint code, OnigUChar *buf, const struct OnigEncodingTypeST* enc);
+ int (*mbc_case_fold)(OnigCaseFoldType flag, const OnigUChar** pp, const OnigUChar* end, OnigUChar* to, const struct OnigEncodingTypeST* enc);
+ int (*apply_all_case_fold)(OnigCaseFoldType flag, OnigApplyAllCaseFoldFunc f, void* arg, const struct OnigEncodingTypeST* enc);
+ int (*get_case_fold_codes_by_str)(OnigCaseFoldType flag, const OnigUChar* p, const OnigUChar* end, OnigCaseFoldCodeItem acs[], const struct OnigEncodingTypeST* enc);
+ int (*property_name_to_ctype)(const struct OnigEncodingTypeST* enc, const OnigUChar* p, const OnigUChar* end);
+ int (*is_code_ctype)(OnigCodePoint code, OnigCtype ctype, const struct OnigEncodingTypeST* enc);
+ int (*get_ctype_code_range)(OnigCtype ctype, OnigCodePoint* sb_out, const OnigCodePoint* ranges[], const struct OnigEncodingTypeST* enc);
+ OnigUChar* (*left_adjust_char_head)(const OnigUChar* start, const OnigUChar* p, const OnigUChar* end, const struct OnigEncodingTypeST* enc);
+ int (*is_allowed_reverse_match)(const OnigUChar* p, const OnigUChar* end, const struct OnigEncodingTypeST* enc);
+ int (*case_map)(OnigCaseFoldType* flagP, const OnigUChar** pp, const OnigUChar* end, OnigUChar* to, OnigUChar* to_end, const struct OnigEncodingTypeST* enc);
+ int ruby_encoding_index;
+ unsigned int flags;
+} OnigEncodingType;
+
+typedef const OnigEncodingType* OnigEncoding;
+
+ONIG_EXTERN const OnigEncodingType OnigEncodingASCII;
+#ifndef RUBY
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_1;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_2;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_3;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_4;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_5;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_6;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_7;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_8;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_9;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_10;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_11;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_13;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_14;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_15;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_16;
+ONIG_EXTERN const OnigEncodingType OnigEncodingUTF_8;
+ONIG_EXTERN const OnigEncodingType OnigEncodingUTF_16BE;
+ONIG_EXTERN const OnigEncodingType OnigEncodingUTF_16LE;
+ONIG_EXTERN const OnigEncodingType OnigEncodingUTF_32BE;
+ONIG_EXTERN const OnigEncodingType OnigEncodingUTF_32LE;
+ONIG_EXTERN const OnigEncodingType OnigEncodingEUC_JP;
+ONIG_EXTERN const OnigEncodingType OnigEncodingEUC_TW;
+ONIG_EXTERN const OnigEncodingType OnigEncodingEUC_KR;
+ONIG_EXTERN const OnigEncodingType OnigEncodingEUC_CN;
+ONIG_EXTERN const OnigEncodingType OnigEncodingShift_JIS;
+ONIG_EXTERN const OnigEncodingType OnigEncodingWindows_31J;
+/* ONIG_EXTERN const OnigEncodingType OnigEncodingKOI8; */
+ONIG_EXTERN const OnigEncodingType OnigEncodingKOI8_R;
+ONIG_EXTERN const OnigEncodingType OnigEncodingKOI8_U;
+ONIG_EXTERN const OnigEncodingType OnigEncodingWindows_1250;
+ONIG_EXTERN const OnigEncodingType OnigEncodingWindows_1251;
+ONIG_EXTERN const OnigEncodingType OnigEncodingWindows_1252;
+ONIG_EXTERN const OnigEncodingType OnigEncodingWindows_1253;
+ONIG_EXTERN const OnigEncodingType OnigEncodingWindows_1254;
+ONIG_EXTERN const OnigEncodingType OnigEncodingWindows_1257;
+ONIG_EXTERN const OnigEncodingType OnigEncodingBIG5;
+ONIG_EXTERN const OnigEncodingType OnigEncodingGB18030;
+#endif /* RUBY */
+
+#define ONIG_ENCODING_ASCII (&OnigEncodingASCII)
+#ifndef RUBY
+# define ONIG_ENCODING_ISO_8859_1 (&OnigEncodingISO_8859_1)
+# define ONIG_ENCODING_ISO_8859_2 (&OnigEncodingISO_8859_2)
+# define ONIG_ENCODING_ISO_8859_3 (&OnigEncodingISO_8859_3)
+# define ONIG_ENCODING_ISO_8859_4 (&OnigEncodingISO_8859_4)
+# define ONIG_ENCODING_ISO_8859_5 (&OnigEncodingISO_8859_5)
+# define ONIG_ENCODING_ISO_8859_6 (&OnigEncodingISO_8859_6)
+# define ONIG_ENCODING_ISO_8859_7 (&OnigEncodingISO_8859_7)
+# define ONIG_ENCODING_ISO_8859_8 (&OnigEncodingISO_8859_8)
+# define ONIG_ENCODING_ISO_8859_9 (&OnigEncodingISO_8859_9)
+# define ONIG_ENCODING_ISO_8859_10 (&OnigEncodingISO_8859_10)
+# define ONIG_ENCODING_ISO_8859_11 (&OnigEncodingISO_8859_11)
+# define ONIG_ENCODING_ISO_8859_13 (&OnigEncodingISO_8859_13)
+# define ONIG_ENCODING_ISO_8859_14 (&OnigEncodingISO_8859_14)
+# define ONIG_ENCODING_ISO_8859_15 (&OnigEncodingISO_8859_15)
+# define ONIG_ENCODING_ISO_8859_16 (&OnigEncodingISO_8859_16)
+# define ONIG_ENCODING_UTF_8 (&OnigEncodingUTF_8)
+# define ONIG_ENCODING_UTF_16BE (&OnigEncodingUTF_16BE)
+# define ONIG_ENCODING_UTF_16LE (&OnigEncodingUTF_16LE)
+# define ONIG_ENCODING_UTF_32BE (&OnigEncodingUTF_32BE)
+# define ONIG_ENCODING_UTF_32LE (&OnigEncodingUTF_32LE)
+# define ONIG_ENCODING_EUC_JP (&OnigEncodingEUC_JP)
+# define ONIG_ENCODING_EUC_TW (&OnigEncodingEUC_TW)
+# define ONIG_ENCODING_EUC_KR (&OnigEncodingEUC_KR)
+# define ONIG_ENCODING_EUC_CN (&OnigEncodingEUC_CN)
+# define ONIG_ENCODING_SHIFT_JIS (&OnigEncodingShift_JIS)
+# define ONIG_ENCODING_WINDOWS_31J (&OnigEncodingWindows_31J)
+/* # define ONIG_ENCODING_KOI8 (&OnigEncodingKOI8) */
+# define ONIG_ENCODING_KOI8_R (&OnigEncodingKOI8_R)
+# define ONIG_ENCODING_KOI8_U (&OnigEncodingKOI8_U)
+# define ONIG_ENCODING_WINDOWS_1250 (&OnigEncodingWindows_1250)
+# define ONIG_ENCODING_WINDOWS_1251 (&OnigEncodingWindows_1251)
+# define ONIG_ENCODING_WINDOWS_1252 (&OnigEncodingWindows_1252)
+# define ONIG_ENCODING_WINDOWS_1253 (&OnigEncodingWindows_1253)
+# define ONIG_ENCODING_WINDOWS_1254 (&OnigEncodingWindows_1254)
+# define ONIG_ENCODING_WINDOWS_1257 (&OnigEncodingWindows_1257)
+# define ONIG_ENCODING_BIG5 (&OnigEncodingBIG5)
+# define ONIG_ENCODING_GB18030 (&OnigEncodingGB18030)
+
+/* old names */
+# define ONIG_ENCODING_SJIS ONIG_ENCODING_SHIFT_JIS
+# define ONIG_ENCODING_CP932 ONIG_ENCODING_WINDOWS_31J
+# define ONIG_ENCODING_CP1250 ONIG_ENCODING_WINDOWS_1250
+# define ONIG_ENCODING_CP1251 ONIG_ENCODING_WINDOWS_1251
+# define ONIG_ENCODING_CP1252 ONIG_ENCODING_WINDOWS_1252
+# define ONIG_ENCODING_CP1253 ONIG_ENCODING_WINDOWS_1253
+# define ONIG_ENCODING_CP1254 ONIG_ENCODING_WINDOWS_1254
+# define ONIG_ENCODING_CP1257 ONIG_ENCODING_WINDOWS_1257
+# define ONIG_ENCODING_UTF8 ONIG_ENCODING_UTF_8
+# define ONIG_ENCODING_UTF16_BE ONIG_ENCODING_UTF_16BE
+# define ONIG_ENCODING_UTF16_LE ONIG_ENCODING_UTF_16LE
+# define ONIG_ENCODING_UTF32_BE ONIG_ENCODING_UTF_32BE
+# define ONIG_ENCODING_UTF32_LE ONIG_ENCODING_UTF_32LE
+#endif /* RUBY */
+
+#define ONIG_ENCODING_UNDEF ((OnigEncoding )0)
+
+/* this declaration needs to be here because it is used in string.c in Ruby */
+ONIG_EXTERN
+int onigenc_ascii_only_case_map(OnigCaseFoldType* flagP, const OnigUChar** pp, const OnigUChar* end, OnigUChar* to, OnigUChar* to_end, const struct OnigEncodingTypeST* enc);
+
+
+/* work size */
+#define ONIGENC_CODE_TO_MBC_MAXLEN 7
+#define ONIGENC_MBC_CASE_FOLD_MAXLEN 18
+/* 18: 6(max-byte) * 3(case-fold chars) */
+
+/* character types */
+#define ONIGENC_CTYPE_NEWLINE 0
+#define ONIGENC_CTYPE_ALPHA 1
+#define ONIGENC_CTYPE_BLANK 2
+#define ONIGENC_CTYPE_CNTRL 3
+#define ONIGENC_CTYPE_DIGIT 4
+#define ONIGENC_CTYPE_GRAPH 5
+#define ONIGENC_CTYPE_LOWER 6
+#define ONIGENC_CTYPE_PRINT 7
+#define ONIGENC_CTYPE_PUNCT 8
+#define ONIGENC_CTYPE_SPACE 9
+#define ONIGENC_CTYPE_UPPER 10
+#define ONIGENC_CTYPE_XDIGIT 11
+#define ONIGENC_CTYPE_WORD 12
+#define ONIGENC_CTYPE_ALNUM 13 /* alpha || digit */
+#define ONIGENC_CTYPE_ASCII 14
+#define ONIGENC_MAX_STD_CTYPE ONIGENC_CTYPE_ASCII
+
+/* flags */
+#define ONIGENC_FLAG_NONE 0U
+#define ONIGENC_FLAG_UNICODE 1U
+
+#define onig_enc_len(enc,p,e) ONIGENC_MBC_ENC_LEN(enc, p, e)
+
+#define ONIGENC_IS_UNDEF(enc) ((enc) == ONIG_ENCODING_UNDEF)
+#define ONIGENC_IS_SINGLEBYTE(enc) (ONIGENC_MBC_MAXLEN(enc) == 1)
+#define ONIGENC_IS_MBC_HEAD(enc,p,e) (ONIGENC_MBC_ENC_LEN(enc,p,e) != 1)
+#define ONIGENC_IS_MBC_ASCII(p) (*(p) < 128)
+#define ONIGENC_IS_CODE_ASCII(code) ((code) < 128)
+#define ONIGENC_IS_MBC_WORD(enc,s,end) \
+ ONIGENC_IS_CODE_WORD(enc,ONIGENC_MBC_TO_CODE(enc,s,end))
+#define ONIGENC_IS_MBC_ASCII_WORD(enc,s,end) \
+ onigenc_ascii_is_code_ctype( \
+ ONIGENC_MBC_TO_CODE(enc,s,end),ONIGENC_CTYPE_WORD,enc)
+#define ONIGENC_IS_UNICODE(enc) ((enc)->flags & ONIGENC_FLAG_UNICODE)
+
+
+#define ONIGENC_NAME(enc) ((enc)->name)
+
+#define ONIGENC_MBC_CASE_FOLD(enc,flag,pp,end,buf) \
+ (enc)->mbc_case_fold(flag,(const OnigUChar** )pp,end,buf,enc)
+#define ONIGENC_IS_ALLOWED_REVERSE_MATCH(enc,s,end) \
+ (enc)->is_allowed_reverse_match(s,end,enc)
+#define ONIGENC_LEFT_ADJUST_CHAR_HEAD(enc,start,s,end) \
+ (enc)->left_adjust_char_head(start, s, end, enc)
+#define ONIGENC_APPLY_ALL_CASE_FOLD(enc,case_fold_flag,f,arg) \
+ (enc)->apply_all_case_fold(case_fold_flag,f,arg,enc)
+#define ONIGENC_GET_CASE_FOLD_CODES_BY_STR(enc,case_fold_flag,p,end,acs) \
+ (enc)->get_case_fold_codes_by_str(case_fold_flag,p,end,acs,enc)
+#define ONIGENC_STEP_BACK(enc,start,s,end,n) \
+ onigenc_step_back((enc),(start),(s),(end),(n))
+
+#define ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(n) (n)
+#define ONIGENC_MBCLEN_CHARFOUND_P(r) (0 < (r))
+#define ONIGENC_MBCLEN_CHARFOUND_LEN(r) (r)
+
+#define ONIGENC_CONSTRUCT_MBCLEN_INVALID() (-1)
+#define ONIGENC_MBCLEN_INVALID_P(r) ((r) == -1)
+
+#define ONIGENC_CONSTRUCT_MBCLEN_NEEDMORE(n) (-1-(n))
+#define ONIGENC_MBCLEN_NEEDMORE_P(r) ((r) < -1)
+#define ONIGENC_MBCLEN_NEEDMORE_LEN(r) (-1-(r))
+
+#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);
+
+#define ONIGENC_MBC_ENC_LEN(enc,p,e) onigenc_mbclen_approximate(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)
+#define ONIGENC_IS_MBC_NEWLINE(enc,p,end) (enc)->is_mbc_newline((p),(end),enc)
+#define ONIGENC_MBC_TO_CODE(enc,p,end) (enc)->mbc_to_code((p),(end),enc)
+#define ONIGENC_CODE_TO_MBCLEN(enc,code) (enc)->code_to_mbclen(code,enc)
+#define ONIGENC_CODE_TO_MBC(enc,code,buf) (enc)->code_to_mbc(code,buf,enc)
+#define ONIGENC_PROPERTY_NAME_TO_CTYPE(enc,p,end) \
+ (enc)->property_name_to_ctype(enc,p,end)
+
+#define ONIGENC_IS_CODE_CTYPE(enc,code,ctype) (enc)->is_code_ctype(code,ctype,enc)
+
+#define ONIGENC_IS_CODE_NEWLINE(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_NEWLINE)
+#define ONIGENC_IS_CODE_GRAPH(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_GRAPH)
+#define ONIGENC_IS_CODE_PRINT(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_PRINT)
+#define ONIGENC_IS_CODE_ALNUM(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_ALNUM)
+#define ONIGENC_IS_CODE_ALPHA(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_ALPHA)
+#define ONIGENC_IS_CODE_LOWER(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_LOWER)
+#define ONIGENC_IS_CODE_UPPER(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_UPPER)
+#define ONIGENC_IS_CODE_CNTRL(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_CNTRL)
+#define ONIGENC_IS_CODE_PUNCT(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_PUNCT)
+#define ONIGENC_IS_CODE_SPACE(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_SPACE)
+#define ONIGENC_IS_CODE_BLANK(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_BLANK)
+#define ONIGENC_IS_CODE_DIGIT(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_DIGIT)
+#define ONIGENC_IS_CODE_XDIGIT(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_XDIGIT)
+#define ONIGENC_IS_CODE_WORD(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_WORD)
+
+#define ONIGENC_GET_CTYPE_CODE_RANGE(enc,ctype,sbout,ranges) \
+ (enc)->get_ctype_code_range(ctype,sbout,ranges,enc)
+
+ONIG_EXTERN
+OnigUChar* onigenc_step_back(OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end, int n);
+
+
+/* encoding API */
+ONIG_EXTERN
+int onigenc_init(void);
+ONIG_EXTERN
+int onigenc_set_default_encoding(OnigEncoding enc);
+ONIG_EXTERN
+OnigEncoding onigenc_get_default_encoding(void);
+ONIG_EXTERN
+OnigUChar* onigenc_get_right_adjust_char_head_with_prev(OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end, const OnigUChar** prev);
+ONIG_EXTERN
+OnigUChar* onigenc_get_prev_char_head(OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end);
+ONIG_EXTERN
+OnigUChar* onigenc_get_left_adjust_char_head(OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end);
+ONIG_EXTERN
+OnigUChar* onigenc_get_right_adjust_char_head(OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end);
+ONIG_EXTERN
+int onigenc_strlen(OnigEncoding enc, const OnigUChar* p, const OnigUChar* end);
+ONIG_EXTERN
+int onigenc_strlen_null(OnigEncoding enc, const OnigUChar* p);
+ONIG_EXTERN
+int onigenc_str_bytelen_null(OnigEncoding enc, const OnigUChar* p);
+
+
+
+/* PART: regular expression */
+
+/* config parameters */
+#define ONIG_NREGION 10
+#define ONIG_MAX_CAPTURE_GROUP_NUM 32767
+#define ONIG_MAX_BACKREF_NUM 1000
+#define ONIG_MAX_REPEAT_NUM 100000
+#define ONIG_MAX_MULTI_BYTE_RANGES_NUM 10000
+/* constants */
+#define ONIG_MAX_ERROR_MESSAGE_LEN 90
+
+typedef unsigned int OnigOptionType;
+
+#define ONIG_OPTION_DEFAULT ONIG_OPTION_NONE
+
+/* options */
+#define ONIG_OPTION_NONE 0U
+#define ONIG_OPTION_IGNORECASE 1U
+#define ONIG_OPTION_EXTEND (ONIG_OPTION_IGNORECASE << 1)
+#define ONIG_OPTION_MULTILINE (ONIG_OPTION_EXTEND << 1)
+#define ONIG_OPTION_DOTALL ONIG_OPTION_MULTILINE
+#define ONIG_OPTION_SINGLELINE (ONIG_OPTION_MULTILINE << 1)
+#define ONIG_OPTION_FIND_LONGEST (ONIG_OPTION_SINGLELINE << 1)
+#define ONIG_OPTION_FIND_NOT_EMPTY (ONIG_OPTION_FIND_LONGEST << 1)
+#define ONIG_OPTION_NEGATE_SINGLELINE (ONIG_OPTION_FIND_NOT_EMPTY << 1)
+#define ONIG_OPTION_DONT_CAPTURE_GROUP (ONIG_OPTION_NEGATE_SINGLELINE << 1)
+#define ONIG_OPTION_CAPTURE_GROUP (ONIG_OPTION_DONT_CAPTURE_GROUP << 1)
+/* options (search time) */
+#define ONIG_OPTION_NOTBOL (ONIG_OPTION_CAPTURE_GROUP << 1)
+#define ONIG_OPTION_NOTEOL (ONIG_OPTION_NOTBOL << 1)
+#define ONIG_OPTION_NOTBOS (ONIG_OPTION_NOTEOL << 1)
+#define ONIG_OPTION_NOTEOS (ONIG_OPTION_NOTBOS << 1)
+/* options (ctype range) */
+#define ONIG_OPTION_ASCII_RANGE (ONIG_OPTION_NOTEOS << 1)
+#define ONIG_OPTION_POSIX_BRACKET_ALL_RANGE (ONIG_OPTION_ASCII_RANGE << 1)
+#define ONIG_OPTION_WORD_BOUND_ALL_RANGE (ONIG_OPTION_POSIX_BRACKET_ALL_RANGE << 1)
+/* options (newline) */
+#define ONIG_OPTION_NEWLINE_CRLF (ONIG_OPTION_WORD_BOUND_ALL_RANGE << 1)
+#define ONIG_OPTION_MAXBIT ONIG_OPTION_NEWLINE_CRLF /* limit */
+
+#define ONIG_OPTION_ON(options,regopt) ((options) |= (regopt))
+#define ONIG_OPTION_OFF(options,regopt) ((options) &= ~(regopt))
+#define ONIG_IS_OPTION_ON(options,option) ((options) & (option))
+
+/* syntax */
+typedef struct {
+ unsigned int op;
+ unsigned int op2;
+ unsigned int behavior;
+ OnigOptionType options; /* default option */
+ OnigMetaCharTableType meta_char_table;
+} OnigSyntaxType;
+
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxASIS;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxPosixBasic;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxPosixExtended;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxEmacs;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxGrep;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxGnuRegex;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxJava;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxPerl58;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxPerl58_NG;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxPerl;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxRuby;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxPython;
+
+/* predefined syntaxes (see regsyntax.c) */
+#define ONIG_SYNTAX_ASIS (&OnigSyntaxASIS)
+#define ONIG_SYNTAX_POSIX_BASIC (&OnigSyntaxPosixBasic)
+#define ONIG_SYNTAX_POSIX_EXTENDED (&OnigSyntaxPosixExtended)
+#define ONIG_SYNTAX_EMACS (&OnigSyntaxEmacs)
+#define ONIG_SYNTAX_GREP (&OnigSyntaxGrep)
+#define ONIG_SYNTAX_GNU_REGEX (&OnigSyntaxGnuRegex)
+#define ONIG_SYNTAX_JAVA (&OnigSyntaxJava)
+#define ONIG_SYNTAX_PERL58 (&OnigSyntaxPerl58)
+#define ONIG_SYNTAX_PERL58_NG (&OnigSyntaxPerl58_NG)
+#define ONIG_SYNTAX_PERL (&OnigSyntaxPerl)
+#define ONIG_SYNTAX_RUBY (&OnigSyntaxRuby)
+#define ONIG_SYNTAX_PYTHON (&OnigSyntaxPython)
+
+/* default syntax */
+ONIG_EXTERN const OnigSyntaxType* OnigDefaultSyntax;
+#define ONIG_SYNTAX_DEFAULT OnigDefaultSyntax
+
+/* syntax (operators) */
+#define ONIG_SYN_OP_VARIABLE_META_CHARACTERS (1U<<0)
+#define ONIG_SYN_OP_DOT_ANYCHAR (1U<<1) /* . */
+#define ONIG_SYN_OP_ASTERISK_ZERO_INF (1U<<2) /* * */
+#define ONIG_SYN_OP_ESC_ASTERISK_ZERO_INF (1U<<3)
+#define ONIG_SYN_OP_PLUS_ONE_INF (1U<<4) /* + */
+#define ONIG_SYN_OP_ESC_PLUS_ONE_INF (1U<<5)
+#define ONIG_SYN_OP_QMARK_ZERO_ONE (1U<<6) /* ? */
+#define ONIG_SYN_OP_ESC_QMARK_ZERO_ONE (1U<<7)
+#define ONIG_SYN_OP_BRACE_INTERVAL (1U<<8) /* {lower,upper} */
+#define ONIG_SYN_OP_ESC_BRACE_INTERVAL (1U<<9) /* \{lower,upper\} */
+#define ONIG_SYN_OP_VBAR_ALT (1U<<10) /* | */
+#define ONIG_SYN_OP_ESC_VBAR_ALT (1U<<11) /* \| */
+#define ONIG_SYN_OP_LPAREN_SUBEXP (1U<<12) /* (...) */
+#define ONIG_SYN_OP_ESC_LPAREN_SUBEXP (1U<<13) /* \(...\) */
+#define ONIG_SYN_OP_ESC_AZ_BUF_ANCHOR (1U<<14) /* \A, \Z, \z */
+#define ONIG_SYN_OP_ESC_CAPITAL_G_BEGIN_ANCHOR (1U<<15) /* \G */
+#define ONIG_SYN_OP_DECIMAL_BACKREF (1U<<16) /* \num */
+#define ONIG_SYN_OP_BRACKET_CC (1U<<17) /* [...] */
+#define ONIG_SYN_OP_ESC_W_WORD (1U<<18) /* \w, \W */
+#define ONIG_SYN_OP_ESC_LTGT_WORD_BEGIN_END (1U<<19) /* \<. \> */
+#define ONIG_SYN_OP_ESC_B_WORD_BOUND (1U<<20) /* \b, \B */
+#define ONIG_SYN_OP_ESC_S_WHITE_SPACE (1U<<21) /* \s, \S */
+#define ONIG_SYN_OP_ESC_D_DIGIT (1U<<22) /* \d, \D */
+#define ONIG_SYN_OP_LINE_ANCHOR (1U<<23) /* ^, $ */
+#define ONIG_SYN_OP_POSIX_BRACKET (1U<<24) /* [:xxxx:] */
+#define ONIG_SYN_OP_QMARK_NON_GREEDY (1U<<25) /* ??,*?,+?,{n,m}? */
+#define ONIG_SYN_OP_ESC_CONTROL_CHARS (1U<<26) /* \n,\r,\t,\a ... */
+#define ONIG_SYN_OP_ESC_C_CONTROL (1U<<27) /* \cx */
+#define ONIG_SYN_OP_ESC_OCTAL3 (1U<<28) /* \OOO */
+#define ONIG_SYN_OP_ESC_X_HEX2 (1U<<29) /* \xHH */
+#define ONIG_SYN_OP_ESC_X_BRACE_HEX8 (1U<<30) /* \x{7HHHHHHH} */
+#define ONIG_SYN_OP_ESC_O_BRACE_OCTAL (1U<<31) /* \o{OOO} */
+
+#define ONIG_SYN_OP2_ESC_CAPITAL_Q_QUOTE (1U<<0) /* \Q...\E */
+#define ONIG_SYN_OP2_QMARK_GROUP_EFFECT (1U<<1) /* (?...) */
+#define ONIG_SYN_OP2_OPTION_PERL (1U<<2) /* (?imsxadlu), (?-imsx), (?^imsxalu) */
+#define ONIG_SYN_OP2_OPTION_RUBY (1U<<3) /* (?imxadu), (?-imx) */
+#define ONIG_SYN_OP2_PLUS_POSSESSIVE_REPEAT (1U<<4) /* ?+,*+,++ */
+#define ONIG_SYN_OP2_PLUS_POSSESSIVE_INTERVAL (1U<<5) /* {n,m}+ */
+#define ONIG_SYN_OP2_CCLASS_SET_OP (1U<<6) /* [...&&..[..]..] */
+#define ONIG_SYN_OP2_QMARK_LT_NAMED_GROUP (1U<<7) /* (?<name>...) */
+#define ONIG_SYN_OP2_ESC_K_NAMED_BACKREF (1U<<8) /* \k<name> */
+#define ONIG_SYN_OP2_ESC_G_SUBEXP_CALL (1U<<9) /* \g<name>, \g<n> */
+#define ONIG_SYN_OP2_ATMARK_CAPTURE_HISTORY (1U<<10) /* (?@..),(?@<x>..) */
+#define ONIG_SYN_OP2_ESC_CAPITAL_C_BAR_CONTROL (1U<<11) /* \C-x */
+#define ONIG_SYN_OP2_ESC_CAPITAL_M_BAR_META (1U<<12) /* \M-x */
+#define ONIG_SYN_OP2_ESC_V_VTAB (1U<<13) /* \v as VTAB */
+#define ONIG_SYN_OP2_ESC_U_HEX4 (1U<<14) /* \uHHHH */
+#define ONIG_SYN_OP2_ESC_GNU_BUF_ANCHOR (1U<<15) /* \`, \' */
+#define ONIG_SYN_OP2_ESC_P_BRACE_CHAR_PROPERTY (1U<<16) /* \p{...}, \P{...} */
+#define ONIG_SYN_OP2_ESC_P_BRACE_CIRCUMFLEX_NOT (1U<<17) /* \p{^..}, \P{^..} */
+/* #define ONIG_SYN_OP2_CHAR_PROPERTY_PREFIX_IS (1U<<18) */
+#define ONIG_SYN_OP2_ESC_H_XDIGIT (1U<<19) /* \h, \H */
+#define ONIG_SYN_OP2_INEFFECTIVE_ESCAPE (1U<<20) /* \ */
+#define ONIG_SYN_OP2_ESC_CAPITAL_R_LINEBREAK (1U<<21) /* \R as (?>\x0D\x0A|[\x0A-\x0D\x{85}\x{2028}\x{2029}]) */
+#define ONIG_SYN_OP2_ESC_CAPITAL_X_EXTENDED_GRAPHEME_CLUSTER (1U<<22) /* \X */
+#define ONIG_SYN_OP2_ESC_V_VERTICAL_WHITESPACE (1U<<23) /* \v, \V -- Perl */ /* NOTIMPL */
+#define ONIG_SYN_OP2_ESC_H_HORIZONTAL_WHITESPACE (1U<<24) /* \h, \H -- Perl */ /* NOTIMPL */
+#define ONIG_SYN_OP2_ESC_CAPITAL_K_KEEP (1U<<25) /* \K */
+#define ONIG_SYN_OP2_ESC_G_BRACE_BACKREF (1U<<26) /* \g{name}, \g{n} */
+#define ONIG_SYN_OP2_QMARK_SUBEXP_CALL (1U<<27) /* (?&name), (?n), (?R), (?0) */
+#define ONIG_SYN_OP2_QMARK_VBAR_BRANCH_RESET (1U<<28) /* (?|...) */ /* NOTIMPL */
+#define ONIG_SYN_OP2_QMARK_LPAREN_CONDITION (1U<<29) /* (?(cond)yes...|no...) */
+#define ONIG_SYN_OP2_QMARK_CAPITAL_P_NAMED_GROUP (1U<<30) /* (?P<name>...), (?P=name), (?P>name) -- Python/PCRE */
+#define ONIG_SYN_OP2_QMARK_TILDE_ABSENT (1U<<31) /* (?~...) */
+/* #define ONIG_SYN_OP2_OPTION_JAVA (1U<<xx) */ /* (?idmsux), (?-idmsux) */ /* NOTIMPL */
+
+/* syntax (behavior) */
+#define ONIG_SYN_CONTEXT_INDEP_ANCHORS (1U<<31) /* not implemented */
+#define ONIG_SYN_CONTEXT_INDEP_REPEAT_OPS (1U<<0) /* ?, *, +, {n,m} */
+#define ONIG_SYN_CONTEXT_INVALID_REPEAT_OPS (1U<<1) /* error or ignore */
+#define ONIG_SYN_ALLOW_UNMATCHED_CLOSE_SUBEXP (1U<<2) /* ...)... */
+#define ONIG_SYN_ALLOW_INVALID_INTERVAL (1U<<3) /* {??? */
+#define ONIG_SYN_ALLOW_INTERVAL_LOW_ABBREV (1U<<4) /* {,n} => {0,n} */
+#define ONIG_SYN_STRICT_CHECK_BACKREF (1U<<5) /* /(\1)/,/\1()/ ..*/
+#define ONIG_SYN_DIFFERENT_LEN_ALT_LOOK_BEHIND (1U<<6) /* (?<=a|bc) */
+#define ONIG_SYN_CAPTURE_ONLY_NAMED_GROUP (1U<<7) /* see doc/RE */
+#define ONIG_SYN_ALLOW_MULTIPLEX_DEFINITION_NAME (1U<<8) /* (?<x>)(?<x>) */
+#define ONIG_SYN_FIXED_INTERVAL_IS_GREEDY_ONLY (1U<<9) /* a{n}?=(?:a{n})? */
+#define ONIG_SYN_ALLOW_MULTIPLEX_DEFINITION_NAME_CALL (1U<<10) /* (?<x>)(?<x>)(?&x) */
+#define ONIG_SYN_USE_LEFT_MOST_NAMED_GROUP (1U<<11) /* (?<x>)(?<x>)\k<x> */
+
+/* syntax (behavior) in char class [...] */
+#define ONIG_SYN_NOT_NEWLINE_IN_NEGATIVE_CC (1U<<20) /* [^...] */
+#define ONIG_SYN_BACKSLASH_ESCAPE_IN_CC (1U<<21) /* [..\w..] etc.. */
+#define ONIG_SYN_ALLOW_EMPTY_RANGE_IN_CC (1U<<22)
+#define ONIG_SYN_ALLOW_DOUBLE_RANGE_OP_IN_CC (1U<<23) /* [0-9-a]=[0-9\-a] */
+/* syntax (behavior) warning */
+#define ONIG_SYN_WARN_CC_OP_NOT_ESCAPED (1U<<24) /* [,-,] */
+#define ONIG_SYN_WARN_REDUNDANT_NESTED_REPEAT (1U<<25) /* (?:a*)+ */
+#define ONIG_SYN_WARN_CC_DUP (1U<<26) /* [aa] */
+
+/* meta character specifiers (onig_set_meta_char()) */
+#define ONIG_META_CHAR_ESCAPE 0
+#define ONIG_META_CHAR_ANYCHAR 1
+#define ONIG_META_CHAR_ANYTIME 2
+#define ONIG_META_CHAR_ZERO_OR_ONE_TIME 3
+#define ONIG_META_CHAR_ONE_OR_MORE_TIME 4
+#define ONIG_META_CHAR_ANYCHAR_ANYTIME 5
+
+#define ONIG_INEFFECTIVE_META_CHAR 0
+
+/* error codes */
+#define ONIG_IS_PATTERN_ERROR(ecode) ((ecode) <= -100 && (ecode) > -1000)
+/* normal return */
+#define ONIG_NORMAL 0
+#define ONIG_MISMATCH -1
+#define ONIG_NO_SUPPORT_CONFIG -2
+
+/* internal error */
+#define ONIGERR_MEMORY -5
+#define ONIGERR_TYPE_BUG -6
+#define ONIGERR_PARSER_BUG -11
+#define ONIGERR_STACK_BUG -12
+#define ONIGERR_UNDEFINED_BYTECODE -13
+#define ONIGERR_UNEXPECTED_BYTECODE -14
+#define ONIGERR_MATCH_STACK_LIMIT_OVER -15
+#define ONIGERR_PARSE_DEPTH_LIMIT_OVER -16
+#define ONIGERR_DEFAULT_ENCODING_IS_NOT_SET -21
+#define ONIGERR_SPECIFIED_ENCODING_CANT_CONVERT_TO_WIDE_CHAR -22
+/* general error */
+#define ONIGERR_INVALID_ARGUMENT -30
+/* syntax error */
+#define ONIGERR_END_PATTERN_AT_LEFT_BRACE -100
+#define ONIGERR_END_PATTERN_AT_LEFT_BRACKET -101
+#define ONIGERR_EMPTY_CHAR_CLASS -102
+#define ONIGERR_PREMATURE_END_OF_CHAR_CLASS -103
+#define ONIGERR_END_PATTERN_AT_ESCAPE -104
+#define ONIGERR_END_PATTERN_AT_META -105
+#define ONIGERR_END_PATTERN_AT_CONTROL -106
+#define ONIGERR_META_CODE_SYNTAX -108
+#define ONIGERR_CONTROL_CODE_SYNTAX -109
+#define ONIGERR_CHAR_CLASS_VALUE_AT_END_OF_RANGE -110
+#define ONIGERR_CHAR_CLASS_VALUE_AT_START_OF_RANGE -111
+#define ONIGERR_UNMATCHED_RANGE_SPECIFIER_IN_CHAR_CLASS -112
+#define ONIGERR_TARGET_OF_REPEAT_OPERATOR_NOT_SPECIFIED -113
+#define ONIGERR_TARGET_OF_REPEAT_OPERATOR_INVALID -114
+#define ONIGERR_NESTED_REPEAT_OPERATOR -115
+#define ONIGERR_UNMATCHED_CLOSE_PARENTHESIS -116
+#define ONIGERR_END_PATTERN_WITH_UNMATCHED_PARENTHESIS -117
+#define ONIGERR_END_PATTERN_IN_GROUP -118
+#define ONIGERR_UNDEFINED_GROUP_OPTION -119
+#define ONIGERR_INVALID_POSIX_BRACKET_TYPE -121
+#define ONIGERR_INVALID_LOOK_BEHIND_PATTERN -122
+#define ONIGERR_INVALID_REPEAT_RANGE_PATTERN -123
+#define ONIGERR_INVALID_CONDITION_PATTERN -124
+/* values error (syntax error) */
+#define ONIGERR_TOO_BIG_NUMBER -200
+#define ONIGERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE -201
+#define ONIGERR_UPPER_SMALLER_THAN_LOWER_IN_REPEAT_RANGE -202
+#define ONIGERR_EMPTY_RANGE_IN_CHAR_CLASS -203
+#define ONIGERR_MISMATCH_CODE_LENGTH_IN_CLASS_RANGE -204
+#define ONIGERR_TOO_MANY_MULTI_BYTE_RANGES -205
+#define ONIGERR_TOO_SHORT_MULTI_BYTE_STRING -206
+#define ONIGERR_TOO_BIG_BACKREF_NUMBER -207
+#define ONIGERR_INVALID_BACKREF -208
+#define ONIGERR_NUMBERED_BACKREF_OR_CALL_NOT_ALLOWED -209
+#define ONIGERR_TOO_MANY_CAPTURE_GROUPS -210
+#define ONIGERR_TOO_SHORT_DIGITS -211
+#define ONIGERR_TOO_LONG_WIDE_CHAR_VALUE -212
+#define ONIGERR_EMPTY_GROUP_NAME -214
+#define ONIGERR_INVALID_GROUP_NAME -215
+#define ONIGERR_INVALID_CHAR_IN_GROUP_NAME -216
+#define ONIGERR_UNDEFINED_NAME_REFERENCE -217
+#define ONIGERR_UNDEFINED_GROUP_REFERENCE -218
+#define ONIGERR_MULTIPLEX_DEFINED_NAME -219
+#define ONIGERR_MULTIPLEX_DEFINITION_NAME_CALL -220
+#define ONIGERR_NEVER_ENDING_RECURSION -221
+#define ONIGERR_GROUP_NUMBER_OVER_FOR_CAPTURE_HISTORY -222
+#define ONIGERR_INVALID_CHAR_PROPERTY_NAME -223
+#define ONIGERR_INVALID_CODE_POINT_VALUE -400
+#define ONIGERR_INVALID_WIDE_CHAR_VALUE -400
+#define ONIGERR_TOO_BIG_WIDE_CHAR_VALUE -401
+#define ONIGERR_NOT_SUPPORTED_ENCODING_COMBINATION -402
+#define ONIGERR_INVALID_COMBINATION_OF_OPTIONS -403
+
+/* errors related to thread */
+/* #define ONIGERR_OVER_THREAD_PASS_LIMIT_COUNT -1001 */
+
+
+/* must be smaller than BIT_STATUS_BITS_NUM (unsigned int * 8) */
+#define ONIG_MAX_CAPTURE_HISTORY_GROUP 31
+#define ONIG_IS_CAPTURE_HISTORY_GROUP(r, i) \
+ ((i) <= ONIG_MAX_CAPTURE_HISTORY_GROUP && (r)->list && (r)->list[i])
+
+typedef struct OnigCaptureTreeNodeStruct {
+ int group; /* group number */
+ OnigPosition beg;
+ OnigPosition end;
+ int allocated;
+ int num_childs;
+ struct OnigCaptureTreeNodeStruct** childs;
+} OnigCaptureTreeNode;
+
+/* match result region type */
+struct re_registers {
+ int allocated;
+ int num_regs;
+ OnigPosition* beg;
+ OnigPosition* end;
+ /* extended */
+ OnigCaptureTreeNode* history_root; /* capture history tree root */
+};
+
+/* capture tree traverse */
+#define ONIG_TRAVERSE_CALLBACK_AT_FIRST 1
+#define ONIG_TRAVERSE_CALLBACK_AT_LAST 2
+#define ONIG_TRAVERSE_CALLBACK_AT_BOTH \
+ ( ONIG_TRAVERSE_CALLBACK_AT_FIRST | ONIG_TRAVERSE_CALLBACK_AT_LAST )
+
+
+#define ONIG_REGION_NOTPOS -1
+
+typedef struct re_registers OnigRegion;
+
+typedef struct {
+ OnigEncoding enc;
+ OnigUChar* par;
+ OnigUChar* par_end;
+} OnigErrorInfo;
+
+typedef struct {
+ int lower;
+ int upper;
+} OnigRepeatRange;
+
+typedef void (*OnigWarnFunc)(const char* s);
+extern void onig_null_warn(const char* s);
+#define ONIG_NULL_WARN onig_null_warn
+
+#define ONIG_CHAR_TABLE_SIZE 256
+
+typedef struct re_pattern_buffer {
+ /* common members of BBuf(bytes-buffer) */
+ unsigned char* p; /* compiled pattern */
+ unsigned int used; /* used space for p */
+ unsigned int alloc; /* allocated space for p */
+
+ int num_mem; /* used memory(...) num counted from 1 */
+ int num_repeat; /* OP_REPEAT/OP_REPEAT_NG id-counter */
+ int num_null_check; /* OP_NULL_CHECK_START/END id counter */
+ int num_comb_exp_check; /* combination explosion check */
+ int num_call; /* number of subexp call */
+ unsigned int capture_history; /* (?@...) flag (1-31) */
+ unsigned int bt_mem_start; /* need backtrack flag */
+ unsigned int bt_mem_end; /* need backtrack flag */
+ int stack_pop_level;
+ int repeat_range_alloc;
+
+ OnigOptionType options;
+
+ OnigRepeatRange* repeat_range;
+
+ OnigEncoding enc;
+ const OnigSyntaxType* syntax;
+ void* name_table;
+ OnigCaseFoldType case_fold_flag;
+
+ /* optimization info (string search, char-map and anchors) */
+ int optimize; /* optimize flag */
+ int threshold_len; /* search str-length for apply optimize */
+ int anchor; /* BEGIN_BUF, BEGIN_POS, (SEMI_)END_BUF */
+ OnigDistance anchor_dmin; /* (SEMI_)END_BUF anchor distance */
+ OnigDistance anchor_dmax; /* (SEMI_)END_BUF anchor distance */
+ int sub_anchor; /* start-anchor for exact or map */
+ unsigned char *exact;
+ unsigned char *exact_end;
+ unsigned char map[ONIG_CHAR_TABLE_SIZE]; /* used as BM skip or char-map */
+ int *int_map; /* BM skip for exact_len > 255 */
+ int *int_map_backward; /* BM skip for backward search */
+ OnigDistance dmin; /* min-distance of exact or map */
+ OnigDistance dmax; /* max-distance of exact or map */
+
+ /* regex_t link chain */
+ struct re_pattern_buffer* chain; /* escape compile-conflict */
+} OnigRegexType;
+
+typedef OnigRegexType* OnigRegex;
+
+#ifndef ONIG_ESCAPE_REGEX_T_COLLISION
+typedef OnigRegexType regex_t;
+#endif
+
+
+typedef struct {
+ int num_of_elements;
+ OnigEncoding pattern_enc;
+ OnigEncoding target_enc;
+ const OnigSyntaxType* syntax;
+ OnigOptionType option;
+ OnigCaseFoldType case_fold_flag;
+} OnigCompileInfo;
+
+/* Oniguruma Native API */
+ONIG_EXTERN
+int onig_initialize(OnigEncoding encodings[], int n);
+ONIG_EXTERN
+int onig_init(void);
+ONIG_EXTERN
+int onig_error_code_to_str(OnigUChar* s, OnigPosition err_code, ...);
+ONIG_EXTERN
+void onig_set_warn_func(OnigWarnFunc f);
+ONIG_EXTERN
+void onig_set_verb_warn_func(OnigWarnFunc f);
+ONIG_EXTERN
+int onig_new(OnigRegex*, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigOptionType option, OnigEncoding enc, const OnigSyntaxType* syntax, OnigErrorInfo* einfo);
+ONIG_EXTERN
+int onig_reg_init(OnigRegex reg, OnigOptionType option, OnigCaseFoldType case_fold_flag, OnigEncoding enc, const OnigSyntaxType* syntax);
+ONIG_EXTERN
+int onig_new_without_alloc(OnigRegex, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigOptionType option, OnigEncoding enc, const OnigSyntaxType* syntax, OnigErrorInfo* einfo);
+ONIG_EXTERN
+int onig_new_deluxe(OnigRegex* reg, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigCompileInfo* ci, OnigErrorInfo* einfo);
+ONIG_EXTERN
+void onig_free(OnigRegex);
+ONIG_EXTERN
+void onig_free_body(OnigRegex);
+ONIG_EXTERN
+OnigPosition onig_scan(OnigRegex reg, const OnigUChar* str, const OnigUChar* end, OnigRegion* region, OnigOptionType option, int (*scan_callback)(OnigPosition, OnigPosition, OnigRegion*, void*), void* callback_arg);
+ONIG_EXTERN
+OnigPosition onig_search(OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* start, const OnigUChar* range, OnigRegion* region, OnigOptionType option);
+ONIG_EXTERN
+OnigPosition onig_search_gpos(OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* global_pos, const OnigUChar* start, const OnigUChar* range, OnigRegion* region, OnigOptionType option);
+ONIG_EXTERN
+OnigPosition onig_match(OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* at, OnigRegion* region, OnigOptionType option);
+ONIG_EXTERN
+OnigRegion* onig_region_new(void);
+ONIG_EXTERN
+void onig_region_init(OnigRegion* region);
+ONIG_EXTERN
+void onig_region_free(OnigRegion* region, int free_self);
+ONIG_EXTERN
+void onig_region_copy(OnigRegion* to, const OnigRegion* from);
+ONIG_EXTERN
+void onig_region_clear(OnigRegion* region);
+ONIG_EXTERN
+int onig_region_resize(OnigRegion* region, int n);
+ONIG_EXTERN
+int onig_region_set(OnigRegion* region, int at, int beg, int end);
+ONIG_EXTERN
+int onig_name_to_group_numbers(OnigRegex reg, const OnigUChar* name, const OnigUChar* name_end, int** nums);
+ONIG_EXTERN
+int onig_name_to_backref_number(OnigRegex reg, const OnigUChar* name, const OnigUChar* name_end, const OnigRegion *region);
+ONIG_EXTERN
+int onig_foreach_name(OnigRegex reg, int (*func)(const OnigUChar*, const OnigUChar*,int,int*,OnigRegex,void*), void* arg);
+ONIG_EXTERN
+int onig_number_of_names(const OnigRegexType *reg);
+ONIG_EXTERN
+int onig_number_of_captures(const OnigRegexType *reg);
+ONIG_EXTERN
+int onig_number_of_capture_histories(const OnigRegexType *reg);
+ONIG_EXTERN
+OnigCaptureTreeNode* onig_get_capture_tree(OnigRegion* region);
+ONIG_EXTERN
+int onig_capture_tree_traverse(OnigRegion* region, int at, int(*callback_func)(int,OnigPosition,OnigPosition,int,int,void*), void* arg);
+ONIG_EXTERN
+int onig_noname_group_capture_is_active(const OnigRegexType *reg);
+ONIG_EXTERN
+OnigEncoding onig_get_encoding(const OnigRegexType *reg);
+ONIG_EXTERN
+OnigOptionType onig_get_options(const OnigRegexType *reg);
+ONIG_EXTERN
+OnigCaseFoldType onig_get_case_fold_flag(const OnigRegexType *reg);
+ONIG_EXTERN
+const OnigSyntaxType* onig_get_syntax(const OnigRegexType *reg);
+ONIG_EXTERN
+int onig_set_default_syntax(const OnigSyntaxType* syntax);
+ONIG_EXTERN
+void onig_copy_syntax(OnigSyntaxType* to, const OnigSyntaxType* from);
+ONIG_EXTERN
+unsigned int onig_get_syntax_op(const OnigSyntaxType* syntax);
+ONIG_EXTERN
+unsigned int onig_get_syntax_op2(const OnigSyntaxType* syntax);
+ONIG_EXTERN
+unsigned int onig_get_syntax_behavior(const OnigSyntaxType* syntax);
+ONIG_EXTERN
+OnigOptionType onig_get_syntax_options(const OnigSyntaxType* syntax);
+ONIG_EXTERN
+void onig_set_syntax_op(OnigSyntaxType* syntax, unsigned int op);
+ONIG_EXTERN
+void onig_set_syntax_op2(OnigSyntaxType* syntax, unsigned int op2);
+ONIG_EXTERN
+void onig_set_syntax_behavior(OnigSyntaxType* syntax, unsigned int behavior);
+ONIG_EXTERN
+void onig_set_syntax_options(OnigSyntaxType* syntax, OnigOptionType options);
+ONIG_EXTERN
+int onig_set_meta_char(OnigSyntaxType* syntax, unsigned int what, OnigCodePoint code);
+ONIG_EXTERN
+void onig_copy_encoding(OnigEncodingType *to, OnigEncoding from);
+ONIG_EXTERN
+OnigCaseFoldType onig_get_default_case_fold_flag(void);
+ONIG_EXTERN
+int onig_set_default_case_fold_flag(OnigCaseFoldType case_fold_flag);
+ONIG_EXTERN
+unsigned int onig_get_match_stack_limit_size(void);
+ONIG_EXTERN
+int onig_set_match_stack_limit_size(unsigned int size);
+ONIG_EXTERN
+unsigned int onig_get_parse_depth_limit(void);
+ONIG_EXTERN
+int onig_set_parse_depth_limit(unsigned int depth);
+ONIG_EXTERN
+int onig_end(void);
+ONIG_EXTERN
+const char* onig_version(void);
+ONIG_EXTERN
+const char* onig_copyright(void);
+
+RUBY_SYMBOL_EXPORT_END
+
+#ifdef __cplusplus
+# if 0
+{ /* satisfy cc-mode */
+# endif
+}
+#endif
+
+#endif /* ONIGMO_H */
diff --git a/include/ruby/oniguruma.h b/include/ruby/oniguruma.h
index 1d8a0198d8..dc83754aca 100644
--- a/include/ruby/oniguruma.h
+++ b/include/ruby/oniguruma.h
@@ -1,880 +1,8 @@
#ifndef ONIGURUMA_H
#define ONIGURUMA_H
-/**********************************************************************
- oniguruma.h - Onigmo (Oniguruma-mod) (regular expression library)
-**********************************************************************/
-/*-
- * Copyright (c) 2002-2009 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
- * Copyright (c) 2011-2014 K.Takata <kentkt AT csc DOT jp>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#if 0
-} /* satisfy cc-mode */
-#endif
-#endif
-
+#include "onigmo.h"
#define ONIGURUMA
-#define ONIGURUMA_VERSION_MAJOR 5
-#define ONIGURUMA_VERSION_MINOR 15
-#define ONIGURUMA_VERSION_TEENY 0
-
-#ifdef __cplusplus
-# ifndef HAVE_PROTOTYPES
-# define HAVE_PROTOTYPES 1
-# endif
-# ifndef HAVE_STDARG_PROTOTYPES
-# define HAVE_STDARG_PROTOTYPES 1
-# endif
-#endif
-
-/* escape Mac OS X/Xcode 2.4/gcc 4.0.1 problem */
-#if defined(__APPLE__) && defined(__GNUC__) && __GNUC__ >= 4
-# ifndef HAVE_STDARG_PROTOTYPES
-# define HAVE_STDARG_PROTOTYPES 1
-# endif
-#endif
-
-#ifdef HAVE_STDARG_H
-# ifndef HAVE_STDARG_PROTOTYPES
-# define HAVE_STDARG_PROTOTYPES 1
-# endif
-#endif
-
-#ifndef P_
-#if defined(__STDC__) || defined(_WIN32)
-# define P_(args) args
-#else
-# define P_(args) ()
-#endif
-#endif
-
-#ifndef PV_
-#ifdef HAVE_STDARG_PROTOTYPES
-# define PV_(args) args
-#else
-# define PV_(args) ()
-#endif
-#endif
-
-#ifndef ONIG_EXTERN
-#ifdef RUBY_EXTERN
-#define ONIG_EXTERN RUBY_EXTERN
-#else
-#if defined(_WIN32) && !defined(__GNUC__)
-#if defined(EXPORT) || defined(RUBY_EXPORT)
-#define ONIG_EXTERN extern __declspec(dllexport)
-#else
-#define ONIG_EXTERN extern __declspec(dllimport)
-#endif
-#endif
-#endif
-#endif
-
-#ifndef ONIG_EXTERN
-#define ONIG_EXTERN extern
-#endif
-
-RUBY_SYMBOL_EXPORT_BEGIN
-
-#include <stddef.h> /* for size_t */
-
-/* PART: character encoding */
-
-#ifndef ONIG_ESCAPE_UCHAR_COLLISION
-#define UChar OnigUChar
-#endif
-
-typedef unsigned char OnigUChar;
-typedef unsigned int OnigCodePoint;
-typedef unsigned int OnigCtype;
-typedef size_t OnigDistance;
-typedef ptrdiff_t OnigPosition;
-
-#define ONIG_INFINITE_DISTANCE ~((OnigDistance )0)
-
-/*
- * Onig casefold/case mapping flags and related definitions
- *
- * Subfields (starting with 0 at LSB):
- * 0-2: Code point count in casefold.h
- * 3-12: Index into SpecialCaseMapping array in casefold.h
- * 13-22: Case folding/mapping flags
- */
-typedef unsigned int OnigCaseFoldType; /* case fold flag */
-
-ONIG_EXTERN OnigCaseFoldType OnigDefaultCaseFoldFlag;
-
-/* bits for actual code point count; 3 bits is more than enough, currently only 2 used */
-#define OnigCodePointMaskWidth 3
-#define OnigCodePointMask ((1<<OnigCodePointMaskWidth)-1)
-#define OnigCodePointCount(n) ((n)&OnigCodePointMask)
-#define OnigCaseFoldFlags(n) ((n)&~OnigCodePointMask)
-
-/* #define ONIGENC_CASE_FOLD_HIRAGANA_KATAKANA (1<<1) */ /* no longer usable with these values! */
-/* #define ONIGENC_CASE_FOLD_KATAKANA_WIDTH (1<<2) */ /* no longer usable with these values! */
-
-/* bits for index into table with separate titlecase mappings */
-/* 10 bits provide 1024 values */
-#define OnigSpecialIndexShift 3
-#define OnigSpecialIndexWidth 10
-
-#define ONIGENC_CASE_UPCASE (1<<13) /* has/needs uppercase mapping */
-#define ONIGENC_CASE_DOWNCASE (1<<14) /* has/needs lowercase mapping */
-#define ONIGENC_CASE_TITLECASE (1<<15) /* has/needs (special) titlecase mapping */
-#define ONIGENC_CASE_SPECIAL_OFFSET 3 /* offset in bytes from ONIGENC_CASE to ONIGENC_CASE_SPECIAL */
-#define ONIGENC_CASE_UP_SPECIAL (1<<16) /* has special upcase mapping */
-#define ONIGENC_CASE_DOWN_SPECIAL (1<<17) /* has special downcase mapping */
-#define ONIGENC_CASE_MODIFIED (1<<18) /* data has been modified */
-#define ONIGENC_CASE_FOLD (1<<19) /* has/needs case folding */
-
-#define ONIGENC_CASE_FOLD_TURKISH_AZERI (1<<20) /* needs mapping specific to Turkic languages; better not change original value! */
-
-#define ONIGENC_CASE_FOLD_LITHUANIAN (1<<21) /* needs Lithuanian-specific mapping */
-#define ONIGENC_CASE_ASCII_ONLY (1<<22) /* only modify ASCII range */
-#define ONIGENC_CASE_IS_TITLECASE (1<<23) /* character itself is already titlecase */
-
-#define INTERNAL_ONIGENC_CASE_FOLD_MULTI_CHAR (1<<30) /* better not change original value! */
-
-#define ONIGENC_CASE_FOLD_MIN INTERNAL_ONIGENC_CASE_FOLD_MULTI_CHAR
-#define ONIGENC_CASE_FOLD_DEFAULT OnigDefaultCaseFoldFlag
-
-
-#define ONIGENC_MAX_COMP_CASE_FOLD_CODE_LEN 3
-#define ONIGENC_GET_CASE_FOLD_CODES_MAX_NUM 13
-/* 13 => Unicode:0x1ffc */
-
-/* code range */
-#define ONIGENC_CODE_RANGE_NUM(range) ((int )range[0])
-#define ONIGENC_CODE_RANGE_FROM(range,i) range[((i)*2) + 1]
-#define ONIGENC_CODE_RANGE_TO(range,i) range[((i)*2) + 2]
-
-typedef struct {
- int byte_len; /* argument(original) character(s) byte length */
- int code_len; /* number of code */
- OnigCodePoint code[ONIGENC_MAX_COMP_CASE_FOLD_CODE_LEN];
-} OnigCaseFoldCodeItem;
-
-typedef struct {
- OnigCodePoint esc;
- OnigCodePoint anychar;
- OnigCodePoint anytime;
- OnigCodePoint zero_or_one_time;
- OnigCodePoint one_or_more_time;
- OnigCodePoint anychar_anytime;
-} OnigMetaCharTableType;
-
-typedef int (*OnigApplyAllCaseFoldFunc)(OnigCodePoint from, OnigCodePoint* to, int to_len, void* arg);
-
-typedef struct OnigEncodingTypeST {
- int (*precise_mbc_enc_len)(const OnigUChar* p,const OnigUChar* e, const struct OnigEncodingTypeST* enc);
- const char* name;
- int max_enc_len;
- int min_enc_len;
- int (*is_mbc_newline)(const OnigUChar* p, const OnigUChar* end, const struct OnigEncodingTypeST* enc);
- OnigCodePoint (*mbc_to_code)(const OnigUChar* p, const OnigUChar* end, const struct OnigEncodingTypeST* enc);
- int (*code_to_mbclen)(OnigCodePoint code, const struct OnigEncodingTypeST* enc);
- int (*code_to_mbc)(OnigCodePoint code, OnigUChar *buf, const struct OnigEncodingTypeST* enc);
- int (*mbc_case_fold)(OnigCaseFoldType flag, const OnigUChar** pp, const OnigUChar* end, OnigUChar* to, const struct OnigEncodingTypeST* enc);
- int (*apply_all_case_fold)(OnigCaseFoldType flag, OnigApplyAllCaseFoldFunc f, void* arg, const struct OnigEncodingTypeST* enc);
- int (*get_case_fold_codes_by_str)(OnigCaseFoldType flag, const OnigUChar* p, const OnigUChar* end, OnigCaseFoldCodeItem acs[], const struct OnigEncodingTypeST* enc);
- int (*property_name_to_ctype)(const struct OnigEncodingTypeST* enc, const OnigUChar* p, const OnigUChar* end);
- int (*is_code_ctype)(OnigCodePoint code, OnigCtype ctype, const struct OnigEncodingTypeST* enc);
- int (*get_ctype_code_range)(OnigCtype ctype, OnigCodePoint* sb_out, const OnigCodePoint* ranges[], const struct OnigEncodingTypeST* enc);
- OnigUChar* (*left_adjust_char_head)(const OnigUChar* start, const OnigUChar* p, const OnigUChar* end, const struct OnigEncodingTypeST* enc);
- int (*is_allowed_reverse_match)(const OnigUChar* p, const OnigUChar* end, const struct OnigEncodingTypeST* enc);
- int ruby_encoding_index;
- unsigned int flags;
- int (*case_map)(OnigCaseFoldType* flagP, const OnigUChar** pp, const OnigUChar* end, OnigUChar* to, OnigUChar* to_end, const struct OnigEncodingTypeST* enc);
-} OnigEncodingType;
-
-typedef const OnigEncodingType* OnigEncoding;
-
-ONIG_EXTERN const OnigEncodingType OnigEncodingASCII;
-
-#define ONIG_ENCODING_ASCII (&OnigEncodingASCII)
-
-#define ONIG_ENCODING_UNDEF ((OnigEncoding )0)
-
-/* this declaration needs to be here because it is used in string.c */
-ONIG_EXTERN int onigenc_ascii_only_case_map P_((OnigCaseFoldType* flagP,
- const OnigUChar** pp, const OnigUChar* end,
- OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc));
-
-
-/* work size */
-#define ONIGENC_CODE_TO_MBC_MAXLEN 7
-#define ONIGENC_MBC_CASE_FOLD_MAXLEN 18
-/* 18: 6(max-byte) * 3(case-fold chars) */
-
-/* character types */
-#define ONIGENC_CTYPE_NEWLINE 0
-#define ONIGENC_CTYPE_ALPHA 1
-#define ONIGENC_CTYPE_BLANK 2
-#define ONIGENC_CTYPE_CNTRL 3
-#define ONIGENC_CTYPE_DIGIT 4
-#define ONIGENC_CTYPE_GRAPH 5
-#define ONIGENC_CTYPE_LOWER 6
-#define ONIGENC_CTYPE_PRINT 7
-#define ONIGENC_CTYPE_PUNCT 8
-#define ONIGENC_CTYPE_SPACE 9
-#define ONIGENC_CTYPE_UPPER 10
-#define ONIGENC_CTYPE_XDIGIT 11
-#define ONIGENC_CTYPE_WORD 12
-#define ONIGENC_CTYPE_ALNUM 13 /* alpha || digit */
-#define ONIGENC_CTYPE_ASCII 14
-#define ONIGENC_MAX_STD_CTYPE ONIGENC_CTYPE_ASCII
-
-/* flags */
-#define ONIGENC_FLAG_NONE 0U
-#define ONIGENC_FLAG_UNICODE 1U
-
-#define onig_enc_len(enc,p,e) ONIGENC_MBC_ENC_LEN(enc, p, e)
-
-#define ONIGENC_IS_UNDEF(enc) ((enc) == ONIG_ENCODING_UNDEF)
-#define ONIGENC_IS_SINGLEBYTE(enc) (ONIGENC_MBC_MAXLEN(enc) == 1)
-#define ONIGENC_IS_MBC_HEAD(enc,p,e) (ONIGENC_MBC_ENC_LEN(enc,p,e) != 1)
-#define ONIGENC_IS_MBC_ASCII(p) (*(p) < 128)
-#define ONIGENC_IS_CODE_ASCII(code) ((code) < 128)
-#define ONIGENC_IS_MBC_WORD(enc,s,end) \
- ONIGENC_IS_CODE_WORD(enc,ONIGENC_MBC_TO_CODE(enc,s,end))
-#define ONIGENC_IS_MBC_ASCII_WORD(enc,s,end) \
- onigenc_ascii_is_code_ctype( \
- ONIGENC_MBC_TO_CODE(enc,s,end),ONIGENC_CTYPE_WORD,enc)
-#define ONIGENC_IS_UNICODE(enc) ((enc)->flags & ONIGENC_FLAG_UNICODE)
-
-
-#define ONIGENC_NAME(enc) ((enc)->name)
-
-#define ONIGENC_MBC_CASE_FOLD(enc,flag,pp,end,buf) \
- (enc)->mbc_case_fold(flag,(const OnigUChar** )pp,end,buf,enc)
-#define ONIGENC_IS_ALLOWED_REVERSE_MATCH(enc,s,end) \
- (enc)->is_allowed_reverse_match(s,end,enc)
-#define ONIGENC_LEFT_ADJUST_CHAR_HEAD(enc,start,s,end) \
- (enc)->left_adjust_char_head(start, s, end, enc)
-#define ONIGENC_APPLY_ALL_CASE_FOLD(enc,case_fold_flag,f,arg) \
- (enc)->apply_all_case_fold(case_fold_flag,f,arg,enc)
-#define ONIGENC_GET_CASE_FOLD_CODES_BY_STR(enc,case_fold_flag,p,end,acs) \
- (enc)->get_case_fold_codes_by_str(case_fold_flag,p,end,acs,enc)
-#define ONIGENC_STEP_BACK(enc,start,s,end,n) \
- onigenc_step_back((enc),(start),(s),(end),(n))
-
-#define ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(n) (n)
-#define ONIGENC_MBCLEN_CHARFOUND_P(r) (0 < (r))
-#define ONIGENC_MBCLEN_CHARFOUND_LEN(r) (r)
-
-#define ONIGENC_CONSTRUCT_MBCLEN_INVALID() (-1)
-#define ONIGENC_MBCLEN_INVALID_P(r) ((r) == -1)
-
-#define ONIGENC_CONSTRUCT_MBCLEN_NEEDMORE(n) (-1-(n))
-#define ONIGENC_MBCLEN_NEEDMORE_P(r) ((r) < -1)
-#define ONIGENC_MBCLEN_NEEDMORE_LEN(r) (-1-(r))
-
-#define ONIGENC_PRECISE_MBC_ENC_LEN(enc,p,e) (enc)->precise_mbc_enc_len(p,e,enc)
-
-ONIG_EXTERN
-int onigenc_mbclen_approximate P_((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_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)
-#define ONIGENC_IS_MBC_NEWLINE(enc,p,end) (enc)->is_mbc_newline((p),(end),enc)
-#define ONIGENC_MBC_TO_CODE(enc,p,end) (enc)->mbc_to_code((p),(end),enc)
-#define ONIGENC_CODE_TO_MBCLEN(enc,code) (enc)->code_to_mbclen(code,enc)
-#define ONIGENC_CODE_TO_MBC(enc,code,buf) (enc)->code_to_mbc(code,buf,enc)
-#define ONIGENC_PROPERTY_NAME_TO_CTYPE(enc,p,end) \
- (enc)->property_name_to_ctype(enc,p,end)
-
-#define ONIGENC_IS_CODE_CTYPE(enc,code,ctype) (enc)->is_code_ctype(code,ctype,enc)
-
-#define ONIGENC_IS_CODE_NEWLINE(enc,code) \
- ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_NEWLINE)
-#define ONIGENC_IS_CODE_GRAPH(enc,code) \
- ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_GRAPH)
-#define ONIGENC_IS_CODE_PRINT(enc,code) \
- ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_PRINT)
-#define ONIGENC_IS_CODE_ALNUM(enc,code) \
- ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_ALNUM)
-#define ONIGENC_IS_CODE_ALPHA(enc,code) \
- ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_ALPHA)
-#define ONIGENC_IS_CODE_LOWER(enc,code) \
- ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_LOWER)
-#define ONIGENC_IS_CODE_UPPER(enc,code) \
- ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_UPPER)
-#define ONIGENC_IS_CODE_CNTRL(enc,code) \
- ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_CNTRL)
-#define ONIGENC_IS_CODE_PUNCT(enc,code) \
- ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_PUNCT)
-#define ONIGENC_IS_CODE_SPACE(enc,code) \
- ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_SPACE)
-#define ONIGENC_IS_CODE_BLANK(enc,code) \
- ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_BLANK)
-#define ONIGENC_IS_CODE_DIGIT(enc,code) \
- ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_DIGIT)
-#define ONIGENC_IS_CODE_XDIGIT(enc,code) \
- ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_XDIGIT)
-#define ONIGENC_IS_CODE_WORD(enc,code) \
- ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_WORD)
-
-#define ONIGENC_GET_CTYPE_CODE_RANGE(enc,ctype,sbout,ranges) \
- (enc)->get_ctype_code_range(ctype,sbout,ranges,enc)
-
-ONIG_EXTERN
-OnigUChar* onigenc_step_back P_((OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end, int n));
-
-
-/* encoding API */
-ONIG_EXTERN
-int onigenc_init P_((void));
-ONIG_EXTERN
-int onigenc_set_default_encoding P_((OnigEncoding enc));
-PUREFUNC(ONIG_EXTERN OnigEncoding onigenc_get_default_encoding P_((void)));
-PUREFUNC(ONIG_EXTERN void onigenc_set_default_caseconv_table P_((const OnigUChar* table)));
-ONIG_EXTERN
-OnigUChar* onigenc_get_right_adjust_char_head_with_prev P_((OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end, const OnigUChar** prev));
-ONIG_EXTERN
-OnigUChar* onigenc_get_prev_char_head P_((OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end));
-ONIG_EXTERN
-OnigUChar* onigenc_get_left_adjust_char_head P_((OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end));
-ONIG_EXTERN
-OnigUChar* onigenc_get_right_adjust_char_head P_((OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end));
-ONIG_EXTERN
-int onigenc_strlen P_((OnigEncoding enc, const OnigUChar* p, const OnigUChar* end));
-ONIG_EXTERN
-int onigenc_strlen_null P_((OnigEncoding enc, const OnigUChar* p));
-ONIG_EXTERN
-int onigenc_str_bytelen_null P_((OnigEncoding enc, const OnigUChar* p));
-
-
-
-/* PART: regular expression */
-
-/* config parameters */
-#define ONIG_NREGION 10
-#define ONIG_MAX_BACKREF_NUM 1000
-#define ONIG_MAX_CAPTURE_GROUP_NUM 32767
-#define ONIG_MAX_REPEAT_NUM 100000
-#define ONIG_MAX_MULTI_BYTE_RANGES_NUM 10000
-/* constants */
-#define ONIG_MAX_ERROR_MESSAGE_LEN 90
-
-typedef unsigned int OnigOptionType;
-
-#define ONIG_OPTION_DEFAULT ONIG_OPTION_NONE
-
-/* options */
-#define ONIG_OPTION_NONE 0U
-#define ONIG_OPTION_IGNORECASE 1U
-#define ONIG_OPTION_EXTEND (ONIG_OPTION_IGNORECASE << 1)
-#define ONIG_OPTION_MULTILINE (ONIG_OPTION_EXTEND << 1)
-#define ONIG_OPTION_DOTALL ONIG_OPTION_MULTILINE
-#define ONIG_OPTION_SINGLELINE (ONIG_OPTION_MULTILINE << 1)
-#define ONIG_OPTION_FIND_LONGEST (ONIG_OPTION_SINGLELINE << 1)
-#define ONIG_OPTION_FIND_NOT_EMPTY (ONIG_OPTION_FIND_LONGEST << 1)
-#define ONIG_OPTION_NEGATE_SINGLELINE (ONIG_OPTION_FIND_NOT_EMPTY << 1)
-#define ONIG_OPTION_DONT_CAPTURE_GROUP (ONIG_OPTION_NEGATE_SINGLELINE << 1)
-#define ONIG_OPTION_CAPTURE_GROUP (ONIG_OPTION_DONT_CAPTURE_GROUP << 1)
-/* options (search time) */
-#define ONIG_OPTION_NOTBOL (ONIG_OPTION_CAPTURE_GROUP << 1)
-#define ONIG_OPTION_NOTEOL (ONIG_OPTION_NOTBOL << 1)
-#define ONIG_OPTION_POSIX_REGION (ONIG_OPTION_NOTEOL << 1)
-/* options (ctype range) */
-#define ONIG_OPTION_ASCII_RANGE (ONIG_OPTION_POSIX_REGION << 1)
-#define ONIG_OPTION_POSIX_BRACKET_ALL_RANGE (ONIG_OPTION_ASCII_RANGE << 1)
-#define ONIG_OPTION_WORD_BOUND_ALL_RANGE (ONIG_OPTION_POSIX_BRACKET_ALL_RANGE << 1)
-/* options (newline) */
-#define ONIG_OPTION_NEWLINE_CRLF (ONIG_OPTION_WORD_BOUND_ALL_RANGE << 1)
-#define ONIG_OPTION_NOTBOS (ONIG_OPTION_NEWLINE_CRLF << 1)
-#define ONIG_OPTION_NOTEOS (ONIG_OPTION_NOTBOS << 1)
-#define ONIG_OPTION_MAXBIT ONIG_OPTION_NOTEOS /* limit */
-
-#define ONIG_OPTION_ON(options,regopt) ((options) |= (regopt))
-#define ONIG_OPTION_OFF(options,regopt) ((options) &= ~(regopt))
-#define ONIG_IS_OPTION_ON(options,option) ((options) & (option))
-
-/* syntax */
-typedef struct {
- unsigned int op;
- unsigned int op2;
- unsigned int behavior;
- OnigOptionType options; /* default option */
- OnigMetaCharTableType meta_char_table;
-} OnigSyntaxType;
-
-ONIG_EXTERN const OnigSyntaxType OnigSyntaxASIS;
-ONIG_EXTERN const OnigSyntaxType OnigSyntaxPosixBasic;
-ONIG_EXTERN const OnigSyntaxType OnigSyntaxPosixExtended;
-ONIG_EXTERN const OnigSyntaxType OnigSyntaxEmacs;
-ONIG_EXTERN const OnigSyntaxType OnigSyntaxGrep;
-ONIG_EXTERN const OnigSyntaxType OnigSyntaxGnuRegex;
-ONIG_EXTERN const OnigSyntaxType OnigSyntaxJava;
-ONIG_EXTERN const OnigSyntaxType OnigSyntaxPerl58;
-ONIG_EXTERN const OnigSyntaxType OnigSyntaxPerl58_NG;
-ONIG_EXTERN const OnigSyntaxType OnigSyntaxPerl;
-ONIG_EXTERN const OnigSyntaxType OnigSyntaxRuby;
-ONIG_EXTERN const OnigSyntaxType OnigSyntaxPython;
-
-/* predefined syntaxes (see regsyntax.c) */
-#define ONIG_SYNTAX_ASIS (&OnigSyntaxASIS)
-#define ONIG_SYNTAX_POSIX_BASIC (&OnigSyntaxPosixBasic)
-#define ONIG_SYNTAX_POSIX_EXTENDED (&OnigSyntaxPosixExtended)
-#define ONIG_SYNTAX_EMACS (&OnigSyntaxEmacs)
-#define ONIG_SYNTAX_GREP (&OnigSyntaxGrep)
-#define ONIG_SYNTAX_GNU_REGEX (&OnigSyntaxGnuRegex)
-#define ONIG_SYNTAX_JAVA (&OnigSyntaxJava)
-#define ONIG_SYNTAX_PERL58 (&OnigSyntaxPerl58)
-#define ONIG_SYNTAX_PERL58_NG (&OnigSyntaxPerl58_NG)
-#define ONIG_SYNTAX_PERL (&OnigSyntaxPerl)
-#define ONIG_SYNTAX_RUBY (&OnigSyntaxRuby)
-#define ONIG_SYNTAX_PYTHON (&OnigSyntaxPython)
-
-/* default syntax */
-ONIG_EXTERN const OnigSyntaxType* OnigDefaultSyntax;
-#define ONIG_SYNTAX_DEFAULT OnigDefaultSyntax
-
-/* syntax (operators) */
-#define ONIG_SYN_OP_VARIABLE_META_CHARACTERS (1U<<0)
-#define ONIG_SYN_OP_DOT_ANYCHAR (1U<<1) /* . */
-#define ONIG_SYN_OP_ASTERISK_ZERO_INF (1U<<2) /* * */
-#define ONIG_SYN_OP_ESC_ASTERISK_ZERO_INF (1U<<3)
-#define ONIG_SYN_OP_PLUS_ONE_INF (1U<<4) /* + */
-#define ONIG_SYN_OP_ESC_PLUS_ONE_INF (1U<<5)
-#define ONIG_SYN_OP_QMARK_ZERO_ONE (1U<<6) /* ? */
-#define ONIG_SYN_OP_ESC_QMARK_ZERO_ONE (1U<<7)
-#define ONIG_SYN_OP_BRACE_INTERVAL (1U<<8) /* {lower,upper} */
-#define ONIG_SYN_OP_ESC_BRACE_INTERVAL (1U<<9) /* \{lower,upper\} */
-#define ONIG_SYN_OP_VBAR_ALT (1U<<10) /* | */
-#define ONIG_SYN_OP_ESC_VBAR_ALT (1U<<11) /* \| */
-#define ONIG_SYN_OP_LPAREN_SUBEXP (1U<<12) /* (...) */
-#define ONIG_SYN_OP_ESC_LPAREN_SUBEXP (1U<<13) /* \(...\) */
-#define ONIG_SYN_OP_ESC_AZ_BUF_ANCHOR (1U<<14) /* \A, \Z, \z */
-#define ONIG_SYN_OP_ESC_CAPITAL_G_BEGIN_ANCHOR (1U<<15) /* \G */
-#define ONIG_SYN_OP_DECIMAL_BACKREF (1U<<16) /* \num */
-#define ONIG_SYN_OP_BRACKET_CC (1U<<17) /* [...] */
-#define ONIG_SYN_OP_ESC_W_WORD (1U<<18) /* \w, \W */
-#define ONIG_SYN_OP_ESC_LTGT_WORD_BEGIN_END (1U<<19) /* \<. \> */
-#define ONIG_SYN_OP_ESC_B_WORD_BOUND (1U<<20) /* \b, \B */
-#define ONIG_SYN_OP_ESC_S_WHITE_SPACE (1U<<21) /* \s, \S */
-#define ONIG_SYN_OP_ESC_D_DIGIT (1U<<22) /* \d, \D */
-#define ONIG_SYN_OP_LINE_ANCHOR (1U<<23) /* ^, $ */
-#define ONIG_SYN_OP_POSIX_BRACKET (1U<<24) /* [:xxxx:] */
-#define ONIG_SYN_OP_QMARK_NON_GREEDY (1U<<25) /* ??,*?,+?,{n,m}? */
-#define ONIG_SYN_OP_ESC_CONTROL_CHARS (1U<<26) /* \n,\r,\t,\a ... */
-#define ONIG_SYN_OP_ESC_C_CONTROL (1U<<27) /* \cx */
-#define ONIG_SYN_OP_ESC_OCTAL3 (1U<<28) /* \OOO */
-#define ONIG_SYN_OP_ESC_X_HEX2 (1U<<29) /* \xHH */
-#define ONIG_SYN_OP_ESC_X_BRACE_HEX8 (1U<<30) /* \x{7HHHHHHH} */
-#define ONIG_SYN_OP_ESC_O_BRACE_OCTAL (1U<<31) /* \o{OOO} */ /* NOTIMPL */
-
-#define ONIG_SYN_OP2_ESC_CAPITAL_Q_QUOTE (1U<<0) /* \Q...\E */
-#define ONIG_SYN_OP2_QMARK_GROUP_EFFECT (1U<<1) /* (?...) */
-#define ONIG_SYN_OP2_OPTION_PERL (1U<<2) /* (?imsxadlu), (?-imsx), (?^imsxalu) */
-#define ONIG_SYN_OP2_OPTION_RUBY (1U<<3) /* (?imxadu), (?-imx) */
-#define ONIG_SYN_OP2_PLUS_POSSESSIVE_REPEAT (1U<<4) /* ?+,*+,++ */
-#define ONIG_SYN_OP2_PLUS_POSSESSIVE_INTERVAL (1U<<5) /* {n,m}+ */
-#define ONIG_SYN_OP2_CCLASS_SET_OP (1U<<6) /* [...&&..[..]..] */
-#define ONIG_SYN_OP2_QMARK_LT_NAMED_GROUP (1U<<7) /* (?<name>...) */
-#define ONIG_SYN_OP2_ESC_K_NAMED_BACKREF (1U<<8) /* \k<name> */
-#define ONIG_SYN_OP2_ESC_G_SUBEXP_CALL (1U<<9) /* \g<name>, \g<n> */
-#define ONIG_SYN_OP2_ATMARK_CAPTURE_HISTORY (1U<<10) /* (?@..),(?@<x>..) */
-#define ONIG_SYN_OP2_ESC_CAPITAL_C_BAR_CONTROL (1U<<11) /* \C-x */
-#define ONIG_SYN_OP2_ESC_CAPITAL_M_BAR_META (1U<<12) /* \M-x */
-#define ONIG_SYN_OP2_ESC_V_VTAB (1U<<13) /* \v as VTAB */
-#define ONIG_SYN_OP2_ESC_U_HEX4 (1U<<14) /* \uHHHH */
-#define ONIG_SYN_OP2_ESC_GNU_BUF_ANCHOR (1U<<15) /* \`, \' */
-#define ONIG_SYN_OP2_ESC_P_BRACE_CHAR_PROPERTY (1U<<16) /* \p{...}, \P{...} */
-#define ONIG_SYN_OP2_ESC_P_BRACE_CIRCUMFLEX_NOT (1U<<17) /* \p{^..}, \P{^..} */
-/* #define ONIG_SYN_OP2_CHAR_PROPERTY_PREFIX_IS (1U<<18) */
-#define ONIG_SYN_OP2_ESC_H_XDIGIT (1U<<19) /* \h, \H */
-#define ONIG_SYN_OP2_INEFFECTIVE_ESCAPE (1U<<20) /* \ */
-#define ONIG_SYN_OP2_ESC_CAPITAL_R_LINEBREAK (1U<<21) /* \R as (?>\x0D\x0A|[\x0A-\x0D\x{85}\x{2028}\x{2029}]) */
-#define ONIG_SYN_OP2_ESC_CAPITAL_X_EXTENDED_GRAPHEME_CLUSTER (1U<<22) /* \X as (?>\P{M}\p{M}*) */
-#define ONIG_SYN_OP2_ESC_V_VERTICAL_WHITESPACE (1U<<23) /* \v, \V -- Perl */ /* NOTIMPL */
-#define ONIG_SYN_OP2_ESC_H_HORIZONTAL_WHITESPACE (1U<<24) /* \h, \H -- Perl */ /* NOTIMPL */
-#define ONIG_SYN_OP2_ESC_CAPITAL_K_KEEP (1U<<25) /* \K */
-#define ONIG_SYN_OP2_ESC_G_BRACE_BACKREF (1U<<26) /* \g{name}, \g{n} */
-#define ONIG_SYN_OP2_QMARK_SUBEXP_CALL (1U<<27) /* (?&name), (?n), (?R), (?0) */
-#define ONIG_SYN_OP2_QMARK_VBAR_BRANCH_RESET (1U<<28) /* (?|...) */ /* NOTIMPL */
-#define ONIG_SYN_OP2_QMARK_LPAREN_CONDITION (1U<<29) /* (?(cond)yes...|no...) */
-#define ONIG_SYN_OP2_QMARK_CAPITAL_P_NAMED_GROUP (1U<<30) /* (?P<name>...), (?P=name), (?P>name) -- Python/PCRE */
-#define ONIG_SYN_OP2_OPTION_JAVA (1U<<31) /* (?idmsux), (?-idmsux) */ /* NOTIMPL */
-
-/* syntax (behavior) */
-#define ONIG_SYN_CONTEXT_INDEP_ANCHORS (1U<<31) /* not implemented */
-#define ONIG_SYN_CONTEXT_INDEP_REPEAT_OPS (1U<<0) /* ?, *, +, {n,m} */
-#define ONIG_SYN_CONTEXT_INVALID_REPEAT_OPS (1U<<1) /* error or ignore */
-#define ONIG_SYN_ALLOW_UNMATCHED_CLOSE_SUBEXP (1U<<2) /* ...)... */
-#define ONIG_SYN_ALLOW_INVALID_INTERVAL (1U<<3) /* {??? */
-#define ONIG_SYN_ALLOW_INTERVAL_LOW_ABBREV (1U<<4) /* {,n} => {0,n} */
-#define ONIG_SYN_STRICT_CHECK_BACKREF (1U<<5) /* /(\1)/,/\1()/ ..*/
-#define ONIG_SYN_DIFFERENT_LEN_ALT_LOOK_BEHIND (1U<<6) /* (?<=a|bc) */
-#define ONIG_SYN_CAPTURE_ONLY_NAMED_GROUP (1U<<7) /* see doc/RE */
-#define ONIG_SYN_ALLOW_MULTIPLEX_DEFINITION_NAME (1U<<8) /* (?<x>)(?<x>) */
-#define ONIG_SYN_FIXED_INTERVAL_IS_GREEDY_ONLY (1U<<9) /* a{n}?=(?:a{n})? */
-#define ONIG_SYN_ALLOW_MULTIPLEX_DEFINITION_NAME_CALL (1U<<10) /* (?<x>)(?<x>)(?&x) */
-
-/* syntax (behavior) in char class [...] */
-#define ONIG_SYN_NOT_NEWLINE_IN_NEGATIVE_CC (1U<<20) /* [^...] */
-#define ONIG_SYN_BACKSLASH_ESCAPE_IN_CC (1U<<21) /* [..\w..] etc.. */
-#define ONIG_SYN_ALLOW_EMPTY_RANGE_IN_CC (1U<<22)
-#define ONIG_SYN_ALLOW_DOUBLE_RANGE_OP_IN_CC (1U<<23) /* [0-9-a]=[0-9\-a] */
-/* syntax (behavior) warning */
-#define ONIG_SYN_WARN_CC_OP_NOT_ESCAPED (1U<<24) /* [,-,] */
-#define ONIG_SYN_WARN_REDUNDANT_NESTED_REPEAT (1U<<25) /* (?:a*)+ */
-#define ONIG_SYN_WARN_CC_DUP (1U<<26) /* [aa] */
-
-/* meta character specifiers (onig_set_meta_char()) */
-#define ONIG_META_CHAR_ESCAPE 0
-#define ONIG_META_CHAR_ANYCHAR 1
-#define ONIG_META_CHAR_ANYTIME 2
-#define ONIG_META_CHAR_ZERO_OR_ONE_TIME 3
-#define ONIG_META_CHAR_ONE_OR_MORE_TIME 4
-#define ONIG_META_CHAR_ANYCHAR_ANYTIME 5
-
-#define ONIG_INEFFECTIVE_META_CHAR 0
-
-/* error codes */
-#define ONIG_IS_PATTERN_ERROR(ecode) ((ecode) <= -100 && (ecode) > -1000)
-/* normal return */
-#define ONIG_NORMAL 0
-#define ONIG_MISMATCH -1
-#define ONIG_NO_SUPPORT_CONFIG -2
-
-/* internal error */
-#define ONIGERR_MEMORY -5
-#define ONIGERR_TYPE_BUG -6
-#define ONIGERR_PARSER_BUG -11
-#define ONIGERR_STACK_BUG -12
-#define ONIGERR_UNDEFINED_BYTECODE -13
-#define ONIGERR_UNEXPECTED_BYTECODE -14
-#define ONIGERR_MATCH_STACK_LIMIT_OVER -15
-#define ONIGERR_DEFAULT_ENCODING_IS_NOT_SET -21
-#define ONIGERR_SPECIFIED_ENCODING_CANT_CONVERT_TO_WIDE_CHAR -22
-/* general error */
-#define ONIGERR_INVALID_ARGUMENT -30
-/* syntax error */
-#define ONIGERR_END_PATTERN_AT_LEFT_BRACE -100
-#define ONIGERR_END_PATTERN_AT_LEFT_BRACKET -101
-#define ONIGERR_EMPTY_CHAR_CLASS -102
-#define ONIGERR_PREMATURE_END_OF_CHAR_CLASS -103
-#define ONIGERR_END_PATTERN_AT_ESCAPE -104
-#define ONIGERR_END_PATTERN_AT_META -105
-#define ONIGERR_END_PATTERN_AT_CONTROL -106
-#define ONIGERR_META_CODE_SYNTAX -108
-#define ONIGERR_CONTROL_CODE_SYNTAX -109
-#define ONIGERR_CHAR_CLASS_VALUE_AT_END_OF_RANGE -110
-#define ONIGERR_CHAR_CLASS_VALUE_AT_START_OF_RANGE -111
-#define ONIGERR_UNMATCHED_RANGE_SPECIFIER_IN_CHAR_CLASS -112
-#define ONIGERR_TARGET_OF_REPEAT_OPERATOR_NOT_SPECIFIED -113
-#define ONIGERR_TARGET_OF_REPEAT_OPERATOR_INVALID -114
-#define ONIGERR_NESTED_REPEAT_OPERATOR -115
-#define ONIGERR_UNMATCHED_CLOSE_PARENTHESIS -116
-#define ONIGERR_END_PATTERN_WITH_UNMATCHED_PARENTHESIS -117
-#define ONIGERR_END_PATTERN_IN_GROUP -118
-#define ONIGERR_UNDEFINED_GROUP_OPTION -119
-#define ONIGERR_INVALID_POSIX_BRACKET_TYPE -121
-#define ONIGERR_INVALID_LOOK_BEHIND_PATTERN -122
-#define ONIGERR_INVALID_REPEAT_RANGE_PATTERN -123
-#define ONIGERR_INVALID_CONDITION_PATTERN -124
-/* values error (syntax error) */
-#define ONIGERR_TOO_BIG_NUMBER -200
-#define ONIGERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE -201
-#define ONIGERR_UPPER_SMALLER_THAN_LOWER_IN_REPEAT_RANGE -202
-#define ONIGERR_EMPTY_RANGE_IN_CHAR_CLASS -203
-#define ONIGERR_MISMATCH_CODE_LENGTH_IN_CLASS_RANGE -204
-#define ONIGERR_TOO_MANY_MULTI_BYTE_RANGES -205
-#define ONIGERR_TOO_SHORT_MULTI_BYTE_STRING -206
-#define ONIGERR_TOO_BIG_BACKREF_NUMBER -207
-#define ONIGERR_INVALID_BACKREF -208
-#define ONIGERR_NUMBERED_BACKREF_OR_CALL_NOT_ALLOWED -209
-#define ONIGERR_TOO_SHORT_DIGITS -210
-#define ONIGERR_TOO_LONG_WIDE_CHAR_VALUE -212
-#define ONIGERR_EMPTY_GROUP_NAME -214
-#define ONIGERR_INVALID_GROUP_NAME -215
-#define ONIGERR_INVALID_CHAR_IN_GROUP_NAME -216
-#define ONIGERR_UNDEFINED_NAME_REFERENCE -217
-#define ONIGERR_UNDEFINED_GROUP_REFERENCE -218
-#define ONIGERR_MULTIPLEX_DEFINED_NAME -219
-#define ONIGERR_MULTIPLEX_DEFINITION_NAME_CALL -220
-#define ONIGERR_NEVER_ENDING_RECURSION -221
-#define ONIGERR_GROUP_NUMBER_OVER_FOR_CAPTURE_HISTORY -222
-#define ONIGERR_INVALID_CHAR_PROPERTY_NAME -223
-#define ONIGERR_TOO_MANY_CAPTURE_GROUPS -224
-#define ONIGERR_INVALID_CODE_POINT_VALUE -400
-#define ONIGERR_INVALID_WIDE_CHAR_VALUE -400
-#define ONIGERR_TOO_BIG_WIDE_CHAR_VALUE -401
-#define ONIGERR_NOT_SUPPORTED_ENCODING_COMBINATION -402
-#define ONIGERR_INVALID_COMBINATION_OF_OPTIONS -403
-
-/* errors related to thread */
-#define ONIGERR_OVER_THREAD_PASS_LIMIT_COUNT -1001
-
-
-/* must be smaller than BIT_STATUS_BITS_NUM (unsigned int * 8) */
-#define ONIG_MAX_CAPTURE_HISTORY_GROUP 31
-#define ONIG_IS_CAPTURE_HISTORY_GROUP(r, i) \
- ((i) <= ONIG_MAX_CAPTURE_HISTORY_GROUP && (r)->list && (r)->list[i])
-
-typedef struct OnigCaptureTreeNodeStruct {
- int group; /* group number */
- OnigPosition beg;
- OnigPosition end;
- int allocated;
- int num_childs;
- struct OnigCaptureTreeNodeStruct** childs;
-} OnigCaptureTreeNode;
-
-/* match result region type */
-struct re_registers {
- int allocated;
- int num_regs;
- OnigPosition* beg;
- OnigPosition* end;
- /* extended */
- OnigCaptureTreeNode* history_root; /* capture history tree root */
-};
-
-/* capture tree traverse */
-#define ONIG_TRAVERSE_CALLBACK_AT_FIRST 1
-#define ONIG_TRAVERSE_CALLBACK_AT_LAST 2
-#define ONIG_TRAVERSE_CALLBACK_AT_BOTH \
- ( ONIG_TRAVERSE_CALLBACK_AT_FIRST | ONIG_TRAVERSE_CALLBACK_AT_LAST )
-
-
-#define ONIG_REGION_NOTPOS -1
-
-typedef struct re_registers OnigRegion;
-
-typedef struct {
- OnigEncoding enc;
- OnigUChar* par;
- OnigUChar* par_end;
-} OnigErrorInfo;
-
-typedef struct {
- int lower;
- int upper;
-} OnigRepeatRange;
-
-typedef void (*OnigWarnFunc) P_((const char* s));
-extern void onig_null_warn P_((const char* s));
-#define ONIG_NULL_WARN onig_null_warn
-
-#define ONIG_CHAR_TABLE_SIZE 256
-
-/* regex_t state */
-#define ONIG_STATE_NORMAL 0
-#define ONIG_STATE_SEARCHING 1
-#define ONIG_STATE_COMPILING -1
-#define ONIG_STATE_MODIFY -2
-
-#define ONIG_STATE(reg) \
- ((reg)->state > 0 ? ONIG_STATE_SEARCHING : (reg)->state)
-
-typedef struct re_pattern_buffer {
- /* common members of BBuf(bytes-buffer) */
- unsigned char* p; /* compiled pattern */
- unsigned int used; /* used space for p */
- unsigned int alloc; /* allocated space for p */
-
- int state; /* normal, searching, compiling */
- int num_mem; /* used memory(...) num counted from 1 */
- int num_repeat; /* OP_REPEAT/OP_REPEAT_NG id-counter */
- int num_null_check; /* OP_NULL_CHECK_START/END id counter */
- int num_comb_exp_check; /* combination explosion check */
- int num_call; /* number of subexp call */
- unsigned int capture_history; /* (?@...) flag (1-31) */
- unsigned int bt_mem_start; /* need backtrack flag */
- unsigned int bt_mem_end; /* need backtrack flag */
- int stack_pop_level;
- int repeat_range_alloc;
-
- OnigOptionType options;
-
- OnigRepeatRange* repeat_range;
-
- OnigEncoding enc;
- const OnigSyntaxType* syntax;
- void* name_table;
- OnigCaseFoldType case_fold_flag;
-
- /* optimization info (string search, char-map and anchors) */
- int optimize; /* optimize flag */
- int threshold_len; /* search str-length for apply optimize */
- int anchor; /* BEGIN_BUF, BEGIN_POS, (SEMI_)END_BUF */
- OnigDistance anchor_dmin; /* (SEMI_)END_BUF anchor distance */
- OnigDistance anchor_dmax; /* (SEMI_)END_BUF anchor distance */
- int sub_anchor; /* start-anchor for exact or map */
- unsigned char *exact;
- unsigned char *exact_end;
- unsigned char map[ONIG_CHAR_TABLE_SIZE]; /* used as BM skip or char-map */
- int *int_map; /* BM skip for exact_len > 255 */
- int *int_map_backward; /* BM skip for backward search */
- OnigDistance dmin; /* min-distance of exact or map */
- OnigDistance dmax; /* max-distance of exact or map */
-
- /* regex_t link chain */
- struct re_pattern_buffer* chain; /* escape compile-conflict */
-} OnigRegexType;
-
-typedef OnigRegexType* OnigRegex;
-
-#ifndef ONIG_ESCAPE_REGEX_T_COLLISION
- typedef OnigRegexType regex_t;
-#endif
-
-
-typedef struct {
- int num_of_elements;
- OnigEncoding pattern_enc;
- OnigEncoding target_enc;
- const OnigSyntaxType* syntax;
- OnigOptionType option;
- OnigCaseFoldType case_fold_flag;
-} OnigCompileInfo;
-
-/* Oniguruma Native API */
-ONIG_EXTERN
-int onig_init P_((void));
-ONIG_EXTERN
-int onig_error_code_to_str PV_((OnigUChar* s, OnigPosition err_code, ...));
-ONIG_EXTERN
-void onig_set_warn_func P_((OnigWarnFunc f));
-ONIG_EXTERN
-void onig_set_verb_warn_func P_((OnigWarnFunc f));
-ONIG_EXTERN
-int onig_new P_((OnigRegex*, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigOptionType option, OnigEncoding enc, const OnigSyntaxType* syntax, OnigErrorInfo* einfo));
-ONIG_EXTERN
-int onig_reg_init P_((OnigRegex reg, OnigOptionType option, OnigCaseFoldType case_fold_flag, OnigEncoding enc, const OnigSyntaxType* syntax));
-ONIG_EXTERN
-int onig_new_without_alloc P_((OnigRegex, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigOptionType option, OnigEncoding enc, OnigSyntaxType* syntax, OnigErrorInfo* einfo));
-ONIG_EXTERN
-int onig_new_deluxe P_((OnigRegex* reg, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigCompileInfo* ci, OnigErrorInfo* einfo));
-ONIG_EXTERN
-void onig_free P_((OnigRegex));
-ONIG_EXTERN
-void onig_free_body P_((OnigRegex));
-ONIG_EXTERN
-int onig_recompile P_((OnigRegex, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigOptionType option, OnigEncoding enc, OnigSyntaxType* syntax, OnigErrorInfo* einfo));
-ONIG_EXTERN
-int onig_recompile_deluxe P_((OnigRegex reg, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigCompileInfo* ci, OnigErrorInfo* einfo));
-ONIG_EXTERN
-OnigPosition onig_search P_((OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* start, const OnigUChar* range, OnigRegion* region, OnigOptionType option));
-ONIG_EXTERN
-OnigPosition onig_search_gpos P_((OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* global_pos, const OnigUChar* start, const OnigUChar* range, OnigRegion* region, OnigOptionType option));
-ONIG_EXTERN
-OnigPosition onig_match P_((OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* at, OnigRegion* region, OnigOptionType option));
-ONIG_EXTERN
-OnigRegion* onig_region_new P_((void));
-ONIG_EXTERN
-void onig_region_init P_((OnigRegion* region));
-ONIG_EXTERN
-void onig_region_free P_((OnigRegion* region, int free_self));
-ONIG_EXTERN
-void onig_region_copy P_((OnigRegion* to, OnigRegion* from));
-ONIG_EXTERN
-void onig_region_clear P_((OnigRegion* region));
-ONIG_EXTERN
-int onig_region_resize P_((OnigRegion* region, int n));
-ONIG_EXTERN
-int onig_region_set P_((OnigRegion* region, int at, int beg, int end));
-ONIG_EXTERN
-int onig_name_to_group_numbers P_((OnigRegex reg, const OnigUChar* name, const OnigUChar* name_end, int** nums));
-ONIG_EXTERN
-int onig_name_to_backref_number P_((OnigRegex reg, const OnigUChar* name, const OnigUChar* name_end, OnigRegion *region));
-ONIG_EXTERN
-int onig_foreach_name P_((OnigRegex reg, int (*func)(const OnigUChar*, const OnigUChar*,int,int*,OnigRegex,void*), void* arg));
-ONIG_EXTERN
-int onig_number_of_names P_((OnigRegex reg));
-ONIG_EXTERN
-int onig_number_of_captures P_((OnigRegex reg));
-ONIG_EXTERN
-int onig_number_of_capture_histories P_((OnigRegex reg));
-ONIG_EXTERN
-OnigCaptureTreeNode* onig_get_capture_tree P_((OnigRegion* region));
-ONIG_EXTERN
-int onig_capture_tree_traverse P_((OnigRegion* region, int at, int(*callback_func)(int,OnigPosition,OnigPosition,int,int,void*), void* arg));
-ONIG_EXTERN
-int onig_noname_group_capture_is_active P_((OnigRegex reg));
-ONIG_EXTERN
-OnigEncoding onig_get_encoding P_((OnigRegex reg));
-ONIG_EXTERN
-OnigOptionType onig_get_options P_((OnigRegex reg));
-ONIG_EXTERN
-OnigCaseFoldType onig_get_case_fold_flag P_((OnigRegex reg));
-ONIG_EXTERN
-const OnigSyntaxType* onig_get_syntax P_((OnigRegex reg));
-ONIG_EXTERN
-int onig_set_default_syntax P_((const OnigSyntaxType* syntax));
-ONIG_EXTERN
-void onig_copy_syntax P_((OnigSyntaxType* to, const OnigSyntaxType* from));
-ONIG_EXTERN
-unsigned int onig_get_syntax_op P_((OnigSyntaxType* syntax));
-ONIG_EXTERN
-unsigned int onig_get_syntax_op2 P_((OnigSyntaxType* syntax));
-ONIG_EXTERN
-unsigned int onig_get_syntax_behavior P_((OnigSyntaxType* syntax));
-ONIG_EXTERN
-OnigOptionType onig_get_syntax_options P_((OnigSyntaxType* syntax));
-ONIG_EXTERN
-void onig_set_syntax_op P_((OnigSyntaxType* syntax, unsigned int op));
-ONIG_EXTERN
-void onig_set_syntax_op2 P_((OnigSyntaxType* syntax, unsigned int op2));
-ONIG_EXTERN
-void onig_set_syntax_behavior P_((OnigSyntaxType* syntax, unsigned int behavior));
-ONIG_EXTERN
-void onig_set_syntax_options P_((OnigSyntaxType* syntax, OnigOptionType options));
-ONIG_EXTERN
-int onig_set_meta_char P_((OnigSyntaxType* syntax, unsigned int what, OnigCodePoint code));
-ONIG_EXTERN
-void onig_copy_encoding P_((OnigEncodingType *to, OnigEncoding from));
-ONIG_EXTERN
-OnigCaseFoldType onig_get_default_case_fold_flag P_((void));
-ONIG_EXTERN
-int onig_set_default_case_fold_flag P_((OnigCaseFoldType case_fold_flag));
-ONIG_EXTERN
-unsigned int onig_get_match_stack_limit_size P_((void));
-ONIG_EXTERN
-int onig_set_match_stack_limit_size P_((unsigned int size));
-ONIG_EXTERN
-int onig_end P_((void));
-ONIG_EXTERN
-const char* onig_version P_((void));
-ONIG_EXTERN
-const char* onig_copyright P_((void));
-
-RUBY_SYMBOL_EXPORT_END
-
-#ifdef __cplusplus
-#if 0
-{ /* satisfy cc-mode */
-#endif
-}
-#endif
-
+#define ONIGURUMA_VERSION_MAJOR ONIGMO_VERSION_MAJOR
+#define ONIGURUMA_VERSION_MINOR ONIGMO_VERSION_MINOR
+#define ONIGURUMA_VERSION_TEENY ONIGMO_VERSION_TEENY
#endif /* ONIGURUMA_H */
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index eeae2f6879..d983114e0a 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -26,15 +26,24 @@ extern "C" {
#include RUBY_EXTCONF_H
#endif
+#include "defines.h"
+
+/* For MinGW, we need __declspec(dllimport) for RUBY_EXTERN on MJIT.
+ mswin's RUBY_EXTERN already has that. See also: win32/Makefile.sub */
+#if defined(MJIT_HEADER) && defined(_WIN32) && defined(__GNUC__)
+# undef RUBY_EXTERN
+# define RUBY_EXTERN extern __declspec(dllimport)
+#endif
+
#if defined(__cplusplus)
/* __builtin_choose_expr and __builtin_types_compatible aren't available
* on C++. See https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html */
# undef HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P
# undef HAVE_BUILTIN___BUILTIN_TYPES_COMPATIBLE_P
+#elif GCC_VERSION_BEFORE(4,8,6) /* Bug #14221 */
+# undef HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P
#endif
-#include "defines.h"
-
#ifndef ASSUME
# ifdef UNREACHABLE
# define ASSUME(x) (RB_LIKELY(!!(x)) ? (void)0 : UNREACHABLE)
@@ -42,6 +51,13 @@ extern "C" {
# define ASSUME(x) ((void)(x))
# endif
#endif
+#ifndef UNREACHABLE_RETURN
+# ifdef UNREACHABLE
+# define UNREACHABLE_RETURN(val) UNREACHABLE
+# else
+# define UNREACHABLE_RETURN(val) return (val)
+# endif
+#endif
#ifndef UNREACHABLE
# define UNREACHABLE ((void)0) /* unreachable */
#endif
@@ -111,12 +127,26 @@ typedef char ruby_check_sizeof_voidp[SIZEOF_VOIDP == sizeof(void*) ? 1 : -1];
#ifndef PRI_LONG_PREFIX
#define PRI_LONG_PREFIX "l"
#endif
+#ifndef PRI_SHORT_PREFIX
+#define PRI_SHORT_PREFIX "h"
+#endif
+#ifndef PRI_64_PREFIX
#if SIZEOF_LONG == 8
#define PRI_64_PREFIX PRI_LONG_PREFIX
#elif SIZEOF_LONG_LONG == 8
#define PRI_64_PREFIX PRI_LL_PREFIX
#endif
+#endif
+
+#ifndef PRIdPTR
+#define PRIdPTR PRI_PTR_PREFIX"d"
+#define PRIiPTR PRI_PTR_PREFIX"i"
+#define PRIoPTR PRI_PTR_PREFIX"o"
+#define PRIuPTR PRI_PTR_PREFIX"u"
+#define PRIxPTR PRI_PTR_PREFIX"x"
+#define PRIXPTR PRI_PTR_PREFIX"X"
+#endif
#define RUBY_PRI_VALUE_MARK "\v"
#if defined PRIdPTR && !defined PRI_VALUE_PREFIX
@@ -233,10 +263,10 @@ typedef char ruby_check_sizeof_voidp[SIZEOF_VOIDP == sizeof(void*) ? 1 : -1];
#define RB_LONG2FIX(i) RB_INT2FIX(i)
#define LONG2FIX(i) RB_INT2FIX(i)
#define rb_fix_new(v) RB_INT2FIX(v)
-VALUE rb_int2inum(SIGNED_VALUE);
+VALUE rb_int2inum(intptr_t);
#define rb_int_new(v) rb_int2inum(v)
-VALUE rb_uint2inum(VALUE);
+VALUE rb_uint2inum(uintptr_t);
#define rb_uint_new(v) rb_uint2inum(v)
@@ -429,10 +459,14 @@ enum ruby_special_consts {
RUBY_SPECIAL_SHIFT = 8
};
-#define Qfalse ((VALUE)RUBY_Qfalse)
-#define Qtrue ((VALUE)RUBY_Qtrue)
-#define Qnil ((VALUE)RUBY_Qnil)
-#define Qundef ((VALUE)RUBY_Qundef) /* undefined value for placeholder */
+#define RUBY_Qfalse ((VALUE)RUBY_Qfalse)
+#define RUBY_Qtrue ((VALUE)RUBY_Qtrue)
+#define RUBY_Qnil ((VALUE)RUBY_Qnil)
+#define RUBY_Qundef ((VALUE)RUBY_Qundef) /* undefined value for placeholder */
+#define Qfalse RUBY_Qfalse
+#define Qtrue RUBY_Qtrue
+#define Qnil RUBY_Qnil
+#define Qundef RUBY_Qundef
#define IMMEDIATE_MASK RUBY_IMMEDIATE_MASK
#define FIXNUM_FLAG RUBY_FIXNUM_FLAG
#if USE_FLONUM
@@ -441,8 +475,10 @@ enum ruby_special_consts {
#endif
#define SYMBOL_FLAG RUBY_SYMBOL_FLAG
-#define RTEST(v) !(((VALUE)(v) & ~RUBY_Qnil) == 0)
-#define NIL_P(v) !((VALUE)(v) != RUBY_Qnil)
+#define RB_TEST(v) !(((VALUE)(v) & (VALUE)~RUBY_Qnil) == 0)
+#define RB_NIL_P(v) !((VALUE)(v) != RUBY_Qnil)
+#define RTEST(v) RB_TEST(v)
+#define NIL_P(v) RB_NIL_P(v)
#define CLASS_OF(v) rb_class_of((VALUE)(v))
@@ -472,7 +508,7 @@ enum ruby_value_type {
RUBY_T_FIXNUM = 0x15,
RUBY_T_UNDEF = 0x16,
- RUBY_T_IMEMO = 0x1a,
+ RUBY_T_IMEMO = 0x1a, /*!< @see imemo_type */
RUBY_T_NODE = 0x1b,
RUBY_T_ICLASS = 0x1c,
RUBY_T_ZOMBIE = 0x1d,
@@ -530,7 +566,11 @@ static inline int rb_type(VALUE obj);
#ifdef __GNUC__
#define RB_GC_GUARD(v) \
- (*__extension__ ({volatile VALUE *rb_gc_guarded_ptr = &(v); rb_gc_guarded_ptr;}))
+ (*__extension__ ({ \
+ volatile VALUE *rb_gc_guarded_ptr = &(v); \
+ __asm__("" : : "m"(rb_gc_guarded_ptr)); \
+ rb_gc_guarded_ptr; \
+ }))
#elif defined _MSC_VER
#pragma optimize("", off)
static inline volatile VALUE *rb_gc_guarded_ptr(volatile VALUE *ptr) {return ptr;}
@@ -618,7 +658,7 @@ int ruby_safe_level_2_warning(void) __attribute__((const,warning("$SAFE=2 to 4 a
# define rb_set_safe_level(level) rb_set_safe_level(RUBY_SAFE_LEVEL_CHECK(level, error))
#endif
void rb_set_safe_level_force(int);
-CONSTFUNC(void rb_secure_update(VALUE));
+void rb_secure_update(VALUE);
NORETURN(void rb_insecure_operation(void));
VALUE rb_errinfo(void);
@@ -732,8 +772,8 @@ rb_num2ll_inline(VALUE x)
double rb_num2dbl(VALUE);
#define NUM2DBL(x) rb_num2dbl((VALUE)(x))
-VALUE rb_uint2big(VALUE);
-VALUE rb_int2big(SIGNED_VALUE);
+VALUE rb_uint2big(uintptr_t);
+VALUE rb_int2big(intptr_t);
VALUE rb_newobj(void);
VALUE rb_newobj_of(VALUE, VALUE);
@@ -841,14 +881,10 @@ enum ruby_fl_type {
RUBY_FL_SINGLETON = RUBY_FL_USER0
};
-struct RBasic {
+struct RUBY_ALIGNAS(SIZEOF_VALUE) RBasic {
VALUE flags;
const VALUE klass;
-}
-#ifdef __GNUC__
- __attribute__((aligned(sizeof(VALUE))))
-#endif
-;
+};
VALUE rb_obj_hide(VALUE obj);
VALUE rb_obj_reveal(VALUE obj, VALUE klass); /* do not use this API to change klass information */
@@ -869,7 +905,7 @@ VALUE rb_obj_reveal(VALUE obj, VALUE klass); /* do not use this API to change kl
#define ROBJECT_EMBED_LEN_MAX ROBJECT_EMBED_LEN_MAX
#define ROBJECT_EMBED ROBJECT_EMBED
-enum {
+enum ruby_robject_flags {
ROBJECT_EMBED_LEN_MAX = 3,
ROBJECT_EMBED = RUBY_FL_USER1,
@@ -900,13 +936,6 @@ struct RObject {
RCLASS_IV_INDEX_TBL(rb_obj_class(o)) : \
ROBJECT(o)->as.heap.iv_index_tbl)
-#define RClass RClassDeprecated
-#ifndef __cplusplus
-DEPRECATED_TYPE(("RClass is internal use only"),
-struct RClass {
- struct RBasic basic;
-});
-#endif
#define RCLASS_SUPER(c) rb_class_get_superclass(c)
#define RMODULE_IV_TBL(m) RCLASS_IV_TBL(m)
#define RMODULE_CONST_TBL(m) RCLASS_CONST_TBL(m)
@@ -915,7 +944,7 @@ struct RClass {
#define RMODULE_IS_OVERLAID RMODULE_IS_OVERLAID
#define RMODULE_IS_REFINEMENT RMODULE_IS_REFINEMENT
#define RMODULE_INCLUDED_INTO_REFINEMENT RMODULE_INCLUDED_INTO_REFINEMENT
-enum {
+enum ruby_rmodule_flags {
RMODULE_IS_OVERLAID = RUBY_FL_USER2,
RMODULE_IS_REFINEMENT = RUBY_FL_USER3,
RMODULE_INCLUDED_INTO_REFINEMENT = RUBY_FL_USER4,
@@ -938,7 +967,7 @@ VALUE rb_float_new_in_heap(double);
#define RSTRING_EMBED_LEN_SHIFT RSTRING_EMBED_LEN_SHIFT
#define RSTRING_EMBED_LEN_MAX RSTRING_EMBED_LEN_MAX
#define RSTRING_FSTR RSTRING_FSTR
-enum {
+enum ruby_rstring_flags {
RSTRING_NOEMBED = RUBY_FL_USER1,
RSTRING_EMBED_LEN_MASK = (RUBY_FL_USER2|RUBY_FL_USER3|RUBY_FL_USER4|
RUBY_FL_USER5|RUBY_FL_USER6),
@@ -948,6 +977,7 @@ enum {
RSTRING_ENUM_END
};
+
struct RString {
struct RBasic basic;
union {
@@ -983,19 +1013,31 @@ struct RString {
((ptrvar) = RSTRING(str)->as.ary, (lenvar) = RSTRING_EMBED_LEN(str)) : \
((ptrvar) = RSTRING(str)->as.heap.ptr, (lenvar) = RSTRING(str)->as.heap.len))
-#define RARRAY_EMBED_FLAG RARRAY_EMBED_FLAG
-#define RARRAY_EMBED_LEN_MASK RARRAY_EMBED_LEN_MASK
-#define RARRAY_EMBED_LEN_MAX RARRAY_EMBED_LEN_MAX
-#define RARRAY_EMBED_LEN_SHIFT RARRAY_EMBED_LEN_SHIFT
-enum {
+#ifndef USE_TRANSIENT_HEAP
+#define USE_TRANSIENT_HEAP 1
+#endif
+
+enum ruby_rarray_flags {
RARRAY_EMBED_LEN_MAX = 3,
RARRAY_EMBED_FLAG = RUBY_FL_USER1,
/* RUBY_FL_USER2 is for ELTS_SHARED */
RARRAY_EMBED_LEN_MASK = (RUBY_FL_USER4|RUBY_FL_USER3),
RARRAY_EMBED_LEN_SHIFT = (RUBY_FL_USHIFT+3),
+#if USE_TRANSIENT_HEAP
+ RARRAY_TRANSIENT_FLAG = RUBY_FL_USER13,
+#define RARRAY_TRANSIENT_FLAG RARRAY_TRANSIENT_FLAG
+#else
+#define RARRAY_TRANSIENT_FLAG 0
+#endif
+
RARRAY_ENUM_END
};
+#define RARRAY_EMBED_FLAG (VALUE)RARRAY_EMBED_FLAG
+#define RARRAY_EMBED_LEN_MASK (VALUE)RARRAY_EMBED_LEN_MASK
+#define RARRAY_EMBED_LEN_MAX RARRAY_EMBED_LEN_MAX
+#define RARRAY_EMBED_LEN_SHIFT RARRAY_EMBED_LEN_SHIFT
+
struct RArray {
struct RBasic basic;
union {
@@ -1016,9 +1058,26 @@ struct RArray {
#define RARRAY_LEN(a) rb_array_len(a)
#define RARRAY_LENINT(ary) rb_long2int(RARRAY_LEN(ary))
#define RARRAY_CONST_PTR(a) rb_array_const_ptr(a)
+#define RARRAY_CONST_PTR_TRANSIENT(a) rb_array_const_ptr_transient(a)
+
+#if USE_TRANSIENT_HEAP
+#define RARRAY_TRANSIENT_P(ary) FL_TEST_RAW((ary), RARRAY_TRANSIENT_FLAG)
+#else
+#define RARRAY_TRANSIENT_P(ary) 0
+#endif
+
+#define RARRAY_PTR_USE_START_TRANSIENT(a) rb_array_ptr_use_start(a, 1)
+#define RARRAY_PTR_USE_END_TRANSIENT(a) rb_array_ptr_use_end(a, 1)
+
+#define RARRAY_PTR_USE_TRANSIENT(ary, ptr_name, expr) do { \
+ const VALUE _ary = (ary); \
+ VALUE *ptr_name = (VALUE *)RARRAY_PTR_USE_START_TRANSIENT(_ary); \
+ expr; \
+ RARRAY_PTR_USE_END_TRANSIENT(_ary); \
+} while (0)
-#define RARRAY_PTR_USE_START(a) ((VALUE *)RARRAY_CONST_PTR(a))
-#define RARRAY_PTR_USE_END(a) /* */
+#define RARRAY_PTR_USE_START(a) rb_array_ptr_use_start(a, 0)
+#define RARRAY_PTR_USE_END(a) rb_array_ptr_use_end(a, 0)
#define RARRAY_PTR_USE(ary, ptr_name, expr) do { \
const VALUE _ary = (ary); \
@@ -1027,12 +1086,13 @@ struct RArray {
RARRAY_PTR_USE_END(_ary); \
} while (0)
-#define RARRAY_AREF(a, i) (RARRAY_CONST_PTR(a)[i])
+#define RARRAY_AREF(a, i) (RARRAY_CONST_PTR_TRANSIENT(a)[i])
#define RARRAY_ASET(a, i, v) do { \
const VALUE _ary = (a); \
- VALUE *ptr = (VALUE *)RARRAY_PTR_USE_START(_ary); \
- RB_OBJ_WRITE(_ary, &ptr[i], (v)); \
- RARRAY_PTR_USE_END(_ary); \
+ const VALUE _v = (v); \
+ VALUE *ptr = (VALUE *)RARRAY_PTR_USE_START_TRANSIENT(_ary); \
+ RB_OBJ_WRITE(_ary, &ptr[i], _v); \
+ RARRAY_PTR_USE_END_TRANSIENT(_ary); \
} while (0)
#define RARRAY_PTR(a) ((VALUE *)RARRAY_CONST_PTR(RB_OBJ_WB_UNPROTECT_FOR(ARRAY, a)))
@@ -1049,11 +1109,13 @@ struct RRegexp {
#define RREGEXP_SRC_LEN(r) RSTRING_LEN(RREGEXP(r)->src)
#define RREGEXP_SRC_END(r) RSTRING_END(RREGEXP(r)->src)
-/* RHASH_TBL allocates st_table if not available. */
-#define RHASH_TBL(h) rb_hash_tbl(h)
+/* RHash is defined at internal.h */
+size_t rb_hash_size_num(VALUE hash);
+
+#define RHASH_TBL(h) rb_hash_tbl(h, __FILE__, __LINE__)
#define RHASH_ITER_LEV(h) rb_hash_iter_lev(h)
#define RHASH_IFNONE(h) rb_hash_ifnone(h)
-#define RHASH_SIZE(h) NUM2SIZET(rb_hash_size(h))
+#define RHASH_SIZE(h) rb_hash_size_num(h)
#define RHASH_EMPTY_P(h) (RHASH_SIZE(h) == 0)
#define RHASH_SET_IFNONE(h, ifnone) rb_hash_set_ifnone((VALUE)h, ifnone)
@@ -1062,9 +1124,6 @@ struct RFile {
struct rb_io_t *fptr;
};
-#define RCOMPLEX_SET_REAL(cmp, r) RB_OBJ_WRITE((cmp), &((struct RComplex *)(cmp))->real,(r))
-#define RCOMPLEX_SET_IMAG(cmp, i) RB_OBJ_WRITE((cmp), &((struct RComplex *)(cmp))->imag,(i))
-
struct RData {
struct RBasic basic;
void (*dmark)(void*);
@@ -1146,10 +1205,10 @@ void *rb_check_typeddata(VALUE, const rb_data_type_t *);
(void)((sval) = (type *)DATA_PTR(result));
#ifdef __GNUC__
-#define Data_Make_Struct(klass,type,mark,free,sval) ({\
+#define Data_Make_Struct(klass,type,mark,free,sval) RB_GNUC_EXTENSION_BLOCK(\
Data_Make_Struct0(data_struct_obj, klass, type, sizeof(type), mark, free, sval); \
- data_struct_obj; \
-})
+ data_struct_obj \
+)
#else
#define Data_Make_Struct(klass,type,mark,free,sval) (\
rb_data_object_make((klass),(RUBY_DATA_FUNC)(mark),(RUBY_DATA_FUNC)(free),(void **)&(sval),sizeof(type)) \
@@ -1164,10 +1223,10 @@ void *rb_check_typeddata(VALUE, const rb_data_type_t *);
(void)((sval) = (type *)DATA_PTR(result));
#ifdef __GNUC__
-#define TypedData_Make_Struct(klass, type, data_type, sval) ({\
+#define TypedData_Make_Struct(klass, type, data_type, sval) RB_GNUC_EXTENSION_BLOCK(\
TypedData_Make_Struct0(data_struct_obj, klass, type, sizeof(type), data_type, sval); \
- data_struct_obj; \
-})
+ data_struct_obj \
+)
#else
#define TypedData_Make_Struct(klass, type, data_type, sval) (\
rb_data_typed_object_make((klass),(data_type),(void **)&(sval),sizeof(type)) \
@@ -1180,14 +1239,15 @@ void *rb_check_typeddata(VALUE, const rb_data_type_t *);
#define TypedData_Get_Struct(obj,type,data_type,sval) \
((sval) = (type*)rb_check_typeddata((obj), (data_type)))
-#define RSTRUCT_LEN(st) rb_struct_size(st)
-#define RSTRUCT_PTR(st) rb_struct_const_ptr(st)
+#define RSTRUCT_LEN(st) NUM2LONG(rb_struct_size(st))
+#define RSTRUCT_PTR(st) rb_struct_ptr(st)
#define RSTRUCT_SET(st, idx, v) rb_struct_aset(st, INT2NUM(idx), (v))
#define RSTRUCT_GET(st, idx) rb_struct_aref(st, INT2NUM(idx))
-#define RBIGNUM_SIGN(b) (RB_FIX2LONG(rb_big_cmp((b), RB_INT2FIX(0))) >= 0)
-#define RBIGNUM_POSITIVE_P(b) (RB_FIX2LONG(rb_big_cmp((b), RB_INT2FIX(0))) >= 0)
-#define RBIGNUM_NEGATIVE_P(b) (RB_FIX2LONG(rb_big_cmp((b), RB_INT2FIX(0))) < 0)
+int rb_big_sign(VALUE);
+#define RBIGNUM_SIGN(b) (rb_big_sign(b))
+#define RBIGNUM_POSITIVE_P(b) (RBIGNUM_SIGN(b)!=0)
+#define RBIGNUM_NEGATIVE_P(b) (RBIGNUM_SIGN(b)==0)
#define R_CAST(st) (struct st*)
#define RBASIC(obj) (R_CAST(RBasic)(obj))
@@ -1201,40 +1261,40 @@ void *rb_check_typeddata(VALUE, const rb_data_type_t *);
#define RTYPEDDATA(obj) (R_CAST(RTypedData)(obj))
#define RFILE(obj) (R_CAST(RFile)(obj))
-#define FL_SINGLETON RUBY_FL_SINGLETON
-#define FL_WB_PROTECTED RUBY_FL_WB_PROTECTED
-#define FL_PROMOTED0 RUBY_FL_PROMOTED0
-#define FL_PROMOTED1 RUBY_FL_PROMOTED1
-#define FL_FINALIZE RUBY_FL_FINALIZE
-#define FL_TAINT RUBY_FL_TAINT
-#define FL_UNTRUSTED RUBY_FL_UNTRUSTED
-#define FL_EXIVAR RUBY_FL_EXIVAR
-#define FL_FREEZE RUBY_FL_FREEZE
-
-#define FL_USHIFT RUBY_FL_USHIFT
-
-#define FL_USER0 RUBY_FL_USER0
-#define FL_USER1 RUBY_FL_USER1
-#define FL_USER2 RUBY_FL_USER2
-#define FL_USER3 RUBY_FL_USER3
-#define FL_USER4 RUBY_FL_USER4
-#define FL_USER5 RUBY_FL_USER5
-#define FL_USER6 RUBY_FL_USER6
-#define FL_USER7 RUBY_FL_USER7
-#define FL_USER8 RUBY_FL_USER8
-#define FL_USER9 RUBY_FL_USER9
-#define FL_USER10 RUBY_FL_USER10
-#define FL_USER11 RUBY_FL_USER11
-#define FL_USER12 RUBY_FL_USER12
-#define FL_USER13 RUBY_FL_USER13
-#define FL_USER14 RUBY_FL_USER14
-#define FL_USER15 RUBY_FL_USER15
-#define FL_USER16 RUBY_FL_USER16
-#define FL_USER17 RUBY_FL_USER17
-#define FL_USER18 RUBY_FL_USER18
-#define FL_USER19 RUBY_FL_USER19
-
-#define RB_SPECIAL_CONST_P(x) (RB_IMMEDIATE_P(x) || !RTEST(x))
+#define FL_SINGLETON ((VALUE)RUBY_FL_SINGLETON)
+#define FL_WB_PROTECTED ((VALUE)RUBY_FL_WB_PROTECTED)
+#define FL_PROMOTED0 ((VALUE)RUBY_FL_PROMOTED0)
+#define FL_PROMOTED1 ((VALUE)RUBY_FL_PROMOTED1)
+#define FL_FINALIZE ((VALUE)RUBY_FL_FINALIZE)
+#define FL_TAINT ((VALUE)RUBY_FL_TAINT)
+#define FL_UNTRUSTED ((VALUE)RUBY_FL_UNTRUSTED)
+#define FL_EXIVAR ((VALUE)RUBY_FL_EXIVAR)
+#define FL_FREEZE ((VALUE)RUBY_FL_FREEZE)
+
+#define FL_USHIFT ((VALUE)RUBY_FL_USHIFT)
+
+#define FL_USER0 ((VALUE)RUBY_FL_USER0)
+#define FL_USER1 ((VALUE)RUBY_FL_USER1)
+#define FL_USER2 ((VALUE)RUBY_FL_USER2)
+#define FL_USER3 ((VALUE)RUBY_FL_USER3)
+#define FL_USER4 ((VALUE)RUBY_FL_USER4)
+#define FL_USER5 ((VALUE)RUBY_FL_USER5)
+#define FL_USER6 ((VALUE)RUBY_FL_USER6)
+#define FL_USER7 ((VALUE)RUBY_FL_USER7)
+#define FL_USER8 ((VALUE)RUBY_FL_USER8)
+#define FL_USER9 ((VALUE)RUBY_FL_USER9)
+#define FL_USER10 ((VALUE)RUBY_FL_USER10)
+#define FL_USER11 ((VALUE)RUBY_FL_USER11)
+#define FL_USER12 ((VALUE)RUBY_FL_USER12)
+#define FL_USER13 ((VALUE)RUBY_FL_USER13)
+#define FL_USER14 ((VALUE)RUBY_FL_USER14)
+#define FL_USER15 ((VALUE)RUBY_FL_USER15)
+#define FL_USER16 ((VALUE)RUBY_FL_USER16)
+#define FL_USER17 ((VALUE)RUBY_FL_USER17)
+#define FL_USER18 ((VALUE)RUBY_FL_USER18)
+#define FL_USER19 ((VALUE)RUBY_FL_USER19)
+
+#define RB_SPECIAL_CONST_P(x) (RB_IMMEDIATE_P(x) || !RB_TEST(x))
#define SPECIAL_CONST_P(x) RB_SPECIAL_CONST_P(x)
#define RB_FL_ABLE(x) (!RB_SPECIAL_CONST_P(x) && RB_BUILTIN_TYPE(x) != RUBY_T_NODE)
@@ -1246,7 +1306,7 @@ void *rb_check_typeddata(VALUE, const rb_data_type_t *);
#define RB_FL_ALL(x,f) (RB_FL_TEST((x),(f)) == (f))
#define RB_FL_SET_RAW(x,f) (void)(RBASIC(x)->flags |= (f))
#define RB_FL_SET(x,f) (RB_FL_ABLE(x) ? RB_FL_SET_RAW(x, f) : (void)0)
-#define RB_FL_UNSET_RAW(x,f) (void)(RBASIC(x)->flags &= ~(f))
+#define RB_FL_UNSET_RAW(x,f) (void)(RBASIC(x)->flags &= ~(VALUE)(f))
#define RB_FL_UNSET(x,f) (RB_FL_ABLE(x) ? RB_FL_UNSET_RAW(x, f) : (void)0)
#define RB_FL_REVERSE_RAW(x,f) (void)(RBASIC(x)->flags ^= (f))
#define RB_FL_REVERSE(x,f) (RB_FL_ABLE(x) ? RB_FL_REVERSE_RAW(x, f) : (void)0)
@@ -1268,6 +1328,11 @@ void *rb_check_typeddata(VALUE, const rb_data_type_t *);
#define RB_OBJ_FREEZE_RAW(x) (void)(RBASIC(x)->flags |= RUBY_FL_FREEZE)
#define RB_OBJ_FREEZE(x) rb_obj_freeze_inline((VALUE)x)
+/*!
+ * \defgroup deprecated_macros deprecated macro APIs
+ * \{
+ * \par These macros are deprecated. Prefer their `RB_`-prefixed versions.
+ */
#define FL_ABLE(x) RB_FL_ABLE(x)
#define FL_TEST_RAW(x,f) RB_FL_TEST_RAW(x,f)
#define FL_TEST(x,f) RB_FL_TEST(x,f)
@@ -1296,6 +1361,8 @@ void *rb_check_typeddata(VALUE, const rb_data_type_t *);
#define OBJ_FREEZE_RAW(x) RB_OBJ_FREEZE_RAW(x)
#define OBJ_FREEZE(x) RB_OBJ_FREEZE(x)
+/* \} */
+
void rb_freeze_singleton_class(VALUE klass);
static inline void
@@ -1473,7 +1540,7 @@ rb_obj_write(VALUE a, VALUE *slot, VALUE b, RB_UNUSED_VAR(const char *filename),
*slot = b;
#if USE_RGENGC
- rb_obj_written(a, Qundef /* ignore `oldv' now */, b, filename, line);
+ rb_obj_written(a, RUBY_Qundef /* ignore `oldv' now */, b, filename, line);
#endif
return a;
}
@@ -1561,14 +1628,22 @@ rb_num2char_inline(VALUE x)
#define LONG2NUM(x) RB_LONG2NUM(x)
#define ULONG2NUM(x) RB_ULONG2NUM(x)
+#define USHORT2NUM(x) RB_INT2FIX(x)
#define NUM2CHR(x) RB_NUM2CHR(x)
#define CHR2FIX(x) RB_CHR2FIX(x)
-#define RB_ALLOC_N(type,n) ((type*)ruby_xmalloc2((n),sizeof(type)))
+#if SIZEOF_LONG < SIZEOF_VALUE
+#define RB_ST2FIX(h) RB_LONG2FIX((long)((h) > 0 ? (h) & (unsigned long)-1 >> 2 : (h) | ~((unsigned long)-1 >> 2)))
+#else
+#define RB_ST2FIX(h) RB_LONG2FIX((long)(h))
+#endif
+#define ST2FIX(h) RB_ST2FIX(h)
+
+#define RB_ALLOC_N(type,n) ((type*)ruby_xmalloc2((size_t)(n),sizeof(type)))
#define RB_ALLOC(type) ((type*)ruby_xmalloc(sizeof(type)))
-#define RB_ZALLOC_N(type,n) ((type*)ruby_xcalloc((n),sizeof(type)))
+#define RB_ZALLOC_N(type,n) ((type*)ruby_xcalloc((size_t)(n),sizeof(type)))
#define RB_ZALLOC(type) (RB_ZALLOC_N(type,1))
-#define RB_REALLOC_N(var,type,n) ((var)=(type*)ruby_xrealloc2((char*)(var),(n),sizeof(type)))
+#define RB_REALLOC_N(var,type,n) ((var)=(type*)ruby_xrealloc2((char*)(var),(size_t)(n),sizeof(type)))
#define ALLOC_N(type,n) RB_ALLOC_N(type,n)
#define ALLOC(type) RB_ALLOC(type)
@@ -1576,7 +1651,23 @@ rb_num2char_inline(VALUE x)
#define ZALLOC(type) RB_ZALLOC(type)
#define REALLOC_N(var,type,n) RB_REALLOC_N(var,type,n)
+#if GCC_VERSION_BEFORE(4,9,5)
+/* GCC 4.9.2 reportedly has this feature and is broken.
+ * The function is not officially documented below.
+ * Seems we should not use it.
+ * https://gcc.gnu.org/onlinedocs/gcc-4.9.4/gcc/Other-Builtins.html#Other-Builtins */
+# undef HAVE_BUILTIN___BUILTIN_ALLOCA_WITH_ALIGN
+#endif
+
+#if defined(HAVE_BUILTIN___BUILTIN_ALLOCA_WITH_ALIGN) && defined(RUBY_ALIGNOF)
+/* I don't know why but __builtin_alloca_with_align's second argument
+ takes bits rather than bytes. */
+#define ALLOCA_N(type, n) \
+ (type*)__builtin_alloca_with_align((sizeof(type)*(n)), \
+ RUBY_ALIGNOF(type) * CHAR_BIT)
+#else
#define ALLOCA_N(type,n) ((type*)alloca(sizeof(type)*(n)))
+#endif
void *rb_alloc_tmp_buffer(volatile VALUE *store, long len) RUBY_ATTR_ALLOC_SIZE((2));
void *rb_alloc_tmp_buffer_with_count(volatile VALUE *store, size_t len,size_t count) RUBY_ATTR_ALLOC_SIZE((2,3));
@@ -1607,14 +1698,14 @@ static inline void *
rb_alloc_tmp_buffer2(volatile VALUE *store, long count, size_t elsize)
{
size_t cnt = (size_t)count;
- if (elsize % sizeof(VALUE) == 0) {
+ if (elsize == sizeof(VALUE)) {
if (RB_UNLIKELY(cnt > LONG_MAX / sizeof(VALUE))) {
ruby_malloc_size_overflow(cnt, elsize);
}
}
else {
size_t size, max = LONG_MAX - sizeof(VALUE) + 1;
- if (RB_UNLIKELY(rb_mul_size_overflow(count, elsize, max, &size))) {
+ if (RB_UNLIKELY(rb_mul_size_overflow(cnt, elsize, max, &size))) {
ruby_malloc_size_overflow(cnt, elsize);
}
cnt = (size + sizeof(VALUE) - 1) / sizeof(VALUE);
@@ -1626,16 +1717,16 @@ rb_alloc_tmp_buffer2(volatile VALUE *store, long count, size_t elsize)
#ifdef C_ALLOCA
# define RB_ALLOCV(v, n) rb_alloc_tmp_buffer(&(v), (n))
# define RB_ALLOCV_N(type, v, n) \
- rb_alloc_tmp_buffer2(&(v), (n), sizeof(type))))
+ rb_alloc_tmp_buffer2(&(v), (n), sizeof(type))
#else
# define RUBY_ALLOCV_LIMIT 1024
# define RB_ALLOCV(v, n) ((n) < RUBY_ALLOCV_LIMIT ? \
- (RB_GC_GUARD(v) = 0, alloca(n)) : \
+ ((v) = 0, alloca(n)) : \
rb_alloc_tmp_buffer(&(v), (n)))
# define RB_ALLOCV_N(type, v, n) \
((type*)(((size_t)(n) < RUBY_ALLOCV_LIMIT / sizeof(type)) ? \
- (RB_GC_GUARD(v) = 0, alloca((n) * sizeof(type))) : \
- rb_alloc_tmp_buffer2(&(v), (n), sizeof(type))))
+ ((v) = 0, alloca((size_t)(n) * sizeof(type))) : \
+ rb_alloc_tmp_buffer2(&(v), (long)(n), sizeof(type))))
#endif
#define RB_ALLOCV_END(v) rb_free_tmp_buffer(&(v))
@@ -1643,12 +1734,12 @@ rb_alloc_tmp_buffer2(volatile VALUE *store, long count, size_t elsize)
#define ALLOCV_N(type, v, n) RB_ALLOCV_N(type, v, n)
#define ALLOCV_END(v) RB_ALLOCV_END(v)
-#define MEMZERO(p,type,n) memset((p), 0, sizeof(type)*(n))
-#define MEMCPY(p1,p2,type,n) memcpy((p1), (p2), sizeof(type)*(n))
-#define MEMMOVE(p1,p2,type,n) memmove((p1), (p2), sizeof(type)*(n))
-#define MEMCMP(p1,p2,type,n) memcmp((p1), (p2), sizeof(type)*(n))
+#define MEMZERO(p,type,n) memset((p), 0, sizeof(type)*(size_t)(n))
+#define MEMCPY(p1,p2,type,n) memcpy((p1), (p2), sizeof(type)*(size_t)(n))
+#define MEMMOVE(p1,p2,type,n) memmove((p1), (p2), sizeof(type)*(size_t)(n))
+#define MEMCMP(p1,p2,type,n) memcmp((p1), (p2), sizeof(type)*(size_t)(n))
-void rb_obj_infect(VALUE,VALUE);
+void rb_obj_infect(VALUE victim, VALUE carrier);
typedef int ruby_glob_func(const char*,VALUE, void*);
void rb_glob(const char*,void(*)(const char*,VALUE,void*),VALUE);
@@ -1727,7 +1818,7 @@ VALUE rb_check_symbol(volatile VALUE *namep);
do RUBY_CONST_ID_CACHE((var) =, (str)) while (0)
#define CONST_ID_CACHE(result, str) RUBY_CONST_ID_CACHE(result, str)
#define CONST_ID(var, str) RUBY_CONST_ID(var, str)
-#ifdef __GNUC__
+#if defined(HAVE_BUILTIN___BUILTIN_CONSTANT_P) && defined(HAVE_STMT_AND_DECL_IN_EXPR)
/* __builtin_constant_p and statement expression is available
* since gcc-2.7.2.3 at least. */
#define rb_intern(str) \
@@ -1738,6 +1829,31 @@ VALUE rb_check_symbol(volatile VALUE *namep);
(__builtin_constant_p(str) ? \
__extension__ (rb_intern2((str), (long)strlen(str))) : \
(rb_intern)(str))
+
+# define rb_varargs_argc_check_runtime(argc, vargc) \
+ (((argc) <= (vargc)) ? (argc) : \
+ (rb_fatal("argc(%d) exceeds actual arguments(%d)", \
+ argc, vargc), 0))
+# define rb_varargs_argc_valid_p(argc, vargc) \
+ ((argc) == 0 ? (vargc) <= 1 : /* [ruby-core:85266] [Bug #14425] */ \
+ (argc) == (vargc))
+# if defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P)
+# if HAVE_ATTRIBUTE_ERRORFUNC
+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))
+# 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))
+# else
+# define rb_varargs_argc_check(argc, vargc) \
+ rb_varargs_argc_check_runtime(argc, vargc)
+# endif
+
#else
#define rb_intern_const(str) rb_intern2((str), (long)strlen(str))
#endif
@@ -1785,7 +1901,7 @@ enum rb_io_wait_readwrite {RB_IO_WAIT_READABLE, RB_IO_WAIT_WRITABLE};
PRINTF_ARGS(NORETURN(void rb_raise(VALUE, const char*, ...)), 2, 3);
PRINTF_ARGS(NORETURN(void rb_fatal(const char*, ...)), 1, 2);
-PRINTF_ARGS(NORETURN(void rb_bug(const char*, ...)), 1, 2);
+COLDFUNC PRINTF_ARGS(NORETURN(void rb_bug(const char*, ...)), 1, 2);
NORETURN(void rb_bug_errno(const char*, int));
NORETURN(void rb_sys_fail(const char*));
NORETURN(void rb_sys_fail_str(VALUE));
@@ -1809,7 +1925,7 @@ PRINTF_ARGS(void rb_warning(const char*, ...), 1, 2);
PRINTF_ARGS(void rb_compile_warning(const char *, int, const char*, ...), 3, 4);
PRINTF_ARGS(void rb_sys_warning(const char*, ...), 1, 2);
/* reports always */
-PRINTF_ARGS(void rb_warn(const char*, ...), 1, 2);
+COLDFUNC PRINTF_ARGS(void rb_warn(const char*, ...), 1, 2);
PRINTF_ARGS(void rb_compile_warn(const char *, int, const char*, ...), 3, 4);
#define RUBY_BLOCK_CALL_FUNC_TAKES_BLOCKARG 1
@@ -1863,19 +1979,20 @@ RUBY_EXTERN VALUE rb_cBignum;
RUBY_EXTERN VALUE rb_cBinding;
RUBY_EXTERN VALUE rb_cClass;
RUBY_EXTERN VALUE rb_cCont;
-RUBY_EXTERN VALUE rb_cDir;
RUBY_EXTERN VALUE rb_cData;
-RUBY_EXTERN VALUE rb_cFalseClass;
+RUBY_EXTERN VALUE rb_cDir;
RUBY_EXTERN VALUE rb_cEncoding;
RUBY_EXTERN VALUE rb_cEnumerator;
+RUBY_EXTERN VALUE rb_cFalseClass;
RUBY_EXTERN VALUE rb_cFile;
#ifndef RUBY_INTEGER_UNIFICATION
RUBY_EXTERN VALUE rb_cFixnum;
#endif
+RUBY_EXTERN VALUE rb_cComplex;
RUBY_EXTERN VALUE rb_cFloat;
RUBY_EXTERN VALUE rb_cHash;
-RUBY_EXTERN VALUE rb_cInteger;
RUBY_EXTERN VALUE rb_cIO;
+RUBY_EXTERN VALUE rb_cInteger;
RUBY_EXTERN VALUE rb_cMatch;
RUBY_EXTERN VALUE rb_cMethod;
RUBY_EXTERN VALUE rb_cModule;
@@ -1886,7 +2003,6 @@ RUBY_EXTERN VALUE rb_cProc;
RUBY_EXTERN VALUE rb_cRandom;
RUBY_EXTERN VALUE rb_cRange;
RUBY_EXTERN VALUE rb_cRational;
-RUBY_EXTERN VALUE rb_cComplex;
RUBY_EXTERN VALUE rb_cRegexp;
RUBY_EXTERN VALUE rb_cStat;
RUBY_EXTERN VALUE rb_cString;
@@ -1911,6 +2027,7 @@ RUBY_EXTERN VALUE rb_eKeyError;
RUBY_EXTERN VALUE rb_eRangeError;
RUBY_EXTERN VALUE rb_eIOError;
RUBY_EXTERN VALUE rb_eRuntimeError;
+RUBY_EXTERN VALUE rb_eFrozenError;
RUBY_EXTERN VALUE rb_eSecurityError;
RUBY_EXTERN VALUE rb_eSystemCallError;
RUBY_EXTERN VALUE rb_eThreadError;
@@ -1944,7 +2061,7 @@ rb_class_of(VALUE obj)
if (obj == RUBY_Qtrue) return rb_cTrueClass;
if (RB_STATIC_SYM_P(obj)) return rb_cSymbol;
}
- else if (!RTEST(obj)) {
+ else if (!RB_TEST(obj)) {
if (obj == RUBY_Qnil) return rb_cNilClass;
if (obj == RUBY_Qfalse) return rb_cFalseClass;
}
@@ -1961,7 +2078,7 @@ rb_type(VALUE obj)
if (RB_STATIC_SYM_P(obj)) return RUBY_T_SYMBOL;
if (obj == RUBY_Qundef) return RUBY_T_UNDEF;
}
- else if (!RTEST(obj)) {
+ else if (!RB_TEST(obj)) {
if (obj == RUBY_Qnil) return RUBY_T_NIL;
if (obj == RUBY_Qfalse) return RUBY_T_FALSE;
}
@@ -1996,7 +2113,8 @@ rb_special_const_p(VALUE obj)
static inline void
rb_clone_setup(VALUE clone, VALUE obj)
{
- rb_obj_setup(clone, rb_singleton_class_clone(obj), RBASIC(obj)->flags);
+ rb_obj_setup(clone, rb_singleton_class_clone(obj),
+ RBASIC(obj)->flags & ~(FL_PROMOTED0|FL_PROMOTED1|FL_FINALIZE));
rb_singleton_class_attached(RBASIC_CLASS(clone), clone);
if (RB_FL_TEST(obj, RUBY_FL_EXIVAR)) rb_copy_generic_ivar(clone, obj);
}
@@ -2023,13 +2141,54 @@ rb_array_len(VALUE a)
# define FIX_CONST_VALUE_PTR(x) (x)
#endif
+/* internal function. do not use this function */
static inline const VALUE *
-rb_array_const_ptr(VALUE a)
+rb_array_const_ptr_transient(VALUE a)
{
return FIX_CONST_VALUE_PTR((RBASIC(a)->flags & RARRAY_EMBED_FLAG) ?
RARRAY(a)->as.ary : RARRAY(a)->as.heap.ptr);
}
+/* internal function. do not use this function */
+static inline const VALUE *
+rb_array_const_ptr(VALUE a)
+{
+#if USE_TRANSIENT_HEAP
+ void rb_ary_detransient(VALUE a);
+
+ if (RARRAY_TRANSIENT_P(a)) {
+ rb_ary_detransient(a);
+ }
+#endif
+ return rb_array_const_ptr_transient(a);
+}
+
+/* internal function. do not use this function */
+static inline VALUE *
+rb_array_ptr_use_start(VALUE a, int allow_transient)
+{
+ VALUE *rb_ary_ptr_use_start(VALUE ary);
+
+#if USE_TRANSIENT_HEAP
+ if (!allow_transient) {
+ if (RARRAY_TRANSIENT_P(a)) {
+ void rb_ary_detransient(VALUE a);
+ rb_ary_detransient(a);
+ }
+ }
+#endif
+
+ return rb_ary_ptr_use_start(a);
+}
+
+/* internal function. do not use this function */
+static inline void
+rb_array_ptr_use_end(VALUE a, int allow_transient)
+{
+ void rb_ary_ptr_use_end(VALUE a);
+ rb_ary_ptr_use_end(a);
+}
+
#if defined(EXTLIB) && defined(USE_DLN_A_OUT)
/* hook for external modules */
static char *dln_libs_to_be_linked[] = { EXTLIB, 0 };
@@ -2057,11 +2216,11 @@ int ruby_native_thread_p(void);
#define RUBY_EVENT_THREAD_BEGIN 0x0400
#define RUBY_EVENT_THREAD_END 0x0800
#define RUBY_EVENT_FIBER_SWITCH 0x1000
+#define RUBY_EVENT_SCRIPT_COMPILED 0x2000
#define RUBY_EVENT_TRACEPOINT_ALL 0xffff
/* special events */
-#define RUBY_EVENT_SPECIFIED_LINE 0x010000
-#define RUBY_EVENT_COVERAGE 0x020000
+#define RUBY_EVENT_RESERVED_FOR_INTERNAL_USE 0x030000
/* internal events */
#define RUBY_INTERNAL_EVENT_SWITCH 0x040000
@@ -2075,7 +2234,7 @@ int ruby_native_thread_p(void);
#define RUBY_INTERNAL_EVENT_GC_ENTER 0x2000000
#define RUBY_INTERNAL_EVENT_GC_EXIT 0x4000000
#define RUBY_INTERNAL_EVENT_OBJSPACE_MASK 0x7f00000
-#define RUBY_INTERNAL_EVENT_MASK 0xfffe0000
+#define RUBY_INTERNAL_EVENT_MASK 0xffff0000
typedef uint32_t rb_event_flag_t;
typedef void (*rb_event_hook_func_t)(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass);
@@ -2130,7 +2289,7 @@ unsigned long ruby_strtoul(const char *str, char **endptr, int base);
PRINTF_ARGS(int ruby_snprintf(char *str, size_t n, char const *fmt, ...), 3, 4);
int ruby_vsnprintf(char *str, size_t n, char const *fmt, va_list ap);
-#if defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P) && defined(__OPTIMIZE__)
+#if defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P) && defined(HAVE_VA_ARGS_MACRO) && defined(__OPTIMIZE__)
# define rb_scan_args(argc,argvp,fmt,...) \
__builtin_choose_expr(__builtin_constant_p(fmt), \
rb_scan_args0(argc,argvp,fmt,\
@@ -2147,8 +2306,16 @@ ERRORFUNC(("variable argument length doesn't match"), int rb_scan_args_length_mi
# define rb_scan_args_isdigit(c) ((unsigned char)((c)-'0')<10)
-# define rb_scan_args_count_end(fmt, ofs, varc, vari) \
- ((vari)/(!fmt[ofs] || rb_scan_args_bad_format(fmt)))
+#if !defined(__has_attribute)
+#define __has_attribute(x) 0
+#endif
+# if __has_attribute(diagnose_if)
+# define rb_scan_args_count_end(fmt, ofs, varc, vari) \
+ (fmt[ofs] ? rb_scan_args_bad_format(fmt) : (vari))
+# else
+# define rb_scan_args_count_end(fmt, ofs, varc, vari) \
+ ((vari)/(!fmt[ofs] || rb_scan_args_bad_format(fmt)))
+# endif
# define rb_scan_args_count_block(fmt, ofs, varc, vari) \
(fmt[ofs]!='&' ? \
@@ -2176,18 +2343,19 @@ ERRORFUNC(("variable argument length doesn't match"), int rb_scan_args_length_mi
rb_scan_args_count_var(fmt, ofs+1, varc, vari+fmt[ofs]-'0'))
# define rb_scan_args_count(fmt, varc) \
- ((!rb_scan_args_isdigit(fmt[0]) ? \
+ (!rb_scan_args_isdigit(fmt[0]) ? \
rb_scan_args_count_var(fmt, 0, varc, 0) : \
- rb_scan_args_count_opt(fmt, 1, varc, fmt[0]-'0')) \
- == (varc) || \
- rb_scan_args_length_mismatch(fmt, varc))
+ rb_scan_args_count_opt(fmt, 1, varc, fmt[0]-'0'))
# define rb_scan_args_verify_count(fmt, varc) \
- ((varc)/(rb_scan_args_count(fmt, varc)))
+ ((varc)/(rb_scan_args_count(fmt, varc) == (varc) || \
+ rb_scan_args_length_mismatch(fmt, varc)))
-# ifdef __GNUC__
+# if defined(__has_attribute) && __has_attribute(diagnose_if)
+# define rb_scan_args_verify(fmt, varc) (void)0
+# elif defined(__GNUC__)
# define rb_scan_args_verify(fmt, varc) \
- ({ \
+ (void)__extension__ ({ \
int verify; \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Warray-bounds\""); \
@@ -2197,7 +2365,7 @@ ERRORFUNC(("variable argument length doesn't match"), int rb_scan_args_length_mi
})
# else
# define rb_scan_args_verify(fmt, varc) \
- rb_scan_args_verify_count(fmt, varc)
+ (void)rb_scan_args_verify_count(fmt, varc)
# endif
ALWAYS_INLINE(static int rb_scan_args_lead_p(const char *fmt));
@@ -2246,29 +2414,16 @@ ALWAYS_INLINE(static int rb_scan_args_trail_idx(const char *fmt));
static inline int
rb_scan_args_trail_idx(const char *fmt)
{
- return (rb_scan_args_lead_p(fmt) ?
- (rb_scan_args_isdigit(fmt[1]) || fmt[1]=='*')+1 :
- (fmt[0]=='*'));
-}
-
-ALWAYS_INLINE(static int rb_scan_args_trail_p(const char *fmt));
-static inline int
-rb_scan_args_trail_p(const char *fmt)
-{
- return (rb_scan_args_lead_p(fmt) ?
- (rb_scan_args_isdigit(fmt[1]) || fmt[1]=='*') &&
- rb_scan_args_isdigit(fmt[2]) :
- fmt[0]=='*' && rb_scan_args_isdigit(fmt[1]));
+ const int idx = rb_scan_args_var_idx(fmt);
+ return idx+(fmt[idx]=='*');
}
ALWAYS_INLINE(static int rb_scan_args_n_trail(const char *fmt));
static inline int
rb_scan_args_n_trail(const char *fmt)
{
- return (rb_scan_args_lead_p(fmt) ?
- ((rb_scan_args_isdigit(fmt[1]) || fmt[1]=='*') &&
- rb_scan_args_isdigit(fmt[2]) ? fmt[2]-'0' : 0) :
- (fmt[0]=='*' && rb_scan_args_isdigit(fmt[1]) ? fmt[1]-'0' : 0));
+ const int idx = rb_scan_args_trail_idx(fmt);
+ return (rb_scan_args_isdigit(fmt[idx]) ? fmt[idx]-'0' : 0);
}
ALWAYS_INLINE(static int rb_scan_args_hash_idx(const char *fmt));
@@ -2311,6 +2466,8 @@ rb_scan_args_end_idx(const char *fmt)
}
# endif
+/* NOTE: Use `char *fmt` instead of `const char *fmt` because of clang's bug*/
+/* https://bugs.llvm.org/show_bug.cgi?id=38095 */
# define rb_scan_args0(argc, argv, fmt, varc, vars) \
rb_scan_args_set(argc, argv, \
rb_scan_args_n_lead(fmt), \
@@ -2319,27 +2476,32 @@ rb_scan_args_end_idx(const char *fmt)
rb_scan_args_f_var(fmt), \
rb_scan_args_f_hash(fmt), \
rb_scan_args_f_block(fmt), \
- rb_scan_args_verify(fmt, varc), vars)
+ (rb_scan_args_verify(fmt, varc), vars), (char *)fmt, varc)
ALWAYS_INLINE(static int
rb_scan_args_set(int argc, const VALUE *argv,
int n_lead, int n_opt, int n_trail,
int f_var, int f_hash, int f_block,
- int varc, VALUE *vars[]));
+ VALUE *vars[], char *fmt, int varc));
+
inline int
rb_scan_args_set(int argc, const VALUE *argv,
int n_lead, int n_opt, int n_trail,
int f_var, int f_hash, int f_block,
- int varc, VALUE *vars[])
+ VALUE *vars[], RB_UNUSED_VAR(char *fmt), RB_UNUSED_VAR(int varc))
+# if defined(__has_attribute) && __has_attribute(diagnose_if)
+ __attribute__((diagnose_if(rb_scan_args_count(fmt,varc)==0,"bad scan arg format","error")))
+ __attribute__((diagnose_if(rb_scan_args_count(fmt,varc)!=varc,"variable argument length doesn't match","error")))
+# endif
{
- int i, argi = 0, vari = 0;
- VALUE *var, hash = Qnil;
+ int i, argi = 0, vari = 0, last_idx = -1;
+ VALUE *var, hash = Qnil, last_hash = 0;
const int n_mand = n_lead + n_trail;
/* capture an option hash - phase 1: pop */
if (f_hash && n_mand < argc) {
VALUE last = argv[argc - 1];
- if (NIL_P(last)) {
+ if (RB_NIL_P(last)) {
/* nil is taken as an empty option hash only if it is not
ambiguous; i.e. '*' is not specified and arguments are
given more than sufficient */
@@ -2348,9 +2510,10 @@ rb_scan_args_set(int argc, const VALUE *argv,
}
else {
hash = rb_check_hash_type(last);
- if (!NIL_P(hash)) {
+ if (!RB_NIL_P(hash)) {
VALUE opts = rb_extract_keywords(&hash);
- if (!hash) argc--;
+ if (!(last_hash = hash)) argc--;
+ else last_idx = argc - 1;
hash = opts ? opts : Qnil;
}
}
@@ -2361,14 +2524,14 @@ rb_scan_args_set(int argc, const VALUE *argv,
/* capture leading mandatory arguments */
for (i = n_lead; i-- > 0; ) {
var = vars[vari++];
- if (var) *var = argv[argi];
+ if (var) *var = (argi == last_idx) ? last_hash : argv[argi];
argi++;
}
/* capture optional arguments */
for (i = n_opt; i-- > 0; ) {
var = vars[vari++];
if (argi < argc - n_trail) {
- if (var) *var = argv[argi];
+ if (var) *var = (argi == last_idx) ? last_hash : argv[argi];
argi++;
}
else {
@@ -2381,7 +2544,11 @@ rb_scan_args_set(int argc, const VALUE *argv,
var = vars[vari++];
if (0 < n_var) {
- if (var) *var = rb_ary_new4(n_var, &argv[argi]);
+ if (var) {
+ int f_last = (last_idx + 1 == argc - n_trail);
+ *var = rb_ary_new4(n_var-f_last, &argv[argi]);
+ if (f_last) rb_ary_push(*var, last_hash);
+ }
argi += n_var;
}
else {
@@ -2391,7 +2558,7 @@ rb_scan_args_set(int argc, const VALUE *argv,
/* capture trailing mandatory arguments */
for (i = n_trail; i-- > 0; ) {
var = vars[vari++];
- if (var) *var = argv[argi];
+ if (var) *var = (argi == last_idx) ? last_hash : argv[argi];
argi++;
}
/* capture an option hash - phase 2: assignment */
@@ -2414,6 +2581,30 @@ rb_scan_args_set(int argc, const VALUE *argv,
}
#endif
+#if 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); \
+ })
+
+# 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)); \
+ rb_funcallv(recv, mid, \
+ rb_varargs_argc_check(rb_funcall_argc, rb_funcall_nargs), \
+ rb_funcall_nargs ? rb_funcall_args : NULL); \
+ })
+#endif
+
#ifndef RUBY_DONT_SUBST
#include "ruby/subst.h"
#endif
diff --git a/include/ruby/st.h b/include/ruby/st.h
index ede75458c4..149e0ebaef 100644
--- a/include/ruby/st.h
+++ b/include/ruby/st.h
@@ -61,13 +61,10 @@ typedef char st_check_for_sizeof_st_index_t[SIZEOF_VOIDP == (int)sizeof(st_index
struct st_hash_type {
int (*compare)(ANYARGS /*st_data_t, st_data_t*/); /* st_compare_func* */
st_index_t (*hash)(ANYARGS /*st_data_t*/); /* st_hash_func* */
- /* The following is an optional func for stronger hash. When we
- have many different keys with the same hash we can switch to
- use it to prevent a denial attack with usage of hash table
- collisions. */
- st_index_t (*strong_hash)(ANYARGS /*st_data_t*/);
};
+#define ST_INDEX_BITS (SIZEOF_ST_INDEX_T * CHAR_BIT)
+
#if defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR) && defined(HAVE_BUILTIN___BUILTIN_TYPES_COMPATIBLE_P)
# define ST_DATA_COMPATIBLE_P(type) \
__builtin_choose_expr(__builtin_types_compatible_p(type, st_data_t), 1, 0)
@@ -82,12 +79,8 @@ struct st_table_entry; /* defined in st.c */
struct st_table {
/* Cached features of the table -- see st.c for more details. */
unsigned char entry_power, bin_power, size_ind;
- /* True when we are rebuilding the table. */
- unsigned char inside_rebuild_p;
/* How many times the table was rebuilt. */
unsigned int rebuilds_num;
- /* Currently used hash function. */
- st_index_t (*curr_hash)(ANYARGS /*st_data_t*/);
const struct st_hash_type *type;
/* Number of entries currently in the table. */
st_index_t num_entries;
@@ -150,6 +143,8 @@ CONSTFUNC(st_index_t st_hash_end(st_index_t h));
CONSTFUNC(st_index_t st_hash_start(st_index_t h));
#define st_hash_start(h) ((st_index_t)(h))
+void rb_hash_bulk_insert_into_st_table(long, const VALUE *, VALUE);
+
RUBY_SYMBOL_EXPORT_END
#if defined(__cplusplus)
diff --git a/include/ruby/util.h b/include/ruby/util.h
index 3b78e467b4..3fecba8ebc 100644
--- a/include/ruby/util.h
+++ b/include/ruby/util.h
@@ -70,7 +70,6 @@ char *ruby_strdup(const char *);
#define strdup(s) ruby_strdup(s)
char *ruby_getcwd(void);
-#define my_getcwd() ruby_getcwd()
double ruby_strtod(const char *, char **);
#undef strtod
diff --git a/include/ruby/version.h b/include/ruby/version.h
index 8b4801fcfd..a4d319f646 100644
--- a/include/ruby/version.h
+++ b/include/ruby/version.h
@@ -31,7 +31,7 @@
/* API version */
#define RUBY_API_VERSION_MAJOR 2
-#define RUBY_API_VERSION_MINOR 4
+#define RUBY_API_VERSION_MINOR 6
#define RUBY_API_VERSION_TEENY 0
#define RUBY_API_VERSION_CODE (RUBY_API_VERSION_MAJOR*10000+RUBY_API_VERSION_MINOR*100+RUBY_API_VERSION_TEENY)
diff --git a/include/ruby/vm.h b/include/ruby/vm.h
index 73345264bd..1bb170d2ce 100644
--- a/include/ruby/vm.h
+++ b/include/ruby/vm.h
@@ -25,7 +25,7 @@ RUBY_SYMBOL_EXPORT_BEGIN
*
* We will prepare VM creation/control APIs on 1.9.2 or later.
* If you have an interest about it, please see mvm branch.
- * http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/mvm/
+ * https://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/mvm/
*/
/* VM type declaration */
diff --git a/include/ruby/win32.h b/include/ruby/win32.h
index 68b8db73e6..fe1978fdde 100644
--- a/include/ruby/win32.h
+++ b/include/ruby/win32.h
@@ -139,6 +139,13 @@ typedef int clockid_t;
#undef fstat
#ifdef RUBY_EXPORT
#define utime(_p, _t) rb_w32_utime(_p, _t)
+#undef HAVE_UTIMES
+#define HAVE_UTIMES 1
+#define utimes(_p, _t) rb_w32_utimes(_p, _t)
+#undef HAVE_UTIMENSAT
+#define HAVE_UTIMENSAT 1
+#define AT_FDCWD -100
+#define utimensat(_d, _p, _t, _f) rb_w32_utimensat(_d, _p, _t, _f)
#define lseek(_f, _o, _w) _lseeki64(_f, _o, _w)
#define pipe(p) rb_w32_pipe(p)
@@ -151,7 +158,6 @@ typedef int clockid_t;
#define getppid() rb_w32_getppid()
#define sleep(x) rb_w32_Sleep((x)*1000)
#define Sleep(msec) (void)rb_w32_Sleep(msec)
-#define fstati64(fd,st) rb_w32_fstati64(fd,st)
#undef execv
#define execv(path,argv) rb_w32_aspawn(P_OVERLAY,path,argv)
@@ -166,26 +172,43 @@ typedef int clockid_t;
#define unlink(p) rb_w32_unlink(p)
#endif /* RUBY_EXPORT */
+/* same with stati64 except the size of st_ino and nanosecond timestamps */
+struct stati128 {
+ _dev_t st_dev;
+ unsigned __int64 st_ino;
+ __int64 st_inohigh;
+ unsigned short st_mode;
+ short st_nlink;
+ short st_uid;
+ short st_gid;
+ _dev_t st_rdev;
+ __int64 st_size;
+ __time64_t st_atime;
+ long st_atimensec;
+ __time64_t st_mtime;
+ long st_mtimensec;
+ __time64_t st_ctime;
+ long st_ctimensec;
+};
+
#if SIZEOF_OFF_T == 8
#define off_t __int64
-#define stat stati64
-#define fstat(fd,st) fstati64(fd,st)
-#if !defined(_MSC_VER) || RUBY_MSVCRT_VERSION < 80
-#define stati64 _stati64
-#ifndef _stati64
-#define _stati64(path, st) rb_w32_stati64(path, st)
-#endif
-#else
-#define stati64 _stat64
-#define _stat64(path, st) rb_w32_stati64(path, st)
-#endif
+#define stat stati128
+#undef SIZEOF_STRUCT_STAT_ST_INO
+#define SIZEOF_STRUCT_STAT_ST_INO sizeof(unsigned __int64)
+#define HAVE_STRUCT_STAT_ST_INOHIGH
+#define HAVE_STRUCT_STAT_ST_ATIMENSEC
+#define HAVE_STRUCT_STAT_ST_MTIMENSEC
+#define HAVE_STRUCT_STAT_ST_CTIMENSEC
+#define fstat(fd,st) rb_w32_fstati128(fd,st)
+#define stati128(path, st) rb_w32_stati128(path,st)
#else
#define stat(path,st) rb_w32_stat(path,st)
#define fstat(fd,st) rb_w32_fstat(fd,st)
extern int rb_w32_stat(const char *, struct stat *);
extern int rb_w32_fstat(int, struct stat *);
#endif
-#define lstat(path,st) rb_w32_lstati64(path,st)
+#define lstat(path,st) rb_w32_lstati128(path,st)
#define access(path,mode) rb_w32_access(path,mode)
#define strcasecmp _stricmp
@@ -233,6 +256,7 @@ struct ifaddrs {
#define IFF_POINTOPOINT IFF_POINTTOPOINT
#endif
+extern void rb_w32_sysinit(int *, char ***);
extern DWORD rb_w32_osid(void);
extern rb_pid_t rb_w32_pipe_exec(const char *, const char *, int, int *, int *);
extern int flock(int fd, int oper);
@@ -315,14 +339,14 @@ extern int rb_w32_urmdir(const char *);
extern int rb_w32_unlink(const char *);
extern int rb_w32_uunlink(const char *);
extern int rb_w32_uchmod(const char *, int);
-extern int rb_w32_stati64(const char *, struct stati64 *);
-extern int rb_w32_ustati64(const char *, struct stati64 *);
-extern int rb_w32_lstati64(const char *, struct stati64 *);
-extern int rb_w32_ulstati64(const char *, struct stati64 *);
+extern int rb_w32_stati128(const char *, struct stati128 *);
+extern int rb_w32_ustati128(const char *, struct stati128 *);
+extern int rb_w32_lstati128(const char *, struct stati128 *);
+extern int rb_w32_ulstati128(const char *, struct stati128 *);
extern int rb_w32_access(const char *, int);
extern int rb_w32_uaccess(const char *, int);
extern char rb_w32_fd_is_text(int);
-extern int rb_w32_fstati64(int, struct stati64 *);
+extern int rb_w32_fstati128(int, struct stati128 *);
extern int rb_w32_dup2(int, int);
#include <float.h>
@@ -439,8 +463,6 @@ extern rb_gid_t getegid (void);
extern int setuid (rb_uid_t);
extern int setgid (rb_gid_t);
-extern int fstati64(int, struct stati64 *);
-
extern char *rb_w32_strerror(int);
#ifdef RUBY_EXPORT
@@ -731,6 +753,10 @@ ssize_t rb_w32_read(int, void *, size_t);
ssize_t rb_w32_write(int, const void *, size_t);
int rb_w32_utime(const char *, const struct utimbuf *);
int rb_w32_uutime(const char *, const struct utimbuf *);
+int rb_w32_utimes(const char *, const struct timeval *);
+int rb_w32_uutimes(const char *, const struct timeval *);
+int rb_w32_utimensat(int /* must be AT_FDCWD */, const char *, const struct timespec *, int /* must be 0 */);
+int rb_w32_uutimensat(int /* must be AT_FDCWD */, const char *, const struct timespec *, int /* must be 0 */);
long rb_w32_write_console(uintptr_t, int); /* use uintptr_t instead of VALUE because it's not defined yet here */
int WINAPI rb_w32_Sleep(unsigned long msec);
int rb_w32_wait_events_blocking(HANDLE *events, int num, DWORD timeout);
@@ -752,7 +778,8 @@ uintptr_t rb_w32_asynchronize(asynchronous_func_t func, uintptr_t self, int argc
RUBY_SYMBOL_EXPORT_END
-#ifdef __MINGW_ATTRIB_PURE
+#if (defined(__MINGW64_VERSION_MAJOR) || defined(__MINGW64__)) && !defined(__cplusplus)
+#ifdef RUBY_MINGW64_BROKEN_FREXP_MODF
/* License: Ruby's */
/* get rid of bugs in math.h of mingw */
#define frexp(_X, _Y) __extension__ ({\
@@ -770,13 +797,6 @@ RUBY_SYMBOL_EXPORT_END
})
#endif
-#if defined(__cplusplus)
-#if 0
-{ /* satisfy cc-mode */
-#endif
-} /* extern "C" { */
-#endif
-
#if defined(__MINGW64__)
/*
* Use powl() instead of broken pow() of x86_64-w64-mingw32.
@@ -786,13 +806,19 @@ RUBY_SYMBOL_EXPORT_END
static inline double
rb_w32_pow(double x, double y)
{
- return powl(x, y);
+ return (double)powl(x, y);
}
#elif defined(__MINGW64_VERSION_MAJOR)
double rb_w32_pow(double x, double y);
#endif
-#if defined(__MINGW64_VERSION_MAJOR) || defined(__MINGW64__)
#define pow rb_w32_pow
#endif
+#if defined(__cplusplus)
+#if 0
+{ /* satisfy cc-mode */
+#endif
+} /* extern "C" { */
+#endif
+
#endif /* RUBY_WIN32_H */
diff --git a/inits.c b/inits.c
index 5822f04cab..f730903b5e 100644
--- a/inits.c
+++ b/inits.c
@@ -16,6 +16,10 @@
void
rb_call_inits(void)
{
+#if USE_TRANSIENT_HEAP
+ CALL(TransientHeap);
+#endif
+ CALL(vm_postponed_job);
CALL(Method);
CALL(RandomSeedCore);
CALL(sym);
@@ -61,5 +65,7 @@ rb_call_inits(void)
CALL(Complex);
CALL(version);
CALL(vm_trace);
+ CALL(vm_stack_canary);
+ CALL(ast);
}
#undef CALL
diff --git a/insns.def b/insns.def
index fdda7d25de..6c62f99770 100644
--- a/insns.def
+++ b/insns.def
@@ -1,36 +1,62 @@
-/** ##skip -*- mode:c; style:ruby; coding: utf-8 -*-
+/* -*- mode:c; style:ruby; coding: utf-8 -*-
insns.def - YARV instruction definitions
$Author: $
created at: 04/01/01 01:17:55 JST
Copyright (C) 2004-2007 Koichi Sasada
-*/
-
-/** ##skip
- instruction comment
- @c: category
- @e: english description
- @j: japanese description
-
- instruction form:
- DEFINE_INSN
- instruction_name
- (instruction_operands, ..)
- (pop_values, ..)
- (return value)
- {
+ Massive rewrite by @shyouhei in 2017.
+ */
+
+/* Some comments about this file's contents:
+
+ - The new format aims to be editable by C editor of your choice;
+ your mileage might vary of course.
+
+ - Each instructions are in following format:
+
+ DEFINE_INSN
+ instruction_name
+ (type operand, type operand, ..)
+ (pop_values, ..)
+ (return values ..)
+ // attr type name contents..
+ {
.. // insn body
- }
+ }
- */
+ - Unlike the old format which was line-oriented, you can now place
+ newlines and comments at liberal positions.
+
+ - `DEFINE_INSN` is a keyword.
+ - An instruction name must be a valid C identifier.
-/**
- @c nop
- @e nop
- @j nop
+ - Operands, pop values, return values are series of either variable
+ declarations, keyword `void`, or keyword `...`. They are much
+ like C function declarations.
+
+ - Attribute pragmas are optional, and can include arbitrary C
+ expressions. You can write anything there but as of writing,
+ supported attributes are:
+
+ * sp_inc: Used to dynamically calculate sp increase in
+ `insn_stack_increase`.
+
+ * handles_sp: If it is true, VM deals with sp in the insn.
+ Default is if the instruction takes ISEQ operand or not.
+
+ * leaf: indicates that the instruction is "leaf" i.e. it does
+ not introduce new stack frame on top of it.
+ If an instruction handles sp, that can never be a leaf.
+
+ - Attributes can access operands, but not stack (push/pop) variables.
+
+ - An instruction's body is a pure C block, copied verbatimly into
+ the generated C source code.
*/
+
+/* nop */
DEFINE_INSN
nop
()
@@ -44,12 +70,8 @@ nop
/* deal with variables */
/**********************************************************/
-/**
- @c variable
- @e Get local variable (pointed by `idx' and `level').
+/* Get local variable (pointed by `idx' and `level').
'level' indicates the nesting depth from the current block.
- @j level, idx ã§æŒ‡å®šã•れãŸãƒ­ãƒ¼ã‚«ãƒ«å¤‰æ•°ã®å€¤ã‚’スタックã«ç½®ã。
- level ã¯ãƒ–ロックã®ãƒã‚¹ãƒˆãƒ¬ãƒ™ãƒ«ã§ã€ä½•段上ã‹ã‚’示ã™ã€‚
*/
DEFINE_INSN
getlocal
@@ -57,22 +79,13 @@ getlocal
()
(VALUE val)
{
- int i, lev = (int)level;
- const VALUE *ep = GET_EP();
-
- /* optimized insns generated for level == (0|1) in defs/opt_operand.def */
- for (i = 0; i < lev; i++) {
- ep = GET_PREV_EP(ep);
- }
- val = *(ep - idx);
+ val = *(vm_get_ep(GET_EP(), level) - idx);
+ RB_DEBUG_COUNTER_INC(lvar_get);
+ (void)RB_DEBUG_COUNTER_INC_IF(lvar_get_dynamic, level > 0);
}
-/**
- @c variable
- @e Set a local variable (pointed to by 'idx') as val.
+/* Set a local variable (pointed to by 'idx') as val.
'level' indicates the nesting depth from the current block.
- @j level, idx ã§æŒ‡å®šã•れãŸãƒ­ãƒ¼ã‚«ãƒ«å¤‰æ•°ã®å€¤ã‚’ val ã«ã™ã‚‹ã€‚
- level ã¯ãƒ–ロックã®ãƒã‚¹ãƒˆãƒ¬ãƒ™ãƒ«ã§ã€ä½•段上ã‹ã‚’示ã™ã€‚
*/
DEFINE_INSN
setlocal
@@ -80,65 +93,128 @@ setlocal
(VALUE val)
()
{
- int i, lev = (int)level;
- const VALUE *ep = GET_EP();
+ vm_env_write(vm_get_ep(GET_EP(), level), -(int)idx, val);
+ RB_DEBUG_COUNTER_INC(lvar_set);
+ (void)RB_DEBUG_COUNTER_INC_IF(lvar_set_dynamic, level > 0);
+}
- /* optimized insns generated for level == (0|1) in defs/opt_operand.def */
- for (i = 0; i < lev; i++) {
- ep = GET_PREV_EP(ep);
+/* Get a block parameter. */
+DEFINE_INSN
+getblockparam
+(lindex_t idx, rb_num_t level)
+()
+(VALUE val)
+{
+ const VALUE *ep = vm_get_ep(GET_EP(), level);
+ 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);
+ }
+ else {
+ val = *(ep - idx);
+ RB_DEBUG_COUNTER_INC(lvar_get);
+ (void)RB_DEBUG_COUNTER_INC_IF(lvar_get_dynamic, level > 0);
}
+}
+
+/* Set block parameter. */
+DEFINE_INSN
+setblockparam
+(lindex_t idx, rb_num_t level)
+(VALUE val)
+()
+{
+ const VALUE *ep = vm_get_ep(GET_EP(), level);
+ VM_ASSERT(VM_ENV_LOCAL_P(ep));
+
vm_env_write(ep, -(int)idx, val);
+ RB_DEBUG_COUNTER_INC(lvar_set);
+ (void)RB_DEBUG_COUNTER_INC_IF(lvar_set_dynamic, level > 0);
+
+ VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM);
}
-/**
- @c variable
- @e Get value of special local variable ($~, $_, ..).
- @j 特殊ãªãƒ­ãƒ¼ã‚«ãƒ«å¤‰æ•°ï¼ˆ$~, $_, ...)ã®å€¤ã‚’得る。
+/* Get special proxy object which only responds to `call` method if the block parameter
+ represents a iseq/ifunc block. Otherwise, same as `getblockparam`.
*/
DEFINE_INSN
+getblockparamproxy
+(lindex_t idx, rb_num_t level)
+()
+(VALUE val)
+{
+ const VALUE *ep = vm_get_ep(GET_EP(), level);
+ 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);
+ }
+ }
+ else {
+ val = *(ep - idx);
+ RB_DEBUG_COUNTER_INC(lvar_get);
+ (void)RB_DEBUG_COUNTER_INC_IF(lvar_get_dynamic, level > 0);
+ }
+}
+
+/* Get value of special local variable ($~, $_, ..). */
+DEFINE_INSN
getspecial
(rb_num_t key, rb_num_t type)
()
(VALUE val)
{
- val = vm_getspecial(th, GET_LEP(), key, type);
+ val = vm_getspecial(ec, GET_LEP(), key, type);
}
-/**
- @c variable
- @e Set value of special local variable ($~, $_, ...) to obj.
- @j 特別ãªãƒ­ãƒ¼ã‚«ãƒ«å¤‰æ•°ï¼ˆ$~, $_, ...)ã®å€¤ã‚’設定ã™ã‚‹ã€‚
- */
+/* Set value of special local variable ($~, $_, ...) to obj. */
DEFINE_INSN
setspecial
(rb_num_t key)
(VALUE obj)
()
{
- lep_svar_set(th, GET_LEP(), key, obj);
+ lep_svar_set(ec, GET_LEP(), key, obj);
}
-/**
- @c variable
- @e Get value of instance variable id of self.
- If is_local is not 0, get value of class local variable.
- @j self ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹å¤‰æ•° id ã®å€¤ã‚’得る。
- */
+/* Get value of instance variable id of self. */
DEFINE_INSN
getinstancevariable
(ID id, IC ic)
()
(VALUE val)
+/* "instance variable not initialized" warning can be hooked. */
+// attr bool leaf = false; /* has rb_warning() */
{
val = vm_getinstancevariable(GET_SELF(), id, ic);
}
-/**
- @c variable
- @e Set value of instance variable id of self to val.
- If is_local is not 0, set value of class local variable.
- @j self ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹å¤‰æ•° id ã‚’ val ã«ã™ã‚‹ã€‚
- */
+/* Set value of instance variable id of self to val. */
DEFINE_INSN
setinstancevariable
(ID id, IC ic)
@@ -148,118 +224,91 @@ setinstancevariable
vm_setinstancevariable(GET_SELF(), id, val, ic);
}
-/**
- @c variable
- @e Get value of class variable id of klass as val.
- @j ç¾åœ¨ã®ã‚¹ã‚³ãƒ¼ãƒ—ã®ã‚¯ãƒ©ã‚¹å¤‰æ•° id ã®å€¤ã‚’得る。
- */
+/* Get value of class variable id of klass as val. */
DEFINE_INSN
getclassvariable
(ID id)
()
(VALUE val)
+/* "class variable access from toplevel" warning can be hooked. */
+// attr bool leaf = false; /* has rb_warning() */
{
val = rb_cvar_get(vm_get_cvar_base(rb_vm_get_cref(GET_EP()), GET_CFP()), id);
}
-/**
- @c variable
- @e Set value of class variable id of klass as val.
- @j klass ã®ã‚¯ãƒ©ã‚¹å¤‰æ•° id ã‚’ val ã«ã™ã‚‹ã€‚
- */
+/* Set value of class variable id of klass as val. */
DEFINE_INSN
setclassvariable
(ID id)
(VALUE val)
()
+/* "class variable access from toplevel" warning can be hooked. */
+// attr bool leaf = false; /* has rb_warning() */
{
vm_ensure_not_refinement_module(GET_SELF());
rb_cvar_set(vm_get_cvar_base(rb_vm_get_cref(GET_EP()), GET_CFP()), id, val);
}
-/**
- @c variable
- @e
- Get constant variable id. If klass is Qnil, constants
- are searched in the current scope. If klass is Qfalse, constants
- are searched as top level constants. Otherwise, get constant under klass
+/* Get constant variable id. If klass is Qnil, constants
+ are searched in the current scope. Otherwise, get constant under klass
class or module.
- @j 定数 id ã®å€¤ã‚’得る。
- klass ㌠Qnil ãªã‚‰ã€ãã®ã‚¹ã‚³ãƒ¼ãƒ—ã§å¾—られる定数ã®å€¤ã‚’得る。
- Qfalse ãªã‚‰ã€ãƒˆãƒƒãƒ—レベルスコープを得る。
- ãれ以外ãªã‚‰ã€klass クラスã®ä¸‹ã®å®šæ•°ã‚’得る。
*/
DEFINE_INSN
getconstant
(ID id)
(VALUE klass)
(VALUE val)
+/* getconstant can kick autoload */
+// attr bool leaf = false; /* has rb_autoload_load() */
{
- val = vm_get_ev_const(th, klass, id, 0);
+ val = vm_get_ev_const(ec, klass, id, 0);
}
-/**
- @c variable
- @e
- Set constant variable id. If klass is Qfalse, constant
- is able to access in this scope. if klass is Qnil, set
- top level constant. otherwise, set constant under klass
- class or module.
-
- @j 定数 id ã®å€¤ã‚’ val ã«ã™ã‚‹ã€‚
- klass ㌠Qfalse ãªã‚‰ã€ãã®ã‚¹ã‚³ãƒ¼ãƒ—ã§å¾—られる定数 id ã®å€¤ã‚’設定ã™ã‚‹ã€‚
- Qnil ãªã‚‰ã€ãƒˆãƒƒãƒ—レベルスコープã®å€¤ã‚’設定ã™ã‚‹ã€‚
- ãれ以外ãªã‚‰ã€klass クラスã®ä¸‹ã®å®šæ•°ã‚’設定ã™ã‚‹ã€‚
+/* Set constant variable id under cbase class or module.
*/
DEFINE_INSN
setconstant
(ID id)
(VALUE val, VALUE cbase)
()
+/* Assigning an object to a constant is basically a leaf operation.
+ * The problem is, assigning a Module instance to a constant _names_
+ * that module. Naming involves string manipulations, which are
+ * method calls. */
+// attr bool leaf = false; /* has StringValue() */
{
vm_check_if_namespace(cbase);
vm_ensure_not_refinement_module(GET_SELF());
rb_const_set(cbase, id, val);
}
-/**
- @c variable
- @e get global variable id.
- @j グローãƒãƒ«å¤‰æ•° id ã®å€¤ã‚’得る。
- */
+/* get global variable id. */
DEFINE_INSN
getglobal
(GENTRY entry)
()
(VALUE val)
+// attr bool leaf = leafness_of_getglobal(entry);
{
val = GET_GLOBAL((VALUE)entry);
}
-/**
- @c variable
- @e set global variable id as val.
- @j グローãƒãƒ«å¤‰æ•° id ã®å€¤ã‚’設定ã™ã‚‹ã€‚
- */
+/* set global variable id as val. */
DEFINE_INSN
setglobal
(GENTRY entry)
(VALUE val)
()
+// attr bool leaf = leafness_of_setglobal(entry);
{
SET_GLOBAL((VALUE)entry, val);
}
-
/**********************************************************/
/* deal with values */
/**********************************************************/
-/**
- @c put
- @e put nil to stack.
- @j スタック㫠nil をプッシュã™ã‚‹ã€‚
- */
+/* put nil to stack. */
DEFINE_INSN
putnil
()
@@ -269,11 +318,7 @@ putnil
val = Qnil;
}
-/**
- @c put
- @e put self.
- @j スタック㫠self をプッシュã™ã‚‹ã€‚
- */
+/* put self. */
DEFINE_INSN
putself
()
@@ -283,11 +328,7 @@ putself
val = GET_SELF();
}
-/**
- @c put
- @e put some object.
- i.e. Fixnum, true, false, nil, and so on.
- @j オブジェクト val をスタックã«ãƒ—ッシュã™ã‚‹ã€‚
+/* put some object.
i.e. Fixnum, true, false, nil, and so on.
*/
DEFINE_INSN
@@ -299,54 +340,31 @@ putobject
/* */
}
-/**
- @c put
- @e put special object. "value_type" is for expansion.
- @j 特別ãªã‚ªãƒ–ジェクト val をスタックã«ãƒ—ッシュã™ã‚‹ã€‚
- オブジェクトã®ç¨®é¡žã¯ value_type ã«ã‚ˆã‚‹ï¼Ž
- */
+/* put special object. "value_type" is for expansion. */
DEFINE_INSN
putspecialobject
(rb_num_t value_type)
()
(VALUE val)
{
- enum vm_special_object_type type = (enum vm_special_object_type)value_type;
-
- switch (type) {
- case VM_SPECIAL_OBJECT_VMCORE:
- val = rb_mRubyVMFrozenCore;
- break;
- case VM_SPECIAL_OBJECT_CBASE:
- val = vm_get_cbase(GET_EP());
- break;
- case VM_SPECIAL_OBJECT_CONST_BASE:
- val = vm_get_const_base(GET_EP());
- break;
- default:
- rb_bug("putspecialobject insn: unknown value_type");
- }
+ enum vm_special_object_type type;
+
+ type = (enum vm_special_object_type)value_type;
+ val = vm_get_special_object(GET_EP(), type);
}
-/**
- @c put
- @e put iseq value.
- @j iseq をスタックã«ãƒ—ッシュã™ã‚‹ã€‚
- */
+/* put iseq value. */
DEFINE_INSN
putiseq
(ISEQ iseq)
()
(VALUE ret)
+// attr bool handles_sp = false; /* of course it doesn't */
{
ret = (VALUE)iseq;
}
-/**
- @c put
- @e put string val. string will be copied.
- @j 文字列をコピーã—ã¦ã‚¹ã‚¿ãƒƒã‚¯ã«ãƒ—ッシュã™ã‚‹ã€‚
- */
+/* put string val. string will be copied. */
DEFINE_INSN
putstring
(VALUE str)
@@ -356,108 +374,99 @@ putstring
val = rb_str_resurrect(str);
}
-/**
- @c put
- @e put concatenate strings
- @j ã‚¹ã‚¿ãƒƒã‚¯ãƒˆãƒƒãƒ—ã®æ–‡å­—列を n 個連çµã—ï¼Œçµæžœã‚’スタックã«ãƒ—ッシュã™ã‚‹ã€‚
- */
+/* put concatenate strings */
DEFINE_INSN
concatstrings
(rb_num_t num)
(...)
-(VALUE val) // inc += 1 - num;
+(VALUE val)
+// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num;
{
- val = rb_str_concat_literals(num, &TOPN(num-1));
- POPN(num);
+ val = rb_str_concat_literals(num, STACK_ADDR_FROM_TOP(num));
}
-/**
- @c put
- @e push the result of to_str.
- @j to_str ã®çµæžœã‚’スタックã«ãƒ—ッシュã™ã‚‹ã€‚
- */
+/* push the result of to_s. */
DEFINE_INSN
tostring
()
-(VALUE val)
+(VALUE val, VALUE str)
(VALUE val)
{
- val = rb_obj_as_string(val);
+ val = rb_obj_as_string_result(str, val);
}
-/**
- @c put
- @e Freeze (dynamically) created strings. if debug_info is given, set it.
- @j (埋ã‚è¾¼ã¿ï¼‰æ–‡å­—列を freeze ã™ã‚‹ã€‚ã‚‚ã—ã€debug_info ãŒä¸Žãˆã‚‰ã‚Œã¦ã„れã°ã€ãれを設定ã™ã‚‹ã€‚
- */
+/* Freeze (dynamically) created strings. if debug_info is given, set it. */
DEFINE_INSN
freezestring
(VALUE debug_info)
(VALUE str)
(VALUE str)
{
- if (!NIL_P(debug_info)) {
- rb_ivar_set(str, id_debug_created_info, debug_info);
- }
- rb_str_freeze(str);
+ vm_freezestring(str, debug_info);
}
-/**
- @c put
- @e compile str to Regexp and push it.
+/* compile str to Regexp and push it.
opt is the option for the Regexp.
- @j 文字列 str ã‚’æ­£è¦è¡¨ç¾ã«ã‚³ãƒ³ãƒ‘イルã—ã¦ã‚¹ã‚¿ãƒƒã‚¯ã«ãƒ—ッシュã™ã‚‹ã€‚
- コンパイル時,opt ã‚’æ­£è¦è¡¨ç¾ã®ã‚ªãƒ—ションã¨ã™ã‚‹ã€‚
*/
DEFINE_INSN
toregexp
(rb_num_t opt, rb_num_t cnt)
(...)
-(VALUE val) // inc += 1 - cnt;
+(VALUE val)
+/* This instruction has StringValue(), which is a method call. But it
+ * seems that path is never covered. */
+// attr bool leaf = true; /* yes it is */
+// attr rb_snum_t sp_inc = 1 - (rb_snum_t)cnt;
{
- VALUE rb_reg_new_ary(VALUE ary, int options);
- rb_num_t i;
- const VALUE ary = rb_ary_tmp_new(cnt);
- for (i = 0; i < cnt; i++) {
- rb_ary_store(ary, cnt-i-1, TOPN(i));
- }
- POPN(cnt);
+ const VALUE ary = rb_ary_tmp_new_from_values(0, cnt, STACK_ADDR_FROM_TOP(cnt));
val = rb_reg_new_ary(ary, (int)opt);
rb_ary_clear(ary);
}
-/**
- @c put
- @e put new array initialized with num values on the stack.
- @j æ–°ã—ã„é…列をスタック上㮠num 個ã®å€¤ã§åˆæœŸåŒ–ã—ã¦ç”Ÿæˆã—プッシュã™ã‚‹ã€‚
- */
+/* intern str to Symbol and push it. */
+DEFINE_INSN
+intern
+()
+(VALUE str)
+(VALUE sym)
+{
+ sym = rb_str_intern(str);
+}
+
+/* put new array initialized with num values on the stack. */
DEFINE_INSN
newarray
(rb_num_t num)
(...)
-(VALUE val) // inc += 1 - num;
+(VALUE val)
+// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num;
{
- val = rb_ary_new4((long)num, STACK_ADDR_FROM_TOP(num));
- POPN(num);
+ val = rb_ary_new4(num, STACK_ADDR_FROM_TOP(num));
}
-/**
- @c put
- @e dup array
- @j é…列 ary ã‚’ dup ã—ã¦ã‚¹ã‚¿ãƒƒã‚¯ã«ãƒ—ッシュã™ã‚‹ã€‚
- */
+/* dup array */
DEFINE_INSN
duparray
(VALUE ary)
()
(VALUE val)
{
+ RUBY_DTRACE_CREATE_HOOK(ARRAY, RARRAY_LEN(ary));
val = rb_ary_resurrect(ary);
}
-/**
- @c put
- @e if TOS is an array expand, expand it to num objects.
+/* dup hash */
+DEFINE_INSN
+duphash
+(VALUE hash)
+()
+(VALUE val)
+{
+ RUBY_DTRACE_CREATE_HOOK(HASH, RHASH_SIZE(hash) << 1);
+ val = rb_hash_resurrect(hash);
+}
+
+/* if TOS is an array expand, expand it to num objects.
if the number of the array is less than num, push nils to fill.
if it is greater than num, exceeding elements are dropped.
unless TOS is an array, push num - 1 nils.
@@ -465,110 +474,66 @@ duparray
flag: 0x01 - rest args array
flag: 0x02 - for postarg
flag: 0x04 - reverse?
- @j スタックトップã®ã‚ªãƒ–ジェクトãŒé…列ã§ã‚れã°ã€ãれを展開ã™ã‚‹ã€‚
- é…列オブジェクトã®è¦ç´ æ•°ãŒ num以下ãªã‚‰ã°ã€ä»£ã‚り㫠nil ã‚’ç©ã‚€ã€‚num以上ãªã‚‰ã€
- num以上ã®è¦ç´ ã¯åˆ‡ã‚Šæ¨ã¦ã‚‹ã€‚
- é…列オブジェクトã§ãªã‘れã°ã€num - 1 個㮠nil ã‚’ç©ã‚€ã€‚
- ã‚‚ã— flag ãŒçœŸãªã‚‰ã€æ®‹ã‚Šè¦ç´ ã®é…列をç©ã‚€
- flag: 0x01 - 最後をé…列ã«
- flag: 0x02 - postarg 用
- flag: 0x04 - reverse?
*/
DEFINE_INSN
expandarray
(rb_num_t num, rb_num_t flag)
(..., VALUE ary)
-(...) // inc += num - 1 + (flag & 1 ? 1 : 0);
+(...)
+// attr bool leaf = false; /* has rb_check_array_type() */
+// attr rb_snum_t sp_inc = (rb_snum_t)num - 1 + (flag & 1 ? 1 : 0);
{
- vm_expandarray(GET_CFP(), ary, num, (int)flag);
+ vm_expandarray(GET_SP(), ary, num, (int)flag);
}
-/**
- @c put
- @e concat two arrays
- @j 二ã¤ã®é…列 ary1, ary2 を連çµã—スタックã¸ãƒ—ッシュã™ã‚‹ã€‚
- */
+/* concat two arrays */
DEFINE_INSN
concatarray
()
-(VALUE ary1, VALUE ary2st)
+(VALUE ary1, VALUE ary2)
(VALUE ary)
+// attr bool leaf = false; /* has rb_check_array_type() */
{
- const VALUE ary2 = ary2st;
- VALUE tmp1 = rb_check_convert_type(ary1, T_ARRAY, "Array", "to_a");
- VALUE tmp2 = rb_check_convert_type(ary2, T_ARRAY, "Array", "to_a");
-
- if (NIL_P(tmp1)) {
- tmp1 = rb_ary_new3(1, ary1);
- }
-
- if (NIL_P(tmp2)) {
- tmp2 = rb_ary_new3(1, ary2);
- }
-
- if (tmp1 == ary1) {
- tmp1 = rb_ary_dup(ary1);
- }
- ary = rb_ary_concat(tmp1, tmp2);
+ ary = vm_concat_array(ary1, ary2);
}
-/**
- @c put
- @e call to_a on array ary to splat
- @j splat ã®ãŸã‚ã«é…列 ary ã«å¯¾ã—㦠to_a を呼ã³å‡ºã™ã€‚
- */
+/* call to_a on array ary to splat */
DEFINE_INSN
splatarray
(VALUE flag)
(VALUE ary)
(VALUE obj)
+// attr bool leaf = false; /* has rb_check_array_type() */
{
- VALUE tmp = rb_check_convert_type(ary, T_ARRAY, "Array", "to_a");
- if (NIL_P(tmp)) {
- tmp = rb_ary_new3(1, ary);
- }
- else if (RTEST(flag)) {
- tmp = rb_ary_dup(tmp);
- }
- obj = tmp;
+ obj = vm_splat_array(flag, ary);
}
-/**
- @c put
- @e put new Hash from n elements. n must be an even number.
- @j æ–°ã—ã„ãƒãƒƒã‚·ãƒ¥ã‚’スタックトップ㮠n å€‹ã‚’åˆæœŸå€¤ã¨ã—ã¦ç”Ÿæˆã™ã‚‹ã€‚
- n ã¯ã‚­ãƒ¼ã¨å€¤ã®ãƒšã‚¢ãªã®ã§ 2 ã®å€æ•°ã§ãªã‘れã°ãªã‚‰ãªã„。
- */
+/* put new Hash from n elements. n must be an even number. */
DEFINE_INSN
newhash
(rb_num_t num)
(...)
-(VALUE val) // inc += 1 - num;
+(VALUE val)
+// attr bool leaf = false; /* has rb_hash_key_str() */
+// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num;
{
- rb_num_t i;
-
RUBY_DTRACE_CREATE_HOOK(HASH, num);
- val = rb_hash_new();
+ val = rb_hash_new_with_size(num / 2);
- for (i = num; i > 0; i -= 2) {
- const VALUE v = TOPN(i - 2);
- const VALUE k = TOPN(i - 1);
- rb_hash_aset(val, k, v);
+ if (num) {
+ rb_hash_bulk_insert(num, STACK_ADDR_FROM_TOP(num), val);
}
- POPN(num);
}
-/**
- @c put
- @e put new Range object.(Range.new(low, high, flag))
- @j Range.new(low, high, flag) ã®ã‚ˆã†ãªã‚ªãƒ–ジェクトを生æˆã—スタックã«ãƒ—ッシュã™ã‚‹ã€‚
- */
+/* put new Range object.(Range.new(low, high, flag)) */
DEFINE_INSN
newrange
(rb_num_t flag)
(VALUE low, VALUE high)
(VALUE val)
+/* rb_range_new() exercises "bad value for range" check. */
+// attr bool leaf = false; /* see also: range.c:range_init() */
{
val = rb_range_new(low, high, (int)flag);
}
@@ -577,11 +542,7 @@ newrange
/* deal with stack operation */
/**********************************************************/
-/**
- @c stack
- @e pop from stack.
- @j スタックã‹ã‚‰ä¸€ã¤ãƒãƒƒãƒ—ã™ã‚‹ã€‚
- */
+/* pop from stack. */
DEFINE_INSN
pop
()
@@ -592,11 +553,7 @@ pop
/* none */
}
-/**
- @c stack
- @e duplicate stack top.
- @j スタックトップをコピーã—ã¦ã‚¹ã‚¿ãƒƒã‚¯ã«ãƒ—ッシュã™ã‚‹ã€‚
- */
+/* duplicate stack top. */
DEFINE_INSN
dup
()
@@ -606,31 +563,21 @@ dup
val1 = val2 = val;
}
-/**
- @c stack
- @e duplicate stack top n elements
- @j スタックトップ㮠n 個をコピーã—ã¦ã‚¹ã‚¿ãƒƒã‚¯ã«ãƒ—ッシュã™ã‚‹ã€‚
- */
+/* duplicate stack top n elements */
DEFINE_INSN
dupn
(rb_num_t n)
(...)
-(...) // inc += n;
+(...)
+// attr rb_snum_t sp_inc = n;
{
- rb_num_t i;
- VALUE *sp = STACK_ADDR_FROM_TOP(n);
- for (i = 0; i < n; i++) {
- GET_SP()[i] = sp[i];
- }
- INC_SP(n);
-}
+ void *dst = GET_SP();
+ void *src = STACK_ADDR_FROM_TOP(n);
+ MEMCPY(dst, src, VALUE, n);
+}
-/**
- @c stack
- @e swap top 2 vals
- @j スタックトップ㮠2 ã¤ã®å€¤ã‚’交æ›ã™ã‚‹ã€‚
- */
+/* swap top 2 vals */
DEFINE_INSN
swap
()
@@ -640,16 +587,13 @@ swap
/* none */
}
-/**
- @c stack
- @e reverse stack top N order.
- @j スタックトップ㮠n 個ã®å€¤ã‚’逆転ã™ã‚‹ã€‚
- */
+/* reverse stack top N order. */
DEFINE_INSN
reverse
(rb_num_t n)
(...)
-(...) // inc += 0;
+(...)
+// attr rb_snum_t sp_inc = 0;
{
rb_num_t i;
VALUE *sp = STACK_ADDR_FROM_TOP(n);
@@ -662,191 +606,108 @@ reverse
}
}
-/**
- @c stack
- @e for stack caching.
- @j スタックキャッシングã®çŠ¶æ…‹ã‚’èª¿æ•´ã™ã‚‹ãŸã‚ã«å¿…è¦ãªå‘½ä»¤ã€‚
- */
+/* for stack caching. */
DEFINE_INSN
reput
()
(..., VALUE val)
-(VALUE val) // inc += 0;
+(VALUE val)
+// attr rb_snum_t sp_inc = 0;
{
/* none */
}
-/**
- @c stack
- @e get nth stack value from stack top
- @j スタックトップã‹ã‚‰ n 個目をスタックã«ãƒ—ッシュã™ã‚‹ã€‚
- */
+/* get nth stack value from stack top */
DEFINE_INSN
topn
(rb_num_t n)
(...)
-(VALUE val) // inc += 1;
+(VALUE val)
+// attr rb_snum_t sp_inc = 1;
{
val = TOPN(n);
}
-/**
- @c stack
- @e set Nth stack entry to stack top
- @j スタックトップã®å€¤ã‚’ n 個目ã®ã‚¹ã‚¿ãƒƒã‚¯ã«ã‚³ãƒ”ー
- */
+/* set Nth stack entry to stack top */
DEFINE_INSN
setn
(rb_num_t n)
(..., VALUE val)
-(VALUE val) // inc += 0
+(VALUE val)
+// attr rb_snum_t sp_inc = 0;
{
- TOPN(n-1) = val;
+ TOPN(n) = val;
}
-/**
- @c stack
- @e empty current stack
- @j current stack を空ã«ã™ã‚‹ã€‚
- */
+/* empty current stack */
DEFINE_INSN
adjuststack
(rb_num_t n)
(...)
-(...) // inc -= n
+(...)
+// attr rb_snum_t sp_inc = -(rb_snum_t)n;
{
- DEC_SP(n);
+ /* none */
}
-
/**********************************************************/
/* deal with setting */
/**********************************************************/
-/**
- @c setting
- @e defined?
- @j defined? を行ã†ã€‚
- */
+/* defined? */
DEFINE_INSN
defined
(rb_num_t op_type, VALUE obj, VALUE needstr)
(VALUE v)
(VALUE val)
+// attr bool leaf = leafness_of_defined(op_type);
{
- val = vm_defined(th, GET_CFP(), op_type, obj, needstr, v);
+ val = vm_defined(ec, GET_CFP(), op_type, obj, needstr, v);
}
-/**
- @c setting
- @e check `target' matches `pattern'.
+/* check `target' matches `pattern'.
`flag & VM_CHECKMATCH_TYPE_MASK' describe how to check pattern.
VM_CHECKMATCH_TYPE_WHEN: ignore target and check pattern is truthy.
VM_CHECKMATCH_TYPE_CASE: check `patten === target'.
- VM_CHECKMATCH_TYPE_RESCUE: check `pattern.kind_op?(Module) && pattern == target'.
+ VM_CHECKMATCH_TYPE_RESCUE: check `pattern.kind_op?(Module) && pattern === target'.
if `flag & VM_CHECKMATCH_ARRAY' is not 0, then `patten' is array of patterns.
- @j see above comments.
*/
DEFINE_INSN
checkmatch
(rb_num_t flag)
(VALUE target, VALUE pattern)
(VALUE result)
+// attr bool leaf = leafness_of_checkmatch(flag);
{
- enum vm_check_match_type checkmatch_type =
- (enum vm_check_match_type)(flag & VM_CHECKMATCH_TYPE_MASK);
- result = Qfalse;
-
- if (flag & VM_CHECKMATCH_ARRAY) {
- int i;
- for (i = 0; i < RARRAY_LEN(pattern); i++) {
- if (RTEST(check_match(RARRAY_AREF(pattern, i), target, checkmatch_type))) {
- result = Qtrue;
- break;
- }
- }
- }
- else {
- if (RTEST(check_match(pattern, target, checkmatch_type))) {
- result = Qtrue;
- }
- }
+ result = vm_check_match(ec, target, pattern, flag);
}
-/**
- @c setting
- @e check keywords are specified or not.
- @j ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ãŒæŒ‡å®šã•れã¦ã„ã‚‹ã‹ã©ã†ã‹ãƒã‚§ãƒƒã‚¯ã™ã‚‹
- */
+/* check keywords are specified or not. */
DEFINE_INSN
checkkeyword
-(lindex_t kw_bits_index, rb_num_t keyword_index)
+(lindex_t kw_bits_index, lindex_t keyword_index)
()
(VALUE ret)
{
- const VALUE *ep = GET_EP();
- const VALUE kw_bits = *(ep - kw_bits_index);
-
- if (FIXNUM_P(kw_bits)) {
- int bits = FIX2INT(kw_bits);
- ret = (bits & (0x01 << keyword_index)) ? Qfalse : Qtrue;
- }
- else {
- VM_ASSERT(RB_TYPE_P(kw_bits, T_HASH));
- ret = rb_hash_has_key(kw_bits, INT2FIX(keyword_index)) ? Qfalse : Qtrue;
- }
+ ret = vm_check_keyword(kw_bits_index, keyword_index, GET_EP());
}
-/**
- @c setting
- @e trace
- @j trace 用ã®å‘½ä»¤ã€‚
- */
+/* check if val is type. */
DEFINE_INSN
-trace
-(rb_num_t nf)
-()
-()
+checktype
+(rb_num_t type)
+(VALUE val)
+(VALUE ret)
{
- rb_event_flag_t flag = (rb_event_flag_t)nf;
-
- if (RUBY_DTRACE_METHOD_ENTRY_ENABLED() ||
- RUBY_DTRACE_METHOD_RETURN_ENABLED() ||
- RUBY_DTRACE_CMETHOD_ENTRY_ENABLED() ||
- RUBY_DTRACE_CMETHOD_RETURN_ENABLED()) {
-
- switch (flag) {
- case RUBY_EVENT_CALL:
- RUBY_DTRACE_METHOD_ENTRY_HOOK(th, 0, 0);
- break;
- case RUBY_EVENT_C_CALL:
- RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, 0, 0);
- break;
- case RUBY_EVENT_RETURN:
- RUBY_DTRACE_METHOD_RETURN_HOOK(th, 0, 0);
- break;
- case RUBY_EVENT_C_RETURN:
- RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, 0, 0);
- break;
- }
- }
-
- EXEC_EVENT_HOOK(th, flag, GET_SELF(), 0, 0, 0 /* id and klass are resolved at callee */,
- (flag & (RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN)) ? TOPN(0) : Qundef);
+ ret = (TYPE(val) == (int)type) ? Qtrue : Qfalse;
}
/**********************************************************/
/* deal with control flow 1: class/module */
/**********************************************************/
-/**
- @c class/module
- @e
- enter class definition scope. if super is Qfalse, and class
+/* enter class definition scope. if super is Qfalse, and class
"klass" is defined, it's redefine. otherwise, define "klass" class.
- @j クラス定義スコープã¸ç§»è¡Œã™ã‚‹ã€‚
- ã‚‚ã— super ㌠Qfalse ã§ klassクラスãŒå®šç¾©ã•れã¦ã„れã°å†å®šç¾©ã§ã‚る。
- ãã†ã§ãªã‘れã°ã€klass クラスを定義ã™ã‚‹ã€‚
*/
DEFINE_INSN
defineclass
@@ -854,89 +715,14 @@ defineclass
(VALUE cbase, VALUE super)
(VALUE val)
{
- VALUE klass;
- rb_vm_defineclass_type_t type = VM_DEFINECLASS_TYPE(flags);
-
- switch (type) {
- case VM_DEFINECLASS_TYPE_CLASS:
- /* val is dummy. classdef returns class scope value */
-
- if (VM_DEFINECLASS_HAS_SUPERCLASS_P(flags) &&
- !RB_TYPE_P(super, T_CLASS)) {
- rb_raise(rb_eTypeError, "superclass must be a Class (%"PRIsVALUE" given)",
- rb_obj_class(super));
- }
-
- vm_check_if_namespace(cbase);
-
- /* find klass */
- rb_autoload_load(cbase, id);
- if ((klass = vm_search_const_defined_class(cbase, id)) != 0) {
- /* already exist */
- klass = VM_DEFINECLASS_SCOPED_P(flags) ?
- rb_public_const_get_at(klass, id) : rb_const_get_at(klass, id);
- if (!RB_TYPE_P(klass, T_CLASS)) {
- rb_raise(rb_eTypeError, "%"PRIsVALUE" is not a class", rb_id2str(id));
- }
-
- if (VM_DEFINECLASS_HAS_SUPERCLASS_P(flags)) {
- VALUE tmp;
- tmp = rb_class_real(RCLASS_SUPER(klass));
-
- if (tmp != super) {
- rb_raise(rb_eTypeError, "superclass mismatch for class %"PRIsVALUE"",
- rb_id2str(id));
- }
- }
- }
- else {
- if (!VM_DEFINECLASS_HAS_SUPERCLASS_P(flags)) {
- super = rb_cObject;
- }
- /* new class declaration */
- klass = rb_define_class_id(id, super);
- rb_set_class_path_string(klass, cbase, rb_id2str(id));
- rb_const_set(cbase, id, klass);
- rb_class_inherited(super, klass);
- }
- break;
- case VM_DEFINECLASS_TYPE_SINGLETON_CLASS:
- /* val is dummy. classdef returns class scope value */
- /* super is dummy */
- klass = rb_singleton_class(cbase);
- break;
- case VM_DEFINECLASS_TYPE_MODULE:
- /* val is dummy. classdef returns class scope value */
- /* super is dummy */
-
- vm_check_if_namespace(cbase);
-
- /* find klass */
- if ((klass = vm_search_const_defined_class(cbase, id)) != 0) {
- klass = VM_DEFINECLASS_SCOPED_P(flags) ?
- rb_public_const_get_at(klass, id) : rb_const_get_at(klass, id);
- /* already exist */
- if (!RB_TYPE_P(klass, T_MODULE)) {
- rb_raise(rb_eTypeError, "%"PRIsVALUE" is not a module", rb_id2str(id));
- }
- }
- else {
- /* new module declaration */
- klass = rb_define_module_id(id);
- rb_set_class_path_string(klass, cbase, rb_id2str(id));
- rb_const_set(cbase, id, klass);
- }
- break;
- default:
- rb_bug("unknown defineclass type: %d", (int)type);
- }
+ VALUE klass = vm_find_or_create_class_by_id(id, flags, cbase, super);
rb_iseq_check(class_iseq);
/* enter scope */
- vm_push_frame(th, class_iseq, VM_FRAME_MAGIC_CLASS | VM_ENV_FLAG_LOCAL, klass,
+ vm_push_frame(ec, class_iseq, VM_FRAME_MAGIC_CLASS | VM_ENV_FLAG_LOCAL, klass,
GET_BLOCK_HANDLER(),
- (VALUE)vm_cref_push(th, klass, NULL, FALSE),
+ (VALUE)vm_cref_push(ec, klass, NULL, FALSE),
class_iseq->body->iseq_encoded, GET_SP(),
class_iseq->body->local_table_size,
class_iseq->body->stack_max);
@@ -944,195 +730,163 @@ defineclass
NEXT_INSN();
}
-
/**********************************************************/
/* deal with control flow 2: method/iterator */
/**********************************************************/
-/**
- @c method/iterator
- @e invoke method.
- @j メソッド呼ã³å‡ºã—を行ã†ã€‚ci ã«å¿…è¦ãªæƒ…å ±ãŒæ ¼ç´ã•れã¦ã„る。
- */
+/* invoke method. */
DEFINE_INSN
send
(CALL_INFO ci, CALL_CACHE cc, ISEQ blockiseq)
(...)
-(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0));
+(VALUE val)
+// attr rb_snum_t sp_inc = - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0));
{
struct rb_calling_info calling;
- vm_caller_setup_arg_block(th, reg_cfp, &calling, ci, blockiseq, FALSE);
+ calling.block_handler = vm_caller_setup_arg_block(ec, reg_cfp, ci, blockiseq, FALSE);
+ calling.recv = TOPN(calling.argc = ci->orig_argc);
+ vm_search_method(ci, cc, calling.recv);
+ CALL_METHOD(&calling, ci, cc);
+}
+
+/* Invoke method without block */
+DEFINE_INSN
+opt_send_without_block
+(CALL_INFO ci, CALL_CACHE cc)
+(...)
+(VALUE val)
+// attr bool handles_sp = true;
+// attr rb_snum_t sp_inc = -ci->orig_argc;
+{
+ struct rb_calling_info calling;
+ calling.block_handler = VM_BLOCK_HANDLER_NONE;
vm_search_method(ci, cc, calling.recv = TOPN(calling.argc = ci->orig_argc));
CALL_METHOD(&calling, ci, cc);
}
DEFINE_INSN
opt_str_freeze
-(VALUE str)
+(VALUE str, CALL_INFO ci, CALL_CACHE cc)
()
(VALUE val)
{
- if (BASIC_OP_UNREDEFINED_P(BOP_FREEZE, STRING_REDEFINED_OP_FLAG)) {
- val = str;
- }
- else {
- val = rb_funcall(rb_str_resurrect(str), idFreeze, 0);
+ val = vm_opt_str_freeze(str, BOP_FREEZE, idFreeze);
+
+ if (val == Qundef) {
+ PUSH(rb_str_resurrect(str));
+ CALL_SIMPLE_METHOD();
}
}
DEFINE_INSN
-opt_newarray_max
-(rb_num_t num)
-(...)
-(VALUE val) // inc += 1 - num;
+opt_str_uminus
+(VALUE str, CALL_INFO ci, CALL_CACHE cc)
+()
+(VALUE val)
{
-#define id_cmp idCmp
- if (BASIC_OP_UNREDEFINED_P(BOP_MAX, ARRAY_REDEFINED_OP_FLAG)) {
- if (num == 0) {
- val = Qnil;
- }
- else {
- struct cmp_opt_data cmp_opt = { 0, 0 };
- VALUE result = Qundef;
- rb_num_t i = num - 1;
- result = TOPN(i);
- while (i-- > 0) {
- const VALUE v = TOPN(i);
- if (result == Qundef || OPTIMIZED_CMP(v, result, cmp_opt) > 0) {
- result = v;
- }
- }
- val = result == Qundef ? Qnil : result;
- }
- POPN(num);
- }
- else {
- VALUE ary = rb_ary_new4((long)num, STACK_ADDR_FROM_TOP(num));
- val = rb_funcall(ary, idMax, 0);
- POPN(num);
+ val = vm_opt_str_freeze(str, BOP_UMINUS, idUMinus);
+
+ if (val == Qundef) {
+ PUSH(rb_str_resurrect(str));
+ CALL_SIMPLE_METHOD();
}
-#undef id_cmp
}
DEFINE_INSN
-opt_newarray_min
+opt_newarray_max
(rb_num_t num)
(...)
-(VALUE val) // inc += 1 - num;
+(VALUE val)
+/* This instruction typically has no funcalls. But it compares array
+ * contents each other by nature. That part could call methods when
+ * necessary. No way to detect such method calls beforehand. We
+ * cannot but mark it being not leaf. */
+// attr bool leaf = false; /* has rb_funcall() */
+// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num;
{
-#define id_cmp idCmp
- if (BASIC_OP_UNREDEFINED_P(BOP_MIN, ARRAY_REDEFINED_OP_FLAG)) {
- if (num == 0) {
- val = Qnil;
- }
- else {
- struct cmp_opt_data cmp_opt = { 0, 0 };
- VALUE result = Qundef;
- rb_num_t i = num - 1;
- result = TOPN(i);
- while (i-- > 0) {
- const VALUE v = TOPN(i);
- if (result == Qundef || OPTIMIZED_CMP(v, result, cmp_opt) < 0) {
- result = v;
- }
- }
- val = result == Qundef ? Qnil : result;
- }
- POPN(num);
- }
- else {
- VALUE ary = rb_ary_new4((long)num, STACK_ADDR_FROM_TOP(num));
- val = rb_funcall(ary, idMin, 0);
- POPN(num);
- }
-#undef id_cmp
+ val = vm_opt_newarray_max(num, STACK_ADDR_FROM_TOP(num));
}
-/**
- @c optimize
- @e Invoke method without block
- @j Invoke method without block
- */
DEFINE_INSN
-opt_send_without_block
-(CALL_INFO ci, CALL_CACHE cc)
+opt_newarray_min
+(rb_num_t num)
(...)
-(VALUE val) // inc += -ci->orig_argc;
+(VALUE val)
+/* Same discussion as opt_newarray_max. */
+// attr bool leaf = false; /* has rb_funcall() */
+// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num;
{
- struct rb_calling_info calling;
- calling.block_handler = VM_BLOCK_HANDLER_NONE;
- vm_search_method(ci, cc, calling.recv = TOPN(calling.argc = ci->orig_argc));
- CALL_METHOD(&calling, ci, cc);
+ val = vm_opt_newarray_min(num, STACK_ADDR_FROM_TOP(num));
}
-/**
- @c method/iterator
- @e super(args) # args.size => num
- @j super を実行ã™ã‚‹ã€‚ci ã«å¿…è¦ãªæƒ…å ±ãŒæ ¼ç´ã•れã¦ã„る。
- */
+/* super(args) # args.size => num */
DEFINE_INSN
invokesuper
(CALL_INFO ci, CALL_CACHE cc, ISEQ blockiseq)
(...)
-(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0));
+(VALUE val)
+// attr rb_snum_t sp_inc = - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0));
{
struct rb_calling_info calling;
- calling.argc = ci->orig_argc;
- vm_caller_setup_arg_block(th, reg_cfp, &calling, ci, blockiseq, TRUE);
- calling.recv = GET_SELF();
- vm_search_super_method(th, GET_CFP(), &calling, ci, cc);
+ calling.block_handler = vm_caller_setup_arg_block(ec, reg_cfp, ci, blockiseq, TRUE);
+ calling.recv = TOPN(calling.argc = ci->orig_argc);
+ vm_search_super_method(ec, GET_CFP(), &calling, ci, cc);
CALL_METHOD(&calling, ci, cc);
}
-/**
- @c method/iterator
- @e yield(args)
- @j yield を実行ã™ã‚‹ã€‚
- */
+/* yield(args) */
DEFINE_INSN
invokeblock
(CALL_INFO ci)
(...)
-(VALUE val) // inc += 1 - ci->orig_argc;
+(VALUE val)
+// attr bool handles_sp = true;
+// attr rb_snum_t sp_inc = 1 - ci->orig_argc;
{
struct rb_calling_info calling;
+ VALUE block_handler;
+
calling.argc = ci->orig_argc;
calling.block_handler = VM_BLOCK_HANDLER_NONE;
- calling.recv = GET_SELF();
+ calling.recv = Qundef; /* should not be used */
+
+ block_handler = VM_CF_BLOCK_HANDLER(GET_CFP());
+ if (block_handler == VM_BLOCK_HANDLER_NONE) {
+ rb_vm_localjump_error("no block given (yield)", Qnil, 0);
+ }
- val = vm_invoke_block(th, GET_CFP(), &calling, ci);
+ val = vm_invoke_block(ec, GET_CFP(), &calling, ci, block_handler);
if (val == Qundef) {
- RESTORE_REGS();
- NEXT_INSN();
+ EXEC_EC_CFP(val);
}
}
-/**
- @c method/iterator
- @e return from this scope.
- @j ã“ã®ã‚¹ã‚³ãƒ¼ãƒ—ã‹ã‚‰æŠœã‘る。
- */
+/* return from this scope. */
DEFINE_INSN
leave
()
(VALUE val)
(VALUE val)
+/* This is super surprising but when leaving from a frame, we check
+ * for interrupts. If any, that should be executed on top of the
+ * current execution context. This is a method call. */
+// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
+// attr bool handles_sp = true;
{
if (OPT_CHECKED_RUN) {
const VALUE *const bp = vm_base_ptr(reg_cfp);
if (reg_cfp->sp != bp) {
- rb_bug("Stack consistency error (sp: %"PRIdPTRDIFF", bp: %"PRIdPTRDIFF")",
- VM_SP_CNT(th, reg_cfp->sp), VM_SP_CNT(th, bp));
+ vm_stack_consistency_error(ec, reg_cfp, bp);
}
}
- RUBY_VM_CHECK_INTS(th);
+ RUBY_VM_CHECK_INTS(ec);
- if (vm_pop_frame(th, GET_CFP(), GET_EP())) {
+ if (vm_pop_frame(ec, GET_CFP(), GET_EP())) {
#if OPT_CALL_THREADED_CODE
- th->retval = val;
+ rb_ec_thread_ptr(ec)->retval = val;
return 0;
#else
return val;
@@ -1147,19 +901,17 @@ leave
/* deal with control flow 3: exception */
/**********************************************************/
-/**
- @c exception
- @e longjump
- @j 大域ジャンプを行ã†ã€‚
- */
+/* longjump */
DEFINE_INSN
throw
(rb_num_t throw_state)
(VALUE throwobj)
(VALUE val)
+/* Same discussion as leave. */
+// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
{
- RUBY_VM_CHECK_INTS(th);
- val = vm_throw(th, GET_CFP(), throw_state, throwobj);
+ RUBY_VM_CHECK_INTS(ec);
+ val = vm_throw(ec, GET_CFP(), throw_state, throwobj);
THROW_EXCEPTION(val);
/* unreachable */
}
@@ -1168,982 +920,513 @@ throw
/* deal with control flow 4: local jump */
/**********************************************************/
-/**
- @c jump
- @e set PC to (PC + dst).
- @j PC ã‚’ (PC + dst) ã«ã™ã‚‹ã€‚
- */
+/* set PC to (PC + dst). */
DEFINE_INSN
jump
(OFFSET dst)
()
()
+/* Same discussion as leave. */
+// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
{
- RUBY_VM_CHECK_INTS(th);
+ RUBY_VM_CHECK_INTS(ec);
JUMP(dst);
}
-/**
- @c jump
- @e if val is not false or nil, set PC to (PC + dst).
- @j ã‚‚ã— val ㌠false ã‹ nil ã§ãªã‘れã°ã€PC ã‚’ (PC + dst) ã«ã™ã‚‹ã€‚
- */
+/* if val is not false or nil, set PC to (PC + dst). */
DEFINE_INSN
branchif
(OFFSET dst)
(VALUE val)
()
+/* Same discussion as jump. */
+// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
{
if (RTEST(val)) {
- RUBY_VM_CHECK_INTS(th);
+ RUBY_VM_CHECK_INTS(ec);
JUMP(dst);
}
}
-/**
- @c jump
- @e if val is false or nil, set PC to (PC + dst).
- @j ã‚‚ã— val ㌠false ã‹ nil ãªã‚‰ã°ã€PC ã‚’ (PC + dst) ã«ã™ã‚‹ã€‚
- */
+/* if val is false or nil, set PC to (PC + dst). */
DEFINE_INSN
branchunless
(OFFSET dst)
(VALUE val)
()
+/* Same discussion as jump. */
+// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
{
if (!RTEST(val)) {
- RUBY_VM_CHECK_INTS(th);
+ RUBY_VM_CHECK_INTS(ec);
JUMP(dst);
}
}
-/**
- @c jump
- @e if val is nil, set PC to (PC + dst).
- @j ã‚‚ã— val ㌠nil ãªã‚‰ã°ã€PC ã‚’ (PC + dst) ã«ã™ã‚‹ã€‚
- */
+/* if val is nil, set PC to (PC + dst). */
DEFINE_INSN
branchnil
(OFFSET dst)
(VALUE val)
()
+/* Same discussion as jump. */
+// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
{
if (NIL_P(val)) {
- RUBY_VM_CHECK_INTS(th);
+ RUBY_VM_CHECK_INTS(ec);
JUMP(dst);
}
}
-
/**********************************************************/
/* for optimize */
/**********************************************************/
-/**
- @c optimize
- @e push inline-cached value and go to dst if it is valid
- @j ã‚¤ãƒ³ãƒ©ã‚¤ãƒ³ã‚­ãƒ£ãƒƒã‚·ãƒ¥ãŒæœ‰åйãªã‚‰ã€å€¤ã‚’スタックã«ãƒ—ッシュã—㦠dst ã¸ã‚¸ãƒ£ãƒ³ãƒ—ã™ã‚‹ã€‚
- */
+/* push inline-cached value and go to dst if it is valid */
DEFINE_INSN
-getinlinecache
+opt_getinlinecache
(OFFSET dst, IC ic)
()
(VALUE val)
{
- if (ic->ic_serial == GET_GLOBAL_CONSTANT_STATE() &&
- (ic->ic_cref == NULL || ic->ic_cref == rb_vm_get_cref(GET_EP()))) {
+ if (vm_ic_hit_p(ic, GET_EP())) {
val = ic->ic_value.value;
JUMP(dst);
}
else {
- /* none */
val = Qnil;
}
}
-/**
- @c optimize
- @e set inline cache
- @j インラインキャッシュã®å€¤ã‚’設定ã™ã‚‹ã€‚
- */
+/* set inline cache */
DEFINE_INSN
-setinlinecache
+opt_setinlinecache
(IC ic)
(VALUE val)
(VALUE val)
{
- VM_ASSERT(ic->ic_value.value != Qundef);
- ic->ic_value.value = val;
- ic->ic_serial = GET_GLOBAL_CONSTANT_STATE() - ruby_vm_const_missing_count;
- ic->ic_cref = vm_get_const_key_cref(GET_EP());
- ruby_vm_const_missing_count = 0;
+ vm_ic_update(ic, val, GET_EP());
}
-/**
- @c optimize
- @e run iseq only once
- @j once を実ç¾ã™ã‚‹ã€‚
- */
+/* run iseq only once */
DEFINE_INSN
once
-(ISEQ iseq, IC ic)
+(ISEQ iseq, ISE ise)
()
(VALUE val)
{
- union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)ic;
-
-#define RUNNING_THREAD_ONCE_DONE ((rb_thread_t *)(0x1))
- retry:
- if (is->once.running_thread == RUNNING_THREAD_ONCE_DONE) {
- val = is->once.value;
- }
- else if (is->once.running_thread == NULL) {
- is->once.running_thread = th;
- val = is->once.value = rb_ensure(vm_once_exec, (VALUE)iseq, vm_once_clear, (VALUE)is);
- /* is->once.running_thread is cleared by vm_once_clear() */
- is->once.running_thread = RUNNING_THREAD_ONCE_DONE; /* success */
- rb_iseq_add_mark_object(GET_ISEQ(), val);
- }
- else if (is->once.running_thread == th) {
- /* recursive once */
- val = vm_once_exec((VALUE)iseq);
- }
- else {
- /* waiting for finish */
- RUBY_VM_CHECK_INTS(th);
- rb_thread_schedule();
- goto retry;
- }
+ val = vm_once_dispatch(ec, iseq, ise);
}
-/**
- @c optimize
- @e case dispatcher, jump by table if possible
- @j case æ–‡ã§ã€å¯èƒ½ãªã‚‰è¡¨å¼•ãã§ã‚¸ãƒ£ãƒ³ãƒ—ã™ã‚‹ã€‚
- */
+/* case dispatcher, jump by table if possible */
DEFINE_INSN
opt_case_dispatch
(CDHASH hash, OFFSET else_offset)
(..., VALUE key)
-() // inc += -1;
-{
- switch (OBJ_BUILTIN_TYPE(key)) {
- case -1:
- case T_FLOAT:
- case T_SYMBOL:
- case T_BIGNUM:
- case T_STRING:
- if (BASIC_OP_UNREDEFINED_P(BOP_EQQ,
- SYMBOL_REDEFINED_OP_FLAG |
- INTEGER_REDEFINED_OP_FLAG |
- FLOAT_REDEFINED_OP_FLAG |
- NIL_REDEFINED_OP_FLAG |
- TRUE_REDEFINED_OP_FLAG |
- FALSE_REDEFINED_OP_FLAG |
- STRING_REDEFINED_OP_FLAG)) {
- st_data_t val;
- if (RB_FLOAT_TYPE_P(key)) {
- double kval = RFLOAT_VALUE(key);
- if (!isinf(kval) && modf(kval, &kval) == 0.0) {
- key = FIXABLE(kval) ? LONG2FIX((long)kval) : rb_dbl2big(kval);
- }
- }
- if (st_lookup(RHASH_TBL_RAW(hash), key, &val)) {
- JUMP(FIX2INT((VALUE)val));
- }
- else {
- JUMP(else_offset);
- }
- }
+()
+// attr rb_snum_t sp_inc = -1;
+{
+ OFFSET dst = vm_case_dispatch(hash, else_offset, key);
+
+ if (dst) {
+ JUMP(dst);
}
}
/** simple functions */
-/**
- @c optimize
- @e optimized X+Y.
- @j 最é©åŒ–ã•れ㟠X+Y。
- */
+/* optimized X+Y. */
DEFINE_INSN
opt_plus
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv, VALUE obj)
(VALUE val)
+/* Array + anything can be handled inside of opt_plus, and that
+ * anything is converted into array using #to_ary. */
+// attr bool leaf = false; /* has rb_to_array_type() */
{
- if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_PLUS,INTEGER_REDEFINED_OP_FLAG)) {
- /* fixnum + fixnum */
-#ifndef LONG_LONG_VALUE
- VALUE msb = (VALUE)1 << ((sizeof(VALUE) * CHAR_BIT) - 1);
- val = recv - 1 + obj;
- if ((~(recv ^ obj) & (recv ^ val)) & msb) {
- val = rb_int2big((SIGNED_VALUE)((val>>1) | (recv & msb)));
- }
-#else
- val = LONG2NUM(FIX2LONG(recv) + FIX2LONG(obj));
-#endif
- }
- else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) {
- val = DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj));
- }
- else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
- if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) {
- val = DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj));
- }
- else if (RBASIC_CLASS(recv) == rb_cString && RBASIC_CLASS(obj) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_PLUS, STRING_REDEFINED_OP_FLAG)) {
- val = rb_str_plus(recv, obj);
- }
- else if (RBASIC_CLASS(recv) == rb_cArray &&
- BASIC_OP_UNREDEFINED_P(BOP_PLUS, ARRAY_REDEFINED_OP_FLAG)) {
- val = rb_ary_plus(recv, obj);
- }
- else {
- goto INSN_LABEL(normal_dispatch);
- }
- }
- else {
- INSN_LABEL(normal_dispatch):
- PUSH(recv);
- PUSH(obj);
- CALL_SIMPLE_METHOD(recv);
+ val = vm_opt_plus(recv, obj);
+
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e optimized X-Y.
- @j 最é©åŒ–ã•れ㟠X-Y。
- */
+/* optimized X-Y. */
DEFINE_INSN
opt_minus
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv, VALUE obj)
(VALUE val)
{
- if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_MINUS, INTEGER_REDEFINED_OP_FLAG)) {
- long a, b, c;
+ val = vm_opt_minus(recv, obj);
- a = FIX2LONG(recv);
- b = FIX2LONG(obj);
- c = a - b;
- val = LONG2NUM(c);
- }
- else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) {
- val = DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj));
- }
- else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
- if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) {
- val = DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj));
- }
- else {
- goto INSN_LABEL(normal_dispatch);
- }
- }
- else {
- /* other */
- INSN_LABEL(normal_dispatch):
- PUSH(recv);
- PUSH(obj);
- CALL_SIMPLE_METHOD(recv);
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e optimized X*Y.
- @j 最é©åŒ–ã•れ㟠X*Y。
- */
+/* optimized X*Y. */
DEFINE_INSN
opt_mult
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv, VALUE obj)
(VALUE val)
{
- if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_MULT, INTEGER_REDEFINED_OP_FLAG)) {
- val = rb_fix_mul_fix(recv, obj);
- }
- else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) {
- val = DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj));
- }
- else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
- if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) {
- val = DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj));
- }
- else {
- goto INSN_LABEL(normal_dispatch);
- }
- }
- else {
- INSN_LABEL(normal_dispatch):
- PUSH(recv);
- PUSH(obj);
- CALL_SIMPLE_METHOD(recv);
+ val = vm_opt_mult(recv, obj);
+
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e optimized X/Y.
- @j 最é©åŒ–ã•れ㟠X/Y。
- */
+/* optimized X/Y. */
DEFINE_INSN
opt_div
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv, VALUE obj)
(VALUE val)
{
- if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_DIV, INTEGER_REDEFINED_OP_FLAG)) {
- if (FIX2LONG(obj) == 0) goto INSN_LABEL(normal_dispatch);
- val = rb_fix_div_fix(recv, obj);
- }
- else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_DIV, FLOAT_REDEFINED_OP_FLAG)) {
- val = DBL2NUM(RFLOAT_VALUE(recv) / RFLOAT_VALUE(obj));
- }
- else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
- if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_DIV, FLOAT_REDEFINED_OP_FLAG)) {
- val = DBL2NUM(RFLOAT_VALUE(recv) / RFLOAT_VALUE(obj));
- }
- else {
- goto INSN_LABEL(normal_dispatch);
- }
- }
- else {
- INSN_LABEL(normal_dispatch):
- PUSH(recv);
- PUSH(obj);
- CALL_SIMPLE_METHOD(recv);
+ val = vm_opt_div(recv, obj);
+
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e optimized X%Y.
- @j 最é©åŒ–ã•れ㟠X%Y。
- */
+/* optimized X%Y. */
DEFINE_INSN
opt_mod
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv, VALUE obj)
(VALUE val)
{
- if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_MOD, INTEGER_REDEFINED_OP_FLAG )) {
- if (FIX2LONG(obj) == 0) goto INSN_LABEL(normal_dispatch);
- val = rb_fix_mod_fix(recv, obj);
- }
- else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) {
- val = DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)));
- }
- else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
- if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) {
- val = DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)));
- }
- else {
- goto INSN_LABEL(normal_dispatch);
- }
- }
- else {
- INSN_LABEL(normal_dispatch):
- PUSH(recv);
- PUSH(obj);
- CALL_SIMPLE_METHOD(recv);
+ val = vm_opt_mod(recv, obj);
+
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e optimized X==Y.
- @j 最é©åŒ–ã•れ㟠X==Y。
- */
+/* optimized X==Y. */
DEFINE_INSN
opt_eq
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv, VALUE obj)
(VALUE val)
+/* This instruction can compare a string with non-string. This
+ * (somewhat) coerces the non-string into a string, via a method
+ * call. */
+// attr bool leaf = false; /* has rb_str_equal() */
{
val = opt_eq_func(recv, obj, ci, cc);
if (val == Qundef) {
- /* other */
- PUSH(recv);
- PUSH(obj);
- CALL_SIMPLE_METHOD(recv);
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e optimized X!=Y.
- @j 最é©åŒ–ã•れ㟠X!=Y。
- */
+/* optimized X!=Y. */
DEFINE_INSN
opt_neq
-(CALL_INFO ci, CALL_CACHE cc, CALL_INFO ci_eq, CALL_CACHE cc_eq)
+(CALL_INFO ci_eq, CALL_CACHE cc_eq, CALL_INFO ci, CALL_CACHE cc)
(VALUE recv, VALUE obj)
(VALUE val)
+/* Same discussion as opt_eq. */
+// attr bool leaf = false; /* has rb_str_equal() */
{
- extern VALUE rb_obj_not_equal(VALUE obj1, VALUE obj2);
- vm_search_method(ci, cc, recv);
-
- val = Qundef;
-
- if (check_cfunc(cc->me, rb_obj_not_equal)) {
- val = opt_eq_func(recv, obj, ci_eq, cc_eq);
-
- if (val != Qundef) {
- val = RTEST(val) ? Qfalse : Qtrue;
- }
- }
+ val = vm_opt_neq(ci, cc, ci_eq, cc_eq, recv, obj);
if (val == Qundef) {
- /* other */
- PUSH(recv);
- PUSH(obj);
- CALL_SIMPLE_METHOD(recv);
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e optimized X<Y.
- @j 最é©åŒ–ã•れ㟠X<Y。
- */
+/* optimized X<Y. */
DEFINE_INSN
opt_lt
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv, VALUE obj)
(VALUE val)
{
- if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_LT, INTEGER_REDEFINED_OP_FLAG)) {
- SIGNED_VALUE a = recv, b = obj;
+ val = vm_opt_lt(recv, obj);
- if (a < b) {
- val = Qtrue;
- }
- else {
- val = Qfalse;
- }
- }
- else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) {
- /* flonum is not NaN */
- val = RFLOAT_VALUE(recv) < RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
- }
- else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
- if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) {
- val = double_cmp_lt(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj));
- }
- else {
- goto INSN_LABEL(normal_dispatch);
- }
- }
- else {
- INSN_LABEL(normal_dispatch):
- PUSH(recv);
- PUSH(obj);
- CALL_SIMPLE_METHOD(recv);
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e optimized X<=Y.
- @j 最é©åŒ–ã•れ㟠X<=Y。
- */
+/* optimized X<=Y. */
DEFINE_INSN
opt_le
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv, VALUE obj)
(VALUE val)
{
- if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_LE, INTEGER_REDEFINED_OP_FLAG)) {
- SIGNED_VALUE a = recv, b = obj;
+ val = vm_opt_le(recv, obj);
- if (a <= b) {
- val = Qtrue;
- }
- else {
- val = Qfalse;
- }
- }
- else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_LE, FLOAT_REDEFINED_OP_FLAG)) {
- /* flonum is not NaN */
- val = RFLOAT_VALUE(recv) <= RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
- }
- else {
- /* other */
- PUSH(recv);
- PUSH(obj);
- CALL_SIMPLE_METHOD(recv);
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e optimized X>Y.
- @j 最é©åŒ–ã•れ㟠X>Y。
- */
+/* optimized X>Y. */
DEFINE_INSN
opt_gt
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv, VALUE obj)
(VALUE val)
{
- if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_GT, INTEGER_REDEFINED_OP_FLAG)) {
- SIGNED_VALUE a = recv, b = obj;
+ val = vm_opt_gt(recv, obj);
- if (a > b) {
- val = Qtrue;
- }
- else {
- val = Qfalse;
- }
- }
- else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) {
- /* flonum is not NaN */
- val = RFLOAT_VALUE(recv) > RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
- }
- else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
- if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) {
- val = double_cmp_gt(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj));
- }
- else {
- goto INSN_LABEL(normal_dispatch);
- }
- }
- else {
- INSN_LABEL(normal_dispatch):
- PUSH(recv);
- PUSH(obj);
- CALL_SIMPLE_METHOD(recv);
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e optimized X>=Y.
- @j 最é©åŒ–ã•れ㟠X>=Y。
- */
+/* optimized X>=Y. */
DEFINE_INSN
opt_ge
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv, VALUE obj)
(VALUE val)
{
- if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_GE, INTEGER_REDEFINED_OP_FLAG)) {
- SIGNED_VALUE a = recv, b = obj;
+ val = vm_opt_ge(recv, obj);
- if (a >= b) {
- val = Qtrue;
- }
- else {
- val = Qfalse;
- }
- }
- else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_GE, FLOAT_REDEFINED_OP_FLAG)) {
- /* flonum is not NaN */
- val = RFLOAT_VALUE(recv) >= RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
- }
- else {
- PUSH(recv);
- PUSH(obj);
- CALL_SIMPLE_METHOD(recv);
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e <<
- @j 最é©åŒ–ã•れ㟠X<<Y。
- */
+/* << */
DEFINE_INSN
opt_ltlt
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv, VALUE obj)
(VALUE val)
{
- if (!SPECIAL_CONST_P(recv)) {
- if (RBASIC_CLASS(recv) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_LTLT, STRING_REDEFINED_OP_FLAG)) {
- val = rb_str_concat(recv, obj);
- }
- else if (RBASIC_CLASS(recv) == rb_cArray &&
- BASIC_OP_UNREDEFINED_P(BOP_LTLT, ARRAY_REDEFINED_OP_FLAG)) {
- val = rb_ary_push(recv, obj);
- }
- else {
- goto INSN_LABEL(normal_dispatch);
- }
+ val = vm_opt_ltlt(recv, obj);
+
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
- else {
- INSN_LABEL(normal_dispatch):
- PUSH(recv);
- PUSH(obj);
- CALL_SIMPLE_METHOD(recv);
+}
+
+/* optimized X&Y. */
+DEFINE_INSN
+opt_and
+(CALL_INFO ci, CALL_CACHE cc)
+(VALUE recv, VALUE obj)
+(VALUE val)
+{
+ val = vm_opt_and(recv, obj);
+
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e []
- @j 最é©åŒ–ã•れ㟠recv[obj]。
- */
+/* optimized X|Y. */
DEFINE_INSN
-opt_aref
+opt_or
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv, VALUE obj)
(VALUE val)
{
- if (!SPECIAL_CONST_P(recv)) {
- if (RBASIC_CLASS(recv) == rb_cArray && BASIC_OP_UNREDEFINED_P(BOP_AREF, ARRAY_REDEFINED_OP_FLAG) && FIXNUM_P(obj)) {
- val = rb_ary_entry(recv, FIX2LONG(obj));
- }
- else if (RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG)) {
- val = rb_hash_aref(recv, obj);
- }
- else {
- goto INSN_LABEL(normal_dispatch);
- }
+ val = vm_opt_or(recv, obj);
+
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
- else {
- INSN_LABEL(normal_dispatch):
- PUSH(recv);
- PUSH(obj);
- CALL_SIMPLE_METHOD(recv);
+}
+
+/* [] */
+DEFINE_INSN
+opt_aref
+(CALL_INFO ci, CALL_CACHE cc)
+(VALUE recv, VALUE obj)
+(VALUE val)
+/* This is complicated. In case of hash, vm_opt_aref() resorts to
+ * rb_hash_aref(). If `recv` has no `obj`, this function then yields
+ * default_proc. This is a method call. So opt_aref is
+ * (surprisingly) not leaf. */
+// attr bool leaf = false; /* has rb_funcall() */ /* calls #yield */
+{
+ val = vm_opt_aref(recv, obj);
+
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e recv[obj] = set
- @j 最é©åŒ–ã•れ㟠recv[obj] = set。
- */
+/* recv[obj] = set */
DEFINE_INSN
opt_aset
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv, VALUE obj, VALUE set)
(VALUE val)
+/* This is another story than opt_aref. When vm_opt_aset() resorts
+ * to rb_hash_aset(), which should call #hash for `obj`. */
+// attr bool leaf = false; /* has rb_funcall() */ /* calls #hash */
{
- if (!SPECIAL_CONST_P(recv)) {
- if (RBASIC_CLASS(recv) == rb_cArray && BASIC_OP_UNREDEFINED_P(BOP_ASET, ARRAY_REDEFINED_OP_FLAG) && FIXNUM_P(obj)) {
- rb_ary_store(recv, FIX2LONG(obj), set);
- val = set;
- }
- else if (RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG)) {
- rb_hash_aset(recv, obj, set);
- val = set;
- }
- else {
- goto INSN_LABEL(normal_dispatch);
- }
- }
- else {
- INSN_LABEL(normal_dispatch):
- PUSH(recv);
- PUSH(obj);
- PUSH(set);
- CALL_SIMPLE_METHOD(recv);
+ val = vm_opt_aset(recv, obj, set);
+
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e recv[str] = set
- @j 最é©åŒ–ã•れ㟠recv[str] = set。
- */
+/* recv[str] = set */
DEFINE_INSN
opt_aset_with
-(CALL_INFO ci, CALL_CACHE cc, VALUE key)
+(VALUE key, CALL_INFO ci, CALL_CACHE cc)
(VALUE recv, VALUE val)
(VALUE val)
+/* Same discussion as opt_aset. */
+// attr bool leaf = false; /* has rb_funcall() */ /* calls #hash */
{
- if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG)) {
- rb_hash_aset(recv, key, val);
+ VALUE tmp = vm_opt_aset_with(recv, key, val);
+
+ if (tmp != Qundef) {
+ val = tmp;
}
else {
- PUSH(recv);
- PUSH(rb_str_resurrect(key));
+#ifndef MJIT_HEADER
+ TOPN(0) = rb_str_resurrect(key);
PUSH(val);
- CALL_SIMPLE_METHOD(recv);
+#endif
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e recv[str]
- @j 最é©åŒ–ã•れ㟠recv[str]。
- */
+/* recv[str] */
DEFINE_INSN
opt_aref_with
-(CALL_INFO ci, CALL_CACHE cc, VALUE key)
+(VALUE key, CALL_INFO ci, CALL_CACHE cc)
(VALUE recv)
(VALUE val)
+/* Same discussion as opt_aref. */
+// attr bool leaf = false; /* has rb_funcall() */ /* calls #yield */
{
- if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG)) {
- val = rb_hash_aref(recv, key);
- }
- else {
- PUSH(recv);
+ val = vm_opt_aref_with(recv, key);
+
+ if (val == Qundef) {
+#ifndef MJIT_HEADER
PUSH(rb_str_resurrect(key));
- CALL_SIMPLE_METHOD(recv);
+#endif
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e optimized length
- @j 最é©åŒ–ã•れ㟠recv.length()。
- */
+/* optimized length */
DEFINE_INSN
opt_length
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv)
(VALUE val)
{
- if (!SPECIAL_CONST_P(recv)) {
- if (RBASIC_CLASS(recv) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_LENGTH, STRING_REDEFINED_OP_FLAG)) {
- val = rb_str_length(recv);
- }
- else if (RBASIC_CLASS(recv) == rb_cArray &&
- BASIC_OP_UNREDEFINED_P(BOP_LENGTH, ARRAY_REDEFINED_OP_FLAG)) {
- val = LONG2NUM(RARRAY_LEN(recv));
- }
- else if (RBASIC_CLASS(recv) == rb_cHash &&
- BASIC_OP_UNREDEFINED_P(BOP_LENGTH, HASH_REDEFINED_OP_FLAG)) {
- val = INT2FIX(RHASH_SIZE(recv));
- }
- else {
- goto INSN_LABEL(normal_dispatch);
- }
- }
- else {
- INSN_LABEL(normal_dispatch):
- PUSH(recv);
- CALL_SIMPLE_METHOD(recv);
+ val = vm_opt_length(recv, BOP_LENGTH);
+
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e optimized size
- @j 最é©åŒ–ã•れ㟠recv.size()。
- */
+/* optimized size */
DEFINE_INSN
opt_size
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv)
(VALUE val)
{
- if (!SPECIAL_CONST_P(recv)) {
- if (RBASIC_CLASS(recv) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_SIZE, STRING_REDEFINED_OP_FLAG)) {
- val = rb_str_length(recv);
- }
- else if (RBASIC_CLASS(recv) == rb_cArray &&
- BASIC_OP_UNREDEFINED_P(BOP_SIZE, ARRAY_REDEFINED_OP_FLAG)) {
- val = LONG2NUM(RARRAY_LEN(recv));
- }
- else if (RBASIC_CLASS(recv) == rb_cHash &&
- BASIC_OP_UNREDEFINED_P(BOP_SIZE, HASH_REDEFINED_OP_FLAG)) {
- val = INT2FIX(RHASH_SIZE(recv));
- }
- else {
- goto INSN_LABEL(normal_dispatch);
- }
- }
- else {
- INSN_LABEL(normal_dispatch):
- PUSH(recv);
- CALL_SIMPLE_METHOD(recv);
+ val = vm_opt_length(recv, BOP_SIZE);
+
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e optimized empty?
- @j 最é©åŒ–ã•れ㟠recv.empty?()。
- */
+/* optimized empty? */
DEFINE_INSN
opt_empty_p
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv)
(VALUE val)
{
- if (!SPECIAL_CONST_P(recv)) {
- if (RBASIC_CLASS(recv) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, STRING_REDEFINED_OP_FLAG)) {
- if (RSTRING_LEN(recv) == 0) val = Qtrue;
- else val = Qfalse;
- }
- else if (RBASIC_CLASS(recv) == rb_cArray &&
- BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, ARRAY_REDEFINED_OP_FLAG)) {
- if (RARRAY_LEN(recv) == 0) val = Qtrue;
- else val = Qfalse;
- }
- else if (RBASIC_CLASS(recv) == rb_cHash &&
- BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, HASH_REDEFINED_OP_FLAG)) {
- if (RHASH_EMPTY_P(recv)) val = Qtrue;
- else val = Qfalse;
- }
- else {
- goto INSN_LABEL(normal_dispatch);
- }
- }
- else {
- INSN_LABEL(normal_dispatch):
- PUSH(recv);
- CALL_SIMPLE_METHOD(recv);
+ val = vm_opt_empty_p(recv);
+
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e optimized succ
- @j 最é©åŒ–ã•れ㟠recv.succ()。
- */
+/* optimized succ */
DEFINE_INSN
opt_succ
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv)
(VALUE val)
{
- if (SPECIAL_CONST_P(recv)) {
- if (FIXNUM_P(recv) &&
- BASIC_OP_UNREDEFINED_P(BOP_SUCC, INTEGER_REDEFINED_OP_FLAG)) {
- /* fixnum + INT2FIX(1) */
- if (recv != LONG2FIX(FIXNUM_MAX)) {
- val = recv - 1 + INT2FIX(1);
- }
- else {
- val = LONG2NUM(FIXNUM_MAX + 1);
- }
- }
- else {
- goto INSN_LABEL(normal_dispatch);
- }
- }
- else {
- if (RBASIC_CLASS(recv) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_SUCC, STRING_REDEFINED_OP_FLAG)) {
- val = rb_str_succ(recv);
- }
- else
- {
- goto INSN_LABEL(normal_dispatch);
- }
- }
- if (0) {
- INSN_LABEL(normal_dispatch):
- PUSH(recv);
- CALL_SIMPLE_METHOD(recv);
+ val = vm_opt_succ(recv);
+
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e optimized not
- @j 最é©åŒ–ã•れ㟠recv.!()。
- */
+/* optimized not */
DEFINE_INSN
opt_not
(CALL_INFO ci, CALL_CACHE cc)
(VALUE recv)
(VALUE val)
{
- vm_search_method(ci, cc, recv);
+ val = vm_opt_not(ci, cc, recv);
- if (check_cfunc(cc->me, rb_obj_not)) {
- val = RTEST(recv) ? Qfalse : Qtrue;
- }
- else {
- PUSH(recv);
- CALL_SIMPLE_METHOD(recv);
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-
-/**
- @c optimize
- @e optimized regexp match
- @j 最é©åŒ–ã•ã‚ŒãŸæ­£è¦è¡¨ç¾ãƒžãƒƒãƒã€‚
- */
+/* optimized regexp match */
DEFINE_INSN
opt_regexpmatch1
-(VALUE r)
+(VALUE recv)
(VALUE obj)
(VALUE val)
+// attr bool leaf = BASIC_OP_UNREDEFINED_P(BOP_MATCH, REGEXP_REDEFINED_OP_FLAG);
{
- if (BASIC_OP_UNREDEFINED_P(BOP_MATCH, REGEXP_REDEFINED_OP_FLAG)) {
- val = rb_reg_match(r, obj);
- }
- else {
- val = rb_funcall(r, idEqTilde, 1, obj);
- }
+ val = vm_opt_regexpmatch1(recv, obj);
}
-/**
- @c optimize
- @e optimized regexp match 2
- @j 最é©åŒ–ã•ã‚ŒãŸæ­£è¦è¡¨ç¾ãƒžãƒƒãƒ 2
- */
+/* optimized regexp match 2 */
DEFINE_INSN
opt_regexpmatch2
(CALL_INFO ci, CALL_CACHE cc)
(VALUE obj2, VALUE obj1)
(VALUE val)
{
- if (CLASS_OF(obj2) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_MATCH, STRING_REDEFINED_OP_FLAG)) {
- val = rb_reg_match(obj1, obj2);
- }
- else {
- PUSH(obj2);
- PUSH(obj1);
- CALL_SIMPLE_METHOD(obj2);
+ val = vm_opt_regexpmatch2(obj2, obj1);
+
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
}
}
-/**
- @c optimize
- @e call native compiled method
- @j ãƒã‚¤ãƒ†ã‚£ãƒ–コンパイルã—ãŸãƒ¡ã‚½ãƒƒãƒ‰ã‚’起動。
- */
+/* call native compiled method */
DEFINE_INSN
opt_call_c_function
(rb_insn_func_t funcptr)
()
()
+// attr bool leaf = false; /* anything can happen inside */
+// attr bool handles_sp = true;
{
- reg_cfp = (funcptr)(th, reg_cfp);
+ reg_cfp = (funcptr)(ec, reg_cfp);
if (reg_cfp == 0) {
- VALUE err = th->errinfo;
- th->errinfo = Qnil;
+ VALUE err = ec->errinfo;
+ ec->errinfo = Qnil;
THROW_EXCEPTION(err);
}
@@ -2151,11 +1434,7 @@ opt_call_c_function
NEXT_INSN();
}
-/**
- @c joke
- @e BLT
- @j BLT
- */
+/* BLT */
DEFINE_INSN
bitblt
()
@@ -2165,11 +1444,7 @@ bitblt
ret = rb_str_new2("a bit of bacon, lettuce and tomato");
}
-/**
- @c joke
- @e The Answer to Life, the Universe, and Everything
- @j 人生ã€å®‡å®™ã€ã™ã¹ã¦ã®ç­”ãˆã€‚
- */
+/* The Answer to Life, the Universe, and Everything */
DEFINE_INSN
answer
()
@@ -2178,4 +1453,3 @@ answer
{
ret = INT2FIX(42);
}
-
diff --git a/internal.h b/internal.h
index acab0d31b7..13a0424344 100644
--- a/internal.h
+++ b/internal.h
@@ -13,8 +13,6 @@
#define RUBY_INTERNAL_H 1
#include "ruby.h"
-#include "ruby/encoding.h"
-#include "ruby/io.h"
#if defined(__cplusplus)
extern "C" {
@@ -23,6 +21,28 @@ extern "C" {
#endif
#endif
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#endif
+
+#ifndef __bool_true_false_are_defined
+# ifndef __cplusplus
+# undef bool
+# undef false
+# undef true
+# define bool signed char
+# define false 0
+# define true 1
+# define __bool_true_false_are_defined 1
+# endif
+#endif
+
+/* The most significant bit of the lower part of half-long integer.
+ * If sizeof(long) == 4, this is 0x8000.
+ * If sizeof(long) == 8, this is 0x80000000.
+ */
+#define HALF_LONG_MSB ((SIGNED_VALUE)1<<((SIZEOF_LONG*CHAR_BIT-1)/2))
+
#define LIKELY(x) RB_LIKELY(x)
#define UNLIKELY(x) RB_UNLIKELY(x)
@@ -34,6 +54,34 @@ extern "C" {
# define WARN_UNUSED_RESULT(x) x
#endif
+#if 0
+#elif defined(NO_SANITIZE)
+# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) \
+ NO_SANITIZE("address", NOINLINE(x))
+#elif defined(NO_SANITIZE_ADDRESS)
+# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) \
+ NO_SANITIZE_ADDRESS(NOINLINE(x))
+#elif defined(NO_ADDRESS_SAFETY_ANALYSIS)
+# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) \
+ NO_ADDRESS_SAFETY_ANALYSIS(NOINLINE(x))
+#else
+# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) x
+#endif
+
+#if defined(NO_SANITIZE) && defined(__GNUC__) &&! defined(__clang__)
+/* GCC warns about unknown sanitizer, which is annoying. */
+#undef NO_SANITIZE
+#define NO_SANITIZE(x, y) \
+ COMPILER_WARNING_PUSH; \
+ COMPILER_WARNING_IGNORED(-Wattributes); \
+ __attribute__((__no_sanitize__(x))) y; \
+ COMPILER_WARNING_POP
+#endif
+
+#ifndef NO_SANITIZE
+# define NO_SANITIZE(x, y) y
+#endif
+
#ifdef HAVE_VALGRIND_MEMCHECK_H
# include <valgrind/memcheck.h>
# ifndef VALGRIND_MAKE_MEM_DEFINED
@@ -57,8 +105,71 @@ extern "C" {
# define __has_extension __has_feature
#endif
-#if GCC_VERSION_SINCE(4, 6, 0) || __has_extension(c_static_assert)
+#ifndef MJIT_HEADER
+
+#ifdef HAVE_SANITIZER_ASAN_INTERFACE_H
+# include <sanitizer/asan_interface.h>
+#endif
+
+#if !__has_feature(address_sanitizer)
+# define __asan_poison_memory_region(x, y)
+# define __asan_unpoison_memory_region(x, y)
+# define __asan_region_is_poisoned(x, y) 0
+#endif
+
+#ifdef HAVE_SANITIZER_MSAN_INTERFACE_H
+# include <sanitizer/msan_interface.h>
+#endif
+
+#if !__has_feature(memory_sanitizer)
+# define __msan_allocated_memory(x, y)
+# define __msan_poison(x, y)
+# define __msan_unpoison(x, y)
+# define __msan_unpoison_string(x)
+#endif
+
+static inline void
+poison_memory_region(const volatile void *ptr, size_t size)
+{
+ __msan_poison(ptr, size);
+ __asan_poison_memory_region(ptr, size);
+}
+
+static inline void
+poison_object(VALUE obj)
+{
+ struct RVALUE *ptr = (void *)obj;
+ poison_memory_region(ptr, SIZEOF_VALUE);
+}
+
+static inline void
+unpoison_memory_region(const volatile void *ptr, size_t size, bool malloc_p)
+{
+ __asan_unpoison_memory_region(ptr, size);
+ if (malloc_p) {
+ __msan_allocated_memory(ptr, size);
+ }
+ else {
+ __msan_unpoison(ptr, size);
+ }
+}
+
+static inline void
+unpoison_object(VALUE obj, bool newobj_p)
+{
+ struct RVALUE *ptr = (void *)obj;
+ unpoison_memory_region(ptr, SIZEOF_VALUE, newobj_p);
+}
+
+#endif
+
+/* Prevent compiler from reordering access */
+#define ACCESS_ONCE(type,x) (*((volatile type *)&(x)))
+
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
# define STATIC_ASSERT(name, expr) _Static_assert(expr, #name ": " #expr)
+#elif GCC_VERSION_SINCE(4, 6, 0) || __has_extension(c_static_assert)
+# define STATIC_ASSERT(name, expr) RB_GNUC_EXTENSION _Static_assert(expr, #name ": " #expr)
#else
# define STATIC_ASSERT(name, expr) typedef int static_assert_##name##_check[1 - 2*!(expr)]
#endif
@@ -80,15 +191,41 @@ extern "C" {
#endif
#define TIMET_MAX_PLUS_ONE (2*(double)(TIMET_MAX/2+1))
+#ifdef HAVE_BUILTIN___BUILTIN_MUL_OVERFLOW_P
+#define MUL_OVERFLOW_P(a, b) \
+ __builtin_mul_overflow_p((a), (b), (__typeof__(a * b))0)
+#elif defined HAVE_BUILTIN___BUILTIN_MUL_OVERFLOW
+#define MUL_OVERFLOW_P(a, b) \
+ RB_GNUC_EXTENSION_BLOCK(__typeof__(a) c; __builtin_mul_overflow((a), (b), &c))
+#endif
+
#define MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, min, max) ( \
(a) == 0 ? 0 : \
(a) == -1 ? (b) < -(max) : \
(a) > 0 ? \
((b) > 0 ? (max) / (a) < (b) : (min) / (a) > (b)) : \
((b) > 0 ? (min) / (a) < (b) : (max) / (a) > (b)))
+
+#ifdef HAVE_BUILTIN___BUILTIN_MUL_OVERFLOW_P
+/* __builtin_mul_overflow_p can take bitfield */
+/* and GCC permits bitfields for integers other than int */
+#define MUL_OVERFLOW_FIXNUM_P(a, b) RB_GNUC_EXTENSION_BLOCK( \
+ struct { long fixnum : SIZEOF_LONG * CHAR_BIT - 1; } c; \
+ __builtin_mul_overflow_p((a), (b), c.fixnum); \
+)
+#else
#define MUL_OVERFLOW_FIXNUM_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, FIXNUM_MIN, FIXNUM_MAX)
-#define MUL_OVERFLOW_LONG_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, LONG_MIN, LONG_MAX)
-#define MUL_OVERFLOW_INT_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, INT_MIN, INT_MAX)
+#endif
+
+#ifdef MUL_OVERFLOW_P
+#define MUL_OVERFLOW_LONG_LONG_P(a, b) MUL_OVERFLOW_P(a, b)
+#define MUL_OVERFLOW_LONG_P(a, b) MUL_OVERFLOW_P(a, b)
+#define MUL_OVERFLOW_INT_P(a, b) MUL_OVERFLOW_P(a, b)
+#else
+#define MUL_OVERFLOW_LONG_LONG_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, LLONG_MIN, LLONG_MAX)
+#define MUL_OVERFLOW_LONG_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, LONG_MIN, LONG_MAX)
+#define MUL_OVERFLOW_INT_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, INT_MIN, INT_MAX)
+#endif
#ifndef swap16
# ifdef HAVE_BUILTIN___BUILTIN_BSWAP16
@@ -133,20 +270,20 @@ extern "C" {
# endif
#endif
-static inline int
+static inline unsigned int
nlz_int(unsigned int x)
{
#if defined(HAVE_BUILTIN___BUILTIN_CLZ)
if (x == 0) return SIZEOF_INT * CHAR_BIT;
- return __builtin_clz(x);
+ return (unsigned int)__builtin_clz(x);
#else
unsigned int y;
# if 64 < SIZEOF_INT * CHAR_BIT
- int n = 128;
+ unsigned int n = 128;
# elif 32 < SIZEOF_INT * CHAR_BIT
- int n = 64;
+ unsigned int n = 64;
# else
- int n = 32;
+ unsigned int n = 32;
# endif
# if 64 < SIZEOF_INT * CHAR_BIT
y = x >> 64; if (y) {n -= 64; x = y;}
@@ -159,24 +296,24 @@ nlz_int(unsigned int x)
y = x >> 4; if (y) {n -= 4; x = y;}
y = x >> 2; if (y) {n -= 2; x = y;}
y = x >> 1; if (y) {return n - 2;}
- return (int)(n - x);
+ return (unsigned int)(n - x);
#endif
}
-static inline int
+static inline unsigned int
nlz_long(unsigned long x)
{
#if defined(HAVE_BUILTIN___BUILTIN_CLZL)
if (x == 0) return SIZEOF_LONG * CHAR_BIT;
- return __builtin_clzl(x);
+ return (unsigned int)__builtin_clzl(x);
#else
unsigned long y;
# if 64 < SIZEOF_LONG * CHAR_BIT
- int n = 128;
+ unsigned int n = 128;
# elif 32 < SIZEOF_LONG * CHAR_BIT
- int n = 64;
+ unsigned int n = 64;
# else
- int n = 32;
+ unsigned int n = 32;
# endif
# if 64 < SIZEOF_LONG * CHAR_BIT
y = x >> 64; if (y) {n -= 64; x = y;}
@@ -189,25 +326,25 @@ nlz_long(unsigned long x)
y = x >> 4; if (y) {n -= 4; x = y;}
y = x >> 2; if (y) {n -= 2; x = y;}
y = x >> 1; if (y) {return n - 2;}
- return (int)(n - x);
+ return (unsigned int)(n - x);
#endif
}
#ifdef HAVE_LONG_LONG
-static inline int
+static inline unsigned int
nlz_long_long(unsigned LONG_LONG x)
{
#if defined(HAVE_BUILTIN___BUILTIN_CLZLL)
if (x == 0) return SIZEOF_LONG_LONG * CHAR_BIT;
- return __builtin_clzll(x);
+ return (unsigned int)__builtin_clzll(x);
#else
unsigned LONG_LONG y;
# if 64 < SIZEOF_LONG_LONG * CHAR_BIT
- int n = 128;
+ unsigned int n = 128;
# elif 32 < SIZEOF_LONG_LONG * CHAR_BIT
- int n = 64;
+ unsigned int n = 64;
# else
- int n = 32;
+ unsigned int n = 32;
# endif
# if 64 < SIZEOF_LONG_LONG * CHAR_BIT
y = x >> 64; if (y) {n -= 64; x = y;}
@@ -220,17 +357,17 @@ nlz_long_long(unsigned LONG_LONG x)
y = x >> 4; if (y) {n -= 4; x = y;}
y = x >> 2; if (y) {n -= 2; x = y;}
y = x >> 1; if (y) {return n - 2;}
- return (int)(n - x);
+ return (unsigned int)(n - x);
#endif
}
#endif
#ifdef HAVE_UINT128_T
-static inline int
+static inline unsigned int
nlz_int128(uint128_t x)
{
uint128_t y;
- int n = 128;
+ unsigned int n = 128;
y = x >> 64; if (y) {n -= 64; x = y;}
y = x >> 32; if (y) {n -= 32; x = y;}
y = x >> 16; if (y) {n -= 16; x = y;}
@@ -238,23 +375,30 @@ nlz_int128(uint128_t x)
y = x >> 4; if (y) {n -= 4; x = y;}
y = x >> 2; if (y) {n -= 2; x = y;}
y = x >> 1; if (y) {return n - 2;}
- return (int)(n - x);
+ return (unsigned int)(n - x);
}
#endif
-static inline int
-nlz_intptr(uintptr_t x) {
-#if SIZEOF_VOIDP == 8
- return nlz_long_long(x);
-#elif SIZEOF_VOIDP == 4
+static inline unsigned int
+nlz_intptr(uintptr_t x)
+{
+#if SIZEOF_UINTPTR_T == SIZEOF_INT
return nlz_int(x);
+#elif SIZEOF_UINTPTR_T == SIZEOF_LONG
+ return nlz_long(x);
+#elif SIZEOF_UINTPTR_T == SIZEOF_LONG_LONG
+ return nlz_long_long(x);
+#else
+ #error no known integer type corresponds uintptr_t
+ return /* sane compiler */ ~0;
#endif
}
-static inline int
-rb_popcount32(uint32_t x) {
+static inline unsigned int
+rb_popcount32(uint32_t x)
+{
#ifdef HAVE_BUILTIN___BUILTIN_POPCOUNT
- return __builtin_popcount(x);
+ return (unsigned int)__builtin_popcount(x);
#else
x = (x & 0x55555555) + (x >> 1 & 0x55555555);
x = (x & 0x33333333) + (x >> 2 & 0x33333333);
@@ -265,7 +409,8 @@ rb_popcount32(uint32_t x) {
}
static inline int
-rb_popcount64(uint64_t x) {
+rb_popcount64(uint64_t x)
+{
#ifdef HAVE_BUILTIN___BUILTIN_POPCOUNT
return __builtin_popcountll(x);
#else
@@ -279,7 +424,8 @@ rb_popcount64(uint64_t x) {
}
static inline int
-rb_popcount_intptr(uintptr_t x) {
+rb_popcount_intptr(uintptr_t x)
+{
#if SIZEOF_VOIDP == 8
return rb_popcount64(x);
#elif SIZEOF_VOIDP == 4
@@ -288,7 +434,8 @@ rb_popcount_intptr(uintptr_t x) {
}
static inline int
-ntz_int32(uint32_t x) {
+ntz_int32(uint32_t x)
+{
#ifdef HAVE_BUILTIN___BUILTIN_CTZ
return __builtin_ctz(x);
#else
@@ -297,7 +444,8 @@ ntz_int32(uint32_t x) {
}
static inline int
-ntz_int64(uint64_t x) {
+ntz_int64(uint64_t x)
+{
#ifdef HAVE_BUILTIN___BUILTIN_CTZLL
return __builtin_ctzll(x);
#else
@@ -306,7 +454,8 @@ ntz_int64(uint64_t x) {
}
static inline int
-ntz_intptr(uintptr_t x) {
+ntz_intptr(uintptr_t x)
+{
#if SIZEOF_VOIDP == 8
return ntz_int64(x);
#elif SIZEOF_VOIDP == 4
@@ -323,8 +472,65 @@ ntz_intptr(uintptr_t x) {
VALUE rb_int128t2big(int128_t n);
#endif
-#define ST2FIX(h) LONG2FIX((long)(h))
+static inline long
+rb_overflowed_fix_to_int(long x)
+{
+ return (long)((unsigned long)(x >> 1) ^ (1LU << (SIZEOF_LONG * CHAR_BIT - 1)));
+}
+static inline VALUE
+rb_fix_plus_fix(VALUE x, VALUE y)
+{
+#ifdef HAVE_BUILTIN___BUILTIN_ADD_OVERFLOW
+ long lz;
+ /* NOTE
+ * (1) `LONG2FIX(FIX2LONG(x)+FIX2LONG(y))`
+ + = `((lx*2+1)/2 + (ly*2+1)/2)*2+1`
+ + = `lx*2 + ly*2 + 1`
+ + = `(lx*2+1) + (ly*2+1) - 1`
+ + = `x + y - 1`
+ * (2) Fixnum's LSB is always 1.
+ * It means you can always run `x - 1` without overflow.
+ * (3) Of course `z = x + (y-1)` may overflow.
+ * At that time true value is
+ * * positive: 0b0 1xxx...1, and z = 0b1xxx...1
+ * * nevative: 0b1 0xxx...1, and z = 0b0xxx...1
+ * To convert this true value to long,
+ * (a) Use arithmetic shift
+ * * positive: 0b11xxx...
+ * * negative: 0b00xxx...
+ * (b) invert MSB
+ * * positive: 0b01xxx...
+ * * negative: 0b10xxx...
+ */
+ if (__builtin_add_overflow((long)x, (long)y-1, &lz)) {
+ return rb_int2big(rb_overflowed_fix_to_int(lz));
+ }
+ else {
+ return (VALUE)lz;
+ }
+#else
+ long lz = FIX2LONG(x) + FIX2LONG(y);
+ return LONG2NUM(lz);
+#endif
+}
+
+static inline VALUE
+rb_fix_minus_fix(VALUE x, VALUE y)
+{
+#ifdef HAVE_BUILTIN___BUILTIN_SUB_OVERFLOW
+ long lz;
+ if (__builtin_sub_overflow((long)x, (long)y-1, &lz)) {
+ return rb_int2big(rb_overflowed_fix_to_int(lz));
+ }
+ else {
+ return (VALUE)lz;
+ }
+#else
+ long lz = FIX2LONG(x) - FIX2LONG(y);
+ return LONG2NUM(lz);
+#endif
+}
/* arguments must be Fixnum */
static inline VALUE
@@ -395,19 +601,28 @@ rb_fix_mod_fix(VALUE x, VALUE y)
return mod;
}
-#if defined(HAVE_UINT128_T)
+#if defined(HAVE_UINT128_T) && defined(HAVE_LONG_LONG)
# define bit_length(x) \
+ (unsigned int) \
(sizeof(x) <= SIZEOF_INT ? SIZEOF_INT * CHAR_BIT - nlz_int((unsigned int)(x)) : \
sizeof(x) <= SIZEOF_LONG ? SIZEOF_LONG * CHAR_BIT - nlz_long((unsigned long)(x)) : \
sizeof(x) <= SIZEOF_LONG_LONG ? SIZEOF_LONG_LONG * CHAR_BIT - nlz_long_long((unsigned LONG_LONG)(x)) : \
SIZEOF_INT128_T * CHAR_BIT - nlz_int128((uint128_t)(x)))
+#elif defined(HAVE_UINT128_T)
+# define bit_length(x) \
+ (unsigned int) \
+ (sizeof(x) <= SIZEOF_INT ? SIZEOF_INT * CHAR_BIT - nlz_int((unsigned int)(x)) : \
+ sizeof(x) <= SIZEOF_LONG ? SIZEOF_LONG * CHAR_BIT - nlz_long((unsigned long)(x)) : \
+ SIZEOF_INT128_T * CHAR_BIT - nlz_int128((uint128_t)(x)))
#elif defined(HAVE_LONG_LONG)
# define bit_length(x) \
+ (unsigned int) \
(sizeof(x) <= SIZEOF_INT ? SIZEOF_INT * CHAR_BIT - nlz_int((unsigned int)(x)) : \
sizeof(x) <= SIZEOF_LONG ? SIZEOF_LONG * CHAR_BIT - nlz_long((unsigned long)(x)) : \
SIZEOF_LONG_LONG * CHAR_BIT - nlz_long_long((unsigned LONG_LONG)(x)))
#else
# define bit_length(x) \
+ (unsigned int) \
(sizeof(x) <= SIZEOF_INT ? SIZEOF_INT * CHAR_BIT - nlz_int((unsigned int)(x)) : \
SIZEOF_LONG * CHAR_BIT - nlz_long((unsigned long)(x)))
#endif
@@ -485,7 +700,7 @@ struct RBignum {
BDIGIT ary[BIGNUM_EMBED_LEN_MAX];
} as;
};
-#define BIGNUM_SIGN_BIT FL_USER1
+#define BIGNUM_SIGN_BIT ((VALUE)FL_USER1)
/* sign: positive:1, negative:0 */
#define BIGNUM_SIGN(b) ((RBASIC(b)->flags & BIGNUM_SIGN_BIT) != 0)
#define BIGNUM_SET_SIGN(b,sign) \
@@ -495,13 +710,15 @@ struct RBignum {
#define BIGNUM_NEGATIVE_P(b) (!BIGNUM_SIGN(b))
#define BIGNUM_NEGATE(b) (RBASIC(b)->flags ^= BIGNUM_SIGN_BIT)
-#define BIGNUM_EMBED_FLAG FL_USER2
-#define BIGNUM_EMBED_LEN_MASK (FL_USER5|FL_USER4|FL_USER3)
-#define BIGNUM_EMBED_LEN_SHIFT (FL_USHIFT+BIGNUM_EMBED_LEN_NUMBITS)
+#define BIGNUM_EMBED_FLAG ((VALUE)FL_USER2)
+#define BIGNUM_EMBED_LEN_MASK \
+ (~(~(VALUE)0U << BIGNUM_EMBED_LEN_NUMBITS) << BIGNUM_EMBED_LEN_SHIFT)
+#define BIGNUM_EMBED_LEN_SHIFT \
+ (FL_USHIFT+3) /* bit offset of BIGNUM_EMBED_LEN_MASK */
#define BIGNUM_LEN(b) \
((RBASIC(b)->flags & BIGNUM_EMBED_FLAG) ? \
- (long)((RBASIC(b)->flags >> BIGNUM_EMBED_LEN_SHIFT) & \
- (BIGNUM_EMBED_LEN_MASK >> BIGNUM_EMBED_LEN_SHIFT)) : \
+ (size_t)((RBASIC(b)->flags >> BIGNUM_EMBED_LEN_SHIFT) & \
+ (BIGNUM_EMBED_LEN_MASK >> BIGNUM_EMBED_LEN_SHIFT)) : \
RBIGNUM(b)->as.heap.len)
/* LSB:BIGNUM_DIGITS(b)[0], MSB:BIGNUM_DIGITS(b)[BIGNUM_LEN(b)-1] */
#define BIGNUM_DIGITS(b) \
@@ -537,30 +754,103 @@ struct RComplex {
#define RCOMPLEX(obj) (R_CAST(RComplex)(obj))
-#ifdef RCOMPLEX_SET_REAL /* shortcut macro for internal only */
-#undef RCOMPLEX_SET_REAL
-#undef RCOMPLEX_SET_IMAG
+/* shortcut macro for internal only */
#define RCOMPLEX_SET_REAL(cmp, r) RB_OBJ_WRITE((cmp), &((struct RComplex *)(cmp))->real,(r))
#define RCOMPLEX_SET_IMAG(cmp, i) RB_OBJ_WRITE((cmp), &((struct RComplex *)(cmp))->imag,(i))
+
+enum ruby_rhash_flags {
+ RHASH_ST_TABLE_FLAG = FL_USER3,
+ RHASH_AR_TABLE_MAX_SIZE = 8,
+ RHASH_AR_TABLE_SIZE_MASK = (FL_USER4|FL_USER5|FL_USER6|FL_USER7),
+ RHASH_AR_TABLE_SIZE_SHIFT = (FL_USHIFT+4),
+ RHASH_AR_TABLE_BOUND_MASK = (FL_USER8|FL_USER9|FL_USER10|FL_USER11),
+ RHASH_AR_TABLE_BOUND_SHIFT = (FL_USHIFT+8),
+
+ RHASH_ENUM_END
+};
+
+#define HASH_PROC_DEFAULT FL_USER2
+
+#define RHASH_AR_TABLE_SIZE_RAW(h) \
+ ((unsigned int)((RBASIC(h)->flags & RHASH_AR_TABLE_SIZE_MASK) >> RHASH_AR_TABLE_SIZE_SHIFT))
+
+int rb_hash_ar_table_p(VALUE hash);
+struct ar_table_struct *rb_hash_ar_table(VALUE hash);
+st_table *rb_hash_st_table(VALUE hash);
+void rb_hash_st_table_set(VALUE hash, st_table *st);
+
+#if 0 /* for debug */
+#define RHASH_AR_TABLE_P(hash) rb_hash_ar_table_p(hash)
+#define RHASH_AR_TABLE(h) rb_hash_ar_table(h)
+#define RHASH_ST_TABLE(h) rb_hash_st_table(h)
+#else
+#define RHASH_AR_TABLE_P(hash) (!FL_TEST_RAW((hash), RHASH_ST_TABLE_FLAG))
+#define RHASH_AR_TABLE(hash) (RHASH(hash)->as.ar)
+#define RHASH_ST_TABLE(hash) (RHASH(hash)->as.st)
+#endif
+
+#define RHASH(obj) (R_CAST(RHash)(obj))
+#define RHASH_ST_SIZE(h) (RHASH_ST_TABLE(h)->num_entries)
+#define RHASH_ST_TABLE_P(h) (!RHASH_AR_TABLE_P(h))
+#define RHASH_ST_CLEAR(h) (FL_UNSET_RAW(h, RHASH_ST_TABLE_FLAG), RHASH(h)->as.ar = NULL)
+
+#define RHASH_AR_TABLE_SIZE_MASK (VALUE)RHASH_AR_TABLE_SIZE_MASK
+#define RHASH_AR_TABLE_SIZE_SHIFT RHASH_AR_TABLE_SIZE_SHIFT
+#define RHASH_AR_TABLE_BOUND_MASK (VALUE)RHASH_AR_TABLE_BOUND_MASK
+#define RHASH_AR_TABLE_BOUND_SHIFT RHASH_AR_TABLE_BOUND_SHIFT
+
+#if USE_TRANSIENT_HEAP
+#define RHASH_TRANSIENT_FLAG FL_USER14
+#define RHASH_TRANSIENT_P(hash) FL_TEST_RAW((hash), RHASH_TRANSIENT_FLAG)
+#define RHASH_SET_TRANSIENT_FLAG(h) FL_SET_RAW(h, RHASH_TRANSIENT_FLAG)
+#define RHASH_UNSET_TRANSIENT_FLAG(h) FL_UNSET_RAW(h, RHASH_TRANSIENT_FLAG)
+#else
+#define RHASH_TRANSIENT_P(hash) 0
+#define RHASH_SET_TRANSIENT_FLAG(h) ((void)0)
+#define RHASH_UNSET_TRANSIENT_FLAG(h) ((void)0)
#endif
+#define RHASH_AR_TABLE_MAX_SIZE 8
+#define RHASH_AR_TABLE_MAX_BOUND RHASH_AR_TABLE_MAX_SIZE
+
+typedef struct ar_table_entry {
+ VALUE hash;
+ VALUE key;
+ VALUE record;
+} ar_table_entry;
+
+typedef struct ar_table_struct {
+ ar_table_entry entries[RHASH_AR_TABLE_MAX_SIZE];
+} ar_table;
+
+/*
+ * RHASH_AR_TABLE_P(h):
+ * * as.ar == NULL or
+ * as.ar points ar_table.
+ * * as.ar is allocated by transient heap or xmalloc.
+ *
+ * !RHASH_AR_TABLE_P(h):
+ * * as.st points st_table.
+ */
struct RHash {
struct RBasic basic;
- struct st_table *ntbl; /* possibly 0 */
+ union {
+ st_table *st;
+ ar_table *ar; /* possibly 0 */
+ } as;
int iter_lev;
const VALUE ifnone;
};
-#define RHASH(obj) (R_CAST(RHash)(obj))
-
#ifdef RHASH_ITER_LEV
-#undef RHASH_ITER_LEV
-#undef RHASH_IFNONE
-#undef RHASH_SIZE
-#define RHASH_ITER_LEV(h) (RHASH(h)->iter_lev)
-#define RHASH_IFNONE(h) (RHASH(h)->ifnone)
-#define RHASH_SIZE(h) (RHASH(h)->ntbl ? (st_index_t)RHASH(h)->ntbl->num_entries : 0)
-#endif
+# undef RHASH_ITER_LEV
+# undef RHASH_IFNONE
+# undef RHASH_SIZE
+
+# define RHASH_ITER_LEV(h) (RHASH(h)->iter_lev)
+# define RHASH_IFNONE(h) (RHASH(h)->ifnone)
+# define RHASH_SIZE(h) (RHASH_AR_TABLE_P(h) ? RHASH_AR_TABLE_SIZE_RAW(h) : RHASH_ST_SIZE(h))
+#endif /* #ifdef RHASH_ITER_LEV */
/* missing/setproctitle.c */
#ifndef HAVE_SETPROCTITLE
@@ -570,14 +860,26 @@ extern void ruby_init_setproctitle(int argc, char *argv[]);
#define RSTRUCT_EMBED_LEN_MAX RSTRUCT_EMBED_LEN_MAX
#define RSTRUCT_EMBED_LEN_MASK RSTRUCT_EMBED_LEN_MASK
#define RSTRUCT_EMBED_LEN_SHIFT RSTRUCT_EMBED_LEN_SHIFT
+
enum {
RSTRUCT_EMBED_LEN_MAX = 3,
RSTRUCT_EMBED_LEN_MASK = (RUBY_FL_USER2|RUBY_FL_USER1),
RSTRUCT_EMBED_LEN_SHIFT = (RUBY_FL_USHIFT+1),
+ RSTRUCT_TRANSIENT_FLAG = FL_USER3,
RSTRUCT_ENUM_END
};
+#if USE_TRANSIENT_HEAP
+#define RSTRUCT_TRANSIENT_P(st) FL_TEST_RAW((obj), RSTRUCT_TRANSIENT_FLAG)
+#define RSTRUCT_TRANSIENT_SET(st) FL_SET_RAW((st), RSTRUCT_TRANSIENT_FLAG)
+#define RSTRUCT_TRANSIENT_UNSET(st) FL_UNSET_RAW((st), RSTRUCT_TRANSIENT_FLAG)
+#else
+#define RSTRUCT_TRANSIENT_P(st) 0
+#define RSTRUCT_TRANSIENT_SET(st) ((void)0)
+#define RSTRUCT_TRANSIENT_UNSET(st) ((void)0)
+#endif
+
struct RStruct {
struct RBasic basic;
union {
@@ -618,6 +920,13 @@ rb_struct_const_ptr(VALUE st)
RSTRUCT(st)->as.ary : RSTRUCT(st)->as.heap.ptr);
}
+static inline const VALUE *
+rb_struct_const_heap_ptr(VALUE st)
+{
+ /* TODO: check embed on debug mode */
+ return RSTRUCT(st)->as.heap.ptr;
+}
+
/* class.c */
struct rb_deprecated_classext_struct {
@@ -635,12 +944,15 @@ struct rb_subclass_entry {
#if defined(HAVE_LONG_LONG)
typedef unsigned LONG_LONG rb_serial_t;
#define SERIALT2NUM ULL2NUM
+#define PRI_SERIALT_PREFIX PRI_LL_PREFIX
#elif defined(HAVE_UINT64_T)
typedef uint64_t rb_serial_t;
#define SERIALT2NUM SIZET2NUM
+#define PRI_SERIALT_PREFIX PRI_64_PREFIX
#else
typedef unsigned long rb_serial_t;
#define SERIALT2NUM ULONG2NUM
+#define PRI_SERIALT_PREFIX PRI_LONG_PREFIX
#endif
struct rb_classext_struct {
@@ -727,45 +1039,64 @@ struct RIMemo {
};
enum imemo_type {
- imemo_env = 0,
- imemo_cref = 1,
- imemo_svar = 2,
- imemo_throw_data = 3,
- imemo_ifunc = 4,
- imemo_memo = 5,
- imemo_ment = 6,
- imemo_iseq = 7,
- imemo_mask = 0x07
+ imemo_env = 0,
+ imemo_cref = 1, /*!< class reference */
+ imemo_svar = 2, /*!< special variable */
+ imemo_throw_data = 3,
+ imemo_ifunc = 4, /*!< iterator function */
+ imemo_memo = 5,
+ imemo_ment = 6,
+ imemo_iseq = 7,
+ imemo_tmpbuf = 8,
+ imemo_ast = 9,
+ imemo_parser_strterm = 10
};
+#define IMEMO_MASK 0x0f
static inline enum imemo_type
imemo_type(VALUE imemo)
{
- return (RBASIC(imemo)->flags >> FL_USHIFT) & imemo_mask;
+ return (RBASIC(imemo)->flags >> FL_USHIFT) & IMEMO_MASK;
}
-/* FL_USER0 to FL_USER2 is for type */
-#define IMEMO_FL_USHIFT (FL_USHIFT + 3)
-#define IMEMO_FL_USER0 FL_USER3
-#define IMEMO_FL_USER1 FL_USER4
-#define IMEMO_FL_USER2 FL_USER5
-#define IMEMO_FL_USER3 FL_USER6
-#define IMEMO_FL_USER4 FL_USER7
+static inline int
+imemo_type_p(VALUE imemo, enum imemo_type imemo_type)
+{
+ if (LIKELY(!RB_SPECIAL_CONST_P(imemo))) {
+ /* fixed at compile time if imemo_type is given. */
+ const VALUE mask = (IMEMO_MASK << FL_USHIFT) | RUBY_T_MASK;
+ const VALUE expected_type = (imemo_type << FL_USHIFT) | T_IMEMO;
+ /* fixed at runtime. */
+ return expected_type == (RBASIC(imemo)->flags & mask);
+ }
+ else {
+ return 0;
+ }
+}
-/* CREF in method.h */
+/* FL_USER0 to FL_USER3 is for type */
+#define IMEMO_FL_USHIFT (FL_USHIFT + 4)
+#define IMEMO_FL_USER0 FL_USER4
+#define IMEMO_FL_USER1 FL_USER5
+#define IMEMO_FL_USER2 FL_USER6
+#define IMEMO_FL_USER3 FL_USER7
+#define IMEMO_FL_USER4 FL_USER8
-/* SVAR */
+/* CREF (Class REFerence) is defined in method.h */
+/*! SVAR (Special VARiable) */
struct vm_svar {
VALUE flags;
- const VALUE cref_or_me;
+ const VALUE cref_or_me; /*!< class reference or rb_method_entry_t */
const VALUE lastline;
const VALUE backref;
const VALUE others;
};
-/* THROW_DATA */
+#define THROW_DATA_CONSUMED IMEMO_FL_USER0
+
+/*! THROW_DATA */
struct vm_throw_data {
VALUE flags;
VALUE reserved;
@@ -774,22 +1105,72 @@ struct vm_throw_data {
VALUE throw_state;
};
-#define THROW_DATA_P(err) RB_TYPE_P((err), T_IMEMO)
+#define THROW_DATA_P(err) RB_TYPE_P((VALUE)(err), T_IMEMO)
-/* IFUNC */
+/* IFUNC (Internal FUNCtion) */
+struct vm_ifunc_argc {
+#if SIZEOF_INT * 2 > SIZEOF_VALUE
+ signed int min: (SIZEOF_VALUE * CHAR_BIT) / 2;
+ signed int max: (SIZEOF_VALUE * CHAR_BIT) / 2;
+#else
+ int min, max;
+#endif
+};
+
+/*! IFUNC (Internal FUNCtion) */
struct vm_ifunc {
VALUE flags;
VALUE reserved;
VALUE (*func)(ANYARGS);
const void *data;
- ID id;
+ struct vm_ifunc_argc argc;
};
#define IFUNC_NEW(a, b, c) ((struct vm_ifunc *)rb_imemo_new(imemo_ifunc, (VALUE)(a), (VALUE)(b), (VALUE)(c), 0))
+struct vm_ifunc *rb_vm_ifunc_new(VALUE (*func)(ANYARGS), const void *data, int min_argc, int max_argc);
+static inline struct vm_ifunc *
+rb_vm_ifunc_proc_new(VALUE (*func)(ANYARGS), const void *data)
+{
+ return rb_vm_ifunc_new(func, data, 0, UNLIMITED_ARGUMENTS);
+}
+
+typedef struct rb_imemo_tmpbuf_struct {
+ VALUE flags;
+ VALUE reserved;
+ VALUE *ptr; /* malloc'ed buffer */
+ struct rb_imemo_tmpbuf_struct *next; /* next imemo */
+ size_t cnt; /* buffer size in VALUE */
+} rb_imemo_tmpbuf_t;
-/* MEMO */
+VALUE rb_imemo_tmpbuf_auto_free_pointer(void *buf);
+VALUE rb_imemo_tmpbuf_auto_free_maybe_mark_buffer(void *buf, size_t cnt);
+rb_imemo_tmpbuf_t *rb_imemo_tmpbuf_parser_heap(void *buf, rb_imemo_tmpbuf_t *old_heap, size_t cnt);
+#define RB_IMEMO_TMPBUF_PTR(v) \
+ ((void *)(((const struct rb_imemo_tmpbuf_struct *)(v))->ptr))
+
+static inline VALUE
+rb_imemo_tmpbuf_auto_free_pointer_new_from_an_RString(VALUE str)
+{
+ const void *src;
+ void *dst;
+ size_t len;
+
+ SafeStringValue(str);
+ len = RSTRING_LEN(str);
+ src = RSTRING_PTR(str);
+ dst = ruby_xmalloc(len);
+ memcpy(dst, src, len);
+ return rb_imemo_tmpbuf_auto_free_pointer(dst);
+}
+
+void rb_strterm_mark(VALUE obj);
+
+/*! MEMO
+ *
+ * @see imemo_type
+ * */
struct MEMO {
VALUE flags;
VALUE reserved;
@@ -830,12 +1211,13 @@ struct MEMO {
enum {
cmp_opt_Fixnum,
cmp_opt_String,
+ cmp_opt_Float,
cmp_optimizable_count
};
struct cmp_opt_data {
- int opt_methods;
- int opt_inited;
+ unsigned int opt_methods;
+ unsigned int opt_inited;
};
#define NEW_CMP_OPT_MEMO(type, value) \
@@ -853,6 +1235,8 @@ struct cmp_opt_data {
(((long)a > (long)b) ? 1 : ((long)a < (long)b) ? -1 : 0) : \
(STRING_P(a) && STRING_P(b) && CMP_OPTIMIZABLE(data, String)) ? \
rb_str_cmp(a, b) : \
+ (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b) && CMP_OPTIMIZABLE(data, Float)) ? \
+ rb_float_cmp(a, b) : \
rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b))
/* ment is in method.h */
@@ -869,16 +1253,50 @@ VALUE rb_gvar_get(struct rb_global_entry *);
VALUE rb_gvar_set(struct rb_global_entry *, VALUE);
VALUE rb_gvar_defined(struct rb_global_entry *);
-struct vtm; /* defined by timev.h */
-
/* array.c */
+
+#ifndef ARRAY_DEBUG
+#define ARRAY_DEBUG 0
+#endif
+
+#ifdef ARRAY_DEBUG
+#define RARRAY_PTR_IN_USE_FLAG FL_USER14
+#define ARY_PTR_USING_P(ary) FL_TEST_RAW((ary), RARRAY_PTR_IN_USE_FLAG)
+#else
+
+/* disable debug function */
+#undef RARRAY_PTR_USE_START_TRANSIENT
+#undef RARRAY_PTR_USE_END_TRANSIENT
+#define RARRAY_PTR_USE_START_TRANSIENT(a) ((VALUE *)RARRAY_CONST_PTR_TRANSIENT(a))
+#define RARRAY_PTR_USE_END_TRANSIENT(a)
+#define ARY_PTR_USING_P(ary) 0
+
+#endif
+
+#if USE_TRANSIENT_HEAP
+#define RARY_TRANSIENT_SET(ary) FL_SET_RAW((ary), RARRAY_TRANSIENT_FLAG);
+#define RARY_TRANSIENT_UNSET(ary) FL_UNSET_RAW((ary), RARRAY_TRANSIENT_FLAG);
+#else
+#undef RARRAY_TRANSIENT_P
+#define RARRAY_TRANSIENT_P(a) 0
+#define RARY_TRANSIENT_SET(ary) ((void)0)
+#define RARY_TRANSIENT_UNSET(ary) ((void)0)
+#endif
+
+
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_at(VALUE, VALUE);
+VALUE rb_ary_aref1(VALUE ary, VALUE i);
+VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e);
size_t rb_ary_memsize(VALUE);
-#ifdef __GNUC__
+VALUE rb_to_array_type(VALUE obj);
+VALUE rb_check_to_array(VALUE ary);
+VALUE rb_ary_tmp_new_from_values(VALUE, long, const VALUE *);
+VALUE rb_ary_behead(VALUE, long);
+#if defined(__GNUC__) && defined(HAVE_VA_ARGS_MACRO)
#define rb_ary_new_from_args(n, ...) \
__extension__ ({ \
const VALUE args_to_new_ary[] = {__VA_ARGS__}; \
@@ -889,6 +1307,22 @@ size_t rb_ary_memsize(VALUE);
})
#endif
+static inline VALUE
+rb_ary_entry_internal(VALUE ary, long offset)
+{
+ long len = RARRAY_LEN(ary);
+ const VALUE *ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
+ if (len == 0) return Qnil;
+ if (offset < 0) {
+ offset += len;
+ if (offset < 0) return Qnil;
+ }
+ else if (len <= offset) {
+ return Qnil;
+ }
+ return ptr[offset];
+}
+
/* bignum.c */
extern const char ruby_digitmap[];
double rb_big_fdiv_double(VALUE x, VALUE y);
@@ -900,6 +1334,7 @@ size_t rb_big_size(VALUE);
VALUE rb_integer_float_cmp(VALUE x, VALUE y);
VALUE rb_integer_float_eq(VALUE x, VALUE y);
VALUE rb_cstr_parse_inum(const char *str, ssize_t len, char **endp, int base);
+VALUE rb_str_convert_to_inum(VALUE str, int base, int badcheck, int raise_exception);
VALUE rb_big_comp(VALUE x);
VALUE rb_big_aref(VALUE x, VALUE y);
VALUE rb_big_abs(VALUE x);
@@ -910,6 +1345,7 @@ VALUE rb_big_gt(VALUE x, VALUE y);
VALUE rb_big_ge(VALUE x, VALUE y);
VALUE rb_big_lt(VALUE x, VALUE y);
VALUE rb_big_le(VALUE x, VALUE y);
+VALUE rb_int_powm(int const argc, VALUE * const argv, VALUE const num);
/* class.c */
VALUE rb_class_boot(VALUE);
@@ -924,7 +1360,6 @@ 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);
VALUE rb_obj_public_methods(int argc, const VALUE *argv, VALUE obj);
-int rb_obj_basic_to_s_p(VALUE);
VALUE rb_special_singleton_class(VALUE);
VALUE rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach);
VALUE rb_singleton_class_get(VALUE obj);
@@ -940,32 +1375,37 @@ VALUE rb_invcmp(VALUE, VALUE);
struct rb_block;
int rb_dvar_defined(ID, const struct rb_block *);
int rb_local_defined(ID, const struct rb_block *);
-CONSTFUNC(const char * rb_insns_name(int i));
+const char * rb_insns_name(int i);
VALUE rb_insns_name_array(void);
+int rb_vm_insn_addr2insn(const void *);
/* complex.c */
-VALUE rb_complex_plus(VALUE, VALUE);
-VALUE rb_complex_mul(VALUE, VALUE);
-VALUE rb_complex_abs(VALUE x);
-VALUE rb_complex_sqrt(VALUE x);
+VALUE rb_dbl_complex_new_polar_pi(double abs, double ang);
+struct rb_thread_struct;
/* cont.c */
+struct rb_fiber_struct;
VALUE rb_obj_is_fiber(VALUE);
-void rb_fiber_reset_root_local_storage(VALUE);
+void rb_fiber_reset_root_local_storage(struct rb_thread_struct *);
void ruby_register_rollback_func_for_ensure(VALUE (*ensure_func)(ANYARGS), VALUE (*rollback_func)(ANYARGS));
+void rb_fiber_init_mjit_cont(struct rb_fiber_struct *fiber);
/* debug.c */
PRINTF_ARGS(void ruby_debug_printf(const char*, ...), 1, 2);
+/* dir.c */
+VALUE rb_dir_getwd_ospath(void);
+
/* dmyext.c */
void Init_enc(void);
void Init_ext(void);
/* encoding.c */
ID rb_id_encoding(void);
-CONSTFUNC(void rb_gc_mark_encodings(void));
+#ifdef RUBY_ENCODING_H
rb_encoding *rb_enc_get_from_index(int index);
rb_encoding *rb_enc_check_str(VALUE str1, VALUE str2);
+#endif
int rb_encdb_replicate(const char *alias, const char *orig);
int rb_encdb_alias(const char *alias, const char *orig);
int rb_encdb_dummy(const char *name);
@@ -976,6 +1416,7 @@ void rb_encdb_set_unicode(int index);
PUREFUNC(int rb_data_is_encoding(VALUE obj));
/* enum.c */
+extern VALUE rb_cArithSeq;
VALUE rb_f_send(int argc, VALUE *argv, VALUE recv);
VALUE rb_nmin_run(VALUE obj, VALUE num, int by, int rev, int ary);
@@ -984,28 +1425,52 @@ extern VALUE rb_eEAGAIN;
extern VALUE rb_eEWOULDBLOCK;
extern VALUE rb_eEINPROGRESS;
void rb_report_bug_valist(VALUE file, int line, const char *fmt, va_list args);
-PRINTF_ARGS(void rb_compile_error_str(VALUE file, int line, void *enc, const char *fmt, ...), 4, 5);
-VALUE rb_syntax_error_append(VALUE, VALUE, int, int, rb_encoding*, const char*, va_list);
VALUE rb_check_backtrace(VALUE);
NORETURN(void rb_async_bug_errno(const char *,int));
const char *rb_builtin_type_name(int t);
const char *rb_builtin_class_name(VALUE x);
+PRINTF_ARGS(void rb_sys_warn(const char *fmt, ...), 1, 2);
+PRINTF_ARGS(void rb_syserr_warn(int err, const char *fmt, ...), 2, 3);
+PRINTF_ARGS(void rb_sys_warning(const char *fmt, ...), 1, 2);
+PRINTF_ARGS(void rb_syserr_warning(int err, const char *fmt, ...), 2, 3);
+#ifdef RUBY_ENCODING_H
+VALUE rb_syntax_error_append(VALUE, VALUE, int, int, rb_encoding*, const char*, va_list);
PRINTF_ARGS(void rb_enc_warn(rb_encoding *enc, const char *fmt, ...), 2, 3);
+PRINTF_ARGS(void rb_sys_enc_warn(rb_encoding *enc, const char *fmt, ...), 2, 3);
+PRINTF_ARGS(void rb_syserr_enc_warn(int err, rb_encoding *enc, const char *fmt, ...), 3, 4);
PRINTF_ARGS(void rb_enc_warning(rb_encoding *enc, const char *fmt, ...), 2, 3);
PRINTF_ARGS(void rb_sys_enc_warning(rb_encoding *enc, const char *fmt, ...), 2, 3);
+PRINTF_ARGS(void rb_syserr_enc_warning(int err, rb_encoding *enc, const char *fmt, ...), 3, 4);
+#endif
+
+#define rb_raise_cstr(etype, mesg) \
+ rb_exc_raise(rb_exc_new_str(etype, rb_str_new_cstr(mesg)))
+#define rb_raise_static(etype, mesg) \
+ rb_exc_raise(rb_exc_new_str(etype, rb_str_new_static(mesg, rb_strlen_lit(mesg))))
+
VALUE rb_name_err_new(VALUE mesg, VALUE recv, VALUE method);
#define rb_name_err_raise_str(mesg, recv, name) \
rb_exc_raise(rb_name_err_new(mesg, recv, name))
#define rb_name_err_raise(mesg, recv, name) \
rb_name_err_raise_str(rb_fstring_cstr(mesg), (recv), (name))
-NORETURN(void ruby_only_for_internal_use(const char *));
-#define ONLY_FOR_INTERNAL_USE(func) ruby_only_for_internal_use(func)
+VALUE rb_nomethod_err_new(VALUE mesg, VALUE recv, VALUE method, VALUE args, int priv);
+VALUE rb_key_err_new(VALUE mesg, VALUE recv, VALUE name);
+#define rb_key_err_raise(mesg, recv, name) \
+ rb_exc_raise(rb_key_err_new(mesg, recv, name))
+NORETURN(void ruby_deprecated_internal_feature(const char *));
+#define DEPRECATED_INTERNAL_FEATURE(func) \
+ (ruby_deprecated_internal_feature(func), UNREACHABLE)
+VALUE rb_warning_warn(VALUE mod, VALUE str);
+PRINTF_ARGS(VALUE rb_warning_string(const char *fmt, ...), 1, 2);
/* eval.c */
VALUE rb_refinement_module_get_refined_class(VALUE module);
+extern ID ruby_static_id_signo, ruby_static_id_status;
+void rb_class_modify_check(VALUE);
+#define id_signo ruby_static_id_signo
+#define id_status ruby_static_id_status
/* eval_error.c */
-void ruby_error_print(void);
VALUE rb_get_backtrace(VALUE info);
/* eval_jump.c */
@@ -1013,15 +1478,18 @@ void rb_call_end_proc(VALUE data);
void rb_mark_end_proc(void);
/* file.c */
+extern const char ruby_null_device[];
VALUE rb_home_dir_of(VALUE user, VALUE result);
VALUE rb_default_home_dir(VALUE result);
VALUE rb_realpath_internal(VALUE basedir, VALUE path, int strict);
+VALUE rb_check_realpath(VALUE basedir, VALUE path);
void rb_file_const(const char*, VALUE);
int rb_file_load_ok(const char *);
VALUE rb_file_expand_path_fast(VALUE, VALUE);
VALUE rb_file_expand_path_internal(VALUE, VALUE, int, int, VALUE);
VALUE rb_get_path_check_to_string(VALUE, int);
VALUE rb_get_path_check_convert(VALUE, VALUE, int);
+VALUE rb_get_path_check(VALUE, int);
void Init_File(void);
int ruby_is_fd_loadable(int fd);
@@ -1062,14 +1530,14 @@ void rb_copy_wb_protected_attribute(VALUE dest, VALUE obj);
#define ruby_sized_xfree(ptr, size) ruby_xfree(ptr)
#define SIZED_REALLOC_N(var,type,n,old_n) REALLOC_N(var, type, n)
#else
+RUBY_SYMBOL_EXPORT_BEGIN
void *ruby_sized_xrealloc(void *ptr, size_t new_size, size_t old_size) RUBY_ATTR_ALLOC_SIZE((2));
void *ruby_sized_xrealloc2(void *ptr, size_t new_count, size_t element_size, size_t old_count) RUBY_ATTR_ALLOC_SIZE((2, 3));
void ruby_sized_xfree(void *x, size_t size);
+RUBY_SYMBOL_EXPORT_END
#define SIZED_REALLOC_N(var,type,n,old_n) ((var)=(type*)ruby_sized_xrealloc((char*)(var), (n) * sizeof(type), (old_n) * sizeof(type)))
#endif
-void rb_gc_resurrect(VALUE ptr);
-
/* optimized version of NEWOBJ() */
#undef NEWOBJF_OF
#undef RB_NEWOBJ_OF
@@ -1079,8 +1547,22 @@ void rb_gc_resurrect(VALUE ptr);
rb_wb_unprotected_newobj_of(klass, flags))
#define NEWOBJ_OF(obj,type,klass,flags) RB_NEWOBJ_OF(obj,type,klass,flags)
+void *rb_aligned_malloc(size_t, size_t);
+void rb_aligned_free(void *);
+
/* hash.c */
+#if RHASH_CONVERT_TABLE_DEBUG
+struct st_table *rb_hash_tbl_raw(VALUE hash, const char *file, int line);
+#define RHASH_TBL_RAW(h) rb_hash_tbl_raw(h, __FILE__, __LINE__)
+#else
struct st_table *rb_hash_tbl_raw(VALUE hash);
+#define RHASH_TBL_RAW(h) rb_hash_tbl_raw(h)
+#endif
+
+VALUE rb_hash_new_with_size(st_index_t size);
+RUBY_SYMBOL_EXPORT_BEGIN
+VALUE rb_hash_new_compare_by_id(void);
+RUBY_SYMBOL_EXPORT_END
VALUE rb_hash_has_key(VALUE hash, VALUE key);
VALUE rb_hash_default_value(VALUE hash, VALUE key);
VALUE rb_hash_set_default_proc(VALUE hash, VALUE proc);
@@ -1088,14 +1570,21 @@ long rb_objid_hash(st_index_t index);
long rb_dbl_long_hash(double d);
st_table *rb_init_identtable(void);
st_table *rb_init_identtable_with_size(st_index_t size);
-
-#define RHASH_TBL_RAW(h) rb_hash_tbl_raw(h)
+VALUE rb_hash_compare_by_id_p(VALUE hash);
+VALUE rb_to_hash_type(VALUE obj);
+VALUE rb_hash_key_str(VALUE);
VALUE rb_hash_keys(VALUE hash);
VALUE rb_hash_values(VALUE hash);
VALUE rb_hash_rehash(VALUE hash);
+VALUE rb_hash_resurrect(VALUE hash);
int rb_hash_add_new_element(VALUE hash, VALUE key, VALUE val);
-#define HASH_DELETED FL_USER1
-#define HASH_PROC_DEFAULT FL_USER2
+VALUE rb_hash_set_pair(VALUE hash, VALUE pair);
+void rb_hash_bulk_insert(long, const VALUE *, VALUE);
+
+int rb_hash_stlike_lookup(VALUE hash, st_data_t key, st_data_t *pval);
+int rb_hash_stlike_delete(VALUE hash, st_data_t *pkey, st_data_t *pval);
+int rb_hash_stlike_foreach(VALUE hash, int (*func)(ANYARGS), st_data_t arg);
+int rb_hash_stlike_update(VALUE hash, st_data_t key, st_update_callback_func func, st_data_t arg);
/* inits.c */
void rb_call_inits(void);
@@ -1106,7 +1595,12 @@ void ruby_set_inplace_mode(const char *);
ssize_t rb_io_bufread(VALUE io, void *buf, size_t size);
void rb_stdio_set_default_encoding(void);
VALUE rb_io_flush_raw(VALUE, int);
+#ifdef RUBY_IO_H
size_t rb_io_memsize(const rb_io_t *);
+#endif
+int rb_stderr_tty_p(void);
+void rb_io_fptr_finalize_internal(void *ptr);
+#define rb_io_fptr_finalize rb_io_fptr_finalize_internal
/* load.c */
VALUE rb_get_load_path(void);
@@ -1132,6 +1626,20 @@ VALUE rb_math_sin(VALUE);
VALUE rb_math_sinh(VALUE);
VALUE rb_math_sqrt(VALUE);
+/* mjit.c */
+
+#if USE_MJIT
+extern int mjit_enabled;
+VALUE mjit_pause(int wait_p);
+VALUE mjit_resume(void);
+void mjit_finish(int close_handle_p);
+#else
+#define mjit_enabled 0
+static inline VALUE mjit_pause(int wait_p){ return Qnil; } /* unreachable */
+static inline VALUE mjit_resume(void){ return Qnil; } /* unreachable */
+static inline void mjit_finish(int close_handle_p){}
+#endif
+
/* newline.c */
void Init_newline(void);
@@ -1141,34 +1649,52 @@ void Init_newline(void);
#define FIXNUM_NEGATIVE_P(num) ((SIGNED_VALUE)(num) < 0)
#define FIXNUM_ZERO_P(num) ((num) == INT2FIX(0))
+#define INT_NEGATIVE_P(x) (FIXNUM_P(x) ? FIXNUM_NEGATIVE_P(x) : BIGNUM_NEGATIVE_P(x))
+
#ifndef ROUND_DEFAULT
-# define ROUND_DEFAULT RUBY_NUM_ROUND_HALF_EVEN
+# define ROUND_DEFAULT RUBY_NUM_ROUND_HALF_UP
#endif
enum ruby_num_rounding_mode {
RUBY_NUM_ROUND_HALF_UP,
RUBY_NUM_ROUND_HALF_EVEN,
+ RUBY_NUM_ROUND_HALF_DOWN,
RUBY_NUM_ROUND_DEFAULT = ROUND_DEFAULT
};
-#define ROUND_TO(mode, up, even) \
- ((mode) == RUBY_NUM_ROUND_HALF_EVEN ? even : up)
+#define ROUND_TO(mode, even, up, down) \
+ ((mode) == RUBY_NUM_ROUND_HALF_EVEN ? even : \
+ (mode) == RUBY_NUM_ROUND_HALF_UP ? up : down)
+#define ROUND_FUNC(mode, name) \
+ ROUND_TO(mode, name##_half_even, name##_half_up, name##_half_down)
+#define ROUND_CALL(mode, name, args) \
+ ROUND_TO(mode, name##_half_even args, \
+ name##_half_up args, name##_half_down args)
int rb_num_to_uint(VALUE val, unsigned int *ret);
VALUE ruby_num_interval_step_size(VALUE from, VALUE to, VALUE step, int excl);
-int ruby_float_step(VALUE from, VALUE to, VALUE step, int excl);
+double ruby_float_step_size(double beg, double end, double unit, int excl);
+int ruby_float_step(VALUE from, VALUE to, VALUE step, int excl, int allow_endless);
double ruby_float_mod(double x, double y);
int rb_num_negative_p(VALUE);
VALUE rb_int_succ(VALUE num);
VALUE rb_int_pred(VALUE num);
VALUE rb_int_uminus(VALUE num);
+VALUE rb_float_uminus(VALUE num);
VALUE rb_int_plus(VALUE x, VALUE y);
+VALUE rb_float_plus(VALUE x, VALUE y);
VALUE rb_int_minus(VALUE x, VALUE y);
+VALUE rb_float_minus(VALUE x, VALUE y);
VALUE rb_int_mul(VALUE x, VALUE y);
+VALUE rb_float_mul(VALUE x, VALUE y);
VALUE rb_int_idiv(VALUE x, VALUE y);
+VALUE rb_float_div(VALUE x, VALUE y);
VALUE rb_int_modulo(VALUE x, VALUE y);
VALUE rb_int_round(VALUE num, int ndigits, enum ruby_num_rounding_mode mode);
VALUE rb_int2str(VALUE num, int base);
VALUE rb_dbl_hash(double d);
VALUE rb_fix_plus(VALUE x, VALUE y);
+VALUE rb_int_gt(VALUE x, VALUE y);
+int rb_float_cmp(VALUE x, VALUE y);
+VALUE rb_float_gt(VALUE x, VALUE y);
VALUE rb_int_ge(VALUE x, VALUE y);
enum ruby_num_rounding_mode rb_num_get_rounding_option(VALUE opts);
double rb_int_fdiv_double(VALUE x, VALUE y);
@@ -1181,6 +1707,62 @@ VALUE rb_int_and(VALUE x, VALUE y);
VALUE rb_int_lshift(VALUE x, VALUE y);
VALUE rb_int_div(VALUE x, VALUE y);
VALUE rb_int_abs(VALUE num);
+VALUE rb_int_odd_p(VALUE num);
+int rb_int_positive_p(VALUE num);
+int rb_int_negative_p(VALUE num);
+VALUE rb_num_pow(VALUE x, VALUE y);
+VALUE rb_float_floor(VALUE x, int ndigits);
+
+
+static inline VALUE
+rb_num_compare_with_zero(VALUE num, ID mid)
+{
+ VALUE zero = INT2FIX(0);
+ VALUE r = rb_check_funcall(num, mid, 1, &zero);
+ if (r == Qundef) {
+ rb_cmperr(num, zero);
+ }
+ return r;
+}
+
+static inline int
+rb_num_positive_int_p(VALUE num)
+{
+ const ID mid = '>';
+
+ if (FIXNUM_P(num)) {
+ if (rb_method_basic_definition_p(rb_cInteger, mid))
+ return FIXNUM_POSITIVE_P(num);
+ }
+ else if (RB_TYPE_P(num, T_BIGNUM)) {
+ if (rb_method_basic_definition_p(rb_cInteger, mid))
+ return BIGNUM_POSITIVE_P(num);
+ }
+ return RTEST(rb_num_compare_with_zero(num, mid));
+}
+
+
+static inline int
+rb_num_negative_int_p(VALUE num)
+{
+ const ID mid = '<';
+
+ if (FIXNUM_P(num)) {
+ if (rb_method_basic_definition_p(rb_cInteger, mid))
+ return FIXNUM_NEGATIVE_P(num);
+ }
+ else if (RB_TYPE_P(num, T_BIGNUM)) {
+ if (rb_method_basic_definition_p(rb_cInteger, mid))
+ return BIGNUM_NEGATIVE_P(num);
+ }
+ return RTEST(rb_num_compare_with_zero(num, mid));
+}
+
+
+VALUE rb_float_abs(VALUE flt);
+VALUE rb_float_equal(VALUE x, VALUE y);
+VALUE rb_float_eql(VALUE x, VALUE y);
+VALUE rb_flo_div_flo(VALUE x, VALUE y);
#if USE_FLONUM
#define RUBY_BIT_ROTL(v, n) (((v) << (n)) | ((v) >> ((sizeof(v) * 8) - n)))
@@ -1201,7 +1783,7 @@ rb_float_flonum_value(VALUE v)
/* e: xx1... -> 011... */
/* xx0... -> 100... */
/* ^b63 */
- t.v = RUBY_BIT_ROTR((2 - b63) | (v & ~0x03), 3);
+ t.v = RUBY_BIT_ROTR((2 - b63) | (v & ~(VALUE)0x03), 3);
return t.d;
}
#endif
@@ -1264,6 +1846,10 @@ VALUE rb_class_search_ancestor(VALUE klass, VALUE super);
NORETURN(void rb_undefined_alloc(VALUE klass));
double rb_num_to_dbl(VALUE val);
VALUE rb_obj_dig(int argc, VALUE *argv, VALUE self, VALUE notfound);
+VALUE rb_immutable_obj_clone(int, VALUE *, VALUE);
+VALUE rb_obj_not_equal(VALUE obj1, VALUE obj2);
+VALUE rb_convert_type_with_id(VALUE,int,const char*,ID);
+VALUE rb_check_convert_type_with_id(VALUE,int,const char*,ID);
struct RBasicRaw {
VALUE flags;
@@ -1283,7 +1869,9 @@ struct RBasicRaw {
#endif
VALUE rb_parser_get_yydebug(VALUE);
VALUE rb_parser_set_yydebug(VALUE, VALUE);
+RUBY_SYMBOL_EXPORT_BEGIN
VALUE rb_parser_set_context(VALUE, const struct rb_block *, int);
+RUBY_SYMBOL_EXPORT_END
void *rb_parser_load_file(VALUE parser, VALUE name);
int rb_is_const_name(VALUE name);
int rb_is_class_name(VALUE name);
@@ -1309,12 +1897,15 @@ ID rb_id_attrget(ID id);
VALUE rb_proc_location(VALUE self);
st_index_t rb_hash_proc(st_index_t hash, VALUE proc);
int rb_block_arity(void);
+int rb_block_min_max_arity(int *max);
VALUE rb_func_proc_new(rb_block_call_func_t func, VALUE val);
-VALUE rb_func_lambda_new(rb_block_call_func_t func, VALUE val);
+VALUE rb_func_lambda_new(rb_block_call_func_t func, VALUE val, int min_argc, int max_argc);
+VALUE rb_block_to_s(VALUE self, const struct rb_block *block, const char *additional_info);
/* process.c */
#define RB_MAX_GROUPS (65536)
+struct waitpid_state;
struct rb_execarg {
union {
struct {
@@ -1343,6 +1934,8 @@ struct rb_execarg {
unsigned new_pgroup_flag : 1;
unsigned uid_given : 1;
unsigned gid_given : 1;
+ unsigned exception : 1;
+ struct waitpid_state *waitpid_state; /* for async process management */
rb_pid_t pgroup_pgid; /* asis(-1), new pgroup(0), specified pgroup (0<V). */
VALUE rlimit_limits; /* Qfalse or [[rtype, softlim, hardlim], ...] */
mode_t umask_mask;
@@ -1362,41 +1955,59 @@ struct rb_execarg {
* The beginning one is for /bin/sh used by exec_with_sh.
* The last one for terminating NULL used by execve.
* See rb_exec_fillarg() in process.c. */
-#define ARGVSTR2ARGC(argv_str) (RSTRING_LEN(argv_str) / sizeof(char *) - 2)
-#define ARGVSTR2ARGV(argv_str) ((char **)RSTRING_PTR(argv_str) + 1)
+#define ARGVSTR2ARGV(argv_str) ((char **)RB_IMEMO_TMPBUF_PTR(argv_str) + 1)
+
+static inline size_t
+ARGVSTR2ARGC(VALUE argv_str)
+{
+ size_t i = 0;
+ char *const *p = ARGVSTR2ARGV(argv_str);
+ while (p[i++])
+ ;
+ return i - 1;
+}
rb_pid_t rb_fork_ruby(int *status);
void rb_last_status_clear(void);
+/* range.c */
+#define RANGE_BEG(r) (RSTRUCT(r)->as.ary[0])
+#define RANGE_END(r) (RSTRUCT(r)->as.ary[1])
+#define RANGE_EXCL(r) (RSTRUCT(r)->as.ary[2])
+
/* rational.c */
+VALUE rb_rational_canonicalize(VALUE x);
+VALUE rb_rational_uminus(VALUE self);
VALUE rb_rational_plus(VALUE self, VALUE other);
+VALUE rb_rational_minus(VALUE self, VALUE other);
+VALUE rb_rational_mul(VALUE self, VALUE other);
+VALUE rb_rational_div(VALUE self, VALUE other);
VALUE rb_lcm(VALUE x, VALUE y);
VALUE rb_rational_reciprocal(VALUE x);
VALUE rb_cstr_to_rat(const char *, int);
+VALUE rb_rational_abs(VALUE self);
+VALUE rb_rational_cmp(VALUE self, VALUE other);
+VALUE rb_rational_pow(VALUE self, VALUE other);
+VALUE rb_rational_floor(VALUE self, int ndigits);
+VALUE rb_numeric_quo(VALUE x, VALUE y);
/* re.c */
VALUE rb_reg_compile(VALUE str, int options, const char *sourcefile, int sourceline);
VALUE rb_reg_check_preprocess(VALUE);
long rb_reg_search0(VALUE, VALUE, long, int, int);
+VALUE rb_reg_match_p(VALUE re, VALUE str, long pos);
+bool rb_reg_start_with_p(VALUE re, VALUE str);
void rb_backref_set_string(VALUE string, long pos, long len);
+void rb_match_unbusy(VALUE);
int rb_match_count(VALUE match);
int rb_match_nth_defined(int nth, VALUE match);
+VALUE rb_reg_new_ary(VALUE ary, int options);
/* signal.c */
extern int ruby_enable_coredump;
int rb_get_next_signal(void);
-int rb_sigaltstack_size(void);
-
-/* strftime.c */
-#ifdef RUBY_ENCODING_H
-VALUE rb_strftime_timespec(const char *format, size_t format_len, rb_encoding *enc,
- const struct vtm *vtm, struct timespec *ts, int gmt);
-VALUE rb_strftime(const char *format, size_t format_len, rb_encoding *enc,
- const struct vtm *vtm, VALUE timev, int gmt);
-#endif
/* string.c */
-void Init_frozen_strings(void);
VALUE rb_fstring(VALUE);
VALUE rb_fstring_new(const char *ptr, long len);
#define rb_fstring_lit(str) rb_fstring_new((str), rb_strlen_lit(str))
@@ -1431,12 +2042,15 @@ VALUE rb_id_quote_unprintable(ID);
char *rb_str_fill_terminator(VALUE str, const int termlen);
void rb_str_change_terminator_length(VALUE str, const int oldtermlen, const int termlen);
VALUE rb_str_locktmp_ensure(VALUE str, VALUE (*func)(VALUE), VALUE arg);
+VALUE rb_str_tmp_frozen_acquire(VALUE str);
+void rb_str_tmp_frozen_release(VALUE str, VALUE tmp);
VALUE rb_str_chomp_string(VALUE str, VALUE chomp);
#ifdef RUBY_ENCODING_H
VALUE rb_external_str_with_enc(VALUE str, rb_encoding *eenc);
VALUE rb_str_cat_conv_enc_opts(VALUE newstr, long ofs, const char *ptr, long len,
rb_encoding *from, int ecflags, VALUE ecopts);
VALUE rb_enc_str_scrub(rb_encoding *enc, VALUE str, VALUE repl);
+VALUE rb_str_initialize(VALUE str, const char *ptr, long len, rb_encoding *enc);
#endif
#define STR_NOEMBED FL_USER1
#define STR_SHARED FL_USER2 /* = ELTS_SHARED */
@@ -1447,6 +2061,9 @@ VALUE rb_enc_str_scrub(rb_encoding *enc, VALUE str, VALUE repl);
size_t rb_str_memsize(VALUE);
VALUE rb_sym_proc_call(ID mid, int argc, const VALUE *argv, VALUE passed_proc);
VALUE rb_sym_to_proc(VALUE sym);
+char *rb_str_to_cstr(VALUE str);
+VALUE rb_str_eql(VALUE str1, VALUE str2);
+VALUE rb_obj_as_string_result(VALUE str, VALUE obj);
/* symbol.c */
#ifdef RUBY_ENCODING_H
@@ -1471,20 +2088,31 @@ VALUE rb_sym_intern_ascii_cstr(const char *ptr);
rb_sym_intern_ascii_cstr(ptr); \
})
#endif
+VALUE rb_to_symbol_type(VALUE obj);
/* struct.c */
VALUE rb_struct_init_copy(VALUE copy, VALUE s);
VALUE rb_struct_lookup(VALUE s, VALUE idx);
+VALUE rb_struct_s_keyword_init(VALUE klass);
/* time.c */
struct timeval rb_time_timeval(VALUE);
/* thread.c */
+#define COVERAGE_INDEX_LINES 0
+#define COVERAGE_INDEX_BRANCHES 1
+#define COVERAGE_TARGET_LINES 1
+#define COVERAGE_TARGET_BRANCHES 2
+#define COVERAGE_TARGET_METHODS 4
+#define COVERAGE_TARGET_ONESHOT_LINES 8
+
VALUE rb_obj_is_mutex(VALUE obj);
VALUE rb_suppress_tracing(VALUE (*func)(VALUE), VALUE arg);
void rb_thread_execute_interrupts(VALUE th);
void rb_clear_trace_func(void);
VALUE rb_get_coverages(void);
+int rb_get_coverage_mode(void);
+VALUE rb_default_coverage(int);
VALUE rb_thread_shield_new(void);
VALUE rb_thread_shield_wait(VALUE self);
VALUE rb_thread_shield_release(VALUE self);
@@ -1493,27 +2121,38 @@ int rb_thread_to_be_killed(VALUE thread);
void rb_mutex_allow_trap(VALUE self, int val);
VALUE rb_uninterruptible(VALUE (*b_proc)(ANYARGS), VALUE data);
VALUE rb_mutex_owned_p(VALUE self);
-void ruby_kill(rb_pid_t pid, int sig);
-
-/* thread_pthread.c, thread_win32.c */
-void Init_native_thread(void);
-int rb_divert_reserved_fd(int fd);
/* transcode.c */
extern VALUE rb_cEncodingConverter;
+#ifdef RUBY_ENCODING_H
size_t rb_econv_memsize(rb_econv_t *);
+#endif
/* us_ascii.c */
+#ifdef RUBY_ENCODING_H
extern rb_encoding OnigEncodingUS_ASCII;
+#endif
/* util.c */
char *ruby_dtoa(double d_, int mode, int ndigits, int *decpt, int *sign, char **rve);
char *ruby_hdtoa(double d, const char *xdigs, int ndigits, int *decpt, int *sign, char **rve);
/* utf_8.c */
+#ifdef RUBY_ENCODING_H
extern rb_encoding OnigEncodingUTF_8;
+#endif
/* variable.c */
+#if USE_TRANSIENT_HEAP
+#define ROBJECT_TRANSIENT_FLAG FL_USER13
+#define ROBJ_TRANSIENT_P(obj) FL_TEST_RAW((obj), ROBJECT_TRANSIENT_FLAG)
+#define ROBJ_TRANSIENT_SET(obj) FL_SET_RAW((obj), ROBJECT_TRANSIENT_FLAG)
+#define ROBJ_TRANSIENT_UNSET(obj) FL_UNSET_RAW((obj), ROBJECT_TRANSIENT_FLAG)
+#else
+#define ROBJ_TRANSIENT_P(obj) 0
+#define ROBJ_TRANSIENT_SET(obj) ((void)0)
+#define ROBJ_TRANSIENT_UNSET(obj) ((void)0)
+#endif
void rb_gc_mark_global_tbl(void);
size_t rb_generic_ivar_memsize(VALUE);
VALUE rb_search_class_path(VALUE);
@@ -1521,9 +2160,10 @@ 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);
void rb_deprecate_constant(VALUE mod, const char *name);
-
-/* version.c */
-extern const char ruby_engine[];
+NORETURN(VALUE rb_mod_const_missing(VALUE,VALUE));
+rb_gvar_getter_t *rb_gvar_getter_function_of(const struct rb_global_entry *);
+rb_gvar_setter_t *rb_gvar_setter_function_of(const struct rb_global_entry *);
+bool rb_gvar_is_traced(const struct rb_global_entry *);
/* vm_insnhelper.h */
rb_serial_t rb_next_class_serial(void);
@@ -1535,18 +2175,17 @@ void Init_BareVM(void);
void Init_vm_objects(void);
PUREFUNC(VALUE rb_vm_top_self(void));
void rb_thread_recycle_stack_release(VALUE *);
+VALUE *rb_thread_recycle_stack(size_t);
void rb_vm_change_state(void);
void rb_vm_inc_const_missing_count(void);
-void rb_thread_mark(void *th);
const void **rb_vm_get_insns_address_table(void);
-VALUE rb_sourcefilename(void);
VALUE rb_source_location(int *pline);
-const char *rb_source_loc(int *pline);
-void rb_vm_pop_cfunc_frame(void);
+const char *rb_source_location_cstr(int *pline);
+MJIT_STATIC void rb_vm_pop_cfunc_frame(void);
int rb_vm_add_root_module(ID id, VALUE module);
void rb_vm_check_redefinition_by_prepend(VALUE klass);
VALUE rb_yield_refine_block(VALUE refinement, VALUE refinements);
-VALUE ruby_vm_sysstack_error_copy(void);
+MJIT_STATIC VALUE ruby_vm_special_exception_copy(VALUE);
PUREFUNC(st_table *rb_vm_fstring_table(void));
@@ -1560,12 +2199,18 @@ VALUE rb_check_block_call(VALUE, ID, int, const VALUE *, rb_block_call_func_t, V
typedef void rb_check_funcall_hook(int, VALUE, ID, int, const VALUE *, VALUE);
VALUE rb_check_funcall_with_hook(VALUE recv, ID mid, int argc, const VALUE *argv,
rb_check_funcall_hook *hook, VALUE arg);
+const char *rb_type_str(enum ruby_value_type type);
VALUE rb_check_funcall_default(VALUE, ID, int, const VALUE *, VALUE);
-VALUE rb_catch_protect(VALUE t, rb_block_call_func *func, VALUE data, int *stateptr);
VALUE rb_yield_1(VALUE val);
+VALUE rb_yield_force_blockarg(VALUE values);
+VALUE rb_lambda_call(VALUE obj, ID mid, int argc, const VALUE *argv,
+ rb_block_call_func_t bl_proc, int min_argc, int max_argc,
+ VALUE data2);
/* vm_insnhelper.c */
VALUE rb_equal_opt(VALUE obj1, VALUE obj2);
+VALUE rb_eql_opt(VALUE obj1, VALUE obj2);
+void Init_vm_stack_canary(void);
/* vm_method.c */
void Init_eval_method(void);
@@ -1584,8 +2229,7 @@ void rb_backtrace_print_as_bugreport(void);
int rb_backtrace_p(VALUE obj);
VALUE rb_backtrace_to_str_ary(VALUE obj);
VALUE rb_backtrace_to_location_ary(VALUE obj);
-void rb_backtrace_print_to(VALUE output);
-VALUE rb_vm_backtrace_object(void);
+void rb_backtrace_each(VALUE (*iter)(VALUE recv, VALUE str), VALUE output);
RUBY_SYMBOL_EXPORT_BEGIN
const char *rb_objspace_data_type_name(VALUE obj);
@@ -1593,6 +2237,11 @@ const char *rb_objspace_data_type_name(VALUE obj);
/* Temporary. This API will be removed (renamed). */
VALUE rb_thread_io_blocking_region(rb_blocking_function_t *func, void *data1, int fd);
+/* array.c (export) */
+void rb_ary_detransient(VALUE a);
+VALUE *rb_ary_ptr_use_start(VALUE ary);
+void rb_ary_ptr_use_end(VALUE ary);
+
/* bignum.c (export) */
VALUE rb_big_mul_normal(VALUE x, VALUE y);
VALUE rb_big_mul_balance(VALUE x, VALUE y);
@@ -1611,6 +2260,19 @@ VALUE rb_big_divrem_gmp(VALUE x, VALUE y);
VALUE rb_big2str_gmp(VALUE x, int base);
VALUE rb_str2big_gmp(VALUE arg, int base, int badcheck);
#endif
+enum rb_int_parse_flags {
+ RB_INT_PARSE_SIGN = 0x01,
+ RB_INT_PARSE_UNDERSCORE = 0x02,
+ RB_INT_PARSE_PREFIX = 0x04,
+ RB_INT_PARSE_ALL = 0x07,
+ RB_INT_PARSE_DEFAULT = 0x07
+};
+VALUE rb_int_parse_cstr(const char *str, ssize_t len, char **endp, size_t *ndigits, int base, int flags);
+
+/* enumerator.c (export) */
+VALUE rb_arith_seq_new(VALUE obj, VALUE meth, int argc, VALUE const *argv,
+ rb_enumerator_size_func *size_fn,
+ VALUE beg, VALUE end, VALUE step, int excl);
/* error.c (export) */
int rb_bug_reporter_add(void (*func)(FILE *, void *), void *data);
@@ -1621,8 +2283,15 @@ NORETURN(void rb_unexpected_type(VALUE,int));
((t) == RUBY_T_DATA && RTYPEDDATA_P(v)) ? \
rb_unexpected_type((VALUE)(v), (t)) : (void)0)
+static inline int
+rb_typeddata_is_instance_of_inline(VALUE obj, const rb_data_type_t *data_type)
+{
+ return RB_TYPE_P(obj, T_DATA) && RTYPEDDATA_P(obj) && (RTYPEDDATA_TYPE(obj) == data_type);
+}
+#define rb_typeddata_is_instance_of rb_typeddata_is_instance_of_inline
+
/* file.c (export) */
-#ifdef HAVE_READLINK
+#if defined HAVE_READLINK && defined RUBY_ENCODING_H
VALUE rb_readlink(VALUE path, rb_encoding *enc);
#endif
#ifdef __APPLE__
@@ -1641,12 +2310,17 @@ void rb_write_error_str(VALUE mesg);
/* numeric.c (export) */
VALUE rb_int_positive_pow(long x, unsigned long y);
+#ifdef HAVE_PWD_H
+VALUE rb_getlogin(void);
+VALUE rb_getpwdirnam_for_login(VALUE login); /* read as: "get pwd db home dir by username for login" */
+VALUE rb_getpwdiruid(void); /* read as: "get pwd db home dir for getuid()" */
+#endif
+
/* process.c (export) */
int rb_exec_async_signal_safe(const struct rb_execarg *e, char *errmsg, size_t errmsg_buflen);
rb_pid_t rb_fork_async_signal_safe(int *status, int (*chfunc)(void*, char *, size_t), void *charg, VALUE fds, char *errmsg, size_t errmsg_buflen);
-VALUE rb_execarg_new(int argc, const VALUE *argv, int accept_shell);
+VALUE rb_execarg_new(int argc, const VALUE *argv, int accept_shell, int allow_exc_opt);
struct rb_execarg *rb_execarg_get(VALUE execarg_obj); /* dangerous. needs GC guard. */
-VALUE rb_execarg_init(int argc, const VALUE *argv, int accept_shell, VALUE execarg_obj);
int rb_execarg_addopt(VALUE execarg_obj, VALUE key, VALUE val);
void rb_execarg_parent_start(VALUE execarg_obj);
void rb_execarg_parent_end(VALUE execarg_obj);
@@ -1655,20 +2329,29 @@ VALUE rb_execarg_extract_options(VALUE execarg_obj, VALUE opthash);
void rb_execarg_setenv(VALUE execarg_obj, VALUE env);
/* rational.c (export) */
+VALUE rb_gcd(VALUE x, VALUE y);
VALUE rb_gcd_normal(VALUE self, VALUE other);
#if defined(HAVE_LIBGMP) && defined(HAVE_GMP_H)
VALUE rb_gcd_gmp(VALUE x, VALUE y);
#endif
+/* signal.c (export) */
+int rb_grantpt(int fd);
+
/* string.c (export) */
#ifdef RUBY_ENCODING_H
/* internal use */
VALUE rb_setup_fake_str(struct RString *fake_str, const char *name, long len, rb_encoding *enc);
#endif
+VALUE rb_str_upto_each(VALUE, VALUE, int, int (*each)(VALUE, VALUE), VALUE);
+VALUE rb_str_upto_endless_each(VALUE, int (*each)(VALUE, VALUE), VALUE);
/* thread.c (export) */
int ruby_thread_has_gvl_p(void); /* for ext/fiddle/closure.c */
+/* time.c (export) */
+void ruby_reset_leap_second_info(void);
+
/* util.c (export) */
extern const signed char ruby_digit36_to_number_table[];
extern const char ruby_hexdigits[];
@@ -1698,6 +2381,9 @@ VALUE rb_imemo_new_debug(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VAL
VALUE rb_imemo_new(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0);
#endif
+/* random.c */
+int ruby_fill_random_bytes(void *, size_t, int);
+
RUBY_SYMBOL_EXPORT_END
#define RUBY_DTRACE_CREATE_HOOK(name, arg) \
@@ -1706,7 +2392,7 @@ RUBY_SYMBOL_EXPORT_END
do { \
if (UNLIKELY(RUBY_DTRACE_##name##_ENABLED())) { \
int dtrace_line; \
- const char *dtrace_file = rb_source_loc(&dtrace_line); \
+ const char *dtrace_file = rb_source_location_cstr(&dtrace_line); \
if (!dtrace_file) dtrace_file = ""; \
RUBY_DTRACE_##name(arg, dtrace_file, dtrace_line); \
} \
@@ -1730,6 +2416,65 @@ rb_obj_builtin_type(VALUE obj)
}
#endif
+/* A macro for defining a flexible array, like: VALUE ary[FLEX_ARY_LEN]; */
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define FLEX_ARY_LEN /* VALUE ary[]; */
+#elif defined(__GNUC__) && !defined(__STRICT_ANSI__)
+# define FLEX_ARY_LEN 0 /* VALUE ary[0]; */
+#else
+# define FLEX_ARY_LEN 1 /* VALUE ary[1]; */
+#endif
+
+/*
+ * For declaring bitfields out of non-unsigned int types:
+ * struct date {
+ * BITFIELD(enum months, month, 4);
+ * ...
+ * };
+ */
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define BITFIELD(type, name, size) type name : size
+#else
+# define BITFIELD(type, name, size) unsigned int name : size
+#endif
+
+#if defined(_MSC_VER)
+# define COMPILER_WARNING_PUSH __pragma(warning(push))
+# define COMPILER_WARNING_POP __pragma(warning(pop))
+# define COMPILER_WARNING_ERROR(flag) __pragma(warning(error: flag)))
+# define COMPILER_WARNING_IGNORED(flag) __pragma(warning(suppress: flag)))
+
+#elif defined(__clang__) /* clang 2.6 already had this feature */
+# define COMPILER_WARNING_PUSH _Pragma("clang diagnostic push")
+# define COMPILER_WARNING_POP _Pragma("clang diagnostic pop")
+# define COMPILER_WARNING_SPECIFIER(kind, msg) \
+ clang diagnostic kind # msg
+# define COMPILER_WARNING_ERROR(flag) \
+ COMPILER_WARNING_PRAGMA(COMPILER_WARNING_SPECIFIER(error, flag))
+# define COMPILER_WARNING_IGNORED(flag) \
+ COMPILER_WARNING_PRAGMA(COMPILER_WARNING_SPECIFIER(ignored, flag))
+
+#elif GCC_VERSION_SINCE(4, 2, 0)
+/* https://gcc.gnu.org/onlinedocs/gcc-4.2.0/gcc/Diagnostic-Pragmas.html */
+# define COMPILER_WARNING_PUSH _Pragma("GCC diagnostic push")
+# define COMPILER_WARNING_POP _Pragma("GCC diagnostic pop")
+# define COMPILER_WARNING_SPECIFIER(kind, msg) \
+ GCC diagnostic kind # msg
+# define COMPILER_WARNING_ERROR(flag) \
+ COMPILER_WARNING_PRAGMA(COMPILER_WARNING_SPECIFIER(error, flag))
+# define COMPILER_WARNING_IGNORED(flag) \
+ COMPILER_WARNING_PRAGMA(COMPILER_WARNING_SPECIFIER(ignored, flag))
+
+#else /* other compilers to follow? */
+# define COMPILER_WARNING_PUSH /* nop */
+# define COMPILER_WARNING_POP /* nop */
+# define COMPILER_WARNING_ERROR(flag) /* nop */
+# define COMPILER_WARNING_IGNORED(flag) /* nop */
+#endif
+
+#define COMPILER_WARNING_PRAGMA(str) COMPILER_WARNING_PRAGMA_(str)
+#define COMPILER_WARNING_PRAGMA_(str) _Pragma(#str)
+
#if defined(__cplusplus)
#if 0
{ /* satisfy cc-mode */
diff --git a/io.c b/io.c
index 1f00f6c4fa..3f7dbde50d 100644
--- a/io.c
+++ b/io.c
@@ -11,15 +11,17 @@
**********************************************************************/
-#include "internal.h"
+#include "ruby/encoding.h"
#include "ruby/io.h"
#include "ruby/thread.h"
+#include "internal.h"
#include "dln.h"
#include "encindex.h"
#include "id.h"
#include <ctype.h>
#include <errno.h>
#include "ruby_atomic.h"
+#include "ccan/list/list.h"
#undef free
#define free(x) xfree(x)
@@ -120,13 +122,6 @@
off_t __syscall(quad_t number, ...);
#endif
-#ifdef __native_client__
-# undef F_GETFD
-# ifdef NACL_NEWLIB
-# undef HAVE_IOCTL
-# endif
-#endif
-
#define IO_RBUF_CAPA_MIN 8192
#define IO_CBUF_CAPA_MIN (128*1024)
#define IO_RBUF_CAPA_FOR(fptr) (NEED_READCONV(fptr) ? IO_CBUF_CAPA_MIN : IO_RBUF_CAPA_MIN)
@@ -136,6 +131,17 @@ off_t __syscall(quad_t number, ...);
#ifdef _WIN32
#undef open
#define open rb_w32_uopen
+#undef rename
+#define rename(f, t) rb_w32_urename((f), (t))
+#endif
+
+#if defined(_WIN32)
+# define RUBY_PIPE_NONBLOCK_DEFAULT (0)
+#elif defined(O_NONBLOCK)
+ /* disabled for [Bug #15356] (Rack::Deflater + rails) failure: */
+# define RUBY_PIPE_NONBLOCK_DEFAULT (0)
+#else /* any platforms where O_NONBLOCK does not exist? */
+# define RUBY_PIPE_NONBLOCK_DEFAULT (0)
#endif
VALUE rb_cIO;
@@ -179,7 +185,7 @@ struct argf {
long last_lineno; /* $. */
long lineno;
VALUE argv;
- char *inplace;
+ VALUE inplace;
struct rb_io_enc_t encs;
int8_t init_p, next_p, binmode;
};
@@ -188,14 +194,22 @@ static rb_atomic_t max_file_descriptor = NOFILE;
void
rb_update_max_fd(int fd)
{
- struct stat buf;
rb_atomic_t afd = (rb_atomic_t)fd;
rb_atomic_t max_fd = max_file_descriptor;
+ int err;
if (afd <= max_fd)
return;
- if (fstat(fd, &buf) != 0 && errno == EBADF) {
+#if defined(HAVE_FCNTL) && defined(F_GETFL)
+ err = fcntl(fd, F_GETFL) == -1;
+#else
+ {
+ struct stat buf;
+ err = fstat(fd, &buf) != 0;
+ }
+#endif
+ if (err && errno == EBADF) {
rb_bug("rb_update_max_fd: invalid fd (%d) given.", fd);
}
@@ -220,7 +234,7 @@ rb_maygvl_fd_fix_cloexec(int fd)
flags2 = flags | FD_CLOEXEC; /* Set CLOEXEC for non-standard file descriptors: 3, 4, 5, ... */
if (flags != flags2) {
ret = fcntl(fd, F_SETFD, flags2);
- if (ret == -1) {
+ if (ret != 0) {
rb_bug("rb_maygvl_fd_fix_cloexec: fcntl(%d, F_SETFD, %d) failed: %s", fd, flags2, strerror(errno));
}
}
@@ -264,7 +278,7 @@ rb_cloexec_open(const char *pathname, int flags, mode_t mode)
flags |= O_NOINHERIT;
#endif
ret = open(pathname, flags, mode);
- if (ret == -1) return -1;
+ if (ret < 0) return ret;
if (ret <= 2 || o_cloexec_state == 0) {
rb_maygvl_fd_fix_cloexec(ret);
}
@@ -313,12 +327,30 @@ rb_cloexec_dup2(int oldfd, int newfd)
#else
ret = dup2(oldfd, newfd);
#endif
- if (ret == -1) return -1;
+ if (ret < 0) return ret;
}
rb_maygvl_fd_fix_cloexec(ret);
return ret;
}
+static int
+rb_fd_set_nonblock(int fd)
+{
+#ifdef _WIN32
+ return rb_w32_set_nonblock(fd);
+#elif defined(F_GETFL)
+ int oflags = fcntl(fd, F_GETFL);
+
+ if (oflags == -1)
+ return -1;
+ if (oflags & O_NONBLOCK)
+ return 0;
+ oflags |= O_NONBLOCK;
+ return fcntl(fd, F_SETFL, oflags);
+#endif
+ return 0;
+}
+
int
rb_cloexec_pipe(int fildes[2])
{
@@ -327,7 +359,7 @@ rb_cloexec_pipe(int fildes[2])
#if defined(HAVE_PIPE2)
static int try_pipe2 = 1;
if (try_pipe2) {
- ret = pipe2(fildes, O_CLOEXEC);
+ ret = pipe2(fildes, O_CLOEXEC | RUBY_PIPE_NONBLOCK_DEFAULT);
if (ret != -1)
return ret;
/* pipe2 is available since Linux 2.6.27, glibc 2.9. */
@@ -342,7 +374,7 @@ rb_cloexec_pipe(int fildes[2])
#else
ret = pipe(fildes);
#endif
- if (ret == -1) return -1;
+ if (ret < 0) return ret;
#ifdef __CYGWIN__
if (ret == 0 && fildes[1] == -1) {
close(fildes[0]);
@@ -353,6 +385,10 @@ rb_cloexec_pipe(int fildes[2])
#endif
rb_maygvl_fd_fix_cloexec(fildes[0]);
rb_maygvl_fd_fix_cloexec(fildes[1]);
+ if (RUBY_PIPE_NONBLOCK_DEFAULT) {
+ rb_fd_set_nonblock(fildes[0]);
+ rb_fd_set_nonblock(fildes[1]);
+ }
return ret;
}
@@ -385,7 +421,7 @@ rb_cloexec_fcntl_dupfd(int fd, int minfd)
ret = fcntl(fd, F_DUPFD, minfd);
#elif defined(HAVE_DUP)
ret = dup(fd);
- if (ret != -1 && ret < minfd) {
+ if (ret >= 0 && ret < minfd) {
const int prev_fd = ret;
ret = rb_cloexec_fcntl_dupfd(fd, minfd);
close(prev_fd);
@@ -394,7 +430,7 @@ rb_cloexec_fcntl_dupfd(int fd, int minfd)
#else
# error "dup() or fcntl(F_DUPFD) must be supported."
#endif
- if (ret == -1) return -1;
+ if (ret < 0) return ret;
rb_maygvl_fd_fix_cloexec(ret);
return ret;
}
@@ -424,7 +460,7 @@ rb_cloexec_fcntl_dupfd(int fd, int minfd)
if (!READ_DATA_PENDING(fptr)) {\
WAIT_FD_IN_WIN32(fptr);\
rb_io_check_closed(fptr);\
- }\
+ }\
} while(0)
#ifndef S_ISSOCK
@@ -614,6 +650,15 @@ is_socket(int fd, VALUE path)
static const char closed_stream[] = "closed stream";
+static void
+io_fd_check_closed(int fd)
+{
+ if (fd < 0) {
+ rb_thread_check_ints(); /* check for ruby_error_stream_closed */
+ rb_raise(rb_eIOError, closed_stream);
+ }
+}
+
void
rb_eof_error(void)
{
@@ -639,9 +684,7 @@ void
rb_io_check_closed(rb_io_t *fptr)
{
rb_io_check_initialized(fptr);
- if (fptr->fd < 0) {
- rb_raise(rb_eIOError, closed_stream);
- }
+ io_fd_check_closed(fptr->fd);
}
static rb_io_t *
@@ -655,13 +698,13 @@ rb_io_get_fptr(VALUE io)
VALUE
rb_io_get_io(VALUE io)
{
- return rb_convert_type(io, T_FILE, "IO", "to_io");
+ return rb_convert_type_with_id(io, T_FILE, "IO", idTo_io);
}
VALUE
rb_io_check_io(VALUE io)
{
- return rb_check_convert_type(io, T_FILE, "IO", "to_io");
+ return rb_check_convert_type_with_id(io, T_FILE, "IO", idTo_io);
}
VALUE
@@ -693,10 +736,10 @@ rb_io_set_write_io(VALUE io, VALUE w)
/*
* call-seq:
- * IO.try_convert(obj) -> io or nil
+ * IO.try_convert(obj) -> io or nil
*
* Try to convert <i>obj</i> into an IO, using to_io method.
- * Returns converted IO or nil if <i>obj</i> cannot be converted
+ * Returns converted IO or +nil+ if <i>obj</i> cannot be converted
* for any reason.
*
* IO.try_convert(STDOUT) #=> STDOUT
@@ -917,6 +960,7 @@ io_alloc(VALUE klass)
struct io_internal_read_struct {
int fd;
+ int nonblock;
void *buf;
size_t capa;
};
@@ -935,25 +979,44 @@ struct io_internal_writev_struct {
};
#endif
+static int nogvl_wait_for_single_fd(int fd, short events);
static VALUE
internal_read_func(void *ptr)
{
struct io_internal_read_struct *iis = ptr;
- return read(iis->fd, iis->buf, iis->capa);
+ ssize_t r;
+retry:
+ r = read(iis->fd, iis->buf, iis->capa);
+ if (r < 0 && !iis->nonblock) {
+ int e = errno;
+ if (e == EAGAIN || e == EWOULDBLOCK) {
+ if (nogvl_wait_for_single_fd(iis->fd, RB_WAITFD_IN) != -1) {
+ goto retry;
+ }
+ errno = e;
+ }
+ }
+ return r;
}
+#if defined __APPLE__
+# define do_write_retry(code) do {ret = code;} while (ret == -1 && errno == EPROTOTYPE)
+#else
+# define do_write_retry(code) ret = code
+#endif
static VALUE
internal_write_func(void *ptr)
{
struct io_internal_write_struct *iis = ptr;
- return write(iis->fd, iis->buf, iis->capa);
+ ssize_t ret;
+ do_write_retry(write(iis->fd, iis->buf, iis->capa));
+ return (VALUE)ret;
}
static void*
internal_write_func2(void *ptr)
{
- struct io_internal_write_struct *iis = ptr;
- return (void*)(intptr_t)write(iis->fd, iis->buf, iis->capa);
+ return (void*)internal_write_func(ptr);
}
#ifdef HAVE_WRITEV
@@ -961,7 +1024,9 @@ static VALUE
internal_writev_func(void *ptr)
{
struct io_internal_writev_struct *iis = ptr;
- return writev(iis->fd, iis->iov, iis->iovcnt);
+ ssize_t ret;
+ do_write_retry(writev(iis->fd, iis->iov, iis->iovcnt));
+ return (VALUE)ret;
}
#endif
@@ -969,7 +1034,9 @@ static ssize_t
rb_read_internal(int fd, void *buf, size_t count)
{
struct io_internal_read_struct iis;
+
iis.fd = fd;
+ iis.nonblock = 0;
iis.buf = buf;
iis.capa = count;
@@ -1091,7 +1158,6 @@ io_fflush(rb_io_t *fptr)
rb_io_check_closed(fptr);
if (fptr->wbuf.len == 0)
return 0;
- rb_io_check_closed(fptr);
while (fptr->wbuf.len > 0 && io_flush_buffer(fptr) != 0) {
if (!rb_io_wait_writable(fptr->fd))
return -1;
@@ -1103,9 +1169,7 @@ io_fflush(rb_io_t *fptr)
int
rb_io_wait_readable(int f)
{
- if (f < 0) {
- rb_raise(rb_eIOError, closed_stream);
- }
+ io_fd_check_closed(f);
switch (errno) {
case EINTR:
#if defined(ERESTART)
@@ -1129,9 +1193,7 @@ rb_io_wait_readable(int f)
int
rb_io_wait_writable(int f)
{
- if (f < 0) {
- rb_raise(rb_eIOError, closed_stream);
- }
+ io_fd_check_closed(f);
switch (errno) {
case EINTR:
#if defined(ERESTART)
@@ -1248,8 +1310,8 @@ io_binwrite_string(VALUE arg)
r = rb_writev_internal(fptr->fd, iov, 2);
- if (r == -1)
- return -1;
+ if (r < 0)
+ return r;
if (fptr->wbuf.len <= r) {
r -= fptr->wbuf.len;
@@ -1362,6 +1424,11 @@ io_binwrite(VALUE str, const char *ptr, long len, rb_io_t *fptr, int nosync)
# define MODE_BTMODE(a,b,c) ((fmode & FMODE_BINMODE) ? (b) : \
(fmode & FMODE_TEXTMODE) ? (c) : (a))
+
+#define MODE_BTXMODE(a, b, c, d, e, f) ((fmode & FMODE_EXCL) ? \
+ MODE_BTMODE(d, e, f) : \
+ MODE_BTMODE(a, b, c))
+
static VALUE
do_writeconv(VALUE str, rb_io_t *fptr, int *converted)
{
@@ -1423,6 +1490,9 @@ static long
io_fwrite(VALUE str, rb_io_t *fptr, int nosync)
{
int converted = 0;
+ VALUE tmp;
+ long n, len;
+ const char *ptr;
#ifdef _WIN32
if (fptr->mode & FMODE_TTY) {
long len = rb_w32_write_console(str, fptr->fd);
@@ -1432,11 +1502,13 @@ io_fwrite(VALUE str, rb_io_t *fptr, int nosync)
str = do_writeconv(str, fptr, &converted);
if (converted)
OBJ_FREEZE(str);
- else
- str = rb_str_new_frozen(str);
- return io_binwrite(str, RSTRING_PTR(str), RSTRING_LEN(str),
- fptr, nosync);
+ tmp = rb_str_tmp_frozen_acquire(str);
+ RSTRING_GETMEM(tmp, ptr, len);
+ n = io_binwrite(tmp, ptr, len, fptr, nosync);
+ rb_str_tmp_frozen_release(str, tmp);
+
+ return n;
}
ssize_t
@@ -1470,21 +1542,210 @@ io_write(VALUE io, VALUE str, int nosync)
rb_io_check_writable(fptr);
n = io_fwrite(str, fptr, nosync);
- if (n == -1L) rb_sys_fail_path(fptr->pathv);
+ if (n < 0L) rb_sys_fail_path(fptr->pathv);
return LONG2FIX(n);
}
+#ifdef HAVE_WRITEV
+struct binwritev_arg {
+ rb_io_t *fptr;
+ const struct iovec *iov;
+ int iovcnt;
+};
+
+static VALUE
+call_writev_internal(VALUE arg)
+{
+ struct binwritev_arg *p = (struct binwritev_arg *)arg;
+ return rb_writev_internal(p->fptr->fd, p->iov, p->iovcnt);
+}
+
+static long
+io_binwritev(struct iovec *iov, int iovcnt, rb_io_t *fptr)
+{
+ int i;
+ long r, total = 0, written_len = 0;
+
+ /* don't write anything if current thread has a pending interrupt. */
+ rb_thread_check_ints();
+
+ if (iovcnt == 0) return 0;
+ for (i = 1; i < iovcnt; i++) total += iov[i].iov_len;
+
+ if (fptr->wbuf.ptr == NULL && !(fptr->mode & FMODE_SYNC)) {
+ fptr->wbuf.off = 0;
+ fptr->wbuf.len = 0;
+ fptr->wbuf.capa = IO_WBUF_CAPA_MIN;
+ fptr->wbuf.ptr = ALLOC_N(char, fptr->wbuf.capa);
+ fptr->write_lock = rb_mutex_new();
+ rb_mutex_allow_trap(fptr->write_lock, 1);
+ }
+
+ if (fptr->wbuf.ptr && fptr->wbuf.len) {
+ long offset = fptr->wbuf.off + fptr->wbuf.len;
+ if (offset + total <= fptr->wbuf.capa) {
+ for (i = 1; i < iovcnt; i++) {
+ memcpy(fptr->wbuf.ptr+offset, iov[i].iov_base, iov[i].iov_len);
+ offset += iov[i].iov_len;
+ }
+ fptr->wbuf.len += total;
+ return total;
+ }
+ else {
+ iov[0].iov_base = fptr->wbuf.ptr + fptr->wbuf.off;
+ iov[0].iov_len = fptr->wbuf.len;
+ }
+ }
+ else {
+ iov++;
+ if (!--iovcnt) return 0;
+ }
+
+ retry:
+ if (fptr->write_lock) {
+ struct binwritev_arg arg;
+ arg.fptr = fptr;
+ arg.iov = iov;
+ arg.iovcnt = iovcnt;
+ r = rb_mutex_synchronize(fptr->write_lock, call_writev_internal, (VALUE)&arg);
+ }
+ else {
+ r = rb_writev_internal(fptr->fd, iov, iovcnt);
+ }
+
+ if (r >= 0) {
+ written_len += r;
+ if (fptr->wbuf.ptr && fptr->wbuf.len) {
+ if (written_len < fptr->wbuf.len) {
+ fptr->wbuf.off += r;
+ fptr->wbuf.len -= r;
+ }
+ else {
+ written_len -= fptr->wbuf.len;
+ fptr->wbuf.off = 0;
+ fptr->wbuf.len = 0;
+ }
+ }
+ if (written_len == total) return total;
+
+ while (r >= (ssize_t)iov->iov_len) {
+ /* iovcnt > 0 */
+ r -= iov->iov_len;
+ iov->iov_len = 0;
+ iov++;
+ if (!--iovcnt) return total;
+ /* defensive check: written_len should == total */
+ }
+ iov->iov_base = (char *)iov->iov_base + r;
+ iov->iov_len -= r;
+
+ errno = EAGAIN;
+ }
+ if (rb_io_wait_writable(fptr->fd)) {
+ rb_io_check_closed(fptr);
+ goto retry;
+ }
+
+ return -1L;
+}
+
+static long
+io_fwritev(int argc, VALUE *argv, rb_io_t *fptr)
+{
+ int i, converted, iovcnt = argc + 1;
+ long n;
+ VALUE v1, v2, str, tmp, *tmp_array;
+ struct iovec *iov;
+
+ iov = ALLOCV_N(struct iovec, v1, iovcnt);
+ tmp_array = ALLOCV_N(VALUE, v2, argc);
+
+ for (i = 0; i < argc; i++) {
+ str = rb_obj_as_string(argv[i]);
+ converted = 0;
+ str = do_writeconv(str, fptr, &converted);
+ if (converted)
+ OBJ_FREEZE(str);
+
+ tmp = rb_str_tmp_frozen_acquire(str);
+ tmp_array[i] = tmp;
+ /* iov[0] is reserved for buffer of fptr */
+ iov[i+1].iov_base = RSTRING_PTR(tmp);
+ iov[i+1].iov_len = RSTRING_LEN(tmp);
+ }
+
+ n = io_binwritev(iov, iovcnt, fptr);
+ if (v1) ALLOCV_END(v1);
+
+ for (i = 0; i < argc; i++) {
+ rb_str_tmp_frozen_release(argv[i], tmp_array[i]);
+ }
+
+ if (v2) ALLOCV_END(v2);
+
+ return n;
+}
+
+static int
+iovcnt_ok(int iovcnt)
+{
+#ifdef IOV_MAX
+ return iovcnt < IOV_MAX;
+#else /* GNU/Hurd has writev, but no IOV_MAX */
+ return 1;
+#endif
+}
+#endif /* HAVE_WRITEV */
+
+static VALUE
+io_writev(int argc, VALUE *argv, VALUE io)
+{
+ rb_io_t *fptr;
+ long n;
+ VALUE tmp, total = INT2FIX(0);
+ int i, cnt = 1;
+
+ io = GetWriteIO(io);
+ tmp = rb_io_check_io(io);
+ if (NIL_P(tmp)) {
+ /* port is not IO, call write method for it. */
+ return rb_funcallv(io, id_write, argc, argv);
+ }
+ io = tmp;
+
+ GetOpenFile(io, fptr);
+ rb_io_check_writable(fptr);
+
+ for (i = 0; i < argc; i += cnt) {
+#ifdef HAVE_WRITEV
+ if ((fptr->mode & (FMODE_SYNC|FMODE_TTY)) && iovcnt_ok(cnt = argc - i)) {
+ n = io_fwritev(cnt, &argv[i], fptr);
+ }
+ else
+#endif
+ {
+ cnt = 1;
+ /* sync at last item */
+ n = io_fwrite(rb_obj_as_string(argv[i]), fptr, (i < argc-1));
+ }
+ if (n < 0L) rb_sys_fail_path(fptr->pathv);
+ total = rb_fix_plus(LONG2FIX(n), total);
+ }
+
+ return total;
+}
+
/*
* call-seq:
- * ios.write(string) -> integer
+ * ios.write(string, ...) -> integer
*
- * Writes the given string to <em>ios</em>. The stream must be opened
- * for writing. If the argument is not a string, it will be converted
+ * Writes the given strings to <em>ios</em>. The stream must be opened
+ * for writing. Arguments that are not a string will be converted
* to a string using <code>to_s</code>. Returns the number of bytes
- * written.
+ * written in total.
*
- * count = $stdout.write("This is a test\n")
+ * count = $stdout.write("This is", " a test\n")
* puts "That was #{count} bytes of data"
*
* <em>produces:</em>
@@ -1494,9 +1755,15 @@ io_write(VALUE io, VALUE str, int nosync)
*/
static VALUE
-io_write_m(VALUE io, VALUE str)
+io_write_m(int argc, VALUE *argv, VALUE io)
{
- return io_write(io, str, 0);
+ if (argc != 1) {
+ return io_writev(argc, argv, io);
+ }
+ else {
+ VALUE str = argv[0];
+ return io_write(io, str, 0);
+ }
}
VALUE
@@ -1505,6 +1772,23 @@ rb_io_write(VALUE io, VALUE str)
return rb_funcallv(io, id_write, 1, &str);
}
+static VALUE
+rb_io_writev(VALUE io, int argc, VALUE *argv)
+{
+ if (argc > 1 && rb_obj_method_arity(io, id_write) == 1) {
+ if (io != rb_stderr && RTEST(ruby_verbose)) {
+ VALUE klass = CLASS_OF(io);
+ char sep = FL_TEST(klass, FL_SINGLETON) ? (klass = io, '.') : '#';
+ rb_warning("%+"PRIsVALUE"%c""write is outdated interface"
+ " which accepts just one argument",
+ klass, sep);
+ }
+ do rb_io_write(io, *argv++); while (--argc);
+ return argv[0]; /* unused right now */
+ }
+ return rb_funcallv(io, id_write, argc, argv);
+}
+
/*
* call-seq:
* ios << obj -> ios
@@ -1649,7 +1933,7 @@ interpret_seek_whence(VALUE vwhence)
/*
* call-seq:
- * ios.seek(amount, whence=IO::SEEK_SET) -> 0
+ * ios.seek(amount, whence=IO::SEEK_SET) -> 0
*
* Seeks to a given offset <i>anInteger</i> in the stream according to
* the value of <i>whence</i>:
@@ -1745,6 +2029,16 @@ rb_io_rewind(VALUE io)
}
static int
+fptr_wait_readable(rb_io_t *fptr)
+{
+ int ret = rb_io_wait_readable(fptr->fd);
+
+ if (ret)
+ rb_io_check_closed(fptr);
+ return ret;
+}
+
+static int
io_fillbuf(rb_io_t *fptr)
{
ssize_t r;
@@ -1764,7 +2058,7 @@ io_fillbuf(rb_io_t *fptr)
r = rb_read_internal(fptr->fd, fptr->rbuf.ptr, fptr->rbuf.capa);
}
if (r < 0) {
- if (rb_io_wait_readable(fptr->fd))
+ if (fptr_wait_readable(fptr))
goto retry;
{
int e = errno;
@@ -1775,6 +2069,7 @@ io_fillbuf(rb_io_t *fptr)
rb_syserr_fail_path(e, path);
}
}
+ if (r > 0) rb_io_check_closed(fptr);
fptr->rbuf.off = 0;
fptr->rbuf.len = (int)r; /* r should be <= rbuf_capa */
if (r == 0)
@@ -1875,8 +2170,6 @@ rb_io_sync(VALUE io)
*
* f = File.new("testfile")
* f.sync = true
- *
- * <em>(produces no output)</em>
*/
static VALUE
@@ -2073,7 +2366,7 @@ rb_io_inspect(VALUE obj)
/*
* call-seq:
- * ios.to_io -> ios
+ * ios.to_io -> ios
*
* Returns <em>ios</em>.
*/
@@ -2112,7 +2405,7 @@ io_bufread(char *ptr, long len, rb_io_t *fptr)
c = rb_read_internal(fptr->fd, ptr+offset, n);
if (c == 0) break;
if (c < 0) {
- if (rb_io_wait_readable(fptr->fd))
+ if (fptr_wait_readable(fptr))
goto again;
return -1;
}
@@ -2136,7 +2429,7 @@ io_bufread(char *ptr, long len, rb_io_t *fptr)
return len - n;
}
-static void io_setstrbuf(VALUE *str, long len);
+static int io_setstrbuf(VALUE *str, long len);
struct bufread_arg {
char *str_ptr;
@@ -2298,7 +2591,7 @@ fill_cbuf(rb_io_t *fptr, int ec_flags)
if (res == econv_source_buffer_empty) {
if (fptr->rbuf.len == 0) {
READ_CHECK(fptr);
- if (io_fillbuf(fptr) == -1) {
+ if (io_fillbuf(fptr) < 0) {
if (!fptr->readconv) {
return MORE_CHAR_FINISHED;
}
@@ -2355,33 +2648,45 @@ io_shift_cbuf(rb_io_t *fptr, int len, VALUE *strp)
return str;
}
-static void
+static int
io_setstrbuf(VALUE *str, long len)
{
#ifdef _WIN32
len = (len + 1) & ~1L; /* round up for wide char */
#endif
if (NIL_P(*str)) {
- *str = rb_str_new(0, 0);
+ *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;
+ return FALSE;
}
len -= clen;
}
rb_str_modify_expand(*str, len);
+ return FALSE;
+}
+
+#define MAX_REALLOC_GAP 4096
+static void
+io_shrink_read_string(VALUE str, long n)
+{
+ if (rb_str_capacity(str) - n > MAX_REALLOC_GAP) {
+ rb_str_resize(str, n);
+ }
}
static void
-io_set_read_length(VALUE str, long n)
+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);
}
}
@@ -2393,11 +2698,12 @@ read_all(rb_io_t *fptr, long siz, VALUE str)
long pos;
rb_encoding *enc;
int cr;
+ int shrinkable;
if (NEED_READCONV(fptr)) {
int first = !NIL_P(str);
SET_BINARY_MODE(fptr);
- io_setstrbuf(&str,0);
+ shrinkable = io_setstrbuf(&str,0);
make_readconv(fptr, 0);
while (1) {
VALUE v;
@@ -2416,6 +2722,7 @@ read_all(rb_io_t *fptr, long siz, VALUE str)
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));
return io_enc_str(str, fptr);
}
}
@@ -2429,7 +2736,7 @@ read_all(rb_io_t *fptr, long siz, VALUE str)
cr = 0;
if (siz == 0) siz = BUFSIZ;
- io_setstrbuf(&str,siz);
+ shrinkable = io_setstrbuf(&str, siz);
for (;;) {
READ_CHECK(fptr);
n = io_fread(str, bytes, siz - bytes, fptr);
@@ -2445,6 +2752,7 @@ read_all(rb_io_t *fptr, long siz, VALUE str)
siz += BUFSIZ;
rb_str_modify_expand(str, BUFSIZ);
}
+ if (shrinkable) io_shrink_read_string(str, RSTRING_LEN(str));
str = io_enc_str(str, fptr);
ENC_CODERANGE_SET(str, cr);
return str;
@@ -2453,41 +2761,23 @@ read_all(rb_io_t *fptr, long siz, VALUE str)
void
rb_io_set_nonblock(rb_io_t *fptr)
{
-#ifdef _WIN32
- if (rb_w32_set_nonblock(fptr->fd) != 0) {
+ if (rb_fd_set_nonblock(fptr->fd) != 0) {
rb_sys_fail_path(fptr->pathv);
}
-#else
- int oflags;
-#ifdef F_GETFL
- oflags = fcntl(fptr->fd, F_GETFL);
- if (oflags == -1) {
- rb_sys_fail_path(fptr->pathv);
- }
-#else
- oflags = 0;
-#endif
- if ((oflags & O_NONBLOCK) == 0) {
- oflags |= O_NONBLOCK;
- if (fcntl(fptr->fd, F_SETFL, oflags) == -1) {
- rb_sys_fail_path(fptr->pathv);
- }
- }
-#endif
}
-struct read_internal_arg {
- int fd;
- char *str_ptr;
- long len;
-};
-
static VALUE
read_internal_call(VALUE arg)
{
- struct read_internal_arg *p = (struct read_internal_arg *)arg;
- p->len = rb_read_internal(p->fd, p->str_ptr, p->len);
- return Qundef;
+ struct io_internal_read_struct *iis = (struct io_internal_read_struct *)arg;
+
+ return rb_thread_io_blocking_region(internal_read_func, iis, iis->fd);
+}
+
+static long
+read_internal_locktmp(VALUE str, struct io_internal_read_struct *iis)
+{
+ return (long)rb_str_locktmp_ensure(str, read_internal_call, (VALUE)iis);
}
static int
@@ -2506,7 +2796,8 @@ io_getpartial(int argc, VALUE *argv, VALUE io, VALUE opts, int nonblock)
rb_io_t *fptr;
VALUE length, str;
long n, len;
- struct read_internal_arg arg;
+ struct io_internal_read_struct iis;
+ int shrinkable;
rb_scan_args(argc, argv, "11", &length, &str);
@@ -2514,7 +2805,7 @@ io_getpartial(int argc, VALUE *argv, VALUE io, VALUE opts, int nonblock)
rb_raise(rb_eArgError, "negative length %ld given", len);
}
- io_setstrbuf(&str,len);
+ shrinkable = io_setstrbuf(&str, len);
OBJ_TAINT(str);
GetOpenFile(io, fptr);
@@ -2532,14 +2823,14 @@ io_getpartial(int argc, VALUE *argv, VALUE io, VALUE opts, int nonblock)
rb_io_set_nonblock(fptr);
}
io_setstrbuf(&str, len);
- arg.fd = fptr->fd;
- arg.str_ptr = RSTRING_PTR(str);
- arg.len = len;
- rb_str_locktmp_ensure(str, read_internal_call, (VALUE)&arg);
- n = arg.len;
+ iis.fd = fptr->fd;
+ iis.nonblock = nonblock;
+ iis.buf = RSTRING_PTR(str);
+ iis.capa = len;
+ n = read_internal_locktmp(str, &iis);
if (n < 0) {
int e = errno;
- if (!nonblock && rb_io_wait_readable(fptr->fd))
+ if (!nonblock && fptr_wait_readable(fptr))
goto again;
if (nonblock && (e == EWOULDBLOCK || e == EAGAIN)) {
if (no_exception_p(opts))
@@ -2551,7 +2842,7 @@ io_getpartial(int argc, VALUE *argv, VALUE io, VALUE opts, int nonblock)
rb_syserr_fail_path(e, fptr->pathv);
}
}
- io_set_read_length(str, n);
+ io_set_read_length(str, n, shrinkable);
if (n == 0)
return Qnil;
@@ -2567,10 +2858,12 @@ io_getpartial(int argc, VALUE *argv, VALUE io, VALUE opts, int nonblock)
* Reads at most <i>maxlen</i> bytes from the I/O stream.
* It blocks only if <em>ios</em> has no data immediately available.
* It doesn't block if some data available.
- * If the optional <i>outbuf</i> argument is present,
+ *
+ * If the optional _outbuf_ argument is present,
* it must reference a String, which will receive the data.
- * The <i>outbuf</i> will contain only the received data after the method call
+ * The _outbuf_ will contain only the received data after the method call
* even if it is not empty at the beginning.
+ *
* It raises <code>EOFError</code> on end of file.
*
* readpartial is designed for streams such as pipe, socket, tty, etc.
@@ -2644,13 +2937,14 @@ io_read_nonblock(VALUE io, VALUE length, VALUE str, VALUE ex)
{
rb_io_t *fptr;
long n, len;
- struct read_internal_arg arg;
+ struct io_internal_read_struct iis;
+ int shrinkable;
if ((len = NUM2LONG(length)) < 0) {
rb_raise(rb_eArgError, "negative length %ld given", len);
}
- io_setstrbuf(&str,len);
+ shrinkable = io_setstrbuf(&str, len);
OBJ_TAINT(str);
GetOpenFile(io, fptr);
rb_io_check_byte_readable(fptr);
@@ -2661,12 +2955,12 @@ io_read_nonblock(VALUE io, VALUE length, VALUE str, VALUE ex)
n = read_buffered_data(RSTRING_PTR(str), len, fptr);
if (n <= 0) {
rb_io_set_nonblock(fptr);
- io_setstrbuf(&str, len);
- arg.fd = fptr->fd;
- arg.str_ptr = RSTRING_PTR(str);
- arg.len = len;
- rb_str_locktmp_ensure(str, read_internal_call, (VALUE)&arg);
- n = arg.len;
+ shrinkable |= io_setstrbuf(&str, len);
+ iis.fd = fptr->fd;
+ iis.nonblock = 1;
+ iis.buf = RSTRING_PTR(str);
+ iis.capa = len;
+ n = read_internal_locktmp(str, &iis);
if (n < 0) {
int e = errno;
if ((e == EWOULDBLOCK || e == EAGAIN)) {
@@ -2677,7 +2971,7 @@ io_read_nonblock(VALUE io, VALUE length, VALUE str, VALUE ex)
rb_syserr_fail_path(e, fptr->pathv);
}
}
- io_set_read_length(str, n);
+ io_set_read_length(str, n, shrinkable);
if (n == 0) {
if (ex == Qfalse) return Qnil;
@@ -2706,8 +3000,9 @@ io_write_nonblock(VALUE io, VALUE str, VALUE ex)
rb_io_set_nonblock(fptr);
n = write(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str));
+ RB_GC_GUARD(str);
- if (n == -1) {
+ if (n < 0) {
int e = errno;
if (e == EWOULDBLOCK || e == EAGAIN) {
if (ex == Qfalse) {
@@ -2727,66 +3022,67 @@ io_write_nonblock(VALUE io, VALUE str, VALUE ex)
* call-seq:
* ios.read([length [, outbuf]]) -> string, outbuf, or nil
*
- * Reads <i>length</i> bytes from the I/O stream.
+ * Reads _length_ bytes from the I/O stream.
*
- * <i>length</i> must be a non-negative integer or <code>nil</code>.
+ * _length_ must be a non-negative integer or +nil+.
*
- * If <i>length</i> is a positive integer,
- * it tries to read <i>length</i> bytes without any conversion (binary mode).
- * It returns <code>nil</code> or a string whose length is 1 to <i>length</i> bytes.
- * <code>nil</code> means it met EOF at beginning.
- * The 1 to <i>length</i>-1 bytes string means it met EOF after reading the result.
- * The <i>length</i> bytes string means it doesn't meet EOF.
- * The resulted string is always ASCII-8BIT encoding.
+ * If _length_ is a positive integer, +read+ tries to read
+ * _length_ bytes without any conversion (binary mode).
+ * It returns +nil+ if an EOF is encountered before anything can be read.
+ * Fewer than _length_ bytes are returned if an EOF is encountered during
+ * the read.
+ * In the case of an integer _length_, the resulting string is always
+ * in ASCII-8BIT encoding.
*
- * If <i>length</i> is omitted or is <code>nil</code>,
- * it reads until EOF and the encoding conversion is applied.
- * It returns a string even if EOF is met at beginning.
+ * If _length_ is omitted or is +nil+, it reads until EOF
+ * and the encoding conversion is applied, if applicable.
+ * A string is returned even if EOF is encountered before any data is read.
*
- * If <i>length</i> is zero, it returns <code>""</code>.
+ * If _length_ is zero, it returns an empty string (<code>""</code>).
*
- * If the optional <i>outbuf</i> argument is present, it must reference
- * a String, which will receive the data.
- * The <i>outbuf</i> will contain only the received data after the method call
+ * If the optional _outbuf_ argument is present,
+ * it must reference a String, which will receive the data.
+ * The _outbuf_ will contain only the received data after the method call
* even if it is not empty at the beginning.
*
- * At end of file, it returns <code>nil</code> or <code>""</code>
- * depend on <i>length</i>.
- * <code><i>ios</i>.read()</code> and
- * <code><i>ios</i>.read(nil)</code> returns <code>""</code>.
- * <code><i>ios</i>.read(<i>positive-integer</i>)</code> returns <code>nil</code>.
+ * When this method is called at end of file, it returns +nil+
+ * or <code>""</code>, depending on _length_:
+ * +read+, <code>read(nil)</code>, and <code>read(0)</code> return
+ * <code>""</code>,
+ * <code>read(<i>positive_integer</i>)</code> returns +nil+.
*
* f = File.new("testfile")
* f.read(16) #=> "This is line one"
*
- * # reads whole file
- * open("file") {|f|
- * data = f.read # This returns a string even if the file is empty.
- * ...
- * }
+ * # read whole file
+ * open("file") do |f|
+ * data = f.read # This returns a string even if the file is empty.
+ * # ...
+ * end
*
- * # iterate over fixed length records.
- * open("fixed-record-file") {|f|
+ * # iterate over fixed length records
+ * open("fixed-record-file") do |f|
* while record = f.read(256)
- * ...
+ * # ...
* end
- * }
+ * end
*
- * # iterate over variable length records.
- * # record is prefixed by 32-bit length.
- * open("variable-record-file") {|f|
+ * # iterate over variable length records,
+ * # each record is prefixed by its 32-bit length
+ * open("variable-record-file") do |f|
* while len = f.read(4)
- * len = len.unpack("N")[0] # 32-bit length
- * record = f.read(len) # This returns a string even if len is 0.
+ * len = len.unpack("N")[0] # 32-bit length
+ * record = f.read(len) # This returns a string even if len is 0.
* end
- * }
+ * end
*
- * Note that this method behaves like fread() function in C.
- * This means it retry to invoke read(2) system call to read data with the specified length (or until EOF).
- * This behavior is preserved even if <i>ios</i> is non-blocking mode.
+ * Note that this method behaves like the fread() function in C.
+ * This means it retries to invoke read(2) system calls to read data
+ * with the specified length (or until EOF).
+ * This behavior is preserved even if <i>ios</i> is in non-blocking mode.
* (This method is non-blocking flag insensitive as other methods.)
- * If you need the behavior like single read(2) system call,
- * consider readpartial, read_nonblock and sysread.
+ * If you need the behavior like a single read(2) system call,
+ * consider #readpartial, #read_nonblock, and #sysread.
*/
static VALUE
@@ -2795,6 +3091,7 @@ io_read(int argc, VALUE *argv, VALUE io)
rb_io_t *fptr;
long n, len;
VALUE length, str;
+ int shrinkable;
#if defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
int previous_mode;
#endif
@@ -2811,12 +3108,12 @@ io_read(int argc, VALUE *argv, VALUE io)
rb_raise(rb_eArgError, "negative length %ld given", len);
}
- io_setstrbuf(&str,len);
+ shrinkable = io_setstrbuf(&str,len);
GetOpenFile(io, fptr);
rb_io_check_byte_readable(fptr);
if (len == 0) {
- io_set_read_length(str, 0);
+ io_set_read_length(str, 0, shrinkable);
return str;
}
@@ -2825,7 +3122,7 @@ io_read(int argc, VALUE *argv, VALUE io)
previous_mode = set_binary_mode_with_seek_cur(fptr);
#endif
n = io_fread(str, 0, len, fptr);
- io_set_read_length(str, n);
+ io_set_read_length(str, n, shrinkable);
#if defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
if (previous_mode == O_TEXT) {
setmode(fptr->fd, O_TEXT);
@@ -3009,6 +3306,12 @@ rb_io_getline_fast(rb_io_t *fptr, rb_encoding *enc, int chomp)
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)
@@ -3266,17 +3569,17 @@ rb_io_gets_internal(VALUE io)
/*
* call-seq:
- * ios.gets(sep=$/) -> string or nil
- * ios.gets(limit) -> string or nil
- * ios.gets(sep, limit) -> string or nil
+ * ios.gets(sep=$/ [, getline_args]) -> string or nil
+ * ios.gets(limit [, getline_args]) -> string or nil
+ * ios.gets(sep, limit [, getline_args]) -> string or nil
*
* Reads the next ``line'' from the I/O stream; lines are separated by
- * <i>sep</i>. A separator of <code>nil</code> reads the entire
+ * <i>sep</i>. A separator of +nil+ reads the entire
* contents, and a zero-length separator reads the input a paragraph at
* a time (two successive newlines in the input separate paragraphs).
* The stream must be opened for reading or an <code>IOError</code>
* will be raised. The line read in will be returned and also assigned
- * to <code>$_</code>. Returns <code>nil</code> if called at end of
+ * to <code>$_</code>. Returns +nil+ if called at end of
* file. If the first argument is an integer, or optional second
* argument is given, the returning string would not be longer than the
* given value in bytes.
@@ -3370,9 +3673,9 @@ rb_io_set_lineno(VALUE io, VALUE lineno)
/*
* call-seq:
- * ios.readline(sep=$/) -> string
- * ios.readline(limit) -> string
- * ios.readline(sep, limit) -> string
+ * ios.readline(sep=$/ [, getline_args]) -> string
+ * ios.readline(limit [, getline_args]) -> string
+ * ios.readline(sep, limit [, getline_args]) -> string
*
* Reads a line as with <code>IO#gets</code>, but raises an
* <code>EOFError</code> on end of file.
@@ -3393,20 +3696,26 @@ static VALUE io_readlines(const struct getline_arg *arg, VALUE io);
/*
* call-seq:
- * ios.readlines(sep=$/) -> array
- * ios.readlines(limit) -> array
- * ios.readlines(sep, limit) -> array
+ * ios.readlines(sep=$/ [, getline_args]) -> array
+ * ios.readlines(limit [, getline_args]) -> array
+ * ios.readlines(sep, limit [, getline_args]) -> array
*
* Reads all of the lines in <em>ios</em>, and returns them in
- * <i>anArray</i>. Lines are separated by the optional <i>sep</i>. If
- * <i>sep</i> is <code>nil</code>, the rest of the stream is returned
- * as a single record. If the first argument is an integer, or
+ * an array. Lines are separated by the optional <i>sep</i>. If
+ * <i>sep</i> is +nil+, the rest of the stream is returned
+ * as a single record.
+ * If the first argument is an integer, or an
* optional second argument is given, the returning string would not be
* longer than the given value in bytes. The stream must be opened for
* reading or an <code>IOError</code> will be raised.
*
* f = File.new("testfile")
* f.readlines[0] #=> "This is line one\n"
+ *
+ * f = File.new("testfile", chomp: true)
+ * f.readlines[0] #=> "This is line one"
+ *
+ * See IO.readlines for details about getline_args.
*/
static VALUE
@@ -3434,15 +3743,15 @@ io_readlines(const struct getline_arg *arg, VALUE io)
/*
* call-seq:
- * ios.each(sep=$/) {|line| block } -> ios
- * ios.each(limit) {|line| block } -> ios
- * ios.each(sep,limit) {|line| block } -> ios
- * ios.each(...) -> an_enumerator
+ * ios.each(sep=$/ [, getline_args]) {|line| block } -> ios
+ * ios.each(limit [, getline_args]) {|line| block } -> ios
+ * ios.each(sep, limit [, getline_args]) {|line| block } -> ios
+ * ios.each(...) -> an_enumerator
*
- * ios.each_line(sep=$/) {|line| block } -> ios
- * ios.each_line(limit) {|line| block } -> ios
- * ios.each_line(sep,limit) {|line| block } -> ios
- * ios.each_line(...) -> an_enumerator
+ * ios.each_line(sep=$/ [, getline_args]) {|line| block } -> ios
+ * ios.each_line(limit [, getline_args]) {|line| block } -> ios
+ * ios.each_line(sep, limit [, getline_args]) {|line| block } -> ios
+ * ios.each_line(...) -> an_enumerator
*
* Executes the block for every line in <em>ios</em>, where lines are
* separated by <i>sep</i>. <em>ios</em> must be opened for
@@ -3459,6 +3768,8 @@ io_readlines(const struct getline_arg *arg, VALUE io)
* 2: This is line two
* 3: This is line three
* 4: And so on...
+ *
+ * See IO.readlines for details about getline_args.
*/
static VALUE
@@ -3548,9 +3859,9 @@ io_getc(rb_io_t *fptr, rb_encoding *enc)
VALUE str;
if (NEED_READCONV(fptr)) {
- VALUE str = Qnil;
rb_encoding *read_enc = io_read_encoding(fptr);
+ str = Qnil;
SET_BINARY_MODE(fptr);
make_readconv(fptr, 0);
@@ -3829,7 +4140,7 @@ rb_io_codepoints(VALUE io)
* ios.getc -> string or nil
*
* Reads a one-character string from <em>ios</em>. Returns
- * <code>nil</code> if called at end of file.
+ * +nil+ if called at end of file.
*
* f = File.new("testfile")
* f.getc #=> "h"
@@ -3878,7 +4189,7 @@ rb_io_readchar(VALUE io)
* ios.getbyte -> integer or nil
*
* Gets the next 8-bit byte (0..255) from <em>ios</em>. Returns
- * <code>nil</code> if called at end of file.
+ * +nil+ if called at end of file.
*
* f = File.new("testfile")
* f.getbyte #=> 84
@@ -3932,7 +4243,7 @@ rb_io_readbyte(VALUE io)
/*
* call-seq:
* ios.ungetbyte(string) -> nil
- * ios.ungetbyte(integer) -> nil
+ * ios.ungetbyte(integer) -> nil
*
* Pushes back bytes (passed as a parameter) onto <em>ios</em>,
* such that a subsequent buffered read will return it. Only one byte
@@ -3950,16 +4261,22 @@ VALUE
rb_io_ungetbyte(VALUE io, VALUE b)
{
rb_io_t *fptr;
+ VALUE v;
+ unsigned char c;
GetOpenFile(io, fptr);
rb_io_check_byte_readable(fptr);
- if (NIL_P(b)) return Qnil;
- if (FIXNUM_P(b)) {
- char cc = FIX2INT(b);
- b = rb_str_new(&cc, 1);
- }
- else {
- SafeStringValue(b);
+ switch (TYPE(b)) {
+ case T_NIL:
+ return Qnil;
+ case T_FIXNUM:
+ case T_BIGNUM: ;
+ v = rb_int_modulo(b, INT2FIX(256));
+ c = NUM2INT(v) & 0xFF;
+ b = rb_str_new((const char *)&c, 1);
+ break;
+ default:
+ SafeStringValue(b);
}
io_ungetbyte(b, fptr);
return Qnil;
@@ -4128,7 +4445,7 @@ rb_io_set_close_on_exec(VALUE io, VALUE arg)
if ((ret & FD_CLOEXEC) != flag) {
ret = (ret & ~FD_CLOEXEC) | flag;
ret = fcntl(fd, F_SETFD, ret);
- if (ret == -1) rb_sys_fail_path(fptr->pathv);
+ if (ret != 0) rb_sys_fail_path(fptr->pathv);
}
}
@@ -4140,7 +4457,7 @@ rb_io_set_close_on_exec(VALUE io, VALUE arg)
if ((ret & FD_CLOEXEC) != flag) {
ret = (ret & ~FD_CLOEXEC) | flag;
ret = fcntl(fd, F_SETFD, ret);
- if (ret == -1) rb_sys_fail_path(fptr->pathv);
+ if (ret != 0) rb_sys_fail_path(fptr->pathv);
}
}
return Qnil;
@@ -4270,7 +4587,8 @@ static void free_io_buffer(rb_io_buffer_t *buf);
static void clear_codeconv(rb_io_t *fptr);
static void
-fptr_finalize(rb_io_t *fptr, int noraise)
+fptr_finalize_flush(rb_io_t *fptr, int noraise, int keepgvl,
+ struct list_head *busy)
{
VALUE err = Qnil;
int fd = fptr->fd;
@@ -4290,8 +4608,7 @@ fptr_finalize(rb_io_t *fptr, int noraise)
}
if (fptr->wbuf.len) {
if (noraise) {
- if ((int)io_flush_buffer_sync(fptr) < 0 && NIL_P(err))
- err = Qtrue;
+ io_flush_buffer_sync(fptr);
}
else {
if (io_fflush(fptr) < 0 && NIL_P(err))
@@ -4303,6 +4620,14 @@ fptr_finalize(rb_io_t *fptr, int noraise)
fptr->stdio_file = 0;
fptr->mode &= ~(FMODE_READABLE|FMODE_WRITABLE);
+ /*
+ * ensure waiting_fd users do not hit EBADF, wait for them
+ * to exit before we call close().
+ */
+ if (busy) {
+ do rb_thread_schedule(); while (!list_empty(busy));
+ }
+
if (IS_PREP_STDIO(fptr) || fd <= 2) {
/* need to keep FILE objects of stdin, stdout and stderr */
}
@@ -4310,7 +4635,7 @@ fptr_finalize(rb_io_t *fptr, int noraise)
/* stdio_file is deallocated anyway
* even if fclose failed. */
if ((maygvl_fclose(stdio_file, noraise) < 0) && NIL_P(err))
- err = noraise ? Qtrue : INT2NUM(errno);
+ if (!noraise) err = INT2NUM(errno);
}
else if (0 <= fd) {
/* fptr->fd may be closed even if close fails.
@@ -4318,10 +4643,10 @@ fptr_finalize(rb_io_t *fptr, int noraise)
* We assumes it is closed. */
/**/
- int keepgvl = !(mode & FMODE_WRITABLE);
+ keepgvl |= !(mode & FMODE_WRITABLE);
keepgvl |= noraise;
if ((maygvl_close(fd, keepgvl) < 0) && NIL_P(err))
- err = noraise ? Qtrue : INT2NUM(errno);
+ if (!noraise) err = INT2NUM(errno);
}
if (!NIL_P(err) && !noraise) {
@@ -4330,6 +4655,12 @@ fptr_finalize(rb_io_t *fptr, int noraise)
else
rb_exc_raise(err);
}
+}
+
+static void
+fptr_finalize(rb_io_t *fptr, int noraise)
+{
+ fptr_finalize_flush(fptr, noraise, FALSE, 0);
free_io_buffer(&fptr->rbuf);
free_io_buffer(&fptr->wbuf);
clear_codeconv(fptr);
@@ -4382,21 +4713,36 @@ clear_codeconv(rb_io_t *fptr)
clear_writeconv(fptr);
}
-int
-rb_io_fptr_finalize(rb_io_t *fptr)
+void
+rb_io_fptr_finalize_internal(void *ptr)
{
- if (!fptr) return 0;
+ rb_io_t *fptr = ptr;
+
+ if (!ptr) return;
fptr->pathv = Qnil;
if (0 <= fptr->fd)
- rb_io_fptr_cleanup(fptr, TRUE);
+ rb_io_fptr_cleanup(fptr, TRUE);
fptr->write_lock = 0;
free_io_buffer(&fptr->rbuf);
free_io_buffer(&fptr->wbuf);
clear_codeconv(fptr);
free(fptr);
- return 1;
}
+#undef rb_io_fptr_finalize
+int
+rb_io_fptr_finalize(rb_io_t *fptr)
+{
+ if (!fptr) {
+ return 0;
+ }
+ else {
+ rb_io_fptr_finalize_internal(fptr);
+ return 1;
+ }
+}
+#define rb_io_fptr_finalize(fptr) rb_io_fptr_finalize_internal(fptr)
+
RUBY_FUNC_EXPORTED size_t
rb_io_memsize(const rb_io_t *fptr)
{
@@ -4409,14 +4755,23 @@ rb_io_memsize(const rb_io_t *fptr)
return size;
}
+#ifdef _WIN32
+/* keep GVL while closing to prevent crash on Windows */
+# define KEEPGVL TRUE
+#else
+# define KEEPGVL FALSE
+#endif
+
+int rb_notify_fd_close(int fd, struct list_head *);
static rb_io_t *
io_close_fptr(VALUE io)
{
rb_io_t *fptr;
- int fd;
VALUE write_io;
rb_io_t *write_fptr;
+ struct list_head busy;
+ list_head_init(&busy);
write_io = GetWriteIO(io);
if (io != write_io) {
write_fptr = RFILE(write_io)->fptr;
@@ -4429,8 +4784,10 @@ io_close_fptr(VALUE io)
if (!fptr) return 0;
if (fptr->fd < 0) return 0;
- fd = fptr->fd;
- rb_thread_fd_close(fd);
+ if (rb_notify_fd_close(fptr->fd, &busy)) {
+ /* calls close(fptr->fd): */
+ fptr_finalize_flush(fptr, FALSE, KEEPGVL, &busy);
+ }
rb_io_fptr_cleanup(fptr, FALSE);
return fptr;
}
@@ -4492,7 +4849,7 @@ static VALUE
ignore_closed_stream(VALUE io, VALUE exc)
{
enum {mesg_len = sizeof(closed_stream)-1};
- VALUE mesg = rb_attr_get(exc, rb_intern("mesg"));
+ 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)) {
@@ -4565,6 +4922,8 @@ rb_io_closed(VALUE io)
*
* prog.rb:3:in `readlines': not opened for reading (IOError)
* from prog.rb:3
+ *
+ * Calling this method on closed IO object is just ignored since Ruby 2.3.
*/
static VALUE
@@ -4625,6 +4984,8 @@ rb_io_close_read(VALUE io)
* prog.rb:3:in `write': not opened for writing (IOError)
* from prog.rb:3:in `print'
* from prog.rb:3
+ *
+ * Calling this method on closed IO object is just ignored since Ruby 2.3.
*/
static VALUE
@@ -4695,7 +5056,7 @@ rb_io_sysseek(int argc, VALUE *argv, VALUE io)
}
errno = 0;
pos = lseek(fptr->fd, pos, whence);
- if (pos == -1 && errno) rb_sys_fail_path(fptr->pathv);
+ if (pos < 0 && errno) rb_sys_fail_path(fptr->pathv);
return OFFT2NUM(pos);
}
@@ -4716,8 +5077,10 @@ rb_io_sysseek(int argc, VALUE *argv, VALUE io)
static VALUE
rb_io_syswrite(VALUE io, VALUE str)
{
+ VALUE tmp;
rb_io_t *fptr;
- long n;
+ long n, len;
+ const char *ptr;
if (!RB_TYPE_P(str, T_STRING))
str = rb_obj_as_string(str);
@@ -4726,16 +5089,15 @@ rb_io_syswrite(VALUE io, VALUE str)
GetOpenFile(io, fptr);
rb_io_check_writable(fptr);
- str = rb_str_new_frozen(str);
-
if (fptr->wbuf.len) {
rb_warn("syswrite for buffered IO");
}
- n = rb_write_internal(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str));
- RB_GC_GUARD(str);
-
- if (n == -1) rb_sys_fail_path(fptr->pathv);
+ tmp = rb_str_tmp_frozen_acquire(str);
+ RSTRING_GETMEM(tmp, ptr, len);
+ n = rb_write_internal(fptr->fd, ptr, len);
+ if (n < 0) rb_sys_fail_path(fptr->pathv);
+ rb_str_tmp_frozen_release(str, tmp);
return LONG2FIX(n);
}
@@ -4747,10 +5109,12 @@ rb_io_syswrite(VALUE io, VALUE str)
* Reads <i>maxlen</i> bytes from <em>ios</em> using a low-level
* read and returns them as a string. Do not mix with other methods
* that read from <em>ios</em> or you may get unpredictable results.
- * If the optional <i>outbuf</i> argument is present, it must reference
- * a String, which will receive the data.
- * The <i>outbuf</i> will contain only the received data after the method call
+ *
+ * If the optional _outbuf_ argument is present,
+ * it must reference a String, which will receive the data.
+ * The _outbuf_ will contain only the received data after the method call
* even if it is not empty at the beginning.
+ *
* Raises <code>SystemCallError</code> on error and
* <code>EOFError</code> at end of file.
*
@@ -4764,12 +5128,13 @@ rb_io_sysread(int argc, VALUE *argv, VALUE io)
VALUE len, str;
rb_io_t *fptr;
long n, ilen;
- struct read_internal_arg arg;
+ struct io_internal_read_struct iis;
+ int shrinkable;
rb_scan_args(argc, argv, "11", &len, &str);
ilen = NUM2LONG(len);
- io_setstrbuf(&str,ilen);
+ shrinkable = io_setstrbuf(&str, ilen);
if (ilen == 0) return str;
GetOpenFile(io, fptr);
@@ -4791,17 +5156,16 @@ rb_io_sysread(int argc, VALUE *argv, VALUE io)
rb_io_check_closed(fptr);
io_setstrbuf(&str, ilen);
- rb_str_locktmp(str);
- arg.fd = fptr->fd;
- arg.str_ptr = RSTRING_PTR(str);
- arg.len = ilen;
- rb_ensure(read_internal_call, (VALUE)&arg, rb_str_unlocktmp, str);
- n = arg.len;
+ iis.fd = fptr->fd;
+ iis.nonblock = 1; /* for historical reasons, maybe (see above) */
+ iis.buf = RSTRING_PTR(str);
+ iis.capa = ilen;
+ n = read_internal_locktmp(str, &iis);
- if (n == -1) {
+ if (n < 0) {
rb_sys_fail_path(fptr->pathv);
}
- io_set_read_length(str, n);
+ io_set_read_length(str, n, shrinkable);
if (n == 0 && ilen > 0) {
rb_eof_error();
}
@@ -4810,6 +5174,154 @@ rb_io_sysread(int argc, VALUE *argv, VALUE io)
return str;
}
+#if defined(HAVE_PREAD) || defined(HAVE_PWRITE)
+struct prdwr_internal_arg {
+ int fd;
+ void *buf;
+ size_t count;
+ off_t offset;
+};
+#endif /* HAVE_PREAD || HAVE_PWRITE */
+
+#if defined(HAVE_PREAD)
+static VALUE
+internal_pread_func(void *arg)
+{
+ struct prdwr_internal_arg *p = arg;
+ return (VALUE)pread(p->fd, p->buf, p->count, p->offset);
+}
+
+static VALUE
+pread_internal_call(VALUE arg)
+{
+ struct prdwr_internal_arg *p = (struct prdwr_internal_arg *)arg;
+ return rb_thread_io_blocking_region(internal_pread_func, p, p->fd);
+}
+
+/*
+ * call-seq:
+ * ios.pread(maxlen, offset[, outbuf]) -> string
+ *
+ * Reads <i>maxlen</i> bytes from <em>ios</em> using the pread system call
+ * and returns them as a string without modifying the underlying
+ * descriptor offset. This is advantageous compared to combining IO#seek
+ * and IO#read in that it is atomic, allowing multiple threads/process to
+ * share the same IO object for reading the file at various locations.
+ * This bypasses any userspace buffering of the IO layer.
+ * If the optional <i>outbuf</i> argument is present, it must
+ * reference a String, which will receive the data.
+ * Raises <code>SystemCallError</code> on error, <code>EOFError</code>
+ * at end of file and <code>NotImplementedError</code> if platform does not
+ * implement the system call.
+ *
+ * File.write("testfile", "This is line one\nThis is line two\n")
+ * File.open("testfile") do |f|
+ * p f.read # => "This is line one\nThis is line two\n"
+ * p f.pread(12, 0) # => "This is line"
+ * p f.pread(9, 8) # => "line one\n"
+ * end
+ */
+static VALUE
+rb_io_pread(int argc, VALUE *argv, VALUE io)
+{
+ VALUE len, offset, str;
+ rb_io_t *fptr;
+ ssize_t n;
+ struct prdwr_internal_arg arg;
+ int shrinkable;
+
+ rb_scan_args(argc, argv, "21", &len, &offset, &str);
+ arg.count = NUM2SIZET(len);
+ arg.offset = NUM2OFFT(offset);
+
+ shrinkable = io_setstrbuf(&str, (long)arg.count);
+ if (arg.count == 0) return str;
+ arg.buf = RSTRING_PTR(str);
+
+ GetOpenFile(io, fptr);
+ rb_io_check_byte_readable(fptr);
+
+ arg.fd = fptr->fd;
+ rb_io_check_closed(fptr);
+
+ rb_str_locktmp(str);
+ n = (ssize_t)rb_ensure(pread_internal_call, (VALUE)&arg, rb_str_unlocktmp, str);
+
+ if (n < 0) {
+ rb_sys_fail_path(fptr->pathv);
+ }
+ io_set_read_length(str, n, shrinkable);
+ if (n == 0 && arg.count > 0) {
+ rb_eof_error();
+ }
+ OBJ_TAINT(str);
+
+ return str;
+}
+#else
+# define rb_io_pread rb_f_notimplement
+#endif /* HAVE_PREAD */
+
+#if defined(HAVE_PWRITE)
+static VALUE
+internal_pwrite_func(void *ptr)
+{
+ struct prdwr_internal_arg *arg = ptr;
+
+ return (VALUE)pwrite(arg->fd, arg->buf, arg->count, arg->offset);
+}
+
+/*
+ * call-seq:
+ * ios.pwrite(string, offset) -> integer
+ *
+ * Writes the given string to <em>ios</em> at <i>offset</i> using pwrite()
+ * system call. This is advantageous to combining IO#seek and IO#write
+ * in that it is atomic, allowing multiple threads/process to share the
+ * same IO object for reading the file at various locations.
+ * This bypasses any userspace buffering of the IO layer.
+ * Returns the number of bytes written.
+ * Raises <code>SystemCallError</code> on error and <code>NotImplementedError</code>
+ * if platform does not implement the system call.
+ *
+ * File.open("out", "w") do |f|
+ * f.pwrite("ABCDEF", 3) #=> 6
+ * end
+ *
+ * File.read("out") #=> "\u0000\u0000\u0000ABCDEF"
+ */
+static VALUE
+rb_io_pwrite(VALUE io, VALUE str, VALUE offset)
+{
+ rb_io_t *fptr;
+ ssize_t n;
+ struct prdwr_internal_arg arg;
+ VALUE tmp;
+
+ if (!RB_TYPE_P(str, T_STRING))
+ str = rb_obj_as_string(str);
+
+ arg.offset = NUM2OFFT(offset);
+
+ io = GetWriteIO(io);
+ GetOpenFile(io, fptr);
+ rb_io_check_writable(fptr);
+ arg.fd = fptr->fd;
+
+ tmp = rb_str_tmp_frozen_acquire(str);
+ arg.buf = RSTRING_PTR(tmp);
+ arg.count = (size_t)RSTRING_LEN(tmp);
+
+ n = (ssize_t)rb_thread_io_blocking_region(internal_pwrite_func, &arg, fptr->fd);
+ if (n < 0) rb_sys_fail_path(fptr->pathv);
+ rb_str_tmp_frozen_release(str, tmp);
+
+ return SSIZET2NUM(n);
+}
+#else
+# define rb_io_pwrite rb_f_notimplement
+#endif /* HAVE_PWRITE */
+
VALUE
rb_io_binmode(VALUE io)
{
@@ -4877,7 +5389,6 @@ rb_io_ascii8bit_binmode(VALUE io)
* - newline conversion disabled
* - encoding conversion disabled
* - content is treated as ASCII-8BIT
- *
*/
static VALUE
@@ -4922,10 +5433,10 @@ rb_io_fmode_modestr(int fmode)
case FMODE_READABLE:
return MODE_BTMODE("r", "rb", "rt");
case FMODE_WRITABLE:
- return MODE_BTMODE("w", "wb", "wt");
+ return MODE_BTXMODE("w", "wb", "wt", "wx", "wbx", "wtx");
case FMODE_READWRITE:
if (fmode & FMODE_CREATE) {
- return MODE_BTMODE("w+", "wb+", "wt+");
+ return MODE_BTXMODE("w+", "wb+", "wt+", "w+x", "wb+x", "wt+x");
}
return MODE_BTMODE("r+", "rb+", "rt+");
}
@@ -4974,6 +5485,11 @@ rb_io_modestr_fmode(const char *modestr)
case '+':
fmode |= FMODE_READWRITE;
break;
+ case 'x':
+ if (modestr[0] != 'w')
+ goto error;
+ fmode |= FMODE_EXCL;
+ break;
default:
goto error;
case ':':
@@ -5017,6 +5533,9 @@ rb_io_oflags_fmode(int oflags)
if (oflags & O_CREAT) {
fmode |= FMODE_CREATE;
}
+ if (oflags & O_EXCL) {
+ fmode |= FMODE_EXCL;
+ }
#ifdef O_BINARY
if (oflags & O_BINARY) {
fmode |= FMODE_BINMODE;
@@ -5052,6 +5571,9 @@ rb_io_fmode_oflags(int fmode)
if (fmode & FMODE_CREATE) {
oflags |= O_CREAT;
}
+ if (fmode & FMODE_EXCL) {
+ oflags |= O_EXCL;
+ }
#ifdef O_BINARY
if (fmode & FMODE_BINMODE) {
oflags |= O_BINARY;
@@ -5075,7 +5597,11 @@ rb_io_oflags_modestr(int oflags)
#else
# define MODE_BINARY(a,b) (a)
#endif
- int accmode = oflags & (O_RDONLY|O_WRONLY|O_RDWR);
+ int accmode;
+ if (oflags & O_EXCL) {
+ rb_raise(rb_eArgError, "exclusive access mode is not supported");
+ }
+ accmode = oflags & (O_RDONLY|O_WRONLY|O_RDWR);
if (oflags & O_APPEND) {
if (accmode == O_WRONLY) {
return MODE_BINARY("a", "ab");
@@ -5084,7 +5610,7 @@ rb_io_oflags_modestr(int oflags)
return MODE_BINARY("a+", "ab+");
}
}
- switch (oflags & (O_RDONLY|O_WRONLY|O_RDWR)) {
+ switch (accmode) {
default:
rb_raise(rb_eArgError, "invalid access oflags 0x%x", oflags);
case O_RDONLY:
@@ -5288,9 +5814,12 @@ validate_enc_binmode(int *fmode_p, int ecflags, rb_encoding *enc, rb_encoding *e
!rb_enc_asciicompat(enc ? enc : rb_default_external_encoding()))
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");
+ }
if (!(fmode & FMODE_BINMODE) &&
(DEFAULT_TEXTMODE || (ecflags & ECONV_NEWLINE_DECORATOR_MASK))) {
- fmode |= DEFAULT_TEXTMODE;
+ fmode |= FMODE_TEXTMODE;
*fmode_p = fmode;
}
#if !DEFAULT_TEXTMODE
@@ -5568,7 +6097,10 @@ static int
io_strip_bom(VALUE io)
{
VALUE b1, b2, b3, b4;
+ rb_io_t *fptr;
+ GetOpenFile(io, fptr);
+ if (!(fptr->mode & FMODE_READABLE)) return 0;
if (NIL_P(b1 = rb_io_getbyte(io))) return 0;
switch (b1) {
case INT2FIX(0xEF):
@@ -5599,12 +6131,9 @@ io_strip_bom(VALUE io)
return ENCINDEX_UTF_32LE;
}
rb_io_ungetbyte(io, b4);
- rb_io_ungetbyte(io, b3);
- }
- else {
- rb_io_ungetbyte(io, b3);
- return ENCINDEX_UTF_16LE;
}
+ rb_io_ungetbyte(io, b3);
+ return ENCINDEX_UTF_16LE;
}
rb_io_ungetbyte(io, b2);
break;
@@ -5647,6 +6176,7 @@ static VALUE
rb_file_open_generic(VALUE io, VALUE filename, int oflags, int fmode,
const convconfig_t *convconfig, mode_t perm)
{
+ VALUE pathv;
rb_io_t *fptr;
convconfig_t cc;
if (!convconfig) {
@@ -5662,8 +6192,15 @@ rb_file_open_generic(VALUE io, VALUE filename, int oflags, int fmode,
MakeOpenFile(io, fptr);
fptr->mode = fmode;
fptr->encs = *convconfig;
- fptr->pathv = rb_str_new_frozen(filename);
- fptr->fd = rb_sysopen(fptr->pathv, oflags, perm);
+ pathv = rb_str_new_frozen(filename);
+#ifdef O_TMPFILE
+ if (!(oflags & O_TMPFILE)) {
+ fptr->pathv = pathv;
+ }
+#else
+ fptr->pathv = pathv;
+#endif
+ fptr->fd = rb_sysopen(pathv, oflags, perm);
io_check_tty(fptr);
if (fmode & FMODE_SETENC_BY_BOM) io_set_encoding_by_bom(io);
@@ -5731,23 +6268,16 @@ pipe_add_fptr(rb_io_t *fptr)
static void
pipe_del_fptr(rb_io_t *fptr)
{
- struct pipe_list *list = pipe_list;
+ struct pipe_list **prev = &pipe_list;
struct pipe_list *tmp;
- if (list->fptr == fptr) {
- pipe_list = list->next;
- free(list);
- return;
- }
-
- while (list->next) {
- if (list->next->fptr == fptr) {
- tmp = list->next;
- list->next = list->next->next;
+ while ((tmp = *prev) != 0) {
+ if (tmp->fptr == fptr) {
+ *prev = tmp->next;
free(tmp);
return;
}
- list = list->next;
+ prev = &tmp->next;
}
}
@@ -5784,6 +6314,31 @@ pipe_finalize(rb_io_t *fptr, int noraise)
}
#endif
+static void
+fptr_copy_finalizer(rb_io_t *fptr, const rb_io_t *orig)
+{
+#if defined(__CYGWIN__) || !defined(HAVE_WORKING_FORK)
+ void (*const old_finalize)(struct rb_io_t*,int) = fptr->finalize;
+
+ if (old_finalize == orig->finalize) return;
+#endif
+
+ fptr->finalize = orig->finalize;
+
+#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);
+ }
+ else {
+ pipe_del_fptr(fptr);
+ }
+#endif
+}
+
void
rb_io_synchronized(rb_io_t *fptr)
{
@@ -5802,7 +6357,7 @@ rb_pipe(int *pipes)
{
int ret;
ret = rb_cloexec_pipe(pipes);
- if (ret == -1) {
+ if (ret < 0) {
if (rb_gc_for_fd(errno)) {
ret = rb_cloexec_pipe(pipes);
}
@@ -5879,9 +6434,9 @@ linux_get_maxfd(void)
char buf[4096], *p, *np, *e;
ssize_t ss;
fd = rb_cloexec_open("/proc/self/status", O_RDONLY|O_NOCTTY, 0);
- if (fd == -1) return -1;
+ if (fd < 0) return fd;
ss = read(fd, buf, sizeof(buf));
- if (ss == -1) goto err;
+ if (ss < 0) goto err;
p = buf;
e = buf + ss;
while ((int)sizeof("FDSize:\t0\n")-1 <= e-p &&
@@ -5900,7 +6455,7 @@ linux_get_maxfd(void)
err:
close(fd);
- return -1;
+ return (int)ss;
}
#endif
@@ -6070,7 +6625,7 @@ pipe_open(VALUE execarg_obj, const char *modestr, int fmode,
# if defined(HAVE_SPAWNVE)
if (eargp->envp_str) envp = (char **)RSTRING_PTR(eargp->envp_str);
# endif
- while ((pid = DO_SPAWN(cmd, args, envp)) == -1) {
+ while ((pid = DO_SPAWN(cmd, args, envp)) < 0) {
/* exec failed */
switch (e = errno) {
case EAGAIN:
@@ -6103,7 +6658,7 @@ pipe_open(VALUE execarg_obj, const char *modestr, int fmode,
}
/* parent */
- if (pid == -1) {
+ if (pid < 0) {
# if defined(HAVE_WORKING_FORK)
e = errno;
# endif
@@ -6214,7 +6769,7 @@ pipe_open_s(VALUE prog, const char *modestr, int fmode,
VALUE execarg_obj = Qnil;
if (!is_popen_fork(prog))
- execarg_obj = rb_execarg_new(argc, argv, TRUE);
+ execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
return pipe_open(execarg_obj, modestr, fmode, convconfig);
}
@@ -6253,7 +6808,7 @@ pipe_close(VALUE io)
*
* If <i>cmd</i> is an +Array+ of +String+,
* then it will be used as the subprocess's +argv+ bypassing a shell.
- * The array can contains a hash at first for environments and
+ * The array can contain a hash at first for environments and
* a hash at last for options similar to <code>spawn</code>.
*
* The default mode for the new file object is ``r'',
@@ -6290,7 +6845,7 @@ pipe_close(VALUE io)
* the block will be run in two separate processes: once in the parent,
* and once in a child. The parent process will be passed the pipe
* object as a parameter to the block, the child version of the block
- * will be passed <code>nil</code>, and the child's standard in and
+ * will be passed +nil+, and the child's standard in and
* standard out will be connected to the parent through the pipe. Not
* available on all platforms.
*
@@ -6298,7 +6853,7 @@ pipe_close(VALUE io)
* p f.readlines
* f.close
* puts "Parent is #{Process.pid}"
- * IO.popen("date") { |f| puts f.gets }
+ * IO.popen("date") {|f| puts f.gets }
* IO.popen("-") {|f| $stderr.puts "#{Process.pid} is here, f is #{f.inspect}"}
* p $?
* IO.popen(%w"sed -e s|^|<foo>| -e s&$&;zot;&", "r+") {|f|
@@ -6347,14 +6902,14 @@ rb_io_s_popen(int argc, VALUE *argv, VALUE klass)
rb_raise(rb_eArgError, "too many arguments");
}
#endif
- execarg_obj = rb_execarg_new((int)len, RARRAY_CONST_PTR(tmp), FALSE);
+ execarg_obj = rb_execarg_new((int)len, RARRAY_CONST_PTR(tmp), FALSE, FALSE);
RB_GC_GUARD(tmp);
}
else {
SafeStringValue(pname);
execarg_obj = Qnil;
if (!is_popen_fork(pname))
- execarg_obj = rb_execarg_new(1, &pname, TRUE);
+ execarg_obj = rb_execarg_new(1, &pname, TRUE, FALSE);
}
if (!NIL_P(execarg_obj)) {
if (!NIL_P(opt))
@@ -6446,7 +7001,7 @@ rb_open_file(int argc, const VALUE *argv, VALUE io)
*
* call-seq:
* IO.open(fd, mode="r" [, opt]) -> io
- * IO.open(fd, mode="r" [, opt]) { |io| block } -> obj
+ * IO.open(fd, mode="r" [, opt]) {|io| block } -> obj
*
* With no associated block, <code>IO.open</code> is a synonym for IO.new. If
* the optional code block is given, it will be passed +io+ as an argument,
@@ -6558,7 +7113,7 @@ check_pipe_command(VALUE filename_or_command)
* parent. If the command is not <code>"-"</code>, the subprocess runs the
* command.
*
- * When the subprocess is ruby (opened via <code>"|-"</code>), the +open+
+ * When the subprocess is Ruby (opened via <code>"|-"</code>), the +open+
* call returns +nil+. If a block is associated with the open call, that
* block will run twice --- once in the parent and once in the child.
*
@@ -6591,7 +7146,7 @@ check_pipe_command(VALUE filename_or_command)
* Open a subprocess running the same Ruby program:
*
* f = open("|-", "w+")
- * if f == nil
+ * if f.nil?
* puts "in Child"
* exit
* else
@@ -6656,10 +7211,10 @@ rb_f_open(int argc, VALUE *argv)
return rb_io_s_open(argc, argv, rb_cFile);
}
-static VALUE rb_io_open_generic(VALUE, int, int, const convconfig_t *, mode_t);
+static VALUE rb_io_open_generic(VALUE, VALUE, int, int, const convconfig_t *, mode_t);
static VALUE
-rb_io_open(VALUE filename, VALUE vmode, VALUE vperm, VALUE opt)
+rb_io_open(VALUE io, VALUE filename, VALUE vmode, VALUE vperm, VALUE opt)
{
int oflags, fmode;
convconfig_t convconfig;
@@ -6667,34 +7222,24 @@ rb_io_open(VALUE filename, VALUE vmode, VALUE vperm, VALUE opt)
rb_io_extract_modeenc(&vmode, &vperm, opt, &oflags, &fmode, &convconfig);
perm = NIL_P(vperm) ? 0666 : NUM2MODET(vperm);
- return rb_io_open_generic(filename, oflags, fmode, &convconfig, perm);
+ return rb_io_open_generic(io, filename, oflags, fmode, &convconfig, perm);
}
static VALUE
-rb_io_open_generic(VALUE filename, int oflags, int fmode,
+rb_io_open_generic(VALUE klass, VALUE filename, int oflags, int fmode,
const convconfig_t *convconfig, mode_t perm)
{
VALUE cmd;
- if (!NIL_P(cmd = check_pipe_command(filename))) {
+ if (klass == rb_cIO && !NIL_P(cmd = check_pipe_command(filename))) {
return pipe_open_s(cmd, rb_io_oflags_modestr(oflags), fmode, convconfig);
}
else {
- return rb_file_open_generic(io_alloc(rb_cFile), filename,
+ return rb_file_open_generic(io_alloc(klass), filename,
oflags, fmode, convconfig, perm);
}
}
static VALUE
-rb_io_open_with_args(int argc, const VALUE *argv)
-{
- VALUE io;
-
- io = io_alloc(rb_cFile);
- rb_open_file(argc, argv, io);
- return io;
-}
-
-static VALUE
io_reopen(VALUE io, VALUE nfile)
{
rb_io_t *fptr, *orig;
@@ -6737,11 +7282,7 @@ io_reopen(VALUE io, VALUE nfile)
fptr->lineno = orig->lineno;
if (RTEST(orig->pathv)) fptr->pathv = orig->pathv;
else if (!IS_PREP_STDIO(fptr)) fptr->pathv = Qnil;
- fptr->finalize = orig->finalize;
-#if defined (__CYGWIN__) || !defined(HAVE_WORKING_FORK)
- if (fptr->finalize == pipe_finalize)
- pipe_add_fptr(fptr);
-#endif
+ fptr_copy_finalizer(fptr, orig);
fd = fptr->fd;
fd2 = orig->fd;
@@ -6796,12 +7337,13 @@ rb_freopen(VALUE fname, const char *mode, FILE *fp)
/*
* call-seq:
- * ios.reopen(other_IO) -> ios
- * ios.reopen(path, mode_str) -> ios
+ * ios.reopen(other_IO) -> ios
+ * ios.reopen(path, mode [, opt]) -> ios
*
* Reassociates <em>ios</em> with the I/O stream given in
* <i>other_IO</i> or to a new stream opened on <i>path</i>. This may
* dynamically change the actual class of this stream.
+ * The +mode+ and +opt+ parameters accept the same values as IO.open.
*
* f1 = File.new("testfile")
* f2 = File.new("testfile")
@@ -6920,11 +7462,7 @@ rb_io_init_copy(VALUE dest, VALUE io)
fptr->pid = orig->pid;
fptr->lineno = orig->lineno;
if (!NIL_P(orig->pathv)) fptr->pathv = orig->pathv;
- fptr->finalize = orig->finalize;
-#if defined (__CYGWIN__) || !defined(HAVE_WORKING_FORK)
- if (fptr->finalize == pipe_finalize)
- pipe_add_fptr(fptr);
-#endif
+ fptr_copy_finalizer(fptr, orig);
fd = ruby_dup(orig->fd);
fptr->fd = fd;
@@ -6993,18 +7531,20 @@ rb_f_printf(int argc, VALUE *argv)
/*
* call-seq:
- * ios.print() -> nil
+ * ios.print -> nil
* ios.print(obj, ...) -> nil
*
- * Writes the given object(s) to <em>ios</em>. The stream must be
- * opened for writing. If the output field separator (<code>$,</code>)
- * is not <code>nil</code>, it will be inserted between each object.
- * If the output record separator (<code>$\\</code>)
- * is not <code>nil</code>, it will be appended to the output. If no
- * arguments are given, prints <code>$_</code>. Objects that aren't
- * strings will be converted by calling their <code>to_s</code> method.
- * With no argument, prints the contents of the variable <code>$_</code>.
- * Returns <code>nil</code>.
+ * Writes the given object(s) to <em>ios</em>. Returns +nil+.
+ *
+ * The stream must be opened for writing.
+ * Each given object that isn't a string will be converted by calling
+ * its <code>to_s</code> method.
+ * When called without arguments, prints the contents of <code>$_</code>.
+ *
+ * If the output field separator (<code>$,</code>) is not +nil+,
+ * it is inserted between objects.
+ * If the output record separator (<code>$\\</code>) is not +nil+,
+ * it is appended to the output.
*
* $stdout.print("This is ", 100, " percent.\n")
*
@@ -7073,10 +7613,10 @@ rb_f_print(int argc, const VALUE *argv)
* ios.putc(obj) -> obj
*
* If <i>obj</i> is <code>Numeric</code>, write the character whose code is
- * the least-significant byte of <i>obj</i>, otherwise write the first byte
- * of the string representation of <i>obj</i> to <em>ios</em>. Note: This
- * method is not safe for use with multi-byte characters as it will truncate
- * them.
+ * the least-significant byte of <i>obj</i>.
+ * If <i>obj</i> is <code>String</code>, write the first character
+ * of <i>obj</i> to <em>ios</em>.
+ * Otherwise, raise <code>TypeError</code>.
*
* $stdout.putc "A"
* $stdout.putc 65
@@ -7109,8 +7649,8 @@ rb_io_putc(VALUE io, VALUE ch)
*
* $stdout.putc(int)
*
- * Refer to the documentation for IO#putc for important information regarding
- * multi-byte characters.
+ * Refer to the documentation for IO#putc for important information regarding
+ * multi-byte characters.
*/
static VALUE
@@ -7123,8 +7663,8 @@ rb_f_putc(VALUE recv, VALUE ch)
}
-static int
-str_end_with_asciichar(VALUE str, int c)
+int
+rb_str_end_with_asciichar(VALUE str, int c)
{
long len = RSTRING_LEN(str);
const char *ptr = RSTRING_PTR(str);
@@ -7162,13 +7702,17 @@ io_puts_ary(VALUE ary, VALUE out, int recur)
* call-seq:
* ios.puts(obj, ...) -> nil
*
- * Writes the given objects to <em>ios</em> as with
- * <code>IO#print</code>. Writes a record separator (typically a
- * newline) after any that do not already end with a newline sequence.
+ * Writes the given object(s) to <em>ios</em>.
+ * Writes a newline after any that do not already end
+ * with a newline sequence. Returns +nil+.
+ *
+ * The stream must be opened for writing.
* If called with an array argument, writes each element on a new line.
- * If called without arguments, outputs a single record separator.
+ * Each given object that isn't a string or array will be converted
+ * by calling its +to_s+ method.
+ * If called without arguments, outputs a single newline.
*
- * $stdout.puts("this", "is", "a", "test")
+ * $stdout.puts("this", "is", ["a", "test"])
*
* <em>produces:</em>
*
@@ -7176,13 +7720,16 @@ io_puts_ary(VALUE ary, VALUE out, int recur)
* is
* a
* test
+ *
+ * Note that +puts+ always uses newlines and is not affected
+ * by the output record separator (<code>$\\</code>).
*/
VALUE
rb_io_puts(int argc, const VALUE *argv, VALUE out)
{
- int i;
- VALUE line;
+ int i, n;
+ VALUE line, args[2];
/* if no argument given, print newline. */
if (argc == 0) {
@@ -7199,11 +7746,13 @@ rb_io_puts(int argc, const VALUE *argv, VALUE out)
}
line = rb_obj_as_string(argv[i]);
string:
- rb_io_write(out, line);
+ n = 0;
+ args[n++] = line;
if (RSTRING_LEN(line) == 0 ||
- !str_end_with_asciichar(line, '\n')) {
- rb_io_write(out, rb_default_rs);
+ !rb_str_end_with_asciichar(line, '\n')) {
+ args[n++] = rb_default_rs;
}
+ rb_io_writev(out, n, args);
}
return Qnil;
@@ -7230,15 +7779,15 @@ rb_f_puts(int argc, VALUE *argv, VALUE recv)
void
rb_p(VALUE obj) /* for debug print within C code */
{
- VALUE str = rb_obj_as_string(rb_inspect(obj));
+ VALUE args[2];
+ args[0] = rb_obj_as_string(rb_inspect(obj));
+ args[1] = rb_default_rs;
if (RB_TYPE_P(rb_stdout, T_FILE) &&
rb_method_basic_definition_p(CLASS_OF(rb_stdout), id_write)) {
- io_write(rb_stdout, str, 1);
- io_write(rb_stdout, rb_default_rs, 0);
+ io_writev(2, args, rb_stdout);
}
else {
- rb_io_write(rb_stdout, str);
- rb_io_write(rb_stdout, rb_default_rs);
+ rb_io_writev(rb_stdout, 2, args);
}
}
@@ -7328,21 +7877,27 @@ rb_obj_display(int argc, VALUE *argv, VALUE self)
{
VALUE out;
- if (argc == 0) {
- out = rb_stdout;
- }
- else {
- rb_scan_args(argc, argv, "01", &out);
- }
+ out = (!rb_check_arity(argc, 0, 1) ? rb_stdout : argv[0]);
rb_io_write(out, self);
return Qnil;
}
+static int
+rb_stderr_to_original_p(void)
+{
+ return (rb_stderr == orig_stderr || RFILE(orig_stderr)->fptr->fd < 0);
+}
+
void
rb_write_error2(const char *mesg, long len)
{
- if (rb_stderr == orig_stderr || RFILE(orig_stderr)->fptr->fd < 0) {
+ if (rb_stderr_to_original_p()) {
+#ifdef _WIN32
+ 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;
@@ -7363,7 +7918,7 @@ void
rb_write_error_str(VALUE mesg)
{
/* a stopgap measure for the time being */
- if (rb_stderr == orig_stderr || RFILE(orig_stderr)->fptr->fd < 0) {
+ if (rb_stderr_to_original_p()) {
size_t len = (size_t)RSTRING_LEN(mesg);
#ifdef _WIN32
if (isatty(fileno(stderr))) {
@@ -7381,6 +7936,14 @@ rb_write_error_str(VALUE mesg)
}
}
+int
+rb_stderr_tty_p(void)
+{
+ if (rb_stderr_to_original_p())
+ return isatty(fileno(stderr));
+ return 0;
+}
+
static void
must_respond_to(ID mid, VALUE val, ID id)
{
@@ -7525,8 +8088,8 @@ rb_io_make_open_file(VALUE obj)
* === Open Mode
*
* When +mode+ is an integer it must be combination of the modes defined in
- * File::Constants (+File::RDONLY+, +File::WRONLY | File::CREAT+). See the
- * open(2) man page for more information.
+ * File::Constants (+File::RDONLY+, <code>File::WRONLY|File::CREAT</code>).
+ * See the open(2) man page for more information.
*
* When +mode+ is a string it must be in one of the following forms:
*
@@ -7569,6 +8132,10 @@ rb_io_make_open_file(VALUE obj)
*
* "t" Text file mode
*
+ * The exclusive access mode ("x") can be used together with "w" to ensure
+ * the file is created. <code>Errno::EEXIST</code> is raised when it already
+ * exists. It may not be supported with all kinds of streams (e.g. pipes).
+ *
* When the open mode of original IO is read only, the mode cannot be
* changed to be writable. Similarly, the open mode cannot be changed from
* write only to readable.
@@ -7587,7 +8154,7 @@ rb_io_make_open_file(VALUE obj)
* converted from +int_enc+ to +ext_enc+ upon output. See Encoding for
* further details of transcoding on input and output.
*
- * If "BOM|UTF-8", "BOM|UTF-16LE" or "BOM|UTF16-BE" are used, ruby checks for
+ * If "BOM|UTF-8", "BOM|UTF-16LE" or "BOM|UTF16-BE" are used, 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. When present, the BOM
* is stripped and the external encoding from the BOM is used. When the BOM
@@ -7607,14 +8174,13 @@ rb_io_make_open_file(VALUE obj)
* If +mode+ parameter is given, this parameter will be bitwise-ORed.
*
* :\external_encoding ::
- * External encoding for the IO. "-" is a synonym for the default external
- * encoding.
+ * External encoding for the IO.
*
* :\internal_encoding ::
* Internal encoding for the IO. "-" is a synonym for the default internal
* encoding.
*
- * If the value is nil no conversion occurs.
+ * If the value is +nil+ no conversion occurs.
*
* :encoding ::
* Specifies external and internal encodings as "extern:intern".
@@ -7687,7 +8253,7 @@ rb_io_initialize(int argc, VALUE *argv, VALUE io)
oflags = fcntl(fd, F_GETFL);
if (oflags == -1) rb_sys_fail(0);
#else
- if (fstat(fd, &st) == -1) rb_sys_fail(0);
+ if (fstat(fd, &st) < 0) rb_sys_fail(0);
#endif
rb_update_max_fd(fd);
#if defined(HAVE_FCNTL) && defined(F_GETFL)
@@ -7734,6 +8300,11 @@ rb_io_initialize(int argc, VALUE *argv, VALUE io)
* mode and permission bits are platform dependent; on Unix systems, see
* open(2) and chmod(2) man pages for details.
*
+ * The new File object is buffered mode (or non-sync mode), unless
+ * +filename+ is a tty.
+ * See IO#flush, IO#fsync, IO#fdatasync, and <code>IO#sync=</code>
+ * about sync mode.
+ *
* === Examples
*
* f = File.new("testfile", "r")
@@ -7842,29 +8413,21 @@ argf_mark(void *ptr)
rb_gc_mark(p->filename);
rb_gc_mark(p->current_file);
rb_gc_mark(p->argv);
+ rb_gc_mark(p->inplace);
rb_gc_mark(p->encs.ecopts);
}
-static void
-argf_free(void *ptr)
-{
- struct argf *p = ptr;
- xfree(p->inplace);
- xfree(p);
-}
-
static size_t
argf_memsize(const void *ptr)
{
const struct argf *p = ptr;
size_t size = sizeof(*p);
- if (p->inplace) size += strlen(p->inplace) + 1;
return size;
}
static const rb_data_type_t argf_type = {
"ARGF",
- {argf_mark, argf_free, argf_memsize},
+ {argf_mark, RUBY_TYPED_DEFAULT_FREE, argf_memsize},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
@@ -7906,11 +8469,6 @@ argf_initialize_copy(VALUE argf, VALUE orig)
if (!OBJ_INIT_COPY(argf, orig)) return argf;
ARGF = argf_of(orig);
ARGF.argv = rb_obj_dup(ARGF.argv);
- if (ARGF.inplace) {
- const char *inplace = ARGF.inplace;
- ARGF.inplace = 0;
- ARGF.inplace = ruby_strdup(inplace);
- }
return argf;
}
@@ -7942,7 +8500,7 @@ argf_set_lineno(VALUE argf, VALUE val)
/*
* call-seq:
- * ARGF.lineno -> integer
+ * ARGF.lineno -> integer
*
* Returns the current line number of ARGF as a whole. This value
* can be set manually with +ARGF.lineno=+.
@@ -7985,7 +8543,7 @@ argf_close(VALUE argf)
if (RB_TYPE_P(file, T_FILE)) {
rb_io_set_write_io(file, Qnil);
}
- rb_funcall3(file, rb_intern("close"), 0, 0);
+ io_close(file);
ARGF.init_p = -1;
}
@@ -8022,11 +8580,13 @@ argf_next_argv(VALUE argf)
}
if (ARGF.next_p == 1) {
+ if (ARGF.init_p == 1) argf_close(argf);
retry:
if (RARRAY_LEN(ARGF.argv) > 0) {
VALUE filename = rb_ary_shift(ARGF.argv);
- StringValueCStr(filename);
- ARGF.filename = rb_str_encode_ospath(filename);
+ 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;
@@ -8052,10 +8612,14 @@ argf_next_argv(VALUE argf)
}
fstat(fr, &st);
str = filename;
- if (*ARGF.inplace) {
+ if (!NIL_P(ARGF.inplace)) {
+ VALUE suffix = ARGF.inplace;
str = rb_str_dup(str);
- rb_str_cat2(str, ARGF.inplace);
- /* TODO: encoding of ARGF.inplace */
+ 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));
@@ -8124,6 +8688,7 @@ argf_next_argv(VALUE argf)
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);
@@ -8209,9 +8774,9 @@ static VALUE argf_gets(int, VALUE *, VALUE);
/*
* call-seq:
- * gets(sep=$/) -> string or nil
- * gets(limit) -> string or nil
- * gets(sep,limit) -> string or nil
+ * gets(sep=$/ [, getline_args]) -> string or nil
+ * gets(limit [, getline_args]) -> string or nil
+ * gets(sep, limit [, getline_args]) -> string or nil
*
* Returns (and assigns to <code>$_</code>) the next line from the list
* of files in +ARGV+ (or <code>$*</code>), or from standard input if
@@ -8223,8 +8788,8 @@ static VALUE argf_gets(int, VALUE *, VALUE);
* divided by two consecutive newlines. If the first argument is an
* integer, or optional second argument is given, the returning string
* would not be longer than the given value in bytes. If multiple
- * filenames are present in +ARGV+, +gets(nil)+ will read the contents
- * one file at a time.
+ * filenames are present in +ARGV+, <code>gets(nil)</code> will read
+ * the contents one file at a time.
*
* ARGV << "testfile"
* print while gets
@@ -8251,18 +8816,21 @@ rb_f_gets(int argc, VALUE *argv, VALUE recv)
/*
* call-seq:
- * ARGF.gets(sep=$/) -> string or nil
- * ARGF.gets(limit) -> string or nil
- * ARGF.gets(sep, limit) -> string or nil
+ * ARGF.gets(sep=$/ [, getline_args]) -> string or nil
+ * 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+.
*
- * By default lines are assumed to be separated by +$/+; to use a different
- * character as a separator, supply it as a +String+ for the _sep_ argument.
+ * By default lines are assumed to be separated by <code>$/</code>;
+ * 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.
*
+ * See IO.readlines for details about getline_args.
+ *
*/
static VALUE
argf_gets(int argc, VALUE *argv, VALUE argf)
@@ -8331,10 +8899,11 @@ rb_f_readline(int argc, VALUE *argv, VALUE recv)
*
* Returns the next line from the current file in +ARGF+.
*
- * By default lines are assumed to be separated by +$/+; to use a different
- * character as a separator, supply it as a +String+ for the _sep_ argument.
+ * By default lines are assumed to be separated by <code>$/</code>;
+ * 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
+ * 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.
@@ -8358,9 +8927,9 @@ static VALUE argf_readlines(int, VALUE *, VALUE);
/*
* call-seq:
- * readlines(sep=$/) -> array
- * readlines(limit) -> array
- * readlines(sep,limit) -> array
+ * readlines(sep=$/) -> array
+ * readlines(limit) -> array
+ * readlines(sep, limit) -> array
*
* Returns an array containing the lines returned by calling
* <code>Kernel.gets(<i>sep</i>)</code> until the end of file.
@@ -8444,6 +9013,7 @@ rb_f_backquote(VALUE obj, VALUE str)
GetOpenFile(port, fptr);
result = read_all(fptr, remain_size(fptr), Qnil);
rb_io_close(port);
+ RFILE(port)->fptr = NULL;
rb_io_fptr_finalize(fptr);
rb_gc_force_recycle(port); /* also guards from premature GC */
@@ -8780,13 +9350,13 @@ rb_io_advise(int argc, VALUE *argv, VALUE io)
/*
* call-seq:
- * IO.select(read_array [, write_array [, error_array [, timeout]]]) -> array or nil
+ * IO.select(read_array [, write_array [, error_array [, timeout]]]) -> array or nil
*
* Calls select(2) system call.
* It monitors given arrays of <code>IO</code> objects, waits until one or more
* of <code>IO</code> objects are ready for reading, are ready for writing,
* and have pending exceptions respectively, and returns an array that
- * contains arrays of those IO objects. It will return <code>nil</code>
+ * contains arrays of those IO objects. It will return +nil+
* if optional <i>timeout</i> value is given and no <code>IO</code> object
* is ready in <i>timeout</i> seconds.
*
@@ -8837,7 +9407,7 @@ rb_io_advise(int argc, VALUE *argv, VALUE io)
* So, the remote side of SSL sends a partial record,
* <code>IO.select</code> notifies readability but
* <code>OpenSSL::SSL::SSLSocket</code> cannot decrypt a byte and
- * <code>OpenSSL::SSL::SSLSocket#readpartial</code> will blocks.
+ * <code>OpenSSL::SSL::SSLSocket#readpartial</code> will block.
*
* Also, the remote side can request SSL renegotiation which forces
* the local SSL engine to write some data.
@@ -8860,7 +9430,7 @@ rb_io_advise(int argc, VALUE *argv, VALUE io)
* However it is not the best way to use <code>IO.select</code>.
*
* The writability notified by select(2) doesn't show
- * how many bytes writable.
+ * how many bytes are writable.
* <code>IO#write</code> method blocks until given whole string is written.
* So, <code>IO#write(two or more bytes)</code> can block after writability is notified by <code>IO.select</code>.
* <code>IO#write_nonblock</code> is required to avoid the blocking.
@@ -9034,14 +9604,6 @@ typedef long fcntl_arg_t;
typedef int fcntl_arg_t;
#endif
-#if defined __native_client__ && !defined __GLIBC__
-// struct flock is currently missing the NaCl newlib headers
-// TODO(sbc): remove this once it gets added.
-#undef F_GETLK
-#undef F_SETLK
-#undef F_SETLKW
-#endif
-
static long
fcntl_narg_len(int cmd)
{
@@ -9297,11 +9859,17 @@ do_fcntl(int fd, int cmd, long narg)
arg.narg = narg;
retval = (int)rb_thread_io_blocking_region(nogvl_fcntl, &arg, fd);
+ if (retval != -1) {
+ switch (cmd) {
#if defined(F_DUPFD)
- if (retval != -1 && cmd == F_DUPFD) {
- rb_update_max_fd(retval);
- }
+ case F_DUPFD:
#endif
+#if defined(F_DUPFD_CLOEXEC)
+ case F_DUPFD_CLOEXEC:
+#endif
+ rb_update_max_fd(retval);
+ }
+ }
return retval;
}
@@ -9380,13 +9948,14 @@ rb_io_fcntl(int argc, VALUE *argv, VALUE io)
*
* hello
*
- *
* Calling +syscall+ on a platform which does not have any way to
* an arbitrary system function just fails with NotImplementedError.
*
- * Note::
- * +syscall+ is essentially unsafe and unportable. Feel free to shoot your foot.
- * DL (Fiddle) library is preferred for safer and a bit more portable programming.
+ * *Note:*
+ * +syscall+ is essentially unsafe and unportable.
+ * Feel free to shoot your foot.
+ * The DL (Fiddle) library is preferred for safer and a bit
+ * more portable programming.
*/
static VALUE
@@ -9655,7 +10224,7 @@ rb_io_s_pipe(int argc, VALUE *argv, VALUE klass)
VALUE ret;
argc = rb_scan_args(argc, argv, "02:", &v1, &v2, &opt);
- if (rb_pipe(pipes) == -1)
+ if (rb_pipe(pipes) < 0)
rb_sys_fail(0);
args[0] = klass;
@@ -9729,9 +10298,10 @@ struct foreach_arg {
};
static void
-open_key_args(int argc, VALUE *argv, VALUE opt, struct foreach_arg *arg)
+open_key_args(VALUE klass, int argc, VALUE *argv, VALUE opt, struct foreach_arg *arg)
{
VALUE path, v;
+ VALUE vmode = Qnil, vperm = Qnil;
path = *argv++;
argc--;
@@ -9740,29 +10310,18 @@ open_key_args(int argc, VALUE *argv, VALUE opt, struct foreach_arg *arg)
arg->argc = argc;
arg->argv = argv;
if (NIL_P(opt)) {
- arg->io = rb_io_open(path, INT2NUM(O_RDONLY), INT2FIX(0666), Qnil);
- return;
+ vmode = INT2NUM(O_RDONLY);
+ vperm = INT2FIX(0666);
}
- v = rb_hash_aref(opt, sym_open_args);
- if (!NIL_P(v)) {
- VALUE args;
- long n;
+ else if (!NIL_P(v = rb_hash_aref(opt, sym_open_args))) {
+ int n;
- v = rb_convert_type(v, T_ARRAY, "Array", "to_ary");
- n = RARRAY_LEN(v) + 1;
-#if SIZEOF_LONG > SIZEOF_INT
- if (n > INT_MAX) {
- rb_raise(rb_eArgError, "too many arguments");
- }
-#endif
- args = rb_ary_tmp_new(n);
- rb_ary_push(args, path);
- rb_ary_concat(args, v);
- arg->io = rb_io_open_with_args((int)n, RARRAY_CONST_PTR(args));
- rb_ary_clear(args); /* prevent from GC */
- return;
+ v = rb_to_array_type(v);
+ n = RARRAY_LENINT(v);
+ rb_check_arity(n, 0, 3); /* rb_io_open */
+ rb_scan_args(n, RARRAY_CONST_PTR(v), "02:", &vmode, &vperm, &opt);
}
- arg->io = rb_io_open(path, Qnil, Qnil, opt);
+ arg->io = rb_io_open(klass, path, vmode, vperm, opt);
}
static VALUE
@@ -9780,9 +10339,9 @@ io_s_foreach(struct getline_arg *arg)
/*
* call-seq:
- * IO.foreach(name, sep=$/ [, open_args]) {|line| block } -> nil
- * IO.foreach(name, limit [, open_args]) {|line| block } -> nil
- * IO.foreach(name, sep, limit [, open_args]) {|line| block } -> nil
+ * IO.foreach(name, sep=$/ [, getline_args, open_args]) {|line| block } -> nil
+ * IO.foreach(name, limit [, getline_args, open_args]) {|line| block } -> nil
+ * IO.foreach(name, sep, limit [, getline_args, open_args]) {|line| block } -> nil
* IO.foreach(...) -> an_enumerator
*
* Executes the block for every line in the named I/O port, where lines
@@ -9800,7 +10359,8 @@ io_s_foreach(struct getline_arg *arg)
* GOT And so on...
*
* If the last argument is a hash, it's the keyword argument to open.
- * See <code>IO.read</code> for detail.
+ * See IO.readlines for details about getline_args.
+ * And see also IO.read for details about open_args.
*
*/
@@ -9815,7 +10375,7 @@ rb_io_s_foreach(int argc, VALUE *argv, VALUE self)
argc = rb_scan_args(argc, argv, "13:", NULL, NULL, NULL, NULL, &opt);
RETURN_ENUMERATOR(self, orig_argc, argv);
extract_getline_args(argc-1, argv+1, &garg);
- open_key_args(argc, argv, opt, &arg);
+ open_key_args(self, argc, argv, opt, &arg);
if (NIL_P(arg.io)) return Qnil;
extract_getline_opts(opt, &garg);
check_getline_args(&garg.rs, &garg.limit, garg.io = arg.io);
@@ -9830,9 +10390,9 @@ io_s_readlines(struct getline_arg *arg)
/*
* call-seq:
- * IO.readlines(name, sep=$/ [, open_args]) -> array
- * IO.readlines(name, limit [, open_args]) -> array
- * IO.readlines(name, sep, limit [, open_args]) -> array
+ * IO.readlines(name, sep=$/ [, getline_args, open_args]) -> array
+ * IO.readlines(name, limit [, getline_args, open_args]) -> array
+ * IO.readlines(name, sep, limit [, getline_args, open_args]) -> array
*
* Reads the entire file specified by <i>name</i> as individual
* lines, and returns those lines in an array. Lines are separated by
@@ -9841,9 +10401,21 @@ io_s_readlines(struct getline_arg *arg)
* a = IO.readlines("testfile")
* a[0] #=> "This is line one\n"
*
+ * b = IO.readlines("testfile", chomp: true)
+ * b[0] #=> "This is line one"
+ *
* If the last argument is a hash, it's the keyword argument to open.
- * See <code>IO.read</code> for detail.
*
+ * === Options for getline
+ *
+ * The options hash accepts the following keys:
+ *
+ * :chomp::
+ * When the optional +chomp+ keyword argument has a true value,
+ * <code>\n</code>, <code>\r</code>, and <code>\r\n</code>
+ * will be removed from the end of each line.
+ *
+ * See also IO.read for details about open_args.
*/
static VALUE
@@ -9855,7 +10427,7 @@ rb_io_s_readlines(int argc, VALUE *argv, VALUE io)
argc = rb_scan_args(argc, argv, "13:", NULL, NULL, NULL, NULL, &opt);
extract_getline_args(argc-1, argv+1, &garg);
- open_key_args(argc, argv, opt, &arg);
+ open_key_args(io, argc, argv, opt, &arg);
if (NIL_P(arg.io)) return Qnil;
extract_getline_opts(opt, &garg);
check_getline_args(&garg.rs, &garg.limit, garg.io = arg.io);
@@ -9897,24 +10469,24 @@ seek_before_access(VALUE argp)
*
* The options hash accepts the following keys:
*
- * encoding::
+ * :encoding::
* string or encoding
*
- * Specifies the encoding of the read string. +encoding:+ will be ignored
+ * Specifies the encoding of the read string. +:encoding+ will be ignored
* if +length+ is specified. See Encoding.aliases for possible encodings.
*
- * mode::
- * string
+ * :mode::
+ * string or integer
*
- * Specifies the mode argument for open(). It must start with an "r"
- * otherwise it will cause an error. See IO.new for the list of possible
- * modes.
+ * Specifies the <i>mode</i> argument for open(). It must start
+ * with an "r", otherwise it will cause an error.
+ * See IO.new for the list of possible modes.
*
- * open_args::
- * array of strings
+ * :open_args::
+ * array
*
* Specifies arguments for open() as an array. This key can not be used
- * in combination with either +encoding:+ or +mode:+.
+ * in combination with either +:encoding+ or +:mode+.
*
* Examples:
*
@@ -9931,7 +10503,7 @@ rb_io_s_read(int argc, VALUE *argv, VALUE io)
struct foreach_arg arg;
argc = rb_scan_args(argc, argv, "13:", NULL, NULL, &offset, NULL, &opt);
- open_key_args(argc, argv, opt, &arg);
+ open_key_args(io, argc, argv, opt, &arg);
if (NIL_P(arg.io)) return Qnil;
if (!NIL_P(offset)) {
struct seek_arg sarg;
@@ -9980,7 +10552,7 @@ rb_io_s_binread(int argc, VALUE *argv, VALUE io)
rb_scan_args(argc, argv, "12", NULL, NULL, &offset);
FilePathValue(argv[0]);
convconfig.enc = rb_ascii8bit_encoding();
- arg.io = rb_io_open_generic(argv[0], oflags, fmode, &convconfig, 0);
+ arg.io = rb_io_open_generic(io, argv[0], oflags, fmode, &convconfig, 0);
if (NIL_P(arg.io)) return Qnil;
arg.argv = argv+1;
arg.argc = (argc > 1) ? 1 : 0;
@@ -10006,7 +10578,7 @@ io_s_write0(struct write_arg *arg)
}
static VALUE
-io_s_write(int argc, VALUE *argv, int binary)
+io_s_write(int argc, VALUE *argv, VALUE klass, int binary)
{
VALUE string, offset, opt;
struct foreach_arg arg;
@@ -10026,7 +10598,7 @@ io_s_write(int argc, VALUE *argv, int binary)
if (NIL_P(offset)) mode |= O_TRUNC;
rb_hash_aset(opt,sym_mode,INT2NUM(mode));
}
- open_key_args(argc,argv,opt,&arg);
+ open_key_args(klass, argc, argv, opt, &arg);
#ifndef O_BINARY
if (binary) rb_io_binmode_m(arg.io);
@@ -10055,63 +10627,67 @@ io_s_write(int argc, VALUE *argv, int binary)
/*
* call-seq:
- * IO.write(name, string, [offset] ) => integer
- * IO.write(name, string, [offset], open_args ) => integer
+ * IO.write(name, string [, offset]) -> integer
+ * IO.write(name, string [, offset] [, opt]) -> integer
*
* Opens the file, optionally seeks to the given <i>offset</i>, writes
* <i>string</i>, then returns the length written.
* <code>write</code> ensures the file is closed before returning.
- * If <i>offset</i> is not given, the file is truncated. Otherwise,
- * it is not truncated.
+ * If <i>offset</i> is not given in write mode, the file is truncated.
+ * Otherwise, it is not truncated.
*
- * If the last argument is a hash, it specifies option for internal
- * open(). The key would be the following. open_args: is exclusive
- * to others.
+ * IO.write("testfile", "0123456789", 20) #=> 10
+ * # File could contain: "This is line one\nThi0123456789two\nThis is line three\nAnd so on...\n"
+ * IO.write("testfile", "0123456789") #=> 10
+ * # File would now read: "0123456789"
*
- * encoding: string or encoding
+ * If the last argument is a hash, it specifies options for the internal
+ * open(). It accepts the following keys:
*
- * specifies encoding of the read string. encoding will be ignored
- * if length is specified.
+ * :encoding::
+ * string or encoding
*
- * mode: string
+ * Specifies the encoding of the read string.
+ * See Encoding.aliases for possible encodings.
*
- * specifies mode argument for open(). it should start with "w" or "a" or "r+"
- * otherwise it would cause error.
+ * :mode::
+ * string or integer
*
- * perm: integer
+ * Specifies the <i>mode</i> argument for open(). It must start
+ * with "w", "a", or "r+", otherwise it will cause an error.
+ * See IO.new for the list of possible modes.
*
- * specifies perm argument for open().
+ * :perm::
+ * integer
*
- * open_args: array
+ * Specifies the <i>perm</i> argument for open().
*
- * specifies arguments for open() as an array.
+ * :open_args::
+ * array
*
- * IO.write("testfile", "0123456789", 20) # => 10
- * # File could contain: "This is line one\nThi0123456789two\nThis is line three\nAnd so on...\n"
- * IO.write("testfile", "0123456789") #=> 10
- * # File would now read: "0123456789"
+ * Specifies arguments for open() as an array.
+ * This key can not be used in combination with other keys.
*/
static VALUE
rb_io_s_write(int argc, VALUE *argv, VALUE io)
{
- return io_s_write(argc, argv, 0);
+ return io_s_write(argc, argv, io, 0);
}
/*
* call-seq:
- * IO.binwrite(name, string, [offset] ) => integer
- * IO.binwrite(name, string, [offset], open_args ) => integer
+ * IO.binwrite(name, string, [offset] ) -> integer
+ * IO.binwrite(name, string, [offset], open_args ) -> integer
*
* Same as <code>IO.write</code> except opening the file in binary mode
* and ASCII-8BIT encoding ("wb:ASCII-8BIT").
- *
*/
static VALUE
rb_io_s_binwrite(int argc, VALUE *argv, VALUE io)
{
- return io_s_write(argc, argv, 1);
+ return io_s_write(argc, argv, io, 1);
}
struct copy_stream_struct {
@@ -10122,13 +10698,12 @@ struct copy_stream_struct {
int src_fd;
int dst_fd;
- int close_src;
- int close_dst;
+ unsigned close_src : 1;
+ unsigned close_dst : 1;
+ int error_no;
off_t total;
const char *syserr;
- int error_no;
const char *notimp;
- rb_fdset_t fds;
VALUE th;
};
@@ -10165,15 +10740,23 @@ maygvl_copy_stream_continue_p(int has_gvl, struct copy_stream_struct *stp)
}
/* non-Linux poll may not work on all FDs */
-#if defined(HAVE_POLL) && defined(__linux__)
-# define USE_POLL 1
-# define IOWAIT_SYSCALL "poll"
-#else
-# define IOWAIT_SYSCALL "select"
+#if defined(HAVE_POLL)
+# if defined(__linux__)
+# define USE_POLL 1
+# endif
+# if defined(__FreeBSD_version) && __FreeBSD_version >= 1100000
+# define USE_POLL 1
+# endif
+#endif
+
+#ifndef USE_POLL
# define USE_POLL 0
#endif
#if USE_POLL
+# define IOWAIT_SYSCALL "poll"
+STATIC_ASSERT(pollin_expected, POLLIN == RB_WAITFD_IN);
+STATIC_ASSERT(pollout_expected, POLLOUT == RB_WAITFD_OUT);
static int
nogvl_wait_for_single_fd(int fd, short events)
{
@@ -10182,8 +10765,35 @@ nogvl_wait_for_single_fd(int fd, short events)
fds.fd = fd;
fds.events = events;
- return poll(&fds, 1, 0);
+ return poll(&fds, 1, -1);
}
+#else /* !USE_POLL */
+# include "vm_core.h"
+# define IOWAIT_SYSCALL "select"
+static int
+nogvl_wait_for_single_fd(int fd, short events)
+{
+ rb_fdset_t fds;
+ int ret;
+
+ rb_fd_init(&fds);
+ rb_fd_set(fd, &fds);
+
+ switch (events) {
+ case RB_WAITFD_IN:
+ ret = rb_fd_select(fd + 1, &fds, 0, 0, 0);
+ break;
+ case RB_WAITFD_OUT:
+ ret = rb_fd_select(fd + 1, 0, &fds, 0, 0);
+ break;
+ default:
+ VM_UNREACHABLE(nogvl_wait_for_single_fd);
+ }
+
+ rb_fd_term(&fds);
+ return ret;
+}
+#endif /* !USE_POLL */
static int
maygvl_copy_stream_wait_read(int has_gvl, struct copy_stream_struct *stp)
@@ -10195,69 +10805,156 @@ maygvl_copy_stream_wait_read(int has_gvl, struct copy_stream_struct *stp)
ret = rb_wait_for_single_fd(stp->src_fd, RB_WAITFD_IN, NULL);
}
else {
- ret = nogvl_wait_for_single_fd(stp->src_fd, POLLIN);
+ ret = nogvl_wait_for_single_fd(stp->src_fd, RB_WAITFD_IN);
}
- } while (ret == -1 && maygvl_copy_stream_continue_p(has_gvl, stp));
+ } while (ret < 0 && maygvl_copy_stream_continue_p(has_gvl, stp));
- if (ret == -1) {
- stp->syserr = "poll";
+ if (ret < 0) {
+ stp->syserr = IOWAIT_SYSCALL;
stp->error_no = errno;
- return -1;
+ return ret;
}
return 0;
}
-#else /* !USE_POLL */
-static int
-maygvl_select(int has_gvl, int n, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct timeval *timeout)
-{
- if (has_gvl)
- return rb_thread_fd_select(n, rfds, wfds, efds, timeout);
- else
- return rb_fd_select(n, rfds, wfds, efds, timeout);
-}
static int
-maygvl_copy_stream_wait_read(int has_gvl, struct copy_stream_struct *stp)
+nogvl_copy_stream_wait_write(struct copy_stream_struct *stp)
{
int ret;
do {
- rb_fd_zero(&stp->fds);
- rb_fd_set(stp->src_fd, &stp->fds);
- ret = maygvl_select(has_gvl, rb_fd_max(&stp->fds), &stp->fds, NULL, NULL, NULL);
- } while (ret == -1 && maygvl_copy_stream_continue_p(has_gvl, stp));
+ ret = nogvl_wait_for_single_fd(stp->dst_fd, RB_WAITFD_OUT);
+ } while (ret < 0 && maygvl_copy_stream_continue_p(0, stp));
- if (ret == -1) {
- stp->syserr = "select";
+ if (ret < 0) {
+ stp->syserr = IOWAIT_SYSCALL;
stp->error_no = errno;
- return -1;
+ return ret;
}
return 0;
}
-#endif /* !USE_POLL */
+
+#if defined __linux__ && defined __NR_copy_file_range
+# define USE_COPY_FILE_RANGE
+#endif
+
+#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)
+{
+ return syscall(__NR_copy_file_range, in_fd, in_offset, out_fd, out_offset, count, flags);
+}
static int
-nogvl_copy_stream_wait_write(struct copy_stream_struct *stp)
+nogvl_copy_file_range(struct copy_stream_struct *stp)
{
+ struct stat sb;
+ ssize_t ss;
+ off_t src_size;
int ret;
+ off_t copy_length, src_offset, *src_offset_ptr;
- do {
-#if USE_POLL
- ret = nogvl_wait_for_single_fd(stp->dst_fd, POLLOUT);
-#else
- rb_fd_zero(&stp->fds);
- rb_fd_set(stp->dst_fd, &stp->fds);
- ret = rb_fd_select(rb_fd_max(&stp->fds), NULL, &stp->fds, NULL, NULL);
+ ret = fstat(stp->src_fd, &sb);
+ if (ret < 0) {
+ stp->syserr = "fstat";
+ stp->error_no = errno;
+ return ret;
+ }
+ if (!S_ISREG(sb.st_mode))
+ return 0;
+
+ src_size = sb.st_size;
+ ret = fstat(stp->dst_fd, &sb);
+ if (ret < 0) {
+ stp->syserr = "fstat";
+ stp->error_no = errno;
+ return ret;
+ }
+
+ src_offset = stp->src_offset;
+ if (src_offset >= (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 */
+ }
+
+ copy_length = stp->copy_length;
+ if (copy_length < (off_t)0) {
+ if (src_offset < (off_t)0) {
+ off_t current_offset;
+ errno = 0;
+ current_offset = lseek(stp->src_fd, 0, SEEK_CUR);
+ if (current_offset < (off_t)0 && errno) {
+ stp->syserr = "lseek";
+ stp->error_no = errno;
+ return (int)current_offset;
+ }
+ copy_length = src_size - current_offset;
+ }
+ 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;
+# else
+ ss = (ssize_t)copy_length;
+# endif
+ ss = simple_copy_file_range(stp->src_fd, src_offset_ptr, stp->dst_fd, NULL, ss, 0);
+ if (0 < ss) {
+ stp->total += ss;
+ copy_length -= ss;
+ if (0 < copy_length) {
+ goto retry_copy_file_range;
+ }
+ }
+ if (ss < 0) {
+ 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) */
+#ifdef ENOSYS
+ case ENOSYS:
+#endif
+#ifdef EXDEV
+ case EXDEV: /* in_fd and out_fd are not on the same filesystem */
#endif
- } while (ret == -1 && maygvl_copy_stream_continue_p(0, stp));
+ return 0;
+ case EAGAIN:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ 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_fd, F_GETFL);
- if (ret == -1) {
- stp->syserr = IOWAIT_SYSCALL;
+ if (flags != -1 && flags & O_APPEND) {
+ return 0;
+ }
+ errno = e;
+ }
+ }
+ stp->syserr = "copy_file_range";
stp->error_no = errno;
- return -1;
+ return (int)ss;
}
- return 0;
+ return 1;
}
+#endif
#ifdef HAVE_SENDFILE
@@ -10292,7 +10989,7 @@ simple_sendfile(int out_fd, int in_fd, off_t *offset, off_t count)
# else
r = sendfile(in_fd, out_fd, pos, (size_t)count, NULL, &sbytes, 0);
# endif
- if (r != 0 && sbytes == 0) return -1;
+ if (r != 0 && sbytes == 0) return r;
if (offset) {
*offset += sbytes;
}
@@ -10310,51 +11007,52 @@ simple_sendfile(int out_fd, int in_fd, off_t *offset, off_t count)
static int
nogvl_copy_stream_sendfile(struct copy_stream_struct *stp)
{
- struct stat src_stat, dst_stat;
+ struct stat sb;
ssize_t ss;
int ret;
-
+ off_t src_size;
off_t copy_length;
off_t src_offset;
int use_pread;
- ret = fstat(stp->src_fd, &src_stat);
- if (ret == -1) {
+ ret = fstat(stp->src_fd, &sb);
+ if (ret < 0) {
stp->syserr = "fstat";
stp->error_no = errno;
- return -1;
+ return ret;
}
- if (!S_ISREG(src_stat.st_mode))
+ if (!S_ISREG(sb.st_mode))
return 0;
- ret = fstat(stp->dst_fd, &dst_stat);
- if (ret == -1) {
+ src_size = sb.st_size;
+ ret = fstat(stp->dst_fd, &sb);
+ if (ret < 0) {
stp->syserr = "fstat";
stp->error_no = errno;
- return -1;
+ return ret;
}
#ifndef __linux__
- if ((dst_stat.st_mode & S_IFMT) != S_IFSOCK)
+ if ((sb.st_mode & S_IFMT) != S_IFSOCK)
return 0;
#endif
src_offset = stp->src_offset;
- use_pread = src_offset != (off_t)-1;
+ use_pread = src_offset >= (off_t)0;
copy_length = stp->copy_length;
- if (copy_length == (off_t)-1) {
+ if (copy_length < (off_t)0) {
if (use_pread)
- copy_length = src_stat.st_size - src_offset;
+ copy_length = src_size - src_offset;
else {
off_t cur;
errno = 0;
cur = lseek(stp->src_fd, 0, SEEK_CUR);
- if (cur == (off_t)-1 && errno) {
+ if (cur < (off_t)0 && errno) {
stp->syserr = "lseek";
stp->error_no = errno;
- return -1;
+ return (int)cur;
}
- copy_length = src_stat.st_size - cur;
+ copy_length = src_size - cur;
}
}
@@ -10378,7 +11076,7 @@ nogvl_copy_stream_sendfile(struct copy_stream_struct *stp)
goto retry_sendfile;
}
}
- if (ss == -1) {
+ if (ss < 0) {
if (maygvl_copy_stream_continue_p(0, stp))
goto retry_sendfile;
switch (errno) {
@@ -10391,24 +11089,27 @@ nogvl_copy_stream_sendfile(struct copy_stream_struct *stp)
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
+ {
+ int ret;
#ifndef __linux__
- /*
- * Linux requires stp->src_fd to be a mmap-able (regular) file,
- * select() reports regular files to always be "ready", so
- * there is no need to select() on it.
- * Other OSes may have the same limitation for sendfile() which
- * allow us to bypass maygvl_copy_stream_wait_read()...
- */
- if (maygvl_copy_stream_wait_read(0, stp) == -1)
- return -1;
-#endif
- if (nogvl_copy_stream_wait_write(stp) == -1)
- return -1;
+ /*
+ * Linux requires stp->src_fd to be a mmap-able (regular) file,
+ * select() reports regular files to always be "ready", so
+ * there is no need to select() on it.
+ * Other OSes may have the same limitation for sendfile() which
+ * allow us to bypass maygvl_copy_stream_wait_read()...
+ */
+ ret = maygvl_copy_stream_wait_read(0, stp);
+ if (ret < 0) return ret;
+#endif
+ ret = nogvl_copy_stream_wait_write(stp);
+ if (ret < 0) return ret;
+ }
goto retry_sendfile;
}
stp->syserr = "sendfile";
stp->error_no = errno;
- return -1;
+ return (int)ss;
}
return 1;
}
@@ -10428,7 +11129,7 @@ maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf,
{
ssize_t ss;
retry_read:
- if (offset == (off_t)-1) {
+ if (offset < (off_t)0) {
ss = maygvl_read(has_gvl, stp->src_fd, buf, len);
}
else {
@@ -10442,7 +11143,7 @@ maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf,
if (ss == 0) {
return 0;
}
- if (ss == -1) {
+ if (ss < 0) {
if (maygvl_copy_stream_continue_p(has_gvl, stp))
goto retry_read;
switch (errno) {
@@ -10450,18 +11151,19 @@ maygvl_copy_stream_read(int has_gvl, struct copy_stream_struct *stp, char *buf,
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
- if (maygvl_copy_stream_wait_read(has_gvl, stp) == -1)
- return -1;
+ {
+ int ret = maygvl_copy_stream_wait_read(has_gvl, stp);
+ if (ret < 0) return ret;
+ }
goto retry_read;
#ifdef ENOSYS
case ENOSYS:
stp->notimp = "pread";
- return -1;
+ return ss;
#endif
}
- stp->syserr = offset == (off_t)-1 ? "read" : "pread";
+ stp->syserr = offset < (off_t)0 ? "read" : "pread";
stp->error_no = errno;
- return -1;
}
return ss;
}
@@ -10473,17 +11175,17 @@ nogvl_copy_stream_write(struct copy_stream_struct *stp, char *buf, size_t len)
int off = 0;
while (len) {
ss = write(stp->dst_fd, buf+off, len);
- if (ss == -1) {
- if (maygvl_copy_stream_continue_p(0, stp))
- continue;
+ if (ss < 0) {
+ if (maygvl_copy_stream_continue_p(0, stp))
+ continue;
if (errno == EAGAIN || errno == EWOULDBLOCK) {
- if (nogvl_copy_stream_wait_write(stp) == -1)
- return -1;
+ int ret = nogvl_copy_stream_wait_write(stp);
+ if (ret < 0) return ret;
continue;
}
stp->syserr = "write";
stp->error_no = errno;
- return -1;
+ return (int)ss;
}
off += (int)ss;
len -= (int)ss;
@@ -10505,15 +11207,15 @@ nogvl_copy_stream_read_write(struct copy_stream_struct *stp)
int use_pread;
copy_length = stp->copy_length;
- use_eof = copy_length == (off_t)-1;
+ use_eof = copy_length < (off_t)0;
src_offset = stp->src_offset;
- use_pread = src_offset != (off_t)-1;
+ use_pread = src_offset >= (off_t)0;
if (use_pread && stp->close_src) {
off_t r;
errno = 0;
r = lseek(stp->src_fd, src_offset, SEEK_SET);
- if (r == (off_t)-1 && errno) {
+ if (r < (off_t)0 && errno) {
stp->syserr = "lseek";
stp->error_no = errno;
return;
@@ -10553,10 +11255,16 @@ static void *
nogvl_copy_stream_func(void *arg)
{
struct copy_stream_struct *stp = (struct copy_stream_struct *)arg;
-#ifdef USE_SENDFILE
+#if defined(USE_SENDFILE) || defined(USE_COPY_FILE_RANGE)
int ret;
#endif
+#ifdef USE_COPY_FILE_RANGE
+ ret = nogvl_copy_file_range(stp);
+ if (ret != 0)
+ goto finish; /* error or success */
+#endif
+
#ifdef USE_SENDFILE
ret = nogvl_copy_stream_sendfile(stp);
if (ret != 0)
@@ -10565,7 +11273,7 @@ nogvl_copy_stream_func(void *arg)
nogvl_copy_stream_read_write(stp);
-#ifdef USE_SENDFILE
+#if defined(USE_SENDFILE) || defined(USE_COPY_FILE_RANGE)
finish:
#endif
return 0;
@@ -10582,7 +11290,7 @@ copy_stream_fallback_body(VALUE arg)
off_t off = stp->src_offset;
ID read_method = id_readpartial;
- if (stp->src_fd == -1) {
+ if (stp->src_fd < 0) {
if (!rb_respond_to(stp->src, read_method)) {
read_method = id_read;
}
@@ -10591,15 +11299,17 @@ copy_stream_fallback_body(VALUE arg)
while (1) {
long numwrote;
long l;
- if (stp->copy_length == (off_t)-1) {
+ if (stp->copy_length < (off_t)0) {
l = buflen;
}
else {
- if (rest == 0)
- break;
+ if (rest == 0) {
+ rb_str_resize(buf, 0);
+ break;
+ }
l = buflen < rest ? buflen : (long)rest;
}
- if (stp->src_fd == -1) {
+ if (stp->src_fd < 0) {
VALUE rc = rb_funcall(stp->src, read_method, 2, INT2FIX(l), buf);
if (read_method == id_read && NIL_P(rc))
@@ -10610,11 +11320,11 @@ copy_stream_fallback_body(VALUE arg)
rb_str_resize(buf, buflen);
ss = maygvl_copy_stream_read(1, stp, RSTRING_PTR(buf), l, off);
rb_str_resize(buf, ss > 0 ? ss : 0);
- if (ss == -1)
+ if (ss < 0)
return Qnil;
if (ss == 0)
rb_eof_error();
- if (off != (off_t)-1)
+ if (off >= (off_t)0)
off += ss;
}
n = rb_io_write(stp->dst, buf);
@@ -10632,7 +11342,7 @@ copy_stream_fallback_body(VALUE arg)
static VALUE
copy_stream_fallback(struct copy_stream_struct *stp)
{
- if (stp->src_fd == -1 && stp->src_offset != (off_t)-1) {
+ if (stp->src_fd < 0 && stp->src_offset >= (off_t)0) {
rb_raise(rb_eArgError, "cannot specify src_offset for non-IO");
}
rb_rescue2(copy_stream_fallback_body, (VALUE)stp,
@@ -10722,10 +11432,10 @@ copy_stream_body(VALUE arg)
if (dst_fptr)
io_ascii8bit_binmode(dst_fptr);
- if (stp->src_offset == (off_t)-1 && src_fptr && src_fptr->rbuf.len) {
+ if (stp->src_offset < (off_t)0 && src_fptr && src_fptr->rbuf.len) {
size_t len = src_fptr->rbuf.len;
VALUE str;
- if (stp->copy_length != (off_t)-1 && stp->copy_length < (off_t)len) {
+ if (stp->copy_length >= (off_t)0 && stp->copy_length < (off_t)len) {
len = (size_t)stp->copy_length;
}
str = rb_str_buf_new(len);
@@ -10737,8 +11447,9 @@ copy_stream_body(VALUE arg)
}
else /* others such as StringIO */
rb_io_write(dst_io, str);
+ rb_str_resize(str, 0);
stp->total += len;
- if (stp->copy_length != (off_t)-1)
+ if (stp->copy_length >= (off_t)0)
stp->copy_length -= len;
}
@@ -10749,13 +11460,10 @@ copy_stream_body(VALUE arg)
if (stp->copy_length == 0)
return Qnil;
- if (src_fd == -1 || dst_fd == -1) {
+ if (src_fd < 0 || dst_fd < 0) {
return copy_stream_fallback(stp);
}
- rb_fd_set(src_fd, &stp->fds);
- rb_fd_set(dst_fd, &stp->fds);
-
rb_thread_call_without_gvl(nogvl_copy_stream_func, (void*)stp, RUBY_UBF_IO, 0);
return Qnil;
}
@@ -10770,7 +11478,6 @@ copy_stream_finalize(VALUE arg)
if (stp->close_dst) {
rb_io_close_m(stp->dst);
}
- rb_fd_term(&stp->fds);
if (stp->syserr) {
rb_syserr_fail(stp->error_no, stp->syserr);
}
@@ -10787,7 +11494,12 @@ copy_stream_finalize(VALUE arg)
* IO.copy_stream(src, dst, copy_length, src_offset)
*
* IO.copy_stream copies <i>src</i> to <i>dst</i>.
- * <i>src</i> and <i>dst</i> is either a filename or an IO.
+ * <i>src</i> and <i>dst</i> is either a filename or an IO-like object.
+ * IO-like object for <i>src</i> should have <code>readpartial</code> or
+ * <code>read</code> method.
+ * IO-like object for <i>dst</i> should have <code>write</code> method.
+ * (Specialized mechanisms, such as sendfile system call, may be used
+ * on appropriate situation.)
*
* This method returns the number of bytes copied.
*
@@ -10831,7 +11543,6 @@ rb_io_s_copy_stream(int argc, VALUE *argv, VALUE io)
else
st.src_offset = NUM2OFFT(src_offset);
- rb_fd_init(&st.fds);
rb_ensure(copy_stream_body, (VALUE)&st, copy_stream_finalize, (VALUE)&st);
return OFFT2NUM(st.total);
@@ -10842,7 +11553,7 @@ rb_io_s_copy_stream(int argc, VALUE *argv, VALUE io)
* io.external_encoding -> encoding
*
* Returns the Encoding object that represents the encoding of the file.
- * If io is write mode and no encoding is specified, returns <code>nil</code>.
+ * If _io_ is in write mode and no encoding is specified, returns +nil+.
*/
static VALUE
@@ -10867,7 +11578,7 @@ rb_io_external_encoding(VALUE io)
* io.internal_encoding -> encoding
*
* Returns the Encoding of the internal string if conversion is
- * specified. Otherwise returns nil.
+ * specified. Otherwise returns +nil+.
*/
static VALUE
@@ -10925,6 +11636,12 @@ rb_stdio_set_default_encoding(void)
rb_io_set_encoding(1, &val, rb_stderr);
}
+static inline int
+global_argf_p(VALUE arg)
+{
+ return arg == argf;
+}
+
/*
* call-seq:
* ARGF.external_encoding -> encoding
@@ -10936,7 +11653,7 @@ rb_stdio_set_default_encoding(void)
*
* To set the external encoding use +ARGF.set_encoding+.
*
- * For example:
+ * For example:
*
* ARGF.external_encoding #=> #<Encoding:UTF-8>
*
@@ -10961,7 +11678,7 @@ argf_external_encoding(VALUE argf)
* 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
- * unknown, nil is returned.
+ * unknown, +nil+ is returned.
*/
static VALUE
argf_internal_encoding(VALUE argf)
@@ -10994,7 +11711,7 @@ argf_internal_encoding(VALUE argf)
*
* If the external encoding and the internal encoding are specified, the
* optional +Hash+ argument can be used to adjust the conversion process. The
- * structure of this hash is explained in the +String#encode+ documentation.
+ * structure of this hash is explained in the String#encode documentation.
*
* For example:
*
@@ -11041,10 +11758,10 @@ argf_tell(VALUE argf)
/*
* call-seq:
- * ARGF.seek(amount, whence=IO::SEEK_SET) -> 0
+ * ARGF.seek(amount, whence=IO::SEEK_SET) -> 0
*
* Seeks to offset _amount_ (an +Integer+) in the +ARGF+ stream according to
- * the value of _whence_. See +IO#seek+ for further details.
+ * the value of _whence_. See IO#seek for further details.
*/
static VALUE
argf_seek_m(int argc, VALUE *argv, VALUE argf)
@@ -11092,11 +11809,19 @@ argf_set_pos(VALUE argf, VALUE offset)
static VALUE
argf_rewind(VALUE argf)
{
+ VALUE ret;
+ int old_lineno;
+
if (!next_argv()) {
rb_raise(rb_eArgError, "no stream to rewind");
}
ARGF_FORWARD(0, 0);
- return rb_io_rewind(ARGF.current_file);
+ 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;
+ }
+ return ret;
}
/*
@@ -11181,22 +11906,28 @@ argf_eof(VALUE argf)
* called without arguments the contents of this pseudo file are returned in
* their entirety.
*
- * _length_ must be a non-negative integer or nil. If it is a positive
- * integer, +read+ tries to read at most _length_ bytes. It returns nil
- * if an EOF was encountered before anything could be read. Fewer than
- * _length_ bytes may be returned if an EOF is encountered during the read.
+ * _length_ must be a non-negative integer or +nil+.
*
- * If _length_ is omitted or is _nil_, it reads until EOF. A String is
- * returned even if EOF is encountered before any data is read.
+ * If _length_ is a positive integer, +read+ tries to read
+ * _length_ bytes without any conversion (binary mode).
+ * It returns +nil+ if an EOF is encountered before anything can be read.
+ * Fewer than _length_ bytes are returned if an EOF is encountered during
+ * the read.
+ * In the case of an integer _length_, the resulting string is always
+ * in ASCII-8BIT encoding.
*
- * If _length_ is zero, it returns _""_.
+ * If _length_ is omitted or is +nil+, it reads until EOF
+ * and the encoding conversion is applied, if applicable.
+ * A string is returned even if EOF is encountered before any data is read.
*
- * If the optional _outbuf_ argument is present, it must reference a String,
- * which will receive the data.
- * The <i>outbuf</i> will contain only the received data after the method call
+ * If _length_ is zero, it returns an empty string (<code>""</code>).
+ *
+ * If the optional _outbuf_ argument is present,
+ * it must reference a String, which will receive the data.
+ * The _outbuf_ will contain only the received data after the method call
* even if it is not empty at the beginning.
*
- * For example:
+ * For example:
*
* $ echo "small" > small.txt
* $ echo "large" > large.txt
@@ -11207,8 +11938,11 @@ argf_eof(VALUE argf)
* ARGF.read(2) #=> "sm"
* ARGF.read(0) #=> ""
*
- * Note that this method behaves like fread() function in C. If you need the
- * behavior like read(2) system call, consider +ARGF.readpartial+.
+ * Note that this method behaves like the fread() function in C.
+ * This means it retries to invoke read(2) system calls to read data
+ * with the specified length.
+ * If you need the behavior like a single read(2) system call,
+ * consider ARGF#readpartial or ARGF#read_nonblock.
*/
static VALUE
@@ -11283,7 +12017,7 @@ static VALUE argf_getpartial(int argc, VALUE *argv, VALUE argf, VALUE opts,
*
* If the optional _outbuf_ argument is present,
* it must reference a String, which will receive the data.
- * The <i>outbuf</i> will contain only the received data after the method call
+ * The _outbuf_ will contain only the received data after the method call
* even if it is not empty at the beginning.
*
* It raises <code>EOFError</code> on end of ARGF stream.
@@ -11302,8 +12036,8 @@ argf_readpartial(int argc, VALUE *argv, VALUE argf)
/*
* call-seq:
- * ARGF.read_nonblock(maxlen) -> string
- * ARGF.read_nonblock(maxlen, outbuf) -> outbuf
+ * ARGF.read_nonblock(maxlen[, options]) -> string
+ * ARGF.read_nonblock(maxlen, outbuf[, options]) -> outbuf
*
* Reads at most _maxlen_ bytes from the ARGF stream in non-blocking mode.
*/
@@ -11541,15 +12275,31 @@ argf_block_call(ID mid, int argc, VALUE *argv, VALUE argf)
if (ret != Qundef) 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;
+ }
+ return argf_block_call_i(i, argf, argc, argv, blockarg);
+}
+
+static void
+argf_block_call_line(ID mid, int argc, VALUE *argv, VALUE argf)
+{
+ VALUE ret = rb_block_call(ARGF.current_file, mid, argc, argv, argf_block_call_line_i, argf);
+ if (ret != Qundef) ARGF.next_p = 1;
+}
+
/*
* call-seq:
- * ARGF.each(sep=$/) {|line| block } -> ARGF
- * ARGF.each(sep=$/,limit) {|line| block } -> ARGF
- * ARGF.each(...) -> an_enumerator
+ * ARGF.each(sep=$/) {|line| block } -> ARGF
+ * ARGF.each(sep=$/, limit) {|line| block } -> ARGF
+ * ARGF.each(...) -> an_enumerator
*
- * ARGF.each_line(sep=$/) {|line| block } -> ARGF
- * ARGF.each_line(sep=$/,limit) {|line| block } -> ARGF
- * ARGF.each_line(...) -> an_enumerator
+ * ARGF.each_line(sep=$/) {|line| block } -> ARGF
+ * ARGF.each_line(sep=$/, limit) {|line| block } -> ARGF
+ * ARGF.each_line(...) -> an_enumerator
*
* Returns an enumerator which iterates over each line (separated by _sep_,
* which defaults to your platform's newline character) of each file in
@@ -11562,13 +12312,21 @@ argf_block_call(ID mid, int argc, VALUE *argv, VALUE argf)
* 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 and line number, respectively, of the
- * current line.
+ * 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:
*
* ARGF.each_line do |line|
+ * puts ARGF.filename if ARGF.file.lineno == 1
+ * puts "#{ARGF.file.lineno}: #{line}"
+ * end
+ *
+ * While the following code prints only the first file's name at first, and
+ * the contents with line number counted through all named files.
+ *
+ * ARGF.each_line do |line|
* puts ARGF.filename if ARGF.lineno == 1
* puts "#{ARGF.lineno}: #{line}"
* end
@@ -11578,7 +12336,7 @@ argf_each_line(int argc, VALUE *argv, VALUE argf)
{
RETURN_ENUMERATOR(argf, argc, argv);
FOREACH_ARGF() {
- argf_block_call(rb_intern("each_line"), argc, argv, argf);
+ argf_block_call_line(rb_intern("each_line"), argc, argv, argf);
}
return argf;
}
@@ -11615,7 +12373,7 @@ argf_lines(int argc, VALUE *argv, VALUE argf)
*
* If no block is given, an enumerator is returned instead.
*
- * For example:
+ * For example:
*
* ARGF.bytes.to_a #=> [35, 32, ... 95, 10]
*
@@ -11645,8 +12403,8 @@ argf_bytes(VALUE argf)
/*
* call-seq:
- * ARGF.each_char {|char| block } -> ARGF
- * ARGF.each_char -> an_enumerator
+ * ARGF.each_char {|char| block } -> ARGF
+ * ARGF.each_char -> an_enumerator
*
* Iterates over each character of each file in +ARGF+.
*
@@ -11684,8 +12442,8 @@ argf_chars(VALUE argf)
/*
* call-seq:
- * ARGF.each_codepoint {|codepoint| block } -> ARGF
- * ARGF.each_codepoint -> an_enumerator
+ * ARGF.each_codepoint {|codepoint| block } -> ARGF
+ * ARGF.each_codepoint -> an_enumerator
*
* Iterates over each codepoint of each file in +ARGF+.
*
@@ -11760,8 +12518,8 @@ 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. #<IO:<STDIN>> is
- * returned when the current file is STDIN.
+ * Returns the current file as an +IO+ or +File+ object.
+ * <code>$stdin</code> is returned when the current file is STDIN.
*
* For example:
*
@@ -11806,10 +12564,10 @@ 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:
+ * For example:
*
* ARGF.binmode? #=> false
* ARGF.binmode
@@ -11828,7 +12586,7 @@ argf_binmode_p(VALUE argf)
* Sets the current file to the next file in ARGV. If there aren't any more
* files it has no effect.
*
- * For example:
+ * For example:
*
* $ ruby argf.rb foo bar
* ARGF.filename #=> "foo"
@@ -11849,11 +12607,11 @@ argf_skip(VALUE argf)
* call-seq:
* ARGF.close -> ARGF
*
- * Closes the current file and skips to the next in the stream. Trying to
- * close a file that has already been closed causes an +IOError+ to be
- * raised.
+ * 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
+ * closed.
*
- * For example:
+ * For example:
*
* $ ruby argf.rb foo bar
*
@@ -11861,7 +12619,6 @@ argf_skip(VALUE argf)
* ARGF.close
* ARGF.filename #=> "bar"
* ARGF.close
- * ARGF.close #=> closed stream (IOError)
*/
static VALUE
argf_close_m(VALUE argf)
@@ -11907,14 +12664,15 @@ argf_to_s(VALUE argf)
* ARGF.inplace_mode -> String
*
* Returns the file extension appended to the names of modified files under
- * inplace-edit mode. This value can be set using +ARGF.inplace_mode=+ or
+ * 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)
{
if (!ARGF.inplace) return Qnil;
- return rb_str_new2(ARGF.inplace);
+ if (NIL_P(ARGF.inplace)) return rb_str_new(0, 0);
+ return rb_str_dup(ARGF.inplace);
}
static VALUE
@@ -11927,7 +12685,7 @@ opt_i_get(ID id, VALUE *var)
* call-seq:
* ARGF.inplace_mode = ext -> ARGF
*
- * Sets the filename extension for in place editing mode to the given String.
+ * 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.
*
@@ -11940,8 +12698,8 @@ 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_.
+ * 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_.
*/
static VALUE
argf_inplace_mode_set(VALUE argf, VALUE val)
@@ -11950,14 +12708,13 @@ argf_inplace_mode_set(VALUE argf, VALUE val)
rb_insecure_operation();
if (!RTEST(val)) {
- if (ARGF.inplace) free(ARGF.inplace);
- ARGF.inplace = 0;
+ ARGF.inplace = Qfalse;
+ }
+ else if (StringValueCStr(val), !RSTRING_LEN(val)) {
+ ARGF.inplace = Qnil;
}
else {
- StringValue(val);
- if (ARGF.inplace) free(ARGF.inplace);
- ARGF.inplace = 0;
- ARGF.inplace = strdup(RSTRING_PTR(val));
+ ARGF.inplace = rb_str_new_frozen(val);
}
return argf;
}
@@ -11971,15 +12728,13 @@ opt_i_set(VALUE val, ID id, VALUE *var)
const char *
ruby_get_inplace_mode(void)
{
- return ARGF.inplace;
+ return RSTRING_PTR(ARGF.inplace);
}
void
ruby_set_inplace_mode(const char *suffix)
{
- if (ARGF.inplace) free(ARGF.inplace);
- ARGF.inplace = 0;
- if (suffix) ARGF.inplace = strdup(suffix);
+ ARGF.inplace = !suffix ? Qfalse : !*suffix ? Qnil : rb_fstring_cstr(suffix);
}
/*
@@ -12103,8 +12858,8 @@ rb_readwrite_syserr_fail(enum rb_io_wait_readwrite writable, int n, const char *
* File.open("/etc/hosts") {|f| f.close; f.read }
* #=> IOError: closed stream
*
- * Note that some IO failures raise +SystemCallError+s and these are not
- * subclasses of IOError:
+ * Note that some IO failures raise <code>SystemCallError</code>s
+ * and these are not subclasses of IOError:
*
* File.open("does/not/exist")
* #=> Errno::ENOENT: No such file or directory - does/not/exist
@@ -12117,7 +12872,7 @@ rb_readwrite_syserr_fail(enum rb_io_wait_readwrite writable, int n, const char *
* methods exist in two forms,
*
* one that returns +nil+ when the end of file is reached, the other
- * raises EOFError +EOFError+.
+ * raises +EOFError+.
*
* +EOFError+ is a subclass of +IOError+.
*
@@ -12205,13 +12960,13 @@ rb_readwrite_syserr_fail(enum rb_io_wait_readwrite writable, int n, const char *
* <code>"\gumby\ruby\test.rb"</code>. When specifying a Windows-style
* filename in a Ruby string, remember to escape the backslashes:
*
- * "c:\\gumby\\ruby\\test.rb"
+ * "C:\\gumby\\ruby\\test.rb"
*
* Our examples here will use the Unix-style forward slashes;
* File::ALT_SEPARATOR can be used to get the platform-specific separator
* character.
*
- * The global constant ARGF (also accessible as $<) provides an
+ * The global constant ARGF (also accessible as <code>$<</code>) provides an
* IO-like stream which allows access to all files mentioned on the
* command line (or STDIN if no files are mentioned). ARGF#path and its alias
* ARGF#filename are provided to access the name of the file currently being
@@ -12298,10 +13053,14 @@ Init_IO(void)
rb_cIO = rb_define_class("IO", rb_cObject);
rb_include_module(rb_cIO, rb_mEnumerable);
+ /* exception to wait for reading. see IO.select. */
rb_mWaitReadable = rb_define_module_under(rb_cIO, "WaitReadable");
+ /* exception to wait for writing. see IO.select. */
rb_mWaitWritable = rb_define_module_under(rb_cIO, "WaitWritable");
+ /* exception to wait for reading by EAGAIN. see IO.select. */
rb_eEAGAINWaitReadable = rb_define_class_under(rb_cIO, "EAGAINWaitReadable", rb_eEAGAIN);
rb_include_module(rb_eEAGAINWaitReadable, rb_mWaitReadable);
+ /* exception to wait for writing by EAGAIN. see IO.select. */
rb_eEAGAINWaitWritable = rb_define_class_under(rb_cIO, "EAGAINWaitWritable", rb_eEAGAIN);
rb_include_module(rb_eEAGAINWaitWritable, rb_mWaitWritable);
#if EAGAIN == EWOULDBLOCK
@@ -12312,13 +13071,17 @@ Init_IO(void)
/* same as IO::EAGAINWaitWritable */
rb_define_const(rb_cIO, "EWOULDBLOCKWaitWritable", rb_eEAGAINWaitWritable);
#else
+ /* exception to wait for reading by EWOULDBLOCK. see IO.select. */
rb_eEWOULDBLOCKWaitReadable = rb_define_class_under(rb_cIO, "EWOULDBLOCKWaitReadable", rb_eEWOULDBLOCK);
rb_include_module(rb_eEWOULDBLOCKWaitReadable, rb_mWaitReadable);
+ /* exception to wait for writing by EWOULDBLOCK. see IO.select. */
rb_eEWOULDBLOCKWaitWritable = rb_define_class_under(rb_cIO, "EWOULDBLOCKWaitWritable", rb_eEWOULDBLOCK);
rb_include_module(rb_eEWOULDBLOCKWaitWritable, rb_mWaitWritable);
#endif
+ /* exception to wait for reading by EINPROGRESS. see IO.select. */
rb_eEINPROGRESSWaitReadable = rb_define_class_under(rb_cIO, "EINPROGRESSWaitReadable", rb_eEINPROGRESS);
rb_include_module(rb_eEINPROGRESSWaitReadable, rb_mWaitReadable);
+ /* exception to wait for writing by EINPROGRESS. see IO.select. */
rb_eEINPROGRESSWaitWritable = rb_define_class_under(rb_cIO, "EINPROGRESSWaitWritable", rb_eEINPROGRESS);
rb_include_module(rb_eEINPROGRESSWaitWritable, rb_mWaitWritable);
@@ -12349,10 +13112,10 @@ Init_IO(void)
rb_output_fs = Qnil;
rb_define_hooked_variable("$,", &rb_output_fs, 0, rb_str_setter);
- rb_rs = rb_default_rs = rb_usascii_str_new2("\n");
+ rb_default_rs = rb_fstring_lit("\n"); /* avoid modifying RS_default */
rb_gc_register_mark_object(rb_default_rs);
+ rb_rs = rb_default_rs;
rb_output_rs = Qnil;
- OBJ_FREEZE(rb_default_rs); /* avoid modifying RS_default */
rb_define_hooked_variable("$/", &rb_rs, 0, rb_str_setter);
rb_define_hooked_variable("$-0", &rb_rs, 0, rb_str_setter);
rb_define_hooked_variable("$\\", &rb_output_rs, 0, rb_str_setter);
@@ -12380,6 +13143,9 @@ Init_IO(void)
rb_define_method(rb_cIO, "syswrite", rb_io_syswrite, 1);
rb_define_method(rb_cIO, "sysread", rb_io_sysread, -1);
+ rb_define_method(rb_cIO, "pread", rb_io_pread, -1);
+ rb_define_method(rb_cIO, "pwrite", rb_io_pwrite, 2);
+
rb_define_method(rb_cIO, "fileno", rb_io_fileno, 0);
rb_define_alias(rb_cIO, "to_i", "fileno");
rb_define_method(rb_cIO, "to_io", rb_io_to_io, 0);
@@ -12400,7 +13166,7 @@ Init_IO(void)
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, "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);
diff --git a/iseq.c b/iseq.c
index 07d8828e9b..e327cd9976 100644
--- a/iseq.c
+++ b/iseq.c
@@ -17,6 +17,7 @@
# include <dlfcn.h>
#endif
+#define RUBY_VM_INSNS_INFO 1
/* #define RUBY_MARK_FREE_DEBUG 1 */
#include "gc.h"
#include "vm_core.h"
@@ -25,11 +26,18 @@
#include "insns.inc"
#include "insns_info.inc"
+#include "mjit.h"
VALUE rb_cISeq;
static VALUE iseqw_new(const rb_iseq_t *iseq);
static const rb_iseq_t *iseqw_check(VALUE iseqw);
+#if VM_INSN_INFO_TABLE_IMPL == 2
+static struct succ_index_table *succ_index_table_create(int max_pos, int *data, int size);
+static unsigned int *succ_index_table_invert(int max_pos, struct succ_index_table *sd, int size);
+static int succ_index_lookup(const struct succ_index_table *sd, int x);
+#endif
+
#define hidden_obj_p(obj) (!SPECIAL_CONST_P(obj) && !RBASIC(obj)->klass)
static inline VALUE
@@ -43,6 +51,9 @@ obj_resurrect(VALUE obj)
case T_ARRAY:
obj = rb_ary_resurrect(obj);
break;
+ case T_HASH:
+ obj = rb_hash_resurrect(obj);
+ break;
}
}
return obj;
@@ -71,63 +82,198 @@ rb_iseq_free(const rb_iseq_t *iseq)
{
RUBY_FREE_ENTER("iseq");
- if (iseq) {
- if (iseq->body) {
- ruby_xfree((void *)iseq->body->iseq_encoded);
- ruby_xfree((void *)iseq->body->line_info_table);
- ruby_xfree((void *)iseq->body->local_table);
- ruby_xfree((void *)iseq->body->is_entries);
-
- if (iseq->body->ci_entries) {
- unsigned int i;
- struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&iseq->body->ci_entries[iseq->body->ci_size];
- for (i=0; i<iseq->body->ci_kw_size; i++) {
- const struct rb_call_info_kw_arg *kw_arg = ci_kw_entries[i].kw_arg;
- ruby_xfree((void *)kw_arg);
- }
- ruby_xfree(iseq->body->ci_entries);
- ruby_xfree(iseq->body->cc_entries);
- }
- ruby_xfree((void *)iseq->body->catch_table);
- ruby_xfree((void *)iseq->body->param.opt_table);
+ if (iseq && iseq->body) {
+ struct rb_iseq_constant_body *const body = iseq->body;
+ mjit_free_iseq(iseq); /* Notify MJIT */
+ 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);
+#endif
+ ruby_xfree((void *)body->local_table);
+ ruby_xfree((void *)body->is_entries);
- if (iseq->body->param.keyword != NULL) {
- ruby_xfree((void *)iseq->body->param.keyword->default_values);
- ruby_xfree((void *)iseq->body->param.keyword);
+ if (body->ci_entries) {
+ unsigned int i;
+ struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&body->ci_entries[body->ci_size];
+ for (i=0; i<body->ci_kw_size; i++) {
+ const struct rb_call_info_kw_arg *kw_arg = ci_kw_entries[i].kw_arg;
+ ruby_xfree((void *)kw_arg);
}
- compile_data_free(ISEQ_COMPILE_DATA(iseq));
- ruby_xfree(iseq->body);
+ ruby_xfree(body->ci_entries);
+ ruby_xfree(body->cc_entries);
}
+ ruby_xfree((void *)body->catch_table);
+ ruby_xfree((void *)body->param.opt_table);
+
+ 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);
+ }
+
+ if (iseq && ISEQ_EXECUTABLE_P(iseq) && iseq->aux.exec.local_hooks) {
+ rb_hook_list_free(iseq->aux.exec.local_hooks);
}
+
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
+
+static VALUE
+rb_vm_insn_null_translator(const void *addr)
+{
+ return (VALUE)addr;
+}
+
+typedef void iseq_value_itr_t(void *ctx, VALUE obj);
+typedef VALUE rb_vm_insns_translator_t(const void *addr);
+
+static int
+iseq_extract_values(const VALUE *code, size_t pos, iseq_value_itr_t * func, void *data, rb_vm_insns_translator_t * translator)
+{
+ VALUE insn = translator((void *)code[pos]);
+ int len = insn_len(insn);
+ int op_no;
+ const char *types = insn_op_types(insn);
+
+ for (op_no = 0; types[op_no]; op_no++) {
+ char type = types[op_no];
+ switch (type) {
+ case TS_CDHASH:
+ case TS_ISEQ:
+ case TS_VALUE:
+ {
+ VALUE op = code[pos + op_no + 1];
+ if (!SPECIAL_CONST_P(op)) {
+ func(data, op);
+ }
+ break;
+ }
+ case TS_ISE:
+ {
+ union iseq_inline_storage_entry *const is = (union iseq_inline_storage_entry *)code[pos + op_no + 1];
+ if (is->once.value) {
+ func(data, is->once.value);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return len;
+}
+
+static void
+rb_iseq_each_value(const rb_iseq_t *iseq, iseq_value_itr_t * func, void *data)
+{
+ unsigned int size;
+ const VALUE *code;
+ size_t n;
+ rb_vm_insns_translator_t * translator;
+ const struct rb_iseq_constant_body *const body = iseq->body;
+
+ size = body->iseq_size;
+ code = body->iseq_encoded;
+
+#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
+ if (FL_TEST(iseq, ISEQ_TRANSLATED)) {
+ translator = rb_vm_insn_addr2insn2;
+ } else {
+ translator = rb_vm_insn_null_translator;
+ }
+#else
+ translator = rb_vm_insn_null_translator;
+#endif
+
+ for (n = 0; n < size;) {
+ n += iseq_extract_values(code, n, func, data, translator);
+ }
+}
+
+static void
+each_insn_value(void *ctx, VALUE obj)
+{
+ rb_gc_mark(obj);
+}
+
void
rb_iseq_mark(const rb_iseq_t *iseq)
{
RUBY_MARK_ENTER("iseq");
- RUBY_GC_INFO("%s @ %s\n", RSTRING_PTR(iseq->body->location.label), RSTRING_PTR(iseq->body->location.path));
+ RUBY_MARK_UNLESS_NULL(iseq->wrapper);
if (iseq->body) {
- const struct rb_iseq_constant_body *body = iseq->body;
+ const struct rb_iseq_constant_body *const body = iseq->body;
+
+ if (FL_TEST(iseq, ISEQ_MARKABLE_ISEQ)) {
+ rb_iseq_each_value(iseq, each_insn_value, NULL);
+ }
- RUBY_MARK_UNLESS_NULL(body->mark_ary);
+ rb_gc_mark(body->variable.coverage);
+ rb_gc_mark(body->variable.pc2branchindex);
rb_gc_mark(body->location.label);
rb_gc_mark(body->location.base_label);
- rb_gc_mark(body->location.path);
- RUBY_MARK_UNLESS_NULL(body->location.absolute_path);
+ rb_gc_mark(body->location.pathobj);
RUBY_MARK_UNLESS_NULL((VALUE)body->parent_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;
+
+ i = keyword->required_num;
+
+ for (j = 0; i < keyword->num; i++, j++) {
+ VALUE obj = keyword->default_values[j];
+ if (!SPECIAL_CONST_P(obj)) {
+ rb_gc_mark(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 = &table->entries[i];
+ if (entry->iseq) {
+ rb_gc_mark((VALUE)entry->iseq);
+ }
+ }
+ }
}
- if (FL_TEST(iseq, ISEQ_NOT_LOADED_YET)) {
+ if (FL_TEST_RAW(iseq, ISEQ_NOT_LOADED_YET)) {
rb_gc_mark(iseq->aux.loader.obj);
}
- else if (ISEQ_COMPILE_DATA(iseq) != 0) {
+ else if (FL_TEST_RAW(iseq, ISEQ_USE_COMPILE_DATA)) {
const struct iseq_compile_data *const compile_data = ISEQ_COMPILE_DATA(iseq);
- RUBY_MARK_UNLESS_NULL(compile_data->mark_ary);
- RUBY_MARK_UNLESS_NULL(compile_data->err_info);
- RUBY_MARK_UNLESS_NULL(compile_data->catch_table_ary);
+ VM_ASSERT(compile_data != NULL);
+
+ RUBY_MARK_UNLESS_NULL(compile_data->mark_ary);
+ RUBY_MARK_UNLESS_NULL(compile_data->err_info);
+ RUBY_MARK_UNLESS_NULL(compile_data->catch_table_ary);
+ }
+ else {
+ /* executable */
+ VM_ASSERT(ISEQ_EXECUTABLE_P(iseq));
+ if (iseq->aux.exec.local_hooks) {
+ rb_hook_list_mark(iseq->aux.exec.local_hooks);
+ }
}
RUBY_MARK_LEAVE("iseq");
@@ -160,7 +306,7 @@ iseq_memsize(const rb_iseq_t *iseq)
size += sizeof(struct rb_iseq_constant_body);
size += body->iseq_size * sizeof(VALUE);
- size += body->line_info_size * sizeof(struct iseq_line_info_entry);
+ size += body->insns_info.size * (sizeof(struct iseq_insn_info_entry) + sizeof(unsigned int));
size += body->local_table_size * sizeof(ID);
if (body->catch_table) {
size += iseq_catch_table_bytes(body->catch_table->size);
@@ -200,7 +346,7 @@ iseq_memsize(const rb_iseq_t *iseq)
cur = compile_data->storage_head;
while (cur) {
- size += cur->size + SIZEOF_ISEQ_COMPILE_DATA_STORAGE;
+ size += cur->size + offsetof(struct iseq_compile_data_storage, buff);
cur = cur->next;
}
}
@@ -216,83 +362,114 @@ iseq_alloc(void)
return iseq;
}
-static rb_iseq_location_t *
-iseq_location_setup(rb_iseq_t *iseq, VALUE path, VALUE absolute_path, VALUE name, VALUE first_lineno)
+VALUE
+rb_iseq_pathobj_new(VALUE path, VALUE realpath)
{
- rb_iseq_location_t *loc = &iseq->body->location;
- RB_OBJ_WRITE(iseq, &loc->path, path);
- if (RTEST(absolute_path) && rb_str_cmp(path, absolute_path) == 0) {
- RB_OBJ_WRITE(iseq, &loc->absolute_path, path);
+ VALUE pathobj;
+ VM_ASSERT(RB_TYPE_P(path, T_STRING));
+ VM_ASSERT(realpath == Qnil || RB_TYPE_P(realpath, T_STRING));
+
+ if (path == realpath ||
+ (!NIL_P(realpath) && rb_str_cmp(path, realpath) == 0)) {
+ pathobj = rb_fstring(path);
}
else {
- RB_OBJ_WRITE(iseq, &loc->absolute_path, absolute_path);
+ 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;
+}
+
+void
+rb_iseq_pathobj_set(const rb_iseq_t *iseq, VALUE path, VALUE realpath)
+{
+ RB_OBJ_WRITE(iseq, &iseq->body->location.pathobj,
+ rb_iseq_pathobj_new(path, realpath));
+}
+
+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)
+{
+ rb_iseq_location_t *loc = &iseq->body->location;
+
+ rb_iseq_pathobj_set(iseq, path, realpath);
RB_OBJ_WRITE(iseq, &loc->label, name);
RB_OBJ_WRITE(iseq, &loc->base_label, name);
loc->first_lineno = first_lineno;
+ if (code_location) {
+ loc->node_id = node_id;
+ 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;
+ }
+
return loc;
}
static void
set_relation(rb_iseq_t *iseq, const rb_iseq_t *piseq)
{
- const VALUE type = iseq->body->type;
+ struct rb_iseq_constant_body *const body = iseq->body;
+ const VALUE type = body->type;
/* set class nest stack */
if (type == ISEQ_TYPE_TOP) {
- iseq->body->local_iseq = iseq;
+ body->local_iseq = iseq;
}
else if (type == ISEQ_TYPE_METHOD || type == ISEQ_TYPE_CLASS) {
- iseq->body->local_iseq = iseq;
+ body->local_iseq = iseq;
}
else if (piseq) {
- iseq->body->local_iseq = piseq->body->local_iseq;
+ body->local_iseq = piseq->body->local_iseq;
}
if (piseq) {
- iseq->body->parent_iseq = piseq;
+ body->parent_iseq = piseq;
}
if (type == ISEQ_TYPE_MAIN) {
- iseq->body->local_iseq = iseq;
+ body->local_iseq = iseq;
}
}
-void
-rb_iseq_add_mark_object(const rb_iseq_t *iseq, VALUE obj)
-{
- /* TODO: check dedup */
- rb_ary_push(ISEQ_MARK_ARY(iseq), obj);
-}
-
static VALUE
prepare_iseq_build(rb_iseq_t *iseq,
- VALUE name, VALUE path, VALUE absolute_path, VALUE first_lineno,
+ 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, enum iseq_type type,
const rb_compile_option_t *option)
{
VALUE coverage = Qfalse;
+ VALUE err_info = Qnil;
+ struct rb_iseq_constant_body *const body = iseq->body;
+
+ if (parent && (type == ISEQ_TYPE_MAIN || type == ISEQ_TYPE_TOP))
+ err_info = Qfalse;
- iseq->body->type = type;
+ body->type = type;
set_relation(iseq, parent);
name = rb_fstring(name);
- path = rb_fstring(path);
- if (RTEST(absolute_path)) absolute_path = rb_fstring(absolute_path);
- iseq_location_setup(iseq, path, absolute_path, name, first_lineno);
- if (iseq != iseq->body->local_iseq) {
- RB_OBJ_WRITE(iseq, &iseq->body->location.base_label, iseq->body->local_iseq->body->location.label);
+ iseq_location_setup(iseq, name, path, realpath, first_lineno, code_location, node_id);
+ if (iseq != body->local_iseq) {
+ RB_OBJ_WRITE(iseq, &body->location.base_label, body->local_iseq->body->location.label);
}
- RB_OBJ_WRITE(iseq, &iseq->body->mark_ary, iseq_mark_ary_create(0));
+ ISEQ_COVERAGE_SET(iseq, Qnil);
+ ISEQ_ORIGINAL_ISEQ_CLEAR(iseq);
+ body->variable.flip_count = 0;
- ISEQ_COMPILE_DATA(iseq) = ZALLOC(struct iseq_compile_data);
- RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, Qnil);
+ ISEQ_COMPILE_DATA_ALLOC(iseq);
+ RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, err_info);
RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->mark_ary, rb_ary_tmp_new(3));
ISEQ_COMPILE_DATA(iseq)->storage_head = ISEQ_COMPILE_DATA(iseq)->storage_current =
(struct iseq_compile_data_storage *)
ALLOC_N(char, INITIAL_ISEQ_COMPILE_DATA_STORAGE_BUFF_SIZE +
- SIZEOF_ISEQ_COMPILE_DATA_STORAGE);
+ offsetof(struct iseq_compile_data_storage, buff));
RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, rb_ary_tmp_new(3));
ISEQ_COMPILE_DATA(iseq)->storage_head->pos = 0;
@@ -300,34 +477,92 @@ prepare_iseq_build(rb_iseq_t *iseq,
ISEQ_COMPILE_DATA(iseq)->storage_head->size =
INITIAL_ISEQ_COMPILE_DATA_STORAGE_BUFF_SIZE;
ISEQ_COMPILE_DATA(iseq)->option = option;
- ISEQ_COMPILE_DATA(iseq)->last_coverable_line = -1;
ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = NULL;
if (option->coverage_enabled) {
VALUE coverages = rb_get_coverages();
if (RTEST(coverages)) {
- coverage = rb_hash_lookup(coverages, path);
+ 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));
return Qtrue;
}
+#if VM_CHECK_MODE > 0 && VM_INSN_INFO_TABLE_IMPL > 0
+static void validate_get_insn_info(const rb_iseq_t *iseq);
+#endif
+
+void
+rb_iseq_insns_info_encode_positions(const rb_iseq_t *iseq)
+{
+#if VM_INSN_INFO_TABLE_IMPL == 2
+ struct rb_iseq_constant_body *const body = iseq->body;
+ int size = body->insns_info.size;
+ int max_pos = body->iseq_size;
+ int *data = (int *)body->insns_info.positions;
+ if (body->insns_info.succ_index_table) ruby_xfree(body->insns_info.succ_index_table);
+ body->insns_info.succ_index_table = succ_index_table_create(max_pos, data, size);
+#if VM_CHECK_MODE == 0
+ ruby_xfree(body->insns_info.positions);
+ body->insns_info.positions = NULL;
+#endif
+#endif
+}
+
+#if VM_INSN_INFO_TABLE_IMPL == 2
+unsigned int *
+rb_iseq_insns_info_decode_positions(const struct rb_iseq_constant_body *body)
+{
+ int size = body->insns_info.size;
+ int max_pos = body->iseq_size;
+ struct succ_index_table *sd = body->insns_info.succ_index_table;
+ return succ_index_table_invert(max_pos, sd, size);
+}
+#endif
+
+void
+rb_iseq_init_trace(rb_iseq_t *iseq)
+{
+ iseq->aux.exec.global_trace_events = 0;
+ if (ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS) {
+ rb_iseq_trace_set(iseq, ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS);
+ }
+}
+
static VALUE
-cleanup_iseq_build(rb_iseq_t *iseq)
+finish_iseq_build(rb_iseq_t *iseq)
{
struct iseq_compile_data *data = ISEQ_COMPILE_DATA(iseq);
+ const struct rb_iseq_constant_body *const body = iseq->body;
VALUE err = data->err_info;
- ISEQ_COMPILE_DATA(iseq) = 0;
+ ISEQ_COMPILE_DATA_CLEAR(iseq);
compile_data_free(data);
+#if VM_INSN_INFO_TABLE_IMPL == 2 /* succinct bitvector */
+ /* create succ_index_table */
+ if (body->insns_info.succ_index_table == NULL) {
+ rb_iseq_insns_info_encode_positions(iseq);
+ }
+#endif
+
+#if VM_CHECK_MODE > 0 && VM_INSN_INFO_TABLE_IMPL > 0
+ validate_get_insn_info(iseq);
+#endif
+
if (RTEST(err)) {
- rb_funcallv(err, rb_intern("set_backtrace"), 1, &iseq->body->location.path);
+ 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_iseq_init_trace(iseq);
return Qtrue;
}
@@ -339,7 +574,6 @@ static rb_compile_option_t COMPILE_OPTION_DEFAULT = {
OPT_OPERANDS_UNIFICATION, /* int operands_unification; */
OPT_INSTRUCTIONS_UNIFICATION, /* int instructions_unification; */
OPT_STACK_CACHING, /* int stack_caching; */
- OPT_TRACE_INSTRUCTION, /* int trace_instruction */
OPT_FROZEN_STRING_LITERAL,
OPT_DEBUG_FROZEN_STRING_LITERAL,
TRUE, /* coverage_enabled */
@@ -366,7 +600,6 @@ set_compile_option_from_hash(rb_compile_option_t *option, VALUE opt)
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, trace_instruction);
SET_COMPILE_OPTION(option, opt, frozen_string_literal);
SET_COMPILE_OPTION(option, opt, debug_frozen_string_literal);
SET_COMPILE_OPTION(option, opt, coverage_enabled);
@@ -421,7 +654,6 @@ make_compile_option_value(rb_compile_option_t *option)
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, trace_instruction);
SET_COMPILE_OPTION(option, opt, frozen_string_literal);
SET_COMPILE_OPTION(option, opt, debug_frozen_string_literal);
SET_COMPILE_OPTION(option, opt, coverage_enabled);
@@ -433,25 +665,34 @@ make_compile_option_value(rb_compile_option_t *option)
}
rb_iseq_t *
-rb_iseq_new(NODE *node, VALUE name, VALUE path, VALUE absolute_path,
+rb_iseq_new(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath,
const rb_iseq_t *parent, enum iseq_type type)
{
- return rb_iseq_new_with_opt(node, name, path, absolute_path, INT2FIX(0), parent, type,
+ return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, type,
&COMPILE_OPTION_DEFAULT);
}
rb_iseq_t *
-rb_iseq_new_top(NODE *node, VALUE name, VALUE path, VALUE absolute_path, const rb_iseq_t *parent)
-{
- return rb_iseq_new_with_opt(node, name, path, absolute_path, INT2FIX(0), parent, ISEQ_TYPE_TOP,
+rb_iseq_new_top(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent)
+{
+ VALUE coverages = rb_get_coverages();
+ if (RTEST(coverages)) {
+ if (ast->line_count >= 0) {
+ int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : ast->line_count;
+ VALUE coverage = rb_default_coverage(len);
+ rb_hash_aset(coverages, path, coverage);
+ }
+ }
+
+ return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, ISEQ_TYPE_TOP,
&COMPILE_OPTION_DEFAULT);
}
rb_iseq_t *
-rb_iseq_new_main(NODE *node, VALUE path, VALUE absolute_path, const rb_iseq_t *parent)
+rb_iseq_new_main(const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_iseq_t *parent)
{
- return rb_iseq_new_with_opt(node, rb_fstring_cstr("<main>"),
- path, absolute_path, INT2FIX(0),
+ return rb_iseq_new_with_opt(ast, rb_fstring_lit("<main>"),
+ path, realpath, INT2FIX(0),
parent, ISEQ_TYPE_MAIN, &COMPILE_OPTION_DEFAULT);
}
@@ -470,18 +711,39 @@ iseq_translate(rb_iseq_t *iseq)
}
rb_iseq_t *
-rb_iseq_new_with_opt(NODE *node, VALUE name, VALUE path, VALUE absolute_path,
+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,
enum iseq_type type, const rb_compile_option_t *option)
{
+ const NODE *node = ast ? ast->root : 0;
/* TODO: argument check */
rb_iseq_t *iseq = iseq_alloc();
+ rb_compile_option_t new_opt;
- if (!option) option = &COMPILE_OPTION_DEFAULT;
- prepare_iseq_build(iseq, name, path, absolute_path, first_lineno, parent, type, option);
+ new_opt = option ? *option : COMPILE_OPTION_DEFAULT;
+ if (ast && ast->compile_option) rb_iseq_make_compile_option(&new_opt, ast->compile_option);
+
+ prepare_iseq_build(iseq, name, path, realpath, first_lineno, node ? &node->nd_loc : NULL, node ? nd_node_id(node) : -1, parent, type, &new_opt);
rb_iseq_compile_node(iseq, node);
- cleanup_iseq_build(iseq);
+ finish_iseq_build(iseq);
+
+ return iseq_translate(iseq);
+}
+
+rb_iseq_t *
+rb_iseq_new_ifunc(const struct vm_ifunc *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)
+{
+ /* TODO: argument check */
+ rb_iseq_t *iseq = iseq_alloc();
+
+ if (!option) option = &COMPILE_OPTION_DEFAULT;
+ prepare_iseq_build(iseq, name, path, realpath, first_lineno, NULL, -1, parent, type, option);
+
+ rb_iseq_compile_ifunc(iseq, ifunc);
+ finish_iseq_build(iseq);
return iseq_translate(iseq);
}
@@ -498,10 +760,10 @@ rb_iseq_load_iseq(VALUE fname)
return NULL;
}
-#define CHECK_ARRAY(v) rb_convert_type((v), T_ARRAY, "Array", "to_ary")
-#define CHECK_HASH(v) rb_convert_type((v), T_HASH, "Hash", "to_hash")
-#define CHECK_STRING(v) rb_convert_type((v), T_STRING, "String", "to_str")
-#define CHECK_SYMBOL(v) rb_convert_type((v), T_SYMBOL, "Symbol", "to_sym")
+#define CHECK_ARRAY(v) rb_to_array_type(v)
+#define CHECK_HASH(v) rb_to_hash_type(v)
+#define CHECK_STRING(v) rb_str_to_str(v)
+#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
@@ -515,7 +777,7 @@ iseq_type_from_sym(VALUE type)
const ID id_ensure = rb_intern("ensure");
const ID id_eval = rb_intern("eval");
const ID id_main = rb_intern("main");
- const ID id_defined_guard = rb_intern("defined_guard");
+ const ID id_plain = rb_intern("plain");
/* ensure all symbols are static or pinned down before
* conversion */
const ID typeid = rb_check_id(&type);
@@ -527,7 +789,7 @@ iseq_type_from_sym(VALUE type)
if (typeid == id_ensure) return ISEQ_TYPE_ENSURE;
if (typeid == id_eval) return ISEQ_TYPE_EVAL;
if (typeid == id_main) return ISEQ_TYPE_MAIN;
- if (typeid == id_defined_guard) return ISEQ_TYPE_DEFINED_GUARD;
+ if (typeid == id_plain) return ISEQ_TYPE_PLAIN;
return (enum iseq_type)-1;
}
@@ -537,12 +799,13 @@ 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, absolute_path, first_lineno;
+ VALUE name, path, realpath, first_lineno, code_location, node_id;
VALUE type, body, locals, params, exception;
st_data_t iseq_type;
rb_compile_option_t option;
int i = 0;
+ rb_code_location_t tmp_loc = { {0, 0}, {-1, -1} };
/* [magic, major_version, minor_version, format_type, misc,
* label, path, first_lineno,
@@ -560,8 +823,8 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt)
name = CHECK_STRING(rb_ary_entry(data, i++));
path = CHECK_STRING(rb_ary_entry(data, i++));
- absolute_path = rb_ary_entry(data, i++);
- absolute_path = NIL_P(absolute_path) ? Qnil : CHECK_STRING(absolute_path);
+ realpath = rb_ary_entry(data, i++);
+ realpath = NIL_P(realpath) ? Qnil : CHECK_STRING(realpath);
first_lineno = CHECK_INTEGER(rb_ary_entry(data, i++));
type = CHECK_SYMBOL(rb_ary_entry(data, i++));
@@ -577,14 +840,24 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt)
rb_raise(rb_eTypeError, "unsupport 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));
+ }
+
make_compile_option(&option, opt);
option.peephole_optimization = FALSE; /* because peephole optimization can modify original iseq */
- prepare_iseq_build(iseq, name, path, absolute_path, first_lineno,
+ prepare_iseq_build(iseq, name, path, realpath, first_lineno, &tmp_loc, NUM2INT(node_id),
parent, (enum iseq_type)iseq_type, &option);
rb_iseq_build_from_ary(iseq, misc, locals, params, exception, body);
- cleanup_iseq_build(iseq);
+ finish_iseq_build(iseq);
return iseqw_new(iseq);
}
@@ -607,9 +880,8 @@ rb_iseq_load(VALUE data, VALUE parent, VALUE opt)
}
rb_iseq_t *
-rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE absolute_path, VALUE line, const struct rb_block *base_block, VALUE opt)
+rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, const struct rb_block *base_block, VALUE opt)
{
- rb_thread_t *th = GET_THREAD();
rb_iseq_t *iseq = NULL;
const rb_iseq_t *const parent = base_block ? vm_block_iseq(base_block) : NULL;
rb_compile_option_t option;
@@ -619,9 +891,9 @@ rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE absolute_path, VALUE li
#else
# define INITIALIZED /* volatile */
#endif
- NODE *(*parse)(VALUE vparser, VALUE fname, VALUE file, int start);
+ rb_ast_t *(*parse)(VALUE vparser, VALUE fname, VALUE file, int start);
int ln;
- NODE *INITIALIZED node;
+ rb_ast_t *INITIALIZED ast;
/* safe results first */
make_compile_option(&option, opt);
@@ -637,18 +909,20 @@ rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE absolute_path, VALUE li
{
const VALUE parser = rb_parser_new();
rb_parser_set_context(parser, base_block, FALSE);
- node = (*parse)(parser, file, src, ln);
+ ast = (*parse)(parser, file, src, ln);
}
- if (!node) {
- rb_exc_raise(th->errinfo);
+ if (!ast->body.root) {
+ rb_ast_dispose(ast);
+ rb_exc_raise(GET_EC()->errinfo);
}
else {
INITIALIZED VALUE label = parent ?
parent->body->location.label :
- rb_fstring_cstr("<compiled>");
- iseq = rb_iseq_new_with_opt(node, label, file, absolute_path, line,
+ rb_fstring_lit("<compiled>");
+ iseq = rb_iseq_new_with_opt(&ast->body, label, file, realpath, line,
parent, type, &option);
+ rb_ast_dispose(ast);
}
return iseq;
@@ -669,13 +943,19 @@ rb_iseq_compile_on_base(VALUE src, VALUE file, VALUE line, const struct rb_block
VALUE
rb_iseq_path(const rb_iseq_t *iseq)
{
- return iseq->body->location.path;
+ return pathobj_path(iseq->body->location.pathobj);
+}
+
+VALUE
+rb_iseq_realpath(const rb_iseq_t *iseq)
+{
+ return pathobj_realpath(iseq->body->location.pathobj);
}
VALUE
rb_iseq_absolute_path(const rb_iseq_t *iseq)
{
- return iseq->body->location.absolute_path;
+ return rb_iseq_realpath(iseq);
}
VALUE
@@ -699,24 +979,51 @@ rb_iseq_first_lineno(const rb_iseq_t *iseq)
VALUE
rb_iseq_method_name(const rb_iseq_t *iseq)
{
- const rb_iseq_t *local_iseq;
+ struct rb_iseq_constant_body *const body = iseq->body->local_iseq->body;
- local_iseq = iseq->body->local_iseq;
-
- if (local_iseq->body->type == ISEQ_TYPE_METHOD) {
- return local_iseq->body->location.base_label;
+ if (body->type == ISEQ_TYPE_METHOD) {
+ return body->location.base_label;
}
else {
return Qnil;
}
}
+void
+rb_iseq_code_location(const rb_iseq_t *iseq, int *beg_pos_lineno, int *beg_pos_column, int *end_pos_lineno, int *end_pos_column)
+{
+ const rb_code_location_t *loc = &iseq->body->location.code_location;
+ if (beg_pos_lineno) *beg_pos_lineno = loc->beg_pos.lineno;
+ if (beg_pos_column) *beg_pos_column = loc->beg_pos.column;
+ if (end_pos_lineno) *end_pos_lineno = loc->end_pos.lineno;
+ if (end_pos_column) *end_pos_column = loc->end_pos.column;
+}
+
VALUE
rb_iseq_coverage(const rb_iseq_t *iseq)
{
return ISEQ_COVERAGE(iseq);
}
+static int
+remove_coverage_i(void *vstart, void *vend, size_t stride, void *data)
+{
+ VALUE v = (VALUE)vstart;
+ for (; v != (VALUE)vend; v += stride) {
+ if (rb_obj_is_iseq(v)) {
+ rb_iseq_t *iseq = (rb_iseq_t *)v;
+ ISEQ_COVERAGE_SET(iseq, Qnil);
+ }
+ }
+ return 0;
+}
+
+void
+rb_iseq_remove_coverage_all(void)
+{
+ rb_objspace_each_objects(remove_coverage_i, NULL);
+}
+
/* define wrapper class methods (RubyVM::InstructionSequence) */
static void
@@ -740,14 +1047,22 @@ static const rb_data_type_t iseqw_data_type = {
static VALUE
iseqw_new(const rb_iseq_t *iseq)
{
- union { const rb_iseq_t *in; void *out; } deconst;
- VALUE obj;
+ if (iseq->wrapper) {
+ return iseq->wrapper;
+ }
+ else {
+ union { const rb_iseq_t *in; void *out; } deconst;
+ VALUE obj;
+ deconst.in = iseq;
+ obj = TypedData_Wrap_Struct(rb_cISeq, &iseqw_data_type, deconst.out);
+ RB_OBJ_WRITTEN(obj, Qundef, iseq);
- deconst.in = iseq;
- obj = TypedData_Wrap_Struct(rb_cISeq, &iseqw_data_type, deconst.out);
- RB_OBJ_WRITTEN(obj, Qundef, iseq);
+ /* cache a wrapper object */
+ RB_OBJ_WRITE((VALUE)iseq, &iseq->wrapper, obj);
+ RB_OBJ_FREEZE((VALUE)iseq);
- return obj;
+ return obj;
+ }
}
VALUE
@@ -793,9 +1108,14 @@ iseqw_s_compile(int argc, VALUE *argv, VALUE self)
case 3: path = argv[--i];
case 2: file = argv[--i];
}
- if (NIL_P(file)) file = rb_fstring_cstr("<compiled>");
+
+ if (NIL_P(file)) file = rb_fstring_lit("<compiled>");
+ if (NIL_P(path)) path = file;
if (NIL_P(line)) line = INT2FIX(1);
+ Check_Type(path, T_STRING);
+ Check_Type(file, T_STRING);
+
return iseqw_new(rb_iseq_compile_with_option(src, file, path, line, 0, opt));
}
@@ -823,8 +1143,8 @@ static VALUE
iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
{
VALUE file, line = INT2FIX(1), opt = Qnil;
- VALUE parser, f, exc = Qnil;
- NODE *node;
+ VALUE parser, f, exc = Qnil, ret;
+ rb_ast_t *ast;
rb_compile_option_t option;
int i;
@@ -841,18 +1161,23 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
parser = rb_parser_new();
rb_parser_set_context(parser, NULL, FALSE);
- node = rb_parser_compile_file_path(parser, file, f, NUM2INT(line));
- if (!node) exc = GET_THREAD()->errinfo;
+ ast = rb_parser_compile_file_path(parser, file, f, NUM2INT(line));
+ if (!ast->body.root) exc = GET_EC()->errinfo;
rb_io_close(f);
- if (!node) rb_exc_raise(exc);
+ if (!ast->body.root) {
+ rb_ast_dispose(ast);
+ rb_exc_raise(exc);
+ }
make_compile_option(&option, opt);
- return iseqw_new(rb_iseq_new_with_opt(node, rb_fstring_cstr("<main>"),
- file,
- rb_realpath_internal(Qnil, file, 1),
- line, NULL, ISEQ_TYPE_TOP, &option));
+ ret = iseqw_new(rb_iseq_new_with_opt(&ast->body, rb_fstring_lit("<main>"),
+ file,
+ rb_realpath_internal(Qnil, file, 1),
+ line, NULL, ISEQ_TYPE_TOP, &option));
+ rb_ast_dispose(ast);
+ return ret;
}
/*
@@ -879,7 +1204,6 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
* * +:specialized_instruction+
* * +:stack_caching+
* * +:tailcall_optimization+
- * * +:trace_instruction+
*
* Additionally, +:debug_level+ can be set to an integer.
*
@@ -917,7 +1241,7 @@ iseqw_check(VALUE iseqw)
rb_iseq_t *iseq = DATA_PTR(iseqw);
if (!iseq->body) {
- ibf_load_iseq_complete(iseq);
+ rb_ibf_load_iseq_complete(iseq);
}
if (!iseq->body->location.label) {
@@ -955,14 +1279,17 @@ static VALUE
iseqw_inspect(VALUE self)
{
const rb_iseq_t *iseq = iseqw_check(self);
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ VALUE klass = rb_class_name(rb_obj_class(self));
- if (!iseq->body->location.label) {
- return rb_sprintf("#<%s: uninitialized>", rb_obj_classname(self));
+ if (!body->location.label) {
+ return rb_sprintf("#<%"PRIsVALUE": uninitialized>", klass);
}
else {
- return rb_sprintf("<%s:%s@%s>",
- rb_obj_classname(self),
- RSTRING_PTR(iseq->body->location.label), RSTRING_PTR(iseq->body->location.path));
+ return rb_sprintf("<%"PRIsVALUE":%"PRIsVALUE"@%"PRIsVALUE":%d>",
+ klass,
+ body->location.label, rb_iseq_path(iseq),
+ FIX2INT(rb_iseq_first_lineno(iseq)));
}
}
@@ -1014,7 +1341,7 @@ iseqw_path(VALUE self)
static VALUE
iseqw_absolute_path(VALUE self)
{
- return rb_iseq_absolute_path(iseqw_check(self));
+ return rb_iseq_realpath(iseqw_check(self));
}
/* Returns the label of this instruction sequence.
@@ -1149,7 +1476,7 @@ static VALUE iseq_data_to_ary(const rb_iseq_t *iseq);
* The type of the instruction sequence.
*
* Valid values are +:top+, +:method+, +:block+, +:class+, +:rescue+,
- * +:ensure+, +:eval+, +:main+, and +:defined_guard+.
+ * +:ensure+, +:eval+, +:main+, and +plain+.
*
* [locals]
* An array containing the names of all arguments and local variables as
@@ -1179,48 +1506,167 @@ iseqw_to_a(VALUE self)
return iseq_data_to_ary(iseq);
}
-/* TODO: search algorithm is brute force.
- this should be binary search or so. */
+#if VM_INSN_INFO_TABLE_IMPL == 1 /* binary search */
+static const struct iseq_insn_info_entry *
+get_insn_info_binary_search(const rb_iseq_t *iseq, size_t pos)
+{
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ size_t size = body->insns_info.size;
+ const struct iseq_insn_info_entry *insns_info = body->insns_info.body;
+ const unsigned int *positions = body->insns_info.positions;
+ const int debug = 0;
-static const struct iseq_line_info_entry *
-get_line_info(const rb_iseq_t *iseq, size_t pos)
+ 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);
+ }
+
+ if (size == 0) {
+ return NULL;
+ }
+ else if (size == 1) {
+ 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];
+ }
+}
+
+static const struct iseq_insn_info_entry *
+get_insn_info(const rb_iseq_t *iseq, size_t pos)
+{
+ return get_insn_info_binary_search(iseq, pos);
+}
+#endif
+
+#if VM_INSN_INFO_TABLE_IMPL == 2 /* succinct bitvector */
+static const struct iseq_insn_info_entry *
+get_insn_info_succinct_bitvector(const rb_iseq_t *iseq, size_t pos)
{
- size_t i = 0, size = iseq->body->line_info_size;
- const struct iseq_line_info_entry *table = iseq->body->line_info_table;
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ size_t size = body->insns_info.size;
+ const struct iseq_insn_info_entry *insns_info = body->insns_info.body;
+ const int debug = 0;
+
+ if (debug) {
+#if VM_CHECK_MODE > 0
+ const unsigned int *positions = body->insns_info.positions;
+ 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);
+#else
+ printf("size: %"PRIuSIZE"\n", size);
+ printf("insns_info[%"PRIuSIZE"]: line: %d, pos: %"PRIuSIZE"\n",
+ (size_t)0, insns_info[0].line_no, pos);
+#endif
+ }
+
+ if (size == 0) {
+ return NULL;
+ }
+ else if (size == 1) {
+ 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];
+ }
+}
+
+static const struct iseq_insn_info_entry *
+get_insn_info(const rb_iseq_t *iseq, size_t pos)
+{
+ return get_insn_info_succinct_bitvector(iseq, pos);
+}
+#endif
+
+#if VM_CHECK_MODE > 0 || VM_INSN_INFO_TABLE_IMPL == 0
+static const struct iseq_insn_info_entry *
+get_insn_info_linear_search(const rb_iseq_t *iseq, size_t pos)
+{
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ size_t i = 0, size = body->insns_info.size;
+ const struct iseq_insn_info_entry *insns_info = body->insns_info.body;
+ const unsigned int *positions = body->insns_info.positions;
const int debug = 0;
if (debug) {
printf("size: %"PRIuSIZE"\n", size);
- printf("table[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
- i, table[i].position, table[i].line_no, pos);
+ printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
+ i, positions[i], insns_info[i].line_no, pos);
}
if (size == 0) {
- return 0;
+ return NULL;
}
else if (size == 1) {
- return &table[0];
+ return &insns_info[0];
}
else {
for (i=1; i<size; i++) {
- if (debug) printf("table[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
- i, table[i].position, table[i].line_no, pos);
+ if (debug) printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
+ i, positions[i], insns_info[i].line_no, pos);
- if (table[i].position == pos) {
- return &table[i];
+ if (positions[i] == pos) {
+ return &insns_info[i];
}
- if (table[i].position > pos) {
- return &table[i-1];
+ if (positions[i] > pos) {
+ return &insns_info[i-1];
}
}
}
- return &table[i-1];
+ return &insns_info[i-1];
}
+#endif
-static unsigned int
-find_line_no(const rb_iseq_t *iseq, size_t pos)
+#if VM_INSN_INFO_TABLE_IMPL == 0 /* linear search */
+static const struct iseq_insn_info_entry *
+get_insn_info(const rb_iseq_t *iseq, size_t pos)
{
- const struct iseq_line_info_entry *entry = get_line_info(iseq, pos);
+ return get_insn_info_linear_search(iseq, pos);
+}
+#endif
+
+#if VM_CHECK_MODE > 0 && VM_INSN_INFO_TABLE_IMPL > 0
+static void
+validate_get_insn_info(const rb_iseq_t *iseq)
+{
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ 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);
+ }
+ }
+}
+#endif
+
+unsigned int
+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;
@@ -1230,30 +1676,60 @@ find_line_no(const rb_iseq_t *iseq, size_t pos)
}
}
-unsigned int
-rb_iseq_line_no(const rb_iseq_t *iseq, size_t pos)
+MJIT_FUNC_EXPORTED rb_event_flag_t
+rb_iseq_event_flags(const rb_iseq_t *iseq, size_t pos)
{
- if (pos == 0) {
- return find_line_no(iseq, pos);
+ const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pos);
+ if (entry) {
+ return entry->events;
}
else {
- return find_line_no(iseq, pos - 1);
+ return 0;
+ }
+}
+
+void
+rb_iseq_clear_event_flags(const rb_iseq_t *iseq, size_t pos, rb_event_flag_t reset)
+{
+ struct iseq_insn_info_entry *entry = (struct iseq_insn_info_entry *)get_insn_info(iseq, pos);
+ if (entry) {
+ entry->events &= ~reset;
+ if (!(entry->events & iseq->aux.exec.global_trace_events)) {
+ void rb_iseq_trace_flag_cleared(const rb_iseq_t *iseq, size_t pos);
+ rb_iseq_trace_flag_cleared(iseq, pos);
+ }
}
}
static VALUE
-id_to_name(ID id, VALUE default_value)
+local_var_name(const rb_iseq_t *diseq, VALUE level, VALUE op)
{
- VALUE str = rb_id2str(id);
- if (!str) {
- str = default_value;
+ VALUE i;
+ VALUE name;
+ ID lid;
+ int idx;
+
+ for (i = 0; i < level; i++) {
+ diseq = diseq->body->parent_iseq;
}
- else if (!rb_str_symname_p(str)) {
- str = rb_str_inspect(str);
+ idx = diseq->body->local_table_size - (int)op - 1;
+ lid = diseq->body->local_table[idx];
+ name = rb_id2str(lid);
+ if (!name) {
+ name = rb_str_new_cstr("?");
}
- return str;
+ else if (!rb_str_symname_p(name)) {
+ name = rb_str_inspect(name);
+ }
+ else {
+ name = rb_str_dup(name);
+ }
+ rb_str_catf(name, "@%d", idx);
+ return name;
}
+int rb_insn_unified_local_var_level(VALUE);
+
VALUE
rb_insn_operand_intern(const rb_iseq_t *iseq,
VALUE insn, int op_no, VALUE op,
@@ -1269,26 +1745,33 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
break;
case TS_NUM: /* ULONG */
+ if (insn == BIN(defined) && op_no == 0) {
+ enum defined_type deftype = (enum defined_type)op;
+ if (deftype == DEFINED_FUNC) {
+ ret = rb_fstring_lit("func"); break;
+ }
+ if (deftype == DEFINED_REF) {
+ ret = rb_fstring_lit("ref"); break;
+ }
+ ret = rb_iseq_defined_string(deftype);
+ 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:{
- if (insn == BIN(getlocal) || insn == BIN(setlocal)) {
- if (pnop) {
- const rb_iseq_t *diseq = iseq;
- VALUE level = *pnop, i;
- ID lid;
-
- for (i = 0; i < level; i++) {
- diseq = diseq->body->parent_iseq;
- }
- lid = diseq->body->local_table[diseq->body->local_table_size +
- VM_ENV_DATA_SIZE - 1 - op];
- ret = id_to_name(lid, INT2FIX('*'));
- }
- else {
- ret = rb_sprintf("%"PRIuVALUE, op);
- }
+ 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));
@@ -1296,10 +1779,24 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
break;
}
case TS_ID: /* ID (symbol) */
- op = ID2SYM(op);
+ 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_inspect(op);
if (CLASS_OF(op) == rb_cISeq) {
if (child) {
@@ -1330,6 +1827,7 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
break;
case TS_IC:
+ case TS_ISE:
ret = rb_sprintf("<is:%"PRIdPTRDIFF">", (union iseq_inline_storage_entry *)op - iseq->body->is_entries);
break;
@@ -1352,15 +1850,19 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
if (ci->flag) {
VALUE flags = rb_ary_new();
- if (ci->flag & VM_CALL_ARGS_SPLAT) rb_ary_push(flags, rb_str_new2("ARGS_SPLAT"));
- if (ci->flag & VM_CALL_ARGS_BLOCKARG) rb_ary_push(flags, rb_str_new2("ARGS_BLOCKARG"));
- if (ci->flag & VM_CALL_FCALL) rb_ary_push(flags, rb_str_new2("FCALL"));
- if (ci->flag & VM_CALL_VCALL) rb_ary_push(flags, rb_str_new2("VCALL"));
- if (ci->flag & VM_CALL_TAILCALL) rb_ary_push(flags, rb_str_new2("TAILCALL"));
- if (ci->flag & VM_CALL_SUPER) rb_ary_push(flags, rb_str_new2("SUPER"));
- if (ci->flag & VM_CALL_KWARG) rb_ary_push(flags, rb_str_new2("KWARG"));
- if (ci->flag & VM_CALL_OPT_SEND) rb_ary_push(flags, rb_str_new2("SNED")); /* maybe not reachable */
- if (ci->flag & VM_CALL_ARGS_SIMPLE) rb_ary_push(flags, rb_str_new2("ARGS_SIMPLE")); /* maybe not reachable */
+# define CALL_FLAG(n) if (ci->flag & 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(OPT_SEND); /* maybe not reachable */
rb_ary_push(ary, rb_ary_join(flags, rb_str_new2("|")));
}
ret = rb_sprintf("<callinfo!%"PRIsVALUE">", rb_ary_join(ary, rb_str_new2(", ")));
@@ -1389,11 +1891,20 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
break;
default:
- rb_bug("insn_operand_intern: unknown operand type: %c", type);
+ rb_bug("unknown operand type: %c", type);
}
return ret;
}
+static VALUE
+right_strip(VALUE str)
+{
+ const char *beg = RSTRING_PTR(str), *end = RSTRING_END(str);
+ while (end-- > beg && *end == ' ');
+ rb_str_set_len(str, end - beg + 1);
+ return str;
+}
+
/**
* Disassemble a instruction
* Iseq -> Iseq inspect object
@@ -1411,15 +1922,15 @@ rb_iseq_disasm_insn(VALUE ret, const VALUE *code, size_t pos,
insn_name_buff = insn_name(insn);
if (1) {
- rb_str_catf(str, "%04"PRIuSIZE" %-16s ", pos, 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" %-16.*s ", pos,
+ rb_str_catf(str, "%04"PRIuSIZE" %-28.*s ", pos,
(int)strcspn(insn_name_buff, "_"), insn_name_buff);
}
for (j = 0; types[j]; j++) {
- const char *types = insn_op_types(insn);
VALUE opstr = rb_insn_operand_intern(iseq, insn, j, code[pos + j + 1],
len, pos, &code[pos + j + 2],
child);
@@ -1431,8 +1942,8 @@ rb_iseq_disasm_insn(VALUE ret, const VALUE *code, size_t pos,
}
{
- unsigned int line_no = find_line_no(iseq, pos);
- unsigned int prev = pos == 0 ? 0 : find_line_no(iseq, pos - 1);
+ 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);
@@ -1440,12 +1951,31 @@ rb_iseq_disasm_insn(VALUE ret, const VALUE *code, size_t pos,
}
}
+ {
+ 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_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);
}
else {
- printf("%s\n", RSTRING_PTR(str));
+ printf("%.*s\n", (int)RSTRING_LEN(str), RSTRING_PTR(str));
}
return len;
}
@@ -1467,7 +1997,7 @@ catch_type(int type)
case CATCH_TYPE_NEXT:
return "next";
default:
- rb_bug("unknown catch type (%d)", type);
+ rb_bug("unknown catch type: %d", type);
return 0;
}
}
@@ -1475,127 +2005,163 @@ catch_type(int type)
static VALUE
iseq_inspect(const rb_iseq_t *iseq)
{
- if (!iseq->body->location.label) {
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ if (!body->location.label) {
return rb_sprintf("#<ISeq: uninitialized>");
}
else {
- return rb_sprintf("#<ISeq:%s@%s>", RSTRING_PTR(iseq->body->location.label), RSTRING_PTR(iseq->body->location.path));
+ 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);
}
}
-VALUE
-rb_iseq_disasm(const rb_iseq_t *iseq)
+static VALUE
+rb_iseq_disasm_recursive(const rb_iseq_t *iseq, VALUE indent)
{
+ const struct rb_iseq_constant_body *const body = iseq->body;
VALUE *code;
VALUE str = rb_str_new(0, 0);
VALUE child = rb_ary_tmp_new(3);
unsigned int size;
unsigned int i;
long l;
- const ID *tbl;
size_t n;
enum {header_minlen = 72};
+ st_table *done_iseq = 0;
+ const char *indent_str;
+ long indent_len;
rb_secure(1);
- size = iseq->body->iseq_size;
+ size = body->iseq_size;
+
+ indent_len = RSTRING_LEN(indent);
+ indent_str = RSTRING_PTR(indent);
+ rb_str_cat(str, indent_str, indent_len);
rb_str_cat2(str, "== disasm: ");
- rb_str_concat(str, iseq_inspect(iseq));
- if ((l = RSTRING_LEN(str)) < header_minlen) {
- rb_str_resize(str, header_minlen);
- memset(RSTRING_PTR(str) + l, '=', header_minlen - l);
+ rb_str_append(str, iseq_inspect(iseq));
+ 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_cat2(str, "\n");
/* show catch table information */
- if (iseq->body->catch_table) {
+ if (body->catch_table) {
+ rb_str_cat(str, indent_str, indent_len);
rb_str_cat2(str, "== catch table\n");
}
- if (iseq->body->catch_table) {
- for (i = 0; i < iseq->body->catch_table->size; i++) {
- const struct iseq_catch_table_entry *entry = &iseq->body->catch_table->entries[i];
+ 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 = &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) {
- rb_str_concat(str, rb_iseq_disasm(rb_iseq_check(entry->iseq)));
+ 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();
+ 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 (iseq->body->catch_table) {
+ if (body->catch_table) {
+ rb_str_cat(str, indent_str, indent_len);
rb_str_cat2(str, "|-------------------------------------"
"-----------------------------------\n");
}
/* show local table information */
- tbl = iseq->body->local_table;
-
- if (tbl) {
+ 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",
- iseq->body->local_table_size,
- iseq->body->param.lead_num,
- iseq->body->param.opt_num,
- iseq->body->param.flags.has_rest ? iseq->body->param.rest_start : -1,
- iseq->body->param.post_num,
- iseq->body->param.flags.has_block ? iseq->body->param.block_start : -1,
- iseq->body->param.flags.has_kw ? iseq->body->param.keyword->num : -1,
- iseq->body->param.flags.has_kw ? iseq->body->param.keyword->required_num : -1,
- iseq->body->param.flags.has_kwrest ? iseq->body->param.keyword->rest_start : -1);
-
- for (i = 0; i < iseq->body->local_table_size; i++) {
- int li = (int)i;
+ 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 = id_to_name(tbl[i], 0);
+ VALUE name = local_var_name(iseq, 0, i);
char argi[0x100] = "";
char opti[0x100] = "";
- if (iseq->body->param.flags.has_opt) {
- int argc = iseq->body->param.lead_num;
- int opts = iseq->body->param.opt_num;
+ 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,
- iseq->body->param.opt_table[li - argc]);
+ body->param.opt_table[li - argc]);
}
}
- snprintf(argi, sizeof(argi), "%s%s%s%s%s", /* arg, opts, rest, post block */
- iseq->body->param.lead_num > li ? "Arg" : "",
+ snprintf(argi, sizeof(argi), "%s%s%s%s%s%s", /* arg, opts, rest, post, kwrest, block */
+ body->param.lead_num > li ? "Arg" : "",
opti,
- (iseq->body->param.flags.has_rest && iseq->body->param.rest_start == li) ? "Rest" : "",
- (iseq->body->param.flags.has_post && iseq->body->param.post_start <= li && li < iseq->body->param.post_start + iseq->body->param.post_num) ? "Post" : "",
- (iseq->body->param.flags.has_block && iseq->body->param.block_start == li) ? "Block" : "");
+ (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_catf(str, "[%2d] ", iseq->body->local_table_size - i);
+ rb_str_cat(str, indent_str, indent_len);
+ rb_str_catf(str, "[%2d] ", i + 1);
width = RSTRING_LEN(str) + 11;
- if (name)
- rb_str_append(str, name);
- else
- rb_str_cat2(str, "?");
+ 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_cat2(str, "\n");
+ 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);
}
for (l = 0; l < RARRAY_LEN(child); l++) {
VALUE isv = rb_ary_entry(child, l);
- rb_str_concat(str, rb_iseq_disasm(rb_iseq_check((rb_iseq_t *)isv)));
+ 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);
}
+ if (done_iseq) st_free_table(done_iseq);
return str;
}
+VALUE
+rb_iseq_disasm(const rb_iseq_t *iseq)
+{
+ return rb_iseq_disasm_recursive(iseq, rb_str_new(0, 0));
+}
+
/*
* call-seq:
* iseq.disasm -> str
@@ -1620,6 +2186,115 @@ iseqw_disasm(VALUE self)
return rb_iseq_disasm(iseqw_check(self));
}
+static int
+iseq_iterate_children(const rb_iseq_t *iseq, void (*iter_func)(const rb_iseq_t *child_iseq, void *data), void *data)
+{
+ unsigned int i;
+ VALUE *code = rb_iseq_original_iseq(iseq);
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ const rb_iseq_t *child;
+ VALUE all_children = rb_obj_hide(rb_ident_hash_new());
+
+ if (body->catch_table) {
+ for (i = 0; i < body->catch_table->size; i++) {
+ const struct iseq_catch_table_entry *entry = &body->catch_table->entries[i];
+ child = entry->iseq;
+ if (child) {
+ if (rb_hash_aref(all_children, (VALUE)child) == Qnil) {
+ rb_hash_aset(all_children, (VALUE)child, Qtrue);
+ (*iter_func)(child, data);
+ }
+ }
+ }
+ }
+
+ for (i=0; i<body->iseq_size;) {
+ VALUE insn = code[i];
+ int len = insn_len(insn);
+ const char *types = insn_op_types(insn);
+ int j;
+
+ for (j=0; types[j]; j++) {
+ switch (types[j]) {
+ case TS_ISEQ:
+ child = (const rb_iseq_t *)code[i+j+1];
+ if (child) {
+ if (rb_hash_aref(all_children, (VALUE)child) == Qnil) {
+ rb_hash_aset(all_children, (VALUE)child, Qtrue);
+ (*iter_func)(child, data);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ i += len;
+ }
+
+ return (int)RHASH_SIZE(all_children);
+}
+
+static void
+yield_each_children(const rb_iseq_t *child_iseq, void *data)
+{
+ rb_yield(iseqw_new(child_iseq));
+}
+
+/*
+ * call-seq:
+ * iseq.each_child{|child_iseq| ...} -> iseq
+ *
+ * Iterate all direct child instruction sequences.
+ * Iteration order is implementation/version defined
+ * so that people should not rely on the order.
+ */
+static VALUE
+iseqw_each_child(VALUE self)
+{
+ const rb_iseq_t *iseq = iseqw_check(self);
+ iseq_iterate_children(iseq, yield_each_children, NULL);
+ return self;
+}
+
+static void
+push_event_info(const rb_iseq_t *iseq, rb_event_flag_t events, int line, VALUE ary)
+{
+#define C(ev, cstr, l) if (events & ev) rb_ary_push(ary, rb_ary_new_from_args(2, l, ID2SYM(rb_intern(cstr))));
+ C(RUBY_EVENT_CLASS, "class", rb_iseq_first_lineno(iseq));
+ C(RUBY_EVENT_CALL, "call", rb_iseq_first_lineno(iseq));
+ C(RUBY_EVENT_B_CALL, "b_call", rb_iseq_first_lineno(iseq));
+ C(RUBY_EVENT_LINE, "line", INT2FIX(line));
+ C(RUBY_EVENT_END, "end", INT2FIX(line));
+ C(RUBY_EVENT_RETURN, "return", INT2FIX(line));
+ C(RUBY_EVENT_B_RETURN, "b_return", INT2FIX(line));
+#undef C
+}
+
+/*
+ * call-seq:
+ * iseq.trace_points -> ary
+ *
+ * Return trace points in the instruction sequence.
+ * Return an array of [line, event_symbol] pair.
+ */
+static VALUE
+iseqw_trace_points(VALUE self)
+{
+ const rb_iseq_t *iseq = iseqw_check(self);
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ unsigned int i;
+ 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);
+ }
+ }
+ return ary;
+}
+
/*
* Returns the instruction sequence containing the given proc or method.
*
@@ -1663,14 +2338,17 @@ iseqw_s_of(VALUE klass, VALUE body)
rb_secure(1);
if (rb_obj_is_proc(body)) {
- iseq = vm_proc_iseq(body);
+ iseq = vm_proc_iseq(body);
- if (!rb_obj_is_iseq((VALUE)iseq)) {
- iseq = NULL;
- }
+ if (!rb_obj_is_iseq((VALUE)iseq)) {
+ iseq = NULL;
+ }
}
- else {
- iseq = rb_method_iseq(body);
+ else if (rb_obj_is_method(body)) {
+ iseq = rb_method_iseq(body);
+ }
+ else if (rb_typeddata_is_instance_of(body, &iseqw_data_type)) {
+ return body;
}
return iseq ? iseqw_new(iseq) : Qnil;
@@ -1741,7 +2419,7 @@ ruby_node_name(int node)
switch (node) {
#include "node_name.inc"
default:
- rb_bug("unknown node (%d)", node);
+ rb_bug("unknown node: %d", node);
return 0;
}
}
@@ -1772,7 +2450,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("exception_type2symbol: unknown type %d", (int)type);
+ rb_bug("unknown exception type: %d", (int)type);
}
return ID2SYM(id);
}
@@ -1790,9 +2468,10 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
{
unsigned int i;
long l;
- size_t ti;
+ const struct rb_iseq_constant_body *const iseq_body = iseq->body;
+ const struct iseq_insn_info_entry *prev_insn_info;
unsigned int pos;
- unsigned int line = 0;
+ int last_line = 0;
VALUE *seq, *iseq_original;
VALUE val = rb_ary_new();
@@ -1804,7 +2483,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
VALUE exception = rb_ary_new(); /* [[....]] */
VALUE misc = rb_hash_new();
- static VALUE insn_syms[VM_INSTRUCTION_SIZE];
+ static VALUE insn_syms[VM_INSTRUCTION_SIZE/2]; /* w/o-trace only */
struct st_table *labels_table = st_init_numtable();
DECL_SYMBOL(top);
@@ -1815,11 +2494,11 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
DECL_SYMBOL(ensure);
DECL_SYMBOL(eval);
DECL_SYMBOL(main);
- DECL_SYMBOL(defined_guard);
+ DECL_SYMBOL(plain);
if (sym_top == 0) {
int i;
- for (i=0; i<VM_INSTRUCTION_SIZE; i++) {
+ for (i=0; i<numberof(insn_syms); i++) {
insn_syms[i] = ID2SYM(rb_intern(insn_name(i)));
}
INIT_SYMBOL(top);
@@ -1830,11 +2509,11 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
INIT_SYMBOL(ensure);
INIT_SYMBOL(eval);
INIT_SYMBOL(main);
- INIT_SYMBOL(defined_guard);
+ INIT_SYMBOL(plain);
}
/* type */
- switch (iseq->body->type) {
+ switch (iseq_body->type) {
case ISEQ_TYPE_TOP: type = sym_top; break;
case ISEQ_TYPE_METHOD: type = sym_method; break;
case ISEQ_TYPE_BLOCK: type = sym_block; break;
@@ -1843,19 +2522,19 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
case ISEQ_TYPE_ENSURE: type = sym_ensure; break;
case ISEQ_TYPE_EVAL: type = sym_eval; break;
case ISEQ_TYPE_MAIN: type = sym_main; break;
- case ISEQ_TYPE_DEFINED_GUARD: type = sym_defined_guard; break;
- default: rb_bug("unsupported iseq type");
+ case ISEQ_TYPE_PLAIN: type = sym_plain; break;
+ default: rb_bug("unsupported iseq type: %d", (int)iseq_body->type);
};
/* locals */
- for (i=0; i<iseq->body->local_table_size; i++) {
- ID lid = iseq->body->local_table[i];
+ 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));
+ rb_ary_push(locals, ULONG2NUM(iseq_body->local_table_size-i+1));
}
}
else {
@@ -1865,57 +2544,58 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
/* 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);
+ 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);
+ 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) {
+ 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<iseq->body->param.keyword->required_num; i++) {
- rb_ary_push(keywords, ID2SYM(iseq->body->param.keyword->table[i]));
+ for (i=0; i<keyword->required_num; i++) {
+ rb_ary_push(keywords, ID2SYM(keyword->table[i]));
}
- for (j=0; i<iseq->body->param.keyword->num; i++, j++) {
- VALUE key = rb_ary_new_from_args(1, ID2SYM(iseq->body->param.keyword->table[i]));
- if (iseq->body->param.keyword->default_values[j] != Qundef) {
- rb_ary_push(key, iseq->body->param.keyword->default_values[j]);
+ 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(iseq->body->param.keyword->bits_start));
+ 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(iseq->body->param.keyword->rest_start));
- if (iseq->body->param.flags.ambiguous_param0) rb_hash_aset(params, ID2SYM(rb_intern("ambiguous_param0")), Qtrue);
+ 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; ) {
+ 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);
- rb_ary_push(ary, insn_syms[insn]);
+ rb_ary_push(ary, insn_syms[insn%numberof(insn_syms)]);
for (j=0; j<len-1; j++, seq++) {
switch (insn_op_type(insn, j)) {
case TS_OFFSET: {
@@ -1949,9 +2629,10 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
}
break;
case TS_IC:
+ case TS_ISE:
{
union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)*seq;
- rb_ary_push(ary, INT2FIX(is - iseq->body->is_entries));
+ rb_ary_push(ary, INT2FIX(is - iseq_body->is_entries));
}
break;
case TS_CALLINFO:
@@ -2024,9 +2705,9 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
nbody = body;
/* exception */
- if (iseq->body->catch_table) for (i=0; i<iseq->body->catch_table->size; i++) {
+ 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 = &iseq->body->catch_table->entries[i];
+ const struct iseq_catch_table_entry *entry = &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)));
@@ -2043,9 +2724,10 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
/* make body with labels and insert line number */
body = rb_ary_new();
- ti = 0;
+ prev_insn_info = NULL;
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;
@@ -2053,10 +2735,26 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
rb_ary_push(body, (VALUE)label);
}
- if (ti < iseq->body->line_info_size && iseq->body->line_info_table[ti].position == pos) {
- line = iseq->body->line_info_table[ti].line_no;
- rb_ary_push(body, INT2FIX(line));
- ti++;
+ info = get_insn_info(iseq, pos);
+
+ 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;
+ }
+#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);
+#undef CHECK_EVENT
+ prev_insn_info = info;
}
rb_ary_push(body, ary);
@@ -2066,11 +2764,17 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
st_free_table(labels_table);
- rb_hash_aset(misc, ID2SYM(rb_intern("arg_size")), INT2FIX(iseq->body->param.size));
- rb_hash_aset(misc, ID2SYM(rb_intern("local_size")), INT2FIX(iseq->body->local_table_size));
- rb_hash_aset(misc, ID2SYM(rb_intern("stack_max")), INT2FIX(iseq->body->stack_max));
+ rb_hash_aset(misc, ID2SYM(rb_intern("arg_size")), INT2FIX(iseq_body->param.size));
+ rb_hash_aset(misc, ID2SYM(rb_intern("local_size")), INT2FIX(iseq_body->local_table_size));
+ 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)));
- /* TODO: compatibility issue */
/*
* [:magic, :major_version, :minor_version, :format_type, :misc,
* :name, :path, :absolute_path, :start_lineno, :type, :locals, :args,
@@ -2081,10 +2785,10 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
rb_ary_push(val, INT2FIX(ISEQ_MINOR_VERSION)); /* minor */
rb_ary_push(val, INT2FIX(1));
rb_ary_push(val, misc);
- rb_ary_push(val, iseq->body->location.label);
- rb_ary_push(val, iseq->body->location.path);
- rb_ary_push(val, iseq->body->location.absolute_path);
- rb_ary_push(val, iseq->body->location.first_lineno);
+ 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, type);
rb_ary_push(val, locals);
rb_ary_push(val, params);
@@ -2097,10 +2801,12 @@ VALUE
rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc)
{
int i, r;
- VALUE a, args = rb_ary_new2(iseq->body->param.size);
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ const struct rb_iseq_param_keyword *const keyword = body->param.keyword;
+ VALUE a, args = rb_ary_new2(body->param.size);
ID req, opt, rest, block, key, keyrest;
#define PARAM_TYPE(type) rb_ary_push(a = rb_ary_new2(2), ID2SYM(type))
-#define PARAM_ID(i) iseq->body->local_table[(i)]
+#define PARAM_ID(i) body->local_table[(i)]
#define PARAM(i, type) ( \
PARAM_TYPE(type), \
rb_id2str(PARAM_ID(i)) ? \
@@ -2110,18 +2816,18 @@ rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc)
CONST_ID(req, "req");
CONST_ID(opt, "opt");
if (is_proc) {
- for (i = 0; i < iseq->body->param.lead_num; i++) {
+ 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 < iseq->body->param.lead_num; i++) {
+ for (i = 0; i < body->param.lead_num; i++) {
rb_ary_push(args, PARAM(i, req));
}
}
- r = iseq->body->param.lead_num + iseq->body->param.opt_num;
+ r = body->param.lead_num + body->param.opt_num;
for (; i < r; i++) {
PARAM_TYPE(opt);
if (rb_id2str(PARAM_ID(i))) {
@@ -2129,52 +2835,52 @@ rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc)
}
rb_ary_push(args, a);
}
- if (iseq->body->param.flags.has_rest) {
+ if (body->param.flags.has_rest) {
CONST_ID(rest, "rest");
- rb_ary_push(args, PARAM(iseq->body->param.rest_start, rest));
+ rb_ary_push(args, PARAM(body->param.rest_start, rest));
}
- r = iseq->body->param.post_start + iseq->body->param.post_num;
+ r = body->param.post_start + body->param.post_num;
if (is_proc) {
- for (i = iseq->body->param.post_start; i < r; i++) {
+ 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 = iseq->body->param.post_start; i < r; i++) {
+ for (i = body->param.post_start; i < r; i++) {
rb_ary_push(args, PARAM(i, req));
}
}
- if (iseq->body->param.flags.has_kw) {
+ if (body->param.flags.has_kw) {
i = 0;
- if (iseq->body->param.keyword->required_num > 0) {
+ if (keyword->required_num > 0) {
ID keyreq;
CONST_ID(keyreq, "keyreq");
- for (; i < iseq->body->param.keyword->required_num; i++) {
+ for (; i < keyword->required_num; i++) {
PARAM_TYPE(keyreq);
- if (rb_id2str(iseq->body->param.keyword->table[i])) {
- rb_ary_push(a, ID2SYM(iseq->body->param.keyword->table[i]));
+ 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 < iseq->body->param.keyword->num; i++) {
+ for (; i < keyword->num; i++) {
PARAM_TYPE(key);
- if (rb_id2str(iseq->body->param.keyword->table[i])) {
- rb_ary_push(a, ID2SYM(iseq->body->param.keyword->table[i]));
+ if (rb_id2str(keyword->table[i])) {
+ rb_ary_push(a, ID2SYM(keyword->table[i]));
}
rb_ary_push(args, a);
}
}
- if (iseq->body->param.flags.has_kwrest) {
+ if (body->param.flags.has_kwrest) {
CONST_ID(keyrest, "keyrest");
- rb_ary_push(args, PARAM(iseq->body->param.keyword->rest_start, keyrest));
+ rb_ary_push(args, PARAM(keyword->rest_start, keyrest));
}
- if (iseq->body->param.flags.has_block) {
+ if (body->param.flags.has_block) {
CONST_ID(block, "block");
- rb_ary_push(args, PARAM(iseq->body->param.block_start, block));
+ rb_ary_push(args, PARAM(body->param.block_start, block));
}
return args;
}
@@ -2219,131 +2925,255 @@ rb_iseq_defined_string(enum defined_type type)
return str;
}
-/* Experimental tracing support: trace(line) -> trace(specified_line)
- * MRI Specific.
- */
+/* A map from encoded_insn to insn_data: decoded insn number, its len,
+ * non-trace version of encoded insn, and trace version. */
+
+static st_table *encoded_insn_data;
+typedef struct insn_data_struct {
+ int insn;
+ int insn_len;
+ void *notrace_encoded_insn;
+ void *trace_encoded_insn;
+} insn_data_t;
+static insn_data_t insn_data[VM_INSTRUCTION_SIZE/2];
+
+void
+rb_vm_encoded_insn_data_table_init(void)
+{
+#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
+ const void * const *table = rb_vm_get_insns_address_table();
+#define INSN_CODE(insn) ((VALUE)table[insn])
+#else
+#define INSN_CODE(insn) (insn)
+#endif
+ st_data_t insn;
+ encoded_insn_data = st_init_numtable_with_size(VM_INSTRUCTION_SIZE / 2);
+
+ for (insn = 0; insn < VM_INSTRUCTION_SIZE/2; insn++) {
+ st_data_t key1 = (st_data_t)INSN_CODE(insn);
+ st_data_t key2 = (st_data_t)INSN_CODE(insn + VM_INSTRUCTION_SIZE/2);
+
+ insn_data[insn].insn = (int)insn;
+ insn_data[insn].insn_len = insn_len(insn);
+ insn_data[insn].notrace_encoded_insn = (void *) key1;
+ insn_data[insn].trace_encoded_insn = (void *) key2;
+
+ st_add_direct(encoded_insn_data, key1, (st_data_t)&insn_data[insn]);
+ st_add_direct(encoded_insn_data, key2, (st_data_t)&insn_data[insn]);
+ }
+}
int
-rb_iseqw_line_trace_each(VALUE iseqw, int (*func)(int line, rb_event_flag_t *events_ptr, void *d), void *data)
+rb_vm_insn_addr2insn(const void *addr)
{
- int trace_num = 0;
- unsigned int pos;
- size_t insn;
- const rb_iseq_t *iseq = iseqw_check(iseqw);
- int cont = 1;
- VALUE *iseq_original;
-
- iseq_original = rb_iseq_original_iseq(iseq);
- for (pos = 0; cont && pos < iseq->body->iseq_size; pos += insn_len(insn)) {
- insn = iseq_original[pos];
-
- if (insn == BIN(trace)) {
- rb_event_flag_t current_events;
-
- current_events = (rb_event_flag_t)iseq_original[pos+1];
-
- if (current_events & RUBY_EVENT_LINE) {
- rb_event_flag_t events = current_events & RUBY_EVENT_SPECIFIED_LINE;
- trace_num++;
-
- if (func) {
- int line = find_line_no(iseq, pos);
- /* printf("line: %d\n", line); */
- cont = (*func)(line, &events, data);
- if (current_events != events) {
- VALUE *encoded = (VALUE *)iseq->body->iseq_encoded;
- iseq_original[pos+1] = encoded[pos+1] =
- (VALUE)(current_events | (events & RUBY_EVENT_SPECIFIED_LINE));
- }
- }
- }
- }
+ st_data_t key = (st_data_t)addr;
+ st_data_t val;
+
+ if (st_lookup(encoded_insn_data, key, &val)) {
+ insn_data_t *e = (insn_data_t *)val;
+ return (int)e->insn;
}
- return trace_num;
+
+ rb_bug("rb_vm_insn_addr2insn: invalid insn address: %p", addr);
}
-static int
-collect_trace(int line, rb_event_flag_t *events_ptr, void *ptr)
+static inline int
+encoded_iseq_trace_instrument(VALUE *iseq_encoded_insn, rb_event_flag_t turnon)
+{
+ st_data_t key = (st_data_t)*iseq_encoded_insn;
+ st_data_t val;
+
+ if (st_lookup(encoded_insn_data, key, &val)) {
+ insn_data_t *e = (insn_data_t *)val;
+ *iseq_encoded_insn = (VALUE) (turnon ? e->trace_encoded_insn : e->notrace_encoded_insn);
+ return e->insn_len;
+ }
+
+ rb_bug("trace_instrument: invalid insn address: %p", (void *)*iseq_encoded_insn);
+}
+
+void
+rb_iseq_trace_flag_cleared(const rb_iseq_t *iseq, size_t pos)
{
- VALUE result = (VALUE)ptr;
- rb_ary_push(result, INT2NUM(line));
- return 1;
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ VALUE *iseq_encoded = (VALUE *)body->iseq_encoded;
+ encoded_iseq_trace_instrument(&iseq_encoded[pos], 0);
}
-/*
- * <b>Experimental MRI specific feature, only available as C level api.</b>
- *
- * Returns all +specified_line+ events.
- */
-VALUE
-rb_iseqw_line_trace_all(VALUE iseqw)
+static int
+iseq_add_local_tracepoint(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line)
{
- VALUE result = rb_ary_new();
- rb_iseqw_line_trace_each(iseqw, collect_trace, (void *)result);
- return result;
+ unsigned int pc;
+ int n = 0;
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ VALUE *iseq_encoded = (VALUE *)body->iseq_encoded;
+
+ VM_ASSERT(ISEQ_EXECUTABLE_P(iseq));
+
+ for (pc=0; pc<body->iseq_size;) {
+ const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pc);
+ rb_event_flag_t pc_events = entry->events;
+ rb_event_flag_t target_events = turnon_events;
+ unsigned int line = (int)entry->line_no;
+
+ if (target_line == 0 || target_line == line) {
+ /* ok */
+ }
+ else {
+ target_events &= ~RUBY_EVENT_LINE;
+ }
+
+ if (pc_events & target_events) {
+ n++;
+ }
+ pc += encoded_iseq_trace_instrument(&iseq_encoded[pc], pc_events & (target_events | iseq->aux.exec.global_trace_events));
+ }
+
+ if (n > 0) {
+ if (iseq->aux.exec.local_hooks == NULL) {
+ ((rb_iseq_t *)iseq)->aux.exec.local_hooks = RB_ZALLOC(rb_hook_list_t);
+ }
+ rb_hook_list_connect_tracepoint((VALUE)iseq, iseq->aux.exec.local_hooks, tpval, target_line);
+ }
+
+ return n;
}
-struct set_specifc_data {
- int pos;
- int set;
- int prev; /* 1: set, 2: unset, 0: not found */
+struct trace_set_local_events_struct {
+ rb_event_flag_t turnon_events;
+ VALUE tpval;
+ unsigned int target_line;
+ int n;
};
+static void
+iseq_add_local_tracepoint_i(const rb_iseq_t *iseq, void *p)
+{
+ struct trace_set_local_events_struct *data = (struct trace_set_local_events_struct *)p;
+ data->n += iseq_add_local_tracepoint(iseq, data->turnon_events, data->tpval, data->target_line);
+ iseq_iterate_children(iseq, iseq_add_local_tracepoint_i, p);
+}
+
+int
+rb_iseq_add_local_tracepoint_recursively(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line)
+{
+ struct trace_set_local_events_struct data;
+ data.turnon_events = turnon_events;
+ data.tpval = tpval;
+ data.target_line = target_line;
+ data.n = 0;
+
+ iseq_add_local_tracepoint_i(iseq, (void *)&data);
+ if (0) rb_funcall(Qnil, rb_intern("puts"), 1, rb_iseq_disasm(iseq)); /* for debug */
+ return data.n;
+}
+
static int
-line_trace_specify(int line, rb_event_flag_t *events_ptr, void *ptr)
+iseq_remove_local_tracepoint(const rb_iseq_t *iseq, VALUE tpval)
{
- struct set_specifc_data *data = (struct set_specifc_data *)ptr;
+ int n = 0;
- if (data->pos == 0) {
- data->prev = *events_ptr & RUBY_EVENT_SPECIFIED_LINE ? 1 : 2;
- if (data->set) {
- *events_ptr = *events_ptr | RUBY_EVENT_SPECIFIED_LINE;
- }
- else {
- *events_ptr = *events_ptr & ~RUBY_EVENT_SPECIFIED_LINE;
- }
- return 0; /* found */
- }
- else {
- data->pos--;
- return 1;
+ if (iseq->aux.exec.local_hooks) {
+ unsigned int pc;
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ VALUE *iseq_encoded = (VALUE *)body->iseq_encoded;
+ rb_event_flag_t local_events = 0;
+
+ rb_hook_list_remove_tracepoint(iseq->aux.exec.local_hooks, tpval);
+ local_events = iseq->aux.exec.local_hooks->events;
+
+ if (local_events == 0) {
+ if (iseq->aux.exec.local_hooks->running == 0) {
+ rb_hook_list_free(iseq->aux.exec.local_hooks);
+ }
+ ((rb_iseq_t *)iseq)->aux.exec.local_hooks = NULL;
+ }
+
+ 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 & (local_events | iseq->aux.exec.global_trace_events));
+ }
}
+ return n;
}
-/*
- * <b>Experimental MRI specific feature, only available as C level api.</b>
- *
- * Set a +specified_line+ event at the given line position, if the +set+
- * parameter is +true+.
- *
- * This method is useful for building a debugger breakpoint at a specific line.
- *
- * A TypeError is raised if +set+ is not boolean.
- *
- * If +pos+ is a negative integer a TypeError exception is raised.
- */
-VALUE
-rb_iseqw_line_trace_specify(VALUE iseqval, VALUE pos, VALUE set)
+struct trace_clear_local_events_struct {
+ VALUE tpval;
+ int n;
+};
+
+static void
+iseq_remove_local_tracepoint_i(const rb_iseq_t *iseq, void *p)
{
- struct set_specifc_data data;
+ struct trace_clear_local_events_struct *data = (struct trace_clear_local_events_struct *)p;
+ data->n += iseq_remove_local_tracepoint(iseq, data->tpval);
+ iseq_iterate_children(iseq, iseq_remove_local_tracepoint_i, p);
+}
- data.prev = 0;
- data.pos = NUM2INT(pos);
- if (data.pos < 0) rb_raise(rb_eTypeError, "`pos' is negative");
+int
+rb_iseq_remove_local_tracepoint_recursively(const rb_iseq_t *iseq, VALUE tpval)
+{
+ struct trace_clear_local_events_struct data;
+ data.tpval = tpval;
+ data.n = 0;
- switch (set) {
- case Qtrue: data.set = 1; break;
- case Qfalse: data.set = 0; break;
- default:
- rb_raise(rb_eTypeError, "`set' should be true/false");
+ iseq_remove_local_tracepoint_i(iseq, (void *)&data);
+ return data.n;
+}
+
+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;
}
- rb_iseqw_line_trace_each(iseqval, line_trace_specify, (void *)&data);
+ if (!ISEQ_EXECUTABLE_P(iseq)) {
+ /* this is building ISeq */
+ return;
+ }
+ else {
+ unsigned int pc;
+ const struct rb_iseq_constant_body *const body = iseq->body;
+ 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;
+ enabled_events = turnon_events | local_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);
+ }
+ }
+}
+
+static int
+trace_set_i(void *vstart, void *vend, size_t stride, void *data)
+{
+ rb_event_flag_t turnon_events = *(rb_event_flag_t *)data;
- if (data.prev == 0) {
- rb_raise(rb_eTypeError, "`pos' is out of range.");
+ VALUE v = (VALUE)vstart;
+ for (; v != (VALUE)vend; v += stride) {
+ if (rb_obj_is_iseq(v)) {
+ rb_iseq_trace_set(rb_iseq_check((rb_iseq_t *)v), turnon_events);
+ }
}
- return data.prev == 1 ? Qtrue : Qfalse;
+ return 0;
+}
+
+void
+rb_iseq_trace_set_all(rb_event_flag_t turnon_events)
+{
+ rb_objspace_each_objects(trace_set_i, &turnon_events);
+}
+
+/* This is exported since Ruby 2.5 but not internally used for now. If you're going to use this, please
+ update `ruby_vm_event_enabled_global_flags` and set `mjit_call_p = FALSE` as well to cancel MJIT code. */
+void
+rb_iseq_trace_on_all(void)
+{
+ rb_iseq_trace_set_all(RUBY_EVENT_TRACEPOINT_ALL);
}
VALUE
@@ -2372,9 +3202,8 @@ rb_iseqw_local_variables(VALUE iseqval)
static VALUE
iseqw_to_binary(int argc, VALUE *argv, VALUE self)
{
- VALUE opt;
- rb_scan_args(argc, argv, "01", &opt);
- return iseq_ibf_dump(iseqw_check(self), opt);
+ VALUE opt = !rb_check_arity(argc, 0, 1) ? Qnil : argv[0];
+ return rb_iseq_ibf_dump(iseqw_check(self), opt);
}
/*
@@ -2393,7 +3222,7 @@ iseqw_to_binary(int argc, VALUE *argv, VALUE self)
static VALUE
iseqw_s_load_from_binary(VALUE self, VALUE str)
{
- return iseqw_new(iseq_ibf_load(str));
+ return iseqw_new(rb_iseq_ibf_load(str));
}
/*
@@ -2405,9 +3234,141 @@ iseqw_s_load_from_binary(VALUE self, VALUE str)
static VALUE
iseqw_s_load_from_binary_extra_data(VALUE self, VALUE str)
{
- return iseq_ibf_load_extra_data(str);
+ return rb_iseq_ibf_load_extra_data(str);
}
+#if VM_INSN_INFO_TABLE_IMPL == 2
+
+/* An implementation of succinct bit-vector for insn_info table.
+ *
+ * A succinct bit-vector is a small and efficient data structure that provides
+ * a bit-vector augmented with an index for O(1) rank operation:
+ *
+ * rank(bv, n): the number of 1's within a range from index 0 to index n
+ *
+ * This can be used to lookup insn_info table from PC.
+ * For example, consider the following iseq and insn_info_table:
+ *
+ * iseq insn_info_table
+ * PC insn+operand position lineno event
+ * 0: insn1 0: 1 [Li]
+ * 2: insn2 2: 2 [Li] <= (A)
+ * 5: insn3 8: 3 [Li] <= (B)
+ * 8: insn4
+ *
+ * In this case, a succinct bit-vector whose indexes 0, 2, 8 is "1" and
+ * other indexes is "0", i.e., "101000001", is created.
+ * To lookup the lineno of insn2, calculate rank("10100001", 2) = 2, so
+ * the line (A) is the entry in question.
+ * To lookup the lineno of insn4, calculate rank("10100001", 8) = 3, so
+ * the line (B) is the entry in question.
+ *
+ * A naive implementatoin of succinct bit-vector works really well
+ * not only for large size but also for small size. However, it has
+ * tiny overhead for very small size. So, this implementation consist
+ * of two parts: one part is the "immediate" table that keeps rank result
+ * as a raw table, and the other part is a normal succinct bit-vector.
+ */
+
+#define IMMEDIATE_TABLE_SIZE 54 /* a multiple of 9, and < 128 */
+
+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];
+ } succ_part[FLEX_ARY_LEN];
+};
+
+#define imm_block_rank_set(v, i, r) (v) |= (uint64_t)(r) << (7 * (i))
+#define imm_block_rank_get(v, i) (((int)((v) >> ((i) * 7))) & 0x7f)
+#define small_block_rank_set(v, i, r) (v) |= (uint64_t)(r) << (9 * ((i) - 1))
+#define small_block_rank_get(v, i) ((i) == 0 ? 0 : (((int)((v) >> (((i) - 1) * 9))) & 0x1ff))
+
+static struct succ_index_table *
+succ_index_table_create(int max_pos, int *data, int size)
+{
+ const int imm_size = (max_pos < IMMEDIATE_TABLE_SIZE ? max_pos + 8 : IMMEDIATE_TABLE_SIZE) / 9;
+ const int succ_size = (max_pos < IMMEDIATE_TABLE_SIZE ? 0 : (max_pos - IMMEDIATE_TABLE_SIZE + 511)) / 512;
+ struct succ_index_table *sd = ruby_xcalloc(imm_size * sizeof(uint64_t) + succ_size * sizeof(struct succ_dict_block), 1); /* zero cleared */
+ int i, j, k, r;
+
+ 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 (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);
+ }
+ }
+ return sd;
+}
+
+static unsigned int *
+succ_index_table_invert(int max_pos, struct succ_index_table *sd, int size)
+{
+ const int imm_size = (max_pos < IMMEDIATE_TABLE_SIZE ? max_pos + 8 : IMMEDIATE_TABLE_SIZE) / 9;
+ const int succ_size = (max_pos < IMMEDIATE_TABLE_SIZE ? 0 : (max_pos - IMMEDIATE_TABLE_SIZE + 511)) / 512;
+ unsigned int *positions = ruby_xmalloc(sizeof(unsigned int) * size), *p;
+ 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 (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;
+ }
+ }
+ }
+ }
+ return positions;
+}
+
+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);
+ }
+ 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;
+ }
+}
+#endif
+
/*
* Document-class: RubyVM::InstructionSequence
*
@@ -2451,16 +3412,8 @@ Init_ISeq(void)
rb_define_method(rb_cISeq, "label", iseqw_label, 0);
rb_define_method(rb_cISeq, "base_label", iseqw_base_label, 0);
rb_define_method(rb_cISeq, "first_lineno", iseqw_first_lineno, 0);
-
-#if 0
- /* Now, it is experimental. No discussions, no tests. */
- /* They can be used from C level. Please give us feedback. */
- rb_define_method(rb_cISeq, "line_trace_all", rb_iseqw_line_trace_all, 0);
- rb_define_method(rb_cISeq, "line_trace_specify", rb_iseqw_line_trace_specify, 2);
-#else
- (void)rb_iseqw_line_trace_all;
- (void)rb_iseqw_line_trace_specify;
-#endif
+ rb_define_method(rb_cISeq, "trace_points", iseqw_trace_points, 0);
+ rb_define_method(rb_cISeq, "each_child", iseqw_each_child, 0);
#if 0 /* TBD */
rb_define_private_method(rb_cISeq, "marshal_dump", iseqw_marshal_dump, 0);
diff --git a/iseq.h b/iseq.h
index 6a5cb8bdad..f3f269b572 100644
--- a/iseq.h
+++ b/iseq.h
@@ -12,8 +12,9 @@
#ifndef RUBY_ISEQ_H
#define RUBY_ISEQ_H 1
-#define ISEQ_MAJOR_VERSION 2
-#define ISEQ_MINOR_VERSION 3
+RUBY_EXTERN const int ruby_api_version[];
+#define ISEQ_MAJOR_VERSION ((unsigned int)ruby_api_version[0])
+#define ISEQ_MINOR_VERSION ((unsigned int)ruby_api_version[1])
#ifndef rb_iseq_t
typedef struct rb_iseq_struct rb_iseq_t;
@@ -26,55 +27,117 @@ rb_call_info_kw_arg_bytes(int keyword_len)
return sizeof(struct rb_call_info_kw_arg) + sizeof(VALUE) * (keyword_len - 1);
}
-enum iseq_mark_ary_index {
- ISEQ_MARK_ARY_COVERAGE,
- ISEQ_MARK_ARY_FLIP_CNT,
- ISEQ_MARK_ARY_ORIGINAL_ISEQ,
- ISEQ_MARK_ARY_INITIAL_SIZE
-};
-
-static inline VALUE
-iseq_mark_ary_create(int flip_cnt)
-{
- VALUE ary = rb_ary_tmp_new(ISEQ_MARK_ARY_INITIAL_SIZE);
- rb_ary_push(ary, Qnil); /* ISEQ_MARK_ARY_COVERAGE */
- rb_ary_push(ary, INT2FIX(flip_cnt)); /* ISEQ_MARK_ARY_FLIP_CNT */
- rb_ary_push(ary, Qnil); /* ISEQ_MARK_ARY_ORIGINAL_ISEQ */
- return ary;
-}
-
-#define ISEQ_MARK_ARY(iseq) (iseq)->body->mark_ary
+#define ISEQ_COVERAGE(iseq) iseq->body->variable.coverage
+#define ISEQ_COVERAGE_SET(iseq, cov) RB_OBJ_WRITE(iseq, &iseq->body->variable.coverage, cov)
+#define ISEQ_LINE_COVERAGE(iseq) RARRAY_AREF(ISEQ_COVERAGE(iseq), COVERAGE_INDEX_LINES)
+#define ISEQ_BRANCH_COVERAGE(iseq) RARRAY_AREF(ISEQ_COVERAGE(iseq), COVERAGE_INDEX_BRANCHES)
-#define ISEQ_COVERAGE(iseq) RARRAY_AREF(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_COVERAGE)
-#define ISEQ_COVERAGE_SET(iseq, cov) RARRAY_ASET(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_COVERAGE, cov)
+#define ISEQ_PC2BRANCHINDEX(iseq) iseq->body->variable.pc2branchindex
+#define ISEQ_PC2BRANCHINDEX_SET(iseq, h) RB_OBJ_WRITE(iseq, &iseq->body->variable.pc2branchindex, h)
-#define ISEQ_FLIP_CNT(iseq) FIX2INT(RARRAY_AREF(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_FLIP_CNT))
+#define ISEQ_FLIP_CNT(iseq) (iseq)->body->variable.flip_count
-static inline int
+static inline rb_snum_t
ISEQ_FLIP_CNT_INCREMENT(const rb_iseq_t *iseq)
{
- int cnt = ISEQ_FLIP_CNT(iseq);
- RARRAY_ASET(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_FLIP_CNT, INT2FIX(cnt+1));
+ rb_snum_t cnt = iseq->body->variable.flip_count;
+ iseq->body->variable.flip_count += 1;
return cnt;
}
static inline VALUE *
ISEQ_ORIGINAL_ISEQ(const rb_iseq_t *iseq)
{
- VALUE str = RARRAY_AREF(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_ORIGINAL_ISEQ);
- if (RTEST(str)) return (VALUE *)RSTRING_PTR(str);
- return NULL;
+ return iseq->body->variable.original_iseq;
+}
+
+static inline void
+ISEQ_ORIGINAL_ISEQ_CLEAR(const rb_iseq_t *iseq)
+{
+ void *ptr = iseq->body->variable.original_iseq;
+ iseq->body->variable.original_iseq = NULL;
+ if (ptr) {
+ ruby_xfree(ptr);
+ }
}
static inline VALUE *
ISEQ_ORIGINAL_ISEQ_ALLOC(const rb_iseq_t *iseq, long size)
{
- VALUE str = rb_str_tmp_new(size * sizeof(VALUE));
- RARRAY_ASET(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_ORIGINAL_ISEQ, str);
- return (VALUE *)RSTRING_PTR(str);
+ return iseq->body->variable.original_iseq =
+ ruby_xmalloc2(size, sizeof(VALUE));
+}
+
+#define ISEQ_TRACE_EVENTS (RUBY_EVENT_LINE | \
+ RUBY_EVENT_CLASS | \
+ RUBY_EVENT_END | \
+ RUBY_EVENT_CALL | \
+ RUBY_EVENT_RETURN| \
+ RUBY_EVENT_B_CALL| \
+ RUBY_EVENT_B_RETURN| \
+ RUBY_EVENT_COVERAGE_LINE| \
+ RUBY_EVENT_COVERAGE_BRANCH)
+
+#define ISEQ_NOT_LOADED_YET IMEMO_FL_USER1
+#define ISEQ_USE_COMPILE_DATA IMEMO_FL_USER2
+#define ISEQ_TRANSLATED IMEMO_FL_USER3
+#define ISEQ_MARKABLE_ISEQ IMEMO_FL_USER4
+
+#define ISEQ_EXECUTABLE_P(iseq) (FL_TEST_RAW((iseq), ISEQ_NOT_LOADED_YET | ISEQ_USE_COMPILE_DATA) == 0)
+
+struct iseq_compile_data {
+ /* GC is needed */
+ const VALUE err_info;
+ VALUE mark_ary;
+ const VALUE catch_table_ary; /* Array */
+
+ /* GC is not needed */
+ struct iseq_label_data *start_label;
+ struct iseq_label_data *end_label;
+ struct iseq_label_data *redo_label;
+ const rb_iseq_t *current_block;
+ VALUE ensure_node;
+ VALUE for_iseq;
+ struct iseq_compile_data_ensure_node_stack *ensure_node_stack;
+ struct iseq_compile_data_storage *storage_head;
+ struct iseq_compile_data_storage *storage_current;
+ int loopval_popped; /* used by NODE_BREAK */
+ int last_line;
+ int label_no;
+ int node_level;
+ unsigned int ci_index;
+ unsigned int ci_kw_index;
+ const rb_compile_option_t *option;
+ struct rb_id_table *ivar_cache_table;
+#if SUPPORT_JOKE
+ st_table *labels_table;
+#endif
+};
+
+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;
+ }
+ else {
+ return NULL;
+ }
}
-#define ISEQ_COMPILE_DATA(iseq) (iseq)->aux.compile_data
+static inline void
+ISEQ_COMPILE_DATA_ALLOC(rb_iseq_t *iseq)
+{
+ iseq->aux.compile_data = ZALLOC(struct iseq_compile_data);
+ iseq->flags |= ISEQ_USE_COMPILE_DATA;
+}
+
+static inline void
+ISEQ_COMPILE_DATA_CLEAR(rb_iseq_t *iseq)
+{
+ iseq->flags &= ~ISEQ_USE_COMPILE_DATA;
+ iseq->aux.compile_data = NULL;
+}
static inline rb_iseq_t *
iseq_imemo_alloc(void)
@@ -82,17 +145,24 @@ iseq_imemo_alloc(void)
return (rb_iseq_t *)rb_imemo_new(imemo_iseq, 0, 0, 0, 0);
}
-#define ISEQ_NOT_LOADED_YET IMEMO_FL_USER1
-
-VALUE iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt);
-void ibf_load_iseq_complete(rb_iseq_t *iseq);
-const rb_iseq_t *iseq_ibf_load(VALUE str);
-VALUE iseq_ibf_load_extra_data(VALUE str);
+VALUE rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt);
+void rb_ibf_load_iseq_complete(rb_iseq_t *iseq);
+const rb_iseq_t *rb_iseq_ibf_load(VALUE str);
+VALUE rb_iseq_ibf_load_extra_data(VALUE str);
+void rb_iseq_init_trace(rb_iseq_t *iseq);
+int rb_iseq_add_local_tracepoint_recursively(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line);
+int rb_iseq_remove_local_tracepoint_recursively(const rb_iseq_t *iseq, VALUE tpval);
+const rb_iseq_t *rb_iseq_load_iseq(VALUE fname);
+
+#if VM_INSN_INFO_TABLE_IMPL == 2
+unsigned int *rb_iseq_insns_info_decode_positions(const struct rb_iseq_constant_body *body);
+#endif
RUBY_SYMBOL_EXPORT_BEGIN
/* compile.c */
-VALUE rb_iseq_compile_node(rb_iseq_t *iseq, NODE *node);
+VALUE rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node);
+VALUE rb_iseq_compile_ifunc(rb_iseq_t *iseq, const struct vm_ifunc *ifunc);
int rb_iseq_translate_threaded_code(rb_iseq_t *iseq);
VALUE *rb_iseq_original_iseq(const rb_iseq_t *iseq);
void rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc,
@@ -100,24 +170,26 @@ void rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc,
VALUE exception, VALUE body);
/* iseq.c */
-void rb_iseq_add_mark_object(const rb_iseq_t *iseq, VALUE obj);
VALUE rb_iseq_load(VALUE data, VALUE parent, VALUE opt);
VALUE rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc);
struct st_table *ruby_insn_make_insn_table(void);
unsigned int rb_iseq_line_no(const rb_iseq_t *iseq, size_t pos);
+void rb_iseq_trace_set(const rb_iseq_t *iseq, rb_event_flag_t turnon_events);
+void rb_iseq_trace_set_all(rb_event_flag_t turnon_events);
+void rb_iseq_trace_on_all(void);
+void rb_iseq_insns_info_encode_positions(const rb_iseq_t *iseq);
-int rb_iseqw_line_trace_each(VALUE iseqval, int (*func)(int line, rb_event_flag_t *events_ptr, void *d), void *data);
-VALUE rb_iseqw_line_trace_all(VALUE iseqval);
-VALUE rb_iseqw_line_trace_specify(VALUE iseqval, VALUE pos, VALUE set);
VALUE rb_iseqw_new(const rb_iseq_t *iseq);
const rb_iseq_t *rb_iseqw_to_iseq(VALUE iseqw);
-VALUE rb_iseq_path(const rb_iseq_t *iseq);
-VALUE rb_iseq_absolute_path(const rb_iseq_t *iseq);
+VALUE rb_iseq_absolute_path(const rb_iseq_t *iseq); /* obsolete */
VALUE rb_iseq_label(const rb_iseq_t *iseq);
VALUE rb_iseq_base_label(const rb_iseq_t *iseq);
VALUE rb_iseq_first_lineno(const rb_iseq_t *iseq);
VALUE rb_iseq_method_name(const rb_iseq_t *iseq);
+void rb_iseq_code_location(const rb_iseq_t *iseq, int *first_lineno, int *first_column, int *last_lineno, int *last_column);
+
+void rb_iseq_remove_coverage_all(void);
/* proc.c */
const rb_iseq_t *rb_method_iseq(VALUE body);
@@ -131,16 +203,15 @@ struct rb_compile_option_struct {
unsigned int operands_unification: 1;
unsigned int instructions_unification: 1;
unsigned int stack_caching: 1;
- unsigned int trace_instruction: 1;
unsigned int frozen_string_literal: 1;
unsigned int debug_frozen_string_literal: 1;
unsigned int coverage_enabled: 1;
int debug_level;
};
-struct iseq_line_info_entry {
- unsigned int position;
- unsigned int line_no;
+struct iseq_insn_info_entry {
+ int line_no;
+ rb_event_flag_t events;
};
struct iseq_catch_table_entry {
@@ -152,7 +223,21 @@ struct iseq_catch_table_entry {
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.
+ */
const rb_iseq_t *iseq;
+
unsigned int start;
unsigned int end;
unsigned int cont;
@@ -161,18 +246,19 @@ struct iseq_catch_table_entry {
PACKED_STRUCT_UNALIGNED(struct iseq_catch_table {
unsigned int size;
- struct iseq_catch_table_entry entries[1]; /* flexible array */
+ struct iseq_catch_table_entry entries[FLEX_ARY_LEN];
});
static inline int
iseq_catch_table_bytes(int n)
{
enum {
- catch_table_entries_max = (INT_MAX - sizeof(struct iseq_catch_table)) / sizeof(struct iseq_catch_table_entry)
+ 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)(sizeof(struct iseq_catch_table) +
- (n - 1) * sizeof(struct iseq_catch_table_entry));
+ return (int)(offsetof(struct iseq_catch_table, entries) +
+ n * catch_table_entry_size);
}
#define INITIAL_ISEQ_COMPILE_DATA_STORAGE_BUFF_SIZE (512)
@@ -181,47 +267,13 @@ struct iseq_compile_data_storage {
struct iseq_compile_data_storage *next;
unsigned int pos;
unsigned int size;
- char buff[1]; /* flexible array */
-};
-
-/* account for flexible array */
-#define SIZEOF_ISEQ_COMPILE_DATA_STORAGE \
- (sizeof(struct iseq_compile_data_storage) - 1)
-
-struct iseq_compile_data {
- /* GC is needed */
- const VALUE err_info;
- VALUE mark_ary;
- const VALUE catch_table_ary; /* Array */
-
- /* GC is not needed */
- struct iseq_label_data *start_label;
- struct iseq_label_data *end_label;
- struct iseq_label_data *redo_label;
- const rb_iseq_t *current_block;
- VALUE ensure_node;
- VALUE for_iseq;
- struct iseq_compile_data_ensure_node_stack *ensure_node_stack;
- int loopval_popped; /* used by NODE_BREAK */
- int cached_const;
- struct iseq_compile_data_storage *storage_head;
- struct iseq_compile_data_storage *storage_current;
- int last_line;
- int last_coverable_line;
- int label_no;
- int node_level;
- unsigned int ci_index;
- unsigned int ci_kw_index;
- const rb_compile_option_t *option;
- struct rb_id_table *ivar_cache_table;
-#if SUPPORT_JOKE
- st_table *labels_table;
-#endif
+ char buff[FLEX_ARY_LEN];
};
/* defined? */
enum defined_type {
+ DEFINED_NOT_DEFINED,
DEFINED_NIL = 1,
DEFINED_IVAR,
DEFINED_LVAR,
diff --git a/lex.c.blt b/lex.c.blt
index 7d8dd5b6e7..92a4793b00 100644
--- a/lex.c.blt
+++ b/lex.c.blt
@@ -1,4 +1,4 @@
-/* C code produced by gperf version 3.0.4 */
+/* ANSI-C code produced by gperf version 3.1 */
/* Command-line: gperf -C -P -p -j1 -i 1 -g -o -t -N rb_reserved_word -k'1,3,$' defs/keywords */
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
@@ -25,15 +25,16 @@
&& ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
&& ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
/* The character set is not based on ISO-646. */
-error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#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 "defs/keywords"
-struct kwtable {int name, id[2], state;};
+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(/*const char *, unsigned int*/);
#define rb_reserved_word(str, len) reserved_word(str, len)
#line 9 "defs/keywords"
struct kwtable;
@@ -45,15 +46,15 @@ struct kwtable;
#define MAX_HASH_VALUE 50
/* maximum key range = 43, duplicates = 0 */
-#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) || defined(__GNUC_STDC_INLINE__)
-inline
-#elif defined(__GNUC__)
+#ifdef __GNUC__
__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
#endif
static unsigned int
-hash (str, len)
- register const char *str;
- register unsigned int len;
+hash (register const char *str, register size_t len)
{
static const unsigned char asso_values[] =
{
@@ -84,7 +85,7 @@ hash (str, len)
51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
51, 51, 51, 51, 51, 51
};
- register int hval = len;
+ register unsigned int hval = (unsigned int)len;
switch (hval)
{
@@ -96,7 +97,7 @@ hash (str, len)
hval += asso_values[(unsigned char)str[0]];
break;
}
- return hval + asso_values[(unsigned char)str[len - 1]];
+ return (unsigned int)hval + asso_values[(unsigned char)str[len - 1]];
}
struct stringpool_t
@@ -188,110 +189,102 @@ static const struct stringpool_t stringpool_contents =
"while"
};
#define stringpool ((const char *) &stringpool_contents)
-#ifdef __GNUC__
-__inline
-#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
-__attribute__ ((__gnu_inline__))
-#endif
-#endif
const struct kwtable *
-rb_reserved_word (str, len)
- register const char *str;
- register unsigned int len;
+rb_reserved_word (register const char *str, register size_t len)
{
static const struct kwtable wordlist[] =
{
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#line 19 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str8), {keyword_break, keyword_break}, EXPR_MID},
+ {gperf_offsetof(stringpool, 8), {keyword_break, keyword_break}, EXPR_MID},
#line 25 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str9), {keyword_else, keyword_else}, EXPR_BEG},
+ {gperf_offsetof(stringpool, 9), {keyword_else, keyword_else}, EXPR_BEG},
#line 35 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str10), {keyword_nil, keyword_nil}, EXPR_END},
+ {gperf_offsetof(stringpool, 10), {keyword_nil, keyword_nil}, EXPR_END},
#line 28 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str11), {keyword_ensure, keyword_ensure}, EXPR_BEG},
+ {gperf_offsetof(stringpool, 11), {keyword_ensure, keyword_ensure}, EXPR_BEG},
#line 27 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str12), {keyword_end, keyword_end}, EXPR_END},
+ {gperf_offsetof(stringpool, 12), {keyword_end, keyword_end}, EXPR_END},
#line 44 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str13), {keyword_then, keyword_then}, EXPR_BEG},
+ {gperf_offsetof(stringpool, 13), {keyword_then, keyword_then}, EXPR_BEG},
#line 36 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str14), {keyword_not, keyword_not}, EXPR_ARG},
+ {gperf_offsetof(stringpool, 14), {keyword_not, keyword_not}, EXPR_ARG},
#line 29 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str15), {keyword_false, keyword_false}, EXPR_END},
+ {gperf_offsetof(stringpool, 15), {keyword_false, keyword_false}, EXPR_END},
#line 42 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str16), {keyword_self, keyword_self}, EXPR_END},
+ {gperf_offsetof(stringpool, 16), {keyword_self, keyword_self}, EXPR_END},
#line 26 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str17), {keyword_elsif, keyword_elsif}, EXPR_VALUE},
+ {gperf_offsetof(stringpool, 17), {keyword_elsif, keyword_elsif}, EXPR_VALUE},
#line 39 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str18), {keyword_rescue, modifier_rescue}, EXPR_MID},
+ {gperf_offsetof(stringpool, 18), {keyword_rescue, modifier_rescue}, EXPR_MID},
#line 45 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str19), {keyword_true, keyword_true}, EXPR_END},
+ {gperf_offsetof(stringpool, 19), {keyword_true, keyword_true}, EXPR_END},
#line 48 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str20), {keyword_until, modifier_until}, EXPR_VALUE},
+ {gperf_offsetof(stringpool, 20), {keyword_until, modifier_until}, EXPR_VALUE},
#line 47 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str21), {keyword_unless, modifier_unless}, EXPR_VALUE},
+ {gperf_offsetof(stringpool, 21), {keyword_unless, modifier_unless}, EXPR_VALUE},
#line 41 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str22), {keyword_return, keyword_return}, EXPR_MID},
+ {gperf_offsetof(stringpool, 22), {keyword_return, keyword_return}, EXPR_MID},
#line 22 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str23), {keyword_def, keyword_def}, EXPR_FNAME},
+ {gperf_offsetof(stringpool, 23), {keyword_def, keyword_def}, EXPR_FNAME},
#line 17 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str24), {keyword_and, keyword_and}, EXPR_VALUE},
+ {gperf_offsetof(stringpool, 24), {keyword_and, keyword_and}, EXPR_VALUE},
#line 24 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str25), {keyword_do, keyword_do}, EXPR_BEG},
+ {gperf_offsetof(stringpool, 25), {keyword_do, keyword_do}, EXPR_BEG},
#line 51 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str26), {keyword_yield, keyword_yield}, EXPR_ARG},
+ {gperf_offsetof(stringpool, 26), {keyword_yield, keyword_yield}, EXPR_ARG},
#line 30 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str27), {keyword_for, keyword_for}, EXPR_VALUE},
+ {gperf_offsetof(stringpool, 27), {keyword_for, keyword_for}, EXPR_VALUE},
#line 46 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str28), {keyword_undef, keyword_undef}, EXPR_FNAME|EXPR_FITEM},
+ {gperf_offsetof(stringpool, 28), {keyword_undef, keyword_undef}, EXPR_FNAME|EXPR_FITEM},
#line 37 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str29), {keyword_or, keyword_or}, EXPR_VALUE},
+ {gperf_offsetof(stringpool, 29), {keyword_or, keyword_or}, EXPR_VALUE},
#line 32 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str30), {keyword_in, keyword_in}, EXPR_VALUE},
+ {gperf_offsetof(stringpool, 30), {keyword_in, keyword_in}, EXPR_VALUE},
#line 49 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str31), {keyword_when, keyword_when}, EXPR_VALUE},
+ {gperf_offsetof(stringpool, 31), {keyword_when, keyword_when}, EXPR_VALUE},
#line 40 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str32), {keyword_retry, keyword_retry}, EXPR_END},
+ {gperf_offsetof(stringpool, 32), {keyword_retry, keyword_retry}, EXPR_END},
#line 31 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str33), {keyword_if, modifier_if}, EXPR_VALUE},
+ {gperf_offsetof(stringpool, 33), {keyword_if, modifier_if}, EXPR_VALUE},
#line 20 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str34), {keyword_case, keyword_case}, EXPR_VALUE},
+ {gperf_offsetof(stringpool, 34), {keyword_case, keyword_case}, EXPR_VALUE},
#line 38 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str35), {keyword_redo, keyword_redo}, EXPR_END},
+ {gperf_offsetof(stringpool, 35), {keyword_redo, keyword_redo}, EXPR_END},
#line 34 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str36), {keyword_next, keyword_next}, EXPR_MID},
+ {gperf_offsetof(stringpool, 36), {keyword_next, keyword_next}, EXPR_MID},
#line 43 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str37), {keyword_super, keyword_super}, EXPR_ARG},
+ {gperf_offsetof(stringpool, 37), {keyword_super, keyword_super}, EXPR_ARG},
#line 33 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str38), {keyword_module, keyword_module}, EXPR_VALUE},
+ {gperf_offsetof(stringpool, 38), {keyword_module, keyword_module}, EXPR_VALUE},
#line 18 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str39), {keyword_begin, keyword_begin}, EXPR_BEG},
+ {gperf_offsetof(stringpool, 39), {keyword_begin, keyword_begin}, EXPR_BEG},
#line 12 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str40), {keyword__LINE__, keyword__LINE__}, EXPR_END},
+ {gperf_offsetof(stringpool, 40), {keyword__LINE__, keyword__LINE__}, EXPR_END},
#line 13 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str41), {keyword__FILE__, keyword__FILE__}, EXPR_END},
+ {gperf_offsetof(stringpool, 41), {keyword__FILE__, keyword__FILE__}, EXPR_END},
#line 11 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str42), {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END},
+ {gperf_offsetof(stringpool, 42), {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END},
#line 15 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str43), {keyword_END, keyword_END}, EXPR_END},
+ {gperf_offsetof(stringpool, 43), {keyword_END, keyword_END}, EXPR_END},
#line 16 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str44), {keyword_alias, keyword_alias}, EXPR_FNAME|EXPR_FITEM},
+ {gperf_offsetof(stringpool, 44), {keyword_alias, keyword_alias}, EXPR_FNAME|EXPR_FITEM},
#line 14 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str45), {keyword_BEGIN, keyword_BEGIN}, EXPR_END},
+ {gperf_offsetof(stringpool, 45), {keyword_BEGIN, keyword_BEGIN}, EXPR_END},
#line 23 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str46), {keyword_defined, keyword_defined}, EXPR_ARG},
+ {gperf_offsetof(stringpool, 46), {keyword_defined, keyword_defined}, EXPR_ARG},
#line 21 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str47), {keyword_class, keyword_class}, EXPR_CLASS},
+ {gperf_offsetof(stringpool, 47), {keyword_class, keyword_class}, EXPR_CLASS},
{-1}, {-1},
#line 50 "defs/keywords"
- {(int)offsetof(struct stringpool_t, stringpool_str50), {keyword_while, modifier_while}, EXPR_VALUE}
+ {gperf_offsetof(stringpool, 50), {keyword_while, modifier_while}, EXPR_VALUE}
};
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
{
- register int key = hash (str, len);
+ register unsigned int key = hash (str, len);
- if (key <= MAX_HASH_VALUE && key >= 0)
+ if (key <= MAX_HASH_VALUE)
{
register int o = wordlist[key].name;
if (o >= 0)
diff --git a/lib/.document b/lib/.document
new file mode 100644
index 0000000000..668152021d
--- /dev/null
+++ b/lib/.document
@@ -0,0 +1,25 @@
+# Add files to this as they become documented
+
+*.rb
+
+bundler
+cgi
+csv
+drb
+forwardable
+irb
+matrix
+net
+optparse
+racc
+rdoc
+rexml
+rinda
+rss
+rubygems
+scanf.rb
+shell
+unicode_normalize
+uri
+webrick
+yaml
diff --git a/lib/English.rb b/lib/English.rb
index 0c17229682..a4f5bb6620 100644
--- a/lib/English.rb
+++ b/lib/English.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# Include the English library file in a Ruby script, and you can
# reference the global variables such as \VAR{\$\_} using less
# cryptic names, listed in the following table.% \vref{tab:english}.
@@ -153,9 +153,7 @@ alias $CHILD_STATUS $?
# scope.
alias $LAST_MATCH_INFO $~
-# If set to any value apart from +nil+ or +false+, all pattern matches
-# will be case insensitive, string comparisons will ignore case, and
-# string hash values will be case insensitive. Deprecated
+# This variable is no longer effective. Deprecated.
alias $IGNORECASE $=
# An array of strings containing the command-line
diff --git a/lib/abbrev.rb b/lib/abbrev.rb
index 2eac293c30..2af01d2eae 100644
--- a/lib/abbrev.rb
+++ b/lib/abbrev.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
# Copyright (c) 2001,2003 Akinori MUSHA <knu@iDaemons.org>
#
diff --git a/lib/base64.rb b/lib/base64.rb
index a08941af92..24f0b02966 100644
--- a/lib/base64.rb
+++ b/lib/base64.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# = base64.rb: methods for base64-encoding and -decoding strings
#
@@ -56,7 +56,7 @@ module Base64
# This is line three
# And so on...
def decode64(str)
- str.unpack("m").first
+ str.unpack1("m")
end
# Returns the Base64-encoded version of +bin+.
@@ -71,7 +71,7 @@ module Base64
# ArgumentError is raised if +str+ is incorrectly padded or contains
# non-alphabet characters. Note that CR or LF are also rejected.
def strict_decode64(str)
- str.unpack("m0").first
+ str.unpack1("m0")
end
# Returns the Base64-encoded version of +bin+.
diff --git a/lib/benchmark.rb b/lib/benchmark.rb
index ec1b658cde..5ce9710586 100644
--- a/lib/benchmark.rb
+++ b/lib/benchmark.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
# benchmark.rb - a performance benchmarking library
#
@@ -439,6 +439,9 @@ module Benchmark
#
# An in-place version of #add.
+ # Changes the times of this Tms object by making it the sum of the times
+ # for this Tms object, plus the time required to execute
+ # the code block (+blk+).
#
def add!(&blk)
t = Benchmark.measure(&blk)
@@ -452,7 +455,7 @@ module Benchmark
#
# Returns a new Tms object obtained by memberwise summation
- # of the individual times for this Tms object with those of the other
+ # of the individual times for this Tms object with those of the +other+
# Tms object.
# This method and #/() are useful for taking statistics.
#
@@ -460,20 +463,20 @@ module Benchmark
#
# Returns a new Tms object obtained by memberwise subtraction
- # of the individual times for the other Tms object from those of this
+ # of the individual times for the +other+ Tms object from those of this
# Tms object.
#
def -(other); memberwise(:-, other) end
#
# Returns a new Tms object obtained by memberwise multiplication
- # of the individual times for this Tms object by _x_.
+ # of the individual times for this Tms object by +x+.
#
def *(x); memberwise(:*, x) end
#
# Returns a new Tms object obtained by memberwise division
- # of the individual times for this Tms object by _x_.
+ # of the individual times for this Tms object by +x+.
# This method and #+() are useful for taking statistics.
#
def /(x); memberwise(:/, x) end
@@ -529,7 +532,7 @@ module Benchmark
#
# Returns a new Tms object obtained by memberwise operation +op+
# of the individual times for this Tms object with those of the other
- # Tms object.
+ # Tms object (+x+).
#
# +op+ can be a mathematical operation such as <tt>+</tt>, <tt>-</tt>,
# <tt>*</tt>, <tt>/</tt>
diff --git a/lib/bundler.rb b/lib/bundler.rb
new file mode 100644
index 0000000000..1cb3b4fb21
--- /dev/null
+++ b/lib/bundler.rb
@@ -0,0 +1,567 @@
+# frozen_string_literal: true
+
+require "bundler/compatibility_guard"
+
+require "bundler/vendored_fileutils"
+require "pathname"
+require "rbconfig"
+require "thread"
+
+require "bundler/errors"
+require "bundler/environment_preserver"
+require "bundler/plugin"
+require "bundler/rubygems_ext"
+require "bundler/rubygems_integration"
+require "bundler/version"
+require "bundler/constants"
+require "bundler/current_ruby"
+require "bundler/build_metadata"
+
+module Bundler
+ environment_preserver = EnvironmentPreserver.new(ENV, EnvironmentPreserver::BUNDLER_KEYS)
+ ORIGINAL_ENV = environment_preserver.restore
+ ENV.replace(environment_preserver.backup)
+ SUDO_MUTEX = Mutex.new
+
+ autoload :Definition, "bundler/definition"
+ autoload :Dependency, "bundler/dependency"
+ autoload :DepProxy, "bundler/dep_proxy"
+ autoload :Deprecate, "bundler/deprecate"
+ autoload :Dsl, "bundler/dsl"
+ autoload :EndpointSpecification, "bundler/endpoint_specification"
+ autoload :Env, "bundler/env"
+ autoload :Fetcher, "bundler/fetcher"
+ autoload :FeatureFlag, "bundler/feature_flag"
+ autoload :GemHelper, "bundler/gem_helper"
+ autoload :GemHelpers, "bundler/gem_helpers"
+ autoload :GemRemoteFetcher, "bundler/gem_remote_fetcher"
+ autoload :GemVersionPromoter, "bundler/gem_version_promoter"
+ autoload :Graph, "bundler/graph"
+ autoload :Index, "bundler/index"
+ autoload :Injector, "bundler/injector"
+ autoload :Installer, "bundler/installer"
+ autoload :LazySpecification, "bundler/lazy_specification"
+ autoload :LockfileParser, "bundler/lockfile_parser"
+ autoload :MatchPlatform, "bundler/match_platform"
+ autoload :ProcessLock, "bundler/process_lock"
+ autoload :RemoteSpecification, "bundler/remote_specification"
+ autoload :Resolver, "bundler/resolver"
+ autoload :Retry, "bundler/retry"
+ autoload :RubyDsl, "bundler/ruby_dsl"
+ autoload :RubyGemsGemInstaller, "bundler/rubygems_gem_installer"
+ autoload :RubyVersion, "bundler/ruby_version"
+ autoload :Runtime, "bundler/runtime"
+ autoload :Settings, "bundler/settings"
+ autoload :SharedHelpers, "bundler/shared_helpers"
+ autoload :Source, "bundler/source"
+ autoload :SourceList, "bundler/source_list"
+ autoload :SpecSet, "bundler/spec_set"
+ autoload :StubSpecification, "bundler/stub_specification"
+ autoload :UI, "bundler/ui"
+ autoload :URICredentialsFilter, "bundler/uri_credentials_filter"
+ autoload :VersionRanges, "bundler/version_ranges"
+
+ class << self
+ def configure
+ @configured ||= configure_gem_home_and_path
+ end
+
+ def ui
+ (defined?(@ui) && @ui) || (self.ui = UI::Silent.new)
+ end
+
+ def ui=(ui)
+ Bundler.rubygems.ui = ui ? UI::RGProxy.new(ui) : nil
+ @ui = ui
+ end
+
+ # Returns absolute path of where gems are installed on the filesystem.
+ def bundle_path
+ @bundle_path ||= Pathname.new(configured_bundle_path.path).expand_path(root)
+ end
+
+ def configured_bundle_path
+ @configured_bundle_path ||= settings.path.tap(&:validate!)
+ end
+
+ # Returns absolute location of where binstubs are installed to.
+ def bin_path
+ @bin_path ||= begin
+ path = settings[:bin] || "bin"
+ path = Pathname.new(path).expand_path(root).expand_path
+ SharedHelpers.filesystem_access(path) {|p| FileUtils.mkdir_p(p) }
+ path
+ end
+ end
+
+ def setup(*groups)
+ # Return if all groups are already loaded
+ return @setup if defined?(@setup) && @setup
+
+ definition.validate_runtime!
+
+ SharedHelpers.print_major_deprecations!
+
+ if groups.empty?
+ # Load all groups, but only once
+ @setup = load.setup
+ else
+ load.setup(*groups)
+ end
+ end
+
+ def require(*groups)
+ setup(*groups).require(*groups)
+ end
+
+ def load
+ @load ||= Runtime.new(root, definition)
+ end
+
+ def environment
+ SharedHelpers.major_deprecation 2, "Bundler.environment has been removed in favor of Bundler.load"
+ load
+ end
+
+ # Returns an instance of Bundler::Definition for given Gemfile and lockfile
+ #
+ # @param unlock [Hash, Boolean, nil] Gems that have been requested
+ # to be updated or true if all gems should be updated
+ # @return [Bundler::Definition]
+ def definition(unlock = nil)
+ @definition = nil if unlock
+ @definition ||= begin
+ configure
+ Definition.build(default_gemfile, default_lockfile, unlock)
+ end
+ end
+
+ def frozen_bundle?
+ frozen = settings[:deployment]
+ frozen ||= settings[:frozen] unless feature_flag.deployment_means_frozen?
+ frozen
+ end
+
+ def locked_gems
+ @locked_gems ||=
+ if defined?(@definition) && @definition
+ definition.locked_gems
+ elsif Bundler.default_lockfile.file?
+ lock = Bundler.read_file(Bundler.default_lockfile)
+ LockfileParser.new(lock)
+ end
+ end
+
+ def ruby_scope
+ "#{Bundler.rubygems.ruby_engine}/#{Bundler.rubygems.config_map[:ruby_version]}"
+ end
+
+ def user_home
+ @user_home ||= begin
+ home = Bundler.rubygems.user_home
+ bundle_home = home ? File.join(home, ".bundle") : nil
+
+ warning = if home.nil?
+ "Your home directory is not set."
+ elsif !File.directory?(home)
+ "`#{home}` is not a directory."
+ elsif !File.writable?(home) && (!File.directory?(bundle_home) || !File.writable?(bundle_home))
+ "`#{home}` is not writable."
+ end
+
+ if warning
+ Kernel.send(:require, "etc")
+ user_home = tmp_home_path(Etc.getlogin, warning)
+ Bundler.ui.warn "#{warning}\nBundler will use `#{user_home}' as your home directory temporarily.\n"
+ user_home
+ else
+ Pathname.new(home)
+ end
+ end
+ end
+
+ def tmp_home_path(login, warning)
+ login ||= "unknown"
+ Kernel.send(:require, "tmpdir")
+ path = Pathname.new(Dir.tmpdir).join("bundler", "home")
+ SharedHelpers.filesystem_access(path) do |tmp_home_path|
+ unless tmp_home_path.exist?
+ tmp_home_path.mkpath
+ tmp_home_path.chmod(0o777)
+ end
+ tmp_home_path.join(login).tap(&:mkpath)
+ end
+ rescue RuntimeError => e
+ raise e.exception("#{warning}\nBundler also failed to create a temporary home directory at `#{path}':\n#{e}")
+ end
+
+ def user_bundle_path(dir = "home")
+ env_var, fallback = case dir
+ when "home"
+ ["BUNDLE_USER_HOME", Pathname.new(user_home).join(".bundle")]
+ when "cache"
+ ["BUNDLE_USER_CACHE", user_bundle_path.join("cache")]
+ when "config"
+ ["BUNDLE_USER_CONFIG", user_bundle_path.join("config")]
+ when "plugin"
+ ["BUNDLE_USER_PLUGIN", user_bundle_path.join("plugin")]
+ else
+ raise BundlerError, "Unknown user path requested: #{dir}"
+ end
+ # `fallback` will already be a Pathname, but Pathname.new() is
+ # idempotent so it's OK
+ Pathname.new(ENV.fetch(env_var, fallback))
+ end
+
+ def user_cache
+ user_bundle_path("cache")
+ end
+
+ def home
+ bundle_path.join("bundler")
+ end
+
+ def install_path
+ home.join("gems")
+ end
+
+ def specs_path
+ bundle_path.join("specifications")
+ end
+
+ def root
+ @root ||= begin
+ SharedHelpers.root
+ rescue GemfileNotFound
+ bundle_dir = default_bundle_dir
+ raise GemfileNotFound, "Could not locate Gemfile or .bundle/ directory" unless bundle_dir
+ Pathname.new(File.expand_path("..", bundle_dir))
+ end
+ end
+
+ def app_config_path
+ if app_config = ENV["BUNDLE_APP_CONFIG"]
+ Pathname.new(app_config).expand_path(root)
+ else
+ root.join(".bundle")
+ end
+ end
+
+ def app_cache(custom_path = nil)
+ path = custom_path || root
+ Pathname.new(path).join(settings.app_cache_path)
+ end
+
+ def tmp(name = Process.pid.to_s)
+ Kernel.send(:require, "tmpdir")
+ Pathname.new(Dir.mktmpdir(["bundler", name]))
+ end
+
+ def rm_rf(path)
+ 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.
+You should probably consider fixing this issue by running `chmod o-w ~` on *nix.
+Please refer to http://ruby-doc.org/stdlib-2.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"
+ end
+
+ def settings
+ @settings ||= Settings.new(app_config_path)
+ rescue GemfileNotFound
+ @settings = Settings.new(Pathname.new(".bundle").expand_path)
+ end
+
+ # @return [Hash] Environment present before Bundler was activated
+ def original_env
+ ORIGINAL_ENV.clone
+ end
+
+ # @deprecated Use `original_env` instead
+ # @return [Hash] Environment with all bundler-related variables removed
+ def clean_env
+ Bundler::SharedHelpers.major_deprecation(2, "`Bundler.clean_env` has weird edge cases, use `.original_env` instead")
+ env = original_env
+
+ if env.key?("BUNDLER_ORIG_MANPATH")
+ env["MANPATH"] = env["BUNDLER_ORIG_MANPATH"]
+ end
+
+ env.delete_if {|k, _| k[0, 7] == "BUNDLE_" }
+
+ if env.key?("RUBYOPT")
+ env["RUBYOPT"] = env["RUBYOPT"].sub "-rbundler/setup", ""
+ end
+
+ if env.key?("RUBYLIB")
+ rubylib = env["RUBYLIB"].split(File::PATH_SEPARATOR)
+ rubylib.delete(File.expand_path("..", __FILE__))
+ env["RUBYLIB"] = rubylib.join(File::PATH_SEPARATOR)
+ end
+
+ env
+ end
+
+ def with_original_env
+ with_env(original_env) { yield }
+ end
+
+ def with_clean_env
+ with_env(clean_env) { yield }
+ end
+
+ def clean_system(*args)
+ with_clean_env { Kernel.system(*args) }
+ end
+
+ def clean_exec(*args)
+ with_clean_env { Kernel.exec(*args) }
+ end
+
+ def local_platform
+ return Gem::Platform::RUBY if settings[:force_ruby_platform]
+ Gem::Platform.local
+ end
+
+ def default_gemfile
+ SharedHelpers.default_gemfile
+ end
+
+ def default_lockfile
+ SharedHelpers.default_lockfile
+ end
+
+ def default_bundle_dir
+ SharedHelpers.default_bundle_dir
+ end
+
+ def system_bindir
+ # Gem.bindir doesn't always return the location that RubyGems will install
+ # system binaries. If you put '-n foo' in your .gemrc, RubyGems will
+ # install binstubs there instead. Unfortunately, RubyGems doesn't expose
+ # that directory at all, so rather than parse .gemrc ourselves, we allow
+ # the directory to be set as well, via `bundle config bindir foo`.
+ Bundler.settings[:system_bindir] || Bundler.rubygems.gem_bindir
+ end
+
+ def use_system_gems?
+ 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
+ end
+ end
+
+ def which(executable)
+ if File.file?(executable) && File.executable?(executable)
+ executable
+ elsif paths = ENV["PATH"]
+ quote = '"'.freeze
+ 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)
+ return executable_path if File.file?(executable_path) && File.executable?(executable_path)
+ end
+ 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 install --path vendor/bundle
+
+ 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 StandardError => e
+ raise MarshalError, "#{e.class}: #{e.message}"
+ end
+
+ def load_gemspec(file, validate = false)
+ @gemspec_cache ||= {}
+ key = File.expand_path(file)
+ @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]
+ end
+
+ def load_gemspec_uncached(file, validate = false)
+ path = Pathname.new(file)
+ contents = read_file(file)
+ spec = if contents.start_with?("---") # YAML header
+ eval_yaml_gemspec(path, contents)
+ else
+ # Eval the gemspec from its parent directory, because some gemspecs
+ # depend on "./" relative paths.
+ SharedHelpers.chdir(path.dirname.to_s) do
+ eval_gemspec(path, contents)
+ end
+ end
+ return unless spec
+ spec.loaded_from = path.expand_path.to_s
+ Bundler.rubygems.validate(spec) if validate
+ spec
+ end
+
+ def clear_gemspec_cache
+ @gemspec_cache = {}
+ end
+
+ def git_present?
+ return @git_present if defined?(@git_present)
+ @git_present = Bundler.which("git") || Bundler.which("git.exe")
+ end
+
+ def feature_flag
+ @feature_flag ||= FeatureFlag.new(VERSION)
+ end
+
+ def reset!
+ reset_paths!
+ Plugin.reset!
+ reset_rubygems!
+ end
+
+ def reset_paths!
+ @bin_path = nil
+ @bundler_major_version = nil
+ @bundle_path = nil
+ @configured = nil
+ @configured_bundle_path = nil
+ @definition = nil
+ @load = nil
+ @locked_gems = nil
+ @root = nil
+ @settings = nil
+ @setup = nil
+ @user_home = nil
+ end
+
+ def reset_rubygems!
+ return unless defined?(@rubygems) && @rubygems
+ rubygems.undo_replacements
+ rubygems.reset
+ @rubygems = nil
+ end
+
+ private
+
+ def eval_yaml_gemspec(path, contents)
+ Kernel.send(:require, "bundler/psyched_yaml")
+
+ # If the YAML is invalid, Syck raises an ArgumentError, and Psych
+ # raises a Psych::SyntaxError. See psyched_yaml.rb for more info.
+ Gem::Specification.from_yaml(contents)
+ rescue YamlLibrarySyntaxError, ArgumentError, Gem::EndOfYAMLException, Gem::Exception
+ eval_gemspec(path, contents)
+ end
+
+ def eval_gemspec(path, contents)
+ eval(contents, TOPLEVEL_BINDING.dup, path.expand_path.to_s)
+ rescue ScriptError, StandardError => e
+ msg = "There was an error while loading `#{path.basename}`: #{e.message}"
+
+ if e.is_a?(LoadError) && RUBY_VERSION >= "1.9"
+ msg += "\nDoes it try to require a relative path? That's been removed in Ruby 1.9"
+ end
+
+ raise GemspecError, Dsl::DSLError.new(msg, path, e.backtrace, contents)
+ end
+
+ def configure_gem_home_and_path
+ configure_gem_path
+ configure_gem_home
+ bundle_path
+ end
+
+ def configure_gem_path(env = ENV)
+ blank_home = env["GEM_HOME"].nil? || env["GEM_HOME"].empty?
+ if !use_system_gems?
+ # this needs to be empty string to cause
+ # PathSupport.split_gem_path to only load up the
+ # Bundler --path setting as the GEM_PATH.
+ env["GEM_PATH"] = ""
+ elsif blank_home
+ possibles = [Bundler.rubygems.gem_dir, Bundler.rubygems.gem_path]
+ paths = possibles.flatten.compact.uniq.reject(&:empty?)
+ env["GEM_PATH"] = paths.join(File::PATH_SEPARATOR)
+ end
+ end
+
+ def configure_gem_home
+ Bundler::SharedHelpers.set_env "GEM_HOME", File.expand_path(bundle_path, root)
+ Bundler.rubygems.clear_paths
+ end
+
+ # @param env [Hash]
+ def with_env(env)
+ backup = ENV.to_hash
+ ENV.replace(env)
+ yield
+ ensure
+ ENV.replace(backup)
+ end
+ end
+end
diff --git a/lib/bundler/build_metadata.rb b/lib/bundler/build_metadata.rb
new file mode 100644
index 0000000000..6146f16cb6
--- /dev/null
+++ b/lib/bundler/build_metadata.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module Bundler
+ # Represents metadata from when the Bundler gem was built.
+ module BuildMetadata
+ # begin ivars
+ @built_at = "2018-12-19".freeze
+ @git_commit_sha = "3fc4de72b".freeze
+ @release = false
+ # end ivars
+
+ # A hash representation of the build metadata.
+ def self.to_h
+ {
+ "Built At" => built_at,
+ "Git SHA" => git_commit_sha,
+ "Released Version" => release?,
+ }
+ end
+
+ # A string representing the date the bundler gem was built.
+ def self.built_at
+ @built_at ||= Time.now.utc.strftime("%Y-%m-%d").freeze
+ end
+
+ # The SHA for the git commit the bundler gem was built from.
+ def self.git_commit_sha
+ return @git_commit_sha if @git_commit_sha
+
+ # If Bundler has been installed without its .git directory and without a
+ # commit instance variable then we can't determine its commits SHA.
+ git_dir = File.join(File.expand_path("../../..", __FILE__), ".git")
+ if File.directory?(git_dir)
+ return @git_commit_sha = Dir.chdir(git_dir) { `git rev-parse --short HEAD`.strip.freeze }
+ end
+
+ # If Bundler is a submodule in RubyGems, get the submodule commit
+ git_sub_dir = File.join(File.expand_path("../../../..", __FILE__), ".git")
+ if File.directory?(git_sub_dir)
+ return @git_commit_sha = Dir.chdir(git_sub_dir) do
+ `git ls-tree --abbrev=8 HEAD bundler`.split(/\s/).fetch(2, "").strip.freeze
+ end
+ end
+
+ @git_commit_sha ||= "unknown"
+ end
+
+ # Whether this is an official release build of Bundler.
+ def self.release?
+ @release
+ end
+ end
+end
diff --git a/lib/bundler/bundler.gemspec b/lib/bundler/bundler.gemspec
new file mode 100644
index 0000000000..b229ab224d
--- /dev/null
+++ b/lib/bundler/bundler.gemspec
@@ -0,0 +1,64 @@
+# coding: utf-8
+# frozen_string_literal: true
+
+begin
+ require File.expand_path("../lib/bundler/version", __FILE__)
+rescue LoadError
+ # for Ruby core repository
+ require File.expand_path("../version", __FILE__)
+end
+
+Gem::Specification.new do |s|
+ s.name = "bundler"
+ s.version = Bundler::VERSION
+ s.license = "MIT"
+ 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.email = ["team@bundler.io"]
+ s.homepage = "http://bundler.io"
+ 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" => "http://github.com/bundler/bundler/issues",
+ "changelog_uri" => "https://github.com/bundler/bundler/blob/master/CHANGELOG.md",
+ "homepage_uri" => "https://bundler.io/",
+ "source_code_uri" => "http://github.com/bundler/bundler/",
+ }
+ end
+
+ if s.version >= Gem::Version.new("2.a".dup)
+ s.required_ruby_version = ">= 2.3.0"
+ s.required_rubygems_version = ">= 2.5.0"
+ else
+ s.required_ruby_version = ">= 1.8.7"
+ s.required_rubygems_version = ">= 1.3.6"
+ end
+
+ s.add_development_dependency "automatiek", "~> 0.1.0"
+ s.add_development_dependency "mustache", "0.99.6"
+ s.add_development_dependency "rake", "~> 10.0"
+ s.add_development_dependency "rdiscount", "~> 2.2"
+ s.add_development_dependency "ronn", "~> 0.7.3"
+ s.add_development_dependency "rspec", "~> 3.6"
+
+ # base_dir = File.dirname(__FILE__).gsub(%r{([^A-Za-z0-9_\-.,:\/@\n])}, "\\\\\\1")
+ # s.files = IO.popen("git -C #{base_dir} ls-files -z", &:read).split("\x0").select {|f| f.match(%r{^(lib|exe)/}) }
+
+ # we don't check in man pages, but we need to ship them because
+ # we use them to generate the long-form help for each command.
+ # s.files += Dir.glob("man/**/*")
+ # Include the CHANGELOG.md, LICENSE.md, README.md manually
+ # s.files += %w[CHANGELOG.md LICENSE.md README.md]
+ # include the gemspec itself because warbler breaks w/o it
+ s.files += %w[bundler.gemspec]
+
+ s.bindir = "exe"
+ s.executables = %w[bundle bundler]
+ s.require_paths = ["lib"]
+end
diff --git a/lib/bundler/capistrano.rb b/lib/bundler/capistrano.rb
new file mode 100644
index 0000000000..1b7145b72b
--- /dev/null
+++ b/lib/bundler/capistrano.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require "bundler/shared_helpers"
+Bundler::SharedHelpers.major_deprecation 2,
+ "The Bundler task for Capistrano. Please use http://github.com/capistrano/bundler"
+
+# Capistrano task for Bundler.
+#
+# Add "require 'bundler/capistrano'" in your Capistrano deploy.rb, and
+# Bundler will be activated after each new deployment.
+require "bundler/deployment"
+require "capistrano/version"
+
+if defined?(Capistrano::Version) && Gem::Version.new(Capistrano::Version).release >= Gem::Version.new("3.0")
+ raise "For Capistrano 3.x integration, please use http://github.com/capistrano/bundler"
+end
+
+Capistrano::Configuration.instance(:must_exist).load do
+ before "deploy:finalize_update", "bundle:install"
+ Bundler::Deployment.define_task(self, :task, :except => { :no_release => true })
+ set :rake, lambda { "#{fetch(:bundle_cmd, "bundle")} exec rake" }
+end
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
new file mode 100644
index 0000000000..e658ffce72
--- /dev/null
+++ b/lib/bundler/cli.rb
@@ -0,0 +1,790 @@
+# frozen_string_literal: true
+
+require "bundler"
+require "bundler/vendored_thor"
+
+module Bundler
+ class CLI < Thor
+ require "bundler/cli/common"
+
+ package_name "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
+
+ def self.start(*)
+ super
+ rescue Exception => e
+ Bundler.ui = UI::Shell.new
+ raise e
+ ensure
+ Bundler::SharedHelpers.print_major_deprecations!
+ end
+
+ def self.dispatch(*)
+ super do |i|
+ i.send(:print_command)
+ i.send(:warn_on_outdated_bundler)
+ end
+ end
+
+ def initialize(*args)
+ super
+
+ custom_gemfile = options[:gemfile] || Bundler.settings[:gemfile]
+ if custom_gemfile && !custom_gemfile.empty?
+ Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", File.expand_path(custom_gemfile)
+ Bundler.reset_paths!
+ end
+
+ Bundler.settings.set_command_option_if_given :retry, options[:retry]
+
+ current_cmd = args.last[:current_command].name
+ auto_install if AUTO_INSTALL_CMDS.include?(current_cmd)
+ rescue UnknownArgumentError => e
+ raise InvalidOption, e.message
+ ensure
+ self.options ||= {}
+ unprinted_warnings = Bundler.ui.unprinted_warnings
+ Bundler.ui = UI::Shell.new(options)
+ Bundler.ui.level = "debug" if options["verbose"]
+ unprinted_warnings.each {|w| Bundler.ui.warn(w) }
+
+ if ENV["RUBYGEMS_GEMDEPS"] && !ENV["RUBYGEMS_GEMDEPS"].empty?
+ Bundler.ui.warn(
+ "The RUBYGEMS_GEMDEPS environment variable is set. This enables RubyGems' " \
+ "experimental Gemfile mode, which may conflict with Bundler and cause unexpected errors. " \
+ "To remove this warning, unset RUBYGEMS_GEMDEPS.", :wrap => true
+ )
+ end
+ end
+
+ def self.deprecated_option(*args, &blk)
+ return if Bundler.feature_flag.forget_cli_options?
+ method_option(*args, &blk)
+ end
+
+ check_unknown_options!(:except => [:config, :exec])
+ stop_on_unknown_option! :exec
+
+ desc "cli_help", "Prints a summary of bundler commands", :hide => true
+ def cli_help
+ version
+ Bundler.ui.info "\n"
+
+ primary_commands = ["install", "update",
+ Bundler.feature_flag.cache_command_is_package? ? "cache" : "package",
+ "exec", "config", "help"]
+
+ list = self.class.printable_commands(true)
+ by_name = list.group_by {|name, _message| name.match(/^bundle (\w+)/)[1] }
+ utilities = by_name.keys.sort - primary_commands
+ primary_commands.map! {|name| (by_name[name] || raise("no primary command #{name}")).first }
+ utilities.map! {|name| by_name[name].first }
+
+ shell.say "Bundler commands:\n\n"
+
+ shell.say " Primary commands:\n"
+ shell.print_table(primary_commands, :indent => 4, :truncate => true)
+ shell.say
+ shell.say " Utilities:\n"
+ shell.print_table(utilities, :indent => 4, :truncate => true)
+ shell.say
+ self.class.send(:class_options_help, shell)
+ end
+ default_task(Bundler.feature_flag.default_cli_command)
+
+ class_option "no-color", :type => :boolean, :desc => "Disable colorization in output"
+ class_option "retry", :type => :numeric, :aliases => "-r", :banner => "NUM",
+ :desc => "Specify the number of times you wish to attempt network commands"
+ class_option "verbose", :type => :boolean, :desc => "Enable verbose output mode", :aliases => "-V"
+
+ def help(cli = nil)
+ case cli
+ when "gemfile" then command = "gemfile"
+ when nil then command = "bundle"
+ else command = "bundle-#{cli}"
+ end
+
+ man_path = File.expand_path("../../../man", __FILE__)
+ man_pages = Hash[Dir.glob(File.join(man_path, "*")).grep(/.*\.\d*\Z/).collect do |f|
+ [File.basename(f, ".*"), f]
+ end]
+
+ if man_pages.include?(command)
+ if Bundler.which("man") && man_path !~ %r{^file:/.+!/META-INF/jruby.home/.+}
+ Kernel.exec "man #{man_pages[command]}"
+ else
+ puts File.read("#{man_path}/#{File.basename(man_pages[command])}.txt")
+ end
+ elsif command_path = Bundler.which("bundler-#{cli}")
+ Kernel.exec(command_path, "--help")
+ else
+ super
+ end
+ end
+
+ def self.handle_no_command_error(command, has_namespace = $thor_runner)
+ if Bundler.feature_flag.plugins? && Bundler::Plugin.command?(command)
+ return Bundler::Plugin.exec_command(command, ARGV[1..-1])
+ end
+
+ return super unless command_path = Bundler.which("bundler-#{command}")
+
+ Kernel.exec(command_path, *ARGV[1..-1])
+ end
+
+ desc "init [OPTIONS]", "Generates a Gemfile into the current working directory"
+ long_desc <<-D
+ Init generates a default Gemfile in the current working directory. When adding a
+ Gemfile to a gem with a gemspec, the --gemspec option will automatically add each
+ dependency listed in the gemspec file to the newly created Gemfile.
+ D
+ deprecated_option "gemspec", :type => :string, :banner => "Use the specified .gemspec to create the Gemfile"
+ def init
+ require "bundler/cli/init"
+ Init.new(options.dup).run
+ end
+
+ desc "check [OPTIONS]", "Checks if the dependencies listed in Gemfile are satisfied by currently installed gems"
+ long_desc <<-D
+ Check searches the local machine for each of the gems requested in the Gemfile. If
+ all gems are found, Bundler prints a success message and exits with a status of 0.
+ If not, the first missing gem is listed and Bundler exits status 1.
+ D
+ method_option "dry-run", :type => :boolean, :default => false, :banner =>
+ "Lock the Gemfile"
+ method_option "gemfile", :type => :string, :banner =>
+ "Use the specified gemfile instead of Gemfile"
+ method_option "path", :type => :string, :banner =>
+ "Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME).#{" Bundler will remember this value for future installs on this machine" unless Bundler.feature_flag.forget_cli_options?}"
+ map "c" => "check"
+ def check
+ require "bundler/cli/check"
+ Check.new(options).run
+ end
+
+ desc "remove [GEM [GEM ...]]", "Removes gems from the Gemfile"
+ long_desc <<-D
+ Removes the given gems from the Gemfile while ensuring that the resulting Gemfile is still valid. If the gem is not found, Bundler prints a error message and if gem could not be removed due to any reason Bundler will display a warning.
+ D
+ method_option "install", :type => :boolean, :banner =>
+ "Runs 'bundle install' after removing the gems from the Gemfile"
+ def remove(*gems)
+ require "bundler/cli/remove"
+ Remove.new(gems, options).run
+ end
+
+ desc "install [OPTIONS]", "Install the current environment to the system"
+ long_desc <<-D
+ Install will install all of the gems in the current bundle, making them available
+ for use. In a freshly checked out repository, this command will give you the same
+ gem versions as the last person who updated the Gemfile and ran `bundle update`.
+
+ Passing [DIR] to install (e.g. vendor) will cause the unpacked gems to be installed
+ into the [DIR] directory rather than into system gems.
+
+ If the bundle has already been installed, bundler will tell you so and then exit.
+ D
+ deprecated_option "binstubs", :type => :string, :lazy_default => "bin", :banner =>
+ "Generate bin stubs for bundled gems to ./bin"
+ deprecated_option "clean", :type => :boolean, :banner =>
+ "Run bundle clean automatically after install"
+ deprecated_option "deployment", :type => :boolean, :banner =>
+ "Install using defaults tuned for deployment environments"
+ deprecated_option "frozen", :type => :boolean, :banner =>
+ "Do not allow the Gemfile.lock to be updated after this install"
+ method_option "full-index", :type => :boolean, :banner =>
+ "Fall back to using the single-file index of all gems"
+ method_option "gemfile", :type => :string, :banner =>
+ "Use the specified gemfile instead of Gemfile"
+ method_option "jobs", :aliases => "-j", :type => :numeric, :banner =>
+ "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"
+ deprecated_option "no-cache", :type => :boolean, :banner =>
+ "Don't update the existing gem cache."
+ method_option "redownload", :type => :boolean, :aliases => "--force", :banner =>
+ "Force downloading every gem."
+ deprecated_option "no-prune", :type => :boolean, :banner =>
+ "Don't remove stale gems from the cache."
+ deprecated_option "path", :type => :string, :banner =>
+ "Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME). Bundler will remember this value for future installs on this machine"
+ method_option "quiet", :type => :boolean, :banner =>
+ "Only output warnings and errors."
+ deprecated_option "shebang", :type => :string, :banner =>
+ "Specify a different shebang executable name than the default (usually 'ruby')"
+ method_option "standalone", :type => :array, :lazy_default => [], :banner =>
+ "Make a bundle that can work without the Bundler runtime"
+ deprecated_option "system", :type => :boolean, :banner =>
+ "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("|")
+ deprecated_option "without", :type => :array, :banner =>
+ "Exclude gems that are part of the specified named group."
+ deprecated_option "with", :type => :array, :banner =>
+ "Include gems that are part of the specified named group."
+ map "i" => "install"
+ def install
+ SharedHelpers.major_deprecation(2, "The `--force` option has been renamed to `--redownload`") if ARGV.include?("--force")
+ require "bundler/cli/install"
+ Bundler.settings.temporary(:no_install => false) do
+ Install.new(options.dup).run
+ end
+ end
+
+ desc "update [OPTIONS]", "Update the current environment"
+ long_desc <<-D
+ Update will install the newest versions of the gems listed in the Gemfile. Use
+ update when you have changed the Gemfile, or if you want to get the newest
+ possible versions of the gems in the bundle.
+ D
+ method_option "full-index", :type => :boolean, :banner =>
+ "Fall back to using the single-file index of all gems"
+ method_option "gemfile", :type => :string, :banner =>
+ "Use the specified gemfile instead of Gemfile"
+ method_option "group", :aliases => "-g", :type => :array, :banner =>
+ "Update a specific group"
+ method_option "jobs", :aliases => "-j", :type => :numeric, :banner =>
+ "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 "quiet", :type => :boolean, :banner =>
+ "Only output warnings and errors."
+ method_option "source", :type => :array, :banner =>
+ "Update a specific source (and all gems associated with it)"
+ method_option "redownload", :type => :boolean, :aliases => "--force", :banner =>
+ "Force downloading every gem."
+ method_option "ruby", :type => :boolean, :banner =>
+ "Update ruby specified in Gemfile.lock"
+ method_option "bundler", :type => :string, :lazy_default => "> 0.a", :banner =>
+ "Update the locked version of bundler"
+ method_option "patch", :type => :boolean, :banner =>
+ "Prefer updating only to next patch version"
+ 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)"
+ 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 =>
+ "Use bundle install conservative update behavior and do not allow shared dependencies to be updated."
+ method_option "all", :type => :boolean, :banner =>
+ "Update everything."
+ def update(*gems)
+ SharedHelpers.major_deprecation(2, "The `--force` option has been renamed to `--redownload`") if ARGV.include?("--force")
+ require "bundler/cli/update"
+ Update.new(options, gems).run
+ end
+
+ desc "show GEM [OPTIONS]", "Shows all gems that are part of the bundle, or the path to a given gem"
+ long_desc <<-D
+ Show lists the names and versions of all gems that are required by your Gemfile.
+ Calling show with [GEM] will list the exact location of that gem on your machine.
+ D
+ method_option "paths", :type => :boolean,
+ :banner => "List the paths of all gems that are required by your Gemfile."
+ method_option "outdated", :type => :boolean,
+ :banner => "Show verbose output including whether gems are outdated."
+ def show(gem_name = nil)
+ if ARGV[0] == "show"
+ rest = ARGV[1..-1]
+
+ new_command = rest.find {|arg| !arg.start_with?("--") } ? "info" : "list"
+
+ new_arguments = rest.map do |arg|
+ next arg if arg != "--paths"
+ next "--path" if new_command == "info"
+ end
+
+ old_argv = ARGV.join(" ")
+ new_argv = [new_command, *new_arguments.compact].join(" ")
+
+ Bundler::SharedHelpers.major_deprecation(2, "use `bundle #{new_argv}` instead of `bundle #{old_argv}`")
+ end
+ require "bundler/cli/show"
+ Show.new(options, gem_name).run
+ end
+ # TODO: 2.0 remove `bundle show`
+
+ if Bundler.feature_flag.list_command?
+ desc "list", "List all gems in the bundle"
+ method_option "name-only", :type => :boolean, :banner => "print only the gem names"
+ method_option "only-group", :type => :string, :banner => "print gems from a particular group"
+ method_option "without-group", :type => :string, :banner => "print all gems expect from a group"
+ method_option "paths", :type => :boolean, :banner => "print the path to each gem in the bundle"
+ def list
+ require "bundler/cli/list"
+ List.new(options).run
+ end
+
+ map %w[ls] => "list"
+ else
+ map %w[list] => "show"
+ end
+
+ desc "info GEM [OPTIONS]", "Show information for the given gem"
+ method_option "path", :type => :boolean, :banner => "Print full path to gem"
+ def info(gem_name)
+ require "bundler/cli/info"
+ Info.new(options, gem_name).run
+ end
+
+ desc "binstubs GEM [OPTIONS]", "Install the binstubs of the listed gem"
+ long_desc <<-D
+ Generate binstubs for executables in [GEM]. Binstubs are put into bin,
+ or the --binstubs directory if one has been set. Calling binstubs with [GEM [GEM]]
+ will create binstubs for all given gems.
+ D
+ method_option "force", :type => :boolean, :default => false, :banner =>
+ "Overwrite existing binstubs if they exist"
+ method_option "path", :type => :string, :lazy_default => "bin", :banner =>
+ "Binstub destination directory (default bin)"
+ method_option "shebang", :type => :string, :banner =>
+ "Specify a different shebang executable name than the default (usually 'ruby')"
+ method_option "standalone", :type => :boolean, :banner =>
+ "Make binstubs that can work without the Bundler runtime"
+ method_option "all", :type => :boolean, :banner =>
+ "Install binstubs for all gems"
+ def binstubs(*gems)
+ require "bundler/cli/binstubs"
+ Binstubs.new(options, gems).run
+ end
+
+ desc "add GEM VERSION", "Add gem to Gemfile and run bundle install"
+ long_desc <<-D
+ Adds the specified gem to Gemfile (if valid) and run 'bundle install' in one step.
+ D
+ method_option "version", :aliases => "-v", :type => :string
+ method_option "group", :aliases => "-g", :type => :string
+ method_option "source", :aliases => "-s", :type => :string
+ method_option "skip-install", :type => :boolean, :banner =>
+ "Adds gem to the Gemfile but does not install it"
+ method_option "optimistic", :type => :boolean, :banner => "Adds optimistic declaration of version to gem"
+ method_option "strict", :type => :boolean, :banner => "Adds strict declaration of version to gem"
+ def add(*gems)
+ require "bundler/cli/add"
+ Add.new(options.dup, gems).run
+ end
+
+ desc "outdated GEM [OPTIONS]", "List installed gems with newer versions available"
+ long_desc <<-D
+ Outdated lists the names and versions of gems that have a newer version available
+ in the given source. Calling outdated with [GEM [GEM]] will only check for newer
+ versions of the given gems. Prerelease gems are ignored by default. If your gems
+ are up to date, Bundler will exit with a status of 0. Otherwise, it will exit 1.
+
+ For more information on patch level options (--major, --minor, --patch,
+ --update-strict) see documentation on the same options on the update command.
+ D
+ method_option "group", :type => :string, :banner => "List gems from a specific group"
+ method_option "groups", :type => :boolean, :banner => "List gems organized by groups"
+ method_option "local", :type => :boolean, :banner =>
+ "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 "strict", :type => :boolean, :banner =>
+ "Only list newer versions allowed by your Gemfile requirements"
+ 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)"
+ method_option "patch", :type => :boolean, :banner => "Prefer updating only to next patch version"
+ method_option "filter-major", :type => :boolean, :banner => "Only list major newer versions"
+ method_option "filter-minor", :type => :boolean, :banner => "Only list minor newer versions"
+ method_option "filter-patch", :type => :boolean, :banner => "Only list patch newer versions"
+ method_option "parseable", :aliases => "--porcelain", :type => :boolean, :banner =>
+ "Use minimal formatting for more parseable output"
+ method_option "only-explicit", :type => :boolean, :banner =>
+ "Only list gems specified in your Gemfile, not their dependencies"
+ def outdated(*gems)
+ require "bundler/cli/outdated"
+ Outdated.new(options, gems).run
+ end
+
+ if Bundler.feature_flag.cache_command_is_package?
+ map %w[cache] => :package
+ else
+ desc "cache [OPTIONS]", "Cache all the gems to vendor/cache", :hide => true
+ unless Bundler.feature_flag.cache_command_is_package?
+ method_option "all", :type => :boolean,
+ :banner => "Include all sources (including path and git)."
+ end
+ method_option "all-platforms", :type => :boolean, :banner => "Include gems for all platforms present in the lockfile, not only the current one"
+ method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache."
+ def cache
+ require "bundler/cli/cache"
+ Cache.new(options).run
+ end
+ end
+
+ desc "#{Bundler.feature_flag.cache_command_is_package? ? :cache : :package} [OPTIONS]", "Locks and then caches all of the gems into vendor/cache"
+ unless Bundler.feature_flag.cache_command_is_package?
+ method_option "all", :type => :boolean,
+ :banner => "Include all sources (including path and git)."
+ end
+ method_option "all-platforms", :type => :boolean, :banner => "Include gems for all platforms present in the lockfile, not only the current one"
+ method_option "cache-path", :type => :string, :banner =>
+ "Specify a different cache path than the default (vendor/cache)."
+ method_option "gemfile", :type => :string, :banner => "Use the specified gemfile instead of Gemfile"
+ method_option "no-install", :type => :boolean, :banner => "Don't install the gems, only the package."
+ method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache."
+ method_option "path", :type => :string, :banner =>
+ "Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME). Bundler will remember this value for future installs on this machine"
+ method_option "quiet", :type => :boolean, :banner => "Only output warnings and errors."
+ method_option "frozen", :type => :boolean, :banner =>
+ "Do not allow the Gemfile.lock to be updated after this package operation's install"
+ long_desc <<-D
+ The package command will copy the .gem files for every gem in the bundle into the
+ directory ./vendor/cache. If you then check that directory into your source
+ control repository, others who check out your source will be able to install the
+ bundle without having to download any additional gems.
+ D
+ def package
+ require "bundler/cli/package"
+ Package.new(options).run
+ end
+ map %w[pack] => :package
+
+ desc "exec [OPTIONS]", "Run the command in context of the bundle"
+ method_option :keep_file_descriptors, :type => :boolean, :default => false
+ method_option :gemfile, :type => :string, :required => false
+ long_desc <<-D
+ Exec runs a command, providing it access to the gems in the bundle. While using
+ bundle exec you can require and call the bundled gems as if they were installed
+ into the system wide RubyGems repository.
+ D
+ map "e" => "exec"
+ def exec(*args)
+ require "bundler/cli/exec"
+ Exec.new(options, args).run
+ end
+
+ desc "config NAME [VALUE]", "Retrieve or set a configuration value"
+ long_desc <<-D
+ Retrieves or sets a configuration value. If only one parameter is provided, retrieve the value. If two parameters are provided, replace the
+ existing value with the newly provided one.
+
+ By default, setting a configuration value sets it for all projects
+ on the machine.
+
+ If a global setting is superceded by local configuration, this command
+ will show the current value, as well as any superceded values and
+ where they were specified.
+ D
+ method_option "parseable", :type => :boolean, :banner => "Use minimal formatting for more parseable output"
+ def config(*args)
+ require "bundler/cli/config"
+ Config.new(options, args, self).run
+ end
+
+ desc "open GEM", "Opens the source directory of the given bundled gem"
+ def open(name)
+ require "bundler/cli/open"
+ Open.new(options, name).run
+ end
+
+ if Bundler.feature_flag.console_command?
+ desc "console [GROUP]", "Opens an IRB session with the bundle pre-loaded"
+ def console(group = nil)
+ require "bundler/cli/console"
+ Console.new(options, group).run
+ end
+ end
+
+ desc "version", "Prints the bundler's version information"
+ def version
+ cli_help = current_command.name == "cli_help"
+ if cli_help || ARGV.include?("version")
+ build_info = " (#{BuildMetadata.built_at} commit #{BuildMetadata.git_commit_sha})"
+ end
+
+ if !cli_help && Bundler.feature_flag.print_only_version_number?
+ Bundler.ui.info "#{Bundler::VERSION}#{build_info}"
+ else
+ Bundler.ui.info "Bundler version #{Bundler::VERSION}#{build_info}"
+ end
+ end
+ map %w[-v --version] => :version
+
+ desc "licenses", "Prints the license of all gems in the bundle"
+ def licenses
+ Bundler.load.specs.sort_by {|s| s.license.to_s }.reverse_each do |s|
+ gem_name = s.name
+ license = s.license || s.licenses
+
+ if license.empty?
+ Bundler.ui.warn "#{gem_name}: Unknown"
+ else
+ Bundler.ui.info "#{gem_name}: #{license}"
+ end
+ end
+ end
+
+ if Bundler.feature_flag.viz_command?
+ desc "viz [OPTIONS]", "Generates a visual dependency graph", :hide => true
+ long_desc <<-D
+ Viz generates a PNG file of the current Gemfile as a dependency graph.
+ Viz requires the ruby-graphviz gem (and its dependencies).
+ The associated gems must also be installed via 'bundle install'.
+ D
+ method_option :file, :type => :string, :default => "gem_graph", :aliases => "-f", :desc => "The name to use for the generated file. see format option"
+ method_option :format, :type => :string, :default => "png", :aliases => "-F", :desc => "This is output format option. Supported format is png, jpg, svg, dot ..."
+ method_option :requirements, :type => :boolean, :default => false, :aliases => "-R", :desc => "Set to show the version of each required dependency."
+ method_option :version, :type => :boolean, :default => false, :aliases => "-v", :desc => "Set to show each gem version."
+ method_option :without, :type => :array, :default => [], :aliases => "-W", :banner => "GROUP[ GROUP...]", :desc => "Exclude gems that are part of the specified named group."
+ def viz
+ SharedHelpers.major_deprecation 2, "The `viz` command has been moved to the `bundle-viz` gem, see https://github.com/bundler/bundler-viz"
+ require "bundler/cli/viz"
+ Viz.new(options.dup).run
+ end
+ end
+
+ old_gem = instance_method(:gem)
+
+ desc "gem NAME [OPTIONS]", "Creates a skeleton for creating a rubygem"
+ method_option :exe, :type => :boolean, :default => false, :aliases => ["--bin", "-b"], :desc => "Generate a binary executable for your library."
+ method_option :coc, :type => :boolean, :desc => "Generate a code of conduct file. Set a default with `bundle config gem.coc true`."
+ 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 :mit, :type => :boolean, :desc => "Generate an MIT license file. Set a default with `bundle config gem.mit true`."
+ method_option :test, :type => :string, :lazy_default => "rspec", :aliases => "-t", :banner => "rspec",
+ :desc => "Generate a test directory for your library, either rspec or minitest. Set a default with `bundle config gem.test rspec`."
+ def gem(name)
+ end
+
+ commands["gem"].tap do |gem_command|
+ def gem_command.run(instance, args = [])
+ arity = 1 # name
+
+ require "bundler/cli/gem"
+ cmd_args = args + [instance]
+ cmd_args.unshift(instance.options)
+
+ cmd = begin
+ Gem.new(*cmd_args)
+ rescue ArgumentError => e
+ instance.class.handle_argument_error(self, e, args, arity)
+ end
+
+ cmd.run
+ end
+ end
+
+ undef_method(:gem)
+ define_method(:gem, old_gem)
+ private :gem
+
+ def self.source_root
+ File.expand_path(File.join(File.dirname(__FILE__), "templates"))
+ end
+
+ desc "clean [OPTIONS]", "Cleans up unused gems in your bundler directory", :hide => true
+ 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"
+ def clean
+ require "bundler/cli/clean"
+ Clean.new(options.dup).run
+ end
+
+ desc "platform [OPTIONS]", "Displays platform compatibility information"
+ method_option "ruby", :type => :boolean, :default => false, :banner =>
+ "only display ruby related platform information"
+ def platform
+ require "bundler/cli/platform"
+ Platform.new(options).run
+ end
+
+ desc "inject GEM VERSION", "Add the named gem, with version requirements, to the resolved Gemfile", :hide => true
+ method_option "source", :type => :string, :banner =>
+ "Install gem from the given source"
+ method_option "group", :type => :string, :banner =>
+ "Install gem into a bundler group"
+ def inject(name, version)
+ SharedHelpers.major_deprecation 2, "The `inject` command has been replaced by the `add` command"
+ require "bundler/cli/inject"
+ Inject.new(options.dup, name, version).run
+ end
+
+ desc "lock", "Creates a lockfile without installing"
+ method_option "update", :type => :array, :lazy_default => true, :banner =>
+ "ignore the existing lockfile, update all gems by default, or update list of given gems"
+ method_option "local", :type => :boolean, :default => false, :banner =>
+ "do not attempt to fetch remote gemspecs and use the local gem cache only"
+ method_option "print", :type => :boolean, :default => false, :banner =>
+ "print the lockfile to STDOUT instead of writing to the file system"
+ method_option "lockfile", :type => :string, :default => nil, :banner =>
+ "the path the lockfile should be written to"
+ method_option "full-index", :type => :boolean, :default => false, :banner =>
+ "Fall back to using the single-file index of all gems"
+ method_option "add-platform", :type => :array, :default => [], :banner =>
+ "Add a new platform to the lockfile"
+ method_option "remove-platform", :type => :array, :default => [], :banner =>
+ "Remove a platform from the lockfile"
+ method_option "patch", :type => :boolean, :banner =>
+ "If updating, prefer updating only to next patch version"
+ method_option "minor", :type => :boolean, :banner =>
+ "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 "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"
+ def lock
+ require "bundler/cli/lock"
+ Lock.new(options).run
+ end
+
+ desc "env", "Print information about the environment Bundler is running under"
+ def env
+ Env.write($stdout)
+ end
+
+ desc "doctor [OPTIONS]", "Checks the bundle for common problems"
+ long_desc <<-D
+ Doctor scans the OS dependencies of each of the gems requested in the Gemfile. If
+ missing dependencies are detected, Bundler prints them and exits status 1.
+ Otherwise, Bundler prints a success message and exits with a status of 0.
+ D
+ method_option "gemfile", :type => :string, :banner =>
+ "Use the specified gemfile instead of Gemfile"
+ method_option "quiet", :type => :boolean, :banner =>
+ "Only output warnings and errors."
+ def doctor
+ require "bundler/cli/doctor"
+ Doctor.new(options).run
+ end
+
+ desc "issue", "Learn how to report an issue in Bundler"
+ def issue
+ require "bundler/cli/issue"
+ Issue.new.run
+ end
+
+ desc "pristine [GEMS...]", "Restores installed gems to pristine condition"
+ long_desc <<-D
+ Restores installed gems to pristine condition from files located in the
+ gem cache. Gems installed from a git repository will be issued `git
+ checkout --force`.
+ D
+ def pristine(*gems)
+ require "bundler/cli/pristine"
+ Pristine.new(gems).run
+ end
+
+ if Bundler.feature_flag.plugins?
+ require "bundler/cli/plugin"
+ desc "plugin", "Manage the bundler plugins"
+ subcommand "plugin", Plugin
+ end
+
+ # Reformat the arguments passed to bundle that include a --help flag
+ # into the corresponding `bundle help #{command}` call
+ def self.reformatted_help_args(args)
+ bundler_commands = all_commands.keys
+ help_flags = %w[--help -h]
+ exec_commands = %w[e ex exe exec]
+ help_used = args.index {|a| help_flags.include? a }
+ exec_used = args.index {|a| exec_commands.include? a }
+ command = args.find {|a| bundler_commands.include? a }
+ if exec_used && help_used
+ if exec_used + help_used == 1
+ %w[help exec]
+ else
+ args
+ end
+ elsif help_used
+ args = args.dup
+ args.delete_at(help_used)
+ ["help", command || args].flatten.compact
+ else
+ args
+ end
+ end
+
+ private
+
+ # Automatically invoke `bundle install` and resume if
+ # Bundler.settings[:auto_install] exists. This is set through config cmd
+ # `bundle config auto_install 1`.
+ #
+ # Note that this method `nil`s out the global Definition object, so it
+ # should be called first, before you instantiate anything like an
+ # `Installer` that'll keep a reference to the old one instead.
+ def auto_install
+ return unless Bundler.settings[:auto_install]
+
+ begin
+ Bundler.definition.specs
+ rescue GemNotFound
+ Bundler.ui.info "Automatically installing missing gems."
+ Bundler.reset!
+ invoke :install, []
+ Bundler.reset!
+ end
+ end
+
+ def current_command
+ _, _, config = @_initializer
+ config[:current_command]
+ end
+
+ def print_command
+ return unless Bundler.ui.debug?
+ cmd = current_command
+ command_name = cmd.name
+ return if PARSEABLE_COMMANDS.include?(command_name)
+ command = ["bundle", command_name] + args
+ options_to_print = options.dup
+ options_to_print.delete_if do |k, v|
+ next unless o = cmd.options[k]
+ o.default == v
+ end
+ command << Thor::Options.to_switches(options_to_print.sort_by(&:first)).strip
+ command.reject!(&:empty?)
+ Bundler.ui.info "Running `#{command * " "}` with bundler #{Bundler::VERSION}"
+ end
+
+ def warn_on_outdated_bundler
+ return if Bundler.settings[:disable_version_check]
+
+ command_name = current_command.name
+ return if PARSEABLE_COMMANDS.include?(command_name)
+
+ return unless SharedHelpers.md5_available?
+
+ latest = Fetcher::CompactIndex.
+ new(nil, Source::Rubygems::Remote.new(URI("https://rubygems.org")), nil).
+ send(:compact_index_client).
+ instance_variable_get(:@cache).
+ dependencies("bundler").
+ map {|d| Gem::Version.new(d.first) }.
+ max
+ return unless latest
+
+ current = Gem::Version.new(VERSION)
+ return if current >= latest
+ latest_installed = Bundler.rubygems.find_name("bundler").map(&:version).max
+
+ installation = "To install the latest version, run `gem install bundler#{" --pre" if latest.prerelease?}`"
+ if latest_installed && latest_installed > current
+ suggestion = "To update to the most recent installed version (#{latest_installed}), run `bundle update --bundler`"
+ suggestion = "#{installation}\n#{suggestion}" if latest_installed < latest
+ else
+ suggestion = installation
+ end
+
+ Bundler.ui.warn "The latest bundler is #{latest}, but you are currently running #{current}.\n#{suggestion}"
+ rescue RuntimeError
+ nil
+ end
+ end
+end
diff --git a/lib/bundler/cli/add.rb b/lib/bundler/cli/add.rb
new file mode 100644
index 0000000000..9709e71be0
--- /dev/null
+++ b/lib/bundler/cli/add.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Add
+ def initialize(options, gems)
+ @gems = gems
+ @options = options
+ @options[:group] = @options[:group].split(",").map(&:strip) if !@options[:group].nil? && !@options[:group].empty?
+ end
+
+ def run
+ raise InvalidOption, "You can not specify `--strict` and `--optimistic` at the same time." if @options[:strict] && @options[:optimistic]
+
+ # raise error when no gems are specified
+ raise InvalidOption, "Please specify gems to add." if @gems.empty?
+
+ version = @options[:version].nil? ? nil : @options[:version].split(",").map(&:strip)
+
+ unless version.nil?
+ version.each do |v|
+ raise InvalidOption, "Invalid gem requirement pattern '#{v}'" unless Gem::Requirement::PATTERN =~ v.to_s
+ end
+ end
+
+ dependencies = @gems.map {|g| Bundler::Dependency.new(g, version, @options) }
+
+ Injector.inject(dependencies,
+ :conservative_versioning => @options[:version].nil?, # Perform conservative versioning only when version is not specified
+ :optimistic => @options[:optimistic],
+ :strict => @options[:strict])
+
+ Installer.install(Bundler.root, Bundler.definition) unless @options["skip-install"]
+ end
+ end
+end
diff --git a/lib/bundler/cli/binstubs.rb b/lib/bundler/cli/binstubs.rb
new file mode 100644
index 0000000000..266396eedc
--- /dev/null
+++ b/lib/bundler/cli/binstubs.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Binstubs
+ attr_reader :options, :gems
+ def initialize(options, gems)
+ @options = options
+ @gems = gems
+ end
+
+ def run
+ Bundler.definition.validate_runtime!
+ path_option = options["path"]
+ path_option = nil if path_option && 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)
+
+ installer_opts = { :force => options[:force], :binstubs_cmd => true }
+
+ if options[:all]
+ raise InvalidOption, "Cannot specify --all with specific gems" unless gems.empty?
+ @gems = Bundler.definition.specs.map(&:name)
+ installer_opts.delete(:binstubs_cmd)
+ elsif gems.empty?
+ Bundler.ui.error "`bundle binstubs` needs at least one gem to run."
+ exit 1
+ end
+
+ gems.each do |gem_name|
+ spec = Bundler.definition.specs.find {|s| s.name == gem_name }
+ unless spec
+ raise GemNotFound, Bundler::CLI::Common.gem_not_found_message(
+ gem_name, Bundler.definition.specs
+ )
+ end
+
+ if options[:standalone]
+ next Bundler.ui.warn("Sorry, Bundler can only be run via RubyGems.") if gem_name == "bundler"
+ Bundler.settings.temporary(:path => (Bundler.settings[:path] || Bundler.root)) do
+ installer.generate_standalone_bundler_executable_stubs(spec)
+ end
+ else
+ installer.generate_bundler_executable_stubs(spec, installer_opts)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/cli/cache.rb b/lib/bundler/cli/cache.rb
new file mode 100644
index 0000000000..9d2ba87d34
--- /dev/null
+++ b/lib/bundler/cli/cache.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Cache
+ attr_reader :options
+ def initialize(options)
+ @options = options
+ end
+
+ def run
+ Bundler.definition.validate_runtime!
+ Bundler.definition.resolve_with_cache!
+ setup_cache_all
+ Bundler.settings.set_command_option_if_given :cache_all_platforms, options["all-platforms"]
+ Bundler.load.cache
+ Bundler.settings.set_command_option_if_given :no_prune, options["no-prune"]
+ Bundler.load.lock
+ rescue GemNotFound => e
+ Bundler.ui.error(e.message)
+ Bundler.ui.warn "Run `bundle install` to install missing gems."
+ exit 1
+ end
+
+ private
+
+ def setup_cache_all
+ Bundler.settings.set_command_option_if_given :cache_all, options[:all]
+
+ if Bundler.definition.has_local_dependencies? && !Bundler.feature_flag.cache_all?
+ Bundler.ui.warn "Your Gemfile contains path and git dependencies. If you want " \
+ "to package them as well, please pass the --all flag. This will be the default " \
+ "on Bundler 2.0."
+ end
+ end
+ end
+end
diff --git a/lib/bundler/cli/check.rb b/lib/bundler/cli/check.rb
new file mode 100644
index 0000000000..19c0aaea06
--- /dev/null
+++ b/lib/bundler/cli/check.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Check
+ attr_reader :options
+
+ def initialize(options)
+ @options = options
+ end
+
+ def run
+ Bundler.settings.set_command_option_if_given :path, options[:path]
+
+ begin
+ definition = Bundler.definition
+ definition.validate_runtime!
+ not_installed = definition.missing_specs
+ rescue GemNotFound, VersionConflict
+ Bundler.ui.error "Bundler can't satisfy your Gemfile's dependencies."
+ Bundler.ui.warn "Install missing gems with `bundle install`."
+ exit 1
+ end
+
+ if not_installed.any?
+ Bundler.ui.error "The following gems are missing"
+ not_installed.each {|s| Bundler.ui.error " * #{s.name} (#{s.version})" }
+ Bundler.ui.warn "Install missing gems with `bundle install`"
+ exit 1
+ elsif !Bundler.default_lockfile.file? && Bundler.frozen_bundle?
+ Bundler.ui.error "This bundle has been frozen, but there is no #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} present"
+ exit 1
+ else
+ Bundler.load.lock(:preserve_unknown_sections => true) unless options[:"dry-run"]
+ Bundler.ui.info "The Gemfile's dependencies are satisfied"
+ end
+ end
+ end
+end
diff --git a/lib/bundler/cli/clean.rb b/lib/bundler/cli/clean.rb
new file mode 100644
index 0000000000..4a407fbae7
--- /dev/null
+++ b/lib/bundler/cli/clean.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Clean
+ attr_reader :options
+
+ def initialize(options)
+ @options = options
+ end
+
+ def run
+ require_path_or_force unless options[:"dry-run"]
+ Bundler.load.clean(options[:"dry-run"])
+ end
+
+ protected
+
+ def require_path_or_force
+ return unless Bundler.use_system_gems? && !options[:force]
+ raise InvalidOption, "Cleaning all the gems on your system is dangerous! " \
+ "If you're sure you want to remove every system gem not in this " \
+ "bundle, run `bundle clean --force`."
+ end
+ end
+end
diff --git a/lib/bundler/cli/common.rb b/lib/bundler/cli/common.rb
new file mode 100644
index 0000000000..9d40ee9dfd
--- /dev/null
+++ b/lib/bundler/cli/common.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+
+module Bundler
+ module CLI::Common
+ def self.output_post_install_messages(messages)
+ return if Bundler.settings["ignore_messages"]
+ messages.to_a.each do |name, msg|
+ print_post_install_message(name, msg) unless Bundler.settings["ignore_messages.#{name}"]
+ end
+ end
+
+ def self.print_post_install_message(name, msg)
+ Bundler.ui.confirm "Post-install message from #{name}:"
+ Bundler.ui.info msg
+ end
+
+ def self.output_without_groups_message
+ return if Bundler.settings[:without].empty?
+ Bundler.ui.confirm without_groups_message
+ end
+
+ def self.without_groups_message
+ groups = Bundler.settings[:without]
+ group_list = [groups[0...-1].join(", "), groups[-1..-1]].
+ reject {|s| s.to_s.empty? }.join(" and ")
+ group_str = (groups.size == 1) ? "group" : "groups"
+ "Gems in the #{group_str} #{group_list} were not installed."
+ end
+
+ def self.select_spec(name, regex_match = nil)
+ specs = []
+ regexp = Regexp.new(name) if regex_match
+
+ Bundler.definition.specs.each do |spec|
+ return spec if spec.name == name
+ specs << spec if regexp && spec.name =~ regexp
+ end
+
+ case specs.count
+ when 0
+ raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies)
+ when 1
+ specs.first
+ else
+ ask_for_spec_from(specs)
+ end
+ rescue RegexpError
+ raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies)
+ end
+
+ def self.ask_for_spec_from(specs)
+ if !$stdout.tty? && ENV["BUNDLE_SPEC_RUN"].nil?
+ raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies)
+ end
+
+ specs.each_with_index do |spec, index|
+ Bundler.ui.info "#{index.succ} : #{spec.name}", true
+ end
+ Bundler.ui.info "0 : - exit -", true
+
+ num = Bundler.ui.ask("> ").to_i
+ num > 0 ? specs[num - 1] : nil
+ end
+
+ def self.gem_not_found_message(missing_gem_name, alternatives)
+ require "bundler/similarity_detector"
+ message = "Could not find gem '#{missing_gem_name}'."
+ alternate_names = alternatives.map {|a| a.respond_to?(:name) ? a.name : a }
+ suggestions = SimilarityDetector.new(alternate_names).similar_word_list(missing_gem_name)
+ message += "\nDid you mean #{suggestions}?" if suggestions
+ message
+ end
+
+ def self.ensure_all_gems_in_lockfile!(names, locked_gems = Bundler.locked_gems)
+ locked_names = locked_gems.specs.map(&:name)
+ names.-(locked_names).each do |g|
+ raise GemNotFound, gem_not_found_message(g, locked_names)
+ end
+ end
+
+ def self.configure_gem_version_promoter(definition, options)
+ patch_level = patch_level_options(options)
+ raise InvalidOption, "Provide only one of the following options: #{patch_level.join(", ")}" unless patch_level.length <= 1
+ definition.gem_version_promoter.tap do |gvp|
+ gvp.level = patch_level.first || :major
+ gvp.strict = options[:strict] || options["update-strict"]
+ end
+ end
+
+ def self.patch_level_options(options)
+ [:major, :minor, :patch].select {|v| options.keys.include?(v.to_s) }
+ end
+
+ def self.clean_after_install?
+ clean = Bundler.settings[:clean]
+ return clean unless clean.nil?
+ clean ||= Bundler.feature_flag.auto_clean_without_path? && Bundler.settings[:path].nil?
+ clean &&= !Bundler.use_system_gems?
+ clean
+ end
+ end
+end
diff --git a/lib/bundler/cli/config.rb b/lib/bundler/cli/config.rb
new file mode 100644
index 0000000000..12f71ea8fe
--- /dev/null
+++ b/lib/bundler/cli/config.rb
@@ -0,0 +1,119 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Config
+ attr_reader :name, :options, :scope, :thor
+ attr_accessor :args
+
+ def initialize(options, args, thor)
+ @options = options
+ @args = args
+ @thor = thor
+ @name = peek = args.shift
+ @scope = "global"
+ return unless peek && peek.start_with?("--")
+ @name = args.shift
+ @scope = peek[2..-1]
+ end
+
+ def run
+ unless name
+ confirm_all
+ return
+ end
+
+ unless valid_scope?(scope)
+ Bundler.ui.error "Invalid scope --#{scope} given. Please use --local or --global."
+ exit 1
+ end
+
+ if scope == "delete"
+ Bundler.settings.set_local(name, nil)
+ Bundler.settings.set_global(name, nil)
+ return
+ end
+
+ if args.empty?
+ if options[:parseable]
+ if value = Bundler.settings[name]
+ Bundler.ui.info("#{name}=#{value}")
+ end
+ return
+ end
+
+ confirm(name)
+ return
+ end
+
+ Bundler.ui.info(message) if message
+ Bundler.settings.send("set_#{scope}", name, new_value)
+ end
+
+ private
+
+ def confirm_all
+ if @options[:parseable]
+ thor.with_padding do
+ Bundler.settings.all.each do |setting|
+ val = Bundler.settings[setting]
+ Bundler.ui.info "#{setting}=#{val}"
+ end
+ end
+ else
+ Bundler.ui.confirm "Settings are listed in order of priority. The top value will be used.\n"
+ Bundler.settings.all.each do |setting|
+ Bundler.ui.confirm "#{setting}"
+ show_pretty_values_for(setting)
+ Bundler.ui.confirm ""
+ end
+ end
+ end
+
+ def confirm(name)
+ Bundler.ui.confirm "Settings for `#{name}` in order of priority. The top value will be used"
+ show_pretty_values_for(name)
+ end
+
+ def new_value
+ pathname = Pathname.new(args.join(" "))
+ if name.start_with?("local.") && pathname.directory?
+ pathname.expand_path.to_s
+ else
+ args.join(" ")
+ end
+ end
+
+ def message
+ locations = Bundler.settings.locations(name)
+ if @options[:parseable]
+ "#{name}=#{new_value}" if new_value
+ elsif scope == "global"
+ if locations[:local]
+ "Your application has set #{name} to #{locations[:local].inspect}. " \
+ "This will override the global value you are currently setting"
+ elsif locations[:env]
+ "You have a bundler environment variable for #{name} set to " \
+ "#{locations[:env].inspect}. This will take precedence over the global value you are setting"
+ elsif locations[:global] && locations[:global] != args.join(" ")
+ "You are replacing the current global value of #{name}, which is currently " \
+ "#{locations[:global].inspect}"
+ end
+ elsif scope == "local" && locations[:local] != args.join(" ")
+ "You are replacing the current local value of #{name}, which is currently " \
+ "#{locations[:local].inspect}"
+ end
+ end
+
+ def show_pretty_values_for(setting)
+ thor.with_padding do
+ Bundler.settings.pretty_values_for(setting).each do |line|
+ Bundler.ui.info line
+ end
+ end
+ end
+
+ def valid_scope?(scope)
+ %w[delete local global].include?(scope)
+ end
+ end
+end
diff --git a/lib/bundler/cli/console.rb b/lib/bundler/cli/console.rb
new file mode 100644
index 0000000000..853eca8358
--- /dev/null
+++ b/lib/bundler/cli/console.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Console
+ attr_reader :options, :group
+ def initialize(options, group)
+ @options = options
+ @group = group
+ end
+
+ def run
+ Bundler::SharedHelpers.major_deprecation 2, "bundle console will be replaced " \
+ "by `bin/console` generated by `bundle gem <name>`"
+
+ group ? Bundler.require(:default, *(group.split.map!(&:to_sym))) : Bundler.require
+ ARGV.clear
+
+ console = get_console(Bundler.settings[:console] || "irb")
+ console.start
+ end
+
+ def get_console(name)
+ require name
+ get_constant(name)
+ rescue LoadError
+ Bundler.ui.error "Couldn't load console #{name}, falling back to irb"
+ require "irb"
+ get_constant("irb")
+ end
+
+ def get_constant(name)
+ const_name = {
+ "pry" => :Pry,
+ "ripl" => :Ripl,
+ "irb" => :IRB,
+ }[name]
+ Object.const_get(const_name)
+ rescue NameError
+ Bundler.ui.error "Could not find constant #{const_name}"
+ exit 1
+ end
+ end
+end
diff --git a/lib/bundler/cli/doctor.rb b/lib/bundler/cli/doctor.rb
new file mode 100644
index 0000000000..3e0898ff8a
--- /dev/null
+++ b/lib/bundler/cli/doctor.rb
@@ -0,0 +1,140 @@
+# frozen_string_literal: true
+
+require "rbconfig"
+
+module Bundler
+ class CLI::Doctor
+ DARWIN_REGEX = /\s+(.+) \(compatibility /
+ LDD_REGEX = /\t\S+ => (\S+) \(\S+\)/
+
+ attr_reader :options
+
+ def initialize(options)
+ @options = options
+ end
+
+ def otool_available?
+ Bundler.which("otool")
+ end
+
+ def ldd_available?
+ Bundler.which("ldd")
+ end
+
+ def dylibs_darwin(path)
+ output = `/usr/bin/otool -L "#{path}"`.chomp
+ dylibs = output.split("\n")[1..-1].map {|l| l.match(DARWIN_REGEX).captures[0] }.uniq
+ # ignore @rpath and friends
+ dylibs.reject {|dylib| dylib.start_with? "@" }
+ end
+
+ def dylibs_ldd(path)
+ output = `/usr/bin/ldd "#{path}"`.chomp
+ output.split("\n").map do |l|
+ match = l.match(LDD_REGEX)
+ next if match.nil?
+ match.captures[0]
+ end.compact
+ end
+
+ def dylibs(path)
+ case RbConfig::CONFIG["host_os"]
+ when /darwin/
+ return [] unless otool_available?
+ dylibs_darwin(path)
+ when /(linux|solaris|bsd)/
+ return [] unless ldd_available?
+ dylibs_ldd(path)
+ else # Windows, etc.
+ Bundler.ui.warn("Dynamic library check not supported on this platform.")
+ []
+ end
+ end
+
+ def bundles_for_gem(spec)
+ Dir.glob("#{spec.full_gem_path}/**/*.bundle")
+ end
+
+ def check!
+ require "bundler/cli/check"
+ Bundler::CLI::Check.new({}).run
+ end
+
+ def run
+ Bundler.ui.level = "error" if options[:quiet]
+ Bundler.settings.validate!
+ check!
+
+ definition = Bundler.definition
+ broken_links = {}
+
+ definition.specs.each do |spec|
+ bundles_for_gem(spec).each do |bundle|
+ bad_paths = dylibs(bundle).select {|f| !File.exist?(f) }
+ if bad_paths.any?
+ broken_links[spec] ||= []
+ broken_links[spec].concat(bad_paths)
+ end
+ end
+ end
+
+ permissions_valid = check_home_permissions
+
+ if broken_links.any?
+ message = "The following gems are missing OS dependencies:"
+ broken_links.map do |spec, paths|
+ paths.uniq.map do |path|
+ "\n * #{spec.name}: #{path}"
+ end
+ end.flatten.sort.each {|m| message += m }
+ raise ProductionError, message
+ elsif !permissions_valid
+ Bundler.ui.info "No issues found with the installed bundle"
+ end
+ end
+
+ private
+
+ def check_home_permissions
+ require "find"
+ files_not_readable_or_writable = []
+ files_not_rw_and_owned_by_different_user = []
+ files_not_owned_by_current_user_but_still_rw = []
+ Find.find(Bundler.home.to_s).each do |f|
+ if !File.writable?(f) || !File.readable?(f)
+ if File.stat(f).uid != Process.uid
+ files_not_rw_and_owned_by_different_user << f
+ else
+ files_not_readable_or_writable << f
+ end
+ elsif File.stat(f).uid != Process.uid
+ files_not_owned_by_current_user_but_still_rw << f
+ end
+ end
+
+ ok = true
+ if files_not_owned_by_current_user_but_still_rw.any?
+ Bundler.ui.warn "Files exist in the Bundler home that are owned by another " \
+ "user, but are still readable/writable. These files are:\n - #{files_not_owned_by_current_user_but_still_rw.join("\n - ")}"
+
+ ok = false
+ end
+
+ if files_not_rw_and_owned_by_different_user.any?
+ Bundler.ui.warn "Files exist in the Bundler home that are owned by another " \
+ "user, and are not readable/writable. These files are:\n - #{files_not_rw_and_owned_by_different_user.join("\n - ")}"
+
+ ok = false
+ end
+
+ if files_not_readable_or_writable.any?
+ Bundler.ui.warn "Files exist in the Bundler home that are not " \
+ "readable/writable by the current user. These files are:\n - #{files_not_readable_or_writable.join("\n - ")}"
+
+ ok = false
+ end
+
+ ok
+ end
+ end
+end
diff --git a/lib/bundler/cli/exec.rb b/lib/bundler/cli/exec.rb
new file mode 100644
index 0000000000..c29d632307
--- /dev/null
+++ b/lib/bundler/cli/exec.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+
+require "bundler/current_ruby"
+
+module Bundler
+ class CLI::Exec
+ attr_reader :options, :args, :cmd
+
+ TRAPPED_SIGNALS = %w[INT].freeze
+
+ def initialize(options, args)
+ @options = options
+ @cmd = args.shift
+ @args = args
+
+ if Bundler.current_ruby.ruby_2? && !Bundler.current_ruby.jruby?
+ @args << { :close_others => !options.keep_file_descriptors? }
+ elsif options.keep_file_descriptors?
+ Bundler.ui.warn "Ruby version #{RUBY_VERSION} defaults to keeping non-standard file descriptors on Kernel#exec."
+ end
+ end
+
+ def run
+ validate_cmd!
+ SharedHelpers.set_bundle_environment
+ if bin_path = Bundler.which(cmd)
+ if !Bundler.settings[:disable_exec_load] && ruby_shebang?(bin_path)
+ return kernel_load(bin_path, *args)
+ end
+ # First, try to exec directly to something in PATH
+ if Bundler.current_ruby.jruby_18?
+ kernel_exec(bin_path, *args)
+ else
+ kernel_exec([bin_path, cmd], *args)
+ end
+ else
+ # exec using the given command
+ kernel_exec(cmd, *args)
+ end
+ end
+
+ private
+
+ def validate_cmd!
+ return unless cmd.nil?
+ Bundler.ui.error "bundler: exec needs a command to run"
+ exit 128
+ end
+
+ def kernel_exec(*args)
+ ui = Bundler.ui
+ Bundler.ui = nil
+ Kernel.exec(*args)
+ rescue Errno::EACCES, Errno::ENOEXEC
+ Bundler.ui = ui
+ Bundler.ui.error "bundler: not executable: #{cmd}"
+ exit 126
+ rescue Errno::ENOENT
+ Bundler.ui = ui
+ Bundler.ui.error "bundler: command not found: #{cmd}"
+ Bundler.ui.warn "Install missing gem executables with `bundle install`"
+ exit 127
+ end
+
+ def kernel_load(file, *args)
+ args.pop if args.last.is_a?(Hash)
+ ARGV.replace(args)
+ $0 = file
+ Process.setproctitle(process_title(file, args)) if Process.respond_to?(:setproctitle)
+ ui = Bundler.ui
+ Bundler.ui = nil
+ require "bundler/setup"
+ TRAPPED_SIGNALS.each {|s| trap(s, "DEFAULT") }
+ Kernel.load(file)
+ rescue SystemExit, SignalException
+ raise
+ rescue Exception => e # rubocop:disable Lint/RescueException
+ Bundler.ui = ui
+ Bundler.ui.error "bundler: failed to load command: #{cmd} (#{file})"
+ backtrace = e.backtrace ? e.backtrace.take_while {|bt| !bt.start_with?(__FILE__) } : []
+ abort "#{e.class}: #{e.message}\n #{backtrace.join("\n ")}"
+ end
+
+ def process_title(file, args)
+ "#{file} #{args.join(" ")}".strip
+ end
+
+ def ruby_shebang?(file)
+ possibilities = [
+ "#!/usr/bin/env ruby\n",
+ "#!/usr/bin/env jruby\n",
+ "#!/usr/bin/env truffleruby\n",
+ "#!#{Gem.ruby}\n",
+ ]
+
+ if File.zero?(file)
+ Bundler.ui.warn "#{file} is empty"
+ return false
+ end
+
+ first_line = File.open(file, "rb") {|f| f.read(possibilities.map(&:size).max) }
+ possibilities.any? {|shebang| first_line.start_with?(shebang) }
+ end
+ end
+end
diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb
new file mode 100644
index 0000000000..58e2f8a3fd
--- /dev/null
+++ b/lib/bundler/cli/gem.rb
@@ -0,0 +1,252 @@
+# frozen_string_literal: true
+
+require "pathname"
+
+module Bundler
+ class CLI
+ Bundler.require_thor_actions
+ include Thor::Actions
+ end
+
+ class CLI::Gem
+ TEST_FRAMEWORK_VERSIONS = {
+ "rspec" => "3.0",
+ "minitest" => "5.0"
+ }.freeze
+
+ attr_reader :options, :gem_name, :thor, :name, :target
+
+ def initialize(options, gem_name, thor)
+ @options = options
+ @gem_name = resolve_name(gem_name)
+
+ @thor = thor
+ thor.behavior = :invoke
+ thor.destination_root = nil
+
+ @name = @gem_name
+ @target = SharedHelpers.pwd.join(gem_name)
+
+ validate_ext_name if options[:ext]
+ end
+
+ def run
+ Bundler.ui.confirm "Creating gem '#{name}'..."
+
+ underscored_name = name.tr("-", "_")
+ namespaced_path = name.tr("-", "/")
+ constant_name = name.gsub(/-[_-]*(?![_-]|$)/) { "::" }.gsub(/([_-]+|(::)|^)(.|$)/) { $2.to_s + $3.upcase }
+ constant_array = constant_name.split("::")
+
+ git_installed = Bundler.git_present?
+
+ git_author_name = git_installed ? `git config user.name`.chomp : ""
+ github_username = git_installed ? `git config github.user`.chomp : ""
+ git_user_email = git_installed ? `git config user.email`.chomp : ""
+
+ config = {
+ :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,
+ :github_username => github_username.empty? ? "[USERNAME]" : github_username
+ }
+ ensure_safe_gem_name(name, constant_array)
+
+ templates = {
+ "Gemfile.tt" => "Gemfile",
+ "lib/newgem.rb.tt" => "lib/#{namespaced_path}.rb",
+ "lib/newgem/version.rb.tt" => "lib/#{namespaced_path}/version.rb",
+ "newgem.gemspec.tt" => "#{name}.gemspec",
+ "Rakefile.tt" => "Rakefile",
+ "README.md.tt" => "README.md",
+ "bin/console.tt" => "bin/console",
+ "bin/setup.tt" => "bin/setup"
+ }
+
+ executables = %w[
+ bin/console
+ bin/setup
+ ]
+
+ templates.merge!("gitignore.tt" => ".gitignore") if Bundler.git_present?
+
+ if test_framework = ask_and_set_test_framework
+ config[:test] = test_framework
+ config[:test_framework_version] = TEST_FRAMEWORK_VERSIONS[test_framework]
+
+ templates.merge!("travis.yml.tt" => ".travis.yml")
+
+ case test_framework
+ when "rspec"
+ templates.merge!(
+ "rspec.tt" => ".rspec",
+ "spec/spec_helper.rb.tt" => "spec/spec_helper.rb",
+ "spec/newgem_spec.rb.tt" => "spec/#{namespaced_path}_spec.rb"
+ )
+ when "minitest"
+ templates.merge!(
+ "test/test_helper.rb.tt" => "test/test_helper.rb",
+ "test/newgem_test.rb.tt" => "test/#{namespaced_path}_test.rb"
+ )
+ end
+ end
+
+ config[:test_task] = config[:test] == "minitest" ? "test" : "spec"
+
+ if ask_and_set(:mit, "Do you want to license your code permissively under the MIT license?",
+ "This means that any other developer or company will be legally allowed to use your code " \
+ "for free as long as they admit you created it. You can read more about the MIT license " \
+ "at https://choosealicense.com/licenses/mit.")
+ config[:mit] = true
+ Bundler.ui.info "MIT License enabled in config"
+ templates.merge!("LICENSE.txt.tt" => "LICENSE.txt")
+ end
+
+ if ask_and_set(:coc, "Do you want to include a code of conduct in gems you generate?",
+ "Codes of conduct can increase contributions to your project by contributors who " \
+ "prefer collaborative, safe spaces. You can read more about the code of conduct at " \
+ "contributor-covenant.org. Having a code of conduct means agreeing to the responsibility " \
+ "of enforcing it, so be sure that you are prepared to do that. Be sure that your email " \
+ "address is specified as a contact in the generated code of conduct so that people know " \
+ "who to contact in case of a violation. For suggestions about " \
+ "how to enforce codes of conduct, see https://bit.ly/coc-enforcement.")
+ config[:coc] = true
+ Bundler.ui.info "Code of conduct enabled in config"
+ templates.merge!("CODE_OF_CONDUCT.md.tt" => "CODE_OF_CONDUCT.md")
+ end
+
+ templates.merge!("exe/newgem.tt" => "exe/#{name}") if config[:exe]
+
+ if options[:ext]
+ templates.merge!(
+ "ext/newgem/extconf.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
+
+ templates.each do |src, dst|
+ destination = target.join(dst)
+ SharedHelpers.filesystem_access(destination) do
+ thor.template("newgem/#{src}", destination, config)
+ end
+ end
+
+ executables.each do |file|
+ SharedHelpers.filesystem_access(target.join(file)) do |path|
+ executable = (path.stat.mode | 0o111)
+ path.chmod(executable)
+ end
+ end
+
+ if Bundler.git_present?
+ Bundler.ui.info "Initializing git repo in #{target}"
+ Dir.chdir(target) do
+ `git init`
+ `git add .`
+ end
+ end
+
+ # Open gemspec in editor
+ open_editor(options["edit"], target.join("#{name}.gemspec")) if options[:edit]
+
+ Bundler.ui.info "Gem '#{name}' was successfully created. " \
+ "For more information on making a RubyGem visit https://bundler.io/guides/creating_gem.html"
+ rescue Errno::EEXIST => e
+ raise GenericSystemCallError.new(e, "There was a conflict while creating the new gem.")
+ end
+
+ private
+
+ def resolve_name(name)
+ SharedHelpers.pwd.join(name).basename.to_s
+ end
+
+ def ask_and_set(key, header, message)
+ choice = options[key]
+ choice = Bundler.settings["gem.#{key}"] if choice.nil?
+
+ if choice.nil?
+ Bundler.ui.confirm header
+ choice = Bundler.ui.yes? "#{message} y/(n):"
+ Bundler.settings.set_global("gem.#{key}", choice)
+ end
+
+ choice
+ end
+
+ def validate_ext_name
+ return unless gem_name.index("-")
+
+ Bundler.ui.error "You have specified a gem name which does not conform to the \n" \
+ "naming guidelines for C extensions. For more information, \n" \
+ "see the 'Extension Naming' section at the following URL:\n" \
+ "http://guides.rubygems.org/gems-with-extensions/\n"
+ exit 1
+ end
+
+ def ask_and_set_test_framework
+ test_framework = options[:test] || Bundler.settings["gem.test"]
+
+ if test_framework.nil?
+ Bundler.ui.confirm "Do you want to generate tests with your gem?"
+ result = Bundler.ui.ask "Type 'rspec' or 'minitest' to generate those test files now and " \
+ "in the future. rspec/minitest/(none):"
+ if result =~ /rspec|minitest/
+ test_framework = result
+ else
+ test_framework = false
+ end
+ end
+
+ if Bundler.settings["gem.test"].nil?
+ Bundler.settings.set_global("gem.test", test_framework)
+ end
+
+ test_framework
+ end
+
+ def bundler_dependency_version
+ v = Gem::Version.new(Bundler::VERSION)
+ req = v.segments[0..1]
+ req << "a" if v.prerelease?
+ req.join(".")
+ end
+
+ def ensure_safe_gem_name(name, constant_array)
+ if name =~ /^\d/
+ Bundler.ui.error "Invalid gem name #{name} Please give a name which does not start with numbers."
+ exit 1
+ end
+
+ constant_name = constant_array.join("::")
+
+ existing_constant = constant_array.inject(Object) do |c, s|
+ defined = begin
+ c.const_defined?(s)
+ rescue NameError
+ Bundler.ui.error "Invalid gem name #{name} -- `#{constant_name}` is an invalid constant name"
+ exit 1
+ end
+ (defined && c.const_get(s)) || break
+ end
+
+ return unless existing_constant
+ Bundler.ui.error "Invalid gem name #{name} constant #{constant_name} is already in use. Please choose another gem name."
+ exit 1
+ end
+
+ def open_editor(editor, file)
+ thor.run(%(#{editor} "#{file}"))
+ end
+ end
+end
diff --git a/lib/bundler/cli/info.rb b/lib/bundler/cli/info.rb
new file mode 100644
index 0000000000..958b525067
--- /dev/null
+++ b/lib/bundler/cli/info.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Info
+ attr_reader :gem_name, :options
+ def initialize(options, gem_name)
+ @options = options
+ @gem_name = gem_name
+ end
+
+ def run
+ spec = spec_for_gem(gem_name)
+
+ spec_not_found(gem_name) unless spec
+ return print_gem_path(spec) if @options[:path]
+ print_gem_info(spec)
+ end
+
+ private
+
+ def spec_for_gem(gem_name)
+ spec = Bundler.definition.specs.find {|s| s.name == gem_name }
+ spec || default_gem_spec(gem_name)
+ end
+
+ 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?
+ end
+
+ def spec_not_found(gem_name)
+ raise GemNotFound, Bundler::CLI::Common.gem_not_found_message(gem_name, Bundler.definition.dependencies)
+ end
+
+ def print_gem_path(spec)
+ Bundler.ui.info spec.full_gem_path
+ end
+
+ def print_gem_info(spec)
+ gem_info = String.new
+ gem_info << " * #{spec.name} (#{spec.version}#{spec.git_version})\n"
+ gem_info << "\tSummary: #{spec.summary}\n" if spec.summary
+ gem_info << "\tHomepage: #{spec.homepage}\n" if spec.homepage
+ gem_info << "\tPath: #{spec.full_gem_path}\n"
+ gem_info << "\tDefault Gem: yes" if spec.respond_to?(:default_gem?) && spec.default_gem?
+ Bundler.ui.info gem_info
+ end
+ end
+end
diff --git a/lib/bundler/cli/init.rb b/lib/bundler/cli/init.rb
new file mode 100644
index 0000000000..40df797269
--- /dev/null
+++ b/lib/bundler/cli/init.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Init
+ attr_reader :options
+ def initialize(options)
+ @options = options
+ end
+
+ def run
+ if File.exist?(gemfile)
+ Bundler.ui.error "#{gemfile} already exists at #{File.expand_path(gemfile)}"
+ exit 1
+ end
+
+ unless File.writable?(Dir.pwd)
+ Bundler.ui.error "Can not create #{gemfile} as the current directory is not writable."
+ exit 1
+ end
+
+ if options[:gemspec]
+ gemspec = File.expand_path(options[:gemspec])
+ unless File.exist?(gemspec)
+ Bundler.ui.error "Gem specification #{gemspec} doesn't exist"
+ exit 1
+ end
+
+ spec = Bundler.load_gemspec_uncached(gemspec)
+
+ File.open(gemfile, "wb") do |file|
+ file << "# Generated from #{gemspec}\n"
+ file << spec.to_gemfile
+ end
+ else
+ FileUtils.cp(File.expand_path("../../templates/#{gemfile}", __FILE__), gemfile)
+ end
+
+ puts "Writing new #{gemfile} to #{SharedHelpers.pwd}/#{gemfile}"
+ end
+
+ private
+
+ def gemfile
+ @gemfile ||= Bundler.feature_flag.init_gems_rb? ? "gems.rb" : "Gemfile"
+ end
+ end
+end
diff --git a/lib/bundler/cli/inject.rb b/lib/bundler/cli/inject.rb
new file mode 100644
index 0000000000..b00675d348
--- /dev/null
+++ b/lib/bundler/cli/inject.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Inject
+ attr_reader :options, :name, :version, :group, :source, :gems
+ def initialize(options, name, version)
+ @options = options
+ @name = name
+ @version = version || last_version_number
+ @group = options[:group].split(",") unless options[:group].nil?
+ @source = options[:source]
+ @gems = []
+ end
+
+ def run
+ # The required arguments allow Thor to give useful feedback when the arguments
+ # are incorrect. This adds those first two arguments onto the list as a whole.
+ gems.unshift(source).unshift(group).unshift(version).unshift(name)
+
+ # Build an array of Dependency objects out of the arguments
+ deps = []
+ # when `inject` support addition of more than one gem, then this loop will
+ # help. Currently this loop is running once.
+ gems.each_slice(4) do |gem_name, gem_version, gem_group, gem_source|
+ ops = Gem::Requirement::OPS.map {|key, _val| key }
+ has_op = ops.any? {|op| gem_version.start_with? op }
+ gem_version = "~> #{gem_version}" unless has_op
+ deps << Bundler::Dependency.new(gem_name, gem_version, "group" => gem_group, "source" => gem_source)
+ end
+
+ added = Injector.inject(deps, options)
+
+ if added.any?
+ Bundler.ui.confirm "Added to Gemfile:"
+ Bundler.ui.confirm(added.map do |d|
+ name = "'#{d.name}'"
+ requirement = ", '#{d.requirement}'"
+ group = ", :group => #{d.groups.inspect}" if d.groups != Array(:default)
+ source = ", :source => '#{d.source}'" unless d.source.nil?
+ %(gem #{name}#{requirement}#{group}#{source})
+ end.join("\n"))
+ else
+ Bundler.ui.confirm "All gems were already present in the Gemfile"
+ end
+ end
+
+ private
+
+ def last_version_number
+ definition = Bundler.definition(true)
+ definition.resolve_remotely!
+ specs = definition.index[name].sort_by(&:version)
+ unless options[:pre]
+ specs.delete_if {|b| b.respond_to?(:version) && b.version.prerelease? }
+ end
+ spec = specs.last
+ spec.version.to_s
+ end
+ end
+end
diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb
new file mode 100644
index 0000000000..b40e5f0e9e
--- /dev/null
+++ b/lib/bundler/cli/install.rb
@@ -0,0 +1,217 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Install
+ attr_reader :options
+ def initialize(options)
+ @options = options
+ end
+
+ def run
+ Bundler.ui.level = "error" if options[:quiet]
+
+ warn_if_root
+
+ normalize_groups
+
+ Bundler::SharedHelpers.set_env "RB_USER_INSTALL", "1" if Bundler::FREEBSD
+
+ # Disable color in deployment mode
+ Bundler.ui.shell = Thor::Shell::Basic.new if options[:deployment]
+
+ check_for_options_conflicts
+
+ check_trust_policy
+
+ if options[:deployment] || options[:frozen] || Bundler.frozen_bundle?
+ unless Bundler.default_lockfile.exist?
+ flag = "--deployment flag" if options[:deployment]
+ flag ||= "--frozen flag" if options[:frozen]
+ flag ||= "deployment setting"
+ raise ProductionError, "The #{flag} requires a #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}. Please make " \
+ "sure you have checked your #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} into version control " \
+ "before deploying."
+ end
+
+ options[:local] = true if Bundler.app_cache.exist?
+
+ if Bundler.feature_flag.deployment_means_frozen?
+ Bundler.settings.set_command_option :deployment, true
+ else
+ Bundler.settings.set_command_option :frozen, true
+ end
+ end
+
+ # When install is called with --no-deployment, disable deployment mode
+ if options[:deployment] == false
+ Bundler.settings.set_command_option :frozen, nil
+ options[:system] = true
+ end
+
+ normalize_settings
+
+ Bundler::Fetcher.disable_endpoint = options["full-index"]
+
+ if options["binstubs"]
+ Bundler::SharedHelpers.major_deprecation 2,
+ "The --binstubs option will be removed in favor of `bundle binstubs`"
+ end
+
+ Plugin.gemfile_install(Bundler.default_gemfile) if Bundler.feature_flag.plugins?
+
+ definition = Bundler.definition
+ definition.validate_runtime!
+
+ installer = Installer.install(Bundler.root, definition, options)
+ Bundler.load.cache if Bundler.app_cache.exist? && !options["no-cache"] && !Bundler.frozen_bundle?
+
+ Bundler.ui.confirm "Bundle complete! #{dependencies_count_for(definition)}, #{gems_installed_for(definition)}."
+ Bundler::CLI::Common.output_without_groups_message
+
+ if Bundler.use_system_gems?
+ Bundler.ui.confirm "Use `bundle info [gemname]` to see where a bundled gem is installed."
+ else
+ relative_path = Bundler.configured_bundle_path.base_path_relative_to_pwd
+ Bundler.ui.confirm "Bundled gems are installed into `#{relative_path}`"
+ end
+
+ Bundler::CLI::Common.output_post_install_messages installer.post_install_messages
+
+ warn_ambiguous_gems
+
+ if CLI::Common.clean_after_install?
+ require "bundler/cli/clean"
+ Bundler::CLI::Clean.new(options).run
+ end
+ rescue GemNotFound, VersionConflict => e
+ if options[:local] && Bundler.app_cache.exist?
+ Bundler.ui.warn "Some gems seem to be missing from your #{Bundler.settings.app_cache_path} directory."
+ end
+
+ unless Bundler.definition.has_rubygems_remotes?
+ Bundler.ui.warn <<-WARN, :wrap => true
+ Your Gemfile has no gem server sources. If you need gems that are \
+ not already on your machine, add a line like this to your Gemfile:
+ source 'https://rubygems.org'
+ WARN
+ end
+ raise e
+ rescue Gem::InvalidSpecificationException => e
+ Bundler.ui.warn "You have one or more invalid gemspecs that need to be fixed."
+ raise e
+ end
+
+ private
+
+ def warn_if_root
+ return if Bundler.settings[:silence_root_warning] || Bundler::WINDOWS || !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
+ end
+
+ def dependencies_count_for(definition)
+ count = definition.dependencies.count
+ "#{count} Gemfile #{count == 1 ? "dependency" : "dependencies"}"
+ end
+
+ def gems_installed_for(definition)
+ count = definition.specs.count
+ "#{count} #{count == 1 ? "gem" : "gems"} now installed"
+ end
+
+ def check_for_group_conflicts_in_cli_options
+ conflicting_groups = Array(options[:without]) & Array(options[:with])
+ return if conflicting_groups.empty?
+ raise InvalidOption, "You can't list a group in both with and without." \
+ " The offending groups are: #{conflicting_groups.join(", ")}."
+ end
+
+ def check_for_options_conflicts
+ if (options[:path] || options[:deployment]) && options[:system]
+ error_message = String.new
+ error_message << "You have specified both --path as well as --system. Please choose only one option.\n" if options[:path]
+ error_message << "You have specified both --deployment as well as --system. Please choose only one option.\n" if options[:deployment]
+ raise InvalidOption.new(error_message)
+ end
+ end
+
+ def check_trust_policy
+ trust_policy = options["trust-policy"]
+ unless Bundler.rubygems.security_policies.keys.unshift(nil).include?(trust_policy)
+ raise InvalidOption, "RubyGems doesn't know about trust policy '#{trust_policy}'. " \
+ "The known policies are: #{Bundler.rubygems.security_policies.keys.join(", ")}."
+ end
+ Bundler.settings.set_command_option_if_given :"trust-policy", trust_policy
+ end
+
+ def normalize_groups
+ options[:with] &&= options[:with].join(":").tr(" ", ":").split(":")
+ options[:without] &&= options[:without].join(":").tr(" ", ":").split(":")
+
+ check_for_group_conflicts_in_cli_options
+
+ Bundler.settings.set_command_option :with, nil if options[:with] == []
+ Bundler.settings.set_command_option :without, nil if options[:without] == []
+
+ with = options.fetch(:with, [])
+ with |= Bundler.settings[:with].map(&:to_s)
+ with -= options[:without] if options[:without]
+
+ without = options.fetch(:without, [])
+ without |= Bundler.settings[:without].map(&:to_s)
+ without -= options[:with] if options[:with]
+
+ options[:with] = with
+ options[:without] = without
+ end
+
+ def normalize_settings
+ Bundler.settings.set_command_option :path, nil if options[:system]
+ Bundler.settings.temporary(:path_relative_to_cwd => false) do
+ Bundler.settings.set_command_option :path, "vendor/bundle" if options[:deployment]
+ end
+ 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?
+ end
+
+ bin_option = options["binstubs"]
+ bin_option = nil if bin_option && bin_option.empty?
+ Bundler.settings.set_command_option :bin, bin_option if options["binstubs"]
+
+ Bundler.settings.set_command_option_if_given :shebang, options["shebang"]
+
+ Bundler.settings.set_command_option_if_given :jobs, options["jobs"]
+
+ Bundler.settings.set_command_option_if_given :no_prune, options["no-prune"]
+
+ Bundler.settings.set_command_option_if_given :no_install, options["no-install"]
+
+ Bundler.settings.set_command_option_if_given :clean, options["clean"]
+
+ unless Bundler.settings[:without] == options[:without] && Bundler.settings[:with] == options[:with]
+ # need to nil them out first to get around validation for backwards compatibility
+ Bundler.settings.set_command_option :without, nil
+ Bundler.settings.set_command_option :with, nil
+ Bundler.settings.set_command_option :without, options[:without] - options[:with]
+ Bundler.settings.set_command_option :with, options[:with]
+ end
+
+ options[:force] = options[:redownload]
+ end
+
+ def warn_ambiguous_gems
+ Installer.ambiguous_gems.to_a.each do |name, installed_from_uri, *also_found_in_uris|
+ Bundler.ui.error "Warning: the gem '#{name}' was found in multiple sources."
+ Bundler.ui.error "Installed from: #{installed_from_uri}"
+ Bundler.ui.error "Also found in:"
+ also_found_in_uris.each {|uri| Bundler.ui.error " * #{uri}" }
+ Bundler.ui.error "You should add a source requirement to restrict this gem to your preferred source."
+ Bundler.ui.error "For example:"
+ Bundler.ui.error " gem '#{name}', :source => '#{installed_from_uri}'"
+ Bundler.ui.error "Then uninstall the gem '#{name}' (or delete all bundled gems) and then install again."
+ end
+ end
+ end
+end
diff --git a/lib/bundler/cli/issue.rb b/lib/bundler/cli/issue.rb
new file mode 100644
index 0000000000..91f827ea99
--- /dev/null
+++ b/lib/bundler/cli/issue.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require "rbconfig"
+
+module Bundler
+ class CLI::Issue
+ def run
+ Bundler.ui.info <<-EOS.gsub(/^ {8}/, "")
+ Did you find an issue with Bundler? Before filing a new issue,
+ be sure to check out these resources:
+
+ 1. Check out our troubleshooting guide for quick fixes to common issues:
+ https://github.com/bundler/bundler/blob/master/doc/TROUBLESHOOTING.md
+
+ 2. Instructions for common Bundler uses can be found on the documentation
+ site: http://bundler.io/
+
+ 3. Information about each Bundler command can be found in the Bundler
+ man pages: http://bundler.io/man/bundle.1.html
+
+ Hopefully the troubleshooting steps above resolved your problem! If things
+ still aren't working the way you expect them to, please let us know so
+ that we can diagnose and help fix the problem you're having. Please
+ view the Filing Issues guide for more information:
+ https://github.com/bundler/bundler/blob/master/doc/contributing/ISSUES.md
+
+ EOS
+
+ Bundler.ui.info Bundler::Env.report
+
+ Bundler.ui.info "\n## Bundle Doctor"
+ doctor
+ end
+
+ def doctor
+ require "bundler/cli/doctor"
+ Bundler::CLI::Doctor.new({}).run
+ end
+ end
+end
diff --git a/lib/bundler/cli/list.rb b/lib/bundler/cli/list.rb
new file mode 100644
index 0000000000..d1799196e7
--- /dev/null
+++ b/lib/bundler/cli/list.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::List
+ def initialize(options)
+ @options = options
+ end
+
+ def run
+ raise InvalidOption, "The `--only-group` and `--without-group` options cannot be used together" if @options["only-group"] && @options["without-group"]
+
+ raise InvalidOption, "The `--name-only` and `--paths` options cannot be used together" if @options["name-only"] && @options[:paths]
+
+ specs = if @options["only-group"] || @options["without-group"]
+ filtered_specs_by_groups
+ else
+ Bundler.load.specs
+ end.reject {|s| s.name == "bundler" }.sort_by(&:name)
+
+ return Bundler.ui.info "No gems in the Gemfile" if specs.empty?
+
+ return specs.each {|s| Bundler.ui.info s.name } if @options["name-only"]
+ return specs.each {|s| Bundler.ui.info s.full_gem_path } if @options["paths"]
+
+ Bundler.ui.info "Gems included by the bundle:"
+
+ specs.each {|s| Bundler.ui.info " * #{s.name} (#{s.version}#{s.git_version})" }
+
+ Bundler.ui.info "Use `bundle info` to print more detailed information about a gem"
+ end
+
+ private
+
+ def verify_group_exists(groups)
+ raise InvalidOption, "`#{@options["without-group"]}` group could not be found." if @options["without-group"] && !groups.include?(@options["without-group"].to_sym)
+
+ raise InvalidOption, "`#{@options["only-group"]}` group could not be found." if @options["only-group"] && !groups.include?(@options["only-group"].to_sym)
+ end
+
+ def filtered_specs_by_groups
+ definition = Bundler.definition
+ groups = definition.groups
+
+ verify_group_exists(groups)
+
+ show_groups =
+ if @options["without-group"]
+ groups.reject {|g| g == @options["without-group"].to_sym }
+ elsif @options["only-group"]
+ groups.select {|g| g == @options["only-group"].to_sym }
+ else
+ groups
+ end.map(&:to_sym)
+
+ definition.specs_for(show_groups)
+ end
+ end
+end
diff --git a/lib/bundler/cli/lock.rb b/lib/bundler/cli/lock.rb
new file mode 100644
index 0000000000..7dd078b1ef
--- /dev/null
+++ b/lib/bundler/cli/lock.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Lock
+ attr_reader :options
+
+ def initialize(options)
+ @options = options
+ end
+
+ def run
+ unless Bundler.default_gemfile
+ Bundler.ui.error "Unable to find a Gemfile to lock"
+ exit 1
+ end
+
+ print = options[:print]
+ ui = Bundler.ui
+ Bundler.ui = UI::Silent.new if print
+
+ Bundler::Fetcher.disable_endpoint = options["full-index"]
+
+ update = options[:update]
+ if update.is_a?(Array) # unlocking specific gems
+ Bundler::CLI::Common.ensure_all_gems_in_lockfile!(update)
+ update = { :gems => update, :lock_shared_dependencies => options[:conservative] }
+ end
+ definition = Bundler.definition(update)
+
+ Bundler::CLI::Common.configure_gem_version_promoter(Bundler.definition, options) if options[:update]
+
+ options["remove-platform"].each do |platform|
+ definition.remove_platform(platform)
+ end
+
+ options["add-platform"].each do |platform_string|
+ platform = Gem::Platform.new(platform_string)
+ if platform.to_s == "unknown"
+ Bundler.ui.warn "The platform `#{platform_string}` is unknown to RubyGems " \
+ "and adding it will likely lead to resolution errors"
+ end
+ definition.add_platform(platform)
+ end
+
+ if definition.platforms.empty?
+ raise InvalidOption, "Removing all platforms from the bundle is not allowed"
+ end
+
+ definition.resolve_remotely! unless options[:local]
+
+ if print
+ puts definition.to_lock
+ else
+ file = options[:lockfile]
+ file = file ? File.expand_path(file) : Bundler.default_lockfile
+ puts "Writing lockfile to #{file}"
+ definition.lock(file)
+ end
+
+ Bundler.ui = ui
+ end
+ end
+end
diff --git a/lib/bundler/cli/open.rb b/lib/bundler/cli/open.rb
new file mode 100644
index 0000000000..552fe6f128
--- /dev/null
+++ b/lib/bundler/cli/open.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require "shellwords"
+
+module Bundler
+ class CLI::Open
+ attr_reader :options, :name
+ def initialize(options, name)
+ @options = options
+ @name = name
+ end
+
+ def run
+ 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)
+ path = spec.full_gem_path
+ Dir.chdir(path) do
+ command = Shellwords.split(editor) + [path]
+ Bundler.with_original_env do
+ system(*command)
+ end || Bundler.ui.info("Could not run '#{command.join(" ")}'")
+ end
+ end
+ end
+end
diff --git a/lib/bundler/cli/outdated.rb b/lib/bundler/cli/outdated.rb
new file mode 100644
index 0000000000..2ca90293db
--- /dev/null
+++ b/lib/bundler/cli/outdated.rb
@@ -0,0 +1,266 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Outdated
+ attr_reader :options, :gems
+
+ def initialize(options, gems)
+ @options = options
+ @gems = gems
+ end
+
+ def run
+ check_for_deployment_mode
+
+ sources = Array(options[:source])
+
+ gems.each do |gem_name|
+ Bundler::CLI::Common.select_spec(gem_name)
+ end
+
+ Bundler.definition.validate_runtime!
+ current_specs = Bundler.ui.silence { Bundler.definition.resolve }
+ current_dependencies = {}
+ Bundler.ui.silence do
+ Bundler.load.dependencies.each do |dep|
+ current_dependencies[dep.name] = dep
+ end
+ end
+
+ definition = if gems.empty? && sources.empty?
+ # We're doing a full update
+ Bundler.definition(true)
+ else
+ Bundler.definition(:gems => gems, :sources => sources)
+ end
+
+ Bundler::CLI::Common.configure_gem_version_promoter(
+ Bundler.definition,
+ options
+ )
+
+ # the patch level options imply strict is also true. It wouldn't make
+ # sense otherwise.
+ strict = options[:strict] ||
+ Bundler::CLI::Common.patch_level_options(options).any?
+
+ filter_options_patch = options.keys &
+ %w[filter-major filter-minor filter-patch]
+
+ definition_resolution = proc do
+ options[:local] ? definition.resolve_with_cache! : definition.resolve_remotely!
+ end
+
+ if options[:parseable]
+ Bundler.ui.silence(&definition_resolution)
+ else
+ definition_resolution.call
+ end
+
+ Bundler.ui.info ""
+ outdated_gems_by_groups = {}
+ outdated_gems_list = []
+
+ # Loop through the current specs
+ gemfile_specs, dependency_specs = current_specs.partition do |spec|
+ current_dependencies.key? spec.name
+ end
+
+ specs = if options["only-explicit"]
+ gemfile_specs
+ else
+ gemfile_specs + dependency_specs
+ end
+
+ specs.sort_by(&:name).each do |current_spec|
+ next if !gems.empty? && !gems.include?(current_spec.name)
+
+ dependency = current_dependencies[current_spec.name]
+ active_spec = retrieve_active_spec(strict, definition, current_spec)
+
+ next if active_spec.nil?
+ if filter_options_patch.any?
+ update_present = update_present_via_semver_portions(current_spec, active_spec, options)
+ next unless update_present
+ end
+
+ gem_outdated = Gem::Version.new(active_spec.version) > Gem::Version.new(current_spec.version)
+ next unless gem_outdated || (current_spec.git_version != active_spec.git_version)
+ groups = nil
+ if dependency && !options[:parseable]
+ groups = dependency.groups.join(", ")
+ end
+
+ outdated_gems_list << { :active_spec => active_spec,
+ :current_spec => current_spec,
+ :dependency => dependency,
+ :groups => groups }
+
+ outdated_gems_by_groups[groups] ||= []
+ outdated_gems_by_groups[groups] << { :active_spec => active_spec,
+ :current_spec => current_spec,
+ :dependency => dependency,
+ :groups => groups }
+ end
+
+ if outdated_gems_list.empty?
+ display_nothing_outdated_message(filter_options_patch)
+ else
+ unless options[:parseable]
+ if options[:pre]
+ Bundler.ui.info "Outdated gems included in the bundle (including " \
+ "pre-releases):"
+ else
+ Bundler.ui.info "Outdated gems included in the bundle:"
+ end
+ end
+
+ options_include_groups = [:group, :groups].select do |v|
+ options.keys.include?(v.to_s)
+ end
+
+ if options_include_groups.any?
+ ordered_groups = outdated_gems_by_groups.keys.compact.sort
+ [nil, ordered_groups].flatten.each do |groups|
+ gems = outdated_gems_by_groups[groups]
+ contains_group = if groups
+ groups.split(",").include?(options[:group])
+ else
+ options[:group] == "group"
+ end
+
+ next if (!options[:groups] && !contains_group) || gems.nil?
+
+ unless options[:parseable]
+ if groups
+ Bundler.ui.info "===== Group #{groups} ====="
+ else
+ Bundler.ui.info "===== Without group ====="
+ end
+ end
+
+ gems.each do |gem|
+ print_gem(
+ gem[:current_spec],
+ gem[:active_spec],
+ gem[:dependency],
+ groups,
+ options_include_groups.any?
+ )
+ end
+ end
+ else
+ outdated_gems_list.each do |gem|
+ print_gem(
+ gem[:current_spec],
+ gem[:active_spec],
+ gem[:dependency],
+ gem[:groups],
+ options_include_groups.any?
+ )
+ end
+ end
+
+ exit 1
+ end
+ end
+
+ private
+
+ def retrieve_active_spec(strict, definition, current_spec)
+ if strict
+ active_spec = definition.find_resolved_spec(current_spec)
+ else
+ active_specs = definition.find_indexed_specs(current_spec)
+ if !current_spec.version.prerelease? && !options[:pre] && active_specs.size > 1
+ active_specs.delete_if {|b| b.respond_to?(:version) && b.version.prerelease? }
+ end
+ active_spec = active_specs.last
+ end
+
+ active_spec
+ end
+
+ def display_nothing_outdated_message(filter_options_patch)
+ unless options[:parseable]
+ if filter_options_patch.any?
+ display = filter_options_patch.map do |o|
+ o.sub("filter-", "")
+ end.join(" or ")
+
+ Bundler.ui.info "No #{display} updates to display.\n"
+ else
+ Bundler.ui.info "Bundle up to date!\n"
+ end
+ end
+ end
+
+ def print_gem(current_spec, active_spec, dependency, groups, options_include_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
+ current_version = "#{current_spec.version}#{current_spec.git_version}"
+
+ if dependency && dependency.specific?
+ dependency_version = %(, requested #{dependency.requirement})
+ end
+
+ spec_outdated_info = "#{active_spec.name} (newest #{spec_version}, " \
+ "installed #{current_version}#{dependency_version})"
+
+ output_message = if options[:parseable]
+ spec_outdated_info.to_s
+ elsif options_include_groups || !groups
+ " * #{spec_outdated_info}"
+ else
+ " * #{spec_outdated_info} in groups \"#{groups}\""
+ end
+
+ Bundler.ui.info output_message.rstrip
+ end
+
+ def check_for_deployment_mode
+ return unless Bundler.frozen_bundle?
+ suggested_command = if Bundler.settings.locations("frozen")[:global]
+ "bundle config --delete frozen"
+ elsif Bundler.settings.locations("deployment").keys.&([:global, :local]).any?
+ "bundle config --delete deployment"
+ else
+ "bundle install --no-deployment"
+ end
+ raise ProductionError, "You are trying to check outdated gems in " \
+ "deployment mode. Run `bundle outdated` elsewhere.\n" \
+ "\nIf this is a development machine, remove the " \
+ "#{Bundler.default_gemfile} freeze" \
+ "\nby running `#{suggested_command}`."
+ end
+
+ def update_present_via_semver_portions(current_spec, active_spec, options)
+ current_major = current_spec.version.segments.first
+ active_major = active_spec.version.segments.first
+
+ update_present = false
+ update_present = active_major > current_major if options["filter-major"]
+
+ if !update_present && (options["filter-minor"] || options["filter-patch"]) && current_major == active_major
+ current_minor = get_version_semver_portion_value(current_spec, 1)
+ active_minor = get_version_semver_portion_value(active_spec, 1)
+
+ update_present = active_minor > current_minor if options["filter-minor"]
+
+ if !update_present && options["filter-patch"] && current_minor == active_minor
+ current_patch = get_version_semver_portion_value(current_spec, 2)
+ active_patch = get_version_semver_portion_value(active_spec, 2)
+
+ update_present = active_patch > current_patch
+ end
+ end
+
+ update_present
+ end
+
+ def get_version_semver_portion_value(spec, version_portion_index)
+ version_section = spec.version.segments[version_portion_index, 1]
+ version_section.nil? ? 0 : (version_section.first || 0)
+ end
+ end
+end
diff --git a/lib/bundler/cli/package.rb b/lib/bundler/cli/package.rb
new file mode 100644
index 0000000000..2dcd0e1e29
--- /dev/null
+++ b/lib/bundler/cli/package.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Package
+ attr_reader :options
+
+ def initialize(options)
+ @options = options
+ end
+
+ def run
+ Bundler.ui.level = "error" if options[:quiet]
+ Bundler.settings.set_command_option_if_given :path, options[:path]
+ Bundler.settings.set_command_option_if_given :cache_all_platforms, options["all-platforms"]
+ Bundler.settings.set_command_option_if_given :cache_path, options["cache-path"]
+
+ setup_cache_all
+ install
+
+ # TODO: move cache contents here now that all bundles are locked
+ custom_path = Bundler.settings[:path] if options[:path]
+ Bundler.load.cache(custom_path)
+ end
+
+ private
+
+ def install
+ require "bundler/cli/install"
+ options = self.options.dup
+ if Bundler.settings[:cache_all_platforms]
+ options["local"] = false
+ options["update"] = true
+ end
+ Bundler::CLI::Install.new(options).run
+ end
+
+ def setup_cache_all
+ all = options.fetch(:all, Bundler.feature_flag.cache_command_is_package? || nil)
+
+ Bundler.settings.set_command_option_if_given :cache_all, all
+
+ if Bundler.definition.has_local_dependencies? && !Bundler.feature_flag.cache_all?
+ Bundler.ui.warn "Your Gemfile contains path and git dependencies. If you want " \
+ "to package them as well, please pass the --all flag. This will be the default " \
+ "on Bundler 2.0."
+ end
+ end
+ end
+end
diff --git a/lib/bundler/cli/platform.rb b/lib/bundler/cli/platform.rb
new file mode 100644
index 0000000000..e97cad49a4
--- /dev/null
+++ b/lib/bundler/cli/platform.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Platform
+ attr_reader :options
+ def initialize(options)
+ @options = options
+ 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]
+ end
+ output = []
+
+ if options[:ruby]
+ if ruby_version
+ output << ruby_version
+ else
+ output << "No ruby version specified"
+ end
+ else
+ output << "Your platform is: #{RUBY_PLATFORM}"
+ output << "Your app has gems that work on these platforms:\n#{platforms.join("\n")}"
+
+ if ruby_version
+ output << "Your Gemfile specifies a Ruby version requirement:\n* #{ruby_version}"
+
+ begin
+ Bundler.definition.validate_runtime!
+ output << "Your current platform satisfies the Ruby version requirement."
+ rescue RubyVersionMismatch => e
+ output << e.message
+ end
+ else
+ output << "Your Gemfile does not specify a Ruby version requirement."
+ end
+ end
+
+ Bundler.ui.info output.join("\n\n")
+ end
+ end
+end
diff --git a/lib/bundler/cli/plugin.rb b/lib/bundler/cli/plugin.rb
new file mode 100644
index 0000000000..5488a9f28d
--- /dev/null
+++ b/lib/bundler/cli/plugin.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require "bundler/vendored_thor"
+module Bundler
+ class CLI::Plugin < Thor
+ desc "install PLUGINS", "Install the plugin from the source"
+ long_desc <<-D
+ Install plugins either from the rubygems source provided (with --source option) or from a git source provided with (--git option). If no sources are provided, it uses Gem.sources
+ D
+ method_option "source", :type => :string, :default => nil, :banner =>
+ "URL of the RubyGems source to fetch the plugin from"
+ method_option "version", :type => :string, :default => nil, :banner =>
+ "The version of the plugin to fetch"
+ method_option "git", :type => :string, :default => nil, :banner =>
+ "URL of the git repo to fetch from"
+ method_option "branch", :type => :string, :default => nil, :banner =>
+ "The git branch to checkout"
+ method_option "ref", :type => :string, :default => nil, :banner =>
+ "The git revision to check out"
+ def install(*plugins)
+ Bundler::Plugin.install(plugins, options)
+ end
+ end
+end
diff --git a/lib/bundler/cli/pristine.rb b/lib/bundler/cli/pristine.rb
new file mode 100644
index 0000000000..4a411a83fc
--- /dev/null
+++ b/lib/bundler/cli/pristine.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Pristine
+ def initialize(gems)
+ @gems = gems
+ end
+
+ def run
+ CLI::Common.ensure_all_gems_in_lockfile!(@gems)
+ definition = Bundler.definition
+ definition.validate_runtime!
+ installer = Bundler::Installer.new(Bundler.root, definition)
+
+ Bundler.load.specs.each do |spec|
+ next if spec.name == "bundler" # Source::Rubygems doesn't install bundler
+ next if !@gems.empty? && !@gems.include?(spec.name)
+
+ gem_name = "#{spec.name} (#{spec.version}#{spec.git_version})"
+ gem_name += " (#{spec.platform})" if !spec.platform.nil? && spec.platform != Gem::Platform::RUBY
+
+ case source = spec.source
+ when Source::Rubygems
+ cached_gem = spec.cache_file
+ unless File.exist?(cached_gem)
+ Bundler.ui.error("Failed to pristine #{gem_name}. Cached gem #{cached_gem} does not exist.")
+ next
+ end
+
+ FileUtils.rm_rf spec.full_gem_path
+ when Source::Git
+ source.remote!
+ if extension_cache_path = source.extension_cache_path(spec)
+ FileUtils.rm_rf extension_cache_path
+ end
+ FileUtils.rm_rf spec.extension_dir if spec.respond_to?(:extension_dir)
+ FileUtils.rm_rf spec.full_gem_path
+ else
+ Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is sourced from local path.")
+ next
+ end
+
+ Bundler::GemInstaller.new(spec, installer, false, 0, true).install_from_spec
+ end
+ end
+ end
+end
diff --git a/lib/bundler/cli/remove.rb b/lib/bundler/cli/remove.rb
new file mode 100644
index 0000000000..cd6a2cec28
--- /dev/null
+++ b/lib/bundler/cli/remove.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Remove
+ def initialize(gems, options)
+ @gems = gems
+ @options = options
+ end
+
+ def run
+ raise InvalidOption, "Please specify gems to remove." if @gems.empty?
+
+ Injector.remove(@gems, {})
+
+ Installer.install(Bundler.root, Bundler.definition) if @options["install"]
+ end
+ end
+end
diff --git a/lib/bundler/cli/show.rb b/lib/bundler/cli/show.rb
new file mode 100644
index 0000000000..61756801b2
--- /dev/null
+++ b/lib/bundler/cli/show.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Show
+ attr_reader :options, :gem_name, :latest_specs
+ def initialize(options, gem_name)
+ @options = options
+ @gem_name = gem_name
+ @verbose = options[:verbose] || options[:outdated]
+ @latest_specs = fetch_latest_specs if @verbose
+ end
+
+ def run
+ Bundler.ui.silence do
+ Bundler.definition.validate_runtime!
+ Bundler.load.lock
+ end
+
+ if gem_name
+ if gem_name == "bundler"
+ path = File.expand_path("../../../..", __FILE__)
+ else
+ spec = Bundler::CLI::Common.select_spec(gem_name, :regex_match)
+ return unless spec
+ path = spec.full_gem_path
+ unless File.directory?(path)
+ Bundler.ui.warn "The gem #{gem_name} has been deleted. It was installed at:"
+ end
+ end
+ return Bundler.ui.info(path)
+ end
+
+ if options[:paths]
+ Bundler.load.specs.sort_by(&:name).map do |s|
+ Bundler.ui.info s.full_gem_path
+ end
+ else
+ Bundler.ui.info "Gems included by the bundle:"
+ Bundler.load.specs.sort_by(&:name).each do |s|
+ desc = " * #{s.name} (#{s.version}#{s.git_version})"
+ if @verbose
+ latest = latest_specs.find {|l| l.name == s.name }
+ Bundler.ui.info <<-END.gsub(/^ +/, "")
+ #{desc}
+ \tSummary: #{s.summary || "No description available."}
+ \tHomepage: #{s.homepage || "No website available."}
+ \tStatus: #{outdated?(s, latest) ? "Outdated - #{s.version} < #{latest.version}" : "Up to date"}
+ END
+ else
+ Bundler.ui.info desc
+ end
+ end
+ end
+ end
+
+ private
+
+ def fetch_latest_specs
+ definition = Bundler.definition(true)
+ if options[:outdated]
+ Bundler.ui.info "Fetching remote specs for outdated check...\n\n"
+ Bundler.ui.silence { definition.resolve_remotely! }
+ else
+ definition.resolve_with_cache!
+ end
+ Bundler.reset!
+ definition.specs
+ end
+
+ def outdated?(current, latest)
+ return false unless latest
+ Gem::Version.new(current.version) < Gem::Version.new(latest.version)
+ end
+ end
+end
diff --git a/lib/bundler/cli/update.rb b/lib/bundler/cli/update.rb
new file mode 100644
index 0000000000..b088853768
--- /dev/null
+++ b/lib/bundler/cli/update.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Update
+ attr_reader :options, :gems
+ def initialize(options, gems)
+ @options = options
+ @gems = gems
+ end
+
+ def run
+ Bundler.ui.level = "error" if options[:quiet]
+
+ Plugin.gemfile_install(Bundler.default_gemfile) if Bundler.feature_flag.plugins?
+
+ sources = Array(options[:source])
+ groups = Array(options[:group]).map(&:to_sym)
+
+ full_update = gems.empty? && sources.empty? && groups.empty? && !options[:ruby] && !options[:bundler]
+
+ if full_update && !options[:all]
+ if Bundler.feature_flag.update_requires_all_flag?
+ raise InvalidOption, "To update everything, pass the `--all` flag."
+ end
+ SharedHelpers.major_deprecation 2, "Pass --all to `bundle update` to update everything"
+ elsif !full_update && options[:all]
+ raise InvalidOption, "Cannot specify --all along with specific options."
+ end
+
+ if full_update
+ # We're doing a full update
+ Bundler.definition(true)
+ else
+ unless Bundler.default_lockfile.exist?
+ raise GemfileLockNotFound, "This Bundle hasn't been installed yet. " \
+ "Run `bundle install` to update and install the bundled gems."
+ end
+ Bundler::CLI::Common.ensure_all_gems_in_lockfile!(gems)
+
+ if groups.any?
+ deps = Bundler.definition.dependencies.select {|d| (d.groups & groups).any? }
+ gems.concat(deps.map(&:name))
+ end
+
+ Bundler.definition(:gems => gems, :sources => sources, :ruby => options[:ruby],
+ :lock_shared_dependencies => options[:conservative],
+ :bundler => options[:bundler])
+ end
+
+ Bundler::CLI::Common.configure_gem_version_promoter(Bundler.definition, options)
+
+ Bundler::Fetcher.disable_endpoint = options["full-index"]
+
+ opts = options.dup
+ opts["update"] = true
+ opts["local"] = options[:local]
+
+ Bundler.settings.set_command_option_if_given :jobs, opts["jobs"]
+
+ Bundler.definition.validate_runtime!
+ installer = Installer.install Bundler.root, Bundler.definition, opts
+ Bundler.load.cache if Bundler.app_cache.exist?
+
+ if CLI::Common.clean_after_install?
+ require "bundler/cli/clean"
+ Bundler::CLI::Clean.new(options).run
+ end
+
+ if locked_gems = Bundler.definition.locked_gems
+ gems.each do |name|
+ locked_version = locked_gems.specs.find {|s| s.name == name }
+ locked_version &&= locked_version.version
+ next unless locked_version
+ new_version = Bundler.definition.specs[name].first
+ new_version &&= new_version.version
+ if !new_version
+ Bundler.ui.warn "Bundler attempted to update #{name} but it was removed from the bundle"
+ elsif new_version < locked_version
+ Bundler.ui.warn "Note: #{name} version regressed from #{locked_version} to #{new_version}"
+ elsif new_version == locked_version
+ Bundler.ui.warn "Bundler attempted to update #{name} but its version stayed the same"
+ end
+ end
+ end
+
+ Bundler.ui.confirm "Bundle updated!"
+ Bundler::CLI::Common.output_without_groups_message
+ Bundler::CLI::Common.output_post_install_messages installer.post_install_messages
+ end
+ end
+end
diff --git a/lib/bundler/cli/viz.rb b/lib/bundler/cli/viz.rb
new file mode 100644
index 0000000000..644f9b25cf
--- /dev/null
+++ b/lib/bundler/cli/viz.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CLI::Viz
+ attr_reader :options, :gem_name
+ def initialize(options)
+ @options = options
+ end
+
+ def run
+ # make sure we get the right `graphviz`. There is also a `graphviz`
+ # gem we're not built to support
+ gem "ruby-graphviz"
+ require "graphviz"
+
+ options[:without] = options[:without].join(":").tr(" ", ":").split(":")
+ output_file = File.expand_path(options[:file])
+
+ graph = Graph.new(Bundler.load, output_file, options[:version], options[:requirements], options[:format], options[:without])
+ graph.viz
+ rescue LoadError => e
+ Bundler.ui.error e.inspect
+ 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/
+ Bundler.ui.error e.message
+ Bundler.ui.warn "Please install GraphViz. On a Mac with Homebrew, you can run `brew install graphviz`."
+ end
+ end
+end
diff --git a/lib/bundler/compact_index_client.rb b/lib/bundler/compact_index_client.rb
new file mode 100644
index 0000000000..6c241ca07a
--- /dev/null
+++ b/lib/bundler/compact_index_client.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+require "pathname"
+require "set"
+
+module Bundler
+ class CompactIndexClient
+ DEBUG_MUTEX = Mutex.new
+ def self.debug
+ return unless ENV["DEBUG_COMPACT_INDEX"]
+ DEBUG_MUTEX.synchronize { warn("[#{self}] #{yield}") }
+ end
+
+ class Error < StandardError; end
+
+ require "bundler/compact_index_client/cache"
+ require "bundler/compact_index_client/updater"
+
+ attr_reader :directory
+
+ # @return [Lambda] A lambda that takes an array of inputs and a block, and
+ # maps the inputs with the block in parallel.
+ #
+ attr_accessor :in_parallel
+
+ def initialize(directory, fetcher)
+ @directory = Pathname.new(directory)
+ @updater = Updater.new(fetcher)
+ @cache = Cache.new(@directory)
+ @endpoints = Set.new
+ @info_checksums_by_name = {}
+ @parsed_checksums = false
+ @mutex = Mutex.new
+ @in_parallel = lambda do |inputs, &blk|
+ inputs.map(&blk)
+ end
+ end
+
+ def names
+ Bundler::CompactIndexClient.debug { "/names" }
+ update(@cache.names_path, "names")
+ @cache.names
+ end
+
+ def versions
+ Bundler::CompactIndexClient.debug { "/versions" }
+ update(@cache.versions_path, "versions")
+ versions, @info_checksums_by_name = @cache.versions
+ versions
+ end
+
+ def dependencies(names)
+ Bundler::CompactIndexClient.debug { "dependencies(#{names})" }
+ in_parallel.call(names) do |name|
+ update_info(name)
+ @cache.dependencies(name).map {|d| d.unshift(name) }
+ end.flatten(1)
+ end
+
+ def spec(name, version, platform = nil)
+ Bundler::CompactIndexClient.debug { "spec(name = #{name}, version = #{version}, platform = #{platform})" }
+ update_info(name)
+ @cache.specific_dependency(name, version, platform)
+ end
+
+ def update_and_parse_checksums!
+ Bundler::CompactIndexClient.debug { "update_and_parse_checksums!" }
+ return @info_checksums_by_name if @parsed_checksums
+ update(@cache.versions_path, "versions")
+ @info_checksums_by_name = @cache.checksums
+ @parsed_checksums = true
+ end
+
+ private
+
+ def update(local_path, remote_path)
+ Bundler::CompactIndexClient.debug { "update(#{local_path}, #{remote_path})" }
+ unless synchronize { @endpoints.add?(remote_path) }
+ Bundler::CompactIndexClient.debug { "already fetched #{remote_path}" }
+ return
+ end
+ @updater.update(local_path, url(remote_path))
+ end
+
+ def update_info(name)
+ Bundler::CompactIndexClient.debug { "update_info(#{name})" }
+ path = @cache.info_path(name)
+ checksum = @updater.checksum_for_file(path)
+ unless existing = @info_checksums_by_name[name]
+ Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since it is missing from versions" }
+ return
+ end
+ if checksum == existing
+ Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since the versions checksum matches the local checksum" }
+ return
+ end
+ Bundler::CompactIndexClient.debug { "updating info for #{name} since the versions checksum #{existing} != the local checksum #{checksum}" }
+ update(path, "info/#{name}")
+ end
+
+ def url(path)
+ path
+ end
+
+ def synchronize
+ @mutex.synchronize { yield }
+ end
+ end
+end
diff --git a/lib/bundler/compact_index_client/cache.rb b/lib/bundler/compact_index_client/cache.rb
new file mode 100644
index 0000000000..f6105d3bb3
--- /dev/null
+++ b/lib/bundler/compact_index_client/cache.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+module Bundler
+ class CompactIndexClient
+ class Cache
+ attr_reader :directory
+
+ def initialize(directory)
+ @directory = Pathname.new(directory).expand_path
+ info_roots.each do |dir|
+ SharedHelpers.filesystem_access(dir) do
+ FileUtils.mkdir_p(dir)
+ end
+ end
+ end
+
+ def names
+ lines(names_path)
+ end
+
+ def names_path
+ directory.join("names")
+ end
+
+ def versions
+ versions_by_name = Hash.new {|hash, key| hash[key] = [] }
+ info_checksums_by_name = {}
+
+ lines(versions_path).each do |line|
+ name, versions_string, info_checksum = line.split(" ", 3)
+ info_checksums_by_name[name] = info_checksum || ""
+ versions_string.split(",").each do |version|
+ if version.start_with?("-")
+ version = version[1..-1].split("-", 2).unshift(name)
+ versions_by_name[name].delete(version)
+ else
+ version = version.split("-", 2).unshift(name)
+ versions_by_name[name] << version
+ end
+ end
+ end
+
+ [versions_by_name, info_checksums_by_name]
+ end
+
+ def versions_path
+ directory.join("versions")
+ end
+
+ def checksums
+ checksums = {}
+
+ lines(versions_path).each do |line|
+ name, _, checksum = line.split(" ", 3)
+ checksums[name] = checksum
+ end
+
+ checksums
+ end
+
+ def dependencies(name)
+ lines(info_path(name)).map do |line|
+ parse_gem(line)
+ end
+ end
+
+ def info_path(name)
+ name = name.to_s
+ if name =~ /[^a-z0-9_-]/
+ name += "-#{SharedHelpers.digest(:MD5).hexdigest(name).downcase}"
+ info_roots.last.join(name)
+ else
+ info_roots.first.join(name)
+ end
+ end
+
+ def specific_dependency(name, version, platform)
+ pattern = [version, platform].compact.join("-")
+ return nil if pattern.empty?
+
+ gem_lines = info_path(name).read
+ gem_line = gem_lines[/^#{Regexp.escape(pattern)}\b.*/, 0]
+ gem_line ? parse_gem(gem_line) : nil
+ end
+
+ private
+
+ def lines(path)
+ return [] unless path.file?
+ lines = SharedHelpers.filesystem_access(path, :read, &:read).split("\n")
+ header = lines.index("---")
+ header ? lines[header + 1..-1] : lines
+ end
+
+ def parse_gem(string)
+ version_and_platform, rest = string.split(" ", 2)
+ version, platform = version_and_platform.split("-", 2)
+ dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest
+ dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : []
+ requirements = requirements ? requirements.map {|r| parse_dependency(r) } : []
+ [version, platform, dependencies, requirements]
+ end
+
+ def parse_dependency(string)
+ dependency = string.split(":")
+ dependency[-1] = dependency[-1].split("&") if dependency.size > 1
+ dependency
+ end
+
+ def info_roots
+ [
+ directory.join("info"),
+ directory.join("info-special-characters"),
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/bundler/compact_index_client/updater.rb b/lib/bundler/compact_index_client/updater.rb
new file mode 100644
index 0000000000..4d6eb80044
--- /dev/null
+++ b/lib/bundler/compact_index_client/updater.rb
@@ -0,0 +1,116 @@
+# frozen_string_literal: true
+
+require "bundler/vendored_fileutils"
+require "stringio"
+require "zlib"
+
+module Bundler
+ class CompactIndexClient
+ class Updater
+ class MisMatchedChecksumError < Error
+ def initialize(path, server_checksum, local_checksum)
+ @path = path
+ @server_checksum = server_checksum
+ @local_checksum = local_checksum
+ end
+
+ def message
+ "The checksum of /#{@path} does not match the checksum provided by the server! Something is wrong " \
+ "(local checksum is #{@local_checksum.inspect}, was expecting #{@server_checksum.inspect})."
+ end
+ end
+
+ def initialize(fetcher)
+ @fetcher = fetcher
+ require "tmpdir"
+ end
+
+ def update(local_path, remote_path, retrying = nil)
+ headers = {}
+
+ 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?
+ SharedHelpers.filesystem_access(local_temp_path) do
+ FileUtils.cp local_path, local_temp_path
+ end
+ 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
+ else
+ # Fastly ignores Range when Accept-Encoding: gzip is set
+ headers["Accept-Encoding"] = "gzip"
+ end
+
+ response = @fetcher.call(remote_path, headers)
+ return nil if response.is_a?(Net::HTTPNotModified)
+
+ content = response.body
+ if response["Content-Encoding"] == "gzip"
+ content = Zlib::GzipReader.new(StringIO.new(content)).read
+ end
+
+ 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) }
+ else
+ local_temp_path.open("w") {|f| f << content }
+ end
+ end
+
+ response_etag = (response["ETag"] || "").gsub(%r{\AW/}, "")
+ if etag_for(local_temp_path) == response_etag
+ SharedHelpers.filesystem_access(local_path) do
+ FileUtils.mv(local_temp_path, local_path)
+ end
+ return nil
+ end
+
+ if retrying
+ raise MisMatchedChecksumError.new(remote_path, response_etag, etag_for(local_temp_path))
+ end
+
+ update(local_path, remote_path, :retrying)
+ end
+ rescue Errno::EACCES
+ raise Bundler::PermissionError,
+ "Bundler does not have write access to create a temp directory " \
+ "within #{Dir.tmpdir}. Bundler must have write access to your " \
+ "systems temp directory to function properly. "
+ rescue Zlib::GzipFile::Error
+ raise Bundler::HTTPError
+ end
+
+ def etag_for(path)
+ sum = checksum_for_file(path)
+ sum ? %("#{sum}") : nil
+ end
+
+ def slice_body(body, range)
+ if body.respond_to?(:byteslice)
+ body.byteslice(range)
+ else # pre-1.9.3
+ body.unpack("@#{range.first}a#{range.end + 1}").first
+ end
+ end
+
+ def checksum_for_file(path)
+ return nil unless path.file?
+ # This must use IO.read instead of Digest.file().hexdigest
+ # because we need to preserve \n line endings on windows when calculating
+ # the checksum
+ SharedHelpers.filesystem_access(path, :read) do
+ SharedHelpers.digest(:MD5).hexdigest(IO.read(path))
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/compatibility_guard.rb b/lib/bundler/compatibility_guard.rb
new file mode 100644
index 0000000000..750a1db04f
--- /dev/null
+++ b/lib/bundler/compatibility_guard.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: false
+
+require "rubygems"
+require "bundler/version"
+
+if Bundler::VERSION.split(".").first.to_i >= 2
+ if Gem::Version.new(Object::RUBY_VERSION.dup) < Gem::Version.new("2.3")
+ abort "Bundler 2 requires Ruby 2.3 or later. Either install bundler 1 or update to a supported Ruby version."
+ end
+
+ if Gem::Version.new(Gem::VERSION.dup) < Gem::Version.new("2.5")
+ abort "Bundler 2 requires RubyGems 2.5 or later. Either install bundler 1 or update to a supported RubyGems version."
+ end
+end
diff --git a/lib/bundler/constants.rb b/lib/bundler/constants.rb
new file mode 100644
index 0000000000..2e4ebb37ee
--- /dev/null
+++ b/lib/bundler/constants.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module Bundler
+ WINDOWS = RbConfig::CONFIG["host_os"] =~ /(msdos|mswin|djgpp|mingw)/
+ FREEBSD = RbConfig::CONFIG["host_os"] =~ /bsd/
+ NULL = WINDOWS ? "NUL" : "/dev/null"
+end
diff --git a/lib/bundler/current_ruby.rb b/lib/bundler/current_ruby.rb
new file mode 100644
index 0000000000..d5efaad6c5
--- /dev/null
+++ b/lib/bundler/current_ruby.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+module Bundler
+ # Returns current version of Ruby
+ #
+ # @return [CurrentRuby] Current version of Ruby
+ def self.current_ruby
+ @current_ruby ||= CurrentRuby.new
+ end
+
+ class CurrentRuby
+ KNOWN_MINOR_VERSIONS = %w[
+ 1.8
+ 1.9
+ 2.0
+ 2.1
+ 2.2
+ 2.3
+ 2.4
+ 2.5
+ 2.6
+ ].freeze
+
+ KNOWN_MAJOR_VERSIONS = KNOWN_MINOR_VERSIONS.map {|v| v.split(".", 2).first }.uniq.freeze
+
+ KNOWN_PLATFORMS = %w[
+ jruby
+ maglev
+ mingw
+ mri
+ mswin
+ mswin64
+ rbx
+ ruby
+ truffleruby
+ x64_mingw
+ ].freeze
+
+ def ruby?
+ !mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" ||
+ RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev" || RUBY_ENGINE == "truffleruby")
+ end
+
+ def mri?
+ !mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby")
+ end
+
+ def rbx?
+ ruby? && defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx"
+ end
+
+ def jruby?
+ defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
+ end
+
+ def maglev?
+ defined?(RUBY_ENGINE) && RUBY_ENGINE == "maglev"
+ end
+
+ def truffleruby?
+ defined?(RUBY_ENGINE) && RUBY_ENGINE == "truffleruby"
+ end
+
+ def mswin?
+ Bundler::WINDOWS
+ end
+
+ def mswin64?
+ Bundler::WINDOWS && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mswin64" && Bundler.local_platform.cpu == "x64"
+ end
+
+ def mingw?
+ Bundler::WINDOWS && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu != "x64"
+ end
+
+ def x64_mingw?
+ Bundler::WINDOWS && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu == "x64"
+ end
+
+ (KNOWN_MINOR_VERSIONS + KNOWN_MAJOR_VERSIONS).each do |version|
+ trimmed_version = version.tr(".", "")
+ define_method(:"on_#{trimmed_version}?") do
+ RUBY_VERSION.start_with?("#{version}.")
+ end
+
+ KNOWN_PLATFORMS.each do |platform|
+ define_method(:"#{platform}_#{trimmed_version}?") do
+ send(:"#{platform}?") && send(:"on_#{trimmed_version}?")
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
new file mode 100644
index 0000000000..8e56d4a9bc
--- /dev/null
+++ b/lib/bundler/definition.rb
@@ -0,0 +1,993 @@
+# frozen_string_literal: true
+
+require "bundler/lockfile_parser"
+require "set"
+
+module Bundler
+ class Definition
+ include GemHelpers
+
+ attr_reader(
+ :dependencies,
+ :locked_deps,
+ :locked_gems,
+ :platforms,
+ :requires,
+ :ruby_version,
+ :lockfile,
+ :gemfiles
+ )
+
+ # Given a gemfile and lockfile creates a Bundler definition
+ #
+ # @param gemfile [Pathname] Path to Gemfile
+ # @param lockfile [Pathname,nil] Path to Gemfile.lock
+ # @param unlock [Hash, Boolean, nil] Gems that have been requested
+ # to be updated or true if all gems should be updated
+ # @return [Bundler::Definition]
+ def self.build(gemfile, lockfile, unlock)
+ unlock ||= {}
+ gemfile = Pathname.new(gemfile).expand_path
+
+ raise GemfileNotFound, "#{gemfile} not found" unless gemfile.file?
+
+ Dsl.evaluate(gemfile, lockfile, unlock)
+ end
+
+ #
+ # How does the new system work?
+ #
+ # * Load information from Gemfile and Lockfile
+ # * Invalidate stale locked specs
+ # * All specs from stale source are stale
+ # * All specs that are reachable only through a stale
+ # dependency are stale.
+ # * If all fresh dependencies are satisfied by the locked
+ # specs, then we can try to resolve locally.
+ #
+ # @param lockfile [Pathname] Path to Gemfile.lock
+ # @param dependencies [Array(Bundler::Dependency)] array of dependencies from Gemfile
+ # @param sources [Bundler::SourceList]
+ # @param unlock [Hash, Boolean, nil] Gems that have been requested
+ # to be updated or true if all gems should be updated
+ # @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version
+ # @param optional_groups [Array(String)] A list of optional groups
+ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = [], gemfiles = [])
+ if [true, false].include?(unlock)
+ @unlocking_bundler = false
+ @unlocking = unlock
+ else
+ unlock = unlock.dup
+ @unlocking_bundler = unlock.delete(:bundler)
+ unlock.delete_if {|_k, v| Array(v).empty? }
+ @unlocking = !unlock.empty?
+ end
+
+ @dependencies = dependencies
+ @sources = sources
+ @unlock = unlock
+ @optional_groups = optional_groups
+ @remote = false
+ @specs = nil
+ @ruby_version = ruby_version
+ @gemfiles = gemfiles
+
+ @lockfile = lockfile
+ @lockfile_contents = String.new
+ @locked_bundler_version = nil
+ @locked_ruby_version = nil
+ @locked_specs_incomplete_for_platform = false
+
+ if lockfile && File.exist?(lockfile)
+ @lockfile_contents = Bundler.read_file(lockfile)
+ @locked_gems = LockfileParser.new(@lockfile_contents)
+ @locked_platforms = @locked_gems.platforms
+ @platforms = @locked_platforms.dup
+ @locked_bundler_version = @locked_gems.bundler_version
+ @locked_ruby_version = @locked_gems.ruby_version
+
+ if unlock != true
+ @locked_deps = @locked_gems.dependencies
+ @locked_specs = SpecSet.new(@locked_gems.specs)
+ @locked_sources = @locked_gems.sources
+ else
+ @unlock = {}
+ @locked_deps = {}
+ @locked_specs = SpecSet.new([])
+ @locked_sources = []
+ end
+ else
+ @unlock = {}
+ @platforms = []
+ @locked_gems = nil
+ @locked_deps = {}
+ @locked_specs = SpecSet.new([])
+ @locked_sources = []
+ @locked_platforms = []
+ end
+
+ @unlock[:gems] ||= []
+ @unlock[:sources] ||= []
+ @unlock[:ruby] ||= if @ruby_version && locked_ruby_version_object
+ @ruby_version.diff(locked_ruby_version_object)
+ end
+ @unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version)
+
+ add_current_platform unless Bundler.frozen_bundle?
+
+ converge_path_sources_to_gemspec_sources
+ @path_changes = converge_paths
+ @source_changes = converge_sources
+
+ unless @unlock[:lock_shared_dependencies]
+ eager_unlock = expand_dependencies(@unlock[:gems], true)
+ @unlock[:gems] = @locked_specs.for(eager_unlock, [], false, false, false).map(&:name)
+ end
+
+ @dependency_changes = converge_dependencies
+ @local_changes = converge_locals
+
+ @requires = compute_requires
+ 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
+ end
+
+ def resolve_with_cache!
+ raise "Specs already loaded" if @specs
+ sources.cached!
+ specs
+ end
+
+ def resolve_remotely!
+ raise "Specs already loaded" if @specs
+ @remote = true
+ sources.remote!
+ specs
+ 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
+ # 2. After that it tries and fetches gemspec of resolved dependencies
+ #
+ # @return [Bundler::SpecSet]
+ def specs
+ @specs ||= begin
+ begin
+ specs = resolve.materialize(Bundler.settings[:cache_all_platforms] ? dependencies : requested_dependencies)
+ rescue GemNotFound => e # Handle yanked gem
+ gem_name, gem_version = extract_gem_info(e)
+ locked_gem = @locked_specs[gem_name].last
+ raise if locked_gem.nil? || locked_gem.version.to_s != gem_version || !@remote
+ raise GemNotFound, "Your bundle is locked to #{locked_gem}, but that version could not " \
+ "be found in any of the sources listed in your Gemfile. If you haven't changed sources, " \
+ "that means the author of #{locked_gem} has removed it. You'll need to update your bundle " \
+ "to a version other than #{locked_gem} that hasn't been removed in order to install."
+ end
+ unless specs["bundler"].any?
+ bundler = sources.metadata_source.specs.search(Gem::Dependency.new("bundler", VERSION)).last
+ specs["bundler"] = bundler
+ end
+
+ specs
+ end
+ end
+
+ def new_specs
+ specs - @locked_specs
+ end
+
+ def removed_specs
+ @locked_specs - specs
+ end
+
+ def new_platform?
+ @new_platform
+ end
+
+ def missing_specs
+ missing = []
+ resolve.materialize(requested_dependencies, missing)
+ missing
+ end
+
+ def missing_specs?
+ missing = missing_specs
+ return false if missing.empty?
+ Bundler.ui.debug "The definition is missing #{missing.map(&:full_name)}"
+ true
+ rescue BundlerError => e
+ @index = nil
+ @resolve = nil
+ @specs = nil
+ @gem_version_promoter = nil
+
+ Bundler.ui.debug "The definition is missing dependencies, failed to resolve & materialize locally (#{e})"
+ true
+ end
+
+ def requested_specs
+ @requested_specs ||= begin
+ groups = requested_groups
+ groups.map!(&:to_sym)
+ specs_for(groups)
+ end
+ end
+
+ def current_dependencies
+ dependencies.select(&:should_include?)
+ end
+
+ def specs_for(groups)
+ deps = dependencies.select {|d| (d.groups & groups).any? }
+ deps.delete_if {|d| !d.should_include? }
+ specs.for(expand_dependencies(deps))
+ end
+
+ # Resolve all the dependencies specified in Gemfile. It ensures that
+ # dependencies that have been already resolved via locked file and are fresh
+ # are reused when resolving dependencies
+ #
+ # @return [SpecSet] resolved dependencies
+ def resolve
+ @resolve ||= begin
+ last_resolve = converge_locked_specs
+ resolve =
+ if Bundler.frozen_bundle?
+ Bundler.ui.debug "Frozen, using resolution from the lockfile"
+ last_resolve
+ elsif !unlocking? && nothing_changed?
+ Bundler.ui.debug("Found no changes, using resolution from the lockfile")
+ last_resolve
+ else
+ # Run a resolve against the locally available gems
+ Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}")
+ last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms)
+ end
+
+ # filter out gems that _can_ be installed on multiple platforms, but don't need
+ # to be
+ resolve.for(expand_dependencies(dependencies, true), [], false, false, false)
+ end
+ end
+
+ def index
+ @index ||= Index.build do |idx|
+ dependency_names = @dependencies.map(&:name)
+
+ sources.all_sources.each do |source|
+ source.dependency_names = dependency_names - pinned_spec_names(source)
+ idx.add_source source.specs
+ dependency_names.concat(source.unmet_deps).uniq!
+ end
+
+ double_check_for_index(idx, dependency_names)
+ end
+ end
+
+ # Suppose the gem Foo depends on the gem Bar. Foo exists in Source A. Bar has some versions that exist in both
+ # sources A and B. At this point, the API request will have found all the versions of Bar in source A,
+ # but will not have found any versions of Bar from source B, which is a problem if the requested version
+ # of Foo specifically depends on a version of Bar that is only found in source B. This ensures that for
+ # each spec we found, we add all possible versions from all sources to the index.
+ def double_check_for_index(idx, dependency_names)
+ pinned_names = pinned_spec_names
+ loop do
+ idxcount = idx.size
+
+ names = :names # do this so we only have to traverse to get dependency_names from the index once
+ unmet_dependency_names = lambda do
+ return names unless names == :names
+ new_names = sources.all_sources.map(&:dependency_names_to_double_check)
+ return names = nil if new_names.compact!
+ names = new_names.flatten(1).concat(dependency_names)
+ names.uniq!
+ names -= pinned_names
+ names
+ end
+
+ sources.all_sources.each do |source|
+ source.double_check_for(unmet_dependency_names)
+ end
+
+ break if idxcount == idx.size
+ end
+ end
+ private :double_check_for_index
+
+ def has_rubygems_remotes?
+ sources.rubygems_sources.any? {|s| s.remotes.any? }
+ end
+
+ def has_local_dependencies?
+ !sources.path_sources.empty? || !sources.git_sources.empty?
+ end
+
+ def spec_git_paths
+ sources.git_sources.map {|s| s.path.to_s }
+ end
+
+ def groups
+ dependencies.map(&:groups).flatten.uniq
+ end
+
+ def lock(file, preserve_unknown_sections = false)
+ contents = to_lock
+
+ # 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")
+
+ if @locked_bundler_version
+ locked_major = @locked_bundler_version.segments.first
+ current_major = Gem::Version.create(Bundler::VERSION).segments.first
+
+ if updating_major = locked_major < current_major
+ Bundler.ui.warn "Warning: the lockfile is being updated to Bundler #{current_major}, " \
+ "after which you will be unable to return to Bundler #{@locked_bundler_version.segments.first}."
+ end
+ end
+
+ preserve_unknown_sections ||= !updating_major && (Bundler.frozen_bundle? || !(unlocking? || @unlocking_bundler))
+
+ return if file && File.exist?(file) && lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections)
+
+ if Bundler.frozen_bundle?
+ Bundler.ui.error "Cannot write a changed lockfile while frozen."
+ return
+ end
+
+ SharedHelpers.filesystem_access(file) do |p|
+ File.open(p, "wb") {|f| f.puts(contents) }
+ end
+ end
+
+ def locked_bundler_version
+ if @locked_bundler_version && @locked_bundler_version < Gem::Version.new(Bundler::VERSION)
+ new_version = Bundler::VERSION
+ end
+
+ new_version || @locked_bundler_version || Bundler::VERSION
+ end
+
+ def locked_ruby_version
+ return unless ruby_version
+ if @unlock[:ruby] || !@locked_ruby_version
+ Bundler::RubyVersion.system
+ else
+ @locked_ruby_version
+ end
+ end
+
+ def locked_ruby_version_object
+ return unless @locked_ruby_version
+ @locked_ruby_version_object ||= begin
+ unless version = RubyVersion.from_string(@locked_ruby_version)
+ raise LockfileError, "The Ruby version #{@locked_ruby_version} from " \
+ "#{@lockfile} could not be parsed. " \
+ "Try running bundle update --ruby to resolve this."
+ end
+ version
+ end
+ end
+
+ def to_lock
+ require "bundler/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")[:global]
+ "bundle config --delete frozen"
+ elsif Bundler.settings.locations("deployment").keys.&([:global, :local]).any?
+ "bundle config --delete deployment"
+ else
+ "bundle install --no-deployment"
+ end
+ msg << "\n\nIf this is a development machine, remove the #{Bundler.default_gemfile} " \
+ "freeze \nby running `#{suggested_command}`."
+ end
+
+ added = []
+ deleted = []
+ changed = []
+
+ new_platforms = @platforms - @locked_platforms
+ deleted_platforms = @locked_platforms - @platforms
+ added.concat new_platforms.map {|p| "* platform: #{p}" }
+ deleted.concat deleted_platforms.map {|p| "* platform: #{p}" }
+
+ gemfile_sources = sources.lock_sources
+
+ new_sources = gemfile_sources - @locked_sources
+ deleted_sources = @locked_sources - gemfile_sources
+
+ new_deps = @dependencies - @locked_deps.values
+ deleted_deps = @locked_deps.values - @dependencies
+
+ # Check if it is possible that the source is only changed thing
+ if (new_deps.empty? && deleted_deps.empty?) && (!new_sources.empty? && !deleted_sources.empty?)
+ new_sources.reject! {|source| (source.path? && source.path.exist?) || equivalent_rubygems_remotes?(source) }
+ deleted_sources.reject! {|source| (source.path? && source.path.exist?) || equivalent_rubygems_remotes?(source) }
+ end
+
+ if @locked_sources != gemfile_sources
+ if new_sources.any?
+ added.concat new_sources.map {|source| "* source: #{source}" }
+ end
+
+ if deleted_sources.any?
+ deleted.concat deleted_sources.map {|source| "* source: #{source}" }
+ end
+ end
+
+ added.concat new_deps.map {|d| "* #{pretty_dep(d)}" } if new_deps.any?
+ if deleted_deps.any?
+ deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" }
+ end
+
+ both_sources = Hash.new {|h, k| h[k] = [] }
+ @dependencies.each {|d| both_sources[d.name][0] = d }
+ @locked_deps.each {|name, d| both_sources[name][1] = d.source }
+
+ both_sources.each do |name, (dep, lock_source)|
+ next unless (dep.nil? && !lock_source.nil?) || (!dep.nil? && !lock_source.nil? && !lock_source.can_lock?(dep))
+ gemfile_source_name = (dep && dep.source) || "no specified source"
+ lockfile_source_name = lock_source || "no specified source"
+ changed << "* #{name} from `#{gemfile_source_name}` to `#{lockfile_source_name}`"
+ end
+
+ reason = change_reason
+ msg << "\n\n#{reason.split(", ").map(&:capitalize).join("\n")}" unless reason.strip.empty?
+ 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"
+
+ raise ProductionError, msg if added.any? || deleted.any? || changed.any? || !nothing_changed?
+ end
+
+ def validate_runtime!
+ validate_ruby!
+ validate_platforms!
+ end
+
+ def validate_ruby!
+ return unless ruby_version
+
+ if diff = ruby_version.diff(Bundler::RubyVersion.system)
+ problem, expected, actual = diff
+
+ msg = case problem
+ when :engine
+ "Your Ruby engine is #{actual}, but your Gemfile specified #{expected}"
+ when :version
+ "Your Ruby version is #{actual}, but your Gemfile specified #{expected}"
+ when :engine_version
+ "Your #{Bundler::RubyVersion.system.engine} version is #{actual}, but your Gemfile specified #{ruby_version.engine} #{expected}"
+ when :patchlevel
+ if !expected.is_a?(String)
+ "The Ruby patchlevel in your Gemfile must be a string"
+ else
+ "Your Ruby patchlevel is #{actual}, but your Gemfile specified #{expected}"
+ end
+ end
+
+ raise RubyVersionMismatch, msg
+ end
+ end
+
+ def validate_platforms!
+ return if @platforms.any? do |bundle_platform|
+ Bundler.rubygems.platforms.any? do |local_platform|
+ MatchPlatform.platforms_match?(bundle_platform, local_platform)
+ end
+ end
+
+ raise ProductionError, "Your bundle only supports platforms #{@platforms.map(&:to_s)} " \
+ "but your local platforms are #{Bundler.rubygems.platforms.map(&:to_s)}, and " \
+ "there's no compatible match between those two lists."
+ end
+
+ def add_platform(platform)
+ @new_platform ||= !@platforms.include?(platform)
+ @platforms |= [platform]
+ end
+
+ def remove_platform(platform)
+ return if @platforms.delete(Gem::Platform.new(platform))
+ raise InvalidOption, "Unable to remove the platform `#{platform}` since the only platforms are #{@platforms.join ", "}"
+ end
+
+ def add_current_platform
+ current_platform = Bundler.local_platform
+ add_platform(current_platform) if Bundler.feature_flag.specific_platform?
+ add_platform(generic(current_platform))
+ end
+
+ def find_resolved_spec(current_spec)
+ specs.find_by_name_and_platform(current_spec.name, current_spec.platform)
+ end
+
+ def find_indexed_specs(current_spec)
+ index[current_spec.name].select {|spec| spec.match_platform(current_spec.platform) }.sort_by(&:version)
+ end
+
+ attr_reader :sources
+ private :sources
+
+ def nothing_changed?
+ !@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes && !@locked_specs_incomplete_for_platform
+ end
+
+ def unlocking?
+ @unlocking
+ end
+
+ private
+
+ def change_reason
+ if unlocking?
+ unlock_reason = @unlock.reject {|_k, v| Array(v).empty? }.map do |k, v|
+ if v == true
+ k.to_s
+ else
+ v = Array(v)
+ "#{k}: (#{v.join(", ")})"
+ end
+ end.join(", ")
+ return "bundler is unlocking #{unlock_reason}"
+ end
+ [
+ [@source_changes, "the list of sources changed"],
+ [@dependency_changes, "the dependencies in your gemfile changed"],
+ [@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"],
+ ].select(&:first).map(&:last).join(", ")
+ end
+
+ def pretty_dep(dep, source = false)
+ SharedHelpers.pretty_dependency(dep, source)
+ end
+
+ # Check if the specs of the given source changed
+ # according to the locked source.
+ def specs_changed?(source)
+ locked = @locked_sources.find {|s| s == source }
+
+ !locked || dependencies_for_source_changed?(source, locked) || specs_for_source_changed?(source)
+ end
+
+ def dependencies_for_source_changed?(source, locked_source = source)
+ deps_for_source = @dependencies.select {|s| s.source == source }
+ locked_deps_for_source = @locked_deps.values.select {|dep| dep.source == locked_source }
+
+ Set.new(deps_for_source) != Set.new(locked_deps_for_source)
+ end
+
+ def specs_for_source_changed?(source)
+ locked_index = Index.new
+ locked_index.use(@locked_specs.select {|s| source.can_lock?(s) })
+
+ # order here matters, since Index#== is checking source.specs.include?(locked_index)
+ locked_index != source.specs
+ rescue PathError, GitError => e
+ Bundler.ui.debug "Assuming that #{source} has not changed since fetching its specs errored (#{e})"
+ false
+ end
+
+ # Get all locals and override their matching sources.
+ # Return true if any of the locals changed (for example,
+ # they point to a new revision) or depend on new specs.
+ def converge_locals
+ locals = []
+
+ 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.unlock! if @unlock[:gems].include?(spec.name)
+ locals << [source, source.local_override!(v)]
+ end
+ end
+
+ sources_with_changes = locals.select do |source, changed|
+ changed || specs_changed?(source)
+ end.map(&:first)
+ !sources_with_changes.each {|source| @unlock[:sources] << source.name }.empty?
+ end
+
+ def converge_paths
+ sources.path_sources.any? do |source|
+ specs_changed?(source)
+ end
+ end
+
+ def converge_path_source_to_gemspec_source(source)
+ return source unless source.instance_of?(Source::Path)
+ gemspec_source = sources.path_sources.find {|s| s.is_a?(Source::Gemspec) && s.as_path_source == source }
+ gemspec_source || source
+ end
+
+ def converge_path_sources_to_gemspec_sources
+ @locked_sources.map! do |source|
+ converge_path_source_to_gemspec_source(source)
+ end
+ @locked_specs.each do |spec|
+ spec.source &&= converge_path_source_to_gemspec_source(spec.source)
+ end
+ @locked_deps.each do |_, dep|
+ dep.source &&= converge_path_source_to_gemspec_source(dep.source)
+ end
+ end
+
+ def converge_rubygems_sources
+ return false if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources?
+
+ changes = false
+
+ # Get the RubyGems sources from the Gemfile.lock
+ locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) }
+ # Get the RubyGems remotes from the Gemfile
+ actual_remotes = sources.rubygems_remotes
+
+ # If there is a RubyGems source in both
+ if !locked_gem_sources.empty? && !actual_remotes.empty?
+ locked_gem_sources.each do |locked_gem|
+ # Merge the remotes from the Gemfile into the Gemfile.lock
+ changes |= locked_gem.replace_remotes(actual_remotes, Bundler.settings[:allow_deployment_source_credential_changes])
+ end
+ end
+
+ changes
+ end
+
+ def converge_sources
+ changes = false
+
+ changes |= converge_rubygems_sources
+
+ # Replace the sources from the Gemfile with the sources from the Gemfile.lock,
+ # if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent
+ # source in the Gemfile.lock, use the one from the Gemfile.
+ changes |= sources.replace_sources!(@locked_sources)
+
+ sources.all_sources.each do |source|
+ # If the source is unlockable and the current command allows an unlock of
+ # the source (for example, you are doing a `bundle update <foo>` of a git-pinned
+ # gem), unlock it. For git sources, this means to unlock the revision, which
+ # will cause the `ref` used to be the most recent for the branch (or master) if
+ # an explicit `ref` is not used.
+ if source.respond_to?(:unlock!) && @unlock[:sources].include?(source.name)
+ source.unlock!
+ changes = true
+ end
+ end
+
+ changes
+ end
+
+ def converge_dependencies
+ frozen = Bundler.frozen_bundle?
+ (@dependencies + @locked_deps.values).each do |dep|
+ locked_source = @locked_deps[dep.name]
+ # This is to make sure that if bundler is installing in deployment mode and
+ # after locked_source and sources don't match, we still use locked_source.
+ if frozen && !locked_source.nil? &&
+ locked_source.respond_to?(:source) && locked_source.source.instance_of?(Source::Path) && locked_source.source.path.exist?
+ dep.source = locked_source.source
+ elsif dep.source
+ dep.source = sources.get(dep.source)
+ end
+ if dep.source.is_a?(Source::Gemspec)
+ dep.platforms.concat(@platforms.map {|p| Dependency::REVERSE_PLATFORM_MAP[p] }.flatten(1)).uniq!
+ end
+ end
+
+ changes = false
+ # We want to know if all match, but don't want to check all entries
+ # This means we need to return false if any dependency doesn't match
+ # the lock or doesn't exist in the lock.
+ @dependencies.each do |dependency|
+ unless locked_dep = @locked_deps[dependency.name]
+ changes = true
+ next
+ end
+
+ # Gem::Dependency#== matches Gem::Dependency#type. As the lockfile
+ # doesn't carry a notion of the dependency type, if you use
+ # add_development_dependency in a gemspec that's loaded with the gemspec
+ # directive, the lockfile dependencies and resolved dependencies end up
+ # with a mismatch on #type. Work around that by setting the type on the
+ # dep from the lockfile.
+ locked_dep.instance_variable_set(:@type, dependency.type)
+
+ # We already know the name matches from the hash lookup
+ # so we only need to check the requirement now
+ changes ||= dependency.requirement != locked_dep.requirement
+ end
+
+ changes
+ end
+
+ # Remove elements from the locked specs that are expired. This will most
+ # commonly happen if the Gemfile has changed since the lockfile was last
+ # generated
+ def converge_locked_specs
+ deps = []
+
+ # Build a list of dependencies that are the same in the Gemfile
+ # and Gemfile.lock. If the Gemfile modified a dependency, but
+ # the gem in the Gemfile.lock still satisfies it, this is fine
+ # too.
+ @dependencies.each do |dep|
+ locked_dep = @locked_deps[dep.name]
+
+ # If the locked_dep doesn't match the dependency we're looking for then we ignore the locked_dep
+ locked_dep = nil unless locked_dep == dep
+
+ if in_locked_deps?(dep, locked_dep) || satisfies_locked_spec?(dep)
+ deps << dep
+ elsif dep.source.is_a?(Source::Path) && dep.current_platform? && (!locked_dep || dep.source != locked_dep.source)
+ @locked_specs.each do |s|
+ @unlock[:gems] << s.name if s.source == dep.source
+ end
+
+ dep.source.unlock! if dep.source.respond_to?(:unlock!)
+ dep.source.specs.each {|s| @unlock[:gems] << s.name }
+ end
+ end
+
+ unlock_source_unlocks_spec = Bundler.feature_flag.unlock_source_unlocks_spec?
+
+ converged = []
+ @locked_specs.each do |s|
+ # Replace the locked dependency's source with the equivalent source from the Gemfile
+ dep = @dependencies.find {|d| s.satisfies?(d) }
+ s.source = (dep && dep.source) || sources.get(s.source)
+
+ # Don't add a spec to the list if its source is expired. For example,
+ # if you change a Git gem to RubyGems.
+ next if s.source.nil?
+ next if @unlock[:sources].include?(s.source.name)
+
+ # XXX This is a backwards-compatibility fix to preserve the ability to
+ # unlock a single gem by passing its name via `--source`. See issue #3759
+ # TODO: delete in Bundler 2
+ next if unlock_source_unlocks_spec && @unlock[:sources].include?(s.name)
+
+ # If the spec is from a path source and it doesn't exist anymore
+ # then we unlock it.
+
+ # Path sources have special logic
+ if s.source.instance_of?(Source::Path) || s.source.instance_of?(Source::Gemspec)
+ other_sources_specs = begin
+ s.source.specs
+ rescue PathError, GitError
+ # if we won't need the source (according to the lockfile),
+ # don't error if the path/git source isn't available
+ next if @locked_specs.
+ for(requested_dependencies, [], false, true, false).
+ none? {|locked_spec| locked_spec.source == s.source }
+
+ raise
+ end
+
+ other = other_sources_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 other
+
+ deps2 = other.dependencies.select {|d| d.type != :development }
+ runtime_dependencies = s.dependencies.select {|d| d.type != :development }
+ # If the dependencies of the path source have changed, unlock it
+ next unless runtime_dependencies.sort == deps2.sort
+ end
+
+ converged << s
+ end
+
+ resolve = SpecSet.new(converged)
+ expanded_deps = expand_dependencies(deps, true)
+ @locked_specs_incomplete_for_platform = !resolve.for(expanded_deps, @unlock[:gems], true, true)
+ resolve = resolve.for(expanded_deps, @unlock[:gems], false, false, false)
+ diff = nil
+
+ # Now, we unlock any sources that do not have anymore gems pinned to it
+ sources.all_sources.each do |source|
+ next unless source.respond_to?(:unlock!)
+
+ unless resolve.any? {|s| s.source == source }
+ diff ||= @locked_specs.to_a - resolve.to_a
+ source.unlock! if diff.any? {|s| s.source == source }
+ end
+ end
+
+ resolve
+ end
+
+ def in_locked_deps?(dep, locked_dep)
+ # Because the lockfile can't link a dep to a specific remote, we need to
+ # treat sources as equivalent anytime the locked dep has all the remotes
+ # that the Gemfile dep does.
+ locked_dep && locked_dep.source && dep.source && locked_dep.source.include?(dep.source)
+ end
+
+ def satisfies_locked_spec?(dep)
+ @locked_specs[dep].any? {|s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) }
+ end
+
+ # This list of dependencies is only used in #resolve, so it's OK to add
+ # the metadata dependencies here
+ def expanded_dependencies
+ @expanded_dependencies ||= begin
+ expand_dependencies(dependencies + metadata_dependencies, @remote)
+ end
+ end
+
+ def metadata_dependencies
+ @metadata_dependencies ||= begin
+ ruby_versions = concat_ruby_version_requirements(@ruby_version)
+ if ruby_versions.empty? || !@ruby_version.exact?
+ concat_ruby_version_requirements(RubyVersion.system)
+ concat_ruby_version_requirements(locked_ruby_version_object) unless @unlock[:ruby]
+ end
+ [
+ Dependency.new("ruby\0", ruby_versions),
+ Dependency.new("rubygems\0", Gem::VERSION),
+ ]
+ end
+ end
+
+ def concat_ruby_version_requirements(ruby_version, ruby_versions = [])
+ return ruby_versions unless ruby_version
+ if ruby_version.patchlevel
+ ruby_versions << ruby_version.to_gem_version_with_patchlevel
+ else
+ ruby_versions.concat(ruby_version.versions.map do |version|
+ requirement = Gem::Requirement.new(version)
+ if requirement.exact?
+ "~> #{version}.0"
+ else
+ requirement
+ end
+ end)
+ end
+ end
+
+ def expand_dependencies(dependencies, remote = false)
+ sorted_platforms = Resolver.sort_platforms(@platforms)
+ deps = []
+ dependencies.each do |dep|
+ dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name)
+ next if !remote && !dep.current_platform?
+ platforms = dep.gem_platforms(sorted_platforms)
+ if platforms.empty? && !Bundler.settings[:disable_platform_warnings]
+ mapped_platforms = dep.platforms.map {|p| Dependency::PLATFORM_MAP[p] }
+ Bundler.ui.warn \
+ "The dependency #{dep} will be unused by any of the platforms Bundler is installing for. " \
+ "Bundler is installing for #{@platforms.join ", "} but the dependency " \
+ "is only for #{mapped_platforms.join ", "}. " \
+ "To add those platforms to the bundle, " \
+ "run `bundle lock --add-platform #{mapped_platforms.join " "}`."
+ end
+ platforms.each do |p|
+ deps << DepProxy.new(dep, p) if remote || p == generic_local_platform
+ end
+ end
+ deps
+ end
+
+ def requested_dependencies
+ groups = requested_groups
+ groups.map!(&:to_sym)
+ dependencies.reject {|d| !d.should_include? || (d.groups & groups).empty? }
+ end
+
+ def source_requirements
+ # Load all specs from remote sources
+ index
+
+ # 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)
+ default = sources.default_source
+ source_requirements = { :default => default }
+ default = nil unless Bundler.feature_flag.lockfile_uses_separate_rubygems_sources?
+ dependencies.each do |dep|
+ next unless source = dep.source || default
+ source_requirements[dep.name] = source
+ end
+ metadata_dependencies.each do |dep|
+ source_requirements[dep.name] = sources.metadata_source
+ end
+ source_requirements["bundler"] = sources.metadata_source # needs to come last to override
+ source_requirements
+ end
+
+ def pinned_spec_names(skip = nil)
+ pinned_names = []
+ default = Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? && sources.default_source
+ @dependencies.each do |dep|
+ next unless dep_source = dep.source || default
+ next if dep_source == skip
+ pinned_names << dep.name
+ end
+ pinned_names
+ end
+
+ def requested_groups
+ groups - Bundler.settings[:without] - @optional_groups + Bundler.settings[:with]
+ 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
+ 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
+ proposed = proposed.gsub(pattern, "\n").gsub(whitespace_cleanup, "\n\n").strip
+ end
+ current == proposed
+ end
+
+ def extract_gem_info(error)
+ # This method will extract the error message like "Could not find foo-1.2.3 in any of the sources"
+ # to an array. The first element will be the gem name (e.g. foo), the second will be the version number.
+ error.message.scan(/Could not find (\w+)-(\d+(?:\.\d+)+)/).flatten
+ 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
+ end
+ end
+
+ def additional_base_requirements_for_resolve
+ return [] unless @locked_gems && Bundler.feature_flag.only_update_to_newer_versions?
+ dependencies_by_name = dependencies.inject({}) {|memo, dep| memo.update(dep.name => dep) }
+ @locked_gems.specs.reduce({}) do |requirements, locked_spec|
+ name = locked_spec.name
+ next requirements if @locked_gems.dependencies[name] != dependencies_by_name[name]
+ dep = Gem::Dependency.new(name, ">= #{locked_spec.version}")
+ requirements[name] = DepProxy.new(dep, locked_spec.platform)
+ requirements
+ end.values
+ end
+
+ def equivalent_rubygems_remotes?(source)
+ return false unless source.is_a?(Source::Rubygems)
+
+ Bundler.settings[:allow_deployment_source_credential_changes] && source.equivalent_remotes?(sources.rubygems_remotes)
+ end
+ end
+end
diff --git a/lib/bundler/dep_proxy.rb b/lib/bundler/dep_proxy.rb
new file mode 100644
index 0000000000..6c32179ac1
--- /dev/null
+++ b/lib/bundler/dep_proxy.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module Bundler
+ class DepProxy
+ attr_reader :__platform, :dep
+
+ def initialize(dep, platform)
+ @dep = dep
+ @__platform = platform
+ end
+
+ def hash
+ @hash ||= [dep, __platform].hash
+ end
+
+ def ==(other)
+ return false if other.class != self.class
+ dep == other.dep && __platform == other.__platform
+ end
+
+ 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
+
+ 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
new file mode 100644
index 0000000000..8840ad6a9c
--- /dev/null
+++ b/lib/bundler/dependency.rb
@@ -0,0 +1,139 @@
+# frozen_string_literal: true
+
+require "rubygems/dependency"
+require "bundler/shared_helpers"
+require "bundler/rubygems_ext"
+
+module Bundler
+ class Dependency < Gem::Dependency
+ attr_reader :autorequire
+ attr_reader :groups, :platforms, :gemfile
+
+ 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,
+ :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,
+ :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,
+ :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,
+ :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,
+ :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,
+ }.freeze
+
+ REVERSE_PLATFORM_MAP = {}.tap do |reverse_platform_map|
+ PLATFORM_MAP.each do |key, value|
+ reverse_platform_map[value] ||= []
+ reverse_platform_map[value] << key
+ end
+
+ reverse_platform_map.each {|_, platforms| platforms.freeze }
+ end.freeze
+
+ def initialize(name, version, options = {}, &blk)
+ type = options["type"] || :runtime
+ super(name, version, type)
+
+ @autorequire = nil
+ @groups = Array(options["group"] || :default).map(&:to_sym)
+ @source = options["source"]
+ @platforms = Array(options["platforms"])
+ @env = options["env"]
+ @should_include = options.fetch("should_include", true)
+ @gemfile = options["gemfile"]
+
+ @autorequire = Array(options["require"] || []) if options.key?("require")
+ end
+
+ # 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 valid_platforms if @platforms.empty?
+
+ @gem_platforms ||= @platforms.map {|pl| PLATFORM_MAP[pl] }.compact.uniq
+
+ valid_platforms & @gem_platforms
+ end
+
+ def should_include?
+ @should_include && current_env? && current_platform?
+ end
+
+ def current_env?
+ return true unless @env
+ if @env.is_a?(Hash)
+ @env.all? do |key, val|
+ ENV[key.to_s] && (val.is_a?(String) ? ENV[key.to_s] == val : ENV[key.to_s] =~ val)
+ end
+ else
+ ENV[@env.to_s]
+ end
+ end
+
+ def current_platform?
+ return true if @platforms.empty?
+ @platforms.any? do |p|
+ Bundler.current_ruby.send("#{p}?")
+ end
+ end
+
+ def to_lock
+ out = super
+ out << "!" if source
+ out << "\n"
+ end
+
+ def specific?
+ super
+ rescue NoMethodError
+ requirement != ">= 0"
+ end
+ end
+end
diff --git a/lib/bundler/deployment.rb b/lib/bundler/deployment.rb
new file mode 100644
index 0000000000..291e158ca0
--- /dev/null
+++ b/lib/bundler/deployment.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require "bundler/shared_helpers"
+Bundler::SharedHelpers.major_deprecation 2, "Bundler no longer integrates with " \
+ "Capistrano, but Capistrano provides its own integration with " \
+ "Bundler via the capistrano-bundler gem. Use it instead."
+
+module Bundler
+ class Deployment
+ def self.define_task(context, task_method = :task, opts = {})
+ if defined?(Capistrano) && context.is_a?(Capistrano::Configuration)
+ context_name = "capistrano"
+ role_default = "{:except => {:no_release => true}}"
+ error_type = ::Capistrano::CommandError
+ else
+ context_name = "vlad"
+ role_default = "[:app]"
+ error_type = ::Rake::CommandFailedError
+ end
+
+ roles = context.fetch(:bundle_roles, false)
+ opts[:roles] = roles if roles
+
+ context.send :namespace, :bundle do
+ send :desc, <<-DESC
+ Install the current Bundler environment. By default, gems will be \
+ installed to the shared/bundle path. Gems in the development and \
+ test group will not be installed. The install command is executed \
+ with the --deployment and --quiet flags. If the bundle cmd cannot \
+ be found then you can override the bundle_cmd variable to specify \
+ which one it should use. The base path to the app is fetched from \
+ the :latest_release variable. Set it for custom deploy layouts.
+
+ You can override any of these defaults by setting the variables shown below.
+
+ N.B. bundle_roles must be defined before you require 'bundler/#{context_name}' \
+ in your deploy.rb file.
+
+ set :bundle_gemfile, "Gemfile"
+ set :bundle_dir, File.join(fetch(:shared_path), 'bundle')
+ set :bundle_flags, "--deployment --quiet"
+ set :bundle_without, [:development, :test]
+ set :bundle_with, [:mysql]
+ set :bundle_cmd, "bundle" # e.g. "/opt/ruby/bin/bundle"
+ set :bundle_roles, #{role_default} # e.g. [:app, :batch]
+ DESC
+ send task_method, :install, opts do
+ bundle_cmd = context.fetch(:bundle_cmd, "bundle")
+ bundle_flags = context.fetch(:bundle_flags, "--deployment --quiet")
+ bundle_dir = context.fetch(:bundle_dir, File.join(context.fetch(:shared_path), "bundle"))
+ bundle_gemfile = context.fetch(:bundle_gemfile, "Gemfile")
+ bundle_without = [*context.fetch(:bundle_without, [:development, :test])].compact
+ bundle_with = [*context.fetch(:bundle_with, [])].compact
+ app_path = context.fetch(:latest_release)
+ if app_path.to_s.empty?
+ raise error_type.new("Cannot detect current release path - make sure you have deployed at least once.")
+ end
+ args = ["--gemfile #{File.join(app_path, bundle_gemfile)}"]
+ args << "--path #{bundle_dir}" unless bundle_dir.to_s.empty?
+ args << bundle_flags.to_s
+ args << "--without #{bundle_without.join(" ")}" unless bundle_without.empty?
+ args << "--with #{bundle_with.join(" ")}" unless bundle_with.empty?
+
+ run "cd #{app_path} && #{bundle_cmd} install #{args.join(" ")}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/deprecate.rb b/lib/bundler/deprecate.rb
new file mode 100644
index 0000000000..f59533630e
--- /dev/null
+++ b/lib/bundler/deprecate.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+begin
+ require "rubygems/deprecate"
+rescue LoadError
+ # it's fine if it doesn't exist on the current RubyGems...
+ nil
+end
+
+module Bundler
+ # If Bundler::Deprecate is an autoload constant, we need to define it
+ if defined?(Bundler::Deprecate) && !autoload?(:Deprecate)
+ # nothing to do!
+ elsif defined? ::Deprecate
+ Deprecate = ::Deprecate
+ elsif defined? Gem::Deprecate
+ Deprecate = Gem::Deprecate
+ else
+ class Deprecate
+ end
+ end
+
+ unless Deprecate.respond_to?(:skip_during)
+ def Deprecate.skip_during
+ original = skip
+ self.skip = true
+ yield
+ ensure
+ self.skip = original
+ end
+ end
+
+ unless Deprecate.respond_to?(:skip)
+ def Deprecate.skip
+ @skip ||= false
+ end
+ end
+
+ unless Deprecate.respond_to?(:skip=)
+ def Deprecate.skip=(skip)
+ @skip = skip
+ end
+ end
+end
diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb
new file mode 100644
index 0000000000..1a2114ed93
--- /dev/null
+++ b/lib/bundler/dsl.rb
@@ -0,0 +1,615 @@
+# frozen_string_literal: true
+
+require "bundler/dependency"
+require "bundler/ruby_dsl"
+
+module Bundler
+ class Dsl
+ include RubyDsl
+
+ def self.evaluate(gemfile, lockfile, unlock)
+ builder = new
+ builder.eval_gemfile(gemfile)
+ builder.to_definition(lockfile, unlock)
+ end
+
+ VALID_PLATFORMS = Bundler::Dependency::PLATFORM_MAP.keys.freeze
+
+ VALID_KEYS = %w[group groups git path glob name branch ref tag require submodules
+ platform platforms type source install_if gemfile].freeze
+
+ attr_reader :gemspecs
+ attr_accessor :dependencies
+
+ def initialize
+ @source = nil
+ @sources = SourceList.new
+ @git_sources = {}
+ @dependencies = []
+ @groups = []
+ @install_conditionals = []
+ @optional_groups = []
+ @platforms = []
+ @env = nil
+ @ruby_version = nil
+ @gemspecs = []
+ @gemfile = nil
+ @gemfiles = []
+ add_git_sources
+ end
+
+ def eval_gemfile(gemfile, contents = nil)
+ expanded_gemfile_path = Pathname.new(gemfile).expand_path(@gemfile && @gemfile.parent)
+ original_gemfile = @gemfile
+ @gemfile = expanded_gemfile_path
+ @gemfiles << expanded_gemfile_path
+ contents ||= Bundler.read_file(@gemfile.to_s)
+ instance_eval(contents.dup.untaint, gemfile.to_s, 1)
+ rescue Exception => e
+ message = "There was an error " \
+ "#{e.is_a?(GemfileEvalError) ? "evaluating" : "parsing"} " \
+ "`#{File.basename gemfile.to_s}`: #{e.message}"
+
+ raise DSLError.new(message, gemfile, e.backtrace, contents)
+ ensure
+ @gemfile = original_gemfile
+ end
+
+ def gemspec(opts = nil)
+ opts ||= {}
+ path = opts[:path] || "."
+ glob = opts[:glob]
+ name = opts[:name]
+ development_group = opts[:development_group] || :development
+ expanded_path = gemfile_root.join(path)
+
+ gemspecs = Dir[File.join(expanded_path, "{,*}.gemspec")].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
+ when 1
+ specs = specs_by_name_and_version.values.first
+ spec = specs.find {|s| s.match_platform(Bundler.local_platform) } || specs.first
+
+ @gemspecs << spec
+
+ gem_platforms = Bundler::Dependency::REVERSE_PLATFORM_MAP[Bundler::GemHelpers.generic_local_platform]
+ gem spec.name, :name => spec.name, :path => path, :glob => glob, :platforms => gem_platforms
+
+ group(development_group) do
+ spec.development_dependencies.each do |dep|
+ gem dep.name, *(dep.requirement.as_list + [:type => :development])
+ end
+ end
+ when 0
+ raise InvalidOption, "There are no gemspecs at #{expanded_path}"
+ else
+ raise InvalidOption, "There are multiple gemspecs at #{expanded_path}. " \
+ "Please use the :name option to specify which one should be used"
+ end
+ end
+
+ def gem(name, *args)
+ options = args.last.is_a?(Hash) ? args.pop.dup : {}
+ options["gemfile"] = @gemfile
+ version = args || [">= 0"]
+
+ normalize_options(name, version, options)
+
+ dep = Dependency.new(name, version, options)
+
+ # if there's already a dependency with this name we try to prefer one
+ if current = @dependencies.find {|d| d.name == dep.name }
+ deleted_dep = @dependencies.delete(current) if current.type == :development
+
+ if current.requirement != dep.requirement
+ unless deleted_dep
+ return if dep.type == :development
+
+ update_prompt = ""
+
+ if File.basename(@gemfile) == Injector::INJECTED_GEMS
+ if dep.requirements_list.include?(">= 0") && !current.requirements_list.include?(">= 0")
+ update_prompt = ". Gem already added"
+ else
+ update_prompt = ". If you want to update the gem version, run `bundle update #{current.name}`"
+
+ update_prompt += ". You may also need to change the version requirement specified in the Gemfile if it's too restrictive." unless current.requirements_list.include?(">= 0")
+ end
+ end
+
+ raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \
+ "You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \
+ "#{update_prompt}"
+ end
+
+ else
+ Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
+ "You should probably keep only one of them.\n" \
+ "Remove any duplicate entries and specify the gem only once (per group).\n" \
+ "While it's not a problem now, it could cause errors if you change the version of one of them later."
+ end
+
+ if current.source != dep.source
+ unless deleted_dep
+ return if dep.type == :development
+ raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
+ "You specified that #{dep.name} (#{dep.requirement}) should come from " \
+ "#{current.source || "an unspecified source"} and #{dep.source}\n"
+ end
+ end
+ end
+
+ @dependencies << dep
+ end
+
+ def source(source, *args, &blk)
+ options = args.last.is_a?(Hash) ? args.pop.dup : {}
+ options = normalize_hash(options)
+ source = normalize_source(source)
+
+ if options.key?("type")
+ options["type"] = options["type"].to_s
+ unless Plugin.source?(options["type"])
+ raise InvalidOption, "No plugin sources available for #{options["type"]}"
+ end
+
+ unless block_given?
+ raise InvalidOption, "You need to pass a block to #source with :type option"
+ end
+
+ source_opts = options.merge("uri" => source)
+ with_source(@sources.add_plugin_source(options["type"], source_opts), &blk)
+ elsif block_given?
+ with_source(@sources.add_rubygems_source("remotes" => source), &blk)
+ else
+ check_primary_source_safety(@sources)
+ @sources.global_rubygems_source = source
+ end
+ end
+
+ def git_source(name, &block)
+ unless block_given?
+ raise InvalidOption, "You need to pass a block to #git_source"
+ end
+
+ if valid_keys.include?(name.to_s)
+ raise InvalidOption, "You cannot use #{name} as a git source. It " \
+ "is a reserved key. Reserved keys are: #{valid_keys.join(", ")}"
+ end
+
+ @git_sources[name.to_s] = block
+ end
+
+ def path(path, options = {}, &blk)
+ unless block_given?
+ msg = "You can no longer specify a path source by itself. Instead, \n" \
+ "either use the :path option on a gem, or specify the gems that \n" \
+ "bundler should find in the path source by passing a block to \n" \
+ "the path method, like: \n\n" \
+ " path 'dir/containing/rails' do\n" \
+ " gem 'rails'\n" \
+ " end\n\n"
+
+ raise DeprecatedError, msg if Bundler.feature_flag.disable_multisource?
+ SharedHelpers.major_deprecation(2, msg.strip)
+ end
+
+ source_options = normalize_hash(options).merge(
+ "path" => Pathname.new(path),
+ "root_path" => gemfile_root,
+ "gemspec" => gemspecs.find {|g| g.name == options["name"] }
+ )
+ source = @sources.add_path_source(source_options)
+ with_source(source, &blk)
+ end
+
+ def git(uri, options = {}, &blk)
+ unless block_given?
+ msg = "You can no longer specify a git source by itself. Instead, \n" \
+ "either use the :git option on a gem, or specify the gems that \n" \
+ "bundler should find in the git source by passing a block to \n" \
+ "the git method, like: \n\n" \
+ " git 'git://github.com/rails/rails.git' do\n" \
+ " gem 'rails'\n" \
+ " end"
+ raise DeprecatedError, msg
+ end
+
+ with_source(@sources.add_git_source(normalize_hash(options).merge("uri" => uri)), &blk)
+ end
+
+ def github(repo, options = {})
+ raise ArgumentError, "GitHub sources require a block" unless block_given?
+ raise DeprecatedError, "The #github method has been removed" if Bundler.feature_flag.skip_default_git_sources?
+ github_uri = @git_sources["github"].call(repo)
+ git_options = normalize_hash(options).merge("uri" => github_uri)
+ git_source = @sources.add_git_source(git_options)
+ with_source(git_source) { yield }
+ end
+
+ def to_definition(lockfile, unlock)
+ Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version, @optional_groups, @gemfiles)
+ end
+
+ def group(*args, &blk)
+ options = args.last.is_a?(Hash) ? args.pop.dup : {}
+ normalize_group_options(options, args)
+
+ @groups.concat args
+
+ if options["optional"]
+ optional_groups = args - @optional_groups
+ @optional_groups.concat optional_groups
+ end
+
+ yield
+ ensure
+ args.each { @groups.pop }
+ end
+
+ def install_if(*args)
+ @install_conditionals.concat args
+ yield
+ ensure
+ args.each { @install_conditionals.pop }
+ end
+
+ def platforms(*platforms)
+ @platforms.concat platforms
+ yield
+ ensure
+ platforms.each { @platforms.pop }
+ end
+ alias_method :platform, :platforms
+
+ def env(name)
+ old = @env
+ @env = name
+ yield
+ ensure
+ @env = old
+ end
+
+ def plugin(*args)
+ # Pass on
+ end
+
+ def method_missing(name, *args)
+ raise GemfileError, "Undefined local variable or method `#{name}' for Gemfile"
+ end
+
+ private
+
+ def add_git_sources
+ return if Bundler.feature_flag.skip_default_git_sources?
+
+ git_source(:github) do |repo_name|
+ warn_deprecated_git_source(:github, <<-'RUBY'.strip, 'Change any "reponame" :github sources to "username/reponame".')
+"https://github.com/#{repo_name}.git"
+ RUBY
+ # It would be better to use https instead of the git protocol, but this
+ # can break deployment of existing locked bundles when switching between
+ # different versions of Bundler. The change will be made in 2.0, which
+ # does not guarantee compatibility with the 1.x series.
+ #
+ # See https://github.com/bundler/bundler/pull/2569 for discussion
+ #
+ # This can be overridden by adding this code to your Gemfiles:
+ #
+ # git_source(:github) do |repo_name|
+ # repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
+ # "https://github.com/#{repo_name}.git"
+ # end
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
+ # TODO: 2.0 upgrade this setting to the default
+ if Bundler.feature_flag.github_https?
+ Bundler::SharedHelpers.major_deprecation 2, "The `github.https` setting will be removed"
+ "https://github.com/#{repo_name}.git"
+ else
+ "git://github.com/#{repo_name}.git"
+ end
+ end
+
+ # TODO: 2.0 remove this deprecated git source
+ git_source(:gist) do |repo_name|
+ warn_deprecated_git_source(:gist, '"https://gist.github.com/#{repo_name}.git"')
+
+ "https://gist.github.com/#{repo_name}.git"
+ end
+
+ # TODO: 2.0 remove this deprecated git source
+ git_source(:bitbucket) do |repo_name|
+ warn_deprecated_git_source(:bitbucket, <<-'RUBY'.strip)
+user_name, repo_name = repo_name.split("/")
+repo_name ||= user_name
+"https://#{user_name}@bitbucket.org/#{user_name}/#{repo_name}.git"
+ RUBY
+
+ user_name, repo_name = repo_name.split("/")
+ repo_name ||= user_name
+ "https://#{user_name}@bitbucket.org/#{user_name}/#{repo_name}.git"
+ end
+ end
+
+ def with_source(source)
+ old_source = @source
+ if block_given?
+ @source = source
+ yield
+ end
+ source
+ ensure
+ @source = old_source
+ end
+
+ def normalize_hash(opts)
+ opts.keys.each do |k|
+ opts[k.to_s] = opts.delete(k) unless k.is_a?(String)
+ end
+ opts
+ end
+
+ def valid_keys
+ @valid_keys ||= VALID_KEYS
+ end
+
+ def normalize_options(name, version, opts)
+ if name.is_a?(Symbol)
+ raise GemfileError, %(You need to specify gem names as Strings. Use 'gem "#{name}"' instead)
+ end
+ if name =~ /\s/
+ raise GemfileError, %('#{name}' is not a valid gem name because it contains whitespace)
+ end
+ if name.empty?
+ raise GemfileError, %(an empty gem name is not valid)
+ end
+
+ normalize_hash(opts)
+
+ git_names = @git_sources.keys.map(&:to_s)
+ validate_keys("gem '#{name}'", opts, valid_keys + git_names)
+
+ groups = @groups.dup
+ opts["group"] = opts.delete("groups") || opts["group"]
+ groups.concat Array(opts.delete("group"))
+ groups = [:default] if groups.empty?
+
+ install_if = @install_conditionals.dup
+ install_if.concat Array(opts.delete("install_if"))
+ install_if = install_if.reduce(true) do |memo, val|
+ memo && (val.respond_to?(:call) ? val.call : val)
+ end
+
+ platforms = @platforms.dup
+ opts["platforms"] = opts["platform"] || opts["platforms"]
+ platforms.concat Array(opts.delete("platforms"))
+ platforms.map!(&:to_sym)
+ platforms.each do |p|
+ next if VALID_PLATFORMS.include?(p)
+ raise GemfileError, "`#{p}` is not a valid platform. The available options are: #{VALID_PLATFORMS.inspect}"
+ end
+
+ # Save sources passed in a key
+ if opts.key?("source")
+ source = normalize_source(opts["source"])
+ opts["source"] = @sources.add_rubygems_source("remotes" => source)
+ end
+
+ git_name = (git_names & opts.keys).last
+ if @git_sources[git_name]
+ opts["git"] = @git_sources[git_name].call(opts[git_name])
+ end
+
+ %w[git path].each do |type|
+ next unless param = opts[type]
+ if version.first && version.first =~ /^\s*=?\s*(\d[^\s]*)\s*$/
+ options = opts.merge("name" => name, "version" => $1)
+ else
+ options = opts.dup
+ end
+ source = send(type, param, options) {}
+ opts["source"] = source
+ end
+
+ opts["source"] ||= @source
+ opts["env"] ||= @env
+ opts["platforms"] = platforms.dup
+ opts["group"] = groups
+ opts["should_include"] = install_if
+ end
+
+ def normalize_group_options(opts, groups)
+ normalize_hash(opts)
+
+ groups = groups.map {|group| ":#{group}" }.join(", ")
+ validate_keys("group #{groups}", opts, %w[optional])
+
+ opts["optional"] ||= false
+ end
+
+ def validate_keys(command, opts, valid_keys)
+ invalid_keys = opts.keys - valid_keys
+
+ git_source = opts.keys & @git_sources.keys.map(&:to_s)
+ if opts["branch"] && !(opts["git"] || opts["github"] || git_source.any?)
+ raise GemfileError, %(The `branch` option for `#{command}` is not allowed. Only gems with a git source can specify a branch)
+ end
+
+ return true unless invalid_keys.any?
+
+ message = String.new
+ message << "You passed #{invalid_keys.map {|k| ":" + k }.join(", ")} "
+ message << if invalid_keys.size > 1
+ "as options for #{command}, but they are invalid."
+ else
+ "as an option for #{command}, but it is invalid."
+ end
+
+ message << " Valid options are: #{valid_keys.join(", ")}."
+ message << " You may be able to resolve this by upgrading Bundler to the newest version."
+ raise InvalidOption, message
+ end
+
+ def normalize_source(source)
+ case source
+ when :gemcutter, :rubygems, :rubyforge
+ Bundler::SharedHelpers.major_deprecation 2, "The source :#{source} is deprecated because HTTP " \
+ "requests are insecure.\nPlease change your source to 'https://" \
+ "rubygems.org' if possible, or 'http://rubygems.org' if not."
+ "http://rubygems.org"
+ when String
+ source
+ else
+ raise GemfileError, "Unknown source '#{source}'"
+ end
+ end
+
+ def check_primary_source_safety(source_list)
+ return if source_list.rubygems_primary_remotes.empty? && source_list.global_rubygems_source.nil?
+
+ if Bundler.feature_flag.disable_multisource?
+ msg = "This Gemfile contains multiple primary sources. " \
+ "Each source after the first must include a block to indicate which gems " \
+ "should come from that source"
+ unless Bundler.feature_flag.bundler_2_mode?
+ msg += ". To downgrade this error to a warning, run " \
+ "`bundle config --delete disable_multisource`"
+ end
+ raise GemfileEvalError, msg
+ else
+ Bundler::SharedHelpers.major_deprecation 2, "Your Gemfile contains multiple primary sources. " \
+ "Using `source` more than once without a block is a security risk, and " \
+ "may result in installing unexpected gems. To resolve this warning, use " \
+ "a block to indicate which gems should come from the secondary source. " \
+ "To upgrade this warning to an error, run `bundle config " \
+ "disable_multisource true`."
+ end
+ end
+
+ def warn_deprecated_git_source(name, replacement, additional_message = nil)
+ # TODO: 2.0 remove deprecation
+ additional_message &&= " #{additional_message}"
+ replacement = if replacement.count("\n").zero?
+ "{|repo_name| #{replacement} }"
+ else
+ "do |repo_name|\n#{replacement.to_s.gsub(/^/, " ")}\n end"
+ end
+
+ Bundler::SharedHelpers.major_deprecation 2, <<-EOS
+The :#{name} git source is deprecated, and will be removed in Bundler 2.0.#{additional_message} Add this code to the top of your Gemfile to ensure it continues to work:
+
+ git_source(:#{name}) #{replacement}
+
+ EOS
+ end
+
+ class DSLError < GemfileError
+ # @return [String] the description that should be presented to the user.
+ #
+ attr_reader :description
+
+ # @return [String] the path of the dsl file that raised the exception.
+ #
+ attr_reader :dsl_path
+
+ # @return [Exception] the backtrace of the exception raised by the
+ # evaluation of the dsl file.
+ #
+ attr_reader :backtrace
+
+ # @param [Exception] backtrace @see backtrace
+ # @param [String] dsl_path @see dsl_path
+ #
+ def initialize(description, dsl_path, backtrace, contents = nil)
+ @status_code = $!.respond_to?(:status_code) && $!.status_code
+
+ @description = description
+ @dsl_path = dsl_path
+ @backtrace = backtrace
+ @contents = contents
+ end
+
+ def status_code
+ @status_code || super
+ end
+
+ # @return [String] the contents of the DSL that cause the exception to
+ # be raised.
+ #
+ def contents
+ @contents ||= begin
+ dsl_path && File.exist?(dsl_path) && File.read(dsl_path)
+ end
+ end
+
+ # The message of the exception reports the content of podspec for the
+ # line that generated the original exception.
+ #
+ # @example Output
+ #
+ # Invalid podspec at `RestKit.podspec` - undefined method
+ # `exclude_header_search_paths=' for #<Pod::Specification for
+ # `RestKit/Network (0.9.3)`>
+ #
+ # from spec-repos/master/RestKit/0.9.3/RestKit.podspec:36
+ # -------------------------------------------
+ # # because it would break: #import <CoreData/CoreData.h>
+ # > ns.exclude_header_search_paths = 'Code/RestKit.h'
+ # end
+ # -------------------------------------------
+ #
+ # @return [String] the message of the exception.
+ #
+ def to_s
+ @to_s ||= begin
+ trace_line, description = parse_line_number_from_description
+
+ m = String.new("\n[!] ")
+ m << description
+ m << ". Bundler cannot continue.\n"
+
+ return m unless backtrace && dsl_path && contents
+
+ trace_line = backtrace.find {|l| l.include?(dsl_path.to_s) } || trace_line
+ return m unless trace_line
+ line_numer = trace_line.split(":")[1].to_i - 1
+ return m unless line_numer
+
+ lines = contents.lines.to_a
+ indent = " # "
+ indicator = indent.tr("#", ">")
+ first_line = line_numer.zero?
+ last_line = (line_numer == (lines.count - 1))
+
+ m << "\n"
+ m << "#{indent}from #{trace_line.gsub(/:in.*$/, "")}\n"
+ m << "#{indent}-------------------------------------------\n"
+ m << "#{indent}#{lines[line_numer - 1]}" unless first_line
+ m << "#{indicator}#{lines[line_numer]}"
+ m << "#{indent}#{lines[line_numer + 1]}" unless last_line
+ m << "\n" unless m.end_with?("\n")
+ m << "#{indent}-------------------------------------------\n"
+ end
+ end
+
+ private
+
+ def parse_line_number_from_description
+ description = self.description
+ if dsl_path && description =~ /((#{Regexp.quote File.expand_path(dsl_path)}|#{Regexp.quote dsl_path.to_s}):\d+)/
+ trace_line = Regexp.last_match[1]
+ description = description.sub(/#{Regexp.quote trace_line}:\s*/, "").sub("\n", " - ")
+ end
+ [trace_line, description]
+ end
+ end
+
+ def gemfile_root
+ @gemfile ||= Bundler.default_gemfile
+ @gemfile.dirname
+ end
+ end
+end
diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb
new file mode 100644
index 0000000000..9a00b64e0e
--- /dev/null
+++ b/lib/bundler/endpoint_specification.rb
@@ -0,0 +1,141 @@
+# frozen_string_literal: true
+
+module Bundler
+ # used for Creating Specifications from the Gemcutter Endpoint
+ class EndpointSpecification < Gem::Specification
+ ILLFORMED_MESSAGE = 'Ill-formed requirement ["#<YAML::Syck::DefaultKey'.freeze
+ include MatchPlatform
+
+ attr_reader :name, :version, :platform, :required_rubygems_version, :required_ruby_version, :checksum
+ attr_accessor :source, :remote, :dependencies
+
+ def initialize(name, version, platform, dependencies, metadata = nil)
+ super()
+ @name = name
+ @version = Gem::Version.create version
+ @platform = platform
+ @dependencies = dependencies.map {|dep, reqs| build_dependency(dep, reqs) }
+
+ @loaded_from = nil
+ @remote_specification = nil
+
+ parse_metadata(metadata)
+ end
+
+ def fetch_platform
+ @platform
+ end
+
+ # needed for standalone, load required_paths from local gemspec
+ # after the gem is installed
+ def require_paths
+ if @remote_specification
+ @remote_specification.require_paths
+ elsif _local_specification
+ _local_specification.require_paths
+ else
+ super
+ end
+ end
+
+ # needed for inline
+ def load_paths
+ # remote specs aren't installed, and can't have load_paths
+ if _local_specification
+ _local_specification.load_paths
+ else
+ super
+ end
+ end
+
+ # needed for binstubs
+ def executables
+ if @remote_specification
+ @remote_specification.executables
+ elsif _local_specification
+ _local_specification.executables
+ else
+ super
+ end
+ end
+
+ # needed for bundle clean
+ def bindir
+ if @remote_specification
+ @remote_specification.bindir
+ elsif _local_specification
+ _local_specification.bindir
+ else
+ super
+ end
+ end
+
+ # needed for post_install_messages during install
+ def post_install_message
+ if @remote_specification
+ @remote_specification.post_install_message
+ elsif _local_specification
+ _local_specification.post_install_message
+ else
+ super
+ end
+ end
+
+ # needed for "with native extensions" during install
+ def extensions
+ if @remote_specification
+ @remote_specification.extensions
+ elsif _local_specification
+ _local_specification.extensions
+ else
+ super
+ end
+ end
+
+ def _local_specification
+ return unless @loaded_from && File.exist?(local_specification_path)
+ eval(File.read(local_specification_path)).tap do |spec|
+ spec.loaded_from = @loaded_from
+ end
+ end
+
+ def __swap__(spec)
+ SharedHelpers.ensure_same_dependencies(self, dependencies, spec.dependencies)
+ @remote_specification = spec
+ end
+
+ private
+
+ def local_specification_path
+ "#{base_dir}/specifications/#{full_name}.gemspec"
+ end
+
+ def parse_metadata(data)
+ return unless data
+ data.each do |k, v|
+ next unless v
+ case k.to_s
+ when "checksum"
+ @checksum = v.last
+ when "rubygems"
+ @required_rubygems_version = Gem::Requirement.new(v)
+ when "ruby"
+ @required_ruby_version = Gem::Requirement.new(v)
+ end
+ end
+ rescue StandardError => e
+ raise GemspecError, "There was an error parsing the metadata for the gem #{name} (#{version}): #{e.class}\n#{e}\nThe metadata was #{data.inspect}"
+ end
+
+ def build_dependency(name, requirements)
+ Gem::Dependency.new(name, requirements)
+ rescue ArgumentError => e
+ raise unless e.message.include?(ILLFORMED_MESSAGE)
+ puts # we shouldn't print the error message on the "fetching info" status line
+ raise GemspecError,
+ "Unfortunately, the gem #{name} (#{version}) has an invalid " \
+ "gemspec.\nPlease ask the gem author to yank the bad version to fix " \
+ "this issue. For more information, see http://bit.ly/syck-defaultkey."
+ end
+ end
+end
diff --git a/lib/bundler/env.rb b/lib/bundler/env.rb
new file mode 100644
index 0000000000..51738139fa
--- /dev/null
+++ b/lib/bundler/env.rb
@@ -0,0 +1,155 @@
+# frozen_string_literal: true
+
+require "bundler/rubygems_integration"
+require "bundler/source/git/git_proxy"
+
+module Bundler
+ class Env
+ def self.write(io)
+ io.write report
+ end
+
+ def self.report(options = {})
+ print_gemfile = options.delete(:print_gemfile) { true }
+ print_gemspecs = options.delete(:print_gemspecs) { true }
+
+ out = String.new
+ append_formatted_table("Environment", environment, out)
+ append_formatted_table("Bundler Build Metadata", BuildMetadata.to_h, out)
+
+ unless Bundler.settings.all.empty?
+ out << "\n## Bundler settings\n\n```\n"
+ Bundler.settings.all.each do |setting|
+ out << setting << "\n"
+ Bundler.settings.pretty_values_for(setting).each do |line|
+ out << " " << line << "\n"
+ end
+ end
+ out << "```\n"
+ end
+
+ return out unless SharedHelpers.in_bundle?
+
+ if print_gemfile
+ gemfiles = [Bundler.default_gemfile]
+ begin
+ gemfiles = Bundler.definition.gemfiles
+ rescue GemfileNotFound
+ nil
+ end
+
+ out << "\n## Gemfile\n"
+ gemfiles.each do |gemfile|
+ out << "\n### #{Pathname.new(gemfile).relative_path_from(SharedHelpers.pwd)}\n\n"
+ out << "```ruby\n" << read_file(gemfile).chomp << "\n```\n"
+ end
+
+ out << "\n### #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}\n\n"
+ out << "```\n" << read_file(Bundler.default_lockfile).chomp << "\n```\n"
+ end
+
+ if print_gemspecs
+ dsl = Dsl.new.tap {|d| d.eval_gemfile(Bundler.default_gemfile) }
+ out << "\n## Gemspecs\n" unless dsl.gemspecs.empty?
+ dsl.gemspecs.each do |gs|
+ out << "\n### #{File.basename(gs.loaded_from)}"
+ out << "\n\n```ruby\n" << read_file(gs.loaded_from).chomp << "\n```\n"
+ end
+ end
+
+ out
+ end
+
+ def self.read_file(filename)
+ Bundler.read_file(filename.to_s).strip
+ rescue Errno::ENOENT
+ "<No #{filename} found>"
+ rescue RuntimeError => e
+ "#{e.class}: #{e.message}"
+ end
+
+ def self.ruby_version
+ str = String.new("#{RUBY_VERSION}")
+ if RUBY_VERSION < "1.9"
+ str << " (#{RUBY_RELEASE_DATE}"
+ str << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
+ str << ") [#{RUBY_PLATFORM}]"
+ else
+ str << "p#{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
+ str << " (#{RUBY_RELEASE_DATE} revision #{RUBY_REVISION}) [#{RUBY_PLATFORM}]"
+ end
+ end
+
+ def self.git_version
+ Bundler::Source::Git::GitProxy.new(nil, nil, nil).full_version
+ rescue Bundler::Source::Git::GitNotInstalledError
+ "not installed"
+ end
+
+ def self.version_of(script)
+ return "not installed" unless Bundler.which(script)
+ `#{script} --version`.chomp
+ end
+
+ def self.chruby_version
+ return "not installed" unless Bundler.which("chruby-exec")
+ `chruby-exec -- chruby --version`.
+ sub(/.*^chruby: (#{Gem::Version::VERSION_PATTERN}).*/m, '\1')
+ end
+
+ def self.environment
+ out = []
+
+ out << ["Bundler", Bundler::VERSION]
+ out << [" Platforms", Gem.platforms.join(", ")]
+ out << ["Ruby", ruby_version]
+ out << [" Full Path", Gem.ruby]
+ out << [" Config Dir", Pathname.new(Gem::ConfigFile::SYSTEM_WIDE_CONFIG_FILE).dirname]
+ out << ["RubyGems", Gem::VERSION]
+ out << [" Gem Home", ENV.fetch("GEM_HOME") { Gem.dir }]
+ out << [" Gem Path", ENV.fetch("GEM_PATH") { Gem.path.join(File::PATH_SEPARATOR) }]
+ out << [" User Path", Gem.user_dir]
+ out << [" Bin Dir", Gem.bindir]
+ if defined?(OpenSSL)
+ out << ["OpenSSL"]
+ out << [" Compiled", OpenSSL::OPENSSL_VERSION] if defined?(OpenSSL::OPENSSL_VERSION)
+ out << [" Loaded", OpenSSL::OPENSSL_LIBRARY_VERSION] if defined?(OpenSSL::OPENSSL_LIBRARY_VERSION)
+ out << [" Cert File", OpenSSL::X509::DEFAULT_CERT_FILE] if defined?(OpenSSL::X509::DEFAULT_CERT_FILE)
+ out << [" Cert Dir", OpenSSL::X509::DEFAULT_CERT_DIR] if defined?(OpenSSL::X509::DEFAULT_CERT_DIR)
+ end
+ out << ["Tools"]
+ out << [" Git", git_version]
+ out << [" RVM", ENV.fetch("rvm_version") { version_of("rvm") }]
+ out << [" rbenv", version_of("rbenv")]
+ out << [" chruby", chruby_version]
+
+ %w[rubygems-bundler open_gem].each do |name|
+ 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}
+ shebang = File.read(exe).lines.first
+ shebang.sub!(/^#!\s*/, "")
+ unless shebang.start_with?(Gem.ruby, "/usr/bin/env ruby")
+ out << ["Gem.ruby", Gem.ruby]
+ out << ["bundle #!", shebang]
+ end
+ end
+
+ out
+ end
+
+ def self.append_formatted_table(title, pairs, out)
+ return if pairs.empty?
+ out << "\n" unless out.empty?
+ out << "## #{title}\n\n```\n"
+ ljust = pairs.map {|k, _v| k.to_s.length }.max
+ pairs.each do |k, v|
+ out << "#{k.to_s.ljust(ljust)} #{v}\n"
+ end
+ out << "```\n"
+ end
+
+ private_class_method :read_file, :ruby_version, :git_version, :append_formatted_table, :version_of, :chruby_version
+ end
+end
diff --git a/lib/bundler/environment_preserver.rb b/lib/bundler/environment_preserver.rb
new file mode 100644
index 0000000000..af7c1ef0a4
--- /dev/null
+++ b/lib/bundler/environment_preserver.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+module Bundler
+ class EnvironmentPreserver
+ INTENTIONALLY_NIL = "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL".freeze
+ BUNDLER_KEYS = %w[
+ BUNDLE_BIN_PATH
+ BUNDLE_GEMFILE
+ BUNDLER_ORIG_MANPATH
+ BUNDLER_VERSION
+ GEM_HOME
+ GEM_PATH
+ MANPATH
+ PATH
+ RB_USER_INSTALL
+ RUBYLIB
+ RUBYOPT
+ ].map(&:freeze).freeze
+ BUNDLER_PREFIX = "BUNDLER_ORIG_".freeze
+
+ # @param env [ENV]
+ # @param keys [Array<String>]
+ def initialize(env, keys)
+ @original = env.to_hash
+ @keys = keys
+ @prefix = BUNDLER_PREFIX
+ end
+
+ # @return [Hash]
+ def backup
+ env = @original.clone
+ @keys.each do |key|
+ value = env[key]
+ if !value.nil? && !value.empty?
+ env[@prefix + key] ||= value
+ elsif value.nil?
+ env[@prefix + key] ||= INTENTIONALLY_NIL
+ end
+ end
+ env
+ end
+
+ # @return [Hash]
+ def restore
+ env = @original.clone
+ @keys.each do |key|
+ value_original = env[@prefix + key]
+ next if value_original.nil? || value_original.empty?
+ if value_original == INTENTIONALLY_NIL
+ env.delete(key)
+ else
+ env[key] = value_original
+ end
+ env.delete(@prefix + key)
+ end
+ env
+ end
+ end
+end
diff --git a/lib/bundler/errors.rb b/lib/bundler/errors.rb
new file mode 100644
index 0000000000..e471bce0b6
--- /dev/null
+++ b/lib/bundler/errors.rb
@@ -0,0 +1,158 @@
+# frozen_string_literal: true
+
+module Bundler
+ class BundlerError < StandardError
+ def self.status_code(code)
+ define_method(:status_code) { code }
+ if match = BundlerError.all_errors.find {|_k, v| v == code }
+ error, _ = match
+ raise ArgumentError,
+ "Trying to register #{self} for status code #{code} but #{error} is already registered"
+ end
+ BundlerError.all_errors[self] = code
+ end
+
+ def self.all_errors
+ @all_errors ||= {}
+ end
+ end
+
+ class GemfileError < BundlerError; status_code(4); end
+ 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 GemNotFound < BundlerError; status_code(7); end
+ class InstallHookError < BundlerError; status_code(8); end
+ class GemfileNotFound < BundlerError; status_code(10); end
+ class GitError < BundlerError; status_code(11); end
+ class DeprecatedError < BundlerError; status_code(12); end
+ class PathError < BundlerError; status_code(13); end
+ class GemspecError < BundlerError; status_code(14); end
+ class InvalidOption < BundlerError; status_code(15); end
+ class ProductionError < BundlerError; status_code(16); end
+ class HTTPError < BundlerError
+ status_code(17)
+ def filter_uri(uri)
+ URICredentialsFilter.credential_filtered_uri(uri)
+ end
+ end
+ class RubyVersionMismatch < BundlerError; status_code(18); end
+ class SecurityError < BundlerError; status_code(19); end
+ class LockfileError < BundlerError; status_code(20); end
+ 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 GemfileEvalError < GemfileError; end
+ class MarshalError < StandardError; end
+
+ class PermissionError < BundlerError
+ def initialize(path, permission_type = :write)
+ @path = path
+ @permission_type = permission_type
+ end
+
+ def action
+ case @permission_type
+ when :read then "read from"
+ when :write then "write to"
+ when :executable, :exec then "execute"
+ else @permission_type.to_s
+ end
+ end
+
+ def message
+ "There was an error while trying to #{action} `#{@path}`. " \
+ "It is likely that you need to grant #{@permission_type} permissions " \
+ "for that path."
+ end
+
+ status_code(23)
+ end
+
+ class GemRequireError < BundlerError
+ attr_reader :orig_exception
+
+ def initialize(orig_exception, msg)
+ full_message = msg + "\nGem Load Error is: #{orig_exception.message}\n"\
+ "Backtrace for gem load error is:\n"\
+ "#{orig_exception.backtrace.join("\n")}\n"\
+ "Bundler Error Backtrace:\n"
+ super(full_message)
+ @orig_exception = orig_exception
+ end
+
+ status_code(24)
+ end
+
+ class YamlSyntaxError < BundlerError
+ attr_reader :orig_exception
+
+ def initialize(orig_exception, msg)
+ super(msg)
+ @orig_exception = orig_exception
+ end
+
+ status_code(25)
+ end
+
+ class TemporaryResourceError < PermissionError
+ def message
+ "There was an error while trying to #{action} `#{@path}`. " \
+ "Some resource was temporarily unavailable. It's suggested that you try" \
+ "the operation again."
+ end
+
+ status_code(26)
+ end
+
+ class VirtualProtocolError < BundlerError
+ def message
+ "There was an error relating to virtualization and file access." \
+ "It is likely that you need to grant access to or mount some file system correctly."
+ end
+
+ status_code(27)
+ end
+
+ class OperationNotSupportedError < PermissionError
+ def message
+ "Attempting to #{action} `#{@path}` is unsupported by your OS."
+ end
+
+ status_code(28)
+ end
+
+ class NoSpaceOnDeviceError < PermissionError
+ def message
+ "There was an error while trying to #{action} `#{@path}`. " \
+ "There was insufficient space remaining on the device."
+ end
+
+ status_code(31)
+ end
+
+ class GenericSystemCallError < BundlerError
+ attr_reader :underlying_error
+
+ def initialize(underlying_error, message)
+ @underlying_error = underlying_error
+ super("#{message}\nThe underlying system error is #{@underlying_error.class}: #{@underlying_error}")
+ end
+
+ status_code(32)
+ end
+end
diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb
new file mode 100644
index 0000000000..249170c4b2
--- /dev/null
+++ b/lib/bundler/feature_flag.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+module Bundler
+ class FeatureFlag
+ def self.settings_flag(flag, &default)
+ unless Bundler::Settings::BOOL_KEYS.include?(flag.to_s)
+ raise "Cannot use `#{flag}` as a settings feature flag since it isn't a bool key"
+ end
+
+ settings_method("#{flag}?", flag, &default)
+ end
+ private_class_method :settings_flag
+
+ def self.settings_option(key, &default)
+ settings_method(key, key, &default)
+ end
+ private_class_method :settings_option
+
+ def self.settings_method(name, key, &default)
+ define_method(name) do
+ value = Bundler.settings[key]
+ value = instance_eval(&default) if value.nil? && !default.nil?
+ value
+ end
+ end
+ private_class_method :settings_method
+
+ (1..10).each {|v| define_method("bundler_#{v}_mode?") { major_version >= v } }
+
+ settings_flag(:allow_bundler_dependency_conflicts) { bundler_2_mode? }
+ settings_flag(:allow_offline_install) { bundler_2_mode? }
+ settings_flag(:auto_clean_without_path) { bundler_2_mode? }
+ settings_flag(:auto_config_jobs) { bundler_2_mode? }
+ settings_flag(:cache_all) { bundler_2_mode? }
+ settings_flag(:cache_command_is_package) { bundler_2_mode? }
+ settings_flag(:console_command) { !bundler_2_mode? }
+ settings_flag(:default_install_uses_path) { bundler_2_mode? }
+ settings_flag(:deployment_means_frozen) { bundler_2_mode? }
+ settings_flag(:disable_multisource) { bundler_2_mode? }
+ settings_flag(:error_on_stderr) { bundler_2_mode? }
+ settings_flag(:forget_cli_options) { bundler_2_mode? }
+ settings_flag(:global_path_appends_ruby_scope) { bundler_2_mode? }
+ settings_flag(:global_gem_cache) { bundler_2_mode? }
+ settings_flag(:init_gems_rb) { bundler_2_mode? }
+ settings_flag(:list_command) { bundler_2_mode? }
+ settings_flag(:lockfile_uses_separate_rubygems_sources) { bundler_2_mode? }
+ settings_flag(:only_update_to_newer_versions) { bundler_2_mode? }
+ settings_flag(:path_relative_to_cwd) { bundler_2_mode? }
+ settings_flag(:plugins) { @bundler_version >= Gem::Version.new("1.14") }
+ settings_flag(:prefer_gems_rb) { bundler_2_mode? }
+ settings_flag(:print_only_version_number) { bundler_2_mode? }
+ settings_flag(:setup_makes_kernel_gem_public) { !bundler_2_mode? }
+ settings_flag(:skip_default_git_sources) { bundler_2_mode? }
+ settings_flag(:specific_platform) { bundler_2_mode? }
+ settings_flag(:suppress_install_using_messages) { bundler_2_mode? }
+ settings_flag(:unlock_source_unlocks_spec) { !bundler_2_mode? }
+ settings_flag(:update_requires_all_flag) { bundler_2_mode? }
+ settings_flag(:use_gem_version_promoter_for_major_updates) { bundler_2_mode? }
+ settings_flag(:viz_command) { !bundler_2_mode? }
+
+ settings_option(:default_cli_command) { bundler_2_mode? ? :cli_help : :install }
+
+ settings_method(:github_https?, "github.https") { bundler_2_mode? }
+
+ def initialize(bundler_version)
+ @bundler_version = Gem::Version.create(bundler_version)
+ end
+
+ def major_version
+ @bundler_version.segments.first
+ end
+ private :major_version
+ end
+end
diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb
new file mode 100644
index 0000000000..4dd42e42ff
--- /dev/null
+++ b/lib/bundler/fetcher.rb
@@ -0,0 +1,312 @@
+# frozen_string_literal: true
+
+require "bundler/vendored_persistent"
+require "cgi"
+require "securerandom"
+require "zlib"
+
+module Bundler
+ # Handles all the fetching with the rubygems server
+ class Fetcher
+ autoload :CompactIndex, "bundler/fetcher/compact_index"
+ autoload :Downloader, "bundler/fetcher/downloader"
+ autoload :Dependency, "bundler/fetcher/dependency"
+ autoload :Index, "bundler/fetcher/index"
+
+ # This error is raised when it looks like the network is down
+ class NetworkDownError < HTTPError; end
+ # This error is raised if the API returns a 413 (only printed in verbose)
+ class FallbackError < HTTPError; end
+ # This is the error raised if OpenSSL fails the cert verification
+ class CertificateFailureError < HTTPError
+ def initialize(remote_uri)
+ remote_uri = filter_uri(remote_uri)
+ super "Could not verify the SSL certificate for #{remote_uri}.\nThere" \
+ " 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" \
+ " http://bit.ly/ruby-ssl. To connect without using SSL, edit your Gemfile" \
+ " sources and change 'https' to 'http'."
+ end
+ end
+ # This is the error raised when a source is HTTPS and OpenSSL didn't load
+ 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."
+ end
+ end
+ # This error is raised if HTTP authentication is required, but not provided.
+ class AuthenticationRequiredError < HTTPError
+ def initialize(remote_uri)
+ remote_uri = filter_uri(remote_uri)
+ super "Authentication is required for #{remote_uri}.\n" \
+ "Please supply credentials for this source. You can do this by running:\n" \
+ " bundle config #{remote_uri} username:password"
+ end
+ end
+ # This error is raised if HTTP authentication is provided, but incorrect.
+ class BadAuthenticationError < HTTPError
+ def initialize(remote_uri)
+ remote_uri = filter_uri(remote_uri)
+ super "Bad username or password for #{remote_uri}.\n" \
+ "Please double-check your credentials and correct them."
+ 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,
+ :HTTPForbidden, :HTTPInsufficientStorage, :HTTPMethodNotAllowed,
+ :HTTPMovedPermanently, :HTTPNoContent, :HTTPNotFound,
+ :HTTPNotImplemented, :HTTPPreconditionFailed, :HTTPRequestEntityTooLarge,
+ :HTTPRequestURITooLong, :HTTPUnauthorized, :HTTPUnprocessableEntity,
+ :HTTPUnsupportedMediaType, :HTTPVersionNotSupported].freeze
+ FAIL_ERRORS = begin
+ fail_errors = [AuthenticationRequiredError, BadAuthenticationError, FallbackError]
+ fail_errors << Gem::Requirement::BadRequirementError if defined?(Gem::Requirement::BadRequirementError)
+ fail_errors.concat(NET_ERRORS.map {|e| SharedHelpers.const_get_safely(e, Net) }.compact)
+ end.freeze
+
+ class << self
+ attr_accessor :disable_endpoint, :api_timeout, :redirect_limit, :max_retries
+ end
+
+ self.redirect_limit = Bundler.settings[:redirect] # How many redirects to allow in one request
+ self.api_timeout = Bundler.settings[:timeout] # How long to wait for each API call
+ self.max_retries = Bundler.settings[:retry] # How many retries for the API call
+
+ def initialize(remote)
+ @remote = remote
+
+ Socket.do_not_reverse_lookup = true
+ connection # create persistent connection
+ end
+
+ def uri
+ @remote.anonymized_uri
+ end
+
+ # fetch a gem specification
+ def fetch_spec(spec)
+ spec -= [nil, "ruby", ""]
+ spec_file_name = "#{spec.join "-"}.gemspec"
+
+ uri = URI.parse("#{remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz")
+ if uri.scheme == "file"
+ Bundler.load_marshal Bundler.rubygems.inflate(Gem.read_binary(uri.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)
+ end
+ rescue MarshalError
+ raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \
+ "Your network or your gem server is probably having issues right now."
+ end
+
+ # return the specs in the bundler format as an index with retries
+ def specs_with_retry(gem_names, source)
+ Bundler::Retry.new("fetcher", FAIL_ERRORS).attempts do
+ specs(gem_names, source)
+ end
+ end
+
+ # return the specs in the bundler format as an index
+ def specs(gem_names, source)
+ old = Bundler.rubygems.sources
+ index = Bundler::Index.new
+
+ if Bundler::Fetcher.disable_endpoint
+ @use_api = false
+ specs = fetchers.last.specs(gem_names)
+ else
+ specs = []
+ fetchers.shift until fetchers.first.available? || fetchers.empty?
+ fetchers.dup.each do |f|
+ break unless f.api_fetcher? && !gem_names || !specs = f.specs(gem_names)
+ fetchers.delete(f)
+ end
+ @use_api = false if fetchers.none?(&:api_fetcher?)
+ end
+
+ specs.each do |name, version, platform, dependencies, metadata|
+ next if name == "bundler"
+ spec = if dependencies
+ EndpointSpecification.new(name, version, platform, dependencies, metadata)
+ else
+ RemoteSpecification.new(name, version, platform, self)
+ end
+ spec.source = source
+ spec.remote = @remote
+ index << spec
+ end
+
+ index
+ rescue CertificateFailureError
+ Bundler.ui.info "" if gem_names && use_api # newline after dots
+ raise
+ ensure
+ Bundler.rubygems.sources = old
+ end
+
+ def use_api
+ return @use_api if defined?(@use_api)
+
+ fetchers.shift until fetchers.first.available?
+
+ @use_api = if remote_uri.scheme == "file" || Bundler::Fetcher.disable_endpoint
+ false
+ else
+ fetchers.first.api_fetcher?
+ end
+ end
+
+ def user_agent
+ @user_agent ||= begin
+ ruby = Bundler::RubyVersion.system
+
+ agent = String.new("bundler/#{Bundler::VERSION}")
+ agent << " rubygems/#{Gem::VERSION}"
+ agent << " ruby/#{ruby.versions_string(ruby.versions)}"
+ agent << " (#{ruby.host})"
+ agent << " command/#{ARGV.first}"
+
+ if ruby.engine != "ruby"
+ # engine_version raises on unknown engines
+ engine_version = begin
+ ruby.engine_versions
+ rescue RuntimeError
+ "???"
+ end
+ agent << " #{ruby.engine}/#{ruby.versions_string(engine_version)}"
+ end
+
+ agent << " options/#{Bundler.settings.all.join(",")}"
+
+ agent << " ci/#{cis.join(",")}" if cis.any?
+
+ # add a random ID so we can consolidate runs server-side
+ agent << " " << SecureRandom.hex(8)
+
+ # add any user agent strings set in the config
+ extra_ua = Bundler.settings[:user_agent]
+ agent << " " << extra_ua if extra_ua
+
+ agent
+ end
+ end
+
+ def fetchers
+ @fetchers ||= FETCHERS.map {|f| f.new(downloader, @remote, uri) }
+ end
+
+ def http_proxy
+ return unless uri = connection.proxy_uri
+ uri.to_s
+ end
+
+ def inspect
+ "#<#{self.class}:0x#{object_id} uri=#{uri}>"
+ end
+
+ private
+
+ FETCHERS = [CompactIndex, Dependency, Index].freeze
+
+ def cis
+ env_cis = {
+ "TRAVIS" => "travis",
+ "CIRCLECI" => "circle",
+ "SEMAPHORE" => "semaphore",
+ "JENKINS_URL" => "jenkins",
+ "BUILDBOX" => "buildbox",
+ "GO_SERVER_URL" => "go",
+ "SNAP_CI" => "snap",
+ "CI_NAME" => ENV["CI_NAME"],
+ "CI" => "ci"
+ }
+ env_cis.find_all {|env, _| ENV[env] }.map {|_, ci| ci }
+ end
+
+ def connection
+ @connection ||= begin
+ needs_ssl = remote_uri.scheme == "https" ||
+ Bundler.settings[:ssl_verify_mode] ||
+ Bundler.settings[:ssl_client_cert]
+ raise SSLError if needs_ssl && !defined?(OpenSSL::SSL)
+
+ con = PersistentHTTP.new "bundler", :ENV
+ if gem_proxy = Bundler.rubygems.configuration[:http_proxy]
+ con.proxy = URI.parse(gem_proxy) if gem_proxy != :no_proxy
+ end
+
+ if remote_uri.scheme == "https"
+ con.verify_mode = (Bundler.settings[:ssl_verify_mode] ||
+ OpenSSL::SSL::VERIFY_PEER)
+ con.cert_store = bundler_cert_store
+ end
+
+ ssl_client_cert = Bundler.settings[:ssl_client_cert] ||
+ (Bundler.rubygems.configuration.ssl_client_cert if
+ Bundler.rubygems.configuration.respond_to?(:ssl_client_cert))
+ if ssl_client_cert
+ pem = File.read(ssl_client_cert)
+ con.cert = OpenSSL::X509::Certificate.new(pem)
+ con.key = OpenSSL::PKey::RSA.new(pem)
+ end
+
+ con.read_timeout = Fetcher.api_timeout
+ con.open_timeout = Fetcher.api_timeout
+ con.override_headers["User-Agent"] = user_agent
+ con.override_headers["X-Gemfile-Source"] = @remote.original_uri.to_s if @remote.original_uri
+ con
+ end
+ end
+
+ # cached gem specification path, if one exists
+ def gemspec_cached_path(spec_file_name)
+ paths = Bundler.rubygems.spec_cache_dirs.map {|dir| File.join(dir, spec_file_name) }
+ paths = paths.select {|path| File.file? path }
+ paths.first
+ end
+
+ HTTP_ERRORS = [
+ Timeout::Error, EOFError, SocketError, Errno::ENETDOWN, Errno::ENETUNREACH,
+ Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN,
+ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
+ PersistentHTTP::Error, Zlib::BufError, Errno::EHOSTUNREACH
+ ].freeze
+
+ def bundler_cert_store
+ store = OpenSSL::X509::Store.new
+ ssl_ca_cert = Bundler.settings[:ssl_ca_cert] ||
+ (Bundler.rubygems.configuration.ssl_ca_cert if
+ Bundler.rubygems.configuration.respond_to?(:ssl_ca_cert))
+ if ssl_ca_cert
+ if File.directory? ssl_ca_cert
+ store.add_path ssl_ca_cert
+ else
+ store.add_file ssl_ca_cert
+ end
+ else
+ store.set_default_paths
+ certs = File.expand_path("../ssl_certs/*/*.pem", __FILE__)
+ Dir.glob(certs).each {|c| store.add_file c }
+ end
+ store
+ end
+
+ private
+
+ def remote_uri
+ @remote.uri
+ end
+
+ def downloader
+ @downloader ||= Downloader.new(connection, self.class.redirect_limit)
+ end
+ end
+end
diff --git a/lib/bundler/fetcher/base.rb b/lib/bundler/fetcher/base.rb
new file mode 100644
index 0000000000..27987f670a
--- /dev/null
+++ b/lib/bundler/fetcher/base.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Fetcher
+ class Base
+ attr_reader :downloader
+ attr_reader :display_uri
+ attr_reader :remote
+
+ def initialize(downloader, remote, display_uri)
+ raise "Abstract class" if self.class == Base
+ @downloader = downloader
+ @remote = remote
+ @display_uri = display_uri
+ end
+
+ def remote_uri
+ @remote.uri
+ end
+
+ def fetch_uri
+ @fetch_uri ||= begin
+ if remote_uri.host == "rubygems.org"
+ uri = remote_uri.dup
+ uri.host = "index.rubygems.org"
+ uri
+ else
+ remote_uri
+ end
+ end
+ end
+
+ def available?
+ true
+ end
+
+ def api_fetcher?
+ false
+ end
+
+ private
+
+ def log_specs(debug_msg)
+ if Bundler.ui.debug?
+ Bundler.ui.debug debug_msg
+ else
+ Bundler.ui.info ".", false
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/fetcher/compact_index.rb b/lib/bundler/fetcher/compact_index.rb
new file mode 100644
index 0000000000..cfc74d642c
--- /dev/null
+++ b/lib/bundler/fetcher/compact_index.rb
@@ -0,0 +1,126 @@
+# frozen_string_literal: true
+
+require "bundler/fetcher/base"
+require "bundler/worker"
+
+module Bundler
+ autoload :CompactIndexClient, "bundler/compact_index_client"
+
+ class Fetcher
+ class CompactIndex < Base
+ def self.compact_index_request(method_name)
+ 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
+ end
+ end
+
+ def specs(gem_names)
+ specs_for_names(gem_names)
+ end
+ compact_index_request :specs
+
+ def specs_for_names(gem_names)
+ gem_info = []
+ complete_gems = []
+ remaining_gems = gem_names.dup
+
+ until remaining_gems.empty?
+ log_specs "Looking up gems #{remaining_gems.inspect}"
+
+ deps = compact_index_client.dependencies(remaining_gems)
+ next_gems = deps.map {|d| d[3].map(&:first).flatten(1) }.flatten(1).uniq
+ deps.each {|dep| gem_info << dep }
+ complete_gems.concat(deps.map(&:first)).uniq!
+ remaining_gems = next_gems - complete_gems
+ end
+ @bundle_worker.stop if @bundle_worker
+ @bundle_worker = nil # reset it. Not sure if necessary
+
+ gem_info
+ end
+
+ def fetch_spec(spec)
+ spec -= [nil, "ruby", ""]
+ contents = compact_index_client.spec(*spec)
+ return nil if contents.nil?
+ contents.unshift(spec.first)
+ contents[3].map! {|d| Gem::Dependency.new(*d) }
+ EndpointSpecification.new(*contents)
+ end
+ compact_index_request :fetch_spec
+
+ def available?
+ return nil unless SharedHelpers.md5_available?
+ user_home = Bundler.user_home
+ return nil unless user_home.directory? && user_home.writable?
+ # Read info file checksums out of /versions, so we can know if gems are up to date
+ fetch_uri.scheme != "file" && compact_index_client.update_and_parse_checksums!
+ rescue CompactIndexClient::Updater::MisMatchedChecksumError => e
+ Bundler.ui.debug(e.message)
+ nil
+ end
+ compact_index_request :available?
+
+ def api_fetcher?
+ true
+ end
+
+ private
+
+ def compact_index_client
+ @compact_index_client ||= begin
+ SharedHelpers.filesystem_access(cache_path) do
+ CompactIndexClient.new(cache_path, client_fetcher)
+ end.tap do |client|
+ client.in_parallel = lambda do |inputs, &blk|
+ func = lambda {|object, _index| blk.call(object) }
+ worker = bundle_worker(func)
+ inputs.each {|input| worker.enq(input) }
+ inputs.map { worker.deq }
+ end
+ end
+ end
+ end
+
+ def bundle_worker(func = nil)
+ @bundle_worker ||= begin
+ worker_name = "Compact Index (#{display_uri.host})"
+ Bundler::Worker.new(Bundler.current_ruby.rbx? ? 1 : 25, worker_name, func)
+ end
+ @bundle_worker.tap do |worker|
+ worker.instance_variable_set(:@func, func) if func
+ end
+ end
+
+ def cache_path
+ Bundler.user_cache.join("compact_index", remote.cache_slug)
+ end
+
+ def client_fetcher
+ ClientFetcher.new(self, Bundler.ui)
+ end
+
+ ClientFetcher = Struct.new(:fetcher, :ui) do
+ def call(path, headers)
+ fetcher.downloader.fetch(fetcher.fetch_uri + path, headers)
+ rescue NetworkDownError => e
+ raise unless Bundler.feature_flag.allow_offline_install? && headers["If-None-Match"]
+ ui.warn "Using the cached data for the new index because of a network error: #{e}"
+ Net::HTTPNotModified.new(nil, nil, nil)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/fetcher/dependency.rb b/lib/bundler/fetcher/dependency.rb
new file mode 100644
index 0000000000..1430d1ebeb
--- /dev/null
+++ b/lib/bundler/fetcher/dependency.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require "bundler/fetcher/base"
+require "cgi"
+
+module Bundler
+ class Fetcher
+ class Dependency < Base
+ def available?
+ @available ||= fetch_uri.scheme != "file" && downloader.fetch(dependency_api_uri)
+ rescue NetworkDownError => e
+ raise HTTPError, e.message
+ rescue AuthenticationRequiredError
+ # Fail since we got a 401 from the server.
+ raise
+ rescue HTTPError
+ false
+ end
+
+ def api_fetcher?
+ true
+ end
+
+ def specs(gem_names, full_dependency_list = [], last_spec_list = [])
+ query_list = gem_names.uniq - full_dependency_list
+
+ log_specs "Query List: #{query_list.inspect}"
+
+ return last_spec_list if query_list.empty?
+
+ spec_list, deps_list = Bundler::Retry.new("dependency api", FAIL_ERRORS).attempts do
+ dependency_specs(query_list)
+ end
+
+ returned_gems = spec_list.map(&:first).uniq
+ specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list)
+ rescue MarshalError
+ 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)
+ Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(",")}"
+
+ gem_list = unmarshalled_dep_gems(gem_names)
+ get_formatted_specs_and_deps(gem_list)
+ end
+
+ def unmarshalled_dep_gems(gem_names)
+ 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))
+ end
+ gem_list
+ end
+
+ def get_formatted_specs_and_deps(gem_list)
+ deps_list = []
+ spec_list = []
+
+ gem_list.each do |s|
+ deps_list.concat(s[:dependencies].map(&:first))
+ deps = s[:dependencies].map {|n, d| [n, d.split(", ")] }
+ spec_list.push([s[:name], s[:number], s[:platform], deps])
+ end
+ [spec_list, deps_list]
+ end
+
+ def dependency_api_uri(gem_names = [])
+ uri = fetch_uri + "api/v1/dependencies"
+ uri.query = "gems=#{CGI.escape(gem_names.sort.join(","))}" if gem_names.any?
+ uri
+ end
+ end
+ end
+end
diff --git a/lib/bundler/fetcher/downloader.rb b/lib/bundler/fetcher/downloader.rb
new file mode 100644
index 0000000000..e0e0cbf1c9
--- /dev/null
+++ b/lib/bundler/fetcher/downloader.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Fetcher
+ class Downloader
+ attr_reader :connection
+ attr_reader :redirect_limit
+
+ def initialize(connection, redirect_limit)
+ @connection = connection
+ @redirect_limit = redirect_limit
+ end
+
+ def fetch(uri, headers = {}, counter = 0)
+ raise HTTPError, "Too many redirects" if counter >= redirect_limit
+
+ response = request(uri, headers)
+ Bundler.ui.debug("HTTP #{response.code} #{response.message} #{uri}")
+
+ case response
+ when Net::HTTPSuccess, Net::HTTPNotModified
+ response
+ when Net::HTTPRedirection
+ new_uri = URI.parse(response["location"])
+ if new_uri.host == uri.host
+ new_uri.user = uri.user
+ new_uri.password = uri.password
+ end
+ fetch(new_uri, headers, counter + 1)
+ when Net::HTTPRequestedRangeNotSatisfiable
+ new_headers = headers.dup
+ new_headers.delete("Range")
+ new_headers["Accept-Encoding"] = "gzip"
+ fetch(uri, new_headers)
+ when Net::HTTPRequestEntityTooLarge
+ raise FallbackError, response.body
+ when Net::HTTPUnauthorized
+ raise AuthenticationRequiredError, uri.host
+ when Net::HTTPNotFound
+ raise FallbackError, "Net::HTTPNotFound"
+ else
+ raise HTTPError, "#{response.class}#{": #{response.body}" unless response.body.empty?}"
+ end
+ end
+
+ def request(uri, headers)
+ validate_uri_scheme!(uri)
+
+ Bundler.ui.debug "HTTP GET #{uri}"
+ req = Net::HTTP::Get.new uri.request_uri, headers
+ if uri.user
+ user = CGI.unescape(uri.user)
+ password = uri.password ? CGI.unescape(uri.password) : nil
+ 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
+ case e.message
+ when /host down:/, /getaddrinfo: nodename nor servname provided/
+ raise NetworkDownError, "Could not reach host #{uri.host}. Check your network " \
+ "connection and try again."
+ else
+ raise HTTPError, "Network error while fetching #{URICredentialsFilter.credential_filtered_uri(uri)}" \
+ " (#{e})"
+ end
+ end
+
+ private
+
+ def validate_uri_scheme!(uri)
+ return if uri.scheme =~ /\Ahttps?\z/
+ raise InvalidOption,
+ "The request uri `#{uri}` has an invalid scheme (`#{uri.scheme}`). " \
+ "Did you mean `http` or `https`?"
+ end
+ end
+ end
+end
diff --git a/lib/bundler/fetcher/index.rb b/lib/bundler/fetcher/index.rb
new file mode 100644
index 0000000000..1a8064624d
--- /dev/null
+++ b/lib/bundler/fetcher/index.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require "bundler/fetcher/base"
+require "rubygems/remote_fetcher"
+
+module Bundler
+ class Fetcher
+ class Index < Base
+ def specs(_gem_names)
+ Bundler.rubygems.fetch_all_remote_specs(remote)
+ rescue Gem::RemoteFetcher::FetchError, OpenSSL::SSL::SSLError, Net::HTTPFatalError => e
+ case e.message
+ when /certificate verify failed/
+ raise CertificateFailureError.new(display_uri)
+ when /401/
+ raise AuthenticationRequiredError, remote_uri
+ when /403/
+ raise BadAuthenticationError, remote_uri if remote_uri.userinfo
+ raise AuthenticationRequiredError, remote_uri
+ else
+ Bundler.ui.trace e
+ raise HTTPError, "Could not fetch specs from #{display_uri}"
+ end
+ end
+
+ def fetch_spec(spec)
+ spec -= [nil, "ruby", ""]
+ spec_file_name = "#{spec.join "-"}.gemspec"
+
+ uri = URI.parse("#{remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz")
+ if uri.scheme == "file"
+ Bundler.load_marshal Bundler.rubygems.inflate(Gem.read_binary(uri.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)
+ end
+ rescue MarshalError
+ raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \
+ "Your network or your gem server is probably having issues right now."
+ end
+
+ private
+
+ # cached gem specification path, if one exists
+ def gemspec_cached_path(spec_file_name)
+ paths = Bundler.rubygems.spec_cache_dirs.map {|dir| File.join(dir, spec_file_name) }
+ paths.find {|path| File.file? path }
+ end
+ end
+ end
+end
diff --git a/lib/bundler/friendly_errors.rb b/lib/bundler/friendly_errors.rb
new file mode 100644
index 0000000000..ae3299a7c8
--- /dev/null
+++ b/lib/bundler/friendly_errors.rb
@@ -0,0 +1,131 @@
+# encoding: utf-8
+# frozen_string_literal: true
+
+require "cgi"
+require "bundler/vendored_thor"
+
+module Bundler
+ module FriendlyErrors
+ module_function
+
+ def log_error(error)
+ case error
+ when YamlSyntaxError
+ Bundler.ui.error error.message
+ Bundler.ui.trace error.orig_exception
+ when Dsl::DSLError, GemspecError
+ Bundler.ui.error error.message
+ when GemRequireError
+ Bundler.ui.error error.message
+ Bundler.ui.trace error.orig_exception, nil, true
+ when BundlerError
+ Bundler.ui.error error.message, :wrap => true
+ Bundler.ui.trace error
+ 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."
+ Bundler.ui.warn <<-WARN, :wrap => true
+ 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 http://rvm.io/packages/openssl.
+ WARN
+ Bundler.ui.trace error
+ when Interrupt
+ Bundler.ui.error "\nQuitting..."
+ Bundler.ui.trace error
+ when Gem::InvalidSpecificationException
+ Bundler.ui.error error.message, :wrap => true
+ when SystemExit
+ when *[defined?(Java::JavaLang::OutOfMemoryError) && Java::JavaLang::OutOfMemoryError].compact
+ Bundler.ui.error "\nYour JVM has run out of memory, and Bundler cannot continue. " \
+ "You can decrease the amount of memory Bundler needs by removing gems from your Gemfile, " \
+ "especially large gems. (Gems can be as large as hundreds of megabytes, and Bundler has to read those files!). " \
+ "Alternatively, you can increase the amount of memory the JVM is able to use by running Bundler with jruby -J-Xmx1024m -S bundle (JRuby defaults to 500MB)."
+ else request_issue_report_for(error)
+ end
+ rescue
+ raise error
+ end
+
+ def exit_status(error)
+ case error
+ when BundlerError then error.status_code
+ when Thor::Error then 15
+ when SystemExit then error.status
+ else 1
+ end
+ end
+
+ def request_issue_report_for(e)
+ Bundler.ui.info <<-EOS.gsub(/^ {8}/, "")
+ --- ERROR REPORT TEMPLATE -------------------------------------------------------
+ # Error Report
+
+ ## Questions
+
+ Please fill out answers to these questions, it'll help us figure out
+ why things are going wrong.
+
+ - **What did you do?**
+
+ I ran the command `#{$PROGRAM_NAME} #{ARGV.join(" ")}`
+
+ - **What did you expect to happen?**
+
+ I expected Bundler to...
+
+ - **What happened instead?**
+
+ Instead, what happened was...
+
+ - **Have you tried any solutions posted on similar issues in our issue tracker, stack overflow, or google?**
+
+ I tried...
+
+ - **Have you read our issues document, https://github.com/bundler/bundler/blob/master/doc/contributing/ISSUES.md?**
+
+ ...
+
+ ## Backtrace
+
+ ```
+ #{e.class}: #{e.message}
+ #{e.backtrace && e.backtrace.join("\n ").chomp}
+ ```
+
+ #{Bundler::Env.report}
+ --- TEMPLATE END ----------------------------------------------------------------
+
+ EOS
+
+ Bundler.ui.error "Unfortunately, an unexpected error occurred, and Bundler cannot continue."
+
+ Bundler.ui.warn <<-EOS.gsub(/^ {8}/, "")
+
+ First, try this link to see if there are any existing issue reports for this error:
+ #{issues_url(e)}
+
+ If there aren't any reports for this error yet, please create copy and paste the report template above into a new issue. Don't forget to anonymize any private data! The new issue form is located at:
+ https://github.com/bundler/bundler/issues/new
+ EOS
+ end
+
+ def issues_url(exception)
+ message = exception.message.lines.first.tr(":", " ").chomp
+ message = message.split("-").first if exception.is_a?(Errno)
+ "https://github.com/bundler/bundler/search?q=" \
+ "#{CGI.escape(message)}&type=Issues"
+ end
+ end
+
+ def self.with_friendly_errors
+ yield
+ rescue SignalException
+ raise
+ rescue Exception => e
+ FriendlyErrors.log_error(e)
+ exit FriendlyErrors.exit_status(e)
+ end
+end
diff --git a/lib/bundler/gem_helper.rb b/lib/bundler/gem_helper.rb
new file mode 100644
index 0000000000..e7673cba88
--- /dev/null
+++ b/lib/bundler/gem_helper.rb
@@ -0,0 +1,202 @@
+# frozen_string_literal: true
+
+require "bundler/vendored_thor" unless defined?(Thor)
+require "bundler"
+
+module Bundler
+ class GemHelper
+ include Rake::DSL if defined? Rake::DSL
+
+ class << self
+ # set when install'd.
+ attr_accessor :instance
+
+ def install_tasks(opts = {})
+ new(opts[:dir], opts[:name]).install
+ end
+
+ def gemspec(&block)
+ gemspec = instance.gemspec
+ block.call(gemspec) if block
+ gemspec
+ end
+ end
+
+ attr_reader :spec_path, :base, :gemspec
+
+ def initialize(base = nil, name = nil)
+ Bundler.ui = UI::Shell.new
+ @base = (base ||= SharedHelpers.pwd)
+ gemspecs = name ? [File.join(base, "#{name}.gemspec")] : Dir[File.join(base, "{,*}.gemspec")]
+ raise "Unable to determine name from existing gemspec. Use :name => 'gemname' in #install_tasks to manually set it." unless gemspecs.size == 1
+ @spec_path = gemspecs.first
+ @gemspec = Bundler.load_gemspec(@spec_path)
+ end
+
+ def install
+ built_gem_path = nil
+
+ desc "Build #{name}-#{version}.gem into the pkg directory."
+ task "build" do
+ built_gem_path = build_gem
+ end
+
+ desc "Build and install #{name}-#{version}.gem into system gems."
+ task "install" => "build" do
+ install_gem(built_gem_path)
+ end
+
+ desc "Build and install #{name}-#{version}.gem into system gems without network access."
+ task "install:local" => "build" do
+ install_gem(built_gem_path, :local)
+ end
+
+ desc "Create tag #{version_tag} and build and push #{name}-#{version}.gem to #{gem_push_host}\n" \
+ "To prevent publishing in RubyGems use `gem_push=no rake release`"
+ task "release", [:remote] => ["build", "release:guard_clean",
+ "release:source_control_push", "release:rubygem_push"] do
+ end
+
+ task "release:guard_clean" do
+ guard_clean
+ end
+
+ task "release:source_control_push", [:remote] do |_, args|
+ tag_version { git_push(args[:remote]) } unless already_tagged?
+ end
+
+ task "release:rubygem_push" do
+ rubygem_push(built_gem_path) if gem_push?
+ end
+
+ GemHelper.instance = self
+ end
+
+ def build_gem
+ file_name = nil
+ sh("gem build -V '#{spec_path}'") do
+ file_name = File.basename(built_gem_path)
+ SharedHelpers.filesystem_access(File.join(base, "pkg")) {|p| FileUtils.mkdir_p(p) }
+ FileUtils.mv(built_gem_path, "pkg")
+ Bundler.ui.confirm "#{name} #{version} built to pkg/#{file_name}."
+ end
+ File.join(base, "pkg", file_name)
+ end
+
+ def install_gem(built_gem_path = nil, local = false)
+ built_gem_path ||= build_gem
+ out, _ = sh_with_code("gem install '#{built_gem_path}'#{" --local" if local}")
+ raise "Couldn't install gem, run `gem install #{built_gem_path}' for more detailed output" unless out[/Successfully installed/]
+ Bundler.ui.confirm "#{name} (#{version}) installed."
+ end
+
+ protected
+
+ def rubygem_push(path)
+ gem_command = "gem push '#{path}'"
+ gem_command += " --key #{gem_key}" if gem_key
+ gem_command += " --host #{allowed_push_host}" if allowed_push_host
+ unless allowed_push_host || Bundler.user_home.join(".gem/credentials").file?
+ raise "Your rubygems.org credentials aren't set. Run `gem push` to set them."
+ end
+ sh(gem_command)
+ Bundler.ui.confirm "Pushed #{name} #{version} to #{gem_push_host}"
+ end
+
+ def built_gem_path
+ Dir[File.join(base, "#{name}-*.gem")].sort_by {|f| File.mtime(f) }.last
+ end
+
+ def git_push(remote = "")
+ perform_git_push remote
+ perform_git_push "#{remote} --tags"
+ Bundler.ui.confirm "Pushed git commits and tags."
+ end
+
+ def allowed_push_host
+ @gemspec.metadata["allowed_push_host"] if @gemspec.respond_to?(:metadata)
+ end
+
+ def gem_push_host
+ env_rubygems_host = ENV["RUBYGEMS_HOST"]
+ env_rubygems_host = nil if
+ env_rubygems_host && env_rubygems_host.empty?
+
+ allowed_push_host || env_rubygems_host || "rubygems.org"
+ end
+
+ def perform_git_push(options = "")
+ cmd = "git push #{options}"
+ out, code = sh_with_code(cmd)
+ raise "Couldn't git push. `#{cmd}' failed with the following output:\n\n#{out}\n" unless code == 0
+ end
+
+ def already_tagged?
+ return false unless sh("git tag").split(/\n/).include?(version_tag)
+ Bundler.ui.confirm "Tag #{version_tag} has already been created."
+ true
+ end
+
+ def guard_clean
+ clean? && committed? || raise("There are files that need to be committed first.")
+ end
+
+ def clean?
+ sh_with_code("git diff --exit-code")[1] == 0
+ end
+
+ def committed?
+ sh_with_code("git diff-index --quiet --cached HEAD")[1] == 0
+ end
+
+ def tag_version
+ sh "git tag -m \"Version #{version}\" #{version_tag}"
+ Bundler.ui.confirm "Tagged #{version_tag}."
+ yield if block_given?
+ rescue RuntimeError
+ Bundler.ui.error "Untagging #{version_tag} due to error."
+ sh_with_code "git tag -d #{version_tag}"
+ raise
+ end
+
+ def version
+ gemspec.version
+ end
+
+ def version_tag
+ "v#{version}"
+ end
+
+ def name
+ gemspec.name
+ end
+
+ def sh(cmd, &block)
+ out, code = sh_with_code(cmd, &block)
+ unless code.zero?
+ raise(out.empty? ? "Running `#{cmd}` failed. Run this command directly for more detailed output." : out)
+ end
+ out
+ end
+
+ def sh_with_code(cmd, &block)
+ cmd += " 2>&1"
+ outbuf = String.new
+ Bundler.ui.debug(cmd)
+ SharedHelpers.chdir(base) do
+ outbuf = `#{cmd}`
+ status = $?.exitstatus
+ block.call(outbuf) if status.zero? && block
+ [outbuf, status]
+ end
+ end
+
+ def gem_key
+ Bundler.settings["gem.push_key"].to_s.downcase if Bundler.settings["gem.push_key"]
+ end
+
+ def gem_push?
+ !%w[n no nil false off 0].include?(ENV["gem_push"].to_s.downcase)
+ end
+ end
+end
diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb
new file mode 100644
index 0000000000..019ae10c66
--- /dev/null
+++ b/lib/bundler/gem_helpers.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+module Bundler
+ module GemHelpers
+ GENERIC_CACHE = {} # rubocop:disable MutableConstant
+ GENERICS = [
+ [Gem::Platform.new("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")],
+ [Gem::Platform.new("x64-mingw32"), Gem::Platform.new("x64-mingw32")],
+ [Gem::Platform.new("x86_64-mingw32"), Gem::Platform.new("x64-mingw32")],
+ [Gem::Platform.new("mingw32"), Gem::Platform.new("x86-mingw32")]
+ ].freeze
+
+ def generic(p)
+ return p if p == Gem::Platform::RUBY
+
+ GENERIC_CACHE[p] ||= begin
+ _, found = GENERICS.find do |match, _generic|
+ p.os == match.os && (!match.cpu || p.cpu == match.cpu)
+ end
+ found || Gem::Platform::RUBY
+ end
+ end
+ module_function :generic
+
+ def generic_local_platform
+ generic(Bundler.local_platform)
+ end
+ module_function :generic_local_platform
+
+ def platform_specificity_match(spec_platform, user_platform)
+ spec_platform = Gem::Platform.new(spec_platform)
+ return PlatformMatch::EXACT_MATCH if spec_platform == user_platform
+ return PlatformMatch::WORST_MATCH if spec_platform.nil? || spec_platform == Gem::Platform::RUBY || user_platform == Gem::Platform::RUBY
+
+ PlatformMatch.new(
+ PlatformMatch.os_match(spec_platform, user_platform),
+ PlatformMatch.cpu_match(spec_platform, user_platform),
+ PlatformMatch.platform_version_match(spec_platform, user_platform)
+ )
+ end
+ module_function :platform_specificity_match
+
+ def select_best_platform_match(specs, platform)
+ specs.select {|spec| spec.match_platform(platform) }.
+ min_by {|spec| platform_specificity_match(spec.platform, platform) }
+ end
+ module_function :select_best_platform_match
+
+ PlatformMatch = Struct.new(:os_match, :cpu_match, :platform_version_match)
+ class PlatformMatch
+ def <=>(other)
+ return nil unless other.is_a?(PlatformMatch)
+
+ m = os_match <=> other.os_match
+ return m unless m.zero?
+
+ m = cpu_match <=> other.cpu_match
+ return m unless m.zero?
+
+ m = platform_version_match <=> other.platform_version_match
+ m
+ end
+
+ EXACT_MATCH = new(-1, -1, -1).freeze
+ WORST_MATCH = new(1_000_000, 1_000_000, 1_000_000).freeze
+
+ def self.os_match(spec_platform, user_platform)
+ if spec_platform.os == user_platform.os
+ 0
+ else
+ 1
+ end
+ end
+
+ def self.cpu_match(spec_platform, user_platform)
+ if spec_platform.cpu == user_platform.cpu
+ 0
+ elsif spec_platform.cpu == "arm" && user_platform.cpu.to_s.start_with?("arm")
+ 0
+ elsif spec_platform.cpu.nil? || spec_platform.cpu == "universal"
+ 1
+ else
+ 2
+ end
+ end
+
+ def self.platform_version_match(spec_platform, user_platform)
+ if spec_platform.version == user_platform.version
+ 0
+ elsif spec_platform.version.nil?
+ 1
+ else
+ 2
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/gem_remote_fetcher.rb b/lib/bundler/gem_remote_fetcher.rb
new file mode 100644
index 0000000000..9577535d63
--- /dev/null
+++ b/lib/bundler/gem_remote_fetcher.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require "rubygems/remote_fetcher"
+
+module Bundler
+ # Adds support for setting custom HTTP headers when fetching gems from the
+ # server.
+ #
+ # TODO: Get rid of this when and if gemstash only supports RubyGems versions
+ # that contain https://github.com/rubygems/rubygems/commit/3db265cc20b2f813.
+ class GemRemoteFetcher < Gem::RemoteFetcher
+ attr_accessor :headers
+
+ # Extracted from RubyGems 2.4.
+ def fetch_http(uri, last_modified = nil, head = false, depth = 0)
+ fetch_type = head ? Net::HTTP::Head : Net::HTTP::Get
+ # beginning of change
+ response = request uri, fetch_type, last_modified do |req|
+ headers.each {|k, v| req.add_field(k, v) } if headers
+ end
+ # end of change
+
+ case response
+ when Net::HTTPOK, Net::HTTPNotModified then
+ response.uri = uri if response.respond_to? :uri
+ head ? response : response.body
+ when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther,
+ Net::HTTPTemporaryRedirect then
+ raise FetchError.new("too many redirects", uri) if depth > 10
+
+ location = URI.parse response["Location"]
+
+ if https?(uri) && !https?(location)
+ raise FetchError.new("redirecting to non-https resource: #{location}", uri)
+ end
+
+ fetch_http(location, last_modified, head, depth + 1)
+ else
+ raise FetchError.new("bad response #{response.message} #{response.code}", uri)
+ end
+ end
+ end
+end
diff --git a/lib/bundler/gem_tasks.rb b/lib/bundler/gem_tasks.rb
new file mode 100644
index 0000000000..f736517bd7
--- /dev/null
+++ b/lib/bundler/gem_tasks.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require "rake/clean"
+CLOBBER.include "pkg"
+
+require "bundler/gem_helper"
+Bundler::GemHelper.install_tasks
diff --git a/lib/bundler/gem_version_promoter.rb b/lib/bundler/gem_version_promoter.rb
new file mode 100644
index 0000000000..adb951a7a0
--- /dev/null
+++ b/lib/bundler/gem_version_promoter.rb
@@ -0,0 +1,190 @@
+# frozen_string_literal: true
+
+module Bundler
+ # This class contains all of the logic for determining the next version of a
+ # Gem to update to based on the requested level (patch, minor, major).
+ # Primarily designed to work with Resolver which will provide it the list of
+ # 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["DEBUG_RESOLVER"]
+
+ attr_reader :level, :locked_specs, :unlock_gems
+
+ # 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,
+ # or even reverted to older versions.
+ #
+ # If strict is set to true, the results from sort_versions will be
+ # truncated, eliminating any version outside the current level scope.
+ # This can lead to unexpected outcomes or even VersionConflict exceptions
+ # that report a version of a gem not existing for versions that indeed do
+ # 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.
+ #
+ # @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 = [])
+ @level = :major
+ @strict = false
+ @locked_specs = locked_specs
+ @unlock_gems = unlock_gems
+ @sort_versions = {}
+ @prerelease_specified = {}
+ end
+
+ # @param value [Symbol] One of three Symbols: :major, :minor or :patch.
+ def level=(value)
+ v = case value
+ when String, Symbol
+ value.to_sym
+ end
+
+ raise ArgumentError, "Unexpected level #{v}. Must be :major, :minor or :patch" unless [:major, :minor, :patch].include?(v)
+ @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
+ # 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
+
+ # 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
+ STDERR.puts before_result
+ STDERR.puts " after sort_versions: #{debug_format_result(dep, specs).inspect}"
+ end
+ end
+ end
+ end
+
+ # @return [bool] Convenience method for testing value of level variable.
+ def major?
+ level == :major
+ end
+
+ # @return [bool] Convenience method for testing value of level variable.
+ def minor?
+ level == :minor
+ 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
+
+ must_match = minor? ? [0] : [0, 1]
+
+ matches = must_match.map {|idx| gsv.segments[idx] == lsv.segments[idx] }
+ (matches.uniq == [true]) ? (gsv >= lsv) : false
+ else
+ true
+ end
+ end
+
+ sort_dep_specs(res, locked_spec)
+ 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
+
+ unless @prerelease_specified[@gem_name]
+ a_pre = @a_ver.prerelease?
+ b_pre = @b_ver.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
+ else
+ @a_ver <=> @b_ver
+ end
+ end
+ post_sort(result)
+ end
+
+ def either_version_older_than_locked
+ @a_ver < @locked_version || @b_ver < @locked_version
+ end
+
+ def segments_do_not_match(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)
+ 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)
+ # default :major behavior in Bundler does not do this
+ return result if major?
+ if unlocking_gem?
+ result
+ else
+ move_version_to_end(result, @locked_version)
+ end
+ end
+
+ def move_version_to_end(result, version)
+ 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/gemdeps.rb b/lib/bundler/gemdeps.rb
new file mode 100644
index 0000000000..cd4b25d0e6
--- /dev/null
+++ b/lib/bundler/gemdeps.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Gemdeps
+ def initialize(runtime)
+ @runtime = runtime
+ end
+
+ def requested_specs
+ @runtime.requested_specs
+ end
+
+ def specs
+ @runtime.specs
+ end
+
+ def dependencies
+ @runtime.dependencies
+ end
+
+ def current_dependencies
+ @runtime.current_dependencies
+ end
+
+ def requires
+ @runtime.requires
+ end
+ end
+end
diff --git a/lib/bundler/graph.rb b/lib/bundler/graph.rb
new file mode 100644
index 0000000000..de6bba0214
--- /dev/null
+++ b/lib/bundler/graph.rb
@@ -0,0 +1,152 @@
+# frozen_string_literal: true
+
+require "set"
+module Bundler
+ class Graph
+ GRAPH_NAME = :Gemfile
+
+ def initialize(env, output_file, show_version = false, show_requirements = false, output_format = "png", without = [])
+ @env = env
+ @output_file = output_file
+ @show_version = show_version
+ @show_requirements = show_requirements
+ @output_format = output_format
+ @without_groups = without.map(&:to_sym)
+
+ @groups = []
+ @relations = Hash.new {|h, k| h[k] = Set.new }
+ @node_options = {}
+ @edge_options = {}
+
+ _populate_relations
+ end
+
+ attr_reader :groups, :relations, :node_options, :edge_options, :output_file, :output_format
+
+ def viz
+ GraphVizClient.new(self).run
+ end
+
+ private
+
+ def _populate_relations
+ parent_dependencies = _groups.values.to_set.flatten
+ loop do
+ break if parent_dependencies.empty?
+
+ tmp = Set.new
+ parent_dependencies.each do |dependency|
+ child_dependencies = spec_for_dependency(dependency).runtime_dependencies.to_set
+ @relations[dependency.name] += child_dependencies.map(&:name).to_set
+ tmp += child_dependencies
+
+ @node_options[dependency.name] = _make_label(dependency, :node)
+ child_dependencies.each do |c_dependency|
+ @edge_options["#{dependency.name}_#{c_dependency.name}"] = _make_label(c_dependency, :edge)
+ end
+ end
+ parent_dependencies = tmp
+ end
+ end
+
+ def _groups
+ relations = Hash.new {|h, k| h[k] = Set.new }
+ @env.current_dependencies.each do |dependency|
+ dependency.groups.each do |group|
+ next if @without_groups.include?(group)
+
+ relations[group.to_s].add(dependency)
+ @relations[group.to_s].add(dependency.name)
+
+ @node_options[group.to_s] ||= _make_label(group, :node)
+ @edge_options["#{group}_#{dependency.name}"] = _make_label(dependency, :edge)
+ end
+ end
+ @groups = relations.keys
+ relations
+ end
+
+ def _make_label(symbol_or_string_or_dependency, element_type)
+ case element_type.to_sym
+ when :node
+ if symbol_or_string_or_dependency.is_a?(Gem::Dependency)
+ label = symbol_or_string_or_dependency.name.dup
+ label << "\n#{spec_for_dependency(symbol_or_string_or_dependency).version}" if @show_version
+ else
+ label = symbol_or_string_or_dependency.to_s
+ end
+ when :edge
+ label = nil
+ if symbol_or_string_or_dependency.respond_to?(:requirements_list) && @show_requirements
+ tmp = symbol_or_string_or_dependency.requirements_list.join(", ")
+ label = tmp if tmp != ">= 0"
+ end
+ else
+ raise ArgumentError, "2nd argument is invalid"
+ end
+ label.nil? ? {} : { :label => label }
+ end
+
+ def spec_for_dependency(dependency)
+ @env.requested_specs.find {|s| s.name == dependency.name }
+ end
+
+ class GraphVizClient
+ def initialize(graph_instance)
+ @graph_name = graph_instance.class::GRAPH_NAME
+ @groups = graph_instance.groups
+ @relations = graph_instance.relations
+ @node_options = graph_instance.node_options
+ @edge_options = graph_instance.edge_options
+ @output_file = graph_instance.output_file
+ @output_format = graph_instance.output_format
+ end
+
+ def g
+ @g ||= ::GraphViz.digraph(@graph_name, :concentrate => true, :normalize => true, :nodesep => 0.55) do |g|
+ g.edge[:weight] = 2
+ g.edge[:fontname] = g.node[:fontname] = "Arial, Helvetica, SansSerif"
+ g.edge[:fontsize] = 12
+ end
+ end
+
+ def run
+ @groups.each do |group|
+ g.add_nodes(
+ group, {
+ :style => "filled",
+ :fillcolor => "#B9B9D5",
+ :shape => "box3d",
+ :fontsize => 16
+ }.merge(@node_options[group])
+ )
+ end
+
+ @relations.each do |parent, children|
+ children.each do |child|
+ if @groups.include?(parent)
+ g.add_nodes(child, { :style => "filled", :fillcolor => "#B9B9D5" }.merge(@node_options[child]))
+ g.add_edges(parent, child, { :constraint => false }.merge(@edge_options["#{parent}_#{child}"]))
+ else
+ g.add_nodes(child, @node_options[child])
+ g.add_edges(parent, child, @edge_options["#{parent}_#{child}"])
+ end
+ end
+ end
+
+ if @output_format.to_s == "debug"
+ $stdout.puts g.output :none => String
+ Bundler.ui.info "debugging bundle viz..."
+ else
+ begin
+ g.output @output_format.to_sym => "#{@output_file}.#{@output_format}"
+ Bundler.ui.info "#{@output_file}.#{@output_format}"
+ rescue ArgumentError => e
+ $stderr.puts "Unsupported output format. See Ruby-Graphviz/lib/graphviz/constants.rb"
+ raise e
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb
new file mode 100644
index 0000000000..9166a92738
--- /dev/null
+++ b/lib/bundler/index.rb
@@ -0,0 +1,213 @@
+# frozen_string_literal: true
+
+require "set"
+
+module Bundler
+ class Index
+ include Enumerable
+
+ def self.build
+ i = new
+ yield i
+ i
+ end
+
+ attr_reader :specs, :all_specs, :sources
+ protected :specs, :all_specs
+
+ RUBY = "ruby".freeze
+ NULL = "\0".freeze
+
+ def initialize
+ @sources = []
+ @cache = {}
+ @specs = Hash.new {|h, k| h[k] = {} }
+ @all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH }
+ end
+
+ def initialize_copy(o)
+ @sources = o.sources.dup
+ @cache = {}
+ @specs = Hash.new {|h, k| h[k] = {} }
+ @all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH }
+
+ o.specs.each do |name, hash|
+ @specs[name] = hash.dup
+ end
+ o.all_specs.each do |name, array|
+ @all_specs[name] = array.dup
+ end
+ end
+
+ def inspect
+ "#<#{self.class}:0x#{object_id} sources=#{sources.map(&:inspect)} specs.size=#{specs.size}>"
+ end
+
+ def empty?
+ each { return false }
+ true
+ end
+
+ def search_all(name)
+ all_matches = local_search(name) + @all_specs[name]
+ @sources.each do |source|
+ all_matches.concat(source.search_all(name))
+ end
+ all_matches
+ end
+
+ # 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).to_set unless @sources.empty?
+
+ @sources.each do |source|
+ source.unsorted_search(query, base).each do |spec|
+ results << spec if seen.add?(spec.full_name)
+ 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]
+ end
+ end
+
+ def sort_specs(specs)
+ self.class.sort_specs(specs)
+ end
+
+ def local_search(query, base = nil)
+ 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)
+ else
+ raise "You can't search for a #{query.inspect}."
+ end
+ end
+
+ alias_method :[], :search
+
+ def <<(spec)
+ @specs[spec.name][spec.full_name] = spec
+ spec
+ end
+
+ def each(&blk)
+ return enum_for(:each) unless blk
+ specs.values.each do |spec_sets|
+ spec_sets.values.each(&blk)
+ end
+ sources.each {|s| s.each(&blk) }
+ self
+ end
+
+ def spec_names
+ names = specs.keys + sources.map(&:spec_names)
+ names.uniq!
+ names
+ end
+
+ # returns a list of the dependencies
+ def unmet_dependency_names
+ dependency_names.select do |name|
+ name != "bundler" && search(name).empty?
+ end
+ end
+
+ def dependency_names
+ names = []
+ each do |spec|
+ spec.dependencies.each do |dep|
+ next if dep.type == :development
+ names << dep.name
+ end
+ end
+ names.uniq
+ end
+
+ def use(other, override_dupes = false)
+ return unless other
+ other.each do |s|
+ if (dupes = search_by_spec(s)) && !dupes.empty?
+ # safe to << since it's a new array when it has contents
+ @all_specs[s.name] = dupes << s
+ next unless override_dupes
+ end
+ self << s
+ end
+ self
+ end
+
+ def size
+ @sources.inject(@specs.size) do |size, source|
+ size += source.size
+ end
+ end
+
+ # Whether all the specs in self are in other
+ # TODO: rename to #include?
+ def ==(other)
+ all? do |spec|
+ other_spec = other[spec].first
+ other_spec && dependencies_eql?(spec, other_spec) && spec.source == other_spec.source
+ end
+ end
+
+ def dependencies_eql?(spec, other_spec)
+ deps = spec.dependencies.select {|d| d.type != :development }
+ other_deps = other_spec.dependencies.select {|d| d.type != :development }
+ Set.new(deps) == Set.new(other_deps)
+ end
+
+ def add_source(index)
+ raise ArgumentError, "Source must be an index, not #{index.class}" unless index.is_a?(Index)
+ @sources << index
+ @sources.uniq! # need to use uniq! here instead of checking for the item before adding
+ end
+
+ private
+
+ def specs_by_name(name)
+ @specs[name].values
+ 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)
+ if base # allow all platforms when searching from a lockfile
+ dependency.matches_spec?(spec)
+ else
+ dependency.matches_spec?(spec) && Gem::Platform.match(spec.platform)
+ end
+ end
+
+ found
+ end
+ end
+
+ EMPTY_SEARCH = [].freeze
+
+ def search_by_spec(spec)
+ spec = @specs[spec.name][spec.full_name]
+ spec ? [spec] : EMPTY_SEARCH
+ end
+ end
+end
diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb
new file mode 100644
index 0000000000..1bb29f0b36
--- /dev/null
+++ b/lib/bundler/injector.rb
@@ -0,0 +1,253 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Injector
+ INJECTED_GEMS = "injected gems".freeze
+
+ def self.inject(new_deps, options = {})
+ injector = new(new_deps, options)
+ injector.inject(Bundler.default_gemfile, Bundler.default_lockfile)
+ end
+
+ def self.remove(gems, options = {})
+ injector = new(gems, options)
+ injector.remove(Bundler.default_gemfile, Bundler.default_lockfile)
+ end
+
+ def initialize(deps, options = {})
+ @deps = deps
+ @options = options
+ end
+
+ # @param [Pathname] gemfile_path The Gemfile in which to inject the new dependency.
+ # @param [Pathname] lockfile_path The lockfile in which to inject the new dependency.
+ # @return [Array]
+ def inject(gemfile_path, lockfile_path)
+ if Bundler.frozen_bundle?
+ # ensure the lock and Gemfile are synced
+ Bundler.definition.ensure_equivalent_gemfile_and_lockfile(true)
+ end
+
+ # temporarily unfreeze
+ Bundler.settings.temporary(:deployment => false, :frozen => false) do
+ # evaluate the Gemfile we have now
+ builder = Dsl.new
+ builder.eval_gemfile(gemfile_path)
+
+ # don't inject any gems that are already in the Gemfile
+ @deps -= builder.dependencies
+
+ # add new deps to the end of the in-memory Gemfile
+ # Set conservative versioning to false because
+ # we want to let the resolver resolve the version first
+ builder.eval_gemfile(INJECTED_GEMS, build_gem_lines(false)) if @deps.any?
+
+ # resolve to see if the new deps broke anything
+ @definition = builder.to_definition(lockfile_path, {})
+ @definition.resolve_remotely!
+
+ # since nothing broke, we can add those gems to the gemfile
+ append_to(gemfile_path, build_gem_lines(@options[:conservative_versioning])) if @deps.any?
+
+ # since we resolved successfully, write out the lockfile
+ @definition.lock(Bundler.default_lockfile)
+
+ # invalidate the cached Bundler.definition
+ Bundler.reset_paths!
+
+ # return an array of the deps that we added
+ @deps
+ end
+ end
+
+ # @param [Pathname] gemfile_path The Gemfile from which to remove dependencies.
+ # @param [Pathname] lockfile_path The lockfile from which to remove dependencies.
+ # @return [Array]
+ def remove(gemfile_path, lockfile_path)
+ # remove gems from each gemfiles we have
+ Bundler.definition.gemfiles.each do |path|
+ deps = remove_deps(path)
+
+ 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." }
+ end
+ end
+
+ private
+
+ def conservative_version(spec)
+ version = spec.version
+ return ">= 0" if version.nil?
+ segments = version.segments
+ seg_end_index = version >= Gem::Version.new("1.0") ? 1 : 2
+
+ prerelease_suffix = version.to_s.gsub(version.release.to_s, "") if version.prerelease?
+ "#{version_prefix}#{segments[0..seg_end_index].join(".")}#{prerelease_suffix}"
+ end
+
+ def version_prefix
+ if @options[:strict]
+ "= "
+ elsif @options[:optimistic]
+ ">= "
+ else
+ "~> "
+ end
+ end
+
+ def build_gem_lines(conservative_versioning)
+ @deps.map do |d|
+ name = d.name.dump
+
+ requirement = if conservative_versioning
+ ", \"#{conservative_version(@definition.specs[d.name][0])}\""
+ else
+ ", #{d.requirement.as_list.map(&:dump).join(", ")}"
+ end
+
+ if d.groups != Array(:default)
+ group = d.groups.size == 1 ? ", :group => #{d.groups.first.inspect}" : ", :groups => #{d.groups.inspect}"
+ end
+
+ source = ", :source => \"#{d.source}\"" unless d.source.nil?
+
+ %(gem #{name}#{requirement}#{group}#{source})
+ end.join("\n")
+ end
+
+ def append_to(gemfile_path, new_gem_lines)
+ gemfile_path.open("a") do |f|
+ f.puts
+ f.puts new_gem_lines
+ end
+ end
+
+ # evalutes a gemfile to remove the specified gem
+ # from it.
+ def remove_deps(gemfile_path)
+ initial_gemfile = IO.readlines(gemfile_path)
+
+ Bundler.ui.info "Removing gems from #{gemfile_path}"
+
+ # evaluate the Gemfile we have
+ builder = Dsl.new
+ builder.eval_gemfile(gemfile_path)
+
+ removed_deps = remove_gems_from_dependencies(builder, @deps, gemfile_path)
+
+ # abort the opertion if no gems were removed
+ # no need to operate on gemfile furthur
+ return [] if removed_deps.empty?
+
+ cleaned_gemfile = remove_gems_from_gemfile(@deps, gemfile_path)
+
+ SharedHelpers.write_to_gemfile(gemfile_path, cleaned_gemfile)
+
+ # check for errors
+ # including extra gems being removed
+ # or some gems not being removed
+ # and return the actual removed deps
+ cross_check_for_errors(gemfile_path, builder.dependencies, removed_deps, initial_gemfile)
+ end
+
+ # @param [Dsl] builder Dsl object of current Gemfile.
+ # @param [Array] gems Array of names of gems to be removed.
+ # @param [Pathname] path of the Gemfile
+ # @return [Array] removed_deps Array of removed dependencies.
+ def remove_gems_from_dependencies(builder, gems, gemfile_path)
+ removed_deps = []
+
+ gems.each do |gem_name|
+ deleted_dep = builder.dependencies.find {|d| d.name == gem_name }
+
+ if deleted_dep.nil?
+ raise GemfileError, "`#{gem_name}` is not specified in #{gemfile_path} so it could not be removed."
+ end
+
+ builder.dependencies.delete(deleted_dep)
+
+ removed_deps << deleted_dep
+ end
+
+ removed_deps
+ end
+
+ # @param [Array] gems Array of names of gems to be removed.
+ # @param [Pathname] gemfile_path The Gemfile from which to remove dependencies.
+ def remove_gems_from_gemfile(gems, gemfile_path)
+ patterns = /gem\s+(['"])#{Regexp.union(gems)}\1|gem\s*\((['"])#{Regexp.union(gems)}\2\)/
+
+ # remove lines which match the regex
+ new_gemfile = IO.readlines(gemfile_path).reject {|line| line.match(patterns) }
+
+ # remove lone \n and append them with other strings
+ new_gemfile.each_with_index do |_line, index|
+ if new_gemfile[index + 1] == "\n"
+ new_gemfile[index] += new_gemfile[index + 1]
+ new_gemfile.delete_at(index + 1)
+ end
+ end
+
+ %w[group source env install_if].each {|block| remove_nested_blocks(new_gemfile, block) }
+
+ new_gemfile.join.chomp
+ end
+
+ # @param [Array] gemfile Array of gemfile contents.
+ # @param [String] block_name Name of block name to look for.
+ def remove_nested_blocks(gemfile, block_name)
+ nested_blocks = 0
+
+ # count number of nested blocks
+ gemfile.each_with_index {|line, index| nested_blocks += 1 if !gemfile[index + 1].nil? && gemfile[index + 1].include?(block_name) && line.include?(block_name) }
+
+ while nested_blocks >= 0
+ nested_blocks -= 1
+
+ gemfile.each_with_index do |line, index|
+ next unless !line.nil? && line.include?(block_name)
+ if gemfile[index + 1] =~ /^\s*end\s*$/
+ gemfile[index] = nil
+ gemfile[index + 1] = nil
+ end
+ end
+
+ gemfile.compact!
+ end
+ end
+
+ # @param [Pathname] gemfile_path The Gemfile from which to remove dependencies.
+ # @param [Array] original_deps Array of original dependencies.
+ # @param [Array] removed_deps Array of removed dependencies.
+ # @param [Array] initial_gemfile Contents of original Gemfile before any operation.
+ def cross_check_for_errors(gemfile_path, original_deps, removed_deps, initial_gemfile)
+ # evalute the new gemfile to look for any failure cases
+ builder = Dsl.new
+ builder.eval_gemfile(gemfile_path)
+
+ # record gems which were removed but not requested
+ extra_removed_gems = original_deps - builder.dependencies
+
+ # if some extra gems were removed then raise error
+ # and revert Gemfile to original
+ unless extra_removed_gems.empty?
+ SharedHelpers.write_to_gemfile(gemfile_path, initial_gemfile.join)
+
+ raise InvalidOption, "Gems could not be removed. #{extra_removed_gems.join(", ")} would also have been removed. Bundler cannot continue."
+ end
+
+ # record gems which could not be removed due to some reasons
+ errored_deps = builder.dependencies.select {|d| d.gemfile == gemfile_path } & removed_deps.select {|d| d.gemfile == gemfile_path }
+
+ show_warning "#{errored_deps.map(&:name).join(", ")} could not be removed." unless errored_deps.empty?
+
+ # return actual removed dependencies
+ removed_deps - errored_deps
+ end
+
+ def show_warning(message)
+ Bundler.ui.info Bundler.ui.add_color(message, :yellow)
+ end
+ end
+end
diff --git a/lib/bundler/inline.rb b/lib/bundler/inline.rb
new file mode 100644
index 0000000000..9d25f3261a
--- /dev/null
+++ b/lib/bundler/inline.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require "bundler/compatibility_guard"
+
+# Allows for declaring a Gemfile inline in a ruby script, optionally installing
+# any gems that aren't already installed on the user's system.
+#
+# @note Every gem that is specified in this 'Gemfile' will be `require`d, as if
+# the user had manually called `Bundler.require`. To avoid a requested gem
+# being automatically required, add the `:require => false` option to the
+# `gem` dependency declaration.
+#
+# @param install [Boolean] whether gems that aren't already installed on the
+# user's system should be installed.
+# Defaults to `false`.
+#
+# @param gemfile [Proc] a block that is evaluated as a `Gemfile`.
+#
+# @example Using an inline Gemfile
+#
+# #!/usr/bin/env ruby
+#
+# require 'bundler/inline'
+#
+# gemfile do
+# source 'https://rubygems.org'
+# gem 'json', require: false
+# gem 'nap', require: 'rest'
+# gem 'cocoapods', '~> 0.34.1'
+# end
+#
+# puts Pod::VERSION # => "0.34.4"
+#
+def gemfile(install = false, options = {}, &gemfile)
+ require "bundler"
+
+ opts = options.dup
+ ui = opts.delete(:ui) { Bundler::UI::Shell.new }
+ raise ArgumentError, "Unknown options: #{opts.keys.join(", ")}" unless opts.empty?
+
+ old_root = Bundler.method(:root)
+ def Bundler.root
+ Bundler::SharedHelpers.pwd.expand_path
+ end
+ Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", "Gemfile"
+
+ Bundler::Plugin.gemfile_install(&gemfile) if Bundler.feature_flag.plugins?
+ builder = Bundler::Dsl.new
+ builder.instance_eval(&gemfile)
+
+ definition = builder.to_definition(nil, true)
+ def definition.lock(*); end
+ definition.validate_runtime!
+
+ missing_specs = proc do
+ definition.missing_specs?
+ end
+
+ Bundler.ui = ui if install
+ if install || missing_specs.call
+ Bundler.settings.temporary(:inline => true) 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}"
+ end
+ end
+ end
+
+ runtime = Bundler::Runtime.new(nil, definition)
+ runtime.setup.require
+ensure
+ bundler_module = class << Bundler; self; end
+ bundler_module.send(:define_method, :root, old_root) if old_root
+end
diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb
new file mode 100644
index 0000000000..b49cfb6703
--- /dev/null
+++ b/lib/bundler/installer.rb
@@ -0,0 +1,318 @@
+# frozen_string_literal: true
+
+require "erb"
+require "rubygems/dependency_installer"
+require "bundler/worker"
+require "bundler/installer/parallel_installer"
+require "bundler/installer/standalone"
+require "bundler/installer/gem_installer"
+
+module Bundler
+ class Installer
+ class << self
+ attr_accessor :ambiguous_gems
+
+ Installer.ambiguous_gems = []
+ end
+
+ attr_reader :post_install_messages
+
+ # Begins the installation process for Bundler.
+ # For more information see the #run method on this class.
+ def self.install(root, definition, options = {})
+ installer = new(root, definition)
+ Plugin.hook(Plugin::Events::GEM_BEFORE_INSTALL_ALL, definition.dependencies)
+ installer.run(options)
+ Plugin.hook(Plugin::Events::GEM_AFTER_INSTALL_ALL, definition.dependencies)
+ installer
+ end
+
+ def initialize(root, definition)
+ @root = root
+ @definition = definition
+ @post_install_messages = {}
+ end
+
+ # Runs the install procedures for a specific Gemfile.
+ #
+ # Firstly, this method will check to see if `Bundler.bundle_path` exists
+ # and if not then Bundler will create the directory. This is usually the same
+ # location as RubyGems which typically is the `~/.gem` directory
+ # unless other specified.
+ #
+ # Secondly, it checks if Bundler has been configured to be "frozen".
+ # Frozen ensures that the Gemfile and the Gemfile.lock file are matching.
+ # This stops a situation where a developer may update the Gemfile but may not run
+ # `bundle install`, which leads to the Gemfile.lock file not being correctly updated.
+ # If this file is not correctly updated then any other developer running
+ # `bundle install` will potentially not install the correct gems.
+ #
+ # Thirdly, Bundler checks if there are any dependencies specified in the Gemfile.
+ # If there are no dependencies specified then Bundler returns a warning message stating
+ # so and this method returns.
+ #
+ # Fourthly, Bundler checks if the Gemfile.lock exists, and if so
+ # then proceeds to set up a definition based on the Gemfile and the Gemfile.lock.
+ # During this step Bundler will also download information about any new gems
+ # that are not in the Gemfile.lock and resolve any dependencies if needed.
+ #
+ # Fifthly, Bundler resolves the dependencies either through a cache of gems or by remote.
+ # This then leads into the gems being installed, along with stubs for their executables,
+ # but only if the --binstubs option has been passed or Bundler.options[:bin] has been set
+ # earlier.
+ #
+ # Sixthly, a new Gemfile.lock is created from the installed gems to ensure that the next time
+ # that a user runs `bundle install` they will receive any updates from this process.
+ #
+ # Finally, if the user has specified the standalone flag, Bundler will generate the needed
+ # require paths and save them in a `setup.rb` file. See `bundle standalone --help` for more
+ # information.
+ def run(options)
+ create_bundle_path
+
+ ProcessLock.lock do
+ if Bundler.frozen_bundle?
+ @definition.ensure_equivalent_gemfile_and_lockfile(options[:deployment])
+ end
+
+ if @definition.dependencies.empty?
+ Bundler.ui.warn "The Gemfile specifies no dependencies"
+ lock
+ return
+ end
+
+ if resolve_if_needed(options)
+ ensure_specs_are_compatible!
+ warn_on_incompatible_bundler_deps
+ load_plugins
+ options.delete(:jobs)
+ else
+ options[:jobs] = 1 # to avoid the overhead of Bundler::Worker
+ end
+ install(options)
+
+ lock unless Bundler.frozen_bundle?
+ Standalone.new(options[:standalone], @definition).generate if options[:standalone]
+ end
+ end
+
+ def generate_bundler_executable_stubs(spec, options = {})
+ if options[:binstubs_cmd] && spec.executables.empty?
+ options = {}
+ spec.runtime_dependencies.each do |dep|
+ bins = @definition.specs[dep].first.executables
+ options[dep.name] = bins unless bins.empty?
+ end
+ if options.any?
+ Bundler.ui.warn "#{spec.name} has no executables, but you may want " \
+ "one from a gem it depends on."
+ options.each {|name, bins| Bundler.ui.warn " #{name} has: #{bins.join(", ")}" }
+ else
+ Bundler.ui.warn "There are no executables for the gem #{spec.name}."
+ end
+ return
+ end
+
+ # double-assignment to avoid warnings about variables that will be used by ERB
+ bin_path = Bundler.bin_path
+ bin_path = bin_path
+ relative_gemfile_path = Bundler.default_gemfile.relative_path_from(bin_path)
+ relative_gemfile_path = relative_gemfile_path
+ ruby_command = Thor::Util.ruby_command
+ ruby_command = ruby_command
+ template_path = File.expand_path("../templates/Executable", __FILE__)
+ if spec.name == "bundler"
+ template_path += ".bundler"
+ spec.executables = %(bundle)
+ end
+ template = File.read(template_path)
+
+ exists = []
+ spec.executables.each do |executable|
+ binstub_path = "#{bin_path}/#{executable}"
+ if File.exist?(binstub_path) && !options[:force]
+ exists << executable
+ next
+ end
+
+ File.open(binstub_path, "w", 0o777 & ~File.umask) do |f|
+ if RUBY_VERSION >= "2.6"
+ f.puts ERB.new(template, :trim_mode => "-").result(binding)
+ else
+ f.puts ERB.new(template, nil, "-").result(binding)
+ end
+ end
+ end
+
+ if options[:binstubs_cmd] && exists.any?
+ case exists.size
+ when 1
+ Bundler.ui.warn "Skipped #{exists[0]} since it already exists."
+ when 2
+ Bundler.ui.warn "Skipped #{exists.join(" and ")} since they already exist."
+ else
+ items = exists[0...-1].empty? ? nil : exists[0...-1].join(", ")
+ skipped = [items, exists[-1]].compact.join(" and ")
+ Bundler.ui.warn "Skipped #{skipped} since they already exist."
+ end
+ Bundler.ui.warn "If you want to overwrite skipped stubs, use --force."
+ end
+ end
+
+ def generate_standalone_bundler_executable_stubs(spec)
+ # double-assignment to avoid warnings about variables that will be used by ERB
+ bin_path = Bundler.bin_path
+ unless path = Bundler.settings[:path]
+ raise "Can't standalone without an explicit path set"
+ end
+ standalone_path = Bundler.root.join(path).relative_path_from(bin_path)
+ standalone_path = standalone_path
+ template = File.read(File.expand_path("../templates/Executable.standalone", __FILE__))
+ ruby_command = Thor::Util.ruby_command
+ ruby_command = ruby_command
+
+ spec.executables.each do |executable|
+ next if executable == "bundle"
+ executable_path = Pathname(spec.full_gem_path).join(spec.bindir, executable).relative_path_from(bin_path)
+ executable_path = executable_path
+ File.open "#{bin_path}/#{executable}", "w", 0o755 do |f|
+ if RUBY_VERSION >= "2.6"
+ f.puts ERB.new(template, :trim_mode => "-").result(binding)
+ else
+ f.puts ERB.new(template, nil, "-").result(binding)
+ end
+ end
+ end
+ end
+
+ private
+
+ # the order that the resolver provides is significant, since
+ # dependencies might affect the installation of a gem.
+ # that said, it's a rare situation (other than rake), and parallel
+ # installation is SO MUCH FASTER. so we let people opt in.
+ def install(options)
+ force = options["force"]
+ jobs = installation_parallelization(options)
+ install_in_parallel jobs, options[:standalone], force
+ end
+
+ def installation_parallelization(options)
+ if jobs = options.delete(:jobs)
+ return jobs
+ end
+
+ return 1 unless can_install_in_parallel?
+
+ auto_config_jobs = Bundler.feature_flag.auto_config_jobs?
+ if jobs = Bundler.settings[:jobs]
+ if auto_config_jobs
+ jobs
+ else
+ [jobs.pred, 1].max
+ end
+ elsif auto_config_jobs
+ processor_count
+ else
+ 1
+ end
+ end
+
+ def processor_count
+ require "etc"
+ Etc.nprocessors
+ rescue
+ 1
+ end
+
+ def load_plugins
+ Bundler.rubygems.load_plugins
+
+ 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
+ end.flatten
+ Bundler.rubygems.load_plugin_files(path_plugin_files)
+ end
+
+ def ensure_specs_are_compatible!
+ system_ruby = Bundler::RubyVersion.system
+ rubygems_version = Gem::Version.create(Gem::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
+ 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}"
+ end
+ end
+ end
+
+ def warn_on_incompatible_bundler_deps
+ bundler_version = Gem::Version.create(Bundler::VERSION)
+ @definition.specs.each do |spec|
+ spec.dependencies.each do |dep|
+ next if dep.type == :development
+ next unless dep.name == "bundler".freeze
+ next if dep.requirement.satisfied_by?(bundler_version)
+
+ Bundler.ui.warn "#{spec.name} (#{spec.version}) has dependency" \
+ " #{SharedHelpers.pretty_dependency(dep)}" \
+ ", which is unsatisfied by the current bundler version #{VERSION}" \
+ ", so the dependency is being ignored"
+ end
+ end
+ end
+
+ def can_install_in_parallel?
+ if Bundler.rubygems.provides?(">= 2.1.0")
+ true
+ else
+ Bundler.ui.warn "RubyGems #{Gem::VERSION} is not threadsafe, so your "\
+ "gems will be installed one at a time. Upgrade to RubyGems 2.1.0 " \
+ "or higher to enable parallel gem installation."
+ false
+ end
+ end
+
+ def install_in_parallel(size, standalone, force = false)
+ spec_installations = ParallelInstaller.call(self, @definition.specs, size, standalone, force)
+ spec_installations.each do |installation|
+ post_install_messages[installation.name] = installation.post_install_message if installation.has_post_install_message?
+ end
+ end
+
+ def create_bundle_path
+ SharedHelpers.filesystem_access(Bundler.bundle_path.to_s) do |p|
+ Bundler.mkdir_p(p)
+ end unless Bundler.bundle_path.exist?
+ rescue Errno::EEXIST
+ raise PathError, "Could not install to path `#{Bundler.bundle_path}` " \
+ "because a file already exists at that path. Either remove or rename the file so the directory can be created."
+ end
+
+ # returns whether or not a re-resolve was needed
+ def resolve_if_needed(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!
+ true
+ end
+
+ def lock(opts = {})
+ @definition.lock(Bundler.default_lockfile, opts[:preserve_unknown_sections])
+ end
+ end
+end
diff --git a/lib/bundler/installer/gem_installer.rb b/lib/bundler/installer/gem_installer.rb
new file mode 100644
index 0000000000..e5e245f970
--- /dev/null
+++ b/lib/bundler/installer/gem_installer.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+module Bundler
+ class GemInstaller
+ attr_reader :spec, :standalone, :worker, :force, :installer
+
+ def initialize(spec, installer, standalone = false, worker = 0, force = false)
+ @spec = spec
+ @installer = installer
+ @standalone = standalone
+ @worker = worker
+ @force = force
+ end
+
+ def install_from_spec
+ post_install_message = spec_settings ? install_with_settings : install
+ Bundler.ui.debug "#{worker}: #{spec.name} (#{spec.version}) from #{spec.loaded_from}"
+ generate_executable_stubs
+ return true, post_install_message
+ rescue Bundler::InstallHookError, Bundler::SecurityError, APIResponseMismatchError
+ raise
+ rescue Errno::ENOSPC
+ return false, out_of_space_message
+ rescue StandardError => e
+ return false, specific_failure_message(e)
+ end
+
+ private
+
+ def specific_failure_message(e)
+ message = "#{e.class}: #{e.message}\n"
+ message += " " + e.backtrace.join("\n ") + "\n\n" if Bundler.ui.debug?
+ message = message.lines.first + Bundler.ui.add_color(message.lines.drop(1).join, :clear)
+ message + Bundler.ui.add_color(failure_message, :red)
+ end
+
+ def failure_message
+ return install_error_message if spec.source.options["git"]
+ "#{install_error_message}\n#{gem_install_message}"
+ end
+
+ def install_error_message
+ "An error occurred while installing #{spec.name} (#{spec.version}), and Bundler cannot continue."
+ end
+
+ def gem_install_message
+ source = spec.source
+ return unless source.respond_to?(:remotes)
+
+ if source.remotes.size == 1
+ "Make sure that `gem install #{spec.name} -v '#{spec.version}' --source '#{source.remotes.first}'` succeeds before bundling."
+ else
+ "Make sure that `gem install #{spec.name} -v '#{spec.version}'` succeeds before bundling."
+ end
+ end
+
+ def spec_settings
+ # Fetch the build settings, if there are any
+ Bundler.settings["build.#{spec.name}"]
+ end
+
+ def install
+ spec.source.install(spec, :force => force, :ensure_builtin_gems_cached => standalone, :build_args => Array(spec_settings))
+ end
+
+ def install_with_settings
+ # Build arguments are global, so this is mutexed
+ Bundler.rubygems.install_with_build_args([spec_settings]) { install }
+ end
+
+ def out_of_space_message
+ "#{install_error_message}\nYour disk is out of space. Free some space to be able to install your bundle."
+ end
+
+ def generate_executable_stubs
+ return if Bundler.feature_flag.forget_cli_options?
+ return if Bundler.settings[:inline]
+ if Bundler.settings[:bin] && standalone
+ installer.generate_standalone_bundler_executable_stubs(spec)
+ elsif Bundler.settings[:bin]
+ installer.generate_bundler_executable_stubs(spec, :force => true)
+ end
+ end
+ end
+end
diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb
new file mode 100644
index 0000000000..f8a849ccfc
--- /dev/null
+++ b/lib/bundler/installer/parallel_installer.rb
@@ -0,0 +1,233 @@
+# frozen_string_literal: true
+
+require "bundler/worker"
+require "bundler/installer/gem_installer"
+
+module Bundler
+ class ParallelInstaller
+ class SpecInstallation
+ attr_accessor :spec, :name, :post_install_message, :state, :error
+ def initialize(spec)
+ @spec = spec
+ @name = spec.name
+ @state = :none
+ @post_install_message = ""
+ @error = nil
+ end
+
+ def installed?
+ state == :installed
+ end
+
+ def enqueued?
+ state == :enqueued
+ end
+
+ def failed?
+ state == :failed
+ end
+
+ def installation_attempted?
+ installed? || failed?
+ end
+
+ # Only true when spec in neither installed nor already enqueued
+ def ready_to_enqueue?
+ !enqueued? && !installation_attempted?
+ end
+
+ def has_post_install_message?
+ !post_install_message.empty?
+ end
+
+ def ignorable_dependency?(dep)
+ dep.type == :development || dep.name == @name
+ end
+
+ # Checks installed dependencies against spec's dependencies to make
+ # sure needed dependencies have been installed.
+ def dependencies_installed?(all_specs)
+ installed_specs = all_specs.select(&:installed?).map(&:name)
+ dependencies.all? {|d| installed_specs.include? d.name }
+ end
+
+ # Represents only the non-development dependencies, the ones that are
+ # itself and are in the total list.
+ def dependencies
+ @dependencies ||= begin
+ all_dependencies.reject {|dep| ignorable_dependency? dep }
+ end
+ end
+
+ def missing_lockfile_dependencies(all_spec_names)
+ deps = all_dependencies.reject {|dep| ignorable_dependency? dep }
+ deps.reject {|dep| all_spec_names.include? dep.name }
+ end
+
+ # Represents all dependencies
+ def all_dependencies
+ @spec.dependencies
+ end
+
+ def to_s
+ "#<#{self.class} #{@spec.full_name} (#{state})>"
+ end
+ end
+
+ def self.call(*args)
+ new(*args).call
+ end
+
+ attr_reader :size
+
+ def initialize(installer, all_specs, size, standalone, force)
+ @installer = installer
+ @size = size
+ @standalone = standalone
+ @force = force
+ @specs = all_specs.map {|s| SpecInstallation.new(s) }
+ @spec_set = all_specs
+ @rake = @specs.find {|s| s.name == "rake" }
+ end
+
+ def call
+ # Since `autoload` has the potential for threading issues on 1.8.7
+ # TODO: remove in bundler 2.0
+ require "bundler/gem_remote_fetcher" if RUBY_VERSION < "1.9"
+
+ check_for_corrupt_lockfile
+
+ if @size > 1
+ install_with_worker
+ else
+ install_serially
+ end
+
+ handle_error if @specs.any?(&:failed?)
+ @specs
+ ensure
+ worker_pool && worker_pool.stop
+ 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 install_with_worker
+ enqueue_specs
+ process_specs until finished_installing?
+ end
+
+ def install_serially
+ until finished_installing?
+ raise "failed to find a spec to enqueue while installing serially" unless spec_install = @specs.find(&:ready_to_enqueue?)
+ spec_install.state = :enqueued
+ do_install(spec_install, 0)
+ end
+ end
+
+ def worker_pool
+ @worker_pool ||= Bundler::Worker.new @size, "Parallel Installer", lambda { |spec_install, worker_num|
+ do_install(spec_install, worker_num)
+ }
+ end
+
+ def do_install(spec_install, worker_num)
+ Plugin.hook(Plugin::Events::GEM_BEFORE_INSTALL, spec_install)
+ gem_installer = Bundler::GemInstaller.new(
+ spec_install.spec, @installer, @standalone, worker_num, @force
+ )
+ success, message = begin
+ gem_installer.install_from_spec
+ rescue RuntimeError => e
+ raise e, "#{e}\n\n#{require_tree_for_spec(spec_install.spec)}"
+ end
+ if success
+ spec_install.state = :installed
+ spec_install.post_install_message = message unless message.nil?
+ else
+ spec_install.state = :failed
+ spec_install.error = "#{message}\n\n#{require_tree_for_spec(spec_install.spec)}"
+ end
+ Plugin.hook(Plugin::Events::GEM_AFTER_INSTALL, spec_install)
+ spec_install
+ end
+
+ # Dequeue a spec and save its post-install message and then enqueue the
+ # remaining specs.
+ # Some specs might've had to wait til this spec was installed to be
+ # processed so the call to `enqueue_specs` is important after every
+ # dequeue.
+ def process_specs
+ worker_pool.deq
+ enqueue_specs
+ end
+
+ def finished_installing?
+ @specs.all? do |spec|
+ return true if spec.failed?
+ spec.installed?
+ end
+ end
+
+ def handle_error
+ errors = @specs.select(&:failed?).map(&:error)
+ if exception = errors.find {|e| e.is_a?(Bundler::BundlerError) }
+ raise exception
+ end
+ raise Bundler::InstallError, errors.map(&:to_s).join("\n\n")
+ end
+
+ def require_tree_for_spec(spec)
+ tree = @spec_set.what_required(spec)
+ t = String.new("In #{File.basename(SharedHelpers.default_gemfile)}:\n")
+ tree.each_with_index do |s, depth|
+ t << " " * depth.succ << s.name
+ unless tree.last == s
+ t << %( was resolved to #{s.version}, which depends on)
+ end
+ t << %(\n)
+ end
+ t
+ end
+
+ # Keys in the remains hash represent uninstalled gems specs.
+ # We enqueue all gem specs that do not have any dependencies.
+ # Later we call this lambda again to install specs that depended on
+ # previously installed specifications. We continue until all specs
+ # are installed.
+ def enqueue_specs
+ @specs.select(&:ready_to_enqueue?).each do |spec|
+ next if @rake && !@rake.installed? && spec.name != @rake.name
+
+ if spec.dependencies_installed? @specs
+ spec.state = :enqueued
+ worker_pool.enq spec
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/installer/standalone.rb b/lib/bundler/installer/standalone.rb
new file mode 100644
index 0000000000..ce0c9df1eb
--- /dev/null
+++ b/lib/bundler/installer/standalone.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Standalone
+ def initialize(groups, definition)
+ @specs = groups.empty? ? definition.requested_specs : definition.specs_for(groups.map(&:to_sym))
+ end
+
+ def generate
+ SharedHelpers.filesystem_access(bundler_path) do |p|
+ FileUtils.mkdir_p(p)
+ end
+ File.open File.join(bundler_path, "setup.rb"), "w" do |file|
+ file.puts "require 'rbconfig'"
+ file.puts "# ruby 1.8.7 doesn't define RUBY_ENGINE"
+ file.puts "ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'"
+ file.puts "ruby_version = RbConfig::CONFIG[\"ruby_version\"]"
+ file.puts "path = File.expand_path('..', __FILE__)"
+ paths.each do |path|
+ file.puts %($:.unshift "\#{path}/#{path}")
+ end
+ end
+ end
+
+ private
+
+ def paths
+ @specs.map do |spec|
+ next if spec.name == "bundler"
+ Array(spec.require_paths).map do |path|
+ gem_path(path, spec).sub(version_dir, '#{ruby_engine}/#{ruby_version}')
+ # This is a static string intentionally. It's interpolated at a later time.
+ end
+ end.flatten
+ end
+
+ def version_dir
+ "#{Bundler::RubyVersion.system.engine}/#{RbConfig::CONFIG["ruby_version"]}"
+ end
+
+ def bundler_path
+ Bundler.root.join(Bundler.settings[:path], "bundler")
+ end
+
+ def gem_path(path, spec)
+ full_path = Pathname.new(path).absolute? ? path : File.join(spec.full_gem_path, path)
+ Pathname.new(full_path).relative_path_from(Bundler.root.join(bundler_path)).to_s
+ rescue TypeError
+ error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
+ raise Gem::InvalidSpecificationException.new(error_message)
+ end
+ end
+end
diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb
new file mode 100644
index 0000000000..d9cb01f810
--- /dev/null
+++ b/lib/bundler/lazy_specification.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+require "uri"
+require "bundler/match_platform"
+
+module Bundler
+ class LazySpecification
+ Identifier = Struct.new(:name, :version, :source, :platform, :dependencies)
+ class Identifier
+ include Comparable
+ def <=>(other)
+ return unless other.is_a?(Identifier)
+ [name, version, platform_string] <=> [other.name, other.version, other.platform_string]
+ end
+
+ protected
+
+ def platform_string
+ platform_string = platform.to_s
+ platform_string == Index::RUBY ? Index::NULL : platform_string
+ end
+ end
+
+ include MatchPlatform
+
+ attr_reader :name, :version, :dependencies, :platform
+ attr_accessor :source, :remote
+
+ def initialize(name, version, platform, source = nil)
+ @name = name
+ @version = version
+ @dependencies = []
+ @platform = platform || Gem::Platform::RUBY
+ @source = source
+ @specification = nil
+ end
+
+ def full_name
+ if platform == Gem::Platform::RUBY || platform.nil?
+ "#{@name}-#{@version}"
+ else
+ "#{@name}-#{@version}-#{platform}"
+ end
+ end
+
+ def ==(other)
+ identifier == other.identifier
+ end
+
+ def satisfies?(dependency)
+ @name == dependency.name && dependency.requirement.satisfied_by?(Gem::Version.new(@version))
+ end
+
+ def to_lock
+ out = String.new
+
+ if platform == Gem::Platform::RUBY || platform.nil?
+ out << " #{name} (#{version})\n"
+ else
+ out << " #{name} (#{version}-#{platform})\n"
+ end
+
+ dependencies.sort_by(&:to_s).uniq.each do |dep|
+ next if dep.type == :development
+ out << " #{dep.to_lock}\n"
+ end
+
+ out
+ end
+
+ def __materialize__
+ search_object = Bundler.feature_flag.specific_platform? || Bundler.settings[:force_ruby_platform] ? self : Dependency.new(name, version)
+ @specification = if source.is_a?(Source::Gemspec) && source.gemspec.name == name
+ source.gemspec.tap {|s| s.source = source }
+ else
+ search = source.specs.search(search_object).last
+ if search && Gem::Platform.new(search.platform) != Gem::Platform.new(platform) && !search.runtime_dependencies.-(dependencies.reject {|d| d.type == :development }).empty?
+ Bundler.ui.warn "Unable to use the platform-specific (#{search.platform}) version of #{name} (#{version}) " \
+ "because it has different dependencies from the #{platform} version. " \
+ "To use the platform-specific version of the gem, run `bundle config specific_platform true` and install again."
+ search = source.specs.search(self).last
+ end
+ search.dependencies = dependencies if search && (search.is_a?(RemoteSpecification) || search.is_a?(EndpointSpecification))
+ search
+ end
+ end
+
+ def respond_to?(*args)
+ super || @specification ? @specification.respond_to?(*args) : nil
+ end
+
+ def to_s
+ @__to_s ||= if platform == Gem::Platform::RUBY || platform.nil?
+ "#{name} (#{version})"
+ else
+ "#{name} (#{version}-#{platform})"
+ end
+ end
+
+ def identifier
+ @__identifier ||= Identifier.new(name, version, source, platform, dependencies)
+ end
+
+ def git_version
+ return unless source.is_a?(Bundler::Source::Git)
+ " #{source.revision[0..6]}"
+ 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)
+ end
+ end
+end
diff --git a/lib/bundler/lockfile_generator.rb b/lib/bundler/lockfile_generator.rb
new file mode 100644
index 0000000000..585077d18d
--- /dev/null
+++ b/lib/bundler/lockfile_generator.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+module Bundler
+ class LockfileGenerator
+ attr_reader :definition
+ attr_reader :out
+
+ # @private
+ def initialize(definition)
+ @definition = definition
+ @out = String.new
+ end
+
+ def self.generate(definition)
+ new(definition).generate!
+ end
+
+ def generate!
+ add_sources
+ add_platforms
+ add_dependencies
+ add_locked_ruby_version
+ add_bundled_with
+
+ out
+ end
+
+ private
+
+ def add_sources
+ definition.send(:sources).lock_sources.each_with_index do |source, idx|
+ out << "\n" unless idx.zero?
+
+ # Add the source header
+ out << source.to_lock
+
+ # Find all specs for this source
+ specs = definition.resolve.select {|s| source.can_lock?(s) }
+ add_specs(specs)
+ end
+ end
+
+ def add_specs(specs)
+ # This needs to be sorted by full name so that
+ # 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
+ out << spec.to_lock
+ end
+ end
+
+ def add_platforms
+ add_section("PLATFORMS", definition.platforms)
+ end
+
+ def add_dependencies
+ out << "\nDEPENDENCIES\n"
+
+ handled = []
+ definition.dependencies.sort_by(&:to_s).each do |dep|
+ next if handled.include?(dep.name)
+ out << dep.to_lock
+ handled << dep.name
+ end
+ end
+
+ def add_locked_ruby_version
+ return unless locked_ruby_version = definition.locked_ruby_version
+ add_section("RUBY VERSION", locked_ruby_version.to_s)
+ end
+
+ def add_bundled_with
+ add_section("BUNDLED WITH", definition.locked_bundler_version.to_s)
+ end
+
+ def add_section(name, value)
+ out << "\n#{name}\n"
+ case value
+ when Array
+ value.map(&:to_s).sort.each do |val|
+ out << " #{val}\n"
+ end
+ when Hash
+ value.to_a.sort_by {|k, _| k.to_s }.each do |key, val|
+ out << " #{key}: #{val}\n"
+ end
+ when String
+ out << " #{value}\n"
+ else
+ raise ArgumentError, "#{value.inspect} can't be serialized in a lockfile"
+ end
+ end
+ end
+end
diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb
new file mode 100644
index 0000000000..ff706fca1d
--- /dev/null
+++ b/lib/bundler/lockfile_parser.rb
@@ -0,0 +1,256 @@
+# frozen_string_literal: true
+
+# Some versions of the Bundler 1.1 RC series introduced corrupted
+# lockfiles. There were two major problems:
+#
+# * multiple copies of the same GIT section appeared in the lockfile
+# * when this happened, those sections got multiple copies of gems
+# in those sections.
+#
+# As a result, Bundler 1.1 contains code that fixes the earlier
+# corruption. We will remove this fix-up code in Bundler 1.2.
+
+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
+ OPTIONS = /^ ([a-z]+): (.*)$/i
+ SOURCE = [GIT, GEM, PATH, PLUGIN].freeze
+
+ SECTIONS_BY_VERSION_INTRODUCED = {
+ # The strings have to be dup'ed for old RG on Ruby 2.3+
+ # TODO: remove dup in Bundler 2.0
+ Gem::Version.create("1.0".dup) => [DEPENDENCIES, PLATFORMS, GIT, GEM, PATH].freeze,
+ Gem::Version.create("1.10".dup) => [BUNDLED].freeze,
+ Gem::Version.create("1.12".dup) => [RUBY].freeze,
+ Gem::Version.create("1.13".dup) => [PLUGIN].freeze,
+ }.freeze
+
+ KNOWN_SECTIONS = SECTIONS_BY_VERSION_INTRODUCED.values.flatten.freeze
+
+ ENVIRONMENT_VERSION_SECTIONS = [BUNDLED, RUBY].freeze
+
+ def self.sections_in_lockfile(lockfile_contents)
+ lockfile_contents.scan(/^\w[\w ]*$/).uniq
+ end
+
+ def self.unknown_sections_in_lockfile(lockfile_contents)
+ sections_in_lockfile(lockfile_contents) - KNOWN_SECTIONS
+ end
+
+ def self.sections_to_ignore(base_version = nil)
+ base_version &&= base_version.release
+ base_version ||= Gem::Version.create("1.0".dup)
+ attributes = []
+ SECTIONS_BY_VERSION_INTRODUCED.each do |version, introduced|
+ next if version <= base_version
+ attributes += introduced
+ end
+ attributes
+ end
+
+ def initialize(lockfile)
+ @platforms = []
+ @sources = []
+ @dependencies = {}
+ @state = nil
+ @specs = {}
+
+ @rubygems_aggregate = Source::Rubygems.new
+
+ 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
+
+ lockfile.split(/(?:\r?\n)+/).each do |line|
+ if SOURCE.include?(line)
+ @state = :source
+ parse_source(line)
+ elsif line == DEPENDENCIES
+ @state = :dependency
+ elsif line == PLATFORMS
+ @state = :platform
+ elsif line == RUBY
+ @state = :ruby
+ elsif line == BUNDLED
+ @state = :bundled_with
+ elsif line =~ /^[^\s]/
+ @state = nil
+ elsif @state
+ send("parse_#{@state}", line)
+ end
+ end
+ @sources << @rubygems_aggregate unless Bundler.feature_flag.lockfile_uses_separate_rubygems_sources?
+ @specs = @specs.values.sort_by(&:identifier)
+ warn_for_outdated_bundler_version
+ rescue ArgumentError => e
+ Bundler.ui.debug(e)
+ raise LockfileError, "Your lockfile is unreadable. Run `rm #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` " \
+ "and then `bundle install` to generate a new lockfile."
+ end
+
+ def warn_for_outdated_bundler_version
+ return unless bundler_version
+ prerelease_text = bundler_version.prerelease? ? " --pre" : ""
+ current_version = Gem::Version.create(Bundler::VERSION)
+ case current_version.segments.first <=> bundler_version.segments.first
+ when -1
+ raise LockfileError, "You must use Bundler #{bundler_version.segments.first} or greater with this lockfile."
+ when 0
+ if current_version < bundler_version
+ Bundler.ui.warn "Warning: the running version of Bundler (#{current_version}) is older " \
+ "than the version that created the lockfile (#{bundler_version}). We suggest you " \
+ "upgrade to the latest version of Bundler by running `gem " \
+ "install bundler#{prerelease_text}`.\n"
+ end
+ end
+ end
+
+ private
+
+ TYPES = {
+ GIT => Bundler::Source::Git,
+ GEM => Bundler::Source::Rubygems,
+ PATH => Bundler::Source::Path,
+ PLUGIN => Bundler::Plugin,
+ }.freeze
+
+ def parse_source(line)
+ case line
+ when SPECS
+ case @type
+ when PATH
+ @current_source = TYPES[@type].from_lock(@opts)
+ @sources << @current_source
+ when GIT
+ @current_source = TYPES[@type].from_lock(@opts)
+ # Strip out duplicate GIT sections
+ if @sources.include?(@current_source)
+ @current_source = @sources.find {|s| s == @current_source }
+ else
+ @sources << @current_source
+ end
+ when GEM
+ if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources?
+ @opts["remotes"] = @opts.delete("remote")
+ @current_source = TYPES[@type].from_lock(@opts)
+ @sources << @current_source
+ else
+ Array(@opts["remote"]).each do |url|
+ @rubygems_aggregate.add_remote(url)
+ end
+ @current_source = @rubygems_aggregate
+ end
+ when PLUGIN
+ @current_source = Plugin.source_from_lock(@opts)
+ @sources << @current_source
+ end
+ when OPTIONS
+ value = $2
+ value = true if value == "true"
+ value = false if value == "false"
+
+ key = $1
+
+ if @opts[key]
+ @opts[key] = Array(@opts[key])
+ @opts[key] << value
+ else
+ @opts[key] = value
+ end
+ when *SOURCE
+ @current_source = nil
+ @opts = {}
+ @type = line
+ else
+ parse_spec(line)
+ end
+ end
+
+ space = / /
+ NAME_VERSION = /
+ ^(#{space}{2}|#{space}{4}|#{space}{6})(?!#{space}) # Exactly 2, 4, or 6 spaces at the start of the line
+ (.*?) # Name
+ (?:#{space}\(([^-]*) # Space, followed by version
+ (?:-(.*))?\))? # Optional platform
+ (!)? # Optional pinned marker
+ $ # Line end
+ /xo
+
+ def parse_dependency(line)
+ return unless line =~ NAME_VERSION
+ spaces = $1
+ return unless spaces.size == 2
+ name = $2
+ version = $3
+ pinned = $5
+
+ version = version.split(",").map(&:strip) if version
+
+ dep = Bundler::Dependency.new(name, version)
+
+ if pinned && dep.name != "bundler"
+ spec = @specs.find {|_, v| v.name == dep.name }
+ dep.source = spec.last.source if spec
+
+ # Path sources need to know what the default name / version
+ # to use in the case that there are no gemspecs present. A fake
+ # gemspec is created based on the version set on the dependency
+ # TODO: Use the version from the spec instead of from the dependency
+ if version && version.size == 1 && version.first =~ /^\s*= (.+)\s*$/ && dep.source.is_a?(Bundler::Source::Path)
+ dep.source.name = name
+ dep.source.version = $1
+ end
+ end
+
+ @dependencies[dep.name] = dep
+ end
+
+ def parse_spec(line)
+ return unless line =~ NAME_VERSION
+ spaces = $1
+ name = $2
+ version = $3
+ platform = $4
+
+ if spaces.size == 4
+ version = Gem::Version.new(version)
+ platform = platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY
+ @current_spec = LazySpecification.new(name, version, platform)
+ @current_spec.source = @current_source
+
+ # Avoid introducing multiple copies of the same spec (caused by
+ # duplicate GIT sections)
+ @specs[@current_spec.identifier] ||= @current_spec
+ elsif spaces.size == 6
+ version = version.split(",").map(&:strip) if version
+ dep = Gem::Dependency.new(name, version)
+ @current_spec.dependencies << dep
+ end
+ end
+
+ def parse_platform(line)
+ @platforms << Gem::Platform.new($1) if line =~ /^ (.*)$/
+ end
+
+ def parse_bundled_with(line)
+ line = line.strip
+ return unless Gem::Version.correct?(line)
+ @bundler_version = Gem::Version.create(line)
+ end
+
+ def parse_ruby(line)
+ @ruby_version = line.strip
+ end
+ end
+end
diff --git a/lib/bundler/match_platform.rb b/lib/bundler/match_platform.rb
new file mode 100644
index 0000000000..56cbbfb95d
--- /dev/null
+++ b/lib/bundler/match_platform.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require "bundler/gem_helpers"
+
+module Bundler
+ module MatchPlatform
+ include GemHelpers
+
+ def match_platform(p)
+ MatchPlatform.platforms_match?(platform, p)
+ end
+
+ def self.platforms_match?(gemspec_platform, local_platform)
+ return true if gemspec_platform.nil?
+ return true if Gem::Platform::RUBY == gemspec_platform
+ return true if local_platform == gemspec_platform
+ gemspec_platform = Gem::Platform.new(gemspec_platform)
+ return true if GemHelpers.generic(gemspec_platform) === local_platform
+ return true if gemspec_platform === local_platform
+
+ false
+ end
+ end
+end
diff --git a/lib/bundler/mirror.rb b/lib/bundler/mirror.rb
new file mode 100644
index 0000000000..b15190e7e5
--- /dev/null
+++ b/lib/bundler/mirror.rb
@@ -0,0 +1,223 @@
+# frozen_string_literal: true
+
+require "socket"
+
+module Bundler
+ class Settings
+ # Class used to build the mirror set and then find a mirror for a given URI
+ #
+ # @param prober [Prober object, nil] by default a TCPSocketProbe, this object
+ # will be used to probe the mirror address to validate that the mirror replies.
+ class Mirrors
+ def initialize(prober = nil)
+ @all = Mirror.new
+ @prober = prober || TCPSocketProbe.new
+ @mirrors = {}
+ end
+
+ # Returns a mirror for the given uri.
+ #
+ # Depending on the uri having a valid mirror or not, it may be a
+ # mirror that points to the provided uri
+ def for(uri)
+ if @all.validate!(@prober).valid?
+ @all
+ else
+ fetch_valid_mirror_for(Settings.normalize_uri(uri))
+ end
+ end
+
+ def each
+ @mirrors.each do |k, v|
+ yield k, v.uri.to_s
+ end
+ end
+
+ def parse(key, value)
+ config = MirrorConfig.new(key, value)
+ mirror = if config.all?
+ @all
+ else
+ @mirrors[config.uri] ||= Mirror.new
+ end
+ config.update_mirror(mirror)
+ end
+
+ private
+
+ def fetch_valid_mirror_for(uri)
+ downcased = uri.to_s.downcase
+ mirror = @mirrors[downcased] || @mirrors[URI(downcased).host] || Mirror.new(uri)
+ mirror.validate!(@prober)
+ mirror = Mirror.new(uri) unless mirror.valid?
+ mirror
+ end
+ end
+
+ # A mirror
+ #
+ # Contains both the uri that should be used as a mirror and the
+ # fallback timeout which will be used for probing if the mirror
+ # replies on time or not.
+ class Mirror
+ DEFAULT_FALLBACK_TIMEOUT = 0.1
+
+ attr_reader :uri, :fallback_timeout
+
+ def initialize(uri = nil, fallback_timeout = 0)
+ self.uri = uri
+ self.fallback_timeout = fallback_timeout
+ @valid = nil
+ end
+
+ def uri=(uri)
+ @uri = if uri.nil?
+ nil
+ else
+ URI(uri.to_s)
+ end
+ @valid = nil
+ end
+
+ def fallback_timeout=(timeout)
+ case timeout
+ when true, "true"
+ @fallback_timeout = DEFAULT_FALLBACK_TIMEOUT
+ when false, "false"
+ @fallback_timeout = 0
+ else
+ @fallback_timeout = timeout.to_i
+ end
+ @valid = nil
+ end
+
+ def ==(other)
+ !other.nil? && uri == other.uri && fallback_timeout == other.fallback_timeout
+ end
+
+ def valid?
+ return false if @uri.nil?
+ return @valid unless @valid.nil?
+ false
+ end
+
+ def validate!(probe = nil)
+ @valid = false if uri.nil?
+ if @valid.nil?
+ @valid = fallback_timeout == 0 || (probe || TCPSocketProbe.new).replies?(self)
+ end
+ self
+ end
+ end
+
+ # Class used to parse one configuration line
+ #
+ # Gets the configuration line and the value.
+ # This object provides a `update_mirror` method
+ # used to setup the given mirror value.
+ class MirrorConfig
+ attr_accessor :uri, :value
+
+ def initialize(config_line, value)
+ uri, fallback =
+ config_line.match(%r{\Amirror\.(all|.+?)(\.fallback_timeout)?\/?\z}).captures
+ @fallback = !fallback.nil?
+ @all = false
+ if uri == "all"
+ @all = true
+ else
+ @uri = URI(uri).absolute? ? Settings.normalize_uri(uri) : uri
+ end
+ @value = value
+ end
+
+ def all?
+ @all
+ end
+
+ def update_mirror(mirror)
+ if @fallback
+ mirror.fallback_timeout = @value
+ else
+ mirror.uri = Settings.normalize_uri(@value)
+ end
+ end
+ end
+
+ # Class used for probing TCP availability for a given mirror.
+ 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
+ end
+ end
+
+ private
+
+ def wait_for_writtable_socket(socket, address, timeout)
+ if IO.select(nil, [socket], nil, timeout)
+ probe_writtable_socket(socket, address)
+ else # TCP Handshake timed out, or there is something dropping packets
+ false
+ end
+ end
+
+ def probe_writtable_socket(socket, address)
+ socket.connect_nonblock(address)
+ rescue Errno::EISCONN
+ true
+ rescue StandardError # Connection failed
+ false
+ end
+ end
+ end
+
+ # Class used to build the list of sockets that correspond to
+ # a given mirror.
+ #
+ # One mirror may correspond to many different addresses, both
+ # because of it having many dns entries or because
+ # the network interface is both ipv4 and ipv5
+ class MirrorSockets
+ def initialize(mirror)
+ @timeout = mirror.fallback_timeout
+ @addresses = Socket.getaddrinfo(mirror.uri.host, mirror.uri.port).map do |address|
+ SocketAddress.new(address[0], address[3], address[1])
+ end
+ end
+
+ def any?
+ @addresses.any? do |address|
+ socket = Socket.new(Socket.const_get(address.type), Socket::SOCK_STREAM, 0)
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
+ value = yield socket, address.to_socket_address, @timeout
+ socket.close unless socket.closed?
+ value
+ end
+ end
+ end
+
+ # Socket address builder.
+ #
+ # Given a socket type, a host and a port,
+ # provides a method to build sockaddr string
+ class SocketAddress
+ attr_reader :type, :host, :port
+
+ def initialize(type, host, port)
+ @type = type
+ @host = host
+ @port = port
+ end
+
+ def to_socket_address
+ Socket.pack_sockaddr_in(@port, @host)
+ end
+ end
+end
diff --git a/lib/bundler/plugin.rb b/lib/bundler/plugin.rb
new file mode 100644
index 0000000000..53f9806b73
--- /dev/null
+++ b/lib/bundler/plugin.rb
@@ -0,0 +1,292 @@
+# frozen_string_literal: true
+
+require "bundler/plugin/api"
+
+module Bundler
+ module Plugin
+ autoload :DSL, "bundler/plugin/dsl"
+ autoload :Events, "bundler/plugin/events"
+ autoload :Index, "bundler/plugin/index"
+ autoload :Installer, "bundler/plugin/installer"
+ autoload :SourceList, "bundler/plugin/source_list"
+
+ class MalformattedPlugin < PluginError; end
+ class UndefinedCommandError < PluginError; end
+ class UnknownSourceError < PluginError; end
+
+ PLUGIN_FILE_NAME = "plugins.rb".freeze
+
+ module_function
+
+ def reset!
+ instance_variables.each {|i| remove_instance_variable(i) }
+
+ @sources = {}
+ @commands = {}
+ @hooks_by_event = Hash.new {|h, k| h[k] = [] }
+ @loaded_plugin_names = []
+ end
+
+ reset!
+
+ # Installs a new plugin by the given name
+ #
+ # @param [Array<String>] names the name of plugin to be installed
+ # @param [Hash] options various parameters as described in description.
+ # Refer to cli/plugin for available options
+ def install(names, options)
+ specs = Installer.new.install(names, options)
+
+ save_plugins names, specs
+ rescue PluginError => e
+ if specs
+ specs_to_delete = Hash[specs.select {|k, _v| names.include?(k) && !index.commands.values.include?(k) }]
+ specs_to_delete.values.each {|spec| Bundler.rm_rf(spec.full_gem_path) }
+ end
+
+ Bundler.ui.error "Failed to install plugin #{name}: #{e.message}\n #{e.backtrace.join("\n ")}"
+ end
+
+ # Evaluates the Gemfile with a limited DSL and installs the plugins
+ # specified by plugin method
+ #
+ # @param [Pathname] gemfile path
+ # @param [Proc] block that can be evaluated for (inline) Gemfile
+ def gemfile_install(gemfile = nil, &inline)
+ builder = DSL.new
+ if block_given?
+ builder.instance_eval(&inline)
+ else
+ builder.eval_gemfile(gemfile)
+ end
+ definition = builder.to_definition(nil, true)
+
+ return if definition.dependencies.empty?
+
+ plugins = definition.dependencies.map(&:name).reject {|p| index.installed? p }
+ installed_specs = Installer.new.install_definition(definition)
+
+ save_plugins plugins, installed_specs, builder.inferred_plugins
+ rescue RuntimeError => e
+ unless e.is_a?(GemfileError)
+ Bundler.ui.error "Failed to install plugin: #{e.message}\n #{e.backtrace[0]}"
+ end
+ raise
+ end
+
+ # The index object used to store the details about the plugin
+ def index
+ @index ||= Index.new
+ end
+
+ # The directory root for all plugin related data
+ #
+ # If run in an app, points to local root, in app_config_path
+ # Otherwise, points to global root, in Bundler.user_bundle_path("plugin")
+ def root
+ @root ||= if SharedHelpers.in_bundle?
+ local_root
+ else
+ global_root
+ end
+ end
+
+ def local_root
+ Bundler.app_config_path.join("plugin")
+ end
+
+ # The global directory root for all plugin related data
+ def global_root
+ Bundler.user_bundle_path("plugin")
+ end
+
+ # The cache directory for plugin stuffs
+ def cache
+ @cache ||= root.join("cache")
+ end
+
+ # To be called via the API to register to handle a command
+ def add_command(command, cls)
+ @commands[command] = cls
+ end
+
+ # Checks if any plugin handles the command
+ def command?(command)
+ !index.command_plugin(command).nil?
+ end
+
+ # To be called from Cli class to pass the command and argument to
+ # approriate plugin class
+ def exec_command(command, args)
+ raise UndefinedCommandError, "Command `#{command}` not found" unless command? command
+
+ load_plugin index.command_plugin(command) unless @commands.key? command
+
+ @commands[command].new.exec(command, args)
+ end
+
+ # To be called via the API to register to handle a source plugin
+ def add_source(source, cls)
+ @sources[source] = cls
+ end
+
+ # Checks if any plugin declares the source
+ def source?(name)
+ !index.source_plugin(name.to_s).nil?
+ end
+
+ # @return [Class] that handles the source. The calss includes API::Source
+ def source(name)
+ raise UnknownSourceError, "Source #{name} not found" unless source? name
+
+ load_plugin(index.source_plugin(name)) unless @sources.key? name
+
+ @sources[name]
+ end
+
+ # @param [Hash] The options that are present in the lock file
+ # @return [API::Source] the instance of the class that handles the source
+ # type passed in locked_opts
+ def source_from_lock(locked_opts)
+ src = source(locked_opts["type"])
+
+ src.new(locked_opts.merge("uri" => locked_opts["remote"]))
+ end
+
+ # To be called via the API to register a hooks and corresponding block that
+ # will be called to handle the hook
+ def add_hook(event, &block)
+ unless Events.defined_event?(event)
+ raise ArgumentError, "Event '#{event}' not defined in Bundler::Plugin::Events"
+ end
+ @hooks_by_event[event.to_s] << block
+ end
+
+ # Runs all the hooks that are registered for the passed event
+ #
+ # It passes the passed arguments and block to the block registered with
+ # the api.
+ #
+ # @param [String] event
+ def hook(event, *args, &arg_blk)
+ return unless Bundler.feature_flag.plugins?
+ unless Events.defined_event?(event)
+ raise ArgumentError, "Event '#{event}' not defined in Bundler::Plugin::Events"
+ end
+
+ plugins = index.hook_plugins(event)
+ return unless plugins.any?
+
+ (plugins - @loaded_plugin_names).each {|name| load_plugin(name) }
+
+ @hooks_by_event[event].each {|blk| blk.call(*args, &arg_blk) }
+ end
+
+ # currently only intended for specs
+ #
+ # @return [String, nil] installed path
+ def installed?(plugin)
+ Index.new.installed?(plugin)
+ end
+
+ # Post installation processing and registering with index
+ #
+ # @param [Array<String>] plugins list to be installed
+ # @param [Hash] specs of plugins mapped to installation path (currently they
+ # contain all the installed specs, including plugins)
+ # @param [Array<String>] names of inferred source plugins that can be ignored
+ def save_plugins(plugins, specs, optional_plugins = [])
+ plugins.each do |name|
+ spec = specs[name]
+ validate_plugin! Pathname.new(spec.full_gem_path)
+ installed = register_plugin(name, spec, optional_plugins.include?(name))
+ Bundler.ui.info "Installed plugin #{name}" if installed
+ end
+ end
+
+ # Checks if the gem is good to be a plugin
+ #
+ # At present it only checks whether it contains plugins.rb file
+ #
+ # @param [Pathname] plugin_path the path plugin is installed at
+ # @raise [MalformattedPlugin] if plugins.rb file is not found
+ def validate_plugin!(plugin_path)
+ plugin_file = plugin_path.join(PLUGIN_FILE_NAME)
+ raise MalformattedPlugin, "#{PLUGIN_FILE_NAME} was not found in the plugin." unless plugin_file.file?
+ end
+
+ # Runs the plugins.rb file in an isolated namespace, records the plugin
+ # actions it registers for and then passes the data to index to be stored.
+ #
+ # @param [String] name the name of the plugin
+ # @param [Specification] spec of installed plugin
+ # @param [Boolean] optional_plugin, removed if there is conflict with any
+ # other plugin (used for default source plugins)
+ #
+ # @raise [MalformattedPlugin] if plugins.rb raises any error
+ def register_plugin(name, spec, optional_plugin = false)
+ commands = @commands
+ sources = @sources
+ hooks = @hooks_by_event
+
+ @commands = {}
+ @sources = {}
+ @hooks_by_event = Hash.new {|h, k| h[k] = [] }
+
+ load_paths = spec.load_paths
+ add_to_load_path(load_paths)
+ path = Pathname.new spec.full_gem_path
+
+ begin
+ load path.join(PLUGIN_FILE_NAME), true
+ rescue StandardError => e
+ raise MalformattedPlugin, "#{e.class}: #{e.message}"
+ end
+
+ if optional_plugin && @sources.keys.any? {|s| source? s }
+ Bundler.rm_rf(path)
+ false
+ else
+ index.register_plugin(name, path.to_s, load_paths, @commands.keys,
+ @sources.keys, @hooks_by_event.keys)
+ true
+ end
+ ensure
+ @commands = commands
+ @sources = sources
+ @hooks_by_event = hooks
+ end
+
+ # Executes the plugins.rb file
+ #
+ # @param [String] name of the plugin
+ def load_plugin(name)
+ # Need to ensure before this that plugin root where the rest of gems
+ # are installed to be on load path to support plugin deps. Currently not
+ # done to avoid conflicts
+ path = index.plugin_path(name)
+
+ add_to_load_path(index.load_paths(name))
+
+ load path.join(PLUGIN_FILE_NAME)
+
+ @loaded_plugin_names << name
+ rescue RuntimeError => e
+ Bundler.ui.error "Failed loading plugin #{name}: #{e.message}"
+ raise
+ end
+
+ def add_to_load_path(load_paths)
+ if insert_index = Bundler.rubygems.load_path_insert_index
+ $LOAD_PATH.insert(insert_index, *load_paths)
+ else
+ $LOAD_PATH.unshift(*load_paths)
+ end
+ end
+
+ class << self
+ private :load_plugin, :register_plugin, :save_plugins, :validate_plugin!,
+ :add_to_load_path
+ end
+ end
+end
diff --git a/lib/bundler/plugin/api.rb b/lib/bundler/plugin/api.rb
new file mode 100644
index 0000000000..a2d5cbb4ac
--- /dev/null
+++ b/lib/bundler/plugin/api.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+module Bundler
+ # This is the interfacing class represents the API that we intend to provide
+ # the plugins to use.
+ #
+ # For plugins to be independent of the Bundler internals they shall limit their
+ # interactions to methods of this class only. This will save them from breaking
+ # when some internal change.
+ #
+ # Currently we are delegating the methods defined in Bundler class to
+ # itself. So, this class acts as a buffer.
+ #
+ # If there is some change in the Bundler class that is incompatible to its
+ # previous behavior or if otherwise desired, we can reimplement(or implement)
+ # the method to preserve compatibility.
+ #
+ # To use this, either the class can inherit this class or use it directly.
+ # For example of both types of use, refer the file `spec/plugins/command.rb`
+ #
+ # To use it without inheriting, you will have to create an object of this
+ # to use the functions (except for declaration functions like command, source,
+ # and hooks).
+ module Plugin
+ class API
+ autoload :Source, "bundler/plugin/api/source"
+
+ # The plugins should declare that they handle a command through this helper.
+ #
+ # @param [String] command being handled by them
+ # @param [Class] (optional) class that handles the command. If not
+ # provided, the `self` class will be used.
+ def self.command(command, cls = self)
+ Plugin.add_command command, cls
+ end
+
+ # The plugins should declare that they provide a installation source
+ # through this helper.
+ #
+ # @param [String] the source type they provide
+ # @param [Class] (optional) class that handles the source. If not
+ # provided, the `self` class will be used.
+ def self.source(source, cls = self)
+ cls.send :include, Bundler::Plugin::API::Source
+ Plugin.add_source source, cls
+ end
+
+ def self.hook(event, &block)
+ Plugin.add_hook(event, &block)
+ end
+
+ # The cache dir to be used by the plugins for storage
+ #
+ # @return [Pathname] path of the cache dir
+ def cache_dir
+ Plugin.cache.join("plugins")
+ end
+
+ # A tmp dir to be used by plugins
+ # Accepts names that get concatenated as suffix
+ #
+ # @return [Pathname] object for the new directory created
+ def tmp(*names)
+ Bundler.tmp(["plugin", *names].join("-"))
+ end
+
+ def method_missing(name, *args, &blk)
+ return Bundler.send(name, *args, &blk) if Bundler.respond_to?(name)
+
+ return SharedHelpers.send(name, *args, &blk) if SharedHelpers.respond_to?(name)
+
+ super
+ end
+
+ def respond_to_missing?(name, include_private = false)
+ SharedHelpers.respond_to?(name, include_private) ||
+ Bundler.respond_to?(name, include_private) || super
+ end
+ end
+ end
+end
diff --git a/lib/bundler/plugin/api/source.rb b/lib/bundler/plugin/api/source.rb
new file mode 100644
index 0000000000..586477efb5
--- /dev/null
+++ b/lib/bundler/plugin/api/source.rb
@@ -0,0 +1,306 @@
+# frozen_string_literal: true
+
+require "uri"
+
+module Bundler
+ module Plugin
+ class API
+ # This class provides the base to build source plugins
+ # All the method here are required to build a source plugin (except
+ # `uri_hash`, `gem_install_dir`; they are helpers).
+ #
+ # Defaults for methods, where ever possible are provided which is
+ # expected to work. But, all source plugins have to override
+ # `fetch_gemspec_files` and `install`. Defaults are also not provided for
+ # `remote!`, `cache!` and `unlock!`.
+ #
+ # The defaults shall work for most situations but nevertheless they can
+ # be (preferably should be) overridden as per the plugins' needs safely
+ # (as long as they behave as expected).
+ # On overriding `initialize` you should call super first.
+ #
+ # If required plugin should override `hash`, `==` and `eql?` methods to be
+ # able to match objects representing same sources, but may be created in
+ # different situation (like form gemfile and lockfile). The default ones
+ # checks only for class and uri, but elaborate source plugins may need
+ # more comparisons (e.g. git checking on branch or tag).
+ #
+ # @!attribute [r] uri
+ # @return [String] the remote specified with `source` block in Gemfile
+ #
+ # @!attribute [r] options
+ # @return [String] options passed during initialization (either from
+ # lockfile or Gemfile)
+ #
+ # @!attribute [r] name
+ # @return [String] name that can be used to uniquely identify a source
+ #
+ # @!attribute [rw] dependency_names
+ # @return [Array<String>] Names of dependencies that the source should
+ # try to resolve. It is not necessary to use this list intenally. This
+ # is present to be compatible with `Definition` and is used by
+ # rubygems source.
+ module Source
+ attr_reader :uri, :options, :name
+ attr_accessor :dependency_names
+
+ def initialize(opts)
+ @options = opts
+ @dependency_names = []
+ @uri = opts["uri"]
+ @type = opts["type"]
+ @name = opts["name"] || "#{@type} at #{@uri}"
+ end
+
+ # This is used by the default `spec` method to constructs the
+ # Specification objects for the gems and versions that can be installed
+ # by this source plugin.
+ #
+ # Note: If the spec method is overridden, this function is not necessary
+ #
+ # @return [Array<String>] paths of the gemspec files for gems that can
+ # be installed
+ def fetch_gemspec_files
+ []
+ end
+
+ # Options to be saved in the lockfile so that the source plugin is able
+ # to check out same version of gem later.
+ #
+ # There options are passed when the source plugin is created from the
+ # lock file.
+ #
+ # @return [Hash]
+ def options_to_lock
+ {}
+ end
+
+ # Install the gem specified by the spec at appropriate path.
+ # `install_path` provides a sufficient default, if the source can only
+ # satisfy one gem, but is not binding.
+ #
+ # @return [String] post installation message (if any)
+ def install(spec, opts)
+ raise MalformattedPlugin, "Source plugins need to override the install method."
+ end
+
+ # It builds extensions, generates bins and installs them for the spec
+ # provided.
+ #
+ # It depends on `spec.loaded_from` to get full_gem_path. The source
+ # plugins should set that.
+ #
+ # It should be called in `install` after the plugin is done placing the
+ # gem at correct install location.
+ #
+ # It also runs Gem hooks `pre_install`, `post_build` and `post_install`
+ #
+ # Note: Do not override if you don't know what you are doing.
+ def post_install(spec, disable_exts = false)
+ opts = { :env_shebang => false, :disable_extensions => disable_exts }
+ installer = Bundler::Source::Path::Installer.new(spec, opts)
+ installer.post_install
+ end
+
+ # A default installation path to install a single gem. If the source
+ # servers multiple gems, it's not of much use and the source should one
+ # of its own.
+ def install_path
+ @install_path ||=
+ begin
+ base_name = File.basename(URI.parse(uri).normalize.path)
+
+ gem_install_dir.join("#{base_name}-#{uri_hash[0..11]}")
+ end
+ end
+
+ # Parses the gemspec files to find the specs for the gems that can be
+ # satisfied by the source.
+ #
+ # Few important points to keep in mind:
+ # - If the gems are not installed then it shall return specs for all
+ # the gems it can satisfy
+ # - If gem is installed (that is to be detected by the plugin itself)
+ # then it shall return at least the specs that are installed.
+ # - The `loaded_from` for each of the specs shall be correct (it is
+ # used to find the load path)
+ #
+ # @return [Bundler::Index] index containing the specs
+ def specs
+ files = fetch_gemspec_files
+
+ Bundler::Index.build do |index|
+ files.each do |file|
+ next unless spec = Bundler.load_gemspec(file)
+ Bundler.rubygems.set_installed_by_version(spec)
+
+ spec.source = self
+ Bundler.rubygems.validate(spec)
+
+ index << spec
+ end
+ end
+ end
+
+ # Set internal representation to fetch the gems/specs from remote.
+ #
+ # When this is called, the source should try to fetch the specs and
+ # install from remote path.
+ def remote!
+ end
+
+ # Set internal representation to fetch the gems/specs from app cache.
+ #
+ # When this is called, the source should try to fetch the specs and
+ # install from the path provided by `app_cache_path`.
+ def cached!
+ end
+
+ # This is called to update the spec and installation.
+ #
+ # If the source plugin is loaded from lockfile or otherwise, it shall
+ # refresh the cache/specs (e.g. git sources can make a fresh clone).
+ def unlock!
+ end
+
+ # Name of directory where plugin the is expected to cache the gems when
+ # #cache is called.
+ #
+ # Also this name is matched against the directories in cache for pruning
+ #
+ # This is used by `app_cache_path`
+ def app_cache_dirname
+ base_name = File.basename(URI.parse(uri).normalize.path)
+ "#{base_name}-#{uri_hash}"
+ end
+
+ # This method is called while caching to save copy of the gems that the
+ # source can resolve to path provided by `app_cache_app`so that they can
+ # be reinstalled from the cache without querying the remote (i.e. an
+ # alternative to remote)
+ #
+ # This is stored with the app and source plugins should try to provide
+ # specs and install only from this cache when `cached!` is called.
+ #
+ # This cache is different from the internal caching that can be done
+ # at sub paths of `cache_path` (from API). This can be though as caching
+ # by bundler.
+ def cache(spec, custom_path = nil)
+ new_cache_path = app_cache_path(custom_path)
+
+ FileUtils.rm_rf(new_cache_path)
+ FileUtils.cp_r(install_path, new_cache_path)
+ FileUtils.touch(app_cache_path.join(".bundlecache"))
+ end
+
+ # This shall check if two source object represent the same source.
+ #
+ # The comparison shall take place only on the attribute that can be
+ # inferred from the options passed from Gemfile and not on attibutes
+ # that are used to pin down the gem to specific version (e.g. Git
+ # sources should compare on branch and tag but not on commit hash)
+ #
+ # The sources objects are constructed from Gemfile as well as from
+ # lockfile. To converge the sources, it is necessary that they match.
+ #
+ # The same applies for `eql?` and `hash`
+ def ==(other)
+ other.is_a?(self.class) && uri == other.uri
+ end
+
+ # When overriding `eql?` please preserve the behaviour as mentioned in
+ # docstring for `==` method.
+ alias_method :eql?, :==
+
+ # When overriding `hash` please preserve the behaviour as mentioned in
+ # docstring for `==` method, i.e. two methods equal by above comparison
+ # should have same hash.
+ def hash
+ [self.class, uri].hash
+ end
+
+ # A helper method, not necessary if not used internally.
+ def installed?
+ File.directory?(install_path)
+ end
+
+ # The full path where the plugin should cache the gem so that it can be
+ # installed latter.
+ #
+ # Note: Do not override if you don't know what you are doing.
+ def app_cache_path(custom_path = nil)
+ @app_cache_path ||= Bundler.app_cache(custom_path).join(app_cache_dirname)
+ end
+
+ # Used by definition.
+ #
+ # Note: Do not override if you don't know what you are doing.
+ def unmet_deps
+ specs.unmet_dependency_names
+ end
+
+ # Note: Do not override if you don't know what you are doing.
+ def can_lock?(spec)
+ spec.source == self
+ end
+
+ # Generates the content to be entered into the lockfile.
+ # Saves type and remote and also calls to `options_to_lock`.
+ #
+ # Plugin should use `options_to_lock` to save information in lockfile
+ # and not override this.
+ #
+ # Note: Do not override if you don't know what you are doing.
+ def to_lock
+ out = String.new("#{LockfileParser::PLUGIN}\n")
+ out << " remote: #{@uri}\n"
+ out << " type: #{@type}\n"
+ options_to_lock.each do |opt, value|
+ out << " #{opt}: #{value}\n"
+ end
+ out << " specs:\n"
+ end
+
+ def to_s
+ "plugin source for #{options[:type]} with uri #{uri}"
+ end
+
+ # Note: Do not override if you don't know what you are doing.
+ def include?(other)
+ other == self
+ end
+
+ def uri_hash
+ SharedHelpers.digest(:SHA1).hexdigest(uri)
+ end
+
+ # Note: Do not override if you don't know what you are doing.
+ def gem_install_dir
+ Bundler.install_path
+ end
+
+ # It is used to obtain the full_gem_path.
+ #
+ # spec's loaded_from path is expanded against this to get full_gem_path
+ #
+ # Note: Do not override if you don't know what you are doing.
+ def root
+ Bundler.root
+ end
+
+ # @private
+ # Returns true
+ def bundler_plugin_api_source?
+ true
+ end
+
+ # @private
+ # This API on source might not be stable, and for now we expect plugins
+ # to download all specs in `#specs`, so we implement the method for
+ # compatibility purposes and leave it undocumented (and don't support)
+ # overriding it)
+ def double_check_for(*); end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/plugin/dsl.rb b/lib/bundler/plugin/dsl.rb
new file mode 100644
index 0000000000..4bfc8437e0
--- /dev/null
+++ b/lib/bundler/plugin/dsl.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module Bundler
+ module Plugin
+ # Dsl to parse the Gemfile looking for plugins to install
+ class DSL < Bundler::Dsl
+ class PluginGemfileError < PluginError; end
+ alias_method :_gem, :gem # To use for plugin installation as gem
+
+ # So that we don't have to override all there methods to dummy ones
+ # explicitly.
+ # They will be handled by method_missing
+ [:gemspec, :gem, :path, :install_if, :platforms, :env].each {|m| undef_method m }
+
+ # This lists the plugins that was added automatically and not specified by
+ # the user.
+ #
+ # When we encounter :type attribute with a source block, we add a plugin
+ # by name bundler-source-<type> to list of plugins to be installed.
+ #
+ # These plugins are optional and are not installed when there is conflict
+ # with any other plugin.
+ attr_reader :inferred_plugins
+
+ def initialize
+ super
+ @sources = Plugin::SourceList.new
+ @inferred_plugins = [] # The source plugins inferred from :type
+ end
+
+ def plugin(name, *args)
+ _gem(name, *args)
+ end
+
+ def method_missing(name, *args)
+ raise PluginGemfileError, "Undefined local variable or method `#{name}' for Gemfile" unless Bundler::Dsl.method_defined? name
+ end
+
+ def source(source, *args, &blk)
+ options = args.last.is_a?(Hash) ? args.pop.dup : {}
+ options = normalize_hash(options)
+ return super unless options.key?("type")
+
+ plugin_name = "bundler-source-#{options["type"]}"
+
+ return if @dependencies.any? {|d| d.name == plugin_name }
+
+ plugin(plugin_name)
+ @inferred_plugins << plugin_name
+ end
+ end
+ end
+end
diff --git a/lib/bundler/plugin/events.rb b/lib/bundler/plugin/events.rb
new file mode 100644
index 0000000000..bc037d1af5
--- /dev/null
+++ b/lib/bundler/plugin/events.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+module Bundler
+ module Plugin
+ module Events
+ def self.define(const, event)
+ const = const.to_sym.freeze
+ if const_defined?(const) && const_get(const) != event
+ raise ArgumentError, "Attempting to reassign #{const} to a different value"
+ end
+ const_set(const, event) unless const_defined?(const)
+ @events ||= {}
+ @events[event] = const
+ end
+ private_class_method :define
+
+ def self.reset
+ @events.each_value do |const|
+ remove_const(const)
+ end
+ @events = nil
+ end
+ private_class_method :reset
+
+ # Check if an event has been defined
+ # @param event [String] An event to check
+ # @return [Boolean] A boolean indicating if the event has been defined
+ def self.defined_event?(event)
+ @events ||= {}
+ @events.key?(event)
+ end
+
+ # @!parse
+ # A hook called before each individual gem is installed
+ # Includes a Bundler::ParallelInstaller::SpecInstallation.
+ # No state, error, post_install_message will be present as nothing has installed yet
+ # GEM_BEFORE_INSTALL = "before-install"
+ define :GEM_BEFORE_INSTALL, "before-install"
+
+ # @!parse
+ # A hook called after each individual gem is installed
+ # Includes a Bundler::ParallelInstaller::SpecInstallation.
+ # - If state is failed, an error will be present.
+ # - If state is success, a post_install_message may be present.
+ # GEM_AFTER_INSTALL = "after-install"
+ define :GEM_AFTER_INSTALL, "after-install"
+
+ # @!parse
+ # A hook called before any gems install
+ # Includes an Array of Bundler::Dependency objects
+ # GEM_BEFORE_INSTALL_ALL = "before-install-all"
+ define :GEM_BEFORE_INSTALL_ALL, "before-install-all"
+
+ # @!parse
+ # A hook called after any gems install
+ # Includes an Array of Bundler::Dependency objects
+ # GEM_AFTER_INSTALL_ALL = "after-install-all"
+ define :GEM_AFTER_INSTALL_ALL, "after-install-all"
+ end
+ end
+end
diff --git a/lib/bundler/plugin/index.rb b/lib/bundler/plugin/index.rb
new file mode 100644
index 0000000000..f09587dfda
--- /dev/null
+++ b/lib/bundler/plugin/index.rb
@@ -0,0 +1,162 @@
+# frozen_string_literal: true
+
+module Bundler
+ # Manages which plugins are installed and their sources. This also is supposed to map
+ # which plugin does what (currently the features are not implemented so this class is
+ # now a stub class).
+ module Plugin
+ class Index
+ class CommandConflict < PluginError
+ def initialize(plugin, commands)
+ msg = "Command(s) `#{commands.join("`, `")}` declared by #{plugin} are already registered."
+ super msg
+ end
+ end
+
+ class SourceConflict < PluginError
+ def initialize(plugin, sources)
+ msg = "Source(s) `#{sources.join("`, `")}` declared by #{plugin} are already registered."
+ super msg
+ end
+ end
+
+ attr_reader :commands
+
+ def initialize
+ @plugin_paths = {}
+ @commands = {}
+ @sources = {}
+ @hooks = {}
+ @load_paths = {}
+
+ begin
+ load_index(global_index_file, true)
+ rescue GenericSystemCallError
+ # no need to fail when on a read-only FS, for example
+ nil
+ end
+ load_index(local_index_file) if SharedHelpers.in_bundle?
+ end
+
+ # This function is to be called when a new plugin is installed. This
+ # function shall add the functions of the plugin to existing maps and also
+ # the name to source location.
+ #
+ # @param [String] name of the plugin to be registered
+ # @param [String] path where the plugin is installed
+ # @param [Array<String>] load_paths for the plugin
+ # @param [Array<String>] commands that are handled by the plugin
+ # @param [Array<String>] sources that are handled by the plugin
+ def register_plugin(name, path, load_paths, commands, sources, hooks)
+ old_commands = @commands.dup
+
+ common = commands & @commands.keys
+ raise CommandConflict.new(name, common) unless common.empty?
+ commands.each {|c| @commands[c] = name }
+
+ common = sources & @sources.keys
+ raise SourceConflict.new(name, common) unless common.empty?
+ sources.each {|k| @sources[k] = name }
+
+ hooks.each {|e| (@hooks[e] ||= []) << name }
+
+ @plugin_paths[name] = path
+ @load_paths[name] = load_paths
+ save_index
+ rescue StandardError
+ @commands = old_commands
+ raise
+ end
+
+ # Path of default index file
+ def index_file
+ Plugin.root.join("index")
+ end
+
+ # Path where the global index file is stored
+ def global_index_file
+ Plugin.global_root.join("index")
+ end
+
+ # Path where the local index file is stored
+ def local_index_file
+ Plugin.local_root.join("index")
+ end
+
+ def plugin_path(name)
+ Pathname.new @plugin_paths[name]
+ end
+
+ def load_paths(name)
+ @load_paths[name]
+ end
+
+ # Fetch the name of plugin handling the command
+ def command_plugin(command)
+ @commands[command]
+ end
+
+ def installed?(name)
+ @plugin_paths[name]
+ end
+
+ def source?(source)
+ @sources.key? source
+ end
+
+ def source_plugin(name)
+ @sources[name]
+ end
+
+ # Returns the list of plugin names handling the passed event
+ def hook_plugins(event)
+ @hooks[event] || []
+ end
+
+ private
+
+ # Reads the index file from the directory and initializes the instance
+ # variables.
+ #
+ # It skips the sources if the second param is true
+ # @param [Pathname] index file path
+ # @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?
+ break unless valid_file
+
+ data = index_f.read
+
+ require "bundler/yaml_serializer"
+ index = YAMLSerializer.load(data)
+
+ @commands.merge!(index["commands"])
+ @hooks.merge!(index["hooks"])
+ @load_paths.merge!(index["load_paths"])
+ @plugin_paths.merge!(index["plugin_paths"])
+ @sources.merge!(index["sources"]) unless global
+ end
+ end
+
+ # Should be called when any of the instance variables change. Stores the
+ # instance variables in YAML format. (The instance variables are supposed
+ # to be only String key value pairs)
+ def save_index
+ index = {
+ "commands" => @commands,
+ "hooks" => @hooks,
+ "load_paths" => @load_paths,
+ "plugin_paths" => @plugin_paths,
+ "sources" => @sources,
+ }
+
+ require "bundler/yaml_serializer"
+ SharedHelpers.filesystem_access(index_file) do |index_f|
+ FileUtils.mkdir_p(index_f.dirname)
+ File.open(index_f, "w") {|f| f.puts YAMLSerializer.dump(index) }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/plugin/installer.rb b/lib/bundler/plugin/installer.rb
new file mode 100644
index 0000000000..5379c38979
--- /dev/null
+++ b/lib/bundler/plugin/installer.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+module Bundler
+ # Handles the installation of plugin in appropriate directories.
+ #
+ # This class is supposed to be wrapper over the existing gem installation infra
+ # but currently it itself handles everything as the Source's subclasses (e.g. Source::RubyGems)
+ # are heavily dependent on the Gemfile.
+ module Plugin
+ class Installer
+ autoload :Rubygems, "bundler/plugin/installer/rubygems"
+ autoload :Git, "bundler/plugin/installer/git"
+
+ def install(names, options)
+ version = options[:version] || [">= 0"]
+ Bundler.settings.temporary(:lockfile_uses_separate_rubygems_sources => false, :disable_multisource => false) do
+ if options[:git]
+ install_git(names, version, options)
+ else
+ sources = options[:source] || Bundler.rubygems.sources
+ install_rubygems(names, version, sources)
+ end
+ end
+ end
+
+ # Installs the plugin from Definition object created by limited parsing of
+ # Gemfile searching for plugins to be installed
+ #
+ # @param [Definition] definition object
+ # @return [Hash] map of names to their specs they are installed with
+ def install_definition(definition)
+ def definition.lock(*); end
+ definition.resolve_remotely!
+ specs = definition.specs
+
+ install_from_specs specs
+ end
+
+ private
+
+ def install_git(names, version, options)
+ uri = options.delete(:git)
+ options["uri"] = uri
+
+ source_list = SourceList.new
+ source_list.add_git_source(options)
+
+ # To support both sources
+ if options[:source]
+ source_list.add_rubygems_source("remotes" => options[:source])
+ end
+
+ deps = names.map {|name| Dependency.new name, version }
+
+ definition = Definition.new(nil, deps, source_list, true)
+ install_definition(definition)
+ end
+
+ # Installs the plugin from rubygems source and returns the path where the
+ # plugin was installed
+ #
+ # @param [String] name of the plugin gem to search in the source
+ # @param [Array] version of the gem to install
+ # @param [String, Array<String>] source(s) to resolve the gem
+ #
+ # @return [Hash] map of names to the specs of plugins installed
+ def install_rubygems(names, version, sources)
+ deps = names.map {|name| Dependency.new name, version }
+
+ source_list = SourceList.new
+ source_list.add_rubygems_source("remotes" => sources)
+
+ definition = Definition.new(nil, deps, source_list, true)
+ install_definition(definition)
+ end
+
+ # Installs the plugins and deps from the provided specs and returns map of
+ # gems to their paths
+ #
+ # @param specs to install
+ #
+ # @return [Hash] map of names to the specs
+ def install_from_specs(specs)
+ paths = {}
+
+ specs.each do |spec|
+ spec.source.install spec
+
+ paths[spec.name] = spec
+ end
+
+ paths
+ end
+ end
+ end
+end
diff --git a/lib/bundler/plugin/installer/git.rb b/lib/bundler/plugin/installer/git.rb
new file mode 100644
index 0000000000..fbb6c5e40e
--- /dev/null
+++ b/lib/bundler/plugin/installer/git.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module Bundler
+ module Plugin
+ class Installer
+ class Git < Bundler::Source::Git
+ def cache_path
+ @cache_path ||= begin
+ git_scope = "#{base_name}-#{uri_hash}"
+
+ Plugin.cache.join("bundler", "git", git_scope)
+ end
+ end
+
+ def install_path
+ @install_path ||= begin
+ git_scope = "#{base_name}-#{shortref_for_path(revision)}"
+
+ Plugin.root.join("bundler", "gems", git_scope)
+ end
+ end
+
+ def version_message(spec)
+ "#{spec.name} #{spec.version}"
+ end
+
+ def root
+ Plugin.root
+ end
+
+ def generate_bin(spec, disable_extensions = false)
+ # Need to find a way without code duplication
+ # For now, we can ignore this
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/plugin/installer/rubygems.rb b/lib/bundler/plugin/installer/rubygems.rb
new file mode 100644
index 0000000000..7ae74fa93b
--- /dev/null
+++ b/lib/bundler/plugin/installer/rubygems.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Bundler
+ module Plugin
+ class Installer
+ class Rubygems < Bundler::Source::Rubygems
+ def version_message(spec)
+ "#{spec.name} #{spec.version}"
+ end
+
+ private
+
+ def requires_sudo?
+ false # Will change on implementation of project level plugins
+ end
+
+ def rubygems_dir
+ Plugin.root
+ end
+
+ def cache_path
+ Plugin.cache
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/plugin/source_list.rb b/lib/bundler/plugin/source_list.rb
new file mode 100644
index 0000000000..f0e212205f
--- /dev/null
+++ b/lib/bundler/plugin/source_list.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Bundler
+ # SourceList object to be used while parsing the Gemfile, setting the
+ # approptiate options to be used with Source classes for plugin installation
+ module Plugin
+ class SourceList < Bundler::SourceList
+ def add_git_source(options = {})
+ add_source_to_list Plugin::Installer::Git.new(options), git_sources
+ end
+
+ def add_rubygems_source(options = {})
+ add_source_to_list Plugin::Installer::Rubygems.new(options), @rubygems_sources
+ end
+
+ def all_sources
+ path_sources + git_sources + rubygems_sources + [metadata_source]
+ end
+
+ private
+
+ def rubygems_aggregate_class
+ Plugin::Installer::Rubygems
+ end
+ end
+ end
+end
diff --git a/lib/bundler/process_lock.rb b/lib/bundler/process_lock.rb
new file mode 100644
index 0000000000..cba4fcdba5
--- /dev/null
+++ b/lib/bundler/process_lock.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Bundler
+ class ProcessLock
+ def self.lock(bundle_path = Bundler.bundle_path)
+ lock_file_path = File.join(bundle_path, "bundler.lock")
+ has_lock = false
+
+ File.open(lock_file_path, "w") do |f|
+ f.flock(File::LOCK_EX)
+ has_lock = true
+ yield
+ f.flock(File::LOCK_UN)
+ end
+ rescue Errno::EACCES, Errno::ENOLCK, *[SharedHelpers.const_get_safely(:ENOTSUP, Errno)].compact
+ # In the case the user does not have access to
+ # create the lock file or is using NFS where
+ # locks are not available we skip locking.
+ yield
+ ensure
+ FileUtils.rm_f(lock_file_path) if has_lock
+ end
+ end
+end
diff --git a/lib/bundler/psyched_yaml.rb b/lib/bundler/psyched_yaml.rb
new file mode 100644
index 0000000000..e654416a5a
--- /dev/null
+++ b/lib/bundler/psyched_yaml.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+# Psych could be a gem, so try to ask for it
+begin
+ gem "psych"
+rescue LoadError
+end if defined?(gem)
+
+# Psych could be in the stdlib
+# but it's too late if Syck is already loaded
+begin
+ require "psych" unless defined?(Syck)
+rescue LoadError
+ # Apparently Psych wasn't available. Oh well.
+end
+
+# At least load the YAML stdlib, whatever that may be
+require "yaml" unless defined?(YAML.dump)
+
+module Bundler
+ # On encountering invalid YAML,
+ # Psych raises Psych::SyntaxError
+ if defined?(::Psych::SyntaxError)
+ YamlLibrarySyntaxError = ::Psych::SyntaxError
+ else # Syck raises ArgumentError
+ YamlLibrarySyntaxError = ::ArgumentError
+ end
+end
+
+require "bundler/deprecate"
+begin
+ Bundler::Deprecate.skip_during do
+ require "rubygems/safe_yaml"
+ end
+rescue LoadError
+ # it's OK if the file isn't there
+end
diff --git a/lib/bundler/remote_specification.rb b/lib/bundler/remote_specification.rb
new file mode 100644
index 0000000000..23e1234330
--- /dev/null
+++ b/lib/bundler/remote_specification.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+require "uri"
+
+module Bundler
+ # Represents a lazily loaded gem specification, where the full specification
+ # is on the source server in rubygems' "quick" index. The proxy object is to
+ # 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 MatchPlatform
+ include Comparable
+
+ attr_reader :name, :version, :platform
+ attr_writer :dependencies
+ attr_accessor :source, :remote
+
+ def initialize(name, version, platform, spec_fetcher)
+ @name = name
+ @version = Gem::Version.create version
+ @platform = platform
+ @spec_fetcher = spec_fetcher
+ @dependencies = nil
+ end
+
+ # Needed before installs, since the arch matters then and quick
+ # specs don't bother to include the arch in the platform string
+ def fetch_platform
+ @platform = _remote_specification.platform
+ end
+
+ def full_name
+ if platform == Gem::Platform::RUBY || platform.nil?
+ "#{@name}-#{@version}"
+ else
+ "#{@name}-#{@version}-#{platform}"
+ end
+ end
+
+ # Compare this specification against another object. Using sort_obj
+ # is compatible with Gem::Specification and other Bundler or RubyGems
+ # objects. Otherwise, use the default Object comparison.
+ def <=>(other)
+ if other.respond_to?(:sort_obj)
+ sort_obj <=> other.sort_obj
+ else
+ super
+ end
+ end
+
+ # Because Rubyforge cannot be trusted to provide valid specifications
+ # once the remote gem is downloaded, the backend specification will
+ # be swapped out.
+ def __swap__(spec)
+ SharedHelpers.ensure_same_dependencies(self, dependencies, spec.dependencies)
+ @_remote_specification = spec
+ end
+
+ # Create a delegate used for sorting. This strategy is copied from
+ # RubyGems 2.23 and ensures that Bundler's specifications can be
+ # compared and sorted with RubyGems' own specifications.
+ #
+ # @see #<=>
+ # @see Gem::Specification#sort_obj
+ #
+ # @return [Array] an object you can use to compare and sort this
+ # specification against other specifications
+ def sort_obj
+ [@name, @version, @platform == Gem::Platform::RUBY ? -1 : 1]
+ end
+
+ def to_s
+ "#<#{self.class} name=#{name} version=#{version} platform=#{platform}>"
+ end
+
+ def dependencies
+ @dependencies ||= begin
+ deps = method_missing(:dependencies)
+
+ # allow us to handle when the specs dependencies are an array of array of string
+ # see https://github.com/bundler/bundler/issues/5797
+ deps = deps.map {|d| d.is_a?(Gem::Dependency) ? d : Gem::Dependency.new(*d) }
+
+ deps
+ end
+ end
+
+ def git_version
+ return unless loaded_from && source.is_a?(Bundler::Source::Git)
+ " #{source.revision[0..6]}"
+ end
+
+ private
+
+ def to_ary
+ nil
+ end
+
+ def _remote_specification
+ @_remote_specification ||= @spec_fetcher.fetch_spec([@name, @version, @platform])
+ @_remote_specification || raise(GemspecError, "Gemspec data for #{full_name} was" \
+ " missing from the server! Try installing with `--full-index` as a workaround.")
+ end
+
+ def method_missing(method, *args, &blk)
+ _remote_specification.send(method, *args, &blk)
+ end
+
+ def respond_to?(method, include_all = false)
+ super || _remote_specification.respond_to?(method, include_all)
+ end
+ public :respond_to?
+ end
+end
diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb
new file mode 100644
index 0000000000..545b4cc88a
--- /dev/null
+++ b/lib/bundler/resolver.rb
@@ -0,0 +1,373 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Resolver
+ require "bundler/vendored_molinillo"
+ require "bundler/resolver/spec_group"
+
+ # 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, index, source_requirements = {}, base = [], gem_version_promoter = GemVersionPromoter.new, additional_base_requirements = [], platforms = nil)
+ platforms = Set.new(platforms) if platforms
+ base = SpecSet.new(base) unless base.is_a?(SpecSet)
+ resolver = new(index, source_requirements, base, gem_version_promoter, additional_base_requirements, platforms)
+ result = resolver.start(requirements)
+ SpecSet.new(result)
+ end
+
+ def initialize(index, source_requirements, base, gem_version_promoter, additional_base_requirements, platforms)
+ @index = index
+ @source_requirements = 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.new(dep, ls.platform), true)
+ end
+ additional_base_requirements.each {|d| @base_dg.add_vertex(d.name, d) }
+ @platforms = platforms
+ @gem_version_promoter = gem_version_promoter
+ @allow_bundler_dependency_conflicts = Bundler.feature_flag.allow_bundler_dependency_conflicts?
+ @lockfile_uses_separate_rubygems_sources = Bundler.feature_flag.lockfile_uses_separate_rubygems_sources?
+ @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
+
+ 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)
+ STDERR.puts debug_info.split("\n").map {|s| " " * depth + s }
+ end
+
+ def debug?
+ return @debug_mode if defined?(@debug_mode)
+ @debug_mode = ENV["DEBUG_RESOLVER"] || ENV["DEBUG_RESOLVER_TREE"] || false
+ end
+
+ def before_resolution
+ Bundler.ui.info "Resolving dependencies...", debug?
+ end
+
+ def after_resolution
+ Bundler.ui.info ""
+ end
+
+ def indicate_progress
+ Bundler.ui.info ".", false unless debug?
+ end
+
+ include Molinillo::SpecificationProvider
+
+ def dependencies_for(specification)
+ specification.dependencies_for_activated_platforms
+ end
+
+ def search_for(dependency)
+ platform = dependency.__platform
+ dependency = dependency.dep unless dependency.is_a? Gem::Dependency
+ search = @search_for[dependency] ||= begin
+ index = index_for(dependency)
+ results = index.search(dependency, @base[dependency.name])
+
+ if vertex = @base_dg.vertex_named(dependency.name)
+ locked_requirement = vertex.payload.requirement
+ end
+
+ if !@prerelease_specified[dependency.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
+ 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)
+ spec_group = SpecGroup.new(specs)
+ spec_group.ignores_bundler_dependencies = @allow_bundler_dependency_conflicts
+ groups << spec_group
+ 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)
+ end
+ end
+ search.select {|sg| sg.for?(platform) }.each {|sg| sg.activate_platform!(platform) }
+ end
+
+ def index_for(dependency)
+ source = @source_requirements[dependency.name]
+ if source
+ source.specs
+ elsif @lockfile_uses_separate_rubygems_sources
+ Index.build do |idx|
+ if dependency.all_sources
+ dependency.all_sources.each {|s| idx.add_source(s.specs) if s }
+ else
+ idx.add_source @source_requirements[:default].specs
+ end
+ end
+ else
+ @index
+ end
+ end
+
+ def name_for(dependency)
+ dependency.name
+ end
+
+ def name_for_explicit_dependency_source
+ Bundler.default_gemfile.basename.to_s
+ rescue
+ "Gemfile"
+ end
+
+ def name_for_locking_dependency_source
+ Bundler.default_lockfile.basename.to_s
+ rescue
+ "Gemfile.lock"
+ end
+
+ def requirement_satisfied_by?(requirement, activated, spec)
+ return false unless requirement.matches_spec?(spec) || spec.source.is_a?(Source::Gemspec)
+ spec.activate_platform!(requirement.__platform) if !@platforms || @platforms.include?(requirement.__platform)
+ true
+ end
+
+ def relevant_sources_for_vertex(vertex)
+ if vertex.root?
+ [@source_requirements[vertex.name]]
+ elsif @lockfile_uses_separate_rubygems_sources
+ vertex.recursive_predecessors.map do |v|
+ @source_requirements[v.name]
+ end << @source_requirements[:default]
+ end
+ end
+
+ def sort_dependencies(dependencies, activated, conflicts)
+ dependencies.sort_by do |dependency|
+ dependency.all_sources = relevant_sources_for_vertex(activated.vertex_named(dependency.name))
+ 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),
+ ]
+ end
+ end
+
+ # Sort platforms from most general to most specific
+ def self.sort_platforms(platforms)
+ platforms.sort_by do |platform|
+ platform_sort_key(platform)
+ end
+ end
+
+ def self.platform_sort_key(platform)
+ return ["", "", ""] if Gem::Platform::RUBY == platform
+ platform.to_a.map {|part| part || "" }
+ end
+
+ private
+
+ # 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] ||= begin
+ 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
+
+ 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
+ end
+ end
+ end
+ end
+
+ def verify_gemfile_dependencies_are_found!(requirements)
+ requirements.each do |requirement|
+ name = requirement.name
+ next if name == "bundler"
+ next unless search_for(requirement).empty?
+
+ cache_message = begin
+ " or in gems cached in #{Bundler.settings.app_cache_path}" if Bundler.app_cache.exist?
+ rescue GemfileNotFound
+ nil
+ end
+
+ 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`"
+ elsif source = @source_requirements[name]
+ specs = source.specs[name]
+ versions_with_platforms = specs.map {|s| [s.version, s.platform] }
+ message = String.new("Could not find gem '#{SharedHelpers.pretty_dependency(requirement)}' in #{source}#{cache_message}.\n")
+ message << if versions_with_platforms.any?
+ "The source contains '#{name}' at: #{formatted_versions_with_platforms(versions_with_platforms)}"
+ else
+ "The source does not contain any versions of '#{name}'"
+ end
+ else
+ message = "Could not find gem '#{requirement}' in any of the gem sources " \
+ "listed in your Gemfile#{cache_message}."
+ end
+ raise GemNotFound, message
+ end
+ end
+
+ def formatted_versions_with_platforms(versions_with_platforms)
+ version_platform_strs = versions_with_platforms.map do |vwp|
+ version = vwp.first
+ platform = vwp.last
+ version_platform_str = String.new(version.to_s)
+ version_platform_str << " #{platform}" unless platform.nil? || platform == Gem::Platform::RUBY
+ version_platform_str
+ end
+ version_platform_strs.join(", ")
+ end
+
+ def version_conflict_message(e)
+ e.message_with_trees(
+ :solver_name => "Bundler",
+ :possibility_type => "gem",
+ :reduce_trees => lambda do |trees|
+ # 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] } }
+
+ # bail out if tree size is too big for Array#combination to make any sense
+ return trees 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)
+ trees.reject! {|t| !maximal.include?(t.last) } if maximal
+
+ trees = trees.sort_by {|t| t.flatten.map(&:to_s) }
+ trees.uniq! {|t| t.flatten.map {|dep| [dep.name, dep.requirement] } }
+
+ trees.sort_by {|t| t.reverse.map(&:name) }
+ end,
+ :printable_requirement => lambda {|req| SharedHelpers.pretty_dependency(req) },
+ :additional_message_for_conflict => lambda do |o, name, conflict|
+ if name == "bundler"
+ o << %(\n Current Bundler version:\n bundler (#{Bundler::VERSION}))
+ other_bundler_required = !conflict.requirement.requirement.satisfied_by?(Gem::Version.new Bundler::VERSION)
+ end
+
+ if name == "bundler" && other_bundler_required
+ o << "\n"
+ o << "This Gemfile requires a different version of Bundler.\n"
+ o << "Perhaps you need to update Bundler by running `gem install bundler`?\n"
+ end
+ if conflict.locked_requirement
+ o << "\n"
+ o << %(Running `bundle update` 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_sources = if conflict.requirement.source
+ [conflict.requirement.source]
+ elsif conflict.requirement.all_sources
+ conflict.requirement.all_sources
+ elsif @lockfile_uses_separate_rubygems_sources
+ # every conflict should have an explicit group of sources when we
+ # enforce strict pinning
+ raise "no source set for #{conflict}"
+ else
+ []
+ end.compact.map(&:to_s).uniq.sort
+
+ o << "Could not find gem '#{SharedHelpers.pretty_dependency(conflict.requirement)}'"
+ if conflict.requirement_trees.first.size > 1
+ o << ", which is required by "
+ o << "gem '#{SharedHelpers.pretty_dependency(conflict.requirement_trees.first[-2])}',"
+ end
+ o << " "
+
+ o << if relevant_sources.empty?
+ "in any of the sources.\n"
+ else
+ "in any of the relevant sources:\n #{relevant_sources * "\n "}\n"
+ end
+ end
+ end,
+ :version_for_spec => lambda {|spec| spec.version }
+ )
+ end
+ end
+end
diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb
new file mode 100644
index 0000000000..34d043aed7
--- /dev/null
+++ b/lib/bundler/resolver/spec_group.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Resolver
+ class SpecGroup
+ include GemHelpers
+
+ attr_accessor :name, :version, :source
+ attr_accessor :ignores_bundler_dependencies
+
+ def initialize(all_specs)
+ raise ArgumentError, "cannot initialize with an empty value" unless exemplary_spec = all_specs.first
+ @name = exemplary_spec.name
+ @version = exemplary_spec.version
+ @source = exemplary_spec.source
+
+ @activated_platforms = []
+ @dependencies = nil
+ @specs = Hash.new do |specs, platform|
+ specs[platform] = select_best_platform_match(all_specs, platform)
+ end
+ @ignores_bundler_dependencies = true
+ end
+
+ def to_specs
+ @activated_platforms.map do |p|
+ next unless s = @specs[p]
+ lazy_spec = LazySpecification.new(name, version, s.platform, source)
+ lazy_spec.dependencies.replace s.dependencies
+ lazy_spec
+ end.compact
+ end
+
+ def activate_platform!(platform)
+ return unless for?(platform)
+ return if @activated_platforms.include?(platform)
+ @activated_platforms << platform
+ end
+
+ def for?(platform)
+ spec = @specs[platform]
+ !spec.nil?
+ end
+
+ def to_s
+ @to_s ||= "#{name} (#{version})"
+ end
+
+ def dependencies_for_activated_platforms
+ dependencies = @activated_platforms.map {|p| __dependencies[p] }
+ metadata_dependencies = @activated_platforms.map do |platform|
+ metadata_dependencies(@specs[platform], platform)
+ end
+ dependencies.concat(metadata_dependencies).flatten
+ end
+
+ def ==(other)
+ return unless other.is_a?(SpecGroup)
+ name == other.name &&
+ version == other.version &&
+ source == other.source
+ end
+
+ def eql?(other)
+ name.eql?(other.name) &&
+ version.eql?(other.version) &&
+ source.eql?(other.source)
+ end
+
+ def hash
+ to_s.hash ^ source.hash
+ end
+
+ private
+
+ def __dependencies
+ @dependencies = Hash.new do |dependencies, platform|
+ dependencies[platform] = []
+ if spec = @specs[platform]
+ spec.dependencies.each do |dep|
+ next if dep.type == :development
+ next if @ignores_bundler_dependencies && dep.name == "bundler".freeze
+ dependencies[platform] << DepProxy.new(dep, platform)
+ end
+ end
+ dependencies[platform]
+ end
+ end
+
+ def metadata_dependencies(spec, platform)
+ return [] unless spec
+ # Only allow endpoint specifications since they won't hit the network to
+ # fetch the full gemspec when calling required_ruby_version
+ return [] if !spec.is_a?(EndpointSpecification) && !spec.is_a?(Gem::Specification)
+ dependencies = []
+ if !spec.required_ruby_version.nil? && !spec.required_ruby_version.none?
+ dependencies << DepProxy.new(Gem::Dependency.new("ruby\0", spec.required_ruby_version), platform)
+ end
+ if !spec.required_rubygems_version.nil? && !spec.required_rubygems_version.none?
+ dependencies << DepProxy.new(Gem::Dependency.new("rubygems\0", spec.required_rubygems_version), platform)
+ end
+ dependencies
+ end
+ end
+ end
+end
diff --git a/lib/bundler/retry.rb b/lib/bundler/retry.rb
new file mode 100644
index 0000000000..244606dcc9
--- /dev/null
+++ b/lib/bundler/retry.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+module Bundler
+ # General purpose class for retrying code that may fail
+ class Retry
+ attr_accessor :name, :total_runs, :current_run
+
+ class << self
+ def default_attempts
+ default_retries + 1
+ end
+ alias_method :attempts, :default_attempts
+
+ def default_retries
+ Bundler.settings[:retry]
+ end
+ end
+
+ def initialize(name, exceptions = nil, retries = self.class.default_retries)
+ @name = name
+ @retries = retries
+ @exceptions = Array(exceptions) || []
+ @total_runs = @retries + 1 # will run once, then upto attempts.times
+ end
+
+ def attempt(&block)
+ @current_run = 0
+ @failed = false
+ @error = nil
+ run(&block) while keep_trying?
+ @result
+ end
+ alias_method :attempts, :attempt
+
+ private
+
+ def run(&block)
+ @failed = false
+ @current_run += 1
+ @result = block.call
+ rescue => e
+ fail_attempt(e)
+ end
+
+ def fail_attempt(e)
+ @failed = true
+ if last_attempt? || @exceptions.any? {|k| e.is_a?(k) }
+ Bundler.ui.info "" unless Bundler.ui.debug?
+ raise e
+ end
+ return true unless name
+ Bundler.ui.info "" unless Bundler.ui.debug? # Add new line incase dots preceded this
+ Bundler.ui.warn "Retrying #{name} due to error (#{current_run.next}/#{total_runs}): #{e.class} #{e.message}", Bundler.ui.debug?
+ end
+
+ def keep_trying?
+ return true if current_run.zero?
+ return false if last_attempt?
+ return true if @failed
+ end
+
+ def last_attempt?
+ current_run >= total_runs
+ end
+ end
+end
diff --git a/lib/bundler/ruby_dsl.rb b/lib/bundler/ruby_dsl.rb
new file mode 100644
index 0000000000..f6ba220cd5
--- /dev/null
+++ b/lib/bundler/ruby_dsl.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Bundler
+ module RubyDsl
+ 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[:engine] == "ruby" && 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])
+ end
+ end
+end
diff --git a/lib/bundler/ruby_version.rb b/lib/bundler/ruby_version.rb
new file mode 100644
index 0000000000..e6c31a94c9
--- /dev/null
+++ b/lib/bundler/ruby_version.rb
@@ -0,0 +1,152 @@
+# frozen_string_literal: true
+
+module Bundler
+ class RubyVersion
+ attr_reader :versions,
+ :patchlevel,
+ :engine,
+ :engine_versions,
+ :gem_version,
+ :engine_gem_version
+
+ def initialize(versions, patchlevel, engine, engine_version)
+ # The parameters to this method must satisfy the
+ # following constraints, which are verified in
+ # the DSL:
+ #
+ # * If an engine is specified, an engine version
+ # must also be specified
+ # * If an engine version is specified, an engine
+ # must also be specified
+ # * If the engine is "ruby", the engine version
+ # must not be specified, or the engine version
+ # specified must match the version.
+
+ @versions = Array(versions).map do |v|
+ op, v = Gem::Requirement.parse(v)
+ op == "=" ? v.to_s : "#{op} #{v}"
+ end
+
+ @gem_version = Gem::Requirement.create(@versions.first).requirements.first.last
+ @input_engine = engine && engine.to_s
+ @engine = 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
+ end
+
+ def to_s(versions = self.versions)
+ output = String.new("ruby #{versions_string(versions)}")
+ output << "p#{patchlevel}" if patchlevel
+ output << " (#{engine} #{versions_string(engine_versions)})" unless engine == "ruby"
+
+ output
+ end
+
+ # @private
+ PATTERN = /
+ ruby\s
+ ([\d.]+) # ruby version
+ (?:p(-?\d+))? # optional patchlevel
+ (?:\s\((\S+)\s(.+)\))? # optional engine info
+ /xo
+
+ # Returns a RubyVersion from the given string.
+ # @param [String] the version string to match.
+ # @return [RubyVersion,Nil] The version if the string is a valid RubyVersion
+ # description, and nil otherwise.
+ def self.from_string(string)
+ new($1, $2, $3, $4) if string =~ PATTERN
+ end
+
+ def single_version_string
+ to_s(gem_version)
+ end
+
+ def ==(other)
+ versions == other.versions &&
+ engine == other.engine &&
+ engine_versions == other.engine_versions &&
+ patchlevel == other.patchlevel
+ end
+
+ def host
+ @host ||= [
+ RbConfig::CONFIG["host_cpu"],
+ RbConfig::CONFIG["host_vendor"],
+ RbConfig::CONFIG["host_os"]
+ ].join("-")
+ end
+
+ # Returns a tuple of these things:
+ # [diff, this, other]
+ # The priority of attributes are
+ # 1. engine
+ # 2. ruby_version
+ # 3. engine_version
+ def diff(other)
+ raise ArgumentError, "Can only diff with a RubyVersion, not a #{other.class}" unless other.is_a?(RubyVersion)
+ if engine != other.engine && @input_engine
+ [:engine, engine, other.engine]
+ elsif versions.empty? || !matches?(versions, other.gem_version)
+ [:version, versions_string(versions), versions_string(other.versions)]
+ elsif @input_engine && !matches?(engine_versions, other.engine_gem_version)
+ [:engine_version, versions_string(engine_versions), versions_string(other.engine_versions)]
+ elsif patchlevel && (!patchlevel.is_a?(String) || !other.patchlevel.is_a?(String) || !matches?(patchlevel, other.patchlevel))
+ [:patchlevel, patchlevel, other.patchlevel]
+ end
+ end
+
+ def versions_string(versions)
+ Array(versions).join(", ")
+ end
+
+ def self.system
+ ruby_engine = if defined?(RUBY_ENGINE) && !RUBY_ENGINE.nil?
+ RUBY_ENGINE.dup
+ else
+ # not defined in ruby 1.8.7
+ "ruby"
+ end
+ # :sob: mocking RUBY_VERSION breaks stuff on 1.8.7
+ ruby_version = ENV.fetch("BUNDLER_SPEC_RUBY_VERSION") { RUBY_VERSION }.dup
+ ruby_engine_version = case ruby_engine
+ when "ruby"
+ ruby_version
+ when "rbx"
+ Rubinius::VERSION.dup
+ when "jruby"
+ JRUBY_VERSION.dup
+ else
+ RUBY_ENGINE_VERSION.dup
+ end
+ patchlevel = RUBY_PATCHLEVEL.to_s
+
+ @ruby_version ||= RubyVersion.new(ruby_version, patchlevel, ruby_engine, ruby_engine_version)
+ end
+
+ def to_gem_version_with_patchlevel
+ @gem_version_with_patch ||= begin
+ Gem::Version.create("#{@gem_version}.#{@patchlevel}")
+ rescue ArgumentError
+ @gem_version
+ end
+ end
+
+ def exact?
+ return @exact if defined?(@exact)
+ @exact = versions.all? {|v| Gem::Requirement.create(v).exact? }
+ end
+
+ private
+
+ def matches?(requirements, version)
+ # Handles RUBY_PATCHLEVEL of -1 for instances like ruby-head
+ return requirements == version if requirements.to_s == "-1" || version.to_s == "-1"
+
+ Array(requirements).all? do |requirement|
+ Gem::Requirement.create(requirement).satisfied_by?(Gem::Version.create(version))
+ end
+ end
+ end
+end
diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb
new file mode 100644
index 0000000000..e9f0eac355
--- /dev/null
+++ b/lib/bundler/rubygems_ext.rb
@@ -0,0 +1,210 @@
+# frozen_string_literal: true
+
+require "pathname"
+
+if defined?(Gem::QuickLoader)
+ # Gem Prelude makes me a sad panda :'(
+ Gem::QuickLoader.load_full_rubygems_library
+end
+
+require "rubygems"
+require "rubygems/specification"
+
+begin
+ # Possible use in Gem::Specification#source below and require
+ # shouldn't be deferred.
+ require "rubygems/source"
+rescue LoadError
+ # Not available before RubyGems 2.0.0, ignore
+ nil
+end
+
+require "bundler/match_platform"
+
+module Gem
+ @loaded_stacks = Hash.new {|h, k| h[k] = [] }
+
+ class Specification
+ attr_accessor :remote, :location, :relative_loaded_from
+
+ if instance_methods(false).map(&:to_sym).include?(:source)
+ remove_method :source
+ attr_writer :source
+ def source
+ (defined?(@source) && @source) || Gem::Source::Installed.new
+ end
+ else
+ attr_accessor :source
+ end
+
+ alias_method :rg_full_gem_path, :full_gem_path
+ alias_method :rg_loaded_from, :loaded_from
+
+ attr_writer :full_gem_path unless instance_methods.include?(:full_gem_path=)
+
+ def full_gem_path
+ # this cannot check source.is_a?(Bundler::Plugin::API::Source)
+ # because that _could_ trip the autoload, and if there are unresolved
+ # gems at that time, this method could be called inside another require,
+ # thus raising with that constant being undefined. Better to check a method
+ if source.respond_to?(:path) || (source.respond_to?(:bundler_plugin_api_source?) && source.bundler_plugin_api_source?)
+ Pathname.new(loaded_from).dirname.expand_path(source.root).to_s.untaint
+ else
+ rg_full_gem_path
+ end
+ end
+
+ def loaded_from
+ if relative_loaded_from
+ source.path.join(relative_loaded_from).to_s
+ else
+ rg_loaded_from
+ end
+ end
+
+ def load_paths
+ return full_require_paths if respond_to?(:full_require_paths)
+
+ require_paths.map do |require_path|
+ if require_path.include?(full_gem_path)
+ require_path
+ else
+ File.join(full_gem_path, require_path)
+ end
+ end
+ end
+
+ if method_defined?(:extension_dir)
+ alias_method :rg_extension_dir, :extension_dir
+ def extension_dir
+ @bundler_extension_dir ||= if source.respond_to?(:extension_dir_name)
+ File.expand_path(File.join(extensions_dir, source.extension_dir_name))
+ else
+ rg_extension_dir
+ end
+ end
+ end
+
+ # RubyGems 1.8+ used only.
+ methods = instance_methods(false)
+ gem_dir = methods.first.is_a?(String) ? "gem_dir" : :gem_dir
+ remove_method :gem_dir if methods.include?(gem_dir)
+ def gem_dir
+ full_gem_path
+ end
+
+ def groups
+ @groups ||= []
+ end
+
+ def git_version
+ return unless loaded_from && source.is_a?(Bundler::Source::Git)
+ " #{source.revision[0..6]}"
+ end
+
+ def to_gemfile(path = nil)
+ gemfile = String.new("source 'https://rubygems.org'\n")
+ gemfile << dependencies_to_gemfile(nondevelopment_dependencies)
+ unless development_dependencies.empty?
+ gemfile << "\n"
+ gemfile << dependencies_to_gemfile(development_dependencies, :development)
+ end
+ gemfile
+ end
+
+ def nondevelopment_dependencies
+ dependencies - development_dependencies
+ end
+
+ private
+
+ def dependencies_to_gemfile(dependencies, group = nil)
+ gemfile = String.new
+ if dependencies.any?
+ gemfile << "group :#{group} do\n" if group
+ dependencies.each do |dependency|
+ gemfile << " " if group
+ gemfile << %(gem "#{dependency.name}")
+ req = dependency.requirements_list.first
+ gemfile << %(, "#{req}") if req
+ gemfile << "\n"
+ end
+ gemfile << "end\n" if group
+ end
+ gemfile
+ end
+ end
+
+ class Dependency
+ attr_accessor :source, :groups, :all_sources
+
+ alias_method :eql?, :==
+
+ def encode_with(coder)
+ to_yaml_properties.each do |ivar|
+ coder[ivar.to_s.sub(/^@/, "")] = instance_variable_get(ivar)
+ end
+ end
+
+ def to_yaml_properties
+ instance_variables.reject {|p| ["@source", "@groups", "@all_sources"].include?(p.to_s) }
+ end
+
+ def to_lock
+ out = String.new(" #{name}")
+ unless requirement.none?
+ reqs = requirement.requirements.map {|o, v| "#{o} #{v}" }.sort.reverse
+ out << " (#{reqs.join(", ")})"
+ end
+ out
+ end
+
+ # Backport of performance enhancement added to RubyGems 1.4
+ def matches_spec?(spec)
+ # name can be a Regexp, so use ===
+ return false unless name === spec.name
+ return true if requirement.none?
+
+ requirement.satisfied_by?(spec.version)
+ end unless allocate.respond_to?(:matches_spec?)
+ end
+
+ class Requirement
+ # Backport of performance enhancement added to RubyGems 1.4
+ def none?
+ # note that it might be tempting to replace with with RubyGems 2.0's
+ # improved implementation. Don't. It requires `DefaultRequirement` to be
+ # defined, and more importantantly, these overrides are not used when the
+ # running RubyGems defines these methods
+ to_s == ">= 0"
+ end unless allocate.respond_to?(:none?)
+
+ # Backport of performance enhancement added to RubyGems 2.2
+ def exact?
+ return false unless @requirements.size == 1
+ @requirements[0][0] == "="
+ end unless allocate.respond_to?(:exact?)
+ end
+
+ class Platform
+ JAVA = Gem::Platform.new("java") unless defined?(JAVA)
+ MSWIN = Gem::Platform.new("mswin32") unless defined?(MSWIN)
+ MSWIN64 = Gem::Platform.new("mswin64") unless defined?(MSWIN64)
+ MINGW = Gem::Platform.new("x86-mingw32") unless defined?(MINGW)
+ X64_MINGW = Gem::Platform.new("x64-mingw32") unless defined?(X64_MINGW)
+
+ undef_method :hash if method_defined? :hash
+ def hash
+ @cpu.hash ^ @os.hash ^ @version.hash
+ end
+
+ undef_method :eql? if method_defined? :eql?
+ alias_method :eql?, :==
+ end
+end
+
+module Gem
+ class Specification
+ include ::Bundler::MatchPlatform
+ end
+end
diff --git a/lib/bundler/rubygems_gem_installer.rb b/lib/bundler/rubygems_gem_installer.rb
new file mode 100644
index 0000000000..2b7fa8e0f6
--- /dev/null
+++ b/lib/bundler/rubygems_gem_installer.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+require "rubygems/installer"
+
+module Bundler
+ class RubyGemsGemInstaller < Gem::Installer
+ unless respond_to?(:at)
+ def self.at(*args)
+ new(*args)
+ end
+ end
+
+ def check_executable_overwrite(filename)
+ # Bundler needs to install gems regardless of binstub overwriting
+ end
+
+ def pre_install_checks
+ super && validate_bundler_checksum(options[:bundler_expected_checksum])
+ end
+
+ def build_extensions
+ extension_cache_path = options[:bundler_extension_cache_path]
+ return super unless extension_cache_path && extension_dir = Bundler.rubygems.spec_extension_dir(spec)
+
+ 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(extension_cache_path) do
+ FileUtils.cp_r extension_cache_path, spec.extension_dir
+ end
+ else
+ super
+ if extension_dir.directory? # not made for gems without extensions
+ SharedHelpers.filesystem_access(extension_cache_path.parent, &:mkpath)
+ SharedHelpers.filesystem_access(extension_cache_path) do
+ FileUtils.cp_r extension_dir, extension_cache_path
+ end
+ end
+ end
+ end
+
+ private
+
+ def validate_bundler_checksum(checksum)
+ return true if Bundler.settings[:disable_checksum_validation]
+ return true unless checksum
+ return true unless source = @package.instance_variable_get(:@gem)
+ return true unless source.respond_to?(:with_read_io)
+ digest = source.with_read_io do |io|
+ digest = SharedHelpers.digest(:SHA256).new
+ digest << io.read(16_384) until io.eof?
+ io.rewind
+ send(checksum_type(checksum), digest)
+ end
+ unless digest == checksum
+ raise SecurityError, <<-MESSAGE
+ Bundler cannot continue installing #{spec.name} (#{spec.version}).
+ The checksum for the downloaded `#{spec.full_name}.gem` does not match \
+ the checksum given by the server. This means the contents of the downloaded \
+ gem is different from what was uploaded to the server, and could be a potential security issue.
+
+ To resolve this issue:
+ 1. delete the downloaded gem located at: `#{spec.gem_dir}/#{spec.full_name}.gem`
+ 2. run `bundle install`
+
+ If you wish to continue installing the downloaded gem, and are certain it does not pose a \
+ security issue despite the mismatching checksum, do the following:
+ 1. run `bundle config disable_checksum_validation true` to turn off checksum verification
+ 2. run `bundle install`
+
+ (More info: The expected SHA256 checksum was #{checksum.inspect}, but the \
+ checksum for the downloaded gem was #{digest.inspect}.)
+ MESSAGE
+ end
+ true
+ end
+
+ def checksum_type(checksum)
+ case checksum.length
+ when 64 then :hexdigest!
+ when 44 then :base64digest!
+ else raise InstallError, "The given checksum for #{spec.full_name} (#{checksum.inspect}) is not a valid SHA256 hexdigest nor base64digest"
+ end
+ end
+
+ def hexdigest!(digest)
+ digest.hexdigest!
+ end
+
+ def base64digest!(digest)
+ if digest.respond_to?(:base64digest!)
+ digest.base64digest!
+ else
+ [digest.digest!].pack("m0")
+ end
+ end
+ end
+end
diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb
new file mode 100644
index 0000000000..783d106e7b
--- /dev/null
+++ b/lib/bundler/rubygems_integration.rb
@@ -0,0 +1,898 @@
+# frozen_string_literal: true
+
+require "monitor"
+require "rubygems"
+require "rubygems/config_file"
+
+module Bundler
+ class RubygemsIntegration
+ if defined?(Gem::Ext::Builder::CHDIR_MONITOR)
+ EXT_LOCK = Gem::Ext::Builder::CHDIR_MONITOR
+ else
+ EXT_LOCK = Monitor.new
+ end
+
+ def self.version
+ @version ||= Gem::Version.new(Gem::VERSION)
+ end
+
+ def self.provides?(req_str)
+ Gem::Requirement.new(req_str).satisfied_by?(version)
+ end
+
+ def initialize
+ @replaced_methods = {}
+ end
+
+ def version
+ self.class.version
+ end
+
+ def provides?(req_str)
+ self.class.provides?(req_str)
+ end
+
+ def build_args
+ Gem::Command.build_args
+ end
+
+ def build_args=(args)
+ Gem::Command.build_args = args
+ end
+
+ def load_path_insert_index
+ Gem.load_path_insert_index
+ end
+
+ def loaded_specs(name)
+ Gem.loaded_specs[name]
+ end
+
+ def mark_loaded(spec)
+ if spec.respond_to?(:activated=)
+ current = Gem.loaded_specs[spec.name]
+ current.activated = false if current
+ spec.activated = true
+ end
+ Gem.loaded_specs[spec.name] = spec
+ end
+
+ def validate(spec)
+ Bundler.ui.silence { spec.validate(false) }
+ rescue Gem::InvalidSpecificationException => e
+ error_message = "The gemspec at #{spec.loaded_from} is not valid. Please fix this gemspec.\n" \
+ "The validation error was '#{e.message}'\n"
+ raise Gem::InvalidSpecificationException.new(error_message)
+ rescue Errno::ENOENT
+ nil
+ end
+
+ def set_installed_by_version(spec, installed_by_version = Gem::VERSION)
+ return unless spec.respond_to?(:installed_by_version=)
+ spec.installed_by_version = Gem::Version.create(installed_by_version)
+ end
+
+ def spec_missing_extensions?(spec, default = true)
+ return spec.missing_extensions? if spec.respond_to?(:missing_extensions?)
+
+ return false if spec_default_gem?(spec)
+ return false if spec.extensions.empty?
+
+ default
+ end
+
+ def spec_default_gem?(spec)
+ spec.respond_to?(:default_gem?) && spec.default_gem?
+ end
+
+ def spec_matches_for_glob(spec, glob)
+ return spec.matches_for_glob(glob) if spec.respond_to?(:matches_for_glob)
+
+ spec.load_paths.map do |lp|
+ Dir["#{lp}/#{glob}#{suffix_pattern}"]
+ end.flatten(1)
+ end
+
+ def spec_extension_dir(spec)
+ return unless spec.respond_to?(:extension_dir)
+ spec.extension_dir
+ end
+
+ def stub_set_spec(stub, spec)
+ stub.instance_variable_set(:@spec, spec)
+ end
+
+ def path(obj)
+ obj.to_s
+ end
+
+ def platforms
+ return [Gem::Platform::RUBY] if Bundler.settings[:force_ruby_platform]
+ Gem.platforms
+ end
+
+ def configuration
+ require "bundler/psyched_yaml"
+ Gem.configuration
+ rescue Gem::SystemExitException, LoadError => e
+ Bundler.ui.error "#{e.class}: #{e.message}"
+ Bundler.ui.trace e
+ raise
+ rescue YamlLibrarySyntaxError => e
+ raise YamlSyntaxError.new(e, "Your RubyGems configuration, which is " \
+ "usually located in ~/.gemrc, contains invalid YAML syntax.")
+ end
+
+ def ruby_engine
+ Gem.ruby_engine
+ end
+
+ def read_binary(path)
+ Gem.read_binary(path)
+ end
+
+ def inflate(obj)
+ if defined?(Gem::Util)
+ Gem::Util.inflate(obj)
+ else
+ Gem.inflate(obj)
+ end
+ end
+
+ def sources=(val)
+ # Gem.configuration creates a new Gem::ConfigFile, which by default will read ~/.gemrc
+ # If that file exists, its settings (including sources) will overwrite the values we
+ # are about to set here. In order to avoid that, we force memoizing the config file now.
+ configuration
+
+ Gem.sources = val
+ end
+
+ def sources
+ Gem.sources
+ end
+
+ def gem_dir
+ Gem.dir
+ end
+
+ def gem_bindir
+ Gem.bindir
+ end
+
+ def user_home
+ Gem.user_home
+ end
+
+ def gem_path
+ Gem.path
+ end
+
+ def reset
+ Gem::Specification.reset
+ end
+
+ def post_reset_hooks
+ Gem.post_reset_hooks
+ end
+
+ def suffix_pattern
+ Gem.suffix_pattern
+ end
+
+ def gem_cache
+ gem_path.map {|p| File.expand_path("cache", p) }
+ end
+
+ def spec_cache_dirs
+ @spec_cache_dirs ||= begin
+ dirs = gem_path.map {|dir| File.join(dir, "specifications") }
+ dirs << Gem.spec_cache_dir if Gem.respond_to?(:spec_cache_dir) # Not in RubyGems 2.0.3 or earlier
+ dirs.uniq.select {|dir| File.directory? dir }
+ end
+ end
+
+ def marshal_spec_dir
+ Gem::MARSHAL_SPEC_DIR
+ end
+
+ def config_map
+ Gem::ConfigMap
+ end
+
+ def repository_subdirectories
+ %w[cache doc gems specifications]
+ end
+
+ def clear_paths
+ Gem.clear_paths
+ end
+
+ def bin_path(gem, bin, ver)
+ Gem.bin_path(gem, bin, ver)
+ end
+
+ def path_separator
+ File::PATH_SEPARATOR
+ end
+
+ def preserve_paths
+ # this is a no-op outside of RubyGems 1.8
+ yield
+ end
+
+ def loaded_gem_paths
+ # RubyGems 2.2+ can put binary extension into dedicated folders,
+ # therefore use RubyGems facilities to obtain their load paths.
+ if Gem::Specification.method_defined? :full_require_paths
+ loaded_gem_paths = Gem.loaded_specs.map {|_, s| s.full_require_paths }
+ loaded_gem_paths.flatten
+ else
+ $LOAD_PATH.select do |p|
+ Bundler.rubygems.gem_path.any? {|gp| p =~ /^#{Regexp.escape(gp)}/ }
+ end
+ end
+ end
+
+ def load_plugins
+ Gem.load_plugins if Gem.respond_to?(:load_plugins)
+ end
+
+ def load_plugin_files(files)
+ Gem.load_plugin_files(files) if Gem.respond_to?(:load_plugin_files)
+ end
+
+ def ui=(obj)
+ Gem::DefaultUserInteraction.ui = obj
+ end
+
+ def ext_lock
+ EXT_LOCK
+ end
+
+ def fetch_specs(all, pre, &blk)
+ require "rubygems/spec_fetcher"
+ specs = Gem::SpecFetcher.new.list(all, pre)
+ specs.each { yield } if block_given?
+ specs
+ end
+
+ def fetch_prerelease_specs
+ fetch_specs(false, true)
+ rescue Gem::RemoteFetcher::FetchError
+ {} # if we can't download them, there aren't any
+ end
+
+ # TODO: This is for older versions of RubyGems... should we support the
+ # X-Gemfile-Source header on these old versions?
+ # Maybe the newer implementation will work on older RubyGems?
+ # It seems difficult to keep this implementation and still send the header.
+ def fetch_all_remote_specs(remote)
+ old_sources = Bundler.rubygems.sources
+ Bundler.rubygems.sources = [remote.uri.to_s]
+ # Fetch all specs, minus prerelease specs
+ spec_list = fetch_specs(true, false)
+ # Then fetch the prerelease specs
+ fetch_prerelease_specs.each {|k, v| spec_list[k].concat(v) }
+
+ spec_list.values.first
+ ensure
+ Bundler.rubygems.sources = old_sources
+ end
+
+ def with_build_args(args)
+ ext_lock.synchronize do
+ old_args = build_args
+ begin
+ self.build_args = args
+ yield
+ ensure
+ self.build_args = old_args
+ end
+ end
+ end
+
+ def install_with_build_args(args)
+ with_build_args(args) { yield }
+ end
+
+ def gem_from_path(path, policy = nil)
+ require "rubygems/format"
+ Gem::Format.from_file_by_path(path, policy)
+ end
+
+ def spec_from_gem(path, policy = nil)
+ require "rubygems/security"
+ require "bundler/psyched_yaml"
+ gem_from_path(path, security_policies[policy]).spec
+ rescue Gem::Package::FormatError
+ raise GemspecError, "Could not read gem at #{path}. It may be corrupted."
+ rescue Exception, Gem::Exception, Gem::Security::Exception => e
+ if e.is_a?(Gem::Security::Exception) ||
+ e.message =~ /unknown trust policy|unsigned gem/i ||
+ e.message =~ /couldn't verify (meta)?data signature/i
+ raise SecurityError,
+ "The gem #{File.basename(path, ".gem")} can't be installed because " \
+ "the security policy didn't allow it, with the message: #{e.message}"
+ else
+ raise e
+ end
+ end
+
+ def build(spec, skip_validation = false)
+ require "rubygems/builder"
+ Gem::Builder.new(spec).build
+ end
+
+ def build_gem(gem_dir, spec)
+ build(spec)
+ end
+
+ def download_gem(spec, uri, path)
+ uri = Bundler.settings.mirror_for(uri)
+ fetcher = Gem::RemoteFetcher.new(configuration[:http_proxy])
+ Bundler::Retry.new("download gem from #{uri}").attempts do
+ fetcher.download(spec, uri, path)
+ end
+ end
+
+ def security_policy_keys
+ %w[High Medium Low AlmostNo No].map {|level| "#{level}Security" }
+ end
+
+ def security_policies
+ @security_policies ||= begin
+ require "rubygems/security"
+ Gem::Security::Policies
+ rescue LoadError, NameError
+ {}
+ end
+ end
+
+ 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))
+ end
+ end
+ end
+
+ def binstubs_call_gem?
+ true
+ end
+
+ def stubs_provide_full_functionality?
+ false
+ end
+
+ def replace_gem(specs, specs_by_name)
+ reverse_rubygems_kernel_mixin
+
+ executables = nil
+
+ kernel = (class << ::Kernel; self; end)
+ [kernel, ::Kernel].each do |kernel_class|
+ redefine_method(kernel_class, :gem) do |dep, *reqs|
+ executables ||= specs.map(&:executables).flatten if ::Bundler.rubygems.binstubs_call_gem?
+ if executables && executables.include?(File.basename(caller.first.split(":").first))
+ break
+ end
+
+ reqs.pop if reqs.last.is_a?(Hash)
+
+ unless dep.respond_to?(:name) && dep.respond_to?(:requirement)
+ dep = Gem::Dependency.new(dep, reqs)
+ end
+
+ if spec = specs_by_name[dep.name]
+ return true if dep.matches_spec?(spec)
+ end
+
+ message = if spec.nil?
+ "#{dep.name} is not part of the bundle." \
+ " Add it to your #{Bundler.default_gemfile.basename}."
+ else
+ "can't activate #{dep}, already activated #{spec.full_name}. " \
+ "Make sure all dependencies are added to Gemfile."
+ end
+
+ e = Gem::LoadError.new(message)
+ e.name = dep.name
+ if e.respond_to?(:requirement=)
+ e.requirement = dep.requirement
+ elsif e.respond_to?(:version_requirement=)
+ e.version_requirement = dep.requirement
+ end
+ raise e
+ end
+
+ # backwards compatibility shim, see https://github.com/bundler/bundler/issues/5102
+ kernel_class.send(:public, :gem) if Bundler.feature_flag.setup_makes_kernel_gem_public?
+ end
+ end
+
+ def stub_source_index(specs)
+ Gem::SourceIndex.send(:alias_method, :old_initialize, :initialize)
+ redefine_method(Gem::SourceIndex, :initialize) do |*args|
+ @gems = {}
+ # You're looking at this thinking: Oh! This is how I make those
+ # rubygems deprecations go away!
+ #
+ # You'd be correct BUT using of this method in production code
+ # must be approved by the rubygems team itself!
+ #
+ # This is your warning. If you use this and don't have approval
+ # we can't protect you.
+ #
+ Deprecate.skip_during do
+ self.spec_dirs = *args
+ add_specs(*specs)
+ end
+ end
+ end
+
+ # Used to make bin stubs that are not created by bundler work
+ # under bundler. The new Gem.bin_path only considers gems in
+ # +specs+
+ def replace_bin_path(specs, specs_by_name)
+ gem_class = (class << Gem; self; end)
+
+ redefine_method(gem_class, :find_spec_for_exe) do |gem_name, *args|
+ exec_name = args.first
+
+ spec_with_name = specs_by_name[gem_name]
+ spec = if exec_name
+ if spec_with_name && spec_with_name.executables.include?(exec_name)
+ spec_with_name
+ else
+ specs.find {|s| s.executables.include?(exec_name) }
+ end
+ else
+ spec_with_name
+ end
+
+ unless spec
+ message = "can't find executable #{exec_name} for gem #{gem_name}"
+ if !exec_name || spec_with_name.nil?
+ message += ". #{gem_name} is not currently included in the bundle, " \
+ "perhaps you meant to add it to your #{Bundler.default_gemfile.basename}?"
+ end
+ raise Gem::Exception, message
+ end
+
+ raise Gem::Exception, "no default executable for #{spec.full_name}" unless exec_name ||= spec.default_executable
+
+ unless spec.name == gem_name
+ Bundler::SharedHelpers.major_deprecation 2,
+ "Bundler is using a binstub that was created for a different gem (#{spec.name}).\n" \
+ "You should run `bundle binstub #{gem_name}` " \
+ "to work around a system/bundle conflict."
+ end
+ spec
+ end
+
+ redefine_method(gem_class, :activate_bin_path) do |name, *args|
+ exec_name = args.first
+ return ENV["BUNDLE_BIN_PATH"] if exec_name == "bundle"
+
+ # Copy of Rubygems activate_bin_path impl
+ requirement = args.last
+ spec = find_spec_for_exe name, exec_name, [requirement]
+
+ gem_bin = File.join(spec.full_gem_path, spec.bindir, exec_name)
+ gem_from_path_bin = File.join(File.dirname(spec.loaded_from), spec.bindir, exec_name)
+ File.exist?(gem_bin) ? gem_bin : gem_from_path_bin
+ end
+
+ redefine_method(gem_class, :bin_path) do |name, *args|
+ exec_name = args.first
+ return ENV["BUNDLE_BIN_PATH"] if exec_name == "bundle"
+
+ spec = find_spec_for_exe(name, *args)
+ exec_name ||= spec.default_executable
+
+ gem_bin = File.join(spec.full_gem_path, spec.bindir, exec_name)
+ gem_from_path_bin = File.join(File.dirname(spec.loaded_from), spec.bindir, exec_name)
+ File.exist?(gem_bin) ? gem_bin : gem_from_path_bin
+ end
+ end
+
+ # Because Bundler has a static view of what specs are available,
+ # we don't #refresh, so stub it out.
+ def replace_refresh
+ gem_class = (class << Gem; self; end)
+ redefine_method(gem_class, :refresh) {}
+ end
+
+ # Replace or hook into RubyGems to provide a bundlerized view
+ # of the world.
+ def replace_entrypoints(specs)
+ specs_by_name = specs.reduce({}) do |h, s|
+ h[s.name] = s
+ h
+ end
+
+ replace_gem(specs, specs_by_name)
+ stub_rubygems(specs)
+ replace_bin_path(specs, specs_by_name)
+ replace_refresh
+
+ Gem.clear_paths
+ end
+
+ # This backports the correct segment generation code from RubyGems 1.4+
+ # by monkeypatching it into the method in RubyGems 1.3.6 and 1.3.7.
+ def backport_segment_generation
+ redefine_method(Gem::Version, :segments) do
+ @segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s|
+ /^\d+$/ =~ s ? s.to_i : s
+ end
+ end
+ end
+
+ # This backport fixes the marshaling of @segments.
+ def backport_yaml_initialize
+ redefine_method(Gem::Version, :yaml_initialize) do |_, map|
+ @version = map["version"]
+ @segments = nil
+ @hash = nil
+ end
+ end
+
+ # This backports base_dir which replaces installation path
+ # RubyGems 1.8+
+ def backport_base_dir
+ redefine_method(Gem::Specification, :base_dir) do
+ return Gem.dir unless loaded_from
+ File.dirname File.dirname loaded_from
+ end
+ end
+
+ def backport_cache_file
+ redefine_method(Gem::Specification, :cache_dir) do
+ @cache_dir ||= File.join base_dir, "cache"
+ end
+
+ redefine_method(Gem::Specification, :cache_file) do
+ @cache_file ||= File.join cache_dir, "#{full_name}.gem"
+ end
+ end
+
+ def backport_spec_file
+ redefine_method(Gem::Specification, :spec_dir) do
+ @spec_dir ||= File.join base_dir, "specifications"
+ end
+
+ redefine_method(Gem::Specification, :spec_file) do
+ @spec_file ||= File.join spec_dir, "#{full_name}.gemspec"
+ end
+ end
+
+ def undo_replacements
+ @replaced_methods.each do |(sym, klass), method|
+ redefine_method(klass, sym, method)
+ end
+ if Binding.public_method_defined?(:source_location)
+ post_reset_hooks.reject! {|proc| proc.binding.source_location[0] == __FILE__ }
+ else
+ post_reset_hooks.reject! {|proc| proc.binding.eval("__FILE__") == __FILE__ }
+ end
+ @replaced_methods.clear
+ end
+
+ def redefine_method(klass, method, unbound_method = nil, &block)
+ visibility = method_visibility(klass, method)
+ begin
+ if (instance_method = klass.instance_method(method)) && method != :initialize
+ # doing this to ensure we also get private methods
+ klass.send(:remove_method, method)
+ end
+ rescue NameError
+ # method isn't defined
+ nil
+ end
+ @replaced_methods[[method, klass]] = instance_method
+ if unbound_method
+ klass.send(:define_method, method, unbound_method)
+ klass.send(visibility, method)
+ elsif block
+ klass.send(:define_method, method, &block)
+ klass.send(visibility, method)
+ end
+ end
+
+ def method_visibility(klass, method)
+ if klass.private_method_defined?(method)
+ :private
+ elsif klass.protected_method_defined?(method)
+ :protected
+ else
+ :public
+ end
+ end
+
+ # RubyGems 1.4 through 1.6
+ class Legacy < RubygemsIntegration
+ def initialize
+ super
+ backport_base_dir
+ backport_cache_file
+ backport_spec_file
+ backport_yaml_initialize
+ end
+
+ def stub_rubygems(specs)
+ # RubyGems versions lower than 1.7 use SourceIndex#from_gems_in
+ source_index_class = (class << Gem::SourceIndex; self; end)
+ redefine_method(source_index_class, :from_gems_in) do |*args|
+ Gem::SourceIndex.new.tap do |source_index|
+ source_index.spec_dirs = *args
+ source_index.add_specs(*specs)
+ end
+ end
+ end
+
+ def all_specs
+ Gem.source_index.gems.values
+ end
+
+ def find_name(name)
+ Gem.source_index.find_name(name)
+ end
+
+ def validate(spec)
+ # These versions of RubyGems always validate in "packaging" mode,
+ # which is too strict for the kinds of checks we care about. As a
+ # result, validation is disabled on versions of RubyGems below 1.7.
+ end
+
+ def post_reset_hooks
+ []
+ end
+
+ def reset
+ end
+ end
+
+ # RubyGems versions 1.3.6 and 1.3.7
+ class Ancient < Legacy
+ def initialize
+ super
+ backport_segment_generation
+ end
+ end
+
+ # RubyGems 1.7
+ class Transitional < Legacy
+ def stub_rubygems(specs)
+ stub_source_index(specs)
+ end
+
+ def validate(spec)
+ # Missing summary is downgraded to a warning in later versions,
+ # so we set it to an empty string to prevent an exception here.
+ spec.summary ||= ""
+ RubygemsIntegration.instance_method(:validate).bind(self).call(spec)
+ end
+ end
+
+ # RubyGems 1.8.5-1.8.19
+ class Modern < RubygemsIntegration
+ def stub_rubygems(specs)
+ Gem::Specification.all = specs
+
+ Gem.post_reset do
+ Gem::Specification.all = specs
+ end
+
+ stub_source_index(specs)
+ end
+
+ def all_specs
+ Gem::Specification.to_a
+ end
+
+ def find_name(name)
+ Gem::Specification.find_all_by_name name
+ end
+ end
+
+ # RubyGems 1.8.0 to 1.8.4
+ class AlmostModern < Modern
+ # RubyGems [>= 1.8.0, < 1.8.5] has a bug that changes Gem.dir whenever
+ # you call Gem::Installer#install with an :install_dir set. We have to
+ # change it back for our sudo mode to work.
+ def preserve_paths
+ old_dir = gem_dir
+ old_path = gem_path
+ yield
+ Gem.use_paths(old_dir, old_path)
+ end
+ end
+
+ # RubyGems 1.8.20+
+ class MoreModern < Modern
+ # RubyGems 1.8.20 and adds the skip_validation parameter, so that's
+ # when we start passing it through.
+ def build(spec, skip_validation = false)
+ require "rubygems/builder"
+ Gem::Builder.new(spec).build(skip_validation)
+ end
+ end
+
+ # RubyGems 2.0
+ class Future < RubygemsIntegration
+ def stub_rubygems(specs)
+ Gem::Specification.all = specs
+
+ Gem.post_reset do
+ Gem::Specification.all = specs
+ end
+
+ redefine_method((class << Gem; self; end), :finish_resolve) do |*|
+ []
+ end
+ end
+
+ def all_specs
+ Gem::Specification.to_a
+ end
+
+ def find_name(name)
+ Gem::Specification.find_all_by_name name
+ end
+
+ def fetch_specs(source, remote, name)
+ path = source + "#{name}.#{Gem.marshal_version}.gz"
+ 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)
+ rescue Gem::RemoteFetcher::FetchError => e
+ # it's okay for prerelease to fail
+ raise e unless name == "prerelease_specs"
+ end
+
+ def fetch_all_remote_specs(remote)
+ source = remote.uri.is_a?(URI) ? remote.uri : URI.parse(source.to_s)
+
+ specs = fetch_specs(source, remote, "specs")
+ pres = fetch_specs(source, remote, "prerelease_specs") || []
+
+ specs.concat(pres)
+ end
+
+ def download_gem(spec, uri, path)
+ uri = Bundler.settings.mirror_for(uri)
+ fetcher = gem_remote_fetcher
+ fetcher.headers = { "X-Gemfile-Source" => spec.remote.original_uri.to_s } if spec.remote.original_uri
+ Bundler::Retry.new("download gem from #{uri}").attempts do
+ fetcher.download(spec, uri, path)
+ end
+ end
+
+ def gem_remote_fetcher
+ require "resolv"
+ proxy = configuration[:http_proxy]
+ dns = Resolv::DNS.new
+ Bundler::GemRemoteFetcher.new(proxy, dns)
+ end
+
+ def gem_from_path(path, policy = nil)
+ require "rubygems/package"
+ p = Gem::Package.new(path)
+ p.security_policy = policy if policy
+ p
+ end
+
+ def build(spec, skip_validation = false)
+ require "rubygems/package"
+ Gem::Package.build(spec, skip_validation)
+ end
+
+ def repository_subdirectories
+ Gem::REPOSITORY_SUBDIRECTORIES
+ end
+
+ def install_with_build_args(args)
+ yield
+ end
+
+ def path_separator
+ Gem.path_separator
+ end
+ end
+
+ # RubyGems 2.1.0
+ class MoreFuture < Future
+ def initialize
+ super
+ backport_ext_builder_monitor
+ end
+
+ def all_specs
+ require "bundler/remote_specification"
+ Gem::Specification.stubs.map do |stub|
+ StubSpecification.from_stub(stub)
+ end
+ end
+
+ def backport_ext_builder_monitor
+ # So we can avoid requiring "rubygems/ext" in its entirety
+ Gem.module_eval <<-RB, __FILE__, __LINE__ + 1
+ module Ext
+ end
+ RB
+
+ require "rubygems/ext/builder"
+
+ Gem::Ext::Builder.class_eval do
+ unless const_defined?(:CHDIR_MONITOR)
+ const_set(:CHDIR_MONITOR, EXT_LOCK)
+ end
+
+ remove_const(:CHDIR_MUTEX) if const_defined?(:CHDIR_MUTEX)
+ const_set(:CHDIR_MUTEX, const_get(:CHDIR_MONITOR))
+ end
+ end
+
+ if Gem::Specification.respond_to?(:stubs_for)
+ def find_name(name)
+ Gem::Specification.stubs_for(name).map(&:to_spec)
+ end
+ else
+ def find_name(name)
+ Gem::Specification.stubs.find_all do |spec|
+ spec.name == name
+ end.map(&:to_spec)
+ end
+ end
+
+ def use_gemdeps(gemfile)
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path(gemfile)
+ require "bundler/gemdeps"
+ runtime = Bundler.setup
+ Bundler.ui = nil
+ activated_spec_names = runtime.requested_specs.map(&:to_spec).sort_by(&:name)
+ [Gemdeps.new(runtime), activated_spec_names]
+ end
+
+ if provides?(">= 2.5.2")
+ # RubyGems-generated binstubs call Kernel#gem
+ def binstubs_call_gem?
+ false
+ end
+
+ # only 2.5.2+ has all of the stub methods we want to use, and since this
+ # is a performance optimization _only_,
+ # we'll restrict ourselves to the most
+ # recent RG versions instead of all versions that have stubs
+ def stubs_provide_full_functionality?
+ true
+ end
+ end
+ end
+ end
+
+ def self.rubygems
+ @rubygems ||= if RubygemsIntegration.provides?(">= 2.1.0")
+ RubygemsIntegration::MoreFuture.new
+ elsif RubygemsIntegration.provides?(">= 1.99.99")
+ RubygemsIntegration::Future.new
+ elsif RubygemsIntegration.provides?(">= 1.8.20")
+ RubygemsIntegration::MoreModern.new
+ elsif RubygemsIntegration.provides?(">= 1.8.5")
+ RubygemsIntegration::Modern.new
+ elsif RubygemsIntegration.provides?(">= 1.8.0")
+ RubygemsIntegration::AlmostModern.new
+ elsif RubygemsIntegration.provides?(">= 1.7.0")
+ RubygemsIntegration::Transitional.new
+ elsif RubygemsIntegration.provides?(">= 1.4.0")
+ RubygemsIntegration::Legacy.new
+ else # RubyGems 1.3.6 and 1.3.7
+ RubygemsIntegration::Ancient.new
+ end
+ end
+end
diff --git a/lib/bundler/runtime.rb b/lib/bundler/runtime.rb
new file mode 100644
index 0000000000..762e7b3ec6
--- /dev/null
+++ b/lib/bundler/runtime.rb
@@ -0,0 +1,322 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Runtime
+ include SharedHelpers
+
+ def initialize(root, definition)
+ @root = root
+ @definition = definition
+ end
+
+ def setup(*groups)
+ @definition.ensure_equivalent_gemfile_and_lockfile if Bundler.frozen_bundle?
+
+ groups.map!(&:to_sym)
+
+ # Has to happen first
+ clean_load_path
+
+ specs = groups.any? ? @definition.specs_for(groups) : requested_specs
+
+ SharedHelpers.set_bundle_environment
+ Bundler.rubygems.replace_entrypoints(specs)
+
+ # Activate the specs
+ load_paths = specs.map do |spec|
+ unless spec.loaded_from
+ raise GemNotFound, "#{spec.full_name} is missing. Run `bundle install` to get it."
+ end
+
+ check_for_activated_spec!(spec)
+
+ Bundler.rubygems.mark_loaded(spec)
+ spec.load_paths.reject {|path| $LOAD_PATH.include?(path) }
+ end.reverse.flatten
+
+ # See Gem::Specification#add_self_to_load_path (since RubyGems 1.8)
+ if insert_index = Bundler.rubygems.load_path_insert_index
+ # Gem directories must come after -I and ENV['RUBYLIB']
+ $LOAD_PATH.insert(insert_index, *load_paths)
+ else
+ # We are probably testing in core, -I and RUBYLIB don't apply
+ $LOAD_PATH.unshift(*load_paths)
+ end
+
+ setup_manpath
+
+ lock(:preserve_unknown_sections => true)
+
+ self
+ end
+
+ REQUIRE_ERRORS = [
+ /^no such file to load -- (.+)$/i,
+ /^Missing \w+ (?:file\s*)?([^\s]+.rb)$/i,
+ /^Missing API definition file in (.+)$/i,
+ /^cannot load such file -- (.+)$/i,
+ /^dlopen\([^)]*\): Library not loaded: (.+)$/i,
+ ].freeze
+
+ def require(*groups)
+ groups.map!(&:to_sym)
+ groups = [:default] if groups.empty?
+
+ @definition.dependencies.each do |dep|
+ # Skip the dependency if it is not in any of the requested groups, or
+ # not for the current platform, or doesn't match the gem constraints.
+ next unless (dep.groups & groups).any? && dep.should_include?
+
+ required_file = nil
+
+ begin
+ # Loop through all the specified autorequires for the
+ # dependency. If there are none, use the dependency's name
+ # as the autorequire.
+ Array(dep.autorequire || dep.name).each do |file|
+ # Allow `require: true` as an alias for `require: <name>`
+ file = dep.name if file == true
+ required_file = file
+ begin
+ Kernel.require file
+ rescue RuntimeError => e
+ raise e if e.is_a?(LoadError) # we handle this a little later
+ raise Bundler::GemRequireError.new e,
+ "There was an error while trying to load the gem '#{file}'."
+ end
+ end
+ rescue LoadError => e
+ REQUIRE_ERRORS.find {|r| r =~ e.message }
+ raise if dep.autorequire || $1 != required_file
+
+ if dep.autorequire.nil? && dep.name.include?("-")
+ begin
+ namespaced_file = dep.name.tr("-", "/")
+ Kernel.require namespaced_file
+ rescue LoadError => e
+ REQUIRE_ERRORS.find {|r| r =~ e.message }
+ raise if $1 != namespaced_file
+ end
+ end
+ end
+ end
+ end
+
+ def self.definition_method(meth)
+ define_method(meth) do
+ raise ArgumentError, "no definition when calling Runtime##{meth}" unless @definition
+ @definition.send(meth)
+ end
+ end
+ private_class_method :definition_method
+
+ definition_method :requested_specs
+ definition_method :specs
+ definition_method :dependencies
+ definition_method :current_dependencies
+ definition_method :requires
+
+ def lock(opts = {})
+ return if @definition.nothing_changed? && !@definition.unlocking?
+ @definition.lock(Bundler.default_lockfile, opts[:preserve_unknown_sections])
+ end
+
+ alias_method :gems, :specs
+
+ def cache(custom_path = nil)
+ cache_path = Bundler.app_cache(custom_path)
+ SharedHelpers.filesystem_access(cache_path) do |p|
+ FileUtils.mkdir_p(p)
+ end unless File.exist?(cache_path)
+
+ Bundler.ui.info "Updating files in #{Bundler.settings.app_cache_path}"
+
+ specs_to_cache = Bundler.settings[:cache_all_platforms] ? @definition.resolve.materialized_for_all_platforms : specs
+ specs_to_cache.each do |spec|
+ next if spec.name == "bundler"
+ next if spec.source.is_a?(Source::Gemspec)
+ spec.source.send(:fetch_gem, spec) if Bundler.settings[:cache_all_platforms] && spec.source.respond_to?(:fetch_gem, true)
+ spec.source.cache(spec, custom_path) if spec.source.respond_to?(:cache)
+ end
+
+ Dir[cache_path.join("*/.git")].each do |git_dir|
+ FileUtils.rm_rf(git_dir)
+ FileUtils.touch(File.expand_path("../.bundlecache", git_dir))
+ end
+
+ prune_cache(cache_path) unless Bundler.settings[:no_prune]
+ end
+
+ def prune_cache(cache_path)
+ SharedHelpers.filesystem_access(cache_path) do |p|
+ FileUtils.mkdir_p(p)
+ end unless File.exist?(cache_path)
+ resolve = @definition.resolve
+ prune_gem_cache(resolve, cache_path)
+ prune_git_and_path_cache(resolve, cache_path)
+ end
+
+ def clean(dry_run = false)
+ gem_bins = Dir["#{Gem.dir}/bin/*"]
+ git_dirs = Dir["#{Gem.dir}/bundler/gems/*"]
+ git_cache_dirs = Dir["#{Gem.dir}/cache/bundler/git/*"]
+ gem_dirs = Dir["#{Gem.dir}/gems/*"]
+ gem_files = Dir["#{Gem.dir}/cache/*.gem"]
+ gemspec_files = Dir["#{Gem.dir}/specifications/*.gemspec"]
+ extension_dirs = Dir["#{Gem.dir}/extensions/*/*/*"]
+ spec_gem_paths = []
+ # need to keep git sources around
+ spec_git_paths = @definition.spec_git_paths
+ spec_git_cache_dirs = []
+ spec_gem_executables = []
+ spec_cache_paths = []
+ spec_gemspec_paths = []
+ spec_extension_paths = []
+ specs.each do |spec|
+ spec_gem_paths << spec.full_gem_path
+ # need to check here in case gems are nested like for the rails git repo
+ md = %r{(.+bundler/gems/.+-[a-f0-9]{7,12})}.match(spec.full_gem_path)
+ spec_git_paths << md[1] if md
+ spec_gem_executables << spec.executables.collect do |executable|
+ e = "#{Bundler.rubygems.gem_bindir}/#{executable}"
+ [e, "#{e}.bat"]
+ end
+ spec_cache_paths << spec.cache_file
+ spec_gemspec_paths << spec.spec_file
+ spec_extension_paths << spec.extension_dir if spec.respond_to?(:extension_dir)
+ spec_git_cache_dirs << spec.source.cache_path.to_s if spec.source.is_a?(Bundler::Source::Git)
+ end
+ spec_gem_paths.uniq!
+ spec_gem_executables.flatten!
+
+ stale_gem_bins = gem_bins - spec_gem_executables
+ stale_git_dirs = git_dirs - spec_git_paths - ["#{Gem.dir}/bundler/gems/extensions"]
+ stale_git_cache_dirs = git_cache_dirs - spec_git_cache_dirs
+ stale_gem_dirs = gem_dirs - spec_gem_paths
+ stale_gem_files = gem_files - spec_cache_paths
+ stale_gemspec_files = gemspec_files - spec_gemspec_paths
+ stale_extension_dirs = extension_dirs - spec_extension_paths
+
+ removed_stale_gem_dirs = stale_gem_dirs.collect {|dir| remove_dir(dir, dry_run) }
+ removed_stale_git_dirs = stale_git_dirs.collect {|dir| remove_dir(dir, dry_run) }
+ output = removed_stale_gem_dirs + removed_stale_git_dirs
+
+ unless dry_run
+ stale_files = stale_gem_bins + stale_gem_files + stale_gemspec_files
+ stale_files.each do |file|
+ SharedHelpers.filesystem_access(File.dirname(file)) do |_p|
+ FileUtils.rm(file) if File.exist?(file)
+ end
+ end
+
+ stale_dirs = stale_git_cache_dirs + stale_extension_dirs
+ stale_dirs.each do |stale_dir|
+ SharedHelpers.filesystem_access(stale_dir) do |dir|
+ FileUtils.rm_rf(dir) if File.exist?(dir)
+ end
+ end
+ end
+
+ output
+ end
+
+ private
+
+ def prune_gem_cache(resolve, cache_path)
+ cached = Dir["#{cache_path}/*.gem"]
+
+ cached = cached.delete_if do |path|
+ spec = Bundler.rubygems.spec_from_gem path
+
+ resolve.any? do |s|
+ s.name == spec.name && s.version == spec.version && !s.source.is_a?(Bundler::Source::Git)
+ end
+ end
+
+ if cached.any?
+ Bundler.ui.info "Removing outdated .gem files from #{Bundler.settings.app_cache_path}"
+
+ cached.each do |path|
+ Bundler.ui.info " * #{File.basename(path)}"
+ File.delete(path)
+ end
+ end
+ end
+
+ def prune_git_and_path_cache(resolve, cache_path)
+ cached = Dir["#{cache_path}/*/.bundlecache"]
+
+ cached = cached.delete_if do |path|
+ name = File.basename(File.dirname(path))
+
+ resolve.any? do |s|
+ source = s.source
+ source.respond_to?(:app_cache_dirname) && source.app_cache_dirname == name
+ end
+ end
+
+ if cached.any?
+ Bundler.ui.info "Removing outdated git and path gems from #{Bundler.settings.app_cache_path}"
+
+ cached.each do |path|
+ path = File.dirname(path)
+ Bundler.ui.info " * #{File.basename(path)}"
+ FileUtils.rm_rf(path)
+ end
+ end
+ end
+
+ def setup_manpath
+ # Add man/ subdirectories from activated bundles to MANPATH for man(1)
+ manuals = $LOAD_PATH.map do |path|
+ man_subdir = path.sub(/lib$/, "man")
+ man_subdir unless Dir[man_subdir + "/man?/"].empty?
+ end.compact
+
+ return if manuals.empty?
+ Bundler::SharedHelpers.set_env "MANPATH", manuals.concat(
+ ENV["MANPATH"].to_s.split(File::PATH_SEPARATOR)
+ ).uniq.join(File::PATH_SEPARATOR)
+ end
+
+ def remove_dir(dir, dry_run)
+ full_name = Pathname.new(dir).basename.to_s
+
+ parts = full_name.split("-")
+ name = parts[0..-2].join("-")
+ version = parts.last
+ output = "#{name} (#{version})"
+
+ if dry_run
+ Bundler.ui.info "Would have removed #{output}"
+ else
+ Bundler.ui.info "Removing #{output}"
+ FileUtils.rm_rf(dir)
+ end
+
+ output
+ end
+
+ def check_for_activated_spec!(spec)
+ return unless activated_spec = Bundler.rubygems.loaded_specs(spec.name)
+ return if activated_spec.version == spec.version
+
+ suggestion = if Bundler.rubygems.spec_default_gem?(activated_spec)
+ "Since #{spec.name} is a default gem, you can either remove your dependency on it" \
+ " or try updating to a newer version of bundler that supports #{spec.name} as a default gem."
+ else
+ "Prepending `bundle exec` to your command may solve this."
+ end
+
+ e = Gem::LoadError.new "You have already activated #{activated_spec.name} #{activated_spec.version}, " \
+ "but your Gemfile requires #{spec.name} #{spec.version}. #{suggestion}"
+ e.name = spec.name
+ if e.respond_to?(:requirement=)
+ e.requirement = Gem::Requirement.new(spec.version.to_s)
+ else
+ e.version_requirement = Gem::Requirement.new(spec.version.to_s)
+ end
+ raise e
+ end
+ end
+end
diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb
new file mode 100644
index 0000000000..fe68d510ff
--- /dev/null
+++ b/lib/bundler/settings.rb
@@ -0,0 +1,463 @@
+# frozen_string_literal: true
+
+require "uri"
+
+module Bundler
+ class Settings
+ autoload :Mirror, "bundler/mirror"
+ autoload :Mirrors, "bundler/mirror"
+ autoload :Validator, "bundler/settings/validator"
+
+ BOOL_KEYS = %w[
+ allow_bundler_dependency_conflicts
+ allow_deployment_source_credential_changes
+ allow_offline_install
+ auto_clean_without_path
+ auto_install
+ auto_config_jobs
+ cache_all
+ cache_all_platforms
+ cache_command_is_package
+ console_command
+ default_install_uses_path
+ deployment
+ deployment_means_frozen
+ disable_checksum_validation
+ disable_exec_load
+ disable_local_branch_check
+ disable_multisource
+ disable_platform_warnings
+ disable_shared_gems
+ disable_version_check
+ error_on_stderr
+ force_ruby_platform
+ forget_cli_options
+ frozen
+ gem.coc
+ gem.mit
+ global_path_appends_ruby_scope
+ global_gem_cache
+ ignore_messages
+ init_gems_rb
+ list_command
+ lockfile_uses_separate_rubygems_sources
+ major_deprecations
+ no_install
+ no_prune
+ only_update_to_newer_versions
+ path_relative_to_cwd
+ path.system
+ plugins
+ prefer_gems_rb
+ print_only_version_number
+ setup_makes_kernel_gem_public
+ silence_root_warning
+ skip_default_git_sources
+ specific_platform
+ suppress_install_using_messages
+ unlock_source_unlocks_spec
+ update_requires_all_flag
+ use_gem_version_promoter_for_major_updates
+ viz_command
+ ].freeze
+
+ NUMBER_KEYS = %w[
+ jobs
+ redirect
+ retry
+ ssl_verify_mode
+ timeout
+ ].freeze
+
+ ARRAY_KEYS = %w[
+ with
+ without
+ ].freeze
+
+ DEFAULT_CONFIG = {
+ :disable_version_check => true,
+ :redirect => 5,
+ :retry => 3,
+ :timeout => 10,
+ }.freeze
+
+ def initialize(root = nil)
+ @root = root
+ @local_config = load_config(local_config_file)
+ @global_config = load_config(global_config_file)
+ @temporary = {}
+ end
+
+ def [](name)
+ key = key_for(name)
+ value = @temporary.fetch(key) do
+ @local_config.fetch(key) do
+ ENV.fetch(key) do
+ @global_config.fetch(key) do
+ DEFAULT_CONFIG.fetch(name) do
+ nil
+ end end end end end
+
+ converted_value(value, name)
+ end
+
+ def set_command_option(key, value)
+ if Bundler.feature_flag.forget_cli_options?
+ temporary(key => value)
+ value
+ else
+ command = if value.nil?
+ "bundle config --delete #{key}"
+ else
+ "bundle config #{key} #{Array(value).join(":")}"
+ end
+
+ Bundler::SharedHelpers.major_deprecation 2,\
+ "flags passed to commands " \
+ "will no longer be automatically remembered. Instead please set flags " \
+ "you want remembered between commands using `bundle config " \
+ "<setting name> <setting value>`, i.e. `#{command}`"
+
+ set_local(key, value)
+ end
+ end
+
+ def set_command_option_if_given(key, value)
+ return if value.nil?
+ set_command_option(key, value)
+ end
+
+ def set_local(key, value)
+ local_config_file || raise(GemfileNotFound, "Could not locate Gemfile")
+
+ set_key(key, value, @local_config, local_config_file)
+ end
+
+ def temporary(update)
+ existing = Hash[update.map {|k, _| [k, @temporary[key_for(k)]] }]
+ update.each do |k, v|
+ set_key(k, v, @temporary, nil)
+ end
+ return unless block_given?
+ begin
+ yield
+ ensure
+ existing.each {|k, v| set_key(k, v, @temporary, nil) }
+ end
+ end
+
+ def set_global(key, value)
+ set_key(key, value, @global_config, global_config_file)
+ end
+
+ def all
+ env_keys = ENV.keys.grep(/\ABUNDLE_.+/)
+
+ keys = @temporary.keys | @global_config.keys | @local_config.keys | env_keys
+
+ keys.map do |key|
+ key.sub(/^BUNDLE_/, "").gsub(/__/, ".").downcase
+ end
+ end
+
+ def local_overrides
+ repos = {}
+ all.each do |k|
+ repos[$'] = self[k] if k =~ /^local\./
+ end
+ repos
+ end
+
+ def mirror_for(uri)
+ uri = URI(uri.to_s) unless uri.is_a?(URI)
+ gem_mirrors.for(uri.to_s).uri
+ end
+
+ def credentials_for(uri)
+ self[uri.to_s] || self[uri.host]
+ end
+
+ def gem_mirrors
+ all.inject(Mirrors.new) do |mirrors, k|
+ mirrors.parse(k, self[k]) if k.start_with?("mirror.")
+ mirrors
+ end
+ end
+
+ def locations(key)
+ key = key_for(key)
+ locations = {}
+ locations[:temporary] = @temporary[key] if @temporary.key?(key)
+ locations[:local] = @local_config[key] if @local_config.key?(key)
+ locations[:env] = ENV[key] if ENV[key]
+ locations[:global] = @global_config[key] if @global_config.key?(key)
+ locations[:default] = DEFAULT_CONFIG[key] if DEFAULT_CONFIG.key?(key)
+ locations
+ end
+
+ def pretty_values_for(exposed_key)
+ key = key_for(exposed_key)
+
+ locations = []
+
+ if @temporary.key?(key)
+ locations << "Set for the current command: #{converted_value(@temporary[key], exposed_key).inspect}"
+ end
+
+ if @local_config.key?(key)
+ locations << "Set for your local app (#{local_config_file}): #{converted_value(@local_config[key], exposed_key).inspect}"
+ end
+
+ if value = ENV[key]
+ locations << "Set via #{key}: #{converted_value(value, exposed_key).inspect}"
+ end
+
+ if @global_config.key?(key)
+ locations << "Set for the current user (#{global_config_file}): #{converted_value(@global_config[key], exposed_key).inspect}"
+ end
+
+ return ["You have not configured a value for `#{exposed_key}`"] if locations.empty?
+ locations
+ end
+
+ # for legacy reasons, in Bundler 1, the ruby scope isnt appended when the setting comes from ENV or the global config,
+ # nor do we respect :disable_shared_gems
+ def path
+ key = key_for(:path)
+ path = ENV[key] || @global_config[key]
+ if path && !@temporary.key?(key) && !@local_config.key?(key)
+ return Path.new(path, Bundler.feature_flag.global_path_appends_ruby_scope?, false, false)
+ end
+
+ system_path = self["path.system"] || (self[:disable_shared_gems] == false)
+ Path.new(self[:path], true, system_path, Bundler.feature_flag.default_install_uses_path?)
+ end
+
+ Path = Struct.new(:explicit_path, :append_ruby_scope, :system_path, :default_install_uses_path) do
+ def path
+ path = base_path
+ path = File.join(path, Bundler.ruby_scope) if append_ruby_scope && !use_system_gems?
+ path
+ end
+
+ def use_system_gems?
+ return true if system_path
+ return false if explicit_path
+ !default_install_uses_path
+ end
+
+ def base_path
+ path = explicit_path
+ path ||= ".bundle" unless use_system_gems?
+ path ||= Bundler.rubygems.gem_dir
+ path
+ end
+
+ def base_path_relative_to_pwd
+ base_path = Pathname.new(self.base_path)
+ expanded_base_path = base_path.expand_path(Bundler.root)
+ relative_path = expanded_base_path.relative_path_from(Pathname.pwd)
+ if relative_path.to_s.start_with?("..")
+ relative_path = base_path if base_path.absolute?
+ else
+ relative_path = Pathname.new(File.join(".", relative_path))
+ end
+ relative_path
+ rescue ArgumentError
+ expanded_base_path
+ end
+
+ def validate!
+ return unless explicit_path && system_path
+ path = Bundler.settings.pretty_values_for(:path)
+ path.unshift(nil, "path:") unless path.empty?
+ system_path = Bundler.settings.pretty_values_for("path.system")
+ system_path.unshift(nil, "path.system:") unless system_path.empty?
+ disable_shared_gems = Bundler.settings.pretty_values_for(:disable_shared_gems)
+ disable_shared_gems.unshift(nil, "disable_shared_gems:") unless disable_shared_gems.empty?
+ raise InvalidOption,
+ "Using a custom path while using system gems is unsupported.\n#{path.join("\n")}\n#{system_path.join("\n")}\n#{disable_shared_gems.join("\n")}"
+ 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
+
+ def app_cache_path
+ @app_cache_path ||= self[:cache_path] || "vendor/cache"
+ end
+
+ def validate!
+ all.each do |raw_key|
+ [@local_config, ENV, @global_config].each do |settings|
+ value = converted_value(settings[key_for(raw_key)], raw_key)
+ Validator.validate!(raw_key, value, settings.to_hash.dup)
+ end
+ end
+ end
+
+ def key_for(key)
+ key = Settings.normalize_uri(key).to_s if key.is_a?(String) && /https?:/ =~ key
+ key = key.to_s.gsub(".", "__").upcase
+ "BUNDLE_#{key}"
+ end
+
+ private
+
+ def parent_setting_for(name)
+ split_specific_setting_for(name)[0]
+ end
+
+ def specific_gem_for(name)
+ split_specific_setting_for(name)[1]
+ end
+
+ def split_specific_setting_for(name)
+ name.split(".")
+ end
+
+ def is_bool(name)
+ BOOL_KEYS.include?(name.to_s) || BOOL_KEYS.include?(parent_setting_for(name.to_s))
+ end
+
+ def to_bool(value)
+ case value
+ when nil, /\A(false|f|no|n|0|)\z/i, false
+ false
+ else
+ true
+ end
+ end
+
+ def is_num(key)
+ NUMBER_KEYS.include?(key.to_s)
+ end
+
+ def is_array(key)
+ ARRAY_KEYS.include?(key.to_s)
+ end
+
+ def to_array(value)
+ return [] unless value
+ value.split(":").map(&:to_sym)
+ end
+
+ def array_to_s(array)
+ array = Array(array)
+ return nil if array.empty?
+ array.join(":").tr(" ", ":")
+ end
+
+ def set_key(raw_key, value, hash, file)
+ raw_key = raw_key.to_s
+ value = array_to_s(value) if is_array(raw_key)
+
+ key = key_for(raw_key)
+
+ return if hash[key] == value
+
+ hash[key] = value
+ hash.delete(key) if value.nil?
+
+ Validator.validate!(raw_key, converted_value(value, raw_key), hash)
+
+ return unless file
+ SharedHelpers.filesystem_access(file) do |p|
+ FileUtils.mkdir_p(p.dirname)
+ require "bundler/yaml_serializer"
+ p.open("w") {|f| f.write(YAMLSerializer.dump(hash)) }
+ end
+ end
+
+ def converted_value(value, key)
+ if is_array(key)
+ to_array(value)
+ elsif value.nil?
+ nil
+ elsif is_bool(key) || value == "false"
+ to_bool(value)
+ elsif is_num(key)
+ value.to_i
+ else
+ value.to_s
+ end
+ end
+
+ def global_config_file
+ if ENV["BUNDLE_CONFIG"] && !ENV["BUNDLE_CONFIG"].empty?
+ Pathname.new(ENV["BUNDLE_CONFIG"])
+ else
+ begin
+ Bundler.user_bundle_path("config")
+ rescue PermissionError, GenericSystemCallError
+ nil
+ end
+ end
+ end
+
+ def local_config_file
+ Pathname.new(@root).join("config") if @root
+ end
+
+ CONFIG_REGEX = %r{ # rubocop:disable Style/RegexpLiteral
+ ^
+ (BUNDLE_.+):\s # the key
+ (?: !\s)? # optional exclamation mark found with ruby 1.9.3
+ (['"]?) # optional opening quote
+ (.* # contents of the value
+ (?: # optionally, up until the next key
+ (\n(?!BUNDLE).+)*
+ )
+ )
+ \2 # matching closing quote
+ $
+ }xo
+
+ def load_config(config_file)
+ return {} if !config_file || ignore_config?
+ SharedHelpers.filesystem_access(config_file, :read) do |file|
+ valid_file = file.exist? && !file.size.zero?
+ return {} unless valid_file
+ require "bundler/yaml_serializer"
+ YAMLSerializer.load file.read
+ end
+ end
+
+ PER_URI_OPTIONS = %w[
+ fallback_timeout
+ ].freeze
+
+ NORMALIZE_URI_OPTIONS_PATTERN =
+ /
+ \A
+ (\w+\.)? # optional prefix key
+ (https?.*?) # URI
+ (\.#{Regexp.union(PER_URI_OPTIONS)})? # optional suffix key
+ \z
+ /ix
+
+ # TODO: duplicates Rubygems#normalize_uri
+ # TODO: is this the correct place to validate mirror URIs?
+ def self.normalize_uri(uri)
+ uri = uri.to_s
+ if uri =~ NORMALIZE_URI_OPTIONS_PATTERN
+ prefix = $1
+ uri = $2
+ suffix = $3
+ end
+ uri = "#{uri}/" unless uri.end_with?("/")
+ uri = URI(uri)
+ unless uri.absolute?
+ raise ArgumentError, format("Gem sources must be absolute. You provided '%s'.", uri)
+ end
+ "#{prefix}#{uri}#{suffix}"
+ end
+ end
+end
diff --git a/lib/bundler/settings/validator.rb b/lib/bundler/settings/validator.rb
new file mode 100644
index 0000000000..0a57ea7f03
--- /dev/null
+++ b/lib/bundler/settings/validator.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Settings
+ class Validator
+ class Rule
+ attr_reader :description
+
+ def initialize(keys, description, &validate)
+ @keys = keys
+ @description = description
+ @validate = validate
+ end
+
+ def validate!(key, value, settings)
+ instance_exec(key, value, settings, &@validate)
+ end
+
+ def fail!(key, value, *reasons)
+ reasons.unshift @description
+ raise InvalidOption, "Setting `#{key}` to #{value.inspect} failed:\n#{reasons.map {|r| " - #{r}" }.join("\n")}"
+ end
+
+ def set(settings, key, value, *reasons)
+ hash_key = k(key)
+ return if settings[hash_key] == value
+ reasons.unshift @description
+ Bundler.ui.info "Setting `#{key}` to #{value.inspect}, since #{reasons.join(", ")}"
+ if value.nil?
+ settings.delete(hash_key)
+ else
+ settings[hash_key] = value
+ end
+ end
+
+ def k(key)
+ Bundler.settings.key_for(key)
+ end
+ end
+
+ def self.rules
+ @rules ||= Hash.new {|h, k| h[k] = [] }
+ end
+ private_class_method :rules
+
+ def self.rule(keys, description, &blk)
+ rule = Rule.new(keys, description, &blk)
+ keys.each {|k| rules[k] << rule }
+ end
+ private_class_method :rule
+
+ def self.validate!(key, value, settings)
+ rules_to_validate = rules[key]
+ rules_to_validate.each {|rule| rule.validate!(key, value, settings) }
+ end
+
+ rule %w[path path.system], "path and path.system are mutually exclusive" do |key, value, settings|
+ if key == "path" && value
+ set(settings, "path.system", nil)
+ elsif key == "path.system" && value
+ set(settings, :path, nil)
+ end
+ end
+
+ rule %w[with without], "a group cannot be in both `with` & `without` simultaneously" do |key, value, settings|
+ with = settings.fetch(k(:with), "").split(":").map(&:to_sym)
+ without = settings.fetch(k(:without), "").split(":").map(&:to_sym)
+
+ other_key = key == "with" ? :without : :with
+ other_setting = key == "with" ? without : with
+
+ conflicting = with & without
+ if conflicting.any?
+ fail!(key, value, "`#{other_key}` is current set to #{other_setting.inspect}", "the `#{conflicting.join("`, `")}` groups conflict")
+ end
+ end
+
+ rule %w[path], "relative paths are expanded relative to the current working directory" do |key, value, settings|
+ next if value.nil?
+
+ path = Pathname.new(value)
+ next if !path.relative? || !Bundler.feature_flag.path_relative_to_cwd?
+
+ path = path.expand_path
+
+ root = begin
+ Bundler.root
+ rescue GemfileNotFound
+ Pathname.pwd.expand_path
+ end
+
+ path = begin
+ path.relative_path_from(root)
+ rescue ArgumentError
+ path
+ end
+
+ set(settings, key, path.to_s)
+ end
+ end
+ end
+end
diff --git a/lib/bundler/setup.rb b/lib/bundler/setup.rb
new file mode 100644
index 0000000000..ac6a5bf861
--- /dev/null
+++ b/lib/bundler/setup.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require "bundler/shared_helpers"
+
+if Bundler::SharedHelpers.in_bundle?
+ require "bundler"
+
+ if STDOUT.tty? || ENV["BUNDLER_FORCE_TTY"]
+ begin
+ Bundler.setup
+ rescue Bundler::BundlerError => e
+ puts "\e[31m#{e.message}\e[0m"
+ puts e.backtrace.join("\n") if ENV["DEBUG"]
+ if e.is_a?(Bundler::GemNotFound)
+ puts "\e[33mRun `bundle install` to install missing gems.\e[0m"
+ end
+ exit e.status_code
+ end
+ else
+ Bundler.setup
+ end
+
+ # Add bundler to the load path after disabling system gems
+ bundler_lib = File.expand_path("../..", __FILE__)
+ $LOAD_PATH.unshift(bundler_lib) unless $LOAD_PATH.include?(bundler_lib)
+
+ Bundler.ui = nil
+end
diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb
new file mode 100644
index 0000000000..a4c09e36eb
--- /dev/null
+++ b/lib/bundler/shared_helpers.rb
@@ -0,0 +1,384 @@
+# frozen_string_literal: true
+
+require "bundler/compatibility_guard"
+
+require "pathname"
+require "rubygems"
+
+require "bundler/version"
+require "bundler/constants"
+require "bundler/rubygems_integration"
+require "bundler/current_ruby"
+
+module Gem
+ class Dependency
+ # This is only needed for RubyGems < 1.4
+ unless method_defined? :requirement
+ def requirement
+ version_requirements
+ end
+ end
+ end
+end
+
+module Bundler
+ module SharedHelpers
+ def root
+ gemfile = find_gemfile
+ raise GemfileNotFound, "Could not locate Gemfile" unless gemfile
+ Pathname.new(gemfile).untaint.expand_path.parent
+ end
+
+ def default_gemfile
+ gemfile = find_gemfile(:order_matters)
+ raise GemfileNotFound, "Could not locate Gemfile" unless gemfile
+ Pathname.new(gemfile).untaint.expand_path
+ end
+
+ def default_lockfile
+ gemfile = default_gemfile
+
+ case gemfile.basename.to_s
+ when "gems.rb" then Pathname.new(gemfile.sub(/.rb$/, ".locked"))
+ else Pathname.new("#{gemfile}.lock")
+ end.untaint
+ end
+
+ def default_bundle_dir
+ bundle_dir = find_directory(".bundle")
+ return nil unless bundle_dir
+
+ bundle_dir = Pathname.new(bundle_dir)
+
+ global_bundle_dir = Bundler.user_home.join(".bundle")
+ return nil if bundle_dir == global_bundle_dir
+
+ bundle_dir
+ end
+
+ def in_bundle?
+ find_gemfile
+ end
+
+ def chdir(dir, &blk)
+ Bundler.rubygems.ext_lock.synchronize do
+ Dir.chdir dir, &blk
+ end
+ end
+
+ def pwd
+ Bundler.rubygems.ext_lock.synchronize do
+ Pathname.pwd
+ end
+ end
+
+ def with_clean_git_env(&block)
+ keys = %w[GIT_DIR GIT_WORK_TREE]
+ old_env = keys.inject({}) do |h, k|
+ h.update(k => ENV[k])
+ end
+
+ keys.each {|key| ENV.delete(key) }
+
+ block.call
+ ensure
+ keys.each {|key| ENV[key] = old_env[key] }
+ end
+
+ def set_bundle_environment
+ set_bundle_variables
+ set_path
+ set_rubyopt
+ set_rubylib
+ end
+
+ # Rescues permissions errors raised by file system operations
+ # (ie. Errno:EACCESS, Errno::EAGAIN) and raises more friendly errors instead.
+ #
+ # @param path [String] the path that the action will be attempted to
+ # @param action [Symbol, #to_s] the type of operation that will be
+ # performed. For example: :write, :read, :exec
+ #
+ # @yield path
+ #
+ # @raise [Bundler::PermissionError] if Errno:EACCES is raised in the
+ # given block
+ # @raise [Bundler::TemporaryResourceError] if Errno:EAGAIN is raised in the
+ # given block
+ #
+ # @example
+ # filesystem_access("vendor/cache", :write) do
+ # FileUtils.mkdir_p("vendor/cache")
+ # end
+ #
+ # @see {Bundler::PermissionError}
+ def filesystem_access(path, action = :write, &block)
+ # Use block.call instead of yield because of a bug in Ruby 2.2.2
+ # See https://github.com/bundler/bundler/issues/5341 for details
+ block.call(path.dup.untaint)
+ rescue Errno::EACCES
+ raise PermissionError.new(path, action)
+ rescue Errno::EAGAIN
+ raise TemporaryResourceError.new(path, action)
+ rescue Errno::EPROTO
+ raise VirtualProtocolError.new
+ rescue Errno::ENOSPC
+ raise NoSpaceOnDeviceError.new(path, action)
+ rescue *[const_get_safely(:ENOTSUP, Errno)].compact
+ raise OperationNotSupportedError.new(path, action)
+ rescue Errno::EEXIST, Errno::ENOENT
+ raise
+ rescue SystemCallError => e
+ raise GenericSystemCallError.new(e, "There was an error accessing `#{path}`.")
+ end
+
+ def const_get_safely(constant_name, namespace)
+ const_in_namespace = namespace.constants.include?(constant_name.to_s) ||
+ namespace.constants.include?(constant_name.to_sym)
+ return nil unless const_in_namespace
+ namespace.const_get(constant_name)
+ end
+
+ def major_deprecation(major_version, message)
+ if Bundler.bundler_major_version >= major_version
+ require "bundler/errors"
+ raise DeprecatedError, "[REMOVED FROM #{major_version}.0] #{message}"
+ end
+
+ return unless prints_major_deprecations?
+ @major_deprecation_ui ||= Bundler::UI::Shell.new("no-color" => true)
+ ui = Bundler.ui.is_a?(@major_deprecation_ui.class) ? Bundler.ui : @major_deprecation_ui
+ ui.warn("[DEPRECATED FOR #{major_version}.0] #{message}")
+ end
+
+ def print_major_deprecations!
+ multiple_gemfiles = search_up(".") do |dir|
+ gemfiles = gemfile_names.select {|gf| File.file? File.expand_path(gf, dir) }
+ next if gemfiles.empty?
+ break false if gemfiles.size == 1
+ end
+ if multiple_gemfiles && Bundler.bundler_major_version == 1
+ Bundler::SharedHelpers.major_deprecation 2, \
+ "gems.rb and gems.locked will be preferred to Gemfile and Gemfile.lock."
+ end
+
+ if RUBY_VERSION < "2"
+ major_deprecation(2, "Bundler will only support ruby >= 2.0, you are running #{RUBY_VERSION}")
+ end
+ return if Bundler.rubygems.provides?(">= 2")
+ major_deprecation(2, "Bundler will only support rubygems >= 2.0, you are running #{Bundler.rubygems.version}")
+ end
+
+ def trap(signal, override = false, &block)
+ prior = Signal.trap(signal) do
+ block.call
+ prior.call unless override
+ end
+ end
+
+ def ensure_same_dependencies(spec, old_deps, new_deps)
+ new_deps = new_deps.reject {|d| d.type == :development }
+ old_deps = old_deps.reject {|d| d.type == :development }
+
+ without_type = proc {|d| Gem::Dependency.new(d.name, d.requirements_list.sort) }
+ new_deps.map!(&without_type)
+ old_deps.map!(&without_type)
+
+ extra_deps = new_deps - old_deps
+ return if extra_deps.empty?
+
+ Bundler.ui.debug "#{spec.full_name} from #{spec.remote} has either corrupted API or lockfile dependencies" \
+ " (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."
+ end
+
+ def pretty_dependency(dep, print_source = false)
+ msg = String.new(dep.name)
+ msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default
+
+ if dep.is_a?(Bundler::Dependency)
+ platform_string = dep.platforms.join(", ")
+ 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
+
+ def md5_available?
+ return @md5_available if defined?(@md5_available)
+ @md5_available = begin
+ require "openssl"
+ OpenSSL::Digest::MD5.digest("")
+ true
+ rescue LoadError
+ true
+ rescue OpenSSL::Digest::DigestError
+ false
+ end
+ end
+
+ def digest(name)
+ require "digest"
+ Digest(name)
+ end
+
+ def write_to_gemfile(gemfile_path, contents)
+ filesystem_access(gemfile_path) {|g| File.open(g, "w") {|file| file.puts contents } }
+ end
+
+ private
+
+ def validate_bundle_path
+ path_separator = Bundler.rubygems.path_separator
+ return unless Bundler.bundle_path.to_s.split(path_separator).size > 1
+ message = "Your bundle path contains text matching #{path_separator.inspect}, " \
+ "which is the path separator for your system. Bundler cannot " \
+ "function correctly when the Bundle path contains the " \
+ "system's PATH separator. Please change your " \
+ "bundle path to not match #{path_separator.inspect}." \
+ "\nYour current bundle path is '#{Bundler.bundle_path}'."
+ raise Bundler::PathError, message
+ end
+
+ def find_gemfile(order_matters = false)
+ given = ENV["BUNDLE_GEMFILE"]
+ return given if given && !given.empty?
+ names = gemfile_names
+ names.reverse! if order_matters && Bundler.feature_flag.prefer_gems_rb?
+ find_file(*names)
+ end
+
+ def gemfile_names
+ ["Gemfile", "gems.rb"]
+ end
+
+ def find_file(*names)
+ search_up(*names) do |filename|
+ return filename if File.file?(filename)
+ end
+ end
+
+ def find_directory(*names)
+ search_up(*names) do |dirname|
+ return dirname if File.directory?(dirname)
+ end
+ end
+
+ def search_up(*names)
+ previous = nil
+ current = File.expand_path(SharedHelpers.pwd).untaint
+
+ until !File.directory?(current) || current == previous
+ if ENV["BUNDLE_SPEC_RUN"]
+ # avoid stepping above the tmp directory when testing
+ gemspec = if ENV["BUNDLE_RUBY"] && ENV["BUNDLE_GEM"]
+ # for Ruby Core
+ "lib/bundler.gemspec"
+ else
+ "bundler.gemspec"
+ end
+
+ # avoid stepping above the tmp directory when testing
+ return nil if File.file?(File.join(current, gemspec))
+ end
+
+ names.each do |name|
+ filename = File.join(current, name)
+ yield filename
+ end
+ previous = current
+ current = File.expand_path("..", current)
+ end
+ end
+
+ def set_env(key, value)
+ raise ArgumentError, "new key #{key}" unless EnvironmentPreserver::BUNDLER_KEYS.include?(key)
+ orig_key = "#{EnvironmentPreserver::BUNDLER_PREFIX}#{key}"
+ orig = ENV[key]
+ orig ||= EnvironmentPreserver::INTENTIONALLY_NIL
+ ENV[orig_key] ||= orig
+
+ ENV[key] = value
+ end
+ public :set_env
+
+ def set_bundle_variables
+ begin
+ exe_file = Bundler.rubygems.bin_path("bundler", "bundle", VERSION)
+ unless File.exist?(exe_file)
+ exe_file = File.expand_path("../../../exe/bundle", __FILE__)
+ end
+ Bundler::SharedHelpers.set_env "BUNDLE_BIN_PATH", exe_file
+ rescue Gem::GemNotFoundException
+ exe_file = File.expand_path("../../../exe/bundle", __FILE__)
+ # for Ruby core repository
+ exe_file = File.expand_path("../../../../bin/bundle", __FILE__) unless File.exist?(exe_file)
+ Bundler::SharedHelpers.set_env "BUNDLE_BIN_PATH", exe_file
+ end
+
+ # Set BUNDLE_GEMFILE
+ Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", find_gemfile(:order_matters).to_s
+ Bundler::SharedHelpers.set_env "BUNDLER_VERSION", Bundler::VERSION
+ end
+
+ def set_path
+ validate_bundle_path
+ paths = (ENV["PATH"] || "").split(File::PATH_SEPARATOR)
+ paths.unshift "#{Bundler.bundle_path}/bin"
+ Bundler::SharedHelpers.set_env "PATH", paths.uniq.join(File::PATH_SEPARATOR)
+ end
+
+ def set_rubyopt
+ rubyopt = [ENV["RUBYOPT"]].compact
+ return if !rubyopt.empty? && rubyopt.first =~ %r{-rbundler/setup}
+ rubyopt.unshift %(-rbundler/setup)
+ Bundler::SharedHelpers.set_env "RUBYOPT", rubyopt.join(" ")
+ end
+
+ def set_rubylib
+ rubylib = (ENV["RUBYLIB"] || "").split(File::PATH_SEPARATOR)
+ rubylib.unshift bundler_ruby_lib unless RbConfig::CONFIG["rubylibdir"] == bundler_ruby_lib
+ Bundler::SharedHelpers.set_env "RUBYLIB", rubylib.uniq.join(File::PATH_SEPARATOR)
+ end
+
+ def bundler_ruby_lib
+ resolve_path File.expand_path("../..", __FILE__)
+ end
+
+ def clean_load_path
+ # handle 1.9 where system gems are always on the load path
+ return unless defined?(::Gem)
+
+ bundler_lib = bundler_ruby_lib
+
+ loaded_gem_paths = Bundler.rubygems.loaded_gem_paths
+
+ $LOAD_PATH.reject! do |p|
+ next if resolve_path(p).start_with?(bundler_lib)
+ loaded_gem_paths.delete(p)
+ end
+ $LOAD_PATH.uniq!
+ end
+
+ def resolve_path(path)
+ expanded = File.expand_path(path)
+ return expanded unless File.respond_to?(:realpath) && File.exist?(expanded)
+
+ File.realpath(expanded)
+ end
+
+ def prints_major_deprecations?
+ require "bundler"
+ deprecation_release = Bundler::VERSION.split(".").drop(1).include?("99")
+ return false if !deprecation_release && !Bundler.settings[:major_deprecations]
+ require "bundler/deprecate"
+ return false if Bundler::Deprecate.skip
+ true
+ end
+
+ extend self
+ end
+end
diff --git a/lib/bundler/similarity_detector.rb b/lib/bundler/similarity_detector.rb
new file mode 100644
index 0000000000..b7f3ee7afa
--- /dev/null
+++ b/lib/bundler/similarity_detector.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module Bundler
+ class SimilarityDetector
+ SimilarityScore = Struct.new(:string, :distance)
+
+ # initialize with an array of words to be matched against
+ def initialize(corpus)
+ @corpus = corpus
+ end
+
+ # return an array of words similar to 'word' from the corpus
+ def similar_words(word, limit = 3)
+ words_by_similarity = @corpus.map {|w| SimilarityScore.new(w, levenshtein_distance(word, w)) }
+ words_by_similarity.select {|s| s.distance <= limit }.sort_by(&:distance).map(&:string)
+ end
+
+ # return the result of 'similar_words', concatenated into a list
+ # (eg "a, b, or c")
+ def similar_word_list(word, limit = 3)
+ words = similar_words(word, limit)
+ if words.length == 1
+ words[0]
+ elsif words.length > 1
+ [words[0..-2].join(", "), words[-1]].join(" or ")
+ end
+ end
+
+ protected
+
+ # http://www.informit.com/articles/article.aspx?p=683059&seqNum=36
+ def levenshtein_distance(this, that, ins = 2, del = 2, sub = 1)
+ # ins, del, sub are weighted costs
+ return nil if this.nil?
+ return nil if that.nil?
+ dm = [] # distance matrix
+
+ # Initialize first row values
+ dm[0] = (0..this.length).collect {|i| i * ins }
+ fill = [0] * (this.length - 1)
+
+ # Initialize first column values
+ (1..that.length).each do |i|
+ dm[i] = [i * del, fill.flatten]
+ end
+
+ # populate matrix
+ (1..that.length).each do |i|
+ (1..this.length).each do |j|
+ # critical comparison
+ dm[i][j] = [
+ dm[i - 1][j - 1] + (this[j - 1] == that[i - 1] ? 0 : sub),
+ dm[i][j - 1] + ins,
+ dm[i - 1][j] + del
+ ].min
+ end
+ end
+
+ # The last value in matrix is the Levenshtein distance between the strings
+ dm[that.length][this.length]
+ end
+ end
+end
diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb
new file mode 100644
index 0000000000..26a3625bb1
--- /dev/null
+++ b/lib/bundler/source.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Source
+ autoload :Gemspec, "bundler/source/gemspec"
+ autoload :Git, "bundler/source/git"
+ autoload :Metadata, "bundler/source/metadata"
+ autoload :Path, "bundler/source/path"
+ autoload :Rubygems, "bundler/source/rubygems"
+
+ attr_accessor :dependency_names
+
+ def unmet_deps
+ specs.unmet_dependency_names
+ end
+
+ def version_message(spec)
+ message = "#{spec.name} #{spec.version}"
+ message += " (#{spec.platform})" if spec.platform != Gem::Platform::RUBY && !spec.platform.nil?
+
+ if Bundler.locked_gems
+ locked_spec = Bundler.locked_gems.specs.find {|s| s.name == spec.name }
+ locked_spec_version = locked_spec.version if locked_spec
+ if locked_spec_version && spec.version != locked_spec_version
+ message += Bundler.ui.add_color(" (was #{locked_spec_version})", version_color(spec.version, locked_spec_version))
+ end
+ end
+
+ message
+ end
+
+ def can_lock?(spec)
+ spec.source == self
+ end
+
+ # it's possible that gems from one source depend on gems from some
+ # other source, so now we download gemspecs and iterate over those
+ # dependencies, looking for gems we don't have info on yet.
+ def double_check_for(*); end
+
+ def dependency_names_to_double_check
+ specs.dependency_names
+ end
+
+ def include?(other)
+ other == self
+ end
+
+ def inspect
+ "#<#{self.class}:0x#{object_id} #{self}>"
+ end
+
+ def path?
+ instance_of?(Bundler::Source::Path)
+ end
+
+ def extension_cache_path(spec)
+ return unless Bundler.feature_flag.global_gem_cache?
+ return unless source_slug = extension_cache_slug(spec)
+ Bundler.user_cache.join(
+ "extensions", Gem::Platform.local.to_s, Bundler.ruby_scope,
+ source_slug, spec.full_name
+ )
+ end
+
+ private
+
+ def version_color(spec_version, locked_spec_version)
+ if Gem::Version.correct?(spec_version) && Gem::Version.correct?(locked_spec_version)
+ # display yellow if there appears to be a regression
+ earlier_version?(spec_version, locked_spec_version) ? :yellow : :green
+ else
+ # default to green if the versions cannot be directly compared
+ :green
+ end
+ end
+
+ def earlier_version?(spec_version, locked_spec_version)
+ Gem::Version.new(spec_version) < Gem::Version.new(locked_spec_version)
+ end
+
+ def print_using_message(message)
+ if !message.include?("(was ") && Bundler.feature_flag.suppress_install_using_messages?
+ Bundler.ui.debug message
+ else
+ Bundler.ui.info message
+ end
+ end
+
+ def extension_cache_slug(_)
+ nil
+ end
+ end
+end
diff --git a/lib/bundler/source/gemspec.rb b/lib/bundler/source/gemspec.rb
new file mode 100644
index 0000000000..7e3447e776
--- /dev/null
+++ b/lib/bundler/source/gemspec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Source
+ class Gemspec < Path
+ attr_reader :gemspec
+
+ def initialize(options)
+ super
+ @gemspec = options["gemspec"]
+ end
+
+ def as_path_source
+ Path.new(options)
+ end
+ end
+ end
+end
diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb
new file mode 100644
index 0000000000..0b00608bdd
--- /dev/null
+++ b/lib/bundler/source/git.rb
@@ -0,0 +1,329 @@
+# frozen_string_literal: true
+
+require "bundler/vendored_fileutils"
+require "uri"
+
+module Bundler
+ class Source
+ class Git < Path
+ autoload :GitProxy, "bundler/source/git/git_proxy"
+
+ attr_reader :uri, :ref, :branch, :options, :submodules
+
+ def initialize(options)
+ @options = options
+ @glob = options["glob"] || DEFAULT_GLOB
+
+ @allow_cached = false
+ @allow_remote = false
+
+ # 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"] || ""
+ @safe_uri = URICredentialsFilter.credential_filtered_uri(@uri)
+ @branch = options["branch"]
+ @ref = options["ref"] || options["branch"] || options["tag"] || "master"
+ @submodules = options["submodules"]
+ @name = options["name"]
+ @version = options["version"].to_s.strip.gsub("-", ".pre.")
+
+ @copied = false
+ @local = false
+ end
+
+ def self.from_lock(options)
+ new(options.merge("uri" => options.delete("remote")))
+ end
+
+ def to_lock
+ out = String.new("GIT\n")
+ out << " remote: #{@uri}\n"
+ out << " revision: #{revision}\n"
+ %w[ref branch tag submodules].each do |opt|
+ out << " #{opt}: #{options[opt]}\n" if options[opt]
+ end
+ out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
+ out << " specs:\n"
+ end
+
+ def hash
+ [self.class, uri, ref, branch, name, version, submodules].hash
+ end
+
+ def eql?(other)
+ other.is_a?(Git) && uri == other.uri && ref == other.ref &&
+ branch == other.branch && name == other.name &&
+ version == other.version && submodules == other.submodules
+ end
+
+ alias_method :==, :eql?
+
+ def to_s
+ 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
+ else
+ ref
+ end
+
+ rev = begin
+ "@#{shortref_for_display(revision)}"
+ rescue GitError
+ nil
+ end
+
+ "#{@safe_uri} (at #{at}#{rev})"
+ end
+
+ def name
+ File.basename(@uri, ".git")
+ end
+
+ # This is the path which is going to contain a specific
+ # checkout of the git repository. When using local git
+ # repos, this is set to the local repo.
+ def install_path
+ @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
+ end
+ end
+
+ alias_method :path, :install_path
+
+ def extension_dir_name
+ "#{base_name}-#{shortref_for_path(revision)}"
+ end
+
+ def unlock!
+ git_proxy.revision = nil
+ options["revision"] = nil
+
+ @unlocked = true
+ end
+
+ def local_override!(path)
+ return false if local?
+
+ path = Pathname.new(path)
+ path = path.expand_path(Bundler.root) unless path.relative?
+
+ unless options["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 use " \
+ "`bundle config --delete` to remove the local override"
+ end
+
+ unless path.exist?
+ raise GitError, "Cannot use local override for #{name} because #{path} " \
+ "does not exist. Check `bundle config --delete` to remove the local override"
+ end
+
+ set_local!(path)
+
+ # 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)
+
+ if git_proxy.branch != options["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"]}"
+ end
+
+ changed = cached_revision && cached_revision != git_proxy.revision
+
+ if changed && !@unlocked && !git_proxy.contains?(cached_revision)
+ raise GitError, "The Gemfile lock is pointing to revision #{shortref_for_display(cached_revision)} " \
+ "but the current branch in your local override for #{name} does not contain such commit. " \
+ "Please make sure your branch is up to date."
+ end
+
+ changed
+ end
+
+ def specs(*)
+ set_local!(app_cache_path) if has_app_cache? && !local?
+
+ if requires_checkout? && !@copied
+ fetch
+ git_proxy.copy_to(install_path, submodules)
+ serialize_gemspecs_in(install_path)
+ @copied = true
+ end
+
+ local_specs
+ end
+
+ def install(spec, options = {})
+ force = options[:force]
+
+ print_using_message "Using #{version_message(spec)} from #{self}"
+
+ if (requires_checkout? && !@copied) || force
+ Bundler.ui.debug " * Checking out revision: #{ref}"
+ git_proxy.copy_to(install_path, submodules)
+ serialize_gemspecs_in(install_path)
+ @copied = true
+ end
+
+ generate_bin_options = { :disable_extensions => !Bundler.rubygems.spec_missing_extensions?(spec), :build_args => options[:build_args] }
+ generate_bin(spec, generate_bin_options)
+
+ requires_checkout? ? spec.post_install_message : nil
+ end
+
+ def cache(spec, custom_path = nil)
+ app_cache_path = app_cache_path(custom_path)
+ return unless Bundler.feature_flag.cache_all?
+ return if path == app_cache_path
+ cached!
+ FileUtils.rm_rf(app_cache_path)
+ git_proxy.checkout if requires_checkout?
+ git_proxy.copy_to(app_cache_path, @submodules)
+ serialize_gemspecs_in(app_cache_path)
+ end
+
+ def load_spec_files
+ super
+ rescue PathError => e
+ Bundler.ui.trace e
+ raise GitError, "#{self} is not yet checked out. Run `bundle install` first."
+ end
+
+ # This is the path which is going to contain a cache
+ # of the git repository. When using the same git repository
+ # 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 ||= begin
+ if Bundler.requires_sudo? || Bundler.feature_flag.global_gem_cache?
+ Bundler.user_cache
+ else
+ Bundler.bundle_path.join("cache", "bundler")
+ end.join("git", git_scope)
+ end
+ end
+
+ def app_cache_dirname
+ "#{base_name}-#{shortref_for_path(cached_revision || revision)}"
+ end
+
+ def revision
+ git_proxy.revision
+ end
+
+ def allow_git_ops?
+ @allow_remote || @allow_cached
+ end
+
+ private
+
+ def serialize_gemspecs_in(destination)
+ destination = destination.expand_path(Bundler.root) if destination.relative?
+ Dir["#{destination}/#{@glob}"].each do |spec_path|
+ # Evaluate gemspecs and cache the result. Gemspecs
+ # in git might require git or other dependencies.
+ # The gemspecs we cache should already be evaluated.
+ spec = Bundler.load_gemspec(spec_path)
+ next unless spec
+ Bundler.rubygems.set_installed_by_version(spec)
+ Bundler.rubygems.validate(spec)
+ File.open(spec_path, "wb") {|file| file.write(spec.to_ruby) }
+ end
+ end
+
+ def set_local!(path)
+ @local = true
+ @local_specs = @git_proxy = nil
+ @cache_path = @install_path = path
+ end
+
+ def has_app_cache?
+ cached_revision && super
+ end
+
+ def local?
+ @local
+ end
+
+ def requires_checkout?
+ allow_git_ops? && !local?
+ end
+
+ def base_name
+ File.basename(uri.sub(%r{^(\w+://)?([^/:]+:)?(//\w*/)?(\w*/)*}, ""), ".git")
+ end
+
+ def shortref_for_display(ref)
+ ref[0..6]
+ end
+
+ def shortref_for_path(ref)
+ ref[0..11]
+ end
+
+ def uri_hash
+ if uri =~ %r{^\w+://(\w+@)?}
+ # Downcase the domain component of the URI
+ # and strip off a trailing slash, if one is present
+ input = URI.parse(uri).normalize.to_s.sub(%r{/$}, "")
+ else
+ # If there is no URI scheme, assume it is an ssh/git URI
+ input = uri
+ end
+ SharedHelpers.digest(:SHA1).hexdigest(input)
+ end
+
+ def cached_revision
+ options["revision"]
+ end
+
+ def cached?
+ cache_path.exist?
+ end
+
+ def git_proxy
+ @git_proxy ||= GitProxy.new(cache_path, uri, ref, cached_revision, self)
+ end
+
+ def fetch
+ git_proxy.checkout
+ rescue GitError => e
+ raise unless Bundler.feature_flag.allow_offline_install?
+ Bundler.ui.warn "Using cached git data because of network errors:\n#{e}"
+ end
+
+ # no-op, since we validate when re-serializing the gemspec
+ def validate_spec(_spec); end
+
+ if Bundler.rubygems.stubs_provide_full_functionality?
+ def load_gemspec(file)
+ stub = Gem::StubSpecification.gemspec_stub(file, install_path.parent, install_path.parent)
+ stub.full_gem_path = Pathname.new(file).dirname.expand_path(root).to_s.untaint
+ StubSpecification.from_stub(stub)
+ end
+ end
+
+ def git_scope
+ "#{base_name}-#{uri_hash}"
+ end
+
+ def extension_cache_slug(_)
+ extension_dir_name
+ end
+ end
+ end
+end
diff --git a/lib/bundler/source/git/git_proxy.rb b/lib/bundler/source/git/git_proxy.rb
new file mode 100644
index 0000000000..cd964f7e56
--- /dev/null
+++ b/lib/bundler/source/git/git_proxy.rb
@@ -0,0 +1,262 @@
+# frozen_string_literal: true
+
+require "shellwords"
+require "tempfile"
+module Bundler
+ class Source
+ class Git
+ class GitNotInstalledError < GitError
+ def initialize
+ msg = String.new
+ msg << "You need to install git to be able to use gems from git repositories. "
+ msg << "For help installing git, please refer to GitHub's tutorial at https://help.github.com/articles/set-up-git"
+ super msg
+ end
+ end
+
+ class GitNotAllowedError < GitError
+ def initialize(command)
+ msg = String.new
+ msg << "Bundler is trying to run a `git #{command}` at runtime. You probably need to run `bundle install`. However, "
+ msg << "this error message could probably be more useful. Please submit a ticket at http://github.com/bundler/bundler/issues "
+ msg << "with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}"
+ super msg
+ end
+ end
+
+ class GitCommandError < GitError
+ def initialize(command, path = nil, extra_info = nil)
+ msg = String.new
+ msg << "Git error: command `git #{command}` in directory #{SharedHelpers.pwd} has failed."
+ msg << "\n#{extra_info}" if extra_info
+ msg << "\nIf this error persists you could try removing the cache directory '#{path}'" if path && path.exist?
+ super msg
+ end
+ end
+
+ class MissingGitRevisionError < GitError
+ def initialize(ref, repo)
+ msg = "Revision #{ref} does not exist in the repository #{repo}. Maybe you misspelled it?"
+ super msg
+ end
+ end
+
+ # The GitProxy is responsible to interact with git repositories.
+ # All actions required by the Git source is encapsulated in this
+ # object.
+ class GitProxy
+ attr_accessor :path, :uri, :ref
+ attr_writer :revision
+
+ def initialize(path, uri, ref, revision = nil, git = nil)
+ @path = path
+ @uri = uri
+ @ref = ref
+ @revision = revision
+ @git = git
+ raise GitNotInstalledError.new if allow? && !Bundler.git_present?
+ end
+
+ def revision
+ return @revision if @revision
+
+ begin
+ @revision ||= find_local_revision
+ rescue GitCommandError
+ raise MissingGitRevisionError.new(ref, URICredentialsFilter.credential_filtered_uri(uri))
+ end
+
+ @revision
+ end
+
+ def branch
+ @branch ||= allowed_in_path do
+ git("rev-parse --abbrev-ref HEAD").strip
+ end
+ end
+
+ def contains?(commit)
+ allowed_in_path do
+ result = git_null("branch --contains #{commit}")
+ $? == 0 && result =~ /^\* (.*)$/
+ end
+ end
+
+ def version
+ git("--version").match(/(git version\s*)?((\.?\d+)+).*/)[2]
+ end
+
+ def full_version
+ git("--version").sub("git version", "").strip
+ end
+
+ def checkout
+ return if path.exist? && has_revision_cached?
+ extra_ref = "#{Shellwords.shellescape(ref)}:#{Shellwords.shellescape(ref)}" if ref && ref.start_with?("refs/")
+
+ Bundler.ui.info "Fetching #{URICredentialsFilter.credential_filtered_uri(uri)}"
+
+ unless path.exist?
+ SharedHelpers.filesystem_access(path.dirname) do |p|
+ FileUtils.mkdir_p(p)
+ end
+ git_retry %(clone #{uri_escaped_with_configured_credentials} "#{path}" --bare --no-hardlinks --quiet)
+ return unless extra_ref
+ end
+
+ in_path do
+ git_retry %(fetch --force --quiet --tags #{uri_escaped_with_configured_credentials} "refs/heads/*:refs/heads/*" #{extra_ref})
+ end
+ end
+
+ def copy_to(destination, submodules = false)
+ # method 1
+ unless File.exist?(destination.join(".git"))
+ begin
+ SharedHelpers.filesystem_access(destination.dirname) do |p|
+ FileUtils.mkdir_p(p)
+ end
+ SharedHelpers.filesystem_access(destination) do |p|
+ FileUtils.rm_rf(p)
+ end
+ git_retry %(clone --no-checkout --quiet "#{path}" "#{destination}")
+ File.chmod(((File.stat(destination).mode | 0o777) & ~File.umask), destination)
+ rescue Errno::EEXIST => e
+ file_path = e.message[%r{.*?(/.*)}, 1]
+ raise GitError, "Bundler could not install a gem because it needs to " \
+ "create a directory, but a file exists - #{file_path}. Please delete " \
+ "this file and try again."
+ end
+ end
+ # method 2
+ SharedHelpers.chdir(destination) do
+ git_retry %(fetch --force --quiet --tags "#{path}")
+
+ begin
+ git "reset --hard #{@revision}"
+ rescue GitCommandError
+ raise MissingGitRevisionError.new(@revision, URICredentialsFilter.credential_filtered_uri(uri))
+ end
+
+ if submodules
+ git_retry "submodule update --init --recursive"
+ elsif Gem::Version.create(version) >= Gem::Version.create("2.9.0")
+ git_retry "submodule deinit --all --force"
+ end
+ end
+ end
+
+ private
+
+ # TODO: Do not rely on /dev/null.
+ # Given that open3 is not cross platform until Ruby 1.9.3,
+ # the best solution is to pipe to /dev/null if it exists.
+ # If it doesn't, everything will work fine, but the user
+ # will get the $stderr messages as well.
+ def git_null(command)
+ git("#{command} 2>#{Bundler::NULL}", false)
+ end
+
+ def git_retry(command)
+ Bundler::Retry.new("`git #{URICredentialsFilter.credential_filtered_string(command, uri)}`", GitNotAllowedError).attempts do
+ git(command)
+ end
+ end
+
+ def git(command, check_errors = true, error_msg = nil)
+ command_with_no_credentials = URICredentialsFilter.credential_filtered_string(command, uri)
+ raise GitNotAllowedError.new(command_with_no_credentials) unless allow?
+
+ out = SharedHelpers.with_clean_git_env do
+ capture_and_filter_stderr(uri) { `git #{command}` }
+ end
+
+ stdout_with_no_credentials = URICredentialsFilter.credential_filtered_string(out, uri)
+ raise GitCommandError.new(command_with_no_credentials, path, error_msg) if check_errors && !$?.success?
+ stdout_with_no_credentials
+ end
+
+ def has_revision_cached?
+ return unless @revision
+ in_path { git("cat-file -e #{@revision}") }
+ true
+ rescue GitError
+ false
+ end
+
+ def remove_cache
+ FileUtils.rm_rf(path)
+ end
+
+ def find_local_revision
+ allowed_in_path do
+ git("rev-parse --verify #{Shellwords.shellescape(ref)}", true).strip
+ end
+ end
+
+ # Escape the URI for git commands
+ def uri_escaped_with_configured_credentials
+ remote = configured_uri_for(uri)
+ if Bundler::WINDOWS
+ # Windows quoting requires double quotes only, with double quotes
+ # inside the string escaped by being doubled.
+ '"' + remote.gsub('"') { '""' } + '"'
+ else
+ # Bash requires single quoted strings, with the single quotes escaped
+ # by ending the string, escaping the quote, and restarting the string.
+ "'" + remote.gsub("'") { "'\\''" } + "'"
+ end
+ end
+
+ # Adds credentials to the URI as Fetcher#configured_uri_for does
+ def configured_uri_for(uri)
+ if /https?:/ =~ uri
+ remote = URI(uri)
+ config_auth = Bundler.settings[remote.to_s] || Bundler.settings[remote.host]
+ remote.userinfo ||= config_auth
+ remote.to_s
+ else
+ uri
+ end
+ end
+
+ def allow?
+ @git ? @git.allow_git_ops? : true
+ end
+
+ def in_path(&blk)
+ checkout unless path.exist?
+ _ = URICredentialsFilter # load it before we chdir
+ SharedHelpers.chdir(path, &blk)
+ end
+
+ def allowed_in_path
+ return in_path { yield } if allow?
+ raise GitError, "The git source #{uri} is not yet checked out. Please run `bundle install` before trying to start your application"
+ end
+
+ # TODO: Replace this with Open3 when upgrading to bundler 2
+ # Similar to #git_null, as Open3 is not cross-platform,
+ # a temporary way is to use Tempfile to capture the stderr.
+ # When replacing this using Open3, make sure git_null is
+ # also replaced by Open3, so stdout and stderr all got handled properly.
+ def capture_and_filter_stderr(uri)
+ return_value, captured_err = ""
+ backup_stderr = STDERR.dup
+ begin
+ Tempfile.open("captured_stderr") do |f|
+ STDERR.reopen(f)
+ return_value = yield
+ f.rewind
+ captured_err = f.read
+ end
+ ensure
+ STDERR.reopen backup_stderr
+ end
+ $stderr.puts URICredentialsFilter.credential_filtered_string(captured_err, uri) if uri && !captured_err.empty?
+ return_value
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb
new file mode 100644
index 0000000000..9c5657eef6
--- /dev/null
+++ b/lib/bundler/source/metadata.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Source
+ class Metadata < Source
+ def specs
+ @specs ||= Index.build do |idx|
+ idx << Gem::Specification.new("ruby\0", RubyVersion.system.to_gem_version_with_patchlevel)
+ idx << Gem::Specification.new("rubygems\0", Gem::VERSION)
+
+ idx << Gem::Specification.new do |s|
+ s.name = "bundler"
+ s.version = VERSION
+ s.platform = Gem::Platform::RUBY
+ s.source = self
+ s.authors = ["bundler team"]
+ s.bindir = "exe"
+ s.executables = %w[bundle]
+ # can't point to the actual gemspec or else the require paths will be wrong
+ s.loaded_from = File.expand_path("..", __FILE__)
+ end
+ if loaded_spec = Bundler.rubygems.loaded_specs("bundler")
+ idx << loaded_spec # this has to come after the fake gemspec, to override it
+ elsif local_spec = Bundler.rubygems.find_name("bundler").find {|s| s.version.to_s == VERSION }
+ idx << local_spec
+ end
+
+ idx.each {|s| s.source = self }
+ end
+ end
+
+ def cached!; end
+
+ def remote!; end
+
+ def options
+ {}
+ end
+
+ def install(spec, _opts = {})
+ print_using_message "Using #{version_message(spec)}"
+ nil
+ end
+
+ def to_s
+ "the local ruby installation"
+ end
+
+ def ==(other)
+ self.class == other.class
+ end
+ alias_method :eql?, :==
+
+ def hash
+ self.class.hash
+ end
+
+ def version_message(spec)
+ "#{spec.name} #{spec.version}"
+ end
+ end
+ end
+end
diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb
new file mode 100644
index 0000000000..ed734bf549
--- /dev/null
+++ b/lib/bundler/source/path.rb
@@ -0,0 +1,249 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Source
+ class Path < Source
+ autoload :Installer, "bundler/source/path/installer"
+
+ attr_reader :path, :options, :root_path, :original_path
+ attr_writer :name
+ attr_accessor :version
+
+ protected :original_path
+
+ DEFAULT_GLOB = "{,*,*/*}.gemspec".freeze
+
+ def initialize(options)
+ @options = options.dup
+ @glob = options["glob"] || DEFAULT_GLOB
+
+ @allow_cached = false
+ @allow_remote = false
+
+ @root_path = options["root_path"] || Bundler.root
+
+ if options["path"]
+ @path = Pathname.new(options["path"])
+ @path = expand(@path) unless @path.relative?
+ end
+
+ @name = options["name"]
+ @version = options["version"]
+
+ # Stores the original path. If at any point we move to the
+ # cached directory, we still have the original path to copy from.
+ @original_path = @path
+ end
+
+ def remote!
+ @local_specs = nil
+ @allow_remote = true
+ end
+
+ def cached!
+ @local_specs = nil
+ @allow_cached = true
+ end
+
+ def self.from_lock(options)
+ new(options.merge("path" => options.delete("remote")))
+ end
+
+ def to_lock
+ out = String.new("PATH\n")
+ out << " remote: #{lockfile_path}\n"
+ out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
+ out << " specs:\n"
+ end
+
+ def to_s
+ "source at `#{@path}`"
+ end
+
+ def hash
+ [self.class, expanded_path, version].hash
+ end
+
+ def eql?(other)
+ return unless other.class == self.class
+ expanded_original_path == other.expanded_original_path &&
+ version == other.version
+ end
+
+ alias_method :==, :eql?
+
+ def name
+ File.basename(expanded_path.to_s)
+ end
+
+ def install(spec, options = {})
+ print_using_message "Using #{version_message(spec)} from #{self}"
+ generate_bin(spec, :disable_extensions => true)
+ nil # no post-install message
+ end
+
+ def cache(spec, custom_path = nil)
+ app_cache_path = app_cache_path(custom_path)
+ return unless Bundler.feature_flag.cache_all?
+ return if expand(@original_path).to_s.index(root_path.to_s + "/") == 0
+
+ unless @original_path.exist?
+ raise GemNotFound, "Can't cache gem #{version_message(spec)} because #{self} is missing!"
+ end
+
+ FileUtils.rm_rf(app_cache_path)
+ FileUtils.cp_r("#{@original_path}/.", app_cache_path)
+ FileUtils.touch(app_cache_path.join(".bundlecache"))
+ end
+
+ def local_specs(*)
+ @local_specs ||= load_spec_files
+ end
+
+ def specs
+ if has_app_cache?
+ @path = app_cache_path
+ @expanded_path = nil # Invalidate
+ end
+ local_specs
+ end
+
+ def app_cache_dirname
+ name
+ end
+
+ def root
+ Bundler.root
+ end
+
+ def expanded_original_path
+ @expanded_original_path ||= expand(original_path)
+ end
+
+ private
+
+ def expanded_path
+ @expanded_path ||= expand(path)
+ end
+
+ def expand(somepath)
+ somepath.expand_path(root_path)
+ rescue ArgumentError => e
+ Bundler.ui.debug(e)
+ raise PathError, "There was an error while trying to use the path " \
+ "`#{somepath}`.\nThe error message was: #{e.message}."
+ end
+
+ def lockfile_path
+ return relative_path(original_path) if original_path.absolute?
+ expand(original_path).relative_path_from(Bundler.root)
+ end
+
+ def app_cache_path(custom_path = nil)
+ @app_cache_path ||= Bundler.app_cache(custom_path).join(app_cache_dirname)
+ end
+
+ def has_app_cache?
+ SharedHelpers.in_bundle? && app_cache_path.exist?
+ end
+
+ def load_gemspec(file)
+ return unless spec = Bundler.load_gemspec(file)
+ Bundler.rubygems.set_installed_by_version(spec)
+ spec
+ end
+
+ def validate_spec(spec)
+ Bundler.rubygems.validate(spec)
+ end
+
+ def load_spec_files
+ index = Index.new
+
+ if File.directory?(expanded_path)
+ # We sort depth-first since `<<` will override the earlier-found specs
+ Dir["#{expanded_path}/#{@glob}"].sort_by {|p| -p.split(File::SEPARATOR).size }.each do |file|
+ next unless spec = load_gemspec(file)
+ spec.source = self
+
+ # Validation causes extension_dir to be calculated, which depends
+ # on #source, so we validate here instead of load_gemspec
+ validate_spec(spec)
+ index << spec
+ end
+
+ if index.empty? && @name && @version
+ index << Gem::Specification.new do |s|
+ s.name = @name
+ s.source = self
+ s.version = Gem::Version.new(@version)
+ s.platform = Gem::Platform::RUBY
+ s.summary = "Fake gemspec for #{@name}"
+ s.relative_loaded_from = "#{@name}.gemspec"
+ s.authors = ["no one"]
+ if expanded_path.join("bin").exist?
+ executables = expanded_path.join("bin").children
+ executables.reject! {|p| File.directory?(p) }
+ s.executables = executables.map {|c| c.basename.to_s }
+ end
+ end
+ end
+ else
+ message = String.new("The path `#{expanded_path}` ")
+ message << if File.exist?(expanded_path)
+ "is not a directory."
+ else
+ "does not exist."
+ end
+ raise PathError, message
+ end
+
+ index
+ end
+
+ def relative_path(path = self.path)
+ if path.to_s.start_with?(root_path.to_s)
+ return path.relative_path_from(root_path)
+ end
+ path
+ end
+
+ def generate_bin(spec, options = {})
+ gem_dir = Pathname.new(spec.full_gem_path)
+
+ # 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)
+ begin
+ Pathname.new(p).relative_path_from(gem_dir).to_s
+ rescue ArgumentError
+ p
+ end
+ end.compact
+
+ installer = Path::Installer.new(
+ spec,
+ :env_shebang => false,
+ :disable_extensions => options[:disable_extensions],
+ :build_args => options[:build_args],
+ :bundler_extension_cache_path => extension_cache_path(spec)
+ )
+ installer.post_install
+ rescue Gem::InvalidSpecificationException => e
+ Bundler.ui.warn "\n#{spec.name} at #{spec.full_gem_path} did not have a valid gemspec.\n" \
+ "This prevents bundler from installing bins or native extensions, but " \
+ "that may not affect its functionality."
+
+ if !spec.extensions.empty? && !spec.email.empty?
+ Bundler.ui.warn "If you need to use this package without installing it from a gem " \
+ "repository, please contact #{spec.email} and ask them " \
+ "to modify their .gemspec so it can work with `gem build`."
+ end
+
+ Bundler.ui.warn "The validation message from RubyGems was:\n #{e.message}"
+ end
+ end
+ end
+end
diff --git a/lib/bundler/source/path/installer.rb b/lib/bundler/source/path/installer.rb
new file mode 100644
index 0000000000..a0357ffa39
--- /dev/null
+++ b/lib/bundler/source/path/installer.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Source
+ class Path
+ class Installer < Bundler::RubyGemsGemInstaller
+ attr_reader :spec
+
+ def initialize(spec, options = {})
+ @options = options
+ @spec = spec
+ @gem_dir = Bundler.rubygems.path(spec.full_gem_path)
+ @wrappers = true
+ @env_shebang = true
+ @format_executable = options[:format_executable] || false
+ @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
+ end
+
+ def post_install
+ SharedHelpers.chdir(@gem_dir) do
+ run_hooks(:pre_install)
+
+ unless @disable_extensions
+ build_extensions
+ run_hooks(:post_build)
+ end
+
+ generate_bin unless spec.executables.nil? || spec.executables.empty?
+
+ run_hooks(:post_install)
+ end
+ 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)
+ Gem.send(hooks_meth).each do |hook|
+ result = hook.call(self)
+ next unless result == false
+ location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
+ message = "#{type} hook#{location} failed for #{spec.full_name}"
+ raise InstallHookError, message
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb
new file mode 100644
index 0000000000..485b388a32
--- /dev/null
+++ b/lib/bundler/source/rubygems.rb
@@ -0,0 +1,539 @@
+# frozen_string_literal: true
+
+require "uri"
+require "rubygems/user_interaction"
+
+module Bundler
+ class Source
+ class Rubygems < Source
+ autoload :Remote, "bundler/source/rubygems/remote"
+
+ # 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
+
+ def initialize(options = {})
+ @options = options
+ @remotes = []
+ @dependency_names = []
+ @allow_remote = false
+ @allow_cached = false
+ @caches = [cache_path, *Bundler.rubygems.gem_cache]
+
+ Array(options["remotes"] || []).reverse_each {|r| add_remote(r) }
+ end
+
+ def remote!
+ @specs = nil
+ @allow_remote = true
+ end
+
+ def cached!
+ @specs = nil
+ @allow_cached = true
+ end
+
+ def hash
+ @remotes.hash
+ end
+
+ def eql?(other)
+ other.is_a?(Rubygems) && other.credless_remotes == credless_remotes
+ end
+
+ alias_method :==, :eql?
+
+ def include?(o)
+ o.is_a?(Rubygems) && (o.credless_remotes - credless_remotes).empty?
+ end
+
+ def can_lock?(spec)
+ return super if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources?
+ spec.source.is_a?(Rubygems)
+ end
+
+ def options
+ { "remotes" => @remotes.map(&:to_s) }
+ end
+
+ def self.from_lock(options)
+ new(options)
+ end
+
+ def to_lock
+ out = String.new("GEM\n")
+ remotes.reverse_each do |remote|
+ out << " remote: #{suppress_configured_credentials remote}\n"
+ end
+ out << " specs:\n"
+ end
+
+ def to_s
+ if remotes.empty?
+ "locally installed gems"
+ else
+ remote_names = remotes.map(&:to_s).join(", ")
+ "rubygems repository #{remote_names} or installed locally"
+ end
+ end
+ alias_method :name, :to_s
+
+ def specs
+ @specs ||= begin
+ # remote_specs usually generates a way larger Index than the other
+ # sources, and large_idx.use small_idx is way faster than
+ # small_idx.use large_idx.
+ idx = @allow_remote ? remote_specs.dup : Index.new
+ idx.use(cached_specs, :override_dupes) if @allow_cached || @allow_remote
+ idx.use(installed_specs, :override_dupes)
+ idx
+ end
+ end
+
+ def install(spec, opts = {})
+ force = opts[:force]
+ ensure_builtin_gems_cached = opts[:ensure_builtin_gems_cached]
+
+ if ensure_builtin_gems_cached && builtin_gem?(spec)
+ if !cached_path(spec)
+ cached_built_in_gem(spec) unless spec.remote
+ force = true
+ else
+ spec.loaded_from = loaded_from(spec)
+ end
+ end
+
+ if installed?(spec) && !force
+ print_using_message "Using #{version_message(spec)}"
+ return nil # no post-install message
+ end
+
+ # Download the gem to get the spec, because some specs that are returned
+ # by rubygems.org are broken and wrong.
+ if spec.remote
+ # Check for this spec from other sources
+ uris = [spec.remote.anonymized_uri]
+ uris += remotes_for_spec(spec).map(&:anonymized_uri)
+ uris.uniq!
+ Installer.ambiguous_gems << [spec.name, *uris] if uris.length > 1
+
+ s = Bundler.rubygems.spec_from_gem(fetch_gem(spec), Bundler.settings["trust-policy"])
+ spec.__swap__(s)
+ end
+
+ unless Bundler.settings[:no_install]
+ message = "Installing #{version_message(spec)}"
+ message += " with native extensions" if spec.extensions.any?
+ Bundler.ui.confirm message
+
+ path = cached_gem(spec)
+ 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")
+
+ installed_spec = nil
+ Bundler.rubygems.preserve_paths do
+ installed_spec = Bundler::RubyGemsGemInstaller.at(
+ path,
+ :install_dir => install_path.to_s,
+ :bin_dir => bin_path.to_s,
+ :ignore_dependencies => true,
+ :wrappers => true,
+ :env_shebang => true,
+ :build_args => opts[:build_args],
+ :bundler_expected_checksum => spec.respond_to?(:checksum) && spec.checksum,
+ :bundler_extension_cache_path => extension_cache_path(spec)
+ ).install
+ end
+ spec.full_gem_path = installed_spec.full_gem_path
+
+ # 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
+ installed_spec.loaded_from = loaded_from(spec)
+ end
+ spec.loaded_from = loaded_from(spec)
+
+ spec.post_install_message
+ ensure
+ Bundler.rm_rf(install_path) if requires_sudo?
+ end
+
+ def cache(spec, custom_path = nil)
+ if builtin_gem?(spec)
+ cached_path = cached_built_in_gem(spec)
+ else
+ cached_path = cached_gem(spec)
+ end
+ raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path
+ return if File.dirname(cached_path) == Bundler.app_cache.to_s
+ Bundler.ui.info " * #{File.basename(cached_path)}"
+ FileUtils.cp(cached_path, Bundler.app_cache(custom_path))
+ rescue Errno::EACCES => e
+ Bundler.ui.debug(e)
+ raise InstallError, e.message
+ end
+
+ def cached_built_in_gem(spec)
+ cached_path = cached_path(spec)
+ if cached_path.nil?
+ remote_spec = remote_specs.search(spec).first
+ if remote_spec
+ cached_path = fetch_gem(remote_spec)
+ else
+ Bundler.ui.warn "#{spec.full_name} is built in to Ruby, and can't be cached because your Gemfile doesn't have any sources that contain it."
+ end
+ end
+ cached_path
+ end
+
+ def add_remote(source)
+ uri = normalize_uri(source)
+ @remotes.unshift(uri) unless @remotes.include?(uri)
+ end
+
+ def equivalent_remotes?(other_remotes)
+ other_remotes.map(&method(:remove_auth)) == @remotes.map(&method(:remove_auth))
+ end
+
+ def replace_remotes(other_remotes, allow_equivalent = false)
+ return false if other_remotes == @remotes
+
+ equivalent = allow_equivalent && equivalent_remotes?(other_remotes)
+
+ @remotes = []
+ other_remotes.reverse_each do |r|
+ add_remote r.to_s
+ end
+
+ !equivalent
+ end
+
+ def unmet_deps
+ if @allow_remote && api_fetchers.any?
+ remote_specs.unmet_dependency_names
+ else
+ []
+ end
+ end
+
+ def fetchers
+ @fetchers ||= remotes.map do |uri|
+ remote = Source::Rubygems::Remote.new(uri)
+ Bundler::Fetcher.new(remote)
+ end
+ end
+
+ def double_check_for(unmet_dependency_names)
+ return unless @allow_remote
+ return unless api_fetchers.any?
+
+ unmet_dependency_names = unmet_dependency_names.call
+ unless unmet_dependency_names.nil?
+ if api_fetchers.size <= 1
+ # can't do this when there are multiple fetchers because then we might not fetch from _all_
+ # of them
+ unmet_dependency_names -= remote_specs.spec_names # avoid re-fetching things we've already gotten
+ end
+ return if unmet_dependency_names.empty?
+ end
+
+ Bundler.ui.debug "Double checking for #{unmet_dependency_names || "all specs (due to the size of the request)"} in #{self}"
+
+ fetch_names(api_fetchers, unmet_dependency_names, specs, false)
+ end
+
+ def dependency_names_to_double_check
+ names = []
+ remote_specs.each do |spec|
+ case spec
+ when EndpointSpecification, Gem::Specification, StubSpecification, LazySpecification
+ names.concat(spec.runtime_dependencies)
+ when RemoteSpecification # from the full index
+ return nil
+ else
+ raise "unhandled spec type (#{spec.inspect})"
+ end
+ end
+ names.map!(&:name) if names
+ names
+ end
+
+ protected
+
+ def credless_remotes
+ remotes.map(&method(:suppress_configured_credentials))
+ end
+
+ def remotes_for_spec(spec)
+ specs.search_all(spec.name).inject([]) do |uris, s|
+ uris << s.remote if s.remote
+ uris
+ end
+ end
+
+ def loaded_from(spec)
+ "#{rubygems_dir}/specifications/#{spec.full_name}.gemspec"
+ end
+
+ def cached_gem(spec)
+ cached_gem = cached_path(spec)
+ unless cached_gem
+ raise Bundler::GemNotFound, "Could not find #{spec.file_name} for installation"
+ end
+ cached_gem
+ end
+
+ def cached_path(spec)
+ possibilities = @caches.map {|p| "#{p}/#{spec.file_name}" }
+ possibilities.find {|p| File.exist?(p) }
+ end
+
+ def normalize_uri(uri)
+ uri = uri.to_s
+ uri = "#{uri}/" unless uri =~ %r{/$}
+ uri = URI(uri)
+ raise ArgumentError, "The source must be an absolute URI. For example:\n" \
+ "source 'https://rubygems.org'" if !uri.absolute? || (uri.is_a?(URI::HTTP) && uri.host.nil?)
+ uri
+ end
+
+ def suppress_configured_credentials(remote)
+ remote_nouser = remove_auth(remote)
+ if remote.userinfo && remote.userinfo == Bundler.settings[remote_nouser]
+ remote_nouser
+ else
+ remote
+ end
+ end
+
+ def remove_auth(remote)
+ if remote.user || remote.password
+ remote.dup.tap {|uri| uri.user = uri.password = nil }.to_s
+ else
+ remote.to_s
+ end
+ end
+
+ def installed_specs
+ @installed_specs ||= Index.build do |idx|
+ Bundler.rubygems.all_specs.reverse_each do |spec|
+ next if spec.name == "bundler"
+ spec.source = self
+ if Bundler.rubygems.spec_missing_extensions?(spec, false)
+ Bundler.ui.debug "Source #{self} is ignoring #{spec} because it is missing extensions"
+ next
+ end
+ idx << spec
+ end
+ end
+ end
+
+ def cached_specs
+ @cached_specs ||= begin
+ idx = installed_specs.dup
+
+ Dir["#{cache_path}/*.gem"].each do |gemfile|
+ next if gemfile =~ /^bundler\-[\d\.]+?\.gem/
+ s ||= Bundler.rubygems.spec_from_gem(gemfile)
+ s.source = self
+ if Bundler.rubygems.spec_missing_extensions?(s, false)
+ Bundler.ui.debug "Source #{self} is ignoring #{s} because it is missing extensions"
+ next
+ end
+ idx << s
+ end
+
+ idx
+ end
+ end
+
+ def api_fetchers
+ fetchers.select {|f| f.use_api && f.fetchers.first.api_fetcher? }
+ end
+
+ def remote_specs
+ @remote_specs ||= Index.build do |idx|
+ index_fetchers = fetchers - api_fetchers
+
+ # 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
+
+ fetch_names(api_fetchers, allow_api && dependency_names, idx, false)
+ end
+ end
+
+ def fetch_names(fetchers, dependency_names, index, override_dupes)
+ fetchers.each do |f|
+ if dependency_names
+ Bundler.ui.info "Fetching gem metadata from #{f.uri}", Bundler.ui.debug?
+ index.use f.specs_with_retry(dependency_names, self), override_dupes
+ Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
+ else
+ Bundler.ui.info "Fetching source index from #{f.uri}"
+ index.use f.specs_with_retry(nil, self), override_dupes
+ end
+ end
+ end
+
+ def fetch_gem(spec)
+ return false unless spec.remote
+
+ spec.fetch_platform
+
+ download_path = requires_sudo? ? Bundler.tmp(spec.full_name) : rubygems_dir
+ gem_path = "#{rubygems_dir}/cache/#{spec.full_name}.gem"
+
+ SharedHelpers.filesystem_access("#{download_path}/cache") do |p|
+ FileUtils.mkdir_p(p)
+ end
+ download_gem(spec, download_path)
+
+ if requires_sudo?
+ SharedHelpers.filesystem_access("#{rubygems_dir}/cache") do |p|
+ Bundler.mkdir_p(p)
+ end
+ Bundler.sudo "mv #{download_path}/cache/#{spec.full_name}.gem #{gem_path}"
+ end
+
+ gem_path
+ ensure
+ Bundler.rm_rf(download_path) if requires_sudo?
+ end
+
+ def builtin_gem?(spec)
+ # Ruby 2.1, where all included gems have this summary
+ return true if spec.summary =~ /is bundled with Ruby/
+
+ # Ruby 2.0, where gemspecs are stored in specifications/default/
+ spec.loaded_from && spec.loaded_from.include?("specifications/default/")
+ end
+
+ def installed?(spec)
+ installed_specs[spec].any?
+ end
+
+ def requires_sudo?
+ Bundler.requires_sudo?
+ end
+
+ def rubygems_dir
+ Bundler.rubygems.gem_dir
+ end
+
+ def cache_path
+ Bundler.app_cache
+ end
+
+ private
+
+ # Checks if the requested spec exists in the global cache. If it does,
+ # we copy it to the download path, and if it does not, we download it.
+ #
+ # @param [Specification] spec
+ # the spec we want to download or retrieve from the cache.
+ #
+ # @param [String] download_path
+ # the local directory the .gem will end up in.
+ #
+ def download_gem(spec, download_path)
+ local_path = File.join(download_path, "cache/#{spec.full_name}.gem")
+
+ if (cache_path = download_cache_path(spec)) && cache_path.file?
+ SharedHelpers.filesystem_access(local_path) do
+ FileUtils.cp(cache_path, local_path)
+ end
+ else
+ uri = spec.remote.uri
+ Bundler.ui.confirm("Fetching #{version_message(spec)}")
+ rubygems_local_path = Bundler.rubygems.download_gem(spec, uri, download_path)
+ if rubygems_local_path != local_path
+ FileUtils.mv(rubygems_local_path, local_path)
+ end
+ cache_globally(spec, local_path)
+ end
+ end
+
+ # Checks if the requested spec exists in the global cache. If it does
+ # not, we create the relevant global cache subdirectory if it does not
+ # exist and copy the spec from the local cache to the global cache.
+ #
+ # @param [Specification] spec
+ # the spec we want to copy to the global cache.
+ #
+ # @param [String] local_cache_path
+ # the local directory from which we want to copy the .gem.
+ #
+ def cache_globally(spec, local_cache_path)
+ return unless cache_path = download_cache_path(spec)
+ return if cache_path.exist?
+
+ SharedHelpers.filesystem_access(cache_path.dirname, &:mkpath)
+ SharedHelpers.filesystem_access(cache_path) do
+ FileUtils.cp(local_cache_path, cache_path)
+ end
+ end
+
+ # Returns the global cache path of the calling Rubygems::Source object.
+ #
+ # Note that the Source determines the path's subdirectory. We use this
+ # subdirectory in the global cache path so that gems with the same name
+ # -- and possibly different versions -- from different sources are saved
+ # to their respective subdirectories and do not override one another.
+ #
+ # @param [Gem::Specification] specification
+ #
+ # @return [Pathname] The global cache path.
+ #
+ def download_cache_path(spec)
+ return unless Bundler.feature_flag.global_gem_cache?
+ return unless remote = spec.remote
+ return unless cache_slug = remote.cache_slug
+
+ Bundler.user_cache.join("gems", cache_slug, spec.file_name)
+ end
+
+ def extension_cache_slug(spec)
+ return unless remote = spec.remote
+ remote.cache_slug
+ end
+ end
+ end
+end
diff --git a/lib/bundler/source/rubygems/remote.rb b/lib/bundler/source/rubygems/remote.rb
new file mode 100644
index 0000000000..b45f33770a
--- /dev/null
+++ b/lib/bundler/source/rubygems/remote.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Source
+ class Rubygems
+ class Remote
+ attr_reader :uri, :anonymized_uri, :original_uri
+
+ def initialize(uri)
+ orig_uri = uri
+ uri = Bundler.settings.mirror_for(uri)
+ @original_uri = orig_uri if orig_uri != uri
+ fallback_auth = Bundler.settings.credentials_for(uri)
+
+ @uri = apply_auth(uri, fallback_auth).freeze
+ @anonymized_uri = remove_auth(@uri).freeze
+ end
+
+ # @return [String] A slug suitable for use as a cache key for this
+ # remote.
+ #
+ def cache_slug
+ @cache_slug ||= begin
+ return nil unless SharedHelpers.md5_available?
+
+ cache_uri = original_uri || uri
+
+ # URI::File of Ruby 2.6 returns empty string when given "file://".
+ host = defined?(URI::File) && cache_uri.is_a?(URI::File) ? nil : cache_uri.host
+
+ uri_parts = [host, cache_uri.user, cache_uri.port, cache_uri.path]
+ uri_digest = SharedHelpers.digest(:MD5).hexdigest(uri_parts.compact.join("."))
+
+ uri_parts[-1] = uri_digest
+ uri_parts.compact.join(".")
+ end
+ end
+
+ def to_s
+ "rubygems remote at #{anonymized_uri}"
+ end
+
+ private
+
+ def apply_auth(uri, auth)
+ if auth && uri.userinfo.nil?
+ uri = uri.dup
+ uri.userinfo = auth
+ end
+
+ uri
+ rescue URI::InvalidComponentError
+ error_message = "Please CGI escape your usernames and passwords before " \
+ "setting them for authentication."
+ raise HTTPError.new(error_message)
+ end
+
+ def remove_auth(uri)
+ if uri.userinfo
+ uri = uri.dup
+ uri.user = uri.password = nil
+ end
+
+ uri
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb
new file mode 100644
index 0000000000..ac2adacb3d
--- /dev/null
+++ b/lib/bundler/source_list.rb
@@ -0,0 +1,186 @@
+# frozen_string_literal: true
+
+module Bundler
+ class SourceList
+ attr_reader :path_sources,
+ :git_sources,
+ :plugin_sources,
+ :global_rubygems_source,
+ :metadata_source
+
+ def initialize
+ @path_sources = []
+ @git_sources = []
+ @plugin_sources = []
+ @global_rubygems_source = nil
+ @rubygems_aggregate = rubygems_aggregate_class.new
+ @rubygems_sources = []
+ @metadata_source = Source::Metadata.new
+ end
+
+ def add_path_source(options = {})
+ if options["gemspec"]
+ add_source_to_list Source::Gemspec.new(options), path_sources
+ else
+ add_source_to_list Source::Path.new(options), path_sources
+ end
+ end
+
+ def add_git_source(options = {})
+ add_source_to_list(Source::Git.new(options), git_sources).tap do |source|
+ warn_on_git_protocol(source)
+ end
+ end
+
+ def add_rubygems_source(options = {})
+ add_source_to_list Source::Rubygems.new(options), @rubygems_sources
+ end
+
+ def add_plugin_source(source, options = {})
+ add_source_to_list Plugin.source(source).new(options), @plugin_sources
+ end
+
+ def global_rubygems_source=(uri)
+ if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources?
+ @global_rubygems_source ||= rubygems_aggregate_class.new("remotes" => uri)
+ end
+ add_rubygems_remote(uri)
+ end
+
+ def add_rubygems_remote(uri)
+ if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources?
+ return if Bundler.feature_flag.disable_multisource?
+ raise InvalidOption, "`lockfile_uses_separate_rubygems_sources` cannot be set without `disable_multisource` being set"
+ end
+ @rubygems_aggregate.add_remote(uri)
+ @rubygems_aggregate
+ end
+
+ def default_source
+ global_rubygems_source || @rubygems_aggregate
+ end
+
+ def rubygems_sources
+ @rubygems_sources + [default_source]
+ end
+
+ def rubygems_remotes
+ rubygems_sources.map(&:remotes).flatten.uniq
+ end
+
+ def all_sources
+ path_sources + git_sources + plugin_sources + rubygems_sources + [metadata_source]
+ end
+
+ def get(source)
+ source_list_for(source).find {|s| equal_source?(source, s) || equivalent_source?(source, s) }
+ end
+
+ def lock_sources
+ if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources?
+ [[default_source], @rubygems_sources, git_sources, path_sources, plugin_sources].map do |sources|
+ sources.sort_by(&:to_s)
+ end.flatten(1)
+ else
+ lock_sources = (path_sources + git_sources + plugin_sources).sort_by(&:to_s)
+ lock_sources << combine_rubygems_sources
+ end
+ end
+
+ # Returns true if there are changes
+ def replace_sources!(replacement_sources)
+ return true if replacement_sources.empty?
+
+ [path_sources, git_sources, plugin_sources].each do |source_list|
+ source_list.map! do |source|
+ replacement_sources.find {|s| s == source } || source
+ end
+ end
+
+ replacement_rubygems = !Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? &&
+ replacement_sources.detect {|s| s.is_a?(Source::Rubygems) }
+ @rubygems_aggregate = replacement_rubygems if replacement_rubygems
+
+ return true if !equal_sources?(lock_sources, replacement_sources) && !equivalent_sources?(lock_sources, replacement_sources)
+ return true if replacement_rubygems && rubygems_remotes.to_set != replacement_rubygems.remotes.to_set
+
+ false
+ end
+
+ def cached!
+ all_sources.each(&:cached!)
+ end
+
+ def remote!
+ all_sources.each(&:remote!)
+ end
+
+ def rubygems_primary_remotes
+ @rubygems_aggregate.remotes
+ end
+
+ private
+
+ def rubygems_aggregate_class
+ Source::Rubygems
+ end
+
+ def add_source_to_list(source, list)
+ list.unshift(source).uniq!
+ source
+ end
+
+ def source_list_for(source)
+ case source
+ when Source::Git then git_sources
+ when Source::Path then path_sources
+ when Source::Rubygems then rubygems_sources
+ when Plugin::API::Source then plugin_sources
+ else raise ArgumentError, "Invalid source: #{source.inspect}"
+ end
+ end
+
+ def combine_rubygems_sources
+ Source::Rubygems.new("remotes" => rubygems_remotes)
+ end
+
+ def warn_on_git_protocol(source)
+ return if Bundler.settings["git.allow_insecure"]
+
+ if source.uri =~ /^git\:/
+ Bundler.ui.warn "The git source `#{source.uri}` uses the `git` protocol, " \
+ "which transmits data without encryption. Disable this warning with " \
+ "`bundle config git.allow_insecure true`, or switch to the `https` " \
+ "protocol to keep your data secure."
+ end
+ end
+
+ def equal_sources?(lock_sources, replacement_sources)
+ lock_sources.to_set == replacement_sources.to_set
+ end
+
+ def equal_source?(source, other_source)
+ source == other_source
+ end
+
+ def equivalent_source?(source, other_source)
+ return false unless Bundler.settings[:allow_deployment_source_credential_changes] && source.is_a?(Source::Rubygems)
+
+ equivalent_rubygems_sources?([source], [other_source])
+ end
+
+ def equivalent_sources?(lock_sources, replacement_sources)
+ return false unless Bundler.settings[:allow_deployment_source_credential_changes]
+
+ lock_rubygems_sources, lock_other_sources = lock_sources.partition {|s| s.is_a?(Source::Rubygems) }
+ replacement_rubygems_sources, replacement_other_sources = replacement_sources.partition {|s| s.is_a?(Source::Rubygems) }
+
+ equivalent_rubygems_sources?(lock_rubygems_sources, replacement_rubygems_sources) && equal_sources?(lock_other_sources, replacement_other_sources)
+ end
+
+ def equivalent_rubygems_sources?(lock_sources, replacement_sources)
+ actual_remotes = replacement_sources.map(&:remotes).flatten.uniq
+ lock_sources.all? {|s| s.equivalent_remotes?(actual_remotes) }
+ end
+ end
+end
diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb
new file mode 100644
index 0000000000..5003b2cbec
--- /dev/null
+++ b/lib/bundler/spec_set.rb
@@ -0,0 +1,192 @@
+# frozen_string_literal: true
+
+require "tsort"
+require "forwardable"
+require "set"
+
+module Bundler
+ class SpecSet
+ extend Forwardable
+ include TSort, Enumerable
+
+ def_delegators :@specs, :<<, :length, :add, :remove, :size, :empty?
+ def_delegators :sorted, :each
+
+ def initialize(specs)
+ @specs = specs
+ end
+
+ def for(dependencies, skip = [], check = false, match_current_platform = false, raise_on_missing = true)
+ handled = Set.new
+ deps = dependencies.dup
+ specs = []
+ skip += ["bundler"]
+
+ loop do
+ break unless dep = deps.shift
+ next if !handled.add?(dep) || skip.include?(dep.name)
+
+ if spec = spec_for_dependency(dep, match_current_platform)
+ specs << spec
+
+ spec.dependencies.each do |d|
+ next if d.type == :development
+ d = DepProxy.new(d, dep.__platform) unless match_current_platform
+ deps << d
+ end
+ elsif check
+ return false
+ elsif raise_on_missing
+ others = lookup[dep.name] if match_current_platform
+ message = "Unable to find a spec satisfying #{dep} in the set. Perhaps the lockfile is corrupted?"
+ message += " Found #{others.join(", ")} that did not match the current platform." if others && !others.empty?
+ raise GemNotFound, message
+ end
+ end
+
+ if spec = lookup["bundler"].first
+ specs << spec
+ end
+
+ check ? true : SpecSet.new(specs)
+ end
+
+ def valid_for?(deps)
+ self.for(deps, [], true)
+ end
+
+ def [](key)
+ key = key.name if key.respond_to?(:name)
+ lookup[key].reverse
+ end
+
+ def []=(key, value)
+ @specs << value
+ @lookup = nil
+ @sorted = nil
+ value
+ end
+
+ def sort!
+ self
+ end
+
+ def to_a
+ sorted.dup
+ end
+
+ def to_hash
+ lookup.dup
+ end
+
+ def materialize(deps, missing_specs = nil)
+ materialized = self.for(deps, [], false, true, !missing_specs).to_a
+ deps = materialized.map(&:name).uniq
+ materialized.map! do |s|
+ next s unless s.is_a?(LazySpecification)
+ s.source.dependency_names = deps if s.source.respond_to?(:dependency_names=)
+ spec = s.__materialize__
+ unless spec
+ unless missing_specs
+ raise GemNotFound, "Could not find #{s.full_name} in any of the sources"
+ end
+ missing_specs << s
+ end
+ spec
+ end
+ SpecSet.new(missing_specs ? materialized.compact : materialized)
+ end
+
+ # Materialize for all the specs in the spec set, regardless of what platform they're for
+ # This is in contrast to how for does platform filtering (and specifically different from how `materialize` calls `for` only for the current platform)
+ # @return [Array<Gem::Specification>]
+ def materialized_for_all_platforms
+ names = @specs.map(&:name).uniq
+ @specs.map do |s|
+ next s unless s.is_a?(LazySpecification)
+ s.source.dependency_names = names if s.source.respond_to?(:dependency_names=)
+ spec = s.__materialize__
+ raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec
+ spec
+ end
+ end
+
+ def merge(set)
+ arr = sorted.dup
+ set.each do |set_spec|
+ full_name = set_spec.full_name
+ next if arr.any? {|spec| spec.full_name == full_name }
+ arr << set_spec
+ end
+ SpecSet.new(arr)
+ end
+
+ def find_by_name_and_platform(name, platform)
+ @specs.detect {|spec| spec.name == name && spec.match_platform(platform) }
+ end
+
+ def what_required(spec)
+ unless req = find {|s| s.dependencies.any? {|d| d.type == :runtime && d.name == spec.name } }
+ return [spec]
+ end
+ what_required(req) << spec
+ end
+
+ private
+
+ def sorted
+ rake = @specs.find {|s| s.name == "rake" }
+ begin
+ @sorted ||= ([rake] + tsort).compact.uniq
+ rescue TSort::Cyclic => error
+ 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."
+ end
+ end
+
+ def extract_circular_gems(error)
+ if Bundler.current_ruby.mri? && Bundler.current_ruby.on_19?
+ error.message.scan(/(\w+) \([^)]/).flatten
+ else
+ error.message.scan(/@name="(.*?)"/).flatten
+ end
+ end
+
+ def lookup
+ @lookup ||= begin
+ lookup = Hash.new {|h, k| h[k] = [] }
+ Index.sort_specs(@specs).reverse_each do |s|
+ lookup[s.name] << s
+ end
+ lookup
+ end
+ end
+
+ def tsort_each_node
+ # MUST sort by name for backwards compatibility
+ @specs.sort_by(&:name).each {|s| yield s }
+ end
+
+ def spec_for_dependency(dep, match_current_platform)
+ specs_for_platforms = lookup[dep.name]
+ if match_current_platform
+ Bundler.rubygems.platforms.reverse_each do |pl|
+ match = GemHelpers.select_best_platform_match(specs_for_platforms, pl)
+ return match if match
+ end
+ nil
+ else
+ GemHelpers.select_best_platform_match(specs_for_platforms, dep.__platform)
+ end
+ end
+
+ def tsort_each_child(s)
+ s.dependencies.sort_by(&:name).each do |d|
+ next if d.type == :development
+ lookup[d.name].each {|s2| yield s2 }
+ end
+ end
+ end
+end
diff --git a/lib/bundler/ssl_certs/.document b/lib/bundler/ssl_certs/.document
new file mode 100644
index 0000000000..fb66f13c33
--- /dev/null
+++ b/lib/bundler/ssl_certs/.document
@@ -0,0 +1 @@
+# Ignore all files in this directory
diff --git a/lib/bundler/ssl_certs/certificate_manager.rb b/lib/bundler/ssl_certs/certificate_manager.rb
new file mode 100644
index 0000000000..26fc38ec18
--- /dev/null
+++ b/lib/bundler/ssl_certs/certificate_manager.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require "bundler/vendored_fileutils"
+require "net/https"
+require "openssl"
+
+module Bundler
+ module SSLCerts
+ class CertificateManager
+ attr_reader :bundler_cert_path, :bundler_certs, :rubygems_certs
+
+ def self.update_from!(rubygems_path)
+ new(rubygems_path).update!
+ end
+
+ def initialize(rubygems_path = nil)
+ if rubygems_path
+ rubygems_cert_path = File.join(rubygems_path, "lib/rubygems/ssl_certs")
+ @rubygems_certs = certificates_in(rubygems_cert_path)
+ end
+
+ @bundler_cert_path = File.expand_path("..", __FILE__)
+ @bundler_certs = certificates_in(bundler_cert_path)
+ end
+
+ def up_to_date?
+ rubygems_certs.all? do |rc|
+ bundler_certs.find do |bc|
+ File.basename(bc) == File.basename(rc) && FileUtils.compare_file(bc, rc)
+ end
+ end
+ end
+
+ def update!
+ return if up_to_date?
+
+ FileUtils.rm bundler_certs
+ FileUtils.cp rubygems_certs, bundler_cert_path
+ end
+
+ def connect_to(host)
+ http = Net::HTTP.new(host, 443)
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ http.cert_store = store
+ http.head("/")
+ end
+
+ private
+
+ def certificates_in(path)
+ Dir[File.join(path, "**/*.pem")].sort
+ end
+
+ def store
+ @store ||= begin
+ store = OpenSSL::X509::Store.new
+ bundler_certs.each do |cert|
+ store.add_file cert
+ end
+ store
+ end
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem b/lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem
index f4ce4ca43d..f4ce4ca43d 100644
--- a/lib/rubygems/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem
+++ b/lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem
diff --git a/lib/rubygems/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem b/lib/bundler/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem
index 9e6810ab70..9e6810ab70 100644
--- a/lib/rubygems/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem
+++ b/lib/bundler/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem
diff --git a/lib/rubygems/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem b/lib/bundler/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem
index 20585f1c01..20585f1c01 100644
--- a/lib/rubygems/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem
+++ b/lib/bundler/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem
diff --git a/lib/bundler/stub_specification.rb b/lib/bundler/stub_specification.rb
new file mode 100644
index 0000000000..0dd024024a
--- /dev/null
+++ b/lib/bundler/stub_specification.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+require "bundler/remote_specification"
+
+module Bundler
+ class StubSpecification < RemoteSpecification
+ def self.from_stub(stub)
+ return stub if stub.is_a?(Bundler::StubSpecification)
+ spec = new(stub.name, stub.version, stub.platform, nil)
+ spec.stub = stub
+ spec
+ end
+
+ attr_accessor :stub, :ignored
+
+ # Pre 2.2.0 did not include extension_dir
+ # https://github.com/rubygems/rubygems/commit/9485ca2d101b82a946d6f327f4bdcdea6d4946ea
+ if Bundler.rubygems.provides?(">= 2.2.0")
+ def source=(source)
+ super
+ # Stub has no concept of source, which means that extension_dir may be wrong
+ # This is the case for git-based gems. So, instead manually assign the extension dir
+ return unless source.respond_to?(:extension_dir_name)
+ path = File.join(stub.extensions_dir, source.extension_dir_name)
+ stub.extension_dir = File.expand_path(path)
+ end
+ end
+
+ def to_yaml
+ _remote_specification.to_yaml
+ end
+
+ # @!group Stub Delegates
+
+ if Bundler.rubygems.provides?(">= 2.3")
+ # This is defined directly to avoid having to load every installed spec
+ def missing_extensions?
+ stub.missing_extensions?
+ end
+ end
+
+ def activated
+ stub.activated
+ end
+
+ def activated=(activated)
+ stub.instance_variable_set(:@activated, activated)
+ end
+
+ def default_gem
+ stub.default_gem
+ end
+
+ def full_gem_path
+ # deleted gems can have their stubs return nil, so in that case grab the
+ # expired path from the full spec
+ stub.full_gem_path || method_missing(:full_gem_path)
+ end
+
+ if Bundler.rubygems.provides?(">= 2.2.0")
+ def full_require_paths
+ stub.full_require_paths
+ end
+
+ # This is what we do in bundler/rubygems_ext
+ # full_require_paths is always implemented in >= 2.2.0
+ def load_paths
+ full_require_paths
+ end
+ end
+
+ def loaded_from
+ stub.loaded_from
+ end
+
+ if Bundler.rubygems.stubs_provide_full_functionality?
+ def matches_for_glob(glob)
+ stub.matches_for_glob(glob)
+ end
+ end
+
+ def raw_require_paths
+ stub.raw_require_paths
+ end
+
+ private
+
+ def _remote_specification
+ @_remote_specification ||= begin
+ rs = stub.to_spec
+ if rs.equal?(self) # happens when to_spec gets the spec from Gem.loaded_specs
+ rs = Gem::Specification.load(loaded_from)
+ Bundler.rubygems.stub_set_spec(stub, rs)
+ end
+
+ unless rs
+ raise GemspecError, "The gemspec for #{full_name} at #{loaded_from}" \
+ " was missing or broken. Try running `gem pristine #{name} -v #{version}`" \
+ " to fix the cached spec."
+ end
+
+ rs.source = source
+
+ rs
+ end
+ end
+ end
+end
diff --git a/lib/bundler/templates/.document b/lib/bundler/templates/.document
new file mode 100644
index 0000000000..fb66f13c33
--- /dev/null
+++ b/lib/bundler/templates/.document
@@ -0,0 +1 @@
+# Ignore all files in this directory
diff --git a/lib/bundler/templates/Executable b/lib/bundler/templates/Executable
new file mode 100644
index 0000000000..3e8d5b317a
--- /dev/null
+++ b/lib/bundler/templates/Executable
@@ -0,0 +1,29 @@
+#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG["ruby_install_name"] %>
+# frozen_string_literal: true
+
+#
+# This file was generated by Bundler.
+#
+# The application '<%= executable %>' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require "pathname"
+ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../<%= relative_gemfile_path %>",
+ Pathname.new(__FILE__).realpath)
+
+bundle_binstub = File.expand_path("../bundle", __FILE__)
+
+if File.file?(bundle_binstub)
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
+ load(bundle_binstub)
+ else
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
+Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
+ end
+end
+
+require "rubygems"
+require "bundler/setup"
+
+load Gem.bin_path("<%= spec.name %>", "<%= executable %>")
diff --git a/lib/bundler/templates/Executable.bundler b/lib/bundler/templates/Executable.bundler
new file mode 100644
index 0000000000..eeda90b584
--- /dev/null
+++ b/lib/bundler/templates/Executable.bundler
@@ -0,0 +1,105 @@
+#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG["ruby_install_name"] %>
+# frozen_string_literal: true
+
+#
+# This file was generated by Bundler.
+#
+# The application '<%= executable %>' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require "rubygems"
+
+m = Module.new do
+ module_function
+
+ def invoked_as_script?
+ File.expand_path($0) == File.expand_path(__FILE__)
+ end
+
+ def env_var_version
+ ENV["BUNDLER_VERSION"]
+ end
+
+ def cli_arg_version
+ return unless invoked_as_script? # don't want to hijack other binstubs
+ return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
+ bundler_version = nil
+ update_index = nil
+ ARGV.each_with_index do |a, i|
+ if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
+ bundler_version = a
+ end
+ next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
+ bundler_version = $1 || ">= 0.a"
+ update_index = i
+ end
+ bundler_version
+ end
+
+ def gemfile
+ gemfile = ENV["BUNDLE_GEMFILE"]
+ return gemfile if gemfile && !gemfile.empty?
+
+ File.expand_path("../<%= relative_gemfile_path %>", __FILE__)
+ end
+
+ def lockfile
+ lockfile =
+ case File.basename(gemfile)
+ when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
+ else "#{gemfile}.lock"
+ end
+ File.expand_path(lockfile)
+ end
+
+ def lockfile_version
+ return unless File.file?(lockfile)
+ lockfile_contents = File.read(lockfile)
+ return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
+ Regexp.last_match(1)
+ end
+
+ def bundler_version
+ @bundler_version ||= begin
+ env_var_version || cli_arg_version ||
+ lockfile_version || "#{Gem::Requirement.default}.a"
+ end
+ end
+
+ def load_bundler!
+ ENV["BUNDLE_GEMFILE"] ||= gemfile
+
+ # must dup string for RG < 1.8 compatibility
+ activate_bundler(bundler_version.dup)
+ end
+
+ def activate_bundler(bundler_version)
+ if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0")
+ bundler_version = "< 2"
+ end
+ gem_error = activation_error_handling do
+ gem "bundler", bundler_version
+ end
+ return if gem_error.nil?
+ require_error = activation_error_handling do
+ require "bundler/version"
+ end
+ return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION))
+ warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`"
+ exit 42
+ end
+
+ def activation_error_handling
+ yield
+ nil
+ rescue StandardError, LoadError => e
+ e
+ end
+end
+
+m.load_bundler!
+
+if m.invoked_as_script?
+ load Gem.bin_path("<%= spec.name %>", "<%= executable %>")
+end
diff --git a/lib/bundler/templates/Executable.standalone b/lib/bundler/templates/Executable.standalone
new file mode 100644
index 0000000000..4bf0753f44
--- /dev/null
+++ b/lib/bundler/templates/Executable.standalone
@@ -0,0 +1,14 @@
+#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG["ruby_install_name"] %>
+#
+# This file was generated by Bundler.
+#
+# The application '<%= executable %>' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require "pathname"
+path = Pathname.new(__FILE__)
+$:.unshift File.expand_path "../<%= standalone_path %>", path.realpath
+
+require "bundler/setup"
+load File.expand_path "../<%= executable_path %>", path.realpath
diff --git a/lib/bundler/templates/Gemfile b/lib/bundler/templates/Gemfile
new file mode 100644
index 0000000000..1afd2cce67
--- /dev/null
+++ b/lib/bundler/templates/Gemfile
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
+
+# gem "rails"
diff --git a/lib/bundler/templates/gems.rb b/lib/bundler/templates/gems.rb
new file mode 100644
index 0000000000..547cd6e8d9
--- /dev/null
+++ b/lib/bundler/templates/gems.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+# A sample gems.rb
+source "https://rubygems.org"
+
+git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
+
+# gem "rails"
diff --git a/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt b/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt
new file mode 100644
index 0000000000..a3833d29d7
--- /dev/null
+++ b/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt
@@ -0,0 +1,74 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, gender identity and expression, level of experience,
+nationality, personal appearance, race, religion, or sexual identity and
+orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at <%= config[:email] %>. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff --git a/lib/bundler/templates/newgem/Gemfile.tt b/lib/bundler/templates/newgem/Gemfile.tt
new file mode 100644
index 0000000000..c114bd6665
--- /dev/null
+++ b/lib/bundler/templates/newgem/Gemfile.tt
@@ -0,0 +1,6 @@
+source "https://rubygems.org"
+
+git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
+
+# Specify your gem's dependencies in <%= config[:name] %>.gemspec
+gemspec
diff --git a/lib/bundler/templates/newgem/LICENSE.txt.tt b/lib/bundler/templates/newgem/LICENSE.txt.tt
new file mode 100644
index 0000000000..76ef4b0191
--- /dev/null
+++ b/lib/bundler/templates/newgem/LICENSE.txt.tt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) <%= Time.now.year %> <%= config[:author] %>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/lib/bundler/templates/newgem/README.md.tt b/lib/bundler/templates/newgem/README.md.tt
new file mode 100644
index 0000000000..868a0afe67
--- /dev/null
+++ b/lib/bundler/templates/newgem/README.md.tt
@@ -0,0 +1,47 @@
+# <%= 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 above, and describe your gem
+
+## Installation
+
+Add this line to your application's Gemfile:
+
+```ruby
+gem '<%= config[:name] %>'
+```
+
+And then execute:
+
+ $ bundle
+
+Or install it yourself as:
+
+ $ gem install <%= config[:name] %>
+
+## Usage
+
+TODO: Write usage instructions here
+
+## Development
+
+After checking out the repo, run `bin/setup` to install dependencies.<% if config[:test] %> Then, run `rake <%= config[:test].sub('mini', '').sub('rspec', 'spec') %>` to run the tests.<% end %> You can also run `bin/console` for an interactive prompt that will allow you to experiment.<% if config[:bin] %> Run `bundle exec <%= config[:name] %>` to use the gem in this directory, ignoring other installed copies of this gem.<% end %>
+
+To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
+
+## Contributing
+
+Bug reports and pull requests are welcome on GitHub at https://github.com/<%= config[:github_username] %>/<%= config[:name] %>.<% if config[:coc] %> This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.<% end %>
+<% if config[:mit] -%>
+
+## License
+
+The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
+<% end -%>
+<% if config[:coc] -%>
+
+## Code of Conduct
+
+Everyone interacting in the <%= config[:constant_name] %> project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/<%= config[:github_username] %>/<%= config[:name] %>/blob/master/CODE_OF_CONDUCT.md).
+<% end -%>
diff --git a/lib/bundler/templates/newgem/Rakefile.tt b/lib/bundler/templates/newgem/Rakefile.tt
new file mode 100644
index 0000000000..099da6f3ec
--- /dev/null
+++ b/lib/bundler/templates/newgem/Rakefile.tt
@@ -0,0 +1,29 @@
+require "bundler/gem_tasks"
+<% if config[:test] == "minitest" -%>
+require "rake/testtask"
+
+Rake::TestTask.new(:test) do |t|
+ t.libs << "test"
+ t.libs << "lib"
+ t.test_files = FileList["test/**/*_test.rb"]
+end
+
+<% elsif config[:test] == "rspec" -%>
+require "rspec/core/rake_task"
+
+RSpec::Core::RakeTask.new(:spec)
+
+<% end -%>
+<% if config[:ext] -%>
+require "rake/extensiontask"
+
+task :build => :compile
+
+Rake::ExtensionTask.new("<%= config[:underscored_name] %>") do |ext|
+ ext.lib_dir = "lib/<%= config[:namespaced_path] %>"
+end
+
+task :default => [:clobber, :compile, :<%= config[:test_task] %>]
+<% else -%>
+task :default => :<%= config[:test_task] %>
+<% end -%>
diff --git a/lib/bundler/templates/newgem/bin/console.tt b/lib/bundler/templates/newgem/bin/console.tt
new file mode 100644
index 0000000000..a27f82430f
--- /dev/null
+++ b/lib/bundler/templates/newgem/bin/console.tt
@@ -0,0 +1,14 @@
+#!/usr/bin/env ruby
+
+require "bundler/setup"
+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/bin/setup.tt b/lib/bundler/templates/newgem/bin/setup.tt
new file mode 100644
index 0000000000..dce67d860a
--- /dev/null
+++ b/lib/bundler/templates/newgem/bin/setup.tt
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+set -euo pipefail
+IFS=$'\n\t'
+set -vx
+
+bundle install
+
+# Do any other automated setup that you need to do here
diff --git a/lib/bundler/templates/newgem/exe/newgem.tt b/lib/bundler/templates/newgem/exe/newgem.tt
new file mode 100644
index 0000000000..a8339bb79f
--- /dev/null
+++ b/lib/bundler/templates/newgem/exe/newgem.tt
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+
+require "<%= config[:namespaced_path] %>"
diff --git a/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt b/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt
new file mode 100644
index 0000000000..8cfc828f94
--- /dev/null
+++ b/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 0000000000..8177c4d202
--- /dev/null
+++ b/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt
@@ -0,0 +1,9 @@
+#include "<%= config[:underscored_name] %>.h"
+
+VALUE rb_m<%= config[:constant_array].join %>;
+
+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/newgem.h.tt b/lib/bundler/templates/newgem/ext/newgem/newgem.h.tt
new file mode 100644
index 0000000000..c6e420b66e
--- /dev/null
+++ b/lib/bundler/templates/newgem/ext/newgem/newgem.h.tt
@@ -0,0 +1,6 @@
+#ifndef <%= config[:underscored_name].upcase %>_H
+#define <%= config[:underscored_name].upcase %>_H 1
+
+#include "ruby.h"
+
+#endif /* <%= config[:underscored_name].upcase %>_H */
diff --git a/lib/bundler/templates/newgem/gitignore.tt b/lib/bundler/templates/newgem/gitignore.tt
new file mode 100644
index 0000000000..b1c9f9986c
--- /dev/null
+++ b/lib/bundler/templates/newgem/gitignore.tt
@@ -0,0 +1,20 @@
+/.bundle/
+/.yardoc
+/_yardoc/
+/coverage/
+/doc/
+/pkg/
+/spec/reports/
+/tmp/
+<%- if config[:ext] -%>
+*.bundle
+*.so
+*.o
+*.a
+mkmf.log
+<%- end -%>
+<%- if config[:test] == "rspec" -%>
+
+# rspec failure tracking
+.rspec_status
+<%- end -%>
diff --git a/lib/bundler/templates/newgem/lib/newgem.rb.tt b/lib/bundler/templates/newgem/lib/newgem.rb.tt
new file mode 100644
index 0000000000..fae6337c3e
--- /dev/null
+++ b/lib/bundler/templates/newgem/lib/newgem.rb.tt
@@ -0,0 +1,13 @@
+require "<%= config[:namespaced_path] %>/version"
+<%- if config[:ext] -%>
+require "<%= config[:namespaced_path] %>/<%= config[:underscored_name] %>"
+<%- end -%>
+
+<%- config[:constant_array].each_with_index do |c, i| -%>
+<%= " " * i %>module <%= c %>
+<%- end -%>
+<%= " " * config[:constant_array].size %>class Error < StandardError; end
+<%= " " * config[:constant_array].size %># Your code goes here...
+<%- (config[:constant_array].size-1).downto(0) do |i| -%>
+<%= " " * i %>end
+<%- end -%>
diff --git a/lib/bundler/templates/newgem/lib/newgem/version.rb.tt b/lib/bundler/templates/newgem/lib/newgem/version.rb.tt
new file mode 100644
index 0000000000..389daf5048
--- /dev/null
+++ b/lib/bundler/templates/newgem/lib/newgem/version.rb.tt
@@ -0,0 +1,7 @@
+<%- config[:constant_array].each_with_index do |c, i| -%>
+<%= " " * i %>module <%= c %>
+<%- end -%>
+<%= " " * config[:constant_array].size %>VERSION = "0.1.0"
+<%- (config[:constant_array].size-1).downto(0) do |i| -%>
+<%= " " * i %>end
+<%- end -%>
diff --git a/lib/bundler/templates/newgem/newgem.gemspec.tt b/lib/bundler/templates/newgem/newgem.gemspec.tt
new file mode 100644
index 0000000000..faf6f7bbc5
--- /dev/null
+++ b/lib/bundler/templates/newgem/newgem.gemspec.tt
@@ -0,0 +1,55 @@
+<%- if RUBY_VERSION < "2.0.0" -%>
+# coding: utf-8
+<%- end -%>
+
+lib = File.expand_path("../lib", __FILE__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+require "<%= config[:namespaced_path] %>/version"
+
+Gem::Specification.new do |spec|
+ spec.name = <%= config[:name].inspect %>
+ spec.version = <%= config[:constant_name] %>::VERSION
+ spec.authors = [<%= config[:author].inspect %>]
+ spec.email = [<%= config[:email].inspect %>]
+
+ spec.summary = %q{TODO: Write a short summary, because RubyGems requires one.}
+ spec.description = %q{TODO: Write a longer description or delete this line.}
+ spec.homepage = "TODO: Put your gem's website or public repo URL here."
+<%- if config[:mit] -%>
+ spec.license = "MIT"
+<%- end -%>
+
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
+ if spec.respond_to?(:metadata)
+ spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
+
+ spec.metadata["homepage_uri"] = spec.homepage
+ spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
+ spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
+ else
+ raise "RubyGems 2.0 or newer is required to protect against " \
+ "public gem pushes."
+ end
+
+ # Specify which files should be added to the gem when it is released.
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
+ end
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+<%- if config[:ext] -%>
+ spec.extensions = ["ext/<%= config[:underscored_name] %>/extconf.rb"]
+<%- end -%>
+
+ spec.add_development_dependency "bundler", "~> <%= config[:bundler_version] %>"
+ spec.add_development_dependency "rake", "~> 10.0"
+<%- if config[:ext] -%>
+ spec.add_development_dependency "rake-compiler"
+<%- end -%>
+<%- if config[:test] -%>
+ spec.add_development_dependency "<%= config[:test] %>", "~> <%= config[:test_framework_version] %>"
+<%- end -%>
+end
diff --git a/lib/bundler/templates/newgem/rspec.tt b/lib/bundler/templates/newgem/rspec.tt
new file mode 100644
index 0000000000..34c5164d9b
--- /dev/null
+++ b/lib/bundler/templates/newgem/rspec.tt
@@ -0,0 +1,3 @@
+--format documentation
+--color
+--require spec_helper
diff --git a/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt b/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt
new file mode 100644
index 0000000000..c63b487830
--- /dev/null
+++ b/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt
@@ -0,0 +1,9 @@
+RSpec.describe <%= config[:constant_name] %> do
+ it "has a version number" do
+ expect(<%= config[:constant_name] %>::VERSION).not_to be nil
+ end
+
+ it "does something useful" do
+ expect(false).to eq(true)
+ end
+end
diff --git a/lib/bundler/templates/newgem/spec/spec_helper.rb.tt b/lib/bundler/templates/newgem/spec/spec_helper.rb.tt
new file mode 100644
index 0000000000..805cf57e01
--- /dev/null
+++ b/lib/bundler/templates/newgem/spec/spec_helper.rb.tt
@@ -0,0 +1,14 @@
+require "bundler/setup"
+require "<%= config[:namespaced_path] %>"
+
+RSpec.configure do |config|
+ # Enable flags like --only-failures and --next-failure
+ config.example_status_persistence_file_path = ".rspec_status"
+
+ # Disable RSpec exposing methods globally on `Module` and `main`
+ config.disable_monkey_patching!
+
+ config.expect_with :rspec do |c|
+ c.syntax = :expect
+ end
+end
diff --git a/lib/bundler/templates/newgem/test/newgem_test.rb.tt b/lib/bundler/templates/newgem/test/newgem_test.rb.tt
new file mode 100644
index 0000000000..f2af9f90e0
--- /dev/null
+++ b/lib/bundler/templates/newgem/test/newgem_test.rb.tt
@@ -0,0 +1,11 @@
+require "test_helper"
+
+class <%= config[:constant_name] %>Test < Minitest::Test
+ def test_that_it_has_a_version_number
+ refute_nil ::<%= config[:constant_name] %>::VERSION
+ end
+
+ def test_it_does_something_useful
+ assert false
+ end
+end
diff --git a/lib/bundler/templates/newgem/test/test_helper.rb.tt b/lib/bundler/templates/newgem/test/test_helper.rb.tt
new file mode 100644
index 0000000000..725e3e4647
--- /dev/null
+++ b/lib/bundler/templates/newgem/test/test_helper.rb.tt
@@ -0,0 +1,4 @@
+$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
+require "<%= config[:namespaced_path] %>"
+
+require "minitest/autorun"
diff --git a/lib/bundler/templates/newgem/travis.yml.tt b/lib/bundler/templates/newgem/travis.yml.tt
new file mode 100644
index 0000000000..7a3381a889
--- /dev/null
+++ b/lib/bundler/templates/newgem/travis.yml.tt
@@ -0,0 +1,7 @@
+---
+sudo: false
+language: ruby
+cache: bundler
+rvm:
+ - <%= RUBY_VERSION %>
+before_install: gem install bundler -v <%= Bundler::VERSION %>
diff --git a/lib/bundler/ui.rb b/lib/bundler/ui.rb
new file mode 100644
index 0000000000..8138b30d38
--- /dev/null
+++ b/lib/bundler/ui.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Bundler
+ module UI
+ autoload :RGProxy, "bundler/ui/rg_proxy"
+ autoload :Shell, "bundler/ui/shell"
+ autoload :Silent, "bundler/ui/silent"
+ end
+end
diff --git a/lib/bundler/ui/rg_proxy.rb b/lib/bundler/ui/rg_proxy.rb
new file mode 100644
index 0000000000..e2f98481db
--- /dev/null
+++ b/lib/bundler/ui/rg_proxy.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require "bundler/ui"
+require "rubygems/user_interaction"
+
+module Bundler
+ module UI
+ class RGProxy < ::Gem::SilentUI
+ def initialize(ui)
+ @ui = ui
+ super()
+ end
+
+ def say(message)
+ @ui && @ui.debug(message)
+ end
+ end
+ end
+end
diff --git a/lib/bundler/ui/shell.rb b/lib/bundler/ui/shell.rb
new file mode 100644
index 0000000000..16e3d15713
--- /dev/null
+++ b/lib/bundler/ui/shell.rb
@@ -0,0 +1,146 @@
+# frozen_string_literal: true
+
+require "bundler/vendored_thor"
+
+module Bundler
+ module UI
+ class Shell
+ LEVELS = %w[silent error warn confirm info debug].freeze
+
+ attr_writer :shell
+
+ def initialize(options = {})
+ if options["no-color"] || !$stdout.tty?
+ Thor::Base.shell = Thor::Shell::Basic
+ end
+ @shell = Thor::Base.shell.new
+ @level = ENV["DEBUG"] ? "debug" : "info"
+ @warning_history = []
+ end
+
+ def add_color(string, *color)
+ @shell.set_color(string, *color)
+ end
+
+ def info(msg, newline = nil)
+ tell_me(msg, nil, newline) if level("info")
+ end
+
+ def confirm(msg, newline = nil)
+ tell_me(msg, :green, newline) if level("confirm")
+ end
+
+ def warn(msg, newline = nil)
+ return unless level("warn")
+ return if @warning_history.include? msg
+ @warning_history << msg
+
+ return tell_err(msg, :yellow, newline) if Bundler.feature_flag.error_on_stderr?
+ tell_me(msg, :yellow, newline)
+ end
+
+ def error(msg, newline = nil)
+ return unless level("error")
+ return tell_err(msg, :red, newline) if Bundler.feature_flag.error_on_stderr?
+ tell_me(msg, :red, newline)
+ end
+
+ def debug(msg, newline = nil)
+ tell_me(msg, nil, newline) if debug?
+ end
+
+ def debug?
+ level("debug")
+ end
+
+ def quiet?
+ level("quiet")
+ end
+
+ def ask(msg)
+ @shell.ask(msg)
+ end
+
+ def yes?(msg)
+ @shell.yes?(msg)
+ end
+
+ def no?
+ @shell.no?(msg)
+ end
+
+ def level=(level)
+ raise ArgumentError unless LEVELS.include?(level.to_s)
+ @level = level.to_s
+ end
+
+ def level(name = nil)
+ return @level unless name
+ unless index = LEVELS.index(name)
+ raise "#{name.inspect} is not a valid level"
+ end
+ index <= LEVELS.index(@level)
+ end
+
+ def trace(e, newline = nil, force = false)
+ return unless debug? || force
+ msg = "#{e.class}: #{e.message}\n#{e.backtrace.join("\n ")}"
+ tell_me(msg, nil, newline)
+ end
+
+ def silence(&blk)
+ with_level("silent", &blk)
+ end
+
+ def unprinted_warnings
+ []
+ end
+
+ private
+
+ # valimism
+ def tell_me(msg, color = nil, newline = nil)
+ msg = word_wrap(msg) if newline.is_a?(Hash) && newline[:wrap]
+ if newline.nil?
+ @shell.say(msg, color)
+ else
+ @shell.say(msg, color, newline)
+ end
+ end
+
+ def tell_err(message, color = nil, newline = nil)
+ return if @shell.send(:stderr).closed?
+
+ newline ||= message.to_s !~ /( |\t)\Z/
+ message = word_wrap(message) if newline.is_a?(Hash) && newline[:wrap]
+
+ color = nil if color && !$stderr.tty?
+
+ buffer = @shell.send(:prepare_message, message, *color)
+ buffer << "\n" if newline && !message.to_s.end_with?("\n")
+
+ @shell.send(:stderr).print(buffer)
+ @shell.send(:stderr).flush
+ end
+
+ def strip_leading_spaces(text)
+ spaces = text[/\A\s+/, 0]
+ spaces ? text.gsub(/#{spaces}/, "") : text
+ end
+
+ def word_wrap(text, line_width = @shell.terminal_width)
+ strip_leading_spaces(text).split("\n").collect do |line|
+ line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
+ end * "\n"
+ end
+
+ def with_level(level)
+ original = @level
+ @level = level
+ yield
+ ensure
+ @level = original
+ end
+ end
+ end
+end
diff --git a/lib/bundler/ui/silent.rb b/lib/bundler/ui/silent.rb
new file mode 100644
index 0000000000..dca1b2ac86
--- /dev/null
+++ b/lib/bundler/ui/silent.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+module Bundler
+ module UI
+ class Silent
+ attr_writer :shell
+
+ def initialize
+ @warnings = []
+ end
+
+ def add_color(string, color)
+ string
+ end
+
+ def info(message, newline = nil)
+ end
+
+ def confirm(message, newline = nil)
+ end
+
+ def warn(message, newline = nil)
+ @warnings |= [message]
+ end
+
+ def error(message, newline = nil)
+ end
+
+ def debug(message, newline = nil)
+ end
+
+ def debug?
+ false
+ end
+
+ def quiet?
+ false
+ end
+
+ def ask(message)
+ end
+
+ def yes?(msg)
+ raise "Cannot ask yes? with a silent shell"
+ end
+
+ def no?
+ raise "Cannot ask no? with a silent shell"
+ end
+
+ def level=(name)
+ end
+
+ def level(name = nil)
+ end
+
+ def trace(message, newline = nil, force = false)
+ end
+
+ def silence
+ yield
+ end
+
+ def unprinted_warnings
+ @warnings
+ end
+ end
+ end
+end
diff --git a/lib/bundler/uri_credentials_filter.rb b/lib/bundler/uri_credentials_filter.rb
new file mode 100644
index 0000000000..ee3692268c
--- /dev/null
+++ b/lib/bundler/uri_credentials_filter.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Bundler
+ module URICredentialsFilter
+ module_function
+
+ def credential_filtered_uri(uri_to_anonymize)
+ return uri_to_anonymize if uri_to_anonymize.nil?
+ uri = uri_to_anonymize.dup
+ uri = URI(uri.to_s) unless uri.is_a?(URI)
+ if uri.userinfo
+ # oauth authentication
+ if uri.password == "x-oauth-basic" || uri.password == "x"
+ # URI as string does not display with password if no user is set
+ oauth_designation = uri.password
+ uri.user = oauth_designation
+ end
+ uri.password = nil
+ end
+ return uri if uri_to_anonymize.is_a?(URI)
+ return uri.to_s if uri_to_anonymize.is_a?(String)
+ rescue URI::InvalidURIError # uri is not canonical uri scheme
+ uri
+ end
+
+ def credential_filtered_string(str_to_filter, uri)
+ return str_to_filter if uri.nil? || str_to_filter.nil?
+ str_with_no_credentials = str_to_filter.dup
+ anonymous_uri_str = credential_filtered_uri(uri).to_s
+ uri_str = uri.to_s
+ if anonymous_uri_str != uri_str
+ str_with_no_credentials = str_with_no_credentials.gsub(uri_str, anonymous_uri_str)
+ end
+ str_with_no_credentials
+ end
+ end
+end
diff --git a/lib/bundler/vendor/fileutils/lib/fileutils.rb b/lib/bundler/vendor/fileutils/lib/fileutils.rb
new file mode 100644
index 0000000000..cc69740845
--- /dev/null
+++ b/lib/bundler/vendor/fileutils/lib/fileutils.rb
@@ -0,0 +1,1638 @@
+# frozen_string_literal: true
+#
+# = fileutils.rb
+#
+# Copyright (c) 2000-2007 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the same terms of ruby.
+#
+# == module Bundler::FileUtils
+#
+# Namespace for several file utility methods for copying, moving, removing, etc.
+#
+# === Module Functions
+#
+# require 'bundler/vendor/fileutils/lib/fileutils'
+#
+# 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)
+#
+# The <tt>options</tt> parameter is a hash of options, taken from the list
+# <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and <tt>:verbose</tt>.
+# <tt>:noop</tt> means that no changes are made. The other three are obvious.
+# Each method documents the options that it honours.
+#
+# 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.
+#
+# There are some `low level' methods, which do not accept any option:
+#
+# Bundler::FileUtils.copy_entry(src, dest, preserve = false, dereference = 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)
+#
+# == module Bundler::FileUtils::Verbose
+#
+# 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.
+#
+# == module Bundler::FileUtils::NoWrite
+#
+# 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.
+#
+# == module Bundler::FileUtils::DryRun
+#
+# 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.
+#
+
+module Bundler::FileUtils
+
+ def self.private_module_function(name) #:nodoc:
+ module_function name
+ private_class_method name
+ end
+
+ #
+ # Returns the name of the current directory.
+ #
+ def pwd
+ Dir.pwd
+ end
+ module_function :pwd
+
+ alias getwd pwd
+ module_function :getwd
+
+ #
+ # Changes the current directory to the directory +dir+.
+ #
+ # If this method is called with block, resumes to the old
+ # working directory after the block execution finished.
+ #
+ # Bundler::FileUtils.cd('/', :verbose => true) # chdir and report it
+ #
+ # Bundler::FileUtils.cd('/') do # chdir
+ # # ... # do something
+ # end # return to original directory
+ #
+ def cd(dir, verbose: nil, &block) # :yield: dir
+ fu_output_message "cd #{dir}" if verbose
+ Dir.chdir(dir, &block)
+ fu_output_message 'cd -' if verbose and block
+ end
+ module_function :cd
+
+ alias chdir cd
+ module_function :chdir
+
+ #
+ # Returns true if +new+ is newer than all +old_list+.
+ # Non-existent files are older than any file.
+ #
+ # Bundler::FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
+ # system 'make hello.o'
+ #
+ def uptodate?(new, old_list)
+ return false unless File.exist?(new)
+ new_time = File.mtime(new)
+ old_list.each do |old|
+ if File.exist?(old)
+ return false unless new_time > File.mtime(old)
+ end
+ end
+ true
+ end
+ module_function :uptodate?
+
+ def remove_trailing_slash(dir) #:nodoc:
+ dir == '/' ? dir : dir.chomp(?/)
+ end
+ private_module_function :remove_trailing_slash
+
+ #
+ # Creates one or more directories.
+ #
+ # 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
+ #
+ def mkdir(list, mode: nil, noop: nil, verbose: nil)
+ list = fu_list(list)
+ fu_output_message "mkdir #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
+ return if noop
+
+ list.each do |dir|
+ fu_mkdir dir, mode
+ end
+ end
+ module_function :mkdir
+
+ #
+ # Creates a directory and all its parent directories.
+ # For example,
+ #
+ # Bundler::FileUtils.mkdir_p '/usr/local/lib/ruby'
+ #
+ # causes to make following directories, if it does not exist.
+ #
+ # * /usr
+ # * /usr/local
+ # * /usr/local/lib
+ # * /usr/local/lib/ruby
+ #
+ # You can pass several directories at a time in a list.
+ #
+ 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
+
+ stack = []
+ until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
+ stack.push path
+ path = File.dirname(path)
+ end
+ stack.pop # root directory should exist
+ stack.reverse_each do |dir|
+ begin
+ fu_mkdir dir, mode
+ rescue SystemCallError
+ raise unless File.directory?(dir)
+ end
+ end
+ end
+
+ return *list
+ end
+ module_function :mkdir_p
+
+ alias mkpath mkdir_p
+ alias makedirs mkdir_p
+ module_function :mkpath
+ module_function :makedirs
+
+ def fu_mkdir(path, mode) #:nodoc:
+ path = remove_trailing_slash(path)
+ if mode
+ Dir.mkdir path, mode
+ File.chmod mode, path
+ else
+ Dir.mkdir path
+ end
+ end
+ private_module_function :fu_mkdir
+
+ #
+ # Removes one or more directories.
+ #
+ # 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
+ #
+ def rmdir(list, parents: nil, noop: nil, verbose: nil)
+ list = fu_list(list)
+ fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if verbose
+ return if noop
+ list.each do |dir|
+ begin
+ Dir.rmdir(dir = remove_trailing_slash(dir))
+ if parents
+ until (parent = File.dirname(dir)) == '.' or parent == dir
+ dir = parent
+ Dir.rmdir(dir)
+ end
+ end
+ rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT
+ end
+ end
+ end
+ module_function :rmdir
+
+ #
+ # :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)
+ #
+ # 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+.
+ #
+ # Bundler::FileUtils.ln 'gcc', 'cc', verbose: true
+ # Bundler::FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
+ #
+ # 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.
+ #
+ # Bundler::FileUtils.cd '/sbin'
+ # Bundler::FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
+ #
+ def ln(src, dest, force: nil, noop: nil, verbose: nil)
+ fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
+ return if noop
+ fu_each_src_dest0(src, dest) do |s,d|
+ remove_file d, true if force
+ File.link s, d
+ end
+ end
+ module_function :ln
+
+ alias link ln
+ module_function :link
+
+ #
+ # :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)
+ #
+ # In the first form, creates a symbolic link +link+ which points to +target+.
+ # If +link+ already exists, raises Errno::EEXIST.
+ # But if the :force option is set, overwrites +link+.
+ #
+ # Bundler::FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
+ # Bundler::FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
+ #
+ # 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.
+ #
+ # Bundler::FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin'
+ #
+ def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
+ 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|
+ remove_file d, true if force
+ File.symlink s, d
+ end
+ end
+ module_function :ln_s
+
+ alias symlink ln_s
+ module_function :symlink
+
+ #
+ # :call-seq:
+ # Bundler::FileUtils.ln_sf(*args)
+ #
+ # Same as
+ #
+ # Bundler::FileUtils.ln_s(*args, force: true)
+ #
+ def ln_sf(src, dest, noop: nil, verbose: nil)
+ ln_s src, dest, force: true, noop: noop, verbose: verbose
+ end
+ module_function :ln_sf
+
+ #
+ # Copies a file content +src+ to +dest+. If +dest+ is a directory,
+ # copies +src+ to +dest/src+.
+ #
+ # If +src+ is a list of files, then +dest+ must be a directory.
+ #
+ # 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
+ #
+ def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
+ fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
+ return if noop
+ fu_each_src_dest(src, dest) do |s, d|
+ copy_file s, d, preserve
+ end
+ end
+ module_function :cp
+
+ 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.
+ #
+ # # Installing Ruby library "mylib" under the site_ruby
+ # Bundler::FileUtils.rm_r site_ruby + '/mylib', :force
+ # 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.
+ #
+ def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil,
+ dereference_root: true, remove_destination: nil)
+ fu_output_message "cp -r#{preserve ? 'p' : ''}#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
+ return if noop
+ fu_each_src_dest(src, dest) do |s, d|
+ copy_entry s, d, preserve, dereference_root, remove_destination
+ end
+ end
+ module_function :cp_r
+
+ #
+ # 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)
+ #
+ # Both of +src+ and +dest+ must be a path name.
+ # +src+ must exist, +dest+ must not exist.
+ #
+ # If +preserve+ is true, this method preserves owner, group, and
+ # modified time. Permissions are copied regardless +preserve+.
+ #
+ # If +dereference_root+ is true, this method dereference tree root.
+ #
+ # If +remove_destination+ is true, this method removes each destination file before copy.
+ #
+ def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
+ Entry_.new(src, nil, dereference_root).wrap_traverse(proc do |ent|
+ destent = Entry_.new(dest, ent.rel, false)
+ File.unlink destent.path if remove_destination && File.file?(destent.path)
+ ent.copy destent.path
+ end, proc do |ent|
+ destent = Entry_.new(dest, ent.rel, false)
+ ent.copy_metadata destent.path if preserve
+ end)
+ end
+ module_function :copy_entry
+
+ #
+ # Copies file contents of +src+ to +dest+.
+ # Both of +src+ and +dest+ must be a path name.
+ #
+ def copy_file(src, dest, preserve = false, dereference = true)
+ ent = Entry_.new(src, nil, dereference)
+ ent.copy_file dest
+ ent.copy_metadata dest if preserve
+ end
+ module_function :copy_file
+
+ #
+ # Copies stream +src+ to +dest+.
+ # +src+ must respond to #read(n) and
+ # +dest+ must respond to #write(str).
+ #
+ 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
+ #
+ def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
+ fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
+ return if noop
+ fu_each_src_dest(src, dest) do |s, d|
+ destent = Entry_.new(d, nil, true)
+ begin
+ if destent.exist?
+ if destent.directory?
+ raise Errno::EEXIST, d
+ else
+ destent.remove_file if rename_cannot_overwrite_file?
+ end
+ end
+ begin
+ File.rename s, d
+ rescue Errno::EXDEV
+ copy_entry s, d, true
+ if secure
+ remove_entry_secure s, force
+ else
+ remove_entry s, force
+ end
+ end
+ rescue SystemCallError
+ raise unless force
+ end
+ end
+ end
+ module_function :mv
+
+ alias move mv
+ module_function :move
+
+ def rename_cannot_overwrite_file? #:nodoc:
+ /emx/ =~ RUBY_PLATFORM
+ end
+ private_module_function :rename_cannot_overwrite_file?
+
+ #
+ # Remove file(s) specified in +list+. This method cannot remove directories.
+ # All StandardErrors are ignored when the :force option is set.
+ #
+ # Bundler::FileUtils.rm %w( junk.txt dust.txt )
+ # Bundler::FileUtils.rm Dir.glob('*.so')
+ # Bundler::FileUtils.rm 'NotExistFile', :force => true # never raises exception
+ #
+ def rm(list, force: nil, noop: nil, verbose: nil)
+ list = fu_list(list)
+ fu_output_message "rm#{force ? ' -f' : ''} #{list.join ' '}" if verbose
+ return if noop
+
+ list.each do |path|
+ remove_file path, force
+ end
+ end
+ module_function :rm
+
+ alias remove rm
+ module_function :remove
+
+ #
+ # Equivalent to
+ #
+ # Bundler::FileUtils.rm(list, :force => true)
+ #
+ def rm_f(list, noop: nil, verbose: nil)
+ rm list, force: true, noop: noop, verbose: verbose
+ end
+ module_function :rm_f
+
+ alias safe_unlink rm_f
+ module_function :safe_unlink
+
+ #
+ # 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.
+ #
+ # Bundler::FileUtils.rm_r Dir.glob('/tmp/*')
+ # Bundler::FileUtils.rm_r 'some_dir', :force => true
+ #
+ # 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 :secure=>false.
+ #
+ # NOTE: This method calls #remove_entry_secure if :secure option is set.
+ # See also #remove_entry_secure.
+ #
+ 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
+ return if noop
+ list.each do |path|
+ if secure
+ remove_entry_secure path, force
+ else
+ remove_entry path, force
+ end
+ end
+ end
+ module_function :rm_r
+
+ #
+ # Equivalent to
+ #
+ # Bundler::FileUtils.rm_r(list, :force => true)
+ #
+ # WARNING: This method causes local vulnerability.
+ # Read the documentation of #rm_r first.
+ #
+ def rm_rf(list, noop: nil, verbose: nil, secure: nil)
+ rm_r list, force: true, noop: noop, verbose: verbose, secure: secure
+ end
+ module_function :rm_rf
+
+ alias rmtree rm_rf
+ module_function :rmtree
+
+ #
+ # 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.
+ #
+ # 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).
+ #
+ # 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.
+ #
+ # 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:
+ #
+ # * http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448
+ # * http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452
+ #
+ # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100].
+ #
+ def remove_entry_secure(path, force = false)
+ unless fu_have_symlink?
+ remove_entry path, force
+ return
+ end
+ fullpath = File.expand_path(path)
+ st = File.lstat(fullpath)
+ unless st.directory?
+ File.unlink fullpath
+ return
+ end
+ # is a directory.
+ parent_st = File.stat(File.dirname(fullpath))
+ unless parent_st.world_writable?
+ remove_entry path, force
+ return
+ end
+ unless parent_st.sticky?
+ raise ArgumentError, "parent directory is world writable, Bundler::FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})"
+ end
+ # freeze tree root
+ euid = Process.euid
+ File.open(fullpath + '/.') {|f|
+ unless fu_stat_identical_entry?(st, f.stat)
+ # symlink (TOC-to-TOU attack?)
+ File.unlink fullpath
+ return
+ end
+ f.chown euid, -1
+ f.chmod 0700
+ unless fu_stat_identical_entry?(st, File.lstat(fullpath))
+ # TOC-to-TOU attack?
+ File.unlink fullpath
+ return
+ end
+ }
+ # ---- tree root is frozen ----
+ root = Entry_.new(path)
+ root.preorder_traverse do |ent|
+ if ent.directory?
+ ent.chown euid, -1
+ ent.chmod 0700
+ end
+ end
+ root.postorder_traverse do |ent|
+ begin
+ ent.remove
+ rescue
+ raise unless force
+ end
+ end
+ rescue
+ raise unless force
+ end
+ module_function :remove_entry_secure
+
+ def fu_have_symlink? #:nodoc:
+ File.symlink nil, nil
+ rescue NotImplementedError
+ return false
+ rescue TypeError
+ return true
+ end
+ private_module_function :fu_have_symlink?
+
+ def fu_stat_identical_entry?(a, b) #:nodoc:
+ a.dev == b.dev and a.ino == b.ino
+ end
+ private_module_function :fu_stat_identical_entry?
+
+ #
+ # 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.
+ #
+ # See also #remove_entry_secure.
+ #
+ def remove_entry(path, force = false)
+ Entry_.new(path).postorder_traverse do |ent|
+ begin
+ ent.remove
+ rescue
+ raise unless force
+ end
+ end
+ rescue
+ raise unless force
+ end
+ module_function :remove_entry
+
+ #
+ # Removes a file +path+.
+ # This method ignores StandardError if +force+ is true.
+ #
+ def remove_file(path, force = false)
+ Entry_.new(path).remove_file
+ rescue
+ raise unless force
+ end
+ module_function :remove_file
+
+ #
+ # Removes a directory +dir+ and its contents recursively.
+ # This method ignores StandardError if +force+ is true.
+ #
+ 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 a file +a+ and a file +b+ are identical.
+ #
+ # Bundler::FileUtils.compare_file('somefile', 'somefile') #=> true
+ # Bundler::FileUtils.compare_file('/dev/null', '/dev/urandom') #=> false
+ #
+ def compare_file(a, b)
+ return false unless File.size(a) == File.size(b)
+ File.open(a, 'rb') {|fa|
+ File.open(b, 'rb') {|fb|
+ return compare_stream(fa, fb)
+ }
+ }
+ end
+ module_function :compare_file
+
+ alias identical? compare_file
+ alias cmp compare_file
+ module_function :identical?
+ module_function :cmp
+
+ #
+ # Returns true if the contents of a stream +a+ and +b+ are identical.
+ #
+ def compare_stream(a, b)
+ bsize = fu_stream_blksize(a, b)
+ sa = String.new(capacity: bsize)
+ sb = String.new(capacity: bsize)
+ begin
+ a.read(bsize, sa)
+ b.read(bsize, sb)
+ return true if sa.empty? && sb.empty?
+ end while sa == sb
+ false
+ end
+ module_function :compare_stream
+
+ #
+ # 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.
+ #
+ # 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
+ #
+ def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil,
+ noop: nil, verbose: nil)
+ if verbose
+ msg = +"install -c"
+ msg << ' -p' if preserve
+ msg << ' -m ' << mode_to_s(mode) if mode
+ msg << " -o #{owner}" if owner
+ msg << " -g #{group}" if group
+ msg << ' ' << [src,dest].flatten.join(' ')
+ fu_output_message msg
+ end
+ return if noop
+ uid = fu_get_uid(owner)
+ gid = fu_get_gid(group)
+ fu_each_src_dest(src, dest) do |s, d|
+ st = File.stat(s)
+ unless File.exist?(d) and compare_file(s, d)
+ remove_file d, true
+ copy_file s, d
+ File.utime st.atime, st.mtime, d if preserve
+ File.chmod fu_mode(mode, st), d if mode
+ File.chown uid, gid, d if uid or gid
+ end
+ end
+ end
+ module_function :install
+
+ def user_mask(target) #:nodoc:
+ target.each_char.inject(0) do |mask, chr|
+ case chr
+ when "u"
+ mask | 04700
+ when "g"
+ mask | 02070
+ when "o"
+ mask | 01007
+ when "a"
+ mask | 07777
+ else
+ raise ArgumentError, "invalid `who' symbol in file mode: #{chr}"
+ end
+ end
+ end
+ private_module_function :user_mask
+
+ def apply_mask(mode, user_mask, op, mode_mask) #:nodoc:
+ case op
+ when '='
+ (mode & ~user_mask) | (user_mask & mode_mask)
+ when '+'
+ mode | (user_mask & mode_mask)
+ when '-'
+ mode & ~(user_mask & mode_mask)
+ end
+ end
+ 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
+ mode_sym.split(/,/).inject(mode & 07777) do |current_mode, clause|
+ target, *actions = clause.split(/([=+-])/)
+ raise ArgumentError, "invalid file mode: #{mode_sym}" if actions.empty?
+ target = 'a' if target.empty?
+ user_mask = user_mask(target)
+ actions.each_slice(2) do |op, perm|
+ need_apply = op == '='
+ mode_mask = (perm || '').each_char.inject(0) do |mask, chr|
+ case chr
+ when "r"
+ mask | 0444
+ when "w"
+ mask | 0222
+ when "x"
+ mask | 0111
+ when "X"
+ if FileTest.directory? path
+ mask | 0111
+ else
+ mask
+ end
+ when "s"
+ mask | 06000
+ when "t"
+ mask | 01000
+ when "u", "g", "o"
+ if mask.nonzero?
+ current_mode = apply_mask(current_mode, user_mask, op, mask)
+ end
+ need_apply = false
+ copy_mask = user_mask(chr)
+ (current_mode & copy_mask) / (copy_mask & 0111) * (user_mask & 0111)
+ else
+ raise ArgumentError, "invalid `perm' symbol in file mode: #{chr}"
+ end
+ end
+
+ if mode_mask.nonzero? || need_apply
+ current_mode = apply_mask(current_mode, user_mask, op, mode_mask)
+ end
+ end
+ current_mode
+ end
+ end
+ private_module_function :symbolic_modes_to_i
+
+ def fu_mode(mode, path) #:nodoc:
+ mode.is_a?(String) ? symbolic_modes_to_i(mode, path) : mode
+ end
+ private_module_function :fu_mode
+
+ def mode_to_s(mode) #:nodoc:
+ mode.is_a?(String) ? mode : "%o" % mode
+ end
+ private_module_function :mode_to_s
+
+ #
+ # 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
+ return if noop
+ list.each do |path|
+ Entry_.new(path).chmod(fu_mode(mode, path))
+ end
+ 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.#{$$}"
+ #
+ def chmod_R(mode, list, noop: nil, verbose: nil, force: nil)
+ list = fu_list(list)
+ fu_output_message sprintf('chmod -R%s %s %s',
+ (force ? 'f' : ''),
+ mode_to_s(mode), list.join(' ')) if verbose
+ return if noop
+ list.each do |root|
+ Entry_.new(root).traverse do |ent|
+ begin
+ ent.chmod(fu_mode(mode, ent.path))
+ rescue
+ raise unless force
+ end
+ end
+ end
+ end
+ module_function :chmod_R
+
+ #
+ # 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.
+ #
+ # Bundler::FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
+ # Bundler::FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), :verbose => true
+ #
+ def chown(user, group, list, noop: nil, verbose: nil)
+ list = fu_list(list)
+ fu_output_message sprintf('chown %s %s',
+ (group ? "#{user}:#{group}" : user || ':'),
+ list.join(' ')) if verbose
+ return if noop
+ uid = fu_get_uid(user)
+ gid = fu_get_gid(group)
+ list.each do |path|
+ Entry_.new(path).chown uid, gid
+ end
+ 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
+ #
+ def chown_R(user, group, list, noop: nil, verbose: nil, force: nil)
+ list = fu_list(list)
+ fu_output_message sprintf('chown -R%s %s %s',
+ (force ? 'f' : ''),
+ (group ? "#{user}:#{group}" : user || ':'),
+ list.join(' ')) if verbose
+ return if noop
+ uid = fu_get_uid(user)
+ gid = fu_get_gid(group)
+ list.each do |root|
+ Entry_.new(root).traverse do |ent|
+ begin
+ ent.chown uid, gid
+ rescue
+ raise unless force
+ end
+ end
+ end
+ end
+ module_function :chown_R
+
+ begin
+ require 'etc'
+ rescue LoadError # rescue LoadError for miniruby
+ end
+
+ def fu_get_uid(user) #:nodoc:
+ return nil unless user
+ case user
+ when Integer
+ user
+ when /\A\d+\z/
+ user.to_i
+ else
+ Etc.getpwnam(user) ? Etc.getpwnam(user).uid : nil
+ end
+ end
+ private_module_function :fu_get_uid
+
+ def fu_get_gid(group) #:nodoc:
+ return nil unless group
+ case group
+ when Integer
+ group
+ when /\A\d+\z/
+ group.to_i
+ else
+ Etc.getgrnam(group) ? Etc.getgrnam(group).gid : nil
+ end
+ end
+ private_module_function :fu_get_gid
+
+ #
+ # Updates modification time (mtime) and access time (atime) of file(s) in
+ # +list+. Files are created if they don't exist.
+ #
+ # Bundler::FileUtils.touch 'timestamp'
+ # Bundler::FileUtils.touch Dir.glob('*.c'); system 'make'
+ #
+ def touch(list, noop: nil, verbose: nil, mtime: nil, nocreate: nil)
+ list = fu_list(list)
+ t = mtime
+ if verbose
+ fu_output_message "touch #{nocreate ? '-c ' : ''}#{t ? t.strftime('-t %Y%m%d%H%M.%S ') : ''}#{list.join ' '}"
+ end
+ return if noop
+ list.each do |path|
+ created = nocreate
+ begin
+ File.utime(t, t, path)
+ rescue Errno::ENOENT
+ raise if created
+ File.open(path, 'a') {
+ ;
+ }
+ created = true
+ retry if t
+ end
+ end
+ end
+ module_function :touch
+
+ private
+
+ module StreamUtils_
+ private
+
+ def fu_windows?
+ /mswin|mingw|bccwin|emx/ =~ RUBY_PLATFORM
+ end
+
+ def fu_copy_stream0(src, dest, blksize = nil) #:nodoc:
+ IO.copy_stream(src, dest)
+ end
+
+ def fu_stream_blksize(*streams)
+ streams.each do |s|
+ next unless s.respond_to?(:stat)
+ size = fu_blksize(s.stat)
+ return size if size
+ end
+ fu_default_blksize()
+ end
+
+ def fu_blksize(st)
+ s = st.blksize
+ return nil unless s
+ return nil if s == 0
+ s
+ end
+
+ def fu_default_blksize
+ 1024
+ end
+ end
+
+ include StreamUtils_
+ extend StreamUtils_
+
+ class Entry_ #:nodoc: internal use only
+ include StreamUtils_
+
+ def initialize(a, b = nil, deref = false)
+ @prefix = @rel = @path = nil
+ if b
+ @prefix = a
+ @rel = b
+ else
+ @path = a
+ end
+ @deref = deref
+ @stat = nil
+ @lstat = nil
+ end
+
+ def inspect
+ "\#<#{self.class} #{path()}>"
+ end
+
+ def path
+ if @path
+ File.path(@path)
+ else
+ join(@prefix, @rel)
+ end
+ end
+
+ def prefix
+ @prefix || @path
+ end
+
+ def rel
+ @rel
+ end
+
+ def dereference?
+ @deref
+ end
+
+ def exist?
+ begin
+ lstat
+ true
+ rescue Errno::ENOENT
+ false
+ end
+ end
+
+ def file?
+ s = lstat!
+ s and s.file?
+ end
+
+ def directory?
+ s = lstat!
+ s and s.directory?
+ end
+
+ def symlink?
+ s = lstat!
+ s and s.symlink?
+ end
+
+ def chardev?
+ s = lstat!
+ s and s.chardev?
+ end
+
+ def blockdev?
+ s = lstat!
+ s and s.blockdev?
+ end
+
+ def socket?
+ s = lstat!
+ s and s.socket?
+ end
+
+ def pipe?
+ s = lstat!
+ s and s.pipe?
+ end
+
+ S_IF_DOOR = 0xD000
+
+ def door?
+ s = lstat!
+ s and (s.mode & 0xF000 == S_IF_DOOR)
+ end
+
+ def entries
+ opts = {}
+ opts[:encoding] = ::Encoding::UTF_8 if fu_windows?
+ Dir.entries(path(), opts)\
+ .reject {|n| n == '.' or n == '..' }\
+ .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) }
+ end
+
+ def stat
+ return @stat if @stat
+ if lstat() and lstat().symlink?
+ @stat = File.stat(path())
+ else
+ @stat = lstat()
+ end
+ @stat
+ end
+
+ def stat!
+ return @stat if @stat
+ if lstat! and lstat!.symlink?
+ @stat = File.stat(path())
+ else
+ @stat = lstat!
+ end
+ @stat
+ rescue SystemCallError
+ nil
+ end
+
+ def lstat
+ if dereference?
+ @lstat ||= File.stat(path())
+ else
+ @lstat ||= File.lstat(path())
+ end
+ end
+
+ def lstat!
+ lstat()
+ rescue SystemCallError
+ nil
+ end
+
+ def chmod(mode)
+ if symlink?
+ File.lchmod mode, path() if have_lchmod?
+ else
+ File.chmod mode, path()
+ end
+ end
+
+ def chown(uid, gid)
+ if symlink?
+ File.lchown uid, gid, path() if have_lchown?
+ else
+ File.chown uid, gid, path()
+ end
+ end
+
+ def copy(dest)
+ lstat
+ case
+ when file?
+ copy_file dest
+ when directory?
+ if !File.exist?(dest) and descendant_directory?(dest, path)
+ raise ArgumentError, "cannot copy directory %s to itself %s" % [path, dest]
+ end
+ begin
+ Dir.mkdir dest
+ rescue
+ raise unless File.directory?(dest)
+ end
+ when symlink?
+ File.symlink File.readlink(path()), dest
+ when chardev?
+ raise "cannot handle device file" unless File.respond_to?(:mknod)
+ mknod dest, ?c, 0666, lstat().rdev
+ when blockdev?
+ raise "cannot handle device file" unless File.respond_to?(:mknod)
+ mknod dest, ?b, 0666, lstat().rdev
+ when socket?
+ raise "cannot handle socket" unless File.respond_to?(:mknod)
+ mknod dest, nil, lstat().mode, 0
+ when pipe?
+ raise "cannot handle FIFO" unless File.respond_to?(:mkfifo)
+ mkfifo dest, 0666
+ when door?
+ raise "cannot handle door: #{path()}"
+ else
+ raise "unknown file type: #{path()}"
+ end
+ end
+
+ def copy_file(dest)
+ File.open(path()) do |s|
+ File.open(dest, 'wb', s.stat.mode) do |f|
+ IO.copy_stream(s, f)
+ end
+ end
+ end
+
+ def copy_metadata(path)
+ st = lstat()
+ if !st.symlink?
+ File.utime st.atime, st.mtime, path
+ end
+ mode = st.mode
+ begin
+ if st.symlink?
+ begin
+ File.lchown st.uid, st.gid, path
+ rescue NotImplementedError
+ end
+ else
+ File.chown st.uid, st.gid, path
+ end
+ rescue Errno::EPERM, Errno::EACCES
+ # clear setuid/setgid
+ mode &= 01777
+ end
+ if st.symlink?
+ begin
+ File.lchmod mode, path
+ rescue NotImplementedError
+ end
+ else
+ File.chmod mode, path
+ end
+ end
+
+ def remove
+ if directory?
+ remove_dir1
+ else
+ remove_file
+ end
+ end
+
+ def remove_dir1
+ platform_support {
+ Dir.rmdir path().chomp(?/)
+ }
+ end
+
+ def remove_file
+ platform_support {
+ File.unlink path
+ }
+ end
+
+ def platform_support
+ return yield unless fu_windows?
+ first_time_p = true
+ begin
+ yield
+ rescue Errno::ENOENT
+ raise
+ rescue => err
+ if first_time_p
+ first_time_p = false
+ begin
+ File.chmod 0700, path() # Windows does not have symlink
+ retry
+ rescue SystemCallError
+ end
+ end
+ raise err
+ end
+ end
+
+ def preorder_traverse
+ stack = [self]
+ while ent = stack.pop
+ yield ent
+ stack.concat ent.entries.reverse if ent.directory?
+ end
+ end
+
+ alias traverse preorder_traverse
+
+ def postorder_traverse
+ if directory?
+ entries().each do |ent|
+ ent.postorder_traverse do |e|
+ yield e
+ end
+ end
+ end
+ ensure
+ yield self
+ end
+
+ def wrap_traverse(pre, post)
+ pre.call self
+ if directory?
+ entries.each do |ent|
+ ent.wrap_traverse pre, post
+ end
+ end
+ post.call self
+ end
+
+ private
+
+ $fileutils_rb_have_lchmod = nil
+
+ def have_lchmod?
+ # This is not MT-safe, but it does not matter.
+ if $fileutils_rb_have_lchmod == nil
+ $fileutils_rb_have_lchmod = check_have_lchmod?
+ end
+ $fileutils_rb_have_lchmod
+ end
+
+ def check_have_lchmod?
+ return false unless File.respond_to?(:lchmod)
+ File.lchmod 0
+ return true
+ rescue NotImplementedError
+ return false
+ end
+
+ $fileutils_rb_have_lchown = nil
+
+ def have_lchown?
+ # This is not MT-safe, but it does not matter.
+ if $fileutils_rb_have_lchown == nil
+ $fileutils_rb_have_lchown = check_have_lchown?
+ end
+ $fileutils_rb_have_lchown
+ end
+
+ def check_have_lchown?
+ return false unless File.respond_to?(:lchown)
+ File.lchown nil, nil
+ return true
+ rescue NotImplementedError
+ return false
+ end
+
+ 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)
+ end
+
+ if File::ALT_SEPARATOR
+ DIRECTORY_TERM = "(?=[/#{Regexp.quote(File::ALT_SEPARATOR)}]|\\z)"
+ else
+ DIRECTORY_TERM = "(?=/|\\z)"
+ end
+ SYSCASE = File::FNM_SYSCASE.nonzero? ? "-i" : ""
+
+ def descendant_directory?(descendant, ascendant)
+ /\A(?#{SYSCASE}:#{Regexp.quote(ascendant)})#{DIRECTORY_TERM}/ =~ File.dirname(descendant)
+ end
+ end # class Entry_
+
+ def fu_list(arg) #:nodoc:
+ [arg].flatten.map {|path| File.path(path) }
+ end
+ private_module_function :fu_list
+
+ def fu_each_src_dest(src, dest) #:nodoc:
+ fu_each_src_dest0(src, dest) do |s, d|
+ raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, d)
+ yield s, d
+ end
+ end
+ private_module_function :fu_each_src_dest
+
+ def fu_each_src_dest0(src, dest) #:nodoc:
+ if tmp = Array.try_convert(src)
+ tmp.each do |s|
+ s = File.path(s)
+ yield s, File.join(dest, File.basename(s))
+ end
+ else
+ src = File.path(src)
+ if File.directory?(dest)
+ yield src, File.join(dest, File.basename(src))
+ else
+ yield src, File.path(dest)
+ end
+ end
+ end
+ private_module_function :fu_each_src_dest0
+
+ def fu_same?(a, b) #:nodoc:
+ File.identical?(a, b)
+ end
+ private_module_function :fu_same?
+
+ @fileutils_output = $stderr
+ @fileutils_label = ''
+
+ def fu_output_message(msg) #:nodoc:
+ @fileutils_output ||= $stderr
+ @fileutils_label ||= ''
+ @fileutils_output.puts @fileutils_label + msg
+ end
+ private_module_function :fu_output_message
+
+ # This hash table holds command options.
+ OPT_TABLE = {} #:nodoc: internal use only
+ (private_instance_methods & methods(false)).inject(OPT_TABLE) {|tbl, name|
+ (tbl[name.to_s] = instance_method(name).parameters).map! {|t, n| n if t == :key}.compact!
+ tbl
+ }
+
+ #
+ # Returns an Array of method names which have any options.
+ #
+ # p Bundler::FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...]
+ #
+ def self.commands
+ OPT_TABLE.keys
+ end
+
+ #
+ # Returns an Array of option names.
+ #
+ # p Bundler::FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"]
+ #
+ def self.options
+ OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s }
+ end
+
+ #
+ # 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
+ #
+ 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 option names of the method +mid+.
+ #
+ # p Bundler::FileUtils.options_of(:rm) #=> ["noop", "verbose", "force"]
+ #
+ def self.options_of(mid)
+ OPT_TABLE[mid.to_s].map {|sym| sym.to_s }
+ end
+
+ #
+ # Returns an Array of method names which have the option +opt+.
+ #
+ # p Bundler::FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
+ #
+ def self.collect_method(opt)
+ OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }
+ end
+
+ LOW_METHODS = singleton_methods(false) - collect_method(:noop).map(&:intern)
+ module LowMethods
+ private
+ def _do_nothing(*)end
+ ::Bundler::FileUtils::LOW_METHODS.map {|name| alias_method name, :_do_nothing}
+ end
+
+ METHODS = singleton_methods() - [:private_module_function,
+ :commands, :options, :have_option?, :options_of, :collect_method]
+
+ #
+ # 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.
+ #
+ module Verbose
+ include Bundler::FileUtils
+ @fileutils_output = $stderr
+ @fileutils_label = ''
+ names = ::Bundler::FileUtils.collect_method(:verbose)
+ names.each do |name|
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{name}(*args, **options)
+ super(*args, **options, verbose: true)
+ end
+ EOS
+ end
+ private(*names)
+ extend self
+ class << self
+ public(*::Bundler::FileUtils::METHODS)
+ end
+ end
+
+ #
+ # 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.
+ #
+ module NoWrite
+ include Bundler::FileUtils
+ include LowMethods
+ @fileutils_output = $stderr
+ @fileutils_label = ''
+ names = ::Bundler::FileUtils.collect_method(:noop)
+ names.each do |name|
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{name}(*args, **options)
+ super(*args, **options, noop: true)
+ end
+ EOS
+ end
+ private(*names)
+ extend self
+ class << self
+ public(*::Bundler::FileUtils::METHODS)
+ end
+ end
+
+ #
+ # This module has all methods of Bundler::FileUtils module, but never changes
+ # files/directories, with printing message before acting.
+ # This equates to passing the <tt>:noop</tt> and <tt>:verbose</tt> flag
+ # to methods in Bundler::FileUtils.
+ #
+ module DryRun
+ include Bundler::FileUtils
+ include LowMethods
+ @fileutils_output = $stderr
+ @fileutils_label = ''
+ names = ::Bundler::FileUtils.collect_method(:noop)
+ names.each do |name|
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{name}(*args, **options)
+ super(*args, **options, noop: true, verbose: true)
+ end
+ EOS
+ end
+ private(*names)
+ extend self
+ class << self
+ public(*::Bundler::FileUtils::METHODS)
+ end
+ end
+
+end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo.rb b/lib/bundler/vendor/molinillo/lib/molinillo.rb
new file mode 100644
index 0000000000..9e2867144f
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+require 'bundler/vendor/molinillo/lib/molinillo/compatibility'
+require 'bundler/vendor/molinillo/lib/molinillo/gem_metadata'
+require 'bundler/vendor/molinillo/lib/molinillo/errors'
+require 'bundler/vendor/molinillo/lib/molinillo/resolver'
+require 'bundler/vendor/molinillo/lib/molinillo/modules/ui'
+require 'bundler/vendor/molinillo/lib/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/compatibility.rb b/lib/bundler/vendor/molinillo/lib/molinillo/compatibility.rb
new file mode 100644
index 0000000000..3eba8e4083
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/compatibility.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Bundler::Molinillo
+ # Hacks needed for old Ruby versions.
+ module Compatibility
+ module_function
+
+ if [].respond_to?(:flat_map)
+ # Flat map
+ # @param [Enumerable] enum an enumerable object
+ # @block the block to flat-map with
+ # @return The enum, flat-mapped
+ def flat_map(enum, &blk)
+ enum.flat_map(&blk)
+ end
+ else
+ # Flat map
+ # @param [Enumerable] enum an enumerable object
+ # @block the block to flat-map with
+ # @return The enum, flat-mapped
+ def flat_map(enum, &blk)
+ enum.map(&blk).flatten(1)
+ end
+ end
+ end
+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
new file mode 100644
index 0000000000..bcacf35243
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb
@@ -0,0 +1,57 @@
+# 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
new file mode 100644
index 0000000000..ec9c770a28
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb
@@ -0,0 +1,81 @@
+# 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#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
new file mode 100644
index 0000000000..677a8bd916
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb
@@ -0,0 +1,223 @@
+# frozen_string_literal: true
+
+require 'set'
+require 'tsort'
+
+require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/log'
+require 'bundler/vendor/molinillo/lib/molinillo/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 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)
+ 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
+
+ # @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([origin, destination])
+ 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
+ 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
new file mode 100644
index 0000000000..c04c7eec9c
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb
@@ -0,0 +1,36 @@
+# 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
new file mode 100644
index 0000000000..9849aea2fe
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/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
new file mode 100644
index 0000000000..0a1e08255b
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/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
new file mode 100644
index 0000000000..1d9f4b327d
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/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
new file mode 100644
index 0000000000..385dcbdd06
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/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
new file mode 100644
index 0000000000..8582dd19c1
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb
@@ -0,0 +1,126 @@
+# frozen_string_literal: true
+
+require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular'
+require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex'
+require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge'
+require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named'
+require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload'
+require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/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
new file mode 100644
index 0000000000..37286d104a
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/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
new file mode 100644
index 0000000000..d6ad16e07a
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/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
new file mode 100644
index 0000000000..7ecdc4b65a
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb
@@ -0,0 +1,136 @@
+# 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 [Array<Vertex>] the vertices of {#graph} where `self` is a
+ # {#descendent?}
+ def recursive_predecessors
+ vertices = predecessors
+ vertices += Compatibility.flat_map(vertices, &:recursive_predecessors)
+ vertices.uniq!
+ vertices
+ end
+
+ # @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 [Array<Vertex>] the vertices of {#graph} where `self` is an
+ # {#ancestor?}
+ def recursive_successors
+ vertices = successors
+ vertices += Compatibility.flat_map(vertices, &:recursive_successors)
+ vertices.uniq!
+ vertices
+ end
+
+ # @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 true iff 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 = Set.new)
+ 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 true iff there is a path following edges within this {#graph}
+ def ancestor?(other)
+ other.path_to?(self)
+ end
+
+ alias is_reachable_from? ancestor?
+ end
+ end
+end
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb b/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb
new file mode 100644
index 0000000000..ce0931f103
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb
@@ -0,0 +1,143 @@
+# 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 iff 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 = []
+ Compatibility.flat_map(conflicts.values.flatten, &: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 'bundler/vendor/molinillo/lib/molinillo/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
+
+ 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 << req.to_s
+ 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.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
new file mode 100644
index 0000000000..73f8fbf2ac
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module Bundler::Molinillo
+ # The version of Bundler::Molinillo.
+ VERSION = '0.6.6'.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
new file mode 100644
index 0000000000..fa094c1981
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+module Bundler::Molinillo
+ # Provides information about specifcations 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
+
+ # 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
new file mode 100644
index 0000000000..a166bc6991
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb
@@ -0,0 +1,67 @@
+# 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
new file mode 100644
index 0000000000..0eb665d17a
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb
@@ -0,0 +1,837 @@
+# 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
+
+ handle_missing_or_push_dependency_state(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 'bundler/vendor/molinillo/lib/molinillo/state'
+ require 'bundler/vendor/molinillo/lib/molinillo/modules/specification_provider'
+
+ require 'bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state'
+ require 'bundler/vendor/molinillo/lib/molinillo/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 the initial state for the resolution, based upon the
+ # {#requested} dependencies
+ # @return [DependencyState] the initial state for the resolution
+ def 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
+
+ requirements = sort_dependencies(original_requested, graph, {})
+ initial_requirement = requirements.shift
+ DependencyState.new(
+ initial_requirement && name_for(initial_requirement),
+ requirements,
+ graph,
+ initial_requirement,
+ possibilities_for_requirement(initial_requirement, graph),
+ 0,
+ {},
+ []
+ )
+ 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
+ relevant_unused_unwinds = unused_unwind_options.select do |alternative|
+ intersecting_requirements =
+ last_detail_for_current_unwind.all_requirements &
+ alternative.requirements_unwound_to_instead
+ next if intersecting_requirements.empty?
+ # 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 { |d| d.requirements_unwound_to_instead << current_detail.state_requirement }
+ unwind_details.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement }
+
+ current_detail
+ end
+
+ # @param [Array<Object>] 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] 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] 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] 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] 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 = Compatibility.flat_map(primary_unwinds) 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 = Compatibility.flat_map(parent_unwinds, &: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] array of requirements
+ # @param [Array] 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
+
+ # @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
+
+ # @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
+
+ # @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
+
+ # @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
+
+ # @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
+ # use reject!(!satisfied) for 1.8.7 compatibility
+ possibility.possibilities.reject! 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] 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] activated_possibility 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
+ # @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] the proposed requirement
+ # @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] the proposed requirement
+ # @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] an array of possibilities
+ # @return [Array] 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 && 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
new file mode 100644
index 0000000000..7d36858778
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'bundler/vendor/molinillo/lib/molinillo/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 'bundler/vendor/molinillo/lib/molinillo/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
new file mode 100644
index 0000000000..68fa1f54e3
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/state.rb
@@ -0,0 +1,58 @@
+# 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/net-http-persistent/lib/net/http/faster.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/faster.rb
new file mode 100644
index 0000000000..e5e09080c2
--- /dev/null
+++ b/lib/bundler/vendor/net-http-persistent/lib/net/http/faster.rb
@@ -0,0 +1,27 @@
+require 'net/protocol'
+
+##
+# Aaron Patterson's monkeypatch (accepted into 1.9.1) to fix Net::HTTP's speed
+# problems.
+#
+# http://gist.github.com/251244
+
+class Net::BufferedIO #:nodoc:
+ alias :old_rbuf_fill :rbuf_fill
+
+ def rbuf_fill
+ if @io.respond_to? :read_nonblock then
+ begin
+ @rbuf << @io.read_nonblock(65536)
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN => e
+ retry if IO.select [@io], nil, nil, @read_timeout
+ raise Timeout::Error, e.message
+ end
+ else # SSL sockets do not have read_nonblock
+ timeout @read_timeout do
+ @rbuf << @io.sysread(65536)
+ end
+ end
+ end
+end if RUBY_VERSION < '1.9'
+
diff --git a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb
new file mode 100644
index 0000000000..7cbca5bc06
--- /dev/null
+++ b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb
@@ -0,0 +1,1233 @@
+require 'net/http'
+begin
+ require 'net/https'
+rescue LoadError
+ # net/https or openssl
+end if RUBY_VERSION < '1.9' # but only for 1.8
+require 'bundler/vendor/net-http-persistent/lib/net/http/faster'
+require 'uri'
+require 'cgi' # for escaping
+
+begin
+ require 'net/http/pipeline'
+rescue LoadError
+end
+
+autoload :OpenSSL, 'openssl'
+
+##
+# Persistent connections for Net::HTTP
+#
+# Bundler::Persistent::Net::HTTP::Persistent maintains persistent connections across all the
+# servers you wish to talk to. For each host:port you communicate with a
+# single persistent connection is created.
+#
+# Multiple Bundler::Persistent::Net::HTTP::Persistent objects will share the same set of
+# connections.
+#
+# For each thread you start a new connection will be created. A
+# Bundler::Persistent::Net::HTTP::Persistent connection will not be shared across threads.
+#
+# You can shut down the HTTP connections when done by calling #shutdown. You
+# should name your Bundler::Persistent::Net::HTTP::Persistent object if you intend to call this
+# method.
+#
+# Example:
+#
+# require 'bundler/vendor/net-http-persistent/lib/net/http/persistent'
+#
+# uri = URI 'http://example.com/awesome/web/service'
+#
+# http = Bundler::Persistent::Net::HTTP::Persistent.new 'my_app_name'
+#
+# # perform a GET
+# response = http.request uri
+#
+# # or
+#
+# get = Net::HTTP::Get.new uri.request_uri
+# response = http.request get
+#
+# # create a POST
+# post_uri = uri + 'create'
+# post = Net::HTTP::Post.new post_uri.path
+# post.set_form_data 'some' => 'cool data'
+#
+# # perform the POST, the URI is always required
+# response http.request post_uri, post
+#
+# Note that for GET, HEAD and other requests that do not have a body you want
+# to use URI#request_uri not URI#path. The request_uri contains the query
+# params which are sent in the body for other requests.
+#
+# == SSL
+#
+# SSL connections are automatically created depending upon the scheme of the
+# URI. SSL connections are automatically verified against the default
+# certificate store for your computer. You can override this by changing
+# verify_mode or by specifying an alternate cert_store.
+#
+# Here are the SSL settings, see the individual methods for documentation:
+#
+# #certificate :: This client's certificate
+# #ca_file :: The certificate-authority
+# #cert_store :: An SSL certificate store
+# #private_key :: The client's SSL private key
+# #reuse_ssl_sessions :: Reuse a previously opened SSL session for a new
+# connection
+# #ssl_version :: Which specific SSL version to use
+# #verify_callback :: For server certificate verification
+# #verify_mode :: How connections should be verified
+#
+# == Proxies
+#
+# A proxy can be set through #proxy= or at initialization time by providing a
+# second argument to ::new. The proxy may be the URI of the proxy server or
+# <code>:ENV</code> which will consult environment variables.
+#
+# See #proxy= and #proxy_from_env for details.
+#
+# == Headers
+#
+# Headers may be specified for use in every request. #headers are appended to
+# any headers on the request. #override_headers replace existing headers on
+# the request.
+#
+# The difference between the two can be seen in setting the User-Agent. Using
+# <code>http.headers['User-Agent'] = 'MyUserAgent'</code> will send "Ruby,
+# MyUserAgent" while <code>http.override_headers['User-Agent'] =
+# 'MyUserAgent'</code> will send "MyUserAgent".
+#
+# == Tuning
+#
+# === Segregation
+#
+# By providing an application name to ::new you can separate your connections
+# from the connections of other applications.
+#
+# === Idle Timeout
+#
+# If a connection hasn't been used for this number of seconds it will automatically be
+# reset upon the next use to avoid attempting to send to a closed connection.
+# The default value is 5 seconds. nil means no timeout. Set through #idle_timeout.
+#
+# Reducing this value may help avoid the "too many connection resets" error
+# when sending non-idempotent requests while increasing this value will cause
+# fewer round-trips.
+#
+# === Read Timeout
+#
+# The amount of time allowed between reading two chunks from the socket. Set
+# through #read_timeout
+#
+# === Max Requests
+#
+# The number of requests that should be made before opening a new connection.
+# Typically many keep-alive capable servers tune this to 100 or less, so the
+# 101st request will fail with ECONNRESET. If unset (default), this value has no
+# effect, if set, connections will be reset on the request after max_requests.
+#
+# === Open Timeout
+#
+# The amount of time to wait for a connection to be opened. Set through
+# #open_timeout.
+#
+# === Socket Options
+#
+# Socket options may be set on newly-created connections. See #socket_options
+# for details.
+#
+# === Non-Idempotent Requests
+#
+# By default non-idempotent requests will not be retried per RFC 2616. By
+# setting retry_change_requests to true requests will automatically be retried
+# once.
+#
+# Only do this when you know that retrying a POST or other non-idempotent
+# request is safe for your application and will not create duplicate
+# resources.
+#
+# The recommended way to handle non-idempotent requests is the following:
+#
+# require 'bundler/vendor/net-http-persistent/lib/net/http/persistent'
+#
+# uri = URI 'http://example.com/awesome/web/service'
+# post_uri = uri + 'create'
+#
+# http = Bundler::Persistent::Net::HTTP::Persistent.new 'my_app_name'
+#
+# post = Net::HTTP::Post.new post_uri.path
+# # ... fill in POST request
+#
+# begin
+# response = http.request post_uri, post
+# rescue Bundler::Persistent::Net::HTTP::Persistent::Error
+#
+# # POST failed, make a new request to verify the server did not process
+# # the request
+# exists_uri = uri + '...'
+# response = http.get exists_uri
+#
+# # Retry if it failed
+# retry if response.code == '404'
+# end
+#
+# The method of determining if the resource was created or not is unique to
+# the particular service you are using. Of course, you will want to add
+# protection from infinite looping.
+#
+# === Connection Termination
+#
+# If you are done using the Bundler::Persistent::Net::HTTP::Persistent instance you may shut down
+# all the connections in the current thread with #shutdown. This is not
+# recommended for normal use, it should only be used when it will be several
+# minutes before you make another HTTP request.
+#
+# If you are using multiple threads, call #shutdown in each thread when the
+# thread is done making requests. If you don't call shutdown, that's OK.
+# Ruby will automatically garbage collect and shutdown your HTTP connections
+# when the thread terminates.
+
+class Bundler::Persistent::Net::HTTP::Persistent
+
+ ##
+ # The beginning of Time
+
+ EPOCH = Time.at 0 # :nodoc:
+
+ ##
+ # Is OpenSSL available? This test works with autoload
+
+ HAVE_OPENSSL = defined? OpenSSL::SSL # :nodoc:
+
+ ##
+ # The version of Bundler::Persistent::Net::HTTP::Persistent you are using
+
+ VERSION = '2.9.4'
+
+ ##
+ # Exceptions rescued for automatic retry on ruby 2.0.0. This overlaps with
+ # the exception list for ruby 1.x.
+
+ RETRIED_EXCEPTIONS = [ # :nodoc:
+ (Net::ReadTimeout if Net.const_defined? :ReadTimeout),
+ IOError,
+ EOFError,
+ Errno::ECONNRESET,
+ Errno::ECONNABORTED,
+ Errno::EPIPE,
+ (OpenSSL::SSL::SSLError if HAVE_OPENSSL),
+ Timeout::Error,
+ ].compact
+
+ ##
+ # Error class for errors raised by Bundler::Persistent::Net::HTTP::Persistent. Various
+ # SystemCallErrors are re-raised with a human-readable message under this
+ # class.
+
+ class Error < StandardError; end
+
+ ##
+ # Use this method to detect the idle timeout of the host at +uri+. The
+ # value returned can be used to configure #idle_timeout. +max+ controls the
+ # maximum idle timeout to detect.
+ #
+ # After
+ #
+ # Idle timeout detection is performed by creating a connection then
+ # performing a HEAD request in a loop until the connection terminates
+ # waiting one additional second per loop.
+ #
+ # NOTE: This may not work on ruby > 1.9.
+
+ def self.detect_idle_timeout uri, max = 10
+ uri = URI uri unless URI::Generic === uri
+ uri += '/'
+
+ req = Net::HTTP::Head.new uri.request_uri
+
+ http = new 'net-http-persistent detect_idle_timeout'
+
+ connection = http.connection_for uri
+
+ sleep_time = 0
+
+ loop do
+ response = connection.request req
+
+ $stderr.puts "HEAD #{uri} => #{response.code}" if $DEBUG
+
+ unless Net::HTTPOK === response then
+ raise Error, "bad response code #{response.code} detecting idle timeout"
+ end
+
+ break if sleep_time >= max
+
+ sleep_time += 1
+
+ $stderr.puts "sleeping #{sleep_time}" if $DEBUG
+ sleep sleep_time
+ end
+ rescue
+ # ignore StandardErrors, we've probably found the idle timeout.
+ ensure
+ http.shutdown
+
+ return sleep_time unless $!
+ end
+
+ ##
+ # This client's OpenSSL::X509::Certificate
+
+ attr_reader :certificate
+
+ # For Net::HTTP parity
+ alias cert certificate
+
+ ##
+ # An SSL certificate authority. Setting this will set verify_mode to
+ # VERIFY_PEER.
+
+ attr_reader :ca_file
+
+ ##
+ # An SSL certificate store. Setting this will override the default
+ # certificate store. See verify_mode for more information.
+
+ attr_reader :cert_store
+
+ ##
+ # Sends debug_output to this IO via Net::HTTP#set_debug_output.
+ #
+ # Never use this method in production code, it causes a serious security
+ # hole.
+
+ attr_accessor :debug_output
+
+ ##
+ # Current connection generation
+
+ attr_reader :generation # :nodoc:
+
+ ##
+ # Where this instance's connections live in the thread local variables
+
+ attr_reader :generation_key # :nodoc:
+
+ ##
+ # Headers that are added to every request using Net::HTTP#add_field
+
+ attr_reader :headers
+
+ ##
+ # Maps host:port to an HTTP version. This allows us to enable version
+ # specific features.
+
+ attr_reader :http_versions
+
+ ##
+ # Maximum time an unused connection can remain idle before being
+ # automatically closed.
+
+ attr_accessor :idle_timeout
+
+ ##
+ # Maximum number of requests on a connection before it is considered expired
+ # and automatically closed.
+
+ attr_accessor :max_requests
+
+ ##
+ # The value sent in the Keep-Alive header. Defaults to 30. Not needed for
+ # HTTP/1.1 servers.
+ #
+ # This may not work correctly for HTTP/1.0 servers
+ #
+ # This method may be removed in a future version as RFC 2616 does not
+ # require this header.
+
+ attr_accessor :keep_alive
+
+ ##
+ # A name for this connection. Allows you to keep your connections apart
+ # from everybody else's.
+
+ attr_reader :name
+
+ ##
+ # Seconds to wait until a connection is opened. See Net::HTTP#open_timeout
+
+ attr_accessor :open_timeout
+
+ ##
+ # Headers that are added to every request using Net::HTTP#[]=
+
+ attr_reader :override_headers
+
+ ##
+ # This client's SSL private key
+
+ attr_reader :private_key
+
+ # For Net::HTTP parity
+ alias key private_key
+
+ ##
+ # The URL through which requests will be proxied
+
+ attr_reader :proxy_uri
+
+ ##
+ # List of host suffixes which will not be proxied
+
+ attr_reader :no_proxy
+
+ ##
+ # Seconds to wait until reading one block. See Net::HTTP#read_timeout
+
+ attr_accessor :read_timeout
+
+ ##
+ # Where this instance's request counts live in the thread local variables
+
+ attr_reader :request_key # :nodoc:
+
+ ##
+ # By default SSL sessions are reused to avoid extra SSL handshakes. Set
+ # this to false if you have problems communicating with an HTTPS server
+ # like:
+ #
+ # SSL_connect [...] read finished A: unexpected message (OpenSSL::SSL::SSLError)
+
+ attr_accessor :reuse_ssl_sessions
+
+ ##
+ # An array of options for Socket#setsockopt.
+ #
+ # By default the TCP_NODELAY option is set on sockets.
+ #
+ # To set additional options append them to this array:
+ #
+ # http.socket_options << [Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1]
+
+ attr_reader :socket_options
+
+ ##
+ # Current SSL connection generation
+
+ attr_reader :ssl_generation # :nodoc:
+
+ ##
+ # Where this instance's SSL connections live in the thread local variables
+
+ attr_reader :ssl_generation_key # :nodoc:
+
+ ##
+ # SSL version to use.
+ #
+ # By default, the version will be negotiated automatically between client
+ # and server. Ruby 1.9 and newer only.
+
+ attr_reader :ssl_version if RUBY_VERSION > '1.9'
+
+ ##
+ # Where this instance's last-use times live in the thread local variables
+
+ attr_reader :timeout_key # :nodoc:
+
+ ##
+ # SSL verification callback. Used when ca_file is set.
+
+ attr_reader :verify_callback
+
+ ##
+ # HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_PEER which verifies
+ # the server certificate.
+ #
+ # If no ca_file or cert_store is set the default system certificate store is
+ # used.
+ #
+ # You can use +verify_mode+ to override any default values.
+
+ attr_reader :verify_mode
+
+ ##
+ # Enable retries of non-idempotent requests that change data (e.g. POST
+ # requests) when the server has disconnected.
+ #
+ # This will in the worst case lead to multiple requests with the same data,
+ # but it may be useful for some applications. Take care when enabling
+ # this option to ensure it is safe to POST or perform other non-idempotent
+ # requests to the server.
+
+ attr_accessor :retry_change_requests
+
+ ##
+ # Creates a new Bundler::Persistent::Net::HTTP::Persistent.
+ #
+ # Set +name+ to keep your connections apart from everybody else's. Not
+ # required currently, but highly recommended. Your library name should be
+ # good enough. This parameter will be required in a future version.
+ #
+ # +proxy+ may be set to a URI::HTTP or :ENV to pick up proxy options from
+ # the environment. See proxy_from_env for details.
+ #
+ # In order to use a URI for the proxy you may need to do some extra work
+ # beyond URI parsing if the proxy requires a password:
+ #
+ # proxy = URI 'http://proxy.example'
+ # proxy.user = 'AzureDiamond'
+ # proxy.password = 'hunter2'
+
+ def initialize name = nil, proxy = nil
+ @name = name
+
+ @debug_output = nil
+ @proxy_uri = nil
+ @no_proxy = []
+ @headers = {}
+ @override_headers = {}
+ @http_versions = {}
+ @keep_alive = 30
+ @open_timeout = nil
+ @read_timeout = nil
+ @idle_timeout = 5
+ @max_requests = nil
+ @socket_options = []
+
+ @socket_options << [Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1] if
+ Socket.const_defined? :TCP_NODELAY
+
+ key = ['net_http_persistent', name].compact
+ @generation_key = [key, 'generations' ].join('_').intern
+ @ssl_generation_key = [key, 'ssl_generations'].join('_').intern
+ @request_key = [key, 'requests' ].join('_').intern
+ @timeout_key = [key, 'timeouts' ].join('_').intern
+
+ @certificate = nil
+ @ca_file = nil
+ @private_key = nil
+ @ssl_version = nil
+ @verify_callback = nil
+ @verify_mode = nil
+ @cert_store = nil
+
+ @generation = 0 # incremented when proxy URI changes
+ @ssl_generation = 0 # incremented when SSL session variables change
+
+ if HAVE_OPENSSL then
+ @verify_mode = OpenSSL::SSL::VERIFY_PEER
+ @reuse_ssl_sessions = OpenSSL::SSL.const_defined? :Session
+ end
+
+ @retry_change_requests = false
+
+ @ruby_1 = RUBY_VERSION < '2'
+ @retried_on_ruby_2 = !@ruby_1
+
+ self.proxy = proxy if proxy
+ end
+
+ ##
+ # Sets this client's OpenSSL::X509::Certificate
+
+ def certificate= certificate
+ @certificate = certificate
+
+ reconnect_ssl
+ end
+
+ # For Net::HTTP parity
+ alias cert= certificate=
+
+ ##
+ # Sets the SSL certificate authority file.
+
+ def ca_file= file
+ @ca_file = file
+
+ reconnect_ssl
+ end
+
+ ##
+ # Overrides the default SSL certificate store used for verifying
+ # connections.
+
+ def cert_store= store
+ @cert_store = store
+
+ reconnect_ssl
+ end
+
+ ##
+ # Finishes all connections on the given +thread+ that were created before
+ # the given +generation+ in the threads +generation_key+ list.
+ #
+ # See #shutdown for a bunch of scary warning about misusing this method.
+
+ def cleanup(generation, thread = Thread.current,
+ generation_key = @generation_key) # :nodoc:
+ timeouts = thread[@timeout_key]
+
+ (0...generation).each do |old_generation|
+ next unless thread[generation_key]
+
+ conns = thread[generation_key].delete old_generation
+
+ conns.each_value do |conn|
+ finish conn, thread
+
+ timeouts.delete conn.object_id if timeouts
+ end if conns
+ end
+ end
+
+ ##
+ # Creates a new connection for +uri+
+
+ def connection_for uri
+ Thread.current[@generation_key] ||= Hash.new { |h,k| h[k] = {} }
+ Thread.current[@ssl_generation_key] ||= Hash.new { |h,k| h[k] = {} }
+ Thread.current[@request_key] ||= Hash.new 0
+ Thread.current[@timeout_key] ||= Hash.new EPOCH
+
+ use_ssl = uri.scheme.downcase == 'https'
+
+ if use_ssl then
+ raise Bundler::Persistent::Net::HTTP::Persistent::Error, 'OpenSSL is not available' unless
+ HAVE_OPENSSL
+
+ ssl_generation = @ssl_generation
+
+ ssl_cleanup ssl_generation
+
+ connections = Thread.current[@ssl_generation_key][ssl_generation]
+ else
+ generation = @generation
+
+ cleanup generation
+
+ connections = Thread.current[@generation_key][generation]
+ end
+
+ net_http_args = [uri.host, uri.port]
+ connection_id = net_http_args.join ':'
+
+ if @proxy_uri and not proxy_bypass? uri.host, uri.port then
+ connection_id << @proxy_connection_id
+ net_http_args.concat @proxy_args
+ else
+ net_http_args.concat [nil, nil, nil, nil]
+ end
+
+ connection = connections[connection_id]
+
+ unless connection = connections[connection_id] then
+ connections[connection_id] = http_class.new(*net_http_args)
+ connection = connections[connection_id]
+ ssl connection if use_ssl
+ else
+ reset connection if expired? connection
+ end
+
+ start connection unless connection.started?
+
+ connection.read_timeout = @read_timeout if @read_timeout
+ connection.keep_alive_timeout = @idle_timeout if @idle_timeout && connection.respond_to?(:keep_alive_timeout=)
+
+ connection
+ rescue Errno::ECONNREFUSED
+ address = connection.proxy_address || connection.address
+ port = connection.proxy_port || connection.port
+
+ raise Error, "connection refused: #{address}:#{port}"
+ rescue Errno::EHOSTDOWN
+ address = connection.proxy_address || connection.address
+ port = connection.proxy_port || connection.port
+
+ raise Error, "host down: #{address}:#{port}"
+ end
+
+ ##
+ # Returns an error message containing the number of requests performed on
+ # this connection
+
+ def error_message connection
+ requests = Thread.current[@request_key][connection.object_id] - 1 # fixup
+ last_use = Thread.current[@timeout_key][connection.object_id]
+
+ age = Time.now - last_use
+
+ "after #{requests} requests on #{connection.object_id}, " \
+ "last used #{age} seconds ago"
+ end
+
+ ##
+ # URI::escape wrapper
+
+ def escape str
+ CGI.escape str if str
+ end
+
+ ##
+ # URI::unescape wrapper
+
+ def unescape str
+ CGI.unescape str if str
+ end
+
+
+ ##
+ # Returns true if the connection should be reset due to an idle timeout, or
+ # maximum request count, false otherwise.
+
+ def expired? connection
+ requests = Thread.current[@request_key][connection.object_id]
+ return true if @max_requests && requests >= @max_requests
+ return false unless @idle_timeout
+ return true if @idle_timeout.zero?
+
+ last_used = Thread.current[@timeout_key][connection.object_id]
+
+ Time.now - last_used > @idle_timeout
+ end
+
+ ##
+ # Starts the Net::HTTP +connection+
+
+ def start connection
+ connection.set_debug_output @debug_output if @debug_output
+ connection.open_timeout = @open_timeout if @open_timeout
+
+ connection.start
+
+ socket = connection.instance_variable_get :@socket
+
+ if socket then # for fakeweb
+ @socket_options.each do |option|
+ socket.io.setsockopt(*option)
+ end
+ end
+ end
+
+ ##
+ # Finishes the Net::HTTP +connection+
+
+ def finish connection, thread = Thread.current
+ if requests = thread[@request_key] then
+ requests.delete connection.object_id
+ end
+
+ connection.finish
+ rescue IOError
+ end
+
+ def http_class # :nodoc:
+ if RUBY_VERSION > '2.0' then
+ Net::HTTP
+ elsif [:Artifice, :FakeWeb, :WebMock].any? { |klass|
+ Object.const_defined?(klass)
+ } or not @reuse_ssl_sessions then
+ Net::HTTP
+ else
+ Bundler::Persistent::Net::HTTP::Persistent::SSLReuse
+ end
+ end
+
+ ##
+ # Returns the HTTP protocol version for +uri+
+
+ def http_version uri
+ @http_versions["#{uri.host}:#{uri.port}"]
+ end
+
+ ##
+ # Is +req+ idempotent according to RFC 2616?
+
+ def idempotent? req
+ case req
+ when Net::HTTP::Delete, Net::HTTP::Get, Net::HTTP::Head,
+ Net::HTTP::Options, Net::HTTP::Put, Net::HTTP::Trace then
+ true
+ end
+ end
+
+ ##
+ # Is the request +req+ idempotent or is retry_change_requests allowed.
+ #
+ # If +retried_on_ruby_2+ is true, true will be returned if we are on ruby,
+ # retry_change_requests is allowed and the request is not idempotent.
+
+ def can_retry? req, retried_on_ruby_2 = false
+ return @retry_change_requests && !idempotent?(req) if retried_on_ruby_2
+
+ @retry_change_requests || idempotent?(req)
+ end
+
+ if RUBY_VERSION > '1.9' then
+ ##
+ # Workaround for missing Net::HTTPHeader#connection_close? on Ruby 1.8
+
+ def connection_close? header
+ header.connection_close?
+ end
+
+ ##
+ # Workaround for missing Net::HTTPHeader#connection_keep_alive? on Ruby 1.8
+
+ def connection_keep_alive? header
+ header.connection_keep_alive?
+ end
+ else
+ ##
+ # Workaround for missing Net::HTTPRequest#connection_close? on Ruby 1.8
+
+ def connection_close? header
+ header['connection'] =~ /close/ or header['proxy-connection'] =~ /close/
+ end
+
+ ##
+ # Workaround for missing Net::HTTPRequest#connection_keep_alive? on Ruby
+ # 1.8
+
+ def connection_keep_alive? header
+ header['connection'] =~ /keep-alive/ or
+ header['proxy-connection'] =~ /keep-alive/
+ end
+ end
+
+ ##
+ # Deprecated in favor of #expired?
+
+ def max_age # :nodoc:
+ return Time.now + 1 unless @idle_timeout
+
+ Time.now - @idle_timeout
+ end
+
+ ##
+ # Adds "http://" to the String +uri+ if it is missing.
+
+ def normalize_uri uri
+ (uri =~ /^https?:/) ? uri : "http://#{uri}"
+ end
+
+ ##
+ # Pipelines +requests+ to the HTTP server at +uri+ yielding responses if a
+ # block is given. Returns all responses received.
+ #
+ # See
+ # Net::HTTP::Pipeline[http://docs.seattlerb.org/net-http-pipeline/Net/HTTP/Pipeline.html]
+ # for further details.
+ #
+ # Only if <tt>net-http-pipeline</tt> was required before
+ # <tt>net-http-persistent</tt> #pipeline will be present.
+
+ def pipeline uri, requests, &block # :yields: responses
+ connection = connection_for uri
+
+ connection.pipeline requests, &block
+ end
+
+ ##
+ # Sets this client's SSL private key
+
+ def private_key= key
+ @private_key = key
+
+ reconnect_ssl
+ end
+
+ # For Net::HTTP parity
+ alias key= private_key=
+
+ ##
+ # Sets the proxy server. The +proxy+ may be the URI of the proxy server,
+ # the symbol +:ENV+ which will read the proxy from the environment or nil to
+ # disable use of a proxy. See #proxy_from_env for details on setting the
+ # proxy from the environment.
+ #
+ # If the proxy URI is set after requests have been made, the next request
+ # will shut-down and re-open all connections.
+ #
+ # The +no_proxy+ query parameter can be used to specify hosts which shouldn't
+ # be reached via proxy; if set it should be a comma separated list of
+ # hostname suffixes, optionally with +:port+ appended, for example
+ # <tt>example.com,some.host:8080</tt>.
+
+ def proxy= proxy
+ @proxy_uri = case proxy
+ when :ENV then proxy_from_env
+ when URI::HTTP then proxy
+ when nil then # ignore
+ else raise ArgumentError, 'proxy must be :ENV or a URI::HTTP'
+ end
+
+ @no_proxy.clear
+
+ if @proxy_uri then
+ @proxy_args = [
+ @proxy_uri.host,
+ @proxy_uri.port,
+ unescape(@proxy_uri.user),
+ unescape(@proxy_uri.password),
+ ]
+
+ @proxy_connection_id = [nil, *@proxy_args].join ':'
+
+ if @proxy_uri.query then
+ @no_proxy = CGI.parse(@proxy_uri.query)['no_proxy'].join(',').downcase.split(',').map { |x| x.strip }.reject { |x| x.empty? }
+ end
+ end
+
+ reconnect
+ reconnect_ssl
+ end
+
+ ##
+ # Creates a URI for an HTTP proxy server from ENV variables.
+ #
+ # If +HTTP_PROXY+ is set a proxy will be returned.
+ #
+ # If +HTTP_PROXY_USER+ or +HTTP_PROXY_PASS+ are set the URI is given the
+ # indicated user and password unless HTTP_PROXY contains either of these in
+ # the URI.
+ #
+ # The +NO_PROXY+ ENV variable can be used to specify hosts which shouldn't
+ # be reached via proxy; if set it should be a comma separated list of
+ # hostname suffixes, optionally with +:port+ appended, for example
+ # <tt>example.com,some.host:8080</tt>. When set to <tt>*</tt> no proxy will
+ # be returned.
+ #
+ # For Windows users, lowercase ENV variables are preferred over uppercase ENV
+ # variables.
+
+ def proxy_from_env
+ env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
+
+ return nil if env_proxy.nil? or env_proxy.empty?
+
+ uri = URI normalize_uri env_proxy
+
+ env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY']
+
+ # '*' is special case for always bypass
+ return nil if env_no_proxy == '*'
+
+ if env_no_proxy then
+ uri.query = "no_proxy=#{escape(env_no_proxy)}"
+ end
+
+ unless uri.user or uri.password then
+ uri.user = escape ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER']
+ uri.password = escape ENV['http_proxy_pass'] || ENV['HTTP_PROXY_PASS']
+ end
+
+ uri
+ end
+
+ ##
+ # Returns true when proxy should by bypassed for host.
+
+ def proxy_bypass? host, port
+ host = host.downcase
+ host_port = [host, port].join ':'
+
+ @no_proxy.each do |name|
+ return true if host[-name.length, name.length] == name or
+ host_port[-name.length, name.length] == name
+ end
+
+ false
+ end
+
+ ##
+ # Forces reconnection of HTTP connections.
+
+ def reconnect
+ @generation += 1
+ end
+
+ ##
+ # Forces reconnection of SSL connections.
+
+ def reconnect_ssl
+ @ssl_generation += 1
+ end
+
+ ##
+ # Finishes then restarts the Net::HTTP +connection+
+
+ def reset connection
+ Thread.current[@request_key].delete connection.object_id
+ Thread.current[@timeout_key].delete connection.object_id
+
+ finish connection
+
+ start connection
+ rescue Errno::ECONNREFUSED
+ e = Error.new "connection refused: #{connection.address}:#{connection.port}"
+ e.set_backtrace $@
+ raise e
+ rescue Errno::EHOSTDOWN
+ e = Error.new "host down: #{connection.address}:#{connection.port}"
+ e.set_backtrace $@
+ raise e
+ end
+
+ ##
+ # Makes a request on +uri+. If +req+ is nil a Net::HTTP::Get is performed
+ # against +uri+.
+ #
+ # If a block is passed #request behaves like Net::HTTP#request (the body of
+ # the response will not have been read).
+ #
+ # +req+ must be a Net::HTTPRequest subclass (see Net::HTTP for a list).
+ #
+ # If there is an error and the request is idempotent according to RFC 2616
+ # it will be retried automatically.
+
+ def request uri, req = nil, &block
+ retried = false
+ bad_response = false
+
+ req = request_setup req || uri
+
+ connection = connection_for uri
+ connection_id = connection.object_id
+
+ begin
+ Thread.current[@request_key][connection_id] += 1
+ response = connection.request req, &block
+
+ if connection_close?(req) or
+ (response.http_version <= '1.0' and
+ not connection_keep_alive?(response)) or
+ connection_close?(response) then
+ connection.finish
+ end
+ rescue Net::HTTPBadResponse => e
+ message = error_message connection
+
+ finish connection
+
+ raise Error, "too many bad responses #{message}" if
+ bad_response or not can_retry? req
+
+ bad_response = true
+ retry
+ rescue *RETRIED_EXCEPTIONS => e # retried on ruby 2
+ request_failed e, req, connection if
+ retried or not can_retry? req, @retried_on_ruby_2
+
+ reset connection
+
+ retried = true
+ retry
+ rescue Errno::EINVAL, Errno::ETIMEDOUT => e # not retried on ruby 2
+ request_failed e, req, connection if retried or not can_retry? req
+
+ reset connection
+
+ retried = true
+ retry
+ rescue Exception => e
+ finish connection
+
+ raise
+ ensure
+ Thread.current[@timeout_key][connection_id] = Time.now
+ end
+
+ @http_versions["#{uri.host}:#{uri.port}"] ||= response.http_version
+
+ response
+ end
+
+ ##
+ # Raises an Error for +exception+ which resulted from attempting the request
+ # +req+ on the +connection+.
+ #
+ # Finishes the +connection+.
+
+ def request_failed exception, req, connection # :nodoc:
+ due_to = "(due to #{exception.message} - #{exception.class})"
+ message = "too many connection resets #{due_to} #{error_message connection}"
+
+ finish connection
+
+
+ raise Error, message, exception.backtrace
+ end
+
+ ##
+ # Creates a GET request if +req_or_uri+ is a URI and adds headers to the
+ # request.
+ #
+ # Returns the request.
+
+ def request_setup req_or_uri # :nodoc:
+ req = if URI === req_or_uri then
+ Net::HTTP::Get.new req_or_uri.request_uri
+ else
+ req_or_uri
+ end
+
+ @headers.each do |pair|
+ req.add_field(*pair)
+ end
+
+ @override_headers.each do |name, value|
+ req[name] = value
+ end
+
+ unless req['Connection'] then
+ req.add_field 'Connection', 'keep-alive'
+ req.add_field 'Keep-Alive', @keep_alive
+ end
+
+ req
+ end
+
+ ##
+ # Shuts down all connections for +thread+.
+ #
+ # Uses the current thread by default.
+ #
+ # If you've used Bundler::Persistent::Net::HTTP::Persistent across multiple threads you should
+ # call this in each thread when you're done making HTTP requests.
+ #
+ # *NOTE*: Calling shutdown for another thread can be dangerous!
+ #
+ # If the thread is still using the connection it may cause an error! It is
+ # best to call #shutdown in the thread at the appropriate time instead!
+
+ def shutdown thread = Thread.current
+ generation = reconnect
+ cleanup generation, thread, @generation_key
+
+ ssl_generation = reconnect_ssl
+ cleanup ssl_generation, thread, @ssl_generation_key
+
+ thread[@request_key] = nil
+ thread[@timeout_key] = nil
+ end
+
+ ##
+ # Shuts down all connections in all threads
+ #
+ # *NOTE*: THIS METHOD IS VERY DANGEROUS!
+ #
+ # Do not call this method if other threads are still using their
+ # connections! Call #shutdown at the appropriate time instead!
+ #
+ # Use this method only as a last resort!
+
+ def shutdown_in_all_threads
+ Thread.list.each do |thread|
+ shutdown thread
+ end
+
+ nil
+ end
+
+ ##
+ # Enables SSL on +connection+
+
+ def ssl connection
+ connection.use_ssl = true
+
+ connection.ssl_version = @ssl_version if @ssl_version
+
+ connection.verify_mode = @verify_mode
+
+ if OpenSSL::SSL::VERIFY_PEER == OpenSSL::SSL::VERIFY_NONE and
+ not Object.const_defined?(:I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG) then
+ warn <<-WARNING
+ !!!SECURITY WARNING!!!
+
+The SSL HTTP connection to:
+
+ #{connection.address}:#{connection.port}
+
+ !!!MAY NOT BE VERIFIED!!!
+
+On your platform your OpenSSL implementation is broken.
+
+There is no difference between the values of VERIFY_NONE and VERIFY_PEER.
+
+This means that attempting to verify the security of SSL connections may not
+work. This exposes you to man-in-the-middle exploits, snooping on the
+contents of your connection and other dangers to the security of your data.
+
+To disable this warning define the following constant at top-level in your
+application:
+
+ I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG = nil
+
+ WARNING
+ end
+
+ if @ca_file then
+ connection.ca_file = @ca_file
+ connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ connection.verify_callback = @verify_callback if @verify_callback
+ end
+
+ if @certificate and @private_key then
+ connection.cert = @certificate
+ connection.key = @private_key
+ end
+
+ connection.cert_store = if @cert_store then
+ @cert_store
+ else
+ store = OpenSSL::X509::Store.new
+ store.set_default_paths
+ store
+ end
+ end
+
+ ##
+ # Finishes all connections that existed before the given SSL parameter
+ # +generation+.
+
+ def ssl_cleanup generation # :nodoc:
+ cleanup generation, Thread.current, @ssl_generation_key
+ end
+
+ ##
+ # SSL version to use
+
+ def ssl_version= ssl_version
+ @ssl_version = ssl_version
+
+ reconnect_ssl
+ end if RUBY_VERSION > '1.9'
+
+ ##
+ # Sets the HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_PEER.
+ #
+ # Setting this to VERIFY_NONE is a VERY BAD IDEA and should NEVER be used.
+ # Securely transfer the correct certificate and update the default
+ # certificate store or set the ca file instead.
+
+ def verify_mode= verify_mode
+ @verify_mode = verify_mode
+
+ reconnect_ssl
+ end
+
+ ##
+ # SSL verification callback.
+
+ def verify_callback= callback
+ @verify_callback = callback
+
+ reconnect_ssl
+ end
+
+end
+
+require 'bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse'
+
diff --git a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse.rb
new file mode 100644
index 0000000000..1b6b789f6d
--- /dev/null
+++ b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse.rb
@@ -0,0 +1,129 @@
+##
+# This Net::HTTP subclass adds SSL session reuse and Server Name Indication
+# (SNI) RFC 3546.
+#
+# DO NOT DEPEND UPON THIS CLASS
+#
+# This class is an implementation detail and is subject to change or removal
+# at any time.
+
+class Bundler::Persistent::Net::HTTP::Persistent::SSLReuse < Net::HTTP
+
+ @is_proxy_class = false
+ @proxy_addr = nil
+ @proxy_port = nil
+ @proxy_user = nil
+ @proxy_pass = nil
+
+ def initialize address, port = nil # :nodoc:
+ super
+
+ @ssl_session = nil
+ end
+
+ ##
+ # From ruby trunk r33086 including http://redmine.ruby-lang.org/issues/5341
+
+ def connect # :nodoc:
+ D "opening connection to #{conn_address()}..."
+ s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) }
+ D "opened"
+ if use_ssl?
+ ssl_parameters = Hash.new
+ iv_list = instance_variables
+ SSL_ATTRIBUTES.each do |name|
+ ivname = "@#{name}".intern
+ if iv_list.include?(ivname) and
+ value = instance_variable_get(ivname)
+ ssl_parameters[name] = value
+ end
+ end
+ unless @ssl_context then
+ @ssl_context = OpenSSL::SSL::SSLContext.new
+ @ssl_context.set_params(ssl_parameters)
+ end
+ s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
+ s.sync_close = true
+ end
+ @socket = Net::BufferedIO.new(s)
+ @socket.read_timeout = @read_timeout
+ @socket.continue_timeout = @continue_timeout if
+ @socket.respond_to? :continue_timeout
+ @socket.debug_output = @debug_output
+ if use_ssl?
+ begin
+ if proxy?
+ @socket.writeline sprintf('CONNECT %s:%s HTTP/%s',
+ @address, @port, HTTPVersion)
+ @socket.writeline "Host: #{@address}:#{@port}"
+ if proxy_user
+ credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
+ credential.delete!("\r\n")
+ @socket.writeline "Proxy-Authorization: Basic #{credential}"
+ end
+ @socket.writeline ''
+ Net::HTTPResponse.read_new(@socket).value
+ end
+ s.session = @ssl_session if @ssl_session
+ # Server Name Indication (SNI) RFC 3546
+ s.hostname = @address if s.respond_to? :hostname=
+ timeout(@open_timeout) { s.connect }
+ if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
+ s.post_connection_check(@address)
+ end
+ @ssl_session = s.session
+ rescue => exception
+ D "Conn close because of connect error #{exception}"
+ @socket.close if @socket and not @socket.closed?
+ raise exception
+ end
+ end
+ on_connect
+ end if RUBY_VERSION > '1.9'
+
+ ##
+ # From ruby_1_8_7 branch r29865 including a modified
+ # http://redmine.ruby-lang.org/issues/5341
+
+ def connect # :nodoc:
+ D "opening connection to #{conn_address()}..."
+ s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) }
+ D "opened"
+ if use_ssl?
+ unless @ssl_context.verify_mode
+ warn "warning: peer certificate won't be verified in this SSL session"
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ end
+ s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
+ s.sync_close = true
+ end
+ @socket = Net::BufferedIO.new(s)
+ @socket.read_timeout = @read_timeout
+ @socket.debug_output = @debug_output
+ if use_ssl?
+ if proxy?
+ @socket.writeline sprintf('CONNECT %s:%s HTTP/%s',
+ @address, @port, HTTPVersion)
+ @socket.writeline "Host: #{@address}:#{@port}"
+ if proxy_user
+ credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
+ credential.delete!("\r\n")
+ @socket.writeline "Proxy-Authorization: Basic #{credential}"
+ end
+ @socket.writeline ''
+ Net::HTTPResponse.read_new(@socket).value
+ end
+ s.session = @ssl_session if @ssl_session
+ s.connect
+ if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
+ s.post_connection_check(@address)
+ end
+ @ssl_session = s.session
+ end
+ on_connect
+ end if RUBY_VERSION < '1.9'
+
+ private :connect
+
+end
+
diff --git a/lib/bundler/vendor/thor/lib/thor.rb b/lib/bundler/vendor/thor/lib/thor.rb
new file mode 100644
index 0000000000..999e8b7e61
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor.rb
@@ -0,0 +1,509 @@
+require "set"
+require "bundler/vendor/thor/lib/thor/base"
+
+class Bundler::Thor
+ class << self
+ # Allows for custom "Command" package naming.
+ #
+ # === Parameters
+ # name<String>
+ # options<Hash>
+ #
+ def package_name(name, _ = {})
+ @package_name = name.nil? || name == "" ? nil : name
+ end
+
+ # Sets the default command when thor is executed without an explicit command to be called.
+ #
+ # ==== Parameters
+ # meth<Symbol>:: name of the default command
+ #
+ def default_command(meth = nil)
+ if meth
+ @default_command = meth == :none ? "help" : meth.to_s
+ else
+ @default_command ||= from_superclass(:default_command, "help")
+ end
+ end
+ alias_method :default_task, :default_command
+
+ # Registers another Bundler::Thor subclass as a command.
+ #
+ # ==== Parameters
+ # klass<Class>:: Bundler::Thor subclass to register
+ # command<String>:: Subcommand name to use
+ # usage<String>:: Short usage for the subcommand
+ # description<String>:: Description for the subcommand
+ def register(klass, subcommand_name, usage, description, options = {})
+ if klass <= Bundler::Thor::Group
+ desc usage, description, options
+ define_method(subcommand_name) { |*args| invoke(klass, args) }
+ else
+ desc usage, description, options
+ subcommand subcommand_name, klass
+ end
+ end
+
+ # Defines the usage and the description of the next command.
+ #
+ # ==== Parameters
+ # usage<String>
+ # description<String>
+ # options<String>
+ #
+ def desc(usage, description, options = {})
+ if options[:for]
+ command = find_and_refresh_command(options[:for])
+ command.usage = usage if usage
+ command.description = description if description
+ else
+ @usage = usage
+ @desc = description
+ @hide = options[:hide] || false
+ end
+ end
+
+ # Defines the long description of the next command.
+ #
+ # ==== Parameters
+ # long description<String>
+ #
+ def long_desc(long_description, options = {})
+ if options[:for]
+ command = find_and_refresh_command(options[:for])
+ command.long_description = long_description if long_description
+ else
+ @long_desc = long_description
+ end
+ end
+
+ # Maps an input to a command. If you define:
+ #
+ # map "-T" => "list"
+ #
+ # Running:
+ #
+ # thor -T
+ #
+ # Will invoke the list command.
+ #
+ # ==== Parameters
+ # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command.
+ #
+ def map(mappings = nil)
+ @map ||= from_superclass(:map, {})
+
+ if mappings
+ mappings.each do |key, value|
+ if key.respond_to?(:each)
+ key.each { |subkey| @map[subkey] = value }
+ else
+ @map[key] = value
+ end
+ end
+ end
+
+ @map
+ end
+
+ # Declares the options for the next command to be declared.
+ #
+ # ==== Parameters
+ # Hash[Symbol => Object]:: The hash key is the name of the option and the value
+ # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric
+ # or :required (string). If you give a value, the type of the value is used.
+ #
+ def method_options(options = nil)
+ @method_options ||= {}
+ build_options(options, @method_options) if options
+ @method_options
+ end
+
+ alias_method :options, :method_options
+
+ # Adds an option to the set of method options. If :for is given as option,
+ # it allows you to change the options from a previous defined command.
+ #
+ # def previous_command
+ # # magic
+ # end
+ #
+ # method_option :foo => :bar, :for => :previous_command
+ #
+ # def next_command
+ # # magic
+ # end
+ #
+ # ==== Parameters
+ # name<Symbol>:: The name of the argument.
+ # options<Hash>:: Described below.
+ #
+ # ==== Options
+ # :desc - Description for the argument.
+ # :required - If the argument is required or not.
+ # :default - Default value for this argument. It cannot be required and have default values.
+ # :aliases - Aliases for this option.
+ # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
+ # :banner - String to show on usage notes.
+ # :hide - If you want to hide this option from the help.
+ #
+ def method_option(name, options = {})
+ scope = if options[:for]
+ find_and_refresh_command(options[:for]).options
+ else
+ method_options
+ end
+
+ build_option(name, options, scope)
+ end
+ alias_method :option, :method_option
+
+ # Prints help information for the given command.
+ #
+ # ==== Parameters
+ # shell<Bundler::Thor::Shell>
+ # command_name<String>
+ #
+ def command_help(shell, command_name)
+ meth = normalize_command_name(command_name)
+ command = all_commands[meth]
+ handle_no_command_error(meth) unless command
+
+ shell.say "Usage:"
+ shell.say " #{banner(command)}"
+ shell.say
+ class_options_help(shell, nil => command.options.values)
+ if command.long_description
+ shell.say "Description:"
+ shell.print_wrapped(command.long_description, :indent => 2)
+ else
+ shell.say command.description
+ end
+ end
+ alias_method :task_help, :command_help
+
+ # Prints help information for this class.
+ #
+ # ==== Parameters
+ # shell<Bundler::Thor::Shell>
+ #
+ def help(shell, subcommand = false)
+ list = printable_commands(true, subcommand)
+ Bundler::Thor::Util.thor_classes_in(self).each do |klass|
+ list += klass.printable_commands(false)
+ end
+ list.sort! { |a, b| a[0] <=> b[0] }
+
+ if defined?(@package_name) && @package_name
+ shell.say "#{@package_name} commands:"
+ else
+ shell.say "Commands:"
+ end
+
+ shell.print_table(list, :indent => 2, :truncate => true)
+ shell.say
+ class_options_help(shell)
+ end
+
+ # Returns commands ready to be printed.
+ def printable_commands(all = true, subcommand = false)
+ (all ? all_commands : commands).map do |_, command|
+ next if command.hidden?
+ item = []
+ item << banner(command, false, subcommand)
+ item << (command.description ? "# #{command.description.gsub(/\s+/m, ' ')}" : "")
+ item
+ end.compact
+ end
+ alias_method :printable_tasks, :printable_commands
+
+ def subcommands
+ @subcommands ||= from_superclass(:subcommands, [])
+ end
+ alias_method :subtasks, :subcommands
+
+ def subcommand_classes
+ @subcommand_classes ||= {}
+ end
+
+ def subcommand(subcommand, subcommand_class)
+ subcommands << subcommand.to_s
+ subcommand_class.subcommand_help subcommand
+ subcommand_classes[subcommand.to_s] = subcommand_class
+
+ define_method(subcommand) do |*args|
+ args, opts = Bundler::Thor::Arguments.split(args)
+ invoke_args = [args, opts, {:invoked_via_subcommand => true, :class_options => options}]
+ invoke_args.unshift "help" if opts.delete("--help") || opts.delete("-h")
+ invoke subcommand_class, *invoke_args
+ end
+ subcommand_class.commands.each do |_meth, command|
+ command.ancestor_name = subcommand
+ end
+ end
+ alias_method :subtask, :subcommand
+
+ # Extend check unknown options to accept a hash of conditions.
+ #
+ # === Parameters
+ # options<Hash>: A hash containing :only and/or :except keys
+ def check_unknown_options!(options = {})
+ @check_unknown_options ||= {}
+ options.each do |key, value|
+ if value
+ @check_unknown_options[key] = Array(value)
+ else
+ @check_unknown_options.delete(key)
+ end
+ end
+ @check_unknown_options
+ end
+
+ # Overwrite check_unknown_options? to take subcommands and options into account.
+ def check_unknown_options?(config) #:nodoc:
+ options = check_unknown_options
+ return false unless options
+
+ command = config[:current_command]
+ return true unless command
+
+ name = command.name
+
+ if subcommands.include?(name)
+ false
+ elsif options[:except]
+ !options[:except].include?(name.to_sym)
+ elsif options[:only]
+ options[:only].include?(name.to_sym)
+ else
+ true
+ end
+ end
+
+ # Stop parsing of options as soon as an unknown option or a regular
+ # argument is encountered. All remaining arguments are passed to the command.
+ # This is useful if you have a command that can receive arbitrary additional
+ # options, and where those additional options should not be handled by
+ # Bundler::Thor.
+ #
+ # ==== Example
+ #
+ # To better understand how this is useful, let's consider a command that calls
+ # an external command. A user may want to pass arbitrary options and
+ # arguments to that command. The command itself also accepts some options,
+ # which should be handled by Bundler::Thor.
+ #
+ # class_option "verbose", :type => :boolean
+ # stop_on_unknown_option! :exec
+ # check_unknown_options! :except => :exec
+ #
+ # desc "exec", "Run a shell command"
+ # def exec(*args)
+ # puts "diagnostic output" if options[:verbose]
+ # Kernel.exec(*args)
+ # end
+ #
+ # Here +exec+ can be called with +--verbose+ to get diagnostic output,
+ # e.g.:
+ #
+ # $ thor exec --verbose echo foo
+ # diagnostic output
+ # foo
+ #
+ # But if +--verbose+ is given after +echo+, it is passed to +echo+ instead:
+ #
+ # $ thor exec echo --verbose foo
+ # --verbose foo
+ #
+ # ==== Parameters
+ # Symbol ...:: A list of commands that should be affected.
+ def stop_on_unknown_option!(*command_names)
+ stop_on_unknown_option.merge(command_names)
+ end
+
+ def stop_on_unknown_option?(command) #:nodoc:
+ command && stop_on_unknown_option.include?(command.name.to_sym)
+ end
+
+ # Disable the check for required options for the given commands.
+ # This is useful if you have a command that does not need the required options
+ # to work, like help.
+ #
+ # ==== Parameters
+ # Symbol ...:: A list of commands that should be affected.
+ def disable_required_check!(*command_names)
+ disable_required_check.merge(command_names)
+ end
+
+ def disable_required_check?(command) #:nodoc:
+ command && disable_required_check.include?(command.name.to_sym)
+ end
+
+ protected
+
+ def stop_on_unknown_option #:nodoc:
+ @stop_on_unknown_option ||= Set.new
+ end
+
+ # help command has the required check disabled by default.
+ def disable_required_check #:nodoc:
+ @disable_required_check ||= Set.new([:help])
+ end
+
+ # The method responsible for dispatching given the args.
+ def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable MethodLength
+ meth ||= retrieve_command_name(given_args)
+ command = all_commands[normalize_command_name(meth)]
+
+ if !command && config[:invoked_via_subcommand]
+ # We're a subcommand and our first argument didn't match any of our
+ # commands. So we put it back and call our default command.
+ given_args.unshift(meth)
+ command = all_commands[normalize_command_name(default_command)]
+ end
+
+ if command
+ args, opts = Bundler::Thor::Options.split(given_args)
+ if stop_on_unknown_option?(command) && !args.empty?
+ # given_args starts with a non-option, so we treat everything as
+ # ordinary arguments
+ args.concat opts
+ opts.clear
+ end
+ else
+ args = given_args
+ opts = nil
+ command = dynamic_command_class.new(meth)
+ end
+
+ opts = given_opts || opts || []
+ config[:current_command] = command
+ config[:command_options] = command.options
+
+ instance = new(args, opts, config)
+ yield instance if block_given?
+ args = instance.args
+ trailing = args[Range.new(arguments.size, -1)]
+ instance.invoke_command(command, trailing || [])
+ end
+
+ # The banner for this class. You can customize it if you are invoking the
+ # thor class by another ways which is not the Bundler::Thor::Runner. It receives
+ # the command that is going to be invoked and a boolean which indicates if
+ # the namespace should be displayed as arguments.
+ #
+ def banner(command, namespace = nil, subcommand = false)
+ "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}"
+ end
+
+ def baseclass #:nodoc:
+ Bundler::Thor
+ end
+
+ def dynamic_command_class #:nodoc:
+ Bundler::Thor::DynamicCommand
+ end
+
+ def create_command(meth) #:nodoc:
+ @usage ||= nil
+ @desc ||= nil
+ @long_desc ||= nil
+ @hide ||= nil
+
+ if @usage && @desc
+ base_class = @hide ? Bundler::Thor::HiddenCommand : Bundler::Thor::Command
+ commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
+ @usage, @desc, @long_desc, @method_options, @hide = nil
+ true
+ elsif all_commands[meth] || meth == "method_missing"
+ true
+ else
+ puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " \
+ "Call desc if you want this method to be available as command or declare it inside a " \
+ "no_commands{} block. Invoked from #{caller[1].inspect}."
+ false
+ end
+ end
+ alias_method :create_task, :create_command
+
+ def initialize_added #:nodoc:
+ class_options.merge!(method_options)
+ @method_options = nil
+ end
+
+ # Retrieve the command name from given args.
+ def retrieve_command_name(args) #:nodoc:
+ meth = args.first.to_s unless args.empty?
+ args.shift if meth && (map[meth] || meth !~ /^\-/)
+ end
+ alias_method :retrieve_task_name, :retrieve_command_name
+
+ # receives a (possibly nil) command name and returns a name that is in
+ # the commands hash. In addition to normalizing aliases, this logic
+ # will determine if a shortened command is an unambiguous substring of
+ # a command or alias.
+ #
+ # +normalize_command_name+ also converts names like +animal-prison+
+ # into +animal_prison+.
+ def normalize_command_name(meth) #:nodoc:
+ return default_command.to_s.tr("-", "_") unless meth
+
+ possibilities = find_command_possibilities(meth)
+ raise AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]" if possibilities.size > 1
+
+ if possibilities.empty?
+ meth ||= default_command
+ elsif map[meth]
+ meth = map[meth]
+ else
+ meth = possibilities.first
+ end
+
+ meth.to_s.tr("-", "_") # treat foo-bar as foo_bar
+ end
+ alias_method :normalize_task_name, :normalize_command_name
+
+ # this is the logic that takes the command name passed in by the user
+ # and determines whether it is an unambiguous substrings of a command or
+ # alias name.
+ def find_command_possibilities(meth)
+ len = meth.to_s.length
+ possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort
+ unique_possibilities = possibilities.map { |k| map[k] || k }.uniq
+
+ if possibilities.include?(meth)
+ [meth]
+ elsif unique_possibilities.size == 1
+ unique_possibilities
+ else
+ possibilities
+ end
+ end
+ alias_method :find_task_possibilities, :find_command_possibilities
+
+ def subcommand_help(cmd)
+ desc "help [COMMAND]", "Describe subcommands or one specific subcommand"
+ class_eval "
+ def help(command = nil, subcommand = true); super; end
+"
+ end
+ alias_method :subtask_help, :subcommand_help
+ end
+
+ include Bundler::Thor::Base
+
+ map HELP_MAPPINGS => :help
+
+ desc "help [COMMAND]", "Describe available commands or one specific command"
+ def help(command = nil, subcommand = false)
+ if command
+ if self.class.subcommands.include? command
+ self.class.subcommand_classes[command].help(shell, true)
+ else
+ self.class.command_help(shell, command)
+ end
+ else
+ self.class.help(shell, subcommand)
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/actions.rb b/lib/bundler/vendor/thor/lib/thor/actions.rb
new file mode 100644
index 0000000000..e6698572a9
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/actions.rb
@@ -0,0 +1,321 @@
+require "uri"
+require "bundler/vendor/thor/lib/thor/core_ext/io_binary_read"
+require "bundler/vendor/thor/lib/thor/actions/create_file"
+require "bundler/vendor/thor/lib/thor/actions/create_link"
+require "bundler/vendor/thor/lib/thor/actions/directory"
+require "bundler/vendor/thor/lib/thor/actions/empty_directory"
+require "bundler/vendor/thor/lib/thor/actions/file_manipulation"
+require "bundler/vendor/thor/lib/thor/actions/inject_into_file"
+
+class Bundler::Thor
+ module Actions
+ attr_accessor :behavior
+
+ def self.included(base) #:nodoc:
+ base.extend ClassMethods
+ end
+
+ module ClassMethods
+ # Hold source paths for one Bundler::Thor instance. source_paths_for_search is the
+ # method responsible to gather source_paths from this current class,
+ # inherited paths and the source root.
+ #
+ def source_paths
+ @_source_paths ||= []
+ end
+
+ # Stores and return the source root for this class
+ def source_root(path = nil)
+ @_source_root = path if path
+ @_source_root ||= nil
+ end
+
+ # Returns the source paths in the following order:
+ #
+ # 1) This class source paths
+ # 2) Source root
+ # 3) Parents source paths
+ #
+ def source_paths_for_search
+ paths = []
+ paths += source_paths
+ paths << source_root if source_root
+ paths += from_superclass(:source_paths, [])
+ paths
+ end
+
+ # Add runtime options that help actions execution.
+ #
+ def add_runtime_options!
+ class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime,
+ :desc => "Overwrite files that already exist"
+
+ class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime,
+ :desc => "Run but do not make any changes"
+
+ class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime,
+ :desc => "Suppress status output"
+
+ class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime,
+ :desc => "Skip files that already exist"
+ end
+ end
+
+ # Extends initializer to add more configuration options.
+ #
+ # ==== Configuration
+ # behavior<Symbol>:: The actions default behavior. Can be :invoke or :revoke.
+ # It also accepts :force, :skip and :pretend to set the behavior
+ # and the respective option.
+ #
+ # destination_root<String>:: The root directory needed for some actions.
+ #
+ def initialize(args = [], options = {}, config = {})
+ self.behavior = case config[:behavior].to_s
+ when "force", "skip"
+ _cleanup_options_and_set(options, config[:behavior])
+ :invoke
+ when "revoke"
+ :revoke
+ else
+ :invoke
+ end
+
+ super
+ self.destination_root = config[:destination_root]
+ end
+
+ # Wraps an action object and call it accordingly to the thor class behavior.
+ #
+ def action(instance) #:nodoc:
+ if behavior == :revoke
+ instance.revoke!
+ else
+ instance.invoke!
+ end
+ end
+
+ # Returns the root for this thor class (also aliased as destination root).
+ #
+ def destination_root
+ @destination_stack.last
+ end
+
+ # Sets the root for this thor class. Relatives path are added to the
+ # directory where the script was invoked and expanded.
+ #
+ def destination_root=(root)
+ @destination_stack ||= []
+ @destination_stack[0] = File.expand_path(root || "")
+ end
+
+ # Returns the given path relative to the absolute root (ie, root where
+ # the script started).
+ #
+ def relative_to_original_destination_root(path, remove_dot = true)
+ path = path.dup
+ if path.gsub!(@destination_stack[0], ".")
+ remove_dot ? (path[2..-1] || "") : path
+ else
+ path
+ end
+ end
+
+ # Holds source paths in instance so they can be manipulated.
+ #
+ def source_paths
+ @source_paths ||= self.class.source_paths_for_search
+ end
+
+ # Receives a file or directory and search for it in the source paths.
+ #
+ def find_in_source_paths(file)
+ possible_files = [file, file + TEMPLATE_EXTNAME]
+ relative_root = relative_to_original_destination_root(destination_root, false)
+
+ source_paths.each do |source|
+ possible_files.each do |f|
+ source_file = File.expand_path(f, File.join(source, relative_root))
+ return source_file if File.exist?(source_file)
+ end
+ end
+
+ message = "Could not find #{file.inspect} in any of your source paths. ".dup
+
+ unless self.class.source_root
+ message << "Please invoke #{self.class.name}.source_root(PATH) with the PATH containing your templates. "
+ end
+
+ message << if source_paths.empty?
+ "Currently you have no source paths."
+ else
+ "Your current source paths are: \n#{source_paths.join("\n")}"
+ end
+
+ raise Error, message
+ end
+
+ # Do something in the root or on a provided subfolder. If a relative path
+ # is given it's referenced from the current root. The full path is yielded
+ # to the block you provide. The path is set back to the previous path when
+ # the method exits.
+ #
+ # ==== Parameters
+ # dir<String>:: the directory to move to.
+ # config<Hash>:: give :verbose => true to log and use padding.
+ #
+ def inside(dir = "", config = {}, &block)
+ verbose = config.fetch(:verbose, false)
+ pretend = options[:pretend]
+
+ say_status :inside, dir, verbose
+ shell.padding += 1 if verbose
+ @destination_stack.push File.expand_path(dir, destination_root)
+
+ # If the directory doesnt exist and we're not pretending
+ if !File.exist?(destination_root) && !pretend
+ require "fileutils"
+ FileUtils.mkdir_p(destination_root)
+ end
+
+ if pretend
+ # In pretend mode, just yield down to the block
+ block.arity == 1 ? yield(destination_root) : yield
+ else
+ require "fileutils"
+ FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield }
+ end
+
+ @destination_stack.pop
+ shell.padding -= 1 if verbose
+ end
+
+ # Goes to the root and execute the given block.
+ #
+ def in_root
+ inside(@destination_stack.first) { yield }
+ end
+
+ # Loads an external file and execute it in the instance binding.
+ #
+ # ==== Parameters
+ # path<String>:: The path to the file to execute. Can be a web address or
+ # a relative path from the source root.
+ #
+ # ==== Examples
+ #
+ # apply "http://gist.github.com/103208"
+ #
+ # apply "recipes/jquery.rb"
+ #
+ def apply(path, config = {})
+ verbose = config.fetch(:verbose, true)
+ is_uri = path =~ %r{^https?\://}
+ path = find_in_source_paths(path) unless is_uri
+
+ say_status :apply, path, verbose
+ shell.padding += 1 if verbose
+
+ contents = if is_uri
+ open(path, "Accept" => "application/x-thor-template", &:read)
+ else
+ open(path, &:read)
+ end
+
+ instance_eval(contents, path)
+ shell.padding -= 1 if verbose
+ end
+
+ # Executes a command returning the contents of the command.
+ #
+ # ==== Parameters
+ # command<String>:: the command to be executed.
+ # config<Hash>:: give :verbose => false to not log the status, :capture => true to hide to output. Specify :with
+ # to append an executable to command execution.
+ #
+ # ==== Example
+ #
+ # inside('vendor') do
+ # run('ln -s ~/edge rails')
+ # end
+ #
+ def run(command, config = {})
+ return unless behavior == :invoke
+
+ destination = relative_to_original_destination_root(destination_root, false)
+ desc = "#{command} from #{destination.inspect}"
+
+ if config[:with]
+ desc = "#{File.basename(config[:with].to_s)} #{desc}"
+ command = "#{config[:with]} #{command}"
+ end
+
+ say_status :run, desc, config.fetch(:verbose, true)
+
+ unless options[:pretend]
+ config[:capture] ? `#{command}` : system(command.to_s)
+ end
+ end
+
+ # Executes a ruby script (taking into account WIN32 platform quirks).
+ #
+ # ==== Parameters
+ # command<String>:: the command to be executed.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ def run_ruby_script(command, config = {})
+ return unless behavior == :invoke
+ run command, config.merge(:with => Bundler::Thor::Util.ruby_command)
+ end
+
+ # Run a thor command. A hash of options can be given and it's converted to
+ # switches.
+ #
+ # ==== Parameters
+ # command<String>:: the command to be invoked
+ # args<Array>:: arguments to the command
+ # config<Hash>:: give :verbose => false to not log the status, :capture => true to hide to output.
+ # Other options are given as parameter to Bundler::Thor.
+ #
+ #
+ # ==== Examples
+ #
+ # thor :install, "http://gist.github.com/103208"
+ # #=> thor install http://gist.github.com/103208
+ #
+ # thor :list, :all => true, :substring => 'rails'
+ # #=> thor list --all --substring=rails
+ #
+ def thor(command, *args)
+ config = args.last.is_a?(Hash) ? args.pop : {}
+ verbose = config.key?(:verbose) ? config.delete(:verbose) : true
+ pretend = config.key?(:pretend) ? config.delete(:pretend) : false
+ capture = config.key?(:capture) ? config.delete(:capture) : false
+
+ args.unshift(command)
+ args.push Bundler::Thor::Options.to_switches(config)
+ command = args.join(" ").strip
+
+ run command, :with => :thor, :verbose => verbose, :pretend => pretend, :capture => capture
+ end
+
+ protected
+
+ # Allow current root to be shared between invocations.
+ #
+ def _shared_configuration #:nodoc:
+ super.merge!(:destination_root => destination_root)
+ end
+
+ def _cleanup_options_and_set(options, key) #:nodoc:
+ case options
+ when Array
+ %w(--force -f --skip -s).each { |i| options.delete(i) }
+ options << "--#{key}"
+ when Hash
+ [:force, :skip, "force", "skip"].each { |i| options.delete(i) }
+ options.merge!(key => true)
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb b/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb
new file mode 100644
index 0000000000..97d22d9bbd
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb
@@ -0,0 +1,104 @@
+require "bundler/vendor/thor/lib/thor/actions/empty_directory"
+
+class Bundler::Thor
+ module Actions
+ # Create a new file relative to the destination root with the given data,
+ # which is the return value of a block or a data string.
+ #
+ # ==== Parameters
+ # destination<String>:: the relative path to the destination root.
+ # data<String|NilClass>:: the data to append to the file.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Examples
+ #
+ # create_file "lib/fun_party.rb" do
+ # hostname = ask("What is the virtual hostname I should use?")
+ # "vhost.name = #{hostname}"
+ # end
+ #
+ # create_file "config/apache.conf", "your apache config"
+ #
+ def create_file(destination, *args, &block)
+ config = args.last.is_a?(Hash) ? args.pop : {}
+ data = args.first
+ action CreateFile.new(self, destination, block || data.to_s, config)
+ end
+ alias_method :add_file, :create_file
+
+ # CreateFile is a subset of Template, which instead of rendering a file with
+ # ERB, it gets the content from the user.
+ #
+ class CreateFile < EmptyDirectory #:nodoc:
+ attr_reader :data
+
+ def initialize(base, destination, data, config = {})
+ @data = data
+ super(base, destination, config)
+ end
+
+ # Checks if the content of the file at the destination is identical to the rendered result.
+ #
+ # ==== Returns
+ # Boolean:: true if it is identical, false otherwise.
+ #
+ def identical?
+ exists? && File.binread(destination) == render
+ end
+
+ # Holds the content to be added to the file.
+ #
+ def render
+ @render ||= if data.is_a?(Proc)
+ data.call
+ else
+ data
+ end
+ end
+
+ def invoke!
+ invoke_with_conflict_check do
+ require "fileutils"
+ FileUtils.mkdir_p(File.dirname(destination))
+ File.open(destination, "wb") { |f| f.write render }
+ end
+ given_destination
+ end
+
+ protected
+
+ # Now on conflict we check if the file is identical or not.
+ #
+ def on_conflict_behavior(&block)
+ if identical?
+ say_status :identical, :blue
+ else
+ options = base.options.merge(config)
+ force_or_skip_or_conflict(options[:force], options[:skip], &block)
+ end
+ end
+
+ # If force is true, run the action, otherwise check if it's not being
+ # skipped. If both are false, show the file_collision menu, if the menu
+ # returns true, force it, otherwise skip.
+ #
+ def force_or_skip_or_conflict(force, skip, &block)
+ if force
+ say_status :force, :yellow
+ yield unless pretend?
+ elsif skip
+ say_status :skip, :yellow
+ else
+ say_status :conflict, :red
+ force_or_skip_or_conflict(force_on_collision?, true, &block)
+ end
+ end
+
+ # Shows the file collision menu to the user and gets the result.
+ #
+ def force_on_collision?
+ base.shell.file_collision(destination) { render }
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb b/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb
new file mode 100644
index 0000000000..3a664401b4
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb
@@ -0,0 +1,60 @@
+require "bundler/vendor/thor/lib/thor/actions/create_file"
+
+class Bundler::Thor
+ module Actions
+ # Create a new file relative to the destination root from the given source.
+ #
+ # ==== Parameters
+ # destination<String>:: the relative path to the destination root.
+ # source<String|NilClass>:: the relative path to the source root.
+ # config<Hash>:: give :verbose => false to not log the status.
+ # :: give :symbolic => false for hard link.
+ #
+ # ==== Examples
+ #
+ # create_link "config/apache.conf", "/etc/apache.conf"
+ #
+ def create_link(destination, *args)
+ config = args.last.is_a?(Hash) ? args.pop : {}
+ source = args.first
+ action CreateLink.new(self, destination, source, config)
+ end
+ alias_method :add_link, :create_link
+
+ # CreateLink is a subset of CreateFile, which instead of taking a block of
+ # data, just takes a source string from the user.
+ #
+ class CreateLink < CreateFile #:nodoc:
+ attr_reader :data
+
+ # Checks if the content of the file at the destination is identical to the rendered result.
+ #
+ # ==== Returns
+ # Boolean:: true if it is identical, false otherwise.
+ #
+ def identical?
+ exists? && File.identical?(render, destination)
+ end
+
+ def invoke!
+ invoke_with_conflict_check do
+ require "fileutils"
+ FileUtils.mkdir_p(File.dirname(destination))
+ # Create a symlink by default
+ config[:symbolic] = true if config[:symbolic].nil?
+ File.unlink(destination) if exists?
+ if config[:symbolic]
+ File.symlink(render, destination)
+ else
+ File.link(render, destination)
+ end
+ end
+ given_destination
+ end
+
+ def exists?
+ super || File.symlink?(destination)
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/directory.rb b/lib/bundler/vendor/thor/lib/thor/actions/directory.rb
new file mode 100644
index 0000000000..f555f7b7e0
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/actions/directory.rb
@@ -0,0 +1,118 @@
+require "bundler/vendor/thor/lib/thor/actions/empty_directory"
+
+class Bundler::Thor
+ module Actions
+ # Copies recursively the files from source directory to root directory.
+ # If any of the files finishes with .tt, it's considered to be a template
+ # and is placed in the destination without the extension .tt. If any
+ # empty directory is found, it's copied and all .empty_directory files are
+ # ignored. If any file name is wrapped within % signs, the text within
+ # the % signs will be executed as a method and replaced with the returned
+ # value. Let's suppose a doc directory with the following files:
+ #
+ # doc/
+ # components/.empty_directory
+ # README
+ # rdoc.rb.tt
+ # %app_name%.rb
+ #
+ # When invoked as:
+ #
+ # directory "doc"
+ #
+ # It will create a doc directory in the destination with the following
+ # files (assuming that the `app_name` method returns the value "blog"):
+ #
+ # doc/
+ # components/
+ # README
+ # rdoc.rb
+ # blog.rb
+ #
+ # <b>Encoded path note:</b> Since Bundler::Thor internals use Object#respond_to? to check if it can
+ # expand %something%, this `something` should be a public method in the class calling
+ # #directory. If a method is private, Bundler::Thor stack raises PrivateMethodEncodedError.
+ #
+ # ==== Parameters
+ # source<String>:: the relative path to the source root.
+ # destination<String>:: the relative path to the destination root.
+ # config<Hash>:: give :verbose => false to not log the status.
+ # If :recursive => false, does not look for paths recursively.
+ # If :mode => :preserve, preserve the file mode from the source.
+ # If :exclude_pattern => /regexp/, prevents copying files that match that regexp.
+ #
+ # ==== Examples
+ #
+ # directory "doc"
+ # directory "doc", "docs", :recursive => false
+ #
+ def directory(source, *args, &block)
+ config = args.last.is_a?(Hash) ? args.pop : {}
+ destination = args.first || source
+ action Directory.new(self, source, destination || source, config, &block)
+ end
+
+ class Directory < EmptyDirectory #:nodoc:
+ attr_reader :source
+
+ def initialize(base, source, destination = nil, config = {}, &block)
+ @source = File.expand_path(base.find_in_source_paths(source.to_s))
+ @block = block
+ super(base, destination, {:recursive => true}.merge(config))
+ end
+
+ def invoke!
+ base.empty_directory given_destination, config
+ execute!
+ end
+
+ def revoke!
+ execute!
+ end
+
+ protected
+
+ def execute!
+ lookup = Util.escape_globs(source)
+ lookup = config[:recursive] ? File.join(lookup, "**") : lookup
+ lookup = file_level_lookup(lookup)
+
+ files(lookup).sort.each do |file_source|
+ next if File.directory?(file_source)
+ next if config[:exclude_pattern] && file_source.match(config[:exclude_pattern])
+ file_destination = File.join(given_destination, file_source.gsub(source, "."))
+ file_destination.gsub!("/./", "/")
+
+ case file_source
+ when /\.empty_directory$/
+ dirname = File.dirname(file_destination).gsub(%r{/\.$}, "")
+ next if dirname == given_destination
+ base.empty_directory(dirname, config)
+ when /#{TEMPLATE_EXTNAME}$/
+ base.template(file_source, file_destination[0..-4], config, &@block)
+ else
+ base.copy_file(file_source, file_destination, config, &@block)
+ end
+ end
+ end
+
+ if RUBY_VERSION < "2.0"
+ def file_level_lookup(previous_lookup)
+ File.join(previous_lookup, "{*,.[a-z]*}")
+ end
+
+ def files(lookup)
+ Dir[lookup]
+ end
+ else
+ def file_level_lookup(previous_lookup)
+ File.join(previous_lookup, "*")
+ end
+
+ def files(lookup)
+ Dir.glob(lookup, File::FNM_DOTMATCH)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb b/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb
new file mode 100644
index 0000000000..284d92c19a
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb
@@ -0,0 +1,143 @@
+class Bundler::Thor
+ module Actions
+ # Creates an empty directory.
+ #
+ # ==== Parameters
+ # destination<String>:: the relative path to the destination root.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Examples
+ #
+ # empty_directory "doc"
+ #
+ def empty_directory(destination, config = {})
+ action EmptyDirectory.new(self, destination, config)
+ end
+
+ # Class which holds create directory logic. This is the base class for
+ # other actions like create_file and directory.
+ #
+ # This implementation is based in Templater actions, created by Jonas Nicklas
+ # and Michael S. Klishin under MIT LICENSE.
+ #
+ class EmptyDirectory #:nodoc:
+ attr_reader :base, :destination, :given_destination, :relative_destination, :config
+
+ # Initializes given the source and destination.
+ #
+ # ==== Parameters
+ # base<Bundler::Thor::Base>:: A Bundler::Thor::Base instance
+ # source<String>:: Relative path to the source of this file
+ # destination<String>:: Relative path to the destination of this file
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ def initialize(base, destination, config = {})
+ @base = base
+ @config = {:verbose => true}.merge(config)
+ self.destination = destination
+ end
+
+ # Checks if the destination file already exists.
+ #
+ # ==== Returns
+ # Boolean:: true if the file exists, false otherwise.
+ #
+ def exists?
+ ::File.exist?(destination)
+ end
+
+ def invoke!
+ invoke_with_conflict_check do
+ require "fileutils"
+ ::FileUtils.mkdir_p(destination)
+ end
+ end
+
+ def revoke!
+ say_status :remove, :red
+ require "fileutils"
+ ::FileUtils.rm_rf(destination) if !pretend? && exists?
+ given_destination
+ end
+
+ protected
+
+ # Shortcut for pretend.
+ #
+ def pretend?
+ base.options[:pretend]
+ end
+
+ # Sets the absolute destination value from a relative destination value.
+ # It also stores the given and relative destination. Let's suppose our
+ # script is being executed on "dest", it sets the destination root to
+ # "dest". The destination, given_destination and relative_destination
+ # are related in the following way:
+ #
+ # inside "bar" do
+ # empty_directory "baz"
+ # end
+ #
+ # destination #=> dest/bar/baz
+ # relative_destination #=> bar/baz
+ # given_destination #=> baz
+ #
+ def destination=(destination)
+ return unless destination
+ @given_destination = convert_encoded_instructions(destination.to_s)
+ @destination = ::File.expand_path(@given_destination, base.destination_root)
+ @relative_destination = base.relative_to_original_destination_root(@destination)
+ end
+
+ # Filenames in the encoded form are converted. If you have a file:
+ #
+ # %file_name%.rb
+ #
+ # It calls #file_name from the base and replaces %-string with the
+ # return value (should be String) of #file_name:
+ #
+ # user.rb
+ #
+ # The method referenced can be either public or private.
+ #
+ def convert_encoded_instructions(filename)
+ filename.gsub(/%(.*?)%/) do |initial_string|
+ method = $1.strip
+ base.respond_to?(method, true) ? base.send(method) : initial_string
+ end
+ end
+
+ # Receives a hash of options and just execute the block if some
+ # conditions are met.
+ #
+ def invoke_with_conflict_check(&block)
+ if exists?
+ on_conflict_behavior(&block)
+ else
+ yield unless pretend?
+ say_status :create, :green
+ end
+
+ destination
+ rescue Errno::EISDIR, Errno::EEXIST
+ on_file_clash_behavior
+ end
+
+ def on_file_clash_behavior
+ say_status :file_clash, :red
+ end
+
+ # What to do when the destination file already exists.
+ #
+ def on_conflict_behavior
+ say_status :exist, :blue
+ end
+
+ # Shortcut to say_status shell method.
+ #
+ def say_status(status, color)
+ base.shell.say_status status, relative_destination, color if config[:verbose]
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb
new file mode 100644
index 0000000000..4c83bebc86
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb
@@ -0,0 +1,364 @@
+require "erb"
+
+class Bundler::Thor
+ module Actions
+ # Copies the file from the relative source to the relative destination. If
+ # the destination is not given it's assumed to be equal to the source.
+ #
+ # ==== Parameters
+ # source<String>:: the relative path to the source root.
+ # destination<String>:: the relative path to the destination root.
+ # config<Hash>:: give :verbose => false to not log the status, and
+ # :mode => :preserve, to preserve the file mode from the source.
+
+ #
+ # ==== Examples
+ #
+ # copy_file "README", "doc/README"
+ #
+ # copy_file "doc/README"
+ #
+ def copy_file(source, *args, &block)
+ config = args.last.is_a?(Hash) ? args.pop : {}
+ destination = args.first || source
+ source = File.expand_path(find_in_source_paths(source.to_s))
+
+ create_file destination, nil, config do
+ content = File.binread(source)
+ content = yield(content) if block
+ content
+ end
+ if config[:mode] == :preserve
+ mode = File.stat(source).mode
+ chmod(destination, mode, config)
+ end
+ end
+
+ # Links the file from the relative source to the relative destination. If
+ # the destination is not given it's assumed to be equal to the source.
+ #
+ # ==== Parameters
+ # source<String>:: the relative path to the source root.
+ # destination<String>:: the relative path to the destination root.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Examples
+ #
+ # link_file "README", "doc/README"
+ #
+ # link_file "doc/README"
+ #
+ def link_file(source, *args)
+ config = args.last.is_a?(Hash) ? args.pop : {}
+ destination = args.first || source
+ source = File.expand_path(find_in_source_paths(source.to_s))
+
+ create_link destination, source, config
+ end
+
+ # Gets the content at the given address and places it at the given relative
+ # destination. If a block is given instead of destination, the content of
+ # the url is yielded and used as location.
+ #
+ # ==== Parameters
+ # source<String>:: the address of the given content.
+ # destination<String>:: the relative path to the destination root.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Examples
+ #
+ # get "http://gist.github.com/103208", "doc/README"
+ #
+ # get "http://gist.github.com/103208" do |content|
+ # content.split("\n").first
+ # end
+ #
+ def get(source, *args, &block)
+ config = args.last.is_a?(Hash) ? args.pop : {}
+ destination = args.first
+
+ if source =~ %r{^https?\://}
+ require "open-uri"
+ else
+ source = File.expand_path(find_in_source_paths(source.to_s))
+ end
+
+ render = open(source) { |input| input.binmode.read }
+
+ destination ||= if block_given?
+ block.arity == 1 ? yield(render) : yield
+ else
+ File.basename(source)
+ end
+
+ create_file destination, render, config
+ end
+
+ # Gets an ERB template at the relative source, executes it and makes a copy
+ # at the relative destination. If the destination is not given it's assumed
+ # to be equal to the source removing .tt from the filename.
+ #
+ # ==== Parameters
+ # source<String>:: the relative path to the source root.
+ # destination<String>:: the relative path to the destination root.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Examples
+ #
+ # template "README", "doc/README"
+ #
+ # template "doc/README"
+ #
+ def template(source, *args, &block)
+ config = args.last.is_a?(Hash) ? args.pop : {}
+ destination = args.first || source.sub(/#{TEMPLATE_EXTNAME}$/, "")
+
+ source = File.expand_path(find_in_source_paths(source.to_s))
+ context = config.delete(:context) || instance_eval("binding")
+
+ create_file destination, nil, config do
+ content = CapturableERB.new(::File.binread(source), nil, "-", "@output_buffer").tap do |erb|
+ erb.filename = source
+ end.result(context)
+ content = yield(content) if block
+ content
+ end
+ end
+
+ # Changes the mode of the given file or directory.
+ #
+ # ==== Parameters
+ # mode<Integer>:: the file mode
+ # path<String>:: the name of the file to change mode
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Example
+ #
+ # chmod "script/server", 0755
+ #
+ def chmod(path, mode, config = {})
+ return unless behavior == :invoke
+ path = File.expand_path(path, destination_root)
+ say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true)
+ unless options[:pretend]
+ require "fileutils"
+ FileUtils.chmod_R(mode, path)
+ end
+ end
+
+ # Prepend text to a file. Since it depends on insert_into_file, it's reversible.
+ #
+ # ==== Parameters
+ # path<String>:: path of the file to be changed
+ # data<String>:: the data to prepend to the file, can be also given as a block.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Example
+ #
+ # prepend_to_file 'config/environments/test.rb', 'config.gem "rspec"'
+ #
+ # prepend_to_file 'config/environments/test.rb' do
+ # 'config.gem "rspec"'
+ # end
+ #
+ def prepend_to_file(path, *args, &block)
+ config = args.last.is_a?(Hash) ? args.pop : {}
+ config[:after] = /\A/
+ insert_into_file(path, *(args << config), &block)
+ end
+ alias_method :prepend_file, :prepend_to_file
+
+ # Append text to a file. Since it depends on insert_into_file, it's reversible.
+ #
+ # ==== Parameters
+ # path<String>:: path of the file to be changed
+ # data<String>:: the data to append to the file, can be also given as a block.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Example
+ #
+ # append_to_file 'config/environments/test.rb', 'config.gem "rspec"'
+ #
+ # append_to_file 'config/environments/test.rb' do
+ # 'config.gem "rspec"'
+ # end
+ #
+ def append_to_file(path, *args, &block)
+ config = args.last.is_a?(Hash) ? args.pop : {}
+ config[:before] = /\z/
+ insert_into_file(path, *(args << config), &block)
+ end
+ alias_method :append_file, :append_to_file
+
+ # Injects text right after the class definition. Since it depends on
+ # insert_into_file, it's reversible.
+ #
+ # ==== Parameters
+ # path<String>:: path of the file to be changed
+ # klass<String|Class>:: the class to be manipulated
+ # data<String>:: the data to append to the class, can be also given as a block.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Examples
+ #
+ # inject_into_class "app/controllers/application_controller.rb", ApplicationController, " filter_parameter :password\n"
+ #
+ # inject_into_class "app/controllers/application_controller.rb", ApplicationController do
+ # " filter_parameter :password\n"
+ # end
+ #
+ def inject_into_class(path, klass, *args, &block)
+ config = args.last.is_a?(Hash) ? args.pop : {}
+ config[:after] = /class #{klass}\n|class #{klass} .*\n/
+ insert_into_file(path, *(args << config), &block)
+ end
+
+ # Injects text right after the module definition. Since it depends on
+ # insert_into_file, it's reversible.
+ #
+ # ==== Parameters
+ # path<String>:: path of the file to be changed
+ # module_name<String|Class>:: the module to be manipulated
+ # data<String>:: the data to append to the class, can be also given as a block.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Examples
+ #
+ # inject_into_module "app/helpers/application_helper.rb", ApplicationHelper, " def help; 'help'; end\n"
+ #
+ # inject_into_module "app/helpers/application_helper.rb", ApplicationHelper do
+ # " def help; 'help'; end\n"
+ # end
+ #
+ def inject_into_module(path, module_name, *args, &block)
+ config = args.last.is_a?(Hash) ? args.pop : {}
+ config[:after] = /module #{module_name}\n|module #{module_name} .*\n/
+ insert_into_file(path, *(args << config), &block)
+ end
+
+ # Run a regular expression replacement on a file.
+ #
+ # ==== Parameters
+ # path<String>:: path of the file to be changed
+ # flag<Regexp|String>:: the regexp or string to be replaced
+ # replacement<String>:: the replacement, can be also given as a block
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Example
+ #
+ # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'
+ #
+ # gsub_file 'README', /rake/, :green do |match|
+ # match << " no more. Use thor!"
+ # end
+ #
+ def gsub_file(path, flag, *args, &block)
+ return unless behavior == :invoke
+ config = args.last.is_a?(Hash) ? args.pop : {}
+
+ path = File.expand_path(path, destination_root)
+ say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true)
+
+ unless options[:pretend]
+ content = File.binread(path)
+ content.gsub!(flag, *args, &block)
+ File.open(path, "wb") { |file| file.write(content) }
+ end
+ end
+
+ # Uncomment all lines matching a given regex. It will leave the space
+ # which existed before the comment hash in tact but will remove any spacing
+ # between the comment hash and the beginning of the line.
+ #
+ # ==== Parameters
+ # path<String>:: path of the file to be changed
+ # flag<Regexp|String>:: the regexp or string used to decide which lines to uncomment
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Example
+ #
+ # uncomment_lines 'config/initializers/session_store.rb', /active_record/
+ #
+ def uncomment_lines(path, flag, *args)
+ flag = flag.respond_to?(:source) ? flag.source : flag
+
+ gsub_file(path, /^(\s*)#[[:blank:]]*(.*#{flag})/, '\1\2', *args)
+ end
+
+ # Comment all lines matching a given regex. It will leave the space
+ # which existed before the beginning of the line in tact and will insert
+ # a single space after the comment hash.
+ #
+ # ==== Parameters
+ # path<String>:: path of the file to be changed
+ # flag<Regexp|String>:: the regexp or string used to decide which lines to comment
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Example
+ #
+ # comment_lines 'config/initializers/session_store.rb', /cookie_store/
+ #
+ def comment_lines(path, flag, *args)
+ flag = flag.respond_to?(:source) ? flag.source : flag
+
+ gsub_file(path, /^(\s*)([^#|\n]*#{flag})/, '\1# \2', *args)
+ end
+
+ # Removes a file at the given location.
+ #
+ # ==== Parameters
+ # path<String>:: path of the file to be changed
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Example
+ #
+ # remove_file 'README'
+ # remove_file 'app/controllers/application_controller.rb'
+ #
+ def remove_file(path, config = {})
+ return unless behavior == :invoke
+ path = File.expand_path(path, destination_root)
+
+ say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true)
+ if !options[:pretend] && File.exist?(path)
+ require "fileutils"
+ ::FileUtils.rm_rf(path)
+ end
+ end
+ alias_method :remove_dir, :remove_file
+
+ attr_accessor :output_buffer
+ private :output_buffer, :output_buffer=
+
+ private
+
+ def concat(string)
+ @output_buffer.concat(string)
+ end
+
+ def capture(*args)
+ with_output_buffer { yield(*args) }
+ end
+
+ def with_output_buffer(buf = "".dup) #:nodoc:
+ raise ArgumentError, "Buffer can not be a frozen object" if buf.frozen?
+ old_buffer = output_buffer
+ self.output_buffer = buf
+ yield
+ output_buffer
+ ensure
+ self.output_buffer = old_buffer
+ end
+
+ # Bundler::Thor::Actions#capture depends on what kind of buffer is used in ERB.
+ # Thus CapturableERB fixes ERB to use String buffer.
+ class CapturableERB < ERB
+ def set_eoutvar(compiler, eoutvar = "_erbout")
+ compiler.put_cmd = "#{eoutvar}.concat"
+ compiler.insert_cmd = "#{eoutvar}.concat"
+ compiler.pre_cmd = ["#{eoutvar} = ''.dup"]
+ compiler.post_cmd = [eoutvar]
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb b/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb
new file mode 100644
index 0000000000..349b26ff65
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb
@@ -0,0 +1,109 @@
+require "bundler/vendor/thor/lib/thor/actions/empty_directory"
+
+class Bundler::Thor
+ module Actions
+ # Injects the given content into a file. Different from gsub_file, this
+ # method is reversible.
+ #
+ # ==== Parameters
+ # destination<String>:: Relative path to the destination root
+ # data<String>:: Data to add to the file. Can be given as a block.
+ # config<Hash>:: give :verbose => false to not log the status and the flag
+ # for injection (:after or :before) or :force => true for
+ # insert two or more times the same content.
+ #
+ # ==== Examples
+ #
+ # insert_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n"
+ #
+ # insert_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do
+ # gems = ask "Which gems would you like to add?"
+ # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n")
+ # end
+ #
+ def insert_into_file(destination, *args, &block)
+ data = block_given? ? block : args.shift
+ config = args.shift
+ action InjectIntoFile.new(self, destination, data, config)
+ end
+ alias_method :inject_into_file, :insert_into_file
+
+ class InjectIntoFile < EmptyDirectory #:nodoc:
+ attr_reader :replacement, :flag, :behavior
+
+ def initialize(base, destination, data, config)
+ super(base, destination, {:verbose => true}.merge(config))
+
+ @behavior, @flag = if @config.key?(:after)
+ [:after, @config.delete(:after)]
+ else
+ [:before, @config.delete(:before)]
+ end
+
+ @replacement = data.is_a?(Proc) ? data.call : data
+ @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp)
+ end
+
+ def invoke!
+ say_status :invoke
+
+ content = if @behavior == :after
+ '\0' + replacement
+ else
+ replacement + '\0'
+ end
+
+ if exists?
+ replace!(/#{flag}/, content, config[:force])
+ else
+ unless pretend?
+ raise Bundler::Thor::Error, "The file #{ destination } does not appear to exist"
+ end
+ end
+ end
+
+ def revoke!
+ say_status :revoke
+
+ regexp = if @behavior == :after
+ content = '\1\2'
+ /(#{flag})(.*)(#{Regexp.escape(replacement)})/m
+ else
+ content = '\2\3'
+ /(#{Regexp.escape(replacement)})(.*)(#{flag})/m
+ end
+
+ replace!(regexp, content, true)
+ end
+
+ protected
+
+ def say_status(behavior)
+ status = if behavior == :invoke
+ if flag == /\A/
+ :prepend
+ elsif flag == /\z/
+ :append
+ else
+ :insert
+ end
+ else
+ :subtract
+ end
+
+ super(status, config[:verbose])
+ end
+
+ # Adds the content to the file.
+ #
+ def replace!(regexp, string, force)
+ return if pretend?
+ content = File.read(destination)
+ if force || !content.include?(replacement)
+ content.gsub!(regexp, string)
+ File.open(destination, "wb") { |file| file.write(content) }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/base.rb b/lib/bundler/vendor/thor/lib/thor/base.rb
new file mode 100644
index 0000000000..9bd1077170
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/base.rb
@@ -0,0 +1,679 @@
+require "bundler/vendor/thor/lib/thor/command"
+require "bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access"
+require "bundler/vendor/thor/lib/thor/core_ext/ordered_hash"
+require "bundler/vendor/thor/lib/thor/error"
+require "bundler/vendor/thor/lib/thor/invocation"
+require "bundler/vendor/thor/lib/thor/parser"
+require "bundler/vendor/thor/lib/thor/shell"
+require "bundler/vendor/thor/lib/thor/line_editor"
+require "bundler/vendor/thor/lib/thor/util"
+
+class Bundler::Thor
+ autoload :Actions, "bundler/vendor/thor/lib/thor/actions"
+ autoload :RakeCompat, "bundler/vendor/thor/lib/thor/rake_compat"
+ autoload :Group, "bundler/vendor/thor/lib/thor/group"
+
+ # Shortcuts for help.
+ HELP_MAPPINGS = %w(-h -? --help -D)
+
+ # Bundler::Thor methods that should not be overwritten by the user.
+ THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root
+ action add_file create_file in_root inside run run_ruby_script)
+
+ TEMPLATE_EXTNAME = ".tt"
+
+ module Base
+ attr_accessor :options, :parent_options, :args
+
+ # It receives arguments in an Array and two hashes, one for options and
+ # other for configuration.
+ #
+ # Notice that it does not check if all required arguments were supplied.
+ # It should be done by the parser.
+ #
+ # ==== Parameters
+ # args<Array[Object]>:: An array of objects. The objects are applied to their
+ # respective accessors declared with <tt>argument</tt>.
+ #
+ # options<Hash>:: An options hash that will be available as self.options.
+ # The hash given is converted to a hash with indifferent
+ # access, magic predicates (options.skip?) and then frozen.
+ #
+ # config<Hash>:: Configuration for this Bundler::Thor class.
+ #
+ def initialize(args = [], local_options = {}, config = {})
+ parse_options = self.class.class_options
+
+ # The start method splits inbound arguments at the first argument
+ # that looks like an option (starts with - or --). It then calls
+ # new, passing in the two halves of the arguments Array as the
+ # first two parameters.
+
+ command_options = config.delete(:command_options) # hook for start
+ parse_options = parse_options.merge(command_options) if command_options
+ if local_options.is_a?(Array)
+ array_options = local_options
+ hash_options = {}
+ else
+ # Handle the case where the class was explicitly instantiated
+ # with pre-parsed options.
+ array_options = []
+ hash_options = local_options
+ end
+
+ # Let Bundler::Thor::Options parse the options first, so it can remove
+ # declared options from the array. This will leave us with
+ # a list of arguments that weren't declared.
+ stop_on_unknown = self.class.stop_on_unknown_option? config[:current_command]
+ disable_required_check = self.class.disable_required_check? config[:current_command]
+ opts = Bundler::Thor::Options.new(parse_options, hash_options, stop_on_unknown, disable_required_check)
+ self.options = opts.parse(array_options)
+ self.options = config[:class_options].merge(options) if config[:class_options]
+
+ # If unknown options are disallowed, make sure that none of the
+ # remaining arguments looks like an option.
+ opts.check_unknown! if self.class.check_unknown_options?(config)
+
+ # Add the remaining arguments from the options parser to the
+ # arguments passed in to initialize. Then remove any positional
+ # arguments declared using #argument (this is primarily used
+ # by Bundler::Thor::Group). Tis will leave us with the remaining
+ # positional arguments.
+ to_parse = args
+ to_parse += opts.remaining unless self.class.strict_args_position?(config)
+
+ thor_args = Bundler::Thor::Arguments.new(self.class.arguments)
+ thor_args.parse(to_parse).each { |k, v| __send__("#{k}=", v) }
+ @args = thor_args.remaining
+ end
+
+ class << self
+ def included(base) #:nodoc:
+ base.extend ClassMethods
+ base.send :include, Invocation
+ base.send :include, Shell
+ end
+
+ # Returns the classes that inherits from Bundler::Thor or Bundler::Thor::Group.
+ #
+ # ==== Returns
+ # Array[Class]
+ #
+ def subclasses
+ @subclasses ||= []
+ end
+
+ # Returns the files where the subclasses are kept.
+ #
+ # ==== Returns
+ # Hash[path<String> => Class]
+ #
+ def subclass_files
+ @subclass_files ||= Hash.new { |h, k| h[k] = [] }
+ end
+
+ # Whenever a class inherits from Bundler::Thor or Bundler::Thor::Group, we should track the
+ # class and the file on Bundler::Thor::Base. This is the method responsable for it.
+ #
+ def register_klass_file(klass) #:nodoc:
+ file = caller[1].match(/(.*):\d+/)[1]
+ Bundler::Thor::Base.subclasses << klass unless Bundler::Thor::Base.subclasses.include?(klass)
+
+ file_subclasses = Bundler::Thor::Base.subclass_files[File.expand_path(file)]
+ file_subclasses << klass unless file_subclasses.include?(klass)
+ end
+ end
+
+ module ClassMethods
+ def attr_reader(*) #:nodoc:
+ no_commands { super }
+ end
+
+ def attr_writer(*) #:nodoc:
+ no_commands { super }
+ end
+
+ def attr_accessor(*) #:nodoc:
+ no_commands { super }
+ end
+
+ # If you want to raise an error for unknown options, call check_unknown_options!
+ # This is disabled by default to allow dynamic invocations.
+ def check_unknown_options!
+ @check_unknown_options = true
+ end
+
+ def check_unknown_options #:nodoc:
+ @check_unknown_options ||= from_superclass(:check_unknown_options, false)
+ end
+
+ def check_unknown_options?(config) #:nodoc:
+ !!check_unknown_options
+ end
+
+ # If you want to raise an error when the default value of an option does not match
+ # the type call check_default_type!
+ # This is disabled by default for compatibility.
+ def check_default_type!
+ @check_default_type = true
+ end
+
+ def check_default_type #:nodoc:
+ @check_default_type ||= from_superclass(:check_default_type, false)
+ end
+
+ def check_default_type? #:nodoc:
+ !!check_default_type
+ end
+
+ # If true, option parsing is suspended as soon as an unknown option or a
+ # regular argument is encountered. All remaining arguments are passed to
+ # the command as regular arguments.
+ def stop_on_unknown_option?(command_name) #:nodoc:
+ false
+ end
+
+ # If true, option set will not suspend the execution of the command when
+ # a required option is not provided.
+ def disable_required_check?(command_name) #:nodoc:
+ false
+ end
+
+ # If you want only strict string args (useful when cascading thor classes),
+ # call strict_args_position! This is disabled by default to allow dynamic
+ # invocations.
+ def strict_args_position!
+ @strict_args_position = true
+ end
+
+ def strict_args_position #:nodoc:
+ @strict_args_position ||= from_superclass(:strict_args_position, false)
+ end
+
+ def strict_args_position?(config) #:nodoc:
+ !!strict_args_position
+ end
+
+ # Adds an argument to the class and creates an attr_accessor for it.
+ #
+ # Arguments are different from options in several aspects. The first one
+ # is how they are parsed from the command line, arguments are retrieved
+ # from position:
+ #
+ # thor command NAME
+ #
+ # Instead of:
+ #
+ # thor command --name=NAME
+ #
+ # Besides, arguments are used inside your code as an accessor (self.argument),
+ # while options are all kept in a hash (self.options).
+ #
+ # Finally, arguments cannot have type :default or :boolean but can be
+ # optional (supplying :optional => :true or :required => false), although
+ # you cannot have a required argument after a non-required argument. If you
+ # try it, an error is raised.
+ #
+ # ==== Parameters
+ # name<Symbol>:: The name of the argument.
+ # options<Hash>:: Described below.
+ #
+ # ==== Options
+ # :desc - Description for the argument.
+ # :required - If the argument is required or not.
+ # :optional - If the argument is optional or not.
+ # :type - The type of the argument, can be :string, :hash, :array, :numeric.
+ # :default - Default value for this argument. It cannot be required and have default values.
+ # :banner - String to show on usage notes.
+ #
+ # ==== Errors
+ # ArgumentError:: Raised if you supply a required argument after a non required one.
+ #
+ def argument(name, options = {})
+ is_thor_reserved_word?(name, :argument)
+ no_commands { attr_accessor name }
+
+ required = if options.key?(:optional)
+ !options[:optional]
+ elsif options.key?(:required)
+ options[:required]
+ else
+ options[:default].nil?
+ end
+
+ remove_argument name
+
+ if required
+ arguments.each do |argument|
+ next if argument.required?
+ raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " \
+ "the non-required argument #{argument.human_name.inspect}."
+ end
+ end
+
+ options[:required] = required
+
+ arguments << Bundler::Thor::Argument.new(name, options)
+ end
+
+ # Returns this class arguments, looking up in the ancestors chain.
+ #
+ # ==== Returns
+ # Array[Bundler::Thor::Argument]
+ #
+ def arguments
+ @arguments ||= from_superclass(:arguments, [])
+ end
+
+ # Adds a bunch of options to the set of class options.
+ #
+ # class_options :foo => false, :bar => :required, :baz => :string
+ #
+ # If you prefer more detailed declaration, check class_option.
+ #
+ # ==== Parameters
+ # Hash[Symbol => Object]
+ #
+ def class_options(options = nil)
+ @class_options ||= from_superclass(:class_options, {})
+ build_options(options, @class_options) if options
+ @class_options
+ end
+
+ # Adds an option to the set of class options
+ #
+ # ==== Parameters
+ # name<Symbol>:: The name of the argument.
+ # options<Hash>:: Described below.
+ #
+ # ==== Options
+ # :desc:: -- Description for the argument.
+ # :required:: -- If the argument is required or not.
+ # :default:: -- Default value for this argument.
+ # :group:: -- The group for this options. Use by class options to output options in different levels.
+ # :aliases:: -- Aliases for this option. <b>Note:</b> Bundler::Thor follows a convention of one-dash-one-letter options. Thus aliases like "-something" wouldn't be parsed; use either "\--something" or "-s" instead.
+ # :type:: -- The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
+ # :banner:: -- String to show on usage notes.
+ # :hide:: -- If you want to hide this option from the help.
+ #
+ def class_option(name, options = {})
+ build_option(name, options, class_options)
+ end
+
+ # Removes a previous defined argument. If :undefine is given, undefine
+ # accessors as well.
+ #
+ # ==== Parameters
+ # names<Array>:: Arguments to be removed
+ #
+ # ==== Examples
+ #
+ # remove_argument :foo
+ # remove_argument :foo, :bar, :baz, :undefine => true
+ #
+ def remove_argument(*names)
+ options = names.last.is_a?(Hash) ? names.pop : {}
+
+ names.each do |name|
+ arguments.delete_if { |a| a.name == name.to_s }
+ undef_method name, "#{name}=" if options[:undefine]
+ end
+ end
+
+ # Removes a previous defined class option.
+ #
+ # ==== Parameters
+ # names<Array>:: Class options to be removed
+ #
+ # ==== Examples
+ #
+ # remove_class_option :foo
+ # remove_class_option :foo, :bar, :baz
+ #
+ def remove_class_option(*names)
+ names.each do |name|
+ class_options.delete(name)
+ end
+ end
+
+ # Defines the group. This is used when thor list is invoked so you can specify
+ # that only commands from a pre-defined group will be shown. Defaults to standard.
+ #
+ # ==== Parameters
+ # name<String|Symbol>
+ #
+ def group(name = nil)
+ if name
+ @group = name.to_s
+ else
+ @group ||= from_superclass(:group, "standard")
+ end
+ end
+
+ # Returns the commands for this Bundler::Thor class.
+ #
+ # ==== Returns
+ # OrderedHash:: An ordered hash with commands names as keys and Bundler::Thor::Command
+ # objects as values.
+ #
+ def commands
+ @commands ||= Bundler::Thor::CoreExt::OrderedHash.new
+ end
+ alias_method :tasks, :commands
+
+ # Returns the commands for this Bundler::Thor class and all subclasses.
+ #
+ # ==== Returns
+ # OrderedHash:: An ordered hash with commands names as keys and Bundler::Thor::Command
+ # objects as values.
+ #
+ def all_commands
+ @all_commands ||= from_superclass(:all_commands, Bundler::Thor::CoreExt::OrderedHash.new)
+ @all_commands.merge!(commands)
+ end
+ alias_method :all_tasks, :all_commands
+
+ # Removes a given command from this Bundler::Thor class. This is usually done if you
+ # are inheriting from another class and don't want it to be available
+ # anymore.
+ #
+ # By default it only remove the mapping to the command. But you can supply
+ # :undefine => true to undefine the method from the class as well.
+ #
+ # ==== Parameters
+ # name<Symbol|String>:: The name of the command to be removed
+ # options<Hash>:: You can give :undefine => true if you want commands the method
+ # to be undefined from the class as well.
+ #
+ def remove_command(*names)
+ options = names.last.is_a?(Hash) ? names.pop : {}
+
+ names.each do |name|
+ commands.delete(name.to_s)
+ all_commands.delete(name.to_s)
+ undef_method name if options[:undefine]
+ end
+ end
+ alias_method :remove_task, :remove_command
+
+ # All methods defined inside the given block are not added as commands.
+ #
+ # So you can do:
+ #
+ # class MyScript < Bundler::Thor
+ # no_commands do
+ # def this_is_not_a_command
+ # end
+ # end
+ # end
+ #
+ # You can also add the method and remove it from the command list:
+ #
+ # class MyScript < Bundler::Thor
+ # def this_is_not_a_command
+ # end
+ # remove_command :this_is_not_a_command
+ # end
+ #
+ def no_commands
+ @no_commands = true
+ yield
+ ensure
+ @no_commands = false
+ end
+ alias_method :no_tasks, :no_commands
+
+ # Sets the namespace for the Bundler::Thor or Bundler::Thor::Group class. By default the
+ # namespace is retrieved from the class name. If your Bundler::Thor class is named
+ # Scripts::MyScript, the help method, for example, will be called as:
+ #
+ # thor scripts:my_script -h
+ #
+ # If you change the namespace:
+ #
+ # namespace :my_scripts
+ #
+ # You change how your commands are invoked:
+ #
+ # thor my_scripts -h
+ #
+ # Finally, if you change your namespace to default:
+ #
+ # namespace :default
+ #
+ # Your commands can be invoked with a shortcut. Instead of:
+ #
+ # thor :my_command
+ #
+ def namespace(name = nil)
+ if name
+ @namespace = name.to_s
+ else
+ @namespace ||= Bundler::Thor::Util.namespace_from_thor_class(self)
+ end
+ end
+
+ # Parses the command and options from the given args, instantiate the class
+ # and invoke the command. This method is used when the arguments must be parsed
+ # from an array. If you are inside Ruby and want to use a Bundler::Thor class, you
+ # can simply initialize it:
+ #
+ # script = MyScript.new(args, options, config)
+ # script.invoke(:command, first_arg, second_arg, third_arg)
+ #
+ def start(given_args = ARGV, config = {})
+ config[:shell] ||= Bundler::Thor::Base.shell.new
+ dispatch(nil, given_args.dup, nil, config)
+ rescue Bundler::Thor::Error => e
+ config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
+ exit(1) if exit_on_failure?
+ rescue Errno::EPIPE
+ # This happens if a thor command is piped to something like `head`,
+ # which closes the pipe when it's done reading. This will also
+ # mean that if the pipe is closed, further unnecessary
+ # computation will not occur.
+ exit(0)
+ end
+
+ # Allows to use private methods from parent in child classes as commands.
+ #
+ # ==== Parameters
+ # names<Array>:: Method names to be used as commands
+ #
+ # ==== Examples
+ #
+ # public_command :foo
+ # public_command :foo, :bar, :baz
+ #
+ def public_command(*names)
+ names.each do |name|
+ class_eval "def #{name}(*); super end"
+ end
+ end
+ alias_method :public_task, :public_command
+
+ def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc:
+ raise UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace." if has_namespace
+ raise UndefinedCommandError, "Could not find command #{command.inspect}."
+ end
+ alias_method :handle_no_task_error, :handle_no_command_error
+
+ def handle_argument_error(command, error, args, arity) #:nodoc:
+ name = [command.ancestor_name, command.name].compact.join(" ")
+ msg = "ERROR: \"#{basename} #{name}\" was called with ".dup
+ msg << "no arguments" if args.empty?
+ msg << "arguments " << args.inspect unless args.empty?
+ msg << "\nUsage: #{banner(command).inspect}"
+ raise InvocationError, msg
+ end
+
+ protected
+
+ # Prints the class options per group. If an option does not belong to
+ # any group, it's printed as Class option.
+ #
+ def class_options_help(shell, groups = {}) #:nodoc:
+ # Group options by group
+ class_options.each do |_, value|
+ groups[value.group] ||= []
+ groups[value.group] << value
+ end
+
+ # Deal with default group
+ global_options = groups.delete(nil) || []
+ print_options(shell, global_options)
+
+ # Print all others
+ groups.each do |group_name, options|
+ print_options(shell, options, group_name)
+ end
+ end
+
+ # Receives a set of options and print them.
+ def print_options(shell, options, group_name = nil)
+ return if options.empty?
+
+ list = []
+ padding = options.map { |o| o.aliases.size }.max.to_i * 4
+
+ options.each do |option|
+ next if option.hide
+ item = [option.usage(padding)]
+ item.push(option.description ? "# #{option.description}" : "")
+
+ list << item
+ list << ["", "# Default: #{option.default}"] if option.show_default?
+ list << ["", "# Possible values: #{option.enum.join(', ')}"] if option.enum
+ end
+
+ shell.say(group_name ? "#{group_name} options:" : "Options:")
+ shell.print_table(list, :indent => 2)
+ shell.say ""
+ end
+
+ # Raises an error if the word given is a Bundler::Thor reserved word.
+ def is_thor_reserved_word?(word, type) #:nodoc:
+ return false unless THOR_RESERVED_WORDS.include?(word.to_s)
+ raise "#{word.inspect} is a Bundler::Thor reserved word and cannot be defined as #{type}"
+ end
+
+ # Build an option and adds it to the given scope.
+ #
+ # ==== Parameters
+ # name<Symbol>:: The name of the argument.
+ # options<Hash>:: Described in both class_option and method_option.
+ # scope<Hash>:: Options hash that is being built up
+ def build_option(name, options, scope) #:nodoc:
+ scope[name] = Bundler::Thor::Option.new(name, options.merge(:check_default_type => check_default_type?))
+ end
+
+ # Receives a hash of options, parse them and add to the scope. This is a
+ # fast way to set a bunch of options:
+ #
+ # build_options :foo => true, :bar => :required, :baz => :string
+ #
+ # ==== Parameters
+ # Hash[Symbol => Object]
+ def build_options(options, scope) #:nodoc:
+ options.each do |key, value|
+ scope[key] = Bundler::Thor::Option.parse(key, value)
+ end
+ end
+
+ # Finds a command with the given name. If the command belongs to the current
+ # class, just return it, otherwise dup it and add the fresh copy to the
+ # current command hash.
+ def find_and_refresh_command(name) #:nodoc:
+ if commands[name.to_s]
+ commands[name.to_s]
+ elsif command = all_commands[name.to_s] # rubocop:disable AssignmentInCondition
+ commands[name.to_s] = command.clone
+ else
+ raise ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found."
+ end
+ end
+ alias_method :find_and_refresh_task, :find_and_refresh_command
+
+ # Everytime someone inherits from a Bundler::Thor class, register the klass
+ # and file into baseclass.
+ def inherited(klass)
+ Bundler::Thor::Base.register_klass_file(klass)
+ klass.instance_variable_set(:@no_commands, false)
+ end
+
+ # Fire this callback whenever a method is added. Added methods are
+ # tracked as commands by invoking the create_command method.
+ def method_added(meth)
+ meth = meth.to_s
+
+ if meth == "initialize"
+ initialize_added
+ return
+ end
+
+ # Return if it's not a public instance method
+ return unless public_method_defined?(meth.to_sym)
+
+ @no_commands ||= false
+ return if @no_commands || !create_command(meth)
+
+ is_thor_reserved_word?(meth, :command)
+ Bundler::Thor::Base.register_klass_file(self)
+ end
+
+ # Retrieves a value from superclass. If it reaches the baseclass,
+ # returns default.
+ def from_superclass(method, default = nil)
+ if self == baseclass || !superclass.respond_to?(method, true)
+ default
+ else
+ value = superclass.send(method)
+
+ # Ruby implements `dup` on Object, but raises a `TypeError`
+ # if the method is called on immediates. As a result, we
+ # don't have a good way to check whether dup will succeed
+ # without calling it and rescuing the TypeError.
+ begin
+ value.dup
+ rescue TypeError
+ value
+ end
+
+ end
+ end
+
+ # A flag that makes the process exit with status 1 if any error happens.
+ def exit_on_failure?
+ false
+ end
+
+ #
+ # The basename of the program invoking the thor class.
+ #
+ def basename
+ File.basename($PROGRAM_NAME).split(" ").first
+ end
+
+ # SIGNATURE: Sets the baseclass. This is where the superclass lookup
+ # finishes.
+ def baseclass #:nodoc:
+ end
+
+ # SIGNATURE: Creates a new command if valid_command? is true. This method is
+ # called when a new method is added to the class.
+ def create_command(meth) #:nodoc:
+ end
+ alias_method :create_task, :create_command
+
+ # SIGNATURE: Defines behavior when the initialize method is added to the
+ # class.
+ def initialize_added #:nodoc:
+ end
+
+ # SIGNATURE: The hook invoked by start.
+ def dispatch(command, given_args, given_opts, config) #:nodoc:
+ raise NotImplementedError
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/command.rb b/lib/bundler/vendor/thor/lib/thor/command.rb
new file mode 100644
index 0000000000..c636948e5d
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/command.rb
@@ -0,0 +1,135 @@
+class Bundler::Thor
+ class Command < Struct.new(:name, :description, :long_description, :usage, :options, :ancestor_name)
+ FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/
+
+ def initialize(name, description, long_description, usage, options = nil)
+ super(name.to_s, description, long_description, usage, options || {})
+ end
+
+ def initialize_copy(other) #:nodoc:
+ super(other)
+ self.options = other.options.dup if other.options
+ end
+
+ def hidden?
+ false
+ end
+
+ # By default, a command invokes a method in the thor class. You can change this
+ # implementation to create custom commands.
+ def run(instance, args = [])
+ arity = nil
+
+ if private_method?(instance)
+ instance.class.handle_no_command_error(name)
+ elsif public_method?(instance)
+ arity = instance.method(name).arity
+ instance.__send__(name, *args)
+ elsif local_method?(instance, :method_missing)
+ instance.__send__(:method_missing, name.to_sym, *args)
+ else
+ instance.class.handle_no_command_error(name)
+ end
+ rescue ArgumentError => e
+ handle_argument_error?(instance, e, caller) ? instance.class.handle_argument_error(self, e, args, arity) : (raise e)
+ rescue NoMethodError => e
+ handle_no_method_error?(instance, e, caller) ? instance.class.handle_no_command_error(name) : (raise e)
+ end
+
+ # Returns the formatted usage by injecting given required arguments
+ # and required options into the given usage.
+ def formatted_usage(klass, namespace = true, subcommand = false)
+ if ancestor_name
+ formatted = "#{ancestor_name} ".dup # add space
+ elsif namespace
+ namespace = klass.namespace
+ formatted = "#{namespace.gsub(/^(default)/, '')}:".dup
+ end
+ formatted ||= "#{klass.namespace.split(':').last} ".dup if subcommand
+
+ formatted ||= "".dup
+
+ # Add usage with required arguments
+ formatted << if klass && !klass.arguments.empty?
+ usage.to_s.gsub(/^#{name}/) do |match|
+ match << " " << klass.arguments.map(&:usage).compact.join(" ")
+ end
+ else
+ usage.to_s
+ end
+
+ # Add required options
+ formatted << " #{required_options}"
+
+ # Strip and go!
+ formatted.strip
+ end
+
+ protected
+
+ def not_debugging?(instance)
+ !(instance.class.respond_to?(:debugging) && instance.class.debugging)
+ end
+
+ def required_options
+ @required_options ||= options.map { |_, o| o.usage if o.required? }.compact.sort.join(" ")
+ end
+
+ # Given a target, checks if this class name is a public method.
+ def public_method?(instance) #:nodoc:
+ !(instance.public_methods & [name.to_s, name.to_sym]).empty?
+ end
+
+ def private_method?(instance)
+ !(instance.private_methods & [name.to_s, name.to_sym]).empty?
+ end
+
+ def local_method?(instance, name)
+ methods = instance.public_methods(false) + instance.private_methods(false) + instance.protected_methods(false)
+ !(methods & [name.to_s, name.to_sym]).empty?
+ end
+
+ def sans_backtrace(backtrace, caller) #:nodoc:
+ saned = backtrace.reject { |frame| frame =~ FILE_REGEXP || (frame =~ /\.java:/ && RUBY_PLATFORM =~ /java/) || (frame =~ %r{^kernel/} && RUBY_ENGINE =~ /rbx/) }
+ saned - caller
+ end
+
+ def handle_argument_error?(instance, error, caller)
+ not_debugging?(instance) && (error.message =~ /wrong number of arguments/ || error.message =~ /given \d*, expected \d*/) && begin
+ saned = sans_backtrace(error.backtrace, caller)
+ # Ruby 1.9 always include the called method in the backtrace
+ saned.empty? || (saned.size == 1 && RUBY_VERSION >= "1.9")
+ end
+ end
+
+ def handle_no_method_error?(instance, error, caller)
+ not_debugging?(instance) &&
+ error.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/
+ end
+ end
+ Task = Command
+
+ # A command that is hidden in help messages but still invocable.
+ class HiddenCommand < Command
+ def hidden?
+ true
+ end
+ end
+ HiddenTask = HiddenCommand
+
+ # A dynamic command that handles method missing scenarios.
+ class DynamicCommand < Command
+ def initialize(name, options = nil)
+ super(name.to_s, "A dynamically-generated command", name.to_s, name.to_s, options)
+ end
+
+ def run(instance, args = [])
+ if (instance.methods & [name.to_s, name.to_sym]).empty?
+ super
+ else
+ instance.class.handle_no_command_error(name)
+ end
+ end
+ end
+ DynamicTask = DynamicCommand
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb
new file mode 100644
index 0000000000..c167aa33b8
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb
@@ -0,0 +1,97 @@
+class Bundler::Thor
+ module CoreExt #:nodoc:
+ # A hash with indifferent access and magic predicates.
+ #
+ # hash = Bundler::Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true
+ #
+ # hash[:foo] #=> 'bar'
+ # hash['foo'] #=> 'bar'
+ # hash.foo? #=> true
+ #
+ class HashWithIndifferentAccess < ::Hash #:nodoc:
+ def initialize(hash = {})
+ super()
+ hash.each do |key, value|
+ self[convert_key(key)] = value
+ end
+ end
+
+ def [](key)
+ super(convert_key(key))
+ end
+
+ def []=(key, value)
+ super(convert_key(key), value)
+ end
+
+ def delete(key)
+ super(convert_key(key))
+ end
+
+ def fetch(key, *args)
+ super(convert_key(key), *args)
+ end
+
+ def key?(key)
+ super(convert_key(key))
+ end
+
+ def values_at(*indices)
+ indices.map { |key| self[convert_key(key)] }
+ end
+
+ def merge(other)
+ dup.merge!(other)
+ end
+
+ def merge!(other)
+ other.each do |key, value|
+ self[convert_key(key)] = value
+ end
+ self
+ end
+
+ def reverse_merge(other)
+ self.class.new(other).merge(self)
+ end
+
+ def reverse_merge!(other_hash)
+ replace(reverse_merge(other_hash))
+ end
+
+ def replace(other_hash)
+ super(other_hash)
+ end
+
+ # Convert to a Hash with String keys.
+ def to_hash
+ Hash.new(default).merge!(self)
+ end
+
+ protected
+
+ def convert_key(key)
+ key.is_a?(Symbol) ? key.to_s : key
+ end
+
+ # Magic predicates. For instance:
+ #
+ # options.force? # => !!options['force']
+ # options.shebang # => "/usr/lib/local/ruby"
+ # options.test_framework?(:rspec) # => options[:test_framework] == :rspec
+ #
+ def method_missing(method, *args)
+ method = method.to_s
+ if method =~ /^(\w+)\?$/
+ if args.empty?
+ !!self[$1]
+ else
+ self[$1] == args.first
+ end
+ else
+ self[method]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb
new file mode 100644
index 0000000000..0f6e2e0af2
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb
@@ -0,0 +1,12 @@
+class IO #:nodoc:
+ class << self
+ unless method_defined? :binread
+ def binread(file, *args)
+ raise ArgumentError, "wrong number of arguments (#{1 + args.size} for 1..3)" unless args.size < 3
+ File.open(file, "rb") do |f|
+ f.read(*args)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb
new file mode 100644
index 0000000000..76f1e43c65
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb
@@ -0,0 +1,129 @@
+class Bundler::Thor
+ module CoreExt
+ class OrderedHash < ::Hash
+ if RUBY_VERSION < "1.9"
+ def initialize(*args, &block)
+ super
+ @keys = []
+ end
+
+ def initialize_copy(other)
+ super
+ # make a deep copy of keys
+ @keys = other.keys
+ end
+
+ def []=(key, value)
+ @keys << key unless key?(key)
+ super
+ end
+
+ def delete(key)
+ if key? key
+ index = @keys.index(key)
+ @keys.delete_at index
+ end
+ super
+ end
+
+ def delete_if
+ super
+ sync_keys!
+ self
+ end
+
+ alias_method :reject!, :delete_if
+
+ def reject(&block)
+ dup.reject!(&block)
+ end
+
+ def keys
+ @keys.dup
+ end
+
+ def values
+ @keys.map { |key| self[key] }
+ end
+
+ def to_hash
+ self
+ end
+
+ def to_a
+ @keys.map { |key| [key, self[key]] }
+ end
+
+ def each_key
+ return to_enum(:each_key) unless block_given?
+ @keys.each { |key| yield(key) }
+ self
+ end
+
+ def each_value
+ return to_enum(:each_value) unless block_given?
+ @keys.each { |key| yield(self[key]) }
+ self
+ end
+
+ def each
+ return to_enum(:each) unless block_given?
+ @keys.each { |key| yield([key, self[key]]) }
+ self
+ end
+
+ def each_pair
+ return to_enum(:each_pair) unless block_given?
+ @keys.each { |key| yield(key, self[key]) }
+ self
+ end
+
+ alias_method :select, :find_all
+
+ def clear
+ super
+ @keys.clear
+ self
+ end
+
+ def shift
+ k = @keys.first
+ v = delete(k)
+ [k, v]
+ end
+
+ def merge!(other_hash)
+ if block_given?
+ other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v }
+ else
+ other_hash.each { |k, v| self[k] = v }
+ end
+ self
+ end
+
+ alias_method :update, :merge!
+
+ def merge(other_hash, &block)
+ dup.merge!(other_hash, &block)
+ end
+
+ # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
+ def replace(other)
+ super
+ @keys = other.keys
+ self
+ end
+
+ def inspect
+ "#<#{self.class} #{super}>"
+ end
+
+ private
+
+ def sync_keys!
+ @keys.delete_if { |k| !key?(k) }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/error.rb b/lib/bundler/vendor/thor/lib/thor/error.rb
new file mode 100644
index 0000000000..2f816081f3
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/error.rb
@@ -0,0 +1,32 @@
+class Bundler::Thor
+ # Bundler::Thor::Error is raised when it's caused by wrong usage of thor classes. Those
+ # errors have their backtrace suppressed and are nicely shown to the user.
+ #
+ # Errors that are caused by the developer, like declaring a method which
+ # overwrites a thor keyword, SHOULD NOT raise a Bundler::Thor::Error. This way, we
+ # ensure that developer errors are shown with full backtrace.
+ class Error < StandardError
+ end
+
+ # Raised when a command was not found.
+ class UndefinedCommandError < Error
+ end
+ UndefinedTaskError = UndefinedCommandError
+
+ class AmbiguousCommandError < Error
+ end
+ AmbiguousTaskError = AmbiguousCommandError
+
+ # Raised when a command was found, but not invoked properly.
+ class InvocationError < Error
+ end
+
+ class UnknownArgumentError < Error
+ end
+
+ class RequiredArgumentMissingError < InvocationError
+ end
+
+ class MalformattedArgumentError < InvocationError
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/group.rb b/lib/bundler/vendor/thor/lib/thor/group.rb
new file mode 100644
index 0000000000..05ddc10cd3
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/group.rb
@@ -0,0 +1,281 @@
+require "bundler/vendor/thor/lib/thor/base"
+
+# Bundler::Thor has a special class called Bundler::Thor::Group. The main difference to Bundler::Thor class
+# is that it invokes all commands at once. It also include some methods that allows
+# invocations to be done at the class method, which are not available to Bundler::Thor
+# commands.
+class Bundler::Thor::Group
+ class << self
+ # The description for this Bundler::Thor::Group. If none is provided, but a source root
+ # exists, tries to find the USAGE one folder above it, otherwise searches
+ # in the superclass.
+ #
+ # ==== Parameters
+ # description<String>:: The description for this Bundler::Thor::Group.
+ #
+ def desc(description = nil)
+ if description
+ @desc = description
+ else
+ @desc ||= from_superclass(:desc, nil)
+ end
+ end
+
+ # Prints help information.
+ #
+ # ==== Options
+ # short:: When true, shows only usage.
+ #
+ def help(shell)
+ shell.say "Usage:"
+ shell.say " #{banner}\n"
+ shell.say
+ class_options_help(shell)
+ shell.say desc if desc
+ end
+
+ # Stores invocations for this class merging with superclass values.
+ #
+ def invocations #:nodoc:
+ @invocations ||= from_superclass(:invocations, {})
+ end
+
+ # Stores invocation blocks used on invoke_from_option.
+ #
+ def invocation_blocks #:nodoc:
+ @invocation_blocks ||= from_superclass(:invocation_blocks, {})
+ end
+
+ # Invoke the given namespace or class given. It adds an instance
+ # method that will invoke the klass and command. You can give a block to
+ # configure how it will be invoked.
+ #
+ # The namespace/class given will have its options showed on the help
+ # usage. Check invoke_from_option for more information.
+ #
+ def invoke(*names, &block)
+ options = names.last.is_a?(Hash) ? names.pop : {}
+ verbose = options.fetch(:verbose, true)
+
+ names.each do |name|
+ invocations[name] = false
+ invocation_blocks[name] = block if block_given?
+
+ class_eval <<-METHOD, __FILE__, __LINE__
+ def _invoke_#{name.to_s.gsub(/\W/, '_')}
+ klass, command = self.class.prepare_for_invocation(nil, #{name.inspect})
+
+ if klass
+ say_status :invoke, #{name.inspect}, #{verbose.inspect}
+ block = self.class.invocation_blocks[#{name.inspect}]
+ _invoke_for_class_method klass, command, &block
+ else
+ say_status :error, %(#{name.inspect} [not found]), :red
+ end
+ end
+ METHOD
+ end
+ end
+
+ # Invoke a thor class based on the value supplied by the user to the
+ # given option named "name". A class option must be created before this
+ # method is invoked for each name given.
+ #
+ # ==== Examples
+ #
+ # class GemGenerator < Bundler::Thor::Group
+ # class_option :test_framework, :type => :string
+ # invoke_from_option :test_framework
+ # end
+ #
+ # ==== Boolean options
+ #
+ # In some cases, you want to invoke a thor class if some option is true or
+ # false. This is automatically handled by invoke_from_option. Then the
+ # option name is used to invoke the generator.
+ #
+ # ==== Preparing for invocation
+ #
+ # In some cases you want to customize how a specified hook is going to be
+ # invoked. You can do that by overwriting the class method
+ # prepare_for_invocation. The class method must necessarily return a klass
+ # and an optional command.
+ #
+ # ==== Custom invocations
+ #
+ # You can also supply a block to customize how the option is going to be
+ # invoked. The block receives two parameters, an instance of the current
+ # class and the klass to be invoked.
+ #
+ def invoke_from_option(*names, &block)
+ options = names.last.is_a?(Hash) ? names.pop : {}
+ verbose = options.fetch(:verbose, :white)
+
+ names.each do |name|
+ unless class_options.key?(name)
+ raise ArgumentError, "You have to define the option #{name.inspect} " \
+ "before setting invoke_from_option."
+ end
+
+ invocations[name] = true
+ invocation_blocks[name] = block if block_given?
+
+ class_eval <<-METHOD, __FILE__, __LINE__
+ def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')}
+ return unless options[#{name.inspect}]
+
+ value = options[#{name.inspect}]
+ value = #{name.inspect} if TrueClass === value
+ klass, command = self.class.prepare_for_invocation(#{name.inspect}, value)
+
+ if klass
+ say_status :invoke, value, #{verbose.inspect}
+ block = self.class.invocation_blocks[#{name.inspect}]
+ _invoke_for_class_method klass, command, &block
+ else
+ say_status :error, %(\#{value} [not found]), :red
+ end
+ end
+ METHOD
+ end
+ end
+
+ # Remove a previously added invocation.
+ #
+ # ==== Examples
+ #
+ # remove_invocation :test_framework
+ #
+ def remove_invocation(*names)
+ names.each do |name|
+ remove_command(name)
+ remove_class_option(name)
+ invocations.delete(name)
+ invocation_blocks.delete(name)
+ end
+ end
+
+ # Overwrite class options help to allow invoked generators options to be
+ # shown recursively when invoking a generator.
+ #
+ def class_options_help(shell, groups = {}) #:nodoc:
+ get_options_from_invocations(groups, class_options) do |klass|
+ klass.send(:get_options_from_invocations, groups, class_options)
+ end
+ super(shell, groups)
+ end
+
+ # Get invocations array and merge options from invocations. Those
+ # options are added to group_options hash. Options that already exists
+ # in base_options are not added twice.
+ #
+ def get_options_from_invocations(group_options, base_options) #:nodoc: # rubocop:disable MethodLength
+ invocations.each do |name, from_option|
+ value = if from_option
+ option = class_options[name]
+ option.type == :boolean ? name : option.default
+ else
+ name
+ end
+ next unless value
+
+ klass, _ = prepare_for_invocation(name, value)
+ next unless klass && klass.respond_to?(:class_options)
+
+ value = value.to_s
+ human_name = value.respond_to?(:classify) ? value.classify : value
+
+ group_options[human_name] ||= []
+ group_options[human_name] += klass.class_options.values.select do |class_option|
+ base_options[class_option.name.to_sym].nil? && class_option.group.nil? &&
+ !group_options.values.flatten.any? { |i| i.name == class_option.name }
+ end
+
+ yield klass if block_given?
+ end
+ end
+
+ # Returns commands ready to be printed.
+ def printable_commands(*)
+ item = []
+ item << banner
+ item << (desc ? "# #{desc.gsub(/\s+/m, ' ')}" : "")
+ [item]
+ end
+ alias_method :printable_tasks, :printable_commands
+
+ def handle_argument_error(command, error, _args, arity) #:nodoc:
+ msg = "#{basename} #{command.name} takes #{arity} argument".dup
+ msg << "s" if arity > 1
+ msg << ", but it should not."
+ raise error, msg
+ end
+
+ protected
+
+ # The method responsible for dispatching given the args.
+ def dispatch(command, given_args, given_opts, config) #:nodoc:
+ if Bundler::Thor::HELP_MAPPINGS.include?(given_args.first)
+ help(config[:shell])
+ return
+ end
+
+ args, opts = Bundler::Thor::Options.split(given_args)
+ opts = given_opts || opts
+
+ instance = new(args, opts, config)
+ yield instance if block_given?
+
+ if command
+ instance.invoke_command(all_commands[command])
+ else
+ instance.invoke_all
+ end
+ end
+
+ # The banner for this class. You can customize it if you are invoking the
+ # thor class by another ways which is not the Bundler::Thor::Runner.
+ def banner
+ "#{basename} #{self_command.formatted_usage(self, false)}"
+ end
+
+ # Represents the whole class as a command.
+ def self_command #:nodoc:
+ Bundler::Thor::DynamicCommand.new(namespace, class_options)
+ end
+ alias_method :self_task, :self_command
+
+ def baseclass #:nodoc:
+ Bundler::Thor::Group
+ end
+
+ def create_command(meth) #:nodoc:
+ commands[meth.to_s] = Bundler::Thor::Command.new(meth, nil, nil, nil, nil)
+ true
+ end
+ alias_method :create_task, :create_command
+ end
+
+ include Bundler::Thor::Base
+
+protected
+
+ # Shortcut to invoke with padding and block handling. Use internally by
+ # invoke and invoke_from_option class methods.
+ def _invoke_for_class_method(klass, command = nil, *args, &block) #:nodoc:
+ with_padding do
+ if block
+ case block.arity
+ when 3
+ yield(self, klass, command)
+ when 2
+ yield(self, klass)
+ when 1
+ instance_exec(klass, &block)
+ end
+ else
+ invoke klass, command, *args
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/invocation.rb b/lib/bundler/vendor/thor/lib/thor/invocation.rb
new file mode 100644
index 0000000000..866d2212a7
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/invocation.rb
@@ -0,0 +1,177 @@
+class Bundler::Thor
+ module Invocation
+ def self.included(base) #:nodoc:
+ base.extend ClassMethods
+ end
+
+ module ClassMethods
+ # This method is responsible for receiving a name and find the proper
+ # class and command for it. The key is an optional parameter which is
+ # available only in class methods invocations (i.e. in Bundler::Thor::Group).
+ def prepare_for_invocation(key, name) #:nodoc:
+ case name
+ when Symbol, String
+ Bundler::Thor::Util.find_class_and_command_by_namespace(name.to_s, !key)
+ else
+ name
+ end
+ end
+ end
+
+ # Make initializer aware of invocations and the initialization args.
+ def initialize(args = [], options = {}, config = {}, &block) #:nodoc:
+ @_invocations = config[:invocations] || Hash.new { |h, k| h[k] = [] }
+ @_initializer = [args, options, config]
+ super
+ end
+
+ # Make the current command chain accessible with in a Bundler::Thor-(sub)command
+ def current_command_chain
+ @_invocations.values.flatten.map(&:to_sym)
+ end
+
+ # Receives a name and invokes it. The name can be a string (either "command" or
+ # "namespace:command"), a Bundler::Thor::Command, a Class or a Bundler::Thor instance. If the
+ # command cannot be guessed by name, it can also be supplied as second argument.
+ #
+ # You can also supply the arguments, options and configuration values for
+ # the command to be invoked, if none is given, the same values used to
+ # initialize the invoker are used to initialize the invoked.
+ #
+ # When no name is given, it will invoke the default command of the current class.
+ #
+ # ==== Examples
+ #
+ # class A < Bundler::Thor
+ # def foo
+ # invoke :bar
+ # invoke "b:hello", ["Erik"]
+ # end
+ #
+ # def bar
+ # invoke "b:hello", ["Erik"]
+ # end
+ # end
+ #
+ # class B < Bundler::Thor
+ # def hello(name)
+ # puts "hello #{name}"
+ # end
+ # end
+ #
+ # You can notice that the method "foo" above invokes two commands: "bar",
+ # which belongs to the same class and "hello" which belongs to the class B.
+ #
+ # By using an invocation system you ensure that a command is invoked only once.
+ # In the example above, invoking "foo" will invoke "b:hello" just once, even
+ # if it's invoked later by "bar" method.
+ #
+ # When class A invokes class B, all arguments used on A initialization are
+ # supplied to B. This allows lazy parse of options. Let's suppose you have
+ # some rspec commands:
+ #
+ # class Rspec < Bundler::Thor::Group
+ # class_option :mock_framework, :type => :string, :default => :rr
+ #
+ # def invoke_mock_framework
+ # invoke "rspec:#{options[:mock_framework]}"
+ # end
+ # end
+ #
+ # As you noticed, it invokes the given mock framework, which might have its
+ # own options:
+ #
+ # class Rspec::RR < Bundler::Thor::Group
+ # class_option :style, :type => :string, :default => :mock
+ # end
+ #
+ # Since it's not rspec concern to parse mock framework options, when RR
+ # is invoked all options are parsed again, so RR can extract only the options
+ # that it's going to use.
+ #
+ # If you want Rspec::RR to be initialized with its own set of options, you
+ # have to do that explicitly:
+ #
+ # invoke "rspec:rr", [], :style => :foo
+ #
+ # Besides giving an instance, you can also give a class to invoke:
+ #
+ # invoke Rspec::RR, [], :style => :foo
+ #
+ def invoke(name = nil, *args)
+ if name.nil?
+ warn "[Bundler::Thor] Calling invoke() without argument is deprecated. Please use invoke_all instead.\n#{caller.join("\n")}"
+ return invoke_all
+ end
+
+ args.unshift(nil) if args.first.is_a?(Array) || args.first.nil?
+ command, args, opts, config = args
+
+ klass, command = _retrieve_class_and_command(name, command)
+ raise "Missing Bundler::Thor class for invoke #{name}" unless klass
+ raise "Expected Bundler::Thor class, got #{klass}" unless klass <= Bundler::Thor::Base
+
+ args, opts, config = _parse_initialization_options(args, opts, config)
+ klass.send(:dispatch, command, args, opts, config) do |instance|
+ instance.parent_options = options
+ end
+ end
+
+ # Invoke the given command if the given args.
+ def invoke_command(command, *args) #:nodoc:
+ current = @_invocations[self.class]
+
+ unless current.include?(command.name)
+ current << command.name
+ command.run(self, *args)
+ end
+ end
+ alias_method :invoke_task, :invoke_command
+
+ # Invoke all commands for the current instance.
+ def invoke_all #:nodoc:
+ self.class.all_commands.map { |_, command| invoke_command(command) }
+ end
+
+ # Invokes using shell padding.
+ def invoke_with_padding(*args)
+ with_padding { invoke(*args) }
+ end
+
+ protected
+
+ # Configuration values that are shared between invocations.
+ def _shared_configuration #:nodoc:
+ {:invocations => @_invocations}
+ end
+
+ # This method simply retrieves the class and command to be invoked.
+ # If the name is nil or the given name is a command in the current class,
+ # use the given name and return self as class. Otherwise, call
+ # prepare_for_invocation in the current class.
+ def _retrieve_class_and_command(name, sent_command = nil) #:nodoc:
+ if name.nil?
+ [self.class, nil]
+ elsif self.class.all_commands[name.to_s]
+ [self.class, name.to_s]
+ else
+ klass, command = self.class.prepare_for_invocation(nil, name)
+ [klass, command || sent_command]
+ end
+ end
+ alias_method :_retrieve_class_and_task, :_retrieve_class_and_command
+
+ # Initialize klass using values stored in the @_initializer.
+ def _parse_initialization_options(args, opts, config) #:nodoc:
+ stored_args, stored_opts, stored_config = @_initializer
+
+ args ||= stored_args.dup
+ opts ||= stored_opts.dup
+
+ config ||= {}
+ config = stored_config.merge(_shared_configuration).merge!(config)
+
+ [args, opts, config]
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/line_editor.rb b/lib/bundler/vendor/thor/lib/thor/line_editor.rb
new file mode 100644
index 0000000000..ce81a17484
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/line_editor.rb
@@ -0,0 +1,17 @@
+require "bundler/vendor/thor/lib/thor/line_editor/basic"
+require "bundler/vendor/thor/lib/thor/line_editor/readline"
+
+class Bundler::Thor
+ module LineEditor
+ def self.readline(prompt, options = {})
+ best_available.new(prompt, options).readline
+ end
+
+ def self.best_available
+ [
+ Bundler::Thor::LineEditor::Readline,
+ Bundler::Thor::LineEditor::Basic
+ ].detect(&:available?)
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb b/lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb
new file mode 100644
index 0000000000..0adb2b3137
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb
@@ -0,0 +1,37 @@
+class Bundler::Thor
+ module LineEditor
+ class Basic
+ attr_reader :prompt, :options
+
+ def self.available?
+ true
+ end
+
+ def initialize(prompt, options)
+ @prompt = prompt
+ @options = options
+ end
+
+ def readline
+ $stdout.print(prompt)
+ get_input
+ end
+
+ private
+
+ def get_input
+ if echo?
+ $stdin.gets
+ else
+ # Lazy-load io/console since it is gem-ified as of 2.3
+ require "io/console" if RUBY_VERSION > "1.9.2"
+ $stdin.noecho(&:gets)
+ end
+ end
+
+ def echo?
+ options.fetch(:echo, true)
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb b/lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb
new file mode 100644
index 0000000000..dd39cff35d
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb
@@ -0,0 +1,88 @@
+begin
+ require "readline"
+rescue LoadError
+end
+
+class Bundler::Thor
+ module LineEditor
+ class Readline < Basic
+ def self.available?
+ Object.const_defined?(:Readline)
+ end
+
+ def readline
+ if echo?
+ ::Readline.completion_append_character = nil
+ # Ruby 1.8.7 does not allow Readline.completion_proc= to receive nil.
+ if complete = completion_proc
+ ::Readline.completion_proc = complete
+ end
+ ::Readline.readline(prompt, add_to_history?)
+ else
+ super
+ end
+ end
+
+ private
+
+ def add_to_history?
+ options.fetch(:add_to_history, true)
+ end
+
+ def completion_proc
+ if use_path_completion?
+ proc { |text| PathCompletion.new(text).matches }
+ elsif completion_options.any?
+ proc do |text|
+ completion_options.select { |option| option.start_with?(text) }
+ end
+ end
+ end
+
+ def completion_options
+ options.fetch(:limited_to, [])
+ end
+
+ def use_path_completion?
+ options.fetch(:path, false)
+ end
+
+ class PathCompletion
+ attr_reader :text
+ private :text
+
+ def initialize(text)
+ @text = text
+ end
+
+ def matches
+ relative_matches
+ end
+
+ private
+
+ def relative_matches
+ absolute_matches.map { |path| path.sub(base_path, "") }
+ end
+
+ def absolute_matches
+ Dir[glob_pattern].map do |path|
+ if File.directory?(path)
+ "#{path}/"
+ else
+ path
+ end
+ end
+ end
+
+ def glob_pattern
+ "#{base_path}#{text}*"
+ end
+
+ def base_path
+ "#{Dir.pwd}/"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/parser.rb b/lib/bundler/vendor/thor/lib/thor/parser.rb
new file mode 100644
index 0000000000..08f80e565d
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/parser.rb
@@ -0,0 +1,4 @@
+require "bundler/vendor/thor/lib/thor/parser/argument"
+require "bundler/vendor/thor/lib/thor/parser/arguments"
+require "bundler/vendor/thor/lib/thor/parser/option"
+require "bundler/vendor/thor/lib/thor/parser/options"
diff --git a/lib/bundler/vendor/thor/lib/thor/parser/argument.rb b/lib/bundler/vendor/thor/lib/thor/parser/argument.rb
new file mode 100644
index 0000000000..dfe7398583
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/parser/argument.rb
@@ -0,0 +1,70 @@
+class Bundler::Thor
+ class Argument #:nodoc:
+ VALID_TYPES = [:numeric, :hash, :array, :string]
+
+ attr_reader :name, :description, :enum, :required, :type, :default, :banner
+ alias_method :human_name, :name
+
+ def initialize(name, options = {})
+ class_name = self.class.name.split("::").last
+
+ type = options[:type]
+
+ raise ArgumentError, "#{class_name} name can't be nil." if name.nil?
+ raise ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type)
+
+ @name = name.to_s
+ @description = options[:desc]
+ @required = options.key?(:required) ? options[:required] : true
+ @type = (type || :string).to_sym
+ @default = options[:default]
+ @banner = options[:banner] || default_banner
+ @enum = options[:enum]
+
+ validate! # Trigger specific validations
+ end
+
+ def usage
+ required? ? banner : "[#{banner}]"
+ end
+
+ def required?
+ required
+ end
+
+ def show_default?
+ case default
+ when Array, String, Hash
+ !default.empty?
+ else
+ default
+ end
+ end
+
+ protected
+
+ def validate!
+ raise ArgumentError, "An argument cannot be required and have default value." if required? && !default.nil?
+ raise ArgumentError, "An argument cannot have an enum other than an array." if @enum && !@enum.is_a?(Array)
+ end
+
+ def valid_type?(type)
+ self.class::VALID_TYPES.include?(type.to_sym)
+ end
+
+ def default_banner
+ case type
+ when :boolean
+ nil
+ when :string, :default
+ human_name.upcase
+ when :numeric
+ "N"
+ when :hash
+ "key:value"
+ when :array
+ "one two three"
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb b/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb
new file mode 100644
index 0000000000..1fd790f4b7
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb
@@ -0,0 +1,175 @@
+class Bundler::Thor
+ class Arguments #:nodoc: # rubocop:disable ClassLength
+ NUMERIC = /[-+]?(\d*\.\d+|\d+)/
+
+ # Receives an array of args and returns two arrays, one with arguments
+ # and one with switches.
+ #
+ def self.split(args)
+ arguments = []
+
+ args.each do |item|
+ break if item =~ /^-/
+ arguments << item
+ end
+
+ [arguments, args[Range.new(arguments.size, -1)]]
+ end
+
+ def self.parse(*args)
+ to_parse = args.pop
+ new(*args).parse(to_parse)
+ end
+
+ # Takes an array of Bundler::Thor::Argument objects.
+ #
+ def initialize(arguments = [])
+ @assigns = {}
+ @non_assigned_required = []
+ @switches = arguments
+
+ arguments.each do |argument|
+ if !argument.default.nil?
+ @assigns[argument.human_name] = argument.default
+ elsif argument.required?
+ @non_assigned_required << argument
+ end
+ end
+ end
+
+ def parse(args)
+ @pile = args.dup
+
+ @switches.each do |argument|
+ break unless peek
+ @non_assigned_required.delete(argument)
+ @assigns[argument.human_name] = send(:"parse_#{argument.type}", argument.human_name)
+ end
+
+ check_requirement!
+ @assigns
+ end
+
+ def remaining
+ @pile
+ end
+
+ private
+
+ def no_or_skip?(arg)
+ arg =~ /^--(no|skip)-([-\w]+)$/
+ $2
+ end
+
+ def last?
+ @pile.empty?
+ end
+
+ def peek
+ @pile.first
+ end
+
+ def shift
+ @pile.shift
+ end
+
+ def unshift(arg)
+ if arg.is_a?(Array)
+ @pile = arg + @pile
+ else
+ @pile.unshift(arg)
+ end
+ end
+
+ def current_is_value?
+ peek && peek.to_s !~ /^-/
+ end
+
+ # Runs through the argument array getting strings that contains ":" and
+ # mark it as a hash:
+ #
+ # [ "name:string", "age:integer" ]
+ #
+ # Becomes:
+ #
+ # { "name" => "string", "age" => "integer" }
+ #
+ def parse_hash(name)
+ return shift if peek.is_a?(Hash)
+ hash = {}
+
+ while current_is_value? && peek.include?(":")
+ key, value = shift.split(":", 2)
+ raise MalformattedArgumentError, "You can't specify '#{key}' more than once in option '#{name}'; got #{key}:#{hash[key]} and #{key}:#{value}" if hash.include? key
+ hash[key] = value
+ end
+ hash
+ end
+
+ # Runs through the argument array getting all strings until no string is
+ # found or a switch is found.
+ #
+ # ["a", "b", "c"]
+ #
+ # And returns it as an array:
+ #
+ # ["a", "b", "c"]
+ #
+ def parse_array(name)
+ return shift if peek.is_a?(Array)
+ array = []
+ array << shift while current_is_value?
+ array
+ end
+
+ # Check if the peek is numeric format and return a Float or Integer.
+ # Check if the peek is included in enum if enum is provided.
+ # Otherwise raises an error.
+ #
+ def parse_numeric(name)
+ return shift if peek.is_a?(Numeric)
+
+ unless peek =~ NUMERIC && $& == peek
+ raise MalformattedArgumentError, "Expected numeric value for '#{name}'; got #{peek.inspect}"
+ end
+
+ value = $&.index(".") ? shift.to_f : shift.to_i
+ if @switches.is_a?(Hash) && switch = @switches[name]
+ if switch.enum && !switch.enum.include?(value)
+ raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
+ end
+ end
+ value
+ end
+
+ # Parse string:
+ # for --string-arg, just return the current value in the pile
+ # for --no-string-arg, nil
+ # Check if the peek is included in enum if enum is provided. Otherwise raises an error.
+ #
+ def parse_string(name)
+ if no_or_skip?(name)
+ nil
+ else
+ value = shift
+ if @switches.is_a?(Hash) && switch = @switches[name]
+ if switch.enum && !switch.enum.include?(value)
+ raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
+ end
+ end
+ value
+ end
+ end
+
+ # Raises an error if @non_assigned_required array is not empty.
+ #
+ def check_requirement!
+ return if @non_assigned_required.empty?
+ names = @non_assigned_required.map do |o|
+ o.respond_to?(:switch_name) ? o.switch_name : o.human_name
+ end.join("', '")
+ class_name = self.class.name.split("::").last.downcase
+ raise RequiredArgumentMissingError, "No value provided for required #{class_name} '#{names}'"
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/parser/option.rb b/lib/bundler/vendor/thor/lib/thor/parser/option.rb
new file mode 100644
index 0000000000..85169b56c8
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/parser/option.rb
@@ -0,0 +1,146 @@
+class Bundler::Thor
+ class Option < Argument #:nodoc:
+ attr_reader :aliases, :group, :lazy_default, :hide
+
+ VALID_TYPES = [:boolean, :numeric, :hash, :array, :string]
+
+ def initialize(name, options = {})
+ @check_default_type = options[:check_default_type]
+ options[:required] = false unless options.key?(:required)
+ super
+ @lazy_default = options[:lazy_default]
+ @group = options[:group].to_s.capitalize if options[:group]
+ @aliases = Array(options[:aliases])
+ @hide = options[:hide]
+ end
+
+ # This parse quick options given as method_options. It makes several
+ # assumptions, but you can be more specific using the option method.
+ #
+ # parse :foo => "bar"
+ # #=> Option foo with default value bar
+ #
+ # parse [:foo, :baz] => "bar"
+ # #=> Option foo with default value bar and alias :baz
+ #
+ # parse :foo => :required
+ # #=> Required option foo without default value
+ #
+ # parse :foo => 2
+ # #=> Option foo with default value 2 and type numeric
+ #
+ # parse :foo => :numeric
+ # #=> Option foo without default value and type numeric
+ #
+ # parse :foo => true
+ # #=> Option foo with default value true and type boolean
+ #
+ # The valid types are :boolean, :numeric, :hash, :array and :string. If none
+ # is given a default type is assumed. This default type accepts arguments as
+ # string (--foo=value) or booleans (just --foo).
+ #
+ # By default all options are optional, unless :required is given.
+ #
+ def self.parse(key, value)
+ if key.is_a?(Array)
+ name, *aliases = key
+ else
+ name = key
+ aliases = []
+ end
+
+ name = name.to_s
+ default = value
+
+ type = case value
+ when Symbol
+ default = nil
+ if VALID_TYPES.include?(value)
+ value
+ elsif required = (value == :required) # rubocop:disable AssignmentInCondition
+ :string
+ end
+ when TrueClass, FalseClass
+ :boolean
+ when Numeric
+ :numeric
+ when Hash, Array, String
+ value.class.name.downcase.to_sym
+ end
+
+ new(name.to_s, :required => required, :type => type, :default => default, :aliases => aliases)
+ end
+
+ def switch_name
+ @switch_name ||= dasherized? ? name : dasherize(name)
+ end
+
+ def human_name
+ @human_name ||= dasherized? ? undasherize(name) : name
+ end
+
+ def usage(padding = 0)
+ sample = if banner && !banner.to_s.empty?
+ "#{switch_name}=#{banner}".dup
+ else
+ switch_name
+ end
+
+ sample = "[#{sample}]".dup unless required?
+
+ if boolean?
+ sample << ", [#{dasherize('no-' + human_name)}]" unless (name == "force") || name.start_with?("no-")
+ end
+
+ if aliases.empty?
+ (" " * padding) << sample
+ else
+ "#{aliases.join(', ')}, #{sample}"
+ end
+ end
+
+ VALID_TYPES.each do |type|
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{type}?
+ self.type == #{type.inspect}
+ end
+ RUBY
+ end
+
+ protected
+
+ def validate!
+ raise ArgumentError, "An option cannot be boolean and required." if boolean? && required?
+ validate_default_type! if @check_default_type
+ end
+
+ def validate_default_type!
+ default_type = case @default
+ when nil
+ return
+ when TrueClass, FalseClass
+ required? ? :string : :boolean
+ when Numeric
+ :numeric
+ when Symbol
+ :string
+ when Hash, Array, String
+ @default.class.name.downcase.to_sym
+ end
+
+ raise ArgumentError, "Expected #{@type} default value for '#{switch_name}'; got #{@default.inspect} (#{default_type})" unless default_type == @type
+ end
+
+ def dasherized?
+ name.index("-") == 0
+ end
+
+ def undasherize(str)
+ str.sub(/^-{1,2}/, "")
+ end
+
+ def dasherize(str)
+ (str.length > 1 ? "--" : "-") + str.tr("_", "-")
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/parser/options.rb b/lib/bundler/vendor/thor/lib/thor/parser/options.rb
new file mode 100644
index 0000000000..70f6366842
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/parser/options.rb
@@ -0,0 +1,221 @@
+class Bundler::Thor
+ class Options < Arguments #:nodoc: # rubocop:disable ClassLength
+ LONG_RE = /^(--\w+(?:-\w+)*)$/
+ SHORT_RE = /^(-[a-z])$/i
+ EQ_RE = /^(--\w+(?:-\w+)*|-[a-z])=(.*)$/i
+ SHORT_SQ_RE = /^-([a-z]{2,})$/i # Allow either -x -v or -xv style for single char args
+ SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i
+ OPTS_END = "--".freeze
+
+ # Receives a hash and makes it switches.
+ def self.to_switches(options)
+ options.map do |key, value|
+ case value
+ when true
+ "--#{key}"
+ when Array
+ "--#{key} #{value.map(&:inspect).join(' ')}"
+ when Hash
+ "--#{key} #{value.map { |k, v| "#{k}:#{v}" }.join(' ')}"
+ when nil, false
+ nil
+ else
+ "--#{key} #{value.inspect}"
+ end
+ end.compact.join(" ")
+ end
+
+ # Takes a hash of Bundler::Thor::Option and a hash with defaults.
+ #
+ # If +stop_on_unknown+ is true, #parse will stop as soon as it encounters
+ # an unknown option or a regular argument.
+ def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false, disable_required_check = false)
+ @stop_on_unknown = stop_on_unknown
+ @disable_required_check = disable_required_check
+ options = hash_options.values
+ super(options)
+
+ # Add defaults
+ defaults.each do |key, value|
+ @assigns[key.to_s] = value
+ @non_assigned_required.delete(hash_options[key])
+ end
+
+ @shorts = {}
+ @switches = {}
+ @extra = []
+
+ options.each do |option|
+ @switches[option.switch_name] = option
+
+ option.aliases.each do |short|
+ name = short.to_s.sub(/^(?!\-)/, "-")
+ @shorts[name] ||= option.switch_name
+ end
+ end
+ end
+
+ def remaining
+ @extra
+ end
+
+ def peek
+ return super unless @parsing_options
+
+ result = super
+ if result == OPTS_END
+ shift
+ @parsing_options = false
+ super
+ else
+ result
+ end
+ end
+
+ def parse(args) # rubocop:disable MethodLength
+ @pile = args.dup
+ @parsing_options = true
+
+ while peek
+ if parsing_options?
+ match, is_switch = current_is_switch?
+ shifted = shift
+
+ if is_switch
+ case shifted
+ when SHORT_SQ_RE
+ unshift($1.split("").map { |f| "-#{f}" })
+ next
+ when EQ_RE, SHORT_NUM
+ unshift($2)
+ switch = $1
+ when LONG_RE, SHORT_RE
+ switch = $1
+ end
+
+ switch = normalize_switch(switch)
+ option = switch_option(switch)
+ @assigns[option.human_name] = parse_peek(switch, option)
+ elsif @stop_on_unknown
+ @parsing_options = false
+ @extra << shifted
+ @extra << shift while peek
+ break
+ elsif match
+ @extra << shifted
+ @extra << shift while peek && peek !~ /^-/
+ else
+ @extra << shifted
+ end
+ else
+ @extra << shift
+ end
+ end
+
+ check_requirement! unless @disable_required_check
+
+ assigns = Bundler::Thor::CoreExt::HashWithIndifferentAccess.new(@assigns)
+ assigns.freeze
+ assigns
+ end
+
+ def check_unknown!
+ # an unknown option starts with - or -- and has no more --'s afterward.
+ unknown = @extra.select { |str| str =~ /^--?(?:(?!--).)*$/ }
+ raise UnknownArgumentError, "Unknown switches '#{unknown.join(', ')}'" unless unknown.empty?
+ end
+
+ protected
+
+ # Check if the current value in peek is a registered switch.
+ #
+ # Two booleans are returned. The first is true if the current value
+ # starts with a hyphen; the second is true if it is a registered switch.
+ def current_is_switch?
+ case peek
+ when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM
+ [true, switch?($1)]
+ when SHORT_SQ_RE
+ [true, $1.split("").any? { |f| switch?("-#{f}") }]
+ else
+ [false, false]
+ end
+ end
+
+ def current_is_switch_formatted?
+ case peek
+ when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM, SHORT_SQ_RE
+ true
+ else
+ false
+ end
+ end
+
+ def current_is_value?
+ peek && (!parsing_options? || super)
+ end
+
+ def switch?(arg)
+ switch_option(normalize_switch(arg))
+ end
+
+ def switch_option(arg)
+ if match = no_or_skip?(arg) # rubocop:disable AssignmentInCondition
+ @switches[arg] || @switches["--#{match}"]
+ else
+ @switches[arg]
+ end
+ end
+
+ # Check if the given argument is actually a shortcut.
+ #
+ def normalize_switch(arg)
+ (@shorts[arg] || arg).tr("_", "-")
+ end
+
+ def parsing_options?
+ peek
+ @parsing_options
+ end
+
+ # Parse boolean values which can be given as --foo=true, --foo or --no-foo.
+ #
+ def parse_boolean(switch)
+ if current_is_value?
+ if ["true", "TRUE", "t", "T", true].include?(peek)
+ shift
+ true
+ elsif ["false", "FALSE", "f", "F", false].include?(peek)
+ shift
+ false
+ else
+ !no_or_skip?(switch)
+ end
+ else
+ @switches.key?(switch) || !no_or_skip?(switch)
+ end
+ end
+
+ # Parse the value at the peek analyzing if it requires an input or not.
+ #
+ def parse_peek(switch, option)
+ if parsing_options? && (current_is_switch_formatted? || last?)
+ if option.boolean?
+ # No problem for boolean types
+ elsif no_or_skip?(switch)
+ return nil # User set value to nil
+ elsif option.string? && !option.required?
+ # Return the default if there is one, else the human name
+ return option.lazy_default || option.default || option.human_name
+ elsif option.lazy_default
+ return option.lazy_default
+ else
+ raise MalformattedArgumentError, "No value provided for option '#{switch}'"
+ end
+ end
+
+ @non_assigned_required.delete(option)
+ send(:"parse_#{option.type}", switch)
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/rake_compat.rb b/lib/bundler/vendor/thor/lib/thor/rake_compat.rb
new file mode 100644
index 0000000000..60282e2991
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/rake_compat.rb
@@ -0,0 +1,71 @@
+require "rake"
+require "rake/dsl_definition"
+
+class Bundler::Thor
+ # Adds a compatibility layer to your Bundler::Thor classes which allows you to use
+ # rake package tasks. For example, to use rspec rake tasks, one can do:
+ #
+ # require 'bundler/vendor/thor/lib/thor/rake_compat'
+ # require 'rspec/core/rake_task'
+ #
+ # class Default < Bundler::Thor
+ # include Bundler::Thor::RakeCompat
+ #
+ # RSpec::Core::RakeTask.new(:spec) do |t|
+ # t.spec_opts = ['--options', './.rspec']
+ # t.spec_files = FileList['spec/**/*_spec.rb']
+ # end
+ # end
+ #
+ module RakeCompat
+ include Rake::DSL if defined?(Rake::DSL)
+
+ def self.rake_classes
+ @rake_classes ||= []
+ end
+
+ def self.included(base)
+ # Hack. Make rakefile point to invoker, so rdoc task is generated properly.
+ rakefile = File.basename(caller[0].match(/(.*):\d+/)[1])
+ Rake.application.instance_variable_set(:@rakefile, rakefile)
+ rake_classes << base
+ end
+ end
+end
+
+# override task on (main), for compatibility with Rake 0.9
+instance_eval do
+ alias rake_namespace namespace
+
+ def task(*)
+ task = super
+
+ if klass = Bundler::Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition
+ non_namespaced_name = task.name.split(":").last
+
+ description = non_namespaced_name
+ description << task.arg_names.map { |n| n.to_s.upcase }.join(" ")
+ description.strip!
+
+ klass.desc description, Rake.application.last_description || non_namespaced_name
+ Rake.application.last_description = nil
+ klass.send :define_method, non_namespaced_name do |*args|
+ Rake::Task[task.name.to_sym].invoke(*args)
+ end
+ end
+
+ task
+ end
+
+ def namespace(name)
+ if klass = Bundler::Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition
+ const_name = Bundler::Thor::Util.camel_case(name.to_s).to_sym
+ klass.const_set(const_name, Class.new(Bundler::Thor))
+ new_klass = klass.const_get(const_name)
+ Bundler::Thor::RakeCompat.rake_classes << new_klass
+ end
+
+ super
+ Bundler::Thor::RakeCompat.rake_classes.pop
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/runner.rb b/lib/bundler/vendor/thor/lib/thor/runner.rb
new file mode 100644
index 0000000000..b110b8d478
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/runner.rb
@@ -0,0 +1,324 @@
+require "bundler/vendor/thor/lib/thor"
+require "bundler/vendor/thor/lib/thor/group"
+require "bundler/vendor/thor/lib/thor/core_ext/io_binary_read"
+
+require "yaml"
+require "digest"
+require "pathname"
+
+class Bundler::Thor::Runner < Bundler::Thor #:nodoc: # rubocop:disable ClassLength
+ map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version
+
+ def self.banner(command, all = false, subcommand = false)
+ "thor " + command.formatted_usage(self, all, subcommand)
+ end
+
+ def self.exit_on_failure?
+ true
+ end
+
+ # Override Bundler::Thor#help so it can give information about any class and any method.
+ #
+ def help(meth = nil)
+ if meth && !respond_to?(meth)
+ initialize_thorfiles(meth)
+ klass, command = Bundler::Thor::Util.find_class_and_command_by_namespace(meth)
+ self.class.handle_no_command_error(command, false) if klass.nil?
+ klass.start(["-h", command].compact, :shell => shell)
+ else
+ super
+ end
+ end
+
+ # If a command is not found on Bundler::Thor::Runner, method missing is invoked and
+ # Bundler::Thor::Runner is then responsible for finding the command in all classes.
+ #
+ def method_missing(meth, *args)
+ meth = meth.to_s
+ initialize_thorfiles(meth)
+ klass, command = Bundler::Thor::Util.find_class_and_command_by_namespace(meth)
+ self.class.handle_no_command_error(command, false) if klass.nil?
+ args.unshift(command) if command
+ klass.start(args, :shell => shell)
+ end
+
+ desc "install NAME", "Install an optionally named Bundler::Thor file into your system commands"
+ method_options :as => :string, :relative => :boolean, :force => :boolean
+ def install(name) # rubocop:disable MethodLength
+ initialize_thorfiles
+
+ # If a directory name is provided as the argument, look for a 'main.thor'
+ # command in said directory.
+ begin
+ if File.directory?(File.expand_path(name))
+ base = File.join(name, "main.thor")
+ package = :directory
+ contents = open(base, &:read)
+ else
+ base = name
+ package = :file
+ contents = open(name, &:read)
+ end
+ rescue OpenURI::HTTPError
+ raise Error, "Error opening URI '#{name}'"
+ rescue Errno::ENOENT
+ raise Error, "Error opening file '#{name}'"
+ end
+
+ say "Your Bundler::Thorfile contains:"
+ say contents
+
+ unless options["force"]
+ return false if no?("Do you wish to continue [y/N]?")
+ end
+
+ as = options["as"] || begin
+ first_line = contents.split("\n")[0]
+ (match = first_line.match(/\s*#\s*module:\s*([^\n]*)/)) ? match[1].strip : nil
+ end
+
+ unless as
+ basename = File.basename(name)
+ as = ask("Please specify a name for #{name} in the system repository [#{basename}]:")
+ as = basename if as.empty?
+ end
+
+ location = if options[:relative] || name =~ %r{^https?://}
+ name
+ else
+ File.expand_path(name)
+ end
+
+ thor_yaml[as] = {
+ :filename => Digest(:MD5).hexdigest(name + as),
+ :location => location,
+ :namespaces => Bundler::Thor::Util.namespaces_in_content(contents, base)
+ }
+
+ save_yaml(thor_yaml)
+ say "Storing thor file in your system repository"
+ destination = File.join(thor_root, thor_yaml[as][:filename])
+
+ if package == :file
+ File.open(destination, "w") { |f| f.puts contents }
+ else
+ require "fileutils"
+ FileUtils.cp_r(name, destination)
+ end
+
+ thor_yaml[as][:filename] # Indicate success
+ end
+
+ desc "version", "Show Bundler::Thor version"
+ def version
+ require "bundler/vendor/thor/lib/thor/version"
+ say "Bundler::Thor #{Bundler::Thor::VERSION}"
+ end
+
+ desc "uninstall NAME", "Uninstall a named Bundler::Thor module"
+ def uninstall(name)
+ raise Error, "Can't find module '#{name}'" unless thor_yaml[name]
+ say "Uninstalling #{name}."
+ require "fileutils"
+ FileUtils.rm_rf(File.join(thor_root, (thor_yaml[name][:filename]).to_s))
+
+ thor_yaml.delete(name)
+ save_yaml(thor_yaml)
+
+ puts "Done."
+ end
+
+ desc "update NAME", "Update a Bundler::Thor file from its original location"
+ def update(name)
+ raise Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location]
+
+ say "Updating '#{name}' from #{thor_yaml[name][:location]}"
+
+ old_filename = thor_yaml[name][:filename]
+ self.options = options.merge("as" => name)
+
+ if File.directory? File.expand_path(name)
+ require "fileutils"
+ FileUtils.rm_rf(File.join(thor_root, old_filename))
+
+ thor_yaml.delete(old_filename)
+ save_yaml(thor_yaml)
+
+ filename = install(name)
+ else
+ filename = install(thor_yaml[name][:location])
+ end
+
+ File.delete(File.join(thor_root, old_filename)) unless filename == old_filename
+ end
+
+ desc "installed", "List the installed Bundler::Thor modules and commands"
+ method_options :internal => :boolean
+ def installed
+ initialize_thorfiles(nil, true)
+ display_klasses(true, options["internal"])
+ end
+
+ desc "list [SEARCH]", "List the available thor commands (--substring means .*SEARCH)"
+ method_options :substring => :boolean, :group => :string, :all => :boolean, :debug => :boolean
+ def list(search = "")
+ initialize_thorfiles
+
+ search = ".*#{search}" if options["substring"]
+ search = /^#{search}.*/i
+ group = options[:group] || "standard"
+
+ klasses = Bundler::Thor::Base.subclasses.select do |k|
+ (options[:all] || k.group == group) && k.namespace =~ search
+ end
+
+ display_klasses(false, false, klasses)
+ end
+
+private
+
+ def thor_root
+ Bundler::Thor::Util.thor_root
+ end
+
+ def thor_yaml
+ @thor_yaml ||= begin
+ yaml_file = File.join(thor_root, "thor.yml")
+ yaml = YAML.load_file(yaml_file) if File.exist?(yaml_file)
+ yaml || {}
+ end
+ end
+
+ # Save the yaml file. If none exists in thor root, creates one.
+ #
+ def save_yaml(yaml)
+ yaml_file = File.join(thor_root, "thor.yml")
+
+ unless File.exist?(yaml_file)
+ require "fileutils"
+ FileUtils.mkdir_p(thor_root)
+ yaml_file = File.join(thor_root, "thor.yml")
+ FileUtils.touch(yaml_file)
+ end
+
+ File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml }
+ end
+
+ # Load the Bundler::Thorfiles. If relevant_to is supplied, looks for specific files
+ # in the thor_root instead of loading them all.
+ #
+ # By default, it also traverses the current path until find Bundler::Thor files, as
+ # described in thorfiles. This look up can be skipped by supplying
+ # skip_lookup true.
+ #
+ def initialize_thorfiles(relevant_to = nil, skip_lookup = false)
+ thorfiles(relevant_to, skip_lookup).each do |f|
+ Bundler::Thor::Util.load_thorfile(f, nil, options[:debug]) unless Bundler::Thor::Base.subclass_files.keys.include?(File.expand_path(f))
+ end
+ end
+
+ # Finds Bundler::Thorfiles by traversing from your current directory down to the root
+ # directory of your system. If at any time we find a Bundler::Thor file, we stop.
+ #
+ # We also ensure that system-wide Bundler::Thorfiles are loaded first, so local
+ # Bundler::Thorfiles can override them.
+ #
+ # ==== Example
+ #
+ # If we start at /Users/wycats/dev/thor ...
+ #
+ # 1. /Users/wycats/dev/thor
+ # 2. /Users/wycats/dev
+ # 3. /Users/wycats <-- we find a Bundler::Thorfile here, so we stop
+ #
+ # Suppose we start at c:\Documents and Settings\james\dev\thor ...
+ #
+ # 1. c:\Documents and Settings\james\dev\thor
+ # 2. c:\Documents and Settings\james\dev
+ # 3. c:\Documents and Settings\james
+ # 4. c:\Documents and Settings
+ # 5. c:\ <-- no Bundler::Thorfiles found!
+ #
+ def thorfiles(relevant_to = nil, skip_lookup = false)
+ thorfiles = []
+
+ unless skip_lookup
+ Pathname.pwd.ascend do |path|
+ thorfiles = Bundler::Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten
+ break unless thorfiles.empty?
+ end
+ end
+
+ files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Bundler::Thor::Util.thor_root_glob)
+ files += thorfiles
+ files -= ["#{thor_root}/thor.yml"]
+
+ files.map! do |file|
+ File.directory?(file) ? File.join(file, "main.thor") : file
+ end
+ end
+
+ # Load Bundler::Thorfiles relevant to the given method. If you provide "foo:bar" it
+ # will load all thor files in the thor.yaml that has "foo" e "foo:bar"
+ # namespaces registered.
+ #
+ def thorfiles_relevant_to(meth)
+ lookup = [meth, meth.split(":")[0...-1].join(":")]
+
+ files = thor_yaml.select do |_, v|
+ v[:namespaces] && !(v[:namespaces] & lookup).empty?
+ end
+
+ files.map { |_, v| File.join(thor_root, (v[:filename]).to_s) }
+ end
+
+ # Display information about the given klasses. If with_module is given,
+ # it shows a table with information extracted from the yaml file.
+ #
+ def display_klasses(with_modules = false, show_internal = false, klasses = Bundler::Thor::Base.subclasses)
+ klasses -= [Bundler::Thor, Bundler::Thor::Runner, Bundler::Thor::Group] unless show_internal
+
+ raise Error, "No Bundler::Thor commands available" if klasses.empty?
+ show_modules if with_modules && !thor_yaml.empty?
+
+ list = Hash.new { |h, k| h[k] = [] }
+ groups = klasses.select { |k| k.ancestors.include?(Bundler::Thor::Group) }
+
+ # Get classes which inherit from Bundler::Thor
+ (klasses - groups).each { |k| list[k.namespace.split(":").first] += k.printable_commands(false) }
+
+ # Get classes which inherit from Bundler::Thor::Base
+ groups.map! { |k| k.printable_commands(false).first }
+ list["root"] = groups
+
+ # Order namespaces with default coming first
+ list = list.sort { |a, b| a[0].sub(/^default/, "") <=> b[0].sub(/^default/, "") }
+ list.each { |n, commands| display_commands(n, commands) unless commands.empty? }
+ end
+
+ def display_commands(namespace, list) #:nodoc:
+ list.sort! { |a, b| a[0] <=> b[0] }
+
+ say shell.set_color(namespace, :blue, true)
+ say "-" * namespace.size
+
+ print_table(list, :truncate => true)
+ say
+ end
+ alias_method :display_tasks, :display_commands
+
+ def show_modules #:nodoc:
+ info = []
+ labels = %w(Modules Namespaces)
+
+ info << labels
+ info << ["-" * labels[0].size, "-" * labels[1].size]
+
+ thor_yaml.each do |name, hash|
+ info << [name, hash[:namespaces].join(", ")]
+ end
+
+ print_table info
+ say ""
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/shell.rb b/lib/bundler/vendor/thor/lib/thor/shell.rb
new file mode 100644
index 0000000000..e945549324
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/shell.rb
@@ -0,0 +1,81 @@
+require "rbconfig"
+
+class Bundler::Thor
+ module Base
+ class << self
+ attr_writer :shell
+
+ # Returns the shell used in all Bundler::Thor classes. If you are in a Unix platform
+ # it will use a colored log, otherwise it will use a basic one without color.
+ #
+ def shell
+ @shell ||= if ENV["THOR_SHELL"] && !ENV["THOR_SHELL"].empty?
+ Bundler::Thor::Shell.const_get(ENV["THOR_SHELL"])
+ elsif RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ && !ENV["ANSICON"]
+ Bundler::Thor::Shell::Basic
+ else
+ Bundler::Thor::Shell::Color
+ end
+ end
+ end
+ end
+
+ module Shell
+ SHELL_DELEGATED_METHODS = [:ask, :error, :set_color, :yes?, :no?, :say, :say_status, :print_in_columns, :print_table, :print_wrapped, :file_collision, :terminal_width]
+ attr_writer :shell
+
+ autoload :Basic, "bundler/vendor/thor/lib/thor/shell/basic"
+ autoload :Color, "bundler/vendor/thor/lib/thor/shell/color"
+ autoload :HTML, "bundler/vendor/thor/lib/thor/shell/html"
+
+ # Add shell to initialize config values.
+ #
+ # ==== Configuration
+ # shell<Object>:: An instance of the shell to be used.
+ #
+ # ==== Examples
+ #
+ # class MyScript < Bundler::Thor
+ # argument :first, :type => :numeric
+ # end
+ #
+ # MyScript.new [1.0], { :foo => :bar }, :shell => Bundler::Thor::Shell::Basic.new
+ #
+ def initialize(args = [], options = {}, config = {})
+ super
+ self.shell = config[:shell]
+ shell.base ||= self if shell.respond_to?(:base)
+ end
+
+ # Holds the shell for the given Bundler::Thor instance. If no shell is given,
+ # it gets a default shell from Bundler::Thor::Base.shell.
+ def shell
+ @shell ||= Bundler::Thor::Base.shell.new
+ end
+
+ # Common methods that are delegated to the shell.
+ SHELL_DELEGATED_METHODS.each do |method|
+ module_eval <<-METHOD, __FILE__, __LINE__
+ def #{method}(*args,&block)
+ shell.#{method}(*args,&block)
+ end
+ METHOD
+ end
+
+ # Yields the given block with padding.
+ def with_padding
+ shell.padding += 1
+ yield
+ ensure
+ shell.padding -= 1
+ end
+
+ protected
+
+ # Allow shell to be shared between invocations.
+ #
+ def _shared_configuration #:nodoc:
+ super.merge!(:shell => shell)
+ 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
new file mode 100644
index 0000000000..5162390efd
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb
@@ -0,0 +1,437 @@
+class Bundler::Thor
+ module Shell
+ class Basic
+ attr_accessor :base
+ attr_reader :padding
+
+ # Initialize base, mute and padding to nil.
+ #
+ def initialize #:nodoc:
+ @base = nil
+ @mute = false
+ @padding = 0
+ @always_force = false
+ end
+
+ # Mute everything that's inside given block
+ #
+ def mute
+ @mute = true
+ yield
+ ensure
+ @mute = false
+ end
+
+ # Check if base is muted
+ #
+ def mute?
+ @mute
+ end
+
+ # Sets the output padding, not allowing less than zero values.
+ #
+ def padding=(value)
+ @padding = [0, value].max
+ end
+
+ # Sets the output padding while executing a block and resets it.
+ #
+ def indent(count = 1)
+ orig_padding = padding
+ self.padding = padding + count
+ yield
+ self.padding = orig_padding
+ end
+
+ # Asks something to the user and receives a response.
+ #
+ # If asked to limit the correct responses, you can pass in an
+ # array of acceptable answers. If one of those is not supplied,
+ # they will be shown a message stating that one of those answers
+ # must be given and re-asked the question.
+ #
+ # If asking for sensitive information, the :echo option can be set
+ # to false to mask user input from $stdin.
+ #
+ # If the required input is a path, then set the path option to
+ # true. This will enable tab completion for file paths relative
+ # to the current working directory on systems that support
+ # Readline.
+ #
+ # ==== Example
+ # ask("What is your name?")
+ #
+ # ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"])
+ #
+ # ask("What is your password?", :echo => false)
+ #
+ # ask("Where should the file be saved?", :path => true)
+ #
+ def ask(statement, *args)
+ options = args.last.is_a?(Hash) ? args.pop : {}
+ color = args.first
+
+ if options[:limited_to]
+ ask_filtered(statement, color, options)
+ else
+ ask_simply(statement, color, options)
+ end
+ end
+
+ # Say (print) something to the user. If the sentence ends with a whitespace
+ # or tab character, a new line is not appended (print + flush). Otherwise
+ # are passed straight to puts (behavior got from Highline).
+ #
+ # ==== Example
+ # say("I know you knew that.")
+ #
+ def say(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/))
+ buffer = prepare_message(message, *color)
+ buffer << "\n" if force_new_line && !message.to_s.end_with?("\n")
+
+ stdout.print(buffer)
+ stdout.flush
+ end
+
+ # Say a status with the given color and appends the message. Since this
+ # method is used frequently by actions, it allows nil or false to be given
+ # in log_status, avoiding the message from being shown. If a Symbol is
+ # given in log_status, it's used as the color.
+ #
+ def say_status(status, message, log_status = true)
+ return if quiet? || log_status == false
+ spaces = " " * (padding + 1)
+ color = log_status.is_a?(Symbol) ? log_status : :green
+
+ status = status.to_s.rjust(12)
+ status = set_color status, color, true if color
+
+ buffer = "#{status}#{spaces}#{message}"
+ buffer = "#{buffer}\n" unless buffer.end_with?("\n")
+
+ stdout.print(buffer)
+ stdout.flush
+ end
+
+ # Make a question the to user and returns true if the user replies "y" or
+ # "yes".
+ #
+ def yes?(statement, color = nil)
+ !!(ask(statement, color, :add_to_history => false) =~ is?(:yes))
+ end
+
+ # Make a question the to user and returns true if the user replies "n" or
+ # "no".
+ #
+ def no?(statement, color = nil)
+ !!(ask(statement, color, :add_to_history => false) =~ is?(:no))
+ end
+
+ # Prints values in columns
+ #
+ # ==== Parameters
+ # Array[String, String, ...]
+ #
+ def print_in_columns(array)
+ return if array.empty?
+ colwidth = (array.map { |el| el.to_s.size }.max || 0) + 2
+ array.each_with_index do |value, index|
+ # Don't output trailing spaces when printing the last column
+ if ((((index + 1) % (terminal_width / colwidth))).zero? && !index.zero?) || index + 1 == array.length
+ stdout.puts value
+ else
+ stdout.printf("%-#{colwidth}s", value)
+ end
+ end
+ end
+
+ # Prints a table.
+ #
+ # ==== Parameters
+ # Array[Array[String, String, ...]]
+ #
+ # ==== Options
+ # indent<Integer>:: Indent the first column by indent value.
+ # colwidth<Integer>:: Force the first column to colwidth spaces wide.
+ #
+ def print_table(array, options = {}) # rubocop:disable MethodLength
+ return if array.empty?
+
+ formats = []
+ indent = options[:indent].to_i
+ colwidth = options[:colwidth]
+ options[:truncate] = terminal_width if options[:truncate] == true
+
+ formats << "%-#{colwidth + 2}s".dup if colwidth
+ start = colwidth ? 1 : 0
+
+ colcount = array.max { |a, b| a.size <=> b.size }.size
+
+ maximas = []
+
+ start.upto(colcount - 1) do |index|
+ maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max
+ maximas << maxima
+ formats << if index == colcount - 1
+ # Don't output 2 trailing spaces when printing the last column
+ "%-s".dup
+ else
+ "%-#{maxima + 2}s".dup
+ end
+ end
+
+ formats[0] = formats[0].insert(0, " " * indent)
+ formats << "%s"
+
+ array.each do |row|
+ sentence = "".dup
+
+ row.each_with_index do |column, index|
+ maxima = maximas[index]
+
+ f = if column.is_a?(Numeric)
+ if index == row.size - 1
+ # Don't output 2 trailing spaces when printing the last column
+ "%#{maxima}s"
+ else
+ "%#{maxima}s "
+ end
+ else
+ formats[index]
+ end
+ sentence << f % column.to_s
+ end
+
+ sentence = truncate(sentence, options[:truncate]) if options[:truncate]
+ stdout.puts sentence
+ end
+ end
+
+ # Prints a long string, word-wrapping the text to the current width of the
+ # terminal display. Ideal for printing heredocs.
+ #
+ # ==== Parameters
+ # String
+ #
+ # ==== Options
+ # indent<Integer>:: Indent each line of the printed paragraph by indent value.
+ #
+ def print_wrapped(message, options = {})
+ indent = options[:indent] || 0
+ width = terminal_width - indent
+ paras = message.split("\n\n")
+
+ paras.map! do |unwrapped|
+ unwrapped.strip.tr("\n", " ").squeeze(" ").gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") }
+ end
+
+ paras.each do |para|
+ para.split("\n").each do |line|
+ stdout.puts line.insert(0, " " * indent)
+ end
+ stdout.puts unless para == paras.last
+ end
+ end
+
+ # Deals with file collision and returns true if the file should be
+ # overwritten and false otherwise. If a block is given, it uses the block
+ # response as the content for the diff.
+ #
+ # ==== Parameters
+ # destination<String>:: the destination file to solve conflicts
+ # block<Proc>:: an optional block that returns the value to be used in diff
+ #
+ def file_collision(destination)
+ return true if @always_force
+ options = block_given? ? "[Ynaqdh]" : "[Ynaqh]"
+
+ loop do
+ answer = ask(
+ %[Overwrite #{destination}? (enter "h" for help) #{options}],
+ :add_to_history => false
+ )
+
+ case answer
+ when nil
+ say ""
+ return true
+ when is?(:yes), is?(:force), ""
+ return true
+ when is?(:no), is?(:skip)
+ return false
+ when is?(:always)
+ return @always_force = true
+ when is?(:quit)
+ say "Aborting..."
+ raise SystemExit
+ when is?(:diff)
+ show_diff(destination, yield) if block_given?
+ say "Retrying..."
+ else
+ say file_collision_help
+ end
+ end
+ end
+
+ # This code was copied from Rake, available under MIT-LICENSE
+ # Copyright (c) 2003, 2004 Jim Weirich
+ def terminal_width
+ result = if ENV["THOR_COLUMNS"]
+ ENV["THOR_COLUMNS"].to_i
+ else
+ unix? ? dynamic_width : 80
+ end
+ result < 10 ? 80 : result
+ rescue
+ 80
+ end
+
+ # Called if something goes wrong during the execution. This is used by Bundler::Thor
+ # internally and should not be used inside your scripts. If something went
+ # wrong, you can always raise an exception. If you raise a Bundler::Thor::Error, it
+ # will be rescued and wrapped in the method below.
+ #
+ def error(statement)
+ stderr.puts statement
+ end
+
+ # Apply color to the given string with optional bold. Disabled in the
+ # Bundler::Thor::Shell::Basic class.
+ #
+ def set_color(string, *) #:nodoc:
+ string
+ end
+
+ protected
+
+ def prepare_message(message, *color)
+ spaces = " " * padding
+ spaces + set_color(message.to_s, *color)
+ end
+
+ def can_display_colors?
+ false
+ end
+
+ def lookup_color(color)
+ return color unless color.is_a?(Symbol)
+ self.class.const_get(color.to_s.upcase)
+ end
+
+ def stdout
+ $stdout
+ end
+
+ def stderr
+ $stderr
+ end
+
+ def is?(value) #:nodoc:
+ value = value.to_s
+
+ if value.size == 1
+ /\A#{value}\z/i
+ else
+ /\A(#{value}|#{value[0, 1]})\z/i
+ end
+ end
+
+ def file_collision_help #:nodoc:
+ <<-HELP
+ Y - yes, overwrite
+ n - no, do not overwrite
+ a - all, overwrite this and all others
+ q - quit, abort
+ d - diff, show the differences between the old and the new
+ h - help, show this help
+ HELP
+ end
+
+ def show_diff(destination, content) #:nodoc:
+ diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u"
+
+ require "tempfile"
+ Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp|
+ temp.write content
+ temp.rewind
+ system %(#{diff_cmd} "#{destination}" "#{temp.path}")
+ end
+ end
+
+ def quiet? #:nodoc:
+ mute? || (base && base.options[:quiet])
+ end
+
+ # Calculate the dynamic width of the terminal
+ def dynamic_width
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
+ end
+
+ def dynamic_width_stty
+ `stty size 2>/dev/null`.split[1].to_i
+ end
+
+ def dynamic_width_tput
+ `tput cols 2>/dev/null`.to_i
+ end
+
+ def unix?
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
+ end
+
+ def truncate(string, width)
+ as_unicode do
+ chars = string.chars.to_a
+ if chars.length <= width
+ chars.join
+ else
+ chars[0, width - 3].join + "..."
+ end
+ end
+ end
+
+ if "".respond_to?(:encode)
+ def as_unicode
+ yield
+ end
+ else
+ def as_unicode
+ old = $KCODE
+ $KCODE = "U"
+ yield
+ ensure
+ $KCODE = old
+ end
+ end
+
+ def ask_simply(statement, color, options)
+ default = options[:default]
+ message = [statement, ("(#{default})" if default), nil].uniq.join(" ")
+ message = prepare_message(message, *color)
+ result = Bundler::Thor::LineEditor.readline(message, options)
+
+ return unless result
+
+ result = result.strip
+
+ if default && result == ""
+ default
+ else
+ result
+ end
+ end
+
+ def ask_filtered(statement, color, options)
+ answer_set = options[:limited_to]
+ correct_answer = nil
+ until correct_answer
+ answers = answer_set.join(", ")
+ answer = ask_simply("#{statement} [#{answers}]", color, options)
+ correct_answer = answer_set.include?(answer) ? answer : nil
+ say("Your response must be one of: [#{answers}]. Please try again.") unless correct_answer
+ end
+ correct_answer
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/shell/color.rb b/lib/bundler/vendor/thor/lib/thor/shell/color.rb
new file mode 100644
index 0000000000..da289cb50c
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/shell/color.rb
@@ -0,0 +1,149 @@
+require "bundler/vendor/thor/lib/thor/shell/basic"
+
+class Bundler::Thor
+ module Shell
+ # Inherit from Bundler::Thor::Shell::Basic and add set_color behavior. Check
+ # Bundler::Thor::Shell::Basic to see all available methods.
+ #
+ class Color < Basic
+ # Embed in a String to clear all previous ANSI sequences.
+ CLEAR = "\e[0m"
+ # The start of an ANSI bold sequence.
+ BOLD = "\e[1m"
+
+ # Set the terminal's foreground ANSI color to black.
+ BLACK = "\e[30m"
+ # Set the terminal's foreground ANSI color to red.
+ RED = "\e[31m"
+ # Set the terminal's foreground ANSI color to green.
+ GREEN = "\e[32m"
+ # Set the terminal's foreground ANSI color to yellow.
+ YELLOW = "\e[33m"
+ # Set the terminal's foreground ANSI color to blue.
+ BLUE = "\e[34m"
+ # Set the terminal's foreground ANSI color to magenta.
+ MAGENTA = "\e[35m"
+ # Set the terminal's foreground ANSI color to cyan.
+ CYAN = "\e[36m"
+ # Set the terminal's foreground ANSI color to white.
+ WHITE = "\e[37m"
+
+ # Set the terminal's background ANSI color to black.
+ ON_BLACK = "\e[40m"
+ # Set the terminal's background ANSI color to red.
+ ON_RED = "\e[41m"
+ # Set the terminal's background ANSI color to green.
+ ON_GREEN = "\e[42m"
+ # Set the terminal's background ANSI color to yellow.
+ ON_YELLOW = "\e[43m"
+ # Set the terminal's background ANSI color to blue.
+ ON_BLUE = "\e[44m"
+ # Set the terminal's background ANSI color to magenta.
+ ON_MAGENTA = "\e[45m"
+ # Set the terminal's background ANSI color to cyan.
+ ON_CYAN = "\e[46m"
+ # Set the terminal's background ANSI color to white.
+ ON_WHITE = "\e[47m"
+
+ # Set color by using a string or one of the defined constants. If a third
+ # option is set to true, it also adds bold to the string. This is based
+ # on Highline implementation and it automatically appends CLEAR to the end
+ # of the returned String.
+ #
+ # Pass foreground, background and bold options to this method as
+ # symbols.
+ #
+ # Example:
+ #
+ # set_color "Hi!", :red, :on_white, :bold
+ #
+ # The available colors are:
+ #
+ # :bold
+ # :black
+ # :red
+ # :green
+ # :yellow
+ # :blue
+ # :magenta
+ # :cyan
+ # :white
+ # :on_black
+ # :on_red
+ # :on_green
+ # :on_yellow
+ # :on_blue
+ # :on_magenta
+ # :on_cyan
+ # :on_white
+ def set_color(string, *colors)
+ if colors.compact.empty? || !can_display_colors?
+ string
+ elsif colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) }
+ ansi_colors = colors.map { |color| lookup_color(color) }
+ "#{ansi_colors.join}#{string}#{CLEAR}"
+ else
+ # The old API was `set_color(color, bold=boolean)`. We
+ # continue to support the old API because you should never
+ # break old APIs unnecessarily :P
+ foreground, bold = colors
+ foreground = self.class.const_get(foreground.to_s.upcase) if foreground.is_a?(Symbol)
+
+ bold = bold ? BOLD : ""
+ "#{bold}#{foreground}#{string}#{CLEAR}"
+ end
+ end
+
+ protected
+
+ def can_display_colors?
+ stdout.tty?
+ end
+
+ # Overwrite show_diff to show diff with colors if Diff::LCS is
+ # available.
+ #
+ def show_diff(destination, content) #:nodoc:
+ if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil?
+ actual = File.binread(destination).to_s.split("\n")
+ content = content.to_s.split("\n")
+
+ Diff::LCS.sdiff(actual, content).each do |diff|
+ output_diff_line(diff)
+ end
+ else
+ super
+ end
+ end
+
+ def output_diff_line(diff) #:nodoc:
+ case diff.action
+ when "-"
+ say "- #{diff.old_element.chomp}", :red, true
+ when "+"
+ say "+ #{diff.new_element.chomp}", :green, true
+ when "!"
+ say "- #{diff.old_element.chomp}", :red, true
+ say "+ #{diff.new_element.chomp}", :green, true
+ else
+ say " #{diff.old_element.chomp}", nil, true
+ end
+ end
+
+ # Check if Diff::LCS is loaded. If it is, use it to create pretty output
+ # for diff.
+ #
+ def diff_lcs_loaded? #:nodoc:
+ return true if defined?(Diff::LCS)
+ return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
+
+ @diff_lcs_loaded = begin
+ require "diff/lcs"
+ true
+ rescue LoadError
+ false
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/shell/html.rb b/lib/bundler/vendor/thor/lib/thor/shell/html.rb
new file mode 100644
index 0000000000..83d2054988
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/shell/html.rb
@@ -0,0 +1,126 @@
+require "bundler/vendor/thor/lib/thor/shell/basic"
+
+class Bundler::Thor
+ module Shell
+ # Inherit from Bundler::Thor::Shell::Basic and add set_color behavior. Check
+ # Bundler::Thor::Shell::Basic to see all available methods.
+ #
+ class HTML < Basic
+ # The start of an HTML bold sequence.
+ BOLD = "font-weight: bold"
+
+ # Set the terminal's foreground HTML color to black.
+ BLACK = "color: black"
+ # Set the terminal's foreground HTML color to red.
+ RED = "color: red"
+ # Set the terminal's foreground HTML color to green.
+ GREEN = "color: green"
+ # Set the terminal's foreground HTML color to yellow.
+ YELLOW = "color: yellow"
+ # Set the terminal's foreground HTML color to blue.
+ BLUE = "color: blue"
+ # Set the terminal's foreground HTML color to magenta.
+ MAGENTA = "color: magenta"
+ # Set the terminal's foreground HTML color to cyan.
+ CYAN = "color: cyan"
+ # Set the terminal's foreground HTML color to white.
+ WHITE = "color: white"
+
+ # Set the terminal's background HTML color to black.
+ ON_BLACK = "background-color: black"
+ # Set the terminal's background HTML color to red.
+ ON_RED = "background-color: red"
+ # Set the terminal's background HTML color to green.
+ ON_GREEN = "background-color: green"
+ # Set the terminal's background HTML color to yellow.
+ ON_YELLOW = "background-color: yellow"
+ # Set the terminal's background HTML color to blue.
+ ON_BLUE = "background-color: blue"
+ # Set the terminal's background HTML color to magenta.
+ ON_MAGENTA = "background-color: magenta"
+ # Set the terminal's background HTML color to cyan.
+ ON_CYAN = "background-color: cyan"
+ # Set the terminal's background HTML color to white.
+ ON_WHITE = "background-color: white"
+
+ # Set color by using a string or one of the defined constants. If a third
+ # option is set to true, it also adds bold to the string. This is based
+ # on Highline implementation and it automatically appends CLEAR to the end
+ # of the returned String.
+ #
+ def set_color(string, *colors)
+ if colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) }
+ html_colors = colors.map { |color| lookup_color(color) }
+ "<span style=\"#{html_colors.join('; ')};\">#{string}</span>"
+ else
+ color, bold = colors
+ html_color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol)
+ styles = [html_color]
+ styles << BOLD if bold
+ "<span style=\"#{styles.join('; ')};\">#{string}</span>"
+ end
+ end
+
+ # Ask something to the user and receives a response.
+ #
+ # ==== Example
+ # ask("What is your name?")
+ #
+ # TODO: Implement #ask for Bundler::Thor::Shell::HTML
+ def ask(statement, color = nil)
+ raise NotImplementedError, "Implement #ask for Bundler::Thor::Shell::HTML"
+ end
+
+ protected
+
+ def can_display_colors?
+ true
+ end
+
+ # Overwrite show_diff to show diff with colors if Diff::LCS is
+ # available.
+ #
+ def show_diff(destination, content) #:nodoc:
+ if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil?
+ actual = File.binread(destination).to_s.split("\n")
+ content = content.to_s.split("\n")
+
+ Diff::LCS.sdiff(actual, content).each do |diff|
+ output_diff_line(diff)
+ end
+ else
+ super
+ end
+ end
+
+ def output_diff_line(diff) #:nodoc:
+ case diff.action
+ when "-"
+ say "- #{diff.old_element.chomp}", :red, true
+ when "+"
+ say "+ #{diff.new_element.chomp}", :green, true
+ when "!"
+ say "- #{diff.old_element.chomp}", :red, true
+ say "+ #{diff.new_element.chomp}", :green, true
+ else
+ say " #{diff.old_element.chomp}", nil, true
+ end
+ end
+
+ # Check if Diff::LCS is loaded. If it is, use it to create pretty output
+ # for diff.
+ #
+ def diff_lcs_loaded? #:nodoc:
+ return true if defined?(Diff::LCS)
+ return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
+
+ @diff_lcs_loaded = begin
+ require "diff/lcs"
+ true
+ rescue LoadError
+ false
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/util.rb b/lib/bundler/vendor/thor/lib/thor/util.rb
new file mode 100644
index 0000000000..5d03177a28
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/util.rb
@@ -0,0 +1,268 @@
+require "rbconfig"
+
+class Bundler::Thor
+ module Sandbox #:nodoc:
+ end
+
+ # This module holds several utilities:
+ #
+ # 1) Methods to convert thor namespaces to constants and vice-versa.
+ #
+ # Bundler::Thor::Util.namespace_from_thor_class(Foo::Bar::Baz) #=> "foo:bar:baz"
+ #
+ # 2) Loading thor files and sandboxing:
+ #
+ # Bundler::Thor::Util.load_thorfile("~/.thor/foo")
+ #
+ module Util
+ class << self
+ # Receives a namespace and search for it in the Bundler::Thor::Base subclasses.
+ #
+ # ==== Parameters
+ # namespace<String>:: The namespace to search for.
+ #
+ def find_by_namespace(namespace)
+ namespace = "default#{namespace}" if namespace.empty? || namespace =~ /^:/
+ Bundler::Thor::Base.subclasses.detect { |klass| klass.namespace == namespace }
+ end
+
+ # Receives a constant and converts it to a Bundler::Thor namespace. Since Bundler::Thor
+ # commands can be added to a sandbox, this method is also responsable for
+ # removing the sandbox namespace.
+ #
+ # This method should not be used in general because it's used to deal with
+ # older versions of Bundler::Thor. On current versions, if you need to get the
+ # namespace from a class, just call namespace on it.
+ #
+ # ==== Parameters
+ # constant<Object>:: The constant to be converted to the thor path.
+ #
+ # ==== Returns
+ # String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz"
+ #
+ def namespace_from_thor_class(constant)
+ constant = constant.to_s.gsub(/^Bundler::Thor::Sandbox::/, "")
+ constant = snake_case(constant).squeeze(":")
+ constant
+ end
+
+ # Given the contents, evaluate it inside the sandbox and returns the
+ # namespaces defined in the sandbox.
+ #
+ # ==== Parameters
+ # contents<String>
+ #
+ # ==== Returns
+ # Array[Object]
+ #
+ def namespaces_in_content(contents, file = __FILE__)
+ old_constants = Bundler::Thor::Base.subclasses.dup
+ Bundler::Thor::Base.subclasses.clear
+
+ load_thorfile(file, contents)
+
+ new_constants = Bundler::Thor::Base.subclasses.dup
+ Bundler::Thor::Base.subclasses.replace(old_constants)
+
+ new_constants.map!(&:namespace)
+ new_constants.compact!
+ new_constants
+ end
+
+ # Returns the thor classes declared inside the given class.
+ #
+ def thor_classes_in(klass)
+ stringfied_constants = klass.constants.map(&:to_s)
+ Bundler::Thor::Base.subclasses.select do |subclass|
+ next unless subclass.name
+ stringfied_constants.include?(subclass.name.gsub("#{klass.name}::", ""))
+ end
+ end
+
+ # Receives a string and convert it to snake case. SnakeCase returns snake_case.
+ #
+ # ==== Parameters
+ # String
+ #
+ # ==== Returns
+ # String
+ #
+ def snake_case(str)
+ return str.downcase if str =~ /^[A-Z_]+$/
+ str.gsub(/\B[A-Z]/, '_\&').squeeze("_") =~ /_*(.*)/
+ $+.downcase
+ end
+
+ # Receives a string and convert it to camel case. camel_case returns CamelCase.
+ #
+ # ==== Parameters
+ # String
+ #
+ # ==== Returns
+ # String
+ #
+ def camel_case(str)
+ return str if str !~ /_/ && str =~ /[A-Z]+.*/
+ str.split("_").map(&:capitalize).join
+ end
+
+ # Receives a namespace and tries to retrieve a Bundler::Thor or Bundler::Thor::Group class
+ # from it. It first searches for a class using the all the given namespace,
+ # if it's not found, removes the highest entry and searches for the class
+ # again. If found, returns the highest entry as the class name.
+ #
+ # ==== Examples
+ #
+ # class Foo::Bar < Bundler::Thor
+ # def baz
+ # end
+ # end
+ #
+ # class Baz::Foo < Bundler::Thor::Group
+ # end
+ #
+ # Bundler::Thor::Util.namespace_to_thor_class("foo:bar") #=> Foo::Bar, nil # will invoke default command
+ # Bundler::Thor::Util.namespace_to_thor_class("baz:foo") #=> Baz::Foo, nil
+ # Bundler::Thor::Util.namespace_to_thor_class("foo:bar:baz") #=> Foo::Bar, "baz"
+ #
+ # ==== Parameters
+ # namespace<String>
+ #
+ def find_class_and_command_by_namespace(namespace, fallback = true)
+ if namespace.include?(":") # look for a namespaced command
+ pieces = namespace.split(":")
+ command = pieces.pop
+ klass = Bundler::Thor::Util.find_by_namespace(pieces.join(":"))
+ end
+ unless klass # look for a Bundler::Thor::Group with the right name
+ klass = Bundler::Thor::Util.find_by_namespace(namespace)
+ command = nil
+ end
+ if !klass && fallback # try a command in the default namespace
+ command = namespace
+ klass = Bundler::Thor::Util.find_by_namespace("")
+ end
+ [klass, command]
+ end
+ alias_method :find_class_and_task_by_namespace, :find_class_and_command_by_namespace
+
+ # Receives a path and load the thor file in the path. The file is evaluated
+ # inside the sandbox to avoid namespacing conflicts.
+ #
+ def load_thorfile(path, content = nil, debug = false)
+ content ||= File.binread(path)
+
+ begin
+ Bundler::Thor::Sandbox.class_eval(content, path)
+ rescue StandardError => e
+ $stderr.puts("WARNING: unable to load thorfile #{path.inspect}: #{e.message}")
+ if debug
+ $stderr.puts(*e.backtrace)
+ else
+ $stderr.puts(e.backtrace.first)
+ end
+ end
+ end
+
+ def user_home
+ @@user_home ||= if ENV["HOME"]
+ ENV["HOME"]
+ elsif ENV["USERPROFILE"]
+ ENV["USERPROFILE"]
+ elsif ENV["HOMEDRIVE"] && ENV["HOMEPATH"]
+ File.join(ENV["HOMEDRIVE"], ENV["HOMEPATH"])
+ elsif ENV["APPDATA"]
+ ENV["APPDATA"]
+ else
+ begin
+ File.expand_path("~")
+ rescue
+ if File::ALT_SEPARATOR
+ "C:/"
+ else
+ "/"
+ end
+ end
+ end
+ end
+
+ # Returns the root where thor files are located, depending on the OS.
+ #
+ def thor_root
+ File.join(user_home, ".thor").tr('\\', "/")
+ end
+
+ # Returns the files in the thor root. On Windows thor_root will be something
+ # like this:
+ #
+ # C:\Documents and Settings\james\.thor
+ #
+ # If we don't #gsub the \ character, Dir.glob will fail.
+ #
+ def thor_root_glob
+ files = Dir["#{escape_globs(thor_root)}/*"]
+
+ files.map! do |file|
+ File.directory?(file) ? File.join(file, "main.thor") : file
+ end
+ end
+
+ # Where to look for Bundler::Thor files.
+ #
+ def globs_for(path)
+ path = escape_globs(path)
+ ["#{path}/Bundler::Thorfile", "#{path}/*.thor", "#{path}/tasks/*.thor", "#{path}/lib/tasks/*.thor"]
+ end
+
+ # Return the path to the ruby interpreter taking into account multiple
+ # installations and windows extensions.
+ #
+ def ruby_command
+ @ruby_command ||= begin
+ ruby_name = RbConfig::CONFIG["ruby_install_name"]
+ ruby = File.join(RbConfig::CONFIG["bindir"], ruby_name)
+ ruby << RbConfig::CONFIG["EXEEXT"]
+
+ # avoid using different name than ruby (on platforms supporting links)
+ if ruby_name != "ruby" && File.respond_to?(:readlink)
+ begin
+ alternate_ruby = File.join(RbConfig::CONFIG["bindir"], "ruby")
+ alternate_ruby << RbConfig::CONFIG["EXEEXT"]
+
+ # ruby is a symlink
+ if File.symlink? alternate_ruby
+ linked_ruby = File.readlink alternate_ruby
+
+ # symlink points to 'ruby_install_name'
+ ruby = alternate_ruby if linked_ruby == ruby_name || linked_ruby == ruby
+ end
+ rescue NotImplementedError # rubocop:disable HandleExceptions
+ # just ignore on windows
+ end
+ end
+
+ # escape string in case path to ruby executable contain spaces.
+ ruby.sub!(/.*\s.*/m, '"\&"')
+ ruby
+ end
+ end
+
+ # Returns a string that has had any glob characters escaped.
+ # The glob characters are `* ? { } [ ]`.
+ #
+ # ==== Examples
+ #
+ # Bundler::Thor::Util.escape_globs('[apps]') # => '\[apps\]'
+ #
+ # ==== Parameters
+ # String
+ #
+ # ==== Returns
+ # String
+ #
+ def escape_globs(path)
+ path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&')
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/thor/lib/thor/version.rb b/lib/bundler/vendor/thor/lib/thor/version.rb
new file mode 100644
index 0000000000..df8f18821a
--- /dev/null
+++ b/lib/bundler/vendor/thor/lib/thor/version.rb
@@ -0,0 +1,3 @@
+class Bundler::Thor
+ VERSION = "0.20.0"
+end
diff --git a/lib/bundler/vendored_fileutils.rb b/lib/bundler/vendored_fileutils.rb
new file mode 100644
index 0000000000..d14e98baf7
--- /dev/null
+++ b/lib/bundler/vendored_fileutils.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Bundler; end
+if RUBY_VERSION >= "2.4"
+ require "bundler/vendor/fileutils/lib/fileutils"
+else
+ # the version we vendor is 2.4+
+ require "fileutils"
+end
diff --git a/lib/bundler/vendored_molinillo.rb b/lib/bundler/vendored_molinillo.rb
new file mode 100644
index 0000000000..061b634f72
--- /dev/null
+++ b/lib/bundler/vendored_molinillo.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+module Bundler; end
+require "bundler/vendor/molinillo/lib/molinillo"
diff --git a/lib/bundler/vendored_persistent.rb b/lib/bundler/vendored_persistent.rb
new file mode 100644
index 0000000000..de9c42fcc1
--- /dev/null
+++ b/lib/bundler/vendored_persistent.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+# We forcibly require OpenSSL, because net/http/persistent will only autoload
+# it. On some Rubies, autoload fails but explicit require succeeds.
+begin
+ require "openssl"
+rescue LoadError
+ # some Ruby builds don't have OpenSSL
+end
+module Bundler
+ module Persistent
+ module Net
+ module HTTP
+ end
+ end
+ end
+end
+require "bundler/vendor/net-http-persistent/lib/net/http/persistent"
+
+module Bundler
+ class PersistentHTTP < Persistent::Net::HTTP::Persistent
+ def connection_for(uri)
+ connection = super
+ warn_old_tls_version_rubygems_connection(uri, connection)
+ connection
+ end
+
+ def warn_old_tls_version_rubygems_connection(uri, connection)
+ return unless connection.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
+end
diff --git a/lib/bundler/vendored_thor.rb b/lib/bundler/vendored_thor.rb
new file mode 100644
index 0000000000..8cca090f55
--- /dev/null
+++ b/lib/bundler/vendored_thor.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+module Bundler
+ def self.require_thor_actions
+ Kernel.send(:require, "bundler/vendor/thor/lib/thor/actions")
+ end
+end
+require "bundler/vendor/thor/lib/thor"
diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb
new file mode 100644
index 0000000000..01b28c08eb
--- /dev/null
+++ b/lib/bundler/version.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: false
+
+# Ruby 1.9.3 and old RubyGems don't play nice with frozen version strings
+# rubocop:disable MutableConstant
+
+module Bundler
+ # We're doing this because we might write tests that deal
+ # with other versions of bundler and we are unsure how to
+ # handle this better.
+ VERSION = "1.17.2" unless defined?(::Bundler::VERSION)
+
+ def self.overwrite_loaded_gem_version
+ begin
+ require "rubygems"
+ rescue LoadError
+ return
+ end
+ return unless bundler_spec = Gem.loaded_specs["bundler"]
+ return if bundler_spec.version == VERSION
+ bundler_spec.version = Bundler::VERSION
+ end
+ private_class_method :overwrite_loaded_gem_version
+ overwrite_loaded_gem_version
+
+ def self.bundler_major_version
+ @bundler_major_version ||= VERSION.split(".").first.to_i
+ end
+end
diff --git a/lib/bundler/version_ranges.rb b/lib/bundler/version_ranges.rb
new file mode 100644
index 0000000000..ec25716cde
--- /dev/null
+++ b/lib/bundler/version_ranges.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+module Bundler
+ module VersionRanges
+ NEq = Struct.new(:version)
+ ReqR = Struct.new(:left, :right)
+ class ReqR
+ Endpoint = Struct.new(:version, :inclusive)
+ def to_s
+ "#{left.inclusive ? "[" : "("}#{left.version}, #{right.version}#{right.inclusive ? "]" : ")"}"
+ end
+ INFINITY = Object.new.freeze
+ 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
+
+ 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_by {|range| [range.left.version, range.left.inclusive ? 0 : 1] }, 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
+ when 1 then next curr_range
+ when 0 then next(last_range.right.inclusive && curr_range.left.inclusive && !neqs.include?(curr_range.left.version) && curr_range)
+ when -1 then next false
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vlad.rb b/lib/bundler/vlad.rb
new file mode 100644
index 0000000000..68181e7db8
--- /dev/null
+++ b/lib/bundler/vlad.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require "bundler/shared_helpers"
+Bundler::SharedHelpers.major_deprecation 2,
+ "The Bundler task for Vlad"
+
+# Vlad task for Bundler.
+#
+# Add "require 'bundler/vlad'" in your Vlad deploy.rb, and
+# include the vlad:bundle:install task in your vlad:deploy task.
+require "bundler/deployment"
+
+include Rake::DSL if defined? Rake::DSL
+
+namespace :vlad do
+ Bundler::Deployment.define_task(Rake::RemoteTask, :remote_task, :roles => :app)
+end
diff --git a/lib/bundler/worker.rb b/lib/bundler/worker.rb
new file mode 100644
index 0000000000..e91cfa7805
--- /dev/null
+++ b/lib/bundler/worker.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require "thread"
+
+module Bundler
+ class Worker
+ POISON = Object.new
+
+ class WrappedException < StandardError
+ attr_reader :exception
+ def initialize(exn)
+ @exception = exn
+ end
+ end
+
+ # @return [String] the name of the worker
+ attr_reader :name
+
+ # Creates a worker pool of specified size
+ #
+ # @param size [Integer] Size of pool
+ # @param name [String] name the name of the worker
+ # @param func [Proc] job to run in inside the worker pool
+ def initialize(size, name, func)
+ @name = name
+ @request_queue = Queue.new
+ @response_queue = Queue.new
+ @func = func
+ @size = size
+ @threads = nil
+ SharedHelpers.trap("INT") { abort_threads }
+ end
+
+ # Enqueue a request to be executed in the worker pool
+ #
+ # @param obj [String] mostly it is name of spec that should be downloaded
+ def enq(obj)
+ create_threads unless @threads
+ @request_queue.enq obj
+ end
+
+ # Retrieves results of job function being executed in worker pool
+ def deq
+ result = @response_queue.deq
+ raise result.exception if result.is_a?(WrappedException)
+ result
+ end
+
+ def stop
+ stop_threads
+ end
+
+ private
+
+ def process_queue(i)
+ loop do
+ obj = @request_queue.deq
+ break if obj.equal? POISON
+ @response_queue.enq apply_func(obj, i)
+ end
+ end
+
+ def apply_func(obj, i)
+ @func.call(obj, i)
+ rescue Exception => e
+ WrappedException.new(e)
+ end
+
+ # Stop the worker threads by sending a poison object down the request queue
+ # so as worker threads after retrieving it, shut themselves down
+ def stop_threads
+ return unless @threads
+ @threads.each { @request_queue.enq POISON }
+ @threads.each(&:join)
+ @threads = nil
+ end
+
+ def abort_threads
+ return unless @threads
+ Bundler.ui.debug("\n#{caller.join("\n")}")
+ @threads.each(&:exit)
+ exit 1
+ end
+
+ def create_threads
+ 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
+ end
+ end.compact
+
+ return if creation_errors.empty?
+
+ message = "Failed to create threads for the #{name} worker: #{creation_errors.map(&:to_s).uniq.join(", ")}"
+ raise ThreadCreationError, message if @threads.empty?
+ Bundler.ui.info message
+ end
+ end
+end
diff --git a/lib/bundler/yaml_serializer.rb b/lib/bundler/yaml_serializer.rb
new file mode 100644
index 0000000000..0fd81c40ef
--- /dev/null
+++ b/lib/bundler/yaml_serializer.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+module Bundler
+ # A stub yaml serializer that can handle only hashes and strings (as of now).
+ module YAMLSerializer
+ module_function
+
+ def dump(hash)
+ yaml = String.new("---")
+ yaml << dump_hash(hash)
+ end
+
+ def dump_hash(hash)
+ yaml = String.new("\n")
+ hash.each do |k, v|
+ yaml << k << ":"
+ if v.is_a?(Hash)
+ yaml << dump_hash(v).gsub(/^(?!$)/, " ") # indent all non-empty lines
+ elsif v.is_a?(Array) # Expected to be array of strings
+ yaml << "\n- " << v.map {|s| s.to_s.gsub(/\s+/, " ").inspect }.join("\n- ") << "\n"
+ else
+ yaml << " " << v.to_s.gsub(/\s+/, " ").inspect << "\n"
+ end
+ end
+ yaml
+ end
+
+ ARRAY_REGEX = /
+ ^
+ (?:[ ]*-[ ]) # '- ' before array items
+ (['"]?) # optional opening quote
+ (.*) # value
+ \1 # matching closing quote
+ $
+ /xo
+
+ HASH_REGEX = /
+ ^
+ ([ ]*) # indentations
+ (.+) # key
+ (?::(?=(?:\s|$))) # : (without the lookahead the #key includes this when : is present in value)
+ [ ]?
+ (?: !\s)? # optional exclamation mark found with ruby 1.9.3
+ (['"]?) # optional opening quote
+ (.*) # value
+ \3 # matching closing quote
+ $
+ /xo
+
+ def load(str)
+ res = {}
+ stack = [res]
+ last_hash = nil
+ last_empty_key = nil
+ str.split(/\r?\n/).each do |line|
+ if match = HASH_REGEX.match(line)
+ indent, key, quote, val = match.captures
+ key = convert_to_backward_compatible_key(key)
+ depth = indent.scan(/ /).length
+ if quote.empty? && val.empty?
+ new_hash = {}
+ stack[depth][key] = new_hash
+ stack[depth + 1] = new_hash
+ last_empty_key = key
+ last_hash = stack[depth]
+ else
+ stack[depth][key] = val
+ end
+ elsif match = ARRAY_REGEX.match(line)
+ _, val = match.captures
+ last_hash[last_empty_key] = [] unless last_hash[last_empty_key].is_a?(Array)
+
+ last_hash[last_empty_key].push(val)
+ end
+ end
+ res
+ end
+
+ # for settings' keys
+ def convert_to_backward_compatible_key(key)
+ key = "#{key}/" if key =~ /https?:/i && key !~ %r{/\Z}
+ key = key.gsub(".", "__") if key.include?(".")
+ key
+ end
+
+ class << self
+ private :dump_hash, :convert_to_backward_compatible_key
+ end
+ end
+end
diff --git a/lib/cgi.rb b/lib/cgi.rb
index 167b76cef7..0f44a929e4 100644
--- a/lib/cgi.rb
+++ b/lib/cgi.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# cgi.rb - cgi support library
#
diff --git a/lib/cgi/cookie.rb b/lib/cgi/cookie.rb
index 4cc050b90d..9e0908386e 100644
--- a/lib/cgi/cookie.rb
+++ b/lib/cgi/cookie.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'cgi/util'
+# frozen_string_literal: true
+require_relative 'util'
class CGI
# Class representing an HTTP cookie.
#
@@ -143,7 +143,7 @@ class CGI
# Convert the Cookie to its string representation.
def to_s
val = collect{|v| CGI.escape(v) }.join("&")
- buf = "#{@name}=#{val}"
+ buf = "#{@name}=#{val}".dup
buf << "; domain=#{@domain}" if @domain
buf << "; path=#{@path}" if @path
buf << "; expires=#{CGI::rfc1123_date(@expires)}" if @expires
@@ -165,7 +165,6 @@ class CGI
raw_cookie.split(/;\s?/).each do |pairs|
name, values = pairs.split('=',2)
next unless name and values
- name = CGI.unescape(name)
values ||= ""
values = values.split('&').collect{|v| CGI.unescape(v,@@accept_charset) }
if cookies.has_key?(name)
diff --git a/lib/cgi/core.rb b/lib/cgi/core.rb
index 1a741dcd76..c45f9b3a9e 100644
--- a/lib/cgi/core.rb
+++ b/lib/cgi/core.rb
@@ -1,9 +1,16 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
# Methods for generating HTML, parsing CGI-related parameters, and
# generating HTTP responses.
#++
class CGI
+ unless const_defined?(:Util)
+ module Util
+ @@accept_charset = "UTF-8" # :nodoc:
+ end
+ include Util
+ extend Util
+ end
$CGI_ENV = ENV # for FCGI support
@@ -146,7 +153,7 @@ class CGI
# "language" => "ja",
# "expires" => Time.now + 30,
# "cookie" => [cookie1, cookie2],
- # "my_header1" => "my_value"
+ # "my_header1" => "my_value",
# "my_header2" => "my_value")
#
# This method does not perform charset conversion.
@@ -182,7 +189,7 @@ class CGI
alias :header :http_header
def _header_for_string(content_type) #:nodoc:
- buf = ''
+ buf = ''.dup
if nph?()
buf << "#{$CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0'} 200 OK#{EOL}"
buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}"
@@ -198,7 +205,7 @@ class CGI
private :_header_for_string
def _header_for_hash(options) #:nodoc:
- buf = ''
+ buf = ''.dup
## add charset to option['type']
options['type'] ||= 'text/html'
charset = options.delete('charset')
@@ -260,7 +267,7 @@ class CGI
def _header_for_modruby(buf) #:nodoc:
request = Apache::request
buf.scan(/([^:]+): (.+)#{EOL}/o) do |name, value|
- warn sprintf("name:%s value:%s\n", name, value) if $DEBUG
+ $stderr.printf("name:%s value:%s\n", name, value) if $DEBUG
case name
when 'Set-Cookie'
request.headers_out.add(name, value)
@@ -414,7 +421,7 @@ class CGI
module QueryExtension
%w[ CONTENT_LENGTH SERVER_PORT ].each do |env|
- define_method(env.sub(/^HTTP_/, '').downcase) do
+ define_method(env.delete_prefix('HTTP_').downcase) do
(val = env_table[env]) && Integer(val)
end
end
@@ -427,7 +434,7 @@ class CGI
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST
HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
- define_method(env.sub(/^HTTP_/, '').downcase) do
+ define_method(env.delete_prefix('HTTP_').downcase) do
env_table[env]
end
end
@@ -480,7 +487,7 @@ class CGI
@files = {}
boundary_rexp = /--#{Regexp.quote(boundary)}(#{EOL}|--)/
boundary_size = "#{EOL}--#{boundary}#{EOL}".bytesize
- buf = ''
+ buf = ''.dup
bufsize = 10 * 1024
max_count = MAX_MULTIPART_COUNT
n = 0
@@ -535,12 +542,12 @@ class CGI
body.rewind
## original filename
/Content-Disposition:.* filename=(?:"(.*?)"|([^;\r\n]*))/i.match(head)
- filename = $1 || $2 || ''
+ filename = $1 || $2 || ''.dup
filename = CGI.unescape(filename) if unescape_filename?()
body.instance_variable_set(:@original_filename, filename.taint)
## content type
/Content-Type: (.*)/i.match(head)
- (content_type = $1 || '').chomp!
+ (content_type = $1 || ''.dup).chomp!
body.instance_variable_set(:@content_type, content_type.taint)
## query parameter name
/Content-Disposition:.* name=(?:"(.*?)"|([^;\r\n]*))/i.match(head)
@@ -589,7 +596,7 @@ class CGI
else
begin
require 'stringio'
- body = StringIO.new("".force_encoding(Encoding::ASCII_8BIT))
+ body = StringIO.new("".b)
rescue LoadError
require 'tempfile'
body = Tempfile.new('CGI', encoding: Encoding::ASCII_8BIT)
@@ -700,7 +707,7 @@ class CGI
if value
return value
elsif defined? StringIO
- StringIO.new("".force_encoding(Encoding::ASCII_8BIT))
+ StringIO.new("".b)
else
Tempfile.new("CGI",encoding: Encoding::ASCII_8BIT)
end
@@ -734,7 +741,7 @@ class CGI
#
# CGI.accept_charset = "EUC-JP"
#
- @@accept_charset="UTF-8"
+ @@accept_charset="UTF-8" if false # needed for rdoc?
# Return the accept character set for all new CGI instances.
def self.accept_charset
@@ -855,24 +862,24 @@ class CGI
case @options[:tag_maker]
when "html3"
- require 'cgi/html'
+ require_relative 'html'
extend Html3
extend HtmlExtension
when "html4"
- require 'cgi/html'
+ require_relative 'html'
extend Html4
extend HtmlExtension
when "html4Tr"
- require 'cgi/html'
+ require_relative 'html'
extend Html4Tr
extend HtmlExtension
when "html4Fr"
- require 'cgi/html'
+ require_relative 'html'
extend Html4Tr
extend Html4Fr
extend HtmlExtension
when "html5"
- require 'cgi/html'
+ require_relative 'html'
extend Html5
extend HtmlExtension
end
diff --git a/lib/cgi/html.rb b/lib/cgi/html.rb
index 4b9e577b32..02d847ebd3 100644
--- a/lib/cgi/html.rb
+++ b/lib/cgi/html.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
class CGI
# Base module for HTML-generation mixins.
#
@@ -26,7 +26,7 @@ class CGI
# - O EMPTY
def nOE_element(element, attributes = {})
attributes={attributes=>nil} if attributes.kind_of?(String)
- s = "<#{element.upcase}"
+ s = "<#{element.upcase}".dup
attributes.each do|name, value|
next unless value
s << " "
@@ -408,7 +408,7 @@ class CGI
end
pretty = attributes.delete("PRETTY")
pretty = " " if true == pretty
- buf = ""
+ buf = "".dup
if attributes.has_key?("DOCTYPE")
if attributes["DOCTYPE"]
diff --git a/lib/cgi/session.rb b/lib/cgi/session.rb
index d44a5f84b0..5afc7e69aa 100644
--- a/lib/cgi/session.rb
+++ b/lib/cgi/session.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# cgi/session.rb - session support for cgi scripts
#
@@ -406,8 +406,8 @@ class CGI
@hash[CGI::unescape(k)] = Marshal.restore(CGI::unescape(v))
end
ensure
- f.close unless f.nil?
- lockf.close if lockf
+ f&.close
+ lockf&.close
end
end
@hash
@@ -426,8 +426,8 @@ class CGI
f.close
File.rename @path+".new", @path
ensure
- f.close if f and !f.closed?
- lockf.close if lockf
+ f&.close
+ lockf&.close
end
end
diff --git a/lib/cgi/session/pstore.rb b/lib/cgi/session/pstore.rb
index 2dfb72bdce..5a6e25d137 100644
--- a/lib/cgi/session/pstore.rb
+++ b/lib/cgi/session/pstore.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# cgi/session/pstore.rb - persistent storage of marshalled session data
#
@@ -10,7 +10,7 @@
# persistent of session data on top of the pstore library. See
# cgi/session.rb for more details on session storage managers.
-require 'cgi/session'
+require_relative '../session'
require 'pstore'
class CGI
diff --git a/lib/cgi/util.rb b/lib/cgi/util.rb
index 66fa54d8e9..464115262f 100644
--- a/lib/cgi/util.rb
+++ b/lib/cgi/util.rb
@@ -1,5 +1,9 @@
-# frozen_string_literal: false
-class CGI; module Util; end; extend Util; end
+# frozen_string_literal: true
+class CGI
+ module Util; end
+ include Util
+ extend Util
+end
module CGI::Util
@@accept_charset="UTF-8" unless defined?(@@accept_charset)
# URL-encode a string.
@@ -7,7 +11,7 @@ module CGI::Util
# # => "%27Stop%21%27+said+Fred"
def escape(string)
encoding = string.encoding
- string.b.gsub(/([^ a-zA-Z0-9_.-]+)/) do |m|
+ string.b.gsub(/([^ a-zA-Z0-9_.\-~]+)/) do |m|
'%' + m.unpack('H2' * m.bytesize).join('%').upcase
end.tr(' ', '+').force_encoding(encoding)
end
diff --git a/lib/cmath.gemspec b/lib/cmath.gemspec
new file mode 100644
index 0000000000..953cfe04c4
--- /dev/null
+++ b/lib/cmath.gemspec
@@ -0,0 +1,23 @@
+# coding: utf-8
+# frozen_string_literal: true
+
+Gem::Specification.new do |spec|
+ spec.name = "cmath"
+ spec.version = "1.0.0"
+ spec.authors = ["Tadayoshi Funaba"]
+ spec.email = [nil]
+
+ spec.summary = "Provides Trigonometric and Transcendental functions for complex numbers"
+ spec.description = "CMath is a library that provides trigonometric and transcendental functions for complex numbers. The functions in this module accept integers, floating-point numbers or complex numbers as arguments."
+ spec.homepage = "https://github.com/ruby/cmath"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = "lib/cmath.rb"
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+ spec.required_ruby_version = ">= 2.3.0"
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+end
diff --git a/lib/cmath.rb b/lib/cmath.rb
index 41ab06e77c..7dbd65e799 100644
--- a/lib/cmath.rb
+++ b/lib/cmath.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# = Trigonometric and transcendental functions for complex numbers.
#
@@ -50,7 +50,7 @@ module CMath
atanh
].each do |meth|
define_method(meth + '!') do |*args, &block|
- warn("CMath##{meth}! is deprecated; use CMath##{meth} or Math##{meth}") if $VERBOSE
+ warn("CMath##{meth}! is deprecated; use CMath##{meth} or Math##{meth}", uplevel: 1) if $VERBOSE
RealMath.send(meth, *args, &block)
end
end
diff --git a/lib/csv.rb b/lib/csv.rb
index 03b87983e6..1239554ad6 100644
--- a/lib/csv.rb
+++ b/lib/csv.rb
@@ -2,9 +2,7 @@
# frozen_string_literal: true
# = csv.rb -- CSV Reading and Writing
#
-# Created by James Edward Gray II on 2005-10-31.
-# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
-# under the terms of Ruby's license.
+# Created by James Edward Gray II on 2005-10-31.
#
# See CSV for documentation.
#
@@ -96,73 +94,131 @@ require "English"
require "date"
require "stringio"
+require_relative "csv/fields_converter"
+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)
+
#
# This class provides a complete interface to CSV files and data. It offers
# tools to enable you to read and write to and from Strings or IO objects, as
# needed.
#
-# == Reading
-#
-# === From a File
+# The most generic interface of the library is:
#
-# ==== A Line at a Time
+# csv = CSV.new(string_or_io, **options)
#
-# CSV.foreach("path/to/file.csv") do |row|
-# # use row here...
-# end
+# # Reading: IO object should be open for read
+# csv.read # => array of rows
+# # or
+# csv.each do |row|
+# # ...
+# end
+# # or
+# row = csv.shift
#
-# ==== All at Once
+# # Writing: IO object should be open for write
+# csv << row
#
-# arr_of_arrs = CSV.read("path/to/file.csv")
+# There are several specialized class methods for one-statement reading or writing,
+# described in the Specialized Methods section.
#
-# === From a String
+# If a String is passed into ::new, it is internally wrapped into a StringIO object.
#
-# ==== A Line at a Time
+# +options+ can be used for specifying the particular CSV flavor (column
+# separators, row separators, value quoting and so on), and for data conversion,
+# see Data Conversion section for the description of the latter.
#
-# CSV.parse("CSV,data,String") do |row|
-# # use row here...
-# end
+# == Specialized Methods
#
-# ==== All at Once
+# === Reading
#
-# arr_of_arrs = CSV.parse("CSV,data,String")
+# # From a file: all at once
+# arr_of_rows = CSV.read("path/to/file.csv", **options)
+# # iterator-style:
+# CSV.foreach("path/to/file.csv", **options) do |row|
+# # ...
+# end
#
-# == Writing
+# # From a string
+# arr_of_rows = CSV.parse("CSV,data,String", **options)
+# # or
+# CSV.parse("CSV,data,String", **options) do |row|
+# # ...
+# end
#
-# === To a File
+# === Writing
#
+# # To a file
# CSV.open("path/to/file.csv", "wb") do |csv|
# csv << ["row", "of", "CSV", "data"]
# csv << ["another", "row"]
# # ...
# end
#
-# === To a String
-#
+# # To a String
# csv_string = CSV.generate do |csv|
# csv << ["row", "of", "CSV", "data"]
# csv << ["another", "row"]
# # ...
# end
#
-# == Convert a Single Line
+# === Shortcuts
#
+# # Core extensions for converting one line
# csv_string = ["CSV", "data"].to_csv # to CSV
# csv_array = "CSV,String".parse_csv # from CSV
#
-# == Shortcut Interface
-#
+# # CSV() method
# CSV { |csv_out| csv_out << %w{my data here} } # to $stdout
# CSV(csv = "") { |csv_str| csv_str << %w{my data here} } # to a String
# CSV($stderr) { |csv_err| csv_err << %w{my data here} } # to $stderr
# CSV($stdin) { |csv_in| csv_in.each { |row| p row } } # from $stdin
#
-# == Advanced Usage
+# == Data Conversion
#
-# === Wrap an IO Object
+# === CSV with headers
#
-# csv = CSV.new(io, options)
-# # ... read (with gets() or each()) from and write (with <<) to csv here ...
+# CSV allows to specify column names of CSV file, whether they are in data, or
+# provided separately. If headers specified, reading methods return an instance
+# of CSV::Table, consisting of CSV::Row.
+#
+# # Headers are part of data
+# data = CSV.parse(<<~ROWS, headers: true)
+# Name,Department,Salary
+# Bob,Engineering,1000
+# Jane,Sales,2000
+# John,Management,5000
+# ROWS
+#
+# data.class #=> CSV::Table
+# data.first #=> #<CSV::Row "Name":"Bob" "Department":"Engineering" "Salary":"1000">
+# data.first.to_h #=> {"Name"=>"Bob", "Department"=>"Engineering", "Salary"=>"1000"}
+#
+# # Headers provided by developer
+# data = CSV.parse('Bob,Engeneering,1000', headers: %i[name department salary])
+# data.first #=> #<CSV::Row name:"Bob" department:"Engineering" salary:"1000">
+#
+# === Typed data reading
+#
+# CSV allows to provide a set of data _converters_ e.g. transformations to try on input
+# data. Converter could be a symbol from CSV::Converters constant's keys, or lambda.
+#
+# # Without any converters:
+# CSV.parse('Bob,2018-03-01,100')
+# #=> [["Bob", "2018-03-01", "100"]]
+#
+# # With built-in converters:
+# CSV.parse('Bob,2018-03-01,100', converters: %i[numeric date])
+# #=> [["Bob", #<Date: 2018-03-01>, 100]]
+#
+# # With custom converters:
+# CSV.parse('Bob,2018-03-01,100', converters: [->(v) { Time.parse(v) rescue v }])
+# #=> [["Bob", 2018-03-01 00:00:00 +0200, "100"]]
#
# == CSV and Character Encodings (M17n or Multilingualization)
#
@@ -207,715 +263,17 @@ require "stringio"
# find with it.
#
class CSV
- # The version of the installed library.
- VERSION = "2.4.8"
-
- #
- # 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.
- #
- # All rows returned by CSV will be constructed from this class, if header row
- # processing is activated.
- #
- class Row
- #
- # Construct 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 is assumes to be a field row.
- #
- # A CSV::Row object supports the following Array methods through delegation:
- #
- # * empty?()
- # * length()
- # * size()
- #
- def initialize(headers, fields, header_row = false)
- @header_row = header_row
- headers.each { |h| h.freeze if h.is_a? String }
-
- # handle extra headers or fields
- @row = if headers.size >= fields.size
- headers.zip(fields)
- else
- fields.zip(headers).map { |pair| pair.reverse! }
- end
- end
-
- # Internal data format used to compare equality.
- attr_reader :row
- protected :row
-
- ### Array Delegation ###
-
- extend Forwardable
- def_delegators :@row, :empty?, :length, :size
-
- # Returns +true+ if this is a header row.
- def header_row?
- @header_row
- end
-
- # Returns +true+ if this is a field row.
- def field_row?
- not header_row?
- end
-
- # Returns the headers of this row.
- def headers
- @row.map { |pair| pair.first }
- end
-
- #
- # :call-seq:
- # field( header )
- # field( header, offset )
- # field( index )
- #
- # This method will return the field value by +header+ or +index+. If a field
- # is not found, +nil+ is returned.
- #
- # When provided, +offset+ ensures that a header match occurs on or later
- # than the +offset+ index. You can use this to find duplicate headers,
- # without resorting to hard-coding exact indices.
- #
- def field(header_or_index, minimum_index = 0)
- # locate the pair
- finder = (header_or_index.is_a?(Integer) || header_or_index.is_a?(Range)) ? :[] : :assoc
- pair = @row[minimum_index..-1].send(finder, header_or_index)
-
- # return the field if we have a pair
- if pair.nil?
- nil
- else
- header_or_index.is_a?(Range) ? pair.map(&:last) : pair.last
- end
- end
- alias_method :[], :field
-
- #
- # :call-seq:
- # fetch( header )
- # fetch( header ) { |row| ... }
- # fetch( header, default )
- #
- # This method will fetch the field value by +header+. It has the same
- # behavior as Hash#fetch: if there is a field with the given +header+, its
- # value is returned. Otherwise, if a block is given, it is yielded the
- # +header+ and its result is returned; if a +default+ is given as the
- # second argument, it is returned; otherwise a KeyError is raised.
- #
- def fetch(header, *varargs)
- raise ArgumentError, "Too many arguments" if varargs.length > 1
- pair = @row.assoc(header)
- if pair
- pair.last
- else
- if block_given?
- yield header
- elsif varargs.empty?
- raise KeyError, "key not found: #{header}"
- else
- varargs.first
- end
- end
- end
-
- # Returns +true+ if there is a field with the given +header+.
- def has_key?(header)
- !!@row.assoc(header)
- end
- alias_method :include?, :has_key?
- alias_method :key?, :has_key?
- alias_method :member?, :has_key?
-
- #
- # :call-seq:
- # []=( header, value )
- # []=( header, offset, value )
- # []=( index, value )
- #
- # Looks up the field by the semantics described in CSV::Row.field() and
- # assigns the +value+.
- #
- # Assigning past the end of the row with an index will set all pairs between
- # to <tt>[nil, nil]</tt>. Assigning to an unused header appends the new
- # pair.
- #
- def []=(*args)
- value = args.pop
-
- if args.first.is_a? Integer
- if @row[args.first].nil? # extending past the end with index
- @row[args.first] = [nil, value]
- @row.map! { |pair| pair.nil? ? [nil, nil] : pair }
- else # normal index assignment
- @row[args.first][1] = value
- end
- else
- index = index(*args)
- if index.nil? # appending a field
- self << [args.first, value]
- else # normal header assignment
- @row[index][1] = value
- end
- end
- end
-
- #
- # :call-seq:
- # <<( field )
- # <<( header_and_field_array )
- # <<( header_and_field_hash )
- #
- # If a two-element Array is provided, it is assumed to be a header and field
- # and the pair is appended. A Hash works the same way with the key being
- # the header and the value being the field. Anything else is assumed to be
- # a lone field which is appended with a +nil+ header.
- #
- # This method returns the row for chaining.
- #
- def <<(arg)
- if arg.is_a?(Array) and arg.size == 2 # appending a header and name
- @row << arg
- elsif arg.is_a?(Hash) # append header and name pairs
- arg.each { |pair| @row << pair }
- else # append field value
- @row << [nil, arg]
- end
-
- self # for chaining
- end
-
- #
- # A shortcut for appending multiple fields. Equivalent to:
- #
- # args.each { |arg| csv_row << arg }
- #
- # This method returns the row for chaining.
- #
- def push(*args)
- args.each { |arg| self << arg }
-
- self # for chaining
- end
-
- #
- # :call-seq:
- # delete( header )
- # delete( header, offset )
- # delete( index )
- #
- # Used to remove a pair from the row by +header+ or +index+. The pair is
- # located as described in CSV::Row.field(). The deleted pair is returned,
- # or +nil+ if a pair could not be found.
- #
- def delete(header_or_index, minimum_index = 0)
- if header_or_index.is_a? Integer # by index
- @row.delete_at(header_or_index)
- elsif i = index(header_or_index, minimum_index) # by header
- @row.delete_at(i)
- else
- [ ]
- end
- end
-
- #
- # The provided +block+ is passed a header and field for each pair in the row
- # and expected to return +true+ or +false+, depending on whether the pair
- # should be deleted.
- #
- # This method returns the row for chaining.
- #
- # If no block is given, an Enumerator is returned.
- #
- def delete_if(&block)
- block or return enum_for(__method__) { size }
-
- @row.delete_if(&block)
-
- self # for chaining
- end
-
- #
- # This method accepts any number of arguments which can be headers, indices,
- # Ranges of either, or two-element Arrays containing a header and offset.
- # Each argument will be replaced with a field lookup as described in
- # CSV::Row.field().
- #
- # If called with no arguments, all fields are returned.
- #
- def fields(*headers_and_or_indices)
- if headers_and_or_indices.empty? # return all fields--no arguments
- @row.map { |pair| pair.last }
- else # or work like values_at()
- headers_and_or_indices.inject(Array.new) do |all, h_or_i|
- all + if h_or_i.is_a? Range
- index_begin = h_or_i.begin.is_a?(Integer) ? h_or_i.begin :
- index(h_or_i.begin)
- index_end = h_or_i.end.is_a?(Integer) ? h_or_i.end :
- index(h_or_i.end)
- new_range = h_or_i.exclude_end? ? (index_begin...index_end) :
- (index_begin..index_end)
- fields.values_at(new_range)
- else
- [field(*Array(h_or_i))]
- end
- end
- end
- end
- alias_method :values_at, :fields
-
- #
- # :call-seq:
- # index( header )
- # index( header, offset )
- #
- # 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().
- #
- def index(header, minimum_index = 0)
- # find the pair
- index = headers[minimum_index..-1].index(header)
- # return the index at the right offset, if we found one
- index.nil? ? nil : index + minimum_index
- end
-
- # Returns +true+ if +name+ is a header for this row, and +false+ otherwise.
- def header?(name)
- headers.include? name
- end
- alias_method :include?, :header?
-
- #
- # Returns +true+ if +data+ matches a field in this row, and +false+
- # otherwise.
- #
- def field?(data)
- fields.include? data
- end
-
- include Enumerable
-
- #
- # 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.
- #
- def each(&block)
- block or return enum_for(__method__) { size }
-
- @row.each(&block)
-
- self # for chaining
- end
-
- #
- # Returns +true+ if this row contains the same headers and fields in the
- # same order as +other+.
- #
- def ==(other)
- return @row == other.row if other.is_a? CSV::Row
- @row == other
- end
-
- #
- # Collapses the row into a simple Hash. Be warned that this discards field
- # order and clobbers duplicate fields.
- #
- def to_hash
- # flatten just one level of the internal Array
- Hash[*@row.inject(Array.new) { |ary, pair| ary.push(*pair) }]
- end
-
- #
- # Returns the row as a CSV String. Headers are not used. Equivalent to:
- #
- # csv_row.fields.to_csv( options )
- #
- def to_csv(options = Hash.new)
- fields.to_csv(options)
- end
- alias_method :to_s, :to_csv
-
- # A summary of fields, by header, in an ASCII compatible String.
- def inspect
- str = ["#<", self.class.to_s]
- each do |header, field|
- str << " " << (header.is_a?(Symbol) ? header.to_s : header.inspect) <<
- ":" << field.inspect
- end
- str << ">"
- begin
- str.join('')
- rescue # any encoding error
- str.map do |s|
- e = Encoding::Converter.asciicompat_encoding(s.encoding)
- e ? s.encode(e) : s.force_encoding("ASCII-8BIT")
- end.join('')
- end
- end
- end
-
- #
- # 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.
- #
- # All tables returned by CSV will be constructed from this class, if header
- # row processing is activated.
- #
- class Table
- #
- # Construct 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.
- #
- # A CSV::Table object supports the following Array methods through
- # delegation:
- #
- # * empty?()
- # * length()
- # * size()
- #
- def initialize(array_of_rows)
- @table = array_of_rows
- @mode = :col_or_row
- end
- # The current access mode for indexing and iteration.
- attr_reader :mode
-
- # Internal data format used to compare equality.
- attr_reader :table
- protected :table
-
- ### Array Delegation ###
-
- extend Forwardable
- def_delegators :@table, :empty?, :length, :size
-
- #
- # 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.
- #
- # This method returns the duplicate table for chaining. Don't chain
- # destructive methods (like []=()) this way though, since you are working
- # with a duplicate.
- #
- def by_col
- self.class.new(@table.dup).by_col!
- end
-
- #
- # 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.
- #
- def by_col!
- @mode = :col
-
- self
- end
-
- #
- # 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.
- #
- # This method returns the duplicate table for chaining. Don't chain
- # destructive methods (like []=()) this way though, since you are working
- # with a duplicate.
- #
- def by_col_or_row
- self.class.new(@table.dup).by_col_or_row!
- end
-
- #
- # 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.
- #
- def by_col_or_row!
- @mode = :col_or_row
-
- self
- end
-
- #
- # 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.
- #
- # This method returns the duplicate table for chaining. Don't chain
- # destructive methods (like []=()) this way though, since you are working
- # with a duplicate.
- #
- def by_row
- self.class.new(@table.dup).by_row!
- end
-
- #
- # 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.
- #
- def by_row!
- @mode = :row
-
- self
- end
-
- #
- # Returns the headers for the first row of this table (assumed to match all
- # other rows). An empty Array is returned for empty tables.
- #
- def headers
- if @table.empty?
- Array.new
- else
- @table.first.headers
- end
- end
-
- #
- # In the default mixed mode, this method returns rows for index access and
- # columns for header access. You can force the index association by first
- # calling by_col!() or by_row!().
- #
- # Columns are returned as an Array of values. Altering that Array has no
- # effect on the table.
- #
- def [](index_or_header)
- if @mode == :row or # by index
- (@mode == :col_or_row and (index_or_header.is_a?(Integer) or index_or_header.is_a?(Range)))
- @table[index_or_header]
- else # by header
- @table.map { |row| row[index_or_header] }
- end
- end
-
- #
- # 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!().
- #
- # Rows may be set to an Array of values (which will inherit the table's
- # headers()) or a CSV::Row.
- #
- # 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+.
- #
- # Assigning to an existing column or row clobbers the data. Assigning to
- # new columns creates them at the right end of the table.
- #
- def []=(index_or_header, value)
- if @mode == :row or # by index
- (@mode == :col_or_row and index_or_header.is_a? Integer)
- if value.is_a? Array
- @table[index_or_header] = Row.new(headers, value)
- else
- @table[index_or_header] = value
- end
- else # set column
- if value.is_a? Array # multiple values
- @table.each_with_index do |row, i|
- if row.header_row?
- row[index_or_header] = index_or_header
- else
- row[index_or_header] = value[i]
- end
- end
- else # repeated value
- @table.each do |row|
- if row.header_row?
- row[index_or_header] = index_or_header
- else
- row[index_or_header] = value
- end
- end
- end
- end
- end
-
- #
- # The mixed mode default is to treat a list of indices as row access,
- # returning the rows indicated. Anything else is considered columnar
- # access. For columnar access, the return set has an Array for each row
- # with the values indicated by the headers in each Array. You can force
- # column or row mode using by_col!() or by_row!().
- #
- # You cannot mix column and row access.
- #
- def values_at(*indices_or_headers)
- if @mode == :row or # by indices
- ( @mode == :col_or_row and indices_or_headers.all? do |index|
- index.is_a?(Integer) or
- ( index.is_a?(Range) and
- index.first.is_a?(Integer) and
- index.last.is_a?(Integer) )
- end )
- @table.values_at(*indices_or_headers)
- else # by headers
- @table.map { |row| row.values_at(*indices_or_headers) }
- end
- end
-
- #
- # Adds a new row to the bottom end of this table. You can provide an Array,
- # which will be converted to a CSV::Row (inheriting the table's headers()),
- # or a CSV::Row.
- #
- # This method returns the table for chaining.
- #
- def <<(row_or_array)
- if row_or_array.is_a? Array # append Array
- @table << Row.new(headers, row_or_array)
- else # append Row
- @table << row_or_array
- end
-
- self # for chaining
- end
-
- #
- # A shortcut for appending multiple rows. Equivalent to:
- #
- # rows.each { |row| self << row }
- #
- # This method returns the table for chaining.
- #
- def push(*rows)
- rows.each { |row| self << row }
-
- self # for chaining
- end
-
- #
- # Removes and returns the indicated column or row. In the default mixed
- # mode indices refer to rows and everything else is assumed to be a column
- # header. Use by_col!() or by_row!() to force the lookup.
- #
- def delete(index_or_header)
- if @mode == :row or # by index
- (@mode == :col_or_row and index_or_header.is_a? Integer)
- @table.delete_at(index_or_header)
- else # by header
- @table.map { |row| row.delete(index_or_header).last }
- end
- end
-
- #
- # Removes any column or row for which the block returns +true+. In the
- # default mixed mode or row mode, iteration is the standard row major
- # walking of rows. In column mode, iteration will +yield+ two element
- # tuples containing the column name and an Array of values for that column.
- #
- # This method returns the table for chaining.
- #
- # If no block is given, an Enumerator is returned.
- #
- def delete_if(&block)
- block or return enum_for(__method__) { @mode == :row or @mode == :col_or_row ? size : headers.size }
-
- if @mode == :row or @mode == :col_or_row # by index
- @table.delete_if(&block)
- else # by header
- to_delete = Array.new
- headers.each_with_index do |header, i|
- to_delete << header if block[[header, self[header]]]
- end
- to_delete.map { |header| delete(header) }
- end
-
- self # for chaining
- end
-
- include Enumerable
-
- #
- # In the default mixed mode or row mode, iteration is the standard row major
- # walking of rows. In column mode, iteration will +yield+ two element
- # tuples containing the column name and an Array of values for that column.
- #
- # This method returns the table for chaining.
- #
- # If no block is given, an Enumerator is returned.
- #
- def each(&block)
- block or return enum_for(__method__) { @mode == :col ? headers.size : size }
-
- if @mode == :col
- headers.each { |header| block[[header, self[header]]] }
- else
- @table.each(&block)
- end
-
- self # for chaining
- end
-
- # Returns +true+ if all rows of this table ==() +other+'s rows.
- def ==(other)
- @table == other.table
- end
-
- #
- # Returns the table as an Array of Arrays. Headers will be the first row,
- # then all of the field rows will follow.
- #
- def to_a
- @table.inject([headers]) do |array, row|
- if row.header_row?
- array
- else
- array + [row.fields]
- end
- end
- end
-
- #
- # Returns the table as a complete CSV String. Headers will be listed first,
- # then all of the field rows.
- #
- # This method assumes you want the Table.headers(), unless you explicitly
- # pass <tt>:write_headers => false</tt>.
- #
- def to_csv(options = Hash.new)
- wh = options.fetch(:write_headers, true)
- @table.inject(wh ? [headers.to_csv(options)] : [ ]) do |rows, row|
- if row.header_row?
- rows
- else
- rows + [row.fields.to_csv(options)]
- end
- end.join('')
- end
- alias_method :to_s, :to_csv
-
- # Shows the mode and size of this table in a US-ASCII String.
- def inspect
- "#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>".encode("US-ASCII")
+ # The error thrown when the parser encounters illegal CSV formatting.
+ class MalformedCSVError < RuntimeError
+ attr_reader :line_number
+ alias_method :lineno, :line_number
+ def initialize(message, line_number)
+ @line_number = line_number
+ super("#{message} in line #{line_number}.")
end
end
- # The error thrown when the parser encounters illegal CSV formatting.
- class MalformedCSVError < RuntimeError; end
-
#
# A FieldInfo Struct contains details about a field's position in the data
# source it was read from. CSV will pass this Struct to some blocks that make
@@ -934,7 +292,11 @@ 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} )\z /x
+ \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2} |
+ # ISO-8601
+ \d{4}-\d{2}-\d{2}
+ (?:T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?(?:[+-]\d{2}(?::\d{2})|Z)?)?)?
+ )\z /x
# The encoding used by all converters.
ConverterEncoding = Encoding.find("UTF-8")
@@ -974,7 +336,7 @@ class CSV
date: lambda { |f|
begin
e = f.encode(ConverterEncoding)
- e =~ DateMatcher ? Date.parse(e) : f
+ e.match?(DateMatcher) ? Date.parse(e) : f
rescue # encoding conversion or date parse errors
f
end
@@ -982,7 +344,7 @@ class CSV
date_time: lambda { |f|
begin
e = f.encode(ConverterEncoding)
- e =~ DateTimeMatcher ? DateTime.parse(e) : f
+ e.match?(DateTimeMatcher) ? DateTime.parse(e) : f
rescue # encoding conversion or date parse errors
f
end
@@ -1014,8 +376,8 @@ class CSV
HeaderConverters = {
downcase: lambda { |h| h.encode(ConverterEncoding).downcase },
symbol: lambda { |h|
- h.encode(ConverterEncoding).downcase.strip.gsub(/\s+/, "_").
- gsub(/\W+/, "").to_sym
+ h.encode(ConverterEncoding).downcase.gsub(/[^\s\w]+/, "").strip.
+ gsub(/\s+/, "_").to_sym
}
}
@@ -1035,6 +397,7 @@ class CSV
# <b><tt>:force_quotes</tt></b>:: +false+
# <b><tt>:skip_lines</tt></b>:: +nil+
# <b><tt>:liberal_parsing</tt></b>:: +false+
+ # <b><tt>:quote_empty</tt></b>:: +true+
#
DEFAULT_OPTIONS = {
col_sep: ",",
@@ -1050,6 +413,7 @@ class CSV
force_quotes: false,
skip_lines: nil,
liberal_parsing: false,
+ quote_empty: true,
}.freeze
#
@@ -1061,14 +425,14 @@ class CSV
# If a block is given, the instance is passed to the block and the return
# value becomes the return value of the block.
#
- def self.instance(data = $stdout, options = Hash.new)
+ def self.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 })
# fetch or create the instance for this signature
@@instances ||= Hash.new
- instance = (@@instances[sig] ||= new(data, options))
+ instance = (@@instances[sig] ||= new(data, options))
if block_given?
yield instance # run block, if given, returning result
@@ -1079,9 +443,9 @@ class CSV
#
# :call-seq:
- # filter( options = Hash.new ) { |row| ... }
- # filter( input, options = Hash.new ) { |row| ... }
- # filter( input, output, options = Hash.new ) { |row| ... }
+ # filter( **options ) { |row| ... }
+ # filter( input, **options ) { |row| ... }
+ # filter( input, output, **options ) { |row| ... }
#
# This method is a convenience for building Unix-like filters for CSV data.
# Each row is yielded to the provided block which can alter it as needed.
@@ -1101,25 +465,23 @@ class CSV
# The <tt>:output_row_sep</tt> +option+ defaults to
# <tt>$INPUT_RECORD_SEPARATOR</tt> (<tt>$/</tt>).
#
- def self.filter(*args)
+ def self.filter(input=nil, output=nil, **options)
# parse options for input, output, or both
in_options, out_options = Hash.new, {row_sep: $INPUT_RECORD_SEPARATOR}
- if args.last.is_a? Hash
- args.pop.each do |key, value|
- case key.to_s
- when /\Ain(?:put)?_(.+)\Z/
- in_options[$1.to_sym] = value
- when /\Aout(?:put)?_(.+)\Z/
- out_options[$1.to_sym] = value
- else
- in_options[key] = value
- out_options[key] = value
- end
+ options.each do |key, value|
+ case key.to_s
+ when /\Ain(?:put)?_(.+)\Z/
+ in_options[$1.to_sym] = value
+ when /\Aout(?:put)?_(.+)\Z/
+ out_options[$1.to_sym] = value
+ else
+ in_options[key] = value
+ out_options[key] = value
end
end
# build input and output wrappers
- input = new(args.shift || ARGF, in_options)
- output = new(args.shift || $stdout, out_options)
+ input = new(input || ARGF, in_options)
+ output = new(output || $stdout, out_options)
# read, yield, write
input.each do |row|
@@ -1142,17 +504,17 @@ class CSV
# <tt>encoding: "UTF-32BE:UTF-8"</tt> would read UTF-32BE data from the file
# but transcode it to UTF-8 before CSV parses it.
#
- def self.foreach(path, options = Hash.new, &block)
- return to_enum(__method__, path, options) unless block
- open(path, options) do |csv|
+ def self.foreach(path, mode="r", **options, &block)
+ return to_enum(__method__, path, mode, options) unless block_given?
+ open(path, mode, options) do |csv|
csv.each(&block)
end
end
#
# :call-seq:
- # generate( str, options = Hash.new ) { |csv| ... }
- # generate( options = Hash.new ) { |csv| ... }
+ # generate( str, **options ) { |csv| ... }
+ # generate( **options ) { |csv| ... }
#
# This method wraps a String you provide, or an empty default String, in a
# CSV object which is passed to the provided block. You can use the block to
@@ -1167,19 +529,17 @@ class CSV
# String to set the base Encoding for the output. CSV needs this hint if you
# plan to output non-ASCII compatible data.
#
- def self.generate(*args)
+ def self.generate(str=nil, **options)
# add a default empty String, if none was given
- if args.first.is_a? String
- io = StringIO.new(args.shift)
- io.seek(0, IO::SEEK_END)
- args.unshift(io)
+ if str
+ str = StringIO.new(str)
+ str.seek(0, IO::SEEK_END)
else
- encoding = args[-1][:encoding] if args.last.is_a?(Hash)
- str = String.new
+ encoding = options[:encoding]
+ str = +""
str.force_encoding(encoding) if encoding
- args.unshift(str)
end
- csv = new(*args) # wrap
+ csv = new(str, options) # wrap
yield csv # yield for appending
csv.string # return final String
end
@@ -1197,24 +557,23 @@ class CSV
# The <tt>:row_sep</tt> +option+ defaults to <tt>$INPUT_RECORD_SEPARATOR</tt>
# (<tt>$/</tt>) when calling this method.
#
- def self.generate_line(row, options = Hash.new)
- options = {row_sep: $INPUT_RECORD_SEPARATOR}.merge(options)
- encoding = options.delete(:encoding)
- str = String.new
- if encoding
- str.force_encoding(encoding)
- elsif field = row.find { |f| not f.nil? }
- str.force_encoding(String(field).encoding)
+ def self.generate_line(row, **options)
+ options = {row_sep: $INPUT_RECORD_SEPARATOR}.merge(options)
+ str = +""
+ if options[:encoding]
+ str.force_encoding(options[:encoding])
+ elsif field = row.find {|f| f.is_a?(String)}
+ str.force_encoding(field.encoding)
end
(new(str, options) << row).string
end
#
# :call-seq:
- # open( filename, mode = "rb", options = Hash.new ) { |faster_csv| ... }
- # open( filename, options = Hash.new ) { |faster_csv| ... }
- # open( filename, mode = "rb", options = Hash.new )
- # open( filename, options = Hash.new )
+ # open( filename, mode = "rb", **options ) { |faster_csv| ... }
+ # open( filename, **options ) { |faster_csv| ... }
+ # open( filename, mode = "rb", **options )
+ # open( filename, **options )
#
# This method opens an IO object, and wraps that with CSV. This is intended
# as the primary interface for writing a CSV file.
@@ -1272,17 +631,16 @@ class CSV
# * truncate()
# * tty?()
#
- def self.open(*args)
- # find the +options+ Hash
- options = if args.last.is_a? Hash then args.pop else Hash.new end
+ def self.open(filename, mode="r", **options)
# wrap a File opened with the remaining +args+ with no newline
# decorator
file_opts = {universal_newline: false}.merge(options)
+
begin
- f = File.open(*args, file_opts)
+ f = File.open(filename, mode, file_opts)
rescue ArgumentError => e
- raise unless /needs binmode/ =~ e.message and args.size == 1
- args << "rb"
+ raise unless /needs binmode/.match?(e.message) and mode == "r"
+ mode = "rb"
file_opts = {encoding: Encoding.default_external}.merge(file_opts)
retry
end
@@ -1307,26 +665,26 @@ class CSV
#
# :call-seq:
- # parse( str, options = Hash.new ) { |row| ... }
- # parse( str, options = Hash.new )
+ # parse( str, **options ) { |row| ... }
+ # parse( str, **options )
#
# This method can be used to easily parse CSV out of a String. You may either
# provide a +block+ which will be called with each row of the String in turn,
# or just use the returned Array of Arrays (when no +block+ is given).
#
- # You pass your +str+ to read from, and an optional +options+ Hash containing
+ # You pass your +str+ to read from, and an optional +options+ containing
# anything CSV::new() understands.
#
def self.parse(*args, &block)
csv = new(*args)
- if block.nil? # slurp contents, if no block is given
- begin
- csv.read
- ensure
- csv.close
- end
- else # or pass each row to a provided block
- csv.each(&block)
+
+ return csv.each(&block) if block_given?
+
+ # slurp contents, if no block is given
+ begin
+ csv.read
+ ensure
+ csv.close
end
end
@@ -1337,7 +695,7 @@ class CSV
#
# The +options+ parameter can be anything CSV::new() understands.
#
- def self.parse_line(line, options = Hash.new)
+ def self.parse_line(line, **options)
new(line, options).shift
end
@@ -1368,7 +726,7 @@ class CSV
# converters: :numeric,
# header_converters: :symbol }.merge(options) )
#
- def self.table(path, options = Hash.new)
+ def self.table(path, **options)
read( path, { headers: true,
converters: :numeric,
header_converters: :symbol }.merge(options) )
@@ -1520,79 +878,133 @@ class CSV
# attempt to parse input not conformant
# with RFC 4180, such as double quotes
# in unquoted fields.
+ # <b><tt>:nil_value</tt></b>:: When set an object, any values of an
+ # empty field are replaced by the set
+ # object, not nil.
+ # <b><tt>:empty_value</tt></b>:: When set an object, any values of a
+ # blank string field is replaced by
+ # the set object.
+ # <b><tt>:quote_empty</tt></b>:: TODO
+ # <b><tt>:write_converters</tt></b>:: TODO
+ # <b><tt>:write_nil_value</tt></b>:: TODO
+ # <b><tt>:write_empty_value</tt></b>:: TODO
+ # <b><tt>:strip</tt></b>:: TODO
#
# See CSV::DEFAULT_OPTIONS for the default settings.
#
# Options cannot be overridden in the instance methods for performance reasons,
# so be sure to set what you want here.
#
- def initialize(data, options = Hash.new)
- if data.nil?
- raise ArgumentError.new("Cannot parse nil as CSV")
- end
-
- # build the options for this read/write
- options = DEFAULT_OPTIONS.merge(options)
+ def initialize(data,
+ col_sep: ",",
+ row_sep: :auto,
+ quote_char: '"',
+ field_size_limit: nil,
+ converters: nil,
+ unconverted_fields: nil,
+ headers: false,
+ return_headers: false,
+ write_headers: nil,
+ header_converters: nil,
+ skip_blanks: false,
+ force_quotes: false,
+ skip_lines: nil,
+ liberal_parsing: false,
+ internal_encoding: nil,
+ external_encoding: nil,
+ encoding: nil,
+ nil_value: nil,
+ empty_value: "",
+ quote_empty: true,
+ write_converters: nil,
+ write_nil_value: nil,
+ write_empty_value: "",
+ strip: false)
+ raise ArgumentError.new("Cannot parse nil as CSV") if data.nil?
# create the IO object we will read from
- @io = data.is_a?(String) ? StringIO.new(data) : data
- # honor the IO encoding if we can, otherwise default to ASCII-8BIT
- @encoding = raw_encoding(nil) ||
- ( if encoding = options.delete(:internal_encoding)
- case encoding
- when Encoding; encoding
- else Encoding.find(encoding)
- end
- end ) ||
- ( case encoding = options.delete(:encoding)
- when Encoding; encoding
- when /\A[^:]+/; Encoding.find($&)
- end ) ||
- Encoding.default_internal || Encoding.default_external
- #
- # prepare for building safe regular expressions in the target encoding,
- # if we can transcode the needed characters
- #
- @re_esc = "\\".encode(@encoding).freeze rescue ""
- @re_chars = /#{%"[-\\]\\[\\.^$?*+{}()|# \r\n\t\f\v]".encode(@encoding)}/
-
- init_separators(options)
- init_parsers(options)
- init_converters(options)
- init_headers(options)
- init_comments(options)
-
- @force_encoding = !!(encoding || options.delete(:encoding))
- options.delete(:internal_encoding)
- options.delete(:external_encoding)
- unless options.empty?
- raise ArgumentError, "Unknown options: #{options.keys.join(', ')}."
- end
+ @io = data.is_a?(String) ? StringIO.new(data) : data
+ @encoding = determine_encoding(encoding, internal_encoding)
+
+ @base_fields_converter_options = {
+ nil_value: nil_value,
+ empty_value: empty_value,
+ }
+ @write_fields_converter_options = {
+ nil_value: write_nil_value,
+ empty_value: write_empty_value,
+ }
+ @initial_converters = converters
+ @initial_header_converters = header_converters
+ @initial_write_converters = write_converters
+
+ @parser_options = {
+ column_separator: col_sep,
+ row_separator: row_sep,
+ quote_character: quote_char,
+ field_size_limit: field_size_limit,
+ unconverted_fields: unconverted_fields,
+ headers: headers,
+ return_headers: return_headers,
+ skip_blanks: skip_blanks,
+ skip_lines: skip_lines,
+ liberal_parsing: liberal_parsing,
+ encoding: @encoding,
+ nil_value: nil_value,
+ empty_value: empty_value,
+ strip: strip,
+ }
+ @parser = nil
+
+ @writer_options = {
+ encoding: @encoding,
+ force_encoding: (not encoding.nil?),
+ force_quotes: force_quotes,
+ headers: headers,
+ write_headers: write_headers,
+ column_separator: col_sep,
+ row_separator: row_sep,
+ quote_character: quote_char,
+ quote_empty: quote_empty,
+ }
- # track our own lineno since IO gets confused about line-ends is CSV fields
- @lineno = 0
+ @writer = nil
+ writer if @writer_options[:write_headers]
end
#
# The encoded <tt>:col_sep</tt> used in parsing and writing. See CSV::new
# for details.
#
- attr_reader :col_sep
+ def col_sep
+ parser.column_separator
+ end
+
#
# The encoded <tt>:row_sep</tt> used in parsing and writing. See CSV::new
# for details.
#
- attr_reader :row_sep
+ def row_sep
+ parser.row_separator
+ end
+
#
# The encoded <tt>:quote_char</tt> used in parsing and writing. See CSV::new
# for details.
#
- attr_reader :quote_char
+ def quote_char
+ parser.quote_character
+ end
+
# The limit for field size, if any. See CSV::new for details.
- attr_reader :field_size_limit
+ def field_size_limit
+ parser.field_size_limit
+ end
# The regex marking a line as a comment. See CSV::new for details
- attr_reader :skip_lines
+ def skip_lines
+ parser.skip_lines
+ end
#
# Returns the current list of converters in effect. See CSV::new for details.
@@ -1600,7 +1012,7 @@ class CSV
# as is.
#
def converters
- @converters.map do |converter|
+ parser_fields_converter.map do |converter|
name = Converters.rassoc(converter)
name ? name.first : converter
end
@@ -1609,42 +1021,68 @@ class CSV
# Returns +true+ if unconverted_fields() to parsed results. See CSV::new
# for details.
#
- def unconverted_fields?() @unconverted_fields end
+ def unconverted_fields?
+ parser.unconverted_fields?
+ end
+
#
# Returns +nil+ if headers will not be used, +true+ if they will but have not
# yet been read, or the actual headers after they have been read. See
# CSV::new for details.
#
def headers
- @headers || true if @use_headers
+ if @writer
+ @writer.headers
+ else
+ parsed_headers = parser.headers
+ return parsed_headers if parsed_headers
+ raw_headers = @parser_options[:headers]
+ raw_headers = nil if raw_headers == false
+ raw_headers
+ end
end
#
# Returns +true+ if headers will be returned as a row of results.
# See CSV::new for details.
#
- def return_headers?() @return_headers end
+ def return_headers?
+ parser.return_headers?
+ end
+
# Returns +true+ if headers are written in output. See CSV::new for details.
- def write_headers?() @write_headers end
+ def write_headers?
+ @writer_options[:write_headers]
+ end
+
#
# Returns the current list of converters in effect for headers. See CSV::new
# for details. Built-in converters will be returned by name, while others
# will be returned as is.
#
def header_converters
- @header_converters.map do |converter|
+ header_fields_converter.map do |converter|
name = HeaderConverters.rassoc(converter)
name ? name.first : converter
end
end
+
#
# Returns +true+ blank lines are skipped by the parser. See CSV::new
# for details.
#
- def skip_blanks?() @skip_blanks end
+ def skip_blanks?
+ parser.skip_blanks?
+ end
+
# Returns +true+ if all output fields are quoted. See CSV::new for details.
- def force_quotes?() @force_quotes end
+ def force_quotes?
+ @writer_options[:force_quotes]
+ end
+
# Returns +true+ if illegal input is handled. See CSV::new for details.
- def liberal_parsing?() @liberal_parsing end
+ def liberal_parsing?
+ parser.liberal_parsing?
+ end
#
# The Encoding CSV is parsing or writing in. This will be the Encoding you
@@ -1653,26 +1091,85 @@ class CSV
attr_reader :encoding
#
- # The line number of the last row read from this file. Fields with nested
+ # The line number of the last row read from this file. Fields with nested
# line-end characters will not affect this count.
#
- attr_reader :lineno
+ def lineno
+ if @writer
+ @writer.lineno
+ else
+ parser.lineno
+ end
+ end
+
+ #
+ # The last row read from this file.
+ #
+ def line
+ parser.line
+ end
### IO and StringIO Delegation ###
extend Forwardable
- def_delegators :@io, :binmode, :binmode?, :close, :close_read, :close_write,
- :closed?, :eof, :eof?, :external_encoding, :fcntl,
- :fileno, :flock, :flush, :fsync, :internal_encoding,
- :ioctl, :isatty, :path, :pid, :pos, :pos=, :reopen,
- :seek, :stat, :string, :sync, :sync=, :tell, :to_i,
- :to_io, :truncate, :tty?
+ def_delegators :@io, :binmode, :close, :close_read, :close_write,
+ :closed?, :external_encoding, :fcntl,
+ :fileno, :flush, :fsync, :internal_encoding,
+ :isatty, :pid, :pos, :pos=, :reopen,
+ :seek, :string, :sync, :sync=, :tell,
+ :truncate, :tty?
+
+ def binmode?
+ if @io.respond_to?(:binmode?)
+ @io.binmode?
+ else
+ false
+ end
+ end
+
+ def flock(*args)
+ raise NotImplementedError unless @io.respond_to?(:flock)
+ @io.flock(*args)
+ end
+
+ def ioctl(*args)
+ raise NotImplementedError unless @io.respond_to?(:ioctl)
+ @io.ioctl(*args)
+ end
+
+ def path
+ @io.path if @io.respond_to?(:path)
+ end
+
+ def stat(*args)
+ raise NotImplementedError unless @io.respond_to?(:stat)
+ @io.stat(*args)
+ end
+
+ def to_i
+ raise NotImplementedError unless @io.respond_to?(:to_i)
+ @io.to_i
+ end
+
+ def to_io
+ @io.respond_to?(:to_io) ? @io.to_io : @io
+ end
+
+ def eof?
+ begin
+ parser_enumerator.peek
+ false
+ rescue StopIteration
+ true
+ end
+ end
+ alias_method :eof, :eof?
# Rewinds the underlying IO object and resets CSV's lineno() counter.
def rewind
- @headers = nil
- @lineno = 0
-
+ @parser = nil
+ @parser_enumerator = nil
+ @writer.rewind if @writer
@io.rewind
end
@@ -1686,35 +1183,8 @@ class CSV
# The data source must be open for writing.
#
def <<(row)
- # make sure headers have been assigned
- if header_row? and [Array, String].include? @use_headers.class
- parse_headers # won't read data for Array or String
- self << @headers if @write_headers
- end
-
- # handle CSV::Row objects and Hashes
- row = case row
- when self.class::Row then row.fields
- when Hash then @headers.map { |header| row[header] }
- else row
- end
-
- @headers = row if header_row?
- @lineno += 1
-
- output = row.map(&@quote).join(@col_sep) + @row_sep # quote and separate
- if @io.is_a?(StringIO) and
- output.encoding != (encoding = raw_encoding)
- if @force_encoding
- output = output.encode(encoding)
- elsif (compatible_encoding = Encoding.compatible?(@io.string, output))
- @io.set_encoding(compatible_encoding)
- @io.seek(0, IO::SEEK_END)
- end
- end
- @io << output
-
- self # for chaining
+ writer << row
+ self
end
alias_method :add_row, :<<
alias_method :puts, :<<
@@ -1735,7 +1205,7 @@ class CSV
# converted field or the field itself.
#
def convert(name = nil, &converter)
- add_converter(:converters, self.class::Converters, name, &converter)
+ parser_fields_converter.add_converter(name, &converter)
end
#
@@ -1750,10 +1220,7 @@ class CSV
# effect.
#
def header_convert(name = nil, &converter)
- add_converter( :header_converters,
- self.class::HeaderConverters,
- name,
- &converter )
+ header_fields_converter.add_converter(name, &converter)
end
include Enumerable
@@ -1765,14 +1232,8 @@ class CSV
#
# The data source must be open for reading.
#
- def each
- if block_given?
- while row = shift
- yield row
- end
- else
- to_enum
- end
+ def each(&block)
+ parser_enumerator.each(&block)
end
#
@@ -1782,8 +1243,8 @@ class CSV
#
def read
rows = to_a
- if @use_headers
- Table.new(rows)
+ if parser.use_headers?
+ Table.new(rows, headers: parser.headers)
else
rows
end
@@ -1792,7 +1253,7 @@ class CSV
# Returns +true+ if the next row read will be a header row.
def header_row?
- @use_headers and @headers.nil?
+ parser.header_row?
end
#
@@ -1803,153 +1264,10 @@ class CSV
# The data source must be open for reading.
#
def shift
- #########################################################################
- ### This method is purposefully kept a bit long as simple conditional ###
- ### checks are faster than numerous (expensive) method calls. ###
- #########################################################################
-
- # handle headers not based on document content
- if header_row? and @return_headers and
- [Array, String].include? @use_headers.class
- if @unconverted_fields
- return add_unconverted_fields(parse_headers, Array.new)
- else
- return parse_headers
- end
- end
-
- #
- # it can take multiple calls to <tt>@io.gets()</tt> to get a full line,
- # because of \r and/or \n characters embedded in quoted fields
- #
- in_extended_col = false
- csv = Array.new
-
- loop do
- # add another read to the line
- unless parse = @io.gets(@row_sep)
- return nil
- end
-
- parse.sub!(@parsers[:line_end], "")
-
- if csv.empty?
- #
- # I believe a blank line should be an <tt>Array.new</tt>, not Ruby 1.8
- # CSV's <tt>[nil]</tt>
- #
- if parse.empty?
- @lineno += 1
- if @skip_blanks
- next
- elsif @unconverted_fields
- return add_unconverted_fields(Array.new, Array.new)
- elsif @use_headers
- return self.class::Row.new(Array.new, Array.new)
- else
- return Array.new
- end
- end
- end
-
- next if @skip_lines and @skip_lines.match parse
-
- parts = parse.split(@col_sep, -1)
- if parts.empty?
- if in_extended_col
- csv[-1] << @col_sep # will be replaced with a @row_sep after the parts.each loop
- else
- csv << nil
- end
- end
-
- # This loop is the hot path of csv parsing. Some things may be non-dry
- # for a reason. Make sure to benchmark when refactoring.
- parts.each do |part|
- if in_extended_col
- # If we are continuing a previous column
- if part[-1] == @quote_char && part.count(@quote_char) % 2 != 0
- # extended column ends
- csv[-1] = csv[-1].push(part[0..-2]).join("")
- if csv.last =~ @parsers[:stray_quote]
- raise MalformedCSVError,
- "Missing or stray quote in line #{lineno + 1}"
- end
- csv.last.gsub!(@quote_char * 2, @quote_char)
- in_extended_col = false
- else
- csv.last.push(part, @col_sep)
- end
- elsif part[0] == @quote_char
- # If we are starting a new quoted column
- if part.count(@quote_char) % 2 != 0
- # start an extended column
- csv << [part[1..-1], @col_sep]
- in_extended_col = true
- elsif part[-1] == @quote_char
- # regular quoted column
- csv << part[1..-2]
- if csv.last =~ @parsers[:stray_quote]
- raise MalformedCSVError,
- "Missing or stray quote in line #{lineno + 1}"
- end
- csv.last.gsub!(@quote_char * 2, @quote_char)
- elsif @liberal_parsing
- csv << part
- else
- raise MalformedCSVError,
- "Missing or stray quote in line #{lineno + 1}"
- end
- elsif part =~ @parsers[:quote_or_nl]
- # Unquoted field with bad characters.
- if part =~ @parsers[:nl_or_lf]
- raise MalformedCSVError, "Unquoted fields do not allow " +
- "\\r or \\n (line #{lineno + 1})."
- else
- if @liberal_parsing
- csv << part
- else
- raise MalformedCSVError, "Illegal quoting in line #{lineno + 1}."
- end
- end
- else
- # Regular ole unquoted field.
- csv << (part.empty? ? nil : part)
- end
- end
-
- # Replace tacked on @col_sep with @row_sep if we are still in an extended
- # column.
- csv[-1][-1] = @row_sep if in_extended_col
-
- if in_extended_col
- # if we're at eof?(), a quoted field wasn't closed...
- if @io.eof?
- raise MalformedCSVError,
- "Unclosed quoted field on line #{lineno + 1}."
- elsif @field_size_limit and csv.last.size >= @field_size_limit
- raise MalformedCSVError, "Field size exceeded on line #{lineno + 1}."
- end
- # otherwise, we need to loop and pull some more data to complete the row
- else
- @lineno += 1
-
- # save fields unconverted fields, if needed...
- unconverted = csv.dup if @unconverted_fields
-
- # convert fields, if needed...
- csv = convert_fields(csv) unless @use_headers or @converters.empty?
- # parse out header rows and handle CSV::Row conversions...
- csv = parse_headers(csv) if @use_headers
-
- # inject unconverted fields and accessor, if requested...
- if @unconverted_fields and not csv.respond_to? :unconverted_fields
- add_unconverted_fields(csv, unconverted)
- end
-
- # return the results
- break csv
- end
+ begin
+ parser_enumerator.next
+ rescue StopIteration
+ nil
end
end
alias_method :gets, :shift
@@ -1974,15 +1292,18 @@ class CSV
# show encoding
str << " encoding:" << @encoding.name
# show other attributes
- %w[ lineno col_sep row_sep
- quote_char skip_blanks liberal_parsing ].each do |attr_name|
- if a = instance_variable_get("@#{attr_name}")
+ ["lineno", "col_sep", "row_sep", "quote_char"].each do |attr_name|
+ if a = __send__(attr_name)
str << " " << attr_name << ":" << a.inspect
end
end
- if @use_headers
- str << " headers:" << headers.inspect
+ ["skip_blanks", "liberal_parsing"].each do |attr_name|
+ if a = __send__("#{attr_name}?")
+ str << " " << attr_name << ":" << a.inspect
+ end
end
+ _headers = headers
+ str << " headers:" << _headers.inspect if _headers
str << ">"
begin
str.join('')
@@ -1996,221 +1317,32 @@ class CSV
private
- #
- # Stores the indicated separators for later use.
- #
- # If auto-discovery was requested for <tt>@row_sep</tt>, this method will read
- # ahead in the <tt>@io</tt> and try to find one. +ARGF+, +STDIN+, +STDOUT+,
- # +STDERR+ and any stream open for output only with a default
- # <tt>@row_sep</tt> of <tt>$INPUT_RECORD_SEPARATOR</tt> (<tt>$/</tt>).
- #
- # This method also establishes the quoting rules used for CSV output.
- #
- def init_separators(options)
- # store the selected separators
- @col_sep = options.delete(:col_sep).to_s.encode(@encoding)
- @row_sep = options.delete(:row_sep) # encode after resolving :auto
- @quote_char = options.delete(:quote_char).to_s.encode(@encoding)
-
- if @quote_char.length != 1
- raise ArgumentError, ":quote_char has to be a single character String"
- end
-
- #
- # automatically discover row separator when requested
- # (not fully encoding safe)
- #
- if @row_sep == :auto
- if [ARGF, STDIN, STDOUT, STDERR].include?(@io) or
- (defined?(Zlib) and @io.class == Zlib::GzipWriter)
- @row_sep = $INPUT_RECORD_SEPARATOR
- else
- begin
- #
- # remember where we were (pos() will raise an exception if @io is pipe
- # or not opened for reading)
- #
- saved_pos = @io.pos
- while @row_sep == :auto
- #
- # if we run out of data, it's probably a single line
- # (ensure will set default value)
- #
- break unless sample = @io.gets(nil, 1024)
- # extend sample if we're unsure of the line ending
- if sample.end_with? encode_str("\r")
- sample << (@io.gets(nil, 1) || "")
- end
-
- # try to find a standard separator
- if sample =~ encode_re("\r\n?|\n")
- @row_sep = $&
- break
- end
- end
-
- # tricky seek() clone to work around GzipReader's lack of seek()
- @io.rewind
- # reset back to the remembered position
- while saved_pos > 1024 # avoid loading a lot of data into memory
- @io.read(1024)
- saved_pos -= 1024
- end
- @io.read(saved_pos) if saved_pos.nonzero?
- rescue IOError # not opened for reading
- # do nothing: ensure will set default
- rescue NoMethodError # Zlib::GzipWriter doesn't have some IO methods
- # do nothing: ensure will set default
- rescue SystemCallError # pipe
- # do nothing: ensure will set default
- ensure
- #
- # set default if we failed to detect
- # (stream not opened for reading, a pipe, or a single line of data)
- #
- @row_sep = $INPUT_RECORD_SEPARATOR if @row_sep == :auto
- end
- end
- end
- @row_sep = @row_sep.to_s.encode(@encoding)
-
- # establish quoting rules
- @force_quotes = options.delete(:force_quotes)
- do_quote = lambda do |field|
- field = String(field)
- encoded_quote = @quote_char.encode(field.encoding)
- encoded_quote +
- field.gsub(encoded_quote, encoded_quote * 2) +
- encoded_quote
- end
- quotable_chars = encode_str("\r\n", @col_sep, @quote_char)
- @quote = if @force_quotes
- do_quote
- else
- lambda do |field|
- if field.nil? # represent +nil+ fields as empty unquoted fields
- ""
- else
- field = String(field) # Stringify fields
- # represent empty fields as empty quoted fields
- if field.empty? or
- field.count(quotable_chars).nonzero?
- do_quote.call(field)
- else
- field # unquoted field
- end
- end
- end
- end
- end
-
- # Pre-compiles parsers and stores them by name for access during reads.
- def init_parsers(options)
- # store the parser behaviors
- @skip_blanks = options.delete(:skip_blanks)
- @field_size_limit = options.delete(:field_size_limit)
- @liberal_parsing = options.delete(:liberal_parsing)
-
- # prebuild Regexps for faster parsing
- esc_row_sep = escape_re(@row_sep)
- esc_quote = escape_re(@quote_char)
- @parsers = {
- # for detecting parse errors
- quote_or_nl: encode_re("[", esc_quote, "\r\n]"),
- nl_or_lf: encode_re("[\r\n]"),
- stray_quote: encode_re( "[^", esc_quote, "]", esc_quote,
- "[^", esc_quote, "]" ),
- # safer than chomp!()
- line_end: encode_re(esc_row_sep, "\\z"),
- # illegal unquoted characters
- return_newline: encode_str("\r\n")
- }
- end
-
- #
- # Loads any converters requested during construction.
- #
- # If +field_name+ is set <tt>:converters</tt> (the default) field converters
- # are set. When +field_name+ is <tt>:header_converters</tt> header converters
- # are added instead.
- #
- # The <tt>:unconverted_fields</tt> option is also activated for
- # <tt>:converters</tt> calls, if requested.
- #
- def init_converters(options, field_name = :converters)
- if field_name == :converters
- @unconverted_fields = options.delete(:unconverted_fields)
- end
-
- instance_variable_set("@#{field_name}", Array.new)
+ def determine_encoding(encoding, internal_encoding)
+ # honor the IO encoding if we can, otherwise default to ASCII-8BIT
+ io_encoding = raw_encoding
+ return io_encoding if io_encoding
- # find the correct method to add the converters
- convert = method(field_name.to_s.sub(/ers\Z/, ""))
+ return Encoding.find(internal_encoding) if internal_encoding
- # load converters
- unless options[field_name].nil?
- # allow a single converter not wrapped in an Array
- unless options[field_name].is_a? Array
- options[field_name] = [options[field_name]]
- end
- # load each converter...
- options[field_name].each do |converter|
- if converter.is_a? Proc # custom code block
- convert.call(&converter)
- else # by name
- convert.call(converter)
- end
- end
+ if encoding
+ encoding, = encoding.split(":", 2) if encoding.is_a?(String)
+ return Encoding.find(encoding)
end
- options.delete(field_name)
- end
-
- # Stores header row settings and loads header converters, if needed.
- def init_headers(options)
- @use_headers = options.delete(:headers)
- @return_headers = options.delete(:return_headers)
- @write_headers = options.delete(:write_headers)
-
- # headers must be delayed until shift(), in case they need a row of content
- @headers = nil
-
- init_converters(options, :header_converters)
+ Encoding.default_internal || Encoding.default_external
end
- # Stores the pattern of comments to skip from the provided options.
- #
- # The pattern must respond to +.match+, else ArgumentError is raised.
- # Strings are converted to a Regexp.
- #
- # See also CSV.new
- def init_comments(options)
- @skip_lines = options.delete(:skip_lines)
- @skip_lines = Regexp.new(@skip_lines) if @skip_lines.is_a? String
- if @skip_lines and not @skip_lines.respond_to?(:match)
- raise ArgumentError, ":skip_lines has to respond to matches"
- end
- end
- #
- # The actual work method for adding converters, used by both CSV.convert() and
- # CSV.header_convert().
- #
- # This method requires the +var_name+ of the instance variable to place the
- # converters in, the +const+ Hash to lookup named converters in, and the
- # normal parameters of the CSV.convert() and CSV.header_convert() methods.
- #
- def add_converter(var_name, const, name = nil, &converter)
- if name.nil? # custom converter
- instance_variable_get("@#{var_name}") << converter
- else # named converter
- combo = const[name]
- case combo
- when Array # combo converter
- combo.each do |converter_name|
- add_converter(var_name, const, converter_name)
- end
- else # individual named converter
- instance_variable_get("@#{var_name}") << combo
+ def normalize_converters(converters)
+ converters ||= []
+ unless converters.is_a?(Array)
+ converters = [converters]
+ end
+ converters.collect do |converter|
+ case converter
+ when Proc # custom code block
+ [nil, converter]
+ else # by name
+ [converter, nil]
end
end
end
@@ -2223,120 +1355,88 @@ class CSV
# shortcut.
#
def convert_fields(fields, headers = false)
- # see if we are converting headers or fields
- converters = headers ? @header_converters : @converters
-
- fields.map.with_index do |field, index|
- converters.each do |converter|
- break if field.nil?
- field = if converter.arity == 1 # straight field converter
- converter[field]
- else # FieldInfo converter
- header = @use_headers && !headers ? @headers[index] : nil
- converter[field, FieldInfo.new(index, lineno, header)]
- end
- break unless field.is_a? String # short-circuit pipeline for speed
- end
- field # final state of each field, converted or original
+ if headers
+ header_fields_converter.convert(fields, nil, 0)
+ else
+ parser_fields_converter.convert(fields, @headers, lineno)
end
end
#
- # This method is used to turn a finished +row+ into a CSV::Row. Header rows
- # are also dealt with here, either by returning a CSV::Row with identical
- # headers and fields (save that the fields do not go through the converters)
- # or by reading past them to return a field row. Headers are also saved in
- # <tt>@headers</tt> for use in future rows.
- #
- # When +nil+, +row+ is assumed to be a header row not based on an actual row
- # of the stream.
- #
- def parse_headers(row = nil)
- if @headers.nil? # header row
- @headers = case @use_headers # save headers
- # Array of headers
- when Array then @use_headers
- # CSV header String
- when String
- self.class.parse_line( @use_headers,
- col_sep: @col_sep,
- row_sep: @row_sep,
- quote_char: @quote_char )
- # first row is headers
- else row
- end
-
- # prepare converted and unconverted copies
- row = @headers if row.nil?
- @headers = convert_fields(@headers, true)
- @headers.each { |h| h.freeze if h.is_a? String }
-
- if @return_headers # return headers
- return self.class::Row.new(@headers, row, true)
- elsif not [Array, String].include? @use_headers.class # skip to field row
- return shift
- end
+ # Returns the encoding of the internal IO object.
+ #
+ def raw_encoding
+ if @io.respond_to? :internal_encoding
+ @io.internal_encoding || @io.external_encoding
+ elsif @io.respond_to? :encoding
+ @io.encoding
+ else
+ nil
end
+ end
- self.class::Row.new(@headers, convert_fields(row)) # field row
+ def parser_fields_converter
+ @parser_fields_converter ||= build_parser_fields_converter
end
- #
- # This method injects an instance variable <tt>unconverted_fields</tt> into
- # +row+ and an accessor method for +row+ called unconverted_fields(). The
- # variable is set to the contents of +fields+.
- #
- def add_unconverted_fields(row, fields)
- class << row
- attr_reader :unconverted_fields
- end
- row.instance_eval { @unconverted_fields = fields }
- row
+ def build_parser_fields_converter
+ specific_options = {
+ builtin_converters: Converters,
+ }
+ options = @base_fields_converter_options.merge(specific_options)
+ build_fields_converter(@initial_converters, options)
end
- #
- # This method is an encoding safe version of Regexp::escape(). It will escape
- # any characters that would change the meaning of a regular expression in the
- # encoding of +str+. Regular expression characters that cannot be transcoded
- # to the target encoding will be skipped and no escaping will be performed if
- # a backslash cannot be transcoded.
- #
- def escape_re(str)
- str.gsub(@re_chars) {|c| @re_esc + c}
+ def header_fields_converter
+ @header_fields_converter ||= build_header_fields_converter
end
- #
- # Builds a regular expression in <tt>@encoding</tt>. All +chunks+ will be
- # transcoded to that encoding.
- #
- def encode_re(*chunks)
- Regexp.new(encode_str(*chunks))
+ def build_header_fields_converter
+ specific_options = {
+ builtin_converters: HeaderConverters,
+ accept_nil: true,
+ }
+ options = @base_fields_converter_options.merge(specific_options)
+ build_fields_converter(@initial_header_converters, options)
end
- #
- # Builds a String in <tt>@encoding</tt>. All +chunks+ will be transcoded to
- # that encoding.
- #
- def encode_str(*chunks)
- chunks.map { |chunk| chunk.encode(@encoding.name) }.join('')
+ def writer_fields_converter
+ @writer_fields_converter ||= build_writer_fields_converter
end
- private
+ def build_writer_fields_converter
+ build_fields_converter(@initial_write_converters,
+ @write_fields_converter_options)
+ end
- #
- # Returns the encoding of the internal IO object or the +default+ if the
- # encoding cannot be determined.
- #
- def raw_encoding(default = Encoding::ASCII_8BIT)
- if @io.respond_to? :internal_encoding
- @io.internal_encoding || @io.external_encoding
- elsif @io.is_a? StringIO
- @io.string.encoding
- elsif @io.respond_to? :encoding
- @io.encoding
- else
- default
+ def build_fields_converter(initial_converters, options)
+ fields_converter = FieldsConverter.new(options)
+ normalize_converters(initial_converters).each do |name, converter|
+ fields_converter.add_converter(name, &converter)
end
+ fields_converter
+ end
+
+ def parser
+ @parser ||= Parser.new(@io, parser_options)
+ end
+
+ def parser_options
+ @parser_options.merge(header_fields_converter: header_fields_converter,
+ fields_converter: parser_fields_converter)
+ end
+
+ def parser_enumerator
+ @parser_enumerator ||= parser.parse
+ end
+
+ def writer
+ @writer ||= Writer.new(@io, writer_options)
+ end
+
+ def writer_options
+ @writer_options.merge(header_fields_converter: header_fields_converter,
+ fields_converter: writer_fields_converter)
end
end
@@ -2360,22 +1460,6 @@ def CSV(*args, &block)
CSV.instance(*args, &block)
end
-class Array # :nodoc:
- # Equivalent to CSV::generate_line(self, options)
- #
- # ["CSV", "data"].to_csv
- # #=> "CSV,data\n"
- def to_csv(options = Hash.new)
- CSV.generate_line(self, options)
- end
-end
-
-class String # :nodoc:
- # Equivalent to CSV::parse_line(self, options)
- #
- # "CSV,data".parse_csv
- # #=> ["CSV", "data"]
- def parse_csv(options = Hash.new)
- CSV.parse_line(self, options)
- end
-end
+require_relative "csv/version"
+require_relative "csv/core_ext/array"
+require_relative "csv/core_ext/string"
diff --git a/lib/csv/core_ext/array.rb b/lib/csv/core_ext/array.rb
new file mode 100644
index 0000000000..94df7d5c35
--- /dev/null
+++ b/lib/csv/core_ext/array.rb
@@ -0,0 +1,9 @@
+class Array # :nodoc:
+ # Equivalent to CSV::generate_line(self, options)
+ #
+ # ["CSV", "data"].to_csv
+ # #=> "CSV,data\n"
+ def to_csv(**options)
+ CSV.generate_line(self, options)
+ end
+end
diff --git a/lib/csv/core_ext/string.rb b/lib/csv/core_ext/string.rb
new file mode 100644
index 0000000000..8f2070f3bd
--- /dev/null
+++ b/lib/csv/core_ext/string.rb
@@ -0,0 +1,9 @@
+class String # :nodoc:
+ # Equivalent to CSV::parse_line(self, options)
+ #
+ # "CSV,data".parse_csv
+ # #=> ["CSV", "data"]
+ def parse_csv(**options)
+ CSV.parse_line(self, options)
+ end
+end
diff --git a/lib/csv/csv.gemspec b/lib/csv/csv.gemspec
new file mode 100644
index 0000000000..98110bc13c
--- /dev/null
+++ b/lib/csv/csv.gemspec
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+begin
+ require_relative "lib/csv/version"
+rescue LoadError
+ # for Ruby core repository
+ require_relative "version"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "csv"
+ spec.version = CSV::VERSION
+ spec.authors = ["James Edward Gray II", "Kouhei Sutou"]
+ spec.email = [nil, "kou@cozmixng.org"]
+
+ spec.summary = "CSV Reading and Writing"
+ spec.description = "The CSV library provides a complete interface to CSV files and data. It offers tools to enable you to read and write to and from Strings or IO objects, as needed."
+ spec.homepage = "https://github.com/ruby/csv"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [
+ "LICENSE.txt",
+ "NEWS.md",
+ "README.md",
+ "lib/csv.rb",
+ "lib/csv/core_ext/array.rb",
+ "lib/csv/core_ext/string.rb",
+ "lib/csv/delete_suffix.rb",
+ "lib/csv/fields_converter.rb",
+ "lib/csv/match_p.rb",
+ "lib/csv/parser.rb",
+ "lib/csv/row.rb",
+ "lib/csv/table.rb",
+ "lib/csv/version.rb",
+ "lib/csv/writer.rb",
+ ]
+ spec.require_paths = ["lib"]
+ spec.required_ruby_version = ">= 2.3.0"
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+ spec.add_development_dependency "benchmark_driver"
+ spec.add_development_dependency "simplecov"
+end
diff --git a/lib/csv/delete_suffix.rb b/lib/csv/delete_suffix.rb
new file mode 100644
index 0000000000..e0b40c7aab
--- /dev/null
+++ b/lib/csv/delete_suffix.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+# This provides String#delete_suffix? for Ruby 2.4.
+unless String.method_defined?(:delete_suffix)
+ class CSV
+ module DeleteSuffix
+ refine String do
+ def delete_suffix(suffix)
+ if end_with?(suffix)
+ self[0..(-(suffix.size + 1))]
+ else
+ self
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/csv/fields_converter.rb b/lib/csv/fields_converter.rb
new file mode 100644
index 0000000000..c2fa5798ff
--- /dev/null
+++ b/lib/csv/fields_converter.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+class CSV
+ class FieldsConverter
+ include Enumerable
+
+ def initialize(options={})
+ @converters = []
+ @nil_value = options[:nil_value]
+ @empty_value = options[:empty_value]
+ @empty_value_is_empty_string = (@empty_value == "")
+ @accept_nil = options[:accept_nil]
+ @builtin_converters = options[:builtin_converters]
+ @need_static_convert = need_static_convert?
+ end
+
+ def add_converter(name=nil, &converter)
+ if name.nil? # custom converter
+ @converters << converter
+ else # named converter
+ combo = @builtin_converters[name]
+ case combo
+ when Array # combo converter
+ combo.each do |sub_name|
+ add_converter(sub_name)
+ end
+ else # individual named converter
+ @converters << combo
+ end
+ end
+ end
+
+ def each(&block)
+ @converters.each(&block)
+ end
+
+ def empty?
+ @converters.empty?
+ end
+
+ def convert(fields, headers, lineno)
+ return fields unless need_convert?
+
+ fields.collect.with_index do |field, index|
+ if field.nil?
+ field = @nil_value
+ elsif field.empty?
+ field = @empty_value unless @empty_value_is_empty_string
+ end
+ @converters.each do |converter|
+ break if field.nil? and @accept_nil
+ if converter.arity == 1 # straight field converter
+ field = converter[field]
+ else # FieldInfo converter
+ if headers
+ header = headers[index]
+ else
+ header = nil
+ end
+ field = converter[field, FieldInfo.new(index, lineno, header)]
+ end
+ break unless field.is_a?(String) # short-circuit pipeline for speed
+ end
+ field # final state of each field, converted or original
+ end
+ end
+
+ private
+ def need_static_convert?
+ not (@nil_value.nil? and @empty_value_is_empty_string)
+ end
+
+ def need_convert?
+ @need_static_convert or
+ (not @converters.empty?)
+ end
+ end
+end
diff --git a/lib/csv/match_p.rb b/lib/csv/match_p.rb
new file mode 100644
index 0000000000..775559a3eb
--- /dev/null
+++ b/lib/csv/match_p.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+# This provides String#match? and Regexp#match? for Ruby 2.3.
+unless String.method_defined?(:match?)
+ class CSV
+ module MatchP
+ refine String do
+ def match?(pattern)
+ self =~ pattern
+ end
+ end
+
+ refine Regexp do
+ def match?(string)
+ self =~ string
+ end
+ end
+ end
+ end
+end
diff --git a/lib/csv/parser.rb b/lib/csv/parser.rb
new file mode 100644
index 0000000000..85252203e4
--- /dev/null
+++ b/lib/csv/parser.rb
@@ -0,0 +1,1092 @@
+# frozen_string_literal: true
+
+require "strscan"
+
+require_relative "delete_suffix"
+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
+ class Parser
+ class InvalidEncoding < StandardError
+ end
+
+ class Scanner < StringScanner
+ alias_method :scan_all, :scan
+
+ def initialize(*args)
+ super
+ @keeps = []
+ end
+
+ def each_line(row_separator)
+ position = pos
+ rest.each_line(row_separator) do |line|
+ position += line.bytesize
+ self.pos = position
+ yield(line)
+ end
+ end
+
+ def keep_start
+ @keeps.push(pos)
+ end
+
+ def keep_end
+ start = @keeps.pop
+ string[start, pos - start]
+ end
+
+ def keep_back
+ self.pos = @keeps.pop
+ end
+
+ def keep_drop
+ @keeps.pop
+ end
+ end
+
+ class InputsScanner
+ def initialize(inputs, encoding, chunk_size: 8192)
+ @inputs = inputs.dup
+ @encoding = encoding
+ @chunk_size = chunk_size
+ @last_scanner = @inputs.empty?
+ @keeps = []
+ read_chunk
+ end
+
+ def each_line(row_separator)
+ buffer = nil
+ input = @scanner.rest
+ position = @scanner.pos
+ offset = 0
+ n_row_separator_chars = row_separator.size
+ while true
+ input.each_line(row_separator) do |line|
+ @scanner.pos += line.bytesize
+ if buffer
+ if n_row_separator_chars == 2 and
+ buffer.end_with?(row_separator[0]) and
+ line.start_with?(row_separator[1])
+ buffer << line[0]
+ line = line[1..-1]
+ position += buffer.bytesize + offset
+ @scanner.pos = position
+ offset = 0
+ yield(buffer)
+ buffer = nil
+ next if line.empty?
+ else
+ buffer << line
+ line = buffer
+ buffer = nil
+ end
+ end
+ if line.end_with?(row_separator)
+ position += line.bytesize + offset
+ @scanner.pos = position
+ offset = 0
+ yield(line)
+ else
+ buffer = line
+ end
+ end
+ break unless read_chunk
+ input = @scanner.rest
+ position = @scanner.pos
+ offset = -buffer.bytesize if buffer
+ end
+ yield(buffer) if buffer
+ end
+
+ def scan(pattern)
+ value = @scanner.scan(pattern)
+ return value if @last_scanner
+
+ if value
+ read_chunk if @scanner.eos?
+ return value
+ else
+ nil
+ end
+ end
+
+ def scan_all(pattern)
+ value = @scanner.scan(pattern)
+ return value if @last_scanner
+
+ return nil if value.nil?
+ while @scanner.eos? and read_chunk and (sub_value = @scanner.scan(pattern))
+ value << sub_value
+ end
+ value
+ end
+
+ def eos?
+ @scanner.eos?
+ end
+
+ def keep_start
+ @keeps.push([@scanner.pos, nil])
+ end
+
+ def keep_end
+ start, buffer = @keeps.pop
+ keep = @scanner.string[start, @scanner.pos - start]
+ if buffer
+ buffer << keep
+ keep = buffer
+ end
+ keep
+ end
+
+ def keep_back
+ start, buffer = @keeps.pop
+ if buffer
+ string = @scanner.string
+ keep = string.byteslice(start, string.bytesize - start)
+ if keep and not keep.empty?
+ @inputs.unshift(StringIO.new(keep))
+ @last_scanner = false
+ end
+ @scanner = StringScanner.new(buffer)
+ else
+ @scanner.pos = start
+ end
+ read_chunk if @scanner.eos?
+ end
+
+ def keep_drop
+ @keeps.pop
+ end
+
+ def rest
+ @scanner.rest
+ end
+
+ private
+ def read_chunk
+ return false if @last_scanner
+
+ 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
+ end
+ keep[0] = 0
+ end
+
+ input = @inputs.first
+ case input
+ when StringIO
+ string = input.string
+ raise InvalidEncoding unless string.valid_encoding?
+ @scanner = StringScanner.new(string)
+ @inputs.shift
+ @last_scanner = @inputs.empty?
+ true
+ else
+ chunk = input.gets(nil, @chunk_size)
+ if chunk
+ raise InvalidEncoding unless chunk.valid_encoding?
+ @scanner = StringScanner.new(chunk)
+ if input.respond_to?(:eof?) and input.eof?
+ @inputs.shift
+ @last_scanner = @inputs.empty?
+ end
+ true
+ else
+ @scanner = StringScanner.new("".encode(@encoding))
+ @inputs.shift
+ @last_scanner = @inputs.empty?
+ if @last_scanner
+ false
+ else
+ read_chunk
+ end
+ end
+ end
+ end
+ end
+
+ def initialize(input, options)
+ @input = input
+ @options = options
+ @samples = []
+
+ prepare
+ end
+
+ def column_separator
+ @column_separator
+ end
+
+ def row_separator
+ @row_separator
+ end
+
+ def quote_character
+ @quote_character
+ end
+
+ def field_size_limit
+ @field_size_limit
+ end
+
+ def skip_lines
+ @skip_lines
+ end
+
+ def unconverted_fields?
+ @unconverted_fields
+ end
+
+ def headers
+ @headers
+ end
+
+ def header_row?
+ @use_headers and @headers.nil?
+ end
+
+ def return_headers?
+ @return_headers
+ end
+
+ def skip_blanks?
+ @skip_blanks
+ end
+
+ def liberal_parsing?
+ @liberal_parsing
+ end
+
+ def lineno
+ @lineno
+ end
+
+ def line
+ last_line
+ end
+
+ def parse(&block)
+ return to_enum(__method__) unless block_given?
+
+ if @return_headers and @headers and @raw_headers
+ headers = Row.new(@headers, @raw_headers, true)
+ if @unconverted_fields
+ headers = add_unconverted_fields(headers, [])
+ end
+ yield headers
+ end
+
+ begin
+ @scanner ||= build_scanner
+ if quote_character.nil?
+ parse_no_quote(&block)
+ elsif @need_robust_parsing
+ parse_quotable_robust(&block)
+ else
+ parse_quotable_loose(&block)
+ end
+ rescue InvalidEncoding
+ if @scanner
+ ignore_broken_line
+ lineno = @lineno
+ else
+ lineno = @lineno + 1
+ end
+ message = "Invalid byte sequence in #{@encoding}"
+ raise MalformedCSVError.new(message, lineno)
+ end
+ end
+
+ def use_headers?
+ @use_headers
+ end
+
+ private
+ def prepare
+ prepare_variable
+ prepare_quote_character
+ prepare_backslash
+ prepare_skip_lines
+ prepare_strip
+ prepare_separators
+ prepare_quoted
+ prepare_unquoted
+ prepare_line
+ prepare_header
+ prepare_parser
+ end
+
+ def prepare_variable
+ @need_robust_parsing = false
+ @encoding = @options[:encoding]
+ liberal_parsing = @options[:liberal_parsing]
+ if liberal_parsing
+ @liberal_parsing = true
+ if liberal_parsing.is_a?(Hash)
+ @double_quote_outside_quote =
+ liberal_parsing[:double_quote_outside_quote]
+ @backslash_quote = liberal_parsing[:backslash_quote]
+ else
+ @double_quote_outside_quote = false
+ @backslash_quote = false
+ end
+ @need_robust_parsing = true
+ else
+ @liberal_parsing = false
+ @backslash_quote = false
+ end
+ @unconverted_fields = @options[:unconverted_fields]
+ @field_size_limit = @options[:field_size_limit]
+ @skip_blanks = @options[:skip_blanks]
+ @fields_converter = @options[:fields_converter]
+ @header_fields_converter = @options[:header_fields_converter]
+ end
+
+ def prepare_quote_character
+ @quote_character = @options[:quote_character]
+ if @quote_character.nil?
+ @escaped_quote_character = nil
+ @escaped_quote = nil
+ else
+ @quote_character = @quote_character.to_s.encode(@encoding)
+ if @quote_character.length != 1
+ message = ":quote_char has to be nil or a single character String"
+ raise ArgumentError, message
+ end
+ @double_quote_character = @quote_character * 2
+ @escaped_quote_character = Regexp.escape(@quote_character)
+ @escaped_quote = Regexp.new(@escaped_quote_character)
+ end
+ end
+
+ def prepare_backslash
+ return unless @backslash_quote
+
+ @backslash_character = "\\".encode(@encoding)
+
+ @escaped_backslash_character = Regexp.escape(@backslash_character)
+ @escaped_backslash = Regexp.new(@escaped_backslash_character)
+ if @quote_character.nil?
+ @backslash_quote_character = nil
+ else
+ @backslash_quote_character =
+ @backslash_character + @escaped_quote_character
+ end
+ end
+
+ def prepare_skip_lines
+ skip_lines = @options[:skip_lines]
+ case skip_lines
+ when String
+ @skip_lines = skip_lines.encode(@encoding)
+ when Regexp, nil
+ @skip_lines = skip_lines
+ else
+ unless skip_lines.respond_to?(:match)
+ message =
+ ":skip_lines has to respond to \#match: #{skip_lines.inspect}"
+ raise ArgumentError, message
+ end
+ @skip_lines = skip_lines
+ end
+ end
+
+ def prepare_strip
+ @strip = @options[:strip]
+ @escaped_strip = nil
+ @strip_value = nil
+ if @strip.is_a?(String)
+ case @strip.length
+ when 0
+ raise ArgumentError, ":strip must not be an empty String"
+ when 1
+ # ok
+ else
+ raise ArgumentError, ":strip doesn't support 2 or more characters yet"
+ end
+ @strip = @strip.encode(@encoding)
+ @escaped_strip = Regexp.escape(@strip)
+ if @quote_character
+ @strip_value = Regexp.new(@escaped_strip +
+ "+".encode(@encoding))
+ end
+ @need_robust_parsing = true
+ elsif @strip
+ strip_values = " \t\r\n\f\v"
+ @escaped_strip = strip_values.encode(@encoding)
+ if @quote_character
+ @strip_value = Regexp.new("[#{strip_values}]+".encode(@encoding))
+ end
+ @need_robust_parsing = true
+ end
+ end
+
+ begin
+ StringScanner.new("x").scan("x")
+ rescue TypeError
+ @@string_scanner_scan_accept_string = false
+ else
+ @@string_scanner_scan_accept_string = true
+ end
+
+ def prepare_separators
+ @column_separator = @options[:column_separator].to_s.encode(@encoding)
+ @row_separator =
+ resolve_row_separator(@options[:row_separator]).encode(@encoding)
+
+ @escaped_column_separator = Regexp.escape(@column_separator)
+ @escaped_first_column_separator = Regexp.escape(@column_separator[0])
+ if @column_separator.size > 1
+ @column_end = Regexp.new(@escaped_column_separator)
+ @column_ends = @column_separator.each_char.collect do |char|
+ Regexp.new(Regexp.escape(char))
+ end
+ @first_column_separators = Regexp.new(@escaped_first_column_separator +
+ "+".encode(@encoding))
+ else
+ if @@string_scanner_scan_accept_string
+ @column_end = @column_separator
+ else
+ @column_end = Regexp.new(@escaped_column_separator)
+ end
+ @column_ends = nil
+ @first_column_separators = nil
+ end
+
+ escaped_row_separator = Regexp.escape(@row_separator)
+ @row_end = Regexp.new(escaped_row_separator)
+ if @row_separator.size > 1
+ @row_ends = @row_separator.each_char.collect do |char|
+ Regexp.new(Regexp.escape(char))
+ end
+ else
+ @row_ends = nil
+ end
+
+ @cr = "\r".encode(@encoding)
+ @lf = "\n".encode(@encoding)
+ @cr_or_lf = Regexp.new("[\r\n]".encode(@encoding))
+ @not_line_end = Regexp.new("[^\r\n]+".encode(@encoding))
+ end
+
+ def prepare_quoted
+ if @quote_character
+ @quotes = Regexp.new(@escaped_quote_character +
+ "+".encode(@encoding))
+ no_quoted_values = @escaped_quote_character.dup
+ if @backslash_quote
+ no_quoted_values << @escaped_backslash_character
+ end
+ @quoted_value = Regexp.new("[^".encode(@encoding) +
+ no_quoted_values +
+ "]+".encode(@encoding))
+ end
+ if @escaped_strip
+ @split_column_separator = Regexp.new(@escaped_strip +
+ "*".encode(@encoding) +
+ @escaped_column_separator +
+ @escaped_strip +
+ "*".encode(@encoding))
+ else
+ if @column_separator == " ".encode(@encoding)
+ @split_column_separator = Regexp.new(@escaped_column_separator)
+ else
+ @split_column_separator = @column_separator
+ end
+ end
+ end
+
+ def prepare_unquoted
+ return if @quote_character.nil?
+
+ no_unquoted_values = "\r\n".encode(@encoding)
+ no_unquoted_values << @escaped_first_column_separator
+ unless @liberal_parsing
+ no_unquoted_values << @escaped_quote_character
+ end
+ if @escaped_strip
+ no_unquoted_values << @escaped_strip
+ end
+ @unquoted_value = Regexp.new("[^".encode(@encoding) +
+ no_unquoted_values +
+ "]+".encode(@encoding))
+ end
+
+ def resolve_row_separator(separator)
+ if separator == :auto
+ cr = "\r".encode(@encoding)
+ lf = "\n".encode(@encoding)
+ if @input.is_a?(StringIO)
+ separator = detect_row_separator(@input.string, cr, lf)
+ elsif @input.respond_to?(:gets)
+ if @input.is_a?(File)
+ chunk_size = 32 * 1024
+ else
+ chunk_size = 1024
+ end
+ begin
+ while separator == :auto
+ #
+ # if we run out of data, it's probably a single line
+ # (ensure will set default value)
+ #
+ break unless sample = @input.gets(nil, chunk_size)
+
+ # extend sample if we're unsure of the line ending
+ if sample.end_with?(cr)
+ sample << (@input.gets(nil, 1) || "")
+ end
+
+ @samples << sample
+
+ separator = detect_row_separator(sample, cr, lf)
+ end
+ rescue IOError
+ # do nothing: ensure will set default
+ end
+ end
+ separator = $INPUT_RECORD_SEPARATOR if separator == :auto
+ end
+ separator.to_s.encode(@encoding)
+ end
+
+ def detect_row_separator(sample, cr, lf)
+ lf_index = sample.index(lf)
+ if lf_index
+ cr_index = sample[0, lf_index].index(cr)
+ else
+ cr_index = sample.index(cr)
+ end
+ if cr_index and lf_index
+ if cr_index + 1 == lf_index
+ cr + lf
+ elsif cr_index < lf_index
+ cr
+ else
+ lf
+ end
+ elsif cr_index
+ cr
+ elsif lf_index
+ lf
+ else
+ :auto
+ end
+ end
+
+ def prepare_line
+ @lineno = 0
+ @last_line = nil
+ @scanner = nil
+ end
+
+ def last_line
+ if @scanner
+ @last_line ||= @scanner.keep_end
+ else
+ @last_line
+ end
+ end
+
+ def prepare_header
+ @return_headers = @options[:return_headers]
+
+ headers = @options[:headers]
+ case headers
+ when Array
+ @raw_headers = headers
+ @use_headers = true
+ when String
+ @raw_headers = parse_headers(headers)
+ @use_headers = true
+ when nil, false
+ @raw_headers = nil
+ @use_headers = false
+ else
+ @raw_headers = nil
+ @use_headers = true
+ end
+ if @raw_headers
+ @headers = adjust_headers(@raw_headers)
+ 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)
+ end
+
+ def adjust_headers(headers)
+ adjusted_headers = @header_fields_converter.convert(headers, nil, @lineno)
+ adjusted_headers.each {|h| h.freeze if h.is_a? String}
+ adjusted_headers
+ end
+
+ def prepare_parser
+ @may_quoted = may_quoted?
+ end
+
+ def may_quoted?
+ return false if @quote_character.nil?
+
+ if @input.is_a?(StringIO)
+ sample = @input.string
+ else
+ return false if @samples.empty?
+ sample = @samples.first
+ end
+ 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)
+ end
+
+ def gets(*args)
+ @io.gets(*args)
+ end
+
+ def each_line(*args, &block)
+ @io.each_line(*args, &block)
+ end
+
+ def eof?
+ @io.eof?
+ end
+ end
+
+ def build_scanner
+ inputs = @samples.collect do |sample|
+ UnoptimizedStringIO.new(sample)
+ end
+ if @input.is_a?(StringIO)
+ inputs << UnoptimizedStringIO.new(@input.string)
+ else
+ inputs << @input
+ end
+ chunk_size = ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"] || "1"
+ InputsScanner.new(inputs,
+ @encoding,
+ chunk_size: Integer(chunk_size, 10))
+ end
+ else
+ def build_scanner
+ string = nil
+ if @samples.empty? and @input.is_a?(StringIO)
+ string = @input.string
+ elsif @samples.size == 1 and @input.respond_to?(:eof?) and @input.eof?
+ string = @samples[0]
+ end
+ if string
+ unless string.valid_encoding?
+ index = string.lines(@row_separator).index do |line|
+ !line.valid_encoding?
+ end
+ if index
+ message = "Invalid byte sequence in #{@encoding}"
+ raise MalformedCSVError.new(message, @lineno + index + 1)
+ end
+ end
+ Scanner.new(string)
+ else
+ inputs = @samples.collect do |sample|
+ StringIO.new(sample)
+ end
+ inputs << @input
+ InputsScanner.new(inputs, @encoding)
+ end
+ end
+ end
+
+ def skip_needless_lines
+ return unless @skip_lines
+
+ while true
+ @scanner.keep_start
+ line = @scanner.scan_all(@not_line_end) || "".encode(@encoding)
+ line << @row_separator if parse_row_end
+ if skip_line?(line)
+ @lineno += 1
+ @scanner.keep_drop
+ else
+ @scanner.keep_back
+ return
+ end
+ end
+ end
+
+ def skip_line?(line)
+ case @skip_lines
+ when String
+ line.include?(@skip_lines)
+ when Regexp
+ @skip_lines.match?(line)
+ else
+ @skip_lines.match(line)
+ end
+ end
+
+ def parse_no_quote(&block)
+ @scanner.each_line(@row_separator) do |line|
+ next if @skip_lines and skip_line?(line)
+ original_line = line
+ line = line.delete_suffix(@row_separator)
+
+ if line.empty?
+ next if @skip_blanks
+ row = []
+ else
+ line = strip_value(line)
+ row = line.split(@split_column_separator, -1)
+ n_columns = row.size
+ i = 0
+ while i < n_columns
+ row[i] = nil if row[i].empty?
+ i += 1
+ end
+ end
+ @last_line = original_line
+ emit_row(row, &block)
+ end
+ end
+
+ def parse_quotable_loose(&block)
+ @scanner.keep_start
+ @scanner.each_line(@row_separator) do |line|
+ if @skip_lines and skip_line?(line)
+ @scanner.keep_drop
+ @scanner.keep_start
+ next
+ end
+ original_line = line
+ line = line.delete_suffix(@row_separator)
+
+ if line.empty?
+ if @skip_blanks
+ @scanner.keep_drop
+ @scanner.keep_start
+ next
+ end
+ row = []
+ 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)
+ n_columns = row.size
+ i = 0
+ while i < n_columns
+ column = row[i]
+ if column.empty?
+ row[i] = nil
+ else
+ n_quotes = column.count(@quote_character)
+ if n_quotes.zero?
+ # no quote
+ elsif n_quotes == 2 and
+ column.start_with?(@quote_character) and
+ column.end_with?(@quote_character)
+ row[i] = column[1..-2]
+ else
+ @scanner.keep_back
+ @need_robust_parsing = true
+ return parse_quotable_robust(&block)
+ end
+ end
+ i += 1
+ end
+ end
+ @scanner.keep_drop
+ @scanner.keep_start
+ @last_line = original_line
+ emit_row(row, &block)
+ end
+ @scanner.keep_drop
+ end
+
+ def parse_quotable_robust(&block)
+ row = []
+ skip_needless_lines
+ start_row
+ while true
+ @quoted_column_value = false
+ @unquoted_column_value = false
+ @scanner.scan_all(@strip_value) if @strip_value
+ 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
+ end
+ if parse_column_end
+ row << value
+ elsif parse_row_end
+ if row.empty? and value.nil?
+ emit_row([], &block) unless @skip_blanks
+ else
+ row << value
+ emit_row(row, &block)
+ row = []
+ end
+ skip_needless_lines
+ start_row
+ elsif @scanner.eos?
+ break if row.empty? and value.nil?
+ row << value
+ emit_row(row, &block)
+ break
+ else
+ if @quoted_column_value
+ 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(@cr_or_lf))
+ ignore_broken_line
+ message = "Unquoted fields do not allow new line " +
+ "<#{new_line.inspect}>"
+ raise MalformedCSVError.new(message, @lineno)
+ elsif @scanner.rest.start_with?(@quote_character)
+ ignore_broken_line
+ message = "Illegal quoting"
+ raise MalformedCSVError.new(message, @lineno)
+ elsif (new_line = @scanner.scan(@cr_or_lf))
+ ignore_broken_line
+ message = "New line must be <#{@row_separator.inspect}> " +
+ "not <#{new_line.inspect}>"
+ raise MalformedCSVError.new(message, @lineno)
+ else
+ ignore_broken_line
+ raise MalformedCSVError.new("TODO: Meaningful message",
+ @lineno)
+ end
+ end
+ end
+ end
+
+ def parse_column_value
+ if @liberal_parsing
+ quoted_value = parse_quoted_column_value
+ if quoted_value
+ unquoted_value = parse_unquoted_column_value
+ if unquoted_value
+ if @double_quote_outside_quote
+ unquoted_value = unquoted_value.gsub(@quote_character * 2,
+ @quote_character)
+ if quoted_value.empty? # %Q{""...} case
+ return @quote_character + unquoted_value
+ end
+ end
+ @quote_character + quoted_value + @quote_character + unquoted_value
+ else
+ quoted_value
+ end
+ else
+ parse_unquoted_column_value
+ end
+ elsif @may_quoted
+ parse_quoted_column_value ||
+ parse_unquoted_column_value
+ else
+ parse_unquoted_column_value ||
+ parse_quoted_column_value
+ end
+ end
+
+ def parse_unquoted_column_value
+ value = @scanner.scan_all(@unquoted_value)
+ return nil unless value
+
+ @unquoted_column_value = true
+ if @first_column_separators
+ while true
+ @scanner.keep_start
+ is_column_end = @column_ends.all? do |column_end|
+ @scanner.scan(column_end)
+ end
+ @scanner.keep_back
+ break if is_column_end
+ sub_separator = @scanner.scan_all(@first_column_separators)
+ break if sub_separator.nil?
+ value << sub_separator
+ sub_value = @scanner.scan_all(@unquoted_value)
+ break if sub_value.nil?
+ value << sub_value
+ end
+ end
+ value.gsub!(@backslash_quote_character, @quote_character) if @backslash_quote
+ value
+ end
+
+ def parse_quoted_column_value
+ quotes = @scanner.scan_all(@quotes)
+ return nil unless quotes
+
+ @quoted_column_value = true
+ n_quotes = quotes.size
+ if (n_quotes % 2).zero?
+ quotes[0, (n_quotes - 2) / 2]
+ else
+ value = quotes[0, (n_quotes - 1) / 2]
+ while true
+ quoted_value = @scanner.scan_all(@quoted_value)
+ value << quoted_value if quoted_value
+ if @backslash_quote
+ if @scanner.scan(@escaped_backslash)
+ if @scanner.scan(@escaped_quote)
+ value << @quote_character
+ else
+ value << @backslash_character
+ end
+ next
+ end
+ end
+
+ quotes = @scanner.scan_all(@quotes)
+ unless quotes
+ ignore_broken_line
+ message = "Unclosed quoted field"
+ raise MalformedCSVError.new(message, @lineno)
+ end
+ 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]
+ end
+ end
+ value
+ end
+ end
+
+ def parse_column_end
+ return true if @scanner.scan(@column_end)
+ return false unless @column_ends
+
+ @scanner.keep_start
+ if @column_ends.all? {|column_end| @scanner.scan(column_end)}
+ @scanner.keep_drop
+ true
+ else
+ @scanner.keep_back
+ false
+ end
+ end
+
+ def parse_row_end
+ return true if @scanner.scan(@row_end)
+ return false unless @row_ends
+ @scanner.keep_start
+ if @row_ends.all? {|row_end| @scanner.scan(row_end)}
+ @scanner.keep_drop
+ true
+ else
+ @scanner.keep_back
+ false
+ end
+ end
+
+ def strip_value(value)
+ return value unless @strip
+ return nil if value.nil?
+
+ case @strip
+ when String
+ size = value.size
+ while value.start_with?(@strip)
+ size -= 1
+ value = value[1, size]
+ end
+ while value.end_with?(@strip)
+ size -= 1
+ value = value[0, size]
+ end
+ else
+ value.strip!
+ end
+ value
+ end
+
+ def ignore_broken_line
+ @scanner.scan_all(@not_line_end)
+ @scanner.scan_all(@cr_or_lf)
+ @lineno += 1
+ end
+
+ def start_row
+ if @last_line
+ @last_line = nil
+ else
+ @scanner.keep_drop
+ end
+ @scanner.keep_start
+ end
+
+ def emit_row(row, &block)
+ @lineno += 1
+
+ raw_row = row
+ if @use_headers
+ if @headers.nil?
+ @headers = adjust_headers(row)
+ return unless @return_headers
+ row = Row.new(@headers, row, true)
+ else
+ row = Row.new(@headers,
+ @fields_converter.convert(raw_row, @headers, @lineno))
+ end
+ else
+ # convert fields, if needed...
+ row = @fields_converter.convert(raw_row, nil, @lineno)
+ end
+
+ # inject unconverted fields and accessor, if requested...
+ if @unconverted_fields and not row.respond_to?(:unconverted_fields)
+ add_unconverted_fields(row, raw_row)
+ end
+
+ yield(row)
+ end
+
+ # This method injects an instance variable <tt>unconverted_fields</tt> into
+ # +row+ and an accessor method for +row+ called unconverted_fields(). The
+ # variable is set to the contents of +fields+.
+ def add_unconverted_fields(row, fields)
+ class << row
+ attr_reader :unconverted_fields
+ end
+ row.instance_variable_set(:@unconverted_fields, fields)
+ row
+ end
+ end
+end
diff --git a/lib/csv/row.rb b/lib/csv/row.rb
new file mode 100644
index 0000000000..c79d75cd8a
--- /dev/null
+++ b/lib/csv/row.rb
@@ -0,0 +1,388 @@
+# frozen_string_literal: true
+
+require "forwardable"
+
+class CSV
+ #
+ # 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.
+ #
+ # All rows returned by CSV will be constructed from this class, if header row
+ # processing is activated.
+ #
+ class Row
+ #
+ # Construct 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 is assumes to be a field row.
+ #
+ # A CSV::Row object supports the following Array methods through delegation:
+ #
+ # * empty?()
+ # * length()
+ # * size()
+ #
+ def initialize(headers, fields, header_row = false)
+ @header_row = header_row
+ headers.each { |h| h.freeze if h.is_a? String }
+
+ # handle extra headers or fields
+ @row = if headers.size >= fields.size
+ headers.zip(fields)
+ else
+ fields.zip(headers).each(&:reverse!)
+ end
+ end
+
+ # Internal data format used to compare equality.
+ attr_reader :row
+ protected :row
+
+ ### Array Delegation ###
+
+ extend Forwardable
+ def_delegators :@row, :empty?, :length, :size
+
+ def initialize_copy(other)
+ super
+ @row = @row.dup
+ end
+
+ # Returns +true+ if this is a header row.
+ def header_row?
+ @header_row
+ end
+
+ # Returns +true+ if this is a field row.
+ def field_row?
+ not header_row?
+ end
+
+ # Returns the headers of this row.
+ def headers
+ @row.map(&:first)
+ end
+
+ #
+ # :call-seq:
+ # field( header )
+ # field( header, offset )
+ # field( index )
+ #
+ # This method will return the field value by +header+ or +index+. If a field
+ # is not found, +nil+ is returned.
+ #
+ # When provided, +offset+ ensures that a header match occurs on or later
+ # than the +offset+ index. You can use this to find duplicate headers,
+ # without resorting to hard-coding exact indices.
+ #
+ def field(header_or_index, minimum_index = 0)
+ # locate the pair
+ finder = (header_or_index.is_a?(Integer) || header_or_index.is_a?(Range)) ? :[] : :assoc
+ pair = @row[minimum_index..-1].send(finder, header_or_index)
+
+ # return the field if we have a pair
+ if pair.nil?
+ nil
+ else
+ header_or_index.is_a?(Range) ? pair.map(&:last) : pair.last
+ end
+ end
+ alias_method :[], :field
+
+ #
+ # :call-seq:
+ # fetch( header )
+ # fetch( header ) { |row| ... }
+ # fetch( header, default )
+ #
+ # This method will fetch the field value by +header+. It has the same
+ # behavior as Hash#fetch: if there is a field with the given +header+, its
+ # value is returned. Otherwise, if a block is given, it is yielded the
+ # +header+ and its result is returned; if a +default+ is given as the
+ # second argument, it is returned; otherwise a KeyError is raised.
+ #
+ def fetch(header, *varargs)
+ raise ArgumentError, "Too many arguments" if varargs.length > 1
+ pair = @row.assoc(header)
+ if pair
+ pair.last
+ else
+ if block_given?
+ yield header
+ elsif varargs.empty?
+ raise KeyError, "key not found: #{header}"
+ else
+ varargs.first
+ end
+ end
+ end
+
+ # Returns +true+ if there is a field with the given +header+.
+ def has_key?(header)
+ !!@row.assoc(header)
+ end
+ alias_method :include?, :has_key?
+ alias_method :key?, :has_key?
+ alias_method :member?, :has_key?
+ alias_method :header?, :has_key?
+
+ #
+ # :call-seq:
+ # []=( header, value )
+ # []=( header, offset, value )
+ # []=( index, value )
+ #
+ # Looks up the field by the semantics described in CSV::Row.field() and
+ # assigns the +value+.
+ #
+ # Assigning past the end of the row with an index will set all pairs between
+ # to <tt>[nil, nil]</tt>. Assigning to an unused header appends the new
+ # pair.
+ #
+ def []=(*args)
+ value = args.pop
+
+ if args.first.is_a? Integer
+ if @row[args.first].nil? # extending past the end with index
+ @row[args.first] = [nil, value]
+ @row.map! { |pair| pair.nil? ? [nil, nil] : pair }
+ else # normal index assignment
+ @row[args.first][1] = value
+ end
+ else
+ index = index(*args)
+ if index.nil? # appending a field
+ self << [args.first, value]
+ else # normal header assignment
+ @row[index][1] = value
+ end
+ end
+ end
+
+ #
+ # :call-seq:
+ # <<( field )
+ # <<( header_and_field_array )
+ # <<( header_and_field_hash )
+ #
+ # If a two-element Array is provided, it is assumed to be a header and field
+ # and the pair is appended. A Hash works the same way with the key being
+ # the header and the value being the field. Anything else is assumed to be
+ # a lone field which is appended with a +nil+ header.
+ #
+ # This method returns the row for chaining.
+ #
+ def <<(arg)
+ if arg.is_a?(Array) and arg.size == 2 # appending a header and name
+ @row << arg
+ elsif arg.is_a?(Hash) # append header and name pairs
+ arg.each { |pair| @row << pair }
+ else # append field value
+ @row << [nil, arg]
+ end
+
+ self # for chaining
+ end
+
+ #
+ # A shortcut for appending multiple fields. Equivalent to:
+ #
+ # args.each { |arg| csv_row << arg }
+ #
+ # This method returns the row for chaining.
+ #
+ def push(*args)
+ args.each { |arg| self << arg }
+
+ self # for chaining
+ end
+
+ #
+ # :call-seq:
+ # delete( header )
+ # delete( header, offset )
+ # delete( index )
+ #
+ # Used to remove a pair from the row by +header+ or +index+. The pair is
+ # located as described in CSV::Row.field(). The deleted pair is returned,
+ # or +nil+ if a pair could not be found.
+ #
+ def delete(header_or_index, minimum_index = 0)
+ if header_or_index.is_a? Integer # by index
+ @row.delete_at(header_or_index)
+ elsif i = index(header_or_index, minimum_index) # by header
+ @row.delete_at(i)
+ else
+ [ ]
+ end
+ end
+
+ #
+ # The provided +block+ is passed a header and field for each pair in the row
+ # and expected to return +true+ or +false+, depending on whether the pair
+ # should be deleted.
+ #
+ # This method returns the row for chaining.
+ #
+ # If no block is given, an Enumerator is returned.
+ #
+ def delete_if(&block)
+ return enum_for(__method__) { size } unless block_given?
+
+ @row.delete_if(&block)
+
+ self # for chaining
+ end
+
+ #
+ # This method accepts any number of arguments which can be headers, indices,
+ # Ranges of either, or two-element Arrays containing a header and offset.
+ # Each argument will be replaced with a field lookup as described in
+ # CSV::Row.field().
+ #
+ # If called with no arguments, all fields are returned.
+ #
+ def fields(*headers_and_or_indices)
+ if headers_and_or_indices.empty? # return all fields--no arguments
+ @row.map(&:last)
+ else # or work like values_at()
+ all = []
+ headers_and_or_indices.each do |h_or_i|
+ if h_or_i.is_a? Range
+ index_begin = h_or_i.begin.is_a?(Integer) ? h_or_i.begin :
+ index(h_or_i.begin)
+ index_end = h_or_i.end.is_a?(Integer) ? h_or_i.end :
+ index(h_or_i.end)
+ new_range = h_or_i.exclude_end? ? (index_begin...index_end) :
+ (index_begin..index_end)
+ all.concat(fields.values_at(new_range))
+ else
+ all << field(*Array(h_or_i))
+ end
+ end
+ return all
+ end
+ end
+ alias_method :values_at, :fields
+
+ #
+ # :call-seq:
+ # index( header )
+ # index( header, offset )
+ #
+ # 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().
+ #
+ def index(header, minimum_index = 0)
+ # find the pair
+ index = headers[minimum_index..-1].index(header)
+ # return the index at the right offset, if we found one
+ index.nil? ? nil : index + minimum_index
+ end
+
+ #
+ # Returns +true+ if +data+ matches a field in this row, and +false+
+ # otherwise.
+ #
+ def field?(data)
+ fields.include? data
+ end
+
+ include Enumerable
+
+ #
+ # 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.
+ #
+ def each(&block)
+ return enum_for(__method__) { size } unless block_given?
+
+ @row.each(&block)
+
+ self # for chaining
+ end
+
+ alias_method :each_pair, :each
+
+ #
+ # Returns +true+ if this row contains the same headers and fields in the
+ # same order as +other+.
+ #
+ def ==(other)
+ return @row == other.row if other.is_a? CSV::Row
+ @row == other
+ end
+
+ #
+ # Collapses the row into a simple Hash. Be warned that this discards field
+ # order and clobbers duplicate fields.
+ #
+ def to_h
+ hash = {}
+ each do |key, _value|
+ hash[key] = self[key] unless hash.key?(key)
+ end
+ hash
+ end
+ alias_method :to_hash, :to_h
+
+ alias_method :to_ary, :to_a
+
+ #
+ # Returns the row as a CSV String. Headers are not used. Equivalent to:
+ #
+ # csv_row.fields.to_csv( options )
+ #
+ def to_csv(**options)
+ fields.to_csv(options)
+ end
+ alias_method :to_s, :to_csv
+
+ #
+ # Extracts the nested value specified by the sequence of +index+ or +header+ objects by calling dig at each step,
+ # returning nil if any intermediate step is nil.
+ #
+ def dig(index_or_header, *indexes)
+ value = field(index_or_header)
+ if value.nil?
+ nil
+ elsif indexes.empty?
+ value
+ else
+ unless value.respond_to?(:dig)
+ raise TypeError, "#{value.class} does not have \#dig method"
+ end
+ value.dig(*indexes)
+ end
+ end
+
+ # A summary of fields, by header, in an ASCII compatible String.
+ def inspect
+ str = ["#<", self.class.to_s]
+ each do |header, field|
+ str << " " << (header.is_a?(Symbol) ? header.to_s : header.inspect) <<
+ ":" << field.inspect
+ end
+ str << ">"
+ begin
+ str.join('')
+ rescue # any encoding error
+ str.map do |s|
+ e = Encoding::Converter.asciicompat_encoding(s.encoding)
+ e ? s.encode(e) : s.force_encoding("ASCII-8BIT")
+ end.join('')
+ end
+ end
+ end
+end
diff --git a/lib/csv/table.rb b/lib/csv/table.rb
new file mode 100644
index 0000000000..71eb885de5
--- /dev/null
+++ b/lib/csv/table.rb
@@ -0,0 +1,402 @@
+# frozen_string_literal: true
+
+require "forwardable"
+
+class CSV
+ #
+ # 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.
+ #
+ # All tables returned by CSV will be constructed from this class, if header
+ # row processing is activated.
+ #
+ class Table
+ #
+ # Construct 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.
+ #
+ # 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.
+ #
+ # A CSV::Table object supports the following Array methods through
+ # delegation:
+ #
+ # * empty?()
+ # * length()
+ # * size()
+ #
+ def initialize(array_of_rows, headers: nil)
+ @table = array_of_rows
+ @headers = headers
+ unless @headers
+ if @table.empty?
+ @headers = []
+ else
+ @headers = @table.first.headers
+ end
+ end
+
+ @mode = :col_or_row
+ end
+
+ # The current access mode for indexing and iteration.
+ attr_reader :mode
+
+ # Internal data format used to compare equality.
+ attr_reader :table
+ protected :table
+
+ ### Array Delegation ###
+
+ extend Forwardable
+ def_delegators :@table, :empty?, :length, :size
+
+ #
+ # 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.
+ #
+ # This method returns the duplicate table for chaining. Don't chain
+ # destructive methods (like []=()) this way though, since you are working
+ # with a duplicate.
+ #
+ def by_col
+ self.class.new(@table.dup).by_col!
+ end
+
+ #
+ # 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.
+ #
+ def by_col!
+ @mode = :col
+
+ self
+ end
+
+ #
+ # 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.
+ #
+ # This method returns the duplicate table for chaining. Don't chain
+ # destructive methods (like []=()) this way though, since you are working
+ # with a duplicate.
+ #
+ def by_col_or_row
+ self.class.new(@table.dup).by_col_or_row!
+ end
+
+ #
+ # 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.
+ #
+ def by_col_or_row!
+ @mode = :col_or_row
+
+ self
+ end
+
+ #
+ # 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.
+ #
+ # This method returns the duplicate table for chaining. Don't chain
+ # destructive methods (like []=()) this way though, since you are working
+ # with a duplicate.
+ #
+ def by_row
+ self.class.new(@table.dup).by_row!
+ end
+
+ #
+ # 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.
+ #
+ def by_row!
+ @mode = :row
+
+ self
+ end
+
+ #
+ # 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.
+ #
+ def headers
+ if @table.empty?
+ @headers.dup
+ else
+ @table.first.headers
+ end
+ end
+
+ #
+ # In the default mixed mode, this method returns rows for index access and
+ # columns for header access. You can force the index association by first
+ # calling by_col!() or by_row!().
+ #
+ # Columns are returned as an Array of values. Altering that Array has no
+ # effect on the table.
+ #
+ def [](index_or_header)
+ if @mode == :row or # by index
+ (@mode == :col_or_row and (index_or_header.is_a?(Integer) or index_or_header.is_a?(Range)))
+ @table[index_or_header]
+ else # by header
+ @table.map { |row| row[index_or_header] }
+ end
+ end
+
+ #
+ # 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!().
+ #
+ # Rows may be set to an Array of values (which will inherit the table's
+ # headers()) or a CSV::Row.
+ #
+ # 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+.
+ #
+ # Assigning to an existing column or row clobbers the data. Assigning to
+ # new columns creates them at the right end of the table.
+ #
+ def []=(index_or_header, value)
+ if @mode == :row or # by index
+ (@mode == :col_or_row and index_or_header.is_a? Integer)
+ if value.is_a? Array
+ @table[index_or_header] = Row.new(headers, value)
+ else
+ @table[index_or_header] = value
+ end
+ else # set column
+ unless index_or_header.is_a? Integer
+ index = @headers.index(index_or_header) || @headers.size
+ @headers[index] = index_or_header
+ end
+ if value.is_a? Array # multiple values
+ @table.each_with_index do |row, i|
+ if row.header_row?
+ row[index_or_header] = index_or_header
+ else
+ row[index_or_header] = value[i]
+ end
+ end
+ else # repeated value
+ @table.each do |row|
+ if row.header_row?
+ row[index_or_header] = index_or_header
+ else
+ row[index_or_header] = value
+ end
+ end
+ end
+ end
+ end
+
+ #
+ # The mixed mode default is to treat a list of indices as row access,
+ # returning the rows indicated. Anything else is considered columnar
+ # access. For columnar access, the return set has an Array for each row
+ # with the values indicated by the headers in each Array. You can force
+ # column or row mode using by_col!() or by_row!().
+ #
+ # You cannot mix column and row access.
+ #
+ def values_at(*indices_or_headers)
+ if @mode == :row or # by indices
+ ( @mode == :col_or_row and indices_or_headers.all? do |index|
+ index.is_a?(Integer) or
+ ( index.is_a?(Range) and
+ index.first.is_a?(Integer) and
+ index.last.is_a?(Integer) )
+ end )
+ @table.values_at(*indices_or_headers)
+ else # by headers
+ @table.map { |row| row.values_at(*indices_or_headers) }
+ end
+ end
+
+ #
+ # Adds a new row to the bottom end of this table. You can provide an Array,
+ # which will be converted to a CSV::Row (inheriting the table's headers()),
+ # or a CSV::Row.
+ #
+ # This method returns the table for chaining.
+ #
+ def <<(row_or_array)
+ if row_or_array.is_a? Array # append Array
+ @table << Row.new(headers, row_or_array)
+ else # append Row
+ @table << row_or_array
+ end
+
+ self # for chaining
+ end
+
+ #
+ # A shortcut for appending multiple rows. Equivalent to:
+ #
+ # rows.each { |row| self << row }
+ #
+ # This method returns the table for chaining.
+ #
+ def push(*rows)
+ rows.each { |row| self << row }
+
+ self # for chaining
+ end
+
+ #
+ # Removes and returns the indicated columns or rows. In the default mixed
+ # mode indices refer to rows and everything else is assumed to be a column
+ # headers. Use by_col!() or by_row!() to force the lookup.
+ #
+ def delete(*indexes_or_headers)
+ if indexes_or_headers.empty?
+ raise ArgumentError, "wrong number of arguments (given 0, expected 1+)"
+ end
+ deleted_values = indexes_or_headers.map do |index_or_header|
+ if @mode == :row or # by index
+ (@mode == :col_or_row and index_or_header.is_a? Integer)
+ @table.delete_at(index_or_header)
+ else # by header
+ if index_or_header.is_a? Integer
+ @headers.delete_at(index_or_header)
+ else
+ @headers.delete(index_or_header)
+ end
+ @table.map { |row| row.delete(index_or_header).last }
+ end
+ end
+ if indexes_or_headers.size == 1
+ deleted_values[0]
+ else
+ deleted_values
+ end
+ end
+
+ #
+ # Removes any column or row for which the block returns +true+. In the
+ # default mixed mode or row mode, iteration is the standard row major
+ # walking of rows. In column mode, iteration will +yield+ two element
+ # tuples containing the column name and an Array of values for that column.
+ #
+ # This method returns the table for chaining.
+ #
+ # If no block is given, an Enumerator is returned.
+ #
+ def delete_if(&block)
+ return enum_for(__method__) { @mode == :row or @mode == :col_or_row ? size : headers.size } unless block_given?
+
+ 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]])
+ end
+ end
+
+ self # for chaining
+ end
+
+ include Enumerable
+
+ #
+ # In the default mixed mode or row mode, iteration is the standard row major
+ # walking of rows. In column mode, iteration will +yield+ two element
+ # tuples containing the column name and an Array of values for that column.
+ #
+ # This method returns the table for chaining.
+ #
+ # If no block is given, an Enumerator is returned.
+ #
+ def each(&block)
+ return enum_for(__method__) { @mode == :col ? headers.size : size } unless block_given?
+
+ if @mode == :col
+ headers.each { |header| yield([header, self[header]]) }
+ else
+ @table.each(&block)
+ end
+
+ self # for chaining
+ end
+
+ # Returns +true+ if all rows of this table ==() +other+'s rows.
+ def ==(other)
+ return @table == other.table if other.is_a? CSV::Table
+ @table == other
+ end
+
+ #
+ # Returns the table as an Array of Arrays. Headers will be the first row,
+ # then all of the field rows will follow.
+ #
+ def to_a
+ array = [headers]
+ @table.each do |row|
+ array.push(row.fields) unless row.header_row?
+ end
+
+ array
+ end
+
+ #
+ # Returns the table as a complete CSV String. Headers will be listed first,
+ # then all of the field rows.
+ #
+ # This method assumes you want the Table.headers(), unless you explicitly
+ # pass <tt>:write_headers => false</tt>.
+ #
+ def to_csv(write_headers: true, **options)
+ array = write_headers ? [headers.to_csv(options)] : []
+ @table.each do |row|
+ array.push(row.fields.to_csv(options)) unless row.header_row?
+ end
+
+ array.join("")
+ end
+ alias_method :to_s, :to_csv
+
+ #
+ # Extracts the nested value specified by the sequence of +index+ or +header+ objects by calling dig at each step,
+ # returning nil if any intermediate step is nil.
+ #
+ def dig(index_or_header, *index_or_headers)
+ value = self[index_or_header]
+ if value.nil?
+ nil
+ elsif index_or_headers.empty?
+ value
+ else
+ unless value.respond_to?(:dig)
+ raise TypeError, "#{value.class} does not have \#dig method"
+ end
+ value.dig(*index_or_headers)
+ end
+ end
+
+ # Shows the mode and size of this table in a US-ASCII String.
+ def inspect
+ "#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>".encode("US-ASCII")
+ end
+ end
+end
diff --git a/lib/csv/version.rb b/lib/csv/version.rb
new file mode 100644
index 0000000000..b2b0ad743a
--- /dev/null
+++ b/lib/csv/version.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class CSV
+ # The version of the installed library.
+ VERSION = "3.0.9"
+end
diff --git a/lib/csv/writer.rb b/lib/csv/writer.rb
new file mode 100644
index 0000000000..8e0aab32ff
--- /dev/null
+++ b/lib/csv/writer.rb
@@ -0,0 +1,156 @@
+# frozen_string_literal: true
+
+require_relative "match_p"
+require_relative "row"
+
+using CSV::MatchP if CSV.const_defined?(:MatchP)
+
+class CSV
+ class Writer
+ attr_reader :lineno
+ attr_reader :headers
+
+ def initialize(output, options)
+ @output = output
+ @options = options
+ @lineno = 0
+ prepare
+ if @options[:write_headers] and @headers
+ self << @headers
+ end
+ @fields_converter = @options[:fields_converter]
+ end
+
+ def <<(row)
+ case row
+ when Row
+ row = row.fields
+ when Hash
+ row = @headers.collect {|header| row[header]}
+ end
+
+ @headers ||= row if @use_headers
+ @lineno += 1
+
+ row = @fields_converter.convert(row, nil, lineno) if @fields_converter
+
+ converted_row = row.collect do |field|
+ quote(field)
+ end
+ line = converted_row.join(@column_separator) + @row_separator
+ if @output_encoding
+ line = line.encode(@output_encoding)
+ end
+ @output << line
+
+ self
+ end
+
+ def rewind
+ @lineno = 0
+ @headers = nil if @options[:headers].nil?
+ end
+
+ private
+ def prepare
+ @encoding = @options[:encoding]
+
+ prepare_header
+ prepare_format
+ prepare_output
+ end
+
+ def prepare_header
+ headers = @options[:headers]
+ case headers
+ when Array
+ @headers = headers
+ @use_headers = true
+ when String
+ @headers = CSV.parse_line(headers,
+ col_sep: @options[:column_separator],
+ row_sep: @options[:row_separator],
+ quote_char: @options[:quote_character])
+ @use_headers = true
+ when true
+ @headers = nil
+ @use_headers = true
+ else
+ @headers = nil
+ @use_headers = false
+ end
+ return unless @headers
+
+ converter = @options[:header_fields_converter]
+ @headers = converter.convert(@headers, nil, 0)
+ @headers.each do |header|
+ header.freeze if header.is_a?(String)
+ end
+ end
+
+ def prepare_format
+ @column_separator = @options[:column_separator].to_s.encode(@encoding)
+ row_separator = @options[:row_separator]
+ if row_separator == :auto
+ @row_separator = $INPUT_RECORD_SEPARATOR.encode(@encoding)
+ else
+ @row_separator = row_separator.to_s.encode(@encoding)
+ end
+ @quote_character = @options[:quote_character]
+ @force_quotes = @options[:force_quotes]
+ unless @force_quotes
+ @quotable_pattern =
+ Regexp.new("[\r\n".encode(@encoding) +
+ Regexp.escape(@column_separator) +
+ Regexp.escape(@quote_character.encode(@encoding)) +
+ "]".encode(@encoding))
+ end
+ @quote_empty = @options.fetch(:quote_empty, true)
+ end
+
+ def prepare_output
+ @output_encoding = nil
+ return unless @output.is_a?(StringIO)
+
+ output_encoding = @output.internal_encoding || @output.external_encoding
+ if @encoding != output_encoding
+ if @options[:force_encoding]
+ @output_encoding = output_encoding
+ else
+ compatible_encoding = Encoding.compatible?(@encoding, output_encoding)
+ if compatible_encoding
+ @output.set_encoding(compatible_encoding)
+ @output.seek(0, IO::SEEK_END)
+ end
+ end
+ end
+ end
+
+ def quote_field(field)
+ field = String(field)
+ encoded_quote_character = @quote_character.encode(field.encoding)
+ encoded_quote_character +
+ field.gsub(encoded_quote_character,
+ encoded_quote_character * 2) +
+ encoded_quote_character
+ end
+
+ def quote(field)
+ if @force_quotes
+ quote_field(field)
+ else
+ if field.nil? # represent +nil+ fields as empty unquoted fields
+ ""
+ else
+ field = String(field) # Stringify fields
+ # represent empty fields as empty quoted fields
+ if (@quote_empty and field.empty?) or @quotable_pattern.match?(field)
+ quote_field(field)
+ else
+ field # unquoted field
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/debug.rb b/lib/debug.rb
index 394a53e172..34d7d27406 100644
--- a/lib/debug.rb
+++ b/lib/debug.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
# Copyright (C) 2000 Information-technology Promotion Agency, Japan
# Copyright (C) 2000-2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
diff --git a/lib/delegate.rb b/lib/delegate.rb
index b4188037b4..37819a28f4 100644
--- a/lib/delegate.rb
+++ b/lib/delegate.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# = delegate -- Support for the Delegation Pattern
#
# Documentation by James Edward Gray II and Gavin Sinclair
@@ -40,7 +40,7 @@ class Delegator < BasicObject
kernel = ::Kernel.dup
kernel.class_eval do
alias __raise__ raise
- [:to_s, :inspect, :=~, :!~, :===, :<=>, :eql?, :hash].each do |m|
+ [:to_s, :inspect, :=~, :!~, :===, :<=>, :hash].each do |m|
undef_method m
end
private_instance_methods.each do |m|
@@ -97,7 +97,7 @@ class Delegator < BasicObject
target = self.__getobj__ {r = false}
r &&= target.respond_to?(m, include_private)
if r && include_private && !target.respond_to?(m, false)
- warn "#{caller(3)[0]}: delegator does not forward private method \##{m}"
+ warn "delegator does not forward private method \##{m}", uplevel: 3
return false
end
r
@@ -146,6 +146,14 @@ class Delegator < BasicObject
end
#
+ # Returns true if two objects are considered of equal value.
+ #
+ def eql?(obj)
+ return true if obj.equal?(self)
+ obj.eql?(__getobj__)
+ end
+
+ #
# Delegates ! to the \_\_getobj\_\_
#
def !
diff --git a/lib/drb/acl.rb b/lib/drb/acl.rb
index 520b7df71d..b004656f09 100644
--- a/lib/drb/acl.rb
+++ b/lib/drb/acl.rb
@@ -49,6 +49,9 @@ class ACL
# +str+ may be "*" or "all" to match any address, an IP address string
# to match a specific address, an IP address mask per IPAddr, or one
# containing "*" to match part of an IPv4 address.
+ #
+ # IPAddr::InvalidPrefixError may be raised when an IP network
+ # address with an invalid netmask/prefix is given.
def initialize(str)
if str == '*' or str == 'all'
@@ -58,6 +61,10 @@ class ACL
else
begin
@pat = [:ip, IPAddr.new(str)]
+ rescue IPAddr::InvalidPrefixError
+ # In this case, `str` shouldn't be a host name pattern
+ # because it contains a slash.
+ raise
rescue ArgumentError
@pat = [:name, dot_pat(str)]
end
@@ -82,8 +89,7 @@ class ACL
# Creates a Regexp to match an address.
def dot_pat(str)
- exp = "^" + dot_pat_str(str) + "$"
- Regexp.new(exp)
+ /\A#{dot_pat_str(str)}\z/
end
public
diff --git a/lib/drb/drb.rb b/lib/drb/drb.rb
index 38cb38563b..de57362f24 100644
--- a/lib/drb/drb.rb
+++ b/lib/drb/drb.rb
@@ -47,9 +47,8 @@
# Translation of presentation on Ruby by Masatoshi Seki.
require 'socket'
-require 'thread'
require 'io/wait'
-require 'drb/eq'
+require_relative 'eq'
#
# == Overview
@@ -361,7 +360,7 @@ module DRb
# drb remains valid only while that object instance remains alive
# within the server runtime.
#
- # For alternative mechanisms, see DRb::TimerIdConv in rdb/timeridconv.rb
+ # For alternative mechanisms, see DRb::TimerIdConv in drb/timeridconv.rb
# and DRbNameIdConv in sample/name.rb in the full drb distribution.
class DRbIdConv
@@ -801,7 +800,7 @@ module DRb
module_function :uri_option
def auto_load(uri) # :nodoc:
- if uri =~ /^drb([a-z0-9]+):/
+ if /\Adrb([a-z0-9]+):/ =~ uri
require("drb/#{$1}") rescue nil
end
end
@@ -817,13 +816,13 @@ module DRb
# :stopdoc:
private
def self.parse_uri(uri)
- if uri =~ /^druby:\/\/(.*?):(\d+)(\?(.*))?$/
+ if /\Adruby:\/\/(.*?):(\d+)(\?(.*))?\z/ =~ uri
host = $1
port = $2.to_i
option = $4
[host, port, option]
else
- raise(DRbBadScheme, uri) unless uri =~ /^druby:/
+ raise(DRbBadScheme, uri) unless uri.start_with?('druby:')
raise(DRbBadURI, 'can\'t parse uri:' + uri)
end
end
@@ -848,7 +847,11 @@ module DRb
def self.getservername
host = Socket::gethostname
begin
- Socket::gethostbyname(host)[0]
+ Socket::getaddrinfo(host, nil,
+ Socket::AF_UNSPEC,
+ Socket::SOCK_STREAM,
+ 0,
+ Socket::AI_PASSIVE)[0][3]
rescue
'localhost'
end
@@ -950,6 +953,7 @@ module DRb
# returned by #open or by #accept, then it closes this particular
# client-server session.
def close
+ shutdown
if @socket
@socket.close
@socket = nil
@@ -958,14 +962,8 @@ module DRb
end
def close_shutdown_pipe
- if @shutdown_pipe_r && !@shutdown_pipe_r.closed?
- @shutdown_pipe_r.close
- @shutdown_pipe_r = nil
- end
- if @shutdown_pipe_w && !@shutdown_pipe_w.closed?
- @shutdown_pipe_w.close
- @shutdown_pipe_w = nil
- end
+ @shutdown_pipe_w.close
+ @shutdown_pipe_r.close
end
private :close_shutdown_pipe
@@ -998,7 +996,7 @@ module DRb
# Graceful shutdown
def shutdown
- @shutdown_pipe_w.close if @shutdown_pipe_w && !@shutdown_pipe_w.closed?
+ @shutdown_pipe_w.close
end
# Check to see if this connection is alive.
@@ -1173,7 +1171,7 @@ module DRb
bt = []
result.backtrace.each do |x|
break if /`__send__'$/ =~ x
- if /^\(druby:\/\// =~ x
+ if /\A\(druby:\/\// =~ x
bt.push(x)
else
bt.push(prefix + x)
@@ -1467,12 +1465,7 @@ module DRb
if Thread.current['DRb'] && Thread.current['DRb']['server'] == self
Thread.current['DRb']['stop_service'] = true
else
- if @protocol.respond_to? :shutdown
- @protocol.shutdown
- else
- [@thread, *@grp.list].each {|thread| thread.kill} # xxx: Thread#kill
- end
- @thread.join
+ shutdown
end
end
@@ -1491,6 +1484,18 @@ module DRb
private
+ def shutdown
+ current = Thread.current
+ if @protocol.respond_to? :shutdown
+ @protocol.shutdown
+ else
+ [@thread, *@grp.list].each { |thread|
+ thread.kill unless thread == current # xxx: Thread#kill
+ }
+ end
+ @thread.join unless @thread == current
+ end
+
##
# Starts the DRb main loop in a new thread.
@@ -1565,17 +1570,23 @@ module DRb
if $SAFE < @safe_level
info = Thread.current['DRb']
if @block
- @result = Thread.new {
+ @result = Thread.new do
Thread.current['DRb'] = info
+ prev_safe_level = $SAFE
$SAFE = @safe_level
perform_with_block
- }.value
+ ensure
+ $SAFE = prev_safe_level
+ end.value
else
- @result = Thread.new {
+ @result = Thread.new do
Thread.current['DRb'] = info
+ prev_safe_level = $SAFE
$SAFE = @safe_level
perform_without_block
- }.value
+ ensure
+ $SAFE = prev_safe_level
+ end.value
end
else
if @block
@@ -1627,7 +1638,7 @@ module DRb
end
- require 'drb/invokemethod'
+ require_relative 'invokemethod'
class InvokeMethod
include InvokeMethod18Mixin
end
@@ -1673,7 +1684,8 @@ module DRb
ensure
client.close unless succ
if Thread.current['DRb']['stop_service']
- Thread.new { stop_service }
+ shutdown
+ break
end
break unless succ
end
@@ -1854,6 +1866,11 @@ module DRb
# Removes +server+ from the list of registered servers.
def remove_server(server)
@server.delete(server.uri)
+ mutex.synchronize do
+ if @primary_server == server
+ @primary_server = nil
+ end
+ end
end
module_function :remove_server
diff --git a/lib/drb/extserv.rb b/lib/drb/extserv.rb
index 1cb1be4709..a93d5d1576 100644
--- a/lib/drb/extserv.rb
+++ b/lib/drb/extserv.rb
@@ -4,7 +4,7 @@
Copyright (c) 2000,2002 Masatoshi SEKI
=end
-require 'drb/drb'
+require_relative 'drb'
require 'monitor'
module DRb
diff --git a/lib/drb/extservm.rb b/lib/drb/extservm.rb
index 7e70a3cd82..040e4e3e08 100644
--- a/lib/drb/extservm.rb
+++ b/lib/drb/extservm.rb
@@ -4,8 +4,7 @@
Copyright (c) 2000 Masatoshi SEKI
=end
-require 'drb/drb'
-require 'thread'
+require_relative 'drb'
require 'monitor'
module DRb
@@ -38,7 +37,7 @@ module DRb
synchronize do
while true
server = @servers[name]
- return server if server&.alive?
+ return server if server && server.alive? # server may be `false'
invoke_service(name)
@cond.wait
end
@@ -62,8 +61,7 @@ module DRb
private
def invoke_thread
Thread.new do
- while true
- name = @queue.pop
+ while name = @queue.pop
invoke_service_command(name, @@command[name])
end
end
diff --git a/lib/drb/gw.rb b/lib/drb/gw.rb
index d000507644..65a525476e 100644
--- a/lib/drb/gw.rb
+++ b/lib/drb/gw.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require 'drb/drb'
+require_relative 'drb'
require 'monitor'
module DRb
diff --git a/lib/drb/ssl.rb b/lib/drb/ssl.rb
index 8d2724e736..48ba35ace7 100644
--- a/lib/drb/ssl.rb
+++ b/lib/drb/ssl.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
require 'socket'
require 'openssl'
-require 'drb/drb'
+require_relative 'drb'
require 'singleton'
module DRb
@@ -162,7 +162,7 @@ module DRb
return
end
- rsa = OpenSSL::PKey::RSA.new(1024){|p, n|
+ rsa = OpenSSL::PKey::RSA.new(2048){|p, n|
next unless self[:verbose]
case p
when 0; $stderr.putc "." # BN_generate_prime
@@ -196,7 +196,7 @@ module DRb
if comment = self[:SSLCertComment]
cert.add_extension(ef.create_extension("nsComment", comment))
end
- cert.sign(rsa, OpenSSL::Digest::SHA1.new)
+ cert.sign(rsa, OpenSSL::Digest::SHA256.new)
@cert = cert
@pkey = rsa
@@ -226,13 +226,13 @@ module DRb
#
# Raises DRbBadScheme or DRbBadURI if +uri+ is not matching or malformed
def self.parse_uri(uri) # :nodoc:
- if uri =~ /^drbssl:\/\/(.*?):(\d+)(\?(.*))?$/
+ if /\Adrbssl:\/\/(.*?):(\d+)(\?(.*))?\z/ =~ uri
host = $1
port = $2.to_i
option = $4
[host, port, option]
else
- raise(DRbBadScheme, uri) unless uri =~ /^drbssl:/
+ raise(DRbBadScheme, uri) unless uri.start_with?('drbssl:')
raise(DRbBadURI, 'can\'t parse uri:' + uri)
end
end
@@ -336,7 +336,7 @@ module DRb
end
self.class.new(uri, ssl, @config, true)
rescue OpenSSL::SSL::SSLError
- warn("#{__FILE__}:#{__LINE__}: warning: #{$!.message} (#{$!.class})") if @config[:verbose]
+ warn("#{$!.message} (#{$!.class})", uplevel: 0) if @config[:verbose]
retry
end
end
diff --git a/lib/drb/timeridconv.rb b/lib/drb/timeridconv.rb
index 9ac7e1e69c..3ead98a7f2 100644
--- a/lib/drb/timeridconv.rb
+++ b/lib/drb/timeridconv.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require 'drb/drb'
+require_relative 'drb'
require 'monitor'
module DRb
diff --git a/lib/drb/unix.rb b/lib/drb/unix.rb
index adacf6df5b..89957c9e7b 100644
--- a/lib/drb/unix.rb
+++ b/lib/drb/unix.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
require 'socket'
-require 'drb/drb'
+require_relative 'drb'
require 'tmpdir'
raise(LoadError, "UNIXServer is required") unless defined?(UNIXServer)
@@ -15,12 +15,12 @@ module DRb
class DRbUNIXSocket < DRbTCPSocket
# :stopdoc:
def self.parse_uri(uri)
- if /^drbunix:(.*?)(\?(.*))?$/ =~ uri
+ if /\Adrbunix:(.*?)(\?(.*))?\z/ =~ uri
filename = $1
option = $3
[filename, option]
else
- raise(DRbBadScheme, uri) unless uri =~ /^drbunix:/
+ raise(DRbBadScheme, uri) unless uri.start_with?('drbunix:')
raise(DRbBadURI, 'can\'t parse uri:' + uri)
end
end
@@ -95,6 +95,7 @@ module DRb
public
def close
return unless @socket
+ shutdown # DRbProtocol#shutdown
path = @socket.path if @server_mode
@socket.close
File.unlink(path) if @server_mode
diff --git a/lib/e2mmap.gemspec b/lib/e2mmap.gemspec
new file mode 100644
index 0000000000..b9808d89ff
--- /dev/null
+++ b/lib/e2mmap.gemspec
@@ -0,0 +1,26 @@
+begin
+ require_relative "lib/e2mmap/version"
+rescue LoadError
+ # for Ruby core repository
+ require_relative "e2mmap/version"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "e2mmap"
+ spec.version = Exception2MessageMapper::VERSION
+ spec.authors = ["Keiju ISHITSUKA"]
+ spec.email = ["keiju@ruby-lang.org"]
+
+ spec.summary = %q{Module for defining custom exceptions with specific messages.}
+ spec.description = %q{Module for defining custom exceptions with specific messages.}
+ spec.homepage = "https://github.com/ruby/e2mmap"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "e2mmap.gemspec", "lib/e2mmap.rb", "lib/e2mmap/version.rb"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler", "~> 1.16"
+ spec.add_development_dependency "rake", "~> 10.0"
+end
diff --git a/lib/e2mmap.rb b/lib/e2mmap.rb
index ba1e87dd85..1c1d7148ff 100644
--- a/lib/e2mmap.rb
+++ b/lib/e2mmap.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
#--
# e2mmap.rb - for Ruby 1.1
diff --git a/lib/e2mmap/version.rb b/lib/e2mmap/version.rb
new file mode 100644
index 0000000000..c459aeace3
--- /dev/null
+++ b/lib/e2mmap/version.rb
@@ -0,0 +1,3 @@
+module Exception2MessageMapper
+ VERSION = "0.1.0"
+end
diff --git a/lib/erb.rb b/lib/erb.rb
index 9483711024..5b725d7820 100644
--- a/lib/erb.rb
+++ b/lib/erb.rb
@@ -1,5 +1,5 @@
# -*- coding: us-ascii -*-
-# frozen_string_literal: false
+# frozen_string_literal: true
# = ERB -- Ruby Templating
#
# Author:: Masatoshi SEKI
@@ -115,7 +115,7 @@ require "cgi/util"
# James Edward Gray II
# }.gsub(/^ /, '')
#
-# message = ERB.new(template, 0, "%<>")
+# message = ERB.new(template, trim_mode: "%<>")
#
# # Set up template data.
# to = "Community Spokesman <spokesman@ruby_community.org>"
@@ -263,7 +263,7 @@ class ERB
# Returns revision information for the erb.rb module.
def self.version
- "erb.rb [2.1.0 #{ERB::Revision.split[1]}]"
+ "erb.rb [2.2.0 #{ERB::Revision.split[1]}]"
end
end
@@ -280,9 +280,9 @@ class ERB
# ERB#src:
#
# compiler = ERB::Compiler.new('<>')
- # compiler.pre_cmd = ["_erbout=String.new"]
- # compiler.put_cmd = "_erbout.concat"
- # compiler.insert_cmd = "_erbout.concat"
+ # compiler.pre_cmd = ["_erbout=+''"]
+ # compiler.put_cmd = "_erbout.<<"
+ # compiler.insert_cmd = "_erbout.<<"
# compiler.post_cmd = ["_erbout"]
#
# code, enc = compiler.compile("Got <%= obj %>!\n")
@@ -291,7 +291,7 @@ class ERB
# <i>Generates</i>:
#
# #coding:UTF-8
- # _erbout=String.new; _erbout.concat "Got "; _erbout.concat(( obj ).to_s); _erbout.concat "!\n"; _erbout
+ # _erbout=+''; _erbout.<< "Got ".freeze; _erbout.<<(( obj ).to_s); _erbout.<< "!\n".freeze; _erbout
#
# By default the output is sent to the print method. For example:
#
@@ -302,7 +302,7 @@ class ERB
# <i>Generates</i>:
#
# #coding:UTF-8
- # print "Got "; print(( obj ).to_s); print "!\n"
+ # print "Got ".freeze; print(( obj ).to_s); print "!\n".freeze
#
# == Evaluation
#
@@ -347,10 +347,6 @@ class ERB
end
attr_reader :value
alias :to_s :value
-
- def empty?
- @value.empty?
- end
end
class Scanner # :nodoc:
@@ -371,11 +367,13 @@ class ERB
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 = %w(<%% <%= <%# <%).freeze
- @etags = %w(%%> %>).freeze
+ @stags = DEFAULT_STAGS
+ @etags = DEFAULT_ETAGS
end
attr_accessor :stag
attr_reader :stags, :etags
@@ -389,13 +387,13 @@ class ERB
@trim_mode = trim_mode
@percent = percent
if @trim_mode == '>'
- @scan_reg = /(.*?)(%>\n|#{(stags + etags).join('|')}|\n|\z)/m
+ @scan_reg = /(.*?)(%>\r?\n|#{(stags + etags).join('|')}|\n|\z)/m
@scan_line = self.method(:trim_line1)
elsif @trim_mode == '<>'
- @scan_reg = /(.*?)(%>\n|#{(stags + etags).join('|')}|\n|\z)/m
+ @scan_reg = /(.*?)(%>\r?\n|#{(stags + etags).join('|')}|\n|\z)/m
@scan_line = self.method(:trim_line2)
elsif @trim_mode == '-'
- @scan_reg = /(.*?)(^[ \t]*<%\-|<%\-|-%>\n|-%>|#{(stags + etags).join('|')}|\z)/m
+ @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
@@ -441,7 +439,7 @@ class ERB
line.scan(@scan_reg) do |tokens|
tokens.each do |token|
next if token.empty?
- if token == "%>\n"
+ if token == "%>\n" || token == "%>\r\n"
yield('%>')
yield(:cr)
else
@@ -457,7 +455,7 @@ class ERB
tokens.each do |token|
next if token.empty?
head = token unless head
- if token == "%>\n"
+ if token == "%>\n" || token == "%>\r\n"
yield('%>')
if is_erb_stag?(head)
yield(:cr)
@@ -479,7 +477,7 @@ class ERB
next if token.empty?
if @stag.nil? && /[ \t]*<%-/ =~ token
yield('<%')
- elsif @stag && token == "-%>\n"
+ elsif @stag && (token == "-%>\n" || token == "-%>\r\n")
yield('%>')
yield(:cr)
elsif @stag && token == '-%>'
@@ -499,25 +497,14 @@ class ERB
Scanner.default_scanner = TrimScanner
- class SimpleScanner < Scanner # :nodoc:
- def scan
- @src.scan(/(.*?)(#{(stags + etags).join('|')}|\n|\z)/m) do |tokens|
- tokens.each do |token|
- next if token.empty?
- yield(token)
- end
- end
- end
- end
-
- Scanner.regist_scanner(SimpleScanner, nil, false)
-
begin
require 'strscan'
- class SimpleScanner2 < Scanner # :nodoc:
+ rescue LoadError
+ else
+ class SimpleScanner < Scanner # :nodoc:
def scan
- stag_reg = /(.*?)(#{stags.join('|')}|\z)/m
- etag_reg = /(.*?)(#{etags.join('|')}|\z)/m
+ 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)
@@ -526,7 +513,7 @@ class ERB
end
end
end
- Scanner.regist_scanner(SimpleScanner2, nil, false)
+ Scanner.register_scanner(SimpleScanner, nil, false)
class ExplicitScanner < Scanner # :nodoc:
def scan
@@ -542,23 +529,21 @@ class ERB
yield('<%')
elsif elem == '-%>'
yield('%>')
- yield(:cr) if scanner.scan(/(\n|\z)/)
+ yield(:cr) if scanner.scan(/(\r?\n|\z)/)
else
yield(elem)
end
end
end
end
- Scanner.regist_scanner(ExplicitScanner, '-', false)
-
- rescue LoadError
+ Scanner.register_scanner(ExplicitScanner, '-', false)
end
class Buffer # :nodoc:
def initialize(compiler, enc=nil, frozen=nil)
@compiler = compiler
@line = []
- @script = ''
+ @script = +''
@script << "#coding:#{enc}\n" if enc
@script << "#frozen-string-literal:#{frozen}\n" unless frozen.nil?
@compiler.pre_cmd.each do |x|
@@ -587,17 +572,8 @@ class ERB
end
end
- def content_dump(s) # :nodoc:
- n = s.count("\n")
- if n > 0
- s.dump + "\n" * n
- else
- s.dump
- end
- end
-
def add_put_cmd(out, content)
- out.push("#{@put_cmd} #{content_dump(content)}")
+ out.push("#{@put_cmd} #{content.dump}.freeze#{"\n" * content.count("\n")}")
end
def add_insert_cmd(out, content)
@@ -613,7 +589,7 @@ class ERB
magic_comment = detect_magic_comment(s, enc)
out = Buffer.new(self, *magic_comment)
- self.content = ''
+ self.content = +''
scanner = make_scanner(s)
scanner.scan do |token|
next if token.nil?
@@ -633,7 +609,7 @@ class ERB
case stag
when PercentLine
add_put_cmd(out, content) if content.size > 0
- self.content = ''
+ self.content = +''
out.push(stag.to_s)
out.cr
when :cr
@@ -641,11 +617,11 @@ class ERB
when '<%', '<%=', '<%#'
scanner.stag = stag
add_put_cmd(out, content) if content.size > 0
- self.content = ''
+ self.content = +''
when "\n"
content << "\n"
add_put_cmd(out, content)
- self.content = ''
+ self.content = +''
when '<%%'
content << '<%'
else
@@ -658,7 +634,7 @@ class ERB
when '%>'
compile_content(scanner.stag, out)
scanner.stag = nil
- self.content = ''
+ self.content = +''
when '%%>'
content << '%>'
else
@@ -679,7 +655,7 @@ class ERB
when '<%='
add_insert_cmd(out, content)
when '<%#'
- # out.push("# #{content_dump(content)}")
+ # commented out
end
end
@@ -689,9 +665,13 @@ class ERB
return [false, '>']
when 2
return [false, '<>']
- when 0
+ 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, '-']
@@ -703,6 +683,7 @@ class ERB
[perc, nil]
end
else
+ warn_invalid_trim_mode(mode, uplevel: 5)
return [false, nil]
end
end
@@ -754,6 +735,10 @@ class ERB
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
@@ -801,11 +786,11 @@ class ERB
# def build
# b = binding
# # create and run templates, filling member data variables
- # ERB.new(<<-'END_PRODUCT'.gsub(/^\s+/, ""), 0, "", "@product").result b
+ # ERB.new(<<-'END_PRODUCT'.gsub(/^\s+/, ""), trim_mode: "", eoutvar: "@product").result b
# <%= PRODUCT[:name] %>
# <%= PRODUCT[:desc] %>
# END_PRODUCT
- # ERB.new(<<-'END_PRICE'.gsub(/^\s+/, ""), 0, "", "@price").result b
+ # ERB.new(<<-'END_PRICE'.gsub(/^\s+/, ""), trim_mode: "", eoutvar: "@price").result b
# <%= PRODUCT[:name] %> -- <%= PRODUCT[:cost] %>
# <%= PRODUCT[:desc] %>
# END_PRICE
@@ -826,7 +811,22 @@ class ERB
# Chicken Fried Steak -- 9.95
# A well messages pattie, breaded and fried.
#
- def initialize(str, safe_level=nil, trim_mode=nil, eoutvar='_erbout')
+ def initialize(str, safe_level=NOT_GIVEN, legacy_trim_mode=NOT_GIVEN, legacy_eoutvar=NOT_GIVEN, trim_mode: nil, eoutvar: '_erbout')
+ # Complex initializer for $SAFE deprecation at Feature #14256, which should be removed at Ruby 2.7.
+ if safe_level != NOT_GIVEN
+ warn 'Passing safe_level with the 2nd argument of ERB.new is deprecated. Do not use it, and specify other arguments as keyword arguments.', uplevel: 1 if $VERBOSE
+ else
+ safe_level = nil
+ end
+ if legacy_trim_mode != NOT_GIVEN
+ warn 'Passing trim_mode with the 3rd argument of ERB.new is deprecated. Use keyword argument like ERB.new(str, trim_mode: ...) instead.', uplevel: 1 if $VERBOSE
+ trim_mode = legacy_trim_mode
+ end
+ if legacy_eoutvar != NOT_GIVEN
+ warn 'Passing eoutvar with the 4th argument of ERB.new is deprecated. Use keyword argument like ERB.new(str, eoutvar: ...) instead.', uplevel: 1 if $VERBOSE
+ eoutvar = legacy_eoutvar
+ end
+
@safe_level = safe_level
compiler = make_compiler(trim_mode)
set_eoutvar(compiler, eoutvar)
@@ -834,6 +834,8 @@ class ERB
@filename = nil
@lineno = 0
end
+ NOT_GIVEN = Object.new
+ private_constant :NOT_GIVEN
##
# Creates a new compiler for ERB. See ERB::Compiler.new for details
@@ -867,10 +869,10 @@ class ERB
# requires the setup of an ERB _compiler_ object.
#
def set_eoutvar(compiler, eoutvar = '_erbout')
- compiler.put_cmd = "#{eoutvar}.concat"
- compiler.insert_cmd = "#{eoutvar}.concat"
- compiler.pre_cmd = ["#{eoutvar} = String.new"]
- compiler.post_cmd = ["#{eoutvar}.force_encoding(__ENCODING__)"]
+ compiler.put_cmd = "#{eoutvar}.<<"
+ compiler.insert_cmd = "#{eoutvar}.<<"
+ compiler.pre_cmd = ["#{eoutvar} = +''"]
+ compiler.post_cmd = [eoutvar]
end
# Generate results and print them. (see ERB#result)
@@ -888,21 +890,41 @@ class ERB
#
def result(b=new_toplevel)
if @safe_level
- proc {
+ proc do
+ prev_safe_level = $SAFE
$SAFE = @safe_level
eval(@src, b, (@filename || '(erb)'), @lineno)
- }.call
+ ensure
+ $SAFE = prev_safe_level
+ end.call
else
eval(@src, b, (@filename || '(erb)'), @lineno)
end
end
+ # Render a template on a new toplevel binding with local variables specified
+ # by a Hash object.
+ def result_with_hash(hash)
+ b = new_toplevel(hash.keys)
+ hash.each_pair do |key, value|
+ b.local_variable_set(key, value)
+ end
+ result(b)
+ end
+
##
# Returns a new binding each time *near* TOPLEVEL_BINDING for runs that do
# not specify a binding.
- def new_toplevel
- TOPLEVEL_BINDING.dup
+ def new_toplevel(vars = nil)
+ b = TOPLEVEL_BINDING
+ if vars
+ vars = vars.select {|v| b.local_variable_defined?(v)}
+ unless vars.empty?
+ return b.eval("tap {|;#{vars.join(',')}| break binding}")
+ end
+ end
+ b.dup
end
private :new_toplevel
@@ -994,8 +1016,8 @@ class ERB
# 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.unpack("C")[0])
+ s.to_s.b.gsub(/[^a-zA-Z0-9_\-.~]/n) { |m|
+ sprintf("%%%02X", m.unpack1("C"))
}
end
alias u url_encode
diff --git a/lib/fileutils.rb b/lib/fileutils.rb
index efceb2fba7..6332fcd6f1 100644
--- a/lib/fileutils.rb
+++ b/lib/fileutils.rb
@@ -1,4 +1,13 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
+
+begin
+ require 'rbconfig'
+rescue LoadError
+ # for make mjit-headers
+end
+
+require "fileutils/version"
+
#
# = fileutils.rb
#
@@ -16,7 +25,7 @@
# require 'fileutils'
#
# FileUtils.cd(dir, options)
-# FileUtils.cd(dir, options) {|dir| .... }
+# FileUtils.cd(dir, options) {|dir| block }
# FileUtils.pwd()
# FileUtils.mkdir(dir, options)
# FileUtils.mkdir(list, options)
@@ -24,11 +33,11 @@
# FileUtils.mkdir_p(list, options)
# FileUtils.rmdir(dir, options)
# FileUtils.rmdir(list, options)
-# FileUtils.ln(old, new, options)
-# FileUtils.ln(list, destdir, options)
-# FileUtils.ln_s(old, new, options)
-# FileUtils.ln_s(list, destdir, options)
-# FileUtils.ln_sf(src, dest, options)
+# FileUtils.ln(target, link, options)
+# FileUtils.ln(targets, dir, options)
+# FileUtils.ln_s(target, link, options)
+# FileUtils.ln_s(targets, dir, options)
+# FileUtils.ln_sf(target, link, options)
# FileUtils.cp(src, dest, options)
# FileUtils.cp(list, dir, options)
# FileUtils.cp_r(src, dest, options)
@@ -38,7 +47,7 @@
# FileUtils.rm(list, options)
# FileUtils.rm_r(list, options)
# FileUtils.rm_rf(list, options)
-# FileUtils.install(src, dest, mode = <src's>, options)
+# FileUtils.install(src, dest, options)
# FileUtils.chmod(mode, list, options)
# FileUtils.chmod_R(mode, list, options)
# FileUtils.chown(user, group, list, options)
@@ -47,7 +56,7 @@
#
# The <tt>options</tt> parameter is a hash of options, taken from the list
# <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and <tt>:verbose</tt>.
-# <tt>:noop</tt> means that no changes are made. The other two are obvious.
+# <tt>:noop</tt> means that no changes are made. The other three are obvious.
# Each method documents the options that it honours.
#
# All methods that have the concept of a "source" file or directory can take
@@ -56,7 +65,7 @@
#
# There are some `low level' methods, which do not accept any option:
#
-# FileUtils.copy_entry(src, dest, preserve = false, dereference = false)
+# FileUtils.copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
# FileUtils.copy_file(src, dest, preserve = false, dereference = true)
# FileUtils.copy_stream(srcstream, deststream)
# FileUtils.remove_entry(path, force = false)
@@ -84,7 +93,6 @@
# files/directories. This equates to passing the <tt>:noop</tt> and
# <tt>:verbose</tt> flags to methods in FileUtils.
#
-
module FileUtils
def self.private_module_function(name) #:nodoc:
@@ -112,13 +120,14 @@ module FileUtils
# FileUtils.cd('/', :verbose => true) # chdir and report it
#
# FileUtils.cd('/') do # chdir
- # [...] # do something
+ # # ... # do something
# end # return to original directory
#
def cd(dir, verbose: nil, &block) # :yield: dir
fu_output_message "cd #{dir}" if verbose
- Dir.chdir(dir, &block)
+ result = Dir.chdir(dir, &block)
fu_output_message 'cd -' if verbose and block
+ result
end
module_function :cd
@@ -144,7 +153,7 @@ module FileUtils
end
module_function :uptodate?
- def remove_trailing_slash(dir)
+ def remove_trailing_slash(dir) #:nodoc:
dir == '/' ? dir : dir.chomp(?/)
end
private_module_function :remove_trailing_slash
@@ -175,10 +184,11 @@ module FileUtils
# FileUtils.mkdir_p '/usr/local/lib/ruby'
#
# causes to make following directories, if it does not exist.
- # * /usr
- # * /usr/local
- # * /usr/local/lib
- # * /usr/local/lib/ruby
+ #
+ # * /usr
+ # * /usr/local
+ # * /usr/local/lib
+ # * /usr/local/lib/ruby
#
# You can pass several directories at a time in a list.
#
@@ -201,6 +211,7 @@ module FileUtils
stack.push path
path = File.dirname(path)
end
+ stack.pop # root directory should exist
stack.reverse_each do |dir|
begin
fu_mkdir dir, mode
@@ -243,38 +254,39 @@ module FileUtils
fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if verbose
return if noop
list.each do |dir|
- begin
- Dir.rmdir(dir = remove_trailing_slash(dir))
- if parents
+ Dir.rmdir(dir = remove_trailing_slash(dir))
+ if parents
+ begin
until (parent = File.dirname(dir)) == '.' or parent == dir
dir = parent
Dir.rmdir(dir)
end
+ rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT
end
- rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT
end
end
end
module_function :rmdir
#
- # <b><tt>ln(old, new, **options)</tt></b>
+ # :call-seq:
+ # FileUtils.ln(target, link, force: nil, noop: nil, verbose: nil)
+ # FileUtils.ln(target, dir, force: nil, noop: nil, verbose: nil)
+ # FileUtils.ln(targets, dir, force: nil, noop: nil, verbose: nil)
#
- # Creates a hard link +new+ which points to +old+.
- # If +new+ already exists and it is a directory, creates a link +new/old+.
- # If +new+ already exists and it is not a directory, raises Errno::EEXIST.
- # But if :force option is set, overwrite +new+.
+ # 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+.
#
- # FileUtils.ln 'gcc', 'cc', :verbose => true
+ # FileUtils.ln 'gcc', 'cc', verbose: true
# FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
#
- # <b><tt>ln(list, destdir, **options)</tt></b>
+ # 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.
#
- # Creates several hard links in a directory, with each one pointing to the
- # item in +list+. If +destdir+ is not a directory, raises Errno::ENOTDIR.
- #
- # include FileUtils
- # cd '/sbin'
+ # FileUtils.cd '/sbin'
# FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
#
def ln(src, dest, force: nil, noop: nil, verbose: nil)
@@ -291,24 +303,57 @@ module FileUtils
module_function :link
#
- # <b><tt>ln_s(old, new, **options)</tt></b>
+ # :call-seq:
+ # FileUtils.cp_lr(src, dest, noop: nil, verbose: nil, dereference_root: true, remove_destination: false)
#
- # Creates a symbolic link +new+ which points to +old+. If +new+ already
- # exists and it is a directory, creates a symbolic link +new/old+. If +new+
- # already exists and it is not a directory, raises Errno::EEXIST. But if
- # :force option is set, overwrite +new+.
+ # 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+.
#
- # FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
- # FileUtils.ln_s 'verylongsourcefilename.c', 'c', :force => true
+ # +src+ can be a list of files.
+ #
+ # # Installing the library "mylib" under the site_ruby directory.
+ # FileUtils.rm_r site_ruby + '/mylib', :force => true
+ # FileUtils.cp_lr 'lib/', site_ruby + '/mylib'
+ #
+ # # Examples of linking several files to target directory.
+ # FileUtils.cp_lr %w(mail.rb field.rb debug/), site_ruby + '/tmail'
+ # 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.
+ # FileUtils.cp_lr 'src/.', 'dest' # cp_lr('src', 'dest') makes dest/src, but this doesn't.
+ #
+ def cp_lr(src, dest, noop: nil, verbose: nil,
+ dereference_root: true, remove_destination: false)
+ fu_output_message "cp -lr#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
+ return if noop
+ fu_each_src_dest(src, dest) do |s, d|
+ link_entry s, d, dereference_root, remove_destination
+ end
+ end
+ module_function :cp_lr
+
#
- # <b><tt>ln_s(list, destdir, **options)</tt></b>
+ # :call-seq:
+ # FileUtils.ln_s(target, link, force: nil, noop: nil, verbose: nil)
+ # FileUtils.ln_s(target, dir, force: nil, noop: nil, verbose: nil)
+ # FileUtils.ln_s(targets, dir, force: nil, noop: nil, verbose: nil)
#
- # Creates several symbolic links in a directory, with each one pointing to the
- # item in +list+. If +destdir+ is not a directory, raises Errno::ENOTDIR.
+ # In the first form, creates a symbolic link +link+ which points to +target+.
+ # If +link+ already exists, raises Errno::EEXIST.
+ # But if the :force option is set, overwrites +link+.
+ #
+ # FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
+ # FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
#
- # If +destdir+ is not a directory, raises Errno::ENOTDIR.
+ # 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.
#
- # FileUtils.ln_s Dir.glob('bin/*.rb'), '/home/aamine/bin'
+ # FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin'
#
def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
@@ -324,8 +369,12 @@ module FileUtils
module_function :symlink
#
+ # :call-seq:
+ # FileUtils.ln_sf(*args)
+ #
# Same as
- # #ln_s(src, dest, :force => true)
+ #
+ # FileUtils.ln_s(*args, force: true)
#
def ln_sf(src, dest, noop: nil, verbose: nil)
ln_s src, dest, force: true, noop: noop, verbose: verbose
@@ -333,6 +382,26 @@ module FileUtils
module_function :ln_sf
#
+ # Hard links a file system entry +src+ to +dest+.
+ # If +src+ is a directory, this method links its contents recursively.
+ #
+ # Both of +src+ and +dest+ must be a path name.
+ # +src+ must exist, +dest+ must not exist.
+ #
+ # If +dereference_root+ is true, this method dereferences the tree root.
+ #
+ # If +remove_destination+ is true, this method removes each destination file before copy.
+ #
+ def link_entry(src, dest, dereference_root = false, remove_destination = false)
+ Entry_.new(src, nil, dereference_root).traverse do |ent|
+ destent = Entry_.new(dest, ent.rel, false)
+ File.unlink destent.path if remove_destination && File.file?(destent.path)
+ ent.link destent.path
+ end
+ end
+ module_function :link_entry
+
+ #
# Copies a file content +src+ to +dest+. If +dest+ is a directory,
# copies +src+ to +dest/src+.
#
@@ -368,7 +437,7 @@ module FileUtils
#
# # Examples of copying several files to target directory.
# FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
- # FileUtils.cp_r Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop => true, :verbose => true
+ # 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,
@@ -405,7 +474,7 @@ module FileUtils
def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
Entry_.new(src, nil, dereference_root).wrap_traverse(proc do |ent|
destent = Entry_.new(dest, ent.rel, false)
- File.unlink destent.path if remove_destination && File.file?(destent.path)
+ File.unlink destent.path if remove_destination && (File.file?(destent.path) || File.symlink?(destent.path))
ent.copy destent.path
end, proc do |ent|
destent = Entry_.new(dest, ent.rel, false)
@@ -442,7 +511,7 @@ module FileUtils
# FileUtils.mv 'badname.rb', 'goodname.rb'
# FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true # no error
#
- # FileUtils.mv %w(junk.txt dust.txt), '/home/aamine/.trash/'
+ # FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/'
# FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true
#
def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
@@ -454,8 +523,6 @@ module FileUtils
if destent.exist?
if destent.directory?
raise Errno::EEXIST, d
- else
- destent.remove_file if rename_cannot_overwrite_file?
end
end
begin
@@ -478,11 +545,6 @@ module FileUtils
alias move mv
module_function :move
- def rename_cannot_overwrite_file? #:nodoc:
- /emx/ =~ RUBY_PLATFORM
- end
- private_module_function :rename_cannot_overwrite_file?
-
#
# Remove file(s) specified in +list+. This method cannot remove directories.
# All StandardErrors are ignored when the :force option is set.
@@ -508,7 +570,7 @@ module FileUtils
#
# Equivalent to
#
- # #rm(list, :force => true)
+ # FileUtils.rm(list, :force => true)
#
def rm_f(list, noop: nil, verbose: nil)
rm list, force: true, noop: noop, verbose: verbose
@@ -524,7 +586,7 @@ module FileUtils
# StandardError when :force option is set.
#
# FileUtils.rm_r Dir.glob('/tmp/*')
- # FileUtils.rm_r '/', :force => true # :-)
+ # FileUtils.rm_r 'some_dir', :force => true
#
# WARNING: This method causes local vulnerability
# if one of parent directories or removing directory tree are world
@@ -554,7 +616,7 @@ module FileUtils
#
# Equivalent to
#
- # #rm_r(list, :force => true)
+ # FileUtils.rm_r(list, :force => true)
#
# WARNING: This method causes local vulnerability.
# Read the documentation of #rm_r first.
@@ -574,9 +636,9 @@ module FileUtils
# (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.
+ # * Parent directory is world writable (including /tmp).
+ # * Removing directory tree includes world writable directory.
+ # * The system has symbolic link.
#
# To avoid this security hole, this method applies special preprocess.
# If +path+ is a directory, this method chown(2) and chmod(2) all
@@ -594,8 +656,8 @@ module FileUtils
#
# For details of this security vulnerability, see Perl's case:
#
- # http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448
- # http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452
+ # * 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].
#
@@ -619,22 +681,38 @@ module FileUtils
unless parent_st.sticky?
raise ArgumentError, "parent directory is world writable, FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})"
end
+
# freeze tree root
euid = Process.euid
- File.open(fullpath + '/.') {|f|
- unless fu_stat_identical_entry?(st, f.stat)
- # symlink (TOC-to-TOU attack?)
- File.unlink fullpath
- return
- end
- f.chown euid, -1
- f.chmod 0700
- unless fu_stat_identical_entry?(st, File.lstat(fullpath))
- # TOC-to-TOU attack?
- File.unlink fullpath
- return
- end
- }
+ dot_file = fullpath + "/."
+ begin
+ File.open(dot_file) {|f|
+ unless fu_stat_identical_entry?(st, f.stat)
+ # symlink (TOC-to-TOU attack?)
+ File.unlink fullpath
+ return
+ end
+ f.chown euid, -1
+ f.chmod 0700
+ }
+ rescue Errno::EISDIR # JRuby in non-native mode can't open files as dirs
+ File.lstat(dot_file).tap {|fstat|
+ unless fu_stat_identical_entry?(st, fstat)
+ # symlink (TOC-to-TOU attack?)
+ File.unlink fullpath
+ return
+ end
+ File.chown euid, -1, dot_file
+ File.chmod 0700, dot_file
+ }
+ end
+
+ unless fu_stat_identical_entry?(st, File.lstat(fullpath))
+ # TOC-to-TOU attack?
+ File.unlink fullpath
+ return
+ end
+
# ---- tree root is frozen ----
root = Entry_.new(path)
root.preorder_traverse do |ent|
@@ -710,10 +788,10 @@ module FileUtils
module_function :remove_dir
#
- # Returns true if the contents of a file A and a file B are identical.
+ # Returns true if the contents of a file +a+ and a file +b+ are identical.
#
- # FileUtils.compare_file('somefile', 'somefile') #=> true
- # FileUtils.compare_file('/bin/cp', '/bin/mv') #=> maybe false
+ # FileUtils.compare_file('somefile', 'somefile') #=> true
+ # FileUtils.compare_file('/dev/null', '/dev/urandom') #=> false
#
def compare_file(a, b)
return false unless File.size(a) == File.size(b)
@@ -735,8 +813,15 @@ module FileUtils
#
def compare_stream(a, b)
bsize = fu_stream_blksize(a, b)
- sa = ""
- sb = ""
+
+ if RUBY_VERSION > "2.4"
+ sa = String.new(capacity: bsize)
+ sb = String.new(capacity: bsize)
+ else
+ sa = String.new
+ sb = String.new
+ end
+
begin
a.read(bsize, sa)
b.read(bsize, sb)
@@ -799,7 +884,7 @@ module FileUtils
end
private_module_function :user_mask
- def apply_mask(mode, user_mask, op, mode_mask)
+ def apply_mask(mode, user_mask, op, mode_mask) #:nodoc:
case op
when '='
(mode & ~user_mask) | (user_mask & mode_mask)
@@ -1060,8 +1145,11 @@ module FileUtils
module StreamUtils_
private
- def fu_windows?
- /mswin|mingw|bccwin|emx/ =~ RUBY_PLATFORM
+ case (defined?(::RbConfig) ? ::RbConfig::CONFIG['host_os'] : ::RUBY_PLATFORM)
+ when /mswin|mingw/
+ def fu_windows?; true end
+ else
+ def fu_windows?; false end
end
def fu_copy_stream0(src, dest, blksize = nil) #:nodoc:
@@ -1186,8 +1274,7 @@ module FileUtils
def entries
opts = {}
opts[:encoding] = ::Encoding::UTF_8 if fu_windows?
- Dir.entries(path(), opts)\
- .reject {|n| n == '.' or n == '..' }\
+ Dir.children(path, opts)\
.map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) }
end
@@ -1233,6 +1320,7 @@ module FileUtils
else
File.chmod mode, path()
end
+ rescue Errno::EOPNOTSUPP
end
def chown(uid, gid)
@@ -1243,6 +1331,22 @@ module FileUtils
end
end
+ def link(dest)
+ case
+ when directory?
+ if !File.exist?(dest) and descendant_directory?(dest, path)
+ raise ArgumentError, "cannot link directory %s to itself %s" % [path, dest]
+ end
+ begin
+ Dir.mkdir dest
+ rescue
+ raise unless File.directory?(dest)
+ end
+ else
+ File.link path(), dest
+ end
+ end
+
def copy(dest)
lstat
case
@@ -1308,7 +1412,7 @@ module FileUtils
if st.symlink?
begin
File.lchmod mode, path
- rescue NotImplementedError
+ rescue NotImplementedError, Errno::EOPNOTSUPP
end
else
File.chmod mode, path
@@ -1432,9 +1536,9 @@ module FileUtils
end
if File::ALT_SEPARATOR
- DIRECTORY_TERM = "(?=[/#{Regexp.quote(File::ALT_SEPARATOR)}]|\\z)".freeze
+ DIRECTORY_TERM = "(?=[/#{Regexp.quote(File::ALT_SEPARATOR)}]|\\z)"
else
- DIRECTORY_TERM = "(?=/|\\z)".freeze
+ DIRECTORY_TERM = "(?=/|\\z)"
end
SYSCASE = File::FNM_SYSCASE.nonzero? ? "-i" : ""
diff --git a/lib/fileutils/fileutils.gemspec b/lib/fileutils/fileutils.gemspec
new file mode 100644
index 0000000000..7212883d2b
--- /dev/null
+++ b/lib/fileutils/fileutils.gemspec
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+begin
+ require_relative "lib/fileutils/version"
+rescue LoadError
+ # for Ruby core repository
+ require_relative "version"
+end
+
+Gem::Specification.new do |s|
+ s.name = "fileutils"
+ s.version = FileUtils::VERSION
+ s.summary = "Several file utility methods for copying, moving, removing, etc."
+ s.description = "Several file utility methods for copying, moving, removing, etc."
+
+ s.require_path = %w{lib}
+ s.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "fileutils.gemspec", "lib/fileutils.rb", "lib/fileutils/version.rb"]
+ s.required_ruby_version = ">= 2.3.0"
+
+ s.authors = ["Minero Aoki"]
+ s.email = [nil]
+ s.homepage = "https://github.com/ruby/fileutils"
+ s.license = "BSD-2-Clause"
+
+ if s.respond_to?(:metadata=)
+ s.metadata = {
+ "source_code_uri" => "https://github.com/ruby/fileutils"
+ }
+ end
+
+ s.add_development_dependency 'rake'
+end
diff --git a/lib/fileutils/version.rb b/lib/fileutils/version.rb
new file mode 100644
index 0000000000..e82734dfec
--- /dev/null
+++ b/lib/fileutils/version.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+module FileUtils
+ VERSION = "1.1.0"
+end
diff --git a/lib/find.rb b/lib/find.rb
index 093f8557c3..f97cc1b836 100644
--- a/lib/find.rb
+++ b/lib/find.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# find.rb: the Find module for processing all files under a given directory.
#
@@ -55,14 +55,13 @@ module Find
end
if s.directory? then
begin
- fs = Dir.entries(file, encoding: enc)
+ fs = Dir.children(file, encoding: enc)
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
raise unless ignore_error
next
end
fs.sort!
fs.reverse_each {|f|
- next if f == "." or f == ".."
f = File.join(file, f)
ps.unshift f.untaint
}
diff --git a/lib/forwardable.rb b/lib/forwardable.rb
index b94c9a3e61..bcd462a97c 100644
--- a/lib/forwardable.rb
+++ b/lib/forwardable.rb
@@ -110,8 +110,11 @@
# +delegate.rb+.
#
module Forwardable
+ require 'forwardable/impl'
+
# Version of +forwardable.rb+
- FORWARDABLE_VERSION = "1.1.0"
+ VERSION = "1.2.0"
+ FORWARDABLE_VERSION = VERSION
@debug = nil
class << self
@@ -128,12 +131,13 @@ module Forwardable
# delegate [method, method, ...] => accessor
#
def instance_delegate(hash)
- hash.each{ |methods, accessor|
- methods = [methods] unless methods.respond_to?(:each)
- methods.each{ |method|
- def_instance_delegator(accessor, method)
- }
- }
+ hash.each do |methods, accessor|
+ unless defined?(methods.each)
+ def_instance_delegator(accessor, methods)
+ else
+ methods.each {|method| def_instance_delegator(accessor, method)}
+ end
+ end
end
#
@@ -195,21 +199,15 @@ module Forwardable
accessor = "#{accessor}()"
end
- vm = RubyVM::InstructionSequence
method_call = ".__send__(:#{method}, *args, &block)"
- if begin
- iseq = vm.compile("().#{method}", nil, nil, 0, false)
- rescue SyntaxError
- else
- iseq.to_a.dig(-1, 1, 1, :mid) == method.to_sym
- end
+ if _valid_method?(method)
loc, = caller_locations(2,1)
pre = "_ ="
mesg = "#{Module === obj ? obj : obj.class}\##{ali} at #{loc.path}:#{loc.lineno} forwarding to private method "
method_call = "#{<<-"begin;"}\n#{<<-"end;".chomp}"
begin;
- unless ::Kernel.instance_method(:respond_to?).bind(_).call(:"#{method}")
- ::Kernel.warn "\#{caller_locations(1)[0]}: "#{mesg.dump}"\#{_.class}"'##{method}'
+ unless defined? _.#{method}
+ ::Kernel.warn #{mesg.dump}"\#{_.class}"'##{method}', uplevel: 1
_#{method_call}
else
_.#{method}(*args, &block)
@@ -217,7 +215,7 @@ module Forwardable
end;
end
- line_no = __LINE__+1; str = "#{<<-"begin;"}\n#{<<-"end;"}"
+ _compile_method("#{<<-"begin;"}\n#{<<-"end;"}", __FILE__, __LINE__+1)
begin;
proc do
def #{ali}(*args, &block)
@@ -228,11 +226,6 @@ module Forwardable
end
end
end;
-
- vm.compile(str, __FILE__, __FILE__, line_no,
- trace_instruction: false,
- tailcall_optimization: true)
- .eval
end
end
@@ -270,12 +263,13 @@ module SingleForwardable
# delegate [method, method, ...] => accessor
#
def single_delegate(hash)
- hash.each{ |methods, accessor|
- methods = [methods] unless methods.respond_to?(:each)
- methods.each{ |method|
- def_single_delegator(accessor, method)
- }
- }
+ hash.each do |methods, accessor|
+ unless defined?(methods.each)
+ def_single_delegator(accessor, methods)
+ else
+ methods.each {|method| def_single_delegator(accessor, method)}
+ end
+ end
end
#
diff --git a/lib/forwardable/forwardable.gemspec b/lib/forwardable/forwardable.gemspec
new file mode 100644
index 0000000000..f90b82269d
--- /dev/null
+++ b/lib/forwardable/forwardable.gemspec
@@ -0,0 +1,26 @@
+begin
+ require_relative "lib/forwardable"
+rescue LoadError
+ # for Ruby core repository
+ require_relative "../forwardable"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "forwardable"
+ spec.version = Forwardable::VERSION
+ spec.authors = ["Keiju ISHITSUKA"]
+ spec.email = ["keiju@ruby-lang.org"]
+
+ spec.summary = %q{Provides delegation of specified methods to a designated object.}
+ spec.description = %q{Provides delegation of specified methods to a designated object.}
+ spec.homepage = "https://github.com/ruby/forwardable"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "forwardable.gemspec", "lib/forwardable.rb", "lib/forwardable/impl.rb"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+end
diff --git a/lib/forwardable/impl.rb b/lib/forwardable/impl.rb
new file mode 100644
index 0000000000..58a9dfb69c
--- /dev/null
+++ b/lib/forwardable/impl.rb
@@ -0,0 +1,16 @@
+# :stopdoc:
+module Forwardable
+ def self._valid_method?(method)
+ catch {|tag|
+ eval("BEGIN{throw tag}; ().#{method}", binding, __FILE__, __LINE__)
+ }
+ rescue SyntaxError
+ false
+ else
+ true
+ end
+
+ def self._compile_method(src, file, line)
+ eval(src, nil, file, line)
+ end
+end
diff --git a/lib/getoptlong.rb b/lib/getoptlong.rb
index c0ba5399b7..ff7674f1cb 100644
--- a/lib/getoptlong.rb
+++ b/lib/getoptlong.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# GetoptLong for Ruby
#
@@ -310,7 +310,7 @@ class GetoptLong
#
next if i == argument_flag
begin
- if !i.is_a?(String) || i !~ /^-([^-]|-.+)$/
+ if !i.is_a?(String) || i !~ /\A-([^-]|-.+)\z/
raise ArgumentError, "an invalid option `#{i}'"
end
if (@canonical_names.include?(i))
@@ -447,7 +447,7 @@ class GetoptLong
terminate
return nil
elsif @ordering == PERMUTE
- while 0 < ARGV.length && ARGV[0] !~ /^-./
+ while 0 < ARGV.length && ARGV[0] !~ /\A-./
@non_option_arguments.push(ARGV.shift)
end
if ARGV.length == 0
@@ -456,7 +456,7 @@ class GetoptLong
end
argument = ARGV.shift
elsif @ordering == REQUIRE_ORDER
- if (ARGV[0] !~ /^-./)
+ if (ARGV[0] !~ /\A-./)
terminate
return nil
end
@@ -477,7 +477,7 @@ class GetoptLong
#
# Check for long and short options.
#
- if argument =~ /^(--[^=]+)/ && @rest_singles.length == 0
+ if argument =~ /\A(--[^=]+)/ && @rest_singles.length == 0
#
# This is a long style option, which start with `--'.
#
@@ -507,7 +507,7 @@ class GetoptLong
# Check an argument to the option.
#
if @argument_flags[option_name] == REQUIRED_ARGUMENT
- if argument =~ /=(.*)$/
+ if argument =~ /=(.*)/m
option_argument = $1
elsif 0 < ARGV.length
option_argument = ARGV.shift
@@ -516,19 +516,19 @@ class GetoptLong
"option `#{argument}' requires an argument")
end
elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT
- if argument =~ /=(.*)$/
+ if argument =~ /=(.*)/m
option_argument = $1
- elsif 0 < ARGV.length && ARGV[0] !~ /^-./
+ elsif 0 < ARGV.length && ARGV[0] !~ /\A-./
option_argument = ARGV.shift
else
option_argument = ''
end
- elsif argument =~ /=(.*)$/
+ elsif argument =~ /=(.*)/m
set_error(NeedlessArgument,
"option `#{option_name}' doesn't allow an argument")
end
- elsif argument =~ /^(-(.))(.*)/
+ elsif argument =~ /\A(-(.))(.*)/m
#
# This is a short style option, which start with `-' (not `--').
# Short options may be catenated (e.g. `-l -g' is equivalent to
@@ -555,7 +555,7 @@ class GetoptLong
if 0 < @rest_singles.length
option_argument = @rest_singles
@rest_singles = ''
- elsif 0 < ARGV.length && ARGV[0] !~ /^-./
+ elsif 0 < ARGV.length && ARGV[0] !~ /\A-./
option_argument = ARGV.shift
else
option_argument = ''
diff --git a/lib/ipaddr.gemspec b/lib/ipaddr.gemspec
new file mode 100644
index 0000000000..2de9ef4881
--- /dev/null
+++ b/lib/ipaddr.gemspec
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+# coding: utf-8
+lib = File.expand_path("../lib", __FILE__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+
+Gem::Specification.new do |spec|
+ spec.name = "ipaddr"
+ spec.version = "1.2.2"
+ spec.authors = ["Akinori MUSHA", "Hajimu UMEMOTO"]
+ spec.email = ["knu@idaemons.org", "ume@mahoroba.org"]
+
+ spec.summary = %q{A class to manipulate an IP address in ruby}
+ spec.description = <<-'DESCRIPTION'
+IPAddr provides a set of methods to manipulate an IP address.
+Both IPv4 and IPv6 are supported.
+ DESCRIPTION
+ spec.homepage = "https://github.com/ruby/ipaddr"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "ipaddr.gemspec", "lib/ipaddr.rb"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler", "~> 1.15"
+ spec.add_development_dependency "rake", "~> 10.0"
+ spec.add_development_dependency "test-unit"
+end
diff --git a/lib/ipaddr.rb b/lib/ipaddr.rb
index 6f70ebf773..7fff54b9d0 100644
--- a/lib/ipaddr.rb
+++ b/lib/ipaddr.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# ipaddr.rb - A class to manipulate an IP address
#
@@ -103,13 +103,13 @@ class IPAddr
# Creates a new ipaddr containing the given network byte ordered
# string form of an IP address.
- def IPAddr::new_ntoh(addr)
- return IPAddr.new(IPAddr::ntop(addr))
+ def self.new_ntoh(addr)
+ return new(ntop(addr))
end
# Convert a network byte ordered string form of an IP address into
# human readable form.
- def IPAddr::ntop(addr)
+ def self.ntop(addr)
case addr.size
when 4
s = addr.unpack('C4').join('.')
@@ -259,6 +259,50 @@ class IPAddr
return @family == Socket::AF_INET6
end
+ # Returns true if the ipaddr is a loopback address.
+ def loopback?
+ case @family
+ when Socket::AF_INET
+ @addr & 0xff000000 == 0x7f000000
+ when Socket::AF_INET6
+ @addr == 1
+ else
+ raise AddressFamilyError, "unsupported address family"
+ end
+ end
+
+ # Returns true if the ipaddr is a private address. IPv4 addresses
+ # in 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16 as defined in RFC
+ # 1918 and IPv6 Unique Local Addresses in fc00::/7 as defined in RFC
+ # 4193 are considered private.
+ def private?
+ case @family
+ when Socket::AF_INET
+ @addr & 0xff000000 == 0x0a000000 || # 10.0.0.0/8
+ @addr & 0xfff00000 == 0xac100000 || # 172.16.0.0/12
+ @addr & 0xffff0000 == 0xc0a80000 # 192.168.0.0/16
+ when Socket::AF_INET6
+ @addr & 0xfe00_0000_0000_0000_0000_0000_0000_0000 == 0xfc00_0000_0000_0000_0000_0000_0000_0000
+ else
+ raise AddressFamilyError, "unsupported address family"
+ end
+ end
+
+ # Returns true if the ipaddr is a link-local address. IPv4
+ # addresses in 169.254.0.0/16 reserved by RFC 3927 and Link-Local
+ # IPv6 Unicast Addresses in fe80::/10 reserved by RFC 4291 are
+ # considered link-local.
+ def link_local?
+ case @family
+ when Socket::AF_INET
+ @addr & 0xffff0000 == 0xa9fe0000 # 169.254.0.0/16
+ when Socket::AF_INET6
+ @addr & 0xffc0_0000_0000_0000_0000_0000_0000_0000 == 0xfe80_0000_0000_0000_0000_0000_0000_0000
+ else
+ raise AddressFamilyError, "unsupported address family"
+ end
+ end
+
# Returns true if the ipaddr is an IPv4-mapped IPv6 address.
def ipv4_mapped?
return ipv6? && (@addr >> 32) == 0xffff
@@ -266,6 +310,11 @@ class IPAddr
# Returns true if the ipaddr is an IPv4-compatible IPv6 address.
def ipv4_compat?
+ warn "IPAddr\##{__callee__} is obsolete", uplevel: 1 if $VERBOSE
+ _ipv4_compat?
+ end
+
+ def _ipv4_compat?
if !ipv6? || (@addr >> 32) != 0
return false
end
@@ -273,6 +322,8 @@ class IPAddr
return a != 0 && a != 1
end
+ private :_ipv4_compat?
+
# Returns a new ipaddr built by converting the native IPv4 address
# into an IPv4-mapped IPv6 address.
def ipv4_mapped
@@ -285,6 +336,7 @@ class IPAddr
# Returns a new ipaddr built by converting the native IPv4 address
# into an IPv4-compatible IPv6 address.
def ipv4_compat
+ warn "IPAddr\##{__callee__} is obsolete", uplevel: 1 if $VERBOSE
if !ipv4?
raise InvalidAddressError, "not an IPv4 address"
end
@@ -295,7 +347,7 @@ class IPAddr
# native IPv4 address. If the IP address is not an IPv4-mapped or
# IPv4-compatible IPv6 address, returns self.
def native
- if !ipv4_mapped? && !ipv4_compat?
+ if !ipv4_mapped? && !_ipv4_compat?
return self
end
return self.clone.set(@addr & IN4MASK, Socket::AF_INET)
@@ -371,6 +423,35 @@ class IPAddr
return clone.set(begin_addr, @family)..clone.set(end_addr, @family)
end
+ # Returns the prefix length in bits for the ipaddr.
+ def prefix
+ case @family
+ when Socket::AF_INET
+ n = IN4MASK ^ @mask_addr
+ i = 32
+ when Socket::AF_INET6
+ n = IN6MASK ^ @mask_addr
+ i = 128
+ else
+ raise AddressFamilyError, "unsupported address family"
+ end
+ while n.positive?
+ n >>= 1
+ i -= 1
+ end
+ i
+ end
+
+ # Sets the prefix length in bits
+ def prefix=(prefix)
+ case prefix
+ when Integer
+ mask!(prefix)
+ else
+ raise InvalidPrefixError, "prefix must be an integer"
+ end
+ end
+
# Returns a string containing a human-readable representation of the
# ipaddr. ("#<IPAddr: family:address/mask>")
def inspect
@@ -413,7 +494,8 @@ class IPAddr
# Set current netmask to given mask.
def mask!(mask)
- if mask.kind_of?(String)
+ case mask
+ when String
if mask =~ /\A\d+\z/
prefixlen = mask.to_i
else
@@ -422,6 +504,10 @@ class IPAddr
raise InvalidPrefixError, "address family is not same"
end
@mask_addr = m.to_i
+ n = @mask_addr ^ m.instance_variable_get(:@mask_addr)
+ unless ((n + 1) & n).zero?
+ raise InvalidPrefixError, "invalid mask #{mask}"
+ end
@addr &= @mask_addr
return self
end
@@ -508,6 +594,8 @@ class IPAddr
else
@mask_addr = (@family == Socket::AF_INET) ? IN4MASK : IN6MASK
end
+ rescue InvalidAddressError => e
+ raise e.class, "#{e.message}: #{addr}"
end
def coerce_other(other)
diff --git a/lib/irb.rb b/lib/irb.rb
index 676abe42cd..ee3e649e81 100644
--- a/lib/irb.rb
+++ b/lib/irb.rb
@@ -19,6 +19,8 @@ require "irb/ruby-lex"
require "irb/input-method"
require "irb/locale"
+require "irb/version"
+
# IRB stands for "interactive Ruby" and is a tool to interactively execute Ruby
# expressions read from the standard input.
#
@@ -48,7 +50,6 @@ require "irb/locale"
#
# Usage: irb.rb [options] [programfile] [arguments]
# -f Suppress read of ~/.irbrc
-# -m Bc mode (load mathn, fraction or matrix are available)
# -d Set $DEBUG to true (same as `ruby -d')
# -r load-module Same as `ruby -r'
# -I path Specify $LOAD_PATH directory
@@ -90,7 +91,6 @@ require "irb/locale"
# as follows in an +irb+ session:
#
# IRB.conf[:IRB_NAME]="irb"
-# IRB.conf[:MATH_MODE]=false
# IRB.conf[:INSPECT_MODE]=nil
# IRB.conf[:IRB_RC] = nil
# IRB.conf[:BACK_TRACE_LIMIT]=16
@@ -356,9 +356,7 @@ module IRB
def IRB.version
if v = @CONF[:VERSION] then return v end
- require "irb/version"
- rv = @RELEASE_VERSION.sub(/\.0/, "")
- @CONF[:VERSION] = format("irb %s(%s)", rv, @LAST_UPDATE_DATE)
+ @CONF[:VERSION] = format("irb %s (%s)", @RELEASE_VERSION, @LAST_UPDATE_DATE)
end
# The current IRB::Context of the session, see IRB.conf
@@ -441,6 +439,8 @@ module IRB
# Evaluates input for this session.
def eval_input
+ exc = nil
+
@scanner.set_prompt do
|ltype, indent, continue, line_no|
if ltype
@@ -490,54 +490,74 @@ module IRB
signal_status(:IN_EVAL) do
begin
line.untaint
- @context.evaluate(line, line_no)
+ @context.evaluate(line, line_no, exception: exc)
output_value if @context.echo?
- exc = nil
rescue Interrupt => exc
rescue SystemExit, SignalException
raise
rescue Exception => exc
+ else
+ exc = nil
+ next
end
- if exc
- print exc.class, ": ", exc, "\n"
- if exc.backtrace && exc.backtrace[0] =~ /irb(2)?(\/.*|-.*|\.rb)?:/ && exc.class.to_s !~ /^IRB/ &&
- !(SyntaxError === exc)
- irb_bug = true
- else
- irb_bug = false
- end
+ handle_exception(exc)
+ end
+ end
+ end
- messages = []
- lasts = []
- levels = 0
- if exc.backtrace
- for m in exc.backtrace
- m = @context.workspace.filter_backtrace(m) unless irb_bug
- if m
- if messages.size < @context.back_trace_limit
- messages.push "\tfrom "+m
- else
- lasts.push "\tfrom "+m
- if lasts.size > @context.back_trace_limit
- lasts.shift
- levels += 1
- end
- end
- end
- end
- end
- print messages.join("\n"), "\n"
- unless lasts.empty?
- printf "... %d levels...\n", levels if levels > 0
- print lasts.join("\n"), "\n"
- end
- print "Maybe IRB bug!\n" if irb_bug
+ def handle_exception(exc)
+ if exc.backtrace && exc.backtrace[0] =~ /irb(2)?(\/.*|-.*|\.rb)?:/ && exc.class.to_s !~ /^IRB/ &&
+ !(SyntaxError === exc)
+ irb_bug = true
+ else
+ irb_bug = false
+ end
+
+ if STDOUT.tty?
+ attr = ATTR_TTY
+ print "#{attr[1]}Traceback#{attr[]} (most recent call last):\n"
+ else
+ attr = ATTR_PLAIN
+ end
+ messages = []
+ lasts = []
+ levels = 0
+ if exc.backtrace
+ count = 0
+ exc.backtrace.each do |m|
+ m = @context.workspace.filter_backtrace(m) or next unless irb_bug
+ count += 1
+ if attr == ATTR_TTY
+ m = sprintf("%9d: from %s", count, m)
+ else
+ m = "\tfrom #{m}"
end
- if $SAFE > 2
- abort "Error: irb does not work for $SAFE level higher than 2"
+ if messages.size < @context.back_trace_limit
+ messages.push(m)
+ elsif lasts.size < @context.back_trace_limit
+ lasts.push(m).shift
+ levels += 1
end
end
end
+ if attr == ATTR_TTY
+ unless lasts.empty?
+ puts lasts.reverse
+ printf "... %d levels...\n", levels if levels > 0
+ end
+ puts messages.reverse
+ end
+ m = exc.to_s.split(/\n/)
+ print "#{attr[1]}#{exc.class} (#{attr[4]}#{m.shift}#{attr[0, 1]})#{attr[]}\n"
+ puts m.map {|s| "#{attr[1]}#{s}#{attr[]}\n"}
+ if attr == ATTR_PLAIN
+ puts messages
+ unless lasts.empty?
+ puts lasts
+ printf "... %d levels...\n", levels if levels > 0
+ end
+ end
+ print "Maybe IRB bug!\n" if irb_bug
end
# Evaluates the given block using the given +path+ as the Context#irb_path
@@ -681,6 +701,11 @@ module IRB
end
format("#<%s: %s>", self.class, ary.join(", "))
end
+
+ ATTR_TTY = "\e[%sm"
+ def ATTR_TTY.[](*a) self % a.join(";"); end
+ ATTR_PLAIN = ""
+ def ATTR_PLAIN.[](*) self; end
end
def @CONF.inspect
@@ -707,9 +732,67 @@ module IRB
end
class Binding
- # :nodoc:
+ # Opens an IRB session where +binding.irb+ is called which allows for
+ # interactive debugging. You can call any methods or variables available in
+ # the current scope, and mutate state if you need to.
+ #
+ #
+ # Given a Ruby file called +potato.rb+ containing the following code:
+ #
+ # class Potato
+ # def initialize
+ # @cooked = false
+ # binding.irb
+ # puts "Cooked potato: #{@cooked}"
+ # end
+ # end
+ #
+ # Potato.new
+ #
+ # Running +ruby potato.rb+ will open an IRB session where +binding.irb+ is
+ # called, and you will see the following:
+ #
+ # $ ruby potato.rb
+ #
+ # From: potato.rb @ line 4 :
+ #
+ # 1: class Potato
+ # 2: def initialize
+ # 3: @cooked = false
+ # => 4: binding.irb
+ # 5: puts "Cooked potato: #{@cooked}"
+ # 6: end
+ # 7: end
+ # 8:
+ # 9: Potato.new
+ #
+ # irb(#<Potato:0x00007feea1916670>):001:0>
+ #
+ # You can type any valid Ruby code and it will be evaluated in the current
+ # context. This allows you to debug without having to run your code repeatedly:
+ #
+ # irb(#<Potato:0x00007feea1916670>):001:0> @cooked
+ # => false
+ # irb(#<Potato:0x00007feea1916670>):002:0> self.class
+ # => Potato
+ # irb(#<Potato:0x00007feea1916670>):003:0> caller.first
+ # => ".../2.5.1/lib/ruby/2.5.0/irb/workspace.rb:85:in `eval'"
+ # irb(#<Potato:0x00007feea1916670>):004:0> @cooked = true
+ # => true
+ #
+ # You can exit the IRB session with the `exit` command. Note that exiting will
+ # resume execution where +binding.irb+ had paused it, as you can see from the
+ # output printed to standard output in this example:
+ #
+ # irb(#<Potato:0x00007feea1916670>):005:0> exit
+ # Cooked potato: true
+ #
+ #
+ # See IRB@IRB+Usage for more information.
def irb
- IRB.setup(eval("__FILE__"))
- IRB::Irb.new(IRB::WorkSpace.new(self)).run(IRB.conf)
+ IRB.setup(eval("__FILE__"), argv: [])
+ workspace = IRB::WorkSpace.new(self)
+ STDOUT.print(workspace.code_around_binding)
+ IRB::Irb.new(workspace).run(IRB.conf)
end
end
diff --git a/lib/irb/cmd/chws.rb b/lib/irb/cmd/chws.rb
index e93c976f82..e9f257791c 100644
--- a/lib/irb/cmd/chws.rb
+++ b/lib/irb/cmd/chws.rb
@@ -10,8 +10,8 @@
#
#
-require "irb/cmd/nop.rb"
-require "irb/ext/change-ws.rb"
+require_relative "nop"
+require_relative "../ext/change-ws"
# :stopdoc:
module IRB
diff --git a/lib/irb/cmd/help.rb b/lib/irb/cmd/help.rb
index db2bd567e5..71590ee844 100644
--- a/lib/irb/cmd/help.rb
+++ b/lib/irb/cmd/help.rb
@@ -11,7 +11,7 @@
require 'rdoc/ri/driver'
-require "irb/cmd/nop.rb"
+require_relative "nop"
# :stopdoc:
module IRB
diff --git a/lib/irb/cmd/load.rb b/lib/irb/cmd/load.rb
index f800b741eb..b6769a4124 100644
--- a/lib/irb/cmd/load.rb
+++ b/lib/irb/cmd/load.rb
@@ -10,8 +10,8 @@
#
#
-require "irb/cmd/nop.rb"
-require "irb/ext/loader"
+require_relative "nop"
+require_relative "../ext/loader"
# :stopdoc:
module IRB
diff --git a/lib/irb/cmd/pushws.rb b/lib/irb/cmd/pushws.rb
index ffe55abed6..187b276e48 100644
--- a/lib/irb/cmd/pushws.rb
+++ b/lib/irb/cmd/pushws.rb
@@ -10,8 +10,8 @@
#
#
-require "irb/cmd/nop.rb"
-require "irb/ext/workspaces.rb"
+require_relative "nop"
+require_relative "../ext/workspaces"
# :stopdoc:
module IRB
diff --git a/lib/irb/cmd/subirb.rb b/lib/irb/cmd/subirb.rb
index c1602f6e45..1e18607d1a 100644
--- a/lib/irb/cmd/subirb.rb
+++ b/lib/irb/cmd/subirb.rb
@@ -9,8 +9,8 @@
#
#
-require "irb/cmd/nop.rb"
-require "irb/ext/multi-irb"
+require_relative "nop"
+require_relative "../ext/multi-irb"
# :stopdoc:
module IRB
diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb
index e7499a8e2b..390e7254dd 100644
--- a/lib/irb/completion.rb
+++ b/lib/irb/completion.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
#
-# irb/completor.rb -
+# irb/completion.rb -
# $Release Version: 0.9$
# $Revision$
# by Keiju ISHITSUKA(keiju@ishitsuka.com)
@@ -169,18 +169,9 @@ module IRB
else
# func1.func2
candidates = []
+ to_ignore = ignored_modules
ObjectSpace.each_object(Module){|m|
- begin
- name = m.name
- rescue Exception
- name = ""
- end
- begin
- next if name != "IRB::Context" and
- /^(IRB|SLex|RubyLex|RubyToken)/ =~ name
- rescue Exception
- next
- end
+ next if (to_ignore.include?(m) rescue true)
candidates.concat m.instance_methods(false).collect{|x| x.to_s}
}
candidates.sort!
@@ -218,6 +209,31 @@ module IRB
end
end
end
+
+ def self.ignored_modules
+ # We could cache the result, but this is very fast already.
+ # By using this approach, we avoid Module#name calls, which are
+ # relatively slow when there are a lot of anonymous modules defined.
+ s = {}
+
+ scanner = lambda do |m|
+ next if s.include?(m) # IRB::ExtendCommandBundle::EXCB recurses.
+ s[m] = true
+ m.constants(false).each do |c|
+ value = m.const_get(c)
+ scanner.call(value) if value.is_a?(Module)
+ end
+ end
+
+ %i(IRB SLex RubyLex RubyToken).each do |sym|
+ next unless Object.const_defined?(sym)
+ scanner.call(Object.const_get(sym))
+ end
+
+ s.delete(IRB::Context) if defined?(IRB::Context)
+
+ s
+ end
end
end
diff --git a/lib/irb/context.rb b/lib/irb/context.rb
index 4cf6128930..e8e6a118e6 100644
--- a/lib/irb/context.rb
+++ b/lib/irb/context.rb
@@ -9,8 +9,10 @@
#
#
#
-require "irb/workspace"
-require "irb/inspector"
+require_relative "workspace"
+require_relative "inspector"
+require_relative "input-method"
+require_relative "output-method"
module IRB
# A class that wraps the current state of the irb session, including the
@@ -42,7 +44,6 @@ module IRB
@io = nil
self.inspect_mode = IRB.conf[:INSPECT_MODE]
- self.math_mode = IRB.conf[:MATH_MODE] if IRB.conf[:MATH_MODE]
self.use_tracer = IRB.conf[:USE_TRACER] if IRB.conf[:USE_TRACER]
self.use_loader = IRB.conf[:USE_LOADER] if IRB.conf[:USE_LOADER]
self.eval_history = IRB.conf[:EVAL_HISTORY] if IRB.conf[:EVAL_HISTORY]
@@ -261,7 +262,7 @@ module IRB
# to #last_value.
def set_last_value(value)
@last_value = value
- @workspace.evaluate self, "_ = IRB.CurrentContext.last_value"
+ @workspace.local_variable_set :_, value
end
# Sets the +mode+ of the prompt in this context.
@@ -375,8 +376,12 @@ module IRB
@debug_level > 0
end
- def evaluate(line, line_no) # :nodoc:
+ def evaluate(line, line_no, exception: nil) # :nodoc:
@line_no = line_no
+ if exception
+ line = "begin ::Kernel.raise _; rescue _.class; #{line}; end"
+ @workspace.local_variable_set(:_, exception)
+ end
set_last_value(@workspace.evaluate(self, line, irb_path, line_no))
end
diff --git a/lib/irb/ext/math-mode.rb b/lib/irb/ext/math-mode.rb
deleted file mode 100644
index e409dbdc6a..0000000000
--- a/lib/irb/ext/math-mode.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-# frozen_string_literal: false
-#
-# math-mode.rb -
-# $Release Version: 0.9.6$
-# $Revision$
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#
-# --
-#
-#
-#
-require "mathn"
-
-module IRB
- class Context
- # Returns whether bc mode is enabled.
- #
- # See #math_mode=
- attr_reader :math_mode
- # Alias for #math_mode
- alias math? math_mode
-
- # Sets bc mode, which loads +lib/mathn.rb+ so fractions or matrix are
- # available.
- #
- # Also available as the +-m+ command line option.
- #
- # See IRB@Command+line+options and the unix manpage <code>bc(1)</code> for
- # more information.
- def math_mode=(opt)
- if @math_mode == true && !opt
- IRB.fail CantReturnToNormalMode
- return
- end
-
- @math_mode = opt
- if math_mode
- main.extend Math
- print "start math mode\n" if verbose?
- end
- end
-
- def inspect?
- @inspect_mode.nil? && !@math_mode or @inspect_mode
- end
- end
-end
-
diff --git a/lib/irb/ext/multi-irb.rb b/lib/irb/ext/multi-irb.rb
index 982a319611..28d6fba832 100644
--- a/lib/irb/ext/multi-irb.rb
+++ b/lib/irb/ext/multi-irb.rb
@@ -10,7 +10,6 @@
#
#
IRB.fail CantShiftToMultiIrbMode unless defined?(Thread)
-require "thread"
module IRB
class JobManager
diff --git a/lib/irb/ext/use-loader.rb b/lib/irb/ext/use-loader.rb
index 571dd25d17..cc71706671 100644
--- a/lib/irb/ext/use-loader.rb
+++ b/lib/irb/ext/use-loader.rb
@@ -10,8 +10,8 @@
#
#
-require "irb/cmd/load"
-require "irb/ext/loader"
+require_relative "../cmd/load"
+require_relative "loader"
class Object
alias __original__load__IRB_use_loader__ load
diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb
index 6f15e6403a..064f21ba52 100644
--- a/lib/irb/extend-command.rb
+++ b/lib/irb/extend-command.rb
@@ -214,7 +214,6 @@ module IRB # :nodoc:
@EXTEND_COMMANDS = [
[:eval_history=, "irb/ext/history.rb"],
[:use_tracer=, "irb/ext/tracer.rb"],
- [:math_mode=, "irb/ext/math-mode.rb"],
[:use_loader=, "irb/ext/use-loader.rb"],
[:save_history=, "irb/ext/save-history.rb"],
]
@@ -223,7 +222,6 @@ module IRB # :nodoc:
#
# Context#eval_history=:: +irb/ext/history.rb+
# Context#use_tracer=:: +irb/ext/tracer.rb+
- # Context#math_mode=:: +irb/ext/math-mode.rb+
# Context#use_loader=:: +irb/ext/use-loader.rb+
# Context#save_history=:: +irb/ext/save-history.rb+
def self.install_extend_commands
@@ -306,4 +304,3 @@ module IRB # :nodoc:
end
end
end
-
diff --git a/lib/irb/help.rb b/lib/irb/help.rb
index a4264ab4ab..7868a70a6c 100644
--- a/lib/irb/help.rb
+++ b/lib/irb/help.rb
@@ -10,7 +10,7 @@
#
#
-require 'irb/magic-file'
+require_relative 'magic-file'
module IRB
# Outputs the irb help message, see IRB@Command+line+options.
diff --git a/lib/irb/init.rb b/lib/irb/init.rb
index 1184db15ea..2066d8cb64 100644
--- a/lib/irb/init.rb
+++ b/lib/irb/init.rb
@@ -13,10 +13,10 @@
module IRB # :nodoc:
# initialize config
- def IRB.setup(ap_path)
+ def IRB.setup(ap_path, argv: ::ARGV)
IRB.init_config(ap_path)
IRB.init_error
- IRB.parse_opts
+ IRB.parse_opts(argv: argv)
IRB.run_config
IRB.load_modules
@@ -43,7 +43,6 @@ module IRB # :nodoc:
@CONF[:LOAD_MODULES] = []
@CONF[:IRB_RC] = nil
- @CONF[:MATH_MODE] = false
@CONF[:USE_READLINE] = false unless defined?(ReadlineInputMethod)
@CONF[:INSPECT_MODE] = true
@CONF[:USE_TRACER] = false
@@ -122,21 +121,19 @@ module IRB # :nodoc:
end
# option analyzing
- def IRB.parse_opts
+ def IRB.parse_opts(argv: ::ARGV)
load_path = []
- while opt = ARGV.shift
+ while opt = argv.shift
case opt
when "-f"
@CONF[:RC] = false
- when "-m"
- @CONF[:MATH_MODE] = true
when "-d"
$DEBUG = true
$VERBOSE = true
when "-w"
$VERBOSE = true
when /^-W(.+)?/
- opt = $1 || ARGV.shift
+ opt = $1 || argv.shift
case opt
when "0"
$VERBOSE = nil
@@ -146,19 +143,19 @@ module IRB # :nodoc:
$VERBOSE = true
end
when /^-r(.+)?/
- opt = $1 || ARGV.shift
+ opt = $1 || argv.shift
@CONF[:LOAD_MODULES].push opt if opt
when /^-I(.+)?/
- opt = $1 || ARGV.shift
+ opt = $1 || argv.shift
load_path.concat(opt.split(File::PATH_SEPARATOR)) if opt
when '-U'
set_encoding("UTF-8", "UTF-8")
when /^-E(.+)?/, /^--encoding(?:=(.+))?/
- opt = $1 || ARGV.shift
+ opt = $1 || argv.shift
set_encoding(*opt.split(':', 2))
when "--inspect"
- if /^-/ !~ ARGV.first
- @CONF[:INSPECT_MODE] = ARGV.shift
+ if /^-/ !~ argv.first
+ @CONF[:INSPECT_MODE] = argv.shift
else
@CONF[:INSPECT_MODE] = true
end
@@ -177,7 +174,7 @@ module IRB # :nodoc:
when "--noverbose"
@CONF[:VERBOSE] = false
when /^--prompt-mode(?:=(.+))?/, /^--prompt(?:=(.+))?/
- opt = $1 || ARGV.shift
+ opt = $1 || argv.shift
prompt_mode = opt.upcase.tr("-", "_").intern
@CONF[:PROMPT_MODE] = prompt_mode
when "--noprompt"
@@ -189,22 +186,22 @@ module IRB # :nodoc:
when "--tracer"
@CONF[:USE_TRACER] = true
when /^--back-trace-limit(?:=(.+))?/
- @CONF[:BACK_TRACE_LIMIT] = ($1 || ARGV.shift).to_i
+ @CONF[:BACK_TRACE_LIMIT] = ($1 || argv.shift).to_i
when /^--context-mode(?:=(.+))?/
- @CONF[:CONTEXT_MODE] = ($1 || ARGV.shift).to_i
+ @CONF[:CONTEXT_MODE] = ($1 || argv.shift).to_i
when "--single-irb"
@CONF[:SINGLE_IRB] = true
when /^--irb_debug(?:=(.+))?/
- @CONF[:DEBUG_LEVEL] = ($1 || ARGV.shift).to_i
+ @CONF[:DEBUG_LEVEL] = ($1 || argv.shift).to_i
when "-v", "--version"
print IRB.version, "\n"
exit 0
when "-h", "--help"
- require "irb/help"
+ require_relative "help"
IRB.print_usage
exit 0
when "--"
- if opt = ARGV.shift
+ if opt = argv.shift
@CONF[:SCRIPT] = opt
$0 = opt
end
@@ -280,7 +277,7 @@ module IRB # :nodoc:
begin
require m
rescue LoadError => err
- warn err.backtrace[0] << ":#{err.class}: #{err}"
+ warn "#{err.class}: #{err}", uplevel: 0
end
end
end
diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb
index f7b1aac3bf..f491d5a760 100644
--- a/lib/irb/input-method.rb
+++ b/lib/irb/input-method.rb
@@ -9,8 +9,8 @@
#
#
#
-require 'irb/src_encoding'
-require 'irb/magic-file'
+require_relative 'src_encoding'
+require_relative 'magic-file'
module IRB
STDIN_FILE_NAME = "(line)" # :nodoc:
diff --git a/lib/irb/irb.gemspec b/lib/irb/irb.gemspec
new file mode 100644
index 0000000000..57a44fecb7
--- /dev/null
+++ b/lib/irb/irb.gemspec
@@ -0,0 +1,26 @@
+begin
+ require_relative "lib/irb/version"
+rescue LoadError
+ # for Ruby core repository
+ require_relative "version"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "irb"
+ spec.version = IRB::VERSION
+ spec.authors = ["Keiju ISHITSUKA"]
+ spec.email = ["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).}
+ spec.homepage = "https://github.com/ruby/irb"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "exe/irb", "irb.gemspec", "lib/irb.rb", "lib/irb/cmd/chws.rb", "lib/irb/cmd/fork.rb", "lib/irb/cmd/help.rb", "lib/irb/cmd/load.rb", "lib/irb/cmd/nop.rb", "lib/irb/cmd/pushws.rb", "lib/irb/cmd/subirb.rb", "lib/irb/completion.rb", "lib/irb/context.rb", "lib/irb/ext/change-ws.rb", "lib/irb/ext/history.rb", "lib/irb/ext/loader.rb", "lib/irb/ext/multi-irb.rb", "lib/irb/ext/save-history.rb", "lib/irb/ext/tracer.rb", "lib/irb/ext/use-loader.rb", "lib/irb/ext/workspaces.rb", "lib/irb/extend-command.rb", "lib/irb/frame.rb", "lib/irb/help.rb", "lib/irb/init.rb", "lib/irb/input-method.rb", "lib/irb/inspector.rb", "lib/irb/lc/.document", "lib/irb/lc/error.rb", "lib/irb/lc/help-message", "lib/irb/lc/ja/encoding_aliases.rb", "lib/irb/lc/ja/error.rb", "lib/irb/lc/ja/help-message", "lib/irb/locale.rb", "lib/irb/magic-file.rb", "lib/irb/notifier.rb", "lib/irb/output-method.rb", "lib/irb/ruby-lex.rb", "lib/irb/ruby-token.rb", "lib/irb/slex.rb", "lib/irb/src_encoding.rb", "lib/irb/version.rb", "lib/irb/workspace.rb", "lib/irb/ws-for-case-2.rb", "lib/irb/xmp.rb"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+end
diff --git a/lib/irb/lc/help-message b/lib/irb/lc/help-message
index 5853693eb0..d43c6a1695 100644
--- a/lib/irb/lc/help-message
+++ b/lib/irb/lc/help-message
@@ -11,7 +11,6 @@
#
Usage: irb.rb [options] [programfile] [arguments]
-f Suppress read of ~/.irbrc
- -m Bc mode (load mathn, fraction or matrix are available)
-d Set $DEBUG to true (same as `ruby -d')
-r load-module Same as `ruby -r'
-I path Specify $LOAD_PATH directory
diff --git a/lib/irb/lc/ja/help-message b/lib/irb/lc/ja/help-message
index 288eb5245c..1b24d14d28 100644
--- a/lib/irb/lc/ja/help-message
+++ b/lib/irb/lc/ja/help-message
@@ -10,7 +10,6 @@
#
Usage: irb.rb [options] [programfile] [arguments]
-f ~/.irbrc を読ã¿è¾¼ã¾ãªã„.
- -m bcモード(分数, 行列ã®è¨ˆç®—ãŒã§ãã‚‹)
-d $DEBUG ã‚’trueã«ã™ã‚‹(ruby -d ã¨åŒã˜)
-r load-module ruby -r ã¨åŒã˜.
-I path $LOAD_PATH ã« path を追加ã™ã‚‹.
diff --git a/lib/irb/locale.rb b/lib/irb/locale.rb
index df540c8cbb..b713f50e76 100644
--- a/lib/irb/locale.rb
+++ b/lib/irb/locale.rb
@@ -31,7 +31,7 @@ module IRB # :nodoc:
if @encoding_name
begin load 'irb/encoding_aliases.rb'; rescue LoadError; end
if @encoding = @@legacy_encoding_alias_map[@encoding_name]
- warn "%s is obsolete. use %s" % ["#{@lang}_#{@territory}.#{@encoding_name}", "#{@lang}_#{@territory}.#{@encoding.name}"]
+ warn(("%s is obsolete. use %s" % ["#{@lang}_#{@territory}.#{@encoding_name}", "#{@lang}_#{@territory}.#{@encoding.name}"]), uplevel: 1)
end
@encoding = Encoding.find(@encoding_name) rescue nil
end
diff --git a/lib/irb/magic-file.rb b/lib/irb/magic-file.rb
index 2dee684657..34e06d64b3 100644
--- a/lib/irb/magic-file.rb
+++ b/lib/irb/magic-file.rb
@@ -10,7 +10,7 @@ module IRB
line = io.gets if line[0,2] == "#!"
encoding = detect_encoding(line)
internal_encoding = encoding
- encoding ||= default_src_encoding
+ encoding ||= IRB.default_src_encoding
io.rewind
io.set_encoding(encoding, internal_encoding)
diff --git a/lib/irb/notifier.rb b/lib/irb/notifier.rb
index a21e865f2e..5a68b0e8c8 100644
--- a/lib/irb/notifier.rb
+++ b/lib/irb/notifier.rb
@@ -11,7 +11,7 @@
#
require "e2mmap"
-require "irb/output-method"
+require_relative "output-method"
module IRB
# An output formatter used internally by the lexer.
diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb
index 879c45c565..555d1f024f 100644
--- a/lib/irb/ruby-lex.rb
+++ b/lib/irb/ruby-lex.rb
@@ -11,8 +11,8 @@
#
require "e2mmap"
-require "irb/slex"
-require "irb/ruby-token"
+require_relative "slex"
+require_relative "ruby-token"
# :stopdoc:
class RubyLex
@@ -262,9 +262,18 @@ class RubyLex
end
def lex
- until (((tk = token).kind_of?(TkNL) || tk.kind_of?(TkEND_OF_SCRIPT)) &&
- !@continue or
- tk.nil?)
+ continue = @continue
+ while tk = token
+ case tk
+ when TkNL, TkEND_OF_SCRIPT
+ @continue = continue unless continue.nil?
+ break unless @continue
+ when TkSPACE, TkCOMMENT
+ when TkSEMICOLON, TkBEGIN, TkELSE
+ @continue = continue = false
+ else
+ continue = nil
+ end
end
line = get_readed
if line == "" and tk.kind_of?(TkEND_OF_SCRIPT) || tk.nil?
@@ -406,7 +415,7 @@ class RubyLex
if @lex_state != EXPR_END && @lex_state != EXPR_CLASS &&
(@lex_state != EXPR_ARG || @space_seen)
c = peek(0)
- if /\S/ =~ c && (/["'`]/ =~ c || /\w/ =~ c || c == "-" || c == "~")
+ if /[-~"'`\w]/ =~ c
tk = identify_here_document
end
end
@@ -792,7 +801,7 @@ class RubyLex
token_c = TkSymbol2Token[trans[1]]
@lex_state = trans[0]
else
- if @lex_state != EXPR_FNAME
+ if @lex_state != EXPR_FNAME and peek(0) != ':'
if ENINDENT_CLAUSE.include?(token)
# check for ``class = val'' etc.
valid = true
diff --git a/lib/irb/slex.rb b/lib/irb/slex.rb
index 68174771a7..e584b312bd 100644
--- a/lib/irb/slex.rb
+++ b/lib/irb/slex.rb
@@ -11,7 +11,7 @@
#
require "e2mmap"
-require "irb/notifier"
+require_relative "notifier"
# :stopdoc:
module IRB
@@ -53,7 +53,7 @@ module IRB
node.preproc=proc
end
- #$BMW%A%'%C%/(B?
+ # need a check?
def postproc(token)
node = search(token, proc)
node.postproc=proc
diff --git a/lib/irb/src_encoding.rb b/lib/irb/src_encoding.rb
index 32f997fc7c..99aea2b43e 100644
--- a/lib/irb/src_encoding.rb
+++ b/lib/irb/src_encoding.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: false
# DO NOT WRITE ANY MAGIC COMMENT HERE.
-def default_src_encoding
- return __ENCODING__
+module IRB
+ def self.default_src_encoding
+ return __ENCODING__
+ end
end
diff --git a/lib/irb/version.rb b/lib/irb/version.rb
index 094cb33c05..41cfff404c 100644
--- a/lib/irb/version.rb
+++ b/lib/irb/version.rb
@@ -11,6 +11,7 @@
#
module IRB # :nodoc:
- @RELEASE_VERSION = "0.9.6"
- @LAST_UPDATE_DATE = "09/06/30"
+ VERSION = "1.0.0"
+ @RELEASE_VERSION = VERSION
+ @LAST_UPDATE_DATE = "2018-12-18"
end
diff --git a/lib/irb/workspace.rb b/lib/irb/workspace.rb
index 16f714d66e..53914fb7c1 100644
--- a/lib/irb/workspace.rb
+++ b/lib/irb/workspace.rb
@@ -39,8 +39,6 @@ EOF
when 2 # binding in loaded file(thread use)
unless defined? BINDING_QUEUE
- require "thread"
-
IRB.const_set(:BINDING_QUEUE, Thread::SizedQueue.new(1))
Thread.abort_on_exception = true
Thread.start do
@@ -73,7 +71,7 @@ EOF
end
end
end
- eval("_=nil", @binding)
+ @binding.local_variable_set(:_, nil)
end
# The Binding of this workspace
@@ -87,6 +85,14 @@ EOF
eval(statements, @binding, file, line)
end
+ def local_variable_set(name, value)
+ @binding.local_variable_set(name, value)
+ end
+
+ def local_variable_get(name)
+ @binding.local_variable_get(name)
+ end
+
# error message manipulator
def filter_backtrace(bt)
case IRB.conf[:CONTEXT_MODE]
@@ -109,6 +115,28 @@ EOF
bt
end
+ def code_around_binding
+ file, pos = @binding.source_location
+
+ unless defined?(::SCRIPT_LINES__[file]) && lines = ::SCRIPT_LINES__[file]
+ begin
+ lines = File.readlines(file)
+ rescue SystemCallError
+ return
+ end
+ end
+ pos -= 1
+
+ start_pos = [pos - 5, 0].max
+ end_pos = [pos + 5, lines.size - 1].min
+
+ fmt = " %2s %#{end_pos.to_s.length}d: %s"
+ body = (start_pos..end_pos).map do |current_pos|
+ sprintf(fmt, pos == current_pos ? '=>' : '', current_pos + 1, lines[current_pos])
+ end.join("")
+ "\nFrom: #{file} @ line #{pos + 1} :\n\n#{body}\n"
+ end
+
def IRB.delete_caller
end
end
diff --git a/lib/irb/xmp.rb b/lib/irb/xmp.rb
index 3234cff7f3..60cf3b4e4d 100644
--- a/lib/irb/xmp.rb
+++ b/lib/irb/xmp.rb
@@ -11,7 +11,7 @@
#
require "irb"
-require "irb/frame"
+require_relative "frame"
# An example printer for irb.
#
diff --git a/lib/logger.gemspec b/lib/logger.gemspec
new file mode 100644
index 0000000000..815ae5bc46
--- /dev/null
+++ b/lib/logger.gemspec
@@ -0,0 +1,27 @@
+begin
+ require_relative "lib/logger"
+rescue LoadError
+ # for Ruby core repository
+ require_relative "logger"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "logger"
+ spec.version = Logger::VERSION
+ spec.authors = ["SHIBATA Hiroshi"]
+ spec.email = ["hsbt@ruby-lang.org"]
+
+ spec.summary = %q{Provides a simple logging utility for outputting messages.}
+ spec.description = %q{Provides a simple logging utility for outputting messages.}
+ spec.homepage = "https://github.com/ruby/logger"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "lib/logger.rb", "logger.gemspec"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler", "~> 1.16"
+ spec.add_development_dependency "rake", "~> 10.0"
+ spec.add_development_dependency "minitest", "~> 5.0"
+end
diff --git a/lib/logger.rb b/lib/logger.rb
index e0c93c323c..918caf0956 100644
--- a/lib/logger.rb
+++ b/lib/logger.rb
@@ -109,7 +109,7 @@ require 'monitor'
# 3. Create a logger for the specified file.
#
# file = File.open('foo.log', File::WRONLY | File::APPEND)
-# # To create new (and to remove old) logfile, add File::CREAT like:
+# # To create new logfile, add File::CREAT like:
# # file = File.open('foo.log', File::WRONLY | File::APPEND | File::CREAT)
# logger = Logger.new(file)
#
@@ -224,7 +224,7 @@ require 'monitor'
# })
#
class Logger
- VERSION = "1.2.7"
+ VERSION = "1.3.0"
_, name, rev = %w$Id$
if name
name = name.chomp(",v")
@@ -268,17 +268,17 @@ class Logger
@level = severity
else
case severity.to_s.downcase
- when 'debug'.freeze
+ when 'debug'
@level = DEBUG
- when 'info'.freeze
+ when 'info'
@level = INFO
- when 'warn'.freeze
+ when 'warn'
@level = WARN
- when 'error'.freeze
+ when 'error'
@level = ERROR
- when 'fatal'.freeze
+ when 'fatal'
@level = FATAL
- when 'unknown'.freeze
+ when 'unknown'
@level = UNKNOWN
else
raise ArgumentError, "invalid log level: #{severity}"
@@ -457,7 +457,9 @@ class Logger
if @logdev.nil? or severity < @level
return true
end
- progname ||= @progname
+ if progname.nil?
+ progname = @progname
+ end
if message.nil?
if block_given?
message = yield
@@ -477,9 +479,7 @@ class Logger
# device exists, return +nil+.
#
def <<(msg)
- unless @logdev.nil?
- @logdev.write(msg)
- end
+ @logdev&.write(msg)
end
#
@@ -566,7 +566,7 @@ class Logger
# Close the logging device.
#
def close
- @logdev.close if @logdev
+ @logdev&.close
end
private
@@ -673,7 +673,11 @@ private
@shift_age = shift_age || 7
@shift_size = shift_size || 1048576
@shift_period_suffix = shift_period_suffix || '%Y%m%d'
- @next_rotate_time = next_rotate_time(Time.now, @shift_age) unless @shift_age.is_a?(Integer)
+
+ unless @shift_age.is_a?(Integer)
+ base_time = @dev.respond_to?(:stat) ? @dev.stat.mtime : Time.now
+ @next_rotate_time = next_rotate_time(base_time, @shift_age)
+ end
end
end
@@ -737,7 +741,7 @@ private
def open_logfile(filename)
begin
- open(filename, (File::WRONLY | File::APPEND))
+ File.open(filename, (File::WRONLY | File::APPEND))
rescue Errno::ENOENT
create_logfile(filename)
end
@@ -745,7 +749,7 @@ private
def create_logfile(filename)
begin
- logdev = open(filename, (File::WRONLY | File::APPEND | File::CREAT | File::EXCL))
+ logdev = File.open(filename, (File::WRONLY | File::APPEND | File::CREAT | File::EXCL))
logdev.flock(File::LOCK_EX)
logdev.sync = true
add_log_header(logdev)
diff --git a/lib/mathn.rb b/lib/mathn.rb
deleted file mode 100644
index 9cb9401e37..0000000000
--- a/lib/mathn.rb
+++ /dev/null
@@ -1,170 +0,0 @@
-# frozen_string_literal: false
-#--
-# $Release Version: 0.5 $
-# $Revision: 1.1.1.1.4.1 $
-
-##
-# = mathn
-#
-# mathn serves to make mathematical operations more precise in Ruby
-# and to integrate other mathematical standard libraries.
-#
-# Without mathn:
-#
-# 3 / 2 => 1 # Integer
-#
-# With mathn:
-#
-# 3 / 2 => 3/2 # Rational
-#
-# mathn keeps value in exact terms.
-#
-# Without mathn:
-#
-# 20 / 9 * 3 * 14 / 7 * 3 / 2 # => 18
-#
-# With mathn:
-#
-# 20 / 9 * 3 * 14 / 7 * 3 / 2 # => 20
-#
-#
-# When you require 'mathn', the libraries for Prime, CMath, Matrix and Vector
-# are also loaded.
-#
-# == Copyright
-#
-# Author: Keiju ISHITSUKA (SHL Japan Inc.)
-#--
-# class Numeric follows to make this documentation findable in a reasonable
-# location
-
-warn('lib/mathn.rb is deprecated') if $VERBOSE
-
-class Numeric; end
-
-require "cmath.rb"
-require "matrix.rb"
-require "prime.rb"
-
-require "mathn/rational"
-require "mathn/complex"
-
-unless defined?(Math.exp!)
- Object.instance_eval{remove_const :Math}
- Math = CMath # :nodoc:
-end
-
-##
-# When mathn is required, Integer's division is enhanced to
-# return more precise values from mathematical expressions.
-#
-# 2/3*3 # => 0
-# require 'mathn'
-# 2/3*3 # => 2
-#
-# (2**72) / ((2**70) * 3) # => 4/3
-
-class Integer
- remove_method :/
-
- ##
- # +/+ defines the Rational division for Bignum.
- #
- # (2**72) / ((2**70) * 3) # => 4/3
-
- alias / quo
-end
-
-##
-# When mathn is required, the Math module changes as follows:
-#
-# Standard Math module behaviour:
-# Math.sqrt(4/9) # => 0.0
-# Math.sqrt(4.0/9.0) # => 0.666666666666667
-# Math.sqrt(- 4/9) # => Errno::EDOM: Numerical argument out of domain - sqrt
-#
-# After require 'mathn', this is changed to:
-#
-# require 'mathn'
-# Math.sqrt(4/9) # => 2/3
-# Math.sqrt(4.0/9.0) # => 0.666666666666667
-# Math.sqrt(- 4/9) # => Complex(0, 2/3)
-
-module Math
- remove_method(:sqrt)
-
- ##
- # Computes the square root of +a+. It makes use of Complex and
- # Rational to have no rounding errors if possible.
- #
- # Math.sqrt(4/9) # => 2/3
- # Math.sqrt(- 4/9) # => Complex(0, 2/3)
- # Math.sqrt(4.0/9.0) # => 0.666666666666667
-
- def sqrt(a)
- if a.kind_of?(Complex)
- sqrt!(a)
- elsif a.respond_to?(:nan?) and a.nan?
- a
- elsif a >= 0
- rsqrt(a)
- else
- Complex(0,rsqrt(-a))
- end
- end
-
- ##
- # Compute square root of a non negative number. This method is
- # internally used by +Math.sqrt+.
-
- def rsqrt(a)
- if a.kind_of?(Float)
- sqrt!(a)
- elsif a.kind_of?(Rational)
- rsqrt(a.numerator)/rsqrt(a.denominator)
- else
- src = a
- max = 2 ** 32
- byte_a = [src & 0xffffffff]
- # ruby's bug
- while (src >= max) and (src >>= 32)
- byte_a.unshift src & 0xffffffff
- end
-
- answer = 0
- main = 0
- side = 0
- for elm in byte_a
- main = (main << 32) + elm
- side <<= 16
- if answer != 0
- if main * 4 < side * side
- applo = main.div(side)
- else
- applo = ((sqrt!(side * side + 4 * main) - side)/2.0).to_i + 1
- end
- else
- applo = sqrt!(main).to_i + 1
- end
-
- while (x = (side + applo) * applo) > main
- applo -= 1
- end
- main -= x
- answer = (answer << 16) + applo
- side += applo * 2
- end
- if main == 0
- answer
- else
- sqrt!(a)
- end
- end
- end
-
- class << self
- remove_method(:sqrt)
- end
- module_function :sqrt
- module_function :rsqrt
-end
diff --git a/lib/matrix.rb b/lib/matrix.rb
index fe61b6d120..7f338bb07e 100644
--- a/lib/matrix.rb
+++ b/lib/matrix.rb
@@ -12,7 +12,7 @@
# Original Documentation:: Gavin Sinclair (sourced from <i>Ruby in a Nutshell</i> (Matsumoto, O'Reilly))
##
-require "e2mmap.rb"
+require "e2mmap"
module ExceptionForMatrix # :nodoc:
extend Exception2MessageMapper
@@ -28,110 +28,8 @@ end
#
# The +Matrix+ class represents a mathematical matrix. It provides methods for creating
# matrices, operating on them arithmetically and algebraically,
-# and determining their mathematical properties (trace, rank, inverse, determinant).
-#
-# == Method Catalogue
-#
-# To create a matrix:
-# * Matrix[*rows]
-# * Matrix.[](*rows)
-# * Matrix.rows(rows, copy = true)
-# * Matrix.columns(columns)
-# * Matrix.build(row_count, column_count, &block)
-# * Matrix.diagonal(*values)
-# * Matrix.scalar(n, value)
-# * Matrix.identity(n)
-# * Matrix.unit(n)
-# * Matrix.I(n)
-# * Matrix.zero(n)
-# * Matrix.row_vector(row)
-# * Matrix.column_vector(column)
-# * Matrix.empty(row_count, column_count)
-# * Matrix.hstack(*matrices)
-# * Matrix.vstack(*matrices)
-#
-# To access Matrix elements/columns/rows/submatrices/properties:
-# * #[](i, j)
-# * #row_count (row_size)
-# * #column_count (column_size)
-# * #row(i)
-# * #column(j)
-# * #collect
-# * #map
-# * #each
-# * #each_with_index
-# * #find_index
-# * #minor(*param)
-# * #first_minor(row, column)
-# * #cofactor(row, column)
-# * #adjugate
-# * #laplace_expansion(row_or_column: num)
-# * #cofactor_expansion(row_or_column: num)
-#
-# Properties of a matrix:
-# * #diagonal?
-# * #empty?
-# * #hermitian?
-# * #lower_triangular?
-# * #normal?
-# * #orthogonal?
-# * #permutation?
-# * #real?
-# * #regular?
-# * #singular?
-# * #square?
-# * #symmetric?
-# * #unitary?
-# * #upper_triangular?
-# * #zero?
-#
-# Matrix arithmetic:
-# * #*(m)
-# * #+(m)
-# * #-(m)
-# * #/(m)
-# * #inverse
-# * #inv
-# * #**
-# * #+@
-# * #-@
-#
-# Matrix functions:
-# * #determinant
-# * #det
-# * #hstack(*matrices)
-# * #rank
-# * #round
-# * #trace
-# * #tr
-# * #transpose
-# * #t
-# * #vstack(*matrices)
-#
-# Matrix decompositions:
-# * #eigen
-# * #eigensystem
-# * #lup
-# * #lup_decomposition
-#
-# Complex arithmetic:
-# * conj
-# * conjugate
-# * imag
-# * imaginary
-# * real
-# * rect
-# * rectangular
-#
-# Conversion to other data types:
-# * #coerce(other)
-# * #row_vectors
-# * #column_vectors
-# * #to_a
-#
-# String representations:
-# * #to_s
-# * #inspect
+# and determining their mathematical properties such as trace, rank, inverse, determinant,
+# or eigensystem.
#
class Matrix
include Enumerable
@@ -247,8 +145,8 @@ class Matrix
scalar(n, 1)
end
class << Matrix
- alias unit identity
- alias I identity
+ alias_method :unit, :identity
+ alias_method :I, :identity
end
#
@@ -314,10 +212,10 @@ class Matrix
# Matrix.vstack(x, y) # => Matrix[[1, 2], [3, 4], [5, 6], [7, 8]]
#
def Matrix.vstack(x, *matrices)
- raise TypeError, "Expected a Matrix, got a #{x.class}" unless x.is_a?(Matrix)
+ x = CoercionHelper.coerce_to_matrix(x)
result = x.send(:rows).map(&:dup)
matrices.each do |m|
- raise TypeError, "Expected a Matrix, got a #{m.class}" unless m.is_a?(Matrix)
+ m = CoercionHelper.coerce_to_matrix(m)
if m.column_count != x.column_count
raise ErrDimensionMismatch, "The given matrices must have #{x.column_count} columns, but one has #{m.column_count}"
end
@@ -335,11 +233,11 @@ class Matrix
# Matrix.hstack(x, y) # => Matrix[[1, 2, 5, 6], [3, 4, 7, 8]]
#
def Matrix.hstack(x, *matrices)
- raise TypeError, "Expected a Matrix, got a #{x.class}" unless x.is_a?(Matrix)
+ x = CoercionHelper.coerce_to_matrix(x)
result = x.send(:rows).map(&:dup)
total_column_count = x.column_count
matrices.each do |m|
- raise TypeError, "Expected a Matrix, got a #{m.class}" unless m.is_a?(Matrix)
+ m = CoercionHelper.coerce_to_matrix(m)
if m.row_count != x.row_count
raise ErrDimensionMismatch, "The given matrices must have #{x.row_count} rows, but one has #{m.row_count}"
end
@@ -352,6 +250,35 @@ class Matrix
end
#
+ # Create a matrix by combining matrices entrywise, using the given block
+ #
+ # x = Matrix[[6, 6], [4, 4]]
+ # y = Matrix[[1, 2], [3, 4]]
+ # Matrix.combine(x, y) {|a, b| a - b} # => Matrix[[5, 4], [1, 0]]
+ #
+ def Matrix.combine(*matrices)
+ return to_enum(__method__, *matrices) unless block_given?
+
+ return Matrix.empty if matrices.empty?
+ matrices.map!(&CoercionHelper.method(:coerce_to_matrix))
+ x = matrices.first
+ matrices.each do |m|
+ Matrix.Raise ErrDimensionMismatch unless x.row_count == m.row_count && x.column_count == m.column_count
+ end
+
+ rows = Array.new(x.row_count) do |i|
+ Array.new(x.column_count) do |j|
+ yield matrices.map{|m| m[i,j]}
+ end
+ end
+ new rows, x.column_count
+ end
+
+ def combine(*matrices, &block)
+ Matrix.combine(self, *matrices, &block)
+ end
+
+ #
# Matrix.new is private; use Matrix.rows, columns, [], etc... to create.
#
def initialize(rows, column_count = rows[0].size)
@@ -362,10 +289,9 @@ class Matrix
@column_count = column_count
end
- def new_matrix(rows, column_count = rows[0].size) # :nodoc:
+ private def new_matrix(rows, column_count = rows[0].size) # :nodoc:
self.class.send(:new, rows, column_count) # bypass privacy of Matrix.new
end
- private :new_matrix
#
# Returns element (+i+,+j+) of the matrix. That is: row +i+, column +j+.
@@ -376,12 +302,107 @@ class Matrix
alias element []
alias component []
+ #
+ # :call-seq:
+ # matrix[range, range] = matrix/element
+ # matrix[range, integer] = vector/column_matrix/element
+ # matrix[integer, range] = vector/row_matrix/element
+ # matrix[integer, integer] = element
+ #
+ # Set element or elements of matrix.
def []=(i, j, v)
- @rows[i][j] = v
+ raise FrozenError, "can't modify frozen Matrix" if frozen?
+ rows = check_range(i, :row) or row = check_int(i, :row)
+ columns = check_range(j, :column) or column = check_int(j, :column)
+ if rows && columns
+ set_row_and_col_range(rows, columns, v)
+ elsif rows
+ set_row_range(rows, column, v)
+ elsif columns
+ set_col_range(row, columns, v)
+ else
+ set_value(row, column, v)
+ end
end
alias set_element []=
alias set_component []=
- private :[]=, :set_element, :set_component
+ private :set_element, :set_component
+
+ # Returns range or nil
+ private def check_range(val, direction)
+ return unless val.is_a?(Range)
+ count = direction == :row ? row_count : column_count
+ CoercionHelper.check_range(val, count, direction)
+ end
+
+ private def check_int(val, direction)
+ count = direction == :row ? row_count : column_count
+ CoercionHelper.check_int(val, count, direction)
+ end
+
+ private def set_value(row, col, value)
+ raise ErrDimensionMismatch, "Expected a a value, got a #{value.class}" if value.respond_to?(:to_matrix)
+
+ @rows[row][col] = value
+ end
+
+ private def set_row_and_col_range(row_range, col_range, value)
+ if value.is_a?(Matrix)
+ if row_range.size != value.row_count || col_range.size != value.column_count
+ raise ErrDimensionMismatch, [
+ 'Expected a Matrix of dimensions',
+ "#{row_range.size}x#{col_range.size}",
+ 'got',
+ "#{value.row_count}x#{value.column_count}",
+ ].join(' ')
+ end
+ source = value.instance_variable_get :@rows
+ row_range.each_with_index do |row, i|
+ @rows[row][col_range] = source[i]
+ end
+ elsif value.is_a?(Vector)
+ raise ErrDimensionMismatch, 'Expected a Matrix or a value, got a Vector'
+ else
+ value_to_set = Array.new(col_range.size, value)
+ row_range.each do |i|
+ @rows[i][col_range] = value_to_set
+ end
+ end
+ end
+
+ private def set_row_range(row_range, col, value)
+ if value.is_a?(Vector)
+ Matrix.Raise ErrDimensionMismatch unless row_range.size == value.size
+ set_column_vector(row_range, col, value)
+ elsif value.is_a?(Matrix)
+ Matrix.Raise ErrDimensionMismatch unless value.column_count == 1
+ value = value.column(0)
+ Matrix.Raise ErrDimensionMismatch unless row_range.size == value.size
+ set_column_vector(row_range, col, value)
+ else
+ @rows[row_range].each{|e| e[col] = value }
+ end
+ end
+
+ private def set_column_vector(row_range, col, value)
+ value.each_with_index do |e, index|
+ r = row_range.begin + index
+ @rows[r][col] = e
+ end
+ end
+
+ private def set_col_range(row, col_range, value)
+ value = if value.is_a?(Vector)
+ value.to_a
+ elsif value.is_a?(Matrix)
+ Matrix.Raise ErrDimensionMismatch unless value.row_count == 1
+ value.row(0).to_a
+ else
+ Array.new(col_range.size, value)
+ end
+ Matrix.Raise ErrDimensionMismatch unless col_range.size == value.size
+ @rows[row][col_range] = value
+ end
#
# Returns the number of rows.
@@ -434,16 +455,48 @@ class Matrix
#
# Returns a matrix that is the result of iteration of the given block over all
# elements of the matrix.
+ # Elements can be restricted by passing an argument:
+ # * :all (default): yields all elements
+ # * :diagonal: yields only elements on the diagonal
+ # * :off_diagonal: yields all elements except on the diagonal
+ # * :lower: yields only elements on or below the diagonal
+ # * :strict_lower: yields only elements below the diagonal
+ # * :strict_upper: yields only elements above the diagonal
+ # * :upper: yields only elements on or above the diagonal
# Matrix[ [1,2], [3,4] ].collect { |e| e**2 }
# => 1 4
# 9 16
#
- def collect(&block) # :yield: e
- return to_enum(:collect) unless block_given?
- rows = @rows.collect{|row| row.collect(&block)}
- new_matrix rows, column_count
+ def collect(which = :all, &block) # :yield: e
+ return to_enum(:collect, which) unless block_given?
+ dup.collect!(which, &block)
+ end
+ alias_method :map, :collect
+
+ #
+ # Invokes the given block for each element of matrix, replacing the element with the value
+ # returned by the block.
+ # Elements can be restricted by passing an argument:
+ # * :all (default): yields all elements
+ # * :diagonal: yields only elements on the diagonal
+ # * :off_diagonal: yields all elements except on the diagonal
+ # * :lower: yields only elements on or below the diagonal
+ # * :strict_lower: yields only elements below the diagonal
+ # * :strict_upper: yields only elements above the diagonal
+ # * :upper: yields only elements on or above the diagonal
+ #
+ def collect!(which = :all)
+ return to_enum(:collect!, which) unless block_given?
+ raise FrozenError, "can't modify frozen Matrix" if frozen?
+ each_with_index(which){ |e, row_index, col_index| @rows[row_index][col_index] = yield e }
+ end
+
+ alias map! collect!
+
+ def freeze
+ @rows.freeze
+ super
end
- alias map collect
#
# Yields all elements of the matrix, starting with those of the first row,
@@ -875,6 +928,19 @@ class Matrix
end
#
+ # Returns +true+ if this is an antisymmetric matrix.
+ # Raises an error if matrix is not square.
+ #
+ def antisymmetric?
+ Matrix.Raise ErrDimensionMismatch unless square?
+ each_with_index(:upper) do |e, row, col|
+ return false unless e == -rows[col][row]
+ end
+ true
+ end
+ alias_method :skew_symmetric?, :antisymmetric?
+
+ #
# Returns +true+ if this is a unitary matrix
# Raises an error if matrix is not square.
#
@@ -926,12 +992,11 @@ class Matrix
end
#
- # Returns a clone of the matrix, so that the contents of each do not reference
- # identical objects.
- # There should be no good reason to do this since Matrices are immutable.
+ # Called for dup & clone.
#
- def clone
- new_matrix @rows.map(&:dup), column_count
+ private def initialize_copy(m)
+ super
+ @rows = @rows.map(&:dup) unless frozen?
end
#
@@ -1053,6 +1118,17 @@ class Matrix
end
#
+ # Hadamard product
+ # Matrix[[1,2], [3,4]].hadamard_product(Matrix[[1,2], [3,2]])
+ # => 1 4
+ # 9 8
+ #
+ def hadamard_product(m)
+ combine(m){|a, b| a * b}
+ end
+ alias_method :entrywise_product, :hadamard_product
+
+ #
# Returns the inverse of the matrix.
# Matrix[[-1, -1], [0, -1]].inverse
# => -1 1
@@ -1062,9 +1138,9 @@ class Matrix
Matrix.Raise ErrDimensionMismatch unless square?
self.class.I(row_count).send(:inverse_from, self)
end
- alias inv inverse
+ alias_method :inv, :inverse
- def inverse_from(src) # :nodoc:
+ private def inverse_from(src) # :nodoc:
last = row_count - 1
a = src.to_a
@@ -1107,7 +1183,6 @@ class Matrix
end
self
end
- private :inverse_from
#
# Matrix exponentiation.
@@ -1118,7 +1193,7 @@ class Matrix
# => 67 96
# 48 99
#
- def ** (other)
+ def **(other)
case other
when Integer
x = self
@@ -1214,7 +1289,7 @@ class Matrix
# with smaller bignums (if any), while a matrix of Float will usually have
# intermediate results with better precision.
#
- def determinant_bareiss
+ private def determinant_bareiss
size = row_count
last = size - 1
a = to_a
@@ -1240,16 +1315,15 @@ class Matrix
end
sign * pivot
end
- private :determinant_bareiss
#
# deprecated; use Matrix#determinant
#
def determinant_e
- warn "#{caller(1)[0]}: warning: Matrix#determinant_e is deprecated; use #determinant"
+ warn "Matrix#determinant_e is deprecated; use #determinant", uplevel: 1
determinant
end
- alias det_e determinant_e
+ alias_method :det_e, :determinant_e
#
# Returns a new matrix resulting by stacking horizontally
@@ -1304,7 +1378,7 @@ class Matrix
# deprecated; use Matrix#rank
#
def rank_e
- warn "#{caller(1)[0]}: warning: Matrix#rank_e is deprecated; use #rank"
+ warn "Matrix#rank_e is deprecated; use #rank", uplevel: 1
rank
end
@@ -1326,7 +1400,7 @@ class Matrix
tr + @rows[i][i]
end
end
- alias tr trace
+ alias_method :tr, :trace
#
# Returns the transpose of the matrix.
@@ -1342,7 +1416,7 @@ class Matrix
return self.class.empty(column_count, 0) if row_count.zero?
new_matrix @rows.transpose, row_count
end
- alias t transpose
+ alias_method :t, :transpose
#
# Returns a new matrix resulting by stacking vertically
@@ -1371,7 +1445,7 @@ class Matrix
def eigensystem
EigenvalueDecomposition.new(self)
end
- alias eigen eigensystem
+ alias_method :eigen, :eigensystem
#
# Returns the LUP decomposition of the matrix; see +LUPDecomposition+.
@@ -1386,7 +1460,7 @@ class Matrix
def lup
LUPDecomposition.new(self)
end
- alias lup_decomposition lup
+ alias_method :lup_decomposition, :lup
#--
# COMPLEX ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
@@ -1404,7 +1478,7 @@ class Matrix
def conjugate
collect(&:conjugate)
end
- alias conj conjugate
+ alias_method :conj, :conjugate
#
# Returns the imaginary part of the matrix.
@@ -1418,7 +1492,7 @@ class Matrix
def imaginary
collect(&:imaginary)
end
- alias imag imaginary
+ alias_method :imag, :imaginary
#
# Returns the real part of the matrix.
@@ -1442,7 +1516,7 @@ class Matrix
def rect
[real, imag]
end
- alias rectangular rect
+ alias_method :rectangular, :rect
#--
# CONVERTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
@@ -1483,24 +1557,40 @@ class Matrix
end
#
+ # Explicit conversion to a Matrix. Returns self
+ #
+ def to_matrix
+ self
+ end
+
+ #
# Returns an array of arrays that describe the rows of the matrix.
#
def to_a
@rows.collect(&:dup)
end
+ # Deprecated.
+ #
+ # Use map(&:to_f)
def elements_to_f
- warn "#{caller(1)[0]}: warning: Matrix#elements_to_f is deprecated, use map(&:to_f)"
+ warn "Matrix#elements_to_f is deprecated, use map(&:to_f)", uplevel: 1
map(&:to_f)
end
+ # Deprecated.
+ #
+ # Use map(&:to_i)
def elements_to_i
- warn "#{caller(1)[0]}: warning: Matrix#elements_to_i is deprecated, use map(&:to_i)"
+ warn "Matrix#elements_to_i is deprecated, use map(&:to_i)", uplevel: 1
map(&:to_i)
end
+ # Deprecated.
+ #
+ # Use map(&:to_r)
def elements_to_r
- warn "#{caller(1)[0]}: warning: Matrix#elements_to_r is deprecated, use map(&:to_r)"
+ warn "Matrix#elements_to_r is deprecated, use map(&:to_r)", uplevel: 1
map(&:to_r)
end
@@ -1539,7 +1629,7 @@ class Matrix
# Converts the obj to an Array. If copy is set to true
# a copy of obj will be made if necessary.
#
- def convert_to_array(obj, copy = false) # :nodoc:
+ private def convert_to_array(obj, copy = false) # :nodoc:
case obj
when Array
copy ? obj.dup : obj
@@ -1555,7 +1645,6 @@ class Matrix
converted
end
end
- private :convert_to_array
end
extend ConversionHelper
@@ -1565,14 +1654,13 @@ class Matrix
# Applies the operator +oper+ with argument +obj+
# through coercion of +obj+
#
- def apply_through_coercion(obj, oper)
+ private def apply_through_coercion(obj, oper)
coercion = obj.coerce(self)
raise TypeError unless coercion.is_a?(Array) && coercion.length == 2
coercion[0].public_send(oper, coercion[1])
rescue
raise TypeError, "#{obj.inspect} can't be coerced into #{self.class}"
end
- private :apply_through_coercion
#
# Helper method to coerce a value into a specific class.
@@ -1582,7 +1670,7 @@ class Matrix
#
def self.coerce_to(obj, cls, meth) # :nodoc:
return obj if obj.kind_of?(cls)
-
+ raise TypeError, "Expected a #{cls} but got a #{obj.class}" unless obj.respond_to? meth
begin
ret = obj.__send__(meth)
rescue Exception => e
@@ -1596,6 +1684,30 @@ class Matrix
def self.coerce_to_int(obj)
coerce_to(obj, Integer, :to_int)
end
+
+ def self.coerce_to_matrix(obj)
+ coerce_to(obj, Matrix, :to_matrix)
+ end
+
+ # Returns `nil` for non Ranges
+ # Checks range validity, return canonical range with 0 <= begin <= end < count
+ def self.check_range(val, count, kind)
+ canonical = (val.begin + (val.begin < 0 ? count : 0))..
+ (val.end ? val.end + (val.end < 0 ? count : 0) - (val.exclude_end? ? 1 : 0)
+ : count - 1)
+ unless 0 <= canonical.begin && canonical.begin <= canonical.end && canonical.end < count
+ raise IndexError, "given range #{val} is outside of #{kind} dimensions: 0...#{count}"
+ end
+ canonical
+ end
+
+ def self.check_int(val, count, kind)
+ val = CoercionHelper.coerce_to_int(val)
+ if val >= count || val < -count
+ raise IndexError, "given #{kind} #{val} is outside of #{-count}...#{count}"
+ end
+ val
+ end
end
include CoercionHelper
@@ -1644,7 +1756,7 @@ class Matrix
end
end
- def / (other)
+ def /(other)
case other
when Numeric
Scalar.new(@value / other)
@@ -1657,7 +1769,7 @@ class Matrix
end
end
- def ** (other)
+ def **(other)
case other
when Numeric
Scalar.new(@value ** other)
@@ -1685,10 +1797,14 @@ end
# * Vector.[](*array)
# * Vector.elements(array, copy = true)
# * Vector.basis(size: n, index: k)
+# * Vector.zero(n)
#
# To access elements:
# * #[](i)
#
+# To set elements:
+# * #[]=(i, v)
+#
# To enumerate the elements:
# * #each2(v)
# * #collect2(v)
@@ -1697,6 +1813,7 @@ end
# * #angle_with(v)
# * Vector.independent?(*vs)
# * #independent?(*vs)
+# * #zero?
#
# Vector arithmetic:
# * #*(x) "is matrix or number"
@@ -1710,8 +1827,10 @@ end
# * #inner_product(v), dot(v)
# * #cross_product(v), cross(v)
# * #collect
+# * #collect!
# * #magnitude
# * #map
+# * #map!
# * #map2(v)
# * #norm
# * #normalize
@@ -1769,6 +1888,17 @@ class Vector
end
#
+ # Return a zero vector.
+ #
+ # Vector.zero(3) => Vector[0, 0, 0]
+ #
+ def Vector.zero(size)
+ raise ArgumentError, "invalid size (#{size} for 0..)" if size < 0
+ array = Array.new(size, 0)
+ new convert_to_array(array, false)
+ end
+
+ #
# Vector.new is private; use Vector[] or Vector.elements to create.
#
def initialize(array)
@@ -1779,7 +1909,11 @@ class Vector
# ACCESSING
#
- # Returns element number +i+ (starting at zero) of the vector.
+ # :call-seq:
+ # vector[range]
+ # vector[integer]
+ #
+ # Returns element or elements of the vector.
#
def [](i)
@elements[i]
@@ -1787,12 +1921,44 @@ class Vector
alias element []
alias component []
+ #
+ # :call-seq:
+ # vector[range] = new_vector
+ # vector[range] = row_matrix
+ # vector[range] = new_element
+ # vector[integer] = new_element
+ #
+ # Set element or elements of vector.
+ #
def []=(i, v)
- @elements[i]= v
+ raise FrozenError, "can't modify frozen Vector" if frozen?
+ if i.is_a?(Range)
+ range = Matrix::CoercionHelper.check_range(i, size, :vector)
+ set_range(range, v)
+ else
+ index = Matrix::CoercionHelper.check_int(i, size, :index)
+ set_value(index, v)
+ end
end
alias set_element []=
alias set_component []=
- private :[]=, :set_element, :set_component
+ private :set_element, :set_component
+
+ private def set_value(index, value)
+ @elements[index] = value
+ end
+
+ private def set_range(range, value)
+ if value.is_a?(Vector)
+ raise ArgumentError, "vector to be set has wrong size" unless range.size == value.size
+ @elements[range] = value.elements
+ elsif value.is_a?(Matrix)
+ Matrix.Raise ErrDimensionMismatch unless value.row_count == 1
+ @elements[range] = value.row(0).elements
+ else
+ @elements[range] = Array.new(range.size, value)
+ end
+ end
# Returns a vector with entries rounded to the given precision
# (see Float#round)
@@ -1882,6 +2048,27 @@ class Vector
self.class.independent?(self, *vs)
end
+ #
+ # Returns +true+ iff all elements are zero.
+ #
+ def zero?
+ all?(&:zero?)
+ end
+
+ def freeze
+ @elements.freeze
+ super
+ end
+
+ #
+ # Called for dup & clone.
+ #
+ private def initialize_copy(v)
+ super
+ @elements = @elements.dup unless frozen?
+ end
+
+
#--
# COMPARING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#++
@@ -1900,13 +2087,6 @@ class Vector
end
#
- # Returns a copy of the vector.
- #
- def clone
- self.class.elements(@elements)
- end
-
- #
# Returns a hash-code for the vector.
#
def hash
@@ -2054,7 +2234,18 @@ class Vector
els = @elements.collect(&block)
self.class.elements(els, false)
end
- alias map collect
+ alias_method :map, :collect
+
+ #
+ # Like Array#collect!
+ #
+ def collect!(&block)
+ return to_enum(:collect!) unless block_given?
+ raise FrozenError, "can't modify frozen Vector" if frozen?
+ @elements.collect!(&block)
+ self
+ end
+ alias map! collect!
#
# Returns the modulus (Pythagorean distance) of the vector.
@@ -2063,8 +2254,8 @@ class Vector
def magnitude
Math.sqrt(@elements.inject(0) {|v, e| v + e.abs2})
end
- alias r magnitude
- alias norm magnitude
+ alias_method :r, :magnitude
+ alias_method :norm, :magnitude
#
# Like Vector#collect2, but returns a Vector instead of an Array.
@@ -2090,7 +2281,7 @@ class Vector
end
#
- # Returns an angle with another vector. Result is within the [0...Math::PI].
+ # Returns an angle with another vector. Result is within the [0..Math::PI].
# Vector[1,0].angle_with(Vector[0,1])
# # => Math::PI / 2
#
@@ -2099,8 +2290,12 @@ class Vector
Vector.Raise ErrDimensionMismatch if size != v.size
prod = magnitude * v.magnitude
raise ZeroVectorError, "Can't get angle of zero vector" if prod == 0
-
- Math.acos( inner_product(v) / prod )
+ dot = inner_product(v)
+ if dot.abs >= prod
+ dot.positive? ? 0 : Math::PI
+ else
+ Math.acos(dot / prod)
+ end
end
#--
@@ -2121,18 +2316,25 @@ class Vector
@elements.dup
end
+ #
+ # Return a single-column matrix from this vector
+ #
+ def to_matrix
+ Matrix.column_vector(self)
+ end
+
def elements_to_f
- warn "#{caller(1)[0]}: warning: Vector#elements_to_f is deprecated"
+ warn "Vector#elements_to_f is deprecated", uplevel: 1
map(&:to_f)
end
def elements_to_i
- warn "#{caller(1)[0]}: warning: Vector#elements_to_i is deprecated"
+ warn "Vector#elements_to_i is deprecated", uplevel: 1
map(&:to_i)
end
def elements_to_r
- warn "#{caller(1)[0]}: warning: Vector#elements_to_r is deprecated"
+ warn "Vector#elements_to_r is deprecated", uplevel: 1
map(&:to_r)
end
diff --git a/lib/matrix/eigenvalue_decomposition.rb b/lib/matrix/eigenvalue_decomposition.rb
index 919db9e83d..bf6637635a 100644
--- a/lib/matrix/eigenvalue_decomposition.rb
+++ b/lib/matrix/eigenvalue_decomposition.rb
@@ -43,7 +43,7 @@ class Matrix
def eigenvector_matrix
Matrix.send(:new, build_eigenvectors.transpose)
end
- alias v eigenvector_matrix
+ alias_method :v, :eigenvector_matrix
# Returns the inverse of the eigenvector matrix +V+
#
@@ -52,7 +52,7 @@ class Matrix
r = r.transpose.inverse unless @symmetric
r
end
- alias v_inv eigenvector_matrix_inv
+ alias_method :v_inv, :eigenvector_matrix_inv
# Returns the eigenvalues in an array
#
@@ -73,7 +73,7 @@ class Matrix
def eigenvalue_matrix
Matrix.diagonal(*eigenvalues)
end
- alias d eigenvalue_matrix
+ alias_method :d, :eigenvalue_matrix
# Returns [eigenvector_matrix, eigenvalue_matrix, eigenvector_matrix_inv]
#
@@ -82,8 +82,8 @@ class Matrix
end
alias_method :to_a, :to_ary
- private
- def build_eigenvectors
+
+ private def build_eigenvectors
# JAMA stores complex eigenvectors in a strange way
# See http://web.archive.org/web/20111016032731/http://cio.nist.gov/esd/emaildir/lists/jama/msg01021.html
@e.each_with_index.map do |imag, i|
@@ -96,9 +96,10 @@ class Matrix
end
end
end
+
# Complex scalar division.
- def cdiv(xr, xi, yr, yi)
+ private def cdiv(xr, xi, yr, yi)
if (yr.abs > yi.abs)
r = yi/yr
d = yr + r*yi
@@ -113,7 +114,7 @@ class Matrix
# Symmetric Householder reduction to tridiagonal form.
- def tridiagonalize
+ private def tridiagonalize
# This is derived from the Algol procedures tred2 by
# Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
@@ -231,7 +232,7 @@ class Matrix
# Symmetric tridiagonal QL algorithm.
- def diagonalize
+ private def diagonalize
# This is derived from the Algol procedures tql2, by
# Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
# Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
@@ -350,7 +351,7 @@ class Matrix
# Nonsymmetric reduction to Hessenberg form.
- def reduce_to_hessenberg
+ private def reduce_to_hessenberg
# This is derived from the Algol procedures orthes and ortran,
# by Martin and Wilkinson, Handbook for Auto. Comp.,
# Vol.ii-Linear Algebra, and the corresponding
@@ -440,11 +441,9 @@ class Matrix
end
end
-
-
# Nonsymmetric reduction from Hessenberg to real Schur form.
- def hessenberg_to_real_schur
+ private def hessenberg_to_real_schur
# This is derived from the Algol procedure hqr2,
# by Martin and Wilkinson, Handbook for Auto. Comp.,
diff --git a/lib/matrix/lup_decomposition.rb b/lib/matrix/lup_decomposition.rb
index 9c1998fd36..c001770a12 100644
--- a/lib/matrix/lup_decomposition.rb
+++ b/lib/matrix/lup_decomposition.rb
@@ -64,7 +64,7 @@ class Matrix
# Returns +true+ if +U+, and hence +A+, is singular.
- def singular? ()
+ def singular?
@column_count.times do |j|
if (@lu[j][j] == 0)
return true
diff --git a/lib/matrix/matrix.gemspec b/lib/matrix/matrix.gemspec
new file mode 100644
index 0000000000..71f0371856
--- /dev/null
+++ b/lib/matrix/matrix.gemspec
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+Gem::Specification.new do |spec|
+ spec.name = "matrix"
+ spec.version = "0.1.0"
+ spec.authors = ["Marc-Andre Lafortune"]
+ spec.email = ["ruby-core@marc-andre.ca"]
+
+ spec.summary = %q{An implementation of Matrix and Vector classes.}
+ spec.description = %q{An implementation of Matrix and Vector classes.}
+ spec.homepage = "https://github.com/ruby/matrix"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "lib/matrix.rb", "matrix.gemspec"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+end
diff --git a/lib/mkmf.rb b/lib/mkmf.rb
index 34dbcda657..0d9d3d9f38 100644
--- a/lib/mkmf.rb
+++ b/lib/mkmf.rb
@@ -7,8 +7,9 @@ require 'rbconfig'
require 'fileutils'
require 'shellwords'
-# :stopdoc:
class String
+ # :stopdoc:
+
# Wraps a string in escaped quotes if it contains whitespace.
def quote
/\s/ =~ self ? "\"#{self}\"" : "#{self}"
@@ -31,15 +32,20 @@ class String
def sans_arguments
self[/\A[^()]+/]
end
+
+ # :startdoc:
end
class Array
+ # :stopdoc:
+
# Wraps all strings in escaped quotes if they contain whitespace.
def quote
map {|s| s.quote}
end
+
+ # :startdoc:
end
-# :startdoc:
##
# mkmf.rb is used by Ruby C extensions to generate a Makefile which will
@@ -129,7 +135,6 @@ module MakeMakefile
$vendorarchdir = CONFIG["vendorarchdir"]
$mswin = /mswin/ =~ RUBY_PLATFORM
- $bccwin = /bccwin/ =~ RUBY_PLATFORM
$mingw = /mingw/ =~ RUBY_PLATFORM
$cygwin = /cygwin/ =~ RUBY_PLATFORM
$netbsd = /netbsd/ =~ RUBY_PLATFORM
@@ -242,7 +247,12 @@ module MakeMakefile
$topdir ||= RbConfig::CONFIG["topdir"]
$arch_hdrdir = "$(extout)/include/$(arch)"
else
- abort "mkmf.rb can't find header files for ruby at #{$hdrdir}/ruby.h"
+ abort <<MESSAGE
+mkmf.rb can't find header files for ruby at #{$hdrdir}/ruby.h
+
+You might have to install separate package for the ruby development
+environment, ruby-dev or ruby-devel for example.
+MESSAGE
end
CONFTEST = "conftest".freeze
@@ -464,7 +474,6 @@ MSG
xsystem(command, *opts)
ensure
log_src(src)
- MakeMakefile.rm_rf "#{CONFTEST}.dSYM"
end
end
@@ -527,6 +536,7 @@ MSG
end
def try_link0(src, opt="", *opts, &b) # :nodoc:
+ exe = CONFTEST+$EXEEXT
cmd = link_command("", opt)
if $universal
require 'tmpdir'
@@ -540,7 +550,10 @@ MSG
end
else
try_do(src, cmd, *opts, &b)
- end and File.executable?(CONFTEST+$EXEEXT)
+ end and File.executable?(exe) or return nil
+ exe
+ ensure
+ MakeMakefile.rm_rf(*Dir["#{CONFTEST}*"]-[exe])
end
# Returns whether or not the +src+ can be compiled as a C source and linked
@@ -554,9 +567,9 @@ MSG
# [+src+] a String which contains a C source
# [+opt+] a String which contains linker options
def try_link(src, opt="", *opts, &b)
- try_link0(src, opt, *opts, &b)
- ensure
- MakeMakefile.rm_f "#{CONFTEST}*", "c0x32*"
+ exe = try_link0(src, opt, *opts, &b) or return false
+ MakeMakefile.rm_f exe
+ true
end
# Returns whether or not the +src+ can be compiled as a C source. +opt+ is
@@ -655,7 +668,8 @@ MSG
end
def try_ldflags(flags, opts = {})
- try_link(MAIN_DOES_NOTHING, flags, {:werror => true}.update(opts))
+ opts = {:werror => true}.update(opts) if $mswin
+ try_link(MAIN_DOES_NOTHING, flags, opts)
end
def append_ldflags(flags, *opts)
@@ -730,7 +744,7 @@ int main() {printf("%"PRI_CONFTEST_PREFIX"#{neg ? 'd' : 'u'}\\n", conftest_const
end
end
ensure
- MakeMakefile.rm_f "#{CONFTEST}*"
+ MakeMakefile.rm_f "#{CONFTEST}#{$EXEEXT}"
end
end
nil
@@ -943,10 +957,10 @@ SRC
a = r = nil
Logging::postpone do
r = yield
- a = (fmt ? "#{fmt % r}" : r ? "yes" : "no") << "\n"
- "#{f}#{m}-------------------- #{a}\n"
+ a = (fmt ? "#{fmt % r}" : r ? "yes" : "no")
+ "#{f}#{m}-------------------- #{a}\n\n"
end
- message(a)
+ message "%s\n", a
Logging::message "--------------------\n\n"
r
end
@@ -1957,6 +1971,7 @@ VPATH = #{vpath.join(CONFIG['PATH_SEPARATOR'])}
headers << '$(RUBY_EXTCONF_H)' if $extconf_h
mk << %{
+CC_WRAPPER = #{CONFIG['CC_WRAPPER']}
CC = #{CONFIG['CC']}
CXX = #{CONFIG['CXX']}
LIBRUBY = #{CONFIG['LIBRUBY']}
@@ -1974,6 +1989,7 @@ cxxflags = #{CONFIG['cxxflags']}
optflags = #{CONFIG['optflags']}
debugflags = #{CONFIG['debugflags']}
warnflags = #{$warnflags}
+cppflags = #{CONFIG['cppflags']}
CCDLFLAGS = #{$static ? '' : CONFIG['CCDLFLAGS']}
CFLAGS = $(CCDLFLAGS) #$CFLAGS $(ARCH_FLAG)
INCFLAGS = -I. #$INCFLAGS
@@ -2121,7 +2137,10 @@ RULES
unless suffixes.empty?
depout.unshift(".SUFFIXES: ." + suffixes.uniq.join(" .") + "\n\n")
end
- depout.unshift("$(OBJS): $(RUBY_EXTCONF_H)\n\n") if $extconf_h
+ if $extconf_h
+ depout.unshift("$(OBJS): $(RUBY_EXTCONF_H)\n\n")
+ depout.unshift("$(OBJS): $(hdrdir)/ruby/win32.h\n\n") if $mswin or $mingw
+ end
depout.flatten!
depout
end
@@ -2245,7 +2264,7 @@ RULES
origdef ||= ''
if $extout and $INSTALLFILES
- $cleanfiles.concat($INSTALLFILES.collect {|files, dir|File.join(dir, files.sub(/\A\.\//, ''))})
+ $cleanfiles.concat($INSTALLFILES.collect {|files, dir|File.join(dir, files.delete_prefix('./'))})
$distcleandirs.concat($INSTALLFILES.collect {|files, dir| dir})
end
@@ -2297,7 +2316,7 @@ TIMESTAMP_DIR = #{$extout && $extmk ? '$(extout)/.timestamp' : '.'}
conf << "\
TARGET_SO_DIR =#{$extout ? " $(RUBYARCHDIR)/" : ''}
TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
-CLEANLIBS = $(TARGET_SO) #{config_string('cleanlibs') {|t| t.gsub(/\$\*/) {n}}}
+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
" #"
@@ -2306,7 +2325,7 @@ CLEANOBJS = *.#{$OBJEXT} #{config_string('cleanobjs') {|t| t.gsub(/\$\*/, "$
mfile.puts(conf)
mfile.print "
all: #{$extout ? "install" : target ? "$(DLLIB)" : "Makefile"}
-static: #{$extmk && !$static ? "all" : "$(STATIC_LIB)#{!$extmk ? " install-rb" : ""}"}
+static: #{$extmk && !$static ? "all" : "$(STATIC_LIB)#{$extout ? " install-rb" : ""}"}
.PHONY: all install static install-so install-rb
.PHONY: clean clean-so clean-static clean-rb
" #"
@@ -2501,6 +2520,9 @@ site-install-rb: install-rb
end
$warnflags = config['warnflags'] unless $extmk
end
+ if (w = rbconfig['CC_WRAPPER']) and !w.empty? and !File.executable?(w)
+ rbconfig['CC_WRAPPER'] = config['CC_WRAPPER'] = ''
+ end
$CFLAGS = with_config("cflags", arg_config("CFLAGS", config["CFLAGS"])).dup
$CXXFLAGS = (with_config("cxxflags", arg_config("CXXFLAGS", config["CXXFLAGS"]))||'').dup
$ARCH_FLAG = with_config("arch_flag", arg_config("ARCH_FLAG", config["ARCH_FLAG"])).dup
@@ -2579,7 +2601,8 @@ MESSAGE
src = src.sub(/\{/) do
$& +
"\n if (argc > 1000000) {\n" +
- refs.map {|n|" printf(\"%p\", &#{n});\n"}.join("") +
+ refs.map {|n|" int (* volatile #{n}p)(void)=(int (*)(void))&#{n};\n"}.join("") +
+ refs.map {|n|" printf(\"%d\", (*#{n}p)());\n"}.join("") +
" }\n"
end
end
diff --git a/lib/monitor.rb b/lib/monitor.rb
index 3ded0b3658..64b1f85e7c 100644
--- a/lib/monitor.rb
+++ b/lib/monitor.rb
@@ -7,8 +7,6 @@
# You can freely distribute/modify this library.
#
-require 'thread'
-
#
# In concurrent programming, a monitor is an object or module intended to be
# used safely by more than one thread. The defining characteristic of a
@@ -89,6 +87,9 @@ require 'thread'
# MonitorMixin module.
#
module MonitorMixin
+ EXCEPTION_NEVER = {Exception => :never}.freeze
+ EXCEPTION_IMMEDIATE = {Exception => :immediate}.freeze
+
#
# FIXME: This isn't documented in Nutshell.
#
@@ -105,13 +106,17 @@ module MonitorMixin
# even if no other thread doesn't signal.
#
def wait(timeout = nil)
- @monitor.__send__(:mon_check_owner)
- count = @monitor.__send__(:mon_exit_for_cond)
- begin
- @cond.wait(@monitor.instance_variable_get(:@mon_mutex), timeout)
- return true
- ensure
- @monitor.__send__(:mon_enter_for_cond, count)
+ Thread.handle_interrupt(EXCEPTION_NEVER) do
+ @monitor.__send__(:mon_check_owner)
+ count = @monitor.__send__(:mon_exit_for_cond)
+ begin
+ Thread.handle_interrupt(EXCEPTION_IMMEDIATE) do
+ @cond.wait(@monitor.instance_variable_get(:@mon_mutex), timeout)
+ end
+ return true
+ ensure
+ @monitor.__send__(:mon_enter_for_cond, count)
+ end
end
end
@@ -204,16 +209,32 @@ module MonitorMixin
end
#
+ # Returns true if this monitor is locked by any thread
+ #
+ def mon_locked?
+ @mon_mutex.locked?
+ end
+
+ #
+ # Returns true if this monitor is locked by current thread.
+ #
+ def mon_owned?
+ @mon_mutex.locked? && @mon_owner == Thread.current
+ end
+
+ #
# Enters exclusive section and executes the block. Leaves the exclusive
# section automatically when the block exits. See example under
# +MonitorMixin+.
#
def mon_synchronize
- mon_enter
+ # Prevent interrupt on handling interrupts; for example timeout errors
+ # it may break locking state.
+ Thread.handle_interrupt(EXCEPTION_NEVER){ mon_enter }
begin
yield
ensure
- mon_exit
+ Thread.handle_interrupt(EXCEPTION_NEVER){ mon_exit }
end
end
alias synchronize mon_synchronize
@@ -239,9 +260,13 @@ module MonitorMixin
# Initializes the MonitorMixin after being included in a class or when an
# object has been extended with the MonitorMixin
def mon_initialize
+ if defined?(@mon_mutex) && @mon_mutex_owner_object_id == object_id
+ raise ThreadError, "already initialized"
+ end
+ @mon_mutex = Thread::Mutex.new
+ @mon_mutex_owner_object_id = object_id
@mon_owner = nil
@mon_count = 0
- @mon_mutex = Thread::Mutex.new
end
def mon_check_owner
diff --git a/lib/mutex_m.gemspec b/lib/mutex_m.gemspec
new file mode 100644
index 0000000000..409ed5b7b2
--- /dev/null
+++ b/lib/mutex_m.gemspec
@@ -0,0 +1,27 @@
+begin
+ require_relative "lib/mutex_m"
+rescue LoadError
+ # for Ruby core repository
+ require_relative "mutex_m"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "mutex_m"
+ spec.version = Mutex_m::VERSION
+ spec.authors = ["Keiju ISHITSUKA"]
+ spec.email = ["keiju@ruby-lang.org"]
+
+ spec.summary = %q{Mixin to extend objects to be handled like a Mutex.}
+ spec.description = %q{Mixin to extend objects to be handled like a Mutex.}
+ spec.homepage = "https://github.com/ruby/mutex_m"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "lib/mutex_m.rb", "mutex_m.gemspec"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+ spec.add_development_dependency "test-unit"
+end
diff --git a/lib/mutex_m.rb b/lib/mutex_m.rb
index a8472f1582..dd47934bea 100644
--- a/lib/mutex_m.rb
+++ b/lib/mutex_m.rb
@@ -10,9 +10,6 @@
#
# --
-
-require 'thread'
-
# = mutex_m.rb
#
# When 'mutex_m' is required, any object that extends or includes Mutex_m will
@@ -28,16 +25,23 @@ require 'thread'
# obj.extend Mutex_m
#
# Or mixin Mutex_m into your module to your class inherit Mutex instance
-# methods.
+# methods --- remember to call super() in your class initialize method.
#
# class Foo
# include Mutex_m
+# def initialize
+# # ...
+# super()
+# end
# # ...
# end
# obj = Foo.new
# # this obj can be handled like Mutex
#
module Mutex_m
+
+ VERSION = "0.1.0"
+
def Mutex_m.define_aliases(cl) # :nodoc:
cl.module_eval %q{
alias locked? mu_locked?
diff --git a/lib/net/ftp.rb b/lib/net/ftp.rb
index bd89956d36..c5d669d898 100644
--- a/lib/net/ftp.rb
+++ b/lib/net/ftp.rb
@@ -17,8 +17,12 @@
require "socket"
require "monitor"
-require "net/protocol"
+require_relative "protocol"
require "time"
+begin
+ require "openssl"
+rescue LoadError
+end
module Net
@@ -73,8 +77,12 @@ module Net
# - #rename
# - #delete
#
- class FTP
+ class FTP < Protocol
include MonitorMixin
+ if defined?(OpenSSL::SSL)
+ include OpenSSL
+ include SSL
+ end
# :stopdoc:
FTP_PORT = 21
@@ -89,6 +97,10 @@ module Net
# When +true+, the connection is in passive mode. Default: +true+.
attr_accessor :passive
+ # When +true+, use the IP address in PASV responses. Otherwise, it uses
+ # the same IP address for the control connection. Default: +false+.
+ attr_accessor :use_pasv_ip
+
# When +true+, all traffic to and from the server is written
# to +$stdout+. Default: +false+.
attr_accessor :debug_mode
@@ -103,6 +115,13 @@ module Net
# Net::OpenTimeout exception. The default value is +nil+.
attr_accessor :open_timeout
+ # Number of seconds to wait for the TLS handshake. Any number
+ # may be used, including Floats for fractional seconds. If the FTP
+ # object cannot complete the TLS handshake in this many seconds, it
+ # raises a Net::OpenTimeout exception. The default value is +nil+.
+ # If +ssl_handshake_timeout+ is +nil+, +open_timeout+ is used instead.
+ attr_accessor :ssl_handshake_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 FTP object cannot read data in this many seconds,
@@ -143,38 +162,121 @@ module Net
# If a block is given, it is passed the +FTP+ object, which will be closed
# when the block finishes, or when an exception is raised.
#
- def FTP.open(host, user = nil, passwd = nil, acct = nil)
+ def FTP.open(host, *args)
if block_given?
- ftp = new(host, user, passwd, acct)
+ ftp = new(host, *args)
begin
yield ftp
ensure
ftp.close
end
else
- new(host, user, passwd, acct)
+ new(host, *args)
end
end
+ # :call-seq:
+ # Net::FTP.new(host = nil, options = {})
#
# Creates and returns a new +FTP+ object. If a +host+ is given, a connection
- # is made. Additionally, if the +user+ is given, the given user name,
- # password, and (optionally) account are used to log in. See #login.
- #
- def initialize(host = nil, user = nil, passwd = nil, acct = nil)
+ # is made.
+ #
+ # +options+ is an option hash, each key of which is a symbol.
+ #
+ # The available options are:
+ #
+ # port:: Port number (default value is 21)
+ # ssl:: If options[:ssl] is true, then an attempt will be made
+ # to use SSL (now TLS) to connect to the server. For this
+ # to work OpenSSL [OSSL] and the Ruby OpenSSL [RSSL]
+ # extensions need to be installed. If options[:ssl] is a
+ # hash, it's passed to OpenSSL::SSL::SSLContext#set_params
+ # as parameters.
+ # private_data_connection:: If true, TLS is used for data connections.
+ # Default: +true+ when options[:ssl] is true.
+ # username:: Username for login. If options[:username] is the string
+ # "anonymous" and the options[:password] is +nil+,
+ # "anonymous@" is used as a password.
+ # password:: Password for login.
+ # account:: Account information for ACCT.
+ # passive:: When +true+, the connection is in passive mode. Default:
+ # +true+.
+ # open_timeout:: Number of seconds to wait for the connection to open.
+ # See Net::FTP#open_timeout for details. Default: +nil+.
+ # read_timeout:: Number of seconds to wait for one block to be read.
+ # See Net::FTP#read_timeout for details. Default: +60+.
+ # ssl_handshake_timeout:: Number of seconds to wait for the TLS
+ # handshake.
+ # See Net::FTP#ssl_handshake_timeout for
+ # details. Default: +nil+.
+ # use_pasv_ip:: When +true+, use the IP address in PASV responses.
+ # Otherwise, it uses the same IP address for the control
+ # connection. Default: +false+.
+ # debug_mode:: When +true+, all traffic to and from the server is
+ # written to +$stdout+. Default: +false+.
+ #
+ def initialize(host = nil, user_or_options = {}, passwd = nil, acct = nil)
super()
+ begin
+ options = user_or_options.to_hash
+ rescue NoMethodError
+ # for backward compatibility
+ options = {}
+ options[:username] = user_or_options
+ options[:password] = passwd
+ options[:account] = acct
+ end
+ @host = nil
+ if options[:ssl]
+ unless defined?(OpenSSL::SSL)
+ raise "SSL extension not installed"
+ end
+ ssl_params = options[:ssl] == true ? {} : options[:ssl]
+ @ssl_context = SSLContext.new
+ @ssl_context.set_params(ssl_params)
+ if defined?(VerifyCallbackProc)
+ @ssl_context.verify_callback = VerifyCallbackProc
+ end
+ @ssl_context.session_cache_mode =
+ OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
+ OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
+ @ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess }
+ @ssl_session = nil
+ if options[:private_data_connection].nil?
+ @private_data_connection = true
+ else
+ @private_data_connection = options[:private_data_connection]
+ end
+ else
+ @ssl_context = nil
+ if options[:private_data_connection]
+ raise ArgumentError,
+ "private_data_connection can be set to true only when ssl is enabled"
+ end
+ @private_data_connection = false
+ end
@binary = true
- @passive = @@default_passive
- @debug_mode = false
+ if options[:passive].nil?
+ @passive = @@default_passive
+ else
+ @passive = options[:passive]
+ end
+ if options[:debug_mode].nil?
+ @debug_mode = false
+ else
+ @debug_mode = options[:debug_mode]
+ end
@resume = false
- @sock = NullSocket.new
+ @bare_sock = @sock = NullSocket.new
@logged_in = false
- @open_timeout = nil
- @read_timeout = 60
+ @open_timeout = options[:open_timeout]
+ @ssl_handshake_timeout = options[:ssl_handshake_timeout]
+ @read_timeout = options[:read_timeout] || 60
+ @use_pasv_ip = options[:use_pasv_ip] || false
if host
- connect(host)
- if user
- login(user, passwd, acct)
+ connect(host, options[:port] || FTP_PORT)
+ if options[:username]
+ login(options[:username], options[:password], options[:account])
end
end
end
@@ -220,35 +322,49 @@ module Net
# Obsolete
def return_code # :nodoc:
- $stderr.puts("warning: Net::FTP#return_code is obsolete and do nothing")
+ warn("Net::FTP#return_code is obsolete and do nothing", uplevel: 1)
return "\n"
end
# Obsolete
def return_code=(s) # :nodoc:
- $stderr.puts("warning: Net::FTP#return_code= is obsolete and do nothing")
+ warn("Net::FTP#return_code= is obsolete and do nothing", uplevel: 1)
end
# Constructs a socket with +host+ and +port+.
#
# If SOCKSSocket is defined and the environment (ENV) defines
- # SOCKS_SERVER, then a SOCKSSocket is returned, else a TCPSocket is
+ # SOCKS_SERVER, then a SOCKSSocket is returned, else a Socket is
# returned.
def open_socket(host, port) # :nodoc:
- return Timeout.timeout(@open_timeout, Net::OpenTimeout) {
+ return Timeout.timeout(@open_timeout, OpenTimeout) {
if defined? SOCKSSocket and ENV["SOCKS_SERVER"]
@passive = true
- sock = SOCKSSocket.open(host, port)
+ SOCKSSocket.open(host, port)
else
- sock = TCPSocket.open(host, port)
+ Socket.tcp(host, port)
end
- io = BufferedSocket.new(sock)
- io.read_timeout = @read_timeout
- io
}
end
private :open_socket
+ def start_tls_session(sock)
+ ssl_sock = SSLSocket.new(sock, @ssl_context)
+ ssl_sock.sync_close = true
+ ssl_sock.hostname = @host if ssl_sock.respond_to? :hostname=
+ if @ssl_session &&
+ Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout
+ # ProFTPD returns 425 for data connections if session is not reused.
+ ssl_sock.session = @ssl_session
+ end
+ ssl_socket_connect(ssl_sock, @ssl_handshake_timeout || @open_timeout)
+ if @ssl_context.verify_mode != VERIFY_NONE
+ ssl_sock.post_connection_check(@host)
+ end
+ return ssl_sock
+ end
+ private :start_tls_session
+
#
# Establishes an FTP connection to host, optionally overriding the default
# port. If the environment variable +SOCKS_SERVER+ is set, sets up the
@@ -260,8 +376,24 @@ module Net
print "connect: ", host, ", ", port, "\n"
end
synchronize do
- @sock = open_socket(host, port)
+ @host = host
+ @bare_sock = open_socket(host, port)
+ @sock = BufferedSocket.new(@bare_sock, read_timeout: @read_timeout)
voidresp
+ if @ssl_context
+ begin
+ voidcmd("AUTH TLS")
+ ssl_sock = start_tls_session(@bare_sock)
+ @sock = BufferedSSLSocket.new(ssl_sock, read_timeout: @read_timeout)
+ if @private_data_connection
+ voidcmd("PBSZ 0")
+ voidcmd("PROT P")
+ end
+ rescue OpenSSL::SSL::SSLError, OpenTimeout
+ @sock.close
+ raise
+ end
+ end
end
end
@@ -383,10 +515,10 @@ module Net
# Constructs and send the appropriate PORT (or EPRT) command
def sendport(host, port) # :nodoc:
- af = (@sock.peeraddr)[0]
- if af == "AF_INET"
+ remote_address = @bare_sock.remote_address
+ if remote_address.ipv4?
cmd = "PORT " + (host.split(".") + port.divmod(256)).join(",")
- elsif af == "AF_INET6"
+ elsif remote_address.ipv6?
cmd = sprintf("EPRT |2|%s|%d|", host, port)
else
raise FTPProtoError, host
@@ -397,13 +529,13 @@ module Net
# Constructs a TCPServer socket
def makeport # :nodoc:
- TCPServer.open(@sock.addr[3], 0)
+ Addrinfo.tcp(@bare_sock.local_address.ip_address, 0).listen
end
private :makeport
# sends the appropriate command to enable a passive connection
def makepasv # :nodoc:
- if @sock.peeraddr[0] == "AF_INET"
+ if @bare_sock.remote_address.ipv4?
host, port = parse227(sendcmd("PASV"))
else
host, port = parse229(sendcmd("EPSV"))
@@ -433,7 +565,8 @@ module Net
else
sock = makeport
begin
- sendport(sock.addr[3], sock.addr[1])
+ addr = sock.local_address
+ sendport(addr.ip_address, addr.ip_port)
if @resume and rest_offset
resp = sendcmd("REST " + rest_offset.to_s)
if !resp.start_with?("3")
@@ -446,15 +579,19 @@ module Net
if !resp.start_with?("1")
raise FTPReplyError, resp
end
- conn = BufferedSocket.new(sock.accept)
- conn.read_timeout = @read_timeout
+ conn, = sock.accept
sock.shutdown(Socket::SHUT_WR) rescue nil
sock.read rescue nil
ensure
sock.close
end
end
- return conn
+ if @private_data_connection
+ return BufferedSSLSocket.new(start_tls_session(conn),
+ read_timeout: @read_timeout)
+ else
+ return BufferedSocket.new(conn, read_timeout: @read_timeout)
+ end
end
private :transfercmd
@@ -622,10 +759,10 @@ module Net
if localfile
if @resume
rest_offset = File.size?(localfile)
- f = open(localfile, "a")
+ f = File.open(localfile, "a")
else
rest_offset = nil
- f = open(localfile, "w")
+ f = File.open(localfile, "w")
end
elsif !block_given?
result = String.new
@@ -655,7 +792,7 @@ module Net
f = nil
result = nil
if localfile
- f = open(localfile, "w")
+ f = File.open(localfile, "w")
elsif !block_given?
result = String.new
end
@@ -701,7 +838,7 @@ module Net
else
rest_offset = nil
end
- f = open(localfile)
+ f = File.open(localfile)
begin
f.binmode
if rest_offset
@@ -720,7 +857,7 @@ module Net
# passing in the transmitted data one line at a time.
#
def puttextfile(localfile, remotefile = File.basename(localfile), &block) # :yield: line
- f = open(localfile)
+ f = File.open(localfile)
begin
storlines("STOR #{remotefile}", f, &block)
ensure
@@ -1107,11 +1244,16 @@ module Net
#
# Returns the status (STAT command).
+ # pathname - when stat is invoked with pathname as a parameter it acts like
+ # list but alot faster and over the same tcp session.
#
- def status
- line = "STAT" + CRLF
- print "put: STAT\n" if @debug_mode
- @sock.send(line, Socket::MSG_OOB)
+ def status(pathname = nil)
+ line = pathname ? "STAT #{pathname}" : "STAT"
+ if /[\r\n]/ =~ line
+ raise ArgumentError, "A line must not contain CR or LF"
+ end
+ print "put: #{line}\n" if @debug_mode
+ @sock.send(line + CRLF, Socket::MSG_OOB)
return getresp
end
@@ -1196,7 +1338,12 @@ module Net
raise FTPReplyError, resp
end
if m = /\((?<host>\d+(,\d+){3}),(?<port>\d+,\d+)\)/.match(resp)
- return parse_pasv_ipv4_host(m["host"]), parse_pasv_port(m["port"])
+ if @use_pasv_ip
+ host = parse_pasv_ipv4_host(m["host"])
+ else
+ host = @bare_sock.remote_address.ip_address
+ end
+ return host, parse_pasv_port(m["port"])
else
raise FTPProtoError, resp
end
@@ -1249,7 +1396,7 @@ module Net
raise FTPReplyError, resp
end
if m = /\((?<d>[!-~])\k<d>\k<d>(?<port>\d+)\k<d>\)/.match(resp)
- return @sock.peeraddr[3], m["port"].to_i
+ return @bare_sock.remote_address.ip_address, m["port"].to_i
else
raise FTPProtoError, resp
end
@@ -1286,7 +1433,7 @@ module Net
end
class BufferedSocket < BufferedIO
- [:addr, :peeraddr, :send, :shutdown].each do |method|
+ [:local_address, :remote_address, :addr, :peeraddr, :send, :shutdown].each do |method|
define_method(method) { |*args|
@io.__send__(method, *args)
}
@@ -1297,7 +1444,7 @@ module Net
s = super(len, String.new, true)
return s.empty? ? nil : s
else
- result = ""
+ result = String.new
while s = super(DEFAULT_BLOCKSIZE, String.new, true)
break if s.empty?
result << s
@@ -1319,6 +1466,37 @@ module Net
return line
end
end
+
+ if defined?(OpenSSL::SSL::SSLSocket)
+ class BufferedSSLSocket < BufferedSocket
+ def initialize(*args)
+ super
+ @is_shutdown = false
+ end
+
+ def shutdown(*args)
+ # SSL_shutdown() will be called from SSLSocket#close, and
+ # SSL_shutdown() will send the "close notify" alert to the peer,
+ # so shutdown(2) should not be called.
+ @is_shutdown = true
+ end
+
+ def send(mesg, flags, dest = nil)
+ # Ignore flags and dest.
+ @io.write(mesg)
+ end
+
+ private
+
+ def rbuf_fill
+ if @is_shutdown
+ raise EOFError, "shutdown has been called"
+ else
+ super
+ end
+ end
+ end
+ end
# :startdoc:
end
end
diff --git a/lib/net/http.rb b/lib/net/http.rb
index d68d2c740e..31cbb7a866 100644
--- a/lib/net/http.rb
+++ b/lib/net/http.rb
@@ -20,11 +20,11 @@
# See Net::HTTP for an overview and examples.
#
-require 'net/protocol'
+require_relative 'protocol'
require 'uri'
+autoload :OpenSSL, 'openssl'
module Net #:nodoc:
- autoload :OpenSSL, 'openssl'
# :stopdoc:
class HTTPBadResponse < StandardError; end
@@ -35,7 +35,7 @@ module Net #:nodoc:
#
# 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)
+ # [RFC2616](http://www.ietf.org/rfc/rfc2616.txt).
#
# 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
@@ -87,7 +87,7 @@ module Net #:nodoc:
#
# == How to use Net::HTTP
#
- # The following example code can be used as the basis of a HTTP user-agent
+ # 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.
#
@@ -104,14 +104,13 @@ module Net #:nodoc:
# open for multiple requests in the block if the server indicates it
# supports persistent connections.
#
+ # 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.
+ #
# The request types Net::HTTP supports are listed below in the section "HTTP
# Request Classes".
#
- # If you wish to re-use a connection across multiple HTTP requests without
- # automatically closing it you can use ::new instead of ::start. #request
- # will automatically open a connection to the server if one is not currently
- # open. You can manually close the connection with #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.
@@ -170,7 +169,7 @@ module Net #:nodoc:
# === POST
#
# A POST can be made using the Net::HTTP::Post request class. This example
- # creates a urlencoded POST body:
+ # creates a URL encoded POST body:
#
# uri = URI('http://www.example.com/todo.cgi')
# req = Net::HTTP::Post.new(uri)
@@ -187,13 +186,10 @@ module Net #:nodoc:
# res.value
# end
#
- # At this time Net::HTTP does not support multipart/form-data. To send
- # multipart/form-data use Net::HTTPRequest#body= and
- # Net::HTTPRequest#content_type=:
+ # To send multipart/form-data use Net::HTTPHeader#set_form:
#
# req = Net::HTTP::Post.new(uri)
- # req.body = multipart_data
- # req.content_type = 'multipart/form-data'
+ # req.set_form([['upload', File.open('foo.bar')]], 'multipart/form-data')
#
# 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).
@@ -222,7 +218,7 @@ module Net #:nodoc:
# === Basic Authentication
#
# Basic authentication is performed according to
- # [RFC2617](http://www.ietf.org/rfc/rfc2617.txt)
+ # [RFC2617](http://www.ietf.org/rfc/rfc2617.txt).
#
# uri = URI('http://example.com/index.html?key=value')
#
@@ -266,7 +262,7 @@ module Net #:nodoc:
# end
#
# Or if you simply want to make a GET request, you may pass in an URI
- # object that has a HTTPS URL. Net::HTTP automatically turn on TLS
+ # 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://example.com/')
@@ -503,7 +499,7 @@ module Net #:nodoc:
def HTTP.post(url, data, header = nil)
start(url.hostname, url.port,
:use_ssl => url.scheme == 'https' ) {|http|
- http.post(url.path, data, header)
+ http.post(url, data, header)
}
end
@@ -560,7 +556,7 @@ module Net #:nodoc:
# :call-seq:
# HTTP.start(address, port, p_addr, p_port, p_user, p_pass, &block)
- # HTTP.start(address, port=nil, p_addr=nil, p_port=nil, p_user=nil, p_pass=nil, opt, &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.
@@ -576,7 +572,7 @@ module Net #:nodoc:
#
# _opt_ sets following values by its accessor.
# The keys are ca_file, ca_path, cert, cert_store, ciphers,
- # close_on_empty_response, key, open_timeout, read_timeout, ssl_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.
@@ -591,6 +587,7 @@ module Net #:nodoc:
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
+ p_addr = :ENV if arg.size < 2
port = https_default_port if !port && opt && opt[:use_ssl]
http = new(address, port, p_addr, p_port, p_user, p_pass)
@@ -626,12 +623,13 @@ module Net #:nodoc:
# detection from the environment. To disable proxy detection set +p_addr+
# to nil.
#
- # If you are connecting to a custom proxy, +p_addr+ the DNS name or IP
- # address of the proxy host, +p_port+ the port to use to access the proxy,
- # and +p_user+ and +p_pass+ the username and password if authorization is
- # required to use the proxy.
+ # 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.
#
- def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil)
+ 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
if proxy_class? then # from Net::HTTP::Proxy()
@@ -643,6 +641,10 @@ 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)
+ p_addr = nil
+ p_port = nil
+ end
http.proxy_address = p_addr
http.proxy_port = p_port || default_port
http.proxy_user = p_user
@@ -668,7 +670,9 @@ module Net #:nodoc:
@started = false
@open_timeout = 60
@read_timeout = 60
+ @write_timeout = 60
@continue_timeout = nil
+ @max_retries = 1
@debug_output = nil
@proxy_from_env = false
@@ -701,7 +705,7 @@ module Net #:nodoc:
# http.start { .... }
#
def set_debug_output(output)
- warn 'Net::HTTP#set_debug_output called after HTTP started' if started?
+ warn 'Net::HTTP#set_debug_output called after HTTP started', uplevel: 1 if started?
@debug_output = output
end
@@ -735,12 +739,41 @@ module Net #:nodoc:
# it raises a Net::ReadTimeout exception. The default value is 60 seconds.
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.
+ attr_reader :write_timeout
+
+ # 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.
+ def max_retries=(retries)
+ retries = retries.to_int
+ if retries < 0
+ raise ArgumentError, 'max_retries should be non-negative integer number'
+ end
+ @max_retries = retries
+ end
+
+ attr_reader :max_retries
+
# Setter for the read_timeout attribute.
def read_timeout=(sec)
@socket.read_timeout = sec if @socket
@read_timeout = sec
end
+ # Setter for the write_timeout attribute.
+ 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+.
@@ -793,6 +826,8 @@ module Net #:nodoc:
:@key,
:@ssl_timeout,
:@ssl_version,
+ :@min_version,
+ :@max_version,
:@verify_callback,
:@verify_depth,
:@verify_mode,
@@ -806,6 +841,8 @@ module Net #:nodoc:
:key,
:ssl_timeout,
:ssl_version,
+ :min_version,
+ :max_version,
:verify_callback,
:verify_depth,
:verify_mode,
@@ -840,6 +877,12 @@ module Net #:nodoc:
# Sets the SSL version. See OpenSSL::SSL::SSLContext#ssl_version=
attr_accessor :ssl_version
+ # Sets the minimum SSL version. See OpenSSL::SSL::SSLContext#min_version=
+ attr_accessor :min_version
+
+ # Sets the maximum SSL version. See OpenSSL::SSL::SSLContext#max_version=
+ attr_accessor :max_version
+
# Sets the verify callback for the server certification verification.
attr_accessor :verify_callback
@@ -910,6 +953,23 @@ module Net #:nodoc:
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
D "opened"
if use_ssl?
+ if proxy?
+ plain_sock = BufferedIO.new(s, read_timeout: @read_timeout,
+ write_timeout: @write_timeout,
+ continue_timeout: @continue_timeout,
+ debug_output: @debug_output)
+ buf = "CONNECT #{@address}:#{@port} HTTP/#{HTTPVersion}\r\n"
+ buf << "Host: #{@address}:#{@port}\r\n"
+ if proxy_user
+ credential = ["#{proxy_user}:#{proxy_pass}"].pack('m0')
+ buf << "Proxy-Authorization: Basic #{credential}\r\n"
+ end
+ buf << "\r\n"
+ plain_sock.write(buf)
+ HTTPResponse.read_new(plain_sock).value
+ # assuming nothing left in buffers after successful CONNECT response
+ end
+
ssl_parameters = Hash.new
iv_list = instance_variables
SSL_IVNAMES.each_with_index do |ivname, i|
@@ -920,46 +980,36 @@ module Net #:nodoc:
end
@ssl_context = OpenSSL::SSL::SSLContext.new
@ssl_context.set_params(ssl_parameters)
+ @ssl_context.session_cache_mode =
+ OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
+ OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
+ @ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess }
D "starting SSL for #{conn_address}:#{conn_port}..."
s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
s.sync_close = true
- D "SSL established"
- end
- @socket = BufferedIO.new(s)
- @socket.read_timeout = @read_timeout
- @socket.continue_timeout = @continue_timeout
- @socket.debug_output = @debug_output
- if use_ssl?
- begin
- if proxy?
- buf = "CONNECT #{@address}:#{@port} HTTP/#{HTTPVersion}\r\n"
- buf << "Host: #{@address}:#{@port}\r\n"
- if proxy_user
- credential = ["#{proxy_user}:#{proxy_pass}"].pack('m0')
- buf << "Proxy-Authorization: Basic #{credential}\r\n"
- end
- buf << "\r\n"
- @socket.write(buf)
- HTTPResponse.read_new(@socket).value
- end
- # Server Name Indication (SNI) RFC 3546
- s.hostname = @address if s.respond_to? :hostname=
- if @ssl_session and
- Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout
- s.session = @ssl_session if @ssl_session
- end
- ssl_socket_connect(s, @open_timeout)
- if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
- s.post_connection_check(@address)
- end
- @ssl_session = s.session
- rescue => exception
- D "Conn close because of connect error #{exception}"
- @socket.close if @socket and not @socket.closed?
- raise exception
+ # Server Name Indication (SNI) RFC 3546
+ s.hostname = @address if s.respond_to? :hostname=
+ if @ssl_session and
+ Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout
+ s.session = @ssl_session
end
+ ssl_socket_connect(s, @open_timeout)
+ if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
+ s.post_connection_check(@address)
+ end
+ D "SSL established, protocol: #{s.ssl_version}, cipher: #{s.cipher[0]}"
end
+ @socket = BufferedIO.new(s, read_timeout: @read_timeout,
+ write_timeout: @write_timeout,
+ continue_timeout: @continue_timeout,
+ debug_output: @debug_output)
on_connect
+ rescue => exception
+ if s
+ D "Conn close because of connect error #{exception}"
+ s.close
+ end
+ raise
end
private :connect
@@ -976,7 +1026,7 @@ module Net #:nodoc:
def do_finish
@started = false
- @socket.close if @socket and not @socket.closed?
+ @socket.close if @socket
@socket = nil
end
private :do_finish
@@ -1078,14 +1128,29 @@ module Net #:nodoc:
end
end
- # The proxy username, if one is configured
+ # [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.
def proxy_user
- @proxy_user
+ if ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE && @proxy_from_env
+ proxy_uri&.user
+ else
+ @proxy_user
+ end
end
- # The proxy password, if one is configured
+ # The password of the proxy server, if one is configured.
def proxy_pass
- @proxy_pass
+ if ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE && @proxy_from_env
+ proxy_uri&.password
+ else
+ @proxy_pass
+ end
end
alias proxyaddr proxy_address #:nodoc: obsolete
@@ -1459,14 +1524,14 @@ module Net #:nodoc:
# avoid a dependency on OpenSSL
defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : IOError,
Timeout::Error => exception
- if count == 0 && IDEMPOTENT_METHODS_.include?(req.method)
+ if count < max_retries && IDEMPOTENT_METHODS_.include?(req.method)
count += 1
- @socket.close if @socket and not @socket.closed?
+ @socket.close if @socket
D "Conn close because of error #{exception}, and retry"
retry
end
D "Conn close because of error #{exception}"
- @socket.close if @socket and not @socket.closed?
+ @socket.close if @socket
raise
end
@@ -1474,7 +1539,7 @@ module Net #:nodoc:
res
rescue => exception
D "Conn close because of error #{exception}"
- @socket.close if @socket and not @socket.closed?
+ @socket.close if @socket
raise exception
end
@@ -1562,11 +1627,10 @@ module Net #:nodoc:
private
def addr_port
- if use_ssl?
- address() + (port == HTTP.https_default_port ? '' : ":#{port()}")
- else
- address() + (port == HTTP.http_default_port ? '' : ":#{port()}")
- end
+ addr = address
+ addr = "[#{addr}]" if addr.include?(":")
+ default_port = use_ssl? ? HTTP.https_default_port : HTTP.http_default_port
+ default_port == port ? addr : "#{addr}:#{port}"
end
def D(msg)
@@ -1578,17 +1642,17 @@ module Net #:nodoc:
end
-require 'net/http/exceptions'
+require_relative 'http/exceptions'
-require 'net/http/header'
+require_relative 'http/header'
-require 'net/http/generic_request'
-require 'net/http/request'
-require 'net/http/requests'
+require_relative 'http/generic_request'
+require_relative 'http/request'
+require_relative 'http/requests'
-require 'net/http/response'
-require 'net/http/responses'
+require_relative 'http/response'
+require_relative 'http/responses'
-require 'net/http/proxy_delta'
+require_relative 'http/proxy_delta'
-require 'net/http/backward'
+require_relative 'http/backward'
diff --git a/lib/net/http/exceptions.rb b/lib/net/http/exceptions.rb
index 0d34526616..da5f7a70fc 100644
--- a/lib/net/http/exceptions.rb
+++ b/lib/net/http/exceptions.rb
@@ -20,7 +20,14 @@ class Net::HTTPServerException < Net::ProtoServerError
# We cannot use the name "HTTPServerError", it is the name of the response.
include Net::HTTPExceptions
end
+
+# for compatibility
+Net::HTTPClientException = Net::HTTPServerException
+
class Net::HTTPFatalError < Net::ProtoFatalError
include Net::HTTPExceptions
end
+module Net
+ deprecate_constant(:HTTPServerException)
+end
diff --git a/lib/net/http/generic_request.rb b/lib/net/http/generic_request.rb
index 6c5ceafe61..3ff6d88f0c 100644
--- a/lib/net/http/generic_request.rb
+++ b/lib/net/http/generic_request.rb
@@ -14,6 +14,8 @@ class Net::HTTPGenericRequest
@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
@uri = uri_or_path.dup
host = @uri.hostname.dup
host << ":".freeze << @uri.port.to_s if @uri.port != @uri.default_port
@@ -82,7 +84,7 @@ class Net::HTTPGenericRequest
end
def body_exist?
- warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?" if $VERBOSE
+ warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?", uplevel: 1 if $VERBOSE
response_body_permitted?
end
@@ -168,9 +170,8 @@ class Net::HTTPGenericRequest
def write(buf)
# avoid memcpy() of buf, buf can huge and eat memory bandwidth
- @sock.write("#{buf.bytesize.to_s(16)}\r\n")
- rv = @sock.write(buf)
- @sock.write("\r\n")
+ rv = buf.bytesize
+ @sock.write("#{rv.to_s(16)}\r\n", buf, "\r\n")
rv
end
@@ -299,7 +300,7 @@ class Net::HTTPGenericRequest
def supply_default_content_type
return if content_type()
- warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
+ warn 'net/http: Content-Type did not set; using application/x-www-form-urlencoded', uplevel: 1 if $VERBOSE
set_content_type 'application/x-www-form-urlencoded'
end
diff --git a/lib/net/http/header.rb b/lib/net/http/header.rb
index 63a163afbd..7865814208 100644
--- a/lib/net/http/header.rb
+++ b/lib/net/http/header.rb
@@ -14,11 +14,15 @@ module Net::HTTPHeader
@header = {}
return unless initheader
initheader.each do |key, value|
- warn "net/http: warning: duplicated HTTP header: #{key}" if key?(key) and $VERBOSE
+ warn "net/http: duplicated HTTP header: #{key}", uplevel: 1 if key?(key) and $VERBOSE
if value.nil?
- warn "net/http: warning: nil HTTP header: #{key}" if $VERBOSE
+ warn "net/http: nil HTTP header: #{key}", uplevel: 1 if $VERBOSE
else
- @header[key.downcase] = [value.strip]
+ value = value.strip # raise error for invalid byte sequences
+ if value.count("\r\n") > 0
+ raise ArgumentError, "header #{key} has field value #{value.inspect}, this cannot include CR/LF"
+ end
+ @header[key.downcase.to_s] = [value]
end
end
end
@@ -32,17 +36,17 @@ module Net::HTTPHeader
# Returns the header field corresponding to the case-insensitive key.
# For example, a key of "Content-Type" might return "text/html"
def [](key)
- a = @header[key.downcase] or return nil
+ a = @header[key.downcase.to_s] or return nil
a.join(', ')
end
# Sets the header field corresponding to the case-insensitive key.
def []=(key, val)
unless val
- @header.delete key.downcase
+ @header.delete key.downcase.to_s
return val
end
- @header[key.downcase] = [val]
+ set_field(key, val)
end
# [Ruby 1.8.3]
@@ -61,10 +65,39 @@ module Net::HTTPHeader
# p request.get_fields('X-My-Header') #=> ["a", "b", "c"]
#
def add_field(key, val)
- if @header.key?(key.downcase)
- @header[key.downcase].push val
+ stringified_downcased_key = key.downcase.to_s
+ if @header.key?(stringified_downcased_key)
+ append_field_value(@header[stringified_downcased_key], val)
+ else
+ set_field(key, val)
+ end
+ end
+
+ private def set_field(key, val)
+ case val
+ when Enumerable
+ ary = []
+ append_field_value(ary, val)
+ @header[key.downcase.to_s] = ary
else
- @header[key.downcase] = [val]
+ val = val.to_s # for compatibility use to_s instead of to_str
+ if val.b.count("\r\n") > 0
+ raise ArgumentError, 'header field value cannot include CR/LF'
+ end
+ @header[key.downcase.to_s] = [val]
+ end
+ end
+
+ private def append_field_value(ary, val)
+ case val
+ when Enumerable
+ val.each{|x| append_field_value(ary, x)}
+ else
+ val = val.to_s
+ if /[\r\n]/n.match?(val.b)
+ raise ArgumentError, 'header field value cannot include CR/LF'
+ end
+ ary.push val
end
end
@@ -80,8 +113,9 @@ module Net::HTTPHeader
# #=> "session=al98axx; expires=Fri, 31-Dec-1999 23:58:23, query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"
#
def get_fields(key)
- return nil unless @header[key.downcase]
- @header[key.downcase].dup
+ stringified_downcased_key = key.downcase.to_s
+ return nil unless @header[stringified_downcased_key]
+ @header[stringified_downcased_key].dup
end
# Returns the header field corresponding to the case-insensitive key.
@@ -89,7 +123,7 @@ module Net::HTTPHeader
# raises an IndexError if there's no header field named +key+
# See Hash#fetch
def fetch(key, *args, &block) #:yield: +key+
- a = @header.fetch(key.downcase, *args, &block)
+ a = @header.fetch(key.downcase.to_s, *args, &block)
a.kind_of?(Array) ? a.join(', ') : a
end
@@ -150,12 +184,12 @@ module Net::HTTPHeader
# Removes a header field, specified by case-insensitive key.
def delete(key)
- @header.delete(key.downcase)
+ @header.delete(key.downcase.to_s)
end
# true if +key+ header exists.
def key?(key)
- @header.key?(key.downcase)
+ @header.key?(key.downcase.to_s)
end
# Returns a Hash consisting of header names and array of values.
diff --git a/lib/net/http/response.rb b/lib/net/http/response.rb
index 1351d7b2d2..ca0a6eb379 100644
--- a/lib/net/http/response.rb
+++ b/lib/net/http/response.rb
@@ -140,17 +140,17 @@ class Net::HTTPResponse
#
def response #:nodoc:
- warn "#{caller(1)[0]}: warning: Net::HTTPResponse#response is obsolete" if $VERBOSE
+ warn "Net::HTTPResponse#response is obsolete", uplevel: 1 if $VERBOSE
self
end
def header #:nodoc:
- warn "#{caller(1)[0]}: warning: Net::HTTPResponse#header is obsolete" if $VERBOSE
+ warn "Net::HTTPResponse#header is obsolete", uplevel: 1 if $VERBOSE
self
end
def read_header #:nodoc:
- warn "#{caller(1)[0]}: warning: Net::HTTPResponse#read_header is obsolete" if $VERBOSE
+ warn "Net::HTTPResponse#read_header is obsolete", uplevel: 1 if $VERBOSE
self
end
@@ -262,12 +262,13 @@ class Net::HTTPResponse
begin
yield inflate_body_io
+ success = true
ensure
- orig_err = $!
begin
inflate_body_io.finish
rescue => err
- raise orig_err || err
+ # Ignore #finish's error if there is an exception from yield
+ raise err if success
end
end
when 'none', 'identity' then
@@ -380,6 +381,7 @@ class Net::HTTPResponse
end
block = proc do |compressed_chunk|
@inflate.inflate(compressed_chunk) do |chunk|
+ compressed_chunk.clear
dest << chunk
end
end
diff --git a/lib/net/http/responses.rb b/lib/net/http/responses.rb
index 8e75f75d4f..50352032df 100644
--- a/lib/net/http/responses.rb
+++ b/lib/net/http/responses.rb
@@ -1,5 +1,6 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# :stopdoc:
+# https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
class Net::HTTPUnknownResponse < Net::HTTPResponse
HAS_BODY = true
EXCEPTION_TYPE = Net::HTTPError
@@ -18,7 +19,7 @@ class Net::HTTPRedirection < Net::HTTPResponse # 3xx
end
class Net::HTTPClientError < Net::HTTPResponse # 4xx
HAS_BODY = true
- EXCEPTION_TYPE = Net::HTTPServerException # for backward compatibility
+ EXCEPTION_TYPE = Net::HTTPClientException # for backward compatibility
end
class Net::HTTPServerError < Net::HTTPResponse # 5xx
HAS_BODY = true
@@ -31,7 +32,12 @@ end
class Net::HTTPSwitchProtocol < Net::HTTPInformation # 101
HAS_BODY = false
end
-# 102 - RFC 2518; removed in RFC 4918
+class Net::HTTPProcessing < Net::HTTPInformation # 102
+ HAS_BODY = false
+end
+class Net::HTTPEarlyHints < Net::HTTPInformation # 103 - RFC 8297
+ HAS_BODY = false
+end
class Net::HTTPOK < Net::HTTPSuccess # 200
HAS_BODY = true
@@ -57,7 +63,9 @@ end
class Net::HTTPMultiStatus < Net::HTTPSuccess # 207 - RFC 4918
HAS_BODY = true
end
-# 208 Already Reported - RFC 5842; experimental
+class Net::HTTPAlreadyReported < Net::HTTPSuccess # 208 - RFC 5842
+ HAS_BODY = true
+end
class Net::HTTPIMUsed < Net::HTTPSuccess # 226 - RFC 3229
HAS_BODY = true
end
@@ -114,9 +122,10 @@ end
class Net::HTTPProxyAuthenticationRequired < Net::HTTPClientError # 407
HAS_BODY = true
end
-class Net::HTTPRequestTimeOut < Net::HTTPClientError # 408
+class Net::HTTPRequestTimeout < Net::HTTPClientError # 408
HAS_BODY = true
end
+Net::HTTPRequestTimeOut = Net::HTTPRequestTimeout
class Net::HTTPConflict < Net::HTTPClientError # 409
HAS_BODY = true
end
@@ -129,24 +138,30 @@ end
class Net::HTTPPreconditionFailed < Net::HTTPClientError # 412
HAS_BODY = true
end
-class Net::HTTPRequestEntityTooLarge < Net::HTTPClientError # 413
+class Net::HTTPPayloadTooLarge < Net::HTTPClientError # 413
HAS_BODY = true
end
-class Net::HTTPRequestURITooLong < Net::HTTPClientError # 414
+Net::HTTPRequestEntityTooLarge = Net::HTTPPayloadTooLarge
+class Net::HTTPURITooLong < Net::HTTPClientError # 414
HAS_BODY = true
end
+Net::HTTPRequestURITooLong = Net::HTTPURITooLong
Net::HTTPRequestURITooLarge = Net::HTTPRequestURITooLong
class Net::HTTPUnsupportedMediaType < Net::HTTPClientError # 415
HAS_BODY = true
end
-class Net::HTTPRequestedRangeNotSatisfiable < Net::HTTPClientError # 416
+class Net::HTTPRangeNotSatisfiable < Net::HTTPClientError # 416
HAS_BODY = true
end
+Net::HTTPRequestedRangeNotSatisfiable = Net::HTTPRangeNotSatisfiable
class Net::HTTPExpectationFailed < Net::HTTPClientError # 417
HAS_BODY = true
end
# 418 I'm a teapot - RFC 2324; a joke RFC
# 420 Enhance Your Calm - Twitter
+class Net::HTTPMisdirectedRequest < Net::HTTPClientError # 421 - RFC 7540
+ HAS_BODY = true
+end
class Net::HTTPUnprocessableEntity < Net::HTTPClientError # 422 - RFC 4918
HAS_BODY = true
end
@@ -169,7 +184,7 @@ end
class Net::HTTPRequestHeaderFieldsTooLarge < Net::HTTPClientError # 431 - RFC 6585
HAS_BODY = true
end
-class Net::HTTPUnavailableForLegalReasons < Net::HTTPClientError # 451
+class Net::HTTPUnavailableForLegalReasons < Net::HTTPClientError # 451 - RFC 7725
HAS_BODY = true
end
# 444 No Response - Nginx
@@ -189,19 +204,26 @@ end
class Net::HTTPServiceUnavailable < Net::HTTPServerError # 503
HAS_BODY = true
end
-class Net::HTTPGatewayTimeOut < Net::HTTPServerError # 504
+class Net::HTTPGatewayTimeout < Net::HTTPServerError # 504
HAS_BODY = true
end
+Net::HTTPGatewayTimeOut = Net::HTTPGatewayTimeout
class Net::HTTPVersionNotSupported < Net::HTTPServerError # 505
HAS_BODY = true
end
-# 506 Variant Also Negotiates - RFC 2295; experimental
+class Net::HTTPVariantAlsoNegotiates < Net::HTTPServerError # 506
+ HAS_BODY = true
+end
class Net::HTTPInsufficientStorage < Net::HTTPServerError # 507 - RFC 4918
HAS_BODY = true
end
-# 508 Loop Detected - RFC 5842; experimental
+class Net::HTTPLoopDetected < Net::HTTPServerError # 508 - RFC 5842
+ HAS_BODY = true
+end
# 509 Bandwidth Limit Exceeded - Apache bw/limited extension
-# 510 Not Extended - RFC 2774; experimental
+class Net::HTTPNotExtended < Net::HTTPServerError # 510 - RFC 2774
+ HAS_BODY = true
+end
class Net::HTTPNetworkAuthenticationRequired < Net::HTTPServerError # 511 - RFC 6585
HAS_BODY = true
end
@@ -217,6 +239,8 @@ class Net::HTTPResponse
CODE_TO_OBJ = {
'100' => Net::HTTPContinue,
'101' => Net::HTTPSwitchProtocol,
+ '102' => Net::HTTPProcessing,
+ '103' => Net::HTTPEarlyHints,
'200' => Net::HTTPOK,
'201' => Net::HTTPCreated,
@@ -226,6 +250,7 @@ class Net::HTTPResponse
'205' => Net::HTTPResetContent,
'206' => Net::HTTPPartialContent,
'207' => Net::HTTPMultiStatus,
+ '208' => Net::HTTPAlreadyReported,
'226' => Net::HTTPIMUsed,
'300' => Net::HTTPMultipleChoices,
@@ -245,16 +270,17 @@ class Net::HTTPResponse
'405' => Net::HTTPMethodNotAllowed,
'406' => Net::HTTPNotAcceptable,
'407' => Net::HTTPProxyAuthenticationRequired,
- '408' => Net::HTTPRequestTimeOut,
+ '408' => Net::HTTPRequestTimeout,
'409' => Net::HTTPConflict,
'410' => Net::HTTPGone,
'411' => Net::HTTPLengthRequired,
'412' => Net::HTTPPreconditionFailed,
- '413' => Net::HTTPRequestEntityTooLarge,
- '414' => Net::HTTPRequestURITooLong,
+ '413' => Net::HTTPPayloadTooLarge,
+ '414' => Net::HTTPURITooLong,
'415' => Net::HTTPUnsupportedMediaType,
- '416' => Net::HTTPRequestedRangeNotSatisfiable,
+ '416' => Net::HTTPRangeNotSatisfiable,
'417' => Net::HTTPExpectationFailed,
+ '421' => Net::HTTPMisdirectedRequest,
'422' => Net::HTTPUnprocessableEntity,
'423' => Net::HTTPLocked,
'424' => Net::HTTPFailedDependency,
@@ -262,17 +288,20 @@ class Net::HTTPResponse
'428' => Net::HTTPPreconditionRequired,
'429' => Net::HTTPTooManyRequests,
'431' => Net::HTTPRequestHeaderFieldsTooLarge,
+ '451' => Net::HTTPUnavailableForLegalReasons,
'500' => Net::HTTPInternalServerError,
'501' => Net::HTTPNotImplemented,
'502' => Net::HTTPBadGateway,
'503' => Net::HTTPServiceUnavailable,
- '504' => Net::HTTPGatewayTimeOut,
+ '504' => Net::HTTPGatewayTimeout,
'505' => Net::HTTPVersionNotSupported,
+ '506' => Net::HTTPVariantAlsoNegotiates,
'507' => Net::HTTPInsufficientStorage,
+ '508' => Net::HTTPLoopDetected,
+ '510' => Net::HTTPNotExtended,
'511' => Net::HTTPNetworkAuthenticationRequired,
}
end
# :startdoc:
-
diff --git a/lib/net/http/status.rb b/lib/net/http/status.rb
new file mode 100644
index 0000000000..b3995f763f
--- /dev/null
+++ b/lib/net/http/status.rb
@@ -0,0 +1,84 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require_relative '../http'
+
+if $0 == __FILE__
+ require 'open-uri'
+ IO.foreach(__FILE__) do |line|
+ puts line
+ break if line.start_with?('end')
+ end
+ puts
+ puts "Net::HTTP::STATUS_CODES = {"
+ url = "https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv"
+ URI(url).read.each_line do |line|
+ code, mes, = line.split(',')
+ next if ['(Unused)', 'Unassigned', 'Description'].include?(mes)
+ puts " #{code} => '#{mes}',"
+ end
+ puts "}"
+end
+
+Net::HTTP::STATUS_CODES = {
+ 100 => 'Continue',
+ 101 => 'Switching Protocols',
+ 102 => 'Processing',
+ 103 => 'Early Hints',
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+ 207 => 'Multi-Status',
+ 208 => 'Already Reported',
+ 226 => 'IM Used',
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 307 => 'Temporary Redirect',
+ 308 => 'Permanent Redirect',
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Timeout',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition Failed',
+ 413 => 'Payload Too Large',
+ 414 => 'URI Too Long',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Range Not Satisfiable',
+ 417 => 'Expectation Failed',
+ 421 => 'Misdirected Request',
+ 422 => 'Unprocessable Entity',
+ 423 => 'Locked',
+ 424 => 'Failed Dependency',
+ 426 => 'Upgrade Required',
+ 428 => 'Precondition Required',
+ 429 => 'Too Many Requests',
+ 431 => 'Request Header Fields Too Large',
+ 451 => 'Unavailable For Legal Reasons',
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Timeout',
+ 505 => 'HTTP Version Not Supported',
+ 506 => 'Variant Also Negotiates',
+ 507 => 'Insufficient Storage',
+ 508 => 'Loop Detected',
+ 510 => 'Not Extended',
+ 511 => 'Network Authentication Required',
+}
diff --git a/lib/net/https.rb b/lib/net/https.rb
index 58cb6ddf19..d46721c82a 100644
--- a/lib/net/https.rb
+++ b/lib/net/https.rb
@@ -19,5 +19,5 @@
=end
-require 'net/http'
+require_relative 'http'
require 'openssl'
diff --git a/lib/net/imap.rb b/lib/net/imap.rb
index 9895ed68ce..91df89b79e 100644
--- a/lib/net/imap.rb
+++ b/lib/net/imap.rb
@@ -18,6 +18,7 @@ require "socket"
require "monitor"
require "digest/md5"
require "strscan"
+require_relative 'protocol'
begin
require "openssl"
rescue LoadError
@@ -199,7 +200,7 @@ module Net
# Goldsmith, D. and Davis, M., "UTF-7: A Mail-Safe Transformation Format of
# Unicode", RFC 2152, May 1997.
#
- class IMAP
+ class IMAP < Protocol
include MonitorMixin
if defined?(OpenSSL::SSL)
include OpenSSL
@@ -221,6 +222,11 @@ module Net
# Returns all response handlers.
attr_reader :response_handlers
+ # Seconds to wait until a connection is opened.
+ # If the IMAP object cannot open a connection within this time,
+ # it raises a Net::OpenTimeout exception. The default value is 30 seconds.
+ attr_reader :open_timeout
+
# The thread to receive exceptions.
attr_accessor :client_thread
@@ -314,6 +320,7 @@ module Net
# Disconnects from the server.
def disconnect
+ return if disconnected?
begin
begin
# try to call SSL::SSLSocket#io.
@@ -329,9 +336,7 @@ module Net
end
@receiver_thread.join
synchronize do
- unless @sock.closed?
- @sock.close
- end
+ @sock.close
end
raise e if e
end
@@ -809,13 +814,13 @@ module Net
# #=> "12-Oct-2000 22:40:59 +0900"
# p data.attr["UID"]
# #=> 98
- def fetch(set, attr)
- return fetch_internal("FETCH", set, attr)
+ def fetch(set, attr, mod = nil)
+ return fetch_internal("FETCH", set, attr, mod)
end
# Similar to #fetch(), but +set+ contains unique identifiers.
- def uid_fetch(set, attr)
- return fetch_internal("UID FETCH", set, attr)
+ def uid_fetch(set, attr, mod = nil)
+ return fetch_internal("UID FETCH", set, attr, mod)
end
# Sends a STORE command to alter data associated with messages
@@ -959,7 +964,7 @@ module Net
@idle_done_cond.wait(timeout)
@idle_done_cond = nil
if @receiver_thread_terminating
- raise Net::IMAP::Error, "connection closed"
+ raise @exception || Net::IMAP::Error.new("connection closed")
end
ensure
unless @receiver_thread_terminating
@@ -994,7 +999,7 @@ module Net
def self.decode_utf7(s)
return s.gsub(/&([^-]+)?-/n) {
if $1
- ($1.tr(",", "/") + "===").unpack("m")[0].encode(Encoding::UTF_8, Encoding::UTF_16BE)
+ ($1.tr(",", "/") + "===").unpack1("m").encode(Encoding::UTF_8, Encoding::UTF_16BE)
else
"&"
end
@@ -1050,6 +1055,7 @@ module Net
# be installed.
# If options[:ssl] is a hash, it's passed to
# OpenSSL::SSL::SSLContext#set_params as parameters.
+ # open_timeout:: Seconds to wait until a connection is opened
#
# The most common errors are:
#
@@ -1078,8 +1084,9 @@ module Net
@port = options[:port] || (options[:ssl] ? SSL_PORT : PORT)
@tag_prefix = "RUBY"
@tagno = 0
+ @open_timeout = options[:open_timeout] || 30
@parser = ResponseParser.new
- @sock = TCPSocket.open(@host, @port)
+ @sock = tcp_socket(@host, @port)
begin
if options[:ssl]
start_tls_session(options[:ssl])
@@ -1091,7 +1098,9 @@ module Net
@tagged_responses = {}
@response_handlers = []
@tagged_response_arrival = new_cond
+ @continued_command_tag = nil
@continuation_request_arrival = new_cond
+ @continuation_request_exception = nil
@idle_done_cond = nil
@logout_command_tag = nil
@debug_output_bol = true
@@ -1119,6 +1128,15 @@ module Net
end
end
+ def tcp_socket(host, port)
+ s = Socket.tcp(host, port, :connect_timeout => @open_timeout)
+ s.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, true)
+ s
+ rescue Errno::ETIMEDOUT
+ raise Net::OpenTimeout, "Timeout to open TCP connection to " +
+ "#{host}:#{port} (exceeds #{@open_timeout} seconds)"
+ end
+
def receive_responses
connection_closed = false
until connection_closed
@@ -1146,8 +1164,13 @@ module Net
when TaggedResponse
@tagged_responses[resp.tag] = resp
@tagged_response_arrival.broadcast
- if resp.tag == @logout_command_tag
+ case resp.tag
+ when @logout_command_tag
return
+ when @continued_command_tag
+ @continuation_request_exception =
+ RESPONSE_ERRORS[resp.name].new(resp)
+ @continuation_request_arrival.signal
end
when UntaggedResponse
record_response(resp.name, resp.data)
@@ -1192,12 +1215,14 @@ module Net
end
resp = @tagged_responses.delete(tag)
case resp.name
+ when /\A(?:OK)\z/ni
+ return resp
when /\A(?:NO)\z/ni
raise NoResponseError, resp
when /\A(?:BAD)\z/ni
raise BadResponseError, resp
else
- return resp
+ raise UnknownResponseError, resp
end
end
@@ -1237,7 +1262,7 @@ module Net
put_string(tag + " " + cmd)
args.each do |i|
put_string(" ")
- send_data(i)
+ send_data(i, tag)
end
put_string(CRLF)
if cmd == "LOGOUT"
@@ -1283,8 +1308,12 @@ module Net
when Integer
NumValidator.ensure_number(data)
when Array
- data.each do |i|
- validate_data(i)
+ if data[0] == 'CHANGEDSINCE'
+ NumValidator.ensure_mod_sequence_value(data[1])
+ else
+ data.each do |i|
+ validate_data(i)
+ end
end
when Time
when Symbol
@@ -1293,32 +1322,32 @@ module Net
end
end
- def send_data(data)
+ def send_data(data, tag = nil)
case data
when nil
put_string("NIL")
when String
- send_string_data(data)
+ send_string_data(data, tag)
when Integer
send_number_data(data)
when Array
- send_list_data(data)
+ send_list_data(data, tag)
when Time
send_time_data(data)
when Symbol
send_symbol_data(data)
else
- data.send_data(self)
+ data.send_data(self, tag)
end
end
- def send_string_data(str)
+ def send_string_data(str, tag = nil)
case str
when ""
put_string('""')
when /[\x80-\xff\r\n]/n
# literal
- send_literal(str)
+ send_literal(str, tag)
when /[(){ \x00-\x1f\x7f%*"\\]/n
# quoted string
send_quoted_string(str)
@@ -1331,18 +1360,28 @@ module Net
put_string('"' + str.gsub(/["\\]/n, "\\\\\\&") + '"')
end
- def send_literal(str)
- put_string("{" + str.bytesize.to_s + "}" + CRLF)
- @continuation_request_arrival.wait
- raise @exception if @exception
- put_string(str)
+ def send_literal(str, tag = nil)
+ synchronize do
+ put_string("{" + str.bytesize.to_s + "}" + CRLF)
+ @continued_command_tag = tag
+ @continuation_request_exception = nil
+ begin
+ @continuation_request_arrival.wait
+ e = @continuation_request_exception || @exception
+ raise e if e
+ put_string(str)
+ ensure
+ @continued_command_tag = nil
+ @continuation_request_exception = nil
+ end
+ end
end
def send_number_data(num)
put_string(num.to_s)
end
- def send_list_data(list)
+ def send_list_data(list, tag = nil)
put_string("(")
first = true
list.each do |i|
@@ -1351,7 +1390,7 @@ module Net
else
put_string(" ")
end
- send_data(i)
+ send_data(i, tag)
end
put_string(")")
end
@@ -1386,7 +1425,7 @@ module Net
end
end
- def fetch_internal(cmd, set, attr)
+ def fetch_internal(cmd, set, attr, mod = nil)
case attr
when String then
attr = RawData.new(attr)
@@ -1398,7 +1437,11 @@ module Net
synchronize do
@responses.delete("FETCH")
- send_command(cmd, MessageSet.new(set), attr)
+ if mod
+ send_command(cmd, MessageSet.new(set), attr, mod)
+ else
+ send_command(cmd, MessageSet.new(set), attr)
+ end
return @responses.delete("FETCH")
end
end
@@ -1489,14 +1532,15 @@ module Net
end
@sock = SSLSocket.new(@sock, context)
@sock.sync_close = true
- @sock.connect
+ @sock.hostname = @host if @sock.respond_to? :hostname=
+ ssl_socket_connect(@sock, @open_timeout)
if context.verify_mode != VERIFY_NONE
@sock.post_connection_check(@host)
end
end
class RawData # :nodoc:
- def send_data(imap)
+ def send_data(imap, tag)
imap.send(:put_string, @data)
end
@@ -1511,7 +1555,7 @@ module Net
end
class Atom # :nodoc:
- def send_data(imap)
+ def send_data(imap, tag)
imap.send(:put_string, @data)
end
@@ -1526,7 +1570,7 @@ module Net
end
class QuotedString # :nodoc:
- def send_data(imap)
+ def send_data(imap, tag)
imap.send(:send_quoted_string, @data)
end
@@ -1541,8 +1585,8 @@ module Net
end
class Literal # :nodoc:
- def send_data(imap)
- imap.send(:send_literal, @data)
+ def send_data(imap, tag)
+ imap.send(:send_literal, @data, tag)
end
def validate
@@ -1556,7 +1600,7 @@ module Net
end
class MessageSet # :nodoc:
- def send_data(imap)
+ def send_data(imap, tag)
imap.send(:put_string, format_internal(@data))
end
@@ -1632,6 +1676,15 @@ module Net
num != 0 && valid_number?(num)
end
+ # Check is passed argument valid 'mod_sequence_value' in RFC 4551 terminology
+ def valid_mod_sequence_value?(num)
+ # mod-sequence-value = 1*DIGIT
+ # ; Positive unsigned 64-bit integer
+ # ; (mod-sequence)
+ # ; (1 <= n < 18,446,744,073,709,551,615)
+ num >= 1 && num < 18446744073709551615
+ end
+
# Ensure argument is 'number' or raise DataFormatError
def ensure_number(num)
return if valid_number?(num)
@@ -1647,6 +1700,14 @@ module Net
msg = "nz_number must be non-zero unsigned 32-bit integer: #{num}"
raise DataFormatError, msg
end
+
+ # Ensure argument is 'mod_sequence_value' or raise DataFormatError
+ def ensure_mod_sequence_value(num)
+ return if valid_mod_sequence_value?(num)
+
+ msg = "mod_sequence_value must be unsigned 64-bit integer: #{num}"
+ raise DataFormatError, msg
+ end
end
end
@@ -1976,8 +2037,7 @@ module Net
# generate a warning message to +stderr+, then return
# the value of +subtype+.
def media_subtype
- $stderr.printf("warning: media_subtype is obsolete.\n")
- $stderr.printf(" use subtype instead.\n")
+ warn("media_subtype is obsolete, use subtype instead.\n", uplevel: 1)
return subtype
end
end
@@ -2004,8 +2064,7 @@ module Net
# generate a warning message to +stderr+, then return
# the value of +subtype+.
def media_subtype
- $stderr.printf("warning: media_subtype is obsolete.\n")
- $stderr.printf(" use subtype instead.\n")
+ warn("media_subtype is obsolete, use subtype instead.\n", uplevel: 1)
return subtype
end
end
@@ -2034,8 +2093,7 @@ module Net
# generate a warning message to +stderr+, then return
# the value of +subtype+.
def media_subtype
- $stderr.printf("warning: media_subtype is obsolete.\n")
- $stderr.printf(" use subtype instead.\n")
+ warn("media_subtype is obsolete, use subtype instead.\n", uplevel: 1)
return subtype
end
end
@@ -2095,8 +2153,7 @@ module Net
# generate a warning message to +stderr+, then return
# the value of +subtype+.
def media_subtype
- $stderr.printf("warning: media_subtype is obsolete.\n")
- $stderr.printf(" use subtype instead.\n")
+ warn("media_subtype is obsolete, use subtype instead.\n", uplevel: 1)
return subtype
end
end
@@ -2201,6 +2258,10 @@ module Net
else
result = response_tagged
end
+ while lookahead.symbol == T_SPACE
+ # Ignore trailing space for Microsoft Exchange Server
+ shift_token
+ end
match(T_CRLF)
match(T_EOF)
return result
@@ -2208,8 +2269,13 @@ module Net
def continue_req
match(T_PLUS)
- match(T_SPACE)
- return ContinuationRequest.new(resp_text, @str)
+ token = lookahead
+ if token.symbol == T_SPACE
+ shift_token
+ return ContinuationRequest.new(resp_text, @str)
+ else
+ return ContinuationRequest.new(ResponseText.new(nil, ""), @str)
+ end
end
def response_untagged
@@ -2308,6 +2374,8 @@ module Net
name, val = body_data
when /\A(?:UID)\z/ni
name, val = uid_data
+ when /\A(?:MODSEQ)\z/ni
+ name, val = modseq_data
else
parse_error("unknown attribute `%s' for {%d}", token.value, n)
end
@@ -2797,6 +2865,16 @@ module Net
return name, number
end
+ def modseq_data
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ match(T_LPAR)
+ modseq = number
+ match(T_RPAR)
+ return name, modseq
+ end
+
def text_response
token = match(T_ATOM)
name = token.value.upcase
@@ -3640,6 +3718,14 @@ module Net
class ByeResponseError < ResponseError
end
+ # Error raised upon an unknown response from the server.
+ class UnknownResponseError < ResponseError
+ end
+
+ RESPONSE_ERRORS = Hash.new(ResponseError)
+ RESPONSE_ERRORS["NO"] = NoResponseError
+ RESPONSE_ERRORS["BAD"] = BadResponseError
+
# Error raised when too many flags are interned to symbols.
class FlagCountError < Error
end
diff --git a/lib/net/pop.rb b/lib/net/pop.rb
index 7f5c09c504..a6374cd78c 100644
--- a/lib/net/pop.rb
+++ b/lib/net/pop.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# = net/pop.rb
#
# Copyright (c) 1999-2007 Yukihiro Matsumoto.
@@ -21,7 +21,7 @@
# See Net::POP3 for documentation.
#
-require 'net/protocol'
+require_relative 'protocol'
require 'digest/md5'
require 'timeout'
@@ -168,8 +168,8 @@ module Net
# require 'net/pop'
#
# # Use APOP authentication if $isapop == true
- # pop = Net::POP3.APOP($is_apop).new('apop.example.com', 110)
- # pop.start(YourAccount', 'YourPassword') do |pop|
+ # pop = Net::POP3.APOP($isapop).new('apop.example.com', 110)
+ # pop.start('YourAccount', 'YourPassword') do |pop|
# # Rest of the code is the same.
# end
#
@@ -467,7 +467,7 @@ module Net
# Provide human-readable stringification of class state.
def inspect
- "#<#{self.class} #{@address}:#{@port} open=#{@started}>"
+ +"#<#{self.class} #{@address}:#{@port} open=#{@started}>"
end
# *WARNING*: This method causes a serious security hole.
@@ -550,15 +550,15 @@ module Net
context.set_params(@ssl_params)
s = OpenSSL::SSL::SSLSocket.new(s, context)
s.sync_close = true
- s.connect
+ ssl_socket_connect(s, @open_timeout)
if context.verify_mode != OpenSSL::SSL::VERIFY_NONE
s.post_connection_check(@address)
end
end
- @socket = InternetMessageIO.new(s)
+ @socket = InternetMessageIO.new(s,
+ read_timeout: @read_timeout,
+ debug_output: @debug_output)
logging "POP session started: #{@address}:#{@port} (#{@apop ? 'APOP' : 'POP'})"
- @socket.read_timeout = @read_timeout
- @socket.debug_output = @debug_output
on_connect
@command = POP3Command.new(@socket)
if apop?
@@ -570,7 +570,7 @@ module Net
ensure
# Authentication failed, clean up connection.
unless @started
- s.close if s and not s.closed?
+ s.close if s
@socket = nil
@command = nil
end
@@ -601,7 +601,7 @@ module Net
ensure
@started = false
@command = nil
- @socket.close if @socket and not @socket.closed?
+ @socket.close if @socket
@socket = nil
end
private :do_finish
@@ -758,7 +758,7 @@ module Net
# Provide human-readable stringification of class state.
def inspect
- "#<#{self.class} #{@number}#{@deleted ? ' deleted' : ''}>"
+ +"#<#{self.class} #{@number}#{@deleted ? ' deleted' : ''}>"
end
#
@@ -771,7 +771,7 @@ module Net
# === Example without block
#
# POP3.start('pop.example.com', 110,
- # 'YourAccount, 'YourPassword') do |pop|
+ # 'YourAccount', 'YourPassword') do |pop|
# n = 1
# pop.mails.each do |popmail|
# File.open("inbox/#{n}", 'w') do |f|
@@ -785,7 +785,7 @@ module Net
# === Example with block
#
# POP3.start('pop.example.com', 110,
- # 'YourAccount, 'YourPassword') do |pop|
+ # 'YourAccount', 'YourPassword') do |pop|
# n = 1
# pop.mails.each do |popmail|
# File.open("inbox/#{n}", 'w') do |f|
@@ -799,7 +799,7 @@ module Net
#
# This method raises a POPError if an error occurs.
#
- def pop( dest = '', &block ) # :yield: message_chunk
+ def pop( dest = +'', &block ) # :yield: message_chunk
if block_given?
@command.retr(@number, &block)
nil
@@ -819,7 +819,7 @@ module Net
# The optional +dest+ argument is obsolete.
#
# This method raises a POPError if an error occurs.
- def top(lines, dest = '')
+ def top(lines, dest = +'')
@command.top(@number, lines) do |chunk|
dest << chunk
end
@@ -831,7 +831,7 @@ module Net
# The optional +dest+ argument is obsolete.
#
# This method raises a POPError if an error occurs.
- def header(dest = '')
+ def header(dest = +'')
top(0, dest)
end
@@ -844,7 +844,7 @@ module Net
# === Example
#
# POP3.start('pop.example.com', 110,
- # 'YourAccount, 'YourPassword') do |pop|
+ # 'YourAccount', 'YourPassword') do |pop|
# n = 1
# pop.mails.each do |popmail|
# File.open("inbox/#{n}", 'w') do |f|
@@ -898,7 +898,7 @@ module Net
attr_reader :socket
def inspect
- "#<#{self.class} socket=#{@socket}>"
+ +"#<#{self.class} socket=#{@socket}>"
end
def auth(account, password)
diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb
index 6b75b94cda..60e23f1aa5 100644
--- a/lib/net/protocol.rb
+++ b/lib/net/protocol.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# = net/protocol.rb
#
@@ -75,20 +75,54 @@ module Net # :nodoc:
# ReadTimeout, a subclass of Timeout::Error, is raised if a chunk of the
# response cannot be read within the read_timeout.
- class ReadTimeout < Timeout::Error; end
+ class ReadTimeout < Timeout::Error
+ def initialize(io = nil)
+ @io = io
+ end
+ attr_reader :io
+
+ def message
+ msg = super
+ if @io
+ msg = "#{msg} with #{@io.inspect}"
+ end
+ msg
+ end
+ end
+
+ ##
+ # WriteTimeout, a subclass of Timeout::Error, is raised if a chunk of the
+ # response cannot be written within the write_timeout. Not raised on Windows.
+
+ class WriteTimeout < Timeout::Error
+ def initialize(io = nil)
+ @io = io
+ end
+ attr_reader :io
+
+ def message
+ msg = super
+ if @io
+ msg = "#{msg} with #{@io.inspect}"
+ end
+ msg
+ end
+ end
class BufferedIO #:nodoc: internal use only
- def initialize(io)
+ def initialize(io, read_timeout: 60, write_timeout: 60, continue_timeout: nil, debug_output: nil)
@io = io
- @read_timeout = 60
- @continue_timeout = nil
- @debug_output = nil
- @rbuf = ''
+ @read_timeout = read_timeout
+ @write_timeout = write_timeout
+ @continue_timeout = continue_timeout
+ @debug_output = debug_output
+ @rbuf = ''.b
end
attr_reader :io
attr_accessor :read_timeout
+ attr_accessor :write_timeout
attr_accessor :continue_timeout
attr_accessor :debug_output
@@ -114,17 +148,19 @@ module Net # :nodoc:
public
- def read(len, dest = '', ignore_eof = false)
+ def read(len, dest = ''.b, ignore_eof = false)
LOG "reading #{len} bytes..."
read_bytes = 0
begin
while read_bytes + @rbuf.size < len
- dest << (s = rbuf_consume(@rbuf.size))
+ s = rbuf_consume(@rbuf.size)
read_bytes += s.size
+ dest << s
rbuf_fill
end
- dest << (s = rbuf_consume(len - read_bytes))
+ s = rbuf_consume(len - read_bytes)
read_bytes += s.size
+ dest << s
rescue EOFError
raise unless ignore_eof
end
@@ -132,13 +168,14 @@ module Net # :nodoc:
dest
end
- def read_all(dest = '')
+ def read_all(dest = ''.b)
LOG 'reading all...'
read_bytes = 0
begin
while true
- dest << (s = rbuf_consume(@rbuf.size))
+ s = rbuf_consume(@rbuf.size)
read_bytes += s.size
+ dest << s
rbuf_fill
end
rescue EOFError
@@ -169,25 +206,33 @@ module Net # :nodoc:
BUFSIZE = 1024 * 16
def rbuf_fill
- case rv = @io.read_nonblock(BUFSIZE, exception: false)
+ tmp = @rbuf.empty? ? @rbuf : nil
+ case rv = @io.read_nonblock(BUFSIZE, tmp, exception: false)
when String
- return @rbuf << rv
+ return if rv.equal?(tmp)
+ @rbuf << rv
+ rv.clear
+ return
when :wait_readable
- @io.to_io.wait_readable(@read_timeout) or raise Net::ReadTimeout
+ (io = @io.to_io).wait_readable(@read_timeout) or raise Net::ReadTimeout.new(io)
# continue looping
when :wait_writable
# OpenSSL::Buffering#read_nonblock may fail with IO::WaitWritable.
# http://www.openssl.org/support/faq.html#PROG10
- @io.to_io.wait_writable(@read_timeout) or raise Net::ReadTimeout
+ (io = @io.to_io).wait_writable(@read_timeout) or raise Net::ReadTimeout.new(io)
# continue looping
when nil
- # callers do not care about backtrace, so avoid allocating for it
- raise EOFError, 'end of file reached', []
+ raise EOFError, 'end of file reached'
end while true
end
def rbuf_consume(len)
- s = @rbuf.slice!(0, len)
+ if len == @rbuf.size
+ s = @rbuf
+ @rbuf = ''.b
+ else
+ s = @rbuf.slice!(0, len)
+ end
@debug_output << %Q[-> #{s.dump}\n] if @debug_output
s
end
@@ -198,9 +243,9 @@ module Net # :nodoc:
public
- def write(str)
+ def write(*strs)
writing {
- write0 str
+ write0(*strs)
}
end
@@ -224,11 +269,34 @@ module Net # :nodoc:
bytes
end
- def write0(str)
- @debug_output << str.dump if @debug_output
- len = @io.write(str)
- @written_bytes += len
- len
+ def write0(*strs)
+ @debug_output << strs.map(&:dump).join if @debug_output
+ orig_written_bytes = @written_bytes
+ strs.each_with_index do |str, i|
+ need_retry = true
+ case len = @io.write_nonblock(str, exception: false)
+ when Integer
+ @written_bytes += len
+ len -= str.bytesize
+ if len == 0
+ if strs.size == i+1
+ return @written_bytes - orig_written_bytes
+ else
+ need_retry = false
+ # next string
+ end
+ elsif len < 0
+ str = str.byteslice(len, -len)
+ else # len > 0
+ need_retry = false
+ # next string
+ end
+ # continue looping
+ when :wait_writable
+ (io = @io.to_io).wait_writable(@write_timeout) or raise Net::WriteTimeout.new(io)
+ # continue looping
+ end while need_retry
+ end
end
#
@@ -254,7 +322,7 @@ module Net # :nodoc:
class InternetMessageIO < BufferedIO #:nodoc: internal use only
- def initialize(io)
+ def initialize(*)
super
@wbuf = nil
end
@@ -269,7 +337,7 @@ module Net # :nodoc:
read_bytes = 0
while (line = readuntil("\r\n")) != ".\r\n"
read_bytes += line.size
- yield line.sub(/\A\./, '')
+ yield line.delete_prefix('.')
end
LOG_on()
LOG "read message (#{read_bytes} bytes)"
@@ -331,7 +399,7 @@ module Net # :nodoc:
end
def using_each_crlf_line
- @wbuf = ''
+ @wbuf = ''.b
yield
if not @wbuf.empty? # unterminated last line
write0 dot_stuff(@wbuf.chomp) + "\r\n"
diff --git a/lib/net/smtp.rb b/lib/net/smtp.rb
index ac8ddc5bf2..86b55d278b 100644
--- a/lib/net/smtp.rb
+++ b/lib/net/smtp.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# = net/smtp.rb
#
# Copyright (c) 1999-2007 Yukihiro Matsumoto.
@@ -17,7 +17,7 @@
# See Net::SMTP for documentation.
#
-require 'net/protocol'
+require_relative 'protocol'
require 'digest/md5'
require 'timeout'
begin
@@ -567,7 +567,7 @@ module Net
ensure
unless @started
# authentication failed, cancel connection.
- s.close if s and not s.closed?
+ s.close if s
@socket = nil
end
end
@@ -592,10 +592,8 @@ module Net
end
def new_internet_message_io(s)
- io = InternetMessageIO.new(s)
- io.read_timeout = @read_timeout
- io.debug_output = @debug_output
- io
+ InternetMessageIO.new(s, read_timeout: @read_timeout,
+ debug_output: @debug_output)
end
def do_helo(helo_domain)
@@ -615,7 +613,7 @@ module Net
ensure
@started = false
@error_occurred = false
- @socket.close if @socket and not @socket.closed?
+ @socket.close if @socket
@socket = nil
end
@@ -947,7 +945,7 @@ module Net
end
def recv_response
- buf = ''
+ buf = ''.dup
while true
line = @socket.readline
buf << line << "\n"
@@ -1037,9 +1035,9 @@ module Net
end
# Creates a CRAM-MD5 challenge. You can view more information on CRAM-MD5
- # on Wikipedia: http://en.wikipedia.org/wiki/CRAM-MD5
+ # on Wikipedia: https://en.wikipedia.org/wiki/CRAM-MD5
def cram_md5_challenge
- @string.split(/ /)[1].unpack('m')[0]
+ @string.split(/ /)[1].unpack1('m')
end
# Returns a hash of the human readable reply text in the response if it
diff --git a/lib/observer.rb b/lib/observer.rb
index fa7446f384..acfe654301 100644
--- a/lib/observer.rb
+++ b/lib/observer.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# Implementation of the _Observer_ object-oriented design pattern. The
# following documentation is copied, with modifications, from "Programming
@@ -114,7 +114,7 @@
module Observable
#
- # Add +observer+ as an observer on this object. so that it will receive
+ # Add +observer+ as an observer on this object. So that it will receive
# notifications.
#
# +observer+:: the object that will be notified of changes.
diff --git a/lib/open-uri.rb b/lib/open-uri.rb
index b09c18efd9..38f074ef59 100644
--- a/lib/open-uri.rb
+++ b/lib/open-uri.rb
@@ -40,6 +40,13 @@ module Kernel
module_function :open
end
+module URI #:nodoc:
+ # alias for Kernel.open defined in open-uri.
+ def self.open(name, *rest, &block)
+ Kernel.open(name, *rest, &block)
+ end
+end
+
# OpenURI is an easy-to-use wrapper for Net::HTTP, Net::HTTPS and Net::FTP.
#
# == Example
@@ -108,6 +115,7 @@ module OpenURI
:ssl_verify_mode => nil,
:ftp_active_mode => false,
:redirect => true,
+ :encoding => nil,
}
def OpenURI.check_options(options) # :nodoc:
@@ -141,6 +149,12 @@ module OpenURI
encoding, = $1,Encoding.find($1) if $1
mode = nil
end
+ if options.has_key? :encoding
+ if !encoding.nil?
+ raise ArgumentError, "encoding specified twice"
+ end
+ encoding = Encoding.find(options[:encoding])
+ end
unless mode == nil ||
mode == 'r' || mode == 'rb' ||
@@ -340,6 +354,7 @@ module OpenURI
if options[:progress_proc] && Net::HTTPSuccess === resp
options[:progress_proc].call(buf.size)
end
+ str.clear
}
}
}
diff --git a/lib/open3.rb b/lib/open3.rb
index 3c9d450737..5e725317a4 100644
--- a/lib/open3.rb
+++ b/lib/open3.rb
@@ -81,7 +81,13 @@ module Open3
# If merged stdout and stderr output is not a problem, you can use Open3.popen2e.
# If you really need stdout and stderr output as separate strings, you can consider Open3.capture3.
#
- def popen3(*cmd, **opts, &block)
+ def popen3(*cmd, &block)
+ if Hash === cmd.last
+ opts = cmd.pop.dup
+ else
+ opts = {}
+ end
+
in_r, in_w = IO.pipe
opts[:in] = in_r
in_w.sync = true
@@ -136,7 +142,13 @@ module Open3
# p o.read #=> "*"
# }
#
- def popen2(*cmd, **opts, &block)
+ def popen2(*cmd, &block)
+ if Hash === cmd.last
+ opts = cmd.pop.dup
+ else
+ opts = {}
+ end
+
in_r, in_w = IO.pipe
opts[:in] = in_r
in_w.sync = true
@@ -179,7 +191,13 @@ module Open3
# }
# }
#
- def popen2e(*cmd, **opts, &block)
+ def popen2e(*cmd, &block)
+ if Hash === cmd.last
+ opts = cmd.pop.dup
+ else
+ opts = {}
+ end
+
in_r, in_w = IO.pipe
opts[:in] = in_r
in_w.sync = true
@@ -192,19 +210,15 @@ module Open3
module_function :popen2e
def popen_run(cmd, opts, child_io, parent_io) # :nodoc:
- if last = Hash.try_convert(cmd.last)
- opts = opts.merge(last)
- cmd.pop
- end
pid = spawn(*cmd, opts)
wait_thr = Process.detach(pid)
- child_io.each {|io| io.close }
+ child_io.each(&:close)
result = [*parent_io, wait_thr]
if defined? yield
begin
return yield(*result)
ensure
- parent_io.each{|io| io.close unless io.closed?}
+ parent_io.each(&:close)
wait_thr.join
end
end
@@ -254,7 +268,16 @@ module Open3
# STDOUT.binmode; print thumbnail
# end
#
- def capture3(*cmd, stdin_data: '', binmode: false, **opts)
+ def capture3(*cmd)
+ if Hash === cmd.last
+ opts = cmd.pop.dup
+ else
+ opts = {}
+ end
+
+ stdin_data = opts.delete(:stdin_data) || ''
+ binmode = opts.delete(:binmode)
+
popen3(*cmd, opts) {|i, o, e, t|
if binmode
i.binmode
@@ -264,7 +287,11 @@ module Open3
out_reader = Thread.new { o.read }
err_reader = Thread.new { e.read }
begin
- i.write stdin_data
+ if stdin_data.respond_to? :readpartial
+ IO.copy_stream(stdin_data, i)
+ else
+ i.write stdin_data
+ end
rescue Errno::EPIPE
end
i.close
@@ -302,7 +329,16 @@ module Open3
# End
# image, s = Open3.capture2("gnuplot", :stdin_data=>gnuplot_commands, :binmode=>true)
#
- def capture2(*cmd, stdin_data: nil, binmode: false, **opts)
+ def capture2(*cmd)
+ if Hash === cmd.last
+ opts = cmd.pop.dup
+ else
+ opts = {}
+ end
+
+ stdin_data = opts.delete(:stdin_data)
+ binmode = opts.delete(:binmode)
+
popen2(*cmd, opts) {|i, o, t|
if binmode
i.binmode
@@ -311,7 +347,11 @@ module Open3
out_reader = Thread.new { o.read }
if stdin_data
begin
- i.write stdin_data
+ if stdin_data.respond_to? :readpartial
+ IO.copy_stream(stdin_data, i)
+ else
+ i.write stdin_data
+ end
rescue Errno::EPIPE
end
end
@@ -337,7 +377,16 @@ module Open3
# # capture make log
# make_log, s = Open3.capture2e("make")
#
- def capture2e(*cmd, stdin_data: nil, binmode: false, **opts)
+ def capture2e(*cmd)
+ if Hash === cmd.last
+ opts = cmd.pop.dup
+ else
+ opts = {}
+ end
+
+ stdin_data = opts.delete(:stdin_data)
+ binmode = opts.delete(:binmode)
+
popen2e(*cmd, opts) {|i, oe, t|
if binmode
i.binmode
@@ -346,7 +395,11 @@ module Open3
outerr_reader = Thread.new { oe.read }
if stdin_data
begin
- i.write stdin_data
+ if stdin_data.respond_to? :readpartial
+ IO.copy_stream(stdin_data, i)
+ else
+ i.write stdin_data
+ end
rescue Errno::EPIPE
end
end
@@ -398,7 +451,13 @@ module Open3
# stdin.close # send EOF to sort.
# p stdout.read #=> " 1\tbar\n 2\tbaz\n 3\tfoo\n"
# }
- def pipeline_rw(*cmds, **opts, &block)
+ def pipeline_rw(*cmds, &block)
+ if Hash === cmds.last
+ opts = cmds.pop.dup
+ else
+ opts = {}
+ end
+
in_r, in_w = IO.pipe
opts[:in] = in_r
in_w.sync = true
@@ -448,7 +507,13 @@ module Open3
# p ts[1].value #=> #<Process::Status: pid 24913 exit 0>
# }
#
- def pipeline_r(*cmds, **opts, &block)
+ def pipeline_r(*cmds, &block)
+ if Hash === cmds.last
+ opts = cmds.pop.dup
+ else
+ opts = {}
+ end
+
out_r, out_w = IO.pipe
opts[:out] = out_w
@@ -484,7 +549,13 @@ module Open3
# i.puts "hello"
# }
#
- def pipeline_w(*cmds, **opts, &block)
+ def pipeline_w(*cmds, &block)
+ if Hash === cmds.last
+ opts = cmds.pop.dup
+ else
+ opts = {}
+ end
+
in_r, in_w = IO.pipe
opts[:in] = in_r
in_w.sync = true
@@ -537,7 +608,13 @@ module Open3
# p err_r.read # error messages of pdftops and lpr.
# }
#
- def pipeline_start(*cmds, **opts, &block)
+ def pipeline_start(*cmds, &block)
+ if Hash === cmds.last
+ opts = cmds.pop.dup
+ else
+ opts = {}
+ end
+
if block
pipeline_run(cmds, opts, [], [], &block)
else
@@ -599,9 +676,15 @@ module Open3
# # 106
# # 202
#
- def pipeline(*cmds, **opts)
+ def pipeline(*cmds)
+ if Hash === cmds.last
+ opts = cmds.pop.dup
+ else
+ opts = {}
+ end
+
pipeline_run(cmds, opts, [], []) {|ts|
- ts.map {|t| t.value }
+ ts.map(&:value)
}
end
module_function :pipeline
@@ -645,18 +728,18 @@ module Open3
end
pid = spawn(*cmd, cmd_opts)
wait_thrs << Process.detach(pid)
- r.close if r
- w2.close if w2
+ r&.close
+ w2&.close
r = r2
}
result = parent_io + [wait_thrs]
- child_io.each {|io| io.close }
+ child_io.each(&:close)
if defined? yield
begin
return yield(*result)
ensure
- parent_io.each{|io| io.close unless io.closed?}
- wait_thrs.each {|t| t.join }
+ parent_io.each(&:close)
+ wait_thrs.each(&:join)
end
end
result
diff --git a/lib/optparse.rb b/lib/optparse.rb
index afeff80740..5cdcabf4a7 100644
--- a/lib/optparse.rb
+++ b/lib/optparse.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# optparse.rb - command-line option analysis with the OptionParser class.
#
@@ -125,6 +125,7 @@
# For options that require an argument, option specification strings may include an
# option name in all caps. If an option is used without the required argument,
# an exception will be raised.
+#
# require 'optparse'
#
# options = {}
@@ -137,9 +138,9 @@
#
# Used:
#
-# bash-3.2$ ruby optparse-test.rb -r
+# $ ruby optparse-test.rb -r
# optparse-test.rb:9:in `<main>': missing argument: -r (OptionParser::MissingArgument)
-# bash-3.2$ ruby optparse-test.rb -r my-library
+# $ ruby optparse-test.rb -r my-library
# You required my-library!
#
# === Type Coercion
@@ -187,13 +188,12 @@
# end.parse!
#
# Used:
-# bash-3.2$ ruby optparse-test.rb -t nonsense
+#
+# $ ruby optparse-test.rb -t nonsense
# ... invalid argument: -t nonsense (OptionParser::InvalidArgument)
-# from ... time.rb:5:in `block in <top (required)>'
-# from optparse-test.rb:31:in `<main>'
-# bash-3.2$ ruby optparse-test.rb -t 10-11-12
+# $ ruby optparse-test.rb -t 10-11-12
# 2010-11-12 00:00:00 -0500
-# bash-3.2$ ruby optparse-test.rb -t 9:30
+# $ ruby optparse-test.rb -t 9:30
# 2014-08-13 09:30:00 -0400
#
# ==== Creating Custom Conversions
@@ -225,13 +225,39 @@
#
# op.parse!
#
-# output:
-# bash-3.2$ ruby optparse-test.rb --user 1
+# Used:
+#
+# $ ruby optparse-test.rb --user 1
# #<struct User id=1, name="Sam">
-# bash-3.2$ ruby optparse-test.rb --user 2
+# $ ruby optparse-test.rb --user 2
# #<struct User id=2, name="Gandalf">
-# bash-3.2$ ruby optparse-test.rb --user 3
+# $ ruby optparse-test.rb --user 3
# optparse-test.rb:15:in `block in find_user': No User Found for id 3 (RuntimeError)
+#
+# === Store options to a Hash
+#
+# The +into+ option of +order+, +parse+ and so on methods stores command line options into a Hash.
+#
+# require 'optparse'
+#
+# params = {}
+# OptionParser.new do |opts|
+# opts.on('-a')
+# opts.on('-b NUM', Integer)
+# opts.on('-v', '--verbose')
+# end.parse!(into: params)
+#
+# p params
+#
+# Used:
+#
+# $ ruby optparse-test.rb -a
+# {:a=>true}
+# $ ruby optparse-test.rb -a -v
+# {:a=>true, :verbose=>true}
+# $ ruby optparse-test.rb -a -b 100
+# {:a=>true, :b=>100}
+#
# === Complete example
#
# The following example is a complete Ruby program. You can run it and see the
@@ -413,7 +439,7 @@ class OptionParser
candidates = []
block.call do |k, *v|
(if Regexp === k
- kn = "".freeze
+ kn = ""
k === key
else
kn = defined?(k.id2name) ? k.id2name : k
@@ -508,8 +534,9 @@ class OptionParser
def initialize(pattern = nil, conv = nil,
short = nil, long = nil, arg = nil,
- desc = ([] if short or long), block = Proc.new)
+ desc = ([] if short or long), block = nil, &_block)
raise if Array === pattern
+ block ||= _block
@pattern, @conv, @short, @long, @arg, @desc, @block =
pattern, conv, short, long, arg, desc, block
end
@@ -577,7 +604,7 @@ class OptionParser
while s = lopts.shift
l = left[-1].length + s.length
l += arg.length if left.size == 1 && arg
- l < max or sopts.empty? or left << ''
+ l < max or sopts.empty? or left << +''
left[-1] << (left[-1].empty? ? ' ' * 4 : ', ') << s
end
@@ -628,7 +655,7 @@ class OptionParser
return if sopts.empty? and lopts.empty? # completely hidden
(sopts+lopts).each do |opt|
- # "(-x -c -r)-l[left justify]" \
+ # "(-x -c -r)-l[left justify]"
if /^--\[no-\](.+)$/ =~ opt
o = $1
yield("--#{o}", desc.join(""))
@@ -1136,7 +1163,7 @@ XXX
#
def banner
unless @banner
- @banner = "Usage: #{program_name} [options]"
+ @banner = +"Usage: #{program_name} [options]"
visit(:add_banner, @banner)
end
@banner
@@ -1165,14 +1192,14 @@ XXX
# Version
#
def version
- @version || (defined?(::Version) && ::Version)
+ (defined?(@version) && @version) || (defined?(::Version) && ::Version)
end
#
# Release code
#
def release
- @release || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE)
+ (defined?(@release) && @release) || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE)
end
#
@@ -1180,7 +1207,7 @@ XXX
#
def ver
if v = version
- str = "#{program_name} #{[v].join('.')}"
+ str = +"#{program_name} #{[v].join('.')}"
str << " (#{v})" if v = release
str
end
@@ -1237,7 +1264,8 @@ XXX
# +indent+:: Indentation, defaults to @summary_indent.
#
def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk)
- blk ||= proc {|l| to << (l.index($/, -1) ? l : l + $/)}
+ nl = "\n"
+ blk ||= proc {|l| to << (l.index(nl, -1) ? l : l + nl)}
visit(:summarize, {}, {}, width, max, indent, &blk)
to
end
@@ -1324,6 +1352,8 @@ XXX
# [Description:]
# Description string for the option.
# "Run verbosely"
+ # If you give multiple description strings, each string will be printed
+ # line by line.
#
# [Handler:]
# Handler for the parsed argument value. Either give a block or pass a
@@ -1565,7 +1595,7 @@ XXX
begin
sw, = complete(:short, opt)
# short option matched.
- val = arg.sub(/\A-/, '')
+ val = arg.delete_prefix('-')
has_arg = true
rescue InvalidOption
# if no short options match, try completion with long
@@ -1653,11 +1683,11 @@ XXX
# Wrapper method for getopts.rb.
#
# params = ARGV.getopts("ab:", "foo", "bar:", "zot:Z;zot option")
- # # params[:a] = true # -a
- # # params[:b] = "1" # -b1
- # # params[:foo] = "1" # --foo
- # # params[:bar] = "x" # --bar x
- # # params[:zot] = "z" # --zot Z
+ # # params["a"] = true # -a
+ # # params["b"] = "1" # -b1
+ # # params["foo"] = "1" # --foo
+ # # params["bar"] = "x" # --bar x
+ # # params["zot"] = "z" # --zot Z
#
def getopts(*args)
argv = Array === args.first ? args.shift : default_argv
@@ -1744,16 +1774,16 @@ XXX
def candidate(word)
list = []
case word
+ when '-'
+ long = short = true
when /\A--/
word, arg = word.split(/=/, 2)
argpat = Completion.regexp(arg, false) if arg and !arg.empty?
long = true
- when /\A-(!-)/
- short = true
when /\A-/
- long = short = true
+ short = true
end
- pat = Completion.regexp(word, true)
+ pat = Completion.regexp(word, long)
visit(:each_option) do |opt|
next unless Switch === opt
opts = (long ? opt.long : []) + (short ? opt.short : [])
@@ -1842,7 +1872,7 @@ XXX
#
# Float number format, and converts to Float.
#
- float = "(?:#{decimal}(?:\\.(?:#{decimal})?)?|\\.#{decimal})(?:E[-+]?#{decimal})?"
+ float = "(?:#{decimal}(?=(.)?)(?:\\.(?:#{decimal})?)?|\\.#{decimal})(?:E[-+]?#{decimal})?"
floatpat = %r"\A[-+]?#{float}\z"io
accept(Float, floatpat) {|s,| s.to_f if s}
@@ -1851,11 +1881,13 @@ XXX
# for float format, and Rational for rational format.
#
real = "[-+]?(?:#{octal}|#{float})"
- accept(Numeric, /\A(#{real})(?:\/(#{real}))?\z/io) {|s, d, n|
+ accept(Numeric, /\A(#{real})(?:\/(#{real}))?\z/io) {|s, d, f, n,|
if n
Rational(d, n)
- elsif s
- eval(s)
+ elsif f
+ Float(s)
+ else
+ Integer(s)
end
}
@@ -1865,7 +1897,7 @@ XXX
DecimalInteger = /\A[-+]?#{decimal}\z/io
accept(DecimalInteger, DecimalInteger) {|s,|
begin
- Integer(s)
+ Integer(s, 10)
rescue ArgumentError
raise OptionParser::InvalidArgument, s
end if s
@@ -1889,10 +1921,14 @@ XXX
# integer format, Float for float format.
#
DecimalNumeric = floatpat # decimal integer is allowed as float also.
- accept(DecimalNumeric, floatpat) {|s,|
+ accept(DecimalNumeric, floatpat) {|s, f|
begin
- eval(s)
- rescue SyntaxError
+ if f
+ Float(s)
+ else
+ Integer(s)
+ end
+ rescue ArgumentError
raise OptionParser::InvalidArgument, s
end if s
}
@@ -1946,7 +1982,7 @@ XXX
#
class ParseError < RuntimeError
# Reason which caused the error.
- Reason = 'parse error'.freeze
+ Reason = 'parse error'
def initialize(*args)
@args = args
@@ -2009,42 +2045,42 @@ XXX
# Raises when ambiguously completable string is encountered.
#
class AmbiguousOption < ParseError
- const_set(:Reason, 'ambiguous option'.freeze)
+ const_set(:Reason, 'ambiguous option')
end
#
# Raises when there is an argument for a switch which takes no argument.
#
class NeedlessArgument < ParseError
- const_set(:Reason, 'needless argument'.freeze)
+ const_set(:Reason, 'needless argument')
end
#
# Raises when a switch with mandatory argument has no argument.
#
class MissingArgument < ParseError
- const_set(:Reason, 'missing argument'.freeze)
+ const_set(:Reason, 'missing argument')
end
#
# Raises when switch is undefined.
#
class InvalidOption < ParseError
- const_set(:Reason, 'invalid option'.freeze)
+ const_set(:Reason, 'invalid option')
end
#
# Raises when the given argument does not match required format.
#
class InvalidArgument < ParseError
- const_set(:Reason, 'invalid argument'.freeze)
+ const_set(:Reason, 'invalid argument')
end
#
# Raises when the given argument word can't be completed uniquely.
#
class AmbiguousArgument < InvalidArgument
- const_set(:Reason, 'ambiguous argument'.freeze)
+ const_set(:Reason, 'ambiguous argument')
end
#
@@ -2151,4 +2187,5 @@ end
# ARGV is arguable by OptionParser
ARGV.extend(OptionParser::Arguable)
-OptParse = OptionParser
+# An alias for OptionParser.
+OptParse = OptionParser # :nodoc:
diff --git a/lib/ostruct.gemspec b/lib/ostruct.gemspec
new file mode 100644
index 0000000000..4faf5b84aa
--- /dev/null
+++ b/lib/ostruct.gemspec
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+Gem::Specification.new do |spec|
+ spec.name = "ostruct"
+ spec.version = "0.1.0"
+ spec.authors = ["Marc-Andre Lafortune"]
+ spec.email = ["ruby-core@marc-andre.ca"]
+
+ spec.summary = %q{Class to build custom data structures, similar to a Hash.}
+ spec.description = %q{Class to build custom data structures, similar to a Hash.}
+ spec.homepage = "https://github.com/ruby/ostruct"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "lib/ostruct.rb", "ostruct.gemspec"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+end
diff --git a/lib/ostruct.rb b/lib/ostruct.rb
index bbf1743247..a758a65979 100644
--- a/lib/ostruct.rb
+++ b/lib/ostruct.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# = ostruct.rb: OpenStruct implementation
#
@@ -15,65 +15,64 @@
# accomplished by using Ruby's metaprogramming to define methods on the class
# itself.
#
-# == Examples:
+# == Examples
#
-# require 'ostruct'
+# require "ostruct"
#
# person = OpenStruct.new
-# person.name = "John Smith"
-# person.age = 70
-# person.pension = 300
+# person.name = "John Smith"
+# person.age = 70
#
-# puts person.name # -> "John Smith"
-# puts person.age # -> 70
-# puts person.address # -> nil
+# person.name # => "John Smith"
+# person.age # => 70
+# person.address # => nil
#
-# An OpenStruct employs a Hash internally to store the methods and values and
-# can even be initialized with one:
+# An OpenStruct employs a Hash internally to store the attributes and values
+# and can even be initialized with one:
#
-# australia = OpenStruct.new(:country => "Australia", :population => 20_000_000)
-# p australia # -> <OpenStruct country="Australia" population=20000000>
+# australia = OpenStruct.new(:country => "Australia", :capital => "Canberra")
+# # => #<OpenStruct country="Australia", capital="Canberra">
#
-# Hash keys with spaces or characters that would normally not be able to use for
-# method calls (e.g. ()[]*) will not be immediately available on the
-# OpenStruct object as a method for retrieval or assignment, but can be still be
-# reached through the Object#send method.
+# Hash keys with spaces or characters that could normally not be used for
+# method calls (e.g. <code>()[]*</code>) will not be immediately available
+# on the OpenStruct object as a method for retrieval or assignment, but can
+# still be reached through the Object#send method.
#
# measurements = OpenStruct.new("length (in inches)" => 24)
-# measurements.send("length (in inches)") # -> 24
+# measurements.send("length (in inches)") # => 24
#
-# data_point = OpenStruct.new(:queued? => true)
-# data_point.queued? # -> true
-# data_point.send("queued?=",false)
-# data_point.queued? # -> false
+# message = OpenStruct.new(:queued? => true)
+# message.queued? # => true
+# message.send("queued?=", false)
+# message.queued? # => false
#
-# Removing the presence of a method requires the execution the delete_field
-# method as setting the property value to +nil+ will not remove the method.
+# Removing the presence of an attribute requires the execution of the
+# delete_field method as setting the property value to +nil+ will not
+# remove the attribute.
#
-# first_pet = OpenStruct.new(:name => 'Rowdy', :owner => 'John Smith')
-# first_pet.owner = nil
-# second_pet = OpenStruct.new(:name => 'Rowdy')
+# first_pet = OpenStruct.new(:name => "Rowdy", :owner => "John Smith")
+# second_pet = OpenStruct.new(:name => "Rowdy")
#
-# first_pet == second_pet # -> false
+# first_pet.owner = nil
+# first_pet # => #<OpenStruct name="Rowdy", owner=nil>
+# first_pet == second_pet # => false
#
# first_pet.delete_field(:owner)
-# first_pet == second_pet # -> true
+# first_pet # => #<OpenStruct name="Rowdy">
+# first_pet == second_pet # => true
#
#
-# == Implementation:
+# == Implementation
#
# An OpenStruct utilizes Ruby's method lookup structure to find and define the
-# necessary methods for properties. This is accomplished through the method
-# method_missing and define_method.
+# necessary methods for properties. This is accomplished through the methods
+# method_missing and define_singleton_method.
#
# This should be a consideration if there is a concern about the performance of
# the objects that are created, as there is much more overhead in the setting
# of these properties compared to using a Hash or a Struct.
#
class OpenStruct
- class << self # :nodoc:
- alias allocate new
- end
#
# Creates a new OpenStruct object. By default, the resulting OpenStruct
@@ -83,11 +82,11 @@ class OpenStruct
# (can be a Hash, an OpenStruct or a Struct).
# For example:
#
- # require 'ostruct'
- # hash = { "country" => "Australia", :population => 20_000_000 }
+ # require "ostruct"
+ # hash = { "country" => "Australia", :capital => "Canberra" }
# data = OpenStruct.new(hash)
#
- # p data # -> <OpenStruct country="Australia" population=20000000>
+ # data # => #<OpenStruct country="Australia", capital="Canberra">
#
def initialize(hash=nil)
@table = {}
@@ -99,37 +98,53 @@ class OpenStruct
end
end
- # Duplicate an OpenStruct object members.
- def initialize_copy(orig)
+ # Duplicates an OpenStruct object's Hash table.
+ def initialize_copy(orig) # :nodoc:
super
@table = @table.dup
end
#
+ # call-seq:
+ # ostruct.to_h -> hash
+ # ostruct.to_h {|name, value| block } -> hash
+ #
# Converts the OpenStruct to a hash with keys representing
- # each attribute (as symbols) and their corresponding values
- # Example:
+ # each attribute (as symbols) and their corresponding values.
#
- # require 'ostruct'
- # data = OpenStruct.new("country" => "Australia", :population => 20_000_000)
- # data.to_h # => {:country => "Australia", :population => 20000000 }
+ # If a block is given, the results of the block on each pair of
+ # the receiver will be used as pairs.
#
- def to_h
- @table.dup
+ # require "ostruct"
+ # data = OpenStruct.new("country" => "Australia", :capital => "Canberra")
+ # data.to_h # => {:country => "Australia", :capital => "Canberra" }
+ # data.to_h {|name, value| [name.to_s, value.upcase] }
+ # # => {"country" => "AUSTRALIA", "capital" => "CANBERRA" }
+ #
+ def to_h(&block)
+ if block_given?
+ @table.to_h(&block)
+ else
+ @table.dup
+ end
end
#
- # Yields all attributes (as a symbol) along with the corresponding values
- # or returns an enumerator if not block is given.
- # Example:
+ # :call-seq:
+ # ostruct.each_pair {|name, value| block } -> ostruct
+ # ostruct.each_pair -> Enumerator
#
- # require 'ostruct'
- # data = OpenStruct.new("country" => "Australia", :population => 20_000_000)
- # data.each_pair.to_a # => [[:country, "Australia"], [:population, 20000000]]
+ # Yields all attributes (as symbols) along with the corresponding values
+ # or returns an enumerator if no block is given.
+ #
+ # require "ostruct"
+ # data = OpenStruct.new("country" => "Australia", :capital => "Canberra")
+ # data.each_pair.to_a # => [[:country, "Australia"], [:capital, "Canberra"]]
#
def each_pair
return to_enum(__method__) { @table.size } unless block_given?
@table.each_pair{|p| yield p}
+ self
end
#
@@ -154,13 +169,14 @@ class OpenStruct
begin
@modifiable = true
rescue
- raise RuntimeError, "can't modify frozen #{self.class}", caller(3)
+ exception_class = defined?(FrozenError) ? FrozenError : RuntimeError
+ raise exception_class, "can't modify frozen #{self.class}", caller(3)
end
@table
end
private :modifiable?
- # ::Kernel.warn("#{caller(1, 1)[0]}: do not use OpenStruct#modifiable")
+ # ::Kernel.warn("do not use OpenStruct#modifiable", uplevel: 1)
alias modifiable modifiable? # :nodoc:
protected :modifiable
@@ -179,7 +195,7 @@ class OpenStruct
end
private :new_ostruct_member!
- # ::Kernel.warn("#{caller(1, 1)[0]}: do not use OpenStruct#new_ostruct_member")
+ # ::Kernel.warn("do not use OpenStruct#new_ostruct_member", uplevel: 1)
alias new_ostruct_member new_ostruct_member! # :nodoc:
protected :new_ostruct_member
@@ -188,9 +204,9 @@ class OpenStruct
super
end
- def respond_to_missing?(mid, include_private = false)
+ def respond_to_missing?(mid, include_private = false) # :nodoc:
mname = mid.to_s.chomp("=").to_sym
- @table.key?(mname) || super
+ @table&.key?(mname) || super
end
def method_missing(mid, *args) # :nodoc:
@@ -200,46 +216,69 @@ class OpenStruct
raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
end
modifiable?[new_ostruct_member!(mname)] = args[0]
- elsif len == 0
+ elsif len == 0 # and /\A[a-z_]\w*\z/ =~ mid #
if @table.key?(mid)
new_ostruct_member!(mid) unless frozen?
@table[mid]
end
else
- err = NoMethodError.new "undefined method `#{mid}' for #{self}", mid, args
- err.set_backtrace caller(1)
- raise err
+ begin
+ super
+ rescue NoMethodError => err
+ err.backtrace.shift
+ raise
+ end
end
end
- # Returns the value of a member.
#
- # person = OpenStruct.new('name' => 'John Smith', 'age' => 70)
- # person[:age] # => 70, same as ostruct.age
+ # :call-seq:
+ # ostruct[name] -> object
+ #
+ # Returns the value of an attribute.
+ #
+ # require "ostruct"
+ # person = OpenStruct.new("name" => "John Smith", "age" => 70)
+ # person[:age] # => 70, same as person.age
#
def [](name)
@table[name.to_sym]
end
#
- # Sets the value of a member.
+ # :call-seq:
+ # ostruct[name] = obj -> obj
+ #
+ # Sets the value of an attribute.
#
- # person = OpenStruct.new('name' => 'John Smith', 'age' => 70)
- # person[:age] = 42 # => equivalent to ostruct.age = 42
- # person.age # => 42
+ # require "ostruct"
+ # person = OpenStruct.new("name" => "John Smith", "age" => 70)
+ # person[:age] = 42 # equivalent to person.age = 42
+ # person.age # => 42
#
def []=(name, value)
modifiable?[new_ostruct_member!(name)] = value
end
#
- # Retrieves the value object corresponding to the each +name+
- # objects repeatedly.
+ # :call-seq:
+ # ostruct.dig(name, ...) -> object
+ #
+ # Extracts the nested value specified by the sequence of +name+
+ # objects by calling +dig+ at each step, returning +nil+ if any
+ # intermediate step is +nil+.
#
- # address = OpenStruct.new('city' => "Anytown NC", 'zip' => 12345)
- # person = OpenStruct.new('name' => 'John Smith', 'address' => address)
- # person.dig(:address, 'zip') # => 12345
- # person.dig(:business_address, 'zip') # => nil
+ # require "ostruct"
+ # address = OpenStruct.new("city" => "Anytown NC", "zip" => 12345)
+ # person = OpenStruct.new("name" => "John Smith", "address" => address)
+ #
+ # person.dig(:address, "zip") # => 12345
+ # person.dig(:business_address, "zip") # => nil
+ #
+ # data = OpenStruct.new(:array => [1, [2, 3]])
+ #
+ # data.dig(:array, 1, 0) # => 2
+ # data.dig(:array, 0, 0) # TypeError: Integer does not have #dig method
#
def dig(name, *names)
begin
@@ -251,19 +290,25 @@ class OpenStruct
end
#
- # Remove the named field from the object. Returns the value that the field
+ # Removes the named field from the object. Returns the value that the field
# contained if it was defined.
#
- # require 'ostruct'
+ # require "ostruct"
+ #
+ # person = OpenStruct.new(name: "John", age: 70, pension: 300)
#
- # person = OpenStruct.new('name' => 'John Smith', 'age' => 70)
+ # person.delete_field("age") # => 70
+ # person # => #<OpenStruct name="John", pension=300>
#
- # person.delete_field('name') # => 'John Smith'
+ # Setting the value to +nil+ will not remove the attribute:
+ #
+ # person.pension = nil
+ # person # => #<OpenStruct name="John", pension=nil>
#
def delete_field(name)
sym = name.to_sym
begin
- singleton_class.__send__(:remove_method, sym, "#{sym}=")
+ singleton_class.remove_method(sym, "#{sym}=")
rescue NameError
end
@table.delete(sym) do
@@ -277,25 +322,20 @@ class OpenStruct
# Returns a string containing a detailed summary of the keys and values.
#
def inspect
- str = "#<#{self.class}"
-
ids = (Thread.current[InspectKey] ||= [])
if ids.include?(object_id)
- return str << ' ...>'
- end
-
- ids << object_id
- begin
- first = true
- for k,v in @table
- str << "," unless first
- first = false
- str << " #{k}=#{v.inspect}"
+ detail = ' ...'
+ else
+ ids << object_id
+ begin
+ detail = @table.map do |key, value|
+ " #{key}=#{value.inspect}"
+ end.join(',')
+ ensure
+ ids.pop
end
- return str << '>'
- ensure
- ids.pop
end
+ ['#<', self.class, detail, '>'].join
end
alias :to_s :inspect
@@ -308,6 +348,14 @@ class OpenStruct
# +other+ when +other+ is an OpenStruct and the two objects' Hash tables are
# equal.
#
+ # require "ostruct"
+ # first_pet = OpenStruct.new("name" => "Rowdy")
+ # second_pet = OpenStruct.new(:name => "Rowdy")
+ # third_pet = OpenStruct.new("name" => "Rowdy", :age => nil)
+ #
+ # first_pet == second_pet # => true
+ # first_pet == third_pet # => false
+ #
def ==(other)
return false unless other.kind_of?(OpenStruct)
@table == other.table!
@@ -323,9 +371,11 @@ class OpenStruct
@table.eql?(other.table!)
end
- # Compute a hash-code for this OpenStruct.
- # Two hashes with the same content will have the same hash code
- # (and will be eql?).
+ # Computes a hash code for this OpenStruct.
+ # Two OpenStruct objects with the same content will have the same hash code
+ # (and will compare using #eql?).
+ #
+ # See also Object#hash.
def hash
@table.hash
end
diff --git a/lib/pp.rb b/lib/pp.rb
index aa2b67a420..cbc49e72e9 100644
--- a/lib/pp.rb
+++ b/lib/pp.rb
@@ -2,36 +2,9 @@
require 'prettyprint'
-module Kernel
- # Returns a pretty printed object as a string.
- #
- # In order to use this method you must first require the PP module:
- #
- # require 'pp'
- #
- # See the PP module for more information.
- def pretty_inspect
- PP.pp(self, ''.dup)
- end
-
- # prints arguments in pretty form.
- #
- # pp returns argument(s).
- def pp(*objs)
- objs.each {|obj|
- PP.pp(obj)
- }
- objs.size <= 1 ? objs.first : objs
- end
- module_function :pp
-end
-
##
# A pretty-printer for Ruby objects.
#
-# All examples assume you have loaded the PP class with:
-# require 'pp'
-#
##
# == What PP Does
#
@@ -311,7 +284,7 @@ class PP < PrettyPrint
inspect_method = method_method.call(:inspect)
rescue NameError
end
- if inspect_method && /\(Kernel\)#/ !~ inspect_method.inspect
+ if inspect_method && inspect_method.owner != Kernel
q.text self.inspect
elsif !inspect_method && self.respond_to?(:inspect)
q.text self.inspect
@@ -345,7 +318,7 @@ class PP < PrettyPrint
# However, doing this requires that every class that #inspect is called on
# implement #pretty_print, or a RuntimeError will be raised.
def pretty_print_inspect
- if /\(PP::ObjectMixin\)#/ =~ Object.instance_method(:method).bind(self).call(:pretty_print).inspect
+ if Object.instance_method(:method).bind(self).call(:pretty_print).owner == PP::ObjectMixin
raise "pretty_print is not overridden for #{self.class}"
end
PP.singleline_pp(self, ''.dup)
@@ -413,12 +386,12 @@ class Range # :nodoc:
q.breakable ''
q.text(self.exclude_end? ? '...' : '..')
q.breakable ''
- q.pp self.end
+ q.pp self.end if self.end
end
end
-class String
- def pretty_print(q)
+class String # :nodoc:
+ def pretty_print(q) # :nodoc:
lines = self.lines
if lines.size > 1
q.group(0, '', '') do
@@ -541,6 +514,36 @@ class MatchData # :nodoc:
end
end
+class RubyVM::AbstractSyntaxTree::Node
+ def pretty_print_children(q, names = [])
+ children.zip(names) do |c, n|
+ if n
+ q.breakable
+ q.text "#{n}:"
+ end
+ q.group(2) do
+ q.breakable
+ q.pp c
+ end
+ end
+ end
+
+ def pretty_print(q)
+ q.group(1, "(#{type}@#{first_lineno}:#{first_column}-#{last_lineno}:#{last_column}", ")") {
+ case type
+ when :SCOPE
+ pretty_print_children(q, %w"tbl args body")
+ when :ARGS
+ pretty_print_children(q, %w[pre_num pre_init opt first_post post_num post_init rest kw kwrest block])
+ when :DEFN
+ pretty_print_children(q, %w[mid body])
+ else
+ pretty_print_children(q)
+ end
+ }
+ end
+end
+
class Object < BasicObject # :nodoc:
include PP::ObjectMixin
end
@@ -560,3 +563,27 @@ end
end
}
}
+
+module Kernel
+ # Returns a pretty printed object as a string.
+ #
+ # In order to use this method you must first require the PP module:
+ #
+ # require 'pp'
+ #
+ # See the PP module for more information.
+ def pretty_inspect
+ PP.pp(self, ''.dup)
+ end
+
+ # prints arguments in pretty form.
+ #
+ # pp returns argument(s).
+ def pp(*objs)
+ objs.each {|obj|
+ PP.pp(obj)
+ }
+ objs.size <= 1 ? objs.first : objs
+ end
+ module_function :pp
+end
diff --git a/lib/prime.gemspec b/lib/prime.gemspec
new file mode 100644
index 0000000000..6a9aa683e6
--- /dev/null
+++ b/lib/prime.gemspec
@@ -0,0 +1,27 @@
+begin
+ require_relative "lib/prime"
+rescue LoadError
+ # for Ruby core repository
+ require_relative "prime"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "prime"
+ spec.version = Prime::VERSION
+ spec.authors = ["Yuki Sonoda"]
+ spec.email = ["yugui@yugui.jp"]
+
+ spec.summary = %q{Prime numbers and factorization library.}
+ spec.description = %q{Prime numbers and factorization library.}
+ spec.homepage = "https://github.com/ruby/prime"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "lib/prime.rb", "lib/prime/version.rb", "prime.gemspec"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+ spec.add_development_dependency "test-unit"
+end
diff --git a/lib/prime.rb b/lib/prime.rb
index ab9e05b2fe..9f6db448be 100644
--- a/lib/prime.rb
+++ b/lib/prime.rb
@@ -35,7 +35,7 @@ class Integer
return self >= 2 if self <= 3
return true if self == 5
return false unless 30.gcd(self) == 1
- (7..Math.sqrt(self).to_i).step(30) do |p|
+ (7..Integer.sqrt(self)).step(30) do |p|
return false if
self%(p) == 0 || self%(p+4) == 0 || self%(p+6) == 0 || self%(p+10) == 0 ||
self%(p+12) == 0 || self%(p+16) == 0 || self%(p+22) == 0 || self%(p+24) == 0
@@ -95,6 +95,9 @@ end
# has many prime factors. e.g. for Prime#prime? .
class Prime
+
+ VERSION = "0.1.0"
+
include Enumerable
include Singleton
@@ -388,13 +391,6 @@ class Prime
@ulticheck_next_squared = 121 # @primes[@ulticheck_index + 1] ** 2
end
- # Returns the cached prime numbers.
- def cache
- @primes
- end
- alias primes cache
- alias primes_so_far cache
-
# Returns the +index+th prime number.
#
# +index+ is a 0-based index.
@@ -419,7 +415,7 @@ class Prime
end
end
- # Internal use. An implementation of eratosthenes' sieve
+ # Internal use. An implementation of Eratosthenes' sieve
class EratosthenesSieve
include Singleton
@@ -445,7 +441,7 @@ class Prime
segment_min = @max_checked
segment_max = [segment_min + max_segment_size, max_cached_prime * 2].min
- root = Integer(Math.sqrt(segment_max).floor)
+ root = Integer.sqrt(segment_max)
segment = ((segment_min + 1) .. segment_max).step(2).to_a
diff --git a/lib/profile.rb b/lib/profile.rb
index 39f8370370..e58c92125b 100644
--- a/lib/profile.rb
+++ b/lib/profile.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'profiler'
RubyVM::InstructionSequence.compile_option = {
diff --git a/lib/profiler.rb b/lib/profiler.rb
index ab55e1fe48..b3c9f7f46a 100644
--- a/lib/profiler.rb
+++ b/lib/profiler.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# Profile provides a way to Profile your Ruby application.
#
# Profiling your program is a way of determining which methods are called and
diff --git a/lib/pstore.rb b/lib/pstore.rb
index 3e2f594b6f..4daa2e003f 100644
--- a/lib/pstore.rb
+++ b/lib/pstore.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# = PStore -- Transactional File Storage for Ruby Objects
#
# pstore.rb -
@@ -335,7 +335,7 @@ class PStore
save_data(checksum, original_data_size, file)
end
ensure
- file.close if !file.closed?
+ file.close
end
else
# This can only occur if read_only == true.
diff --git a/lib/racc/rdoc/grammar.en.rdoc b/lib/racc/rdoc/grammar.en.rdoc
index c00e19daff..af10803f3a 100644
--- a/lib/racc/rdoc/grammar.en.rdoc
+++ b/lib/racc/rdoc/grammar.en.rdoc
@@ -16,7 +16,7 @@ You can insert comments about all places. Two styles of comments can be used, Ru
The class block is formed like this:
class CLASS_NAME
- [precedance table]
+ [precedence table]
[token declarations]
[expected number of S/R conflicts]
[options]
diff --git a/lib/rbconfig/.document b/lib/rbconfig/.document
deleted file mode 100644
index 4cea83cd0e..0000000000
--- a/lib/rbconfig/.document
+++ /dev/null
@@ -1 +0,0 @@
-# these files are obsolete
diff --git a/lib/rbconfig/datadir.rb b/lib/rbconfig/datadir.rb
deleted file mode 100644
index 136162da44..0000000000
--- a/lib/rbconfig/datadir.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: false
-#--
-# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
-# All rights reserved.
-# See LICENSE.txt for permissions.
-#++
-
-# N.B. This file is used by Config.datadir in rubygems.rb, and must not be
-# removed before that require is removed. I require to avoid warning more than
-# once.
-
-warn 'rbconfig/datadir.rb and {Rb}Config.datadir is being deprecated from '\
- 'RubyGems. It will be removed completely on or after June 2011. If you '\
- 'wish to rely on a datadir, please use Gem.datadir.'
diff --git a/lib/rdoc.rb b/lib/rdoc.rb
index 18b8fcb9f3..fc8ad9e144 100644
--- a/lib/rdoc.rb
+++ b/lib/rdoc.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
$DEBUG_RDOC = nil
# :main: README.rdoc
@@ -62,10 +62,7 @@ module RDoc
class Error < RuntimeError; end
- ##
- # RDoc version you are using
-
- VERSION = '5.0.0'
+ require 'rdoc/version'
##
# Method visibilities
@@ -125,8 +122,6 @@ module RDoc
autoload :RDoc, 'rdoc/rdoc'
- autoload :TestCase, 'rdoc/test_case'
-
autoload :CrossReference, 'rdoc/cross_reference'
autoload :ERBIO, 'rdoc/erbio'
autoload :ERBPartial, 'rdoc/erb_partial'
@@ -148,13 +143,11 @@ module RDoc
autoload :KNOWN_CLASSES, 'rdoc/known_classes'
- autoload :RubyLex, 'rdoc/ruby_lex'
- autoload :RubyToken, 'rdoc/ruby_token'
autoload :TokenStream, 'rdoc/token_stream'
autoload :Comment, 'rdoc/comment'
- autoload :I18n, 'rdoc/i18n'
+ require 'rdoc/i18n'
# code objects
#
diff --git a/lib/rdoc/alias.rb b/lib/rdoc/alias.rb
index 1e06fb96e5..858e053049 100644
--- a/lib/rdoc/alias.rb
+++ b/lib/rdoc/alias.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Represent an alias, which is an old_name/new_name pair associated with a
# particular context
diff --git a/lib/rdoc/anon_class.rb b/lib/rdoc/anon_class.rb
index 098bfdfcf9..d02a38c2cf 100644
--- a/lib/rdoc/anon_class.rb
+++ b/lib/rdoc/anon_class.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# An anonymous class like:
#
diff --git a/lib/rdoc/any_method.rb b/lib/rdoc/any_method.rb
index 16ac8e024e..9b0d309653 100644
--- a/lib/rdoc/any_method.rb
+++ b/lib/rdoc/any_method.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# AnyMethod is the base class for objects representing methods
@@ -244,9 +244,9 @@ class RDoc::AnyMethod < RDoc::MethodAttr
if @block_params then
# If this method has explicit block parameters, remove any explicit
# &block
- params.sub!(/,?\s*&\w+/, '')
+ params = params.sub(/,?\s*&\w+/, '')
else
- params.sub!(/\&(\w+)/, '\1')
+ params = params.sub(/\&(\w+)/, '\1')
end
params = params.gsub(/\s+/, '').split(',').reject(&:empty?)
@@ -265,7 +265,7 @@ class RDoc::AnyMethod < RDoc::MethodAttr
params = params.sub(/(\|[^|]+\|)\s*\.\.\.\s*(end|\})/, '\1 \2')
elsif @params then
params = @params.gsub(/\s*\#.*/, '')
- params = params.tr("\n", " ").squeeze(" ")
+ params = params.tr_s("\n ", " ")
params = "(#{params})" unless params[0] == ?(
else
params = ''
@@ -274,12 +274,11 @@ class RDoc::AnyMethod < RDoc::MethodAttr
if @block_params then
# If this method has explicit block parameters, remove any explicit
# &block
- params.sub!(/,?\s*&\w+/, '')
+ params = params.sub(/,?\s*&\w+/, '')
- block = @block_params.gsub(/\s*\#.*/, '')
- block = block.tr("\n", " ").squeeze(" ")
+ block = @block_params.tr_s("\n ", " ")
if block[0] == ?(
- block.sub!(/^\(/, '').sub!(/\)/, '')
+ block = block.sub(/^\(/, '').sub(/\)/, '')
end
params << " { |#{block}| ... }"
end
diff --git a/lib/rdoc/attr.rb b/lib/rdoc/attr.rb
index f77a5c04a7..f780b3b976 100644
--- a/lib/rdoc/attr.rb
+++ b/lib/rdoc/attr.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# An attribute created by \#attr, \#attr_reader, \#attr_writer or
# \#attr_accessor
diff --git a/lib/rdoc/class_module.rb b/lib/rdoc/class_module.rb
index 5881d6cf24..fdd56e236b 100644
--- a/lib/rdoc/class_module.rb
+++ b/lib/rdoc/class_module.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# ClassModule is the base class for objects representing either a class or a
# module.
@@ -136,7 +136,9 @@ class RDoc::ClassModule < RDoc::Context
normalize_comment comment
end
- @comment_location.delete_if { |(_, l)| l == location }
+ if location.parser == RDoc::Parser::C
+ @comment_location.delete_if { |(_, l)| l == location }
+ end
@comment_location << [comment, location]
diff --git a/lib/rdoc/code_object.rb b/lib/rdoc/code_object.rb
index 21aa2b09f5..aeb4b4762e 100644
--- a/lib/rdoc/code_object.rb
+++ b/lib/rdoc/code_object.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Base class for the RDoc code tree.
#
@@ -70,13 +70,6 @@ class RDoc::CodeObject
attr_reader :metadata
##
- # Offset in #file where this CodeObject was defined
- #--
- # TODO character or byte?
-
- attr_accessor :offset
-
- ##
# Sets the parent CodeObject
attr_writer :parent
@@ -151,7 +144,7 @@ class RDoc::CodeObject
# HACK correct fix is to have #initialize create @comment
# with the correct encoding
if String === @comment and @comment.empty? then
- @comment.force_encoding comment.encoding
+ @comment = RDoc::Encoding.change_encoding @comment, comment.encoding
end
@comment
end
diff --git a/lib/rdoc/code_objects.rb b/lib/rdoc/code_objects.rb
index 564849e1d1..434a25ac7f 100644
--- a/lib/rdoc/code_objects.rb
+++ b/lib/rdoc/code_objects.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# This file was used to load all the RDoc::CodeObject subclasses at once. Now
# autoload handles this.
diff --git a/lib/rdoc/comment.rb b/lib/rdoc/comment.rb
index c655763b3e..134f6440a0 100644
--- a/lib/rdoc/comment.rb
+++ b/lib/rdoc/comment.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A comment holds the text comment for a RDoc::CodeObject and provides a
# unified way of cleaning it up and parsing it into an RDoc::Markup::Document.
@@ -45,7 +45,7 @@ class RDoc::Comment
def initialize text = nil, location = nil
@location = location
- @text = text
+ @text = text.nil? ? nil : text.dup
@document = nil
@format = 'rdoc'
@@ -114,10 +114,14 @@ class RDoc::Comment
method.call_seq = seq.chomp
- elsif @text.sub!(/^\s*:?call-seq:(.*?)(^\s*$|\z)/m, '') then
- seq = $1
- seq.gsub!(/^\s*/, '')
- method.call_seq = seq
+ else
+ regexp = /^\s*:?call-seq:(.*?)(^\s*$|\z)/m
+ if regexp =~ @text then
+ @text = @text.sub(regexp, '')
+ seq = $1
+ seq.gsub!(/^\s*/, '')
+ method.call_seq = seq
+ end
end
method
@@ -133,8 +137,14 @@ class RDoc::Comment
##
# HACK dubious
- def force_encoding encoding
- @text.force_encoding encoding
+ def encode! encoding
+ # TODO: Remove this condition after Ruby 2.2 EOL
+ if RUBY_VERSION < '2.3.0'
+ @text = @text.force_encoding encoding
+ else
+ @text = String.new @text, encoding: encoding
+ end
+ self
end
##
@@ -200,7 +210,7 @@ class RDoc::Comment
def remove_private
# Workaround for gsub encoding for Ruby 1.9.2 and earlier
empty = ''
- empty.force_encoding @text.encoding
+ empty = RDoc::Encoding.change_encoding empty, @text.encoding
@text = @text.gsub(%r%^\s*([#*]?)--.*?^\s*(\1)\+\+\n?%m, empty)
@text = @text.sub(%r%^\s*[#*]?--.*%m, '')
@@ -216,7 +226,7 @@ class RDoc::Comment
@text.nil? and @document
@document = nil
- @text = text
+ @text = text.nil? ? nil : text.dup
end
##
diff --git a/lib/rdoc/constant.rb b/lib/rdoc/constant.rb
index 4fd5c5f10f..0c3d7505a1 100644
--- a/lib/rdoc/constant.rb
+++ b/lib/rdoc/constant.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A constant
@@ -36,7 +36,7 @@ class RDoc::Constant < RDoc::CodeObject
@value = value
@is_alias_for = nil
- @visibility = nil
+ @visibility = :public
self.comment = comment
end
@@ -136,7 +136,7 @@ class RDoc::Constant < RDoc::CodeObject
initialize array[1], nil, array[5]
@full_name = array[2]
- @visibility = array[3]
+ @visibility = array[3] || :public
@is_alias_for = array[4]
# 5 handled above
# 6 handled below
diff --git a/lib/rdoc/context.rb b/lib/rdoc/context.rb
index dc34c3f34b..6caf0d6712 100644
--- a/lib/rdoc/context.rb
+++ b/lib/rdoc/context.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'cgi'
##
@@ -99,6 +99,11 @@ class RDoc::Context < RDoc::CodeObject
attr_accessor :visibility
##
+ # Current visibility of this line
+
+ attr_writer :current_line_visibility
+
+ ##
# Hash of registered methods. Attributes are also registered here,
# twice if they are RW.
@@ -148,6 +153,7 @@ class RDoc::Context < RDoc::CodeObject
@extends = []
@constants = []
@external_aliases = []
+ @current_line_visibility = nil
# This Hash maps a method name to a list of unmatched aliases (aliases of
# a method not yet encountered).
@@ -233,7 +239,7 @@ class RDoc::Context < RDoc::CodeObject
if known then
known.comment = attribute.comment if known.comment.empty?
- elsif registered = @methods_hash[attribute.pretty_name << '='] and
+ elsif registered = @methods_hash[attribute.pretty_name + '='] and
RDoc::Attr === registered then
registered.rw = 'RW'
else
@@ -243,7 +249,7 @@ class RDoc::Context < RDoc::CodeObject
end
if attribute.rw.index 'W' then
- key = attribute.pretty_name << '='
+ key = attribute.pretty_name + '='
known = @methods_hash[key]
if known then
@@ -401,6 +407,7 @@ class RDoc::Context < RDoc::CodeObject
mod.section = current_section # TODO declaring context? something is
# wrong here...
mod.parent = self
+ mod.full_name = nil
mod.store = @store
unless @done_documenting then
@@ -408,6 +415,10 @@ class RDoc::Context < RDoc::CodeObject
# this must be done AFTER adding mod to its parent, so that the full
# name is correct:
all_hash[mod.full_name] = mod
+ if @store.unmatched_constant_alias[mod.full_name] then
+ to, file = @store.unmatched_constant_alias[mod.full_name]
+ add_module_alias mod, mod.name, to, file
+ end
end
mod
@@ -478,7 +489,11 @@ class RDoc::Context < RDoc::CodeObject
end
else
@methods_hash[key] = method
- method.visibility = @visibility
+ if @current_line_visibility
+ method.visibility, @current_line_visibility = @current_line_visibility, nil
+ else
+ method.visibility = @visibility
+ end
add_to @method_list, method
resolve_aliases method
end
@@ -501,40 +516,52 @@ class RDoc::Context < RDoc::CodeObject
end
##
+ # Adds a module by +RDoc::NormalModule+ instance. See also #add_module.
+
+ def add_module_by_normal_module(mod)
+ add_class_or_module mod, @modules, @store.modules_hash
+ end
+
+ ##
# Adds an alias from +from+ (a class or module) to +name+ which was defined
# in +file+.
- def add_module_alias from, name, file
+ def add_module_alias from, from_name, to, file
return from if @done_documenting
- to_name = child_name name
+ to_full_name = child_name to.name
# if we already know this name, don't register an alias:
# see the metaprogramming in lib/active_support/basic_object.rb,
# where we already know BasicObject is a class when we find
# BasicObject = BlankSlate
- return from if @store.find_class_or_module to_name
+ return from if @store.find_class_or_module to_full_name
- to = from.dup
- to.name = name
- to.full_name = nil
+ unless from
+ @store.unmatched_constant_alias[child_name(from_name)] = [to, file]
+ return to
+ end
+
+ new_to = from.dup
+ new_to.name = to.name
+ new_to.full_name = nil
- if to.module? then
- @store.modules_hash[to_name] = to
- @modules[name] = to
+ if new_to.module? then
+ @store.modules_hash[to_full_name] = new_to
+ @modules[to.name] = new_to
else
- @store.classes_hash[to_name] = to
- @classes[name] = to
+ @store.classes_hash[to_full_name] = new_to
+ @classes[to.name] = new_to
end
# Registers a constant for this alias. The constant value and comment
# will be updated later, when the Ruby parser adds the constant
- const = RDoc::Constant.new name, nil, to.comment
+ const = RDoc::Constant.new to.name, nil, new_to.comment
const.record_location file
const.is_alias_for = from
add_constant const
- to
+ new_to
end
##
@@ -752,7 +779,7 @@ class RDoc::Context < RDoc::CodeObject
attributes.default = []
sort_sections.each do |section|
- yield section, constants[section].sort, attributes[section].sort
+ yield section, constants[section].select(&:display?).sort, attributes[section].select(&:display?).sort
end
end
@@ -853,7 +880,13 @@ class RDoc::Context < RDoc::CodeObject
# Finds a method named +name+ with singleton value +singleton+.
def find_method(name, singleton)
- @method_list.find { |m| m.name == name && m.singleton == singleton }
+ @method_list.find { |m|
+ if m.singleton
+ m.name == name && m.singleton == singleton
+ else
+ m.name == name && !m.singleton && !singleton
+ end
+ }
end
##
@@ -1069,6 +1102,7 @@ class RDoc::Context < RDoc::CodeObject
return if [:private, :nodoc].include? min_visibility
remove_invisible_in @method_list, min_visibility
remove_invisible_in @attributes, min_visibility
+ remove_invisible_in @constants, min_visibility
end
##
@@ -1156,6 +1190,17 @@ class RDoc::Context < RDoc::CodeObject
end
##
+ # Given an array +names+ of constants, set the visibility of each constant to
+ # +visibility+
+
+ def set_constant_visibility_for(names, visibility)
+ names.each do |name|
+ constant = @constants_hash[name] or next
+ constant.visibility = visibility
+ end
+ end
+
+ ##
# Sorts sections alphabetically (default) or in TomDoc fashion (none,
# Public, Internal, Deprecated)
diff --git a/lib/rdoc/context/section.rb b/lib/rdoc/context/section.rb
index 7c3c8c603d..11f9ceaf87 100644
--- a/lib/rdoc/context/section.rb
+++ b/lib/rdoc/context/section.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A section of documentation like:
#
@@ -43,7 +43,7 @@ class RDoc::Context::Section
@parent = parent
@title = title ? title.strip : title
- @@sequence.succ!
+ @@sequence = @@sequence.succ
@sequence = @@sequence.dup
@comments = []
diff --git a/lib/rdoc/cross_reference.rb b/lib/rdoc/cross_reference.rb
index 0e40d23159..7b137483d5 100644
--- a/lib/rdoc/cross_reference.rb
+++ b/lib/rdoc/cross_reference.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# RDoc::CrossReference is a reusable way to create cross references for names.
@@ -19,12 +19,12 @@ class RDoc::CrossReference
#
# See CLASS_REGEXP_STR
- METHOD_REGEXP_STR = '([a-z]\w*[!?=]?|%|===|\[\]=?|<<|>>)(?:\([\w.+*/=<>-]*\))?'
+ METHOD_REGEXP_STR = '([a-z]\w*[!?=]?|%|===|\[\]=?|<<|>>|-|\+|\*)(?:\([\w.+*/=<>-]*\))?'
##
# Regular expressions matching text that should potentially have
- # cross-reference links generated are passed to add_special. Note that
- # these expressions are meant to pick up text for which cross-references
+ # cross-reference links generated are passed to add_regexp_handling. Note
+ # that these expressions are meant to pick up text for which cross-references
# have been suppressed, since the suppression characters are removed by the
# code that is triggered.
@@ -127,23 +127,41 @@ class RDoc::CrossReference
if /#{CLASS_REGEXP_STR}([.#]|::)#{METHOD_REGEXP_STR}/o =~ name then
type = $2
- type = '' if type == '.' # will find either #method or ::method
- method = "#{type}#{$3}"
+ if '.' == type # will find either #method or ::method
+ method = $3
+ else
+ method = "#{type}#{$3}"
+ end
container = @context.find_symbol_module($1)
elsif /^([.#]|::)#{METHOD_REGEXP_STR}/o =~ name then
type = $1
- type = '' if type == '.'
- method = "#{type}#{$2}"
+ if '.' == type
+ method = $2
+ else
+ method = "#{type}#{$2}"
+ end
container = @context
else
+ type = nil
container = nil
end
if container then
- ref = container.find_local_symbol method
-
- unless ref || RDoc::TopLevel === container then
- ref = container.find_ancestor_local_symbol method
+ unless RDoc::TopLevel === container then
+ if '.' == type then
+ if 'new' == method then # AnyClassName.new will be class method
+ ref = container.find_local_symbol method
+ ref = container.find_ancestor_local_symbol method unless ref
+ else
+ ref = container.find_local_symbol "::#{method}"
+ ref = container.find_ancestor_local_symbol "::#{method}" unless ref
+ ref = container.find_local_symbol "##{method}" unless ref
+ ref = container.find_ancestor_local_symbol "##{method}" unless ref
+ end
+ else
+ ref = container.find_local_symbol method
+ ref = container.find_ancestor_local_symbol method unless ref
+ end
end
end
diff --git a/lib/rdoc/encoding.rb b/lib/rdoc/encoding.rb
index 44881d043c..cf60badd24 100644
--- a/lib/rdoc/encoding.rb
+++ b/lib/rdoc/encoding.rb
@@ -1,5 +1,5 @@
# coding: US-ASCII
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# This class is a wrapper around File IO and Encoding that helps RDoc load
@@ -7,6 +7,18 @@
module RDoc::Encoding
+ HEADER_REGEXP = /^
+ (?:
+ \A\#!.*\n
+ |
+ ^\#\s+frozen[-_]string[-_]literal[=:].+\n
+ |
+ ^\#[^\n]+\b(?:en)?coding[=:]\s*(?<name>[^\s;]+).*\n
+ |
+ <\?xml[^?]*encoding=(?<quote>["'])(?<name>.*?)\k<quote>.*\n
+ )+
+ /xi # :nodoc:
+
##
# Reads the contents of +filename+ and handles any encoding directives in
# the file.
@@ -18,31 +30,32 @@ module RDoc::Encoding
# unknown character in the target encoding will be replaced with '?'
def self.read_file filename, encoding, force_transcode = false
- content = open filename, "rb" do |f| f.read end
+ content = File.open filename, "rb" do |f| f.read end
content.gsub!("\r\n", "\n") if RUBY_PLATFORM =~ /mswin|mingw/
utf8 = content.sub!(/\A\xef\xbb\xbf/, '')
- RDoc::Encoding.set_encoding content
+ enc = RDoc::Encoding.detect_encoding content
+ content = RDoc::Encoding.change_encoding content, enc if enc
begin
encoding ||= Encoding.default_external
orig_encoding = content.encoding
if not orig_encoding.ascii_compatible? then
- content.encode! encoding
+ content = content.encode encoding
elsif utf8 then
- content.force_encoding Encoding::UTF_8
- content.encode! encoding
+ content = RDoc::Encoding.change_encoding content, Encoding::UTF_8
+ content = content.encode encoding
else
# assume the content is in our output encoding
- content.force_encoding encoding
+ content = RDoc::Encoding.change_encoding content, encoding
end
unless content.valid_encoding? then
# revert and try to transcode
- content.force_encoding orig_encoding
- content.encode! encoding
+ content = RDoc::Encoding.change_encoding content, orig_encoding
+ content = content.encode encoding
end
unless content.valid_encoding? then
@@ -52,10 +65,11 @@ module RDoc::Encoding
rescue Encoding::InvalidByteSequenceError,
Encoding::UndefinedConversionError => e
if force_transcode then
- content.force_encoding orig_encoding
- content.encode!(encoding,
- :invalid => :replace, :undef => :replace,
- :replace => '?')
+ content = RDoc::Encoding.change_encoding content, orig_encoding
+ content = content.encode(encoding,
+ :invalid => :replace,
+ :undef => :replace,
+ :replace => '?')
return content
else
warn "unable to convert #{e.message} for #{filename}, skipping"
@@ -77,32 +91,46 @@ module RDoc::Encoding
first_line = $1
if first_line =~ /\A# +frozen[-_]string[-_]literal[=:].+$/i
- string.sub! first_line, ''
+ string = string.sub first_line, ''
end
+
+ string
end
##
- # Sets the encoding of +string+ based on the magic comment
+ # Detects the encoding of +string+ based on the magic comment
- def self.set_encoding string
- remove_frozen_string_literal string
-
- string =~ /\A(?:#!.*\n)?(.*\n)/
+ def self.detect_encoding string
+ result = HEADER_REGEXP.match string
+ name = result && result[:name]
- first_line = $1
-
- name = case first_line
- when /^<\?xml[^?]*encoding=(["'])(.*?)\1/ then $2
- when /\b(?:en)?coding[=:]\s*([^\s;]+)/i then $1
- else return
- end
+ name ? Encoding.find(name) : nil
+ end
- string.sub! first_line, ''
+ ##
+ # Removes magic comments and shebang
- remove_frozen_string_literal string
+ def self.remove_magic_comment string
+ string.sub HEADER_REGEXP do |s|
+ s.gsub(/[^\n]/, '')
+ end
+ end
- enc = Encoding.find name
- string.force_encoding enc if enc
+ ##
+ # Changes encoding based on +encoding+ without converting and returns new
+ # string
+
+ def self.change_encoding text, encoding
+ if text.kind_of? RDoc::Comment
+ text.encode! encoding
+ else
+ # TODO: Remove this condition after Ruby 2.2 EOL
+ if RUBY_VERSION < '2.3.0'
+ text.force_encoding encoding
+ else
+ String.new text, encoding: encoding
+ end
+ end
end
end
diff --git a/lib/rdoc/erb_partial.rb b/lib/rdoc/erb_partial.rb
index d17dda20a9..8dc2c46013 100644
--- a/lib/rdoc/erb_partial.rb
+++ b/lib/rdoc/erb_partial.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Allows an ERB template to be rendered in the context (binding) of an
# existing ERB template evaluation.
diff --git a/lib/rdoc/erbio.rb b/lib/rdoc/erbio.rb
index a2aaa90e67..820a25ae01 100644
--- a/lib/rdoc/erbio.rb
+++ b/lib/rdoc/erbio.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'erb'
##
@@ -9,7 +9,7 @@ require 'erb'
#
# erbio = RDoc::ERBIO.new '<%= "hello world" %>', nil, nil
#
-# open 'hello.txt', 'w' do |io|
+# File.open 'hello.txt', 'w' do |io|
# erbio.result binding
# end
#
@@ -21,7 +21,11 @@ class RDoc::ERBIO < ERB
# Defaults +eoutvar+ to 'io', otherwise is identical to ERB's initialize
def initialize str, safe_level = nil, trim_mode = nil, eoutvar = 'io'
- super
+ if RUBY_VERSION >= '2.6'
+ super(str, trim_mode: trim_mode, eoutvar: eoutvar)
+ else
+ super
+ end
end
##
diff --git a/lib/rdoc/extend.rb b/lib/rdoc/extend.rb
index 30b51a1dbd..e1b182902e 100644
--- a/lib/rdoc/extend.rb
+++ b/lib/rdoc/extend.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A Module extension to a class with \#extend
#
diff --git a/lib/rdoc/generator.rb b/lib/rdoc/generator.rb
index 6efc5e4474..340dcbf7ae 100644
--- a/lib/rdoc/generator.rb
+++ b/lib/rdoc/generator.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# RDoc uses generators to turn parsed source code in the form of an
# RDoc::CodeObject tree into some form of output. RDoc comes with the HTML
diff --git a/lib/rdoc/generator/darkfish.rb b/lib/rdoc/generator/darkfish.rb
index e961518fcc..a07f74e716 100644
--- a/lib/rdoc/generator/darkfish.rb
+++ b/lib/rdoc/generator/darkfish.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# -*- mode: ruby; ruby-indent-level: 2; tab-width: 2 -*-
require 'erb'
@@ -313,12 +313,16 @@ class RDoc::Generator::Darkfish
search_index_rel_prefix = rel_prefix
search_index_rel_prefix += @asset_rel_path if @file_output
- # suppress 1.9.3 warning
- asset_rel_prefix = asset_rel_prefix = rel_prefix + @asset_rel_path
+ asset_rel_prefix = rel_prefix + @asset_rel_path
@title = @options.title
- render_template template_file, out_file do |io| binding end
+ render_template template_file, out_file do |io|
+ here = binding
+ # suppress 1.9.3 warning
+ here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
+ here
+ end
rescue => e
error = RDoc::Error.new \
"error generating index.html: #{e.message} (#{e.class})"
@@ -343,14 +347,19 @@ class RDoc::Generator::Darkfish
search_index_rel_prefix = rel_prefix
search_index_rel_prefix += @asset_rel_path if @file_output
- # suppress 1.9.3 warning
- asset_rel_prefix = asset_rel_prefix = rel_prefix + @asset_rel_path
- svninfo = svninfo = get_svninfo(current)
+ asset_rel_prefix = rel_prefix + @asset_rel_path
+ svninfo = get_svninfo(current)
@title = "#{klass.type} #{klass.full_name} - #{@options.title}"
debug_msg " rendering #{out_file}"
- render_template template_file, out_file do |io| binding end
+ render_template template_file, out_file do |io|
+ here = binding
+ # suppress 1.9.3 warning
+ here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
+ here.local_variable_set(:svninfo, svninfo)
+ here
+ end
end
##
@@ -416,8 +425,7 @@ class RDoc::Generator::Darkfish
search_index_rel_prefix = rel_prefix
search_index_rel_prefix += @asset_rel_path if @file_output
- # suppress 1.9.3 warning
- asset_rel_prefix = asset_rel_prefix = rel_prefix + @asset_rel_path
+ asset_rel_prefix = rel_prefix + @asset_rel_path
unless filepage_file then
if file.text? then
@@ -434,7 +442,13 @@ class RDoc::Generator::Darkfish
@title += " - #{@options.title}"
template_file ||= filepage_file
- render_template template_file, out_file do |io| binding end
+ render_template template_file, out_file do |io|
+ here = binding
+ # suppress 1.9.3 warning
+ here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
+ here.local_variable_set(:current, current)
+ here
+ end
end
rescue => e
error =
@@ -458,14 +472,19 @@ class RDoc::Generator::Darkfish
search_index_rel_prefix = rel_prefix
search_index_rel_prefix += @asset_rel_path if @file_output
- # suppress 1.9.3 warning
- current = current = file
- asset_rel_prefix = asset_rel_prefix = rel_prefix + @asset_rel_path
+ current = file
+ asset_rel_prefix = rel_prefix + @asset_rel_path
@title = "#{file.page_name} - #{@options.title}"
debug_msg " rendering #{out_file}"
- render_template template_file, out_file do |io| binding end
+ render_template template_file, out_file do |io|
+ here = binding
+ # suppress 1.9.3 warning
+ here.local_variable_set(:current, current)
+ here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
+ here
+ end
end
##
@@ -483,12 +502,16 @@ class RDoc::Generator::Darkfish
search_index_rel_prefix = rel_prefix
search_index_rel_prefix += @asset_rel_path if @file_output
- # suppress 1.9.3 warning
- asset_rel_prefix = asset_rel_prefix = ''
+ asset_rel_prefix = ''
@title = 'Not Found'
- render_template template_file do |io| binding end
+ render_template template_file do |io|
+ here = binding
+ # suppress 1.9.3 warning
+ here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
+ here
+ end
rescue => e
error = RDoc::Error.new \
"error generating servlet_not_found: #{e.message} (#{e.class})"
@@ -540,12 +563,16 @@ class RDoc::Generator::Darkfish
search_index_rel_prefix = rel_prefix
search_index_rel_prefix += @asset_rel_path if @file_output
- # suppress 1.9.3 warning
- asset_rel_prefix = asset_rel_prefix = rel_prefix + @asset_rel_path
+ asset_rel_prefix = rel_prefix + @asset_rel_path
@title = "Table of Contents - #{@options.title}"
- render_template template_file, out_file do |io| binding end
+ render_template template_file, out_file do |io|
+ here = binding
+ # suppress 1.9.3 warning
+ here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
+ here
+ end
rescue => e
error = RDoc::Error.new \
"error generating table_of_contents.html: #{e.message} (#{e.class})"
@@ -751,7 +778,11 @@ class RDoc::Generator::Darkfish
erbout = "_erbout_#{file_var}"
end
- template = klass.new template, nil, '<>', erbout
+ if RUBY_VERSION >= '2.6'
+ template = klass.new template, trim_mode: '<>', eoutvar: erbout
+ else
+ template = klass.new template, nil, '<>', erbout
+ end
@template_cache[file] = template
template
end
diff --git a/lib/rdoc/generator/json_index.rb b/lib/rdoc/generator/json_index.rb
index 931438b3c3..3a1000033d 100644
--- a/lib/rdoc/generator/json_index.rb
+++ b/lib/rdoc/generator/json_index.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'json'
begin
require 'zlib'
@@ -147,12 +147,15 @@ class RDoc::Generator::JsonIndex
JSON.dump data, io, 0
end
+ unless ENV['SOURCE_DATE_EPOCH'].nil?
+ index_file.utime index_file.atime, Time.at(ENV['SOURCE_DATE_EPOCH'].to_i).gmtime
+ end
Dir.chdir @template_dir do
Dir['**/*.js'].each do |source|
dest = File.join out_dir, source
- FileUtils.install source, dest, :mode => 0644, :verbose => $DEBUG_RDOC
+ FileUtils.install source, dest, :mode => 0644, :preserve => true, :verbose => $DEBUG_RDOC
end
end
end
@@ -161,7 +164,7 @@ class RDoc::Generator::JsonIndex
# Compress the search_index.js file using gzip
def generate_gzipped
- return unless defined?(Zlib)
+ return if @options.dry_run or not defined?(Zlib)
debug_msg "Compressing generated JSON index"
out_dir = @base_dir + @options.op_dir
@@ -170,7 +173,7 @@ class RDoc::Generator::JsonIndex
outfile = out_dir + "#{search_index_file}.gz"
debug_msg "Reading the JSON index file from %s" % search_index_file
- search_index = search_index_file.read
+ search_index = search_index_file.read(mode: 'r:utf-8')
debug_msg "Writing gzipped search index to %s" % outfile
diff --git a/lib/rdoc/generator/markup.rb b/lib/rdoc/generator/markup.rb
index 3ca423bb69..41e132450d 100644
--- a/lib/rdoc/generator/markup.rb
+++ b/lib/rdoc/generator/markup.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Handle common RDoc::Markup tasks for various CodeObjects
#
@@ -65,16 +65,6 @@ end
class RDoc::MethodAttr
- @add_line_numbers = false
-
- class << self
- ##
- # Allows controlling whether <tt>#markup_code</tt> adds line numbers to
- # the source code.
-
- attr_accessor :add_line_numbers
- end
-
##
# Prepend +src+ with line numbers. Relies on the first line of a source
# code listing having:
@@ -106,7 +96,7 @@ class RDoc::MethodAttr
##
# Turns the method's token stream into HTML.
#
- # Prepends line numbers if +add_line_numbers+ is true.
+ # Prepends line numbers if +options.line_numbers+ is true.
def markup_code
return '' unless @token_stream
@@ -126,7 +116,7 @@ class RDoc::MethodAttr
end
src.gsub!(/^#{' ' * indent}/, '') if indent > 0
- add_line_numbers(src) if RDoc::MethodAttr.add_line_numbers
+ add_line_numbers(src) if options.line_numbers
src
end
diff --git a/lib/rdoc/generator/pot.rb b/lib/rdoc/generator/pot.rb
index e2cf22d730..a12cba7505 100644
--- a/lib/rdoc/generator/pot.rb
+++ b/lib/rdoc/generator/pot.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Generates a POT file.
#
@@ -91,8 +91,8 @@ class RDoc::Generator::POT
extractor.extract
end
- autoload :MessageExtractor, 'rdoc/generator/pot/message_extractor'
- autoload :PO, 'rdoc/generator/pot/po'
- autoload :POEntry, 'rdoc/generator/pot/po_entry'
+ require 'rdoc/generator/pot/message_extractor'
+ require 'rdoc/generator/pot/po'
+ require 'rdoc/generator/pot/po_entry'
end
diff --git a/lib/rdoc/generator/pot/message_extractor.rb b/lib/rdoc/generator/pot/message_extractor.rb
index 0dd2497c26..313dfd2dc7 100644
--- a/lib/rdoc/generator/pot/message_extractor.rb
+++ b/lib/rdoc/generator/pot/message_extractor.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Extracts message from RDoc::Store
diff --git a/lib/rdoc/generator/pot/po.rb b/lib/rdoc/generator/pot/po.rb
index 60e14db831..37d45e5258 100644
--- a/lib/rdoc/generator/pot/po.rb
+++ b/lib/rdoc/generator/pot/po.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Generates a PO format text
@@ -29,8 +29,8 @@ class RDoc::Generator::POT::PO
def to_s
po = ''
sort_entries.each do |entry|
- po << "\n" unless po.empty?
- po << entry.to_s
+ po += "\n" unless po.empty?
+ po += entry.to_s
end
po
end
diff --git a/lib/rdoc/generator/pot/po_entry.rb b/lib/rdoc/generator/pot/po_entry.rb
index 515d02b48d..3c278826f4 100644
--- a/lib/rdoc/generator/pot/po_entry.rb
+++ b/lib/rdoc/generator/pot/po_entry.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A PO entry in PO
@@ -40,11 +40,11 @@ class RDoc::Generator::POT::POEntry
def to_s
entry = ''
- entry << format_translator_comment
- entry << format_extracted_comment
- entry << format_references
- entry << format_flags
- entry << <<-ENTRY
+ entry += format_translator_comment
+ entry += format_extracted_comment
+ entry += format_references
+ entry += format_flags
+ entry += <<-ENTRY
msgid #{format_message(@msgid)}
msgstr #{format_message(@msgstr)}
ENTRY
@@ -75,9 +75,9 @@ msgstr #{format_message(@msgstr)}
formatted_comment = ''
comment.each_line do |line|
- formatted_comment << "#{mark} #{line}"
+ formatted_comment += "#{mark} #{line}"
end
- formatted_comment << "\n" unless formatted_comment.end_with?("\n")
+ formatted_comment += "\n" unless formatted_comment.end_with?("\n")
formatted_comment
end
@@ -94,7 +94,7 @@ msgstr #{format_message(@msgstr)}
formatted_references = ''
@references.sort.each do |file, line|
- formatted_references << "\#: #{file}:#{line}\n"
+ formatted_references += "\#: #{file}:#{line}\n"
end
formatted_references
end
@@ -111,8 +111,8 @@ msgstr #{format_message(@msgstr)}
formatted_message = '""'
message.each_line do |line|
- formatted_message << "\n"
- formatted_message << "\"#{escape(line)}\""
+ formatted_message += "\n"
+ formatted_message += "\"#{escape(line)}\""
end
formatted_message
end
diff --git a/lib/rdoc/generator/ri.rb b/lib/rdoc/generator/ri.rb
index 830777e587..0eef1d03f5 100644
--- a/lib/rdoc/generator/ri.rb
+++ b/lib/rdoc/generator/ri.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Generates ri data files
diff --git a/lib/rdoc/generator/template/darkfish/_footer.rhtml b/lib/rdoc/generator/template/darkfish/_footer.rhtml
index 7c4debd1f7..9791b42901 100644
--- a/lib/rdoc/generator/template/darkfish/_footer.rhtml
+++ b/lib/rdoc/generator/template/darkfish/_footer.rhtml
@@ -1,5 +1,5 @@
<footer id="validator-badges" role="contentinfo">
- <p><a href="http://validator.w3.org/check/referer">Validate</a>
- <p>Generated by <a href="https://rdoc.github.io/rdoc">RDoc</a> <%= RDoc::VERSION %>.
+ <p><a href="https://validator.w3.org/check/referer">Validate</a>
+ <p>Generated by <a href="https://ruby.github.io/rdoc/">RDoc</a> <%= RDoc::VERSION %>.
<p>Based on <a href="http://deveiate.org/projects/Darkfish-RDoc/">Darkfish</a> by <a href="http://deveiate.org">Michael Granger</a>.
</footer>
diff --git a/lib/rdoc/generator/template/darkfish/_head.rhtml b/lib/rdoc/generator/template/darkfish/_head.rhtml
index f308526823..8304310d4b 100644
--- a/lib/rdoc/generator/template/darkfish/_head.rhtml
+++ b/lib/rdoc/generator/template/darkfish/_head.rhtml
@@ -7,8 +7,11 @@
var index_rel_prefix = "<%= rel_prefix %>/";
</script>
-<script src="<%= asset_rel_prefix %>/js/jquery.js"></script>
-<script src="<%= asset_rel_prefix %>/js/darkfish.js"></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>
<link href="<%= asset_rel_prefix %>/css/fonts.css" rel="stylesheet">
<link href="<%= asset_rel_prefix %>/css/rdoc.css" rel="stylesheet">
diff --git a/lib/rdoc/generator/template/darkfish/class.rhtml b/lib/rdoc/generator/template/darkfish/class.rhtml
index b497000112..7733095086 100644
--- a/lib/rdoc/generator/template/darkfish/class.rhtml
+++ b/lib/rdoc/generator/template/darkfish/class.rhtml
@@ -26,8 +26,6 @@
</section>
<% klass.each_section do |section, constants, attributes| %>
- <% constants = constants.select { |const| const.display? } %>
- <% attributes = attributes.select { |attr| attr.display? } %>
<section id="<%= section.aref %>" class="documentation-section">
<% if section.title then %>
<header class="documentation-section-title">
@@ -116,7 +114,7 @@
<% else %>
<div class="method-heading">
<span class="method-name"><%= h method.name %></span><span
- class="method-args"><%= method.param_seq %></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 %>
diff --git a/lib/rdoc/generator/template/darkfish/css/rdoc.css b/lib/rdoc/generator/template/darkfish/css/rdoc.css
index 2f4dca7e08..1bdb6e6223 100644
--- a/lib/rdoc/generator/template/darkfish/css/rdoc.css
+++ b/lib/rdoc/generator/template/darkfish/css/rdoc.css
@@ -9,6 +9,8 @@
/* vim: ft=css et sw=2 ts=2 sts=2 */
/* Base Green is: #6C8C22 */
+.hide { display: none !important; }
+
* { padding: 0; margin: 0; }
body {
@@ -48,6 +50,16 @@ h6:hover span {
display: inline;
}
+h1:target,
+h2:target,
+h3:target,
+h4:target,
+h5:target,
+h6:target {
+ margin-left: -10px;
+ border-left: 10px solid #f1edba;
+}
+
:link,
:visited {
color: #6C8C22;
@@ -441,7 +453,16 @@ main header h3 {
/* @group Method Details */
main .method-source-code {
- display: none;
+ max-height: 0;
+ overflow: hidden;
+ transition-duration: 200ms;
+ transition-delay: 0ms;
+ transition-property: all;
+ transition-timing-function: ease-in-out;
+}
+
+main .method-source-code.active-menu {
+ max-height: 100vh;
}
main .method-description .method-calls-super {
diff --git a/lib/rdoc/generator/template/darkfish/js/darkfish.js b/lib/rdoc/generator/template/darkfish/js/darkfish.js
index 38f877ed40..111bbf8eb9 100644
--- a/lib/rdoc/generator/template/darkfish/js/darkfish.js
+++ b/lib/rdoc/generator/template/darkfish/js/darkfish.js
@@ -8,6 +8,7 @@
*/
/* Provide console simulation for firebug-less environments */
+/*
if (!("console" in window) || !("firebug" in console)) {
var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
"group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
@@ -16,41 +17,35 @@ if (!("console" in window) || !("firebug" in console)) {
for (var i = 0; i < names.length; ++i)
window.console[names[i]] = function() {};
};
-
-
-/**
- * Unwrap the first element that matches the given @expr@ from the targets and return them.
- */
-$.fn.unwrap = function( expr ) {
- return this.each( function() {
- $(this).parents( expr ).eq( 0 ).after( this ).remove();
- });
-};
+*/
function showSource( e ) {
var target = e.target;
- var codeSections = $(target).
- parents('.method-detail').
- find('.method-source-code');
-
- $(target).
- parents('.method-detail').
- find('.method-source-code').
- slideToggle();
+ while (!target.classList.contains('method-detail')) {
+ target = target.parentNode;
+ }
+ if (typeof target !== "undefined" && target !== null) {
+ target = target.querySelector('.method-source-code');
+ }
+ if (typeof target !== "undefined" && target !== null) {
+ target.classList.toggle('active-menu')
+ }
};
function hookSourceViews() {
- $('.method-heading').click( showSource );
+ document.querySelectorAll('.method-heading').forEach(function (codeObject) {
+ codeObject.addEventListener('click', showSource);
+ });
};
function hookSearch() {
- var input = $('#search-field').eq(0);
- var result = $('#search-results').eq(0);
- $(result).show();
+ var input = document.querySelector('#search-field');
+ var result = document.querySelector('#search-results');
+ result.classList.remove("initially-hidden");
- var search_section = $('#search-section').get(0);
- $(search_section).show();
+ var search_section = document.querySelector('#search-section');
+ search_section.classList.remove("initially-hidden");
var search = new Search(search_data, input, result);
@@ -77,85 +72,13 @@ function hookSearch() {
}
search.select = function(result) {
- var result_element = result.get(0);
- window.location.href = result_element.firstChild.firstChild.href;
+ window.location.href = result.firstChild.firstChild.href;
}
search.scrollIntoView = search.scrollInWindow;
};
-function highlightTarget( anchor ) {
- console.debug( "Highlighting target '%s'.", anchor );
-
- $("a[name]").each( function() {
- if ( $(this).attr("name") == anchor ) {
- if ( !$(this).parent().parent().hasClass('target-section') ) {
- console.debug( "Wrapping the target-section" );
- $('div.method-detail').unwrap( 'div.target-section' );
- $(this).parent().wrap( '<div class="target-section"></div>' );
- } else {
- console.debug( "Already wrapped." );
- }
- }
- });
-};
-
-function highlightLocationTarget() {
- console.debug( "Location hash: %s", window.location.hash );
- if ( ! window.location.hash || window.location.hash.length == 0 ) return;
-
- var anchor = window.location.hash.substring(1);
- console.debug( "Found anchor: %s; matching %s", anchor, "a[name=" + anchor + "]" );
-
- highlightTarget( anchor );
-};
-
-function highlightClickTarget( event ) {
- console.debug( "Highlighting click target for event %o", event.target );
- try {
- var anchor = $(event.target).attr( 'href' ).substring(1);
- console.debug( "Found target anchor: %s", anchor );
- highlightTarget( anchor );
- } catch ( err ) {
- console.error( "Exception while highlighting: %o", err );
- };
-};
-
-function loadAsync(path, success, prefix) {
- $.ajax({
- url: prefix + path,
- dataType: 'script',
- success: success,
- cache: true
- });
-};
-
-$(document).ready( function() {
+document.addEventListener('DOMContentLoaded', function() {
hookSourceViews();
- highlightLocationTarget();
- $('ul.link-list a').bind( "click", highlightClickTarget );
-
- var search_scripts_loaded = {
- navigation_loaded: false,
- search_loaded: false,
- search_index_loaded: false,
- searcher_loaded: false,
- }
-
- var search_success_function = function(variable) {
- return (function (data, status, xhr) {
- search_scripts_loaded[variable] = true;
-
- if (search_scripts_loaded['navigation_loaded'] == true &&
- search_scripts_loaded['search_loaded'] == true &&
- search_scripts_loaded['search_index_loaded'] == true &&
- search_scripts_loaded['searcher_loaded'] == true)
- hookSearch();
- });
- }
-
- loadAsync('js/navigation.js', search_success_function('navigation_loaded'), rdoc_rel_prefix);
- loadAsync('js/search.js', search_success_function('search_loaded'), rdoc_rel_prefix);
- loadAsync('js/search_index.js', search_success_function('search_index_loaded'), index_rel_prefix);
- loadAsync('js/searcher.js', search_success_function('searcher_loaded'), rdoc_rel_prefix);
+ hookSearch();
});
diff --git a/lib/rdoc/generator/template/darkfish/js/jquery.js b/lib/rdoc/generator/template/darkfish/js/jquery.js
deleted file mode 100644
index 628ed9b316..0000000000
--- a/lib/rdoc/generator/template/darkfish/js/jquery.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! jQuery v1.6.4 http://jquery.com/ | http://jquery.org/license */
-(function(a,b){function cu(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cr(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cq(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cp(){cn=b}function co(){setTimeout(cp,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function bZ(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function bY(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bA.test(a)?d(a,e):bY(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)bY(a+"["+e+"]",b[e],c,d);else d(a,b)}function bX(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bW(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bP,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bW(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bW(a,c,d,e,"*",g));return l}function bV(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bL),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function by(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bt:bu;if(d>0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bv(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bl(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bd,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bk(a){f.nodeName(a,"input")?bj(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bj)}function bj(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bi(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bh(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bg(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i<j;i++)f.event.add(b,h+(g[h][i].namespace?".":"")+g[h][i].namespace,g[h][i],g[h][i].data)}}}}function bf(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function V(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(Q.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function U(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function M(a,b){return(a&&a!=="*"?a+".":"")+b.replace(y,"`").replace(z,"&")}function L(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;i<s.length;i++)g=s[i],g.origType.replace(w,"")===a.type?q.push(g.selector):s.splice(i--,1);e=f(a.target).closest(q,a.currentTarget);for(j=0,k=e.length;j<k;j++){m=e[j];for(i=0;i<s.length;i++){g=s[i];if(m.selector===g.selector&&(!n||n.test(g.namespace))&&!m.elem.disabled){h=m.elem,d=null;if(g.preType==="mouseenter"||g.preType==="mouseleave")a.type=g.preType,d=f(a.relatedTarget).closest(g.selector)[0],d&&f.contains(h,d)&&(d=h);(!d||d!==h)&&p.push({elem:h,handleObj:g,level:m.level})}}}for(j=0,k=p.length;j<k;j++){e=p[j];if(c&&e.level>c)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function J(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function D(){return!0}function C(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function K(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(K,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z]|[0-9])/ig,x=/^-ms-/,y=function(a,b){return(b+"").toUpperCase()},z=d.userAgent,A,B,C,D=Object.prototype.toString,E=Object.prototype.hasOwnProperty,F=Array.prototype.push,G=Array.prototype.slice,H=String.prototype.trim,I=Array.prototype.indexOf,J={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.4",length:0,size:function(){return this.length},toArray:function(){return G.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?F.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),B.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(G.apply(this,arguments),"slice",G.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:F,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;B.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!B){B=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",C,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",C),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&K()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):J[D.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!E.call(a,"constructor")&&!E.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||E.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(x,"ms-").replace(w,y)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:H?function(a){return a==null?"":H.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?F.call(c,a):e.merge(c,a)}return c},inArray:function(a,b){if(!b)return-1;if(I)return I.call(b,a);for(var c=0,d=b.length;c<d;c++)if(b[c]===a)return c;return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=G.call(arguments,2),g=function(){return a.apply(c,f.concat(G.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=s.exec(a)||t.exec(a)||u.exec(a)||a.indexOf("compatible")<0&&v.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){J["[object "+b+"]"]=b.toLowerCase()}),A=e.uaMatch(z),A.browser&&(e.browser[A.browser]=!0,e.browser.version=A.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?C=function(){c.removeEventListener("DOMContentLoaded",C,!1),e.ready()}:c.attachEvent&&(C=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",C),e.ready())});return e}(),g="done fail isResolved isRejected promise then always pipe".split(" "),h=[].slice;f.extend({_Deferred:function(){var a=[],b,c,d,e={done:function(){if(!d){var c=arguments,g,h,i,j,k;b&&(k=b,b=0);for(g=0,h=c.length;g<h;g++)i=c[g],j=f.type(i),j==="array"?e.done.apply(e,i):j==="function"&&a.push(i);k&&e.resolveWith(k[0],k[1])}return this},resolveWith:function(e,f){if(!d&&!b&&!c){f=f||[],c=1;try{while(a[0])a.shift().apply(e,f)}finally{b=[e,f],c=0}}return this},resolve:function(){e.resolveWith(this,arguments);return this},isResolved:function(){return!!c||!!b},cancel:function(){d=1,a=[];return this}};return e},Deferred:function(a){var b=f._Deferred(),c=f._Deferred(),d;f.extend(b,{then:function(a,c){b.done(a).fail(c);return this},always:function(){return b.done.apply(b,arguments).fail.apply(this,arguments)},fail:c.done,rejectWith:c.resolveWith,reject:c.resolve,isRejected:c.isResolved,pipe:function(a,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[c,"reject"]},function(a,c){var e=c[0],g=c[1],h;f.isFunction(e)?b[a](function(){h=e.apply(this,arguments),h&&f.isFunction(h.promise)?h.promise().then(d.resolve,d.reject):d[g+"With"](this===b?d:this,[h])}):b[a](d[g])})}).promise()},promise:function(a){if(a==null){if(d)return d;d=a={}}var c=g.length;while(c--)a[g[c]]=b[g[c]];return a}}),b.done(c.cancel).fail(b.cancel),delete b.cancel,a&&a.call(b,b);return b},when:function(a){function i(a){return function(c){b[a]=arguments.length>1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c<d;c++)b[c]&&f.isFunction(b[c].promise)?b[c].promise().then(i(c),g.reject):--e;e||g.resolveWith(g,b)}else g!==a&&g.resolveWith(g,d?[a]:[]);return g.promise()}}),f.support=function(){var a=c.createElement("div"),b=c.documentElement,d,e,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;a.setAttribute("className","t"),a.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},m&&f.extend(p,{position:"absolute",left:"-1000px",top:"-1000px"});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="<div style='width:4px;'></div>",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0),o.innerHTML="",n.removeChild(o);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i=f.expando,j=typeof c=="string",k=a.nodeType,l=k?f.cache:a,m=k?a[f.expando]:a[f.expando]&&f.expando;if((!m||e&&m&&l[m]&&!l[m][i])&&j&&d===b)return;m||(k?a[f.expando]=m=++f.uuid:m=f.expando),l[m]||(l[m]={},k||(l[m].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?l[m][i]=f.extend(l[m][i],c):l[m]=f.extend(l[m],c);g=l[m],e&&(g[i]||(g[i]={}),g=g[i]),d!==b&&(g[f.camelCase(c)]=d);if(c==="events"&&!g[c])return g[i]&&g[i].events;j?(h=g[c],h==null&&(h=g[f.camelCase(c)])):h=g;return h}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e=f.expando,g=a.nodeType,h=g?f.cache:a,i=g?a[f.expando]:f.expando;if(!h[i])return;if(b){d=c?h[i][e]:h[i];if(d){d[b]||(b=f.camelCase(b)),delete d[b];if(!l(d))return}}if(c){delete h[i][e];if(!l(h[i]))return}var j=h[i][e];f.support.deleteExpando||!h.setInterval?delete h[i]:h[i]=null,j?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=j):g&&(f.support.deleteExpando?delete a[f.expando]:a.removeAttribute?a.removeAttribute(f.expando):a[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h<i;h++)g=e[h].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),k(this[0],g,d[g]))}}return d}if(typeof a=="object")return this.each(function(){f.data(this,a)});var j=a.split(".");j[1]=j[1]?"."+j[1]:"";if(c===b){d=this.triggerHandler("getData"+j[1]+"!",[j[0]]),d===b&&this.length&&(d=f.data(this[0],a),d=k(this[0],a,d));return d===b&&j[1]?this.data(j[0]):d}return this.each(function(){var b=f(this),d=[j[0],c];b.triggerHandler("setData"+j[1]+"!",d),f.data(this,a,c),b.triggerHandler("changeData"+j[1]+"!",d)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,c){a&&(c=(c||"fx")+"mark",f.data(a,c,(f.data(a,c,b,!0)||0)+1,!0))},_unmark:function(a,c,d){a!==!0&&(d=c,c=a,a=!1);if(c){d=d||"fx";var e=d+"mark",g=a?0:(f.data(c,e,b,!0)||1)-1;g?f.data(c,e,g,!0):(f.removeData(c,e,!0),m(c,d,"mark"))}},queue:function(a,c,d){if(a){c=(c||"fx")+"queue";var e=f.data(a,c,b,!0);d&&(!e||f.isArray(d)?e=f.data(a,c,f.makeArray(d),!0):e.push(d));return e||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e;d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),d.call(a,function(){f.dequeue(a,b)})),c.length||(f.removeData(a,b+"queue",!0),m(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(){var c=this;setTimeout(function(){f.dequeue(c,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f._Deferred(),!0))h++,l.done(m);m();return d.promise()}});var n=/[\n\t\r]/g,o=/\s+/,p=/\r/g,q=/^(?:button|input)$/i,r=/^(?:button|input|object|select|textarea)$/i,s=/^a(?:rea)?$/i,t=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,u,v;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(o);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(o);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(n," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(o);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ";for(var c=0,d=this.length;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(n," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;d=e.value;return typeof d=="string"?d.replace(p,""):d==null?"":d}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h<i;h++){var j=e[h];if(j.selected&&(f.support.optDisabled?!j.disabled:j.getAttribute("disabled")===null)&&(!j.parentNode.disabled||!f.nodeName(j.parentNode,"optgroup"))){b=f(j).val();if(g)return b;d.push(b)}}if(g&&!d.length&&e.length)return f(e[c]).val();return d},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);j&&(c=f.attrFix[c]||c,i=f.attrHooks[c],i||(t.test(c)?i=v:u&&(i=u)));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j&&(h=i.get(a,c))!==null)return h;h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.attr(a,b,""),a.removeAttribute(b),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(u&&f.nodeName(a,"button"))return u.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(u&&f.nodeName(a,"button"))return u.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);i&&(c=f.propFix[c]||c,h=f.propHooks[c]);return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==null?g:a[c]},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabIndex=f.propHooks.tabIndex,v={get:function(a,c){var d;return f.prop(a,c)===!0||(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},f.support.getSetAttribute||(u=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var w=/\.(.*)$/,x=/^(?:textarea|input|select)$/i,y=/\./g,z=/ /g,A=/[^\w\s.|`]/g,B=function(a){return a.replace(A,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=C;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=C);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),B).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j<p.length;j++){q=p[j];if(l||n.test(q.namespace))f.event.remove(a,r,q.handler,j),p.splice(j--,1)}continue}o=f.event.special[h]||{};for(j=e||0;j<p.length;j++){q=p[j];if(d.guid===q.guid){if(l||n.test(q.namespace))e==null&&p.splice(j--,1),o.remove&&o.remove.call(a,q);if(e!=null)break}}if(p.length===0||e!=null&&p.length===1)(!o.teardown||o.teardown.call(a,m)===!1)&&f.removeEvent(a,h,s.handle),g=null,delete
-t[h]}if(f.isEmptyObject(t)){var u=s.handle;u&&(u.elem=null),delete s.events,delete s.handle,f.isEmptyObject(s)&&f.removeData(a,b,!0)}}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){var h=c.type||c,i=[],j;h.indexOf("!")>=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h<i;h++){var j=d[h];if(e||c.namespace_re.test(j.namespace)){c.handler=j.handler,c.data=j.data,c.handleObj=j;var k=j.handler.apply(this,g);k!==b&&(c.result=k,k===!1&&(c.preventDefault(),c.stopPropagation()));if(c.isImmediatePropagationStopped())break}}return c.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a){if(a[f.expando])return a;var d=a;a=f.Event(d);for(var e=this.props.length,g;e;)g=this.props[--e],a[g]=d[g];a.target||(a.target=a.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),!a.relatedTarget&&a.fromElement&&(a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement);if(a.pageX==null&&a.clientX!=null){var h=a.target.ownerDocument||c,i=h.documentElement,j=h.body;a.pageX=a.clientX+(i&&i.scrollLeft||j&&j.scrollLeft||0)-(i&&i.clientLeft||j&&j.clientLeft||0),a.pageY=a.clientY+(i&&i.scrollTop||j&&j.scrollTop||0)-(i&&i.clientTop||j&&j.clientTop||0)}a.which==null&&(a.charCode!=null||a.keyCode!=null)&&(a.which=a.charCode!=null?a.charCode:a.keyCode),!a.metaKey&&a.ctrlKey&&(a.metaKey=a.ctrlKey),!a.which&&a.button!==b&&(a.which=a.button&1?1:a.button&2?3:a.button&4?2:0);return a},guid:1e8,proxy:f.proxy,special:{ready:{setup:f.bindReady,teardown:f.noop},live:{add:function(a){f.event.add(this,M(a.origType,a.selector),f.extend({},a,{handler:L,guid:a.handler.guid}))},remove:function(a){f.event.remove(this,M(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}}},f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!this.preventDefault)return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?D:C):this.type=a,b&&f.extend(this,b),this.timeStamp=f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=D;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=D;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=D,this.stopPropagation()},isDefaultPrevented:C,isPropagationStopped:C,isImmediatePropagationStopped:C};var E=function(a){var b=a.relatedTarget,c=!1,d=a.type;a.type=a.data,b!==this&&(b&&(c=f.contains(this,b)),c||(f.event.handle.apply(this,arguments),a.type=d))},F=function(a){a.type=a.data,f.event.handle.apply(this,arguments)};f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={setup:function(c){f.event.add(this,b,c&&c.selector?F:E,a)},teardown:function(a){f.event.remove(this,b,a&&a.selector?F:E)}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(a,b){if(!f.nodeName(this,"form"))f.event.add(this,"click.specialSubmit",function(a){var b=a.target,c=f.nodeName(b,"input")||f.nodeName(b,"button")?b.type:"";(c==="submit"||c==="image")&&f(b).closest("form").length&&J("submit",this,arguments)}),f.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,c=f.nodeName(b,"input")||f.nodeName(b,"button")?b.type:"";(c==="text"||c==="password")&&f(b).closest("form").length&&a.keyCode===13&&J("submit",this,arguments)});else return!1},teardown:function(a){f.event.remove(this,".specialSubmit")}});if(!f.support.changeBubbles){var G,H=function(a){var b=f.nodeName(a,"input")?a.type:"",c=a.value;b==="radio"||b==="checkbox"?c=a.checked:b==="select-multiple"?c=a.selectedIndex>-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},I=function(c){var d=c.target,e,g;if(!!x.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=H(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:I,beforedeactivate:I,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&I.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&I.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",H(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in G)f.event.add(this,c+".specialChange",G[c]);return x.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return x.test(this.nodeName)}},G=f.event.special.change.filters,G.focus=G.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i<j;i++)f.event.add(this[i],a,g,d);return this}}),f.fn.extend({unbind:function(a,b){if(typeof a=="object"&&!a.preventDefault)for(var c in a)this.unbind(c,a[c]);else for(var d=0,e=this.length;d<e;d++)f.event.remove(this[d],a,b);return this},delegate:function(a,b,c,d){return this.live(b,c,d,a)},undelegate:function(a,b,c){return arguments.length===0?this.unbind("live"):this.die(b,null,c,a)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f.data(this,"lastToggle"+a.guid)||0)%d;f.data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var K={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};f.each(["live","die"],function(a,c){f.fn[c]=function(a,d,e,g){var h,i=0,j,k,l,m=g||this.selector,n=g?this:f(this.context);if(typeof a=="object"&&!a.preventDefault){for(var o in a)n[c](o,d,a[o],m);return this}if(c==="die"&&!a&&g&&g.charAt(0)==="."){n.unbind(g);return this}if(d===!1||f.isFunction(d))e=d||C,d=b;a=(a||"").split(" ");while((h=a[i++])!=null){j=w.exec(h),k="",j&&(k=j[0],h=h.replace(w,""));if(h==="hover"){a.push("mouseenter"+k,"mouseleave"+k);continue}l=h,K[h]?(a.push(K[h]+k),h=h+k):h=(K[h]||h)+k;if(c==="live")for(var p=0,q=n.length;p<q;p++)f.event.add(n[p],"live."+M(h,m),{data:d,selector:m,handler:e,origType:h,origHandler:e,preType:l});else n.unbind("live."+M(h,m),e)}return this}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}if(i.nodeType===1){f||(i.sizcache=c,i.sizset=g);if(typeof b!="string"){if(i===b){j=!0;break}}else if(k.filter(b,[i]).length>0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}i.nodeType===1&&!f&&(i.sizcache=c,i.sizset=g);if(i.nodeName.toLowerCase()===b){j=i;break}i=i[a]}d[g]=j}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},k.matches=function(a,b){return k(a,null,null,b)},k.matchesSelector=function(a,b){return k(b,null,null,[a]).length>0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e<f;e++){var g,h=l.order[e];if(g=l.leftMatch[h].exec(a)){var j=g[1];g.splice(1,1);if(j.substr(j.length-1)!=="\\"){g[1]=(g[1]||"").replace(i,""),d=l.find[h](g,b,c);if(d!=null){a=a.replace(l.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},k.filter=function(a,c,d,e){var f,g,h=a,i=[],j=c,m=c&&c[0]&&k.isXML(c[0]);while(a&&c.length){for(var n in l.filter)if((f=l.leftMatch[n].exec(a))!=null&&f[2]){var o,p,q=l.filter[n],r=f[1];g=!1,f.splice(1,1);if(r.substr(r.length-1)==="\\")continue;j===i&&(i=[]);if(l.preFilter[n]){f=l.preFilter[n](f,j,d,i,e,m);if(!f)g=o=!0;else if(f===!0)continue}if(f)for(var s=0;(p=j[s])!=null;s++)if(p){o=q(p,f,s,j);var t=e^!!o;d&&o!=null?t?g=!0:j[s]=!1:t&&(i.push(p),g=!0)}if(o!==b){d||(j=i),a=a.replace(l.match[n],"");if(!g)return[];break}}if(a===h)if(g==null)k.error(a);else break;h=a}return j},k.error=function(a){throw"Syntax error, unrecognized expression: "+a};var l=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!j.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&k.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&k.filter(b,a,!0)}},"":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("parentNode",b,f,a,e,c)},"~":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("previousSibling",b,f,a,e,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(i,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}k.error(e)},CHILD:function(a,b){var c=b[1],d=a;switch(c){case"only":case"first":while(d=d.previousSibling)if(d.nodeType===1)return!1;if(c==="first")return!0;d=a;case"last":while(d=d.nextSibling)if(d.nodeType===1)return!1;return!0;case"nth":var e=b[2],f=b[3];if(e===1&&f===0)return!0;var g=b[0],h=a.parentNode;if(h&&(h.sizcache!==g||!a.nodeIndex)){var i=0;for(d=h.firstChild;d;d=d.nextSibling)d.nodeType===1&&(d.nodeIndex=++i);h.sizcache=g}var j=a.nodeIndex-f;return e===0?j===0:j%e===0&&j/e>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c<f;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var r,s;c.documentElement.compareDocumentPosition?r=function(a,b){if(a===b){g=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(r=function(a,b){if(a===b){g=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],h=a.parentNode,i=b.parentNode,j=h;if(h===i)return s(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return s(e[k],f[k]);return k===c?s(a,f[k],-1):s(e[k],b,1)},s=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),k.getText=function(a){var b="",c;for(var d=0;a[d];d++)c=a[d],c.nodeType===3||c.nodeType===4?b+=c.nodeValue:c.nodeType!==8&&(b+=k.getText(c.childNodes));return b},function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g<h;g++)k(a,f[g],d);return k.filter(e,d)};f.find=k,f.expr=k.selectors,f.expr[":"]=f.expr.filters,f.unique=k.uniqueSort,f.text=k.getText,f.isXMLDoc=k.isXML,f.contains=k.contains}();var N=/Until$/,O=/^(?:parents|prevUntil|prevAll)/,P=/,/,Q=/^.[^:#\[\.,]*$/,R=Array.prototype.slice,S=f.expr.match.POS,T={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(V(this,a,!1),"not",a)},filter:function(a){return this.pushStack(V(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d<e;d++)i=a[d],j[i]||(j[i]=S.test(i)?f(i,b||this.context):i);while(g&&g.ownerDocument&&g!==b){for(i in j)h=j[i],(h.jquery?h.index(g)>-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=S.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(l?l.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(U(c[0])||U(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=R.call(arguments);N.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!T[a]?f.unique(e):e,(this.length>1||P.test(d))&&O.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|object|embed|option|style)/i,bb=/checked\s*(?:[^=]|=\s*.checked.)/i,bc=/\/(java|ecma)script/i,bd=/^\s*<!(?:\[CDATA\[|\-\-)/,be={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};be.optgroup=be.option,be.tbody=be.tfoot=be.colgroup=be.caption=be.thead,be.th=be.td,f.support.htmlSerialize||(be._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!be[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bb.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bf(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bl)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i;b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof a[0]=="string"&&a[0].length<512&&i===c&&a[0].charAt(0)==="<"&&!ba.test(a[0])&&(f.support.checkClone||!bb.test(a[0]))&&(g=!0,h=f.fragments[a[0]],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean
-(a,i,e,d)),g&&(f.fragments[a[0]]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bh(a,d),e=bi(a),g=bi(d);for(h=0;e[h];++h)g[h]&&bh(e[h],g[h])}if(b){bg(a,d);if(c){e=bi(a),g=bi(d);for(h=0;e[h];++h)bg(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1></$2>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=be[l]||be._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bk(k[i]);else bk(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||bc.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.expando,g=f.event.special,h=f.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&f.noData[j.nodeName.toLowerCase()])continue;c=j[f.expando];if(c){b=d[c]&&d[c][e];if(b&&b.events){for(var k in b.events)g[k]?f.event.remove(j,k):f.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[f.expando]:j.removeAttribute&&j.removeAttribute(f.expando),delete d[c]}}}});var bm=/alpha\([^)]*\)/i,bn=/opacity=([^)]*)/,bo=/([A-Z]|^ms)/g,bp=/^-?\d+(?:px)?$/i,bq=/^-?\d/,br=/^([\-+])=([\-+.\de]+)/,bs={position:"absolute",visibility:"hidden",display:"block"},bt=["Left","Right"],bu=["Top","Bottom"],bv,bw,bx;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bv(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=br.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bv)return bv(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return by(a,b,d);f.swap(a,bs,function(){e=by(a,b,d)});return e}},set:function(a,b){if(!bp.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bn.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bm,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bm.test(g)?g.replace(bm,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bv(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bw=function(a,c){var d,e,g;c=c.replace(bo,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bx=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bp.test(d)&&bq.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bv=bw||bx,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bz=/%20/g,bA=/\[\]$/,bB=/\r?\n/g,bC=/#.*$/,bD=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bE=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bF=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bG=/^(?:GET|HEAD)$/,bH=/^\/\//,bI=/\?/,bJ=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bK=/^(?:select|textarea)/i,bL=/\s+/,bM=/([?&])_=[^&]*/,bN=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bO=f.fn.load,bP={},bQ={},bR,bS,bT=["*/"]+["*"];try{bR=e.href}catch(bU){bR=c.createElement("a"),bR.href="",bR=bR.href}bS=bN.exec(bR.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bO)return bO.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bJ,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bK.test(this.nodeName)||bE.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bB,"\r\n")}}):{name:b.name,value:c.replace(bB,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?bX(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),bX(a,b);return a},ajaxSettings:{url:bR,isLocal:bF.test(bS[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bT},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bV(bP),ajaxTransport:bV(bQ),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?bZ(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=b$(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bD.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bC,"").replace(bH,bS[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bL),d.crossDomain==null&&(r=bN.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bS[1]&&r[2]==bS[2]&&(r[3]||(r[1]==="http:"?80:443))==(bS[3]||(bS[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bW(bP,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bG.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bI.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bM,"$1_="+x);d.url=y+(y===d.url?(bI.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bT+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bW(bQ,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){s<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)bY(g,a[g],c,e);return d.join("&").replace(bz,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var b_=f.now(),ca=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+b_++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ca.test(b.url)||e&&ca.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ca,l),b.url===j&&(e&&(k=k.replace(ca,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cb=a.ActiveXObject?function(){for(var a in cd)cd[a](0,1)}:!1,cc=0,cd;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ce()||cf()}:ce,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cb&&delete cd[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cc,cb&&(cd||(cd={},f(a).unload(cb)),cd[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cg={},ch,ci,cj=/^(?:toggle|show|hide)$/,ck=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cl,cm=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cn;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cq("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cr(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cq("hide",3),a,b,c);for(var d=0,e=this.length;d<e;d++)if(this[d].style){var g=f.css(this[d],"display");g!=="none"&&!f._data(this[d],"olddisplay")&&f._data(this[d],"olddisplay",g)}for(d=0;d<e;d++)this[d].style&&(this[d].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cq("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return this[e.queue===!1?"each":"queue"](function(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(f.support.inlineBlockNeedsLayout?(j=cr(this.nodeName),j==="inline"?this.style.display="inline-block":(this.style.display="inline",this.style.zoom=1)):this.style.display="inline-block"))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)k=new f.fx(this,b,i),h=a[i],cj.test(h)?k[h==="toggle"?d?"show":"hide":h]():(l=ck.exec(h),m=k.cur(),l?(n=parseFloat(l[2]),o=l[3]||(f.cssNumber[i]?"":"px"),o!=="px"&&(f.style(this,i,(n||1)+o),m=(n||1)/k.cur()*m,f.style(this,i,m+o)),l[1]&&(n=(l[1]==="-="?-1:1)*n+m),k.custom(m,n,o)):k.custom(m,h,""));return!0})},stop:function(a,b){a&&this.queue([]),this.each(function(){var a=f.timers,c=a.length;b||f._unmark(!0,this);while(c--)a[c].elem===this&&(b&&a[c](!0),a.splice(c,1))}),b||this.dequeue();return this}}),f.each({slideDown:cq("show",1),slideUp:cq("hide",1),slideToggle:cq("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default,d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue!==!1?f.dequeue(this):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function g(a){return d.step(a)}var d=this,e=f.fx;this.startTime=cn||co(),this.start=a,this.end=b,this.unit=c||this.unit||(f.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,g.elem=this.elem,g()&&f.timers.push(g)&&!cl&&(cl=setInterval(e.tick,e.interval))},show:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=cn||co(),c=!0,d=this.elem,e=this.options,g,h;if(a||b>=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b<a.length;++b)a[b]()||a.splice(b--,1);a.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cl),cl=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit:a.elem[a.prop]=a.now}}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cs=/^t(?:able|d|h)$/i,ct=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cu(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);f.offset.initialize();var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.offset.supportsFixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.offset.doesNotAddBorder&&(!f.offset.doesAddBorderForTableAndCells||!cs.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.offset.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.offset.supportsFixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={initialize:function(){var a=c.body,b=c.createElement("div"),d,e,g,h,i=parseFloat(f.css(a,"marginTop"))||0,j="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=ct.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!ct.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cu(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cu(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a&&a.style?parseFloat(f.css(a,d,"padding")):null},f.fn["outer"+c]=function(a){var b=this[0];return b&&b.style?parseFloat(f.css(b,d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNaN(j)?i:j}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file
diff --git a/lib/rdoc/generator/template/darkfish/js/search.js b/lib/rdoc/generator/template/darkfish/js/search.js
index 60ac295e6c..b558ca5b4f 100644
--- a/lib/rdoc/generator/template/darkfish/js/search.js
+++ b/lib/rdoc/generator/template/darkfish/js/search.js
@@ -1,29 +1,29 @@
Search = function(data, input, result) {
this.data = data;
- this.$input = $(input);
- this.$result = $(result);
+ this.input = input;
+ this.result = result;
- this.$current = null;
- this.$view = this.$result.parent();
+ this.current = null;
+ this.view = this.result.parentNode;
this.searcher = new Searcher(data.index);
this.init();
}
-Search.prototype = $.extend({}, Navigation, new function() {
+Search.prototype = Object.assign({}, Navigation, new function() {
var suid = 1;
this.init = function() {
var _this = this;
var observer = function(e) {
- switch(e.originalEvent.keyCode) {
+ switch(e.keyCode) {
case 38: // Event.KEY_UP
case 40: // Event.KEY_DOWN
return;
}
- _this.search(_this.$input[0].value);
+ _this.search(_this.input.value);
};
- this.$input.keyup(observer);
- this.$input.click(observer); // mac's clear field
+ this.input.addEventListener('keyup', observer);
+ this.input.addEventListener('click', observer); // mac's clear field
this.searcher.ready(function(results, isLast) {
_this.addResults(results, isLast);
@@ -34,7 +34,7 @@ Search.prototype = $.extend({}, Navigation, new function() {
}
this.search = function(value, selectFirstMatch) {
- value = jQuery.trim(value).toLowerCase();
+ value = value.trim().toLowerCase();
if (value) {
this.setNavigationActive(true);
} else {
@@ -43,23 +43,23 @@ Search.prototype = $.extend({}, Navigation, new function() {
if (value == '') {
this.lastQuery = value;
- this.$result.empty();
- this.$result.attr('aria-expanded', 'false');
+ this.result.innerHTML = '';
+ this.result.setAttribute('aria-expanded', 'false');
this.setNavigationActive(false);
} else if (value != this.lastQuery) {
this.lastQuery = value;
- this.$result.attr('aria-busy', 'true');
- this.$result.attr('aria-expanded', 'true');
+ this.result.setAttribute('aria-busy', 'true');
+ this.result.setAttribute('aria-expanded', 'true');
this.firstRun = true;
this.searcher.find(value);
}
}
this.addResults = function(results, isLast) {
- var target = this.$result.get(0);
+ var target = this.result;
if (this.firstRun && (results.length > 0 || isLast)) {
- this.$current = null;
- this.$result.empty();
+ this.current = null;
+ this.result.innerHTML = '';
}
for (var i=0, l = results.length; i < l; i++) {
@@ -70,25 +70,26 @@ Search.prototype = $.extend({}, Navigation, new function() {
if (this.firstRun && results.length > 0) {
this.firstRun = false;
- this.$current = $(target.firstChild);
- this.$current.addClass('search-selected');
+ this.current = target.firstChild;
+ this.current.classList.add('search-selected');
}
- if (jQuery.browser.msie) this.$element[0].className += '';
+ //TODO: ECMAScript
+ //if (jQuery.browser.msie) this.$element[0].className += '';
- if (isLast) this.$result.attr('aria-busy', 'false');
+ if (isLast) this.result.setAttribute('aria-busy', 'false');
}
this.move = function(isDown) {
- if (!this.$current) return;
- var $next = this.$current[isDown ? 'next' : 'prev']();
- if ($next.length) {
- this.$current.removeClass('search-selected');
- $next.addClass('search-selected');
- this.$input.attr('aria-activedescendant', $next.attr('id'));
- this.scrollIntoView($next[0], this.$view[0]);
- this.$current = $next;
- this.$input.val($next[0].firstChild.firstChild.text);
- this.$input.select();
+ if (!this.current) return;
+ var next = isDown ? this.current.nextElementSibling : this.current.previousElementSibling;
+ if (next) {
+ this.current.classList.remove('search-selected');
+ next.classList.add('search-selected');
+ this.input.setAttribute('aria-activedescendant', next.getAttribute('id'));
+ this.scrollIntoView(next, this.view);
+ this.current = next;
+ this.input.value = next.firstChild.firstChild.text;
+ this.input.select();
}
return true;
}
diff --git a/lib/rdoc/generator/template/json_index/js/navigation.js b/lib/rdoc/generator/template/json_index/js/navigation.js
index e41268123e..dfad74b1ae 100644
--- a/lib/rdoc/generator/template/json_index/js/navigation.js
+++ b/lib/rdoc/generator/template/json_index/js/navigation.js
@@ -10,10 +10,8 @@ Navigation = new function() {
this.initNavigation = function() {
var _this = this;
- $(document).keydown(function(e) {
+ document.addEventListener('keydown', function(e) {
_this.onkeydown(e);
- }).keyup(function(e) {
- _this.onkeyup(e);
});
this.navigationActive = true;
@@ -21,20 +19,6 @@ Navigation = new function() {
this.setNavigationActive = function(state) {
this.navigationActive = state;
- this.clearMoveTimeout();
- }
-
- this.onkeyup = function(e) {
- if (!this.navigationActive) return;
-
- switch(e.keyCode) {
- case 37: //Event.KEY_LEFT:
- case 38: //Event.KEY_UP:
- case 39: //Event.KEY_RIGHT:
- case 40: //Event.KEY_DOWN:
- this.clearMoveTimeout();
- break;
- }
}
this.onkeydown = function(e) {
@@ -46,7 +30,6 @@ Navigation = new function() {
case 38: //Event.KEY_UP:
if (e.keyCode == 38 || e.ctrlKey) {
if (this.moveUp()) e.preventDefault();
- this.startMoveTimeout(false);
}
break;
case 39: //Event.KEY_RIGHT:
@@ -55,34 +38,14 @@ Navigation = new function() {
case 40: //Event.KEY_DOWN:
if (e.keyCode == 40 || e.ctrlKey) {
if (this.moveDown()) e.preventDefault();
- this.startMoveTimeout(true);
}
break;
case 13: //Event.KEY_RETURN:
- if (this.$current)
- e.preventDefault();
- this.select(this.$current);
+ if (this.current) e.preventDefault();
+ this.select(this.current);
break;
}
- if (e.ctrlKey && e.shiftKey) this.select(this.$current);
- }
-
- this.clearMoveTimeout = function() {
- clearTimeout(this.moveTimeout);
- this.moveTimeout = null;
- }
-
- this.startMoveTimeout = function(isDown) {
- if (!$.browser.mozilla && !$.browser.opera) return;
- if (this.moveTimeout) this.clearMoveTimeout();
- var _this = this;
-
- var go = function() {
- if (!_this.moveTimeout) return;
- _this[isDown ? 'moveDown' : 'moveUp']();
- _this.moveTimout = setTimeout(go, 100);
- }
- this.moveTimeout = setTimeout(go, 200);
+ if (e.ctrlKey && e.shiftKey) this.select(this.current);
}
this.moveRight = function() {
diff --git a/lib/rdoc/generator/template/json_index/js/searcher.js b/lib/rdoc/generator/template/json_index/js/searcher.js
index b3b1c58a0f..e200a168b0 100644
--- a/lib/rdoc/generator/template/json_index/js/searcher.js
+++ b/lib/rdoc/generator/template/json_index/js/searcher.js
@@ -51,20 +51,20 @@ Searcher.prototype = new function() {
/* ----- Utilities ------ */
function splitQuery(query) {
- return jQuery.grep(query.split(/(\s+|::?|\(\)?)/), function(string) {
+ return query.split(/(\s+|::?|\(\)?)/).filter(function(string) {
return string.match(/\S/);
});
}
function buildRegexps(queries) {
- return jQuery.map(queries, function(query) {
+ return queries.map(function(query) {
return new RegExp(query.replace(/(.)/g, '([$1])([^$1]*?)'), 'i');
});
}
function buildHilighters(queries) {
- return jQuery.map(queries, function(query) {
- return jQuery.map(query.split(''), function(l, i) {
+ return queries.map(function(query) {
+ return query.split('').map(function(l, i) {
return '\u0001$' + (i*2+1) + '\u0002$' + (i*2+2);
}).join('');
});
@@ -221,9 +221,9 @@ Searcher.prototype = new function() {
}
function triggerResults(results, isLast) {
- jQuery.each(this.handlers, function(i, fn) {
+ this.handlers.forEach(function(fn) {
fn.call(this, results, isLast)
- })
+ });
}
}
diff --git a/lib/rdoc/ghost_method.rb b/lib/rdoc/ghost_method.rb
index a1f75bfe4b..2488feb9d7 100644
--- a/lib/rdoc/ghost_method.rb
+++ b/lib/rdoc/ghost_method.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# GhostMethod represents a method referenced only by a comment
diff --git a/lib/rdoc/i18n.rb b/lib/rdoc/i18n.rb
index 20848aad75..af303858b9 100644
--- a/lib/rdoc/i18n.rb
+++ b/lib/rdoc/i18n.rb
@@ -1,10 +1,10 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
-# This module provides i18n realated features.
+# This module provides i18n related features.
module RDoc::I18n
autoload :Locale, 'rdoc/i18n/locale'
- autoload :Text, 'rdoc/i18n/text'
+ require 'rdoc/i18n/text'
end
diff --git a/lib/rdoc/i18n/locale.rb b/lib/rdoc/i18n/locale.rb
index 735a271bf3..6a70d6c986 100644
--- a/lib/rdoc/i18n/locale.rb
+++ b/lib/rdoc/i18n/locale.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A message container for a locale.
#
@@ -92,7 +92,7 @@ class RDoc::I18n::Locale
end
##
- # Translates the +message+ into locale. If there is no tranlsation
+ # Translates the +message+ into locale. If there is no translation
# messages for +message+ in locale, +message+ itself is returned.
def translate(message)
diff --git a/lib/rdoc/i18n/text.rb b/lib/rdoc/i18n/text.rb
index fcfe7611bc..7ea6664442 100644
--- a/lib/rdoc/i18n/text.rb
+++ b/lib/rdoc/i18n/text.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# An i18n supported text.
#
@@ -46,9 +46,9 @@ class RDoc::I18n::Text
parse do |part|
case part[:type]
when :paragraph
- translated_text << locale.translate(part[:paragraph])
+ translated_text += locale.translate(part[:paragraph])
when :empty_line
- translated_text << part[:line]
+ translated_text += part[:line]
else
raise "should not reach here: unexpected type: #{type}"
end
@@ -69,14 +69,14 @@ class RDoc::I18n::Text
if paragraph.empty?
emit_empty_line_event(line, line_no, &block)
else
- paragraph << line
+ paragraph += line
emit_paragraph_event(paragraph, paragraph_start_line, line_no,
&block)
paragraph = ''
end
else
paragraph_start_line = line_no if paragraph.empty?
- paragraph << line
+ paragraph += line
end
end
diff --git a/lib/rdoc/include.rb b/lib/rdoc/include.rb
index efce43bffb..b3ad610649 100644
--- a/lib/rdoc/include.rb
+++ b/lib/rdoc/include.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A Module included in a class with \#include
#
diff --git a/lib/rdoc/known_classes.rb b/lib/rdoc/known_classes.rb
index 8d9421255b..4d7f4aa995 100644
--- a/lib/rdoc/known_classes.rb
+++ b/lib/rdoc/known_classes.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module RDoc
##
diff --git a/lib/rdoc/markdown.rb b/lib/rdoc/markdown.rb
index b10f5193da..43c70c8de6 100644
--- a/lib/rdoc/markdown.rb
+++ b/lib/rdoc/markdown.rb
@@ -1,4 +1,5 @@
# coding: UTF-8
+# frozen_string_literal: true
# :markup: markdown
##
@@ -81,6 +82,18 @@
# : A little insect that is known
# to enjoy picnics
#
+# ### Strike
+#
+# Example:
+#
+# ```
+# This is ~~striked~~.
+# ```
+#
+# Produces:
+#
+# This is ~~striked~~.
+#
# ### Github
#
# The #github extension enables a partial set of [Github Flavored Markdown]
@@ -540,6 +553,7 @@ class RDoc::Markdown
:github,
:html,
:notes,
+ :strike,
]
# :section: Extensions
@@ -590,6 +604,11 @@ class RDoc::Markdown
extension :notes
+ ##
+ # Enables the strike extension
+
+ extension :strike
+
# :section:
##
@@ -678,7 +697,7 @@ class RDoc::Markdown
# with the linking `text` (usually whitespace).
def link_to content, label = content, text = nil
- raise 'enable notes extension' if
+ raise ParseError, 'enable notes extension' if
content.start_with? '^' and label.equal? content
if ref = @references[label] then
@@ -814,6 +833,17 @@ class RDoc::Markdown
end
end
+ ##
+ # Wraps `text` in strike markup for rdoc inline formatting
+
+ def strike text
+ if text =~ /\A[a-z\d.\/-]+\z/i then
+ "~#{text}~"
+ else
+ "<s>#{text}</s>"
+ end
+ end
+
# :stopdoc:
def setup_foreign_grammar
@@ -1005,7 +1035,7 @@ class RDoc::Markdown
return _tmp
end
- # AtxInline = !@Newline !(@Sp? /#*/ @Sp @Newline) Inline
+ # AtxInline = !@Newline !(@Sp /#*/ @Sp @Newline) Inline
def _AtxInline
_save = self.pos
@@ -1022,13 +1052,8 @@ class RDoc::Markdown
_save3 = self.pos
while true # sequence
- _save4 = self.pos
_tmp = _Sp()
unless _tmp
- _tmp = true
- self.pos = _save4
- end
- unless _tmp
self.pos = _save3
break
end
@@ -1092,7 +1117,7 @@ class RDoc::Markdown
return _tmp
end
- # AtxHeading = AtxStart:s @Sp? AtxInline+:a (@Sp? /#*/ @Sp)? @Newline { RDoc::Markup::Heading.new(s, a.join) }
+ # AtxHeading = AtxStart:s @Sp AtxInline+:a (@Sp /#*/ @Sp)? @Newline { RDoc::Markup::Heading.new(s, a.join) }
def _AtxHeading
_save = self.pos
@@ -1103,17 +1128,12 @@ class RDoc::Markdown
self.pos = _save
break
end
- _save1 = self.pos
_tmp = _Sp()
unless _tmp
- _tmp = true
- self.pos = _save1
- end
- unless _tmp
self.pos = _save
break
end
- _save2 = self.pos
+ _save1 = self.pos
_ary = []
_tmp = apply(:_AtxInline)
if _tmp
@@ -1126,42 +1146,37 @@ class RDoc::Markdown
_tmp = true
@result = _ary
else
- self.pos = _save2
+ self.pos = _save1
end
a = @result
unless _tmp
self.pos = _save
break
end
- _save3 = self.pos
+ _save2 = self.pos
- _save4 = self.pos
+ _save3 = self.pos
while true # sequence
- _save5 = self.pos
_tmp = _Sp()
unless _tmp
- _tmp = true
- self.pos = _save5
- end
- unless _tmp
- self.pos = _save4
+ self.pos = _save3
break
end
_tmp = scan(/\A(?-mix:#*)/)
unless _tmp
- self.pos = _save4
+ self.pos = _save3
break
end
_tmp = _Sp()
unless _tmp
- self.pos = _save4
+ self.pos = _save3
end
break
end # end sequence
unless _tmp
_tmp = true
- self.pos = _save3
+ self.pos = _save2
end
unless _tmp
self.pos = _save
@@ -1202,12 +1217,12 @@ class RDoc::Markdown
return _tmp
end
- # SetextBottom1 = /={3,}/ @Newline
+ # SetextBottom1 = /={1,}/ @Newline
def _SetextBottom1
_save = self.pos
while true # sequence
- _tmp = scan(/\A(?-mix:={3,})/)
+ _tmp = scan(/\A(?-mix:={1,})/)
unless _tmp
self.pos = _save
break
@@ -1223,12 +1238,12 @@ class RDoc::Markdown
return _tmp
end
- # SetextBottom2 = /-{3,}/ @Newline
+ # SetextBottom2 = /-{1,}/ @Newline
def _SetextBottom2
_save = self.pos
while true # sequence
- _tmp = scan(/\A(?-mix:-{3,})/)
+ _tmp = scan(/\A(?-mix:-{1,})/)
unless _tmp
self.pos = _save
break
@@ -1244,7 +1259,7 @@ class RDoc::Markdown
return _tmp
end
- # SetextHeading1 = &(@RawLine SetextBottom1) @StartList:a (!@Endline Inline:b { a << b })+ @Sp? @Newline SetextBottom1 { RDoc::Markup::Heading.new(1, a.join) }
+ # SetextHeading1 = &(@RawLine SetextBottom1) @StartList:a (!@Endline Inline:b { a << b })+ @Sp @Newline SetextBottom1 { RDoc::Markup::Heading.new(1, a.join) }
def _SetextHeading1
_save = self.pos
@@ -1339,13 +1354,8 @@ class RDoc::Markdown
self.pos = _save
break
end
- _save8 = self.pos
_tmp = _Sp()
unless _tmp
- _tmp = true
- self.pos = _save8
- end
- unless _tmp
self.pos = _save
break
end
@@ -1371,7 +1381,7 @@ class RDoc::Markdown
return _tmp
end
- # SetextHeading2 = &(@RawLine SetextBottom2) @StartList:a (!@Endline Inline:b { a << b })+ @Sp? @Newline SetextBottom2 { RDoc::Markup::Heading.new(2, a.join) }
+ # SetextHeading2 = &(@RawLine SetextBottom2) @StartList:a (!@Endline Inline:b { a << b })+ @Sp @Newline SetextBottom2 { RDoc::Markup::Heading.new(2, a.join) }
def _SetextHeading2
_save = self.pos
@@ -1466,13 +1476,8 @@ class RDoc::Markdown
self.pos = _save
break
end
- _save8 = self.pos
_tmp = _Sp()
unless _tmp
- _tmp = true
- self.pos = _save8
- end
- unless _tmp
self.pos = _save
break
end
@@ -8446,7 +8451,162 @@ class RDoc::Markdown
return _tmp
end
- # HtmlBlockInTags = (HtmlAnchor | HtmlBlockAddress | HtmlBlockBlockquote | HtmlBlockCenter | HtmlBlockDir | HtmlBlockDiv | HtmlBlockDl | HtmlBlockFieldset | HtmlBlockForm | HtmlBlockH1 | HtmlBlockH2 | HtmlBlockH3 | HtmlBlockH4 | HtmlBlockH5 | HtmlBlockH6 | HtmlBlockMenu | HtmlBlockNoframes | HtmlBlockNoscript | HtmlBlockOl | HtmlBlockP | HtmlBlockPre | HtmlBlockTable | HtmlBlockUl | HtmlBlockDd | HtmlBlockDt | HtmlBlockFrameset | HtmlBlockLi | HtmlBlockTbody | HtmlBlockTd | HtmlBlockTfoot | HtmlBlockTh | HtmlBlockThead | HtmlBlockTr | HtmlBlockScript)
+ # HtmlBlockOpenHead = "<" Spnl ("head" | "HEAD") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenHead
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("head")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("HEAD")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenHead unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseHead = "<" Spnl "/" ("head" | "HEAD") Spnl ">"
+ def _HtmlBlockCloseHead
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("head")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("HEAD")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseHead unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockHead = HtmlBlockOpenHead (!HtmlBlockCloseHead .)* HtmlBlockCloseHead
+ def _HtmlBlockHead
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenHead)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # sequence
+ _save3 = self.pos
+ _tmp = apply(:_HtmlBlockCloseHead)
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseHead)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockHead unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockInTags = (HtmlAnchor | HtmlBlockAddress | HtmlBlockBlockquote | HtmlBlockCenter | HtmlBlockDir | HtmlBlockDiv | HtmlBlockDl | HtmlBlockFieldset | HtmlBlockForm | HtmlBlockH1 | HtmlBlockH2 | HtmlBlockH3 | HtmlBlockH4 | HtmlBlockH5 | HtmlBlockH6 | HtmlBlockMenu | HtmlBlockNoframes | HtmlBlockNoscript | HtmlBlockOl | HtmlBlockP | HtmlBlockPre | HtmlBlockTable | HtmlBlockUl | HtmlBlockDd | HtmlBlockDt | HtmlBlockFrameset | HtmlBlockLi | HtmlBlockTbody | HtmlBlockTd | HtmlBlockTfoot | HtmlBlockTh | HtmlBlockThead | HtmlBlockTr | HtmlBlockScript | HtmlBlockHead)
def _HtmlBlockInTags
_save = self.pos
@@ -8553,6 +8713,9 @@ class RDoc::Markdown
_tmp = apply(:_HtmlBlockScript)
break if _tmp
self.pos = _save
+ _tmp = apply(:_HtmlBlockHead)
+ break if _tmp
+ self.pos = _save
break
end # end choice
@@ -9320,7 +9483,7 @@ class RDoc::Markdown
return _tmp
end
- # Inline = (Str | @Endline | UlOrStarLine | @Space | Strong | Emph | Image | Link | NoteReference | InlineNote | Code | RawHtml | Entity | EscapedChar | Symbol)
+ # Inline = (Str | @Endline | UlOrStarLine | @Space | Strong | Emph | Strike | Image | Link | NoteReference | InlineNote | Code | RawHtml | Entity | EscapedChar | Symbol)
def _Inline
_save = self.pos
@@ -9343,6 +9506,9 @@ class RDoc::Markdown
_tmp = apply(:_Emph)
break if _tmp
self.pos = _save
+ _tmp = apply(:_Strike)
+ break if _tmp
+ self.pos = _save
_tmp = apply(:_Image)
break if _tmp
self.pos = _save
@@ -9669,7 +9835,7 @@ class RDoc::Markdown
return _tmp
end
- # NormalEndline = @Sp @Newline !@BlankLine !">" !AtxStart !(Line /={3,}|-{3,}=/ @Newline) { "\n" }
+ # NormalEndline = @Sp @Newline !@BlankLine !">" !AtxStart !(Line /={1,}|-{1,}/ @Newline) { "\n" }
def _NormalEndline
_save = self.pos
@@ -9717,7 +9883,7 @@ class RDoc::Markdown
self.pos = _save5
break
end
- _tmp = scan(/\A(?-mix:={3,}|-{3,}=)/)
+ _tmp = scan(/\A(?-mix:={1,}|-{1,})/)
unless _tmp
self.pos = _save5
break
@@ -10032,96 +10198,38 @@ class RDoc::Markdown
return _tmp
end
- # OneStarOpen = !StarLine "*" !@Spacechar !@Newline
- def _OneStarOpen
+ # Whitespace = (@Spacechar | @Newline)
+ def _Whitespace
_save = self.pos
- while true # sequence
- _save1 = self.pos
- _tmp = apply(:_StarLine)
- _tmp = _tmp ? nil : true
- self.pos = _save1
- unless _tmp
- self.pos = _save
- break
- end
- _tmp = match_string("*")
- unless _tmp
- self.pos = _save
- break
- end
- _save2 = self.pos
+ while true # choice
_tmp = _Spacechar()
- _tmp = _tmp ? nil : true
- self.pos = _save2
- unless _tmp
- self.pos = _save
- break
- end
- _save3 = self.pos
+ break if _tmp
+ self.pos = _save
_tmp = _Newline()
- _tmp = _tmp ? nil : true
- self.pos = _save3
- unless _tmp
- self.pos = _save
- end
+ break if _tmp
+ self.pos = _save
break
- end # end sequence
+ end # end choice
- set_failed_rule :_OneStarOpen unless _tmp
+ set_failed_rule :_Whitespace unless _tmp
return _tmp
end
- # OneStarClose = !@Spacechar !@Newline Inline:a "*" { a }
- def _OneStarClose
+ # EmphStar = "*" !@Whitespace @StartList:a (!"*" Inline:b { a << b } | StrongStar:b { a << b })+ "*" { emphasis a.join }
+ def _EmphStar
_save = self.pos
while true # sequence
- _save1 = self.pos
- _tmp = _Spacechar()
- _tmp = _tmp ? nil : true
- self.pos = _save1
- unless _tmp
- self.pos = _save
- break
- end
- _save2 = self.pos
- _tmp = _Newline()
- _tmp = _tmp ? nil : true
- self.pos = _save2
- unless _tmp
- self.pos = _save
- break
- end
- _tmp = apply(:_Inline)
- a = @result
- unless _tmp
- self.pos = _save
- break
- end
_tmp = match_string("*")
unless _tmp
self.pos = _save
break
end
- @result = begin; a ; end
- _tmp = true
- unless _tmp
- self.pos = _save
- end
- break
- end # end sequence
-
- set_failed_rule :_OneStarClose unless _tmp
- return _tmp
- end
-
- # EmphStar = OneStarOpen @StartList:a (!OneStarClose Inline:l { a << l })* OneStarClose:l { a << l } { emphasis a.join }
- def _EmphStar
-
- _save = self.pos
- while true # sequence
- _tmp = apply(:_OneStarOpen)
+ _save1 = self.pos
+ _tmp = _Whitespace()
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
unless _tmp
self.pos = _save
break
@@ -10132,47 +10240,124 @@ class RDoc::Markdown
self.pos = _save
break
end
- while true
+ _save2 = self.pos
- _save2 = self.pos
+ _save3 = self.pos
+ while true # choice
+
+ _save4 = self.pos
while true # sequence
- _save3 = self.pos
- _tmp = apply(:_OneStarClose)
+ _save5 = self.pos
+ _tmp = match_string("*")
_tmp = _tmp ? nil : true
- self.pos = _save3
+ self.pos = _save5
unless _tmp
- self.pos = _save2
+ self.pos = _save4
break
end
_tmp = apply(:_Inline)
- l = @result
+ b = @result
unless _tmp
- self.pos = _save2
+ self.pos = _save4
break
end
- @result = begin; a << l ; end
+ @result = begin; a << b ; end
_tmp = true
unless _tmp
- self.pos = _save2
+ self.pos = _save4
end
break
end # end sequence
- break unless _tmp
- end
- _tmp = true
- unless _tmp
- self.pos = _save
+ break if _tmp
+ self.pos = _save3
+
+ _save6 = self.pos
+ while true # sequence
+ _tmp = apply(:_StrongStar)
+ b = @result
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save6
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save3
break
+ end # end choice
+
+ if _tmp
+ while true
+
+ _save7 = self.pos
+ while true # choice
+
+ _save8 = self.pos
+ while true # sequence
+ _save9 = self.pos
+ _tmp = match_string("*")
+ _tmp = _tmp ? nil : true
+ self.pos = _save9
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save8
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save7
+
+ _save10 = self.pos
+ while true # sequence
+ _tmp = apply(:_StrongStar)
+ b = @result
+ unless _tmp
+ self.pos = _save10
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save10
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save7
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
end
- _tmp = apply(:_OneStarClose)
- l = @result
unless _tmp
self.pos = _save
break
end
- @result = begin; a << l ; end
- _tmp = true
+ _tmp = match_string("*")
unless _tmp
self.pos = _save
break
@@ -10189,147 +10374,148 @@ class RDoc::Markdown
return _tmp
end
- # OneUlOpen = !UlLine "_" !@Spacechar !@Newline
- def _OneUlOpen
+ # EmphUl = "_" !@Whitespace @StartList:a (!"_" Inline:b { a << b } | StrongUl:b { a << b })+ "_" { emphasis a.join }
+ def _EmphUl
_save = self.pos
while true # sequence
- _save1 = self.pos
- _tmp = apply(:_UlLine)
- _tmp = _tmp ? nil : true
- self.pos = _save1
- unless _tmp
- self.pos = _save
- break
- end
_tmp = match_string("_")
unless _tmp
self.pos = _save
break
end
- _save2 = self.pos
- _tmp = _Spacechar()
- _tmp = _tmp ? nil : true
- self.pos = _save2
- unless _tmp
- self.pos = _save
- break
- end
- _save3 = self.pos
- _tmp = _Newline()
- _tmp = _tmp ? nil : true
- self.pos = _save3
- unless _tmp
- self.pos = _save
- end
- break
- end # end sequence
-
- set_failed_rule :_OneUlOpen unless _tmp
- return _tmp
- end
-
- # OneUlClose = !@Spacechar !@Newline Inline:a "_" { a }
- def _OneUlClose
-
- _save = self.pos
- while true # sequence
_save1 = self.pos
- _tmp = _Spacechar()
+ _tmp = _Whitespace()
_tmp = _tmp ? nil : true
self.pos = _save1
unless _tmp
self.pos = _save
break
end
- _save2 = self.pos
- _tmp = _Newline()
- _tmp = _tmp ? nil : true
- self.pos = _save2
- unless _tmp
- self.pos = _save
- break
- end
- _tmp = apply(:_Inline)
- a = @result
- unless _tmp
- self.pos = _save
- break
- end
- _tmp = match_string("_")
- unless _tmp
- self.pos = _save
- break
- end
- @result = begin; a ; end
- _tmp = true
- unless _tmp
- self.pos = _save
- end
- break
- end # end sequence
-
- set_failed_rule :_OneUlClose unless _tmp
- return _tmp
- end
-
- # EmphUl = OneUlOpen @StartList:a (!OneUlClose Inline:l { a << l })* OneUlClose:l { a << l } { emphasis a.join }
- def _EmphUl
-
- _save = self.pos
- while true # sequence
- _tmp = apply(:_OneUlOpen)
- unless _tmp
- self.pos = _save
- break
- end
_tmp = _StartList()
a = @result
unless _tmp
self.pos = _save
break
end
- while true
+ _save2 = self.pos
- _save2 = self.pos
+ _save3 = self.pos
+ while true # choice
+
+ _save4 = self.pos
while true # sequence
- _save3 = self.pos
- _tmp = apply(:_OneUlClose)
+ _save5 = self.pos
+ _tmp = match_string("_")
_tmp = _tmp ? nil : true
- self.pos = _save3
+ self.pos = _save5
unless _tmp
- self.pos = _save2
+ self.pos = _save4
break
end
_tmp = apply(:_Inline)
- l = @result
+ b = @result
unless _tmp
- self.pos = _save2
+ self.pos = _save4
break
end
- @result = begin; a << l ; end
+ @result = begin; a << b ; end
_tmp = true
unless _tmp
- self.pos = _save2
+ self.pos = _save4
end
break
end # end sequence
- break unless _tmp
- end
- _tmp = true
- unless _tmp
- self.pos = _save
+ break if _tmp
+ self.pos = _save3
+
+ _save6 = self.pos
+ while true # sequence
+ _tmp = apply(:_StrongUl)
+ b = @result
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save6
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save3
break
+ end # end choice
+
+ if _tmp
+ while true
+
+ _save7 = self.pos
+ while true # choice
+
+ _save8 = self.pos
+ while true # sequence
+ _save9 = self.pos
+ _tmp = match_string("_")
+ _tmp = _tmp ? nil : true
+ self.pos = _save9
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save8
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save7
+
+ _save10 = self.pos
+ while true # sequence
+ _tmp = apply(:_StrongUl)
+ b = @result
+ unless _tmp
+ self.pos = _save10
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save10
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save7
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
end
- _tmp = apply(:_OneUlClose)
- l = @result
unless _tmp
self.pos = _save
break
end
- @result = begin; a << l ; end
- _tmp = true
+ _tmp = match_string("_")
unless _tmp
self.pos = _save
break
@@ -10364,69 +10550,89 @@ class RDoc::Markdown
return _tmp
end
- # TwoStarOpen = !StarLine "**" !@Spacechar !@Newline
- def _TwoStarOpen
+ # StrongStar = "**" !@Whitespace @StartList:a (!"**" Inline:b { a << b })+ "**" { strong a.join }
+ def _StrongStar
_save = self.pos
while true # sequence
- _save1 = self.pos
- _tmp = apply(:_StarLine)
- _tmp = _tmp ? nil : true
- self.pos = _save1
- unless _tmp
- self.pos = _save
- break
- end
_tmp = match_string("**")
unless _tmp
self.pos = _save
break
end
- _save2 = self.pos
- _tmp = _Spacechar()
+ _save1 = self.pos
+ _tmp = _Whitespace()
_tmp = _tmp ? nil : true
- self.pos = _save2
+ self.pos = _save1
unless _tmp
self.pos = _save
break
end
- _save3 = self.pos
- _tmp = _Newline()
- _tmp = _tmp ? nil : true
- self.pos = _save3
- unless _tmp
- self.pos = _save
- end
- break
- end # end sequence
-
- set_failed_rule :_TwoStarOpen unless _tmp
- return _tmp
- end
-
- # TwoStarClose = !@Spacechar !@Newline Inline:a "**" { a }
- def _TwoStarClose
-
- _save = self.pos
- while true # sequence
- _save1 = self.pos
- _tmp = _Spacechar()
- _tmp = _tmp ? nil : true
- self.pos = _save1
+ _tmp = _StartList()
+ a = @result
unless _tmp
self.pos = _save
break
end
_save2 = self.pos
- _tmp = _Newline()
- _tmp = _tmp ? nil : true
- self.pos = _save2
- unless _tmp
- self.pos = _save
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = match_string("**")
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save3
+ end
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
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
end
- _tmp = apply(:_Inline)
- a = @result
unless _tmp
self.pos = _save
break
@@ -10436,7 +10642,7 @@ class RDoc::Markdown
self.pos = _save
break
end
- @result = begin; a ; end
+ @result = begin; strong a.join ; end
_tmp = true
unless _tmp
self.pos = _save
@@ -10444,16 +10650,24 @@ class RDoc::Markdown
break
end # end sequence
- set_failed_rule :_TwoStarClose unless _tmp
+ set_failed_rule :_StrongStar unless _tmp
return _tmp
end
- # StrongStar = TwoStarOpen @StartList:a (!TwoStarClose Inline:l { a << l })* TwoStarClose:l { a << l } { strong a.join }
- def _StrongStar
+ # StrongUl = "__" !@Whitespace @StartList:a (!"__" Inline:b { a << b })+ "__" { strong a.join }
+ def _StrongUl
_save = self.pos
while true # sequence
- _tmp = apply(:_TwoStarOpen)
+ _tmp = match_string("__")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = _Whitespace()
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
unless _tmp
self.pos = _save
break
@@ -10464,47 +10678,70 @@ class RDoc::Markdown
self.pos = _save
break
end
- while true
+ _save2 = self.pos
- _save2 = self.pos
- while true # sequence
- _save3 = self.pos
- _tmp = apply(:_TwoStarClose)
- _tmp = _tmp ? nil : true
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = match_string("__")
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
self.pos = _save3
- unless _tmp
- self.pos = _save2
- break
- end
- _tmp = apply(:_Inline)
- l = @result
- unless _tmp
- self.pos = _save2
- break
- end
- @result = begin; a << l ; end
- _tmp = true
- unless _tmp
- self.pos = _save2
- end
break
- end # end sequence
-
- break unless _tmp
- end
- _tmp = true
- unless _tmp
- self.pos = _save
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save3
+ end
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
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
end
- _tmp = apply(:_TwoStarClose)
- l = @result
unless _tmp
self.pos = _save
break
end
- @result = begin; a << l ; end
- _tmp = true
+ _tmp = match_string("__")
unless _tmp
self.pos = _save
break
@@ -10517,156 +10754,110 @@ class RDoc::Markdown
break
end # end sequence
- set_failed_rule :_StrongStar unless _tmp
+ set_failed_rule :_StrongUl unless _tmp
return _tmp
end
- # TwoUlOpen = !UlLine "__" !@Spacechar !@Newline
- def _TwoUlOpen
+ # Strike = &{ strike? } "~~" !@Whitespace @StartList:a (!"~~" Inline:b { a << b })+ "~~" { strike a.join }
+ def _Strike
_save = self.pos
while true # sequence
_save1 = self.pos
- _tmp = apply(:_UlLine)
- _tmp = _tmp ? nil : true
+ _tmp = begin; strike? ; end
self.pos = _save1
unless _tmp
self.pos = _save
break
end
- _tmp = match_string("__")
+ _tmp = match_string("~~")
unless _tmp
self.pos = _save
break
end
_save2 = self.pos
- _tmp = _Spacechar()
+ _tmp = _Whitespace()
_tmp = _tmp ? nil : true
self.pos = _save2
unless _tmp
self.pos = _save
break
end
- _save3 = self.pos
- _tmp = _Newline()
- _tmp = _tmp ? nil : true
- self.pos = _save3
- unless _tmp
- self.pos = _save
- end
- break
- end # end sequence
-
- set_failed_rule :_TwoUlOpen unless _tmp
- return _tmp
- end
-
- # TwoUlClose = !@Spacechar !@Newline Inline:a "__" { a }
- def _TwoUlClose
-
- _save = self.pos
- while true # sequence
- _save1 = self.pos
- _tmp = _Spacechar()
- _tmp = _tmp ? nil : true
- self.pos = _save1
- unless _tmp
- self.pos = _save
- break
- end
- _save2 = self.pos
- _tmp = _Newline()
- _tmp = _tmp ? nil : true
- self.pos = _save2
- unless _tmp
- self.pos = _save
- break
- end
- _tmp = apply(:_Inline)
+ _tmp = _StartList()
a = @result
unless _tmp
self.pos = _save
break
end
- _tmp = match_string("__")
- unless _tmp
- self.pos = _save
- break
- end
- @result = begin; a ; end
- _tmp = true
- unless _tmp
- self.pos = _save
- end
- break
- end # end sequence
-
- set_failed_rule :_TwoUlClose unless _tmp
- return _tmp
- end
-
- # StrongUl = TwoUlOpen @StartList:a (!TwoUlClose Inline:i { a << i })* TwoUlClose:l { a << l } { strong a.join }
- def _StrongUl
+ _save3 = self.pos
- _save = self.pos
- while true # sequence
- _tmp = apply(:_TwoUlOpen)
- unless _tmp
- self.pos = _save
- break
- end
- _tmp = _StartList()
- a = @result
- unless _tmp
- self.pos = _save
+ _save4 = self.pos
+ while true # sequence
+ _save5 = self.pos
+ _tmp = match_string("~~")
+ _tmp = _tmp ? nil : true
+ self.pos = _save5
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save4
+ end
break
- end
- while true
+ end # end sequence
- _save2 = self.pos
- while true # sequence
- _save3 = self.pos
- _tmp = apply(:_TwoUlClose)
- _tmp = _tmp ? nil : true
- self.pos = _save3
- unless _tmp
- self.pos = _save2
- break
- end
- _tmp = apply(:_Inline)
- i = @result
- unless _tmp
- self.pos = _save2
+ if _tmp
+ while true
+
+ _save6 = self.pos
+ while true # sequence
+ _save7 = self.pos
+ _tmp = match_string("~~")
+ _tmp = _tmp ? nil : true
+ self.pos = _save7
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save6
+ end
break
- end
- @result = begin; a << i ; end
- _tmp = true
- unless _tmp
- self.pos = _save2
- end
- break
- end # end sequence
+ end # end sequence
- break unless _tmp
- end
- _tmp = true
- unless _tmp
- self.pos = _save
- break
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save3
end
- _tmp = apply(:_TwoUlClose)
- l = @result
unless _tmp
self.pos = _save
break
end
- @result = begin; a << l ; end
- _tmp = true
+ _tmp = match_string("~~")
unless _tmp
self.pos = _save
break
end
- @result = begin; strong a.join ; end
+ @result = begin; strike a.join ; end
_tmp = true
unless _tmp
self.pos = _save
@@ -10674,7 +10865,7 @@ class RDoc::Markdown
break
end # end sequence
- set_failed_rule :_StrongUl unless _tmp
+ set_failed_rule :_Strike unless _tmp
return _tmp
end
@@ -10853,7 +11044,7 @@ class RDoc::Markdown
return _tmp
end
- # ExplicitLink = Label:l Spnl "(" @Sp Source:s Spnl Title @Sp ")" { "{#{l}}[#{s}]" }
+ # ExplicitLink = Label:l "(" @Sp Source:s Spnl Title @Sp ")" { "{#{l}}[#{s}]" }
def _ExplicitLink
_save = self.pos
@@ -10864,11 +11055,6 @@ class RDoc::Markdown
self.pos = _save
break
end
- _tmp = apply(:_Spnl)
- unless _tmp
- self.pos = _save
- break
- end
_tmp = match_string("(")
unless _tmp
self.pos = _save
@@ -10977,130 +11163,119 @@ class RDoc::Markdown
return _tmp
end
- # SourceContents = (((!"(" !")" !">" Nonspacechar)+ | "(" SourceContents ")")* | "")
+ # SourceContents = ((!"(" !")" !">" Nonspacechar)+ | "(" SourceContents ")")*
def _SourceContents
+ while true
- _save = self.pos
- while true # choice
- while true
-
+ _save1 = self.pos
+ while true # choice
_save2 = self.pos
- while true # choice
- _save3 = self.pos
+ _save3 = self.pos
+ while true # sequence
_save4 = self.pos
- while true # sequence
- _save5 = self.pos
- _tmp = match_string("(")
- _tmp = _tmp ? nil : true
- self.pos = _save5
- unless _tmp
- self.pos = _save4
- break
- end
- _save6 = self.pos
- _tmp = match_string(")")
- _tmp = _tmp ? nil : true
- self.pos = _save6
- unless _tmp
- self.pos = _save4
- break
- end
- _save7 = self.pos
- _tmp = match_string(">")
- _tmp = _tmp ? nil : true
- self.pos = _save7
- unless _tmp
- self.pos = _save4
- break
- end
- _tmp = apply(:_Nonspacechar)
- unless _tmp
- self.pos = _save4
- end
+ _tmp = match_string("(")
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
break
- end # end sequence
+ end
+ _save5 = self.pos
+ _tmp = match_string(")")
+ _tmp = _tmp ? nil : true
+ self.pos = _save5
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _save6 = self.pos
+ _tmp = match_string(">")
+ _tmp = _tmp ? nil : true
+ self.pos = _save6
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
- if _tmp
- while true
+ if _tmp
+ while true
+ _save7 = self.pos
+ while true # sequence
_save8 = self.pos
- while true # sequence
- _save9 = self.pos
- _tmp = match_string("(")
- _tmp = _tmp ? nil : true
- self.pos = _save9
- unless _tmp
- self.pos = _save8
- break
- end
- _save10 = self.pos
- _tmp = match_string(")")
- _tmp = _tmp ? nil : true
- self.pos = _save10
- unless _tmp
- self.pos = _save8
- break
- end
- _save11 = self.pos
- _tmp = match_string(">")
- _tmp = _tmp ? nil : true
- self.pos = _save11
- unless _tmp
- self.pos = _save8
- break
- end
- _tmp = apply(:_Nonspacechar)
- unless _tmp
- self.pos = _save8
- end
+ _tmp = match_string("(")
+ _tmp = _tmp ? nil : true
+ self.pos = _save8
+ unless _tmp
+ self.pos = _save7
break
- end # end sequence
+ end
+ _save9 = self.pos
+ _tmp = match_string(")")
+ _tmp = _tmp ? nil : true
+ self.pos = _save9
+ unless _tmp
+ self.pos = _save7
+ break
+ end
+ _save10 = self.pos
+ _tmp = match_string(">")
+ _tmp = _tmp ? nil : true
+ self.pos = _save10
+ unless _tmp
+ self.pos = _save7
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save7
+ end
+ break
+ end # end sequence
- break unless _tmp
- end
- _tmp = true
- else
- self.pos = _save3
+ break unless _tmp
end
- break if _tmp
+ _tmp = true
+ else
self.pos = _save2
+ end
+ break if _tmp
+ self.pos = _save1
- _save12 = self.pos
- while true # sequence
- _tmp = match_string("(")
- unless _tmp
- self.pos = _save12
- break
- end
- _tmp = apply(:_SourceContents)
- unless _tmp
- self.pos = _save12
- break
- end
- _tmp = match_string(")")
- unless _tmp
- self.pos = _save12
- end
+ _save11 = self.pos
+ while true # sequence
+ _tmp = match_string("(")
+ unless _tmp
+ self.pos = _save11
break
- end # end sequence
-
- break if _tmp
- self.pos = _save2
+ end
+ _tmp = apply(:_SourceContents)
+ unless _tmp
+ self.pos = _save11
+ break
+ end
+ _tmp = match_string(")")
+ unless _tmp
+ self.pos = _save11
+ end
break
- end # end choice
+ end # end sequence
- break unless _tmp
- end
- _tmp = true
- break if _tmp
- self.pos = _save
- _tmp = match_string("")
- break if _tmp
- self.pos = _save
- break
- end # end choice
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+ break unless _tmp
+ end
+ _tmp = true
set_failed_rule :_SourceContents unless _tmp
return _tmp
end
@@ -14357,12 +14532,12 @@ class RDoc::Markdown
return _tmp
end
- # SpecialChar = (/[*_`&\[\]()<!#\\'"]/ | @ExtendedSpecialChar)
+ # SpecialChar = (/[~*_`&\[\]()<!#\\'"]/ | @ExtendedSpecialChar)
def _SpecialChar
_save = self.pos
while true # choice
- _tmp = scan(/\A(?-mix:[*_`&\[\]()<!#\\'"])/)
+ _tmp = scan(/\A(?-mix:[~*_`&\[\]()<!#\\'"])/)
break if _tmp
self.pos = _save
_tmp = _ExtendedSpecialChar()
@@ -14455,13 +14630,6 @@ class RDoc::Markdown
return _tmp
end
- # NonAlphanumeric = %literals.NonAlphanumeric
- def _NonAlphanumeric
- _tmp = @_grammar_literals.external_invoke(self, :_NonAlphanumeric)
- set_failed_rule :_NonAlphanumeric unless _tmp
- return _tmp
- end
-
# Spacechar = %literals.Spacechar
def _Spacechar
_tmp = @_grammar_literals.external_invoke(self, :_Spacechar)
@@ -15189,7 +15357,7 @@ class RDoc::Markdown
return _tmp
end
- # InlineNote = &{ notes? } "^[" @StartList:a (!"]" Inline:l { a << l })+ "]" { ref = [:inline, @note_order.length] @footnotes[ref] = paragraph a note_for ref }
+ # InlineNote = &{ notes? } "^[" @StartList:a (!"]" Inline:l { a << l })+ "]" { ref = [:inline, @note_order.length] @footnotes[ref] = paragraph a note_for ref }
def _InlineNote
_save = self.pos
@@ -15280,8 +15448,7 @@ class RDoc::Markdown
self.pos = _save
break
end
- @result = begin;
- ref = [:inline, @note_order.length]
+ @result = begin; ref = [:inline, @note_order.length]
@footnotes[ref] = paragraph a
note_for ref
@@ -15424,7 +15591,7 @@ class RDoc::Markdown
return _tmp
end
- # 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 verbatim }
+ # 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 }
def _CodeFence
_save = self.pos
@@ -15666,7 +15833,7 @@ class RDoc::Markdown
break
end
@result = begin; verbatim = RDoc::Markup::Verbatim.new text
- verbatim.format = format.intern if format
+ verbatim.format = format.intern if format.instance_of?(String)
verbatim
; end
_tmp = true
@@ -15882,14 +16049,14 @@ class RDoc::Markdown
Rules[:_Block] = rule_info("Block", "@BlankLine* (BlockQuote | Verbatim | CodeFence | Note | Reference | HorizontalRule | Heading | OrderedList | BulletList | DefinitionList | HtmlBlock | StyleBlock | Para | Plain)")
Rules[:_Para] = rule_info("Para", "@NonindentSpace Inlines:a @BlankLine+ { paragraph a }")
Rules[:_Plain] = rule_info("Plain", "Inlines:a { paragraph a }")
- Rules[:_AtxInline] = rule_info("AtxInline", "!@Newline !(@Sp? /\#*/ @Sp @Newline) Inline")
+ Rules[:_AtxInline] = rule_info("AtxInline", "!@Newline !(@Sp /\#*/ @Sp @Newline) Inline")
Rules[:_AtxStart] = rule_info("AtxStart", "< /\\\#{1,6}/ > { text.length }")
- Rules[:_AtxHeading] = rule_info("AtxHeading", "AtxStart:s @Sp? AtxInline+:a (@Sp? /\#*/ @Sp)? @Newline { RDoc::Markup::Heading.new(s, a.join) }")
+ Rules[:_AtxHeading] = rule_info("AtxHeading", "AtxStart:s @Sp AtxInline+:a (@Sp /\#*/ @Sp)? @Newline { RDoc::Markup::Heading.new(s, a.join) }")
Rules[:_SetextHeading] = rule_info("SetextHeading", "(SetextHeading1 | SetextHeading2)")
- Rules[:_SetextBottom1] = rule_info("SetextBottom1", "/={3,}/ @Newline")
- Rules[:_SetextBottom2] = rule_info("SetextBottom2", "/-{3,}/ @Newline")
- Rules[:_SetextHeading1] = rule_info("SetextHeading1", "&(@RawLine SetextBottom1) @StartList:a (!@Endline Inline:b { a << b })+ @Sp? @Newline SetextBottom1 { RDoc::Markup::Heading.new(1, a.join) }")
- Rules[:_SetextHeading2] = rule_info("SetextHeading2", "&(@RawLine SetextBottom2) @StartList:a (!@Endline Inline:b { a << b })+ @Sp? @Newline SetextBottom2 { RDoc::Markup::Heading.new(2, a.join) }")
+ Rules[:_SetextBottom1] = rule_info("SetextBottom1", "/={1,}/ @Newline")
+ Rules[:_SetextBottom2] = rule_info("SetextBottom2", "/-{1,}/ @Newline")
+ Rules[:_SetextHeading1] = rule_info("SetextHeading1", "&(@RawLine SetextBottom1) @StartList:a (!@Endline Inline:b { a << b })+ @Sp @Newline SetextBottom1 { RDoc::Markup::Heading.new(1, a.join) }")
+ Rules[:_SetextHeading2] = rule_info("SetextHeading2", "&(@RawLine SetextBottom2) @StartList:a (!@Endline Inline:b { a << b })+ @Sp @Newline SetextBottom2 { RDoc::Markup::Heading.new(2, a.join) }")
Rules[:_Heading] = rule_info("Heading", "(SetextHeading | AtxHeading)")
Rules[:_BlockQuote] = rule_info("BlockQuote", "BlockQuoteRaw:a { RDoc::Markup::BlockQuote.new(*a) }")
Rules[:_BlockQuoteRaw] = rule_info("BlockQuoteRaw", "@StartList:a (\">\" \" \"? Line:l { a << l } (!\">\" !@BlankLine Line:c { a << c })* (@BlankLine:n { a << n })*)+ { inner_parse a.join }")
@@ -16010,7 +16177,10 @@ class RDoc::Markdown
Rules[:_HtmlBlockOpenScript] = rule_info("HtmlBlockOpenScript", "\"<\" Spnl (\"script\" | \"SCRIPT\") Spnl HtmlAttribute* \">\"")
Rules[:_HtmlBlockCloseScript] = rule_info("HtmlBlockCloseScript", "\"<\" Spnl \"/\" (\"script\" | \"SCRIPT\") Spnl \">\"")
Rules[:_HtmlBlockScript] = rule_info("HtmlBlockScript", "HtmlBlockOpenScript (!HtmlBlockCloseScript .)* HtmlBlockCloseScript")
- Rules[:_HtmlBlockInTags] = rule_info("HtmlBlockInTags", "(HtmlAnchor | HtmlBlockAddress | HtmlBlockBlockquote | HtmlBlockCenter | HtmlBlockDir | HtmlBlockDiv | HtmlBlockDl | HtmlBlockFieldset | HtmlBlockForm | HtmlBlockH1 | HtmlBlockH2 | HtmlBlockH3 | HtmlBlockH4 | HtmlBlockH5 | HtmlBlockH6 | HtmlBlockMenu | HtmlBlockNoframes | HtmlBlockNoscript | HtmlBlockOl | HtmlBlockP | HtmlBlockPre | HtmlBlockTable | HtmlBlockUl | HtmlBlockDd | HtmlBlockDt | HtmlBlockFrameset | HtmlBlockLi | HtmlBlockTbody | HtmlBlockTd | HtmlBlockTfoot | HtmlBlockTh | HtmlBlockThead | HtmlBlockTr | HtmlBlockScript)")
+ Rules[:_HtmlBlockOpenHead] = rule_info("HtmlBlockOpenHead", "\"<\" Spnl (\"head\" | \"HEAD\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseHead] = rule_info("HtmlBlockCloseHead", "\"<\" Spnl \"/\" (\"head\" | \"HEAD\") Spnl \">\"")
+ Rules[:_HtmlBlockHead] = rule_info("HtmlBlockHead", "HtmlBlockOpenHead (!HtmlBlockCloseHead .)* HtmlBlockCloseHead")
+ Rules[:_HtmlBlockInTags] = rule_info("HtmlBlockInTags", "(HtmlAnchor | HtmlBlockAddress | HtmlBlockBlockquote | HtmlBlockCenter | HtmlBlockDir | HtmlBlockDiv | HtmlBlockDl | HtmlBlockFieldset | HtmlBlockForm | HtmlBlockH1 | HtmlBlockH2 | HtmlBlockH3 | HtmlBlockH4 | HtmlBlockH5 | HtmlBlockH6 | HtmlBlockMenu | HtmlBlockNoframes | HtmlBlockNoscript | HtmlBlockOl | HtmlBlockP | HtmlBlockPre | HtmlBlockTable | HtmlBlockUl | HtmlBlockDd | HtmlBlockDt | HtmlBlockFrameset | HtmlBlockLi | HtmlBlockTbody | HtmlBlockTd | HtmlBlockTfoot | HtmlBlockTh | HtmlBlockThead | HtmlBlockTr | HtmlBlockScript | HtmlBlockHead)")
Rules[:_HtmlBlock] = rule_info("HtmlBlock", "< (HtmlBlockInTags | HtmlComment | HtmlBlockSelfClosing | HtmlUnclosed) > @BlankLine+ { if html? then RDoc::Markup::Raw.new text end }")
Rules[:_HtmlUnclosed] = rule_info("HtmlUnclosed", "\"<\" Spnl HtmlUnclosedType Spnl HtmlAttribute* Spnl \">\"")
Rules[:_HtmlUnclosedType] = rule_info("HtmlUnclosedType", "(\"HR\" | \"hr\")")
@@ -16021,14 +16191,14 @@ class RDoc::Markdown
Rules[:_InStyleTags] = rule_info("InStyleTags", "StyleOpen (!StyleClose .)* StyleClose")
Rules[:_StyleBlock] = rule_info("StyleBlock", "< InStyleTags > @BlankLine* { if css? then RDoc::Markup::Raw.new text end }")
Rules[:_Inlines] = rule_info("Inlines", "(!@Endline Inline:i { i } | @Endline:c &Inline { c })+:chunks @Endline? { chunks }")
- Rules[:_Inline] = rule_info("Inline", "(Str | @Endline | UlOrStarLine | @Space | Strong | Emph | Image | Link | NoteReference | InlineNote | Code | RawHtml | Entity | EscapedChar | Symbol)")
+ Rules[:_Inline] = rule_info("Inline", "(Str | @Endline | UlOrStarLine | @Space | Strong | Emph | Strike | Image | Link | NoteReference | InlineNote | Code | RawHtml | Entity | EscapedChar | Symbol)")
Rules[:_Space] = rule_info("Space", "@Spacechar+ { \" \" }")
Rules[:_Str] = rule_info("Str", "@StartList:a < @NormalChar+ > { a = text } (StrChunk:c { a << c })* { a }")
Rules[:_StrChunk] = rule_info("StrChunk", "< (@NormalChar | /_+/ &Alphanumeric)+ > { text }")
Rules[:_EscapedChar] = rule_info("EscapedChar", "\"\\\\\" !@Newline < /[:\\\\`|*_{}\\[\\]()\#+.!><-]/ > { text }")
Rules[:_Entity] = rule_info("Entity", "(HexEntity | DecEntity | CharEntity):a { a }")
Rules[:_Endline] = rule_info("Endline", "(@LineBreak | @TerminalEndline | @NormalEndline)")
- Rules[:_NormalEndline] = rule_info("NormalEndline", "@Sp @Newline !@BlankLine !\">\" !AtxStart !(Line /={3,}|-{3,}=/ @Newline) { \"\\n\" }")
+ Rules[:_NormalEndline] = rule_info("NormalEndline", "@Sp @Newline !@BlankLine !\">\" !AtxStart !(Line /={1,}|-{1,}/ @Newline) { \"\\n\" }")
Rules[:_TerminalEndline] = rule_info("TerminalEndline", "@Sp @Newline @Eof")
Rules[:_LineBreak] = rule_info("LineBreak", "\" \" @NormalEndline { RDoc::Markup::HardBreak.new }")
Rules[:_Symbol] = rule_info("Symbol", "< @SpecialChar > { text }")
@@ -16036,27 +16206,21 @@ class RDoc::Markdown
Rules[:_StarLine] = rule_info("StarLine", "(< /\\*{4,}/ > { text } | < @Spacechar /\\*+/ &@Spacechar > { text })")
Rules[:_UlLine] = rule_info("UlLine", "(< /_{4,}/ > { text } | < @Spacechar /_+/ &@Spacechar > { text })")
Rules[:_Emph] = rule_info("Emph", "(EmphStar | EmphUl)")
- Rules[:_OneStarOpen] = rule_info("OneStarOpen", "!StarLine \"*\" !@Spacechar !@Newline")
- Rules[:_OneStarClose] = rule_info("OneStarClose", "!@Spacechar !@Newline Inline:a \"*\" { a }")
- Rules[:_EmphStar] = rule_info("EmphStar", "OneStarOpen @StartList:a (!OneStarClose Inline:l { a << l })* OneStarClose:l { a << l } { emphasis a.join }")
- Rules[:_OneUlOpen] = rule_info("OneUlOpen", "!UlLine \"_\" !@Spacechar !@Newline")
- Rules[:_OneUlClose] = rule_info("OneUlClose", "!@Spacechar !@Newline Inline:a \"_\" { a }")
- Rules[:_EmphUl] = rule_info("EmphUl", "OneUlOpen @StartList:a (!OneUlClose Inline:l { a << l })* OneUlClose:l { a << l } { emphasis a.join }")
+ Rules[:_Whitespace] = rule_info("Whitespace", "(@Spacechar | @Newline)")
+ Rules[:_EmphStar] = rule_info("EmphStar", "\"*\" !@Whitespace @StartList:a (!\"*\" Inline:b { a << b } | StrongStar:b { a << b })+ \"*\" { emphasis a.join }")
+ Rules[:_EmphUl] = rule_info("EmphUl", "\"_\" !@Whitespace @StartList:a (!\"_\" Inline:b { a << b } | StrongUl:b { a << b })+ \"_\" { emphasis a.join }")
Rules[:_Strong] = rule_info("Strong", "(StrongStar | StrongUl)")
- Rules[:_TwoStarOpen] = rule_info("TwoStarOpen", "!StarLine \"**\" !@Spacechar !@Newline")
- Rules[:_TwoStarClose] = rule_info("TwoStarClose", "!@Spacechar !@Newline Inline:a \"**\" { a }")
- Rules[:_StrongStar] = rule_info("StrongStar", "TwoStarOpen @StartList:a (!TwoStarClose Inline:l { a << l })* TwoStarClose:l { a << l } { strong a.join }")
- Rules[:_TwoUlOpen] = rule_info("TwoUlOpen", "!UlLine \"__\" !@Spacechar !@Newline")
- Rules[:_TwoUlClose] = rule_info("TwoUlClose", "!@Spacechar !@Newline Inline:a \"__\" { a }")
- Rules[:_StrongUl] = rule_info("StrongUl", "TwoUlOpen @StartList:a (!TwoUlClose Inline:i { a << i })* TwoUlClose:l { a << l } { strong a.join }")
+ Rules[:_StrongStar] = rule_info("StrongStar", "\"**\" !@Whitespace @StartList:a (!\"**\" Inline:b { a << b })+ \"**\" { strong a.join }")
+ Rules[:_StrongUl] = rule_info("StrongUl", "\"__\" !@Whitespace @StartList:a (!\"__\" Inline:b { a << b })+ \"__\" { strong a.join }")
+ Rules[:_Strike] = rule_info("Strike", "&{ strike? } \"~~\" !@Whitespace @StartList:a (!\"~~\" Inline:b { a << b })+ \"~~\" { strike a.join }")
Rules[:_Image] = rule_info("Image", "\"!\" (ExplicitLink | ReferenceLink):a { \"rdoc-image:\#{a[/\\[(.*)\\]/, 1]}\" }")
Rules[:_Link] = rule_info("Link", "(ExplicitLink | ReferenceLink | AutoLink)")
Rules[:_ReferenceLink] = rule_info("ReferenceLink", "(ReferenceLinkDouble | ReferenceLinkSingle)")
Rules[:_ReferenceLinkDouble] = rule_info("ReferenceLinkDouble", "Label:content < Spnl > !\"[]\" Label:label { link_to content, label, text }")
Rules[:_ReferenceLinkSingle] = rule_info("ReferenceLinkSingle", "Label:content < (Spnl \"[]\")? > { link_to content, content, text }")
- Rules[:_ExplicitLink] = rule_info("ExplicitLink", "Label:l Spnl \"(\" @Sp Source:s Spnl Title @Sp \")\" { \"{\#{l}}[\#{s}]\" }")
+ Rules[:_ExplicitLink] = rule_info("ExplicitLink", "Label:l \"(\" @Sp Source:s Spnl Title @Sp \")\" { \"{\#{l}}[\#{s}]\" }")
Rules[:_Source] = rule_info("Source", "(\"<\" < SourceContents > \">\" | < SourceContents >) { text }")
- Rules[:_SourceContents] = rule_info("SourceContents", "(((!\"(\" !\")\" !\">\" Nonspacechar)+ | \"(\" SourceContents \")\")* | \"\")")
+ Rules[:_SourceContents] = rule_info("SourceContents", "((!\"(\" !\")\" !\">\" Nonspacechar)+ | \"(\" SourceContents \")\")*")
Rules[:_Title] = rule_info("Title", "(TitleSingle | TitleDouble | \"\"):a { a }")
Rules[:_TitleSingle] = rule_info("TitleSingle", "\"'\" (!(\"'\" @Sp (\")\" | @Newline)) .)* \"'\"")
Rules[:_TitleDouble] = rule_info("TitleDouble", "\"\\\"\" (!(\"\\\"\" @Sp (\")\" | @Newline)) .)* \"\\\"\"")
@@ -16088,14 +16252,13 @@ class RDoc::Markdown
Rules[:_Nonspacechar] = rule_info("Nonspacechar", "!@Spacechar !@Newline .")
Rules[:_Sp] = rule_info("Sp", "@Spacechar*")
Rules[:_Spnl] = rule_info("Spnl", "@Sp (@Newline @Sp)?")
- Rules[:_SpecialChar] = rule_info("SpecialChar", "(/[*_`&\\[\\]()<!\#\\\\'\"]/ | @ExtendedSpecialChar)")
+ Rules[:_SpecialChar] = rule_info("SpecialChar", "(/[~*_`&\\[\\]()<!\#\\\\'\"]/ | @ExtendedSpecialChar)")
Rules[:_NormalChar] = rule_info("NormalChar", "!(@SpecialChar | @Spacechar | @Newline) .")
Rules[:_Digit] = rule_info("Digit", "[0-9]")
Rules[:_Alphanumeric] = rule_info("Alphanumeric", "%literals.Alphanumeric")
Rules[:_AlphanumericAscii] = rule_info("AlphanumericAscii", "%literals.AlphanumericAscii")
Rules[:_BOM] = rule_info("BOM", "%literals.BOM")
Rules[:_Newline] = rule_info("Newline", "%literals.Newline")
- Rules[:_NonAlphanumeric] = rule_info("NonAlphanumeric", "%literals.NonAlphanumeric")
Rules[:_Spacechar] = rule_info("Spacechar", "%literals.Spacechar")
Rules[:_HexEntity] = rule_info("HexEntity", "/&\#x/i < /[0-9a-fA-F]+/ > \";\" { [text.to_i(16)].pack 'U' }")
Rules[:_DecEntity] = rule_info("DecEntity", "\"&\#\" < /[0-9]+/ > \";\" { [text.to_i].pack 'U' }")
@@ -16112,10 +16275,10 @@ class RDoc::Markdown
Rules[:_NoteReference] = rule_info("NoteReference", "&{ notes? } RawNoteReference:ref { note_for ref }")
Rules[:_RawNoteReference] = rule_info("RawNoteReference", "\"[^\" < (!@Newline !\"]\" .)+ > \"]\" { text }")
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[:_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[:_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 verbatim }")
+ 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[:_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/entities.rb b/lib/rdoc/markdown/entities.rb
index d32ae51053..d2cf610293 100644
--- a/lib/rdoc/markdown/entities.rb
+++ b/lib/rdoc/markdown/entities.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# HTML entity name map for RDoc::Markdown
diff --git a/lib/rdoc/markdown/literals.rb b/lib/rdoc/markdown/literals.rb
index b6bb89e0c6..31cd237f12 100644
--- a/lib/rdoc/markdown/literals.rb
+++ b/lib/rdoc/markdown/literals.rb
@@ -1,5 +1,5 @@
# coding: UTF-8
-# frozen_string_literal: false
+# frozen_string_literal: true
# :markup: markdown
##
@@ -183,14 +183,26 @@ class RDoc::Markdown::Literals
return nil
end
- def get_byte
- if @pos >= @string_size
- return nil
+ if "".respond_to? :ord
+ def get_byte
+ if @pos >= @string_size
+ return nil
+ end
+
+ s = @string[@pos].ord
+ @pos += 1
+ s
end
+ else
+ def get_byte
+ if @pos >= @string_size
+ return nil
+ end
- s = @string[@pos].ord
- @pos += 1
- s
+ s = @string[@pos]
+ @pos += 1
+ s
+ end
end
def parse(rule=nil)
diff --git a/lib/rdoc/markup.rb b/lib/rdoc/markup.rb
index 3406522596..f20ee1169e 100644
--- a/lib/rdoc/markup.rb
+++ b/lib/rdoc/markup.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# RDoc::Markup parses plain text documents and attempts to decompose them into
# their constituent parts. Some of these parts are high-level: paragraphs,
@@ -65,17 +65,16 @@
# puts h.convert(input_string)
#
# You can extend the RDoc::Markup parser to recognize new markup
-# sequences, and to add special processing for text that matches a
-# regular expression. Here we make WikiWords significant to the parser,
-# and also make the sequences {word} and \<no>text...</no> signify
+# sequences, and to add regexp handling. Here we make WikiWords significant to
+# the parser, and also make the sequences {word} and \<no>text...</no> signify
# strike-through text. We then subclass the HTML output class to deal
# with these:
#
# require 'rdoc'
#
# class WikiHtml < RDoc::Markup::ToHtml
-# def handle_special_WIKIWORD(special)
-# "<font color=red>" + special.text + "</font>"
+# def handle_regexp_WIKIWORD(target)
+# "<font color=red>" + target.text + "</font>"
# end
# end
#
@@ -83,7 +82,7 @@
# markup.add_word_pair("{", "}", :STRIKE)
# markup.add_html("no", :STRIKE)
#
-# markup.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
+# markup.add_regexp_handling(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
#
# wh = WikiHtml.new RDoc::Options.new, markup
# wh.add_tag(:STRIKE, "<strike>", "</strike>")
@@ -377,7 +376,7 @@
#
# Example links:
#
-# https://github.com/rdoc/rdoc
+# https://github.com/ruby/rdoc
# mailto:user@example.com
# {RDoc Documentation}[http://rdoc.rubyforge.org]
# {RDoc Markup}[rdoc-ref:RDoc::Markup]
@@ -764,7 +763,7 @@ Ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} #{RUBY_RELEASE_DATE}
Please file a bug report with the above information at:
-https://github.com/rdoc/rdoc/issues
+https://github.com/ruby/rdoc/issues
EOF
raise
@@ -800,13 +799,12 @@ https://github.com/rdoc/rdoc/issues
# Add to other inline sequences. For example, we could add WikiWords using
# something like:
#
- # parser.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
+ # parser.add_regexp_handling(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
#
- # Each wiki word will be presented to the output formatter via the
- # accept_special method.
+ # Each wiki word will be presented to the output formatter.
- def add_special(pattern, name)
- @attribute_manager.add_special(pattern, name)
+ def add_regexp_handling(pattern, name)
+ @attribute_manager.add_regexp_handling(pattern, name)
end
##
@@ -832,7 +830,7 @@ https://github.com/rdoc/rdoc/issues
autoload :AttrSpan, 'rdoc/markup/attr_span'
autoload :Attributes, 'rdoc/markup/attributes'
autoload :AttributeManager, 'rdoc/markup/attribute_manager'
- autoload :Special, 'rdoc/markup/special'
+ autoload :RegexpHandling, 'rdoc/markup/regexp_handling'
# RDoc::Markup AST
autoload :BlankLine, 'rdoc/markup/blank_line'
diff --git a/lib/rdoc/markup/attr_changer.rb b/lib/rdoc/markup/attr_changer.rb
index 9a1a9c8c66..4c4bc6479e 100644
--- a/lib/rdoc/markup/attr_changer.rb
+++ b/lib/rdoc/markup/attr_changer.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
class RDoc::Markup
AttrChanger = Struct.new :turn_on, :turn_off # :nodoc:
diff --git a/lib/rdoc/markup/attr_span.rb b/lib/rdoc/markup/attr_span.rb
index 4d9e5b0217..63aace60d2 100644
--- a/lib/rdoc/markup/attr_span.rb
+++ b/lib/rdoc/markup/attr_span.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# An array of attributes which parallels the characters in a string.
diff --git a/lib/rdoc/markup/attribute_manager.rb b/lib/rdoc/markup/attribute_manager.rb
index 3296d17af2..f052bc8b01 100644
--- a/lib/rdoc/markup/attribute_manager.rb
+++ b/lib/rdoc/markup/attribute_manager.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Manages changes of attributes in a block of text
@@ -53,10 +53,10 @@ class RDoc::Markup::AttributeManager
attr_reader :protectable
##
- # And this maps _special_ sequences to a name. A special sequence is
- # something like a WikiWord
+ # And this maps _regexp handling_ sequences to a name. A regexp handling
+ # sequence is something like a WikiWord
- attr_reader :special
+ attr_reader :regexp_handlings
##
# Creates a new attribute manager that understands bold, emphasized and
@@ -66,7 +66,7 @@ class RDoc::Markup::AttributeManager
@html_tags = {}
@matching_word_pairs = {}
@protectable = %w[<]
- @special = []
+ @regexp_handlings = []
@word_pair_map = {}
@attributes = RDoc::Markup::Attributes.new
@@ -166,22 +166,22 @@ class RDoc::Markup::AttributeManager
end
##
- # Converts special sequences to RDoc attributes
+ # Converts regexp handling sequences to RDoc attributes
- def convert_specials str, attrs
- @special.each do |regexp, attribute|
+ def convert_regexp_handlings str, attrs
+ @regexp_handlings.each do |regexp, attribute|
str.scan(regexp) do
capture = $~.size == 1 ? 0 : 1
s, e = $~.offset capture
- attrs.set_attrs s, e - s, attribute | @attributes.special
+ attrs.set_attrs s, e - s, attribute | @attributes.regexp_handling
end
end
end
##
- # Escapes special sequences of text to prevent conversion to RDoc
+ # Escapes regexp handling sequences of text to prevent conversion to RDoc
def mask_protected_sequences
# protect __send__, __FILE__, etc.
@@ -193,7 +193,7 @@ class RDoc::Markup::AttributeManager
end
##
- # Unescapes special sequences of text
+ # Unescapes regexp handling sequences of text
def unmask_protected_sequences
@str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000")
@@ -233,28 +233,28 @@ class RDoc::Markup::AttributeManager
end
##
- # Adds a special handler for +pattern+ with +name+. A simple URL handler
+ # Adds a regexp handling for +pattern+ with +name+. A simple URL handler
# would be:
#
- # @am.add_special(/((https?:)\S+\w)/, :HYPERLINK)
+ # @am.add_regexp_handling(/((https?:)\S+\w)/, :HYPERLINK)
- def add_special pattern, name
- @special << [pattern, @attributes.bitmap_for(name)]
+ def add_regexp_handling pattern, name
+ @regexp_handlings << [pattern, @attributes.bitmap_for(name)]
end
##
- # Processes +str+ converting attributes, HTML and specials
+ # Processes +str+ converting attributes, HTML and regexp handlings
def flow str
- @str = str
+ @str = str.dup
mask_protected_sequences
@attrs = RDoc::Markup::AttrSpan.new @str.length
- convert_attrs @str, @attrs
- convert_html @str, @attrs
- convert_specials @str, @attrs
+ convert_attrs @str, @attrs
+ convert_html @str, @attrs
+ convert_regexp_handlings @str, @attrs
unmask_protected_sequences
@@ -312,12 +312,12 @@ class RDoc::Markup::AttributeManager
res << change_attribute(current_attr, new_attr)
current_attr = new_attr
- if (current_attr & @attributes.special) != 0 then
+ if (current_attr & @attributes.regexp_handling) != 0 then
i += 1 while
- i < str_len and (@attrs[i] & @attributes.special) != 0
+ i < str_len and (@attrs[i] & @attributes.regexp_handling) != 0
- res << RDoc::Markup::Special.new(current_attr,
- copy_string(start_pos, i))
+ res << RDoc::Markup::RegexpHandling.new(current_attr,
+ copy_string(start_pos, i))
start_pos = i
next
end
diff --git a/lib/rdoc/markup/attributes.rb b/lib/rdoc/markup/attributes.rb
index 8776c4ed29..ce014ce928 100644
--- a/lib/rdoc/markup/attributes.rb
+++ b/lib/rdoc/markup/attributes.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# We manage a set of attributes. Each attribute has a symbol name and a bit
# value.
@@ -6,21 +6,21 @@
class RDoc::Markup::Attributes
##
- # The special attribute type. See RDoc::Markup#add_special
+ # The regexp handling attribute type. See RDoc::Markup#add_regexp_handling
- attr_reader :special
+ attr_reader :regexp_handling
##
# Creates a new attributes set.
def initialize
- @special = 1
+ @regexp_handling = 1
@name_to_bitmap = [
- [:_SPECIAL_, @special],
+ [:_REGEXP_HANDLING_, @regexp_handling],
]
- @next_bitmap = @special << 1
+ @next_bitmap = @regexp_handling << 1
end
##
@@ -61,7 +61,7 @@ class RDoc::Markup::Attributes
return enum_for __method__, bitmap unless block_given?
@name_to_bitmap.each do |name, bit|
- next if bit == @special
+ next if bit == @regexp_handling
yield name.to_s if (bitmap & bit) != 0
end
diff --git a/lib/rdoc/markup/blank_line.rb b/lib/rdoc/markup/blank_line.rb
index 2b1ab91b47..3129ab5e7f 100644
--- a/lib/rdoc/markup/blank_line.rb
+++ b/lib/rdoc/markup/blank_line.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# An empty line. This class is a singleton.
diff --git a/lib/rdoc/markup/block_quote.rb b/lib/rdoc/markup/block_quote.rb
index 3be022f9dd..7a4b3e36b0 100644
--- a/lib/rdoc/markup/block_quote.rb
+++ b/lib/rdoc/markup/block_quote.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A quoted section which contains markup items.
diff --git a/lib/rdoc/markup/document.rb b/lib/rdoc/markup/document.rb
index 0692c3522f..f3a5de1fc3 100644
--- a/lib/rdoc/markup/document.rb
+++ b/lib/rdoc/markup/document.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A Document containing lists, headings, paragraphs, etc.
diff --git a/lib/rdoc/markup/formatter.rb b/lib/rdoc/markup/formatter.rb
index 197ff003e3..6dff96c7d0 100644
--- a/lib/rdoc/markup/formatter.rb
+++ b/lib/rdoc/markup/formatter.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Base class for RDoc markup formatters
#
@@ -50,7 +50,7 @@ class RDoc::Markup::Formatter
@markup = markup || RDoc::Markup.new
@am = @markup.attribute_manager
- @am.add_special(/<br>/, :HARD_BREAK)
+ @am.add_regexp_handling(/<br>/, :HARD_BREAK)
@attributes = @am.attributes
@@ -78,23 +78,24 @@ class RDoc::Markup::Formatter
end
##
- # Adds a special for links of the form rdoc-...:
+ # Adds a regexp handling for links of the form rdoc-...:
- def add_special_RDOCLINK
- @markup.add_special(/rdoc-[a-z]+:[^\s\]]+/, :RDOCLINK)
+ def add_regexp_handling_RDOCLINK
+ @markup.add_regexp_handling(/rdoc-[a-z]+:[^\s\]]+/, :RDOCLINK)
end
##
- # Adds a special for links of the form {<text>}[<url>] and <word>[<url>]
+ # Adds a regexp handling for links of the form {<text>}[<url>] and
+ # <word>[<url>]
- def add_special_TIDYLINK
- @markup.add_special(/(?:
- \{.*?\} | # multi-word label
- \b[^\s{}]+? # single-word label
- )
+ def add_regexp_handling_TIDYLINK
+ @markup.add_regexp_handling(/(?:
+ \{.*?\} | # multi-word label
+ \b[^\s{}]+? # single-word label
+ )
- \[\S+?\] # link target
- /x, :TIDYLINK)
+ \[\S+?\] # link target
+ /x, :TIDYLINK)
end
##
@@ -133,8 +134,8 @@ class RDoc::Markup::Formatter
when RDoc::Markup::AttrChanger then
off_tags res, item
on_tags res, item
- when RDoc::Markup::Special then
- res << convert_special(item)
+ when RDoc::Markup::RegexpHandling then
+ res << convert_regexp_handling(item)
else
raise "Unknown flow element: #{item.inspect}"
end
@@ -144,29 +145,29 @@ class RDoc::Markup::Formatter
end
##
- # Converts added specials. See RDoc::Markup#add_special
+ # Converts added regexp handlings. See RDoc::Markup#add_regexp_handling
- def convert_special special
- return special.text if in_tt?
+ def convert_regexp_handling target
+ return target.text if in_tt?
handled = false
- @attributes.each_name_of special.type do |name|
- method_name = "handle_special_#{name}"
+ @attributes.each_name_of target.type do |name|
+ method_name = "handle_regexp_#{name}"
if respond_to? method_name then
- special.text = send method_name, special
+ target.text = send method_name, target
handled = true
end
end
unless handled then
- special_name = @attributes.as_string special.type
+ target_name = @attributes.as_string target.type
- raise RDoc::Error, "Unhandled special #{special_name}: #{special}"
+ raise RDoc::Error, "Unhandled regexp handling #{target_name}: #{target}"
end
- special.text
+ target.text
end
##
diff --git a/lib/rdoc/markup/formatter_test_case.rb b/lib/rdoc/markup/formatter_test_case.rb
index a5b4933de4..076b7c81bb 100644
--- a/lib/rdoc/markup/formatter_test_case.rb
+++ b/lib/rdoc/markup/formatter_test_case.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'minitest/unit'
##
diff --git a/lib/rdoc/markup/hard_break.rb b/lib/rdoc/markup/hard_break.rb
index 5898bfb644..046068d5c2 100644
--- a/lib/rdoc/markup/hard_break.rb
+++ b/lib/rdoc/markup/hard_break.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A hard-break in the middle of a paragraph.
diff --git a/lib/rdoc/markup/heading.rb b/lib/rdoc/markup/heading.rb
index 5229287d5b..93a3a52000 100644
--- a/lib/rdoc/markup/heading.rb
+++ b/lib/rdoc/markup/heading.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A heading with a level (1-6) and text
@@ -23,12 +23,12 @@ RDoc::Markup::Heading =
return @to_html if @to_html
markup = RDoc::Markup.new
- markup.add_special RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
+ markup.add_regexp_handling RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
@to_html = RDoc::Markup::ToHtml.new nil
- def @to_html.handle_special_CROSSREF special
- special.text.sub(/^\\/, '')
+ def @to_html.handle_regexp_CROSSREF target
+ target.text.sub(/^\\/, '')
end
@to_html
diff --git a/lib/rdoc/markup/include.rb b/lib/rdoc/markup/include.rb
index 891be71b85..ad7c4a9640 100644
--- a/lib/rdoc/markup/include.rb
+++ b/lib/rdoc/markup/include.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A file included at generation time. Objects of this class are created by
# RDoc::RD for an extension-less include.
diff --git a/lib/rdoc/markup/indented_paragraph.rb b/lib/rdoc/markup/indented_paragraph.rb
index 56a96bd3c9..d42b2e52b8 100644
--- a/lib/rdoc/markup/indented_paragraph.rb
+++ b/lib/rdoc/markup/indented_paragraph.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# An Indented Paragraph of text
diff --git a/lib/rdoc/markup/inline.rb b/lib/rdoc/markup/inline.rb
deleted file mode 100644
index 58072fef06..0000000000
--- a/lib/rdoc/markup/inline.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-# frozen_string_literal: false
-warn "requiring rdoc/markup/inline is deprecated and will be removed in RDoc 4." if $-w
diff --git a/lib/rdoc/markup/list.rb b/lib/rdoc/markup/list.rb
index bcaea7d7c1..05c3609202 100644
--- a/lib/rdoc/markup/list.rb
+++ b/lib/rdoc/markup/list.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A List is a homogeneous set of ListItems.
#
diff --git a/lib/rdoc/markup/list_item.rb b/lib/rdoc/markup/list_item.rb
index 115ec0412c..d22554ee73 100644
--- a/lib/rdoc/markup/list_item.rb
+++ b/lib/rdoc/markup/list_item.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# An item within a List that contains paragraphs, headings, etc.
#
diff --git a/lib/rdoc/markup/paragraph.rb b/lib/rdoc/markup/paragraph.rb
index fefa12f9ef..a2e45ef009 100644
--- a/lib/rdoc/markup/paragraph.rb
+++ b/lib/rdoc/markup/paragraph.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A Paragraph of text
diff --git a/lib/rdoc/markup/parser.rb b/lib/rdoc/markup/parser.rb
index 22cca20420..14f1f6c719 100644
--- a/lib/rdoc/markup/parser.rb
+++ b/lib/rdoc/markup/parser.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'strscan'
##
@@ -9,8 +9,9 @@ require 'strscan'
# RDoc::Markup::ToHTML.
#
# The parser only handles the block-level constructs Paragraph, List,
-# ListItem, Heading, Verbatim, BlankLine and Rule. Inline markup such as
-# <tt>\+blah\+</tt> is handled separately by RDoc::Markup::AttributeManager.
+# ListItem, Heading, Verbatim, BlankLine, Rule and BlockQuote.
+# Inline markup such as <tt>\+blah\+</tt> is handled separately by
+# RDoc::Markup::AttributeManager.
#
# To see what markup the Parser implements read RDoc. To see how to use
# RDoc markup to format text in your program read RDoc::Markup.
@@ -249,7 +250,7 @@ class RDoc::Markup::Parser
min_indent = nil
generate_leading_spaces = true
- line = ''
+ line = ''.dup
until @tokens.empty? do
type, data, column, = get
@@ -257,7 +258,7 @@ class RDoc::Markup::Parser
if type == :NEWLINE then
line << data
verbatim << line
- line = ''
+ line = ''.dup
generate_leading_spaces = true
next
end
@@ -381,6 +382,17 @@ class RDoc::Markup::Parser
when :TEXT then
unget
parse_text parent, indent
+ when :BLOCKQUOTE then
+ type, _, column = get
+ if type == :NEWLINE
+ type, _, column = get
+ end
+ unget if type
+ bq = RDoc::Markup::BlockQuote.new
+ p :blockquote_start => [data, column] if @debug
+ parse bq, column
+ p :blockquote_end => indent if @debug
+ parent << bq
when *LIST_TOKENS then
unget
parent << build_list(indent)
@@ -504,8 +516,12 @@ class RDoc::Markup::Parser
# text:: followed by spaces or end of line => :NOTE
when @s.scan(/(.*?)::( +|\r?$)/) then
[:NOTE, @s[1], *token_pos(pos)]
+ # >>> followed by end of line => :BLOCKQUOTE
+ when @s.scan(/>>> *(\w+)?$/) then
+ [:BLOCKQUOTE, @s[1], *token_pos(pos)]
# anything else: :TEXT
- else @s.scan(/(.*?)( )?\r?$/)
+ else
+ @s.scan(/(.*?)( )?\r?$/)
token = [:TEXT, @s[1], *token_pos(pos)]
if @s[2] then
diff --git a/lib/rdoc/markup/pre_process.rb b/lib/rdoc/markup/pre_process.rb
index 6ce523be89..d9e0dcac14 100644
--- a/lib/rdoc/markup/pre_process.rb
+++ b/lib/rdoc/markup/pre_process.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Handle common directives that can occur in a block of text:
#
@@ -105,7 +105,7 @@ class RDoc::Markup::PreProcess
# regexp helper (square brackets for optional)
# $1 $2 $3 $4 $5
# [prefix][\]:directive:[spaces][param]newline
- text.gsub!(/^([ \t]*(?:#|\/?\*)?[ \t]*)(\\?):(\w+):([ \t]*)(.+)?(\r?\n|$)/) do
+ text = text.gsub(/^([ \t]*(?:#|\/?\*)?[ \t]*)(\\?):(\w+):([ \t]*)(.+)?(\r?\n|$)/) do
# skip something like ':toto::'
next $& if $4.empty? and $5 and $5[0, 1] == ':'
@@ -123,7 +123,11 @@ class RDoc::Markup::PreProcess
handle_directive $1, $3, $5, code_object, text.encoding, &block
end
- comment = text unless comment
+ if comment then
+ comment.text = text
+ else
+ comment = text
+ end
self.class.post_processors.each do |handler|
handler.call comment, code_object
@@ -150,7 +154,7 @@ class RDoc::Markup::PreProcess
case directive
when 'arg', 'args' then
- return "#{prefix}:#{directive}: #{param}\n" unless code_object
+ return "#{prefix}:#{directive}: #{param}\n" unless code_object && code_object.kind_of?(RDoc::AnyMethod)
code_object.params = param
@@ -212,7 +216,7 @@ class RDoc::Markup::PreProcess
when 'yield', 'yields' then
return blankline unless code_object
# remove parameter &block
- code_object.params.sub!(/,?\s*&\w+/, '') if code_object.params
+ code_object.params = code_object.params.sub(/,?\s*&\w+/, '') if code_object.params
code_object.block_params = param
@@ -262,6 +266,7 @@ class RDoc::Markup::PreProcess
end
content = RDoc::Encoding.read_file full_name, encoding, true
+ content = RDoc::Encoding.remove_magic_comment content
# strip magic comment
content = content.sub(/\A# .*coding[=:].*$/, '').lstrip
diff --git a/lib/rdoc/markup/raw.rb b/lib/rdoc/markup/raw.rb
index 8012d2cea6..85e2c8b825 100644
--- a/lib/rdoc/markup/raw.rb
+++ b/lib/rdoc/markup/raw.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A section of text that is added to the output document as-is
diff --git a/lib/rdoc/markup/regexp_handling.rb b/lib/rdoc/markup/regexp_handling.rb
new file mode 100644
index 0000000000..6ed868c2c1
--- /dev/null
+++ b/lib/rdoc/markup/regexp_handling.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+##
+# Hold details of a regexp handling sequence
+
+class RDoc::Markup::RegexpHandling
+
+ ##
+ # Regexp handling type
+
+ attr_reader :type
+
+ ##
+ # Regexp handling text
+
+ attr_accessor :text
+
+ ##
+ # Creates a new regexp handling sequence of +type+ with +text+
+
+ def initialize(type, text)
+ @type, @text = type, text
+ end
+
+ ##
+ # Regexp handlings are equal when the have the same text and type
+
+ def ==(o)
+ self.text == o.text && self.type == o.type
+ end
+
+ def inspect # :nodoc:
+ "#<RDoc::Markup::RegexpHandling:0x%x @type=%p, @text=%p>" % [
+ object_id, @type, text.dump]
+ end
+
+ def to_s # :nodoc:
+ "RegexpHandling: type=#{type} text=#{text.dump}"
+ end
+
+end
+
diff --git a/lib/rdoc/markup/rule.rb b/lib/rdoc/markup/rule.rb
index b96d4fb293..38c1dc7f56 100644
--- a/lib/rdoc/markup/rule.rb
+++ b/lib/rdoc/markup/rule.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A horizontal rule with a weight
diff --git a/lib/rdoc/markup/special.rb b/lib/rdoc/markup/special.rb
deleted file mode 100644
index 4d834b9e37..0000000000
--- a/lib/rdoc/markup/special.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# frozen_string_literal: false
-##
-# Hold details of a special sequence
-
-class RDoc::Markup::Special
-
- ##
- # Special type
-
- attr_reader :type
-
- ##
- # Special text
-
- attr_accessor :text
-
- ##
- # Creates a new special sequence of +type+ with +text+
-
- def initialize(type, text)
- @type, @text = type, text
- end
-
- ##
- # Specials are equal when the have the same text and type
-
- def ==(o)
- self.text == o.text && self.type == o.type
- end
-
- def inspect # :nodoc:
- "#<RDoc::Markup::Special:0x%x @type=%p, @text=%p>" % [
- object_id, @type, text.dump]
- end
-
- def to_s # :nodoc:
- "Special: type=#{type} text=#{text.dump}"
- end
-
-end
-
diff --git a/lib/rdoc/markup/text_formatter_test_case.rb b/lib/rdoc/markup/text_formatter_test_case.rb
index 1c8882aa36..22a762b5f0 100644
--- a/lib/rdoc/markup/text_formatter_test_case.rb
+++ b/lib/rdoc/markup/text_formatter_test_case.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Test case for creating new plain-text RDoc::Markup formatters. See also
# RDoc::Markup::FormatterTestCase
diff --git a/lib/rdoc/markup/to_ansi.rb b/lib/rdoc/markup/to_ansi.rb
index 56cd1fe446..6cc3b70e93 100644
--- a/lib/rdoc/markup/to_ansi.rb
+++ b/lib/rdoc/markup/to_ansi.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Outputs RDoc markup with vibrant ANSI color!
diff --git a/lib/rdoc/markup/to_bs.rb b/lib/rdoc/markup/to_bs.rb
index 9a1c7c6ccd..f9b86487db 100644
--- a/lib/rdoc/markup/to_bs.rb
+++ b/lib/rdoc/markup/to_bs.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Outputs RDoc markup with hot backspace action! You will probably need a
# pager to use this output format.
@@ -41,7 +41,7 @@ class RDoc::Markup::ToBs < RDoc::Markup::ToRdoc
end
##
- # Turns on or off special handling for +convert_string+
+ # Turns on or off regexp handling for +convert_string+
def annotate tag
case tag
@@ -54,9 +54,9 @@ class RDoc::Markup::ToBs < RDoc::Markup::ToRdoc
end
##
- # Calls convert_string on the result of convert_special
+ # Calls convert_string on the result of convert_regexp_handling
- def convert_special special
+ def convert_regexp_handling target
convert_string super
end
diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb
index 3bf66c2c31..9ae0fff8a7 100644
--- a/lib/rdoc/markup/to_html.rb
+++ b/lib/rdoc/markup/to_html.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'cgi'
##
@@ -53,18 +53,18 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
@hard_break = "<br>\n"
# external links
- @markup.add_special(/(?:link:|https?:|mailto:|ftp:|irc:|www\.)\S+\w/,
- :HYPERLINK)
+ @markup.add_regexp_handling(/(?:link:|https?:|mailto:|ftp:|irc:|www\.)\S+\w/,
+ :HYPERLINK)
- add_special_RDOCLINK
- add_special_TIDYLINK
+ add_regexp_handling_RDOCLINK
+ add_regexp_handling_TIDYLINK
init_tags
end
- # :section: Special Handling
+ # :section: Regexp Handling
#
- # These methods handle special markup added by RDoc::Markup#add_special.
+ # These methods are used by regexp handling markup added by RDoc::Markup#add_regexp_handling.
def handle_RDOCLINK url # :nodoc:
case url
@@ -91,14 +91,14 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
end
##
- # +special+ is a <code><br></code>
+ # +target+ is a <code><br></code>
- def handle_special_HARD_BREAK special
+ def handle_regexp_HARD_BREAK target
'<br>'
end
##
- # +special+ is a potential link. The following schemes are handled:
+ # +target+ is a potential link. The following schemes are handled:
#
# <tt>mailto:</tt>::
# Inserted as-is.
@@ -109,14 +109,14 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
# <tt>link:</tt>::
# Reference to a local file relative to the output directory.
- def handle_special_HYPERLINK(special)
- url = special.text
+ def handle_regexp_HYPERLINK(target)
+ url = target.text
gen_url url, url
end
##
- # +special+ is an rdoc-schemed link that will be converted into a hyperlink.
+ # +target+ is an rdoc-schemed link that will be converted into a hyperlink.
#
# For the +rdoc-ref+ scheme the named reference will be returned without
# creating a link.
@@ -124,16 +124,16 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
# For the +rdoc-label+ scheme the footnote and label prefixes are stripped
# when creating a link. All other contents will be linked verbatim.
- def handle_special_RDOCLINK special
- handle_RDOCLINK special.text
+ def handle_regexp_RDOCLINK target
+ handle_RDOCLINK target.text
end
##
- # This +special+ is a link where the label is different from the URL
+ # This +target+ is a link where the label is different from the URL
# <tt>label[url]</tt> or <tt>{long label}[url]</tt>
- def handle_special_TIDYLINK(special)
- text = special.text
+ def handle_regexp_TIDYLINK(target)
+ text = target.text
return text unless
text =~ /^\{(.*)\}\[(.*?)\]$/ or text =~ /^(\S+)\[(.*?)\]$/
@@ -186,7 +186,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
@res << "\n<p>"
text = paragraph.text @hard_break
text = text.gsub(/\r?\n/, ' ')
- @res << wrap(to_html(text))
+ @res << to_html(text)
@res << "</p>\n"
end
@@ -200,11 +200,13 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
content = if verbatim.ruby? or parseable? text then
begin
- tokens = RDoc::RubyLex.tokenize text, @options
+ tokens = RDoc::Parser::RipperStateLex.parse text
klass = ' class="ruby"'
- RDoc::TokenStream.to_html tokens
- rescue RDoc::RubyLex::Error
+ result = RDoc::TokenStream.to_html tokens
+ result = result + "\n" unless "\n" == result[-1]
+ result
+ rescue
CGI.escapeHTML text
end
else
@@ -212,7 +214,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
end
if @options.pipe then
- @res << "\n<pre><code>#{CGI.escapeHTML text}</code></pre>\n"
+ @res << "\n<pre><code>#{CGI.escapeHTML text}\n</code></pre>\n"
else
@res << "\n<pre#{klass}>#{content}</pre>\n"
end
@@ -310,7 +312,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
##
# Generate a link to +url+ with content +text+. Handles the special cases
- # for img: and link: described under handle_special_HYPERLINK
+ # for img: and link: described under handle_regexp_HYPERLINK
def gen_url url, text
scheme, url, id = parse_url url
@@ -383,9 +385,12 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
# Returns true if text is valid ruby syntax
def parseable? text
+ verbose, $VERBOSE = $VERBOSE, nil
eval("BEGIN {return true}\n#{text}")
rescue SyntaxError
false
+ ensure
+ $VERBOSE = verbose
end
##
diff --git a/lib/rdoc/markup/to_html_crossref.rb b/lib/rdoc/markup/to_html_crossref.rb
index 3f03c65898..2fbddeb83b 100644
--- a/lib/rdoc/markup/to_html_crossref.rb
+++ b/lib/rdoc/markup/to_html_crossref.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Subclass of the RDoc::Markup::ToHtml class that supports looking up method
# names, classes, etc to create links. RDoc::CrossReference is used to
@@ -40,7 +40,7 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
@show_hash = @options.show_hash
crossref_re = @hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP
- @markup.add_special crossref_re, :CROSSREF
+ @markup.add_regexp_handling crossref_re, :CROSSREF
@cross_reference = RDoc::CrossReference.new @context
end
@@ -49,16 +49,19 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# Creates a link to the reference +name+ if the name exists. If +text+ is
# given it is used as the link text, otherwise +name+ is used.
- def cross_reference name, text = nil
+ def cross_reference name, text = nil, code = true
lookup = name
name = name[1..-1] unless @show_hash if name[0, 1] == '#'
- name = "#{CGI.unescape $'} at #{$1}" if name =~ /(.*[^#:])@/
-
- text = name unless text
+ if name =~ /(.*[^#:])@/
+ text ||= "#{CGI.unescape $'} at <code>#{$1}</code>"
+ code = false
+ else
+ text ||= name
+ end
- link lookup, text
+ link lookup, text, code
end
##
@@ -68,8 +71,8 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# example, ToHtml is found, even without the <tt>RDoc::Markup::</tt> prefix,
# because we look for it in module Markup first.
- def handle_special_CROSSREF(special)
- name = special.text
+ def handle_regexp_CROSSREF(target)
+ name = target.text
return name if name =~ /@[\w-]+\.[\w-]/ # labels that look like emails
@@ -87,22 +90,22 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# Handles <tt>rdoc-ref:</tt> scheme links and allows RDoc::Markup::ToHtml to
# handle other schemes.
- def handle_special_HYPERLINK special
- return cross_reference $' if special.text =~ /\Ardoc-ref:/
+ def handle_regexp_HYPERLINK target
+ return cross_reference $' if target.text =~ /\Ardoc-ref:/
super
end
##
- # +special+ is an rdoc-schemed link that will be converted into a hyperlink.
+ # +target+ is an rdoc-schemed link that will be converted into a hyperlink.
# For the rdoc-ref scheme the cross-reference will be looked up and the
# given name will be used.
#
# All other contents are handled by
- # {the superclass}[rdoc-ref:RDoc::Markup::ToHtml#handle_special_RDOCLINK]
+ # {the superclass}[rdoc-ref:RDoc::Markup::ToHtml#handle_regexp_RDOCLINK]
- def handle_special_RDOCLINK special
- url = special.text
+ def handle_regexp_RDOCLINK target
+ url = target.text
case url
when /\Ardoc-ref:/ then
@@ -119,15 +122,14 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
def gen_url url, text
return super unless url =~ /\Ardoc-ref:/
- cross_reference $', text
+ name = $'
+ cross_reference name, text, name == text
end
##
# Creates an HTML link to +name+ with the given +text+.
- def link name, text
- original_name = name
-
+ def link name, text, code = true
if name =~ /(.*[^#:])@/ then
name = $1
label = $'
@@ -135,15 +137,16 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
ref = @cross_reference.resolve name, text
- text = ref.output_name @context if
- RDoc::MethodAttr === ref and text == original_name
-
case ref
when String then
ref
else
path = ref.as_href @from_path
+ if code and RDoc::CodeObject === ref and !(RDoc::TopLevel === ref)
+ text = "<code>#{text}</code>"
+ end
+
if path =~ /#/ then
path << "-label-#{label}"
elsif ref.sections and
diff --git a/lib/rdoc/markup/to_html_snippet.rb b/lib/rdoc/markup/to_html_snippet.rb
index 75c1df94d9..4eb36592b7 100644
--- a/lib/rdoc/markup/to_html_snippet.rb
+++ b/lib/rdoc/markup/to_html_snippet.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Outputs RDoc markup as paragraphs with inline markup only.
@@ -44,7 +44,7 @@ class RDoc::Markup::ToHtmlSnippet < RDoc::Markup::ToHtml
@mask = 0
@paragraphs = 0
- @markup.add_special RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
+ @markup.add_regexp_handling RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
end
##
@@ -71,7 +71,7 @@ class RDoc::Markup::ToHtmlSnippet < RDoc::Markup::ToHtml
text = paragraph.text @hard_break
- @res << "#{para}#{wrap to_html text}\n"
+ @res << "#{para}#{to_html text}\n"
add_paragraph
end
@@ -123,16 +123,16 @@ class RDoc::Markup::ToHtmlSnippet < RDoc::Markup::ToHtml
end
##
- # Removes escaping from the cross-references in +special+
+ # Removes escaping from the cross-references in +target+
- def handle_special_CROSSREF special
- special.text.sub(/\A\\/, '')
+ def handle_regexp_CROSSREF target
+ target.text.sub(/\A\\/, '')
end
##
- # +special+ is a <code><br></code>
+ # +target+ is a <code><br></code>
- def handle_special_HARD_BREAK special
+ def handle_regexp_HARD_BREAK target
@characters -= 4
'<br>'
end
@@ -226,8 +226,8 @@ class RDoc::Markup::ToHtmlSnippet < RDoc::Markup::ToHtml
when String then
text = convert_string item
res << truncate(text)
- when RDoc::Markup::Special then
- text = convert_special item
+ when RDoc::Markup::RegexpHandling then
+ text = convert_regexp_handling item
res << truncate(text)
else
raise "Unknown flow element: #{item.inspect}"
diff --git a/lib/rdoc/markup/to_joined_paragraph.rb b/lib/rdoc/markup/to_joined_paragraph.rb
index 293258c092..795f3f62ee 100644
--- a/lib/rdoc/markup/to_joined_paragraph.rb
+++ b/lib/rdoc/markup/to_joined_paragraph.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Joins the parts of an RDoc::Markup::Paragraph into a single String.
#
@@ -23,37 +23,11 @@ class RDoc::Markup::ToJoinedParagraph < RDoc::Markup::Formatter
# Converts the parts of +paragraph+ to a single entry.
def accept_paragraph paragraph
- parts = []
- string = false
-
- paragraph.parts.each do |part|
- if String === part then
- if string then
- string << part
- else
- parts << part
- string = part
- end
- else
- parts << part
- string = false
- end
- end
-
- parts = parts.map do |part|
- if String === part then
- part.rstrip
- else
- part
- end
- end
-
- # TODO use Enumerable#chunk when Ruby 1.8 support is dropped
- #parts = paragraph.parts.chunk do |part|
- # String === part
- #end.map do |string, chunk|
- # string ? chunk.join.rstrip : chunk
- #end.flatten
+ parts = paragraph.parts.chunk do |part|
+ String === part
+ end.map do |string, chunk|
+ string ? chunk.join.rstrip : chunk
+ end.flatten
paragraph.parts.replace parts
end
diff --git a/lib/rdoc/markup/to_label.rb b/lib/rdoc/markup/to_label.rb
index bdf08b7aee..3d95ccc2e2 100644
--- a/lib/rdoc/markup/to_label.rb
+++ b/lib/rdoc/markup/to_label.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'cgi'
##
@@ -16,8 +16,8 @@ class RDoc::Markup::ToLabel < RDoc::Markup::Formatter
def initialize markup = nil
super nil, markup
- @markup.add_special RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
- @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\])/, :TIDYLINK)
+ @markup.add_regexp_handling RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
+ @markup.add_regexp_handling(/(((\{.*?\})|\b\S+?)\[\S+?\])/, :TIDYLINK)
add_tag :BOLD, '', ''
add_tag :TT, '', ''
@@ -36,20 +36,20 @@ class RDoc::Markup::ToLabel < RDoc::Markup::Formatter
end
##
- # Converts the CROSSREF +special+ to plain text, removing the suppression
+ # Converts the CROSSREF +target+ to plain text, removing the suppression
# marker, if any
- def handle_special_CROSSREF special
- text = special.text
+ def handle_regexp_CROSSREF target
+ text = target.text
text.sub(/^\\/, '')
end
##
- # Converts the TIDYLINK +special+ to just the text part
+ # Converts the TIDYLINK +target+ to just the text part
- def handle_special_TIDYLINK special
- text = special.text
+ def handle_regexp_TIDYLINK target
+ text = target.text
return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
@@ -68,7 +68,7 @@ class RDoc::Markup::ToLabel < RDoc::Markup::Formatter
alias accept_rule ignore
alias accept_verbatim ignore
alias end_accepting ignore
- alias handle_special_HARD_BREAK ignore
+ alias handle_regexp_HARD_BREAK ignore
alias start_accepting ignore
end
diff --git a/lib/rdoc/markup/to_markdown.rb b/lib/rdoc/markup/to_markdown.rb
index 9074547b4c..3ee48becb0 100644
--- a/lib/rdoc/markup/to_markdown.rb
+++ b/lib/rdoc/markup/to_markdown.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# :markup: markdown
##
@@ -19,8 +19,8 @@ class RDoc::Markup::ToMarkdown < RDoc::Markup::ToRdoc
@headings[5] = ['##### ', '']
@headings[6] = ['###### ', '']
- add_special_RDOCLINK
- add_special_TIDYLINK
+ add_regexp_handling_RDOCLINK
+ add_regexp_handling_TIDYLINK
@hard_break = " \n"
end
@@ -37,7 +37,7 @@ class RDoc::Markup::ToMarkdown < RDoc::Markup::ToRdoc
##
# Adds a newline to the output
- def handle_special_HARD_BREAK special
+ def handle_regexp_HARD_BREAK target
" \n"
end
@@ -131,7 +131,7 @@ class RDoc::Markup::ToMarkdown < RDoc::Markup::ToRdoc
@res << part
end
- @res << "\n" unless @res =~ /\n\z/
+ @res << "\n"
end
##
@@ -166,8 +166,8 @@ class RDoc::Markup::ToMarkdown < RDoc::Markup::ToRdoc
##
# Converts the RDoc markup tidylink into a Markdown.style link.
- def handle_special_TIDYLINK special
- text = special.text
+ def handle_regexp_TIDYLINK target
+ text = target.text
return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
@@ -184,8 +184,8 @@ class RDoc::Markup::ToMarkdown < RDoc::Markup::ToRdoc
##
# Converts the rdoc-...: links into a Markdown.style links.
- def handle_special_RDOCLINK special
- handle_rdoc_link special.text
+ def handle_regexp_RDOCLINK target
+ handle_rdoc_link target.text
end
end
diff --git a/lib/rdoc/markup/to_rdoc.rb b/lib/rdoc/markup/to_rdoc.rb
index a40c09859f..81b16c4973 100644
--- a/lib/rdoc/markup/to_rdoc.rb
+++ b/lib/rdoc/markup/to_rdoc.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Outputs RDoc markup as RDoc markup! (mostly)
@@ -45,7 +45,7 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
def initialize markup = nil
super nil, markup
- @markup.add_special(/\\\S/, :SUPPRESSED_CROSSREF)
+ @markup.add_regexp_handling(/\\\S/, :SUPPRESSED_CROSSREF)
@width = 78
init_tags
@@ -234,7 +234,7 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
@res << part
end
- @res << "\n" unless @res =~ /\n\z/
+ @res << "\n"
end
##
@@ -253,10 +253,10 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
end
##
- # Removes preceding \\ from the suppressed crossref +special+
+ # Removes preceding \\ from the suppressed crossref +target+
- def handle_special_SUPPRESSED_CROSSREF special
- text = special.text
+ def handle_regexp_SUPPRESSED_CROSSREF target
+ text = target.text
text = text.sub('\\', '') unless in_tt?
text
end
@@ -264,7 +264,7 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
##
# Adds a newline to the output
- def handle_special_HARD_BREAK special
+ def handle_regexp_HARD_BREAK target
"\n"
end
diff --git a/lib/rdoc/markup/to_table_of_contents.rb b/lib/rdoc/markup/to_table_of_contents.rb
index eae7c59d94..f68b90bcf6 100644
--- a/lib/rdoc/markup/to_table_of_contents.rb
+++ b/lib/rdoc/markup/to_table_of_contents.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Extracts just the RDoc::Markup::Heading elements from a
# RDoc::Markup::Document to help build a table of contents
diff --git a/lib/rdoc/markup/to_test.rb b/lib/rdoc/markup/to_test.rb
index 7b1fa8c630..61d3cffaf0 100644
--- a/lib/rdoc/markup/to_test.rb
+++ b/lib/rdoc/markup/to_test.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# This Markup outputter is used for testing purposes.
diff --git a/lib/rdoc/markup/to_tt_only.rb b/lib/rdoc/markup/to_tt_only.rb
index ba20fcdd00..9235d33f04 100644
--- a/lib/rdoc/markup/to_tt_only.rb
+++ b/lib/rdoc/markup/to_tt_only.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Extracts sections of text enclosed in plus, tt or code. Used to discover
# undocumented parameters.
@@ -91,8 +91,8 @@ class RDoc::Markup::ToTtOnly < RDoc::Markup::Formatter
when RDoc::Markup::AttrChanger then
off_tags res, item
on_tags res, item
- when RDoc::Markup::Special then
- @res << convert_special(item) if in_tt? # TODO can this happen?
+ when RDoc::Markup::RegexpHandling then
+ @res << convert_regexp_handling(item) if in_tt? # TODO can this happen?
else
raise "Unknown flow element: #{item.inspect}"
end
diff --git a/lib/rdoc/markup/verbatim.rb b/lib/rdoc/markup/verbatim.rb
index a0b1d05928..7f1bc29a09 100644
--- a/lib/rdoc/markup/verbatim.rb
+++ b/lib/rdoc/markup/verbatim.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A section of verbatim text
diff --git a/lib/rdoc/meta_method.rb b/lib/rdoc/meta_method.rb
index 408c089dd1..7927a9ce9c 100644
--- a/lib/rdoc/meta_method.rb
+++ b/lib/rdoc/meta_method.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# MetaMethod represents a meta-programmed method
diff --git a/lib/rdoc/method_attr.rb b/lib/rdoc/method_attr.rb
index 50eab141be..3cef78c4a5 100644
--- a/lib/rdoc/method_attr.rb
+++ b/lib/rdoc/method_attr.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Abstract class representing either a method or an attribute.
@@ -188,7 +188,7 @@ class RDoc::MethodAttr < RDoc::CodeObject
next if String === ancestor
next if parent == ancestor
- other = ancestor.find_method_named('#' << name) ||
+ other = ancestor.find_method_named('#' + name) ||
ancestor.find_attribute_named(name)
return other if other
diff --git a/lib/rdoc/mixin.rb b/lib/rdoc/mixin.rb
index 14f04c15e7..379d7cc526 100644
--- a/lib/rdoc/mixin.rb
+++ b/lib/rdoc/mixin.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A Mixin adds features from a module into another context. RDoc::Include and
# RDoc::Extend are both mixins.
diff --git a/lib/rdoc/normal_class.rb b/lib/rdoc/normal_class.rb
index eb53e964dd..6729b18448 100644
--- a/lib/rdoc/normal_class.rb
+++ b/lib/rdoc/normal_class.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A normal class, neither singleton nor anonymous
@@ -47,9 +47,9 @@ class RDoc::NormalClass < RDoc::ClassModule
def to_s # :nodoc:
display = "#{self.class.name} #{self.full_name}"
if superclass
- display << ' < ' << (superclass.is_a?(String) ? superclass : superclass.full_name)
+ display += ' < ' + (superclass.is_a?(String) ? superclass : superclass.full_name)
end
- display << ' -> ' << is_alias_for.to_s if is_alias_for
+ display += ' -> ' + is_alias_for.to_s if is_alias_for
display
end
diff --git a/lib/rdoc/normal_module.rb b/lib/rdoc/normal_module.rb
index d046c8cbfe..8f364be41c 100644
--- a/lib/rdoc/normal_module.rb
+++ b/lib/rdoc/normal_module.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A normal module, like NormalClass
diff --git a/lib/rdoc/options.rb b/lib/rdoc/options.rb
index 60cfb5e553..801a84b21f 100644
--- a/lib/rdoc/options.rb
+++ b/lib/rdoc/options.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'optparse'
require 'pathname'
@@ -164,7 +164,7 @@ class RDoc::Options
##
# Files matching this pattern will be excluded
- attr_accessor :exclude
+ attr_writer :exclude
##
# The list of files to be processed
@@ -494,6 +494,20 @@ class RDoc::Options
end
##
+ # Create a regexp for #exclude
+
+ def exclude
+ if @exclude.nil? or Regexp === @exclude then
+ # done, #finish is being re-run
+ @exclude
+ elsif @exclude.empty? then
+ nil
+ else
+ Regexp.new(@exclude.join("|"))
+ end
+ end
+
+ ##
# Completes any unfinished option setup business such as filtering for
# existent files, creating a regexp for #exclude and setting a default
# #template.
@@ -505,13 +519,7 @@ class RDoc::Options
root = @root.to_s
@rdoc_include << root unless @rdoc_include.include?(root)
- if @exclude.nil? or Regexp === @exclude then
- # done, #finish is being re-run
- elsif @exclude.empty? then
- @exclude = nil
- else
- @exclude = Regexp.new(@exclude.join("|"))
- end
+ @exclude = self.exclude
finish_page_dir
@@ -624,16 +632,16 @@ Usage: #{opt.program_name} [options] [names...]
end
parsers.sort.each do |parser, regexp|
- opt.banner << " - #{parser}: #{regexp.join ', '}\n"
+ opt.banner += " - #{parser}: #{regexp.join ', '}\n"
end
- opt.banner << " - TomDoc: Only in ruby files\n"
+ opt.banner += " - TomDoc: Only in ruby files\n"
- opt.banner << "\n The following options are deprecated:\n\n"
+ opt.banner += "\n The following options are deprecated:\n\n"
name_length = DEPRECATED.keys.sort_by { |k| k.length }.last.length
DEPRECATED.sort_by { |k,| k }.each do |name, reason|
- opt.banner << " %*1$2$s %3$s\n" % [-name_length, name, reason]
+ opt.banner += " %*1$2$s %3$s\n" % [-name_length, name, reason]
end
opt.accept Template do |template|
@@ -1087,7 +1095,7 @@ Usage: #{opt.program_name} [options] [names...]
unless quiet then
deprecated.each do |opt|
- $stderr.puts 'option ' << opt << ' is deprecated: ' << DEPRECATED[opt]
+ $stderr.puts 'option ' + opt + ' is deprecated: ' + DEPRECATED[opt]
end
end
@@ -1217,7 +1225,7 @@ Usage: #{opt.program_name} [options] [names...]
def write_options
RDoc.load_yaml
- open '.rdoc_options', 'w' do |io|
+ File.open '.rdoc_options', 'w' do |io|
io.set_encoding Encoding::UTF_8
YAML.dump self, io
diff --git a/lib/rdoc/parser.rb b/lib/rdoc/parser.rb
index 5abc374bf7..597bcd6b9d 100644
--- a/lib/rdoc/parser.rb
+++ b/lib/rdoc/parser.rb
@@ -1,5 +1,5 @@
# -*- coding: us-ascii -*-
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A parser is simple a class that subclasses RDoc::Parser and implements #scan
@@ -78,7 +78,7 @@ class RDoc::Parser
return true if s[0, 2] == Marshal.dump('')[0, 2] or s.index("\x00")
- mode = "r"
+ mode = 'r:utf-8' # default source encoding has been chagened to utf-8
s.sub!(/\A#!.*\n/, '') # assume shebang line isn't longer than 1024.
encoding = s[/^\s*\#\s*(?:-\*-\s*)?(?:en)?coding:\s*([^\s;]+?)(?:-\*-|[\s;])/, 1]
mode = "rb:#{encoding}" if encoding
@@ -88,31 +88,6 @@ class RDoc::Parser
end
##
- # Processes common directives for CodeObjects for the C and Ruby parsers.
- #
- # Applies +directive+'s +value+ to +code_object+, if appropriate
-
- def self.process_directive code_object, directive, value
- warn "RDoc::Parser::process_directive is deprecated and wil be removed in RDoc 4. Use RDoc::Markup::PreProcess#handle_directive instead" if $-w
-
- case directive
- when 'nodoc' then
- code_object.document_self = nil # notify nodoc
- code_object.document_children = value.downcase != 'all'
- when 'doc' then
- code_object.document_self = true
- code_object.force_documentation = true
- when 'yield', 'yields' then
- # remove parameter &block
- code_object.params.sub!(/,?\s*&\w+/, '') if code_object.params
-
- code_object.block_params = value
- when 'arg', 'args' then
- code_object.params = value
- end
- end
-
- ##
# Checks if +file+ is a zip file in disguise. Signatures from
# http://www.garykessler.net/library/file_sigs.html
@@ -164,7 +139,7 @@ class RDoc::Parser
# Returns the file type from the modeline in +file_name+
def self.check_modeline file_name
- line = open file_name do |io|
+ line = File.open file_name do |io|
io.gets
end
@@ -180,7 +155,9 @@ class RDoc::Parser
return nil if /coding:/i =~ type
type.downcase
- rescue ArgumentError # invalid byte sequence, etc.
+ rescue ArgumentError
+ rescue Encoding::InvalidByteSequenceError # invalid byte sequence
+
end
##
diff --git a/lib/rdoc/parser/c.rb b/lib/rdoc/parser/c.rb
index 087d56f7b9..425ea11f63 100644
--- a/lib/rdoc/parser/c.rb
+++ b/lib/rdoc/parser/c.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'tsort'
##
@@ -217,6 +217,7 @@ class RDoc::Parser::C < RDoc::Parser
def deduplicate_call_seq
@methods.each do |var_name, functions|
class_name = @known_classes[var_name]
+ next unless class_name
class_obj = find_class var_name, class_name
functions.each_value do |method_names|
@@ -666,11 +667,9 @@ class RDoc::Parser::C < RDoc::Parser
#meth_obj.params = params
meth_obj.start_collecting_tokens
- tk = RDoc::RubyToken::Token.new nil, 1, 1
- tk.set_text body
+ tk = { :line_no => 1, :char_no => 1, :text => body }
meth_obj.add_token tk
meth_obj.comment = comment
- meth_obj.offset = offset
meth_obj.line = file_content[0, offset].count("\n") + 1
body
@@ -685,11 +684,9 @@ class RDoc::Parser::C < RDoc::Parser
find_modifiers comment, meth_obj
meth_obj.start_collecting_tokens
- tk = RDoc::RubyToken::Token.new nil, 1, 1
- tk.set_text body
+ tk = { :line_no => 1, :char_no => 1, :text => body }
meth_obj.add_token tk
meth_obj.comment = comment
- meth_obj.offset = offset
meth_obj.line = file_content[0, offset].count("\n") + 1
body
@@ -869,8 +866,8 @@ class RDoc::Parser::C < RDoc::Parser
def handle_attr(var_name, attr_name, read, write)
rw = ''
- rw << 'R' if '1' == read
- rw << 'W' if '1' == write
+ rw += 'R' if '1' == read
+ rw += 'W' if '1' == write
class_name = @known_classes[var_name]
@@ -986,8 +983,8 @@ class RDoc::Parser::C < RDoc::Parser
if new_definition.empty? then # Default to literal C definition
new_definition = definition
else
- new_definition.gsub!("\:", ":")
- new_definition.gsub!("\\", '\\')
+ new_definition = new_definition.gsub("\:", ":")
+ new_definition = new_definition.gsub("\\", '\\')
end
new_definition.sub!(/\A(\s+)/, '')
@@ -1241,7 +1238,7 @@ class RDoc::Parser::C < RDoc::Parser
# when scanning for classes and methods
def remove_commented_out_lines
- @content.gsub!(%r%//.*rb_define_%, '//')
+ @content = @content.gsub(%r%//.*rb_define_%, '//')
end
##
diff --git a/lib/rdoc/parser/changelog.rb b/lib/rdoc/parser/changelog.rb
index c6c2d2bb23..167892f543 100644
--- a/lib/rdoc/parser/changelog.rb
+++ b/lib/rdoc/parser/changelog.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'time'
##
@@ -29,13 +29,13 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
if last =~ /\)\s*\z/ and continuation =~ /\A\(/ then
last.sub!(/\)\s*\z/, ',')
- continuation.sub!(/\A\(/, '')
+ continuation = continuation.sub(/\A\(/, '')
end
if last =~ /\s\z/ then
last << continuation
else
- last << ' ' << continuation
+ last << ' ' + continuation
end
end
@@ -162,12 +162,12 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
entry_body = []
when /^(\t| {8})?\*\s*(.*)/ then # "\t* file.c (func): ..."
- entry_body << $2
+ entry_body << $2.dup
when /^(\t| {8})?\s*(\(.*)/ then # "\t(func): ..."
entry = $2
if entry_body.last =~ /:/ then
- entry_body << entry
+ entry_body << entry.dup
else
continue_entry_body entry_body, entry
end
diff --git a/lib/rdoc/parser/markdown.rb b/lib/rdoc/parser/markdown.rb
index feffb26ced..9ff478f872 100644
--- a/lib/rdoc/parser/markdown.rb
+++ b/lib/rdoc/parser/markdown.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Parse a Markdown format file. The parsed RDoc::Markup::Document is attached
# as a file comment.
diff --git a/lib/rdoc/parser/rd.rb b/lib/rdoc/parser/rd.rb
index e6693b9ac8..25f5711731 100644
--- a/lib/rdoc/parser/rd.rb
+++ b/lib/rdoc/parser/rd.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Parse a RD format file. The parsed RDoc::Markup::Document is attached as a
# file comment.
diff --git a/lib/rdoc/parser/ripper_state_lex.rb b/lib/rdoc/parser/ripper_state_lex.rb
new file mode 100644
index 0000000000..cc7a87976c
--- /dev/null
+++ b/lib/rdoc/parser/ripper_state_lex.rb
@@ -0,0 +1,589 @@
+# frozen_string_literal: true
+require 'ripper'
+
+class RDoc::Parser::RipperStateLex
+ # TODO: Remove this constants after Ruby 2.4 EOL
+ RIPPER_HAS_LEX_STATE = Ripper::Filter.method_defined?(:state)
+
+ Token = Struct.new(:line_no, :char_no, :kind, :text, :state)
+
+ EXPR_NONE = 0
+ EXPR_BEG = 1
+ EXPR_END = 2
+ EXPR_ENDARG = 4
+ EXPR_ENDFN = 8
+ EXPR_ARG = 16
+ EXPR_CMDARG = 32
+ EXPR_MID = 64
+ EXPR_FNAME = 128
+ EXPR_DOT = 256
+ EXPR_CLASS = 512
+ EXPR_LABEL = 1024
+ EXPR_LABELED = 2048
+ EXPR_FITEM = 4096
+ EXPR_VALUE = EXPR_BEG
+ EXPR_BEG_ANY = (EXPR_BEG | EXPR_MID | EXPR_CLASS)
+ EXPR_ARG_ANY = (EXPR_ARG | EXPR_CMDARG)
+ EXPR_END_ANY = (EXPR_END | EXPR_ENDARG | EXPR_ENDFN)
+
+ class InnerStateLex < Ripper::Filter
+ attr_accessor :lex_state
+
+ def initialize(code)
+ @lex_state = EXPR_BEG
+ @in_fname = false
+ @continue = false
+ reset
+ super(code)
+ end
+
+ def reset
+ @command_start = false
+ @cmd_state = @command_start
+ end
+
+ def on_nl(tok, data)
+ case @lex_state
+ when EXPR_FNAME, EXPR_DOT
+ @continue = true
+ else
+ @continue = false
+ @lex_state = EXPR_BEG unless (EXPR_LABEL & @lex_state) != 0
+ end
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_ignored_nl(tok, data)
+ case @lex_state
+ when EXPR_FNAME, EXPR_DOT
+ @continue = true
+ else
+ @continue = false
+ @lex_state = EXPR_BEG unless (EXPR_LABEL & @lex_state) != 0
+ end
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_op(tok, data)
+ case tok
+ when '&', '|', '!', '!=', '!~'
+ case @lex_state
+ when EXPR_FNAME, EXPR_DOT
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_BEG
+ end
+ when '<<'
+ # TODO next token?
+ case @lex_state
+ when EXPR_FNAME, EXPR_DOT
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_BEG
+ end
+ when '?'
+ @lex_state = EXPR_BEG
+ when '&&', '||', '+=', '-=', '*=', '**=',
+ '&=', '|=', '^=', '<<=', '>>=', '||=', '&&='
+ @lex_state = EXPR_BEG
+ when '::'
+ case @lex_state
+ when EXPR_ARG, EXPR_CMDARG
+ @lex_state = EXPR_DOT
+ when EXPR_FNAME, EXPR_DOT
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_BEG
+ end
+ else
+ case @lex_state
+ when EXPR_FNAME, EXPR_DOT
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_BEG
+ end
+ end
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_kw(tok, data)
+ case tok
+ when 'class'
+ @lex_state = EXPR_CLASS
+ @in_fname = true
+ when 'def'
+ @lex_state = EXPR_FNAME
+ @continue = true
+ @in_fname = true
+ when 'if', 'unless', 'while', 'until'
+ if ((EXPR_MID | EXPR_END | EXPR_ENDARG | EXPR_ENDFN | EXPR_ARG | EXPR_CMDARG) & @lex_state) != 0 # postfix if
+ @lex_state = EXPR_BEG | EXPR_LABEL
+ else
+ @lex_state = EXPR_BEG
+ end
+ when 'begin', 'case', 'when'
+ @lex_state = EXPR_BEG
+ when 'return', 'break'
+ @lex_state = EXPR_MID
+ else
+ if @lex_state == EXPR_FNAME
+ @lex_state = EXPR_END
+ else
+ @lex_state = EXPR_END
+ end
+ end
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_tstring_beg(tok, data)
+ @lex_state = EXPR_BEG
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_tstring_end(tok, data)
+ @lex_state = EXPR_END | EXPR_ENDARG
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_CHAR(tok, data)
+ @lex_state = EXPR_END
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_period(tok, data)
+ @lex_state = EXPR_DOT
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_int(tok, data)
+ @lex_state = EXPR_END | EXPR_ENDARG
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_float(tok, data)
+ @lex_state = EXPR_END | EXPR_ENDARG
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_rational(tok, data)
+ @lex_state = EXPR_END | EXPR_ENDARG
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_imaginary(tok, data)
+ @lex_state = EXPR_END | EXPR_ENDARG
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_symbeg(tok, data)
+ @lex_state = EXPR_FNAME
+ @continue = true
+ @in_fname = true
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ private def on_variables(event, tok, data)
+ if @in_fname
+ @lex_state = EXPR_ENDFN
+ @in_fname = false
+ @continue = false
+ elsif @continue
+ case @lex_state
+ when EXPR_DOT
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_ENDFN
+ @continue = false
+ end
+ else
+ @lex_state = EXPR_CMDARG
+ end
+ data << Token.new(lineno, column, event, tok, @lex_state)
+ end
+
+ def on_ident(tok, data)
+ on_variables(__method__, tok, data)
+ end
+
+ def on_ivar(tok, data)
+ @lex_state = EXPR_END
+ on_variables(__method__, tok, data)
+ end
+
+ def on_cvar(tok, data)
+ @lex_state = EXPR_END
+ on_variables(__method__, tok, data)
+ end
+
+ def on_gvar(tok, data)
+ @lex_state = EXPR_END
+ on_variables(__method__, tok, data)
+ end
+
+ def on_backref(tok, data)
+ @lex_state = EXPR_END
+ on_variables(__method__, tok, data)
+ end
+
+ def on_lparen(tok, data)
+ @lex_state = EXPR_LABEL | EXPR_BEG
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_rparen(tok, data)
+ @lex_state = EXPR_ENDFN
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_lbrace(tok, data)
+ @lex_state = EXPR_LABEL | EXPR_BEG
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_rbrace(tok, data)
+ @lex_state = EXPR_ENDARG
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_lbracket(tok, data)
+ @lex_state = EXPR_LABEL | EXPR_BEG
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_rbracket(tok, data)
+ @lex_state = EXPR_ENDARG
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_const(tok, data)
+ case @lex_state
+ when EXPR_FNAME
+ @lex_state = EXPR_ENDFN
+ when EXPR_CLASS, EXPR_CMDARG, EXPR_MID
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_CMDARG
+ end
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_sp(tok, data)
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_comma(tok, data)
+ @lex_state = EXPR_BEG | EXPR_LABEL if (EXPR_ARG_ANY & @lex_state) != 0
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_comment(tok, data)
+ @lex_state = EXPR_BEG unless (EXPR_LABEL & @lex_state) != 0
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_ignored_sp(tok, data)
+ @lex_state = EXPR_BEG unless (EXPR_LABEL & @lex_state) != 0
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ end
+
+ def on_heredoc_beg(tok, data)
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ @lex_state = EXPR_END
+ data
+ end
+
+ def on_heredoc_end(tok, data)
+ data << Token.new(lineno, column, __method__, tok, @lex_state)
+ @lex_state = EXPR_BEG
+ data
+ end
+
+ def on_default(event, tok, data)
+ reset
+ data << Token.new(lineno, column, event, tok, @lex_state)
+ end
+ end unless RIPPER_HAS_LEX_STATE
+
+ class InnerStateLex < Ripper::Filter
+ def initialize(code)
+ super(code)
+ end
+
+ def on_default(event, tok, data)
+ data << Token.new(lineno, column, event, tok, state)
+ end
+ end if RIPPER_HAS_LEX_STATE
+
+ def get_squashed_tk
+ if @buf.empty?
+ tk = @tokens.shift
+ else
+ tk = @buf.shift
+ end
+ return nil if tk.nil?
+ case tk[:kind]
+ when :on_symbeg then
+ tk = get_symbol_tk(tk)
+ when :on_tstring_beg then
+ tk = get_string_tk(tk)
+ when :on_backtick then
+ if (tk[:state] & (EXPR_FNAME | EXPR_ENDFN)) != 0
+ @inner_lex.lex_state = EXPR_ARG unless RIPPER_HAS_LEX_STATE
+ tk[:kind] = :on_ident
+ tk[:state] = Ripper::Lexer.const_defined?(:State) ? Ripper::Lexer::State.new(EXPR_ARG) : EXPR_ARG
+ else
+ tk = get_string_tk(tk)
+ end
+ when :on_regexp_beg then
+ tk = get_regexp_tk(tk)
+ when :on_embdoc_beg then
+ tk = get_embdoc_tk(tk)
+ when :on_heredoc_beg then
+ @heredoc_queue << retrieve_heredoc_info(tk)
+ @inner_lex.lex_state = EXPR_END unless RIPPER_HAS_LEX_STATE
+ when :on_nl, :on_ignored_nl, :on_comment, :on_heredoc_end then
+ if !@heredoc_queue.empty?
+ get_heredoc_tk(*@heredoc_queue.shift)
+ elsif tk[:text].nil? # :on_ignored_nl sometimes gives nil
+ tk[:text] = ''
+ end
+ when :on_words_beg then
+ tk = get_words_tk(tk)
+ when :on_qwords_beg then
+ tk = get_words_tk(tk)
+ when :on_symbols_beg then
+ tk = get_words_tk(tk)
+ when :on_qsymbols_beg then
+ tk = get_words_tk(tk)
+ when :on_op then
+ if '&.' == tk[:text]
+ tk[:kind] = :on_period
+ else
+ tk = get_op_tk(tk)
+ end
+ end
+ tk
+ end
+
+ private def get_symbol_tk(tk)
+ is_symbol = true
+ symbol_tk = Token.new(tk.line_no, tk.char_no, :on_symbol)
+ if ":'" == tk[:text] or ':"' == tk[:text]
+ tk1 = get_string_tk(tk)
+ symbol_tk[:text] = tk1[:text]
+ symbol_tk[:state] = tk1[:state]
+ else
+ case (tk1 = get_squashed_tk)[:kind]
+ when :on_ident
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ when :on_tstring_content
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = get_squashed_tk[:state] # skip :on_tstring_end
+ when :on_tstring_end
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ when :on_op
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ when :on_ivar
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ when :on_cvar
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ when :on_gvar
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ when :on_const
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ when :on_kw
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ else
+ is_symbol = false
+ tk = tk1
+ end
+ end
+ if is_symbol
+ tk = symbol_tk
+ end
+ tk
+ end
+
+ private def get_string_tk(tk)
+ string = tk[:text]
+ state = nil
+ kind = :on_tstring
+ loop do
+ inner_str_tk = get_squashed_tk
+ if inner_str_tk.nil?
+ break
+ elsif :on_tstring_end == inner_str_tk[:kind]
+ string = string + inner_str_tk[:text]
+ state = inner_str_tk[:state]
+ break
+ elsif :on_label_end == inner_str_tk[:kind]
+ string = string + inner_str_tk[:text]
+ state = inner_str_tk[:state]
+ kind = :on_symbol
+ break
+ else
+ string = string + inner_str_tk[:text]
+ if :on_embexpr_beg == inner_str_tk[:kind] then
+ kind = :on_dstring if :on_tstring == kind
+ end
+ end
+ end
+ Token.new(tk.line_no, tk.char_no, kind, string, state)
+ end
+
+ private def get_regexp_tk(tk)
+ string = tk[:text]
+ state = nil
+ loop do
+ inner_str_tk = get_squashed_tk
+ if inner_str_tk.nil?
+ break
+ elsif :on_regexp_end == inner_str_tk[:kind]
+ string = string + inner_str_tk[:text]
+ state = inner_str_tk[:state]
+ break
+ else
+ string = string + inner_str_tk[:text]
+ end
+ end
+ Token.new(tk.line_no, tk.char_no, :on_regexp, string, state)
+ end
+
+ private def get_embdoc_tk(tk)
+ string = tk[:text]
+ until :on_embdoc_end == (embdoc_tk = get_squashed_tk)[:kind] do
+ string = string + embdoc_tk[:text]
+ end
+ string = string + embdoc_tk[:text]
+ Token.new(tk.line_no, tk.char_no, :on_embdoc, string, embdoc_tk.state)
+ end
+
+ private def get_heredoc_tk(heredoc_name, indent)
+ string = ''
+ start_tk = nil
+ prev_tk = nil
+ until heredoc_end?(heredoc_name, indent, tk = @tokens.shift) do
+ start_tk = tk unless start_tk
+ if (prev_tk.nil? or "\n" == prev_tk[:text][-1]) and 0 != tk[:char_no]
+ string = string + (' ' * tk[:char_no])
+ end
+ string = string + tk[:text]
+ prev_tk = tk
+ end
+ start_tk = tk unless start_tk
+ prev_tk = tk unless prev_tk
+ @buf.unshift tk # closing heredoc
+ heredoc_tk = Token.new(start_tk.line_no, start_tk.char_no, :on_heredoc, string, prev_tk.state)
+ @buf.unshift heredoc_tk
+ end
+
+ private def retrieve_heredoc_info(tk)
+ name = tk[:text].gsub(/\A<<[-~]?(['"`]?)(.+)\1\z/, '\2')
+ indent = tk[:text] =~ /\A<<[-~]/
+ [name, indent]
+ end
+
+ private def heredoc_end?(name, indent, tk)
+ result = false
+ if :on_heredoc_end == tk[:kind] then
+ tk_name = (indent ? tk[:text].gsub(/^ *(.+)\n?$/, '\1') : tk[:text].gsub(/\n\z/, ''))
+ if name == tk_name
+ result = true
+ end
+ end
+ result
+ end
+
+ private def get_words_tk(tk)
+ string = ''
+ start_token = tk[:text]
+ start_quote = tk[:text].rstrip[-1]
+ line_no = tk[:line_no]
+ char_no = tk[:char_no]
+ state = tk[:state]
+ end_quote =
+ case start_quote
+ when ?( then ?)
+ when ?[ then ?]
+ when ?{ then ?}
+ when ?< then ?>
+ else start_quote
+ end
+ end_token = nil
+ loop do
+ tk = get_squashed_tk
+ if tk.nil?
+ end_token = end_quote
+ break
+ elsif :on_tstring_content == tk[:kind] then
+ string += tk[:text]
+ elsif :on_words_sep == tk[:kind] or :on_tstring_end == tk[:kind] then
+ if end_quote == tk[:text].strip then
+ end_token = tk[:text]
+ break
+ else
+ string += tk[:text]
+ end
+ else
+ string += tk[:text]
+ end
+ end
+ text = "#{start_token}#{string}#{end_token}"
+ Token.new(line_no, char_no, :on_dstring, text, state)
+ end
+
+ private def get_op_tk(tk)
+ redefinable_operators = %w[! != !~ % & * ** + +@ - -@ / < << <= <=> == === =~ > >= >> [] []= ^ ` | ~]
+ if redefinable_operators.include?(tk[:text]) and tk[:state] == EXPR_ARG then
+ @inner_lex.lex_state = EXPR_ARG unless RIPPER_HAS_LEX_STATE
+ tk[:state] = Ripper::Lexer.const_defined?(:State) ? Ripper::Lexer::State.new(EXPR_ARG) : EXPR_ARG
+ tk[:kind] = :on_ident
+ elsif tk[:text] =~ /^[-+]$/ then
+ tk_ahead = get_squashed_tk
+ case tk_ahead[:kind]
+ when :on_int, :on_float, :on_rational, :on_imaginary then
+ tk[:text] += tk_ahead[:text]
+ tk[:kind] = tk_ahead[:kind]
+ tk[:state] = tk_ahead[:state]
+ when :on_heredoc_beg, :on_tstring, :on_dstring # frozen/non-frozen string literal
+ tk[:text] += tk_ahead[:text]
+ tk[:kind] = tk_ahead[:kind]
+ tk[:state] = tk_ahead[:state]
+ else
+ @buf.unshift tk_ahead
+ end
+ end
+ tk
+ end
+
+ def initialize(code)
+ @buf = []
+ @heredoc_queue = []
+ @inner_lex = InnerStateLex.new(code)
+ @tokens = @inner_lex.parse([])
+ end
+
+ def self.parse(code)
+ lex = self.new(code)
+ tokens = []
+ begin
+ while tk = lex.get_squashed_tk
+ tokens.push tk
+ end
+ rescue StopIteration
+ end
+ tokens
+ end
+
+ def self.end?(token)
+ (token[:state] & EXPR_END)
+ end
+end
diff --git a/lib/rdoc/parser/ruby.rb b/lib/rdoc/parser/ruby.rb
index c30dbf0f4e..97399f87ff 100644
--- a/lib/rdoc/parser/ruby.rb
+++ b/lib/rdoc/parser/ruby.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# This file contains stuff stolen outright from:
#
@@ -8,8 +8,6 @@
# by Keiju ISHITSUKA (Nippon Rational Inc.)
#
-$TOKEN_DEBUG ||= nil
-
##
# Extracts code elements from a source file returning a TopLevel object
# containing the constituent file elements.
@@ -24,6 +22,7 @@ $TOKEN_DEBUG ||= nil
# * aliases
# * private, public, protected
# * private_class_function, public_class_function
+# * private_constant, public_constant
# * module_function
# * attr, attr_reader, attr_writer, attr_accessor
# * extra accessors given on the command line
@@ -139,11 +138,13 @@ $TOKEN_DEBUG ||= nil
# Note that by default, the :method: directive will be ignored if there is a
# standard rdocable item following it.
+require 'ripper'
+require_relative 'ripper_state_lex'
+
class RDoc::Parser::Ruby < RDoc::Parser
parse_files_matching(/\.rbw?$/)
- include RDoc::RubyToken
include RDoc::TokenStream
include RDoc::Parser::RubyTools
@@ -163,10 +164,22 @@ 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
+
@size = 0
@token_listeners = nil
- @scanner = RDoc::RubyLex.new content, @options
- @scanner.exception_on_syntax_error = false
+ content = RDoc::Encoding.remove_magic_comment content
+ @scanner = RDoc::Parser::RipperStateLex.parse(content)
+ @content = content
+ @scanner_point = 0
@prev_seek = nil
@markup = @options.markup
@track_visibility = :nodoc != @options.visibility
@@ -175,6 +188,10 @@ class RDoc::Parser::Ruby < RDoc::Parser
reset
end
+ def tk_nl?(tk)
+ :on_nl == tk[:kind] or :on_ignored_nl == tk[:kind]
+ end
+
##
# Retrieves the read token stream and replaces +pattern+ with +replacement+
# using gsub. If the result is only a ";" returns an empty string.
@@ -194,7 +211,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
# methods.
def get_visibility_information tk, single # :nodoc:
- vis_type = tk.name
+ vis_type = tk[:text]
singleton = single == SINGLE
vis =
@@ -223,31 +240,32 @@ class RDoc::Parser::Ruby < RDoc::Parser
def collect_first_comment
skip_tkspace
- comment = ''
- comment.force_encoding @encoding if @encoding
+ comment = ''.dup
+ comment = RDoc::Encoding.change_encoding comment, @encoding if @encoding
first_line = true
- first_comment_tk_class = nil
+ first_comment_tk_kind = nil
tk = get_tk
- while TkCOMMENT === tk
- if first_line and tk.text =~ /\A#!/ then
+ while tk && (:on_comment == tk[:kind] or :on_embdoc == tk[:kind])
+ comment_body = retrieve_comment_body(tk)
+ if first_line and comment_body =~ /\A#!/ then
skip_tkspace
tk = get_tk
- elsif first_line and tk.text =~ /\A#\s*-\*-/ then
+ elsif first_line and comment_body =~ /\A#\s*-\*-/ then
first_line = false
skip_tkspace
tk = get_tk
else
- break if first_comment_tk_class and not first_comment_tk_class === tk
- first_comment_tk_class = tk.class
+ break if first_comment_tk_kind and not first_comment_tk_kind === tk[:kind]
+ first_comment_tk_kind = tk[:kind]
first_line = false
- comment << tk.text << "\n"
+ comment << comment_body
tk = get_tk
- if TkNL === tk then
- skip_tkspace false
+ if :on_nl === tk then
+ skip_tkspace_without_nl
tk = get_tk
end
end
@@ -262,8 +280,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
# Consumes trailing whitespace from the token stream
def consume_trailing_spaces # :nodoc:
- get_tkread
- skip_tkspace false
+ skip_tkspace_without_nl
end
##
@@ -290,7 +307,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
container.find_module_named rhs_name
end
- container.add_module_alias mod, constant.name, @top_level if mod
+ container.add_module_alias mod, rhs_name, constant, @top_level
end
##
@@ -303,16 +320,14 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
##
- # Looks for a true or false token. Returns false if TkFALSE or TkNIL are
- # found.
+ # Looks for a true or false token.
def get_bool
skip_tkspace
tk = get_tk
- case tk
- when TkTRUE
+ if :on_kw == tk[:kind] && 'true' == tk[:text]
true
- when TkFALSE, TkNIL
+ elsif :on_kw == tk[:kind] && ('false' == tk[:text] || 'nil' == tk[:text])
false
else
unget_tk tk
@@ -328,27 +343,31 @@ class RDoc::Parser::Ruby < RDoc::Parser
def get_class_or_module container, ignore_constants = false
skip_tkspace
name_t = get_tk
- given_name = ''
+ given_name = ''.dup
# class ::A -> A is in the top level
- case name_t
- when TkCOLON2, TkCOLON3 then # bug
+ if :on_op == name_t[:kind] and '::' == name_t[:text] then # bug
name_t = get_tk
container = @top_level
given_name << '::'
end
- skip_tkspace false
- given_name << name_t.name
+ skip_tkspace_without_nl
+ given_name << name_t[:text]
- while TkCOLON2 === peek_tk do
+ is_self = name_t[:kind] == :on_op && name_t[:text] == '<<'
+ new_modules = []
+ while !is_self && (tk = peek_tk) and :on_op == tk[:kind] and '::' == tk[:text] do
prev_container = container
- container = container.find_module_named name_t.name
+ container = container.find_module_named name_t[:text]
container ||=
if ignore_constants then
- RDoc::Context.new
+ c = RDoc::NormalModule.new name_t[:text]
+ c.store = @store
+ new_modules << [prev_container, c]
+ c
else
- c = prev_container.add_module RDoc::NormalModule, name_t.name
+ c = prev_container.add_module RDoc::NormalModule, name_t[:text]
c.ignore unless prev_container.document_children
@top_level.add_to_classes_or_modules c
c
@@ -357,35 +376,47 @@ class RDoc::Parser::Ruby < RDoc::Parser
record_location container
get_tk
- skip_tkspace false
+ skip_tkspace_without_nl
name_t = get_tk
- given_name << '::' << name_t.name
+ unless :on_const == name_t[:kind] || :on_ident == name_t[:kind]
+ raise RDoc::Error, "Invalid class or module definition: #{given_name}"
+ end
+ if prev_container == container and !ignore_constants
+ given_name = name_t[:text]
+ else
+ given_name << '::' + name_t[:text]
+ end
end
- skip_tkspace false
+ skip_tkspace_without_nl
- return [container, name_t, given_name]
+ return [container, name_t, given_name, new_modules]
end
##
# Return a superclass, which can be either a constant of an expression
def get_class_specification
- case peek_tk
- when TkSELF then return 'self'
- when TkGVAR then return ''
+ tk = peek_tk
+ if tk.nil?
+ return ''
+ elsif :on_kw == tk[:kind] && 'self' == tk[:text]
+ return 'self'
+ elsif :on_gvar == tk[:kind]
+ return ''
end
res = get_constant
- skip_tkspace false
+ skip_tkspace_without_nl
get_tkread # empty out read buffer
tk = get_tk
+ return res unless tk
- case tk
- when TkNL, TkCOMMENT, TkSEMICOLON then
+ case tk[:kind]
+ when :on_nl, :on_comment, :on_embdoc, :on_semicolon then
unget_tk(tk)
return res
end
@@ -400,11 +431,11 @@ class RDoc::Parser::Ruby < RDoc::Parser
def get_constant
res = ""
- skip_tkspace false
+ skip_tkspace_without_nl
tk = get_tk
- while TkCOLON2 === tk or TkCOLON3 === tk or TkCONSTANT === tk do
- res += tk.name
+ while tk && ((:on_op == tk[:kind] && '::' == tk[:text]) || :on_const == tk[:kind]) do
+ res += tk[:text]
tk = get_tk
end
@@ -413,28 +444,83 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
##
- # Get a constant that may be surrounded by parens
+ # Get an included module that may be surrounded by parens
- def get_constant_with_optional_parens
- skip_tkspace false
+ def get_included_module_with_optional_parens
+ skip_tkspace_without_nl
+ get_tkread
+ tk = get_tk
+ end_token = get_end_token tk
+ return '' unless end_token
nest = 0
+ continue = false
+ only_constant = true
- while TkLPAREN === (tk = peek_tk) or TkfLPAREN === tk do
- get_tk
- skip_tkspace
- nest += 1
- end
-
- name = get_constant
-
- while nest > 0
- skip_tkspace
+ while tk != nil do
+ is_element_of_constant = false
+ case tk[:kind]
+ when :on_semicolon then
+ break if nest == 0
+ when :on_lbracket then
+ nest += 1
+ when :on_rbracket then
+ nest -= 1
+ when :on_lbrace then
+ nest += 1
+ when :on_rbrace then
+ nest -= 1
+ if nest <= 0
+ # we might have a.each { |i| yield i }
+ unget_tk(tk) if nest < 0
+ break
+ end
+ when :on_lparen then
+ nest += 1
+ when end_token[:kind] then
+ if end_token[:kind] == :on_rparen
+ nest -= 1
+ break if nest <= 0
+ else
+ break if nest <= 0
+ end
+ when :on_rparen then
+ nest -= 1
+ when :on_comment, :on_embdoc then
+ @read.pop
+ if :on_nl == end_token[:kind] and "\n" == tk[:text][-1] and
+ (!continue or (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) != 0) then
+ break if !continue and nest <= 0
+ end
+ when :on_comma then
+ continue = true
+ when :on_ident then
+ continue = false if continue
+ when :on_kw then
+ case tk[:text]
+ when 'def', 'do', 'case', 'for', 'begin', 'class', 'module'
+ nest += 1
+ when 'if', 'unless', 'while', 'until', 'rescue'
+ # postfix if/unless/while/until/rescue must be EXPR_LABEL
+ nest += 1 unless (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) != 0
+ when 'end'
+ nest -= 1
+ break if nest == 0
+ end
+ when :on_const then
+ is_element_of_constant = true
+ when :on_op then
+ is_element_of_constant = true if '::' == tk[:text]
+ end
+ only_constant = false unless is_element_of_constant
tk = get_tk
- nest -= 1 if TkRPAREN === tk
end
- name
+ if only_constant
+ get_tkread_clean(/\s+/, ' ')
+ else
+ ''
+ end
end
##
@@ -446,13 +532,19 @@ class RDoc::Parser::Ruby < RDoc::Parser
# won't catch all cases (such as "a = yield + 1"
def get_end_token tk # :nodoc:
- case tk
- when TkLPAREN, TkfLPAREN
- TkRPAREN
- when TkRPAREN
+ case tk[:kind]
+ when :on_lparen
+ token = RDoc::Parser::RipperStateLex::Token.new
+ token[:kind] = :on_rparen
+ token[:text] = ')'
+ token
+ when :on_rparen
nil
else
- TkNL
+ token = RDoc::Parser::RipperStateLex::Token.new
+ token[:kind] = :on_nl
+ token[:text] = "\n"
+ token
end
end
@@ -461,11 +553,11 @@ class RDoc::Parser::Ruby < RDoc::Parser
def get_method_container container, name_t # :nodoc:
prev_container = container
- container = container.find_module_named(name_t.name)
+ container = container.find_module_named(name_t[:text])
unless container then
constant = prev_container.constants.find do |const|
- const.name == name_t.name
+ const.name == name_t[:text]
end
if constant then
@@ -476,21 +568,21 @@ class RDoc::Parser::Ruby < RDoc::Parser
unless container then
# TODO seems broken, should starting at Object in @store
- obj = name_t.name.split("::").inject(Object) do |state, item|
+ obj = name_t[:text].split("::").inject(Object) do |state, item|
state.const_get(item)
end rescue nil
type = obj.class == Class ? RDoc::NormalClass : RDoc::NormalModule
unless [Class, Module].include?(obj.class) then
- warn("Couldn't find #{name_t.name}. Assuming it's a module")
+ warn("Couldn't find #{name_t[:text]}. Assuming it's a module")
end
if type == RDoc::NormalClass then
sclass = obj.superclass ? obj.superclass.name : nil
- container = prev_container.add_class type, name_t.name, sclass
+ container = prev_container.add_class type, name_t[:text], sclass
else
- container = prev_container.add_module type, name_t.name
+ container = prev_container.add_module type, name_t[:text]
end
record_location container
@@ -504,32 +596,26 @@ class RDoc::Parser::Ruby < RDoc::Parser
def get_symbol_or_name
tk = get_tk
- case tk
- when TkSYMBOL then
- text = tk.text.sub(/^:/, '')
+ case tk[:kind]
+ when :on_symbol then
+ text = tk[:text].sub(/^:/, '')
- if TkASSIGN === peek_tk then
+ next_tk = peek_tk
+ if next_tk && :on_op == next_tk[:kind] && '=' == next_tk[:text] then
get_tk
text << '='
end
text
- when TkId, TkOp then
- tk.name
- when TkAMPER,
- TkDSTRING,
- TkSTAR,
- TkSTRING then
- tk.text
+ when :on_ident, :on_const, :on_gvar, :on_cvar, :on_ivar, :on_op, :on_kw then
+ tk[:text]
+ when :on_tstring, :on_dstring then
+ tk[:text][1..-2]
else
raise RDoc::Error, "Name or symbol expected (got #{tk})"
end
end
- def stop_at_EXPR_END # :nodoc:
- @scanner.lex_state == :EXPR_END || !@scanner.continue
- end
-
##
# Marks containers between +container+ and +ancestor+ as ignored
@@ -548,29 +634,31 @@ class RDoc::Parser::Ruby < RDoc::Parser
#
# This routine modifies its +comment+ parameter.
- def look_for_directives_in context, comment
- @preprocess.handle comment, context do |directive, param|
+ def look_for_directives_in container, comment
+ @preprocess.handle comment, container do |directive, param|
case directive
when 'method', 'singleton-method',
'attr', 'attr_accessor', 'attr_reader', 'attr_writer' then
false # handled elsewhere
when 'section' then
- context.set_current_section param, comment.dup
+ break unless container.kind_of?(RDoc::Context)
+ container.set_current_section param, comment.dup
comment.text = ''
break
end
end
- remove_private_comments comment
+ comment.remove_private
end
##
# Adds useful info about the parser to +message+
def make_message message
- prefix = "#{@file_name}:"
+ prefix = "#{@file_name}:".dup
- prefix << "#{@scanner.line_no}:#{@scanner.char_no}:" if @scanner
+ tk = peek_tk
+ prefix << "#{tk[:line_no]}:#{tk[:char_no]}:" if tk
"#{prefix} #{message}"
end
@@ -589,24 +677,22 @@ class RDoc::Parser::Ruby < RDoc::Parser
# +comment+.
def parse_attr(context, single, tk, comment)
- offset = tk.seek
- line_no = tk.line_no
+ line_no = tk[:line_no]
args = parse_symbol_arg 1
if args.size > 0 then
name = args[0]
rw = "R"
- skip_tkspace false
+ skip_tkspace_without_nl
tk = get_tk
- if TkCOMMA === tk then
+ if :on_comma == tk[:kind] then
rw = "RW" if get_bool
else
unget_tk tk
end
att = create_attr context, single, name, rw, comment
- att.offset = offset
att.line = line_no
read_documentation_modifiers att, RDoc::ATTR_MODIFIERS
@@ -620,8 +706,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
# comment for each to +comment+.
def parse_attr_accessor(context, single, tk, comment)
- offset = tk.seek
- line_no = tk.line_no
+ line_no = tk[:line_no]
args = parse_symbol_arg
rw = "?"
@@ -632,7 +717,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
# and add found items appropriately but here we do not. I'm not sure why.
return if @track_visibility and not tmp.document_self
- case tk.name
+ case tk[:text]
when "attr_reader" then rw = "R"
when "attr_writer" then rw = "W"
when "attr_accessor" then rw = "RW"
@@ -642,7 +727,6 @@ class RDoc::Parser::Ruby < RDoc::Parser
for name in args
att = create_attr context, single, name, rw, comment
- att.offset = offset
att.line = line_no
end
end
@@ -651,22 +735,19 @@ class RDoc::Parser::Ruby < RDoc::Parser
# Parses an +alias+ in +context+ with +comment+
def parse_alias(context, single, tk, comment)
- offset = tk.seek
- line_no = tk.line_no
+ line_no = tk[:line_no]
skip_tkspace
- if TkLPAREN === peek_tk then
+ if :on_lparen === peek_tk[:kind] then
get_tk
skip_tkspace
end
new_name = get_symbol_or_name
- @scanner.lex_state = :EXPR_FNAME
-
skip_tkspace
- if TkCOMMA === peek_tk then
+ if :on_comma === peek_tk[:kind] then
get_tk
skip_tkspace
end
@@ -680,7 +761,6 @@ class RDoc::Parser::Ruby < RDoc::Parser
al = RDoc::Alias.new(get_tkread, old_name, new_name, comment,
single == SINGLE)
record_location al
- al.offset = offset
al.line = line_no
read_documentation_modifiers al, RDoc::ATTR_MODIFIERS
@@ -694,34 +774,38 @@ class RDoc::Parser::Ruby < RDoc::Parser
# Extracts call parameters from the token stream.
def parse_call_parameters(tk)
- end_token = case tk
- when TkLPAREN, TkfLPAREN
- TkRPAREN
- when TkRPAREN
+ end_token = case tk[:kind]
+ when :on_lparen
+ :on_rparen
+ when :on_rparen
return ""
else
- TkNL
+ :on_nl
end
nest = 0
loop do
- case tk
- when TkSEMICOLON
+ break if tk.nil?
+ case tk[:kind]
+ when :on_semicolon
break
- when TkLPAREN, TkfLPAREN
+ when :on_lparen
nest += 1
when end_token
- if end_token == TkRPAREN
+ if end_token == :on_rparen
nest -= 1
- break if @scanner.lex_state == :EXPR_END and nest <= 0
+ break if RDoc::Parser::RipperStateLex.end?(tk) and nest <= 0
else
- break unless @scanner.continue
+ break if RDoc::Parser::RipperStateLex.end?(tk)
end
- when TkCOMMENT, TkASSIGN, TkOPASGN
+ when :on_comment, :on_embdoc
unget_tk(tk)
break
- when nil then
- break
+ when :on_op
+ if tk[:text] =~ /^(.{1,2})?=$/
+ unget_tk(tk)
+ break
+ end
end
tk = get_tk
end
@@ -733,33 +817,33 @@ class RDoc::Parser::Ruby < RDoc::Parser
# Parses a class in +context+ with +comment+
def parse_class container, single, tk, comment
- offset = tk.seek
- line_no = tk.line_no
+ line_no = tk[:line_no]
declaration_context = container
- container, name_t, given_name = get_class_or_module container
-
- cls =
- case name_t
- when TkCONSTANT
- parse_class_regular container, declaration_context, single,
- name_t, given_name, comment
- when TkLSHFT
- case name = get_class_specification
- when 'self', container.name
- parse_statements container, SINGLE
- return # don't update offset or line
- else
- parse_class_singleton container, name, comment
- end
+ container, name_t, given_name, = get_class_or_module container
+
+ if name_t[:kind] == :on_const
+ cls = parse_class_regular container, declaration_context, single,
+ name_t, given_name, comment
+ elsif name_t[:kind] == :on_op && name_t[:text] == '<<'
+ case name = get_class_specification
+ when 'self', container.name
+ read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
+ parse_statements container, SINGLE
+ return # don't update line
else
- warn "Expected class name or '<<'. Got #{name_t.class}: #{name_t.text.inspect}"
- return
+ cls = parse_class_singleton container, name, comment
end
+ else
+ warn "Expected class name or '<<'. Got #{name_t[:kind]}: #{name_t[:text].inspect}"
+ return
+ end
- cls.offset = offset
cls.line = line_no
+ # after end modifiers
+ read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
+
cls
end
@@ -775,7 +859,8 @@ class RDoc::Parser::Ruby < RDoc::Parser
given_name = $'
end
- if TkLT === peek_tk then
+ tk = peek_tk
+ if tk[:kind] == :on_op && tk[:text] == '<' then
get_tk
skip_tkspace
superclass = get_class_specification
@@ -845,102 +930,125 @@ class RDoc::Parser::Ruby < RDoc::Parser
# true, no found constants will be added to RDoc.
def parse_constant container, tk, comment, ignore_constants = false
- offset = tk.seek
- line_no = tk.line_no
+ line_no = tk[:line_no]
- name = tk.name
- skip_tkspace false
+ name = tk[:text]
+ skip_tkspace_without_nl
return unless name =~ /^\w+$/
- eq_tk = get_tk
-
- if TkCOLON2 === eq_tk then
- unget_tk eq_tk
+ new_modules = []
+ if :on_op == peek_tk[:kind] && '::' == peek_tk[:text] then
unget_tk tk
- container, name_t, = get_class_or_module container, ignore_constants
+ container, name_t, _, new_modules = get_class_or_module container, true
- name = name_t.name
+ name = name_t[:text]
+ end
- eq_tk = get_tk
+ is_array_or_hash = false
+ if peek_tk && :on_lbracket == peek_tk[:kind]
+ get_tk
+ nest = 1
+ while bracket_tk = get_tk
+ case bracket_tk[:kind]
+ when :on_lbracket
+ nest += 1
+ when :on_rbracket
+ nest -= 1
+ break if nest == 0
+ end
+ end
+ skip_tkspace_without_nl
+ is_array_or_hash = true
end
- unless TkASSIGN === eq_tk then
- unget_tk eq_tk
+ unless peek_tk && :on_op == peek_tk[:kind] && '=' == peek_tk[:text] then
return false
end
+ get_tk
- if TkGT === peek_tk then
- unget_tk eq_tk
- return
+ unless ignore_constants
+ new_modules.each do |prev_c, new_module|
+ prev_c.add_module_by_normal_module new_module
+ new_module.ignore unless prev_c.document_children
+ @top_level.add_to_classes_or_modules new_module
+ end
end
value = ''
con = RDoc::Constant.new name, value, comment
- body = parse_constant_body container, con
+ body = parse_constant_body container, con, is_array_or_hash
return unless body
- value.replace body
+ con.value = body
record_location con
- con.offset = offset
con.line = line_no
read_documentation_modifiers con, RDoc::CONSTANT_MODIFIERS
+ return if is_array_or_hash
+
@stats.add_constant con
container.add_constant con
true
end
- def parse_constant_body container, constant # :nodoc:
+ def parse_constant_body container, constant, is_array_or_hash # :nodoc:
nest = 0
- rhs_name = ''
+ rhs_name = ''.dup
get_tkread
tk = get_tk
+ body = nil
loop do
- case tk
- when TkSEMICOLON then
+ break if tk.nil?
+ if :on_semicolon == tk[:kind] then
break if nest <= 0
- when TkLPAREN, TkfLPAREN, TkLBRACE, TkfLBRACE, TkLBRACK, TkfLBRACK,
- TkDO, TkIF, TkUNLESS, TkCASE, TkDEF, TkBEGIN then
+ elsif [:on_tlambeg, :on_lparen, :on_lbrace, :on_lbracket].include?(tk[:kind]) then
+ nest += 1
+ elsif (:on_kw == tk[:kind] && 'def' == tk[:text]) then
nest += 1
- when TkRPAREN, TkRBRACE, TkRBRACK, TkEND then
+ elsif (:on_kw == tk[:kind] && %w{do if unless case begin}.include?(tk[:text])) then
+ if (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) == 0
+ nest += 1
+ end
+ elsif [:on_rparen, :on_rbrace, :on_rbracket].include?(tk[:kind]) ||
+ (:on_kw == tk[:kind] && 'end' == tk[:text]) then
nest -= 1
- when TkCOMMENT then
- if nest <= 0 and stop_at_EXPR_END then
- unget_tk tk
+ elsif (:on_comment == tk[:kind] or :on_embdoc == tk[:kind]) then
+ unget_tk tk
+ if nest <= 0 and RDoc::Parser::RipperStateLex.end?(tk) then
+ body = get_tkread_clean(/^[ \t]+/, '')
+ read_documentation_modifiers constant, RDoc::CONSTANT_MODIFIERS
break
else
- unget_tk tk
read_documentation_modifiers constant, RDoc::CONSTANT_MODIFIERS
end
- when TkCONSTANT then
- rhs_name << tk.name
+ elsif :on_const == tk[:kind] then
+ rhs_name << tk[:text]
- if nest <= 0 and TkNL === peek_tk then
- create_module_alias container, constant, rhs_name
+ next_tk = peek_tk
+ if nest <= 0 and (next_tk.nil? || :on_nl == next_tk[:kind]) then
+ create_module_alias container, constant, rhs_name unless is_array_or_hash
break
end
- when TkNL then
- if nest <= 0 and stop_at_EXPR_END then
+ elsif :on_nl == tk[:kind] then
+ if nest <= 0 and RDoc::Parser::RipperStateLex.end?(tk) then
unget_tk tk
break
end
- when TkCOLON2, TkCOLON3 then
+ elsif :on_op == tk[:kind] && '::' == tk[:text]
rhs_name << '::'
- when nil then
- break
end
tk = get_tk
end
- get_tkread_clean(/^[ \t]+/, '')
+ body ? body : get_tkread_clean(/^[ \t]+/, '')
end
##
@@ -949,24 +1057,21 @@ class RDoc::Parser::Ruby < RDoc::Parser
def parse_comment container, tk, comment
return parse_comment_tomdoc container, tk, comment if @markup == 'tomdoc'
- column = tk.char_no
- offset = tk.seek
- line_no = tk.line_no
+ column = tk[:char_no]
+ line_no = tk[:line_no]
- text = comment.text
-
- singleton = !!text.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3')
+ comment.text = comment.text.sub(/(^# +:?)(singleton-)(method:)/, '\1\3')
+ singleton = !!$~
co =
- if text.sub!(/^# +:?method: *(\S*).*?\n/i, '') then
- parse_comment_ghost container, text, $1, column, line_no, comment
- elsif text.sub!(/# +:?(attr(_reader|_writer|_accessor)?): *(\S*).*?\n/i, '') then
+ if (comment.text = comment.text.sub(/^# +:?method: *(\S*).*?\n/i, '')) && !!$~ then
+ parse_comment_ghost container, comment.text, $1, column, line_no, comment
+ elsif (comment.text = comment.text.sub(/# +:?(attr(_reader|_writer|_accessor)?): *(\S*).*?\n/i, '')) && !!$~ then
parse_comment_attr container, $1, $3, comment
end
if co then
co.singleton = singleton
- co.offset = offset
co.line = line_no
end
@@ -997,12 +1102,11 @@ class RDoc::Parser::Ruby < RDoc::Parser
record_location meth
meth.start_collecting_tokens
- indent = TkSPACE.new 0, 1, 1
- indent.set_text " " * column
-
- position_comment = TkCOMMENT.new 0, line_no, 1
- position_comment.set_text "# File #{@top_level.relative_name}, line #{line_no}"
- meth.add_tokens [position_comment, NEWLINE_TOKEN, indent]
+ indent = RDoc::Parser::RipperStateLex::Token.new(1, 1, :on_sp, ' ' * column)
+ position_comment = RDoc::Parser::RipperStateLex::Token.new(line_no, 1, :on_comment)
+ position_comment[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
+ newline = RDoc::Parser::RipperStateLex::Token.new(0, 0, :on_nl, "\n")
+ meth.add_tokens [position_comment, newline, indent]
meth.params =
if text.sub!(/^#\s+:?args?:\s*(.*?)\s*$/i, '') then
@@ -1031,23 +1135,21 @@ class RDoc::Parser::Ruby < RDoc::Parser
def parse_comment_tomdoc container, tk, comment
return unless signature = RDoc::TomDoc.signature(comment)
- offset = tk.seek
- line_no = tk.line_no
+ column = tk[:char_no]
+ line_no = tk[:line_no]
name, = signature.split %r%[ \(]%, 2
meth = RDoc::GhostMethod.new get_tkread, name
record_location meth
- meth.offset = offset
meth.line = line_no
meth.start_collecting_tokens
- indent = TkSPACE.new 0, 1, 1
- indent.set_text " " * offset
-
- position_comment = TkCOMMENT.new 0, line_no, 1
- position_comment.set_text "# File #{@top_level.relative_name}, line #{line_no}"
- meth.add_tokens [position_comment, NEWLINE_TOKEN, indent]
+ indent = RDoc::Parser::RipperStateLex::Token.new(1, 1, :on_sp, ' ' * column)
+ position_comment = RDoc::Parser::RipperStateLex::Token.new(line_no, 1, :on_comment)
+ position_comment[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
+ newline = RDoc::Parser::RipperStateLex::Token.new(0, 0, :on_nl, "\n")
+ meth.add_tokens [position_comment, newline, indent]
meth.call_seq = signature
@@ -1070,14 +1172,14 @@ class RDoc::Parser::Ruby < RDoc::Parser
loop do
skip_tkspace_comment
- name = get_constant_with_optional_parens
+ name = get_included_module_with_optional_parens
unless name.empty? then
obj = container.add klass, name, comment
record_location obj
end
- return unless TkCOMMA === peek_tk
+ return if peek_tk.nil? || :on_comma != peek_tk[:kind]
get_tk
end
@@ -1089,11 +1191,14 @@ class RDoc::Parser::Ruby < RDoc::Parser
# Returns true if the comment was not consumed.
def parse_identifier container, single, tk, comment # :nodoc:
- case tk.name
+ case tk[:text]
when 'private', 'protected', 'public', 'private_class_method',
'public_class_method', 'module_function' then
parse_visibility container, single, tk
return true
+ when 'private_constant', 'public_constant'
+ parse_constant_visibility container, single, tk
+ return true
when 'attr' then
parse_attr container, single, tk, comment
when /^attr_(reader|writer|accessor)$/ then
@@ -1158,7 +1263,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
tmp = RDoc::CodeObject.new
read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
- if comment.text.sub!(/^# +:?(attr(_reader|_writer|_accessor)?): *(\S*).*?\n/i, '') then
+ regexp = /^# +:?(attr(_reader|_writer|_accessor)?): *(\S*).*?\n/i
+ if regexp =~ comment.text then
+ comment.text = comment.text.sub(regexp, '')
rw = case $1
when 'attr_reader' then 'R'
when 'attr_writer' then 'W'
@@ -1182,17 +1289,17 @@ class RDoc::Parser::Ruby < RDoc::Parser
# Parses a meta-programmed method
def parse_meta_method(container, single, tk, comment)
- column = tk.char_no
- offset = tk.seek
- line_no = tk.line_no
+ column = tk[:char_no]
+ line_no = tk[:line_no]
start_collecting_tokens
add_token tk
add_token_listener self
- skip_tkspace false
+ skip_tkspace_without_nl
- singleton = !!comment.text.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3')
+ comment.text = comment.text.sub(/(^# +:?)(singleton-)(method:)/, '\1\3')
+ singleton = !!$~
name = parse_meta_method_name comment, tk
@@ -1200,19 +1307,17 @@ class RDoc::Parser::Ruby < RDoc::Parser
meth = RDoc::MetaMethod.new get_tkread, name
record_location meth
- meth.offset = offset
meth.line = line_no
meth.singleton = singleton
remove_token_listener self
meth.start_collecting_tokens
- indent = TkSPACE.new 0, 1, 1
- indent.set_text " " * column
-
- position_comment = TkCOMMENT.new 0, line_no, 1
- position_comment.value = "# File #{@top_level.relative_name}, line #{line_no}"
- meth.add_tokens [position_comment, NEWLINE_TOKEN, indent]
+ indent = RDoc::Parser::RipperStateLex::Token.new(1, 1, :on_sp, ' ' * column)
+ position_comment = RDoc::Parser::RipperStateLex::Token.new(line_no, 1, :on_comment)
+ position_comment[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
+ newline = RDoc::Parser::RipperStateLex::Token.new(0, 0, :on_nl, "\n")
+ meth.add_tokens [position_comment, newline, indent]
meth.add_tokens @token_stream
parse_meta_method_params container, single, meth, tk, comment
@@ -1236,17 +1341,16 @@ class RDoc::Parser::Ruby < RDoc::Parser
name_t = get_tk
- case name_t
- when TkSYMBOL then
- name_t.text[1..-1]
- when TkSTRING then
- name_t.value[1..-2]
- when TkASSIGN then # ignore
+ if :on_symbol == name_t[:kind] then
+ name_t[:text][1..-1]
+ elsif :on_tstring == name_t[:kind] then
+ name_t[:text][1..-2]
+ elsif :on_op == name_t[:kind] && '=' == name_t[:text] then # ignore
remove_token_listener self
nil
else
- warn "unknown name token #{name_t.inspect} for meta-method '#{tk.name}'"
+ warn "unknown name token #{name_t.inspect} for meta-method '#{tk[:text]}'"
'unknown'
end
end
@@ -1258,6 +1362,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
token_listener meth do
meth.params = ''
+ look_for_directives_in meth, comment
comment.normalize
comment.extract_call_seq meth
@@ -1266,14 +1371,13 @@ class RDoc::Parser::Ruby < RDoc::Parser
last_tk = tk
while tk = get_tk do
- case tk
- when TkSEMICOLON then
+ if :on_semicolon == tk[:kind] then
break
- when TkNL then
- break unless last_tk and TkCOMMA === last_tk
- when TkSPACE then
+ elsif :on_nl == tk[:kind] then
+ break unless last_tk and :on_comma == last_tk[:kind]
+ elsif :on_sp == tk[:kind] then
# expression continues
- when TkDO then
+ elsif :on_kw == tk[:kind] && 'do' == tk[:text] then
parse_statements container, single, meth
break
else
@@ -1290,9 +1394,8 @@ class RDoc::Parser::Ruby < RDoc::Parser
singleton = nil
added_container = false
name = nil
- column = tk.char_no
- offset = tk.seek
- line_no = tk.line_no
+ column = tk[:char_no]
+ line_no = tk[:line_no]
start_collecting_tokens
add_token tk
@@ -1306,19 +1409,18 @@ class RDoc::Parser::Ruby < RDoc::Parser
return unless name
meth = RDoc::AnyMethod.new get_tkread, name
+ look_for_directives_in meth, comment
meth.singleton = single == SINGLE ? true : singleton
record_location meth
- meth.offset = offset
meth.line = line_no
meth.start_collecting_tokens
- indent = TkSPACE.new 0, 1, 1
- indent.set_text " " * column
-
- token = TkCOMMENT.new 0, line_no, 1
- token.set_text "# File #{@top_level.relative_name}, line #{line_no}"
- meth.add_tokens [token, NEWLINE_TOKEN, indent]
+ indent = RDoc::Parser::RipperStateLex::Token.new(1, 1, :on_sp, ' ' * column)
+ token = RDoc::Parser::RipperStateLex::Token.new(line_no, 1, :on_comment)
+ token[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
+ newline = RDoc::Parser::RipperStateLex::Token.new(0, 0, :on_nl, "\n")
+ meth.add_tokens [token, newline, indent]
meth.add_tokens @token_stream
parse_method_params_and_body container, single, meth, added_container
@@ -1328,6 +1430,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
meth.comment = comment
+ # after end modifiers
+ read_documentation_modifiers meth, RDoc::METHOD_MODIFIERS
+
@stats.add_method meth
end
@@ -1336,7 +1441,6 @@ class RDoc::Parser::Ruby < RDoc::Parser
def parse_method_params_and_body container, single, meth, added_container
token_listener meth do
- @scanner.continue = false
parse_method_parameters meth
if meth.document_self or not @track_visibility then
@@ -1379,15 +1483,13 @@ class RDoc::Parser::Ruby < RDoc::Parser
# it is a singleton or regular method.
def parse_method_name container # :nodoc:
- @scanner.lex_state = :EXPR_FNAME
-
skip_tkspace
name_t = get_tk
- back_tk = skip_tkspace
+ back_tk = skip_tkspace_without_nl
singleton = false
- case dot = get_tk
- when TkDOT, TkCOLON2 then
+ dot = get_tk
+ if dot[:kind] == :on_period || (dot[:kind] == :on_op && dot[:text] == '::') then
singleton = true
name, container = parse_method_name_singleton container, name_t
@@ -1408,16 +1510,15 @@ class RDoc::Parser::Ruby < RDoc::Parser
# is parsed from the token stream for a regular method.
def parse_method_name_regular container, name_t # :nodoc:
- case name_t
- when TkSTAR, TkAMPER then
- name_t.text
+ if :on_op == name_t[:kind] && (%w{* & [] []= <<}.include?(name_t[:text])) then
+ name_t[:text]
else
- unless name_t.respond_to? :name then
+ unless [:on_kw, :on_const, :on_ident].include?(name_t[:kind]) then
warn "expected method name token, . or ::, got #{name_t.inspect}"
skip_method container
return
end
- name_t.name
+ name_t[:text]
end
end
@@ -1427,47 +1528,42 @@ class RDoc::Parser::Ruby < RDoc::Parser
# for a singleton method.
def parse_method_name_singleton container, name_t # :nodoc:
- @scanner.lex_state = :EXPR_FNAME
skip_tkspace
name_t2 = get_tk
- name =
- case name_t
- when TkSELF, TkMOD then
- case name_t2
- # NOTE: work around '[' being consumed early and not being re-tokenized
- # as a TkAREF
- when TkfLBRACK then
- get_tk
- '[]'
- else
- name_t2.name
- end
- when TkCONSTANT then
- name = name_t2.name
+ if (:on_kw == name_t[:kind] && 'self' == name_t[:text]) || (:on_op == name_t[:kind] && '%' == name_t[:text]) then
+ # NOTE: work around '[' being consumed early
+ if :on_lbracket == name_t2[:kind]
+ get_tk
+ name = '[]'
+ else
+ name = name_t2[:text]
+ end
+ elsif :on_const == name_t[:kind] then
+ name = name_t2[:text]
- container = get_method_container container, name_t
+ container = get_method_container container, name_t
- return unless container
+ return unless container
- name
- when TkIDENTIFIER, TkIVAR, TkGVAR then
- parse_method_dummy container
+ name
+ elsif :on_ident == name_t[:kind] || :on_ivar == name_t[:kind] || :on_gvar == name_t[:kind] then
+ parse_method_dummy container
- nil
- when TkTRUE, TkFALSE, TkNIL then
- klass_name = "#{name_t.name.capitalize}Class"
- container = @store.find_class_named klass_name
- container ||= @top_level.add_class RDoc::NormalClass, klass_name
+ name = nil
+ elsif (:on_kw == name_t[:kind]) && ('true' == name_t[:text] || 'false' == name_t[:text] || 'nil' == name_t[:text]) then
+ klass_name = "#{name_t[:text].capitalize}Class"
+ container = @store.find_class_named klass_name
+ container ||= @top_level.add_class RDoc::NormalClass, klass_name
- name_t2.name
- else
- warn "unexpected method name token #{name_t.inspect}"
- # break
- skip_method container
+ name = name_t2[:text]
+ else
+ warn "unexpected method name token #{name_t.inspect}"
+ # break
+ skip_method container
- nil
- end
+ name = nil
+ end
return name, container
end
@@ -1477,45 +1573,56 @@ class RDoc::Parser::Ruby < RDoc::Parser
def parse_method_or_yield_parameters(method = nil,
modifiers = RDoc::METHOD_MODIFIERS)
- skip_tkspace false
+ skip_tkspace_without_nl
tk = get_tk
end_token = get_end_token tk
return '' unless end_token
nest = 0
+ continue = false
- loop do
- case tk
- when TkSEMICOLON then
+ while tk != nil do
+ case tk[:kind]
+ when :on_semicolon then
break if nest == 0
- when TkLBRACE, TkfLBRACE then
+ when :on_lbracket then
nest += 1
- when TkRBRACE then
+ when :on_rbracket then
+ nest -= 1
+ when :on_lbrace then
+ nest += 1
+ when :on_rbrace then
nest -= 1
if nest <= 0
# we might have a.each { |i| yield i }
unget_tk(tk) if nest < 0
break
end
- when TkLPAREN, TkfLPAREN then
+ when :on_lparen then
nest += 1
- when end_token then
- if end_token == TkRPAREN
+ when end_token[:kind] then
+ if end_token[:kind] == :on_rparen
nest -= 1
break if nest <= 0
else
- break unless @scanner.continue
+ break
end
- when TkRPAREN then
+ when :on_rparen then
nest -= 1
- when method && method.block_params.nil? && TkCOMMENT then
- unget_tk tk
- read_documentation_modifiers method, modifiers
- @read.pop
- when TkCOMMENT then
+ when :on_comment, :on_embdoc then
@read.pop
- when nil then
- break
+ if :on_nl == end_token[:kind] and "\n" == tk[:text][-1] and
+ (!continue or (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) != 0) then
+ if method && method.block_params.nil? then
+ unget_tk tk
+ read_documentation_modifiers method, modifiers
+ end
+ break if !continue and nest <= 0
+ end
+ when :on_comma then
+ continue = true
+ when :on_ident then
+ continue = false if continue
end
tk = get_tk
end
@@ -1539,7 +1646,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
return if method.block_params
- skip_tkspace false
+ skip_tkspace_without_nl
read_documentation_modifiers method, RDoc::METHOD_MODIFIERS
end
@@ -1549,7 +1656,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
def parse_module container, single, tk, comment
container, name_t, = get_class_or_module container
- name = name_t.name
+ name = name_t[:text]
mod = container.add_module RDoc::NormalModule, name
mod.ignore unless container.document_children
@@ -1559,6 +1666,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
mod.add_comment comment, @top_level
parse_statements mod
+ # after end modifiers
+ read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS
+
@stats.add_module mod
end
@@ -1569,12 +1679,12 @@ class RDoc::Parser::Ruby < RDoc::Parser
skip_tkspace_comment
tk = get_tk
- if TkLPAREN === tk then
+ if :on_lparen == tk[:kind] then
skip_tkspace_comment
tk = get_tk
end
- name = tk.text if TkSTRING === tk
+ name = tk[:text][1..-2] if :on_tstring == tk[:kind]
if name then
@top_level.add_require RDoc::Require.new(name, comment)
@@ -1587,19 +1697,30 @@ class RDoc::Parser::Ruby < RDoc::Parser
# Parses a rescue
def parse_rescue
- skip_tkspace false
+ skip_tkspace_without_nl
while tk = get_tk
- case tk
- when TkNL, TkSEMICOLON then
+ case tk[:kind]
+ when :on_nl, :on_semicolon, :on_comment then
break
- when TkCOMMA then
- skip_tkspace false
+ when :on_comma then
+ skip_tkspace_without_nl
- get_tk if TkNL === peek_tk
+ get_tk if :on_nl == peek_tk[:kind]
end
- skip_tkspace false
+ skip_tkspace_without_nl
+ end
+ end
+
+ ##
+ # Retrieve comment body without =begin/=end
+
+ def retrieve_comment_body(tk)
+ if :on_embdoc == tk[:kind]
+ tk[:text].gsub(/\A=begin.*\n/, '').gsub(/=end\n?\z/, '')
+ else
+ tk[:text]
end
end
@@ -1609,7 +1730,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
def parse_statements(container, single = NORMAL, current_method = nil,
comment = new_comment(''))
raise 'no' unless RDoc::Comment === comment
- comment.force_encoding @encoding if @encoding
+ comment = RDoc::Encoding.change_encoding comment, @encoding if @encoding
nest = 1
save_visibility = container.visibility
@@ -1620,32 +1741,50 @@ class RDoc::Parser::Ruby < RDoc::Parser
keep_comment = false
try_parse_comment = false
- non_comment_seen = true unless TkCOMMENT === tk
+ non_comment_seen = true unless (:on_comment == tk[:kind] or :on_embdoc == tk[:kind])
- case tk
- when TkNL then
- skip_tkspace
- tk = get_tk
+ case tk[:kind]
+ when :on_nl, :on_ignored_nl, :on_comment, :on_embdoc then
+ if :on_nl == tk[:kind] or :on_ignored_nl == tk[:kind]
+ skip_tkspace
+ tk = get_tk
+ else
+ past_tokens = @read.size > 1 ? @read[0..-2] : []
+ nl_position = 0
+ past_tokens.reverse.each_with_index do |read_tk, i|
+ if read_tk =~ /^\n$/ then
+ nl_position = (past_tokens.size - 1) - i
+ break
+ elsif read_tk =~ /^#.*\n$/ then
+ nl_position = ((past_tokens.size - 1) - i) + 1
+ break
+ end
+ end
+ comment_only_line = past_tokens[nl_position..-1].all?{ |c| c =~ /^\s+$/ }
+ unless comment_only_line then
+ tk = get_tk
+ end
+ end
- if TkCOMMENT === tk then
+ if tk and (:on_comment == tk[:kind] or :on_embdoc == tk[:kind]) then
if non_comment_seen then
# Look for RDoc in a comment about to be thrown away
non_comment_seen = parse_comment container, tk, comment unless
comment.empty?
comment = ''
- comment.force_encoding @encoding if @encoding
+ comment = RDoc::Encoding.change_encoding comment, @encoding if @encoding
end
- while TkCOMMENT === tk do
- comment << tk.text << "\n"
+ while tk and (:on_comment == tk[:kind] or :on_embdoc == tk[:kind]) do
+ comment_body = retrieve_comment_body(tk)
+ comment += comment_body
+ comment += "\n" unless "\n" == comment_body.chars.to_a.last
- tk = get_tk
-
- if TkNL === tk then
- skip_tkspace false # leading spaces
- tk = get_tk
+ if comment_body.size > 1 && "\n" == comment_body.chars.to_a.last then
+ skip_tkspace_without_nl # leading spaces
end
+ tk = get_tk
end
comment = new_comment comment
@@ -1666,60 +1805,78 @@ class RDoc::Parser::Ruby < RDoc::Parser
unget_tk tk
keep_comment = true
+ container.current_line_visibility = nil
- when TkCLASS then
- parse_class container, single, tk, comment
+ when :on_kw then
+ case tk[:text]
+ when 'class' then
+ parse_class container, single, tk, comment
- when TkMODULE then
- parse_module container, single, tk, comment
+ when 'module' then
+ parse_module container, single, tk, comment
- when TkDEF then
- parse_method container, single, tk, comment
+ when 'def' then
+ parse_method container, single, tk, comment
- when TkCONSTANT then
- unless parse_constant container, tk, comment, current_method then
- try_parse_comment = true
- end
+ when 'alias' then
+ parse_alias container, single, tk, comment unless current_method
- when TkALIAS then
- parse_alias container, single, tk, comment unless current_method
+ when 'yield' then
+ if current_method.nil? then
+ warn "Warning: yield outside of method" if container.document_self
+ else
+ parse_yield container, single, tk, current_method
+ end
- when TkYIELD then
- if current_method.nil? then
- warn "Warning: yield outside of method" if container.document_self
- else
- parse_yield container, single, tk, current_method
- end
+ when 'until', 'while' then
+ if (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) == 0
+ nest += 1
+ skip_optional_do_after_expression
+ end
- # Until and While can have a 'do', which shouldn't increase the nesting.
- # We can't solve the general case, but we can handle most occurrences by
- # ignoring a do at the end of a line.
+ # Until and While can have a 'do', which shouldn't increase the nesting.
+ # We can't solve the general case, but we can handle most occurrences by
+ # ignoring a do at the end of a line.
- when TkUNTIL, TkWHILE then
- nest += 1
- skip_optional_do_after_expression
+ # 'for' is trickier
+ when 'for' then
+ nest += 1
+ skip_for_variable
+ skip_optional_do_after_expression
- # 'for' is trickier
- when TkFOR then
- nest += 1
- skip_for_variable
- skip_optional_do_after_expression
+ when 'case', 'do', 'if', 'unless', 'begin' then
+ if (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) == 0
+ nest += 1
+ end
- when TkCASE, TkDO, TkIF, TkUNLESS, TkBEGIN then
- nest += 1
+ when 'super' then
+ current_method.calls_super = true if current_method
+
+ when 'rescue' then
+ parse_rescue
- when TkSUPER then
- current_method.calls_super = true if current_method
+ when 'end' then
+ nest -= 1
+ if nest == 0 then
+ container.ongoing_visibility = save_visibility
- when TkRESCUE then
- parse_rescue
+ parse_comment container, tk, comment unless comment.empty?
+
+ return
+ end
+ end
+
+ when :on_const then
+ unless parse_constant container, tk, comment, current_method then
+ try_parse_comment = true
+ end
- when TkIDENTIFIER then
+ when :on_ident then
if nest == 1 and current_method.nil? then
keep_comment = parse_identifier container, single, tk, comment
end
- case tk.name
+ case tk[:text]
when "require" then
parse_require container, comment
when "include" then
@@ -1728,16 +1885,6 @@ class RDoc::Parser::Ruby < RDoc::Parser
parse_extend_or_include RDoc::Extend, container, comment
end
- when TkEND then
- nest -= 1
- if nest == 0 then
- read_documentation_modifiers container, RDoc::CLASS_MODIFIERS
- container.ongoing_visibility = save_visibility
-
- parse_comment container, tk, comment unless comment.empty?
-
- return
- end
else
try_parse_comment = nest == 1
end
@@ -1751,7 +1898,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
unless keep_comment then
comment = new_comment ''
- comment.force_encoding @encoding if @encoding
+ comment = RDoc::Encoding.change_encoding comment, @encoding if @encoding
container.params = nil
container.block_params = nil
end
@@ -1769,8 +1916,8 @@ class RDoc::Parser::Ruby < RDoc::Parser
def parse_symbol_arg(no = nil)
skip_tkspace_comment
- case tk = get_tk
- when TkLPAREN
+ tk = get_tk
+ if tk[:kind] == :on_lparen
parse_symbol_arg_paren no
else
parse_symbol_arg_space no, tk
@@ -1792,10 +1939,10 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
skip_tkspace_comment
- case tk2 = get_tk
- when TkRPAREN
+ case (tk2 = get_tk)[:kind]
+ when :on_rparen
break
- when TkCOMMA
+ when :on_comma
else
warn("unexpected token: '#{tk2.inspect}'") if $DEBUG_RDOC
break
@@ -1819,10 +1966,10 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
loop do
- skip_tkspace false
+ skip_tkspace_without_nl
tk1 = get_tk
- unless TkCOMMA === tk1 then
+ if tk1.nil? || :on_comma != tk1[:kind] then
unget_tk tk1
break
end
@@ -1841,12 +1988,12 @@ class RDoc::Parser::Ruby < RDoc::Parser
# Returns symbol text from the next token
def parse_symbol_in_arg
- case tk = get_tk
- when TkSYMBOL
- tk.text.sub(/^:/, '')
- when TkSTRING
- eval @read[-1]
- when TkDSTRING, TkIDENTIFIER then
+ tk = get_tk
+ if :on_symbol == tk[:kind] then
+ tk[:text].sub(/^:/, '')
+ elsif :on_tstring == tk[:kind] then
+ tk[:text][1..-2]
+ elsif :on_dstring == tk[:kind] or :on_ident == tk[:kind] then
nil # ignore
else
warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG_RDOC
@@ -1880,27 +2027,44 @@ class RDoc::Parser::Ruby < RDoc::Parser
skip_tkspace_comment false
- case peek_tk
- # Ryan Davis suggested the extension to ignore modifiers, because he
- # often writes
- #
- # protected unless $TESTING
- #
- when TkNL, TkUNLESS_MOD, TkIF_MOD, TkSEMICOLON then
+ ptk = peek_tk
+ # Ryan Davis suggested the extension to ignore modifiers, because he
+ # often writes
+ #
+ # protected unless $TESTING
+ #
+ if [:on_nl, :on_semicolon].include?(ptk[:kind]) || (:on_kw == ptk[:kind] && (['if', 'unless'].include?(ptk[:text]))) then
container.ongoing_visibility = vis
+ elsif :on_kw == ptk[:kind] && 'def' == ptk[:text]
+ container.current_line_visibility = vis
else
update_visibility container, vis_type, vis, singleton
end
end
##
+ # Parses a Module#private_constant or Module#public_constant call from +tk+.
+
+ def parse_constant_visibility(container, single, tk)
+ args = parse_symbol_arg
+ case tk[:text]
+ when 'private_constant'
+ vis = :private
+ when 'public_constant'
+ vis = :public
+ else
+ raise RDoc::Error, 'Unreachable'
+ end
+ container.set_constant_visibility_for args, vis
+ end
+
+ ##
# Determines the block parameter for +context+
def parse_yield(context, single, tk, method)
return if method.block_params
get_tkread
- @scanner.continue = false
method.block_params = parse_method_or_yield_parameters
end
@@ -1924,11 +2088,10 @@ class RDoc::Parser::Ruby < RDoc::Parser
while tk = get_tk do
tokens << tk
- case tk
- when TkNL, TkDEF then
+ if :on_nl == tk[:kind] or (:on_kw == tk[:kind] && 'def' == tk[:text]) then
return
- when TkCOMMENT then
- return unless tk.text =~ /\s*:?([\w-]+):\s*(.*)/
+ elsif :on_comment == tk[:kind] or :on_embdoc == tk[:kind] then
+ return unless tk[:text] =~ /\s*:?([\w-]+):\s*(.*)/
directive = $1.downcase
@@ -1938,7 +2101,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
ensure
- unless tokens.length == 1 and TkCOMMENT === tokens.first then
+ unless tokens.length == 1 and (:on_comment == tokens.first[:kind] or :on_embdoc == tokens.first[:kind]) then
tokens.reverse_each do |token|
unget_tk token
end
@@ -1952,6 +2115,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
# See also RDoc::Markup::PreProcess#handle_directive
def read_documentation_modifiers context, allowed
+ skip_tkspace_without_nl
directive, value = read_directive allowed
return unless directive
@@ -1979,15 +2143,6 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
##
- # Removes private comments from +comment+
- #--
- # TODO remove
-
- def remove_private_comments comment
- comment.remove_private
- end
-
- ##
# Scans this Ruby file for Ruby constructs
def scan
@@ -1998,29 +2153,33 @@ class RDoc::Parser::Ruby < RDoc::Parser
parse_top_level_statements @top_level
rescue StandardError => e
- bytes = ''
-
- 20.times do @scanner.ungetc end
- count = 0
- 60.times do |i|
- count = i
- byte = @scanner.getc
- break unless byte
- bytes << byte
+ if @content.include?('<%') and @content.include?('%>') then
+ # Maybe, this is ERB.
+ $stderr.puts "\033[2KRDoc detects ERB file. Skips it for compatibility:"
+ $stderr.puts @file_name
+ return
end
- count -= 20
- count.times do @scanner.ungetc end
+
+ if @scanner_point >= @scanner.size
+ now_line_no = @scanner[@scanner.size - 1][:line_no]
+ else
+ now_line_no = peek_tk[:line_no]
+ end
+ first_tk_index = @scanner.find_index { |tk| tk[:line_no] == now_line_no }
+ last_tk_index = @scanner.find_index { |tk| tk[:line_no] == now_line_no + 1 }
+ last_tk_index = last_tk_index ? last_tk_index - 1 : @scanner.size - 1
+ code = @scanner[first_tk_index..last_tk_index].map{ |t| t[:text] }.join
$stderr.puts <<-EOF
-#{self.class} failure around line #{@scanner.line_no} of
+#{self.class} failure around line #{now_line_no} of
#{@file_name}
EOF
- unless bytes.empty? then
+ unless code.empty? then
+ $stderr.puts code
$stderr.puts
- $stderr.puts bytes.inspect
end
raise e
@@ -2034,53 +2193,52 @@ class RDoc::Parser::Ruby < RDoc::Parser
# while, until, and for have an optional do
def skip_optional_do_after_expression
- skip_tkspace false
+ skip_tkspace_without_nl
tk = get_tk
- end_token = get_end_token tk
b_nest = 0
nest = 0
- @scanner.continue = false
loop do
- case tk
- when TkSEMICOLON then
+ break unless tk
+ case tk[:kind]
+ when :on_semicolon, :on_nl, :on_ignored_nl then
break if b_nest.zero?
- when TkLPAREN, TkfLPAREN then
+ when :on_lparen then
nest += 1
- when TkBEGIN then
- b_nest += 1
- when TkEND then
- b_nest -= 1
- when TkDO
- break if nest.zero?
- when end_token then
- if end_token == TkRPAREN
- nest -= 1
- break if @scanner.lex_state == :EXPR_END and nest.zero?
- else
- break unless @scanner.continue
+ when :on_rparen then
+ nest -= 1
+ when :on_kw then
+ case tk[:text]
+ when 'begin'
+ b_nest += 1
+ when 'end'
+ b_nest -= 1
+ when 'do'
+ break if nest.zero?
+ end
+ when :on_comment, :on_embdoc then
+ if b_nest.zero? and "\n" == tk[:text][-1] then
+ break
end
- when nil then
- break
end
tk = get_tk
end
- skip_tkspace false
+ skip_tkspace_without_nl
- get_tk if TkDO === peek_tk
+ get_tk if peek_tk && :on_kw == peek_tk[:kind] && 'do' == peek_tk[:text]
end
##
# skip the var [in] part of a 'for' statement
def skip_for_variable
- skip_tkspace false
+ skip_tkspace_without_nl
get_tk
- skip_tkspace false
+ skip_tkspace_without_nl
tk = get_tk
- unget_tk(tk) unless TkIN === tk
+ unget_tk(tk) unless :on_kw == tk[:kind] and 'in' == tk[:text]
end
##
@@ -2097,8 +2255,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
def skip_tkspace_comment(skip_nl = true)
loop do
- skip_tkspace skip_nl
- return unless TkCOMMENT === peek_tk
+ skip_nl ? skip_tkspace : skip_tkspace_without_nl
+ next_tk = peek_tk
+ return if next_tk.nil? || (:on_comment != next_tk[:kind] and :on_embdoc != next_tk[:kind])
get_tk
end
end
diff --git a/lib/rdoc/parser/ruby_tools.rb b/lib/rdoc/parser/ruby_tools.rb
index bbca065b5e..681d7166ce 100644
--- a/lib/rdoc/parser/ruby_tools.rb
+++ b/lib/rdoc/parser/ruby_tools.rb
@@ -1,12 +1,9 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
-# Collection of methods for writing parsers against RDoc::RubyLex and
-# RDoc::RubyToken
+# Collection of methods for writing parsers
module RDoc::Parser::RubyTools
- include RDoc::RubyToken
-
##
# Adds a token listener +obj+, but you should probably use token_listener
@@ -22,37 +19,24 @@ module RDoc::Parser::RubyTools
tk = nil
if @tokens.empty? then
- tk = @scanner.token
- @read.push @scanner.get_readed
- puts "get_tk1 => #{tk.inspect}" if $TOKEN_DEBUG
+ if @scanner_point >= @scanner.size
+ return nil
+ else
+ tk = @scanner[@scanner_point]
+ @scanner_point += 1
+ @read.push tk[:text]
+ end
else
@read.push @unget_read.shift
tk = @tokens.shift
- puts "get_tk2 => #{tk.inspect}" if $TOKEN_DEBUG
end
- tk = nil if TkEND_OF_SCRIPT === tk
-
- if TkSYMBEG === tk then
- set_token_position tk.line_no, tk.char_no
-
- case tk1 = get_tk
- when TkId, TkOp, TkSTRING, TkDSTRING, TkSTAR, TkAMPER then
- if tk1.respond_to?(:name) then
- tk = Token(TkSYMBOL).set_text(":" + tk1.name)
- else
- tk = Token(TkSYMBOL).set_text(":" + tk1.text)
- end
-
- # remove the identifier we just read to replace it with a symbol
- @token_listeners.each do |obj|
- obj.pop_token
- end if @token_listeners
- else
- tk = tk1
- end
+ if tk == nil || :on___end__ == tk[:kind]
+ tk = nil
end
+ return nil unless tk
+
# inform any listeners of our shiny new token
@token_listeners.each do |obj|
obj.add_token(tk)
@@ -122,19 +106,34 @@ module RDoc::Parser::RubyTools
@tokens = []
@unget_read = []
@nest = 0
+ @scanner_point = 0
+ end
+
+ ##
+ # Skips whitespace tokens including newlines
+
+ def skip_tkspace
+ tokens = []
+
+ while (tk = get_tk) and (:on_sp == tk[:kind] or :on_nl == tk[:kind] or :on_ignored_nl == tk[:kind]) do
+ tokens.push(tk)
+ end
+
+ unget_tk(tk)
+ tokens
end
##
- # Skips whitespace tokens including newlines if +skip_nl+ is true
+ # Skips whitespace tokens excluding newlines
- def skip_tkspace(skip_nl = true) # HACK dup
+ def skip_tkspace_without_nl
tokens = []
- while TkSPACE === (tk = get_tk) or (skip_nl and TkNL === tk) do
- tokens.push tk
+ while (tk = get_tk) and :on_sp == tk[:kind] do
+ tokens.push(tk)
end
- unget_tk tk
+ unget_tk(tk)
tokens
end
diff --git a/lib/rdoc/parser/simple.rb b/lib/rdoc/parser/simple.rb
index f2ab27a92e..b1dabad0f8 100644
--- a/lib/rdoc/parser/simple.rb
+++ b/lib/rdoc/parser/simple.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Parse a non-source file. We basically take the whole thing as one big
# comment.
@@ -19,7 +19,7 @@ class RDoc::Parser::Simple < RDoc::Parser
preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
- preprocess.handle @content, @top_level
+ @content = preprocess.handle @content, @top_level
end
##
@@ -52,7 +52,7 @@ class RDoc::Parser::Simple < RDoc::Parser
def remove_private_comment comment
# Workaround for gsub encoding for Ruby 1.9.2 and earlier
empty = ''
- empty.force_encoding comment.encoding
+ empty = RDoc::Encoding.change_encoding empty, comment.encoding
comment = comment.gsub(%r%^--\n.*?^\+\+\n?%m, empty)
comment.sub(%r%^--\n.*%m, empty)
diff --git a/lib/rdoc/parser/text.rb b/lib/rdoc/parser/text.rb
index 1a13fd1186..01de0cc595 100644
--- a/lib/rdoc/parser/text.rb
+++ b/lib/rdoc/parser/text.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Indicates this parser is text and doesn't contain code constructs.
#
diff --git a/lib/rdoc/rd.rb b/lib/rdoc/rd.rb
index 39af3294f5..0d3d3cea85 100644
--- a/lib/rdoc/rd.rb
+++ b/lib/rdoc/rd.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# RDoc::RD implements the RD format from the rdtool gem.
#
diff --git a/lib/rdoc/rd/block_parser.rb b/lib/rdoc/rd/block_parser.rb
index fc4af97c1a..37b35a5924 100644
--- a/lib/rdoc/rd/block_parser.rb
+++ b/lib/rdoc/rd/block_parser.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
#
# DO NOT MODIFY!!!!
# This file is automatically generated by Racc 1.4.14
@@ -207,7 +208,7 @@ def next_token # :nodoc:
if @in_verbatim
[:STRINGLINE, line]
else
- @indent_stack.push("\s" << newIndent)
+ @indent_stack.push("\s" + newIndent)
[:ITEMLISTLINE, rest]
end
end
@@ -219,7 +220,7 @@ def next_token # :nodoc:
if @in_verbatim
[:STRINGLINE, line]
else
- @indent_stack.push("\s" * mark.size << newIndent)
+ @indent_stack.push("\s" * mark.size + newIndent)
[:ENUMLISTLINE, rest]
end
end
@@ -253,7 +254,7 @@ def next_token # :nodoc:
[:STRINGLINE, line]
end
else
- raise "[BUG] parsing error may occured."
+ raise "[BUG] parsing error may occurred."
end
end
@@ -419,52 +420,52 @@ end
racc_action_table = [
34, 35, 30, 33, 40, 34, 35, 30, 33, 40,
- 65, 34, 35, 30, 33, 14, 73, 14, 54, 76,
- 15, 88, 34, 35, 30, 33, 14, 73, 77, 33,
- 54, 15, 34, 35, 30, 33, 14, 73, 81, 38,
- 38, 15, 34, 35, 30, 33, 14, 73, 40, 36,
- 83, 15, 34, 35, 30, 33, 54, 47, 30, 35,
- 34, 15, 34, 35, 30, 33, 14, 73, 38, 67,
- 59, 15, 34, 35, 30, 33, 14, 9, 10, 11,
- 12, 15, 34, 35, 30, 33, 14, 73, 14, nil,
+ 65, 34, 35, 30, 33, 14, 73, 36, 38, 34,
+ 15, 88, 34, 35, 30, 33, 14, 9, 10, 11,
+ 12, 15, 34, 35, 30, 33, 14, 9, 10, 11,
+ 12, 15, 34, 35, 30, 33, 35, 47, 30, 54,
+ 33, 15, 34, 35, 30, 33, 54, 47, 14, 14,
+ 59, 15, 34, 35, 30, 33, 14, 73, 67, 76,
+ 77, 15, 34, 35, 30, 33, 14, 73, 54, 81,
+ 38, 15, 34, 35, 30, 33, 14, 73, 38, 40,
+ 83, 15, 34, 35, 30, 33, 14, 73, nil, nil,
nil, 15, 34, 35, 30, 33, 14, 73, nil, nil,
- nil, 15, 34, 35, 30, 33, nil, 47, nil, nil,
nil, 15, 34, 35, 30, 33, 14, 73, nil, nil,
nil, 15, 34, 35, 30, 33, 14, 73, nil, nil,
- nil, 15, 34, 35, 30, 33, 14, 9, 10, 11,
- 12, 15, 34, 35, 30, 33, 14, 73, 61, 63,
+ nil, 15, 34, 35, 30, 33, 14, 73, nil, nil,
+ nil, 15, 34, 35, 30, 33, 14, 73, 61, 63,
nil, 15, 14, 62, 60, 61, 63, 79, 61, 63,
62, 87, nil, 62, 34, 35, 30, 33 ]
racc_action_check = [
41, 41, 41, 41, 41, 15, 15, 15, 15, 15,
- 41, 86, 86, 86, 86, 86, 86, 34, 33, 49,
- 86, 86, 85, 85, 85, 85, 85, 85, 51, 31,
- 54, 85, 79, 79, 79, 79, 79, 79, 56, 57,
- 58, 79, 78, 78, 78, 78, 78, 78, 62, 1,
- 66, 78, 24, 24, 24, 24, 30, 24, 28, 25,
- 22, 24, 75, 75, 75, 75, 75, 75, 13, 44,
- 36, 75, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 46, 46, 46, 46, 46, 46, 35, nil,
- nil, 46, 45, 45, 45, 45, 45, 45, nil, nil,
- nil, 45, 27, 27, 27, 27, nil, 27, nil, nil,
- nil, 27, 74, 74, 74, 74, 74, 74, nil, nil,
- nil, 74, 68, 68, 68, 68, 68, 68, nil, nil,
- nil, 68, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 47, 47, 47, 47, 47, 47, 39, 39,
- nil, 47, 52, 39, 39, 82, 82, 52, 64, 64,
+ 41, 86, 86, 86, 86, 86, 86, 1, 13, 22,
+ 86, 86, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 24, 24, 24, 24, 25, 24, 28, 30,
+ 31, 24, 27, 27, 27, 27, 33, 27, 34, 35,
+ 36, 27, 45, 45, 45, 45, 45, 45, 44, 49,
+ 51, 45, 46, 46, 46, 46, 46, 46, 54, 56,
+ 57, 46, 47, 47, 47, 47, 47, 47, 58, 62,
+ 66, 47, 68, 68, 68, 68, 68, 68, nil, nil,
+ nil, 68, 74, 74, 74, 74, 74, 74, nil, nil,
+ nil, 74, 75, 75, 75, 75, 75, 75, nil, nil,
+ nil, 75, 78, 78, 78, 78, 78, 78, nil, nil,
+ nil, 78, 79, 79, 79, 79, 79, 79, nil, nil,
+ nil, 79, 85, 85, 85, 85, 85, 85, 39, 39,
+ nil, 85, 52, 39, 39, 82, 82, 52, 64, 64,
82, 82, nil, 64, 20, 20, 20, 20 ]
racc_action_pointer = [
- 129, 49, 69, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 61, nil, 2, nil, nil, nil, nil,
- 161, nil, 57, nil, 49, 55, nil, 99, 53, nil,
- 48, 23, nil, 10, 10, 81, 70, nil, nil, 141,
- nil, -3, nil, nil, 56, 89, 79, 139, nil, 6,
- nil, 15, 145, nil, 22, nil, 25, 32, 33, nil,
- nil, nil, 41, nil, 151, nil, 37, nil, 119, nil,
- nil, nil, nil, nil, 109, 59, nil, nil, 39, 29,
- nil, nil, 148, nil, nil, 19, 8, nil, nil ]
+ 19, 17, 29, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, 11, nil, 2, nil, nil, nil, nil,
+ 161, nil, 16, nil, 39, 42, nil, 49, 43, nil,
+ 41, 44, nil, 48, 51, 52, 60, nil, nil, 141,
+ nil, -3, nil, nil, 55, 59, 69, 79, nil, 56,
+ nil, 57, 145, nil, 70, nil, 66, 73, 81, nil,
+ nil, nil, 82, nil, 151, nil, 77, nil, 89, nil,
+ nil, nil, nil, nil, 99, 109, nil, nil, 119, 129,
+ nil, nil, 148, nil, nil, 139, 8, nil, nil ]
racc_action_default = [
-2, -73, -1, -4, -5, -6, -7, -8, -9, -10,
@@ -478,26 +479,26 @@ racc_action_default = [
-60, -47, -73, -29, -52, -48, -73, -20, -50 ]
racc_goto_table = [
- 4, 39, 4, 68, 74, 75, 6, 5, 6, 5,
- 44, 42, 51, 49, 3, 56, 37, 57, 58, 80,
+ 4, 39, 4, 68, 74, 75, 5, 6, 5, 6,
+ 44, 42, 51, 49, 3, 56, 37, 57, 58, 1,
2, 66, 84, 41, 43, 48, 50, 64, 84, 84,
- 46, 45, 42, 46, 45, 55, 85, 86, 1, 84,
+ 45, 46, 42, 45, 46, 55, 85, 86, 80, 84,
84, nil, nil, nil, nil, nil, nil, nil, 82, nil,
nil, nil, 78 ]
racc_goto_check = [
- 4, 10, 4, 31, 31, 31, 6, 5, 6, 5,
- 21, 12, 27, 21, 3, 27, 3, 9, 9, 33,
+ 4, 10, 4, 31, 31, 31, 5, 6, 5, 6,
+ 21, 12, 27, 21, 3, 27, 3, 9, 9, 1,
2, 11, 32, 17, 19, 23, 26, 10, 32, 32,
- 6, 5, 12, 6, 5, 29, 31, 31, 1, 32,
+ 5, 6, 12, 5, 6, 29, 31, 31, 33, 32,
32, nil, nil, nil, nil, nil, nil, nil, 10, nil,
nil, nil, 4 ]
racc_goto_pointer = [
- nil, 38, 20, 14, 0, 7, 6, nil, nil, -17,
+ nil, 19, 20, 14, 0, 6, 7, nil, nil, -17,
-14, -20, -9, nil, nil, nil, nil, 8, nil, 2,
nil, -14, nil, 0, nil, nil, -2, -18, nil, 4,
- nil, -42, -46, -35 ]
+ nil, -42, -46, -16 ]
racc_goto_default = [
nil, nil, nil, nil, 70, 71, 72, 7, 8, 13,
diff --git a/lib/rdoc/rd/inline.rb b/lib/rdoc/rd/inline.rb
index 011ec67e33..e5cb545728 100644
--- a/lib/rdoc/rd/inline.rb
+++ b/lib/rdoc/rd/inline.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Inline keeps track of markup and labels to create proper links.
@@ -50,11 +50,11 @@ class RDoc::RD::Inline
def append more
case more
when String then
- @reference << more
- @rdoc << more
+ @reference += more
+ @rdoc += more
when RDoc::RD::Inline then
- @reference << more.reference
- @rdoc << more.rdoc
+ @reference += more.reference
+ @rdoc += more.rdoc
else
raise "unknown thingy #{more}"
end
diff --git a/lib/rdoc/rd/inline_parser.rb b/lib/rdoc/rd/inline_parser.rb
index cc63ea6f70..85e4215964 100644
--- a/lib/rdoc/rd/inline_parser.rb
+++ b/lib/rdoc/rd/inline_parser.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
#
# DO NOT MODIFY!!!!
# This file is automatically generated by Racc 1.4.14
@@ -96,7 +97,7 @@ end
def parse inline
@inline = inline
@src = StringScanner.new inline
- @pre = ""
+ @pre = "".dup
@yydebug = true
do_parse.to_s
end
@@ -243,23 +244,34 @@ end
##### State transition tables begin ###
racc_action_table = [
- 104, 103, 102, 100, 101, 99, 115, 116, 117, 86,
+ 104, 103, 102, 100, 101, 99, 115, 116, 117, 29,
105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
- 164, 118, 119, 104, 103, 102, 100, 101, 99, 115,
- 116, 117, 175, 105, 106, 107, 108, 109, 110, 111,
- 112, 113, 114, 85, 118, 119, 63, 64, 65, 61,
- 81, 62, 76, 78, 79, 84, 66, 67, 68, 69,
- 70, 71, 72, 73, 74, 75, 77, 80, 149, 104,
- 103, 102, 100, 101, 99, 115, 116, 117, 29, 105,
- 106, 107, 108, 109, 110, 111, 112, 113, 114, 173,
+ 84, 118, 119, 63, 64, 65, 61, 81, 62, 76,
+ 78, 79, 85, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 77, 80, 149, 63, 64, 65, 153,
+ 81, 62, 76, 78, 79, 86, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 77, 80, 152, 104,
+ 103, 102, 100, 101, 99, 115, 116, 117, 87, 105,
+ 106, 107, 108, 109, 110, 111, 112, 113, 114, 88,
118, 119, 104, 103, 102, 100, 101, 99, 115, 116,
- 117, 137, 105, 106, 107, 108, 109, 110, 111, 112,
- 113, 114, 177, 118, 119, 63, 64, 65, 153, 81,
- 62, 76, 78, 79, 148, 66, 67, 68, 69, 70,
- 71, 72, 73, 74, 75, 77, 80, 152, 22, 23,
- 24, 25, 26, 21, 18, 19, 176, 177, 13, 124,
- 14, 96, 15, 89, 16, 154, 17, 88, 137, 20,
- 22, 23, 24, 25, 26, 21, 18, 19, 87, 161,
+ 117, 89, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 96, 118, 119, 104, 103, 102, 100, 101,
+ 99, 115, 116, 117, 124, 105, 106, 107, 108, 109,
+ 110, 111, 112, 113, 114, 137, 118, 119, 22, 23,
+ 24, 25, 26, 21, 18, 19, 176, 177, 13, 148,
+ 14, 154, 15, 137, 16, 161, 17, 164, 173, 20,
+ 22, 23, 24, 25, 26, 21, 18, 19, 175, 177,
+ 13, nil, 14, nil, 15, nil, 16, nil, 17, nil,
+ nil, 20, 22, 23, 24, 25, 26, 21, 18, 19,
+ nil, nil, 13, nil, 14, nil, 15, nil, 16, nil,
+ 17, nil, nil, 20, 22, 23, 24, 25, 26, 21,
+ 18, 19, nil, nil, 13, nil, 14, nil, 15, nil,
+ 16, nil, 17, nil, nil, 20, 22, 23, 24, 25,
+ 26, 21, 18, 19, nil, nil, 13, nil, 14, nil,
+ 15, nil, 16, nil, 17, nil, nil, 20, 22, 23,
+ 24, 25, 26, 21, 18, 19, nil, nil, 13, nil,
+ 14, nil, 15, nil, 16, nil, 17, nil, nil, 20,
+ 22, 23, 24, 25, 26, 21, 18, 19, nil, nil,
13, nil, 14, nil, 15, nil, 16, nil, 17, 42,
nil, 20, 54, 38, 53, 55, 56, 57, nil, 13,
nil, 14, nil, 15, nil, 16, nil, 17, nil, nil,
@@ -267,63 +279,63 @@ racc_action_table = [
nil, 13, nil, 14, nil, 15, nil, 16, nil, 17,
nil, nil, 20, 63, 64, 65, 61, 81, 62, 76,
78, 79, nil, 66, 67, 68, 69, 70, 71, 72,
- 73, 74, 75, 77, 80, 145, nil, nil, 54, 133,
- 53, 55, 56, 57, nil, 13, nil, 14, nil, 15,
- nil, 16, nil, 17, 145, nil, 20, 54, 133, 53,
- 55, 56, 57, nil, 13, nil, 14, nil, 15, nil,
- 16, nil, 17, nil, nil, 20, 22, 23, 24, 25,
- 26, 21, 18, 19, nil, nil, 13, nil, 14, nil,
- 15, nil, 16, nil, 17, 145, nil, 20, 54, 133,
+ 73, 74, 75, 77, 80, 122, nil, nil, 54, nil,
53, 55, 56, 57, nil, 13, nil, 14, nil, 15,
nil, 16, nil, 17, 145, nil, 20, 54, 133, 53,
55, 56, 57, nil, 13, nil, 14, nil, 15, nil,
- 16, nil, 17, nil, nil, 20, 22, 23, 24, 25,
- 26, 21, 18, 19, nil, nil, 13, nil, 14, nil,
- 15, nil, 16, nil, 17, nil, nil, 20, 22, 23,
- 24, 25, 26, 21, 18, 19, nil, nil, 13, nil,
- 14, nil, 15, nil, 16, nil, 17, nil, nil, 20,
- 22, 23, 24, 25, 26, 21, 18, 19, nil, nil,
- 13, nil, 14, nil, 15, nil, 16, nil, 17, nil,
- nil, 20, 22, 23, 24, 25, 26, 21, 18, 19,
- nil, nil, 13, nil, 14, nil, 15, nil, 16, 122,
- 17, nil, 54, 20, 53, 55, 56, 57, nil, 13,
- nil, 14, nil, 15, nil, 16, nil, 17, nil, nil,
- 20, 135, 136, 54, 133, 53, 55, 56, 57, nil,
- 13, nil, 14, nil, 15, nil, 16, nil, 17, nil,
- nil, 20, 135, 136, 54, 133, 53, 55, 56, 57,
+ 16, nil, 17, 145, nil, 20, 54, 133, 53, 55,
+ 56, 57, nil, 13, nil, 14, nil, 15, nil, 16,
+ nil, 17, 145, nil, 20, 54, 133, 53, 55, 56,
+ 57, nil, 13, nil, 14, nil, 15, nil, 16, nil,
+ 17, 145, nil, 20, 54, 133, 53, 55, 56, 57,
nil, 13, nil, 14, nil, 15, nil, 16, nil, 17,
nil, nil, 20, 135, 136, 54, 133, 53, 55, 56,
- 57, nil, 13, nil, 14, nil, 15, nil, 16, 158,
- 17, nil, 54, 20, 53, 55, 56, 57, 95, nil,
- nil, 54, 91, 53, 55, 56, 57, 145, nil, nil,
- 54, 133, 53, 55, 56, 57, 165, 135, 136, 54,
- 133, 53, 55, 56, 57, 145, nil, nil, 54, 133,
- 53, 55, 56, 57, 172, 135, 136, 54, 133, 53,
- 55, 56, 57, 174, 135, 136, 54, 133, 53, 55,
- 56, 57, 178, 135, 136, 54, 133, 53, 55, 56,
- 57, 135, 136, 54, 133, 53, 55, 56, 57, 135,
- 136, 54, 133, 53, 55, 56, 57, 135, 136, 54,
- 133, 53, 55, 56, 57, 22, 23, 24, 25, 26,
- 21 ]
+ 57, nil, 13, nil, 14, nil, 15, nil, 16, nil,
+ 17, nil, nil, 20, 135, 136, 54, 133, 53, 55,
+ 56, 57, nil, 13, nil, 14, nil, 15, nil, 16,
+ nil, 17, nil, nil, 20, 135, 136, 54, 133, 53,
+ 55, 56, 57, nil, 13, nil, 14, nil, 15, nil,
+ 16, nil, 17, 95, nil, 20, 54, 91, 53, 55,
+ 56, 57, 145, nil, nil, 54, 133, 53, 55, 56,
+ 57, 158, nil, nil, 54, nil, 53, 55, 56, 57,
+ 165, 135, 136, 54, 133, 53, 55, 56, 57, 145,
+ nil, nil, 54, 133, 53, 55, 56, 57, 172, 135,
+ 136, 54, 133, 53, 55, 56, 57, 174, 135, 136,
+ 54, 133, 53, 55, 56, 57, 178, 135, 136, 54,
+ 133, 53, 55, 56, 57, 135, 136, 54, 133, 53,
+ 55, 56, 57, 135, 136, 54, 133, 53, 55, 56,
+ 57, 135, 136, 54, 133, 53, 55, 56, 57, 22,
+ 23, 24, 25, 26, 21 ]
racc_action_check = [
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 32,
+ 38, 38, 38, 38, 38, 38, 38, 38, 38, 1,
38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 125, 38, 38, 97, 97, 97, 97, 97, 97, 97,
- 97, 97, 164, 97, 97, 97, 97, 97, 97, 97,
- 97, 97, 97, 31, 97, 97, 59, 59, 59, 59,
- 59, 59, 59, 59, 59, 29, 59, 59, 59, 59,
- 59, 59, 59, 59, 59, 59, 59, 59, 59, 91,
- 91, 91, 91, 91, 91, 91, 91, 91, 1, 91,
- 91, 91, 91, 91, 91, 91, 91, 91, 91, 162,
- 91, 91, 155, 155, 155, 155, 155, 155, 155, 155,
- 155, 43, 155, 155, 155, 155, 155, 155, 155, 155,
- 155, 155, 172, 155, 155, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 58, 61, 61, 61, 61, 61,
- 61, 61, 61, 61, 61, 61, 61, 61, 16, 16,
- 16, 16, 16, 16, 16, 16, 165, 165, 16, 41,
- 16, 37, 16, 35, 16, 90, 16, 34, 94, 16,
- 17, 17, 17, 17, 17, 17, 17, 17, 33, 100,
+ 29, 38, 38, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 31, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 32, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61, 61, 91,
+ 91, 91, 91, 91, 91, 91, 91, 91, 33, 91,
+ 91, 91, 91, 91, 91, 91, 91, 91, 91, 34,
+ 91, 91, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 35, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 37, 97, 97, 155, 155, 155, 155, 155,
+ 155, 155, 155, 155, 41, 155, 155, 155, 155, 155,
+ 155, 155, 155, 155, 155, 43, 155, 155, 0, 0,
+ 0, 0, 0, 0, 0, 0, 165, 165, 0, 58,
+ 0, 90, 0, 94, 0, 100, 0, 125, 162, 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 164, 172,
+ 2, nil, 2, nil, 2, nil, 2, nil, 2, nil,
+ nil, 2, 13, 13, 13, 13, 13, 13, 13, 13,
+ nil, nil, 13, nil, 13, nil, 13, nil, 13, nil,
+ 13, nil, nil, 13, 14, 14, 14, 14, 14, 14,
+ 14, 14, nil, nil, 14, nil, 14, nil, 14, nil,
+ 14, nil, 14, nil, nil, 14, 15, 15, 15, 15,
+ 15, 15, 15, 15, nil, nil, 15, nil, 15, nil,
+ 15, nil, 15, nil, 15, nil, nil, 15, 16, 16,
+ 16, 16, 16, 16, 16, 16, nil, nil, 16, nil,
+ 16, nil, 16, nil, 16, nil, 16, nil, nil, 16,
+ 17, 17, 17, 17, 17, 17, 17, 17, nil, nil,
17, nil, 17, nil, 17, nil, 17, nil, 17, 18,
nil, 17, 18, 18, 18, 18, 18, 18, nil, 18,
nil, 18, nil, 18, nil, 18, nil, 18, nil, nil,
@@ -331,64 +343,53 @@ racc_action_check = [
nil, 19, nil, 19, nil, 19, nil, 19, nil, 19,
nil, nil, 19, 20, 20, 20, 20, 20, 20, 20,
20, 20, nil, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 146, nil, nil, 146, 146,
- 146, 146, 146, 146, nil, 146, nil, 146, nil, 146,
- nil, 146, nil, 146, 138, nil, 146, 138, 138, 138,
- 138, 138, 138, nil, 138, nil, 138, nil, 138, nil,
- 138, nil, 138, nil, nil, 138, 0, 0, 0, 0,
- 0, 0, 0, 0, nil, nil, 0, nil, 0, nil,
- 0, nil, 0, nil, 0, 45, nil, 0, 45, 45,
- 45, 45, 45, 45, nil, 45, nil, 45, nil, 45,
- nil, 45, nil, 45, 44, nil, 45, 44, 44, 44,
+ 20, 20, 20, 20, 20, 39, nil, nil, 39, nil,
+ 39, 39, 39, 39, nil, 39, nil, 39, nil, 39,
+ nil, 39, nil, 39, 44, nil, 39, 44, 44, 44,
44, 44, 44, nil, 44, nil, 44, nil, 44, nil,
- 44, nil, 44, nil, nil, 44, 2, 2, 2, 2,
- 2, 2, 2, 2, nil, nil, 2, nil, 2, nil,
- 2, nil, 2, nil, 2, nil, nil, 2, 13, 13,
- 13, 13, 13, 13, 13, 13, nil, nil, 13, nil,
- 13, nil, 13, nil, 13, nil, 13, nil, nil, 13,
- 14, 14, 14, 14, 14, 14, 14, 14, nil, nil,
- 14, nil, 14, nil, 14, nil, 14, nil, 14, nil,
- nil, 14, 15, 15, 15, 15, 15, 15, 15, 15,
- nil, nil, 15, nil, 15, nil, 15, nil, 15, 39,
- 15, nil, 39, 15, 39, 39, 39, 39, nil, 39,
- nil, 39, nil, 39, nil, 39, nil, 39, nil, nil,
- 39, 42, 42, 42, 42, 42, 42, 42, 42, nil,
+ 44, nil, 44, 45, nil, 44, 45, 45, 45, 45,
+ 45, 45, nil, 45, nil, 45, nil, 45, nil, 45,
+ nil, 45, 138, nil, 45, 138, 138, 138, 138, 138,
+ 138, nil, 138, nil, 138, nil, 138, nil, 138, nil,
+ 138, 146, nil, 138, 146, 146, 146, 146, 146, 146,
+ nil, 146, nil, 146, nil, 146, nil, 146, nil, 146,
+ nil, nil, 146, 42, 42, 42, 42, 42, 42, 42,
42, nil, 42, nil, 42, nil, 42, nil, 42, nil,
- nil, 42, 127, 127, 127, 127, 127, 127, 127, 127,
- nil, 127, nil, 127, nil, 127, nil, 127, nil, 127,
- nil, nil, 127, 122, 122, 122, 122, 122, 122, 122,
- 122, nil, 122, nil, 122, nil, 122, nil, 122, 92,
- 122, nil, 92, 122, 92, 92, 92, 92, 36, nil,
- nil, 36, 36, 36, 36, 36, 36, 52, nil, nil,
- 52, 52, 52, 52, 52, 52, 126, 126, 126, 126,
- 126, 126, 126, 126, 126, 142, nil, nil, 142, 142,
- 142, 142, 142, 142, 159, 159, 159, 159, 159, 159,
- 159, 159, 159, 163, 163, 163, 163, 163, 163, 163,
- 163, 163, 171, 171, 171, 171, 171, 171, 171, 171,
- 171, 95, 95, 95, 95, 95, 95, 95, 95, 158,
- 158, 158, 158, 158, 158, 158, 158, 168, 168, 168,
- 168, 168, 168, 168, 168, 27, 27, 27, 27, 27,
- 27 ]
+ 42, nil, nil, 42, 122, 122, 122, 122, 122, 122,
+ 122, 122, nil, 122, nil, 122, nil, 122, nil, 122,
+ nil, 122, nil, nil, 122, 127, 127, 127, 127, 127,
+ 127, 127, 127, nil, 127, nil, 127, nil, 127, nil,
+ 127, nil, 127, 36, nil, 127, 36, 36, 36, 36,
+ 36, 36, 52, nil, nil, 52, 52, 52, 52, 52,
+ 52, 92, nil, nil, 92, nil, 92, 92, 92, 92,
+ 126, 126, 126, 126, 126, 126, 126, 126, 126, 142,
+ nil, nil, 142, 142, 142, 142, 142, 142, 159, 159,
+ 159, 159, 159, 159, 159, 159, 159, 163, 163, 163,
+ 163, 163, 163, 163, 163, 163, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 95, 95, 95, 95, 95,
+ 95, 95, 95, 158, 158, 158, 158, 158, 158, 158,
+ 158, 168, 168, 168, 168, 168, 168, 168, 168, 27,
+ 27, 27, 27, 27, 27 ]
racc_action_pointer = [
- 283, 78, 343, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, 365, 387, 409, 135, 157, 176, 198,
- 220, nil, nil, nil, nil, nil, nil, 602, nil, 55,
- nil, 29, -7, 150, 137, 131, 515, 128, -3, 426,
- nil, 145, 447, 96, 321, 302, nil, nil, nil, nil,
- nil, nil, 524, nil, nil, nil, nil, nil, 113, 43,
- nil, 112, nil, nil, nil, nil, nil, nil, nil, nil,
+ 135, 9, 157, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, 179, 201, 223, 245, 267, 286, 308,
+ 330, nil, nil, nil, nil, nil, nil, 606, nil, 20,
+ nil, 18, 39, 60, 69, 79, 510, 89, -3, 352,
+ nil, 120, 449, 130, 371, 390, nil, nil, nil, nil,
+ nil, nil, 519, nil, nil, nil, nil, nil, 138, 20,
+ nil, 43, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 132, 66, 506, nil, 153, 577, nil, 20, nil, nil,
- 163, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ 128, 66, 528, nil, 148, 581, nil, 89, nil, nil,
+ 149, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 489, nil, nil, 17, 533, 468, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 261, nil,
- nil, nil, 542, nil, nil, nil, 242, nil, nil, nil,
- nil, nil, nil, nil, nil, 89, nil, nil, 585, 551,
- nil, nil, 86, 560, 28, 142, nil, nil, 593, nil,
- nil, 569, 107, nil, nil, nil, nil, nil, nil ]
+ nil, nil, 470, nil, nil, 154, 537, 491, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, 409, nil,
+ nil, nil, 546, nil, nil, nil, 428, nil, nil, nil,
+ nil, nil, nil, nil, nil, 112, nil, nil, 589, 555,
+ nil, nil, 155, 564, 164, 142, nil, nil, 597, nil,
+ nil, 573, 164, nil, nil, nil, nil, nil, nil ]
racc_action_default = [
-138, -138, -1, -3, -4, -5, -6, -7, -8, -9,
@@ -411,15 +412,15 @@ racc_action_default = [
-60, -138, -34, -36, -37, -29, -30, -32, -34 ]
racc_goto_table = [
- 126, 44, 125, 52, 144, 144, 160, 93, 97, 43,
- 166, 82, 144, 40, 41, 39, 138, 146, 169, 90,
- 36, 52, 44, 1, 52, 129, 169, 94, 59, 83,
- 123, 30, 151, 92, 120, 121, 31, 32, 33, 34,
- 35, 170, 58, 166, 167, 147, 170, 166, 37, nil,
+ 126, 44, 125, 43, 144, 144, 160, 93, 97, 52,
+ 166, 82, 144, 40, 41, 39, 138, 146, 169, 30,
+ 36, 94, 44, 1, 123, 129, 169, 52, 90, 37,
+ 52, 167, 147, 92, 120, 121, 31, 32, 33, 34,
+ 35, 170, 58, 166, 59, 83, 170, 166, 151, nil,
150, nil, 166, 159, 4, 166, 4, nil, nil, nil,
nil, 155, nil, 156, 160, nil, nil, 4, 4, 4,
- 4, 4, nil, 4, 5, nil, 5, 52, nil, nil,
- 163, nil, 162, 157, nil, 168, nil, 5, 5, 5,
+ 4, 4, nil, 4, 5, nil, 5, 157, nil, nil,
+ 163, nil, 162, 52, nil, 168, nil, 5, 5, 5,
5, 5, nil, 5, nil, nil, nil, nil, 144, nil,
nil, nil, 144, nil, nil, 129, 144, 144, nil, 6,
129, 6, nil, nil, nil, nil, 171, 7, nil, 7,
@@ -429,15 +430,15 @@ racc_goto_table = [
11, 11, 11, nil, 11 ]
racc_goto_check = [
- 22, 24, 21, 34, 36, 36, 37, 18, 16, 23,
- 35, 41, 36, 19, 20, 17, 25, 25, 28, 14,
- 13, 34, 24, 1, 34, 24, 28, 23, 38, 39,
- 23, 3, 42, 17, 19, 20, 1, 1, 1, 1,
- 1, 33, 1, 35, 29, 32, 33, 35, 15, nil,
+ 22, 24, 21, 23, 36, 36, 37, 18, 16, 34,
+ 35, 41, 36, 19, 20, 17, 25, 25, 28, 3,
+ 13, 23, 24, 1, 23, 24, 28, 34, 14, 15,
+ 34, 29, 32, 17, 19, 20, 1, 1, 1, 1,
+ 1, 33, 1, 35, 38, 39, 33, 35, 42, nil,
41, nil, 35, 22, 4, 35, 4, nil, nil, nil,
nil, 16, nil, 18, 37, nil, nil, 4, 4, 4,
- 4, 4, nil, 4, 5, nil, 5, 34, nil, nil,
- 22, nil, 21, 23, nil, 22, nil, 5, 5, 5,
+ 4, 4, nil, 4, 5, nil, 5, 23, nil, nil,
+ 22, nil, 21, 34, nil, 22, nil, 5, 5, 5,
5, 5, nil, 5, nil, nil, nil, nil, 36, nil,
nil, nil, 36, nil, nil, 24, 36, 36, nil, 6,
24, 6, nil, nil, nil, nil, 22, 7, nil, 7,
@@ -447,11 +448,11 @@ racc_goto_check = [
11, 11, 11, nil, 11 ]
racc_goto_pointer = [
- nil, 23, nil, 29, 54, 74, 109, 117, 127, nil,
- nil, 135, nil, 2, -17, 30, -30, -3, -29, -5,
- -4, -40, -42, -9, -17, -28, nil, nil, -120, -83,
- nil, nil, -7, -101, -15, -116, -40, -91, 8, 2,
- nil, -9, -29 ]
+ nil, 23, nil, 17, 54, 74, 109, 117, 127, nil,
+ nil, 135, nil, 2, -8, 11, -30, -3, -29, -5,
+ -4, -40, -42, -15, -17, -28, nil, nil, -120, -96,
+ nil, nil, -20, -101, -9, -116, -40, -91, 24, 18,
+ nil, -9, -13 ]
racc_goto_default = [
nil, nil, 2, 3, 46, 47, 48, 49, 50, 9,
diff --git a/lib/rdoc/rdoc.gemspec b/lib/rdoc/rdoc.gemspec
index e605560868..f28d3e86e5 100644
--- a/lib/rdoc/rdoc.gemspec
+++ b/lib/rdoc/rdoc.gemspec
@@ -1,6 +1,9 @@
-# -*- encoding: utf-8 -*-
-$:.unshift File.expand_path("../lib", __FILE__)
-require 'rdoc'
+begin
+ require_relative "lib/rdoc/version"
+rescue LoadError
+ # for Ruby repository
+ require_relative "version"
+end
Gem::Specification.new do |s|
s.name = "rdoc"
@@ -12,25 +15,26 @@ Gem::Specification.new do |s|
"Phil Hagelberg",
"Tony Strauss",
"Zachary Scott",
- "Hiroshi SHIBATA"
+ "Hiroshi SHIBATA",
+ "ITOYANAGI Sakura"
]
- s.email = ["drbrain@segment7.net", "", "", "", "mail@zzak.io", "hsbt@ruby-lang.org"]
+ s.email = ["drbrain@segment7.net", "", "", "", "mail@zzak.io", "hsbt@ruby-lang.org", "aycabta@gmail.com"]
s.summary = "RDoc produces HTML and command-line documentation for Ruby projects"
s.description = <<-DESCRIPTION
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.
DESCRIPTION
- s.homepage = "https://rdoc.github.io/rdoc"
+ s.homepage = "https://ruby.github.io/rdoc"
s.licenses = ["Ruby"]
s.bindir = "exe"
s.executables = ["rdoc", "ri"]
s.require_paths = ["lib"]
# for ruby core repository. It was generated by `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
- s.files = [".document", ".gitignore", ".travis.yml", "CONTRIBUTING.rdoc", "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/gauntlet_rdoc.rb", "lib/rdoc.rb", "lib/rdoc/alias.rb", "lib/rdoc/anon_class.rb", "lib/rdoc/any_method.rb", "lib/rdoc/attr.rb", "lib/rdoc/class_module.rb", "lib/rdoc/code_object.rb", "lib/rdoc/code_objects.rb", "lib/rdoc/comment.rb", "lib/rdoc/constant.rb", "lib/rdoc/context.rb", "lib/rdoc/context/section.rb", "lib/rdoc/cross_reference.rb", "lib/rdoc/encoding.rb", "lib/rdoc/erb_partial.rb", "lib/rdoc/erbio.rb", "lib/rdoc/extend.rb", "lib/rdoc/generator.rb", "lib/rdoc/generator/darkfish.rb", "lib/rdoc/generator/json_index.rb", "lib/rdoc/generator/markup.rb", "lib/rdoc/generator/pot.rb", "lib/rdoc/generator/pot/message_extractor.rb", "lib/rdoc/generator/pot/po.rb", "lib/rdoc/generator/pot/po_entry.rb", "lib/rdoc/generator/ri.rb", "lib/rdoc/generator/template/darkfish/.document", "lib/rdoc/generator/template/darkfish/_footer.rhtml", "lib/rdoc/generator/template/darkfish/_head.rhtml","lib/rdoc/generator/template/darkfish/_sidebar_VCS_info.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_classes.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_in_files.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_methods.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_navigation.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_parent.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml", "lib/rdoc/generator/template/darkfish/class.rhtml", "lib/rdoc/generator/template/darkfish/css/fonts.css", "lib/rdoc/generator/template/darkfish/css/rdoc.css", "lib/rdoc/generator/template/darkfish/fonts/Lato-Light.ttf", "lib/rdoc/generator/template/darkfish/fonts/Lato-LightItalic.ttf", "lib/rdoc/generator/template/darkfish/fonts/Lato-Regular.ttf", "lib/rdoc/generator/template/darkfish/fonts/Lato-RegularItalic.ttf", "lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Bold.ttf", "lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Regular.ttf", "lib/rdoc/generator/template/darkfish/images/add.png", "lib/rdoc/generator/template/darkfish/images/arrow_up.png", "lib/rdoc/generator/template/darkfish/images/brick.png", "lib/rdoc/generator/template/darkfish/images/brick_link.png", "lib/rdoc/generator/template/darkfish/images/bug.png", "lib/rdoc/generator/template/darkfish/images/bullet_black.png", "lib/rdoc/generator/template/darkfish/images/bullet_toggle_minus.png", "lib/rdoc/generator/template/darkfish/images/bullet_toggle_plus.png", "lib/rdoc/generator/template/darkfish/images/date.png", "lib/rdoc/generator/template/darkfish/images/delete.png", "lib/rdoc/generator/template/darkfish/images/find.png", "lib/rdoc/generator/template/darkfish/images/loadingAnimation.gif", "lib/rdoc/generator/template/darkfish/images/macFFBgHack.png", "lib/rdoc/generator/template/darkfish/images/package.png", "lib/rdoc/generator/template/darkfish/images/page_green.png", "lib/rdoc/generator/template/darkfish/images/page_white_text.png", "lib/rdoc/generator/template/darkfish/images/page_white_width.png", "lib/rdoc/generator/template/darkfish/images/plugin.png", "lib/rdoc/generator/template/darkfish/images/ruby.png", "lib/rdoc/generator/template/darkfish/images/tag_blue.png", "lib/rdoc/generator/template/darkfish/images/tag_green.png", "lib/rdoc/generator/template/darkfish/images/transparent.png", "lib/rdoc/generator/template/darkfish/images/wrench.png", "lib/rdoc/generator/template/darkfish/images/wrench_orange.png", "lib/rdoc/generator/template/darkfish/images/zoom.png", "lib/rdoc/generator/template/darkfish/index.rhtml", "lib/rdoc/generator/template/darkfish/js/darkfish.js", "lib/rdoc/generator/template/darkfish/js/jquery.js", "lib/rdoc/generator/template/darkfish/js/search.js", "lib/rdoc/generator/template/darkfish/page.rhtml", "lib/rdoc/generator/template/darkfish/servlet_not_found.rhtml", "lib/rdoc/generator/template/darkfish/servlet_root.rhtml", "lib/rdoc/generator/template/darkfish/table_of_contents.rhtml", "lib/rdoc/generator/template/json_index/.document", "lib/rdoc/generator/template/json_index/js/navigation.js", "lib/rdoc/generator/template/json_index/js/searcher.js", "lib/rdoc/ghost_method.rb", "lib/rdoc/i18n.rb", "lib/rdoc/i18n/locale.rb", "lib/rdoc/i18n/text.rb", "lib/rdoc/include.rb", "lib/rdoc/known_classes.rb", "lib/rdoc/markdown.kpeg", "lib/rdoc/markdown/entities.rb", "lib/rdoc/markdown/literals.kpeg", "lib/rdoc/markdown/literals.rb", "lib/rdoc/markup.rb", "lib/rdoc/markup/attr_changer.rb", "lib/rdoc/markup/attr_span.rb", "lib/rdoc/markup/attribute_manager.rb", "lib/rdoc/markup/attributes.rb", "lib/rdoc/markup/blank_line.rb", "lib/rdoc/markup/block_quote.rb", "lib/rdoc/markup/document.rb", "lib/rdoc/markup/formatter.rb", "lib/rdoc/markup/formatter_test_case.rb", "lib/rdoc/markup/hard_break.rb", "lib/rdoc/markup/heading.rb", "lib/rdoc/markup/include.rb", "lib/rdoc/markup/indented_paragraph.rb", "lib/rdoc/markup/inline.rb", "lib/rdoc/markup/list.rb", "lib/rdoc/markup/list_item.rb", "lib/rdoc/markup/paragraph.rb", "lib/rdoc/markup/parser.rb", "lib/rdoc/markup/pre_process.rb", "lib/rdoc/markup/raw.rb", "lib/rdoc/markup/rule.rb", "lib/rdoc/markup/special.rb", "lib/rdoc/markup/text_formatter_test_case.rb", "lib/rdoc/markup/to_ansi.rb", "lib/rdoc/markup/to_bs.rb", "lib/rdoc/markup/to_html.rb", "lib/rdoc/markup/to_html_crossref.rb", "lib/rdoc/markup/to_html_snippet.rb", "lib/rdoc/markup/to_joined_paragraph.rb", "lib/rdoc/markup/to_label.rb", "lib/rdoc/markup/to_markdown.rb", "lib/rdoc/markup/to_rdoc.rb", "lib/rdoc/markup/to_table_of_contents.rb", "lib/rdoc/markup/to_test.rb", "lib/rdoc/markup/to_tt_only.rb", "lib/rdoc/markup/verbatim.rb", "lib/rdoc/meta_method.rb", "lib/rdoc/method_attr.rb", "lib/rdoc/mixin.rb", "lib/rdoc/normal_class.rb", "lib/rdoc/normal_module.rb", "lib/rdoc/options.rb", "lib/rdoc/parser.rb", "lib/rdoc/parser/c.rb", "lib/rdoc/parser/changelog.rb", "lib/rdoc/parser/markdown.rb", "lib/rdoc/parser/rd.rb", "lib/rdoc/parser/ruby.rb", "lib/rdoc/parser/ruby_tools.rb", "lib/rdoc/parser/simple.rb", "lib/rdoc/parser/text.rb", "lib/rdoc/rd.rb", "lib/rdoc/rd/block_parser.ry", "lib/rdoc/rd/inline.rb", "lib/rdoc/rd/inline_parser.ry", "lib/rdoc/rdoc.rb", "lib/rdoc/require.rb", "lib/rdoc/ri.rb", "lib/rdoc/ri/driver.rb", "lib/rdoc/ri/formatter.rb", "lib/rdoc/ri/paths.rb", "lib/rdoc/ri/store.rb", "lib/rdoc/ri/task.rb", "lib/rdoc/ruby_lex.rb", "lib/rdoc/ruby_token.rb", "lib/rdoc/rubygems_hook.rb", "lib/rdoc/servlet.rb", "lib/rdoc/single_class.rb", "lib/rdoc/stats.rb", "lib/rdoc/stats/normal.rb", "lib/rdoc/stats/quiet.rb", "lib/rdoc/stats/verbose.rb", "lib/rdoc/store.rb", "lib/rdoc/task.rb", "lib/rdoc/test_case.rb", "lib/rdoc/text.rb", "lib/rdoc/token_stream.rb", "lib/rdoc/tom_doc.rb", "lib/rdoc/top_level.rb", "rdoc.gemspec"]
# files from .gitignore
- s.files << "lib/rdoc/rd/block_parser.rb" << "lib/rdoc/rd/inline_parser.rb" << "lib/rdoc/markdown.rb"
+ s.files = [".document", ".gitignore", ".travis.yml", "CONTRIBUTING.rdoc", "CVE-2013-0256.rdoc", "ExampleMarkdown.md", "ExampleRDoc.rdoc", "Gemfile", "History.rdoc", "LEGAL.rdoc", "LICENSE.rdoc", "README.rdoc", "RI.rdoc", "Rakefile", "TODO.rdoc", "appveyor.yml", "bin/console", "bin/setup", "exe/rdoc", "exe/ri", "lib/rdoc.rb", "lib/rdoc/alias.rb", "lib/rdoc/anon_class.rb", "lib/rdoc/any_method.rb", "lib/rdoc/attr.rb", "lib/rdoc/class_module.rb", "lib/rdoc/code_object.rb", "lib/rdoc/code_objects.rb", "lib/rdoc/comment.rb", "lib/rdoc/constant.rb", "lib/rdoc/context.rb", "lib/rdoc/context/section.rb", "lib/rdoc/cross_reference.rb", "lib/rdoc/encoding.rb", "lib/rdoc/erb_partial.rb", "lib/rdoc/erbio.rb", "lib/rdoc/extend.rb", "lib/rdoc/generator.rb", "lib/rdoc/generator/darkfish.rb", "lib/rdoc/generator/json_index.rb", "lib/rdoc/generator/markup.rb", "lib/rdoc/generator/pot.rb", "lib/rdoc/generator/pot/message_extractor.rb", "lib/rdoc/generator/pot/po.rb", "lib/rdoc/generator/pot/po_entry.rb", "lib/rdoc/generator/ri.rb", "lib/rdoc/generator/template/darkfish/.document", "lib/rdoc/generator/template/darkfish/_footer.rhtml", "lib/rdoc/generator/template/darkfish/_head.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_VCS_info.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_classes.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_in_files.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_methods.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_navigation.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_parent.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml", "lib/rdoc/generator/template/darkfish/class.rhtml", "lib/rdoc/generator/template/darkfish/css/fonts.css", "lib/rdoc/generator/template/darkfish/css/rdoc.css", "lib/rdoc/generator/template/darkfish/fonts/Lato-Light.ttf", "lib/rdoc/generator/template/darkfish/fonts/Lato-LightItalic.ttf", "lib/rdoc/generator/template/darkfish/fonts/Lato-Regular.ttf", "lib/rdoc/generator/template/darkfish/fonts/Lato-RegularItalic.ttf", "lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Bold.ttf", "lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Regular.ttf", "lib/rdoc/generator/template/darkfish/images/add.png", "lib/rdoc/generator/template/darkfish/images/arrow_up.png", "lib/rdoc/generator/template/darkfish/images/brick.png", "lib/rdoc/generator/template/darkfish/images/brick_link.png", "lib/rdoc/generator/template/darkfish/images/bug.png", "lib/rdoc/generator/template/darkfish/images/bullet_black.png", "lib/rdoc/generator/template/darkfish/images/bullet_toggle_minus.png", "lib/rdoc/generator/template/darkfish/images/bullet_toggle_plus.png", "lib/rdoc/generator/template/darkfish/images/date.png", "lib/rdoc/generator/template/darkfish/images/delete.png", "lib/rdoc/generator/template/darkfish/images/find.png", "lib/rdoc/generator/template/darkfish/images/loadingAnimation.gif", "lib/rdoc/generator/template/darkfish/images/macFFBgHack.png", "lib/rdoc/generator/template/darkfish/images/package.png", "lib/rdoc/generator/template/darkfish/images/page_green.png", "lib/rdoc/generator/template/darkfish/images/page_white_text.png", "lib/rdoc/generator/template/darkfish/images/page_white_width.png", "lib/rdoc/generator/template/darkfish/images/plugin.png", "lib/rdoc/generator/template/darkfish/images/ruby.png", "lib/rdoc/generator/template/darkfish/images/tag_blue.png", "lib/rdoc/generator/template/darkfish/images/tag_green.png", "lib/rdoc/generator/template/darkfish/images/transparent.png", "lib/rdoc/generator/template/darkfish/images/wrench.png", "lib/rdoc/generator/template/darkfish/images/wrench_orange.png", "lib/rdoc/generator/template/darkfish/images/zoom.png", "lib/rdoc/generator/template/darkfish/index.rhtml", "lib/rdoc/generator/template/darkfish/js/darkfish.js", "lib/rdoc/generator/template/darkfish/js/jquery.js", "lib/rdoc/generator/template/darkfish/js/search.js", "lib/rdoc/generator/template/darkfish/page.rhtml", "lib/rdoc/generator/template/darkfish/servlet_not_found.rhtml", "lib/rdoc/generator/template/darkfish/servlet_root.rhtml", "lib/rdoc/generator/template/darkfish/table_of_contents.rhtml", "lib/rdoc/generator/template/json_index/.document", "lib/rdoc/generator/template/json_index/js/navigation.js", "lib/rdoc/generator/template/json_index/js/searcher.js", "lib/rdoc/ghost_method.rb", "lib/rdoc/i18n.rb", "lib/rdoc/i18n/locale.rb", "lib/rdoc/i18n/text.rb", "lib/rdoc/include.rb", "lib/rdoc/known_classes.rb", "lib/rdoc/markdown.kpeg", "lib/rdoc/markdown/entities.rb", "lib/rdoc/markdown/literals.kpeg", "lib/rdoc/markup.rb", "lib/rdoc/markup/attr_changer.rb", "lib/rdoc/markup/attr_span.rb", "lib/rdoc/markup/attribute_manager.rb", "lib/rdoc/markup/attributes.rb", "lib/rdoc/markup/blank_line.rb", "lib/rdoc/markup/block_quote.rb", "lib/rdoc/markup/document.rb", "lib/rdoc/markup/formatter.rb", "lib/rdoc/markup/formatter_test_case.rb", "lib/rdoc/markup/hard_break.rb", "lib/rdoc/markup/heading.rb", "lib/rdoc/markup/include.rb", "lib/rdoc/markup/indented_paragraph.rb", "lib/rdoc/markup/list.rb", "lib/rdoc/markup/list_item.rb", "lib/rdoc/markup/paragraph.rb", "lib/rdoc/markup/parser.rb", "lib/rdoc/markup/pre_process.rb", "lib/rdoc/markup/raw.rb", "lib/rdoc/markup/regexp_handling.rb", "lib/rdoc/markup/rule.rb", "lib/rdoc/markup/text_formatter_test_case.rb", "lib/rdoc/markup/to_ansi.rb", "lib/rdoc/markup/to_bs.rb", "lib/rdoc/markup/to_html.rb", "lib/rdoc/markup/to_html_crossref.rb", "lib/rdoc/markup/to_html_snippet.rb", "lib/rdoc/markup/to_joined_paragraph.rb", "lib/rdoc/markup/to_label.rb", "lib/rdoc/markup/to_markdown.rb", "lib/rdoc/markup/to_rdoc.rb", "lib/rdoc/markup/to_table_of_contents.rb", "lib/rdoc/markup/to_test.rb", "lib/rdoc/markup/to_tt_only.rb", "lib/rdoc/markup/verbatim.rb", "lib/rdoc/meta_method.rb", "lib/rdoc/method_attr.rb", "lib/rdoc/mixin.rb", "lib/rdoc/normal_class.rb", "lib/rdoc/normal_module.rb", "lib/rdoc/options.rb", "lib/rdoc/parser.rb", "lib/rdoc/parser/c.rb", "lib/rdoc/parser/changelog.rb", "lib/rdoc/parser/markdown.rb", "lib/rdoc/parser/rd.rb", "lib/rdoc/parser/ripper_state_lex.rb", "lib/rdoc/parser/ruby.rb", "lib/rdoc/parser/ruby_tools.rb", "lib/rdoc/parser/simple.rb", "lib/rdoc/parser/text.rb", "lib/rdoc/rd.rb", "lib/rdoc/rd/block_parser.ry", "lib/rdoc/rd/inline.rb", "lib/rdoc/rd/inline_parser.ry", "lib/rdoc/rdoc.rb", "lib/rdoc/require.rb", "lib/rdoc/ri.rb", "lib/rdoc/ri/driver.rb", "lib/rdoc/ri/formatter.rb", "lib/rdoc/ri/paths.rb", "lib/rdoc/ri/store.rb", "lib/rdoc/ri/task.rb", "lib/rdoc/rubygems_hook.rb", "lib/rdoc/servlet.rb", "lib/rdoc/single_class.rb", "lib/rdoc/stats.rb", "lib/rdoc/stats/normal.rb", "lib/rdoc/stats/quiet.rb", "lib/rdoc/stats/verbose.rb", "lib/rdoc/store.rb", "lib/rdoc/task.rb", "lib/rdoc/text.rb", "lib/rdoc/token_stream.rb", "lib/rdoc/tom_doc.rb", "lib/rdoc/top_level.rb", "lib/rdoc/version.rb", "rdoc.gemspec"]
+ s.files << "lib/rdoc/rd/block_parser.rb" << "lib/rdoc/rd/inline_parser.rb" << "lib/rdoc/markdown.rb" << "lib/rdoc/markdown/literals.rb"
s.rdoc_options = ["--main", "README.rdoc"]
s.extra_rdoc_files += %w[
@@ -46,12 +50,13 @@ RDoc includes the +rdoc+ and +ri+ tools for generating and displaying documentat
TODO.rdoc
]
- s.required_ruby_version = Gem::Requirement.new(">= 1.9.3")
+ s.required_ruby_version = Gem::Requirement.new(">= 2.2.2")
s.rubygems_version = "2.5.2"
s.required_rubygems_version = Gem::Requirement.new(">= 2.2")
s.add_development_dependency("rake")
s.add_development_dependency("racc", "> 1.4.10")
s.add_development_dependency("kpeg")
- s.add_development_dependency("minitest", "~> 4")
+ s.add_development_dependency("minitest", "~> 5")
+ s.add_development_dependency("rubocop")
end
diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb
index adcb65b13b..46aace7839 100644
--- a/lib/rdoc/rdoc.rb
+++ b/lib/rdoc/rdoc.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'rdoc'
require 'find'
@@ -36,11 +36,6 @@ class RDoc::RDoc
GENERATORS = {}
##
- # File pattern to exclude
-
- attr_accessor :exclude
-
- ##
# Generator instance used for creating output
attr_accessor :generator
@@ -93,7 +88,6 @@ class RDoc::RDoc
def initialize
@current = nil
- @exclude = nil
@generator = nil
@last_modified = {}
@old_siginfo = nil
@@ -116,7 +110,7 @@ class RDoc::RDoc
def gather_files files
files = ["."] if files.empty?
- file_list = normalized_file_list files, true, @exclude
+ file_list = normalized_file_list files, true, @options.exclude
file_list = file_list.uniq
@@ -188,7 +182,7 @@ class RDoc::RDoc
error "#{dir} exists and is not a directory" unless File.directory? dir
begin
- open flag_file do |io|
+ File.open flag_file do |io|
unless force then
Time.parse io.gets
@@ -232,8 +226,11 @@ option)
def update_output_dir(op_dir, time, last = {})
return if @options.dry_run or not @options.update_output_dir
+ unless ENV['SOURCE_DATE_EPOCH'].nil?
+ time = Time.at(ENV['SOURCE_DATE_EPOCH'].to_i).gmtime
+ end
- open output_flag_file(op_dir), "w" do |f|
+ File.open output_flag_file(op_dir), "w" do |f|
f.puts time.rfc2822
last.each do |n, t|
f.puts "#{n}\t#{t.rfc2822}"
@@ -261,7 +258,7 @@ option)
patterns.split.each do |patt|
candidates = Dir.glob(File.join(in_dir, patt))
- result.concat normalized_file_list(candidates)
+ result.concat normalized_file_list(candidates, false, @options.exclude)
end
result
@@ -358,7 +355,7 @@ option)
relative_path.relative_path_from @options.page_dir
end
- top_level = @store.add_file filename, relative_path.to_s
+ top_level = @store.add_file filename, relative_name: relative_path.to_s
parser = RDoc::Parser.for top_level, filename, content, @options, @stats
@@ -433,7 +430,7 @@ The internal error was:
files.reject do |file|
file =~ /\.(?:class|eps|erb|scpt\.txt|svg|ttf|yml)$/i or
(file =~ /tags$/i and
- open(file, 'rb') { |io|
+ File.open(file, 'rb') { |io|
io.read(100) =~ /\A(\f\n[^,]+,\d+$|!_TAG_)/
})
end
@@ -469,8 +466,6 @@ The internal error was:
exit
end
- @exclude = @options.exclude
-
unless @options.coverage_report then
@last_modified = setup_output_dir @options.op_dir, @options.force_update
end
@@ -521,13 +516,18 @@ The internal error was:
# by the RDoc options
def generate
- Dir.chdir @options.op_dir do
- unless @options.quiet then
- $stderr.puts "\nGenerating #{@generator.class.name.sub(/^.*::/, '')} format into #{Dir.pwd}..."
- end
-
+ if @options.dry_run then
+ # do nothing
@generator.generate
- update_output_dir '.', @start_time, @last_modified
+ else
+ Dir.chdir @options.op_dir do
+ unless @options.quiet then
+ $stderr.puts "\nGenerating #{@generator.class.name.sub(/^.*::/, '')} format into #{Dir.pwd}..."
+ end
+
+ @generator.generate
+ update_output_dir '.', @start_time, @last_modified
+ end
end
end
diff --git a/lib/rdoc/require.rb b/lib/rdoc/require.rb
index f565ffad78..91f9c24e5d 100644
--- a/lib/rdoc/require.rb
+++ b/lib/rdoc/require.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A file loaded by \#require
diff --git a/lib/rdoc/ri.rb b/lib/rdoc/ri.rb
index 388cb12c70..c798c1fc49 100644
--- a/lib/rdoc/ri.rb
+++ b/lib/rdoc/ri.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'rdoc'
##
diff --git a/lib/rdoc/ri/driver.rb b/lib/rdoc/ri/driver.rb
index 23d24e9a20..46b98e99b5 100644
--- a/lib/rdoc/ri/driver.rb
+++ b/lib/rdoc/ri/driver.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'abbrev'
require 'optparse'
@@ -47,13 +47,24 @@ class RDoc::RI::Driver
class NotFoundError < Error
+ def initialize(klass, suggestions = nil) # :nodoc:
+ @klass = klass
+ @suggestions = suggestions
+ end
+
##
# Name that wasn't found
- alias name message
+ def name
+ @klass
+ end
def message # :nodoc:
- "Nothing known about #{super}"
+ str = "Nothing known about #{@klass}"
+ if @suggestions and !@suggestions.empty?
+ str += "\nDid you mean? #{@suggestions.join("\n ")}"
+ end
+ str
end
end
@@ -80,7 +91,6 @@ class RDoc::RI::Driver
options[:interactive] = false
options[:profile] = false
options[:show_all] = false
- options[:use_cache] = true
options[:use_stdout] = !$stdout.tty?
options[:width] = 72
@@ -100,7 +110,7 @@ class RDoc::RI::Driver
def self.dump data_path
require 'pp'
- open data_path, 'rb' do |io|
+ File.open data_path, 'rb' do |io|
pp Marshal.load(io.read)
end
end
@@ -122,7 +132,7 @@ class RDoc::RI::Driver
opt.summary_indent = ' ' * 4
opt.banner = <<-EOT
-Usage: #{opt.program_name} [options] [names...]
+Usage: #{opt.program_name} [options] [name ...]
Where name can be:
@@ -132,8 +142,8 @@ Where name can be:
gem_name: | gem_name:README | gem_name:History
-All class names may be abbreviated to their minimum unambiguous form. If a name
-is ambiguous, all valid options will be listed.
+All class names may be abbreviated to their minimum unambiguous form.
+If a name is ambiguous, all valid options will be listed.
A '.' matches either class or instance methods, while #method
matches only instance and ::method matches only class methods.
@@ -151,23 +161,23 @@ For example:
#{opt.program_name} zip
#{opt.program_name} rdoc:README
-Note that shell quoting or escaping may be required for method names containing
-punctuation:
+Note that shell quoting or escaping may be required for method names
+containing punctuation:
#{opt.program_name} 'Array.[]'
#{opt.program_name} compact\\!
-To see the default directories ri will search, run:
+To see the default directories #{opt.program_name} will search, run:
#{opt.program_name} --list-doc-dirs
-Specifying the --system, --site, --home, --gems or --doc-dir options will
-limit ri to searching only the specified directories.
+Specifying the --system, --site, --home, --gems, or --doc-dir options
+will limit ri to searching only the specified directories.
-ri options may be set in the 'RI' environment variable.
+ri options may be set in the RI environment variable.
-The ri pager can be set with the 'RI_PAGER' environment variable or the
-'PAGER' environment variable.
+The ri pager can be set with the RI_PAGER environment variable
+or the PAGER environment variable.
EOT
opt.separator nil
@@ -199,15 +209,15 @@ The ri pager can be set with the 'RI_PAGER' environment variable or the
opt.separator nil
opt.on("--[no-]pager",
- "Send output directly to stdout,",
- "rather than to a pager.") do |use_pager|
+ "Send output to a pager,",
+ "rather than directly to stdout.") do |use_pager|
options[:use_stdout] = !use_pager
end
opt.separator nil
opt.on("-T",
- "Synonym for --no-pager") do
+ "Synonym for --no-pager.") do
options[:use_stdout] = true
end
@@ -220,7 +230,7 @@ The ri pager can be set with the 'RI_PAGER' environment variable or the
opt.separator nil
- opt.on("--server [PORT]", Integer,
+ opt.on("--server[=PORT]", Integer,
"Run RDoc server on the given port.",
"The default port is 8214.") do |port|
options[:server] = port || 8214
@@ -235,14 +245,30 @@ The ri pager can be set with the 'RI_PAGER' environment variable or the
formatters -= %w[html label test] # remove useless output formats
opt.on("--format=NAME", "-f",
- "Uses the selected formatter. The default",
+ "Use the selected formatter. The default",
"formatter is bs for paged output and ansi",
- "otherwise. Valid formatters are:",
- formatters.join(' '), formatters) do |value|
+ "otherwise. Valid formatters are:",
+ "#{formatters.join(', ')}.", formatters) do |value|
options[:formatter] = RDoc::Markup.const_get "To#{value.capitalize}"
end
opt.separator nil
+
+ opt.on("--help", "-h",
+ "Show help and exit.") do
+ puts opts
+ exit
+ end
+
+ opt.separator nil
+
+ opt.on("--version", "-v",
+ "Output version information and exit.") do
+ puts "#{opts.program_name} #{opts.version}"
+ exit
+ end
+
+ opt.separator nil
opt.separator "Data source options:"
opt.separator nil
@@ -273,7 +299,7 @@ The ri pager can be set with the 'RI_PAGER' environment variable or the
"Do not include documentation from",
"the Ruby standard library, site_lib,",
"installed gems, or ~/.rdoc.",
- "Use with --doc-dir") do
+ "Use with --doc-dir.") do
options[:use_system] = false
options[:use_site] = false
options[:use_gems] = false
@@ -283,8 +309,8 @@ The ri pager can be set with the 'RI_PAGER' environment variable or the
opt.separator nil
opt.on("--[no-]system",
- "Include documentation from Ruby's standard",
- "library. Defaults to true.") do |value|
+ "Include documentation from Ruby's",
+ "standard library. Defaults to true.") do |value|
options[:use_system] = value
end
@@ -318,14 +344,14 @@ The ri pager can be set with the 'RI_PAGER' environment variable or the
opt.separator nil
opt.on("--[no-]profile",
- "Run with the ruby profiler") do |value|
+ "Run with the ruby profiler.") do |value|
options[:profile] = value
end
opt.separator nil
opt.on("--dump=CACHE", File,
- "Dumps data from an ri cache or data file") do |value|
+ "Dump data from an ri cache or data file.") do |value|
options[:dump_path] = value
end
end
@@ -399,6 +425,7 @@ The ri pager can be set with the 'RI_PAGER' environment variable or the
@server = options[:server]
@use_stdout = options[:use_stdout]
@show_all = options[:show_all]
+ @width = options[:width]
# pager process for jruby
@jruby_pager_process = nil
@@ -769,7 +796,9 @@ The ri pager can be set with the 'RI_PAGER' environment variable or the
def display document
page do |io|
- text = document.accept formatter(io)
+ f = formatter(io)
+ f.width = @width if @width and f.respond_to?(:width)
+ text = document.accept f
io.write text
end
@@ -902,13 +931,38 @@ The ri pager can be set with the 'RI_PAGER' environment variable or the
display out
end
+ def check_did_you_mean # :nodoc:
+ if defined? DidYouMean::SpellChecker
+ true
+ else
+ begin
+ require 'did_you_mean'
+ if defined? DidYouMean::SpellChecker
+ true
+ else
+ false
+ end
+ rescue LoadError
+ false
+ end
+ end
+ end
+
##
# Expands abbreviated klass +klass+ into a fully-qualified class. "Zl::Da"
# will be expanded to Zlib::DataError.
def expand_class klass
- ary = classes.keys.grep(Regexp.new("\\A#{klass.gsub(/(?=::|\z)/, '[^:]*')}\\z"))
- raise NotFoundError, klass if ary.length != 1 && ary.first != klass
+ class_names = classes.keys
+ ary = class_names.grep(Regexp.new("\\A#{klass.gsub(/(?=::|\z)/, '[^:]*')}\\z"))
+ if ary.length != 1 && ary.first != klass
+ if check_did_you_mean
+ suggestions = DidYouMean::SpellChecker.new(dictionary: class_names).correct(klass)
+ raise NotFoundError.new(klass, suggestions)
+ else
+ raise NotFoundError, klass
+ end
+ end
ary.first
end
@@ -1220,7 +1274,21 @@ The ri pager can be set with the 'RI_PAGER' environment variable or the
def lookup_method name
found = load_methods_matching name
- raise NotFoundError, name if found.empty?
+ if found.empty?
+ if check_did_you_mean
+ methods = []
+ _, _, method_name = parse_name name
+ find_methods name do |store, klass, ancestor, types, method|
+ methods.push(*store.class_methods[klass]) if [:class, :both].include? types
+ methods.push(*store.instance_methods[klass]) if [:instance, :both].include? types
+ end
+ methods = methods.uniq
+ suggestions = DidYouMean::SpellChecker.new(dictionary: methods).correct(method_name)
+ raise NotFoundError.new(name, suggestions)
+ else
+ raise NotFoundError, name
+ end
+ end
filter_methods found, name
end
@@ -1375,7 +1443,13 @@ The ri pager can be set with the 'RI_PAGER' environment variable or the
render_method_arguments out, method.arglists
render_method_superclass out, method
- render_method_comment out, method
+ if method.is_alias_for
+ al = method.is_alias_for
+ alias_for = store.load_method al.parent_name, "#{al.name_prefix}#{al.name}"
+ render_method_comment out, method, alias_for
+ else
+ render_method_comment out, method
+ end
end
def render_method_arguments out, arglists # :nodoc:
@@ -1387,10 +1461,22 @@ The ri pager can be set with the 'RI_PAGER' environment variable or the
out << RDoc::Markup::Rule.new(1)
end
- def render_method_comment out, method # :nodoc:
- out << RDoc::Markup::BlankLine.new
- out << method.comment
- out << RDoc::Markup::BlankLine.new
+ def render_method_comment out, method, alias_for = nil# :nodoc:
+ if alias_for
+ unless method.comment.nil? or method.comment.empty?
+ out << RDoc::Markup::BlankLine.new
+ out << method.comment
+ end
+ out << RDoc::Markup::BlankLine.new
+ out << RDoc::Markup::Paragraph.new("(This method is an alias for #{alias_for.full_name}.)")
+ out << RDoc::Markup::BlankLine.new
+ out << alias_for.comment
+ out << RDoc::Markup::BlankLine.new
+ else
+ out << RDoc::Markup::BlankLine.new
+ out << method.comment
+ out << RDoc::Markup::BlankLine.new
+ end
end
def render_method_superclass out, method # :nodoc:
diff --git a/lib/rdoc/ri/formatter.rb b/lib/rdoc/ri/formatter.rb
index d0c85dbe6b..832a101e6c 100644
--- a/lib/rdoc/ri/formatter.rb
+++ b/lib/rdoc/ri/formatter.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# For RubyGems backwards compatibility
diff --git a/lib/rdoc/ri/paths.rb b/lib/rdoc/ri/paths.rb
index 94db2216a2..d41e610591 100644
--- a/lib/rdoc/ri/paths.rb
+++ b/lib/rdoc/ri/paths.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'rdoc/ri'
##
diff --git a/lib/rdoc/ri/store.rb b/lib/rdoc/ri/store.rb
index 66e234f521..9f4b03734a 100644
--- a/lib/rdoc/ri/store.rb
+++ b/lib/rdoc/ri/store.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module RDoc::RI
Store = RDoc::Store # :nodoc:
diff --git a/lib/rdoc/ri/task.rb b/lib/rdoc/ri/task.rb
index cc0a85d4b7..6a6ea572bf 100644
--- a/lib/rdoc/ri/task.rb
+++ b/lib/rdoc/ri/task.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
gem 'rdoc'
rescue Gem::LoadError
diff --git a/lib/rdoc/ruby_lex.rb b/lib/rdoc/ruby_lex.rb
deleted file mode 100644
index 1fc3c12c4a..0000000000
--- a/lib/rdoc/ruby_lex.rb
+++ /dev/null
@@ -1,1367 +0,0 @@
-# coding: US-ASCII
-# frozen_string_literal: false
-
-#--
-# irb/ruby-lex.rb - ruby lexcal analyzer
-# $Release Version: 0.9.5$
-# $Revision: 17979 $
-# $Date: 2008-07-09 10:17:05 -0700 (Wed, 09 Jul 2008) $
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#
-#++
-
-require "e2mmap"
-require "irb/slex"
-require "stringio"
-
-##
-# Ruby lexer adapted from irb.
-#
-# The internals are not documented because they are scary.
-
-class RDoc::RubyLex
-
- ##
- # Raised upon invalid input
-
- class Error < RDoc::Error
- end
-
- # :stopdoc:
-
- extend Exception2MessageMapper
-
- def_exception(:AlreadyDefinedToken, "Already defined token(%s)")
- def_exception(:TkReading2TokenNoKey, "key nothing(key='%s')")
- def_exception(:TkSymbol2TokenNoKey, "key nothing(key='%s')")
- def_exception(:TkReading2TokenDuplicateError,
- "key duplicate(token_n='%s', key='%s')")
- def_exception(:SyntaxError, "%s")
-
- def_exception(:TerminateLineInput, "Terminate Line Input")
-
- include RDoc::RubyToken
- include IRB
-
- attr_accessor :continue
- attr_accessor :lex_state
- attr_reader :reader
-
- class << self
- attr_accessor :debug_level
- end
-
- def self.debug?
- @debug_level > 0
- end
-
- self.debug_level = 0
-
- # :startdoc:
-
- ##
- # Returns an Array of +ruby+ tokens. See ::new for a description of
- # +options+.
-
- def self.tokenize ruby, options
- tokens = []
-
- scanner = RDoc::RubyLex.new ruby, options
- scanner.exception_on_syntax_error = true
-
- while token = scanner.token do
- tokens << token
- end
-
- tokens
- end
-
- ##
- # Creates a new lexer for +content+. +options+ is an RDoc::Options, only
- # +tab_width is used.
-
- def initialize(content, options)
- lex_init
-
- 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 << "\n" unless content[-1, 1] == "\n"
-
- set_input StringIO.new content
-
- @base_char_no = 0
- @char_no = 0
- @exp_line_no = @line_no = 1
- @here_readed = []
- @readed = []
- @current_readed = @readed
- @rests = []
- @seek = 0
-
- @indent = 0
- @indent_stack = []
- @lex_state = :EXPR_BEG
- @space_seen = false
-
- @continue = false
- @line = ""
-
- @skip_space = false
- @readed_auto_clean_up = false
- @exception_on_syntax_error = true
-
- @prompt = nil
- @prev_seek = nil
- @ltype = nil
- end
-
- # :stopdoc:
-
- def inspect # :nodoc:
- "#<%s:0x%x pos %d lex_state %p space_seen %p>" % [
- self.class, object_id,
- @io.pos, @lex_state, @space_seen,
- ]
- end
-
- attr_accessor :skip_space
- attr_accessor :readed_auto_clean_up
- attr_accessor :exception_on_syntax_error
-
- attr_reader :seek
- attr_reader :char_no
- attr_reader :line_no
- attr_reader :indent
-
- # io functions
- def set_input(io, p = nil, &block)
- @io = io
- if p.respond_to?(:call)
- @input = p
- elsif block_given?
- @input = block
- else
- @input = Proc.new{@io.gets}
- end
- end
-
- def get_readed
- if idx = @readed.rindex("\n")
- @base_char_no = @readed.size - (idx + 1)
- else
- @base_char_no += @readed.size
- end
-
- readed = @readed.join("")
- @readed.clear
- readed
- end
-
- def getc
- while @rests.empty?
- # return nil unless buf_input
- @rests.push nil unless buf_input
- end
- c = @rests.shift
- @current_readed.push c
- @seek += 1
- if c == "\n".freeze
- @line_no += 1
- @char_no = 0
- else
- @char_no += 1
- end
-
- c
- end
-
- def gets
- l = ""
- while c = getc
- l.concat(c)
- break if c == "\n"
- end
- return nil if l == "" and c.nil?
- l
- end
-
- def eof?
- @io.eof?
- end
-
- def getc_of_rests
- if @rests.empty?
- nil
- else
- getc
- end
- end
-
- def ungetc(c = nil)
- if @here_readed.empty?
- c2 = @readed.pop
- else
- c2 = @here_readed.pop
- end
- c = c2 unless c
- @rests.unshift c #c =
- @seek -= 1
- if c == "\n"
- @line_no -= 1
- if idx = @readed.rindex("\n")
- @char_no = idx + 1
- else
- @char_no = @base_char_no + @readed.size
- end
- else
- @char_no -= 1
- end
- end
-
- def peek_equal?(str)
- chrs = str.split(//)
- until @rests.size >= chrs.size
- return false unless buf_input
- end
- @rests[0, chrs.size] == chrs
- end
-
- def peek_match?(regexp)
- while @rests.empty?
- return false unless buf_input
- end
- regexp =~ @rests.join("")
- end
-
- def peek(i = 0)
- while @rests.size <= i
- return nil unless buf_input
- end
- @rests[i]
- end
-
- def buf_input
- prompt
- line = @input.call
- return nil unless line
- @rests.concat line.split(//)
- true
- end
- private :buf_input
-
- def set_prompt(p = nil, &block)
- p = block if block_given?
- if p.respond_to?(:call)
- @prompt = p
- else
- @prompt = Proc.new{print p}
- end
- end
-
- def prompt
- if @prompt
- @prompt.call(@ltype, @indent, @continue, @line_no)
- end
- end
-
- def initialize_input
- @ltype = nil
- @quoted = nil
- @indent = 0
- @indent_stack = []
- @lex_state = :EXPR_BEG
- @space_seen = false
- @current_readed = @readed
-
- @continue = false
- prompt
-
- @line = ""
- @exp_line_no = @line_no
- end
-
- def each_top_level_statement
- initialize_input
- catch(:TERM_INPUT) do
- loop do
- begin
- @continue = false
- prompt
- unless l = lex
- throw :TERM_INPUT if @line == ''
- else
- #p l
- @line.concat l
- if @ltype or @continue or @indent > 0
- next
- end
- end
- if @line != "\n"
- yield @line, @exp_line_no
- end
- break unless l
- @line = ''
- @exp_line_no = @line_no
-
- @indent = 0
- @indent_stack = []
- prompt
- rescue TerminateLineInput
- initialize_input
- prompt
- get_readed
- end
- end
- end
- end
-
- def lex
- until (((tk = token).kind_of?(TkNL) || tk.kind_of?(TkEND_OF_SCRIPT)) &&
- !@continue or
- tk.nil?)
- #p tk
- #p @lex_state
- #p self
- end
- line = get_readed
- # print self.inspect
- if line == "" and tk.kind_of?(TkEND_OF_SCRIPT) || tk.nil?
- nil
- else
- line
- end
- end
-
- def token
- # require "tracer"
- # Tracer.on
- @prev_seek = @seek
- @prev_line_no = @line_no
- @prev_char_no = @char_no
- begin
- begin
- tk = @OP.match(self)
- @space_seen = tk.kind_of?(TkSPACE)
- rescue SyntaxError => e
- raise Error, "syntax error: #{e.message}" if
- @exception_on_syntax_error
-
- tk = TkError.new(@seek, @line_no, @char_no)
- end
- end while @skip_space and tk.kind_of?(TkSPACE)
-
- if @readed_auto_clean_up
- get_readed
- end
- # Tracer.off
- tk
- end
-
- ENINDENT_CLAUSE = [
- "case", "class", "def", "do", "for", "if",
- "module", "unless", "until", "while", "begin" #, "when"
- ]
-
- DEINDENT_CLAUSE = ["end" #, "when"
- ]
-
- PERCENT_LTYPE = {
- "q" => "\'",
- "Q" => "\"",
- "x" => "\`",
- "r" => "/",
- "w" => "]",
- "W" => "]",
- "s" => ":"
- }
-
- PERCENT_PAREN = {
- "{" => "}",
- "[" => "]",
- "<" => ">",
- "(" => ")"
- }
-
- PERCENT_PAREN_REV = PERCENT_PAREN.invert
-
- Ltype2Token = {
- "\'" => TkSTRING,
- "\"" => TkSTRING,
- "\`" => TkXSTRING,
- "/" => TkREGEXP,
- "]" => TkDSTRING,
- ":" => TkSYMBOL
- }
- DLtype2Token = {
- "\"" => TkDSTRING,
- "\`" => TkDXSTRING,
- "/" => TkDREGEXP,
- }
-
- def lex_init()
- @OP = IRB::SLex.new
- @OP.def_rules("\0", "\004", "\032") do |op, io|
- Token(TkEND_OF_SCRIPT, '')
- end
-
- @OP.def_rules(" ", "\t", "\f", "\r", "\13") do |op, io|
- @space_seen = true
- str = op
- while (ch = getc) =~ /[ \t\f\r\13]/ do
- str << ch
- end
- ungetc
- Token TkSPACE, str
- end
-
- @OP.def_rule("#") do |op, io|
- identify_comment
- end
-
- @OP.def_rule("=begin",
- proc{|op, io| @prev_char_no == 0 && peek(0) =~ /\s/}) do
- |op, io|
- @ltype = "="
- res = ''
- nil until getc == "\n"
-
- until ( peek_equal?("=end") && peek(4) =~ /\s/ ) do
- (ch = getc)
- res << ch
- end
-
- gets # consume =end
-
- @ltype = nil
- Token(TkRD_COMMENT, res)
- end
-
- @OP.def_rule("\n") do |op, io|
- print "\\n\n" if RDoc::RubyLex.debug?
- case @lex_state
- when :EXPR_BEG, :EXPR_FNAME, :EXPR_DOT
- @continue = true
- else
- @continue = false
- @lex_state = :EXPR_BEG
- until (@indent_stack.empty? ||
- [TkLPAREN, TkLBRACK, TkLBRACE,
- TkfLPAREN, TkfLBRACK, TkfLBRACE].include?(@indent_stack.last))
- @indent_stack.pop
- end
- end
- @current_readed = @readed
- @here_readed.clear
- Token(TkNL)
- end
-
- @OP.def_rules("*", "**",
- "=", "==", "===",
- "=~", "<=>",
- "<", "<=",
- ">", ">=", ">>") do
- |op, io|
- case @lex_state
- when :EXPR_FNAME, :EXPR_DOT
- @lex_state = :EXPR_ARG
- else
- @lex_state = :EXPR_BEG
- end
- Token(op)
- end
-
- @OP.def_rules("!", "!=", "!~") do
- |op, io|
- @lex_state = :EXPR_BEG
- Token(op)
- end
-
- @OP.def_rules("<<") do
- |op, io|
- tk = nil
- if @lex_state != :EXPR_END && @lex_state != :EXPR_CLASS &&
- (@lex_state != :EXPR_ARG || @space_seen)
- c = peek(0)
- if /\S/ =~ c && (/["'`]/ =~ c || /\w/ =~ c || c == "-")
- tk = identify_here_document
- end
- end
- unless tk
- tk = Token(op)
- case @lex_state
- when :EXPR_FNAME, :EXPR_DOT
- @lex_state = :EXPR_ARG
- else
- @lex_state = :EXPR_BEG
- end
- end
- tk
- end
-
- @OP.def_rules("'", '"') do
- |op, io|
- identify_string(op)
- end
-
- @OP.def_rules("`") do
- |op, io|
- if @lex_state == :EXPR_FNAME
- @lex_state = :EXPR_END
- Token(op)
- else
- identify_string(op)
- end
- end
-
- @OP.def_rules('?') do
- |op, io|
- if @lex_state == :EXPR_END
- @lex_state = :EXPR_BEG
- Token(TkQUESTION)
- else
- ch = getc
- if @lex_state == :EXPR_ARG && ch =~ /\s/
- ungetc
- @lex_state = :EXPR_BEG;
- Token(TkQUESTION)
- else
- @lex_state = :EXPR_END
- Token(TkCHAR, "?#{ch}")
- end
- end
- end
-
- @OP.def_rules("&", "&&", "|", "||") do
- |op, io|
- @lex_state = :EXPR_BEG
- Token(op)
- end
-
- @OP.def_rules("+=", "-=", "*=", "**=",
- "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do
- |op, io|
- @lex_state = :EXPR_BEG
- op =~ /^(.*)=$/
- Token(TkOPASGN, $1)
- end
-
- @OP.def_rule("+@", proc{|op, io| @lex_state == :EXPR_FNAME}) do
- |op, io|
- @lex_state = :EXPR_ARG
- Token(op)
- end
-
- @OP.def_rule("-@", proc{|op, io| @lex_state == :EXPR_FNAME}) do
- |op, io|
- @lex_state = :EXPR_ARG
- Token(op)
- end
-
- @OP.def_rules("+", "-") do
- |op, io|
- catch(:RET) do
- if @lex_state == :EXPR_ARG
- if @space_seen and peek(0) =~ /[0-9]/
- throw :RET, identify_number(op)
- else
- @lex_state = :EXPR_BEG
- end
- elsif @lex_state != :EXPR_END and peek(0) =~ /[0-9]/
- throw :RET, identify_number(op)
- else
- @lex_state = :EXPR_BEG
- end
- Token(op)
- end
- end
-
- @OP.def_rule(".") do
- |op, io|
- @lex_state = :EXPR_BEG
- if peek(0) =~ /[0-9]/
- ungetc
- identify_number
- else
- # for "obj.if" etc.
- @lex_state = :EXPR_DOT
- Token(TkDOT)
- end
- end
-
- @OP.def_rules("..", "...") do
- |op, io|
- @lex_state = :EXPR_BEG
- Token(op)
- end
-
- lex_int2
- end
-
- def lex_int2
- @OP.def_rules("]", "}", ")") do
- |op, io|
- @lex_state = :EXPR_END
- @indent -= 1
- @indent_stack.pop
- Token(op)
- end
-
- @OP.def_rule(":") do
- |op, io|
- if @lex_state == :EXPR_END || peek(0) =~ /\s/
- @lex_state = :EXPR_BEG
- Token(TkCOLON)
- else
- @lex_state = :EXPR_FNAME;
- Token(TkSYMBEG)
- end
- end
-
- @OP.def_rule("::") do
- |op, io|
- # p @lex_state.id2name, @space_seen
- if @lex_state == :EXPR_BEG or @lex_state == :EXPR_ARG && @space_seen
- @lex_state = :EXPR_BEG
- Token(TkCOLON3)
- else
- @lex_state = :EXPR_DOT
- Token(TkCOLON2)
- end
- end
-
- @OP.def_rule("/") do
- |op, io|
- if @lex_state == :EXPR_BEG || @lex_state == :EXPR_MID
- identify_string(op)
- elsif peek(0) == '='
- getc
- @lex_state = :EXPR_BEG
- Token(TkOPASGN, "/") #/)
- elsif @lex_state == :EXPR_ARG and @space_seen and peek(0) !~ /\s/
- identify_string(op)
- else
- @lex_state = :EXPR_BEG
- Token("/") #/)
- end
- end
-
- @OP.def_rules("^") do
- |op, io|
- @lex_state = :EXPR_BEG
- Token("^")
- end
-
- # @OP.def_rules("^=") do
- # @lex_state = :EXPR_BEG
- # Token(OP_ASGN, :^)
- # end
-
- @OP.def_rules(",") do
- |op, io|
- @lex_state = :EXPR_BEG
- Token(op)
- end
-
- @OP.def_rules(";") do
- |op, io|
- @lex_state = :EXPR_BEG
- until (@indent_stack.empty? ||
- [TkLPAREN, TkLBRACK, TkLBRACE,
- TkfLPAREN, TkfLBRACK, TkfLBRACE].include?(@indent_stack.last))
- @indent_stack.pop
- end
- Token(op)
- end
-
- @OP.def_rule("~") do
- |op, io|
- @lex_state = :EXPR_BEG
- Token("~")
- end
-
- @OP.def_rule("~@", proc{|op, io| @lex_state == :EXPR_FNAME}) do
- |op, io|
- @lex_state = :EXPR_BEG
- Token("~")
- end
-
- @OP.def_rule("(") do
- |op, io|
- @indent += 1
- if @lex_state == :EXPR_BEG || @lex_state == :EXPR_MID
- @lex_state = :EXPR_BEG
- tk_c = TkfLPAREN
- else
- @lex_state = :EXPR_BEG
- tk_c = TkLPAREN
- end
- @indent_stack.push tk_c
- Token tk_c
- end
-
- @OP.def_rule("[]", proc{|op, io| @lex_state == :EXPR_FNAME}) do
- |op, io|
- @lex_state = :EXPR_ARG
- Token("[]")
- end
-
- @OP.def_rule("[]=", proc{|op, io| @lex_state == :EXPR_FNAME}) do
- |op, io|
- @lex_state = :EXPR_ARG
- Token("[]=")
- end
-
- @OP.def_rule("[") do
- |op, io|
- @indent += 1
- if @lex_state == :EXPR_FNAME
- tk_c = TkfLBRACK
- else
- if @lex_state == :EXPR_BEG || @lex_state == :EXPR_MID
- tk_c = TkLBRACK
- elsif @lex_state == :EXPR_ARG && @space_seen
- tk_c = TkLBRACK
- else
- tk_c = TkfLBRACK
- end
- @lex_state = :EXPR_BEG
- end
- @indent_stack.push tk_c
- Token(tk_c)
- end
-
- @OP.def_rule("{") do
- |op, io|
- @indent += 1
- if @lex_state != :EXPR_END && @lex_state != :EXPR_ARG
- tk_c = TkLBRACE
- else
- tk_c = TkfLBRACE
- end
- @lex_state = :EXPR_BEG
- @indent_stack.push tk_c
- Token(tk_c)
- end
-
- @OP.def_rule('\\') do
- |op, io|
- if getc == "\n"
- @space_seen = true
- @continue = true
- Token(TkSPACE)
- else
- ungetc
- Token("\\")
- end
- end
-
- @OP.def_rule('%') do
- |op, io|
- if @lex_state == :EXPR_BEG || @lex_state == :EXPR_MID
- identify_quotation
- elsif peek(0) == '='
- getc
- Token(TkOPASGN, :%)
- elsif @lex_state == :EXPR_ARG and @space_seen and peek(0) !~ /\s/
- identify_quotation
- else
- @lex_state = :EXPR_BEG
- Token("%") #))
- end
- end
-
- @OP.def_rule('$') do
- |op, io|
- identify_gvar
- end
-
- @OP.def_rule('@') do
- |op, io|
- if peek(0) =~ /[\w@]/
- ungetc
- identify_identifier
- else
- Token("@")
- end
- end
-
- # @OP.def_rule("def", proc{|op, io| /\s/ =~ io.peek(0)}) do
- # |op, io|
- # @indent += 1
- # @lex_state = :EXPR_FNAME
- # # @lex_state = :EXPR_END
- # # until @rests[0] == "\n" or @rests[0] == ";"
- # # rests.shift
- # # end
- # end
-
- @OP.def_rule("_") do
- if peek_match?(/_END__/) and @lex_state == :EXPR_BEG then
- 6.times { getc }
- Token(TkEND_OF_SCRIPT, '__END__')
- else
- ungetc
- identify_identifier
- end
- end
-
- @OP.def_rule("") do
- |op, io|
- printf "MATCH: start %s: %s\n", op, io.inspect if RDoc::RubyLex.debug?
- if peek(0) =~ /[0-9]/
- t = identify_number
- else
- t = identify_identifier
- end
- printf "MATCH: end %s: %s\n", op, io.inspect if RDoc::RubyLex.debug?
- t
- end
-
- p @OP if RDoc::RubyLex.debug?
- end
-
- def identify_gvar
- @lex_state = :EXPR_END
-
- case ch = getc
- when /[~_*$?!@\/\\;,=:<>".]/ #"
- Token(TkGVAR, "$" + ch)
- when "-"
- Token(TkGVAR, "$-" + getc)
- when "&", "`", "'", "+"
- Token(TkBACK_REF, "$"+ch)
- when /[1-9]/
- ref = ch
- while (ch = getc) =~ /[0-9]/ do ref << ch end
- ungetc
- Token(TkNTH_REF, "$#{ref}")
- when /\w/
- ungetc
- ungetc
- identify_identifier
- else
- ungetc
- Token("$")
- end
- end
-
- IDENT_RE = eval '/[\w\u{0080}-\u{FFFFF}]/u'
-
- def identify_identifier
- token = ""
- if peek(0) =~ /[$@]/
- token.concat(c = getc)
- if c == "@" and peek(0) == "@"
- token.concat getc
- end
- end
-
- while (ch = getc) =~ IDENT_RE do
- print " :#{ch}: " if RDoc::RubyLex.debug?
- token.concat ch
- end
-
- ungetc
-
- if (ch == "!" || ch == "?") && token[0,1] =~ /\w/ && peek(0) != "="
- token.concat getc
- end
-
- # almost fix token
-
- case token
- when /^\$/
- return Token(TkGVAR, token)
- when /^\@\@/
- @lex_state = :EXPR_END
- # p Token(TkCVAR, token)
- return Token(TkCVAR, token)
- when /^\@/
- @lex_state = :EXPR_END
- return Token(TkIVAR, token)
- end
-
- if @lex_state != :EXPR_DOT
- print token, "\n" if RDoc::RubyLex.debug?
-
- token_c, *trans = TkReading2Token[token]
- if token_c
- # reserved word?
-
- if (@lex_state != :EXPR_BEG &&
- @lex_state != :EXPR_FNAME &&
- trans[1])
- # modifiers
- token_c = TkSymbol2Token[trans[1]]
- @lex_state = trans[0]
- else
- if @lex_state != :EXPR_FNAME
- if ENINDENT_CLAUSE.include?(token)
- valid = peek(0) != ':'
-
- # check for ``class = val'' etc.
- case token
- when "class"
- valid = false unless peek_match?(/^\s*(<<|\w|::)/)
- when "def"
- valid = false if peek_match?(/^\s*(([+-\/*&\|^]|<<|>>|\|\||\&\&)=|\&\&|\|\|)/)
- when "do"
- valid = false if peek_match?(/^\s*([+-\/*]?=|\*|<|>|\&)/)
- when *ENINDENT_CLAUSE
- valid = false if peek_match?(/^\s*([+-\/*]?=|\*|<|>|\&|\|)/)
- else
- # no nothing
- end if valid
-
- if valid
- if token == "do"
- if ![TkFOR, TkWHILE, TkUNTIL].include?(@indent_stack.last)
- @indent += 1
- @indent_stack.push token_c
- end
- else
- @indent += 1
- @indent_stack.push token_c
- end
- else
- token_c = TkIDENTIFIER
- end
-
- elsif DEINDENT_CLAUSE.include?(token)
- @indent -= 1
- @indent_stack.pop
- end
- @lex_state = trans[0]
- else
- @lex_state = :EXPR_END
- end
- end
- return Token(token_c, token)
- end
- end
-
- if @lex_state == :EXPR_FNAME
- @lex_state = :EXPR_END
- if peek(0) == '='
- token.concat getc
- end
- elsif @lex_state == :EXPR_BEG || @lex_state == :EXPR_DOT ||
- @lex_state == :EXPR_ARG
- @lex_state = :EXPR_ARG
- else
- @lex_state = :EXPR_END
- end
-
- if token[0, 1] =~ /[A-Z]/
- return Token(TkCONSTANT, token)
- elsif token[token.size - 1, 1] =~ /[!?]/
- return Token(TkFID, token)
- else
- return Token(TkIDENTIFIER, token)
- end
- end
-
- def identify_here_document
- ch = getc
- # if lt = PERCENT_LTYPE[ch]
- if ch == "-"
- ch = getc
- indent = true
- end
- if /['"`]/ =~ ch
- user_quote = lt = ch
- quoted = ""
- while (c = getc) && c != lt
- quoted.concat c
- end
- else
- user_quote = nil
- lt = '"'
- quoted = ch.dup
- while (c = getc) && c =~ /\w/
- quoted.concat c
- end
- ungetc
- end
-
- ltback, @ltype = @ltype, lt
- reserve = []
- while ch = getc
- reserve.push ch
- if ch == "\\"
- reserve.push ch = getc
- elsif ch == "\n"
- break
- end
- end
-
- output_heredoc = reserve.join =~ /\A\r?\n\z/
-
- if output_heredoc then
- doc = '<<'
- doc << '-' if indent
- doc << "#{user_quote}#{quoted}#{user_quote}\n"
- else
- doc = '"'
- end
-
- @current_readed = @readed
- while l = gets
- l = l.sub(/(:?\r)?\n\z/, "\n")
- if (indent ? l.strip : l.chomp) == quoted
- break
- end
- doc << l
- end
-
- if output_heredoc then
- raise Error, "Missing terminating #{quoted} for string" unless l
-
- doc << l.chomp
- else
- doc << '"'
- end
-
- @current_readed = @here_readed
- @here_readed.concat reserve
- while ch = reserve.pop
- ungetc ch
- end
-
- token_class = output_heredoc ? RDoc::RubyLex::TkHEREDOC : Ltype2Token[lt]
- @ltype = ltback
- @lex_state = :EXPR_END
- Token(token_class, doc)
- end
-
- def identify_quotation
- type = ch = getc
- if lt = PERCENT_LTYPE[type]
- ch = getc
- elsif type =~ /\W/
- type = nil
- lt = "\""
- else
- return Token(TkMOD, '%')
- end
- # if ch !~ /\W/
- # ungetc
- # next
- # end
- #@ltype = lt
- @quoted = ch unless @quoted = PERCENT_PAREN[ch]
- identify_string(lt, @quoted, type)
- end
-
- def identify_number(op = "")
- @lex_state = :EXPR_END
-
- num = op
-
- if peek(0) == "0" && peek(1) !~ /[.eE]/
- num << getc
-
- case peek(0)
- when /[xX]/
- ch = getc
- match = /[0-9a-fA-F_]/
- when /[bB]/
- ch = getc
- match = /[01_]/
- when /[oO]/
- ch = getc
- match = /[0-7_]/
- when /[dD]/
- ch = getc
- match = /[0-9_]/
- when /[0-7]/
- match = /[0-7_]/
- when /[89]/
- raise Error, "Illegal octal digit"
- else
- return Token(TkINTEGER, num)
- end
-
- num << ch if ch
-
- len0 = true
- non_digit = false
- while ch = getc
- num << ch
- if match =~ ch
- if ch == "_"
- if non_digit
- raise Error, "trailing `#{ch}' in number"
- else
- non_digit = ch
- end
- else
- non_digit = false
- len0 = false
- end
- else
- ungetc
- num[-1, 1] = ''
- if len0
- raise Error, "numeric literal without digits"
- end
- if non_digit
- raise Error, "trailing `#{non_digit}' in number"
- end
- break
- end
- end
- return Token(TkINTEGER, num)
- end
-
- type = TkINTEGER
- allow_point = true
- allow_e = true
- non_digit = false
- while ch = getc
- num << ch
- case ch
- when /[0-9]/
- non_digit = false
- when "_"
- non_digit = ch
- when allow_point && "."
- if non_digit
- raise Error, "trailing `#{non_digit}' in number"
- end
- type = TkFLOAT
- if peek(0) !~ /[0-9]/
- type = TkINTEGER
- ungetc
- num[-1, 1] = ''
- break
- end
- allow_point = false
- when allow_e && "e", allow_e && "E"
- if non_digit
- raise Error, "trailing `#{non_digit}' in number"
- end
- type = TkFLOAT
- if peek(0) =~ /[+-]/
- num << getc
- end
- allow_e = false
- allow_point = false
- non_digit = ch
- else
- if non_digit
- raise Error, "trailing `#{non_digit}' in number"
- end
- ungetc
- num[-1, 1] = ''
- break
- end
- end
-
- Token(type, num)
- end
-
- def identify_string(ltype, quoted = ltype, type = nil)
- close = PERCENT_PAREN.values.include?(quoted)
- @ltype = ltype
- @quoted = quoted
-
- str = if ltype == quoted and %w[" ' /].include? ltype then
- ltype.dup
- else
- "%#{type or PERCENT_LTYPE.key ltype}#{PERCENT_PAREN_REV[quoted]||quoted}"
- end
-
- subtype = nil
- begin
- nest = 0
-
- while ch = getc
- str << ch
-
- if @quoted == ch and nest <= 0
- break
- elsif @ltype != "'" && @ltype != "]" && @ltype != ":" and ch == "#"
- ch = getc
- subtype = true
- if ch == "{" then
- str << ch << skip_inner_expression
- next
- else
- ungetc
- end
- elsif ch == '\\'
- if %w[' /].include? @ltype then
- case ch = getc
- when "\\", "\n", "'"
- when @ltype
- str << ch
- else
- ungetc
- end
- else
- str << read_escape
- end
- end
-
- if close then
- if PERCENT_PAREN[ch] == @quoted
- nest += 1
- elsif ch == @quoted
- nest -= 1
- end
- end
- end
-
- if @ltype == "/"
- while peek(0) =~ /i|m|x|o|e|s|u|n/
- str << getc
- end
- end
-
- if subtype
- Token(DLtype2Token[ltype], str)
- else
- Token(Ltype2Token[ltype], str)
- end
- ensure
- @ltype = nil
- @quoted = nil
- @lex_state = :EXPR_END
- end
- end
-
- def skip_inner_expression
- res = ""
- nest = 0
- while ch = getc
- res << ch
- if ch == '}'
- break if nest.zero?
- nest -= 1
- elsif ch == '{'
- nest += 1
- end
- end
- res
- end
-
- def identify_comment
- @ltype = "#"
-
- comment = '#'
-
- while ch = getc
- # if ch == "\\" #"
- # read_escape
- # end
- if ch == "\n"
- @ltype = nil
- ungetc
- break
- end
-
- comment << ch
- end
-
- return Token(TkCOMMENT, comment)
- end
-
- def read_escape
- escape = ''
- ch = getc
-
- case ch
- when "\n", "\r", "\f"
- escape << ch
- when "\\", "n", "t", "r", "f", "v", "a", "e", "b", "s" #"
- escape << ch
- when /[0-7]/
- ungetc ch
- 3.times do
- ch = getc
- case ch
- when /[0-7]/
- escape << ch
- when nil
- break
- else
- ungetc
- break
- end
- end
-
- when "x"
- escape << ch
-
- 2.times do
- ch = getc
- case ch
- when /[0-9a-fA-F]/
- escape << ch
- when nil
- break
- else
- ungetc
- break
- end
- end
-
- when "M"
- escape << ch
-
- ch = getc
- if ch != '-'
- ungetc
- else
- escape << ch
-
- ch = getc
- if ch == "\\" #"
- ungetc
- escape << read_escape
- else
- escape << ch
- end
- end
-
- when "C", "c" #, "^"
- escape << ch
-
- if ch == "C"
- ch = getc
-
- if ch == "-"
- escape << ch
- ch = getc
- escape << ch
-
- escape << read_escape if ch == "\\"
- else
- ungetc
- end
- elsif (ch = getc) == "\\" #"
- escape << ch << read_escape
- end
- else
- escape << ch
-
- # other characters
- end
-
- escape
- end
-
- # :startdoc:
-
-end
-
-#RDoc::RubyLex.debug_level = 1
diff --git a/lib/rdoc/ruby_token.rb b/lib/rdoc/ruby_token.rb
deleted file mode 100644
index d923e24b18..0000000000
--- a/lib/rdoc/ruby_token.rb
+++ /dev/null
@@ -1,461 +0,0 @@
-# frozen_string_literal: false
-#--
-# irb/ruby-token.rb - ruby tokens
-# $Release Version: 0.9.5$
-# $Revision: 11708 $
-# $Date: 2007-02-12 15:01:19 -0800 (Mon, 12 Feb 2007) $
-# by Keiju ISHITSUKA(keiju@ruby-lang.org)
-#++
-# Definitions of all tokens involved in the lexical analysis.
-#
-# This class is not documented because it is so deep in the internals.
-
-module RDoc::RubyToken
- # :stopdoc:
-
- EXPR_BEG = :EXPR_BEG
- EXPR_MID = :EXPR_MID
- EXPR_END = :EXPR_END
- EXPR_ARG = :EXPR_ARG
- EXPR_FNAME = :EXPR_FNAME
- EXPR_DOT = :EXPR_DOT
- EXPR_CLASS = :EXPR_CLASS
-
- # for ruby 1.4X
- if !defined?(Symbol)
- Symbol = Integer
- end
-
- def set_token_position(line, char)
- @prev_line_no = line
- @prev_char_no = char
- end
-
- class Token
- def initialize(seek, line_no, char_no, text = nil)
- @seek = seek
- @line_no = line_no
- @char_no = char_no
- @text = text
- end
-
- attr_reader :seek
- attr_reader :line_no
- attr_reader :char_no
-
- attr_accessor :text
-
- def ==(other)
- self.class == other.class and
- other.line_no == @line_no and
- other.char_no == @char_no and
- other.text == @text
- end
-
- ##
- # Because we're used in contexts that expect to return a token, we set the
- # text string and then return ourselves
-
- def set_text(text)
- @text = text
- self
- end
-
- def inspect # :nodoc:
- klass = self.class.name.split('::').last
- "{%s %d, %d:%d %p}" % [klass, @seek, @line_no, @char_no, @text]
- end
-
- end
-
- class TkNode < Token
- def initialize(seek, line_no, char_no, node = nil)
- super seek, line_no, char_no
- @node = node
- end
-
- attr_reader:node
-
- def ==(other)
- self.class == other.class and
- other.line_no == @line_no and
- other.char_no == @char_no and
- other.node == @node
- end
-
- def set_text text
- @node = text
- self
- end
-
- alias text node
-
- def inspect # :nodoc:
- klass = self.class.name.split('::').last
- "{%s %d, %d:%d %p}" % [klass, @seek, @line_no, @char_no, @node]
- end
-
- end
-
- class TkId < Token
- def initialize(seek, line_no, char_no, name)
- super(seek, line_no, char_no)
- @name = name
- end
- attr_reader:name
-
- def ==(other)
- self.class == other.class and
- other.line_no == @line_no and
- other.char_no == @char_no and
- other.name == @name
- end
-
- def set_text text
- @name = text
- self
- end
-
- alias text name
-
- def inspect # :nodoc:
- klass = self.class.name.split('::').last
- "{%s %d, %d:%d %p}" % [klass, @seek, @line_no, @char_no, @name]
- end
-
- end
-
- class TkKW < TkId
- end
-
- class TkVal < Token
- def initialize(seek, line_no, char_no, value = nil)
- super(seek, line_no, char_no)
- @value = value
- end
- attr_accessor :value
-
- def ==(other)
- self.class == other.class and
- other.line_no == @line_no and
- other.char_no == @char_no and
- other.value == @value
- end
-
- def set_text text
- @value = text
- self
- end
-
- alias text value
-
- def inspect # :nodoc:
- klass = self.class.name.split('::').last
- "{%s %s, %d:%d %p}" % [klass, @seek, @line_no, @char_no, @value]
- end
-
- end
-
- class TkOp < Token
- def initialize(seek, line_no, char_no, name = nil)
- super seek, line_no, char_no
- @name = name
- end
-
- attr_accessor :name
-
- def ==(other)
- self.class == other.class and
- other.line_no == @line_no and
- other.char_no == @char_no and
- other.name == @name
- end
-
- def set_text text
- @name = text
- self
- end
-
- alias text name
-
- def inspect # :nodoc:
- klass = self.class.name.split('::').last
- "{%s %d, %d:%d %p}" % [klass, @seek, @line_no, @char_no, @name]
- end
-
- end
-
- class TkOPASGN < TkOp
- def initialize(seek, line_no, char_no, op)
- super(seek, line_no, char_no)
- op = TkReading2Token[op][0] unless op.kind_of?(Symbol)
- @op = op
- @text = nil
- end
-
- attr_reader:op
-
- def ==(other)
- self.class == other.class and
- other.line_no == @line_no and
- other.char_no == @char_no and
- other.op == @op
- end
-
- def text
- @text ||= "#{TkToken2Reading[op]}="
- end
-
- def inspect # :nodoc:
- klass = self.class.name.split('::').last
- "{%s %d, %d:%d %p}" % [klass, @seek, @line_no, @char_no, @op]
- end
-
- end
-
- class TkUnknownChar < Token
- def initialize(seek, line_no, char_no, name)
- super(seek, line_no, char_no)
- @name = name
- end
- attr_reader:name
-
- def ==(other)
- self.class == other.class and
- other.line_no == @line_no and
- other.char_no == @char_no and
- other.name == @name
- end
-
- def set_text text
- @name = text
- self
- end
-
- alias text name
-
- def inspect # :nodoc:
- klass = self.class.name.split('::').last
- "{%s %d, %d:%d %p}" % [klass, @seek, @line_no, @char_no, @name]
- end
-
- end
-
- class TkError < Token
- end
-
- def Token(token, value = nil)
- value ||= TkToken2Reading[token]
-
- case token
- when String
- if (tk = TkReading2Token[token]).nil?
- IRB.fail TkReading2TokenNoKey, token
- end
-
- tk = Token(tk[0], value)
-
- if tk.kind_of?(TkOp) then
- tk.name = token
- end
- when Symbol
- if (tk = TkSymbol2Token[token]).nil?
- IRB.fail TkSymbol2TokenNoKey, token
- end
-
- tk = Token(tk[0], value)
- else
- if token.instance_method(:initialize).arity == 3 then
- tk = token.new(@prev_seek, @prev_line_no, @prev_char_no)
- tk.set_text value
- else
- tk = token.new(@prev_seek, @prev_line_no, @prev_char_no, value)
- end
- end
-
- tk
- end
-
- TokenDefinitions = [
- [:TkCLASS, TkKW, "class", :EXPR_CLASS],
- [:TkMODULE, TkKW, "module", :EXPR_BEG],
- [:TkDEF, TkKW, "def", :EXPR_FNAME],
- [:TkUNDEF, TkKW, "undef", :EXPR_FNAME],
- [:TkBEGIN, TkKW, "begin", :EXPR_BEG],
- [:TkRESCUE, TkKW, "rescue", :EXPR_MID],
- [:TkENSURE, TkKW, "ensure", :EXPR_BEG],
- [:TkEND, TkKW, "end", :EXPR_END],
- [:TkIF, TkKW, "if", :EXPR_BEG, :TkIF_MOD],
- [:TkUNLESS, TkKW, "unless", :EXPR_BEG, :TkUNLESS_MOD],
- [:TkTHEN, TkKW, "then", :EXPR_BEG],
- [:TkELSIF, TkKW, "elsif", :EXPR_BEG],
- [:TkELSE, TkKW, "else", :EXPR_BEG],
- [:TkCASE, TkKW, "case", :EXPR_BEG],
- [:TkWHEN, TkKW, "when", :EXPR_BEG],
- [:TkWHILE, TkKW, "while", :EXPR_BEG, :TkWHILE_MOD],
- [:TkUNTIL, TkKW, "until", :EXPR_BEG, :TkUNTIL_MOD],
- [:TkFOR, TkKW, "for", :EXPR_BEG],
- [:TkBREAK, TkKW, "break", :EXPR_MID],
- [:TkNEXT, TkKW, "next", :EXPR_END],
- [:TkREDO, TkKW, "redo", :EXPR_END],
- [:TkRETRY, TkKW, "retry", :EXPR_END],
- [:TkIN, TkKW, "in", :EXPR_BEG],
- [:TkDO, TkKW, "do", :EXPR_BEG],
- [:TkRETURN, TkKW, "return", :EXPR_MID],
- [:TkYIELD, TkKW, "yield", :EXPR_END],
- [:TkSUPER, TkKW, "super", :EXPR_END],
- [:TkSELF, TkKW, "self", :EXPR_END],
- [:TkNIL, TkKW, "nil", :EXPR_END],
- [:TkTRUE, TkKW, "true", :EXPR_END],
- [:TkFALSE, TkKW, "false", :EXPR_END],
- [:TkAND, TkKW, "and", :EXPR_BEG],
- [:TkOR, TkKW, "or", :EXPR_BEG],
- [:TkNOT, TkKW, "not", :EXPR_BEG],
- [:TkIF_MOD, TkKW],
- [:TkUNLESS_MOD, TkKW],
- [:TkWHILE_MOD, TkKW],
- [:TkUNTIL_MOD, TkKW],
- [:TkALIAS, TkKW, "alias", :EXPR_FNAME],
- [:TkDEFINED, TkKW, "defined?", :EXPR_END],
- [:TklBEGIN, TkKW, "BEGIN", :EXPR_END],
- [:TklEND, TkKW, "END", :EXPR_END],
- [:Tk__LINE__, TkKW, "__LINE__", :EXPR_END],
- [:Tk__FILE__, TkKW, "__FILE__", :EXPR_END],
-
- [:TkIDENTIFIER, TkId],
- [:TkFID, TkId],
- [:TkGVAR, TkId],
- [:TkCVAR, TkId],
- [:TkIVAR, TkId],
- [:TkCONSTANT, TkId],
-
- [:TkINTEGER, TkVal],
- [:TkFLOAT, TkVal],
- [:TkSTRING, TkVal],
- [:TkHEREDOC, TkVal],
- [:TkXSTRING, TkVal],
- [:TkREGEXP, TkVal],
- [:TkSYMBOL, TkVal],
- [:TkCHAR, TkVal],
-
- [:TkDSTRING, TkNode],
- [:TkDXSTRING, TkNode],
- [:TkDREGEXP, TkNode],
- [:TkNTH_REF, TkNode],
- [:TkBACK_REF, TkNode],
-
- [:TkUPLUS, TkOp, "+@"],
- [:TkUMINUS, TkOp, "-@"],
- [:TkPOW, TkOp, "**"],
- [:TkCMP, TkOp, "<=>"],
- [:TkEQ, TkOp, "=="],
- [:TkEQQ, TkOp, "==="],
- [:TkNEQ, TkOp, "!="],
- [:TkGEQ, TkOp, ">="],
- [:TkLEQ, TkOp, "<="],
- [:TkANDOP, TkOp, "&&"],
- [:TkOROP, TkOp, "||"],
- [:TkMATCH, TkOp, "=~"],
- [:TkNMATCH, TkOp, "!~"],
- [:TkDOT2, TkOp, ".."],
- [:TkDOT3, TkOp, "..."],
- [:TkAREF, TkOp, "[]"],
- [:TkASET, TkOp, "[]="],
- [:TkLSHFT, TkOp, "<<"],
- [:TkRSHFT, TkOp, ">>"],
- [:TkCOLON2, TkOp, '::'],
- [:TkCOLON3, TkOp, '::'],
- #[:OPASGN, TkOp], # +=, -= etc. #
- [:TkASSOC, TkOp, "=>"],
- [:TkQUESTION, TkOp, "?"], #?
- [:TkCOLON, TkOp, ":"], #:
-
- [:TkfLPAREN, Token, "("], # func( #
- [:TkfLBRACK, Token, "["], # func[ #
- [:TkfLBRACE, Token, "{"], # func{ #
- [:TkSYMBEG, Token, ":"], # :SYMBOL
-
- [:TkAMPER, TkOp, "&"],
- [:TkGT, TkOp, ">"],
- [:TkLT, TkOp, "<"],
- [:TkPLUS, TkOp, "+"],
- [:TkSTAR, TkOp, "*"],
- [:TkMINUS, TkOp, "-"],
- [:TkMULT, TkOp, "*"],
- [:TkDIV, TkOp, "/"],
- [:TkMOD, TkOp, "%"],
- [:TkBITOR, TkOp, "|"],
- [:TkBITXOR, TkOp, "^"],
- [:TkBITAND, TkOp, "&"],
- [:TkBITNOT, TkOp, "~"],
- [:TkNOTOP, TkOp, "!"],
-
- [:TkBACKQUOTE, TkOp, "`"],
-
- [:TkASSIGN, Token, "="],
- [:TkDOT, Token, "."],
- [:TkLPAREN, Token, "("], #(exp)
- [:TkLBRACK, Token, "["], #[arry]
- [:TkLBRACE, Token, "{"], #{hash}
- [:TkRPAREN, Token, ")"],
- [:TkRBRACK, Token, "]"],
- [:TkRBRACE, Token, "}"],
- [:TkCOMMA, Token, ","],
- [:TkSEMICOLON, Token, ";"],
-
- [:TkCOMMENT, TkVal],
- [:TkSPACE, Token, " "],
- [:TkNL, Token, "\n"],
- [:TkEND_OF_SCRIPT],
-
- [:TkBACKSLASH, TkUnknownChar, "\\"],
- [:TkAT, TkUnknownChar, "@"],
- [:TkDOLLAR, TkUnknownChar, "$"],
- ]
-
- # {reading => token_class}
- # {reading => [token_class, *opt]}
- TkReading2Token = {}
- TkToken2Reading = {}
- TkSymbol2Token = {}
-
- def self.def_token(token_n, super_token = Token, reading = nil, *opts)
- token_n = token_n.id2name if token_n.kind_of?(Symbol)
- if const_defined?(token_n)
- IRB.fail AlreadyDefinedToken, token_n
- end
- token_c = eval("class #{token_n} < #{super_token}; end; #{token_n}")
-
- if reading
- TkToken2Reading[token_c] = reading
-
- return if TkReading2Token[reading]
-
- if opts.empty?
- TkReading2Token[reading] = [token_c]
- else
- TkReading2Token[reading] = [token_c].concat(opts)
- end
- end
- TkSymbol2Token[token_n.intern] = token_c
- end
-
- for defs in TokenDefinitions
- def_token(*defs)
- end
-
- def_token :TkRD_COMMENT, TkCOMMENT
-
- NEWLINE_TOKEN = TkNL.new 0, 0, 0, "\n"
-
- class TkSYMBOL
-
- def to_sym
- @sym ||= text[1..-1].intern
- end
-
- end
-
- # :startdoc:
-end
-
diff --git a/lib/rdoc/rubygems_hook.rb b/lib/rdoc/rubygems_hook.rb
index e9351b7280..90b0541fcf 100644
--- a/lib/rdoc/rubygems_hook.rb
+++ b/lib/rdoc/rubygems_hook.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'rubygems/user_interaction'
require 'fileutils'
require 'rdoc'
diff --git a/lib/rdoc/servlet.rb b/lib/rdoc/servlet.rb
index 90f403bc64..79550fe63b 100644
--- a/lib/rdoc/servlet.rb
+++ b/lib/rdoc/servlet.rb
@@ -1,5 +1,6 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'rdoc'
+require 'erb'
require 'time'
require 'json'
require 'webrick'
@@ -217,7 +218,7 @@ exception:
<pre>#{ERB::Util.html_escape exception.message}</pre>
<p>Please report this to the
-<a href="https://github.com/rdoc/rdoc/issues">RDoc issues tracker</a>. Please
+<a href="https://github.com/ruby/rdoc/issues">RDoc issues tracker</a>. Please
include the RDoc version, the URI above and exception class, message and
backtrace. If you're viewing a gem's documentation, include the gem name and
version. If you're viewing Ruby's documentation, include the version of ruby.
@@ -427,14 +428,14 @@ version. If you're viewing Ruby's documentation, include the version of ruby.
end
raise WEBrick::HTTPStatus::NotFound,
- "Could not find gem \"#{source_name}\". Are you sure you installed it?" unless ri_dir
+ "Could not find gem \"#{ERB::Util.html_escape(source_name)}\". Are you sure you installed it?" unless ri_dir
store = RDoc::Store.new ri_dir, type
return store if File.exist? store.cache_path
raise WEBrick::HTTPStatus::NotFound,
- "Could not find documentation for \"#{source_name}\". Please run `gem rdoc --ri gem_name`"
+ "Could not find documentation for \"#{ERB::Util.html_escape(source_name)}\". Please run `gem rdoc --ri gem_name`"
end
end
diff --git a/lib/rdoc/single_class.rb b/lib/rdoc/single_class.rb
index 7affa027e1..6a7b67deb3 100644
--- a/lib/rdoc/single_class.rb
+++ b/lib/rdoc/single_class.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A singleton class
diff --git a/lib/rdoc/stats.rb b/lib/rdoc/stats.rb
index 354e904b19..bd6c0ef23a 100644
--- a/lib/rdoc/stats.rb
+++ b/lib/rdoc/stats.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# RDoc statistics collector which prints a summary and report of a project's
# documentation totals.
diff --git a/lib/rdoc/stats/normal.rb b/lib/rdoc/stats/normal.rb
index ba00b6cbdf..0a22f0582b 100644
--- a/lib/rdoc/stats/normal.rb
+++ b/lib/rdoc/stats/normal.rb
@@ -1,5 +1,10 @@
-# frozen_string_literal: false
-require 'io/console/size'
+# frozen_string_literal: true
+begin
+ require 'io/console/size'
+rescue LoadError
+ # for JRuby
+ require 'io/console'
+end
##
# Stats printer that prints just the files being documented with a progress
@@ -21,27 +26,28 @@ class RDoc::Stats::Normal < RDoc::Stats::Quiet
files_so_far,
@num_files)
- # Print a progress bar, but make sure it fits on a single line. Filename
- # will be truncated if necessary.
- terminal_width = IO.console_size[1].to_i.nonzero? || 80
- max_filename_size = terminal_width - progress_bar.size
-
- if filename.size > max_filename_size then
- # Turn "some_long_filename.rb" to "...ong_filename.rb"
- filename = filename[(filename.size - max_filename_size) .. -1]
- filename[0..2] = "..."
- end
-
- line = "#{progress_bar}#{filename}"
if $stdout.tty?
+ # Print a progress bar, but make sure it fits on a single line. Filename
+ # will be truncated if necessary.
+ size = IO.respond_to?(:console_size) ? IO.console_size : IO.console.winsize
+ terminal_width = size[1].to_i.nonzero? || 80
+ max_filename_size = (terminal_width - progress_bar.size) - 1
+
+ if filename.size > max_filename_size then
+ # Turn "some_long_filename.rb" to "...ong_filename.rb"
+ filename = filename[(filename.size - max_filename_size) .. -1]
+ filename[0..2] = "..."
+ end
+
# Clean the line with whitespaces so that leftover output from the
# previous line doesn't show up.
- $stdout.print("\r" << (" " * @last_width) << ("\b" * @last_width) << "\r") if @last_width && @last_width > 0
- @last_width = line.size
- $stdout.print("#{line}\r")
+ $stdout.print("\r\e[K") if @last_width && @last_width > 0
+ @last_width = progress_bar.size + filename.size
+ term = "\r"
else
- $stdout.puts(line)
+ term = "\n"
end
+ $stdout.print(progress_bar, filename, term)
$stdout.flush
end
diff --git a/lib/rdoc/stats/quiet.rb b/lib/rdoc/stats/quiet.rb
index 561c272ef7..bc4161b2d4 100644
--- a/lib/rdoc/stats/quiet.rb
+++ b/lib/rdoc/stats/quiet.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Stats printer that prints nothing
diff --git a/lib/rdoc/stats/verbose.rb b/lib/rdoc/stats/verbose.rb
index e04edade52..6ace8937a2 100644
--- a/lib/rdoc/stats/verbose.rb
+++ b/lib/rdoc/stats/verbose.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# Stats printer that prints everything documented, including the documented
# status
diff --git a/lib/rdoc/store.rb b/lib/rdoc/store.rb
index aaefff8f13..f420fc2bd2 100644
--- a/lib/rdoc/store.rb
+++ b/lib/rdoc/store.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'fileutils'
##
@@ -117,6 +117,11 @@ class RDoc::Store
attr_accessor :encoding
##
+ # The lazy constants alias will be discovered in passing
+
+ attr_reader :unmatched_constant_alias
+
+ ##
# Creates a new Store of +type+ that will load or save to +path+
def initialize path = nil, type = nil
@@ -143,6 +148,7 @@ class RDoc::Store
@classes_hash = {}
@modules_hash = {}
@files_hash = {}
+ @text_files_hash = {}
@c_enclosure_classes = {}
@c_enclosure_names = {}
@@ -152,6 +158,8 @@ class RDoc::Store
@unique_classes = nil
@unique_modules = nil
+
+ @unmatched_constant_alias = {}
end
##
@@ -177,16 +185,24 @@ class RDoc::Store
# Adds the file with +name+ as an RDoc::TopLevel to the store. Returns the
# created RDoc::TopLevel.
- def add_file absolute_name, relative_name = absolute_name
+ def add_file absolute_name, relative_name: absolute_name, parser: nil
unless top_level = @files_hash[relative_name] then
top_level = RDoc::TopLevel.new absolute_name, relative_name
+ top_level.parser = parser if parser
top_level.store = self
@files_hash[relative_name] = top_level
+ @text_files_hash[relative_name] = top_level if top_level.text?
end
top_level
end
+ def update_parser_of_file(absolute_name, parser)
+ if top_level = @files_hash[absolute_name] then
+ @text_files_hash[absolute_name] = top_level if top_level.text?
+ end
+ end
+
##
# Returns all classes discovered by RDoc
@@ -421,8 +437,8 @@ class RDoc::Store
# +file_name+
def find_text_page file_name
- @files_hash.each_value.find do |file|
- file.text? and file.full_name == file_name
+ @text_files_hash.each_value.find do |file|
+ file.full_name == file_name
end
end
@@ -530,6 +546,7 @@ class RDoc::Store
@cache[:pages].each do |page_name|
page = load_page page_name
@files_hash[page_name] = page
+ @text_files_hash[page_name] = page if page.text?
end
end
@@ -539,7 +556,7 @@ class RDoc::Store
def load_cache
#orig_enc = @encoding
- open cache_path, 'rb' do |io|
+ File.open cache_path, 'rb' do |io|
@cache = Marshal.load io.read
end
@@ -585,6 +602,8 @@ class RDoc::Store
case obj
when RDoc::NormalClass then
@classes_hash[klass_name] = obj
+ when RDoc::SingleClass then
+ @classes_hash[klass_name] = obj
when RDoc::NormalModule then
@modules_hash[klass_name] = obj
end
@@ -596,7 +615,7 @@ class RDoc::Store
def load_class_data klass_name
file = class_file klass_name
- open file, 'rb' do |io|
+ File.open file, 'rb' do |io|
Marshal.load io.read
end
rescue Errno::ENOENT => e
@@ -611,7 +630,7 @@ class RDoc::Store
def load_method klass_name, method_name
file = method_file klass_name, method_name
- open file, 'rb' do |io|
+ File.open file, 'rb' do |io|
obj = Marshal.load io.read
obj.store = self
obj.parent =
@@ -631,7 +650,7 @@ class RDoc::Store
def load_page page_name
file = page_file page_name
- open file, 'rb' do |io|
+ File.open file, 'rb' do |io|
obj = Marshal.load io.read
obj.store = self
obj
@@ -703,8 +722,8 @@ class RDoc::Store
# Returns the RDoc::TopLevel that is a text file and has the given +name+
def page name
- @files_hash.each_value.find do |file|
- file.text? and file.page_name == name
+ @text_files_hash.each_value.find do |file|
+ file.page_name == name
end
end
@@ -778,7 +797,7 @@ class RDoc::Store
marshal = Marshal.dump @cache
- open cache_path, 'wb' do |io|
+ File.open cache_path, 'wb' do |io|
io.write marshal
end
end
@@ -854,7 +873,7 @@ class RDoc::Store
marshal = Marshal.dump klass
- open path, 'wb' do |io|
+ File.open path, 'wb' do |io|
io.write marshal
end
end
@@ -879,7 +898,7 @@ class RDoc::Store
marshal = Marshal.dump method
- open method_file(full_name, method.full_name), 'wb' do |io|
+ File.open method_file(full_name, method.full_name), 'wb' do |io|
io.write marshal
end
end
@@ -901,7 +920,7 @@ class RDoc::Store
marshal = Marshal.dump page
- open path, 'wb' do |io|
+ File.open path, 'wb' do |io|
io.write marshal
end
end
diff --git a/lib/rdoc/task.rb b/lib/rdoc/task.rb
index 1074de0197..323d00eabc 100644
--- a/lib/rdoc/task.rb
+++ b/lib/rdoc/task.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
# Copyright (c) 2003, 2004 Jim Weirich, 2009 Eric Hodel
#
diff --git a/lib/rdoc/test_case.rb b/lib/rdoc/test_case.rb
deleted file mode 100644
index aa7120cb59..0000000000
--- a/lib/rdoc/test_case.rb
+++ /dev/null
@@ -1,204 +0,0 @@
-# frozen_string_literal: false
-begin
- gem 'minitest', '~> 4.0' unless defined?(Test::Unit)
-rescue NoMethodError, Gem::LoadError
- # for ruby tests
-end
-
-require 'minitest/autorun'
-require 'minitest/benchmark' if ENV['BENCHMARK']
-
-require 'fileutils'
-require 'pp'
-require 'tempfile'
-require 'tmpdir'
-require 'stringio'
-
-require 'rdoc'
-
-##
-# RDoc::TestCase is an abstract TestCase to provide common setup and teardown
-# across all RDoc tests. The test case uses minitest, so all the assertions
-# of minitest may be used.
-#
-# The testcase provides the following:
-#
-# * A reset code-object tree
-# * A reset markup preprocessor (RDoc::Markup::PreProcess)
-# * The <code>@RM</code> alias of RDoc::Markup (for less typing)
-# * <code>@pwd</code> containing the current working directory
-# * FileUtils, pp, Tempfile, Dir.tmpdir and StringIO
-
-class RDoc::TestCase < MiniTest::Unit::TestCase
-
- ##
- # Abstract test-case setup
-
- def setup
- super
-
- @top_level = nil
-
- @RM = RDoc::Markup
-
- RDoc::Markup::PreProcess.reset
-
- @pwd = Dir.pwd
-
- @store = RDoc::Store.new
-
- @rdoc = RDoc::RDoc.new
- @rdoc.store = @store
- @rdoc.options = RDoc::Options.new
-
- g = Object.new
- def g.class_dir() end
- def g.file_dir() end
- @rdoc.generator = g
- end
-
- ##
- # Asserts +path+ is a file
-
- def assert_file path
- assert File.file?(path), "#{path} is not a file"
- end
-
- ##
- # Asserts +path+ is a directory
-
- def assert_directory path
- assert File.directory?(path), "#{path} is not a directory"
- end
-
- ##
- # Refutes +path+ exists
-
- def refute_file path
- refute File.exist?(path), "#{path} exists"
- end
-
- ##
- # Shortcut for RDoc::Markup::BlankLine.new
-
- def blank_line
- @RM::BlankLine.new
- end
-
- ##
- # Shortcut for RDoc::Markup::BlockQuote.new with +contents+
-
- def block *contents
- @RM::BlockQuote.new(*contents)
- end
-
- ##
- # Creates an RDoc::Comment with +text+ which was defined on +top_level+.
- # By default the comment has the 'rdoc' format.
-
- def comment text, top_level = @top_level
- RDoc::Comment.new text, top_level
- end
-
- ##
- # Shortcut for RDoc::Markup::Document.new with +contents+
-
- def doc *contents
- @RM::Document.new(*contents)
- end
-
- ##
- # Shortcut for RDoc::Markup::HardBreak.new
-
- def hard_break
- @RM::HardBreak.new
- end
-
- ##
- # Shortcut for RDoc::Markup::Heading.new with +level+ and +text+
-
- def head level, text
- @RM::Heading.new level, text
- end
-
- ##
- # Shortcut for RDoc::Markup::ListItem.new with +label+ and +parts+
-
- def item label = nil, *parts
- @RM::ListItem.new label, *parts
- end
-
- ##
- # Shortcut for RDoc::Markup::List.new with +type+ and +items+
-
- def list type = nil, *items
- @RM::List.new type, *items
- end
-
- ##
- # Enables pretty-print output
-
- def mu_pp obj # :nodoc:
- s = ''
- s = PP.pp obj, s
- s = s.force_encoding Encoding.default_external
- s.chomp
- end
-
- ##
- # Shortcut for RDoc::Markup::Paragraph.new with +contents+
-
- def para *a
- @RM::Paragraph.new(*a)
- end
-
- ##
- # Shortcut for RDoc::Markup::Rule.new with +weight+
-
- def rule weight
- @RM::Rule.new weight
- end
-
- ##
- # Shortcut for RDoc::Markup::Raw.new with +contents+
-
- def raw *contents
- @RM::Raw.new(*contents)
- end
-
- ##
- # Creates a temporary directory changes the current directory to it for the
- # duration of the block.
- #
- # Depends upon Dir.mktmpdir
-
- def temp_dir
- Dir.mktmpdir do |temp_dir|
- Dir.chdir temp_dir do
- yield temp_dir
- end
- end
- end
-
- ##
- # Shortcut for RDoc::Markup::Verbatim.new with +parts+
-
- def verb *parts
- @RM::Verbatim.new(*parts)
- end
-
- ##
- # run capture_io with setting $VERBOSE = true
-
- def verbose_capture_io
- capture_io do
- begin
- orig_verbose = $VERBOSE
- $VERBOSE = true
- yield
- ensure
- $VERBOSE = orig_verbose
- end
- end
- end
-end
diff --git a/lib/rdoc/text.rb b/lib/rdoc/text.rb
index a38bb921ad..22c3777ff9 100644
--- a/lib/rdoc/text.rb
+++ b/lib/rdoc/text.rb
@@ -1,5 +1,4 @@
-# coding: utf-8
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# For RDoc::Text#to_html
@@ -7,16 +6,6 @@
require 'strscan'
##
-# For RDoc::Text#snippet
-
-begin
- gem 'json'
-rescue NameError => e # --disable-gems
- raise unless e.name == :gem
-rescue Gem::LoadError
-end
-
-##
# Methods for manipulating comment text
module RDoc::Text
@@ -71,7 +60,7 @@ module RDoc::Text
text.each_line do |line|
nil while line.gsub!(/(?:\G|\r)((?:.{8})*?)([^\t\r\n]{0,7})\t/) do
r = "#{$1}#{$2}#{' ' * (8 - $2.size)}"
- r.force_encoding text.encoding
+ r = RDoc::Encoding.change_encoding r, text.encoding
r
end
@@ -93,7 +82,7 @@ module RDoc::Text
end
empty = ''
- empty.force_encoding text.encoding
+ empty = RDoc::Encoding.change_encoding empty, text.encoding
text.gsub(/^ {0,#{indent}}/, empty)
end
@@ -160,7 +149,7 @@ module RDoc::Text
return text if text =~ /^(?>\s*)[^\#]/
empty = ''
- empty.force_encoding text.encoding
+ empty = RDoc::Encoding.change_encoding empty, text.encoding
text.gsub(/^\s*(#+)/) { $1.tr '#', ' ' }.gsub(/^\s+$/, empty)
end
@@ -180,17 +169,17 @@ module RDoc::Text
encoding = text.encoding
- text = text.gsub %r%Document-method:\s+[\w:.#=!?]+%, ''
+ text = text.gsub %r%Document-method:\s+[\w:.#=!?|^&<>~+\-/*\%@`\[\]]+%, ''
space = ' '
- space.force_encoding encoding if encoding
+ space = RDoc::Encoding.change_encoding space, encoding if encoding
text.sub! %r%/\*+% do space * $&.length end
text.sub! %r%\*+/% do space * $&.length end
text.gsub! %r%^[ \t]*\*%m do space * $&.length end
empty = ''
- empty.force_encoding encoding if encoding
+ empty = RDoc::Encoding.change_encoding empty, encoding if encoding
text.gsub(/^\s+$/, empty)
end
@@ -199,7 +188,7 @@ module RDoc::Text
# trademark symbols in +text+ to properly encoded characters.
def to_html text
- html = ''.encode text.encoding
+ html = (''.encode text.encoding).dup
encoded = RDoc::Text::TO_HTML_CHARACTERS[text.encoding]
diff --git a/lib/rdoc/token_stream.rb b/lib/rdoc/token_stream.rb
index b0035227fa..dbe6c5ae85 100644
--- a/lib/rdoc/token_stream.rb
+++ b/lib/rdoc/token_stream.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A TokenStream is a list of tokens, gathered during the parse of some entity
# (say a method). Entities populate these streams by being registered with the
@@ -10,43 +10,61 @@ module RDoc::TokenStream
##
# Converts +token_stream+ to HTML wrapping various tokens with
- # <tt><span></tt> elements. The following tokens types are wrapped in spans
- # with the given class names:
- #
- # TkCONSTANT :: 'ruby-constant'
- # TkKW :: 'ruby-keyword'
- # TkIVAR :: 'ruby-ivar'
- # TkOp :: 'ruby-operator'
- # TkId :: 'ruby-identifier'
- # TkNode :: 'ruby-node'
- # TkCOMMENT :: 'ruby-comment'
- # TkREGEXP :: 'ruby-regexp'
- # TkSTRING :: 'ruby-string'
- # TkVal :: 'ruby-value'
- #
- # Other token types are not wrapped in spans.
+ # <tt><span></tt> elements. Some tokens types are wrapped in spans
+ # with the given class names. Other token types are not wrapped in spans.
def self.to_html token_stream
+ starting_title = false
+
token_stream.map do |t|
next unless t
- style = case t
- when RDoc::RubyToken::TkCONSTANT then 'ruby-constant'
- when RDoc::RubyToken::TkKW then 'ruby-keyword'
- when RDoc::RubyToken::TkIVAR then 'ruby-ivar'
- when RDoc::RubyToken::TkOp then 'ruby-operator'
- when RDoc::RubyToken::TkId then 'ruby-identifier'
- when RDoc::RubyToken::TkNode then 'ruby-node'
- when RDoc::RubyToken::TkCOMMENT then 'ruby-comment'
- when RDoc::RubyToken::TkREGEXP then 'ruby-regexp'
- when RDoc::RubyToken::TkSTRING then 'ruby-string'
- when RDoc::RubyToken::TkVal then 'ruby-value'
+ style = case t[:kind]
+ when :on_const then 'ruby-constant'
+ when :on_kw then 'ruby-keyword'
+ when :on_ivar then 'ruby-ivar'
+ when :on_cvar then 'ruby-identifier'
+ when :on_gvar then 'ruby-identifier'
+ when '=' != t[:text] && :on_op
+ then 'ruby-operator'
+ when :on_tlambda then 'ruby-operator'
+ when :on_ident then 'ruby-identifier'
+ when :on_label then 'ruby-value'
+ when :on_backref, :on_dstring
+ then 'ruby-node'
+ when :on_comment then 'ruby-comment'
+ when :on_embdoc then 'ruby-comment'
+ when :on_regexp then 'ruby-regexp'
+ when :on_tstring then 'ruby-string'
+ when :on_int, :on_float,
+ :on_rational, :on_imaginary,
+ :on_heredoc,
+ :on_symbol, :on_CHAR then 'ruby-value'
+ when :on_heredoc_beg, :on_heredoc_end
+ then 'ruby-identifier'
end
- text = CGI.escapeHTML t.text
+ comment_with_nl = false
+ if :on_comment == t[:kind] or :on_embdoc == t[:kind] or :on_heredoc_end == t[:kind]
+ comment_with_nl = true if "\n" == t[:text][-1]
+ text = t[:text].rstrip
+ else
+ text = t[:text]
+ end
+
+ if :on_ident == t[:kind] && starting_title
+ starting_title = false
+ style = 'ruby-identifier ruby-title'
+ end
+
+ if :on_kw == t[:kind] and 'def' == t[:text]
+ starting_title = true
+ end
+
+ text = CGI.escapeHTML text
if style then
- "<span class=\"#{style}\">#{text}</span>"
+ "<span class=\"#{style}\">#{text}</span>#{"\n" if comment_with_nl}"
else
text
end
@@ -89,7 +107,7 @@ module RDoc::TokenStream
# Returns a string representation of the token stream
def tokens_to_s
- token_stream.compact.map { |token| token.text }.join ''
+ token_stream.compact.map { |token| token[:text] }.join ''
end
end
diff --git a/lib/rdoc/tom_doc.rb b/lib/rdoc/tom_doc.rb
index d760849938..625a6b5cfa 100644
--- a/lib/rdoc/tom_doc.rb
+++ b/lib/rdoc/tom_doc.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# :markup: tomdoc
# A parser for TomDoc based on TomDoc 1.0.0-rc1 (02adef9b5a)
@@ -180,12 +180,19 @@ class RDoc::TomDoc < RDoc::Markup::Parser
case type
when :TEXT then
- @section = 'Returns' if data =~ /\AReturns/
+ @section = 'Returns' if data =~ /\A(Returns|Raises)/
paragraph << data
when :NEWLINE then
if :TEXT == peek_token[0] then
- paragraph << ' '
+ # Lines beginning with 'Raises' in the Returns section should not be
+ # treated as multiline text
+ if 'Returns' == @section and
+ peek_token[1].start_with?('Raises') then
+ break
+ else
+ paragraph << ' '
+ end
else
break
end
@@ -222,7 +229,7 @@ class RDoc::TomDoc < RDoc::Markup::Parser
# Returns self.
def tokenize text
- text.sub!(/\A(Public|Internal|Deprecated):\s+/, '')
+ text = text.sub(/\A(Public|Internal|Deprecated):\s+/, '')
setup_scanner text
@@ -255,4 +262,3 @@ class RDoc::TomDoc < RDoc::Markup::Parser
end
end
-
diff --git a/lib/rdoc/top_level.rb b/lib/rdoc/top_level.rb
index 38be646ad0..b8b6110bb2 100644
--- a/lib/rdoc/top_level.rb
+++ b/lib/rdoc/top_level.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
##
# A TopLevel context is a representation of the contents of a single file
@@ -33,7 +33,7 @@ class RDoc::TopLevel < RDoc::Context
##
# The parser class that processed this file
- attr_accessor :parser
+ attr_reader :parser
##
# Creates a new TopLevel for the file at +absolute_name+. If documentation
@@ -52,6 +52,12 @@ class RDoc::TopLevel < RDoc::Context
@classes_or_modules = []
end
+ def parser=(val)
+ @parser = val
+ @store.update_parser_of_file(absolute_name, val) if @store
+ @parser
+ end
+
##
# An RDoc::TopLevel is equal to another with the same relative_name
@@ -272,7 +278,7 @@ class RDoc::TopLevel < RDoc::Context
# Is this TopLevel from a text file instead of a source code file?
def text?
- @parser and @parser.ancestors.include? RDoc::Parser::Text
+ @parser and @parser.include? RDoc::Parser::Text
end
def to_s # :nodoc:
diff --git a/lib/rdoc/version.rb b/lib/rdoc/version.rb
new file mode 100644
index 0000000000..dc1ea6cbb1
--- /dev/null
+++ b/lib/rdoc/version.rb
@@ -0,0 +1,8 @@
+module RDoc
+
+ ##
+ # RDoc version you are using
+
+ VERSION = '6.1.2.1'
+
+end
diff --git a/lib/resolv.rb b/lib/resolv.rb
index bff673c990..02c2dedc49 100644
--- a/lib/resolv.rb
+++ b/lib/resolv.rb
@@ -2,7 +2,6 @@
require 'socket'
require 'timeout'
-require 'thread'
require 'io/wait'
begin
@@ -25,7 +24,7 @@ end
#
# Resolv::DNS.open do |dns|
# ress = dns.getresources "www.ruby-lang.org", Resolv::DNS::Resource::IN::A
-# p ress.map { |r| r.address }
+# p ress.map(&:address)
# ress = dns.getresources "ruby-lang.org", Resolv::DNS::Resource::IN::MX
# p ress.map { |r| [r.exchange.to_s, r.preference] }
# end
@@ -170,7 +169,7 @@ class Resolv
begin
raise LoadError unless /mswin|mingw|cygwin/ =~ RUBY_PLATFORM
require 'win32/resolv'
- DefaultFileName = Win32::Resolv.get_hosts_path
+ DefaultFileName = Win32::Resolv.get_hosts_path || IO::NULL
rescue LoadError
DefaultFileName = '/etc/hosts'
end
@@ -189,7 +188,7 @@ class Resolv
unless @initialized
@name2addr = {}
@addr2name = {}
- open(@filename, 'rb') {|f|
+ File.open(@filename, 'rb') {|f|
f.each {|line|
line.sub!(/#.*/, '')
addr, hostname, *aliases = line.split(/\s+/)
@@ -237,9 +236,7 @@ class Resolv
def each_address(name, &proc)
lazy_initialize
- if @name2addr.include?(name)
- @name2addr[name].each(&proc)
- end
+ @name2addr[name]&.each(&proc)
end
##
@@ -264,9 +261,7 @@ class Resolv
def each_name(address, &proc)
lazy_initialize
- if @addr2name.include?(address)
- @addr2name[address].each(&proc)
- end
+ @addr2name[address]&.each(&proc)
end
end
@@ -614,16 +609,6 @@ class Resolv
end
end
-
- def self.rangerand(range) # :nodoc:
- base = range.begin
- len = range.end - range.begin
- if !range.exclude_end?
- len += 1
- end
- base + random(len)
- end
-
RequestID = {} # :nodoc:
RequestIDMutex = Thread::Mutex.new # :nodoc:
@@ -632,7 +617,7 @@ class Resolv
RequestIDMutex.synchronize {
h = (RequestID[[host, port]] ||= {})
begin
- id = rangerand(0x0000..0xffff)
+ id = random(0x0000..0xffff)
end while h[id]
h[id] = true
}
@@ -653,7 +638,7 @@ class Resolv
def self.bind_random_port(udpsock, bind_host="0.0.0.0") # :nodoc:
begin
- port = rangerand(1024..65535)
+ port = random(1024..65535)
udpsock.bind(bind_host, port)
rescue Errno::EADDRINUSE, # POSIX
Errno::EACCES, # SunOS: See PRIV_SYS_NFS in privileges(5)
@@ -706,13 +691,13 @@ class Resolv
rescue DecodeError
next # broken DNS message ignored
end
- if s = sender_for(from, msg)
+ if sender == sender_for(from, msg)
break
else
# unexpected DNS message ignored
end
end
- return msg, s.data
+ return msg, sender.data
end
def sender_for(addr, msg)
@@ -722,9 +707,7 @@ class Resolv
def close
socks = @socks
@socks = nil
- if socks
- socks.each {|sock| sock.close }
- end
+ socks&.each(&:close)
end
class Sender # :nodoc:
@@ -739,35 +722,47 @@ class Resolv
def initialize(*nameserver_port)
super()
@nameserver_port = nameserver_port
- @socks_hash = {}
- @socks = []
- nameserver_port.each {|host, port|
- if host.index(':')
- bind_host = "::"
- af = Socket::AF_INET6
- else
- bind_host = "0.0.0.0"
- af = Socket::AF_INET
- end
- next if @socks_hash[bind_host]
- begin
- sock = UDPSocket.new(af)
- rescue Errno::EAFNOSUPPORT
- next # The kernel doesn't support the address family.
- end
- sock.do_not_reverse_lookup = true
- DNS.bind_random_port(sock, bind_host)
- @socks << sock
- @socks_hash[bind_host] = sock
+ @initialized = false
+ @mutex = Thread::Mutex.new
+ end
+
+ def lazy_initialize
+ @mutex.synchronize {
+ next if @initialized
+ @initialized = true
+ @socks_hash = {}
+ @socks = []
+ @nameserver_port.each {|host, port|
+ if host.index(':')
+ bind_host = "::"
+ af = Socket::AF_INET6
+ else
+ bind_host = "0.0.0.0"
+ af = Socket::AF_INET
+ end
+ next if @socks_hash[bind_host]
+ begin
+ sock = UDPSocket.new(af)
+ rescue Errno::EAFNOSUPPORT
+ next # The kernel doesn't support the address family.
+ end
+ @socks << sock
+ @socks_hash[bind_host] = sock
+ sock.do_not_reverse_lookup = true
+ DNS.bind_random_port(sock, bind_host)
+ }
}
+ self
end
def recv_reply(readable_socks)
+ lazy_initialize
reply, from = readable_socks[0].recvfrom(UDPSize)
return reply, [from[3],from[1]]
end
def sender(msg, data, host, port=Port)
+ lazy_initialize
sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"]
return nil if !sock
service = [host, port]
@@ -779,9 +774,14 @@ class Resolv
end
def close
- super
- @senders.each_key {|service, id|
- DNS.free_request_id(service[0], service[1], id)
+ @mutex.synchronize {
+ if @initialized
+ super
+ @senders.each_key {|service, id|
+ DNS.free_request_id(service[0], service[1], id)
+ }
+ @initialized = false
+ end
}
end
@@ -805,20 +805,32 @@ class Resolv
super()
@host = host
@port = port
- is_ipv6 = host.index(':')
- sock = UDPSocket.new(is_ipv6 ? Socket::AF_INET6 : Socket::AF_INET)
- @socks = [sock]
- sock.do_not_reverse_lookup = true
- DNS.bind_random_port(sock, is_ipv6 ? "::" : "0.0.0.0")
- sock.connect(host, port)
+ @mutex = Thread::Mutex.new
+ @initialized = false
+ end
+
+ def lazy_initialize
+ @mutex.synchronize {
+ next if @initialized
+ @initialized = true
+ is_ipv6 = @host.index(':')
+ sock = UDPSocket.new(is_ipv6 ? Socket::AF_INET6 : Socket::AF_INET)
+ @socks = [sock]
+ sock.do_not_reverse_lookup = true
+ DNS.bind_random_port(sock, is_ipv6 ? "::" : "0.0.0.0")
+ sock.connect(@host, @port)
+ }
+ self
end
def recv_reply(readable_socks)
+ lazy_initialize
reply = readable_socks[0].recv(UDPSize)
return reply, nil
end
def sender(msg, data, host=@host, port=@port)
+ lazy_initialize
unless host == @host && port == @port
raise RequestError.new("host/port don't match: #{host}:#{port}")
end
@@ -829,10 +841,15 @@ class Resolv
end
def close
- super
- @senders.each_key {|from, id|
- DNS.free_request_id(@host, @port, id)
- }
+ @mutex.synchronize do
+ if @initialized
+ super
+ @senders.each_key {|from, id|
+ DNS.free_request_id(@host, @port, id)
+ }
+ @initialized = false
+ end
+ end
end
class Sender < Requester::Sender # :nodoc:
@@ -846,6 +863,7 @@ class Resolv
class MDNSOneShot < UnconnectedUDP # :nodoc:
def sender(msg, data, host, port=Port)
+ lazy_initialize
id = DNS.allocate_request_id(host, port)
request = msg.encode
request[0,2] = [id].pack('n')
@@ -855,6 +873,7 @@ class Resolv
end
def sender_for(addr, msg)
+ lazy_initialize
@senders[msg.id]
end
end
@@ -933,13 +952,11 @@ class Resolv
nameserver = []
search = nil
ndots = 1
- open(filename, 'rb') {|f|
+ File.open(filename, 'rb') {|f|
f.each {|line|
line.sub!(/[#;].*/, '')
keyword, *args = line.split(/\s+/)
- args.each { |arg|
- arg.untaint
- }
+ args.each(&:untaint)
next unless keyword
case keyword
when 'nameserver'
@@ -1527,12 +1544,12 @@ class Resolv
def initialize(data)
@data = data
@index = 0
- @limit = data.length
+ @limit = data.bytesize
yield self
end
def inspect
- "\#<#{self.class}: #{@data[0, @index].inspect} #{@data[@index..-1].inspect}>"
+ "\#<#{self.class}: #{@data.byteslice(0, @index).inspect} #{@data.byteslice(@index..-1).inspect}>"
end
def get_length16
@@ -1551,7 +1568,7 @@ class Resolv
def get_bytes(len = @limit - @index)
raise DecodeError.new("limit exceeded") if @limit < @index + len
- d = @data[@index, len]
+ d = @data.byteslice(@index, len)
@index += len
return d
end
@@ -1579,9 +1596,9 @@ class Resolv
def get_string
raise DecodeError.new("limit exceeded") if @limit <= @index
- len = @data[@index].ord
+ len = @data.getbyte(@index)
raise DecodeError.new("limit exceeded") if @limit < @index + 1 + len
- d = @data[@index + 1, len]
+ d = @data.byteslice(@index + 1, len)
@index += 1 + len
return d
end
@@ -1604,7 +1621,7 @@ class Resolv
d = []
while true
raise DecodeError.new("limit exceeded") if @limit <= @index
- case @data[@index].ord
+ case @data.getbyte(@index)
when 0
@index += 1
if save_index
@@ -1641,7 +1658,13 @@ class Resolv
name = self.get_name
type, klass, ttl = self.get_unpack('nnN')
typeclass = Resource.get_class(type, klass)
- res = self.get_length16 { typeclass.decode_rdata self }
+ res = self.get_length16 do
+ begin
+ typeclass.decode_rdata self
+ rescue => e
+ raise DecodeError, e.message, e.backtrace
+ end
+ end
res.instance_variable_set :@ttl, ttl
return name, ttl, res
end
@@ -2604,7 +2627,7 @@ class Resolv
def each_address(name)
name = Resolv::DNS::Name.create(name)
- return unless name.to_a.last == 'local'
+ return unless name[-1].to_s == 'local'
super(name)
end
diff --git a/lib/rexml/attlistdecl.rb b/lib/rexml/attlistdecl.rb
index dc1d2add0b..44a91d66d6 100644
--- a/lib/rexml/attlistdecl.rb
+++ b/lib/rexml/attlistdecl.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
#vim:ts=2 sw=2 noexpandtab:
-require 'rexml/child'
-require 'rexml/source'
+require_relative 'child'
+require_relative 'source'
module REXML
# This class needs:
diff --git a/lib/rexml/attribute.rb b/lib/rexml/attribute.rb
index ca5984e178..4ae8b10062 100644
--- a/lib/rexml/attribute.rb
+++ b/lib/rexml/attribute.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
-require "rexml/namespace"
-require 'rexml/text'
+require_relative "namespace"
+require_relative 'text'
module REXML
# Defines an Element Attribute; IE, a attribute=value pair, as in:
diff --git a/lib/rexml/cdata.rb b/lib/rexml/cdata.rb
index fe9b49b5f7..997f5a08db 100644
--- a/lib/rexml/cdata.rb
+++ b/lib/rexml/cdata.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rexml/text"
+require_relative "text"
module REXML
class CData < Text
@@ -58,7 +58,7 @@ module REXML
# c = CData.new( " Some text " )
# c.write( $stdout ) #-> <![CDATA[ Some text ]]>
def write( output=$stdout, indent=-1, transitive=false, ie_hack=false )
- Kernel.warn( "#{self.class.name}.write is deprecated" )
+ Kernel.warn( "#{self.class.name}.write is deprecated", uplevel: 1)
indent( output, indent )
output << START
output << @string
diff --git a/lib/rexml/child.rb b/lib/rexml/child.rb
index d23451e71e..cc6e9a4719 100644
--- a/lib/rexml/child.rb
+++ b/lib/rexml/child.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rexml/node"
+require_relative "node"
module REXML
##
diff --git a/lib/rexml/comment.rb b/lib/rexml/comment.rb
index 746af77296..52c58b46f6 100644
--- a/lib/rexml/comment.rb
+++ b/lib/rexml/comment.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rexml/child"
+require_relative "child"
module REXML
##
@@ -48,7 +48,7 @@ module REXML
# ie_hack::
# Needed for conformity to the child API, but not used by this class.
def write( output, indent=-1, transitive=false, ie_hack=false )
- Kernel.warn("Comment.write is deprecated. See REXML::Formatters")
+ Kernel.warn("Comment.write is deprecated. See REXML::Formatters", uplevel: 1)
indent( output, indent )
output << START
output << @string
diff --git a/lib/rexml/doctype.rb b/lib/rexml/doctype.rb
index 1eb1f5b4e1..a4e91529ac 100644
--- a/lib/rexml/doctype.rb
+++ b/lib/rexml/doctype.rb
@@ -1,12 +1,50 @@
# frozen_string_literal: false
-require "rexml/parent"
-require "rexml/parseexception"
-require "rexml/namespace"
-require 'rexml/entity'
-require 'rexml/attlistdecl'
-require 'rexml/xmltokens'
+require_relative "parent"
+require_relative "parseexception"
+require_relative "namespace"
+require_relative 'entity'
+require_relative 'attlistdecl'
+require_relative 'xmltokens'
module REXML
+ class ReferenceWriter
+ def initialize(id_type,
+ public_id_literal,
+ system_literal,
+ context=nil)
+ @id_type = id_type
+ @public_id_literal = public_id_literal
+ @system_literal = system_literal
+ if context and context[:prologue_quote] == :apostrophe
+ @default_quote = "'"
+ else
+ @default_quote = "\""
+ end
+ end
+
+ def write(output)
+ output << " #{@id_type}"
+ if @public_id_literal
+ if @public_id_literal.include?("'")
+ quote = "\""
+ else
+ quote = @default_quote
+ end
+ output << " #{quote}#{@public_id_literal}#{quote}"
+ end
+ if @system_literal
+ if @system_literal.include?("'")
+ quote = "\""
+ elsif @system_literal.include?("\"")
+ quote = "'"
+ else
+ quote = @default_quote
+ end
+ output << " #{quote}#{@system_literal}#{quote}"
+ end
+ end
+ end
+
# Represents an XML DOCTYPE declaration; that is, the contents of <!DOCTYPE
# ... >. DOCTYPES can be used to declare the DTD of a document, as well as
# being used to declare entities used in the document.
@@ -50,6 +88,8 @@ module REXML
super( parent )
@name = first.name
@external_id = first.external_id
+ @long_name = first.instance_variable_get(:@long_name)
+ @uri = first.instance_variable_get(:@uri)
elsif first.kind_of? Array
super( parent )
@name = first[0]
@@ -112,9 +152,13 @@ module REXML
output << START
output << ' '
output << @name
- output << " #@external_id" if @external_id
- output << " #{@long_name.inspect}" if @long_name
- output << " #{@uri.inspect}" if @uri
+ if @external_id
+ reference_writer = ReferenceWriter.new(@external_id,
+ @long_name,
+ @uri,
+ context)
+ reference_writer.write(output)
+ end
unless @children.empty?
output << ' ['
@children.each { |child|
@@ -127,7 +171,11 @@ module REXML
end
def context
- @parent.context
+ if @parent
+ @parent.context
+ else
+ nil
+ end
end
def entity( name )
@@ -249,9 +297,11 @@ module REXML
end
def to_s
- notation = "<!NOTATION #{@name} #{@middle}"
- notation << " #{@public.inspect}" if @public
- notation << " #{@system.inspect}" if @system
+ context = nil
+ context = parent.context if parent
+ notation = "<!NOTATION #{@name}"
+ reference_writer = ReferenceWriter.new(@middle, @public, @system, context)
+ reference_writer.write(notation)
notation << ">"
notation
end
diff --git a/lib/rexml/document.rb b/lib/rexml/document.rb
index 806bc499cd..adec293066 100644
--- a/lib/rexml/document.rb
+++ b/lib/rexml/document.rb
@@ -1,17 +1,17 @@
# frozen_string_literal: false
-require "rexml/security"
-require "rexml/element"
-require "rexml/xmldecl"
-require "rexml/source"
-require "rexml/comment"
-require "rexml/doctype"
-require "rexml/instruction"
-require "rexml/rexml"
-require "rexml/parseexception"
-require "rexml/output"
-require "rexml/parsers/baseparser"
-require "rexml/parsers/streamparser"
-require "rexml/parsers/treeparser"
+require_relative "security"
+require_relative "element"
+require_relative "xmldecl"
+require_relative "source"
+require_relative "comment"
+require_relative "doctype"
+require_relative "instruction"
+require_relative "rexml"
+require_relative "parseexception"
+require_relative "output"
+require_relative "parsers/baseparser"
+require_relative "parsers/streamparser"
+require_relative "parsers/treeparser"
module REXML
# Represents a full XML document, including PIs, a doctype, etc. A
@@ -226,7 +226,7 @@ module REXML
end
formatter = if indent > -1
if transitive
- require "rexml/formatters/transitive"
+ require_relative "formatters/transitive"
REXML::Formatters::Transitive.new( indent, ie_hack )
else
REXML::Formatters::Pretty.new( indent, ie_hack )
diff --git a/lib/rexml/dtd/attlistdecl.rb b/lib/rexml/dtd/attlistdecl.rb
index 32847daadb..1326cb21e4 100644
--- a/lib/rexml/dtd/attlistdecl.rb
+++ b/lib/rexml/dtd/attlistdecl.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rexml/child"
+require_relative "../child"
module REXML
module DTD
class AttlistDecl < Child
diff --git a/lib/rexml/dtd/dtd.rb b/lib/rexml/dtd/dtd.rb
index 927d5d847b..8b0f2d753a 100644
--- a/lib/rexml/dtd/dtd.rb
+++ b/lib/rexml/dtd/dtd.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: false
-require "rexml/dtd/elementdecl"
-require "rexml/dtd/entitydecl"
-require "rexml/comment"
-require "rexml/dtd/notationdecl"
-require "rexml/dtd/attlistdecl"
-require "rexml/parent"
+require_relative "elementdecl"
+require_relative "entitydecl"
+require_relative "../comment"
+require_relative "notationdecl"
+require_relative "attlistdecl"
+require_relative "../parent"
module REXML
module DTD
diff --git a/lib/rexml/dtd/elementdecl.rb b/lib/rexml/dtd/elementdecl.rb
index 119fd41a8f..20ed023244 100644
--- a/lib/rexml/dtd/elementdecl.rb
+++ b/lib/rexml/dtd/elementdecl.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rexml/child"
+require_relative "../child"
module REXML
module DTD
class ElementDecl < Child
diff --git a/lib/rexml/dtd/entitydecl.rb b/lib/rexml/dtd/entitydecl.rb
index 45707e2f42..312df655ff 100644
--- a/lib/rexml/dtd/entitydecl.rb
+++ b/lib/rexml/dtd/entitydecl.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rexml/child"
+require_relative "../child"
module REXML
module DTD
class EntityDecl < Child
diff --git a/lib/rexml/dtd/notationdecl.rb b/lib/rexml/dtd/notationdecl.rb
index cfdf0b9b74..04a9b08aa7 100644
--- a/lib/rexml/dtd/notationdecl.rb
+++ b/lib/rexml/dtd/notationdecl.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rexml/child"
+require_relative "../child"
module REXML
module DTD
class NotationDecl < Child
diff --git a/lib/rexml/element.rb b/lib/rexml/element.rb
index f725d5a2be..7903d83453 100644
--- a/lib/rexml/element.rb
+++ b/lib/rexml/element.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: false
-require "rexml/parent"
-require "rexml/namespace"
-require "rexml/attribute"
-require "rexml/cdata"
-require "rexml/xpath"
-require "rexml/parseexception"
+require_relative "parent"
+require_relative "namespace"
+require_relative "attribute"
+require_relative "cdata"
+require_relative "xpath"
+require_relative "parseexception"
module REXML
# An implementation note about namespaces:
@@ -551,6 +551,30 @@ module REXML
# Attributes #
#################################################
+ # Fetches an attribute value or a child.
+ #
+ # If String or Symbol is specified, it's treated as attribute
+ # name. Attribute value as String or +nil+ is returned. This case
+ # is shortcut of +attributes[name]+.
+ #
+ # If Integer is specified, it's treated as the index of
+ # child. It returns Nth child.
+ #
+ # doc = REXML::Document.new("<a attr='1'><b/><c/></a>")
+ # doc.root["attr"] # => "1"
+ # doc.root.attributes["attr"] # => "1"
+ # doc.root[1] # => <c/>
+ def [](name_or_index)
+ case name_or_index
+ when String
+ attributes[name_or_index]
+ when Symbol
+ attributes[name_or_index.to_s]
+ else
+ super
+ end
+ end
+
def attribute( name, namespace=nil )
prefix = nil
if namespaces.respond_to? :key
@@ -686,10 +710,10 @@ module REXML
# doc.write( out ) #-> doc is written to the string 'out'
# doc.write( $stdout ) #-> doc written to the console
def write(output=$stdout, indent=-1, transitive=false, ie_hack=false)
- Kernel.warn("#{self.class.name}.write is deprecated. See REXML::Formatters")
+ Kernel.warn("#{self.class.name}.write is deprecated. See REXML::Formatters", uplevel: 1)
formatter = if indent > -1
if transitive
- require "rexml/formatters/transitive"
+ require_relative "formatters/transitive"
REXML::Formatters::Transitive.new( indent, ie_hack )
else
REXML::Formatters::Pretty.new( indent, ie_hack )
@@ -1009,6 +1033,7 @@ module REXML
# p attr.expanded_name+" => "+attr.value
# }
def each_attribute # :yields: attribute
+ return to_enum(__method__) unless block_given?
each_value do |val|
if val.kind_of? Attribute
yield val
@@ -1024,6 +1049,7 @@ module REXML
# doc = Document.new '<a x="1" y="2"/>'
# doc.root.attributes.each {|name, value| p name+" => "+value }
def each
+ return to_enum(__method__) unless block_given?
each_attribute do |attr|
yield [attr.expanded_name, attr.value]
end
diff --git a/lib/rexml/entity.rb b/lib/rexml/entity.rb
index d9a72cc8fa..d6fd5edd0d 100644
--- a/lib/rexml/entity.rb
+++ b/lib/rexml/entity.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: false
-require 'rexml/child'
-require 'rexml/source'
-require 'rexml/xmltokens'
+require_relative 'child'
+require_relative 'source'
+require_relative 'xmltokens'
module REXML
- # God, I hate DTDs. I really do. Why this idiot standard still
- # plagues us is beyond me.
class Entity < Child
include XMLTokens
PUBIDCHAR = "\x20\x0D\x0Aa-zA-Z0-9\\-()+,./:=?;!*@$_%#"
@@ -29,8 +27,7 @@ module REXML
# the constructor with the entity definition, or use the accessor methods.
# +WARNING+: There is no validation of entity state except when the entity
# is read from a stream. If you start poking around with the accessors,
- # you can easily create a non-conformant Entity. The best thing to do is
- # dump the stupid DTDs and use XMLSchema instead.
+ # you can easily create a non-conformant Entity.
#
# e = Entity.new( 'amp', '&' )
def initialize stream, value=nil, parent=nil, reference=false
diff --git a/lib/rexml/formatters/default.rb b/lib/rexml/formatters/default.rb
index b84759d2ff..811b2ff3d5 100644
--- a/lib/rexml/formatters/default.rb
+++ b/lib/rexml/formatters/default.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: false
+
module REXML
module Formatters
class Default
- # Prints out the XML document with no formatting -- except if id_hack is
+ # Prints out the XML document with no formatting -- except if ie_hack is
# set.
#
# ie_hack::
@@ -101,11 +102,14 @@ module REXML
end
def write_instruction( node, output )
- output << Instruction::START.sub(/\\/u, '')
+ output << Instruction::START
output << node.target
- output << ' '
- output << node.content
- output << Instruction::STOP.sub(/\\/u, '')
+ content = node.content
+ if content
+ output << ' '
+ output << content
+ end
+ output << Instruction::STOP
end
end
end
diff --git a/lib/rexml/formatters/pretty.rb b/lib/rexml/formatters/pretty.rb
index a80274bdad..562ef9462e 100644
--- a/lib/rexml/formatters/pretty.rb
+++ b/lib/rexml/formatters/pretty.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require 'rexml/formatters/default'
+require_relative 'default'
module REXML
module Formatters
diff --git a/lib/rexml/formatters/transitive.rb b/lib/rexml/formatters/transitive.rb
index 81e67f3274..5ff51e10f3 100644
--- a/lib/rexml/formatters/transitive.rb
+++ b/lib/rexml/formatters/transitive.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require 'rexml/formatters/pretty'
+require_relative 'pretty'
module REXML
module Formatters
diff --git a/lib/rexml/functions.rb b/lib/rexml/functions.rb
index b56103d4f2..219f9c8db5 100644
--- a/lib/rexml/functions.rb
+++ b/lib/rexml/functions.rb
@@ -8,10 +8,28 @@ module REXML
# Therefore, in XML, "local-name()" is identical (and actually becomes)
# "local_name()"
module Functions
+ @@available_functions = {}
@@context = nil
@@namespace_context = {}
@@variables = {}
+ INTERNAL_METHODS = [
+ :namespace_context,
+ :namespace_context=,
+ :variables,
+ :variables=,
+ :context=,
+ :get_namespace,
+ :send,
+ ]
+ class << self
+ def singleton_method_added(name)
+ unless INTERNAL_METHODS.include?(name)
+ @@available_functions[name] = true
+ end
+ end
+ end
+
def Functions::namespace_context=(x) ; @@namespace_context=x ; end
def Functions::variables=(x) ; @@variables=x ; end
def Functions::namespace_context ; @@namespace_context ; end
@@ -68,10 +86,14 @@ module REXML
# Helper method.
def Functions::get_namespace( node_set = nil )
if node_set == nil
- yield @@context[:node] if defined? @@context[:node].namespace
+ yield @@context[:node] if @@context[:node].respond_to?(:namespace)
else
if node_set.respond_to? :each
- node_set.each { |node| yield node if defined? node.namespace }
+ result = []
+ node_set.each do |node|
+ result << yield(node) if node.respond_to?(:namespace)
+ end
+ result
elsif node_set.respond_to? :namespace
yield node_set
end
@@ -114,21 +136,40 @@ module REXML
# An object of a type other than the four basic types is converted to a
# string in a way that is dependent on that type.
def Functions::string( object=nil )
- #object = @context unless object
- if object.instance_of? Array
- string( object[0] )
- elsif defined? object.node_type
- if object.node_type == :attribute
+ object = @@context[:node] if object.nil?
+ if object.respond_to?(:node_type)
+ case object.node_type
+ when :attribute
object.value
- elsif object.node_type == :element || object.node_type == :document
+ when :element
string_value(object)
+ when :document
+ string_value(object.root)
+ when :processing_instruction
+ object.content
else
object.to_s
end
- elsif object.nil?
- return ""
else
- object.to_s
+ case object
+ when Array
+ string(object[0])
+ when Float
+ if object.nan?
+ "NaN"
+ else
+ integer = object.to_i
+ if object == integer
+ "%d" % integer
+ else
+ object.to_s
+ end
+ end
+ when nil
+ ""
+ else
+ object.to_s
+ end
end
end
@@ -149,9 +190,12 @@ module REXML
rv
end
- # UNTESTED
def Functions::concat( *objects )
- objects.join
+ concatenated = ""
+ objects.each do |object|
+ concatenated << string(object)
+ end
+ concatenated
end
# Fixed by Mike Stok
@@ -379,7 +423,7 @@ module REXML
number = number(number)
begin
neg = number.negative?
- number = number.abs.round(half: :up)
+ number = number.abs.round
neg ? -number : number
rescue FloatDomainError
number
@@ -390,9 +434,14 @@ module REXML
node.node_type == :processing_instruction
end
- def Functions::method_missing( id )
- puts "METHOD MISSING #{id.id2name}"
- XPath.match( @@context[:node], id.id2name )
+ def Functions::send(name, *args)
+ if @@available_functions[name.to_sym]
+ super
+ else
+ # TODO: Maybe, this is not XPath spec behavior.
+ # This behavior must be reconsidered.
+ XPath.match(@@context[:node], name.to_s)
+ end
end
end
end
diff --git a/lib/rexml/instruction.rb b/lib/rexml/instruction.rb
index 576939ca2b..318741f03b 100644
--- a/lib/rexml/instruction.rb
+++ b/lib/rexml/instruction.rb
@@ -1,13 +1,14 @@
# frozen_string_literal: false
-require "rexml/child"
-require "rexml/source"
+
+require_relative "child"
+require_relative "source"
module REXML
# Represents an XML Instruction; IE, <? ... ?>
# TODO: Add parent arg (3rd arg) to constructor
class Instruction < Child
- START = '<\?'
- STOP = '\?>'
+ START = "<?"
+ STOP = "?>"
# target is the "name" of the Instruction; IE, the "tag" in <?tag ...?>
# content is everything else.
@@ -17,20 +18,25 @@ module REXML
# @param target can be one of a number of things. If String, then
# the target of this instruction is set to this. If an Instruction,
# then the Instruction is shallowly cloned (target and content are
- # copied). If a Source, then the source is scanned and parsed for
- # an Instruction declaration.
+ # copied).
# @param content Must be either a String, or a Parent. Can only
# be a Parent if the target argument is a Source. Otherwise, this
# String is set as the content of this instruction.
def initialize(target, content=nil)
- if target.kind_of? String
+ case target
+ when String
super()
@target = target
@content = content
- elsif target.kind_of? Instruction
+ when Instruction
super(content)
@target = target.target
@content = target.content
+ else
+ message =
+ "processing instruction target must be String or REXML::Instruction: "
+ message << "<#{target.inspect}>"
+ raise ArgumentError, message
end
@content.strip! if @content
end
@@ -43,13 +49,15 @@ module REXML
# See the rexml/formatters package
#
def write writer, indent=-1, transitive=false, ie_hack=false
- Kernel.warn( "#{self.class.name}.write is deprecated" )
+ Kernel.warn( "#{self.class.name}.write is deprecated", uplevel: 1)
indent(writer, indent)
- writer << START.sub(/\\/u, '')
+ writer << START
writer << @target
- writer << ' '
- writer << @content
- writer << STOP.sub(/\\/u, '')
+ if @content
+ writer << ' '
+ writer << @content
+ end
+ writer << STOP
end
# @return true if other is an Instruction, and the content and target
diff --git a/lib/rexml/light/node.rb b/lib/rexml/light/node.rb
index d58119a3a4..01177c64d2 100644
--- a/lib/rexml/light/node.rb
+++ b/lib/rexml/light/node.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require 'rexml/xmltokens'
+require_relative '../xmltokens'
# [ :element, parent, name, attributes, children* ]
# a = Node.new
diff --git a/lib/rexml/namespace.rb b/lib/rexml/namespace.rb
index 90ba7cc635..924edf9506 100644
--- a/lib/rexml/namespace.rb
+++ b/lib/rexml/namespace.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: false
-require 'rexml/xmltokens'
+
+require_relative 'xmltokens'
module REXML
# Adds named attributes to an object.
@@ -14,14 +15,24 @@ module REXML
# Sets the name and the expanded name
def name=( name )
@expanded_name = name
- name =~ NAMESPLIT
- if $1
- @prefix = $1
+ case name
+ when NAMESPLIT
+ if $1
+ @prefix = $1
+ else
+ @prefix = ""
+ @namespace = ""
+ end
+ @name = $2
+ when ""
+ @prefix = nil
+ @namespace = nil
+ @name = nil
else
- @prefix = ""
- @namespace = ""
+ message = "name must be \#{PREFIX}:\#{LOCAL_NAME} or \#{LOCAL_NAME}: "
+ message += "<#{name.inspect}>"
+ raise ArgumentError, message
end
- @name = $2
end
# Compares names optionally WITH namespaces
diff --git a/lib/rexml/node.rb b/lib/rexml/node.rb
index c7a3936799..081caba6cb 100644
--- a/lib/rexml/node.rb
+++ b/lib/rexml/node.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
-require "rexml/parseexception"
-require "rexml/formatters/pretty"
-require "rexml/formatters/default"
+require_relative "parseexception"
+require_relative "formatters/pretty"
+require_relative "formatters/default"
module REXML
# Represents a node in the tree. Nodes are never encountered except as
@@ -26,7 +26,7 @@ module REXML
# REXML::Formatters package for changing the output style.
def to_s indent=nil
unless indent.nil?
- Kernel.warn( "#{self.class.name}.to_s(indent) parameter is deprecated" )
+ Kernel.warn( "#{self.class.name}.to_s(indent) parameter is deprecated", uplevel: 1)
f = REXML::Formatters::Pretty.new( indent )
f.write( self, rv = "" )
else
diff --git a/lib/rexml/output.rb b/lib/rexml/output.rb
index 96dfea570e..88a5fb378d 100644
--- a/lib/rexml/output.rb
+++ b/lib/rexml/output.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require 'rexml/encoding'
+require_relative 'encoding'
module REXML
class Output
diff --git a/lib/rexml/parent.rb b/lib/rexml/parent.rb
index 3bd0a96255..6a53b37a12 100644
--- a/lib/rexml/parent.rb
+++ b/lib/rexml/parent.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rexml/child"
+require_relative "child"
module REXML
# A parent has children, and has methods for accessing them. The Parent
diff --git a/lib/rexml/parsers/baseparser.rb b/lib/rexml/parsers/baseparser.rb
index 80eeb0fa79..b10cdcd30e 100644
--- a/lib/rexml/parsers/baseparser.rb
+++ b/lib/rexml/parsers/baseparser.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: false
-require 'rexml/parseexception'
-require 'rexml/undefinednamespaceexception'
-require 'rexml/source'
+require_relative '../parseexception'
+require_relative '../undefinednamespaceexception'
+require_relative '../source'
require 'set'
+require "strscan"
module REXML
module Parsers
@@ -32,8 +33,12 @@ module REXML
COMBININGCHAR = '' # TODO
EXTENDER = '' # TODO
- NCNAME_STR= "[#{LETTER}_:][-[:alnum:]._:#{COMBININGCHAR}#{EXTENDER}]*"
- NAME_STR= "(?:(#{NCNAME_STR}):)?(#{NCNAME_STR})"
+ NCNAME_STR= "[#{LETTER}_][-[:alnum:]._#{COMBININGCHAR}#{EXTENDER}]*"
+ QNAME_STR= "(?:(#{NCNAME_STR}):)?(#{NCNAME_STR})"
+ QNAME = /(#{QNAME_STR})/
+
+ # Just for backward compatibility. For example, kramdown uses this.
+ # It's not used in REXML.
UNAME_STR= "(?:#{NCNAME_STR}:)?#{NCNAME_STR}"
NAMECHAR = '[\-\w\.:]'
@@ -45,8 +50,7 @@ module REXML
DOCTYPE_START = /\A\s*<!DOCTYPE\s/um
DOCTYPE_END = /\A\s*\]\s*>/um
- DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um
- ATTRIBUTE_PATTERN = /\s*(#{NAME_STR})\s*=\s*(["'])(.*?)\4/um
+ ATTRIBUTE_PATTERN = /\s*(#{QNAME_STR})\s*=\s*(["'])(.*?)\4/um
COMMENT_START = /\A<!--/u
COMMENT_PATTERN = /<!--(.*?)-->/um
CDATA_START = /\A<!\[CDATA\[/u
@@ -55,16 +59,15 @@ module REXML
XMLDECL_START = /\A<\?xml\s/u;
XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>/um
INSTRUCTION_START = /\A<\?/u
- INSTRUCTION_PATTERN = /<\?(.*?)(\s+.*?)?\?>/um
- TAG_MATCH = /^<((?>#{NAME_STR}))\s*((?>\s+#{UNAME_STR}\s*=\s*(["']).*?\5)*)\s*(\/)?>/um
- CLOSE_MATCH = /^\s*<\/(#{NAME_STR})\s*>/um
+ INSTRUCTION_PATTERN = /<\?#{NAME}(\s+.*?)?\?>/um
+ TAG_MATCH = /\A<((?>#{QNAME_STR}))/um
+ CLOSE_MATCH = /\A\s*<\/(#{QNAME_STR})\s*>/um
VERSION = /\bversion\s*=\s*["'](.*?)['"]/um
ENCODING = /\bencoding\s*=\s*["'](.*?)['"]/um
STANDALONE = /\bstandalone\s*=\s*["'](.*?)['"]/um
ENTITY_START = /\A\s*<!ENTITY/
- IDENTITY = /^([!\*\w\-]+)(\s+#{NCNAME_STR})?(\s+["'](.*?)['"])?(\s+['"](.*?)["'])?/u
ELEMENTDECL_START = /\A\s*<!ELEMENT/um
ELEMENTDECL_PATTERN = /\A\s*(<!ELEMENT.*?)>/um
SYSTEMENTITY = /\A\s*(%.*?;)\s*$/um
@@ -78,9 +81,6 @@ module REXML
ATTDEF_RE = /#{ATTDEF}/
ATTLISTDECL_START = /\A\s*<!ATTLIST/um
ATTLISTDECL_PATTERN = /\A\s*<!ATTLIST\s+#{NAME}(?:#{ATTDEF})*\s*>/um
- NOTATIONDECL_START = /\A\s*<!NOTATION/um
- PUBLIC = /\A\s*<!NOTATION\s+(\w[\-\w]*)\s+(PUBLIC)\s+(["'])(.*?)\3(?:\s+(["'])(.*?)\5)?\s*>/um
- SYSTEM = /\A\s*<!NOTATION\s+(\w[\-\w]*)\s+(SYSTEM)\s+(["'])(.*?)\3\s*>/um
TEXT_PATTERN = /\A([^<]*)/um
@@ -98,6 +98,11 @@ module REXML
GEDECL = "<!ENTITY\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>"
ENTITYDECL = /\s*(?:#{GEDECL})|(?:#{PEDECL})/um
+ NOTATIONDECL_START = /\A\s*<!NOTATION/um
+ EXTERNAL_ID_PUBLIC = /\A\s*PUBLIC\s+#{PUBIDLITERAL}\s+#{SYSTEMLITERAL}\s*/um
+ EXTERNAL_ID_SYSTEM = /\A\s*SYSTEM\s+#{SYSTEMLITERAL}\s*/um
+ PUBLIC_ID = /\A\s*PUBLIC\s+#{PUBIDLITERAL}\s*/um
+
EREFERENCE = /&(?!#{NAME};)/
DEFAULT_ENTITIES = {
@@ -107,13 +112,6 @@ module REXML
"apos" => [/&apos;/, "&apos;", "'", /'/]
}
-
- ######################################################################
- # These are patterns to identify common markup errors, to make the
- # error messages more informative.
- ######################################################################
- MISSING_ATTRIBUTE_QUOTES = /^<#{NAME_STR}\s+#{NAME_STR}\s*=\s*[^"']/um
-
def initialize( source )
self.stream = source
@listeners = []
@@ -197,11 +195,9 @@ module REXML
return [ :end_document ] if empty?
return @stack.shift if @stack.size > 0
#STDERR.puts @source.encoding
- @source.read if @source.buffer.size<2
#STDERR.puts "BUFFER = #{@source.buffer.inspect}"
if @document_status == nil
- #@source.consume( /^\s*/um )
- word = @source.match( /^((?:\s+)|(?:<[^>]*>))/um )
+ word = @source.match( /\A((?:\s+)|(?:<[^>]*>))/um )
word = word[1] unless word.nil?
#STDERR.puts "WORD = #{word.inspect}"
case word
@@ -224,40 +220,51 @@ module REXML
standalone = standalone[1] unless standalone.nil?
return [ :xmldecl, version, encoding, standalone ]
when INSTRUCTION_START
- return [ :processing_instruction, *@source.match(INSTRUCTION_PATTERN, true)[1,2] ]
+ return process_instruction
when DOCTYPE_START
- md = @source.match( DOCTYPE_PATTERN, true )
+ base_error_message = "Malformed DOCTYPE"
+ @source.match(DOCTYPE_START, true)
@nsstack.unshift(curr_ns=Set.new)
- identity = md[1]
- close = md[2]
- identity =~ IDENTITY
- name = $1
- raise REXML::ParseException.new("DOCTYPE is missing a name") if name.nil?
- pub_sys = $2.nil? ? nil : $2.strip
- long_name = $4.nil? ? nil : $4.strip
- uri = $6.nil? ? nil : $6.strip
- args = [ :start_doctype, name, pub_sys, long_name, uri ]
- if close == ">"
+ name = parse_name(base_error_message)
+ if @source.match(/\A\s*\[/um, true)
+ id = [nil, nil, nil]
+ @document_status = :in_doctype
+ elsif @source.match(/\A\s*>/um, true)
+ id = [nil, nil, nil]
@document_status = :after_doctype
- @source.read if @source.buffer.size<2
- md = @source.match(/^\s*/um, true)
- @stack << [ :end_doctype ]
else
- @document_status = :in_doctype
+ id = parse_id(base_error_message,
+ accept_external_id: true,
+ accept_public_id: false)
+ if id[0] == "SYSTEM"
+ # For backward compatibility
+ id[1], id[2] = id[2], nil
+ end
+ if @source.match(/\A\s*\[/um, true)
+ @document_status = :in_doctype
+ elsif @source.match(/\A\s*>/um, true)
+ @document_status = :after_doctype
+ else
+ message = "#{base_error_message}: garbage after external ID"
+ raise REXML::ParseException.new(message, @source)
+ end
+ end
+ args = [:start_doctype, name, *id]
+ if @document_status == :after_doctype
+ @source.match(/\A\s*/um, true)
+ @stack << [ :end_doctype ]
end
return args
- when /^\s+/
+ when /\A\s+/
else
@document_status = :after_doctype
- @source.read if @source.buffer.size<2
- md = @source.match(/\s*/um, true)
if @source.encoding == "UTF-8"
@source.buffer.force_encoding(::Encoding::UTF_8)
end
end
end
if @document_status == :in_doctype
- md = @source.match(/\s*(.*?>)/um)
+ md = @source.match(/\A\s*(.*?>)/um)
case md[1]
when SYSTEMENTITY
match = @source.match( SYSTEMENTITY, true )[1]
@@ -314,33 +321,45 @@ module REXML
end
return [ :attlistdecl, element, pairs, contents ]
when NOTATIONDECL_START
- md = nil
- if @source.match( PUBLIC )
- md = @source.match( PUBLIC, true )
- vals = [md[1],md[2],md[4],md[6]]
- elsif @source.match( SYSTEM )
- md = @source.match( SYSTEM, true )
- vals = [md[1],md[2],nil,md[4]]
- else
- raise REXML::ParseException.new( "error parsing notation: no matching pattern", @source )
+ base_error_message = "Malformed notation declaration"
+ unless @source.match(/\A\s*<!NOTATION\s+/um, true)
+ if @source.match(/\A\s*<!NOTATION\s*>/um)
+ message = "#{base_error_message}: name is missing"
+ else
+ message = "#{base_error_message}: invalid declaration name"
+ end
+ raise REXML::ParseException.new(message, @source)
end
- return [ :notationdecl, *vals ]
+ name = parse_name(base_error_message)
+ id = parse_id(base_error_message,
+ accept_external_id: true,
+ accept_public_id: true)
+ unless @source.match(/\A\s*>/um, true)
+ message = "#{base_error_message}: garbage before end >"
+ raise REXML::ParseException.new(message, @source)
+ end
+ return [:notationdecl, name, *id]
when DOCTYPE_END
@document_status = :after_doctype
@source.match( DOCTYPE_END, true )
return [ :end_doctype ]
end
end
+ if @document_status == :after_doctype
+ @source.match(/\A\s*/um, true)
+ end
begin
+ @source.read if @source.buffer.size<2
if @source.buffer[0] == ?<
if @source.buffer[1] == ?/
@nsstack.shift
last_tag = @tags.pop
- #md = @source.match_to_consume( '>', CLOSE_MATCH)
md = @source.match( CLOSE_MATCH, true )
- raise REXML::ParseException.new( "Missing end tag for "+
- "'#{last_tag}' (got \"#{md[1]}\")",
- @source) unless last_tag == md[1]
+ if md.nil? or last_tag != md[1]
+ message = "Missing end tag for '#{last_tag}'"
+ message << " (got '#{md[1]}')" if md
+ raise REXML::ParseException.new(message, @source)
+ end
return [ :end_element, last_tag ]
elsif @source.buffer[1] == ?!
md = @source.match(/\A(\s*[^>]*>)/um)
@@ -362,52 +381,18 @@ module REXML
raise REXML::ParseException.new( "Declarations can only occur "+
"in the doctype declaration.", @source)
elsif @source.buffer[1] == ??
- md = @source.match( INSTRUCTION_PATTERN, true )
- return [ :processing_instruction, md[1], md[2] ] if md
- raise REXML::ParseException.new( "Bad instruction declaration",
- @source)
+ return process_instruction
else
# Get the next tag
md = @source.match(TAG_MATCH, true)
unless md
- # Check for missing attribute quotes
- raise REXML::ParseException.new("missing attribute quote", @source) if @source.match(MISSING_ATTRIBUTE_QUOTES )
raise REXML::ParseException.new("malformed XML: missing tag start", @source)
end
- attributes = {}
+ @document_status = :in_element
prefixes = Set.new
prefixes << md[2] if md[2]
@nsstack.unshift(curr_ns=Set.new)
- if md[4].size > 0
- attrs = md[4].scan( ATTRIBUTE_PATTERN )
- raise REXML::ParseException.new( "error parsing attributes: [#{attrs.join ', '}], excess = \"#$'\"", @source) if $' and $'.strip.size > 0
- attrs.each do |attr_name, prefix, local_part, quote, value|
- if prefix == "xmlns"
- if local_part == "xml"
- if value != "http://www.w3.org/XML/1998/namespace"
- msg = "The 'xml' prefix must not be bound to any other namespace "+
- "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
- raise REXML::ParseException.new( msg, @source, self )
- end
- elsif local_part == "xmlns"
- msg = "The 'xmlns' prefix must not be declared "+
- "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
- raise REXML::ParseException.new( msg, @source, self)
- end
- curr_ns << local_part
- elsif prefix
- prefixes << prefix unless prefix == "xml"
- end
-
- if attributes.has_key?(attr_name)
- msg = "Duplicate attribute #{attr_name.inspect}"
- raise REXML::ParseException.new(msg, @source, self)
- end
-
- attributes[attr_name] = value
- end
- end
-
+ attributes, closed = parse_attributes(prefixes, curr_ns)
# Verify that all of the prefixes have been defined
for prefix in prefixes
unless @nsstack.find{|k| k.member?(prefix)}
@@ -415,7 +400,7 @@ module REXML
end
end
- if md[6]
+ if closed
@closed = md[1]
@nsstack.shift
else
@@ -438,7 +423,7 @@ module REXML
raise
rescue REXML::ParseException
raise
- rescue Exception, NameError => error
+ rescue => error
raise REXML::ParseException.new( "Exception parsing",
@source, self, (error ? error : $!) )
end
@@ -508,6 +493,178 @@ module REXML
return false if /\AUTF-16\z/i =~ xml_declaration_encoding
true
end
+
+ def parse_name(base_error_message)
+ md = @source.match(/\A\s*#{NAME}/um, true)
+ unless md
+ if @source.match(/\A\s*\S/um)
+ message = "#{base_error_message}: invalid name"
+ else
+ message = "#{base_error_message}: name is missing"
+ end
+ raise REXML::ParseException.new(message, @source)
+ end
+ md[1]
+ end
+
+ def parse_id(base_error_message,
+ accept_external_id:,
+ accept_public_id:)
+ if accept_external_id and (md = @source.match(EXTERNAL_ID_PUBLIC, true))
+ pubid = system = nil
+ pubid_literal = md[1]
+ pubid = pubid_literal[1..-2] if pubid_literal # Remove quote
+ system_literal = md[2]
+ system = system_literal[1..-2] if system_literal # Remove quote
+ ["PUBLIC", pubid, system]
+ elsif accept_public_id and (md = @source.match(PUBLIC_ID, true))
+ pubid = system = nil
+ pubid_literal = md[1]
+ pubid = pubid_literal[1..-2] if pubid_literal # Remove quote
+ ["PUBLIC", pubid, nil]
+ elsif accept_external_id and (md = @source.match(EXTERNAL_ID_SYSTEM, true))
+ system = nil
+ system_literal = md[1]
+ system = system_literal[1..-2] if system_literal # Remove quote
+ ["SYSTEM", nil, system]
+ else
+ details = parse_id_invalid_details(accept_external_id: accept_external_id,
+ accept_public_id: accept_public_id)
+ message = "#{base_error_message}: #{details}"
+ raise REXML::ParseException.new(message, @source)
+ end
+ end
+
+ def parse_id_invalid_details(accept_external_id:,
+ accept_public_id:)
+ public = /\A\s*PUBLIC/um
+ system = /\A\s*SYSTEM/um
+ if (accept_external_id or accept_public_id) and @source.match(/#{public}/um)
+ if @source.match(/#{public}(?:\s+[^'"]|\s*[\[>])/um)
+ return "public ID literal is missing"
+ end
+ unless @source.match(/#{public}\s+#{PUBIDLITERAL}/um)
+ return "invalid public ID literal"
+ end
+ if accept_public_id
+ if @source.match(/#{public}\s+#{PUBIDLITERAL}\s+[^'"]/um)
+ return "system ID literal is missing"
+ end
+ unless @source.match(/#{public}\s+#{PUBIDLITERAL}\s+#{SYSTEMLITERAL}/um)
+ return "invalid system literal"
+ end
+ "garbage after system literal"
+ else
+ "garbage after public ID literal"
+ end
+ elsif accept_external_id and @source.match(/#{system}/um)
+ if @source.match(/#{system}(?:\s+[^'"]|\s*[\[>])/um)
+ return "system literal is missing"
+ end
+ unless @source.match(/#{system}\s+#{SYSTEMLITERAL}/um)
+ return "invalid system literal"
+ end
+ "garbage after system literal"
+ else
+ unless @source.match(/\A\s*(?:PUBLIC|SYSTEM)\s/um)
+ return "invalid ID type"
+ end
+ "ID type is missing"
+ end
+ end
+
+ def process_instruction
+ match_data = @source.match(INSTRUCTION_PATTERN, true)
+ unless match_data
+ message = "Invalid processing instruction node"
+ raise REXML::ParseException.new(message, @source)
+ end
+ [:processing_instruction, match_data[1], match_data[2]]
+ end
+
+ def parse_attributes(prefixes, curr_ns)
+ attributes = {}
+ closed = false
+ match_data = @source.match(/^(.*?)(\/)?>/um, true)
+ if match_data.nil?
+ message = "Start tag isn't ended"
+ raise REXML::ParseException.new(message, @source)
+ end
+
+ raw_attributes = match_data[1]
+ closed = !match_data[2].nil?
+ return attributes, closed if raw_attributes.nil?
+ return attributes, closed if raw_attributes.empty?
+
+ scanner = StringScanner.new(raw_attributes)
+ until scanner.eos?
+ if scanner.scan(/\s+/)
+ break if scanner.eos?
+ end
+
+ pos = scanner.pos
+ loop do
+ break if scanner.scan(ATTRIBUTE_PATTERN)
+ unless scanner.scan(QNAME)
+ message = "Invalid attribute name: <#{scanner.rest}>"
+ raise REXML::ParseException.new(message, @source)
+ end
+ name = scanner[0]
+ unless scanner.scan(/\s*=\s*/um)
+ message = "Missing attribute equal: <#{name}>"
+ raise REXML::ParseException.new(message, @source)
+ end
+ quote = scanner.scan(/['"]/)
+ unless quote
+ message = "Missing attribute value start quote: <#{name}>"
+ raise REXML::ParseException.new(message, @source)
+ end
+ unless scanner.scan(/.*#{Regexp.escape(quote)}/um)
+ match_data = @source.match(/^(.*?)(\/)?>/um, true)
+ if match_data
+ scanner << "/" if closed
+ scanner << ">"
+ scanner << match_data[1]
+ scanner.pos = pos
+ closed = !match_data[2].nil?
+ next
+ end
+ message =
+ "Missing attribute value end quote: <#{name}>: <#{quote}>"
+ raise REXML::ParseException.new(message, @source)
+ end
+ end
+ name = scanner[1]
+ prefix = scanner[2]
+ local_part = scanner[3]
+ # quote = scanner[4]
+ value = scanner[5]
+ if prefix == "xmlns"
+ if local_part == "xml"
+ if value != "http://www.w3.org/XML/1998/namespace"
+ msg = "The 'xml' prefix must not be bound to any other namespace "+
+ "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
+ raise REXML::ParseException.new( msg, @source, self )
+ end
+ elsif local_part == "xmlns"
+ msg = "The 'xmlns' prefix must not be declared "+
+ "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
+ raise REXML::ParseException.new( msg, @source, self)
+ end
+ curr_ns << local_part
+ elsif prefix
+ prefixes << prefix unless prefix == "xml"
+ end
+
+ if attributes.has_key?(name)
+ msg = "Duplicate attribute #{name.inspect}"
+ raise REXML::ParseException.new(msg, @source, self)
+ end
+
+ attributes[name] = value
+ end
+ return attributes, closed
+ end
end
end
end
diff --git a/lib/rexml/parsers/lightparser.rb b/lib/rexml/parsers/lightparser.rb
index f0601ae51b..bdc08276a9 100644
--- a/lib/rexml/parsers/lightparser.rb
+++ b/lib/rexml/parsers/lightparser.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
-require 'rexml/parsers/streamparser'
-require 'rexml/parsers/baseparser'
-require 'rexml/light/node'
+require_relative 'streamparser'
+require_relative 'baseparser'
+require_relative '../light/node'
module REXML
module Parsers
diff --git a/lib/rexml/parsers/pullparser.rb b/lib/rexml/parsers/pullparser.rb
index 8c49217553..f8b232a2cd 100644
--- a/lib/rexml/parsers/pullparser.rb
+++ b/lib/rexml/parsers/pullparser.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: false
require 'forwardable'
-require 'rexml/parseexception'
-require 'rexml/parsers/baseparser'
-require 'rexml/xmltokens'
+require_relative '../parseexception'
+require_relative 'baseparser'
+require_relative '../xmltokens'
module REXML
module Parsers
diff --git a/lib/rexml/parsers/sax2parser.rb b/lib/rexml/parsers/sax2parser.rb
index 1386f69c83..6a24ce2227 100644
--- a/lib/rexml/parsers/sax2parser.rb
+++ b/lib/rexml/parsers/sax2parser.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: false
-require 'rexml/parsers/baseparser'
-require 'rexml/parseexception'
-require 'rexml/namespace'
-require 'rexml/text'
+require_relative 'baseparser'
+require_relative '../parseexception'
+require_relative '../namespace'
+require_relative '../text'
module REXML
module Parsers
diff --git a/lib/rexml/parsers/streamparser.rb b/lib/rexml/parsers/streamparser.rb
index b271e6743e..9e0eb0b363 100644
--- a/lib/rexml/parsers/streamparser.rb
+++ b/lib/rexml/parsers/streamparser.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rexml/parsers/baseparser"
+require_relative "baseparser"
module REXML
module Parsers
@@ -7,6 +7,7 @@ module REXML
def initialize source, listener
@listener = listener
@parser = BaseParser.new( source )
+ @tag_stack = []
end
def add_listener( listener )
@@ -19,14 +20,21 @@ module REXML
event = @parser.pull
case event[0]
when :end_document
+ unless @tag_stack.empty?
+ tag_path = "/" + @tag_stack.join("/")
+ raise ParseException.new("Missing end tag for '#{tag_path}'",
+ @parser.source)
+ end
return
when :start_element
+ @tag_stack << event[1]
attrs = event[2].each do |n, v|
event[2][n] = @parser.unnormalize( v )
end
@listener.tag_start( event[1], attrs )
when :end_element
@listener.tag_end( event[1] )
+ @tag_stack.pop
when :text
normalized = @parser.unnormalize( event[1] )
@listener.text( normalized )
diff --git a/lib/rexml/parsers/treeparser.rb b/lib/rexml/parsers/treeparser.rb
index fc0993c72a..bf9a42545b 100644
--- a/lib/rexml/parsers/treeparser.rb
+++ b/lib/rexml/parsers/treeparser.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
-require 'rexml/validation/validationexception'
-require 'rexml/undefinednamespaceexception'
+require_relative '../validation/validationexception'
+require_relative '../undefinednamespaceexception'
module REXML
module Parsers
diff --git a/lib/rexml/parsers/ultralightparser.rb b/lib/rexml/parsers/ultralightparser.rb
index 6571d119bd..e0029f43da 100644
--- a/lib/rexml/parsers/ultralightparser.rb
+++ b/lib/rexml/parsers/ultralightparser.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
-require 'rexml/parsers/streamparser'
-require 'rexml/parsers/baseparser'
+require_relative 'streamparser'
+require_relative 'baseparser'
module REXML
module Parsers
diff --git a/lib/rexml/parsers/xpathparser.rb b/lib/rexml/parsers/xpathparser.rb
index 32b70bb798..d01d325e04 100644
--- a/lib/rexml/parsers/xpathparser.rb
+++ b/lib/rexml/parsers/xpathparser.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
-require 'rexml/namespace'
-require 'rexml/xmltokens'
+require_relative '../namespace'
+require_relative '../xmltokens'
module REXML
module Parsers
@@ -185,7 +185,7 @@ module REXML
# | '/' RelativeLocationPath?
# | '//' RelativeLocationPath
def LocationPath path, parsed
- path = path.strip
+ path = path.lstrip
if path[0] == ?/
parsed << :document
if path[1] == ?/
@@ -209,7 +209,12 @@ module REXML
# | RelativeLocationPath '//' Step
AXIS = /^(ancestor|ancestor-or-self|attribute|child|descendant|descendant-or-self|following|following-sibling|namespace|parent|preceding|preceding-sibling|self)::/
def RelativeLocationPath path, parsed
- while path.size > 0
+ loop do
+ original_path = path
+ path = path.lstrip
+
+ return original_path if path.empty?
+
# (axis or @ or <child::>) nodetest predicate >
# OR > / Step
# (. or ..) >
@@ -239,28 +244,25 @@ module REXML
n = []
path = NodeTest( path, n)
- if path[0] == ?[
- path = Predicate( path, n )
- end
+ path = Predicate( path, n )
parsed.concat(n)
end
- if path.size > 0
- if path[0] == ?/
- if path[1] == ?/
- parsed << :descendant_or_self
- parsed << :node
- path = path[2..-1]
- else
- path = path[1..-1]
- end
- else
- return path
- end
+ original_path = path
+ path = path.lstrip
+ return original_path if path.empty?
+
+ return original_path if path[0] != ?/
+
+ if path[1] == ?/
+ parsed << :descendant_or_self
+ parsed << :node
+ path = path[2..-1]
+ else
+ path = path[1..-1]
end
end
- return path
end
# Returns a 1-1 map of the nodeset
@@ -269,15 +271,26 @@ module REXML
# String, if a name match
#NodeTest
# | ('*' | NCNAME ':' '*' | QNAME) NameTest
- # | NODE_TYPE '(' ')' NodeType
+ # | '*' ':' NCNAME NameTest since XPath 2.0
+ # | NODE_TYPE '(' ')' NodeType
# | PI '(' LITERAL ')' PI
# | '[' expr ']' Predicate
- NCNAMETEST= /^(#{NCNAME_STR}):\*/u
+ PREFIX_WILDCARD = /^\*:(#{NCNAME_STR})/u
+ LOCAL_NAME_WILDCARD = /^(#{NCNAME_STR}):\*/u
QNAME = Namespace::NAMESPLIT
NODE_TYPE = /^(comment|text|node)\(\s*\)/m
PI = /^processing-instruction\(/
def NodeTest path, parsed
+ original_path = path
+ path = path.lstrip
case path
+ when PREFIX_WILDCARD
+ prefix = nil
+ name = $1
+ path = $'
+ parsed << :qname
+ parsed << prefix
+ parsed << name
when /^\*/
path = $'
parsed << :any
@@ -297,7 +310,7 @@ module REXML
end
parsed << :processing_instruction
parsed << (literal || '')
- when NCNAMETEST
+ when LOCAL_NAME_WILDCARD
prefix = $1
path = $'
parsed << :namespace
@@ -310,13 +323,17 @@ module REXML
parsed << :qname
parsed << prefix
parsed << name
+ else
+ path = original_path
end
return path
end
# Filters the supplied nodeset on the predicate(s)
def Predicate path, parsed
- return nil unless path[0] == ?[
+ original_path = path
+ path = path.lstrip
+ return original_path unless path[0] == ?[
predicates = []
while path[0] == ?[
path, expr = get_group(path)
@@ -421,13 +438,13 @@ module REXML
rest
end
- #| AdditiveExpr ('+' | S '-') MultiplicativeExpr
+ #| AdditiveExpr ('+' | '-') MultiplicativeExpr
#| MultiplicativeExpr
def AdditiveExpr path, parsed
n = []
rest = MultiplicativeExpr( path, n )
if rest != path
- while rest =~ /^\s*(\+| -)\s*/
+ while rest =~ /^\s*(\+|-)\s*/
if $1[0] == ?+
n = [ :plus, n, [] ]
else
@@ -509,13 +526,14 @@ module REXML
#| LocationPath
#| FilterExpr ('/' | '//') RelativeLocationPath
def PathExpr path, parsed
- path =~ /^\s*/
- path = $'
+ path = path.lstrip
n = []
rest = FilterExpr( path, n )
if rest != path
if rest and rest[0] == ?/
- return RelativeLocationPath(rest, n)
+ rest = RelativeLocationPath(rest, n)
+ parsed.concat(n)
+ return rest
end
end
rest = LocationPath(rest, n) if rest =~ /\A[\/\.\@\[\w*]/
@@ -528,7 +546,7 @@ module REXML
def FilterExpr path, parsed
n = []
path = PrimaryExpr( path, n )
- path = Predicate(path, n) if path and path[0] == ?[
+ path = Predicate(path, n)
parsed.concat(n)
path
end
diff --git a/lib/rexml/quickpath.rb b/lib/rexml/quickpath.rb
index 5d6c77ca38..a0466b25d9 100644
--- a/lib/rexml/quickpath.rb
+++ b/lib/rexml/quickpath.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
-require 'rexml/functions'
-require 'rexml/xmltokens'
+require_relative 'functions'
+require_relative 'xmltokens'
module REXML
class QuickPath
diff --git a/lib/rexml/rexml.gemspec b/lib/rexml/rexml.gemspec
new file mode 100644
index 0000000000..6fe7e635f1
--- /dev/null
+++ b/lib/rexml/rexml.gemspec
@@ -0,0 +1,85 @@
+begin
+ require_relative "lib/rexml/rexml"
+rescue LoadError
+ # for Ruby core repository
+ require_relative "rexml"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "rexml"
+ spec.version = REXML::VERSION
+ spec.authors = ["Kouhei Sutou"]
+ spec.email = ["kou@cozmixng.org"]
+
+ spec.summary = %q{An XML toolkit for Ruby}
+ spec.description = %q{An XML toolkit for Ruby}
+ spec.homepage = "https://github.com/ruby/rexml"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [
+ ".gitignore",
+ ".travis.yml",
+ "Gemfile",
+ "LICENSE.txt",
+ "NEWS.md",
+ "README.md",
+ "Rakefile",
+ "lib/rexml/attlistdecl.rb",
+ "lib/rexml/attribute.rb",
+ "lib/rexml/cdata.rb",
+ "lib/rexml/child.rb",
+ "lib/rexml/comment.rb",
+ "lib/rexml/doctype.rb",
+ "lib/rexml/document.rb",
+ "lib/rexml/dtd/attlistdecl.rb",
+ "lib/rexml/dtd/dtd.rb",
+ "lib/rexml/dtd/elementdecl.rb",
+ "lib/rexml/dtd/entitydecl.rb",
+ "lib/rexml/dtd/notationdecl.rb",
+ "lib/rexml/element.rb",
+ "lib/rexml/encoding.rb",
+ "lib/rexml/entity.rb",
+ "lib/rexml/formatters/default.rb",
+ "lib/rexml/formatters/pretty.rb",
+ "lib/rexml/formatters/transitive.rb",
+ "lib/rexml/functions.rb",
+ "lib/rexml/instruction.rb",
+ "lib/rexml/light/node.rb",
+ "lib/rexml/namespace.rb",
+ "lib/rexml/node.rb",
+ "lib/rexml/output.rb",
+ "lib/rexml/parent.rb",
+ "lib/rexml/parseexception.rb",
+ "lib/rexml/parsers/baseparser.rb",
+ "lib/rexml/parsers/lightparser.rb",
+ "lib/rexml/parsers/pullparser.rb",
+ "lib/rexml/parsers/sax2parser.rb",
+ "lib/rexml/parsers/streamparser.rb",
+ "lib/rexml/parsers/treeparser.rb",
+ "lib/rexml/parsers/ultralightparser.rb",
+ "lib/rexml/parsers/xpathparser.rb",
+ "lib/rexml/quickpath.rb",
+ "lib/rexml/rexml.rb",
+ "lib/rexml/sax2listener.rb",
+ "lib/rexml/security.rb",
+ "lib/rexml/source.rb",
+ "lib/rexml/streamlistener.rb",
+ "lib/rexml/syncenumerator.rb",
+ "lib/rexml/text.rb",
+ "lib/rexml/undefinednamespaceexception.rb",
+ "lib/rexml/validation/relaxng.rb",
+ "lib/rexml/validation/validation.rb",
+ "lib/rexml/validation/validationexception.rb",
+ "lib/rexml/xmldecl.rb",
+ "lib/rexml/xmltokens.rb",
+ "lib/rexml/xpath.rb",
+ "lib/rexml/xpath_parser.rb",
+ "rexml.gemspec",
+ ]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+end
diff --git a/lib/rexml/rexml.rb b/lib/rexml/rexml.rb
index fbc0d339d8..627dff4768 100644
--- a/lib/rexml/rexml.rb
+++ b/lib/rexml/rexml.rb
@@ -1,4 +1,4 @@
-# -*- encoding: utf-8 -*-
+# -*- coding: utf-8 -*-
# frozen_string_literal: false
# REXML is an XML toolkit for Ruby[http://www.ruby-lang.org], in Ruby.
#
@@ -24,8 +24,8 @@
module REXML
COPYRIGHT = "Copyright © 2001-2008 Sean Russell <ser@germane-software.com>"
DATE = "2008/019"
- VERSION = "3.1.7.3"
- REVISION = %w$Revision$[1] || ''
+ VERSION = "3.1.9.1"
+ REVISION = ""
Copyright = COPYRIGHT
Version = VERSION
diff --git a/lib/rexml/source.rb b/lib/rexml/source.rb
index af65cf4751..770aefc818 100644
--- a/lib/rexml/source.rb
+++ b/lib/rexml/source.rb
@@ -1,6 +1,6 @@
# coding: US-ASCII
# frozen_string_literal: false
-require 'rexml/encoding'
+require_relative 'encoding'
module REXML
# Generates Source-s. USE THIS CLASS.
@@ -254,6 +254,7 @@ module REXML
end
rescue
end
+ @er_source.seek(pos)
rescue IOError
pos = -1
line = -1
diff --git a/lib/rexml/text.rb b/lib/rexml/text.rb
index 9ea8ba9df3..6139caecd7 100644
--- a/lib/rexml/text.rb
+++ b/lib/rexml/text.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: false
-require 'rexml/security'
-require 'rexml/entity'
-require 'rexml/doctype'
-require 'rexml/child'
-require 'rexml/doctype'
-require 'rexml/parseexception'
+require_relative 'security'
+require_relative 'entity'
+require_relative 'doctype'
+require_relative 'child'
+require_relative 'doctype'
+require_relative 'parseexception'
module REXML
# Represents text nodes in an XML document
@@ -96,27 +96,28 @@ module REXML
@raw = false
@parent = nil
+ @entity_filter = nil
if parent
super( parent )
@raw = parent.raw
end
- @raw = raw unless raw.nil?
- @entity_filter = entity_filter
- clear_cache
-
if arg.kind_of? String
@string = arg.dup
- @string.squeeze!(" \n\t") unless respect_whitespace
elsif arg.kind_of? Text
- @string = arg.to_s
+ @string = arg.instance_variable_get(:@string).dup
@raw = arg.raw
+ @entity_filter = arg.instance_variable_get(:@entity_filter)
elsif
raise "Illegal argument of type #{arg.type} for Text constructor (#{arg})"
end
- @string.gsub!( /\r\n?/, "\n" )
+ @string.squeeze!(" \n\t") unless respect_whitespace
+ @string.gsub!(/\r\n?/, "\n")
+ @raw = raw unless raw.nil?
+ @entity_filter = entity_filter if entity_filter
+ clear_cache
Text.check(@string, illegal, doctype) if @raw
end
@@ -181,7 +182,7 @@ module REXML
def clone
- return Text.new(self)
+ return Text.new(self, true)
end
@@ -226,9 +227,7 @@ module REXML
# u.to_s #-> "sean russell"
def to_s
return @string if @raw
- return @normalized if @normalized
-
- @normalized = Text::normalize( @string, doctype, @entity_filter )
+ @normalized ||= Text::normalize( @string, doctype, @entity_filter )
end
def inspect
@@ -249,8 +248,7 @@ module REXML
# u = Text.new( "sean russell", false, nil, true )
# u.value #-> "sean russell"
def value
- return @unnormalized if @unnormalized
- @unnormalized = Text::unnormalize( @string, doctype )
+ @unnormalized ||= Text::unnormalize( @string, doctype )
end
# Sets the contents of this text node. This expects the text to be
@@ -266,16 +264,16 @@ module REXML
@raw = false
end
- def wrap(string, width, addnewline=false)
- # Recursively wrap string at width.
- return string if string.length <= width
- place = string.rindex(' ', width) # Position in string with last ' ' before cutoff
- if addnewline then
- return "\n" + string[0,place] + "\n" + wrap(string[place+1..-1], width)
- else
- return string[0,place] + "\n" + wrap(string[place+1..-1], width)
- end
- end
+ def wrap(string, width, addnewline=false)
+ # Recursively wrap string at width.
+ return string if string.length <= width
+ place = string.rindex(' ', width) # Position in string with last ' ' before cutoff
+ if addnewline then
+ return "\n" + string[0,place] + "\n" + wrap(string[place+1..-1], width)
+ else
+ return string[0,place] + "\n" + wrap(string[place+1..-1], width)
+ end
+ end
def indent_text(string, level=1, style="\t", indentfirstline=true)
return string if level < 0
@@ -293,7 +291,7 @@ module REXML
# See REXML::Formatters
#
def write( writer, indent=-1, transitive=false, ie_hack=false )
- Kernel.warn("#{self.class.name}.write is deprecated. See REXML::Formatters")
+ Kernel.warn("#{self.class.name}.write is deprecated. See REXML::Formatters", uplevel: 1)
formatter = if indent > -1
REXML::Formatters::Pretty.new( indent )
else
diff --git a/lib/rexml/undefinednamespaceexception.rb b/lib/rexml/undefinednamespaceexception.rb
index e522ed57ea..492a098183 100644
--- a/lib/rexml/undefinednamespaceexception.rb
+++ b/lib/rexml/undefinednamespaceexception.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require 'rexml/parseexception'
+require_relative 'parseexception'
module REXML
class UndefinedNamespaceException < ParseException
def initialize( prefix, source, parser )
diff --git a/lib/rexml/validation/relaxng.rb b/lib/rexml/validation/relaxng.rb
index fb52438290..f29a2c05e5 100644
--- a/lib/rexml/validation/relaxng.rb
+++ b/lib/rexml/validation/relaxng.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
-require "rexml/validation/validation"
-require "rexml/parsers/baseparser"
+require_relative "validation"
+require_relative "../parsers/baseparser"
module REXML
module Validation
diff --git a/lib/rexml/validation/validation.rb b/lib/rexml/validation/validation.rb
index f0c76f976c..0ad6ada427 100644
--- a/lib/rexml/validation/validation.rb
+++ b/lib/rexml/validation/validation.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require 'rexml/validation/validationexception'
+require_relative 'validationexception'
module REXML
module Validation
diff --git a/lib/rexml/xmldecl.rb b/lib/rexml/xmldecl.rb
index a37e9f3ddc..89c0747d49 100644
--- a/lib/rexml/xmldecl.rb
+++ b/lib/rexml/xmldecl.rb
@@ -1,17 +1,18 @@
# frozen_string_literal: false
-require 'rexml/encoding'
-require 'rexml/source'
+
+require_relative 'encoding'
+require_relative 'source'
module REXML
# NEEDS DOCUMENTATION
class XMLDecl < Child
include Encoding
- DEFAULT_VERSION = "1.0";
- DEFAULT_ENCODING = "UTF-8";
- DEFAULT_STANDALONE = "no";
- START = '<\?xml';
- STOP = '\?>';
+ DEFAULT_VERSION = "1.0"
+ DEFAULT_ENCODING = "UTF-8"
+ DEFAULT_STANDALONE = "no"
+ START = "<?xml"
+ STOP = "?>"
attr_accessor :version, :standalone
attr_reader :writeencoding, :writethis
@@ -46,9 +47,9 @@ module REXML
# Ignored
def write(writer, indent=-1, transitive=false, ie_hack=false)
return nil unless @writethis or writer.kind_of? Output
- writer << START.sub(/\\/u, '')
+ writer << START
writer << " #{content encoding}"
- writer << STOP.sub(/\\/u, '')
+ writer << STOP
end
def ==( other )
@@ -102,14 +103,26 @@ module REXML
end
def inspect
- START.sub(/\\/u, '') + " ... " + STOP.sub(/\\/u, '')
+ "#{START} ... #{STOP}"
end
private
def content(enc)
- rv = "version='#@version'"
- rv << " encoding='#{enc}'" if @writeencoding || enc !~ /\Autf-8\z/i
- rv << " standalone='#@standalone'" if @standalone
+ context = nil
+ context = parent.context if parent
+ if context and context[:prologue_quote] == :quote
+ quote = "\""
+ else
+ quote = "'"
+ end
+
+ rv = "version=#{quote}#{@version}#{quote}"
+ if @writeencoding or enc !~ /\Autf-8\z/i
+ rv << " encoding=#{quote}#{enc}#{quote}"
+ end
+ if @standalone
+ rv << " standalone=#{quote}#{@standalone}#{quote}"
+ end
rv
end
end
diff --git a/lib/rexml/xpath.rb b/lib/rexml/xpath.rb
index f1cb99baea..a0921bd8e1 100644
--- a/lib/rexml/xpath.rb
+++ b/lib/rexml/xpath.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
-require 'rexml/functions'
-require 'rexml/xpath_parser'
+require_relative 'functions'
+require_relative 'xpath_parser'
module REXML
# Wrapper class. Use this class to access the XPath functions.
@@ -28,10 +28,10 @@ module REXML
# XPath.first( doc, "//b"} )
# XPath.first( node, "a/x:b", { "x"=>"http://doofus" } )
# XPath.first( node, '/book/publisher/text()=$publisher', {}, {"publisher"=>"O'Reilly"})
- def XPath::first element, path=nil, namespaces=nil, variables={}
+ def XPath::first(element, path=nil, namespaces=nil, variables={}, options={})
raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.nil? or namespaces.kind_of?(Hash)
raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of?(Hash)
- parser = XPathParser.new
+ parser = XPathParser.new(**options)
parser.namespaces = namespaces
parser.variables = variables
path = "*" unless path
@@ -57,10 +57,10 @@ module REXML
# XPath.each( node, 'ancestor::x' ) { |el| ... }
# XPath.each( node, '/book/publisher/text()=$publisher', {}, {"publisher"=>"O'Reilly"}) \
# {|el| ... }
- def XPath::each element, path=nil, namespaces=nil, variables={}, &block
+ def XPath::each(element, path=nil, namespaces=nil, variables={}, options={}, &block)
raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.nil? or namespaces.kind_of?(Hash)
raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of?(Hash)
- parser = XPathParser.new
+ parser = XPathParser.new(**options)
parser.namespaces = namespaces
parser.variables = variables
path = "*" unless path
@@ -69,8 +69,8 @@ module REXML
end
# Returns an array of nodes matching a given XPath.
- def XPath::match element, path=nil, namespaces=nil, variables={}
- parser = XPathParser.new
+ def XPath::match(element, path=nil, namespaces=nil, variables={}, options={})
+ parser = XPathParser.new(**options)
parser.namespaces = namespaces
parser.variables = variables
path = "*" unless path
diff --git a/lib/rexml/xpath_parser.rb b/lib/rexml/xpath_parser.rb
index 181b2b6e85..e30581d3d0 100644
--- a/lib/rexml/xpath_parser.rb
+++ b/lib/rexml/xpath_parser.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: false
-require 'rexml/namespace'
-require 'rexml/xmltokens'
-require 'rexml/attribute'
-require 'rexml/syncenumerator'
-require 'rexml/parsers/xpathparser'
+require_relative 'namespace'
+require_relative 'xmltokens'
+require_relative 'attribute'
+require_relative 'syncenumerator'
+require_relative 'parsers/xpathparser'
class Object
# provides a unified +clone+ operation, for REXML::XPathParser
@@ -47,10 +47,12 @@ module REXML
include XMLTokens
LITERAL = /^'([^']*)'|^"([^"]*)"/u
- def initialize( )
+ def initialize(strict: false)
@parser = REXML::Parsers::XPathParser.new
@namespaces = nil
@variables = {}
+ @nest = 0
+ @strict = strict
end
def namespaces=( namespaces={} )
@@ -75,7 +77,7 @@ module REXML
def predicate path, nodeset
path_stack = @parser.parse( path )
- expr( path_stack, nodeset )
+ match( path_stack, nodeset )
end
def []=( variable_name, value )
@@ -123,13 +125,24 @@ module REXML
end
- def match( path_stack, nodeset )
- r = expr( path_stack, nodeset )
- r
+ def match(path_stack, nodeset)
+ nodeset = nodeset.collect.with_index do |node, i|
+ position = i + 1
+ XPathNode.new(node, position: position)
+ end
+ result = expr(path_stack, nodeset)
+ case result
+ when Array # nodeset
+ unnode(result)
+ else
+ result
+ end
end
private
-
+ def strict?
+ @strict
+ end
# Returns a String namespace for a node, given a prefix
# The rules are:
@@ -148,233 +161,179 @@ module REXML
# Expr takes a stack of path elements and a set of nodes (either a Parent
# or an Array and returns an Array of matching nodes
- ALL = [ :attribute, :element, :text, :processing_instruction, :comment ]
- ELEMENTS = [ :element ]
def expr( path_stack, nodeset, context=nil )
- node_types = ELEMENTS
+ # enter(:expr, path_stack, nodeset)
return nodeset if path_stack.length == 0 || nodeset.length == 0
while path_stack.length > 0
+ # trace(:while, path_stack, nodeset)
if nodeset.length == 0
path_stack.clear
return []
end
- case (op = path_stack.shift)
+ op = path_stack.shift
+ case op
when :document
- nodeset = [ nodeset[0].root_node ]
-
- when :qname
- prefix = path_stack.shift
- name = path_stack.shift
- nodeset.delete_if do |node|
- # FIXME: This DOUBLES the time XPath searches take
- ns = get_namespace( node, prefix )
- if node.node_type == :element
- if node.name == name
- end
- end
- !(node.node_type == :element and
- node.name == name and
- node.namespace == ns )
- end
- node_types = ELEMENTS
-
- when :any
- nodeset.delete_if { |node| !node_types.include?(node.node_type) }
-
+ first_raw_node = nodeset.first.raw_node
+ nodeset = [XPathNode.new(first_raw_node.root_node, position: 1)]
when :self
- # This space left intentionally blank
-
- when :processing_instruction
- target = path_stack.shift
- nodeset.delete_if do |node|
- (node.node_type != :processing_instruction) or
- ( target!='' and ( node.target != target ) )
+ nodeset = step(path_stack) do
+ [nodeset]
end
-
- when :text
- nodeset.delete_if { |node| node.node_type != :text }
-
- when :comment
- nodeset.delete_if { |node| node.node_type != :comment }
-
- when :node
- # This space left intentionally blank
- node_types = ALL
-
when :child
- new_nodeset = []
- nt = nil
- nodeset.each do |node|
- nt = node.node_type
- new_nodeset += node.children if nt == :element or nt == :document
+ nodeset = step(path_stack) do
+ child(nodeset)
end
- nodeset = new_nodeset
- node_types = ELEMENTS
-
when :literal
+ # trace(:literal, path_stack, nodeset)
return path_stack.shift
-
when :attribute
- new_nodeset = []
- case path_stack.shift
- when :qname
- prefix = path_stack.shift
- name = path_stack.shift
- for element in nodeset
- if element.node_type == :element
- attrib = element.attribute( name, get_namespace(element, prefix) )
- new_nodeset << attrib if attrib
+ nodeset = step(path_stack, any_type: :attribute) do
+ nodesets = []
+ nodeset.each do |node|
+ raw_node = node.raw_node
+ next unless raw_node.node_type == :element
+ attributes = raw_node.attributes
+ next if attributes.empty?
+ nodesets << attributes.each_attribute.collect.with_index do |attribute, i|
+ XPathNode.new(attribute, position: i + 1)
end
end
- when :any
- for element in nodeset
- if element.node_type == :element
- new_nodeset += element.attributes.to_a
+ nodesets
+ end
+ when :namespace
+ pre_defined_namespaces = {
+ "xml" => "http://www.w3.org/XML/1998/namespace",
+ }
+ nodeset = step(path_stack, any_type: :namespace) do
+ nodesets = []
+ nodeset.each do |node|
+ raw_node = node.raw_node
+ case raw_node.node_type
+ when :element
+ if @namespaces
+ nodesets << pre_defined_namespaces.merge(@namespaces)
+ else
+ nodesets << pre_defined_namespaces.merge(raw_node.namespaces)
+ end
+ when :attribute
+ if @namespaces
+ nodesets << pre_defined_namespaces.merge(@namespaces)
+ else
+ nodesets << pre_defined_namespaces.merge(raw_node.element.namespaces)
+ end
end
end
+ nodesets
end
- nodeset = new_nodeset
-
when :parent
- nodeset = nodeset.collect{|n| n.parent}.compact
- #nodeset = expr(path_stack.dclone, nodeset.collect{|n| n.parent}.compact)
- node_types = ELEMENTS
-
- when :ancestor
- new_nodeset = []
- nodeset.each do |node|
- while node.parent
- node = node.parent
- new_nodeset << node unless new_nodeset.include? node
+ nodeset = step(path_stack) do
+ nodesets = []
+ nodeset.each do |node|
+ raw_node = node.raw_node
+ if raw_node.node_type == :attribute
+ parent = raw_node.element
+ else
+ parent = raw_node.parent
+ end
+ nodesets << [XPathNode.new(parent, position: 1)] if parent
end
+ nodesets
end
- nodeset = new_nodeset
- node_types = ELEMENTS
-
- when :ancestor_or_self
- new_nodeset = []
- nodeset.each do |node|
- if node.node_type == :element
- new_nodeset << node
- while ( node.parent )
- node = node.parent
- new_nodeset << node unless new_nodeset.include? node
+ when :ancestor
+ nodeset = step(path_stack) do
+ nodesets = []
+ # new_nodes = {}
+ nodeset.each do |node|
+ raw_node = node.raw_node
+ new_nodeset = []
+ while raw_node.parent
+ raw_node = raw_node.parent
+ # next if new_nodes.key?(node)
+ new_nodeset << XPathNode.new(raw_node,
+ position: new_nodeset.size + 1)
+ # new_nodes[node] = true
end
+ nodesets << new_nodeset unless new_nodeset.empty?
end
+ nodesets
end
- nodeset = new_nodeset
- node_types = ELEMENTS
-
- when :predicate
- new_nodeset = []
- subcontext = { :size => nodeset.size }
- pred = path_stack.shift
- nodeset.each_with_index { |node, index|
- subcontext[ :node ] = node
- subcontext[ :index ] = index+1
- pc = pred.dclone
- result = expr( pc, [node], subcontext )
- result = result[0] if result.kind_of? Array and result.length == 1
- if result.kind_of? Numeric
- new_nodeset << node if result == (index+1)
- elsif result.instance_of? Array
- if result.size > 0 and result.inject(false) {|k,s| s or k}
- new_nodeset << node if result.size > 0
+ when :ancestor_or_self
+ nodeset = step(path_stack) do
+ nodesets = []
+ # new_nodes = {}
+ nodeset.each do |node|
+ raw_node = node.raw_node
+ next unless raw_node.node_type == :element
+ new_nodeset = [XPathNode.new(raw_node, position: 1)]
+ # new_nodes[node] = true
+ while raw_node.parent
+ raw_node = raw_node.parent
+ # next if new_nodes.key?(node)
+ new_nodeset << XPathNode.new(raw_node,
+ position: new_nodeset.size + 1)
+ # new_nodes[node] = true
end
- else
- new_nodeset << node if result
+ nodesets << new_nodeset unless new_nodeset.empty?
end
- }
- nodeset = new_nodeset
-=begin
- predicate = path_stack.shift
- ns = nodeset.clone
- result = expr( predicate, ns )
- if result.kind_of? Array
- nodeset = result.zip(ns).collect{|m,n| n if m}.compact
- else
- nodeset = result ? nodeset : []
+ nodesets
end
-=end
-
when :descendant_or_self
- rv = descendant_or_self( path_stack, nodeset )
- path_stack.clear
- nodeset = rv
- node_types = ELEMENTS
-
+ nodeset = step(path_stack) do
+ descendant(nodeset, true)
+ end
when :descendant
- results = []
- nt = nil
- nodeset.each do |node|
- nt = node.node_type
- results += expr( path_stack.dclone.unshift( :descendant_or_self ),
- node.children ) if nt == :element or nt == :document
+ nodeset = step(path_stack) do
+ descendant(nodeset, false)
end
- nodeset = results
- node_types = ELEMENTS
-
when :following_sibling
- results = []
- nodeset.each do |node|
- next if node.parent.nil?
- all_siblings = node.parent.children
- current_index = all_siblings.index( node )
- following_siblings = all_siblings[ current_index+1 .. -1 ]
- results += expr( path_stack.dclone, following_siblings )
+ nodeset = step(path_stack) do
+ nodesets = []
+ nodeset.each do |node|
+ raw_node = node.raw_node
+ next unless raw_node.respond_to?(:parent)
+ next if raw_node.parent.nil?
+ all_siblings = raw_node.parent.children
+ current_index = all_siblings.index(raw_node)
+ following_siblings = all_siblings[(current_index + 1)..-1]
+ next if following_siblings.empty?
+ nodesets << following_siblings.collect.with_index do |sibling, i|
+ XPathNode.new(sibling, position: i + 1)
+ end
+ end
+ nodesets
end
- nodeset = results
-
when :preceding_sibling
- results = []
- nodeset.each do |node|
- next if node.parent.nil?
- all_siblings = node.parent.children
- current_index = all_siblings.index( node )
- preceding_siblings = all_siblings[ 0, current_index ].reverse
- results += preceding_siblings
+ nodeset = step(path_stack, order: :reverse) do
+ nodesets = []
+ nodeset.each do |node|
+ raw_node = node.raw_node
+ next unless raw_node.respond_to?(:parent)
+ next if raw_node.parent.nil?
+ all_siblings = raw_node.parent.children
+ current_index = all_siblings.index(raw_node)
+ preceding_siblings = all_siblings[0, current_index].reverse
+ next if preceding_siblings.empty?
+ nodesets << preceding_siblings.collect.with_index do |sibling, i|
+ XPathNode.new(sibling, position: i + 1)
+ end
+ end
+ nodesets
end
- nodeset = results
- node_types = ELEMENTS
-
when :preceding
- new_nodeset = []
- nodeset.each do |node|
- new_nodeset += preceding( node )
+ nodeset = step(path_stack, order: :reverse) do
+ unnode(nodeset) do |node|
+ preceding(node)
+ end
end
- nodeset = new_nodeset
- node_types = ELEMENTS
-
when :following
- new_nodeset = []
- nodeset.each do |node|
- new_nodeset += following( node )
- end
- nodeset = new_nodeset
- node_types = ELEMENTS
-
- when :namespace
- new_nodeset = []
- prefix = path_stack.shift
- nodeset.each do |node|
- if (node.node_type == :element or node.node_type == :attribute)
- if @namespaces
- namespaces = @namespaces
- elsif (node.node_type == :element)
- namespaces = node.namespaces
- else
- namespaces = node.element.namesapces
- end
- if (node.namespace == namespaces[prefix])
- new_nodeset << node
- end
+ nodeset = step(path_stack) do
+ unnode(nodeset) do |node|
+ following(node)
end
end
- nodeset = new_nodeset
-
when :variable
var_name = path_stack.shift
- return @variables[ var_name ]
+ return [@variables[var_name]]
# :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
# TODO: Special case for :or and :and -- not evaluate the right
@@ -384,6 +343,7 @@ module REXML
left = expr( path_stack.shift, nodeset.dup, context )
right = expr( path_stack.shift, nodeset.dup, context )
res = equality_relational_compare( left, op, right )
+ # trace(op, left, right, res)
return res
when :and
@@ -396,40 +356,37 @@ module REXML
res = equality_relational_compare( left, op, right )
return res
- when :div
- left = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
- right = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
- return (left / right)
-
- when :mod
- left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
- right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
- return (left % right)
-
- when :mult
- left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
- right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
- return (left * right)
-
- when :plus
- left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
- right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
- return (left + right)
-
- when :minus
- left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
- right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
- return (left - right)
-
+ when :div, :mod, :mult, :plus, :minus
+ left = expr(path_stack.shift, nodeset, context)
+ right = expr(path_stack.shift, nodeset, context)
+ left = unnode(left) if left.is_a?(Array)
+ right = unnode(right) if right.is_a?(Array)
+ left = Functions::number(left)
+ right = Functions::number(right)
+ case op
+ when :div
+ return left / right
+ when :mod
+ return left % right
+ when :mult
+ return left * right
+ when :plus
+ return left + right
+ when :minus
+ return left - right
+ else
+ raise "[BUG] Unexpected operator: <#{op.inspect}>"
+ end
when :union
left = expr( path_stack.shift, nodeset, context )
right = expr( path_stack.shift, nodeset, context )
+ left = unnode(left) if left.is_a?(Array)
+ right = unnode(right) if right.is_a?(Array)
return (left | right)
-
when :neg
res = expr( path_stack, nodeset, context )
- return -(res.to_f)
-
+ res = unnode(res) if res.is_a?(Array)
+ return -Functions.number(res)
when :not
when :function
func_name = path_stack.shift.tr('-','_')
@@ -438,53 +395,242 @@ module REXML
res = []
cont = context
- nodeset.each_with_index { |n, i|
+ nodeset.each_with_index do |node, i|
if subcontext
- subcontext[:node] = n
- subcontext[:index] = i
+ if node.is_a?(XPathNode)
+ subcontext[:node] = node.raw_node
+ subcontext[:index] = node.position
+ else
+ subcontext[:node] = node
+ subcontext[:index] = i
+ end
cont = subcontext
end
arg_clone = arguments.dclone
- args = arg_clone.collect { |arg|
- expr( arg, [n], cont )
- }
+ args = arg_clone.collect do |arg|
+ result = expr( arg, [node], cont )
+ result = unnode(result) if result.is_a?(Array)
+ result
+ end
Functions.context = cont
res << Functions.send( func_name, *args )
- }
+ end
return res
+ else
+ raise "[BUG] Unexpected path: <#{op.inspect}>: <#{path_stack.inspect}>"
end
end # while
return nodeset
+ # ensure
+ # leave(:expr, path_stack, nodeset)
end
+ def step(path_stack, any_type: :element, order: :forward)
+ nodesets = yield
+ begin
+ # enter(:step, path_stack, nodesets)
+ nodesets = node_test(path_stack, nodesets, any_type: any_type)
+ while path_stack[0] == :predicate
+ path_stack.shift # :predicate
+ predicate_expression = path_stack.shift.dclone
+ nodesets = evaluate_predicate(predicate_expression, nodesets)
+ end
+ if nodesets.size == 1
+ ordered_nodeset = nodesets[0]
+ else
+ raw_nodes = []
+ nodesets.each do |nodeset|
+ nodeset.each do |node|
+ if node.respond_to?(:raw_node)
+ raw_nodes << node.raw_node
+ else
+ raw_nodes << node
+ end
+ end
+ end
+ ordered_nodeset = sort(raw_nodes, order)
+ end
+ new_nodeset = []
+ ordered_nodeset.each do |node|
+ # TODO: Remove duplicated
+ new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1)
+ end
+ new_nodeset
+ # ensure
+ # leave(:step, path_stack, new_nodeset)
+ end
+ end
- ##########################################################
- # FIXME
- # The next two methods are BAD MOJO!
- # This is my achilles heel. If anybody thinks of a better
- # way of doing this, be my guest. This really sucks, but
- # it is a wonder it works at all.
- # ########################################################
+ def node_test(path_stack, nodesets, any_type: :element)
+ # enter(:node_test, path_stack, nodesets)
+ operator = path_stack.shift
+ case operator
+ when :qname
+ prefix = path_stack.shift
+ name = path_stack.shift
+ new_nodesets = nodesets.collect do |nodeset|
+ filter_nodeset(nodeset) do |node|
+ raw_node = node.raw_node
+ case raw_node.node_type
+ when :element
+ if prefix.nil?
+ raw_node.name == name
+ elsif prefix.empty?
+ if strict?
+ raw_node.name == name and raw_node.namespace == ""
+ else
+ # FIXME: This DOUBLES the time XPath searches take
+ ns = get_namespace(raw_node, prefix)
+ raw_node.name == name and raw_node.namespace == ns
+ end
+ else
+ # FIXME: This DOUBLES the time XPath searches take
+ ns = get_namespace(raw_node, prefix)
+ raw_node.name == name and raw_node.namespace == ns
+ end
+ when :attribute
+ if prefix.nil?
+ raw_node.name == name
+ elsif prefix.empty?
+ # FIXME: This DOUBLES the time XPath searches take
+ raw_node.name == name and
+ raw_node.namespace == raw_node.element.namespace
+ else
+ # FIXME: This DOUBLES the time XPath searches take
+ ns = get_namespace(raw_node.element, prefix)
+ raw_node.name == name and raw_node.namespace == ns
+ end
+ else
+ false
+ end
+ end
+ end
+ when :namespace
+ prefix = path_stack.shift
+ new_nodesets = nodesets.collect do |nodeset|
+ filter_nodeset(nodeset) do |node|
+ raw_node = node.raw_node
+ case raw_node.node_type
+ when :element
+ namespaces = @namespaces || raw_node.namespaces
+ raw_node.namespace == namespaces[prefix]
+ when :attribute
+ namespaces = @namespaces || raw_node.element.namespaces
+ raw_node.namespace == namespaces[prefix]
+ else
+ false
+ end
+ end
+ end
+ when :any
+ new_nodesets = nodesets.collect do |nodeset|
+ filter_nodeset(nodeset) do |node|
+ raw_node = node.raw_node
+ raw_node.node_type == any_type
+ end
+ end
+ when :comment
+ new_nodesets = nodesets.collect do |nodeset|
+ filter_nodeset(nodeset) do |node|
+ raw_node = node.raw_node
+ raw_node.node_type == :comment
+ end
+ end
+ when :text
+ new_nodesets = nodesets.collect do |nodeset|
+ filter_nodeset(nodeset) do |node|
+ raw_node = node.raw_node
+ raw_node.node_type == :text
+ end
+ end
+ when :processing_instruction
+ target = path_stack.shift
+ new_nodesets = nodesets.collect do |nodeset|
+ filter_nodeset(nodeset) do |node|
+ raw_node = node.raw_node
+ (raw_node.node_type == :processing_instruction) and
+ (target.empty? or (raw_node.target == target))
+ end
+ end
+ when :node
+ new_nodesets = nodesets.collect do |nodeset|
+ filter_nodeset(nodeset) do |node|
+ true
+ end
+ end
+ else
+ message = "[BUG] Unexpected node test: " +
+ "<#{operator.inspect}>: <#{path_stack.inspect}>"
+ raise message
+ end
+ new_nodesets
+ # ensure
+ # leave(:node_test, path_stack, new_nodesets)
+ end
- def descendant_or_self( path_stack, nodeset )
- rs = []
- d_o_s( path_stack, nodeset, rs )
- document_order(rs.flatten.compact)
- #rs.flatten.compact
+ def filter_nodeset(nodeset)
+ new_nodeset = []
+ nodeset.each do |node|
+ next unless yield(node)
+ new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1)
+ end
+ new_nodeset
end
- def d_o_s( p, ns, r )
- nt = nil
- ns.each_index do |i|
- n = ns[i]
- x = expr( p.dclone, [ n ] )
- nt = n.node_type
- d_o_s( p, n.children, x ) if nt == :element or nt == :document and n.children.size > 0
- r.concat(x) if x.size > 0
+ def evaluate_predicate(expression, nodesets)
+ # enter(:predicate, expression, nodesets)
+ new_nodesets = nodesets.collect do |nodeset|
+ new_nodeset = []
+ subcontext = { :size => nodeset.size }
+ nodeset.each_with_index do |node, index|
+ if node.is_a?(XPathNode)
+ subcontext[:node] = node.raw_node
+ subcontext[:index] = node.position
+ else
+ subcontext[:node] = node
+ subcontext[:index] = index + 1
+ end
+ result = expr(expression.dclone, [node], subcontext)
+ # trace(:predicate_evaluate, expression, node, subcontext, result)
+ result = result[0] if result.kind_of? Array and result.length == 1
+ if result.kind_of? Numeric
+ if result == node.position
+ new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1)
+ end
+ elsif result.instance_of? Array
+ if result.size > 0 and result.inject(false) {|k,s| s or k}
+ if result.size > 0
+ new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1)
+ end
+ end
+ else
+ if result
+ new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1)
+ end
+ end
+ end
+ new_nodeset
end
+ new_nodesets
+ # ensure
+ # leave(:predicate, new_nodesets)
+ end
+
+ def trace(*args)
+ indent = " " * @nest
+ puts("#{indent}#{args.inspect}")
end
+ def enter(tag, *args)
+ trace(:enter, tag, *args)
+ @nest += 1
+ end
+
+ def leave(tag, *args)
+ @nest -= 1
+ trace(:leave, tag, *args)
+ end
# Reorders an array of nodes so that they are in document order
# It tries to do this efficiently.
@@ -494,7 +640,7 @@ module REXML
# in and out of function calls. If I knew what the index of the nodes was,
# I wouldn't have to do this. Maybe add a document IDX for each node?
# Problems with mutable documents. Or, rewrite everything.
- def document_order( array_of_nodes )
+ def sort(array_of_nodes, order)
new_arry = []
array_of_nodes.each { |node|
node_idx = []
@@ -505,42 +651,68 @@ module REXML
end
new_arry << [ node_idx.reverse, node ]
}
- new_arry.sort{ |s1, s2| s1[0] <=> s2[0] }.collect{ |s| s[1] }
+ ordered = new_arry.sort_by do |index, node|
+ if order == :forward
+ index
+ else
+ -index
+ end
+ end
+ ordered.collect do |_index, node|
+ node
+ end
end
-
- def recurse( nodeset, &block )
- for node in nodeset
- yield node
- recurse( node, &block ) if node.node_type == :element
+ def descendant(nodeset, include_self)
+ nodesets = []
+ nodeset.each do |node|
+ new_nodeset = []
+ new_nodes = {}
+ descendant_recursive(node.raw_node, new_nodeset, new_nodes, include_self)
+ nodesets << new_nodeset unless new_nodeset.empty?
end
+ nodesets
end
+ def descendant_recursive(raw_node, new_nodeset, new_nodes, include_self)
+ if include_self
+ return if new_nodes.key?(raw_node)
+ new_nodeset << XPathNode.new(raw_node, position: new_nodeset.size + 1)
+ new_nodes[raw_node] = true
+ end
+ node_type = raw_node.node_type
+ if node_type == :element or node_type == :document
+ raw_node.children.each do |child|
+ descendant_recursive(child, new_nodeset, new_nodes, true)
+ end
+ end
+ end
# Builds a nodeset of all of the preceding nodes of the supplied node,
# in reverse document order
# preceding:: includes every element in the document that precedes this node,
# except for ancestors
- def preceding( node )
+ def preceding(node)
ancestors = []
- p = node.parent
- while p
- ancestors << p
- p = p.parent
+ parent = node.parent
+ while parent
+ ancestors << parent
+ parent = parent.parent
end
- acc = []
- p = preceding_node_of( node )
- while p
- if ancestors.include? p
- ancestors.delete(p)
+ precedings = []
+ preceding_node = preceding_node_of(node)
+ while preceding_node
+ if ancestors.include?(preceding_node)
+ ancestors.delete(preceding_node)
else
- acc << p
+ precedings << XPathNode.new(preceding_node,
+ position: precedings.size + 1)
end
- p = preceding_node_of( p )
+ preceding_node = preceding_node_of(preceding_node)
end
- acc
+ precedings
end
def preceding_node_of( node )
@@ -558,14 +730,15 @@ module REXML
psn
end
- def following( node )
- acc = []
- p = next_sibling_node( node )
- while p
- acc << p
- p = following_node_of( p )
+ def following(node)
+ followings = []
+ following_node = next_sibling_node(node)
+ while following_node
+ followings << XPathNode.new(following_node,
+ position: followings.size + 1)
+ following_node = following_node_of(following_node)
end
- acc
+ followings
end
def following_node_of( node )
@@ -587,13 +760,40 @@ module REXML
return psn
end
+ def child(nodeset)
+ nodesets = []
+ nodeset.each do |node|
+ raw_node = node.raw_node
+ node_type = raw_node.node_type
+ # trace(:child, node_type, node)
+ case node_type
+ when :element
+ nodesets << raw_node.children.collect.with_index do |child_node, i|
+ XPathNode.new(child_node, position: i + 1)
+ end
+ when :document
+ new_nodeset = []
+ raw_node.children.each do |child|
+ case child
+ when XMLDecl, Text
+ # Ignore
+ else
+ new_nodeset << XPathNode.new(child, position: new_nodeset.size + 1)
+ end
+ end
+ nodesets << new_nodeset unless new_nodeset.empty?
+ end
+ end
+ nodesets
+ end
+
def norm b
case b
when true, false
return b
when 'true', 'false'
return Functions::boolean( b )
- when /^\d+(\.\d+)?$/
+ when /^\d+(\.\d+)?$/, Numeric
return Functions::number( b )
else
return Functions::string( b )
@@ -601,11 +801,10 @@ module REXML
end
def equality_relational_compare( set1, op, set2 )
+ set1 = unnode(set1) if set1.is_a?(Array)
+ set2 = unnode(set2) if set2.is_a?(Array)
if set1.kind_of? Array and set2.kind_of? Array
- if set1.size == 1 and set2.size == 1
- set1 = set1[0]
- set2 = set2[0]
- elsif set1.size == 0 or set2.size == 0
+ if set1.size == 0 or set2.size == 0
nd = set1.size==0 ? set2 : set1
rv = nd.collect { |il| compare( il, op, nil ) }
return rv
@@ -636,15 +835,15 @@ module REXML
case b
when true, false
- return a.collect {|v| compare( Functions::boolean(v), op, b ) }
+ return unnode(a) {|v| compare( Functions::boolean(v), op, b ) }
when Numeric
- return a.collect {|v| compare( Functions::number(v), op, b )}
+ return unnode(a) {|v| compare( Functions::number(v), op, b )}
when /^\d+(\.\d+)?$/
b = Functions::number( b )
- return a.collect {|v| compare( Functions::number(v), op, b )}
+ return unnode(a) {|v| compare( Functions::number(v), op, b )}
else
b = Functions::string( b )
- return a.collect { |v| compare( Functions::string(v), op, b ) }
+ return unnode(a) { |v| compare( Functions::string(v), op, b ) }
end
else
# If neither is nodeset,
@@ -654,8 +853,10 @@ module REXML
# Else, convert to string
# Else
# Convert both to numbers and compare
- s1 = set1.to_s
- s2 = set2.to_s
+ set1 = unnode(set1) if set1.is_a?(Array)
+ set2 = unnode(set2) if set2.is_a?(Array)
+ s1 = Functions.string(set1)
+ s2 = Functions.string(set2)
if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false'
set1 = Functions::boolean( set1 )
set2 = Functions::boolean( set2 )
@@ -700,5 +901,34 @@ module REXML
false
end
end
+
+ def unnode(nodeset)
+ nodeset.collect do |node|
+ if node.is_a?(XPathNode)
+ unnoded = node.raw_node
+ else
+ unnoded = node
+ end
+ unnoded = yield(unnoded) if block_given?
+ unnoded
+ end
+ end
+ end
+
+ # @private
+ class XPathNode
+ attr_reader :raw_node, :context
+ def initialize(node, context=nil)
+ if node.is_a?(XPathNode)
+ @raw_node = node.raw_node
+ else
+ @raw_node = node
+ end
+ @context = context || {}
+ end
+
+ def position
+ @context[:position]
+ end
end
end
diff --git a/lib/rinda/rinda.rb b/lib/rinda/rinda.rb
index 36c3503aa8..e762286d3b 100644
--- a/lib/rinda/rinda.rb
+++ b/lib/rinda/rinda.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: false
require 'drb/drb'
-require 'thread'
##
# A module to implement the Linda distributed computing paradigm in Ruby.
diff --git a/lib/rinda/ring.rb b/lib/rinda/ring.rb
index 9b3f13eb93..948cfaf208 100644
--- a/lib/rinda/ring.rb
+++ b/lib/rinda/ring.rb
@@ -3,8 +3,7 @@
# Note: Rinda::Ring API is unstable.
#
require 'drb/drb'
-require 'rinda/rinda'
-require 'thread'
+require_relative 'rinda'
require 'ipaddr'
module Rinda
@@ -135,7 +134,6 @@ module Rinda
socket = Socket.new(addrinfo.pfamily, addrinfo.socktype,
addrinfo.protocol)
- @sockets << socket
if addrinfo.ipv4_multicast? or addrinfo.ipv6_multicast? then
if Socket.const_defined?(:SO_REUSEPORT) then
@@ -166,6 +164,11 @@ module Rinda
end
socket
+ rescue
+ socket = socket.close if socket
+ raise
+ ensure
+ @sockets << socket if socket
end
##
diff --git a/lib/rinda/tuplespace.rb b/lib/rinda/tuplespace.rb
index 3ce8d2984f..6a41a7ba75 100644
--- a/lib/rinda/tuplespace.rb
+++ b/lib/rinda/tuplespace.rb
@@ -1,8 +1,7 @@
# frozen_string_literal: false
require 'monitor'
-require 'thread'
require 'drb/drb'
-require 'rinda/rinda'
+require_relative 'rinda'
require 'forwardable'
module Rinda
diff --git a/lib/rss/0.9.rb b/lib/rss/0.9.rb
index d852a6a85e..219ccefcdb 100644
--- a/lib/rss/0.9.rb
+++ b/lib/rss/0.9.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rss/parser"
+require_relative "parser"
module RSS
diff --git a/lib/rss/1.0.rb b/lib/rss/1.0.rb
index fb63937c5e..c8f92fb54e 100644
--- a/lib/rss/1.0.rb
+++ b/lib/rss/1.0.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rss/parser"
+require_relative "parser"
module RSS
diff --git a/lib/rss/atom.rb b/lib/rss/atom.rb
index a232e358be..48c27330d0 100644
--- a/lib/rss/atom.rb
+++ b/lib/rss/atom.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require 'rss/parser'
+require_relative 'parser'
module RSS
##
@@ -37,10 +37,12 @@ module RSS
end
klass.class_eval do
class << self
+ # Returns the Atom URI W3C Namespace
def required_uri
URI
end
+ # Returns true
def need_parent?
true
end
@@ -92,9 +94,11 @@ module RSS
end
# The TextConstruct module is used to define a Text construct Atom element,
- # which is used to store small quantities of human-readable text
+ # which is used to store small quantities of human-readable text.
#
# The TextConstruct has a type attribute, e.g. text, html, xhtml
+ #
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#text.constructs
module TextConstruct
def self.append_features(klass)
super
@@ -122,6 +126,7 @@ module RSS
attr_writer :xhtml
+ # Returns or builds the XHTML content.
def xhtml
return @xhtml if @xhtml.nil?
if @xhtml.is_a?(XML::Element) and
@@ -135,11 +140,13 @@ module RSS
{"xmlns" => XHTML_URI}, children)
end
- # Returns true if type is "xhtml"
+ # Returns true if type is "xhtml".
def have_xml_content?
@type == "xhtml"
end
+ # Raises a MissingTagError or NotExpectedTagError
+ # if the element is not properly formatted.
def atom_validate(ignore_unknown_element, tags, uri)
if have_xml_content?
if @xhtml.nil?
@@ -163,10 +170,12 @@ module RSS
end
end
- # The PersonConstruct module is used to define a Person Atom element that can be
- # used to describe a person, corporation, or similar entity
+ # The PersonConstruct module is used to define a person Atom element that can be
+ # used to describe a person, corporation or similar entity.
+ #
+ # The PersonConstruct has a Name, Uri and Email child elements.
#
- # The PersonConstruct has a Name, Uri, and Email child elements
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#atomPersonConstruct
module PersonConstruct
# Adds attributes for name, uri, and email to the +klass+
@@ -187,19 +196,25 @@ module RSS
target.__send__("new_#{self.class.name.split(/::/).last.downcase}")
end
- # The name of the person or entity
+ # The name of the person or entity.
+ #
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.name
class Name < RSS::Element
include CommonModel
include ContentModel
end
- # The URI of the person or entity
+ # The URI of the person or entity.
+ #
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.uri
class Uri < RSS::Element
include CommonModel
include URIContentModel
end
- # The email of the person or entity
+ # The email of the person or entity.
+ #
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.email
class Email < RSS::Element
include CommonModel
include ContentModel
@@ -245,9 +260,28 @@ module RSS
end
end
- # Atom feed element
+ # Defines the top-level element of an Atom Feed Document.
+ # It consists of a number of children Entry elements,
+ # and has the following attributes:
#
- # A Feed has several metadata attributes in addition to a number of Entry child elements
+ # * author
+ # * categories
+ # * category
+ # * content
+ # * contributor
+ # * entries (aliased as items)
+ # * entry
+ # * generator
+ # * icon
+ # * id
+ # * link
+ # * logo
+ # * rights
+ # * subtitle
+ # * title
+ # * updated
+ #
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.feed
class Feed < RSS::Element
include RootElementMixin
include CommonModel
@@ -322,11 +356,23 @@ module RSS
end
end
+ # PersonConstruct that contains information regarding the author
+ # of a Feed or Entry.
+ #
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.author
class Author < RSS::Element
include CommonModel
include PersonConstruct
end
+ # Contains information about a category associated with a Feed or Entry.
+ # It has the following attributes:
+ #
+ # * term
+ # * scheme
+ # * label
+ #
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.category
class Category < RSS::Element
include CommonModel
@@ -344,11 +390,18 @@ module RSS
end
end
+ # PersonConstruct that contains information regarding the
+ # contributors of a Feed or Entry.
+ #
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.contributor
class Contributor < RSS::Element
include CommonModel
include PersonConstruct
end
+ # Contains information on the agent used to generate the feed.
+ #
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.generator
class Generator < RSS::Element
include CommonModel
include ContentModel
@@ -369,32 +422,34 @@ module RSS
end
end
- # Atom Icon element
+ # Defines an image that provides a visual identification for a eed.
+ # The image should have an aspect ratio of 1:1.
#
- # Image that provides a visual identification for the Feed. Image should have an aspect
- # ratio of 1:1
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.icon
class Icon < RSS::Element
include CommonModel
include URIContentModel
end
- # Atom ID element
+ # Defines the Universally Unique Identifier (UUID) for a Feed or Entry.
#
- # Universally Unique Identifier (UUID) for the Feed
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.id
class Id < RSS::Element
include CommonModel
include URIContentModel
end
- # Defines an Atom Link element
+ # Defines a reference to a Web resource. It has the following
+ # attributes:
#
- # A Link has the following attributes:
# * href
# * rel
# * type
# * hreflang
# * title
# * length
+ #
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.link
class Link < RSS::Element
include CommonModel
@@ -415,10 +470,10 @@ module RSS
end
end
- # Atom Logo element
+ # Defines an image that provides a visual identification for the Feed.
+ # The image should have an aspect ratio of 2:1 (horizontal:vertical).
#
- # Image that provides a visual identification for the Feed. Image should have an aspect
- # ratio of 2:1 (horizontal:vertical)
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.logo
class Logo < RSS::Element
include CommonModel
include URIContentModel
@@ -433,40 +488,60 @@ module RSS
end
end
- # Atom Rights element
+ # TextConstruct that contains copyright information regarding
+ # the content in an Entry or Feed. It should not be used to
+ # convey machine readable licensing information.
#
- # TextConstruct that contains copyright information regarding the content in an Entry or Feed
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.rights
class Rights < RSS::Element
include CommonModel
include TextConstruct
end
- # Atom Subtitle element
+ # TextConstruct that conveys a description or subtitle for a Feed.
#
- # TextConstruct that conveys a description or subtitle for a Feed
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.subtitle
class Subtitle < RSS::Element
include CommonModel
include TextConstruct
end
- # Atom Title element
+ # TextConstruct that conveys a description or title for a Feed or Entry.
#
- # TextConstruct that conveys a description or title for a feed or Entry
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.title
class Title < RSS::Element
include CommonModel
include TextConstruct
end
- # Atom Updated element
+ # DateConstruct indicating the most recent time when a Feed or
+ # Entry was modified in a way the publisher considers
+ # significant.
#
- # DateConstruct indicating the most recent time when an Entry or Feed was modified
- # in a way the publisher considers significant
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.updated
class Updated < RSS::Element
include CommonModel
include DateConstruct
end
- # Defines a child Atom Entry element for an Atom Feed
+ # Defines a child Atom Entry element of an Atom Feed element.
+ # It has the following attributes:
+ #
+ # * author
+ # * category
+ # * categories
+ # * content
+ # * contributor
+ # * id
+ # * link
+ # * published
+ # * rights
+ # * source
+ # * summary
+ # * title
+ # * updated
+ #
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.entry
class Entry < RSS::Element
include CommonModel
include DuplicateLinkChecker
@@ -490,9 +565,11 @@ module RSS
tag, URI, occurs, tag, *args)
end
- # Returns whether any of the following are true
+ # Returns whether any of the following are true:
+ #
# * There are any authors in the feed
- # * If the parent element has an author and the +check_parent+ parameter was given.
+ # * If the parent element has an author and the +check_parent+
+ # parameter was given.
# * There is a source element that has an author
def have_author?(check_parent=true)
authors.any? {|author| !author.to_s.empty?} or
@@ -520,9 +597,18 @@ module RSS
items.new_item
end
+ # Feed::Author
Author = Feed::Author
+ # Feed::Category
Category = Feed::Category
+ # Contains or links to the content of the Entry.
+ # It has the following attributes:
+ #
+ # * type
+ # * src
+ #
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.content
class Content < RSS::Element
include CommonModel
@@ -546,11 +632,15 @@ module RSS
content_setup
add_need_initialize_variable("xml")
+ # Returns the element content in XML.
attr_writer :xml
+
+ # Returns true if the element has inline XML content.
def have_xml_content?
inline_xhtml? or inline_other_xml?
end
+ # Returns or builds the element content in XML.
def xml
return @xml unless inline_xhtml?
return @xml if @xml.nil?
@@ -565,6 +655,7 @@ module RSS
{"xmlns" => XHTML_URI}, children)
end
+ # Returns the element content in XHTML.
def xhtml
if inline_xhtml?
xml
@@ -573,6 +664,9 @@ module RSS
end
end
+ # Raises a MissingAttributeError, NotAvailableValueError,
+ # MissingTagError or NotExpectedTagError if the element is
+ # not properly formatted.
def atom_validate(ignore_unknown_element, tags, uri)
if out_of_line?
raise MissingAttributeError.new(tag_name, "type") if @type.nil?
@@ -589,19 +683,27 @@ module RSS
end
end
+ # Returns true if the element contains inline content
+ # that has a text or HTML media type, or no media type at all.
def inline_text?
!out_of_line? and [nil, "text", "html"].include?(@type)
end
+ # Returns true if the element contains inline content that
+ # has a HTML media type.
def inline_html?
return false if out_of_line?
@type == "html" or mime_split == ["text", "html"]
end
+ # Returns true if the element contains inline content that
+ # has a XHTML media type.
def inline_xhtml?
!out_of_line? and @type == "xhtml"
end
+ # Returns true if the element contains inline content that
+ # has a MIME media type.
def inline_other?
return false if out_of_line?
media_type, subtype = mime_split
@@ -609,6 +711,8 @@ module RSS
true
end
+ # Returns true if the element contains inline content that
+ # has a text media type.
def inline_other_text?
return false unless inline_other?
return false if inline_other_xml?
@@ -618,6 +722,8 @@ module RSS
false
end
+ # Returns true if the element contains inline content that
+ # has a XML media type.
def inline_other_xml?
return false unless inline_other?
@@ -632,14 +738,18 @@ module RSS
false
end
+ # Returns true if the element contains inline content
+ # encoded in base64.
def inline_other_base64?
inline_other? and !inline_other_text? and !inline_other_xml?
end
+ # Returns true if the element contains linked content.
def out_of_line?
not @src.nil?
end
+ # Splits the type attribute into an array, e.g. ["text", "xml"]
def mime_split
media_type = subtype = nil
if /\A\s*([a-z]+)\/([a-z\+]+)\s*(?:;.*)?\z/i =~ @type.to_s
@@ -649,6 +759,7 @@ module RSS
[media_type, subtype]
end
+ # Returns true if the content needs to be encoded in base64.
def need_base64_encode?
inline_other_base64?
end
@@ -659,17 +770,43 @@ module RSS
end
end
+ # Feed::Contributor
Contributor = Feed::Contributor
+ # Feed::Id
Id = Feed::Id
+ # Feed::Link
Link = Feed::Link
+ # DateConstruct that usually indicates the time of the initial
+ # creation of an Entry.
+ #
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.published
class Published < RSS::Element
include CommonModel
include DateConstruct
end
+ # Feed::Rights
Rights = Feed::Rights
+ # Defines a Atom Source element. It has the following attributes:
+ #
+ # * author
+ # * category
+ # * categories
+ # * content
+ # * contributor
+ # * generator
+ # * icon
+ # * id
+ # * link
+ # * logo
+ # * rights
+ # * subtitle
+ # * title
+ # * updated
+ #
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.source
class Source < RSS::Element
include CommonModel
@@ -692,36 +829,71 @@ module RSS
tag, URI, occurs, tag, *args)
end
+ # Returns true if the Source element has an author.
def have_author?
!author.to_s.empty?
end
+ # Feed::Author
Author = Feed::Author
+ # Feed::Category
Category = Feed::Category
+ # Feed::Contributor
Contributor = Feed::Contributor
+ # Feed::Generator
Generator = Feed::Generator
+ # Feed::Icon
Icon = Feed::Icon
+ # Feed::Id
Id = Feed::Id
+ # Feed::Link
Link = Feed::Link
+ # Feed::Logo
Logo = Feed::Logo
+ # Feed::Rights
Rights = Feed::Rights
+ # Feed::Subtitle
Subtitle = Feed::Subtitle
+ # Feed::Title
Title = Feed::Title
+ # Feed::Updated
Updated = Feed::Updated
end
+ # TextConstruct that describes a summary of the Entry.
+ #
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.summary
class Summary < RSS::Element
include CommonModel
include TextConstruct
end
+ # Feed::Title
Title = Feed::Title
+ # Feed::Updated
Updated = Feed::Updated
end
end
- # Defines a top-level Atom Entry element
+ # Defines a top-level Atom Entry element,
+ # used as the document element of a stand-alone Atom Entry Document.
+ # It has the following attributes:
+ #
+ # * author
+ # * category
+ # * categories
+ # * content
+ # * contributor
+ # * id
+ # * link
+ # * published
+ # * rights
+ # * source
+ # * summary
+ # * title
+ # * updated
#
+ # Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.entry]
class Entry < RSS::Element
include RootElementMixin
include CommonModel
@@ -746,25 +918,26 @@ module RSS
tag, URI, occurs, tag, *args)
end
- # Creates a new Atom Entry element
+ # Creates a new Atom Entry element.
def initialize(version=nil, encoding=nil, standalone=nil)
super("1.0", version, encoding, standalone)
@feed_type = "atom"
@feed_subtype = "entry"
end
- # Returns the Entry in an array
+ # Returns the Entry in an array.
def items
[self]
end
- # sets up the +maker+ for constructing Entry elements
+ # Sets up the +maker+ for constructing Entry elements.
def setup_maker(maker)
maker = maker.maker if maker.respond_to?("maker")
super(maker)
end
- # Returns where there are any authors present or there is a source with an author
+ # Returns where there are any authors present or there is a
+ # source with an author.
def have_author?
authors.any? {|author| !author.to_s.empty?} or
(source and source.have_author?)
@@ -786,17 +959,29 @@ module RSS
maker.items.new_item
end
+ # Feed::Entry::Author
Author = Feed::Entry::Author
+ # Feed::Entry::Category
Category = Feed::Entry::Category
+ # Feed::Entry::Content
Content = Feed::Entry::Content
+ # Feed::Entry::Contributor
Contributor = Feed::Entry::Contributor
+ # Feed::Entry::Id
Id = Feed::Entry::Id
+ # Feed::Entry::Link
Link = Feed::Entry::Link
+ # Feed::Entry::Published
Published = Feed::Entry::Published
+ # Feed::Entry::Rights
Rights = Feed::Entry::Rights
+ # Feed::Entry::Source
Source = Feed::Entry::Source
+ # Feed::Entry::Summary
Summary = Feed::Entry::Summary
+ # Feed::Entry::Title
Title = Feed::Entry::Title
+ # Feed::Entry::Updated
Updated = Feed::Entry::Updated
end
end
diff --git a/lib/rss/content.rb b/lib/rss/content.rb
index d35311075a..78c18d103c 100644
--- a/lib/rss/content.rb
+++ b/lib/rss/content.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rss/rss"
+require_relative "rss"
module RSS
# The prefix for the Content XML namespace.
diff --git a/lib/rss/converter.rb b/lib/rss/converter.rb
index b92e35a051..d372e06725 100644
--- a/lib/rss/converter.rb
+++ b/lib/rss/converter.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rss/utils"
+require_relative "utils"
module RSS
diff --git a/lib/rss/dublincore.rb b/lib/rss/dublincore.rb
index 8d1a551947..85b836d3bf 100644
--- a/lib/rss/dublincore.rb
+++ b/lib/rss/dublincore.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rss/rss"
+require_relative "rss"
module RSS
# The prefix for the Dublin Core XML namespace.
@@ -161,4 +161,4 @@ end
require 'rss/dublincore/1.0'
require 'rss/dublincore/2.0'
-require 'rss/dublincore/atom'
+require_relative 'dublincore/atom'
diff --git a/lib/rss/dublincore/atom.rb b/lib/rss/dublincore/atom.rb
index 0b8b11e440..1cfcdec677 100644
--- a/lib/rss/dublincore/atom.rb
+++ b/lib/rss/dublincore/atom.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rss/atom"
+require_relative "../atom"
module RSS
module Atom
diff --git a/lib/rss/image.rb b/lib/rss/image.rb
index 6b86ec0e5b..837f7d18f4 100644
--- a/lib/rss/image.rb
+++ b/lib/rss/image.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
require 'rss/1.0'
-require 'rss/dublincore'
+require_relative 'dublincore'
module RSS
diff --git a/lib/rss/itunes.rb b/lib/rss/itunes.rb
index 827970c209..987b090f21 100644
--- a/lib/rss/itunes.rb
+++ b/lib/rss/itunes.rb
@@ -51,7 +51,7 @@ module RSS
ELEMENT_INFOS = [["author"],
["block", :yes_other],
- ["explicit", :yes_clean_other],
+ ["explicit", :explicit_clean_other],
["keywords", :csv],
["subtitle"],
["summary"]]
diff --git a/lib/rss/maker.rb b/lib/rss/maker.rb
index 33d285f6af..e32de81806 100644
--- a/lib/rss/maker.rb
+++ b/lib/rss/maker.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rss/rss"
+require_relative "rss"
module RSS
##
@@ -65,15 +65,15 @@ module RSS
end
end
-require "rss/maker/1.0"
-require "rss/maker/2.0"
-require "rss/maker/feed"
-require "rss/maker/entry"
-require "rss/maker/content"
-require "rss/maker/dublincore"
-require "rss/maker/slash"
-require "rss/maker/syndication"
-require "rss/maker/taxonomy"
-require "rss/maker/trackback"
-require "rss/maker/image"
-require "rss/maker/itunes"
+require_relative "maker/1.0"
+require_relative "maker/2.0"
+require_relative "maker/feed"
+require_relative "maker/entry"
+require_relative "maker/content"
+require_relative "maker/dublincore"
+require_relative "maker/slash"
+require_relative "maker/syndication"
+require_relative "maker/taxonomy"
+require_relative "maker/trackback"
+require_relative "maker/image"
+require_relative "maker/itunes"
diff --git a/lib/rss/maker/0.9.rb b/lib/rss/maker/0.9.rb
index 622a4c30b4..7f961b392e 100644
--- a/lib/rss/maker/0.9.rb
+++ b/lib/rss/maker/0.9.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
-require "rss/0.9"
+require_relative "../0.9"
-require "rss/maker/base"
+require_relative "base"
module RSS
module Maker
diff --git a/lib/rss/maker/1.0.rb b/lib/rss/maker/1.0.rb
index 3aee77e913..3934f9536c 100644
--- a/lib/rss/maker/1.0.rb
+++ b/lib/rss/maker/1.0.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
-require "rss/1.0"
+require_relative "../1.0"
-require "rss/maker/base"
+require_relative "base"
module RSS
module Maker
diff --git a/lib/rss/maker/2.0.rb b/lib/rss/maker/2.0.rb
index 1f77a014d1..43d00226b7 100644
--- a/lib/rss/maker/2.0.rb
+++ b/lib/rss/maker/2.0.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
-require "rss/2.0"
+require_relative "../2.0"
-require "rss/maker/0.9"
+require_relative "0.9"
module RSS
module Maker
diff --git a/lib/rss/maker/atom.rb b/lib/rss/maker/atom.rb
index e0cd7623c8..cdd1d8753e 100644
--- a/lib/rss/maker/atom.rb
+++ b/lib/rss/maker/atom.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
-require "rss/atom"
+require_relative "../atom"
-require "rss/maker/base"
+require_relative "base"
module RSS
module Maker
diff --git a/lib/rss/maker/base.rb b/lib/rss/maker/base.rb
index bc4ca84141..17537b7006 100644
--- a/lib/rss/maker/base.rb
+++ b/lib/rss/maker/base.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
require 'forwardable'
-require 'rss/rss'
+require_relative '../rss'
module RSS
module Maker
diff --git a/lib/rss/maker/content.rb b/lib/rss/maker/content.rb
index 3559a45ad0..b3f4e5036e 100644
--- a/lib/rss/maker/content.rb
+++ b/lib/rss/maker/content.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
-require 'rss/content'
-require 'rss/maker/1.0'
-require 'rss/maker/2.0'
+require_relative '../content'
+require_relative '1.0'
+require_relative '2.0'
module RSS
module Maker
diff --git a/lib/rss/maker/dublincore.rb b/lib/rss/maker/dublincore.rb
index 988209c045..beea3134b4 100644
--- a/lib/rss/maker/dublincore.rb
+++ b/lib/rss/maker/dublincore.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
-require 'rss/dublincore'
-require 'rss/maker/1.0'
+require_relative '../dublincore'
+require_relative '1.0'
module RSS
module Maker
diff --git a/lib/rss/maker/entry.rb b/lib/rss/maker/entry.rb
index f806cbcaae..ccdf9608ae 100644
--- a/lib/rss/maker/entry.rb
+++ b/lib/rss/maker/entry.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
-require "rss/maker/atom"
-require "rss/maker/feed"
+require_relative "atom"
+require_relative "feed"
module RSS
module Maker
diff --git a/lib/rss/maker/feed.rb b/lib/rss/maker/feed.rb
index fdef7ad643..72ee704d6a 100644
--- a/lib/rss/maker/feed.rb
+++ b/lib/rss/maker/feed.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rss/maker/atom"
+require_relative "atom"
module RSS
module Maker
diff --git a/lib/rss/maker/image.rb b/lib/rss/maker/image.rb
index 1957ba8689..e3e62d8b9e 100644
--- a/lib/rss/maker/image.rb
+++ b/lib/rss/maker/image.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
-require 'rss/image'
-require 'rss/maker/1.0'
-require 'rss/maker/dublincore'
+require_relative '../image'
+require_relative '1.0'
+require_relative 'dublincore'
module RSS
module Maker
diff --git a/lib/rss/maker/itunes.rb b/lib/rss/maker/itunes.rb
index cc1051ae0c..28cca32021 100644
--- a/lib/rss/maker/itunes.rb
+++ b/lib/rss/maker/itunes.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
-require 'rss/itunes'
-require 'rss/maker/2.0'
+require_relative '../itunes'
+require_relative '2.0'
module RSS
module Maker
@@ -13,8 +13,8 @@ module RSS
klass.def_other_element(full_name)
when :yes_other
def_yes_other_accessor(klass, full_name)
- when :yes_clean_other
- def_yes_clean_other_accessor(klass, full_name)
+ when :explicit_clean_other
+ def_explicit_clean_other_accessor(klass, full_name)
when :csv
def_csv_accessor(klass, full_name)
when :element, :attribute
@@ -43,11 +43,11 @@ module RSS
EOC
end
- def def_yes_clean_other_accessor(klass, full_name)
+ def def_explicit_clean_other_accessor(klass, full_name)
klass.def_other_element(full_name)
klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1)
def #{full_name}?
- Utils::YesCleanOther.parse(#{full_name})
+ Utils::ExplicitCleanOther.parse(#{full_name})
end
EOC
end
diff --git a/lib/rss/maker/slash.rb b/lib/rss/maker/slash.rb
index 3bd82d3057..473991903f 100644
--- a/lib/rss/maker/slash.rb
+++ b/lib/rss/maker/slash.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
-require 'rss/slash'
-require 'rss/maker/1.0'
+require_relative '../slash'
+require_relative '1.0'
module RSS
module Maker
diff --git a/lib/rss/maker/syndication.rb b/lib/rss/maker/syndication.rb
index 840b70229a..9fd0efe99e 100644
--- a/lib/rss/maker/syndication.rb
+++ b/lib/rss/maker/syndication.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
-require 'rss/syndication'
-require 'rss/maker/1.0'
+require_relative '../syndication'
+require_relative '1.0'
module RSS
module Maker
diff --git a/lib/rss/maker/taxonomy.rb b/lib/rss/maker/taxonomy.rb
index 76a2d1600d..f9858922da 100644
--- a/lib/rss/maker/taxonomy.rb
+++ b/lib/rss/maker/taxonomy.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
-require 'rss/taxonomy'
-require 'rss/maker/1.0'
-require 'rss/maker/dublincore'
+require_relative '../taxonomy'
+require_relative '1.0'
+require_relative 'dublincore'
module RSS
module Maker
diff --git a/lib/rss/maker/trackback.rb b/lib/rss/maker/trackback.rb
index f97691c608..f78b4058f9 100644
--- a/lib/rss/maker/trackback.rb
+++ b/lib/rss/maker/trackback.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
-require 'rss/trackback'
-require 'rss/maker/1.0'
-require 'rss/maker/2.0'
+require_relative '../trackback'
+require_relative '1.0'
+require_relative '2.0'
module RSS
module Maker
diff --git a/lib/rss/parser.rb b/lib/rss/parser.rb
index a9842e6d40..3a34d0adb4 100644
--- a/lib/rss/parser.rb
+++ b/lib/rss/parser.rb
@@ -2,8 +2,8 @@
require "forwardable"
require "open-uri"
-require "rss/rss"
-require "rss/xml"
+require_relative "rss"
+require_relative "xml"
module RSS
@@ -72,13 +72,31 @@ module RSS
end
end
- def parse(rss, do_validate=true, ignore_unknown_element=true,
- parser_class=default_parser)
+ def parse(rss, *args)
+ if args.last.is_a?(Hash)
+ options = args.pop
+ else
+ options = {}
+ end
+ do_validate = boolean_argument(args[0], options[:validate], true)
+ ignore_unknown_element =
+ boolean_argument(args[1], options[:ignore_unknown_element], true)
+ parser_class = args[2] || options[:parser_class] || default_parser
parser = new(rss, parser_class)
parser.do_validate = do_validate
parser.ignore_unknown_element = ignore_unknown_element
parser.parse
end
+
+ private
+ def boolean_argument(positioned_value, option_value, default)
+ value = positioned_value
+ if value.nil? and not option_value.nil?
+ value = option_value
+ end
+ value = default if value.nil?
+ value
+ end
end
def_delegators(:@parser, :parse, :rss,
diff --git a/lib/rss/rss.gemspec b/lib/rss/rss.gemspec
new file mode 100644
index 0000000000..669c6c1ad4
--- /dev/null
+++ b/lib/rss/rss.gemspec
@@ -0,0 +1,38 @@
+begin
+ require_relative "lib/rss"
+rescue LoadError
+ # for Ruby core repository
+ require_relative "rss"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "rss"
+ spec.version = RSS::VERSION
+ spec.authors = ["Kouhei Sutou"]
+ spec.email = ["kou@cozmixng.org"]
+
+ spec.summary = %q{Family of libraries that support various formats of XML "feeds".}
+ spec.description = %q{Family of libraries that support various formats of XML "feeds".}
+ spec.homepage = "https://github.com/ruby/rss"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile",
+ "bin/console", "bin/setup", "lib/rss.rb", "lib/rss/0.9.rb", "lib/rss/1.0.rb", "lib/rss/2.0.rb",
+ "lib/rss/atom.rb", "lib/rss/content.rb", "lib/rss/content/1.0.rb", "lib/rss/content/2.0.rb",
+ "lib/rss/converter.rb", "lib/rss/dublincore.rb", "lib/rss/dublincore/1.0.rb", "lib/rss/dublincore/2.0.rb",
+ "lib/rss/dublincore/atom.rb", "lib/rss/image.rb", "lib/rss/itunes.rb", "lib/rss/maker.rb",
+ "lib/rss/maker/0.9.rb", "lib/rss/maker/1.0.rb", "lib/rss/maker/2.0.rb", "lib/rss/maker/atom.rb",
+ "lib/rss/maker/base.rb", "lib/rss/maker/content.rb", "lib/rss/maker/dublincore.rb", "lib/rss/maker/entry.rb",
+ "lib/rss/maker/feed.rb", "lib/rss/maker/image.rb", "lib/rss/maker/itunes.rb", "lib/rss/maker/slash.rb",
+ "lib/rss/maker/syndication.rb", "lib/rss/maker/taxonomy.rb", "lib/rss/maker/trackback.rb",
+ "lib/rss/parser.rb", "lib/rss/rexmlparser.rb", "lib/rss/rss.rb", "lib/rss/slash.rb", "lib/rss/syndication.rb",
+ "lib/rss/taxonomy.rb", "lib/rss/trackback.rb", "lib/rss/utils.rb", "lib/rss/xml-stylesheet.rb",
+ "lib/rss/xml.rb", "lib/rss/xmlparser.rb", "lib/rss/xmlscanner.rb", "rss.gemspec"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+ spec.add_development_dependency "test-unit"
+end
diff --git a/lib/rss/rss.rb b/lib/rss/rss.rb
index d161366f92..daaf6793b9 100644
--- a/lib/rss/rss.rb
+++ b/lib/rss/rss.rb
@@ -63,9 +63,9 @@ end
require "English"
-require "rss/utils"
-require "rss/converter"
-require "rss/xml-stylesheet"
+require_relative "utils"
+require_relative "converter"
+require_relative "xml-stylesheet"
module RSS
@@ -374,12 +374,12 @@ EOC
end
end
- def yes_clean_other_attr_reader(*attrs)
+ def explicit_clean_other_attr_reader(*attrs)
attrs.each do |attr|
module_eval(<<-EOC, __FILE__, __LINE__ + 1)
attr_reader(:#{attr})
def #{attr}?
- YesCleanOther.parse(@#{attr})
+ ExplicitCleanOther.parse(@#{attr})
end
EOC
end
@@ -544,7 +544,7 @@ EOC
EOC
end
- def yes_clean_other_writer(name, disp_name=name)
+ def explicit_clean_other_writer(name, disp_name=name)
module_eval(<<-EOC, __FILE__, __LINE__ + 1)
def #{name}=(value)
value = (value ? "yes" : "no") if [true, false].include?(value)
@@ -596,11 +596,10 @@ EOC
def #{accessor_name}=(*args)
receiver = self.class.name
- warn("Warning:\#{caller.first.sub(/:in `.*'\z/, '')}: " \
- "Don't use `\#{receiver}\##{accessor_name} = XXX'/" \
+ warn("Don't use `\#{receiver}\##{accessor_name} = XXX'/" \
"`\#{receiver}\#set_#{accessor_name}(XXX)'. " \
"Those APIs are not sense of Ruby. " \
- "Use `\#{receiver}\##{plural_name} << XXX' instead of them.")
+ "Use `\#{receiver}\##{plural_name} << XXX' instead of them.", uplevel: 1)
if args.size == 1
@#{accessor_name}.push(args[0])
else
@@ -763,8 +762,8 @@ EOC
text_type_writer name, disp_name
when :content
content_writer name, disp_name
- when :yes_clean_other
- yes_clean_other_writer name, disp_name
+ when :explicit_clean_other
+ explicit_clean_other_writer name, disp_name
when :yes_other
yes_other_writer name, disp_name
when :csv
@@ -782,8 +781,8 @@ EOC
inherit_convert_attr_reader name
when :uri
uri_convert_attr_reader name
- when :yes_clean_other
- yes_clean_other_attr_reader name
+ when :explicit_clean_other
+ explicit_clean_other_attr_reader name
when :yes_other
yes_other_attr_reader name
when :csv
@@ -957,7 +956,7 @@ EOC
children = child
children.any? {|c| c.have_required_elements?}
else
- !child.to_s.empty?
+ not child.nil?
end
else
true
diff --git a/lib/rss/taxonomy.rb b/lib/rss/taxonomy.rb
index b7ea219e8c..50688ee6c1 100644
--- a/lib/rss/taxonomy.rb
+++ b/lib/rss/taxonomy.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: false
require "rss/1.0"
-require "rss/dublincore"
+require_relative "dublincore"
module RSS
# The prefix for the Taxonomy XML namespace.
diff --git a/lib/rss/utils.rb b/lib/rss/utils.rb
index ce7dbf1b02..9203df9a9b 100644
--- a/lib/rss/utils.rb
+++ b/lib/rss/utils.rb
@@ -125,16 +125,16 @@ module RSS
[true, false].include?(args[0]) and args[1].is_a?(Hash)
end
- module YesCleanOther
+ module ExplicitCleanOther
module_function
def parse(value)
if [true, false, nil].include?(value)
value
else
case value.to_s
- when /\Ayes\z/i
+ when /\Aexplicit|yes|true\z/i
true
- when /\Aclean\z/i
+ when /\Aclean|no|false\z/i
false
else
nil
diff --git a/lib/rss/xml-stylesheet.rb b/lib/rss/xml-stylesheet.rb
index be9cfaaf64..175c95fbcd 100644
--- a/lib/rss/xml-stylesheet.rb
+++ b/lib/rss/xml-stylesheet.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rss/utils"
+require_relative "utils"
module RSS
diff --git a/lib/rss/xml.rb b/lib/rss/xml.rb
index cda8668044..b74630295f 100644
--- a/lib/rss/xml.rb
+++ b/lib/rss/xml.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: false
-require "rss/utils"
+require_relative "utils"
module RSS
module XML
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 246956bb14..62fc54a202 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -7,10 +7,9 @@
#++
require 'rbconfig'
-require 'thread'
module Gem
- VERSION = '2.6.8'
+ VERSION = "3.0.3.1".freeze
end
# Must be first since it unloads the prelude from 1.9.2
@@ -39,7 +38,7 @@ require 'rubygems/errors'
# Further RubyGems documentation can be found at:
#
# * {RubyGems Guides}[http://guides.rubygems.org]
-# * {RubyGems API}[http://rubygems.rubyforge.org/rdoc] (also available from
+# * {RubyGems API}[http://www.rubydoc.info/github/rubygems/rubygems] (also available from
# <tt>gem server</tt>)
#
# == RubyGems Plugins
@@ -47,15 +46,14 @@ require 'rubygems/errors'
# As of RubyGems 1.3.2, RubyGems will load plugins installed in gems or
# $LOAD_PATH. Plugins must be named 'rubygems_plugin' (.rb, .so, etc) and
# placed at the root of your gem's #require_path. Plugins are discovered via
-# Gem::find_files then loaded. Take care when implementing a plugin as your
-# plugin file may be loaded multiple times if multiple versions of your gem
-# are installed.
+# Gem::find_files and then loaded.
#
-# For an example plugin, see the graph gem which adds a `gem graph` command.
+# For an example plugin, see the {Graph gem}[https://github.com/seattlerb/graph]
+# which adds a `gem graph` command.
#
# == RubyGems Defaults, Packaging
#
-# RubyGems defaults are stored in rubygems/defaults.rb. If you're packaging
+# RubyGems defaults are stored in lib/rubygems/defaults.rb. If you're packaging
# RubyGems or implementing Ruby you can change RubyGems' defaults.
#
# For RubyGems packagers, provide lib/rubygems/defaults/operating_system.rb
@@ -65,7 +63,7 @@ require 'rubygems/errors'
# override any defaults from lib/rubygems/defaults.rb.
#
# If you need RubyGems to perform extra work on install or uninstall, your
-# defaults override file can set pre and post install and uninstall hooks.
+# defaults override file can set pre/post install and uninstall hooks.
# See Gem::pre_install, Gem::pre_uninstall, Gem::post_install,
# Gem::post_uninstall.
#
@@ -106,6 +104,8 @@ require 'rubygems/errors'
#
# (If your name is missing, PLEASE let us know!)
#
+# == License
+#
# See {LICENSE.txt}[rdoc-ref:lib/rubygems/LICENSE.txt] for permissions.
#
# Thanks!
@@ -126,13 +126,14 @@ module Gem
/mingw/i,
/mswin/i,
/wince/i,
- ]
+ ].freeze
GEM_DEP_FILES = %w[
gem.deps.rb
+ gems.rb
Gemfile
Isolate
- ]
+ ].freeze
##
# Subdirectories in a gem repository
@@ -144,7 +145,7 @@ module Gem
extensions
gems
specifications
- ]
+ ].freeze
##
# Subdirectories in a gem repository for default gems
@@ -152,14 +153,14 @@ module Gem
REPOSITORY_DEFAULT_GEM_SUBDIRECTORIES = %w[
gems
specifications/default
- ]
+ ].freeze
##
# Exception classes used in a Gem.read_binary +rescue+ statement. Not all of
# these are defined in Ruby 1.8.7, hence the need for this convoluted setup.
READ_BINARY_ERRORS = begin
- read_binary_errors = [Errno::EACCES]
+ read_binary_errors = [Errno::EACCES, Errno::EROFS, Errno::ENOSYS]
read_binary_errors << Errno::ENOTSUP if Errno.const_defined?(:ENOTSUP)
read_binary_errors
end.freeze
@@ -169,11 +170,13 @@ module Gem
# these are defined in Ruby 1.8.7.
WRITE_BINARY_ERRORS = begin
- write_binary_errors = []
+ write_binary_errors = [Errno::ENOSYS]
write_binary_errors << Errno::ENOTSUP if Errno.const_defined?(:ENOTSUP)
write_binary_errors
end.freeze
+ USE_BUNDLER_FOR_GEMDEPS = !ENV['DONT_USE_BUNDLER_FOR_GEMDEPS'] # :nodoc:
+
@@win_platform = nil
@configuration = nil
@@ -199,7 +202,7 @@ module Gem
# activation succeeded or wasn't needed because it was already
# activated. Returns false if it can't find the path in a gem.
- def self.try_activate path
+ def self.try_activate(path)
# finds the _latest_ version... regardless of loaded specs and their deps
# if another gem had a requirement that would mean we shouldn't
# activate the latest version, then either it would already be activated
@@ -234,6 +237,7 @@ module Gem
def self.finish_resolve(request_set=Gem::RequestSet.new)
request_set.import Gem::Specification.unresolved_deps.values
+ request_set.import Gem.loaded_specs.values.map {|s| Gem::Dependency.new(s.name, s.version) }
request_set.resolve_current.each do |s|
s.full_spec.activate
@@ -242,7 +246,7 @@ module Gem
##
# Find the full path to the executable for gem +name+. If the +exec_name+
- # is not given, the gem's default_executable is chosen, otherwise the
+ # is not given, an exception will be raised, otherwise the
# specified executable's path is returned. +requirements+ allows
# you to specify specific gem versions.
@@ -258,7 +262,7 @@ module Gem
find_spec_for_exe(name, exec_name, requirements).bin_file exec_name
end
- def self.find_spec_for_exe name, exec_name, requirements
+ def self.find_spec_for_exe(name, exec_name, requirements)
dep = Gem::Dependency.new name, requirements
loaded = Gem.loaded_specs[name]
@@ -267,15 +271,15 @@ module Gem
specs = dep.matching_specs(true)
- raise Gem::GemNotFoundException,
- "can't find gem #{dep}" if specs.empty?
-
specs = specs.find_all { |spec|
spec.executables.include? exec_name
} if exec_name
unless spec = specs.first
- msg = "can't find gem #{name} (#{requirements}) with executable #{exec_name}"
+ msg = "can't find gem #{dep} with executable #{exec_name}"
+ if name == "bundler" && bundler_message = Gem::BundlerVersionFinder.missing_version_message
+ msg = bundler_message
+ end
raise Gem::GemNotFoundException, msg
end
@@ -285,7 +289,7 @@ module Gem
##
# Find the full path to the executable for gem +name+. If the +exec_name+
- # is not given, the gem's default_executable is chosen, otherwise the
+ # is not given, an exception will be raised, otherwise the
# specified executable's path is returned. +requirements+ allows
# you to specify specific gem versions.
#
@@ -294,9 +298,12 @@ module Gem
#
# This method should *only* be used in bin stub files.
- def self.activate_bin_path name, exec_name, requirement # :nodoc:
+ def self.activate_bin_path(name, exec_name, requirement) # :nodoc:
spec = find_spec_for_exe name, exec_name, [requirement]
- Gem::LOADED_SPECS_MUTEX.synchronize { spec.activate }
+ Gem::LOADED_SPECS_MUTEX.synchronize do
+ spec.activate
+ finish_resolve
+ end
spec.bin_file exec_name
end
@@ -355,7 +362,6 @@ module Gem
# package is not available as a gem, return nil.
def self.datadir(gem_name)
-# TODO: deprecate
spec = @loaded_specs[gem_name]
return nil if spec.nil?
spec.datadir
@@ -435,7 +441,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
#
# World-writable directories will never be created.
- def self.ensure_gem_subdirectories dir = Gem.dir, mode = nil
+ def self.ensure_gem_subdirectories(dir = Gem.dir, mode = nil)
ensure_subdirectories(dir, mode, REPOSITORY_SUBDIRECTORIES)
end
@@ -448,11 +454,11 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
#
# World-writable directories will never be created.
- def self.ensure_default_gem_subdirectories dir = Gem.dir, mode = nil
+ def self.ensure_default_gem_subdirectories(dir = Gem.dir, mode = nil)
ensure_subdirectories(dir, mode, REPOSITORY_DEFAULT_GEM_SUBDIRECTORIES)
end
- def self.ensure_subdirectories dir, mode, subdirs # :nodoc:
+ def self.ensure_subdirectories(dir, mode, subdirs) # :nodoc:
old_umask = File.umask
File.umask old_umask | 002
@@ -476,7 +482,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'] then
+ if 'no' == RbConfig::CONFIG['ENABLE_SHARED']
"#{ruby_api_version}-static"
else
ruby_api_version
@@ -513,9 +519,10 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
return files
end
- def self.find_files_from_load_path glob # :nodoc:
+ def self.find_files_from_load_path(glob) # :nodoc:
+ glob_with_suffixes = "#{glob}#{Gem.suffix_pattern}"
$LOAD_PATH.map { |load_path|
- Dir["#{File.expand_path glob, load_path}#{Gem.suffix_pattern}"]
+ Gem::Util.glob_files_in_dir(glob_with_suffixes, load_path)
}.flatten.select { |file| File.file? file.untaint }
end
@@ -565,20 +572,9 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
#++
def self.find_home
- windows = File::ALT_SEPARATOR
- if not windows or RUBY_VERSION >= '1.9' then
- File.expand_path "~"
- else
- ['HOME', 'USERPROFILE'].each do |key|
- return File.expand_path ENV[key] if ENV[key]
- end
-
- if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
- File.expand_path "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}"
- end
- end
+ Dir.home.dup
rescue
- if windows then
+ if Gem.win_platform?
File.expand_path File.join(ENV['HOMEDRIVE'] || ENV['SystemDrive'], '/')
else
File.expand_path "/"
@@ -587,32 +583,44 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
private_class_method :find_home
- # FIXME deprecate these in 3.0
+ # TODO: remove in RubyGems 4.0
##
# Zlib::GzipReader wrapper that unzips +data+.
def self.gunzip(data)
- require 'rubygems/util'
Gem::Util.gunzip data
end
+ class << self
+ extend Gem::Deprecate
+ deprecate :gunzip, "Gem::Util.gunzip", 2018, 12
+ end
+
##
# Zlib::GzipWriter wrapper that zips +data+.
def self.gzip(data)
- require 'rubygems/util'
Gem::Util.gzip data
end
+ class << self
+ extend Gem::Deprecate
+ deprecate :gzip, "Gem::Util.gzip", 2018, 12
+ end
+
##
# A Zlib::Inflate#inflate wrapper
def self.inflate(data)
- require 'rubygems/util'
Gem::Util.inflate data
end
+ class << self
+ extend Gem::Deprecate
+ deprecate :inflate, "Gem::Util.inflate", 2018, 12
+ end
+
##
# Top level install helper method. Allows you to install gems interactively:
#
@@ -621,7 +629,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Fetching: minitest-3.0.1.gem (100%)
# => [#<Gem::Specification:0x1013b4528 @name="minitest", ...>]
- def self.install name, version = Gem::Requirement.default, *options
+ def self.install(name, version = Gem::Requirement.default, *options)
require "rubygems/dependency_installer"
inst = Gem::DependencyInstaller.new(*options)
inst.install name, version
@@ -639,7 +647,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
## Set the default RubyGems API host.
- def self.host= host
+ def self.host=(host)
# TODO: move to utils
@host = host
end
@@ -667,43 +675,31 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
return if @yaml_loaded
return unless defined?(gem)
- test_syck = ENV['TEST_SYCK']
-
- # Only Ruby 1.8 and 1.9 have syck
- test_syck = false unless /^1\./ =~ RUBY_VERSION
+ begin
+ gem 'psych', '>= 2.0.0'
+ rescue Gem::LoadError
+ # It's OK if the user does not have the psych gem installed. We will
+ # attempt to require the stdlib version
+ end
- unless test_syck
- begin
- gem 'psych', '>= 1.2.1'
- rescue Gem::LoadError
- # It's OK if the user does not have the psych gem installed. We will
- # attempt to require the stdlib version
+ begin
+ # Try requiring the gem version *or* stdlib version of psych.
+ require 'psych'
+ rescue ::LoadError
+ # If we can't load psych, thats fine, go on.
+ else
+ # If 'yaml' has already been required, then we have to
+ # be sure to switch it over to the newly loaded psych.
+ if defined?(YAML::ENGINE) && YAML::ENGINE.yamler != "psych"
+ YAML::ENGINE.yamler = "psych"
end
- begin
- # Try requiring the gem version *or* stdlib version of psych.
- require 'psych'
- rescue ::LoadError
- # If we can't load psych, thats fine, go on.
- else
- # If 'yaml' has already been required, then we have to
- # be sure to switch it over to the newly loaded psych.
- if defined?(YAML::ENGINE) && YAML::ENGINE.yamler != "psych"
- YAML::ENGINE.yamler = "psych"
- end
-
- require 'rubygems/psych_additions'
- require 'rubygems/psych_tree'
- end
+ require 'rubygems/psych_additions'
+ require 'rubygems/psych_tree'
end
require 'yaml'
-
- # If we're supposed to be using syck, then we may have to force
- # activate it via the YAML::ENGINE API.
- if test_syck and defined?(YAML::ENGINE)
- YAML::ENGINE.yamler = "syck" unless YAML::ENGINE.syck?
- end
+ require 'rubygems/safe_yaml'
# Now that we're sure some kind of yaml library is loaded, pull
# in our hack to deal with Syck's DefaultKey ugliness.
@@ -714,9 +710,20 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
##
# The file name and line number of the caller of the caller of this method.
+ #
+ # +depth+ is how many layers up the call stack it should go.
+ #
+ # e.g.,
+ #
+ # def a; Gem.location_of_caller; end
+ # a #=> ["x.rb", 2] # (it'll vary depending on file name and line number)
+ #
+ # def b; c; end
+ # def c; Gem.location_of_caller(2); end
+ # b #=> ["x.rb", 6] # (it'll vary depending on file name and line number)
- def self.location_of_caller
- caller[1] =~ /(.*?):(\d+).*?$/i
+ def self.location_of_caller(depth = 1)
+ caller[depth] =~ /(.*?):(\d+).*?$/i
file = $1
lineno = $2.to_i
@@ -829,7 +836,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
if prefix != File.expand_path(RbConfig::CONFIG['sitelibdir']) and
prefix != File.expand_path(RbConfig::CONFIG['libdir']) and
- 'lib' == File.basename(RUBYGEMS_DIR) then
+ 'lib' == File.basename(RUBYGEMS_DIR)
prefix
end
end
@@ -845,19 +852,19 @@ 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 path, 'rb+' do |f|
+ File.open path, 'rb+' do |f|
f.flock(File::LOCK_EX)
f.read
end
rescue *READ_BINARY_ERRORS
- open path, 'rb' do |f|
+ File.open path, 'rb' do |f|
f.read
end
rescue Errno::ENOLCK # NFS
if Thread.main != Thread.current
raise
else
- open path, 'rb' do |f|
+ File.open path, 'rb' do |f|
f.read
end
end
@@ -887,7 +894,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# The path to the running Ruby interpreter.
def self.ruby
- if @ruby.nil? then
+ if @ruby.nil?
@ruby = File.join(RbConfig::CONFIG['bindir'],
"#{RbConfig::CONFIG['ruby_install_name']}#{RbConfig::CONFIG['EXEEXT']}")
@@ -916,7 +923,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
##
# Returns the latest release-version specification for the gem +name+.
- def self.latest_spec_for name
+ def self.latest_spec_for(name)
dependency = Gem::Dependency.new name
fetcher = Gem::SpecFetcher.fetcher
spec_tuples, = fetcher.spec_for_dependency dependency
@@ -937,7 +944,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
##
# Returns the version of the latest release-version of gem +name+
- def self.latest_version_for name
+ def self.latest_version_for(name)
spec = latest_spec_for name
spec and spec.version
end
@@ -949,10 +956,15 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
return @ruby_version if defined? @ruby_version
version = RUBY_VERSION.dup
- if defined?(RUBY_PATCHLEVEL) && RUBY_PATCHLEVEL != -1 then
+ if defined?(RUBY_PATCHLEVEL) && RUBY_PATCHLEVEL != -1
version << ".#{RUBY_PATCHLEVEL}"
- elsif defined?(RUBY_REVISION) then
- version << ".dev.#{RUBY_REVISION}"
+ elsif defined?(RUBY_DESCRIPTION)
+ if RUBY_ENGINE == "ruby"
+ desc = RUBY_DESCRIPTION[/\Aruby #{Regexp.quote(RUBY_VERSION)}([^ ]+) /, 1]
+ else
+ desc = RUBY_DESCRIPTION[/\A#{RUBY_ENGINE} #{Regexp.quote(RUBY_ENGINE_VERSION)} \(#{RUBY_VERSION}([^ ]+)\) /, 1]
+ end
+ version << ".#{desc}" if desc
end
@ruby_version = Gem::Version.new version
@@ -982,7 +994,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# DOC: This comment is not documentation about the method itself, it's
# more of a code comment about the implementation.
- def self.sources= new_sources
+ def self.sources=(new_sources)
if !new_sources
@sources = nil
else
@@ -1059,7 +1071,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Is this a windows platform?
def self.win_platform?
- if @@win_platform.nil? then
+ if @@win_platform.nil?
ruby_platform = RbConfig::CONFIG['host_os']
@@win_platform = !!WIN_PATTERNS.find { |r| ruby_platform =~ r }
end
@@ -1070,7 +1082,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
##
# Load +plugins+ as Ruby files
- def self.load_plugin_files plugins # :nodoc:
+ def self.load_plugin_files(plugins) # :nodoc:
plugins.each do |plugin|
# Skip older versions of the GemCutter plugin: Its commands are in
@@ -1107,8 +1119,9 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
path = "rubygems_plugin"
files = []
+ glob = "#{path}#{Gem.suffix_pattern}"
$LOAD_PATH.each do |load_path|
- globbed = Dir["#{File.expand_path path, load_path}#{Gem.suffix_pattern}"]
+ globbed = Gem::Util.glob_files_in_dir(glob, load_path)
globbed.each do |load_path_file|
files << load_path_file if File.file?(load_path_file.untaint)
@@ -1138,7 +1151,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# execution of arbitrary code when used from directories outside your
# control.
- def self.use_gemdeps path = nil
+ def self.use_gemdeps(path = nil)
raise_exception = path
path ||= ENV['RUBYGEMS_GEMDEPS']
@@ -1146,9 +1159,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
path = path.dup
- if path == "-" then
- require 'rubygems/util'
-
+ if path == "-"
Gem::Util.traverse_parents Dir.pwd do |directory|
dep_file = GEM_DEP_FILES.find { |f| File.file?(f) }
@@ -1161,31 +1172,52 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
path.untaint
- unless File.file? path then
+ unless File.file? path
return unless raise_exception
raise ArgumentError, "Unable to find gem dependencies file at #{path}"
end
- rs = Gem::RequestSet.new
- @gemdeps = rs.load_gemdeps path
+ if USE_BUNDLER_FOR_GEMDEPS
+
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path(path)
+ require 'rubygems/user_interaction'
+ Gem::DefaultUserInteraction.use_ui(ui) do
+ require "bundler"
+ @gemdeps = Bundler.setup
+ Bundler.ui = nil
+ @gemdeps.requested_specs.map(&:to_spec).sort_by(&:name)
+ end
+
+ else
+
+ rs = Gem::RequestSet.new
+ @gemdeps = rs.load_gemdeps path
+
+ rs.resolve_current.map do |s|
+ s.full_spec.tap(&:activate)
+ end
- rs.resolve_current.map do |s|
- sp = s.full_spec
- sp.activate
- sp
end
- rescue Gem::LoadError, Gem::UnsatisfiableDependencyError => e
- warn e.message
- warn "You may need to `gem install -g` to install missing gems"
- warn ""
+ rescue => e
+ case e
+ when Gem::LoadError, Gem::UnsatisfiableDependencyError, (defined?(Bundler::GemNotFound) ? Bundler::GemNotFound : Gem::LoadError)
+ warn e.message
+ warn "You may need to `gem install -g` to install missing gems"
+ warn ""
+ else
+ raise
+ end
end
class << self
##
- # TODO remove with RubyGems 3.0
+ # TODO remove with RubyGems 4.0
alias detect_gemdeps use_gemdeps # :nodoc:
+
+ extend Gem::Deprecate
+ deprecate :detect_gemdeps, "Gem.use_gemdeps", 2018, 12
end
# FIX: Almost everywhere else we use the `def self.` way of defining class
@@ -1224,6 +1256,8 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
prefix_pattern = /^(#{prefix_group})/
end
+ suffix_pattern = /#{Regexp.union(Gem.suffixes)}\z/
+
spec.files.each do |file|
if new_format
file = file.sub(prefix_pattern, "")
@@ -1231,6 +1265,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
end
@path_to_default_spec_map[file] = spec
+ @path_to_default_spec_map[file.sub(suffix_pattern, "")] = spec
end
end
@@ -1238,11 +1273,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Find a Gem::Specification of default gem from +path+
def find_unresolved_default_spec(path)
- Gem.suffixes.each do |suffix|
- spec = @path_to_default_spec_map["#{path}#{suffix}"]
- return spec if spec
- end
- nil
+ @path_to_default_spec_map[path]
end
##
@@ -1311,12 +1342,12 @@ 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}/"
+ MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/".freeze
+ autoload :BundlerVersionFinder, 'rubygems/bundler_version_finder'
autoload :ConfigFile, 'rubygems/config_file'
autoload :Dependency, 'rubygems/dependency'
autoload :DependencyList, 'rubygems/dependency_list'
- autoload :DependencyResolver, 'rubygems/resolver'
autoload :Installer, 'rubygems/installer'
autoload :Licenses, 'rubygems/util/licenses'
autoload :PathSupport, 'rubygems/path_support'
@@ -1328,6 +1359,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
autoload :SourceList, 'rubygems/source_list'
autoload :SpecFetcher, 'rubygems/spec_fetcher'
autoload :Specification, 'rubygems/specification'
+ autoload :Util, 'rubygems/util'
autoload :Version, 'rubygems/version'
require "rubygems/specification"
@@ -1336,25 +1368,22 @@ end
require 'rubygems/exceptions'
# REFACTOR: This should be pulled out into some kind of hacks file.
-gem_preluded = Gem::GEM_PRELUDE_SUCKAGE and defined? Gem
-unless gem_preluded then # TODO: remove guard after 1.9.2 dropped
+begin
+ ##
+ # Defaults the operating system (or packager) wants to provide for RubyGems.
+
+ require 'rubygems/defaults/operating_system'
+rescue LoadError
+end
+
+if defined?(RUBY_ENGINE)
begin
##
- # Defaults the operating system (or packager) wants to provide for RubyGems.
+ # Defaults the Ruby implementation wants to provide for RubyGems
- require 'rubygems/defaults/operating_system'
+ require "rubygems/defaults/#{RUBY_ENGINE}"
rescue LoadError
end
-
- if defined?(RUBY_ENGINE) then
- begin
- ##
- # Defaults the Ruby implementation wants to provide for RubyGems
-
- require "rubygems/defaults/#{RUBY_ENGINE}"
- rescue LoadError
- end
- end
end
##
@@ -1363,5 +1392,6 @@ Gem::Specification.load_defaults
require 'rubygems/core_ext/kernel_gem'
require 'rubygems/core_ext/kernel_require'
+require 'rubygems/core_ext/kernel_warn'
Gem.use_gemdeps
diff --git a/lib/rubygems/LICENSE.txt b/lib/rubygems/LICENSE.txt
deleted file mode 100644
index 8a0a51dec1..0000000000
--- a/lib/rubygems/LICENSE.txt
+++ /dev/null
@@ -1,54 +0,0 @@
-RubyGems is copyrighted free software by Chad Fowler, Rich Kilmer, Jim
-Weirich and others. You can redistribute it and/or modify it under
-either the terms of the MIT license (see the file MIT.txt), or the
-conditions below:
-
-1. You may make and give away verbatim copies of the source form of the
- software without restriction, provided that you duplicate all of the
- original copyright notices and associated disclaimers.
-
-2. You may modify your copy of the software in any way, provided that
- you do at least ONE of the following:
-
- a. place your modifications in the Public Domain or otherwise
- make them Freely Available, such as by posting said
- modifications to Usenet or an equivalent medium, or by allowing
- the author to include your modifications in the software.
-
- b. use the modified software only within your corporation or
- organization.
-
- c. give non-standard executables non-standard names, with
- instructions on where to get the original software distribution.
-
- d. make other distribution arrangements with the author.
-
-3. You may distribute the software in object code or executable
- form, provided that you do at least ONE of the following:
-
- a. distribute the executables and library files of the software,
- together with instructions (in the manual page or equivalent)
- on where to get the original distribution.
-
- b. accompany the distribution with the machine-readable source of
- the software.
-
- c. give non-standard executables non-standard names, with
- instructions on where to get the original software distribution.
-
- d. make other distribution arrangements with the author.
-
-4. You may modify and include the part of the software into any other
- software (possibly commercial).
-
-5. The scripts and library files supplied as input to or produced as
- output from the software do not automatically fall under the
- copyright of the software, but belong to whomever generated them,
- and may be sold commercially, and may be aggregated with this
- software.
-
-6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE.
-
diff --git a/lib/rubygems/available_set.rb b/lib/rubygems/available_set.rb
index 49b5d5fd06..8334a73ecb 100644
--- a/lib/rubygems/available_set.rb
+++ b/lib/rubygems/available_set.rb
@@ -103,7 +103,7 @@ class Gem::AvailableSet
# Other options are :shallow for only direct development dependencies of the
# gems in this set or :all for all development dependencies.
- def to_request_set development = :none
+ def to_request_set(development = :none)
request_set = Gem::RequestSet.new
request_set.development = :all == development
diff --git a/lib/rubygems/basic_specification.rb b/lib/rubygems/basic_specification.rb
index 5aed17437e..d1878021ba 100644
--- a/lib/rubygems/basic_specification.rb
+++ b/lib/rubygems/basic_specification.rb
@@ -65,13 +65,13 @@ class Gem::BasicSpecification
##
# Return true if this spec can require +file+.
- def contains_requirable_file? file
- if @ignored then
+ def contains_requirable_file?(file)
+ if @ignored
return false
- elsif missing_extensions? then
+ elsif missing_extensions?
@ignored = true
- warn "Ignoring #{full_name} because its extensions are not built. " +
+ warn "Ignoring #{full_name} because its extensions are not built. " +
"Try: gem pristine #{name} --version #{version}"
return false
end
@@ -124,7 +124,7 @@ class Gem::BasicSpecification
# default Ruby platform.
def full_name
- if platform == Gem::Platform::RUBY or platform.nil? then
+ if platform == Gem::Platform::RUBY or platform.nil?
"#{name}-#{version}".dup.untaint
else
"#{name}-#{version}-#{platform}".dup.untaint
@@ -152,7 +152,7 @@ class Gem::BasicSpecification
# The path to the data directory for this gem.
def datadir
-# TODO: drop the extra ", gem_name" which is uselessly redundant
+ # TODO: drop the extra ", gem_name" which is uselessly redundant
File.expand_path(File.join(gems_dir, full_name, "data", name)).untaint
end
@@ -160,8 +160,8 @@ class Gem::BasicSpecification
# Full path of the target library file.
# If the file is not in this gem, return nil.
- def to_fullpath path
- if activated? then
+ def to_fullpath(path)
+ if activated?
@paths_map ||= {}
@paths_map[path] ||=
begin
@@ -249,7 +249,7 @@ class Gem::BasicSpecification
def source_paths
paths = raw_require_paths.dup
- if have_extensions? then
+ if have_extensions?
ext_dirs = extensions.map do |extension|
extension.split(File::SEPARATOR, 2).first
end.uniq
@@ -263,7 +263,7 @@ class Gem::BasicSpecification
##
# Return all files in this gem that match for +glob+.
- def matches_for_glob glob # TODO: rename?
+ def matches_for_glob(glob) # TODO: rename?
# TODO: do we need these?? Kill it
glob = File.join(self.lib_dirs_glob, glob)
@@ -275,10 +275,14 @@ class Gem::BasicSpecification
# for this spec.
def lib_dirs_glob
- dirs = if self.require_paths.size > 1 then
- "{#{self.require_paths.join(',')}}"
+ dirs = if self.raw_require_paths
+ if self.raw_require_paths.size > 1
+ "{#{self.raw_require_paths.join(',')}}"
+ else
+ self.raw_require_paths.first
+ end
else
- self.require_paths.first
+ "lib" # default value for require_paths for bundler/inline
end
"#{self.full_gem_path}/#{dirs}".dup.untaint
@@ -312,7 +316,7 @@ class Gem::BasicSpecification
def have_extensions?; !extensions.empty?; end
- def have_file? file, suffixes
+ def have_file?(file, suffixes)
return true if raw_require_paths.any? do |path|
base = File.join(gems_dir, full_name, path.untaint, file).untaint
suffixes.any? { |suf| File.file? base + suf }
diff --git a/lib/rubygems/bundler_version_finder.rb b/lib/rubygems/bundler_version_finder.rb
new file mode 100644
index 0000000000..e74baca1ee
--- /dev/null
+++ b/lib/rubygems/bundler_version_finder.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+require "rubygems/util"
+
+module Gem::BundlerVersionFinder
+ def self.bundler_version
+ version, _ = bundler_version_with_reason
+
+ return unless version
+
+ Gem::Version.new(version)
+ end
+
+ def self.bundler_version_with_reason
+ if v = ENV["BUNDLER_VERSION"]
+ return [v, "`$BUNDLER_VERSION`"]
+ end
+ if v = bundle_update_bundler_version
+ return if v == true
+ return [v, "`bundle update --bundler`"]
+ end
+ v, lockfile = lockfile_version
+ if v
+ return [v, "your #{lockfile}"]
+ end
+ end
+
+ def self.missing_version_message
+ return unless vr = bundler_version_with_reason
+ <<-EOS
+Could not find 'bundler' (#{vr.first}) required by #{vr.last}.
+To update to the latest version installed on your system, run `bundle update --bundler`.
+To install the missing version, run `gem install bundler:#{vr.first}`
+ EOS
+ end
+
+ def self.compatible?(spec)
+ return true unless spec.name == "bundler".freeze
+ return true unless bundler_version = self.bundler_version
+
+ spec.version.segments.first == bundler_version.segments.first
+ end
+
+ def self.filter!(specs)
+ return unless bundler_version = self.bundler_version
+
+ specs.reject! { |spec| spec.version.segments.first != bundler_version.segments.first }
+ end
+
+ def self.bundle_update_bundler_version
+ return unless File.basename($0) == "bundle".freeze
+ return unless "update".start_with?(ARGV.first || " ")
+ bundler_version = nil
+ update_index = nil
+ ARGV.each_with_index do |a, i|
+ if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
+ bundler_version = a
+ end
+ next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
+ bundler_version = $1 || true
+ update_index = i
+ end
+ bundler_version
+ end
+ private_class_method :bundle_update_bundler_version
+
+ def self.lockfile_version
+ return unless lockfile = lockfile_contents
+ lockfile, contents = lockfile
+ lockfile ||= "lockfile"
+ regexp = /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
+ return unless contents =~ regexp
+ [$1, lockfile]
+ end
+ private_class_method :lockfile_version
+
+ def self.lockfile_contents
+ gemfile = ENV["BUNDLE_GEMFILE"]
+ gemfile = nil if gemfile && gemfile.empty?
+ Gem::Util.traverse_parents Dir.pwd do |directory|
+ next unless gemfile = Gem::GEM_DEP_FILES.find { |f| File.file?(f.untaint) }
+
+ gemfile = File.join directory, gemfile
+ break
+ end unless gemfile
+
+ return unless gemfile
+
+ lockfile = case gemfile
+ when "gems.rb" then "gems.locked"
+ else "#{gemfile}.lock"
+ end.dup.untaint
+
+ return unless File.file?(lockfile)
+
+ [lockfile, File.read(lockfile)]
+ end
+ private_class_method :lockfile_contents
+end
diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb
index 14b70b75ec..5b8868b0cd 100644
--- a/lib/rubygems/command.rb
+++ b/lib/rubygems/command.rb
@@ -152,16 +152,24 @@ class Gem::Command
#--
# TODO: replace +domain+ with a parameter to suppress suggestions
- def show_lookup_failure(gem_name, version, errors, domain)
+ def show_lookup_failure(gem_name, version, errors, domain, required_by = nil)
+ gem = "'#{gem_name}' (#{version})"
+ msg = String.new "Could not find a valid gem #{gem}"
+
if errors and !errors.empty?
- msg = "Could not find a valid gem '#{gem_name}' (#{version}), here is why:\n".dup
+ msg << ", here is why:\n"
errors.each { |x| msg << " #{x.wordy}\n" }
- alert_error msg
else
- alert_error "Could not find a valid gem '#{gem_name}' (#{version}) in any repository"
+ if required_by and gem != required_by
+ msg << " (required by #{required_by}) in any repository"
+ else
+ msg << " in any repository"
+ end
end
- unless domain == :local then # HACK
+ alert_error msg
+
+ unless domain == :local # HACK
suggestions = Gem::SpecFetcher.fetcher.suggest_gems_from_name gem_name
unless suggestions.empty?
@@ -176,7 +184,7 @@ class Gem::Command
def get_all_gem_names
args = options[:args]
- if args.nil? or args.empty? then
+ if args.nil? or args.empty?
raise Gem::CommandLineError,
"Please specify at least one gem name (e.g. gem build GEMNAME)"
end
@@ -206,12 +214,12 @@ class Gem::Command
def get_one_gem_name
args = options[:args]
- if args.nil? or args.empty? then
+ if args.nil? or args.empty?
raise Gem::CommandLineError,
"Please specify a gem name on the command line (e.g. gem build GEMNAME)"
end
- if args.size > 1 then
+ if args.size > 1
raise Gem::CommandLineError,
"Too many gem names (#{args.join(', ')}); please specify only one"
end
@@ -300,15 +308,23 @@ class Gem::Command
options[:build_args] = build_args
- self.ui = Gem::SilentUI.new if options[:silent]
+ if options[:silent]
+ old_ui = self.ui
+ self.ui = ui = Gem::SilentUI.new
+ end
- if options[:help] then
+ if options[:help]
show_help
- elsif @when_invoked then
+ elsif @when_invoked
@when_invoked.call options
else
execute
end
+ ensure
+ if ui
+ self.ui = old_ui
+ ui.close
+ end
end
##
@@ -345,7 +361,7 @@ class Gem::Command
def remove_option(name)
@option_groups.each do |_, option_list|
- option_list.reject! { |args, _| args.any? { |x| x =~ /^#{name}/ } }
+ option_list.reject! { |args, _| args.any? { |x| x.is_a?(String) && x =~ /^#{name}/ } }
end
end
@@ -435,7 +451,7 @@ class Gem::Command
# Adds a section with +title+ and +content+ to the parser help view. Used
# for adding command arguments and default arguments.
- def add_parser_run_info title, content
+ def add_parser_run_info(title, content)
return if content.empty?
@parser.separator nil
@@ -488,7 +504,6 @@ class Gem::Command
@parser.separator " #{header}Options:"
option_list.each do |args, handler|
- args.select { |arg| arg =~ /^-/ }
@parser.on(*args) do |value|
handler.call(value, @options)
end
@@ -515,7 +530,7 @@ class Gem::Command
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 then
+ if Gem.configuration.verbose and value
Gem.configuration.verbose = 1
else
Gem.configuration.verbose = value
@@ -527,7 +542,7 @@ class Gem::Command
end
add_common_option("--silent",
- "Silence rubygems output") do |value, options|
+ "Silence RubyGems output") do |value, options|
options[:silent] = true
end
@@ -554,7 +569,7 @@ class Gem::Command
# :stopdoc:
- HELP = <<-HELP
+ HELP = <<-HELP.freeze
RubyGems is a sophisticated package manager for Ruby. This is a
basic help message containing pointers to more information.
diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb
index 451b719c46..8ad723be55 100644
--- a/lib/rubygems/command_manager.rb
+++ b/lib/rubygems/command_manager.rb
@@ -7,6 +7,7 @@
require 'rubygems/command'
require 'rubygems/user_interaction'
+require 'rubygems/text'
##
# The command manager registers and installs all the individual sub-commands
@@ -32,6 +33,7 @@ require 'rubygems/user_interaction'
class Gem::CommandManager
+ include Gem::Text
include Gem::UserInteraction
BUILTIN_COMMANDS = [ # :nodoc:
@@ -45,6 +47,7 @@ class Gem::CommandManager
:fetch,
:generate_index,
:help,
+ :info,
:install,
:list,
:lock,
@@ -58,6 +61,8 @@ class Gem::CommandManager
:rdoc,
:search,
:server,
+ :signin,
+ :signout,
:sources,
:specification,
:stale,
@@ -66,7 +71,11 @@ class Gem::CommandManager
:update,
:which,
:yank,
- ]
+ ].freeze
+
+ ALIAS_COMMANDS = {
+ 'i' => 'install'
+ }.freeze
##
# Return the authoritative instance of the command manager.
@@ -138,17 +147,17 @@ class Gem::CommandManager
def run(args, build_args=nil)
process_args(args, build_args)
rescue StandardError, Timeout::Error => ex
- alert_error "While executing gem ... (#{ex.class})\n #{ex}"
+ alert_error clean_text("While executing gem ... (#{ex.class})\n #{ex}")
ui.backtrace ex
terminate_interaction(1)
rescue Interrupt
- alert_error "Interrupted"
+ alert_error clean_text("Interrupted")
terminate_interaction(1)
end
def process_args(args, build_args=nil)
- if args.empty? then
+ if args.empty?
say Gem::Command::HELP
terminate_interaction 1
end
@@ -161,7 +170,7 @@ class Gem::CommandManager
say Gem::VERSION
terminate_interaction 0
when /^-/ then
- alert_error "Invalid option: #{args.first}. See 'gem --help'."
+ alert_error clean_text("Invalid option: #{args.first}. See 'gem --help'.")
terminate_interaction 1
else
cmd_name = args.shift.downcase
@@ -171,18 +180,25 @@ class Gem::CommandManager
end
def find_command(cmd_name)
+ cmd_name = find_alias_command cmd_name
+
possibilities = find_command_possibilities cmd_name
- if possibilities.size > 1 then
+ if possibilities.size > 1
raise Gem::CommandLineError,
"Ambiguous command #{cmd_name} matches [#{possibilities.join(', ')}]"
- elsif possibilities.empty? then
+ elsif possibilities.empty?
raise Gem::CommandLineError, "Unknown command #{cmd_name}"
end
self[possibilities.first]
end
+ def find_alias_command(cmd_name)
+ alias_name = ALIAS_COMMANDS[cmd_name]
+ alias_name ? alias_name : cmd_name
+ end
+
def find_command_possibilities(cmd_name)
len = cmd_name.length
@@ -210,10 +226,9 @@ class Gem::CommandManager
rescue Exception => e
e = load_error if load_error
- alert_error "Loading command: #{command_name} (#{e.class})\n\t#{e}"
+ alert_error clean_text("Loading command: #{command_name} (#{e.class})\n\t#{e}")
ui.backtrace e
end
end
end
-
diff --git a/lib/rubygems/commands/build_command.rb b/lib/rubygems/commands/build_command.rb
index 38c45e46f0..e59471e976 100644
--- a/lib/rubygems/commands/build_command.rb
+++ b/lib/rubygems/commands/build_command.rb
@@ -10,6 +10,14 @@ class Gem::Commands::BuildCommand < Gem::Command
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|
+ options[:strict] = true
+ end
+
+ add_option '-o', '--output FILE', 'output gem with the given filename' do |value, options|
+ options[:output] = value
+ end
end
def arguments # :nodoc:
@@ -32,6 +40,11 @@ with gem spec:
$ cd my_gem-1.0
[edit gem contents]
$ gem build my_gem-1.0.gemspec
+
+Gems can be saved to a specified filename with the output option:
+
+ $ gem build my_gem-1.0.gemspec --output=release.gem
+
EOF
end
@@ -46,14 +59,21 @@ with gem spec:
gemspec += '.gemspec' if File.exist? gemspec + '.gemspec'
end
- if File.exist? gemspec then
- spec = Gem::Specification.load gemspec
+ if File.exist? gemspec
+ Dir.chdir(File.dirname(gemspec)) do
+ spec = Gem::Specification.load File.basename(gemspec)
- if spec then
- Gem::Package.build spec, options[:force]
- else
- alert_error "Error loading gemspec. Aborting."
- terminate_interaction 1
+ if spec
+ Gem::Package.build(
+ spec,
+ options[:force],
+ options[:strict],
+ options[:output]
+ )
+ else
+ alert_error "Error loading gemspec. Aborting."
+ terminate_interaction 1
+ end
end
else
alert_error "Gemspec file not found: #{gemspec}"
@@ -62,4 +82,3 @@ with gem spec:
end
end
-
diff --git a/lib/rubygems/commands/cert_command.rb b/lib/rubygems/commands/cert_command.rb
index 7adf5b01b1..5695460d94 100644
--- a/lib/rubygems/commands/cert_command.rb
+++ b/lib/rubygems/commands/cert_command.rb
@@ -14,15 +14,16 @@ class Gem::Commands::CertCommand < Gem::Command
super 'cert', 'Manage RubyGems certificates and signing settings',
:add => [], :remove => [], :list => [], :build => [], :sign => []
- OptionParser.accept OpenSSL::X509::Certificate do |certificate|
+ OptionParser.accept OpenSSL::X509::Certificate do |certificate_file|
begin
- OpenSSL::X509::Certificate.new File.read certificate
+ certificate = OpenSSL::X509::Certificate.new File.read certificate_file
rescue Errno::ENOENT
- raise OptionParser::InvalidArgument, "#{certificate}: does not exist"
+ raise OptionParser::InvalidArgument, "#{certificate_file}: does not exist"
rescue OpenSSL::X509::CertificateError
raise OptionParser::InvalidArgument,
- "#{certificate}: invalid X509 certificate"
+ "#{certificate_file}: invalid X509 certificate"
end
+ [certificate, certificate_file]
end
OptionParser.accept OpenSSL::PKey::RSA do |key_file|
@@ -42,7 +43,7 @@ class Gem::Commands::CertCommand < Gem::Command
end
add_option('-a', '--add CERT', OpenSSL::X509::Certificate,
- 'Add a trusted certificate.') do |cert, options|
+ 'Add a trusted certificate.') do |(cert, _), options|
options[:add] << cert
end
@@ -67,8 +68,9 @@ class Gem::Commands::CertCommand < Gem::Command
end
add_option('-C', '--certificate CERT', OpenSSL::X509::Certificate,
- 'Signing certificate for --sign') do |cert, options|
+ 'Signing certificate for --sign') do |(cert, cert_file), options|
options[:issuer_cert] = cert
+ options[:issuer_cert_file] = cert_file
end
add_option('-K', '--private-key KEY', OpenSSL::PKey::RSA,
@@ -84,9 +86,19 @@ class Gem::Commands::CertCommand < Gem::Command
options[:sign] << cert_file
end
+
+ 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|
+ options[:resign] = resign
+ end
end
- def add_certificate certificate # :nodoc:
+ def add_certificate(certificate) # :nodoc:
Gem::Security.trust_dir.trust_cert certificate
say "Added '#{certificate.subject}'"
@@ -105,16 +117,28 @@ class Gem::Commands::CertCommand < Gem::Command
list_certificates_matching filter
end
- options[:build].each do |name|
- build name
+ options[:build].each do |email|
+ build email
+ end
+
+ if options[:resign]
+ re_sign_cert(
+ options[:issuer_cert],
+ options[:issuer_cert_file],
+ options[:key]
+ )
end
sign_certificates unless options[:sign].empty?
end
- def build name
+ def build(email)
+ if !valid_email?(email)
+ raise Gem::CommandLineError, "Invalid email address #{email}"
+ end
+
key, key_path = build_key
- cert_path = build_cert name, key
+ cert_path = build_cert email, key
say "Certificate: #{cert_path}"
@@ -124,8 +148,16 @@ class Gem::Commands::CertCommand < Gem::Command
end
end
- def build_cert name, key # :nodoc:
- cert = Gem::Security.create_cert_email name, key
+ def build_cert(email, key) # :nodoc:
+ expiration_length_days = options[:expiration_length_days] ||
+ Gem.configuration.cert_expiration_length_days
+
+ cert = Gem::Security.create_cert_email(
+ email,
+ key,
+ (Gem::Security::ONE_DAY * expiration_length_days)
+ )
+
Gem::Security.write cert, "gem-public_cert.pem"
end
@@ -147,7 +179,7 @@ class Gem::Commands::CertCommand < Gem::Command
return key, key_path
end
- def certificates_matching filter
+ def certificates_matching(filter)
return enum_for __method__, filter unless block_given?
Gem::Security.trusted_certificates.select do |certificate, _|
@@ -199,7 +231,7 @@ For further reading on signing gems see `ri Gem::Security`.
EOF
end
- def list_certificates_matching filter # :nodoc:
+ def list_certificates_matching(filter) # :nodoc:
certificates_matching filter do |certificate, _|
# this could probably be formatted more gracefully
say certificate.subject.to_s
@@ -244,14 +276,14 @@ For further reading on signing gems see `ri Gem::Security`.
load_default_key unless options[:key]
end
- def remove_certificates_matching filter # :nodoc:
+ def remove_certificates_matching(filter) # :nodoc:
certificates_matching filter do |certificate, path|
FileUtils.rm path
say "Removed '#{certificate.subject}'"
end
end
- def sign cert_file
+ def sign(cert_file)
cert = File.read cert_file
cert = OpenSSL::X509::Certificate.new cert
@@ -273,5 +305,19 @@ For further reading on signing gems see `ri Gem::Security`.
end
end
-end if defined?(OpenSSL::SSL)
+ def re_sign_cert(cert, cert_path, private_key)
+ Gem::Security::Signer.re_sign_cert(cert, cert_path, private_key) do |expired_cert_path, new_expired_cert_path|
+ alert("Your certificate #{expired_cert_path} has been re-signed")
+ alert("Your expired certificate will be located at: #{new_expired_cert_path}")
+ end
+ end
+
+ private
+ def valid_email?(email)
+ # It's simple, but is all we need
+ email =~ /\A.+@.+\z/
+ end
+
+
+end if defined?(OpenSSL::SSL)
diff --git a/lib/rubygems/commands/check_command.rb b/lib/rubygems/commands/check_command.rb
index 818cb05f55..7905b8ab69 100644
--- a/lib/rubygems/commands/check_command.rb
+++ b/lib/rubygems/commands/check_command.rb
@@ -44,7 +44,7 @@ class Gem::Commands::CheckCommand < Gem::Command
gems = get_all_gem_names rescue []
Gem::Validator.new.alien(gems).sort.each do |key, val|
- unless val.empty? then
+ unless val.empty?
say "#{key} has #{val.size} problems"
val.each do |error_entry|
say " #{error_entry.path}:"
diff --git a/lib/rubygems/commands/cleanup_command.rb b/lib/rubygems/commands/cleanup_command.rb
index 83ee6b5c7c..aeb4d82fae 100644
--- a/lib/rubygems/commands/cleanup_command.rb
+++ b/lib/rubygems/commands/cleanup_command.rb
@@ -8,13 +8,26 @@ class Gem::Commands::CleanupCommand < Gem::Command
def initialize
super 'cleanup',
'Clean up old versions of installed gems',
- :force => false, :install_dir => Gem.dir
+ :force => false, :install_dir => Gem.dir,
+ :check_dev => true
add_option('-n', '-d', '--dryrun',
'Do not uninstall gems') do |value, options|
options[:dryrun] = true
end
+ 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|
+ options[:user_install] = value
+ end
+
@candidate_gems = nil
@default_gems = []
@full = nil
@@ -49,7 +62,7 @@ If no gems are named all gems in GEM_HOME are cleaned.
def execute
say "Cleaning up installed gems..."
- if options[:args].empty? then
+ if options[:args].empty?
done = false
last_set = nil
@@ -66,7 +79,7 @@ If no gems are named all gems in GEM_HOME are cleaned.
clean_gems
end
- say "Clean Up Complete"
+ say "Clean up complete"
verbose do
skipped = @default_gems.map { |spec| spec.full_name }
@@ -98,7 +111,7 @@ If no gems are named all gems in GEM_HOME are cleaned.
end
def get_candidate_gems
- @candidate_gems = unless options[:args].empty? then
+ @candidate_gems = unless options[:args].empty?
options[:args].map do |gem_name|
Gem::Specification.find_all_by_name gem_name
end.flatten
@@ -108,7 +121,6 @@ If no gems are named all gems in GEM_HOME are cleaned.
end
def get_gems_to_cleanup
-
gems_to_cleanup = @candidate_gems.select { |spec|
@primary_gems[spec.name].version != spec.version
}
@@ -117,8 +129,10 @@ If no gems are named all gems in GEM_HOME are cleaned.
spec.default_gem?
}
+ uninstall_from = options[:user_install] ? Gem.user_dir : @original_home
+
gems_to_cleanup = gems_to_cleanup.select { |spec|
- spec.base_dir == @original_home
+ spec.base_dir == uninstall_from
}
@default_gems += default_gems
@@ -131,16 +145,16 @@ If no gems are named all gems in GEM_HOME are cleaned.
Gem::Specification.each do |spec|
if @primary_gems[spec.name].nil? or
- @primary_gems[spec.name].version < spec.version then
+ @primary_gems[spec.name].version < spec.version
@primary_gems[spec.name] = spec
end
end
end
- def uninstall_dep spec
- return unless @full.ok_to_remove?(spec.full_name)
+ def uninstall_dep(spec)
+ return unless @full.ok_to_remove?(spec.full_name, options[:check_dev])
- if options[:dryrun] then
+ if options[:dryrun]
say "Dry Run Mode: Would uninstall #{spec.full_name}"
return
end
diff --git a/lib/rubygems/commands/contents_command.rb b/lib/rubygems/commands/contents_command.rb
index e0f2eedb5d..6da89a136e 100644
--- a/lib/rubygems/commands/contents_command.rb
+++ b/lib/rubygems/commands/contents_command.rb
@@ -73,7 +73,7 @@ prefix or only the files that are requireable.
names.each do |name|
found =
- if options[:show_install_dir] then
+ if options[:show_install_dir]
gem_install_dir name
else
gem_contents name
@@ -83,15 +83,15 @@ prefix or only the files that are requireable.
end
end
- def files_in spec
- if spec.default_gem? then
+ def files_in(spec)
+ if spec.default_gem?
files_in_default_gem spec
else
files_in_gem spec
end
end
- def files_in_gem spec
+ def files_in_gem(spec)
gem_path = spec.full_gem_path
extra = "/{#{spec.require_paths.join ','}}" if options[:lib_only]
glob = "#{gem_path}#{extra}/**/*"
@@ -102,7 +102,7 @@ prefix or only the files that are requireable.
end
end
- def files_in_default_gem spec
+ def files_in_default_gem(spec)
spec.files.map do |file|
case file
when /\A#{spec.bindir}\//
@@ -115,7 +115,7 @@ prefix or only the files that are requireable.
end
end
- def gem_contents name
+ def gem_contents(name)
spec = spec_for name
return false unless spec
@@ -127,7 +127,7 @@ prefix or only the files that are requireable.
true
end
- def gem_install_dir name
+ def gem_install_dir(name)
spec = spec_for name
return false unless spec
@@ -138,27 +138,27 @@ prefix or only the files that are requireable.
end
def gem_names # :nodoc:
- if options[:all] then
+ if options[:all]
Gem::Specification.map(&:name)
else
get_all_gem_names
end
end
- def path_description spec_dirs # :nodoc:
- if spec_dirs.empty? then
+ def path_description(spec_dirs) # :nodoc:
+ if spec_dirs.empty?
"default gem paths"
else
"specified path"
end
end
- def show_files files
+ def show_files(files)
files.sort.each do |prefix, basename|
absolute_path = File.join(prefix, basename)
next if File.directory? absolute_path
- if options[:prefix] then
+ if options[:prefix]
say absolute_path
else
say basename
@@ -166,14 +166,14 @@ prefix or only the files that are requireable.
end
end
- def spec_for name
+ def spec_for(name)
spec = Gem::Specification.find_all_by_name(name, @version).last
return spec if spec
say "Unable to find gem '#{name}' in #{@path_kind}"
- if Gem.configuration.verbose then
+ if Gem.configuration.verbose
say "\nDirectories searched:"
@spec_dirs.sort.each { |dir| say dir }
end
@@ -188,4 +188,3 @@ prefix or only the files that are requireable.
end
end
-
diff --git a/lib/rubygems/commands/dependency_command.rb b/lib/rubygems/commands/dependency_command.rb
index 97fd812ffa..9e88b04ea5 100644
--- a/lib/rubygems/commands/dependency_command.rb
+++ b/lib/rubygems/commands/dependency_command.rb
@@ -54,7 +54,7 @@ use with other commands.
"#{program_name} REGEXP"
end
- def fetch_remote_specs dependency # :nodoc:
+ def fetch_remote_specs(dependency) # :nodoc:
fetcher = Gem::SpecFetcher.fetcher
ss, = fetcher.spec_for_dependency dependency
@@ -62,7 +62,7 @@ use with other commands.
ss.map { |spec, _| spec }
end
- def fetch_specs name_pattern, dependency # :nodoc:
+ def fetch_specs(name_pattern, dependency) # :nodoc:
specs = []
if local?
@@ -79,7 +79,7 @@ use with other commands.
specs.uniq.sort
end
- def gem_dependency pattern, version, prerelease # :nodoc:
+ def gem_dependency(pattern, version, prerelease) # :nodoc:
dependency = Gem::Deprecate.skip_during {
Gem::Dependency.new pattern, version
}
@@ -89,9 +89,9 @@ use with other commands.
dependency
end
- def display_pipe specs # :nodoc:
+ def display_pipe(specs) # :nodoc:
specs.each do |spec|
- unless spec.dependencies.empty? then
+ unless spec.dependencies.empty?
spec.dependencies.sort_by { |dep| dep.name }.each do |dep|
say "#{dep.name} --version '#{dep.requirement}'"
end
@@ -99,12 +99,12 @@ use with other commands.
end
end
- def display_readable specs, reverse # :nodoc:
+ def display_readable(specs, reverse) # :nodoc:
response = String.new
specs.each do |spec|
response << print_dependencies(spec)
- unless reverse[spec.full_name].empty? then
+ unless reverse[spec.full_name].empty?
response << " Used by\n"
reverse[spec.full_name].each do |sp, dep|
response << " #{sp} (#{dep})\n"
@@ -128,7 +128,7 @@ use with other commands.
reverse = reverse_dependencies specs
- if options[:pipe_format] then
+ if options[:pipe_format]
display_pipe specs
else
display_readable specs, reverse
@@ -136,13 +136,13 @@ use with other commands.
end
def ensure_local_only_reverse_dependencies # :nodoc:
- if options[:reverse_dependencies] and remote? and not local? then
+ if options[:reverse_dependencies] and remote? and not local?
alert_error 'Only reverse dependencies for local gems are supported.'
terminate_interaction 1
end
end
- def ensure_specs specs # :nodoc:
+ def ensure_specs(specs) # :nodoc:
return unless specs.empty?
patterns = options[:args].join ','
@@ -155,7 +155,7 @@ use with other commands.
def print_dependencies(spec, level = 0) # :nodoc:
response = String.new
response << ' ' * level + "Gem #{spec.full_name}\n"
- unless spec.dependencies.empty? then
+ unless spec.dependencies.empty?
spec.dependencies.sort_by { |dep| dep.name }.each do |dep|
response << ' ' * level + " #{dep}\n"
end
@@ -163,7 +163,7 @@ use with other commands.
response
end
- def remote_specs dependency # :nodoc:
+ def remote_specs(dependency) # :nodoc:
fetcher = Gem::SpecFetcher.fetcher
ss, _ = fetcher.spec_for_dependency dependency
@@ -171,7 +171,7 @@ use with other commands.
ss.map { |s,o| s }
end
- def reverse_dependencies specs # :nodoc:
+ def reverse_dependencies(specs) # :nodoc:
reverse = Hash.new { |h, k| h[k] = [] }
return reverse unless options[:reverse_dependencies]
@@ -186,7 +186,7 @@ use with other commands.
##
# Returns an Array of [specification, dep] that are satisfied by +spec+.
- def find_reverse_dependencies spec # :nodoc:
+ def find_reverse_dependencies(spec) # :nodoc:
result = []
Gem::Specification.each do |sp|
@@ -194,7 +194,7 @@ use with other commands.
dep = Gem::Dependency.new(*dep) unless Gem::Dependency === dep
if spec.name == dep.name and
- dep.requirement.satisfied_by?(spec.version) then
+ dep.requirement.satisfied_by?(spec.version)
result << [sp.full_name, dep]
end
end
@@ -205,10 +205,10 @@ use with other commands.
private
- def name_pattern args
+ def name_pattern(args)
args << '' if args.empty?
- if args.length == 1 and args.first =~ /\A\/(.*)\/(i)?\z/m then
+ if args.length == 1 and 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 e825c761ad..11fb45f68d 100644
--- a/lib/rubygems/commands/environment_command.rb
+++ b/lib/rubygems/commands/environment_command.rb
@@ -97,7 +97,7 @@ lib/rubygems/defaults/operating_system.rb
true
end
- def add_path out, path
+ def add_path(out, path)
path.each do |component|
out << " - #{component}\n"
end
@@ -120,6 +120,8 @@ lib/rubygems/defaults/operating_system.rb
out << " - RUBY EXECUTABLE: #{Gem.ruby}\n"
+ out << " - GIT EXECUTABLE: #{git_path}\n"
+
out << " - EXECUTABLE DIRECTORY: #{Gem.bindir}\n"
out << " - SPEC CACHE DIRECTORY: #{Gem.spec_cache_dir}\n"
@@ -157,4 +159,21 @@ lib/rubygems/defaults/operating_system.rb
out
end
+ private
+
+ ##
+ # Git binary path
+
+ def git_path
+ exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
+ ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
+ exts.each do |ext|
+ exe = File.join(path, "git#{ext}")
+ return exe if File.executable?(exe) && !File.directory?(exe)
+ end
+ end
+
+ return nil
+ end
+
end
diff --git a/lib/rubygems/commands/fetch_command.rb b/lib/rubygems/commands/fetch_command.rb
index 19559a7774..66562d7fb7 100644
--- a/lib/rubygems/commands/fetch_command.rb
+++ b/lib/rubygems/commands/fetch_command.rb
@@ -56,14 +56,14 @@ then repackaging it.
specs_and_sources, errors =
Gem::SpecFetcher.fetcher.spec_for_dependency dep
- if platform then
+ if platform
filtered = specs_and_sources.select { |s,| s.platform == platform }
specs_and_sources = filtered unless filtered.empty?
end
spec, source = specs_and_sources.max_by { |s,| s.version }
- if spec.nil? then
+ if spec.nil?
show_lookup_failure gem_name, version, errors, options[:domain]
next
end
@@ -75,4 +75,3 @@ then repackaging it.
end
end
-
diff --git a/lib/rubygems/commands/generate_index_command.rb b/lib/rubygems/commands/generate_index_command.rb
index 01f1f88405..941637ea9c 100644
--- a/lib/rubygems/commands/generate_index_command.rb
+++ b/lib/rubygems/commands/generate_index_command.rb
@@ -67,13 +67,13 @@ Marshal::MINOR_VERSION constants. It is used to ensure compatibility.
options[:build_modern] = true
if not File.exist?(options[:directory]) or
- not File.directory?(options[:directory]) then
- alert_error "unknown directory name #{directory}."
+ not File.directory?(options[:directory])
+ alert_error "unknown directory name #{options[:directory]}."
terminate_interaction 1
else
indexer = Gem::Indexer.new options.delete(:directory), options
- if options[:update] then
+ if options[:update]
indexer.update_index
else
indexer.generate_index
@@ -82,4 +82,3 @@ Marshal::MINOR_VERSION constants. It is used to ensure compatibility.
end
end
-
diff --git a/lib/rubygems/commands/help_command.rb b/lib/rubygems/commands/help_command.rb
index de3f175fad..9f14e22f90 100644
--- a/lib/rubygems/commands/help_command.rb
+++ b/lib/rubygems/commands/help_command.rb
@@ -4,7 +4,7 @@ require 'rubygems/command'
class Gem::Commands::HelpCommand < Gem::Command
# :stopdoc:
- EXAMPLES = <<-EOF
+ EXAMPLES = <<-EOF.freeze
Some examples of 'gem' usage.
* Install 'rake', either from local directory or remote server:
@@ -53,7 +53,7 @@ Some examples of 'gem' usage.
gem update --system
EOF
- GEM_DEPENDENCIES = <<-EOF
+ GEM_DEPENDENCIES = <<-EOF.freeze
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
@@ -230,7 +230,7 @@ default. This may be overridden with the :development_group option:
EOF
- PLATFORMS = <<-'EOF'
+ PLATFORMS = <<-'EOF'.freeze
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`.
@@ -277,7 +277,7 @@ platform.
["examples", EXAMPLES],
["gem_dependencies", GEM_DEPENDENCIES],
["platforms", PLATFORMS],
- ]
+ ].freeze
# :startdoc:
def initialize
@@ -297,8 +297,8 @@ platform.
begins? command, arg
end
- if help then
- if Symbol === help then
+ if help
+ if Symbol === help
send help
else
say help
@@ -306,10 +306,10 @@ platform.
return
end
- if options[:help] then
+ if options[:help]
show_help
- elsif arg then
+ elsif arg
show_command_help arg
else
@@ -334,7 +334,7 @@ platform.
command = @command_manager[cmd_name]
summary =
- if command then
+ if command
command.summary
else
"[No command found for #{cmd_name}]"
@@ -356,20 +356,19 @@ platform.
say out.join("\n")
end
- def show_command_help command_name # :nodoc:
+ def show_command_help(command_name) # :nodoc:
command_name = command_name.downcase
possibilities = @command_manager.find_command_possibilities command_name
- if possibilities.size == 1 then
+ if possibilities.size == 1
command = @command_manager[possibilities.first]
command.invoke("--help")
- elsif possibilities.size > 1 then
+ elsif possibilities.size > 1
alert_warning "Ambiguous command #{command_name} (#{possibilities.join(', ')})"
else
- alert_warning "Unknown command #{command_name}. Try: gem help commands"
+ alert_warning "Unknown command #{command_name}. Try: gem help commands"
end
end
end
-
diff --git a/lib/rubygems/commands/info_command.rb b/lib/rubygems/commands/info_command.rb
new file mode 100644
index 0000000000..8d9611a957
--- /dev/null
+++ b/lib/rubygems/commands/info_command.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'rubygems/command'
+require 'rubygems/commands/query_command'
+
+class Gem::Commands::InfoCommand < Gem::Commands::QueryCommand
+ def initialize
+ super "info", "Show information for the given gem"
+
+ remove_option('--name-matches')
+ remove_option('-d')
+
+ defaults[:details] = true
+ defaults[:exact] = true
+ end
+
+ def description # :nodoc:
+ "Info prints information about the gem such as name,"\
+ " description, website, license and installed paths"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME"
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of the gem to print information about"
+ end
+
+ def defaults_str
+ "--local"
+ end
+end
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
index 3a7d50517f..776b58651f 100644
--- a/lib/rubygems/commands/install_command.rb
+++ b/lib/rubygems/commands/install_command.rb
@@ -117,6 +117,13 @@ to write the specification by hand. For example:
some_extension_gem (1.0)
$
+Command Alias
+==========================
+
+You can use `i` command instead of `install`.
+
+ $ gem i GEMNAME
+
EOF
end
@@ -125,7 +132,7 @@ to write the specification by hand. For example:
end
def check_install_dir # :nodoc:
- if options[:install_dir] and options[:user_install] then
+ if options[:install_dir] and options[:user_install]
alert_error "Use --install-dir or --user-install but not both"
terminate_interaction 1
end
@@ -133,22 +140,22 @@ to write the specification by hand. For example:
def check_version # :nodoc:
if options[:version] != Gem::Requirement.default and
- get_all_gem_names.size > 1 then
- alert_error "Can't use --version w/ multiple gems. Use name:ver instead."
+ 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
end
end
def execute
-
- if options.include? :gemdeps then
+ if options.include? :gemdeps
install_from_gemdeps
return # not reached
end
@installed_specs = []
- ENV.delete 'GEM_PATH' if options[:install_dir].nil? and RUBY_VERSION > '1.9'
+ ENV.delete 'GEM_PATH' if options[:install_dir].nil?
check_install_dir
check_version
@@ -181,13 +188,13 @@ to write the specification by hand. For example:
terminate_interaction
end
- def install_gem name, version # :nodoc:
+ def install_gem(name, version) # :nodoc:
return if options[:conservative] and
not Gem::Dependency.new(name, version).matching_specs.empty?
req = Gem::Requirement.create(version)
- if options[:ignore_dependencies] then
+ if options[:ignore_dependencies]
install_gem_without_dependencies name, req
else
inst = Gem::DependencyInstaller.new options
@@ -209,11 +216,11 @@ to write the specification by hand. For example:
end
end
- def install_gem_without_dependencies name, req # :nodoc:
+ def install_gem_without_dependencies(name, req) # :nodoc:
gem = nil
- if local? then
- if name =~ /\.gem$/ and File.file? name then
+ if local?
+ if name =~ /\.gem$/ and File.file? name
source = Gem::Source::SpecificFile.new name
spec = source.spec
else
@@ -223,7 +230,7 @@ to write the specification by hand. For example:
gem = source.download spec if spec
end
- if remote? and not gem then
+ if remote? and not gem
dependency = Gem::Dependency.new name, req
dependency.prerelease = options[:prerelease]
@@ -250,18 +257,23 @@ to write the specification by hand. For example:
get_all_gem_names_and_versions.each do |gem_name, gem_version|
gem_version ||= options[:version]
+ domain = options[:domain]
+ domain = :local unless options[:suggest_alternate]
begin
install_gem gem_name, gem_version
rescue Gem::InstallError => e
alert_error "Error installing #{gem_name}:\n\t#{e.message}"
exit_code |= 1
- rescue Gem::GemNotFoundException, Gem::UnsatisfiableDependencyError => e
- domain = options[:domain]
- domain = :local unless options[:suggest_alternate]
+ rescue Gem::GemNotFoundException => e
show_lookup_failure e.name, e.version, e.errors, domain
exit_code |= 2
+ rescue Gem::UnsatisfiableDependencyError => e
+ show_lookup_failure e.name, e.version, e.errors, domain,
+ "'#{gem_name}' (#{gem_version})"
+
+ exit_code |= 2
end
end
@@ -280,7 +292,7 @@ to write the specification by hand. For example:
require 'rubygems/rdoc'
end
- def show_install_errors errors # :nodoc:
+ def show_install_errors(errors) # :nodoc:
return unless errors
errors.each do |x|
@@ -300,4 +312,3 @@ to write the specification by hand. For example:
end
end
-
diff --git a/lib/rubygems/commands/list_command.rb b/lib/rubygems/commands/list_command.rb
index 1acb49e5fb..cd21543537 100644
--- a/lib/rubygems/commands/list_command.rb
+++ b/lib/rubygems/commands/list_command.rb
@@ -38,4 +38,3 @@ To search for remote gems use the search command.
end
end
-
diff --git a/lib/rubygems/commands/lock_command.rb b/lib/rubygems/commands/lock_command.rb
index 3eebfadc05..7f02e9feda 100644
--- a/lib/rubygems/commands/lock_command.rb
+++ b/lib/rubygems/commands/lock_command.rb
@@ -59,7 +59,7 @@ lock it down to the exact version.
end
def complain(message)
- if options[:strict] then
+ if options[:strict]
raise Gem::Exception, message
else
say "# #{message}"
@@ -78,7 +78,7 @@ lock it down to the exact version.
spec = Gem::Specification.load spec_path(full_name)
- if spec.nil? then
+ if spec.nil?
complain "Could not find gem #{full_name}, try using the full name"
next
end
@@ -90,7 +90,7 @@ lock it down to the exact version.
next if locked[dep.name]
candidates = dep.matching_specs
- if candidates.empty? then
+ if candidates.empty?
complain "Unable to satisfy '#{dep}' from currently installed gems"
else
pending << candidates.last.full_name
@@ -108,4 +108,3 @@ lock it down to the exact version.
end
end
-
diff --git a/lib/rubygems/commands/open_command.rb b/lib/rubygems/commands/open_command.rb
index a89b7421e3..2794fe05e6 100644
--- a/lib/rubygems/commands/open_command.rb
+++ b/lib/rubygems/commands/open_command.rb
@@ -11,9 +11,9 @@ class Gem::Commands::OpenCommand < Gem::Command
def initialize
super 'open', 'Open gem sources in editor'
- add_option('-e', '--editor EDITOR', String,
- "Opens gem sources in EDITOR") do |editor, options|
- options[:editor] = editor || get_env_editor
+ 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,
"Opens specific gem version") do |version|
@@ -32,14 +32,14 @@ class Gem::Commands::OpenCommand < Gem::Command
def description # :nodoc:
<<-EOF
The open command opens gem in editor and changes current path
- to gem's source directory. Editor can be specified with -e option,
- otherwise rubygems will look for editor in $EDITOR, $VISUAL and
- $GEM_EDITOR variables.
+ to gem's source directory.
+ Editor command can be specified with -e option, otherwise rubygems
+ will look for editor in $EDITOR, $VISUAL and $GEM_EDITOR variables.
EOF
end
def usage # :nodoc:
- "#{program_name} GEMNAME [-e EDITOR]"
+ "#{program_name} GEMNAME [-e COMMAND]"
end
def get_env_editor
@@ -58,21 +58,27 @@ class Gem::Commands::OpenCommand < Gem::Command
terminate_interaction 1 unless found
end
- def open_gem name
+ def open_gem(name)
spec = spec_for name
+
return false unless spec
+ if spec.default_gem?
+ say "'#{name}' is a default gem and can't be opened."
+ return false
+ end
+
open_editor(spec.full_gem_path)
end
- def open_editor path
+ def open_editor(path)
Dir.chdir(path) do
system(*@editor.split(/\s+/) + [path])
end
end
- def spec_for name
- spec = Gem::Specification.find_all_by_name(name, @version).last
+ def spec_for(name)
+ spec = Gem::Specification.find_all_by_name(name, @version).first
return spec if spec
diff --git a/lib/rubygems/commands/owner_command.rb b/lib/rubygems/commands/owner_command.rb
index 4b99434e87..22b7327683 100644
--- a/lib/rubygems/commands/owner_command.rb
+++ b/lib/rubygems/commands/owner_command.rb
@@ -2,8 +2,11 @@
require 'rubygems/command'
require 'rubygems/local_remote_options'
require 'rubygems/gemcutter_utilities'
+require 'rubygems/text'
class Gem::Commands::OwnerCommand < Gem::Command
+
+ include Gem::Text
include Gem::LocalRemoteOptions
include Gem::GemcutterUtilities
@@ -30,6 +33,7 @@ permission to.
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 EMAIL', 'Add an owner' do |value, options|
@@ -40,7 +44,9 @@ permission to.
options[:remove] << value
end
- add_option '-h', '--host HOST', 'Use another gemcutter-compatible host' 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
@@ -56,13 +62,13 @@ permission to.
show_owners name
end
- def show_owners name
+ def show_owners(name)
response = rubygems_api_request :get, "api/v1/gems/#{name}/owners.yaml" do |request|
request.add_field "Authorization", api_key
end
with_response response do |resp|
- owners = YAML.load resp.body
+ owners = Gem::SafeYAML.load clean_text(resp.body)
say "Owners for gem: #{name}"
owners.each do |owner|
@@ -71,20 +77,21 @@ permission to.
end
end
- def add_owners name, owners
+ def add_owners(name, owners)
manage_owners :post, name, owners
end
- def remove_owners name, owners
+ def remove_owners(name, owners)
manage_owners :delete, name, owners
end
- def manage_owners method, name, owners
+ def manage_owners(method, name, owners)
owners.each do |owner|
begin
- response = rubygems_api_request method, "api/v1/gems/#{name}/owners" do |request|
- request.set_form_data 'email' => owner
- request.add_field "Authorization", api_key
+ response = send_owner_request(method, name, owner)
+
+ if need_otp? response
+ response = send_owner_request(method, name, owner, true)
end
action = method == :delete ? "Removing" : "Adding"
@@ -96,4 +103,14 @@ permission to.
end
end
+ private
+
+ def send_owner_request(method, name, owner, use_otp = false)
+ rubygems_api_request method, "api/v1/gems/#{name}/owners" do |request|
+ request.set_form_data 'email' => owner
+ request.add_field "Authorization", api_key
+ request.add_field "OTP", options[:otp] if use_otp
+ end
+ end
+
end
diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb
index 6cab572b86..41decde856 100644
--- a/lib/rubygems/commands/pristine_command.rb
+++ b/lib/rubygems/commands/pristine_command.rb
@@ -24,7 +24,8 @@ class Gem::Commands::PristineCommand < Gem::Command
add_option('--skip=gem_name',
'used on --all, skip if name == gem_name') do |value, options|
- options[:skip] = value
+ options[:skip] ||= []
+ options[:skip] << value
end
add_option('--[no-]extensions',
@@ -45,6 +46,12 @@ class Gem::Commands::PristineCommand < Gem::Command
options[:env_shebang] = value
end
+ 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')
end
@@ -81,13 +88,13 @@ extensions will be restored.
end
def execute
- specs = if options[:all] then
+ specs = if options[:all]
Gem::Specification.map
# `--extensions` must be explicitly given to pristine only gems
# with extensions.
elsif options[:extensions_set] and
- options[:extensions] and options[:args].empty? then
+ options[:extensions] and options[:args].empty?
Gem::Specification.select do |spec|
spec.extensions and not spec.extensions.empty?
end
@@ -97,7 +104,7 @@ extensions will be restored.
end.flatten
end
- if specs.to_a.empty? then
+ if specs.to_a.empty?
raise Gem::Exception,
"Failed to find gems #{options[:args]} #{options[:version]}"
end
@@ -115,24 +122,21 @@ extensions will be restored.
next
end
- if spec.name == options[:skip]
- say "Skipped #{spec.full_name}, it was given through options"
- next
- end
-
- if spec.bundled_gem_in_old_ruby?
- say "Skipped #{spec.full_name}, it is bundled with old Ruby"
- next
+ if options.has_key? :skip
+ if options[:skip].include? spec.name
+ say "Skipped #{spec.full_name}, it was given through options"
+ next
+ end
end
- unless spec.extensions.empty? or options[:extensions] then
+ unless spec.extensions.empty? or options[:extensions] or options[:only_executables]
say "Skipped #{spec.full_name}, it needs to compile an extension"
next
end
gem = spec.cache_file
- unless File.exist? gem then
+ unless File.exist? gem or options[:only_executables]
require 'rubygems/remote_fetcher'
say "Cached gem for #{spec.full_name} not found, attempting to fetch..."
@@ -150,23 +154,29 @@ extensions will be restored.
end
env_shebang =
- if options.include? :env_shebang then
+ if options.include? :env_shebang
options[:env_shebang]
else
install_defaults = Gem::ConfigFile::PLATFORM_DEFAULTS['install']
install_defaults.to_s['--env-shebang']
end
- installer = Gem::Installer.at(gem,
- :wrappers => true,
- :force => true,
- :install_dir => spec.base_dir,
- :env_shebang => env_shebang,
- :build_args => spec.build_args)
+ bin_dir = options[:bin_dir] if options[:bin_dir]
+
+ installer_options = {
+ :wrappers => true,
+ :force => true,
+ :install_dir => spec.base_dir,
+ :env_shebang => env_shebang,
+ :build_args => spec.build_args,
+ :bin_dir => bin_dir
+ }
- if options[:only_executables] then
+ if options[:only_executables]
+ installer = Gem::Installer.for_spec(spec, installer_options)
installer.generate_bin
else
+ installer = Gem::Installer.at(gem, installer_options)
installer.install
end
diff --git a/lib/rubygems/commands/push_command.rb b/lib/rubygems/commands/push_command.rb
index 6adeff6b30..3ea4703a3e 100644
--- a/lib/rubygems/commands/push_command.rb
+++ b/lib/rubygems/commands/push_command.rb
@@ -13,7 +13,7 @@ class Gem::Commands::PushCommand < Gem::Command
The push command uploads a gem to the push server (the default is
https://rubygems.org) and adds it to the index.
-The gem can be removed from the index (but only the index) using the yank
+The gem can be removed from the index and deleted from the server using the yank
command. For further discussion see the help for the yank command.
EOF
end
@@ -29,33 +29,58 @@ command. For further discussion see the help for the yank command.
def initialize
super 'push', 'Push a gem up to the gem server', :host => self.host
+ @user_defined_host = false
+
add_proxy_option
add_key_option
+ add_otp_option
add_option('--host HOST',
- 'Push to another gemcutter-compatible host') do |value, options|
+ 'Push to another gemcutter-compatible host',
+ ' (e.g. https://rubygems.org)') do |value, options|
options[:host] = value
+ @user_defined_host = true
end
@host = nil
end
def execute
- @host = options[:host]
+ gem_name = get_one_gem_name
+ default_gem_server, push_host = get_hosts_for(gem_name)
+
+ default_host = nil
+ user_defined_host = nil
+
+ if @user_defined_host
+ user_defined_host = options[:host]
+ else
+ default_host = options[:host]
+ end
+
+ @host = if user_defined_host
+ user_defined_host
+ elsif default_gem_server
+ default_gem_server
+ elsif push_host
+ push_host
+ else
+ default_host
+ end
sign_in @host
- send_gem get_one_gem_name
+ send_gem(gem_name)
end
- def send_gem name
+ def send_gem(name)
args = [:post, "api/v1/gems"]
latest_rubygems_version = Gem.latest_rubygems_version
if latest_rubygems_version < Gem.rubygems_version and
Gem.rubygems_version.prerelease? and
- Gem::Version.new('2.0.0.rc.2') != Gem.rubygems_version then
+ Gem::Version.new('2.0.0.rc.2') != Gem.rubygems_version
alert_error <<-ERROR
You are using a beta release of RubyGems (#{Gem::VERSION}) which is not
allowed to push gems. Please downgrade or upgrade to a release version.
@@ -72,7 +97,7 @@ You can upgrade or downgrade to the latest release version with:
gem_data = Gem::Package.new(name)
- unless @host then
+ unless @host
@host = gem_data.spec.metadata['default_gem_server']
end
@@ -89,15 +114,33 @@ You can upgrade or downgrade to the latest release version with:
say "Pushing gem to #{@host || Gem.host}..."
- response = rubygems_api_request(*args) do |request|
+ response = send_push_request(name, args)
+
+ if need_otp? response
+ response = send_push_request(name, args, true)
+ end
+
+ with_response response
+ end
+
+ private
+
+ def send_push_request(name, args, use_otp = false)
+ rubygems_api_request(*args) do |request|
request.body = Gem.read_binary name
request.add_field "Content-Length", request.body.size
request.add_field "Content-Type", "application/octet-stream"
request.add_field "Authorization", api_key
+ request.add_field "OTP", options[:otp] if use_otp
end
-
- with_response response
end
-end
+ def get_hosts_for(name)
+ gem_metadata = Gem::Package.new(name).spec.metadata
+ [
+ gem_metadata["default_gem_server"],
+ gem_metadata["allowed_push_host"]
+ ]
+ end
+end
diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb
index f25d120b88..d947d588ef 100644
--- a/lib/rubygems/commands/query_command.rb
+++ b/lib/rubygems/commands/query_command.rb
@@ -86,13 +86,13 @@ is too hard to use.
name = Array(options[:name])
else
args = options[:args].to_a
- name = options[:exact] ? args : args.map{|arg| /#{arg}/i }
+ name = options[:exact] ? args.map{|arg| /\A#{Regexp.escape(arg)}\Z/ } : args.map{|arg| /#{arg}/i }
end
prerelease = options[:prerelease]
- unless options[:installed].nil? then
- if no_name then
+ unless options[:installed].nil?
+ if no_name
alert_error "You must specify a gem name"
exit_code |= 4
elsif name.count > 1
@@ -102,7 +102,7 @@ is too hard to use.
installed = installed? name.first, options[:version]
installed = !installed unless options[:installed]
- if installed then
+ if installed
say "true"
else
say "false"
@@ -119,8 +119,8 @@ is too hard to use.
private
- def display_header type
- if (ui.outs.tty? and Gem.configuration.verbose) or both? then
+ def display_header(type)
+ if (ui.outs.tty? and Gem.configuration.verbose) or both?
say
say "*** #{type} GEMS ***"
say
@@ -128,14 +128,14 @@ is too hard to use.
end
#Guts of original execute
- def show_gems name, prerelease
+ def show_gems(name, prerelease)
req = Gem::Requirement.default
# TODO: deprecate for real
dep = Gem::Deprecate.skip_during { Gem::Dependency.new name, req }
dep.prerelease = prerelease
- if local? then
- if prerelease and not both? then
+ if local?
+ if prerelease and not both?
alert_warning "prereleases are always shown locally"
end
@@ -152,7 +152,7 @@ is too hard to use.
output_query_results spec_tuples
end
- if remote? then
+ if remote?
display_header 'REMOTE'
fetcher = Gem::SpecFetcher.fetcher
@@ -205,7 +205,7 @@ is too hard to use.
say output.join(options[:details] ? "\n\n" : "\n")
end
- def output_versions output, versions
+ def output_versions(output, versions)
versions.each do |gem_name, matching_tuples|
matching_tuples = matching_tuples.sort_by { |n,_| n.version }.reverse
@@ -218,7 +218,7 @@ is too hard to use.
seen = {}
matching_tuples.delete_if do |n,_|
- if seen[n.version] then
+ if seen[n.version]
true
else
seen[n.version] = true
@@ -226,11 +226,11 @@ is too hard to use.
end
end
- output << make_entry(matching_tuples, platforms)
+ output << clean_text(make_entry(matching_tuples, platforms))
end
end
- def entry_details entry, detail_tuple, specs, platforms
+ def entry_details(entry, detail_tuple, specs, platforms)
return unless options[:details]
name_tuple, spec = detail_tuple
@@ -247,37 +247,36 @@ is too hard to use.
spec_summary entry, spec
end
- def entry_versions entry, name_tuples, platforms, specs
+ def entry_versions(entry, name_tuples, platforms, specs)
return unless options[:versions]
list =
- if platforms.empty? or options[:details] then
+ if platforms.empty? or options[:details]
name_tuples.map { |n| n.version }.uniq
else
platforms.sort.reverse.map do |version, pls|
- if pls == [Gem::Platform::RUBY] then
- if options[:domain] == :remote || specs.all? { |spec| spec.is_a? Gem::Source }
- version
- else
- spec = specs.select { |s| s.version == version }
- if spec.first.default_gem?
- "default: #{version}"
- else
- version
- end
+ out = version.to_s
+
+ if options[:domain] == :local
+ default = specs.any? do |s|
+ !s.is_a?(Gem::Source) && s.version == version && s.default_gem?
end
- else
- ruby = pls.delete Gem::Platform::RUBY
- platform_list = [ruby, *pls.sort].compact
- "#{version} #{platform_list.join ' '}"
+ out = "default: #{out}" if default
+ end
+
+ if pls != [Gem::Platform::RUBY]
+ platform_list = [pls.delete(Gem::Platform::RUBY), *pls.sort].compact
+ out = platform_list.unshift(out).join(' ')
end
+
+ out
end
end
entry << " (#{list.join ', '})"
end
- def make_entry entry_tuples, platforms
+ def make_entry(entry_tuples, platforms)
detail_tuple = entry_tuples.first
name_tuples, specs = entry_tuples.flatten.partition do |item|
@@ -292,19 +291,19 @@ is too hard to use.
entry.join
end
- def spec_authors entry, spec
+ def spec_authors(entry, spec)
authors = "Author#{spec.authors.length > 1 ? 's' : ''}: ".dup
authors << spec.authors.join(', ')
entry << format_text(authors, 68, 4)
end
- def spec_homepage entry, spec
+ def spec_homepage(entry, spec)
return if spec.homepage.nil? or spec.homepage.empty?
entry << "\n" << format_text("Homepage: #{spec.homepage}", 68, 4)
end
- def spec_license entry, spec
+ def spec_license(entry, spec)
return if spec.license.nil? or spec.license.empty?
licenses = "License#{spec.licenses.length > 1 ? 's' : ''}: ".dup
@@ -312,10 +311,10 @@ is too hard to use.
entry << "\n" << format_text(licenses, 68, 4)
end
- def spec_loaded_from entry, spec, specs
+ def spec_loaded_from(entry, spec, specs)
return unless spec.loaded_from
- if specs.length == 1 then
+ if specs.length == 1
default = spec.default_gem? ? ' (default)' : nil
entry << "\n" << " Installed at#{default}: #{spec.base_dir}"
else
@@ -329,14 +328,14 @@ is too hard to use.
end
end
- def spec_platforms entry, platforms
+ def spec_platforms(entry, platforms)
non_ruby = platforms.any? do |_, pls|
pls.any? { |pl| pl != Gem::Platform::RUBY }
end
return unless non_ruby
- if platforms.length == 1 then
+ if platforms.length == 1
title = platforms.values.length == 1 ? 'Platform' : 'Platforms'
entry << " #{title}: #{platforms.values.sort.join ', '}\n"
else
@@ -352,8 +351,9 @@ is too hard to use.
end
end
- def spec_summary entry, spec
- entry << "\n\n" << format_text(spec.summary, 68, 4)
+ def spec_summary(entry, spec)
+ summary = truncate_text(spec.summary, "the summary for #{spec.full_name}")
+ entry << "\n\n" << format_text(summary, 68, 4)
end
end
diff --git a/lib/rubygems/commands/rdoc_command.rb b/lib/rubygems/commands/rdoc_command.rb
index 6992040dca..5f8b72eb7a 100644
--- a/lib/rubygems/commands/rdoc_command.rb
+++ b/lib/rubygems/commands/rdoc_command.rb
@@ -60,7 +60,7 @@ Use --overwrite to force rebuilding of documentation.
end
def execute
- specs = if options[:all] then
+ specs = if options[:all]
Gem::Specification.to_a
else
get_all_gem_names.map do |name|
@@ -68,7 +68,7 @@ Use --overwrite to force rebuilding of documentation.
end.flatten.uniq
end
- if specs.empty? then
+ if specs.empty?
alert_error 'No matching gems found'
terminate_interaction 1
end
@@ -78,7 +78,7 @@ Use --overwrite to force rebuilding of documentation.
doc.force = options[:overwrite]
- if options[:overwrite] then
+ if options[:overwrite]
FileUtils.rm_rf File.join(spec.doc_dir, 'ri')
FileUtils.rm_rf File.join(spec.doc_dir, 'rdoc')
end
@@ -94,4 +94,3 @@ Use --overwrite to force rebuilding of documentation.
end
end
-
diff --git a/lib/rubygems/commands/search_command.rb b/lib/rubygems/commands/search_command.rb
index 933436d84d..c9cb1f2d43 100644
--- a/lib/rubygems/commands/search_command.rb
+++ b/lib/rubygems/commands/search_command.rb
@@ -38,4 +38,3 @@ To list local gems use the list command.
end
end
-
diff --git a/lib/rubygems/commands/server_command.rb b/lib/rubygems/commands/server_command.rb
index 245156d50d..e91a8e5176 100644
--- a/lib/rubygems/commands/server_command.rb
+++ b/lib/rubygems/commands/server_command.rb
@@ -9,7 +9,7 @@ class Gem::Commands::ServerCommand < Gem::Command
:port => 8808, :gemdir => [], :daemon => false
OptionParser.accept :Port do |port|
- if port =~ /\A\d+\z/ then
+ if port =~ /\A\d+\z/
port = Integer port
raise OptionParser::InvalidArgument, "#{port}: not a port number" if
port > 65535
@@ -84,4 +84,3 @@ You can set up a shortcut to gem server documentation using the URL:
end
end
-
diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb
index ebb08d24d7..c9c00b5be4 100644
--- a/lib/rubygems/commands/setup_command.rb
+++ b/lib/rubygems/commands/setup_command.rb
@@ -6,8 +6,10 @@ require 'rubygems/command'
# RubyGems checkout or tarball.
class Gem::Commands::SetupCommand < Gem::Command
- HISTORY_HEADER = /^===\s*[\d.]+\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/
- VERSION_MATCHER = /^===\s*([\d.]+)\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/
+ HISTORY_HEADER = /^===\s*[\d.a-zA-Z]+\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/.freeze
+ VERSION_MATCHER = /^===\s*([\d.a-zA-Z]+)\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/.freeze
+
+ ENV_PATHS = %w[/usr/bin/env /bin/env].freeze
def initialize
require 'tmpdir'
@@ -15,10 +17,11 @@ class Gem::Commands::SetupCommand < Gem::Command
super 'setup', 'Install RubyGems',
:format_executable => true, :document => %w[ri],
:site_or_vendor => 'sitelibdir',
- :destdir => '', :prefix => '', :previous_version => ''
+ :destdir => '', :prefix => '', :previous_version => '',
+ :regenerate_binstubs => true
add_option '--previous-version=VERSION',
- 'Previous version of rubygems',
+ 'Previous version of RubyGems',
'Used for changelog processing' do |version, options|
options[:previous_version] = version
end
@@ -42,12 +45,12 @@ class Gem::Commands::SetupCommand < Gem::Command
add_option '--[no-]format-executable',
'Makes `gem` match ruby',
- 'If ruby is ruby18, gem will be gem18' do |value, options|
+ '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.',
+ 'Generate documentation for RubyGems',
'List the documentation types you wish to',
'generate. For example: rdoc,ri' do |value, options|
options[:document] = case value
@@ -59,7 +62,7 @@ class Gem::Commands::SetupCommand < Gem::Command
add_option '--[no-]rdoc',
'Generate RDoc documentation for RubyGems' do |value, options|
- if value then
+ if value
options[:document] << 'rdoc'
else
options[:document].delete 'rdoc'
@@ -70,7 +73,7 @@ class Gem::Commands::SetupCommand < Gem::Command
add_option '--[no-]ri',
'Generate RI documentation for RubyGems' do |value, options|
- if value then
+ if value
options[:document] << 'ri'
else
options[:document].delete 'ri'
@@ -79,20 +82,31 @@ class Gem::Commands::SetupCommand < Gem::Command
options[:document].uniq!
end
+ add_option '--[no-]regenerate-binstubs',
+ 'Regenerate gem binstubs' do |value, options|
+ options[:regenerate_binstubs] = value
+ end
+
+ add_option('-E', '--[no-]env-shebang',
+ 'Rewrite executables with a shebang',
+ 'of /usr/bin/env') do |value, options|
+ options[:env_shebang] = value
+ end
+
@verbose = nil
end
def check_ruby_version
required_version = Gem::Requirement.new '>= 1.8.7'
- unless required_version.satisfied_by? Gem.ruby_version then
+ unless required_version.satisfied_by? Gem.ruby_version
alert_error "Expected Ruby version #{required_version}, is #{Gem.ruby_version}"
terminate_interaction 1
end
end
def defaults_str # :nodoc:
- "--format-executable --document ri"
+ "--format-executable --document ri --regenerate-binstubs"
end
def description # :nodoc:
@@ -113,12 +127,19 @@ By default, this RubyGems will install gem as:
EOF
end
+ module MakeDirs
+ def mkdir_p(path, *opts)
+ super
+ (@mkdirs ||= []) << path
+ end
+ end
+
def execute
@verbose = Gem.configuration.really_verbose
install_destdir = options[:destdir]
- unless install_destdir.empty? then
+ unless install_destdir.empty?
ENV['GEM_HOME'] ||= File.join(install_destdir,
Gem.default_dir.gsub(/^[a-zA-Z]:/, ''))
end
@@ -126,11 +147,12 @@ By default, this RubyGems will install gem as:
check_ruby_version
require 'fileutils'
- if Gem.configuration.really_verbose then
+ if Gem.configuration.really_verbose
extend FileUtils::Verbose
else
extend FileUtils
end
+ extend MakeDirs
lib_dir, bin_dir = make_destination_dirs install_destdir
@@ -142,14 +164,23 @@ By default, this RubyGems will install gem as:
remove_old_lib_files lib_dir
+ install_default_bundler_gem
+
+ if mode = options[:dir_mode]
+ @mkdirs.uniq!
+ File.chmod(mode, @mkdirs)
+ end
+
say "RubyGems #{Gem::VERSION} installed"
+ regenerate_binstubs if options[:regenerate_binstubs]
+
uninstall_old_gemcutter
documentation_success = install_rdoc
say
- if @verbose then
+ if @verbose
say "-" * 78
say
end
@@ -170,14 +201,14 @@ By default, this RubyGems will install gem as:
say @bin_file_names.map { |name| "\t#{name}\n" }
say
- unless @bin_file_names.grep(/#{File::SEPARATOR}gem$/) then
+ unless @bin_file_names.grep(/#{File::SEPARATOR}gem$/)
say "If `gem` was installed by a previous RubyGems installation, you may need"
say "to remove it by hand."
say
end
if documentation_success
- if options[:document].include? 'rdoc' then
+ 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"
@@ -188,9 +219,9 @@ By default, this RubyGems will install gem as:
say
end
- if options[:document].include? 'ri' then
+ 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 "pages for Ruby libraries. You may access it like this:"
say " ri Classname"
say " ri Classname.class_method"
say " ri Classname#instance_method"
@@ -202,85 +233,110 @@ By default, this RubyGems will install gem as:
end
end
- def install_executables(bin_dir)
- say "Installing gem executable" if @verbose
+ def install_executables(bin_dir)
@bin_file_names = []
- Dir.chdir 'bin' do
- bin_files = Dir['*']
+ prog_mode = options[:prog_mode] || 0755
- bin_files.delete 'update_rubygems'
+ executables = { 'gem' => 'bin' }
+ executables['bundler'] = 'bundler/exe' if Gem::USE_BUNDLER_FOR_GEMDEPS
+ executables.each do |tool, path|
+ say "Installing #{tool} executable" if @verbose
- bin_files.each do |bin_file|
- bin_file_formatted = if options[:format_executable] then
- Gem.default_exec_format % bin_file
- else
- bin_file
- end
+ Dir.chdir path do
+ bin_files = Dir['*']
- dest_file = File.join bin_dir, bin_file_formatted
- bin_tmp_file = File.join Dir.tmpdir, "#{bin_file}.#{$$}"
+ bin_files -= %w[update_rubygems bundler bundle_ruby]
- begin
- bin = File.readlines bin_file
- bin[0] = "#!#{Gem.ruby}\n"
+ bin_files.each do |bin_file|
+ bin_file_formatted = if options[:format_executable]
+ Gem.default_exec_format % bin_file
+ else
+ bin_file
+ end
- File.open bin_tmp_file, 'w' do |fp|
- fp.puts bin.join
- end
+ dest_file = File.join bin_dir, bin_file_formatted
+ bin_tmp_file = File.join Dir.tmpdir, "#{bin_file}.#{$$}"
- install bin_tmp_file, dest_file, :mode => 0755
- @bin_file_names << dest_file
- ensure
- rm bin_tmp_file
- end
+ begin
+ bin = File.readlines bin_file
+ bin[0] = shebang
- next unless Gem.win_platform?
+ File.open bin_tmp_file, 'w' do |fp|
+ fp.puts bin.join
+ end
- begin
- bin_cmd_file = File.join Dir.tmpdir, "#{bin_file}.bat"
-
- File.open bin_cmd_file, 'w' do |file|
- file.puts <<-TEXT
-@ECHO OFF
-IF NOT "%~f0" == "~f0" GOTO :WinNT
-@"#{File.basename(Gem.ruby).chomp('"')}" "#{dest_file}" %1 %2 %3 %4 %5 %6 %7 %8 %9
-GOTO :EOF
-:WinNT
-@"#{File.basename(Gem.ruby).chomp('"')}" "%~dpn0" %*
-TEXT
+ install bin_tmp_file, dest_file, :mode => prog_mode
+ @bin_file_names << dest_file
+ ensure
+ rm bin_tmp_file
end
- install bin_cmd_file, "#{dest_file}.bat", :mode => 0755
- ensure
- rm bin_cmd_file
+ next unless Gem.win_platform?
+
+ begin
+ bin_cmd_file = File.join Dir.tmpdir, "#{bin_file}.bat"
+
+ File.open bin_cmd_file, 'w' do |file|
+ file.puts <<-TEXT
+ @ECHO OFF
+ IF NOT "%~f0" == "~f0" GOTO :WinNT
+ @"#{File.basename(Gem.ruby).chomp('"')}" "#{dest_file}" %1 %2 %3 %4 %5 %6 %7 %8 %9
+ GOTO :EOF
+ :WinNT
+ @"#{File.basename(Gem.ruby).chomp('"')}" "%~dpn0" %*
+ TEXT
+ end
+
+ install bin_cmd_file, "#{dest_file}.bat", :mode => prog_mode
+ ensure
+ rm bin_cmd_file
+ end
end
end
end
end
- def install_file file, dest_dir
+ def shebang
+ if options[:env_shebang]
+ ruby_name = RbConfig::CONFIG['ruby_install_name']
+ @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path }
+ "#!#{@env_path} #{ruby_name}\n"
+ else
+ "#!#{Gem.ruby}\n"
+ end
+ end
+
+ def install_file(file, dest_dir)
dest_file = File.join dest_dir, file
dest_dir = File.dirname dest_file
- mkdir_p dest_dir unless File.directory? dest_dir
+ unless File.directory? dest_dir
+ mkdir_p dest_dir, :mode => 0755
+ end
- install file, dest_file, :mode => 0644
+ install file, dest_file, :mode => options[:data_mode] || 0644
end
def install_lib(lib_dir)
- say "Installing RubyGems" if @verbose
+ libs = { 'RubyGems' => 'lib' }
+ libs['Bundler'] = 'bundler/lib' if Gem::USE_BUNDLER_FOR_GEMDEPS
+ libs.each do |tool, path|
+ say "Installing #{tool}" if @verbose
- lib_files = rb_files_in 'lib'
- pem_files = pem_files_in 'lib'
+ lib_files = rb_files_in path
+ lib_files.concat(template_files) if tool == 'Bundler'
- Dir.chdir 'lib' do
- lib_files.each do |lib_file|
- install_file lib_file, lib_dir
- end
+ pem_files = pem_files_in path
- pem_files.each do |pem_file|
- install_file pem_file, lib_dir
+ Dir.chdir path do
+ lib_files.each do |lib_file|
+ install_file lib_file, lib_dir
+ end
+
+ pem_files.each do |pem_file|
+ install_file pem_file, lib_dir
+ end
end
end
end
@@ -298,7 +354,7 @@ TEXT
if File.writable? gem_doc_dir and
(not File.exist? rubygems_doc_dir or
- File.writable? rubygems_doc_dir) then
+ 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|
rm_rf dir
@@ -318,7 +374,7 @@ TEXT
rdoc.generate
return true
- elsif @verbose then
+ elsif @verbose
say "Skipping RDoc generation, #{gem_doc_dir} not writable"
say "Set the GEM_HOME environment variable if you want RDoc generated"
end
@@ -326,6 +382,63 @@ TEXT
return false
end
+ def install_default_bundler_gem
+ return unless Gem::USE_BUNDLER_FOR_GEMDEPS
+
+ specs_dir = Gem::Specification.default_specifications_dir
+ specs_dir = File.join(options[:destdir], specs_dir) unless Gem.win_platform?
+ mkdir_p specs_dir, :mode => 0755
+
+ # Workaround for non-git environment.
+ gemspec = File.open('bundler/bundler.gemspec', 'rb'){|f| f.read.gsub(/`git ls-files -z`/, "''") }
+ File.open('bundler/bundler.gemspec', 'w'){|f| f.write gemspec }
+
+ bundler_spec = Gem::Specification.load("bundler/bundler.gemspec")
+ bundler_spec.files = Dir.chdir("bundler") { Dir["{*.md,{lib,exe,man}/**/*}"] }
+ bundler_spec.executables -= %w[bundler bundle_ruby]
+
+ # Remove bundler-*.gemspec in default specification directory.
+ Dir.entries(specs_dir).
+ select {|gs| gs.start_with?("bundler-") }.
+ each {|gs| File.delete(File.join(specs_dir, gs)) }
+
+ default_spec_path = File.join(specs_dir, "#{bundler_spec.full_name}.gemspec")
+ Gem.write_binary(default_spec_path, bundler_spec.to_ruby)
+
+ bundler_spec = Gem::Specification.load(default_spec_path)
+
+ # Remove gemspec that was same version of vendored bundler.
+ normal_gemspec = File.join(Gem.default_dir, "specifications", "bundler-#{bundler_spec.version}.gemspec")
+ if File.file? normal_gemspec
+ File.delete normal_gemspec
+ end
+
+ # Remove gem files that were same version of vendored bundler.
+ if File.directory? bundler_spec.gems_dir
+ Dir.entries(bundler_spec.gems_dir).
+ select {|default_gem| File.basename(default_gem) == "bundler-#{bundler_spec.version}" }.
+ each {|default_gem| rm_r File.join(bundler_spec.gems_dir, default_gem) }
+ end
+
+ bundler_bin_dir = bundler_spec.bin_dir
+ bundler_bin_dir = File.join(options[:destdir], bundler_bin_dir) unless Gem.win_platform?
+ mkdir_p bundler_bin_dir, :mode => 0755
+ bundler_spec.executables.each do |e|
+ cp File.join("bundler", bundler_spec.bindir, e), File.join(bundler_bin_dir, e)
+ end
+
+ if Gem.win_platform?
+ require 'rubygems/installer'
+
+ installer = Gem::Installer.for_spec bundler_spec
+ bundler_spec.executables.each do |e|
+ installer.generate_windows_script e, bundler_spec.bin_dir
+ end
+ end
+
+ say "Bundler #{bundler_spec.version} installed"
+ end
+
def make_destination_dirs(install_destdir)
lib_dir, bin_dir = Gem.default_rubygems_dirs
@@ -333,8 +446,8 @@ TEXT
lib_dir, bin_dir = generate_default_dirs(install_destdir)
end
- mkdir_p lib_dir
- mkdir_p bin_dir
+ mkdir_p lib_dir, :mode => 0755
+ mkdir_p bin_dir, :mode => 0755
return lib_dir, bin_dir
end
@@ -343,7 +456,7 @@ TEXT
prefix = options[:prefix]
site_or_vendor = options[:site_or_vendor]
- if prefix.empty? then
+ if prefix.empty?
lib_dir = RbConfig::CONFIG[site_or_vendor]
bin_dir = RbConfig::CONFIG['bindir']
else
@@ -354,16 +467,16 @@ TEXT
# just in case Apple and RubyGems don't get this patched up proper.
(prefix == RbConfig::CONFIG['libdir'] or
# this one is important
- prefix == File.join(RbConfig::CONFIG['libdir'], 'ruby')) then
- lib_dir = RbConfig::CONFIG[site_or_vendor]
- bin_dir = RbConfig::CONFIG['bindir']
+ prefix == File.join(RbConfig::CONFIG['libdir'], 'ruby'))
+ lib_dir = RbConfig::CONFIG[site_or_vendor]
+ bin_dir = RbConfig::CONFIG['bindir']
else
lib_dir = File.join prefix, 'lib'
bin_dir = File.join prefix, 'bin'
end
end
- unless install_destdir.empty? then
+ unless install_destdir.empty?
lib_dir = File.join install_destdir, lib_dir.gsub(/^[a-zA-Z]:/, '')
bin_dir = File.join install_destdir, bin_dir.gsub(/^[a-zA-Z]:/, '')
end
@@ -371,18 +484,34 @@ TEXT
[lib_dir, bin_dir]
end
- def pem_files_in dir
+ def pem_files_in(dir)
Dir.chdir dir do
Dir[File.join('**', '*pem')]
end
end
- def rb_files_in dir
+ def rb_files_in(dir)
Dir.chdir dir do
Dir[File.join('**', '*rb')]
end
end
+ # for installation of bundler as default gems
+ def template_files
+ Dir.chdir "bundler/lib" do
+ (Dir[File.join('bundler', 'templates', '**', '{*,.*}')]).
+ select{|f| !File.directory?(f)}
+ end
+ end
+
+ # for cleanup old bundler files
+ def template_files_in(dir)
+ Dir.chdir dir do
+ (Dir[File.join('templates', '**', '{*,.*}')]).
+ select{|f| !File.directory?(f)}
+ end
+ end
+
def remove_old_bin_files(bin_dir)
old_bin_files = {
'gem_mirror' => 'gem mirror',
@@ -397,7 +526,7 @@ TEXT
old_bin_path = File.join bin_dir, old_bin_file
next unless File.exist? old_bin_path
- deprecation_message = "`#{old_bin_file}` has been deprecated. Use `#{new_name}` instead."
+ deprecation_message = "`#{old_bin_file}` has been deprecated. Use `#{new_name}` instead."
File.open old_bin_path, 'w' do |fp|
fp.write <<-EOF
@@ -415,24 +544,29 @@ abort "#{deprecation_message}"
end
end
- def remove_old_lib_files lib_dir
- rubygems_dir = File.join lib_dir, 'rubygems'
- lib_files = rb_files_in 'lib/rubygems'
+ 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' if Gem::USE_BUNDLER_FOR_GEMDEPS
+ lib_dirs.each do |old_lib_dir, new_lib_dir|
+ lib_files = rb_files_in(new_lib_dir)
+ lib_files.concat(template_files_in(new_lib_dir)) if new_lib_dir =~ /bundler/
- old_lib_files = rb_files_in rubygems_dir
+ old_lib_files = rb_files_in(old_lib_dir)
+ old_lib_files.concat(template_files_in(old_lib_dir)) if old_lib_dir =~ /bundler/
- to_remove = old_lib_files - lib_files
+ to_remove = old_lib_files - lib_files
- to_remove.delete_if do |file|
- file.start_with? 'defaults'
- end
+ to_remove.delete_if do |file|
+ file.start_with? 'defaults'
+ end
- Dir.chdir rubygems_dir do
- to_remove.each do |file|
- FileUtils.rm_f file
+ Dir.chdir old_lib_dir do
+ to_remove.each do |file|
+ FileUtils.rm_f file
- warn "unable to remove old file #{file} please remove it by hand" if
- File.exist? file
+ warn "unable to remove old file #{file} please remove it by hand" if
+ File.exist? file
+ end
end
end
end
@@ -441,11 +575,10 @@ abort "#{deprecation_message}"
release_notes = File.join Dir.pwd, 'History.txt'
release_notes =
- if File.exist? release_notes then
+ if File.exist? release_notes
history = File.read release_notes
- history.force_encoding Encoding::UTF_8 if
- Object.const_defined? :Encoding
+ history.force_encoding Encoding::UTF_8
history = history.sub(/^# coding:.*?(?=^=)/m, '')
@@ -480,5 +613,17 @@ abort "#{deprecation_message}"
rescue Gem::InstallError
end
-end
+ def regenerate_binstubs
+ require "rubygems/commands/pristine_command"
+ say "Regenerating binstubs"
+
+ args = %w[--all --only-executables --silent]
+ if options[:env_shebang]
+ args << "--env-shebang"
+ end
+ command = Gem::Commands::PristineCommand.new
+ command.invoke(*args)
+ end
+
+end
diff --git a/lib/rubygems/commands/signin_command.rb b/lib/rubygems/commands/signin_command.rb
new file mode 100644
index 0000000000..0d527fc339
--- /dev/null
+++ b/lib/rubygems/commands/signin_command.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+require 'rubygems/command'
+require 'rubygems/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'
+
+ add_option('--host HOST', 'Push to another gemcutter-compatible host') do |value, options|
+ options[:host] = value
+ end
+
+ add_otp_option
+ 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.'
+ end
+
+ def usage # :nodoc:
+ program_name
+ end
+
+ def execute
+ sign_in options[:host]
+ end
+
+end
diff --git a/lib/rubygems/commands/signout_command.rb b/lib/rubygems/commands/signout_command.rb
new file mode 100644
index 0000000000..2d7329c590
--- /dev/null
+++ b/lib/rubygems/commands/signout_command.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+require 'rubygems/command'
+
+class Gem::Commands::SignoutCommand < Gem::Command
+
+ def initialize
+ 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.'
+ end
+
+ def usage # :nodoc:
+ program_name
+ end
+
+ def execute
+ credentials_path = Gem.configuration.credentials_path
+
+ if !File.exist?(credentials_path)
+ 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.'
+ else
+ Gem.configuration.unset_api_key!
+ 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 9832afd214..af145fd7b4 100644
--- a/lib/rubygems/commands/sources_command.rb
+++ b/lib/rubygems/commands/sources_command.rb
@@ -38,13 +38,13 @@ class Gem::Commands::SourcesCommand < Gem::Command
add_proxy_option
end
- def add_source source_uri # :nodoc:
+ def add_source(source_uri) # :nodoc:
check_rubygems_https source_uri
source = Gem::Source.new source_uri
begin
- if Gem.sources.include? source_uri then
+ if Gem.sources.include? source
say "source #{source_uri} already present in the cache"
else
source.load_specs :released
@@ -62,11 +62,11 @@ class Gem::Commands::SourcesCommand < Gem::Command
end
end
- def check_rubygems_https source_uri # :nodoc:
+ 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' then
+ uri.host.downcase == 'rubygems.org'
question = <<-QUESTION.chomp
https://rubygems.org is recommended for security over #{uri}
@@ -81,10 +81,10 @@ Do you want to add this insecure source?
path = Gem.spec_cache_dir
FileUtils.rm_rf path
- unless File.exist? path then
+ unless File.exist? path
say "*** Removed specs cache ***"
else
- unless File.writable? path then
+ unless File.writable? path
say "*** Unable to remove source cache (write protected) ***"
else
say "*** Unable to remove source cache ***"
@@ -175,8 +175,8 @@ To remove a source use the --remove argument:
list if list?
end
- def remove_source source_uri # :nodoc:
- unless Gem.sources.include? source_uri then
+ def remove_source(source_uri) # :nodoc:
+ unless Gem.sources.include? source_uri
say "source #{source_uri} not present in cache"
else
Gem.sources.delete source_uri
@@ -195,12 +195,12 @@ To remove a source use the --remove argument:
say "source cache successfully updated"
end
- def remove_cache_file desc, path # :nodoc:
+ def remove_cache_file(desc, path) # :nodoc:
FileUtils.rm_rf path
- if not File.exist?(path) then
+ if not File.exist?(path)
say "*** Removed #{desc} source cache ***"
- elsif not File.writable?(path) then
+ elsif not File.writable?(path)
say "*** Unable to remove #{desc} source cache (write protected) ***"
else
say "*** Unable to remove #{desc} source cache ***"
@@ -208,4 +208,3 @@ To remove a source use the --remove argument:
end
end
-
diff --git a/lib/rubygems/commands/specification_command.rb b/lib/rubygems/commands/specification_command.rb
index ad8840adc2..56b9371686 100644
--- a/lib/rubygems/commands/specification_command.rb
+++ b/lib/rubygems/commands/specification_command.rb
@@ -75,7 +75,7 @@ Specific fields in the specification can be extracted in YAML format:
specs = []
gem = options[:args].shift
- unless gem then
+ unless gem
raise Gem::CommandLineError,
"Please specify a gem name or file on the command line"
end
@@ -105,29 +105,29 @@ Specific fields in the specification can be extracted in YAML format:
raise Gem::CommandLineError, "--ruby and FIELD are mutually exclusive" if
field and options[:format] == :ruby
- if local? then
- if File.exist? gem then
+ if local?
+ if File.exist? gem
specs << Gem::Package.new(gem).spec rescue nil
end
- if specs.empty? then
+ if specs.empty?
specs.push(*dep.matching_specs)
end
end
- if remote? then
+ if remote?
dep.prerelease = options[:prerelease]
found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dep
specs.push(*found.map { |spec,| spec })
end
- if specs.empty? then
+ if specs.empty?
alert_error "No gem matching '#{dep}' found"
terminate_interaction 1
end
- unless options[:all] then
+ unless options[:all]
specs = [specs.max_by { |s| s.version }]
end
diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb
index fe97790194..9de0ea722b 100644
--- a/lib/rubygems/commands/uninstall_command.rb
+++ b/lib/rubygems/commands/uninstall_command.rb
@@ -20,7 +20,7 @@ class Gem::Commands::UninstallCommand < Gem::Command
add_option('-a', '--[no-]all',
'Uninstall all matching versions'
- ) do |value, options|
+ ) do |value, options|
options[:all] = value
end
@@ -30,7 +30,7 @@ class Gem::Commands::UninstallCommand < Gem::Command
options[:ignore] = value
end
- add_option('-D', '--[no-]-check-development',
+ add_option('-D', '--[no-]check-development',
'Check development dependencies while uninstalling',
'(default: false)') do |value, options|
options[:check_dev] = value
@@ -48,7 +48,7 @@ class Gem::Commands::UninstallCommand < Gem::Command
end
add_option('-n', '--bindir DIR',
- 'Directory to remove binaries from') do |value, options|
+ 'Directory to remove executables from') do |value, options|
options[:bin_dir] = File.expand_path(value)
end
@@ -81,7 +81,7 @@ class Gem::Commands::UninstallCommand < Gem::Command
add_option('--vendor',
'Uninstall gem from the vendor directory.',
'Only for use by gem repackagers.') do |value, options|
- unless Gem.vendor_dir then
+ unless Gem.vendor_dir
raise OptionParser::InvalidOption.new 'your platform is not supported'
end
@@ -114,10 +114,21 @@ that is a dependency of an existing gem. You can use the
"#{program_name} GEMNAME [GEMNAME ...]"
end
+ def check_version # :nodoc:
+ if options[:version] != Gem::Requirement.default and
+ 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
+ end
+ end
+
def execute
- if options[:all] and not options[:args].empty? then
+ check_version
+
+ if options[:all] and not options[:args].empty?
uninstall_specific
- elsif options[:all] then
+ elsif options[:all]
uninstall_all
else
uninstall_specific
@@ -129,11 +140,7 @@ that is a dependency of an existing gem. You can use the
specs.each do |spec|
options[:version] = spec.version
-
- begin
- Gem::Uninstaller.new(spec.name, options).uninstall
- rescue Gem::InstallError
- end
+ uninstall_gem spec.name
end
alert "Uninstalled all gems in #{options[:install_dir]}"
@@ -142,24 +149,40 @@ that is a dependency of an existing gem. You can use the
def uninstall_specific
deplist = Gem::DependencyList.new
- get_all_gem_names.uniq.each do |name|
- Gem::Specification.find_all_by_name(name).each do |spec|
+ get_all_gem_names_and_versions.each do |name, version|
+ requirement = Array(version || options[:version])
+ gem_specs = Gem::Specification.find_all_by_name(name, *requirement)
+ say("Gem '#{name}' is not installed") if gem_specs.empty?
+ gem_specs.each do |spec|
deplist.add spec
end
end
deps = deplist.strongly_connected_components.flatten.reverse
- deps.map(&:name).uniq.each do |gem_name|
- begin
- Gem::Uninstaller.new(gem_name, options).uninstall
- rescue Gem::GemNotInHomeException => e
- spec = e.spec
- alert("In order to remove #{spec.name}, please execute:\n" +
- "\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}")
- end
+ deps.each do |dep|
+ options[:version] = dep.version
+ uninstall_gem(dep.name)
end
end
-end
+ def uninstall_gem(gem_name)
+ uninstall(gem_name)
+ rescue Gem::InstallError
+ nil
+ rescue Gem::GemNotInHomeException => e
+ spec = e.spec
+ alert("In order to remove #{spec.name}, please execute:\n" +
+ "\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}")
+ rescue Gem::UninstallError => e
+ spec = e.spec
+ alert_error("Error: unable to successfully uninstall '#{spec.name}' which is " +
+ "located at '#{spec.full_gem_path}'. This is most likely because" +
+ "the current user does not have the appropriate permissions")
+ terminate_interaction 1
+ end
+ def uninstall(gem_name)
+ Gem::Uninstaller.new(gem_name, options).uninstall
+ end
+end
diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb
index ffa429de6f..4a1bd8a0d6 100644
--- a/lib/rubygems/commands/unpack_command.rb
+++ b/lib/rubygems/commands/unpack_command.rb
@@ -2,11 +2,20 @@
require 'rubygems/command'
require 'rubygems/installer'
require 'rubygems/version_option'
+require 'rubygems/security_option'
require 'rubygems/remote_fetcher'
+# forward-declare
+
+module Gem::Security # :nodoc:
+ class Policy # :nodoc:
+ end
+end
+
class Gem::Commands::UnpackCommand < Gem::Command
include Gem::VersionOption
+ include Gem::SecurityOption
def initialize
require 'fileutils'
@@ -24,6 +33,7 @@ class Gem::Commands::UnpackCommand < Gem::Command
options[:spec] = true
end
+ add_security_option
add_version_option
end
@@ -63,33 +73,45 @@ command help for an example.
# at the same time.)
def execute
+ security_policy = options[:security_policy]
+
get_all_gem_names.each do |name|
dependency = Gem::Dependency.new name, options[:version]
path = get_path dependency
- unless path then
+ unless path
alert_error "Gem '#{name}' not installed nor fetchable."
next
end
- if @options[:spec] then
- spec, metadata = get_metadata path
+ if @options[:spec]
+ spec, metadata = get_metadata path, security_policy
- if metadata.nil? then
+ if metadata.nil?
alert_error "--spec is unsupported on '#{name}' (old format gem)"
next
end
spec_file = File.basename spec.spec_file
- open spec_file, 'w' do |io|
+ FileUtils.mkdir_p @options[:target] if @options[:target]
+
+ destination = begin
+ if @options[:target]
+ File.join @options[:target], spec_file
+ else
+ spec_file
+ end
+ end
+
+ File.open destination, 'w' do |io|
io.write metadata
end
else
basename = File.basename path, '.gem'
target_dir = File.expand_path basename, options[:target]
- package = Gem::Package.new path
+ package = Gem::Package.new path, security_policy
package.extract_files target_dir
say "Unpacked gem: '#{target_dir}'"
@@ -130,7 +152,7 @@ command help for an example.
# TODO: It just uses Gem.dir for now. What's an easy way to get the list of
# source directories?
- def get_path dependency
+ def get_path(dependency)
return dependency.name if dependency.name =~ /\.gem$/i
specs = dependency.matching_specs
@@ -158,20 +180,20 @@ command help for an example.
#--
# TODO move to Gem::Package as #raw_spec or something
- def get_metadata path
- format = Gem::Package.new path
+ def get_metadata(path, security_policy = nil)
+ format = Gem::Package.new path, security_policy
spec = format.spec
metadata = nil
- open path, Gem.binary_mode do |io|
+ File.open path, Gem.binary_mode do |io|
tar = Gem::Package::TarReader.new io
tar.each_entry do |entry|
case entry.full_name
when 'metadata' then
metadata = entry.read
when 'metadata.gz' then
- metadata = Gem.gunzip entry.read
+ metadata = Gem::Util.gunzip entry.read
end
end
end
@@ -180,4 +202,3 @@ command help for an example.
end
end
-
diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb
index f3d70a92a6..9ab3b80e96 100644
--- a/lib/rubygems/commands/update_command.rb
+++ b/lib/rubygems/commands/update_command.rb
@@ -68,9 +68,9 @@ command to remove old versions.
"#{program_name} GEMNAME [GEMNAME ...]"
end
- def check_latest_rubygems version # :nodoc:
- if Gem.rubygems_version == version then
- say "Latest version currently installed. Aborting."
+ def check_latest_rubygems(version) # :nodoc:
+ if Gem.rubygems_version == version
+ say "Latest version already installed. Done."
terminate_interaction
end
@@ -78,31 +78,40 @@ command to remove old versions.
end
def check_update_arguments # :nodoc:
- unless options[:args].empty? then
+ unless options[:args].empty?
alert_error "Gem names are not allowed with the --system option"
terminate_interaction 1
end
end
def execute
-
- if options[:system] then
+ if options[:system]
update_rubygems
return
end
- say "Updating installed gems"
-
hig = highest_installed_gems
gems_to_update = which_to_update hig, options[:args].uniq
+ if options[:explain]
+ say "Gems to update:"
+
+ gems_to_update.each do |(name, version)|
+ say " #{name}-#{version}"
+ end
+
+ return
+ end
+
+ say "Updating installed gems"
+
updated = update_gems gems_to_update
updated_names = updated.map { |spec| spec.name }
not_updated_names = options[:args].uniq - updated_names
- if updated.empty? then
+ if updated.empty?
say "Nothing to update"
else
say "Gems updated: #{updated_names.join(' ')}"
@@ -110,7 +119,7 @@ command to remove old versions.
end
end
- def fetch_remote_gems spec # :nodoc:
+ def fetch_remote_gems(spec) # :nodoc:
dependency = Gem::Dependency.new spec.name, "> #{spec.version}"
dependency.prerelease = options[:prerelease]
@@ -129,7 +138,7 @@ command to remove old versions.
hig = {} # highest installed gems
Gem::Specification.each do |spec|
- if hig[spec.name].nil? or hig[spec.name].version < spec.version then
+ if hig[spec.name].nil? or hig[spec.name].version < spec.version
hig[spec.name] = spec
end
end
@@ -137,7 +146,7 @@ command to remove old versions.
hig
end
- def highest_remote_version spec # :nodoc:
+ def highest_remote_version(spec) # :nodoc:
spec_tuples = fetch_remote_gems spec
matching_gems = spec_tuples.select do |g,_|
@@ -151,7 +160,7 @@ command to remove old versions.
highest_remote_gem.first.version
end
- def install_rubygems version # :nodoc:
+ def install_rubygems(version) # :nodoc:
args = update_rubygems_arguments
update_dir = File.join Gem.dir, 'gems', "rubygems-update-#{version}"
@@ -159,12 +168,8 @@ command to remove old versions.
Dir.chdir update_dir do
say "Installing RubyGems #{version}"
- # Make sure old rubygems isn't loaded
- old = ENV["RUBYOPT"]
- ENV.delete("RUBYOPT") if old
- installed = system Gem.ruby, 'setup.rb', *args
+ installed = system Gem.ruby, '--disable-gems', 'setup.rb', *args
say "RubyGems system software updated" if installed
- ENV["RUBYOPT"] = old if old
end
end
@@ -172,7 +177,7 @@ command to remove old versions.
version = options[:system]
update_latest = version == true
- if update_latest then
+ if update_latest
version = Gem::Version.new Gem::VERSION
requirement = Gem::Requirement.new ">= #{Gem::VERSION}"
else
@@ -191,7 +196,7 @@ command to remove old versions.
gems_to_update = which_to_update hig, options[:args], :system
_, up_ver = gems_to_update.first
- target = if update_latest then
+ target = if update_latest
up_ver
else
version
@@ -200,7 +205,7 @@ command to remove old versions.
return target, requirement
end
- def update_gem name, version = Gem::Requirement.default
+ def update_gem(name, version = Gem::Requirement.default)
return if @updated.any? { |spec| spec.name == name }
update_options = options.dup
@@ -220,7 +225,7 @@ command to remove old versions.
end
end
- def update_gems gems_to_update
+ def update_gems(gems_to_update)
gems_to_update.uniq.sort.each do |(name, version)|
update_gem name, version
end
@@ -259,7 +264,7 @@ command to remove old versions.
args
end
- def which_to_update highest_installed_gems, gem_names, system = false
+ def which_to_update(highest_installed_gems, gem_names, system = false)
result = []
highest_installed_gems.each do |l_name, l_spec|
@@ -268,7 +273,7 @@ command to remove old versions.
highest_remote_ver = highest_remote_version l_spec
- if system or (l_spec.version < highest_remote_ver) then
+ if system or (l_spec.version < highest_remote_ver)
result << [l_spec.name, [l_spec.version, highest_remote_ver].max]
end
end
diff --git a/lib/rubygems/commands/which_command.rb b/lib/rubygems/commands/which_command.rb
index c028d5d49f..5c51ed72dd 100644
--- a/lib/rubygems/commands/which_command.rb
+++ b/lib/rubygems/commands/which_command.rb
@@ -44,8 +44,8 @@ requiring to see why it does not behave as you expect.
spec = Gem::Specification.find_by_path arg
- if spec then
- if options[:search_gems_first] then
+ if spec
+ if options[:search_gems_first]
dirs = spec.full_require_paths + $LOAD_PATH
else
dirs = $LOAD_PATH + spec.full_require_paths
@@ -55,8 +55,8 @@ requiring to see why it does not behave as you expect.
# TODO: this is totally redundant and stupid
paths = find_paths arg, dirs
- if paths.empty? then
- alert_error "Can't find ruby library file or shared library #{arg}"
+ if paths.empty?
+ alert_error "Can't find Ruby library file or shared library #{arg}"
found &&= false
else
@@ -73,7 +73,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 then
+ if File.exist? full_path and not File.directory? full_path
result << full_path
return result unless options[:show_all]
end
@@ -88,4 +88,3 @@ requiring to see why it does not behave as you expect.
end
end
-
diff --git a/lib/rubygems/commands/yank_command.rb b/lib/rubygems/commands/yank_command.rb
index 0d6575b272..d13b674b48 100644
--- a/lib/rubygems/commands/yank_command.rb
+++ b/lib/rubygems/commands/yank_command.rb
@@ -11,19 +11,11 @@ class Gem::Commands::YankCommand < Gem::Command
def description # :nodoc:
<<-EOF
-The yank command removes a gem you pushed to a server from the server's
-index.
-
-Note that if you push a gem to rubygems.org the yank command does not
-prevent other people from downloading the gem via the download link.
+The yank command permanently removes a gem you pushed to a server.
Once you have pushed a gem several downloads will happen automatically
-via the webhooks. If you accidentally pushed passwords or other sensitive
+via the webhooks. If you accidentally pushed passwords or other sensitive
data you will need to change them immediately and yank your gem.
-
-If you are yanking a gem due to intellectual property reasons contact
-http://help.rubygems.org for permanent removal. Be sure to mention this
-as the reason for the removal request.
EOF
end
@@ -42,7 +34,8 @@ as the reason for the removal request.
add_platform_option("remove")
add_option('--host HOST',
- 'Yank from another gemcutter-compatible host') do |value, options|
+ 'Yank from another gemcutter-compatible host',
+ ' (e.g. https://rubygems.org)') do |value, options|
options[:host] = value
end
@@ -58,7 +51,7 @@ as the reason for the removal request.
version = get_version_from_requirements(options[:version])
platform = get_platform_from_requirements(options)
- if version then
+ if version
yank_gem(version, platform)
else
say "A version argument is required: #{usage}"
@@ -100,4 +93,3 @@ as the reason for the removal request.
end
end
-
diff --git a/lib/rubygems/compatibility.rb b/lib/rubygems/compatibility.rb
index 2056b5b53a..b4332eb9f1 100644
--- a/lib/rubygems/compatibility.rb
+++ b/lib/rubygems/compatibility.rb
@@ -9,26 +9,6 @@
# Ruby 1.9.x has introduced some things that are awkward, and we need to
# support them, so we define some constants to use later.
#++
-module Gem
- # Only MRI 1.9.2 has the custom prelude.
- GEM_PRELUDE_SUCKAGE = RUBY_VERSION =~ /^1\.9\.2/ and RUBY_ENGINE == "ruby"
-end
-
-# Gem::QuickLoader exists in the gem prelude code in ruby 1.9.2 itself.
-# We gotta get rid of it if it's there, before we do anything else.
-if Gem::GEM_PRELUDE_SUCKAGE and defined?(Gem::QuickLoader) then
- Gem::QuickLoader.remove
-
- $LOADED_FEATURES.delete Gem::QuickLoader.path_to_full_rubygems_library
-
- if path = $LOADED_FEATURES.find {|n| n.end_with? '/rubygems.rb'} then
- raise LoadError, "another rubygems is already loaded from #{path}"
- end
-
- class << Gem
- remove_method :try_activate if Gem.respond_to?(:try_activate, true)
- end
-end
module Gem
RubyGemsVersion = VERSION
@@ -42,7 +22,7 @@ module Gem
EXEEXT RUBY_SO_NAME arch bindir datadir libdir ruby_install_name
ruby_version rubylibprefix sitedir sitelibdir vendordir vendorlibdir
rubylibdir
- ]
+ ].freeze
unless defined?(ConfigMap)
##
diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb
index c95d7dd1f1..e02655fc51 100644
--- a/lib/rubygems/config_file.rb
+++ b/lib/rubygems/config_file.rb
@@ -27,6 +27,7 @@ require 'rbconfig'
# +:backtrace+:: See #backtrace
# +:sources+:: Sets Gem::sources
# +:verbose+:: See #verbose
+# +:concurrent_downloads+:: See #concurrent_downloads
#
# gemrc files may exist in various locations and are read and merged in
# the following order:
@@ -43,12 +44,14 @@ class Gem::ConfigFile
DEFAULT_BULK_THRESHOLD = 1000
DEFAULT_VERBOSITY = true
DEFAULT_UPDATE_SOURCES = true
+ DEFAULT_CONCURRENT_DOWNLOADS = 8
+ DEFAULT_CERT_EXPIRATION_LENGTH_DAYS = 365
##
# For Ruby packagers to set configuration defaults. Set in
# rubygems/defaults/operating_system.rb
- OPERATING_SYSTEM_DEFAULTS = {}
+ OPERATING_SYSTEM_DEFAULTS = Gem.operating_system_defaults
##
# For Ruby implementers to set configuration defaults. Set in
@@ -63,26 +66,7 @@ class Gem::ConfigFile
require "etc"
Etc.sysconfdir
rescue LoadError, NoMethodError
- begin
- # TODO: remove after we drop 1.8.7 and 1.9.1
- require 'Win32API'
-
- CSIDL_COMMON_APPDATA = 0x0023
- path = 0.chr * 260
- if RUBY_VERSION > '1.9' then
- SHGetFolderPath = Win32API.new 'shell32', 'SHGetFolderPath', 'PLPLP',
- 'L', :stdcall
- SHGetFolderPath.call nil, CSIDL_COMMON_APPDATA, nil, 1, path
- else
- SHGetFolderPath = Win32API.new 'shell32', 'SHGetFolderPath', 'LLLLP',
- 'L'
- SHGetFolderPath.call 0, CSIDL_COMMON_APPDATA, 0, 1, path
- end
-
- path.strip
- rescue LoadError
- RbConfig::CONFIG["sysconfdir"] || "/etc"
- end
+ RbConfig::CONFIG["sysconfdir"] || "/etc"
end
# :startdoc:
@@ -124,6 +108,11 @@ class Gem::ConfigFile
attr_accessor :verbose
##
+ # Number of gem downloads that should be performed concurrently.
+
+ attr_accessor :concurrent_downloads
+
+ ##
# True if we want to update the SourceInfoCache every time, false otherwise
attr_accessor :update_sources
@@ -148,6 +137,11 @@ class Gem::ConfigFile
attr_accessor :sources
##
+ # Expiration length to sign a certificate
+
+ attr_accessor :cert_expiration_length_days
+
+ ##
# Path name of directory or file of openssl client certificate, used for remote https connection with client authentication
attr_reader :ssl_client_cert
@@ -180,12 +174,12 @@ class Gem::ConfigFile
arg_list = []
args.each do |arg|
- if need_config_file_name then
+ if need_config_file_name
@config_file_name = arg
need_config_file_name = false
- elsif arg =~ /^--config-file=(.*)/ then
+ elsif arg =~ /^--config-file=(.*)/
@config_file_name = $1
- elsif arg =~ /^--config-file$/ then
+ elsif arg =~ /^--config-file$/
need_config_file_name = true
else
arg_list << arg
@@ -196,6 +190,8 @@ class Gem::ConfigFile
@bulk_threshold = DEFAULT_BULK_THRESHOLD
@verbose = DEFAULT_VERBOSITY
@update_sources = DEFAULT_UPDATE_SOURCES
+ @concurrent_downloads = DEFAULT_CONCURRENT_DOWNLOADS
+ @cert_expiration_length_days = DEFAULT_CERT_EXPIRATION_LENGTH_DAYS
operating_system_config = Marshal.load Marshal.dump(OPERATING_SYSTEM_DEFAULTS)
platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS)
@@ -213,14 +209,15 @@ class Gem::ConfigFile
end
# HACK these override command-line args, which is bad
- @backtrace = @hash[:backtrace] if @hash.key? :backtrace
- @bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold
- @home = @hash[:gemhome] if @hash.key? :gemhome
- @path = @hash[:gempath] if @hash.key? :gempath
- @update_sources = @hash[:update_sources] if @hash.key? :update_sources
- @verbose = @hash[:verbose] if @hash.key? :verbose
- @disable_default_gem_server = @hash[:disable_default_gem_server] if @hash.key? :disable_default_gem_server
- @sources = @hash[:sources] if @hash.key? :sources
+ @backtrace = @hash[:backtrace] if @hash.key? :backtrace
+ @bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold
+ @home = @hash[:gemhome] if @hash.key? :gemhome
+ @path = @hash[:gempath] if @hash.key? :gempath
+ @update_sources = @hash[:update_sources] if @hash.key? :update_sources
+ @verbose = @hash[:verbose] if @hash.key? :verbose
+ @disable_default_gem_server = @hash[:disable_default_gem_server] if @hash.key? :disable_default_gem_server
+ @sources = @hash[:sources] if @hash.key? :sources
+ @cert_expiration_length_days = @hash[:cert_expiration_length_days] if @hash.key? :cert_expiration_length_days
@ssl_verify_mode = @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
@ssl_ca_cert = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
@@ -284,13 +281,13 @@ if you believe they were disclosed to a third party.
def load_api_keys
check_credentials_permissions
- @api_keys = if File.exist? credentials_path then
+ @api_keys = if File.exist? credentials_path
load_file(credentials_path)
else
@hash
end
- if @api_keys.key? :rubygems_api_key then
+ if @api_keys.key? :rubygems_api_key
@rubygems_api_key = @api_keys[:rubygems_api_key]
@api_keys[:rubygems] = @api_keys.delete :rubygems_api_key unless
@api_keys.key? :rubygems
@@ -309,7 +306,7 @@ if you believe they were disclosed to a third party.
##
# Sets the RubyGems.org API key to +api_key+
- def rubygems_api_key= api_key
+ def rubygems_api_key=(api_key)
set_api_key :rubygems_api_key, api_key
@rubygems_api_key = api_key
@@ -318,7 +315,7 @@ if you believe they were disclosed to a third party.
##
# Set a specific host's API key to +api_key+
- def set_api_key host, api_key
+ def set_api_key(host, api_key)
check_credentials_permissions
config = load_file(credentials_path).merge(host => api_key)
@@ -336,6 +333,15 @@ if you believe they were disclosed to a third party.
load_api_keys # reload
end
+ ##
+ # Remove the +~/.gem/credentials+ file to clear all the current sessions.
+
+ def unset_api_key!
+ return false unless File.exist?(credentials_path)
+
+ File.delete(credentials_path)
+ end
+
def load_file(filename)
Gem.load_yaml
@@ -345,7 +351,7 @@ if you believe they were disclosed to a third party.
return {} unless filename and File.exist? filename
begin
- content = YAML.load(File.read(filename))
+ content = Gem::SafeYAML.load(File.read(filename))
unless content.kind_of? Hash
warn "Failed to load #{filename} because it doesn't contain valid YAML hash"
return {}
@@ -419,31 +425,14 @@ if you believe they were disclosed to a third party.
# to_yaml only overwrites things you can't override on the command line.
def to_yaml # :nodoc:
yaml_hash = {}
- yaml_hash[:backtrace] = if @hash.key?(:backtrace)
- @hash[:backtrace]
- else
- DEFAULT_BACKTRACE
- end
-
- yaml_hash[:bulk_threshold] = if @hash.key?(:bulk_threshold)
- @hash[:bulk_threshold]
- else
- DEFAULT_BULK_THRESHOLD
- end
-
+ yaml_hash[:backtrace] = @hash.fetch(:backtrace, DEFAULT_BACKTRACE)
+ yaml_hash[:bulk_threshold] = @hash.fetch(:bulk_threshold, DEFAULT_BULK_THRESHOLD)
yaml_hash[:sources] = Gem.sources.to_a
+ yaml_hash[:update_sources] = @hash.fetch(:update_sources, DEFAULT_UPDATE_SOURCES)
+ yaml_hash[:verbose] = @hash.fetch(:verbose, DEFAULT_VERBOSITY)
- yaml_hash[:update_sources] = if @hash.key?(:update_sources)
- @hash[:update_sources]
- else
- DEFAULT_UPDATE_SOURCES
- end
-
- yaml_hash[:verbose] = if @hash.key?(:verbose)
- @hash[:verbose]
- else
- DEFAULT_VERBOSITY
- end
+ yaml_hash[:concurrent_downloads] =
+ @hash.fetch(:concurrent_downloads, DEFAULT_CONCURRENT_DOWNLOADS)
yaml_hash[:ssl_verify_mode] =
@hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
@@ -469,7 +458,7 @@ if you believe they were disclosed to a third party.
# Writes out this config file, replacing its source.
def write
- open config_file_name, 'w' do |io|
+ File.open config_file_name, 'w' do |io|
io.write to_yaml
end
end
diff --git a/lib/rubygems/core_ext/kernel_require.rb b/lib/rubygems/core_ext/kernel_require.rb
index aa56ab5ec4..3b78011619 100755
--- a/lib/rubygems/core_ext/kernel_require.rb
+++ b/lib/rubygems/core_ext/kernel_require.rb
@@ -11,13 +11,8 @@ module Kernel
RUBYGEMS_ACTIVATION_MONITOR = Monitor.new # :nodoc:
- if defined?(gem_original_require) then
- # Ruby ships with a custom_require, override its require
- remove_method :require
- else
- ##
- # The Kernel#require from before RubyGems was loaded.
-
+ # Make sure we have a reference to Ruby's original Kernel#require
+ unless defined?(gem_original_require)
alias gem_original_require require
private :gem_original_require
end
@@ -36,21 +31,25 @@ module Kernel
# The normal <tt>require</tt> functionality of returning false if
# that file has already been loaded is preserved.
- def require path
+ def require(path)
RUBYGEMS_ACTIVATION_MONITOR.enter
path = path.to_path if path.respond_to? :to_path
- spec = Gem.find_unresolved_default_spec(path)
- if spec
+ if spec = Gem.find_unresolved_default_spec(path)
Gem.remove_unresolved_default_spec(spec)
- gem(spec.name)
+ begin
+ Kernel.send(:gem, spec.name)
+ rescue Exception
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ raise
+ end
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? then
+ if Gem::Specification.unresolved_deps.empty?
RUBYGEMS_ACTIVATION_MONITOR.exit
return gem_original_require(path)
end
@@ -61,12 +60,10 @@ module Kernel
#--
# TODO request access to the C implementation of this to speed up RubyGems
- spec = Gem::Specification.find_active_stub_by_path path
-
- begin
+ if Gem::Specification.find_active_stub_by_path(path)
RUBYGEMS_ACTIVATION_MONITOR.exit
return gem_original_require(path)
- end if spec
+ end
# Attempt to find +path+ in any unresolved gems...
@@ -82,7 +79,7 @@ module Kernel
# requested, then find_in_unresolved_tree will find d.rb in d because
# it's a dependency of c.
#
- if found_specs.empty? then
+ if found_specs.empty?
found_specs = Gem::Specification.find_in_unresolved_tree path
found_specs.each do |found_spec|
@@ -97,16 +94,16 @@ module Kernel
# versions of the same gem
names = found_specs.map(&:name).uniq
- if names.size > 1 then
+ 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.reject { |s| s.has_conflicts? }.first
+ valid = found_specs.find { |s| !s.has_conflicts? }
- unless valid then
+ unless valid
le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate"
le.name = names.first
RUBYGEMS_ACTIVATION_MONITOR.exit
@@ -123,7 +120,7 @@ module Kernel
begin
if load_error.message.start_with?("Could not find") or
- (load_error.message.end_with?(path) and Gem.try_activate(path)) then
+ (load_error.message.end_with?(path) and Gem.try_activate(path))
require_again = true
end
ensure
@@ -138,4 +135,3 @@ module Kernel
private :require
end
-
diff --git a/lib/rubygems/core_ext/kernel_warn.rb b/lib/rubygems/core_ext/kernel_warn.rb
new file mode 100755
index 0000000000..3e531441ed
--- /dev/null
+++ b/lib/rubygems/core_ext/kernel_warn.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+# `uplevel` keyword argument of Kernel#warn is available since ruby 2.5.
+if RUBY_VERSION >= "2.5"
+
+ module Kernel
+ path = "#{__dir__}/" # Frames to be skipped start with this path.
+
+ # Suppress "method redefined" warning
+ original_warn = instance_method(:warn)
+ Module.new {define_method(:warn, original_warn)}
+
+ original_warn = method(:warn)
+
+ module_function define_method(:warn) {|*messages, uplevel: nil|
+ unless uplevel
+ return original_warn.call(*messages)
+ 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
+
+ unless loc.path.start_with?(path)
+ # Non-rubygems frames
+ uplevel -= 1
+ end
+ end
+ uplevel = start
+ end
+ original_warn.call(*messages, uplevel: uplevel)
+ }
+ end
+end
diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb
index 43d57fc808..50f46b32a2 100644
--- a/lib/rubygems/defaults.rb
+++ b/lib/rubygems/defaults.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
module Gem
- DEFAULT_HOST = "https://rubygems.org"
+ DEFAULT_HOST = "https://rubygems.org".freeze
@post_install_hooks ||= []
@done_installing_hooks ||= []
@@ -28,17 +28,17 @@ module Gem
# specified in the environment
def self.default_dir
- path = if defined? RUBY_FRAMEWORK_VERSION then
+ path = if defined? RUBY_FRAMEWORK_VERSION
[
File.dirname(RbConfig::CONFIG['sitedir']),
'Gems',
RbConfig::CONFIG['ruby_version']
]
- elsif RbConfig::CONFIG['rubylibprefix'] then
+ elsif RbConfig::CONFIG['rubylibprefix']
[
- RbConfig::CONFIG['rubylibprefix'],
- 'gems',
- RbConfig::CONFIG['ruby_version']
+ RbConfig::CONFIG['rubylibprefix'],
+ 'gems',
+ RbConfig::CONFIG['ruby_version']
]
else
[
@@ -59,7 +59,7 @@ module Gem
# By default, the binary extensions are located side by side with their
# Ruby counterparts, therefore nil is returned
- def self.default_ext_dir_for base_dir
+ def self.default_ext_dir_for(base_dir)
nil
end
@@ -103,7 +103,7 @@ module Gem
def self.default_exec_format
exec_format = RbConfig::CONFIG['ruby_install_name'].sub('ruby', '%s') rescue '%s'
- unless exec_format =~ /%s/ then
+ unless exec_format =~ /%s/
raise Gem::Exception,
"[BUG] invalid exec_format #{exec_format.inspect}, no %s"
end
@@ -115,7 +115,7 @@ module Gem
# The default directory for binaries
def self.default_bindir
- if defined? RUBY_FRAMEWORK_VERSION then # mac framework support
+ if defined? RUBY_FRAMEWORK_VERSION # mac framework support
'/usr/bin'
else # generic install
RbConfig::CONFIG['bindir']
@@ -126,7 +126,7 @@ module Gem
# A wrapper around RUBY_ENGINE const that may not be defined
def self.ruby_engine
- if defined? RUBY_ENGINE then
+ if defined? RUBY_ENGINE
RUBY_ENGINE
else
'ruby'
@@ -165,7 +165,7 @@ module Gem
# Directory where vendor gems are installed.
def self.vendor_dir # :nodoc:
- if vendor_dir = ENV['GEM_VENDOR'] then
+ if vendor_dir = ENV['GEM_VENDOR']
return vendor_dir.dup
end
@@ -176,7 +176,26 @@ module Gem
end
##
- # Default options for gem commands.
+ # Default options for gem commands for Ruby packagers.
+ #
+ # The options here should be structured as an array of string "gem"
+ # command names as keys and a string of the default options as values.
+ #
+ # Example:
+ #
+ # def self.operating_system_defaults
+ # {
+ # 'install' => '--no-rdoc --no-ri --env-shebang',
+ # 'update' => '--no-rdoc --no-ri --env-shebang'
+ # }
+ # end
+
+ def self.operating_system_defaults
+ {}
+ end
+
+ ##
+ # Default options for gem commands for Ruby implementers.
#
# The options here should be structured as an array of string "gem"
# command names as keys and a string of the default options as values.
diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb
index bbdab7ccfa..33ba1968d1 100644
--- a/lib/rubygems/dependency.rb
+++ b/lib/rubygems/dependency.rb
@@ -2,6 +2,7 @@
##
# The Dependency class holds a Gem name and a Gem::Requirement.
+require "rubygems/bundler_version_finder"
require "rubygems/requirement"
class Gem::Dependency
@@ -18,7 +19,7 @@ class Gem::Dependency
TYPES = [
:development,
:runtime,
- ]
+ ].freeze
##
# Dependency name or regular expression.
@@ -35,7 +36,7 @@ class Gem::Dependency
# argument can optionally be the dependency type, which defaults to
# <tt>:runtime</tt>.
- def initialize name, *requirements
+ def initialize(name, *requirements)
case name
when String then # ok
when Regexp then
@@ -75,7 +76,7 @@ class Gem::Dependency
end
def inspect # :nodoc:
- if prerelease? then
+ if prerelease?
"<%s type=%p name=%p requirements=%p prerelease=ok>" %
[self.class, self.type, self.name, requirement.to_s]
else
@@ -99,7 +100,7 @@ class Gem::Dependency
@requirement.none?
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 1, 'Gem::Dependency.new(', ')' do
q.pp name
q.text ','
@@ -151,7 +152,7 @@ class Gem::Dependency
end
def to_s # :nodoc:
- if type != :runtime then
+ if type != :runtime
"#{name} (#{requirement}, #{type})"
else
"#{name} (#{requirement})"
@@ -169,7 +170,7 @@ class Gem::Dependency
@type == :runtime || !@type
end
- def == other # :nodoc:
+ def ==(other) # :nodoc:
Gem::Dependency === other &&
self.name == other.name &&
self.type == other.type &&
@@ -179,7 +180,7 @@ class Gem::Dependency
##
# Dependencies are ordered by name.
- def <=> other
+ def <=>(other)
self.name <=> other.name
end
@@ -189,7 +190,7 @@ class Gem::Dependency
# other has only an equal version requirement that satisfies this
# dependency.
- def =~ other
+ def =~(other)
unless Gem::Dependency === other
return unless other.respond_to?(:name) && other.respond_to?(:version)
other = Gem::Dependency.new other.name, other.version
@@ -221,7 +222,7 @@ class Gem::Dependency
# NOTE: Unlike #matches_spec? this method does not return true when the
# version is a prerelease version unless this is a prerelease dependency.
- def match? obj, version=nil, allow_prerelease=false
+ def match?(obj, version=nil, allow_prerelease=false)
if !version
name = obj.name
version = obj.version
@@ -248,7 +249,7 @@ class Gem::Dependency
# returns true when +spec+ is a prerelease version even if this dependency
# is not a prerelease dependency.
- def matches_spec? spec
+ def matches_spec?(spec)
return false unless name === spec.name
return true if requirement.none?
@@ -258,8 +259,8 @@ class Gem::Dependency
##
# Merges the requirements of +other+ into this dependency
- def merge other
- unless name == other.name then
+ def merge(other)
+ unless name == other.name
raise ArgumentError,
"#{self} and #{other} have different names"
end
@@ -274,12 +275,14 @@ class Gem::Dependency
self.class.new name, self_req.as_list.concat(other_req.as_list)
end
- def matching_specs platform_only = false
+ def matching_specs(platform_only = false)
env_req = Gem.env_requirement(name)
matches = Gem::Specification.stubs_for(name).find_all { |spec|
requirement.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version)
}.map(&:to_spec)
+ Gem::BundlerVersionFinder.filter!(matches) if name == "bundler".freeze
+
if platform_only
matches.reject! { |spec|
spec.nil? || !Gem::Platform.match(spec.platform)
@@ -301,7 +304,7 @@ class Gem::Dependency
# TODO: check Gem.activated_spec[self.name] in case matches falls outside
- if matches.empty? then
+ if matches.empty?
specs = Gem::Specification.stubs_for name
if specs.empty?
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index 28848f7373..346208603c 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -7,6 +7,7 @@ require 'rubygems/spec_fetcher'
require 'rubygems/user_interaction'
require 'rubygems/source'
require 'rubygems/available_set'
+require 'rubygems/deprecate'
##
# Installs a gem along with all its dependencies from local and remote gems.
@@ -14,6 +15,7 @@ require 'rubygems/available_set'
class Gem::DependencyInstaller
include Gem::UserInteraction
+ extend Gem::Deprecate
DEFAULT_OPTIONS = { # :nodoc:
:env_shebang => false,
@@ -41,12 +43,6 @@ class Gem::DependencyInstaller
attr_reader :errors
##
- #--
- # TODO remove, no longer used
-
- attr_reader :gems_to_install # :nodoc:
-
- ##
# List of gems installed by #install in alphabetic order
attr_reader :installed_gems
@@ -70,7 +66,7 @@ class Gem::DependencyInstaller
# :wrappers:: See Gem::Installer::new
# :build_args:: See Gem::Installer::new
- def initialize options = {}
+ def initialize(options = {})
@only_install_dir = !!options[:install_dir]
@install_dir = options[:install_dir] || Gem.dir
@build_root = options[:build_root]
@@ -93,6 +89,9 @@ class Gem::DependencyInstaller
@build_args = options[:build_args]
@build_docs_in_background = options[:build_docs_in_background]
@install_as_default = options[:install_as_default]
+ @dir_mode = options[:dir_mode]
+ @data_mode = options[:data_mode]
+ @prog_mode = options[:prog_mode]
# Indicates that we should not try to update any deps unless
# we absolutely must.
@@ -109,9 +108,9 @@ class Gem::DependencyInstaller
##
#--
- # TODO remove, no longer used
+ # TODO remove at RubyGems 4, no longer used
- def add_found_dependencies to_do, dependency_list # :nodoc:
+ def add_found_dependencies(to_do, dependency_list) # :nodoc:
seen = {}
dependencies = Hash.new { |h, name| h[name] = Gem::Dependency.new name }
@@ -159,13 +158,14 @@ class Gem::DependencyInstaller
dependency_list.remove_specs_unsatisfied_by dependencies
end
+ deprecate :add_found_dependencies, :none, 2018, 12
##
# Creates an AvailableSet to install from based on +dep_or_name+ and
# +version+
- def available_set_for dep_or_name, version # :nodoc:
- if String === dep_or_name then
+ def available_set_for(dep_or_name, version) # :nodoc:
+ if String === dep_or_name
find_spec_by_name_and_version dep_or_name, version, @prerelease
else
dep = dep_or_name.dup
@@ -198,7 +198,7 @@ class Gem::DependencyInstaller
# sources. Gems are sorted with newer gems preferred over older gems, and
# local gems preferred over remote gems.
- def find_gems_with_sources dep, best_only=false # :nodoc:
+ def find_gems_with_sources(dep, best_only=false) # :nodoc:
set = Gem::AvailableSet.new
if consider_local?
@@ -272,16 +272,16 @@ class Gem::DependencyInstaller
# +version+. Returns an Array of specs and sources required for
# installation of the gem.
- def find_spec_by_name_and_version gem_name,
+ def find_spec_by_name_and_version(gem_name,
version = Gem::Requirement.default,
- prerelease = false
+ prerelease = false)
set = Gem::AvailableSet.new
if consider_local?
- if gem_name =~ /\.gem$/ and File.file? gem_name then
+ if gem_name =~ /\.gem$/ and File.file? gem_name
src = Gem::Source::SpecificFile.new(gem_name)
set.add src.spec, src
- elsif gem_name =~ /\.gem$/ then
+ elsif gem_name =~ /\.gem$/
Dir[gem_name].each do |name|
begin
src = Gem::Source::SpecificFile.new name
@@ -317,7 +317,7 @@ class Gem::DependencyInstaller
# Gathers all dependencies necessary for the installation from local and
# remote sources unless the ignore_dependencies was given.
#--
- # TODO remove at RubyGems 3
+ # TODO remove at RubyGems 4
def gather_dependencies # :nodoc:
specs = @available.all_specs
@@ -341,7 +341,7 @@ class Gem::DependencyInstaller
Gem::Specification.include?(spec)
}
- unless dependency_list.ok? or @ignore_dependencies or @force then
+ unless dependency_list.ok? or @ignore_dependencies or @force
reason = dependency_list.why_not_ok?.map { |k,v|
"#{k} requires #{v.join(", ")}"
}.join("; ")
@@ -350,8 +350,9 @@ class Gem::DependencyInstaller
@gems_to_install = dependency_list.dependency_order.reverse
end
+ deprecate :gather_dependencies, :none, 2018, 12
- def in_background what # :nodoc:
+ def in_background(what) # :nodoc:
fork_happened = false
if @build_docs_in_background and Process.respond_to?(:fork)
begin
@@ -380,7 +381,7 @@ class Gem::DependencyInstaller
# c-1.a, b-1 and a-1.a will be installed. b-1.a will need to be installed
# separately.
- def install dep_or_name, version = Gem::Requirement.default
+ def install(dep_or_name, version = Gem::Requirement.default)
request_set = resolve_dependencies dep_or_name, version
@installed_gems = []
@@ -398,7 +399,10 @@ class Gem::DependencyInstaller
:user_install => @user_install,
:wrappers => @wrappers,
:build_root => @build_root,
- :install_as_default => @install_as_default
+ :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
@@ -421,16 +425,16 @@ class Gem::DependencyInstaller
end
def install_development_deps # :nodoc:
- if @development and @dev_shallow then
+ if @development and @dev_shallow
:shallow
- elsif @development then
+ elsif @development
:all
else
:none
end
end
- def resolve_dependencies dep_or_name, version # :nodoc:
+ def resolve_dependencies(dep_or_name, version) # :nodoc:
request_set = Gem::RequestSet.new
request_set.development = @development
request_set.development_shallow = @dev_shallow
@@ -442,11 +446,11 @@ class Gem::DependencyInstaller
installer_set.ignore_installed = @only_install_dir
if consider_local?
- if dep_or_name =~ /\.gem$/ and File.file? dep_or_name then
+ if dep_or_name =~ /\.gem$/ and 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
- elsif dep_or_name =~ /\.gem$/ then
+ elsif dep_or_name =~ /\.gem$/
Dir[dep_or_name].each do |name|
begin
src = Gem::Source::SpecificFile.new name
@@ -454,14 +458,14 @@ class Gem::DependencyInstaller
rescue Gem::Package::FormatError
end
end
- # else This is a dependency. InstallerSet handles this case
+ # else This is a dependency. InstallerSet handles this case
end
end
dependency =
- if spec = installer_set.local?(dep_or_name) then
+ if spec = installer_set.local?(dep_or_name)
Gem::Dependency.new spec.name, version
- elsif String === dep_or_name then
+ elsif String === dep_or_name
Gem::Dependency.new dep_or_name, version
else
dep_or_name
@@ -475,7 +479,7 @@ class Gem::DependencyInstaller
request_set.always_install = installer_set.always_install
- if @ignore_dependencies then
+ if @ignore_dependencies
installer_set.ignore_dependencies = true
request_set.ignore_dependencies = true
request_set.soft_missing = true
diff --git a/lib/rubygems/dependency_list.rb b/lib/rubygems/dependency_list.rb
index 35fe7c4c1a..ee2e4c7343 100644
--- a/lib/rubygems/dependency_list.rb
+++ b/lib/rubygems/dependency_list.rb
@@ -40,7 +40,7 @@ class Gem::DependencyList
# Creates a new DependencyList. If +development+ is true, development
# dependencies will be included.
- def initialize development = false
+ def initialize(development = false)
@specs = []
@development = development
@@ -79,8 +79,8 @@ class Gem::DependencyList
seen = {}
sorted.each do |spec|
- if index = seen[spec.name] then
- if result[index].version < spec.version then
+ if index = seen[spec.name]
+ if result[index].version < spec.version
result[index] = spec
end
else
@@ -104,7 +104,7 @@ class Gem::DependencyList
end
def inspect # :nodoc:
- "#<%s:0x%x %p>" % [self.class, object_id, map { |s| s.full_name }]
+ "%s %p>" % [super[0..-2], map { |s| s.full_name }]
end
##
@@ -114,7 +114,7 @@ class Gem::DependencyList
why_not_ok?(:quick).empty?
end
- def why_not_ok? quick = false
+ def why_not_ok?(quick = false)
unsatisfied = Hash.new { |h,k| h[k] = [] }
each do |spec|
spec.runtime_dependencies.each do |dep|
@@ -123,7 +123,7 @@ class Gem::DependencyList
dep.requirement.satisfied_by? installed_spec.version
}
- unless inst or @specs.find { |s| s.satisfies_requirement? dep } then
+ unless inst or @specs.find { |s| s.satisfies_requirement? dep }
unsatisfied[spec.name] << dep
return unsatisfied if quick
end
@@ -172,7 +172,7 @@ class Gem::DependencyList
# satisfy items in +dependencies+ (a hash of gem names to arrays of
# dependencies).
- def remove_specs_unsatisfied_by dependencies
+ def remove_specs_unsatisfied_by(dependencies)
specs.reject! { |spec|
dep = dependencies[spec.name]
dep and not dep.requirement.satisfied_by? spec.version
@@ -200,7 +200,7 @@ class Gem::DependencyList
next if spec == other
other.dependencies.each do |dep|
- if spec.satisfies_requirement? dep then
+ if spec.satisfies_requirement? dep
result[spec] << other
end
end
@@ -222,7 +222,7 @@ class Gem::DependencyList
dependencies.each do |dep|
specs.each do |spec|
- if spec.satisfies_requirement? dep then
+ if spec.satisfies_requirement? dep
yield spec
break
end
@@ -241,4 +241,3 @@ class Gem::DependencyList
end
end
-
diff --git a/lib/rubygems/deprecate.rb b/lib/rubygems/deprecate.rb
index 375194c1e8..815f42ae8c 100644
--- a/lib/rubygems/deprecate.rb
+++ b/lib/rubygems/deprecate.rb
@@ -27,7 +27,7 @@ module Gem::Deprecate
@skip ||= false
end
- def self.skip= v # :nodoc:
+ def self.skip=(v) # :nodoc:
@skip = v
end
@@ -47,7 +47,7 @@ module Gem::Deprecate
# telling the user of +repl+ (unless +repl+ is :none) and the
# year/month that it is planned to go away.
- def deprecate name, repl, year, month
+ def deprecate(name, repl, year, month)
class_eval {
old = "_deprecated_#{name}"
alias_method old, name
@@ -68,4 +68,3 @@ module Gem::Deprecate
module_function :deprecate, :skip_during
end
-
diff --git a/lib/rubygems/doctor.rb b/lib/rubygems/doctor.rb
index ec4a16c3f8..661ae5a4e1 100644
--- a/lib/rubygems/doctor.rb
+++ b/lib/rubygems/doctor.rb
@@ -26,7 +26,7 @@ class Gem::Doctor
['doc', ''],
['extensions', ''],
['gems', ''],
- ]
+ ].freeze
missing =
Gem::REPOSITORY_SUBDIRECTORIES.sort -
@@ -41,7 +41,7 @@ class Gem::Doctor
#
# If +dry_run+ is true no files or directories will be removed.
- def initialize gem_repository, dry_run = false
+ def initialize(gem_repository, dry_run = false)
@gem_repository = gem_repository
@dry_run = dry_run
@@ -73,7 +73,7 @@ class Gem::Doctor
Gem.use_paths @gem_repository.to_s
- unless gem_repository? then
+ unless gem_repository?
say 'This directory does not appear to be a RubyGems repository, ' +
'skipping'
say
@@ -99,7 +99,7 @@ class Gem::Doctor
##
# Removes files in +sub_directory+ with +extension+
- def doctor_child sub_directory, extension # :nodoc:
+ def doctor_child(sub_directory, extension) # :nodoc:
directory = File.join(@gem_repository, sub_directory)
Dir.entries(directory).sort.each do |ent|
@@ -115,7 +115,7 @@ class Gem::Doctor
type = File.directory?(child) ? 'directory' : 'file'
- action = if @dry_run then
+ action = if @dry_run
'Extra'
else
FileUtils.rm_r(child)
@@ -129,4 +129,3 @@ class Gem::Doctor
end
end
-
diff --git a/lib/rubygems/errors.rb b/lib/rubygems/errors.rb
index 5cd5b14c58..6773bbcd26 100644
--- a/lib/rubygems/errors.rb
+++ b/lib/rubygems/errors.rb
@@ -25,7 +25,7 @@ module Gem
# system. Instead of rescuing from this class, make sure to rescue from the
# superclass Gem::LoadError to catch all types of load errors.
class MissingSpecError < Gem::LoadError
- def initialize name, requirement
+ def initialize(name, requirement)
@name = name
@requirement = requirement
end
@@ -50,7 +50,7 @@ module Gem
class MissingSpecVersionError < MissingSpecError
attr_reader :specs
- def initialize name, requirement, specs
+ def initialize(name, requirement, specs)
super(name, requirement)
@specs = specs
end
@@ -58,6 +58,9 @@ module Gem
private
def build_message
+ if name == "bundler" && message = Gem::BundlerVersionFinder.missing_version_message
+ return message
+ end
names = specs.map(&:full_name)
"Could not find '#{name}' (#{requirement}) - did find: [#{names.join ','}]\n"
end
@@ -78,7 +81,7 @@ module Gem
attr_reader :target
- def initialize target, conflicts
+ def initialize(target, conflicts)
@target = target
@conflicts = conflicts
@name = target.name
diff --git a/lib/rubygems/exceptions.rb b/lib/rubygems/exceptions.rb
index 9089eae4d5..a387cd390f 100644
--- a/lib/rubygems/exceptions.rb
+++ b/lib/rubygems/exceptions.rb
@@ -4,6 +4,8 @@
# Each exception needs a brief description and the scenarios where it is
# likely to be raised
+require 'rubygems/deprecate'
+
##
# Base exception class for RubyGems. All exception raised by RubyGems are a
# subclass of this one.
@@ -11,10 +13,12 @@ class Gem::Exception < RuntimeError
##
#--
- # TODO: remove in RubyGems 3, nobody sets this
+ # TODO: remove in RubyGems 4, nobody sets this
attr_accessor :source_exception # :nodoc:
+ extend Gem::Deprecate
+ deprecate :source_exception, :none, 2018, 12
end
class Gem::CommandLineError < Gem::Exception; end
@@ -32,7 +36,7 @@ class Gem::DependencyResolutionError < Gem::DependencyError
attr_reader :conflict
- def initialize conflict
+ def initialize(conflict)
@conflict = conflict
a, b = conflicting_dependencies
@@ -52,6 +56,13 @@ class Gem::GemNotInHomeException < Gem::Exception
attr_accessor :spec
end
+###
+# Raised when removing a gem with the uninstall command fails
+
+class Gem::UninstallError < Gem::Exception
+ attr_accessor :spec
+end
+
class Gem::DocumentError < Gem::Exception; end
##
@@ -66,7 +77,7 @@ class Gem::FilePermissionError < Gem::Exception
attr_reader :directory
- def initialize directory
+ def initialize(directory)
@directory = directory
super "You don't have write permissions for the #{directory} directory."
@@ -126,7 +137,7 @@ class Gem::ImpossibleDependenciesError < Gem::Exception
attr_reader :conflicts
attr_reader :request
- def initialize request, conflicts
+ def initialize(request, conflicts)
@request = request
@conflicts = conflicts
@@ -154,6 +165,12 @@ class Gem::ImpossibleDependenciesError < Gem::Exception
end
class Gem::InstallError < Gem::Exception; end
+class Gem::RuntimeRequirementNotMetError < Gem::InstallError
+ attr_accessor :suggestion
+ def message
+ [suggestion, super].compact.join("\n\t")
+ end
+end
##
# Potentially raised when a specification is validated.
@@ -232,7 +249,7 @@ class Gem::UnsatisfiableDependencyError < Gem::DependencyError
# Creates a new UnsatisfiableDependencyError for the unsatisfiable
# Gem::Resolver::DependencyRequest +dep+
- def initialize dep, platform_mismatch=nil
+ def initialize(dep, platform_mismatch=nil)
if platform_mismatch and !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(', ')}"
diff --git a/lib/rubygems/ext.rb b/lib/rubygems/ext.rb
index 18d2bc233a..35a486606a 100644
--- a/lib/rubygems/ext.rb
+++ b/lib/rubygems/ext.rb
@@ -16,4 +16,3 @@ require 'rubygems/ext/configure_builder'
require 'rubygems/ext/ext_conf_builder'
require 'rubygems/ext/rake_builder'
require 'rubygems/ext/cmake_builder'
-
diff --git a/lib/rubygems/ext/build_error.rb b/lib/rubygems/ext/build_error.rb
index 0b3c17a9a0..6dffddb5cc 100644
--- a/lib/rubygems/ext/build_error.rb
+++ b/lib/rubygems/ext/build_error.rb
@@ -4,4 +4,3 @@
class Gem::Ext::BuildError < Gem::InstallError
end
-
diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb
index 699903ab0e..f578e7feee 100644
--- a/lib/rubygems/ext/builder.rb
+++ b/lib/rubygems/ext/builder.rb
@@ -6,7 +6,6 @@
#++
require 'rubygems/user_interaction'
-require 'thread'
class Gem::Ext::Builder
@@ -28,18 +27,18 @@ class Gem::Ext::Builder
end
def self.make(dest_path, results)
- unless File.exist? 'Makefile' then
+ unless File.exist? '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 = ENV['MAKE'] || ENV['make'] || $1
- unless make_program then
+ unless make_program
make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make'
end
- destdir = '"DESTDIR=%s"' % ENV['DESTDIR'] if RUBY_VERSION > '2.0'
+ destdir = '"DESTDIR=%s"' % ENV['DESTDIR']
['clean', '', 'install'].each do |target|
# Pass DESTDIR via command line to override what's in MAKEFLAGS
@@ -57,6 +56,7 @@ class Gem::Ext::Builder
end
def self.redirector
+ warn "#{caller[0]}: Use IO.popen(..., err: [:child, :out])"
'2>&1'
end
@@ -64,28 +64,35 @@ class Gem::Ext::Builder
verbose = Gem.configuration.really_verbose
begin
- # TODO use Process.spawn when ruby 1.8 support is dropped.
rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], nil
if verbose
puts("current directory: #{Dir.pwd}")
- puts(command)
- system(command)
- else
- results << "current directory: #{Dir.pwd}"
- results << command
- results << `#{command} #{redirector}`
+ p(command)
end
+ results << "current directory: #{Dir.pwd}"
+ results << (command.respond_to?(:shelljoin) ? command.shelljoin : command)
+
+ redirections = verbose ? {} : {err: [:child, :out]}
+ IO.popen(command, "r", redirections) do |io|
+ if verbose
+ IO.copy_stream(io, $stdout)
+ else
+ results << io.read
+ end
+ end
+ rescue => error
+ raise Gem::InstallError, "#{command_name || class_name} failed#{error.message}"
ensure
ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
end
- unless $?.success? then
+ unless $?.success?
results << "Building has failed. See above output for more information on the failure." if verbose
exit_reason =
- if $?.exited? then
+ if $?.exited?
", exit code #{$?.exitstatus}"
- elsif $?.signaled? then
+ elsif $?.signaled?
", uncaught signal #{$?.termsig}"
end
@@ -98,7 +105,7 @@ class Gem::Ext::Builder
# have build arguments, saved, set +build_args+ which is an ARGV-style
# array.
- def initialize spec, build_args = spec.build_args
+ def initialize(spec, build_args = spec.build_args)
@spec = spec
@build_args = build_args
@gem_dir = spec.full_gem_path
@@ -109,7 +116,7 @@ class Gem::Ext::Builder
##
# Chooses the extension builder class for +extension+
- def builder_for extension # :nodoc:
+ def builder_for(extension) # :nodoc:
case extension
when /extconf/ then
Gem::Ext::ExtConfBuilder
@@ -131,7 +138,7 @@ class Gem::Ext::Builder
##
# Logs the build +output+ in +build_dir+, then raises Gem::Ext::BuildError.
- def build_error build_dir, output, backtrace = nil # :nodoc:
+ def build_error(build_dir, output, backtrace = nil) # :nodoc:
gem_make_out = write_gem_make_out output
message = <<-EOF
@@ -146,12 +153,24 @@ EOF
raise Gem::Ext::BuildError, message, backtrace
end
- def build_extension extension, dest_path # :nodoc:
+ def build_extension(extension, dest_path) # :nodoc:
results = []
+ # FIXME: Determine if this line is necessary and, if so, why.
+ # Notes:
+ # 1. As far as I can tell, this method is only called by +build_extensions+.
+ # 2. The existence of this line implies +extension+ is, or previously was,
+ # sometimes +false+ or +nil+.
+ # 3. #1 and #2 combined suggests, but does not confirm, that
+ # +@specs.extensions+ sometimes contained +false+ or +nil+ values.
+ # 4. Nothing seems to explicitly handle +extension+ being empty,
+ # which makes me wonder both what it should do and what it does.
+ #
+ # - @duckinator
extension ||= '' # I wish I knew why this line existed
+
extension_dir =
- File.expand_path File.join @gem_dir, File.dirname(extension)
+ File.expand_path File.join(@gem_dir, File.dirname(extension))
lib_dir = File.join @spec.full_gem_path, @spec.raw_require_paths.first
builder = builder_for extension
@@ -160,11 +179,19 @@ EOF
FileUtils.mkdir_p dest_path
CHDIR_MUTEX.synchronize do
- Dir.chdir extension_dir do
- results = builder.build(extension, @gem_dir, dest_path,
+ pwd = Dir.getwd
+ Dir.chdir extension_dir
+ begin
+ results = builder.build(extension, dest_path,
results, @build_args, lib_dir)
verbose { results.join("\n") }
+ ensure
+ begin
+ Dir.chdir pwd
+ rescue SystemCallError
+ Dir.chdir dest_path
+ end
end
end
@@ -183,7 +210,7 @@ EOF
return if @spec.extensions.empty?
if @build_args.empty?
- say "Building native extensions. This could take a while..."
+ say "Building native extensions. This could take a while..."
else
say "Building native extensions with: '#{@build_args.join ' '}'"
say "This could take a while..."
@@ -193,6 +220,7 @@ EOF
FileUtils.rm_f @spec.gem_build_complete_path
+ # FIXME: action at a distance: @ran_rake modified deep in build_extension(). - @duckinator
@ran_rake = false # only run rake once
@spec.extensions.each do |extension|
@@ -207,15 +235,14 @@ EOF
##
# Writes +output+ to gem_make.out in the extension install directory.
- def write_gem_make_out output # :nodoc:
+ def write_gem_make_out(output) # :nodoc:
destination = File.join @spec.extension_dir, 'gem_make.out'
FileUtils.mkdir_p @spec.extension_dir
- open destination, 'wb' do |io| io.puts output end
+ File.open destination, 'wb' do |io| io.puts output end
destination
end
end
-
diff --git a/lib/rubygems/ext/cmake_builder.rb b/lib/rubygems/ext/cmake_builder.rb
index efa3bd1d88..ab226733d9 100644
--- a/lib/rubygems/ext/cmake_builder.rb
+++ b/lib/rubygems/ext/cmake_builder.rb
@@ -2,8 +2,8 @@
require 'rubygems/command'
class Gem::Ext::CmakeBuilder < Gem::Ext::Builder
- def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
- unless File.exist?('Makefile') then
+ def self.build(extension, dest_path, results, args=[], lib_dir=nil)
+ unless File.exist?('Makefile')
cmd = "cmake . -DCMAKE_INSTALL_PREFIX=#{dest_path}"
cmd << " #{Gem::Command.build_args.join ' '}" unless Gem::Command.build_args.empty?
diff --git a/lib/rubygems/ext/configure_builder.rb b/lib/rubygems/ext/configure_builder.rb
index 8b42bf7ee9..7d105c9bd3 100644
--- a/lib/rubygems/ext/configure_builder.rb
+++ b/lib/rubygems/ext/configure_builder.rb
@@ -7,8 +7,8 @@
class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder
- def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
- unless File.exist?('Makefile') then
+ def self.build(extension, dest_path, results, args=[], lib_dir=nil)
+ unless File.exist?('Makefile')
cmd = "sh ./configure --prefix=#{dest_path}"
cmd << " #{args.join ' '}" unless args.empty?
@@ -21,4 +21,3 @@ class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder
end
end
-
diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb
index 59e243b972..5a2b3eb533 100644
--- a/lib/rubygems/ext/ext_conf_builder.rb
+++ b/lib/rubygems/ext/ext_conf_builder.rb
@@ -7,11 +7,12 @@
require 'fileutils'
require 'tempfile'
+require 'shellwords'
class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
FileEntry = FileUtils::Entry_ # :nodoc:
- def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
+ def self.build(extension, dest_path, results, args=[], lib_dir=nil)
tmp_dest = Dir.mktmpdir(".gem.", ".")
# Some versions of `mktmpdir` return absolute paths, which will break make
@@ -23,13 +24,9 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
# spaces do not work.
#
# Details: https://github.com/rubygems/rubygems/issues/977#issuecomment-171544940
- #
- # TODO: Make this unconditional when rubygems no longer supports Ruby 1.9.x.
- tmp_dest = get_relative_path(tmp_dest) unless Gem.win_platform? && RUBY_VERSION <= '2.0'
+ tmp_dest = get_relative_path(tmp_dest)
- t = nil
Tempfile.open %w"siteconf .rb", "." do |siteconf|
- t = siteconf
siteconf.puts "require 'rbconfig'"
siteconf.puts "dest_path = #{tmp_dest.dump}"
%w[sitearchdir sitelibdir].each do |dir|
@@ -37,20 +34,24 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
siteconf.puts "RbConfig::CONFIG['#{dir}'] = dest_path"
end
- siteconf.flush
+ siteconf.close
destdir = ENV["DESTDIR"]
begin
- cmd = [Gem.ruby, "-r", get_relative_path(siteconf.path), File.basename(extension), *args].join ' '
+ cmd = Gem.ruby.shellsplit << "-I" << File.expand_path("../../..", __FILE__) <<
+ "-r" << get_relative_path(siteconf.path) << File.basename(extension)
+ cmd.push(*args)
begin
run cmd, results
ensure
if File.exist? 'mkmf.log'
- results << "To see why this extension failed to compile, please check" \
- " the mkmf.log which can be found here:\n"
- results << " " + File.join(dest_path, 'mkmf.log') + "\n"
+ unless $?.success?
+ results << "To see why this extension failed to compile, please check" \
+ " the mkmf.log which can be found here:\n"
+ results << " " + File.join(dest_path, 'mkmf.log') + "\n"
+ end
FileUtils.mv 'mkmf.log', dest_path
end
siteconf.unlink
@@ -62,7 +63,7 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
if tmp_dest
# TODO remove in RubyGems 3
- if Gem.install_extension_in_lib and lib_dir then
+ if Gem.install_extension_in_lib and lib_dir
FileUtils.mkdir_p lib_dir
entries = Dir.entries(tmp_dest) - %w[. ..]
entries = entries.map { |entry| File.join tmp_dest, entry }
@@ -76,9 +77,9 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
end
ensure
ENV["DESTDIR"] = destdir
+ siteconf.close!
end
end
- t.unlink if t and t.path
results
ensure
diff --git a/lib/rubygems/ext/rake_builder.rb b/lib/rubygems/ext/rake_builder.rb
index 682f1253e1..52041a2713 100644
--- a/lib/rubygems/ext/rake_builder.rb
+++ b/lib/rubygems/ext/rake_builder.rb
@@ -5,33 +5,31 @@
# See LICENSE.txt for permissions.
#++
+require "shellwords"
+
class Gem::Ext::RakeBuilder < Gem::Ext::Builder
- def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
- if File.basename(extension) =~ /mkrf_conf/i then
- cmd = "#{Gem.ruby} #{File.basename extension}"
- cmd << " #{args.join " "}" unless args.empty?
- run cmd, results
+ def self.build(extension, dest_path, results, args=[], lib_dir=nil)
+ if File.basename(extension) =~ /mkrf_conf/i
+ run([Gem.ruby, File.basename(extension), *args], results)
end
- # Deal with possible spaces in the path, e.g. C:/Program Files
- dest_path = '"' + dest_path.to_s + '"' if dest_path.to_s.include?(' ')
-
rake = ENV['rake']
- rake ||= begin
- "#{Gem.ruby} -rubygems #{Gem.bin_path('rake', 'rake')}"
- rescue Gem::Exception
- end
-
- rake ||= Gem.default_exec_format % 'rake'
-
- cmd = "#{rake} RUBYARCHDIR=#{dest_path} RUBYLIBDIR=#{dest_path}" # ENV is frozen
+ if rake
+ rake = rake.shellsplit
+ else
+ begin
+ rake = [Gem.ruby, "-rrubygems", Gem.bin_path('rake', 'rake')]
+ rescue Gem::Exception
+ rake = [Gem.default_exec_format % 'rake']
+ end
+ end
- run cmd, results
+ rake_args = ["RUBYARCHDIR=#{dest_path}", "RUBYLIBDIR=#{dest_path}", *args]
+ run(rake + rake_args, results)
results
end
end
-
diff --git a/lib/rubygems/gem_runner.rb b/lib/rubygems/gem_runner.rb
index fec9e403da..4159d81389 100644
--- a/lib/rubygems/gem_runner.rb
+++ b/lib/rubygems/gem_runner.rb
@@ -8,6 +8,7 @@
require 'rubygems'
require 'rubygems/command_manager'
require 'rubygems/config_file'
+require 'rubygems/deprecate'
##
# Load additional plugins from $LOAD_PATH
@@ -26,7 +27,10 @@ Gem.load_env_plugins rescue nil
class Gem::GemRunner
def initialize(options={})
- # TODO: nuke these options
+ if !options.empty? && !Gem::Deprecate.skip
+ Kernel.warn "NOTE: passing options to Gem::GemRunner.new is deprecated with no replacement. It will be removed on or after 2016-10-01."
+ end
+
@command_manager_class = options[:command_manager] || Gem::CommandManager
@config_file_class = options[:config_file] || Gem::ConfigFile
end
@@ -34,7 +38,7 @@ class Gem::GemRunner
##
# Run the gem command with the following arguments.
- def run args
+ def run(args)
build_args = extract_build_args args
do_configuration args
@@ -59,7 +63,7 @@ class Gem::GemRunner
# Separates the build arguments (those following <code>--</code>) from the
# other arguments in the list.
- def extract_build_args args # :nodoc:
+ def extract_build_args(args) # :nodoc:
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 7c6d6bb364..e49bff36f9 100644
--- a/lib/rubygems/gemcutter_utilities.rb
+++ b/lib/rubygems/gemcutter_utilities.rb
@@ -1,11 +1,14 @@
# frozen_string_literal: true
require 'rubygems/remote_fetcher'
+require 'rubygems/text'
##
# Utility methods for using the RubyGems API.
module Gem::GemcutterUtilities
+ include Gem::Text
+
# TODO: move to Gem::Command
OptionParser.accept Symbol do |value|
value.to_sym
@@ -25,10 +28,20 @@ module Gem::GemcutterUtilities
end
##
+ # Add the --otp option
+
+ def add_otp_option
+ add_option('--otp CODE',
+ 'Digit code for multifactor authentication') do |value, options|
+ options[:otp] = value
+ end
+ end
+
+ ##
# The API key from the command options or from the user's configuration.
def api_key
- if options[:key] then
+ if options[:key]
verify_api_key options[:key]
elsif Gem.configuration.api_keys.key?(host)
Gem.configuration.api_keys[host]
@@ -90,11 +103,11 @@ module Gem::GemcutterUtilities
# Signs in with the RubyGems API at +sign_in_host+ and sets the rubygems API
# key.
- def sign_in sign_in_host = nil
+ def sign_in(sign_in_host = nil)
sign_in_host ||= self.host
return if api_key
- pretty_host = if Gem::DEFAULT_HOST == sign_in_host then
+ pretty_host = if Gem::DEFAULT_HOST == sign_in_host
'RubyGems.org'
else
sign_in_host
@@ -113,6 +126,13 @@ module Gem::GemcutterUtilities
request.basic_auth email, password
end
+ if need_otp? response
+ response = rubygems_api_request(:get, "api/v1/api_key", sign_in_host) do |request|
+ request.basic_auth email, password
+ request.add_field "OTP", options[:otp]
+ end
+ end
+
with_response response do |resp|
say "Signed in."
set_api_key host, resp.body
@@ -124,7 +144,7 @@ module Gem::GemcutterUtilities
# an error.
def verify_api_key(key)
- if Gem.configuration.api_keys.key? key then
+ if Gem.configuration.api_keys.key? key
Gem.configuration.api_keys[key]
else
alert_error "No such API key. Please add it to your configuration (done automatically on initial `gem push`)."
@@ -139,24 +159,38 @@ module Gem::GemcutterUtilities
# If the response was not successful, shows an error to the user including
# the +error_prefix+ and the response body.
- def with_response response, error_prefix = nil
+ def with_response(response, error_prefix = nil)
case response
when Net::HTTPSuccess then
- if block_given? then
+ if block_given?
yield response
else
- say response.body
+ say clean_text(response.body)
end
else
message = response.body
message = "#{error_prefix}: #{message}" if error_prefix
- say message
+ say clean_text(message)
terminate_interaction 1 # TODO: question this
end
end
- def set_api_key host, key
+ ##
+ # Returns true when the user has enabled multifactor authentication from
+ # +response+ text.
+
+ def need_otp?(response)
+ return unless response.kind_of?(Net::HTTPUnauthorized) &&
+ response.body.start_with?('You have enabled multifactor authentication')
+ return true if options[:otp]
+
+ say 'You have enabled multi-factor authentication. Please enter OTP code.'
+ options[:otp] = ask 'Code: '
+ true
+ end
+
+ def set_api_key(host, key)
if host == Gem::DEFAULT_HOST
Gem.configuration.rubygems_api_key = key
else
@@ -165,4 +199,3 @@ module Gem::GemcutterUtilities
end
end
-
diff --git a/lib/rubygems/indexer.rb b/lib/rubygems/indexer.rb
index 871cc09d8d..b97347177d 100644
--- a/lib/rubygems/indexer.rb
+++ b/lib/rubygems/indexer.rb
@@ -2,11 +2,19 @@
require 'rubygems'
require 'rubygems/package'
require 'time'
+require 'tmpdir'
+rescue_exceptions = [LoadError]
+begin
+ require 'bundler/errors'
+rescue LoadError # this rubygems + old ruby
+else # this rubygems + ruby trunk with bundler
+ rescue_exceptions << Bundler::GemfileNotFound
+end
begin
gem 'builder'
require 'builder/xchar'
-rescue LoadError
+rescue *rescue_exceptions
end
##
@@ -54,7 +62,7 @@ class Gem::Indexer
require 'tmpdir'
require 'zlib'
- unless defined?(Builder::XChar) then
+ unless defined?(Builder::XChar)
raise "Gem::Indexer requires that the XML Builder library be installed:" +
"\n\tgem install builder"
end
@@ -64,7 +72,7 @@ class Gem::Indexer
@build_modern = options[:build_modern]
@dest_directory = directory
- @directory = File.join(Dir.tmpdir, "gem_generate_index_#{$$}")
+ @directory = Dir.mktmpdir 'gem_generate_index'
marshal_name = "Marshal.#{Gem.marshal_version}"
@@ -108,7 +116,7 @@ class Gem::Indexer
##
# Builds Marshal quick index gemspecs.
- def build_marshal_gemspecs specs
+ def build_marshal_gemspecs(specs)
count = specs.count
progress = ui.progress_reporter count,
"Generating Marshal quick index gemspecs for #{count} gems",
@@ -123,7 +131,7 @@ class Gem::Indexer
marshal_name = File.join @quick_marshal_dir, spec_file_name
marshal_zipped = Gem.deflate Marshal.dump(spec)
- open marshal_name, 'wb' do |io| io.write marshal_zipped end
+ File.open marshal_name, 'wb' do |io| io.write marshal_zipped end
files << marshal_name
@@ -153,7 +161,7 @@ class Gem::Indexer
platform = spec.original_platform
# win32-api-1.0.4-x86-mswin32-60
- unless String === platform then
+ unless String === platform
alert_warning "Skipping invalid platform in gem: #{spec.full_name}"
next
end
@@ -171,7 +179,7 @@ class Gem::Indexer
##
# Builds indices for RubyGems 1.2 and newer. Handles full, latest, prerelease
- def build_modern_indices specs
+ def build_modern_indices(specs)
prerelease, released = specs.partition { |s|
s.version.prerelease?
}
@@ -191,9 +199,9 @@ class Gem::Indexer
"#{@prerelease_specs_index}.gz"]
end
- def map_gems_to_specs gems
+ def map_gems_to_specs(gems)
gems.map { |gemfile|
- if File.size(gemfile) == 0 then
+ if File.size(gemfile) == 0
alert_warning "Skipping zero-length gem: #{gemfile}"
next
end
@@ -227,7 +235,7 @@ class Gem::Indexer
say "Compressing indices"
Gem.time 'Compressed indices' do
- if @build_modern then
+ if @build_modern
gzip @specs_index
gzip @latest_specs_index
gzip @prerelease_specs_index
@@ -261,7 +269,7 @@ class Gem::Indexer
zipped = Gem.deflate data
- open "#{filename}.#{extension}", 'wb' do |io|
+ File.open "#{filename}.#{extension}", 'wb' do |io|
io.write zipped
end
end
@@ -270,7 +278,7 @@ class Gem::Indexer
# List of gem file names to index.
def gem_file_list
- Dir[File.join(@dest_directory, "gems", '*.gem')]
+ Gem::Util.glob_files_in_dir("*.gem", File.join(@dest_directory, "gems"))
end
##
@@ -305,7 +313,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 then
+ if files.include? @quick_marshal_dir and not files.include? @quick_dir
files.delete @quick_marshal_dir
dst_name = File.join(@dest_directory, @quick_marshal_dir_base)
@@ -346,7 +354,7 @@ class Gem::Indexer
data = Gem.read_binary path
compressed_data = Gem.read_binary "#{path}.#{extension}"
- unless data == Gem.inflate(compressed_data) then
+ unless data == Gem::Util.inflate(compressed_data)
raise "Compressed file #{compressed_path} does not match uncompressed file #{path}"
end
end
@@ -366,7 +374,7 @@ class Gem::Indexer
gem_mtime >= specs_mtime
end
- if updated_gems.empty? then
+ if updated_gems.empty?
say 'No new gems'
terminate_interaction 0
end
@@ -427,7 +435,7 @@ class Gem::Indexer
specs_index = compact_specs specs_index.uniq.sort
- 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 dc73fd962b..f68fd2fd04 100644
--- a/lib/rubygems/install_default_message.rb
+++ b/lib/rubygems/install_default_message.rb
@@ -10,4 +10,3 @@ Gem.post_install do |installer|
ui = Gem::DefaultUserInteraction.ui
ui.say "Successfully installed #{installer.spec.full_name} as a default gem"
end
-
diff --git a/lib/rubygems/install_message.rb b/lib/rubygems/install_message.rb
index 6880db583e..3c13888a84 100644
--- a/lib/rubygems/install_message.rb
+++ b/lib/rubygems/install_message.rb
@@ -10,4 +10,3 @@ Gem.post_install do |installer|
ui = Gem::DefaultUserInteraction.ui
ui.say "Successfully installed #{installer.spec.full_name}"
end
-
diff --git a/lib/rubygems/install_update_options.rb b/lib/rubygems/install_update_options.rb
index 5559c94370..38a0682958 100644
--- a/lib/rubygems/install_update_options.rb
+++ b/lib/rubygems/install_update_options.rb
@@ -6,37 +6,18 @@
#++
require 'rubygems'
-
-# forward-declare
-
-module Gem::Security # :nodoc:
- class Policy # :nodoc:
- end
-end
+require 'rubygems/security_option'
##
# Mixin methods for install and update options for Gem::Commands
module Gem::InstallUpdateOptions
+ include Gem::SecurityOption
##
# Add the install/update options to the option parser.
def add_install_update_options
- # TODO: use @parser.accept
- OptionParser.accept Gem::Security::Policy do |value|
- require 'rubygems/security'
-
- raise OptionParser::InvalidArgument, 'OpenSSL not installed' unless
- defined?(Gem::Security::HighSecurity)
-
- value = Gem::Security::Policies[value]
- valid = Gem::Security::Policies.keys.sort
- message = "#{value} (#{valid.join ', '} are valid)"
- raise OptionParser::InvalidArgument, message if value.nil?
- value
- end
-
add_option(:"Install/Update", '-i', '--install-dir DIR',
'Gem repository directory to get installed',
'gems') do |value, options|
@@ -44,12 +25,12 @@ module Gem::InstallUpdateOptions
end
add_option(:"Install/Update", '-n', '--bindir DIR',
- 'Directory where binary files are',
+ 'Directory where executables are',
'located') do |value, options|
options[:bin_dir] = File.expand_path(value)
end
- add_option(:"Install/Update", '--[no-]document [TYPES]', Array,
+ 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|
@@ -69,7 +50,7 @@ module Gem::InstallUpdateOptions
add_option(:"Install/Update", '--vendor',
'Install gem into the vendor directory.',
'Only for use by gem repackagers.') do |value, options|
- unless Gem.vendor_dir then
+ unless Gem.vendor_dir
raise OptionParser::InvalidOption.new 'your platform is not supported'
end
@@ -82,30 +63,6 @@ module Gem::InstallUpdateOptions
options[:document] = []
end
- add_option(:Deprecated, '--[no-]rdoc',
- 'Generate RDoc for installed gems',
- 'Use --document instead') do |value, options|
- if value then
- options[:document] << 'rdoc'
- else
- options[:document].delete 'rdoc'
- end
-
- options[:document].uniq!
- end
-
- add_option(:Deprecated, '--[no-]ri',
- 'Generate ri data for installed gems.',
- 'Use --document instead') do |value, options|
- if value then
- options[:document] << 'ri'
- else
- options[:document].delete 'ri'
- end
-
- options[:document].uniq!
- end
-
add_option(:"Install/Update", '-E', '--[no-]env-shebang',
"Rewrite the shebang line on installed",
"scripts to use /usr/bin/env") do |value, options|
@@ -124,11 +81,7 @@ module Gem::InstallUpdateOptions
options[:wrappers] = value
end
- add_option(:"Install/Update", '-P', '--trust-policy POLICY',
- Gem::Security::Policy,
- 'Specify gem trust policy') do |value, options|
- options[:security_policy] = value
- end
+ add_security_option
add_option(:"Install/Update", '--ignore-dependencies',
'Do not install any required dependent gems') do |value, options|
@@ -136,8 +89,8 @@ module Gem::InstallUpdateOptions
end
add_option(:"Install/Update", '--[no-]format-executable',
- 'Make installed executable names match ruby.',
- 'If ruby is ruby18, foo_exec will be',
+ 'Make installed executable names match Ruby.',
+ 'If Ruby is ruby18, foo_exec will be',
'foo_exec18') do |value, options|
options[:format_executable] = value
end
@@ -187,7 +140,7 @@ module Gem::InstallUpdateOptions
File.exist? file
end unless v
- unless v then
+ unless v
message = v ? v : "(tried #{Gem::GEM_DEP_FILES.join ', '})"
raise OptionParser::InvalidArgument,
@@ -225,7 +178,6 @@ module Gem::InstallUpdateOptions
'Suggest alternates when gems are not found') do |v,o|
options[:suggest_alternate] = v
end
-
end
##
@@ -236,4 +188,3 @@ module Gem::InstallUpdateOptions
end
end
-
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index f4d3e728de..d26b1e88f6 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -7,6 +7,7 @@
require 'rubygems/command'
require 'rubygems/exceptions'
+require 'rubygems/deprecate'
require 'rubygems/package'
require 'rubygems/ext'
require 'rubygems/user_interaction'
@@ -27,11 +28,13 @@ require 'fileutils'
class Gem::Installer
+ extend Gem::Deprecate
+
##
# Paths where env(1) might live. Some systems are broken and have it in
# /bin
- ENV_PATHS = %w[/usr/bin/env /bin/env]
+ ENV_PATHS = %w[/usr/bin/env /bin/env].freeze
##
# Deprecated in favor of Gem::Ext::BuildError
@@ -98,7 +101,7 @@ class Gem::Installer
##
# Construct an installer object for the gem file located at +path+
- def self.at path, options = {}
+ def self.at(path, options = {})
security_policy = options[:security_policy]
package = Gem::Package.new path, security_policy
new package, options
@@ -107,11 +110,15 @@ class Gem::Installer
class FakePackage
attr_accessor :spec
+ attr_accessor :dir_mode
+ attr_accessor :prog_mode
+ attr_accessor :data_mode
+
def initialize(spec)
@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|
@@ -122,7 +129,7 @@ class Gem::Installer
end
end
- def copy_to path
+ def copy_to(path)
end
end
@@ -130,14 +137,15 @@ class Gem::Installer
# Construct an installer object for an ephemeral gem (one where we don't
# actually have a .gem file, just a spec)
- def self.for_spec spec, options = {}
+ def self.for_spec(spec, options = {})
# FIXME: we should have a real Package class for this
new FakePackage.new(spec), options
end
##
- # Constructs an Installer instance that will install the gem located at
- # +gem+. +options+ is a Hash with the following keys:
+ # Constructs an Installer instance that will install the gem at +package+ which
+ # can either be a path or an instance of Gem::Package. +options+ is a Hash
+ # with the following keys:
#
# :bin_dir:: Where to put a bin wrapper if needed.
# :development:: Whether or not development dependencies should be installed.
@@ -157,6 +165,7 @@ class Gem::Installer
# :wrappers:: Install wrappers if true, symlinks if false.
# :build_args:: An Array of arguments to pass to the extension builder
# process. If not set, then Gem::Command.build_args is used
+ # :post_install_message:: Print gem post install message if true
def initialize(package, options={})
require 'fileutils'
@@ -174,7 +183,13 @@ class Gem::Installer
process_options
- if options[:user_install] and not options[:unpack] then
+ @package.dir_mode = options[:dir_mode]
+ @package.prog_mode = options[:prog_mode]
+ @package.data_mode = options[:data_mode]
+
+ @bin_dir = options[:bin_dir] if options[:bin_dir]
+
+ if options[:user_install] and not options[:unpack]
@gem_home = Gem.user_dir
@bin_dir = Gem.bindir gem_home unless options[:bin_dir]
check_that_user_bin_dir_is_in_path
@@ -194,7 +209,7 @@ class Gem::Installer
#
# Otherwise +filename+ is overwritten.
- def check_executable_overwrite filename # :nodoc:
+ def check_executable_overwrite(filename) # :nodoc:
return if @force
generated_bin = File.join @bin_dir, formatted_program_filename(filename)
@@ -204,7 +219,7 @@ class Gem::Installer
ruby_executable = false
existing = nil
- open generated_bin, 'rb' do |io|
+ File.open generated_bin, 'rb' do |io|
next unless io.gets =~ /^#!/ # shebang
io.gets # blankline
@@ -214,7 +229,7 @@ class Gem::Installer
ruby_executable = true
existing = io.read.slice(%r{
- ^(
+ ^\s*(
gem \s |
load \s Gem\.bin_path\( |
load \s Gem\.activate_bin_path\(
@@ -230,7 +245,7 @@ class Gem::Installer
question = "#{spec.name}'s executable \"#{filename}\" conflicts with ".dup
- if ruby_executable then
+ if ruby_executable
question << (existing || 'an unknown executable')
return if ask_yes_no "#{question}\nOverwrite the executable?", false
@@ -283,7 +298,7 @@ class Gem::Installer
run_pre_install_hooks
# Set loaded_from to ensure extension_dir is correct
- if @options[:install_as_default] then
+ if @options[:install_as_default]
spec.loaded_from = default_spec_file
else
spec.loaded_from = spec_file
@@ -293,9 +308,10 @@ class Gem::Installer
FileUtils.rm_rf gem_dir
FileUtils.rm_rf spec.extension_dir
- FileUtils.mkdir_p gem_dir
+ dir_mode = options[:dir_mode]
+ FileUtils.mkdir_p gem_dir, :mode => dir_mode && 0755
- if @options[:install_as_default] then
+ if @options[:install_as_default]
extract_bin
write_default_spec
else
@@ -310,6 +326,8 @@ class Gem::Installer
write_cache_file
end
+ File.chmod(dir_mode, gem_dir) if dir_mode
+
say spec.post_install_message if options[:post_install_message] && !spec.post_install_message.nil?
Gem::Installer.install_lock.synchronize { Gem::Specification.reset }
@@ -326,7 +344,7 @@ class Gem::Installer
def run_pre_install_hooks # :nodoc:
Gem.pre_install_hooks.each do |hook|
- if hook.call(self) == false then
+ if hook.call(self) == false
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
message = "pre-install hook#{location} failed for #{spec.full_name}"
@@ -337,7 +355,7 @@ class Gem::Installer
def run_post_build_hooks # :nodoc:
Gem.post_build_hooks.each do |hook|
- if hook.call(self) == false then
+ if hook.call(self) == false
FileUtils.rm_rf gem_dir
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
@@ -363,7 +381,7 @@ class Gem::Installer
@specs ||= begin
specs = []
- Dir[File.join(gem_home, "specifications", "*.gemspec")].each do |path|
+ Gem::Util.glob_files_in_dir("*.gemspec", File.join(gem_home, "specifications")).each do |path|
spec = Gem::Specification.load path.untaint
specs << spec if spec
end
@@ -380,7 +398,7 @@ class Gem::Installer
# dependency :: Gem::Dependency
def ensure_dependency(spec, dependency)
- unless installation_satisfies_dependency? dependency then
+ unless installation_satisfies_dependency? dependency
raise Gem::InstallError, "#{spec.name} requires #{dependency}"
end
true
@@ -425,7 +443,7 @@ class Gem::Installer
# specifications directory.
def write_spec
- open spec_file, 'w' do |file|
+ File.open spec_file, 'w' do |file|
spec.installed_by_version = Gem.rubygems_version
file.puts spec.to_ruby_for_cache
@@ -448,7 +466,7 @@ class Gem::Installer
# Creates windows .bat files for easy running of commands
def generate_windows_script(filename, bindir)
- if Gem.win_platform? then
+ if Gem.win_platform?
script_name = filename + ".bat"
script_path = File.join bindir, File.basename(script_name)
File.open script_path, 'w' do |file|
@@ -462,25 +480,31 @@ class Gem::Installer
def generate_bin # :nodoc:
return if spec.executables.nil? or spec.executables.empty?
- Dir.mkdir @bin_dir unless File.exist? @bin_dir
+ begin
+ Dir.mkdir @bin_dir, *[options[:dir_mode] && 0755].compact
+ rescue SystemCallError
+ raise unless File.directory? @bin_dir
+ end
+
raise Gem::FilePermissionError.new(@bin_dir) unless File.writable? @bin_dir
spec.executables.each do |filename|
filename.untaint
bin_path = File.join gem_dir, spec.bindir, filename
- unless File.exist? bin_path then
+ unless File.exist? bin_path
# TODO change this to a more useful warning
- warn "#{bin_path} maybe `gem pristine #{spec.name}` will fix it?"
+ warn "`#{bin_path}` does not exist, maybe `gem pristine #{spec.name}` will fix it?"
next
end
mode = File.stat(bin_path).mode
- FileUtils.chmod mode | 0111, bin_path unless (mode | 0111) == mode
+ dir_mode = options[:prog_mode] || (mode | 0111)
+ FileUtils.chmod dir_mode, bin_path unless dir_mode == mode
check_executable_overwrite filename
- if @wrappers then
+ if @wrappers
generate_bin_script filename, @bin_dir
else
generate_bin_symlink filename, @bin_dir
@@ -503,6 +527,7 @@ class Gem::Installer
File.open bin_script_path, 'wb', 0755 do |file|
file.print app_script_text(filename)
+ file.chmod(options[:prog_mode] || 0755)
end
verbose bin_script_path
@@ -518,8 +543,8 @@ class Gem::Installer
src = File.join gem_dir, spec.bindir, filename
dst = File.join bindir, formatted_program_filename(filename)
- if File.exist? dst then
- if File.symlink? dst then
+ if File.exist? dst
+ if File.symlink? dst
link = File.readlink(dst).split File::SEPARATOR
cur_version = Gem::Version.create(link[-3].sub(/^.*-/, ''))
return if spec.version < cur_version
@@ -553,7 +578,7 @@ class Gem::Installer
path = File.join gem_dir, spec.bindir, bin_file_name
first_line = File.open(path, "rb") {|file| file.gets}
- if /\A#!/ =~ first_line then
+ if /\A#!/ =~ first_line
# Preserve extra words on shebang line, like "-w". Thanks RPA.
shebang = first_line.sub(/\A\#!.*?ruby\S*((\s+\S+)+)/, "#!#{Gem.ruby}")
opts = $1
@@ -578,9 +603,9 @@ class Gem::Installer
end
"#!#{which}"
- elsif not ruby_name then
+ elsif not ruby_name
"#!#{Gem.ruby}#{opts}"
- elsif opts then
+ elsif opts
"#!/bin/sh\n'exec' #{ruby_name.dump} '-x' \"$0\" \"$@\"\n#{shebang}"
else
# Create a plain shebang line.
@@ -606,18 +631,21 @@ class Gem::Installer
end
def ensure_required_ruby_version_met # :nodoc:
- if rrv = spec.required_ruby_version then
- unless rrv.satisfied_by? Gem.ruby_version then
- raise Gem::InstallError, "#{spec.name} requires Ruby version #{rrv}."
+ if rrv = spec.required_ruby_version
+ ruby_version = Gem.ruby_version
+ unless rrv.satisfied_by? ruby_version
+ raise Gem::RuntimeRequirementNotMetError,
+ "#{spec.name} requires Ruby version #{rrv}. The current ruby version is #{ruby_version}."
end
end
end
def ensure_required_rubygems_version_met # :nodoc:
- if rrgv = spec.required_rubygems_version then
- unless rrgv.satisfied_by? Gem.rubygems_version then
- raise Gem::InstallError,
- "#{spec.name} requires RubyGems version #{rrgv}. " +
+ if rrgv = spec.required_rubygems_version
+ unless rrgv.satisfied_by? Gem.rubygems_version
+ rg_version = Gem::VERSION
+ raise Gem::RuntimeRequirementNotMetError,
+ "#{spec.name} requires RubyGems version #{rrgv}. The current RubyGems version is #{rg_version}. " +
"Try 'gem update --system' to update RubyGems itself."
end
end
@@ -674,16 +702,16 @@ class Gem::Installer
File::ALT_SEPARATOR
path = ENV['PATH']
- if Gem.win_platform? then
+ if Gem.win_platform?
path = path.downcase
user_bin_dir = user_bin_dir.downcase
end
path = path.split(File::PATH_SEPARATOR)
- unless path.include? user_bin_dir then
+ unless path.include? user_bin_dir
unless !Gem.win_platform? && (path.include? user_bin_dir.sub(ENV['HOME'], '~'))
- unless self.class.path_warning then
+ unless self.class.path_warning
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
@@ -692,15 +720,39 @@ class Gem::Installer
end
def verify_gem_home(unpack = false) # :nodoc:
- FileUtils.mkdir_p gem_home
+ FileUtils.mkdir_p gem_home, :mode => options[:dir_mode] && 0755
raise Gem::FilePermissionError, gem_home unless
unpack or File.writable?(gem_home)
end
+ def verify_spec
+ unless spec.name =~ Gem::Specification::VALID_NAME_PATTERN
+ raise Gem::InstallError, "#{spec} has an invalid name"
+ end
+
+ if spec.raw_require_paths.any?{|path| path =~ /\R/ }
+ raise Gem::InstallError, "#{spec} has an invalid require_paths"
+ end
+
+ if spec.extensions.any?{|ext| ext =~ /\R/ }
+ raise Gem::InstallError, "#{spec} has an invalid extensions"
+ end
+
+ unless spec.specification_version.to_s =~ /\A\d+\z/
+ raise Gem::InstallError, "#{spec} has an invalid specification_version"
+ end
+
+ if spec.dependencies.any? {|dep| dep.type =~ /\R/ || dep.name =~ /\R/ }
+ raise Gem::InstallError, "#{spec} has an invalid dependencies"
+ end
+ end
+
##
# Return the text for an application file.
def app_script_text(bin_file_name)
+ # note that the `load` lines cannot be indented, as old RG versions match
+ # against the beginning of the line
return <<-TEXT
#{shebang bin_file_name}
#
@@ -714,16 +766,21 @@ require 'rubygems'
version = "#{Gem::Requirement.default}.a"
-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
+str = ARGV.first
+if str
+ str = str.b[/\\A_(.*)_\\z/, 1]
+ if str and Gem::Version.correct?(str)
+ version = str
ARGV.shift
end
end
+if Gem.respond_to?(:activate_bin_path)
load Gem.activate_bin_path('#{spec.name}', '#{bin_file_name}', version)
+else
+gem #{spec.name.dump}, version
+load Gem.bin_path(#{spec.name.dump}, #{bin_file_name.dump}, version)
+end
TEXT
end
@@ -731,17 +788,38 @@ TEXT
# return the stub script text used to launch the true Ruby script
def windows_stub_script(bindir, bin_file_name)
- ruby = Gem.ruby.gsub(/^\"|\"$/, "").tr(File::SEPARATOR, "\\")
- return <<-TEXT
+ rb_config = RbConfig::CONFIG
+ rb_topdir = RbConfig::TOPDIR || File.dirname(rb_config["bindir"])
+
+ # get ruby executable file name from RbConfig
+ ruby_exe = "#{rb_config['RUBY_INSTALL_NAME']}#{rb_config['EXEEXT']}"
+ ruby_exe = "ruby.exe" if ruby_exe.empty?
+
+ if File.exist?(File.join bindir, ruby_exe)
+ # stub & ruby.exe withing same folder. Portable
+ <<-TEXT
@ECHO OFF
-IF NOT "%~f0" == "~f0" GOTO :WinNT
-@"#{ruby}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
-GOTO :EOF
-:WinNT
-@"#{ruby}" "%~dpn0" %*
-TEXT
+@"%~dp0ruby.exe" "%~dpn0" %*
+ TEXT
+ elsif bindir.downcase.start_with? rb_topdir.downcase
+ # stub within ruby folder, but not standard bin. Portable
+ require 'pathname'
+ from = Pathname.new bindir
+ to = Pathname.new "#{rb_topdir}/bin"
+ rel = to.relative_path_from from
+ <<-TEXT
+@ECHO OFF
+@"%~dp0#{rel}/ruby.exe" "%~dpn0" %*
+ TEXT
+ else
+ # outside ruby folder, maybe -user-install or bundler. Portable, but ruby
+ # is dependent on PATH
+ <<-TEXT
+@ECHO OFF
+@ruby.exe "%~dpn0" %*
+ TEXT
+ end
end
-
##
# Builds extensions. Valid types of extensions are extconf.rb files,
# configure scripts and rakefiles or mkrf_conf files.
@@ -755,13 +833,14 @@ TEXT
##
# Logs the build +output+ in +build_dir+, then raises Gem::Ext::BuildError.
#
- # TODO: Delete this for RubyGems 3. It remains for API compatibility
+ # TODO: Delete this for RubyGems 4. It remains for API compatibility
def extension_build_error(build_dir, output, backtrace = nil) # :nodoc:
builder = Gem::Ext::Builder.new spec, @build_args
builder.build_error build_dir, output, backtrace
end
+ deprecate :extension_build_error, :none, 2018, 12
##
# Reads the file index and extracts each file into the gem directory.
@@ -785,7 +864,7 @@ TEXT
# Prefix and suffix the program filename the same as ruby.
def formatted_program_filename(filename)
- if @format_executable then
+ if @format_executable
self.class.exec_format % File.basename(filename)
else
filename
@@ -809,11 +888,15 @@ TEXT
#
# Version and dependency checks are skipped if this install is forced.
#
- # The dependent check will be skipped this install is ignoring dependencies.
+ # The dependent check will be skipped if the install is ignoring dependencies.
def pre_install_checks
verify_gem_home options[:unpack]
+ # The name and require_paths must be verified first, since it could contain
+ # ruby code that would be eval'ed in #ensure_loadable_spec
+ verify_spec
+
ensure_loadable_spec
if options[:install_as_default]
@@ -840,15 +923,18 @@ TEXT
build_info_dir = File.join gem_home, 'build_info'
- FileUtils.mkdir_p build_info_dir
+ 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"
- open build_info_file, 'w' do |io|
+ File.open build_info_file, 'w' do |io|
@build_args.each do |arg|
io.puts arg
end
end
+
+ File.chmod(dir_mode, build_info_dir) if dir_mode
end
##
diff --git a/lib/rubygems/installer_test_case.rb b/lib/rubygems/installer_test_case.rb
index eccd5711c2..d6d16a9194 100644
--- a/lib/rubygems/installer_test_case.rb
+++ b/lib/rubygems/installer_test_case.rb
@@ -72,7 +72,7 @@ class Gem::InstallerTestCase < Gem::TestCase
# a spec named 'a', intended for regular installs
# @user_spec::
# a spec named 'b', intended for user installs
-
+ #
# @gem::
# the path to a built gem from @spec
# @user_spec::
@@ -106,16 +106,6 @@ class Gem::InstallerTestCase < Gem::TestCase
Gem::Installer.path_warning = false
end
- def util_gem_bindir spec = @spec # :nodoc:
- # TODO: deprecate
- spec.bin_dir
- end
-
- def util_gem_dir spec = @spec # :nodoc:
- # TODO: deprecate
- spec.gem_dir
- end
-
##
# The path where installed executables live
@@ -193,4 +183,3 @@ class Gem::InstallerTestCase < Gem::TestCase
end
end
-
diff --git a/lib/rubygems/local_remote_options.rb b/lib/rubygems/local_remote_options.rb
index 597b87ea03..9fa256b08a 100644
--- a/lib/rubygems/local_remote_options.rb
+++ b/lib/rubygems/local_remote_options.rb
@@ -24,8 +24,10 @@ module Gem::LocalRemoteOptions
raise OptionParser::InvalidArgument, value
end
- unless ['http', 'https', 'file', 's3'].include?(uri.scheme)
- raise OptionParser::InvalidArgument, value
+ valid_uri_schemes = ["http", "https", "file", "s3"]
+ unless valid_uri_schemes.include?(uri.scheme)
+ msg = "Invalid uri scheme for #{value}\nPreface URLs with one of #{valid_uri_schemes.map{|s| "#{s}://"}}"
+ raise ArgumentError, msg
end
value
@@ -106,7 +108,7 @@ module Gem::LocalRemoteOptions
source << '/' if source !~ /\/\z/
- if options.delete :sources_cleared then
+ if options.delete :sources_cleared
Gem.sources = [source]
else
Gem.sources << source unless Gem.sources.include?(source)
@@ -146,4 +148,3 @@ module Gem::LocalRemoteOptions
end
end
-
diff --git a/lib/rubygems/mock_gem_ui.rb b/lib/rubygems/mock_gem_ui.rb
index 0223f8c35d..92ec85625c 100644
--- a/lib/rubygems/mock_gem_ui.rb
+++ b/lib/rubygems/mock_gem_ui.rb
@@ -12,7 +12,7 @@ class Gem::MockGemUi < Gem::StreamUI
class InputEOFError < RuntimeError
- def initialize question
+ def initialize(question)
super "Out of input for MockGemUi on #{question.inspect}"
end
@@ -21,7 +21,7 @@ class Gem::MockGemUi < Gem::StreamUI
class TermError < RuntimeError
attr_reader :exit_code
- def initialize exit_code
+ def initialize(exit_code)
super
@exit_code = exit_code
end
@@ -56,7 +56,7 @@ class Gem::MockGemUi < Gem::StreamUI
@terminated = false
end
- def ask question
+ def ask(question)
raise InputEOFError, question if @ins.eof?
super
@@ -86,4 +86,3 @@ class Gem::MockGemUi < Gem::StreamUI
end
end
-
diff --git a/lib/rubygems/name_tuple.rb b/lib/rubygems/name_tuple.rb
index 316329a0bd..e948fb3d86 100644
--- a/lib/rubygems/name_tuple.rb
+++ b/lib/rubygems/name_tuple.rb
@@ -24,7 +24,7 @@ class Gem::NameTuple
# Turn an array of [name, version, platform] into an array of
# NameTuple objects.
- def self.from_list list
+ def self.from_list(list)
list.map { |t| new(*t) }
end
@@ -32,7 +32,7 @@ class Gem::NameTuple
# Turn an array of NameTuple objects back into an array of
# [name, version, platform] tuples.
- def self.to_basic list
+ def self.to_basic(list)
list.map { |t| t.to_a }
end
@@ -90,7 +90,7 @@ class Gem::NameTuple
alias to_s inspect # :nodoc:
- def <=> other
+ def <=>(other)
[@name, @version, @platform == Gem::Platform::RUBY ? -1 : 1] <=>
[other.name, other.version,
other.platform == Gem::Platform::RUBY ? -1 : 1]
@@ -102,7 +102,7 @@ class Gem::NameTuple
# Compare with +other+. Supports another NameTuple or an Array
# in the [name, version, platform] format.
- def == other
+ def ==(other)
case other
when self.class
@name == other.name and
diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb
index c36e71d800..3bf4f8fd62 100644
--- a/lib/rubygems/package.rb
+++ b/lib/rubygems/package.rb
@@ -55,7 +55,7 @@ class Gem::Package
class FormatError < Error
attr_reader :path
- def initialize message, source = nil
+ def initialize(message, source = nil)
if source
@path = source.path
@@ -68,7 +68,7 @@ class Gem::Package
end
class PathError < Error
- def initialize destination, destination_dir
+ def initialize(destination, destination_dir)
super "installing into parent path %s of %s is not allowed" %
[destination, destination_dir]
end
@@ -107,12 +107,24 @@ class Gem::Package
attr_writer :spec
- def self.build spec, skip_validation=false
- gem_file = spec.file_name
+ ##
+ # Permission for directories
+ attr_accessor :dir_mode
+
+ ##
+ # Permission for program files
+ attr_accessor :prog_mode
+
+ ##
+ # Permission for other files
+ attr_accessor :data_mode
+
+ def self.build(spec, skip_validation = false, strict_validation = false, file_name = nil)
+ gem_file = file_name || spec.file_name
package = new gem_file
package.spec = spec
- package.build skip_validation
+ package.build skip_validation, strict_validation
gem_file
end
@@ -124,7 +136,7 @@ class Gem::Package
# If +gem+ is an existing file in the old format a Gem::Package::Old will be
# returned.
- def self.new gem, security_policy = nil
+ def self.new(gem, security_policy = nil)
gem = if gem.is_a?(Gem::Package::Source)
gem
elsif gem.respond_to? :read
@@ -145,10 +157,10 @@ class Gem::Package
##
# Creates a new package that will read or write to the file +gem+.
- def initialize gem, security_policy # :notnew:
+ def initialize(gem, security_policy) # :notnew:
@gem = gem
- @build_time = Time.now
+ @build_time = ENV["SOURCE_DATE_EPOCH"] ? Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc : Time.now
@checksums = {}
@contents = nil
@digests = Hash.new { |h, algorithm| h[algorithm] = {} }
@@ -162,14 +174,14 @@ class Gem::Package
##
# Copies this package to +path+ (if possible)
- def copy_to path
+ def copy_to(path)
FileUtils.cp @gem.path, path unless File.exist? path
end
##
# Adds a checksum for each entry in the gem to checksums.yaml.gz.
- def add_checksums tar
+ def add_checksums(tar)
Gem.load_yaml
checksums_by_algorithm = Hash.new { |h, algorithm| h[algorithm] = {} }
@@ -191,7 +203,7 @@ class Gem::Package
# Adds the files listed in the packages's Gem::Specification to data.tar.gz
# and adds this file to the +tar+.
- def add_contents tar # :nodoc:
+ def add_contents(tar) # :nodoc:
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|
@@ -206,20 +218,25 @@ class Gem::Package
##
# Adds files included the package's Gem::Specification to the +tar+ file
- def add_files tar # :nodoc:
+ def add_files(tar) # :nodoc:
@spec.files.each do |file|
stat = File.lstat file
if stat.symlink?
- relative_dir = File.dirname(file).sub("#{Dir.pwd}/", '')
- target_path = File.join(relative_dir, File.readlink(file))
+ target_path = File.readlink(file)
+
+ unless target_path.start_with? '.'
+ relative_dir = File.dirname(file).sub("#{Dir.pwd}/", '')
+ target_path = File.join(relative_dir, target_path)
+ end
+
tar.add_symlink file, target_path, stat.mode
end
next unless stat.file?
tar.add_file_simple file, stat.mode, stat.size do |dst_io|
- 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
@@ -229,7 +246,7 @@ class Gem::Package
##
# Adds the package's Gem::Specification to the +tar+ file
- def add_metadata tar # :nodoc:
+ def add_metadata(tar) # :nodoc:
digests = tar.add_file_signed 'metadata.gz', 0444, @signer do |io|
gzip_to io do |gz_io|
gz_io.write @spec.to_yaml
@@ -242,14 +259,20 @@ class Gem::Package
##
# Builds this package based on the specification set by #spec=
- def build skip_validation = false
+ def build(skip_validation = false, strict_validation = false)
+ raise ArgumentError, "skip_validation = true and strict_validation = true are incompatible" if skip_validation && strict_validation
+
Gem.load_yaml
require 'rubygems/security'
@spec.mark_version
- @spec.validate unless skip_validation
+ @spec.validate true, strict_validation unless skip_validation
- setup_signer
+ setup_signer(
+ signer_options: {
+ expiration_length_days: Gem.configuration.cert_expiration_length_days
+ }
+ )
@gem.with_write_io do |gem_io|
Gem::Package::TarWriter.new gem_io do |gem|
@@ -263,7 +286,7 @@ class Gem::Package
Successfully built RubyGem
Name: #{@spec.name}
Version: #{@spec.version}
- File: #{File.basename @spec.cache_file}
+ File: #{File.basename @gem.path}
EOM
ensure
@signer = nil
@@ -300,8 +323,8 @@ EOM
# Creates a digest of the TarEntry +entry+ from the digest algorithm set by
# the security policy.
- def digest entry # :nodoc:
- algorithms = if @checksums then
+ def digest(entry) # :nodoc:
+ algorithms = if @checksums
@checksums.keys
else
[Gem::Security::DIGEST_NAME].compact
@@ -309,7 +332,7 @@ EOM
algorithms.each do |algorithm|
digester =
- if defined?(OpenSSL::Digest) then
+ if defined?(OpenSSL::Digest)
OpenSSL::Digest.new algorithm
else
Digest.const_get(algorithm).new
@@ -331,10 +354,10 @@ EOM
# If +pattern+ is specified, only entries matching that glob will be
# extracted.
- def extract_files destination_dir, pattern = "*"
+ def extract_files(destination_dir, pattern = "*")
verify unless @spec
- FileUtils.mkdir_p destination_dir
+ FileUtils.mkdir_p destination_dir, :mode => dir_mode && 0755
@gem.with_read_io do |io|
reader = Gem::Package::TarReader.new io
@@ -360,7 +383,8 @@ EOM
# If +pattern+ is specified, only entries matching that glob will be
# extracted.
- def extract_tar_gz io, destination_dir, pattern = "*" # :nodoc:
+ def extract_tar_gz(io, destination_dir, pattern = "*") # :nodoc:
+ directories = [] if dir_mode
open_tar_gz io do |tar|
tar.each do |entry|
next unless File.fnmatch pattern, entry.full_name, File::FNM_DOTMATCH
@@ -370,19 +394,20 @@ EOM
FileUtils.rm_rf destination
mkdir_options = {}
- mkdir_options[:mode] = entry.header.mode if entry.directory?
+ mkdir_options[:mode] = dir_mode ? 0755 : (entry.header.mode if entry.directory?)
mkdir =
- if entry.directory? then
+ if entry.directory?
destination
else
File.dirname destination
end
+ directories << mkdir if directories
- FileUtils.mkdir_p mkdir, mkdir_options
+ mkdir_p_safe mkdir, mkdir_options, destination_dir, entry.full_name
- open destination, 'wb' do |out|
+ File.open destination, 'wb' do |out|
out.write entry.read
- FileUtils.chmod entry.header.mode, destination
+ FileUtils.chmod file_mode(entry.header.mode), destination
end if entry.file?
File.symlink(entry.header.linkname, destination) if entry.symlink?
@@ -390,6 +415,15 @@ EOM
verbose destination
end
end
+
+ if directories
+ directories.uniq!
+ File.chmod(dir_mode, *directories)
+ end
+ end
+
+ def file_mode(mode) # :nodoc:
+ ((mode & 0111).zero? ? data_mode : prog_mode) || mode
end
##
@@ -398,7 +432,7 @@ EOM
# Also sets the gzip modification time to the package build time to ease
# testing.
- def gzip_to io # :yields: gz_io
+ def gzip_to(io) # :yields: gz_io
gz_io = Zlib::GzipWriter.new io, Zlib::BEST_COMPRESSION
gz_io.mtime = @build_time
@@ -412,36 +446,65 @@ EOM
#
# If +filename+ is not inside +destination_dir+ an exception is raised.
- def install_location filename, destination_dir # :nodoc:
+ def install_location(filename, destination_dir) # :nodoc:
raise Gem::Package::PathError.new(filename, destination_dir) if
filename.start_with? '/'
- destination_dir = File.realpath destination_dir if
- File.respond_to? :realpath
- destination_dir = File.expand_path destination_dir
-
- destination = File.join destination_dir, filename
- destination = File.expand_path destination
+ destination_dir = File.expand_path(File.realpath(destination_dir))
+ destination = File.expand_path(File.join(destination_dir, filename))
raise Gem::Package::PathError.new(destination, destination_dir) unless
- destination.start_with? destination_dir
+ destination.start_with? destination_dir + '/'
+
+ begin
+ real_destination = File.expand_path(File.realpath(destination))
+ rescue
+ # it's fine if the destination doesn't exist, because rm -rf'ing it can't cause any damage
+ nil
+ else
+ raise Gem::Package::PathError.new(real_destination, destination_dir) unless
+ real_destination.start_with? destination_dir + '/'
+ end
destination.untaint
destination
end
+ def normalize_path(pathname)
+ if Gem.win_platform?
+ pathname.downcase
+ else
+ pathname
+ end
+ end
+
+ def mkdir_p_safe(mkdir, mkdir_options, destination_dir, file_name)
+ destination_dir = File.realpath(File.expand_path(destination_dir))
+ parts = mkdir.split(File::SEPARATOR)
+ parts.reduce do |path, basename|
+ path = File.realpath(path) unless path == ""
+ path = File.expand_path(path + File::SEPARATOR + basename)
+ lstat = File.lstat path rescue nil
+ if !lstat || !lstat.directory?
+ unless normalize_path(path).start_with? normalize_path(destination_dir) and (FileUtils.mkdir path, mkdir_options rescue false)
+ raise Gem::Package::PathError.new(file_name, destination_dir)
+ end
+ end
+ path
+ end
+ end
+
##
# Loads a Gem::Specification from the TarEntry +entry+
- def load_spec entry # :nodoc:
+ def load_spec(entry) # :nodoc:
case entry.full_name
when 'metadata' then
@spec = Gem::Specification.from_yaml entry.read
when 'metadata.gz' then
args = [entry]
args << { :external_encoding => Encoding::UTF_8 } if
- Object.const_defined?(:Encoding) &&
- Zlib::GzipReader.method(:wrap).arity != 1
+ Zlib::GzipReader.method(:wrap).arity != 1
Zlib::GzipReader.wrap(*args) do |gzio|
@spec = Gem::Specification.from_yaml gzio.read
@@ -452,7 +515,7 @@ EOM
##
# Opens +io+ as a gzipped tar archive
- def open_tar_gz io # :nodoc:
+ def open_tar_gz(io) # :nodoc:
Zlib::GzipReader.wrap io do |gzio|
tar = Gem::Package::TarReader.new gzio
@@ -463,12 +526,12 @@ EOM
##
# Reads and loads checksums.yaml.gz from the tar file +gem+
- def read_checksums gem
+ def read_checksums(gem)
Gem.load_yaml
@checksums = gem.seek 'checksums.yaml.gz' do |entry|
Zlib::GzipReader.wrap entry do |gz_io|
- YAML.load gz_io.read
+ Gem::SafeYAML.safe_load gz_io.read
end
end
end
@@ -477,10 +540,17 @@ EOM
# Prepares the gem for signing and checksum generation. If a signing
# certificate and key are not present only checksum generation is set up.
- def setup_signer
+ def setup_signer(signer_options: {})
passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
- if @spec.signing_key then
- @signer = Gem::Security::Signer.new @spec.signing_key, @spec.cert_chain, passphrase
+ if @spec.signing_key
+ @signer =
+ Gem::Security::Signer.new(
+ @spec.signing_key,
+ @spec.cert_chain,
+ passphrase,
+ signer_options
+ )
+
@spec.signing_key = nil
@spec.cert_chain = @signer.cert_chain.map { |cert| cert.to_s }
else
@@ -545,14 +615,14 @@ EOM
# Verifies the +checksums+ against the +digests+. This check is not
# cryptographically secure. Missing checksums are ignored.
- def verify_checksums digests, checksums # :nodoc:
+ def verify_checksums(digests, checksums) # :nodoc:
return unless checksums
checksums.sort.each do |algorithm, gem_digests|
gem_digests.sort.each do |file_name, gem_hexdigest|
computed_digest = digests[algorithm][file_name]
- unless computed_digest.hexdigest == gem_hexdigest then
+ unless computed_digest.hexdigest == gem_hexdigest
raise Gem::Package::FormatError.new \
"#{algorithm} checksum mismatch for #{file_name}", @gem
end
@@ -563,7 +633,7 @@ EOM
##
# Verifies +entry+ in a .gem file.
- def verify_entry entry
+ def verify_entry(entry)
file_name = entry.full_name
@files << file_name
@@ -576,7 +646,7 @@ EOM
end
case file_name
- when /^metadata(.gz)?$/ then
+ when "metadata", "metadata.gz" then
load_spec entry
when 'data.tar.gz' then
verify_gz entry
@@ -590,25 +660,29 @@ EOM
##
# Verifies the files of the +gem+
- def verify_files gem
+ def verify_files(gem)
gem.each do |entry|
verify_entry entry
end
- unless @spec then
+ unless @spec
raise Gem::Package::FormatError.new 'package metadata is missing', @gem
end
- unless @files.include? 'data.tar.gz' then
+ unless @files.include? 'data.tar.gz'
raise Gem::Package::FormatError.new \
'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?
+ raise Gem::Security::Exception, "duplicate files in the package: (#{duplicates.map(&:inspect).join(', ')})"
+ end
end
##
# Verifies that +entry+ is a valid gzipped file.
- def verify_gz entry # :nodoc:
+ def verify_gz(entry) # :nodoc:
Zlib::GzipReader.wrap entry do |gzio|
gzio.read 16384 until gzio.eof? # gzip checksum verification
end
diff --git a/lib/rubygems/package/digest_io.rb b/lib/rubygems/package/digest_io.rb
index 4930c9aa7d..d9e6c3c021 100644
--- a/lib/rubygems/package/digest_io.rb
+++ b/lib/rubygems/package/digest_io.rb
@@ -31,7 +31,7 @@ class Gem::Package::DigestIO
# digests['SHA1'].hexdigest #=> "aaf4c61d[...]"
# digests['SHA512'].hexdigest #=> "9b71d224[...]"
- def self.wrap io, digests
+ def self.wrap(io, digests)
digest_io = new io, digests
yield digest_io
@@ -43,7 +43,7 @@ class Gem::Package::DigestIO
# Creates a new DigestIO instance. Using ::wrap is recommended, see the
# ::wrap documentation for documentation of +io+ and +digests+.
- def initialize io, digests
+ def initialize(io, digests)
@io = io
@digests = digests
end
@@ -51,7 +51,7 @@ class Gem::Package::DigestIO
##
# Writes +data+ to the underlying IO and updates the digests
- def write data
+ def write(data)
result = @io.write data
@digests.each do |_, digest|
@@ -62,4 +62,3 @@ class Gem::Package::DigestIO
end
end
-
diff --git a/lib/rubygems/package/file_source.rb b/lib/rubygems/package/file_source.rb
index 1a4dc4c824..8a4f9da6f2 100644
--- a/lib/rubygems/package/file_source.rb
+++ b/lib/rubygems/package/file_source.rb
@@ -10,7 +10,7 @@ class Gem::Package::FileSource < Gem::Package::Source # :nodoc: all
attr_reader :path
- def initialize path
+ def initialize(path)
@path = path
end
@@ -22,13 +22,12 @@ class Gem::Package::FileSource < Gem::Package::Source # :nodoc: all
File.exist? path
end
- def with_write_io &block
- open path, 'wb', &block
+ def with_write_io(&block)
+ File.open path, 'wb', &block
end
- def with_read_io &block
- open path, 'rb', &block
+ def with_read_io(&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 ee79a21083..669a859d0a 100644
--- a/lib/rubygems/package/io_source.rb
+++ b/lib/rubygems/package/io_source.rb
@@ -11,7 +11,7 @@ class Gem::Package::IOSource < Gem::Package::Source # :nodoc: all
attr_reader :io
- def initialize io
+ def initialize(io)
@io = io
end
@@ -43,4 +43,3 @@ class Gem::Package::IOSource < Gem::Package::Source # :nodoc: all
end
end
-
diff --git a/lib/rubygems/package/old.rb b/lib/rubygems/package/old.rb
index 5e722baa35..f574b989aa 100644
--- a/lib/rubygems/package/old.rb
+++ b/lib/rubygems/package/old.rb
@@ -19,7 +19,7 @@ class Gem::Package::Old < Gem::Package
# Creates a new old-format package reader for +gem+. Old-format packages
# cannot be written.
- def initialize gem, security_policy
+ def initialize(gem, security_policy)
require 'fileutils'
require 'zlib'
Gem.load_yaml
@@ -49,7 +49,7 @@ class Gem::Package::Old < Gem::Package
##
# Extracts the files in this package into +destination_dir+
- def extract_files destination_dir
+ def extract_files(destination_dir)
verify
errstr = "Error reading files from gem"
@@ -78,9 +78,9 @@ class Gem::Package::Old < Gem::Package
FileUtils.rm_rf destination
- FileUtils.mkdir_p File.dirname destination
+ FileUtils.mkdir_p File.dirname(destination), :mode => dir_mode && 0755
- open destination, 'wb', entry['mode'] do |out|
+ File.open destination, 'wb', file_mode(entry['mode']) do |out|
out.write file_data
end
@@ -94,20 +94,20 @@ class Gem::Package::Old < Gem::Package
##
# Reads the file list section from the old-format gem +io+
- def file_list io # :nodoc:
+ def file_list(io) # :nodoc:
header = String.new
read_until_dashes io do |line|
header << line
end
- YAML.load header
+ Gem::SafeYAML.safe_load header
end
##
# Reads lines until a "---" separator is found
- def read_until_dashes io # :nodoc:
+ def read_until_dashes(io) # :nodoc:
while (line = io.gets) && line.chomp.strip != "---" do
yield line if block_given?
end
@@ -116,7 +116,7 @@ class Gem::Package::Old < Gem::Package
##
# Skips the Ruby self-install header in +io+.
- def skip_ruby io # :nodoc:
+ def skip_ruby(io) # :nodoc:
loop do
line = io.gets
@@ -124,7 +124,7 @@ class Gem::Package::Old < Gem::Package
break unless line
end
- raise Gem::Exception, "Failed to find end of ruby script while reading gem"
+ raise Gem::Exception, "Failed to find end of Ruby script while reading gem"
end
##
@@ -144,17 +144,9 @@ class Gem::Package::Old < Gem::Package
end
end
- yaml_error = if RUBY_VERSION < '1.9' then
- YAML::ParseError
- elsif YAML.const_defined?(:ENGINE) && YAML::ENGINE.yamler == 'syck' then
- YAML::ParseError
- else
- YAML::SyntaxError
- end
-
begin
@spec = Gem::Specification.from_yaml yaml
- rescue yaml_error
+ rescue YAML::SyntaxError
raise Gem::Exception, "Failed to parse gem specification out of gem file"
end
rescue ArgumentError
diff --git a/lib/rubygems/package/source.rb b/lib/rubygems/package/source.rb
index fe19776c38..69701e55e9 100644
--- a/lib/rubygems/package/source.rb
+++ b/lib/rubygems/package/source.rb
@@ -1,4 +1,3 @@
# 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 c54bd14d57..c7b5f88dbd 100644
--- a/lib/rubygems/package/tar_header.rb
+++ b/lib/rubygems/package/tar_header.rb
@@ -50,7 +50,7 @@ class Gem::Package::TarHeader
:uid,
:uname,
:version,
- ]
+ ].freeze
##
# Pack format for a tar header
@@ -94,40 +94,47 @@ class Gem::Package::TarHeader
attr_reader(*FIELDS)
+ EMPTY_HEADER = ("\0" * 512).freeze # :nodoc:
+
##
# Creates a tar header from IO +stream+
def self.from(stream)
header = stream.read 512
- empty = (header == "\0" * 512)
+ empty = (EMPTY_HEADER == header)
fields = header.unpack UNPACK_FORMAT
new :name => fields.shift,
- :mode => fields.shift.oct,
- :uid => fields.shift.oct,
- :gid => fields.shift.oct,
- :size => fields.shift.oct,
- :mtime => fields.shift.oct,
- :checksum => fields.shift.oct,
+ :mode => strict_oct(fields.shift),
+ :uid => strict_oct(fields.shift),
+ :gid => strict_oct(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 => fields.shift.oct,
+ :version => strict_oct(fields.shift),
:uname => fields.shift,
:gname => fields.shift,
- :devmajor => fields.shift.oct,
- :devminor => fields.shift.oct,
+ :devmajor => strict_oct(fields.shift),
+ :devminor => strict_oct(fields.shift),
:prefix => fields.shift,
:empty => empty
end
+ def self.strict_oct(str)
+ return str.oct if str =~ /\A[0-7]*\z/
+ raise ArgumentError, "#{str.inspect} is not an octal string"
+ end
+
##
# Creates a new TarHeader using +vals+
def initialize(vals)
- unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode] then
+ unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode]
raise ArgumentError, ":name, :size, :prefix and :mode required"
end
diff --git a/lib/rubygems/package/tar_reader.rb b/lib/rubygems/package/tar_reader.rb
index 1098336e36..f64915eaea 100644
--- a/lib/rubygems/package/tar_reader.rb
+++ b/lib/rubygems/package/tar_reader.rb
@@ -92,11 +92,9 @@ class Gem::Package::TarReader
# NOTE: Do not call #rewind during #each
def rewind
- if @init_pos == 0 then
- raise Gem::Package::NonSeekableIO unless @io.respond_to? :rewind
+ if @init_pos == 0
@io.rewind
else
- raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos=
@io.pos = @init_pos
end
end
@@ -106,7 +104,7 @@ class Gem::Package::TarReader
# yields it. Rewinds the tar file to the beginning when the block
# terminates.
- def seek name # :yields: entry
+ def seek(name) # :yields: entry
found = find do |entry|
entry.full_name == name
end
diff --git a/lib/rubygems/package/tar_reader/entry.rb b/lib/rubygems/package/tar_reader/entry.rb
index 5f958edc2f..19054c1635 100644
--- a/lib/rubygems/package/tar_reader/entry.rb
+++ b/lib/rubygems/package/tar_reader/entry.rb
@@ -64,7 +64,7 @@ class Gem::Package::TarReader::Entry
# Full name of the tar entry
def full_name
- if @header.prefix != "" then
+ if @header.prefix != ""
File.join @header.prefix, @header.name
else
@header.name
@@ -119,6 +119,12 @@ class Gem::Package::TarReader::Entry
bytes_read
end
+ def size
+ @header.size
+ end
+
+ alias length size
+
##
# Reads +len+ bytes from the tar file entry, or the rest of the entry if
# nil
@@ -137,7 +143,19 @@ class Gem::Package::TarReader::Entry
ret
end
- alias readpartial read # :nodoc:
+ def readpartial(maxlen = nil, outbuf = "".b)
+ check_closed
+
+ raise EOFError if @read >= @header.size
+
+ maxlen ||= @header.size - @read
+ max_read = [maxlen, @header.size - @read].min
+
+ @io.readpartial(max_read, outbuf)
+ @read += outbuf.size
+
+ outbuf
+ end
##
# Rewinds to the beginning of the tar file entry
@@ -145,8 +163,6 @@ class Gem::Package::TarReader::Entry
def rewind
check_closed
- raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos=
-
@io.pos = @orig_pos
@read = 0
end
diff --git a/lib/rubygems/package/tar_test_case.rb b/lib/rubygems/package/tar_test_case.rb
index 46ac949587..75978c8ed0 100644
--- a/lib/rubygems/package/tar_test_case.rb
+++ b/lib/rubygems/package/tar_test_case.rb
@@ -52,7 +52,7 @@ class Gem::Package::TarTestCase < Gem::TestCase
name = fields.shift
length = fields.shift.to_i
- if name == "checksum" then
+ if name == "checksum"
chksum_off = offset
offset += length
next
@@ -94,13 +94,7 @@ class Gem::Package::TarTestCase < Gem::TestCase
ASCIIZ(dname, 155) # char prefix[155]; ASCII + (Z unless filled)
]
- format = "C100C8C8C8C12C12C8CC100C6C2C32C32C8C8C155"
- h = if RUBY_VERSION >= "1.9" then
- arr.join
- else
- arr = arr.join("").split(//).map{|x| x[0]}
- arr.pack format
- end
+ h = arr.join
ret = h + "\0" * (512 - h.size)
assert_equal(512, ret.size)
ret
diff --git a/lib/rubygems/package/tar_writer.rb b/lib/rubygems/package/tar_writer.rb
index f68b8d4c5e..87ee39a944 100644
--- a/lib/rubygems/package/tar_writer.rb
+++ b/lib/rubygems/package/tar_writer.rb
@@ -106,12 +106,10 @@ class Gem::Package::TarWriter
def add_file(name, mode) # :yields: io
check_closed
- raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos=
-
name, prefix = split_name name
init_pos = @io.pos
- @io.write "\0" * 512 # placeholder for the header
+ @io.write Gem::Package::TarHeader::EMPTY_HEADER # placeholder for the header
yield RestrictedStream.new(@io) if block_given?
@@ -125,7 +123,7 @@ class Gem::Package::TarWriter
header = Gem::Package::TarHeader.new :name => name, :mode => mode,
:size => size, :prefix => prefix,
- :mtime => Time.now
+ :mtime => ENV["SOURCE_DATE_EPOCH"] ? Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc : Time.now
@io.write header
@io.pos = final_pos
@@ -141,11 +139,11 @@ class Gem::Package::TarWriter
#
# The created digest object is returned.
- def add_file_digest name, mode, digest_algorithms # :yields: io
+ def add_file_digest(name, mode, digest_algorithms) # :yields: io
digests = digest_algorithms.map do |digest_algorithm|
digest = digest_algorithm.new
digest_name =
- if digest.respond_to? :name then
+ if digest.respond_to? :name
digest.name
else
/::([^:]+)$/ =~ digest_algorithm.name
@@ -174,7 +172,7 @@ class Gem::Package::TarWriter
#
# Returns the digest.
- def add_file_signed name, mode, signer
+ def add_file_signed(name, mode, signer)
digest_algorithms = [
signer.digest_algorithm,
Digest::SHA512,
@@ -186,17 +184,18 @@ class Gem::Package::TarWriter
signature_digest = digests.values.compact.find do |digest|
digest_name =
- if digest.respond_to? :name then
+ if digest.respond_to? :name
digest.name
else
- /::([^:]+)$/ =~ digest.class.name
- $1
+ digest.class.name[/::([^:]+)\z/, 1]
end
digest_name == signer.digest_name
end
- if signer.key then
+ raise "no #{signer.digest_name} in #{digests.values.compact}" unless signature_digest
+
+ if signer.key
signature = signer.sign signature_digest.digest
add_file_simple "#{name}.sig", 0444, signature.length do |io|
@@ -218,7 +217,7 @@ class Gem::Package::TarWriter
header = Gem::Package::TarHeader.new(:name => name, :mode => mode,
:size => size, :prefix => prefix,
- :mtime => Time.now).to_s
+ :mtime => ENV["SOURCE_DATE_EPOCH"] ? Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc : Time.now).to_s
@io.write header
os = BoundedStream.new @io, size
@@ -246,7 +245,7 @@ class Gem::Package::TarWriter
:size => 0, :typeflag => "2",
:linkname => target,
:prefix => prefix,
- :mtime => Time.now).to_s
+ :mtime => ENV["SOURCE_DATE_EPOCH"] ? Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc : Time.now).to_s
@io.write header
@@ -299,7 +298,7 @@ class Gem::Package::TarWriter
header = Gem::Package::TarHeader.new :name => name, :mode => mode,
:typeflag => "5", :size => 0,
:prefix => prefix,
- :mtime => Time.now
+ :mtime => ENV["SOURCE_DATE_EPOCH"] ? Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc : Time.now
@io.write header
@@ -310,12 +309,12 @@ class Gem::Package::TarWriter
# Splits +name+ into a name and prefix that can fit in the TarHeader
def split_name(name) # :nodoc:
- if name.bytesize > 256 then
+ if name.bytesize > 256
raise Gem::Package::TooLongFileName.new("File \"#{name}\" has a too long path (should be 256 or less)")
end
prefix = ''
- if name.bytesize > 100 then
+ if name.bytesize > 100
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)
@@ -324,11 +323,11 @@ class Gem::Package::TarWriter
prefix = parts.join('/')
end
- if name.bytesize > 100 or prefix.empty? then
+ if name.bytesize > 100 or prefix.empty?
raise Gem::Package::TooLongFileName.new("File \"#{prefix}/#{name}\" has a too long name (should be 100 or less)")
end
- if prefix.bytesize > 155 then
+ if prefix.bytesize > 155
raise Gem::Package::TooLongFileName.new("File \"#{prefix}/#{name}\" has a too long base path (should be 155 or less)")
end
end
diff --git a/lib/rubygems/package_task.rb b/lib/rubygems/package_task.rb
index d554e3697b..a11d09fb21 100644
--- a/lib/rubygems/package_task.rb
+++ b/lib/rubygems/package_task.rb
@@ -126,4 +126,3 @@ class Gem::PackageTask < Rake::PackageTask
end
end
-
diff --git a/lib/rubygems/path_support.rb b/lib/rubygems/path_support.rb
index 618bc793c4..ed680d6553 100644
--- a/lib/rubygems/path_support.rb
+++ b/lib/rubygems/path_support.rb
@@ -23,12 +23,14 @@ class Gem::PathSupport
# hashtable, or defaults to ENV, the system environment.
#
def initialize(env)
- @home = env["GEM_HOME"] || Gem.default_dir
+ @home = env["GEM_HOME"] || Gem.default_dir
- if File::ALT_SEPARATOR then
- @home = @home.gsub(File::ALT_SEPARATOR, File::SEPARATOR)
+ if File::ALT_SEPARATOR
+ @home = @home.gsub(File::ALT_SEPARATOR, File::SEPARATOR)
end
+ @home = expand(@home)
+
@path = split_gem_path env["GEM_PATH"], @home
@spec_cache_dir = env["GEM_SPEC_CACHE"] || Gem.default_spec_cache_dir
@@ -41,7 +43,7 @@ class Gem::PathSupport
##
# Split the Gem search path (as reported by Gem.path).
- def split_gem_path gpaths, home
+ def split_gem_path(gpaths, home)
# FIX: it should be [home, *path], not [*path, home]
gem_path = []
@@ -54,7 +56,7 @@ class Gem::PathSupport
gem_path += default_path
end
- if File::ALT_SEPARATOR then
+ if File::ALT_SEPARATOR
gem_path.map! do |this_path|
this_path.gsub File::ALT_SEPARATOR, File::SEPARATOR
end
@@ -65,7 +67,7 @@ class Gem::PathSupport
gem_path = default_path
end
- gem_path.uniq
+ gem_path.map { |path| expand(path) }.uniq
end
# Return the default Gem path
@@ -77,4 +79,12 @@ class Gem::PathSupport
end
gem_path
end
+
+ def expand(path)
+ if File.directory?(path)
+ File.realpath(path)
+ else
+ path
+ end
+ end
end
diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb
index d22d91ae54..f8fe4dad54 100644
--- a/lib/rubygems/platform.rb
+++ b/lib/rubygems/platform.rb
@@ -56,7 +56,7 @@ class Gem::Platform
when String then
arch = arch.split '-'
- if arch.length > 2 and arch.last !~ /\d/ then # reassemble x86-linux-gnu
+ if arch.length > 2 and arch.last !~ /\d/ # reassemble x86-linux-gnu
extra = arch.pop
arch.last << "-#{extra}"
end
@@ -68,7 +68,7 @@ class Gem::Platform
else cpu
end
- if arch.length == 2 and arch.last =~ /^\d+(\.\d+)?$/ then # for command-line
+ if arch.length == 2 and arch.last =~ /^\d+(\.\d+)?$/ # for command-line
@os, @version = arch
return
end
@@ -112,7 +112,7 @@ class Gem::Platform
end
def inspect
- "#<%s:0x%x @cpu=%p, @os=%p, @version=%p>" % [self.class, object_id, *to_a]
+ "%s @cpu=%p, @os=%p, @version=%p>" % [super[0..-2], *to_a]
end
def to_a
@@ -195,12 +195,11 @@ class Gem::Platform
# A pure-Ruby gem that may use Gem::Specification#extensions to build
# binary files.
- RUBY = 'ruby'
+ RUBY = 'ruby'.freeze
##
# A platform-specific gem that is built for the packaging Ruby's platform.
# This will be replaced with Gem::Platform::local.
- CURRENT = 'current'
+ CURRENT = 'current'.freeze
end
-
diff --git a/lib/rubygems/psych_tree.rb b/lib/rubygems/psych_tree.rb
index 41a7314b53..6f399a289e 100644
--- a/lib/rubygems/psych_tree.rb
+++ b/lib/rubygems/psych_tree.rb
@@ -18,7 +18,7 @@ module Gem
end
# This is ported over from the yaml_tree in 1.9.3
- def format_time time
+ def format_time(time)
if time.utc?
time.strftime("%Y-%m-%d %H:%M:%S.%9N Z")
else
diff --git a/lib/rubygems/rdoc.rb b/lib/rubygems/rdoc.rb
index 7043bd2a31..dfaf7c55bf 100644
--- a/lib/rubygems/rdoc.rb
+++ b/lib/rubygems/rdoc.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
require 'rubygems'
-require 'rubygems/user_interaction'
-require 'fileutils'
begin
gem 'rdoc'
@@ -15,321 +13,12 @@ else
Gem.finish_resolve
end
-loaded_hook = false
-
begin
require 'rdoc/rubygems_hook'
- loaded_hook = true
module Gem
RDoc = ::RDoc::RubygemsHook
end
rescue LoadError
end
-##
-# Gem::RDoc provides methods to generate RDoc and ri data for installed gems.
-# It works for RDoc 1.0.1 (in Ruby 1.8) up to RDoc 3.6.
-#
-# This implementation is considered obsolete. The RDoc project is the
-# appropriate location to find this functionality. This file provides the
-# hooks to load RDoc generation code from the "rdoc" gem and a fallback in
-# case the installed version of RDoc does not have them.
-
-class Gem::RDoc # :nodoc: all
-
- include Gem::UserInteraction
- extend Gem::UserInteraction
-
- @rdoc_version = nil
- @specs = []
-
- ##
- # Force installation of documentation?
-
- attr_accessor :force
-
- ##
- # Generate rdoc?
-
- attr_accessor :generate_rdoc
-
- ##
- # Generate ri data?
-
- attr_accessor :generate_ri
-
- class << self
-
- ##
- # Loaded version of RDoc. Set by ::load_rdoc
-
- attr_reader :rdoc_version
-
- end
-
- ##
- # Post installs hook that generates documentation for each specification in
- # +specs+
-
- def self.generation_hook installer, specs
- start = Time.now
- types = installer.document
-
- generate_rdoc = types.include? 'rdoc'
- generate_ri = types.include? 'ri'
-
- specs.each do |spec|
- new(spec, generate_rdoc, generate_ri).generate
- end
-
- return unless generate_rdoc or generate_ri
-
- duration = (Time.now - start).to_i
- names = specs.map(&:name).join ', '
-
- say "Done installing documentation for #{names} after #{duration} seconds"
- end
-
- ##
- # Loads the RDoc generator
-
- def self.load_rdoc
- return if @rdoc_version
-
- require 'rdoc/rdoc'
-
- @rdoc_version = if ::RDoc.const_defined? :VERSION then
- Gem::Version.new ::RDoc::VERSION
- else
- Gem::Version.new '1.0.1'
- end
-
- rescue LoadError => e
- raise Gem::DocumentError, "RDoc is not installed: #{e}"
- end
-
- ##
- # Creates a new documentation generator for +spec+. RDoc and ri data
- # generation can be enabled or disabled through +generate_rdoc+ and
- # +generate_ri+ respectively.
- #
- # Only +generate_ri+ is enabled by default.
-
- def initialize spec, generate_rdoc = true, generate_ri = true
- @doc_dir = spec.doc_dir
- @file_info = nil
- @force = false
- @rdoc = nil
- @spec = spec
-
- @generate_rdoc = generate_rdoc
- @generate_ri = generate_ri
-
- @rdoc_dir = spec.doc_dir 'rdoc'
- @ri_dir = spec.doc_dir 'ri'
- end
-
- ##
- # Removes legacy rdoc arguments from +args+
- #--
- # TODO move to RDoc::Options
-
- def delete_legacy_args args
- args.delete '--inline-source'
- args.delete '--promiscuous'
- args.delete '-p'
- args.delete '--one-file'
- end
-
- ##
- # Generates documentation using the named +generator+ ("darkfish" or "ri")
- # and following the given +options+.
- #
- # Documentation will be generated into +destination+
-
- def document generator, options, destination
- generator_name = generator
-
- options = options.dup
- options.exclude ||= [] # TODO maybe move to RDoc::Options#finish
- options.setup_generator generator
- options.op_dir = destination
- options.finish
-
- generator = options.generator.new @rdoc.store, options
-
- @rdoc.options = options
- @rdoc.generator = generator
-
- say "Installing #{generator_name} documentation for #{@spec.full_name}"
-
- FileUtils.mkdir_p options.op_dir
-
- Dir.chdir options.op_dir do
- begin
- @rdoc.class.current = @rdoc
- @rdoc.generator.generate @file_info
- ensure
- @rdoc.class.current = nil
- end
- end
- end
-
- ##
- # Generates RDoc and ri data
-
- def generate
- return unless @generate_ri or @generate_rdoc
-
- setup
-
- options = nil
-
- if Gem::Requirement.new('< 3').satisfied_by? self.class.rdoc_version then
- generate_legacy
- return
- end
-
- ::RDoc::TopLevel.reset # TODO ::RDoc::RDoc.reset
- ::RDoc::Parser::C.reset
-
- args = @spec.rdoc_options
- args.concat @spec.source_paths
- args.concat @spec.extra_rdoc_files
-
- case config_args = Gem.configuration[:rdoc]
- when String then
- args = args.concat config_args.split
- when Array then
- args = args.concat config_args
- end
-
- delete_legacy_args args
-
- Dir.chdir @spec.full_gem_path do
- options = ::RDoc::Options.new
- options.default_title = "#{@spec.full_name} Documentation"
- options.parse args
- end
-
- options.quiet = !Gem.configuration.really_verbose
-
- @rdoc = new_rdoc
- @rdoc.options = options
-
- say "Parsing documentation for #{@spec.full_name}"
-
- Dir.chdir @spec.full_gem_path do
- @file_info = @rdoc.parse_files options.files
- end
-
- document 'ri', options, @ri_dir if
- @generate_ri and (@force or not File.exist? @ri_dir)
-
- document 'darkfish', options, @rdoc_dir if
- @generate_rdoc and (@force or not File.exist? @rdoc_dir)
- end
-
- ##
- # Generates RDoc and ri data for legacy RDoc versions. This method will not
- # exist in future versions.
-
- def generate_legacy
- if @generate_rdoc then
- FileUtils.rm_rf @rdoc_dir
- say "Installing RDoc documentation for #{@spec.full_name}"
- legacy_rdoc '--op', @rdoc_dir
- end
-
- if @generate_ri then
- FileUtils.rm_rf @ri_dir
- say "Installing ri documentation for #{@spec.full_name}"
- legacy_rdoc '--ri', '--op', @ri_dir
- end
- end
-
- ##
- # Generates RDoc using a legacy version of RDoc from the ARGV-like +args+.
- # This method will not exist in future versions.
-
- def legacy_rdoc *args
- args << @spec.rdoc_options
- args << '--quiet'
- args << @spec.require_paths.clone
- args << @spec.extra_rdoc_files
- args << '--title' << "#{@spec.full_name} Documentation"
- args = args.flatten.map do |arg| arg.to_s end
-
- delete_legacy_args args if
- Gem::Requirement.new('>= 2.4.0') =~ self.class.rdoc_version
-
- r = new_rdoc
- verbose { "rdoc #{args.join ' '}" }
-
- Dir.chdir @spec.full_gem_path do
- begin
- r.document args
- rescue Errno::EACCES => e
- dirname = File.dirname e.message.split("-")[1].strip
- raise Gem::FilePermissionError, dirname
- rescue Interrupt => e
- raise e
- rescue Exception => ex
- alert_error "While generating documentation for #{@spec.full_name}"
- ui.errs.puts "... MESSAGE: #{ex}"
- ui.errs.puts "... RDOC args: #{args.join(' ')}"
- ui.backtrace ex
- ui.errs.puts "(continuing with the rest of the installation)"
- end
- end
- end
-
- ##
- # #new_rdoc creates a new RDoc instance. This method is provided only to
- # make testing easier.
-
- def new_rdoc # :nodoc:
- ::RDoc::RDoc.new
- end
-
- ##
- # Is rdoc documentation installed?
-
- def rdoc_installed?
- File.exist? @rdoc_dir
- end
-
- ##
- # Removes generated RDoc and ri data
-
- def remove
- base_dir = @spec.base_dir
-
- raise Gem::FilePermissionError, base_dir unless File.writable? base_dir
-
- FileUtils.rm_rf @rdoc_dir
- FileUtils.rm_rf @ri_dir
- end
-
- ##
- # Is ri data installed?
-
- def ri_installed?
- File.exist? @ri_dir
- end
-
- ##
- # Prepares the spec for documentation generation
-
- def setup
- self.class.load_rdoc
-
- raise Gem::FilePermissionError, @doc_dir if
- File.exist?(@doc_dir) and not File.writable?(@doc_dir)
-
- FileUtils.mkdir_p @doc_dir unless File.exist? @doc_dir
- end
-
-end unless loaded_hook
-
Gem.done_installing(&Gem::RDoc.method(:generation_hook))
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index e6a13d4b8c..69879df67c 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -71,13 +71,10 @@ class Gem::RemoteFetcher
# HTTP_PROXY_PASS)
# * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy
#
- # +dns+: An object to use for DNS resolution of the API endpoint.
- # By default, use Resolv::DNS.
- #
# +headers+: A set of additional HTTP headers to be sent to the server when
# fetching the gem.
- def initialize(proxy=nil, dns=Resolv::DNS.new, headers={})
+ def initialize(proxy=nil, dns=nil, headers={})
require 'net/http'
require 'stringio'
require 'time'
@@ -90,42 +87,17 @@ class Gem::RemoteFetcher
@pool_lock = Mutex.new
@cert_files = Gem::Request.get_cert_files
- @dns = dns
@headers = headers
end
##
- # Given a source at +uri+, calculate what hostname to actually
- # connect to query the data for it.
-
- def api_endpoint(uri)
- host = uri.host
-
- begin
- res = @dns.getresource "_rubygems._tcp.#{host}",
- Resolv::DNS::Resource::IN::SRV
- rescue Resolv::ResolvError => e
- verbose "Getting SRV record failed: #{e}"
- uri
- else
- target = res.target.to_s.strip
-
- if /\.#{Regexp.quote(host)}\z/ =~ target
- return URI.parse "#{uri.scheme}://#{target}#{uri.path}"
- end
-
- uri
- end
- end
-
- ##
# Given a name and requirement, downloads this gem into cache and returns the
# filename. Returns nil if the gem cannot be located.
#--
# Should probably be integrated with #download below, but that will be a
# larger, more encompassing effort. -erikh
- def download_to_cache dependency
+ def download_to_cache(dependency)
found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dependency
return if found.empty?
@@ -142,9 +114,9 @@ class Gem::RemoteFetcher
def download(spec, source_uri, install_dir = Gem.dir)
cache_dir =
- if Dir.pwd == install_dir then # see fetch_command
+ if Dir.pwd == install_dir # see fetch_command
install_dir
- elsif File.writable? install_dir then
+ elsif File.writable? install_dir
File.join install_dir, "cache"
else
File.join Gem.user_dir, "cache"
@@ -164,9 +136,7 @@ class Gem::RemoteFetcher
begin
source_uri = URI.parse(source_uri)
rescue
- source_uri = URI.parse(URI.const_defined?(:DEFAULT_PARSER) ?
- URI::DEFAULT_PARSER.escape(source_uri.to_s) :
- URI.escape(source_uri.to_s))
+ source_uri = URI.parse(URI::DEFAULT_PARSER.escape(source_uri.to_s))
end
end
@@ -179,7 +149,7 @@ class Gem::RemoteFetcher
# REFACTOR: be sure to clean up fake fetcher when you do this... cleaner
case scheme
when 'http', 'https', 's3' then
- unless File.exist? local_gem_path then
+ unless File.exist? local_gem_path
begin
verbose "Downloading gem #{gem_file_name}"
@@ -213,7 +183,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?(':') then
+ !source_uri.path.include?(':')
"#{source_uri.scheme}:#{source_uri.path}"
else
source_uri.path
@@ -239,14 +209,14 @@ class Gem::RemoteFetcher
##
# File Fetcher. Dispatched by +fetch_path+. Use it instead.
- def fetch_file uri, *_
+ def fetch_file(uri, *_)
Gem.read_binary correct_for_windows_path uri.path
end
##
# HTTP Fetcher. Dispatched by +fetch_path+. Use it instead.
- def fetch_http uri, last_modified = nil, head = false, depth = 0
+ def fetch_http(uri, last_modified = nil, head = false, depth = 0)
fetch_type = head ? Net::HTTP::Head : Net::HTTP::Get
response = request uri, fetch_type, last_modified do |req|
headers.each { |k,v| req.add_field(k,v) }
@@ -293,7 +263,7 @@ class Gem::RemoteFetcher
if data and !head and uri.to_s =~ /\.gz$/
begin
- data = Gem.gunzip data
+ data = Gem::Util.gunzip data
rescue Zlib::GzipFile::Error
raise FetchError.new("server did not return a valid file", uri.to_s)
end
@@ -304,7 +274,8 @@ class Gem::RemoteFetcher
raise
rescue Timeout::Error
raise UnknownHostError.new('timed out', uri.to_s)
- rescue IOError, SocketError, SystemCallError => e
+ rescue IOError, SocketError, SystemCallError,
+ *(OpenSSL::SSL::SSLError if defined?(OpenSSL)) => e
if e.message =~ /getaddrinfo/
raise UnknownHostError.new('no such name', uri.to_s)
else
@@ -321,7 +292,7 @@ class Gem::RemoteFetcher
# Downloads +uri+ to +path+ if necessary. If no path is given, it just
# passes the data.
- def cache_update_path uri, path = nil, update = true
+ def cache_update_path(uri, path = nil, update = true)
mtime = path && File.stat(path).mtime rescue nil
data = fetch_path(uri, mtime)
@@ -386,17 +357,15 @@ class Gem::RemoteFetcher
require 'base64'
require 'openssl'
- unless uri.user && uri.password
- raise FetchError.new("credentials needed in s3 source, like s3://key:secret@bucket-name/", uri.to_s)
- end
+ id, secret = s3_source_auth uri
expiration ||= s3_expiration
canonical_path = "/#{uri.host}#{uri.path}"
payload = "GET\n\n\n#{expiration}\n#{canonical_path}"
- digest = OpenSSL::HMAC.digest('sha1', uri.password, payload)
+ digest = OpenSSL::HMAC.digest('sha1', secret, payload)
# URI.escape is deprecated, and there isn't yet a replacement that does quite what we want
signature = Base64.encode64(digest).gsub("\n", '').gsub(/[\+\/=]/) { |c| BASE64_URI_TRANSLATE[c] }
- URI.parse("https://#{uri.host}.s3.amazonaws.com#{uri.path}?AWSAccessKeyId=#{uri.user}&Expires=#{expiration}&Signature=#{signature}")
+ URI.parse("https://#{uri.host}.s3.amazonaws.com#{uri.path}?AWSAccessKeyId=#{id}&Expires=#{expiration}&Signature=#{signature}")
end
def s3_expiration
@@ -407,13 +376,30 @@ class Gem::RemoteFetcher
private
- def proxy_for proxy, uri
+ def proxy_for(proxy, uri)
Gem::Request.proxy_uri(proxy || Gem::Request.get_proxy_from_env(uri.scheme))
end
- def pools_for proxy
+ def pools_for(proxy)
@pool_lock.synchronize do
@pools[proxy] ||= Gem::Request::ConnectionPools.new proxy, @cert_files
end
end
+
+ def s3_source_auth(uri)
+ return [uri.user, uri.password] if uri.user && uri.password
+
+ s3_source = Gem.configuration[:s3_source] || Gem.configuration['s3_source']
+ host = uri.host
+ raise FetchError.new("no s3_source key exists in .gemrc", "s3://#{host}") unless s3_source
+
+ auth = s3_source[host] || s3_source[host.to_sym]
+ raise FetchError.new("no key for host #{host} in s3_source in .gemrc", "s3://#{host}") unless auth
+
+ id = auth[:id] || auth['id']
+ secret = auth[:secret] || auth['secret']
+ raise FetchError.new("s3_source for #{host} missing id or secret", "s3://#{host}") unless id and secret
+
+ [id, secret]
+ end
end
diff --git a/lib/rubygems/request.rb b/lib/rubygems/request.rb
index a0d766d9ae..fb164d79cf 100644
--- a/lib/rubygems/request.rb
+++ b/lib/rubygems/request.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
require 'net/http'
-require 'thread'
require 'time'
require 'rubygems/user_interaction'
@@ -11,7 +10,7 @@ class Gem::Request
###
# Legacy. This is used in tests.
- def self.create_with_proxy uri, request_class, last_modified, proxy # :nodoc:
+ def self.create_with_proxy(uri, request_class, last_modified, proxy) # :nodoc:
cert_files = get_cert_files
proxy ||= get_proxy_from_env(uri.scheme)
pool = ConnectionPools.new proxy_uri(proxy), cert_files
@@ -19,7 +18,7 @@ class Gem::Request
new(uri, request_class, last_modified, pool.pool_for(uri))
end
- def self.proxy_uri proxy # :nodoc:
+ def self.proxy_uri(proxy) # :nodoc:
case proxy
when :no_proxy then nil
when URI::HTTP then proxy
@@ -52,7 +51,7 @@ class Gem::Request
Gem.configuration.ssl_verify_mode || OpenSSL::SSL::VERIFY_PEER
store = OpenSSL::X509::Store.new
- if Gem.configuration.ssl_client_cert then
+ if Gem.configuration.ssl_client_cert
pem = File.read Gem.configuration.ssl_client_cert
connection.cert = OpenSSL::X509::Certificate.new pem
connection.key = OpenSSL::PKey::RSA.new pem
@@ -83,10 +82,10 @@ class Gem::Request
e.message =~ / -- openssl$/
raise Gem::Exception.new(
- 'Unable to require openssl, install OpenSSL and rebuild ruby (preferred) or use non-HTTPS sources')
+ 'Unable to require openssl, install OpenSSL and rebuild Ruby (preferred) or use non-HTTPS sources')
end
- def self.verify_certificate store_context
+ def self.verify_certificate(store_context)
depth = store_context.error_depth
error = store_context.error_string
number = store_context.error
@@ -99,7 +98,7 @@ class Gem::Request
ui.alert_error extra_message if extra_message
end
- def self.verify_certificate_message error_number, cert
+ def self.verify_certificate_message(error_number, cert)
return unless cert
case error_number
when OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED then
@@ -118,9 +117,11 @@ class Gem::Request
"Certificate #{cert.subject} has an invalid purpose"
when OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN then
"Root certificate is not trusted (#{cert.subject})"
- when OpenSSL::X509::V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
- OpenSSL::X509::V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE then
+ when OpenSSL::X509::V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY then
"You must add #{cert.issuer} to your local trusted store"
+ when
+ OpenSSL::X509::V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE then
+ "Cannot verify certificate issued by #{cert.issuer}"
end
end
@@ -138,7 +139,7 @@ class Gem::Request
def fetch
request = @request_class.new @uri.request_uri
- unless @uri.nil? || @uri.user.nil? || @uri.user.empty? then
+ unless @uri.nil? || @uri.user.nil? || @uri.user.empty?
request.basic_auth Gem::UriFormatter.new(@uri.user).unescape,
Gem::UriFormatter.new(@uri.password).unescape
end
@@ -147,7 +148,7 @@ class Gem::Request
request.add_field 'Connection', 'keep-alive'
request.add_field 'Keep-Alive', '30'
- if @last_modified then
+ if @last_modified
request.add_field 'If-Modified-Since', @last_modified.httpdate
end
@@ -160,7 +161,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"]
@@ -172,7 +173,7 @@ class Gem::Request
uri = URI(Gem::UriFormatter.new(env_proxy).normalize)
- if uri and uri.user.nil? and uri.password.nil? then
+ if uri and uri.user.nil? and uri.password.nil?
user = ENV["#{_scheme}_proxy_user"] || ENV["#{_SCHEME}_PROXY_USER"]
password = ENV["#{_scheme}_proxy_pass"] || ENV["#{_SCHEME}_PROXY_PASS"]
@@ -183,7 +184,7 @@ class Gem::Request
uri
end
- def perform_request request # :nodoc:
+ def perform_request(request) # :nodoc:
connection = connection_for @uri
retried = false
@@ -275,9 +276,9 @@ class Gem::Request
ruby_version += 'dev' if RUBY_PATCHLEVEL == -1
ua << " Ruby/#{ruby_version} (#{RUBY_RELEASE_DATE}"
- if RUBY_PATCHLEVEL >= 0 then
+ if RUBY_PATCHLEVEL >= 0
ua << " patchlevel #{RUBY_PATCHLEVEL}"
- elsif defined?(RUBY_REVISION) then
+ elsif defined?(RUBY_REVISION)
ua << " revision #{RUBY_REVISION}"
end
ua << ")"
diff --git a/lib/rubygems/request/connection_pools.rb b/lib/rubygems/request/connection_pools.rb
index 31fc609800..c02f083b63 100644
--- a/lib/rubygems/request/connection_pools.rb
+++ b/lib/rubygems/request/connection_pools.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-require 'thread'
class Gem::Request::ConnectionPools # :nodoc:
@@ -9,19 +8,19 @@ class Gem::Request::ConnectionPools # :nodoc:
attr_accessor :client
end
- def initialize proxy_uri, cert_files
+ def initialize(proxy_uri, cert_files)
@proxy_uri = proxy_uri
@cert_files = cert_files
@pools = {}
@pool_mutex = Mutex.new
end
- def pool_for uri
+ def pool_for(uri)
http_args = net_http_args(uri, @proxy_uri)
key = http_args + [https?(uri)]
@pool_mutex.synchronize do
@pools[key] ||=
- if https? uri then
+ if https? uri
Gem::Request::HTTPSPool.new(http_args, @cert_files, @proxy_uri)
else
Gem::Request::HTTPPool.new(http_args, @cert_files, @proxy_uri)
@@ -46,22 +45,35 @@ class Gem::Request::ConnectionPools # :nodoc:
env_no_proxy.split(/\s*,\s*/)
end
- def https? uri
+ def https?(uri)
uri.scheme.downcase == 'https'
end
- def no_proxy? host, env_no_proxy
+ def no_proxy?(host, env_no_proxy)
host = host.downcase
env_no_proxy.any? do |pattern|
- pattern = pattern.downcase
+ env_no_proxy_pattern = pattern.downcase.dup
- host[-pattern.length, pattern.length] == pattern or
- (pattern.start_with? '.' and pattern[1..-1] == host)
+ # Remove dot in front of pattern for wildcard matching
+ env_no_proxy_pattern[0] = "" if env_no_proxy_pattern[0] == "."
+
+ host_tokens = host.split(".")
+ pattern_tokens = env_no_proxy_pattern.split(".")
+
+ intersection = (host_tokens - pattern_tokens) | (pattern_tokens - host_tokens)
+
+ # When we do the split into tokens we miss a dot character, so add it back if we need it
+ missing_dot = intersection.length > 0 ? 1 : 0
+ start = intersection.join(".").size + missing_dot
+
+ no_proxy_host = host[start..-1]
+
+ env_no_proxy_pattern == no_proxy_host
end
end
- def net_http_args uri, proxy_uri
+ def net_http_args(uri, proxy_uri)
# URI::Generic#hostname was added in ruby 1.9.3, use it if exists, otherwise
# don't support IPv6 literals and use host.
hostname = uri.respond_to?(:hostname) ? uri.hostname : uri.host
@@ -69,7 +81,7 @@ class Gem::Request::ConnectionPools # :nodoc:
no_proxy = get_no_proxy_from_env
- if proxy_uri and not no_proxy?(hostname, no_proxy) then
+ if proxy_uri and not no_proxy?(hostname, no_proxy)
proxy_hostname = proxy_uri.respond_to?(:hostname) ? proxy_uri.hostname : proxy_uri.host
net_http_args + [
proxy_hostname,
@@ -77,7 +89,7 @@ class Gem::Request::ConnectionPools # :nodoc:
Gem::UriFormatter.new(proxy_uri.user).unescape,
Gem::UriFormatter.new(proxy_uri.password).unescape,
]
- elsif no_proxy? hostname, no_proxy then
+ elsif no_proxy? hostname, no_proxy
net_http_args + [nil, nil]
else
net_http_args
@@ -85,4 +97,3 @@ class Gem::Request::ConnectionPools # :nodoc:
end
end
-
diff --git a/lib/rubygems/request/http_pool.rb b/lib/rubygems/request/http_pool.rb
index bfcd15399d..a85fc2bdf6 100644
--- a/lib/rubygems/request/http_pool.rb
+++ b/lib/rubygems/request/http_pool.rb
@@ -8,7 +8,7 @@
class Gem::Request::HTTPPool # :nodoc:
attr_reader :cert_files, :proxy_uri
- def initialize http_args, cert_files, proxy_uri
+ def initialize(http_args, cert_files, proxy_uri)
@http_args = http_args
@cert_files = cert_files
@proxy_uri = proxy_uri
@@ -20,7 +20,7 @@ class Gem::Request::HTTPPool # :nodoc:
@queue.pop || make_connection
end
- def checkin connection
+ def checkin(connection)
@queue.push connection
end
@@ -39,10 +39,9 @@ class Gem::Request::HTTPPool # :nodoc:
setup_connection Gem::Request::ConnectionPools.client.new(*@http_args)
end
- def setup_connection connection
+ def setup_connection(connection)
connection.start
connection
end
end
-
diff --git a/lib/rubygems/request/https_pool.rb b/lib/rubygems/request/https_pool.rb
index e82c2440e1..50f42d9e0d 100644
--- a/lib/rubygems/request/https_pool.rb
+++ b/lib/rubygems/request/https_pool.rb
@@ -2,10 +2,8 @@
class Gem::Request::HTTPSPool < Gem::Request::HTTPPool # :nodoc:
private
- def setup_connection connection
+ def setup_connection(connection)
Gem::Request.configure_connection_for_https(connection, @cert_files)
super
end
end
-
-
diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb
index 5541e64b88..6017d15d13 100644
--- a/lib/rubygems/request_set.rb
+++ b/lib/rubygems/request_set.rb
@@ -91,7 +91,7 @@ class Gem::RequestSet
#
# set = Gem::RequestSet.new nokogiri, pg
- def initialize *deps
+ def initialize(*deps)
@dependencies = deps
@always_install = []
@@ -119,8 +119,8 @@ class Gem::RequestSet
##
# Declare that a gem of name +name+ with +reqs+ requirements is needed.
- def gem name, *reqs
- if dep = @dependency_names[name] then
+ def gem(name, *reqs)
+ if dep = @dependency_names[name]
dep.requirement.concat reqs
else
dep = Gem::Dependency.new name, *reqs
@@ -132,7 +132,7 @@ class Gem::RequestSet
##
# Add +deps+ Gem::Dependency objects to the set.
- def import deps
+ def import(deps)
@dependencies.concat deps
end
@@ -143,7 +143,7 @@ class Gem::RequestSet
# The +installer+ will be +nil+ if a gem matching the request was already
# installed.
- def install options, &block # :yields: request, installer
+ def install(options, &block) # :yields: request, installer
if dir = options[:install_dir]
requests = install_into dir, false, options, &block
return requests
@@ -152,42 +152,73 @@ class Gem::RequestSet
@prerelease = options[:prerelease]
requests = []
+ download_queue = Queue.new
+ # Create a thread-safe list of gems to download
sorted_requests.each do |req|
- if req.installed? then
+ download_queue << req
+ end
+
+ # Create N threads in a pool, have them download all the gems
+ threads = Gem.configuration.concurrent_downloads.times.map do
+ # When a thread pops this item, it knows to stop running. The symbol
+ # is queued here so that there will be one symbol per thread.
+ download_queue << :stop
+
+ Thread.new do
+ # The pop method will block waiting for items, so the only way
+ # to stop a thread from running is to provide a final item that
+ # means the thread should stop.
+ while req = download_queue.pop
+ break if req == :stop
+ req.spec.download options unless req.installed?
+ end
+ end
+ end
+
+ # Wait for all the downloads to finish before continuing
+ threads.each(&:value)
+
+ # Install requested gems after they have been downloaded
+ sorted_requests.each do |req|
+ if req.installed?
req.spec.spec.build_extensions
- if @always_install.none? { |spec| spec == req.spec.spec } then
+ if @always_install.none? { |spec| spec == req.spec.spec }
yield req, nil if block_given?
next
end
end
- spec = req.spec.install options do |installer|
- yield req, installer if block_given?
- end
+ spec =
+ begin
+ req.spec.install options do |installer|
+ yield req, installer if block_given?
+ end
+ rescue Gem::RuntimeRequirementNotMetError => e
+ recent_match = req.spec.set.find_all(req.request).sort_by(&:version).reverse_each.find do |s|
+ s = s.spec
+ s.required_ruby_version.satisfied_by?(Gem.ruby_version) &&
+ s.required_rubygems_version.satisfied_by?(Gem.rubygems_version) &&
+ Gem::Platform.installable?(s)
+ end
+ if recent_match
+ suggestion = "The last version of #{req.request} to support your Ruby & RubyGems was #{recent_match.version}. Try installing it with `gem install #{recent_match.name} -v #{recent_match.version}`"
+ suggestion += " and then running the current command again" unless @always_install.include?(req.spec.spec)
+ else
+ suggestion = "There are no versions of #{req.request} compatible with your Ruby & RubyGems"
+ suggestion += ". Maybe try installing an older version of the gem you're looking for?" unless @always_install.include?(req.spec.spec)
+ end
+ e.suggestion = suggestion
+ raise
+ end
requests << spec
end
return requests if options[:gemdeps]
- specs = requests.map do |request|
- case request
- when Gem::Resolver::ActivationRequest then
- request.spec.spec
- else
- request
- end
- end
-
- require 'rubygems/dependency_installer'
- inst = Gem::DependencyInstaller.new options
- inst.installed_gems.replace specs
-
- Gem.done_installing_hooks.each do |hook|
- hook.call inst, specs
- end unless Gem.done_installing_hooks.empty?
+ install_hooks requests, options
requests
end
@@ -199,7 +230,7 @@ class Gem::RequestSet
# If +:without_groups+ is given in the +options+, those groups in the gem
# dependencies file are not used. See Gem::Installer for other +options+.
- def install_from_gemdeps options, &block
+ def install_from_gemdeps(options, &block)
gemdeps = options[:gemdeps]
@install_dir = options[:install_dir] || Gem.dir
@@ -224,7 +255,7 @@ class Gem::RequestSet
else
installed = install options, &block
- if options.fetch :lock, true then
+ if options.fetch :lock, true
lockfile =
Gem::RequestSet::Lockfile.build self, gemdeps, gem_deps_api.dependencies
lockfile.write
@@ -234,7 +265,7 @@ class Gem::RequestSet
end
end
- def install_into dir, force = true, options = {}
+ def install_into(dir, force = true, options = {})
gem_home, ENV['GEM_HOME'] = ENV['GEM_HOME'], dir
existing = force ? [] : specs_in(dir)
@@ -252,7 +283,7 @@ class Gem::RequestSet
sorted_requests.each do |request|
spec = request.spec
- if existing.find { |s| s.full_name == spec.full_name } then
+ if existing.find { |s| s.full_name == spec.full_name }
yield request, nil if block_given?
next
end
@@ -264,15 +295,39 @@ class Gem::RequestSet
installed << request
end
+ install_hooks installed, options
+
installed
ensure
ENV['GEM_HOME'] = gem_home
end
##
+ # Call hooks on installed gems
+
+ def install_hooks(requests, options)
+ specs = requests.map do |request|
+ case request
+ when Gem::Resolver::ActivationRequest then
+ request.spec.spec
+ else
+ request
+ end
+ end
+
+ require "rubygems/dependency_installer"
+ inst = Gem::DependencyInstaller.new options
+ inst.installed_gems.replace specs
+
+ Gem.done_installing_hooks.each do |hook|
+ hook.call inst, specs
+ end unless Gem.done_installing_hooks.empty?
+ end
+
+ ##
# Load a dependency management file.
- def load_gemdeps path, without_groups = [], installing = false
+ def load_gemdeps(path, without_groups = [], installing = false)
@git_set = Gem::Resolver::GitSet.new
@vendor_set = Gem::Resolver::VendorSet.new
@source_set = Gem::Resolver::SourceSet.new
@@ -293,29 +348,29 @@ class Gem::RequestSet
gf.load
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[RequestSet:', ']' do
q.breakable
- if @remote then
+ if @remote
q.text 'remote'
q.breakable
end
- if @prerelease then
+ if @prerelease
q.text 'prerelease'
q.breakable
end
- if @development_shallow then
+ if @development_shallow
q.text 'shallow development'
q.breakable
- elsif @development then
+ elsif @development
q.text 'development'
q.breakable
end
- if @soft_missing then
+ if @soft_missing
q.text 'soft missing'
end
@@ -339,7 +394,7 @@ class Gem::RequestSet
# Resolve the requested dependencies and return an Array of Specification
# objects to be activated.
- def resolve set = Gem::Resolver::BestSet.new
+ def resolve(set = Gem::Resolver::BestSet.new)
@sets << set
@sets << @git_set
@sets << @vendor_set
@@ -388,17 +443,17 @@ class Gem::RequestSet
@specs ||= @requests.map { |r| r.full_spec }
end
- def specs_in dir
- Dir["#{dir}/specifications/*.gemspec"].map do |g|
+ def specs_in(dir)
+ Gem::Util.glob_files_in_dir("*.gemspec", File.join(dir, "specifications")).map do |g|
Gem::Specification.load g
end
end
- def tsort_each_node &block # :nodoc:
+ def tsort_each_node(&block) # :nodoc:
@requests.each(&block)
end
- def tsort_each_child node # :nodoc:
+ def tsort_each_child(node) # :nodoc:
node.spec.dependencies.each do |dep|
next if dep.type == :development and not @development
@@ -406,7 +461,7 @@ class Gem::RequestSet
dep.match? r.spec.name, r.spec.version, @prerelease
}
- unless match then
+ unless match
next if dep.type == :development and @development_shallow
next if @soft_missing
raise Gem::DependencyError,
diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb
index 4b2699d7d2..3892e7aa5f 100644
--- a/lib/rubygems/request_set/gem_dependency_api.rb
+++ b/lib/rubygems/request_set/gem_dependency_api.rb
@@ -48,7 +48,7 @@ class Gem::RequestSet::GemDependencyAPI
:ruby_19 => %w[ruby rbx maglev],
:ruby_20 => %w[ruby rbx maglev],
:ruby_21 => %w[ruby rbx maglev],
- }
+ }.freeze
mswin = Gem::Platform.new 'x86-mswin32'
mswin64 = Gem::Platform.new 'x64-mswin64'
@@ -88,7 +88,7 @@ class Gem::RequestSet::GemDependencyAPI
: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'
@@ -129,7 +129,7 @@ class Gem::RequestSet::GemDependencyAPI
: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,
@@ -160,7 +160,7 @@ class Gem::RequestSet::GemDependencyAPI
:x64_mingw => :only,
:x64_mingw_20 => :only,
:x64_mingw_21 => :only,
- }
+ }.freeze
##
# The gems required by #gem statements in the gem.deps.rb file
@@ -191,7 +191,7 @@ class Gem::RequestSet::GemDependencyAPI
# Creates a new GemDependencyAPI that will add dependencies to the
# Gem::RequestSet +set+ based on the dependency API description in +path+.
- def initialize set, path
+ def initialize(set, path)
@set = set
@path = path
@@ -228,7 +228,7 @@ class Gem::RequestSet::GemDependencyAPI
# Adds +dependencies+ to the request set if any of the +groups+ are allowed.
# This is used for gemspec dependencies.
- def add_dependencies groups, dependencies # :nodoc:
+ def add_dependencies(groups, dependencies) # :nodoc:
return unless (groups & @without_groups).empty?
dependencies.each do |dep|
@@ -241,7 +241,7 @@ class Gem::RequestSet::GemDependencyAPI
##
# Finds a gemspec with the given +name+ that lives at +path+.
- def find_gemspec name, path # :nodoc:
+ def find_gemspec(name, path) # :nodoc:
glob = File.join path, "#{name}.gemspec"
spec_files = Dir[glob]
@@ -269,7 +269,7 @@ class Gem::RequestSet::GemDependencyAPI
# In installing mode certain restrictions are ignored such as ruby version
# mismatch checks.
- def installing= installing # :nodoc:
+ def installing=(installing) # :nodoc:
@installing = installing
end
@@ -353,7 +353,7 @@ class Gem::RequestSet::GemDependencyAPI
# tag: ::
# Use the given tag for git:, gist: and github: dependencies.
- def gem name, *requirements
+ def gem(name, *requirements)
options = requirements.pop if requirements.last.kind_of?(Hash)
options ||= {}
@@ -369,9 +369,9 @@ class Gem::RequestSet::GemDependencyAPI
duplicate = @dependencies.include? name
@dependencies[name] =
- if requirements.empty? and not source_set then
+ if requirements.empty? and not source_set
Gem::Requirement.default
- elsif source_set then
+ elsif source_set
Gem::Requirement.source_set
else
Gem::Requirement.create requirements
@@ -387,7 +387,7 @@ class Gem::RequestSet::GemDependencyAPI
gem_requires name, options
- if duplicate then
+ if duplicate
warn <<-WARNING
Gem dependencies file #{@path} requires #{name} more than once.
WARNING
@@ -401,8 +401,8 @@ Gem dependencies file #{@path} requires #{name} more than once.
#
# Returns +true+ if the gist or git option was handled.
- def gem_git name, options # :nodoc:
- if gist = options.delete(:gist) then
+ def gem_git(name, options) # :nodoc:
+ if gist = options.delete(:gist)
options[:git] = "https://gist.github.com/#{gist}.git"
end
@@ -424,7 +424,7 @@ Gem dependencies file #{@path} requires #{name} more than once.
#
# Returns reference for the git gem.
- def gem_git_reference options # :nodoc:
+ def gem_git_reference(options) # :nodoc:
ref = options.delete :ref
branch = options.delete :branch
tag = options.delete :tag
@@ -457,7 +457,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
#
# Returns +true+ if the custom source option was handled.
- def gem_git_source name, options # :nodoc:
+ def gem_git_source(name, options) # :nodoc:
return unless git_source = (@git_sources.keys & options.keys).last
source_callback = @git_sources[git_source]
@@ -478,7 +478,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
# Handles the :group and :groups +options+ for the gem with the given
# +name+.
- def gem_group name, options # :nodoc:
+ def gem_group(name, options) # :nodoc:
g = options.delete :group
all_groups = g ? Array(g) : []
@@ -497,7 +497,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
#
# Returns +true+ if the path option was handled.
- def gem_path name, options # :nodoc:
+ def gem_path(name, options) # :nodoc:
return unless directory = options.delete(:path)
pin_gem_source name, :path, directory
@@ -514,7 +514,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
#
# Returns +true+ if the source option was handled.
- def gem_source name, options # :nodoc:
+ def gem_source(name, options) # :nodoc:
return unless source = options.delete(:source)
pin_gem_source name, :source, source
@@ -530,7 +530,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
# Handles the platforms: option from +options+. Returns true if the
# platform matches the current platform.
- def gem_platforms options # :nodoc:
+ def gem_platforms(options) # :nodoc:
platform_names = Array(options.delete :platform)
platform_names.concat Array(options.delete :platforms)
platform_names.concat @current_platforms if @current_platforms
@@ -543,7 +543,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
next false unless Gem::Platform.match platform
- if engines = ENGINE_MAP[platform_name] then
+ if engines = ENGINE_MAP[platform_name]
next false unless engines.include? Gem.ruby_engine
end
@@ -564,9 +564,9 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
# Records the require: option from +options+ and adds those files, or the
# default file to the require list for +name+.
- def gem_requires name, options # :nodoc:
- if options.include? :require then
- if requires = options.delete(:require) then
+ def gem_requires(name, options) # :nodoc:
+ if options.include? :require
+ if requires = options.delete(:require)
@requires[name].concat Array requires
end
else
@@ -587,7 +587,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
# gem 'activerecord'
# end
- def git repository
+ def git(repository)
@current_repository = repository
yield
@@ -601,7 +601,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
# for use in gems built from git repositories. You must provide a block
# that accepts a git repository name for expansion.
- def git_source name, &callback
+ def git_source(name, &callback)
@git_sources[name] = callback
end
@@ -634,7 +634,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
# The group to add development dependencies to. By default this is
# :development. Only one group may be specified.
- def gemspec options = {}
+ def gemspec(options = {})
name = options.delete(:name) || '{,*}'
path = options.delete(:path) || '.'
development_group = options.delete(:development_group) || :development
@@ -679,7 +679,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
# development`. See `gem help install` and `gem help gem_dependencies` for
# further details.
- def group *groups
+ def group(*groups)
@current_groups = groups
yield
@@ -692,7 +692,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
# Pins the gem +name+ to the given +source+. Adding a gem with the same
# name from a different +source+ will raise an exception.
- def pin_gem_source name, type = :default, source = nil
+ def pin_gem_source(name, type = :default, source = nil)
source_description =
case type
when :default then '(default)'
@@ -754,7 +754,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
# NOTE: There is inconsistency in what environment a platform matches. You
# may need to read the source to know the exact details.
- def platform *platforms
+ def platform(*platforms)
@current_platforms = platforms
yield
@@ -781,36 +781,36 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
# version. This matching is performed by using the RUBY_ENGINE and
# engine_specific VERSION constants. (For JRuby, JRUBY_VERSION).
- def ruby version, options = {}
+ def ruby(version, options = {})
engine = options[:engine]
engine_version = options[:engine_version]
raise ArgumentError,
- 'you must specify engine_version along with the ruby engine' if
+ 'You must specify engine_version along with the Ruby engine' if
engine and not engine_version
return true if @installing
- unless RUBY_VERSION == version then
+ unless RUBY_VERSION == version
message = "Your Ruby version is #{RUBY_VERSION}, " +
"but your #{gem_deps_file} requires #{version}"
raise Gem::RubyVersionMismatch, message
end
- if engine and engine != Gem.ruby_engine then
- message = "Your ruby engine is #{Gem.ruby_engine}, " +
+ if engine and engine != Gem.ruby_engine
+ message = "Your Ruby engine is #{Gem.ruby_engine}, " +
"but your #{gem_deps_file} requires #{engine}"
raise Gem::RubyVersionMismatch, message
end
- if engine_version then
+ if engine_version
my_engine_version = Object.const_get "#{Gem.ruby_engine.upcase}_VERSION"
- if engine_version != my_engine_version then
+ if engine_version != my_engine_version
message =
- "Your ruby engine version is #{Gem.ruby_engine} #{my_engine_version}, " +
+ "Your Ruby engine version is #{Gem.ruby_engine} #{my_engine_version}, " +
"but your #{gem_deps_file} requires #{engine} #{engine_version}"
raise Gem::RubyVersionMismatch, message
@@ -834,7 +834,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
# * The +prepend:+ option is not supported. If you wish to order sources
# then list them in your preferred order.
- def source url
+ def source(url)
Gem.sources.clear if @default_sources
@default_sources = false
@@ -842,8 +842,4 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
Gem.sources << url
end
- # TODO: remove this typo name at RubyGems 3.0
-
- Gem::RequestSet::GemDepedencyAPI = self # :nodoc:
-
end
diff --git a/lib/rubygems/request_set/lockfile.rb b/lib/rubygems/request_set/lockfile.rb
index 7f6eadb939..1b374660f0 100644
--- a/lib/rubygems/request_set/lockfile.rb
+++ b/lib/rubygems/request_set/lockfile.rb
@@ -29,7 +29,7 @@ class Gem::RequestSet::Lockfile
# Raises a ParseError with the given +message+ which was encountered at a
# +line+ and +column+ while parsing.
- def initialize message, column, line, path
+ def initialize(message, column, line, path)
@line = line
@column = column
@path = path
@@ -41,13 +41,13 @@ class Gem::RequestSet::Lockfile
# Creates a new Lockfile for the given +request_set+ and +gem_deps_file+
# location.
- def self.build request_set, gem_deps_file, dependencies = nil
+ def self.build(request_set, gem_deps_file, dependencies = nil)
request_set.resolve
dependencies ||= requests_to_deps request_set.sorted_requests
new request_set, gem_deps_file, dependencies
end
- def self.requests_to_deps requests # :nodoc:
+ def self.requests_to_deps(requests) # :nodoc:
deps = {}
requests.each do |request|
@@ -56,7 +56,7 @@ class Gem::RequestSet::Lockfile
requirement = request.request.dependency.requirement
deps[name] = if [Gem::Resolver::VendorSpecification,
- Gem::Resolver::GitSpecification].include? spec.class then
+ Gem::Resolver::GitSpecification].include? spec.class
Gem::Requirement.source_set
else
requirement
@@ -71,7 +71,7 @@ class Gem::RequestSet::Lockfile
attr_reader :platforms
- def initialize request_set, gem_deps_file, dependencies
+ def initialize(request_set, gem_deps_file, dependencies)
@set = request_set
@dependencies = dependencies
@gem_deps_file = File.expand_path(gem_deps_file)
@@ -82,7 +82,7 @@ class Gem::RequestSet::Lockfile
@platforms = []
end
- def add_DEPENDENCIES out # :nodoc:
+ def add_DEPENDENCIES(out) # :nodoc:
out << "DEPENDENCIES"
out.concat @dependencies.sort_by { |name,| name }.map { |name, requirement|
@@ -92,7 +92,7 @@ class Gem::RequestSet::Lockfile
out << nil
end
- def add_GEM out, spec_groups # :nodoc:
+ def add_GEM(out, spec_groups) # :nodoc:
return if spec_groups.empty?
source_groups = spec_groups.values.flatten.group_by do |request|
@@ -122,7 +122,7 @@ class Gem::RequestSet::Lockfile
end
end
- def add_GIT out, git_requests
+ def add_GIT(out, git_requests)
return if git_requests.empty?
by_repository_revision = git_requests.group_by do |request|
@@ -148,11 +148,11 @@ class Gem::RequestSet::Lockfile
end
end
- def relative_path_from dest, base # :nodoc:
+ def relative_path_from(dest, base) # :nodoc:
dest = File.expand_path(dest)
base = File.expand_path(base)
- if dest.index(base) == 0 then
+ if dest.index(base) == 0
offset = dest[base.size+1..-1]
return '.' unless offset
@@ -163,7 +163,7 @@ class Gem::RequestSet::Lockfile
end
end
- def add_PATH out, path_requests # :nodoc:
+ def add_PATH(out, path_requests) # :nodoc:
return if path_requests.empty?
out << "PATH"
@@ -178,7 +178,7 @@ class Gem::RequestSet::Lockfile
out << nil
end
- def add_PLATFORMS out # :nodoc:
+ def add_PLATFORMS(out) # :nodoc:
out << "PLATFORMS"
platforms = requests.map { |request| request.spec.platform }.uniq
@@ -223,7 +223,7 @@ class Gem::RequestSet::Lockfile
def write
content = to_s
- open "#{@gem_deps_file}.lock", 'w' do |io|
+ File.open "#{@gem_deps_file}.lock", 'w' do |io|
io.write content
end
end
diff --git a/lib/rubygems/request_set/lockfile/parser.rb b/lib/rubygems/request_set/lockfile/parser.rb
index ebea940188..0fe0405e32 100644
--- a/lib/rubygems/request_set/lockfile/parser.rb
+++ b/lib/rubygems/request_set/lockfile/parser.rb
@@ -3,7 +3,7 @@ class Gem::RequestSet::Lockfile::Parser
###
# Parses lockfiles
- def initialize tokenizer, set, platforms, filename = nil
+ def initialize(tokenizer, set, platforms, filename = nil)
@tokens = tokenizer
@filename = filename
@set = set
@@ -41,10 +41,10 @@ class Gem::RequestSet::Lockfile::Parser
##
# Gets the next token for a Lockfile
- def get expected_types = nil, expected_value = nil # :nodoc:
+ def get(expected_types = nil, expected_value = nil) # :nodoc:
token = @tokens.shift
- if expected_types and not Array(expected_types).include? token.type then
+ if expected_types and not Array(expected_types).include? token.type
unget token
message = "unexpected token [#{token.type.inspect}, #{token.value.inspect}], " +
@@ -53,7 +53,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 then
+ if expected_value and expected_value != token.value
unget token
message = "unexpected token [#{token.type.inspect}, #{token.value.inspect}], " +
@@ -93,7 +93,7 @@ class Gem::RequestSet::Lockfile::Parser
get :r_paren
- if peek[0] == :bang then
+ if peek[0] == :bang
requirements.clear
requirements << pinned_requirement(token.value)
@@ -144,7 +144,7 @@ class Gem::RequestSet::Lockfile::Parser
type = token.type
data = token.value
- if type == :text and column == 4 then
+ if type == :text and column == 4
version, platform = data.split '-', 2
platform =
@@ -183,7 +183,7 @@ class Gem::RequestSet::Lockfile::Parser
type = peek.type
value = peek.value
- if type == :entry and %w[branch ref tag].include? value then
+ if type == :entry and %w[branch ref tag].include? value
get
get :text
@@ -214,7 +214,7 @@ class Gem::RequestSet::Lockfile::Parser
type = token.type
data = token.value
- if type == :text and column == 4 then
+ if type == :text and column == 4
last_spec = set.add_git_spec name, data, repository, revision, true
else
dependency = parse_dependency name, data
@@ -261,7 +261,7 @@ class Gem::RequestSet::Lockfile::Parser
type = token.type
data = token.value
- if type == :text and column == 4 then
+ if type == :text and column == 4
last_spec = set.add_vendor_gem name, directory
else
dependency = parse_dependency name, data
@@ -294,7 +294,7 @@ class Gem::RequestSet::Lockfile::Parser
# Parses the requirements following the dependency +name+ and the +op+ for
# the first token of the requirements and returns a Gem::Dependency object.
- def parse_dependency name, op # :nodoc:
+ def parse_dependency(name, op) # :nodoc:
return Gem::Dependency.new name, op unless peek[0] == :text
version = get(:text).value
@@ -314,7 +314,7 @@ class Gem::RequestSet::Lockfile::Parser
private
- def skip type # :nodoc:
+ def skip(type) # :nodoc:
@tokens.skip type
end
@@ -325,30 +325,19 @@ class Gem::RequestSet::Lockfile::Parser
@tokens.peek
end
- if [].respond_to? :flat_map
- def pinned_requirement name # :nodoc:
- requirement = Gem::Dependency.new name
- specification = @set.sets.flat_map { |set|
- set.find_all(requirement)
- }.compact.first
+ def pinned_requirement(name) # :nodoc:
+ requirement = Gem::Dependency.new name
+ specification = @set.sets.flat_map { |set|
+ set.find_all(requirement)
+ }.compact.first
- specification && specification.version
- end
- else # FIXME: remove when 1.8 is dropped
- def pinned_requirement name # :nodoc:
- requirement = Gem::Dependency.new name
- specification = @set.sets.map { |set|
- set.find_all(requirement)
- }.flatten(1).compact.first
-
- specification && specification.version
- end
+ specification && specification.version
end
##
# Ungets the last token retrieved by #get
- def unget token # :nodoc:
+ def unget(token) # :nodoc:
@tokens.unshift token
end
end
diff --git a/lib/rubygems/request_set/lockfile/tokenizer.rb b/lib/rubygems/request_set/lockfile/tokenizer.rb
index c9f1fac75b..bb69c85fb4 100644
--- a/lib/rubygems/request_set/lockfile/tokenizer.rb
+++ b/lib/rubygems/request_set/lockfile/tokenizer.rb
@@ -1,16 +1,15 @@
# frozen_string_literal: true
-require 'strscan'
require 'rubygems/request_set/lockfile/parser'
class Gem::RequestSet::Lockfile::Tokenizer
Token = Struct.new :type, :value, :column, :line
EOF = Token.new :EOF
- def self.from_file file
+ def self.from_file(file)
new File.read(file), file
end
- def initialize input, filename = nil, line = 0, pos = 0
+ def initialize(input, filename = nil, line = 0, pos = 0)
@line = line
@line_pos = pos
@tokens = []
@@ -18,7 +17,7 @@ class Gem::RequestSet::Lockfile::Tokenizer
tokenize input
end
- def make_parser set, platforms
+ def make_parser(set, platforms)
Gem::RequestSet::Lockfile::Parser.new self, set, platforms, @filename
end
@@ -26,7 +25,7 @@ class Gem::RequestSet::Lockfile::Tokenizer
@tokens.map { |token| [token.type, token.value, token.column, token.line] }
end
- def skip type
+ def skip(type)
@tokens.shift while not @tokens.empty? and peek.type == type
end
@@ -34,7 +33,7 @@ class Gem::RequestSet::Lockfile::Tokenizer
# Calculates the column (by byte) and the line of the current token based on
# +byte_offset+.
- def token_pos byte_offset # :nodoc:
+ def token_pos(byte_offset) # :nodoc:
[byte_offset - @line_pos, @line]
end
@@ -42,7 +41,7 @@ class Gem::RequestSet::Lockfile::Tokenizer
@tokens.empty?
end
- def unshift token
+ def unshift(token)
@tokens.unshift token
end
@@ -57,7 +56,8 @@ class Gem::RequestSet::Lockfile::Tokenizer
private
- def tokenize input
+ def tokenize(input)
+ require 'strscan'
s = StringScanner.new input
until s.eos? do
@@ -65,7 +65,7 @@ class Gem::RequestSet::Lockfile::Tokenizer
pos = s.pos if leading_whitespace = s.scan(/ +/)
- if s.scan(/[<|=>]{7}/) then
+ if s.scan(/[<|=>]{7}/)
message = "your #{@filename} contains merge conflict markers"
column, line = token_pos pos
@@ -80,7 +80,7 @@ class Gem::RequestSet::Lockfile::Tokenizer
@line += 1
token
when s.scan(/[A-Z]+/) then
- if leading_whitespace then
+ if leading_whitespace
text = s.matched
text += s.scan(/[^\s)]*/).to_s # in case of no match
Token.new(:text, text, *token_pos(pos))
diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb
index 32dc769055..48f4b00d63 100644
--- a/lib/rubygems/requirement.rb
+++ b/lib/rubygems/requirement.rb
@@ -22,22 +22,22 @@ class Gem::Requirement
">=" => 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*" # :nodoc:
+ PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{Gem::Version::VERSION_PATTERN})\\s*".freeze # :nodoc:
##
# A regular expression that matches a requirement
- PATTERN = /\A#{PATTERN_RAW}\z/
+ PATTERN = /\A#{PATTERN_RAW}\z/.freeze
##
# The default requirement matches any version
- DefaultRequirement = [">=", Gem::Version.new(0)]
+ DefaultRequirement = [">=", Gem::Version.new(0)].freeze
##
# Raised when a bad requirement is encountered
@@ -51,7 +51,11 @@ class Gem::Requirement
# If the input is "weird", the default version requirement is
# returned.
- def self.create input
+ def self.create(*inputs)
+ return new inputs if inputs.length > 1
+
+ input = inputs.shift
+
case input
when Gem::Requirement then
input
@@ -60,7 +64,7 @@ class Gem::Requirement
when '!' then
source_set
else
- if input.respond_to? :to_str then
+ if input.respond_to? :to_str
new [input.to_str]
else
default
@@ -94,7 +98,7 @@ class Gem::Requirement
# parse("1.0") # => ["=", Gem::Version.new("1.0")]
# parse(Gem::Version.new("1.0")) # => ["=, Gem::Version.new("1.0")]
- def self.parse obj
+ def self.parse(obj)
return ["=", obj] if Gem::Version === obj
unless PATTERN =~ obj.to_s
@@ -120,7 +124,7 @@ class Gem::Requirement
# requirements are ignored. An empty set of +requirements+ is the
# same as <tt>">= 0"</tt>.
- def initialize *requirements
+ def initialize(*requirements)
requirements = requirements.flatten
requirements.compact!
requirements.uniq!
@@ -129,19 +133,21 @@ class Gem::Requirement
@requirements = [DefaultRequirement]
else
@requirements = requirements.map! { |r| self.class.parse r }
+ sort_requirements!
end
end
##
# Concatenates the +new+ requirements onto this requirement.
- def concat new
+ def concat(new)
new = new.flatten
new.compact!
new.uniq!
new = new.map { |r| self.class.parse r }
@requirements.concat new
+ sort_requirements!
end
##
@@ -179,11 +185,11 @@ class Gem::Requirement
end
def as_list # :nodoc:
- requirements.map { |op, version| "#{op} #{version}" }.sort
+ requirements.map { |op, version| "#{op} #{version}" }
end
def hash # :nodoc:
- requirements.sort.hash
+ requirements.hash
end
def marshal_dump # :nodoc:
@@ -192,7 +198,7 @@ class Gem::Requirement
[@requirements]
end
- def marshal_load array # :nodoc:
+ def marshal_load(array) # :nodoc:
@requirements = array[0]
fix_syck_default_key_in_requirements
@@ -207,7 +213,7 @@ class Gem::Requirement
fix_syck_default_key_in_requirements
end
- def init_with coder # :nodoc:
+ def init_with(coder) # :nodoc:
yaml_initialize coder.tag, coder.map
end
@@ -215,7 +221,7 @@ class Gem::Requirement
["@requirements"]
end
- def encode_with coder # :nodoc:
+ def encode_with(coder) # :nodoc:
coder.add 'requirements', @requirements
end
@@ -227,7 +233,7 @@ class Gem::Requirement
requirements.any? { |r| r.last.prerelease? }
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 1, 'Gem::Requirement.new(', ')' do
q.pp as_list
end
@@ -236,7 +242,7 @@ class Gem::Requirement
##
# True if +version+ satisfies this Requirement.
- def satisfied_by? version
+ def satisfied_by?(version)
raise ArgumentError, "Need a Gem::Version: #{version.inspect}" unless
Gem::Version === version
# #28965: syck has a bug with unquoted '=' YAML.loading as YAML::DefaultKey
@@ -259,8 +265,24 @@ class Gem::Requirement
as_list.join ", "
end
- def == other # :nodoc:
- Gem::Requirement === other and to_s == other.to_s
+ def ==(other) # :nodoc:
+ return unless Gem::Requirement === other
+
+ # An == check is always necessary
+ return false unless requirements == other.requirements
+
+ # An == check is sufficient unless any requirements use ~>
+ return true unless _tilde_requirements.any?
+
+ # If any requirements use ~> we use the stricter `#eql?` that also checks
+ # that version precision is the same
+ _tilde_requirements.eql?(other._tilde_requirements)
+ end
+
+ protected
+
+ def _tilde_requirements
+ requirements.select { |r| r.first == "~>" }
end
private
@@ -275,6 +297,14 @@ class Gem::Requirement
end
end
end
+
+ def sort_requirements! # :nodoc:
+ @requirements.sort! do |l, r|
+ comp = l.last <=> r.last # first, sort by the requirement's version
+ next comp unless comp == 0
+ l.first <=> r.first # then, sort by the operator (for stability)
+ end
+ end
end
class Gem::Version
diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb
index 50a547e1be..46276f3260 100644
--- a/lib/rubygems/resolver.rb
+++ b/lib/rubygems/resolver.rb
@@ -4,9 +4,6 @@ require 'rubygems/exceptions'
require 'rubygems/util'
require 'rubygems/util/list'
-require 'uri'
-require 'net/http'
-
##
# Given a set of Gem::Dependency objects as +needed+ and a way to query the
# set of available specs via +set+, calculates a set of ActivationRequest
@@ -62,7 +59,7 @@ class Gem::Resolver
# uniform manner. If one of the +sets+ is itself a ComposedSet its sets are
# flattened into the result ComposedSet.
- def self.compose_sets *sets
+ def self.compose_sets(*sets)
sets.compact!
sets = sets.map do |set|
@@ -90,7 +87,7 @@ class Gem::Resolver
# Creates a Resolver that queries only against the already installed gems
# for the +needed+ dependencies.
- def self.for_current_gems needed
+ def self.for_current_gems(needed)
new needed, Gem::Resolver::CurrentSet.new
end
@@ -102,7 +99,7 @@ class Gem::Resolver
# satisfy the Dependencies. This defaults to IndexSet, which will query
# rubygems.org.
- def initialize needed, set = nil
+ def initialize(needed, set = nil)
@set = set || Gem::Resolver::IndexSet.new
@needed = needed
@@ -115,14 +112,14 @@ class Gem::Resolver
@stats = Gem::Resolver::Stats.new
end
- def explain stage, *data # :nodoc:
+ def explain(stage, *data) # :nodoc:
return unless DEBUG_RESOLVER
d = data.map { |x| x.pretty_inspect }.join(", ")
$stderr.printf "%10s %s\n", stage.to_s.upcase, d
end
- def explain_list stage # :nodoc:
+ def explain_list(stage) # :nodoc:
return unless DEBUG_RESOLVER
data = yield
@@ -136,7 +133,7 @@ class Gem::Resolver
#
# Returns the Specification and the ActivationRequest
- def activation_request dep, possible # :nodoc:
+ def activation_request(dep, possible) # :nodoc:
spec = possible.pop
explain :activate, [spec.full_name, possible.size]
@@ -148,7 +145,7 @@ class Gem::Resolver
return spec, activation_request
end
- def requests s, act, reqs=[] # :nodoc:
+ def requests(s, act, reqs=[]) # :nodoc:
return reqs if @ignore_dependencies
s.fetch_development_dependencies if @development
@@ -174,7 +171,7 @@ class Gem::Resolver
include Molinillo::UI
def output
- @output ||= debug? ? $stdout : File.open(Gem::Util::NULL_DEVICE, 'w')
+ @output ||= debug? ? $stdout : File.open(IO::NULL, 'w')
end
def debug?
@@ -200,7 +197,7 @@ class Gem::Resolver
# Extracts the specifications that may be able to fulfill +dependency+ and
# returns those that match the local platform and all those that match.
- def find_possible dependency # :nodoc:
+ def find_possible(dependency) # :nodoc:
all = @set.find_all dependency
if (skip_dep_gems = skip_gems[dependency.name]) && !skip_dep_gems.empty?
@@ -219,7 +216,7 @@ class Gem::Resolver
##
# Returns the gems in +specs+ that match the local platform.
- def select_local_platforms specs # :nodoc:
+ def select_local_platforms(specs) # :nodoc:
specs.select do |spec|
Gem::Platform.installable? spec
end
@@ -233,8 +230,28 @@ class Gem::Resolver
exc.errors = @set.errors
raise exc
end
- possibles.sort_by { |s| [s.source, s.version, Gem::Platform.local =~ s.platform ? 1 : 0] }.
- map { |s| ActivationRequest.new s, dependency, [] }
+
+ sources = []
+
+ groups = Hash.new { |hash, key| hash[key] = [] }
+
+ # create groups & sources in the same loop
+ sources = possibles.map { |spec|
+ source = spec.source
+ groups[source] << spec
+ source
+ }.uniq.reverse
+
+ activation_requests = []
+
+ sources.each do |source|
+ groups[source].
+ sort_by { |spec| [spec.version, Gem::Platform.local =~ spec.platform ? 1 : 0] }.
+ map { |spec| ActivationRequest.new spec, dependency, [] }.
+ each { |activation_request| activation_requests << activation_request }
+ end
+
+ activation_requests
end
def dependencies_for(specification)
@@ -256,12 +273,46 @@ class Gem::Resolver
@soft_missing
end
-end
+ def sort_dependencies(dependencies, activated, conflicts)
+ dependencies.sort_by.with_index do |dependency, i|
+ name = name_for(dependency)
+ [
+ activated.vertex_named(name).payload ? 0 : 1,
+ amount_constrained(dependency),
+ conflicts[name] ? 0 : 1,
+ activated.vertex_named(name).payload ? 0 : search_for(dependency).count,
+ i # for stable sort
+ ]
+ end
+ end
-##
-# TODO remove in RubyGems 3
+ SINGLE_POSSIBILITY_CONSTRAINT_PENALTY = 1_000_000
+ private_constant :SINGLE_POSSIBILITY_CONSTRAINT_PENALTY if defined?(private_constant)
-Gem::DependencyResolver = Gem::Resolver # :nodoc:
+ # 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] ||= begin
+ name_dependency = Gem::Dependency.new(dependency.name)
+ dependency_request_for_name = Gem::Resolver::DependencyRequest.new(name_dependency, dependency.requester)
+ all = @set.find_all(dependency_request_for_name).size
+
+ if all <= 1
+ all - SINGLE_POSSIBILITY_CONSTRAINT_PENALTY
+ else
+ search = search_for(dependency).size
+ search - all
+ end
+ end
+ end
+ private :amount_constrained
+
+end
require 'rubygems/resolver/activation_request'
require 'rubygems/resolver/conflict'
diff --git a/lib/rubygems/resolver/activation_request.rb b/lib/rubygems/resolver/activation_request.rb
index 135d75d6bc..b28e1bef32 100644
--- a/lib/rubygems/resolver/activation_request.rb
+++ b/lib/rubygems/resolver/activation_request.rb
@@ -22,13 +22,13 @@ class Gem::Resolver::ActivationRequest
# +others_possible+ indicates that other specifications may also match this
# activation request.
- def initialize spec, request, others_possible = true
+ def initialize(spec, request, others_possible = true)
@spec = spec
@request = request
@others_possible = others_possible
end
- def == other # :nodoc:
+ def ==(other) # :nodoc:
case other
when Gem::Specification
@spec == other
@@ -49,7 +49,7 @@ class Gem::Resolver::ActivationRequest
##
# Downloads a gem at +path+ and returns the file path.
- def download path
+ def download(path)
Gem.ensure_gem_subdirectories path
if @spec.respond_to? :sources
@@ -97,7 +97,7 @@ class Gem::Resolver::ActivationRequest
when false then # TODO remove at RubyGems 3
nil
else
- unless @others_possible.empty? then
+ unless @others_possible.empty?
others = @others_possible.map { |s| s.full_name }
" (others possible: #{others.join ', '})"
end
@@ -152,7 +152,7 @@ class Gem::Resolver::ActivationRequest
@request.requester
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[Activation request', ']' do
q.breakable
q.pp @spec
@@ -167,7 +167,7 @@ class Gem::Resolver::ActivationRequest
q.breakable
q.text 'others possible'
else
- unless @others_possible.empty? then
+ unless @others_possible.empty?
q.breakable
q.text 'others '
q.pp @others_possible.map { |s| s.full_name }
diff --git a/lib/rubygems/resolver/api_set.rb b/lib/rubygems/resolver/api_set.rb
index ee3046af63..6fd91e3b73 100644
--- a/lib/rubygems/resolver/api_set.rb
+++ b/lib/rubygems/resolver/api_set.rb
@@ -25,7 +25,7 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
# API URL +dep_uri+ which is described at
# http://guides.rubygems.org/rubygems-org-api
- def initialize dep_uri = 'https://rubygems.org/api/v1/dependencies'
+ def initialize(dep_uri = 'https://rubygems.org/api/v1/dependencies')
super()
dep_uri = URI dep_uri unless URI === dep_uri # for ruby 1.8
@@ -43,7 +43,7 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
# Return an array of APISpecification objects matching
# DependencyRequest +req+.
- def find_all req
+ def find_all(req)
res = []
return res unless @remote
@@ -65,7 +65,7 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
# A hint run by the resolver to allow the Set to fetch
# data for DependencyRequests +reqs+.
- def prefetch reqs
+ def prefetch(reqs)
return unless @remote
names = reqs.map { |r| r.dependency.name }
needed = names - @data.keys - @to_fetch
@@ -93,7 +93,7 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
end
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[APISet', ']' do
q.breakable
q.text "URI: #{@dep_uri}"
@@ -107,7 +107,7 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
##
# Return data for all versions of the gem +name+.
- def versions name # :nodoc:
+ def versions(name) # :nodoc:
if @data.key?(name)
return @data[name]
end
@@ -123,4 +123,3 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
end
end
-
diff --git a/lib/rubygems/resolver/api_specification.rb b/lib/rubygems/resolver/api_specification.rb
index 1e22dd0b6f..9bbc095788 100644
--- a/lib/rubygems/resolver/api_specification.rb
+++ b/lib/rubygems/resolver/api_specification.rb
@@ -21,12 +21,13 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
@name = api_data[:name]
@version = Gem::Version.new api_data[:number]
@platform = Gem::Platform.new api_data[:platform]
+ @original_platform = api_data[:platform]
@dependencies = api_data[:dependencies].map do |name, ver|
Gem::Dependency.new name, ver.split(/\s*,\s*/)
end
end
- def == other # :nodoc:
+ def ==(other) # :nodoc:
self.class === other and
@set == other.set and
@name == other.name and
@@ -45,7 +46,7 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
Gem::Platform.match @platform
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[APISpecification', ']' do
q.breakable
q.text "name: #{name}"
@@ -73,7 +74,11 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
@spec ||=
begin
tuple = Gem::NameTuple.new @name, @version, @platform
+ source.fetch_spec tuple
+ rescue Gem::RemoteFetcher::FetchError
+ raise if @original_platform == @platform
+ tuple = Gem::NameTuple.new @name, @version, @original_platform
source.fetch_spec tuple
end
end
@@ -83,4 +88,3 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
end
end
-
diff --git a/lib/rubygems/resolver/best_set.rb b/lib/rubygems/resolver/best_set.rb
index 4479535abe..cc91b65c0b 100644
--- a/lib/rubygems/resolver/best_set.rb
+++ b/lib/rubygems/resolver/best_set.rb
@@ -10,7 +10,7 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
# Creates a BestSet for the given +sources+ or Gem::sources if none are
# specified. +sources+ must be a Gem::SourceList.
- def initialize sources = Gem.sources
+ def initialize(sources = Gem.sources)
super()
@sources = sources
@@ -25,7 +25,7 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
end
end
- def find_all req # :nodoc:
+ def find_all(req) # :nodoc:
pick_sets if @remote and @sets.empty?
super
@@ -35,13 +35,13 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
retry
end
- def prefetch reqs # :nodoc:
+ def prefetch(reqs) # :nodoc:
pick_sets if @remote and @sets.empty?
super
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[BestSet', ']' do
q.breakable
q.text 'sets:'
@@ -58,7 +58,7 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
#
# The calling method must retry the exception to repeat the lookup.
- def replace_failed_api_set error # :nodoc:
+ def replace_failed_api_set(error) # :nodoc:
uri = error.uri
uri = URI uri unless URI === uri
uri.query = nil
@@ -76,4 +76,3 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
end
end
-
diff --git a/lib/rubygems/resolver/composed_set.rb b/lib/rubygems/resolver/composed_set.rb
index 0b65942dca..4baac9c75b 100644
--- a/lib/rubygems/resolver/composed_set.rb
+++ b/lib/rubygems/resolver/composed_set.rb
@@ -16,7 +16,7 @@ class Gem::Resolver::ComposedSet < Gem::Resolver::Set
# Creates a new ComposedSet containing +sets+. Use
# Gem::Resolver::compose_sets instead.
- def initialize *sets
+ def initialize(*sets)
super()
@sets = sets
@@ -26,7 +26,7 @@ class Gem::Resolver::ComposedSet < Gem::Resolver::Set
# When +allow_prerelease+ is set to +true+ prereleases gems are allowed to
# match dependencies.
- def prerelease= allow_prerelease
+ def prerelease=(allow_prerelease)
super
sets.each do |set|
@@ -37,7 +37,7 @@ class Gem::Resolver::ComposedSet < Gem::Resolver::Set
##
# Sets the remote network access for all composed sets.
- def remote= remote
+ def remote=(remote)
super
@sets.each { |set| set.remote = remote }
@@ -50,7 +50,7 @@ class Gem::Resolver::ComposedSet < Gem::Resolver::Set
##
# Finds all specs matching +req+ in all sets.
- def find_all req
+ def find_all(req)
@sets.map do |s|
s.find_all req
end.flatten
@@ -59,9 +59,8 @@ class Gem::Resolver::ComposedSet < Gem::Resolver::Set
##
# Prefetches +reqs+ in all sets.
- def prefetch reqs
+ def prefetch(reqs)
@sets.each { |s| s.prefetch(reqs) }
end
end
-
diff --git a/lib/rubygems/resolver/conflict.rb b/lib/rubygems/resolver/conflict.rb
index 7997f92950..fb1e661b21 100644
--- a/lib/rubygems/resolver/conflict.rb
+++ b/lib/rubygems/resolver/conflict.rb
@@ -27,7 +27,7 @@ class Gem::Resolver::Conflict
@failed_dep = failed_dep
end
- def == other # :nodoc:
+ def ==(other) # :nodoc:
self.class === other and
@dependency == other.dependency and
@activated == other.activated and
@@ -57,7 +57,7 @@ class Gem::Resolver::Conflict
requirement = dependency.requirement
alternates = dependency.matching_specs.map { |spec| spec.full_name }
- unless alternates.empty? then
+ unless alternates.empty?
matching = <<-MATCHING.chomp
Gems matching %s:
@@ -97,7 +97,7 @@ class Gem::Resolver::Conflict
@dependency.name == spec.name
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[Dependency conflict: ', ']' do
q.breakable
@@ -109,7 +109,7 @@ class Gem::Resolver::Conflict
q.pp @dependency
q.breakable
- if @dependency == @failed_dep then
+ if @dependency == @failed_dep
q.text ' failed'
else
q.text ' failed dependency '
@@ -121,7 +121,7 @@ class Gem::Resolver::Conflict
##
# Path of activations from the +current+ list.
- def request_path current
+ def request_path(current)
path = []
while current do
diff --git a/lib/rubygems/resolver/current_set.rb b/lib/rubygems/resolver/current_set.rb
index 265c639f15..d60e46389d 100644
--- a/lib/rubygems/resolver/current_set.rb
+++ b/lib/rubygems/resolver/current_set.rb
@@ -6,9 +6,8 @@
class Gem::Resolver::CurrentSet < Gem::Resolver::Set
- def find_all req
+ def find_all(req)
req.dependency.matching_specs
end
end
-
diff --git a/lib/rubygems/resolver/dependency_request.rb b/lib/rubygems/resolver/dependency_request.rb
index c2918911cd..1984aa9ddc 100644
--- a/lib/rubygems/resolver/dependency_request.rb
+++ b/lib/rubygems/resolver/dependency_request.rb
@@ -19,12 +19,12 @@ class Gem::Resolver::DependencyRequest
# Creates a new DependencyRequest for +dependency+ from +requester+.
# +requester may be nil if the request came from a user.
- def initialize dependency, requester
+ def initialize(dependency, requester)
@dependency = dependency
@requester = requester
end
- def == other # :nodoc:
+ def ==(other) # :nodoc:
case other
when Gem::Dependency
@dependency == other
@@ -48,7 +48,7 @@ class Gem::Resolver::DependencyRequest
# NOTE: #match? only matches prerelease versions when #dependency is a
# prerelease dependency.
- def match? spec, allow_prerelease = false
+ def match?(spec, allow_prerelease = false)
@dependency.match? spec, nil, allow_prerelease
end
@@ -95,7 +95,7 @@ class Gem::Resolver::DependencyRequest
@requester ? @requester.request : "(unknown)"
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[Dependency request ', ']' do
q.breakable
q.text @dependency.to_s
diff --git a/lib/rubygems/resolver/git_set.rb b/lib/rubygems/resolver/git_set.rb
index 723a202d7a..6340b92fae 100644
--- a/lib/rubygems/resolver/git_set.rb
+++ b/lib/rubygems/resolver/git_set.rb
@@ -43,7 +43,7 @@ class Gem::Resolver::GitSet < Gem::Resolver::Set
@specs = {}
end
- def add_git_gem name, repository, reference, submodules # :nodoc:
+ def add_git_gem(name, repository, reference, submodules) # :nodoc:
@repositories[name] = [repository, reference]
@need_submodules[repository] = submodules
end
@@ -56,7 +56,7 @@ class Gem::Resolver::GitSet < Gem::Resolver::Set
# This fills in the prefetch information as enough information about the gem
# is present in the arguments.
- def add_git_spec name, version, repository, reference, submodules # :nodoc:
+ def add_git_spec(name, version, repository, reference, submodules) # :nodoc:
add_git_gem name, repository, reference, submodules
source = Gem::Source::Git.new name, repository, reference
@@ -77,7 +77,7 @@ class Gem::Resolver::GitSet < Gem::Resolver::Set
##
# Finds all git gems matching +req+
- def find_all req
+ def find_all(req)
prefetch nil
specs.values.select do |spec|
@@ -88,7 +88,7 @@ class Gem::Resolver::GitSet < Gem::Resolver::Set
##
# Prefetches specifications from the git repositories in this set.
- def prefetch reqs
+ def prefetch(reqs)
return unless @specs.empty?
@repositories.each do |name, (repository, reference)|
@@ -104,7 +104,7 @@ class Gem::Resolver::GitSet < Gem::Resolver::Set
end
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[GitSet', ']' do
next if @repositories.empty?
q.breakable
@@ -120,4 +120,3 @@ class Gem::Resolver::GitSet < Gem::Resolver::Set
end
end
-
diff --git a/lib/rubygems/resolver/git_specification.rb b/lib/rubygems/resolver/git_specification.rb
index 2448797d3f..f43cfba853 100644
--- a/lib/rubygems/resolver/git_specification.rb
+++ b/lib/rubygems/resolver/git_specification.rb
@@ -6,14 +6,14 @@
class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification
- def == other # :nodoc:
+ def ==(other) # :nodoc:
self.class === other and
@set == other.set and
@spec == other.spec and
@source == other.source
end
- def add_dependency dependency # :nodoc:
+ def add_dependency(dependency) # :nodoc:
spec.dependencies << dependency
end
@@ -21,7 +21,7 @@ class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification
# Installing a git gem only involves building the extensions and generating
# the executables.
- def install options = {}
+ def install(options = {})
require 'rubygems/installer'
installer = Gem::Installer.for_spec spec, options
@@ -35,7 +35,7 @@ class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification
installer.run_post_install_hooks
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[GitSpecification', ']' do
q.breakable
q.text "name: #{name}"
@@ -56,4 +56,3 @@ class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification
end
end
-
diff --git a/lib/rubygems/resolver/index_set.rb b/lib/rubygems/resolver/index_set.rb
index 2450f14b4f..e32e1fa5ba 100644
--- a/lib/rubygems/resolver/index_set.rb
+++ b/lib/rubygems/resolver/index_set.rb
@@ -5,11 +5,11 @@
class Gem::Resolver::IndexSet < Gem::Resolver::Set
- def initialize source = nil # :nodoc:
+ def initialize(source = nil) # :nodoc:
super()
@f =
- if source then
+ if source
sources = Gem::SourceList.from [source]
Gem::SpecFetcher.new sources
@@ -36,7 +36,7 @@ class Gem::Resolver::IndexSet < Gem::Resolver::Set
# Return an array of IndexSpecification objects matching
# DependencyRequest +req+.
- def find_all req
+ def find_all(req)
res = []
return res unless @remote
@@ -44,7 +44,7 @@ class Gem::Resolver::IndexSet < Gem::Resolver::Set
name = req.dependency.name
@all[name].each do |uri, n|
- if req.match? n, @prerelease then
+ if req.match? n, @prerelease
res << Gem::Resolver::IndexSpecification.new(
self, n.name, n.version, uri, n.platform)
end
@@ -53,7 +53,7 @@ class Gem::Resolver::IndexSet < Gem::Resolver::Set
res
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[IndexSet', ']' do
q.breakable
q.text 'sources:'
@@ -78,4 +78,3 @@ class Gem::Resolver::IndexSet < Gem::Resolver::Set
end
end
-
diff --git a/lib/rubygems/resolver/index_specification.rb b/lib/rubygems/resolver/index_specification.rb
index 4340f46943..ed9423791c 100644
--- a/lib/rubygems/resolver/index_specification.rb
+++ b/lib/rubygems/resolver/index_specification.rb
@@ -15,7 +15,7 @@ class Gem::Resolver::IndexSpecification < Gem::Resolver::Specification
# The +name+, +version+ and +platform+ are the name, version and platform of
# the gem.
- def initialize set, name, version, source, platform
+ def initialize(set, name, version, source, platform)
super()
@set = set
@@ -38,12 +38,12 @@ class Gem::Resolver::IndexSpecification < Gem::Resolver::Specification
'#<%s %s source %s>' % [self.class, full_name, @source]
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[Index specification', ']' do
q.breakable
q.text full_name
- unless Gem::Platform::RUBY == @platform then
+ unless Gem::Platform::RUBY == @platform
q.breakable
q.text @platform.to_s
end
@@ -67,4 +67,3 @@ class Gem::Resolver::IndexSpecification < Gem::Resolver::Specification
end
end
-
diff --git a/lib/rubygems/resolver/installed_specification.rb b/lib/rubygems/resolver/installed_specification.rb
index d9c6a5e5cf..9d996fc1da 100644
--- a/lib/rubygems/resolver/installed_specification.rb
+++ b/lib/rubygems/resolver/installed_specification.rb
@@ -5,7 +5,7 @@
class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification
- def == other # :nodoc:
+ def ==(other) # :nodoc:
self.class === other and
@set == other.set and
@spec == other.spec
@@ -15,7 +15,7 @@ class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification
# This is a null install as this specification is already installed.
# +options+ are ignored.
- def install options = {}
+ def install(options = {})
yield nil
end
@@ -30,7 +30,7 @@ class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification
super
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[InstalledSpecification', ']' do
q.breakable
q.text "name: #{name}"
@@ -56,4 +56,3 @@ class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification
end
end
-
diff --git a/lib/rubygems/resolver/installer_set.rb b/lib/rubygems/resolver/installer_set.rb
index 07fffeb150..f3827ad4e9 100644
--- a/lib/rubygems/resolver/installer_set.rb
+++ b/lib/rubygems/resolver/installer_set.rb
@@ -29,7 +29,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
##
# Creates a new InstallerSet that will look for gems in +domain+.
- def initialize domain
+ def initialize(domain)
super()
@domain = domain
@@ -41,6 +41,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
@ignore_dependencies = false
@ignore_installed = false
@local = {}
+ @local_source = Gem::Source::Local.new
@remote_set = Gem::Resolver::BestSet.new
@specs = {}
end
@@ -49,7 +50,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
# Looks up the latest specification for +dependency+ and adds it to the
# always_install list.
- def add_always_install dependency
+ def add_always_install(dependency)
request = Gem::Resolver::DependencyRequest.new dependency, nil
found = find_all request
@@ -64,7 +65,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
Gem::Platform.local === s.platform
end
- if found.empty? then
+ if found.empty?
exc = Gem::UnsatisfiableDependencyError.new request
exc.errors = errors
@@ -82,7 +83,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
# Adds a local gem requested using +dep_name+ with the given +spec+ that can
# be loaded and installed using the +source+.
- def add_local dep_name, spec, source
+ def add_local(dep_name, spec, source)
@local[dep_name] = [spec, source]
end
@@ -111,7 +112,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
# Returns an array of IndexSpecification objects matching DependencyRequest
# +req+.
- def find_all req
+ def find_all(req)
res = []
dep = req.dependency
@@ -127,7 +128,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
res << Gem::Resolver::InstalledSpecification.new(self, gemspec)
end unless @ignore_installed
- if consider_local? then
+ if consider_local?
matching_local = @local.values.select do |spec, _|
req.match? spec
end.map do |spec, source|
@@ -136,13 +137,11 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
res.concat matching_local
- local_source = Gem::Source::Local.new
-
begin
- if local_spec = local_source.find_gem(name, dep.requirement) then
+ if local_spec = @local_source.find_gem(name, dep.requirement)
res << Gem::Resolver::IndexSpecification.new(
self, local_spec.name, local_spec.version,
- local_source, local_spec.platform)
+ @local_source, local_spec.platform)
end
rescue Gem::Package::FormatError
# ignore
@@ -162,7 +161,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
@remote_set.prefetch(reqs) if consider_remote?
end
- def prerelease= allow_prerelease
+ def prerelease=(allow_prerelease)
super
@remote_set.prerelease = allow_prerelease
@@ -180,7 +179,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
# Called from IndexSpecification to get a true Specification
# object.
- def load_spec name, ver, platform, source # :nodoc:
+ def load_spec(name, ver, platform, source) # :nodoc:
key = "#{name}-#{ver}-#{platform}"
@specs.fetch key do
@@ -193,13 +192,13 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
##
# Has a local gem for +dep_name+ been added to this set?
- def local? dep_name # :nodoc:
- spec, = @local[dep_name]
+ def local?(dep_name) # :nodoc:
+ spec, _ = @local[dep_name]
spec
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[InstallerSet', ']' do
q.breakable
q.text "domain: #{@domain}"
@@ -214,7 +213,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
end
end
- def remote= remote # :nodoc:
+ def remote=(remote) # :nodoc:
case @domain
when :local then
@domain = :both if remote
@@ -226,4 +225,3 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
end
end
-
diff --git a/lib/rubygems/resolver/local_specification.rb b/lib/rubygems/resolver/local_specification.rb
index 1d9d22f0ac..7418cfcc86 100644
--- a/lib/rubygems/resolver/local_specification.rb
+++ b/lib/rubygems/resolver/local_specification.rb
@@ -17,7 +17,7 @@ class Gem::Resolver::LocalSpecification < Gem::Resolver::SpecSpecification
true
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[LocalSpecification', ']' do
q.breakable
q.text "name: #{name}"
@@ -39,4 +39,3 @@ class Gem::Resolver::LocalSpecification < Gem::Resolver::SpecSpecification
end
end
-
diff --git a/lib/rubygems/resolver/lock_set.rb b/lib/rubygems/resolver/lock_set.rb
index 7fddc93e1c..4002a963a4 100644
--- a/lib/rubygems/resolver/lock_set.rb
+++ b/lib/rubygems/resolver/lock_set.rb
@@ -9,7 +9,7 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set
##
# Creates a new LockSet from the given +sources+
- def initialize sources
+ def initialize(sources)
super()
@sources = sources.map do |source|
@@ -26,7 +26,7 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set
# The specification's set will be the current set, and the source will be
# the current set's source.
- def add name, version, platform # :nodoc:
+ def add(name, version, platform) # :nodoc:
version = Gem::Version.new version
specs = [
Gem::Resolver::LockSpecification.new(self, name, version, @sources, platform)
@@ -41,7 +41,7 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set
# Returns an Array of IndexSpecification objects matching the
# DependencyRequest +req+.
- def find_all req
+ def find_all(req)
@specs.select do |spec|
req.match? spec
end
@@ -51,7 +51,7 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set
# Loads a Gem::Specification with the given +name+, +version+ and
# +platform+. +source+ is ignored.
- def load_spec name, version, platform, source # :nodoc:
+ def load_spec(name, version, platform, source) # :nodoc:
dep = Gem::Dependency.new name, version
found = @specs.find do |spec|
@@ -63,7 +63,7 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set
found.source.fetch_spec tuple
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[LockSet', ']' do
q.breakable
q.text 'source:'
@@ -80,4 +80,3 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set
end
end
-
diff --git a/lib/rubygems/resolver/lock_specification.rb b/lib/rubygems/resolver/lock_specification.rb
index f485675673..e29b567de4 100644
--- a/lib/rubygems/resolver/lock_specification.rb
+++ b/lib/rubygems/resolver/lock_specification.rb
@@ -9,7 +9,7 @@ class Gem::Resolver::LockSpecification < Gem::Resolver::Specification
attr_reader :sources
- def initialize set, name, version, sources, platform
+ def initialize(set, name, version, sources, platform)
super()
@name = name
@@ -27,10 +27,10 @@ class Gem::Resolver::LockSpecification < Gem::Resolver::Specification
# This is a null install as a locked specification is considered installed.
# +options+ are ignored.
- def install options = {}
+ def install(options = {})
destination = options[:install_dir] || Gem.dir
- if File.exist? File.join(destination, 'specifications', spec.spec_name) then
+ if File.exist? File.join(destination, 'specifications', spec.spec_name)
yield nil
return
end
@@ -41,11 +41,11 @@ class Gem::Resolver::LockSpecification < Gem::Resolver::Specification
##
# Adds +dependency+ from the lockfile to this specification
- def add_dependency dependency # :nodoc:
+ def add_dependency(dependency) # :nodoc:
@dependencies << dependency
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[LockSpecification', ']' do
q.breakable
q.text "name: #{@name}"
@@ -53,12 +53,12 @@ class Gem::Resolver::LockSpecification < Gem::Resolver::Specification
q.breakable
q.text "version: #{@version}"
- unless @platform == Gem::Platform::RUBY then
+ unless @platform == Gem::Platform::RUBY
q.breakable
q.text "platform: #{@platform}"
end
- unless @dependencies.empty? then
+ unless @dependencies.empty?
q.breakable
q.text 'dependencies:'
q.breakable
@@ -85,4 +85,3 @@ class Gem::Resolver::LockSpecification < Gem::Resolver::Specification
end
end
-
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
index 40136eb894..b413e3ab6a 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
@@ -98,18 +98,27 @@ module Gem::Resolver::Molinillo
"#{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
+ 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|
- dot_edges << " #{e.origin.name} -> #{e.destination.name} [label=\"#{e.requirement}\"]"
+ 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
@@ -119,10 +128,12 @@ module Gem::Resolver::Molinillo
# {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 other_vertex.successors.map(&:name).to_set == vertex.successors.map(&:name).to_set
+ return false unless vertex.payload == other_vertex.payload
+ return false unless other_vertex.successors.to_set == vertex.successors.to_set
end
end
@@ -134,6 +145,7 @@ module Gem::Resolver::Molinillo
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_node = vertex_named(parent_name)
add_edge(parent_node, vertex, requirement)
@@ -152,7 +164,7 @@ module Gem::Resolver::Molinillo
# 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 [void]
+ # @return [Array<Vertex>] the vertices which have been detached
def detach_vertex_named(name)
log.detach_vertex_named(self, name)
end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
index b052e3a38e..e994e59d05 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
@@ -23,8 +23,8 @@ module Gem::Resolver::Molinillo
# (see Action#down)
def down(graph)
edge = make_edge(graph)
- edge.origin.outgoing_edges.delete(edge)
- edge.destination.incoming_edges.delete(edge)
+ delete_first(edge.origin.outgoing_edges, edge)
+ delete_first(edge.destination.incoming_edges, edge)
end
# @!group AddEdgeNoCircular
@@ -53,6 +53,13 @@ module Gem::Resolver::Molinillo
@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/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
index 9ad34e8fe0..fa03e2d365 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
@@ -14,16 +14,23 @@ module Gem::Resolver::Molinillo
# (see Action#up)
def up(graph)
- return unless @vertex = graph.vertices.delete(name)
+ return [] unless @vertex = graph.vertices.delete(name)
+
+ removed_vertices = [@vertex]
@vertex.outgoing_edges.each do |e|
v = e.destination
v.incoming_edges.delete(e)
- graph.detach_vertex_named(v.name) unless v.root? || v.predecessors.any?
+ 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)
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb
index 88d109c94f..cebd9cafdd 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb
@@ -10,7 +10,7 @@ module Gem::Resolver::Molinillo
# @return [Object] the payload the vertex holds
attr_accessor :payload
- # @return [Arrary<Object>] the explicit requirements that required
+ # @return [Array<Object>] the explicit requirements that required
# this vertex
attr_reader :explicit_requirements
@@ -81,6 +81,7 @@ module Gem::Resolver::Molinillo
# @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
@@ -89,6 +90,7 @@ module Gem::Resolver::Molinillo
# @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
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb
index c50fa77bd5..c5b5bd729f 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module Gem::Resolver::Molinillo
# The version of Gem::Resolver::Molinillo.
- VERSION = '0.5.3'.freeze
+ VERSION = '0.5.7'.freeze
end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb
index 540b5b809c..dbc4e000e4 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb
@@ -48,7 +48,7 @@ module Gem::Resolver::Molinillo
if debug?
debug_info = yield
debug_info = debug_info.inspect unless debug_info.is_a?(String)
- output.puts debug_info.split("\n").map { |s| ' ' * depth + s }
+ output.puts debug_info.split("\n").map { |s| ' ' * depth + s }
end
end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb
index 54a6c3fc17..73a4242157 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb
@@ -52,7 +52,7 @@ module Gem::Resolver::Molinillo
@base = base
@states = []
@iteration_counter = 0
- @parent_of = {}
+ @parents_of = Hash.new { |h, k| h[k] = [] }
end
# Resolves the {#original_requested} dependencies into a full dependency
@@ -105,7 +105,7 @@ module Gem::Resolver::Molinillo
handle_missing_or_push_dependency_state(initial_state)
- debug { "Starting resolution (#{@started_at})" }
+ debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" }
resolver_ui.before_resolution
end
@@ -178,14 +178,14 @@ module Gem::Resolver::Molinillo
# Unwinds the states stack because a conflict has been encountered
# @return [void]
def unwind_for_conflict
- debug(depth) { "Unwinding for conflict: #{requirement}" }
+ debug(depth) { "Unwinding for conflict: #{requirement} to #{state_index_for_unwind / 2}" }
conflicts.tap do |c|
sliced_states = states.slice!((state_index_for_unwind + 1)..-1)
raise VersionConflict.new(c) unless state
activated.rewind_to(sliced_states.first || :initial_state) if sliced_states
state.conflicts = c
index = states.size - 1
- @parent_of.reject! { |_, i| i >= index }
+ @parents_of.each { |_, a| a.reject! { |i| i >= index } }
end
end
@@ -194,25 +194,27 @@ module Gem::Resolver::Molinillo
def state_index_for_unwind
current_requirement = requirement
existing_requirement = requirement_for_existing_name(name)
- until current_requirement.nil?
- current_state = find_state_for(current_requirement)
- return states.index(current_state) if state_any?(current_state)
- current_requirement = parent_of(current_requirement)
+ index = -1
+ [current_requirement, existing_requirement].each do |r|
+ until r.nil?
+ current_state = find_state_for(r)
+ if state_any?(current_state)
+ current_index = states.index(current_state)
+ index = current_index if current_index > index
+ break
+ end
+ r = parent_of(r)
+ end
end
- until existing_requirement.nil?
- existing_state = find_state_for(existing_requirement)
- return states.index(existing_state) if state_any?(existing_state)
- existing_requirement = parent_of(existing_requirement)
- end
- -1
+ index
end
# @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 = @parent_of[requirement]
+ return unless index = @parents_of[requirement].last
return unless parent_state = @states[index]
parent_state.requirement
end
@@ -354,29 +356,41 @@ module Gem::Resolver::Molinillo
# Ensures there are no orphaned successors to the given {vertex}.
# @param [DependencyGraph::Vertex] vertex the vertex to fix up.
# @return [void]
- def fixup_swapped_children(vertex)
+ def fixup_swapped_children(vertex) # rubocop:disable Metrics/CyclomaticComplexity
payload = vertex.payload
deps = dependencies_for(payload).group_by(&method(:name_for))
vertex.outgoing_edges.each do |outgoing_edge|
- @parent_of[outgoing_edge.requirement] = states.size - 1
+ requirement = outgoing_edge.requirement
+ parent_index = @parents_of[requirement].last
succ = outgoing_edge.destination
matching_deps = Array(deps[succ.name])
+ dep_matched = matching_deps.include?(requirement)
+
+ # only push the current index when it was originally required by the
+ # same named spec
+ if parent_index && states[parent_index].name == name
+ @parents_of[requirement].push(states.size - 1)
+ end
+
if matching_deps.empty? && !succ.root? && succ.predecessors.to_a == [vertex]
debug(depth) { "Removing orphaned spec #{succ.name} after swapping #{name}" }
- succ.requirements.each { |r| @parent_of.delete(r) }
- activated.detach_vertex_named(succ.name)
+ succ.requirements.each { |r| @parents_of.delete(r) }
- all_successor_names = succ.recursive_successors.map(&:name)
-
- requirements.delete_if do |requirement|
- requirement_name = name_for(requirement)
- (requirement_name == succ.name) || all_successor_names.include?(requirement_name)
+ removed_names = activated.detach_vertex_named(succ.name).map(&:name)
+ requirements.delete_if do |r|
+ # the only removed vertices are those with no other requirements,
+ # so it's safe to delete only based upon name here
+ removed_names.include?(name_for(r))
end
- elsif !matching_deps.include?(outgoing_edge.requirement)
+ elsif !dep_matched
+ debug(depth) { "Removing orphaned dependency #{requirement} after swapping #{name}" }
+ # also reset if we're removing the edge, but only if its parent has
+ # already been fixed up
+ @parents_of[requirement].push(states.size - 1) if @parents_of[requirement].empty?
+
activated.delete_edge(outgoing_edge)
- requirements.delete(outgoing_edge.requirement)
+ requirements.delete(requirement)
end
- matching_deps.delete(outgoing_edge.requirement)
end
end
@@ -395,13 +409,18 @@ module Gem::Resolver::Molinillo
# @return [Boolean] whether the current spec is satisfied as a new
# possibility.
def new_spec_satisfied?
+ unless requirement_satisfied_by?(requirement, activated, possibility)
+ debug(depth) { 'Unsatisfied by requested spec' }
+ return false
+ end
+
locked_requirement = locked_requirement_named(name)
- requested_spec_satisfied = requirement_satisfied_by?(requirement, activated, possibility)
+
locked_spec_satisfied = !locked_requirement ||
requirement_satisfied_by?(locked_requirement, activated, possibility)
- debug(depth) { 'Unsatisfied by requested spec' } unless requested_spec_satisfied
debug(depth) { 'Unsatisfied by locked spec' } unless locked_spec_satisfied
- requested_spec_satisfied && locked_spec_satisfied
+
+ locked_spec_satisfied
end
# @param [String] requirement_name the spec name to search for
@@ -417,7 +436,7 @@ module Gem::Resolver::Molinillo
# @return [void]
def activate_spec
conflicts.delete(name)
- debug(depth) { 'Activated ' + name + ' at ' + possibility.to_s }
+ debug(depth) { "Activated #{name} at #{possibility}" }
activated.set_payload(name, possibility)
require_nested_dependencies_for(possibility)
end
@@ -432,7 +451,8 @@ module Gem::Resolver::Molinillo
nested_dependencies.each do |d|
activated.add_child_vertex(name_for(d), nil, [name_for(activated_spec)], d)
parent_index = states.size - 1
- @parent_of[d] ||= parent_index
+ parents = @parents_of[d]
+ parents << parent_index if parents.empty?
end
push_state_for_requirements(requirements + nested_dependencies, !nested_dependencies.empty?)
diff --git a/lib/rubygems/resolver/requirement_list.rb b/lib/rubygems/resolver/requirement_list.rb
index 2768c80170..98d086e63c 100644
--- a/lib/rubygems/resolver/requirement_list.rb
+++ b/lib/rubygems/resolver/requirement_list.rb
@@ -18,7 +18,7 @@ class Gem::Resolver::RequirementList
@list = []
end
- def initialize_copy other # :nodoc:
+ def initialize_copy(other) # :nodoc:
@exact = @exact.dup
@list = @list.dup
end
diff --git a/lib/rubygems/resolver/set.rb b/lib/rubygems/resolver/set.rb
index cc12633d46..242f9cd3dc 100644
--- a/lib/rubygems/resolver/set.rb
+++ b/lib/rubygems/resolver/set.rb
@@ -21,6 +21,7 @@ class Gem::Resolver::Set
attr_accessor :prerelease
def initialize # :nodoc:
+ require 'uri'
@prerelease = false
@remote = true
@errors = []
@@ -30,7 +31,7 @@ class Gem::Resolver::Set
# The find_all method must be implemented. It returns all Resolver
# Specification objects matching the given DependencyRequest +req+.
- def find_all req
+ def find_all(req)
raise NotImplementedError
end
@@ -42,7 +43,7 @@ class Gem::Resolver::Set
# When overridden, the #prefetch method should look up specifications
# matching +reqs+.
- def prefetch reqs
+ def prefetch(reqs)
end
##
@@ -54,4 +55,3 @@ class Gem::Resolver::Set
end
end
-
diff --git a/lib/rubygems/resolver/source_set.rb b/lib/rubygems/resolver/source_set.rb
index 66f5963e54..8e799514fd 100644
--- a/lib/rubygems/resolver/source_set.rb
+++ b/lib/rubygems/resolver/source_set.rb
@@ -16,7 +16,7 @@ class Gem::Resolver::SourceSet < Gem::Resolver::Set
@sets = {}
end
- def find_all req # :nodoc:
+ def find_all(req) # :nodoc:
if set = get_set(req.dependency.name)
set.find_all req
else
@@ -25,7 +25,7 @@ class Gem::Resolver::SourceSet < Gem::Resolver::Set
end
# potentially no-op
- def prefetch reqs # :nodoc:
+ def prefetch(reqs) # :nodoc:
reqs.each do |req|
if set = get_set(req.dependency.name)
set.prefetch reqs
@@ -33,11 +33,11 @@ class Gem::Resolver::SourceSet < Gem::Resolver::Set
end
end
- def add_source_gem name, source
+ def add_source_gem(name, source)
@links[name] = source
end
-private
+ private
def get_set(name)
link = @links[name]
@@ -45,4 +45,3 @@ private
end
end
-
diff --git a/lib/rubygems/resolver/spec_specification.rb b/lib/rubygems/resolver/spec_specification.rb
index 35ee8cc247..d0e744f3a7 100644
--- a/lib/rubygems/resolver/spec_specification.rb
+++ b/lib/rubygems/resolver/spec_specification.rb
@@ -10,7 +10,7 @@ class Gem::Resolver::SpecSpecification < Gem::Resolver::Specification
# +spec+. The +source+ is either where the +spec+ came from, or should be
# loaded from.
- def initialize set, spec, source = nil
+ def initialize(set, spec, source = nil)
@set = set
@source = source
@spec = spec
@@ -54,4 +54,3 @@ class Gem::Resolver::SpecSpecification < Gem::Resolver::Specification
end
end
-
diff --git a/lib/rubygems/resolver/specification.rb b/lib/rubygems/resolver/specification.rb
index 44989d39ae..7c1e9be702 100644
--- a/lib/rubygems/resolver/specification.rb
+++ b/lib/rubygems/resolver/specification.rb
@@ -81,14 +81,10 @@ class Gem::Resolver::Specification
# After installation #spec is updated to point to the just-installed
# specification.
- def install options = {}
+ def install(options = {})
require 'rubygems/installer'
- destination = options[:install_dir] || Gem.dir
-
- Gem.ensure_gem_subdirectories destination
-
- gem = source.download spec, destination
+ gem = download options
installer = Gem::Installer.at gem, options
@@ -97,6 +93,14 @@ class Gem::Resolver::Specification
@spec = installer.install
end
+ def download(options)
+ dir = options[:install_dir] || Gem.dir
+
+ Gem.ensure_gem_subdirectories dir
+
+ source.download spec, dir
+ end
+
##
# Returns true if this specification is installable on this platform.
@@ -108,4 +112,3 @@ class Gem::Resolver::Specification
false
end
end
-
diff --git a/lib/rubygems/resolver/stats.rb b/lib/rubygems/resolver/stats.rb
index 3b95efebf7..64b458f504 100644
--- a/lib/rubygems/resolver/stats.rb
+++ b/lib/rubygems/resolver/stats.rb
@@ -32,7 +32,7 @@ class Gem::Resolver::Stats
@iterations += 1
end
- PATTERN = "%20s: %d\n"
+ PATTERN = "%20s: %d\n".freeze
def display
$stdout.puts "=== Resolver Statistics ==="
diff --git a/lib/rubygems/resolver/vendor_set.rb b/lib/rubygems/resolver/vendor_set.rb
index f30ce534af..7e2e917d5c 100644
--- a/lib/rubygems/resolver/vendor_set.rb
+++ b/lib/rubygems/resolver/vendor_set.rb
@@ -32,7 +32,7 @@ class Gem::Resolver::VendorSet < Gem::Resolver::Set
# Adds a specification to the set with the given +name+ which has been
# unpacked into the given +directory+.
- def add_vendor_gem name, directory # :nodoc:
+ def add_vendor_gem(name, directory) # :nodoc:
gemspec = File.join directory, "#{name}.gemspec"
spec = Gem::Specification.load gemspec
@@ -52,7 +52,7 @@ class Gem::Resolver::VendorSet < Gem::Resolver::Set
# Returns an Array of VendorSpecification objects matching the
# DependencyRequest +req+.
- def find_all req
+ def find_all(req)
@specs.values.select do |spec|
req.match? spec
end.map do |spec|
@@ -65,11 +65,11 @@ class Gem::Resolver::VendorSet < Gem::Resolver::Set
# Loads a spec with the given +name+. +version+, +platform+ and +source+ are
# ignored.
- def load_spec name, version, platform, source # :nodoc:
+ def load_spec(name, version, platform, source) # :nodoc:
@specs.fetch name
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[VendorSet', ']' do
next if @directories.empty?
q.breakable
@@ -85,4 +85,3 @@ class Gem::Resolver::VendorSet < Gem::Resolver::Set
end
end
-
diff --git a/lib/rubygems/resolver/vendor_specification.rb b/lib/rubygems/resolver/vendor_specification.rb
index c624f3e834..56f2e6eb2c 100644
--- a/lib/rubygems/resolver/vendor_specification.rb
+++ b/lib/rubygems/resolver/vendor_specification.rb
@@ -6,7 +6,7 @@
class Gem::Resolver::VendorSpecification < Gem::Resolver::SpecSpecification
- def == other # :nodoc:
+ def ==(other) # :nodoc:
self.class === other and
@set == other.set and
@spec == other.spec and
@@ -17,9 +17,8 @@ class Gem::Resolver::VendorSpecification < Gem::Resolver::SpecSpecification
# This is a null install as this gem was unpacked into a directory.
# +options+ are ignored.
- def install options = {}
+ def install(options = {})
yield nil
end
end
-
diff --git a/lib/rubygems/safe_yaml.rb b/lib/rubygems/safe_yaml.rb
new file mode 100644
index 0000000000..3540fd74dd
--- /dev/null
+++ b/lib/rubygems/safe_yaml.rb
@@ -0,0 +1,59 @@
+module Gem
+
+ ###
+ # This module is used for safely loading YAML specs from a gem. The
+ # `safe_load` method defined on this module is specifically designed for
+ # loading Gem specifications. For loading other YAML safely, please see
+ # Psych.safe_load
+
+ module SafeYAML
+ PERMITTED_CLASSES = %w(
+ Symbol
+ Time
+ Date
+ Gem::Dependency
+ Gem::Platform
+ Gem::Requirement
+ Gem::Specification
+ Gem::Version
+ Gem::Version::Requirement
+ YAML::Syck::DefaultKey
+ Syck::DefaultKey
+ ).freeze
+
+ PERMITTED_SYMBOLS = %w(
+ development
+ runtime
+ ).freeze
+
+ if ::YAML.respond_to? :safe_load
+ def self.safe_load(input)
+ if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0.pre1')
+ ::YAML.safe_load(input, permitted_classes: PERMITTED_CLASSES, permitted_symbols: PERMITTED_SYMBOLS, aliases: true)
+ else
+ ::YAML.safe_load(input, PERMITTED_CLASSES, PERMITTED_SYMBOLS, true)
+ end
+ end
+
+ def self.load(input)
+ if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0.pre1')
+ ::YAML.safe_load(input, permitted_classes: [::Symbol])
+ else
+ ::YAML.safe_load(input, [::Symbol])
+ end
+ end
+ else
+ unless Gem::Deprecate.skip
+ warn "YAML safe loading is not available. Please upgrade psych to a version that supports safe loading (>= 2.0)."
+ end
+
+ def self.safe_load(input, *args)
+ ::YAML.load input
+ end
+
+ def self.load(input)
+ ::YAML.load input
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/security.rb b/lib/rubygems/security.rb
index 119d6d56f7..7b0a0b3c6a 100644
--- a/lib/rubygems/security.rb
+++ b/lib/rubygems/security.rb
@@ -340,30 +340,37 @@ module Gem::Security
# Digest algorithm used to sign gems
DIGEST_ALGORITHM =
- if defined?(OpenSSL::Digest::SHA1) then
+ if defined?(OpenSSL::Digest::SHA256)
+ OpenSSL::Digest::SHA256
+ elsif defined?(OpenSSL::Digest::SHA1)
OpenSSL::Digest::SHA1
+ else
+ require 'digest'
+ Digest::SHA512
end
##
# Used internally to select the signing digest from all computed digests
DIGEST_NAME = # :nodoc:
- if DIGEST_ALGORITHM then
+ if DIGEST_ALGORITHM.method_defined? :name
DIGEST_ALGORITHM.new.name
+ else
+ DIGEST_ALGORITHM.name[/::([^:]+)\z/, 1]
end
##
# Algorithm for creating the key pair used to sign gems
KEY_ALGORITHM =
- if defined?(OpenSSL::PKey::RSA) then
+ if defined?(OpenSSL::PKey::RSA)
OpenSSL::PKey::RSA
end
##
# Length of keys created by KEY_ALGORITHM
- KEY_LENGTH = 2048
+ KEY_LENGTH = 3072
##
# Cipher used to encrypt the key pair used to sign gems.
@@ -372,9 +379,14 @@ module Gem::Security
KEY_CIPHER = OpenSSL::Cipher.new('AES-256-CBC') if defined?(OpenSSL::Cipher)
##
+ # One day in seconds
+
+ ONE_DAY = 86400
+
+ ##
# One year in seconds
- ONE_YEAR = 86400 * 365
+ ONE_YEAR = ONE_DAY * 365
##
# The default set of extensions are:
@@ -389,9 +401,9 @@ module Gem::Security
'keyUsage' =>
'keyEncipherment,dataEncipherment,digitalSignature',
'subjectKeyIdentifier' => 'hash',
- }
+ }.freeze
- def self.alt_name_or_x509_entry certificate, x509_entry
+ def self.alt_name_or_x509_entry(certificate, x509_entry)
alt_name = certificate.extensions.find do |extension|
extension.oid == "#{x509_entry}AltName"
end
@@ -407,8 +419,8 @@ module Gem::Security
#
# The +extensions+ restrict the key to the indicated uses.
- def self.create_cert subject, key, age = ONE_YEAR, extensions = EXTENSIONS,
- serial = 1
+ def self.create_cert(subject, key, age = ONE_YEAR, extensions = EXTENSIONS,
+ serial = 1)
cert = OpenSSL::X509::Certificate.new
cert.public_key = key.public_key
@@ -434,7 +446,7 @@ module Gem::Security
# a subject alternative name of +email+ and the given +extensions+ for the
# +key+.
- def self.create_cert_email email, key, age = ONE_YEAR, extensions = EXTENSIONS
+ def self.create_cert_email(email, key, age = ONE_YEAR, extensions = EXTENSIONS)
subject = email_to_name email
extensions = extensions.merge "subjectAltName" => "email:#{email}"
@@ -446,8 +458,8 @@ module Gem::Security
# Creates a self-signed certificate with an issuer and subject of +subject+
# and the given +extensions+ for the +key+.
- def self.create_cert_self_signed subject, key, age = ONE_YEAR,
- extensions = EXTENSIONS, serial = 1
+ def self.create_cert_self_signed(subject, key, age = ONE_YEAR,
+ extensions = EXTENSIONS, serial = 1)
certificate = create_cert subject, key, age, extensions
sign certificate, key, certificate, age, extensions, serial
@@ -455,16 +467,16 @@ module Gem::Security
##
# Creates a new key pair of the specified +length+ and +algorithm+. The
- # default is a 2048 bit RSA key.
+ # default is a 3072 bit RSA key.
- def self.create_key length = KEY_LENGTH, algorithm = KEY_ALGORITHM
+ def self.create_key(length = KEY_LENGTH, algorithm = KEY_ALGORITHM)
algorithm.new length
end
##
# Turns +email_address+ into an OpenSSL::X509::Name
- def self.email_to_name email_address
+ def self.email_to_name(email_address)
email_address = email_address.gsub(/[^\w@.-]+/i, '_')
cn, dcs = email_address.split '@'
@@ -482,15 +494,15 @@ module Gem::Security
#--
# TODO increment serial
- def self.re_sign expired_certificate, private_key, age = ONE_YEAR,
- extensions = EXTENSIONS
+ def self.re_sign(expired_certificate, private_key, age = ONE_YEAR,
+ extensions = EXTENSIONS)
raise Gem::Security::Exception,
"incorrect signing key for re-signing " +
"#{expired_certificate.subject}" unless
expired_certificate.public_key.to_pem == private_key.public_key.to_pem
unless expired_certificate.subject.to_s ==
- expired_certificate.issuer.to_s then
+ expired_certificate.issuer.to_s
subject = alt_name_or_x509_entry expired_certificate, :subject
issuer = alt_name_or_x509_entry expired_certificate, :issuer
@@ -519,8 +531,8 @@ module Gem::Security
#
# Returns the newly signed certificate.
- def self.sign certificate, signing_key, signing_cert,
- age = ONE_YEAR, extensions = EXTENSIONS, serial = 1
+ def self.sign(certificate, signing_key, signing_cert,
+ age = ONE_YEAR, extensions = EXTENSIONS, serial = 1)
signee_subject = certificate.subject
signee_key = certificate.public_key
@@ -559,7 +571,7 @@ module Gem::Security
##
# Enumerates the trusted certificates via Gem::Security::TrustDir.
- def self.trusted_certificates &block
+ def self.trusted_certificates(&block)
trust_dir.each_certificate(&block)
end
@@ -568,10 +580,10 @@ module Gem::Security
# +permissions+. If passed +cipher+ and +passphrase+ those arguments will be
# passed to +to_pem+.
- def self.write pemmable, path, permissions = 0600, passphrase = nil, cipher = KEY_CIPHER
+ def self.write(pemmable, path, permissions = 0600, passphrase = nil, cipher = KEY_CIPHER)
path = File.expand_path path
- open path, 'wb', permissions do |io|
+ File.open path, 'wb', permissions do |io|
if passphrase and cipher
io.write pemmable.to_pem cipher, passphrase
else
@@ -586,11 +598,10 @@ module Gem::Security
end
-if defined?(OpenSSL::SSL) then
+if defined?(OpenSSL::SSL)
require 'rubygems/security/policy'
require 'rubygems/security/policies'
require 'rubygems/security/trust_dir'
end
require 'rubygems/security/signer'
-
diff --git a/lib/rubygems/security/policies.rb b/lib/rubygems/security/policies.rb
index f16c46306a..8f6ad99316 100644
--- a/lib/rubygems/security/policies.rb
+++ b/lib/rubygems/security/policies.rb
@@ -110,7 +110,6 @@ module Gem::Security
'MediumSecurity' => MediumSecurity,
'HighSecurity' => HighSecurity,
# SigningPolicy is not intended for use by `gem -P` so do not list it
- }
+ }.freeze
end
-
diff --git a/lib/rubygems/security/policy.rb b/lib/rubygems/security/policy.rb
index f43e6c8c96..1aa6eab18c 100644
--- a/lib/rubygems/security/policy.rb
+++ b/lib/rubygems/security/policy.rb
@@ -24,7 +24,7 @@ class Gem::Security::Policy
# Create a new Gem::Security::Policy object with the given mode and
# options.
- def initialize name, policy = {}, opt = {}
+ def initialize(name, policy = {}, opt = {})
require 'openssl'
@name = name
@@ -55,7 +55,7 @@ class Gem::Security::Policy
# Verifies each certificate in +chain+ has signed the following certificate
# and is valid for the given +time+.
- def check_chain chain, 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?
@@ -74,7 +74,7 @@ class Gem::Security::Policy
# Verifies that +data+ matches the +signature+ created by +public_key+ and
# the +digest+ algorithm.
- def check_data public_key, digest, signature, data
+ def check_data(public_key, digest, signature, data)
raise Gem::Security::Exception, "invalid signature" unless
public_key.verify digest.new, signature, data.digest
@@ -85,22 +85,22 @@ class Gem::Security::Policy
# Ensures that +signer+ is valid for +time+ and was signed by the +issuer+.
# If the +issuer+ is +nil+ no verification is performed.
- def check_cert signer, issuer, time
+ def check_cert(signer, issuer, time)
raise Gem::Security::Exception, 'missing signing certificate' unless
signer
message = "certificate #{signer.subject}"
- if not_before = signer.not_before and not_before > time then
+ if not_before = signer.not_before and not_before > time
raise Gem::Security::Exception,
"#{message} not valid before #{not_before}"
end
- if not_after = signer.not_after and not_after < time then
+ if not_after = signer.not_after and not_after < time
raise Gem::Security::Exception, "#{message} not valid after #{not_after}"
end
- if issuer and not signer.verify issuer.public_key then
+ if issuer and not signer.verify issuer.public_key
raise Gem::Security::Exception,
"#{message} was not issued by #{issuer.subject}"
end
@@ -111,8 +111,8 @@ 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 then
+ def check_key(signer, key)
+ unless signer and key
return true unless @only_signed
raise Gem::Security::Exception, 'missing key or signature'
@@ -129,7 +129,7 @@ class Gem::Security::Policy
# Ensures the root certificate in +chain+ is self-signed and valid for
# +time+.
- def check_root chain, time
+ def check_root(chain, time)
raise Gem::Security::Exception, 'missing signing chain' unless chain
root = chain.first
@@ -148,7 +148,7 @@ class Gem::Security::Policy
# Ensures the root of +chain+ has a trusted certificate in +trust_dir+ and
# the digests of the two certificates match according to +digester+
- def check_trust chain, digester, trust_dir
+ def check_trust(chain, digester, trust_dir)
raise Gem::Security::Exception, 'missing signing chain' unless chain
root = chain.first
@@ -157,7 +157,7 @@ class Gem::Security::Policy
path = Gem::Security.trust_dir.cert_path root
- unless File.exist? path then
+ unless File.exist? path
message = "root cert #{root.subject} is not trusted".dup
message << " (root of signing cert #{chain.last.subject})" if
@@ -183,7 +183,7 @@ class Gem::Security::Policy
##
# Extracts the email or subject from +certificate+
- def subject certificate # :nodoc:
+ def subject(certificate) # :nodoc:
certificate.extensions.each do |extension|
next unless extension.oid == 'subjectAltName'
@@ -196,9 +196,9 @@ class Gem::Security::Policy
def inspect # :nodoc:
("[Policy: %s - data: %p signer: %p chain: %p root: %p " +
"signed-only: %p trusted-only: %p]") % [
- @name, @verify_chain, @verify_data, @verify_root, @verify_signer,
- @only_signed, @only_trusted,
- ]
+ @name, @verify_chain, @verify_data, @verify_root, @verify_signer,
+ @only_signed, @only_trusted,
+ ]
end
##
@@ -208,13 +208,13 @@ 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)'
- if signatures.empty? then
- if @only_signed then
+ def verify(chain, key = nil, digests = {}, signatures = {},
+ full_name = '(unknown)')
+ if signatures.empty?
+ if @only_signed
raise Gem::Security::Exception,
"unsigned gems are not allowed by the #{name} policy"
- elsif digests.empty? then
+ elsif digests.empty?
# lack of signatures is irrelevant if there is nothing to check
# against
else
@@ -232,7 +232,7 @@ class Gem::Security::Policy
file_digests.values.first.name == Gem::Security::DIGEST_NAME
end
- if @verify_data then
+ if @verify_data
raise Gem::Security::Exception, 'no digests provided (probable bug)' if
signer_digests.nil? or signer_digests.empty?
else
@@ -249,9 +249,9 @@ class Gem::Security::Policy
check_root chain, time if @verify_root
- if @only_trusted then
+ if @only_trusted
check_trust chain, digester, trust_dir
- elsif signatures.empty? and digests.empty? then
+ elsif signatures.empty? and digests.empty?
# trust is irrelevant if there's no signatures to verify
else
alert_warning "#{subject signer} is not trusted for #{full_name}"
@@ -280,7 +280,7 @@ class Gem::Security::Policy
# Extracts the certificate chain from the +spec+ and calls #verify to ensure
# the signatures and certificate chain is valid according to the policy..
- def verify_signatures spec, digests, signatures
+ def verify_signatures(spec, digests, signatures)
chain = spec.cert_chain.map do |cert_pem|
OpenSSL::X509::Certificate.new cert_pem
end
diff --git a/lib/rubygems/security/signer.rb b/lib/rubygems/security/signer.rb
index 0c6ef60a9a..34e86e921a 100644
--- a/lib/rubygems/security/signer.rb
+++ b/lib/rubygems/security/signer.rb
@@ -2,8 +2,12 @@
##
# Basic OpenSSL-based package signing class.
+require "rubygems/user_interaction"
+
class Gem::Security::Signer
+ include Gem::UserInteraction
+
##
# The chain of certificates for signing including the signing certificate
@@ -26,20 +30,53 @@ class Gem::Security::Signer
attr_reader :digest_name # :nodoc:
##
+ # Gem::Security::Signer options
+
+ attr_reader :options
+
+ DEFAULT_OPTIONS = {
+ expiration_length_days: 365
+ }.freeze
+
+ ##
+ # Attemps to re-sign an expired cert with a given private key
+ 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')
+ expired_cert_file = "#{File.basename(expired_cert_path)}.expired.#{expiry}"
+ new_expired_cert_path = File.join(Gem.user_home, ".gem", expired_cert_file)
+
+ Gem::Security.write(expired_cert, new_expired_cert_path)
+
+ re_signed_cert = Gem::Security.re_sign(
+ expired_cert,
+ private_key,
+ (Gem::Security::ONE_DAY * Gem.configuration.cert_expiration_length_days)
+ )
+
+ Gem::Security.write(re_signed_cert, expired_cert_path)
+
+ yield(expired_cert_path, new_expired_cert_path) if block_given?
+ end
+
+ ##
# Creates a new signer with an RSA +key+ or path to a key, and a certificate
# +chain+ containing X509 certificates, encoding certificates or paths to
# certificates.
- def initialize key, cert_chain, passphrase = nil
+ def initialize(key, cert_chain, passphrase = nil, options = {})
@cert_chain = cert_chain
@key = key
+ @passphrase = passphrase
+ @options = DEFAULT_OPTIONS.merge(options)
- unless @key then
+ unless @key
default_key = File.join Gem.default_key_path
@key = default_key if File.exist? default_key
end
- unless @cert_chain then
+ unless @cert_chain
default_cert = File.join Gem.default_cert_path
@cert_chain = [default_cert] if File.exist? default_cert
end
@@ -47,10 +84,12 @@ class Gem::Security::Signer
@digest_algorithm = Gem::Security::DIGEST_ALGORITHM
@digest_name = Gem::Security::DIGEST_NAME
- @key = OpenSSL::PKey::RSA.new File.read(@key), passphrase if
- @key and not OpenSSL::PKey::RSA === @key
+ if @key && !@key.is_a?(OpenSSL::PKey::RSA)
+ @passphrase ||= ask_for_password("Enter PEM pass phrase:")
+ @key = OpenSSL::PKey::RSA.new(File.read(@key), @passphrase)
+ end
- if @cert_chain then
+ if @cert_chain
@cert_chain = @cert_chain.compact.map do |cert|
next cert if OpenSSL::X509::Certificate === cert
@@ -67,10 +106,10 @@ class Gem::Security::Signer
# Extracts the full name of +cert+. If the certificate has a subjectAltName
# this value is preferred, otherwise the subject is used.
- def extract_name cert # :nodoc:
+ def extract_name(cert) # :nodoc:
subject_alt_name = cert.extensions.find { |e| 'subjectAltName' == e.oid }
- if subject_alt_name then
+ if subject_alt_name
/\Aemail:/ =~ subject_alt_name.value
$' || subject_alt_name.value
@@ -99,13 +138,15 @@ class Gem::Security::Signer
##
# Sign data with given digest algorithm
- def sign data
+ def sign(data)
return unless @key
raise Gem::Security::Exception, 'no certs provided' if @cert_chain.empty?
- if @cert_chain.length == 1 and @cert_chain.last.not_after < Time.now then
- re_sign_key
+ if @cert_chain.length == 1 and @cert_chain.last.not_after < Time.now
+ re_sign_key(
+ expiration_length: (Gem::Security::ONE_DAY * options[:expiration_length_days])
+ )
end
full_name = extract_name @cert_chain.last
@@ -121,6 +162,7 @@ class Gem::Security::Signer
# The key will be re-signed if:
# * The expired certificate is self-signed
# * The expired certificate is saved at ~/.gem/gem-public_cert.pem
+ # and the private key is saved at ~/.gem/gem-private_key.pem
# * There is no file matching the expiry date at
# ~/.gem/gem-public_cert.pem.expired.%Y%m%d%H%M%S
#
@@ -128,25 +170,32 @@ class Gem::Security::Signer
# be saved as ~/.gem/gem-public_cert.pem.expired.%Y%m%d%H%M%S where the
# expiry time (not after) is used for the timestamp.
- def re_sign_key # :nodoc:
+ def re_sign_key(expiration_length: Gem::Security::ONE_YEAR) # :nodoc:
old_cert = @cert_chain.last
- disk_cert_path = File.join Gem.default_cert_path
- disk_cert = File.read disk_cert_path rescue nil
- disk_key =
- File.read File.join(Gem.default_key_path) rescue nil
+ disk_cert_path = File.join(Gem.default_cert_path)
+ disk_cert = File.read(disk_cert_path) rescue nil
- if disk_key == @key.to_pem and disk_cert == old_cert.to_pem then
- expiry = old_cert.not_after.strftime '%Y%m%d%H%M%S'
+ disk_key_path = File.join(Gem.default_key_path)
+ disk_key =
+ OpenSSL::PKey::RSA.new(File.read(disk_key_path), @passphrase) rescue nil
+
+ 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')
old_cert_file = "gem-public_cert.pem.expired.#{expiry}"
- old_cert_path = File.join Gem.user_home, ".gem", old_cert_file
+ old_cert_path = File.join(Gem.user_home, ".gem", old_cert_file)
- unless File.exist? old_cert_path then
- Gem::Security.write old_cert, old_cert_path
+ unless File.exist?(old_cert_path)
+ Gem::Security.write(old_cert, old_cert_path)
- cert = Gem::Security.re_sign old_cert, @key
+ cert = Gem::Security.re_sign(old_cert, @key, expiration_length)
- Gem::Security.write cert, disk_cert_path
+ Gem::Security.write(cert, disk_cert_path)
+
+ alert("Your cert: #{disk_cert_path} has been auto re-signed with the key: #{disk_key_path}")
+ alert("Your expired cert will be located at: #{old_cert_path}")
@cert_chain = [cert]
end
@@ -154,4 +203,3 @@ class Gem::Security::Signer
end
end
-
diff --git a/lib/rubygems/security/trust_dir.rb b/lib/rubygems/security/trust_dir.rb
index bf44975cc6..98031ea22b 100644
--- a/lib/rubygems/security/trust_dir.rb
+++ b/lib/rubygems/security/trust_dir.rb
@@ -11,7 +11,7 @@ class Gem::Security::TrustDir
DEFAULT_PERMISSIONS = {
:trust_dir => 0700,
:trusted_cert => 0600,
- }
+ }.freeze
##
# The directory where trusted certificates will be stored.
@@ -22,7 +22,7 @@ class Gem::Security::TrustDir
# Creates a new TrustDir using +dir+ where the directory and file
# permissions will be checked according to +permissions+
- def initialize dir, permissions = DEFAULT_PERMISSIONS
+ def initialize(dir, permissions = DEFAULT_PERMISSIONS)
@dir = dir
@permissions = permissions
@@ -32,7 +32,7 @@ class Gem::Security::TrustDir
##
# Returns the path to the trusted +certificate+
- def cert_path certificate
+ def cert_path(certificate)
name_path certificate.subject
end
@@ -59,7 +59,7 @@ class Gem::Security::TrustDir
# Returns the issuer certificate of the given +certificate+ if it exists in
# the trust directory.
- def issuer_of certificate
+ def issuer_of(certificate)
path = name_path certificate.issuer
return unless File.exist? path
@@ -70,7 +70,7 @@ class Gem::Security::TrustDir
##
# Returns the path to the trusted certificate with the given ASN.1 +name+
- def name_path name
+ def name_path(name)
digest = @digester.hexdigest name.to_s
File.join @dir, "cert-#{digest}.pem"
@@ -79,7 +79,7 @@ class Gem::Security::TrustDir
##
# Loads the given +certificate_file+
- def load_certificate certificate_file
+ def load_certificate(certificate_file)
pem = File.read certificate_file
OpenSSL::X509::Certificate.new pem
@@ -88,13 +88,14 @@ class Gem::Security::TrustDir
##
# Add a certificate to trusted certificate list.
- def trust_cert certificate
+ def trust_cert(certificate)
verify
destination = cert_path certificate
- open destination, 'wb', @permissions[:trusted_cert] do |io|
+ File.open destination, 'wb', 0600 do |io|
io.write certificate.to_pem
+ io.chmod(@permissions[:trusted_cert])
end
end
@@ -104,7 +105,7 @@ class Gem::Security::TrustDir
# permissions.
def verify
- if File.exist? @dir then
+ if File.exist? @dir
raise Gem::Security::Exception,
"trust directory #{@dir} is not a directory" unless
File.directory? @dir
@@ -116,4 +117,3 @@ class Gem::Security::TrustDir
end
end
-
diff --git a/lib/rubygems/security_option.rb b/lib/rubygems/security_option.rb
new file mode 100644
index 0000000000..4e3473acb4
--- /dev/null
+++ b/lib/rubygems/security_option.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'rubygems'
+
+# forward-declare
+
+module Gem::Security # :nodoc:
+ class Policy # :nodoc:
+ end
+end
+
+##
+# Mixin methods for security option for Gem::Commands
+
+module Gem::SecurityOption
+ def add_security_option
+ # TODO: use @parser.accept
+ OptionParser.accept Gem::Security::Policy do |value|
+ require 'rubygems/security'
+
+ raise OptionParser::InvalidArgument, 'OpenSSL not installed' unless
+ defined?(Gem::Security::HighSecurity)
+
+ policy = Gem::Security::Policies[value]
+ unless policy
+ valid = Gem::Security::Policies.keys.sort
+ raise OptionParser::InvalidArgument, "#{value} (#{valid.join ', '} are valid)"
+ end
+ policy
+ end
+
+ add_option(:"Install/Update", '-P', '--trust-policy POLICY',
+ Gem::Security::Policy,
+ 'Specify gem trust policy') do |value, options|
+ options[:security_policy] = value
+ end
+ end
+end
diff --git a/lib/rubygems/server.rb b/lib/rubygems/server.rb
index 63dfe31b35..1453bf2323 100644
--- a/lib/rubygems/server.rb
+++ b/lib/rubygems/server.rb
@@ -2,6 +2,7 @@
require 'webrick'
require 'zlib'
require 'erb'
+require 'uri'
require 'rubygems'
require 'rubygems/rdoc'
@@ -34,7 +35,7 @@ class Gem::Server
include ERB::Util
include Gem::UserInteraction
- SEARCH = <<-ERB
+ SEARCH = <<-ERB.freeze
<form class="headerSearch" name="headerSearchForm" method="get" action="/rdoc">
<div id="search" style="float:right">
<label for="q">Filter/Search</label>
@@ -44,7 +45,7 @@ class Gem::Server
</form>
ERB
- DOC_TEMPLATE = <<-'ERB'
+ DOC_TEMPLATE = <<-'ERB'.freeze
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
@@ -68,7 +69,7 @@ class Gem::Server
<h1>Summary</h1>
<p>There are <%=values["gem_count"]%> gems installed:</p>
<p>
- <%= values["specs"].map { |v| "<a href\"##{u v["name"]}\">#{h v["name"]}</a>" }.join ', ' %>.
+ <%= values["specs"].map { |v| "<a href=\"##{u v["name"]}\">#{h v["name"]}</a>" }.join ', ' %>.
<h1>Gems</h1>
<dl>
@@ -81,20 +82,20 @@ class Gem::Server
<b><%=h spec["name"]%> <%=h spec["version"]%></b>
<% if spec["ri_installed"] || spec["rdoc_installed"] then %>
- <a href="<%=u spec["doc_path"]%>">[rdoc]</a>
+ <a href="<%=spec["doc_path"]%>">[rdoc]</a>
<% else %>
<span title="rdoc not installed">[rdoc]</span>
<% end %>
<% if spec["homepage"] then %>
- <a href="<%=u spec["homepage"]%>" title="<%=h spec["homepage"]%>">[www]</a>
+ <a href="<%=uri_encode spec["homepage"]%>" title="<%=h spec["homepage"]%>">[www]</a>
<% else %>
<span title="no homepage available">[www]</span>
<% end %>
<% if spec["has_deps"] then %>
- depends on
- <%= spec["dependencies"].map { |v| "<a href=\"##{u v["name"]}>#{h v["name"]}</a>" }.join ', ' %>.
+ <%= spec["dependencies"].map { |v| "<a href=\"##{u v["name"]}\">#{h v["name"]}</a>" }.join ', ' %>.
<% end %>
</dt>
<dd>
@@ -128,7 +129,7 @@ class Gem::Server
ERB
# CSS is copy & paste from rdoc-style.css, RDoc V1.0.1 - 20041108
- RDOC_CSS = <<-CSS
+ RDOC_CSS = <<-CSS.freeze
body {
font-family: Verdana,Arial,Helvetica,sans-serif;
font-size: 90%;
@@ -338,7 +339,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
.ruby-value { color: #7fffd4; background: transparent; }
CSS
- RDOC_NO_DOCUMENTATION = <<-'ERB'
+ RDOC_NO_DOCUMENTATION = <<-'ERB'.freeze
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
@@ -372,7 +373,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
</html>
ERB
- RDOC_SEARCH_TEMPLATE = <<-'ERB'
+ RDOC_SEARCH_TEMPLATE = <<-'ERB'.freeze
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
@@ -449,14 +450,20 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
@have_rdoc_4_plus = nil
end
- def add_date res
+ def add_date(res)
res['date'] = @spec_dirs.map do |spec_dir|
File.stat(spec_dir).mtime
end.max
end
- def doc_root gem_name
- if have_rdoc_4_plus? then
+ def uri_encode(str)
+ str.gsub(URI::UNSAFE) do |match|
+ match.each_byte.map { |c| sprintf('%%%02X', c.ord) }.join
+ end
+ end
+
+ def doc_root(gem_name)
+ if have_rdoc_4_plus?
"/doc_root/#{u gem_name}/"
else
"/doc_root/#{u gem_name}/rdoc/index.html"
@@ -484,14 +491,14 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
specs = Marshal.dump specs
- if req.path =~ /\.gz$/ then
- specs = Gem.gzip specs
+ if req.path =~ /\.gz$/
+ specs = Gem::Util.gzip specs
res['content-type'] = 'application/x-gzip'
else
res['content-type'] = 'application/octet-stream'
end
- if req.request_method == 'HEAD' then
+ if req.request_method == 'HEAD'
res['content-length'] = specs.length
else
res.body << specs
@@ -502,7 +509,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
# Creates server sockets based on the addresses option. If no addresses
# were given a server socket for all interfaces is created.
- def listen addresses = @addresses
+ def listen(addresses = @addresses)
addresses = [nil] unless addresses
listeners = 0
@@ -522,14 +529,14 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
end
end
- if @server.listeners.empty? then
+ if @server.listeners.empty?
say "Unable to start a server."
say "Check for running servers or your --bind and --port arguments"
terminate_interaction 1
end
end
- def prerelease_specs req, res
+ def prerelease_specs(req, res)
reset_gems
res['content-type'] = 'application/x-gzip'
@@ -545,14 +552,14 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
specs = Marshal.dump specs
- if req.path =~ /\.gz$/ then
- specs = Gem.gzip specs
+ if req.path =~ /\.gz$/
+ specs = Gem::Util.gzip specs
res['content-type'] = 'application/x-gzip'
else
res['content-type'] = 'application/octet-stream'
end
- if req.request_method == 'HEAD' then
+ if req.request_method == 'HEAD'
res['content-length'] = specs.length
else
res.body << specs
@@ -566,27 +573,19 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
add_date res
case req.request_uri.path
- when %r|^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)-([0-9.]+[^-]*?)(-.*?)?\.gemspec\.rz$| then
- marshal_format, name, version, platform = $1, $2, $3, $4
- specs = Gem::Specification.find_all_by_name name, version
-
- selector = [name, version, platform].map(&:inspect).join ' '
-
- platform = if platform then
- Gem::Platform.new platform.sub(/^-/, '')
- else
- Gem::Platform::RUBY
- end
+ when %r|^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)\.gemspec\.rz$| then
+ marshal_format, full_name = $1, $2
+ specs = Gem::Specification.find_all_by_full_name(full_name)
- specs = specs.select { |s| s.platform == platform }
+ selector = full_name.inspect
- if specs.empty? then
+ if specs.empty?
res.status = 404
res.body = "No gems found matching #{selector}"
- elsif specs.length > 1 then
+ elsif specs.length > 1
res.status = 500
res.body = "Multiple gems found matching #{selector}"
- elsif marshal_format then
+ elsif marshal_format
res['content-type'] = 'application/x-deflate'
res.body << Gem.deflate(Marshal.dump(specs.first))
end
@@ -624,6 +623,18 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
executables = nil if executables.empty?
executables.last["is_last"] = true if executables
+ # Pre-process spec homepage for safety reasons
+ begin
+ homepage_uri = URI.parse(spec.homepage)
+ if [URI::HTTP, URI::HTTPS].member? homepage_uri.class
+ homepage_uri = spec.homepage
+ else
+ homepage_uri = "."
+ end
+ rescue URI::InvalidURIError
+ homepage_uri = "."
+ end
+
specs << {
"authors" => spec.authors.sort.join(", "),
"date" => spec.date.to_s,
@@ -633,7 +644,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
"only_one_executable" => (executables && executables.size == 1),
"full_name" => spec.full_name,
"has_deps" => !deps.empty?,
- "homepage" => spec.homepage,
+ "homepage" => homepage_uri,
"name" => spec.name,
"rdoc_installed" => Gem::RDoc.new(spec).rdoc_installed?,
"ri_installed" => Gem::RDoc.new(spec).ri_installed?,
@@ -650,7 +661,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
"only_one_executable" => true,
"full_name" => "rubygems-#{Gem::VERSION}",
"has_deps" => false,
- "homepage" => "http://docs.rubygems.org/",
+ "homepage" => "http://guides.rubygems.org/",
"name" => 'rubygems',
"ri_installed" => true,
"summary" => "RubyGems itself",
@@ -807,7 +818,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
'/gems' => '/cache/',
}
- if have_rdoc_4_plus? then
+ if have_rdoc_4_plus?
@server.mount '/doc_root', RDoc::Servlet, '/doc_root'
else
file_handlers['/doc_root'] = '/doc/'
@@ -840,14 +851,14 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
specs = Marshal.dump specs
- if req.path =~ /\.gz$/ then
- specs = Gem.gzip specs
+ if req.path =~ /\.gz$/
+ specs = Gem::Util.gzip specs
res['content-type'] = 'application/x-gzip'
else
res['content-type'] = 'application/octet-stream'
end
- if req.request_method == 'HEAD' then
+ if req.request_method == 'HEAD'
res['content-length'] = specs.length
else
res.body << specs
diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb
index 85f5268fa3..faed7bd350 100644
--- a/lib/rubygems/source.rb
+++ b/lib/rubygems/source.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'uri'
-require 'fileutils'
+autoload :FileUtils, 'fileutils'
+autoload :URI, 'uri'
##
# A Source knows how to list and fetch gems from a RubyGems marshal index.
@@ -16,7 +16,7 @@ class Gem::Source
:released => 'specs',
:latest => 'latest_specs',
:prerelease => 'prerelease_specs',
- }
+ }.freeze
##
# The URI this source will fetch gems from.
@@ -36,15 +36,6 @@ class Gem::Source
end
@uri = uri
- @api_uri = nil
- end
-
- ##
- # Use an SRV record on the host to look up the true endpoint for the index.
-
- def api_uri # :nodoc:
- require 'rubygems/remote_fetcher'
- @api_uri ||= Gem::RemoteFetcher.fetcher.api_endpoint uri
end
##
@@ -67,13 +58,17 @@ class Gem::Source
return -1 if !other.uri
- @uri.to_s <=> other.uri.to_s
+ # Returning 1 here ensures that when sorting a list of sources, the
+ # original ordering of sources supplied by the user is preserved.
+ return 1 unless @uri.to_s == other.uri.to_s
+
+ 0
else
nil
end
end
- def == other # :nodoc:
+ def ==(other) # :nodoc:
self.class === other and @uri == other.uri
end
@@ -83,9 +78,9 @@ 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' == api_uri.scheme
+ return Gem::Resolver::IndexSet.new self if 'file' == uri.scheme
- bundler_api_uri = api_uri + './api/v1/dependencies'
+ bundler_api_uri = uri + './api/v1/dependencies'
begin
fetcher = Gem::RemoteFetcher.fetcher
@@ -93,7 +88,7 @@ class Gem::Source
rescue Gem::RemoteFetcher::FetchError
Gem::Resolver::IndexSet.new self
else
- if response.respond_to? :uri then
+ if response.respond_to? :uri
Gem::Resolver::APISet.new response.uri
else
Gem::Resolver::APISet.new bundler_api_uri
@@ -131,32 +126,32 @@ class Gem::Source
##
# Fetches a specification for the given +name_tuple+.
- def fetch_spec name_tuple
+ def fetch_spec(name_tuple)
fetcher = Gem::RemoteFetcher.fetcher
spec_file_name = name_tuple.spec_name
- uri = api_uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"
+ source_uri = uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"
- cache_dir = cache_dir uri
+ cache_dir = cache_dir source_uri
local_spec = File.join cache_dir, spec_file_name
- if File.exist? local_spec then
+ if File.exist? local_spec
spec = Gem.read_binary local_spec
spec = Marshal.load(spec) rescue nil
return spec if spec
end
- uri.path << '.rz'
+ source_uri.path << '.rz'
- spec = fetcher.fetch_path uri
- spec = Gem.inflate spec
+ spec = fetcher.fetch_path source_uri
+ spec = Gem::Util.inflate spec
- if update_cache? then
+ if update_cache?
FileUtils.mkdir_p cache_dir
- open local_spec, 'wb' do |io|
+ File.open local_spec, 'wb' do |io|
io.write spec
end
end
@@ -180,7 +175,7 @@ class Gem::Source
file = FILES[type]
fetcher = Gem::RemoteFetcher.fetcher
file_name = "#{file}.#{Gem.marshal_version}"
- spec_path = api_uri + "#{file_name}.gz"
+ spec_path = uri + "#{file_name}.gz"
cache_dir = cache_dir spec_path
local_file = File.join(cache_dir, file_name)
retried = false
@@ -208,15 +203,15 @@ class Gem::Source
def download(spec, dir=Dir.pwd)
fetcher = Gem::RemoteFetcher.fetcher
- fetcher.download spec, api_uri.to_s, dir
+ fetcher.download spec, uri.to_s, dir
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[Remote:', ']' do
q.breakable
q.text @uri.to_s
- if api = api_uri
+ if api = uri
q.breakable
q.text 'API URI: '
q.text api.to_s
@@ -232,4 +227,3 @@ require 'rubygems/source/specific_file'
require 'rubygems/source/local'
require 'rubygems/source/lock'
require 'rubygems/source/vendor'
-
diff --git a/lib/rubygems/source/git.rb b/lib/rubygems/source/git.rb
index 0900da0cbc..0b8a4339cc 100644
--- a/lib/rubygems/source/git.rb
+++ b/lib/rubygems/source/git.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-require 'digest'
require 'rubygems/util'
##
@@ -51,7 +50,7 @@ class Gem::Source::Git < Gem::Source
# repository may contain multiple gems. If +submodules+ is true, submodules
# will be checked out when the gem is installed.
- def initialize name, repository, reference, submodules = false
+ def initialize(name, repository, reference, submodules = false)
super repository
@name = name
@@ -64,7 +63,7 @@ class Gem::Source::Git < Gem::Source
@git = ENV['git'] || 'git'
end
- def <=> other
+ def <=>(other)
case other
when Gem::Source::Git then
0
@@ -78,7 +77,7 @@ class Gem::Source::Git < Gem::Source
end
end
- def == other # :nodoc:
+ def ==(other) # :nodoc:
super and
@name == other.name and
@repository == other.repository and
@@ -94,7 +93,7 @@ class Gem::Source::Git < Gem::Source
return false unless File.exist? repo_cache_dir
- unless File.exist? install_dir then
+ unless File.exist? install_dir
system @git, 'clone', '--quiet', '--no-checkout',
repo_cache_dir, install_dir
end
@@ -118,7 +117,7 @@ class Gem::Source::Git < Gem::Source
def cache # :nodoc:
return unless @remote
- if File.exist? repo_cache_dir then
+ if File.exist? repo_cache_dir
Dir.chdir repo_cache_dir do
system @git, 'fetch', '--quiet', '--force', '--tags',
@repository, 'refs/heads/*:refs/heads/*'
@@ -146,7 +145,7 @@ class Gem::Source::Git < Gem::Source
##
# Nothing to download for git gems
- def download full_spec, path # :nodoc:
+ def download(full_spec, path) # :nodoc:
end
##
@@ -158,7 +157,7 @@ class Gem::Source::Git < Gem::Source
File.join base_dir, 'gems', "#{@name}-#{dir_shortref}"
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[Git: ', ']' do
q.breakable
q.text @repository
@@ -207,7 +206,7 @@ class Gem::Source::Git < Gem::Source
Dir.chdir directory do
spec = Gem::Specification.load file
- if spec then
+ if spec
spec.base_dir = base_dir
spec.extension_dir =
@@ -226,8 +225,10 @@ class Gem::Source::Git < Gem::Source
# A hash for the git gem based on the git repository URI.
def uri_hash # :nodoc:
+ require 'digest' # required here to avoid deadlocking in Gem.activate_bin_path (because digest is a gem on 2.5+)
+
normalized =
- if @repository =~ %r%^\w+://(\w+@)?% then
+ if @repository =~ %r%^\w+://(\w+@)?%
uri = URI(@repository).normalize.to_s.sub %r%/$%,''
uri.sub(/\A(\w+)/) { $1.downcase }
else
@@ -238,4 +239,3 @@ class Gem::Source::Git < Gem::Source
end
end
-
diff --git a/lib/rubygems/source/installed.rb b/lib/rubygems/source/installed.rb
index 300491e467..8e20cbd76d 100644
--- a/lib/rubygems/source/installed.rb
+++ b/lib/rubygems/source/installed.rb
@@ -11,7 +11,7 @@ class Gem::Source::Installed < Gem::Source
##
# Installed sources sort before all other sources
- def <=> other
+ def <=>(other)
case other
when Gem::Source::Git,
Gem::Source::Lock,
@@ -29,13 +29,12 @@ class Gem::Source::Installed < Gem::Source
##
# We don't need to download an installed gem
- def download spec, path
+ def download(spec, path)
nil
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.text '[Installed]'
end
end
-
diff --git a/lib/rubygems/source/local.rb b/lib/rubygems/source/local.rb
index 3227fb61b0..875e992d85 100644
--- a/lib/rubygems/source/local.rb
+++ b/lib/rubygems/source/local.rb
@@ -9,12 +9,13 @@ class Gem::Source::Local < Gem::Source
@specs = nil
@api_uri = nil
@uri = nil
+ @load_specs_names = {}
end
##
# Local sorts before Gem::Source and after Gem::Source::Installed
- def <=> other
+ def <=>(other)
case other
when Gem::Source::Installed,
Gem::Source::Lock then
@@ -33,50 +34,52 @@ class Gem::Source::Local < Gem::Source
"#<%s specs: %p>" % [self.class, keys]
end
- def load_specs type # :nodoc:
- names = []
+ def load_specs(type) # :nodoc:
+ @load_specs_names[type] ||= begin
+ names = []
- @specs = {}
+ @specs = {}
- Dir["*.gem"].each do |file|
- begin
- pkg = Gem::Package.new(file)
- rescue SystemCallError, Gem::Package::FormatError
- # ignore
- else
- tup = pkg.spec.name_tuple
- @specs[tup] = [File.expand_path(file), pkg]
-
- case type
- when :released
- unless pkg.spec.version.prerelease?
- names << pkg.spec.name_tuple
- end
- when :prerelease
- if pkg.spec.version.prerelease?
- names << pkg.spec.name_tuple
- end
- when :latest
+ Dir["*.gem"].each do |file|
+ begin
+ pkg = Gem::Package.new(file)
+ rescue SystemCallError, Gem::Package::FormatError
+ # ignore
+ else
tup = pkg.spec.name_tuple
-
- cur = names.find { |x| x.name == tup.name }
- if !cur
- names << tup
- elsif cur.version < tup.version
- names.delete cur
- names << tup
+ @specs[tup] = [File.expand_path(file), pkg]
+
+ case type
+ when :released
+ unless pkg.spec.version.prerelease?
+ names << pkg.spec.name_tuple
+ end
+ when :prerelease
+ if pkg.spec.version.prerelease?
+ names << pkg.spec.name_tuple
+ end
+ when :latest
+ tup = pkg.spec.name_tuple
+
+ cur = names.find { |x| x.name == tup.name }
+ if !cur
+ names << tup
+ elsif cur.version < tup.version
+ names.delete cur
+ names << tup
+ end
+ else
+ names << pkg.spec.name_tuple
end
- else
- names << pkg.spec.name_tuple
end
end
- end
- names
+ names
+ end
end
- def find_gem gem_name, version = Gem::Requirement.default, # :nodoc:
- prerelease = false
+ def find_gem(gem_name, version = Gem::Requirement.default, # :nodoc:
+ prerelease = false)
load_specs :complete
found = []
@@ -88,7 +91,7 @@ class Gem::Source::Local < Gem::Source
if version.satisfied_by?(s.version)
if prerelease
found << s
- elsif !s.version.prerelease?
+ elsif !s.version.prerelease? || version.prerelease?
found << s
end
end
@@ -98,7 +101,7 @@ class Gem::Source::Local < Gem::Source
found.max_by { |s| s.version }
end
- def fetch_spec name # :nodoc:
+ def fetch_spec(name) # :nodoc:
load_specs :complete
if data = @specs[name]
@@ -108,7 +111,7 @@ class Gem::Source::Local < Gem::Source
end
end
- def download spec, cache_dir = nil # :nodoc:
+ def download(spec, cache_dir = nil) # :nodoc:
load_specs :complete
@specs.each do |name, data|
@@ -118,7 +121,7 @@ class Gem::Source::Local < Gem::Source
raise Gem::Exception, "Unable to find file for '#{spec.full_name}'"
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[Local gems:', ']' do
q.breakable
q.seplist @specs.keys do |v|
diff --git a/lib/rubygems/source/lock.rb b/lib/rubygems/source/lock.rb
index 86b16e964c..3b3f491750 100644
--- a/lib/rubygems/source/lock.rb
+++ b/lib/rubygems/source/lock.rb
@@ -15,11 +15,11 @@ class Gem::Source::Lock < Gem::Source
# Creates a new Lock source that wraps +source+ and moves it earlier in the
# sort list.
- def initialize source
+ def initialize(source)
@wrapped = source
end
- def <=> other # :nodoc:
+ def <=>(other) # :nodoc:
case other
when Gem::Source::Lock then
@wrapped <=> other.wrapped
@@ -30,14 +30,18 @@ class Gem::Source::Lock < Gem::Source
end
end
- def == other # :nodoc:
+ def ==(other) # :nodoc:
0 == (self <=> other)
end
+ def hash # :nodoc:
+ @wrapped.hash ^ 3
+ end
+
##
# Delegates to the wrapped source's fetch_spec method.
- def fetch_spec name_tuple
+ def fetch_spec(name_tuple)
@wrapped.fetch_spec name_tuple
end
@@ -46,4 +50,3 @@ class Gem::Source::Lock < Gem::Source
end
end
-
diff --git a/lib/rubygems/source/specific_file.rb b/lib/rubygems/source/specific_file.rb
index 459c803e1a..a22772b9c0 100644
--- a/lib/rubygems/source/specific_file.rb
+++ b/lib/rubygems/source/specific_file.rb
@@ -27,22 +27,22 @@ class Gem::Source::SpecificFile < Gem::Source
attr_reader :spec
- def load_specs *a # :nodoc:
+ def load_specs(*a) # :nodoc:
[@name]
end
- def fetch_spec name # :nodoc:
+ def fetch_spec(name) # :nodoc:
return @spec if name == @name
raise Gem::Exception, "Unable to find '#{name}'"
@spec
end
- def download spec, dir = nil # :nodoc:
+ def download(spec, dir = nil) # :nodoc:
return @path if spec == @spec
raise Gem::Exception, "Unable to download '#{spec.full_name}'"
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[SpecificFile:', ']' do
q.breakable
q.text @path
@@ -59,7 +59,7 @@ class Gem::Source::SpecificFile < Gem::Source
#
# Otherwise Gem::Source#<=> is used.
- def <=> other
+ def <=>(other)
case other
when Gem::Source::SpecificFile then
return nil if @spec.name != other.spec.name
diff --git a/lib/rubygems/source/vendor.rb b/lib/rubygems/source/vendor.rb
index e1b3698607..a87fa63331 100644
--- a/lib/rubygems/source/vendor.rb
+++ b/lib/rubygems/source/vendor.rb
@@ -7,11 +7,11 @@ class Gem::Source::Vendor < Gem::Source::Installed
##
# Creates a new Vendor source for a gem that was unpacked at +path+.
- def initialize path
+ def initialize(path)
@uri = path
end
- def <=> other
+ def <=>(other)
case other
when Gem::Source::Lock then
-1
@@ -25,4 +25,3 @@ class Gem::Source::Vendor < Gem::Source::Installed
end
end
-
diff --git a/lib/rubygems/source_list.rb b/lib/rubygems/source_list.rb
index 66ce4d57ed..83b689f78e 100644
--- a/lib/rubygems/source_list.rb
+++ b/lib/rubygems/source_list.rb
@@ -105,7 +105,7 @@ class Gem::SourceList
@sources.empty?
end
- def == other # :nodoc:
+ def ==(other) # :nodoc:
to_a == other
end
@@ -140,7 +140,7 @@ class Gem::SourceList
##
# Deletes +source+ from the source list which may be a Gem::Source or a URI.
- def delete source
+ def delete(source)
if source.kind_of? Gem::Source
@sources.delete source
else
diff --git a/lib/rubygems/source_local.rb b/lib/rubygems/source_local.rb
index 07cb9e6e8f..5107069fd0 100644
--- a/lib/rubygems/source_local.rb
+++ b/lib/rubygems/source_local.rb
@@ -2,5 +2,6 @@
require 'rubygems/source'
require 'rubygems/source_local'
-# TODO warn upon require, this file is deprecated.
-
+unless Gem::Deprecate.skip
+ Kernel.warn "#{Gem.location_of_caller(3).join(':')}: Warning: Requiring rubygems/source_local is deprecated; please use rubygems/source/local instead."
+end
diff --git a/lib/rubygems/source_specific_file.rb b/lib/rubygems/source_specific_file.rb
index d42e6e7440..b676b1d3a2 100644
--- a/lib/rubygems/source_specific_file.rb
+++ b/lib/rubygems/source_specific_file.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require 'rubygems/source/specific_file'
-# TODO warn upon require, this file is deprecated.
-
+unless Gem::Deprecate.skip
+ Kernel.warn "#{Gem.location_of_caller(3).join(':')}: Warning: Requiring rubygems/source_specific_file is deprecated; please use rubygems/source/specific_file instead."
+end
diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb
index 755d4be1eb..ca901cb8bf 100644
--- a/lib/rubygems/spec_fetcher.rb
+++ b/lib/rubygems/spec_fetcher.rb
@@ -54,7 +54,7 @@ class Gem::SpecFetcher
# If you need to retrieve specifications from a different +source+, you can
# send it as an argument.
- def initialize sources = nil
+ def initialize(sources = nil)
@sources = sources || Gem.sources
@update_cache =
@@ -184,10 +184,10 @@ class Gem::SpecFetcher
# Suggests gems based on the supplied +gem_name+. Returns an array of
# alternative gem names.
- def suggest_gems_from_name gem_name
+ def suggest_gems_from_name(gem_name, type = :latest)
gem_name = gem_name.downcase.tr('_-', '')
max = gem_name.size / 2
- names = available_specs(:latest).first.values.flatten(1)
+ names = available_specs(type).first.values.flatten(1)
matches = names.map { |n|
next unless n.match_platform?
@@ -201,7 +201,11 @@ class Gem::SpecFetcher
[n.name, distance]
}.compact
- matches = matches.uniq.sort_by { |name, dist| dist }
+ matches = if matches.empty? && type != :prerelease
+ suggest_gems_from_name gem_name, :prerelease
+ else
+ matches.uniq.sort_by { |name, dist| dist }
+ end
matches.first(5).map { |name, dist| name }
end
@@ -267,4 +271,3 @@ class Gem::SpecFetcher
end
end
-
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index 5bbaec3911..bba3ffeab5 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -1,5 +1,5 @@
-# -*- coding: utf-8 -*-
# frozen_string_literal: true
+# -*- coding: utf-8 -*-
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
@@ -13,6 +13,7 @@ require 'rubygems/platform'
require 'rubygems/deprecate'
require 'rubygems/basic_specification'
require 'rubygems/stub_specification'
+require 'rubygems/specification_policy'
require 'rubygems/util/list'
require 'stringio'
@@ -30,6 +31,7 @@ require 'stringio'
# s.email = 'rubycoder@example.com'
# s.files = ["lib/example.rb"]
# s.homepage = 'https://rubygems.org/gems/example'
+# s.metadata = { "source_code_uri" => "https://github.com/example/example" }
# end
#
# Starting in RubyGems 2.0, a Specification can hold arbitrary
@@ -38,6 +40,8 @@ require 'stringio'
class Gem::Specification < Gem::BasicSpecification
+ extend Gem::Deprecate
+
# REFACTOR: Consider breaking out this version stuff into a separate
# module. There's enough special stuff around it that it may justify
# a separate class.
@@ -85,13 +89,13 @@ class Gem::Specification < Gem::BasicSpecification
'Added "required_rubygems_version"',
'Now forward-compatible with future versions',
],
- 3 => [
- 'Added Fixnum validation to the specification_version'
+ 3 => [
+ 'Added Fixnum validation to the specification_version'
],
- 4 => [
+ 4 => [
'Added sandboxed freeform metadata to the specification version.'
]
- }
+ }.freeze
MARSHAL_FIELDS = { # :nodoc:
-1 => 16,
@@ -99,15 +103,19 @@ class Gem::Specification < Gem::BasicSpecification
2 => 16,
3 => 17,
4 => 18,
- }
+ }.freeze
today = Time.now.utc
TODAY = Time.utc(today.year, today.month, today.day) # :nodoc:
+ # rubocop:disable Style/MutableConstant
LOAD_CACHE = {} # :nodoc:
+ # rubocop:enable Style/MutableConstant
private_constant :LOAD_CACHE if defined? private_constant
+ VALID_NAME_PATTERN = /\A[a-zA-Z0-9\.\-\_]+\z/.freeze # :nodoc:
+
# :startdoc:
##
@@ -129,7 +137,7 @@ class Gem::Specification < Gem::BasicSpecification
:autorequire => nil,
:bindir => 'bin',
:cert_chain => [],
- :date => TODAY,
+ :date => nil,
:dependencies => [],
:description => nil,
:email => nil,
@@ -148,24 +156,29 @@ class Gem::Specification < Gem::BasicSpecification
:required_ruby_version => Gem::Requirement.default,
:required_rubygems_version => Gem::Requirement.default,
:requirements => [],
- :rubyforge_project => nil,
:rubygems_version => Gem::VERSION,
:signing_key => nil,
:specification_version => CURRENT_SPECIFICATION_VERSION,
:summary => nil,
:test_files => [],
:version => nil,
- }
+ }.freeze
- Dupable = { } # :nodoc:
+ # rubocop:disable Style/MutableConstant
+ INITIALIZE_CODE_FOR_DEFAULTS = { } # :nodoc:
+ # rubocop:enable Style/MutableConstant
@@default_value.each do |k,v|
- case v
- when Time, Numeric, Symbol, true, false, nil
- Dupable[k] = false
- else
- Dupable[k] = true
- end
+ INITIALIZE_CODE_FOR_DEFAULTS[k] = case v
+ when [], {}, true, false, nil, Numeric, Symbol
+ v.inspect
+ when String
+ v.dump
+ when Numeric
+ "default_value(:#{k})"
+ else
+ "default_value(:#{k}).dup"
+ end
end
@@attributes = @@default_value.keys.sort_by { |s| s.to_s }
@@ -207,65 +220,218 @@ class Gem::Specification < Gem::BasicSpecification
attr_reader :version
##
- # Paths in the gem to add to <code>$LOAD_PATH</code> when this gem is
- # activated.
- #--
- # See also #require_paths
- #++
- # If you have an extension you do not need to add <code>"ext"</code> to the
- # require path, the extension build process will copy the extension files
- # into "lib" for you.
+ # A short summary of this gem's description. Displayed in `gem list -d`.
#
- # The default value is <code>"lib"</code>
+ # The #description should be more detailed than the summary.
#
# Usage:
#
- # # If all library files are in the root directory...
- # spec.require_paths = ['.']
+ # spec.summary = "This is a small summary of my gem"
- def require_paths=(val)
- @require_paths = Array(val)
+ attr_reader :summary
+
+ ##
+ # Files included in this gem. You cannot append to this accessor, you must
+ # assign to it.
+ #
+ # Only add files you can require to this list, not directories, etc.
+ #
+ # Directories are automatically stripped from this list when building a gem,
+ # other non-files cause an error.
+ #
+ # Usage:
+ #
+ # require 'rake'
+ # spec.files = FileList['lib/**/*.rb',
+ # 'bin/*',
+ # '[A-Z]*',
+ # 'test/**/*'].to_a
+ #
+ # # or without Rake...
+ # spec.files = Dir['lib/**/*.rb'] + Dir['bin/*']
+ # spec.files += Dir['[A-Z]*'] + Dir['test/**/*']
+ # spec.files.reject! { |fn| fn.include? "CVS" }
+
+ def files
+ # DO NOT CHANGE TO ||= ! This is not a normal accessor. (yes, it sucks)
+ # DOC: Why isn't it normal? Why does it suck? How can we fix this?
+ @files = [@files,
+ @test_files,
+ add_bindir(@executables),
+ @extra_rdoc_files,
+ @extensions,
+ ].flatten.compact.uniq.sort
end
##
- # The version of RubyGems used to create this gem.
+ # A list of authors for this gem.
#
- # Do not set this, it is set automatically when the gem is packaged.
+ # Alternatively, a single author can be specified by assigning a string to
+ # `spec.author`
+ #
+ # Usage:
+ #
+ # spec.authors = ['John Jones', 'Mary Smith']
- attr_accessor :rubygems_version
+ def authors=(value)
+ @authors = Array(value).flatten.grep(String)
+ end
+
+ ######################################################################
+ # :section: Recommended gemspec attributes
##
- # A short summary of this gem's description. Displayed in `gem list -d`.
+ # A long description of this gem
#
- # The #description should be more detailed than the summary.
+ # The description should be more detailed than the summary but not
+ # excessively long. A few paragraphs is a recommended length with no
+ # examples or formatting.
#
# Usage:
#
- # spec.summary = "This is a small summary of my gem"
+ # spec.description = <<-EOF
+ # Rake is a Make-like program implemented in Ruby. Tasks and
+ # dependencies are specified in standard Ruby syntax.
+ # EOF
- attr_reader :summary
+ attr_reader :description
+
+ ##
+ # A contact email address (or addresses) for this gem
+ #
+ # Usage:
+ #
+ # spec.email = 'john.jones@example.com'
+ # spec.email = ['jack@example.com', 'jill@example.com']
+
+ attr_accessor :email
##
- # Singular writer for #authors
+ # The URL of this gem's home page
+ #
+ # Usage:
+ #
+ # spec.homepage = 'https://github.com/ruby/rake'
+
+ attr_accessor :homepage
+
+ ##
+ # The license for this gem.
+ #
+ # The license must be no more than 64 characters.
+ #
+ # This should just be the name of your license. The full text of the license
+ # should be inside of the gem (at the top level) when you build it.
+ #
+ # 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.
+ #
+ # The most commonly used OSI approved licenses are MIT and Apache-2.0.
+ # GitHub also provides a license picker at http://choosealicense.com/.
+ #
+ # You should specify a license for your gem so that people know how they are
+ # permitted to use it, and any restrictions you're placing on it. Not
+ # specifying a license means all rights are reserved; others have no rights
+ # to use the code for any purpose.
+ #
+ # You can set multiple licenses with #licenses=
+ #
+ # Usage:
+ # spec.license = 'MIT'
+
+ def license=(o)
+ self.licenses = [o]
+ end
+
+ ##
+ # The license(s) for the library.
+ #
+ # Each license must be a short name, no more than 64 characters.
+ #
+ # This should just be the name of your license. The full
+ # text of the license should be inside of the gem when you build it.
+ #
+ # See #license= for more discussion
+ #
+ # Usage:
+ # spec.licenses = ['MIT', 'GPL-2.0']
+
+ def licenses=(licenses)
+ @licenses = Array licenses
+ end
+
+ ##
+ # The metadata holds extra data for this gem that may be useful to other
+ # consumers and is settable by gem authors without requiring an update to
+ # the rubygems software.
+ #
+ # Metadata items have the following restrictions:
+ #
+ # * The metadata must be a Hash object
+ # * All keys and values must be Strings
+ # * Keys can be a maximum of 128 bytes and values can be a maximum of 1024
+ # bytes
+ # * All strings must be UTF-8, no binary data is allowed
+ #
+ # You can use metadata to specify links to your gem's homepage, codebase,
+ # documentation, wiki, mailing list, issue tracker and changelog.
+ #
+ # s.metadata = {
+ # "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"
+ # }
+ #
+ # These links will be used on your gem's page on rubygems.org and must pass
+ # validation against following regex.
+ #
+ # %r{\Ahttps?:\/\/([^\s:@]+:[^\s:@]*@)?[A-Za-z\d\-]+(\.[A-Za-z\d\-]+)+\.?(:\d{1,5})?([\/?]\S*)?\z}
+
+ attr_accessor :metadata
+
+ ######################################################################
+ # :section: Optional gemspec attributes
+
+ ##
+ # Singular (alternative) writer for #authors
#
# Usage:
#
# spec.author = 'John Jones'
- def author= o
+ def author=(o)
self.authors = [o]
end
##
- # Sets the list of authors, ensuring it is an array.
+ # The path in the gem for executable scripts. Usually 'bin'
#
# Usage:
#
- # spec.authors = ['John Jones', 'Mary Smith']
+ # spec.bindir = 'bin'
- def authors= value
- @authors = Array(value).flatten.grep(String)
- end
+ attr_accessor :bindir
+
+ ##
+ # The certificate chain used to sign this gem. See Gem::Security for
+ # details.
+
+ attr_accessor :cert_chain
+
+ ##
+ # A message that gets displayed after the gem is installed.
+ #
+ # Usage:
+ #
+ # spec.post_install_message = "Thanks for installing!"
+
+ attr_accessor :post_install_message
##
# The platform this gem runs on.
@@ -290,9 +456,9 @@ class Gem::Specification < Gem::BasicSpecification
#
# spec.platform = Gem::Platform.local
- def platform= platform
+ def platform=(platform)
if @original_platform.nil? or
- @original_platform == Gem::Platform::RUBY then
+ @original_platform == Gem::Platform::RUBY
@original_platform = platform
end
@@ -325,104 +491,26 @@ class Gem::Specification < Gem::BasicSpecification
end
##
- # Files included in this gem. You cannot append to this accessor, you must
- # assign to it.
- #
- # Only add files you can require to this list, not directories, etc.
+ # Paths in the gem to add to <code>$LOAD_PATH</code> when this gem is
+ # activated.
+ #--
+ # See also #require_paths
+ #++
+ # If you have an extension you do not need to add <code>"ext"</code> to the
+ # require path, the extension build process will copy the extension files
+ # into "lib" for you.
#
- # Directories are automatically stripped from this list when building a gem,
- # other non-files cause an error.
+ # The default value is <code>"lib"</code>
#
# Usage:
#
- # require 'rake'
- # spec.files = FileList['lib/**/*.rb',
- # 'bin/*',
- # '[A-Z]*',
- # 'test/**/*'].to_a
- #
- # # or without Rake...
- # spec.files = Dir['lib/**/*.rb'] + Dir['bin/*']
- # spec.files += Dir['[A-Z]*'] + Dir['test/**/*']
- # spec.files.reject! { |fn| fn.include? "CVS" }
+ # # If all library files are in the root directory...
+ # spec.require_paths = ['.']
- def files
- # DO NOT CHANGE TO ||= ! This is not a normal accessor. (yes, it sucks)
- # DOC: Why isn't it normal? Why does it suck? How can we fix this?
- @files = [@files,
- @test_files,
- add_bindir(@executables),
- @extra_rdoc_files,
- @extensions,
- ].flatten.compact.uniq.sort
+ def require_paths=(val)
+ @require_paths = Array(val)
end
- ######################################################################
- # :section: Optional gemspec attributes
-
- ##
- # The path in the gem for executable scripts. Usually 'bin'
- #
- # Usage:
- #
- # spec.bindir = 'bin'
-
- attr_accessor :bindir
-
- ##
- # The certificate chain used to sign this gem. See Gem::Security for
- # details.
-
- attr_accessor :cert_chain
-
- ##
- # A long description of this gem
- #
- # The description should be more detailed than the summary but not
- # excessively long. A few paragraphs is a recommended length with no
- # examples or formatting.
- #
- # Usage:
- #
- # spec.description = <<-EOF
- # Rake is a Make-like program implemented in Ruby. Tasks and
- # dependencies are specified in standard Ruby syntax.
- # EOF
-
- attr_reader :description
-
- ##
- # :category: Recommended gemspec attributes
- #
- # A contact email address (or addresses) for this gem
- #
- # Usage:
- #
- # spec.email = 'john.jones@example.com'
- # spec.email = ['jack@example.com', 'jill@example.com']
-
- attr_accessor :email
-
- ##
- # :category: Recommended gemspec attributes
- #
- # The URL of this gem's home page
- #
- # Usage:
- #
- # spec.homepage = 'https://github.com/ruby/rake'
-
- attr_accessor :homepage
-
- ##
- # A message that gets displayed after the gem is installed.
- #
- # Usage:
- #
- # spec.post_install_message = "Thanks for installing!"
-
- attr_accessor :post_install_message
-
##
# The version of Ruby required by this gem
@@ -434,30 +522,16 @@ class Gem::Specification < Gem::BasicSpecification
attr_reader :required_rubygems_version
##
- # The key used to sign this gem. See Gem::Security for details.
+ # The version of RubyGems used to create this gem.
+ #
+ # Do not set this, it is set automatically when the gem is packaged.
- attr_accessor :signing_key
+ attr_accessor :rubygems_version
##
- # :attr_accessor: metadata
- #
- # The metadata holds extra data for this gem that may be useful to other
- # consumers and is settable by gem authors without requiring an update to
- # the rubygems software.
- #
- # Metadata items have the following restrictions:
- #
- # * The metadata must be a Hash object
- # * All keys and values must be Strings
- # * Keys can be a maximum of 128 bytes and values can be a maximum of 1024
- # bytes
- # * All strings must be UTF-8, no binary data is allowed
- #
- # To add metadata for the location of a issue tracker:
- #
- # s.metadata = { "issue_tracker" => "https://example/issues" }
+ # The key used to sign this gem. See Gem::Security for details.
- attr_accessor :metadata
+ attr_accessor :signing_key
##
# Adds a development dependency named +gem+ with +requirements+ to this
@@ -471,7 +545,7 @@ class Gem::Specification < Gem::BasicSpecification
# activated when a gem is required.
def add_development_dependency(gem, *requirements)
- add_dependency_with_type(gem, :development, *requirements)
+ add_dependency_with_type(gem, :development, requirements)
end
##
@@ -482,7 +556,7 @@ class Gem::Specification < Gem::BasicSpecification
# spec.add_runtime_dependency 'example', '~> 1.1', '>= 1.1.4'
def add_runtime_dependency(gem, *requirements)
- add_dependency_with_type(gem, :runtime, *requirements)
+ add_dependency_with_type(gem, :runtime, requirements)
end
##
@@ -550,61 +624,11 @@ class Gem::Specification < Gem::BasicSpecification
# Sets the version of RubyGems that installed this gem. See also
# #installed_by_version.
- def installed_by_version= version # :nodoc:
+ def installed_by_version=(version) # :nodoc:
@installed_by_version = Gem::Version.new version
end
##
- # :category: Recommended gemspec attributes
- #
- # The license for this gem.
- #
- # The license must be no more than 64 characters.
- #
- # This should just be the name of your license. The full text of the license
- # should be inside of the gem (at the top level) when you build it.
- #
- # 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.
- #
- # The most commonly used OSI approved licenses are MIT and Apache-2.0.
- # GitHub also provides a license picker at http://choosealicense.com/.
- #
- # You should specify a license for your gem so that people know how they are
- # permitted to use it, and any restrictions you're placing on it. Not
- # specifying a license means all rights are reserved; others have no rights
- # to use the code for any purpose.
- #
- # You can set multiple licenses with #licenses=
- #
- # Usage:
- # spec.license = 'MIT'
-
- def license=o
- self.licenses = [o]
- end
-
- ##
- # :category: Recommended gemspec attributes
- # The license(s) for the library.
- #
- # Each license must be a short name, no more than 64 characters.
- #
- # This should just be the name of your license. The full
- # text of the license should be inside of the gem when you build it.
- #
- # See #license= for more discussion
- #
- # Usage:
- # spec.licenses = ['MIT', 'GPL-2.0']
-
- def licenses= licenses
- @licenses = Array licenses
- end
-
- ##
# Specifies the rdoc options to be used when generating API documentation.
#
# Usage:
@@ -625,9 +649,7 @@ class Gem::Specification < Gem::BasicSpecification
# ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-darwin12.4.0]
# #<Gem::Version "2.0.0.247">
#
- # Because patch-level is taken into account, be very careful specifying using
- # `<=`: `<= 2.2.2` will not match any patch-level of 2.2.2 after the `p0`
- # release. It is much safer to specify `< 2.2.3` instead
+ # Prereleases can also be specified.
#
# Usage:
#
@@ -637,17 +659,17 @@ class Gem::Specification < Gem::BasicSpecification
# # Only with ruby 2.0.x
# spec.required_ruby_version = '~> 2.0'
#
- # # Only with ruby between 2.2.0 and 2.2.2
- # spec.required_ruby_version = ['>= 2.2.0', '< 2.2.3']
+ # # Only prereleases or final releases after 2.6.0.preview2
+ # spec.required_ruby_version = '> 2.6.0.preview2'
- def required_ruby_version= req
+ def required_ruby_version=(req)
@required_ruby_version = Gem::Requirement.create req
end
##
# The RubyGems version required by this gem
- def required_rubygems_version= req
+ def required_rubygems_version=(req)
@required_rubygems_version = Gem::Requirement.create req
end
@@ -672,7 +694,7 @@ class Gem::Specification < Gem::BasicSpecification
# spec.test_files = Dir.glob('test/tc_*.rb')
# spec.test_files = ['tests/test-suite.rb']
- def test_files= files # :nodoc:
+ def test_files=(files) # :nodoc:
@test_files = Array files
end
@@ -699,6 +721,7 @@ class Gem::Specification < Gem::BasicSpecification
# Deprecated: You must now specify the executable name to Gem.bin_path.
attr_writer :default_executable
+ deprecate :default_executable=, :none, 2018, 12
##
# Allows deinstallation of gems with legacy platforms.
@@ -706,12 +729,11 @@ class Gem::Specification < Gem::BasicSpecification
attr_writer :original_platform # :nodoc:
##
- # The rubyforge project this gem lives under. i.e. RubyGems'
- # rubyforge_project is "rubygems".
+ # Deprecated and ignored.
#
- # This option is deprecated.
+ # Formerly used to set rubyforge project.
- attr_accessor :rubyforge_project
+ attr_writer :rubyforge_project
##
# The Gem::Specification version of this gemspec.
@@ -721,8 +743,11 @@ class Gem::Specification < Gem::BasicSpecification
attr_accessor :specification_version
def self._all # :nodoc:
- unless defined?(@@all) && @@all then
+ unless defined?(@@all) && @@all
@@all = stubs.map(&:to_spec)
+ if @@all.any?(&:nil?) # TODO: remove once we're happy
+ raise "pid: #{$$} nil spec! included in #{stubs.inspect}"
+ end
# After a reset, make sure already loaded specs
# are still marked as activated.
@@ -739,18 +764,18 @@ class Gem::Specification < Gem::BasicSpecification
def self.each_gemspec(dirs) # :nodoc:
dirs.each do |dir|
- Dir[File.join(dir, "*.gemspec")].each do |path|
+ Gem::Util.glob_files_in_dir("*.gemspec", dir).each do |path|
yield path.untaint
end
end
end
- def self.gemspec_stubs_in dir, pattern
- Dir[File.join(dir, pattern)].map { |path| yield path }.select(&:valid?)
+ def self.gemspec_stubs_in(dir, pattern)
+ Gem::Util.glob_files_in_dir(pattern, dir).map { |path| yield path }.select(&:valid?)
end
private_class_method :gemspec_stubs_in
- def self.default_stubs pattern
+ def self.default_stubs(pattern)
base_dir = Gem.default_dir
gems_dir = File.join base_dir, "gems"
gemspec_stubs_in(default_specifications_dir, pattern) do |path|
@@ -759,59 +784,29 @@ class Gem::Specification < Gem::BasicSpecification
end
private_class_method :default_stubs
- def self.installed_stubs dirs, pattern
+ def self.installed_stubs(dirs, pattern)
map_stubs(dirs, pattern) do |path, base_dir, gems_dir|
Gem::StubSpecification.gemspec_stub(path, base_dir, gems_dir)
end
end
private_class_method :installed_stubs
- if [].respond_to? :flat_map
- def self.map_stubs(dirs, pattern) # :nodoc:
- dirs.flat_map { |dir|
- base_dir = File.dirname dir
- gems_dir = File.join base_dir, "gems"
- gemspec_stubs_in(dir, pattern) { |path| yield path, base_dir, gems_dir }
- }
- end
- else # FIXME: remove when 1.8 is dropped
- def self.map_stubs(dirs, pattern) # :nodoc:
- dirs.map { |dir|
- base_dir = File.dirname dir
- gems_dir = File.join base_dir, "gems"
- gemspec_stubs_in(dir, pattern) { |path| yield path, base_dir, gems_dir }
- }.flatten 1
- end
+ def self.map_stubs(dirs, pattern) # :nodoc:
+ dirs.flat_map { |dir|
+ base_dir = File.dirname dir
+ gems_dir = File.join base_dir, "gems"
+ gemspec_stubs_in(dir, pattern) { |path| yield path, base_dir, gems_dir }
+ }
end
private_class_method :map_stubs
- uniq_takes_a_block = false
- [1,2].uniq { uniq_takes_a_block = true }
-
- if uniq_takes_a_block
- def self.uniq_by(list, &block) # :nodoc:
- list.uniq(&block)
- end
- else # FIXME: remove when 1.8 is dropped
- def self.uniq_by(list) # :nodoc:
- values = {}
- list.each { |item|
- value = yield item
- values[value] ||= item
- }
- values.values
- end
+ def self.uniq_by(list, &block) # :nodoc:
+ list.uniq(&block)
end
private_class_method :uniq_by
- if [].respond_to? :sort_by!
- def self.sort_by! list, &block
- list.sort_by!(&block)
- end
- else # FIXME: remove when 1.8 is dropped
- def self.sort_by! list, &block
- list.replace list.sort_by(&block)
- end
+ def self.sort_by!(list, &block)
+ list.sort_by!(&block)
end
private_class_method :sort_by!
@@ -828,11 +823,11 @@ class Gem::Specification < Gem::BasicSpecification
def self.stubs
@@stubs ||= begin
pattern = "*.gemspec"
- stubs = default_stubs(pattern).concat installed_stubs(dirs, pattern)
+ stubs = Gem.loaded_specs.values + default_stubs(pattern) + installed_stubs(dirs, pattern)
stubs = uniq_by(stubs) { |stub| stub.full_name }
_resort!(stubs)
- @@stubs_by_name = stubs.group_by(&:name)
+ @@stubs_by_name = stubs.select { |s| Gem::Platform.match s.platform }.group_by(&:name)
stubs
end
end
@@ -841,13 +836,15 @@ class Gem::Specification < Gem::BasicSpecification
##
# Returns a Gem::StubSpecification for installed gem named +name+
+ # only returns stubs that match Gem.platforms
- def self.stubs_for name
+ def self.stubs_for(name)
if @@stubs
@@stubs_by_name[name] || []
else
pattern = "#{name}-*.gemspec"
- stubs = default_stubs(pattern) + installed_stubs(dirs, pattern)
+ stubs = Gem.loaded_specs.values + default_stubs(pattern) +
+ installed_stubs(dirs, pattern).select { |s| Gem::Platform.match s.platform }
stubs = uniq_by(stubs) { |stub| stub.full_name }.group_by(&:name)
stubs.each_value { |v| _resort!(v) }
@@ -879,8 +876,8 @@ class Gem::Specification < Gem::BasicSpecification
# Adds +spec+ to the known specifications, keeping the collection
# properly sorted.
- def self.add_spec spec
- warn "Gem::Specification.add_spec is deprecated and will be removed in Rubygems 3.0" unless Gem::Deprecate.skip
+ def self.add_spec(spec)
+ warn "Gem::Specification.add_spec is deprecated and will be removed in RubyGems 3.0" unless Gem::Deprecate.skip
# TODO: find all extraneous adds
# puts
# p :add_spec => [spec.full_name, caller.reject { |s| s =~ /minitest/ }]
@@ -904,8 +901,8 @@ class Gem::Specification < Gem::BasicSpecification
##
# Adds multiple specs to the known specifications.
- def self.add_specs *specs
- warn "Gem::Specification.add_specs is deprecated and will be removed in Rubygems 3.0" unless Gem::Deprecate.skip
+ def self.add_specs(*specs)
+ warn "Gem::Specification.add_specs is deprecated and will be removed in RubyGems 3.0" unless Gem::Deprecate.skip
raise "nil spec!" if specs.any?(&:nil?) # TODO: remove once we're happy
@@ -942,7 +939,8 @@ class Gem::Specification < Gem::BasicSpecification
#
# -- wilsonb
- def self.all= specs
+ def self.all=(specs)
+ raise "nil spec!" if specs.any?(&:nil?) # TODO: remove once we're happy
@@stubs_by_name = specs.group_by(&:name)
@@all = @@stubs = specs
end
@@ -985,7 +983,7 @@ class Gem::Specification < Gem::BasicSpecification
# Set the directories that Specification uses to find specs. Setting
# this resets the list of known specs.
- def self.dirs= dirs
+ def self.dirs=(dirs)
self.reset
@@dirs = Array(dirs).map { |dir| File.join dir, "specifications" }
@@ -1008,7 +1006,7 @@ class Gem::Specification < Gem::BasicSpecification
##
# Returns every spec that matches +name+ and optional +requirements+.
- def self.find_all_by_name name, *requirements
+ def self.find_all_by_name(name, *requirements)
requirements = Gem::Requirement.default if requirements.empty?
# TODO: maybe try: find_all { |s| spec === dep }
@@ -1017,10 +1015,17 @@ class Gem::Specification < Gem::BasicSpecification
end
##
+ # Returns every spec that has the given +full_name+
+
+ def self.find_all_by_full_name(full_name)
+ stubs.select {|s| s.full_name == full_name }.map(&:to_spec)
+ end
+
+ ##
# Find the best specification matching a +name+ and +requirements+. Raises
# if the dependency doesn't resolve to a valid specification.
- def self.find_by_name name, *requirements
+ def self.find_by_name(name, *requirements)
requirements = Gem::Requirement.default if requirements.empty?
# TODO: maybe try: find { |s| spec === dep }
@@ -1031,9 +1036,10 @@ class Gem::Specification < Gem::BasicSpecification
##
# Return the best specification that contains the file matching +path+.
- def self.find_by_path path
+ def self.find_by_path(path)
path = path.dup.freeze
spec = @@spec_with_requirable_file[path] ||= (stubs.find { |s|
+ next unless Gem::BundlerVersionFinder.compatible?(s)
s.contains_requirable_file? path
} || NOT_FOUND)
spec.to_spec
@@ -1043,14 +1049,16 @@ class Gem::Specification < Gem::BasicSpecification
# Return the best specification that contains the file matching +path+
# amongst the specs that are not activated.
- def self.find_inactive_by_path path
+ def self.find_inactive_by_path(path)
stub = stubs.find { |s|
- s.contains_requirable_file? path unless s.activated?
+ next if s.activated?
+ next unless Gem::BundlerVersionFinder.compatible?(s)
+ s.contains_requirable_file? path
}
stub && stub.to_spec
end
- def self.find_active_stub_by_path path
+ def self.find_active_stub_by_path(path)
stub = @@active_stub_with_requirable_file[path] ||= (stubs.find { |s|
s.activated? and s.contains_requirable_file? path
} || NOT_FOUND)
@@ -1060,7 +1068,7 @@ class Gem::Specification < Gem::BasicSpecification
##
# Return currently unresolved specs that contain the file matching +path+.
- def self.find_in_unresolved path
+ def self.find_in_unresolved(path)
# TODO: do we need these?? Kill it
specs = unresolved_deps.values.map { |dep| dep.to_specs }.flatten
@@ -1071,7 +1079,7 @@ class Gem::Specification < Gem::BasicSpecification
# Search through all unresolved deps and sub-dependencies and return
# specs that contain the file matching +path+.
- def self.find_in_unresolved_tree path
+ def self.find_in_unresolved_tree(path)
specs = unresolved_deps.values.map { |dep| dep.to_specs }.flatten
specs.each do |spec|
@@ -1099,13 +1107,13 @@ class Gem::Specification < Gem::BasicSpecification
Gem.load_yaml
input = normalize_yaml_input input
- spec = YAML.load input
+ spec = Gem::SafeYAML.safe_load input
- if spec && spec.class == FalseClass then
+ if spec && spec.class == FalseClass
raise Gem::EndOfYAMLException
end
- unless Gem::Specification === spec then
+ unless Gem::Specification === spec
raise Gem::Exception, "YAML data doesn't evaluate to gem specification"
end
@@ -1119,11 +1127,11 @@ class Gem::Specification < Gem::BasicSpecification
# Return the latest specs, optionally including prerelease specs if
# +prerelease+ is true.
- def self.latest_specs prerelease = false
+ def self.latest_specs(prerelease = false)
_latest_specs Gem::Specification._all, prerelease
end
- def self._latest_specs specs, prerelease = false # :nodoc:
+ def self._latest_specs(specs, prerelease = false) # :nodoc:
result = Hash.new { |h,k| h[k] = {} }
native = {}
@@ -1143,7 +1151,7 @@ class Gem::Specification < Gem::BasicSpecification
##
# Loads Ruby format gemspec from +file+.
- def self.load file
+ def self.load(file)
return unless file
_spec = LOAD_CACHE[file]
@@ -1152,11 +1160,7 @@ class Gem::Specification < Gem::BasicSpecification
file = file.dup.untaint
return unless File.file?(file)
- code = if defined? Encoding
- File.read file, :mode => 'r:UTF-8:-'
- else
- File.read file
- end
+ code = File.read file, :mode => 'r:UTF-8:-'
code.untaint
@@ -1243,8 +1247,8 @@ class Gem::Specification < Gem::BasicSpecification
##
# Removes +spec+ from the known specs.
- def self.remove_spec spec
- warn "Gem::Specification.remove_spec is deprecated and will be removed in Rubygems 3.0" unless Gem::Deprecate.skip
+ def self.remove_spec(spec)
+ warn "Gem::Specification.remove_spec is deprecated and will be removed in RubyGems 3.0" unless Gem::Deprecate.skip
_all.delete spec
stubs.delete_if { |s| s.full_name == spec.full_name }
(@@stubs_by_name[spec.name] || []).delete_if { |s| s.full_name == spec.full_name }
@@ -1279,13 +1283,19 @@ class Gem::Specification < Gem::BasicSpecification
@@active_stub_with_requirable_file = {}
_clear_load_cache
unresolved = unresolved_deps
- unless unresolved.empty? then
+ unless unresolved.empty?
w = "W" + "ARN"
- warn "#{w}: Unresolved specs during Gem::Specification.reset:"
+ warn "#{w}: Unresolved or ambigious specs during Gem::Specification.reset:"
unresolved.values.each do |dep|
warn " #{dep}"
+
+ versions = find_all_by_name(dep.name)
+ unless versions.empty?
+ warn " Available/installed versions of this gem:"
+ versions.each { |s| warn " - #{s.version}" }
+ end
end
- warn "#{w}: Clearing out unresolved specs."
+ warn "#{w}: Clearing out unresolved specs. Try 'gem cleanup <gem>'"
warn "Please report a bug if this causes problems."
unresolved.clear
end
@@ -1308,7 +1318,7 @@ class Gem::Specification < Gem::BasicSpecification
current_version = CURRENT_SPECIFICATION_VERSION
- field_count = if spec.specification_version > current_version then
+ field_count = if spec.specification_version > current_version
spec.instance_variable_set :@specification_version,
current_version
MARSHAL_FIELDS[current_version]
@@ -1316,7 +1326,7 @@ class Gem::Specification < Gem::BasicSpecification
MARSHAL_FIELDS[spec.specification_version]
end
- if array.size < field_count then
+ if array.size < field_count
raise TypeError, "invalid Gem::Specification format #{array.inspect}"
end
@@ -1335,7 +1345,7 @@ class Gem::Specification < Gem::BasicSpecification
spec.instance_variable_set :@required_rubygems_version, array[7]
spec.instance_variable_set :@original_platform, array[8]
spec.instance_variable_set :@dependencies, array[9]
- spec.instance_variable_set :@rubyforge_project, array[10]
+ # offset due to rubyforge_project removal
spec.instance_variable_set :@email, array[11]
spec.instance_variable_set :@authors, array[12]
spec.instance_variable_set :@description, array[13]
@@ -1355,7 +1365,7 @@ class Gem::Specification < Gem::BasicSpecification
sort_obj <=> other.sort_obj
end
- def == other # :nodoc:
+ def ==(other) # :nodoc:
self.class === other &&
name == other.name &&
version == other.version &&
@@ -1380,7 +1390,7 @@ class Gem::Specification < Gem::BasicSpecification
@required_rubygems_version,
@original_platform,
@dependencies,
- @rubyforge_project,
+ '', # rubyforge_project
@email,
@authors,
@description,
@@ -1400,7 +1410,7 @@ class Gem::Specification < Gem::BasicSpecification
def activate
other = Gem.loaded_specs[self.name]
- if other then
+ if other
check_version_conflict other
return false
end
@@ -1438,7 +1448,7 @@ class Gem::Specification < Gem::BasicSpecification
specs = spec_dep.to_specs
- if specs.size == 1 then
+ if specs.size == 1
specs.first.activate
else
name = spec_dep.name
@@ -1500,7 +1510,7 @@ class Gem::Specification < Gem::BasicSpecification
def add_bindir(executables)
return nil if executables.nil?
- if @bindir then
+ if @bindir
Array(executables).map { |e| File.join(@bindir, e) }
else
executables
@@ -1514,8 +1524,8 @@ class Gem::Specification < Gem::BasicSpecification
# +requirements+. Valid types are currently <tt>:runtime</tt> and
# <tt>:development</tt>.
- def add_dependency_with_type(dependency, type, *requirements)
- requirements = if requirements.empty? then
+ def add_dependency_with_type(dependency, type, requirements)
+ requirements = if requirements.empty?
Gem::Requirement.default
else
requirements.flatten
@@ -1544,7 +1554,7 @@ class Gem::Specification < Gem::BasicSpecification
# gem directories must come after -I and ENV['RUBYLIB']
insert_index = Gem.load_path_insert_index
- if insert_index then
+ if insert_index
# gem directories must come after -I and ENV['RUBYLIB']
$LOAD_PATH.insert(insert_index, *paths)
else
@@ -1582,7 +1592,7 @@ class Gem::Specification < Gem::BasicSpecification
##
# Returns the full path to an executable named +name+ in this gem.
- def bin_file name
+ def bin_file(name)
File.join bin_dir, name
end
@@ -1649,16 +1659,6 @@ class Gem::Specification < Gem::BasicSpecification
end
##
- # Used to detect if the gem is bundled in older version of Ruby, but not
- # detectable as default gem (see BasicSpecification#default_gem?).
-
- def bundled_gem_in_old_ruby?
- !default_gem? &&
- RUBY_VERSION < "2.0.0" &&
- summary == "This #{name} is bundled with Ruby"
- end
-
- ##
# Returns the full path to the cache directory containing this
# spec's cached gem.
@@ -1705,7 +1705,7 @@ class Gem::Specification < Gem::BasicSpecification
def has_conflicts?
return true unless Gem.env_requirement(name).satisfied_by?(version)
self.dependencies.any? { |dep|
- if dep.runtime? then
+ if dep.runtime?
spec = Gem.loaded_specs[dep.name]
spec and not spec.satisfies_requirement? dep
else
@@ -1714,13 +1714,16 @@ class Gem::Specification < Gem::BasicSpecification
}
end
- ##
- # The date this gem was created. Lazily defaults to the current UTC date.
+ # The date this gem was created.
#
- # There is no need to set this in your gem specification.
+ # If SOURCE_DATE_EPOCH is set as an environment variable, use that to support
+ # reproducible builds; otherwise, default to the current UTC date.
+ #
+ # Details on SOURCE_DATE_EPOCH:
+ # https://reproducible-builds.org/specs/source-date-epoch/
def date
- @date ||= TODAY
+ @date ||= ENV["SOURCE_DATE_EPOCH"] ? Time.utc(*Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc.to_a[3..5].reverse) : TODAY
end
DateLike = Object.new # :nodoc:
@@ -1732,26 +1735,26 @@ class Gem::Specification < Gem::BasicSpecification
/\A
(\d{4})-(\d{2})-(\d{2})
(\s+ \d{2}:\d{2}:\d{2}\.\d+ \s* (Z | [-+]\d\d:\d\d) )?
- \Z/x
+ \Z/x.freeze
##
# The date this gem was created
#
# DO NOT set this, it is set automatically when the gem is packaged.
- def date= date
+ def date=(date)
# We want to end up with a Time object with one-day resolution.
# This is the cleanest, most-readable, faster-than-using-Date
# way to do it.
@date = case date
when String then
- if DateTimeFormat =~ date then
+ if DateTimeFormat =~ date
Time.utc($1.to_i, $2.to_i, $3.to_i)
# Workaround for where the date format output from psych isn't
# parsed as a Time object by syck and thus comes through as a
# string.
- elsif /\A(\d{4})-(\d{2})-(\d{2}) \d{2}:\d{2}:\d{2}\.\d+?Z\z/ =~ date then
+ elsif /\A(\d{4})-(\d{2})-(\d{2}) \d{2}:\d{2}:\d{2}\.\d+?Z\z/ =~ date
Time.utc($1.to_i, $2.to_i, $3.to_i)
else
raise(Gem::InvalidSpecificationException,
@@ -1780,11 +1783,12 @@ class Gem::Specification < Gem::BasicSpecification
end
result
end
+ deprecate :default_executable, :none, 2018, 12
##
# The default value for specification attribute +name+
- def default_value name
+ def default_value(name)
@@default_value[name]
end
@@ -1808,7 +1812,7 @@ class Gem::Specification < Gem::BasicSpecification
out = []
Gem::Specification.each do |spec|
spec.dependencies.each do |dep|
- if self.satisfies_requirement?(dep) then
+ if self.satisfies_requirement?(dep)
sats = []
find_all_satisfiers(dep) do |sat|
sats << sat
@@ -1830,7 +1834,7 @@ class Gem::Specification < Gem::BasicSpecification
##
# A detailed description of this gem. See also #summary
- def description= str
+ def description=(str)
@description = str.to_s
end
@@ -1849,17 +1853,17 @@ class Gem::Specification < Gem::BasicSpecification
#
# spec.doc_dir 'ri' # => "/path/to/gem_repo/doc/a-1/ri"
- def doc_dir type = nil
+ def doc_dir(type = nil)
@doc_dir ||= File.join base_dir, 'doc', full_name
- if type then
+ if type
File.join @doc_dir, type
else
@doc_dir
end
end
- def encode_with coder # :nodoc:
+ def encode_with(coder) # :nodoc:
mark_version
coder.add 'name', @name
@@ -1880,7 +1884,7 @@ class Gem::Specification < Gem::BasicSpecification
end
end
- def eql? other # :nodoc:
+ def eql?(other) # :nodoc:
self.class === other && same_attributes?(other)
end
@@ -1894,7 +1898,7 @@ class Gem::Specification < Gem::BasicSpecification
##
# Singular accessor for #executables
- def executable=o
+ def executable=(o)
self.executables = [o]
end
@@ -1902,7 +1906,7 @@ class Gem::Specification < Gem::BasicSpecification
# Sets executables to +value+, ensuring it is an array. Don't
# use this, push onto the array instead.
- def executables= value
+ def executables=(value)
# TODO: warn about setting instead of pushing
@executables = Array(value)
end
@@ -1911,7 +1915,7 @@ class Gem::Specification < Gem::BasicSpecification
# Sets extensions to +extensions+, ensuring it is an array. Don't
# use this, push onto the array instead.
- def extensions= extensions
+ def extensions=(extensions)
# TODO: warn about setting instead of pushing
@extensions = Array extensions
end
@@ -1920,7 +1924,7 @@ class Gem::Specification < Gem::BasicSpecification
# Sets extra_rdoc_files to +files+, ensuring it is an array. Don't
# use this, push onto the array instead.
- def extra_rdoc_files= files
+ def extra_rdoc_files=(files)
# TODO: warn about setting instead of pushing
@extra_rdoc_files = Array files
end
@@ -1937,14 +1941,14 @@ class Gem::Specification < Gem::BasicSpecification
##
# Sets files to +files+, ensuring it is an array.
- def files= files
+ def files=(files)
@files = Array files
end
##
# Finds all gems that satisfy +dep+
- def find_all_satisfiers dep
+ def find_all_satisfiers(dep)
Gem::Specification.each do |spec|
yield spec if spec.satisfies_requirement? dep
end
@@ -1988,17 +1992,20 @@ class Gem::Specification < Gem::BasicSpecification
def has_rdoc # :nodoc:
true
end
+ deprecate :has_rdoc, :none, 2018, 12
##
# Deprecated and ignored.
#
# Formerly used to indicate this gem was RDoc-capable.
- def has_rdoc= ignored # :nodoc:
+ def has_rdoc=(ignored) # :nodoc:
@has_rdoc = true
end
+ deprecate :has_rdoc=, :none, 2018, 12
alias :has_rdoc? :has_rdoc # :nodoc:
+ deprecate :has_rdoc?, :none, 2018, 12
##
# True if this gem has files in test_files
@@ -2015,17 +2022,31 @@ class Gem::Specification < Gem::BasicSpecification
name.hash ^ version.hash
end
- def init_with coder # :nodoc:
+ def init_with(coder) # :nodoc:
@installed_by_version ||= nil
yaml_initialize coder.tag, coder.map
end
+
+
+ eval <<-RB, binding, __FILE__, __LINE__ + 1
+ def set_nil_attributes_to_nil
+ #{@@nil_attributes.map {|key| "@#{key} = nil" }.join "; "}
+ end
+ private :set_nil_attributes_to_nil
+
+ def set_not_nil_attributes_to_default_values
+ #{@@non_nil_attributes.map {|key| "@#{key} = #{INITIALIZE_CODE_FOR_DEFAULTS[key]}" }.join ";"}
+ end
+ private :set_not_nil_attributes_to_default_values
+ RB
+
##
# Specification constructor. Assigns the default values to the attributes
# and yields itself for further initialization. Optionally takes +name+ and
# +version+.
- def initialize name = nil, version = nil
+ def initialize(name = nil, version = nil)
super()
@gems_dir = nil
@base_dir = nil
@@ -2035,15 +2056,8 @@ class Gem::Specification < Gem::BasicSpecification
@original_platform = nil
@installed_by_version = nil
- @@nil_attributes.each do |key|
- instance_variable_set "@#{key}", nil
- end
-
- @@non_nil_attributes.each do |key|
- default = default_value(key)
- value = Dupable[key] ? default.dup : default
- instance_variable_set "@#{key}", value
- end
+ set_nil_attributes_to_nil
+ set_not_nil_attributes_to_default_values
@new_platform = Gem::Platform::RUBY
@@ -2056,14 +2070,14 @@ class Gem::Specification < Gem::BasicSpecification
##
# Duplicates array_attributes from +other_spec+ so state isn't shared.
- def initialize_copy other_spec
+ def initialize_copy(other_spec)
self.class.array_attributes.each do |name|
name = :"@#{name}"
next unless other_spec.instance_variable_defined? name
begin
val = other_spec.instance_variable_get(name)
- if val then
+ if val
instance_variable_set name, val.dup
elsif Gem.configuration.really_verbose
warn "WARNING: #{full_name} has an invalid nil value for #{name}"
@@ -2080,7 +2094,7 @@ class Gem::Specification < Gem::BasicSpecification
def base_dir
return Gem.dir unless loaded_from
- @base_dir ||= if default_gem? then
+ @base_dir ||= if default_gem?
File.dirname File.dirname File.dirname loaded_from
else
File.dirname File.dirname loaded_from
@@ -2102,7 +2116,7 @@ class Gem::Specification < Gem::BasicSpecification
if $DEBUG
super
else
- "#<#{self.class}:0x#{__id__.to_s(16)} #{full_name}>"
+ "#{super[0..-2]} #{full_name}>"
end
end
@@ -2156,7 +2170,7 @@ class Gem::Specification < Gem::BasicSpecification
def method_missing(sym, *a, &b) # :nodoc:
if @specification_version > CURRENT_SPECIFICATION_VERSION and
- sym.to_s =~ /=$/ then
+ sym.to_s =~ /=$/
warn "ignoring #{sym} loading #{full_name}" if $DEBUG
else
super
@@ -2183,7 +2197,7 @@ class Gem::Specification < Gem::BasicSpecification
# file list.
def normalize
- if defined?(@extra_rdoc_files) and @extra_rdoc_files then
+ if defined?(@extra_rdoc_files) and @extra_rdoc_files
@extra_rdoc_files.uniq!
@files ||= []
@files.concat(@extra_rdoc_files)
@@ -2208,7 +2222,7 @@ class Gem::Specification < Gem::BasicSpecification
# platform. For use with legacy gems.
def original_name # :nodoc:
- if platform == Gem::Platform::RUBY or platform.nil? then
+ if platform == Gem::Platform::RUBY or platform.nil?
"#{@name}-#{@version}"
else
"#{@name}-#{@version}-#{@original_platform}"
@@ -2241,11 +2255,11 @@ class Gem::Specification < Gem::BasicSpecification
attributes.each do |attr_name|
current_value = self.send attr_name
if current_value != default_value(attr_name) or
- self.class.required_attribute? attr_name then
+ self.class.required_attribute? attr_name
q.text "s.#{attr_name} = "
- if attr_name == :date then
+ if attr_name == :date
current_value = current_value.utc
q.text "Time.utc(#{current_value.year}, #{current_value.month}, #{current_value.day})"
@@ -2263,7 +2277,7 @@ class Gem::Specification < Gem::BasicSpecification
# Raise an exception if the version of this spec conflicts with the one
# that is already loaded (+other+)
- def check_version_conflict other # :nodoc:
+ def check_version_conflict(other) # :nodoc:
return if self.version == other.version
# This gem is already loaded. If the currently loaded gem is not in the
@@ -2284,7 +2298,7 @@ class Gem::Specification < Gem::BasicSpecification
# Check the spec for possible conflicts and freak out if there are any.
def raise_if_conflicts # :nodoc:
- if has_conflicts? then
+ if has_conflicts?
raise Gem::ConflictError.new self, conflicts
end
end
@@ -2293,7 +2307,7 @@ class Gem::Specification < Gem::BasicSpecification
# Sets rdoc_options to +value+, ensuring it is an array. Don't
# use this, push onto the array instead.
- def rdoc_options= options
+ def rdoc_options=(options)
# TODO: warn about setting instead of pushing
@rdoc_options = Array options
end
@@ -2308,7 +2322,7 @@ class Gem::Specification < Gem::BasicSpecification
##
# Singular accessor for #require_paths
- def require_path= path
+ def require_path=(path)
self.require_paths = Array(path)
end
@@ -2316,12 +2330,12 @@ class Gem::Specification < Gem::BasicSpecification
# Set requirements to +req+, ensuring it is an array. Don't
# use this, push onto the array instead.
- def requirements= req
+ def requirements=(req)
# TODO: warn about setting instead of pushing
@requirements = Array req
end
- def respond_to_missing? m, include_private = false # :nodoc:
+ def respond_to_missing?(m, include_private = false) # :nodoc:
false
end
@@ -2368,7 +2382,7 @@ class Gem::Specification < Gem::BasicSpecification
##
# True if this gem has the same attributes as +other+.
- def same_attributes? spec
+ def same_attributes?(spec)
@@attributes.all? { |name, default| self.send(name) == spec.send(name) }
end
@@ -2377,7 +2391,7 @@ class Gem::Specification < Gem::BasicSpecification
##
# Checks if this specification meets the requirement of +dependency+.
- def satisfies_requirement? dependency
+ def satisfies_requirement?(dependency)
return @name == dependency.name &&
dependency.requirement.satisfied_by?(@version)
end
@@ -2424,7 +2438,7 @@ class Gem::Specification < Gem::BasicSpecification
##
# A short summary of this gem's description.
- def summary= str
+ def summary=(str)
@summary = str.to_s.strip.
gsub(/(\w-)\n[ \t]*(\w)/, '\1\2').gsub(/\n[ \t]*/, " ") # so. weird.
end
@@ -2439,7 +2453,7 @@ class Gem::Specification < Gem::BasicSpecification
##
# Singular mutator for #test_files
- def test_file= file # :nodoc:
+ def test_file=(file) # :nodoc:
self.test_files = [file]
end
@@ -2451,11 +2465,11 @@ class Gem::Specification < Gem::BasicSpecification
# Handle the possibility that we have @test_suite_file but not
# @test_files. This will happen when an old gem is loaded via
# YAML.
- if defined? @test_suite_file then
+ if defined? @test_suite_file
@test_files = [@test_suite_file].flatten
@test_suite_file = nil
end
- if defined?(@test_files) and @test_files then
+ if defined?(@test_files) and @test_files
@test_files
else
@test_files = []
@@ -2479,7 +2493,7 @@ 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 then
+ unless platform.nil? or platform == Gem::Platform::RUBY
result << " s.platform = #{ruby_code original_platform}"
end
result << ""
@@ -2507,17 +2521,17 @@ class Gem::Specification < Gem::BasicSpecification
next if handled.include? attr_name
current_value = self.send(attr_name)
if current_value != default_value(attr_name) or
- self.class.required_attribute? attr_name then
+ self.class.required_attribute? attr_name
result << " s.#{attr_name} = #{ruby_code current_value}"
end
end
- if @installed_by_version then
+ if @installed_by_version
result << nil
result << " s.installed_by_version = \"#{Gem::VERSION}\" if s.respond_to? :installed_by_version"
end
- unless dependencies.empty? then
+ unless dependencies.empty?
result << nil
result << " if s.respond_to? :specification_version then"
result << " s.specification_version = #{specification_version}"
@@ -2576,39 +2590,30 @@ class Gem::Specification < Gem::BasicSpecification
end
def to_yaml(opts = {}) # :nodoc:
- if (YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck?) ||
- (defined?(Psych) && YAML == Psych) then
- # Because the user can switch the YAML engine behind our
- # 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 'rubygems/psych_tree'
- end
+ # Because the user can switch the YAML engine behind our
+ # 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 'rubygems/psych_tree'
+ end
- builder = Gem::NoAliasYAMLTree.create
- builder << self
- ast = builder.tree
+ builder = Gem::NoAliasYAMLTree.create
+ builder << self
+ ast = builder.tree
- io = StringIO.new
- io.set_encoding Encoding::UTF_8 if Object.const_defined? :Encoding
+ io = StringIO.new
+ io.set_encoding Encoding::UTF_8
- Psych::Visitors::Emitter.new(io).accept(ast)
+ Psych::Visitors::Emitter.new(io).accept(ast)
- io.string.gsub(/ !!null \n/, " \n")
- else
- YAML.quick_emit object_id, opts do |out|
- out.map taguri, to_yaml_style do |map|
- encode_with map
- end
- end
- end
+ io.string.gsub(/ !!null \n/, " \n")
end
##
# Recursively walk dependencies of this spec, executing the +block+ for each
# hop.
- def traverse trail = [], visited = {}, &block
+ def traverse(trail = [], visited = {}, &block)
trail.push(self)
begin
dependencies.each do |dep|
@@ -2641,304 +2646,40 @@ class Gem::Specification < Gem::BasicSpecification
# Raises InvalidSpecificationException if the spec does not pass the
# checks..
- def validate packaging = true
- @warnings = 0
+ def validate(packaging = true, strict = false)
require 'rubygems/user_interaction'
extend Gem::UserInteraction
normalize
- nil_attributes = self.class.non_nil_attributes.find_all do |attrname|
- instance_variable_get("@#{attrname}").nil?
- end
-
- unless nil_attributes.empty? then
- raise Gem::InvalidSpecificationException,
- "#{nil_attributes.join ', '} must not be nil"
- end
-
- if packaging and rubygems_version != Gem::VERSION then
- raise Gem::InvalidSpecificationException,
- "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}"
- end
-
- @@required_attributes.each do |symbol|
- unless self.send symbol then
- raise Gem::InvalidSpecificationException,
- "missing value for attribute #{symbol}"
- end
- end
-
- unless String === name then
- raise Gem::InvalidSpecificationException,
- "invalid value for attribute name: \"#{name.inspect}\""
- end
-
- if raw_require_paths.empty? then
- raise Gem::InvalidSpecificationException,
- 'specification must have at least one require_path'
- end
+ validation_policy = Gem::SpecificationPolicy.new(self)
+ validation_policy.packaging = packaging
+ validation_policy.validate(strict)
+ end
- @files.delete_if { |x| File.directory?(x) && !File.symlink?(x) }
- @test_files.delete_if { |x| File.directory?(x) && !File.symlink?(x) }
+ def keep_only_files_and_directories
@executables.delete_if { |x| File.directory?(File.join(@bindir, x)) }
- @extra_rdoc_files.delete_if { |x| File.directory?(x) && !File.symlink?(x) }
@extensions.delete_if { |x| File.directory?(x) && !File.symlink?(x) }
+ @extra_rdoc_files.delete_if { |x| File.directory?(x) && !File.symlink?(x) }
+ @files.delete_if { |x| File.directory?(x) && !File.symlink?(x) }
+ @test_files.delete_if { |x| File.directory?(x) && !File.symlink?(x) }
+ end
- non_files = files.reject { |x| File.file?(x) || File.symlink?(x) }
-
- unless not packaging or non_files.empty? then
- raise Gem::InvalidSpecificationException,
- "[\"#{non_files.join "\", \""}\"] are not files"
- end
-
- if files.include? file_name then
- raise Gem::InvalidSpecificationException,
- "#{full_name} contains itself (#{file_name}), check your files list"
- end
-
- unless specification_version.is_a?(Integer)
- raise Gem::InvalidSpecificationException,
- 'specification_version must be an Integer (did you mean version?)'
- end
-
- case platform
- when Gem::Platform, Gem::Platform::RUBY then # ok
- else
- raise Gem::InvalidSpecificationException,
- "invalid platform #{platform.inspect}, see Gem::Platform"
- end
-
- self.class.array_attributes.each do |field|
- val = self.send field
- klass = case field
- when :dependencies
- Gem::Dependency
- else
- String
- end
-
- unless Array === val and val.all? { |x| x.kind_of?(klass) } then
- raise(Gem::InvalidSpecificationException,
- "#{field} must be an Array of #{klass}")
- end
- end
-
- [:authors].each do |field|
- val = self.send field
- raise Gem::InvalidSpecificationException, "#{field} may not be empty" if
- val.empty?
- end
-
- unless Hash === metadata
- raise Gem::InvalidSpecificationException,
- 'metadata must be a hash'
- end
-
- metadata.keys.each do |k|
- if !k.kind_of?(String)
- raise Gem::InvalidSpecificationException,
- 'metadata keys must be a String'
- end
-
- if k.size > 128
- raise Gem::InvalidSpecificationException,
- "metadata key too large (#{k.size} > 128)"
- end
- end
-
- metadata.values.each do |k|
- if !k.kind_of?(String)
- raise Gem::InvalidSpecificationException,
- 'metadata values must be a String'
- end
-
- if k.size > 1024
- raise Gem::InvalidSpecificationException,
- "metadata value too large (#{k.size} > 1024)"
- end
- end
-
- licenses.each { |license|
- if license.length > 64
- raise Gem::InvalidSpecificationException,
- "each license must be 64 characters or less"
- end
-
- if !Gem::Licenses.match?(license)
- suggestions = Gem::Licenses.suggestions(license)
- message = <<-warning
-license value '#{license}' is invalid. Use a license identifier from
-http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license.
- warning
- message += "Did you mean #{suggestions.map { |s| "'#{s}'"}.join(', ')}?\n" unless suggestions.nil?
- warning(message)
- end
- }
-
- warning <<-warning if licenses.empty?
-licenses is empty, but is recommended. Use a license identifier from
-http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license.
- warning
-
- validate_permissions
-
- # reject lazy developers:
-
- lazy = '"FIxxxXME" or "TOxxxDO"'.gsub(/xxx/, '')
-
- unless authors.grep(/FI XME|TO DO/x).empty? then
- raise Gem::InvalidSpecificationException, "#{lazy} is not an author"
- end
-
- unless Array(email).grep(/FI XME|TO DO/x).empty? then
- raise Gem::InvalidSpecificationException, "#{lazy} is not an email"
- end
-
- if description =~ /FI XME|TO DO/x then
- raise Gem::InvalidSpecificationException, "#{lazy} is not a description"
- end
-
- if summary =~ /FI XME|TO DO/x then
- raise Gem::InvalidSpecificationException, "#{lazy} is not a summary"
- end
-
- if homepage and not homepage.empty? and
- homepage !~ /\A[a-z][a-z\d+.-]*:/i then
- raise Gem::InvalidSpecificationException,
- "\"#{homepage}\" is not a URI"
- end
-
- # Warnings
-
- %w[author email homepage summary].each do |attribute|
- value = self.send attribute
- warning "no #{attribute} specified" if value.nil? or value.empty?
- end
-
- if description == summary then
- warning 'description and summary are identical'
- end
-
- # TODO: raise at some given date
- warning "deprecated autorequire specified" if autorequire
-
- executables.each do |executable|
- executable_path = File.join(bindir, executable)
- shebang = File.read(executable_path, 2) == '#!'
-
- warning "#{executable_path} is missing #! line" unless shebang
- end
-
- files.each do |file|
- next unless File.symlink?(file)
- warning "#{file} is a symlink, which is not supported on all platforms"
- end
-
- validate_dependencies
-
- true
- ensure
- if $! or @warnings > 0 then
- alert_warning "See http://guides.rubygems.org/specification-reference/ for help"
- end
+ def validate_metadata
+ Gem::SpecificationPolicy.new(self).validate_metadata
end
##
# Checks that dependencies use requirements as we recommend. Warnings are
# issued when dependencies are open-ended or overly strict for semantic
# versioning.
-
- def validate_dependencies # :nodoc:
- # NOTE: see REFACTOR note in Gem::Dependency about types - this might be brittle
- seen = Gem::Dependency::TYPES.inject({}) { |types, type| types.merge({ type => {}}) }
-
- error_messages = []
- warning_messages = []
- dependencies.each do |dep|
- if prev = seen[dep.type][dep.name] then
- error_messages << <<-MESSAGE
-duplicate dependency on #{dep}, (#{prev.requirement}) use:
- add_#{dep.type}_dependency '#{dep.name}', '#{dep.requirement}', '#{prev.requirement}'
- MESSAGE
- end
-
- seen[dep.type][dep.name] = dep
-
- prerelease_dep = dep.requirements_list.any? do |req|
- Gem::Requirement.new(req).prerelease?
- end
-
- warning_messages << "prerelease dependency on #{dep} is not recommended" if
- prerelease_dep && !version.prerelease?
-
- overly_strict = dep.requirement.requirements.length == 1 &&
- dep.requirement.requirements.any? do |op, version|
- op == '~>' and
- not version.prerelease? and
- version.segments.length > 2 and
- version.segments.first != 0
- end
-
- if overly_strict then
- _, dep_version = dep.requirement.requirements.first
-
- base = dep_version.segments.first 2
-
- warning_messages << <<-WARNING
-pessimistic dependency on #{dep} may be overly strict
- if #{dep.name} is semantically versioned, use:
- add_#{dep.type}_dependency '#{dep.name}', '~> #{base.join '.'}', '>= #{dep_version}'
- WARNING
- end
-
- open_ended = dep.requirement.requirements.all? do |op, version|
- not version.prerelease? and (op == '>' or op == '>=')
- end
-
- if open_ended then
- op, dep_version = dep.requirement.requirements.first
-
- base = dep_version.segments.first 2
-
- bugfix = if op == '>' then
- ", '> #{dep_version}'"
- elsif op == '>=' and base != dep_version.segments then
- ", '>= #{dep_version}'"
- end
-
- warning_messages << <<-WARNING
-open-ended dependency on #{dep} is not recommended
- if #{dep.name} is semantically versioned, use:
- add_#{dep.type}_dependency '#{dep.name}', '~> #{base.join '.'}'#{bugfix}
- WARNING
- end
- end
- if error_messages.any?
- raise Gem::InvalidSpecificationException, error_messages.join
- end
- if warning_messages.any?
- warning_messages.each { |warning_message| warning warning_message }
- end
+ def validate_dependencies
+ Gem::SpecificationPolicy.new(self).validate_dependencies
end
##
# Checks to see if the files to be packaged are world-readable.
-
def validate_permissions
- return if Gem.win_platform?
-
- files.each do |file|
- next unless File.file?(file)
- next if File.stat(file).mode & 0444 == 0444
- warning "#{file} is not world-readable"
- end
-
- executables.each do |name|
- exec = File.join @bindir, name
- next unless File.file?(exec)
- next if File.stat(exec).executable?
- warning "#{exec} is not executable"
- end
+ Gem::SpecificationPolicy.new(self).validate_permissions
end
##
@@ -2946,9 +2687,13 @@ open-ended dependency on #{dep} is not recommended
# required_rubygems_version if +version+ indicates it is a
# prerelease.
- def version= version
+ def version=(version)
@version = Gem::Version.create(version)
- self.required_rubygems_version = '> 1.3.1' if @version.prerelease?
+ # 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'
+ end
invalidate_memoized_attributes
return @version
@@ -2995,26 +2740,10 @@ open-ended dependency on #{dep} is not recommended
@installed_by_version ||= nil
end
- def warning statement # :nodoc:
- @warnings += 1
-
- alert_warning statement
- end
-
def raw_require_paths # :nodoc:
@require_paths
end
- extend Gem::Deprecate
-
- # TODO:
- # deprecate :has_rdoc, :none, 2011, 10
- # deprecate :has_rdoc?, :none, 2011, 10
- # deprecate :has_rdoc=, :none, 2011, 10
- # deprecate :default_executable, :none, 2011, 10
- # deprecate :default_executable=, :none, 2011, 10
- # deprecate :file_name, :cache_file, 2011, 10
- # deprecate :full_gem_path, :cache_file, 2011, 10
end
# DOC: What is this and why is it here, randomly, at the end of this file?
diff --git a/lib/rubygems/specification_policy.rb b/lib/rubygems/specification_policy.rb
new file mode 100644
index 0000000000..4b79c1ac61
--- /dev/null
+++ b/lib/rubygems/specification_policy.rb
@@ -0,0 +1,407 @@
+require 'delegate'
+require 'uri'
+
+class Gem::SpecificationPolicy < SimpleDelegator
+ VALID_NAME_PATTERN = /\A[a-zA-Z0-9\.\-\_]+\z/.freeze # :nodoc:
+
+ SPECIAL_CHARACTERS = /\A[#{Regexp.escape('.-_')}]+/.freeze # :nodoc:
+
+ VALID_URI_PATTERN = %r{\Ahttps?:\/\/([^\s:@]+:[^\s:@]*@)?[A-Za-z\d\-]+(\.[A-Za-z\d\-]+)+\.?(:\d{1,5})?([\/?]\S*)?\z}.freeze # :nodoc:
+
+ METADATA_LINK_KEYS = %w[
+ bug_tracker_uri
+ changelog_uri
+ documentation_uri
+ homepage_uri
+ mailing_list_uri
+ source_code_uri
+ wiki_uri
+ ].freeze # :nodoc:
+
+ def initialize(specification)
+ @warnings = 0
+
+ super(specification)
+ end
+
+ ##
+ # If set to true, run packaging-specific checks, as well.
+
+ attr_accessor :packaging
+
+ ##
+ # Checks that the specification contains all required fields, and does a
+ # very basic sanity check.
+ #
+ # Raises InvalidSpecificationException if the spec does not pass the
+ # checks.
+
+ def validate(strict = false)
+ validate_nil_attributes
+
+ validate_rubygems_version
+
+ validate_required_attributes
+
+ validate_name
+
+ validate_require_paths
+
+ keep_only_files_and_directories
+
+ validate_non_files
+
+ validate_self_inclusion_in_files_list
+
+ validate_specification_version
+
+ validate_platform
+
+ validate_array_attributes
+
+ validate_authors_field
+
+ validate_metadata
+
+ validate_licenses
+
+ validate_permissions
+
+ validate_lazy_metadata
+
+ validate_values
+
+ validate_dependencies
+
+ if @warnings > 0
+ if strict
+ error "specification has warnings"
+ else
+ alert_warning help_text
+ end
+ end
+
+ true
+ end
+
+ ##
+ # Implementation for Specification#validate_metadata
+
+ def validate_metadata
+ unless Hash === metadata
+ error 'metadata must be a hash'
+ end
+
+ metadata.each do |key, value|
+ if !key.kind_of?(String)
+ error "metadata keys must be a String"
+ end
+
+ if key.size > 128
+ error "metadata key too large (#{key.size} > 128)"
+ end
+
+ if !value.kind_of?(String)
+ error "metadata values must be a String"
+ end
+
+ if value.size > 1024
+ error "metadata value too large (#{value.size} > 1024)"
+ end
+
+ if METADATA_LINK_KEYS.include? key
+ if value !~ VALID_URI_PATTERN
+ error "metadata['#{key}'] has invalid link: #{value.inspect}"
+ end
+ end
+ end
+ end
+
+ ##
+ # Implementation for Specification#validate_dependencies
+
+ def validate_dependencies # :nodoc:
+ # NOTE: see REFACTOR note in Gem::Dependency about types - this might be brittle
+ seen = Gem::Dependency::TYPES.inject({}) { |types, type| types.merge({ type => {}}) }
+
+ error_messages = []
+ warning_messages = []
+ dependencies.each do |dep|
+ if prev = seen[dep.type][dep.name]
+ error_messages << <<-MESSAGE
+duplicate dependency on #{dep}, (#{prev.requirement}) use:
+ add_#{dep.type}_dependency '#{dep.name}', '#{dep.requirement}', '#{prev.requirement}'
+ MESSAGE
+ end
+
+ seen[dep.type][dep.name] = dep
+
+ prerelease_dep = dep.requirements_list.any? do |req|
+ Gem::Requirement.new(req).prerelease?
+ end
+
+ warning_messages << "prerelease dependency on #{dep} is not recommended" if
+ prerelease_dep && !version.prerelease?
+
+ open_ended = dep.requirement.requirements.all? do |op, version|
+ not version.prerelease? and (op == '>' or op == '>=')
+ end
+
+ if open_ended
+ op, dep_version = dep.requirement.requirements.first
+
+ segments = dep_version.segments
+
+ base = segments.first 2
+
+ recommendation = if (op == '>' || op == '>=') && segments == [0]
+ " use a bounded requirement, such as '~> x.y'"
+ else
+ bugfix = if op == '>'
+ ", '> #{dep_version}'"
+ elsif op == '>=' and base != segments
+ ", '>= #{dep_version}'"
+ end
+
+ " if #{dep.name} is semantically versioned, use:\n" \
+ " add_#{dep.type}_dependency '#{dep.name}', '~> #{base.join '.'}'#{bugfix}"
+ end
+
+ warning_messages << ["open-ended dependency on #{dep} is not recommended", recommendation].join("\n") + "\n"
+ end
+ end
+ if error_messages.any?
+ error error_messages.join
+ end
+ if warning_messages.any?
+ warning_messages.each { |warning_message| warning warning_message }
+ end
+ end
+
+ ##
+ # Issues a warning for each file to be packaged which is world-readable.
+ #
+ # Implementation for Specification#validate_permissions
+
+ def validate_permissions
+ return if Gem.win_platform?
+
+ files.each do |file|
+ next unless File.file?(file)
+ next if File.stat(file).mode & 0444 == 0444
+ warning "#{file} is not world-readable"
+ end
+
+ executables.each do |name|
+ exec = File.join bindir, name
+ next unless File.file?(exec)
+ next if File.stat(exec).executable?
+ warning "#{exec} is not executable"
+ end
+ end
+
+ private
+
+ def validate_nil_attributes
+ nil_attributes = Gem::Specification.non_nil_attributes.select do |attrname|
+ __getobj__.instance_variable_get("@#{attrname}").nil?
+ end
+ return if nil_attributes.empty?
+ error "#{nil_attributes.join ', '} must not be nil"
+ end
+
+ def validate_rubygems_version
+ return unless packaging
+ return if rubygems_version == Gem::VERSION
+
+ error "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}"
+ end
+
+ def validate_required_attributes
+ Gem::Specification.required_attributes.each do |symbol|
+ unless send symbol
+ error "missing value for attribute #{symbol}"
+ end
+ end
+ end
+
+ def validate_name
+ if !name.is_a?(String)
+ error "invalid value for attribute name: \"#{name.inspect}\" must be a string"
+ elsif name !~ /[a-zA-Z]/
+ error "invalid value for attribute name: #{name.dump} must include at least one letter"
+ elsif name !~ VALID_NAME_PATTERN
+ error "invalid value for attribute name: #{name.dump} can only include letters, numbers, dashes, and underscores"
+ elsif name =~ SPECIAL_CHARACTERS
+ error "invalid value for attribute name: #{name.dump} can not begin with a period, dash, or underscore"
+ end
+ end
+
+ def validate_require_paths
+ return unless raw_require_paths.empty?
+
+ error 'specification must have at least one require_path'
+ end
+
+ def validate_non_files
+ return unless packaging
+ non_files = files.reject {|x| File.file?(x) || File.symlink?(x)}
+
+ unless non_files.empty?
+ error "[\"#{non_files.join "\", \""}\"] are not files"
+ end
+ end
+
+ def validate_self_inclusion_in_files_list
+ return unless files.include?(file_name)
+
+ error "#{full_name} contains itself (#{file_name}), check your files list"
+ end
+
+ def validate_specification_version
+ return if specification_version.is_a?(Integer)
+
+ error 'specification_version must be an Integer (did you mean version?)'
+ end
+
+ def validate_platform
+ case platform
+ when Gem::Platform, Gem::Platform::RUBY # ok
+ else
+ error "invalid platform #{platform.inspect}, see Gem::Platform"
+ end
+ end
+
+ def validate_array_attributes
+ Gem::Specification.array_attributes.each do |field|
+ validate_array_attribute(field)
+ end
+ end
+
+ def validate_array_attribute(field)
+ val = self.send(field)
+ klass = case field
+ when :dependencies then
+ Gem::Dependency
+ else
+ String
+ end
+
+ unless Array === val and val.all? {|x| x.kind_of?(klass)}
+ raise(Gem::InvalidSpecificationException,
+ "#{field} must be an Array of #{klass}")
+ end
+ end
+
+ def validate_authors_field
+ return unless authors.empty?
+
+ error "authors may not be empty"
+ end
+
+ def validate_licenses
+ licenses.each { |license|
+ if license.length > 64
+ error "each license must be 64 characters or less"
+ end
+
+ if !Gem::Licenses.match?(license)
+ suggestions = Gem::Licenses.suggestions(license)
+ message = <<-warning
+license value '#{license}' is invalid. Use a license identifier from
+http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license.
+ warning
+ message += "Did you mean #{suggestions.map { |s| "'#{s}'"}.join(', ')}?\n" unless suggestions.nil?
+ warning(message)
+ end
+ }
+
+ warning <<-warning if licenses.empty?
+licenses is empty, but is recommended. Use a license identifier from
+http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license.
+ warning
+ end
+
+ LAZY = '"FIxxxXME" or "TOxxxDO"'.gsub(/xxx/, '')
+ LAZY_PATTERN = /FI XME|TO DO/x.freeze
+ HOMEPAGE_URI_PATTERN = /\A[a-z][a-z\d+.-]*:/i.freeze
+
+ def validate_lazy_metadata
+ unless authors.grep(LAZY_PATTERN).empty?
+ error "#{LAZY} is not an author"
+ end
+
+ unless Array(email).grep(LAZY_PATTERN).empty?
+ error "#{LAZY} is not an email"
+ end
+
+ if description =~ LAZY_PATTERN
+ error "#{LAZY} is not a description"
+ end
+
+ if summary =~ LAZY_PATTERN
+ error "#{LAZY} is not a summary"
+ end
+
+ # Make sure a homepage is valid HTTP/HTTPS URI
+ if homepage and not homepage.empty?
+ begin
+ homepage_uri = URI.parse(homepage)
+ unless [URI::HTTP, URI::HTTPS].member? homepage_uri.class
+ error "\"#{homepage}\" is not a valid HTTP URI"
+ end
+ rescue URI::InvalidURIError
+ error "\"#{homepage}\" is not a valid HTTP URI"
+ end
+ end
+ end
+
+ def validate_values
+ %w[author homepage summary files].each do |attribute|
+ validate_attribute_present(attribute)
+ end
+
+ if description == summary
+ warning "description and summary are identical"
+ end
+
+ # TODO: raise at some given date
+ warning "deprecated autorequire specified" if autorequire
+
+ executables.each do |executable|
+ validate_shebang_line_in(executable)
+ end
+
+ files.select { |f| File.symlink?(f) }.each do |file|
+ warning "#{file} is a symlink, which is not supported on all platforms"
+ end
+ end
+
+ def validate_attribute_present(attribute)
+ value = self.send attribute
+ warning("no #{attribute} specified") if value.nil? || value.empty?
+ end
+
+ def validate_shebang_line_in(executable)
+ executable_path = File.join(bindir, executable)
+ return if File.read(executable_path, 2) == '#!'
+
+ warning "#{executable_path} is missing #! line"
+ end
+
+ def warning(statement) # :nodoc:
+ @warnings += 1
+
+ alert_warning statement
+ end
+
+ def error(statement) # :nodoc:
+ raise Gem::InvalidSpecificationException, statement
+ ensure
+ alert_warning help_text
+ end
+
+ def help_text # :nodoc:
+ "See http://guides.rubygems.org/specification-reference/ for help"
+ end
+end
diff --git a/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA.pem b/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA.pem
new file mode 100644
index 0000000000..f4ce4ca43d
--- /dev/null
+++ b/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
+MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
+aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
+jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
+xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
+1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
+snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
+U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
+9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
+BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
+AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
+yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
+38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
+AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
+DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
+HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
+-----END CERTIFICATE-----
diff --git a/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA_R3.pem b/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA_R3.pem
new file mode 100644
index 0000000000..8afb219058
--- /dev/null
+++ b/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA_R3.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
+A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
+Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
+MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
+A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
+RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
+gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
+KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
+QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
+XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
+DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
+LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
+RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
+jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
+6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
+mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
+Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
+WD9f
+-----END CERTIFICATE-----
diff --git a/lib/rubygems/stub_specification.rb b/lib/rubygems/stub_specification.rb
index 1c56a102c5..022da9185d 100644
--- a/lib/rubygems/stub_specification.rb
+++ b/lib/rubygems/stub_specification.rb
@@ -6,14 +6,10 @@
class Gem::StubSpecification < Gem::BasicSpecification
# :nodoc:
- PREFIX = "# stub: "
+ PREFIX = "# stub: ".freeze
- OPEN_MODE = # :nodoc:
- if Object.const_defined? :Encoding then
- 'r:UTF-8:-'
- else
- 'r'
- end
+ # :nodoc:
+ OPEN_MODE = 'r:UTF-8:-'.freeze
class StubLine # :nodoc: all
attr_reader :name, :version, :platform, :require_paths, :extensions,
@@ -26,7 +22,7 @@ class Gem::StubSpecification < Gem::BasicSpecification
'lib' => 'lib'.freeze,
'test' => 'test'.freeze,
'ext' => 'ext'.freeze,
- }
+ }.freeze
# These are common require path lists. This hash is used to optimize
# and consolidate require_path objects. Most specs just specify "lib"
@@ -34,12 +30,17 @@ class Gem::StubSpecification < Gem::BasicSpecification
# a require path list for that case.
REQUIRE_PATH_LIST = { # :nodoc:
'lib' => ['lib'].freeze
- }
+ }.freeze
- def initialize data, extensions
+ def initialize(data, extensions)
parts = data[PREFIX.length..-1].split(" ".freeze, 4)
@name = parts[0].freeze
- @version = Gem::Version.new parts[1]
+ @version = if Gem::Version.correct?(parts[1])
+ Gem::Version.new(parts[1])
+ else
+ Gem::Version.new(0)
+ end
+
@platform = Gem::Platform.new parts[2]
@extensions = extensions
@full_name = if platform == Gem::Platform::RUBY
@@ -55,17 +56,17 @@ class Gem::StubSpecification < Gem::BasicSpecification
end
end
- def self.default_gemspec_stub filename, base_dir, gems_dir
+ def self.default_gemspec_stub(filename, base_dir, gems_dir)
new filename, base_dir, gems_dir, true
end
- def self.gemspec_stub filename, base_dir, gems_dir
+ def self.gemspec_stub(filename, base_dir, gems_dir)
new filename, base_dir, gems_dir, false
end
attr_reader :base_dir, :gems_dir
- def initialize filename, base_dir, gems_dir, default_gem
+ def initialize(filename, base_dir, gems_dir, default_gem)
super()
filename.untaint
@@ -108,11 +109,13 @@ class Gem::StubSpecification < Gem::BasicSpecification
unless @data
begin
saved_lineno = $.
+
+ # TODO It should be use `File.open`, but bundler-1.16.1 example expects Kernel#open.
open loaded_from, OPEN_MODE do |file|
begin
file.readline # discard encoding line
stubline = file.readline.chomp
- if stubline.start_with?(PREFIX) then
+ if stubline.start_with?(PREFIX)
extensions = if /\A#{PREFIX}/ =~ file.readline.chomp
$'.split "\0"
else
@@ -182,10 +185,9 @@ class Gem::StubSpecification < Gem::BasicSpecification
# The full Gem::Specification for this gem, loaded from evalling its gemspec
def to_spec
- @spec ||= if @data then
- Gem.loaded_specs.values.find { |spec|
- spec.name == name and spec.version == version
- }
+ @spec ||= if @data
+ loaded = Gem.loaded_specs[name]
+ loaded if loaded && loaded.version == version
end
@spec ||= Gem::Specification.load(loaded_from)
diff --git a/lib/rubygems/test_case.rb b/lib/rubygems/test_case.rb
index f7ae97cd8d..a93f749240 100644
--- a/lib/rubygems/test_case.rb
+++ b/lib/rubygems/test_case.rb
@@ -2,7 +2,7 @@
# TODO: $SAFE = 1
begin
- gem 'minitest', '~> 4.0'
+ gem 'minitest', '~> 5.0'
rescue NoMethodError, Gem::LoadError
# for ruby tests
end
@@ -13,11 +13,30 @@ else
require 'rubygems'
end
+# If bundler gemspec exists, add to stubs
+bundler_gemspec = File.expand_path("../../../bundler/bundler.gemspec", __FILE__)
+if File.exist?(bundler_gemspec)
+ Gem::Specification.dirs.unshift File.dirname(bundler_gemspec)
+ Gem::Specification.class_variable_set :@@stubs, nil
+ Gem::Specification.stubs
+ Gem::Specification.dirs.shift
+end
+
begin
gem 'minitest'
rescue Gem::LoadError
end
+begin
+ require 'simplecov'
+ SimpleCov.start do
+ add_filter "/test/"
+ add_filter "/bundler/"
+ add_filter "/lib/rubygems/resolver/molinillo"
+ end
+rescue LoadError
+end
+
# We have to load these up front because otherwise we'll try to load
# them while we're testing rubygems, and thus we can't actually load them.
unless Gem::Dependency.new('rdoc', '>= 3.10').matching_specs.empty?
@@ -25,6 +44,9 @@ unless Gem::Dependency.new('rdoc', '>= 3.10').matching_specs.empty?
gem 'json'
end
+if Gem::USE_BUNDLER_FOR_GEMDEPS
+ require 'bundler'
+end
require 'minitest/autorun'
require 'rubygems/deprecate'
@@ -62,7 +84,7 @@ module Gem
# Allows setting path to Ruby. This method is available when requiring
# 'rubygems/test_case'
- def self.ruby= ruby
+ def self.ruby=(ruby)
@ruby = ruby
end
@@ -83,7 +105,9 @@ end
#
# Tests are always run at a safe level of 1.
-class Gem::TestCase < MiniTest::Unit::TestCase
+class Gem::TestCase < (defined?(Minitest::Test) ? Minitest::Test : MiniTest::Unit::TestCase)
+
+ extend Gem::Deprecate
attr_accessor :fetcher # :nodoc:
@@ -91,7 +115,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
attr_accessor :uri # :nodoc:
- def assert_activate expected, *specs
+ def assert_activate(expected, *specs)
specs.each do |spec|
case spec
when String then
@@ -109,7 +133,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
end
# TODO: move to minitest
- def assert_path_exists path, msg = nil
+ def assert_path_exists(path, msg = nil)
msg = message(msg) { "Expected path '#{path}' to exist" }
assert File.exist?(path), msg
end
@@ -118,13 +142,13 @@ class Gem::TestCase < MiniTest::Unit::TestCase
# Sets the ENABLE_SHARED entry in RbConfig::CONFIG to +value+ and restores
# the original value when the block ends
- def enable_shared value
+ def enable_shared(value)
enable_shared = RbConfig::CONFIG['ENABLE_SHARED']
RbConfig::CONFIG['ENABLE_SHARED'] = value
yield
ensure
- if enable_shared then
+ if enable_shared
RbConfig::CONFIG['enable_shared'] = enable_shared
else
RbConfig::CONFIG.delete 'enable_shared'
@@ -132,7 +156,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
end
# TODO: move to minitest
- def refute_path_exists path, msg = nil
+ def refute_path_exists(path, msg = nil)
msg = message(msg) { "Expected path '#{path}' to not exist" }
refute File.exist?(path), msg
end
@@ -222,6 +246,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
@orig_gem_vendor = ENV['GEM_VENDOR']
@orig_gem_spec_cache = ENV['GEM_SPEC_CACHE']
@orig_rubygems_gemdeps = ENV['RUBYGEMS_GEMDEPS']
+ @orig_bundle_gemfile = ENV['BUNDLE_GEMFILE']
@orig_rubygems_host = ENV['RUBYGEMS_HOST']
ENV.keys.find_all { |k| k.start_with?('GEM_REQUIREMENT_') }.each do |k|
ENV.delete k
@@ -229,15 +254,24 @@ class Gem::TestCase < MiniTest::Unit::TestCase
@orig_gem_env_requirements = ENV.to_hash
ENV['GEM_VENDOR'] = nil
+ ENV['SOURCE_DATE_EPOCH'] = nil
@current_dir = Dir.pwd
@fetcher = nil
- @ui = Gem::MockGemUi.new
+
+ if Gem::USE_BUNDLER_FOR_GEMDEPS
+ Bundler.ui = Bundler::UI::Silent.new
+ end
+ @back_ui = Gem::DefaultUserInteraction.ui
+ @ui = Gem::MockGemUi.new
+ # This needs to be a new instance since we call use_ui(@ui) when we want to
+ # capture output
+ Gem::DefaultUserInteraction.ui = Gem::MockGemUi.new
tmpdir = File.expand_path Dir.tmpdir
tmpdir.untaint
- if ENV['KEEP_FILES'] then
+ if ENV['KEEP_FILES']
@tempdir = File.join(tmpdir, "test_rubygems_#{$$}.#{Time.now.to_i}")
else
@tempdir = File.join(tmpdir, "test_rubygems_#{$$}")
@@ -271,7 +305,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
@userhome = File.join @tempdir, 'userhome'
ENV["GEM_SPEC_CACHE"] = File.join @tempdir, 'spec_cache'
- @orig_ruby = if ENV['RUBY'] then
+ @orig_ruby = if ENV['RUBY']
ruby = Gem.ruby
Gem.ruby = ENV['RUBY']
ruby
@@ -283,7 +317,16 @@ class Gem::TestCase < MiniTest::Unit::TestCase
@orig_LOAD_PATH = $LOAD_PATH.dup
$LOAD_PATH.map! { |s|
- (expand_path = File.expand_path(s)) == s ? s : expand_path.untaint
+ expand_path = File.expand_path(s)
+ if expand_path != s
+ expand_path.untaint
+ if s.instance_variable_defined?(:@gem_prelude_index)
+ expand_path.instance_variable_set(:@gem_prelude_index, expand_path)
+ end
+ expand_path.freeze if s.frozen?
+ s = expand_path
+ end
+ s
}
Dir.chdir @tempdir
@@ -323,6 +366,9 @@ class Gem::TestCase < MiniTest::Unit::TestCase
Gem.loaded_specs.clear
Gem.clear_default_specs
Gem::Specification.unresolved_deps.clear
+ if Gem::USE_BUNDLER_FOR_GEMDEPS
+ Bundler.reset!
+ end
Gem.configuration.verbose = true
Gem.configuration.update_sources = true
@@ -346,6 +392,11 @@ class Gem::TestCase < MiniTest::Unit::TestCase
util_set_arch 'i686-darwin8.10.1'
end
+ @orig_hooks = {}
+ %w[post_install_hooks done_installing_hooks post_uninstall_hooks pre_uninstall_hooks pre_install_hooks pre_reset_hooks post_reset_hooks post_build_hooks].each do |name|
+ @orig_hooks[name] = Gem.send(name).dup
+ end
+
@marshal_version = "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
@orig_LOADED_FEATURES = $LOADED_FEATURES.dup
end
@@ -376,7 +427,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
end
RbConfig::CONFIG['arch'] = @orig_arch
- if defined? Gem::RemoteFetcher then
+ if defined? Gem::RemoteFetcher
Gem::RemoteFetcher.fetcher = nil
end
@@ -394,11 +445,12 @@ class Gem::TestCase < MiniTest::Unit::TestCase
ENV['GEM_VENDOR'] = @orig_gem_vendor
ENV['GEM_SPEC_CACHE'] = @orig_gem_spec_cache
ENV['RUBYGEMS_GEMDEPS'] = @orig_rubygems_gemdeps
+ ENV['BUNDLE_GEMFILE'] = @orig_bundle_gemfile
ENV['RUBYGEMS_HOST'] = @orig_rubygems_host
Gem.ruby = @orig_ruby if @orig_ruby
- if @orig_ENV_HOME then
+ if @orig_ENV_HOME
ENV['HOME'] = @orig_ENV_HOME
else
ENV.delete 'HOME'
@@ -410,6 +462,13 @@ class Gem::TestCase < MiniTest::Unit::TestCase
Gem::Specification._clear_load_cache
Gem::Specification.unresolved_deps.clear
+ Gem::refresh
+
+ @orig_hooks.each do |name, hooks|
+ Gem.send(name).replace hooks
+ end
+
+ @back_ui.close
end
def common_installer_setup
@@ -455,7 +514,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
#
# Yields the +specification+ to the block, if given
- def git_gem name = 'a', version = 1
+ def git_gem(name = 'a', version = 1)
have_git?
directory = File.join 'git', name
@@ -469,14 +528,14 @@ class Gem::TestCase < MiniTest::Unit::TestCase
gemspec = "#{name}.gemspec"
- open File.join(directory, gemspec), 'w' do |io|
+ File.open File.join(directory, gemspec), 'w' do |io|
io.write git_spec.to_ruby
end
head = nil
Dir.chdir directory do
- unless File.exist? '.git' then
+ unless File.exist? '.git'
system @git, 'init', '--quiet'
system @git, 'config', 'user.name', 'RubyGems Tests'
system @git, 'config', 'user.email', 'rubygems@example'
@@ -484,7 +543,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
system @git, 'add', gemspec
system @git, 'commit', '-a', '-m', 'a non-empty commit message', '--quiet'
- head = Gem::Util.popen('git', 'rev-parse', 'master').strip
+ head = Gem::Util.popen(@git, 'rev-parse', 'master').strip
end
return name, git_spec.version, directory, head
@@ -499,7 +558,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
skip 'cannot find git executable, use GIT environment variable to set'
end
- def in_path? executable # :nodoc:
+ def in_path?(executable) # :nodoc:
return true if %r%\A([A-Z]:|/)% =~ executable and File.exist? executable
ENV['PATH'].split(File::PATH_SEPARATOR).any? do |directory|
@@ -510,12 +569,12 @@ class Gem::TestCase < MiniTest::Unit::TestCase
##
# Builds and installs the Gem::Specification +spec+
- def install_gem spec, options = {}
+ def install_gem(spec, options = {})
require 'rubygems/installer'
gem = File.join @tempdir, "gems", "#{spec.full_name}.gem"
- unless File.exist? gem then
+ unless File.exist? gem
use_ui Gem::MockGemUi.new do
Dir.chdir @tempdir do
Gem::Package.build spec
@@ -531,17 +590,17 @@ class Gem::TestCase < MiniTest::Unit::TestCase
##
# Builds and installs the Gem::Specification +spec+ into the user dir
- def install_gem_user spec
+ def install_gem_user(spec)
install_gem spec, :user_install => true
end
##
# Uninstalls the Gem::Specification +spec+
- def uninstall_gem spec
+ def uninstall_gem(spec)
require 'rubygems/uninstaller'
Class.new(Gem::Uninstaller) {
- def ask_if_ok spec
+ def ask_if_ok(spec)
true
end
}.new(spec.name, :executables => true, :user_install => true).uninstall
@@ -565,7 +624,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
def mu_pp(obj)
s = String.new
s = PP.pp obj, s
- s = s.force_encoding(Encoding.default_external) if defined? Encoding
+ s = s.force_encoding(Encoding.default_external)
s.chomp
end
@@ -573,7 +632,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
# Reads a Marshal file at +path+
def read_cache(path)
- open path.dup.untaint, 'rb' do |io|
+ File.open path.dup.untaint, 'rb' do |io|
Marshal.load io.read
end
end
@@ -593,7 +652,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
dir = File.dirname path
FileUtils.mkdir_p dir unless File.directory? dir
- open path, 'wb' do |io|
+ File.open path, 'wb' do |io|
yield io if block_given?
end
@@ -644,11 +703,13 @@ class Gem::TestCase < MiniTest::Unit::TestCase
end
##
- # TODO: remove in RubyGems 3.0
+ # TODO: remove in RubyGems 4.0
- def quick_spec name, version = '2' # :nodoc:
+ def quick_spec(name, version = '2') # :nodoc:
util_spec name, version
end
+ deprecate :quick_spec, :util_spec, 2018, 12
+
##
# Builds a gem from +spec+ and places it in <tt>File.join @gemhome,
@@ -708,7 +769,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
install_default_specs(*specs)
specs.each do |spec|
- open spec.loaded_from, 'w' do |io|
+ File.open spec.loaded_from, 'w' do |io|
io.write spec.to_ruby_for_cache
end
end
@@ -737,15 +798,18 @@ class Gem::TestCase < MiniTest::Unit::TestCase
old_loaded_features = $LOADED_FEATURES.dup
yield
ensure
+ prefix = File.dirname(__FILE__) + "/"
+ new_features = ($LOADED_FEATURES - old_loaded_features)
+ old_loaded_features.concat(new_features.select {|f| f.rindex(prefix, 0)})
$LOADED_FEATURES.replace old_loaded_features
end
##
# new_spec is deprecated as it is never used.
#
- # TODO: remove in RubyGems 3.0
+ # TODO: remove in RubyGems 4.0
- def new_spec name, version, deps = nil, *files # :nodoc:
+ def new_spec(name, version, deps = nil, *files) # :nodoc:
require 'rubygems/specification'
spec = Gem::Specification.new do |s|
@@ -769,7 +833,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
spec.loaded_from = spec.spec_file
- unless files.empty? then
+ unless files.empty?
write_file spec.spec_file do |io|
io.write spec.to_ruby_for_cache
end
@@ -784,6 +848,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
spec
end
+ deprecate :new_spec, :none, 2018, 12
def new_default_spec(name, version, deps = nil, *files)
spec = util_spec name, version, deps
@@ -808,7 +873,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
# Creates a spec with +name+, +version+. +deps+ can specify the dependency
# or a +block+ can be given for full customization of the specification.
- def util_spec name, version = 2, deps = nil # :yields: specification
+ def util_spec(name, version = 2, deps = nil, *files) # :yields: specification
raise "deps or block, not both" if deps and block_given?
spec = Gem::Specification.new do |s|
@@ -821,10 +886,12 @@ class Gem::TestCase < MiniTest::Unit::TestCase
s.summary = "this is a summary"
s.description = "This is a test description"
+ s.files.push(*files) unless files.empty?
+
yield s if block_given?
end
- if deps then
+ if deps
# Since Hash#each is unordered in 1.8, sort the keys and iterate that
# way so the tests are deterministic on all implementations.
deps.keys.sort.each do |n|
@@ -832,6 +899,19 @@ class Gem::TestCase < MiniTest::Unit::TestCase
end
end
+ unless files.empty?
+ write_file spec.spec_file do |io|
+ io.write spec.to_ruby_for_cache
+ end
+
+ util_build_gem spec
+
+ cache_file = File.join @tempdir, 'gems', "#{spec.full_name}.gem"
+ FileUtils.mkdir_p File.dirname cache_file
+ FileUtils.mv spec.cache_file, cache_file
+ FileUtils.rm spec.spec_file
+ end
+
Gem::Specification.reset
return spec
@@ -847,7 +927,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
# TODO: deprecate
raise "deps or block, not both" if deps and block
- if deps then
+ if deps
block = proc do |s|
# Since Hash#each is unordered in 1.8, sort
# the keys and iterate that way so the tests are
@@ -1043,7 +1123,7 @@ Also, a list:
end
# HACK for test_download_to_cache
- unless Gem::RemoteFetcher === @fetcher then
+ unless Gem::RemoteFetcher === @fetcher
v = Gem.marshal_version
specs = all.map { |spec| spec.name_tuple }
@@ -1082,35 +1162,51 @@ Also, a list:
Zlib::Deflate.deflate data
end
- def util_set_RUBY_VERSION(version, patchlevel = nil, revision = nil)
+ def util_set_RUBY_VERSION(version, patchlevel = nil, revision = nil, description = nil, engine = "ruby", engine_version = nil)
if Gem.instance_variables.include? :@ruby_version or
- Gem.instance_variables.include? '@ruby_version' then
+ Gem.instance_variables.include? '@ruby_version'
Gem.send :remove_instance_variable, :@ruby_version
end
- @RUBY_VERSION = RUBY_VERSION
- @RUBY_PATCHLEVEL = RUBY_PATCHLEVEL if defined?(RUBY_PATCHLEVEL)
- @RUBY_REVISION = RUBY_REVISION if defined?(RUBY_REVISION)
+ @RUBY_VERSION = RUBY_VERSION
+ @RUBY_PATCHLEVEL = RUBY_PATCHLEVEL if defined?(RUBY_PATCHLEVEL)
+ @RUBY_REVISION = RUBY_REVISION if defined?(RUBY_REVISION)
+ @RUBY_DESCRIPTION = RUBY_DESCRIPTION if defined?(RUBY_DESCRIPTION)
+ @RUBY_ENGINE = RUBY_ENGINE
+ @RUBY_ENGINE_VERSION = RUBY_ENGINE_VERSION if defined?(RUBY_ENGINE_VERSION)
- Object.send :remove_const, :RUBY_VERSION
- Object.send :remove_const, :RUBY_PATCHLEVEL if defined?(RUBY_PATCHLEVEL)
- Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
+ util_clear_RUBY_VERSION
- Object.const_set :RUBY_VERSION, version
- Object.const_set :RUBY_PATCHLEVEL, patchlevel if patchlevel
- Object.const_set :RUBY_REVISION, revision if revision
+ Object.const_set :RUBY_VERSION, version
+ Object.const_set :RUBY_PATCHLEVEL, patchlevel if patchlevel
+ Object.const_set :RUBY_REVISION, revision if revision
+ Object.const_set :RUBY_DESCRIPTION, description if description
+ Object.const_set :RUBY_ENGINE, engine
+ Object.const_set :RUBY_ENGINE_VERSION, engine_version if engine_version
end
def util_restore_RUBY_VERSION
- Object.send :remove_const, :RUBY_VERSION
- Object.send :remove_const, :RUBY_PATCHLEVEL if defined?(RUBY_PATCHLEVEL)
- Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
+ util_clear_RUBY_VERSION
- Object.const_set :RUBY_VERSION, @RUBY_VERSION
- Object.const_set :RUBY_PATCHLEVEL, @RUBY_PATCHLEVEL if
+ Object.const_set :RUBY_VERSION, @RUBY_VERSION
+ Object.const_set :RUBY_PATCHLEVEL, @RUBY_PATCHLEVEL if
defined?(@RUBY_PATCHLEVEL)
- Object.const_set :RUBY_REVISION, @RUBY_REVISION if
+ Object.const_set :RUBY_REVISION, @RUBY_REVISION if
defined?(@RUBY_REVISION)
+ Object.const_set :RUBY_DESCRIPTION, @RUBY_DESCRIPTION if
+ defined?(@RUBY_DESCRIPTION)
+ Object.const_set :RUBY_ENGINE, @RUBY_ENGINE
+ Object.const_set :RUBY_ENGINE_VERSION, @RUBY_ENGINE_VERSION if
+ defined?(@RUBY_ENGINE_VERSION)
+ end
+
+ def util_clear_RUBY_VERSION
+ Object.send :remove_const, :RUBY_VERSION
+ Object.send :remove_const, :RUBY_PATCHLEVEL if defined?(RUBY_PATCHLEVEL)
+ Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
+ Object.send :remove_const, :RUBY_DESCRIPTION if defined?(RUBY_DESCRIPTION)
+ Object.send :remove_const, :RUBY_ENGINE
+ Object.send :remove_const, :RUBY_ENGINE_VERSION if defined?(RUBY_ENGINE_VERSION)
end
##
@@ -1240,14 +1336,31 @@ Also, a list:
end
end
+ class << self
+ # :nodoc:
+ ##
+ # Return the join path, with escaping backticks, dollars, and
+ # double-quotes. Unlike `shellescape`, equal-sign is not escaped.
+ private
+ def escape_path(*path)
+ path = File.join(*path)
+ if %r'\A[-+:/=@,.\w]+\z' =~ path
+ path
+ else
+ "\"#{path.gsub(/[`$"]/, '\\&')}\""
+ end
+ end
+ end
+
@@ruby = rubybin
- @@good_rake = "#{rubybin} \"#{File.expand_path('../../../test/rubygems/good_rake.rb', __FILE__)}\""
- @@bad_rake = "#{rubybin} \"#{File.expand_path('../../../test/rubygems/bad_rake.rb', __FILE__)}\""
+ gempath = File.expand_path('../../../test/rubygems', __FILE__)
+ @@good_rake = "#{rubybin} #{escape_path(gempath, 'good_rake.rb')}"
+ @@bad_rake = "#{rubybin} #{escape_path(gempath, 'bad_rake.rb')}"
##
# Construct a new Gem::Dependency.
- def dep name, *requirements
+ def dep(name, *requirements)
Gem::Dependency.new name, *requirements
end
@@ -1256,10 +1369,10 @@ Also, a list:
# Gem::Dependency +dep+, a +from_name+ and +from_version+ requesting the
# dependency and a +parent+ DependencyRequest
- def dependency_request dep, from_name, from_version, parent = nil
+ def dependency_request(dep, from_name, from_version, parent = nil)
remote = Gem::Source.new @uri
- unless parent then
+ unless parent
parent_dep = dep from_name, from_version
parent = Gem::Resolver::DependencyRequest.new parent_dep, nil
end
@@ -1274,7 +1387,7 @@ Also, a list:
##
# Constructs a new Gem::Requirement.
- def req *requirements
+ def req(*requirements)
return requirements.first if Gem::Requirement === requirements.first
Gem::Requirement.create requirements
end
@@ -1282,7 +1395,7 @@ Also, a list:
##
# Constructs a new Gem::Specification.
- def spec name, version, &block
+ def spec(name, version, &block)
Gem::Specification.new name, v(version), &block
end
@@ -1306,7 +1419,7 @@ Also, a list:
# end
# end
- def spec_fetcher repository = @gem_repo
+ def spec_fetcher(repository = @gem_repo)
Gem::TestCase::SpecFetcherSetup.declare self, repository do |spec_fetcher_setup|
yield spec_fetcher_setup if block_given?
end
@@ -1315,7 +1428,7 @@ Also, a list:
##
# Construct a new Gem::Version.
- def v string
+ def v(string)
Gem::Version.create string
end
@@ -1325,7 +1438,7 @@ Also, a list:
#
# Yields the +specification+ to the block, if given
- def vendor_gem name = 'a', version = 1
+ def vendor_gem(name = 'a', version = 1)
directory = File.join 'vendor', name
FileUtils.mkdir_p directory
@@ -1334,17 +1447,17 @@ Also, a list:
end
##
- # create_gemspec creates gem specification in given +direcotry+ or '.'
+ # create_gemspec creates gem specification in given +directory+ or '.'
# for the given +name+ and +version+.
#
# Yields the +specification+ to the block, if given
- def save_gemspec name = 'a', version = 1, directory = '.'
+ def save_gemspec(name = 'a', version = 1, directory = '.')
vendor_spec = Gem::Specification.new name, version do |specification|
yield specification if block_given?
end
- open File.join(directory, "#{name}.gemspec"), 'w' do |io|
+ File.open File.join(directory, "#{name}.gemspec"), 'w' do |io|
io.write vendor_spec.to_ruby
end
@@ -1376,7 +1489,7 @@ Also, a list:
##
# Adds +spec+ to this set.
- def add spec
+ def add(spec)
@specs << spec
end
@@ -1400,7 +1513,7 @@ Also, a list:
# Loads a Gem::Specification from this set which has the given +name+,
# version +ver+, +platform+. The +source+ is ignored.
- def load_spec name, ver, platform, source
+ def load_spec(name, ver, platform, source)
dep = Gem::Dependency.new name, ver
spec = find_spec dep
@@ -1409,14 +1522,14 @@ Also, a list:
end
end
- def prefetch reqs # :nodoc:
+ def prefetch(reqs) # :nodoc:
end
end
##
# Loads certificate named +cert_name+ from <tt>test/rubygems/</tt>.
- def self.load_cert cert_name
+ def self.load_cert(cert_name)
cert_file = cert_path cert_name
cert = File.read cert_file
@@ -1428,8 +1541,8 @@ Also, a list:
# Returns the path to the certificate named +cert_name+ from
# <tt>test/rubygems/</tt>.
- def self.cert_path cert_name
- if 32 == (Time.at(2**32) rescue 32) then
+ def self.cert_path(cert_name)
+ if 32 == (Time.at(2**32) rescue 32)
cert_file =
File.expand_path "../../../test/rubygems/#{cert_name}_cert_32.pem",
__FILE__
@@ -1443,7 +1556,7 @@ Also, a list:
##
# Loads an RSA private key named +key_name+ with +passphrase+ in <tt>test/rubygems/</tt>
- def self.load_key key_name, passphrase = nil
+ def self.load_key(key_name, passphrase = nil)
key_file = key_path key_name
key = File.read key_file
@@ -1454,14 +1567,14 @@ Also, a list:
##
# Returns the path to the key named +key_name+ from <tt>test/rubygems</tt>
- def self.key_path key_name
+ def self.key_path(key_name)
File.expand_path "../../../test/rubygems/#{key_name}_key.pem", __FILE__
end
# :stopdoc:
# only available in RubyGems tests
- PRIVATE_KEY_PASSPHRASE = 'Foo bar'
+ PRIVATE_KEY_PASSPHRASE = 'Foo bar'.freeze
begin
PRIVATE_KEY = load_key 'private'
@@ -1498,6 +1611,8 @@ end
begin
gem 'rdoc'
require 'rdoc'
+
+ require 'rubygems/rdoc'
rescue LoadError, Gem::LoadError
end
@@ -1514,3 +1629,4 @@ tmpdirs << (ENV['GEM_PATH'] = Dir.mktmpdir("path"))
pid = $$
END {tmpdirs.each {|dir| Dir.rmdir(dir)} if $$ == pid}
Gem.clear_paths
+Gem.loaded_specs.clear
diff --git a/lib/rubygems/test_utilities.rb b/lib/rubygems/test_utilities.rb
index 686916ea02..d531239812 100644
--- a/lib/rubygems/test_utilities.rb
+++ b/lib/rubygems/test_utilities.rb
@@ -25,23 +25,17 @@ class Gem::FakeFetcher
attr_reader :data
attr_reader :last_request
- attr_reader :api_endpoints
attr_accessor :paths
def initialize
@data = {}
@paths = []
- @api_endpoints = {}
- end
-
- def api_endpoint(uri)
- @api_endpoints[uri] || uri
end
def find_data(path)
return File.read path.path if URI === path and 'file' == path.scheme
- if URI === path and "URI::#{path.scheme.upcase}" != path.class.name then
+ if URI === path and "URI::#{path.scheme.upcase}" != path.class.name
raise ArgumentError,
"mismatch for scheme #{path.scheme} and class #{path.class}"
end
@@ -50,28 +44,28 @@ class Gem::FakeFetcher
@paths << path
raise ArgumentError, 'need full URI' unless path =~ %r'^https?://'
- unless @data.key? path then
+ unless @data.key? path
raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path)
end
@data[path]
end
- def fetch_path path, mtime = nil, head = false
+ def fetch_path(path, mtime = nil, head = false)
data = find_data(path)
- if data.respond_to?(:call) then
+ if data.respond_to?(:call)
data.call
else
- if path.to_s =~ /gz$/ and not data.nil? and not data.empty? then
- data = Gem.gunzip data
+ if path.to_s =~ /gz$/ and not data.nil? and not data.empty?
+ data = Gem::Util.gunzip data
end
data
end
end
- def cache_update_path uri, path = nil, update = true
+ def cache_update_path(uri, path = nil, update = true)
if data = fetch_path(uri)
open(path, 'wb') { |io| io.write data } if path and update
data
@@ -93,7 +87,7 @@ class Gem::FakeFetcher
def request(uri, request_class, last_modified = nil)
data = find_data(uri)
- body, code, msg = data
+ body, code, msg = (data.respond_to?(:call) ? data.call : data)
@last_request = request_class.new uri.request_uri
yield @last_request if block_given?
@@ -104,21 +98,13 @@ class Gem::FakeFetcher
response
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.group 2, '[FakeFetcher', ']' do
q.breakable
q.text 'URIs:'
q.breakable
q.pp @data.keys
-
- unless @api_endpoints.empty? then
- q.breakable
- q.text 'API endpoints:'
-
- q.breakable
- q.pp @api_endpoints.keys
- end
end
end
@@ -128,7 +114,7 @@ class Gem::FakeFetcher
raise ArgumentError, 'need full URI' unless path =~ %r'^http://'
- unless @data.key? path then
+ unless @data.key? path
raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path)
end
@@ -137,9 +123,9 @@ class Gem::FakeFetcher
data.respond_to?(:call) ? data.call : data.length
end
- def download spec, source_uri, install_dir = Gem.dir
+ def download(spec, source_uri, install_dir = Gem.dir)
name = File.basename spec.cache_file
- path = if Dir.pwd == install_dir then # see fetch_command
+ path = if Dir.pwd == install_dir # see fetch_command
install_dir
else
File.join install_dir, "cache"
@@ -147,7 +133,7 @@ class Gem::FakeFetcher
path = File.join path, name
- if source_uri =~ /^http/ then
+ if source_uri =~ /^http/
File.open(path, "wb") do |f|
f.write fetch_path(File.join(source_uri, "gems", name))
end
@@ -158,7 +144,7 @@ class Gem::FakeFetcher
path
end
- def download_to_cache dependency
+ def download_to_cache(dependency)
found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dependency
return if found.empty?
@@ -201,7 +187,7 @@ class Gem::TestCase::SpecFetcherSetup
# Executes a SpecFetcher setup block. Yields an instance then creates the
# gems and specifications defined in the instance.
- def self.declare test, repository
+ def self.declare(test, repository)
setup = new test, repository
yield setup
@@ -209,7 +195,7 @@ class Gem::TestCase::SpecFetcherSetup
setup.execute
end
- def initialize test, repository # :nodoc:
+ def initialize(test, repository) # :nodoc:
@test = test
@repository = repository
@@ -277,7 +263,7 @@ class Gem::TestCase::SpecFetcherSetup
# The specification will be yielded before gem creation for customization,
# but only the block or the dependencies may be set, not both.
- def gem name, version, dependencies = nil, &block
+ def gem(name, version, dependencies = nil, &block)
@operations << [:gem, name, version, dependencies, block]
end
@@ -288,7 +274,7 @@ class Gem::TestCase::SpecFetcherSetup
# The specification will be yielded before gem creation for customization,
# but only the block or the dependencies may be set, not both.
- def download name, version, dependencies = nil, &block
+ def download(name, version, dependencies = nil, &block)
@operations << [:download, name, version, dependencies, block]
end
@@ -307,7 +293,7 @@ class Gem::TestCase::SpecFetcherSetup
require 'socket'
require 'rubygems/remote_fetcher'
- unless @test.fetcher then
+ unless @test.fetcher
@test.fetcher = Gem::FakeFetcher.new
Gem::RemoteFetcher.fetcher = @test.fetcher
end
@@ -341,12 +327,12 @@ class Gem::TestCase::SpecFetcherSetup
# The specification will be yielded before creation for customization,
# but only the block or the dependencies may be set, not both.
- def spec name, version, dependencies = nil, &block
+ def spec(name, version, dependencies = nil, &block)
@operations << [:spec, name, version, dependencies, block]
end
- def write_spec spec # :nodoc:
- open spec.spec_file, 'w' do |io|
+ def write_spec(spec) # :nodoc:
+ File.open spec.spec_file, 'w' do |io|
io.write spec.to_ruby_for_cache
end
end
@@ -381,4 +367,3 @@ class TempIO < Tempfile
Gem.read_binary path
end
end
-
diff --git a/lib/rubygems/text.rb b/lib/rubygems/text.rb
index 732f1b99f2..5d346b496b 100644
--- a/lib/rubygems/text.rb
+++ b/lib/rubygems/text.rb
@@ -7,15 +7,28 @@ require 'rubygems'
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)
+ end
+
+ def truncate_text(text, description, max_length = 100_000)
+ raise ArgumentError, "max_length must be positive" unless max_length > 0
+ return text if text.size <= max_length
+ "Truncating #{description} to #{max_length.to_s.reverse.gsub(/...(?=.)/,'\&,').reverse} characters:\n" + text[0, max_length]
+ end
+
+ ##
# Wraps +text+ to +wrap+ characters and optionally indents by +indent+
# characters
def format_text(text, wrap, indent=0)
result = []
- work = text.dup
+ work = clean_text(text)
while work.length > wrap do
- if work =~ /^(.{0,#{wrap}})[ \n]/ then
+ if work =~ /^(.{0,#{wrap}})[ \n]/
result << $1.rstrip
work.slice!(0, $&.length)
else
@@ -27,10 +40,10 @@ module Gem::Text
result.join("\n").gsub(/^/, " " * indent)
end
- def min3 a, b, c # :nodoc:
- if a < b && a < c then
+ def min3(a, b, c) # :nodoc:
+ if a < b && a < c
a
- elsif b < c then
+ elsif b < c
b
else
c
@@ -39,7 +52,7 @@ module Gem::Text
# This code is based directly on the Text gem implementation
# Returns a value representing the "cost" of transforming str1 into str2
- def levenshtein_distance str1, str2
+ def levenshtein_distance(str1, str2)
s = str1
t = str2
n = s.length
@@ -60,7 +73,7 @@ module Gem::Text
d[j+1] + 1, # insertion
e + 1, # deletion
d[j] + cost # substitution
- )
+ )
d[j] = e
e = x
end
diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb
index 89f47a45fe..c213cf656e 100644
--- a/lib/rubygems/uninstaller.rb
+++ b/lib/rubygems/uninstaller.rb
@@ -99,7 +99,7 @@ class Gem::Uninstaller
list.sort!
- if list.empty? then
+ if list.empty?
if other_repo_specs.empty?
if default_specs.empty?
raise Gem::InstallError, "gem #{@gem.inspect} is not installed"
@@ -119,19 +119,19 @@ class Gem::Uninstaller
}
raise Gem::InstallError, message.join("\n")
- elsif @force_all then
+ elsif @force_all
remove_all list
- elsif list.size > 1 then
+ elsif list.size > 1
gem_names = list.map { |gem| gem.full_name }
gem_names << "All versions"
say
_, index = choose_from_list "Select gem to uninstall:", gem_names
- if index == list.size then
+ if index == list.size
remove_all list
- elsif index >= 0 && index < list.size then
+ elsif index >= 0 && index < list.size
uninstall_gem list[index]
else
say "Error: must enter a number [1-#{list.size+1}]"
@@ -194,7 +194,7 @@ class Gem::Uninstaller
executables = executables.map { |exec| formatted_program_filename exec }
- remove = if @force_executables.nil? then
+ remove = if @force_executables.nil?
ask_yes_no("Remove executables:\n" +
"\t#{executables.join ', '}\n\n" +
"in addition to the gem?",
@@ -203,7 +203,7 @@ class Gem::Uninstaller
@force_executables
end
- if remove then
+ if remove
bin_dir = @bin_dir || Gem.bindir(spec.base_dir)
raise Gem::FilePermissionError, bin_dir unless File.writable? bin_dir
@@ -213,8 +213,8 @@ class Gem::Uninstaller
exe_file = File.join bin_dir, exe_name
- FileUtils.rm_f exe_file
- FileUtils.rm_f "#{exe_file}.bat"
+ safe_delete { FileUtils.rm exe_file }
+ safe_delete { FileUtils.rm "#{exe_file}.bat" }
end
else
say "Executables and scripts will remain installed."
@@ -239,7 +239,7 @@ class Gem::Uninstaller
def remove(spec)
unless path_ok?(@gem_home, spec) or
- (@user_install and path_ok?(Gem.user_dir, spec)) then
+ (@user_install and path_ok?(Gem.user_dir, spec))
e = Gem::GemNotInHomeException.new \
"Gem '#{spec.full_name}' is not installed in directory #{@gem_home}"
e.spec = spec
@@ -250,26 +250,26 @@ class Gem::Uninstaller
raise Gem::FilePermissionError, spec.base_dir unless
File.writable?(spec.base_dir)
- FileUtils.rm_rf spec.full_gem_path
- FileUtils.rm_rf spec.extension_dir
+ safe_delete { FileUtils.rm_r spec.full_gem_path }
+ safe_delete { FileUtils.rm_r spec.extension_dir }
old_platform_name = spec.original_name
- gemspec = spec.spec_file
-
- unless File.exist? gemspec then
- gemspec = File.join(File.dirname(gemspec), "#{old_platform_name}.gemspec")
- end
-
- FileUtils.rm_rf gemspec
gem = spec.cache_file
gem = File.join(spec.cache_dir, "#{old_platform_name}.gem") unless
File.exist? gem
- FileUtils.rm_rf gem
+ safe_delete { FileUtils.rm_r gem }
Gem::RDoc.new(spec).remove
+ gemspec = spec.spec_file
+
+ unless File.exist? gemspec
+ gemspec = File.join(File.dirname(gemspec), "#{old_platform_name}.gemspec")
+ end
+
+ safe_delete { FileUtils.rm_r gemspec }
say "Successfully uninstalled #{spec.full_name}"
Gem::Specification.reset
@@ -289,7 +289,7 @@ class Gem::Uninstaller
# Returns true if it is OK to remove +spec+ or this is a forced
# uninstallation.
- def dependencies_ok? spec # :nodoc:
+ def dependencies_ok?(spec) # :nodoc:
return true if @force_ignore
deplist = Gem::DependencyList.from_specs
@@ -308,15 +308,15 @@ class Gem::Uninstaller
##
# Asks if it is OK to remove +spec+. Returns true if it is OK.
- def ask_if_ok spec # :nodoc:
+ def ask_if_ok(spec) # :nodoc:
msg = ['']
msg << 'You have requested to uninstall the gem:'
msg << "\t#{spec.full_name}"
msg << ''
siblings = Gem::Specification.select do |s|
- s.name == spec.name && s.full_name != spec.full_name
- end
+ s.name == spec.name && s.full_name != spec.full_name
+ end
spec.dependent_gems.each do |dep_spec, dep, satlist|
unless siblings.any? { |s| s.satisfies_requirement? dep }
@@ -332,15 +332,26 @@ class Gem::Uninstaller
##
# Returns the formatted version of the executable +filename+
- def formatted_program_filename filename # :nodoc:
+ def formatted_program_filename(filename) # :nodoc:
# TODO perhaps the installer should leave a small manifest
# of what it did for us to find rather than trying to recreate
# it again.
- if @format_executable then
+ if @format_executable
require 'rubygems/installer'
Gem::Installer.exec_format % File.basename(filename)
else
filename
end
end
+
+ def safe_delete(&block)
+ block.call
+ rescue Errno::ENOENT
+ nil
+ rescue Errno::EPERM
+ e = Gem::UninstallError.new
+ e.spec = @spec
+
+ raise e
+ end
end
diff --git a/lib/rubygems/uri_formatter.rb b/lib/rubygems/uri_formatter.rb
index bb128e4ef9..0a24dde24d 100644
--- a/lib/rubygems/uri_formatter.rb
+++ b/lib/rubygems/uri_formatter.rb
@@ -19,7 +19,7 @@ class Gem::UriFormatter
##
# Creates a new URI formatter for +uri+.
- def initialize uri
+ def initialize(uri)
@uri = uri
end
@@ -47,4 +47,3 @@ class Gem::UriFormatter
end
end
-
diff --git a/lib/rubygems/user_interaction.rb b/lib/rubygems/user_interaction.rb
index 390d0f2aea..0696d54951 100644
--- a/lib/rubygems/user_interaction.rb
+++ b/lib/rubygems/user_interaction.rb
@@ -6,11 +6,8 @@
#++
require 'rubygems/util'
-
-begin
- require 'io/console'
-rescue LoadError
-end
+require 'rubygems/deprecate'
+require 'rubygems/text'
##
# Module that defines the default UserInteraction. Any class including this
@@ -18,6 +15,8 @@ end
module Gem::DefaultUserInteraction
+ include Gem::Text
+
##
# The default UI is a class variable of the singleton class for this
# module.
@@ -99,7 +98,7 @@ module Gem::UserInteraction
##
# Displays an alert +statement+. Asks a +question+ if given.
- def alert statement, question = nil
+ def alert(statement, question = nil)
ui.alert statement, question
end
@@ -107,7 +106,7 @@ module Gem::UserInteraction
# Displays an error +statement+ to the error output location. Asks a
# +question+ if given.
- def alert_error statement, question = nil
+ def alert_error(statement, question = nil)
ui.alert_error statement, question
end
@@ -115,49 +114,49 @@ module Gem::UserInteraction
# Displays a warning +statement+ to the warning output location. Asks a
# +question+ if given.
- def alert_warning statement, question = nil
+ def alert_warning(statement, question = nil)
ui.alert_warning statement, question
end
##
# Asks a +question+ and returns the answer.
- def ask question
+ def ask(question)
ui.ask question
end
##
# Asks for a password with a +prompt+
- def ask_for_password prompt
+ def ask_for_password(prompt)
ui.ask_for_password prompt
end
##
# Asks a yes or no +question+. Returns true for yes, false for no.
- def ask_yes_no question, default = nil
+ def ask_yes_no(question, default = nil)
ui.ask_yes_no question, default
end
##
# Asks the user to answer +question+ with an answer from the given +list+.
- def choose_from_list question, list
+ def choose_from_list(question, list)
ui.choose_from_list question, list
end
##
# Displays the given +statement+ on the standard output (or equivalent).
- def say statement = ''
+ def say(statement = '')
ui.say statement
end
##
# Terminates the RubyGems process with the given +exit_code+
- def terminate_interaction exit_code = 0
+ def terminate_interaction(exit_code = 0)
ui.terminate_interaction exit_code
end
@@ -165,8 +164,8 @@ module Gem::UserInteraction
# Calls +say+ with +msg+ or the results of the block if really_verbose
# is true.
- def verbose msg = nil
- say(msg || yield) if Gem.configuration.really_verbose
+ def verbose(msg = nil)
+ say(clean_text(msg || yield)) if Gem.configuration.really_verbose
end
end
@@ -175,6 +174,8 @@ end
class Gem::StreamUI
+ extend Gem::Deprecate
+
##
# The input stream
@@ -207,18 +208,14 @@ class Gem::StreamUI
# Returns true if TTY methods should be used on this StreamUI.
def tty?
- if RUBY_VERSION < '1.9.3' and RUBY_PLATFORM =~ /mingw|mswin/ then
- @usetty
- else
- @usetty && @ins.tty?
- end
+ @usetty && @ins.tty?
end
##
# Prints a formatted backtrace to the errors stream if backtraces are
# enabled.
- def backtrace exception
+ def backtrace(exception)
return unless Gem.configuration.backtrace
@errs.puts "\t#{exception.backtrace.join "\n\t"}"
@@ -253,8 +250,8 @@ class Gem::StreamUI
# default.
def ask_yes_no(question, default=nil)
- unless tty? then
- if default.nil? then
+ unless tty?
+ if default.nil?
raise Gem::OperationNotSupportedError,
"Not connected to a tty and no default specified"
else
@@ -314,36 +311,21 @@ class Gem::StreamUI
password
end
- if IO.method_defined?(:noecho) then
- def _gets_noecho
- @ins.noecho {@ins.gets}
- end
- elsif Gem.win_platform?
- def _gets_noecho
- require "Win32API"
- password = ''
-
- while char = Win32API.new("crtdll", "_getch", [ ], "L").Call do
- break if char == 10 || char == 13 # received carriage return or newline
- if char == 127 || char == 8 # backspace and delete
- password.slice!(-1, 1)
- else
- password << char.chr
- end
- end
- password
- end
- else
- def _gets_noecho
- system "stty -echo"
+ def require_io_console
+ @require_io_console ||= begin
begin
- @ins.gets
- ensure
- system "stty echo"
+ require 'io/console'
+ rescue LoadError
end
+ true
end
end
+ def _gets_noecho
+ require_io_console
+ @ins.noecho {@ins.gets}
+ end
+
##
# Display a statement.
@@ -382,6 +364,7 @@ class Gem::StreamUI
def debug(statement)
@errs.puts statement
end
+ deprecate :debug, :none, 2018, 12
##
# Terminate the application with exit code +status+, running any exit
@@ -532,11 +515,10 @@ class Gem::StreamUI
# Return a download reporter object chosen from the current verbosity
def download_reporter(*args)
- case Gem.configuration.verbose
- when nil, false
+ if [nil, false].include?(Gem.configuration.verbose) || !@outs.tty?
SilentDownloadReporter.new(@outs, *args)
else
- VerboseDownloadReporter.new(@outs, *args)
+ ThreadedDownloadReporter.new(@outs, *args)
end
end
@@ -573,9 +555,11 @@ class Gem::StreamUI
end
##
- # A progress reporter that prints out messages about the current progress.
+ # A progress reporter that behaves nicely with threaded downloading.
- class VerboseDownloadReporter
+ class ThreadedDownloadReporter
+
+ MUTEX = Mutex.new
##
# The current file name being displayed
@@ -583,71 +567,44 @@ class Gem::StreamUI
attr_reader :file_name
##
- # The total bytes in the file
-
- attr_reader :total_bytes
-
- ##
- # The current progress (0 to 100)
-
- attr_reader :progress
-
- ##
- # Creates a new verbose download reporter that will display on
+ # Creates a new threaded download reporter that will display on
# +out_stream+. The other arguments are ignored.
def initialize(out_stream, *args)
+ @file_name = nil
@out = out_stream
- @progress = 0
end
##
- # Tells the download reporter that the +file_name+ is being fetched and
- # contains +total_bytes+.
-
- def fetch(file_name, total_bytes)
- @file_name = file_name
- @total_bytes = total_bytes.to_i
- @units = @total_bytes.zero? ? 'B' : '%'
+ # Tells the download reporter that the +file_name+ is being fetched.
+ # The other arguments are ignored.
- update_display(false)
+ def fetch(file_name, *args)
+ if @file_name.nil?
+ @file_name = file_name
+ locked_puts "Fetching #{@file_name}"
+ end
end
##
- # Updates the verbose download reporter for the given number of +bytes+.
+ # Updates the threaded download reporter for the given number of +bytes+.
def update(bytes)
- new_progress = if @units == 'B' then
- bytes
- else
- ((bytes.to_f * 100) / total_bytes.to_f).ceil
- end
-
- return if new_progress == @progress
-
- @progress = new_progress
- update_display
+ # Do nothing.
end
##
# Indicates the download is complete.
def done
- @progress = 100 if @units == '%'
- update_display(true, true)
+ # Do nothing.
end
private
-
- def update_display(show_progress = true, new_line = false) # :nodoc:
- return unless @out.tty?
-
- if show_progress then
- @out.print "\rFetching: %s (%3d%s)" % [@file_name, @progress, @units]
- else
- @out.print "Fetching: %s" % @file_name
+ def locked_puts(message)
+ MUTEX.synchronize do
+ @out.puts message
end
- @out.puts if new_line
end
end
end
@@ -678,8 +635,8 @@ class Gem::SilentUI < Gem::StreamUI
def initialize
reader, writer = nil, nil
- reader = File.open(Gem::Util::NULL_DEVICE, 'r')
- writer = File.open(Gem::Util::NULL_DEVICE, 'w')
+ reader = File.open(IO::NULL, 'r')
+ writer = File.open(IO::NULL, 'w')
super reader, writer, writer, false
end
diff --git a/lib/rubygems/util.rb b/lib/rubygems/util.rb
index ead2babc1f..b9e5dfc8c7 100644
--- a/lib/rubygems/util.rb
+++ b/lib/rubygems/util.rb
@@ -15,7 +15,7 @@ module Gem::Util
data = StringIO.new(data, 'r')
unzipped = Zlib::GzipReader.new(data).read
- unzipped.force_encoding Encoding::BINARY if Object.const_defined? :Encoding
+ unzipped.force_encoding Encoding::BINARY
unzipped
end
@@ -26,7 +26,7 @@ module Gem::Util
require 'zlib'
require 'stringio'
zipped = StringIO.new(String.new, 'w')
- zipped.set_encoding Encoding::BINARY if Object.const_defined? :Encoding
+ zipped.set_encoding Encoding::BINARY
Zlib::GzipWriter.wrap zipped do |io| io.write data end
@@ -46,7 +46,7 @@ module Gem::Util
# and implements an IO.popen-like behavior where it does not accept an array
# for a command.
- def self.popen *command
+ def self.popen(*command)
IO.popen command, &:read
rescue TypeError # ruby 1.8 only supports string command
r, w = IO.pipe
@@ -67,13 +67,11 @@ module Gem::Util
end
end
- NULL_DEVICE = defined?(IO::NULL) ? IO::NULL : Gem.win_platform? ? 'NUL' : '/dev/null'
-
##
# Invokes system, but silences all output.
- def self.silent_system *command
- opt = {:out => NULL_DEVICE, :err => [:child, :out]}
+ def self.silent_system(*command)
+ opt = {:out => IO::NULL, :err => [:child, :out]}
if Hash === command.last
opt.update(command.last)
cmds = command[0...-1]
@@ -82,19 +80,15 @@ module Gem::Util
end
return system(*(cmds << opt))
rescue TypeError
- require 'thread'
-
@silent_mutex ||= Mutex.new
- null_device = NULL_DEVICE
-
@silent_mutex.synchronize do
begin
stdout = STDOUT.dup
stderr = STDERR.dup
- STDOUT.reopen null_device, 'w'
- STDERR.reopen null_device, 'w'
+ STDOUT.reopen IO::NULL, 'w'
+ STDERR.reopen IO::NULL, 'w'
return system(*command)
ensure
@@ -109,26 +103,28 @@ module Gem::Util
##
# Enumerates the parents of +directory+.
- def self.traverse_parents directory
+ def self.traverse_parents(directory, &block)
return enum_for __method__, directory unless block_given?
here = File.expand_path directory
- start = here
-
- Dir.chdir start
-
- begin
- loop do
- yield here
+ loop do
+ Dir.chdir here, &block rescue Errno::EACCES
- Dir.chdir '..'
+ new_here = File.expand_path('..', here)
+ return if new_here == here # toplevel
+ here = new_here
+ end
+ end
- return if Dir.pwd == here # toplevel
+ ##
+ # Globs for files matching +pattern+ inside of +directory+,
+ # returning absolute paths to the matching files.
- here = Dir.pwd
- end
- ensure
- Dir.chdir start
+ 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
end
diff --git a/lib/rubygems/util/licenses.rb b/lib/rubygems/util/licenses.rb
index f4a99af39e..2a536575c0 100644
--- a/lib/rubygems/util/licenses.rb
+++ b/lib/rubygems/util/licenses.rb
@@ -8,7 +8,7 @@ class Gem::Licenses
# Software Package Data Exchange (SPDX) standard open-source software
# license identifiers
- IDENTIFIERS = %w(
+ LICENSE_IDENTIFIERS = %w(
0BSD
AAL
ADSL
@@ -19,6 +19,8 @@ class Gem::Licenses
AFL-3.0
AGPL-1.0
AGPL-3.0
+ AGPL-3.0-only
+ AGPL-3.0-or-later
AMDPLPA
AML
AMPAS
@@ -41,16 +43,22 @@ class Gem::Licenses
Artistic-1.0-Perl
Artistic-1.0-cl8
Artistic-2.0
+ BSD-1-Clause
BSD-2-Clause
BSD-2-Clause-FreeBSD
BSD-2-Clause-NetBSD
+ BSD-2-Clause-Patent
BSD-3-Clause
BSD-3-Clause-Attribution
BSD-3-Clause-Clear
BSD-3-Clause-LBNL
+ BSD-3-Clause-No-Nuclear-License
+ BSD-3-Clause-No-Nuclear-License-2014
+ BSD-3-Clause-No-Nuclear-Warranty
BSD-4-Clause
BSD-4-Clause-UC
BSD-Protection
+ BSD-Source-Code
BSL-1.0
Bahyph
Barr
@@ -92,6 +100,8 @@ class Gem::Licenses
CC0-1.0
CDDL-1.0
CDDL-1.1
+ CDLA-Permissive-1.0
+ CDLA-Sharing-1.0
CECILL-1.0
CECILL-1.1
CECILL-2.0
@@ -120,12 +130,15 @@ class Gem::Licenses
EFL-1.0
EFL-2.0
EPL-1.0
+ EPL-2.0
EUDatagrid
EUPL-1.0
EUPL-1.1
+ EUPL-1.2
Entessa
ErlPL-1.1
Eurosym
+ FSFAP
FSFUL
FSFULLR
FTL
@@ -133,12 +146,34 @@ class Gem::Licenses
Frameworx-1.0
FreeImage
GFDL-1.1
+ GFDL-1.1-only
+ GFDL-1.1-or-later
GFDL-1.2
+ GFDL-1.2-only
+ GFDL-1.2-or-later
GFDL-1.3
+ GFDL-1.3-only
+ GFDL-1.3-or-later
GL2PS
GPL-1.0
+ GPL-1.0+
+ GPL-1.0-only
+ GPL-1.0-or-later
GPL-2.0
+ GPL-2.0+
+ GPL-2.0-only
+ GPL-2.0-or-later
+ GPL-2.0-with-GCC-exception
+ GPL-2.0-with-autoconf-exception
+ GPL-2.0-with-bison-exception
+ GPL-2.0-with-classpath-exception
+ GPL-2.0-with-font-exception
GPL-3.0
+ GPL-3.0+
+ GPL-3.0-only
+ GPL-3.0-or-later
+ GPL-3.0-with-GCC-exception
+ GPL-3.0-with-autoconf-exception
Giftware
Glide
Glulxe
@@ -152,14 +187,26 @@ class Gem::Licenses
ISC
ImageMagick
Imlib2
+ Info-ZIP
Intel
Intel-ACPI
Interbase-1.0
JSON
JasPer-2.0
+ LAL-1.2
+ LAL-1.3
LGPL-2.0
+ LGPL-2.0+
+ LGPL-2.0-only
+ LGPL-2.0-or-later
LGPL-2.1
+ LGPL-2.1+
+ LGPL-2.1-only
+ LGPL-2.1-or-later
LGPL-3.0
+ LGPL-3.0+
+ LGPL-3.0-only
+ LGPL-3.0-or-later
LGPLLR
LPL-1.0
LPL-1.02
@@ -170,6 +217,9 @@ class Gem::Licenses
LPPL-1.3c
Latex2e
Leptonica
+ LiLiQ-P-1.1
+ LiLiQ-R-1.1
+ LiLiQ-Rplus-1.1
Libpng
MIT
MIT-CMU
@@ -193,6 +243,7 @@ class Gem::Licenses
NBPL-1.0
NCSA
NGPL
+ NLOD-1.0
NLPL
NOSL
NPL-1.0
@@ -201,11 +252,13 @@ class Gem::Licenses
NRL
NTP
Naumen
+ Net-SNMP
NetCDF
Newsletr
Nokia
Noweb
Nunit
+ OCCT-PL
OCLC-2.0
ODbL-1.0
OFL-1.0
@@ -229,6 +282,7 @@ class Gem::Licenses
OLDAP-2.8
OML
OPL-1.0
+ OSET-PL-2.1
OSL-1.0
OSL-1.1
OSL-2.0
@@ -259,6 +313,7 @@ class Gem::Licenses
SISSL
SISSL-1.2
SMLNJ
+ SMPPL
SNIA
SPL-1.0
SWL
@@ -269,12 +324,16 @@ class Gem::Licenses
Spencer-86
Spencer-94
Spencer-99
+ StandardML-NJ
SugarCRM-1.1.3
TCL
+ TCP-wrappers
TMate
TORQUE-1.1
TOSL
UPL-1.0
+ Unicode-DFS-2015
+ Unicode-DFS-2016
Unicode-TOU
Unlicense
VOSTROM
@@ -282,6 +341,7 @@ class Gem::Licenses
Vim
W3C
W3C-19980720
+ W3C-20150513
WTFPL
Watcom-1.0
Wsuipa
@@ -302,8 +362,10 @@ class Gem::Licenses
Zlib
bzip2-1.0.5
bzip2-1.0.6
+ curl
diffmark
dvipdfm
+ eCos-2.0
eGenix
gSOAP-1.3b
gnuplot
@@ -312,17 +374,49 @@ class Gem::Licenses
mpich2
psfrag
psutils
+ wxWindows
xinetd
xpp
zlib-acknowledgement
).freeze
+ # exception identifiers
+ EXCEPTION_IDENTIFIERS = %w(
+ 389-exception
+ Autoconf-exception-2.0
+ Autoconf-exception-3.0
+ Bison-exception-2.2
+ Bootloader-exception
+ CLISP-exception-2.0
+ Classpath-exception-2.0
+ DigiRule-FOSS-exception
+ FLTK-exception
+ Fawkes-Runtime-exception
+ Font-exception-2.0
+ GCC-exception-2.0
+ GCC-exception-3.1
+ LZMA-exception
+ Libtool-exception
+ Linux-syscall-note
+ Nokia-Qt-exception-1.1
+ OCCT-exception-1.0
+ Qwt-exception-1.0
+ WxWindows-exception-3.1
+ eCos-exception-2.0
+ freertos-exception-2.0
+ gnu-javamail-exception
+ i2p-gpl-java-exception
+ mif-exception
+ openvpn-openssl-exception
+ u-boot-exception-2.0
+ ).freeze
+
REGEXP = %r{
\A
(
- #{Regexp.union(IDENTIFIERS)}
+ #{Regexp.union(LICENSE_IDENTIFIERS)}
\+?
- (\s WITH \s .+)?
+ (\s WITH \s #{Regexp.union(EXCEPTION_IDENTIFIERS)})?
| #{NONSTANDARD}
)
\Z
@@ -333,7 +427,7 @@ class Gem::Licenses
end
def self.suggestions(license)
- by_distance = IDENTIFIERS.group_by do |identifier|
+ by_distance = LICENSE_IDENTIFIERS.group_by do |identifier|
levenshtein_distance(identifier, license)
end
lowest = by_distance.keys.min
diff --git a/lib/rubygems/util/list.rb b/lib/rubygems/util/list.rb
index 9c25f6b6dc..33c40af4bb 100644
--- a/lib/rubygems/util/list.rb
+++ b/lib/rubygems/util/list.rb
@@ -25,7 +25,7 @@ module Gem
List.new value, self
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.pp to_a
end
diff --git a/lib/rubygems/validator.rb b/lib/rubygems/validator.rb
index 83448229bb..828497f700 100644
--- a/lib/rubygems/validator.rb
+++ b/lib/rubygems/validator.rb
@@ -34,7 +34,7 @@ class Gem::Validator
# gem_path:: [String] Path to gem file
def verify_gem_file(gem_path)
- open gem_path, Gem.binary_mode do |file|
+ File.open gem_path, Gem.binary_mode do |file|
gem_data = file.read
verify_gem gem_data
end
@@ -62,7 +62,7 @@ class Gem::Validator
# Describes a problem with a file in a gem.
ErrorData = Struct.new :path, :problem do
- def <=> other # :nodoc:
+ def <=>(other) # :nodoc:
return nil unless self.class === other
[path, problem] <=> [other.path, other.problem]
@@ -94,13 +94,13 @@ class Gem::Validator
spec_path = spec.spec_file
gem_directory = spec.full_gem_path
- unless File.directory? gem_directory then
+ unless File.directory? gem_directory
errors[gem_name][spec.full_name] =
"Gem registered but doesn't exist at #{gem_directory}"
next
end
- unless File.exist? spec_path then
+ unless File.exist? spec_path
errors[gem_name][spec_path] = "Spec file missing for installed gem"
end
@@ -109,7 +109,7 @@ class Gem::Validator
good, gone, unreadable = nil, nil, nil, nil
- open gem_path, Gem.binary_mode do |file|
+ File.open gem_path, Gem.binary_mode do |file|
package = Gem::Package.new gem_path
good, gone = package.contents.partition { |file_name|
@@ -134,8 +134,8 @@ class Gem::Validator
source = File.join gem_directory, entry['path']
- open source, Gem.binary_mode do |f|
- unless f.read == data then
+ File.open source, Gem.binary_mode do |f|
+ unless f.read == data
errors[gem_name][entry['path']] = "Modified from original"
end
end
@@ -163,4 +163,3 @@ class Gem::Validator
errors
end
end
-
diff --git a/lib/rubygems/version.rb b/lib/rubygems/version.rb
index 2f6cfae6ed..f2f10569e8 100644
--- a/lib/rubygems/version.rb
+++ b/lib/rubygems/version.rb
@@ -154,8 +154,8 @@ class Gem::Version
include Comparable
- 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/ # :nodoc:
+ VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?'.freeze # :nodoc:
+ ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/.freeze # :nodoc:
##
# A string representation of this Version.
@@ -169,8 +169,12 @@ class Gem::Version
##
# True if the +version+ string matches RubyGems' requirements.
- def self.correct? version
- version.to_s =~ ANCHORED_VERSION_PATTERN
+ def self.correct?(version)
+ unless Gem::Deprecate.skip
+ warn "nil versions are discouraged and will be deprecated in Rubygems 4" if version.nil?
+ end
+
+ !!(version.to_s =~ ANCHORED_VERSION_PATTERN)
end
##
@@ -181,10 +185,10 @@ class Gem::Version
# ver2 = Version.create(ver1) # -> (ver1)
# ver3 = Version.create(nil) # -> nil
- def self.create input
- if self === input then # check yourself before you wreck yourself
+ def self.create(input)
+ if self === input # check yourself before you wreck yourself
input
- elsif input.nil? then
+ elsif input.nil?
nil
else
new input
@@ -193,7 +197,7 @@ class Gem::Version
@@all = {}
- def self.new version # :nodoc:
+ def self.new(version) # :nodoc:
return super unless Gem::Version == self
@@all[version] ||= super
@@ -203,9 +207,13 @@ class Gem::Version
# Constructs a Version from the +version+ string. A version string is a
# series of digits or ASCII letters separated by dots.
- def initialize version
- raise ArgumentError, "Malformed version number string #{version}" unless
- self.class.correct?(version)
+ def initialize(version)
+ unless self.class.correct?(version)
+ raise ArgumentError, "Malformed version number string #{version}"
+ end
+
+ # If version is an empty string convert it to 0
+ version = 0 if version.is_a?(String) && version =~ /\A\s*\Z/
@version = version.to_s.strip.gsub("-",".pre.")
@segments = nil
@@ -232,15 +240,15 @@ class Gem::Version
# A Version is only eql? to another version if it's specified to the
# same precision. Version "1.0" is not the same as version "1".
- def eql? other
+ def eql?(other)
self.class === other and @version == other._version
end
def hash # :nodoc:
- @version.hash
+ canonical_segments.hash
end
- def init_with coder # :nodoc:
+ def init_with(coder) # :nodoc:
yaml_initialize coder.tag, coder.map
end
@@ -260,7 +268,7 @@ class Gem::Version
# Load custom marshal format. It's a string for backwards (RubyGems
# 1.3.5 and earlier) compatibility.
- def marshal_load array
+ def marshal_load(array)
initialize array[0]
end
@@ -274,7 +282,7 @@ class Gem::Version
["@version"]
end
- def encode_with coder # :nodoc:
+ def encode_with(coder) # :nodoc:
coder.add 'version', @version
end
@@ -288,7 +296,7 @@ class Gem::Version
@prerelease
end
- def pretty_print q # :nodoc:
+ def pretty_print(q) # :nodoc:
q.text "Gem::Version.new(#{version.inspect})"
end
@@ -320,7 +328,9 @@ class Gem::Version
segments.pop while segments.size > 2
segments.push 0 while segments.size < 2
- "~> #{segments.join(".")}"
+ recommendation = "~> #{segments.join(".")}"
+ recommendation += ".a" if prerelease?
+ recommendation
end
##
@@ -329,9 +339,9 @@ class Gem::Version
# one. Attempts to compare to something that's not a
# <tt>Gem::Version</tt> return +nil+.
- def <=> other
+ def <=>(other)
return unless Gem::Version === other
- return 0 if @version == other._version
+ return 0 if @version == other._version || canonical_segments == other.canonical_segments
lhsegments = _segments
rhsegments = other._segments
@@ -356,6 +366,13 @@ class Gem::Version
return 0
end
+ def canonical_segments
+ @canonical_segments ||=
+ _split_segments.map! do |segments|
+ segments.reverse_each.drop_while {|s| s == 0 }.reverse
+ end.reduce(&:concat)
+ end
+
protected
def _version
@@ -371,4 +388,11 @@ class Gem::Version
/^\d+$/ =~ s ? s.to_i : s
end.freeze
end
+
+ def _split_segments
+ string_start = _segments.index {|s| s.is_a?(String) }
+ string_segments = segments
+ numeric_segments = string_segments.slice!(0, string_start || string_segments.size)
+ return numeric_segments, string_segments
+ end
end
diff --git a/lib/rubygems/version_option.rb b/lib/rubygems/version_option.rb
index 3209d95f0f..458a7a6601 100644
--- a/lib/rubygems/version_option.rb
+++ b/lib/rubygems/version_option.rb
@@ -17,7 +17,7 @@ module Gem::VersionOption
def add_platform_option(task = command, *wrap)
OptionParser.accept Gem::Platform do |value|
- if value == Gem::Platform::RUBY then
+ if value == Gem::Platform::RUBY
value
else
Gem::Platform.new value
@@ -27,7 +27,7 @@ module Gem::VersionOption
add_option('--platform PLATFORM', Gem::Platform,
"Specify the platform of gem to #{task}", *wrap) do
|value, options|
- unless options[:added_platform] then
+ unless options[:added_platform]
Gem.platforms = [Gem::Platform::RUBY]
options[:added_platform] = true
end
@@ -58,7 +58,12 @@ module Gem::VersionOption
add_option('-v', '--version VERSION', Gem::Requirement,
"Specify version of gem to #{task}", *wrap) do
|value, options|
- options[:version] = value
+ # Allow handling for multiple --version operators
+ if options[:version] && !options[:version].none?
+ options[:version].concat([value])
+ else
+ options[:version] = value
+ end
explicit_prerelease_set = !options[:explicit_prerelease].nil?
options[:explicit_prerelease] = false unless explicit_prerelease_set
@@ -69,4 +74,3 @@ module Gem::VersionOption
end
end
-
diff --git a/lib/scanf.gemspec b/lib/scanf.gemspec
new file mode 100644
index 0000000000..e845427215
--- /dev/null
+++ b/lib/scanf.gemspec
@@ -0,0 +1,24 @@
+# coding: utf-8
+# frozen_string_literal: true
+
+Gem::Specification.new do |spec|
+ spec.name = "scanf"
+ spec.version = "1.0.0"
+ spec.authors = ["David Alan Black"]
+ spec.email = ['dblack@superlink.net']
+
+ spec.summary = "scanf is an implementation of the C function scanf(3)."
+ spec.description = "scanf is an implementation of the C function scanf(3)."
+ spec.homepage = "https://github.com/ruby/scanf"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = ["lib/scanf.rb"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+ spec.required_ruby_version = ">= 2.3.0"
+
+ spec.add_development_dependency "bundler", "~> 1.14"
+ spec.add_development_dependency "rake", "~> 10.0"
+ spec.add_development_dependency "test-unit"
+end
diff --git a/lib/scanf.rb b/lib/scanf.rb
index a98c359573..495f8705a4 100644
--- a/lib/scanf.rb
+++ b/lib/scanf.rb
@@ -13,7 +13,7 @@
# scanf is an implementation of the C function scanf(3), modified as necessary
# for Ruby compatibility.
#
-# the methods provided are String#scanf, IO#scanf, and
+# The methods provided are String#scanf, IO#scanf, and
# Kernel#scanf. Kernel#scanf is a wrapper around STDIN.scanf. IO#scanf
# can be used on any IO stream, including file handles and sockets.
# scanf can be called either with or without a block.
@@ -31,8 +31,8 @@
# the conversions themselves are returned as an array.
#
# The format string may also contain characters other than those in the
-# conversion specifiers. White space (blanks, tabs, or newlines) in the
-# format string matches any amount of white space, including none, in
+# conversion specifiers. Whitespace (blanks, tabs, or newlines) in the
+# format string matches any amount of whitespace, including none, in
# the input. Everything else matches only itself.
#
# Scanning stops, and scanf returns, when any input character fails to
@@ -80,7 +80,7 @@
# see below). Otherwise, given a field width of <em>n</em> for a given
# conversion, at most <em>n</em> characters are scanned in processing
# that conversion. Before conversion begins, most conversions skip
-# white space in the input string; this white space is not counted
+# whitespace in the input string; this whitespace is not counted
# against the field width.
#
# The following conversions are available.
@@ -113,17 +113,17 @@
#
# [s]
# Matches a sequence of non-white-space character. The input string stops at
-# white space or at the maximum field width, whichever occurs first.
+# whitespace or at the maximum field width, whichever occurs first.
#
# [c]
# Matches a single character, or a sequence of <em>n</em> characters if a
# field width of <em>n</em> is specified. The usual skip of leading white
-# space is suppressed. To skip white space first, use an explicit space in
+# space is suppressed. To skip whitespace first, use an explicit space in
# the format.
#
# [[]
# Matches a nonempty sequence of characters from the specified set
-# of accepted characters. The usual skip of leading white space is
+# of accepted characters. The usual skip of leading whitespace is
# suppressed. This bracketed sub-expression is interpreted exactly like a
# character class in a Ruby regular expression. (In fact, it is placed as-is
# in a regular expression.) The matching against the input string ends with
@@ -295,7 +295,7 @@ module Scanf
def extract_float(s)
return nil unless s &&! skip
- if /\A(?<sign>[-+]?)0[xX](?<frac>\.\h+|\h+(?:\.\h*)?)[pP](?<exp>[-+]\d+)/ =~ s
+ if /\A(?<sign>[-+]?)0[xX](?<frac>\.\h+|\h+(?:\.\h*)?)[pP](?<exp>[-+]?\d+)/ =~ s
f1, f2 = frac.split('.')
f = f1.hex
if f2
@@ -411,11 +411,11 @@ module Scanf
# %f
when /%\*?[aefgAEFG]/
- [ '([-+]?(?:0[xX](?:\.\h+|\h+(?:\.\h*)?)[pP][-+]\d+|\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))', :extract_float ]
+ [ '([-+]?(?:0[xX](?:\.\h+|\h+(?:\.\h*)?)[pP][-+]?\d+|\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))', :extract_float ]
# %5f
when /%\*?(\d+)[aefgAEFG]/
- [ '(?=[-+]?(?:0[xX](?:\.\h+|\h+(?:\.\h*)?)[pP][-+]\d+|\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))' +
+ [ '(?=[-+]?(?:0[xX](?:\.\h+|\h+(?:\.\h*)?)[pP][-+]?\d+|\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))' +
"(\\S{1,#{$1}})", :extract_float ]
# %5s
diff --git a/lib/securerandom.rb b/lib/securerandom.rb
index 9201c9337a..d01b47499d 100644
--- a/lib/securerandom.rb
+++ b/lib/securerandom.rb
@@ -1,9 +1,5 @@
# -*- coding: us-ascii -*-
# frozen_string_literal: true
-begin
- require 'openssl'
-rescue LoadError
-end
# == Secure random number generator interface.
#
@@ -26,36 +22,78 @@ end
#
# require 'securerandom'
#
-# p SecureRandom.hex(10) #=> "52750b30ffbc7de3b362"
-# p SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559"
-# p SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23"
+# SecureRandom.hex(10) #=> "52750b30ffbc7de3b362"
+# SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559"
+# SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23"
#
# Generate random base64 strings:
#
-# p SecureRandom.base64(10) #=> "EcmTPZwWRAozdA=="
-# p SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg=="
-# p SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8"
+# SecureRandom.base64(10) #=> "EcmTPZwWRAozdA=="
+# SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg=="
+# SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8"
#
# Generate random binary strings:
#
-# p SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
-# p SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
+# SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
+# SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
+#
+# Generate alphanumeric strings:
+#
+# SecureRandom.alphanumeric(10) #=> "S8baxMJnPl"
+# SecureRandom.alphanumeric(10) #=> "aOxAg8BAJe"
#
# Generate UUIDs:
#
-# p SecureRandom.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
-# p SecureRandom.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
+# SecureRandom.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
+# SecureRandom.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
#
module SecureRandom
- if defined?(OpenSSL::Random) && /mswin|mingw/ !~ RUBY_PLATFORM
- def self.gen_random(n)
+ @rng_chooser = Mutex.new # :nodoc:
+
+ class << self
+ def bytes(n)
+ return gen_random(n)
+ end
+
+ def gen_random(n)
+ ret = Random.urandom(1)
+ if ret.nil?
+ begin
+ require 'openssl'
+ rescue NoMethodError
+ raise NotImplementedError, "No random device"
+ else
+ @rng_chooser.synchronize do
+ class << self
+ remove_method :gen_random
+ alias gen_random gen_random_openssl
+ public :gen_random
+ end
+ end
+ return gen_random(n)
+ end
+ else
+ @rng_chooser.synchronize do
+ class << self
+ remove_method :gen_random
+ alias gen_random gen_random_urandom
+ public :gen_random
+ end
+ end
+ return gen_random(n)
+ end
+ end
+
+ private
+
+ def gen_random_openssl(n)
@pid = 0 unless defined?(@pid)
pid = $$
unless @pid == pid
now = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
OpenSSL::Random.random_add([now, @pid, pid].join(""), 0.0)
- seed = Random.raw_seed(16)
+ seed = Random.urandom(16)
if (seed)
OpenSSL::Random.random_add(seed, 16)
end
@@ -63,9 +101,9 @@ module SecureRandom
end
return OpenSSL::Random.random_bytes(n)
end
- else
- def self.gen_random(n)
- ret = Random.raw_seed(n)
+
+ def gen_random_urandom(n)
+ ret = Random.urandom(n)
unless ret
raise NotImplementedError, "No random device"
end
@@ -75,10 +113,6 @@ module SecureRandom
ret
end
end
-
- class << self
- alias bytes gen_random
- end
end
module Random::Formatter
@@ -92,8 +126,10 @@ module Random::Formatter
#
# The result may contain any byte: "\x00" - "\xff".
#
- # p SecureRandom.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6"
- # p SecureRandom.random_bytes #=> "m\xDC\xFC/\a\x00Uf\xB2\xB2P\xBD\xFF6S\x97"
+ # require 'securerandom'
+ #
+ # SecureRandom.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6"
+ # SecureRandom.random_bytes #=> "m\xDC\xFC/\a\x00Uf\xB2\xB2P\xBD\xFF6S\x97"
#
# If a secure random number generator is not available,
# +NotImplementedError+ is raised.
@@ -112,8 +148,10 @@ module Random::Formatter
#
# The result may contain 0-9 and a-f.
#
- # p SecureRandom.hex #=> "eb693ec8252cd630102fd0d0fb7c3485"
- # p SecureRandom.hex #=> "91dc3bfb4de5b11d029d376634589b61"
+ # require 'securerandom'
+ #
+ # SecureRandom.hex #=> "eb693ec8252cd630102fd0d0fb7c3485"
+ # SecureRandom.hex #=> "91dc3bfb4de5b11d029d376634589b61"
#
# If a secure random number generator is not available,
# +NotImplementedError+ is raised.
@@ -131,8 +169,10 @@ module Random::Formatter
#
# The result may contain A-Z, a-z, 0-9, "+", "/" and "=".
#
- # p SecureRandom.base64 #=> "/2BuBuLf3+WfSKyQbRcc/A=="
- # p SecureRandom.base64 #=> "6BbW0pxO0YENxn38HMUbcQ=="
+ # require 'securerandom'
+ #
+ # SecureRandom.base64 #=> "/2BuBuLf3+WfSKyQbRcc/A=="
+ # SecureRandom.base64 #=> "6BbW0pxO0YENxn38HMUbcQ=="
#
# If a secure random number generator is not available,
# +NotImplementedError+ is raised.
@@ -158,11 +198,13 @@ module Random::Formatter
# The result may contain A-Z, a-z, 0-9, "-" and "_".
# "=" is also used if _padding_ is true.
#
- # p SecureRandom.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg"
- # p SecureRandom.urlsafe_base64 #=> "UZLdOkzop70Ddx-IJR0ABg"
+ # require 'securerandom'
+ #
+ # SecureRandom.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg"
+ # SecureRandom.urlsafe_base64 #=> "UZLdOkzop70Ddx-IJR0ABg"
#
- # p SecureRandom.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ=="
- # p SecureRandom.urlsafe_base64(nil, true) #=> "-M8rLhr7JEpJlqFGUMmOxg=="
+ # SecureRandom.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ=="
+ # SecureRandom.urlsafe_base64(nil, true) #=> "-M8rLhr7JEpJlqFGUMmOxg=="
#
# If a secure random number generator is not available,
# +NotImplementedError+ is raised.
@@ -175,58 +217,13 @@ module Random::Formatter
s
end
-=begin
- # SecureRandom.random_number generates a random number.
- #
- # If a positive integer is given as _n_,
- # +SecureRandom.random_number+ returns an integer, such that:
- # +0 <= SecureRandom.random_number(n) < n+.
- #
- # p SecureRandom.random_number(100) #=> 15
- # p SecureRandom.random_number(100) #=> 88
- #
- # If 0 is given or an argument is not given,
- # +SecureRandom.random_number+ returns a float, such that:
- # +0.0 <= SecureRandom.random_number() < 1.0+.
- #
- # p SecureRandom.random_number #=> 0.596506046187744
- # p SecureRandom.random_number #=> 0.350621695741409
- #
- def random_number(n=0)
- if 0 < n
- if defined? OpenSSL::BN
- OpenSSL::BN.rand_range(n).to_i
- else
- hex = n.to_s(16)
- hex = '0' + hex if (hex.length & 1) == 1
- bin = [hex].pack("H*")
- mask = bin[0].ord
- mask |= mask >> 1
- mask |= mask >> 2
- mask |= mask >> 4
- begin
- rnd = random_bytes(bin.length)
- rnd[0] = (rnd[0].ord & mask).chr
- end until rnd < bin
- rnd.unpack("H*")[0].hex
- end
- else
- # assumption: Float::MANT_DIG <= 64
- if defined? OpenSSL::BN
- i64 = OpenSSL::BN.rand(64, -1).to_i
- else
- i64 = random_bytes(8).unpack("Q")[0]
- end
- Math.ldexp(i64 >> (64-Float::MANT_DIG), -Float::MANT_DIG)
- end
- end
-=end
-
# SecureRandom.uuid generates a random v4 UUID (Universally Unique IDentifier).
#
- # p SecureRandom.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
- # p SecureRandom.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
- # p SecureRandom.uuid #=> "62936e70-1815-439b-bf89-8492855a7e6b"
+ # require 'securerandom'
+ #
+ # SecureRandom.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
+ # SecureRandom.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
+ # SecureRandom.uuid #=> "62936e70-1815-439b-bf89-8492855a7e6b"
#
# The version 4 UUID is purely random (except the version).
# It doesn't contain meaningful information such as MAC addresses, timestamps, etc.
@@ -242,10 +239,78 @@ module Random::Formatter
"%08x-%04x-%04x-%04x-%04x%08x" % ary
end
- private
- def gen_random(n)
+ private def gen_random(n)
self.bytes(n)
end
+
+ # SecureRandom.choose generates a string that randomly draws from a
+ # source array of characters.
+ #
+ # The argument _source_ specifies the array of characters from which
+ # to generate the string.
+ # The argument _n_ specifies the length, in characters, of the string to be
+ # generated.
+ #
+ # The result may contain whatever characters are in the source array.
+ #
+ # require 'securerandom'
+ #
+ # SecureRandom.choose([*'l'..'r'], 16) #=> "lmrqpoonmmlqlron"
+ # SecureRandom.choose([*'0'..'9'], 5) #=> "27309"
+ #
+ # If a secure random number generator is not available,
+ # +NotImplementedError+ is raised.
+ private def choose(source, n)
+ size = source.size
+ m = 1
+ limit = size
+ while limit * size <= 0x100000000
+ limit *= size
+ m += 1
+ end
+ result = ''.dup
+ while m <= n
+ rs = random_number(limit)
+ is = rs.digits(size)
+ (m-is.length).times { is << 0 }
+ result << source.values_at(*is).join('')
+ n -= m
+ end
+ if 0 < n
+ rs = random_number(limit)
+ is = rs.digits(size)
+ if is.length < n
+ (n-is.length).times { is << 0 }
+ else
+ is.pop while n < is.length
+ end
+ result.concat source.values_at(*is).join('')
+ end
+ result
+ end
+
+ ALPHANUMERIC = [*'A'..'Z', *'a'..'z', *'0'..'9']
+ # SecureRandom.alphanumeric generates a random alphanumeric string.
+ #
+ # The argument _n_ specifies the length, in characters, of the alphanumeric
+ # string to be generated.
+ #
+ # If _n_ is not specified or is nil, 16 is assumed.
+ # It may be larger in the future.
+ #
+ # The result may contain A-Z, a-z and 0-9.
+ #
+ # require 'securerandom'
+ #
+ # SecureRandom.alphanumeric #=> "2BuBuLf3WfSKyQbR"
+ # SecureRandom.alphanumeric(10) #=> "i6K93NdqiH"
+ #
+ # If a secure random number generator is not available,
+ # +NotImplementedError+ is raised.
+ def alphanumeric(n=nil)
+ n = 16 if n.nil?
+ choose(ALPHANUMERIC, n)
+ end
end
SecureRandom.extend(Random::Formatter)
diff --git a/lib/set.rb b/lib/set.rb
index 43c388ca90..e639636c97 100644
--- a/lib/set.rb
+++ b/lib/set.rb
@@ -47,21 +47,21 @@
#
# == Comparison
#
-# The comparison operators <, >, <= and >= are implemented as
+# The comparison operators <, >, <=, and >= are implemented as
# shorthand for the {proper_,}{subset?,superset?} methods. However,
# the <=> operator is intentionally left out because not every pair of
-# sets is comparable. ({x,y} vs. {x,z} for example)
+# sets is comparable ({x, y} vs. {x, z} for example).
#
# == Example
#
# require 'set'
-# s1 = Set.new [1, 2] # -> #<Set: {1, 2}>
-# s2 = [1, 2].to_set # -> #<Set: {1, 2}>
-# s1 == s2 # -> true
-# s1.add("foo") # -> #<Set: {1, 2, "foo"}>
-# s1.merge([2, 6]) # -> #<Set: {1, 2, "foo", 6}>
-# s1.subset? s2 # -> false
-# s2.subset? s1 # -> true
+# s1 = Set[1, 2] #=> #<Set: {1, 2}>
+# s2 = [1, 2].to_set #=> #<Set: {1, 2}>
+# s1 == s2 #=> true
+# s1.add("foo") #=> #<Set: {1, 2, "foo"}>
+# s1.merge([2, 6]) #=> #<Set: {1, 2, "foo", 6}>
+# s1.subset?(s2) #=> false
+# s2.subset?(s1) #=> true
#
# == Contact
#
@@ -71,6 +71,10 @@ class Set
include Enumerable
# Creates a new set containing the given objects.
+ #
+ # Set[1, 2] # => #<Set: {1, 2}>
+ # Set[1, 2, 1] # => #<Set: {1, 2}>
+ # Set[1, 'c', :s] # => #<Set: {1, "c", :s}>
def self.[](*ary)
new(ary)
end
@@ -80,6 +84,12 @@ class Set
#
# If a block is given, the elements of enum are preprocessed by the
# given block.
+ #
+ # Set.new([1, 2]) #=> #<Set: {1, 2}>
+ # Set.new([1, 2, 1]) #=> #<Set: {1, 2}>
+ # Set.new([1, 'c', :s]) #=> #<Set: {1, "c", :s}>
+ # Set.new(1..5) #=> #<Set: {1, 2, 3, 4, 5}>
+ # Set.new([1, 2, 3]) { |x| x * x } #=> #<Set: {1, 4, 9}>
def initialize(enum = nil, &block) # :yields: o
@hash ||= Hash.new(false)
@@ -159,6 +169,10 @@ class Set
end
# Removes all elements and returns self.
+ #
+ # set = Set[1, 'c', :s] #=> #<Set: {1, "c", :s}>
+ # set.clear #=> #<Set: {}>
+ # set #=> #<Set: {}>
def clear
@hash.clear
self
@@ -166,6 +180,10 @@ class Set
# Replaces the contents of the set with the contents of the given
# enumerable object and returns self.
+ #
+ # set = Set[1, 'c', :s] #=> #<Set: {1, "c", :s}>
+ # set.replace([1, 2]) #=> #<Set: {1, 2}>
+ # set #=> #<Set: {1, 2}>
def replace(enum)
if enum.instance_of?(self.class)
@hash.replace(enum.instance_variable_get(:@hash))
@@ -178,6 +196,9 @@ class Set
end
# Converts the set to an array. The order of elements is uncertain.
+ #
+ # Set[1, 2].to_a #=> [1, 2]
+ # Set[1, 'c', :s].to_a #=> [1, "c", :s]
def to_a
@hash.keys
end
@@ -237,7 +258,7 @@ class Set
# Returns true if the set is a superset of the given set.
def superset?(set)
case
- when set.instance_of?(self.class)
+ when set.instance_of?(self.class) && @hash.respond_to?(:>=)
@hash >= set.instance_variable_get(:@hash)
when set.is_a?(Set)
size >= set.size && set.all? { |o| include?(o) }
@@ -250,7 +271,7 @@ class Set
# Returns true if the set is a proper superset of the given set.
def proper_superset?(set)
case
- when set.instance_of?(self.class)
+ when set.instance_of?(self.class) && @hash.respond_to?(:>)
@hash > set.instance_variable_get(:@hash)
when set.is_a?(Set)
size > set.size && set.all? { |o| include?(o) }
@@ -263,7 +284,7 @@ class Set
# Returns true if the set is a subset of the given set.
def subset?(set)
case
- when set.instance_of?(self.class)
+ when set.instance_of?(self.class) && @hash.respond_to?(:<=)
@hash <= set.instance_variable_get(:@hash)
when set.is_a?(Set)
size <= set.size && all? { |o| set.include?(o) }
@@ -276,7 +297,7 @@ class Set
# Returns true if the set is a proper subset of the given set.
def proper_subset?(set)
case
- when set.instance_of?(self.class)
+ when set.instance_of?(self.class) && @hash.respond_to?(:<)
@hash < set.instance_variable_get(:@hash)
when set.is_a?(Set)
size < set.size && all? { |o| set.include?(o) }
@@ -289,11 +310,8 @@ class Set
# Returns true if the set and the given set have at least one
# element in common.
#
- # e.g.:
- #
- # require 'set'
- # Set[1, 2, 3].intersect? Set[4, 5] # => false
- # Set[1, 2, 3].intersect? Set[3, 4] # => true
+ # Set[1, 2, 3].intersect? Set[4, 5] #=> false
+ # Set[1, 2, 3].intersect? Set[3, 4] #=> true
def intersect?(set)
set.is_a?(Set) or raise ArgumentError, "value must be a set"
if size < set.size
@@ -306,12 +324,8 @@ class Set
# Returns true if the set and the given set have no element in
# common. This method is the opposite of +intersect?+.
#
- # e.g.:
- #
- # require 'set'
- # Set[1, 2, 3].disjoint? Set[3, 4] # => false
- # Set[1, 2, 3].disjoint? Set[4, 5] # => true
-
+ # Set[1, 2, 3].disjoint? Set[3, 4] #=> false
+ # Set[1, 2, 3].disjoint? Set[4, 5] #=> true
def disjoint?(set)
!intersect?(set)
end
@@ -327,6 +341,10 @@ class Set
# Adds the given object to the set and returns self. Use +merge+ to
# add many elements at once.
+ #
+ # Set[1, 2].add(3) #=> #<Set: {1, 2, 3}>
+ # Set[1, 2].add([3, 4]) #=> #<Set: {1, 2, [3, 4]}>
+ # Set[1, 2].add(2) #=> #<Set: {1, 2}>
def add(o)
@hash[o] = true
self
@@ -335,6 +353,10 @@ class Set
# Adds the given object to the set and returns self. If the
# object is already in the set, returns nil.
+ #
+ # Set[1, 2].add?(3) #=> #<Set: {1, 2, 3}>
+ # Set[1, 2].add?([3, 4]) #=> #<Set: {1, 2, [3, 4]}>
+ # Set[1, 2].add?(2) #=> nil
def add?(o)
add(o) unless include?(o)
end
@@ -378,7 +400,9 @@ class Set
# Returns an enumerator if no block is given.
def collect!
block_given? or return enum_for(__method__) { size }
- replace(self.class.new(self) { |o| yield(o) })
+ set = self.class.new
+ each { |o| set << yield(o) }
+ replace(set)
end
alias map! collect!
@@ -400,6 +424,9 @@ class Set
self if size != n
end
+ # Equivalent to Set#select!
+ alias filter! select!
+
# Merges the elements of the given enumerable object to the set and
# returns self.
def merge(enum)
@@ -421,31 +448,43 @@ class Set
# Returns a new set built by merging the set and the elements of the
# given enumerable object.
+ #
+ # Set[1, 2, 3] | Set[2, 4, 5] #=> #<Set: {1, 2, 3, 4, 5}>
+ # Set[1, 5, 'z'] | (1..6) #=> #<Set: {1, 5, "z", 2, 3, 4, 6}>
def |(enum)
dup.merge(enum)
end
- alias + | ##
- alias union | ##
+ alias + |
+ alias union |
# Returns a new set built by duplicating the set, removing every
# element that appears in the given enumerable object.
+ #
+ # Set[1, 3, 5] - Set[1, 5] #=> #<Set: {3}>
+ # Set['a', 'b', 'z'] - ['a', 'c'] #=> #<Set: {"b", "z"}>
def -(enum)
dup.subtract(enum)
end
- alias difference - ##
+ alias difference -
# Returns a new set containing elements common to the set and the
# given enumerable object.
+ #
+ # Set[1, 3, 5] & Set[3, 2, 1] #=> #<Set: {3, 1}>
+ # Set['a', 'b', 'z'] & ['a', 'b', 'c'] #=> #<Set: {"a", "b"}>
def &(enum)
n = self.class.new
do_with_enum(enum) { |o| n.add(o) if include?(o) }
n
end
- alias intersection & ##
+ alias intersection &
# Returns a new set containing elements exclusive between the set
# and the given enumerable object. (set ^ enum) is equivalent to
# ((set | enum) - (set & enum)).
+ #
+ # Set[1, 2] ^ Set[2, 3] #=> #<Set: {3, 1}>
+ # Set[1, 'b', 'c'] ^ ['b', 'd'] #=> #<Set: {"d", 1, "c"}>
def ^(enum)
n = Set.new(enum)
each { |o| n.add(o) unless n.delete?(o) }
@@ -454,6 +493,11 @@ class Set
# Returns true if two sets are equal. The equality of each couple
# of elements is defined according to Object#eql?.
+ #
+ # Set[1, 2] == Set[2, 1] #=> true
+ # Set[1, 3, 5] == Set[1, 5] #=> false
+ # Set['a', 'b', 'c'] == Set['a', 'c', 'b'] #=> true
+ # Set['a', 'b', 'c'] == ['a', 'c', 'b'] #=> false
def ==(other)
if self.equal?(other)
true
@@ -475,19 +519,52 @@ class Set
@hash.eql?(o.instance_variable_get(:@hash))
end
+ # Resets the internal state after modification to existing elements
+ # and returns self.
+ #
+ # Elements will be reindexed and deduplicated.
+ def reset
+ if @hash.respond_to?(:rehash)
+ @hash.rehash # This should perform frozenness check.
+ else
+ raise "can't modify frozen #{self.class.name}" if frozen?
+ end
+ self
+ end
+
+ # Returns true if the given object is a member of the set,
+ # and false otherwise.
+ #
+ # Used in case statements:
+ #
+ # require 'set'
+ #
+ # case :apple
+ # when Set[:potato, :carrot]
+ # "vegetable"
+ # when Set[:apple, :banana]
+ # "fruit"
+ # end
+ # # => "fruit"
+ #
+ # Or by itself:
+ #
+ # Set[1, 2, 3] === 2 #=> true
+ # Set[1, 2, 3] === 4 #=> false
+ #
+ alias === include?
+
# Classifies the set by the return value of the given block and
# returns a hash of {value => set of elements} pairs. The block is
# called once for each element of the set, passing the element as
# parameter.
#
- # e.g.:
- #
# require 'set'
# files = Set.new(Dir.glob("*.rb"))
# hash = files.classify { |f| File.mtime(f).year }
- # p hash # => {2000=>#<Set: {"a.rb", "b.rb"}>,
- # # 2001=>#<Set: {"c.rb", "d.rb", "e.rb"}>,
- # # 2002=>#<Set: {"f.rb"}>}
+ # hash #=> {2000=>#<Set: {"a.rb", "b.rb"}>,
+ # # 2001=>#<Set: {"c.rb", "d.rb", "e.rb"}>,
+ # # 2002=>#<Set: {"f.rb"}>}
#
# Returns an enumerator if no block is given.
def classify # :yields: o
@@ -509,15 +586,13 @@ class Set
# if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are
# in common if block.call(o1) == block.call(o2).
#
- # e.g.:
- #
# require 'set'
# numbers = Set[1, 3, 4, 6, 9, 10, 11]
# set = numbers.divide { |i,j| (i - j).abs == 1 }
- # p set # => #<Set: {#<Set: {1}>,
- # # #<Set: {11, 9, 10}>,
- # # #<Set: {3, 4}>,
- # # #<Set: {6}>}>
+ # set #=> #<Set: {#<Set: {1}>,
+ # # #<Set: {11, 9, 10}>,
+ # # #<Set: {3, 4}>,
+ # # #<Set: {6}>}>
#
# Returns an enumerator if no block is given.
def divide(&func)
@@ -553,7 +628,7 @@ class Set
InspectKey = :__inspect_key__ # :nodoc:
# Returns a string containing a human-readable representation of the
- # set. ("#<Set: {element1, element2, ...}>")
+ # set ("#<Set: {element1, element2, ...}>").
def inspect
ids = (Thread.current[InspectKey] ||= [])
@@ -569,6 +644,8 @@ class Set
end
end
+ alias to_s inspect
+
def pretty_print(pp) # :nodoc:
pp.text sprintf('#<%s: {', self.class.name)
pp.nest(1) {
@@ -615,6 +692,7 @@ end
#
class SortedSet < Set
@@setup = false
+ @@mutex = Mutex.new
class << self
def [](*ary) # :nodoc:
@@ -624,94 +702,103 @@ class SortedSet < Set
def setup # :nodoc:
@@setup and return
- module_eval {
+ @@mutex.synchronize do
# a hack to shut up warning
- alias old_init initialize
- }
- begin
- require 'rbtree'
-
- module_eval <<-END, __FILE__, __LINE__+1
- def initialize(*args)
- @hash = RBTree.new
- super
- end
-
- def add(o)
- o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>"
- super
- end
- alias << add
- END
- rescue LoadError
- module_eval <<-END, __FILE__, __LINE__+1
- def initialize(*args)
- @keys = nil
- super
- end
-
- def clear
- @keys = nil
- super
- end
-
- def replace(enum)
- @keys = nil
- super
- end
-
- def add(o)
- o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>"
- @keys = nil
- super
- end
- alias << add
-
- def delete(o)
- @keys = nil
- @hash.delete(o)
- self
- end
-
- def delete_if
- block_given? or return enum_for(__method__) { size }
- n = @hash.size
- super
- @keys = nil if @hash.size != n
- self
- end
-
- def keep_if
- block_given? or return enum_for(__method__) { size }
- n = @hash.size
- super
- @keys = nil if @hash.size != n
- self
- end
-
- def merge(enum)
- @keys = nil
- super
- end
-
- def each(&block)
- block or return enum_for(__method__) { size }
- to_a.each(&block)
- self
- end
-
- def to_a
- (@keys = @hash.keys).sort! unless @keys
- @keys
- end
- END
- end
- module_eval {
+ alias_method :old_init, :initialize
+
+ begin
+ require 'rbtree'
+
+ module_eval <<-END, __FILE__, __LINE__+1
+ def initialize(*args)
+ @hash = RBTree.new
+ super
+ end
+
+ def add(o)
+ o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>"
+ super
+ end
+ alias << add
+ END
+ rescue LoadError
+ module_eval <<-END, __FILE__, __LINE__+1
+ def initialize(*args)
+ @keys = nil
+ super
+ end
+
+ def clear
+ @keys = nil
+ super
+ end
+
+ def replace(enum)
+ @keys = nil
+ super
+ end
+
+ def add(o)
+ o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>"
+ @keys = nil
+ super
+ end
+ alias << add
+
+ def delete(o)
+ @keys = nil
+ @hash.delete(o)
+ self
+ end
+
+ def delete_if
+ block_given? or return enum_for(__method__) { size }
+ n = @hash.size
+ super
+ @keys = nil if @hash.size != n
+ self
+ end
+
+ def keep_if
+ block_given? or return enum_for(__method__) { size }
+ n = @hash.size
+ super
+ @keys = nil if @hash.size != n
+ self
+ end
+
+ def merge(enum)
+ @keys = nil
+ super
+ end
+
+ def each(&block)
+ block or return enum_for(__method__) { size }
+ to_a.each(&block)
+ self
+ end
+
+ def to_a
+ (@keys = @hash.keys).sort! unless @keys
+ @keys
+ end
+
+ def freeze
+ to_a
+ super
+ end
+
+ def rehash
+ @keys = nil
+ super
+ end
+ END
+ end
# a hack to shut up warning
remove_method :old_init
- }
- @@setup = true
+ @@setup = true
+ end
end
end
diff --git a/lib/shell.rb b/lib/shell.rb
index fb63717391..77b3b97ed3 100644
--- a/lib/shell.rb
+++ b/lib/shell.rb
@@ -17,6 +17,7 @@ require "forwardable"
require "shell/error"
require "shell/command-processor"
require "shell/process-controller"
+require "shell/version"
# Shell implements an idiomatic Ruby interface for common UNIX shell commands.
#
diff --git a/lib/shell/builtin-command.rb b/lib/shell/builtin-command.rb
index e419a68c33..a6a9d232ad 100644
--- a/lib/shell/builtin-command.rb
+++ b/lib/shell/builtin-command.rb
@@ -10,7 +10,7 @@
#
#
-require "shell/filter"
+require_relative "filter"
class Shell
class BuiltInCommand < Filter
diff --git a/lib/shell/command-processor.rb b/lib/shell/command-processor.rb
index 2239ca98f6..82af76dd5e 100644
--- a/lib/shell/command-processor.rb
+++ b/lib/shell/command-processor.rb
@@ -11,12 +11,11 @@
#
require "e2mmap"
-require "thread"
-require "shell/error"
-require "shell/filter"
-require "shell/system-command"
-require "shell/builtin-command"
+require_relative "error"
+require_relative "filter"
+require_relative "system-command"
+require_relative "builtin-command"
class Shell
# In order to execute a command on your OS, you need to define it as a
@@ -54,8 +53,9 @@ class Shell
# include run file.
#
def self.run_config
+ rc = "~/.rb_shell"
begin
- load File.expand_path("~/.rb_shell") if ENV.key?("HOME")
+ load File.expand_path(rc) if ENV.key?("HOME")
rescue LoadError, Errno::ENOENT
rescue
print "load error: #{rc}\n"
@@ -180,6 +180,9 @@ class Shell
top_level_test(command, file1)
end
else
+ unless FileTest.methods(false).include?(command.to_sym)
+ raise "unsupported command: #{ command }"
+ end
if file2
FileTest.send(command, file1, file2)
else
diff --git a/lib/shell/filter.rb b/lib/shell/filter.rb
index c1f4b28a45..caa976ae3e 100644
--- a/lib/shell/filter.rb
+++ b/lib/shell/filter.rb
@@ -47,7 +47,7 @@ class Shell #:nodoc:
#
# Inputs from +source+, which is either a string of a file name or an IO
# object.
- def < (src)
+ def <(src)
case src
when String
cat = Cat.new(@shell, src)
@@ -56,7 +56,7 @@ class Shell #:nodoc:
self.input = src
self
else
- Shell.Fail Error::CantApplyMethod, "<", to.class
+ Shell.Fail Error::CantApplyMethod, "<", src.class
end
end
@@ -65,7 +65,7 @@ class Shell #:nodoc:
#
# Outputs from +source+, which is either a string of a file name or an IO
# object.
- def > (to)
+ def >(to)
case to
when String
dst = @shell.open(to, "w")
@@ -87,7 +87,7 @@ class Shell #:nodoc:
#
# Appends the output to +source+, which is either a string of a file name
# or an IO object.
- def >> (to)
+ def >>(to)
begin
Shell.cd(@shell.pwd).append(to, self)
rescue CantApplyMethod
@@ -99,7 +99,7 @@ class Shell #:nodoc:
# | filter
#
# Processes a pipeline.
- def | (filter)
+ def |(filter)
filter.input = self
if active?
@shell.process_controller.start_job filter
@@ -111,7 +111,7 @@ class Shell #:nodoc:
# filter1 + filter2
#
# Outputs +filter1+, and then +filter2+ using Join.new
- def + (filter)
+ def +(filter)
Join.new(@shell, self, filter)
end
diff --git a/lib/shell/process-controller.rb b/lib/shell/process-controller.rb
index a536ebd6ee..d54da68cb0 100644
--- a/lib/shell/process-controller.rb
+++ b/lib/shell/process-controller.rb
@@ -10,8 +10,6 @@
#
#
require "forwardable"
-
-require "thread"
require "sync"
class Shell
@@ -260,7 +258,7 @@ class Shell
ObjectSpace.each_object(IO) do |io|
if ![STDIN, STDOUT, STDERR].include?(io)
- io.close unless io.closed?
+ io.close
end
end
diff --git a/lib/shell/shell.gemspec b/lib/shell/shell.gemspec
new file mode 100644
index 0000000000..1c27670ca4
--- /dev/null
+++ b/lib/shell/shell.gemspec
@@ -0,0 +1,26 @@
+begin
+ require_relative "lib/shell/version"
+rescue LoadError
+ # for Ruby core repository
+ require_relative "version"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "shell"
+ spec.version = Shell::VERSION
+ spec.authors = ["Keiju ISHITSUKA"]
+ spec.email = ["keiju@ruby-lang.org"]
+
+ spec.summary = %q{An idiomatic Ruby interface for common UNIX shell commands.}
+ spec.description = %q{An idiomatic Ruby interface for common UNIX shell commands.}
+ spec.homepage = "https://github.com/ruby/shell"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "lib/shell.rb", "lib/shell/builtin-command.rb", "lib/shell/command-processor.rb", "lib/shell/error.rb", "lib/shell/filter.rb", "lib/shell/process-controller.rb", "lib/shell/system-command.rb", "lib/shell/version.rb", "shell.gemspec"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+end
diff --git a/lib/shell/system-command.rb b/lib/shell/system-command.rb
index 81456e7db3..767a9ee12c 100644
--- a/lib/shell/system-command.rb
+++ b/lib/shell/system-command.rb
@@ -10,13 +10,13 @@
#
#
-require "shell/filter"
+require_relative "filter"
class Shell
class SystemCommand < Filter
def initialize(sh, command, *opts)
if t = opts.find{|opt| !opt.kind_of?(String) && opt.class}
- Shell.Fail Error::TypeError, t.class, "String"
+ Shell.Fail TypeError, t.class, "String"
end
super(sh)
@command = command
diff --git a/lib/shell/version.rb b/lib/shell/version.rb
index bb4e7dfb8e..f48f781b2c 100644
--- a/lib/shell/version.rb
+++ b/lib/shell/version.rb
@@ -11,6 +11,7 @@
#
class Shell # :nodoc:
- @RELEASE_VERSION = "0.7"
+ VERSION = "0.7"
+ @RELEASE_VERSION = VERSION
@LAST_UPDATE_DATE = "07/03/20"
end
diff --git a/lib/singleton.rb b/lib/singleton.rb
index deb0f52cd6..368febc74d 100644
--- a/lib/singleton.rb
+++ b/lib/singleton.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: false
-require 'thread'
# The Singleton module implements the Singleton pattern.
#
diff --git a/lib/sync.gemspec b/lib/sync.gemspec
new file mode 100644
index 0000000000..42d40f3e66
--- /dev/null
+++ b/lib/sync.gemspec
@@ -0,0 +1,27 @@
+begin
+ require_relative "lib/sync"
+rescue LoadError
+ # for Ruby core repository
+ require_relative "sync"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "sync"
+ spec.version = Sync::VERSION
+ spec.authors = ["Keiju ISHITSUKA"]
+ spec.email = ["keiju@ruby-lang.org"]
+
+ spec.summary = %q{A module that provides a two-phase lock with a counter.}
+ spec.description = %q{A module that provides a two-phase lock with a counter.}
+ spec.homepage = "https://github.com/ruby/sync"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "lib/sync.rb", "sync.gemspec"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+ spec.add_development_dependency "test-unit"
+end
diff --git a/lib/sync.rb b/lib/sync.rb
index 40a48ac985..998e2b7b92 100644
--- a/lib/sync.rb
+++ b/lib/sync.rb
@@ -38,10 +38,6 @@
#
#
-unless defined? Thread
- raise "Thread not available for this ruby interpreter"
-end
-
##
# A module that provides a two-phase lock with a counter.
@@ -320,6 +316,9 @@ Synchronizer_m = Sync_m
# details.
class Sync
+
+ VERSION = "0.5.0"
+
include Sync_m
end
diff --git a/lib/tempfile.rb b/lib/tempfile.rb
index b0ebd0be0e..f703709113 100644
--- a/lib/tempfile.rb
+++ b/lib/tempfile.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# tempfile - manipulates temporary files
#
@@ -47,7 +47,7 @@ require 'tmpdir'
#
# file = Tempfile.new('foo')
# begin
-# ...do something with file...
+# # ...do something with file...
# ensure
# file.close
# file.unlink # deletes the temp file
@@ -79,9 +79,6 @@ require 'tmpdir'
# same Tempfile object from multiple threads then you should protect it with a
# mutex.
class Tempfile < DelegateClass(File)
- # call-seq:
- # new(basename = "", [tmpdir = Dir.tmpdir], [options])
- #
# Creates a temporary file with permissions 0600 (= only readable and
# writable by the owner) and opens it with mode "w+".
#
@@ -114,17 +111,20 @@ class Tempfile < DelegateClass(File)
# +File.open+. This is mostly useful for specifying encoding
# options, e.g.:
#
- # Tempfile.new('hello', '/home/aisaka', :encoding => 'ascii-8bit')
+ # Tempfile.new('hello', '/home/aisaka', encoding: 'ascii-8bit')
#
# # You can also omit the 'tmpdir' parameter:
- # Tempfile.new('hello', :encoding => 'ascii-8bit')
+ # Tempfile.new('hello', encoding: 'ascii-8bit')
+ #
+ # Note: +mode+ keyword argument, as accepted by Tempfile, can only be
+ # numeric, combination of the modes defined in File::Constants.
#
# === Exceptions
#
# If Tempfile.new cannot find a unique filename within a limited
# number of tries, then it will raise an exception.
def initialize(basename="", tmpdir=nil, mode: 0, **options)
- warn "Tempfile.new doesn't call the given block." if block_given?
+ warn "Tempfile.new doesn't call the given block.", uplevel: 1 if block_given?
@unlinked = false
@mode = mode|File::RDWR|File::CREAT|File::EXCL
@@ -147,7 +147,7 @@ class Tempfile < DelegateClass(File)
end
def _close # :nodoc:
- @tmpfile.close unless @tmpfile.closed?
+ @tmpfile.close
end
protected :_close
@@ -174,7 +174,7 @@ class Tempfile < DelegateClass(File)
#
# file = Tempfile.new('foo')
# begin
- # ...do something with file...
+ # # ...do something with file...
# ensure
# file.close
# file.unlink # deletes the temp file
@@ -195,7 +195,7 @@ class Tempfile < DelegateClass(File)
# file = Tempfile.new('foo')
# file.unlink # On Windows this silently fails.
# begin
- # ... do something with file ...
+ # # ... do something with file ...
# ensure
# file.close! # Closes the file handle. If the file wasn't unlinked
# # because #unlink failed, then this method will attempt
@@ -227,7 +227,7 @@ class Tempfile < DelegateClass(File)
if !@tmpfile.closed?
@tmpfile.size # File#size calls rb_io_flush_raw()
else
- File.size?(@tmpfile.path)
+ File.size(@tmpfile.path)
end
end
alias length size
@@ -241,7 +241,7 @@ class Tempfile < DelegateClass(File)
end
end
- class Remover
+ class Remover # :nodoc:
def initialize(tmpfile)
@pid = Process.pid
@tmpfile = tmpfile
@@ -250,15 +250,15 @@ class Tempfile < DelegateClass(File)
def call(*args)
return if @pid != Process.pid
- warn "removing #{@tmpfile.path}..." if $DEBUG
+ $stderr.puts "removing #{@tmpfile.path}..." if $DEBUG
- @tmpfile.close unless @tmpfile.closed?
+ @tmpfile.close
begin
File.unlink(@tmpfile.path)
rescue Errno::ENOENT
end
- warn "done" if $DEBUG
+ $stderr.puts "done" if $DEBUG
end
end
@@ -274,16 +274,16 @@ class Tempfile < DelegateClass(File)
# object will be automatically closed after the block terminates.
# The call returns the value of the block.
#
- # In any case, all arguments (+*args+) will be passed to Tempfile.new.
+ # In any case, all arguments (<code>*args</code>) will be passed to Tempfile.new.
#
# Tempfile.open('foo', '/home/temp') do |f|
- # ... do something with f ...
+ # # ... do something with f ...
# end
#
# # Equivalent:
# f = Tempfile.open('foo', '/home/temp')
# begin
- # ... do something with f ...
+ # # ... do something with f ...
# ensure
# f.close
# end
@@ -317,10 +317,11 @@ end
# the temporary file is removed after the block terminates.
# The call returns the value of the block.
#
-# In any case, all arguments (+*args+) will be treated as Tempfile.new.
+# In any case, all arguments (+basename+, +tmpdir+, +mode+, and
+# <code>**options</code>) will be treated as Tempfile.new.
#
# Tempfile.create('foo', '/home/temp') do |f|
-# ... do something with f ...
+# # ... do something with f ...
# end
#
def Tempfile.create(basename="", tmpdir=nil, mode: 0, **options)
@@ -334,8 +335,18 @@ def Tempfile.create(basename="", tmpdir=nil, mode: 0, **options)
begin
yield tmpfile
ensure
- tmpfile.close if !tmpfile.closed?
- File.unlink tmpfile
+ unless tmpfile.closed?
+ if File.identical?(tmpfile, tmpfile.path)
+ unlinked = File.unlink tmpfile.path rescue nil
+ end
+ tmpfile.close
+ end
+ unless unlinked
+ begin
+ File.unlink tmpfile.path
+ rescue Errno::ENOENT
+ end
+ end
end
else
tmpfile
diff --git a/lib/thwait.rb b/lib/thwait.rb
index 239915baef..541fe1e3c3 100644
--- a/lib/thwait.rb
+++ b/lib/thwait.rb
@@ -5,8 +5,7 @@
# $Revision: 1.3 $
# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd.)
-require "thread.rb"
-require "e2mmap.rb"
+require "e2mmap"
#
# This class watches for termination of multiple threads. Basic functionality
diff --git a/lib/thwait/thwait.gemspec b/lib/thwait/thwait.gemspec
new file mode 100644
index 0000000000..fc03942aba
--- /dev/null
+++ b/lib/thwait/thwait.gemspec
@@ -0,0 +1,26 @@
+begin
+ require_relative "lib/thwait/version"
+rescue LoadError
+ # for Ruby core repository
+ require_relative "version"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "thwait"
+ spec.version = ThreadsWait::VERSION
+ spec.authors = ["Keiju ISHITSUKA"]
+ spec.email = ["keiju@ruby-lang.org"]
+
+ spec.summary = %q{Watches for termination of multiple threads.}
+ spec.description = %q{Watches for termination of multiple threads.}
+ spec.homepage = "https://github.com/ruby/thwait"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "lib/thwait.rb", "lib/thwait/version.rb", "thwait.gemspec"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler", "~> 1.16"
+ spec.add_development_dependency "rake", "~> 10.0"
+end
diff --git a/lib/thwait/version.rb b/lib/thwait/version.rb
new file mode 100644
index 0000000000..a3ffb3af16
--- /dev/null
+++ b/lib/thwait/version.rb
@@ -0,0 +1,3 @@
+class ThreadsWait
+ VERSION = "0.1.0"
+end
diff --git a/lib/time.rb b/lib/time.rb
index 5179e9fee4..81c0ebd7ba 100644
--- a/lib/time.rb
+++ b/lib/time.rb
@@ -2,6 +2,8 @@
require 'date'
+# :stopdoc:
+
# = time.rb
#
# When 'time' is required, Time is extended with additional methods for parsing
@@ -18,73 +20,8 @@ require 'date'
# 8601}[http://www.iso.org/iso/date_and_time_format])
# * various formats handled by Date._parse
# * custom formats handled by Date._strptime
-#
-# == Examples
-#
-# All examples assume you have loaded Time with:
-#
-# require 'time'
-#
-# All of these examples were done using the EST timezone which is GMT-5.
-#
-# === Converting to a String
-#
-# t = Time.now
-# t.iso8601 # => "2011-10-05T22:26:12-04:00"
-# t.rfc2822 # => "Wed, 05 Oct 2011 22:26:12 -0400"
-# t.httpdate # => "Thu, 06 Oct 2011 02:26:12 GMT"
-#
-# === Time.parse
-#
-# #parse takes a string representation of a Time and attempts to parse it
-# using a heuristic.
-#
-# Time.parse("2010-10-31") #=> 2010-10-31 00:00:00 -0500
-#
-# Any missing pieces of the date are inferred based on the current date.
-#
-# # assuming the current date is "2011-10-31"
-# Time.parse("12:00") #=> 2011-10-31 12:00:00 -0500
-#
-# We can change the date used to infer our missing elements by passing a second
-# object that responds to #mon, #day and #year, such as Date, Time or DateTime.
-# We can also use our own object.
-#
-# class MyDate
-# attr_reader :mon, :day, :year
-#
-# def initialize(mon, day, year)
-# @mon, @day, @year = mon, day, year
-# end
-# end
-#
-# d = Date.parse("2010-10-28")
-# t = Time.parse("2010-10-29")
-# dt = DateTime.parse("2010-10-30")
-# md = MyDate.new(10,31,2010)
-#
-# Time.parse("12:00", d) #=> 2010-10-28 12:00:00 -0500
-# Time.parse("12:00", t) #=> 2010-10-29 12:00:00 -0500
-# Time.parse("12:00", dt) #=> 2010-10-30 12:00:00 -0500
-# Time.parse("12:00", md) #=> 2010-10-31 12:00:00 -0500
-#
-# #parse also accepts an optional block. You can use this block to specify how
-# to handle the year component of the date. This is specifically designed for
-# handling two digit years. For example, if you wanted to treat all two digit
-# years prior to 70 as the year 2000+ you could write this:
-#
-# Time.parse("01-10-31") {|year| year + (year < 70 ? 2000 : 1900)}
-# #=> 2001-10-31 00:00:00 -0500
-# Time.parse("70-10-31") {|year| year + (year < 70 ? 2000 : 1900)}
-# #=> 1970-10-31 00:00:00 -0500
-#
-# === Time.strptime
-#
-# #strptime works similar to +parse+ except that instead of using a heuristic
-# to detect the format of the input string, you provide a second argument that
-# describes the format of the string. For example:
-#
-# Time.strptime("2000-10-31", "%Y-%m-%d") #=> 2000-10-31 00:00:00 -0500
+
+# :startdoc:
class Time
class << Time
@@ -131,12 +68,19 @@ class Time
#
# If +zone_offset+ is unable to determine the offset, nil will be
# returned.
+ #
+ # require 'time'
+ #
+ # Time.zone_offset("EST") #=> -18000
+ #
+ # You must require 'time' to use this method.
+ #
def zone_offset(zone, year=self.now.year)
off = nil
zone = zone.upcase
- if /\A([+-])(\d\d):?(\d\d)\z/ =~ zone
- off = ($1 == '-' ? -1 : 1) * ($2.to_i * 60 + $3.to_i) * 60
- elsif /\A[+-]\d\d\z/ =~ zone
+ if /\A([+-])(\d\d)(:?)(\d\d)(?:\3(\d\d))?\z/ =~ zone
+ off = ($1 == '-' ? -1 : 1) * (($2.to_i * 60 + $4.to_i) * 60 + $5.to_i)
+ elsif zone.match?(/\A[+-]\d\d\z/)
off = zone.to_i * 3600
elsif ZoneOffset.include?(zone)
off = ZoneOffset[zone] * 3600
@@ -168,11 +112,7 @@ class Time
# They are not appropriate for specific time zone such as
# Europe/London because time zone neutral,
# So -00:00 and -0000 are treated as UTC.
- if /\A(?:-00:00|-0000|-00|UTC|Z|UT)\z/i =~ zone
- true
- else
- false
- end
+ zone.match?(/\A(?:-00:00|-0000|-00|UTC|Z|UT)\z/i)
end
private :zone_utc?
@@ -249,8 +189,8 @@ class Time
end
private :apply_offset
- def make_time(date, year, mon, day, hour, min, sec, sec_fraction, zone, now)
- if !year && !mon && !day && !hour && !min && !sec && !sec_fraction
+ def make_time(date, year, yday, mon, day, hour, min, sec, sec_fraction, zone, now)
+ if !year && !yday && !mon && !day && !hour && !min && !sec && !sec_fraction
raise ArgumentError, "no time information in #{date.inspect}"
end
@@ -260,7 +200,27 @@ class Time
off = zone_offset(zone, off_year) if zone
end
- if now
+ if yday
+ unless (1..366) === yday
+ raise ArgumentError, "yday #{yday} out of range"
+ end
+ mon, day = (yday-1).divmod(31)
+ mon += 1
+ day += 1
+ t = make_time(date, year, nil, mon, day, hour, min, sec, sec_fraction, zone, now)
+ diff = yday - t.yday
+ return t if diff.zero?
+ day += diff
+ if day > 28 and day > (mday = month_days(off_year, mon))
+ if (mon += 1) > 12
+ raise ArgumentError, "yday #{yday} out of range"
+ end
+ day -= mday
+ end
+ return make_time(date, year, nil, mon, day, hour, min, sec, sec_fraction, zone, now)
+ end
+
+ if now and now.respond_to?(:getlocal)
if off
now = now.getlocal(off) if now.utc_offset != off
else
@@ -309,17 +269,62 @@ class Time
private :make_time
#
- # Parses +date+ using Date._parse and converts it to a Time object.
+ # Takes a string representation of a Time and attempts to parse it
+ # using a heuristic.
#
- # If a block is given, the year described in +date+ is converted by the
- # block. For example:
+ # require 'time'
+ #
+ # Time.parse("2010-10-31") #=> 2010-10-31 00:00:00 -0500
+ #
+ # Any missing pieces of the date are inferred based on the current date.
+ #
+ # require 'time'
+ #
+ # # assuming the current date is "2011-10-31"
+ # Time.parse("12:00") #=> 2011-10-31 12:00:00 -0500
+ #
+ # We can change the date used to infer our missing elements by passing a second
+ # object that responds to #mon, #day and #year, such as Date, Time or DateTime.
+ # We can also use our own object.
+ #
+ # require 'time'
+ #
+ # class MyDate
+ # attr_reader :mon, :day, :year
#
- # Time.parse(...) {|y| 0 <= y && y < 100 ? (y >= 69 ? y + 1900 : y + 2000) : y}
+ # def initialize(mon, day, year)
+ # @mon, @day, @year = mon, day, year
+ # end
+ # end
+ #
+ # d = Date.parse("2010-10-28")
+ # t = Time.parse("2010-10-29")
+ # dt = DateTime.parse("2010-10-30")
+ # md = MyDate.new(10,31,2010)
+ #
+ # Time.parse("12:00", d) #=> 2010-10-28 12:00:00 -0500
+ # Time.parse("12:00", t) #=> 2010-10-29 12:00:00 -0500
+ # Time.parse("12:00", dt) #=> 2010-10-30 12:00:00 -0500
+ # Time.parse("12:00", md) #=> 2010-10-31 12:00:00 -0500
+ #
+ # If a block is given, the year described in +date+ is converted
+ # by the block. This is specifically designed for handling two
+ # digit years. For example, if you wanted to treat all two digit
+ # years prior to 70 as the year 2000+ you could write this:
+ #
+ # require 'time'
+ #
+ # Time.parse("01-10-31") {|year| year + (year < 70 ? 2000 : 1900)}
+ # #=> 2001-10-31 00:00:00 -0500
+ # Time.parse("70-10-31") {|year| year + (year < 70 ? 2000 : 1900)}
+ # #=> 1970-10-31 00:00:00 -0500
#
# If the upper components of the given time are broken or missing, they are
# supplied with those of +now+. For the lower components, the minimum
# values (1 or 0) are assumed if broken or missing. For example:
#
+ # require 'time'
+ #
# # Suppose it is "Thu Nov 29 14:33:20 2001" now and
# # your time zone is EST which is GMT-5.
# now = Time.parse("Thu Nov 29 14:33:20 2001")
@@ -367,11 +372,13 @@ class Time
d = Date._parse(date, comp)
year = d[:year]
year = yield(year) if year && !comp
- make_time(date, year, d[:mon], d[:mday], d[:hour], d[:min], d[:sec], d[:sec_fraction], d[:zone], now)
+ make_time(date, year, d[:yday], d[:mon], d[:mday], d[:hour], d[:min], d[:sec], d[:sec_fraction], d[:zone], now)
end
#
- # Parses +date+ using Date._strptime and converts it to a Time object.
+ # Works similar to +parse+ except that instead of using a
+ # heuristic to detect the format of the input string, you provide
+ # a second argument that describes the format of the string.
#
# If a block is given, the year described in +date+ is converted by the
# block. For example:
@@ -400,12 +407,10 @@ class Time
# %m :: Month of the year (01..12)
# %M :: Minute of the hour (00..59)
# %n :: Newline (\n)
- # %N :: Fractional seconds digits, default is 9 digits (nanosecond)
- # %3N :: millisecond (3 digits)
- # %6N :: microsecond (6 digits)
- # %9N :: nanosecond (9 digits)
+ # %N :: Fractional seconds digits
# %p :: Meridian indicator ("AM" or "PM")
# %P :: Meridian indicator ("am" or "pm")
+ # %Q :: Number of milliseconds since 1970-01-01 00:00:00 UTC.
# %r :: time, 12-hour (same as %I:%M:%S %p)
# %R :: time, 24-hour (%H:%M)
# %s :: Number of seconds since 1970-01-01 00:00:00 UTC.
@@ -427,10 +432,17 @@ class Time
# %z :: Time zone as hour offset from UTC (e.g. +0900)
# %Z :: Time zone name
# %% :: Literal "%" character
-
+ # %+ :: date(1) (%a %b %e %H:%M:%S %Z %Y)
+ #
+ # require 'time'
+ #
+ # Time.strptime("2000-10-31", "%Y-%m-%d") #=> 2000-10-31 00:00:00 -0500
+ #
+ # You must require 'time' to use this method.
+ #
def strptime(date, format, now=self.now)
d = Date._strptime(date, format)
- raise ArgumentError, "invalid strptime format - `#{format}'" unless d
+ raise ArgumentError, "invalid date or strptime format - `#{date}' `#{format}'" unless d
if seconds = d[:seconds]
if sec_fraction = d[:sec_fraction]
usec = sec_fraction * 1000000
@@ -445,7 +457,7 @@ class Time
else
year = d[:year]
year = yield(year) if year && block_given?
- t = make_time(date, year, d[:mon], d[:mday], d[:hour], d[:min], d[:sec], d[:sec_fraction], d[:zone], now)
+ t = make_time(date, year, d[:yday], d[:mon], d[:mday], d[:hour], d[:min], d[:sec], d[:sec_fraction], d[:zone], now)
end
t
end
@@ -465,6 +477,11 @@ class Time
#
# See #rfc2822 for more information on this format.
#
+ # require 'time'
+ #
+ # Time.rfc2822("Wed, 05 Oct 2011 22:26:12 -0400")
+ # #=> 2010-10-05 22:26:12 -0400
+ #
# You must require 'time' to use this method.
#
def rfc2822(date)
@@ -518,17 +535,22 @@ class Time
#
# See #httpdate for more information on this format.
#
+ # require 'time'
+ #
+ # Time.httpdate("Thu, 06 Oct 2011 02:26:12 GMT")
+ # #=> 2011-10-06 02:26:12 UTC
+ #
# You must require 'time' to use this method.
#
def httpdate(date)
- if /\A\s*
+ if date.match?(/\A\s*
(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\x20
(\d{2})\x20
(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\x20
(\d{4})\x20
(\d{2}):(\d{2}):(\d{2})\x20
GMT
- \s*\z/ix =~ date
+ \s*\z/ix)
self.rfc2822(date).utc
elsif /\A\s*
(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday),\x20
@@ -567,6 +589,11 @@ class Time
#
# See #xmlschema for more information on this format.
#
+ # require 'time'
+ #
+ # Time.xmlschema("2011-10-05T22:26:12-04:00")
+ # #=> 2011-10-05 22:26:12-04:00
+ #
# You must require 'time' to use this method.
#
def xmlschema(date)
@@ -575,7 +602,7 @@ class Time
T
(\d\d):(\d\d):(\d\d)
(\.\d+)?
- (Z|[+-]\d\d:\d\d)?
+ (Z|[+-]\d\d(?::?\d\d)?)?
\s*\z/ix =~ date
year = $1.to_i
mon = $2.to_i
@@ -614,6 +641,11 @@ class Time
#
# If +self+ is a UTC time, -0000 is used as zone.
#
+ # require 'time'
+ #
+ # t = Time.now
+ # t.rfc2822 # => "Wed, 05 Oct 2011 22:26:12 -0400"
+ #
# You must require 'time' to use this method.
#
def rfc2822
@@ -649,6 +681,11 @@ class Time
#
# Note that the result is always UTC (GMT).
#
+ # require 'time'
+ #
+ # t = Time.now
+ # t.httpdate # => "Thu, 06 Oct 2011 02:26:12 GMT"
+ #
# You must require 'time' to use this method.
#
def httpdate
@@ -673,6 +710,11 @@ class Time
# +fractional_digits+ specifies a number of digits to use for fractional
# seconds. Its default value is 0.
#
+ # require 'time'
+ #
+ # t = Time.now
+ # t.iso8601 # => "2011-10-05T22:26:12-04:00"
+ #
# You must require 'time' to use this method.
#
def xmlschema(fraction_digits=0)
diff --git a/lib/timeout.rb b/lib/timeout.rb
index 9a0fc91156..a33bb4ce65 100644
--- a/lib/timeout.rb
+++ b/lib/timeout.rb
@@ -15,7 +15,7 @@
#
# Previous versions didn't use a module for namespacing, however
# #timeout is provided for backwards compatibility. You
-# should prefer Timeout#timeout instead.
+# should prefer Timeout.timeout instead.
#
# == Copyright
#
@@ -23,7 +23,7 @@
# Copyright:: (C) 2000 Information-technology Promotion Agency, Japan
module Timeout
- # Raised by Timeout#timeout when the block times out.
+ # Raised by Timeout.timeout when the block times out.
class Error < RuntimeError
attr_reader :thread
@@ -118,7 +118,7 @@ module Timeout
end
def timeout(*args, &block)
- warn "#{caller_locations(1, 1)[0]}: Object##{__method__} is deprecated, use Timeout.timeout instead."
+ warn "Object##{__method__} is deprecated, use Timeout.timeout instead.", uplevel: 1
Timeout.timeout(*args, &block)
end
diff --git a/lib/tmpdir.rb b/lib/tmpdir.rb
index d12afa1ae6..9d61595ec2 100644
--- a/lib/tmpdir.rb
+++ b/lib/tmpdir.rb
@@ -83,14 +83,20 @@ class Dir
# end
#
def self.mktmpdir(prefix_suffix=nil, *rest)
- path = Tmpname.create(prefix_suffix || "d", *rest) {|n| mkdir(n, 0700)}
+ base = nil
+ path = Tmpname.create(prefix_suffix || "d", *rest) {|path, _, _, d|
+ base = d
+ mkdir(path, 0700)
+ }
if block_given?
begin
- yield path
+ yield path.dup
ensure
- stat = File.stat(File.dirname(path))
- if stat.world_writable? and !stat.sticky?
- raise ArgumentError, "parent directory is world writable but not sticky"
+ 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
FileUtils.remove_entry path
end
@@ -106,28 +112,27 @@ class Dir
Dir.tmpdir
end
- def make_tmpname((prefix, suffix), n)
- prefix = (String.try_convert(prefix) or
- raise ArgumentError, "unexpected prefix: #{prefix.inspect}")
- suffix &&= (String.try_convert(suffix) or
- raise ArgumentError, "unexpected suffix: #{suffix.inspect}")
- t = Time.now.strftime("%Y%m%d")
- path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}".dup
- path << "-#{n}" if n
- path << suffix if suffix
- path
- end
-
def create(basename, tmpdir=nil, max_try: nil, **opts)
if $SAFE > 0 and tmpdir.tainted?
tmpdir = '/tmp'
else
+ origdir = tmpdir
tmpdir ||= tmpdir()
end
n = nil
+ prefix, suffix = basename
+ prefix = (String.try_convert(prefix) or
+ raise ArgumentError, "unexpected prefix: #{prefix.inspect}")
+ prefix = prefix.delete("#{File::SEPARATOR}#{File::ALT_SEPARATOR}")
+ suffix &&= (String.try_convert(suffix) or
+ raise ArgumentError, "unexpected suffix: #{suffix.inspect}")
+ suffix &&= suffix.delete("#{File::SEPARATOR}#{File::ALT_SEPARATOR}")
begin
- path = File.join(tmpdir, make_tmpname(basename, n))
- yield(path, n, opts)
+ t = Time.now.strftime("%Y%m%d")
+ path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"\
+ "#{n ? %[-#{n}] : ''}#{suffix||''}"
+ path = File.join(tmpdir, path)
+ yield(path, n, opts, origdir)
rescue Errno::EEXIST
n ||= 0
n += 1
diff --git a/lib/tracer.rb b/lib/tracer.rb
index 24a5313f4c..e463ae1467 100644
--- a/lib/tracer.rb
+++ b/lib/tracer.rb
@@ -2,7 +2,6 @@
#--
# $Release Version: 0.3$
# $Revision: 1.12 $
-require "thread"
##
# Outputs a source level execution trace of a Ruby program.
@@ -61,6 +60,7 @@ require "thread"
# by Keiju ISHITSUKA(keiju@ishitsuka.com)
#
class Tracer
+
class << self
# display additional debug information (defaults to false)
attr_accessor :verbose
diff --git a/lib/tracer/tracer.gemspec b/lib/tracer/tracer.gemspec
new file mode 100644
index 0000000000..f198f3b3c2
--- /dev/null
+++ b/lib/tracer/tracer.gemspec
@@ -0,0 +1,26 @@
+begin
+ require_relative "lib/tracer/version"
+rescue LoadError
+ # for Ruby core repository
+ require_relative "version"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "tracer"
+ spec.version = Tracer::VERSION
+ spec.authors = ["Keiju ISHITSUKA"]
+ spec.email = ["keiju@ruby-lang.org"]
+
+ spec.summary = %q{Outputs a source level execution trace of a Ruby program.}
+ spec.description = %q{Outputs a source level execution trace of a Ruby program.}
+ spec.homepage = "https://github.com/ruby/tracer"
+ spec.license = "BSD-2-Clause"
+
+ spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "lib/tracer.rb", "lib/tracer/version.rb", "tracer.gemspec"]
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler"
+ spec.add_development_dependency "rake"
+end
diff --git a/lib/tracer/version.rb b/lib/tracer/version.rb
new file mode 100644
index 0000000000..f1b6dcd094
--- /dev/null
+++ b/lib/tracer/version.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class Tracer
+ VERSION = "0.1.0"
+end
diff --git a/lib/ubygems.rb b/lib/ubygems.rb
deleted file mode 100644
index 51ee23e880..0000000000
--- a/lib/ubygems.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-# This file allows for the running of rubygems with a nice
-# command line look-and-feel: ruby -rubygems foo.rb
-#--
-# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
-# All rights reserved.
-# See LICENSE.txt for permissions.
-#++
-
-
-require 'rubygems'
diff --git a/lib/un.rb b/lib/un.rb
index b644fd9be4..14f5b10fec 100644
--- a/lib/un.rb
+++ b/lib/un.rb
@@ -47,7 +47,7 @@ def setup(options = "", *long_options)
end
long_options.each do |s|
opt_name, arg_name = s.split(/(?=[\s=])/, 2)
- opt_name.sub!(/\A--/, '')
+ opt_name.delete_prefix!('--')
s = "--#{opt_name.gsub(/([A-Z]+|[a-z])([A-Z])/, '\1-\2').downcase}#{arg_name}"
puts "#{opt_name}=>#{s}" if $DEBUG
opt_name = opt_name.intern
@@ -313,18 +313,33 @@ end
# --do-not-reverse-lookup disable reverse lookup
# --request-timeout=SECOND request timeout in seconds
# --http-version=VERSION HTTP version
+# --server-name=NAME name of the server host
+# --server-software=NAME name and version of the server
+# --ssl-certificate=CERT The SSL certificate file for the server
+# --ssl-private-key=KEY The SSL private key file for the server certificate
# -v verbose
#
def httpd
setup("", "BindAddress=ADDR", "Port=PORT", "MaxClients=NUM", "TempDir=DIR",
- "DoNotReverseLookup", "RequestTimeout=SECOND", "HTTPVersion=VERSION") do
+ "DoNotReverseLookup", "RequestTimeout=SECOND", "HTTPVersion=VERSION",
+ "ServerName=NAME", "ServerSoftware=NAME",
+ "SSLCertificate=CERT", "SSLPrivateKey=KEY") do
|argv, options|
require 'webrick'
opt = options[:RequestTimeout] and options[:RequestTimeout] = opt.to_i
[:Port, :MaxClients].each do |name|
opt = options[name] and (options[name] = Integer(opt)) rescue nil
end
+ if cert = options[:SSLCertificate]
+ key = options[:SSLPrivateKey] or
+ raise "--ssl-private-key option must also be given"
+ require 'webrick/https'
+ options[:SSLEnable] = true
+ options[:SSLCertificate] = OpenSSL::X509::Certificate.new(File.read(cert))
+ options[:SSLPrivateKey] = OpenSSL::PKey.read(File.read(key))
+ options[:Port] ||= 8443 # HTTPS Alternate
+ end
options[:Port] ||= 8080 # HTTP Alternate
options[:DocumentRoot] = argv.shift || '.'
s = WEBrick::HTTPServer.new(options)
diff --git a/lib/unicode_normalize.rb b/lib/unicode_normalize.rb
deleted file mode 100644
index 8daf7b301a..0000000000
--- a/lib/unicode_normalize.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-# coding: utf-8
-# frozen_string_literal: false
-
-# Copyright Ayumu Nojima (野島 歩) and Martin J. Dürst (duerst@it.aoyama.ac.jp)
-
-# additions to class String for Unicode normalization
-class String
- # === Unicode Normalization
- #
- # :call-seq:
- # str.unicode_normalize(form=:nfc)
- #
- # Returns a normalized form of +str+, using Unicode normalizations
- # NFC, NFD, NFKC, or NFKD. The normalization form used is determined
- # by +form+, which is any of the four values :nfc, :nfd, :nfkc, or :nfkd.
- # The default is :nfc.
- #
- # If the string is not in a Unicode Encoding, then an Exception is raised.
- # In this context, 'Unicode Encoding' means any of UTF-8, UTF-16BE/LE,
- # and UTF-32BE/LE, as well as GB18030, UCS_2BE, and UCS_4BE. Anything
- # else than UTF-8 is implemented by converting to UTF-8,
- # which makes it slower than UTF-8.
- #
- # _Examples_
- #
- # "a\u0300".unicode_normalize #=> 'à' (same as "\u00E0")
- # "a\u0300".unicode_normalize(:nfc) #=> 'à' (same as "\u00E0")
- # "\u00E0".unicode_normalize(:nfd) #=> 'à' (same as "a\u0300")
- # "\xE0".force_encoding('ISO-8859-1').unicode_normalize(:nfd)
- # #=> Encoding::CompatibilityError raised
- #
- def unicode_normalize(form = :nfc)
- require 'unicode_normalize/normalize.rb' unless defined? UnicodeNormalize
- ## The following line can be uncommented to avoid repeated checking for
- ## UnicodeNormalize. However, tests didn't show any noticeable speedup
- ## when doing this. This comment also applies to the commented out lines
- ## in String#unicode_normalize! and String#unicode_normalized?.
- # String.send(:define_method, :unicode_normalize, ->(form = :nfc) { UnicodeNormalize.normalize(self, form) } )
- UnicodeNormalize.normalize(self, form)
- end
-
- # :call-seq:
- # str.unicode_normalize!(form=:nfc)
- #
- # Destructive version of String#unicode_normalize, doing Unicode
- # normalization in place.
- #
- def unicode_normalize!(form = :nfc)
- require 'unicode_normalize/normalize.rb' unless defined? UnicodeNormalize
- # String.send(:define_method, :unicode_normalize!, ->(form = :nfc) { replace(unicode_normalize(form)) } )
- replace(unicode_normalize(form))
- end
-
- # :call-seq:
- # str.unicode_normalized?(form=:nfc)
- #
- # Checks whether +str+ is in Unicode normalization form +form+,
- # which is any of the four values :nfc, :nfd, :nfkc, or :nfkd.
- # The default is :nfc.
- #
- # If the string is not in a Unicode Encoding, then an Exception is raised.
- # For details, see String#unicode_normalize.
- #
- # _Examples_
- #
- # "a\u0300".unicode_normalized? #=> false
- # "a\u0300".unicode_normalized?(:nfd) #=> true
- # "\u00E0".unicode_normalized? #=> true
- # "\u00E0".unicode_normalized?(:nfd) #=> false
- # "\xE0".force_encoding('ISO-8859-1').unicode_normalized?
- # #=> Encoding::CompatibilityError raised
- #
- def unicode_normalized?(form = :nfc)
- require 'unicode_normalize/normalize.rb' unless defined? UnicodeNormalize
- # String.send(:define_method, :unicode_normalized?, ->(form = :nfc) { UnicodeNormalize.normalized?(self, form) } )
- UnicodeNormalize.normalized?(self, form)
- end
-end
-
diff --git a/lib/unicode_normalize/normalize.rb b/lib/unicode_normalize/normalize.rb
index 8f0e8a20d1..b27cdadaaa 100644
--- a/lib/unicode_normalize/normalize.rb
+++ b/lib/unicode_normalize/normalize.rb
@@ -3,10 +3,25 @@
# Copyright Ayumu Nojima (野島 歩) and Martin J. Dürst (duerst@it.aoyama.ac.jp)
-require 'unicode_normalize/tables.rb'
+# This file, the companion file tables.rb (autogenerated), and the module,
+# constants, and method defined herein are part of the implementation of the
+# built-in String class, not part of the standard library. They should
+# therefore never be gemified. They implement the methods
+# String#unicode_normalize, String#unicode_normalize!, and String#unicode_normalized?.
+#
+# They are placed here because they are written in Ruby. They are loaded on
+# demand when any of the three methods mentioned above is executed for the
+# first time. This reduces the memory footprint and startup time for scripts
+# and applications that do not use those methods.
+#
+# The name and even the existence of the module UnicodeNormalize and all of its
+# content are purely an implementation detail, and should not be exposed in
+# any test or spec or otherwise.
+require_relative 'tables'
-module UnicodeNormalize
+
+module UnicodeNormalize # :nodoc:
## Constant for max hash capacity to avoid DoS attack
MAX_HASH_LENGTH = 18000 # enough for all test cases, otherwise tests get slow
@@ -55,7 +70,7 @@ module UnicodeNormalize
if length>1 and 0 <= (lead =string[0].ord-LBASE) and lead < LCOUNT and
0 <= (vowel=string[1].ord-VBASE) and vowel < VCOUNT
lead_vowel = SBASE + (lead * VCOUNT + vowel) * TCOUNT
- if length>2 and 0 <= (trail=string[2].ord-TBASE) and trail < TCOUNT
+ if length>2 and 0 < (trail=string[2].ord-TBASE) and trail < TCOUNT
(lead_vowel + trail).chr(Encoding::UTF_8) + string[3..-1]
else
lead_vowel.chr(Encoding::UTF_8) + string[2..-1]
@@ -157,5 +172,4 @@ module UnicodeNormalize
raise Encoding::CompatibilityError, "Unicode Normalization not appropriate for #{encoding}"
end
end
-
end # module
diff --git a/lib/unicode_normalize/tables.rb b/lib/unicode_normalize/tables.rb
index 13a48f739c..a36daa84e6 100644
--- a/lib/unicode_normalize/tables.rb
+++ b/lib/unicode_normalize/tables.rb
@@ -3,1165 +3,9037 @@
# automatically generated by template/unicode_norm_gen.tmpl
-module UnicodeNormalize
+module UnicodeNormalize # :nodoc:
accents = "" \
- "[\u0300-\u034E\u0350-\u036F\u0483-\u0487\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7" \
- "\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711" \
- "\u0730-\u074A\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1" \
- "\u08E3-\u08FF\u093C\u094D\u0951-\u0954\u09BC\u09BE\u09CD\u09D7" \
- "\u0A3C\u0A4D\u0ABC\u0ACD\u0B3C\u0B3E\u0B4D\u0B56\u0B57" \
- "\u0BBE\u0BCD\u0BD7\u0C4D\u0C55\u0C56\u0CBC\u0CC2\u0CCD" \
- "\u0CD5\u0CD6\u0D3E\u0D4D\u0D57\u0DCA\u0DCF\u0DDF\u0E38-\u0E3A" \
- "\u0E48-\u0E4B\u0EB8\u0EB9\u0EC8-\u0ECB\u0F18\u0F19\u0F35\u0F37\u0F39\u0F71\u0F72" \
- "\u0F74\u0F7A-\u0F7D\u0F80\u0F82-\u0F84\u0F86\u0F87\u0FC6\u102E\u1037" \
- "\u1039\u103A\u108D\u135D-\u135F\u1714\u1734\u17D2\u17DD\u18A9" \
- "\u1939-\u193B\u1A17\u1A18\u1A60\u1A75-\u1A7C\u1A7F\u1AB0-\u1ABD\u1B34\u1B35\u1B44" \
- "\u1B6B-\u1B73\u1BAA\u1BAB\u1BE6\u1BF2\u1BF3\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8" \
- "\u1CED\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFB-\u1DFF\u20D0-\u20DC\u20E1\u20E5-\u20F0" \
- "\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F\uA674-\uA67D\uA69E\uA69F" \
- "\uA6F0\uA6F1\uA806\uA8C4\uA8E0-\uA8F1\uA92B-\uA92D\uA953\uA9B3\uA9C0" \
- "\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAF6\uABED\uFB1E" \
- "\uFE20-\uFE2F\u{101FD}\u{102E0}\u{10376}-\u{1037A}\u{10A0D}\u{10A0F}\u{10A38}-\u{10A3A}\u{10A3F}" \
- "\u{10AE5}\u{10AE6}\u{11046}\u{1107F}\u{110B9}\u{110BA}\u{11100}-\u{11102}\u{11127}\u{11133}\u{11134}\u{11173}" \
- "\u{111C0}\u{111CA}\u{11235}\u{11236}\u{112E9}\u{112EA}\u{1133C}\u{1133E}\u{1134D}\u{11357}" \
- "\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11442}\u{11446}\u{114B0}\u{114BA}\u{114BD}\u{114C2}\u{114C3}" \
- "\u{115AF}\u{115BF}\u{115C0}\u{1163F}\u{116B6}\u{116B7}\u{1172B}\u{11C3F}\u{16AF0}-\u{16AF4}\u{16B30}-\u{16B36}" \
- "\u{1BC9E}\u{1D165}-\u{1D169}\u{1D16D}-\u{1D172}\u{1D17B}-\u{1D182}\u{1D185}-\u{1D18B}\u{1D1AA}-\u{1D1AD}\u{1D242}-\u{1D244}\u{1E000}-\u{1E006}" \
- "\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}\u{1E024}\u{1E026}-\u{1E02A}\u{1E8D0}-\u{1E8D6}\u{1E944}-\u{1E94A}" \
+ "[\u0300-\u034E" \
+ "\u0350-\u036F" \
+ "\u0483-\u0487" \
+ "\u0591-\u05BD" \
+ "\u05BF" \
+ "\u05C1\u05C2" \
+ "\u05C4\u05C5" \
+ "\u05C7" \
+ "\u0610-\u061A" \
+ "\u064B-\u065F" \
+ "\u0670" \
+ "\u06D6-\u06DC" \
+ "\u06DF-\u06E4" \
+ "\u06E7\u06E8" \
+ "\u06EA-\u06ED" \
+ "\u0711" \
+ "\u0730-\u074A" \
+ "\u07EB-\u07F3" \
+ "\u07FD" \
+ "\u0816-\u0819" \
+ "\u081B-\u0823" \
+ "\u0825-\u0827" \
+ "\u0829-\u082D" \
+ "\u0859-\u085B" \
+ "\u08D3-\u08E1" \
+ "\u08E3-\u08FF" \
+ "\u093C" \
+ "\u094D" \
+ "\u0951-\u0954" \
+ "\u09BC" \
+ "\u09BE" \
+ "\u09CD" \
+ "\u09D7" \
+ "\u09FE" \
+ "\u0A3C" \
+ "\u0A4D" \
+ "\u0ABC" \
+ "\u0ACD" \
+ "\u0B3C" \
+ "\u0B3E" \
+ "\u0B4D" \
+ "\u0B56\u0B57" \
+ "\u0BBE" \
+ "\u0BCD" \
+ "\u0BD7" \
+ "\u0C4D" \
+ "\u0C55\u0C56" \
+ "\u0CBC" \
+ "\u0CC2" \
+ "\u0CCD" \
+ "\u0CD5\u0CD6" \
+ "\u0D3B\u0D3C" \
+ "\u0D3E" \
+ "\u0D4D" \
+ "\u0D57" \
+ "\u0DCA" \
+ "\u0DCF" \
+ "\u0DDF" \
+ "\u0E38-\u0E3A" \
+ "\u0E48-\u0E4B" \
+ "\u0EB8-\u0EBA" \
+ "\u0EC8-\u0ECB" \
+ "\u0F18\u0F19" \
+ "\u0F35" \
+ "\u0F37" \
+ "\u0F39" \
+ "\u0F71\u0F72" \
+ "\u0F74" \
+ "\u0F7A-\u0F7D" \
+ "\u0F80" \
+ "\u0F82-\u0F84" \
+ "\u0F86\u0F87" \
+ "\u0FC6" \
+ "\u102E" \
+ "\u1037" \
+ "\u1039\u103A" \
+ "\u108D" \
+ "\u135D-\u135F" \
+ "\u1714" \
+ "\u1734" \
+ "\u17D2" \
+ "\u17DD" \
+ "\u18A9" \
+ "\u1939-\u193B" \
+ "\u1A17\u1A18" \
+ "\u1A60" \
+ "\u1A75-\u1A7C" \
+ "\u1A7F" \
+ "\u1AB0-\u1ABD" \
+ "\u1B34\u1B35" \
+ "\u1B44" \
+ "\u1B6B-\u1B73" \
+ "\u1BAA\u1BAB" \
+ "\u1BE6" \
+ "\u1BF2\u1BF3" \
+ "\u1C37" \
+ "\u1CD0-\u1CD2" \
+ "\u1CD4-\u1CE0" \
+ "\u1CE2-\u1CE8" \
+ "\u1CED" \
+ "\u1CF4" \
+ "\u1CF8\u1CF9" \
+ "\u1DC0-\u1DF9" \
+ "\u1DFB-\u1DFF" \
+ "\u20D0-\u20DC" \
+ "\u20E1" \
+ "\u20E5-\u20F0" \
+ "\u2CEF-\u2CF1" \
+ "\u2D7F" \
+ "\u2DE0-\u2DFF" \
+ "\u302A-\u302F" \
+ "\u3099\u309A" \
+ "\uA66F" \
+ "\uA674-\uA67D" \
+ "\uA69E\uA69F" \
+ "\uA6F0\uA6F1" \
+ "\uA806" \
+ "\uA8C4" \
+ "\uA8E0-\uA8F1" \
+ "\uA92B-\uA92D" \
+ "\uA953" \
+ "\uA9B3" \
+ "\uA9C0" \
+ "\uAAB0" \
+ "\uAAB2-\uAAB4" \
+ "\uAAB7\uAAB8" \
+ "\uAABE\uAABF" \
+ "\uAAC1" \
+ "\uAAF6" \
+ "\uABED" \
+ "\uFB1E" \
+ "\uFE20-\uFE2F" \
+ "\u{101FD}" \
+ "\u{102E0}" \
+ "\u{10376}-\u{1037A}" \
+ "\u{10A0D}" \
+ "\u{10A0F}" \
+ "\u{10A38}-\u{10A3A}" \
+ "\u{10A3F}" \
+ "\u{10AE5}\u{10AE6}" \
+ "\u{10D24}-\u{10D27}" \
+ "\u{10F46}-\u{10F50}" \
+ "\u{11046}" \
+ "\u{1107F}" \
+ "\u{110B9}\u{110BA}" \
+ "\u{11100}-\u{11102}" \
+ "\u{11127}" \
+ "\u{11133}\u{11134}" \
+ "\u{11173}" \
+ "\u{111C0}" \
+ "\u{111CA}" \
+ "\u{11235}\u{11236}" \
+ "\u{112E9}\u{112EA}" \
+ "\u{1133B}\u{1133C}" \
+ "\u{1133E}" \
+ "\u{1134D}" \
+ "\u{11357}" \
+ "\u{11366}-\u{1136C}" \
+ "\u{11370}-\u{11374}" \
+ "\u{11442}" \
+ "\u{11446}" \
+ "\u{1145E}" \
+ "\u{114B0}" \
+ "\u{114BA}" \
+ "\u{114BD}" \
+ "\u{114C2}\u{114C3}" \
+ "\u{115AF}" \
+ "\u{115BF}\u{115C0}" \
+ "\u{1163F}" \
+ "\u{116B6}\u{116B7}" \
+ "\u{1172B}" \
+ "\u{11839}\u{1183A}" \
+ "\u{119E0}" \
+ "\u{11A34}" \
+ "\u{11A47}" \
+ "\u{11A99}" \
+ "\u{11C3F}" \
+ "\u{11D42}" \
+ "\u{11D44}\u{11D45}" \
+ "\u{11D97}" \
+ "\u{16AF0}-\u{16AF4}" \
+ "\u{16B30}-\u{16B36}" \
+ "\u{1BC9E}" \
+ "\u{1D165}-\u{1D169}" \
+ "\u{1D16D}-\u{1D172}" \
+ "\u{1D17B}-\u{1D182}" \
+ "\u{1D185}-\u{1D18B}" \
+ "\u{1D1AA}-\u{1D1AD}" \
+ "\u{1D242}-\u{1D244}" \
+ "\u{1E000}-\u{1E006}" \
+ "\u{1E008}-\u{1E018}" \
+ "\u{1E01B}-\u{1E021}" \
+ "\u{1E023}\u{1E024}" \
+ "\u{1E026}-\u{1E02A}" \
+ "\u{1E130}-\u{1E136}" \
+ "\u{1E2EC}-\u{1E2EF}" \
+ "\u{1E8D0}-\u{1E8D6}" \
+ "\u{1E944}-\u{1E94A}" \
"]"
ACCENTS = accents
REGEXP_D_STRING = "#{'' # composition starters and composition exclusions
}" \
- "[\u00C0-\u00C5\u00C7-\u00CF\u00D1-\u00D6\u00D9-\u00DD\u00E0-\u00E5\u00E7-\u00EF\u00F1-\u00F6\u00F9-\u00FD" \
- "\u00FF-\u010F\u0112-\u0125\u0128-\u0130\u0134-\u0137\u0139-\u013E\u0143-\u0148\u014C-\u0151\u0154-\u0165" \
- "\u0168-\u017E\u01A0\u01A1\u01AF\u01B0\u01CD-\u01DC\u01DE-\u01E3\u01E6-\u01F0\u01F4\u01F5\u01F8-\u021B" \
- "\u021E\u021F\u0226-\u0233\u0340\u0341\u0343\u0344\u0374\u037E\u0385-\u038A\u038C" \
- "\u038E-\u0390\u03AA-\u03B0\u03CA-\u03CE\u03D3\u03D4\u0400\u0401\u0403\u0407\u040C-\u040E" \
- "\u0419\u0439\u0450\u0451\u0453\u0457\u045C-\u045E\u0476\u0477\u04C1\u04C2" \
- "\u04D0-\u04D3\u04D6\u04D7\u04DA-\u04DF\u04E2-\u04E7\u04EA-\u04F5\u04F8\u04F9\u0622-\u0626\u06C0" \
- "\u06C2\u06D3\u0929\u0931\u0934\u0958-\u095F\u09CB\u09CC\u09DC\u09DD" \
- "\u09DF\u0A33\u0A36\u0A59-\u0A5B\u0A5E\u0B48\u0B4B\u0B4C\u0B5C\u0B5D" \
- "\u0B94\u0BCA-\u0BCC\u0C48\u0CC0\u0CC7\u0CC8\u0CCA\u0CCB\u0D4A-\u0D4C\u0DDA" \
- "\u0DDC-\u0DDE\u0F43\u0F4D\u0F52\u0F57\u0F5C\u0F69\u0F73" \
- "\u0F75\u0F76\u0F78\u0F81\u0F93\u0F9D\u0FA2\u0FA7\u0FAC" \
- "\u0FB9\u1026\u1B06\u1B08\u1B0A\u1B0C\u1B0E\u1B12" \
- "\u1B3B\u1B3D\u1B40\u1B41\u1B43\u1E00-\u1E99\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15" \
- "\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D" \
- "\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC1-\u1FC4\u1FC6-\u1FD3\u1FD6-\u1FDB\u1FDD-\u1FEF\u1FF2-\u1FF4" \
- "\u1FF6-\u1FFD\u2000\u2001\u2126\u212A\u212B\u219A\u219B\u21AE\u21CD-\u21CF\u2204" \
- "\u2209\u220C\u2224\u2226\u2241\u2244\u2247\u2249" \
- "\u2260\u2262\u226D-\u2271\u2274\u2275\u2278\u2279\u2280\u2281\u2284\u2285\u2288\u2289" \
- "\u22AC-\u22AF\u22E0-\u22E3\u22EA-\u22ED\u2329\u232A\u2ADC\u304C\u304E\u3050" \
- "\u3052\u3054\u3056\u3058\u305A\u305C\u305E\u3060" \
- "\u3062\u3065\u3067\u3069\u3070\u3071\u3073\u3074\u3076\u3077\u3079\u307A" \
- "\u307C\u307D\u3094\u309E\u30AC\u30AE\u30B0\u30B2\u30B4" \
- "\u30B6\u30B8\u30BA\u30BC\u30BE\u30C0\u30C2\u30C5" \
- "\u30C7\u30C9\u30D0\u30D1\u30D3\u30D4\u30D6\u30D7\u30D9\u30DA\u30DC\u30DD\u30F4" \
- "\u30F7-\u30FA\u30FE\uF900-\uFA0D\uFA10\uFA12\uFA15-\uFA1E\uFA20\uFA22" \
- "\uFA25\uFA26\uFA2A-\uFA6D\uFA70-\uFAD9\uFB1D\uFB1F\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E" \
- "\uFB40\uFB41\uFB43\uFB44\uFB46-\uFB4E\u{1109A}\u{1109C}\u{110AB}\u{1112E}\u{1112F}\u{1134B}\u{1134C}" \
- "\u{114BB}\u{114BC}\u{114BE}\u{115BA}\u{115BB}\u{1D15E}-\u{1D164}\u{1D1BB}-\u{1D1C0}\u{2F800}-\u{2FA1D}" \
+ "[\u00C0-\u00C5" \
+ "\u00C7-\u00CF" \
+ "\u00D1-\u00D6" \
+ "\u00D9-\u00DD" \
+ "\u00E0-\u00E5" \
+ "\u00E7-\u00EF" \
+ "\u00F1-\u00F6" \
+ "\u00F9-\u00FD" \
+ "\u00FF-\u010F" \
+ "\u0112-\u0125" \
+ "\u0128-\u0130" \
+ "\u0134-\u0137" \
+ "\u0139-\u013E" \
+ "\u0143-\u0148" \
+ "\u014C-\u0151" \
+ "\u0154-\u0165" \
+ "\u0168-\u017E" \
+ "\u01A0\u01A1" \
+ "\u01AF\u01B0" \
+ "\u01CD-\u01DC" \
+ "\u01DE-\u01E3" \
+ "\u01E6-\u01F0" \
+ "\u01F4\u01F5" \
+ "\u01F8-\u021B" \
+ "\u021E\u021F" \
+ "\u0226-\u0233" \
+ "\u0340\u0341" \
+ "\u0343\u0344" \
+ "\u0374" \
+ "\u037E" \
+ "\u0385-\u038A" \
+ "\u038C" \
+ "\u038E-\u0390" \
+ "\u03AA-\u03B0" \
+ "\u03CA-\u03CE" \
+ "\u03D3\u03D4" \
+ "\u0400\u0401" \
+ "\u0403" \
+ "\u0407" \
+ "\u040C-\u040E" \
+ "\u0419" \
+ "\u0439" \
+ "\u0450\u0451" \
+ "\u0453" \
+ "\u0457" \
+ "\u045C-\u045E" \
+ "\u0476\u0477" \
+ "\u04C1\u04C2" \
+ "\u04D0-\u04D3" \
+ "\u04D6\u04D7" \
+ "\u04DA-\u04DF" \
+ "\u04E2-\u04E7" \
+ "\u04EA-\u04F5" \
+ "\u04F8\u04F9" \
+ "\u0622-\u0626" \
+ "\u06C0" \
+ "\u06C2" \
+ "\u06D3" \
+ "\u0929" \
+ "\u0931" \
+ "\u0934" \
+ "\u0958-\u095F" \
+ "\u09CB\u09CC" \
+ "\u09DC\u09DD" \
+ "\u09DF" \
+ "\u0A33" \
+ "\u0A36" \
+ "\u0A59-\u0A5B" \
+ "\u0A5E" \
+ "\u0B48" \
+ "\u0B4B\u0B4C" \
+ "\u0B5C\u0B5D" \
+ "\u0B94" \
+ "\u0BCA-\u0BCC" \
+ "\u0C48" \
+ "\u0CC0" \
+ "\u0CC7\u0CC8" \
+ "\u0CCA\u0CCB" \
+ "\u0D4A-\u0D4C" \
+ "\u0DDA" \
+ "\u0DDC-\u0DDE" \
+ "\u0F43" \
+ "\u0F4D" \
+ "\u0F52" \
+ "\u0F57" \
+ "\u0F5C" \
+ "\u0F69" \
+ "\u0F73" \
+ "\u0F75\u0F76" \
+ "\u0F78" \
+ "\u0F81" \
+ "\u0F93" \
+ "\u0F9D" \
+ "\u0FA2" \
+ "\u0FA7" \
+ "\u0FAC" \
+ "\u0FB9" \
+ "\u1026" \
+ "\u1B06" \
+ "\u1B08" \
+ "\u1B0A" \
+ "\u1B0C" \
+ "\u1B0E" \
+ "\u1B12" \
+ "\u1B3B" \
+ "\u1B3D" \
+ "\u1B40\u1B41" \
+ "\u1B43" \
+ "\u1E00-\u1E99" \
+ "\u1E9B" \
+ "\u1EA0-\u1EF9" \
+ "\u1F00-\u1F15" \
+ "\u1F18-\u1F1D" \
+ "\u1F20-\u1F45" \
+ "\u1F48-\u1F4D" \
+ "\u1F50-\u1F57" \
+ "\u1F59" \
+ "\u1F5B" \
+ "\u1F5D" \
+ "\u1F5F-\u1F7D" \
+ "\u1F80-\u1FB4" \
+ "\u1FB6-\u1FBC" \
+ "\u1FBE" \
+ "\u1FC1-\u1FC4" \
+ "\u1FC6-\u1FD3" \
+ "\u1FD6-\u1FDB" \
+ "\u1FDD-\u1FEF" \
+ "\u1FF2-\u1FF4" \
+ "\u1FF6-\u1FFD" \
+ "\u2000\u2001" \
+ "\u2126" \
+ "\u212A\u212B" \
+ "\u219A\u219B" \
+ "\u21AE" \
+ "\u21CD-\u21CF" \
+ "\u2204" \
+ "\u2209" \
+ "\u220C" \
+ "\u2224" \
+ "\u2226" \
+ "\u2241" \
+ "\u2244" \
+ "\u2247" \
+ "\u2249" \
+ "\u2260" \
+ "\u2262" \
+ "\u226D-\u2271" \
+ "\u2274\u2275" \
+ "\u2278\u2279" \
+ "\u2280\u2281" \
+ "\u2284\u2285" \
+ "\u2288\u2289" \
+ "\u22AC-\u22AF" \
+ "\u22E0-\u22E3" \
+ "\u22EA-\u22ED" \
+ "\u2329\u232A" \
+ "\u2ADC" \
+ "\u304C" \
+ "\u304E" \
+ "\u3050" \
+ "\u3052" \
+ "\u3054" \
+ "\u3056" \
+ "\u3058" \
+ "\u305A" \
+ "\u305C" \
+ "\u305E" \
+ "\u3060" \
+ "\u3062" \
+ "\u3065" \
+ "\u3067" \
+ "\u3069" \
+ "\u3070\u3071" \
+ "\u3073\u3074" \
+ "\u3076\u3077" \
+ "\u3079\u307A" \
+ "\u307C\u307D" \
+ "\u3094" \
+ "\u309E" \
+ "\u30AC" \
+ "\u30AE" \
+ "\u30B0" \
+ "\u30B2" \
+ "\u30B4" \
+ "\u30B6" \
+ "\u30B8" \
+ "\u30BA" \
+ "\u30BC" \
+ "\u30BE" \
+ "\u30C0" \
+ "\u30C2" \
+ "\u30C5" \
+ "\u30C7" \
+ "\u30C9" \
+ "\u30D0\u30D1" \
+ "\u30D3\u30D4" \
+ "\u30D6\u30D7" \
+ "\u30D9\u30DA" \
+ "\u30DC\u30DD" \
+ "\u30F4" \
+ "\u30F7-\u30FA" \
+ "\u30FE" \
+ "\uF900-\uFA0D" \
+ "\uFA10" \
+ "\uFA12" \
+ "\uFA15-\uFA1E" \
+ "\uFA20" \
+ "\uFA22" \
+ "\uFA25\uFA26" \
+ "\uFA2A-\uFA6D" \
+ "\uFA70-\uFAD9" \
+ "\uFB1D" \
+ "\uFB1F" \
+ "\uFB2A-\uFB36" \
+ "\uFB38-\uFB3C" \
+ "\uFB3E" \
+ "\uFB40\uFB41" \
+ "\uFB43\uFB44" \
+ "\uFB46-\uFB4E" \
+ "\u{1109A}" \
+ "\u{1109C}" \
+ "\u{110AB}" \
+ "\u{1112E}\u{1112F}" \
+ "\u{1134B}\u{1134C}" \
+ "\u{114BB}\u{114BC}" \
+ "\u{114BE}" \
+ "\u{115BA}\u{115BB}" \
+ "\u{1D15E}-\u{1D164}" \
+ "\u{1D1BB}-\u{1D1C0}" \
+ "\u{2F800}-\u{2FA1D}" \
"]#{accents}*" \
"|#{'' # characters that can be the result of a composition, except composition starters
}" \
- "[<->A-PR-Za-pr-z\u00A8\u00C6\u00D8" \
- "\u00E6\u00F8\u017F\u01B7\u0292\u0391\u0395\u0397" \
- "\u0399\u039F\u03A1\u03A5\u03A9\u03B1\u03B5\u03B7" \
- "\u03B9\u03BF\u03C1\u03C5\u03C9\u03D2\u0406\u0410" \
- "\u0413\u0415-\u0418\u041A\u041E\u0423\u0427\u042B\u042D" \
- "\u0430\u0433\u0435-\u0438\u043A\u043E\u0443\u0447\u044B" \
- "\u044D\u0456\u0474\u0475\u04D8\u04D9\u04E8\u04E9\u0627\u0648\u064A" \
- "\u06C1\u06D2\u06D5\u0928\u0930\u0933\u09C7\u0B47" \
- "\u0B92\u0BC6\u0BC7\u0C46\u0CBF\u0CC6\u0D46\u0D47\u0DD9\u1025" \
- "\u1B05\u1B07\u1B09\u1B0B\u1B0D\u1B11\u1B3A\u1B3C" \
- "\u1B3E\u1B3F\u1B42\u1FBF\u1FFE\u2190\u2192\u2194\u21D0" \
- "\u21D2\u21D4\u2203\u2208\u220B\u2223\u2225\u223C" \
- "\u2243\u2245\u2248\u224D\u2261\u2264\u2265\u2272\u2273\u2276\u2277" \
- "\u227A-\u227D\u2282\u2283\u2286\u2287\u2291\u2292\u22A2\u22A8\u22A9\u22AB\u22B2-\u22B5" \
- "\u3046\u304B\u304D\u304F\u3051\u3053\u3055\u3057" \
- "\u3059\u305B\u305D\u305F\u3061\u3064\u3066\u3068" \
- "\u306F\u3072\u3075\u3078\u307B\u309D\u30A6\u30AB" \
- "\u30AD\u30AF\u30B1\u30B3\u30B5\u30B7\u30B9\u30BB" \
- "\u30BD\u30BF\u30C1\u30C4\u30C6\u30C8\u30CF\u30D2" \
- "\u30D5\u30D8\u30DB\u30EF-\u30F2\u30FD\u{11099}\u{1109B}\u{110A5}" \
- "\u{11131}\u{11132}\u{11347}\u{114B9}\u{115B8}\u{115B9}" \
+ "[<->" \
+ "A-P" \
+ "R-Z" \
+ "a-p" \
+ "r-z" \
+ "\u00A8" \
+ "\u00C6" \
+ "\u00D8" \
+ "\u00E6" \
+ "\u00F8" \
+ "\u017F" \
+ "\u01B7" \
+ "\u0292" \
+ "\u0391" \
+ "\u0395" \
+ "\u0397" \
+ "\u0399" \
+ "\u039F" \
+ "\u03A1" \
+ "\u03A5" \
+ "\u03A9" \
+ "\u03B1" \
+ "\u03B5" \
+ "\u03B7" \
+ "\u03B9" \
+ "\u03BF" \
+ "\u03C1" \
+ "\u03C5" \
+ "\u03C9" \
+ "\u03D2" \
+ "\u0406" \
+ "\u0410" \
+ "\u0413" \
+ "\u0415-\u0418" \
+ "\u041A" \
+ "\u041E" \
+ "\u0423" \
+ "\u0427" \
+ "\u042B" \
+ "\u042D" \
+ "\u0430" \
+ "\u0433" \
+ "\u0435-\u0438" \
+ "\u043A" \
+ "\u043E" \
+ "\u0443" \
+ "\u0447" \
+ "\u044B" \
+ "\u044D" \
+ "\u0456" \
+ "\u0474\u0475" \
+ "\u04D8\u04D9" \
+ "\u04E8\u04E9" \
+ "\u0627" \
+ "\u0648" \
+ "\u064A" \
+ "\u06C1" \
+ "\u06D2" \
+ "\u06D5" \
+ "\u0928" \
+ "\u0930" \
+ "\u0933" \
+ "\u09C7" \
+ "\u0B47" \
+ "\u0B92" \
+ "\u0BC6\u0BC7" \
+ "\u0C46" \
+ "\u0CBF" \
+ "\u0CC6" \
+ "\u0D46\u0D47" \
+ "\u0DD9" \
+ "\u1025" \
+ "\u1B05" \
+ "\u1B07" \
+ "\u1B09" \
+ "\u1B0B" \
+ "\u1B0D" \
+ "\u1B11" \
+ "\u1B3A" \
+ "\u1B3C" \
+ "\u1B3E\u1B3F" \
+ "\u1B42" \
+ "\u1FBF" \
+ "\u1FFE" \
+ "\u2190" \
+ "\u2192" \
+ "\u2194" \
+ "\u21D0" \
+ "\u21D2" \
+ "\u21D4" \
+ "\u2203" \
+ "\u2208" \
+ "\u220B" \
+ "\u2223" \
+ "\u2225" \
+ "\u223C" \
+ "\u2243" \
+ "\u2245" \
+ "\u2248" \
+ "\u224D" \
+ "\u2261" \
+ "\u2264\u2265" \
+ "\u2272\u2273" \
+ "\u2276\u2277" \
+ "\u227A-\u227D" \
+ "\u2282\u2283" \
+ "\u2286\u2287" \
+ "\u2291\u2292" \
+ "\u22A2" \
+ "\u22A8\u22A9" \
+ "\u22AB" \
+ "\u22B2-\u22B5" \
+ "\u3046" \
+ "\u304B" \
+ "\u304D" \
+ "\u304F" \
+ "\u3051" \
+ "\u3053" \
+ "\u3055" \
+ "\u3057" \
+ "\u3059" \
+ "\u305B" \
+ "\u305D" \
+ "\u305F" \
+ "\u3061" \
+ "\u3064" \
+ "\u3066" \
+ "\u3068" \
+ "\u306F" \
+ "\u3072" \
+ "\u3075" \
+ "\u3078" \
+ "\u307B" \
+ "\u309D" \
+ "\u30A6" \
+ "\u30AB" \
+ "\u30AD" \
+ "\u30AF" \
+ "\u30B1" \
+ "\u30B3" \
+ "\u30B5" \
+ "\u30B7" \
+ "\u30B9" \
+ "\u30BB" \
+ "\u30BD" \
+ "\u30BF" \
+ "\u30C1" \
+ "\u30C4" \
+ "\u30C6" \
+ "\u30C8" \
+ "\u30CF" \
+ "\u30D2" \
+ "\u30D5" \
+ "\u30D8" \
+ "\u30DB" \
+ "\u30EF-\u30F2" \
+ "\u30FD" \
+ "\u{11099}" \
+ "\u{1109B}" \
+ "\u{110A5}" \
+ "\u{11131}\u{11132}" \
+ "\u{11347}" \
+ "\u{114B9}" \
+ "\u{115B8}\u{115B9}" \
"]?#{accents}+" \
"|#{'' # precomposed Hangul syllables
}" \
"[\u{AC00}-\u{D7A4}]"
REGEXP_C_STRING = "#{'' # composition exclusions
}" \
- "[\u0340\u0341\u0343\u0344\u0374\u037E\u0387\u0958-\u095F\u09DC\u09DD\u09DF" \
- "\u0A33\u0A36\u0A59-\u0A5B\u0A5E\u0B5C\u0B5D\u0F43\u0F4D\u0F52" \
- "\u0F57\u0F5C\u0F69\u0F73\u0F75\u0F76\u0F78\u0F81\u0F93" \
- "\u0F9D\u0FA2\u0FA7\u0FAC\u0FB9\u1F71\u1F73\u1F75" \
- "\u1F77\u1F79\u1F7B\u1F7D\u1FBB\u1FBE\u1FC9\u1FCB" \
- "\u1FD3\u1FDB\u1FE3\u1FEB\u1FEE\u1FEF\u1FF9\u1FFB\u1FFD" \
- "\u2000\u2001\u2126\u212A\u212B\u2329\u232A\u2ADC\uF900-\uFA0D\uFA10\uFA12" \
- "\uFA15-\uFA1E\uFA20\uFA22\uFA25\uFA26\uFA2A-\uFA6D\uFA70-\uFAD9\uFB1D\uFB1F" \
- "\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFB4E\u{1D15E}-\u{1D164}\u{1D1BB}-\u{1D1C0}" \
+ "[\u0340\u0341" \
+ "\u0343\u0344" \
+ "\u0374" \
+ "\u037E" \
+ "\u0387" \
+ "\u0958-\u095F" \
+ "\u09DC\u09DD" \
+ "\u09DF" \
+ "\u0A33" \
+ "\u0A36" \
+ "\u0A59-\u0A5B" \
+ "\u0A5E" \
+ "\u0B5C\u0B5D" \
+ "\u0F43" \
+ "\u0F4D" \
+ "\u0F52" \
+ "\u0F57" \
+ "\u0F5C" \
+ "\u0F69" \
+ "\u0F73" \
+ "\u0F75\u0F76" \
+ "\u0F78" \
+ "\u0F81" \
+ "\u0F93" \
+ "\u0F9D" \
+ "\u0FA2" \
+ "\u0FA7" \
+ "\u0FAC" \
+ "\u0FB9" \
+ "\u1F71" \
+ "\u1F73" \
+ "\u1F75" \
+ "\u1F77" \
+ "\u1F79" \
+ "\u1F7B" \
+ "\u1F7D" \
+ "\u1FBB" \
+ "\u1FBE" \
+ "\u1FC9" \
+ "\u1FCB" \
+ "\u1FD3" \
+ "\u1FDB" \
+ "\u1FE3" \
+ "\u1FEB" \
+ "\u1FEE\u1FEF" \
+ "\u1FF9" \
+ "\u1FFB" \
+ "\u1FFD" \
+ "\u2000\u2001" \
+ "\u2126" \
+ "\u212A\u212B" \
+ "\u2329\u232A" \
+ "\u2ADC" \
+ "\uF900-\uFA0D" \
+ "\uFA10" \
+ "\uFA12" \
+ "\uFA15-\uFA1E" \
+ "\uFA20" \
+ "\uFA22" \
+ "\uFA25\uFA26" \
+ "\uFA2A-\uFA6D" \
+ "\uFA70-\uFAD9" \
+ "\uFB1D" \
+ "\uFB1F" \
+ "\uFB2A-\uFB36" \
+ "\uFB38-\uFB3C" \
+ "\uFB3E" \
+ "\uFB40\uFB41" \
+ "\uFB43\uFB44" \
+ "\uFB46-\uFB4E" \
+ "\u{1D15E}-\u{1D164}" \
+ "\u{1D1BB}-\u{1D1C0}" \
"\u{2F800}-\u{2FA1D}" \
"]#{accents}*" \
"|#{'' # composition starters and characters that can be the result of a composition
}" \
- "[<->A-PR-Za-pr-z\u00A8\u00C0-\u00CF\u00D1-\u00D6" \
- "\u00D8-\u00DD\u00E0-\u00EF\u00F1-\u00F6\u00F8-\u00FD\u00FF-\u010F\u0112-\u0125\u0128-\u0130\u0134-\u0137" \
- "\u0139-\u013E\u0143-\u0148\u014C-\u0151\u0154-\u0165\u0168-\u017F\u01A0\u01A1\u01AF\u01B0\u01B7" \
- "\u01CD-\u01DC\u01DE-\u01E3\u01E6-\u01F0\u01F4\u01F5\u01F8-\u021B\u021E\u021F\u0226-\u0233\u0292" \
- "\u0385\u0386\u0388-\u038A\u038C\u038E-\u0391\u0395\u0397\u0399\u039F" \
- "\u03A1\u03A5\u03A9-\u03B1\u03B5\u03B7\u03B9\u03BF\u03C1" \
- "\u03C5\u03C9-\u03CE\u03D2-\u03D4\u0400\u0401\u0403\u0406\u0407\u040C-\u040E\u0410" \
- "\u0413\u0415-\u041A\u041E\u0423\u0427\u042B\u042D\u0430" \
- "\u0433\u0435-\u043A\u043E\u0443\u0447\u044B\u044D\u0450\u0451" \
- "\u0453\u0456\u0457\u045C-\u045E\u0474-\u0477\u04C1\u04C2\u04D0-\u04D3\u04D6-\u04DF\u04E2-\u04F5" \
- "\u04F8\u04F9\u0622-\u0627\u0648\u064A\u06C0-\u06C2\u06D2\u06D3\u06D5\u0928\u0929" \
- "\u0930\u0931\u0933\u0934\u09C7\u09CB\u09CC\u0B47\u0B48\u0B4B\u0B4C\u0B92\u0B94" \
- "\u0BC6\u0BC7\u0BCA-\u0BCC\u0C46\u0C48\u0CBF\u0CC0\u0CC6-\u0CC8\u0CCA\u0CCB\u0D46\u0D47" \
- "\u0D4A-\u0D4C\u0DD9\u0DDA\u0DDC-\u0DDE\u1025\u1026\u1B05-\u1B0E\u1B11\u1B12\u1B3A-\u1B43\u1E00-\u1E99" \
- "\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59" \
- "\u1F5B\u1F5D\u1F5F-\u1F70\u1F72\u1F74\u1F76\u1F78\u1F7A" \
- "\u1F7C\u1F80-\u1FB4\u1FB6-\u1FBA\u1FBC\u1FBF\u1FC1-\u1FC4\u1FC6-\u1FC8\u1FCA" \
- "\u1FCC-\u1FD2\u1FD6-\u1FDA\u1FDD-\u1FE2\u1FE4-\u1FEA\u1FEC\u1FED\u1FF2-\u1FF4\u1FF6-\u1FF8\u1FFA" \
- "\u1FFC\u1FFE\u2190\u2192\u2194\u219A\u219B\u21AE\u21CD-\u21D0" \
- "\u21D2\u21D4\u2203\u2204\u2208\u2209\u220B\u220C\u2223-\u2226\u223C\u2241" \
- "\u2243-\u2245\u2247-\u2249\u224D\u2260-\u2262\u2264\u2265\u226D-\u227D\u2280-\u2289\u2291\u2292" \
- "\u22A2\u22A8\u22A9\u22AB-\u22AF\u22B2-\u22B5\u22E0-\u22E3\u22EA-\u22ED\u3046\u304B-\u3062" \
- "\u3064-\u3069\u306F-\u307D\u3094\u309D\u309E\u30A6\u30AB-\u30C2\u30C4-\u30C9\u30CF-\u30DD" \
- "\u30EF-\u30F2\u30F4\u30F7-\u30FA\u30FD\u30FE\u{11099}-\u{1109C}\u{110A5}\u{110AB}\u{1112E}\u{1112F}" \
- "\u{11131}\u{11132}\u{11347}\u{1134B}\u{1134C}\u{114B9}\u{114BB}\u{114BC}\u{114BE}\u{115B8}-\u{115BB}" \
+ "[<->" \
+ "A-P" \
+ "R-Z" \
+ "a-p" \
+ "r-z" \
+ "\u00A8" \
+ "\u00C0-\u00CF" \
+ "\u00D1-\u00D6" \
+ "\u00D8-\u00DD" \
+ "\u00E0-\u00EF" \
+ "\u00F1-\u00F6" \
+ "\u00F8-\u00FD" \
+ "\u00FF-\u010F" \
+ "\u0112-\u0125" \
+ "\u0128-\u0130" \
+ "\u0134-\u0137" \
+ "\u0139-\u013E" \
+ "\u0143-\u0148" \
+ "\u014C-\u0151" \
+ "\u0154-\u0165" \
+ "\u0168-\u017F" \
+ "\u01A0\u01A1" \
+ "\u01AF\u01B0" \
+ "\u01B7" \
+ "\u01CD-\u01DC" \
+ "\u01DE-\u01E3" \
+ "\u01E6-\u01F0" \
+ "\u01F4\u01F5" \
+ "\u01F8-\u021B" \
+ "\u021E\u021F" \
+ "\u0226-\u0233" \
+ "\u0292" \
+ "\u0385\u0386" \
+ "\u0388-\u038A" \
+ "\u038C" \
+ "\u038E-\u0391" \
+ "\u0395" \
+ "\u0397" \
+ "\u0399" \
+ "\u039F" \
+ "\u03A1" \
+ "\u03A5" \
+ "\u03A9-\u03B1" \
+ "\u03B5" \
+ "\u03B7" \
+ "\u03B9" \
+ "\u03BF" \
+ "\u03C1" \
+ "\u03C5" \
+ "\u03C9-\u03CE" \
+ "\u03D2-\u03D4" \
+ "\u0400\u0401" \
+ "\u0403" \
+ "\u0406\u0407" \
+ "\u040C-\u040E" \
+ "\u0410" \
+ "\u0413" \
+ "\u0415-\u041A" \
+ "\u041E" \
+ "\u0423" \
+ "\u0427" \
+ "\u042B" \
+ "\u042D" \
+ "\u0430" \
+ "\u0433" \
+ "\u0435-\u043A" \
+ "\u043E" \
+ "\u0443" \
+ "\u0447" \
+ "\u044B" \
+ "\u044D" \
+ "\u0450\u0451" \
+ "\u0453" \
+ "\u0456\u0457" \
+ "\u045C-\u045E" \
+ "\u0474-\u0477" \
+ "\u04C1\u04C2" \
+ "\u04D0-\u04D3" \
+ "\u04D6-\u04DF" \
+ "\u04E2-\u04F5" \
+ "\u04F8\u04F9" \
+ "\u0622-\u0627" \
+ "\u0648" \
+ "\u064A" \
+ "\u06C0-\u06C2" \
+ "\u06D2\u06D3" \
+ "\u06D5" \
+ "\u0928\u0929" \
+ "\u0930\u0931" \
+ "\u0933\u0934" \
+ "\u09C7" \
+ "\u09CB\u09CC" \
+ "\u0B47\u0B48" \
+ "\u0B4B\u0B4C" \
+ "\u0B92" \
+ "\u0B94" \
+ "\u0BC6\u0BC7" \
+ "\u0BCA-\u0BCC" \
+ "\u0C46" \
+ "\u0C48" \
+ "\u0CBF\u0CC0" \
+ "\u0CC6-\u0CC8" \
+ "\u0CCA\u0CCB" \
+ "\u0D46\u0D47" \
+ "\u0D4A-\u0D4C" \
+ "\u0DD9\u0DDA" \
+ "\u0DDC-\u0DDE" \
+ "\u1025\u1026" \
+ "\u1B05-\u1B0E" \
+ "\u1B11\u1B12" \
+ "\u1B3A-\u1B43" \
+ "\u1E00-\u1E99" \
+ "\u1E9B" \
+ "\u1EA0-\u1EF9" \
+ "\u1F00-\u1F15" \
+ "\u1F18-\u1F1D" \
+ "\u1F20-\u1F45" \
+ "\u1F48-\u1F4D" \
+ "\u1F50-\u1F57" \
+ "\u1F59" \
+ "\u1F5B" \
+ "\u1F5D" \
+ "\u1F5F-\u1F70" \
+ "\u1F72" \
+ "\u1F74" \
+ "\u1F76" \
+ "\u1F78" \
+ "\u1F7A" \
+ "\u1F7C" \
+ "\u1F80-\u1FB4" \
+ "\u1FB6-\u1FBA" \
+ "\u1FBC" \
+ "\u1FBF" \
+ "\u1FC1-\u1FC4" \
+ "\u1FC6-\u1FC8" \
+ "\u1FCA" \
+ "\u1FCC-\u1FD2" \
+ "\u1FD6-\u1FDA" \
+ "\u1FDD-\u1FE2" \
+ "\u1FE4-\u1FEA" \
+ "\u1FEC\u1FED" \
+ "\u1FF2-\u1FF4" \
+ "\u1FF6-\u1FF8" \
+ "\u1FFA" \
+ "\u1FFC" \
+ "\u1FFE" \
+ "\u2190" \
+ "\u2192" \
+ "\u2194" \
+ "\u219A\u219B" \
+ "\u21AE" \
+ "\u21CD-\u21D0" \
+ "\u21D2" \
+ "\u21D4" \
+ "\u2203\u2204" \
+ "\u2208\u2209" \
+ "\u220B\u220C" \
+ "\u2223-\u2226" \
+ "\u223C" \
+ "\u2241" \
+ "\u2243-\u2245" \
+ "\u2247-\u2249" \
+ "\u224D" \
+ "\u2260-\u2262" \
+ "\u2264\u2265" \
+ "\u226D-\u227D" \
+ "\u2280-\u2289" \
+ "\u2291\u2292" \
+ "\u22A2" \
+ "\u22A8\u22A9" \
+ "\u22AB-\u22AF" \
+ "\u22B2-\u22B5" \
+ "\u22E0-\u22E3" \
+ "\u22EA-\u22ED" \
+ "\u3046" \
+ "\u304B-\u3062" \
+ "\u3064-\u3069" \
+ "\u306F-\u307D" \
+ "\u3094" \
+ "\u309D\u309E" \
+ "\u30A6" \
+ "\u30AB-\u30C2" \
+ "\u30C4-\u30C9" \
+ "\u30CF-\u30DD" \
+ "\u30EF-\u30F2" \
+ "\u30F4" \
+ "\u30F7-\u30FA" \
+ "\u30FD\u30FE" \
+ "\u{11099}-\u{1109C}" \
+ "\u{110A5}" \
+ "\u{110AB}" \
+ "\u{1112E}\u{1112F}" \
+ "\u{11131}\u{11132}" \
+ "\u{11347}" \
+ "\u{1134B}\u{1134C}" \
+ "\u{114B9}" \
+ "\u{114BB}\u{114BC}" \
+ "\u{114BE}" \
+ "\u{115B8}-\u{115BB}" \
"]?#{accents}+" \
"|#{'' # Hangul syllables with separate trailer
}" \
- "[\uAC00\uAC1C\uAC38\uAC54\uAC70\uAC8C\uACA8\uACC4" \
- "\uACE0\uACFC\uAD18\uAD34\uAD50\uAD6C\uAD88\uADA4" \
- "\uADC0\uADDC\uADF8\uAE14\uAE30\uAE4C\uAE68\uAE84" \
- "\uAEA0\uAEBC\uAED8\uAEF4\uAF10\uAF2C\uAF48\uAF64" \
- "\uAF80\uAF9C\uAFB8\uAFD4\uAFF0\uB00C\uB028\uB044" \
- "\uB060\uB07C\uB098\uB0B4\uB0D0\uB0EC\uB108\uB124" \
- "\uB140\uB15C\uB178\uB194\uB1B0\uB1CC\uB1E8\uB204" \
- "\uB220\uB23C\uB258\uB274\uB290\uB2AC\uB2C8\uB2E4" \
- "\uB300\uB31C\uB338\uB354\uB370\uB38C\uB3A8\uB3C4" \
- "\uB3E0\uB3FC\uB418\uB434\uB450\uB46C\uB488\uB4A4" \
- "\uB4C0\uB4DC\uB4F8\uB514\uB530\uB54C\uB568\uB584" \
- "\uB5A0\uB5BC\uB5D8\uB5F4\uB610\uB62C\uB648\uB664" \
- "\uB680\uB69C\uB6B8\uB6D4\uB6F0\uB70C\uB728\uB744" \
- "\uB760\uB77C\uB798\uB7B4\uB7D0\uB7EC\uB808\uB824" \
- "\uB840\uB85C\uB878\uB894\uB8B0\uB8CC\uB8E8\uB904" \
- "\uB920\uB93C\uB958\uB974\uB990\uB9AC\uB9C8\uB9E4" \
- "\uBA00\uBA1C\uBA38\uBA54\uBA70\uBA8C\uBAA8\uBAC4" \
- "\uBAE0\uBAFC\uBB18\uBB34\uBB50\uBB6C\uBB88\uBBA4" \
- "\uBBC0\uBBDC\uBBF8\uBC14\uBC30\uBC4C\uBC68\uBC84" \
- "\uBCA0\uBCBC\uBCD8\uBCF4\uBD10\uBD2C\uBD48\uBD64" \
- "\uBD80\uBD9C\uBDB8\uBDD4\uBDF0\uBE0C\uBE28\uBE44" \
- "\uBE60\uBE7C\uBE98\uBEB4\uBED0\uBEEC\uBF08\uBF24" \
- "\uBF40\uBF5C\uBF78\uBF94\uBFB0\uBFCC\uBFE8\uC004" \
- "\uC020\uC03C\uC058\uC074\uC090\uC0AC\uC0C8\uC0E4" \
- "\uC100\uC11C\uC138\uC154\uC170\uC18C\uC1A8\uC1C4" \
- "\uC1E0\uC1FC\uC218\uC234\uC250\uC26C\uC288\uC2A4" \
- "\uC2C0\uC2DC\uC2F8\uC314\uC330\uC34C\uC368\uC384" \
- "\uC3A0\uC3BC\uC3D8\uC3F4\uC410\uC42C\uC448\uC464" \
- "\uC480\uC49C\uC4B8\uC4D4\uC4F0\uC50C\uC528\uC544" \
- "\uC560\uC57C\uC598\uC5B4\uC5D0\uC5EC\uC608\uC624" \
- "\uC640\uC65C\uC678\uC694\uC6B0\uC6CC\uC6E8\uC704" \
- "\uC720\uC73C\uC758\uC774\uC790\uC7AC\uC7C8\uC7E4" \
- "\uC800\uC81C\uC838\uC854\uC870\uC88C\uC8A8\uC8C4" \
- "\uC8E0\uC8FC\uC918\uC934\uC950\uC96C\uC988\uC9A4" \
- "\uC9C0\uC9DC\uC9F8\uCA14\uCA30\uCA4C\uCA68\uCA84" \
- "\uCAA0\uCABC\uCAD8\uCAF4\uCB10\uCB2C\uCB48\uCB64" \
- "\uCB80\uCB9C\uCBB8\uCBD4\uCBF0\uCC0C\uCC28\uCC44" \
- "\uCC60\uCC7C\uCC98\uCCB4\uCCD0\uCCEC\uCD08\uCD24" \
- "\uCD40\uCD5C\uCD78\uCD94\uCDB0\uCDCC\uCDE8\uCE04" \
- "\uCE20\uCE3C\uCE58\uCE74\uCE90\uCEAC\uCEC8\uCEE4" \
- "\uCF00\uCF1C\uCF38\uCF54\uCF70\uCF8C\uCFA8\uCFC4" \
- "\uCFE0\uCFFC\uD018\uD034\uD050\uD06C\uD088\uD0A4" \
- "\uD0C0\uD0DC\uD0F8\uD114\uD130\uD14C\uD168\uD184" \
- "\uD1A0\uD1BC\uD1D8\uD1F4\uD210\uD22C\uD248\uD264" \
- "\uD280\uD29C\uD2B8\uD2D4\uD2F0\uD30C\uD328\uD344" \
- "\uD360\uD37C\uD398\uD3B4\uD3D0\uD3EC\uD408\uD424" \
- "\uD440\uD45C\uD478\uD494\uD4B0\uD4CC\uD4E8\uD504" \
- "\uD520\uD53C\uD558\uD574\uD590\uD5AC\uD5C8\uD5E4" \
- "\uD600\uD61C\uD638\uD654\uD670\uD68C\uD6A8\uD6C4" \
- "\uD6E0\uD6FC\uD718\uD734\uD750\uD76C\uD788" \
+ "[\uAC00" \
+ "\uAC1C" \
+ "\uAC38" \
+ "\uAC54" \
+ "\uAC70" \
+ "\uAC8C" \
+ "\uACA8" \
+ "\uACC4" \
+ "\uACE0" \
+ "\uACFC" \
+ "\uAD18" \
+ "\uAD34" \
+ "\uAD50" \
+ "\uAD6C" \
+ "\uAD88" \
+ "\uADA4" \
+ "\uADC0" \
+ "\uADDC" \
+ "\uADF8" \
+ "\uAE14" \
+ "\uAE30" \
+ "\uAE4C" \
+ "\uAE68" \
+ "\uAE84" \
+ "\uAEA0" \
+ "\uAEBC" \
+ "\uAED8" \
+ "\uAEF4" \
+ "\uAF10" \
+ "\uAF2C" \
+ "\uAF48" \
+ "\uAF64" \
+ "\uAF80" \
+ "\uAF9C" \
+ "\uAFB8" \
+ "\uAFD4" \
+ "\uAFF0" \
+ "\uB00C" \
+ "\uB028" \
+ "\uB044" \
+ "\uB060" \
+ "\uB07C" \
+ "\uB098" \
+ "\uB0B4" \
+ "\uB0D0" \
+ "\uB0EC" \
+ "\uB108" \
+ "\uB124" \
+ "\uB140" \
+ "\uB15C" \
+ "\uB178" \
+ "\uB194" \
+ "\uB1B0" \
+ "\uB1CC" \
+ "\uB1E8" \
+ "\uB204" \
+ "\uB220" \
+ "\uB23C" \
+ "\uB258" \
+ "\uB274" \
+ "\uB290" \
+ "\uB2AC" \
+ "\uB2C8" \
+ "\uB2E4" \
+ "\uB300" \
+ "\uB31C" \
+ "\uB338" \
+ "\uB354" \
+ "\uB370" \
+ "\uB38C" \
+ "\uB3A8" \
+ "\uB3C4" \
+ "\uB3E0" \
+ "\uB3FC" \
+ "\uB418" \
+ "\uB434" \
+ "\uB450" \
+ "\uB46C" \
+ "\uB488" \
+ "\uB4A4" \
+ "\uB4C0" \
+ "\uB4DC" \
+ "\uB4F8" \
+ "\uB514" \
+ "\uB530" \
+ "\uB54C" \
+ "\uB568" \
+ "\uB584" \
+ "\uB5A0" \
+ "\uB5BC" \
+ "\uB5D8" \
+ "\uB5F4" \
+ "\uB610" \
+ "\uB62C" \
+ "\uB648" \
+ "\uB664" \
+ "\uB680" \
+ "\uB69C" \
+ "\uB6B8" \
+ "\uB6D4" \
+ "\uB6F0" \
+ "\uB70C" \
+ "\uB728" \
+ "\uB744" \
+ "\uB760" \
+ "\uB77C" \
+ "\uB798" \
+ "\uB7B4" \
+ "\uB7D0" \
+ "\uB7EC" \
+ "\uB808" \
+ "\uB824" \
+ "\uB840" \
+ "\uB85C" \
+ "\uB878" \
+ "\uB894" \
+ "\uB8B0" \
+ "\uB8CC" \
+ "\uB8E8" \
+ "\uB904" \
+ "\uB920" \
+ "\uB93C" \
+ "\uB958" \
+ "\uB974" \
+ "\uB990" \
+ "\uB9AC" \
+ "\uB9C8" \
+ "\uB9E4" \
+ "\uBA00" \
+ "\uBA1C" \
+ "\uBA38" \
+ "\uBA54" \
+ "\uBA70" \
+ "\uBA8C" \
+ "\uBAA8" \
+ "\uBAC4" \
+ "\uBAE0" \
+ "\uBAFC" \
+ "\uBB18" \
+ "\uBB34" \
+ "\uBB50" \
+ "\uBB6C" \
+ "\uBB88" \
+ "\uBBA4" \
+ "\uBBC0" \
+ "\uBBDC" \
+ "\uBBF8" \
+ "\uBC14" \
+ "\uBC30" \
+ "\uBC4C" \
+ "\uBC68" \
+ "\uBC84" \
+ "\uBCA0" \
+ "\uBCBC" \
+ "\uBCD8" \
+ "\uBCF4" \
+ "\uBD10" \
+ "\uBD2C" \
+ "\uBD48" \
+ "\uBD64" \
+ "\uBD80" \
+ "\uBD9C" \
+ "\uBDB8" \
+ "\uBDD4" \
+ "\uBDF0" \
+ "\uBE0C" \
+ "\uBE28" \
+ "\uBE44" \
+ "\uBE60" \
+ "\uBE7C" \
+ "\uBE98" \
+ "\uBEB4" \
+ "\uBED0" \
+ "\uBEEC" \
+ "\uBF08" \
+ "\uBF24" \
+ "\uBF40" \
+ "\uBF5C" \
+ "\uBF78" \
+ "\uBF94" \
+ "\uBFB0" \
+ "\uBFCC" \
+ "\uBFE8" \
+ "\uC004" \
+ "\uC020" \
+ "\uC03C" \
+ "\uC058" \
+ "\uC074" \
+ "\uC090" \
+ "\uC0AC" \
+ "\uC0C8" \
+ "\uC0E4" \
+ "\uC100" \
+ "\uC11C" \
+ "\uC138" \
+ "\uC154" \
+ "\uC170" \
+ "\uC18C" \
+ "\uC1A8" \
+ "\uC1C4" \
+ "\uC1E0" \
+ "\uC1FC" \
+ "\uC218" \
+ "\uC234" \
+ "\uC250" \
+ "\uC26C" \
+ "\uC288" \
+ "\uC2A4" \
+ "\uC2C0" \
+ "\uC2DC" \
+ "\uC2F8" \
+ "\uC314" \
+ "\uC330" \
+ "\uC34C" \
+ "\uC368" \
+ "\uC384" \
+ "\uC3A0" \
+ "\uC3BC" \
+ "\uC3D8" \
+ "\uC3F4" \
+ "\uC410" \
+ "\uC42C" \
+ "\uC448" \
+ "\uC464" \
+ "\uC480" \
+ "\uC49C" \
+ "\uC4B8" \
+ "\uC4D4" \
+ "\uC4F0" \
+ "\uC50C" \
+ "\uC528" \
+ "\uC544" \
+ "\uC560" \
+ "\uC57C" \
+ "\uC598" \
+ "\uC5B4" \
+ "\uC5D0" \
+ "\uC5EC" \
+ "\uC608" \
+ "\uC624" \
+ "\uC640" \
+ "\uC65C" \
+ "\uC678" \
+ "\uC694" \
+ "\uC6B0" \
+ "\uC6CC" \
+ "\uC6E8" \
+ "\uC704" \
+ "\uC720" \
+ "\uC73C" \
+ "\uC758" \
+ "\uC774" \
+ "\uC790" \
+ "\uC7AC" \
+ "\uC7C8" \
+ "\uC7E4" \
+ "\uC800" \
+ "\uC81C" \
+ "\uC838" \
+ "\uC854" \
+ "\uC870" \
+ "\uC88C" \
+ "\uC8A8" \
+ "\uC8C4" \
+ "\uC8E0" \
+ "\uC8FC" \
+ "\uC918" \
+ "\uC934" \
+ "\uC950" \
+ "\uC96C" \
+ "\uC988" \
+ "\uC9A4" \
+ "\uC9C0" \
+ "\uC9DC" \
+ "\uC9F8" \
+ "\uCA14" \
+ "\uCA30" \
+ "\uCA4C" \
+ "\uCA68" \
+ "\uCA84" \
+ "\uCAA0" \
+ "\uCABC" \
+ "\uCAD8" \
+ "\uCAF4" \
+ "\uCB10" \
+ "\uCB2C" \
+ "\uCB48" \
+ "\uCB64" \
+ "\uCB80" \
+ "\uCB9C" \
+ "\uCBB8" \
+ "\uCBD4" \
+ "\uCBF0" \
+ "\uCC0C" \
+ "\uCC28" \
+ "\uCC44" \
+ "\uCC60" \
+ "\uCC7C" \
+ "\uCC98" \
+ "\uCCB4" \
+ "\uCCD0" \
+ "\uCCEC" \
+ "\uCD08" \
+ "\uCD24" \
+ "\uCD40" \
+ "\uCD5C" \
+ "\uCD78" \
+ "\uCD94" \
+ "\uCDB0" \
+ "\uCDCC" \
+ "\uCDE8" \
+ "\uCE04" \
+ "\uCE20" \
+ "\uCE3C" \
+ "\uCE58" \
+ "\uCE74" \
+ "\uCE90" \
+ "\uCEAC" \
+ "\uCEC8" \
+ "\uCEE4" \
+ "\uCF00" \
+ "\uCF1C" \
+ "\uCF38" \
+ "\uCF54" \
+ "\uCF70" \
+ "\uCF8C" \
+ "\uCFA8" \
+ "\uCFC4" \
+ "\uCFE0" \
+ "\uCFFC" \
+ "\uD018" \
+ "\uD034" \
+ "\uD050" \
+ "\uD06C" \
+ "\uD088" \
+ "\uD0A4" \
+ "\uD0C0" \
+ "\uD0DC" \
+ "\uD0F8" \
+ "\uD114" \
+ "\uD130" \
+ "\uD14C" \
+ "\uD168" \
+ "\uD184" \
+ "\uD1A0" \
+ "\uD1BC" \
+ "\uD1D8" \
+ "\uD1F4" \
+ "\uD210" \
+ "\uD22C" \
+ "\uD248" \
+ "\uD264" \
+ "\uD280" \
+ "\uD29C" \
+ "\uD2B8" \
+ "\uD2D4" \
+ "\uD2F0" \
+ "\uD30C" \
+ "\uD328" \
+ "\uD344" \
+ "\uD360" \
+ "\uD37C" \
+ "\uD398" \
+ "\uD3B4" \
+ "\uD3D0" \
+ "\uD3EC" \
+ "\uD408" \
+ "\uD424" \
+ "\uD440" \
+ "\uD45C" \
+ "\uD478" \
+ "\uD494" \
+ "\uD4B0" \
+ "\uD4CC" \
+ "\uD4E8" \
+ "\uD504" \
+ "\uD520" \
+ "\uD53C" \
+ "\uD558" \
+ "\uD574" \
+ "\uD590" \
+ "\uD5AC" \
+ "\uD5C8" \
+ "\uD5E4" \
+ "\uD600" \
+ "\uD61C" \
+ "\uD638" \
+ "\uD654" \
+ "\uD670" \
+ "\uD68C" \
+ "\uD6A8" \
+ "\uD6C4" \
+ "\uD6E0" \
+ "\uD6FC" \
+ "\uD718" \
+ "\uD734" \
+ "\uD750" \
+ "\uD76C" \
+ "\uD788" \
"][\u11A8-\u11C2]" \
"|#{'' # decomposed Hangul syllables
}" \
"[\u1100-\u1112][\u1161-\u1175][\u11A8-\u11C2]?"
REGEXP_K_STRING = "" \
- "[\u00A0\u00A8\u00AA\u00AF\u00B2-\u00B5\u00B8-\u00BA\u00BC-\u00BE\u0132\u0133" \
- "\u013F\u0140\u0149\u017F\u01C4-\u01CC\u01F1-\u01F3\u02B0-\u02B8\u02D8-\u02DD\u02E0-\u02E4" \
- "\u037A\u0384\u0385\u03D0-\u03D6\u03F0-\u03F2\u03F4\u03F5\u03F9\u0587\u0675-\u0678" \
- "\u0E33\u0EB3\u0EDC\u0EDD\u0F0C\u0F77\u0F79\u10FC\u1D2C-\u1D2E" \
- "\u1D30-\u1D3A\u1D3C-\u1D4D\u1D4F-\u1D6A\u1D78\u1D9B-\u1DBF\u1E9A\u1E9B\u1FBD\u1FBF-\u1FC1" \
- "\u1FCD-\u1FCF\u1FDD-\u1FDF\u1FED\u1FEE\u1FFD\u1FFE\u2000-\u200A\u2011\u2017\u2024-\u2026" \
- "\u202F\u2033\u2034\u2036\u2037\u203C\u203E\u2047-\u2049\u2057\u205F" \
- "\u2070\u2071\u2074-\u208E\u2090-\u209C\u20A8\u2100-\u2103\u2105-\u2107\u2109-\u2113\u2115\u2116" \
- "\u2119-\u211D\u2120-\u2122\u2124\u2128\u212C\u212D\u212F-\u2131\u2133-\u2139\u213B-\u2140" \
- "\u2145-\u2149\u2150-\u217F\u2189\u222C\u222D\u222F\u2230\u2460-\u24EA\u2A0C\u2A74-\u2A76" \
- "\u2C7C\u2C7D\u2D6F\u2E9F\u2EF3\u2F00-\u2FD5\u3000\u3036\u3038-\u303A" \
- "\u309B\u309C\u309F\u30FF\u3131-\u318E\u3192-\u319F\u3200-\u321E\u3220-\u3247\u3250-\u327E" \
- "\u3280-\u32FE\u3300-\u33FF\uA69C\uA69D\uA770\uA7F8\uA7F9\uAB5C-\uAB5F\uFB00-\uFB06\uFB13-\uFB17" \
- "\uFB20-\uFB29\uFB4F-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFC\uFE10-\uFE19\uFE30-\uFE44" \
- "\uFE47-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFE70-\uFE72\uFE74\uFE76-\uFEFC\uFF01-\uFFBE\uFFC2-\uFFC7" \
- "\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC\uFFE0-\uFFE6\uFFE8-\uFFEE\u{1D400}-\u{1D454}\u{1D456}-\u{1D49C}\u{1D49E}\u{1D49F}" \
- "\u{1D4A2}\u{1D4A5}\u{1D4A6}\u{1D4A9}-\u{1D4AC}\u{1D4AE}-\u{1D4B9}\u{1D4BB}\u{1D4BD}-\u{1D4C3}\u{1D4C5}-\u{1D505}\u{1D507}-\u{1D50A}" \
- "\u{1D50D}-\u{1D514}\u{1D516}-\u{1D51C}\u{1D51E}-\u{1D539}\u{1D53B}-\u{1D53E}\u{1D540}-\u{1D544}\u{1D546}\u{1D54A}-\u{1D550}\u{1D552}-\u{1D6A5}" \
- "\u{1D6A8}-\u{1D7CB}\u{1D7CE}-\u{1D7FF}\u{1EE00}-\u{1EE03}\u{1EE05}-\u{1EE1F}\u{1EE21}\u{1EE22}\u{1EE24}\u{1EE27}\u{1EE29}-\u{1EE32}" \
- "\u{1EE34}-\u{1EE37}\u{1EE39}\u{1EE3B}\u{1EE42}\u{1EE47}\u{1EE49}\u{1EE4B}\u{1EE4D}-\u{1EE4F}" \
- "\u{1EE51}\u{1EE52}\u{1EE54}\u{1EE57}\u{1EE59}\u{1EE5B}\u{1EE5D}\u{1EE5F}\u{1EE61}\u{1EE62}" \
- "\u{1EE64}\u{1EE67}-\u{1EE6A}\u{1EE6C}-\u{1EE72}\u{1EE74}-\u{1EE77}\u{1EE79}-\u{1EE7C}\u{1EE7E}\u{1EE80}-\u{1EE89}\u{1EE8B}-\u{1EE9B}" \
- "\u{1EEA1}-\u{1EEA3}\u{1EEA5}-\u{1EEA9}\u{1EEAB}-\u{1EEBB}\u{1F100}-\u{1F10A}\u{1F110}-\u{1F12E}\u{1F130}-\u{1F14F}\u{1F16A}\u{1F16B}\u{1F190}" \
- "\u{1F200}-\u{1F202}\u{1F210}-\u{1F23B}\u{1F240}-\u{1F248}\u{1F250}\u{1F251}" \
+ "[\u00A0" \
+ "\u00A8" \
+ "\u00AA" \
+ "\u00AF" \
+ "\u00B2-\u00B5" \
+ "\u00B8-\u00BA" \
+ "\u00BC-\u00BE" \
+ "\u0132\u0133" \
+ "\u013F\u0140" \
+ "\u0149" \
+ "\u017F" \
+ "\u01C4-\u01CC" \
+ "\u01F1-\u01F3" \
+ "\u02B0-\u02B8" \
+ "\u02D8-\u02DD" \
+ "\u02E0-\u02E4" \
+ "\u037A" \
+ "\u0384\u0385" \
+ "\u03D0-\u03D6" \
+ "\u03F0-\u03F2" \
+ "\u03F4\u03F5" \
+ "\u03F9" \
+ "\u0587" \
+ "\u0675-\u0678" \
+ "\u0E33" \
+ "\u0EB3" \
+ "\u0EDC\u0EDD" \
+ "\u0F0C" \
+ "\u0F77" \
+ "\u0F79" \
+ "\u10FC" \
+ "\u1D2C-\u1D2E" \
+ "\u1D30-\u1D3A" \
+ "\u1D3C-\u1D4D" \
+ "\u1D4F-\u1D6A" \
+ "\u1D78" \
+ "\u1D9B-\u1DBF" \
+ "\u1E9A\u1E9B" \
+ "\u1FBD" \
+ "\u1FBF-\u1FC1" \
+ "\u1FCD-\u1FCF" \
+ "\u1FDD-\u1FDF" \
+ "\u1FED\u1FEE" \
+ "\u1FFD\u1FFE" \
+ "\u2000-\u200A" \
+ "\u2011" \
+ "\u2017" \
+ "\u2024-\u2026" \
+ "\u202F" \
+ "\u2033\u2034" \
+ "\u2036\u2037" \
+ "\u203C" \
+ "\u203E" \
+ "\u2047-\u2049" \
+ "\u2057" \
+ "\u205F" \
+ "\u2070\u2071" \
+ "\u2074-\u208E" \
+ "\u2090-\u209C" \
+ "\u20A8" \
+ "\u2100-\u2103" \
+ "\u2105-\u2107" \
+ "\u2109-\u2113" \
+ "\u2115\u2116" \
+ "\u2119-\u211D" \
+ "\u2120-\u2122" \
+ "\u2124" \
+ "\u2128" \
+ "\u212C\u212D" \
+ "\u212F-\u2131" \
+ "\u2133-\u2139" \
+ "\u213B-\u2140" \
+ "\u2145-\u2149" \
+ "\u2150-\u217F" \
+ "\u2189" \
+ "\u222C\u222D" \
+ "\u222F\u2230" \
+ "\u2460-\u24EA" \
+ "\u2A0C" \
+ "\u2A74-\u2A76" \
+ "\u2C7C\u2C7D" \
+ "\u2D6F" \
+ "\u2E9F" \
+ "\u2EF3" \
+ "\u2F00-\u2FD5" \
+ "\u3000" \
+ "\u3036" \
+ "\u3038-\u303A" \
+ "\u309B\u309C" \
+ "\u309F" \
+ "\u30FF" \
+ "\u3131-\u318E" \
+ "\u3192-\u319F" \
+ "\u3200-\u321E" \
+ "\u3220-\u3247" \
+ "\u3250-\u327E" \
+ "\u3280-\u33FF" \
+ "\uA69C\uA69D" \
+ "\uA770" \
+ "\uA7F8\uA7F9" \
+ "\uAB5C-\uAB5F" \
+ "\uFB00-\uFB06" \
+ "\uFB13-\uFB17" \
+ "\uFB20-\uFB29" \
+ "\uFB4F-\uFBB1" \
+ "\uFBD3-\uFD3D" \
+ "\uFD50-\uFD8F" \
+ "\uFD92-\uFDC7" \
+ "\uFDF0-\uFDFC" \
+ "\uFE10-\uFE19" \
+ "\uFE30-\uFE44" \
+ "\uFE47-\uFE52" \
+ "\uFE54-\uFE66" \
+ "\uFE68-\uFE6B" \
+ "\uFE70-\uFE72" \
+ "\uFE74" \
+ "\uFE76-\uFEFC" \
+ "\uFF01-\uFFBE" \
+ "\uFFC2-\uFFC7" \
+ "\uFFCA-\uFFCF" \
+ "\uFFD2-\uFFD7" \
+ "\uFFDA-\uFFDC" \
+ "\uFFE0-\uFFE6" \
+ "\uFFE8-\uFFEE" \
+ "\u{1D400}-\u{1D454}" \
+ "\u{1D456}-\u{1D49C}" \
+ "\u{1D49E}\u{1D49F}" \
+ "\u{1D4A2}" \
+ "\u{1D4A5}\u{1D4A6}" \
+ "\u{1D4A9}-\u{1D4AC}" \
+ "\u{1D4AE}-\u{1D4B9}" \
+ "\u{1D4BB}" \
+ "\u{1D4BD}-\u{1D4C3}" \
+ "\u{1D4C5}-\u{1D505}" \
+ "\u{1D507}-\u{1D50A}" \
+ "\u{1D50D}-\u{1D514}" \
+ "\u{1D516}-\u{1D51C}" \
+ "\u{1D51E}-\u{1D539}" \
+ "\u{1D53B}-\u{1D53E}" \
+ "\u{1D540}-\u{1D544}" \
+ "\u{1D546}" \
+ "\u{1D54A}-\u{1D550}" \
+ "\u{1D552}-\u{1D6A5}" \
+ "\u{1D6A8}-\u{1D7CB}" \
+ "\u{1D7CE}-\u{1D7FF}" \
+ "\u{1EE00}-\u{1EE03}" \
+ "\u{1EE05}-\u{1EE1F}" \
+ "\u{1EE21}\u{1EE22}" \
+ "\u{1EE24}" \
+ "\u{1EE27}" \
+ "\u{1EE29}-\u{1EE32}" \
+ "\u{1EE34}-\u{1EE37}" \
+ "\u{1EE39}" \
+ "\u{1EE3B}" \
+ "\u{1EE42}" \
+ "\u{1EE47}" \
+ "\u{1EE49}" \
+ "\u{1EE4B}" \
+ "\u{1EE4D}-\u{1EE4F}" \
+ "\u{1EE51}\u{1EE52}" \
+ "\u{1EE54}" \
+ "\u{1EE57}" \
+ "\u{1EE59}" \
+ "\u{1EE5B}" \
+ "\u{1EE5D}" \
+ "\u{1EE5F}" \
+ "\u{1EE61}\u{1EE62}" \
+ "\u{1EE64}" \
+ "\u{1EE67}-\u{1EE6A}" \
+ "\u{1EE6C}-\u{1EE72}" \
+ "\u{1EE74}-\u{1EE77}" \
+ "\u{1EE79}-\u{1EE7C}" \
+ "\u{1EE7E}" \
+ "\u{1EE80}-\u{1EE89}" \
+ "\u{1EE8B}-\u{1EE9B}" \
+ "\u{1EEA1}-\u{1EEA3}" \
+ "\u{1EEA5}-\u{1EEA9}" \
+ "\u{1EEAB}-\u{1EEBB}" \
+ "\u{1F100}-\u{1F10A}" \
+ "\u{1F110}-\u{1F12E}" \
+ "\u{1F130}-\u{1F14F}" \
+ "\u{1F16A}-\u{1F16C}" \
+ "\u{1F190}" \
+ "\u{1F200}-\u{1F202}" \
+ "\u{1F210}-\u{1F23B}" \
+ "\u{1F240}-\u{1F248}" \
+ "\u{1F250}\u{1F251}" \
"]"
class_table = {
- "\u0300"=>230, "\u0301"=>230, "\u0302"=>230, "\u0303"=>230, "\u0304"=>230, "\u0305"=>230, "\u0306"=>230, "\u0307"=>230,
- "\u0308"=>230, "\u0309"=>230, "\u030A"=>230, "\u030B"=>230, "\u030C"=>230, "\u030D"=>230, "\u030E"=>230, "\u030F"=>230,
- "\u0310"=>230, "\u0311"=>230, "\u0312"=>230, "\u0313"=>230, "\u0314"=>230, "\u0315"=>232, "\u0316"=>220, "\u0317"=>220,
- "\u0318"=>220, "\u0319"=>220, "\u031A"=>232, "\u031B"=>216, "\u031C"=>220, "\u031D"=>220, "\u031E"=>220, "\u031F"=>220,
- "\u0320"=>220, "\u0321"=>202, "\u0322"=>202, "\u0323"=>220, "\u0324"=>220, "\u0325"=>220, "\u0326"=>220, "\u0327"=>202,
- "\u0328"=>202, "\u0329"=>220, "\u032A"=>220, "\u032B"=>220, "\u032C"=>220, "\u032D"=>220, "\u032E"=>220, "\u032F"=>220,
- "\u0330"=>220, "\u0331"=>220, "\u0332"=>220, "\u0333"=>220, "\u0334"=>1, "\u0335"=>1, "\u0336"=>1, "\u0337"=>1,
- "\u0338"=>1, "\u0339"=>220, "\u033A"=>220, "\u033B"=>220, "\u033C"=>220, "\u033D"=>230, "\u033E"=>230, "\u033F"=>230,
- "\u0340"=>230, "\u0341"=>230, "\u0342"=>230, "\u0343"=>230, "\u0344"=>230, "\u0345"=>240, "\u0346"=>230, "\u0347"=>220,
- "\u0348"=>220, "\u0349"=>220, "\u034A"=>230, "\u034B"=>230, "\u034C"=>230, "\u034D"=>220, "\u034E"=>220, "\u0350"=>230,
- "\u0351"=>230, "\u0352"=>230, "\u0353"=>220, "\u0354"=>220, "\u0355"=>220, "\u0356"=>220, "\u0357"=>230, "\u0358"=>232,
- "\u0359"=>220, "\u035A"=>220, "\u035B"=>230, "\u035C"=>233, "\u035D"=>234, "\u035E"=>234, "\u035F"=>233, "\u0360"=>234,
- "\u0361"=>234, "\u0362"=>233, "\u0363"=>230, "\u0364"=>230, "\u0365"=>230, "\u0366"=>230, "\u0367"=>230, "\u0368"=>230,
- "\u0369"=>230, "\u036A"=>230, "\u036B"=>230, "\u036C"=>230, "\u036D"=>230, "\u036E"=>230, "\u036F"=>230, "\u0483"=>230,
- "\u0484"=>230, "\u0485"=>230, "\u0486"=>230, "\u0487"=>230, "\u0591"=>220, "\u0592"=>230, "\u0593"=>230, "\u0594"=>230,
- "\u0595"=>230, "\u0596"=>220, "\u0597"=>230, "\u0598"=>230, "\u0599"=>230, "\u059A"=>222, "\u059B"=>220, "\u059C"=>230,
- "\u059D"=>230, "\u059E"=>230, "\u059F"=>230, "\u05A0"=>230, "\u05A1"=>230, "\u05A2"=>220, "\u05A3"=>220, "\u05A4"=>220,
- "\u05A5"=>220, "\u05A6"=>220, "\u05A7"=>220, "\u05A8"=>230, "\u05A9"=>230, "\u05AA"=>220, "\u05AB"=>230, "\u05AC"=>230,
- "\u05AD"=>222, "\u05AE"=>228, "\u05AF"=>230, "\u05B0"=>10, "\u05B1"=>11, "\u05B2"=>12, "\u05B3"=>13, "\u05B4"=>14,
- "\u05B5"=>15, "\u05B6"=>16, "\u05B7"=>17, "\u05B8"=>18, "\u05B9"=>19, "\u05BA"=>19, "\u05BB"=>20, "\u05BC"=>21,
- "\u05BD"=>22, "\u05BF"=>23, "\u05C1"=>24, "\u05C2"=>25, "\u05C4"=>230, "\u05C5"=>220, "\u05C7"=>18, "\u0610"=>230,
- "\u0611"=>230, "\u0612"=>230, "\u0613"=>230, "\u0614"=>230, "\u0615"=>230, "\u0616"=>230, "\u0617"=>230, "\u0618"=>30,
- "\u0619"=>31, "\u061A"=>32, "\u064B"=>27, "\u064C"=>28, "\u064D"=>29, "\u064E"=>30, "\u064F"=>31, "\u0650"=>32,
- "\u0651"=>33, "\u0652"=>34, "\u0653"=>230, "\u0654"=>230, "\u0655"=>220, "\u0656"=>220, "\u0657"=>230, "\u0658"=>230,
- "\u0659"=>230, "\u065A"=>230, "\u065B"=>230, "\u065C"=>220, "\u065D"=>230, "\u065E"=>230, "\u065F"=>220, "\u0670"=>35,
- "\u06D6"=>230, "\u06D7"=>230, "\u06D8"=>230, "\u06D9"=>230, "\u06DA"=>230, "\u06DB"=>230, "\u06DC"=>230, "\u06DF"=>230,
- "\u06E0"=>230, "\u06E1"=>230, "\u06E2"=>230, "\u06E3"=>220, "\u06E4"=>230, "\u06E7"=>230, "\u06E8"=>230, "\u06EA"=>220,
- "\u06EB"=>230, "\u06EC"=>230, "\u06ED"=>220, "\u0711"=>36, "\u0730"=>230, "\u0731"=>220, "\u0732"=>230, "\u0733"=>230,
- "\u0734"=>220, "\u0735"=>230, "\u0736"=>230, "\u0737"=>220, "\u0738"=>220, "\u0739"=>220, "\u073A"=>230, "\u073B"=>220,
- "\u073C"=>220, "\u073D"=>230, "\u073E"=>220, "\u073F"=>230, "\u0740"=>230, "\u0741"=>230, "\u0742"=>220, "\u0743"=>230,
- "\u0744"=>220, "\u0745"=>230, "\u0746"=>220, "\u0747"=>230, "\u0748"=>220, "\u0749"=>230, "\u074A"=>230, "\u07EB"=>230,
- "\u07EC"=>230, "\u07ED"=>230, "\u07EE"=>230, "\u07EF"=>230, "\u07F0"=>230, "\u07F1"=>230, "\u07F2"=>220, "\u07F3"=>230,
- "\u0816"=>230, "\u0817"=>230, "\u0818"=>230, "\u0819"=>230, "\u081B"=>230, "\u081C"=>230, "\u081D"=>230, "\u081E"=>230,
- "\u081F"=>230, "\u0820"=>230, "\u0821"=>230, "\u0822"=>230, "\u0823"=>230, "\u0825"=>230, "\u0826"=>230, "\u0827"=>230,
- "\u0829"=>230, "\u082A"=>230, "\u082B"=>230, "\u082C"=>230, "\u082D"=>230, "\u0859"=>220, "\u085A"=>220, "\u085B"=>220,
- "\u08D4"=>230, "\u08D5"=>230, "\u08D6"=>230, "\u08D7"=>230, "\u08D8"=>230, "\u08D9"=>230, "\u08DA"=>230, "\u08DB"=>230,
- "\u08DC"=>230, "\u08DD"=>230, "\u08DE"=>230, "\u08DF"=>230, "\u08E0"=>230, "\u08E1"=>230, "\u08E3"=>220, "\u08E4"=>230,
- "\u08E5"=>230, "\u08E6"=>220, "\u08E7"=>230, "\u08E8"=>230, "\u08E9"=>220, "\u08EA"=>230, "\u08EB"=>230, "\u08EC"=>230,
- "\u08ED"=>220, "\u08EE"=>220, "\u08EF"=>220, "\u08F0"=>27, "\u08F1"=>28, "\u08F2"=>29, "\u08F3"=>230, "\u08F4"=>230,
- "\u08F5"=>230, "\u08F6"=>220, "\u08F7"=>230, "\u08F8"=>230, "\u08F9"=>220, "\u08FA"=>220, "\u08FB"=>230, "\u08FC"=>230,
- "\u08FD"=>230, "\u08FE"=>230, "\u08FF"=>230, "\u093C"=>7, "\u094D"=>9, "\u0951"=>230, "\u0952"=>220, "\u0953"=>230,
- "\u0954"=>230, "\u09BC"=>7, "\u09CD"=>9, "\u0A3C"=>7, "\u0A4D"=>9, "\u0ABC"=>7, "\u0ACD"=>9, "\u0B3C"=>7,
- "\u0B4D"=>9, "\u0BCD"=>9, "\u0C4D"=>9, "\u0C55"=>84, "\u0C56"=>91, "\u0CBC"=>7, "\u0CCD"=>9, "\u0D4D"=>9,
- "\u0DCA"=>9, "\u0E38"=>103, "\u0E39"=>103, "\u0E3A"=>9, "\u0E48"=>107, "\u0E49"=>107, "\u0E4A"=>107, "\u0E4B"=>107,
- "\u0EB8"=>118, "\u0EB9"=>118, "\u0EC8"=>122, "\u0EC9"=>122, "\u0ECA"=>122, "\u0ECB"=>122, "\u0F18"=>220, "\u0F19"=>220,
- "\u0F35"=>220, "\u0F37"=>220, "\u0F39"=>216, "\u0F71"=>129, "\u0F72"=>130, "\u0F74"=>132, "\u0F7A"=>130, "\u0F7B"=>130,
- "\u0F7C"=>130, "\u0F7D"=>130, "\u0F80"=>130, "\u0F82"=>230, "\u0F83"=>230, "\u0F84"=>9, "\u0F86"=>230, "\u0F87"=>230,
- "\u0FC6"=>220, "\u1037"=>7, "\u1039"=>9, "\u103A"=>9, "\u108D"=>220, "\u135D"=>230, "\u135E"=>230, "\u135F"=>230,
- "\u1714"=>9, "\u1734"=>9, "\u17D2"=>9, "\u17DD"=>230, "\u18A9"=>228, "\u1939"=>222, "\u193A"=>230, "\u193B"=>220,
- "\u1A17"=>230, "\u1A18"=>220, "\u1A60"=>9, "\u1A75"=>230, "\u1A76"=>230, "\u1A77"=>230, "\u1A78"=>230, "\u1A79"=>230,
- "\u1A7A"=>230, "\u1A7B"=>230, "\u1A7C"=>230, "\u1A7F"=>220, "\u1AB0"=>230, "\u1AB1"=>230, "\u1AB2"=>230, "\u1AB3"=>230,
- "\u1AB4"=>230, "\u1AB5"=>220, "\u1AB6"=>220, "\u1AB7"=>220, "\u1AB8"=>220, "\u1AB9"=>220, "\u1ABA"=>220, "\u1ABB"=>230,
- "\u1ABC"=>230, "\u1ABD"=>220, "\u1B34"=>7, "\u1B44"=>9, "\u1B6B"=>230, "\u1B6C"=>220, "\u1B6D"=>230, "\u1B6E"=>230,
- "\u1B6F"=>230, "\u1B70"=>230, "\u1B71"=>230, "\u1B72"=>230, "\u1B73"=>230, "\u1BAA"=>9, "\u1BAB"=>9, "\u1BE6"=>7,
- "\u1BF2"=>9, "\u1BF3"=>9, "\u1C37"=>7, "\u1CD0"=>230, "\u1CD1"=>230, "\u1CD2"=>230, "\u1CD4"=>1, "\u1CD5"=>220,
- "\u1CD6"=>220, "\u1CD7"=>220, "\u1CD8"=>220, "\u1CD9"=>220, "\u1CDA"=>230, "\u1CDB"=>230, "\u1CDC"=>220, "\u1CDD"=>220,
- "\u1CDE"=>220, "\u1CDF"=>220, "\u1CE0"=>230, "\u1CE2"=>1, "\u1CE3"=>1, "\u1CE4"=>1, "\u1CE5"=>1, "\u1CE6"=>1,
- "\u1CE7"=>1, "\u1CE8"=>1, "\u1CED"=>220, "\u1CF4"=>230, "\u1CF8"=>230, "\u1CF9"=>230, "\u1DC0"=>230, "\u1DC1"=>230,
- "\u1DC2"=>220, "\u1DC3"=>230, "\u1DC4"=>230, "\u1DC5"=>230, "\u1DC6"=>230, "\u1DC7"=>230, "\u1DC8"=>230, "\u1DC9"=>230,
- "\u1DCA"=>220, "\u1DCB"=>230, "\u1DCC"=>230, "\u1DCD"=>234, "\u1DCE"=>214, "\u1DCF"=>220, "\u1DD0"=>202, "\u1DD1"=>230,
- "\u1DD2"=>230, "\u1DD3"=>230, "\u1DD4"=>230, "\u1DD5"=>230, "\u1DD6"=>230, "\u1DD7"=>230, "\u1DD8"=>230, "\u1DD9"=>230,
- "\u1DDA"=>230, "\u1DDB"=>230, "\u1DDC"=>230, "\u1DDD"=>230, "\u1DDE"=>230, "\u1DDF"=>230, "\u1DE0"=>230, "\u1DE1"=>230,
- "\u1DE2"=>230, "\u1DE3"=>230, "\u1DE4"=>230, "\u1DE5"=>230, "\u1DE6"=>230, "\u1DE7"=>230, "\u1DE8"=>230, "\u1DE9"=>230,
- "\u1DEA"=>230, "\u1DEB"=>230, "\u1DEC"=>230, "\u1DED"=>230, "\u1DEE"=>230, "\u1DEF"=>230, "\u1DF0"=>230, "\u1DF1"=>230,
- "\u1DF2"=>230, "\u1DF3"=>230, "\u1DF4"=>230, "\u1DF5"=>230, "\u1DFB"=>230, "\u1DFC"=>233, "\u1DFD"=>220, "\u1DFE"=>230,
- "\u1DFF"=>220, "\u20D0"=>230, "\u20D1"=>230, "\u20D2"=>1, "\u20D3"=>1, "\u20D4"=>230, "\u20D5"=>230, "\u20D6"=>230,
- "\u20D7"=>230, "\u20D8"=>1, "\u20D9"=>1, "\u20DA"=>1, "\u20DB"=>230, "\u20DC"=>230, "\u20E1"=>230, "\u20E5"=>1,
- "\u20E6"=>1, "\u20E7"=>230, "\u20E8"=>220, "\u20E9"=>230, "\u20EA"=>1, "\u20EB"=>1, "\u20EC"=>220, "\u20ED"=>220,
- "\u20EE"=>220, "\u20EF"=>220, "\u20F0"=>230, "\u2CEF"=>230, "\u2CF0"=>230, "\u2CF1"=>230, "\u2D7F"=>9, "\u2DE0"=>230,
- "\u2DE1"=>230, "\u2DE2"=>230, "\u2DE3"=>230, "\u2DE4"=>230, "\u2DE5"=>230, "\u2DE6"=>230, "\u2DE7"=>230, "\u2DE8"=>230,
- "\u2DE9"=>230, "\u2DEA"=>230, "\u2DEB"=>230, "\u2DEC"=>230, "\u2DED"=>230, "\u2DEE"=>230, "\u2DEF"=>230, "\u2DF0"=>230,
- "\u2DF1"=>230, "\u2DF2"=>230, "\u2DF3"=>230, "\u2DF4"=>230, "\u2DF5"=>230, "\u2DF6"=>230, "\u2DF7"=>230, "\u2DF8"=>230,
- "\u2DF9"=>230, "\u2DFA"=>230, "\u2DFB"=>230, "\u2DFC"=>230, "\u2DFD"=>230, "\u2DFE"=>230, "\u2DFF"=>230, "\u302A"=>218,
- "\u302B"=>228, "\u302C"=>232, "\u302D"=>222, "\u302E"=>224, "\u302F"=>224, "\u3099"=>8, "\u309A"=>8, "\uA66F"=>230,
- "\uA674"=>230, "\uA675"=>230, "\uA676"=>230, "\uA677"=>230, "\uA678"=>230, "\uA679"=>230, "\uA67A"=>230, "\uA67B"=>230,
- "\uA67C"=>230, "\uA67D"=>230, "\uA69E"=>230, "\uA69F"=>230, "\uA6F0"=>230, "\uA6F1"=>230, "\uA806"=>9, "\uA8C4"=>9,
- "\uA8E0"=>230, "\uA8E1"=>230, "\uA8E2"=>230, "\uA8E3"=>230, "\uA8E4"=>230, "\uA8E5"=>230, "\uA8E6"=>230, "\uA8E7"=>230,
- "\uA8E8"=>230, "\uA8E9"=>230, "\uA8EA"=>230, "\uA8EB"=>230, "\uA8EC"=>230, "\uA8ED"=>230, "\uA8EE"=>230, "\uA8EF"=>230,
- "\uA8F0"=>230, "\uA8F1"=>230, "\uA92B"=>220, "\uA92C"=>220, "\uA92D"=>220, "\uA953"=>9, "\uA9B3"=>7, "\uA9C0"=>9,
- "\uAAB0"=>230, "\uAAB2"=>230, "\uAAB3"=>230, "\uAAB4"=>220, "\uAAB7"=>230, "\uAAB8"=>230, "\uAABE"=>230, "\uAABF"=>230,
- "\uAAC1"=>230, "\uAAF6"=>9, "\uABED"=>9, "\uFB1E"=>26, "\uFE20"=>230, "\uFE21"=>230, "\uFE22"=>230, "\uFE23"=>230,
- "\uFE24"=>230, "\uFE25"=>230, "\uFE26"=>230, "\uFE27"=>220, "\uFE28"=>220, "\uFE29"=>220, "\uFE2A"=>220, "\uFE2B"=>220,
- "\uFE2C"=>220, "\uFE2D"=>220, "\uFE2E"=>230, "\uFE2F"=>230, "\u{101FD}"=>220, "\u{102E0}"=>220, "\u{10376}"=>230, "\u{10377}"=>230,
- "\u{10378}"=>230, "\u{10379}"=>230, "\u{1037A}"=>230, "\u{10A0D}"=>220, "\u{10A0F}"=>230, "\u{10A38}"=>230, "\u{10A39}"=>1, "\u{10A3A}"=>220,
- "\u{10A3F}"=>9, "\u{10AE5}"=>230, "\u{10AE6}"=>220, "\u{11046}"=>9, "\u{1107F}"=>9, "\u{110B9}"=>9, "\u{110BA}"=>7, "\u{11100}"=>230,
- "\u{11101}"=>230, "\u{11102}"=>230, "\u{11133}"=>9, "\u{11134}"=>9, "\u{11173}"=>7, "\u{111C0}"=>9, "\u{111CA}"=>7, "\u{11235}"=>9,
- "\u{11236}"=>7, "\u{112E9}"=>7, "\u{112EA}"=>9, "\u{1133C}"=>7, "\u{1134D}"=>9, "\u{11366}"=>230, "\u{11367}"=>230, "\u{11368}"=>230,
- "\u{11369}"=>230, "\u{1136A}"=>230, "\u{1136B}"=>230, "\u{1136C}"=>230, "\u{11370}"=>230, "\u{11371}"=>230, "\u{11372}"=>230, "\u{11373}"=>230,
- "\u{11374}"=>230, "\u{11442}"=>9, "\u{11446}"=>7, "\u{114C2}"=>9, "\u{114C3}"=>7, "\u{115BF}"=>9, "\u{115C0}"=>7, "\u{1163F}"=>9,
- "\u{116B6}"=>9, "\u{116B7}"=>7, "\u{1172B}"=>9, "\u{11C3F}"=>9, "\u{16AF0}"=>1, "\u{16AF1}"=>1, "\u{16AF2}"=>1, "\u{16AF3}"=>1,
- "\u{16AF4}"=>1, "\u{16B30}"=>230, "\u{16B31}"=>230, "\u{16B32}"=>230, "\u{16B33}"=>230, "\u{16B34}"=>230, "\u{16B35}"=>230, "\u{16B36}"=>230,
- "\u{1BC9E}"=>1, "\u{1D165}"=>216, "\u{1D166}"=>216, "\u{1D167}"=>1, "\u{1D168}"=>1, "\u{1D169}"=>1, "\u{1D16D}"=>226, "\u{1D16E}"=>216,
- "\u{1D16F}"=>216, "\u{1D170}"=>216, "\u{1D171}"=>216, "\u{1D172}"=>216, "\u{1D17B}"=>220, "\u{1D17C}"=>220, "\u{1D17D}"=>220, "\u{1D17E}"=>220,
- "\u{1D17F}"=>220, "\u{1D180}"=>220, "\u{1D181}"=>220, "\u{1D182}"=>220, "\u{1D185}"=>230, "\u{1D186}"=>230, "\u{1D187}"=>230, "\u{1D188}"=>230,
- "\u{1D189}"=>230, "\u{1D18A}"=>220, "\u{1D18B}"=>220, "\u{1D1AA}"=>230, "\u{1D1AB}"=>230, "\u{1D1AC}"=>230, "\u{1D1AD}"=>230, "\u{1D242}"=>230,
- "\u{1D243}"=>230, "\u{1D244}"=>230, "\u{1E000}"=>230, "\u{1E001}"=>230, "\u{1E002}"=>230, "\u{1E003}"=>230, "\u{1E004}"=>230, "\u{1E005}"=>230,
- "\u{1E006}"=>230, "\u{1E008}"=>230, "\u{1E009}"=>230, "\u{1E00A}"=>230, "\u{1E00B}"=>230, "\u{1E00C}"=>230, "\u{1E00D}"=>230, "\u{1E00E}"=>230,
- "\u{1E00F}"=>230, "\u{1E010}"=>230, "\u{1E011}"=>230, "\u{1E012}"=>230, "\u{1E013}"=>230, "\u{1E014}"=>230, "\u{1E015}"=>230, "\u{1E016}"=>230,
- "\u{1E017}"=>230, "\u{1E018}"=>230, "\u{1E01B}"=>230, "\u{1E01C}"=>230, "\u{1E01D}"=>230, "\u{1E01E}"=>230, "\u{1E01F}"=>230, "\u{1E020}"=>230,
- "\u{1E021}"=>230, "\u{1E023}"=>230, "\u{1E024}"=>230, "\u{1E026}"=>230, "\u{1E027}"=>230, "\u{1E028}"=>230, "\u{1E029}"=>230, "\u{1E02A}"=>230,
- "\u{1E8D0}"=>220, "\u{1E8D1}"=>220, "\u{1E8D2}"=>220, "\u{1E8D3}"=>220, "\u{1E8D4}"=>220, "\u{1E8D5}"=>220, "\u{1E8D6}"=>220, "\u{1E944}"=>230,
- "\u{1E945}"=>230, "\u{1E946}"=>230, "\u{1E947}"=>230, "\u{1E948}"=>230, "\u{1E949}"=>230, "\u{1E94A}"=>7,
+ "\u0300"=>230,
+ "\u0301"=>230,
+ "\u0302"=>230,
+ "\u0303"=>230,
+ "\u0304"=>230,
+ "\u0305"=>230,
+ "\u0306"=>230,
+ "\u0307"=>230,
+ "\u0308"=>230,
+ "\u0309"=>230,
+ "\u030A"=>230,
+ "\u030B"=>230,
+ "\u030C"=>230,
+ "\u030D"=>230,
+ "\u030E"=>230,
+ "\u030F"=>230,
+ "\u0310"=>230,
+ "\u0311"=>230,
+ "\u0312"=>230,
+ "\u0313"=>230,
+ "\u0314"=>230,
+ "\u0315"=>232,
+ "\u0316"=>220,
+ "\u0317"=>220,
+ "\u0318"=>220,
+ "\u0319"=>220,
+ "\u031A"=>232,
+ "\u031B"=>216,
+ "\u031C"=>220,
+ "\u031D"=>220,
+ "\u031E"=>220,
+ "\u031F"=>220,
+ "\u0320"=>220,
+ "\u0321"=>202,
+ "\u0322"=>202,
+ "\u0323"=>220,
+ "\u0324"=>220,
+ "\u0325"=>220,
+ "\u0326"=>220,
+ "\u0327"=>202,
+ "\u0328"=>202,
+ "\u0329"=>220,
+ "\u032A"=>220,
+ "\u032B"=>220,
+ "\u032C"=>220,
+ "\u032D"=>220,
+ "\u032E"=>220,
+ "\u032F"=>220,
+ "\u0330"=>220,
+ "\u0331"=>220,
+ "\u0332"=>220,
+ "\u0333"=>220,
+ "\u0334"=>1,
+ "\u0335"=>1,
+ "\u0336"=>1,
+ "\u0337"=>1,
+ "\u0338"=>1,
+ "\u0339"=>220,
+ "\u033A"=>220,
+ "\u033B"=>220,
+ "\u033C"=>220,
+ "\u033D"=>230,
+ "\u033E"=>230,
+ "\u033F"=>230,
+ "\u0340"=>230,
+ "\u0341"=>230,
+ "\u0342"=>230,
+ "\u0343"=>230,
+ "\u0344"=>230,
+ "\u0345"=>240,
+ "\u0346"=>230,
+ "\u0347"=>220,
+ "\u0348"=>220,
+ "\u0349"=>220,
+ "\u034A"=>230,
+ "\u034B"=>230,
+ "\u034C"=>230,
+ "\u034D"=>220,
+ "\u034E"=>220,
+ "\u0350"=>230,
+ "\u0351"=>230,
+ "\u0352"=>230,
+ "\u0353"=>220,
+ "\u0354"=>220,
+ "\u0355"=>220,
+ "\u0356"=>220,
+ "\u0357"=>230,
+ "\u0358"=>232,
+ "\u0359"=>220,
+ "\u035A"=>220,
+ "\u035B"=>230,
+ "\u035C"=>233,
+ "\u035D"=>234,
+ "\u035E"=>234,
+ "\u035F"=>233,
+ "\u0360"=>234,
+ "\u0361"=>234,
+ "\u0362"=>233,
+ "\u0363"=>230,
+ "\u0364"=>230,
+ "\u0365"=>230,
+ "\u0366"=>230,
+ "\u0367"=>230,
+ "\u0368"=>230,
+ "\u0369"=>230,
+ "\u036A"=>230,
+ "\u036B"=>230,
+ "\u036C"=>230,
+ "\u036D"=>230,
+ "\u036E"=>230,
+ "\u036F"=>230,
+ "\u0483"=>230,
+ "\u0484"=>230,
+ "\u0485"=>230,
+ "\u0486"=>230,
+ "\u0487"=>230,
+ "\u0591"=>220,
+ "\u0592"=>230,
+ "\u0593"=>230,
+ "\u0594"=>230,
+ "\u0595"=>230,
+ "\u0596"=>220,
+ "\u0597"=>230,
+ "\u0598"=>230,
+ "\u0599"=>230,
+ "\u059A"=>222,
+ "\u059B"=>220,
+ "\u059C"=>230,
+ "\u059D"=>230,
+ "\u059E"=>230,
+ "\u059F"=>230,
+ "\u05A0"=>230,
+ "\u05A1"=>230,
+ "\u05A2"=>220,
+ "\u05A3"=>220,
+ "\u05A4"=>220,
+ "\u05A5"=>220,
+ "\u05A6"=>220,
+ "\u05A7"=>220,
+ "\u05A8"=>230,
+ "\u05A9"=>230,
+ "\u05AA"=>220,
+ "\u05AB"=>230,
+ "\u05AC"=>230,
+ "\u05AD"=>222,
+ "\u05AE"=>228,
+ "\u05AF"=>230,
+ "\u05B0"=>10,
+ "\u05B1"=>11,
+ "\u05B2"=>12,
+ "\u05B3"=>13,
+ "\u05B4"=>14,
+ "\u05B5"=>15,
+ "\u05B6"=>16,
+ "\u05B7"=>17,
+ "\u05B8"=>18,
+ "\u05B9"=>19,
+ "\u05BA"=>19,
+ "\u05BB"=>20,
+ "\u05BC"=>21,
+ "\u05BD"=>22,
+ "\u05BF"=>23,
+ "\u05C1"=>24,
+ "\u05C2"=>25,
+ "\u05C4"=>230,
+ "\u05C5"=>220,
+ "\u05C7"=>18,
+ "\u0610"=>230,
+ "\u0611"=>230,
+ "\u0612"=>230,
+ "\u0613"=>230,
+ "\u0614"=>230,
+ "\u0615"=>230,
+ "\u0616"=>230,
+ "\u0617"=>230,
+ "\u0618"=>30,
+ "\u0619"=>31,
+ "\u061A"=>32,
+ "\u064B"=>27,
+ "\u064C"=>28,
+ "\u064D"=>29,
+ "\u064E"=>30,
+ "\u064F"=>31,
+ "\u0650"=>32,
+ "\u0651"=>33,
+ "\u0652"=>34,
+ "\u0653"=>230,
+ "\u0654"=>230,
+ "\u0655"=>220,
+ "\u0656"=>220,
+ "\u0657"=>230,
+ "\u0658"=>230,
+ "\u0659"=>230,
+ "\u065A"=>230,
+ "\u065B"=>230,
+ "\u065C"=>220,
+ "\u065D"=>230,
+ "\u065E"=>230,
+ "\u065F"=>220,
+ "\u0670"=>35,
+ "\u06D6"=>230,
+ "\u06D7"=>230,
+ "\u06D8"=>230,
+ "\u06D9"=>230,
+ "\u06DA"=>230,
+ "\u06DB"=>230,
+ "\u06DC"=>230,
+ "\u06DF"=>230,
+ "\u06E0"=>230,
+ "\u06E1"=>230,
+ "\u06E2"=>230,
+ "\u06E3"=>220,
+ "\u06E4"=>230,
+ "\u06E7"=>230,
+ "\u06E8"=>230,
+ "\u06EA"=>220,
+ "\u06EB"=>230,
+ "\u06EC"=>230,
+ "\u06ED"=>220,
+ "\u0711"=>36,
+ "\u0730"=>230,
+ "\u0731"=>220,
+ "\u0732"=>230,
+ "\u0733"=>230,
+ "\u0734"=>220,
+ "\u0735"=>230,
+ "\u0736"=>230,
+ "\u0737"=>220,
+ "\u0738"=>220,
+ "\u0739"=>220,
+ "\u073A"=>230,
+ "\u073B"=>220,
+ "\u073C"=>220,
+ "\u073D"=>230,
+ "\u073E"=>220,
+ "\u073F"=>230,
+ "\u0740"=>230,
+ "\u0741"=>230,
+ "\u0742"=>220,
+ "\u0743"=>230,
+ "\u0744"=>220,
+ "\u0745"=>230,
+ "\u0746"=>220,
+ "\u0747"=>230,
+ "\u0748"=>220,
+ "\u0749"=>230,
+ "\u074A"=>230,
+ "\u07EB"=>230,
+ "\u07EC"=>230,
+ "\u07ED"=>230,
+ "\u07EE"=>230,
+ "\u07EF"=>230,
+ "\u07F0"=>230,
+ "\u07F1"=>230,
+ "\u07F2"=>220,
+ "\u07F3"=>230,
+ "\u07FD"=>220,
+ "\u0816"=>230,
+ "\u0817"=>230,
+ "\u0818"=>230,
+ "\u0819"=>230,
+ "\u081B"=>230,
+ "\u081C"=>230,
+ "\u081D"=>230,
+ "\u081E"=>230,
+ "\u081F"=>230,
+ "\u0820"=>230,
+ "\u0821"=>230,
+ "\u0822"=>230,
+ "\u0823"=>230,
+ "\u0825"=>230,
+ "\u0826"=>230,
+ "\u0827"=>230,
+ "\u0829"=>230,
+ "\u082A"=>230,
+ "\u082B"=>230,
+ "\u082C"=>230,
+ "\u082D"=>230,
+ "\u0859"=>220,
+ "\u085A"=>220,
+ "\u085B"=>220,
+ "\u08D3"=>220,
+ "\u08D4"=>230,
+ "\u08D5"=>230,
+ "\u08D6"=>230,
+ "\u08D7"=>230,
+ "\u08D8"=>230,
+ "\u08D9"=>230,
+ "\u08DA"=>230,
+ "\u08DB"=>230,
+ "\u08DC"=>230,
+ "\u08DD"=>230,
+ "\u08DE"=>230,
+ "\u08DF"=>230,
+ "\u08E0"=>230,
+ "\u08E1"=>230,
+ "\u08E3"=>220,
+ "\u08E4"=>230,
+ "\u08E5"=>230,
+ "\u08E6"=>220,
+ "\u08E7"=>230,
+ "\u08E8"=>230,
+ "\u08E9"=>220,
+ "\u08EA"=>230,
+ "\u08EB"=>230,
+ "\u08EC"=>230,
+ "\u08ED"=>220,
+ "\u08EE"=>220,
+ "\u08EF"=>220,
+ "\u08F0"=>27,
+ "\u08F1"=>28,
+ "\u08F2"=>29,
+ "\u08F3"=>230,
+ "\u08F4"=>230,
+ "\u08F5"=>230,
+ "\u08F6"=>220,
+ "\u08F7"=>230,
+ "\u08F8"=>230,
+ "\u08F9"=>220,
+ "\u08FA"=>220,
+ "\u08FB"=>230,
+ "\u08FC"=>230,
+ "\u08FD"=>230,
+ "\u08FE"=>230,
+ "\u08FF"=>230,
+ "\u093C"=>7,
+ "\u094D"=>9,
+ "\u0951"=>230,
+ "\u0952"=>220,
+ "\u0953"=>230,
+ "\u0954"=>230,
+ "\u09BC"=>7,
+ "\u09CD"=>9,
+ "\u09FE"=>230,
+ "\u0A3C"=>7,
+ "\u0A4D"=>9,
+ "\u0ABC"=>7,
+ "\u0ACD"=>9,
+ "\u0B3C"=>7,
+ "\u0B4D"=>9,
+ "\u0BCD"=>9,
+ "\u0C4D"=>9,
+ "\u0C55"=>84,
+ "\u0C56"=>91,
+ "\u0CBC"=>7,
+ "\u0CCD"=>9,
+ "\u0D3B"=>9,
+ "\u0D3C"=>9,
+ "\u0D4D"=>9,
+ "\u0DCA"=>9,
+ "\u0E38"=>103,
+ "\u0E39"=>103,
+ "\u0E3A"=>9,
+ "\u0E48"=>107,
+ "\u0E49"=>107,
+ "\u0E4A"=>107,
+ "\u0E4B"=>107,
+ "\u0EB8"=>118,
+ "\u0EB9"=>118,
+ "\u0EBA"=>9,
+ "\u0EC8"=>122,
+ "\u0EC9"=>122,
+ "\u0ECA"=>122,
+ "\u0ECB"=>122,
+ "\u0F18"=>220,
+ "\u0F19"=>220,
+ "\u0F35"=>220,
+ "\u0F37"=>220,
+ "\u0F39"=>216,
+ "\u0F71"=>129,
+ "\u0F72"=>130,
+ "\u0F74"=>132,
+ "\u0F7A"=>130,
+ "\u0F7B"=>130,
+ "\u0F7C"=>130,
+ "\u0F7D"=>130,
+ "\u0F80"=>130,
+ "\u0F82"=>230,
+ "\u0F83"=>230,
+ "\u0F84"=>9,
+ "\u0F86"=>230,
+ "\u0F87"=>230,
+ "\u0FC6"=>220,
+ "\u1037"=>7,
+ "\u1039"=>9,
+ "\u103A"=>9,
+ "\u108D"=>220,
+ "\u135D"=>230,
+ "\u135E"=>230,
+ "\u135F"=>230,
+ "\u1714"=>9,
+ "\u1734"=>9,
+ "\u17D2"=>9,
+ "\u17DD"=>230,
+ "\u18A9"=>228,
+ "\u1939"=>222,
+ "\u193A"=>230,
+ "\u193B"=>220,
+ "\u1A17"=>230,
+ "\u1A18"=>220,
+ "\u1A60"=>9,
+ "\u1A75"=>230,
+ "\u1A76"=>230,
+ "\u1A77"=>230,
+ "\u1A78"=>230,
+ "\u1A79"=>230,
+ "\u1A7A"=>230,
+ "\u1A7B"=>230,
+ "\u1A7C"=>230,
+ "\u1A7F"=>220,
+ "\u1AB0"=>230,
+ "\u1AB1"=>230,
+ "\u1AB2"=>230,
+ "\u1AB3"=>230,
+ "\u1AB4"=>230,
+ "\u1AB5"=>220,
+ "\u1AB6"=>220,
+ "\u1AB7"=>220,
+ "\u1AB8"=>220,
+ "\u1AB9"=>220,
+ "\u1ABA"=>220,
+ "\u1ABB"=>230,
+ "\u1ABC"=>230,
+ "\u1ABD"=>220,
+ "\u1B34"=>7,
+ "\u1B44"=>9,
+ "\u1B6B"=>230,
+ "\u1B6C"=>220,
+ "\u1B6D"=>230,
+ "\u1B6E"=>230,
+ "\u1B6F"=>230,
+ "\u1B70"=>230,
+ "\u1B71"=>230,
+ "\u1B72"=>230,
+ "\u1B73"=>230,
+ "\u1BAA"=>9,
+ "\u1BAB"=>9,
+ "\u1BE6"=>7,
+ "\u1BF2"=>9,
+ "\u1BF3"=>9,
+ "\u1C37"=>7,
+ "\u1CD0"=>230,
+ "\u1CD1"=>230,
+ "\u1CD2"=>230,
+ "\u1CD4"=>1,
+ "\u1CD5"=>220,
+ "\u1CD6"=>220,
+ "\u1CD7"=>220,
+ "\u1CD8"=>220,
+ "\u1CD9"=>220,
+ "\u1CDA"=>230,
+ "\u1CDB"=>230,
+ "\u1CDC"=>220,
+ "\u1CDD"=>220,
+ "\u1CDE"=>220,
+ "\u1CDF"=>220,
+ "\u1CE0"=>230,
+ "\u1CE2"=>1,
+ "\u1CE3"=>1,
+ "\u1CE4"=>1,
+ "\u1CE5"=>1,
+ "\u1CE6"=>1,
+ "\u1CE7"=>1,
+ "\u1CE8"=>1,
+ "\u1CED"=>220,
+ "\u1CF4"=>230,
+ "\u1CF8"=>230,
+ "\u1CF9"=>230,
+ "\u1DC0"=>230,
+ "\u1DC1"=>230,
+ "\u1DC2"=>220,
+ "\u1DC3"=>230,
+ "\u1DC4"=>230,
+ "\u1DC5"=>230,
+ "\u1DC6"=>230,
+ "\u1DC7"=>230,
+ "\u1DC8"=>230,
+ "\u1DC9"=>230,
+ "\u1DCA"=>220,
+ "\u1DCB"=>230,
+ "\u1DCC"=>230,
+ "\u1DCD"=>234,
+ "\u1DCE"=>214,
+ "\u1DCF"=>220,
+ "\u1DD0"=>202,
+ "\u1DD1"=>230,
+ "\u1DD2"=>230,
+ "\u1DD3"=>230,
+ "\u1DD4"=>230,
+ "\u1DD5"=>230,
+ "\u1DD6"=>230,
+ "\u1DD7"=>230,
+ "\u1DD8"=>230,
+ "\u1DD9"=>230,
+ "\u1DDA"=>230,
+ "\u1DDB"=>230,
+ "\u1DDC"=>230,
+ "\u1DDD"=>230,
+ "\u1DDE"=>230,
+ "\u1DDF"=>230,
+ "\u1DE0"=>230,
+ "\u1DE1"=>230,
+ "\u1DE2"=>230,
+ "\u1DE3"=>230,
+ "\u1DE4"=>230,
+ "\u1DE5"=>230,
+ "\u1DE6"=>230,
+ "\u1DE7"=>230,
+ "\u1DE8"=>230,
+ "\u1DE9"=>230,
+ "\u1DEA"=>230,
+ "\u1DEB"=>230,
+ "\u1DEC"=>230,
+ "\u1DED"=>230,
+ "\u1DEE"=>230,
+ "\u1DEF"=>230,
+ "\u1DF0"=>230,
+ "\u1DF1"=>230,
+ "\u1DF2"=>230,
+ "\u1DF3"=>230,
+ "\u1DF4"=>230,
+ "\u1DF5"=>230,
+ "\u1DF6"=>232,
+ "\u1DF7"=>228,
+ "\u1DF8"=>228,
+ "\u1DF9"=>220,
+ "\u1DFB"=>230,
+ "\u1DFC"=>233,
+ "\u1DFD"=>220,
+ "\u1DFE"=>230,
+ "\u1DFF"=>220,
+ "\u20D0"=>230,
+ "\u20D1"=>230,
+ "\u20D2"=>1,
+ "\u20D3"=>1,
+ "\u20D4"=>230,
+ "\u20D5"=>230,
+ "\u20D6"=>230,
+ "\u20D7"=>230,
+ "\u20D8"=>1,
+ "\u20D9"=>1,
+ "\u20DA"=>1,
+ "\u20DB"=>230,
+ "\u20DC"=>230,
+ "\u20E1"=>230,
+ "\u20E5"=>1,
+ "\u20E6"=>1,
+ "\u20E7"=>230,
+ "\u20E8"=>220,
+ "\u20E9"=>230,
+ "\u20EA"=>1,
+ "\u20EB"=>1,
+ "\u20EC"=>220,
+ "\u20ED"=>220,
+ "\u20EE"=>220,
+ "\u20EF"=>220,
+ "\u20F0"=>230,
+ "\u2CEF"=>230,
+ "\u2CF0"=>230,
+ "\u2CF1"=>230,
+ "\u2D7F"=>9,
+ "\u2DE0"=>230,
+ "\u2DE1"=>230,
+ "\u2DE2"=>230,
+ "\u2DE3"=>230,
+ "\u2DE4"=>230,
+ "\u2DE5"=>230,
+ "\u2DE6"=>230,
+ "\u2DE7"=>230,
+ "\u2DE8"=>230,
+ "\u2DE9"=>230,
+ "\u2DEA"=>230,
+ "\u2DEB"=>230,
+ "\u2DEC"=>230,
+ "\u2DED"=>230,
+ "\u2DEE"=>230,
+ "\u2DEF"=>230,
+ "\u2DF0"=>230,
+ "\u2DF1"=>230,
+ "\u2DF2"=>230,
+ "\u2DF3"=>230,
+ "\u2DF4"=>230,
+ "\u2DF5"=>230,
+ "\u2DF6"=>230,
+ "\u2DF7"=>230,
+ "\u2DF8"=>230,
+ "\u2DF9"=>230,
+ "\u2DFA"=>230,
+ "\u2DFB"=>230,
+ "\u2DFC"=>230,
+ "\u2DFD"=>230,
+ "\u2DFE"=>230,
+ "\u2DFF"=>230,
+ "\u302A"=>218,
+ "\u302B"=>228,
+ "\u302C"=>232,
+ "\u302D"=>222,
+ "\u302E"=>224,
+ "\u302F"=>224,
+ "\u3099"=>8,
+ "\u309A"=>8,
+ "\uA66F"=>230,
+ "\uA674"=>230,
+ "\uA675"=>230,
+ "\uA676"=>230,
+ "\uA677"=>230,
+ "\uA678"=>230,
+ "\uA679"=>230,
+ "\uA67A"=>230,
+ "\uA67B"=>230,
+ "\uA67C"=>230,
+ "\uA67D"=>230,
+ "\uA69E"=>230,
+ "\uA69F"=>230,
+ "\uA6F0"=>230,
+ "\uA6F1"=>230,
+ "\uA806"=>9,
+ "\uA8C4"=>9,
+ "\uA8E0"=>230,
+ "\uA8E1"=>230,
+ "\uA8E2"=>230,
+ "\uA8E3"=>230,
+ "\uA8E4"=>230,
+ "\uA8E5"=>230,
+ "\uA8E6"=>230,
+ "\uA8E7"=>230,
+ "\uA8E8"=>230,
+ "\uA8E9"=>230,
+ "\uA8EA"=>230,
+ "\uA8EB"=>230,
+ "\uA8EC"=>230,
+ "\uA8ED"=>230,
+ "\uA8EE"=>230,
+ "\uA8EF"=>230,
+ "\uA8F0"=>230,
+ "\uA8F1"=>230,
+ "\uA92B"=>220,
+ "\uA92C"=>220,
+ "\uA92D"=>220,
+ "\uA953"=>9,
+ "\uA9B3"=>7,
+ "\uA9C0"=>9,
+ "\uAAB0"=>230,
+ "\uAAB2"=>230,
+ "\uAAB3"=>230,
+ "\uAAB4"=>220,
+ "\uAAB7"=>230,
+ "\uAAB8"=>230,
+ "\uAABE"=>230,
+ "\uAABF"=>230,
+ "\uAAC1"=>230,
+ "\uAAF6"=>9,
+ "\uABED"=>9,
+ "\uFB1E"=>26,
+ "\uFE20"=>230,
+ "\uFE21"=>230,
+ "\uFE22"=>230,
+ "\uFE23"=>230,
+ "\uFE24"=>230,
+ "\uFE25"=>230,
+ "\uFE26"=>230,
+ "\uFE27"=>220,
+ "\uFE28"=>220,
+ "\uFE29"=>220,
+ "\uFE2A"=>220,
+ "\uFE2B"=>220,
+ "\uFE2C"=>220,
+ "\uFE2D"=>220,
+ "\uFE2E"=>230,
+ "\uFE2F"=>230,
+ "\u{101FD}"=>220,
+ "\u{102E0}"=>220,
+ "\u{10376}"=>230,
+ "\u{10377}"=>230,
+ "\u{10378}"=>230,
+ "\u{10379}"=>230,
+ "\u{1037A}"=>230,
+ "\u{10A0D}"=>220,
+ "\u{10A0F}"=>230,
+ "\u{10A38}"=>230,
+ "\u{10A39}"=>1,
+ "\u{10A3A}"=>220,
+ "\u{10A3F}"=>9,
+ "\u{10AE5}"=>230,
+ "\u{10AE6}"=>220,
+ "\u{10D24}"=>230,
+ "\u{10D25}"=>230,
+ "\u{10D26}"=>230,
+ "\u{10D27}"=>230,
+ "\u{10F46}"=>220,
+ "\u{10F47}"=>220,
+ "\u{10F48}"=>230,
+ "\u{10F49}"=>230,
+ "\u{10F4A}"=>230,
+ "\u{10F4B}"=>220,
+ "\u{10F4C}"=>230,
+ "\u{10F4D}"=>220,
+ "\u{10F4E}"=>220,
+ "\u{10F4F}"=>220,
+ "\u{10F50}"=>220,
+ "\u{11046}"=>9,
+ "\u{1107F}"=>9,
+ "\u{110B9}"=>9,
+ "\u{110BA}"=>7,
+ "\u{11100}"=>230,
+ "\u{11101}"=>230,
+ "\u{11102}"=>230,
+ "\u{11133}"=>9,
+ "\u{11134}"=>9,
+ "\u{11173}"=>7,
+ "\u{111C0}"=>9,
+ "\u{111CA}"=>7,
+ "\u{11235}"=>9,
+ "\u{11236}"=>7,
+ "\u{112E9}"=>7,
+ "\u{112EA}"=>9,
+ "\u{1133B}"=>7,
+ "\u{1133C}"=>7,
+ "\u{1134D}"=>9,
+ "\u{11366}"=>230,
+ "\u{11367}"=>230,
+ "\u{11368}"=>230,
+ "\u{11369}"=>230,
+ "\u{1136A}"=>230,
+ "\u{1136B}"=>230,
+ "\u{1136C}"=>230,
+ "\u{11370}"=>230,
+ "\u{11371}"=>230,
+ "\u{11372}"=>230,
+ "\u{11373}"=>230,
+ "\u{11374}"=>230,
+ "\u{11442}"=>9,
+ "\u{11446}"=>7,
+ "\u{1145E}"=>230,
+ "\u{114C2}"=>9,
+ "\u{114C3}"=>7,
+ "\u{115BF}"=>9,
+ "\u{115C0}"=>7,
+ "\u{1163F}"=>9,
+ "\u{116B6}"=>9,
+ "\u{116B7}"=>7,
+ "\u{1172B}"=>9,
+ "\u{11839}"=>9,
+ "\u{1183A}"=>7,
+ "\u{119E0}"=>9,
+ "\u{11A34}"=>9,
+ "\u{11A47}"=>9,
+ "\u{11A99}"=>9,
+ "\u{11C3F}"=>9,
+ "\u{11D42}"=>7,
+ "\u{11D44}"=>9,
+ "\u{11D45}"=>9,
+ "\u{11D97}"=>9,
+ "\u{16AF0}"=>1,
+ "\u{16AF1}"=>1,
+ "\u{16AF2}"=>1,
+ "\u{16AF3}"=>1,
+ "\u{16AF4}"=>1,
+ "\u{16B30}"=>230,
+ "\u{16B31}"=>230,
+ "\u{16B32}"=>230,
+ "\u{16B33}"=>230,
+ "\u{16B34}"=>230,
+ "\u{16B35}"=>230,
+ "\u{16B36}"=>230,
+ "\u{1BC9E}"=>1,
+ "\u{1D165}"=>216,
+ "\u{1D166}"=>216,
+ "\u{1D167}"=>1,
+ "\u{1D168}"=>1,
+ "\u{1D169}"=>1,
+ "\u{1D16D}"=>226,
+ "\u{1D16E}"=>216,
+ "\u{1D16F}"=>216,
+ "\u{1D170}"=>216,
+ "\u{1D171}"=>216,
+ "\u{1D172}"=>216,
+ "\u{1D17B}"=>220,
+ "\u{1D17C}"=>220,
+ "\u{1D17D}"=>220,
+ "\u{1D17E}"=>220,
+ "\u{1D17F}"=>220,
+ "\u{1D180}"=>220,
+ "\u{1D181}"=>220,
+ "\u{1D182}"=>220,
+ "\u{1D185}"=>230,
+ "\u{1D186}"=>230,
+ "\u{1D187}"=>230,
+ "\u{1D188}"=>230,
+ "\u{1D189}"=>230,
+ "\u{1D18A}"=>220,
+ "\u{1D18B}"=>220,
+ "\u{1D1AA}"=>230,
+ "\u{1D1AB}"=>230,
+ "\u{1D1AC}"=>230,
+ "\u{1D1AD}"=>230,
+ "\u{1D242}"=>230,
+ "\u{1D243}"=>230,
+ "\u{1D244}"=>230,
+ "\u{1E000}"=>230,
+ "\u{1E001}"=>230,
+ "\u{1E002}"=>230,
+ "\u{1E003}"=>230,
+ "\u{1E004}"=>230,
+ "\u{1E005}"=>230,
+ "\u{1E006}"=>230,
+ "\u{1E008}"=>230,
+ "\u{1E009}"=>230,
+ "\u{1E00A}"=>230,
+ "\u{1E00B}"=>230,
+ "\u{1E00C}"=>230,
+ "\u{1E00D}"=>230,
+ "\u{1E00E}"=>230,
+ "\u{1E00F}"=>230,
+ "\u{1E010}"=>230,
+ "\u{1E011}"=>230,
+ "\u{1E012}"=>230,
+ "\u{1E013}"=>230,
+ "\u{1E014}"=>230,
+ "\u{1E015}"=>230,
+ "\u{1E016}"=>230,
+ "\u{1E017}"=>230,
+ "\u{1E018}"=>230,
+ "\u{1E01B}"=>230,
+ "\u{1E01C}"=>230,
+ "\u{1E01D}"=>230,
+ "\u{1E01E}"=>230,
+ "\u{1E01F}"=>230,
+ "\u{1E020}"=>230,
+ "\u{1E021}"=>230,
+ "\u{1E023}"=>230,
+ "\u{1E024}"=>230,
+ "\u{1E026}"=>230,
+ "\u{1E027}"=>230,
+ "\u{1E028}"=>230,
+ "\u{1E029}"=>230,
+ "\u{1E02A}"=>230,
+ "\u{1E130}"=>230,
+ "\u{1E131}"=>230,
+ "\u{1E132}"=>230,
+ "\u{1E133}"=>230,
+ "\u{1E134}"=>230,
+ "\u{1E135}"=>230,
+ "\u{1E136}"=>230,
+ "\u{1E2EC}"=>230,
+ "\u{1E2ED}"=>230,
+ "\u{1E2EE}"=>230,
+ "\u{1E2EF}"=>230,
+ "\u{1E8D0}"=>220,
+ "\u{1E8D1}"=>220,
+ "\u{1E8D2}"=>220,
+ "\u{1E8D3}"=>220,
+ "\u{1E8D4}"=>220,
+ "\u{1E8D5}"=>220,
+ "\u{1E8D6}"=>220,
+ "\u{1E944}"=>230,
+ "\u{1E945}"=>230,
+ "\u{1E946}"=>230,
+ "\u{1E947}"=>230,
+ "\u{1E948}"=>230,
+ "\u{1E949}"=>230,
+ "\u{1E94A}"=>7,
}
class_table.default = 0
CLASS_TABLE = class_table.freeze
DECOMPOSITION_TABLE = {
- "\u00C0"=>"A\u0300", "\u00C1"=>"A\u0301", "\u00C2"=>"A\u0302", "\u00C3"=>"A\u0303", "\u00C4"=>"A\u0308", "\u00C5"=>"A\u030A", "\u00C7"=>"C\u0327", "\u00C8"=>"E\u0300",
- "\u00C9"=>"E\u0301", "\u00CA"=>"E\u0302", "\u00CB"=>"E\u0308", "\u00CC"=>"I\u0300", "\u00CD"=>"I\u0301", "\u00CE"=>"I\u0302", "\u00CF"=>"I\u0308", "\u00D1"=>"N\u0303",
- "\u00D2"=>"O\u0300", "\u00D3"=>"O\u0301", "\u00D4"=>"O\u0302", "\u00D5"=>"O\u0303", "\u00D6"=>"O\u0308", "\u00D9"=>"U\u0300", "\u00DA"=>"U\u0301", "\u00DB"=>"U\u0302",
- "\u00DC"=>"U\u0308", "\u00DD"=>"Y\u0301", "\u00E0"=>"a\u0300", "\u00E1"=>"a\u0301", "\u00E2"=>"a\u0302", "\u00E3"=>"a\u0303", "\u00E4"=>"a\u0308", "\u00E5"=>"a\u030A",
- "\u00E7"=>"c\u0327", "\u00E8"=>"e\u0300", "\u00E9"=>"e\u0301", "\u00EA"=>"e\u0302", "\u00EB"=>"e\u0308", "\u00EC"=>"i\u0300", "\u00ED"=>"i\u0301", "\u00EE"=>"i\u0302",
- "\u00EF"=>"i\u0308", "\u00F1"=>"n\u0303", "\u00F2"=>"o\u0300", "\u00F3"=>"o\u0301", "\u00F4"=>"o\u0302", "\u00F5"=>"o\u0303", "\u00F6"=>"o\u0308", "\u00F9"=>"u\u0300",
- "\u00FA"=>"u\u0301", "\u00FB"=>"u\u0302", "\u00FC"=>"u\u0308", "\u00FD"=>"y\u0301", "\u00FF"=>"y\u0308", "\u0100"=>"A\u0304", "\u0101"=>"a\u0304", "\u0102"=>"A\u0306",
- "\u0103"=>"a\u0306", "\u0104"=>"A\u0328", "\u0105"=>"a\u0328", "\u0106"=>"C\u0301", "\u0107"=>"c\u0301", "\u0108"=>"C\u0302", "\u0109"=>"c\u0302", "\u010A"=>"C\u0307",
- "\u010B"=>"c\u0307", "\u010C"=>"C\u030C", "\u010D"=>"c\u030C", "\u010E"=>"D\u030C", "\u010F"=>"d\u030C", "\u0112"=>"E\u0304", "\u0113"=>"e\u0304", "\u0114"=>"E\u0306",
- "\u0115"=>"e\u0306", "\u0116"=>"E\u0307", "\u0117"=>"e\u0307", "\u0118"=>"E\u0328", "\u0119"=>"e\u0328", "\u011A"=>"E\u030C", "\u011B"=>"e\u030C", "\u011C"=>"G\u0302",
- "\u011D"=>"g\u0302", "\u011E"=>"G\u0306", "\u011F"=>"g\u0306", "\u0120"=>"G\u0307", "\u0121"=>"g\u0307", "\u0122"=>"G\u0327", "\u0123"=>"g\u0327", "\u0124"=>"H\u0302",
- "\u0125"=>"h\u0302", "\u0128"=>"I\u0303", "\u0129"=>"i\u0303", "\u012A"=>"I\u0304", "\u012B"=>"i\u0304", "\u012C"=>"I\u0306", "\u012D"=>"i\u0306", "\u012E"=>"I\u0328",
- "\u012F"=>"i\u0328", "\u0130"=>"I\u0307", "\u0134"=>"J\u0302", "\u0135"=>"j\u0302", "\u0136"=>"K\u0327", "\u0137"=>"k\u0327", "\u0139"=>"L\u0301", "\u013A"=>"l\u0301",
- "\u013B"=>"L\u0327", "\u013C"=>"l\u0327", "\u013D"=>"L\u030C", "\u013E"=>"l\u030C", "\u0143"=>"N\u0301", "\u0144"=>"n\u0301", "\u0145"=>"N\u0327", "\u0146"=>"n\u0327",
- "\u0147"=>"N\u030C", "\u0148"=>"n\u030C", "\u014C"=>"O\u0304", "\u014D"=>"o\u0304", "\u014E"=>"O\u0306", "\u014F"=>"o\u0306", "\u0150"=>"O\u030B", "\u0151"=>"o\u030B",
- "\u0154"=>"R\u0301", "\u0155"=>"r\u0301", "\u0156"=>"R\u0327", "\u0157"=>"r\u0327", "\u0158"=>"R\u030C", "\u0159"=>"r\u030C", "\u015A"=>"S\u0301", "\u015B"=>"s\u0301",
- "\u015C"=>"S\u0302", "\u015D"=>"s\u0302", "\u015E"=>"S\u0327", "\u015F"=>"s\u0327", "\u0160"=>"S\u030C", "\u0161"=>"s\u030C", "\u0162"=>"T\u0327", "\u0163"=>"t\u0327",
- "\u0164"=>"T\u030C", "\u0165"=>"t\u030C", "\u0168"=>"U\u0303", "\u0169"=>"u\u0303", "\u016A"=>"U\u0304", "\u016B"=>"u\u0304", "\u016C"=>"U\u0306", "\u016D"=>"u\u0306",
- "\u016E"=>"U\u030A", "\u016F"=>"u\u030A", "\u0170"=>"U\u030B", "\u0171"=>"u\u030B", "\u0172"=>"U\u0328", "\u0173"=>"u\u0328", "\u0174"=>"W\u0302", "\u0175"=>"w\u0302",
- "\u0176"=>"Y\u0302", "\u0177"=>"y\u0302", "\u0178"=>"Y\u0308", "\u0179"=>"Z\u0301", "\u017A"=>"z\u0301", "\u017B"=>"Z\u0307", "\u017C"=>"z\u0307", "\u017D"=>"Z\u030C",
- "\u017E"=>"z\u030C", "\u01A0"=>"O\u031B", "\u01A1"=>"o\u031B", "\u01AF"=>"U\u031B", "\u01B0"=>"u\u031B", "\u01CD"=>"A\u030C", "\u01CE"=>"a\u030C", "\u01CF"=>"I\u030C",
- "\u01D0"=>"i\u030C", "\u01D1"=>"O\u030C", "\u01D2"=>"o\u030C", "\u01D3"=>"U\u030C", "\u01D4"=>"u\u030C", "\u01D5"=>"U\u0308\u0304", "\u01D6"=>"u\u0308\u0304", "\u01D7"=>"U\u0308\u0301",
- "\u01D8"=>"u\u0308\u0301", "\u01D9"=>"U\u0308\u030C", "\u01DA"=>"u\u0308\u030C", "\u01DB"=>"U\u0308\u0300", "\u01DC"=>"u\u0308\u0300", "\u01DE"=>"A\u0308\u0304", "\u01DF"=>"a\u0308\u0304", "\u01E0"=>"A\u0307\u0304",
- "\u01E1"=>"a\u0307\u0304", "\u01E2"=>"\u00C6\u0304", "\u01E3"=>"\u00E6\u0304", "\u01E6"=>"G\u030C", "\u01E7"=>"g\u030C", "\u01E8"=>"K\u030C", "\u01E9"=>"k\u030C", "\u01EA"=>"O\u0328",
- "\u01EB"=>"o\u0328", "\u01EC"=>"O\u0328\u0304", "\u01ED"=>"o\u0328\u0304", "\u01EE"=>"\u01B7\u030C", "\u01EF"=>"\u0292\u030C", "\u01F0"=>"j\u030C", "\u01F4"=>"G\u0301", "\u01F5"=>"g\u0301",
- "\u01F8"=>"N\u0300", "\u01F9"=>"n\u0300", "\u01FA"=>"A\u030A\u0301", "\u01FB"=>"a\u030A\u0301", "\u01FC"=>"\u00C6\u0301", "\u01FD"=>"\u00E6\u0301", "\u01FE"=>"\u00D8\u0301", "\u01FF"=>"\u00F8\u0301",
- "\u0200"=>"A\u030F", "\u0201"=>"a\u030F", "\u0202"=>"A\u0311", "\u0203"=>"a\u0311", "\u0204"=>"E\u030F", "\u0205"=>"e\u030F", "\u0206"=>"E\u0311", "\u0207"=>"e\u0311",
- "\u0208"=>"I\u030F", "\u0209"=>"i\u030F", "\u020A"=>"I\u0311", "\u020B"=>"i\u0311", "\u020C"=>"O\u030F", "\u020D"=>"o\u030F", "\u020E"=>"O\u0311", "\u020F"=>"o\u0311",
- "\u0210"=>"R\u030F", "\u0211"=>"r\u030F", "\u0212"=>"R\u0311", "\u0213"=>"r\u0311", "\u0214"=>"U\u030F", "\u0215"=>"u\u030F", "\u0216"=>"U\u0311", "\u0217"=>"u\u0311",
- "\u0218"=>"S\u0326", "\u0219"=>"s\u0326", "\u021A"=>"T\u0326", "\u021B"=>"t\u0326", "\u021E"=>"H\u030C", "\u021F"=>"h\u030C", "\u0226"=>"A\u0307", "\u0227"=>"a\u0307",
- "\u0228"=>"E\u0327", "\u0229"=>"e\u0327", "\u022A"=>"O\u0308\u0304", "\u022B"=>"o\u0308\u0304", "\u022C"=>"O\u0303\u0304", "\u022D"=>"o\u0303\u0304", "\u022E"=>"O\u0307", "\u022F"=>"o\u0307",
- "\u0230"=>"O\u0307\u0304", "\u0231"=>"o\u0307\u0304", "\u0232"=>"Y\u0304", "\u0233"=>"y\u0304", "\u0340"=>"\u0300", "\u0341"=>"\u0301", "\u0343"=>"\u0313", "\u0344"=>"\u0308\u0301",
- "\u0374"=>"\u02B9", "\u037E"=>";", "\u0385"=>"\u00A8\u0301", "\u0386"=>"\u0391\u0301", "\u0387"=>"\u00B7", "\u0388"=>"\u0395\u0301", "\u0389"=>"\u0397\u0301", "\u038A"=>"\u0399\u0301",
- "\u038C"=>"\u039F\u0301", "\u038E"=>"\u03A5\u0301", "\u038F"=>"\u03A9\u0301", "\u0390"=>"\u03B9\u0308\u0301", "\u03AA"=>"\u0399\u0308", "\u03AB"=>"\u03A5\u0308", "\u03AC"=>"\u03B1\u0301", "\u03AD"=>"\u03B5\u0301",
- "\u03AE"=>"\u03B7\u0301", "\u03AF"=>"\u03B9\u0301", "\u03B0"=>"\u03C5\u0308\u0301", "\u03CA"=>"\u03B9\u0308", "\u03CB"=>"\u03C5\u0308", "\u03CC"=>"\u03BF\u0301", "\u03CD"=>"\u03C5\u0301", "\u03CE"=>"\u03C9\u0301",
- "\u03D3"=>"\u03D2\u0301", "\u03D4"=>"\u03D2\u0308", "\u0400"=>"\u0415\u0300", "\u0401"=>"\u0415\u0308", "\u0403"=>"\u0413\u0301", "\u0407"=>"\u0406\u0308", "\u040C"=>"\u041A\u0301", "\u040D"=>"\u0418\u0300",
- "\u040E"=>"\u0423\u0306", "\u0419"=>"\u0418\u0306", "\u0439"=>"\u0438\u0306", "\u0450"=>"\u0435\u0300", "\u0451"=>"\u0435\u0308", "\u0453"=>"\u0433\u0301", "\u0457"=>"\u0456\u0308", "\u045C"=>"\u043A\u0301",
- "\u045D"=>"\u0438\u0300", "\u045E"=>"\u0443\u0306", "\u0476"=>"\u0474\u030F", "\u0477"=>"\u0475\u030F", "\u04C1"=>"\u0416\u0306", "\u04C2"=>"\u0436\u0306", "\u04D0"=>"\u0410\u0306", "\u04D1"=>"\u0430\u0306",
- "\u04D2"=>"\u0410\u0308", "\u04D3"=>"\u0430\u0308", "\u04D6"=>"\u0415\u0306", "\u04D7"=>"\u0435\u0306", "\u04DA"=>"\u04D8\u0308", "\u04DB"=>"\u04D9\u0308", "\u04DC"=>"\u0416\u0308", "\u04DD"=>"\u0436\u0308",
- "\u04DE"=>"\u0417\u0308", "\u04DF"=>"\u0437\u0308", "\u04E2"=>"\u0418\u0304", "\u04E3"=>"\u0438\u0304", "\u04E4"=>"\u0418\u0308", "\u04E5"=>"\u0438\u0308", "\u04E6"=>"\u041E\u0308", "\u04E7"=>"\u043E\u0308",
- "\u04EA"=>"\u04E8\u0308", "\u04EB"=>"\u04E9\u0308", "\u04EC"=>"\u042D\u0308", "\u04ED"=>"\u044D\u0308", "\u04EE"=>"\u0423\u0304", "\u04EF"=>"\u0443\u0304", "\u04F0"=>"\u0423\u0308", "\u04F1"=>"\u0443\u0308",
- "\u04F2"=>"\u0423\u030B", "\u04F3"=>"\u0443\u030B", "\u04F4"=>"\u0427\u0308", "\u04F5"=>"\u0447\u0308", "\u04F8"=>"\u042B\u0308", "\u04F9"=>"\u044B\u0308", "\u0622"=>"\u0627\u0653", "\u0623"=>"\u0627\u0654",
- "\u0624"=>"\u0648\u0654", "\u0625"=>"\u0627\u0655", "\u0626"=>"\u064A\u0654", "\u06C0"=>"\u06D5\u0654", "\u06C2"=>"\u06C1\u0654", "\u06D3"=>"\u06D2\u0654", "\u0929"=>"\u0928\u093C", "\u0931"=>"\u0930\u093C",
- "\u0934"=>"\u0933\u093C", "\u0958"=>"\u0915\u093C", "\u0959"=>"\u0916\u093C", "\u095A"=>"\u0917\u093C", "\u095B"=>"\u091C\u093C", "\u095C"=>"\u0921\u093C", "\u095D"=>"\u0922\u093C", "\u095E"=>"\u092B\u093C",
- "\u095F"=>"\u092F\u093C", "\u09CB"=>"\u09C7\u09BE", "\u09CC"=>"\u09C7\u09D7", "\u09DC"=>"\u09A1\u09BC", "\u09DD"=>"\u09A2\u09BC", "\u09DF"=>"\u09AF\u09BC", "\u0A33"=>"\u0A32\u0A3C", "\u0A36"=>"\u0A38\u0A3C",
- "\u0A59"=>"\u0A16\u0A3C", "\u0A5A"=>"\u0A17\u0A3C", "\u0A5B"=>"\u0A1C\u0A3C", "\u0A5E"=>"\u0A2B\u0A3C", "\u0B48"=>"\u0B47\u0B56", "\u0B4B"=>"\u0B47\u0B3E", "\u0B4C"=>"\u0B47\u0B57", "\u0B5C"=>"\u0B21\u0B3C",
- "\u0B5D"=>"\u0B22\u0B3C", "\u0B94"=>"\u0B92\u0BD7", "\u0BCA"=>"\u0BC6\u0BBE", "\u0BCB"=>"\u0BC7\u0BBE", "\u0BCC"=>"\u0BC6\u0BD7", "\u0C48"=>"\u0C46\u0C56", "\u0CC0"=>"\u0CBF\u0CD5", "\u0CC7"=>"\u0CC6\u0CD5",
- "\u0CC8"=>"\u0CC6\u0CD6", "\u0CCA"=>"\u0CC6\u0CC2", "\u0CCB"=>"\u0CC6\u0CC2\u0CD5", "\u0D4A"=>"\u0D46\u0D3E", "\u0D4B"=>"\u0D47\u0D3E", "\u0D4C"=>"\u0D46\u0D57", "\u0DDA"=>"\u0DD9\u0DCA", "\u0DDC"=>"\u0DD9\u0DCF",
- "\u0DDD"=>"\u0DD9\u0DCF\u0DCA", "\u0DDE"=>"\u0DD9\u0DDF", "\u0F43"=>"\u0F42\u0FB7", "\u0F4D"=>"\u0F4C\u0FB7", "\u0F52"=>"\u0F51\u0FB7", "\u0F57"=>"\u0F56\u0FB7", "\u0F5C"=>"\u0F5B\u0FB7", "\u0F69"=>"\u0F40\u0FB5",
- "\u0F73"=>"\u0F71\u0F72", "\u0F75"=>"\u0F71\u0F74", "\u0F76"=>"\u0FB2\u0F80", "\u0F78"=>"\u0FB3\u0F80", "\u0F81"=>"\u0F71\u0F80", "\u0F93"=>"\u0F92\u0FB7", "\u0F9D"=>"\u0F9C\u0FB7", "\u0FA2"=>"\u0FA1\u0FB7",
- "\u0FA7"=>"\u0FA6\u0FB7", "\u0FAC"=>"\u0FAB\u0FB7", "\u0FB9"=>"\u0F90\u0FB5", "\u1026"=>"\u1025\u102E", "\u1B06"=>"\u1B05\u1B35", "\u1B08"=>"\u1B07\u1B35", "\u1B0A"=>"\u1B09\u1B35", "\u1B0C"=>"\u1B0B\u1B35",
- "\u1B0E"=>"\u1B0D\u1B35", "\u1B12"=>"\u1B11\u1B35", "\u1B3B"=>"\u1B3A\u1B35", "\u1B3D"=>"\u1B3C\u1B35", "\u1B40"=>"\u1B3E\u1B35", "\u1B41"=>"\u1B3F\u1B35", "\u1B43"=>"\u1B42\u1B35", "\u1E00"=>"A\u0325",
- "\u1E01"=>"a\u0325", "\u1E02"=>"B\u0307", "\u1E03"=>"b\u0307", "\u1E04"=>"B\u0323", "\u1E05"=>"b\u0323", "\u1E06"=>"B\u0331", "\u1E07"=>"b\u0331", "\u1E08"=>"C\u0327\u0301",
- "\u1E09"=>"c\u0327\u0301", "\u1E0A"=>"D\u0307", "\u1E0B"=>"d\u0307", "\u1E0C"=>"D\u0323", "\u1E0D"=>"d\u0323", "\u1E0E"=>"D\u0331", "\u1E0F"=>"d\u0331", "\u1E10"=>"D\u0327",
- "\u1E11"=>"d\u0327", "\u1E12"=>"D\u032D", "\u1E13"=>"d\u032D", "\u1E14"=>"E\u0304\u0300", "\u1E15"=>"e\u0304\u0300", "\u1E16"=>"E\u0304\u0301", "\u1E17"=>"e\u0304\u0301", "\u1E18"=>"E\u032D",
- "\u1E19"=>"e\u032D", "\u1E1A"=>"E\u0330", "\u1E1B"=>"e\u0330", "\u1E1C"=>"E\u0327\u0306", "\u1E1D"=>"e\u0327\u0306", "\u1E1E"=>"F\u0307", "\u1E1F"=>"f\u0307", "\u1E20"=>"G\u0304",
- "\u1E21"=>"g\u0304", "\u1E22"=>"H\u0307", "\u1E23"=>"h\u0307", "\u1E24"=>"H\u0323", "\u1E25"=>"h\u0323", "\u1E26"=>"H\u0308", "\u1E27"=>"h\u0308", "\u1E28"=>"H\u0327",
- "\u1E29"=>"h\u0327", "\u1E2A"=>"H\u032E", "\u1E2B"=>"h\u032E", "\u1E2C"=>"I\u0330", "\u1E2D"=>"i\u0330", "\u1E2E"=>"I\u0308\u0301", "\u1E2F"=>"i\u0308\u0301", "\u1E30"=>"K\u0301",
- "\u1E31"=>"k\u0301", "\u1E32"=>"K\u0323", "\u1E33"=>"k\u0323", "\u1E34"=>"K\u0331", "\u1E35"=>"k\u0331", "\u1E36"=>"L\u0323", "\u1E37"=>"l\u0323", "\u1E38"=>"L\u0323\u0304",
- "\u1E39"=>"l\u0323\u0304", "\u1E3A"=>"L\u0331", "\u1E3B"=>"l\u0331", "\u1E3C"=>"L\u032D", "\u1E3D"=>"l\u032D", "\u1E3E"=>"M\u0301", "\u1E3F"=>"m\u0301", "\u1E40"=>"M\u0307",
- "\u1E41"=>"m\u0307", "\u1E42"=>"M\u0323", "\u1E43"=>"m\u0323", "\u1E44"=>"N\u0307", "\u1E45"=>"n\u0307", "\u1E46"=>"N\u0323", "\u1E47"=>"n\u0323", "\u1E48"=>"N\u0331",
- "\u1E49"=>"n\u0331", "\u1E4A"=>"N\u032D", "\u1E4B"=>"n\u032D", "\u1E4C"=>"O\u0303\u0301", "\u1E4D"=>"o\u0303\u0301", "\u1E4E"=>"O\u0303\u0308", "\u1E4F"=>"o\u0303\u0308", "\u1E50"=>"O\u0304\u0300",
- "\u1E51"=>"o\u0304\u0300", "\u1E52"=>"O\u0304\u0301", "\u1E53"=>"o\u0304\u0301", "\u1E54"=>"P\u0301", "\u1E55"=>"p\u0301", "\u1E56"=>"P\u0307", "\u1E57"=>"p\u0307", "\u1E58"=>"R\u0307",
- "\u1E59"=>"r\u0307", "\u1E5A"=>"R\u0323", "\u1E5B"=>"r\u0323", "\u1E5C"=>"R\u0323\u0304", "\u1E5D"=>"r\u0323\u0304", "\u1E5E"=>"R\u0331", "\u1E5F"=>"r\u0331", "\u1E60"=>"S\u0307",
- "\u1E61"=>"s\u0307", "\u1E62"=>"S\u0323", "\u1E63"=>"s\u0323", "\u1E64"=>"S\u0301\u0307", "\u1E65"=>"s\u0301\u0307", "\u1E66"=>"S\u030C\u0307", "\u1E67"=>"s\u030C\u0307", "\u1E68"=>"S\u0323\u0307",
- "\u1E69"=>"s\u0323\u0307", "\u1E6A"=>"T\u0307", "\u1E6B"=>"t\u0307", "\u1E6C"=>"T\u0323", "\u1E6D"=>"t\u0323", "\u1E6E"=>"T\u0331", "\u1E6F"=>"t\u0331", "\u1E70"=>"T\u032D",
- "\u1E71"=>"t\u032D", "\u1E72"=>"U\u0324", "\u1E73"=>"u\u0324", "\u1E74"=>"U\u0330", "\u1E75"=>"u\u0330", "\u1E76"=>"U\u032D", "\u1E77"=>"u\u032D", "\u1E78"=>"U\u0303\u0301",
- "\u1E79"=>"u\u0303\u0301", "\u1E7A"=>"U\u0304\u0308", "\u1E7B"=>"u\u0304\u0308", "\u1E7C"=>"V\u0303", "\u1E7D"=>"v\u0303", "\u1E7E"=>"V\u0323", "\u1E7F"=>"v\u0323", "\u1E80"=>"W\u0300",
- "\u1E81"=>"w\u0300", "\u1E82"=>"W\u0301", "\u1E83"=>"w\u0301", "\u1E84"=>"W\u0308", "\u1E85"=>"w\u0308", "\u1E86"=>"W\u0307", "\u1E87"=>"w\u0307", "\u1E88"=>"W\u0323",
- "\u1E89"=>"w\u0323", "\u1E8A"=>"X\u0307", "\u1E8B"=>"x\u0307", "\u1E8C"=>"X\u0308", "\u1E8D"=>"x\u0308", "\u1E8E"=>"Y\u0307", "\u1E8F"=>"y\u0307", "\u1E90"=>"Z\u0302",
- "\u1E91"=>"z\u0302", "\u1E92"=>"Z\u0323", "\u1E93"=>"z\u0323", "\u1E94"=>"Z\u0331", "\u1E95"=>"z\u0331", "\u1E96"=>"h\u0331", "\u1E97"=>"t\u0308", "\u1E98"=>"w\u030A",
- "\u1E99"=>"y\u030A", "\u1E9B"=>"\u017F\u0307", "\u1EA0"=>"A\u0323", "\u1EA1"=>"a\u0323", "\u1EA2"=>"A\u0309", "\u1EA3"=>"a\u0309", "\u1EA4"=>"A\u0302\u0301", "\u1EA5"=>"a\u0302\u0301",
- "\u1EA6"=>"A\u0302\u0300", "\u1EA7"=>"a\u0302\u0300", "\u1EA8"=>"A\u0302\u0309", "\u1EA9"=>"a\u0302\u0309", "\u1EAA"=>"A\u0302\u0303", "\u1EAB"=>"a\u0302\u0303", "\u1EAC"=>"A\u0323\u0302", "\u1EAD"=>"a\u0323\u0302",
- "\u1EAE"=>"A\u0306\u0301", "\u1EAF"=>"a\u0306\u0301", "\u1EB0"=>"A\u0306\u0300", "\u1EB1"=>"a\u0306\u0300", "\u1EB2"=>"A\u0306\u0309", "\u1EB3"=>"a\u0306\u0309", "\u1EB4"=>"A\u0306\u0303", "\u1EB5"=>"a\u0306\u0303",
- "\u1EB6"=>"A\u0323\u0306", "\u1EB7"=>"a\u0323\u0306", "\u1EB8"=>"E\u0323", "\u1EB9"=>"e\u0323", "\u1EBA"=>"E\u0309", "\u1EBB"=>"e\u0309", "\u1EBC"=>"E\u0303", "\u1EBD"=>"e\u0303",
- "\u1EBE"=>"E\u0302\u0301", "\u1EBF"=>"e\u0302\u0301", "\u1EC0"=>"E\u0302\u0300", "\u1EC1"=>"e\u0302\u0300", "\u1EC2"=>"E\u0302\u0309", "\u1EC3"=>"e\u0302\u0309", "\u1EC4"=>"E\u0302\u0303", "\u1EC5"=>"e\u0302\u0303",
- "\u1EC6"=>"E\u0323\u0302", "\u1EC7"=>"e\u0323\u0302", "\u1EC8"=>"I\u0309", "\u1EC9"=>"i\u0309", "\u1ECA"=>"I\u0323", "\u1ECB"=>"i\u0323", "\u1ECC"=>"O\u0323", "\u1ECD"=>"o\u0323",
- "\u1ECE"=>"O\u0309", "\u1ECF"=>"o\u0309", "\u1ED0"=>"O\u0302\u0301", "\u1ED1"=>"o\u0302\u0301", "\u1ED2"=>"O\u0302\u0300", "\u1ED3"=>"o\u0302\u0300", "\u1ED4"=>"O\u0302\u0309", "\u1ED5"=>"o\u0302\u0309",
- "\u1ED6"=>"O\u0302\u0303", "\u1ED7"=>"o\u0302\u0303", "\u1ED8"=>"O\u0323\u0302", "\u1ED9"=>"o\u0323\u0302", "\u1EDA"=>"O\u031B\u0301", "\u1EDB"=>"o\u031B\u0301", "\u1EDC"=>"O\u031B\u0300", "\u1EDD"=>"o\u031B\u0300",
- "\u1EDE"=>"O\u031B\u0309", "\u1EDF"=>"o\u031B\u0309", "\u1EE0"=>"O\u031B\u0303", "\u1EE1"=>"o\u031B\u0303", "\u1EE2"=>"O\u031B\u0323", "\u1EE3"=>"o\u031B\u0323", "\u1EE4"=>"U\u0323", "\u1EE5"=>"u\u0323",
- "\u1EE6"=>"U\u0309", "\u1EE7"=>"u\u0309", "\u1EE8"=>"U\u031B\u0301", "\u1EE9"=>"u\u031B\u0301", "\u1EEA"=>"U\u031B\u0300", "\u1EEB"=>"u\u031B\u0300", "\u1EEC"=>"U\u031B\u0309", "\u1EED"=>"u\u031B\u0309",
- "\u1EEE"=>"U\u031B\u0303", "\u1EEF"=>"u\u031B\u0303", "\u1EF0"=>"U\u031B\u0323", "\u1EF1"=>"u\u031B\u0323", "\u1EF2"=>"Y\u0300", "\u1EF3"=>"y\u0300", "\u1EF4"=>"Y\u0323", "\u1EF5"=>"y\u0323",
- "\u1EF6"=>"Y\u0309", "\u1EF7"=>"y\u0309", "\u1EF8"=>"Y\u0303", "\u1EF9"=>"y\u0303", "\u1F00"=>"\u03B1\u0313", "\u1F01"=>"\u03B1\u0314", "\u1F02"=>"\u03B1\u0313\u0300", "\u1F03"=>"\u03B1\u0314\u0300",
- "\u1F04"=>"\u03B1\u0313\u0301", "\u1F05"=>"\u03B1\u0314\u0301", "\u1F06"=>"\u03B1\u0313\u0342", "\u1F07"=>"\u03B1\u0314\u0342", "\u1F08"=>"\u0391\u0313", "\u1F09"=>"\u0391\u0314", "\u1F0A"=>"\u0391\u0313\u0300", "\u1F0B"=>"\u0391\u0314\u0300",
- "\u1F0C"=>"\u0391\u0313\u0301", "\u1F0D"=>"\u0391\u0314\u0301", "\u1F0E"=>"\u0391\u0313\u0342", "\u1F0F"=>"\u0391\u0314\u0342", "\u1F10"=>"\u03B5\u0313", "\u1F11"=>"\u03B5\u0314", "\u1F12"=>"\u03B5\u0313\u0300", "\u1F13"=>"\u03B5\u0314\u0300",
- "\u1F14"=>"\u03B5\u0313\u0301", "\u1F15"=>"\u03B5\u0314\u0301", "\u1F18"=>"\u0395\u0313", "\u1F19"=>"\u0395\u0314", "\u1F1A"=>"\u0395\u0313\u0300", "\u1F1B"=>"\u0395\u0314\u0300", "\u1F1C"=>"\u0395\u0313\u0301", "\u1F1D"=>"\u0395\u0314\u0301",
- "\u1F20"=>"\u03B7\u0313", "\u1F21"=>"\u03B7\u0314", "\u1F22"=>"\u03B7\u0313\u0300", "\u1F23"=>"\u03B7\u0314\u0300", "\u1F24"=>"\u03B7\u0313\u0301", "\u1F25"=>"\u03B7\u0314\u0301", "\u1F26"=>"\u03B7\u0313\u0342", "\u1F27"=>"\u03B7\u0314\u0342",
- "\u1F28"=>"\u0397\u0313", "\u1F29"=>"\u0397\u0314", "\u1F2A"=>"\u0397\u0313\u0300", "\u1F2B"=>"\u0397\u0314\u0300", "\u1F2C"=>"\u0397\u0313\u0301", "\u1F2D"=>"\u0397\u0314\u0301", "\u1F2E"=>"\u0397\u0313\u0342", "\u1F2F"=>"\u0397\u0314\u0342",
- "\u1F30"=>"\u03B9\u0313", "\u1F31"=>"\u03B9\u0314", "\u1F32"=>"\u03B9\u0313\u0300", "\u1F33"=>"\u03B9\u0314\u0300", "\u1F34"=>"\u03B9\u0313\u0301", "\u1F35"=>"\u03B9\u0314\u0301", "\u1F36"=>"\u03B9\u0313\u0342", "\u1F37"=>"\u03B9\u0314\u0342",
- "\u1F38"=>"\u0399\u0313", "\u1F39"=>"\u0399\u0314", "\u1F3A"=>"\u0399\u0313\u0300", "\u1F3B"=>"\u0399\u0314\u0300", "\u1F3C"=>"\u0399\u0313\u0301", "\u1F3D"=>"\u0399\u0314\u0301", "\u1F3E"=>"\u0399\u0313\u0342", "\u1F3F"=>"\u0399\u0314\u0342",
- "\u1F40"=>"\u03BF\u0313", "\u1F41"=>"\u03BF\u0314", "\u1F42"=>"\u03BF\u0313\u0300", "\u1F43"=>"\u03BF\u0314\u0300", "\u1F44"=>"\u03BF\u0313\u0301", "\u1F45"=>"\u03BF\u0314\u0301", "\u1F48"=>"\u039F\u0313", "\u1F49"=>"\u039F\u0314",
- "\u1F4A"=>"\u039F\u0313\u0300", "\u1F4B"=>"\u039F\u0314\u0300", "\u1F4C"=>"\u039F\u0313\u0301", "\u1F4D"=>"\u039F\u0314\u0301", "\u1F50"=>"\u03C5\u0313", "\u1F51"=>"\u03C5\u0314", "\u1F52"=>"\u03C5\u0313\u0300", "\u1F53"=>"\u03C5\u0314\u0300",
- "\u1F54"=>"\u03C5\u0313\u0301", "\u1F55"=>"\u03C5\u0314\u0301", "\u1F56"=>"\u03C5\u0313\u0342", "\u1F57"=>"\u03C5\u0314\u0342", "\u1F59"=>"\u03A5\u0314", "\u1F5B"=>"\u03A5\u0314\u0300", "\u1F5D"=>"\u03A5\u0314\u0301", "\u1F5F"=>"\u03A5\u0314\u0342",
- "\u1F60"=>"\u03C9\u0313", "\u1F61"=>"\u03C9\u0314", "\u1F62"=>"\u03C9\u0313\u0300", "\u1F63"=>"\u03C9\u0314\u0300", "\u1F64"=>"\u03C9\u0313\u0301", "\u1F65"=>"\u03C9\u0314\u0301", "\u1F66"=>"\u03C9\u0313\u0342", "\u1F67"=>"\u03C9\u0314\u0342",
- "\u1F68"=>"\u03A9\u0313", "\u1F69"=>"\u03A9\u0314", "\u1F6A"=>"\u03A9\u0313\u0300", "\u1F6B"=>"\u03A9\u0314\u0300", "\u1F6C"=>"\u03A9\u0313\u0301", "\u1F6D"=>"\u03A9\u0314\u0301", "\u1F6E"=>"\u03A9\u0313\u0342", "\u1F6F"=>"\u03A9\u0314\u0342",
- "\u1F70"=>"\u03B1\u0300", "\u1F71"=>"\u03B1\u0301", "\u1F72"=>"\u03B5\u0300", "\u1F73"=>"\u03B5\u0301", "\u1F74"=>"\u03B7\u0300", "\u1F75"=>"\u03B7\u0301", "\u1F76"=>"\u03B9\u0300", "\u1F77"=>"\u03B9\u0301",
- "\u1F78"=>"\u03BF\u0300", "\u1F79"=>"\u03BF\u0301", "\u1F7A"=>"\u03C5\u0300", "\u1F7B"=>"\u03C5\u0301", "\u1F7C"=>"\u03C9\u0300", "\u1F7D"=>"\u03C9\u0301", "\u1F80"=>"\u03B1\u0313\u0345", "\u1F81"=>"\u03B1\u0314\u0345",
- "\u1F82"=>"\u03B1\u0313\u0300\u0345", "\u1F83"=>"\u03B1\u0314\u0300\u0345", "\u1F84"=>"\u03B1\u0313\u0301\u0345", "\u1F85"=>"\u03B1\u0314\u0301\u0345", "\u1F86"=>"\u03B1\u0313\u0342\u0345", "\u1F87"=>"\u03B1\u0314\u0342\u0345", "\u1F88"=>"\u0391\u0313\u0345", "\u1F89"=>"\u0391\u0314\u0345",
- "\u1F8A"=>"\u0391\u0313\u0300\u0345", "\u1F8B"=>"\u0391\u0314\u0300\u0345", "\u1F8C"=>"\u0391\u0313\u0301\u0345", "\u1F8D"=>"\u0391\u0314\u0301\u0345", "\u1F8E"=>"\u0391\u0313\u0342\u0345", "\u1F8F"=>"\u0391\u0314\u0342\u0345", "\u1F90"=>"\u03B7\u0313\u0345", "\u1F91"=>"\u03B7\u0314\u0345",
- "\u1F92"=>"\u03B7\u0313\u0300\u0345", "\u1F93"=>"\u03B7\u0314\u0300\u0345", "\u1F94"=>"\u03B7\u0313\u0301\u0345", "\u1F95"=>"\u03B7\u0314\u0301\u0345", "\u1F96"=>"\u03B7\u0313\u0342\u0345", "\u1F97"=>"\u03B7\u0314\u0342\u0345", "\u1F98"=>"\u0397\u0313\u0345", "\u1F99"=>"\u0397\u0314\u0345",
- "\u1F9A"=>"\u0397\u0313\u0300\u0345", "\u1F9B"=>"\u0397\u0314\u0300\u0345", "\u1F9C"=>"\u0397\u0313\u0301\u0345", "\u1F9D"=>"\u0397\u0314\u0301\u0345", "\u1F9E"=>"\u0397\u0313\u0342\u0345", "\u1F9F"=>"\u0397\u0314\u0342\u0345", "\u1FA0"=>"\u03C9\u0313\u0345", "\u1FA1"=>"\u03C9\u0314\u0345",
- "\u1FA2"=>"\u03C9\u0313\u0300\u0345", "\u1FA3"=>"\u03C9\u0314\u0300\u0345", "\u1FA4"=>"\u03C9\u0313\u0301\u0345", "\u1FA5"=>"\u03C9\u0314\u0301\u0345", "\u1FA6"=>"\u03C9\u0313\u0342\u0345", "\u1FA7"=>"\u03C9\u0314\u0342\u0345", "\u1FA8"=>"\u03A9\u0313\u0345", "\u1FA9"=>"\u03A9\u0314\u0345",
- "\u1FAA"=>"\u03A9\u0313\u0300\u0345", "\u1FAB"=>"\u03A9\u0314\u0300\u0345", "\u1FAC"=>"\u03A9\u0313\u0301\u0345", "\u1FAD"=>"\u03A9\u0314\u0301\u0345", "\u1FAE"=>"\u03A9\u0313\u0342\u0345", "\u1FAF"=>"\u03A9\u0314\u0342\u0345", "\u1FB0"=>"\u03B1\u0306", "\u1FB1"=>"\u03B1\u0304",
- "\u1FB2"=>"\u03B1\u0300\u0345", "\u1FB3"=>"\u03B1\u0345", "\u1FB4"=>"\u03B1\u0301\u0345", "\u1FB6"=>"\u03B1\u0342", "\u1FB7"=>"\u03B1\u0342\u0345", "\u1FB8"=>"\u0391\u0306", "\u1FB9"=>"\u0391\u0304", "\u1FBA"=>"\u0391\u0300",
- "\u1FBB"=>"\u0391\u0301", "\u1FBC"=>"\u0391\u0345", "\u1FBE"=>"\u03B9", "\u1FC1"=>"\u00A8\u0342", "\u1FC2"=>"\u03B7\u0300\u0345", "\u1FC3"=>"\u03B7\u0345", "\u1FC4"=>"\u03B7\u0301\u0345", "\u1FC6"=>"\u03B7\u0342",
- "\u1FC7"=>"\u03B7\u0342\u0345", "\u1FC8"=>"\u0395\u0300", "\u1FC9"=>"\u0395\u0301", "\u1FCA"=>"\u0397\u0300", "\u1FCB"=>"\u0397\u0301", "\u1FCC"=>"\u0397\u0345", "\u1FCD"=>"\u1FBF\u0300", "\u1FCE"=>"\u1FBF\u0301",
- "\u1FCF"=>"\u1FBF\u0342", "\u1FD0"=>"\u03B9\u0306", "\u1FD1"=>"\u03B9\u0304", "\u1FD2"=>"\u03B9\u0308\u0300", "\u1FD3"=>"\u03B9\u0308\u0301", "\u1FD6"=>"\u03B9\u0342", "\u1FD7"=>"\u03B9\u0308\u0342", "\u1FD8"=>"\u0399\u0306",
- "\u1FD9"=>"\u0399\u0304", "\u1FDA"=>"\u0399\u0300", "\u1FDB"=>"\u0399\u0301", "\u1FDD"=>"\u1FFE\u0300", "\u1FDE"=>"\u1FFE\u0301", "\u1FDF"=>"\u1FFE\u0342", "\u1FE0"=>"\u03C5\u0306", "\u1FE1"=>"\u03C5\u0304",
- "\u1FE2"=>"\u03C5\u0308\u0300", "\u1FE3"=>"\u03C5\u0308\u0301", "\u1FE4"=>"\u03C1\u0313", "\u1FE5"=>"\u03C1\u0314", "\u1FE6"=>"\u03C5\u0342", "\u1FE7"=>"\u03C5\u0308\u0342", "\u1FE8"=>"\u03A5\u0306", "\u1FE9"=>"\u03A5\u0304",
- "\u1FEA"=>"\u03A5\u0300", "\u1FEB"=>"\u03A5\u0301", "\u1FEC"=>"\u03A1\u0314", "\u1FED"=>"\u00A8\u0300", "\u1FEE"=>"\u00A8\u0301", "\u1FEF"=>"`", "\u1FF2"=>"\u03C9\u0300\u0345", "\u1FF3"=>"\u03C9\u0345",
- "\u1FF4"=>"\u03C9\u0301\u0345", "\u1FF6"=>"\u03C9\u0342", "\u1FF7"=>"\u03C9\u0342\u0345", "\u1FF8"=>"\u039F\u0300", "\u1FF9"=>"\u039F\u0301", "\u1FFA"=>"\u03A9\u0300", "\u1FFB"=>"\u03A9\u0301", "\u1FFC"=>"\u03A9\u0345",
- "\u1FFD"=>"\u00B4", "\u2000"=>"\u2002", "\u2001"=>"\u2003", "\u2126"=>"\u03A9", "\u212A"=>"K", "\u212B"=>"A\u030A", "\u219A"=>"\u2190\u0338", "\u219B"=>"\u2192\u0338",
- "\u21AE"=>"\u2194\u0338", "\u21CD"=>"\u21D0\u0338", "\u21CE"=>"\u21D4\u0338", "\u21CF"=>"\u21D2\u0338", "\u2204"=>"\u2203\u0338", "\u2209"=>"\u2208\u0338", "\u220C"=>"\u220B\u0338", "\u2224"=>"\u2223\u0338",
- "\u2226"=>"\u2225\u0338", "\u2241"=>"\u223C\u0338", "\u2244"=>"\u2243\u0338", "\u2247"=>"\u2245\u0338", "\u2249"=>"\u2248\u0338", "\u2260"=>"=\u0338", "\u2262"=>"\u2261\u0338", "\u226D"=>"\u224D\u0338",
- "\u226E"=>"<\u0338", "\u226F"=>">\u0338", "\u2270"=>"\u2264\u0338", "\u2271"=>"\u2265\u0338", "\u2274"=>"\u2272\u0338", "\u2275"=>"\u2273\u0338", "\u2278"=>"\u2276\u0338", "\u2279"=>"\u2277\u0338",
- "\u2280"=>"\u227A\u0338", "\u2281"=>"\u227B\u0338", "\u2284"=>"\u2282\u0338", "\u2285"=>"\u2283\u0338", "\u2288"=>"\u2286\u0338", "\u2289"=>"\u2287\u0338", "\u22AC"=>"\u22A2\u0338", "\u22AD"=>"\u22A8\u0338",
- "\u22AE"=>"\u22A9\u0338", "\u22AF"=>"\u22AB\u0338", "\u22E0"=>"\u227C\u0338", "\u22E1"=>"\u227D\u0338", "\u22E2"=>"\u2291\u0338", "\u22E3"=>"\u2292\u0338", "\u22EA"=>"\u22B2\u0338", "\u22EB"=>"\u22B3\u0338",
- "\u22EC"=>"\u22B4\u0338", "\u22ED"=>"\u22B5\u0338", "\u2329"=>"\u3008", "\u232A"=>"\u3009", "\u2ADC"=>"\u2ADD\u0338", "\u304C"=>"\u304B\u3099", "\u304E"=>"\u304D\u3099", "\u3050"=>"\u304F\u3099",
- "\u3052"=>"\u3051\u3099", "\u3054"=>"\u3053\u3099", "\u3056"=>"\u3055\u3099", "\u3058"=>"\u3057\u3099", "\u305A"=>"\u3059\u3099", "\u305C"=>"\u305B\u3099", "\u305E"=>"\u305D\u3099", "\u3060"=>"\u305F\u3099",
- "\u3062"=>"\u3061\u3099", "\u3065"=>"\u3064\u3099", "\u3067"=>"\u3066\u3099", "\u3069"=>"\u3068\u3099", "\u3070"=>"\u306F\u3099", "\u3071"=>"\u306F\u309A", "\u3073"=>"\u3072\u3099", "\u3074"=>"\u3072\u309A",
- "\u3076"=>"\u3075\u3099", "\u3077"=>"\u3075\u309A", "\u3079"=>"\u3078\u3099", "\u307A"=>"\u3078\u309A", "\u307C"=>"\u307B\u3099", "\u307D"=>"\u307B\u309A", "\u3094"=>"\u3046\u3099", "\u309E"=>"\u309D\u3099",
- "\u30AC"=>"\u30AB\u3099", "\u30AE"=>"\u30AD\u3099", "\u30B0"=>"\u30AF\u3099", "\u30B2"=>"\u30B1\u3099", "\u30B4"=>"\u30B3\u3099", "\u30B6"=>"\u30B5\u3099", "\u30B8"=>"\u30B7\u3099", "\u30BA"=>"\u30B9\u3099",
- "\u30BC"=>"\u30BB\u3099", "\u30BE"=>"\u30BD\u3099", "\u30C0"=>"\u30BF\u3099", "\u30C2"=>"\u30C1\u3099", "\u30C5"=>"\u30C4\u3099", "\u30C7"=>"\u30C6\u3099", "\u30C9"=>"\u30C8\u3099", "\u30D0"=>"\u30CF\u3099",
- "\u30D1"=>"\u30CF\u309A", "\u30D3"=>"\u30D2\u3099", "\u30D4"=>"\u30D2\u309A", "\u30D6"=>"\u30D5\u3099", "\u30D7"=>"\u30D5\u309A", "\u30D9"=>"\u30D8\u3099", "\u30DA"=>"\u30D8\u309A", "\u30DC"=>"\u30DB\u3099",
- "\u30DD"=>"\u30DB\u309A", "\u30F4"=>"\u30A6\u3099", "\u30F7"=>"\u30EF\u3099", "\u30F8"=>"\u30F0\u3099", "\u30F9"=>"\u30F1\u3099", "\u30FA"=>"\u30F2\u3099", "\u30FE"=>"\u30FD\u3099", "\uF900"=>"\u8C48",
- "\uF901"=>"\u66F4", "\uF902"=>"\u8ECA", "\uF903"=>"\u8CC8", "\uF904"=>"\u6ED1", "\uF905"=>"\u4E32", "\uF906"=>"\u53E5", "\uF907"=>"\u9F9C", "\uF908"=>"\u9F9C",
- "\uF909"=>"\u5951", "\uF90A"=>"\u91D1", "\uF90B"=>"\u5587", "\uF90C"=>"\u5948", "\uF90D"=>"\u61F6", "\uF90E"=>"\u7669", "\uF90F"=>"\u7F85", "\uF910"=>"\u863F",
- "\uF911"=>"\u87BA", "\uF912"=>"\u88F8", "\uF913"=>"\u908F", "\uF914"=>"\u6A02", "\uF915"=>"\u6D1B", "\uF916"=>"\u70D9", "\uF917"=>"\u73DE", "\uF918"=>"\u843D",
- "\uF919"=>"\u916A", "\uF91A"=>"\u99F1", "\uF91B"=>"\u4E82", "\uF91C"=>"\u5375", "\uF91D"=>"\u6B04", "\uF91E"=>"\u721B", "\uF91F"=>"\u862D", "\uF920"=>"\u9E1E",
- "\uF921"=>"\u5D50", "\uF922"=>"\u6FEB", "\uF923"=>"\u85CD", "\uF924"=>"\u8964", "\uF925"=>"\u62C9", "\uF926"=>"\u81D8", "\uF927"=>"\u881F", "\uF928"=>"\u5ECA",
- "\uF929"=>"\u6717", "\uF92A"=>"\u6D6A", "\uF92B"=>"\u72FC", "\uF92C"=>"\u90CE", "\uF92D"=>"\u4F86", "\uF92E"=>"\u51B7", "\uF92F"=>"\u52DE", "\uF930"=>"\u64C4",
- "\uF931"=>"\u6AD3", "\uF932"=>"\u7210", "\uF933"=>"\u76E7", "\uF934"=>"\u8001", "\uF935"=>"\u8606", "\uF936"=>"\u865C", "\uF937"=>"\u8DEF", "\uF938"=>"\u9732",
- "\uF939"=>"\u9B6F", "\uF93A"=>"\u9DFA", "\uF93B"=>"\u788C", "\uF93C"=>"\u797F", "\uF93D"=>"\u7DA0", "\uF93E"=>"\u83C9", "\uF93F"=>"\u9304", "\uF940"=>"\u9E7F",
- "\uF941"=>"\u8AD6", "\uF942"=>"\u58DF", "\uF943"=>"\u5F04", "\uF944"=>"\u7C60", "\uF945"=>"\u807E", "\uF946"=>"\u7262", "\uF947"=>"\u78CA", "\uF948"=>"\u8CC2",
- "\uF949"=>"\u96F7", "\uF94A"=>"\u58D8", "\uF94B"=>"\u5C62", "\uF94C"=>"\u6A13", "\uF94D"=>"\u6DDA", "\uF94E"=>"\u6F0F", "\uF94F"=>"\u7D2F", "\uF950"=>"\u7E37",
- "\uF951"=>"\u964B", "\uF952"=>"\u52D2", "\uF953"=>"\u808B", "\uF954"=>"\u51DC", "\uF955"=>"\u51CC", "\uF956"=>"\u7A1C", "\uF957"=>"\u7DBE", "\uF958"=>"\u83F1",
- "\uF959"=>"\u9675", "\uF95A"=>"\u8B80", "\uF95B"=>"\u62CF", "\uF95C"=>"\u6A02", "\uF95D"=>"\u8AFE", "\uF95E"=>"\u4E39", "\uF95F"=>"\u5BE7", "\uF960"=>"\u6012",
- "\uF961"=>"\u7387", "\uF962"=>"\u7570", "\uF963"=>"\u5317", "\uF964"=>"\u78FB", "\uF965"=>"\u4FBF", "\uF966"=>"\u5FA9", "\uF967"=>"\u4E0D", "\uF968"=>"\u6CCC",
- "\uF969"=>"\u6578", "\uF96A"=>"\u7D22", "\uF96B"=>"\u53C3", "\uF96C"=>"\u585E", "\uF96D"=>"\u7701", "\uF96E"=>"\u8449", "\uF96F"=>"\u8AAA", "\uF970"=>"\u6BBA",
- "\uF971"=>"\u8FB0", "\uF972"=>"\u6C88", "\uF973"=>"\u62FE", "\uF974"=>"\u82E5", "\uF975"=>"\u63A0", "\uF976"=>"\u7565", "\uF977"=>"\u4EAE", "\uF978"=>"\u5169",
- "\uF979"=>"\u51C9", "\uF97A"=>"\u6881", "\uF97B"=>"\u7CE7", "\uF97C"=>"\u826F", "\uF97D"=>"\u8AD2", "\uF97E"=>"\u91CF", "\uF97F"=>"\u52F5", "\uF980"=>"\u5442",
- "\uF981"=>"\u5973", "\uF982"=>"\u5EEC", "\uF983"=>"\u65C5", "\uF984"=>"\u6FFE", "\uF985"=>"\u792A", "\uF986"=>"\u95AD", "\uF987"=>"\u9A6A", "\uF988"=>"\u9E97",
- "\uF989"=>"\u9ECE", "\uF98A"=>"\u529B", "\uF98B"=>"\u66C6", "\uF98C"=>"\u6B77", "\uF98D"=>"\u8F62", "\uF98E"=>"\u5E74", "\uF98F"=>"\u6190", "\uF990"=>"\u6200",
- "\uF991"=>"\u649A", "\uF992"=>"\u6F23", "\uF993"=>"\u7149", "\uF994"=>"\u7489", "\uF995"=>"\u79CA", "\uF996"=>"\u7DF4", "\uF997"=>"\u806F", "\uF998"=>"\u8F26",
- "\uF999"=>"\u84EE", "\uF99A"=>"\u9023", "\uF99B"=>"\u934A", "\uF99C"=>"\u5217", "\uF99D"=>"\u52A3", "\uF99E"=>"\u54BD", "\uF99F"=>"\u70C8", "\uF9A0"=>"\u88C2",
- "\uF9A1"=>"\u8AAA", "\uF9A2"=>"\u5EC9", "\uF9A3"=>"\u5FF5", "\uF9A4"=>"\u637B", "\uF9A5"=>"\u6BAE", "\uF9A6"=>"\u7C3E", "\uF9A7"=>"\u7375", "\uF9A8"=>"\u4EE4",
- "\uF9A9"=>"\u56F9", "\uF9AA"=>"\u5BE7", "\uF9AB"=>"\u5DBA", "\uF9AC"=>"\u601C", "\uF9AD"=>"\u73B2", "\uF9AE"=>"\u7469", "\uF9AF"=>"\u7F9A", "\uF9B0"=>"\u8046",
- "\uF9B1"=>"\u9234", "\uF9B2"=>"\u96F6", "\uF9B3"=>"\u9748", "\uF9B4"=>"\u9818", "\uF9B5"=>"\u4F8B", "\uF9B6"=>"\u79AE", "\uF9B7"=>"\u91B4", "\uF9B8"=>"\u96B8",
- "\uF9B9"=>"\u60E1", "\uF9BA"=>"\u4E86", "\uF9BB"=>"\u50DA", "\uF9BC"=>"\u5BEE", "\uF9BD"=>"\u5C3F", "\uF9BE"=>"\u6599", "\uF9BF"=>"\u6A02", "\uF9C0"=>"\u71CE",
- "\uF9C1"=>"\u7642", "\uF9C2"=>"\u84FC", "\uF9C3"=>"\u907C", "\uF9C4"=>"\u9F8D", "\uF9C5"=>"\u6688", "\uF9C6"=>"\u962E", "\uF9C7"=>"\u5289", "\uF9C8"=>"\u677B",
- "\uF9C9"=>"\u67F3", "\uF9CA"=>"\u6D41", "\uF9CB"=>"\u6E9C", "\uF9CC"=>"\u7409", "\uF9CD"=>"\u7559", "\uF9CE"=>"\u786B", "\uF9CF"=>"\u7D10", "\uF9D0"=>"\u985E",
- "\uF9D1"=>"\u516D", "\uF9D2"=>"\u622E", "\uF9D3"=>"\u9678", "\uF9D4"=>"\u502B", "\uF9D5"=>"\u5D19", "\uF9D6"=>"\u6DEA", "\uF9D7"=>"\u8F2A", "\uF9D8"=>"\u5F8B",
- "\uF9D9"=>"\u6144", "\uF9DA"=>"\u6817", "\uF9DB"=>"\u7387", "\uF9DC"=>"\u9686", "\uF9DD"=>"\u5229", "\uF9DE"=>"\u540F", "\uF9DF"=>"\u5C65", "\uF9E0"=>"\u6613",
- "\uF9E1"=>"\u674E", "\uF9E2"=>"\u68A8", "\uF9E3"=>"\u6CE5", "\uF9E4"=>"\u7406", "\uF9E5"=>"\u75E2", "\uF9E6"=>"\u7F79", "\uF9E7"=>"\u88CF", "\uF9E8"=>"\u88E1",
- "\uF9E9"=>"\u91CC", "\uF9EA"=>"\u96E2", "\uF9EB"=>"\u533F", "\uF9EC"=>"\u6EBA", "\uF9ED"=>"\u541D", "\uF9EE"=>"\u71D0", "\uF9EF"=>"\u7498", "\uF9F0"=>"\u85FA",
- "\uF9F1"=>"\u96A3", "\uF9F2"=>"\u9C57", "\uF9F3"=>"\u9E9F", "\uF9F4"=>"\u6797", "\uF9F5"=>"\u6DCB", "\uF9F6"=>"\u81E8", "\uF9F7"=>"\u7ACB", "\uF9F8"=>"\u7B20",
- "\uF9F9"=>"\u7C92", "\uF9FA"=>"\u72C0", "\uF9FB"=>"\u7099", "\uF9FC"=>"\u8B58", "\uF9FD"=>"\u4EC0", "\uF9FE"=>"\u8336", "\uF9FF"=>"\u523A", "\uFA00"=>"\u5207",
- "\uFA01"=>"\u5EA6", "\uFA02"=>"\u62D3", "\uFA03"=>"\u7CD6", "\uFA04"=>"\u5B85", "\uFA05"=>"\u6D1E", "\uFA06"=>"\u66B4", "\uFA07"=>"\u8F3B", "\uFA08"=>"\u884C",
- "\uFA09"=>"\u964D", "\uFA0A"=>"\u898B", "\uFA0B"=>"\u5ED3", "\uFA0C"=>"\u5140", "\uFA0D"=>"\u55C0", "\uFA10"=>"\u585A", "\uFA12"=>"\u6674", "\uFA15"=>"\u51DE",
- "\uFA16"=>"\u732A", "\uFA17"=>"\u76CA", "\uFA18"=>"\u793C", "\uFA19"=>"\u795E", "\uFA1A"=>"\u7965", "\uFA1B"=>"\u798F", "\uFA1C"=>"\u9756", "\uFA1D"=>"\u7CBE",
- "\uFA1E"=>"\u7FBD", "\uFA20"=>"\u8612", "\uFA22"=>"\u8AF8", "\uFA25"=>"\u9038", "\uFA26"=>"\u90FD", "\uFA2A"=>"\u98EF", "\uFA2B"=>"\u98FC", "\uFA2C"=>"\u9928",
- "\uFA2D"=>"\u9DB4", "\uFA2E"=>"\u90DE", "\uFA2F"=>"\u96B7", "\uFA30"=>"\u4FAE", "\uFA31"=>"\u50E7", "\uFA32"=>"\u514D", "\uFA33"=>"\u52C9", "\uFA34"=>"\u52E4",
- "\uFA35"=>"\u5351", "\uFA36"=>"\u559D", "\uFA37"=>"\u5606", "\uFA38"=>"\u5668", "\uFA39"=>"\u5840", "\uFA3A"=>"\u58A8", "\uFA3B"=>"\u5C64", "\uFA3C"=>"\u5C6E",
- "\uFA3D"=>"\u6094", "\uFA3E"=>"\u6168", "\uFA3F"=>"\u618E", "\uFA40"=>"\u61F2", "\uFA41"=>"\u654F", "\uFA42"=>"\u65E2", "\uFA43"=>"\u6691", "\uFA44"=>"\u6885",
- "\uFA45"=>"\u6D77", "\uFA46"=>"\u6E1A", "\uFA47"=>"\u6F22", "\uFA48"=>"\u716E", "\uFA49"=>"\u722B", "\uFA4A"=>"\u7422", "\uFA4B"=>"\u7891", "\uFA4C"=>"\u793E",
- "\uFA4D"=>"\u7949", "\uFA4E"=>"\u7948", "\uFA4F"=>"\u7950", "\uFA50"=>"\u7956", "\uFA51"=>"\u795D", "\uFA52"=>"\u798D", "\uFA53"=>"\u798E", "\uFA54"=>"\u7A40",
- "\uFA55"=>"\u7A81", "\uFA56"=>"\u7BC0", "\uFA57"=>"\u7DF4", "\uFA58"=>"\u7E09", "\uFA59"=>"\u7E41", "\uFA5A"=>"\u7F72", "\uFA5B"=>"\u8005", "\uFA5C"=>"\u81ED",
- "\uFA5D"=>"\u8279", "\uFA5E"=>"\u8279", "\uFA5F"=>"\u8457", "\uFA60"=>"\u8910", "\uFA61"=>"\u8996", "\uFA62"=>"\u8B01", "\uFA63"=>"\u8B39", "\uFA64"=>"\u8CD3",
- "\uFA65"=>"\u8D08", "\uFA66"=>"\u8FB6", "\uFA67"=>"\u9038", "\uFA68"=>"\u96E3", "\uFA69"=>"\u97FF", "\uFA6A"=>"\u983B", "\uFA6B"=>"\u6075", "\uFA6C"=>"\u{242EE}",
- "\uFA6D"=>"\u8218", "\uFA70"=>"\u4E26", "\uFA71"=>"\u51B5", "\uFA72"=>"\u5168", "\uFA73"=>"\u4F80", "\uFA74"=>"\u5145", "\uFA75"=>"\u5180", "\uFA76"=>"\u52C7",
- "\uFA77"=>"\u52FA", "\uFA78"=>"\u559D", "\uFA79"=>"\u5555", "\uFA7A"=>"\u5599", "\uFA7B"=>"\u55E2", "\uFA7C"=>"\u585A", "\uFA7D"=>"\u58B3", "\uFA7E"=>"\u5944",
- "\uFA7F"=>"\u5954", "\uFA80"=>"\u5A62", "\uFA81"=>"\u5B28", "\uFA82"=>"\u5ED2", "\uFA83"=>"\u5ED9", "\uFA84"=>"\u5F69", "\uFA85"=>"\u5FAD", "\uFA86"=>"\u60D8",
- "\uFA87"=>"\u614E", "\uFA88"=>"\u6108", "\uFA89"=>"\u618E", "\uFA8A"=>"\u6160", "\uFA8B"=>"\u61F2", "\uFA8C"=>"\u6234", "\uFA8D"=>"\u63C4", "\uFA8E"=>"\u641C",
- "\uFA8F"=>"\u6452", "\uFA90"=>"\u6556", "\uFA91"=>"\u6674", "\uFA92"=>"\u6717", "\uFA93"=>"\u671B", "\uFA94"=>"\u6756", "\uFA95"=>"\u6B79", "\uFA96"=>"\u6BBA",
- "\uFA97"=>"\u6D41", "\uFA98"=>"\u6EDB", "\uFA99"=>"\u6ECB", "\uFA9A"=>"\u6F22", "\uFA9B"=>"\u701E", "\uFA9C"=>"\u716E", "\uFA9D"=>"\u77A7", "\uFA9E"=>"\u7235",
- "\uFA9F"=>"\u72AF", "\uFAA0"=>"\u732A", "\uFAA1"=>"\u7471", "\uFAA2"=>"\u7506", "\uFAA3"=>"\u753B", "\uFAA4"=>"\u761D", "\uFAA5"=>"\u761F", "\uFAA6"=>"\u76CA",
- "\uFAA7"=>"\u76DB", "\uFAA8"=>"\u76F4", "\uFAA9"=>"\u774A", "\uFAAA"=>"\u7740", "\uFAAB"=>"\u78CC", "\uFAAC"=>"\u7AB1", "\uFAAD"=>"\u7BC0", "\uFAAE"=>"\u7C7B",
- "\uFAAF"=>"\u7D5B", "\uFAB0"=>"\u7DF4", "\uFAB1"=>"\u7F3E", "\uFAB2"=>"\u8005", "\uFAB3"=>"\u8352", "\uFAB4"=>"\u83EF", "\uFAB5"=>"\u8779", "\uFAB6"=>"\u8941",
- "\uFAB7"=>"\u8986", "\uFAB8"=>"\u8996", "\uFAB9"=>"\u8ABF", "\uFABA"=>"\u8AF8", "\uFABB"=>"\u8ACB", "\uFABC"=>"\u8B01", "\uFABD"=>"\u8AFE", "\uFABE"=>"\u8AED",
- "\uFABF"=>"\u8B39", "\uFAC0"=>"\u8B8A", "\uFAC1"=>"\u8D08", "\uFAC2"=>"\u8F38", "\uFAC3"=>"\u9072", "\uFAC4"=>"\u9199", "\uFAC5"=>"\u9276", "\uFAC6"=>"\u967C",
- "\uFAC7"=>"\u96E3", "\uFAC8"=>"\u9756", "\uFAC9"=>"\u97DB", "\uFACA"=>"\u97FF", "\uFACB"=>"\u980B", "\uFACC"=>"\u983B", "\uFACD"=>"\u9B12", "\uFACE"=>"\u9F9C",
- "\uFACF"=>"\u{2284A}", "\uFAD0"=>"\u{22844}", "\uFAD1"=>"\u{233D5}", "\uFAD2"=>"\u3B9D", "\uFAD3"=>"\u4018", "\uFAD4"=>"\u4039", "\uFAD5"=>"\u{25249}", "\uFAD6"=>"\u{25CD0}",
- "\uFAD7"=>"\u{27ED3}", "\uFAD8"=>"\u9F43", "\uFAD9"=>"\u9F8E", "\uFB1D"=>"\u05D9\u05B4", "\uFB1F"=>"\u05F2\u05B7", "\uFB2A"=>"\u05E9\u05C1", "\uFB2B"=>"\u05E9\u05C2", "\uFB2C"=>"\u05E9\u05BC\u05C1",
- "\uFB2D"=>"\u05E9\u05BC\u05C2", "\uFB2E"=>"\u05D0\u05B7", "\uFB2F"=>"\u05D0\u05B8", "\uFB30"=>"\u05D0\u05BC", "\uFB31"=>"\u05D1\u05BC", "\uFB32"=>"\u05D2\u05BC", "\uFB33"=>"\u05D3\u05BC", "\uFB34"=>"\u05D4\u05BC",
- "\uFB35"=>"\u05D5\u05BC", "\uFB36"=>"\u05D6\u05BC", "\uFB38"=>"\u05D8\u05BC", "\uFB39"=>"\u05D9\u05BC", "\uFB3A"=>"\u05DA\u05BC", "\uFB3B"=>"\u05DB\u05BC", "\uFB3C"=>"\u05DC\u05BC", "\uFB3E"=>"\u05DE\u05BC",
- "\uFB40"=>"\u05E0\u05BC", "\uFB41"=>"\u05E1\u05BC", "\uFB43"=>"\u05E3\u05BC", "\uFB44"=>"\u05E4\u05BC", "\uFB46"=>"\u05E6\u05BC", "\uFB47"=>"\u05E7\u05BC", "\uFB48"=>"\u05E8\u05BC", "\uFB49"=>"\u05E9\u05BC",
- "\uFB4A"=>"\u05EA\u05BC", "\uFB4B"=>"\u05D5\u05B9", "\uFB4C"=>"\u05D1\u05BF", "\uFB4D"=>"\u05DB\u05BF", "\uFB4E"=>"\u05E4\u05BF", "\u{1109A}"=>"\u{11099}\u{110BA}", "\u{1109C}"=>"\u{1109B}\u{110BA}", "\u{110AB}"=>"\u{110A5}\u{110BA}",
- "\u{1112E}"=>"\u{11131}\u{11127}", "\u{1112F}"=>"\u{11132}\u{11127}", "\u{1134B}"=>"\u{11347}\u{1133E}", "\u{1134C}"=>"\u{11347}\u{11357}", "\u{114BB}"=>"\u{114B9}\u{114BA}", "\u{114BC}"=>"\u{114B9}\u{114B0}", "\u{114BE}"=>"\u{114B9}\u{114BD}", "\u{115BA}"=>"\u{115B8}\u{115AF}",
- "\u{115BB}"=>"\u{115B9}\u{115AF}", "\u{1D15E}"=>"\u{1D157}\u{1D165}", "\u{1D15F}"=>"\u{1D158}\u{1D165}", "\u{1D160}"=>"\u{1D158}\u{1D165}\u{1D16E}", "\u{1D161}"=>"\u{1D158}\u{1D165}\u{1D16F}", "\u{1D162}"=>"\u{1D158}\u{1D165}\u{1D170}", "\u{1D163}"=>"\u{1D158}\u{1D165}\u{1D171}", "\u{1D164}"=>"\u{1D158}\u{1D165}\u{1D172}",
- "\u{1D1BB}"=>"\u{1D1B9}\u{1D165}", "\u{1D1BC}"=>"\u{1D1BA}\u{1D165}", "\u{1D1BD}"=>"\u{1D1B9}\u{1D165}\u{1D16E}", "\u{1D1BE}"=>"\u{1D1BA}\u{1D165}\u{1D16E}", "\u{1D1BF}"=>"\u{1D1B9}\u{1D165}\u{1D16F}", "\u{1D1C0}"=>"\u{1D1BA}\u{1D165}\u{1D16F}", "\u{2F800}"=>"\u4E3D", "\u{2F801}"=>"\u4E38",
- "\u{2F802}"=>"\u4E41", "\u{2F803}"=>"\u{20122}", "\u{2F804}"=>"\u4F60", "\u{2F805}"=>"\u4FAE", "\u{2F806}"=>"\u4FBB", "\u{2F807}"=>"\u5002", "\u{2F808}"=>"\u507A", "\u{2F809}"=>"\u5099",
- "\u{2F80A}"=>"\u50E7", "\u{2F80B}"=>"\u50CF", "\u{2F80C}"=>"\u349E", "\u{2F80D}"=>"\u{2063A}", "\u{2F80E}"=>"\u514D", "\u{2F80F}"=>"\u5154", "\u{2F810}"=>"\u5164", "\u{2F811}"=>"\u5177",
- "\u{2F812}"=>"\u{2051C}", "\u{2F813}"=>"\u34B9", "\u{2F814}"=>"\u5167", "\u{2F815}"=>"\u518D", "\u{2F816}"=>"\u{2054B}", "\u{2F817}"=>"\u5197", "\u{2F818}"=>"\u51A4", "\u{2F819}"=>"\u4ECC",
- "\u{2F81A}"=>"\u51AC", "\u{2F81B}"=>"\u51B5", "\u{2F81C}"=>"\u{291DF}", "\u{2F81D}"=>"\u51F5", "\u{2F81E}"=>"\u5203", "\u{2F81F}"=>"\u34DF", "\u{2F820}"=>"\u523B", "\u{2F821}"=>"\u5246",
- "\u{2F822}"=>"\u5272", "\u{2F823}"=>"\u5277", "\u{2F824}"=>"\u3515", "\u{2F825}"=>"\u52C7", "\u{2F826}"=>"\u52C9", "\u{2F827}"=>"\u52E4", "\u{2F828}"=>"\u52FA", "\u{2F829}"=>"\u5305",
- "\u{2F82A}"=>"\u5306", "\u{2F82B}"=>"\u5317", "\u{2F82C}"=>"\u5349", "\u{2F82D}"=>"\u5351", "\u{2F82E}"=>"\u535A", "\u{2F82F}"=>"\u5373", "\u{2F830}"=>"\u537D", "\u{2F831}"=>"\u537F",
- "\u{2F832}"=>"\u537F", "\u{2F833}"=>"\u537F", "\u{2F834}"=>"\u{20A2C}", "\u{2F835}"=>"\u7070", "\u{2F836}"=>"\u53CA", "\u{2F837}"=>"\u53DF", "\u{2F838}"=>"\u{20B63}", "\u{2F839}"=>"\u53EB",
- "\u{2F83A}"=>"\u53F1", "\u{2F83B}"=>"\u5406", "\u{2F83C}"=>"\u549E", "\u{2F83D}"=>"\u5438", "\u{2F83E}"=>"\u5448", "\u{2F83F}"=>"\u5468", "\u{2F840}"=>"\u54A2", "\u{2F841}"=>"\u54F6",
- "\u{2F842}"=>"\u5510", "\u{2F843}"=>"\u5553", "\u{2F844}"=>"\u5563", "\u{2F845}"=>"\u5584", "\u{2F846}"=>"\u5584", "\u{2F847}"=>"\u5599", "\u{2F848}"=>"\u55AB", "\u{2F849}"=>"\u55B3",
- "\u{2F84A}"=>"\u55C2", "\u{2F84B}"=>"\u5716", "\u{2F84C}"=>"\u5606", "\u{2F84D}"=>"\u5717", "\u{2F84E}"=>"\u5651", "\u{2F84F}"=>"\u5674", "\u{2F850}"=>"\u5207", "\u{2F851}"=>"\u58EE",
- "\u{2F852}"=>"\u57CE", "\u{2F853}"=>"\u57F4", "\u{2F854}"=>"\u580D", "\u{2F855}"=>"\u578B", "\u{2F856}"=>"\u5832", "\u{2F857}"=>"\u5831", "\u{2F858}"=>"\u58AC", "\u{2F859}"=>"\u{214E4}",
- "\u{2F85A}"=>"\u58F2", "\u{2F85B}"=>"\u58F7", "\u{2F85C}"=>"\u5906", "\u{2F85D}"=>"\u591A", "\u{2F85E}"=>"\u5922", "\u{2F85F}"=>"\u5962", "\u{2F860}"=>"\u{216A8}", "\u{2F861}"=>"\u{216EA}",
- "\u{2F862}"=>"\u59EC", "\u{2F863}"=>"\u5A1B", "\u{2F864}"=>"\u5A27", "\u{2F865}"=>"\u59D8", "\u{2F866}"=>"\u5A66", "\u{2F867}"=>"\u36EE", "\u{2F868}"=>"\u36FC", "\u{2F869}"=>"\u5B08",
- "\u{2F86A}"=>"\u5B3E", "\u{2F86B}"=>"\u5B3E", "\u{2F86C}"=>"\u{219C8}", "\u{2F86D}"=>"\u5BC3", "\u{2F86E}"=>"\u5BD8", "\u{2F86F}"=>"\u5BE7", "\u{2F870}"=>"\u5BF3", "\u{2F871}"=>"\u{21B18}",
- "\u{2F872}"=>"\u5BFF", "\u{2F873}"=>"\u5C06", "\u{2F874}"=>"\u5F53", "\u{2F875}"=>"\u5C22", "\u{2F876}"=>"\u3781", "\u{2F877}"=>"\u5C60", "\u{2F878}"=>"\u5C6E", "\u{2F879}"=>"\u5CC0",
- "\u{2F87A}"=>"\u5C8D", "\u{2F87B}"=>"\u{21DE4}", "\u{2F87C}"=>"\u5D43", "\u{2F87D}"=>"\u{21DE6}", "\u{2F87E}"=>"\u5D6E", "\u{2F87F}"=>"\u5D6B", "\u{2F880}"=>"\u5D7C", "\u{2F881}"=>"\u5DE1",
- "\u{2F882}"=>"\u5DE2", "\u{2F883}"=>"\u382F", "\u{2F884}"=>"\u5DFD", "\u{2F885}"=>"\u5E28", "\u{2F886}"=>"\u5E3D", "\u{2F887}"=>"\u5E69", "\u{2F888}"=>"\u3862", "\u{2F889}"=>"\u{22183}",
- "\u{2F88A}"=>"\u387C", "\u{2F88B}"=>"\u5EB0", "\u{2F88C}"=>"\u5EB3", "\u{2F88D}"=>"\u5EB6", "\u{2F88E}"=>"\u5ECA", "\u{2F88F}"=>"\u{2A392}", "\u{2F890}"=>"\u5EFE", "\u{2F891}"=>"\u{22331}",
- "\u{2F892}"=>"\u{22331}", "\u{2F893}"=>"\u8201", "\u{2F894}"=>"\u5F22", "\u{2F895}"=>"\u5F22", "\u{2F896}"=>"\u38C7", "\u{2F897}"=>"\u{232B8}", "\u{2F898}"=>"\u{261DA}", "\u{2F899}"=>"\u5F62",
- "\u{2F89A}"=>"\u5F6B", "\u{2F89B}"=>"\u38E3", "\u{2F89C}"=>"\u5F9A", "\u{2F89D}"=>"\u5FCD", "\u{2F89E}"=>"\u5FD7", "\u{2F89F}"=>"\u5FF9", "\u{2F8A0}"=>"\u6081", "\u{2F8A1}"=>"\u393A",
- "\u{2F8A2}"=>"\u391C", "\u{2F8A3}"=>"\u6094", "\u{2F8A4}"=>"\u{226D4}", "\u{2F8A5}"=>"\u60C7", "\u{2F8A6}"=>"\u6148", "\u{2F8A7}"=>"\u614C", "\u{2F8A8}"=>"\u614E", "\u{2F8A9}"=>"\u614C",
- "\u{2F8AA}"=>"\u617A", "\u{2F8AB}"=>"\u618E", "\u{2F8AC}"=>"\u61B2", "\u{2F8AD}"=>"\u61A4", "\u{2F8AE}"=>"\u61AF", "\u{2F8AF}"=>"\u61DE", "\u{2F8B0}"=>"\u61F2", "\u{2F8B1}"=>"\u61F6",
- "\u{2F8B2}"=>"\u6210", "\u{2F8B3}"=>"\u621B", "\u{2F8B4}"=>"\u625D", "\u{2F8B5}"=>"\u62B1", "\u{2F8B6}"=>"\u62D4", "\u{2F8B7}"=>"\u6350", "\u{2F8B8}"=>"\u{22B0C}", "\u{2F8B9}"=>"\u633D",
- "\u{2F8BA}"=>"\u62FC", "\u{2F8BB}"=>"\u6368", "\u{2F8BC}"=>"\u6383", "\u{2F8BD}"=>"\u63E4", "\u{2F8BE}"=>"\u{22BF1}", "\u{2F8BF}"=>"\u6422", "\u{2F8C0}"=>"\u63C5", "\u{2F8C1}"=>"\u63A9",
- "\u{2F8C2}"=>"\u3A2E", "\u{2F8C3}"=>"\u6469", "\u{2F8C4}"=>"\u647E", "\u{2F8C5}"=>"\u649D", "\u{2F8C6}"=>"\u6477", "\u{2F8C7}"=>"\u3A6C", "\u{2F8C8}"=>"\u654F", "\u{2F8C9}"=>"\u656C",
- "\u{2F8CA}"=>"\u{2300A}", "\u{2F8CB}"=>"\u65E3", "\u{2F8CC}"=>"\u66F8", "\u{2F8CD}"=>"\u6649", "\u{2F8CE}"=>"\u3B19", "\u{2F8CF}"=>"\u6691", "\u{2F8D0}"=>"\u3B08", "\u{2F8D1}"=>"\u3AE4",
- "\u{2F8D2}"=>"\u5192", "\u{2F8D3}"=>"\u5195", "\u{2F8D4}"=>"\u6700", "\u{2F8D5}"=>"\u669C", "\u{2F8D6}"=>"\u80AD", "\u{2F8D7}"=>"\u43D9", "\u{2F8D8}"=>"\u6717", "\u{2F8D9}"=>"\u671B",
- "\u{2F8DA}"=>"\u6721", "\u{2F8DB}"=>"\u675E", "\u{2F8DC}"=>"\u6753", "\u{2F8DD}"=>"\u{233C3}", "\u{2F8DE}"=>"\u3B49", "\u{2F8DF}"=>"\u67FA", "\u{2F8E0}"=>"\u6785", "\u{2F8E1}"=>"\u6852",
- "\u{2F8E2}"=>"\u6885", "\u{2F8E3}"=>"\u{2346D}", "\u{2F8E4}"=>"\u688E", "\u{2F8E5}"=>"\u681F", "\u{2F8E6}"=>"\u6914", "\u{2F8E7}"=>"\u3B9D", "\u{2F8E8}"=>"\u6942", "\u{2F8E9}"=>"\u69A3",
- "\u{2F8EA}"=>"\u69EA", "\u{2F8EB}"=>"\u6AA8", "\u{2F8EC}"=>"\u{236A3}", "\u{2F8ED}"=>"\u6ADB", "\u{2F8EE}"=>"\u3C18", "\u{2F8EF}"=>"\u6B21", "\u{2F8F0}"=>"\u{238A7}", "\u{2F8F1}"=>"\u6B54",
- "\u{2F8F2}"=>"\u3C4E", "\u{2F8F3}"=>"\u6B72", "\u{2F8F4}"=>"\u6B9F", "\u{2F8F5}"=>"\u6BBA", "\u{2F8F6}"=>"\u6BBB", "\u{2F8F7}"=>"\u{23A8D}", "\u{2F8F8}"=>"\u{21D0B}", "\u{2F8F9}"=>"\u{23AFA}",
- "\u{2F8FA}"=>"\u6C4E", "\u{2F8FB}"=>"\u{23CBC}", "\u{2F8FC}"=>"\u6CBF", "\u{2F8FD}"=>"\u6CCD", "\u{2F8FE}"=>"\u6C67", "\u{2F8FF}"=>"\u6D16", "\u{2F900}"=>"\u6D3E", "\u{2F901}"=>"\u6D77",
- "\u{2F902}"=>"\u6D41", "\u{2F903}"=>"\u6D69", "\u{2F904}"=>"\u6D78", "\u{2F905}"=>"\u6D85", "\u{2F906}"=>"\u{23D1E}", "\u{2F907}"=>"\u6D34", "\u{2F908}"=>"\u6E2F", "\u{2F909}"=>"\u6E6E",
- "\u{2F90A}"=>"\u3D33", "\u{2F90B}"=>"\u6ECB", "\u{2F90C}"=>"\u6EC7", "\u{2F90D}"=>"\u{23ED1}", "\u{2F90E}"=>"\u6DF9", "\u{2F90F}"=>"\u6F6E", "\u{2F910}"=>"\u{23F5E}", "\u{2F911}"=>"\u{23F8E}",
- "\u{2F912}"=>"\u6FC6", "\u{2F913}"=>"\u7039", "\u{2F914}"=>"\u701E", "\u{2F915}"=>"\u701B", "\u{2F916}"=>"\u3D96", "\u{2F917}"=>"\u704A", "\u{2F918}"=>"\u707D", "\u{2F919}"=>"\u7077",
- "\u{2F91A}"=>"\u70AD", "\u{2F91B}"=>"\u{20525}", "\u{2F91C}"=>"\u7145", "\u{2F91D}"=>"\u{24263}", "\u{2F91E}"=>"\u719C", "\u{2F91F}"=>"\u{243AB}", "\u{2F920}"=>"\u7228", "\u{2F921}"=>"\u7235",
- "\u{2F922}"=>"\u7250", "\u{2F923}"=>"\u{24608}", "\u{2F924}"=>"\u7280", "\u{2F925}"=>"\u7295", "\u{2F926}"=>"\u{24735}", "\u{2F927}"=>"\u{24814}", "\u{2F928}"=>"\u737A", "\u{2F929}"=>"\u738B",
- "\u{2F92A}"=>"\u3EAC", "\u{2F92B}"=>"\u73A5", "\u{2F92C}"=>"\u3EB8", "\u{2F92D}"=>"\u3EB8", "\u{2F92E}"=>"\u7447", "\u{2F92F}"=>"\u745C", "\u{2F930}"=>"\u7471", "\u{2F931}"=>"\u7485",
- "\u{2F932}"=>"\u74CA", "\u{2F933}"=>"\u3F1B", "\u{2F934}"=>"\u7524", "\u{2F935}"=>"\u{24C36}", "\u{2F936}"=>"\u753E", "\u{2F937}"=>"\u{24C92}", "\u{2F938}"=>"\u7570", "\u{2F939}"=>"\u{2219F}",
- "\u{2F93A}"=>"\u7610", "\u{2F93B}"=>"\u{24FA1}", "\u{2F93C}"=>"\u{24FB8}", "\u{2F93D}"=>"\u{25044}", "\u{2F93E}"=>"\u3FFC", "\u{2F93F}"=>"\u4008", "\u{2F940}"=>"\u76F4", "\u{2F941}"=>"\u{250F3}",
- "\u{2F942}"=>"\u{250F2}", "\u{2F943}"=>"\u{25119}", "\u{2F944}"=>"\u{25133}", "\u{2F945}"=>"\u771E", "\u{2F946}"=>"\u771F", "\u{2F947}"=>"\u771F", "\u{2F948}"=>"\u774A", "\u{2F949}"=>"\u4039",
- "\u{2F94A}"=>"\u778B", "\u{2F94B}"=>"\u4046", "\u{2F94C}"=>"\u4096", "\u{2F94D}"=>"\u{2541D}", "\u{2F94E}"=>"\u784E", "\u{2F94F}"=>"\u788C", "\u{2F950}"=>"\u78CC", "\u{2F951}"=>"\u40E3",
- "\u{2F952}"=>"\u{25626}", "\u{2F953}"=>"\u7956", "\u{2F954}"=>"\u{2569A}", "\u{2F955}"=>"\u{256C5}", "\u{2F956}"=>"\u798F", "\u{2F957}"=>"\u79EB", "\u{2F958}"=>"\u412F", "\u{2F959}"=>"\u7A40",
- "\u{2F95A}"=>"\u7A4A", "\u{2F95B}"=>"\u7A4F", "\u{2F95C}"=>"\u{2597C}", "\u{2F95D}"=>"\u{25AA7}", "\u{2F95E}"=>"\u{25AA7}", "\u{2F95F}"=>"\u7AEE", "\u{2F960}"=>"\u4202", "\u{2F961}"=>"\u{25BAB}",
- "\u{2F962}"=>"\u7BC6", "\u{2F963}"=>"\u7BC9", "\u{2F964}"=>"\u4227", "\u{2F965}"=>"\u{25C80}", "\u{2F966}"=>"\u7CD2", "\u{2F967}"=>"\u42A0", "\u{2F968}"=>"\u7CE8", "\u{2F969}"=>"\u7CE3",
- "\u{2F96A}"=>"\u7D00", "\u{2F96B}"=>"\u{25F86}", "\u{2F96C}"=>"\u7D63", "\u{2F96D}"=>"\u4301", "\u{2F96E}"=>"\u7DC7", "\u{2F96F}"=>"\u7E02", "\u{2F970}"=>"\u7E45", "\u{2F971}"=>"\u4334",
- "\u{2F972}"=>"\u{26228}", "\u{2F973}"=>"\u{26247}", "\u{2F974}"=>"\u4359", "\u{2F975}"=>"\u{262D9}", "\u{2F976}"=>"\u7F7A", "\u{2F977}"=>"\u{2633E}", "\u{2F978}"=>"\u7F95", "\u{2F979}"=>"\u7FFA",
- "\u{2F97A}"=>"\u8005", "\u{2F97B}"=>"\u{264DA}", "\u{2F97C}"=>"\u{26523}", "\u{2F97D}"=>"\u8060", "\u{2F97E}"=>"\u{265A8}", "\u{2F97F}"=>"\u8070", "\u{2F980}"=>"\u{2335F}", "\u{2F981}"=>"\u43D5",
- "\u{2F982}"=>"\u80B2", "\u{2F983}"=>"\u8103", "\u{2F984}"=>"\u440B", "\u{2F985}"=>"\u813E", "\u{2F986}"=>"\u5AB5", "\u{2F987}"=>"\u{267A7}", "\u{2F988}"=>"\u{267B5}", "\u{2F989}"=>"\u{23393}",
- "\u{2F98A}"=>"\u{2339C}", "\u{2F98B}"=>"\u8201", "\u{2F98C}"=>"\u8204", "\u{2F98D}"=>"\u8F9E", "\u{2F98E}"=>"\u446B", "\u{2F98F}"=>"\u8291", "\u{2F990}"=>"\u828B", "\u{2F991}"=>"\u829D",
- "\u{2F992}"=>"\u52B3", "\u{2F993}"=>"\u82B1", "\u{2F994}"=>"\u82B3", "\u{2F995}"=>"\u82BD", "\u{2F996}"=>"\u82E6", "\u{2F997}"=>"\u{26B3C}", "\u{2F998}"=>"\u82E5", "\u{2F999}"=>"\u831D",
- "\u{2F99A}"=>"\u8363", "\u{2F99B}"=>"\u83AD", "\u{2F99C}"=>"\u8323", "\u{2F99D}"=>"\u83BD", "\u{2F99E}"=>"\u83E7", "\u{2F99F}"=>"\u8457", "\u{2F9A0}"=>"\u8353", "\u{2F9A1}"=>"\u83CA",
- "\u{2F9A2}"=>"\u83CC", "\u{2F9A3}"=>"\u83DC", "\u{2F9A4}"=>"\u{26C36}", "\u{2F9A5}"=>"\u{26D6B}", "\u{2F9A6}"=>"\u{26CD5}", "\u{2F9A7}"=>"\u452B", "\u{2F9A8}"=>"\u84F1", "\u{2F9A9}"=>"\u84F3",
- "\u{2F9AA}"=>"\u8516", "\u{2F9AB}"=>"\u{273CA}", "\u{2F9AC}"=>"\u8564", "\u{2F9AD}"=>"\u{26F2C}", "\u{2F9AE}"=>"\u455D", "\u{2F9AF}"=>"\u4561", "\u{2F9B0}"=>"\u{26FB1}", "\u{2F9B1}"=>"\u{270D2}",
- "\u{2F9B2}"=>"\u456B", "\u{2F9B3}"=>"\u8650", "\u{2F9B4}"=>"\u865C", "\u{2F9B5}"=>"\u8667", "\u{2F9B6}"=>"\u8669", "\u{2F9B7}"=>"\u86A9", "\u{2F9B8}"=>"\u8688", "\u{2F9B9}"=>"\u870E",
- "\u{2F9BA}"=>"\u86E2", "\u{2F9BB}"=>"\u8779", "\u{2F9BC}"=>"\u8728", "\u{2F9BD}"=>"\u876B", "\u{2F9BE}"=>"\u8786", "\u{2F9BF}"=>"\u45D7", "\u{2F9C0}"=>"\u87E1", "\u{2F9C1}"=>"\u8801",
- "\u{2F9C2}"=>"\u45F9", "\u{2F9C3}"=>"\u8860", "\u{2F9C4}"=>"\u8863", "\u{2F9C5}"=>"\u{27667}", "\u{2F9C6}"=>"\u88D7", "\u{2F9C7}"=>"\u88DE", "\u{2F9C8}"=>"\u4635", "\u{2F9C9}"=>"\u88FA",
- "\u{2F9CA}"=>"\u34BB", "\u{2F9CB}"=>"\u{278AE}", "\u{2F9CC}"=>"\u{27966}", "\u{2F9CD}"=>"\u46BE", "\u{2F9CE}"=>"\u46C7", "\u{2F9CF}"=>"\u8AA0", "\u{2F9D0}"=>"\u8AED", "\u{2F9D1}"=>"\u8B8A",
- "\u{2F9D2}"=>"\u8C55", "\u{2F9D3}"=>"\u{27CA8}", "\u{2F9D4}"=>"\u8CAB", "\u{2F9D5}"=>"\u8CC1", "\u{2F9D6}"=>"\u8D1B", "\u{2F9D7}"=>"\u8D77", "\u{2F9D8}"=>"\u{27F2F}", "\u{2F9D9}"=>"\u{20804}",
- "\u{2F9DA}"=>"\u8DCB", "\u{2F9DB}"=>"\u8DBC", "\u{2F9DC}"=>"\u8DF0", "\u{2F9DD}"=>"\u{208DE}", "\u{2F9DE}"=>"\u8ED4", "\u{2F9DF}"=>"\u8F38", "\u{2F9E0}"=>"\u{285D2}", "\u{2F9E1}"=>"\u{285ED}",
- "\u{2F9E2}"=>"\u9094", "\u{2F9E3}"=>"\u90F1", "\u{2F9E4}"=>"\u9111", "\u{2F9E5}"=>"\u{2872E}", "\u{2F9E6}"=>"\u911B", "\u{2F9E7}"=>"\u9238", "\u{2F9E8}"=>"\u92D7", "\u{2F9E9}"=>"\u92D8",
- "\u{2F9EA}"=>"\u927C", "\u{2F9EB}"=>"\u93F9", "\u{2F9EC}"=>"\u9415", "\u{2F9ED}"=>"\u{28BFA}", "\u{2F9EE}"=>"\u958B", "\u{2F9EF}"=>"\u4995", "\u{2F9F0}"=>"\u95B7", "\u{2F9F1}"=>"\u{28D77}",
- "\u{2F9F2}"=>"\u49E6", "\u{2F9F3}"=>"\u96C3", "\u{2F9F4}"=>"\u5DB2", "\u{2F9F5}"=>"\u9723", "\u{2F9F6}"=>"\u{29145}", "\u{2F9F7}"=>"\u{2921A}", "\u{2F9F8}"=>"\u4A6E", "\u{2F9F9}"=>"\u4A76",
- "\u{2F9FA}"=>"\u97E0", "\u{2F9FB}"=>"\u{2940A}", "\u{2F9FC}"=>"\u4AB2", "\u{2F9FD}"=>"\u{29496}", "\u{2F9FE}"=>"\u980B", "\u{2F9FF}"=>"\u980B", "\u{2FA00}"=>"\u9829", "\u{2FA01}"=>"\u{295B6}",
- "\u{2FA02}"=>"\u98E2", "\u{2FA03}"=>"\u4B33", "\u{2FA04}"=>"\u9929", "\u{2FA05}"=>"\u99A7", "\u{2FA06}"=>"\u99C2", "\u{2FA07}"=>"\u99FE", "\u{2FA08}"=>"\u4BCE", "\u{2FA09}"=>"\u{29B30}",
- "\u{2FA0A}"=>"\u9B12", "\u{2FA0B}"=>"\u9C40", "\u{2FA0C}"=>"\u9CFD", "\u{2FA0D}"=>"\u4CCE", "\u{2FA0E}"=>"\u4CED", "\u{2FA0F}"=>"\u9D67", "\u{2FA10}"=>"\u{2A0CE}", "\u{2FA11}"=>"\u4CF8",
- "\u{2FA12}"=>"\u{2A105}", "\u{2FA13}"=>"\u{2A20E}", "\u{2FA14}"=>"\u{2A291}", "\u{2FA15}"=>"\u9EBB", "\u{2FA16}"=>"\u4D56", "\u{2FA17}"=>"\u9EF9", "\u{2FA18}"=>"\u9EFE", "\u{2FA19}"=>"\u9F05",
- "\u{2FA1A}"=>"\u9F0F", "\u{2FA1B}"=>"\u9F16", "\u{2FA1C}"=>"\u9F3B", "\u{2FA1D}"=>"\u{2A600}",
+ "\u00C0"=>"A\u0300",
+ "\u00C1"=>"A\u0301",
+ "\u00C2"=>"A\u0302",
+ "\u00C3"=>"A\u0303",
+ "\u00C4"=>"A\u0308",
+ "\u00C5"=>"A\u030A",
+ "\u00C7"=>"C\u0327",
+ "\u00C8"=>"E\u0300",
+ "\u00C9"=>"E\u0301",
+ "\u00CA"=>"E\u0302",
+ "\u00CB"=>"E\u0308",
+ "\u00CC"=>"I\u0300",
+ "\u00CD"=>"I\u0301",
+ "\u00CE"=>"I\u0302",
+ "\u00CF"=>"I\u0308",
+ "\u00D1"=>"N\u0303",
+ "\u00D2"=>"O\u0300",
+ "\u00D3"=>"O\u0301",
+ "\u00D4"=>"O\u0302",
+ "\u00D5"=>"O\u0303",
+ "\u00D6"=>"O\u0308",
+ "\u00D9"=>"U\u0300",
+ "\u00DA"=>"U\u0301",
+ "\u00DB"=>"U\u0302",
+ "\u00DC"=>"U\u0308",
+ "\u00DD"=>"Y\u0301",
+ "\u00E0"=>"a\u0300",
+ "\u00E1"=>"a\u0301",
+ "\u00E2"=>"a\u0302",
+ "\u00E3"=>"a\u0303",
+ "\u00E4"=>"a\u0308",
+ "\u00E5"=>"a\u030A",
+ "\u00E7"=>"c\u0327",
+ "\u00E8"=>"e\u0300",
+ "\u00E9"=>"e\u0301",
+ "\u00EA"=>"e\u0302",
+ "\u00EB"=>"e\u0308",
+ "\u00EC"=>"i\u0300",
+ "\u00ED"=>"i\u0301",
+ "\u00EE"=>"i\u0302",
+ "\u00EF"=>"i\u0308",
+ "\u00F1"=>"n\u0303",
+ "\u00F2"=>"o\u0300",
+ "\u00F3"=>"o\u0301",
+ "\u00F4"=>"o\u0302",
+ "\u00F5"=>"o\u0303",
+ "\u00F6"=>"o\u0308",
+ "\u00F9"=>"u\u0300",
+ "\u00FA"=>"u\u0301",
+ "\u00FB"=>"u\u0302",
+ "\u00FC"=>"u\u0308",
+ "\u00FD"=>"y\u0301",
+ "\u00FF"=>"y\u0308",
+ "\u0100"=>"A\u0304",
+ "\u0101"=>"a\u0304",
+ "\u0102"=>"A\u0306",
+ "\u0103"=>"a\u0306",
+ "\u0104"=>"A\u0328",
+ "\u0105"=>"a\u0328",
+ "\u0106"=>"C\u0301",
+ "\u0107"=>"c\u0301",
+ "\u0108"=>"C\u0302",
+ "\u0109"=>"c\u0302",
+ "\u010A"=>"C\u0307",
+ "\u010B"=>"c\u0307",
+ "\u010C"=>"C\u030C",
+ "\u010D"=>"c\u030C",
+ "\u010E"=>"D\u030C",
+ "\u010F"=>"d\u030C",
+ "\u0112"=>"E\u0304",
+ "\u0113"=>"e\u0304",
+ "\u0114"=>"E\u0306",
+ "\u0115"=>"e\u0306",
+ "\u0116"=>"E\u0307",
+ "\u0117"=>"e\u0307",
+ "\u0118"=>"E\u0328",
+ "\u0119"=>"e\u0328",
+ "\u011A"=>"E\u030C",
+ "\u011B"=>"e\u030C",
+ "\u011C"=>"G\u0302",
+ "\u011D"=>"g\u0302",
+ "\u011E"=>"G\u0306",
+ "\u011F"=>"g\u0306",
+ "\u0120"=>"G\u0307",
+ "\u0121"=>"g\u0307",
+ "\u0122"=>"G\u0327",
+ "\u0123"=>"g\u0327",
+ "\u0124"=>"H\u0302",
+ "\u0125"=>"h\u0302",
+ "\u0128"=>"I\u0303",
+ "\u0129"=>"i\u0303",
+ "\u012A"=>"I\u0304",
+ "\u012B"=>"i\u0304",
+ "\u012C"=>"I\u0306",
+ "\u012D"=>"i\u0306",
+ "\u012E"=>"I\u0328",
+ "\u012F"=>"i\u0328",
+ "\u0130"=>"I\u0307",
+ "\u0134"=>"J\u0302",
+ "\u0135"=>"j\u0302",
+ "\u0136"=>"K\u0327",
+ "\u0137"=>"k\u0327",
+ "\u0139"=>"L\u0301",
+ "\u013A"=>"l\u0301",
+ "\u013B"=>"L\u0327",
+ "\u013C"=>"l\u0327",
+ "\u013D"=>"L\u030C",
+ "\u013E"=>"l\u030C",
+ "\u0143"=>"N\u0301",
+ "\u0144"=>"n\u0301",
+ "\u0145"=>"N\u0327",
+ "\u0146"=>"n\u0327",
+ "\u0147"=>"N\u030C",
+ "\u0148"=>"n\u030C",
+ "\u014C"=>"O\u0304",
+ "\u014D"=>"o\u0304",
+ "\u014E"=>"O\u0306",
+ "\u014F"=>"o\u0306",
+ "\u0150"=>"O\u030B",
+ "\u0151"=>"o\u030B",
+ "\u0154"=>"R\u0301",
+ "\u0155"=>"r\u0301",
+ "\u0156"=>"R\u0327",
+ "\u0157"=>"r\u0327",
+ "\u0158"=>"R\u030C",
+ "\u0159"=>"r\u030C",
+ "\u015A"=>"S\u0301",
+ "\u015B"=>"s\u0301",
+ "\u015C"=>"S\u0302",
+ "\u015D"=>"s\u0302",
+ "\u015E"=>"S\u0327",
+ "\u015F"=>"s\u0327",
+ "\u0160"=>"S\u030C",
+ "\u0161"=>"s\u030C",
+ "\u0162"=>"T\u0327",
+ "\u0163"=>"t\u0327",
+ "\u0164"=>"T\u030C",
+ "\u0165"=>"t\u030C",
+ "\u0168"=>"U\u0303",
+ "\u0169"=>"u\u0303",
+ "\u016A"=>"U\u0304",
+ "\u016B"=>"u\u0304",
+ "\u016C"=>"U\u0306",
+ "\u016D"=>"u\u0306",
+ "\u016E"=>"U\u030A",
+ "\u016F"=>"u\u030A",
+ "\u0170"=>"U\u030B",
+ "\u0171"=>"u\u030B",
+ "\u0172"=>"U\u0328",
+ "\u0173"=>"u\u0328",
+ "\u0174"=>"W\u0302",
+ "\u0175"=>"w\u0302",
+ "\u0176"=>"Y\u0302",
+ "\u0177"=>"y\u0302",
+ "\u0178"=>"Y\u0308",
+ "\u0179"=>"Z\u0301",
+ "\u017A"=>"z\u0301",
+ "\u017B"=>"Z\u0307",
+ "\u017C"=>"z\u0307",
+ "\u017D"=>"Z\u030C",
+ "\u017E"=>"z\u030C",
+ "\u01A0"=>"O\u031B",
+ "\u01A1"=>"o\u031B",
+ "\u01AF"=>"U\u031B",
+ "\u01B0"=>"u\u031B",
+ "\u01CD"=>"A\u030C",
+ "\u01CE"=>"a\u030C",
+ "\u01CF"=>"I\u030C",
+ "\u01D0"=>"i\u030C",
+ "\u01D1"=>"O\u030C",
+ "\u01D2"=>"o\u030C",
+ "\u01D3"=>"U\u030C",
+ "\u01D4"=>"u\u030C",
+ "\u01D5"=>"U\u0308\u0304",
+ "\u01D6"=>"u\u0308\u0304",
+ "\u01D7"=>"U\u0308\u0301",
+ "\u01D8"=>"u\u0308\u0301",
+ "\u01D9"=>"U\u0308\u030C",
+ "\u01DA"=>"u\u0308\u030C",
+ "\u01DB"=>"U\u0308\u0300",
+ "\u01DC"=>"u\u0308\u0300",
+ "\u01DE"=>"A\u0308\u0304",
+ "\u01DF"=>"a\u0308\u0304",
+ "\u01E0"=>"A\u0307\u0304",
+ "\u01E1"=>"a\u0307\u0304",
+ "\u01E2"=>"\u00C6\u0304",
+ "\u01E3"=>"\u00E6\u0304",
+ "\u01E6"=>"G\u030C",
+ "\u01E7"=>"g\u030C",
+ "\u01E8"=>"K\u030C",
+ "\u01E9"=>"k\u030C",
+ "\u01EA"=>"O\u0328",
+ "\u01EB"=>"o\u0328",
+ "\u01EC"=>"O\u0328\u0304",
+ "\u01ED"=>"o\u0328\u0304",
+ "\u01EE"=>"\u01B7\u030C",
+ "\u01EF"=>"\u0292\u030C",
+ "\u01F0"=>"j\u030C",
+ "\u01F4"=>"G\u0301",
+ "\u01F5"=>"g\u0301",
+ "\u01F8"=>"N\u0300",
+ "\u01F9"=>"n\u0300",
+ "\u01FA"=>"A\u030A\u0301",
+ "\u01FB"=>"a\u030A\u0301",
+ "\u01FC"=>"\u00C6\u0301",
+ "\u01FD"=>"\u00E6\u0301",
+ "\u01FE"=>"\u00D8\u0301",
+ "\u01FF"=>"\u00F8\u0301",
+ "\u0200"=>"A\u030F",
+ "\u0201"=>"a\u030F",
+ "\u0202"=>"A\u0311",
+ "\u0203"=>"a\u0311",
+ "\u0204"=>"E\u030F",
+ "\u0205"=>"e\u030F",
+ "\u0206"=>"E\u0311",
+ "\u0207"=>"e\u0311",
+ "\u0208"=>"I\u030F",
+ "\u0209"=>"i\u030F",
+ "\u020A"=>"I\u0311",
+ "\u020B"=>"i\u0311",
+ "\u020C"=>"O\u030F",
+ "\u020D"=>"o\u030F",
+ "\u020E"=>"O\u0311",
+ "\u020F"=>"o\u0311",
+ "\u0210"=>"R\u030F",
+ "\u0211"=>"r\u030F",
+ "\u0212"=>"R\u0311",
+ "\u0213"=>"r\u0311",
+ "\u0214"=>"U\u030F",
+ "\u0215"=>"u\u030F",
+ "\u0216"=>"U\u0311",
+ "\u0217"=>"u\u0311",
+ "\u0218"=>"S\u0326",
+ "\u0219"=>"s\u0326",
+ "\u021A"=>"T\u0326",
+ "\u021B"=>"t\u0326",
+ "\u021E"=>"H\u030C",
+ "\u021F"=>"h\u030C",
+ "\u0226"=>"A\u0307",
+ "\u0227"=>"a\u0307",
+ "\u0228"=>"E\u0327",
+ "\u0229"=>"e\u0327",
+ "\u022A"=>"O\u0308\u0304",
+ "\u022B"=>"o\u0308\u0304",
+ "\u022C"=>"O\u0303\u0304",
+ "\u022D"=>"o\u0303\u0304",
+ "\u022E"=>"O\u0307",
+ "\u022F"=>"o\u0307",
+ "\u0230"=>"O\u0307\u0304",
+ "\u0231"=>"o\u0307\u0304",
+ "\u0232"=>"Y\u0304",
+ "\u0233"=>"y\u0304",
+ "\u0340"=>"\u0300",
+ "\u0341"=>"\u0301",
+ "\u0343"=>"\u0313",
+ "\u0344"=>"\u0308\u0301",
+ "\u0374"=>"\u02B9",
+ "\u037E"=>";",
+ "\u0385"=>"\u00A8\u0301",
+ "\u0386"=>"\u0391\u0301",
+ "\u0387"=>"\u00B7",
+ "\u0388"=>"\u0395\u0301",
+ "\u0389"=>"\u0397\u0301",
+ "\u038A"=>"\u0399\u0301",
+ "\u038C"=>"\u039F\u0301",
+ "\u038E"=>"\u03A5\u0301",
+ "\u038F"=>"\u03A9\u0301",
+ "\u0390"=>"\u03B9\u0308\u0301",
+ "\u03AA"=>"\u0399\u0308",
+ "\u03AB"=>"\u03A5\u0308",
+ "\u03AC"=>"\u03B1\u0301",
+ "\u03AD"=>"\u03B5\u0301",
+ "\u03AE"=>"\u03B7\u0301",
+ "\u03AF"=>"\u03B9\u0301",
+ "\u03B0"=>"\u03C5\u0308\u0301",
+ "\u03CA"=>"\u03B9\u0308",
+ "\u03CB"=>"\u03C5\u0308",
+ "\u03CC"=>"\u03BF\u0301",
+ "\u03CD"=>"\u03C5\u0301",
+ "\u03CE"=>"\u03C9\u0301",
+ "\u03D3"=>"\u03D2\u0301",
+ "\u03D4"=>"\u03D2\u0308",
+ "\u0400"=>"\u0415\u0300",
+ "\u0401"=>"\u0415\u0308",
+ "\u0403"=>"\u0413\u0301",
+ "\u0407"=>"\u0406\u0308",
+ "\u040C"=>"\u041A\u0301",
+ "\u040D"=>"\u0418\u0300",
+ "\u040E"=>"\u0423\u0306",
+ "\u0419"=>"\u0418\u0306",
+ "\u0439"=>"\u0438\u0306",
+ "\u0450"=>"\u0435\u0300",
+ "\u0451"=>"\u0435\u0308",
+ "\u0453"=>"\u0433\u0301",
+ "\u0457"=>"\u0456\u0308",
+ "\u045C"=>"\u043A\u0301",
+ "\u045D"=>"\u0438\u0300",
+ "\u045E"=>"\u0443\u0306",
+ "\u0476"=>"\u0474\u030F",
+ "\u0477"=>"\u0475\u030F",
+ "\u04C1"=>"\u0416\u0306",
+ "\u04C2"=>"\u0436\u0306",
+ "\u04D0"=>"\u0410\u0306",
+ "\u04D1"=>"\u0430\u0306",
+ "\u04D2"=>"\u0410\u0308",
+ "\u04D3"=>"\u0430\u0308",
+ "\u04D6"=>"\u0415\u0306",
+ "\u04D7"=>"\u0435\u0306",
+ "\u04DA"=>"\u04D8\u0308",
+ "\u04DB"=>"\u04D9\u0308",
+ "\u04DC"=>"\u0416\u0308",
+ "\u04DD"=>"\u0436\u0308",
+ "\u04DE"=>"\u0417\u0308",
+ "\u04DF"=>"\u0437\u0308",
+ "\u04E2"=>"\u0418\u0304",
+ "\u04E3"=>"\u0438\u0304",
+ "\u04E4"=>"\u0418\u0308",
+ "\u04E5"=>"\u0438\u0308",
+ "\u04E6"=>"\u041E\u0308",
+ "\u04E7"=>"\u043E\u0308",
+ "\u04EA"=>"\u04E8\u0308",
+ "\u04EB"=>"\u04E9\u0308",
+ "\u04EC"=>"\u042D\u0308",
+ "\u04ED"=>"\u044D\u0308",
+ "\u04EE"=>"\u0423\u0304",
+ "\u04EF"=>"\u0443\u0304",
+ "\u04F0"=>"\u0423\u0308",
+ "\u04F1"=>"\u0443\u0308",
+ "\u04F2"=>"\u0423\u030B",
+ "\u04F3"=>"\u0443\u030B",
+ "\u04F4"=>"\u0427\u0308",
+ "\u04F5"=>"\u0447\u0308",
+ "\u04F8"=>"\u042B\u0308",
+ "\u04F9"=>"\u044B\u0308",
+ "\u0622"=>"\u0627\u0653",
+ "\u0623"=>"\u0627\u0654",
+ "\u0624"=>"\u0648\u0654",
+ "\u0625"=>"\u0627\u0655",
+ "\u0626"=>"\u064A\u0654",
+ "\u06C0"=>"\u06D5\u0654",
+ "\u06C2"=>"\u06C1\u0654",
+ "\u06D3"=>"\u06D2\u0654",
+ "\u0929"=>"\u0928\u093C",
+ "\u0931"=>"\u0930\u093C",
+ "\u0934"=>"\u0933\u093C",
+ "\u0958"=>"\u0915\u093C",
+ "\u0959"=>"\u0916\u093C",
+ "\u095A"=>"\u0917\u093C",
+ "\u095B"=>"\u091C\u093C",
+ "\u095C"=>"\u0921\u093C",
+ "\u095D"=>"\u0922\u093C",
+ "\u095E"=>"\u092B\u093C",
+ "\u095F"=>"\u092F\u093C",
+ "\u09CB"=>"\u09C7\u09BE",
+ "\u09CC"=>"\u09C7\u09D7",
+ "\u09DC"=>"\u09A1\u09BC",
+ "\u09DD"=>"\u09A2\u09BC",
+ "\u09DF"=>"\u09AF\u09BC",
+ "\u0A33"=>"\u0A32\u0A3C",
+ "\u0A36"=>"\u0A38\u0A3C",
+ "\u0A59"=>"\u0A16\u0A3C",
+ "\u0A5A"=>"\u0A17\u0A3C",
+ "\u0A5B"=>"\u0A1C\u0A3C",
+ "\u0A5E"=>"\u0A2B\u0A3C",
+ "\u0B48"=>"\u0B47\u0B56",
+ "\u0B4B"=>"\u0B47\u0B3E",
+ "\u0B4C"=>"\u0B47\u0B57",
+ "\u0B5C"=>"\u0B21\u0B3C",
+ "\u0B5D"=>"\u0B22\u0B3C",
+ "\u0B94"=>"\u0B92\u0BD7",
+ "\u0BCA"=>"\u0BC6\u0BBE",
+ "\u0BCB"=>"\u0BC7\u0BBE",
+ "\u0BCC"=>"\u0BC6\u0BD7",
+ "\u0C48"=>"\u0C46\u0C56",
+ "\u0CC0"=>"\u0CBF\u0CD5",
+ "\u0CC7"=>"\u0CC6\u0CD5",
+ "\u0CC8"=>"\u0CC6\u0CD6",
+ "\u0CCA"=>"\u0CC6\u0CC2",
+ "\u0CCB"=>"\u0CC6\u0CC2\u0CD5",
+ "\u0D4A"=>"\u0D46\u0D3E",
+ "\u0D4B"=>"\u0D47\u0D3E",
+ "\u0D4C"=>"\u0D46\u0D57",
+ "\u0DDA"=>"\u0DD9\u0DCA",
+ "\u0DDC"=>"\u0DD9\u0DCF",
+ "\u0DDD"=>"\u0DD9\u0DCF\u0DCA",
+ "\u0DDE"=>"\u0DD9\u0DDF",
+ "\u0F43"=>"\u0F42\u0FB7",
+ "\u0F4D"=>"\u0F4C\u0FB7",
+ "\u0F52"=>"\u0F51\u0FB7",
+ "\u0F57"=>"\u0F56\u0FB7",
+ "\u0F5C"=>"\u0F5B\u0FB7",
+ "\u0F69"=>"\u0F40\u0FB5",
+ "\u0F73"=>"\u0F71\u0F72",
+ "\u0F75"=>"\u0F71\u0F74",
+ "\u0F76"=>"\u0FB2\u0F80",
+ "\u0F78"=>"\u0FB3\u0F80",
+ "\u0F81"=>"\u0F71\u0F80",
+ "\u0F93"=>"\u0F92\u0FB7",
+ "\u0F9D"=>"\u0F9C\u0FB7",
+ "\u0FA2"=>"\u0FA1\u0FB7",
+ "\u0FA7"=>"\u0FA6\u0FB7",
+ "\u0FAC"=>"\u0FAB\u0FB7",
+ "\u0FB9"=>"\u0F90\u0FB5",
+ "\u1026"=>"\u1025\u102E",
+ "\u1B06"=>"\u1B05\u1B35",
+ "\u1B08"=>"\u1B07\u1B35",
+ "\u1B0A"=>"\u1B09\u1B35",
+ "\u1B0C"=>"\u1B0B\u1B35",
+ "\u1B0E"=>"\u1B0D\u1B35",
+ "\u1B12"=>"\u1B11\u1B35",
+ "\u1B3B"=>"\u1B3A\u1B35",
+ "\u1B3D"=>"\u1B3C\u1B35",
+ "\u1B40"=>"\u1B3E\u1B35",
+ "\u1B41"=>"\u1B3F\u1B35",
+ "\u1B43"=>"\u1B42\u1B35",
+ "\u1E00"=>"A\u0325",
+ "\u1E01"=>"a\u0325",
+ "\u1E02"=>"B\u0307",
+ "\u1E03"=>"b\u0307",
+ "\u1E04"=>"B\u0323",
+ "\u1E05"=>"b\u0323",
+ "\u1E06"=>"B\u0331",
+ "\u1E07"=>"b\u0331",
+ "\u1E08"=>"C\u0327\u0301",
+ "\u1E09"=>"c\u0327\u0301",
+ "\u1E0A"=>"D\u0307",
+ "\u1E0B"=>"d\u0307",
+ "\u1E0C"=>"D\u0323",
+ "\u1E0D"=>"d\u0323",
+ "\u1E0E"=>"D\u0331",
+ "\u1E0F"=>"d\u0331",
+ "\u1E10"=>"D\u0327",
+ "\u1E11"=>"d\u0327",
+ "\u1E12"=>"D\u032D",
+ "\u1E13"=>"d\u032D",
+ "\u1E14"=>"E\u0304\u0300",
+ "\u1E15"=>"e\u0304\u0300",
+ "\u1E16"=>"E\u0304\u0301",
+ "\u1E17"=>"e\u0304\u0301",
+ "\u1E18"=>"E\u032D",
+ "\u1E19"=>"e\u032D",
+ "\u1E1A"=>"E\u0330",
+ "\u1E1B"=>"e\u0330",
+ "\u1E1C"=>"E\u0327\u0306",
+ "\u1E1D"=>"e\u0327\u0306",
+ "\u1E1E"=>"F\u0307",
+ "\u1E1F"=>"f\u0307",
+ "\u1E20"=>"G\u0304",
+ "\u1E21"=>"g\u0304",
+ "\u1E22"=>"H\u0307",
+ "\u1E23"=>"h\u0307",
+ "\u1E24"=>"H\u0323",
+ "\u1E25"=>"h\u0323",
+ "\u1E26"=>"H\u0308",
+ "\u1E27"=>"h\u0308",
+ "\u1E28"=>"H\u0327",
+ "\u1E29"=>"h\u0327",
+ "\u1E2A"=>"H\u032E",
+ "\u1E2B"=>"h\u032E",
+ "\u1E2C"=>"I\u0330",
+ "\u1E2D"=>"i\u0330",
+ "\u1E2E"=>"I\u0308\u0301",
+ "\u1E2F"=>"i\u0308\u0301",
+ "\u1E30"=>"K\u0301",
+ "\u1E31"=>"k\u0301",
+ "\u1E32"=>"K\u0323",
+ "\u1E33"=>"k\u0323",
+ "\u1E34"=>"K\u0331",
+ "\u1E35"=>"k\u0331",
+ "\u1E36"=>"L\u0323",
+ "\u1E37"=>"l\u0323",
+ "\u1E38"=>"L\u0323\u0304",
+ "\u1E39"=>"l\u0323\u0304",
+ "\u1E3A"=>"L\u0331",
+ "\u1E3B"=>"l\u0331",
+ "\u1E3C"=>"L\u032D",
+ "\u1E3D"=>"l\u032D",
+ "\u1E3E"=>"M\u0301",
+ "\u1E3F"=>"m\u0301",
+ "\u1E40"=>"M\u0307",
+ "\u1E41"=>"m\u0307",
+ "\u1E42"=>"M\u0323",
+ "\u1E43"=>"m\u0323",
+ "\u1E44"=>"N\u0307",
+ "\u1E45"=>"n\u0307",
+ "\u1E46"=>"N\u0323",
+ "\u1E47"=>"n\u0323",
+ "\u1E48"=>"N\u0331",
+ "\u1E49"=>"n\u0331",
+ "\u1E4A"=>"N\u032D",
+ "\u1E4B"=>"n\u032D",
+ "\u1E4C"=>"O\u0303\u0301",
+ "\u1E4D"=>"o\u0303\u0301",
+ "\u1E4E"=>"O\u0303\u0308",
+ "\u1E4F"=>"o\u0303\u0308",
+ "\u1E50"=>"O\u0304\u0300",
+ "\u1E51"=>"o\u0304\u0300",
+ "\u1E52"=>"O\u0304\u0301",
+ "\u1E53"=>"o\u0304\u0301",
+ "\u1E54"=>"P\u0301",
+ "\u1E55"=>"p\u0301",
+ "\u1E56"=>"P\u0307",
+ "\u1E57"=>"p\u0307",
+ "\u1E58"=>"R\u0307",
+ "\u1E59"=>"r\u0307",
+ "\u1E5A"=>"R\u0323",
+ "\u1E5B"=>"r\u0323",
+ "\u1E5C"=>"R\u0323\u0304",
+ "\u1E5D"=>"r\u0323\u0304",
+ "\u1E5E"=>"R\u0331",
+ "\u1E5F"=>"r\u0331",
+ "\u1E60"=>"S\u0307",
+ "\u1E61"=>"s\u0307",
+ "\u1E62"=>"S\u0323",
+ "\u1E63"=>"s\u0323",
+ "\u1E64"=>"S\u0301\u0307",
+ "\u1E65"=>"s\u0301\u0307",
+ "\u1E66"=>"S\u030C\u0307",
+ "\u1E67"=>"s\u030C\u0307",
+ "\u1E68"=>"S\u0323\u0307",
+ "\u1E69"=>"s\u0323\u0307",
+ "\u1E6A"=>"T\u0307",
+ "\u1E6B"=>"t\u0307",
+ "\u1E6C"=>"T\u0323",
+ "\u1E6D"=>"t\u0323",
+ "\u1E6E"=>"T\u0331",
+ "\u1E6F"=>"t\u0331",
+ "\u1E70"=>"T\u032D",
+ "\u1E71"=>"t\u032D",
+ "\u1E72"=>"U\u0324",
+ "\u1E73"=>"u\u0324",
+ "\u1E74"=>"U\u0330",
+ "\u1E75"=>"u\u0330",
+ "\u1E76"=>"U\u032D",
+ "\u1E77"=>"u\u032D",
+ "\u1E78"=>"U\u0303\u0301",
+ "\u1E79"=>"u\u0303\u0301",
+ "\u1E7A"=>"U\u0304\u0308",
+ "\u1E7B"=>"u\u0304\u0308",
+ "\u1E7C"=>"V\u0303",
+ "\u1E7D"=>"v\u0303",
+ "\u1E7E"=>"V\u0323",
+ "\u1E7F"=>"v\u0323",
+ "\u1E80"=>"W\u0300",
+ "\u1E81"=>"w\u0300",
+ "\u1E82"=>"W\u0301",
+ "\u1E83"=>"w\u0301",
+ "\u1E84"=>"W\u0308",
+ "\u1E85"=>"w\u0308",
+ "\u1E86"=>"W\u0307",
+ "\u1E87"=>"w\u0307",
+ "\u1E88"=>"W\u0323",
+ "\u1E89"=>"w\u0323",
+ "\u1E8A"=>"X\u0307",
+ "\u1E8B"=>"x\u0307",
+ "\u1E8C"=>"X\u0308",
+ "\u1E8D"=>"x\u0308",
+ "\u1E8E"=>"Y\u0307",
+ "\u1E8F"=>"y\u0307",
+ "\u1E90"=>"Z\u0302",
+ "\u1E91"=>"z\u0302",
+ "\u1E92"=>"Z\u0323",
+ "\u1E93"=>"z\u0323",
+ "\u1E94"=>"Z\u0331",
+ "\u1E95"=>"z\u0331",
+ "\u1E96"=>"h\u0331",
+ "\u1E97"=>"t\u0308",
+ "\u1E98"=>"w\u030A",
+ "\u1E99"=>"y\u030A",
+ "\u1E9B"=>"\u017F\u0307",
+ "\u1EA0"=>"A\u0323",
+ "\u1EA1"=>"a\u0323",
+ "\u1EA2"=>"A\u0309",
+ "\u1EA3"=>"a\u0309",
+ "\u1EA4"=>"A\u0302\u0301",
+ "\u1EA5"=>"a\u0302\u0301",
+ "\u1EA6"=>"A\u0302\u0300",
+ "\u1EA7"=>"a\u0302\u0300",
+ "\u1EA8"=>"A\u0302\u0309",
+ "\u1EA9"=>"a\u0302\u0309",
+ "\u1EAA"=>"A\u0302\u0303",
+ "\u1EAB"=>"a\u0302\u0303",
+ "\u1EAC"=>"A\u0323\u0302",
+ "\u1EAD"=>"a\u0323\u0302",
+ "\u1EAE"=>"A\u0306\u0301",
+ "\u1EAF"=>"a\u0306\u0301",
+ "\u1EB0"=>"A\u0306\u0300",
+ "\u1EB1"=>"a\u0306\u0300",
+ "\u1EB2"=>"A\u0306\u0309",
+ "\u1EB3"=>"a\u0306\u0309",
+ "\u1EB4"=>"A\u0306\u0303",
+ "\u1EB5"=>"a\u0306\u0303",
+ "\u1EB6"=>"A\u0323\u0306",
+ "\u1EB7"=>"a\u0323\u0306",
+ "\u1EB8"=>"E\u0323",
+ "\u1EB9"=>"e\u0323",
+ "\u1EBA"=>"E\u0309",
+ "\u1EBB"=>"e\u0309",
+ "\u1EBC"=>"E\u0303",
+ "\u1EBD"=>"e\u0303",
+ "\u1EBE"=>"E\u0302\u0301",
+ "\u1EBF"=>"e\u0302\u0301",
+ "\u1EC0"=>"E\u0302\u0300",
+ "\u1EC1"=>"e\u0302\u0300",
+ "\u1EC2"=>"E\u0302\u0309",
+ "\u1EC3"=>"e\u0302\u0309",
+ "\u1EC4"=>"E\u0302\u0303",
+ "\u1EC5"=>"e\u0302\u0303",
+ "\u1EC6"=>"E\u0323\u0302",
+ "\u1EC7"=>"e\u0323\u0302",
+ "\u1EC8"=>"I\u0309",
+ "\u1EC9"=>"i\u0309",
+ "\u1ECA"=>"I\u0323",
+ "\u1ECB"=>"i\u0323",
+ "\u1ECC"=>"O\u0323",
+ "\u1ECD"=>"o\u0323",
+ "\u1ECE"=>"O\u0309",
+ "\u1ECF"=>"o\u0309",
+ "\u1ED0"=>"O\u0302\u0301",
+ "\u1ED1"=>"o\u0302\u0301",
+ "\u1ED2"=>"O\u0302\u0300",
+ "\u1ED3"=>"o\u0302\u0300",
+ "\u1ED4"=>"O\u0302\u0309",
+ "\u1ED5"=>"o\u0302\u0309",
+ "\u1ED6"=>"O\u0302\u0303",
+ "\u1ED7"=>"o\u0302\u0303",
+ "\u1ED8"=>"O\u0323\u0302",
+ "\u1ED9"=>"o\u0323\u0302",
+ "\u1EDA"=>"O\u031B\u0301",
+ "\u1EDB"=>"o\u031B\u0301",
+ "\u1EDC"=>"O\u031B\u0300",
+ "\u1EDD"=>"o\u031B\u0300",
+ "\u1EDE"=>"O\u031B\u0309",
+ "\u1EDF"=>"o\u031B\u0309",
+ "\u1EE0"=>"O\u031B\u0303",
+ "\u1EE1"=>"o\u031B\u0303",
+ "\u1EE2"=>"O\u031B\u0323",
+ "\u1EE3"=>"o\u031B\u0323",
+ "\u1EE4"=>"U\u0323",
+ "\u1EE5"=>"u\u0323",
+ "\u1EE6"=>"U\u0309",
+ "\u1EE7"=>"u\u0309",
+ "\u1EE8"=>"U\u031B\u0301",
+ "\u1EE9"=>"u\u031B\u0301",
+ "\u1EEA"=>"U\u031B\u0300",
+ "\u1EEB"=>"u\u031B\u0300",
+ "\u1EEC"=>"U\u031B\u0309",
+ "\u1EED"=>"u\u031B\u0309",
+ "\u1EEE"=>"U\u031B\u0303",
+ "\u1EEF"=>"u\u031B\u0303",
+ "\u1EF0"=>"U\u031B\u0323",
+ "\u1EF1"=>"u\u031B\u0323",
+ "\u1EF2"=>"Y\u0300",
+ "\u1EF3"=>"y\u0300",
+ "\u1EF4"=>"Y\u0323",
+ "\u1EF5"=>"y\u0323",
+ "\u1EF6"=>"Y\u0309",
+ "\u1EF7"=>"y\u0309",
+ "\u1EF8"=>"Y\u0303",
+ "\u1EF9"=>"y\u0303",
+ "\u1F00"=>"\u03B1\u0313",
+ "\u1F01"=>"\u03B1\u0314",
+ "\u1F02"=>"\u03B1\u0313\u0300",
+ "\u1F03"=>"\u03B1\u0314\u0300",
+ "\u1F04"=>"\u03B1\u0313\u0301",
+ "\u1F05"=>"\u03B1\u0314\u0301",
+ "\u1F06"=>"\u03B1\u0313\u0342",
+ "\u1F07"=>"\u03B1\u0314\u0342",
+ "\u1F08"=>"\u0391\u0313",
+ "\u1F09"=>"\u0391\u0314",
+ "\u1F0A"=>"\u0391\u0313\u0300",
+ "\u1F0B"=>"\u0391\u0314\u0300",
+ "\u1F0C"=>"\u0391\u0313\u0301",
+ "\u1F0D"=>"\u0391\u0314\u0301",
+ "\u1F0E"=>"\u0391\u0313\u0342",
+ "\u1F0F"=>"\u0391\u0314\u0342",
+ "\u1F10"=>"\u03B5\u0313",
+ "\u1F11"=>"\u03B5\u0314",
+ "\u1F12"=>"\u03B5\u0313\u0300",
+ "\u1F13"=>"\u03B5\u0314\u0300",
+ "\u1F14"=>"\u03B5\u0313\u0301",
+ "\u1F15"=>"\u03B5\u0314\u0301",
+ "\u1F18"=>"\u0395\u0313",
+ "\u1F19"=>"\u0395\u0314",
+ "\u1F1A"=>"\u0395\u0313\u0300",
+ "\u1F1B"=>"\u0395\u0314\u0300",
+ "\u1F1C"=>"\u0395\u0313\u0301",
+ "\u1F1D"=>"\u0395\u0314\u0301",
+ "\u1F20"=>"\u03B7\u0313",
+ "\u1F21"=>"\u03B7\u0314",
+ "\u1F22"=>"\u03B7\u0313\u0300",
+ "\u1F23"=>"\u03B7\u0314\u0300",
+ "\u1F24"=>"\u03B7\u0313\u0301",
+ "\u1F25"=>"\u03B7\u0314\u0301",
+ "\u1F26"=>"\u03B7\u0313\u0342",
+ "\u1F27"=>"\u03B7\u0314\u0342",
+ "\u1F28"=>"\u0397\u0313",
+ "\u1F29"=>"\u0397\u0314",
+ "\u1F2A"=>"\u0397\u0313\u0300",
+ "\u1F2B"=>"\u0397\u0314\u0300",
+ "\u1F2C"=>"\u0397\u0313\u0301",
+ "\u1F2D"=>"\u0397\u0314\u0301",
+ "\u1F2E"=>"\u0397\u0313\u0342",
+ "\u1F2F"=>"\u0397\u0314\u0342",
+ "\u1F30"=>"\u03B9\u0313",
+ "\u1F31"=>"\u03B9\u0314",
+ "\u1F32"=>"\u03B9\u0313\u0300",
+ "\u1F33"=>"\u03B9\u0314\u0300",
+ "\u1F34"=>"\u03B9\u0313\u0301",
+ "\u1F35"=>"\u03B9\u0314\u0301",
+ "\u1F36"=>"\u03B9\u0313\u0342",
+ "\u1F37"=>"\u03B9\u0314\u0342",
+ "\u1F38"=>"\u0399\u0313",
+ "\u1F39"=>"\u0399\u0314",
+ "\u1F3A"=>"\u0399\u0313\u0300",
+ "\u1F3B"=>"\u0399\u0314\u0300",
+ "\u1F3C"=>"\u0399\u0313\u0301",
+ "\u1F3D"=>"\u0399\u0314\u0301",
+ "\u1F3E"=>"\u0399\u0313\u0342",
+ "\u1F3F"=>"\u0399\u0314\u0342",
+ "\u1F40"=>"\u03BF\u0313",
+ "\u1F41"=>"\u03BF\u0314",
+ "\u1F42"=>"\u03BF\u0313\u0300",
+ "\u1F43"=>"\u03BF\u0314\u0300",
+ "\u1F44"=>"\u03BF\u0313\u0301",
+ "\u1F45"=>"\u03BF\u0314\u0301",
+ "\u1F48"=>"\u039F\u0313",
+ "\u1F49"=>"\u039F\u0314",
+ "\u1F4A"=>"\u039F\u0313\u0300",
+ "\u1F4B"=>"\u039F\u0314\u0300",
+ "\u1F4C"=>"\u039F\u0313\u0301",
+ "\u1F4D"=>"\u039F\u0314\u0301",
+ "\u1F50"=>"\u03C5\u0313",
+ "\u1F51"=>"\u03C5\u0314",
+ "\u1F52"=>"\u03C5\u0313\u0300",
+ "\u1F53"=>"\u03C5\u0314\u0300",
+ "\u1F54"=>"\u03C5\u0313\u0301",
+ "\u1F55"=>"\u03C5\u0314\u0301",
+ "\u1F56"=>"\u03C5\u0313\u0342",
+ "\u1F57"=>"\u03C5\u0314\u0342",
+ "\u1F59"=>"\u03A5\u0314",
+ "\u1F5B"=>"\u03A5\u0314\u0300",
+ "\u1F5D"=>"\u03A5\u0314\u0301",
+ "\u1F5F"=>"\u03A5\u0314\u0342",
+ "\u1F60"=>"\u03C9\u0313",
+ "\u1F61"=>"\u03C9\u0314",
+ "\u1F62"=>"\u03C9\u0313\u0300",
+ "\u1F63"=>"\u03C9\u0314\u0300",
+ "\u1F64"=>"\u03C9\u0313\u0301",
+ "\u1F65"=>"\u03C9\u0314\u0301",
+ "\u1F66"=>"\u03C9\u0313\u0342",
+ "\u1F67"=>"\u03C9\u0314\u0342",
+ "\u1F68"=>"\u03A9\u0313",
+ "\u1F69"=>"\u03A9\u0314",
+ "\u1F6A"=>"\u03A9\u0313\u0300",
+ "\u1F6B"=>"\u03A9\u0314\u0300",
+ "\u1F6C"=>"\u03A9\u0313\u0301",
+ "\u1F6D"=>"\u03A9\u0314\u0301",
+ "\u1F6E"=>"\u03A9\u0313\u0342",
+ "\u1F6F"=>"\u03A9\u0314\u0342",
+ "\u1F70"=>"\u03B1\u0300",
+ "\u1F71"=>"\u03B1\u0301",
+ "\u1F72"=>"\u03B5\u0300",
+ "\u1F73"=>"\u03B5\u0301",
+ "\u1F74"=>"\u03B7\u0300",
+ "\u1F75"=>"\u03B7\u0301",
+ "\u1F76"=>"\u03B9\u0300",
+ "\u1F77"=>"\u03B9\u0301",
+ "\u1F78"=>"\u03BF\u0300",
+ "\u1F79"=>"\u03BF\u0301",
+ "\u1F7A"=>"\u03C5\u0300",
+ "\u1F7B"=>"\u03C5\u0301",
+ "\u1F7C"=>"\u03C9\u0300",
+ "\u1F7D"=>"\u03C9\u0301",
+ "\u1F80"=>"\u03B1\u0313\u0345",
+ "\u1F81"=>"\u03B1\u0314\u0345",
+ "\u1F82"=>"\u03B1\u0313\u0300\u0345",
+ "\u1F83"=>"\u03B1\u0314\u0300\u0345",
+ "\u1F84"=>"\u03B1\u0313\u0301\u0345",
+ "\u1F85"=>"\u03B1\u0314\u0301\u0345",
+ "\u1F86"=>"\u03B1\u0313\u0342\u0345",
+ "\u1F87"=>"\u03B1\u0314\u0342\u0345",
+ "\u1F88"=>"\u0391\u0313\u0345",
+ "\u1F89"=>"\u0391\u0314\u0345",
+ "\u1F8A"=>"\u0391\u0313\u0300\u0345",
+ "\u1F8B"=>"\u0391\u0314\u0300\u0345",
+ "\u1F8C"=>"\u0391\u0313\u0301\u0345",
+ "\u1F8D"=>"\u0391\u0314\u0301\u0345",
+ "\u1F8E"=>"\u0391\u0313\u0342\u0345",
+ "\u1F8F"=>"\u0391\u0314\u0342\u0345",
+ "\u1F90"=>"\u03B7\u0313\u0345",
+ "\u1F91"=>"\u03B7\u0314\u0345",
+ "\u1F92"=>"\u03B7\u0313\u0300\u0345",
+ "\u1F93"=>"\u03B7\u0314\u0300\u0345",
+ "\u1F94"=>"\u03B7\u0313\u0301\u0345",
+ "\u1F95"=>"\u03B7\u0314\u0301\u0345",
+ "\u1F96"=>"\u03B7\u0313\u0342\u0345",
+ "\u1F97"=>"\u03B7\u0314\u0342\u0345",
+ "\u1F98"=>"\u0397\u0313\u0345",
+ "\u1F99"=>"\u0397\u0314\u0345",
+ "\u1F9A"=>"\u0397\u0313\u0300\u0345",
+ "\u1F9B"=>"\u0397\u0314\u0300\u0345",
+ "\u1F9C"=>"\u0397\u0313\u0301\u0345",
+ "\u1F9D"=>"\u0397\u0314\u0301\u0345",
+ "\u1F9E"=>"\u0397\u0313\u0342\u0345",
+ "\u1F9F"=>"\u0397\u0314\u0342\u0345",
+ "\u1FA0"=>"\u03C9\u0313\u0345",
+ "\u1FA1"=>"\u03C9\u0314\u0345",
+ "\u1FA2"=>"\u03C9\u0313\u0300\u0345",
+ "\u1FA3"=>"\u03C9\u0314\u0300\u0345",
+ "\u1FA4"=>"\u03C9\u0313\u0301\u0345",
+ "\u1FA5"=>"\u03C9\u0314\u0301\u0345",
+ "\u1FA6"=>"\u03C9\u0313\u0342\u0345",
+ "\u1FA7"=>"\u03C9\u0314\u0342\u0345",
+ "\u1FA8"=>"\u03A9\u0313\u0345",
+ "\u1FA9"=>"\u03A9\u0314\u0345",
+ "\u1FAA"=>"\u03A9\u0313\u0300\u0345",
+ "\u1FAB"=>"\u03A9\u0314\u0300\u0345",
+ "\u1FAC"=>"\u03A9\u0313\u0301\u0345",
+ "\u1FAD"=>"\u03A9\u0314\u0301\u0345",
+ "\u1FAE"=>"\u03A9\u0313\u0342\u0345",
+ "\u1FAF"=>"\u03A9\u0314\u0342\u0345",
+ "\u1FB0"=>"\u03B1\u0306",
+ "\u1FB1"=>"\u03B1\u0304",
+ "\u1FB2"=>"\u03B1\u0300\u0345",
+ "\u1FB3"=>"\u03B1\u0345",
+ "\u1FB4"=>"\u03B1\u0301\u0345",
+ "\u1FB6"=>"\u03B1\u0342",
+ "\u1FB7"=>"\u03B1\u0342\u0345",
+ "\u1FB8"=>"\u0391\u0306",
+ "\u1FB9"=>"\u0391\u0304",
+ "\u1FBA"=>"\u0391\u0300",
+ "\u1FBB"=>"\u0391\u0301",
+ "\u1FBC"=>"\u0391\u0345",
+ "\u1FBE"=>"\u03B9",
+ "\u1FC1"=>"\u00A8\u0342",
+ "\u1FC2"=>"\u03B7\u0300\u0345",
+ "\u1FC3"=>"\u03B7\u0345",
+ "\u1FC4"=>"\u03B7\u0301\u0345",
+ "\u1FC6"=>"\u03B7\u0342",
+ "\u1FC7"=>"\u03B7\u0342\u0345",
+ "\u1FC8"=>"\u0395\u0300",
+ "\u1FC9"=>"\u0395\u0301",
+ "\u1FCA"=>"\u0397\u0300",
+ "\u1FCB"=>"\u0397\u0301",
+ "\u1FCC"=>"\u0397\u0345",
+ "\u1FCD"=>"\u1FBF\u0300",
+ "\u1FCE"=>"\u1FBF\u0301",
+ "\u1FCF"=>"\u1FBF\u0342",
+ "\u1FD0"=>"\u03B9\u0306",
+ "\u1FD1"=>"\u03B9\u0304",
+ "\u1FD2"=>"\u03B9\u0308\u0300",
+ "\u1FD3"=>"\u03B9\u0308\u0301",
+ "\u1FD6"=>"\u03B9\u0342",
+ "\u1FD7"=>"\u03B9\u0308\u0342",
+ "\u1FD8"=>"\u0399\u0306",
+ "\u1FD9"=>"\u0399\u0304",
+ "\u1FDA"=>"\u0399\u0300",
+ "\u1FDB"=>"\u0399\u0301",
+ "\u1FDD"=>"\u1FFE\u0300",
+ "\u1FDE"=>"\u1FFE\u0301",
+ "\u1FDF"=>"\u1FFE\u0342",
+ "\u1FE0"=>"\u03C5\u0306",
+ "\u1FE1"=>"\u03C5\u0304",
+ "\u1FE2"=>"\u03C5\u0308\u0300",
+ "\u1FE3"=>"\u03C5\u0308\u0301",
+ "\u1FE4"=>"\u03C1\u0313",
+ "\u1FE5"=>"\u03C1\u0314",
+ "\u1FE6"=>"\u03C5\u0342",
+ "\u1FE7"=>"\u03C5\u0308\u0342",
+ "\u1FE8"=>"\u03A5\u0306",
+ "\u1FE9"=>"\u03A5\u0304",
+ "\u1FEA"=>"\u03A5\u0300",
+ "\u1FEB"=>"\u03A5\u0301",
+ "\u1FEC"=>"\u03A1\u0314",
+ "\u1FED"=>"\u00A8\u0300",
+ "\u1FEE"=>"\u00A8\u0301",
+ "\u1FEF"=>"`",
+ "\u1FF2"=>"\u03C9\u0300\u0345",
+ "\u1FF3"=>"\u03C9\u0345",
+ "\u1FF4"=>"\u03C9\u0301\u0345",
+ "\u1FF6"=>"\u03C9\u0342",
+ "\u1FF7"=>"\u03C9\u0342\u0345",
+ "\u1FF8"=>"\u039F\u0300",
+ "\u1FF9"=>"\u039F\u0301",
+ "\u1FFA"=>"\u03A9\u0300",
+ "\u1FFB"=>"\u03A9\u0301",
+ "\u1FFC"=>"\u03A9\u0345",
+ "\u1FFD"=>"\u00B4",
+ "\u2000"=>"\u2002",
+ "\u2001"=>"\u2003",
+ "\u2126"=>"\u03A9",
+ "\u212A"=>"K",
+ "\u212B"=>"A\u030A",
+ "\u219A"=>"\u2190\u0338",
+ "\u219B"=>"\u2192\u0338",
+ "\u21AE"=>"\u2194\u0338",
+ "\u21CD"=>"\u21D0\u0338",
+ "\u21CE"=>"\u21D4\u0338",
+ "\u21CF"=>"\u21D2\u0338",
+ "\u2204"=>"\u2203\u0338",
+ "\u2209"=>"\u2208\u0338",
+ "\u220C"=>"\u220B\u0338",
+ "\u2224"=>"\u2223\u0338",
+ "\u2226"=>"\u2225\u0338",
+ "\u2241"=>"\u223C\u0338",
+ "\u2244"=>"\u2243\u0338",
+ "\u2247"=>"\u2245\u0338",
+ "\u2249"=>"\u2248\u0338",
+ "\u2260"=>"=\u0338",
+ "\u2262"=>"\u2261\u0338",
+ "\u226D"=>"\u224D\u0338",
+ "\u226E"=>"<\u0338",
+ "\u226F"=>">\u0338",
+ "\u2270"=>"\u2264\u0338",
+ "\u2271"=>"\u2265\u0338",
+ "\u2274"=>"\u2272\u0338",
+ "\u2275"=>"\u2273\u0338",
+ "\u2278"=>"\u2276\u0338",
+ "\u2279"=>"\u2277\u0338",
+ "\u2280"=>"\u227A\u0338",
+ "\u2281"=>"\u227B\u0338",
+ "\u2284"=>"\u2282\u0338",
+ "\u2285"=>"\u2283\u0338",
+ "\u2288"=>"\u2286\u0338",
+ "\u2289"=>"\u2287\u0338",
+ "\u22AC"=>"\u22A2\u0338",
+ "\u22AD"=>"\u22A8\u0338",
+ "\u22AE"=>"\u22A9\u0338",
+ "\u22AF"=>"\u22AB\u0338",
+ "\u22E0"=>"\u227C\u0338",
+ "\u22E1"=>"\u227D\u0338",
+ "\u22E2"=>"\u2291\u0338",
+ "\u22E3"=>"\u2292\u0338",
+ "\u22EA"=>"\u22B2\u0338",
+ "\u22EB"=>"\u22B3\u0338",
+ "\u22EC"=>"\u22B4\u0338",
+ "\u22ED"=>"\u22B5\u0338",
+ "\u2329"=>"\u3008",
+ "\u232A"=>"\u3009",
+ "\u2ADC"=>"\u2ADD\u0338",
+ "\u304C"=>"\u304B\u3099",
+ "\u304E"=>"\u304D\u3099",
+ "\u3050"=>"\u304F\u3099",
+ "\u3052"=>"\u3051\u3099",
+ "\u3054"=>"\u3053\u3099",
+ "\u3056"=>"\u3055\u3099",
+ "\u3058"=>"\u3057\u3099",
+ "\u305A"=>"\u3059\u3099",
+ "\u305C"=>"\u305B\u3099",
+ "\u305E"=>"\u305D\u3099",
+ "\u3060"=>"\u305F\u3099",
+ "\u3062"=>"\u3061\u3099",
+ "\u3065"=>"\u3064\u3099",
+ "\u3067"=>"\u3066\u3099",
+ "\u3069"=>"\u3068\u3099",
+ "\u3070"=>"\u306F\u3099",
+ "\u3071"=>"\u306F\u309A",
+ "\u3073"=>"\u3072\u3099",
+ "\u3074"=>"\u3072\u309A",
+ "\u3076"=>"\u3075\u3099",
+ "\u3077"=>"\u3075\u309A",
+ "\u3079"=>"\u3078\u3099",
+ "\u307A"=>"\u3078\u309A",
+ "\u307C"=>"\u307B\u3099",
+ "\u307D"=>"\u307B\u309A",
+ "\u3094"=>"\u3046\u3099",
+ "\u309E"=>"\u309D\u3099",
+ "\u30AC"=>"\u30AB\u3099",
+ "\u30AE"=>"\u30AD\u3099",
+ "\u30B0"=>"\u30AF\u3099",
+ "\u30B2"=>"\u30B1\u3099",
+ "\u30B4"=>"\u30B3\u3099",
+ "\u30B6"=>"\u30B5\u3099",
+ "\u30B8"=>"\u30B7\u3099",
+ "\u30BA"=>"\u30B9\u3099",
+ "\u30BC"=>"\u30BB\u3099",
+ "\u30BE"=>"\u30BD\u3099",
+ "\u30C0"=>"\u30BF\u3099",
+ "\u30C2"=>"\u30C1\u3099",
+ "\u30C5"=>"\u30C4\u3099",
+ "\u30C7"=>"\u30C6\u3099",
+ "\u30C9"=>"\u30C8\u3099",
+ "\u30D0"=>"\u30CF\u3099",
+ "\u30D1"=>"\u30CF\u309A",
+ "\u30D3"=>"\u30D2\u3099",
+ "\u30D4"=>"\u30D2\u309A",
+ "\u30D6"=>"\u30D5\u3099",
+ "\u30D7"=>"\u30D5\u309A",
+ "\u30D9"=>"\u30D8\u3099",
+ "\u30DA"=>"\u30D8\u309A",
+ "\u30DC"=>"\u30DB\u3099",
+ "\u30DD"=>"\u30DB\u309A",
+ "\u30F4"=>"\u30A6\u3099",
+ "\u30F7"=>"\u30EF\u3099",
+ "\u30F8"=>"\u30F0\u3099",
+ "\u30F9"=>"\u30F1\u3099",
+ "\u30FA"=>"\u30F2\u3099",
+ "\u30FE"=>"\u30FD\u3099",
+ "\uF900"=>"\u8C48",
+ "\uF901"=>"\u66F4",
+ "\uF902"=>"\u8ECA",
+ "\uF903"=>"\u8CC8",
+ "\uF904"=>"\u6ED1",
+ "\uF905"=>"\u4E32",
+ "\uF906"=>"\u53E5",
+ "\uF907"=>"\u9F9C",
+ "\uF908"=>"\u9F9C",
+ "\uF909"=>"\u5951",
+ "\uF90A"=>"\u91D1",
+ "\uF90B"=>"\u5587",
+ "\uF90C"=>"\u5948",
+ "\uF90D"=>"\u61F6",
+ "\uF90E"=>"\u7669",
+ "\uF90F"=>"\u7F85",
+ "\uF910"=>"\u863F",
+ "\uF911"=>"\u87BA",
+ "\uF912"=>"\u88F8",
+ "\uF913"=>"\u908F",
+ "\uF914"=>"\u6A02",
+ "\uF915"=>"\u6D1B",
+ "\uF916"=>"\u70D9",
+ "\uF917"=>"\u73DE",
+ "\uF918"=>"\u843D",
+ "\uF919"=>"\u916A",
+ "\uF91A"=>"\u99F1",
+ "\uF91B"=>"\u4E82",
+ "\uF91C"=>"\u5375",
+ "\uF91D"=>"\u6B04",
+ "\uF91E"=>"\u721B",
+ "\uF91F"=>"\u862D",
+ "\uF920"=>"\u9E1E",
+ "\uF921"=>"\u5D50",
+ "\uF922"=>"\u6FEB",
+ "\uF923"=>"\u85CD",
+ "\uF924"=>"\u8964",
+ "\uF925"=>"\u62C9",
+ "\uF926"=>"\u81D8",
+ "\uF927"=>"\u881F",
+ "\uF928"=>"\u5ECA",
+ "\uF929"=>"\u6717",
+ "\uF92A"=>"\u6D6A",
+ "\uF92B"=>"\u72FC",
+ "\uF92C"=>"\u90CE",
+ "\uF92D"=>"\u4F86",
+ "\uF92E"=>"\u51B7",
+ "\uF92F"=>"\u52DE",
+ "\uF930"=>"\u64C4",
+ "\uF931"=>"\u6AD3",
+ "\uF932"=>"\u7210",
+ "\uF933"=>"\u76E7",
+ "\uF934"=>"\u8001",
+ "\uF935"=>"\u8606",
+ "\uF936"=>"\u865C",
+ "\uF937"=>"\u8DEF",
+ "\uF938"=>"\u9732",
+ "\uF939"=>"\u9B6F",
+ "\uF93A"=>"\u9DFA",
+ "\uF93B"=>"\u788C",
+ "\uF93C"=>"\u797F",
+ "\uF93D"=>"\u7DA0",
+ "\uF93E"=>"\u83C9",
+ "\uF93F"=>"\u9304",
+ "\uF940"=>"\u9E7F",
+ "\uF941"=>"\u8AD6",
+ "\uF942"=>"\u58DF",
+ "\uF943"=>"\u5F04",
+ "\uF944"=>"\u7C60",
+ "\uF945"=>"\u807E",
+ "\uF946"=>"\u7262",
+ "\uF947"=>"\u78CA",
+ "\uF948"=>"\u8CC2",
+ "\uF949"=>"\u96F7",
+ "\uF94A"=>"\u58D8",
+ "\uF94B"=>"\u5C62",
+ "\uF94C"=>"\u6A13",
+ "\uF94D"=>"\u6DDA",
+ "\uF94E"=>"\u6F0F",
+ "\uF94F"=>"\u7D2F",
+ "\uF950"=>"\u7E37",
+ "\uF951"=>"\u964B",
+ "\uF952"=>"\u52D2",
+ "\uF953"=>"\u808B",
+ "\uF954"=>"\u51DC",
+ "\uF955"=>"\u51CC",
+ "\uF956"=>"\u7A1C",
+ "\uF957"=>"\u7DBE",
+ "\uF958"=>"\u83F1",
+ "\uF959"=>"\u9675",
+ "\uF95A"=>"\u8B80",
+ "\uF95B"=>"\u62CF",
+ "\uF95C"=>"\u6A02",
+ "\uF95D"=>"\u8AFE",
+ "\uF95E"=>"\u4E39",
+ "\uF95F"=>"\u5BE7",
+ "\uF960"=>"\u6012",
+ "\uF961"=>"\u7387",
+ "\uF962"=>"\u7570",
+ "\uF963"=>"\u5317",
+ "\uF964"=>"\u78FB",
+ "\uF965"=>"\u4FBF",
+ "\uF966"=>"\u5FA9",
+ "\uF967"=>"\u4E0D",
+ "\uF968"=>"\u6CCC",
+ "\uF969"=>"\u6578",
+ "\uF96A"=>"\u7D22",
+ "\uF96B"=>"\u53C3",
+ "\uF96C"=>"\u585E",
+ "\uF96D"=>"\u7701",
+ "\uF96E"=>"\u8449",
+ "\uF96F"=>"\u8AAA",
+ "\uF970"=>"\u6BBA",
+ "\uF971"=>"\u8FB0",
+ "\uF972"=>"\u6C88",
+ "\uF973"=>"\u62FE",
+ "\uF974"=>"\u82E5",
+ "\uF975"=>"\u63A0",
+ "\uF976"=>"\u7565",
+ "\uF977"=>"\u4EAE",
+ "\uF978"=>"\u5169",
+ "\uF979"=>"\u51C9",
+ "\uF97A"=>"\u6881",
+ "\uF97B"=>"\u7CE7",
+ "\uF97C"=>"\u826F",
+ "\uF97D"=>"\u8AD2",
+ "\uF97E"=>"\u91CF",
+ "\uF97F"=>"\u52F5",
+ "\uF980"=>"\u5442",
+ "\uF981"=>"\u5973",
+ "\uF982"=>"\u5EEC",
+ "\uF983"=>"\u65C5",
+ "\uF984"=>"\u6FFE",
+ "\uF985"=>"\u792A",
+ "\uF986"=>"\u95AD",
+ "\uF987"=>"\u9A6A",
+ "\uF988"=>"\u9E97",
+ "\uF989"=>"\u9ECE",
+ "\uF98A"=>"\u529B",
+ "\uF98B"=>"\u66C6",
+ "\uF98C"=>"\u6B77",
+ "\uF98D"=>"\u8F62",
+ "\uF98E"=>"\u5E74",
+ "\uF98F"=>"\u6190",
+ "\uF990"=>"\u6200",
+ "\uF991"=>"\u649A",
+ "\uF992"=>"\u6F23",
+ "\uF993"=>"\u7149",
+ "\uF994"=>"\u7489",
+ "\uF995"=>"\u79CA",
+ "\uF996"=>"\u7DF4",
+ "\uF997"=>"\u806F",
+ "\uF998"=>"\u8F26",
+ "\uF999"=>"\u84EE",
+ "\uF99A"=>"\u9023",
+ "\uF99B"=>"\u934A",
+ "\uF99C"=>"\u5217",
+ "\uF99D"=>"\u52A3",
+ "\uF99E"=>"\u54BD",
+ "\uF99F"=>"\u70C8",
+ "\uF9A0"=>"\u88C2",
+ "\uF9A1"=>"\u8AAA",
+ "\uF9A2"=>"\u5EC9",
+ "\uF9A3"=>"\u5FF5",
+ "\uF9A4"=>"\u637B",
+ "\uF9A5"=>"\u6BAE",
+ "\uF9A6"=>"\u7C3E",
+ "\uF9A7"=>"\u7375",
+ "\uF9A8"=>"\u4EE4",
+ "\uF9A9"=>"\u56F9",
+ "\uF9AA"=>"\u5BE7",
+ "\uF9AB"=>"\u5DBA",
+ "\uF9AC"=>"\u601C",
+ "\uF9AD"=>"\u73B2",
+ "\uF9AE"=>"\u7469",
+ "\uF9AF"=>"\u7F9A",
+ "\uF9B0"=>"\u8046",
+ "\uF9B1"=>"\u9234",
+ "\uF9B2"=>"\u96F6",
+ "\uF9B3"=>"\u9748",
+ "\uF9B4"=>"\u9818",
+ "\uF9B5"=>"\u4F8B",
+ "\uF9B6"=>"\u79AE",
+ "\uF9B7"=>"\u91B4",
+ "\uF9B8"=>"\u96B8",
+ "\uF9B9"=>"\u60E1",
+ "\uF9BA"=>"\u4E86",
+ "\uF9BB"=>"\u50DA",
+ "\uF9BC"=>"\u5BEE",
+ "\uF9BD"=>"\u5C3F",
+ "\uF9BE"=>"\u6599",
+ "\uF9BF"=>"\u6A02",
+ "\uF9C0"=>"\u71CE",
+ "\uF9C1"=>"\u7642",
+ "\uF9C2"=>"\u84FC",
+ "\uF9C3"=>"\u907C",
+ "\uF9C4"=>"\u9F8D",
+ "\uF9C5"=>"\u6688",
+ "\uF9C6"=>"\u962E",
+ "\uF9C7"=>"\u5289",
+ "\uF9C8"=>"\u677B",
+ "\uF9C9"=>"\u67F3",
+ "\uF9CA"=>"\u6D41",
+ "\uF9CB"=>"\u6E9C",
+ "\uF9CC"=>"\u7409",
+ "\uF9CD"=>"\u7559",
+ "\uF9CE"=>"\u786B",
+ "\uF9CF"=>"\u7D10",
+ "\uF9D0"=>"\u985E",
+ "\uF9D1"=>"\u516D",
+ "\uF9D2"=>"\u622E",
+ "\uF9D3"=>"\u9678",
+ "\uF9D4"=>"\u502B",
+ "\uF9D5"=>"\u5D19",
+ "\uF9D6"=>"\u6DEA",
+ "\uF9D7"=>"\u8F2A",
+ "\uF9D8"=>"\u5F8B",
+ "\uF9D9"=>"\u6144",
+ "\uF9DA"=>"\u6817",
+ "\uF9DB"=>"\u7387",
+ "\uF9DC"=>"\u9686",
+ "\uF9DD"=>"\u5229",
+ "\uF9DE"=>"\u540F",
+ "\uF9DF"=>"\u5C65",
+ "\uF9E0"=>"\u6613",
+ "\uF9E1"=>"\u674E",
+ "\uF9E2"=>"\u68A8",
+ "\uF9E3"=>"\u6CE5",
+ "\uF9E4"=>"\u7406",
+ "\uF9E5"=>"\u75E2",
+ "\uF9E6"=>"\u7F79",
+ "\uF9E7"=>"\u88CF",
+ "\uF9E8"=>"\u88E1",
+ "\uF9E9"=>"\u91CC",
+ "\uF9EA"=>"\u96E2",
+ "\uF9EB"=>"\u533F",
+ "\uF9EC"=>"\u6EBA",
+ "\uF9ED"=>"\u541D",
+ "\uF9EE"=>"\u71D0",
+ "\uF9EF"=>"\u7498",
+ "\uF9F0"=>"\u85FA",
+ "\uF9F1"=>"\u96A3",
+ "\uF9F2"=>"\u9C57",
+ "\uF9F3"=>"\u9E9F",
+ "\uF9F4"=>"\u6797",
+ "\uF9F5"=>"\u6DCB",
+ "\uF9F6"=>"\u81E8",
+ "\uF9F7"=>"\u7ACB",
+ "\uF9F8"=>"\u7B20",
+ "\uF9F9"=>"\u7C92",
+ "\uF9FA"=>"\u72C0",
+ "\uF9FB"=>"\u7099",
+ "\uF9FC"=>"\u8B58",
+ "\uF9FD"=>"\u4EC0",
+ "\uF9FE"=>"\u8336",
+ "\uF9FF"=>"\u523A",
+ "\uFA00"=>"\u5207",
+ "\uFA01"=>"\u5EA6",
+ "\uFA02"=>"\u62D3",
+ "\uFA03"=>"\u7CD6",
+ "\uFA04"=>"\u5B85",
+ "\uFA05"=>"\u6D1E",
+ "\uFA06"=>"\u66B4",
+ "\uFA07"=>"\u8F3B",
+ "\uFA08"=>"\u884C",
+ "\uFA09"=>"\u964D",
+ "\uFA0A"=>"\u898B",
+ "\uFA0B"=>"\u5ED3",
+ "\uFA0C"=>"\u5140",
+ "\uFA0D"=>"\u55C0",
+ "\uFA10"=>"\u585A",
+ "\uFA12"=>"\u6674",
+ "\uFA15"=>"\u51DE",
+ "\uFA16"=>"\u732A",
+ "\uFA17"=>"\u76CA",
+ "\uFA18"=>"\u793C",
+ "\uFA19"=>"\u795E",
+ "\uFA1A"=>"\u7965",
+ "\uFA1B"=>"\u798F",
+ "\uFA1C"=>"\u9756",
+ "\uFA1D"=>"\u7CBE",
+ "\uFA1E"=>"\u7FBD",
+ "\uFA20"=>"\u8612",
+ "\uFA22"=>"\u8AF8",
+ "\uFA25"=>"\u9038",
+ "\uFA26"=>"\u90FD",
+ "\uFA2A"=>"\u98EF",
+ "\uFA2B"=>"\u98FC",
+ "\uFA2C"=>"\u9928",
+ "\uFA2D"=>"\u9DB4",
+ "\uFA2E"=>"\u90DE",
+ "\uFA2F"=>"\u96B7",
+ "\uFA30"=>"\u4FAE",
+ "\uFA31"=>"\u50E7",
+ "\uFA32"=>"\u514D",
+ "\uFA33"=>"\u52C9",
+ "\uFA34"=>"\u52E4",
+ "\uFA35"=>"\u5351",
+ "\uFA36"=>"\u559D",
+ "\uFA37"=>"\u5606",
+ "\uFA38"=>"\u5668",
+ "\uFA39"=>"\u5840",
+ "\uFA3A"=>"\u58A8",
+ "\uFA3B"=>"\u5C64",
+ "\uFA3C"=>"\u5C6E",
+ "\uFA3D"=>"\u6094",
+ "\uFA3E"=>"\u6168",
+ "\uFA3F"=>"\u618E",
+ "\uFA40"=>"\u61F2",
+ "\uFA41"=>"\u654F",
+ "\uFA42"=>"\u65E2",
+ "\uFA43"=>"\u6691",
+ "\uFA44"=>"\u6885",
+ "\uFA45"=>"\u6D77",
+ "\uFA46"=>"\u6E1A",
+ "\uFA47"=>"\u6F22",
+ "\uFA48"=>"\u716E",
+ "\uFA49"=>"\u722B",
+ "\uFA4A"=>"\u7422",
+ "\uFA4B"=>"\u7891",
+ "\uFA4C"=>"\u793E",
+ "\uFA4D"=>"\u7949",
+ "\uFA4E"=>"\u7948",
+ "\uFA4F"=>"\u7950",
+ "\uFA50"=>"\u7956",
+ "\uFA51"=>"\u795D",
+ "\uFA52"=>"\u798D",
+ "\uFA53"=>"\u798E",
+ "\uFA54"=>"\u7A40",
+ "\uFA55"=>"\u7A81",
+ "\uFA56"=>"\u7BC0",
+ "\uFA57"=>"\u7DF4",
+ "\uFA58"=>"\u7E09",
+ "\uFA59"=>"\u7E41",
+ "\uFA5A"=>"\u7F72",
+ "\uFA5B"=>"\u8005",
+ "\uFA5C"=>"\u81ED",
+ "\uFA5D"=>"\u8279",
+ "\uFA5E"=>"\u8279",
+ "\uFA5F"=>"\u8457",
+ "\uFA60"=>"\u8910",
+ "\uFA61"=>"\u8996",
+ "\uFA62"=>"\u8B01",
+ "\uFA63"=>"\u8B39",
+ "\uFA64"=>"\u8CD3",
+ "\uFA65"=>"\u8D08",
+ "\uFA66"=>"\u8FB6",
+ "\uFA67"=>"\u9038",
+ "\uFA68"=>"\u96E3",
+ "\uFA69"=>"\u97FF",
+ "\uFA6A"=>"\u983B",
+ "\uFA6B"=>"\u6075",
+ "\uFA6C"=>"\u{242EE}",
+ "\uFA6D"=>"\u8218",
+ "\uFA70"=>"\u4E26",
+ "\uFA71"=>"\u51B5",
+ "\uFA72"=>"\u5168",
+ "\uFA73"=>"\u4F80",
+ "\uFA74"=>"\u5145",
+ "\uFA75"=>"\u5180",
+ "\uFA76"=>"\u52C7",
+ "\uFA77"=>"\u52FA",
+ "\uFA78"=>"\u559D",
+ "\uFA79"=>"\u5555",
+ "\uFA7A"=>"\u5599",
+ "\uFA7B"=>"\u55E2",
+ "\uFA7C"=>"\u585A",
+ "\uFA7D"=>"\u58B3",
+ "\uFA7E"=>"\u5944",
+ "\uFA7F"=>"\u5954",
+ "\uFA80"=>"\u5A62",
+ "\uFA81"=>"\u5B28",
+ "\uFA82"=>"\u5ED2",
+ "\uFA83"=>"\u5ED9",
+ "\uFA84"=>"\u5F69",
+ "\uFA85"=>"\u5FAD",
+ "\uFA86"=>"\u60D8",
+ "\uFA87"=>"\u614E",
+ "\uFA88"=>"\u6108",
+ "\uFA89"=>"\u618E",
+ "\uFA8A"=>"\u6160",
+ "\uFA8B"=>"\u61F2",
+ "\uFA8C"=>"\u6234",
+ "\uFA8D"=>"\u63C4",
+ "\uFA8E"=>"\u641C",
+ "\uFA8F"=>"\u6452",
+ "\uFA90"=>"\u6556",
+ "\uFA91"=>"\u6674",
+ "\uFA92"=>"\u6717",
+ "\uFA93"=>"\u671B",
+ "\uFA94"=>"\u6756",
+ "\uFA95"=>"\u6B79",
+ "\uFA96"=>"\u6BBA",
+ "\uFA97"=>"\u6D41",
+ "\uFA98"=>"\u6EDB",
+ "\uFA99"=>"\u6ECB",
+ "\uFA9A"=>"\u6F22",
+ "\uFA9B"=>"\u701E",
+ "\uFA9C"=>"\u716E",
+ "\uFA9D"=>"\u77A7",
+ "\uFA9E"=>"\u7235",
+ "\uFA9F"=>"\u72AF",
+ "\uFAA0"=>"\u732A",
+ "\uFAA1"=>"\u7471",
+ "\uFAA2"=>"\u7506",
+ "\uFAA3"=>"\u753B",
+ "\uFAA4"=>"\u761D",
+ "\uFAA5"=>"\u761F",
+ "\uFAA6"=>"\u76CA",
+ "\uFAA7"=>"\u76DB",
+ "\uFAA8"=>"\u76F4",
+ "\uFAA9"=>"\u774A",
+ "\uFAAA"=>"\u7740",
+ "\uFAAB"=>"\u78CC",
+ "\uFAAC"=>"\u7AB1",
+ "\uFAAD"=>"\u7BC0",
+ "\uFAAE"=>"\u7C7B",
+ "\uFAAF"=>"\u7D5B",
+ "\uFAB0"=>"\u7DF4",
+ "\uFAB1"=>"\u7F3E",
+ "\uFAB2"=>"\u8005",
+ "\uFAB3"=>"\u8352",
+ "\uFAB4"=>"\u83EF",
+ "\uFAB5"=>"\u8779",
+ "\uFAB6"=>"\u8941",
+ "\uFAB7"=>"\u8986",
+ "\uFAB8"=>"\u8996",
+ "\uFAB9"=>"\u8ABF",
+ "\uFABA"=>"\u8AF8",
+ "\uFABB"=>"\u8ACB",
+ "\uFABC"=>"\u8B01",
+ "\uFABD"=>"\u8AFE",
+ "\uFABE"=>"\u8AED",
+ "\uFABF"=>"\u8B39",
+ "\uFAC0"=>"\u8B8A",
+ "\uFAC1"=>"\u8D08",
+ "\uFAC2"=>"\u8F38",
+ "\uFAC3"=>"\u9072",
+ "\uFAC4"=>"\u9199",
+ "\uFAC5"=>"\u9276",
+ "\uFAC6"=>"\u967C",
+ "\uFAC7"=>"\u96E3",
+ "\uFAC8"=>"\u9756",
+ "\uFAC9"=>"\u97DB",
+ "\uFACA"=>"\u97FF",
+ "\uFACB"=>"\u980B",
+ "\uFACC"=>"\u983B",
+ "\uFACD"=>"\u9B12",
+ "\uFACE"=>"\u9F9C",
+ "\uFACF"=>"\u{2284A}",
+ "\uFAD0"=>"\u{22844}",
+ "\uFAD1"=>"\u{233D5}",
+ "\uFAD2"=>"\u3B9D",
+ "\uFAD3"=>"\u4018",
+ "\uFAD4"=>"\u4039",
+ "\uFAD5"=>"\u{25249}",
+ "\uFAD6"=>"\u{25CD0}",
+ "\uFAD7"=>"\u{27ED3}",
+ "\uFAD8"=>"\u9F43",
+ "\uFAD9"=>"\u9F8E",
+ "\uFB1D"=>"\u05D9\u05B4",
+ "\uFB1F"=>"\u05F2\u05B7",
+ "\uFB2A"=>"\u05E9\u05C1",
+ "\uFB2B"=>"\u05E9\u05C2",
+ "\uFB2C"=>"\u05E9\u05BC\u05C1",
+ "\uFB2D"=>"\u05E9\u05BC\u05C2",
+ "\uFB2E"=>"\u05D0\u05B7",
+ "\uFB2F"=>"\u05D0\u05B8",
+ "\uFB30"=>"\u05D0\u05BC",
+ "\uFB31"=>"\u05D1\u05BC",
+ "\uFB32"=>"\u05D2\u05BC",
+ "\uFB33"=>"\u05D3\u05BC",
+ "\uFB34"=>"\u05D4\u05BC",
+ "\uFB35"=>"\u05D5\u05BC",
+ "\uFB36"=>"\u05D6\u05BC",
+ "\uFB38"=>"\u05D8\u05BC",
+ "\uFB39"=>"\u05D9\u05BC",
+ "\uFB3A"=>"\u05DA\u05BC",
+ "\uFB3B"=>"\u05DB\u05BC",
+ "\uFB3C"=>"\u05DC\u05BC",
+ "\uFB3E"=>"\u05DE\u05BC",
+ "\uFB40"=>"\u05E0\u05BC",
+ "\uFB41"=>"\u05E1\u05BC",
+ "\uFB43"=>"\u05E3\u05BC",
+ "\uFB44"=>"\u05E4\u05BC",
+ "\uFB46"=>"\u05E6\u05BC",
+ "\uFB47"=>"\u05E7\u05BC",
+ "\uFB48"=>"\u05E8\u05BC",
+ "\uFB49"=>"\u05E9\u05BC",
+ "\uFB4A"=>"\u05EA\u05BC",
+ "\uFB4B"=>"\u05D5\u05B9",
+ "\uFB4C"=>"\u05D1\u05BF",
+ "\uFB4D"=>"\u05DB\u05BF",
+ "\uFB4E"=>"\u05E4\u05BF",
+ "\u{1109A}"=>"\u{11099}\u{110BA}",
+ "\u{1109C}"=>"\u{1109B}\u{110BA}",
+ "\u{110AB}"=>"\u{110A5}\u{110BA}",
+ "\u{1112E}"=>"\u{11131}\u{11127}",
+ "\u{1112F}"=>"\u{11132}\u{11127}",
+ "\u{1134B}"=>"\u{11347}\u{1133E}",
+ "\u{1134C}"=>"\u{11347}\u{11357}",
+ "\u{114BB}"=>"\u{114B9}\u{114BA}",
+ "\u{114BC}"=>"\u{114B9}\u{114B0}",
+ "\u{114BE}"=>"\u{114B9}\u{114BD}",
+ "\u{115BA}"=>"\u{115B8}\u{115AF}",
+ "\u{115BB}"=>"\u{115B9}\u{115AF}",
+ "\u{1D15E}"=>"\u{1D157}\u{1D165}",
+ "\u{1D15F}"=>"\u{1D158}\u{1D165}",
+ "\u{1D160}"=>"\u{1D158}\u{1D165}\u{1D16E}",
+ "\u{1D161}"=>"\u{1D158}\u{1D165}\u{1D16F}",
+ "\u{1D162}"=>"\u{1D158}\u{1D165}\u{1D170}",
+ "\u{1D163}"=>"\u{1D158}\u{1D165}\u{1D171}",
+ "\u{1D164}"=>"\u{1D158}\u{1D165}\u{1D172}",
+ "\u{1D1BB}"=>"\u{1D1B9}\u{1D165}",
+ "\u{1D1BC}"=>"\u{1D1BA}\u{1D165}",
+ "\u{1D1BD}"=>"\u{1D1B9}\u{1D165}\u{1D16E}",
+ "\u{1D1BE}"=>"\u{1D1BA}\u{1D165}\u{1D16E}",
+ "\u{1D1BF}"=>"\u{1D1B9}\u{1D165}\u{1D16F}",
+ "\u{1D1C0}"=>"\u{1D1BA}\u{1D165}\u{1D16F}",
+ "\u{2F800}"=>"\u4E3D",
+ "\u{2F801}"=>"\u4E38",
+ "\u{2F802}"=>"\u4E41",
+ "\u{2F803}"=>"\u{20122}",
+ "\u{2F804}"=>"\u4F60",
+ "\u{2F805}"=>"\u4FAE",
+ "\u{2F806}"=>"\u4FBB",
+ "\u{2F807}"=>"\u5002",
+ "\u{2F808}"=>"\u507A",
+ "\u{2F809}"=>"\u5099",
+ "\u{2F80A}"=>"\u50E7",
+ "\u{2F80B}"=>"\u50CF",
+ "\u{2F80C}"=>"\u349E",
+ "\u{2F80D}"=>"\u{2063A}",
+ "\u{2F80E}"=>"\u514D",
+ "\u{2F80F}"=>"\u5154",
+ "\u{2F810}"=>"\u5164",
+ "\u{2F811}"=>"\u5177",
+ "\u{2F812}"=>"\u{2051C}",
+ "\u{2F813}"=>"\u34B9",
+ "\u{2F814}"=>"\u5167",
+ "\u{2F815}"=>"\u518D",
+ "\u{2F816}"=>"\u{2054B}",
+ "\u{2F817}"=>"\u5197",
+ "\u{2F818}"=>"\u51A4",
+ "\u{2F819}"=>"\u4ECC",
+ "\u{2F81A}"=>"\u51AC",
+ "\u{2F81B}"=>"\u51B5",
+ "\u{2F81C}"=>"\u{291DF}",
+ "\u{2F81D}"=>"\u51F5",
+ "\u{2F81E}"=>"\u5203",
+ "\u{2F81F}"=>"\u34DF",
+ "\u{2F820}"=>"\u523B",
+ "\u{2F821}"=>"\u5246",
+ "\u{2F822}"=>"\u5272",
+ "\u{2F823}"=>"\u5277",
+ "\u{2F824}"=>"\u3515",
+ "\u{2F825}"=>"\u52C7",
+ "\u{2F826}"=>"\u52C9",
+ "\u{2F827}"=>"\u52E4",
+ "\u{2F828}"=>"\u52FA",
+ "\u{2F829}"=>"\u5305",
+ "\u{2F82A}"=>"\u5306",
+ "\u{2F82B}"=>"\u5317",
+ "\u{2F82C}"=>"\u5349",
+ "\u{2F82D}"=>"\u5351",
+ "\u{2F82E}"=>"\u535A",
+ "\u{2F82F}"=>"\u5373",
+ "\u{2F830}"=>"\u537D",
+ "\u{2F831}"=>"\u537F",
+ "\u{2F832}"=>"\u537F",
+ "\u{2F833}"=>"\u537F",
+ "\u{2F834}"=>"\u{20A2C}",
+ "\u{2F835}"=>"\u7070",
+ "\u{2F836}"=>"\u53CA",
+ "\u{2F837}"=>"\u53DF",
+ "\u{2F838}"=>"\u{20B63}",
+ "\u{2F839}"=>"\u53EB",
+ "\u{2F83A}"=>"\u53F1",
+ "\u{2F83B}"=>"\u5406",
+ "\u{2F83C}"=>"\u549E",
+ "\u{2F83D}"=>"\u5438",
+ "\u{2F83E}"=>"\u5448",
+ "\u{2F83F}"=>"\u5468",
+ "\u{2F840}"=>"\u54A2",
+ "\u{2F841}"=>"\u54F6",
+ "\u{2F842}"=>"\u5510",
+ "\u{2F843}"=>"\u5553",
+ "\u{2F844}"=>"\u5563",
+ "\u{2F845}"=>"\u5584",
+ "\u{2F846}"=>"\u5584",
+ "\u{2F847}"=>"\u5599",
+ "\u{2F848}"=>"\u55AB",
+ "\u{2F849}"=>"\u55B3",
+ "\u{2F84A}"=>"\u55C2",
+ "\u{2F84B}"=>"\u5716",
+ "\u{2F84C}"=>"\u5606",
+ "\u{2F84D}"=>"\u5717",
+ "\u{2F84E}"=>"\u5651",
+ "\u{2F84F}"=>"\u5674",
+ "\u{2F850}"=>"\u5207",
+ "\u{2F851}"=>"\u58EE",
+ "\u{2F852}"=>"\u57CE",
+ "\u{2F853}"=>"\u57F4",
+ "\u{2F854}"=>"\u580D",
+ "\u{2F855}"=>"\u578B",
+ "\u{2F856}"=>"\u5832",
+ "\u{2F857}"=>"\u5831",
+ "\u{2F858}"=>"\u58AC",
+ "\u{2F859}"=>"\u{214E4}",
+ "\u{2F85A}"=>"\u58F2",
+ "\u{2F85B}"=>"\u58F7",
+ "\u{2F85C}"=>"\u5906",
+ "\u{2F85D}"=>"\u591A",
+ "\u{2F85E}"=>"\u5922",
+ "\u{2F85F}"=>"\u5962",
+ "\u{2F860}"=>"\u{216A8}",
+ "\u{2F861}"=>"\u{216EA}",
+ "\u{2F862}"=>"\u59EC",
+ "\u{2F863}"=>"\u5A1B",
+ "\u{2F864}"=>"\u5A27",
+ "\u{2F865}"=>"\u59D8",
+ "\u{2F866}"=>"\u5A66",
+ "\u{2F867}"=>"\u36EE",
+ "\u{2F868}"=>"\u36FC",
+ "\u{2F869}"=>"\u5B08",
+ "\u{2F86A}"=>"\u5B3E",
+ "\u{2F86B}"=>"\u5B3E",
+ "\u{2F86C}"=>"\u{219C8}",
+ "\u{2F86D}"=>"\u5BC3",
+ "\u{2F86E}"=>"\u5BD8",
+ "\u{2F86F}"=>"\u5BE7",
+ "\u{2F870}"=>"\u5BF3",
+ "\u{2F871}"=>"\u{21B18}",
+ "\u{2F872}"=>"\u5BFF",
+ "\u{2F873}"=>"\u5C06",
+ "\u{2F874}"=>"\u5F53",
+ "\u{2F875}"=>"\u5C22",
+ "\u{2F876}"=>"\u3781",
+ "\u{2F877}"=>"\u5C60",
+ "\u{2F878}"=>"\u5C6E",
+ "\u{2F879}"=>"\u5CC0",
+ "\u{2F87A}"=>"\u5C8D",
+ "\u{2F87B}"=>"\u{21DE4}",
+ "\u{2F87C}"=>"\u5D43",
+ "\u{2F87D}"=>"\u{21DE6}",
+ "\u{2F87E}"=>"\u5D6E",
+ "\u{2F87F}"=>"\u5D6B",
+ "\u{2F880}"=>"\u5D7C",
+ "\u{2F881}"=>"\u5DE1",
+ "\u{2F882}"=>"\u5DE2",
+ "\u{2F883}"=>"\u382F",
+ "\u{2F884}"=>"\u5DFD",
+ "\u{2F885}"=>"\u5E28",
+ "\u{2F886}"=>"\u5E3D",
+ "\u{2F887}"=>"\u5E69",
+ "\u{2F888}"=>"\u3862",
+ "\u{2F889}"=>"\u{22183}",
+ "\u{2F88A}"=>"\u387C",
+ "\u{2F88B}"=>"\u5EB0",
+ "\u{2F88C}"=>"\u5EB3",
+ "\u{2F88D}"=>"\u5EB6",
+ "\u{2F88E}"=>"\u5ECA",
+ "\u{2F88F}"=>"\u{2A392}",
+ "\u{2F890}"=>"\u5EFE",
+ "\u{2F891}"=>"\u{22331}",
+ "\u{2F892}"=>"\u{22331}",
+ "\u{2F893}"=>"\u8201",
+ "\u{2F894}"=>"\u5F22",
+ "\u{2F895}"=>"\u5F22",
+ "\u{2F896}"=>"\u38C7",
+ "\u{2F897}"=>"\u{232B8}",
+ "\u{2F898}"=>"\u{261DA}",
+ "\u{2F899}"=>"\u5F62",
+ "\u{2F89A}"=>"\u5F6B",
+ "\u{2F89B}"=>"\u38E3",
+ "\u{2F89C}"=>"\u5F9A",
+ "\u{2F89D}"=>"\u5FCD",
+ "\u{2F89E}"=>"\u5FD7",
+ "\u{2F89F}"=>"\u5FF9",
+ "\u{2F8A0}"=>"\u6081",
+ "\u{2F8A1}"=>"\u393A",
+ "\u{2F8A2}"=>"\u391C",
+ "\u{2F8A3}"=>"\u6094",
+ "\u{2F8A4}"=>"\u{226D4}",
+ "\u{2F8A5}"=>"\u60C7",
+ "\u{2F8A6}"=>"\u6148",
+ "\u{2F8A7}"=>"\u614C",
+ "\u{2F8A8}"=>"\u614E",
+ "\u{2F8A9}"=>"\u614C",
+ "\u{2F8AA}"=>"\u617A",
+ "\u{2F8AB}"=>"\u618E",
+ "\u{2F8AC}"=>"\u61B2",
+ "\u{2F8AD}"=>"\u61A4",
+ "\u{2F8AE}"=>"\u61AF",
+ "\u{2F8AF}"=>"\u61DE",
+ "\u{2F8B0}"=>"\u61F2",
+ "\u{2F8B1}"=>"\u61F6",
+ "\u{2F8B2}"=>"\u6210",
+ "\u{2F8B3}"=>"\u621B",
+ "\u{2F8B4}"=>"\u625D",
+ "\u{2F8B5}"=>"\u62B1",
+ "\u{2F8B6}"=>"\u62D4",
+ "\u{2F8B7}"=>"\u6350",
+ "\u{2F8B8}"=>"\u{22B0C}",
+ "\u{2F8B9}"=>"\u633D",
+ "\u{2F8BA}"=>"\u62FC",
+ "\u{2F8BB}"=>"\u6368",
+ "\u{2F8BC}"=>"\u6383",
+ "\u{2F8BD}"=>"\u63E4",
+ "\u{2F8BE}"=>"\u{22BF1}",
+ "\u{2F8BF}"=>"\u6422",
+ "\u{2F8C0}"=>"\u63C5",
+ "\u{2F8C1}"=>"\u63A9",
+ "\u{2F8C2}"=>"\u3A2E",
+ "\u{2F8C3}"=>"\u6469",
+ "\u{2F8C4}"=>"\u647E",
+ "\u{2F8C5}"=>"\u649D",
+ "\u{2F8C6}"=>"\u6477",
+ "\u{2F8C7}"=>"\u3A6C",
+ "\u{2F8C8}"=>"\u654F",
+ "\u{2F8C9}"=>"\u656C",
+ "\u{2F8CA}"=>"\u{2300A}",
+ "\u{2F8CB}"=>"\u65E3",
+ "\u{2F8CC}"=>"\u66F8",
+ "\u{2F8CD}"=>"\u6649",
+ "\u{2F8CE}"=>"\u3B19",
+ "\u{2F8CF}"=>"\u6691",
+ "\u{2F8D0}"=>"\u3B08",
+ "\u{2F8D1}"=>"\u3AE4",
+ "\u{2F8D2}"=>"\u5192",
+ "\u{2F8D3}"=>"\u5195",
+ "\u{2F8D4}"=>"\u6700",
+ "\u{2F8D5}"=>"\u669C",
+ "\u{2F8D6}"=>"\u80AD",
+ "\u{2F8D7}"=>"\u43D9",
+ "\u{2F8D8}"=>"\u6717",
+ "\u{2F8D9}"=>"\u671B",
+ "\u{2F8DA}"=>"\u6721",
+ "\u{2F8DB}"=>"\u675E",
+ "\u{2F8DC}"=>"\u6753",
+ "\u{2F8DD}"=>"\u{233C3}",
+ "\u{2F8DE}"=>"\u3B49",
+ "\u{2F8DF}"=>"\u67FA",
+ "\u{2F8E0}"=>"\u6785",
+ "\u{2F8E1}"=>"\u6852",
+ "\u{2F8E2}"=>"\u6885",
+ "\u{2F8E3}"=>"\u{2346D}",
+ "\u{2F8E4}"=>"\u688E",
+ "\u{2F8E5}"=>"\u681F",
+ "\u{2F8E6}"=>"\u6914",
+ "\u{2F8E7}"=>"\u3B9D",
+ "\u{2F8E8}"=>"\u6942",
+ "\u{2F8E9}"=>"\u69A3",
+ "\u{2F8EA}"=>"\u69EA",
+ "\u{2F8EB}"=>"\u6AA8",
+ "\u{2F8EC}"=>"\u{236A3}",
+ "\u{2F8ED}"=>"\u6ADB",
+ "\u{2F8EE}"=>"\u3C18",
+ "\u{2F8EF}"=>"\u6B21",
+ "\u{2F8F0}"=>"\u{238A7}",
+ "\u{2F8F1}"=>"\u6B54",
+ "\u{2F8F2}"=>"\u3C4E",
+ "\u{2F8F3}"=>"\u6B72",
+ "\u{2F8F4}"=>"\u6B9F",
+ "\u{2F8F5}"=>"\u6BBA",
+ "\u{2F8F6}"=>"\u6BBB",
+ "\u{2F8F7}"=>"\u{23A8D}",
+ "\u{2F8F8}"=>"\u{21D0B}",
+ "\u{2F8F9}"=>"\u{23AFA}",
+ "\u{2F8FA}"=>"\u6C4E",
+ "\u{2F8FB}"=>"\u{23CBC}",
+ "\u{2F8FC}"=>"\u6CBF",
+ "\u{2F8FD}"=>"\u6CCD",
+ "\u{2F8FE}"=>"\u6C67",
+ "\u{2F8FF}"=>"\u6D16",
+ "\u{2F900}"=>"\u6D3E",
+ "\u{2F901}"=>"\u6D77",
+ "\u{2F902}"=>"\u6D41",
+ "\u{2F903}"=>"\u6D69",
+ "\u{2F904}"=>"\u6D78",
+ "\u{2F905}"=>"\u6D85",
+ "\u{2F906}"=>"\u{23D1E}",
+ "\u{2F907}"=>"\u6D34",
+ "\u{2F908}"=>"\u6E2F",
+ "\u{2F909}"=>"\u6E6E",
+ "\u{2F90A}"=>"\u3D33",
+ "\u{2F90B}"=>"\u6ECB",
+ "\u{2F90C}"=>"\u6EC7",
+ "\u{2F90D}"=>"\u{23ED1}",
+ "\u{2F90E}"=>"\u6DF9",
+ "\u{2F90F}"=>"\u6F6E",
+ "\u{2F910}"=>"\u{23F5E}",
+ "\u{2F911}"=>"\u{23F8E}",
+ "\u{2F912}"=>"\u6FC6",
+ "\u{2F913}"=>"\u7039",
+ "\u{2F914}"=>"\u701E",
+ "\u{2F915}"=>"\u701B",
+ "\u{2F916}"=>"\u3D96",
+ "\u{2F917}"=>"\u704A",
+ "\u{2F918}"=>"\u707D",
+ "\u{2F919}"=>"\u7077",
+ "\u{2F91A}"=>"\u70AD",
+ "\u{2F91B}"=>"\u{20525}",
+ "\u{2F91C}"=>"\u7145",
+ "\u{2F91D}"=>"\u{24263}",
+ "\u{2F91E}"=>"\u719C",
+ "\u{2F91F}"=>"\u{243AB}",
+ "\u{2F920}"=>"\u7228",
+ "\u{2F921}"=>"\u7235",
+ "\u{2F922}"=>"\u7250",
+ "\u{2F923}"=>"\u{24608}",
+ "\u{2F924}"=>"\u7280",
+ "\u{2F925}"=>"\u7295",
+ "\u{2F926}"=>"\u{24735}",
+ "\u{2F927}"=>"\u{24814}",
+ "\u{2F928}"=>"\u737A",
+ "\u{2F929}"=>"\u738B",
+ "\u{2F92A}"=>"\u3EAC",
+ "\u{2F92B}"=>"\u73A5",
+ "\u{2F92C}"=>"\u3EB8",
+ "\u{2F92D}"=>"\u3EB8",
+ "\u{2F92E}"=>"\u7447",
+ "\u{2F92F}"=>"\u745C",
+ "\u{2F930}"=>"\u7471",
+ "\u{2F931}"=>"\u7485",
+ "\u{2F932}"=>"\u74CA",
+ "\u{2F933}"=>"\u3F1B",
+ "\u{2F934}"=>"\u7524",
+ "\u{2F935}"=>"\u{24C36}",
+ "\u{2F936}"=>"\u753E",
+ "\u{2F937}"=>"\u{24C92}",
+ "\u{2F938}"=>"\u7570",
+ "\u{2F939}"=>"\u{2219F}",
+ "\u{2F93A}"=>"\u7610",
+ "\u{2F93B}"=>"\u{24FA1}",
+ "\u{2F93C}"=>"\u{24FB8}",
+ "\u{2F93D}"=>"\u{25044}",
+ "\u{2F93E}"=>"\u3FFC",
+ "\u{2F93F}"=>"\u4008",
+ "\u{2F940}"=>"\u76F4",
+ "\u{2F941}"=>"\u{250F3}",
+ "\u{2F942}"=>"\u{250F2}",
+ "\u{2F943}"=>"\u{25119}",
+ "\u{2F944}"=>"\u{25133}",
+ "\u{2F945}"=>"\u771E",
+ "\u{2F946}"=>"\u771F",
+ "\u{2F947}"=>"\u771F",
+ "\u{2F948}"=>"\u774A",
+ "\u{2F949}"=>"\u4039",
+ "\u{2F94A}"=>"\u778B",
+ "\u{2F94B}"=>"\u4046",
+ "\u{2F94C}"=>"\u4096",
+ "\u{2F94D}"=>"\u{2541D}",
+ "\u{2F94E}"=>"\u784E",
+ "\u{2F94F}"=>"\u788C",
+ "\u{2F950}"=>"\u78CC",
+ "\u{2F951}"=>"\u40E3",
+ "\u{2F952}"=>"\u{25626}",
+ "\u{2F953}"=>"\u7956",
+ "\u{2F954}"=>"\u{2569A}",
+ "\u{2F955}"=>"\u{256C5}",
+ "\u{2F956}"=>"\u798F",
+ "\u{2F957}"=>"\u79EB",
+ "\u{2F958}"=>"\u412F",
+ "\u{2F959}"=>"\u7A40",
+ "\u{2F95A}"=>"\u7A4A",
+ "\u{2F95B}"=>"\u7A4F",
+ "\u{2F95C}"=>"\u{2597C}",
+ "\u{2F95D}"=>"\u{25AA7}",
+ "\u{2F95E}"=>"\u{25AA7}",
+ "\u{2F95F}"=>"\u7AEE",
+ "\u{2F960}"=>"\u4202",
+ "\u{2F961}"=>"\u{25BAB}",
+ "\u{2F962}"=>"\u7BC6",
+ "\u{2F963}"=>"\u7BC9",
+ "\u{2F964}"=>"\u4227",
+ "\u{2F965}"=>"\u{25C80}",
+ "\u{2F966}"=>"\u7CD2",
+ "\u{2F967}"=>"\u42A0",
+ "\u{2F968}"=>"\u7CE8",
+ "\u{2F969}"=>"\u7CE3",
+ "\u{2F96A}"=>"\u7D00",
+ "\u{2F96B}"=>"\u{25F86}",
+ "\u{2F96C}"=>"\u7D63",
+ "\u{2F96D}"=>"\u4301",
+ "\u{2F96E}"=>"\u7DC7",
+ "\u{2F96F}"=>"\u7E02",
+ "\u{2F970}"=>"\u7E45",
+ "\u{2F971}"=>"\u4334",
+ "\u{2F972}"=>"\u{26228}",
+ "\u{2F973}"=>"\u{26247}",
+ "\u{2F974}"=>"\u4359",
+ "\u{2F975}"=>"\u{262D9}",
+ "\u{2F976}"=>"\u7F7A",
+ "\u{2F977}"=>"\u{2633E}",
+ "\u{2F978}"=>"\u7F95",
+ "\u{2F979}"=>"\u7FFA",
+ "\u{2F97A}"=>"\u8005",
+ "\u{2F97B}"=>"\u{264DA}",
+ "\u{2F97C}"=>"\u{26523}",
+ "\u{2F97D}"=>"\u8060",
+ "\u{2F97E}"=>"\u{265A8}",
+ "\u{2F97F}"=>"\u8070",
+ "\u{2F980}"=>"\u{2335F}",
+ "\u{2F981}"=>"\u43D5",
+ "\u{2F982}"=>"\u80B2",
+ "\u{2F983}"=>"\u8103",
+ "\u{2F984}"=>"\u440B",
+ "\u{2F985}"=>"\u813E",
+ "\u{2F986}"=>"\u5AB5",
+ "\u{2F987}"=>"\u{267A7}",
+ "\u{2F988}"=>"\u{267B5}",
+ "\u{2F989}"=>"\u{23393}",
+ "\u{2F98A}"=>"\u{2339C}",
+ "\u{2F98B}"=>"\u8201",
+ "\u{2F98C}"=>"\u8204",
+ "\u{2F98D}"=>"\u8F9E",
+ "\u{2F98E}"=>"\u446B",
+ "\u{2F98F}"=>"\u8291",
+ "\u{2F990}"=>"\u828B",
+ "\u{2F991}"=>"\u829D",
+ "\u{2F992}"=>"\u52B3",
+ "\u{2F993}"=>"\u82B1",
+ "\u{2F994}"=>"\u82B3",
+ "\u{2F995}"=>"\u82BD",
+ "\u{2F996}"=>"\u82E6",
+ "\u{2F997}"=>"\u{26B3C}",
+ "\u{2F998}"=>"\u82E5",
+ "\u{2F999}"=>"\u831D",
+ "\u{2F99A}"=>"\u8363",
+ "\u{2F99B}"=>"\u83AD",
+ "\u{2F99C}"=>"\u8323",
+ "\u{2F99D}"=>"\u83BD",
+ "\u{2F99E}"=>"\u83E7",
+ "\u{2F99F}"=>"\u8457",
+ "\u{2F9A0}"=>"\u8353",
+ "\u{2F9A1}"=>"\u83CA",
+ "\u{2F9A2}"=>"\u83CC",
+ "\u{2F9A3}"=>"\u83DC",
+ "\u{2F9A4}"=>"\u{26C36}",
+ "\u{2F9A5}"=>"\u{26D6B}",
+ "\u{2F9A6}"=>"\u{26CD5}",
+ "\u{2F9A7}"=>"\u452B",
+ "\u{2F9A8}"=>"\u84F1",
+ "\u{2F9A9}"=>"\u84F3",
+ "\u{2F9AA}"=>"\u8516",
+ "\u{2F9AB}"=>"\u{273CA}",
+ "\u{2F9AC}"=>"\u8564",
+ "\u{2F9AD}"=>"\u{26F2C}",
+ "\u{2F9AE}"=>"\u455D",
+ "\u{2F9AF}"=>"\u4561",
+ "\u{2F9B0}"=>"\u{26FB1}",
+ "\u{2F9B1}"=>"\u{270D2}",
+ "\u{2F9B2}"=>"\u456B",
+ "\u{2F9B3}"=>"\u8650",
+ "\u{2F9B4}"=>"\u865C",
+ "\u{2F9B5}"=>"\u8667",
+ "\u{2F9B6}"=>"\u8669",
+ "\u{2F9B7}"=>"\u86A9",
+ "\u{2F9B8}"=>"\u8688",
+ "\u{2F9B9}"=>"\u870E",
+ "\u{2F9BA}"=>"\u86E2",
+ "\u{2F9BB}"=>"\u8779",
+ "\u{2F9BC}"=>"\u8728",
+ "\u{2F9BD}"=>"\u876B",
+ "\u{2F9BE}"=>"\u8786",
+ "\u{2F9BF}"=>"\u45D7",
+ "\u{2F9C0}"=>"\u87E1",
+ "\u{2F9C1}"=>"\u8801",
+ "\u{2F9C2}"=>"\u45F9",
+ "\u{2F9C3}"=>"\u8860",
+ "\u{2F9C4}"=>"\u8863",
+ "\u{2F9C5}"=>"\u{27667}",
+ "\u{2F9C6}"=>"\u88D7",
+ "\u{2F9C7}"=>"\u88DE",
+ "\u{2F9C8}"=>"\u4635",
+ "\u{2F9C9}"=>"\u88FA",
+ "\u{2F9CA}"=>"\u34BB",
+ "\u{2F9CB}"=>"\u{278AE}",
+ "\u{2F9CC}"=>"\u{27966}",
+ "\u{2F9CD}"=>"\u46BE",
+ "\u{2F9CE}"=>"\u46C7",
+ "\u{2F9CF}"=>"\u8AA0",
+ "\u{2F9D0}"=>"\u8AED",
+ "\u{2F9D1}"=>"\u8B8A",
+ "\u{2F9D2}"=>"\u8C55",
+ "\u{2F9D3}"=>"\u{27CA8}",
+ "\u{2F9D4}"=>"\u8CAB",
+ "\u{2F9D5}"=>"\u8CC1",
+ "\u{2F9D6}"=>"\u8D1B",
+ "\u{2F9D7}"=>"\u8D77",
+ "\u{2F9D8}"=>"\u{27F2F}",
+ "\u{2F9D9}"=>"\u{20804}",
+ "\u{2F9DA}"=>"\u8DCB",
+ "\u{2F9DB}"=>"\u8DBC",
+ "\u{2F9DC}"=>"\u8DF0",
+ "\u{2F9DD}"=>"\u{208DE}",
+ "\u{2F9DE}"=>"\u8ED4",
+ "\u{2F9DF}"=>"\u8F38",
+ "\u{2F9E0}"=>"\u{285D2}",
+ "\u{2F9E1}"=>"\u{285ED}",
+ "\u{2F9E2}"=>"\u9094",
+ "\u{2F9E3}"=>"\u90F1",
+ "\u{2F9E4}"=>"\u9111",
+ "\u{2F9E5}"=>"\u{2872E}",
+ "\u{2F9E6}"=>"\u911B",
+ "\u{2F9E7}"=>"\u9238",
+ "\u{2F9E8}"=>"\u92D7",
+ "\u{2F9E9}"=>"\u92D8",
+ "\u{2F9EA}"=>"\u927C",
+ "\u{2F9EB}"=>"\u93F9",
+ "\u{2F9EC}"=>"\u9415",
+ "\u{2F9ED}"=>"\u{28BFA}",
+ "\u{2F9EE}"=>"\u958B",
+ "\u{2F9EF}"=>"\u4995",
+ "\u{2F9F0}"=>"\u95B7",
+ "\u{2F9F1}"=>"\u{28D77}",
+ "\u{2F9F2}"=>"\u49E6",
+ "\u{2F9F3}"=>"\u96C3",
+ "\u{2F9F4}"=>"\u5DB2",
+ "\u{2F9F5}"=>"\u9723",
+ "\u{2F9F6}"=>"\u{29145}",
+ "\u{2F9F7}"=>"\u{2921A}",
+ "\u{2F9F8}"=>"\u4A6E",
+ "\u{2F9F9}"=>"\u4A76",
+ "\u{2F9FA}"=>"\u97E0",
+ "\u{2F9FB}"=>"\u{2940A}",
+ "\u{2F9FC}"=>"\u4AB2",
+ "\u{2F9FD}"=>"\u{29496}",
+ "\u{2F9FE}"=>"\u980B",
+ "\u{2F9FF}"=>"\u980B",
+ "\u{2FA00}"=>"\u9829",
+ "\u{2FA01}"=>"\u{295B6}",
+ "\u{2FA02}"=>"\u98E2",
+ "\u{2FA03}"=>"\u4B33",
+ "\u{2FA04}"=>"\u9929",
+ "\u{2FA05}"=>"\u99A7",
+ "\u{2FA06}"=>"\u99C2",
+ "\u{2FA07}"=>"\u99FE",
+ "\u{2FA08}"=>"\u4BCE",
+ "\u{2FA09}"=>"\u{29B30}",
+ "\u{2FA0A}"=>"\u9B12",
+ "\u{2FA0B}"=>"\u9C40",
+ "\u{2FA0C}"=>"\u9CFD",
+ "\u{2FA0D}"=>"\u4CCE",
+ "\u{2FA0E}"=>"\u4CED",
+ "\u{2FA0F}"=>"\u9D67",
+ "\u{2FA10}"=>"\u{2A0CE}",
+ "\u{2FA11}"=>"\u4CF8",
+ "\u{2FA12}"=>"\u{2A105}",
+ "\u{2FA13}"=>"\u{2A20E}",
+ "\u{2FA14}"=>"\u{2A291}",
+ "\u{2FA15}"=>"\u9EBB",
+ "\u{2FA16}"=>"\u4D56",
+ "\u{2FA17}"=>"\u9EF9",
+ "\u{2FA18}"=>"\u9EFE",
+ "\u{2FA19}"=>"\u9F05",
+ "\u{2FA1A}"=>"\u9F0F",
+ "\u{2FA1B}"=>"\u9F16",
+ "\u{2FA1C}"=>"\u9F3B",
+ "\u{2FA1D}"=>"\u{2A600}",
}.freeze
KOMPATIBLE_TABLE = {
- "\u00A0"=>" ", "\u00A8"=>" \u0308", "\u00AA"=>"a", "\u00AF"=>" \u0304", "\u00B2"=>"2", "\u00B3"=>"3", "\u00B4"=>" \u0301", "\u00B5"=>"\u03BC",
- "\u00B8"=>" \u0327", "\u00B9"=>"1", "\u00BA"=>"o", "\u00BC"=>"1\u20444", "\u00BD"=>"1\u20442", "\u00BE"=>"3\u20444", "\u0132"=>"IJ", "\u0133"=>"ij",
- "\u013F"=>"L\u00B7", "\u0140"=>"l\u00B7", "\u0149"=>"\u02BCn", "\u017F"=>"s", "\u01C4"=>"D\u017D", "\u01C5"=>"D\u017E", "\u01C6"=>"d\u017E", "\u01C7"=>"LJ",
- "\u01C8"=>"Lj", "\u01C9"=>"lj", "\u01CA"=>"NJ", "\u01CB"=>"Nj", "\u01CC"=>"nj", "\u01F1"=>"DZ", "\u01F2"=>"Dz", "\u01F3"=>"dz",
- "\u02B0"=>"h", "\u02B1"=>"\u0266", "\u02B2"=>"j", "\u02B3"=>"r", "\u02B4"=>"\u0279", "\u02B5"=>"\u027B", "\u02B6"=>"\u0281", "\u02B7"=>"w",
- "\u02B8"=>"y", "\u02D8"=>" \u0306", "\u02D9"=>" \u0307", "\u02DA"=>" \u030A", "\u02DB"=>" \u0328", "\u02DC"=>" \u0303", "\u02DD"=>" \u030B", "\u02E0"=>"\u0263",
- "\u02E1"=>"l", "\u02E2"=>"s", "\u02E3"=>"x", "\u02E4"=>"\u0295", "\u037A"=>" \u0345", "\u0384"=>" \u0301", "\u03D0"=>"\u03B2", "\u03D1"=>"\u03B8",
- "\u03D2"=>"\u03A5", "\u03D5"=>"\u03C6", "\u03D6"=>"\u03C0", "\u03F0"=>"\u03BA", "\u03F1"=>"\u03C1", "\u03F2"=>"\u03C2", "\u03F4"=>"\u0398", "\u03F5"=>"\u03B5",
- "\u03F9"=>"\u03A3", "\u0587"=>"\u0565\u0582", "\u0675"=>"\u0627\u0674", "\u0676"=>"\u0648\u0674", "\u0677"=>"\u06C7\u0674", "\u0678"=>"\u064A\u0674", "\u0E33"=>"\u0E4D\u0E32", "\u0EB3"=>"\u0ECD\u0EB2",
- "\u0EDC"=>"\u0EAB\u0E99", "\u0EDD"=>"\u0EAB\u0EA1", "\u0F0C"=>"\u0F0B", "\u0F77"=>"\u0FB2\u0F81", "\u0F79"=>"\u0FB3\u0F81", "\u10FC"=>"\u10DC", "\u1D2C"=>"A", "\u1D2D"=>"\u00C6",
- "\u1D2E"=>"B", "\u1D30"=>"D", "\u1D31"=>"E", "\u1D32"=>"\u018E", "\u1D33"=>"G", "\u1D34"=>"H", "\u1D35"=>"I", "\u1D36"=>"J",
- "\u1D37"=>"K", "\u1D38"=>"L", "\u1D39"=>"M", "\u1D3A"=>"N", "\u1D3C"=>"O", "\u1D3D"=>"\u0222", "\u1D3E"=>"P", "\u1D3F"=>"R",
- "\u1D40"=>"T", "\u1D41"=>"U", "\u1D42"=>"W", "\u1D43"=>"a", "\u1D44"=>"\u0250", "\u1D45"=>"\u0251", "\u1D46"=>"\u1D02", "\u1D47"=>"b",
- "\u1D48"=>"d", "\u1D49"=>"e", "\u1D4A"=>"\u0259", "\u1D4B"=>"\u025B", "\u1D4C"=>"\u025C", "\u1D4D"=>"g", "\u1D4F"=>"k", "\u1D50"=>"m",
- "\u1D51"=>"\u014B", "\u1D52"=>"o", "\u1D53"=>"\u0254", "\u1D54"=>"\u1D16", "\u1D55"=>"\u1D17", "\u1D56"=>"p", "\u1D57"=>"t", "\u1D58"=>"u",
- "\u1D59"=>"\u1D1D", "\u1D5A"=>"\u026F", "\u1D5B"=>"v", "\u1D5C"=>"\u1D25", "\u1D5D"=>"\u03B2", "\u1D5E"=>"\u03B3", "\u1D5F"=>"\u03B4", "\u1D60"=>"\u03C6",
- "\u1D61"=>"\u03C7", "\u1D62"=>"i", "\u1D63"=>"r", "\u1D64"=>"u", "\u1D65"=>"v", "\u1D66"=>"\u03B2", "\u1D67"=>"\u03B3", "\u1D68"=>"\u03C1",
- "\u1D69"=>"\u03C6", "\u1D6A"=>"\u03C7", "\u1D78"=>"\u043D", "\u1D9B"=>"\u0252", "\u1D9C"=>"c", "\u1D9D"=>"\u0255", "\u1D9E"=>"\u00F0", "\u1D9F"=>"\u025C",
- "\u1DA0"=>"f", "\u1DA1"=>"\u025F", "\u1DA2"=>"\u0261", "\u1DA3"=>"\u0265", "\u1DA4"=>"\u0268", "\u1DA5"=>"\u0269", "\u1DA6"=>"\u026A", "\u1DA7"=>"\u1D7B",
- "\u1DA8"=>"\u029D", "\u1DA9"=>"\u026D", "\u1DAA"=>"\u1D85", "\u1DAB"=>"\u029F", "\u1DAC"=>"\u0271", "\u1DAD"=>"\u0270", "\u1DAE"=>"\u0272", "\u1DAF"=>"\u0273",
- "\u1DB0"=>"\u0274", "\u1DB1"=>"\u0275", "\u1DB2"=>"\u0278", "\u1DB3"=>"\u0282", "\u1DB4"=>"\u0283", "\u1DB5"=>"\u01AB", "\u1DB6"=>"\u0289", "\u1DB7"=>"\u028A",
- "\u1DB8"=>"\u1D1C", "\u1DB9"=>"\u028B", "\u1DBA"=>"\u028C", "\u1DBB"=>"z", "\u1DBC"=>"\u0290", "\u1DBD"=>"\u0291", "\u1DBE"=>"\u0292", "\u1DBF"=>"\u03B8",
- "\u1E9A"=>"a\u02BE", "\u1FBD"=>" \u0313", "\u1FBF"=>" \u0313", "\u1FC0"=>" \u0342", "\u1FFE"=>" \u0314", "\u2002"=>" ", "\u2003"=>" ", "\u2004"=>" ",
- "\u2005"=>" ", "\u2006"=>" ", "\u2007"=>" ", "\u2008"=>" ", "\u2009"=>" ", "\u200A"=>" ", "\u2011"=>"\u2010", "\u2017"=>" \u0333",
- "\u2024"=>".", "\u2025"=>"..", "\u2026"=>"...", "\u202F"=>" ", "\u2033"=>"\u2032\u2032", "\u2034"=>"\u2032\u2032\u2032", "\u2036"=>"\u2035\u2035", "\u2037"=>"\u2035\u2035\u2035",
- "\u203C"=>"!!", "\u203E"=>" \u0305", "\u2047"=>"??", "\u2048"=>"?!", "\u2049"=>"!?", "\u2057"=>"\u2032\u2032\u2032\u2032", "\u205F"=>" ", "\u2070"=>"0",
- "\u2071"=>"i", "\u2074"=>"4", "\u2075"=>"5", "\u2076"=>"6", "\u2077"=>"7", "\u2078"=>"8", "\u2079"=>"9", "\u207A"=>"+",
- "\u207B"=>"\u2212", "\u207C"=>"=", "\u207D"=>"(", "\u207E"=>")", "\u207F"=>"n", "\u2080"=>"0", "\u2081"=>"1", "\u2082"=>"2",
- "\u2083"=>"3", "\u2084"=>"4", "\u2085"=>"5", "\u2086"=>"6", "\u2087"=>"7", "\u2088"=>"8", "\u2089"=>"9", "\u208A"=>"+",
- "\u208B"=>"\u2212", "\u208C"=>"=", "\u208D"=>"(", "\u208E"=>")", "\u2090"=>"a", "\u2091"=>"e", "\u2092"=>"o", "\u2093"=>"x",
- "\u2094"=>"\u0259", "\u2095"=>"h", "\u2096"=>"k", "\u2097"=>"l", "\u2098"=>"m", "\u2099"=>"n", "\u209A"=>"p", "\u209B"=>"s",
- "\u209C"=>"t", "\u20A8"=>"Rs", "\u2100"=>"a/c", "\u2101"=>"a/s", "\u2102"=>"C", "\u2103"=>"\u00B0C", "\u2105"=>"c/o", "\u2106"=>"c/u",
- "\u2107"=>"\u0190", "\u2109"=>"\u00B0F", "\u210A"=>"g", "\u210B"=>"H", "\u210C"=>"H", "\u210D"=>"H", "\u210E"=>"h", "\u210F"=>"\u0127",
- "\u2110"=>"I", "\u2111"=>"I", "\u2112"=>"L", "\u2113"=>"l", "\u2115"=>"N", "\u2116"=>"No", "\u2119"=>"P", "\u211A"=>"Q",
- "\u211B"=>"R", "\u211C"=>"R", "\u211D"=>"R", "\u2120"=>"SM", "\u2121"=>"TEL", "\u2122"=>"TM", "\u2124"=>"Z", "\u2128"=>"Z",
- "\u212C"=>"B", "\u212D"=>"C", "\u212F"=>"e", "\u2130"=>"E", "\u2131"=>"F", "\u2133"=>"M", "\u2134"=>"o", "\u2135"=>"\u05D0",
- "\u2136"=>"\u05D1", "\u2137"=>"\u05D2", "\u2138"=>"\u05D3", "\u2139"=>"i", "\u213B"=>"FAX", "\u213C"=>"\u03C0", "\u213D"=>"\u03B3", "\u213E"=>"\u0393",
- "\u213F"=>"\u03A0", "\u2140"=>"\u2211", "\u2145"=>"D", "\u2146"=>"d", "\u2147"=>"e", "\u2148"=>"i", "\u2149"=>"j", "\u2150"=>"1\u20447",
- "\u2151"=>"1\u20449", "\u2152"=>"1\u204410", "\u2153"=>"1\u20443", "\u2154"=>"2\u20443", "\u2155"=>"1\u20445", "\u2156"=>"2\u20445", "\u2157"=>"3\u20445", "\u2158"=>"4\u20445",
- "\u2159"=>"1\u20446", "\u215A"=>"5\u20446", "\u215B"=>"1\u20448", "\u215C"=>"3\u20448", "\u215D"=>"5\u20448", "\u215E"=>"7\u20448", "\u215F"=>"1\u2044", "\u2160"=>"I",
- "\u2161"=>"II", "\u2162"=>"III", "\u2163"=>"IV", "\u2164"=>"V", "\u2165"=>"VI", "\u2166"=>"VII", "\u2167"=>"VIII", "\u2168"=>"IX",
- "\u2169"=>"X", "\u216A"=>"XI", "\u216B"=>"XII", "\u216C"=>"L", "\u216D"=>"C", "\u216E"=>"D", "\u216F"=>"M", "\u2170"=>"i",
- "\u2171"=>"ii", "\u2172"=>"iii", "\u2173"=>"iv", "\u2174"=>"v", "\u2175"=>"vi", "\u2176"=>"vii", "\u2177"=>"viii", "\u2178"=>"ix",
- "\u2179"=>"x", "\u217A"=>"xi", "\u217B"=>"xii", "\u217C"=>"l", "\u217D"=>"c", "\u217E"=>"d", "\u217F"=>"m", "\u2189"=>"0\u20443",
- "\u222C"=>"\u222B\u222B", "\u222D"=>"\u222B\u222B\u222B", "\u222F"=>"\u222E\u222E", "\u2230"=>"\u222E\u222E\u222E", "\u2460"=>"1", "\u2461"=>"2", "\u2462"=>"3", "\u2463"=>"4",
- "\u2464"=>"5", "\u2465"=>"6", "\u2466"=>"7", "\u2467"=>"8", "\u2468"=>"9", "\u2469"=>"10", "\u246A"=>"11", "\u246B"=>"12",
- "\u246C"=>"13", "\u246D"=>"14", "\u246E"=>"15", "\u246F"=>"16", "\u2470"=>"17", "\u2471"=>"18", "\u2472"=>"19", "\u2473"=>"20",
- "\u2474"=>"(1)", "\u2475"=>"(2)", "\u2476"=>"(3)", "\u2477"=>"(4)", "\u2478"=>"(5)", "\u2479"=>"(6)", "\u247A"=>"(7)", "\u247B"=>"(8)",
- "\u247C"=>"(9)", "\u247D"=>"(10)", "\u247E"=>"(11)", "\u247F"=>"(12)", "\u2480"=>"(13)", "\u2481"=>"(14)", "\u2482"=>"(15)", "\u2483"=>"(16)",
- "\u2484"=>"(17)", "\u2485"=>"(18)", "\u2486"=>"(19)", "\u2487"=>"(20)", "\u2488"=>"1.", "\u2489"=>"2.", "\u248A"=>"3.", "\u248B"=>"4.",
- "\u248C"=>"5.", "\u248D"=>"6.", "\u248E"=>"7.", "\u248F"=>"8.", "\u2490"=>"9.", "\u2491"=>"10.", "\u2492"=>"11.", "\u2493"=>"12.",
- "\u2494"=>"13.", "\u2495"=>"14.", "\u2496"=>"15.", "\u2497"=>"16.", "\u2498"=>"17.", "\u2499"=>"18.", "\u249A"=>"19.", "\u249B"=>"20.",
- "\u249C"=>"(a)", "\u249D"=>"(b)", "\u249E"=>"(c)", "\u249F"=>"(d)", "\u24A0"=>"(e)", "\u24A1"=>"(f)", "\u24A2"=>"(g)", "\u24A3"=>"(h)",
- "\u24A4"=>"(i)", "\u24A5"=>"(j)", "\u24A6"=>"(k)", "\u24A7"=>"(l)", "\u24A8"=>"(m)", "\u24A9"=>"(n)", "\u24AA"=>"(o)", "\u24AB"=>"(p)",
- "\u24AC"=>"(q)", "\u24AD"=>"(r)", "\u24AE"=>"(s)", "\u24AF"=>"(t)", "\u24B0"=>"(u)", "\u24B1"=>"(v)", "\u24B2"=>"(w)", "\u24B3"=>"(x)",
- "\u24B4"=>"(y)", "\u24B5"=>"(z)", "\u24B6"=>"A", "\u24B7"=>"B", "\u24B8"=>"C", "\u24B9"=>"D", "\u24BA"=>"E", "\u24BB"=>"F",
- "\u24BC"=>"G", "\u24BD"=>"H", "\u24BE"=>"I", "\u24BF"=>"J", "\u24C0"=>"K", "\u24C1"=>"L", "\u24C2"=>"M", "\u24C3"=>"N",
- "\u24C4"=>"O", "\u24C5"=>"P", "\u24C6"=>"Q", "\u24C7"=>"R", "\u24C8"=>"S", "\u24C9"=>"T", "\u24CA"=>"U", "\u24CB"=>"V",
- "\u24CC"=>"W", "\u24CD"=>"X", "\u24CE"=>"Y", "\u24CF"=>"Z", "\u24D0"=>"a", "\u24D1"=>"b", "\u24D2"=>"c", "\u24D3"=>"d",
- "\u24D4"=>"e", "\u24D5"=>"f", "\u24D6"=>"g", "\u24D7"=>"h", "\u24D8"=>"i", "\u24D9"=>"j", "\u24DA"=>"k", "\u24DB"=>"l",
- "\u24DC"=>"m", "\u24DD"=>"n", "\u24DE"=>"o", "\u24DF"=>"p", "\u24E0"=>"q", "\u24E1"=>"r", "\u24E2"=>"s", "\u24E3"=>"t",
- "\u24E4"=>"u", "\u24E5"=>"v", "\u24E6"=>"w", "\u24E7"=>"x", "\u24E8"=>"y", "\u24E9"=>"z", "\u24EA"=>"0", "\u2A0C"=>"\u222B\u222B\u222B\u222B",
- "\u2A74"=>"::=", "\u2A75"=>"==", "\u2A76"=>"===", "\u2C7C"=>"j", "\u2C7D"=>"V", "\u2D6F"=>"\u2D61", "\u2E9F"=>"\u6BCD", "\u2EF3"=>"\u9F9F",
- "\u2F00"=>"\u4E00", "\u2F01"=>"\u4E28", "\u2F02"=>"\u4E36", "\u2F03"=>"\u4E3F", "\u2F04"=>"\u4E59", "\u2F05"=>"\u4E85", "\u2F06"=>"\u4E8C", "\u2F07"=>"\u4EA0",
- "\u2F08"=>"\u4EBA", "\u2F09"=>"\u513F", "\u2F0A"=>"\u5165", "\u2F0B"=>"\u516B", "\u2F0C"=>"\u5182", "\u2F0D"=>"\u5196", "\u2F0E"=>"\u51AB", "\u2F0F"=>"\u51E0",
- "\u2F10"=>"\u51F5", "\u2F11"=>"\u5200", "\u2F12"=>"\u529B", "\u2F13"=>"\u52F9", "\u2F14"=>"\u5315", "\u2F15"=>"\u531A", "\u2F16"=>"\u5338", "\u2F17"=>"\u5341",
- "\u2F18"=>"\u535C", "\u2F19"=>"\u5369", "\u2F1A"=>"\u5382", "\u2F1B"=>"\u53B6", "\u2F1C"=>"\u53C8", "\u2F1D"=>"\u53E3", "\u2F1E"=>"\u56D7", "\u2F1F"=>"\u571F",
- "\u2F20"=>"\u58EB", "\u2F21"=>"\u5902", "\u2F22"=>"\u590A", "\u2F23"=>"\u5915", "\u2F24"=>"\u5927", "\u2F25"=>"\u5973", "\u2F26"=>"\u5B50", "\u2F27"=>"\u5B80",
- "\u2F28"=>"\u5BF8", "\u2F29"=>"\u5C0F", "\u2F2A"=>"\u5C22", "\u2F2B"=>"\u5C38", "\u2F2C"=>"\u5C6E", "\u2F2D"=>"\u5C71", "\u2F2E"=>"\u5DDB", "\u2F2F"=>"\u5DE5",
- "\u2F30"=>"\u5DF1", "\u2F31"=>"\u5DFE", "\u2F32"=>"\u5E72", "\u2F33"=>"\u5E7A", "\u2F34"=>"\u5E7F", "\u2F35"=>"\u5EF4", "\u2F36"=>"\u5EFE", "\u2F37"=>"\u5F0B",
- "\u2F38"=>"\u5F13", "\u2F39"=>"\u5F50", "\u2F3A"=>"\u5F61", "\u2F3B"=>"\u5F73", "\u2F3C"=>"\u5FC3", "\u2F3D"=>"\u6208", "\u2F3E"=>"\u6236", "\u2F3F"=>"\u624B",
- "\u2F40"=>"\u652F", "\u2F41"=>"\u6534", "\u2F42"=>"\u6587", "\u2F43"=>"\u6597", "\u2F44"=>"\u65A4", "\u2F45"=>"\u65B9", "\u2F46"=>"\u65E0", "\u2F47"=>"\u65E5",
- "\u2F48"=>"\u66F0", "\u2F49"=>"\u6708", "\u2F4A"=>"\u6728", "\u2F4B"=>"\u6B20", "\u2F4C"=>"\u6B62", "\u2F4D"=>"\u6B79", "\u2F4E"=>"\u6BB3", "\u2F4F"=>"\u6BCB",
- "\u2F50"=>"\u6BD4", "\u2F51"=>"\u6BDB", "\u2F52"=>"\u6C0F", "\u2F53"=>"\u6C14", "\u2F54"=>"\u6C34", "\u2F55"=>"\u706B", "\u2F56"=>"\u722A", "\u2F57"=>"\u7236",
- "\u2F58"=>"\u723B", "\u2F59"=>"\u723F", "\u2F5A"=>"\u7247", "\u2F5B"=>"\u7259", "\u2F5C"=>"\u725B", "\u2F5D"=>"\u72AC", "\u2F5E"=>"\u7384", "\u2F5F"=>"\u7389",
- "\u2F60"=>"\u74DC", "\u2F61"=>"\u74E6", "\u2F62"=>"\u7518", "\u2F63"=>"\u751F", "\u2F64"=>"\u7528", "\u2F65"=>"\u7530", "\u2F66"=>"\u758B", "\u2F67"=>"\u7592",
- "\u2F68"=>"\u7676", "\u2F69"=>"\u767D", "\u2F6A"=>"\u76AE", "\u2F6B"=>"\u76BF", "\u2F6C"=>"\u76EE", "\u2F6D"=>"\u77DB", "\u2F6E"=>"\u77E2", "\u2F6F"=>"\u77F3",
- "\u2F70"=>"\u793A", "\u2F71"=>"\u79B8", "\u2F72"=>"\u79BE", "\u2F73"=>"\u7A74", "\u2F74"=>"\u7ACB", "\u2F75"=>"\u7AF9", "\u2F76"=>"\u7C73", "\u2F77"=>"\u7CF8",
- "\u2F78"=>"\u7F36", "\u2F79"=>"\u7F51", "\u2F7A"=>"\u7F8A", "\u2F7B"=>"\u7FBD", "\u2F7C"=>"\u8001", "\u2F7D"=>"\u800C", "\u2F7E"=>"\u8012", "\u2F7F"=>"\u8033",
- "\u2F80"=>"\u807F", "\u2F81"=>"\u8089", "\u2F82"=>"\u81E3", "\u2F83"=>"\u81EA", "\u2F84"=>"\u81F3", "\u2F85"=>"\u81FC", "\u2F86"=>"\u820C", "\u2F87"=>"\u821B",
- "\u2F88"=>"\u821F", "\u2F89"=>"\u826E", "\u2F8A"=>"\u8272", "\u2F8B"=>"\u8278", "\u2F8C"=>"\u864D", "\u2F8D"=>"\u866B", "\u2F8E"=>"\u8840", "\u2F8F"=>"\u884C",
- "\u2F90"=>"\u8863", "\u2F91"=>"\u897E", "\u2F92"=>"\u898B", "\u2F93"=>"\u89D2", "\u2F94"=>"\u8A00", "\u2F95"=>"\u8C37", "\u2F96"=>"\u8C46", "\u2F97"=>"\u8C55",
- "\u2F98"=>"\u8C78", "\u2F99"=>"\u8C9D", "\u2F9A"=>"\u8D64", "\u2F9B"=>"\u8D70", "\u2F9C"=>"\u8DB3", "\u2F9D"=>"\u8EAB", "\u2F9E"=>"\u8ECA", "\u2F9F"=>"\u8F9B",
- "\u2FA0"=>"\u8FB0", "\u2FA1"=>"\u8FB5", "\u2FA2"=>"\u9091", "\u2FA3"=>"\u9149", "\u2FA4"=>"\u91C6", "\u2FA5"=>"\u91CC", "\u2FA6"=>"\u91D1", "\u2FA7"=>"\u9577",
- "\u2FA8"=>"\u9580", "\u2FA9"=>"\u961C", "\u2FAA"=>"\u96B6", "\u2FAB"=>"\u96B9", "\u2FAC"=>"\u96E8", "\u2FAD"=>"\u9751", "\u2FAE"=>"\u975E", "\u2FAF"=>"\u9762",
- "\u2FB0"=>"\u9769", "\u2FB1"=>"\u97CB", "\u2FB2"=>"\u97ED", "\u2FB3"=>"\u97F3", "\u2FB4"=>"\u9801", "\u2FB5"=>"\u98A8", "\u2FB6"=>"\u98DB", "\u2FB7"=>"\u98DF",
- "\u2FB8"=>"\u9996", "\u2FB9"=>"\u9999", "\u2FBA"=>"\u99AC", "\u2FBB"=>"\u9AA8", "\u2FBC"=>"\u9AD8", "\u2FBD"=>"\u9ADF", "\u2FBE"=>"\u9B25", "\u2FBF"=>"\u9B2F",
- "\u2FC0"=>"\u9B32", "\u2FC1"=>"\u9B3C", "\u2FC2"=>"\u9B5A", "\u2FC3"=>"\u9CE5", "\u2FC4"=>"\u9E75", "\u2FC5"=>"\u9E7F", "\u2FC6"=>"\u9EA5", "\u2FC7"=>"\u9EBB",
- "\u2FC8"=>"\u9EC3", "\u2FC9"=>"\u9ECD", "\u2FCA"=>"\u9ED1", "\u2FCB"=>"\u9EF9", "\u2FCC"=>"\u9EFD", "\u2FCD"=>"\u9F0E", "\u2FCE"=>"\u9F13", "\u2FCF"=>"\u9F20",
- "\u2FD0"=>"\u9F3B", "\u2FD1"=>"\u9F4A", "\u2FD2"=>"\u9F52", "\u2FD3"=>"\u9F8D", "\u2FD4"=>"\u9F9C", "\u2FD5"=>"\u9FA0", "\u3000"=>" ", "\u3036"=>"\u3012",
- "\u3038"=>"\u5341", "\u3039"=>"\u5344", "\u303A"=>"\u5345", "\u309B"=>" \u3099", "\u309C"=>" \u309A", "\u309F"=>"\u3088\u308A", "\u30FF"=>"\u30B3\u30C8", "\u3131"=>"\u1100",
- "\u3132"=>"\u1101", "\u3133"=>"\u11AA", "\u3134"=>"\u1102", "\u3135"=>"\u11AC", "\u3136"=>"\u11AD", "\u3137"=>"\u1103", "\u3138"=>"\u1104", "\u3139"=>"\u1105",
- "\u313A"=>"\u11B0", "\u313B"=>"\u11B1", "\u313C"=>"\u11B2", "\u313D"=>"\u11B3", "\u313E"=>"\u11B4", "\u313F"=>"\u11B5", "\u3140"=>"\u111A", "\u3141"=>"\u1106",
- "\u3142"=>"\u1107", "\u3143"=>"\u1108", "\u3144"=>"\u1121", "\u3145"=>"\u1109", "\u3146"=>"\u110A", "\u3147"=>"\u110B", "\u3148"=>"\u110C", "\u3149"=>"\u110D",
- "\u314A"=>"\u110E", "\u314B"=>"\u110F", "\u314C"=>"\u1110", "\u314D"=>"\u1111", "\u314E"=>"\u1112", "\u314F"=>"\u1161", "\u3150"=>"\u1162", "\u3151"=>"\u1163",
- "\u3152"=>"\u1164", "\u3153"=>"\u1165", "\u3154"=>"\u1166", "\u3155"=>"\u1167", "\u3156"=>"\u1168", "\u3157"=>"\u1169", "\u3158"=>"\u116A", "\u3159"=>"\u116B",
- "\u315A"=>"\u116C", "\u315B"=>"\u116D", "\u315C"=>"\u116E", "\u315D"=>"\u116F", "\u315E"=>"\u1170", "\u315F"=>"\u1171", "\u3160"=>"\u1172", "\u3161"=>"\u1173",
- "\u3162"=>"\u1174", "\u3163"=>"\u1175", "\u3164"=>"\u1160", "\u3165"=>"\u1114", "\u3166"=>"\u1115", "\u3167"=>"\u11C7", "\u3168"=>"\u11C8", "\u3169"=>"\u11CC",
- "\u316A"=>"\u11CE", "\u316B"=>"\u11D3", "\u316C"=>"\u11D7", "\u316D"=>"\u11D9", "\u316E"=>"\u111C", "\u316F"=>"\u11DD", "\u3170"=>"\u11DF", "\u3171"=>"\u111D",
- "\u3172"=>"\u111E", "\u3173"=>"\u1120", "\u3174"=>"\u1122", "\u3175"=>"\u1123", "\u3176"=>"\u1127", "\u3177"=>"\u1129", "\u3178"=>"\u112B", "\u3179"=>"\u112C",
- "\u317A"=>"\u112D", "\u317B"=>"\u112E", "\u317C"=>"\u112F", "\u317D"=>"\u1132", "\u317E"=>"\u1136", "\u317F"=>"\u1140", "\u3180"=>"\u1147", "\u3181"=>"\u114C",
- "\u3182"=>"\u11F1", "\u3183"=>"\u11F2", "\u3184"=>"\u1157", "\u3185"=>"\u1158", "\u3186"=>"\u1159", "\u3187"=>"\u1184", "\u3188"=>"\u1185", "\u3189"=>"\u1188",
- "\u318A"=>"\u1191", "\u318B"=>"\u1192", "\u318C"=>"\u1194", "\u318D"=>"\u119E", "\u318E"=>"\u11A1", "\u3192"=>"\u4E00", "\u3193"=>"\u4E8C", "\u3194"=>"\u4E09",
- "\u3195"=>"\u56DB", "\u3196"=>"\u4E0A", "\u3197"=>"\u4E2D", "\u3198"=>"\u4E0B", "\u3199"=>"\u7532", "\u319A"=>"\u4E59", "\u319B"=>"\u4E19", "\u319C"=>"\u4E01",
- "\u319D"=>"\u5929", "\u319E"=>"\u5730", "\u319F"=>"\u4EBA", "\u3200"=>"(\u1100)", "\u3201"=>"(\u1102)", "\u3202"=>"(\u1103)", "\u3203"=>"(\u1105)", "\u3204"=>"(\u1106)",
- "\u3205"=>"(\u1107)", "\u3206"=>"(\u1109)", "\u3207"=>"(\u110B)", "\u3208"=>"(\u110C)", "\u3209"=>"(\u110E)", "\u320A"=>"(\u110F)", "\u320B"=>"(\u1110)", "\u320C"=>"(\u1111)",
- "\u320D"=>"(\u1112)", "\u320E"=>"(\u1100\u1161)", "\u320F"=>"(\u1102\u1161)", "\u3210"=>"(\u1103\u1161)", "\u3211"=>"(\u1105\u1161)", "\u3212"=>"(\u1106\u1161)", "\u3213"=>"(\u1107\u1161)", "\u3214"=>"(\u1109\u1161)",
- "\u3215"=>"(\u110B\u1161)", "\u3216"=>"(\u110C\u1161)", "\u3217"=>"(\u110E\u1161)", "\u3218"=>"(\u110F\u1161)", "\u3219"=>"(\u1110\u1161)", "\u321A"=>"(\u1111\u1161)", "\u321B"=>"(\u1112\u1161)", "\u321C"=>"(\u110C\u116E)",
- "\u321D"=>"(\u110B\u1169\u110C\u1165\u11AB)", "\u321E"=>"(\u110B\u1169\u1112\u116E)", "\u3220"=>"(\u4E00)", "\u3221"=>"(\u4E8C)", "\u3222"=>"(\u4E09)", "\u3223"=>"(\u56DB)", "\u3224"=>"(\u4E94)", "\u3225"=>"(\u516D)",
- "\u3226"=>"(\u4E03)", "\u3227"=>"(\u516B)", "\u3228"=>"(\u4E5D)", "\u3229"=>"(\u5341)", "\u322A"=>"(\u6708)", "\u322B"=>"(\u706B)", "\u322C"=>"(\u6C34)", "\u322D"=>"(\u6728)",
- "\u322E"=>"(\u91D1)", "\u322F"=>"(\u571F)", "\u3230"=>"(\u65E5)", "\u3231"=>"(\u682A)", "\u3232"=>"(\u6709)", "\u3233"=>"(\u793E)", "\u3234"=>"(\u540D)", "\u3235"=>"(\u7279)",
- "\u3236"=>"(\u8CA1)", "\u3237"=>"(\u795D)", "\u3238"=>"(\u52B4)", "\u3239"=>"(\u4EE3)", "\u323A"=>"(\u547C)", "\u323B"=>"(\u5B66)", "\u323C"=>"(\u76E3)", "\u323D"=>"(\u4F01)",
- "\u323E"=>"(\u8CC7)", "\u323F"=>"(\u5354)", "\u3240"=>"(\u796D)", "\u3241"=>"(\u4F11)", "\u3242"=>"(\u81EA)", "\u3243"=>"(\u81F3)", "\u3244"=>"\u554F", "\u3245"=>"\u5E7C",
- "\u3246"=>"\u6587", "\u3247"=>"\u7B8F", "\u3250"=>"PTE", "\u3251"=>"21", "\u3252"=>"22", "\u3253"=>"23", "\u3254"=>"24", "\u3255"=>"25",
- "\u3256"=>"26", "\u3257"=>"27", "\u3258"=>"28", "\u3259"=>"29", "\u325A"=>"30", "\u325B"=>"31", "\u325C"=>"32", "\u325D"=>"33",
- "\u325E"=>"34", "\u325F"=>"35", "\u3260"=>"\u1100", "\u3261"=>"\u1102", "\u3262"=>"\u1103", "\u3263"=>"\u1105", "\u3264"=>"\u1106", "\u3265"=>"\u1107",
- "\u3266"=>"\u1109", "\u3267"=>"\u110B", "\u3268"=>"\u110C", "\u3269"=>"\u110E", "\u326A"=>"\u110F", "\u326B"=>"\u1110", "\u326C"=>"\u1111", "\u326D"=>"\u1112",
- "\u326E"=>"\u1100\u1161", "\u326F"=>"\u1102\u1161", "\u3270"=>"\u1103\u1161", "\u3271"=>"\u1105\u1161", "\u3272"=>"\u1106\u1161", "\u3273"=>"\u1107\u1161", "\u3274"=>"\u1109\u1161", "\u3275"=>"\u110B\u1161",
- "\u3276"=>"\u110C\u1161", "\u3277"=>"\u110E\u1161", "\u3278"=>"\u110F\u1161", "\u3279"=>"\u1110\u1161", "\u327A"=>"\u1111\u1161", "\u327B"=>"\u1112\u1161", "\u327C"=>"\u110E\u1161\u11B7\u1100\u1169", "\u327D"=>"\u110C\u116E\u110B\u1174",
- "\u327E"=>"\u110B\u116E", "\u3280"=>"\u4E00", "\u3281"=>"\u4E8C", "\u3282"=>"\u4E09", "\u3283"=>"\u56DB", "\u3284"=>"\u4E94", "\u3285"=>"\u516D", "\u3286"=>"\u4E03",
- "\u3287"=>"\u516B", "\u3288"=>"\u4E5D", "\u3289"=>"\u5341", "\u328A"=>"\u6708", "\u328B"=>"\u706B", "\u328C"=>"\u6C34", "\u328D"=>"\u6728", "\u328E"=>"\u91D1",
- "\u328F"=>"\u571F", "\u3290"=>"\u65E5", "\u3291"=>"\u682A", "\u3292"=>"\u6709", "\u3293"=>"\u793E", "\u3294"=>"\u540D", "\u3295"=>"\u7279", "\u3296"=>"\u8CA1",
- "\u3297"=>"\u795D", "\u3298"=>"\u52B4", "\u3299"=>"\u79D8", "\u329A"=>"\u7537", "\u329B"=>"\u5973", "\u329C"=>"\u9069", "\u329D"=>"\u512A", "\u329E"=>"\u5370",
- "\u329F"=>"\u6CE8", "\u32A0"=>"\u9805", "\u32A1"=>"\u4F11", "\u32A2"=>"\u5199", "\u32A3"=>"\u6B63", "\u32A4"=>"\u4E0A", "\u32A5"=>"\u4E2D", "\u32A6"=>"\u4E0B",
- "\u32A7"=>"\u5DE6", "\u32A8"=>"\u53F3", "\u32A9"=>"\u533B", "\u32AA"=>"\u5B97", "\u32AB"=>"\u5B66", "\u32AC"=>"\u76E3", "\u32AD"=>"\u4F01", "\u32AE"=>"\u8CC7",
- "\u32AF"=>"\u5354", "\u32B0"=>"\u591C", "\u32B1"=>"36", "\u32B2"=>"37", "\u32B3"=>"38", "\u32B4"=>"39", "\u32B5"=>"40", "\u32B6"=>"41",
- "\u32B7"=>"42", "\u32B8"=>"43", "\u32B9"=>"44", "\u32BA"=>"45", "\u32BB"=>"46", "\u32BC"=>"47", "\u32BD"=>"48", "\u32BE"=>"49",
- "\u32BF"=>"50", "\u32C0"=>"1\u6708", "\u32C1"=>"2\u6708", "\u32C2"=>"3\u6708", "\u32C3"=>"4\u6708", "\u32C4"=>"5\u6708", "\u32C5"=>"6\u6708", "\u32C6"=>"7\u6708",
- "\u32C7"=>"8\u6708", "\u32C8"=>"9\u6708", "\u32C9"=>"10\u6708", "\u32CA"=>"11\u6708", "\u32CB"=>"12\u6708", "\u32CC"=>"Hg", "\u32CD"=>"erg", "\u32CE"=>"eV",
- "\u32CF"=>"LTD", "\u32D0"=>"\u30A2", "\u32D1"=>"\u30A4", "\u32D2"=>"\u30A6", "\u32D3"=>"\u30A8", "\u32D4"=>"\u30AA", "\u32D5"=>"\u30AB", "\u32D6"=>"\u30AD",
- "\u32D7"=>"\u30AF", "\u32D8"=>"\u30B1", "\u32D9"=>"\u30B3", "\u32DA"=>"\u30B5", "\u32DB"=>"\u30B7", "\u32DC"=>"\u30B9", "\u32DD"=>"\u30BB", "\u32DE"=>"\u30BD",
- "\u32DF"=>"\u30BF", "\u32E0"=>"\u30C1", "\u32E1"=>"\u30C4", "\u32E2"=>"\u30C6", "\u32E3"=>"\u30C8", "\u32E4"=>"\u30CA", "\u32E5"=>"\u30CB", "\u32E6"=>"\u30CC",
- "\u32E7"=>"\u30CD", "\u32E8"=>"\u30CE", "\u32E9"=>"\u30CF", "\u32EA"=>"\u30D2", "\u32EB"=>"\u30D5", "\u32EC"=>"\u30D8", "\u32ED"=>"\u30DB", "\u32EE"=>"\u30DE",
- "\u32EF"=>"\u30DF", "\u32F0"=>"\u30E0", "\u32F1"=>"\u30E1", "\u32F2"=>"\u30E2", "\u32F3"=>"\u30E4", "\u32F4"=>"\u30E6", "\u32F5"=>"\u30E8", "\u32F6"=>"\u30E9",
- "\u32F7"=>"\u30EA", "\u32F8"=>"\u30EB", "\u32F9"=>"\u30EC", "\u32FA"=>"\u30ED", "\u32FB"=>"\u30EF", "\u32FC"=>"\u30F0", "\u32FD"=>"\u30F1", "\u32FE"=>"\u30F2",
- "\u3300"=>"\u30A2\u30D1\u30FC\u30C8", "\u3301"=>"\u30A2\u30EB\u30D5\u30A1", "\u3302"=>"\u30A2\u30F3\u30DA\u30A2", "\u3303"=>"\u30A2\u30FC\u30EB", "\u3304"=>"\u30A4\u30CB\u30F3\u30B0", "\u3305"=>"\u30A4\u30F3\u30C1", "\u3306"=>"\u30A6\u30A9\u30F3", "\u3307"=>"\u30A8\u30B9\u30AF\u30FC\u30C9",
- "\u3308"=>"\u30A8\u30FC\u30AB\u30FC", "\u3309"=>"\u30AA\u30F3\u30B9", "\u330A"=>"\u30AA\u30FC\u30E0", "\u330B"=>"\u30AB\u30A4\u30EA", "\u330C"=>"\u30AB\u30E9\u30C3\u30C8", "\u330D"=>"\u30AB\u30ED\u30EA\u30FC", "\u330E"=>"\u30AC\u30ED\u30F3", "\u330F"=>"\u30AC\u30F3\u30DE",
- "\u3310"=>"\u30AE\u30AC", "\u3311"=>"\u30AE\u30CB\u30FC", "\u3312"=>"\u30AD\u30E5\u30EA\u30FC", "\u3313"=>"\u30AE\u30EB\u30C0\u30FC", "\u3314"=>"\u30AD\u30ED", "\u3315"=>"\u30AD\u30ED\u30B0\u30E9\u30E0", "\u3316"=>"\u30AD\u30ED\u30E1\u30FC\u30C8\u30EB", "\u3317"=>"\u30AD\u30ED\u30EF\u30C3\u30C8",
- "\u3318"=>"\u30B0\u30E9\u30E0", "\u3319"=>"\u30B0\u30E9\u30E0\u30C8\u30F3", "\u331A"=>"\u30AF\u30EB\u30BC\u30A4\u30ED", "\u331B"=>"\u30AF\u30ED\u30FC\u30CD", "\u331C"=>"\u30B1\u30FC\u30B9", "\u331D"=>"\u30B3\u30EB\u30CA", "\u331E"=>"\u30B3\u30FC\u30DD", "\u331F"=>"\u30B5\u30A4\u30AF\u30EB",
- "\u3320"=>"\u30B5\u30F3\u30C1\u30FC\u30E0", "\u3321"=>"\u30B7\u30EA\u30F3\u30B0", "\u3322"=>"\u30BB\u30F3\u30C1", "\u3323"=>"\u30BB\u30F3\u30C8", "\u3324"=>"\u30C0\u30FC\u30B9", "\u3325"=>"\u30C7\u30B7", "\u3326"=>"\u30C9\u30EB", "\u3327"=>"\u30C8\u30F3",
- "\u3328"=>"\u30CA\u30CE", "\u3329"=>"\u30CE\u30C3\u30C8", "\u332A"=>"\u30CF\u30A4\u30C4", "\u332B"=>"\u30D1\u30FC\u30BB\u30F3\u30C8", "\u332C"=>"\u30D1\u30FC\u30C4", "\u332D"=>"\u30D0\u30FC\u30EC\u30EB", "\u332E"=>"\u30D4\u30A2\u30B9\u30C8\u30EB", "\u332F"=>"\u30D4\u30AF\u30EB",
- "\u3330"=>"\u30D4\u30B3", "\u3331"=>"\u30D3\u30EB", "\u3332"=>"\u30D5\u30A1\u30E9\u30C3\u30C9", "\u3333"=>"\u30D5\u30A3\u30FC\u30C8", "\u3334"=>"\u30D6\u30C3\u30B7\u30A7\u30EB", "\u3335"=>"\u30D5\u30E9\u30F3", "\u3336"=>"\u30D8\u30AF\u30BF\u30FC\u30EB", "\u3337"=>"\u30DA\u30BD",
- "\u3338"=>"\u30DA\u30CB\u30D2", "\u3339"=>"\u30D8\u30EB\u30C4", "\u333A"=>"\u30DA\u30F3\u30B9", "\u333B"=>"\u30DA\u30FC\u30B8", "\u333C"=>"\u30D9\u30FC\u30BF", "\u333D"=>"\u30DD\u30A4\u30F3\u30C8", "\u333E"=>"\u30DC\u30EB\u30C8", "\u333F"=>"\u30DB\u30F3",
- "\u3340"=>"\u30DD\u30F3\u30C9", "\u3341"=>"\u30DB\u30FC\u30EB", "\u3342"=>"\u30DB\u30FC\u30F3", "\u3343"=>"\u30DE\u30A4\u30AF\u30ED", "\u3344"=>"\u30DE\u30A4\u30EB", "\u3345"=>"\u30DE\u30C3\u30CF", "\u3346"=>"\u30DE\u30EB\u30AF", "\u3347"=>"\u30DE\u30F3\u30B7\u30E7\u30F3",
- "\u3348"=>"\u30DF\u30AF\u30ED\u30F3", "\u3349"=>"\u30DF\u30EA", "\u334A"=>"\u30DF\u30EA\u30D0\u30FC\u30EB", "\u334B"=>"\u30E1\u30AC", "\u334C"=>"\u30E1\u30AC\u30C8\u30F3", "\u334D"=>"\u30E1\u30FC\u30C8\u30EB", "\u334E"=>"\u30E4\u30FC\u30C9", "\u334F"=>"\u30E4\u30FC\u30EB",
- "\u3350"=>"\u30E6\u30A2\u30F3", "\u3351"=>"\u30EA\u30C3\u30C8\u30EB", "\u3352"=>"\u30EA\u30E9", "\u3353"=>"\u30EB\u30D4\u30FC", "\u3354"=>"\u30EB\u30FC\u30D6\u30EB", "\u3355"=>"\u30EC\u30E0", "\u3356"=>"\u30EC\u30F3\u30C8\u30B2\u30F3", "\u3357"=>"\u30EF\u30C3\u30C8",
- "\u3358"=>"0\u70B9", "\u3359"=>"1\u70B9", "\u335A"=>"2\u70B9", "\u335B"=>"3\u70B9", "\u335C"=>"4\u70B9", "\u335D"=>"5\u70B9", "\u335E"=>"6\u70B9", "\u335F"=>"7\u70B9",
- "\u3360"=>"8\u70B9", "\u3361"=>"9\u70B9", "\u3362"=>"10\u70B9", "\u3363"=>"11\u70B9", "\u3364"=>"12\u70B9", "\u3365"=>"13\u70B9", "\u3366"=>"14\u70B9", "\u3367"=>"15\u70B9",
- "\u3368"=>"16\u70B9", "\u3369"=>"17\u70B9", "\u336A"=>"18\u70B9", "\u336B"=>"19\u70B9", "\u336C"=>"20\u70B9", "\u336D"=>"21\u70B9", "\u336E"=>"22\u70B9", "\u336F"=>"23\u70B9",
- "\u3370"=>"24\u70B9", "\u3371"=>"hPa", "\u3372"=>"da", "\u3373"=>"AU", "\u3374"=>"bar", "\u3375"=>"oV", "\u3376"=>"pc", "\u3377"=>"dm",
- "\u3378"=>"dm2", "\u3379"=>"dm3", "\u337A"=>"IU", "\u337B"=>"\u5E73\u6210", "\u337C"=>"\u662D\u548C", "\u337D"=>"\u5927\u6B63", "\u337E"=>"\u660E\u6CBB", "\u337F"=>"\u682A\u5F0F\u4F1A\u793E",
- "\u3380"=>"pA", "\u3381"=>"nA", "\u3382"=>"\u03BCA", "\u3383"=>"mA", "\u3384"=>"kA", "\u3385"=>"KB", "\u3386"=>"MB", "\u3387"=>"GB",
- "\u3388"=>"cal", "\u3389"=>"kcal", "\u338A"=>"pF", "\u338B"=>"nF", "\u338C"=>"\u03BCF", "\u338D"=>"\u03BCg", "\u338E"=>"mg", "\u338F"=>"kg",
- "\u3390"=>"Hz", "\u3391"=>"kHz", "\u3392"=>"MHz", "\u3393"=>"GHz", "\u3394"=>"THz", "\u3395"=>"\u03BCl", "\u3396"=>"ml", "\u3397"=>"dl",
- "\u3398"=>"kl", "\u3399"=>"fm", "\u339A"=>"nm", "\u339B"=>"\u03BCm", "\u339C"=>"mm", "\u339D"=>"cm", "\u339E"=>"km", "\u339F"=>"mm2",
- "\u33A0"=>"cm2", "\u33A1"=>"m2", "\u33A2"=>"km2", "\u33A3"=>"mm3", "\u33A4"=>"cm3", "\u33A5"=>"m3", "\u33A6"=>"km3", "\u33A7"=>"m\u2215s",
- "\u33A8"=>"m\u2215s2", "\u33A9"=>"Pa", "\u33AA"=>"kPa", "\u33AB"=>"MPa", "\u33AC"=>"GPa", "\u33AD"=>"rad", "\u33AE"=>"rad\u2215s", "\u33AF"=>"rad\u2215s2",
- "\u33B0"=>"ps", "\u33B1"=>"ns", "\u33B2"=>"\u03BCs", "\u33B3"=>"ms", "\u33B4"=>"pV", "\u33B5"=>"nV", "\u33B6"=>"\u03BCV", "\u33B7"=>"mV",
- "\u33B8"=>"kV", "\u33B9"=>"MV", "\u33BA"=>"pW", "\u33BB"=>"nW", "\u33BC"=>"\u03BCW", "\u33BD"=>"mW", "\u33BE"=>"kW", "\u33BF"=>"MW",
- "\u33C0"=>"k\u03A9", "\u33C1"=>"M\u03A9", "\u33C2"=>"a.m.", "\u33C3"=>"Bq", "\u33C4"=>"cc", "\u33C5"=>"cd", "\u33C6"=>"C\u2215kg", "\u33C7"=>"Co.",
- "\u33C8"=>"dB", "\u33C9"=>"Gy", "\u33CA"=>"ha", "\u33CB"=>"HP", "\u33CC"=>"in", "\u33CD"=>"KK", "\u33CE"=>"KM", "\u33CF"=>"kt",
- "\u33D0"=>"lm", "\u33D1"=>"ln", "\u33D2"=>"log", "\u33D3"=>"lx", "\u33D4"=>"mb", "\u33D5"=>"mil", "\u33D6"=>"mol", "\u33D7"=>"PH",
- "\u33D8"=>"p.m.", "\u33D9"=>"PPM", "\u33DA"=>"PR", "\u33DB"=>"sr", "\u33DC"=>"Sv", "\u33DD"=>"Wb", "\u33DE"=>"V\u2215m", "\u33DF"=>"A\u2215m",
- "\u33E0"=>"1\u65E5", "\u33E1"=>"2\u65E5", "\u33E2"=>"3\u65E5", "\u33E3"=>"4\u65E5", "\u33E4"=>"5\u65E5", "\u33E5"=>"6\u65E5", "\u33E6"=>"7\u65E5", "\u33E7"=>"8\u65E5",
- "\u33E8"=>"9\u65E5", "\u33E9"=>"10\u65E5", "\u33EA"=>"11\u65E5", "\u33EB"=>"12\u65E5", "\u33EC"=>"13\u65E5", "\u33ED"=>"14\u65E5", "\u33EE"=>"15\u65E5", "\u33EF"=>"16\u65E5",
- "\u33F0"=>"17\u65E5", "\u33F1"=>"18\u65E5", "\u33F2"=>"19\u65E5", "\u33F3"=>"20\u65E5", "\u33F4"=>"21\u65E5", "\u33F5"=>"22\u65E5", "\u33F6"=>"23\u65E5", "\u33F7"=>"24\u65E5",
- "\u33F8"=>"25\u65E5", "\u33F9"=>"26\u65E5", "\u33FA"=>"27\u65E5", "\u33FB"=>"28\u65E5", "\u33FC"=>"29\u65E5", "\u33FD"=>"30\u65E5", "\u33FE"=>"31\u65E5", "\u33FF"=>"gal",
- "\uA69C"=>"\u044A", "\uA69D"=>"\u044C", "\uA770"=>"\uA76F", "\uA7F8"=>"\u0126", "\uA7F9"=>"\u0153", "\uAB5C"=>"\uA727", "\uAB5D"=>"\uAB37", "\uAB5E"=>"\u026B",
- "\uAB5F"=>"\uAB52", "\uFB00"=>"ff", "\uFB01"=>"fi", "\uFB02"=>"fl", "\uFB03"=>"ffi", "\uFB04"=>"ffl", "\uFB05"=>"st", "\uFB06"=>"st",
- "\uFB13"=>"\u0574\u0576", "\uFB14"=>"\u0574\u0565", "\uFB15"=>"\u0574\u056B", "\uFB16"=>"\u057E\u0576", "\uFB17"=>"\u0574\u056D", "\uFB20"=>"\u05E2", "\uFB21"=>"\u05D0", "\uFB22"=>"\u05D3",
- "\uFB23"=>"\u05D4", "\uFB24"=>"\u05DB", "\uFB25"=>"\u05DC", "\uFB26"=>"\u05DD", "\uFB27"=>"\u05E8", "\uFB28"=>"\u05EA", "\uFB29"=>"+", "\uFB4F"=>"\u05D0\u05DC",
- "\uFB50"=>"\u0671", "\uFB51"=>"\u0671", "\uFB52"=>"\u067B", "\uFB53"=>"\u067B", "\uFB54"=>"\u067B", "\uFB55"=>"\u067B", "\uFB56"=>"\u067E", "\uFB57"=>"\u067E",
- "\uFB58"=>"\u067E", "\uFB59"=>"\u067E", "\uFB5A"=>"\u0680", "\uFB5B"=>"\u0680", "\uFB5C"=>"\u0680", "\uFB5D"=>"\u0680", "\uFB5E"=>"\u067A", "\uFB5F"=>"\u067A",
- "\uFB60"=>"\u067A", "\uFB61"=>"\u067A", "\uFB62"=>"\u067F", "\uFB63"=>"\u067F", "\uFB64"=>"\u067F", "\uFB65"=>"\u067F", "\uFB66"=>"\u0679", "\uFB67"=>"\u0679",
- "\uFB68"=>"\u0679", "\uFB69"=>"\u0679", "\uFB6A"=>"\u06A4", "\uFB6B"=>"\u06A4", "\uFB6C"=>"\u06A4", "\uFB6D"=>"\u06A4", "\uFB6E"=>"\u06A6", "\uFB6F"=>"\u06A6",
- "\uFB70"=>"\u06A6", "\uFB71"=>"\u06A6", "\uFB72"=>"\u0684", "\uFB73"=>"\u0684", "\uFB74"=>"\u0684", "\uFB75"=>"\u0684", "\uFB76"=>"\u0683", "\uFB77"=>"\u0683",
- "\uFB78"=>"\u0683", "\uFB79"=>"\u0683", "\uFB7A"=>"\u0686", "\uFB7B"=>"\u0686", "\uFB7C"=>"\u0686", "\uFB7D"=>"\u0686", "\uFB7E"=>"\u0687", "\uFB7F"=>"\u0687",
- "\uFB80"=>"\u0687", "\uFB81"=>"\u0687", "\uFB82"=>"\u068D", "\uFB83"=>"\u068D", "\uFB84"=>"\u068C", "\uFB85"=>"\u068C", "\uFB86"=>"\u068E", "\uFB87"=>"\u068E",
- "\uFB88"=>"\u0688", "\uFB89"=>"\u0688", "\uFB8A"=>"\u0698", "\uFB8B"=>"\u0698", "\uFB8C"=>"\u0691", "\uFB8D"=>"\u0691", "\uFB8E"=>"\u06A9", "\uFB8F"=>"\u06A9",
- "\uFB90"=>"\u06A9", "\uFB91"=>"\u06A9", "\uFB92"=>"\u06AF", "\uFB93"=>"\u06AF", "\uFB94"=>"\u06AF", "\uFB95"=>"\u06AF", "\uFB96"=>"\u06B3", "\uFB97"=>"\u06B3",
- "\uFB98"=>"\u06B3", "\uFB99"=>"\u06B3", "\uFB9A"=>"\u06B1", "\uFB9B"=>"\u06B1", "\uFB9C"=>"\u06B1", "\uFB9D"=>"\u06B1", "\uFB9E"=>"\u06BA", "\uFB9F"=>"\u06BA",
- "\uFBA0"=>"\u06BB", "\uFBA1"=>"\u06BB", "\uFBA2"=>"\u06BB", "\uFBA3"=>"\u06BB", "\uFBA4"=>"\u06C0", "\uFBA5"=>"\u06C0", "\uFBA6"=>"\u06C1", "\uFBA7"=>"\u06C1",
- "\uFBA8"=>"\u06C1", "\uFBA9"=>"\u06C1", "\uFBAA"=>"\u06BE", "\uFBAB"=>"\u06BE", "\uFBAC"=>"\u06BE", "\uFBAD"=>"\u06BE", "\uFBAE"=>"\u06D2", "\uFBAF"=>"\u06D2",
- "\uFBB0"=>"\u06D3", "\uFBB1"=>"\u06D3", "\uFBD3"=>"\u06AD", "\uFBD4"=>"\u06AD", "\uFBD5"=>"\u06AD", "\uFBD6"=>"\u06AD", "\uFBD7"=>"\u06C7", "\uFBD8"=>"\u06C7",
- "\uFBD9"=>"\u06C6", "\uFBDA"=>"\u06C6", "\uFBDB"=>"\u06C8", "\uFBDC"=>"\u06C8", "\uFBDD"=>"\u06C7\u0674", "\uFBDE"=>"\u06CB", "\uFBDF"=>"\u06CB", "\uFBE0"=>"\u06C5",
- "\uFBE1"=>"\u06C5", "\uFBE2"=>"\u06C9", "\uFBE3"=>"\u06C9", "\uFBE4"=>"\u06D0", "\uFBE5"=>"\u06D0", "\uFBE6"=>"\u06D0", "\uFBE7"=>"\u06D0", "\uFBE8"=>"\u0649",
- "\uFBE9"=>"\u0649", "\uFBEA"=>"\u0626\u0627", "\uFBEB"=>"\u0626\u0627", "\uFBEC"=>"\u0626\u06D5", "\uFBED"=>"\u0626\u06D5", "\uFBEE"=>"\u0626\u0648", "\uFBEF"=>"\u0626\u0648", "\uFBF0"=>"\u0626\u06C7",
- "\uFBF1"=>"\u0626\u06C7", "\uFBF2"=>"\u0626\u06C6", "\uFBF3"=>"\u0626\u06C6", "\uFBF4"=>"\u0626\u06C8", "\uFBF5"=>"\u0626\u06C8", "\uFBF6"=>"\u0626\u06D0", "\uFBF7"=>"\u0626\u06D0", "\uFBF8"=>"\u0626\u06D0",
- "\uFBF9"=>"\u0626\u0649", "\uFBFA"=>"\u0626\u0649", "\uFBFB"=>"\u0626\u0649", "\uFBFC"=>"\u06CC", "\uFBFD"=>"\u06CC", "\uFBFE"=>"\u06CC", "\uFBFF"=>"\u06CC", "\uFC00"=>"\u0626\u062C",
- "\uFC01"=>"\u0626\u062D", "\uFC02"=>"\u0626\u0645", "\uFC03"=>"\u0626\u0649", "\uFC04"=>"\u0626\u064A", "\uFC05"=>"\u0628\u062C", "\uFC06"=>"\u0628\u062D", "\uFC07"=>"\u0628\u062E", "\uFC08"=>"\u0628\u0645",
- "\uFC09"=>"\u0628\u0649", "\uFC0A"=>"\u0628\u064A", "\uFC0B"=>"\u062A\u062C", "\uFC0C"=>"\u062A\u062D", "\uFC0D"=>"\u062A\u062E", "\uFC0E"=>"\u062A\u0645", "\uFC0F"=>"\u062A\u0649", "\uFC10"=>"\u062A\u064A",
- "\uFC11"=>"\u062B\u062C", "\uFC12"=>"\u062B\u0645", "\uFC13"=>"\u062B\u0649", "\uFC14"=>"\u062B\u064A", "\uFC15"=>"\u062C\u062D", "\uFC16"=>"\u062C\u0645", "\uFC17"=>"\u062D\u062C", "\uFC18"=>"\u062D\u0645",
- "\uFC19"=>"\u062E\u062C", "\uFC1A"=>"\u062E\u062D", "\uFC1B"=>"\u062E\u0645", "\uFC1C"=>"\u0633\u062C", "\uFC1D"=>"\u0633\u062D", "\uFC1E"=>"\u0633\u062E", "\uFC1F"=>"\u0633\u0645", "\uFC20"=>"\u0635\u062D",
- "\uFC21"=>"\u0635\u0645", "\uFC22"=>"\u0636\u062C", "\uFC23"=>"\u0636\u062D", "\uFC24"=>"\u0636\u062E", "\uFC25"=>"\u0636\u0645", "\uFC26"=>"\u0637\u062D", "\uFC27"=>"\u0637\u0645", "\uFC28"=>"\u0638\u0645",
- "\uFC29"=>"\u0639\u062C", "\uFC2A"=>"\u0639\u0645", "\uFC2B"=>"\u063A\u062C", "\uFC2C"=>"\u063A\u0645", "\uFC2D"=>"\u0641\u062C", "\uFC2E"=>"\u0641\u062D", "\uFC2F"=>"\u0641\u062E", "\uFC30"=>"\u0641\u0645",
- "\uFC31"=>"\u0641\u0649", "\uFC32"=>"\u0641\u064A", "\uFC33"=>"\u0642\u062D", "\uFC34"=>"\u0642\u0645", "\uFC35"=>"\u0642\u0649", "\uFC36"=>"\u0642\u064A", "\uFC37"=>"\u0643\u0627", "\uFC38"=>"\u0643\u062C",
- "\uFC39"=>"\u0643\u062D", "\uFC3A"=>"\u0643\u062E", "\uFC3B"=>"\u0643\u0644", "\uFC3C"=>"\u0643\u0645", "\uFC3D"=>"\u0643\u0649", "\uFC3E"=>"\u0643\u064A", "\uFC3F"=>"\u0644\u062C", "\uFC40"=>"\u0644\u062D",
- "\uFC41"=>"\u0644\u062E", "\uFC42"=>"\u0644\u0645", "\uFC43"=>"\u0644\u0649", "\uFC44"=>"\u0644\u064A", "\uFC45"=>"\u0645\u062C", "\uFC46"=>"\u0645\u062D", "\uFC47"=>"\u0645\u062E", "\uFC48"=>"\u0645\u0645",
- "\uFC49"=>"\u0645\u0649", "\uFC4A"=>"\u0645\u064A", "\uFC4B"=>"\u0646\u062C", "\uFC4C"=>"\u0646\u062D", "\uFC4D"=>"\u0646\u062E", "\uFC4E"=>"\u0646\u0645", "\uFC4F"=>"\u0646\u0649", "\uFC50"=>"\u0646\u064A",
- "\uFC51"=>"\u0647\u062C", "\uFC52"=>"\u0647\u0645", "\uFC53"=>"\u0647\u0649", "\uFC54"=>"\u0647\u064A", "\uFC55"=>"\u064A\u062C", "\uFC56"=>"\u064A\u062D", "\uFC57"=>"\u064A\u062E", "\uFC58"=>"\u064A\u0645",
- "\uFC59"=>"\u064A\u0649", "\uFC5A"=>"\u064A\u064A", "\uFC5B"=>"\u0630\u0670", "\uFC5C"=>"\u0631\u0670", "\uFC5D"=>"\u0649\u0670", "\uFC5E"=>" \u064C\u0651", "\uFC5F"=>" \u064D\u0651", "\uFC60"=>" \u064E\u0651",
- "\uFC61"=>" \u064F\u0651", "\uFC62"=>" \u0650\u0651", "\uFC63"=>" \u0651\u0670", "\uFC64"=>"\u0626\u0631", "\uFC65"=>"\u0626\u0632", "\uFC66"=>"\u0626\u0645", "\uFC67"=>"\u0626\u0646", "\uFC68"=>"\u0626\u0649",
- "\uFC69"=>"\u0626\u064A", "\uFC6A"=>"\u0628\u0631", "\uFC6B"=>"\u0628\u0632", "\uFC6C"=>"\u0628\u0645", "\uFC6D"=>"\u0628\u0646", "\uFC6E"=>"\u0628\u0649", "\uFC6F"=>"\u0628\u064A", "\uFC70"=>"\u062A\u0631",
- "\uFC71"=>"\u062A\u0632", "\uFC72"=>"\u062A\u0645", "\uFC73"=>"\u062A\u0646", "\uFC74"=>"\u062A\u0649", "\uFC75"=>"\u062A\u064A", "\uFC76"=>"\u062B\u0631", "\uFC77"=>"\u062B\u0632", "\uFC78"=>"\u062B\u0645",
- "\uFC79"=>"\u062B\u0646", "\uFC7A"=>"\u062B\u0649", "\uFC7B"=>"\u062B\u064A", "\uFC7C"=>"\u0641\u0649", "\uFC7D"=>"\u0641\u064A", "\uFC7E"=>"\u0642\u0649", "\uFC7F"=>"\u0642\u064A", "\uFC80"=>"\u0643\u0627",
- "\uFC81"=>"\u0643\u0644", "\uFC82"=>"\u0643\u0645", "\uFC83"=>"\u0643\u0649", "\uFC84"=>"\u0643\u064A", "\uFC85"=>"\u0644\u0645", "\uFC86"=>"\u0644\u0649", "\uFC87"=>"\u0644\u064A", "\uFC88"=>"\u0645\u0627",
- "\uFC89"=>"\u0645\u0645", "\uFC8A"=>"\u0646\u0631", "\uFC8B"=>"\u0646\u0632", "\uFC8C"=>"\u0646\u0645", "\uFC8D"=>"\u0646\u0646", "\uFC8E"=>"\u0646\u0649", "\uFC8F"=>"\u0646\u064A", "\uFC90"=>"\u0649\u0670",
- "\uFC91"=>"\u064A\u0631", "\uFC92"=>"\u064A\u0632", "\uFC93"=>"\u064A\u0645", "\uFC94"=>"\u064A\u0646", "\uFC95"=>"\u064A\u0649", "\uFC96"=>"\u064A\u064A", "\uFC97"=>"\u0626\u062C", "\uFC98"=>"\u0626\u062D",
- "\uFC99"=>"\u0626\u062E", "\uFC9A"=>"\u0626\u0645", "\uFC9B"=>"\u0626\u0647", "\uFC9C"=>"\u0628\u062C", "\uFC9D"=>"\u0628\u062D", "\uFC9E"=>"\u0628\u062E", "\uFC9F"=>"\u0628\u0645", "\uFCA0"=>"\u0628\u0647",
- "\uFCA1"=>"\u062A\u062C", "\uFCA2"=>"\u062A\u062D", "\uFCA3"=>"\u062A\u062E", "\uFCA4"=>"\u062A\u0645", "\uFCA5"=>"\u062A\u0647", "\uFCA6"=>"\u062B\u0645", "\uFCA7"=>"\u062C\u062D", "\uFCA8"=>"\u062C\u0645",
- "\uFCA9"=>"\u062D\u062C", "\uFCAA"=>"\u062D\u0645", "\uFCAB"=>"\u062E\u062C", "\uFCAC"=>"\u062E\u0645", "\uFCAD"=>"\u0633\u062C", "\uFCAE"=>"\u0633\u062D", "\uFCAF"=>"\u0633\u062E", "\uFCB0"=>"\u0633\u0645",
- "\uFCB1"=>"\u0635\u062D", "\uFCB2"=>"\u0635\u062E", "\uFCB3"=>"\u0635\u0645", "\uFCB4"=>"\u0636\u062C", "\uFCB5"=>"\u0636\u062D", "\uFCB6"=>"\u0636\u062E", "\uFCB7"=>"\u0636\u0645", "\uFCB8"=>"\u0637\u062D",
- "\uFCB9"=>"\u0638\u0645", "\uFCBA"=>"\u0639\u062C", "\uFCBB"=>"\u0639\u0645", "\uFCBC"=>"\u063A\u062C", "\uFCBD"=>"\u063A\u0645", "\uFCBE"=>"\u0641\u062C", "\uFCBF"=>"\u0641\u062D", "\uFCC0"=>"\u0641\u062E",
- "\uFCC1"=>"\u0641\u0645", "\uFCC2"=>"\u0642\u062D", "\uFCC3"=>"\u0642\u0645", "\uFCC4"=>"\u0643\u062C", "\uFCC5"=>"\u0643\u062D", "\uFCC6"=>"\u0643\u062E", "\uFCC7"=>"\u0643\u0644", "\uFCC8"=>"\u0643\u0645",
- "\uFCC9"=>"\u0644\u062C", "\uFCCA"=>"\u0644\u062D", "\uFCCB"=>"\u0644\u062E", "\uFCCC"=>"\u0644\u0645", "\uFCCD"=>"\u0644\u0647", "\uFCCE"=>"\u0645\u062C", "\uFCCF"=>"\u0645\u062D", "\uFCD0"=>"\u0645\u062E",
- "\uFCD1"=>"\u0645\u0645", "\uFCD2"=>"\u0646\u062C", "\uFCD3"=>"\u0646\u062D", "\uFCD4"=>"\u0646\u062E", "\uFCD5"=>"\u0646\u0645", "\uFCD6"=>"\u0646\u0647", "\uFCD7"=>"\u0647\u062C", "\uFCD8"=>"\u0647\u0645",
- "\uFCD9"=>"\u0647\u0670", "\uFCDA"=>"\u064A\u062C", "\uFCDB"=>"\u064A\u062D", "\uFCDC"=>"\u064A\u062E", "\uFCDD"=>"\u064A\u0645", "\uFCDE"=>"\u064A\u0647", "\uFCDF"=>"\u0626\u0645", "\uFCE0"=>"\u0626\u0647",
- "\uFCE1"=>"\u0628\u0645", "\uFCE2"=>"\u0628\u0647", "\uFCE3"=>"\u062A\u0645", "\uFCE4"=>"\u062A\u0647", "\uFCE5"=>"\u062B\u0645", "\uFCE6"=>"\u062B\u0647", "\uFCE7"=>"\u0633\u0645", "\uFCE8"=>"\u0633\u0647",
- "\uFCE9"=>"\u0634\u0645", "\uFCEA"=>"\u0634\u0647", "\uFCEB"=>"\u0643\u0644", "\uFCEC"=>"\u0643\u0645", "\uFCED"=>"\u0644\u0645", "\uFCEE"=>"\u0646\u0645", "\uFCEF"=>"\u0646\u0647", "\uFCF0"=>"\u064A\u0645",
- "\uFCF1"=>"\u064A\u0647", "\uFCF2"=>"\u0640\u064E\u0651", "\uFCF3"=>"\u0640\u064F\u0651", "\uFCF4"=>"\u0640\u0650\u0651", "\uFCF5"=>"\u0637\u0649", "\uFCF6"=>"\u0637\u064A", "\uFCF7"=>"\u0639\u0649", "\uFCF8"=>"\u0639\u064A",
- "\uFCF9"=>"\u063A\u0649", "\uFCFA"=>"\u063A\u064A", "\uFCFB"=>"\u0633\u0649", "\uFCFC"=>"\u0633\u064A", "\uFCFD"=>"\u0634\u0649", "\uFCFE"=>"\u0634\u064A", "\uFCFF"=>"\u062D\u0649", "\uFD00"=>"\u062D\u064A",
- "\uFD01"=>"\u062C\u0649", "\uFD02"=>"\u062C\u064A", "\uFD03"=>"\u062E\u0649", "\uFD04"=>"\u062E\u064A", "\uFD05"=>"\u0635\u0649", "\uFD06"=>"\u0635\u064A", "\uFD07"=>"\u0636\u0649", "\uFD08"=>"\u0636\u064A",
- "\uFD09"=>"\u0634\u062C", "\uFD0A"=>"\u0634\u062D", "\uFD0B"=>"\u0634\u062E", "\uFD0C"=>"\u0634\u0645", "\uFD0D"=>"\u0634\u0631", "\uFD0E"=>"\u0633\u0631", "\uFD0F"=>"\u0635\u0631", "\uFD10"=>"\u0636\u0631",
- "\uFD11"=>"\u0637\u0649", "\uFD12"=>"\u0637\u064A", "\uFD13"=>"\u0639\u0649", "\uFD14"=>"\u0639\u064A", "\uFD15"=>"\u063A\u0649", "\uFD16"=>"\u063A\u064A", "\uFD17"=>"\u0633\u0649", "\uFD18"=>"\u0633\u064A",
- "\uFD19"=>"\u0634\u0649", "\uFD1A"=>"\u0634\u064A", "\uFD1B"=>"\u062D\u0649", "\uFD1C"=>"\u062D\u064A", "\uFD1D"=>"\u062C\u0649", "\uFD1E"=>"\u062C\u064A", "\uFD1F"=>"\u062E\u0649", "\uFD20"=>"\u062E\u064A",
- "\uFD21"=>"\u0635\u0649", "\uFD22"=>"\u0635\u064A", "\uFD23"=>"\u0636\u0649", "\uFD24"=>"\u0636\u064A", "\uFD25"=>"\u0634\u062C", "\uFD26"=>"\u0634\u062D", "\uFD27"=>"\u0634\u062E", "\uFD28"=>"\u0634\u0645",
- "\uFD29"=>"\u0634\u0631", "\uFD2A"=>"\u0633\u0631", "\uFD2B"=>"\u0635\u0631", "\uFD2C"=>"\u0636\u0631", "\uFD2D"=>"\u0634\u062C", "\uFD2E"=>"\u0634\u062D", "\uFD2F"=>"\u0634\u062E", "\uFD30"=>"\u0634\u0645",
- "\uFD31"=>"\u0633\u0647", "\uFD32"=>"\u0634\u0647", "\uFD33"=>"\u0637\u0645", "\uFD34"=>"\u0633\u062C", "\uFD35"=>"\u0633\u062D", "\uFD36"=>"\u0633\u062E", "\uFD37"=>"\u0634\u062C", "\uFD38"=>"\u0634\u062D",
- "\uFD39"=>"\u0634\u062E", "\uFD3A"=>"\u0637\u0645", "\uFD3B"=>"\u0638\u0645", "\uFD3C"=>"\u0627\u064B", "\uFD3D"=>"\u0627\u064B", "\uFD50"=>"\u062A\u062C\u0645", "\uFD51"=>"\u062A\u062D\u062C", "\uFD52"=>"\u062A\u062D\u062C",
- "\uFD53"=>"\u062A\u062D\u0645", "\uFD54"=>"\u062A\u062E\u0645", "\uFD55"=>"\u062A\u0645\u062C", "\uFD56"=>"\u062A\u0645\u062D", "\uFD57"=>"\u062A\u0645\u062E", "\uFD58"=>"\u062C\u0645\u062D", "\uFD59"=>"\u062C\u0645\u062D", "\uFD5A"=>"\u062D\u0645\u064A",
- "\uFD5B"=>"\u062D\u0645\u0649", "\uFD5C"=>"\u0633\u062D\u062C", "\uFD5D"=>"\u0633\u062C\u062D", "\uFD5E"=>"\u0633\u062C\u0649", "\uFD5F"=>"\u0633\u0645\u062D", "\uFD60"=>"\u0633\u0645\u062D", "\uFD61"=>"\u0633\u0645\u062C", "\uFD62"=>"\u0633\u0645\u0645",
- "\uFD63"=>"\u0633\u0645\u0645", "\uFD64"=>"\u0635\u062D\u062D", "\uFD65"=>"\u0635\u062D\u062D", "\uFD66"=>"\u0635\u0645\u0645", "\uFD67"=>"\u0634\u062D\u0645", "\uFD68"=>"\u0634\u062D\u0645", "\uFD69"=>"\u0634\u062C\u064A", "\uFD6A"=>"\u0634\u0645\u062E",
- "\uFD6B"=>"\u0634\u0645\u062E", "\uFD6C"=>"\u0634\u0645\u0645", "\uFD6D"=>"\u0634\u0645\u0645", "\uFD6E"=>"\u0636\u062D\u0649", "\uFD6F"=>"\u0636\u062E\u0645", "\uFD70"=>"\u0636\u062E\u0645", "\uFD71"=>"\u0637\u0645\u062D", "\uFD72"=>"\u0637\u0645\u062D",
- "\uFD73"=>"\u0637\u0645\u0645", "\uFD74"=>"\u0637\u0645\u064A", "\uFD75"=>"\u0639\u062C\u0645", "\uFD76"=>"\u0639\u0645\u0645", "\uFD77"=>"\u0639\u0645\u0645", "\uFD78"=>"\u0639\u0645\u0649", "\uFD79"=>"\u063A\u0645\u0645", "\uFD7A"=>"\u063A\u0645\u064A",
- "\uFD7B"=>"\u063A\u0645\u0649", "\uFD7C"=>"\u0641\u062E\u0645", "\uFD7D"=>"\u0641\u062E\u0645", "\uFD7E"=>"\u0642\u0645\u062D", "\uFD7F"=>"\u0642\u0645\u0645", "\uFD80"=>"\u0644\u062D\u0645", "\uFD81"=>"\u0644\u062D\u064A", "\uFD82"=>"\u0644\u062D\u0649",
- "\uFD83"=>"\u0644\u062C\u062C", "\uFD84"=>"\u0644\u062C\u062C", "\uFD85"=>"\u0644\u062E\u0645", "\uFD86"=>"\u0644\u062E\u0645", "\uFD87"=>"\u0644\u0645\u062D", "\uFD88"=>"\u0644\u0645\u062D", "\uFD89"=>"\u0645\u062D\u062C", "\uFD8A"=>"\u0645\u062D\u0645",
- "\uFD8B"=>"\u0645\u062D\u064A", "\uFD8C"=>"\u0645\u062C\u062D", "\uFD8D"=>"\u0645\u062C\u0645", "\uFD8E"=>"\u0645\u062E\u062C", "\uFD8F"=>"\u0645\u062E\u0645", "\uFD92"=>"\u0645\u062C\u062E", "\uFD93"=>"\u0647\u0645\u062C", "\uFD94"=>"\u0647\u0645\u0645",
- "\uFD95"=>"\u0646\u062D\u0645", "\uFD96"=>"\u0646\u062D\u0649", "\uFD97"=>"\u0646\u062C\u0645", "\uFD98"=>"\u0646\u062C\u0645", "\uFD99"=>"\u0646\u062C\u0649", "\uFD9A"=>"\u0646\u0645\u064A", "\uFD9B"=>"\u0646\u0645\u0649", "\uFD9C"=>"\u064A\u0645\u0645",
- "\uFD9D"=>"\u064A\u0645\u0645", "\uFD9E"=>"\u0628\u062E\u064A", "\uFD9F"=>"\u062A\u062C\u064A", "\uFDA0"=>"\u062A\u062C\u0649", "\uFDA1"=>"\u062A\u062E\u064A", "\uFDA2"=>"\u062A\u062E\u0649", "\uFDA3"=>"\u062A\u0645\u064A", "\uFDA4"=>"\u062A\u0645\u0649",
- "\uFDA5"=>"\u062C\u0645\u064A", "\uFDA6"=>"\u062C\u062D\u0649", "\uFDA7"=>"\u062C\u0645\u0649", "\uFDA8"=>"\u0633\u062E\u0649", "\uFDA9"=>"\u0635\u062D\u064A", "\uFDAA"=>"\u0634\u062D\u064A", "\uFDAB"=>"\u0636\u062D\u064A", "\uFDAC"=>"\u0644\u062C\u064A",
- "\uFDAD"=>"\u0644\u0645\u064A", "\uFDAE"=>"\u064A\u062D\u064A", "\uFDAF"=>"\u064A\u062C\u064A", "\uFDB0"=>"\u064A\u0645\u064A", "\uFDB1"=>"\u0645\u0645\u064A", "\uFDB2"=>"\u0642\u0645\u064A", "\uFDB3"=>"\u0646\u062D\u064A", "\uFDB4"=>"\u0642\u0645\u062D",
- "\uFDB5"=>"\u0644\u062D\u0645", "\uFDB6"=>"\u0639\u0645\u064A", "\uFDB7"=>"\u0643\u0645\u064A", "\uFDB8"=>"\u0646\u062C\u062D", "\uFDB9"=>"\u0645\u062E\u064A", "\uFDBA"=>"\u0644\u062C\u0645", "\uFDBB"=>"\u0643\u0645\u0645", "\uFDBC"=>"\u0644\u062C\u0645",
- "\uFDBD"=>"\u0646\u062C\u062D", "\uFDBE"=>"\u062C\u062D\u064A", "\uFDBF"=>"\u062D\u062C\u064A", "\uFDC0"=>"\u0645\u062C\u064A", "\uFDC1"=>"\u0641\u0645\u064A", "\uFDC2"=>"\u0628\u062D\u064A", "\uFDC3"=>"\u0643\u0645\u0645", "\uFDC4"=>"\u0639\u062C\u0645",
- "\uFDC5"=>"\u0635\u0645\u0645", "\uFDC6"=>"\u0633\u062E\u064A", "\uFDC7"=>"\u0646\u062C\u064A", "\uFDF0"=>"\u0635\u0644\u06D2", "\uFDF1"=>"\u0642\u0644\u06D2", "\uFDF2"=>"\u0627\u0644\u0644\u0647", "\uFDF3"=>"\u0627\u0643\u0628\u0631", "\uFDF4"=>"\u0645\u062D\u0645\u062F",
- "\uFDF5"=>"\u0635\u0644\u0639\u0645", "\uFDF6"=>"\u0631\u0633\u0648\u0644", "\uFDF7"=>"\u0639\u0644\u064A\u0647", "\uFDF8"=>"\u0648\u0633\u0644\u0645", "\uFDF9"=>"\u0635\u0644\u0649", "\uFDFA"=>"\u0635\u0644\u0649 \u0627\u0644\u0644\u0647 \u0639\u0644\u064A\u0647 \u0648\u0633\u0644\u0645", "\uFDFB"=>"\u062C\u0644 \u062C\u0644\u0627\u0644\u0647", "\uFDFC"=>"\u0631\u06CC\u0627\u0644",
- "\uFE10"=>",", "\uFE11"=>"\u3001", "\uFE12"=>"\u3002", "\uFE13"=>":", "\uFE14"=>";", "\uFE15"=>"!", "\uFE16"=>"?", "\uFE17"=>"\u3016",
- "\uFE18"=>"\u3017", "\uFE19"=>"...", "\uFE30"=>"..", "\uFE31"=>"\u2014", "\uFE32"=>"\u2013", "\uFE33"=>"_", "\uFE34"=>"_", "\uFE35"=>"(",
- "\uFE36"=>")", "\uFE37"=>"{", "\uFE38"=>"}", "\uFE39"=>"\u3014", "\uFE3A"=>"\u3015", "\uFE3B"=>"\u3010", "\uFE3C"=>"\u3011", "\uFE3D"=>"\u300A",
- "\uFE3E"=>"\u300B", "\uFE3F"=>"\u3008", "\uFE40"=>"\u3009", "\uFE41"=>"\u300C", "\uFE42"=>"\u300D", "\uFE43"=>"\u300E", "\uFE44"=>"\u300F", "\uFE47"=>"[",
- "\uFE48"=>"]", "\uFE49"=>" \u0305", "\uFE4A"=>" \u0305", "\uFE4B"=>" \u0305", "\uFE4C"=>" \u0305", "\uFE4D"=>"_", "\uFE4E"=>"_", "\uFE4F"=>"_",
- "\uFE50"=>",", "\uFE51"=>"\u3001", "\uFE52"=>".", "\uFE54"=>";", "\uFE55"=>":", "\uFE56"=>"?", "\uFE57"=>"!", "\uFE58"=>"\u2014",
- "\uFE59"=>"(", "\uFE5A"=>")", "\uFE5B"=>"{", "\uFE5C"=>"}", "\uFE5D"=>"\u3014", "\uFE5E"=>"\u3015", "\uFE5F"=>"#", "\uFE60"=>"&",
- "\uFE61"=>"*", "\uFE62"=>"+", "\uFE63"=>"-", "\uFE64"=>"<", "\uFE65"=>">", "\uFE66"=>"=", "\uFE68"=>"\\", "\uFE69"=>"$",
- "\uFE6A"=>"%", "\uFE6B"=>"@", "\uFE70"=>" \u064B", "\uFE71"=>"\u0640\u064B", "\uFE72"=>" \u064C", "\uFE74"=>" \u064D", "\uFE76"=>" \u064E", "\uFE77"=>"\u0640\u064E",
- "\uFE78"=>" \u064F", "\uFE79"=>"\u0640\u064F", "\uFE7A"=>" \u0650", "\uFE7B"=>"\u0640\u0650", "\uFE7C"=>" \u0651", "\uFE7D"=>"\u0640\u0651", "\uFE7E"=>" \u0652", "\uFE7F"=>"\u0640\u0652",
- "\uFE80"=>"\u0621", "\uFE81"=>"\u0622", "\uFE82"=>"\u0622", "\uFE83"=>"\u0623", "\uFE84"=>"\u0623", "\uFE85"=>"\u0624", "\uFE86"=>"\u0624", "\uFE87"=>"\u0625",
- "\uFE88"=>"\u0625", "\uFE89"=>"\u0626", "\uFE8A"=>"\u0626", "\uFE8B"=>"\u0626", "\uFE8C"=>"\u0626", "\uFE8D"=>"\u0627", "\uFE8E"=>"\u0627", "\uFE8F"=>"\u0628",
- "\uFE90"=>"\u0628", "\uFE91"=>"\u0628", "\uFE92"=>"\u0628", "\uFE93"=>"\u0629", "\uFE94"=>"\u0629", "\uFE95"=>"\u062A", "\uFE96"=>"\u062A", "\uFE97"=>"\u062A",
- "\uFE98"=>"\u062A", "\uFE99"=>"\u062B", "\uFE9A"=>"\u062B", "\uFE9B"=>"\u062B", "\uFE9C"=>"\u062B", "\uFE9D"=>"\u062C", "\uFE9E"=>"\u062C", "\uFE9F"=>"\u062C",
- "\uFEA0"=>"\u062C", "\uFEA1"=>"\u062D", "\uFEA2"=>"\u062D", "\uFEA3"=>"\u062D", "\uFEA4"=>"\u062D", "\uFEA5"=>"\u062E", "\uFEA6"=>"\u062E", "\uFEA7"=>"\u062E",
- "\uFEA8"=>"\u062E", "\uFEA9"=>"\u062F", "\uFEAA"=>"\u062F", "\uFEAB"=>"\u0630", "\uFEAC"=>"\u0630", "\uFEAD"=>"\u0631", "\uFEAE"=>"\u0631", "\uFEAF"=>"\u0632",
- "\uFEB0"=>"\u0632", "\uFEB1"=>"\u0633", "\uFEB2"=>"\u0633", "\uFEB3"=>"\u0633", "\uFEB4"=>"\u0633", "\uFEB5"=>"\u0634", "\uFEB6"=>"\u0634", "\uFEB7"=>"\u0634",
- "\uFEB8"=>"\u0634", "\uFEB9"=>"\u0635", "\uFEBA"=>"\u0635", "\uFEBB"=>"\u0635", "\uFEBC"=>"\u0635", "\uFEBD"=>"\u0636", "\uFEBE"=>"\u0636", "\uFEBF"=>"\u0636",
- "\uFEC0"=>"\u0636", "\uFEC1"=>"\u0637", "\uFEC2"=>"\u0637", "\uFEC3"=>"\u0637", "\uFEC4"=>"\u0637", "\uFEC5"=>"\u0638", "\uFEC6"=>"\u0638", "\uFEC7"=>"\u0638",
- "\uFEC8"=>"\u0638", "\uFEC9"=>"\u0639", "\uFECA"=>"\u0639", "\uFECB"=>"\u0639", "\uFECC"=>"\u0639", "\uFECD"=>"\u063A", "\uFECE"=>"\u063A", "\uFECF"=>"\u063A",
- "\uFED0"=>"\u063A", "\uFED1"=>"\u0641", "\uFED2"=>"\u0641", "\uFED3"=>"\u0641", "\uFED4"=>"\u0641", "\uFED5"=>"\u0642", "\uFED6"=>"\u0642", "\uFED7"=>"\u0642",
- "\uFED8"=>"\u0642", "\uFED9"=>"\u0643", "\uFEDA"=>"\u0643", "\uFEDB"=>"\u0643", "\uFEDC"=>"\u0643", "\uFEDD"=>"\u0644", "\uFEDE"=>"\u0644", "\uFEDF"=>"\u0644",
- "\uFEE0"=>"\u0644", "\uFEE1"=>"\u0645", "\uFEE2"=>"\u0645", "\uFEE3"=>"\u0645", "\uFEE4"=>"\u0645", "\uFEE5"=>"\u0646", "\uFEE6"=>"\u0646", "\uFEE7"=>"\u0646",
- "\uFEE8"=>"\u0646", "\uFEE9"=>"\u0647", "\uFEEA"=>"\u0647", "\uFEEB"=>"\u0647", "\uFEEC"=>"\u0647", "\uFEED"=>"\u0648", "\uFEEE"=>"\u0648", "\uFEEF"=>"\u0649",
- "\uFEF0"=>"\u0649", "\uFEF1"=>"\u064A", "\uFEF2"=>"\u064A", "\uFEF3"=>"\u064A", "\uFEF4"=>"\u064A", "\uFEF5"=>"\u0644\u0622", "\uFEF6"=>"\u0644\u0622", "\uFEF7"=>"\u0644\u0623",
- "\uFEF8"=>"\u0644\u0623", "\uFEF9"=>"\u0644\u0625", "\uFEFA"=>"\u0644\u0625", "\uFEFB"=>"\u0644\u0627", "\uFEFC"=>"\u0644\u0627", "\uFF01"=>"!", "\uFF02"=>"\"", "\uFF03"=>"#",
- "\uFF04"=>"$", "\uFF05"=>"%", "\uFF06"=>"&", "\uFF07"=>"'", "\uFF08"=>"(", "\uFF09"=>")", "\uFF0A"=>"*", "\uFF0B"=>"+",
- "\uFF0C"=>",", "\uFF0D"=>"-", "\uFF0E"=>".", "\uFF0F"=>"/", "\uFF10"=>"0", "\uFF11"=>"1", "\uFF12"=>"2", "\uFF13"=>"3",
- "\uFF14"=>"4", "\uFF15"=>"5", "\uFF16"=>"6", "\uFF17"=>"7", "\uFF18"=>"8", "\uFF19"=>"9", "\uFF1A"=>":", "\uFF1B"=>";",
- "\uFF1C"=>"<", "\uFF1D"=>"=", "\uFF1E"=>">", "\uFF1F"=>"?", "\uFF20"=>"@", "\uFF21"=>"A", "\uFF22"=>"B", "\uFF23"=>"C",
- "\uFF24"=>"D", "\uFF25"=>"E", "\uFF26"=>"F", "\uFF27"=>"G", "\uFF28"=>"H", "\uFF29"=>"I", "\uFF2A"=>"J", "\uFF2B"=>"K",
- "\uFF2C"=>"L", "\uFF2D"=>"M", "\uFF2E"=>"N", "\uFF2F"=>"O", "\uFF30"=>"P", "\uFF31"=>"Q", "\uFF32"=>"R", "\uFF33"=>"S",
- "\uFF34"=>"T", "\uFF35"=>"U", "\uFF36"=>"V", "\uFF37"=>"W", "\uFF38"=>"X", "\uFF39"=>"Y", "\uFF3A"=>"Z", "\uFF3B"=>"[",
- "\uFF3C"=>"\\", "\uFF3D"=>"]", "\uFF3E"=>"^", "\uFF3F"=>"_", "\uFF40"=>"`", "\uFF41"=>"a", "\uFF42"=>"b", "\uFF43"=>"c",
- "\uFF44"=>"d", "\uFF45"=>"e", "\uFF46"=>"f", "\uFF47"=>"g", "\uFF48"=>"h", "\uFF49"=>"i", "\uFF4A"=>"j", "\uFF4B"=>"k",
- "\uFF4C"=>"l", "\uFF4D"=>"m", "\uFF4E"=>"n", "\uFF4F"=>"o", "\uFF50"=>"p", "\uFF51"=>"q", "\uFF52"=>"r", "\uFF53"=>"s",
- "\uFF54"=>"t", "\uFF55"=>"u", "\uFF56"=>"v", "\uFF57"=>"w", "\uFF58"=>"x", "\uFF59"=>"y", "\uFF5A"=>"z", "\uFF5B"=>"{",
- "\uFF5C"=>"|", "\uFF5D"=>"}", "\uFF5E"=>"~", "\uFF5F"=>"\u2985", "\uFF60"=>"\u2986", "\uFF61"=>"\u3002", "\uFF62"=>"\u300C", "\uFF63"=>"\u300D",
- "\uFF64"=>"\u3001", "\uFF65"=>"\u30FB", "\uFF66"=>"\u30F2", "\uFF67"=>"\u30A1", "\uFF68"=>"\u30A3", "\uFF69"=>"\u30A5", "\uFF6A"=>"\u30A7", "\uFF6B"=>"\u30A9",
- "\uFF6C"=>"\u30E3", "\uFF6D"=>"\u30E5", "\uFF6E"=>"\u30E7", "\uFF6F"=>"\u30C3", "\uFF70"=>"\u30FC", "\uFF71"=>"\u30A2", "\uFF72"=>"\u30A4", "\uFF73"=>"\u30A6",
- "\uFF74"=>"\u30A8", "\uFF75"=>"\u30AA", "\uFF76"=>"\u30AB", "\uFF77"=>"\u30AD", "\uFF78"=>"\u30AF", "\uFF79"=>"\u30B1", "\uFF7A"=>"\u30B3", "\uFF7B"=>"\u30B5",
- "\uFF7C"=>"\u30B7", "\uFF7D"=>"\u30B9", "\uFF7E"=>"\u30BB", "\uFF7F"=>"\u30BD", "\uFF80"=>"\u30BF", "\uFF81"=>"\u30C1", "\uFF82"=>"\u30C4", "\uFF83"=>"\u30C6",
- "\uFF84"=>"\u30C8", "\uFF85"=>"\u30CA", "\uFF86"=>"\u30CB", "\uFF87"=>"\u30CC", "\uFF88"=>"\u30CD", "\uFF89"=>"\u30CE", "\uFF8A"=>"\u30CF", "\uFF8B"=>"\u30D2",
- "\uFF8C"=>"\u30D5", "\uFF8D"=>"\u30D8", "\uFF8E"=>"\u30DB", "\uFF8F"=>"\u30DE", "\uFF90"=>"\u30DF", "\uFF91"=>"\u30E0", "\uFF92"=>"\u30E1", "\uFF93"=>"\u30E2",
- "\uFF94"=>"\u30E4", "\uFF95"=>"\u30E6", "\uFF96"=>"\u30E8", "\uFF97"=>"\u30E9", "\uFF98"=>"\u30EA", "\uFF99"=>"\u30EB", "\uFF9A"=>"\u30EC", "\uFF9B"=>"\u30ED",
- "\uFF9C"=>"\u30EF", "\uFF9D"=>"\u30F3", "\uFF9E"=>"\u3099", "\uFF9F"=>"\u309A", "\uFFA0"=>"\u1160", "\uFFA1"=>"\u1100", "\uFFA2"=>"\u1101", "\uFFA3"=>"\u11AA",
- "\uFFA4"=>"\u1102", "\uFFA5"=>"\u11AC", "\uFFA6"=>"\u11AD", "\uFFA7"=>"\u1103", "\uFFA8"=>"\u1104", "\uFFA9"=>"\u1105", "\uFFAA"=>"\u11B0", "\uFFAB"=>"\u11B1",
- "\uFFAC"=>"\u11B2", "\uFFAD"=>"\u11B3", "\uFFAE"=>"\u11B4", "\uFFAF"=>"\u11B5", "\uFFB0"=>"\u111A", "\uFFB1"=>"\u1106", "\uFFB2"=>"\u1107", "\uFFB3"=>"\u1108",
- "\uFFB4"=>"\u1121", "\uFFB5"=>"\u1109", "\uFFB6"=>"\u110A", "\uFFB7"=>"\u110B", "\uFFB8"=>"\u110C", "\uFFB9"=>"\u110D", "\uFFBA"=>"\u110E", "\uFFBB"=>"\u110F",
- "\uFFBC"=>"\u1110", "\uFFBD"=>"\u1111", "\uFFBE"=>"\u1112", "\uFFC2"=>"\u1161", "\uFFC3"=>"\u1162", "\uFFC4"=>"\u1163", "\uFFC5"=>"\u1164", "\uFFC6"=>"\u1165",
- "\uFFC7"=>"\u1166", "\uFFCA"=>"\u1167", "\uFFCB"=>"\u1168", "\uFFCC"=>"\u1169", "\uFFCD"=>"\u116A", "\uFFCE"=>"\u116B", "\uFFCF"=>"\u116C", "\uFFD2"=>"\u116D",
- "\uFFD3"=>"\u116E", "\uFFD4"=>"\u116F", "\uFFD5"=>"\u1170", "\uFFD6"=>"\u1171", "\uFFD7"=>"\u1172", "\uFFDA"=>"\u1173", "\uFFDB"=>"\u1174", "\uFFDC"=>"\u1175",
- "\uFFE0"=>"\u00A2", "\uFFE1"=>"\u00A3", "\uFFE2"=>"\u00AC", "\uFFE3"=>" \u0304", "\uFFE4"=>"\u00A6", "\uFFE5"=>"\u00A5", "\uFFE6"=>"\u20A9", "\uFFE8"=>"\u2502",
- "\uFFE9"=>"\u2190", "\uFFEA"=>"\u2191", "\uFFEB"=>"\u2192", "\uFFEC"=>"\u2193", "\uFFED"=>"\u25A0", "\uFFEE"=>"\u25CB", "\u{1D400}"=>"A", "\u{1D401}"=>"B",
- "\u{1D402}"=>"C", "\u{1D403}"=>"D", "\u{1D404}"=>"E", "\u{1D405}"=>"F", "\u{1D406}"=>"G", "\u{1D407}"=>"H", "\u{1D408}"=>"I", "\u{1D409}"=>"J",
- "\u{1D40A}"=>"K", "\u{1D40B}"=>"L", "\u{1D40C}"=>"M", "\u{1D40D}"=>"N", "\u{1D40E}"=>"O", "\u{1D40F}"=>"P", "\u{1D410}"=>"Q", "\u{1D411}"=>"R",
- "\u{1D412}"=>"S", "\u{1D413}"=>"T", "\u{1D414}"=>"U", "\u{1D415}"=>"V", "\u{1D416}"=>"W", "\u{1D417}"=>"X", "\u{1D418}"=>"Y", "\u{1D419}"=>"Z",
- "\u{1D41A}"=>"a", "\u{1D41B}"=>"b", "\u{1D41C}"=>"c", "\u{1D41D}"=>"d", "\u{1D41E}"=>"e", "\u{1D41F}"=>"f", "\u{1D420}"=>"g", "\u{1D421}"=>"h",
- "\u{1D422}"=>"i", "\u{1D423}"=>"j", "\u{1D424}"=>"k", "\u{1D425}"=>"l", "\u{1D426}"=>"m", "\u{1D427}"=>"n", "\u{1D428}"=>"o", "\u{1D429}"=>"p",
- "\u{1D42A}"=>"q", "\u{1D42B}"=>"r", "\u{1D42C}"=>"s", "\u{1D42D}"=>"t", "\u{1D42E}"=>"u", "\u{1D42F}"=>"v", "\u{1D430}"=>"w", "\u{1D431}"=>"x",
- "\u{1D432}"=>"y", "\u{1D433}"=>"z", "\u{1D434}"=>"A", "\u{1D435}"=>"B", "\u{1D436}"=>"C", "\u{1D437}"=>"D", "\u{1D438}"=>"E", "\u{1D439}"=>"F",
- "\u{1D43A}"=>"G", "\u{1D43B}"=>"H", "\u{1D43C}"=>"I", "\u{1D43D}"=>"J", "\u{1D43E}"=>"K", "\u{1D43F}"=>"L", "\u{1D440}"=>"M", "\u{1D441}"=>"N",
- "\u{1D442}"=>"O", "\u{1D443}"=>"P", "\u{1D444}"=>"Q", "\u{1D445}"=>"R", "\u{1D446}"=>"S", "\u{1D447}"=>"T", "\u{1D448}"=>"U", "\u{1D449}"=>"V",
- "\u{1D44A}"=>"W", "\u{1D44B}"=>"X", "\u{1D44C}"=>"Y", "\u{1D44D}"=>"Z", "\u{1D44E}"=>"a", "\u{1D44F}"=>"b", "\u{1D450}"=>"c", "\u{1D451}"=>"d",
- "\u{1D452}"=>"e", "\u{1D453}"=>"f", "\u{1D454}"=>"g", "\u{1D456}"=>"i", "\u{1D457}"=>"j", "\u{1D458}"=>"k", "\u{1D459}"=>"l", "\u{1D45A}"=>"m",
- "\u{1D45B}"=>"n", "\u{1D45C}"=>"o", "\u{1D45D}"=>"p", "\u{1D45E}"=>"q", "\u{1D45F}"=>"r", "\u{1D460}"=>"s", "\u{1D461}"=>"t", "\u{1D462}"=>"u",
- "\u{1D463}"=>"v", "\u{1D464}"=>"w", "\u{1D465}"=>"x", "\u{1D466}"=>"y", "\u{1D467}"=>"z", "\u{1D468}"=>"A", "\u{1D469}"=>"B", "\u{1D46A}"=>"C",
- "\u{1D46B}"=>"D", "\u{1D46C}"=>"E", "\u{1D46D}"=>"F", "\u{1D46E}"=>"G", "\u{1D46F}"=>"H", "\u{1D470}"=>"I", "\u{1D471}"=>"J", "\u{1D472}"=>"K",
- "\u{1D473}"=>"L", "\u{1D474}"=>"M", "\u{1D475}"=>"N", "\u{1D476}"=>"O", "\u{1D477}"=>"P", "\u{1D478}"=>"Q", "\u{1D479}"=>"R", "\u{1D47A}"=>"S",
- "\u{1D47B}"=>"T", "\u{1D47C}"=>"U", "\u{1D47D}"=>"V", "\u{1D47E}"=>"W", "\u{1D47F}"=>"X", "\u{1D480}"=>"Y", "\u{1D481}"=>"Z", "\u{1D482}"=>"a",
- "\u{1D483}"=>"b", "\u{1D484}"=>"c", "\u{1D485}"=>"d", "\u{1D486}"=>"e", "\u{1D487}"=>"f", "\u{1D488}"=>"g", "\u{1D489}"=>"h", "\u{1D48A}"=>"i",
- "\u{1D48B}"=>"j", "\u{1D48C}"=>"k", "\u{1D48D}"=>"l", "\u{1D48E}"=>"m", "\u{1D48F}"=>"n", "\u{1D490}"=>"o", "\u{1D491}"=>"p", "\u{1D492}"=>"q",
- "\u{1D493}"=>"r", "\u{1D494}"=>"s", "\u{1D495}"=>"t", "\u{1D496}"=>"u", "\u{1D497}"=>"v", "\u{1D498}"=>"w", "\u{1D499}"=>"x", "\u{1D49A}"=>"y",
- "\u{1D49B}"=>"z", "\u{1D49C}"=>"A", "\u{1D49E}"=>"C", "\u{1D49F}"=>"D", "\u{1D4A2}"=>"G", "\u{1D4A5}"=>"J", "\u{1D4A6}"=>"K", "\u{1D4A9}"=>"N",
- "\u{1D4AA}"=>"O", "\u{1D4AB}"=>"P", "\u{1D4AC}"=>"Q", "\u{1D4AE}"=>"S", "\u{1D4AF}"=>"T", "\u{1D4B0}"=>"U", "\u{1D4B1}"=>"V", "\u{1D4B2}"=>"W",
- "\u{1D4B3}"=>"X", "\u{1D4B4}"=>"Y", "\u{1D4B5}"=>"Z", "\u{1D4B6}"=>"a", "\u{1D4B7}"=>"b", "\u{1D4B8}"=>"c", "\u{1D4B9}"=>"d", "\u{1D4BB}"=>"f",
- "\u{1D4BD}"=>"h", "\u{1D4BE}"=>"i", "\u{1D4BF}"=>"j", "\u{1D4C0}"=>"k", "\u{1D4C1}"=>"l", "\u{1D4C2}"=>"m", "\u{1D4C3}"=>"n", "\u{1D4C5}"=>"p",
- "\u{1D4C6}"=>"q", "\u{1D4C7}"=>"r", "\u{1D4C8}"=>"s", "\u{1D4C9}"=>"t", "\u{1D4CA}"=>"u", "\u{1D4CB}"=>"v", "\u{1D4CC}"=>"w", "\u{1D4CD}"=>"x",
- "\u{1D4CE}"=>"y", "\u{1D4CF}"=>"z", "\u{1D4D0}"=>"A", "\u{1D4D1}"=>"B", "\u{1D4D2}"=>"C", "\u{1D4D3}"=>"D", "\u{1D4D4}"=>"E", "\u{1D4D5}"=>"F",
- "\u{1D4D6}"=>"G", "\u{1D4D7}"=>"H", "\u{1D4D8}"=>"I", "\u{1D4D9}"=>"J", "\u{1D4DA}"=>"K", "\u{1D4DB}"=>"L", "\u{1D4DC}"=>"M", "\u{1D4DD}"=>"N",
- "\u{1D4DE}"=>"O", "\u{1D4DF}"=>"P", "\u{1D4E0}"=>"Q", "\u{1D4E1}"=>"R", "\u{1D4E2}"=>"S", "\u{1D4E3}"=>"T", "\u{1D4E4}"=>"U", "\u{1D4E5}"=>"V",
- "\u{1D4E6}"=>"W", "\u{1D4E7}"=>"X", "\u{1D4E8}"=>"Y", "\u{1D4E9}"=>"Z", "\u{1D4EA}"=>"a", "\u{1D4EB}"=>"b", "\u{1D4EC}"=>"c", "\u{1D4ED}"=>"d",
- "\u{1D4EE}"=>"e", "\u{1D4EF}"=>"f", "\u{1D4F0}"=>"g", "\u{1D4F1}"=>"h", "\u{1D4F2}"=>"i", "\u{1D4F3}"=>"j", "\u{1D4F4}"=>"k", "\u{1D4F5}"=>"l",
- "\u{1D4F6}"=>"m", "\u{1D4F7}"=>"n", "\u{1D4F8}"=>"o", "\u{1D4F9}"=>"p", "\u{1D4FA}"=>"q", "\u{1D4FB}"=>"r", "\u{1D4FC}"=>"s", "\u{1D4FD}"=>"t",
- "\u{1D4FE}"=>"u", "\u{1D4FF}"=>"v", "\u{1D500}"=>"w", "\u{1D501}"=>"x", "\u{1D502}"=>"y", "\u{1D503}"=>"z", "\u{1D504}"=>"A", "\u{1D505}"=>"B",
- "\u{1D507}"=>"D", "\u{1D508}"=>"E", "\u{1D509}"=>"F", "\u{1D50A}"=>"G", "\u{1D50D}"=>"J", "\u{1D50E}"=>"K", "\u{1D50F}"=>"L", "\u{1D510}"=>"M",
- "\u{1D511}"=>"N", "\u{1D512}"=>"O", "\u{1D513}"=>"P", "\u{1D514}"=>"Q", "\u{1D516}"=>"S", "\u{1D517}"=>"T", "\u{1D518}"=>"U", "\u{1D519}"=>"V",
- "\u{1D51A}"=>"W", "\u{1D51B}"=>"X", "\u{1D51C}"=>"Y", "\u{1D51E}"=>"a", "\u{1D51F}"=>"b", "\u{1D520}"=>"c", "\u{1D521}"=>"d", "\u{1D522}"=>"e",
- "\u{1D523}"=>"f", "\u{1D524}"=>"g", "\u{1D525}"=>"h", "\u{1D526}"=>"i", "\u{1D527}"=>"j", "\u{1D528}"=>"k", "\u{1D529}"=>"l", "\u{1D52A}"=>"m",
- "\u{1D52B}"=>"n", "\u{1D52C}"=>"o", "\u{1D52D}"=>"p", "\u{1D52E}"=>"q", "\u{1D52F}"=>"r", "\u{1D530}"=>"s", "\u{1D531}"=>"t", "\u{1D532}"=>"u",
- "\u{1D533}"=>"v", "\u{1D534}"=>"w", "\u{1D535}"=>"x", "\u{1D536}"=>"y", "\u{1D537}"=>"z", "\u{1D538}"=>"A", "\u{1D539}"=>"B", "\u{1D53B}"=>"D",
- "\u{1D53C}"=>"E", "\u{1D53D}"=>"F", "\u{1D53E}"=>"G", "\u{1D540}"=>"I", "\u{1D541}"=>"J", "\u{1D542}"=>"K", "\u{1D543}"=>"L", "\u{1D544}"=>"M",
- "\u{1D546}"=>"O", "\u{1D54A}"=>"S", "\u{1D54B}"=>"T", "\u{1D54C}"=>"U", "\u{1D54D}"=>"V", "\u{1D54E}"=>"W", "\u{1D54F}"=>"X", "\u{1D550}"=>"Y",
- "\u{1D552}"=>"a", "\u{1D553}"=>"b", "\u{1D554}"=>"c", "\u{1D555}"=>"d", "\u{1D556}"=>"e", "\u{1D557}"=>"f", "\u{1D558}"=>"g", "\u{1D559}"=>"h",
- "\u{1D55A}"=>"i", "\u{1D55B}"=>"j", "\u{1D55C}"=>"k", "\u{1D55D}"=>"l", "\u{1D55E}"=>"m", "\u{1D55F}"=>"n", "\u{1D560}"=>"o", "\u{1D561}"=>"p",
- "\u{1D562}"=>"q", "\u{1D563}"=>"r", "\u{1D564}"=>"s", "\u{1D565}"=>"t", "\u{1D566}"=>"u", "\u{1D567}"=>"v", "\u{1D568}"=>"w", "\u{1D569}"=>"x",
- "\u{1D56A}"=>"y", "\u{1D56B}"=>"z", "\u{1D56C}"=>"A", "\u{1D56D}"=>"B", "\u{1D56E}"=>"C", "\u{1D56F}"=>"D", "\u{1D570}"=>"E", "\u{1D571}"=>"F",
- "\u{1D572}"=>"G", "\u{1D573}"=>"H", "\u{1D574}"=>"I", "\u{1D575}"=>"J", "\u{1D576}"=>"K", "\u{1D577}"=>"L", "\u{1D578}"=>"M", "\u{1D579}"=>"N",
- "\u{1D57A}"=>"O", "\u{1D57B}"=>"P", "\u{1D57C}"=>"Q", "\u{1D57D}"=>"R", "\u{1D57E}"=>"S", "\u{1D57F}"=>"T", "\u{1D580}"=>"U", "\u{1D581}"=>"V",
- "\u{1D582}"=>"W", "\u{1D583}"=>"X", "\u{1D584}"=>"Y", "\u{1D585}"=>"Z", "\u{1D586}"=>"a", "\u{1D587}"=>"b", "\u{1D588}"=>"c", "\u{1D589}"=>"d",
- "\u{1D58A}"=>"e", "\u{1D58B}"=>"f", "\u{1D58C}"=>"g", "\u{1D58D}"=>"h", "\u{1D58E}"=>"i", "\u{1D58F}"=>"j", "\u{1D590}"=>"k", "\u{1D591}"=>"l",
- "\u{1D592}"=>"m", "\u{1D593}"=>"n", "\u{1D594}"=>"o", "\u{1D595}"=>"p", "\u{1D596}"=>"q", "\u{1D597}"=>"r", "\u{1D598}"=>"s", "\u{1D599}"=>"t",
- "\u{1D59A}"=>"u", "\u{1D59B}"=>"v", "\u{1D59C}"=>"w", "\u{1D59D}"=>"x", "\u{1D59E}"=>"y", "\u{1D59F}"=>"z", "\u{1D5A0}"=>"A", "\u{1D5A1}"=>"B",
- "\u{1D5A2}"=>"C", "\u{1D5A3}"=>"D", "\u{1D5A4}"=>"E", "\u{1D5A5}"=>"F", "\u{1D5A6}"=>"G", "\u{1D5A7}"=>"H", "\u{1D5A8}"=>"I", "\u{1D5A9}"=>"J",
- "\u{1D5AA}"=>"K", "\u{1D5AB}"=>"L", "\u{1D5AC}"=>"M", "\u{1D5AD}"=>"N", "\u{1D5AE}"=>"O", "\u{1D5AF}"=>"P", "\u{1D5B0}"=>"Q", "\u{1D5B1}"=>"R",
- "\u{1D5B2}"=>"S", "\u{1D5B3}"=>"T", "\u{1D5B4}"=>"U", "\u{1D5B5}"=>"V", "\u{1D5B6}"=>"W", "\u{1D5B7}"=>"X", "\u{1D5B8}"=>"Y", "\u{1D5B9}"=>"Z",
- "\u{1D5BA}"=>"a", "\u{1D5BB}"=>"b", "\u{1D5BC}"=>"c", "\u{1D5BD}"=>"d", "\u{1D5BE}"=>"e", "\u{1D5BF}"=>"f", "\u{1D5C0}"=>"g", "\u{1D5C1}"=>"h",
- "\u{1D5C2}"=>"i", "\u{1D5C3}"=>"j", "\u{1D5C4}"=>"k", "\u{1D5C5}"=>"l", "\u{1D5C6}"=>"m", "\u{1D5C7}"=>"n", "\u{1D5C8}"=>"o", "\u{1D5C9}"=>"p",
- "\u{1D5CA}"=>"q", "\u{1D5CB}"=>"r", "\u{1D5CC}"=>"s", "\u{1D5CD}"=>"t", "\u{1D5CE}"=>"u", "\u{1D5CF}"=>"v", "\u{1D5D0}"=>"w", "\u{1D5D1}"=>"x",
- "\u{1D5D2}"=>"y", "\u{1D5D3}"=>"z", "\u{1D5D4}"=>"A", "\u{1D5D5}"=>"B", "\u{1D5D6}"=>"C", "\u{1D5D7}"=>"D", "\u{1D5D8}"=>"E", "\u{1D5D9}"=>"F",
- "\u{1D5DA}"=>"G", "\u{1D5DB}"=>"H", "\u{1D5DC}"=>"I", "\u{1D5DD}"=>"J", "\u{1D5DE}"=>"K", "\u{1D5DF}"=>"L", "\u{1D5E0}"=>"M", "\u{1D5E1}"=>"N",
- "\u{1D5E2}"=>"O", "\u{1D5E3}"=>"P", "\u{1D5E4}"=>"Q", "\u{1D5E5}"=>"R", "\u{1D5E6}"=>"S", "\u{1D5E7}"=>"T", "\u{1D5E8}"=>"U", "\u{1D5E9}"=>"V",
- "\u{1D5EA}"=>"W", "\u{1D5EB}"=>"X", "\u{1D5EC}"=>"Y", "\u{1D5ED}"=>"Z", "\u{1D5EE}"=>"a", "\u{1D5EF}"=>"b", "\u{1D5F0}"=>"c", "\u{1D5F1}"=>"d",
- "\u{1D5F2}"=>"e", "\u{1D5F3}"=>"f", "\u{1D5F4}"=>"g", "\u{1D5F5}"=>"h", "\u{1D5F6}"=>"i", "\u{1D5F7}"=>"j", "\u{1D5F8}"=>"k", "\u{1D5F9}"=>"l",
- "\u{1D5FA}"=>"m", "\u{1D5FB}"=>"n", "\u{1D5FC}"=>"o", "\u{1D5FD}"=>"p", "\u{1D5FE}"=>"q", "\u{1D5FF}"=>"r", "\u{1D600}"=>"s", "\u{1D601}"=>"t",
- "\u{1D602}"=>"u", "\u{1D603}"=>"v", "\u{1D604}"=>"w", "\u{1D605}"=>"x", "\u{1D606}"=>"y", "\u{1D607}"=>"z", "\u{1D608}"=>"A", "\u{1D609}"=>"B",
- "\u{1D60A}"=>"C", "\u{1D60B}"=>"D", "\u{1D60C}"=>"E", "\u{1D60D}"=>"F", "\u{1D60E}"=>"G", "\u{1D60F}"=>"H", "\u{1D610}"=>"I", "\u{1D611}"=>"J",
- "\u{1D612}"=>"K", "\u{1D613}"=>"L", "\u{1D614}"=>"M", "\u{1D615}"=>"N", "\u{1D616}"=>"O", "\u{1D617}"=>"P", "\u{1D618}"=>"Q", "\u{1D619}"=>"R",
- "\u{1D61A}"=>"S", "\u{1D61B}"=>"T", "\u{1D61C}"=>"U", "\u{1D61D}"=>"V", "\u{1D61E}"=>"W", "\u{1D61F}"=>"X", "\u{1D620}"=>"Y", "\u{1D621}"=>"Z",
- "\u{1D622}"=>"a", "\u{1D623}"=>"b", "\u{1D624}"=>"c", "\u{1D625}"=>"d", "\u{1D626}"=>"e", "\u{1D627}"=>"f", "\u{1D628}"=>"g", "\u{1D629}"=>"h",
- "\u{1D62A}"=>"i", "\u{1D62B}"=>"j", "\u{1D62C}"=>"k", "\u{1D62D}"=>"l", "\u{1D62E}"=>"m", "\u{1D62F}"=>"n", "\u{1D630}"=>"o", "\u{1D631}"=>"p",
- "\u{1D632}"=>"q", "\u{1D633}"=>"r", "\u{1D634}"=>"s", "\u{1D635}"=>"t", "\u{1D636}"=>"u", "\u{1D637}"=>"v", "\u{1D638}"=>"w", "\u{1D639}"=>"x",
- "\u{1D63A}"=>"y", "\u{1D63B}"=>"z", "\u{1D63C}"=>"A", "\u{1D63D}"=>"B", "\u{1D63E}"=>"C", "\u{1D63F}"=>"D", "\u{1D640}"=>"E", "\u{1D641}"=>"F",
- "\u{1D642}"=>"G", "\u{1D643}"=>"H", "\u{1D644}"=>"I", "\u{1D645}"=>"J", "\u{1D646}"=>"K", "\u{1D647}"=>"L", "\u{1D648}"=>"M", "\u{1D649}"=>"N",
- "\u{1D64A}"=>"O", "\u{1D64B}"=>"P", "\u{1D64C}"=>"Q", "\u{1D64D}"=>"R", "\u{1D64E}"=>"S", "\u{1D64F}"=>"T", "\u{1D650}"=>"U", "\u{1D651}"=>"V",
- "\u{1D652}"=>"W", "\u{1D653}"=>"X", "\u{1D654}"=>"Y", "\u{1D655}"=>"Z", "\u{1D656}"=>"a", "\u{1D657}"=>"b", "\u{1D658}"=>"c", "\u{1D659}"=>"d",
- "\u{1D65A}"=>"e", "\u{1D65B}"=>"f", "\u{1D65C}"=>"g", "\u{1D65D}"=>"h", "\u{1D65E}"=>"i", "\u{1D65F}"=>"j", "\u{1D660}"=>"k", "\u{1D661}"=>"l",
- "\u{1D662}"=>"m", "\u{1D663}"=>"n", "\u{1D664}"=>"o", "\u{1D665}"=>"p", "\u{1D666}"=>"q", "\u{1D667}"=>"r", "\u{1D668}"=>"s", "\u{1D669}"=>"t",
- "\u{1D66A}"=>"u", "\u{1D66B}"=>"v", "\u{1D66C}"=>"w", "\u{1D66D}"=>"x", "\u{1D66E}"=>"y", "\u{1D66F}"=>"z", "\u{1D670}"=>"A", "\u{1D671}"=>"B",
- "\u{1D672}"=>"C", "\u{1D673}"=>"D", "\u{1D674}"=>"E", "\u{1D675}"=>"F", "\u{1D676}"=>"G", "\u{1D677}"=>"H", "\u{1D678}"=>"I", "\u{1D679}"=>"J",
- "\u{1D67A}"=>"K", "\u{1D67B}"=>"L", "\u{1D67C}"=>"M", "\u{1D67D}"=>"N", "\u{1D67E}"=>"O", "\u{1D67F}"=>"P", "\u{1D680}"=>"Q", "\u{1D681}"=>"R",
- "\u{1D682}"=>"S", "\u{1D683}"=>"T", "\u{1D684}"=>"U", "\u{1D685}"=>"V", "\u{1D686}"=>"W", "\u{1D687}"=>"X", "\u{1D688}"=>"Y", "\u{1D689}"=>"Z",
- "\u{1D68A}"=>"a", "\u{1D68B}"=>"b", "\u{1D68C}"=>"c", "\u{1D68D}"=>"d", "\u{1D68E}"=>"e", "\u{1D68F}"=>"f", "\u{1D690}"=>"g", "\u{1D691}"=>"h",
- "\u{1D692}"=>"i", "\u{1D693}"=>"j", "\u{1D694}"=>"k", "\u{1D695}"=>"l", "\u{1D696}"=>"m", "\u{1D697}"=>"n", "\u{1D698}"=>"o", "\u{1D699}"=>"p",
- "\u{1D69A}"=>"q", "\u{1D69B}"=>"r", "\u{1D69C}"=>"s", "\u{1D69D}"=>"t", "\u{1D69E}"=>"u", "\u{1D69F}"=>"v", "\u{1D6A0}"=>"w", "\u{1D6A1}"=>"x",
- "\u{1D6A2}"=>"y", "\u{1D6A3}"=>"z", "\u{1D6A4}"=>"\u0131", "\u{1D6A5}"=>"\u0237", "\u{1D6A8}"=>"\u0391", "\u{1D6A9}"=>"\u0392", "\u{1D6AA}"=>"\u0393", "\u{1D6AB}"=>"\u0394",
- "\u{1D6AC}"=>"\u0395", "\u{1D6AD}"=>"\u0396", "\u{1D6AE}"=>"\u0397", "\u{1D6AF}"=>"\u0398", "\u{1D6B0}"=>"\u0399", "\u{1D6B1}"=>"\u039A", "\u{1D6B2}"=>"\u039B", "\u{1D6B3}"=>"\u039C",
- "\u{1D6B4}"=>"\u039D", "\u{1D6B5}"=>"\u039E", "\u{1D6B6}"=>"\u039F", "\u{1D6B7}"=>"\u03A0", "\u{1D6B8}"=>"\u03A1", "\u{1D6B9}"=>"\u0398", "\u{1D6BA}"=>"\u03A3", "\u{1D6BB}"=>"\u03A4",
- "\u{1D6BC}"=>"\u03A5", "\u{1D6BD}"=>"\u03A6", "\u{1D6BE}"=>"\u03A7", "\u{1D6BF}"=>"\u03A8", "\u{1D6C0}"=>"\u03A9", "\u{1D6C1}"=>"\u2207", "\u{1D6C2}"=>"\u03B1", "\u{1D6C3}"=>"\u03B2",
- "\u{1D6C4}"=>"\u03B3", "\u{1D6C5}"=>"\u03B4", "\u{1D6C6}"=>"\u03B5", "\u{1D6C7}"=>"\u03B6", "\u{1D6C8}"=>"\u03B7", "\u{1D6C9}"=>"\u03B8", "\u{1D6CA}"=>"\u03B9", "\u{1D6CB}"=>"\u03BA",
- "\u{1D6CC}"=>"\u03BB", "\u{1D6CD}"=>"\u03BC", "\u{1D6CE}"=>"\u03BD", "\u{1D6CF}"=>"\u03BE", "\u{1D6D0}"=>"\u03BF", "\u{1D6D1}"=>"\u03C0", "\u{1D6D2}"=>"\u03C1", "\u{1D6D3}"=>"\u03C2",
- "\u{1D6D4}"=>"\u03C3", "\u{1D6D5}"=>"\u03C4", "\u{1D6D6}"=>"\u03C5", "\u{1D6D7}"=>"\u03C6", "\u{1D6D8}"=>"\u03C7", "\u{1D6D9}"=>"\u03C8", "\u{1D6DA}"=>"\u03C9", "\u{1D6DB}"=>"\u2202",
- "\u{1D6DC}"=>"\u03B5", "\u{1D6DD}"=>"\u03B8", "\u{1D6DE}"=>"\u03BA", "\u{1D6DF}"=>"\u03C6", "\u{1D6E0}"=>"\u03C1", "\u{1D6E1}"=>"\u03C0", "\u{1D6E2}"=>"\u0391", "\u{1D6E3}"=>"\u0392",
- "\u{1D6E4}"=>"\u0393", "\u{1D6E5}"=>"\u0394", "\u{1D6E6}"=>"\u0395", "\u{1D6E7}"=>"\u0396", "\u{1D6E8}"=>"\u0397", "\u{1D6E9}"=>"\u0398", "\u{1D6EA}"=>"\u0399", "\u{1D6EB}"=>"\u039A",
- "\u{1D6EC}"=>"\u039B", "\u{1D6ED}"=>"\u039C", "\u{1D6EE}"=>"\u039D", "\u{1D6EF}"=>"\u039E", "\u{1D6F0}"=>"\u039F", "\u{1D6F1}"=>"\u03A0", "\u{1D6F2}"=>"\u03A1", "\u{1D6F3}"=>"\u0398",
- "\u{1D6F4}"=>"\u03A3", "\u{1D6F5}"=>"\u03A4", "\u{1D6F6}"=>"\u03A5", "\u{1D6F7}"=>"\u03A6", "\u{1D6F8}"=>"\u03A7", "\u{1D6F9}"=>"\u03A8", "\u{1D6FA}"=>"\u03A9", "\u{1D6FB}"=>"\u2207",
- "\u{1D6FC}"=>"\u03B1", "\u{1D6FD}"=>"\u03B2", "\u{1D6FE}"=>"\u03B3", "\u{1D6FF}"=>"\u03B4", "\u{1D700}"=>"\u03B5", "\u{1D701}"=>"\u03B6", "\u{1D702}"=>"\u03B7", "\u{1D703}"=>"\u03B8",
- "\u{1D704}"=>"\u03B9", "\u{1D705}"=>"\u03BA", "\u{1D706}"=>"\u03BB", "\u{1D707}"=>"\u03BC", "\u{1D708}"=>"\u03BD", "\u{1D709}"=>"\u03BE", "\u{1D70A}"=>"\u03BF", "\u{1D70B}"=>"\u03C0",
- "\u{1D70C}"=>"\u03C1", "\u{1D70D}"=>"\u03C2", "\u{1D70E}"=>"\u03C3", "\u{1D70F}"=>"\u03C4", "\u{1D710}"=>"\u03C5", "\u{1D711}"=>"\u03C6", "\u{1D712}"=>"\u03C7", "\u{1D713}"=>"\u03C8",
- "\u{1D714}"=>"\u03C9", "\u{1D715}"=>"\u2202", "\u{1D716}"=>"\u03B5", "\u{1D717}"=>"\u03B8", "\u{1D718}"=>"\u03BA", "\u{1D719}"=>"\u03C6", "\u{1D71A}"=>"\u03C1", "\u{1D71B}"=>"\u03C0",
- "\u{1D71C}"=>"\u0391", "\u{1D71D}"=>"\u0392", "\u{1D71E}"=>"\u0393", "\u{1D71F}"=>"\u0394", "\u{1D720}"=>"\u0395", "\u{1D721}"=>"\u0396", "\u{1D722}"=>"\u0397", "\u{1D723}"=>"\u0398",
- "\u{1D724}"=>"\u0399", "\u{1D725}"=>"\u039A", "\u{1D726}"=>"\u039B", "\u{1D727}"=>"\u039C", "\u{1D728}"=>"\u039D", "\u{1D729}"=>"\u039E", "\u{1D72A}"=>"\u039F", "\u{1D72B}"=>"\u03A0",
- "\u{1D72C}"=>"\u03A1", "\u{1D72D}"=>"\u0398", "\u{1D72E}"=>"\u03A3", "\u{1D72F}"=>"\u03A4", "\u{1D730}"=>"\u03A5", "\u{1D731}"=>"\u03A6", "\u{1D732}"=>"\u03A7", "\u{1D733}"=>"\u03A8",
- "\u{1D734}"=>"\u03A9", "\u{1D735}"=>"\u2207", "\u{1D736}"=>"\u03B1", "\u{1D737}"=>"\u03B2", "\u{1D738}"=>"\u03B3", "\u{1D739}"=>"\u03B4", "\u{1D73A}"=>"\u03B5", "\u{1D73B}"=>"\u03B6",
- "\u{1D73C}"=>"\u03B7", "\u{1D73D}"=>"\u03B8", "\u{1D73E}"=>"\u03B9", "\u{1D73F}"=>"\u03BA", "\u{1D740}"=>"\u03BB", "\u{1D741}"=>"\u03BC", "\u{1D742}"=>"\u03BD", "\u{1D743}"=>"\u03BE",
- "\u{1D744}"=>"\u03BF", "\u{1D745}"=>"\u03C0", "\u{1D746}"=>"\u03C1", "\u{1D747}"=>"\u03C2", "\u{1D748}"=>"\u03C3", "\u{1D749}"=>"\u03C4", "\u{1D74A}"=>"\u03C5", "\u{1D74B}"=>"\u03C6",
- "\u{1D74C}"=>"\u03C7", "\u{1D74D}"=>"\u03C8", "\u{1D74E}"=>"\u03C9", "\u{1D74F}"=>"\u2202", "\u{1D750}"=>"\u03B5", "\u{1D751}"=>"\u03B8", "\u{1D752}"=>"\u03BA", "\u{1D753}"=>"\u03C6",
- "\u{1D754}"=>"\u03C1", "\u{1D755}"=>"\u03C0", "\u{1D756}"=>"\u0391", "\u{1D757}"=>"\u0392", "\u{1D758}"=>"\u0393", "\u{1D759}"=>"\u0394", "\u{1D75A}"=>"\u0395", "\u{1D75B}"=>"\u0396",
- "\u{1D75C}"=>"\u0397", "\u{1D75D}"=>"\u0398", "\u{1D75E}"=>"\u0399", "\u{1D75F}"=>"\u039A", "\u{1D760}"=>"\u039B", "\u{1D761}"=>"\u039C", "\u{1D762}"=>"\u039D", "\u{1D763}"=>"\u039E",
- "\u{1D764}"=>"\u039F", "\u{1D765}"=>"\u03A0", "\u{1D766}"=>"\u03A1", "\u{1D767}"=>"\u0398", "\u{1D768}"=>"\u03A3", "\u{1D769}"=>"\u03A4", "\u{1D76A}"=>"\u03A5", "\u{1D76B}"=>"\u03A6",
- "\u{1D76C}"=>"\u03A7", "\u{1D76D}"=>"\u03A8", "\u{1D76E}"=>"\u03A9", "\u{1D76F}"=>"\u2207", "\u{1D770}"=>"\u03B1", "\u{1D771}"=>"\u03B2", "\u{1D772}"=>"\u03B3", "\u{1D773}"=>"\u03B4",
- "\u{1D774}"=>"\u03B5", "\u{1D775}"=>"\u03B6", "\u{1D776}"=>"\u03B7", "\u{1D777}"=>"\u03B8", "\u{1D778}"=>"\u03B9", "\u{1D779}"=>"\u03BA", "\u{1D77A}"=>"\u03BB", "\u{1D77B}"=>"\u03BC",
- "\u{1D77C}"=>"\u03BD", "\u{1D77D}"=>"\u03BE", "\u{1D77E}"=>"\u03BF", "\u{1D77F}"=>"\u03C0", "\u{1D780}"=>"\u03C1", "\u{1D781}"=>"\u03C2", "\u{1D782}"=>"\u03C3", "\u{1D783}"=>"\u03C4",
- "\u{1D784}"=>"\u03C5", "\u{1D785}"=>"\u03C6", "\u{1D786}"=>"\u03C7", "\u{1D787}"=>"\u03C8", "\u{1D788}"=>"\u03C9", "\u{1D789}"=>"\u2202", "\u{1D78A}"=>"\u03B5", "\u{1D78B}"=>"\u03B8",
- "\u{1D78C}"=>"\u03BA", "\u{1D78D}"=>"\u03C6", "\u{1D78E}"=>"\u03C1", "\u{1D78F}"=>"\u03C0", "\u{1D790}"=>"\u0391", "\u{1D791}"=>"\u0392", "\u{1D792}"=>"\u0393", "\u{1D793}"=>"\u0394",
- "\u{1D794}"=>"\u0395", "\u{1D795}"=>"\u0396", "\u{1D796}"=>"\u0397", "\u{1D797}"=>"\u0398", "\u{1D798}"=>"\u0399", "\u{1D799}"=>"\u039A", "\u{1D79A}"=>"\u039B", "\u{1D79B}"=>"\u039C",
- "\u{1D79C}"=>"\u039D", "\u{1D79D}"=>"\u039E", "\u{1D79E}"=>"\u039F", "\u{1D79F}"=>"\u03A0", "\u{1D7A0}"=>"\u03A1", "\u{1D7A1}"=>"\u0398", "\u{1D7A2}"=>"\u03A3", "\u{1D7A3}"=>"\u03A4",
- "\u{1D7A4}"=>"\u03A5", "\u{1D7A5}"=>"\u03A6", "\u{1D7A6}"=>"\u03A7", "\u{1D7A7}"=>"\u03A8", "\u{1D7A8}"=>"\u03A9", "\u{1D7A9}"=>"\u2207", "\u{1D7AA}"=>"\u03B1", "\u{1D7AB}"=>"\u03B2",
- "\u{1D7AC}"=>"\u03B3", "\u{1D7AD}"=>"\u03B4", "\u{1D7AE}"=>"\u03B5", "\u{1D7AF}"=>"\u03B6", "\u{1D7B0}"=>"\u03B7", "\u{1D7B1}"=>"\u03B8", "\u{1D7B2}"=>"\u03B9", "\u{1D7B3}"=>"\u03BA",
- "\u{1D7B4}"=>"\u03BB", "\u{1D7B5}"=>"\u03BC", "\u{1D7B6}"=>"\u03BD", "\u{1D7B7}"=>"\u03BE", "\u{1D7B8}"=>"\u03BF", "\u{1D7B9}"=>"\u03C0", "\u{1D7BA}"=>"\u03C1", "\u{1D7BB}"=>"\u03C2",
- "\u{1D7BC}"=>"\u03C3", "\u{1D7BD}"=>"\u03C4", "\u{1D7BE}"=>"\u03C5", "\u{1D7BF}"=>"\u03C6", "\u{1D7C0}"=>"\u03C7", "\u{1D7C1}"=>"\u03C8", "\u{1D7C2}"=>"\u03C9", "\u{1D7C3}"=>"\u2202",
- "\u{1D7C4}"=>"\u03B5", "\u{1D7C5}"=>"\u03B8", "\u{1D7C6}"=>"\u03BA", "\u{1D7C7}"=>"\u03C6", "\u{1D7C8}"=>"\u03C1", "\u{1D7C9}"=>"\u03C0", "\u{1D7CA}"=>"\u03DC", "\u{1D7CB}"=>"\u03DD",
- "\u{1D7CE}"=>"0", "\u{1D7CF}"=>"1", "\u{1D7D0}"=>"2", "\u{1D7D1}"=>"3", "\u{1D7D2}"=>"4", "\u{1D7D3}"=>"5", "\u{1D7D4}"=>"6", "\u{1D7D5}"=>"7",
- "\u{1D7D6}"=>"8", "\u{1D7D7}"=>"9", "\u{1D7D8}"=>"0", "\u{1D7D9}"=>"1", "\u{1D7DA}"=>"2", "\u{1D7DB}"=>"3", "\u{1D7DC}"=>"4", "\u{1D7DD}"=>"5",
- "\u{1D7DE}"=>"6", "\u{1D7DF}"=>"7", "\u{1D7E0}"=>"8", "\u{1D7E1}"=>"9", "\u{1D7E2}"=>"0", "\u{1D7E3}"=>"1", "\u{1D7E4}"=>"2", "\u{1D7E5}"=>"3",
- "\u{1D7E6}"=>"4", "\u{1D7E7}"=>"5", "\u{1D7E8}"=>"6", "\u{1D7E9}"=>"7", "\u{1D7EA}"=>"8", "\u{1D7EB}"=>"9", "\u{1D7EC}"=>"0", "\u{1D7ED}"=>"1",
- "\u{1D7EE}"=>"2", "\u{1D7EF}"=>"3", "\u{1D7F0}"=>"4", "\u{1D7F1}"=>"5", "\u{1D7F2}"=>"6", "\u{1D7F3}"=>"7", "\u{1D7F4}"=>"8", "\u{1D7F5}"=>"9",
- "\u{1D7F6}"=>"0", "\u{1D7F7}"=>"1", "\u{1D7F8}"=>"2", "\u{1D7F9}"=>"3", "\u{1D7FA}"=>"4", "\u{1D7FB}"=>"5", "\u{1D7FC}"=>"6", "\u{1D7FD}"=>"7",
- "\u{1D7FE}"=>"8", "\u{1D7FF}"=>"9", "\u{1EE00}"=>"\u0627", "\u{1EE01}"=>"\u0628", "\u{1EE02}"=>"\u062C", "\u{1EE03}"=>"\u062F", "\u{1EE05}"=>"\u0648", "\u{1EE06}"=>"\u0632",
- "\u{1EE07}"=>"\u062D", "\u{1EE08}"=>"\u0637", "\u{1EE09}"=>"\u064A", "\u{1EE0A}"=>"\u0643", "\u{1EE0B}"=>"\u0644", "\u{1EE0C}"=>"\u0645", "\u{1EE0D}"=>"\u0646", "\u{1EE0E}"=>"\u0633",
- "\u{1EE0F}"=>"\u0639", "\u{1EE10}"=>"\u0641", "\u{1EE11}"=>"\u0635", "\u{1EE12}"=>"\u0642", "\u{1EE13}"=>"\u0631", "\u{1EE14}"=>"\u0634", "\u{1EE15}"=>"\u062A", "\u{1EE16}"=>"\u062B",
- "\u{1EE17}"=>"\u062E", "\u{1EE18}"=>"\u0630", "\u{1EE19}"=>"\u0636", "\u{1EE1A}"=>"\u0638", "\u{1EE1B}"=>"\u063A", "\u{1EE1C}"=>"\u066E", "\u{1EE1D}"=>"\u06BA", "\u{1EE1E}"=>"\u06A1",
- "\u{1EE1F}"=>"\u066F", "\u{1EE21}"=>"\u0628", "\u{1EE22}"=>"\u062C", "\u{1EE24}"=>"\u0647", "\u{1EE27}"=>"\u062D", "\u{1EE29}"=>"\u064A", "\u{1EE2A}"=>"\u0643", "\u{1EE2B}"=>"\u0644",
- "\u{1EE2C}"=>"\u0645", "\u{1EE2D}"=>"\u0646", "\u{1EE2E}"=>"\u0633", "\u{1EE2F}"=>"\u0639", "\u{1EE30}"=>"\u0641", "\u{1EE31}"=>"\u0635", "\u{1EE32}"=>"\u0642", "\u{1EE34}"=>"\u0634",
- "\u{1EE35}"=>"\u062A", "\u{1EE36}"=>"\u062B", "\u{1EE37}"=>"\u062E", "\u{1EE39}"=>"\u0636", "\u{1EE3B}"=>"\u063A", "\u{1EE42}"=>"\u062C", "\u{1EE47}"=>"\u062D", "\u{1EE49}"=>"\u064A",
- "\u{1EE4B}"=>"\u0644", "\u{1EE4D}"=>"\u0646", "\u{1EE4E}"=>"\u0633", "\u{1EE4F}"=>"\u0639", "\u{1EE51}"=>"\u0635", "\u{1EE52}"=>"\u0642", "\u{1EE54}"=>"\u0634", "\u{1EE57}"=>"\u062E",
- "\u{1EE59}"=>"\u0636", "\u{1EE5B}"=>"\u063A", "\u{1EE5D}"=>"\u06BA", "\u{1EE5F}"=>"\u066F", "\u{1EE61}"=>"\u0628", "\u{1EE62}"=>"\u062C", "\u{1EE64}"=>"\u0647", "\u{1EE67}"=>"\u062D",
- "\u{1EE68}"=>"\u0637", "\u{1EE69}"=>"\u064A", "\u{1EE6A}"=>"\u0643", "\u{1EE6C}"=>"\u0645", "\u{1EE6D}"=>"\u0646", "\u{1EE6E}"=>"\u0633", "\u{1EE6F}"=>"\u0639", "\u{1EE70}"=>"\u0641",
- "\u{1EE71}"=>"\u0635", "\u{1EE72}"=>"\u0642", "\u{1EE74}"=>"\u0634", "\u{1EE75}"=>"\u062A", "\u{1EE76}"=>"\u062B", "\u{1EE77}"=>"\u062E", "\u{1EE79}"=>"\u0636", "\u{1EE7A}"=>"\u0638",
- "\u{1EE7B}"=>"\u063A", "\u{1EE7C}"=>"\u066E", "\u{1EE7E}"=>"\u06A1", "\u{1EE80}"=>"\u0627", "\u{1EE81}"=>"\u0628", "\u{1EE82}"=>"\u062C", "\u{1EE83}"=>"\u062F", "\u{1EE84}"=>"\u0647",
- "\u{1EE85}"=>"\u0648", "\u{1EE86}"=>"\u0632", "\u{1EE87}"=>"\u062D", "\u{1EE88}"=>"\u0637", "\u{1EE89}"=>"\u064A", "\u{1EE8B}"=>"\u0644", "\u{1EE8C}"=>"\u0645", "\u{1EE8D}"=>"\u0646",
- "\u{1EE8E}"=>"\u0633", "\u{1EE8F}"=>"\u0639", "\u{1EE90}"=>"\u0641", "\u{1EE91}"=>"\u0635", "\u{1EE92}"=>"\u0642", "\u{1EE93}"=>"\u0631", "\u{1EE94}"=>"\u0634", "\u{1EE95}"=>"\u062A",
- "\u{1EE96}"=>"\u062B", "\u{1EE97}"=>"\u062E", "\u{1EE98}"=>"\u0630", "\u{1EE99}"=>"\u0636", "\u{1EE9A}"=>"\u0638", "\u{1EE9B}"=>"\u063A", "\u{1EEA1}"=>"\u0628", "\u{1EEA2}"=>"\u062C",
- "\u{1EEA3}"=>"\u062F", "\u{1EEA5}"=>"\u0648", "\u{1EEA6}"=>"\u0632", "\u{1EEA7}"=>"\u062D", "\u{1EEA8}"=>"\u0637", "\u{1EEA9}"=>"\u064A", "\u{1EEAB}"=>"\u0644", "\u{1EEAC}"=>"\u0645",
- "\u{1EEAD}"=>"\u0646", "\u{1EEAE}"=>"\u0633", "\u{1EEAF}"=>"\u0639", "\u{1EEB0}"=>"\u0641", "\u{1EEB1}"=>"\u0635", "\u{1EEB2}"=>"\u0642", "\u{1EEB3}"=>"\u0631", "\u{1EEB4}"=>"\u0634",
- "\u{1EEB5}"=>"\u062A", "\u{1EEB6}"=>"\u062B", "\u{1EEB7}"=>"\u062E", "\u{1EEB8}"=>"\u0630", "\u{1EEB9}"=>"\u0636", "\u{1EEBA}"=>"\u0638", "\u{1EEBB}"=>"\u063A", "\u{1F100}"=>"0.",
- "\u{1F101}"=>"0,", "\u{1F102}"=>"1,", "\u{1F103}"=>"2,", "\u{1F104}"=>"3,", "\u{1F105}"=>"4,", "\u{1F106}"=>"5,", "\u{1F107}"=>"6,", "\u{1F108}"=>"7,",
- "\u{1F109}"=>"8,", "\u{1F10A}"=>"9,", "\u{1F110}"=>"(A)", "\u{1F111}"=>"(B)", "\u{1F112}"=>"(C)", "\u{1F113}"=>"(D)", "\u{1F114}"=>"(E)", "\u{1F115}"=>"(F)",
- "\u{1F116}"=>"(G)", "\u{1F117}"=>"(H)", "\u{1F118}"=>"(I)", "\u{1F119}"=>"(J)", "\u{1F11A}"=>"(K)", "\u{1F11B}"=>"(L)", "\u{1F11C}"=>"(M)", "\u{1F11D}"=>"(N)",
- "\u{1F11E}"=>"(O)", "\u{1F11F}"=>"(P)", "\u{1F120}"=>"(Q)", "\u{1F121}"=>"(R)", "\u{1F122}"=>"(S)", "\u{1F123}"=>"(T)", "\u{1F124}"=>"(U)", "\u{1F125}"=>"(V)",
- "\u{1F126}"=>"(W)", "\u{1F127}"=>"(X)", "\u{1F128}"=>"(Y)", "\u{1F129}"=>"(Z)", "\u{1F12A}"=>"\u3014S\u3015", "\u{1F12B}"=>"C", "\u{1F12C}"=>"R", "\u{1F12D}"=>"CD",
- "\u{1F12E}"=>"WZ", "\u{1F130}"=>"A", "\u{1F131}"=>"B", "\u{1F132}"=>"C", "\u{1F133}"=>"D", "\u{1F134}"=>"E", "\u{1F135}"=>"F", "\u{1F136}"=>"G",
- "\u{1F137}"=>"H", "\u{1F138}"=>"I", "\u{1F139}"=>"J", "\u{1F13A}"=>"K", "\u{1F13B}"=>"L", "\u{1F13C}"=>"M", "\u{1F13D}"=>"N", "\u{1F13E}"=>"O",
- "\u{1F13F}"=>"P", "\u{1F140}"=>"Q", "\u{1F141}"=>"R", "\u{1F142}"=>"S", "\u{1F143}"=>"T", "\u{1F144}"=>"U", "\u{1F145}"=>"V", "\u{1F146}"=>"W",
- "\u{1F147}"=>"X", "\u{1F148}"=>"Y", "\u{1F149}"=>"Z", "\u{1F14A}"=>"HV", "\u{1F14B}"=>"MV", "\u{1F14C}"=>"SD", "\u{1F14D}"=>"SS", "\u{1F14E}"=>"PPV",
- "\u{1F14F}"=>"WC", "\u{1F16A}"=>"MC", "\u{1F16B}"=>"MD", "\u{1F190}"=>"DJ", "\u{1F200}"=>"\u307B\u304B", "\u{1F201}"=>"\u30B3\u30B3", "\u{1F202}"=>"\u30B5", "\u{1F210}"=>"\u624B",
- "\u{1F211}"=>"\u5B57", "\u{1F212}"=>"\u53CC", "\u{1F213}"=>"\u30C7", "\u{1F214}"=>"\u4E8C", "\u{1F215}"=>"\u591A", "\u{1F216}"=>"\u89E3", "\u{1F217}"=>"\u5929", "\u{1F218}"=>"\u4EA4",
- "\u{1F219}"=>"\u6620", "\u{1F21A}"=>"\u7121", "\u{1F21B}"=>"\u6599", "\u{1F21C}"=>"\u524D", "\u{1F21D}"=>"\u5F8C", "\u{1F21E}"=>"\u518D", "\u{1F21F}"=>"\u65B0", "\u{1F220}"=>"\u521D",
- "\u{1F221}"=>"\u7D42", "\u{1F222}"=>"\u751F", "\u{1F223}"=>"\u8CA9", "\u{1F224}"=>"\u58F0", "\u{1F225}"=>"\u5439", "\u{1F226}"=>"\u6F14", "\u{1F227}"=>"\u6295", "\u{1F228}"=>"\u6355",
- "\u{1F229}"=>"\u4E00", "\u{1F22A}"=>"\u4E09", "\u{1F22B}"=>"\u904A", "\u{1F22C}"=>"\u5DE6", "\u{1F22D}"=>"\u4E2D", "\u{1F22E}"=>"\u53F3", "\u{1F22F}"=>"\u6307", "\u{1F230}"=>"\u8D70",
- "\u{1F231}"=>"\u6253", "\u{1F232}"=>"\u7981", "\u{1F233}"=>"\u7A7A", "\u{1F234}"=>"\u5408", "\u{1F235}"=>"\u6E80", "\u{1F236}"=>"\u6709", "\u{1F237}"=>"\u6708", "\u{1F238}"=>"\u7533",
- "\u{1F239}"=>"\u5272", "\u{1F23A}"=>"\u55B6", "\u{1F23B}"=>"\u914D", "\u{1F240}"=>"\u3014\u672C\u3015", "\u{1F241}"=>"\u3014\u4E09\u3015", "\u{1F242}"=>"\u3014\u4E8C\u3015", "\u{1F243}"=>"\u3014\u5B89\u3015", "\u{1F244}"=>"\u3014\u70B9\u3015",
- "\u{1F245}"=>"\u3014\u6253\u3015", "\u{1F246}"=>"\u3014\u76D7\u3015", "\u{1F247}"=>"\u3014\u52DD\u3015", "\u{1F248}"=>"\u3014\u6557\u3015", "\u{1F250}"=>"\u5F97", "\u{1F251}"=>"\u53EF", "\u0385"=>" \u0308\u0301", "\u03D3"=>"\u03A5\u0301",
- "\u03D4"=>"\u03A5\u0308", "\u1E9B"=>"s\u0307", "\u1FC1"=>" \u0308\u0342", "\u1FCD"=>" \u0313\u0300", "\u1FCE"=>" \u0313\u0301", "\u1FCF"=>" \u0313\u0342", "\u1FDD"=>" \u0314\u0300", "\u1FDE"=>" \u0314\u0301",
- "\u1FDF"=>" \u0314\u0342", "\u1FED"=>" \u0308\u0300", "\u1FEE"=>" \u0308\u0301", "\u1FFD"=>" \u0301", "\u2000"=>" ", "\u2001"=>" ",
+ "\u00A0"=>" ",
+ "\u00A8"=>" \u0308",
+ "\u00AA"=>"a",
+ "\u00AF"=>" \u0304",
+ "\u00B2"=>"2",
+ "\u00B3"=>"3",
+ "\u00B4"=>" \u0301",
+ "\u00B5"=>"\u03BC",
+ "\u00B8"=>" \u0327",
+ "\u00B9"=>"1",
+ "\u00BA"=>"o",
+ "\u00BC"=>"1\u20444",
+ "\u00BD"=>"1\u20442",
+ "\u00BE"=>"3\u20444",
+ "\u0132"=>"IJ",
+ "\u0133"=>"ij",
+ "\u013F"=>"L\u00B7",
+ "\u0140"=>"l\u00B7",
+ "\u0149"=>"\u02BCn",
+ "\u017F"=>"s",
+ "\u01C4"=>"D\u017D",
+ "\u01C5"=>"D\u017E",
+ "\u01C6"=>"d\u017E",
+ "\u01C7"=>"LJ",
+ "\u01C8"=>"Lj",
+ "\u01C9"=>"lj",
+ "\u01CA"=>"NJ",
+ "\u01CB"=>"Nj",
+ "\u01CC"=>"nj",
+ "\u01F1"=>"DZ",
+ "\u01F2"=>"Dz",
+ "\u01F3"=>"dz",
+ "\u02B0"=>"h",
+ "\u02B1"=>"\u0266",
+ "\u02B2"=>"j",
+ "\u02B3"=>"r",
+ "\u02B4"=>"\u0279",
+ "\u02B5"=>"\u027B",
+ "\u02B6"=>"\u0281",
+ "\u02B7"=>"w",
+ "\u02B8"=>"y",
+ "\u02D8"=>" \u0306",
+ "\u02D9"=>" \u0307",
+ "\u02DA"=>" \u030A",
+ "\u02DB"=>" \u0328",
+ "\u02DC"=>" \u0303",
+ "\u02DD"=>" \u030B",
+ "\u02E0"=>"\u0263",
+ "\u02E1"=>"l",
+ "\u02E2"=>"s",
+ "\u02E3"=>"x",
+ "\u02E4"=>"\u0295",
+ "\u037A"=>" \u0345",
+ "\u0384"=>" \u0301",
+ "\u03D0"=>"\u03B2",
+ "\u03D1"=>"\u03B8",
+ "\u03D2"=>"\u03A5",
+ "\u03D5"=>"\u03C6",
+ "\u03D6"=>"\u03C0",
+ "\u03F0"=>"\u03BA",
+ "\u03F1"=>"\u03C1",
+ "\u03F2"=>"\u03C2",
+ "\u03F4"=>"\u0398",
+ "\u03F5"=>"\u03B5",
+ "\u03F9"=>"\u03A3",
+ "\u0587"=>"\u0565\u0582",
+ "\u0675"=>"\u0627\u0674",
+ "\u0676"=>"\u0648\u0674",
+ "\u0677"=>"\u06C7\u0674",
+ "\u0678"=>"\u064A\u0674",
+ "\u0E33"=>"\u0E4D\u0E32",
+ "\u0EB3"=>"\u0ECD\u0EB2",
+ "\u0EDC"=>"\u0EAB\u0E99",
+ "\u0EDD"=>"\u0EAB\u0EA1",
+ "\u0F0C"=>"\u0F0B",
+ "\u0F77"=>"\u0FB2\u0F81",
+ "\u0F79"=>"\u0FB3\u0F81",
+ "\u10FC"=>"\u10DC",
+ "\u1D2C"=>"A",
+ "\u1D2D"=>"\u00C6",
+ "\u1D2E"=>"B",
+ "\u1D30"=>"D",
+ "\u1D31"=>"E",
+ "\u1D32"=>"\u018E",
+ "\u1D33"=>"G",
+ "\u1D34"=>"H",
+ "\u1D35"=>"I",
+ "\u1D36"=>"J",
+ "\u1D37"=>"K",
+ "\u1D38"=>"L",
+ "\u1D39"=>"M",
+ "\u1D3A"=>"N",
+ "\u1D3C"=>"O",
+ "\u1D3D"=>"\u0222",
+ "\u1D3E"=>"P",
+ "\u1D3F"=>"R",
+ "\u1D40"=>"T",
+ "\u1D41"=>"U",
+ "\u1D42"=>"W",
+ "\u1D43"=>"a",
+ "\u1D44"=>"\u0250",
+ "\u1D45"=>"\u0251",
+ "\u1D46"=>"\u1D02",
+ "\u1D47"=>"b",
+ "\u1D48"=>"d",
+ "\u1D49"=>"e",
+ "\u1D4A"=>"\u0259",
+ "\u1D4B"=>"\u025B",
+ "\u1D4C"=>"\u025C",
+ "\u1D4D"=>"g",
+ "\u1D4F"=>"k",
+ "\u1D50"=>"m",
+ "\u1D51"=>"\u014B",
+ "\u1D52"=>"o",
+ "\u1D53"=>"\u0254",
+ "\u1D54"=>"\u1D16",
+ "\u1D55"=>"\u1D17",
+ "\u1D56"=>"p",
+ "\u1D57"=>"t",
+ "\u1D58"=>"u",
+ "\u1D59"=>"\u1D1D",
+ "\u1D5A"=>"\u026F",
+ "\u1D5B"=>"v",
+ "\u1D5C"=>"\u1D25",
+ "\u1D5D"=>"\u03B2",
+ "\u1D5E"=>"\u03B3",
+ "\u1D5F"=>"\u03B4",
+ "\u1D60"=>"\u03C6",
+ "\u1D61"=>"\u03C7",
+ "\u1D62"=>"i",
+ "\u1D63"=>"r",
+ "\u1D64"=>"u",
+ "\u1D65"=>"v",
+ "\u1D66"=>"\u03B2",
+ "\u1D67"=>"\u03B3",
+ "\u1D68"=>"\u03C1",
+ "\u1D69"=>"\u03C6",
+ "\u1D6A"=>"\u03C7",
+ "\u1D78"=>"\u043D",
+ "\u1D9B"=>"\u0252",
+ "\u1D9C"=>"c",
+ "\u1D9D"=>"\u0255",
+ "\u1D9E"=>"\u00F0",
+ "\u1D9F"=>"\u025C",
+ "\u1DA0"=>"f",
+ "\u1DA1"=>"\u025F",
+ "\u1DA2"=>"\u0261",
+ "\u1DA3"=>"\u0265",
+ "\u1DA4"=>"\u0268",
+ "\u1DA5"=>"\u0269",
+ "\u1DA6"=>"\u026A",
+ "\u1DA7"=>"\u1D7B",
+ "\u1DA8"=>"\u029D",
+ "\u1DA9"=>"\u026D",
+ "\u1DAA"=>"\u1D85",
+ "\u1DAB"=>"\u029F",
+ "\u1DAC"=>"\u0271",
+ "\u1DAD"=>"\u0270",
+ "\u1DAE"=>"\u0272",
+ "\u1DAF"=>"\u0273",
+ "\u1DB0"=>"\u0274",
+ "\u1DB1"=>"\u0275",
+ "\u1DB2"=>"\u0278",
+ "\u1DB3"=>"\u0282",
+ "\u1DB4"=>"\u0283",
+ "\u1DB5"=>"\u01AB",
+ "\u1DB6"=>"\u0289",
+ "\u1DB7"=>"\u028A",
+ "\u1DB8"=>"\u1D1C",
+ "\u1DB9"=>"\u028B",
+ "\u1DBA"=>"\u028C",
+ "\u1DBB"=>"z",
+ "\u1DBC"=>"\u0290",
+ "\u1DBD"=>"\u0291",
+ "\u1DBE"=>"\u0292",
+ "\u1DBF"=>"\u03B8",
+ "\u1E9A"=>"a\u02BE",
+ "\u1FBD"=>" \u0313",
+ "\u1FBF"=>" \u0313",
+ "\u1FC0"=>" \u0342",
+ "\u1FFE"=>" \u0314",
+ "\u2002"=>" ",
+ "\u2003"=>" ",
+ "\u2004"=>" ",
+ "\u2005"=>" ",
+ "\u2006"=>" ",
+ "\u2007"=>" ",
+ "\u2008"=>" ",
+ "\u2009"=>" ",
+ "\u200A"=>" ",
+ "\u2011"=>"\u2010",
+ "\u2017"=>" \u0333",
+ "\u2024"=>".",
+ "\u2025"=>"..",
+ "\u2026"=>"...",
+ "\u202F"=>" ",
+ "\u2033"=>"\u2032\u2032",
+ "\u2034"=>"\u2032\u2032\u2032",
+ "\u2036"=>"\u2035\u2035",
+ "\u2037"=>"\u2035\u2035\u2035",
+ "\u203C"=>"!!",
+ "\u203E"=>" \u0305",
+ "\u2047"=>"??",
+ "\u2048"=>"?!",
+ "\u2049"=>"!?",
+ "\u2057"=>"\u2032\u2032\u2032\u2032",
+ "\u205F"=>" ",
+ "\u2070"=>"0",
+ "\u2071"=>"i",
+ "\u2074"=>"4",
+ "\u2075"=>"5",
+ "\u2076"=>"6",
+ "\u2077"=>"7",
+ "\u2078"=>"8",
+ "\u2079"=>"9",
+ "\u207A"=>"+",
+ "\u207B"=>"\u2212",
+ "\u207C"=>"=",
+ "\u207D"=>"(",
+ "\u207E"=>")",
+ "\u207F"=>"n",
+ "\u2080"=>"0",
+ "\u2081"=>"1",
+ "\u2082"=>"2",
+ "\u2083"=>"3",
+ "\u2084"=>"4",
+ "\u2085"=>"5",
+ "\u2086"=>"6",
+ "\u2087"=>"7",
+ "\u2088"=>"8",
+ "\u2089"=>"9",
+ "\u208A"=>"+",
+ "\u208B"=>"\u2212",
+ "\u208C"=>"=",
+ "\u208D"=>"(",
+ "\u208E"=>")",
+ "\u2090"=>"a",
+ "\u2091"=>"e",
+ "\u2092"=>"o",
+ "\u2093"=>"x",
+ "\u2094"=>"\u0259",
+ "\u2095"=>"h",
+ "\u2096"=>"k",
+ "\u2097"=>"l",
+ "\u2098"=>"m",
+ "\u2099"=>"n",
+ "\u209A"=>"p",
+ "\u209B"=>"s",
+ "\u209C"=>"t",
+ "\u20A8"=>"Rs",
+ "\u2100"=>"a/c",
+ "\u2101"=>"a/s",
+ "\u2102"=>"C",
+ "\u2103"=>"\u00B0C",
+ "\u2105"=>"c/o",
+ "\u2106"=>"c/u",
+ "\u2107"=>"\u0190",
+ "\u2109"=>"\u00B0F",
+ "\u210A"=>"g",
+ "\u210B"=>"H",
+ "\u210C"=>"H",
+ "\u210D"=>"H",
+ "\u210E"=>"h",
+ "\u210F"=>"\u0127",
+ "\u2110"=>"I",
+ "\u2111"=>"I",
+ "\u2112"=>"L",
+ "\u2113"=>"l",
+ "\u2115"=>"N",
+ "\u2116"=>"No",
+ "\u2119"=>"P",
+ "\u211A"=>"Q",
+ "\u211B"=>"R",
+ "\u211C"=>"R",
+ "\u211D"=>"R",
+ "\u2120"=>"SM",
+ "\u2121"=>"TEL",
+ "\u2122"=>"TM",
+ "\u2124"=>"Z",
+ "\u2128"=>"Z",
+ "\u212C"=>"B",
+ "\u212D"=>"C",
+ "\u212F"=>"e",
+ "\u2130"=>"E",
+ "\u2131"=>"F",
+ "\u2133"=>"M",
+ "\u2134"=>"o",
+ "\u2135"=>"\u05D0",
+ "\u2136"=>"\u05D1",
+ "\u2137"=>"\u05D2",
+ "\u2138"=>"\u05D3",
+ "\u2139"=>"i",
+ "\u213B"=>"FAX",
+ "\u213C"=>"\u03C0",
+ "\u213D"=>"\u03B3",
+ "\u213E"=>"\u0393",
+ "\u213F"=>"\u03A0",
+ "\u2140"=>"\u2211",
+ "\u2145"=>"D",
+ "\u2146"=>"d",
+ "\u2147"=>"e",
+ "\u2148"=>"i",
+ "\u2149"=>"j",
+ "\u2150"=>"1\u20447",
+ "\u2151"=>"1\u20449",
+ "\u2152"=>"1\u204410",
+ "\u2153"=>"1\u20443",
+ "\u2154"=>"2\u20443",
+ "\u2155"=>"1\u20445",
+ "\u2156"=>"2\u20445",
+ "\u2157"=>"3\u20445",
+ "\u2158"=>"4\u20445",
+ "\u2159"=>"1\u20446",
+ "\u215A"=>"5\u20446",
+ "\u215B"=>"1\u20448",
+ "\u215C"=>"3\u20448",
+ "\u215D"=>"5\u20448",
+ "\u215E"=>"7\u20448",
+ "\u215F"=>"1\u2044",
+ "\u2160"=>"I",
+ "\u2161"=>"II",
+ "\u2162"=>"III",
+ "\u2163"=>"IV",
+ "\u2164"=>"V",
+ "\u2165"=>"VI",
+ "\u2166"=>"VII",
+ "\u2167"=>"VIII",
+ "\u2168"=>"IX",
+ "\u2169"=>"X",
+ "\u216A"=>"XI",
+ "\u216B"=>"XII",
+ "\u216C"=>"L",
+ "\u216D"=>"C",
+ "\u216E"=>"D",
+ "\u216F"=>"M",
+ "\u2170"=>"i",
+ "\u2171"=>"ii",
+ "\u2172"=>"iii",
+ "\u2173"=>"iv",
+ "\u2174"=>"v",
+ "\u2175"=>"vi",
+ "\u2176"=>"vii",
+ "\u2177"=>"viii",
+ "\u2178"=>"ix",
+ "\u2179"=>"x",
+ "\u217A"=>"xi",
+ "\u217B"=>"xii",
+ "\u217C"=>"l",
+ "\u217D"=>"c",
+ "\u217E"=>"d",
+ "\u217F"=>"m",
+ "\u2189"=>"0\u20443",
+ "\u222C"=>"\u222B\u222B",
+ "\u222D"=>"\u222B\u222B\u222B",
+ "\u222F"=>"\u222E\u222E",
+ "\u2230"=>"\u222E\u222E\u222E",
+ "\u2460"=>"1",
+ "\u2461"=>"2",
+ "\u2462"=>"3",
+ "\u2463"=>"4",
+ "\u2464"=>"5",
+ "\u2465"=>"6",
+ "\u2466"=>"7",
+ "\u2467"=>"8",
+ "\u2468"=>"9",
+ "\u2469"=>"10",
+ "\u246A"=>"11",
+ "\u246B"=>"12",
+ "\u246C"=>"13",
+ "\u246D"=>"14",
+ "\u246E"=>"15",
+ "\u246F"=>"16",
+ "\u2470"=>"17",
+ "\u2471"=>"18",
+ "\u2472"=>"19",
+ "\u2473"=>"20",
+ "\u2474"=>"(1)",
+ "\u2475"=>"(2)",
+ "\u2476"=>"(3)",
+ "\u2477"=>"(4)",
+ "\u2478"=>"(5)",
+ "\u2479"=>"(6)",
+ "\u247A"=>"(7)",
+ "\u247B"=>"(8)",
+ "\u247C"=>"(9)",
+ "\u247D"=>"(10)",
+ "\u247E"=>"(11)",
+ "\u247F"=>"(12)",
+ "\u2480"=>"(13)",
+ "\u2481"=>"(14)",
+ "\u2482"=>"(15)",
+ "\u2483"=>"(16)",
+ "\u2484"=>"(17)",
+ "\u2485"=>"(18)",
+ "\u2486"=>"(19)",
+ "\u2487"=>"(20)",
+ "\u2488"=>"1.",
+ "\u2489"=>"2.",
+ "\u248A"=>"3.",
+ "\u248B"=>"4.",
+ "\u248C"=>"5.",
+ "\u248D"=>"6.",
+ "\u248E"=>"7.",
+ "\u248F"=>"8.",
+ "\u2490"=>"9.",
+ "\u2491"=>"10.",
+ "\u2492"=>"11.",
+ "\u2493"=>"12.",
+ "\u2494"=>"13.",
+ "\u2495"=>"14.",
+ "\u2496"=>"15.",
+ "\u2497"=>"16.",
+ "\u2498"=>"17.",
+ "\u2499"=>"18.",
+ "\u249A"=>"19.",
+ "\u249B"=>"20.",
+ "\u249C"=>"(a)",
+ "\u249D"=>"(b)",
+ "\u249E"=>"(c)",
+ "\u249F"=>"(d)",
+ "\u24A0"=>"(e)",
+ "\u24A1"=>"(f)",
+ "\u24A2"=>"(g)",
+ "\u24A3"=>"(h)",
+ "\u24A4"=>"(i)",
+ "\u24A5"=>"(j)",
+ "\u24A6"=>"(k)",
+ "\u24A7"=>"(l)",
+ "\u24A8"=>"(m)",
+ "\u24A9"=>"(n)",
+ "\u24AA"=>"(o)",
+ "\u24AB"=>"(p)",
+ "\u24AC"=>"(q)",
+ "\u24AD"=>"(r)",
+ "\u24AE"=>"(s)",
+ "\u24AF"=>"(t)",
+ "\u24B0"=>"(u)",
+ "\u24B1"=>"(v)",
+ "\u24B2"=>"(w)",
+ "\u24B3"=>"(x)",
+ "\u24B4"=>"(y)",
+ "\u24B5"=>"(z)",
+ "\u24B6"=>"A",
+ "\u24B7"=>"B",
+ "\u24B8"=>"C",
+ "\u24B9"=>"D",
+ "\u24BA"=>"E",
+ "\u24BB"=>"F",
+ "\u24BC"=>"G",
+ "\u24BD"=>"H",
+ "\u24BE"=>"I",
+ "\u24BF"=>"J",
+ "\u24C0"=>"K",
+ "\u24C1"=>"L",
+ "\u24C2"=>"M",
+ "\u24C3"=>"N",
+ "\u24C4"=>"O",
+ "\u24C5"=>"P",
+ "\u24C6"=>"Q",
+ "\u24C7"=>"R",
+ "\u24C8"=>"S",
+ "\u24C9"=>"T",
+ "\u24CA"=>"U",
+ "\u24CB"=>"V",
+ "\u24CC"=>"W",
+ "\u24CD"=>"X",
+ "\u24CE"=>"Y",
+ "\u24CF"=>"Z",
+ "\u24D0"=>"a",
+ "\u24D1"=>"b",
+ "\u24D2"=>"c",
+ "\u24D3"=>"d",
+ "\u24D4"=>"e",
+ "\u24D5"=>"f",
+ "\u24D6"=>"g",
+ "\u24D7"=>"h",
+ "\u24D8"=>"i",
+ "\u24D9"=>"j",
+ "\u24DA"=>"k",
+ "\u24DB"=>"l",
+ "\u24DC"=>"m",
+ "\u24DD"=>"n",
+ "\u24DE"=>"o",
+ "\u24DF"=>"p",
+ "\u24E0"=>"q",
+ "\u24E1"=>"r",
+ "\u24E2"=>"s",
+ "\u24E3"=>"t",
+ "\u24E4"=>"u",
+ "\u24E5"=>"v",
+ "\u24E6"=>"w",
+ "\u24E7"=>"x",
+ "\u24E8"=>"y",
+ "\u24E9"=>"z",
+ "\u24EA"=>"0",
+ "\u2A0C"=>"\u222B\u222B\u222B\u222B",
+ "\u2A74"=>"::=",
+ "\u2A75"=>"==",
+ "\u2A76"=>"===",
+ "\u2C7C"=>"j",
+ "\u2C7D"=>"V",
+ "\u2D6F"=>"\u2D61",
+ "\u2E9F"=>"\u6BCD",
+ "\u2EF3"=>"\u9F9F",
+ "\u2F00"=>"\u4E00",
+ "\u2F01"=>"\u4E28",
+ "\u2F02"=>"\u4E36",
+ "\u2F03"=>"\u4E3F",
+ "\u2F04"=>"\u4E59",
+ "\u2F05"=>"\u4E85",
+ "\u2F06"=>"\u4E8C",
+ "\u2F07"=>"\u4EA0",
+ "\u2F08"=>"\u4EBA",
+ "\u2F09"=>"\u513F",
+ "\u2F0A"=>"\u5165",
+ "\u2F0B"=>"\u516B",
+ "\u2F0C"=>"\u5182",
+ "\u2F0D"=>"\u5196",
+ "\u2F0E"=>"\u51AB",
+ "\u2F0F"=>"\u51E0",
+ "\u2F10"=>"\u51F5",
+ "\u2F11"=>"\u5200",
+ "\u2F12"=>"\u529B",
+ "\u2F13"=>"\u52F9",
+ "\u2F14"=>"\u5315",
+ "\u2F15"=>"\u531A",
+ "\u2F16"=>"\u5338",
+ "\u2F17"=>"\u5341",
+ "\u2F18"=>"\u535C",
+ "\u2F19"=>"\u5369",
+ "\u2F1A"=>"\u5382",
+ "\u2F1B"=>"\u53B6",
+ "\u2F1C"=>"\u53C8",
+ "\u2F1D"=>"\u53E3",
+ "\u2F1E"=>"\u56D7",
+ "\u2F1F"=>"\u571F",
+ "\u2F20"=>"\u58EB",
+ "\u2F21"=>"\u5902",
+ "\u2F22"=>"\u590A",
+ "\u2F23"=>"\u5915",
+ "\u2F24"=>"\u5927",
+ "\u2F25"=>"\u5973",
+ "\u2F26"=>"\u5B50",
+ "\u2F27"=>"\u5B80",
+ "\u2F28"=>"\u5BF8",
+ "\u2F29"=>"\u5C0F",
+ "\u2F2A"=>"\u5C22",
+ "\u2F2B"=>"\u5C38",
+ "\u2F2C"=>"\u5C6E",
+ "\u2F2D"=>"\u5C71",
+ "\u2F2E"=>"\u5DDB",
+ "\u2F2F"=>"\u5DE5",
+ "\u2F30"=>"\u5DF1",
+ "\u2F31"=>"\u5DFE",
+ "\u2F32"=>"\u5E72",
+ "\u2F33"=>"\u5E7A",
+ "\u2F34"=>"\u5E7F",
+ "\u2F35"=>"\u5EF4",
+ "\u2F36"=>"\u5EFE",
+ "\u2F37"=>"\u5F0B",
+ "\u2F38"=>"\u5F13",
+ "\u2F39"=>"\u5F50",
+ "\u2F3A"=>"\u5F61",
+ "\u2F3B"=>"\u5F73",
+ "\u2F3C"=>"\u5FC3",
+ "\u2F3D"=>"\u6208",
+ "\u2F3E"=>"\u6236",
+ "\u2F3F"=>"\u624B",
+ "\u2F40"=>"\u652F",
+ "\u2F41"=>"\u6534",
+ "\u2F42"=>"\u6587",
+ "\u2F43"=>"\u6597",
+ "\u2F44"=>"\u65A4",
+ "\u2F45"=>"\u65B9",
+ "\u2F46"=>"\u65E0",
+ "\u2F47"=>"\u65E5",
+ "\u2F48"=>"\u66F0",
+ "\u2F49"=>"\u6708",
+ "\u2F4A"=>"\u6728",
+ "\u2F4B"=>"\u6B20",
+ "\u2F4C"=>"\u6B62",
+ "\u2F4D"=>"\u6B79",
+ "\u2F4E"=>"\u6BB3",
+ "\u2F4F"=>"\u6BCB",
+ "\u2F50"=>"\u6BD4",
+ "\u2F51"=>"\u6BDB",
+ "\u2F52"=>"\u6C0F",
+ "\u2F53"=>"\u6C14",
+ "\u2F54"=>"\u6C34",
+ "\u2F55"=>"\u706B",
+ "\u2F56"=>"\u722A",
+ "\u2F57"=>"\u7236",
+ "\u2F58"=>"\u723B",
+ "\u2F59"=>"\u723F",
+ "\u2F5A"=>"\u7247",
+ "\u2F5B"=>"\u7259",
+ "\u2F5C"=>"\u725B",
+ "\u2F5D"=>"\u72AC",
+ "\u2F5E"=>"\u7384",
+ "\u2F5F"=>"\u7389",
+ "\u2F60"=>"\u74DC",
+ "\u2F61"=>"\u74E6",
+ "\u2F62"=>"\u7518",
+ "\u2F63"=>"\u751F",
+ "\u2F64"=>"\u7528",
+ "\u2F65"=>"\u7530",
+ "\u2F66"=>"\u758B",
+ "\u2F67"=>"\u7592",
+ "\u2F68"=>"\u7676",
+ "\u2F69"=>"\u767D",
+ "\u2F6A"=>"\u76AE",
+ "\u2F6B"=>"\u76BF",
+ "\u2F6C"=>"\u76EE",
+ "\u2F6D"=>"\u77DB",
+ "\u2F6E"=>"\u77E2",
+ "\u2F6F"=>"\u77F3",
+ "\u2F70"=>"\u793A",
+ "\u2F71"=>"\u79B8",
+ "\u2F72"=>"\u79BE",
+ "\u2F73"=>"\u7A74",
+ "\u2F74"=>"\u7ACB",
+ "\u2F75"=>"\u7AF9",
+ "\u2F76"=>"\u7C73",
+ "\u2F77"=>"\u7CF8",
+ "\u2F78"=>"\u7F36",
+ "\u2F79"=>"\u7F51",
+ "\u2F7A"=>"\u7F8A",
+ "\u2F7B"=>"\u7FBD",
+ "\u2F7C"=>"\u8001",
+ "\u2F7D"=>"\u800C",
+ "\u2F7E"=>"\u8012",
+ "\u2F7F"=>"\u8033",
+ "\u2F80"=>"\u807F",
+ "\u2F81"=>"\u8089",
+ "\u2F82"=>"\u81E3",
+ "\u2F83"=>"\u81EA",
+ "\u2F84"=>"\u81F3",
+ "\u2F85"=>"\u81FC",
+ "\u2F86"=>"\u820C",
+ "\u2F87"=>"\u821B",
+ "\u2F88"=>"\u821F",
+ "\u2F89"=>"\u826E",
+ "\u2F8A"=>"\u8272",
+ "\u2F8B"=>"\u8278",
+ "\u2F8C"=>"\u864D",
+ "\u2F8D"=>"\u866B",
+ "\u2F8E"=>"\u8840",
+ "\u2F8F"=>"\u884C",
+ "\u2F90"=>"\u8863",
+ "\u2F91"=>"\u897E",
+ "\u2F92"=>"\u898B",
+ "\u2F93"=>"\u89D2",
+ "\u2F94"=>"\u8A00",
+ "\u2F95"=>"\u8C37",
+ "\u2F96"=>"\u8C46",
+ "\u2F97"=>"\u8C55",
+ "\u2F98"=>"\u8C78",
+ "\u2F99"=>"\u8C9D",
+ "\u2F9A"=>"\u8D64",
+ "\u2F9B"=>"\u8D70",
+ "\u2F9C"=>"\u8DB3",
+ "\u2F9D"=>"\u8EAB",
+ "\u2F9E"=>"\u8ECA",
+ "\u2F9F"=>"\u8F9B",
+ "\u2FA0"=>"\u8FB0",
+ "\u2FA1"=>"\u8FB5",
+ "\u2FA2"=>"\u9091",
+ "\u2FA3"=>"\u9149",
+ "\u2FA4"=>"\u91C6",
+ "\u2FA5"=>"\u91CC",
+ "\u2FA6"=>"\u91D1",
+ "\u2FA7"=>"\u9577",
+ "\u2FA8"=>"\u9580",
+ "\u2FA9"=>"\u961C",
+ "\u2FAA"=>"\u96B6",
+ "\u2FAB"=>"\u96B9",
+ "\u2FAC"=>"\u96E8",
+ "\u2FAD"=>"\u9751",
+ "\u2FAE"=>"\u975E",
+ "\u2FAF"=>"\u9762",
+ "\u2FB0"=>"\u9769",
+ "\u2FB1"=>"\u97CB",
+ "\u2FB2"=>"\u97ED",
+ "\u2FB3"=>"\u97F3",
+ "\u2FB4"=>"\u9801",
+ "\u2FB5"=>"\u98A8",
+ "\u2FB6"=>"\u98DB",
+ "\u2FB7"=>"\u98DF",
+ "\u2FB8"=>"\u9996",
+ "\u2FB9"=>"\u9999",
+ "\u2FBA"=>"\u99AC",
+ "\u2FBB"=>"\u9AA8",
+ "\u2FBC"=>"\u9AD8",
+ "\u2FBD"=>"\u9ADF",
+ "\u2FBE"=>"\u9B25",
+ "\u2FBF"=>"\u9B2F",
+ "\u2FC0"=>"\u9B32",
+ "\u2FC1"=>"\u9B3C",
+ "\u2FC2"=>"\u9B5A",
+ "\u2FC3"=>"\u9CE5",
+ "\u2FC4"=>"\u9E75",
+ "\u2FC5"=>"\u9E7F",
+ "\u2FC6"=>"\u9EA5",
+ "\u2FC7"=>"\u9EBB",
+ "\u2FC8"=>"\u9EC3",
+ "\u2FC9"=>"\u9ECD",
+ "\u2FCA"=>"\u9ED1",
+ "\u2FCB"=>"\u9EF9",
+ "\u2FCC"=>"\u9EFD",
+ "\u2FCD"=>"\u9F0E",
+ "\u2FCE"=>"\u9F13",
+ "\u2FCF"=>"\u9F20",
+ "\u2FD0"=>"\u9F3B",
+ "\u2FD1"=>"\u9F4A",
+ "\u2FD2"=>"\u9F52",
+ "\u2FD3"=>"\u9F8D",
+ "\u2FD4"=>"\u9F9C",
+ "\u2FD5"=>"\u9FA0",
+ "\u3000"=>" ",
+ "\u3036"=>"\u3012",
+ "\u3038"=>"\u5341",
+ "\u3039"=>"\u5344",
+ "\u303A"=>"\u5345",
+ "\u309B"=>" \u3099",
+ "\u309C"=>" \u309A",
+ "\u309F"=>"\u3088\u308A",
+ "\u30FF"=>"\u30B3\u30C8",
+ "\u3131"=>"\u1100",
+ "\u3132"=>"\u1101",
+ "\u3133"=>"\u11AA",
+ "\u3134"=>"\u1102",
+ "\u3135"=>"\u11AC",
+ "\u3136"=>"\u11AD",
+ "\u3137"=>"\u1103",
+ "\u3138"=>"\u1104",
+ "\u3139"=>"\u1105",
+ "\u313A"=>"\u11B0",
+ "\u313B"=>"\u11B1",
+ "\u313C"=>"\u11B2",
+ "\u313D"=>"\u11B3",
+ "\u313E"=>"\u11B4",
+ "\u313F"=>"\u11B5",
+ "\u3140"=>"\u111A",
+ "\u3141"=>"\u1106",
+ "\u3142"=>"\u1107",
+ "\u3143"=>"\u1108",
+ "\u3144"=>"\u1121",
+ "\u3145"=>"\u1109",
+ "\u3146"=>"\u110A",
+ "\u3147"=>"\u110B",
+ "\u3148"=>"\u110C",
+ "\u3149"=>"\u110D",
+ "\u314A"=>"\u110E",
+ "\u314B"=>"\u110F",
+ "\u314C"=>"\u1110",
+ "\u314D"=>"\u1111",
+ "\u314E"=>"\u1112",
+ "\u314F"=>"\u1161",
+ "\u3150"=>"\u1162",
+ "\u3151"=>"\u1163",
+ "\u3152"=>"\u1164",
+ "\u3153"=>"\u1165",
+ "\u3154"=>"\u1166",
+ "\u3155"=>"\u1167",
+ "\u3156"=>"\u1168",
+ "\u3157"=>"\u1169",
+ "\u3158"=>"\u116A",
+ "\u3159"=>"\u116B",
+ "\u315A"=>"\u116C",
+ "\u315B"=>"\u116D",
+ "\u315C"=>"\u116E",
+ "\u315D"=>"\u116F",
+ "\u315E"=>"\u1170",
+ "\u315F"=>"\u1171",
+ "\u3160"=>"\u1172",
+ "\u3161"=>"\u1173",
+ "\u3162"=>"\u1174",
+ "\u3163"=>"\u1175",
+ "\u3164"=>"\u1160",
+ "\u3165"=>"\u1114",
+ "\u3166"=>"\u1115",
+ "\u3167"=>"\u11C7",
+ "\u3168"=>"\u11C8",
+ "\u3169"=>"\u11CC",
+ "\u316A"=>"\u11CE",
+ "\u316B"=>"\u11D3",
+ "\u316C"=>"\u11D7",
+ "\u316D"=>"\u11D9",
+ "\u316E"=>"\u111C",
+ "\u316F"=>"\u11DD",
+ "\u3170"=>"\u11DF",
+ "\u3171"=>"\u111D",
+ "\u3172"=>"\u111E",
+ "\u3173"=>"\u1120",
+ "\u3174"=>"\u1122",
+ "\u3175"=>"\u1123",
+ "\u3176"=>"\u1127",
+ "\u3177"=>"\u1129",
+ "\u3178"=>"\u112B",
+ "\u3179"=>"\u112C",
+ "\u317A"=>"\u112D",
+ "\u317B"=>"\u112E",
+ "\u317C"=>"\u112F",
+ "\u317D"=>"\u1132",
+ "\u317E"=>"\u1136",
+ "\u317F"=>"\u1140",
+ "\u3180"=>"\u1147",
+ "\u3181"=>"\u114C",
+ "\u3182"=>"\u11F1",
+ "\u3183"=>"\u11F2",
+ "\u3184"=>"\u1157",
+ "\u3185"=>"\u1158",
+ "\u3186"=>"\u1159",
+ "\u3187"=>"\u1184",
+ "\u3188"=>"\u1185",
+ "\u3189"=>"\u1188",
+ "\u318A"=>"\u1191",
+ "\u318B"=>"\u1192",
+ "\u318C"=>"\u1194",
+ "\u318D"=>"\u119E",
+ "\u318E"=>"\u11A1",
+ "\u3192"=>"\u4E00",
+ "\u3193"=>"\u4E8C",
+ "\u3194"=>"\u4E09",
+ "\u3195"=>"\u56DB",
+ "\u3196"=>"\u4E0A",
+ "\u3197"=>"\u4E2D",
+ "\u3198"=>"\u4E0B",
+ "\u3199"=>"\u7532",
+ "\u319A"=>"\u4E59",
+ "\u319B"=>"\u4E19",
+ "\u319C"=>"\u4E01",
+ "\u319D"=>"\u5929",
+ "\u319E"=>"\u5730",
+ "\u319F"=>"\u4EBA",
+ "\u3200"=>"(\u1100)",
+ "\u3201"=>"(\u1102)",
+ "\u3202"=>"(\u1103)",
+ "\u3203"=>"(\u1105)",
+ "\u3204"=>"(\u1106)",
+ "\u3205"=>"(\u1107)",
+ "\u3206"=>"(\u1109)",
+ "\u3207"=>"(\u110B)",
+ "\u3208"=>"(\u110C)",
+ "\u3209"=>"(\u110E)",
+ "\u320A"=>"(\u110F)",
+ "\u320B"=>"(\u1110)",
+ "\u320C"=>"(\u1111)",
+ "\u320D"=>"(\u1112)",
+ "\u320E"=>"(\u1100\u1161)",
+ "\u320F"=>"(\u1102\u1161)",
+ "\u3210"=>"(\u1103\u1161)",
+ "\u3211"=>"(\u1105\u1161)",
+ "\u3212"=>"(\u1106\u1161)",
+ "\u3213"=>"(\u1107\u1161)",
+ "\u3214"=>"(\u1109\u1161)",
+ "\u3215"=>"(\u110B\u1161)",
+ "\u3216"=>"(\u110C\u1161)",
+ "\u3217"=>"(\u110E\u1161)",
+ "\u3218"=>"(\u110F\u1161)",
+ "\u3219"=>"(\u1110\u1161)",
+ "\u321A"=>"(\u1111\u1161)",
+ "\u321B"=>"(\u1112\u1161)",
+ "\u321C"=>"(\u110C\u116E)",
+ "\u321D"=>"(\u110B\u1169\u110C\u1165\u11AB)",
+ "\u321E"=>"(\u110B\u1169\u1112\u116E)",
+ "\u3220"=>"(\u4E00)",
+ "\u3221"=>"(\u4E8C)",
+ "\u3222"=>"(\u4E09)",
+ "\u3223"=>"(\u56DB)",
+ "\u3224"=>"(\u4E94)",
+ "\u3225"=>"(\u516D)",
+ "\u3226"=>"(\u4E03)",
+ "\u3227"=>"(\u516B)",
+ "\u3228"=>"(\u4E5D)",
+ "\u3229"=>"(\u5341)",
+ "\u322A"=>"(\u6708)",
+ "\u322B"=>"(\u706B)",
+ "\u322C"=>"(\u6C34)",
+ "\u322D"=>"(\u6728)",
+ "\u322E"=>"(\u91D1)",
+ "\u322F"=>"(\u571F)",
+ "\u3230"=>"(\u65E5)",
+ "\u3231"=>"(\u682A)",
+ "\u3232"=>"(\u6709)",
+ "\u3233"=>"(\u793E)",
+ "\u3234"=>"(\u540D)",
+ "\u3235"=>"(\u7279)",
+ "\u3236"=>"(\u8CA1)",
+ "\u3237"=>"(\u795D)",
+ "\u3238"=>"(\u52B4)",
+ "\u3239"=>"(\u4EE3)",
+ "\u323A"=>"(\u547C)",
+ "\u323B"=>"(\u5B66)",
+ "\u323C"=>"(\u76E3)",
+ "\u323D"=>"(\u4F01)",
+ "\u323E"=>"(\u8CC7)",
+ "\u323F"=>"(\u5354)",
+ "\u3240"=>"(\u796D)",
+ "\u3241"=>"(\u4F11)",
+ "\u3242"=>"(\u81EA)",
+ "\u3243"=>"(\u81F3)",
+ "\u3244"=>"\u554F",
+ "\u3245"=>"\u5E7C",
+ "\u3246"=>"\u6587",
+ "\u3247"=>"\u7B8F",
+ "\u3250"=>"PTE",
+ "\u3251"=>"21",
+ "\u3252"=>"22",
+ "\u3253"=>"23",
+ "\u3254"=>"24",
+ "\u3255"=>"25",
+ "\u3256"=>"26",
+ "\u3257"=>"27",
+ "\u3258"=>"28",
+ "\u3259"=>"29",
+ "\u325A"=>"30",
+ "\u325B"=>"31",
+ "\u325C"=>"32",
+ "\u325D"=>"33",
+ "\u325E"=>"34",
+ "\u325F"=>"35",
+ "\u3260"=>"\u1100",
+ "\u3261"=>"\u1102",
+ "\u3262"=>"\u1103",
+ "\u3263"=>"\u1105",
+ "\u3264"=>"\u1106",
+ "\u3265"=>"\u1107",
+ "\u3266"=>"\u1109",
+ "\u3267"=>"\u110B",
+ "\u3268"=>"\u110C",
+ "\u3269"=>"\u110E",
+ "\u326A"=>"\u110F",
+ "\u326B"=>"\u1110",
+ "\u326C"=>"\u1111",
+ "\u326D"=>"\u1112",
+ "\u326E"=>"\u1100\u1161",
+ "\u326F"=>"\u1102\u1161",
+ "\u3270"=>"\u1103\u1161",
+ "\u3271"=>"\u1105\u1161",
+ "\u3272"=>"\u1106\u1161",
+ "\u3273"=>"\u1107\u1161",
+ "\u3274"=>"\u1109\u1161",
+ "\u3275"=>"\u110B\u1161",
+ "\u3276"=>"\u110C\u1161",
+ "\u3277"=>"\u110E\u1161",
+ "\u3278"=>"\u110F\u1161",
+ "\u3279"=>"\u1110\u1161",
+ "\u327A"=>"\u1111\u1161",
+ "\u327B"=>"\u1112\u1161",
+ "\u327C"=>"\u110E\u1161\u11B7\u1100\u1169",
+ "\u327D"=>"\u110C\u116E\u110B\u1174",
+ "\u327E"=>"\u110B\u116E",
+ "\u3280"=>"\u4E00",
+ "\u3281"=>"\u4E8C",
+ "\u3282"=>"\u4E09",
+ "\u3283"=>"\u56DB",
+ "\u3284"=>"\u4E94",
+ "\u3285"=>"\u516D",
+ "\u3286"=>"\u4E03",
+ "\u3287"=>"\u516B",
+ "\u3288"=>"\u4E5D",
+ "\u3289"=>"\u5341",
+ "\u328A"=>"\u6708",
+ "\u328B"=>"\u706B",
+ "\u328C"=>"\u6C34",
+ "\u328D"=>"\u6728",
+ "\u328E"=>"\u91D1",
+ "\u328F"=>"\u571F",
+ "\u3290"=>"\u65E5",
+ "\u3291"=>"\u682A",
+ "\u3292"=>"\u6709",
+ "\u3293"=>"\u793E",
+ "\u3294"=>"\u540D",
+ "\u3295"=>"\u7279",
+ "\u3296"=>"\u8CA1",
+ "\u3297"=>"\u795D",
+ "\u3298"=>"\u52B4",
+ "\u3299"=>"\u79D8",
+ "\u329A"=>"\u7537",
+ "\u329B"=>"\u5973",
+ "\u329C"=>"\u9069",
+ "\u329D"=>"\u512A",
+ "\u329E"=>"\u5370",
+ "\u329F"=>"\u6CE8",
+ "\u32A0"=>"\u9805",
+ "\u32A1"=>"\u4F11",
+ "\u32A2"=>"\u5199",
+ "\u32A3"=>"\u6B63",
+ "\u32A4"=>"\u4E0A",
+ "\u32A5"=>"\u4E2D",
+ "\u32A6"=>"\u4E0B",
+ "\u32A7"=>"\u5DE6",
+ "\u32A8"=>"\u53F3",
+ "\u32A9"=>"\u533B",
+ "\u32AA"=>"\u5B97",
+ "\u32AB"=>"\u5B66",
+ "\u32AC"=>"\u76E3",
+ "\u32AD"=>"\u4F01",
+ "\u32AE"=>"\u8CC7",
+ "\u32AF"=>"\u5354",
+ "\u32B0"=>"\u591C",
+ "\u32B1"=>"36",
+ "\u32B2"=>"37",
+ "\u32B3"=>"38",
+ "\u32B4"=>"39",
+ "\u32B5"=>"40",
+ "\u32B6"=>"41",
+ "\u32B7"=>"42",
+ "\u32B8"=>"43",
+ "\u32B9"=>"44",
+ "\u32BA"=>"45",
+ "\u32BB"=>"46",
+ "\u32BC"=>"47",
+ "\u32BD"=>"48",
+ "\u32BE"=>"49",
+ "\u32BF"=>"50",
+ "\u32C0"=>"1\u6708",
+ "\u32C1"=>"2\u6708",
+ "\u32C2"=>"3\u6708",
+ "\u32C3"=>"4\u6708",
+ "\u32C4"=>"5\u6708",
+ "\u32C5"=>"6\u6708",
+ "\u32C6"=>"7\u6708",
+ "\u32C7"=>"8\u6708",
+ "\u32C8"=>"9\u6708",
+ "\u32C9"=>"10\u6708",
+ "\u32CA"=>"11\u6708",
+ "\u32CB"=>"12\u6708",
+ "\u32CC"=>"Hg",
+ "\u32CD"=>"erg",
+ "\u32CE"=>"eV",
+ "\u32CF"=>"LTD",
+ "\u32D0"=>"\u30A2",
+ "\u32D1"=>"\u30A4",
+ "\u32D2"=>"\u30A6",
+ "\u32D3"=>"\u30A8",
+ "\u32D4"=>"\u30AA",
+ "\u32D5"=>"\u30AB",
+ "\u32D6"=>"\u30AD",
+ "\u32D7"=>"\u30AF",
+ "\u32D8"=>"\u30B1",
+ "\u32D9"=>"\u30B3",
+ "\u32DA"=>"\u30B5",
+ "\u32DB"=>"\u30B7",
+ "\u32DC"=>"\u30B9",
+ "\u32DD"=>"\u30BB",
+ "\u32DE"=>"\u30BD",
+ "\u32DF"=>"\u30BF",
+ "\u32E0"=>"\u30C1",
+ "\u32E1"=>"\u30C4",
+ "\u32E2"=>"\u30C6",
+ "\u32E3"=>"\u30C8",
+ "\u32E4"=>"\u30CA",
+ "\u32E5"=>"\u30CB",
+ "\u32E6"=>"\u30CC",
+ "\u32E7"=>"\u30CD",
+ "\u32E8"=>"\u30CE",
+ "\u32E9"=>"\u30CF",
+ "\u32EA"=>"\u30D2",
+ "\u32EB"=>"\u30D5",
+ "\u32EC"=>"\u30D8",
+ "\u32ED"=>"\u30DB",
+ "\u32EE"=>"\u30DE",
+ "\u32EF"=>"\u30DF",
+ "\u32F0"=>"\u30E0",
+ "\u32F1"=>"\u30E1",
+ "\u32F2"=>"\u30E2",
+ "\u32F3"=>"\u30E4",
+ "\u32F4"=>"\u30E6",
+ "\u32F5"=>"\u30E8",
+ "\u32F6"=>"\u30E9",
+ "\u32F7"=>"\u30EA",
+ "\u32F8"=>"\u30EB",
+ "\u32F9"=>"\u30EC",
+ "\u32FA"=>"\u30ED",
+ "\u32FB"=>"\u30EF",
+ "\u32FC"=>"\u30F0",
+ "\u32FD"=>"\u30F1",
+ "\u32FE"=>"\u30F2",
+ "\u32FF"=>"\u4EE4\u548C",
+ "\u3300"=>"\u30A2\u30D1\u30FC\u30C8",
+ "\u3301"=>"\u30A2\u30EB\u30D5\u30A1",
+ "\u3302"=>"\u30A2\u30F3\u30DA\u30A2",
+ "\u3303"=>"\u30A2\u30FC\u30EB",
+ "\u3304"=>"\u30A4\u30CB\u30F3\u30B0",
+ "\u3305"=>"\u30A4\u30F3\u30C1",
+ "\u3306"=>"\u30A6\u30A9\u30F3",
+ "\u3307"=>"\u30A8\u30B9\u30AF\u30FC\u30C9",
+ "\u3308"=>"\u30A8\u30FC\u30AB\u30FC",
+ "\u3309"=>"\u30AA\u30F3\u30B9",
+ "\u330A"=>"\u30AA\u30FC\u30E0",
+ "\u330B"=>"\u30AB\u30A4\u30EA",
+ "\u330C"=>"\u30AB\u30E9\u30C3\u30C8",
+ "\u330D"=>"\u30AB\u30ED\u30EA\u30FC",
+ "\u330E"=>"\u30AC\u30ED\u30F3",
+ "\u330F"=>"\u30AC\u30F3\u30DE",
+ "\u3310"=>"\u30AE\u30AC",
+ "\u3311"=>"\u30AE\u30CB\u30FC",
+ "\u3312"=>"\u30AD\u30E5\u30EA\u30FC",
+ "\u3313"=>"\u30AE\u30EB\u30C0\u30FC",
+ "\u3314"=>"\u30AD\u30ED",
+ "\u3315"=>"\u30AD\u30ED\u30B0\u30E9\u30E0",
+ "\u3316"=>"\u30AD\u30ED\u30E1\u30FC\u30C8\u30EB",
+ "\u3317"=>"\u30AD\u30ED\u30EF\u30C3\u30C8",
+ "\u3318"=>"\u30B0\u30E9\u30E0",
+ "\u3319"=>"\u30B0\u30E9\u30E0\u30C8\u30F3",
+ "\u331A"=>"\u30AF\u30EB\u30BC\u30A4\u30ED",
+ "\u331B"=>"\u30AF\u30ED\u30FC\u30CD",
+ "\u331C"=>"\u30B1\u30FC\u30B9",
+ "\u331D"=>"\u30B3\u30EB\u30CA",
+ "\u331E"=>"\u30B3\u30FC\u30DD",
+ "\u331F"=>"\u30B5\u30A4\u30AF\u30EB",
+ "\u3320"=>"\u30B5\u30F3\u30C1\u30FC\u30E0",
+ "\u3321"=>"\u30B7\u30EA\u30F3\u30B0",
+ "\u3322"=>"\u30BB\u30F3\u30C1",
+ "\u3323"=>"\u30BB\u30F3\u30C8",
+ "\u3324"=>"\u30C0\u30FC\u30B9",
+ "\u3325"=>"\u30C7\u30B7",
+ "\u3326"=>"\u30C9\u30EB",
+ "\u3327"=>"\u30C8\u30F3",
+ "\u3328"=>"\u30CA\u30CE",
+ "\u3329"=>"\u30CE\u30C3\u30C8",
+ "\u332A"=>"\u30CF\u30A4\u30C4",
+ "\u332B"=>"\u30D1\u30FC\u30BB\u30F3\u30C8",
+ "\u332C"=>"\u30D1\u30FC\u30C4",
+ "\u332D"=>"\u30D0\u30FC\u30EC\u30EB",
+ "\u332E"=>"\u30D4\u30A2\u30B9\u30C8\u30EB",
+ "\u332F"=>"\u30D4\u30AF\u30EB",
+ "\u3330"=>"\u30D4\u30B3",
+ "\u3331"=>"\u30D3\u30EB",
+ "\u3332"=>"\u30D5\u30A1\u30E9\u30C3\u30C9",
+ "\u3333"=>"\u30D5\u30A3\u30FC\u30C8",
+ "\u3334"=>"\u30D6\u30C3\u30B7\u30A7\u30EB",
+ "\u3335"=>"\u30D5\u30E9\u30F3",
+ "\u3336"=>"\u30D8\u30AF\u30BF\u30FC\u30EB",
+ "\u3337"=>"\u30DA\u30BD",
+ "\u3338"=>"\u30DA\u30CB\u30D2",
+ "\u3339"=>"\u30D8\u30EB\u30C4",
+ "\u333A"=>"\u30DA\u30F3\u30B9",
+ "\u333B"=>"\u30DA\u30FC\u30B8",
+ "\u333C"=>"\u30D9\u30FC\u30BF",
+ "\u333D"=>"\u30DD\u30A4\u30F3\u30C8",
+ "\u333E"=>"\u30DC\u30EB\u30C8",
+ "\u333F"=>"\u30DB\u30F3",
+ "\u3340"=>"\u30DD\u30F3\u30C9",
+ "\u3341"=>"\u30DB\u30FC\u30EB",
+ "\u3342"=>"\u30DB\u30FC\u30F3",
+ "\u3343"=>"\u30DE\u30A4\u30AF\u30ED",
+ "\u3344"=>"\u30DE\u30A4\u30EB",
+ "\u3345"=>"\u30DE\u30C3\u30CF",
+ "\u3346"=>"\u30DE\u30EB\u30AF",
+ "\u3347"=>"\u30DE\u30F3\u30B7\u30E7\u30F3",
+ "\u3348"=>"\u30DF\u30AF\u30ED\u30F3",
+ "\u3349"=>"\u30DF\u30EA",
+ "\u334A"=>"\u30DF\u30EA\u30D0\u30FC\u30EB",
+ "\u334B"=>"\u30E1\u30AC",
+ "\u334C"=>"\u30E1\u30AC\u30C8\u30F3",
+ "\u334D"=>"\u30E1\u30FC\u30C8\u30EB",
+ "\u334E"=>"\u30E4\u30FC\u30C9",
+ "\u334F"=>"\u30E4\u30FC\u30EB",
+ "\u3350"=>"\u30E6\u30A2\u30F3",
+ "\u3351"=>"\u30EA\u30C3\u30C8\u30EB",
+ "\u3352"=>"\u30EA\u30E9",
+ "\u3353"=>"\u30EB\u30D4\u30FC",
+ "\u3354"=>"\u30EB\u30FC\u30D6\u30EB",
+ "\u3355"=>"\u30EC\u30E0",
+ "\u3356"=>"\u30EC\u30F3\u30C8\u30B2\u30F3",
+ "\u3357"=>"\u30EF\u30C3\u30C8",
+ "\u3358"=>"0\u70B9",
+ "\u3359"=>"1\u70B9",
+ "\u335A"=>"2\u70B9",
+ "\u335B"=>"3\u70B9",
+ "\u335C"=>"4\u70B9",
+ "\u335D"=>"5\u70B9",
+ "\u335E"=>"6\u70B9",
+ "\u335F"=>"7\u70B9",
+ "\u3360"=>"8\u70B9",
+ "\u3361"=>"9\u70B9",
+ "\u3362"=>"10\u70B9",
+ "\u3363"=>"11\u70B9",
+ "\u3364"=>"12\u70B9",
+ "\u3365"=>"13\u70B9",
+ "\u3366"=>"14\u70B9",
+ "\u3367"=>"15\u70B9",
+ "\u3368"=>"16\u70B9",
+ "\u3369"=>"17\u70B9",
+ "\u336A"=>"18\u70B9",
+ "\u336B"=>"19\u70B9",
+ "\u336C"=>"20\u70B9",
+ "\u336D"=>"21\u70B9",
+ "\u336E"=>"22\u70B9",
+ "\u336F"=>"23\u70B9",
+ "\u3370"=>"24\u70B9",
+ "\u3371"=>"hPa",
+ "\u3372"=>"da",
+ "\u3373"=>"AU",
+ "\u3374"=>"bar",
+ "\u3375"=>"oV",
+ "\u3376"=>"pc",
+ "\u3377"=>"dm",
+ "\u3378"=>"dm2",
+ "\u3379"=>"dm3",
+ "\u337A"=>"IU",
+ "\u337B"=>"\u5E73\u6210",
+ "\u337C"=>"\u662D\u548C",
+ "\u337D"=>"\u5927\u6B63",
+ "\u337E"=>"\u660E\u6CBB",
+ "\u337F"=>"\u682A\u5F0F\u4F1A\u793E",
+ "\u3380"=>"pA",
+ "\u3381"=>"nA",
+ "\u3382"=>"\u03BCA",
+ "\u3383"=>"mA",
+ "\u3384"=>"kA",
+ "\u3385"=>"KB",
+ "\u3386"=>"MB",
+ "\u3387"=>"GB",
+ "\u3388"=>"cal",
+ "\u3389"=>"kcal",
+ "\u338A"=>"pF",
+ "\u338B"=>"nF",
+ "\u338C"=>"\u03BCF",
+ "\u338D"=>"\u03BCg",
+ "\u338E"=>"mg",
+ "\u338F"=>"kg",
+ "\u3390"=>"Hz",
+ "\u3391"=>"kHz",
+ "\u3392"=>"MHz",
+ "\u3393"=>"GHz",
+ "\u3394"=>"THz",
+ "\u3395"=>"\u03BCl",
+ "\u3396"=>"ml",
+ "\u3397"=>"dl",
+ "\u3398"=>"kl",
+ "\u3399"=>"fm",
+ "\u339A"=>"nm",
+ "\u339B"=>"\u03BCm",
+ "\u339C"=>"mm",
+ "\u339D"=>"cm",
+ "\u339E"=>"km",
+ "\u339F"=>"mm2",
+ "\u33A0"=>"cm2",
+ "\u33A1"=>"m2",
+ "\u33A2"=>"km2",
+ "\u33A3"=>"mm3",
+ "\u33A4"=>"cm3",
+ "\u33A5"=>"m3",
+ "\u33A6"=>"km3",
+ "\u33A7"=>"m\u2215s",
+ "\u33A8"=>"m\u2215s2",
+ "\u33A9"=>"Pa",
+ "\u33AA"=>"kPa",
+ "\u33AB"=>"MPa",
+ "\u33AC"=>"GPa",
+ "\u33AD"=>"rad",
+ "\u33AE"=>"rad\u2215s",
+ "\u33AF"=>"rad\u2215s2",
+ "\u33B0"=>"ps",
+ "\u33B1"=>"ns",
+ "\u33B2"=>"\u03BCs",
+ "\u33B3"=>"ms",
+ "\u33B4"=>"pV",
+ "\u33B5"=>"nV",
+ "\u33B6"=>"\u03BCV",
+ "\u33B7"=>"mV",
+ "\u33B8"=>"kV",
+ "\u33B9"=>"MV",
+ "\u33BA"=>"pW",
+ "\u33BB"=>"nW",
+ "\u33BC"=>"\u03BCW",
+ "\u33BD"=>"mW",
+ "\u33BE"=>"kW",
+ "\u33BF"=>"MW",
+ "\u33C0"=>"k\u03A9",
+ "\u33C1"=>"M\u03A9",
+ "\u33C2"=>"a.m.",
+ "\u33C3"=>"Bq",
+ "\u33C4"=>"cc",
+ "\u33C5"=>"cd",
+ "\u33C6"=>"C\u2215kg",
+ "\u33C7"=>"Co.",
+ "\u33C8"=>"dB",
+ "\u33C9"=>"Gy",
+ "\u33CA"=>"ha",
+ "\u33CB"=>"HP",
+ "\u33CC"=>"in",
+ "\u33CD"=>"KK",
+ "\u33CE"=>"KM",
+ "\u33CF"=>"kt",
+ "\u33D0"=>"lm",
+ "\u33D1"=>"ln",
+ "\u33D2"=>"log",
+ "\u33D3"=>"lx",
+ "\u33D4"=>"mb",
+ "\u33D5"=>"mil",
+ "\u33D6"=>"mol",
+ "\u33D7"=>"PH",
+ "\u33D8"=>"p.m.",
+ "\u33D9"=>"PPM",
+ "\u33DA"=>"PR",
+ "\u33DB"=>"sr",
+ "\u33DC"=>"Sv",
+ "\u33DD"=>"Wb",
+ "\u33DE"=>"V\u2215m",
+ "\u33DF"=>"A\u2215m",
+ "\u33E0"=>"1\u65E5",
+ "\u33E1"=>"2\u65E5",
+ "\u33E2"=>"3\u65E5",
+ "\u33E3"=>"4\u65E5",
+ "\u33E4"=>"5\u65E5",
+ "\u33E5"=>"6\u65E5",
+ "\u33E6"=>"7\u65E5",
+ "\u33E7"=>"8\u65E5",
+ "\u33E8"=>"9\u65E5",
+ "\u33E9"=>"10\u65E5",
+ "\u33EA"=>"11\u65E5",
+ "\u33EB"=>"12\u65E5",
+ "\u33EC"=>"13\u65E5",
+ "\u33ED"=>"14\u65E5",
+ "\u33EE"=>"15\u65E5",
+ "\u33EF"=>"16\u65E5",
+ "\u33F0"=>"17\u65E5",
+ "\u33F1"=>"18\u65E5",
+ "\u33F2"=>"19\u65E5",
+ "\u33F3"=>"20\u65E5",
+ "\u33F4"=>"21\u65E5",
+ "\u33F5"=>"22\u65E5",
+ "\u33F6"=>"23\u65E5",
+ "\u33F7"=>"24\u65E5",
+ "\u33F8"=>"25\u65E5",
+ "\u33F9"=>"26\u65E5",
+ "\u33FA"=>"27\u65E5",
+ "\u33FB"=>"28\u65E5",
+ "\u33FC"=>"29\u65E5",
+ "\u33FD"=>"30\u65E5",
+ "\u33FE"=>"31\u65E5",
+ "\u33FF"=>"gal",
+ "\uA69C"=>"\u044A",
+ "\uA69D"=>"\u044C",
+ "\uA770"=>"\uA76F",
+ "\uA7F8"=>"\u0126",
+ "\uA7F9"=>"\u0153",
+ "\uAB5C"=>"\uA727",
+ "\uAB5D"=>"\uAB37",
+ "\uAB5E"=>"\u026B",
+ "\uAB5F"=>"\uAB52",
+ "\uFB00"=>"ff",
+ "\uFB01"=>"fi",
+ "\uFB02"=>"fl",
+ "\uFB03"=>"ffi",
+ "\uFB04"=>"ffl",
+ "\uFB05"=>"st",
+ "\uFB06"=>"st",
+ "\uFB13"=>"\u0574\u0576",
+ "\uFB14"=>"\u0574\u0565",
+ "\uFB15"=>"\u0574\u056B",
+ "\uFB16"=>"\u057E\u0576",
+ "\uFB17"=>"\u0574\u056D",
+ "\uFB20"=>"\u05E2",
+ "\uFB21"=>"\u05D0",
+ "\uFB22"=>"\u05D3",
+ "\uFB23"=>"\u05D4",
+ "\uFB24"=>"\u05DB",
+ "\uFB25"=>"\u05DC",
+ "\uFB26"=>"\u05DD",
+ "\uFB27"=>"\u05E8",
+ "\uFB28"=>"\u05EA",
+ "\uFB29"=>"+",
+ "\uFB4F"=>"\u05D0\u05DC",
+ "\uFB50"=>"\u0671",
+ "\uFB51"=>"\u0671",
+ "\uFB52"=>"\u067B",
+ "\uFB53"=>"\u067B",
+ "\uFB54"=>"\u067B",
+ "\uFB55"=>"\u067B",
+ "\uFB56"=>"\u067E",
+ "\uFB57"=>"\u067E",
+ "\uFB58"=>"\u067E",
+ "\uFB59"=>"\u067E",
+ "\uFB5A"=>"\u0680",
+ "\uFB5B"=>"\u0680",
+ "\uFB5C"=>"\u0680",
+ "\uFB5D"=>"\u0680",
+ "\uFB5E"=>"\u067A",
+ "\uFB5F"=>"\u067A",
+ "\uFB60"=>"\u067A",
+ "\uFB61"=>"\u067A",
+ "\uFB62"=>"\u067F",
+ "\uFB63"=>"\u067F",
+ "\uFB64"=>"\u067F",
+ "\uFB65"=>"\u067F",
+ "\uFB66"=>"\u0679",
+ "\uFB67"=>"\u0679",
+ "\uFB68"=>"\u0679",
+ "\uFB69"=>"\u0679",
+ "\uFB6A"=>"\u06A4",
+ "\uFB6B"=>"\u06A4",
+ "\uFB6C"=>"\u06A4",
+ "\uFB6D"=>"\u06A4",
+ "\uFB6E"=>"\u06A6",
+ "\uFB6F"=>"\u06A6",
+ "\uFB70"=>"\u06A6",
+ "\uFB71"=>"\u06A6",
+ "\uFB72"=>"\u0684",
+ "\uFB73"=>"\u0684",
+ "\uFB74"=>"\u0684",
+ "\uFB75"=>"\u0684",
+ "\uFB76"=>"\u0683",
+ "\uFB77"=>"\u0683",
+ "\uFB78"=>"\u0683",
+ "\uFB79"=>"\u0683",
+ "\uFB7A"=>"\u0686",
+ "\uFB7B"=>"\u0686",
+ "\uFB7C"=>"\u0686",
+ "\uFB7D"=>"\u0686",
+ "\uFB7E"=>"\u0687",
+ "\uFB7F"=>"\u0687",
+ "\uFB80"=>"\u0687",
+ "\uFB81"=>"\u0687",
+ "\uFB82"=>"\u068D",
+ "\uFB83"=>"\u068D",
+ "\uFB84"=>"\u068C",
+ "\uFB85"=>"\u068C",
+ "\uFB86"=>"\u068E",
+ "\uFB87"=>"\u068E",
+ "\uFB88"=>"\u0688",
+ "\uFB89"=>"\u0688",
+ "\uFB8A"=>"\u0698",
+ "\uFB8B"=>"\u0698",
+ "\uFB8C"=>"\u0691",
+ "\uFB8D"=>"\u0691",
+ "\uFB8E"=>"\u06A9",
+ "\uFB8F"=>"\u06A9",
+ "\uFB90"=>"\u06A9",
+ "\uFB91"=>"\u06A9",
+ "\uFB92"=>"\u06AF",
+ "\uFB93"=>"\u06AF",
+ "\uFB94"=>"\u06AF",
+ "\uFB95"=>"\u06AF",
+ "\uFB96"=>"\u06B3",
+ "\uFB97"=>"\u06B3",
+ "\uFB98"=>"\u06B3",
+ "\uFB99"=>"\u06B3",
+ "\uFB9A"=>"\u06B1",
+ "\uFB9B"=>"\u06B1",
+ "\uFB9C"=>"\u06B1",
+ "\uFB9D"=>"\u06B1",
+ "\uFB9E"=>"\u06BA",
+ "\uFB9F"=>"\u06BA",
+ "\uFBA0"=>"\u06BB",
+ "\uFBA1"=>"\u06BB",
+ "\uFBA2"=>"\u06BB",
+ "\uFBA3"=>"\u06BB",
+ "\uFBA4"=>"\u06C0",
+ "\uFBA5"=>"\u06C0",
+ "\uFBA6"=>"\u06C1",
+ "\uFBA7"=>"\u06C1",
+ "\uFBA8"=>"\u06C1",
+ "\uFBA9"=>"\u06C1",
+ "\uFBAA"=>"\u06BE",
+ "\uFBAB"=>"\u06BE",
+ "\uFBAC"=>"\u06BE",
+ "\uFBAD"=>"\u06BE",
+ "\uFBAE"=>"\u06D2",
+ "\uFBAF"=>"\u06D2",
+ "\uFBB0"=>"\u06D3",
+ "\uFBB1"=>"\u06D3",
+ "\uFBD3"=>"\u06AD",
+ "\uFBD4"=>"\u06AD",
+ "\uFBD5"=>"\u06AD",
+ "\uFBD6"=>"\u06AD",
+ "\uFBD7"=>"\u06C7",
+ "\uFBD8"=>"\u06C7",
+ "\uFBD9"=>"\u06C6",
+ "\uFBDA"=>"\u06C6",
+ "\uFBDB"=>"\u06C8",
+ "\uFBDC"=>"\u06C8",
+ "\uFBDD"=>"\u06C7\u0674",
+ "\uFBDE"=>"\u06CB",
+ "\uFBDF"=>"\u06CB",
+ "\uFBE0"=>"\u06C5",
+ "\uFBE1"=>"\u06C5",
+ "\uFBE2"=>"\u06C9",
+ "\uFBE3"=>"\u06C9",
+ "\uFBE4"=>"\u06D0",
+ "\uFBE5"=>"\u06D0",
+ "\uFBE6"=>"\u06D0",
+ "\uFBE7"=>"\u06D0",
+ "\uFBE8"=>"\u0649",
+ "\uFBE9"=>"\u0649",
+ "\uFBEA"=>"\u0626\u0627",
+ "\uFBEB"=>"\u0626\u0627",
+ "\uFBEC"=>"\u0626\u06D5",
+ "\uFBED"=>"\u0626\u06D5",
+ "\uFBEE"=>"\u0626\u0648",
+ "\uFBEF"=>"\u0626\u0648",
+ "\uFBF0"=>"\u0626\u06C7",
+ "\uFBF1"=>"\u0626\u06C7",
+ "\uFBF2"=>"\u0626\u06C6",
+ "\uFBF3"=>"\u0626\u06C6",
+ "\uFBF4"=>"\u0626\u06C8",
+ "\uFBF5"=>"\u0626\u06C8",
+ "\uFBF6"=>"\u0626\u06D0",
+ "\uFBF7"=>"\u0626\u06D0",
+ "\uFBF8"=>"\u0626\u06D0",
+ "\uFBF9"=>"\u0626\u0649",
+ "\uFBFA"=>"\u0626\u0649",
+ "\uFBFB"=>"\u0626\u0649",
+ "\uFBFC"=>"\u06CC",
+ "\uFBFD"=>"\u06CC",
+ "\uFBFE"=>"\u06CC",
+ "\uFBFF"=>"\u06CC",
+ "\uFC00"=>"\u0626\u062C",
+ "\uFC01"=>"\u0626\u062D",
+ "\uFC02"=>"\u0626\u0645",
+ "\uFC03"=>"\u0626\u0649",
+ "\uFC04"=>"\u0626\u064A",
+ "\uFC05"=>"\u0628\u062C",
+ "\uFC06"=>"\u0628\u062D",
+ "\uFC07"=>"\u0628\u062E",
+ "\uFC08"=>"\u0628\u0645",
+ "\uFC09"=>"\u0628\u0649",
+ "\uFC0A"=>"\u0628\u064A",
+ "\uFC0B"=>"\u062A\u062C",
+ "\uFC0C"=>"\u062A\u062D",
+ "\uFC0D"=>"\u062A\u062E",
+ "\uFC0E"=>"\u062A\u0645",
+ "\uFC0F"=>"\u062A\u0649",
+ "\uFC10"=>"\u062A\u064A",
+ "\uFC11"=>"\u062B\u062C",
+ "\uFC12"=>"\u062B\u0645",
+ "\uFC13"=>"\u062B\u0649",
+ "\uFC14"=>"\u062B\u064A",
+ "\uFC15"=>"\u062C\u062D",
+ "\uFC16"=>"\u062C\u0645",
+ "\uFC17"=>"\u062D\u062C",
+ "\uFC18"=>"\u062D\u0645",
+ "\uFC19"=>"\u062E\u062C",
+ "\uFC1A"=>"\u062E\u062D",
+ "\uFC1B"=>"\u062E\u0645",
+ "\uFC1C"=>"\u0633\u062C",
+ "\uFC1D"=>"\u0633\u062D",
+ "\uFC1E"=>"\u0633\u062E",
+ "\uFC1F"=>"\u0633\u0645",
+ "\uFC20"=>"\u0635\u062D",
+ "\uFC21"=>"\u0635\u0645",
+ "\uFC22"=>"\u0636\u062C",
+ "\uFC23"=>"\u0636\u062D",
+ "\uFC24"=>"\u0636\u062E",
+ "\uFC25"=>"\u0636\u0645",
+ "\uFC26"=>"\u0637\u062D",
+ "\uFC27"=>"\u0637\u0645",
+ "\uFC28"=>"\u0638\u0645",
+ "\uFC29"=>"\u0639\u062C",
+ "\uFC2A"=>"\u0639\u0645",
+ "\uFC2B"=>"\u063A\u062C",
+ "\uFC2C"=>"\u063A\u0645",
+ "\uFC2D"=>"\u0641\u062C",
+ "\uFC2E"=>"\u0641\u062D",
+ "\uFC2F"=>"\u0641\u062E",
+ "\uFC30"=>"\u0641\u0645",
+ "\uFC31"=>"\u0641\u0649",
+ "\uFC32"=>"\u0641\u064A",
+ "\uFC33"=>"\u0642\u062D",
+ "\uFC34"=>"\u0642\u0645",
+ "\uFC35"=>"\u0642\u0649",
+ "\uFC36"=>"\u0642\u064A",
+ "\uFC37"=>"\u0643\u0627",
+ "\uFC38"=>"\u0643\u062C",
+ "\uFC39"=>"\u0643\u062D",
+ "\uFC3A"=>"\u0643\u062E",
+ "\uFC3B"=>"\u0643\u0644",
+ "\uFC3C"=>"\u0643\u0645",
+ "\uFC3D"=>"\u0643\u0649",
+ "\uFC3E"=>"\u0643\u064A",
+ "\uFC3F"=>"\u0644\u062C",
+ "\uFC40"=>"\u0644\u062D",
+ "\uFC41"=>"\u0644\u062E",
+ "\uFC42"=>"\u0644\u0645",
+ "\uFC43"=>"\u0644\u0649",
+ "\uFC44"=>"\u0644\u064A",
+ "\uFC45"=>"\u0645\u062C",
+ "\uFC46"=>"\u0645\u062D",
+ "\uFC47"=>"\u0645\u062E",
+ "\uFC48"=>"\u0645\u0645",
+ "\uFC49"=>"\u0645\u0649",
+ "\uFC4A"=>"\u0645\u064A",
+ "\uFC4B"=>"\u0646\u062C",
+ "\uFC4C"=>"\u0646\u062D",
+ "\uFC4D"=>"\u0646\u062E",
+ "\uFC4E"=>"\u0646\u0645",
+ "\uFC4F"=>"\u0646\u0649",
+ "\uFC50"=>"\u0646\u064A",
+ "\uFC51"=>"\u0647\u062C",
+ "\uFC52"=>"\u0647\u0645",
+ "\uFC53"=>"\u0647\u0649",
+ "\uFC54"=>"\u0647\u064A",
+ "\uFC55"=>"\u064A\u062C",
+ "\uFC56"=>"\u064A\u062D",
+ "\uFC57"=>"\u064A\u062E",
+ "\uFC58"=>"\u064A\u0645",
+ "\uFC59"=>"\u064A\u0649",
+ "\uFC5A"=>"\u064A\u064A",
+ "\uFC5B"=>"\u0630\u0670",
+ "\uFC5C"=>"\u0631\u0670",
+ "\uFC5D"=>"\u0649\u0670",
+ "\uFC5E"=>" \u064C\u0651",
+ "\uFC5F"=>" \u064D\u0651",
+ "\uFC60"=>" \u064E\u0651",
+ "\uFC61"=>" \u064F\u0651",
+ "\uFC62"=>" \u0650\u0651",
+ "\uFC63"=>" \u0651\u0670",
+ "\uFC64"=>"\u0626\u0631",
+ "\uFC65"=>"\u0626\u0632",
+ "\uFC66"=>"\u0626\u0645",
+ "\uFC67"=>"\u0626\u0646",
+ "\uFC68"=>"\u0626\u0649",
+ "\uFC69"=>"\u0626\u064A",
+ "\uFC6A"=>"\u0628\u0631",
+ "\uFC6B"=>"\u0628\u0632",
+ "\uFC6C"=>"\u0628\u0645",
+ "\uFC6D"=>"\u0628\u0646",
+ "\uFC6E"=>"\u0628\u0649",
+ "\uFC6F"=>"\u0628\u064A",
+ "\uFC70"=>"\u062A\u0631",
+ "\uFC71"=>"\u062A\u0632",
+ "\uFC72"=>"\u062A\u0645",
+ "\uFC73"=>"\u062A\u0646",
+ "\uFC74"=>"\u062A\u0649",
+ "\uFC75"=>"\u062A\u064A",
+ "\uFC76"=>"\u062B\u0631",
+ "\uFC77"=>"\u062B\u0632",
+ "\uFC78"=>"\u062B\u0645",
+ "\uFC79"=>"\u062B\u0646",
+ "\uFC7A"=>"\u062B\u0649",
+ "\uFC7B"=>"\u062B\u064A",
+ "\uFC7C"=>"\u0641\u0649",
+ "\uFC7D"=>"\u0641\u064A",
+ "\uFC7E"=>"\u0642\u0649",
+ "\uFC7F"=>"\u0642\u064A",
+ "\uFC80"=>"\u0643\u0627",
+ "\uFC81"=>"\u0643\u0644",
+ "\uFC82"=>"\u0643\u0645",
+ "\uFC83"=>"\u0643\u0649",
+ "\uFC84"=>"\u0643\u064A",
+ "\uFC85"=>"\u0644\u0645",
+ "\uFC86"=>"\u0644\u0649",
+ "\uFC87"=>"\u0644\u064A",
+ "\uFC88"=>"\u0645\u0627",
+ "\uFC89"=>"\u0645\u0645",
+ "\uFC8A"=>"\u0646\u0631",
+ "\uFC8B"=>"\u0646\u0632",
+ "\uFC8C"=>"\u0646\u0645",
+ "\uFC8D"=>"\u0646\u0646",
+ "\uFC8E"=>"\u0646\u0649",
+ "\uFC8F"=>"\u0646\u064A",
+ "\uFC90"=>"\u0649\u0670",
+ "\uFC91"=>"\u064A\u0631",
+ "\uFC92"=>"\u064A\u0632",
+ "\uFC93"=>"\u064A\u0645",
+ "\uFC94"=>"\u064A\u0646",
+ "\uFC95"=>"\u064A\u0649",
+ "\uFC96"=>"\u064A\u064A",
+ "\uFC97"=>"\u0626\u062C",
+ "\uFC98"=>"\u0626\u062D",
+ "\uFC99"=>"\u0626\u062E",
+ "\uFC9A"=>"\u0626\u0645",
+ "\uFC9B"=>"\u0626\u0647",
+ "\uFC9C"=>"\u0628\u062C",
+ "\uFC9D"=>"\u0628\u062D",
+ "\uFC9E"=>"\u0628\u062E",
+ "\uFC9F"=>"\u0628\u0645",
+ "\uFCA0"=>"\u0628\u0647",
+ "\uFCA1"=>"\u062A\u062C",
+ "\uFCA2"=>"\u062A\u062D",
+ "\uFCA3"=>"\u062A\u062E",
+ "\uFCA4"=>"\u062A\u0645",
+ "\uFCA5"=>"\u062A\u0647",
+ "\uFCA6"=>"\u062B\u0645",
+ "\uFCA7"=>"\u062C\u062D",
+ "\uFCA8"=>"\u062C\u0645",
+ "\uFCA9"=>"\u062D\u062C",
+ "\uFCAA"=>"\u062D\u0645",
+ "\uFCAB"=>"\u062E\u062C",
+ "\uFCAC"=>"\u062E\u0645",
+ "\uFCAD"=>"\u0633\u062C",
+ "\uFCAE"=>"\u0633\u062D",
+ "\uFCAF"=>"\u0633\u062E",
+ "\uFCB0"=>"\u0633\u0645",
+ "\uFCB1"=>"\u0635\u062D",
+ "\uFCB2"=>"\u0635\u062E",
+ "\uFCB3"=>"\u0635\u0645",
+ "\uFCB4"=>"\u0636\u062C",
+ "\uFCB5"=>"\u0636\u062D",
+ "\uFCB6"=>"\u0636\u062E",
+ "\uFCB7"=>"\u0636\u0645",
+ "\uFCB8"=>"\u0637\u062D",
+ "\uFCB9"=>"\u0638\u0645",
+ "\uFCBA"=>"\u0639\u062C",
+ "\uFCBB"=>"\u0639\u0645",
+ "\uFCBC"=>"\u063A\u062C",
+ "\uFCBD"=>"\u063A\u0645",
+ "\uFCBE"=>"\u0641\u062C",
+ "\uFCBF"=>"\u0641\u062D",
+ "\uFCC0"=>"\u0641\u062E",
+ "\uFCC1"=>"\u0641\u0645",
+ "\uFCC2"=>"\u0642\u062D",
+ "\uFCC3"=>"\u0642\u0645",
+ "\uFCC4"=>"\u0643\u062C",
+ "\uFCC5"=>"\u0643\u062D",
+ "\uFCC6"=>"\u0643\u062E",
+ "\uFCC7"=>"\u0643\u0644",
+ "\uFCC8"=>"\u0643\u0645",
+ "\uFCC9"=>"\u0644\u062C",
+ "\uFCCA"=>"\u0644\u062D",
+ "\uFCCB"=>"\u0644\u062E",
+ "\uFCCC"=>"\u0644\u0645",
+ "\uFCCD"=>"\u0644\u0647",
+ "\uFCCE"=>"\u0645\u062C",
+ "\uFCCF"=>"\u0645\u062D",
+ "\uFCD0"=>"\u0645\u062E",
+ "\uFCD1"=>"\u0645\u0645",
+ "\uFCD2"=>"\u0646\u062C",
+ "\uFCD3"=>"\u0646\u062D",
+ "\uFCD4"=>"\u0646\u062E",
+ "\uFCD5"=>"\u0646\u0645",
+ "\uFCD6"=>"\u0646\u0647",
+ "\uFCD7"=>"\u0647\u062C",
+ "\uFCD8"=>"\u0647\u0645",
+ "\uFCD9"=>"\u0647\u0670",
+ "\uFCDA"=>"\u064A\u062C",
+ "\uFCDB"=>"\u064A\u062D",
+ "\uFCDC"=>"\u064A\u062E",
+ "\uFCDD"=>"\u064A\u0645",
+ "\uFCDE"=>"\u064A\u0647",
+ "\uFCDF"=>"\u0626\u0645",
+ "\uFCE0"=>"\u0626\u0647",
+ "\uFCE1"=>"\u0628\u0645",
+ "\uFCE2"=>"\u0628\u0647",
+ "\uFCE3"=>"\u062A\u0645",
+ "\uFCE4"=>"\u062A\u0647",
+ "\uFCE5"=>"\u062B\u0645",
+ "\uFCE6"=>"\u062B\u0647",
+ "\uFCE7"=>"\u0633\u0645",
+ "\uFCE8"=>"\u0633\u0647",
+ "\uFCE9"=>"\u0634\u0645",
+ "\uFCEA"=>"\u0634\u0647",
+ "\uFCEB"=>"\u0643\u0644",
+ "\uFCEC"=>"\u0643\u0645",
+ "\uFCED"=>"\u0644\u0645",
+ "\uFCEE"=>"\u0646\u0645",
+ "\uFCEF"=>"\u0646\u0647",
+ "\uFCF0"=>"\u064A\u0645",
+ "\uFCF1"=>"\u064A\u0647",
+ "\uFCF2"=>"\u0640\u064E\u0651",
+ "\uFCF3"=>"\u0640\u064F\u0651",
+ "\uFCF4"=>"\u0640\u0650\u0651",
+ "\uFCF5"=>"\u0637\u0649",
+ "\uFCF6"=>"\u0637\u064A",
+ "\uFCF7"=>"\u0639\u0649",
+ "\uFCF8"=>"\u0639\u064A",
+ "\uFCF9"=>"\u063A\u0649",
+ "\uFCFA"=>"\u063A\u064A",
+ "\uFCFB"=>"\u0633\u0649",
+ "\uFCFC"=>"\u0633\u064A",
+ "\uFCFD"=>"\u0634\u0649",
+ "\uFCFE"=>"\u0634\u064A",
+ "\uFCFF"=>"\u062D\u0649",
+ "\uFD00"=>"\u062D\u064A",
+ "\uFD01"=>"\u062C\u0649",
+ "\uFD02"=>"\u062C\u064A",
+ "\uFD03"=>"\u062E\u0649",
+ "\uFD04"=>"\u062E\u064A",
+ "\uFD05"=>"\u0635\u0649",
+ "\uFD06"=>"\u0635\u064A",
+ "\uFD07"=>"\u0636\u0649",
+ "\uFD08"=>"\u0636\u064A",
+ "\uFD09"=>"\u0634\u062C",
+ "\uFD0A"=>"\u0634\u062D",
+ "\uFD0B"=>"\u0634\u062E",
+ "\uFD0C"=>"\u0634\u0645",
+ "\uFD0D"=>"\u0634\u0631",
+ "\uFD0E"=>"\u0633\u0631",
+ "\uFD0F"=>"\u0635\u0631",
+ "\uFD10"=>"\u0636\u0631",
+ "\uFD11"=>"\u0637\u0649",
+ "\uFD12"=>"\u0637\u064A",
+ "\uFD13"=>"\u0639\u0649",
+ "\uFD14"=>"\u0639\u064A",
+ "\uFD15"=>"\u063A\u0649",
+ "\uFD16"=>"\u063A\u064A",
+ "\uFD17"=>"\u0633\u0649",
+ "\uFD18"=>"\u0633\u064A",
+ "\uFD19"=>"\u0634\u0649",
+ "\uFD1A"=>"\u0634\u064A",
+ "\uFD1B"=>"\u062D\u0649",
+ "\uFD1C"=>"\u062D\u064A",
+ "\uFD1D"=>"\u062C\u0649",
+ "\uFD1E"=>"\u062C\u064A",
+ "\uFD1F"=>"\u062E\u0649",
+ "\uFD20"=>"\u062E\u064A",
+ "\uFD21"=>"\u0635\u0649",
+ "\uFD22"=>"\u0635\u064A",
+ "\uFD23"=>"\u0636\u0649",
+ "\uFD24"=>"\u0636\u064A",
+ "\uFD25"=>"\u0634\u062C",
+ "\uFD26"=>"\u0634\u062D",
+ "\uFD27"=>"\u0634\u062E",
+ "\uFD28"=>"\u0634\u0645",
+ "\uFD29"=>"\u0634\u0631",
+ "\uFD2A"=>"\u0633\u0631",
+ "\uFD2B"=>"\u0635\u0631",
+ "\uFD2C"=>"\u0636\u0631",
+ "\uFD2D"=>"\u0634\u062C",
+ "\uFD2E"=>"\u0634\u062D",
+ "\uFD2F"=>"\u0634\u062E",
+ "\uFD30"=>"\u0634\u0645",
+ "\uFD31"=>"\u0633\u0647",
+ "\uFD32"=>"\u0634\u0647",
+ "\uFD33"=>"\u0637\u0645",
+ "\uFD34"=>"\u0633\u062C",
+ "\uFD35"=>"\u0633\u062D",
+ "\uFD36"=>"\u0633\u062E",
+ "\uFD37"=>"\u0634\u062C",
+ "\uFD38"=>"\u0634\u062D",
+ "\uFD39"=>"\u0634\u062E",
+ "\uFD3A"=>"\u0637\u0645",
+ "\uFD3B"=>"\u0638\u0645",
+ "\uFD3C"=>"\u0627\u064B",
+ "\uFD3D"=>"\u0627\u064B",
+ "\uFD50"=>"\u062A\u062C\u0645",
+ "\uFD51"=>"\u062A\u062D\u062C",
+ "\uFD52"=>"\u062A\u062D\u062C",
+ "\uFD53"=>"\u062A\u062D\u0645",
+ "\uFD54"=>"\u062A\u062E\u0645",
+ "\uFD55"=>"\u062A\u0645\u062C",
+ "\uFD56"=>"\u062A\u0645\u062D",
+ "\uFD57"=>"\u062A\u0645\u062E",
+ "\uFD58"=>"\u062C\u0645\u062D",
+ "\uFD59"=>"\u062C\u0645\u062D",
+ "\uFD5A"=>"\u062D\u0645\u064A",
+ "\uFD5B"=>"\u062D\u0645\u0649",
+ "\uFD5C"=>"\u0633\u062D\u062C",
+ "\uFD5D"=>"\u0633\u062C\u062D",
+ "\uFD5E"=>"\u0633\u062C\u0649",
+ "\uFD5F"=>"\u0633\u0645\u062D",
+ "\uFD60"=>"\u0633\u0645\u062D",
+ "\uFD61"=>"\u0633\u0645\u062C",
+ "\uFD62"=>"\u0633\u0645\u0645",
+ "\uFD63"=>"\u0633\u0645\u0645",
+ "\uFD64"=>"\u0635\u062D\u062D",
+ "\uFD65"=>"\u0635\u062D\u062D",
+ "\uFD66"=>"\u0635\u0645\u0645",
+ "\uFD67"=>"\u0634\u062D\u0645",
+ "\uFD68"=>"\u0634\u062D\u0645",
+ "\uFD69"=>"\u0634\u062C\u064A",
+ "\uFD6A"=>"\u0634\u0645\u062E",
+ "\uFD6B"=>"\u0634\u0645\u062E",
+ "\uFD6C"=>"\u0634\u0645\u0645",
+ "\uFD6D"=>"\u0634\u0645\u0645",
+ "\uFD6E"=>"\u0636\u062D\u0649",
+ "\uFD6F"=>"\u0636\u062E\u0645",
+ "\uFD70"=>"\u0636\u062E\u0645",
+ "\uFD71"=>"\u0637\u0645\u062D",
+ "\uFD72"=>"\u0637\u0645\u062D",
+ "\uFD73"=>"\u0637\u0645\u0645",
+ "\uFD74"=>"\u0637\u0645\u064A",
+ "\uFD75"=>"\u0639\u062C\u0645",
+ "\uFD76"=>"\u0639\u0645\u0645",
+ "\uFD77"=>"\u0639\u0645\u0645",
+ "\uFD78"=>"\u0639\u0645\u0649",
+ "\uFD79"=>"\u063A\u0645\u0645",
+ "\uFD7A"=>"\u063A\u0645\u064A",
+ "\uFD7B"=>"\u063A\u0645\u0649",
+ "\uFD7C"=>"\u0641\u062E\u0645",
+ "\uFD7D"=>"\u0641\u062E\u0645",
+ "\uFD7E"=>"\u0642\u0645\u062D",
+ "\uFD7F"=>"\u0642\u0645\u0645",
+ "\uFD80"=>"\u0644\u062D\u0645",
+ "\uFD81"=>"\u0644\u062D\u064A",
+ "\uFD82"=>"\u0644\u062D\u0649",
+ "\uFD83"=>"\u0644\u062C\u062C",
+ "\uFD84"=>"\u0644\u062C\u062C",
+ "\uFD85"=>"\u0644\u062E\u0645",
+ "\uFD86"=>"\u0644\u062E\u0645",
+ "\uFD87"=>"\u0644\u0645\u062D",
+ "\uFD88"=>"\u0644\u0645\u062D",
+ "\uFD89"=>"\u0645\u062D\u062C",
+ "\uFD8A"=>"\u0645\u062D\u0645",
+ "\uFD8B"=>"\u0645\u062D\u064A",
+ "\uFD8C"=>"\u0645\u062C\u062D",
+ "\uFD8D"=>"\u0645\u062C\u0645",
+ "\uFD8E"=>"\u0645\u062E\u062C",
+ "\uFD8F"=>"\u0645\u062E\u0645",
+ "\uFD92"=>"\u0645\u062C\u062E",
+ "\uFD93"=>"\u0647\u0645\u062C",
+ "\uFD94"=>"\u0647\u0645\u0645",
+ "\uFD95"=>"\u0646\u062D\u0645",
+ "\uFD96"=>"\u0646\u062D\u0649",
+ "\uFD97"=>"\u0646\u062C\u0645",
+ "\uFD98"=>"\u0646\u062C\u0645",
+ "\uFD99"=>"\u0646\u062C\u0649",
+ "\uFD9A"=>"\u0646\u0645\u064A",
+ "\uFD9B"=>"\u0646\u0645\u0649",
+ "\uFD9C"=>"\u064A\u0645\u0645",
+ "\uFD9D"=>"\u064A\u0645\u0645",
+ "\uFD9E"=>"\u0628\u062E\u064A",
+ "\uFD9F"=>"\u062A\u062C\u064A",
+ "\uFDA0"=>"\u062A\u062C\u0649",
+ "\uFDA1"=>"\u062A\u062E\u064A",
+ "\uFDA2"=>"\u062A\u062E\u0649",
+ "\uFDA3"=>"\u062A\u0645\u064A",
+ "\uFDA4"=>"\u062A\u0645\u0649",
+ "\uFDA5"=>"\u062C\u0645\u064A",
+ "\uFDA6"=>"\u062C\u062D\u0649",
+ "\uFDA7"=>"\u062C\u0645\u0649",
+ "\uFDA8"=>"\u0633\u062E\u0649",
+ "\uFDA9"=>"\u0635\u062D\u064A",
+ "\uFDAA"=>"\u0634\u062D\u064A",
+ "\uFDAB"=>"\u0636\u062D\u064A",
+ "\uFDAC"=>"\u0644\u062C\u064A",
+ "\uFDAD"=>"\u0644\u0645\u064A",
+ "\uFDAE"=>"\u064A\u062D\u064A",
+ "\uFDAF"=>"\u064A\u062C\u064A",
+ "\uFDB0"=>"\u064A\u0645\u064A",
+ "\uFDB1"=>"\u0645\u0645\u064A",
+ "\uFDB2"=>"\u0642\u0645\u064A",
+ "\uFDB3"=>"\u0646\u062D\u064A",
+ "\uFDB4"=>"\u0642\u0645\u062D",
+ "\uFDB5"=>"\u0644\u062D\u0645",
+ "\uFDB6"=>"\u0639\u0645\u064A",
+ "\uFDB7"=>"\u0643\u0645\u064A",
+ "\uFDB8"=>"\u0646\u062C\u062D",
+ "\uFDB9"=>"\u0645\u062E\u064A",
+ "\uFDBA"=>"\u0644\u062C\u0645",
+ "\uFDBB"=>"\u0643\u0645\u0645",
+ "\uFDBC"=>"\u0644\u062C\u0645",
+ "\uFDBD"=>"\u0646\u062C\u062D",
+ "\uFDBE"=>"\u062C\u062D\u064A",
+ "\uFDBF"=>"\u062D\u062C\u064A",
+ "\uFDC0"=>"\u0645\u062C\u064A",
+ "\uFDC1"=>"\u0641\u0645\u064A",
+ "\uFDC2"=>"\u0628\u062D\u064A",
+ "\uFDC3"=>"\u0643\u0645\u0645",
+ "\uFDC4"=>"\u0639\u062C\u0645",
+ "\uFDC5"=>"\u0635\u0645\u0645",
+ "\uFDC6"=>"\u0633\u062E\u064A",
+ "\uFDC7"=>"\u0646\u062C\u064A",
+ "\uFDF0"=>"\u0635\u0644\u06D2",
+ "\uFDF1"=>"\u0642\u0644\u06D2",
+ "\uFDF2"=>"\u0627\u0644\u0644\u0647",
+ "\uFDF3"=>"\u0627\u0643\u0628\u0631",
+ "\uFDF4"=>"\u0645\u062D\u0645\u062F",
+ "\uFDF5"=>"\u0635\u0644\u0639\u0645",
+ "\uFDF6"=>"\u0631\u0633\u0648\u0644",
+ "\uFDF7"=>"\u0639\u0644\u064A\u0647",
+ "\uFDF8"=>"\u0648\u0633\u0644\u0645",
+ "\uFDF9"=>"\u0635\u0644\u0649",
+ "\uFDFA"=>"\u0635\u0644\u0649 \u0627\u0644\u0644\u0647 \u0639\u0644\u064A\u0647 \u0648\u0633\u0644\u0645",
+ "\uFDFB"=>"\u062C\u0644 \u062C\u0644\u0627\u0644\u0647",
+ "\uFDFC"=>"\u0631\u06CC\u0627\u0644",
+ "\uFE10"=>",",
+ "\uFE11"=>"\u3001",
+ "\uFE12"=>"\u3002",
+ "\uFE13"=>":",
+ "\uFE14"=>";",
+ "\uFE15"=>"!",
+ "\uFE16"=>"?",
+ "\uFE17"=>"\u3016",
+ "\uFE18"=>"\u3017",
+ "\uFE19"=>"...",
+ "\uFE30"=>"..",
+ "\uFE31"=>"\u2014",
+ "\uFE32"=>"\u2013",
+ "\uFE33"=>"_",
+ "\uFE34"=>"_",
+ "\uFE35"=>"(",
+ "\uFE36"=>")",
+ "\uFE37"=>"{",
+ "\uFE38"=>"}",
+ "\uFE39"=>"\u3014",
+ "\uFE3A"=>"\u3015",
+ "\uFE3B"=>"\u3010",
+ "\uFE3C"=>"\u3011",
+ "\uFE3D"=>"\u300A",
+ "\uFE3E"=>"\u300B",
+ "\uFE3F"=>"\u3008",
+ "\uFE40"=>"\u3009",
+ "\uFE41"=>"\u300C",
+ "\uFE42"=>"\u300D",
+ "\uFE43"=>"\u300E",
+ "\uFE44"=>"\u300F",
+ "\uFE47"=>"[",
+ "\uFE48"=>"]",
+ "\uFE49"=>" \u0305",
+ "\uFE4A"=>" \u0305",
+ "\uFE4B"=>" \u0305",
+ "\uFE4C"=>" \u0305",
+ "\uFE4D"=>"_",
+ "\uFE4E"=>"_",
+ "\uFE4F"=>"_",
+ "\uFE50"=>",",
+ "\uFE51"=>"\u3001",
+ "\uFE52"=>".",
+ "\uFE54"=>";",
+ "\uFE55"=>":",
+ "\uFE56"=>"?",
+ "\uFE57"=>"!",
+ "\uFE58"=>"\u2014",
+ "\uFE59"=>"(",
+ "\uFE5A"=>")",
+ "\uFE5B"=>"{",
+ "\uFE5C"=>"}",
+ "\uFE5D"=>"\u3014",
+ "\uFE5E"=>"\u3015",
+ "\uFE5F"=>"#",
+ "\uFE60"=>"&",
+ "\uFE61"=>"*",
+ "\uFE62"=>"+",
+ "\uFE63"=>"-",
+ "\uFE64"=>"<",
+ "\uFE65"=>">",
+ "\uFE66"=>"=",
+ "\uFE68"=>"\\",
+ "\uFE69"=>"$",
+ "\uFE6A"=>"%",
+ "\uFE6B"=>"@",
+ "\uFE70"=>" \u064B",
+ "\uFE71"=>"\u0640\u064B",
+ "\uFE72"=>" \u064C",
+ "\uFE74"=>" \u064D",
+ "\uFE76"=>" \u064E",
+ "\uFE77"=>"\u0640\u064E",
+ "\uFE78"=>" \u064F",
+ "\uFE79"=>"\u0640\u064F",
+ "\uFE7A"=>" \u0650",
+ "\uFE7B"=>"\u0640\u0650",
+ "\uFE7C"=>" \u0651",
+ "\uFE7D"=>"\u0640\u0651",
+ "\uFE7E"=>" \u0652",
+ "\uFE7F"=>"\u0640\u0652",
+ "\uFE80"=>"\u0621",
+ "\uFE81"=>"\u0622",
+ "\uFE82"=>"\u0622",
+ "\uFE83"=>"\u0623",
+ "\uFE84"=>"\u0623",
+ "\uFE85"=>"\u0624",
+ "\uFE86"=>"\u0624",
+ "\uFE87"=>"\u0625",
+ "\uFE88"=>"\u0625",
+ "\uFE89"=>"\u0626",
+ "\uFE8A"=>"\u0626",
+ "\uFE8B"=>"\u0626",
+ "\uFE8C"=>"\u0626",
+ "\uFE8D"=>"\u0627",
+ "\uFE8E"=>"\u0627",
+ "\uFE8F"=>"\u0628",
+ "\uFE90"=>"\u0628",
+ "\uFE91"=>"\u0628",
+ "\uFE92"=>"\u0628",
+ "\uFE93"=>"\u0629",
+ "\uFE94"=>"\u0629",
+ "\uFE95"=>"\u062A",
+ "\uFE96"=>"\u062A",
+ "\uFE97"=>"\u062A",
+ "\uFE98"=>"\u062A",
+ "\uFE99"=>"\u062B",
+ "\uFE9A"=>"\u062B",
+ "\uFE9B"=>"\u062B",
+ "\uFE9C"=>"\u062B",
+ "\uFE9D"=>"\u062C",
+ "\uFE9E"=>"\u062C",
+ "\uFE9F"=>"\u062C",
+ "\uFEA0"=>"\u062C",
+ "\uFEA1"=>"\u062D",
+ "\uFEA2"=>"\u062D",
+ "\uFEA3"=>"\u062D",
+ "\uFEA4"=>"\u062D",
+ "\uFEA5"=>"\u062E",
+ "\uFEA6"=>"\u062E",
+ "\uFEA7"=>"\u062E",
+ "\uFEA8"=>"\u062E",
+ "\uFEA9"=>"\u062F",
+ "\uFEAA"=>"\u062F",
+ "\uFEAB"=>"\u0630",
+ "\uFEAC"=>"\u0630",
+ "\uFEAD"=>"\u0631",
+ "\uFEAE"=>"\u0631",
+ "\uFEAF"=>"\u0632",
+ "\uFEB0"=>"\u0632",
+ "\uFEB1"=>"\u0633",
+ "\uFEB2"=>"\u0633",
+ "\uFEB3"=>"\u0633",
+ "\uFEB4"=>"\u0633",
+ "\uFEB5"=>"\u0634",
+ "\uFEB6"=>"\u0634",
+ "\uFEB7"=>"\u0634",
+ "\uFEB8"=>"\u0634",
+ "\uFEB9"=>"\u0635",
+ "\uFEBA"=>"\u0635",
+ "\uFEBB"=>"\u0635",
+ "\uFEBC"=>"\u0635",
+ "\uFEBD"=>"\u0636",
+ "\uFEBE"=>"\u0636",
+ "\uFEBF"=>"\u0636",
+ "\uFEC0"=>"\u0636",
+ "\uFEC1"=>"\u0637",
+ "\uFEC2"=>"\u0637",
+ "\uFEC3"=>"\u0637",
+ "\uFEC4"=>"\u0637",
+ "\uFEC5"=>"\u0638",
+ "\uFEC6"=>"\u0638",
+ "\uFEC7"=>"\u0638",
+ "\uFEC8"=>"\u0638",
+ "\uFEC9"=>"\u0639",
+ "\uFECA"=>"\u0639",
+ "\uFECB"=>"\u0639",
+ "\uFECC"=>"\u0639",
+ "\uFECD"=>"\u063A",
+ "\uFECE"=>"\u063A",
+ "\uFECF"=>"\u063A",
+ "\uFED0"=>"\u063A",
+ "\uFED1"=>"\u0641",
+ "\uFED2"=>"\u0641",
+ "\uFED3"=>"\u0641",
+ "\uFED4"=>"\u0641",
+ "\uFED5"=>"\u0642",
+ "\uFED6"=>"\u0642",
+ "\uFED7"=>"\u0642",
+ "\uFED8"=>"\u0642",
+ "\uFED9"=>"\u0643",
+ "\uFEDA"=>"\u0643",
+ "\uFEDB"=>"\u0643",
+ "\uFEDC"=>"\u0643",
+ "\uFEDD"=>"\u0644",
+ "\uFEDE"=>"\u0644",
+ "\uFEDF"=>"\u0644",
+ "\uFEE0"=>"\u0644",
+ "\uFEE1"=>"\u0645",
+ "\uFEE2"=>"\u0645",
+ "\uFEE3"=>"\u0645",
+ "\uFEE4"=>"\u0645",
+ "\uFEE5"=>"\u0646",
+ "\uFEE6"=>"\u0646",
+ "\uFEE7"=>"\u0646",
+ "\uFEE8"=>"\u0646",
+ "\uFEE9"=>"\u0647",
+ "\uFEEA"=>"\u0647",
+ "\uFEEB"=>"\u0647",
+ "\uFEEC"=>"\u0647",
+ "\uFEED"=>"\u0648",
+ "\uFEEE"=>"\u0648",
+ "\uFEEF"=>"\u0649",
+ "\uFEF0"=>"\u0649",
+ "\uFEF1"=>"\u064A",
+ "\uFEF2"=>"\u064A",
+ "\uFEF3"=>"\u064A",
+ "\uFEF4"=>"\u064A",
+ "\uFEF5"=>"\u0644\u0622",
+ "\uFEF6"=>"\u0644\u0622",
+ "\uFEF7"=>"\u0644\u0623",
+ "\uFEF8"=>"\u0644\u0623",
+ "\uFEF9"=>"\u0644\u0625",
+ "\uFEFA"=>"\u0644\u0625",
+ "\uFEFB"=>"\u0644\u0627",
+ "\uFEFC"=>"\u0644\u0627",
+ "\uFF01"=>"!",
+ "\uFF02"=>"\"",
+ "\uFF03"=>"#",
+ "\uFF04"=>"$",
+ "\uFF05"=>"%",
+ "\uFF06"=>"&",
+ "\uFF07"=>"'",
+ "\uFF08"=>"(",
+ "\uFF09"=>")",
+ "\uFF0A"=>"*",
+ "\uFF0B"=>"+",
+ "\uFF0C"=>",",
+ "\uFF0D"=>"-",
+ "\uFF0E"=>".",
+ "\uFF0F"=>"/",
+ "\uFF10"=>"0",
+ "\uFF11"=>"1",
+ "\uFF12"=>"2",
+ "\uFF13"=>"3",
+ "\uFF14"=>"4",
+ "\uFF15"=>"5",
+ "\uFF16"=>"6",
+ "\uFF17"=>"7",
+ "\uFF18"=>"8",
+ "\uFF19"=>"9",
+ "\uFF1A"=>":",
+ "\uFF1B"=>";",
+ "\uFF1C"=>"<",
+ "\uFF1D"=>"=",
+ "\uFF1E"=>">",
+ "\uFF1F"=>"?",
+ "\uFF20"=>"@",
+ "\uFF21"=>"A",
+ "\uFF22"=>"B",
+ "\uFF23"=>"C",
+ "\uFF24"=>"D",
+ "\uFF25"=>"E",
+ "\uFF26"=>"F",
+ "\uFF27"=>"G",
+ "\uFF28"=>"H",
+ "\uFF29"=>"I",
+ "\uFF2A"=>"J",
+ "\uFF2B"=>"K",
+ "\uFF2C"=>"L",
+ "\uFF2D"=>"M",
+ "\uFF2E"=>"N",
+ "\uFF2F"=>"O",
+ "\uFF30"=>"P",
+ "\uFF31"=>"Q",
+ "\uFF32"=>"R",
+ "\uFF33"=>"S",
+ "\uFF34"=>"T",
+ "\uFF35"=>"U",
+ "\uFF36"=>"V",
+ "\uFF37"=>"W",
+ "\uFF38"=>"X",
+ "\uFF39"=>"Y",
+ "\uFF3A"=>"Z",
+ "\uFF3B"=>"[",
+ "\uFF3C"=>"\\",
+ "\uFF3D"=>"]",
+ "\uFF3E"=>"^",
+ "\uFF3F"=>"_",
+ "\uFF40"=>"`",
+ "\uFF41"=>"a",
+ "\uFF42"=>"b",
+ "\uFF43"=>"c",
+ "\uFF44"=>"d",
+ "\uFF45"=>"e",
+ "\uFF46"=>"f",
+ "\uFF47"=>"g",
+ "\uFF48"=>"h",
+ "\uFF49"=>"i",
+ "\uFF4A"=>"j",
+ "\uFF4B"=>"k",
+ "\uFF4C"=>"l",
+ "\uFF4D"=>"m",
+ "\uFF4E"=>"n",
+ "\uFF4F"=>"o",
+ "\uFF50"=>"p",
+ "\uFF51"=>"q",
+ "\uFF52"=>"r",
+ "\uFF53"=>"s",
+ "\uFF54"=>"t",
+ "\uFF55"=>"u",
+ "\uFF56"=>"v",
+ "\uFF57"=>"w",
+ "\uFF58"=>"x",
+ "\uFF59"=>"y",
+ "\uFF5A"=>"z",
+ "\uFF5B"=>"{",
+ "\uFF5C"=>"|",
+ "\uFF5D"=>"}",
+ "\uFF5E"=>"~",
+ "\uFF5F"=>"\u2985",
+ "\uFF60"=>"\u2986",
+ "\uFF61"=>"\u3002",
+ "\uFF62"=>"\u300C",
+ "\uFF63"=>"\u300D",
+ "\uFF64"=>"\u3001",
+ "\uFF65"=>"\u30FB",
+ "\uFF66"=>"\u30F2",
+ "\uFF67"=>"\u30A1",
+ "\uFF68"=>"\u30A3",
+ "\uFF69"=>"\u30A5",
+ "\uFF6A"=>"\u30A7",
+ "\uFF6B"=>"\u30A9",
+ "\uFF6C"=>"\u30E3",
+ "\uFF6D"=>"\u30E5",
+ "\uFF6E"=>"\u30E7",
+ "\uFF6F"=>"\u30C3",
+ "\uFF70"=>"\u30FC",
+ "\uFF71"=>"\u30A2",
+ "\uFF72"=>"\u30A4",
+ "\uFF73"=>"\u30A6",
+ "\uFF74"=>"\u30A8",
+ "\uFF75"=>"\u30AA",
+ "\uFF76"=>"\u30AB",
+ "\uFF77"=>"\u30AD",
+ "\uFF78"=>"\u30AF",
+ "\uFF79"=>"\u30B1",
+ "\uFF7A"=>"\u30B3",
+ "\uFF7B"=>"\u30B5",
+ "\uFF7C"=>"\u30B7",
+ "\uFF7D"=>"\u30B9",
+ "\uFF7E"=>"\u30BB",
+ "\uFF7F"=>"\u30BD",
+ "\uFF80"=>"\u30BF",
+ "\uFF81"=>"\u30C1",
+ "\uFF82"=>"\u30C4",
+ "\uFF83"=>"\u30C6",
+ "\uFF84"=>"\u30C8",
+ "\uFF85"=>"\u30CA",
+ "\uFF86"=>"\u30CB",
+ "\uFF87"=>"\u30CC",
+ "\uFF88"=>"\u30CD",
+ "\uFF89"=>"\u30CE",
+ "\uFF8A"=>"\u30CF",
+ "\uFF8B"=>"\u30D2",
+ "\uFF8C"=>"\u30D5",
+ "\uFF8D"=>"\u30D8",
+ "\uFF8E"=>"\u30DB",
+ "\uFF8F"=>"\u30DE",
+ "\uFF90"=>"\u30DF",
+ "\uFF91"=>"\u30E0",
+ "\uFF92"=>"\u30E1",
+ "\uFF93"=>"\u30E2",
+ "\uFF94"=>"\u30E4",
+ "\uFF95"=>"\u30E6",
+ "\uFF96"=>"\u30E8",
+ "\uFF97"=>"\u30E9",
+ "\uFF98"=>"\u30EA",
+ "\uFF99"=>"\u30EB",
+ "\uFF9A"=>"\u30EC",
+ "\uFF9B"=>"\u30ED",
+ "\uFF9C"=>"\u30EF",
+ "\uFF9D"=>"\u30F3",
+ "\uFF9E"=>"\u3099",
+ "\uFF9F"=>"\u309A",
+ "\uFFA0"=>"\u1160",
+ "\uFFA1"=>"\u1100",
+ "\uFFA2"=>"\u1101",
+ "\uFFA3"=>"\u11AA",
+ "\uFFA4"=>"\u1102",
+ "\uFFA5"=>"\u11AC",
+ "\uFFA6"=>"\u11AD",
+ "\uFFA7"=>"\u1103",
+ "\uFFA8"=>"\u1104",
+ "\uFFA9"=>"\u1105",
+ "\uFFAA"=>"\u11B0",
+ "\uFFAB"=>"\u11B1",
+ "\uFFAC"=>"\u11B2",
+ "\uFFAD"=>"\u11B3",
+ "\uFFAE"=>"\u11B4",
+ "\uFFAF"=>"\u11B5",
+ "\uFFB0"=>"\u111A",
+ "\uFFB1"=>"\u1106",
+ "\uFFB2"=>"\u1107",
+ "\uFFB3"=>"\u1108",
+ "\uFFB4"=>"\u1121",
+ "\uFFB5"=>"\u1109",
+ "\uFFB6"=>"\u110A",
+ "\uFFB7"=>"\u110B",
+ "\uFFB8"=>"\u110C",
+ "\uFFB9"=>"\u110D",
+ "\uFFBA"=>"\u110E",
+ "\uFFBB"=>"\u110F",
+ "\uFFBC"=>"\u1110",
+ "\uFFBD"=>"\u1111",
+ "\uFFBE"=>"\u1112",
+ "\uFFC2"=>"\u1161",
+ "\uFFC3"=>"\u1162",
+ "\uFFC4"=>"\u1163",
+ "\uFFC5"=>"\u1164",
+ "\uFFC6"=>"\u1165",
+ "\uFFC7"=>"\u1166",
+ "\uFFCA"=>"\u1167",
+ "\uFFCB"=>"\u1168",
+ "\uFFCC"=>"\u1169",
+ "\uFFCD"=>"\u116A",
+ "\uFFCE"=>"\u116B",
+ "\uFFCF"=>"\u116C",
+ "\uFFD2"=>"\u116D",
+ "\uFFD3"=>"\u116E",
+ "\uFFD4"=>"\u116F",
+ "\uFFD5"=>"\u1170",
+ "\uFFD6"=>"\u1171",
+ "\uFFD7"=>"\u1172",
+ "\uFFDA"=>"\u1173",
+ "\uFFDB"=>"\u1174",
+ "\uFFDC"=>"\u1175",
+ "\uFFE0"=>"\u00A2",
+ "\uFFE1"=>"\u00A3",
+ "\uFFE2"=>"\u00AC",
+ "\uFFE3"=>" \u0304",
+ "\uFFE4"=>"\u00A6",
+ "\uFFE5"=>"\u00A5",
+ "\uFFE6"=>"\u20A9",
+ "\uFFE8"=>"\u2502",
+ "\uFFE9"=>"\u2190",
+ "\uFFEA"=>"\u2191",
+ "\uFFEB"=>"\u2192",
+ "\uFFEC"=>"\u2193",
+ "\uFFED"=>"\u25A0",
+ "\uFFEE"=>"\u25CB",
+ "\u{1D400}"=>"A",
+ "\u{1D401}"=>"B",
+ "\u{1D402}"=>"C",
+ "\u{1D403}"=>"D",
+ "\u{1D404}"=>"E",
+ "\u{1D405}"=>"F",
+ "\u{1D406}"=>"G",
+ "\u{1D407}"=>"H",
+ "\u{1D408}"=>"I",
+ "\u{1D409}"=>"J",
+ "\u{1D40A}"=>"K",
+ "\u{1D40B}"=>"L",
+ "\u{1D40C}"=>"M",
+ "\u{1D40D}"=>"N",
+ "\u{1D40E}"=>"O",
+ "\u{1D40F}"=>"P",
+ "\u{1D410}"=>"Q",
+ "\u{1D411}"=>"R",
+ "\u{1D412}"=>"S",
+ "\u{1D413}"=>"T",
+ "\u{1D414}"=>"U",
+ "\u{1D415}"=>"V",
+ "\u{1D416}"=>"W",
+ "\u{1D417}"=>"X",
+ "\u{1D418}"=>"Y",
+ "\u{1D419}"=>"Z",
+ "\u{1D41A}"=>"a",
+ "\u{1D41B}"=>"b",
+ "\u{1D41C}"=>"c",
+ "\u{1D41D}"=>"d",
+ "\u{1D41E}"=>"e",
+ "\u{1D41F}"=>"f",
+ "\u{1D420}"=>"g",
+ "\u{1D421}"=>"h",
+ "\u{1D422}"=>"i",
+ "\u{1D423}"=>"j",
+ "\u{1D424}"=>"k",
+ "\u{1D425}"=>"l",
+ "\u{1D426}"=>"m",
+ "\u{1D427}"=>"n",
+ "\u{1D428}"=>"o",
+ "\u{1D429}"=>"p",
+ "\u{1D42A}"=>"q",
+ "\u{1D42B}"=>"r",
+ "\u{1D42C}"=>"s",
+ "\u{1D42D}"=>"t",
+ "\u{1D42E}"=>"u",
+ "\u{1D42F}"=>"v",
+ "\u{1D430}"=>"w",
+ "\u{1D431}"=>"x",
+ "\u{1D432}"=>"y",
+ "\u{1D433}"=>"z",
+ "\u{1D434}"=>"A",
+ "\u{1D435}"=>"B",
+ "\u{1D436}"=>"C",
+ "\u{1D437}"=>"D",
+ "\u{1D438}"=>"E",
+ "\u{1D439}"=>"F",
+ "\u{1D43A}"=>"G",
+ "\u{1D43B}"=>"H",
+ "\u{1D43C}"=>"I",
+ "\u{1D43D}"=>"J",
+ "\u{1D43E}"=>"K",
+ "\u{1D43F}"=>"L",
+ "\u{1D440}"=>"M",
+ "\u{1D441}"=>"N",
+ "\u{1D442}"=>"O",
+ "\u{1D443}"=>"P",
+ "\u{1D444}"=>"Q",
+ "\u{1D445}"=>"R",
+ "\u{1D446}"=>"S",
+ "\u{1D447}"=>"T",
+ "\u{1D448}"=>"U",
+ "\u{1D449}"=>"V",
+ "\u{1D44A}"=>"W",
+ "\u{1D44B}"=>"X",
+ "\u{1D44C}"=>"Y",
+ "\u{1D44D}"=>"Z",
+ "\u{1D44E}"=>"a",
+ "\u{1D44F}"=>"b",
+ "\u{1D450}"=>"c",
+ "\u{1D451}"=>"d",
+ "\u{1D452}"=>"e",
+ "\u{1D453}"=>"f",
+ "\u{1D454}"=>"g",
+ "\u{1D456}"=>"i",
+ "\u{1D457}"=>"j",
+ "\u{1D458}"=>"k",
+ "\u{1D459}"=>"l",
+ "\u{1D45A}"=>"m",
+ "\u{1D45B}"=>"n",
+ "\u{1D45C}"=>"o",
+ "\u{1D45D}"=>"p",
+ "\u{1D45E}"=>"q",
+ "\u{1D45F}"=>"r",
+ "\u{1D460}"=>"s",
+ "\u{1D461}"=>"t",
+ "\u{1D462}"=>"u",
+ "\u{1D463}"=>"v",
+ "\u{1D464}"=>"w",
+ "\u{1D465}"=>"x",
+ "\u{1D466}"=>"y",
+ "\u{1D467}"=>"z",
+ "\u{1D468}"=>"A",
+ "\u{1D469}"=>"B",
+ "\u{1D46A}"=>"C",
+ "\u{1D46B}"=>"D",
+ "\u{1D46C}"=>"E",
+ "\u{1D46D}"=>"F",
+ "\u{1D46E}"=>"G",
+ "\u{1D46F}"=>"H",
+ "\u{1D470}"=>"I",
+ "\u{1D471}"=>"J",
+ "\u{1D472}"=>"K",
+ "\u{1D473}"=>"L",
+ "\u{1D474}"=>"M",
+ "\u{1D475}"=>"N",
+ "\u{1D476}"=>"O",
+ "\u{1D477}"=>"P",
+ "\u{1D478}"=>"Q",
+ "\u{1D479}"=>"R",
+ "\u{1D47A}"=>"S",
+ "\u{1D47B}"=>"T",
+ "\u{1D47C}"=>"U",
+ "\u{1D47D}"=>"V",
+ "\u{1D47E}"=>"W",
+ "\u{1D47F}"=>"X",
+ "\u{1D480}"=>"Y",
+ "\u{1D481}"=>"Z",
+ "\u{1D482}"=>"a",
+ "\u{1D483}"=>"b",
+ "\u{1D484}"=>"c",
+ "\u{1D485}"=>"d",
+ "\u{1D486}"=>"e",
+ "\u{1D487}"=>"f",
+ "\u{1D488}"=>"g",
+ "\u{1D489}"=>"h",
+ "\u{1D48A}"=>"i",
+ "\u{1D48B}"=>"j",
+ "\u{1D48C}"=>"k",
+ "\u{1D48D}"=>"l",
+ "\u{1D48E}"=>"m",
+ "\u{1D48F}"=>"n",
+ "\u{1D490}"=>"o",
+ "\u{1D491}"=>"p",
+ "\u{1D492}"=>"q",
+ "\u{1D493}"=>"r",
+ "\u{1D494}"=>"s",
+ "\u{1D495}"=>"t",
+ "\u{1D496}"=>"u",
+ "\u{1D497}"=>"v",
+ "\u{1D498}"=>"w",
+ "\u{1D499}"=>"x",
+ "\u{1D49A}"=>"y",
+ "\u{1D49B}"=>"z",
+ "\u{1D49C}"=>"A",
+ "\u{1D49E}"=>"C",
+ "\u{1D49F}"=>"D",
+ "\u{1D4A2}"=>"G",
+ "\u{1D4A5}"=>"J",
+ "\u{1D4A6}"=>"K",
+ "\u{1D4A9}"=>"N",
+ "\u{1D4AA}"=>"O",
+ "\u{1D4AB}"=>"P",
+ "\u{1D4AC}"=>"Q",
+ "\u{1D4AE}"=>"S",
+ "\u{1D4AF}"=>"T",
+ "\u{1D4B0}"=>"U",
+ "\u{1D4B1}"=>"V",
+ "\u{1D4B2}"=>"W",
+ "\u{1D4B3}"=>"X",
+ "\u{1D4B4}"=>"Y",
+ "\u{1D4B5}"=>"Z",
+ "\u{1D4B6}"=>"a",
+ "\u{1D4B7}"=>"b",
+ "\u{1D4B8}"=>"c",
+ "\u{1D4B9}"=>"d",
+ "\u{1D4BB}"=>"f",
+ "\u{1D4BD}"=>"h",
+ "\u{1D4BE}"=>"i",
+ "\u{1D4BF}"=>"j",
+ "\u{1D4C0}"=>"k",
+ "\u{1D4C1}"=>"l",
+ "\u{1D4C2}"=>"m",
+ "\u{1D4C3}"=>"n",
+ "\u{1D4C5}"=>"p",
+ "\u{1D4C6}"=>"q",
+ "\u{1D4C7}"=>"r",
+ "\u{1D4C8}"=>"s",
+ "\u{1D4C9}"=>"t",
+ "\u{1D4CA}"=>"u",
+ "\u{1D4CB}"=>"v",
+ "\u{1D4CC}"=>"w",
+ "\u{1D4CD}"=>"x",
+ "\u{1D4CE}"=>"y",
+ "\u{1D4CF}"=>"z",
+ "\u{1D4D0}"=>"A",
+ "\u{1D4D1}"=>"B",
+ "\u{1D4D2}"=>"C",
+ "\u{1D4D3}"=>"D",
+ "\u{1D4D4}"=>"E",
+ "\u{1D4D5}"=>"F",
+ "\u{1D4D6}"=>"G",
+ "\u{1D4D7}"=>"H",
+ "\u{1D4D8}"=>"I",
+ "\u{1D4D9}"=>"J",
+ "\u{1D4DA}"=>"K",
+ "\u{1D4DB}"=>"L",
+ "\u{1D4DC}"=>"M",
+ "\u{1D4DD}"=>"N",
+ "\u{1D4DE}"=>"O",
+ "\u{1D4DF}"=>"P",
+ "\u{1D4E0}"=>"Q",
+ "\u{1D4E1}"=>"R",
+ "\u{1D4E2}"=>"S",
+ "\u{1D4E3}"=>"T",
+ "\u{1D4E4}"=>"U",
+ "\u{1D4E5}"=>"V",
+ "\u{1D4E6}"=>"W",
+ "\u{1D4E7}"=>"X",
+ "\u{1D4E8}"=>"Y",
+ "\u{1D4E9}"=>"Z",
+ "\u{1D4EA}"=>"a",
+ "\u{1D4EB}"=>"b",
+ "\u{1D4EC}"=>"c",
+ "\u{1D4ED}"=>"d",
+ "\u{1D4EE}"=>"e",
+ "\u{1D4EF}"=>"f",
+ "\u{1D4F0}"=>"g",
+ "\u{1D4F1}"=>"h",
+ "\u{1D4F2}"=>"i",
+ "\u{1D4F3}"=>"j",
+ "\u{1D4F4}"=>"k",
+ "\u{1D4F5}"=>"l",
+ "\u{1D4F6}"=>"m",
+ "\u{1D4F7}"=>"n",
+ "\u{1D4F8}"=>"o",
+ "\u{1D4F9}"=>"p",
+ "\u{1D4FA}"=>"q",
+ "\u{1D4FB}"=>"r",
+ "\u{1D4FC}"=>"s",
+ "\u{1D4FD}"=>"t",
+ "\u{1D4FE}"=>"u",
+ "\u{1D4FF}"=>"v",
+ "\u{1D500}"=>"w",
+ "\u{1D501}"=>"x",
+ "\u{1D502}"=>"y",
+ "\u{1D503}"=>"z",
+ "\u{1D504}"=>"A",
+ "\u{1D505}"=>"B",
+ "\u{1D507}"=>"D",
+ "\u{1D508}"=>"E",
+ "\u{1D509}"=>"F",
+ "\u{1D50A}"=>"G",
+ "\u{1D50D}"=>"J",
+ "\u{1D50E}"=>"K",
+ "\u{1D50F}"=>"L",
+ "\u{1D510}"=>"M",
+ "\u{1D511}"=>"N",
+ "\u{1D512}"=>"O",
+ "\u{1D513}"=>"P",
+ "\u{1D514}"=>"Q",
+ "\u{1D516}"=>"S",
+ "\u{1D517}"=>"T",
+ "\u{1D518}"=>"U",
+ "\u{1D519}"=>"V",
+ "\u{1D51A}"=>"W",
+ "\u{1D51B}"=>"X",
+ "\u{1D51C}"=>"Y",
+ "\u{1D51E}"=>"a",
+ "\u{1D51F}"=>"b",
+ "\u{1D520}"=>"c",
+ "\u{1D521}"=>"d",
+ "\u{1D522}"=>"e",
+ "\u{1D523}"=>"f",
+ "\u{1D524}"=>"g",
+ "\u{1D525}"=>"h",
+ "\u{1D526}"=>"i",
+ "\u{1D527}"=>"j",
+ "\u{1D528}"=>"k",
+ "\u{1D529}"=>"l",
+ "\u{1D52A}"=>"m",
+ "\u{1D52B}"=>"n",
+ "\u{1D52C}"=>"o",
+ "\u{1D52D}"=>"p",
+ "\u{1D52E}"=>"q",
+ "\u{1D52F}"=>"r",
+ "\u{1D530}"=>"s",
+ "\u{1D531}"=>"t",
+ "\u{1D532}"=>"u",
+ "\u{1D533}"=>"v",
+ "\u{1D534}"=>"w",
+ "\u{1D535}"=>"x",
+ "\u{1D536}"=>"y",
+ "\u{1D537}"=>"z",
+ "\u{1D538}"=>"A",
+ "\u{1D539}"=>"B",
+ "\u{1D53B}"=>"D",
+ "\u{1D53C}"=>"E",
+ "\u{1D53D}"=>"F",
+ "\u{1D53E}"=>"G",
+ "\u{1D540}"=>"I",
+ "\u{1D541}"=>"J",
+ "\u{1D542}"=>"K",
+ "\u{1D543}"=>"L",
+ "\u{1D544}"=>"M",
+ "\u{1D546}"=>"O",
+ "\u{1D54A}"=>"S",
+ "\u{1D54B}"=>"T",
+ "\u{1D54C}"=>"U",
+ "\u{1D54D}"=>"V",
+ "\u{1D54E}"=>"W",
+ "\u{1D54F}"=>"X",
+ "\u{1D550}"=>"Y",
+ "\u{1D552}"=>"a",
+ "\u{1D553}"=>"b",
+ "\u{1D554}"=>"c",
+ "\u{1D555}"=>"d",
+ "\u{1D556}"=>"e",
+ "\u{1D557}"=>"f",
+ "\u{1D558}"=>"g",
+ "\u{1D559}"=>"h",
+ "\u{1D55A}"=>"i",
+ "\u{1D55B}"=>"j",
+ "\u{1D55C}"=>"k",
+ "\u{1D55D}"=>"l",
+ "\u{1D55E}"=>"m",
+ "\u{1D55F}"=>"n",
+ "\u{1D560}"=>"o",
+ "\u{1D561}"=>"p",
+ "\u{1D562}"=>"q",
+ "\u{1D563}"=>"r",
+ "\u{1D564}"=>"s",
+ "\u{1D565}"=>"t",
+ "\u{1D566}"=>"u",
+ "\u{1D567}"=>"v",
+ "\u{1D568}"=>"w",
+ "\u{1D569}"=>"x",
+ "\u{1D56A}"=>"y",
+ "\u{1D56B}"=>"z",
+ "\u{1D56C}"=>"A",
+ "\u{1D56D}"=>"B",
+ "\u{1D56E}"=>"C",
+ "\u{1D56F}"=>"D",
+ "\u{1D570}"=>"E",
+ "\u{1D571}"=>"F",
+ "\u{1D572}"=>"G",
+ "\u{1D573}"=>"H",
+ "\u{1D574}"=>"I",
+ "\u{1D575}"=>"J",
+ "\u{1D576}"=>"K",
+ "\u{1D577}"=>"L",
+ "\u{1D578}"=>"M",
+ "\u{1D579}"=>"N",
+ "\u{1D57A}"=>"O",
+ "\u{1D57B}"=>"P",
+ "\u{1D57C}"=>"Q",
+ "\u{1D57D}"=>"R",
+ "\u{1D57E}"=>"S",
+ "\u{1D57F}"=>"T",
+ "\u{1D580}"=>"U",
+ "\u{1D581}"=>"V",
+ "\u{1D582}"=>"W",
+ "\u{1D583}"=>"X",
+ "\u{1D584}"=>"Y",
+ "\u{1D585}"=>"Z",
+ "\u{1D586}"=>"a",
+ "\u{1D587}"=>"b",
+ "\u{1D588}"=>"c",
+ "\u{1D589}"=>"d",
+ "\u{1D58A}"=>"e",
+ "\u{1D58B}"=>"f",
+ "\u{1D58C}"=>"g",
+ "\u{1D58D}"=>"h",
+ "\u{1D58E}"=>"i",
+ "\u{1D58F}"=>"j",
+ "\u{1D590}"=>"k",
+ "\u{1D591}"=>"l",
+ "\u{1D592}"=>"m",
+ "\u{1D593}"=>"n",
+ "\u{1D594}"=>"o",
+ "\u{1D595}"=>"p",
+ "\u{1D596}"=>"q",
+ "\u{1D597}"=>"r",
+ "\u{1D598}"=>"s",
+ "\u{1D599}"=>"t",
+ "\u{1D59A}"=>"u",
+ "\u{1D59B}"=>"v",
+ "\u{1D59C}"=>"w",
+ "\u{1D59D}"=>"x",
+ "\u{1D59E}"=>"y",
+ "\u{1D59F}"=>"z",
+ "\u{1D5A0}"=>"A",
+ "\u{1D5A1}"=>"B",
+ "\u{1D5A2}"=>"C",
+ "\u{1D5A3}"=>"D",
+ "\u{1D5A4}"=>"E",
+ "\u{1D5A5}"=>"F",
+ "\u{1D5A6}"=>"G",
+ "\u{1D5A7}"=>"H",
+ "\u{1D5A8}"=>"I",
+ "\u{1D5A9}"=>"J",
+ "\u{1D5AA}"=>"K",
+ "\u{1D5AB}"=>"L",
+ "\u{1D5AC}"=>"M",
+ "\u{1D5AD}"=>"N",
+ "\u{1D5AE}"=>"O",
+ "\u{1D5AF}"=>"P",
+ "\u{1D5B0}"=>"Q",
+ "\u{1D5B1}"=>"R",
+ "\u{1D5B2}"=>"S",
+ "\u{1D5B3}"=>"T",
+ "\u{1D5B4}"=>"U",
+ "\u{1D5B5}"=>"V",
+ "\u{1D5B6}"=>"W",
+ "\u{1D5B7}"=>"X",
+ "\u{1D5B8}"=>"Y",
+ "\u{1D5B9}"=>"Z",
+ "\u{1D5BA}"=>"a",
+ "\u{1D5BB}"=>"b",
+ "\u{1D5BC}"=>"c",
+ "\u{1D5BD}"=>"d",
+ "\u{1D5BE}"=>"e",
+ "\u{1D5BF}"=>"f",
+ "\u{1D5C0}"=>"g",
+ "\u{1D5C1}"=>"h",
+ "\u{1D5C2}"=>"i",
+ "\u{1D5C3}"=>"j",
+ "\u{1D5C4}"=>"k",
+ "\u{1D5C5}"=>"l",
+ "\u{1D5C6}"=>"m",
+ "\u{1D5C7}"=>"n",
+ "\u{1D5C8}"=>"o",
+ "\u{1D5C9}"=>"p",
+ "\u{1D5CA}"=>"q",
+ "\u{1D5CB}"=>"r",
+ "\u{1D5CC}"=>"s",
+ "\u{1D5CD}"=>"t",
+ "\u{1D5CE}"=>"u",
+ "\u{1D5CF}"=>"v",
+ "\u{1D5D0}"=>"w",
+ "\u{1D5D1}"=>"x",
+ "\u{1D5D2}"=>"y",
+ "\u{1D5D3}"=>"z",
+ "\u{1D5D4}"=>"A",
+ "\u{1D5D5}"=>"B",
+ "\u{1D5D6}"=>"C",
+ "\u{1D5D7}"=>"D",
+ "\u{1D5D8}"=>"E",
+ "\u{1D5D9}"=>"F",
+ "\u{1D5DA}"=>"G",
+ "\u{1D5DB}"=>"H",
+ "\u{1D5DC}"=>"I",
+ "\u{1D5DD}"=>"J",
+ "\u{1D5DE}"=>"K",
+ "\u{1D5DF}"=>"L",
+ "\u{1D5E0}"=>"M",
+ "\u{1D5E1}"=>"N",
+ "\u{1D5E2}"=>"O",
+ "\u{1D5E3}"=>"P",
+ "\u{1D5E4}"=>"Q",
+ "\u{1D5E5}"=>"R",
+ "\u{1D5E6}"=>"S",
+ "\u{1D5E7}"=>"T",
+ "\u{1D5E8}"=>"U",
+ "\u{1D5E9}"=>"V",
+ "\u{1D5EA}"=>"W",
+ "\u{1D5EB}"=>"X",
+ "\u{1D5EC}"=>"Y",
+ "\u{1D5ED}"=>"Z",
+ "\u{1D5EE}"=>"a",
+ "\u{1D5EF}"=>"b",
+ "\u{1D5F0}"=>"c",
+ "\u{1D5F1}"=>"d",
+ "\u{1D5F2}"=>"e",
+ "\u{1D5F3}"=>"f",
+ "\u{1D5F4}"=>"g",
+ "\u{1D5F5}"=>"h",
+ "\u{1D5F6}"=>"i",
+ "\u{1D5F7}"=>"j",
+ "\u{1D5F8}"=>"k",
+ "\u{1D5F9}"=>"l",
+ "\u{1D5FA}"=>"m",
+ "\u{1D5FB}"=>"n",
+ "\u{1D5FC}"=>"o",
+ "\u{1D5FD}"=>"p",
+ "\u{1D5FE}"=>"q",
+ "\u{1D5FF}"=>"r",
+ "\u{1D600}"=>"s",
+ "\u{1D601}"=>"t",
+ "\u{1D602}"=>"u",
+ "\u{1D603}"=>"v",
+ "\u{1D604}"=>"w",
+ "\u{1D605}"=>"x",
+ "\u{1D606}"=>"y",
+ "\u{1D607}"=>"z",
+ "\u{1D608}"=>"A",
+ "\u{1D609}"=>"B",
+ "\u{1D60A}"=>"C",
+ "\u{1D60B}"=>"D",
+ "\u{1D60C}"=>"E",
+ "\u{1D60D}"=>"F",
+ "\u{1D60E}"=>"G",
+ "\u{1D60F}"=>"H",
+ "\u{1D610}"=>"I",
+ "\u{1D611}"=>"J",
+ "\u{1D612}"=>"K",
+ "\u{1D613}"=>"L",
+ "\u{1D614}"=>"M",
+ "\u{1D615}"=>"N",
+ "\u{1D616}"=>"O",
+ "\u{1D617}"=>"P",
+ "\u{1D618}"=>"Q",
+ "\u{1D619}"=>"R",
+ "\u{1D61A}"=>"S",
+ "\u{1D61B}"=>"T",
+ "\u{1D61C}"=>"U",
+ "\u{1D61D}"=>"V",
+ "\u{1D61E}"=>"W",
+ "\u{1D61F}"=>"X",
+ "\u{1D620}"=>"Y",
+ "\u{1D621}"=>"Z",
+ "\u{1D622}"=>"a",
+ "\u{1D623}"=>"b",
+ "\u{1D624}"=>"c",
+ "\u{1D625}"=>"d",
+ "\u{1D626}"=>"e",
+ "\u{1D627}"=>"f",
+ "\u{1D628}"=>"g",
+ "\u{1D629}"=>"h",
+ "\u{1D62A}"=>"i",
+ "\u{1D62B}"=>"j",
+ "\u{1D62C}"=>"k",
+ "\u{1D62D}"=>"l",
+ "\u{1D62E}"=>"m",
+ "\u{1D62F}"=>"n",
+ "\u{1D630}"=>"o",
+ "\u{1D631}"=>"p",
+ "\u{1D632}"=>"q",
+ "\u{1D633}"=>"r",
+ "\u{1D634}"=>"s",
+ "\u{1D635}"=>"t",
+ "\u{1D636}"=>"u",
+ "\u{1D637}"=>"v",
+ "\u{1D638}"=>"w",
+ "\u{1D639}"=>"x",
+ "\u{1D63A}"=>"y",
+ "\u{1D63B}"=>"z",
+ "\u{1D63C}"=>"A",
+ "\u{1D63D}"=>"B",
+ "\u{1D63E}"=>"C",
+ "\u{1D63F}"=>"D",
+ "\u{1D640}"=>"E",
+ "\u{1D641}"=>"F",
+ "\u{1D642}"=>"G",
+ "\u{1D643}"=>"H",
+ "\u{1D644}"=>"I",
+ "\u{1D645}"=>"J",
+ "\u{1D646}"=>"K",
+ "\u{1D647}"=>"L",
+ "\u{1D648}"=>"M",
+ "\u{1D649}"=>"N",
+ "\u{1D64A}"=>"O",
+ "\u{1D64B}"=>"P",
+ "\u{1D64C}"=>"Q",
+ "\u{1D64D}"=>"R",
+ "\u{1D64E}"=>"S",
+ "\u{1D64F}"=>"T",
+ "\u{1D650}"=>"U",
+ "\u{1D651}"=>"V",
+ "\u{1D652}"=>"W",
+ "\u{1D653}"=>"X",
+ "\u{1D654}"=>"Y",
+ "\u{1D655}"=>"Z",
+ "\u{1D656}"=>"a",
+ "\u{1D657}"=>"b",
+ "\u{1D658}"=>"c",
+ "\u{1D659}"=>"d",
+ "\u{1D65A}"=>"e",
+ "\u{1D65B}"=>"f",
+ "\u{1D65C}"=>"g",
+ "\u{1D65D}"=>"h",
+ "\u{1D65E}"=>"i",
+ "\u{1D65F}"=>"j",
+ "\u{1D660}"=>"k",
+ "\u{1D661}"=>"l",
+ "\u{1D662}"=>"m",
+ "\u{1D663}"=>"n",
+ "\u{1D664}"=>"o",
+ "\u{1D665}"=>"p",
+ "\u{1D666}"=>"q",
+ "\u{1D667}"=>"r",
+ "\u{1D668}"=>"s",
+ "\u{1D669}"=>"t",
+ "\u{1D66A}"=>"u",
+ "\u{1D66B}"=>"v",
+ "\u{1D66C}"=>"w",
+ "\u{1D66D}"=>"x",
+ "\u{1D66E}"=>"y",
+ "\u{1D66F}"=>"z",
+ "\u{1D670}"=>"A",
+ "\u{1D671}"=>"B",
+ "\u{1D672}"=>"C",
+ "\u{1D673}"=>"D",
+ "\u{1D674}"=>"E",
+ "\u{1D675}"=>"F",
+ "\u{1D676}"=>"G",
+ "\u{1D677}"=>"H",
+ "\u{1D678}"=>"I",
+ "\u{1D679}"=>"J",
+ "\u{1D67A}"=>"K",
+ "\u{1D67B}"=>"L",
+ "\u{1D67C}"=>"M",
+ "\u{1D67D}"=>"N",
+ "\u{1D67E}"=>"O",
+ "\u{1D67F}"=>"P",
+ "\u{1D680}"=>"Q",
+ "\u{1D681}"=>"R",
+ "\u{1D682}"=>"S",
+ "\u{1D683}"=>"T",
+ "\u{1D684}"=>"U",
+ "\u{1D685}"=>"V",
+ "\u{1D686}"=>"W",
+ "\u{1D687}"=>"X",
+ "\u{1D688}"=>"Y",
+ "\u{1D689}"=>"Z",
+ "\u{1D68A}"=>"a",
+ "\u{1D68B}"=>"b",
+ "\u{1D68C}"=>"c",
+ "\u{1D68D}"=>"d",
+ "\u{1D68E}"=>"e",
+ "\u{1D68F}"=>"f",
+ "\u{1D690}"=>"g",
+ "\u{1D691}"=>"h",
+ "\u{1D692}"=>"i",
+ "\u{1D693}"=>"j",
+ "\u{1D694}"=>"k",
+ "\u{1D695}"=>"l",
+ "\u{1D696}"=>"m",
+ "\u{1D697}"=>"n",
+ "\u{1D698}"=>"o",
+ "\u{1D699}"=>"p",
+ "\u{1D69A}"=>"q",
+ "\u{1D69B}"=>"r",
+ "\u{1D69C}"=>"s",
+ "\u{1D69D}"=>"t",
+ "\u{1D69E}"=>"u",
+ "\u{1D69F}"=>"v",
+ "\u{1D6A0}"=>"w",
+ "\u{1D6A1}"=>"x",
+ "\u{1D6A2}"=>"y",
+ "\u{1D6A3}"=>"z",
+ "\u{1D6A4}"=>"\u0131",
+ "\u{1D6A5}"=>"\u0237",
+ "\u{1D6A8}"=>"\u0391",
+ "\u{1D6A9}"=>"\u0392",
+ "\u{1D6AA}"=>"\u0393",
+ "\u{1D6AB}"=>"\u0394",
+ "\u{1D6AC}"=>"\u0395",
+ "\u{1D6AD}"=>"\u0396",
+ "\u{1D6AE}"=>"\u0397",
+ "\u{1D6AF}"=>"\u0398",
+ "\u{1D6B0}"=>"\u0399",
+ "\u{1D6B1}"=>"\u039A",
+ "\u{1D6B2}"=>"\u039B",
+ "\u{1D6B3}"=>"\u039C",
+ "\u{1D6B4}"=>"\u039D",
+ "\u{1D6B5}"=>"\u039E",
+ "\u{1D6B6}"=>"\u039F",
+ "\u{1D6B7}"=>"\u03A0",
+ "\u{1D6B8}"=>"\u03A1",
+ "\u{1D6B9}"=>"\u0398",
+ "\u{1D6BA}"=>"\u03A3",
+ "\u{1D6BB}"=>"\u03A4",
+ "\u{1D6BC}"=>"\u03A5",
+ "\u{1D6BD}"=>"\u03A6",
+ "\u{1D6BE}"=>"\u03A7",
+ "\u{1D6BF}"=>"\u03A8",
+ "\u{1D6C0}"=>"\u03A9",
+ "\u{1D6C1}"=>"\u2207",
+ "\u{1D6C2}"=>"\u03B1",
+ "\u{1D6C3}"=>"\u03B2",
+ "\u{1D6C4}"=>"\u03B3",
+ "\u{1D6C5}"=>"\u03B4",
+ "\u{1D6C6}"=>"\u03B5",
+ "\u{1D6C7}"=>"\u03B6",
+ "\u{1D6C8}"=>"\u03B7",
+ "\u{1D6C9}"=>"\u03B8",
+ "\u{1D6CA}"=>"\u03B9",
+ "\u{1D6CB}"=>"\u03BA",
+ "\u{1D6CC}"=>"\u03BB",
+ "\u{1D6CD}"=>"\u03BC",
+ "\u{1D6CE}"=>"\u03BD",
+ "\u{1D6CF}"=>"\u03BE",
+ "\u{1D6D0}"=>"\u03BF",
+ "\u{1D6D1}"=>"\u03C0",
+ "\u{1D6D2}"=>"\u03C1",
+ "\u{1D6D3}"=>"\u03C2",
+ "\u{1D6D4}"=>"\u03C3",
+ "\u{1D6D5}"=>"\u03C4",
+ "\u{1D6D6}"=>"\u03C5",
+ "\u{1D6D7}"=>"\u03C6",
+ "\u{1D6D8}"=>"\u03C7",
+ "\u{1D6D9}"=>"\u03C8",
+ "\u{1D6DA}"=>"\u03C9",
+ "\u{1D6DB}"=>"\u2202",
+ "\u{1D6DC}"=>"\u03B5",
+ "\u{1D6DD}"=>"\u03B8",
+ "\u{1D6DE}"=>"\u03BA",
+ "\u{1D6DF}"=>"\u03C6",
+ "\u{1D6E0}"=>"\u03C1",
+ "\u{1D6E1}"=>"\u03C0",
+ "\u{1D6E2}"=>"\u0391",
+ "\u{1D6E3}"=>"\u0392",
+ "\u{1D6E4}"=>"\u0393",
+ "\u{1D6E5}"=>"\u0394",
+ "\u{1D6E6}"=>"\u0395",
+ "\u{1D6E7}"=>"\u0396",
+ "\u{1D6E8}"=>"\u0397",
+ "\u{1D6E9}"=>"\u0398",
+ "\u{1D6EA}"=>"\u0399",
+ "\u{1D6EB}"=>"\u039A",
+ "\u{1D6EC}"=>"\u039B",
+ "\u{1D6ED}"=>"\u039C",
+ "\u{1D6EE}"=>"\u039D",
+ "\u{1D6EF}"=>"\u039E",
+ "\u{1D6F0}"=>"\u039F",
+ "\u{1D6F1}"=>"\u03A0",
+ "\u{1D6F2}"=>"\u03A1",
+ "\u{1D6F3}"=>"\u0398",
+ "\u{1D6F4}"=>"\u03A3",
+ "\u{1D6F5}"=>"\u03A4",
+ "\u{1D6F6}"=>"\u03A5",
+ "\u{1D6F7}"=>"\u03A6",
+ "\u{1D6F8}"=>"\u03A7",
+ "\u{1D6F9}"=>"\u03A8",
+ "\u{1D6FA}"=>"\u03A9",
+ "\u{1D6FB}"=>"\u2207",
+ "\u{1D6FC}"=>"\u03B1",
+ "\u{1D6FD}"=>"\u03B2",
+ "\u{1D6FE}"=>"\u03B3",
+ "\u{1D6FF}"=>"\u03B4",
+ "\u{1D700}"=>"\u03B5",
+ "\u{1D701}"=>"\u03B6",
+ "\u{1D702}"=>"\u03B7",
+ "\u{1D703}"=>"\u03B8",
+ "\u{1D704}"=>"\u03B9",
+ "\u{1D705}"=>"\u03BA",
+ "\u{1D706}"=>"\u03BB",
+ "\u{1D707}"=>"\u03BC",
+ "\u{1D708}"=>"\u03BD",
+ "\u{1D709}"=>"\u03BE",
+ "\u{1D70A}"=>"\u03BF",
+ "\u{1D70B}"=>"\u03C0",
+ "\u{1D70C}"=>"\u03C1",
+ "\u{1D70D}"=>"\u03C2",
+ "\u{1D70E}"=>"\u03C3",
+ "\u{1D70F}"=>"\u03C4",
+ "\u{1D710}"=>"\u03C5",
+ "\u{1D711}"=>"\u03C6",
+ "\u{1D712}"=>"\u03C7",
+ "\u{1D713}"=>"\u03C8",
+ "\u{1D714}"=>"\u03C9",
+ "\u{1D715}"=>"\u2202",
+ "\u{1D716}"=>"\u03B5",
+ "\u{1D717}"=>"\u03B8",
+ "\u{1D718}"=>"\u03BA",
+ "\u{1D719}"=>"\u03C6",
+ "\u{1D71A}"=>"\u03C1",
+ "\u{1D71B}"=>"\u03C0",
+ "\u{1D71C}"=>"\u0391",
+ "\u{1D71D}"=>"\u0392",
+ "\u{1D71E}"=>"\u0393",
+ "\u{1D71F}"=>"\u0394",
+ "\u{1D720}"=>"\u0395",
+ "\u{1D721}"=>"\u0396",
+ "\u{1D722}"=>"\u0397",
+ "\u{1D723}"=>"\u0398",
+ "\u{1D724}"=>"\u0399",
+ "\u{1D725}"=>"\u039A",
+ "\u{1D726}"=>"\u039B",
+ "\u{1D727}"=>"\u039C",
+ "\u{1D728}"=>"\u039D",
+ "\u{1D729}"=>"\u039E",
+ "\u{1D72A}"=>"\u039F",
+ "\u{1D72B}"=>"\u03A0",
+ "\u{1D72C}"=>"\u03A1",
+ "\u{1D72D}"=>"\u0398",
+ "\u{1D72E}"=>"\u03A3",
+ "\u{1D72F}"=>"\u03A4",
+ "\u{1D730}"=>"\u03A5",
+ "\u{1D731}"=>"\u03A6",
+ "\u{1D732}"=>"\u03A7",
+ "\u{1D733}"=>"\u03A8",
+ "\u{1D734}"=>"\u03A9",
+ "\u{1D735}"=>"\u2207",
+ "\u{1D736}"=>"\u03B1",
+ "\u{1D737}"=>"\u03B2",
+ "\u{1D738}"=>"\u03B3",
+ "\u{1D739}"=>"\u03B4",
+ "\u{1D73A}"=>"\u03B5",
+ "\u{1D73B}"=>"\u03B6",
+ "\u{1D73C}"=>"\u03B7",
+ "\u{1D73D}"=>"\u03B8",
+ "\u{1D73E}"=>"\u03B9",
+ "\u{1D73F}"=>"\u03BA",
+ "\u{1D740}"=>"\u03BB",
+ "\u{1D741}"=>"\u03BC",
+ "\u{1D742}"=>"\u03BD",
+ "\u{1D743}"=>"\u03BE",
+ "\u{1D744}"=>"\u03BF",
+ "\u{1D745}"=>"\u03C0",
+ "\u{1D746}"=>"\u03C1",
+ "\u{1D747}"=>"\u03C2",
+ "\u{1D748}"=>"\u03C3",
+ "\u{1D749}"=>"\u03C4",
+ "\u{1D74A}"=>"\u03C5",
+ "\u{1D74B}"=>"\u03C6",
+ "\u{1D74C}"=>"\u03C7",
+ "\u{1D74D}"=>"\u03C8",
+ "\u{1D74E}"=>"\u03C9",
+ "\u{1D74F}"=>"\u2202",
+ "\u{1D750}"=>"\u03B5",
+ "\u{1D751}"=>"\u03B8",
+ "\u{1D752}"=>"\u03BA",
+ "\u{1D753}"=>"\u03C6",
+ "\u{1D754}"=>"\u03C1",
+ "\u{1D755}"=>"\u03C0",
+ "\u{1D756}"=>"\u0391",
+ "\u{1D757}"=>"\u0392",
+ "\u{1D758}"=>"\u0393",
+ "\u{1D759}"=>"\u0394",
+ "\u{1D75A}"=>"\u0395",
+ "\u{1D75B}"=>"\u0396",
+ "\u{1D75C}"=>"\u0397",
+ "\u{1D75D}"=>"\u0398",
+ "\u{1D75E}"=>"\u0399",
+ "\u{1D75F}"=>"\u039A",
+ "\u{1D760}"=>"\u039B",
+ "\u{1D761}"=>"\u039C",
+ "\u{1D762}"=>"\u039D",
+ "\u{1D763}"=>"\u039E",
+ "\u{1D764}"=>"\u039F",
+ "\u{1D765}"=>"\u03A0",
+ "\u{1D766}"=>"\u03A1",
+ "\u{1D767}"=>"\u0398",
+ "\u{1D768}"=>"\u03A3",
+ "\u{1D769}"=>"\u03A4",
+ "\u{1D76A}"=>"\u03A5",
+ "\u{1D76B}"=>"\u03A6",
+ "\u{1D76C}"=>"\u03A7",
+ "\u{1D76D}"=>"\u03A8",
+ "\u{1D76E}"=>"\u03A9",
+ "\u{1D76F}"=>"\u2207",
+ "\u{1D770}"=>"\u03B1",
+ "\u{1D771}"=>"\u03B2",
+ "\u{1D772}"=>"\u03B3",
+ "\u{1D773}"=>"\u03B4",
+ "\u{1D774}"=>"\u03B5",
+ "\u{1D775}"=>"\u03B6",
+ "\u{1D776}"=>"\u03B7",
+ "\u{1D777}"=>"\u03B8",
+ "\u{1D778}"=>"\u03B9",
+ "\u{1D779}"=>"\u03BA",
+ "\u{1D77A}"=>"\u03BB",
+ "\u{1D77B}"=>"\u03BC",
+ "\u{1D77C}"=>"\u03BD",
+ "\u{1D77D}"=>"\u03BE",
+ "\u{1D77E}"=>"\u03BF",
+ "\u{1D77F}"=>"\u03C0",
+ "\u{1D780}"=>"\u03C1",
+ "\u{1D781}"=>"\u03C2",
+ "\u{1D782}"=>"\u03C3",
+ "\u{1D783}"=>"\u03C4",
+ "\u{1D784}"=>"\u03C5",
+ "\u{1D785}"=>"\u03C6",
+ "\u{1D786}"=>"\u03C7",
+ "\u{1D787}"=>"\u03C8",
+ "\u{1D788}"=>"\u03C9",
+ "\u{1D789}"=>"\u2202",
+ "\u{1D78A}"=>"\u03B5",
+ "\u{1D78B}"=>"\u03B8",
+ "\u{1D78C}"=>"\u03BA",
+ "\u{1D78D}"=>"\u03C6",
+ "\u{1D78E}"=>"\u03C1",
+ "\u{1D78F}"=>"\u03C0",
+ "\u{1D790}"=>"\u0391",
+ "\u{1D791}"=>"\u0392",
+ "\u{1D792}"=>"\u0393",
+ "\u{1D793}"=>"\u0394",
+ "\u{1D794}"=>"\u0395",
+ "\u{1D795}"=>"\u0396",
+ "\u{1D796}"=>"\u0397",
+ "\u{1D797}"=>"\u0398",
+ "\u{1D798}"=>"\u0399",
+ "\u{1D799}"=>"\u039A",
+ "\u{1D79A}"=>"\u039B",
+ "\u{1D79B}"=>"\u039C",
+ "\u{1D79C}"=>"\u039D",
+ "\u{1D79D}"=>"\u039E",
+ "\u{1D79E}"=>"\u039F",
+ "\u{1D79F}"=>"\u03A0",
+ "\u{1D7A0}"=>"\u03A1",
+ "\u{1D7A1}"=>"\u0398",
+ "\u{1D7A2}"=>"\u03A3",
+ "\u{1D7A3}"=>"\u03A4",
+ "\u{1D7A4}"=>"\u03A5",
+ "\u{1D7A5}"=>"\u03A6",
+ "\u{1D7A6}"=>"\u03A7",
+ "\u{1D7A7}"=>"\u03A8",
+ "\u{1D7A8}"=>"\u03A9",
+ "\u{1D7A9}"=>"\u2207",
+ "\u{1D7AA}"=>"\u03B1",
+ "\u{1D7AB}"=>"\u03B2",
+ "\u{1D7AC}"=>"\u03B3",
+ "\u{1D7AD}"=>"\u03B4",
+ "\u{1D7AE}"=>"\u03B5",
+ "\u{1D7AF}"=>"\u03B6",
+ "\u{1D7B0}"=>"\u03B7",
+ "\u{1D7B1}"=>"\u03B8",
+ "\u{1D7B2}"=>"\u03B9",
+ "\u{1D7B3}"=>"\u03BA",
+ "\u{1D7B4}"=>"\u03BB",
+ "\u{1D7B5}"=>"\u03BC",
+ "\u{1D7B6}"=>"\u03BD",
+ "\u{1D7B7}"=>"\u03BE",
+ "\u{1D7B8}"=>"\u03BF",
+ "\u{1D7B9}"=>"\u03C0",
+ "\u{1D7BA}"=>"\u03C1",
+ "\u{1D7BB}"=>"\u03C2",
+ "\u{1D7BC}"=>"\u03C3",
+ "\u{1D7BD}"=>"\u03C4",
+ "\u{1D7BE}"=>"\u03C5",
+ "\u{1D7BF}"=>"\u03C6",
+ "\u{1D7C0}"=>"\u03C7",
+ "\u{1D7C1}"=>"\u03C8",
+ "\u{1D7C2}"=>"\u03C9",
+ "\u{1D7C3}"=>"\u2202",
+ "\u{1D7C4}"=>"\u03B5",
+ "\u{1D7C5}"=>"\u03B8",
+ "\u{1D7C6}"=>"\u03BA",
+ "\u{1D7C7}"=>"\u03C6",
+ "\u{1D7C8}"=>"\u03C1",
+ "\u{1D7C9}"=>"\u03C0",
+ "\u{1D7CA}"=>"\u03DC",
+ "\u{1D7CB}"=>"\u03DD",
+ "\u{1D7CE}"=>"0",
+ "\u{1D7CF}"=>"1",
+ "\u{1D7D0}"=>"2",
+ "\u{1D7D1}"=>"3",
+ "\u{1D7D2}"=>"4",
+ "\u{1D7D3}"=>"5",
+ "\u{1D7D4}"=>"6",
+ "\u{1D7D5}"=>"7",
+ "\u{1D7D6}"=>"8",
+ "\u{1D7D7}"=>"9",
+ "\u{1D7D8}"=>"0",
+ "\u{1D7D9}"=>"1",
+ "\u{1D7DA}"=>"2",
+ "\u{1D7DB}"=>"3",
+ "\u{1D7DC}"=>"4",
+ "\u{1D7DD}"=>"5",
+ "\u{1D7DE}"=>"6",
+ "\u{1D7DF}"=>"7",
+ "\u{1D7E0}"=>"8",
+ "\u{1D7E1}"=>"9",
+ "\u{1D7E2}"=>"0",
+ "\u{1D7E3}"=>"1",
+ "\u{1D7E4}"=>"2",
+ "\u{1D7E5}"=>"3",
+ "\u{1D7E6}"=>"4",
+ "\u{1D7E7}"=>"5",
+ "\u{1D7E8}"=>"6",
+ "\u{1D7E9}"=>"7",
+ "\u{1D7EA}"=>"8",
+ "\u{1D7EB}"=>"9",
+ "\u{1D7EC}"=>"0",
+ "\u{1D7ED}"=>"1",
+ "\u{1D7EE}"=>"2",
+ "\u{1D7EF}"=>"3",
+ "\u{1D7F0}"=>"4",
+ "\u{1D7F1}"=>"5",
+ "\u{1D7F2}"=>"6",
+ "\u{1D7F3}"=>"7",
+ "\u{1D7F4}"=>"8",
+ "\u{1D7F5}"=>"9",
+ "\u{1D7F6}"=>"0",
+ "\u{1D7F7}"=>"1",
+ "\u{1D7F8}"=>"2",
+ "\u{1D7F9}"=>"3",
+ "\u{1D7FA}"=>"4",
+ "\u{1D7FB}"=>"5",
+ "\u{1D7FC}"=>"6",
+ "\u{1D7FD}"=>"7",
+ "\u{1D7FE}"=>"8",
+ "\u{1D7FF}"=>"9",
+ "\u{1EE00}"=>"\u0627",
+ "\u{1EE01}"=>"\u0628",
+ "\u{1EE02}"=>"\u062C",
+ "\u{1EE03}"=>"\u062F",
+ "\u{1EE05}"=>"\u0648",
+ "\u{1EE06}"=>"\u0632",
+ "\u{1EE07}"=>"\u062D",
+ "\u{1EE08}"=>"\u0637",
+ "\u{1EE09}"=>"\u064A",
+ "\u{1EE0A}"=>"\u0643",
+ "\u{1EE0B}"=>"\u0644",
+ "\u{1EE0C}"=>"\u0645",
+ "\u{1EE0D}"=>"\u0646",
+ "\u{1EE0E}"=>"\u0633",
+ "\u{1EE0F}"=>"\u0639",
+ "\u{1EE10}"=>"\u0641",
+ "\u{1EE11}"=>"\u0635",
+ "\u{1EE12}"=>"\u0642",
+ "\u{1EE13}"=>"\u0631",
+ "\u{1EE14}"=>"\u0634",
+ "\u{1EE15}"=>"\u062A",
+ "\u{1EE16}"=>"\u062B",
+ "\u{1EE17}"=>"\u062E",
+ "\u{1EE18}"=>"\u0630",
+ "\u{1EE19}"=>"\u0636",
+ "\u{1EE1A}"=>"\u0638",
+ "\u{1EE1B}"=>"\u063A",
+ "\u{1EE1C}"=>"\u066E",
+ "\u{1EE1D}"=>"\u06BA",
+ "\u{1EE1E}"=>"\u06A1",
+ "\u{1EE1F}"=>"\u066F",
+ "\u{1EE21}"=>"\u0628",
+ "\u{1EE22}"=>"\u062C",
+ "\u{1EE24}"=>"\u0647",
+ "\u{1EE27}"=>"\u062D",
+ "\u{1EE29}"=>"\u064A",
+ "\u{1EE2A}"=>"\u0643",
+ "\u{1EE2B}"=>"\u0644",
+ "\u{1EE2C}"=>"\u0645",
+ "\u{1EE2D}"=>"\u0646",
+ "\u{1EE2E}"=>"\u0633",
+ "\u{1EE2F}"=>"\u0639",
+ "\u{1EE30}"=>"\u0641",
+ "\u{1EE31}"=>"\u0635",
+ "\u{1EE32}"=>"\u0642",
+ "\u{1EE34}"=>"\u0634",
+ "\u{1EE35}"=>"\u062A",
+ "\u{1EE36}"=>"\u062B",
+ "\u{1EE37}"=>"\u062E",
+ "\u{1EE39}"=>"\u0636",
+ "\u{1EE3B}"=>"\u063A",
+ "\u{1EE42}"=>"\u062C",
+ "\u{1EE47}"=>"\u062D",
+ "\u{1EE49}"=>"\u064A",
+ "\u{1EE4B}"=>"\u0644",
+ "\u{1EE4D}"=>"\u0646",
+ "\u{1EE4E}"=>"\u0633",
+ "\u{1EE4F}"=>"\u0639",
+ "\u{1EE51}"=>"\u0635",
+ "\u{1EE52}"=>"\u0642",
+ "\u{1EE54}"=>"\u0634",
+ "\u{1EE57}"=>"\u062E",
+ "\u{1EE59}"=>"\u0636",
+ "\u{1EE5B}"=>"\u063A",
+ "\u{1EE5D}"=>"\u06BA",
+ "\u{1EE5F}"=>"\u066F",
+ "\u{1EE61}"=>"\u0628",
+ "\u{1EE62}"=>"\u062C",
+ "\u{1EE64}"=>"\u0647",
+ "\u{1EE67}"=>"\u062D",
+ "\u{1EE68}"=>"\u0637",
+ "\u{1EE69}"=>"\u064A",
+ "\u{1EE6A}"=>"\u0643",
+ "\u{1EE6C}"=>"\u0645",
+ "\u{1EE6D}"=>"\u0646",
+ "\u{1EE6E}"=>"\u0633",
+ "\u{1EE6F}"=>"\u0639",
+ "\u{1EE70}"=>"\u0641",
+ "\u{1EE71}"=>"\u0635",
+ "\u{1EE72}"=>"\u0642",
+ "\u{1EE74}"=>"\u0634",
+ "\u{1EE75}"=>"\u062A",
+ "\u{1EE76}"=>"\u062B",
+ "\u{1EE77}"=>"\u062E",
+ "\u{1EE79}"=>"\u0636",
+ "\u{1EE7A}"=>"\u0638",
+ "\u{1EE7B}"=>"\u063A",
+ "\u{1EE7C}"=>"\u066E",
+ "\u{1EE7E}"=>"\u06A1",
+ "\u{1EE80}"=>"\u0627",
+ "\u{1EE81}"=>"\u0628",
+ "\u{1EE82}"=>"\u062C",
+ "\u{1EE83}"=>"\u062F",
+ "\u{1EE84}"=>"\u0647",
+ "\u{1EE85}"=>"\u0648",
+ "\u{1EE86}"=>"\u0632",
+ "\u{1EE87}"=>"\u062D",
+ "\u{1EE88}"=>"\u0637",
+ "\u{1EE89}"=>"\u064A",
+ "\u{1EE8B}"=>"\u0644",
+ "\u{1EE8C}"=>"\u0645",
+ "\u{1EE8D}"=>"\u0646",
+ "\u{1EE8E}"=>"\u0633",
+ "\u{1EE8F}"=>"\u0639",
+ "\u{1EE90}"=>"\u0641",
+ "\u{1EE91}"=>"\u0635",
+ "\u{1EE92}"=>"\u0642",
+ "\u{1EE93}"=>"\u0631",
+ "\u{1EE94}"=>"\u0634",
+ "\u{1EE95}"=>"\u062A",
+ "\u{1EE96}"=>"\u062B",
+ "\u{1EE97}"=>"\u062E",
+ "\u{1EE98}"=>"\u0630",
+ "\u{1EE99}"=>"\u0636",
+ "\u{1EE9A}"=>"\u0638",
+ "\u{1EE9B}"=>"\u063A",
+ "\u{1EEA1}"=>"\u0628",
+ "\u{1EEA2}"=>"\u062C",
+ "\u{1EEA3}"=>"\u062F",
+ "\u{1EEA5}"=>"\u0648",
+ "\u{1EEA6}"=>"\u0632",
+ "\u{1EEA7}"=>"\u062D",
+ "\u{1EEA8}"=>"\u0637",
+ "\u{1EEA9}"=>"\u064A",
+ "\u{1EEAB}"=>"\u0644",
+ "\u{1EEAC}"=>"\u0645",
+ "\u{1EEAD}"=>"\u0646",
+ "\u{1EEAE}"=>"\u0633",
+ "\u{1EEAF}"=>"\u0639",
+ "\u{1EEB0}"=>"\u0641",
+ "\u{1EEB1}"=>"\u0635",
+ "\u{1EEB2}"=>"\u0642",
+ "\u{1EEB3}"=>"\u0631",
+ "\u{1EEB4}"=>"\u0634",
+ "\u{1EEB5}"=>"\u062A",
+ "\u{1EEB6}"=>"\u062B",
+ "\u{1EEB7}"=>"\u062E",
+ "\u{1EEB8}"=>"\u0630",
+ "\u{1EEB9}"=>"\u0636",
+ "\u{1EEBA}"=>"\u0638",
+ "\u{1EEBB}"=>"\u063A",
+ "\u{1F100}"=>"0.",
+ "\u{1F101}"=>"0,",
+ "\u{1F102}"=>"1,",
+ "\u{1F103}"=>"2,",
+ "\u{1F104}"=>"3,",
+ "\u{1F105}"=>"4,",
+ "\u{1F106}"=>"5,",
+ "\u{1F107}"=>"6,",
+ "\u{1F108}"=>"7,",
+ "\u{1F109}"=>"8,",
+ "\u{1F10A}"=>"9,",
+ "\u{1F110}"=>"(A)",
+ "\u{1F111}"=>"(B)",
+ "\u{1F112}"=>"(C)",
+ "\u{1F113}"=>"(D)",
+ "\u{1F114}"=>"(E)",
+ "\u{1F115}"=>"(F)",
+ "\u{1F116}"=>"(G)",
+ "\u{1F117}"=>"(H)",
+ "\u{1F118}"=>"(I)",
+ "\u{1F119}"=>"(J)",
+ "\u{1F11A}"=>"(K)",
+ "\u{1F11B}"=>"(L)",
+ "\u{1F11C}"=>"(M)",
+ "\u{1F11D}"=>"(N)",
+ "\u{1F11E}"=>"(O)",
+ "\u{1F11F}"=>"(P)",
+ "\u{1F120}"=>"(Q)",
+ "\u{1F121}"=>"(R)",
+ "\u{1F122}"=>"(S)",
+ "\u{1F123}"=>"(T)",
+ "\u{1F124}"=>"(U)",
+ "\u{1F125}"=>"(V)",
+ "\u{1F126}"=>"(W)",
+ "\u{1F127}"=>"(X)",
+ "\u{1F128}"=>"(Y)",
+ "\u{1F129}"=>"(Z)",
+ "\u{1F12A}"=>"\u3014S\u3015",
+ "\u{1F12B}"=>"C",
+ "\u{1F12C}"=>"R",
+ "\u{1F12D}"=>"CD",
+ "\u{1F12E}"=>"WZ",
+ "\u{1F130}"=>"A",
+ "\u{1F131}"=>"B",
+ "\u{1F132}"=>"C",
+ "\u{1F133}"=>"D",
+ "\u{1F134}"=>"E",
+ "\u{1F135}"=>"F",
+ "\u{1F136}"=>"G",
+ "\u{1F137}"=>"H",
+ "\u{1F138}"=>"I",
+ "\u{1F139}"=>"J",
+ "\u{1F13A}"=>"K",
+ "\u{1F13B}"=>"L",
+ "\u{1F13C}"=>"M",
+ "\u{1F13D}"=>"N",
+ "\u{1F13E}"=>"O",
+ "\u{1F13F}"=>"P",
+ "\u{1F140}"=>"Q",
+ "\u{1F141}"=>"R",
+ "\u{1F142}"=>"S",
+ "\u{1F143}"=>"T",
+ "\u{1F144}"=>"U",
+ "\u{1F145}"=>"V",
+ "\u{1F146}"=>"W",
+ "\u{1F147}"=>"X",
+ "\u{1F148}"=>"Y",
+ "\u{1F149}"=>"Z",
+ "\u{1F14A}"=>"HV",
+ "\u{1F14B}"=>"MV",
+ "\u{1F14C}"=>"SD",
+ "\u{1F14D}"=>"SS",
+ "\u{1F14E}"=>"PPV",
+ "\u{1F14F}"=>"WC",
+ "\u{1F16A}"=>"MC",
+ "\u{1F16B}"=>"MD",
+ "\u{1F16C}"=>"MR",
+ "\u{1F190}"=>"DJ",
+ "\u{1F200}"=>"\u307B\u304B",
+ "\u{1F201}"=>"\u30B3\u30B3",
+ "\u{1F202}"=>"\u30B5",
+ "\u{1F210}"=>"\u624B",
+ "\u{1F211}"=>"\u5B57",
+ "\u{1F212}"=>"\u53CC",
+ "\u{1F213}"=>"\u30C7",
+ "\u{1F214}"=>"\u4E8C",
+ "\u{1F215}"=>"\u591A",
+ "\u{1F216}"=>"\u89E3",
+ "\u{1F217}"=>"\u5929",
+ "\u{1F218}"=>"\u4EA4",
+ "\u{1F219}"=>"\u6620",
+ "\u{1F21A}"=>"\u7121",
+ "\u{1F21B}"=>"\u6599",
+ "\u{1F21C}"=>"\u524D",
+ "\u{1F21D}"=>"\u5F8C",
+ "\u{1F21E}"=>"\u518D",
+ "\u{1F21F}"=>"\u65B0",
+ "\u{1F220}"=>"\u521D",
+ "\u{1F221}"=>"\u7D42",
+ "\u{1F222}"=>"\u751F",
+ "\u{1F223}"=>"\u8CA9",
+ "\u{1F224}"=>"\u58F0",
+ "\u{1F225}"=>"\u5439",
+ "\u{1F226}"=>"\u6F14",
+ "\u{1F227}"=>"\u6295",
+ "\u{1F228}"=>"\u6355",
+ "\u{1F229}"=>"\u4E00",
+ "\u{1F22A}"=>"\u4E09",
+ "\u{1F22B}"=>"\u904A",
+ "\u{1F22C}"=>"\u5DE6",
+ "\u{1F22D}"=>"\u4E2D",
+ "\u{1F22E}"=>"\u53F3",
+ "\u{1F22F}"=>"\u6307",
+ "\u{1F230}"=>"\u8D70",
+ "\u{1F231}"=>"\u6253",
+ "\u{1F232}"=>"\u7981",
+ "\u{1F233}"=>"\u7A7A",
+ "\u{1F234}"=>"\u5408",
+ "\u{1F235}"=>"\u6E80",
+ "\u{1F236}"=>"\u6709",
+ "\u{1F237}"=>"\u6708",
+ "\u{1F238}"=>"\u7533",
+ "\u{1F239}"=>"\u5272",
+ "\u{1F23A}"=>"\u55B6",
+ "\u{1F23B}"=>"\u914D",
+ "\u{1F240}"=>"\u3014\u672C\u3015",
+ "\u{1F241}"=>"\u3014\u4E09\u3015",
+ "\u{1F242}"=>"\u3014\u4E8C\u3015",
+ "\u{1F243}"=>"\u3014\u5B89\u3015",
+ "\u{1F244}"=>"\u3014\u70B9\u3015",
+ "\u{1F245}"=>"\u3014\u6253\u3015",
+ "\u{1F246}"=>"\u3014\u76D7\u3015",
+ "\u{1F247}"=>"\u3014\u52DD\u3015",
+ "\u{1F248}"=>"\u3014\u6557\u3015",
+ "\u{1F250}"=>"\u5F97",
+ "\u{1F251}"=>"\u53EF",
+ "\u0385"=>" \u0308\u0301",
+ "\u03D3"=>"\u03A5\u0301",
+ "\u03D4"=>"\u03A5\u0308",
+ "\u1E9B"=>"s\u0307",
+ "\u1FC1"=>" \u0308\u0342",
+ "\u1FCD"=>" \u0313\u0300",
+ "\u1FCE"=>" \u0313\u0301",
+ "\u1FCF"=>" \u0313\u0342",
+ "\u1FDD"=>" \u0314\u0300",
+ "\u1FDE"=>" \u0314\u0301",
+ "\u1FDF"=>" \u0314\u0342",
+ "\u1FED"=>" \u0308\u0300",
+ "\u1FEE"=>" \u0308\u0301",
+ "\u1FFD"=>" \u0301",
+ "\u2000"=>" ",
+ "\u2001"=>" ",
}.freeze
COMPOSITION_TABLE = {
- "A\u0300"=>"\u00C0", "A\u0301"=>"\u00C1", "A\u0302"=>"\u00C2", "A\u0303"=>"\u00C3", "A\u0308"=>"\u00C4", "A\u030A"=>"\u00C5", "C\u0327"=>"\u00C7", "E\u0300"=>"\u00C8",
- "E\u0301"=>"\u00C9", "E\u0302"=>"\u00CA", "E\u0308"=>"\u00CB", "I\u0300"=>"\u00CC", "I\u0301"=>"\u00CD", "I\u0302"=>"\u00CE", "I\u0308"=>"\u00CF", "N\u0303"=>"\u00D1",
- "O\u0300"=>"\u00D2", "O\u0301"=>"\u00D3", "O\u0302"=>"\u00D4", "O\u0303"=>"\u00D5", "O\u0308"=>"\u00D6", "U\u0300"=>"\u00D9", "U\u0301"=>"\u00DA", "U\u0302"=>"\u00DB",
- "U\u0308"=>"\u00DC", "Y\u0301"=>"\u00DD", "a\u0300"=>"\u00E0", "a\u0301"=>"\u00E1", "a\u0302"=>"\u00E2", "a\u0303"=>"\u00E3", "a\u0308"=>"\u00E4", "a\u030A"=>"\u00E5",
- "c\u0327"=>"\u00E7", "e\u0300"=>"\u00E8", "e\u0301"=>"\u00E9", "e\u0302"=>"\u00EA", "e\u0308"=>"\u00EB", "i\u0300"=>"\u00EC", "i\u0301"=>"\u00ED", "i\u0302"=>"\u00EE",
- "i\u0308"=>"\u00EF", "n\u0303"=>"\u00F1", "o\u0300"=>"\u00F2", "o\u0301"=>"\u00F3", "o\u0302"=>"\u00F4", "o\u0303"=>"\u00F5", "o\u0308"=>"\u00F6", "u\u0300"=>"\u00F9",
- "u\u0301"=>"\u00FA", "u\u0302"=>"\u00FB", "u\u0308"=>"\u00FC", "y\u0301"=>"\u00FD", "y\u0308"=>"\u00FF", "A\u0304"=>"\u0100", "a\u0304"=>"\u0101", "A\u0306"=>"\u0102",
- "a\u0306"=>"\u0103", "A\u0328"=>"\u0104", "a\u0328"=>"\u0105", "C\u0301"=>"\u0106", "c\u0301"=>"\u0107", "C\u0302"=>"\u0108", "c\u0302"=>"\u0109", "C\u0307"=>"\u010A",
- "c\u0307"=>"\u010B", "C\u030C"=>"\u010C", "c\u030C"=>"\u010D", "D\u030C"=>"\u010E", "d\u030C"=>"\u010F", "E\u0304"=>"\u0112", "e\u0304"=>"\u0113", "E\u0306"=>"\u0114",
- "e\u0306"=>"\u0115", "E\u0307"=>"\u0116", "e\u0307"=>"\u0117", "E\u0328"=>"\u0118", "e\u0328"=>"\u0119", "E\u030C"=>"\u011A", "e\u030C"=>"\u011B", "G\u0302"=>"\u011C",
- "g\u0302"=>"\u011D", "G\u0306"=>"\u011E", "g\u0306"=>"\u011F", "G\u0307"=>"\u0120", "g\u0307"=>"\u0121", "G\u0327"=>"\u0122", "g\u0327"=>"\u0123", "H\u0302"=>"\u0124",
- "h\u0302"=>"\u0125", "I\u0303"=>"\u0128", "i\u0303"=>"\u0129", "I\u0304"=>"\u012A", "i\u0304"=>"\u012B", "I\u0306"=>"\u012C", "i\u0306"=>"\u012D", "I\u0328"=>"\u012E",
- "i\u0328"=>"\u012F", "I\u0307"=>"\u0130", "J\u0302"=>"\u0134", "j\u0302"=>"\u0135", "K\u0327"=>"\u0136", "k\u0327"=>"\u0137", "L\u0301"=>"\u0139", "l\u0301"=>"\u013A",
- "L\u0327"=>"\u013B", "l\u0327"=>"\u013C", "L\u030C"=>"\u013D", "l\u030C"=>"\u013E", "N\u0301"=>"\u0143", "n\u0301"=>"\u0144", "N\u0327"=>"\u0145", "n\u0327"=>"\u0146",
- "N\u030C"=>"\u0147", "n\u030C"=>"\u0148", "O\u0304"=>"\u014C", "o\u0304"=>"\u014D", "O\u0306"=>"\u014E", "o\u0306"=>"\u014F", "O\u030B"=>"\u0150", "o\u030B"=>"\u0151",
- "R\u0301"=>"\u0154", "r\u0301"=>"\u0155", "R\u0327"=>"\u0156", "r\u0327"=>"\u0157", "R\u030C"=>"\u0158", "r\u030C"=>"\u0159", "S\u0301"=>"\u015A", "s\u0301"=>"\u015B",
- "S\u0302"=>"\u015C", "s\u0302"=>"\u015D", "S\u0327"=>"\u015E", "s\u0327"=>"\u015F", "S\u030C"=>"\u0160", "s\u030C"=>"\u0161", "T\u0327"=>"\u0162", "t\u0327"=>"\u0163",
- "T\u030C"=>"\u0164", "t\u030C"=>"\u0165", "U\u0303"=>"\u0168", "u\u0303"=>"\u0169", "U\u0304"=>"\u016A", "u\u0304"=>"\u016B", "U\u0306"=>"\u016C", "u\u0306"=>"\u016D",
- "U\u030A"=>"\u016E", "u\u030A"=>"\u016F", "U\u030B"=>"\u0170", "u\u030B"=>"\u0171", "U\u0328"=>"\u0172", "u\u0328"=>"\u0173", "W\u0302"=>"\u0174", "w\u0302"=>"\u0175",
- "Y\u0302"=>"\u0176", "y\u0302"=>"\u0177", "Y\u0308"=>"\u0178", "Z\u0301"=>"\u0179", "z\u0301"=>"\u017A", "Z\u0307"=>"\u017B", "z\u0307"=>"\u017C", "Z\u030C"=>"\u017D",
- "z\u030C"=>"\u017E", "O\u031B"=>"\u01A0", "o\u031B"=>"\u01A1", "U\u031B"=>"\u01AF", "u\u031B"=>"\u01B0", "A\u030C"=>"\u01CD", "a\u030C"=>"\u01CE", "I\u030C"=>"\u01CF",
- "i\u030C"=>"\u01D0", "O\u030C"=>"\u01D1", "o\u030C"=>"\u01D2", "U\u030C"=>"\u01D3", "u\u030C"=>"\u01D4", "\u00DC\u0304"=>"\u01D5", "\u00FC\u0304"=>"\u01D6", "\u00DC\u0301"=>"\u01D7",
- "\u00FC\u0301"=>"\u01D8", "\u00DC\u030C"=>"\u01D9", "\u00FC\u030C"=>"\u01DA", "\u00DC\u0300"=>"\u01DB", "\u00FC\u0300"=>"\u01DC", "\u00C4\u0304"=>"\u01DE", "\u00E4\u0304"=>"\u01DF", "\u0226\u0304"=>"\u01E0",
- "\u0227\u0304"=>"\u01E1", "\u00C6\u0304"=>"\u01E2", "\u00E6\u0304"=>"\u01E3", "G\u030C"=>"\u01E6", "g\u030C"=>"\u01E7", "K\u030C"=>"\u01E8", "k\u030C"=>"\u01E9", "O\u0328"=>"\u01EA",
- "o\u0328"=>"\u01EB", "\u01EA\u0304"=>"\u01EC", "\u01EB\u0304"=>"\u01ED", "\u01B7\u030C"=>"\u01EE", "\u0292\u030C"=>"\u01EF", "j\u030C"=>"\u01F0", "G\u0301"=>"\u01F4", "g\u0301"=>"\u01F5",
- "N\u0300"=>"\u01F8", "n\u0300"=>"\u01F9", "\u00C5\u0301"=>"\u01FA", "\u00E5\u0301"=>"\u01FB", "\u00C6\u0301"=>"\u01FC", "\u00E6\u0301"=>"\u01FD", "\u00D8\u0301"=>"\u01FE", "\u00F8\u0301"=>"\u01FF",
- "A\u030F"=>"\u0200", "a\u030F"=>"\u0201", "A\u0311"=>"\u0202", "a\u0311"=>"\u0203", "E\u030F"=>"\u0204", "e\u030F"=>"\u0205", "E\u0311"=>"\u0206", "e\u0311"=>"\u0207",
- "I\u030F"=>"\u0208", "i\u030F"=>"\u0209", "I\u0311"=>"\u020A", "i\u0311"=>"\u020B", "O\u030F"=>"\u020C", "o\u030F"=>"\u020D", "O\u0311"=>"\u020E", "o\u0311"=>"\u020F",
- "R\u030F"=>"\u0210", "r\u030F"=>"\u0211", "R\u0311"=>"\u0212", "r\u0311"=>"\u0213", "U\u030F"=>"\u0214", "u\u030F"=>"\u0215", "U\u0311"=>"\u0216", "u\u0311"=>"\u0217",
- "S\u0326"=>"\u0218", "s\u0326"=>"\u0219", "T\u0326"=>"\u021A", "t\u0326"=>"\u021B", "H\u030C"=>"\u021E", "h\u030C"=>"\u021F", "A\u0307"=>"\u0226", "a\u0307"=>"\u0227",
- "E\u0327"=>"\u0228", "e\u0327"=>"\u0229", "\u00D6\u0304"=>"\u022A", "\u00F6\u0304"=>"\u022B", "\u00D5\u0304"=>"\u022C", "\u00F5\u0304"=>"\u022D", "O\u0307"=>"\u022E", "o\u0307"=>"\u022F",
- "\u022E\u0304"=>"\u0230", "\u022F\u0304"=>"\u0231", "Y\u0304"=>"\u0232", "y\u0304"=>"\u0233", "\u00A8\u0301"=>"\u0385", "\u0391\u0301"=>"\u0386", "\u0395\u0301"=>"\u0388", "\u0397\u0301"=>"\u0389",
- "\u0399\u0301"=>"\u038A", "\u039F\u0301"=>"\u038C", "\u03A5\u0301"=>"\u038E", "\u03A9\u0301"=>"\u038F", "\u03CA\u0301"=>"\u0390", "\u0399\u0308"=>"\u03AA", "\u03A5\u0308"=>"\u03AB", "\u03B1\u0301"=>"\u03AC",
- "\u03B5\u0301"=>"\u03AD", "\u03B7\u0301"=>"\u03AE", "\u03B9\u0301"=>"\u03AF", "\u03CB\u0301"=>"\u03B0", "\u03B9\u0308"=>"\u03CA", "\u03C5\u0308"=>"\u03CB", "\u03BF\u0301"=>"\u03CC", "\u03C5\u0301"=>"\u03CD",
- "\u03C9\u0301"=>"\u03CE", "\u03D2\u0301"=>"\u03D3", "\u03D2\u0308"=>"\u03D4", "\u0415\u0300"=>"\u0400", "\u0415\u0308"=>"\u0401", "\u0413\u0301"=>"\u0403", "\u0406\u0308"=>"\u0407", "\u041A\u0301"=>"\u040C",
- "\u0418\u0300"=>"\u040D", "\u0423\u0306"=>"\u040E", "\u0418\u0306"=>"\u0419", "\u0438\u0306"=>"\u0439", "\u0435\u0300"=>"\u0450", "\u0435\u0308"=>"\u0451", "\u0433\u0301"=>"\u0453", "\u0456\u0308"=>"\u0457",
- "\u043A\u0301"=>"\u045C", "\u0438\u0300"=>"\u045D", "\u0443\u0306"=>"\u045E", "\u0474\u030F"=>"\u0476", "\u0475\u030F"=>"\u0477", "\u0416\u0306"=>"\u04C1", "\u0436\u0306"=>"\u04C2", "\u0410\u0306"=>"\u04D0",
- "\u0430\u0306"=>"\u04D1", "\u0410\u0308"=>"\u04D2", "\u0430\u0308"=>"\u04D3", "\u0415\u0306"=>"\u04D6", "\u0435\u0306"=>"\u04D7", "\u04D8\u0308"=>"\u04DA", "\u04D9\u0308"=>"\u04DB", "\u0416\u0308"=>"\u04DC",
- "\u0436\u0308"=>"\u04DD", "\u0417\u0308"=>"\u04DE", "\u0437\u0308"=>"\u04DF", "\u0418\u0304"=>"\u04E2", "\u0438\u0304"=>"\u04E3", "\u0418\u0308"=>"\u04E4", "\u0438\u0308"=>"\u04E5", "\u041E\u0308"=>"\u04E6",
- "\u043E\u0308"=>"\u04E7", "\u04E8\u0308"=>"\u04EA", "\u04E9\u0308"=>"\u04EB", "\u042D\u0308"=>"\u04EC", "\u044D\u0308"=>"\u04ED", "\u0423\u0304"=>"\u04EE", "\u0443\u0304"=>"\u04EF", "\u0423\u0308"=>"\u04F0",
- "\u0443\u0308"=>"\u04F1", "\u0423\u030B"=>"\u04F2", "\u0443\u030B"=>"\u04F3", "\u0427\u0308"=>"\u04F4", "\u0447\u0308"=>"\u04F5", "\u042B\u0308"=>"\u04F8", "\u044B\u0308"=>"\u04F9", "\u0627\u0653"=>"\u0622",
- "\u0627\u0654"=>"\u0623", "\u0648\u0654"=>"\u0624", "\u0627\u0655"=>"\u0625", "\u064A\u0654"=>"\u0626", "\u06D5\u0654"=>"\u06C0", "\u06C1\u0654"=>"\u06C2", "\u06D2\u0654"=>"\u06D3", "\u0928\u093C"=>"\u0929",
- "\u0930\u093C"=>"\u0931", "\u0933\u093C"=>"\u0934", "\u09C7\u09BE"=>"\u09CB", "\u09C7\u09D7"=>"\u09CC", "\u0B47\u0B56"=>"\u0B48", "\u0B47\u0B3E"=>"\u0B4B", "\u0B47\u0B57"=>"\u0B4C", "\u0B92\u0BD7"=>"\u0B94",
- "\u0BC6\u0BBE"=>"\u0BCA", "\u0BC7\u0BBE"=>"\u0BCB", "\u0BC6\u0BD7"=>"\u0BCC", "\u0C46\u0C56"=>"\u0C48", "\u0CBF\u0CD5"=>"\u0CC0", "\u0CC6\u0CD5"=>"\u0CC7", "\u0CC6\u0CD6"=>"\u0CC8", "\u0CC6\u0CC2"=>"\u0CCA",
- "\u0CCA\u0CD5"=>"\u0CCB", "\u0D46\u0D3E"=>"\u0D4A", "\u0D47\u0D3E"=>"\u0D4B", "\u0D46\u0D57"=>"\u0D4C", "\u0DD9\u0DCA"=>"\u0DDA", "\u0DD9\u0DCF"=>"\u0DDC", "\u0DDC\u0DCA"=>"\u0DDD", "\u0DD9\u0DDF"=>"\u0DDE",
- "\u1025\u102E"=>"\u1026", "\u1B05\u1B35"=>"\u1B06", "\u1B07\u1B35"=>"\u1B08", "\u1B09\u1B35"=>"\u1B0A", "\u1B0B\u1B35"=>"\u1B0C", "\u1B0D\u1B35"=>"\u1B0E", "\u1B11\u1B35"=>"\u1B12", "\u1B3A\u1B35"=>"\u1B3B",
- "\u1B3C\u1B35"=>"\u1B3D", "\u1B3E\u1B35"=>"\u1B40", "\u1B3F\u1B35"=>"\u1B41", "\u1B42\u1B35"=>"\u1B43", "A\u0325"=>"\u1E00", "a\u0325"=>"\u1E01", "B\u0307"=>"\u1E02", "b\u0307"=>"\u1E03",
- "B\u0323"=>"\u1E04", "b\u0323"=>"\u1E05", "B\u0331"=>"\u1E06", "b\u0331"=>"\u1E07", "\u00C7\u0301"=>"\u1E08", "\u00E7\u0301"=>"\u1E09", "D\u0307"=>"\u1E0A", "d\u0307"=>"\u1E0B",
- "D\u0323"=>"\u1E0C", "d\u0323"=>"\u1E0D", "D\u0331"=>"\u1E0E", "d\u0331"=>"\u1E0F", "D\u0327"=>"\u1E10", "d\u0327"=>"\u1E11", "D\u032D"=>"\u1E12", "d\u032D"=>"\u1E13",
- "\u0112\u0300"=>"\u1E14", "\u0113\u0300"=>"\u1E15", "\u0112\u0301"=>"\u1E16", "\u0113\u0301"=>"\u1E17", "E\u032D"=>"\u1E18", "e\u032D"=>"\u1E19", "E\u0330"=>"\u1E1A", "e\u0330"=>"\u1E1B",
- "\u0228\u0306"=>"\u1E1C", "\u0229\u0306"=>"\u1E1D", "F\u0307"=>"\u1E1E", "f\u0307"=>"\u1E1F", "G\u0304"=>"\u1E20", "g\u0304"=>"\u1E21", "H\u0307"=>"\u1E22", "h\u0307"=>"\u1E23",
- "H\u0323"=>"\u1E24", "h\u0323"=>"\u1E25", "H\u0308"=>"\u1E26", "h\u0308"=>"\u1E27", "H\u0327"=>"\u1E28", "h\u0327"=>"\u1E29", "H\u032E"=>"\u1E2A", "h\u032E"=>"\u1E2B",
- "I\u0330"=>"\u1E2C", "i\u0330"=>"\u1E2D", "\u00CF\u0301"=>"\u1E2E", "\u00EF\u0301"=>"\u1E2F", "K\u0301"=>"\u1E30", "k\u0301"=>"\u1E31", "K\u0323"=>"\u1E32", "k\u0323"=>"\u1E33",
- "K\u0331"=>"\u1E34", "k\u0331"=>"\u1E35", "L\u0323"=>"\u1E36", "l\u0323"=>"\u1E37", "\u1E36\u0304"=>"\u1E38", "\u1E37\u0304"=>"\u1E39", "L\u0331"=>"\u1E3A", "l\u0331"=>"\u1E3B",
- "L\u032D"=>"\u1E3C", "l\u032D"=>"\u1E3D", "M\u0301"=>"\u1E3E", "m\u0301"=>"\u1E3F", "M\u0307"=>"\u1E40", "m\u0307"=>"\u1E41", "M\u0323"=>"\u1E42", "m\u0323"=>"\u1E43",
- "N\u0307"=>"\u1E44", "n\u0307"=>"\u1E45", "N\u0323"=>"\u1E46", "n\u0323"=>"\u1E47", "N\u0331"=>"\u1E48", "n\u0331"=>"\u1E49", "N\u032D"=>"\u1E4A", "n\u032D"=>"\u1E4B",
- "\u00D5\u0301"=>"\u1E4C", "\u00F5\u0301"=>"\u1E4D", "\u00D5\u0308"=>"\u1E4E", "\u00F5\u0308"=>"\u1E4F", "\u014C\u0300"=>"\u1E50", "\u014D\u0300"=>"\u1E51", "\u014C\u0301"=>"\u1E52", "\u014D\u0301"=>"\u1E53",
- "P\u0301"=>"\u1E54", "p\u0301"=>"\u1E55", "P\u0307"=>"\u1E56", "p\u0307"=>"\u1E57", "R\u0307"=>"\u1E58", "r\u0307"=>"\u1E59", "R\u0323"=>"\u1E5A", "r\u0323"=>"\u1E5B",
- "\u1E5A\u0304"=>"\u1E5C", "\u1E5B\u0304"=>"\u1E5D", "R\u0331"=>"\u1E5E", "r\u0331"=>"\u1E5F", "S\u0307"=>"\u1E60", "s\u0307"=>"\u1E61", "S\u0323"=>"\u1E62", "s\u0323"=>"\u1E63",
- "\u015A\u0307"=>"\u1E64", "\u015B\u0307"=>"\u1E65", "\u0160\u0307"=>"\u1E66", "\u0161\u0307"=>"\u1E67", "\u1E62\u0307"=>"\u1E68", "\u1E63\u0307"=>"\u1E69", "T\u0307"=>"\u1E6A", "t\u0307"=>"\u1E6B",
- "T\u0323"=>"\u1E6C", "t\u0323"=>"\u1E6D", "T\u0331"=>"\u1E6E", "t\u0331"=>"\u1E6F", "T\u032D"=>"\u1E70", "t\u032D"=>"\u1E71", "U\u0324"=>"\u1E72", "u\u0324"=>"\u1E73",
- "U\u0330"=>"\u1E74", "u\u0330"=>"\u1E75", "U\u032D"=>"\u1E76", "u\u032D"=>"\u1E77", "\u0168\u0301"=>"\u1E78", "\u0169\u0301"=>"\u1E79", "\u016A\u0308"=>"\u1E7A", "\u016B\u0308"=>"\u1E7B",
- "V\u0303"=>"\u1E7C", "v\u0303"=>"\u1E7D", "V\u0323"=>"\u1E7E", "v\u0323"=>"\u1E7F", "W\u0300"=>"\u1E80", "w\u0300"=>"\u1E81", "W\u0301"=>"\u1E82", "w\u0301"=>"\u1E83",
- "W\u0308"=>"\u1E84", "w\u0308"=>"\u1E85", "W\u0307"=>"\u1E86", "w\u0307"=>"\u1E87", "W\u0323"=>"\u1E88", "w\u0323"=>"\u1E89", "X\u0307"=>"\u1E8A", "x\u0307"=>"\u1E8B",
- "X\u0308"=>"\u1E8C", "x\u0308"=>"\u1E8D", "Y\u0307"=>"\u1E8E", "y\u0307"=>"\u1E8F", "Z\u0302"=>"\u1E90", "z\u0302"=>"\u1E91", "Z\u0323"=>"\u1E92", "z\u0323"=>"\u1E93",
- "Z\u0331"=>"\u1E94", "z\u0331"=>"\u1E95", "h\u0331"=>"\u1E96", "t\u0308"=>"\u1E97", "w\u030A"=>"\u1E98", "y\u030A"=>"\u1E99", "\u017F\u0307"=>"\u1E9B", "A\u0323"=>"\u1EA0",
- "a\u0323"=>"\u1EA1", "A\u0309"=>"\u1EA2", "a\u0309"=>"\u1EA3", "\u00C2\u0301"=>"\u1EA4", "\u00E2\u0301"=>"\u1EA5", "\u00C2\u0300"=>"\u1EA6", "\u00E2\u0300"=>"\u1EA7", "\u00C2\u0309"=>"\u1EA8",
- "\u00E2\u0309"=>"\u1EA9", "\u00C2\u0303"=>"\u1EAA", "\u00E2\u0303"=>"\u1EAB", "\u1EA0\u0302"=>"\u1EAC", "\u1EA1\u0302"=>"\u1EAD", "\u0102\u0301"=>"\u1EAE", "\u0103\u0301"=>"\u1EAF", "\u0102\u0300"=>"\u1EB0",
- "\u0103\u0300"=>"\u1EB1", "\u0102\u0309"=>"\u1EB2", "\u0103\u0309"=>"\u1EB3", "\u0102\u0303"=>"\u1EB4", "\u0103\u0303"=>"\u1EB5", "\u1EA0\u0306"=>"\u1EB6", "\u1EA1\u0306"=>"\u1EB7", "E\u0323"=>"\u1EB8",
- "e\u0323"=>"\u1EB9", "E\u0309"=>"\u1EBA", "e\u0309"=>"\u1EBB", "E\u0303"=>"\u1EBC", "e\u0303"=>"\u1EBD", "\u00CA\u0301"=>"\u1EBE", "\u00EA\u0301"=>"\u1EBF", "\u00CA\u0300"=>"\u1EC0",
- "\u00EA\u0300"=>"\u1EC1", "\u00CA\u0309"=>"\u1EC2", "\u00EA\u0309"=>"\u1EC3", "\u00CA\u0303"=>"\u1EC4", "\u00EA\u0303"=>"\u1EC5", "\u1EB8\u0302"=>"\u1EC6", "\u1EB9\u0302"=>"\u1EC7", "I\u0309"=>"\u1EC8",
- "i\u0309"=>"\u1EC9", "I\u0323"=>"\u1ECA", "i\u0323"=>"\u1ECB", "O\u0323"=>"\u1ECC", "o\u0323"=>"\u1ECD", "O\u0309"=>"\u1ECE", "o\u0309"=>"\u1ECF", "\u00D4\u0301"=>"\u1ED0",
- "\u00F4\u0301"=>"\u1ED1", "\u00D4\u0300"=>"\u1ED2", "\u00F4\u0300"=>"\u1ED3", "\u00D4\u0309"=>"\u1ED4", "\u00F4\u0309"=>"\u1ED5", "\u00D4\u0303"=>"\u1ED6", "\u00F4\u0303"=>"\u1ED7", "\u1ECC\u0302"=>"\u1ED8",
- "\u1ECD\u0302"=>"\u1ED9", "\u01A0\u0301"=>"\u1EDA", "\u01A1\u0301"=>"\u1EDB", "\u01A0\u0300"=>"\u1EDC", "\u01A1\u0300"=>"\u1EDD", "\u01A0\u0309"=>"\u1EDE", "\u01A1\u0309"=>"\u1EDF", "\u01A0\u0303"=>"\u1EE0",
- "\u01A1\u0303"=>"\u1EE1", "\u01A0\u0323"=>"\u1EE2", "\u01A1\u0323"=>"\u1EE3", "U\u0323"=>"\u1EE4", "u\u0323"=>"\u1EE5", "U\u0309"=>"\u1EE6", "u\u0309"=>"\u1EE7", "\u01AF\u0301"=>"\u1EE8",
- "\u01B0\u0301"=>"\u1EE9", "\u01AF\u0300"=>"\u1EEA", "\u01B0\u0300"=>"\u1EEB", "\u01AF\u0309"=>"\u1EEC", "\u01B0\u0309"=>"\u1EED", "\u01AF\u0303"=>"\u1EEE", "\u01B0\u0303"=>"\u1EEF", "\u01AF\u0323"=>"\u1EF0",
- "\u01B0\u0323"=>"\u1EF1", "Y\u0300"=>"\u1EF2", "y\u0300"=>"\u1EF3", "Y\u0323"=>"\u1EF4", "y\u0323"=>"\u1EF5", "Y\u0309"=>"\u1EF6", "y\u0309"=>"\u1EF7", "Y\u0303"=>"\u1EF8",
- "y\u0303"=>"\u1EF9", "\u03B1\u0313"=>"\u1F00", "\u03B1\u0314"=>"\u1F01", "\u1F00\u0300"=>"\u1F02", "\u1F01\u0300"=>"\u1F03", "\u1F00\u0301"=>"\u1F04", "\u1F01\u0301"=>"\u1F05", "\u1F00\u0342"=>"\u1F06",
- "\u1F01\u0342"=>"\u1F07", "\u0391\u0313"=>"\u1F08", "\u0391\u0314"=>"\u1F09", "\u1F08\u0300"=>"\u1F0A", "\u1F09\u0300"=>"\u1F0B", "\u1F08\u0301"=>"\u1F0C", "\u1F09\u0301"=>"\u1F0D", "\u1F08\u0342"=>"\u1F0E",
- "\u1F09\u0342"=>"\u1F0F", "\u03B5\u0313"=>"\u1F10", "\u03B5\u0314"=>"\u1F11", "\u1F10\u0300"=>"\u1F12", "\u1F11\u0300"=>"\u1F13", "\u1F10\u0301"=>"\u1F14", "\u1F11\u0301"=>"\u1F15", "\u0395\u0313"=>"\u1F18",
- "\u0395\u0314"=>"\u1F19", "\u1F18\u0300"=>"\u1F1A", "\u1F19\u0300"=>"\u1F1B", "\u1F18\u0301"=>"\u1F1C", "\u1F19\u0301"=>"\u1F1D", "\u03B7\u0313"=>"\u1F20", "\u03B7\u0314"=>"\u1F21", "\u1F20\u0300"=>"\u1F22",
- "\u1F21\u0300"=>"\u1F23", "\u1F20\u0301"=>"\u1F24", "\u1F21\u0301"=>"\u1F25", "\u1F20\u0342"=>"\u1F26", "\u1F21\u0342"=>"\u1F27", "\u0397\u0313"=>"\u1F28", "\u0397\u0314"=>"\u1F29", "\u1F28\u0300"=>"\u1F2A",
- "\u1F29\u0300"=>"\u1F2B", "\u1F28\u0301"=>"\u1F2C", "\u1F29\u0301"=>"\u1F2D", "\u1F28\u0342"=>"\u1F2E", "\u1F29\u0342"=>"\u1F2F", "\u03B9\u0313"=>"\u1F30", "\u03B9\u0314"=>"\u1F31", "\u1F30\u0300"=>"\u1F32",
- "\u1F31\u0300"=>"\u1F33", "\u1F30\u0301"=>"\u1F34", "\u1F31\u0301"=>"\u1F35", "\u1F30\u0342"=>"\u1F36", "\u1F31\u0342"=>"\u1F37", "\u0399\u0313"=>"\u1F38", "\u0399\u0314"=>"\u1F39", "\u1F38\u0300"=>"\u1F3A",
- "\u1F39\u0300"=>"\u1F3B", "\u1F38\u0301"=>"\u1F3C", "\u1F39\u0301"=>"\u1F3D", "\u1F38\u0342"=>"\u1F3E", "\u1F39\u0342"=>"\u1F3F", "\u03BF\u0313"=>"\u1F40", "\u03BF\u0314"=>"\u1F41", "\u1F40\u0300"=>"\u1F42",
- "\u1F41\u0300"=>"\u1F43", "\u1F40\u0301"=>"\u1F44", "\u1F41\u0301"=>"\u1F45", "\u039F\u0313"=>"\u1F48", "\u039F\u0314"=>"\u1F49", "\u1F48\u0300"=>"\u1F4A", "\u1F49\u0300"=>"\u1F4B", "\u1F48\u0301"=>"\u1F4C",
- "\u1F49\u0301"=>"\u1F4D", "\u03C5\u0313"=>"\u1F50", "\u03C5\u0314"=>"\u1F51", "\u1F50\u0300"=>"\u1F52", "\u1F51\u0300"=>"\u1F53", "\u1F50\u0301"=>"\u1F54", "\u1F51\u0301"=>"\u1F55", "\u1F50\u0342"=>"\u1F56",
- "\u1F51\u0342"=>"\u1F57", "\u03A5\u0314"=>"\u1F59", "\u1F59\u0300"=>"\u1F5B", "\u1F59\u0301"=>"\u1F5D", "\u1F59\u0342"=>"\u1F5F", "\u03C9\u0313"=>"\u1F60", "\u03C9\u0314"=>"\u1F61", "\u1F60\u0300"=>"\u1F62",
- "\u1F61\u0300"=>"\u1F63", "\u1F60\u0301"=>"\u1F64", "\u1F61\u0301"=>"\u1F65", "\u1F60\u0342"=>"\u1F66", "\u1F61\u0342"=>"\u1F67", "\u03A9\u0313"=>"\u1F68", "\u03A9\u0314"=>"\u1F69", "\u1F68\u0300"=>"\u1F6A",
- "\u1F69\u0300"=>"\u1F6B", "\u1F68\u0301"=>"\u1F6C", "\u1F69\u0301"=>"\u1F6D", "\u1F68\u0342"=>"\u1F6E", "\u1F69\u0342"=>"\u1F6F", "\u03B1\u0300"=>"\u1F70", "\u03B5\u0300"=>"\u1F72", "\u03B7\u0300"=>"\u1F74",
- "\u03B9\u0300"=>"\u1F76", "\u03BF\u0300"=>"\u1F78", "\u03C5\u0300"=>"\u1F7A", "\u03C9\u0300"=>"\u1F7C", "\u1F00\u0345"=>"\u1F80", "\u1F01\u0345"=>"\u1F81", "\u1F02\u0345"=>"\u1F82", "\u1F03\u0345"=>"\u1F83",
- "\u1F04\u0345"=>"\u1F84", "\u1F05\u0345"=>"\u1F85", "\u1F06\u0345"=>"\u1F86", "\u1F07\u0345"=>"\u1F87", "\u1F08\u0345"=>"\u1F88", "\u1F09\u0345"=>"\u1F89", "\u1F0A\u0345"=>"\u1F8A", "\u1F0B\u0345"=>"\u1F8B",
- "\u1F0C\u0345"=>"\u1F8C", "\u1F0D\u0345"=>"\u1F8D", "\u1F0E\u0345"=>"\u1F8E", "\u1F0F\u0345"=>"\u1F8F", "\u1F20\u0345"=>"\u1F90", "\u1F21\u0345"=>"\u1F91", "\u1F22\u0345"=>"\u1F92", "\u1F23\u0345"=>"\u1F93",
- "\u1F24\u0345"=>"\u1F94", "\u1F25\u0345"=>"\u1F95", "\u1F26\u0345"=>"\u1F96", "\u1F27\u0345"=>"\u1F97", "\u1F28\u0345"=>"\u1F98", "\u1F29\u0345"=>"\u1F99", "\u1F2A\u0345"=>"\u1F9A", "\u1F2B\u0345"=>"\u1F9B",
- "\u1F2C\u0345"=>"\u1F9C", "\u1F2D\u0345"=>"\u1F9D", "\u1F2E\u0345"=>"\u1F9E", "\u1F2F\u0345"=>"\u1F9F", "\u1F60\u0345"=>"\u1FA0", "\u1F61\u0345"=>"\u1FA1", "\u1F62\u0345"=>"\u1FA2", "\u1F63\u0345"=>"\u1FA3",
- "\u1F64\u0345"=>"\u1FA4", "\u1F65\u0345"=>"\u1FA5", "\u1F66\u0345"=>"\u1FA6", "\u1F67\u0345"=>"\u1FA7", "\u1F68\u0345"=>"\u1FA8", "\u1F69\u0345"=>"\u1FA9", "\u1F6A\u0345"=>"\u1FAA", "\u1F6B\u0345"=>"\u1FAB",
- "\u1F6C\u0345"=>"\u1FAC", "\u1F6D\u0345"=>"\u1FAD", "\u1F6E\u0345"=>"\u1FAE", "\u1F6F\u0345"=>"\u1FAF", "\u03B1\u0306"=>"\u1FB0", "\u03B1\u0304"=>"\u1FB1", "\u1F70\u0345"=>"\u1FB2", "\u03B1\u0345"=>"\u1FB3",
- "\u03AC\u0345"=>"\u1FB4", "\u03B1\u0342"=>"\u1FB6", "\u1FB6\u0345"=>"\u1FB7", "\u0391\u0306"=>"\u1FB8", "\u0391\u0304"=>"\u1FB9", "\u0391\u0300"=>"\u1FBA", "\u0391\u0345"=>"\u1FBC", "\u00A8\u0342"=>"\u1FC1",
- "\u1F74\u0345"=>"\u1FC2", "\u03B7\u0345"=>"\u1FC3", "\u03AE\u0345"=>"\u1FC4", "\u03B7\u0342"=>"\u1FC6", "\u1FC6\u0345"=>"\u1FC7", "\u0395\u0300"=>"\u1FC8", "\u0397\u0300"=>"\u1FCA", "\u0397\u0345"=>"\u1FCC",
- "\u1FBF\u0300"=>"\u1FCD", "\u1FBF\u0301"=>"\u1FCE", "\u1FBF\u0342"=>"\u1FCF", "\u03B9\u0306"=>"\u1FD0", "\u03B9\u0304"=>"\u1FD1", "\u03CA\u0300"=>"\u1FD2", "\u03B9\u0342"=>"\u1FD6", "\u03CA\u0342"=>"\u1FD7",
- "\u0399\u0306"=>"\u1FD8", "\u0399\u0304"=>"\u1FD9", "\u0399\u0300"=>"\u1FDA", "\u1FFE\u0300"=>"\u1FDD", "\u1FFE\u0301"=>"\u1FDE", "\u1FFE\u0342"=>"\u1FDF", "\u03C5\u0306"=>"\u1FE0", "\u03C5\u0304"=>"\u1FE1",
- "\u03CB\u0300"=>"\u1FE2", "\u03C1\u0313"=>"\u1FE4", "\u03C1\u0314"=>"\u1FE5", "\u03C5\u0342"=>"\u1FE6", "\u03CB\u0342"=>"\u1FE7", "\u03A5\u0306"=>"\u1FE8", "\u03A5\u0304"=>"\u1FE9", "\u03A5\u0300"=>"\u1FEA",
- "\u03A1\u0314"=>"\u1FEC", "\u00A8\u0300"=>"\u1FED", "\u1F7C\u0345"=>"\u1FF2", "\u03C9\u0345"=>"\u1FF3", "\u03CE\u0345"=>"\u1FF4", "\u03C9\u0342"=>"\u1FF6", "\u1FF6\u0345"=>"\u1FF7", "\u039F\u0300"=>"\u1FF8",
- "\u03A9\u0300"=>"\u1FFA", "\u03A9\u0345"=>"\u1FFC", "\u2190\u0338"=>"\u219A", "\u2192\u0338"=>"\u219B", "\u2194\u0338"=>"\u21AE", "\u21D0\u0338"=>"\u21CD", "\u21D4\u0338"=>"\u21CE", "\u21D2\u0338"=>"\u21CF",
- "\u2203\u0338"=>"\u2204", "\u2208\u0338"=>"\u2209", "\u220B\u0338"=>"\u220C", "\u2223\u0338"=>"\u2224", "\u2225\u0338"=>"\u2226", "\u223C\u0338"=>"\u2241", "\u2243\u0338"=>"\u2244", "\u2245\u0338"=>"\u2247",
- "\u2248\u0338"=>"\u2249", "=\u0338"=>"\u2260", "\u2261\u0338"=>"\u2262", "\u224D\u0338"=>"\u226D", "<\u0338"=>"\u226E", ">\u0338"=>"\u226F", "\u2264\u0338"=>"\u2270", "\u2265\u0338"=>"\u2271",
- "\u2272\u0338"=>"\u2274", "\u2273\u0338"=>"\u2275", "\u2276\u0338"=>"\u2278", "\u2277\u0338"=>"\u2279", "\u227A\u0338"=>"\u2280", "\u227B\u0338"=>"\u2281", "\u2282\u0338"=>"\u2284", "\u2283\u0338"=>"\u2285",
- "\u2286\u0338"=>"\u2288", "\u2287\u0338"=>"\u2289", "\u22A2\u0338"=>"\u22AC", "\u22A8\u0338"=>"\u22AD", "\u22A9\u0338"=>"\u22AE", "\u22AB\u0338"=>"\u22AF", "\u227C\u0338"=>"\u22E0", "\u227D\u0338"=>"\u22E1",
- "\u2291\u0338"=>"\u22E2", "\u2292\u0338"=>"\u22E3", "\u22B2\u0338"=>"\u22EA", "\u22B3\u0338"=>"\u22EB", "\u22B4\u0338"=>"\u22EC", "\u22B5\u0338"=>"\u22ED", "\u304B\u3099"=>"\u304C", "\u304D\u3099"=>"\u304E",
- "\u304F\u3099"=>"\u3050", "\u3051\u3099"=>"\u3052", "\u3053\u3099"=>"\u3054", "\u3055\u3099"=>"\u3056", "\u3057\u3099"=>"\u3058", "\u3059\u3099"=>"\u305A", "\u305B\u3099"=>"\u305C", "\u305D\u3099"=>"\u305E",
- "\u305F\u3099"=>"\u3060", "\u3061\u3099"=>"\u3062", "\u3064\u3099"=>"\u3065", "\u3066\u3099"=>"\u3067", "\u3068\u3099"=>"\u3069", "\u306F\u3099"=>"\u3070", "\u306F\u309A"=>"\u3071", "\u3072\u3099"=>"\u3073",
- "\u3072\u309A"=>"\u3074", "\u3075\u3099"=>"\u3076", "\u3075\u309A"=>"\u3077", "\u3078\u3099"=>"\u3079", "\u3078\u309A"=>"\u307A", "\u307B\u3099"=>"\u307C", "\u307B\u309A"=>"\u307D", "\u3046\u3099"=>"\u3094",
- "\u309D\u3099"=>"\u309E", "\u30AB\u3099"=>"\u30AC", "\u30AD\u3099"=>"\u30AE", "\u30AF\u3099"=>"\u30B0", "\u30B1\u3099"=>"\u30B2", "\u30B3\u3099"=>"\u30B4", "\u30B5\u3099"=>"\u30B6", "\u30B7\u3099"=>"\u30B8",
- "\u30B9\u3099"=>"\u30BA", "\u30BB\u3099"=>"\u30BC", "\u30BD\u3099"=>"\u30BE", "\u30BF\u3099"=>"\u30C0", "\u30C1\u3099"=>"\u30C2", "\u30C4\u3099"=>"\u30C5", "\u30C6\u3099"=>"\u30C7", "\u30C8\u3099"=>"\u30C9",
- "\u30CF\u3099"=>"\u30D0", "\u30CF\u309A"=>"\u30D1", "\u30D2\u3099"=>"\u30D3", "\u30D2\u309A"=>"\u30D4", "\u30D5\u3099"=>"\u30D6", "\u30D5\u309A"=>"\u30D7", "\u30D8\u3099"=>"\u30D9", "\u30D8\u309A"=>"\u30DA",
- "\u30DB\u3099"=>"\u30DC", "\u30DB\u309A"=>"\u30DD", "\u30A6\u3099"=>"\u30F4", "\u30EF\u3099"=>"\u30F7", "\u30F0\u3099"=>"\u30F8", "\u30F1\u3099"=>"\u30F9", "\u30F2\u3099"=>"\u30FA", "\u30FD\u3099"=>"\u30FE",
- "\u{11099}\u{110BA}"=>"\u{1109A}", "\u{1109B}\u{110BA}"=>"\u{1109C}", "\u{110A5}\u{110BA}"=>"\u{110AB}", "\u{11131}\u{11127}"=>"\u{1112E}", "\u{11132}\u{11127}"=>"\u{1112F}", "\u{11347}\u{1133E}"=>"\u{1134B}", "\u{11347}\u{11357}"=>"\u{1134C}", "\u{114B9}\u{114BA}"=>"\u{114BB}",
- "\u{114B9}\u{114B0}"=>"\u{114BC}", "\u{114B9}\u{114BD}"=>"\u{114BE}", "\u{115B8}\u{115AF}"=>"\u{115BA}", "\u{115B9}\u{115AF}"=>"\u{115BB}",
+ "A\u0300"=>"\u00C0",
+ "A\u0301"=>"\u00C1",
+ "A\u0302"=>"\u00C2",
+ "A\u0303"=>"\u00C3",
+ "A\u0308"=>"\u00C4",
+ "A\u030A"=>"\u00C5",
+ "C\u0327"=>"\u00C7",
+ "E\u0300"=>"\u00C8",
+ "E\u0301"=>"\u00C9",
+ "E\u0302"=>"\u00CA",
+ "E\u0308"=>"\u00CB",
+ "I\u0300"=>"\u00CC",
+ "I\u0301"=>"\u00CD",
+ "I\u0302"=>"\u00CE",
+ "I\u0308"=>"\u00CF",
+ "N\u0303"=>"\u00D1",
+ "O\u0300"=>"\u00D2",
+ "O\u0301"=>"\u00D3",
+ "O\u0302"=>"\u00D4",
+ "O\u0303"=>"\u00D5",
+ "O\u0308"=>"\u00D6",
+ "U\u0300"=>"\u00D9",
+ "U\u0301"=>"\u00DA",
+ "U\u0302"=>"\u00DB",
+ "U\u0308"=>"\u00DC",
+ "Y\u0301"=>"\u00DD",
+ "a\u0300"=>"\u00E0",
+ "a\u0301"=>"\u00E1",
+ "a\u0302"=>"\u00E2",
+ "a\u0303"=>"\u00E3",
+ "a\u0308"=>"\u00E4",
+ "a\u030A"=>"\u00E5",
+ "c\u0327"=>"\u00E7",
+ "e\u0300"=>"\u00E8",
+ "e\u0301"=>"\u00E9",
+ "e\u0302"=>"\u00EA",
+ "e\u0308"=>"\u00EB",
+ "i\u0300"=>"\u00EC",
+ "i\u0301"=>"\u00ED",
+ "i\u0302"=>"\u00EE",
+ "i\u0308"=>"\u00EF",
+ "n\u0303"=>"\u00F1",
+ "o\u0300"=>"\u00F2",
+ "o\u0301"=>"\u00F3",
+ "o\u0302"=>"\u00F4",
+ "o\u0303"=>"\u00F5",
+ "o\u0308"=>"\u00F6",
+ "u\u0300"=>"\u00F9",
+ "u\u0301"=>"\u00FA",
+ "u\u0302"=>"\u00FB",
+ "u\u0308"=>"\u00FC",
+ "y\u0301"=>"\u00FD",
+ "y\u0308"=>"\u00FF",
+ "A\u0304"=>"\u0100",
+ "a\u0304"=>"\u0101",
+ "A\u0306"=>"\u0102",
+ "a\u0306"=>"\u0103",
+ "A\u0328"=>"\u0104",
+ "a\u0328"=>"\u0105",
+ "C\u0301"=>"\u0106",
+ "c\u0301"=>"\u0107",
+ "C\u0302"=>"\u0108",
+ "c\u0302"=>"\u0109",
+ "C\u0307"=>"\u010A",
+ "c\u0307"=>"\u010B",
+ "C\u030C"=>"\u010C",
+ "c\u030C"=>"\u010D",
+ "D\u030C"=>"\u010E",
+ "d\u030C"=>"\u010F",
+ "E\u0304"=>"\u0112",
+ "e\u0304"=>"\u0113",
+ "E\u0306"=>"\u0114",
+ "e\u0306"=>"\u0115",
+ "E\u0307"=>"\u0116",
+ "e\u0307"=>"\u0117",
+ "E\u0328"=>"\u0118",
+ "e\u0328"=>"\u0119",
+ "E\u030C"=>"\u011A",
+ "e\u030C"=>"\u011B",
+ "G\u0302"=>"\u011C",
+ "g\u0302"=>"\u011D",
+ "G\u0306"=>"\u011E",
+ "g\u0306"=>"\u011F",
+ "G\u0307"=>"\u0120",
+ "g\u0307"=>"\u0121",
+ "G\u0327"=>"\u0122",
+ "g\u0327"=>"\u0123",
+ "H\u0302"=>"\u0124",
+ "h\u0302"=>"\u0125",
+ "I\u0303"=>"\u0128",
+ "i\u0303"=>"\u0129",
+ "I\u0304"=>"\u012A",
+ "i\u0304"=>"\u012B",
+ "I\u0306"=>"\u012C",
+ "i\u0306"=>"\u012D",
+ "I\u0328"=>"\u012E",
+ "i\u0328"=>"\u012F",
+ "I\u0307"=>"\u0130",
+ "J\u0302"=>"\u0134",
+ "j\u0302"=>"\u0135",
+ "K\u0327"=>"\u0136",
+ "k\u0327"=>"\u0137",
+ "L\u0301"=>"\u0139",
+ "l\u0301"=>"\u013A",
+ "L\u0327"=>"\u013B",
+ "l\u0327"=>"\u013C",
+ "L\u030C"=>"\u013D",
+ "l\u030C"=>"\u013E",
+ "N\u0301"=>"\u0143",
+ "n\u0301"=>"\u0144",
+ "N\u0327"=>"\u0145",
+ "n\u0327"=>"\u0146",
+ "N\u030C"=>"\u0147",
+ "n\u030C"=>"\u0148",
+ "O\u0304"=>"\u014C",
+ "o\u0304"=>"\u014D",
+ "O\u0306"=>"\u014E",
+ "o\u0306"=>"\u014F",
+ "O\u030B"=>"\u0150",
+ "o\u030B"=>"\u0151",
+ "R\u0301"=>"\u0154",
+ "r\u0301"=>"\u0155",
+ "R\u0327"=>"\u0156",
+ "r\u0327"=>"\u0157",
+ "R\u030C"=>"\u0158",
+ "r\u030C"=>"\u0159",
+ "S\u0301"=>"\u015A",
+ "s\u0301"=>"\u015B",
+ "S\u0302"=>"\u015C",
+ "s\u0302"=>"\u015D",
+ "S\u0327"=>"\u015E",
+ "s\u0327"=>"\u015F",
+ "S\u030C"=>"\u0160",
+ "s\u030C"=>"\u0161",
+ "T\u0327"=>"\u0162",
+ "t\u0327"=>"\u0163",
+ "T\u030C"=>"\u0164",
+ "t\u030C"=>"\u0165",
+ "U\u0303"=>"\u0168",
+ "u\u0303"=>"\u0169",
+ "U\u0304"=>"\u016A",
+ "u\u0304"=>"\u016B",
+ "U\u0306"=>"\u016C",
+ "u\u0306"=>"\u016D",
+ "U\u030A"=>"\u016E",
+ "u\u030A"=>"\u016F",
+ "U\u030B"=>"\u0170",
+ "u\u030B"=>"\u0171",
+ "U\u0328"=>"\u0172",
+ "u\u0328"=>"\u0173",
+ "W\u0302"=>"\u0174",
+ "w\u0302"=>"\u0175",
+ "Y\u0302"=>"\u0176",
+ "y\u0302"=>"\u0177",
+ "Y\u0308"=>"\u0178",
+ "Z\u0301"=>"\u0179",
+ "z\u0301"=>"\u017A",
+ "Z\u0307"=>"\u017B",
+ "z\u0307"=>"\u017C",
+ "Z\u030C"=>"\u017D",
+ "z\u030C"=>"\u017E",
+ "O\u031B"=>"\u01A0",
+ "o\u031B"=>"\u01A1",
+ "U\u031B"=>"\u01AF",
+ "u\u031B"=>"\u01B0",
+ "A\u030C"=>"\u01CD",
+ "a\u030C"=>"\u01CE",
+ "I\u030C"=>"\u01CF",
+ "i\u030C"=>"\u01D0",
+ "O\u030C"=>"\u01D1",
+ "o\u030C"=>"\u01D2",
+ "U\u030C"=>"\u01D3",
+ "u\u030C"=>"\u01D4",
+ "\u00DC\u0304"=>"\u01D5",
+ "\u00FC\u0304"=>"\u01D6",
+ "\u00DC\u0301"=>"\u01D7",
+ "\u00FC\u0301"=>"\u01D8",
+ "\u00DC\u030C"=>"\u01D9",
+ "\u00FC\u030C"=>"\u01DA",
+ "\u00DC\u0300"=>"\u01DB",
+ "\u00FC\u0300"=>"\u01DC",
+ "\u00C4\u0304"=>"\u01DE",
+ "\u00E4\u0304"=>"\u01DF",
+ "\u0226\u0304"=>"\u01E0",
+ "\u0227\u0304"=>"\u01E1",
+ "\u00C6\u0304"=>"\u01E2",
+ "\u00E6\u0304"=>"\u01E3",
+ "G\u030C"=>"\u01E6",
+ "g\u030C"=>"\u01E7",
+ "K\u030C"=>"\u01E8",
+ "k\u030C"=>"\u01E9",
+ "O\u0328"=>"\u01EA",
+ "o\u0328"=>"\u01EB",
+ "\u01EA\u0304"=>"\u01EC",
+ "\u01EB\u0304"=>"\u01ED",
+ "\u01B7\u030C"=>"\u01EE",
+ "\u0292\u030C"=>"\u01EF",
+ "j\u030C"=>"\u01F0",
+ "G\u0301"=>"\u01F4",
+ "g\u0301"=>"\u01F5",
+ "N\u0300"=>"\u01F8",
+ "n\u0300"=>"\u01F9",
+ "\u00C5\u0301"=>"\u01FA",
+ "\u00E5\u0301"=>"\u01FB",
+ "\u00C6\u0301"=>"\u01FC",
+ "\u00E6\u0301"=>"\u01FD",
+ "\u00D8\u0301"=>"\u01FE",
+ "\u00F8\u0301"=>"\u01FF",
+ "A\u030F"=>"\u0200",
+ "a\u030F"=>"\u0201",
+ "A\u0311"=>"\u0202",
+ "a\u0311"=>"\u0203",
+ "E\u030F"=>"\u0204",
+ "e\u030F"=>"\u0205",
+ "E\u0311"=>"\u0206",
+ "e\u0311"=>"\u0207",
+ "I\u030F"=>"\u0208",
+ "i\u030F"=>"\u0209",
+ "I\u0311"=>"\u020A",
+ "i\u0311"=>"\u020B",
+ "O\u030F"=>"\u020C",
+ "o\u030F"=>"\u020D",
+ "O\u0311"=>"\u020E",
+ "o\u0311"=>"\u020F",
+ "R\u030F"=>"\u0210",
+ "r\u030F"=>"\u0211",
+ "R\u0311"=>"\u0212",
+ "r\u0311"=>"\u0213",
+ "U\u030F"=>"\u0214",
+ "u\u030F"=>"\u0215",
+ "U\u0311"=>"\u0216",
+ "u\u0311"=>"\u0217",
+ "S\u0326"=>"\u0218",
+ "s\u0326"=>"\u0219",
+ "T\u0326"=>"\u021A",
+ "t\u0326"=>"\u021B",
+ "H\u030C"=>"\u021E",
+ "h\u030C"=>"\u021F",
+ "A\u0307"=>"\u0226",
+ "a\u0307"=>"\u0227",
+ "E\u0327"=>"\u0228",
+ "e\u0327"=>"\u0229",
+ "\u00D6\u0304"=>"\u022A",
+ "\u00F6\u0304"=>"\u022B",
+ "\u00D5\u0304"=>"\u022C",
+ "\u00F5\u0304"=>"\u022D",
+ "O\u0307"=>"\u022E",
+ "o\u0307"=>"\u022F",
+ "\u022E\u0304"=>"\u0230",
+ "\u022F\u0304"=>"\u0231",
+ "Y\u0304"=>"\u0232",
+ "y\u0304"=>"\u0233",
+ "\u00A8\u0301"=>"\u0385",
+ "\u0391\u0301"=>"\u0386",
+ "\u0395\u0301"=>"\u0388",
+ "\u0397\u0301"=>"\u0389",
+ "\u0399\u0301"=>"\u038A",
+ "\u039F\u0301"=>"\u038C",
+ "\u03A5\u0301"=>"\u038E",
+ "\u03A9\u0301"=>"\u038F",
+ "\u03CA\u0301"=>"\u0390",
+ "\u0399\u0308"=>"\u03AA",
+ "\u03A5\u0308"=>"\u03AB",
+ "\u03B1\u0301"=>"\u03AC",
+ "\u03B5\u0301"=>"\u03AD",
+ "\u03B7\u0301"=>"\u03AE",
+ "\u03B9\u0301"=>"\u03AF",
+ "\u03CB\u0301"=>"\u03B0",
+ "\u03B9\u0308"=>"\u03CA",
+ "\u03C5\u0308"=>"\u03CB",
+ "\u03BF\u0301"=>"\u03CC",
+ "\u03C5\u0301"=>"\u03CD",
+ "\u03C9\u0301"=>"\u03CE",
+ "\u03D2\u0301"=>"\u03D3",
+ "\u03D2\u0308"=>"\u03D4",
+ "\u0415\u0300"=>"\u0400",
+ "\u0415\u0308"=>"\u0401",
+ "\u0413\u0301"=>"\u0403",
+ "\u0406\u0308"=>"\u0407",
+ "\u041A\u0301"=>"\u040C",
+ "\u0418\u0300"=>"\u040D",
+ "\u0423\u0306"=>"\u040E",
+ "\u0418\u0306"=>"\u0419",
+ "\u0438\u0306"=>"\u0439",
+ "\u0435\u0300"=>"\u0450",
+ "\u0435\u0308"=>"\u0451",
+ "\u0433\u0301"=>"\u0453",
+ "\u0456\u0308"=>"\u0457",
+ "\u043A\u0301"=>"\u045C",
+ "\u0438\u0300"=>"\u045D",
+ "\u0443\u0306"=>"\u045E",
+ "\u0474\u030F"=>"\u0476",
+ "\u0475\u030F"=>"\u0477",
+ "\u0416\u0306"=>"\u04C1",
+ "\u0436\u0306"=>"\u04C2",
+ "\u0410\u0306"=>"\u04D0",
+ "\u0430\u0306"=>"\u04D1",
+ "\u0410\u0308"=>"\u04D2",
+ "\u0430\u0308"=>"\u04D3",
+ "\u0415\u0306"=>"\u04D6",
+ "\u0435\u0306"=>"\u04D7",
+ "\u04D8\u0308"=>"\u04DA",
+ "\u04D9\u0308"=>"\u04DB",
+ "\u0416\u0308"=>"\u04DC",
+ "\u0436\u0308"=>"\u04DD",
+ "\u0417\u0308"=>"\u04DE",
+ "\u0437\u0308"=>"\u04DF",
+ "\u0418\u0304"=>"\u04E2",
+ "\u0438\u0304"=>"\u04E3",
+ "\u0418\u0308"=>"\u04E4",
+ "\u0438\u0308"=>"\u04E5",
+ "\u041E\u0308"=>"\u04E6",
+ "\u043E\u0308"=>"\u04E7",
+ "\u04E8\u0308"=>"\u04EA",
+ "\u04E9\u0308"=>"\u04EB",
+ "\u042D\u0308"=>"\u04EC",
+ "\u044D\u0308"=>"\u04ED",
+ "\u0423\u0304"=>"\u04EE",
+ "\u0443\u0304"=>"\u04EF",
+ "\u0423\u0308"=>"\u04F0",
+ "\u0443\u0308"=>"\u04F1",
+ "\u0423\u030B"=>"\u04F2",
+ "\u0443\u030B"=>"\u04F3",
+ "\u0427\u0308"=>"\u04F4",
+ "\u0447\u0308"=>"\u04F5",
+ "\u042B\u0308"=>"\u04F8",
+ "\u044B\u0308"=>"\u04F9",
+ "\u0627\u0653"=>"\u0622",
+ "\u0627\u0654"=>"\u0623",
+ "\u0648\u0654"=>"\u0624",
+ "\u0627\u0655"=>"\u0625",
+ "\u064A\u0654"=>"\u0626",
+ "\u06D5\u0654"=>"\u06C0",
+ "\u06C1\u0654"=>"\u06C2",
+ "\u06D2\u0654"=>"\u06D3",
+ "\u0928\u093C"=>"\u0929",
+ "\u0930\u093C"=>"\u0931",
+ "\u0933\u093C"=>"\u0934",
+ "\u09C7\u09BE"=>"\u09CB",
+ "\u09C7\u09D7"=>"\u09CC",
+ "\u0B47\u0B56"=>"\u0B48",
+ "\u0B47\u0B3E"=>"\u0B4B",
+ "\u0B47\u0B57"=>"\u0B4C",
+ "\u0B92\u0BD7"=>"\u0B94",
+ "\u0BC6\u0BBE"=>"\u0BCA",
+ "\u0BC7\u0BBE"=>"\u0BCB",
+ "\u0BC6\u0BD7"=>"\u0BCC",
+ "\u0C46\u0C56"=>"\u0C48",
+ "\u0CBF\u0CD5"=>"\u0CC0",
+ "\u0CC6\u0CD5"=>"\u0CC7",
+ "\u0CC6\u0CD6"=>"\u0CC8",
+ "\u0CC6\u0CC2"=>"\u0CCA",
+ "\u0CCA\u0CD5"=>"\u0CCB",
+ "\u0D46\u0D3E"=>"\u0D4A",
+ "\u0D47\u0D3E"=>"\u0D4B",
+ "\u0D46\u0D57"=>"\u0D4C",
+ "\u0DD9\u0DCA"=>"\u0DDA",
+ "\u0DD9\u0DCF"=>"\u0DDC",
+ "\u0DDC\u0DCA"=>"\u0DDD",
+ "\u0DD9\u0DDF"=>"\u0DDE",
+ "\u1025\u102E"=>"\u1026",
+ "\u1B05\u1B35"=>"\u1B06",
+ "\u1B07\u1B35"=>"\u1B08",
+ "\u1B09\u1B35"=>"\u1B0A",
+ "\u1B0B\u1B35"=>"\u1B0C",
+ "\u1B0D\u1B35"=>"\u1B0E",
+ "\u1B11\u1B35"=>"\u1B12",
+ "\u1B3A\u1B35"=>"\u1B3B",
+ "\u1B3C\u1B35"=>"\u1B3D",
+ "\u1B3E\u1B35"=>"\u1B40",
+ "\u1B3F\u1B35"=>"\u1B41",
+ "\u1B42\u1B35"=>"\u1B43",
+ "A\u0325"=>"\u1E00",
+ "a\u0325"=>"\u1E01",
+ "B\u0307"=>"\u1E02",
+ "b\u0307"=>"\u1E03",
+ "B\u0323"=>"\u1E04",
+ "b\u0323"=>"\u1E05",
+ "B\u0331"=>"\u1E06",
+ "b\u0331"=>"\u1E07",
+ "\u00C7\u0301"=>"\u1E08",
+ "\u00E7\u0301"=>"\u1E09",
+ "D\u0307"=>"\u1E0A",
+ "d\u0307"=>"\u1E0B",
+ "D\u0323"=>"\u1E0C",
+ "d\u0323"=>"\u1E0D",
+ "D\u0331"=>"\u1E0E",
+ "d\u0331"=>"\u1E0F",
+ "D\u0327"=>"\u1E10",
+ "d\u0327"=>"\u1E11",
+ "D\u032D"=>"\u1E12",
+ "d\u032D"=>"\u1E13",
+ "\u0112\u0300"=>"\u1E14",
+ "\u0113\u0300"=>"\u1E15",
+ "\u0112\u0301"=>"\u1E16",
+ "\u0113\u0301"=>"\u1E17",
+ "E\u032D"=>"\u1E18",
+ "e\u032D"=>"\u1E19",
+ "E\u0330"=>"\u1E1A",
+ "e\u0330"=>"\u1E1B",
+ "\u0228\u0306"=>"\u1E1C",
+ "\u0229\u0306"=>"\u1E1D",
+ "F\u0307"=>"\u1E1E",
+ "f\u0307"=>"\u1E1F",
+ "G\u0304"=>"\u1E20",
+ "g\u0304"=>"\u1E21",
+ "H\u0307"=>"\u1E22",
+ "h\u0307"=>"\u1E23",
+ "H\u0323"=>"\u1E24",
+ "h\u0323"=>"\u1E25",
+ "H\u0308"=>"\u1E26",
+ "h\u0308"=>"\u1E27",
+ "H\u0327"=>"\u1E28",
+ "h\u0327"=>"\u1E29",
+ "H\u032E"=>"\u1E2A",
+ "h\u032E"=>"\u1E2B",
+ "I\u0330"=>"\u1E2C",
+ "i\u0330"=>"\u1E2D",
+ "\u00CF\u0301"=>"\u1E2E",
+ "\u00EF\u0301"=>"\u1E2F",
+ "K\u0301"=>"\u1E30",
+ "k\u0301"=>"\u1E31",
+ "K\u0323"=>"\u1E32",
+ "k\u0323"=>"\u1E33",
+ "K\u0331"=>"\u1E34",
+ "k\u0331"=>"\u1E35",
+ "L\u0323"=>"\u1E36",
+ "l\u0323"=>"\u1E37",
+ "\u1E36\u0304"=>"\u1E38",
+ "\u1E37\u0304"=>"\u1E39",
+ "L\u0331"=>"\u1E3A",
+ "l\u0331"=>"\u1E3B",
+ "L\u032D"=>"\u1E3C",
+ "l\u032D"=>"\u1E3D",
+ "M\u0301"=>"\u1E3E",
+ "m\u0301"=>"\u1E3F",
+ "M\u0307"=>"\u1E40",
+ "m\u0307"=>"\u1E41",
+ "M\u0323"=>"\u1E42",
+ "m\u0323"=>"\u1E43",
+ "N\u0307"=>"\u1E44",
+ "n\u0307"=>"\u1E45",
+ "N\u0323"=>"\u1E46",
+ "n\u0323"=>"\u1E47",
+ "N\u0331"=>"\u1E48",
+ "n\u0331"=>"\u1E49",
+ "N\u032D"=>"\u1E4A",
+ "n\u032D"=>"\u1E4B",
+ "\u00D5\u0301"=>"\u1E4C",
+ "\u00F5\u0301"=>"\u1E4D",
+ "\u00D5\u0308"=>"\u1E4E",
+ "\u00F5\u0308"=>"\u1E4F",
+ "\u014C\u0300"=>"\u1E50",
+ "\u014D\u0300"=>"\u1E51",
+ "\u014C\u0301"=>"\u1E52",
+ "\u014D\u0301"=>"\u1E53",
+ "P\u0301"=>"\u1E54",
+ "p\u0301"=>"\u1E55",
+ "P\u0307"=>"\u1E56",
+ "p\u0307"=>"\u1E57",
+ "R\u0307"=>"\u1E58",
+ "r\u0307"=>"\u1E59",
+ "R\u0323"=>"\u1E5A",
+ "r\u0323"=>"\u1E5B",
+ "\u1E5A\u0304"=>"\u1E5C",
+ "\u1E5B\u0304"=>"\u1E5D",
+ "R\u0331"=>"\u1E5E",
+ "r\u0331"=>"\u1E5F",
+ "S\u0307"=>"\u1E60",
+ "s\u0307"=>"\u1E61",
+ "S\u0323"=>"\u1E62",
+ "s\u0323"=>"\u1E63",
+ "\u015A\u0307"=>"\u1E64",
+ "\u015B\u0307"=>"\u1E65",
+ "\u0160\u0307"=>"\u1E66",
+ "\u0161\u0307"=>"\u1E67",
+ "\u1E62\u0307"=>"\u1E68",
+ "\u1E63\u0307"=>"\u1E69",
+ "T\u0307"=>"\u1E6A",
+ "t\u0307"=>"\u1E6B",
+ "T\u0323"=>"\u1E6C",
+ "t\u0323"=>"\u1E6D",
+ "T\u0331"=>"\u1E6E",
+ "t\u0331"=>"\u1E6F",
+ "T\u032D"=>"\u1E70",
+ "t\u032D"=>"\u1E71",
+ "U\u0324"=>"\u1E72",
+ "u\u0324"=>"\u1E73",
+ "U\u0330"=>"\u1E74",
+ "u\u0330"=>"\u1E75",
+ "U\u032D"=>"\u1E76",
+ "u\u032D"=>"\u1E77",
+ "\u0168\u0301"=>"\u1E78",
+ "\u0169\u0301"=>"\u1E79",
+ "\u016A\u0308"=>"\u1E7A",
+ "\u016B\u0308"=>"\u1E7B",
+ "V\u0303"=>"\u1E7C",
+ "v\u0303"=>"\u1E7D",
+ "V\u0323"=>"\u1E7E",
+ "v\u0323"=>"\u1E7F",
+ "W\u0300"=>"\u1E80",
+ "w\u0300"=>"\u1E81",
+ "W\u0301"=>"\u1E82",
+ "w\u0301"=>"\u1E83",
+ "W\u0308"=>"\u1E84",
+ "w\u0308"=>"\u1E85",
+ "W\u0307"=>"\u1E86",
+ "w\u0307"=>"\u1E87",
+ "W\u0323"=>"\u1E88",
+ "w\u0323"=>"\u1E89",
+ "X\u0307"=>"\u1E8A",
+ "x\u0307"=>"\u1E8B",
+ "X\u0308"=>"\u1E8C",
+ "x\u0308"=>"\u1E8D",
+ "Y\u0307"=>"\u1E8E",
+ "y\u0307"=>"\u1E8F",
+ "Z\u0302"=>"\u1E90",
+ "z\u0302"=>"\u1E91",
+ "Z\u0323"=>"\u1E92",
+ "z\u0323"=>"\u1E93",
+ "Z\u0331"=>"\u1E94",
+ "z\u0331"=>"\u1E95",
+ "h\u0331"=>"\u1E96",
+ "t\u0308"=>"\u1E97",
+ "w\u030A"=>"\u1E98",
+ "y\u030A"=>"\u1E99",
+ "\u017F\u0307"=>"\u1E9B",
+ "A\u0323"=>"\u1EA0",
+ "a\u0323"=>"\u1EA1",
+ "A\u0309"=>"\u1EA2",
+ "a\u0309"=>"\u1EA3",
+ "\u00C2\u0301"=>"\u1EA4",
+ "\u00E2\u0301"=>"\u1EA5",
+ "\u00C2\u0300"=>"\u1EA6",
+ "\u00E2\u0300"=>"\u1EA7",
+ "\u00C2\u0309"=>"\u1EA8",
+ "\u00E2\u0309"=>"\u1EA9",
+ "\u00C2\u0303"=>"\u1EAA",
+ "\u00E2\u0303"=>"\u1EAB",
+ "\u1EA0\u0302"=>"\u1EAC",
+ "\u1EA1\u0302"=>"\u1EAD",
+ "\u0102\u0301"=>"\u1EAE",
+ "\u0103\u0301"=>"\u1EAF",
+ "\u0102\u0300"=>"\u1EB0",
+ "\u0103\u0300"=>"\u1EB1",
+ "\u0102\u0309"=>"\u1EB2",
+ "\u0103\u0309"=>"\u1EB3",
+ "\u0102\u0303"=>"\u1EB4",
+ "\u0103\u0303"=>"\u1EB5",
+ "\u1EA0\u0306"=>"\u1EB6",
+ "\u1EA1\u0306"=>"\u1EB7",
+ "E\u0323"=>"\u1EB8",
+ "e\u0323"=>"\u1EB9",
+ "E\u0309"=>"\u1EBA",
+ "e\u0309"=>"\u1EBB",
+ "E\u0303"=>"\u1EBC",
+ "e\u0303"=>"\u1EBD",
+ "\u00CA\u0301"=>"\u1EBE",
+ "\u00EA\u0301"=>"\u1EBF",
+ "\u00CA\u0300"=>"\u1EC0",
+ "\u00EA\u0300"=>"\u1EC1",
+ "\u00CA\u0309"=>"\u1EC2",
+ "\u00EA\u0309"=>"\u1EC3",
+ "\u00CA\u0303"=>"\u1EC4",
+ "\u00EA\u0303"=>"\u1EC5",
+ "\u1EB8\u0302"=>"\u1EC6",
+ "\u1EB9\u0302"=>"\u1EC7",
+ "I\u0309"=>"\u1EC8",
+ "i\u0309"=>"\u1EC9",
+ "I\u0323"=>"\u1ECA",
+ "i\u0323"=>"\u1ECB",
+ "O\u0323"=>"\u1ECC",
+ "o\u0323"=>"\u1ECD",
+ "O\u0309"=>"\u1ECE",
+ "o\u0309"=>"\u1ECF",
+ "\u00D4\u0301"=>"\u1ED0",
+ "\u00F4\u0301"=>"\u1ED1",
+ "\u00D4\u0300"=>"\u1ED2",
+ "\u00F4\u0300"=>"\u1ED3",
+ "\u00D4\u0309"=>"\u1ED4",
+ "\u00F4\u0309"=>"\u1ED5",
+ "\u00D4\u0303"=>"\u1ED6",
+ "\u00F4\u0303"=>"\u1ED7",
+ "\u1ECC\u0302"=>"\u1ED8",
+ "\u1ECD\u0302"=>"\u1ED9",
+ "\u01A0\u0301"=>"\u1EDA",
+ "\u01A1\u0301"=>"\u1EDB",
+ "\u01A0\u0300"=>"\u1EDC",
+ "\u01A1\u0300"=>"\u1EDD",
+ "\u01A0\u0309"=>"\u1EDE",
+ "\u01A1\u0309"=>"\u1EDF",
+ "\u01A0\u0303"=>"\u1EE0",
+ "\u01A1\u0303"=>"\u1EE1",
+ "\u01A0\u0323"=>"\u1EE2",
+ "\u01A1\u0323"=>"\u1EE3",
+ "U\u0323"=>"\u1EE4",
+ "u\u0323"=>"\u1EE5",
+ "U\u0309"=>"\u1EE6",
+ "u\u0309"=>"\u1EE7",
+ "\u01AF\u0301"=>"\u1EE8",
+ "\u01B0\u0301"=>"\u1EE9",
+ "\u01AF\u0300"=>"\u1EEA",
+ "\u01B0\u0300"=>"\u1EEB",
+ "\u01AF\u0309"=>"\u1EEC",
+ "\u01B0\u0309"=>"\u1EED",
+ "\u01AF\u0303"=>"\u1EEE",
+ "\u01B0\u0303"=>"\u1EEF",
+ "\u01AF\u0323"=>"\u1EF0",
+ "\u01B0\u0323"=>"\u1EF1",
+ "Y\u0300"=>"\u1EF2",
+ "y\u0300"=>"\u1EF3",
+ "Y\u0323"=>"\u1EF4",
+ "y\u0323"=>"\u1EF5",
+ "Y\u0309"=>"\u1EF6",
+ "y\u0309"=>"\u1EF7",
+ "Y\u0303"=>"\u1EF8",
+ "y\u0303"=>"\u1EF9",
+ "\u03B1\u0313"=>"\u1F00",
+ "\u03B1\u0314"=>"\u1F01",
+ "\u1F00\u0300"=>"\u1F02",
+ "\u1F01\u0300"=>"\u1F03",
+ "\u1F00\u0301"=>"\u1F04",
+ "\u1F01\u0301"=>"\u1F05",
+ "\u1F00\u0342"=>"\u1F06",
+ "\u1F01\u0342"=>"\u1F07",
+ "\u0391\u0313"=>"\u1F08",
+ "\u0391\u0314"=>"\u1F09",
+ "\u1F08\u0300"=>"\u1F0A",
+ "\u1F09\u0300"=>"\u1F0B",
+ "\u1F08\u0301"=>"\u1F0C",
+ "\u1F09\u0301"=>"\u1F0D",
+ "\u1F08\u0342"=>"\u1F0E",
+ "\u1F09\u0342"=>"\u1F0F",
+ "\u03B5\u0313"=>"\u1F10",
+ "\u03B5\u0314"=>"\u1F11",
+ "\u1F10\u0300"=>"\u1F12",
+ "\u1F11\u0300"=>"\u1F13",
+ "\u1F10\u0301"=>"\u1F14",
+ "\u1F11\u0301"=>"\u1F15",
+ "\u0395\u0313"=>"\u1F18",
+ "\u0395\u0314"=>"\u1F19",
+ "\u1F18\u0300"=>"\u1F1A",
+ "\u1F19\u0300"=>"\u1F1B",
+ "\u1F18\u0301"=>"\u1F1C",
+ "\u1F19\u0301"=>"\u1F1D",
+ "\u03B7\u0313"=>"\u1F20",
+ "\u03B7\u0314"=>"\u1F21",
+ "\u1F20\u0300"=>"\u1F22",
+ "\u1F21\u0300"=>"\u1F23",
+ "\u1F20\u0301"=>"\u1F24",
+ "\u1F21\u0301"=>"\u1F25",
+ "\u1F20\u0342"=>"\u1F26",
+ "\u1F21\u0342"=>"\u1F27",
+ "\u0397\u0313"=>"\u1F28",
+ "\u0397\u0314"=>"\u1F29",
+ "\u1F28\u0300"=>"\u1F2A",
+ "\u1F29\u0300"=>"\u1F2B",
+ "\u1F28\u0301"=>"\u1F2C",
+ "\u1F29\u0301"=>"\u1F2D",
+ "\u1F28\u0342"=>"\u1F2E",
+ "\u1F29\u0342"=>"\u1F2F",
+ "\u03B9\u0313"=>"\u1F30",
+ "\u03B9\u0314"=>"\u1F31",
+ "\u1F30\u0300"=>"\u1F32",
+ "\u1F31\u0300"=>"\u1F33",
+ "\u1F30\u0301"=>"\u1F34",
+ "\u1F31\u0301"=>"\u1F35",
+ "\u1F30\u0342"=>"\u1F36",
+ "\u1F31\u0342"=>"\u1F37",
+ "\u0399\u0313"=>"\u1F38",
+ "\u0399\u0314"=>"\u1F39",
+ "\u1F38\u0300"=>"\u1F3A",
+ "\u1F39\u0300"=>"\u1F3B",
+ "\u1F38\u0301"=>"\u1F3C",
+ "\u1F39\u0301"=>"\u1F3D",
+ "\u1F38\u0342"=>"\u1F3E",
+ "\u1F39\u0342"=>"\u1F3F",
+ "\u03BF\u0313"=>"\u1F40",
+ "\u03BF\u0314"=>"\u1F41",
+ "\u1F40\u0300"=>"\u1F42",
+ "\u1F41\u0300"=>"\u1F43",
+ "\u1F40\u0301"=>"\u1F44",
+ "\u1F41\u0301"=>"\u1F45",
+ "\u039F\u0313"=>"\u1F48",
+ "\u039F\u0314"=>"\u1F49",
+ "\u1F48\u0300"=>"\u1F4A",
+ "\u1F49\u0300"=>"\u1F4B",
+ "\u1F48\u0301"=>"\u1F4C",
+ "\u1F49\u0301"=>"\u1F4D",
+ "\u03C5\u0313"=>"\u1F50",
+ "\u03C5\u0314"=>"\u1F51",
+ "\u1F50\u0300"=>"\u1F52",
+ "\u1F51\u0300"=>"\u1F53",
+ "\u1F50\u0301"=>"\u1F54",
+ "\u1F51\u0301"=>"\u1F55",
+ "\u1F50\u0342"=>"\u1F56",
+ "\u1F51\u0342"=>"\u1F57",
+ "\u03A5\u0314"=>"\u1F59",
+ "\u1F59\u0300"=>"\u1F5B",
+ "\u1F59\u0301"=>"\u1F5D",
+ "\u1F59\u0342"=>"\u1F5F",
+ "\u03C9\u0313"=>"\u1F60",
+ "\u03C9\u0314"=>"\u1F61",
+ "\u1F60\u0300"=>"\u1F62",
+ "\u1F61\u0300"=>"\u1F63",
+ "\u1F60\u0301"=>"\u1F64",
+ "\u1F61\u0301"=>"\u1F65",
+ "\u1F60\u0342"=>"\u1F66",
+ "\u1F61\u0342"=>"\u1F67",
+ "\u03A9\u0313"=>"\u1F68",
+ "\u03A9\u0314"=>"\u1F69",
+ "\u1F68\u0300"=>"\u1F6A",
+ "\u1F69\u0300"=>"\u1F6B",
+ "\u1F68\u0301"=>"\u1F6C",
+ "\u1F69\u0301"=>"\u1F6D",
+ "\u1F68\u0342"=>"\u1F6E",
+ "\u1F69\u0342"=>"\u1F6F",
+ "\u03B1\u0300"=>"\u1F70",
+ "\u03B5\u0300"=>"\u1F72",
+ "\u03B7\u0300"=>"\u1F74",
+ "\u03B9\u0300"=>"\u1F76",
+ "\u03BF\u0300"=>"\u1F78",
+ "\u03C5\u0300"=>"\u1F7A",
+ "\u03C9\u0300"=>"\u1F7C",
+ "\u1F00\u0345"=>"\u1F80",
+ "\u1F01\u0345"=>"\u1F81",
+ "\u1F02\u0345"=>"\u1F82",
+ "\u1F03\u0345"=>"\u1F83",
+ "\u1F04\u0345"=>"\u1F84",
+ "\u1F05\u0345"=>"\u1F85",
+ "\u1F06\u0345"=>"\u1F86",
+ "\u1F07\u0345"=>"\u1F87",
+ "\u1F08\u0345"=>"\u1F88",
+ "\u1F09\u0345"=>"\u1F89",
+ "\u1F0A\u0345"=>"\u1F8A",
+ "\u1F0B\u0345"=>"\u1F8B",
+ "\u1F0C\u0345"=>"\u1F8C",
+ "\u1F0D\u0345"=>"\u1F8D",
+ "\u1F0E\u0345"=>"\u1F8E",
+ "\u1F0F\u0345"=>"\u1F8F",
+ "\u1F20\u0345"=>"\u1F90",
+ "\u1F21\u0345"=>"\u1F91",
+ "\u1F22\u0345"=>"\u1F92",
+ "\u1F23\u0345"=>"\u1F93",
+ "\u1F24\u0345"=>"\u1F94",
+ "\u1F25\u0345"=>"\u1F95",
+ "\u1F26\u0345"=>"\u1F96",
+ "\u1F27\u0345"=>"\u1F97",
+ "\u1F28\u0345"=>"\u1F98",
+ "\u1F29\u0345"=>"\u1F99",
+ "\u1F2A\u0345"=>"\u1F9A",
+ "\u1F2B\u0345"=>"\u1F9B",
+ "\u1F2C\u0345"=>"\u1F9C",
+ "\u1F2D\u0345"=>"\u1F9D",
+ "\u1F2E\u0345"=>"\u1F9E",
+ "\u1F2F\u0345"=>"\u1F9F",
+ "\u1F60\u0345"=>"\u1FA0",
+ "\u1F61\u0345"=>"\u1FA1",
+ "\u1F62\u0345"=>"\u1FA2",
+ "\u1F63\u0345"=>"\u1FA3",
+ "\u1F64\u0345"=>"\u1FA4",
+ "\u1F65\u0345"=>"\u1FA5",
+ "\u1F66\u0345"=>"\u1FA6",
+ "\u1F67\u0345"=>"\u1FA7",
+ "\u1F68\u0345"=>"\u1FA8",
+ "\u1F69\u0345"=>"\u1FA9",
+ "\u1F6A\u0345"=>"\u1FAA",
+ "\u1F6B\u0345"=>"\u1FAB",
+ "\u1F6C\u0345"=>"\u1FAC",
+ "\u1F6D\u0345"=>"\u1FAD",
+ "\u1F6E\u0345"=>"\u1FAE",
+ "\u1F6F\u0345"=>"\u1FAF",
+ "\u03B1\u0306"=>"\u1FB0",
+ "\u03B1\u0304"=>"\u1FB1",
+ "\u1F70\u0345"=>"\u1FB2",
+ "\u03B1\u0345"=>"\u1FB3",
+ "\u03AC\u0345"=>"\u1FB4",
+ "\u03B1\u0342"=>"\u1FB6",
+ "\u1FB6\u0345"=>"\u1FB7",
+ "\u0391\u0306"=>"\u1FB8",
+ "\u0391\u0304"=>"\u1FB9",
+ "\u0391\u0300"=>"\u1FBA",
+ "\u0391\u0345"=>"\u1FBC",
+ "\u00A8\u0342"=>"\u1FC1",
+ "\u1F74\u0345"=>"\u1FC2",
+ "\u03B7\u0345"=>"\u1FC3",
+ "\u03AE\u0345"=>"\u1FC4",
+ "\u03B7\u0342"=>"\u1FC6",
+ "\u1FC6\u0345"=>"\u1FC7",
+ "\u0395\u0300"=>"\u1FC8",
+ "\u0397\u0300"=>"\u1FCA",
+ "\u0397\u0345"=>"\u1FCC",
+ "\u1FBF\u0300"=>"\u1FCD",
+ "\u1FBF\u0301"=>"\u1FCE",
+ "\u1FBF\u0342"=>"\u1FCF",
+ "\u03B9\u0306"=>"\u1FD0",
+ "\u03B9\u0304"=>"\u1FD1",
+ "\u03CA\u0300"=>"\u1FD2",
+ "\u03B9\u0342"=>"\u1FD6",
+ "\u03CA\u0342"=>"\u1FD7",
+ "\u0399\u0306"=>"\u1FD8",
+ "\u0399\u0304"=>"\u1FD9",
+ "\u0399\u0300"=>"\u1FDA",
+ "\u1FFE\u0300"=>"\u1FDD",
+ "\u1FFE\u0301"=>"\u1FDE",
+ "\u1FFE\u0342"=>"\u1FDF",
+ "\u03C5\u0306"=>"\u1FE0",
+ "\u03C5\u0304"=>"\u1FE1",
+ "\u03CB\u0300"=>"\u1FE2",
+ "\u03C1\u0313"=>"\u1FE4",
+ "\u03C1\u0314"=>"\u1FE5",
+ "\u03C5\u0342"=>"\u1FE6",
+ "\u03CB\u0342"=>"\u1FE7",
+ "\u03A5\u0306"=>"\u1FE8",
+ "\u03A5\u0304"=>"\u1FE9",
+ "\u03A5\u0300"=>"\u1FEA",
+ "\u03A1\u0314"=>"\u1FEC",
+ "\u00A8\u0300"=>"\u1FED",
+ "\u1F7C\u0345"=>"\u1FF2",
+ "\u03C9\u0345"=>"\u1FF3",
+ "\u03CE\u0345"=>"\u1FF4",
+ "\u03C9\u0342"=>"\u1FF6",
+ "\u1FF6\u0345"=>"\u1FF7",
+ "\u039F\u0300"=>"\u1FF8",
+ "\u03A9\u0300"=>"\u1FFA",
+ "\u03A9\u0345"=>"\u1FFC",
+ "\u2190\u0338"=>"\u219A",
+ "\u2192\u0338"=>"\u219B",
+ "\u2194\u0338"=>"\u21AE",
+ "\u21D0\u0338"=>"\u21CD",
+ "\u21D4\u0338"=>"\u21CE",
+ "\u21D2\u0338"=>"\u21CF",
+ "\u2203\u0338"=>"\u2204",
+ "\u2208\u0338"=>"\u2209",
+ "\u220B\u0338"=>"\u220C",
+ "\u2223\u0338"=>"\u2224",
+ "\u2225\u0338"=>"\u2226",
+ "\u223C\u0338"=>"\u2241",
+ "\u2243\u0338"=>"\u2244",
+ "\u2245\u0338"=>"\u2247",
+ "\u2248\u0338"=>"\u2249",
+ "=\u0338"=>"\u2260",
+ "\u2261\u0338"=>"\u2262",
+ "\u224D\u0338"=>"\u226D",
+ "<\u0338"=>"\u226E",
+ ">\u0338"=>"\u226F",
+ "\u2264\u0338"=>"\u2270",
+ "\u2265\u0338"=>"\u2271",
+ "\u2272\u0338"=>"\u2274",
+ "\u2273\u0338"=>"\u2275",
+ "\u2276\u0338"=>"\u2278",
+ "\u2277\u0338"=>"\u2279",
+ "\u227A\u0338"=>"\u2280",
+ "\u227B\u0338"=>"\u2281",
+ "\u2282\u0338"=>"\u2284",
+ "\u2283\u0338"=>"\u2285",
+ "\u2286\u0338"=>"\u2288",
+ "\u2287\u0338"=>"\u2289",
+ "\u22A2\u0338"=>"\u22AC",
+ "\u22A8\u0338"=>"\u22AD",
+ "\u22A9\u0338"=>"\u22AE",
+ "\u22AB\u0338"=>"\u22AF",
+ "\u227C\u0338"=>"\u22E0",
+ "\u227D\u0338"=>"\u22E1",
+ "\u2291\u0338"=>"\u22E2",
+ "\u2292\u0338"=>"\u22E3",
+ "\u22B2\u0338"=>"\u22EA",
+ "\u22B3\u0338"=>"\u22EB",
+ "\u22B4\u0338"=>"\u22EC",
+ "\u22B5\u0338"=>"\u22ED",
+ "\u304B\u3099"=>"\u304C",
+ "\u304D\u3099"=>"\u304E",
+ "\u304F\u3099"=>"\u3050",
+ "\u3051\u3099"=>"\u3052",
+ "\u3053\u3099"=>"\u3054",
+ "\u3055\u3099"=>"\u3056",
+ "\u3057\u3099"=>"\u3058",
+ "\u3059\u3099"=>"\u305A",
+ "\u305B\u3099"=>"\u305C",
+ "\u305D\u3099"=>"\u305E",
+ "\u305F\u3099"=>"\u3060",
+ "\u3061\u3099"=>"\u3062",
+ "\u3064\u3099"=>"\u3065",
+ "\u3066\u3099"=>"\u3067",
+ "\u3068\u3099"=>"\u3069",
+ "\u306F\u3099"=>"\u3070",
+ "\u306F\u309A"=>"\u3071",
+ "\u3072\u3099"=>"\u3073",
+ "\u3072\u309A"=>"\u3074",
+ "\u3075\u3099"=>"\u3076",
+ "\u3075\u309A"=>"\u3077",
+ "\u3078\u3099"=>"\u3079",
+ "\u3078\u309A"=>"\u307A",
+ "\u307B\u3099"=>"\u307C",
+ "\u307B\u309A"=>"\u307D",
+ "\u3046\u3099"=>"\u3094",
+ "\u309D\u3099"=>"\u309E",
+ "\u30AB\u3099"=>"\u30AC",
+ "\u30AD\u3099"=>"\u30AE",
+ "\u30AF\u3099"=>"\u30B0",
+ "\u30B1\u3099"=>"\u30B2",
+ "\u30B3\u3099"=>"\u30B4",
+ "\u30B5\u3099"=>"\u30B6",
+ "\u30B7\u3099"=>"\u30B8",
+ "\u30B9\u3099"=>"\u30BA",
+ "\u30BB\u3099"=>"\u30BC",
+ "\u30BD\u3099"=>"\u30BE",
+ "\u30BF\u3099"=>"\u30C0",
+ "\u30C1\u3099"=>"\u30C2",
+ "\u30C4\u3099"=>"\u30C5",
+ "\u30C6\u3099"=>"\u30C7",
+ "\u30C8\u3099"=>"\u30C9",
+ "\u30CF\u3099"=>"\u30D0",
+ "\u30CF\u309A"=>"\u30D1",
+ "\u30D2\u3099"=>"\u30D3",
+ "\u30D2\u309A"=>"\u30D4",
+ "\u30D5\u3099"=>"\u30D6",
+ "\u30D5\u309A"=>"\u30D7",
+ "\u30D8\u3099"=>"\u30D9",
+ "\u30D8\u309A"=>"\u30DA",
+ "\u30DB\u3099"=>"\u30DC",
+ "\u30DB\u309A"=>"\u30DD",
+ "\u30A6\u3099"=>"\u30F4",
+ "\u30EF\u3099"=>"\u30F7",
+ "\u30F0\u3099"=>"\u30F8",
+ "\u30F1\u3099"=>"\u30F9",
+ "\u30F2\u3099"=>"\u30FA",
+ "\u30FD\u3099"=>"\u30FE",
+ "\u{11099}\u{110BA}"=>"\u{1109A}",
+ "\u{1109B}\u{110BA}"=>"\u{1109C}",
+ "\u{110A5}\u{110BA}"=>"\u{110AB}",
+ "\u{11131}\u{11127}"=>"\u{1112E}",
+ "\u{11132}\u{11127}"=>"\u{1112F}",
+ "\u{11347}\u{1133E}"=>"\u{1134B}",
+ "\u{11347}\u{11357}"=>"\u{1134C}",
+ "\u{114B9}\u{114BA}"=>"\u{114BB}",
+ "\u{114B9}\u{114B0}"=>"\u{114BC}",
+ "\u{114B9}\u{114BD}"=>"\u{114BE}",
+ "\u{115B8}\u{115AF}"=>"\u{115BA}",
+ "\u{115B9}\u{115AF}"=>"\u{115BB}",
}.freeze
end
diff --git a/lib/uri.rb b/lib/uri.rb
index 971a97038f..9b6bde91c0 100644
--- a/lib/uri.rb
+++ b/lib/uri.rb
@@ -1,34 +1,28 @@
# frozen_string_literal: false
# URI is a module providing classes to handle Uniform Resource Identifiers
-# (RFC2396[http://tools.ietf.org/html/rfc2396])
+# (RFC2396[http://tools.ietf.org/html/rfc2396]).
#
# == Features
#
-# * Uniform handling of handling URIs
-# * Flexibility to introduce custom URI schemes
+# * Uniform way of handling URIs.
+# * Flexibility to introduce custom URI schemes.
# * Flexibility to have an alternate URI::Parser (or just different patterns
-# and regexp's)
+# and regexp's).
#
# == Basic example
#
# require 'uri'
#
# uri = URI("http://foo.com/posts?id=30&limit=5#time=1305298413")
-# #=> #<URI::HTTP:0x00000000b14880
-# URL:http://foo.com/posts?id=30&limit=5#time=1305298413>
-# uri.scheme
-# #=> "http"
-# uri.host
-# #=> "foo.com"
-# uri.path
-# #=> "/posts"
-# uri.query
-# #=> "id=30&limit=5"
-# uri.fragment
-# #=> "time=1305298413"
-#
-# uri.to_s
-# #=> "http://foo.com/posts?id=30&limit=5#time=1305298413"
+# #=> #<URI::HTTP http://foo.com/posts?id=30&limit=5#time=1305298413>
+#
+# uri.scheme #=> "http"
+# uri.host #=> "foo.com"
+# uri.path #=> "/posts"
+# uri.query #=> "id=30&limit=5"
+# uri.fragment #=> "time=1305298413"
+#
+# uri.to_s #=> "http://foo.com/posts?id=30&limit=5#time=1305298413"
#
# == Adding custom URIs
#
@@ -41,18 +35,18 @@
# #=> URI::RSYNC
#
# URI.scheme_list
-# #=> {"FTP"=>URI::FTP, "HTTP"=>URI::HTTP, "HTTPS"=>URI::HTTPS,
-# "LDAP"=>URI::LDAP, "LDAPS"=>URI::LDAPS, "MAILTO"=>URI::MailTo,
-# "RSYNC"=>URI::RSYNC}
+# #=> {"FILE"=>URI::File, "FTP"=>URI::FTP, "HTTP"=>URI::HTTP,
+# # "HTTPS"=>URI::HTTPS, "LDAP"=>URI::LDAP, "LDAPS"=>URI::LDAPS,
+# # "MAILTO"=>URI::MailTo, "RSYNC"=>URI::RSYNC}
#
# uri = URI("rsync://rsync.foo.com")
-# #=> #<URI::RSYNC:0x00000000f648c8 URL:rsync://rsync.foo.com>
+# #=> #<URI::RSYNC rsync://rsync.foo.com>
#
# == RFC References
#
-# A good place to view an RFC spec is http://www.ietf.org/rfc.html
+# A good place to view an RFC spec is http://www.ietf.org/rfc.html.
#
-# Here is a list of all related RFC's.
+# Here is a list of all related RFC's:
# - RFC822[http://tools.ietf.org/html/rfc822]
# - RFC1738[http://tools.ietf.org/html/rfc1738]
# - RFC2255[http://tools.ietf.org/html/rfc2255]
@@ -65,6 +59,7 @@
# == Class tree
#
# - URI::Generic (in uri/generic.rb)
+# - URI::File - (in uri/file.rb)
# - URI::FTP - (in uri/ftp.rb)
# - URI::HTTP - (in uri/http.rb)
# - URI::HTTPS - (in uri/https.rb)
@@ -104,6 +99,7 @@ end
require 'uri/common'
require 'uri/generic'
+require 'uri/file'
require 'uri/ftp'
require 'uri/http'
require 'uri/https'
diff --git a/lib/uri/common.rb b/lib/uri/common.rb
index c9ea9c8390..17d9ffc28c 100644
--- a/lib/uri/common.rb
+++ b/lib/uri/common.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
# = uri/common.rb
#
@@ -10,8 +10,8 @@
# See URI for general documentation
#
-require "uri/rfc2396_parser"
-require "uri/rfc3986_parser"
+require_relative "rfc2396_parser"
+require_relative "rfc3986_parser"
module URI
REGEXP = RFC2396_REGEXP
@@ -61,7 +61,7 @@ module URI
module_function :make_components_hash
end
- # module for escaping unsafe characters with codes.
+ # Module for escaping unsafe characters with codes.
module Escape
#
# == Synopsis
@@ -82,7 +82,7 @@ module URI
# Escapes the string, replacing all unsafe characters with codes.
#
# This method is obsolete and should not be used. Instead, use
- # CGI.escape, URI.www_form_encode or URI.www_form_encode_component
+ # CGI.escape, URI.encode_www_form or URI.encode_www_form_component
# depending on your specific use case.
#
# == Usage
@@ -90,17 +90,16 @@ module URI
# require 'uri'
#
# enc_uri = URI.escape("http://example.com/?a=\11\15")
- # p enc_uri
# # => "http://example.com/?a=%09%0D"
#
- # p URI.unescape(enc_uri)
+ # URI.unescape(enc_uri)
# # => "http://example.com/?a=\t\r"
#
- # p URI.escape("@?@!", "!?")
+ # URI.escape("@?@!", "!?")
# # => "@%3F@%21"
#
def escape(*arg)
- warn "#{caller(1)[0]}: warning: URI.escape is obsolete" if $VERBOSE
+ warn "URI.escape is obsolete", uplevel: 1 if $VERBOSE
DEFAULT_PARSER.escape(*arg)
end
alias encode escape
@@ -112,21 +111,26 @@ module URI
# == Args
#
# +str+::
- # Unescapes the string.
+ # String to unescape.
+ #
+ # == Description
+ #
+ # This method is obsolete and should not be used. Instead, use
+ # CGI.unescape, URI.decode_www_form or URI.decode_www_form_component
+ # depending on your specific use case.
#
# == Usage
#
# require 'uri'
#
# enc_uri = URI.escape("http://example.com/?a=\11\15")
- # p enc_uri
# # => "http://example.com/?a=%09%0D"
#
- # p URI.unescape(enc_uri)
+ # URI.unescape(enc_uri)
# # => "http://example.com/?a=\t\r"
#
def unescape(*arg)
- warn "#{caller(1)[0]}: warning: URI.unescape is obsolete" if $VERBOSE
+ warn "URI.unescape is obsolete", uplevel: 1 if $VERBOSE
DEFAULT_PARSER.unescape(*arg)
end
alias decode unescape
@@ -136,7 +140,7 @@ module URI
include REGEXP
@@schemes = {}
- # Returns a Hash of the defined schemes
+ # Returns a Hash of the defined schemes.
def self.scheme_list
@@schemes
end
@@ -172,21 +176,21 @@ module URI
#
# Splits the string on following parts and returns array with result:
#
- # * Scheme
- # * Userinfo
- # * Host
- # * Port
- # * Registry
- # * Path
- # * Opaque
- # * Query
- # * Fragment
+ # * Scheme
+ # * Userinfo
+ # * Host
+ # * Port
+ # * Registry
+ # * Path
+ # * Opaque
+ # * Query
+ # * Fragment
#
# == Usage
#
# require 'uri'
#
- # p URI.split("http://www.ruby-lang.org/")
+ # URI.split("http://www.ruby-lang.org/")
# # => ["http", nil, "www.ruby-lang.org", nil, nil, "/", nil, nil, nil]
#
def self.split(uri)
@@ -209,7 +213,7 @@ module URI
#
# == Raises
#
- # URI::InvalidURIError
+ # URI::InvalidURIError::
# Raised if URI given is not a correct one.
#
# == Usage
@@ -217,11 +221,10 @@ module URI
# require 'uri'
#
# uri = URI.parse("http://www.ruby-lang.org/")
- # p uri
- # # => #<URI::HTTP:0x202281be URL:http://www.ruby-lang.org/>
- # p uri.scheme
+ # # => #<URI::HTTP http://www.ruby-lang.org/>
+ # uri.scheme
# # => "http"
- # p uri.host
+ # uri.host
# # => "www.ruby-lang.org"
#
# It's recommended to first ::escape the provided +uri_str+ if there are any
@@ -249,21 +252,20 @@ module URI
#
# require 'uri'
#
- # p URI.join("http://example.com/","main.rbx")
- # # => #<URI::HTTP:0x2022ac02 URL:http://example.com/main.rbx>
- #
- # p URI.join('http://example.com', 'foo')
- # # => #<URI::HTTP:0x01ab80a0 URL:http://example.com/foo>
+ # URI.join("http://example.com/","main.rbx")
+ # # => #<URI::HTTP http://example.com/main.rbx>
#
- # p URI.join('http://example.com', '/foo', '/bar')
- # # => #<URI::HTTP:0x01aaf0b0 URL:http://example.com/bar>
+ # URI.join('http://example.com', 'foo')
+ # # => #<URI::HTTP http://example.com/foo>
#
- # p URI.join('http://example.com', '/foo', 'bar')
- # # => #<URI::HTTP:0x801a92af0 URL:http://example.com/bar>
+ # URI.join('http://example.com', '/foo', '/bar')
+ # # => #<URI::HTTP http://example.com/bar>
#
- # p URI.join('http://example.com', '/foo/', 'bar')
- # # => #<URI::HTTP:0x80135a3a0 URL:http://example.com/foo/bar>
+ # URI.join('http://example.com', '/foo', 'bar')
+ # # => #<URI::HTTP http://example.com/bar>
#
+ # URI.join('http://example.com', '/foo/', 'bar')
+ # # => #<URI::HTTP http://example.com/foo/bar>
#
def self.join(*str)
RFC3986_PARSER.join(*str)
@@ -279,7 +281,7 @@ module URI
# +str+::
# String to extract URIs from.
# +schemes+::
- # Limit URI matching to a specific schemes.
+ # Limit URI matching to specific schemes.
#
# == Description
#
@@ -294,7 +296,7 @@ module URI
# # => ["http://foo.example.com/bla", "mailto:test@example.com"]
#
def self.extract(str, schemes = nil, &block)
- warn "#{caller(1)[0]}: warning: URI.extract is obsolete" if $VERBOSE
+ warn "URI.extract is obsolete", uplevel: 1 if $VERBOSE
DEFAULT_PARSER.extract(str, schemes, &block)
end
@@ -310,6 +312,7 @@ module URI
# whose scheme is one of the match_schemes.
#
# == Description
+ #
# Returns a Regexp object which matches to URI-like strings.
# The Regexp object returned by this method includes arbitrary
# number of capture group (parentheses). Never rely on it's number.
@@ -322,7 +325,7 @@ module URI
# html_string.slice(URI.regexp)
#
# # remove ftp URIs
- # html_string.sub(URI.regexp(['ftp'])
+ # html_string.sub(URI.regexp(['ftp']), '')
#
# # You should not rely on the number of parentheses
# html_string.scan(URI.regexp) do |*matches|
@@ -330,31 +333,28 @@ module URI
# end
#
def self.regexp(schemes = nil)
- warn "#{caller(1)[0]}: warning: URI.regexp is obsolete" if $VERBOSE
+ warn "URI.regexp is obsolete", uplevel: 1 if $VERBOSE
DEFAULT_PARSER.make_regexp(schemes)
end
TBLENCWWWCOMP_ = {} # :nodoc:
256.times do |i|
- TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
+ TBLENCWWWCOMP_[-i.chr] = -('%%%02X' % i)
end
TBLENCWWWCOMP_[' '] = '+'
TBLENCWWWCOMP_.freeze
TBLDECWWWCOMP_ = {} # :nodoc:
256.times do |i|
h, l = i>>4, i&15
- TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
- TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
- TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
- TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
+ TBLDECWWWCOMP_[-('%%%X%X' % [h, l])] = -i.chr
+ TBLDECWWWCOMP_[-('%%%x%X' % [h, l])] = -i.chr
+ TBLDECWWWCOMP_[-('%%%X%x' % [h, l])] = -i.chr
+ TBLDECWWWCOMP_[-('%%%x%x' % [h, l])] = -i.chr
end
TBLDECWWWCOMP_['+'] = ' '
TBLDECWWWCOMP_.freeze
- HTML5ASCIIINCOMPAT = defined? Encoding::UTF_7 ? [Encoding::UTF_7, Encoding::UTF_16BE, Encoding::UTF_16LE,
- Encoding::UTF_32BE, Encoding::UTF_32LE] : [] # :nodoc:
-
- # Encode given +str+ to URL-encoded form data.
+ # Encodes given +str+ to URL-encoded form data.
#
# This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP
# (ASCII space) to + and converts others to %XX.
@@ -362,9 +362,9 @@ module URI
# If +enc+ is given, convert +str+ to the encoding before percent encoding.
#
# This is an implementation of
- # http://www.w3.org/TR/2013/CR-html5-20130806/forms.html#url-encoded-form-data
+ # http://www.w3.org/TR/2013/CR-html5-20130806/forms.html#url-encoded-form-data.
#
- # See URI.decode_www_form_component, URI.encode_www_form
+ # See URI.decode_www_form_component, URI.encode_www_form.
def self.encode_www_form_component(str, enc=nil)
str = str.to_s.dup
if str.encoding != Encoding::ASCII_8BIT
@@ -378,17 +378,17 @@ module URI
str.force_encoding(Encoding::US_ASCII)
end
- # Decode given +str+ of URL-encoded form data.
+ # Decodes given +str+ of URL-encoded form data.
#
# This decodes + to SP.
#
- # See URI.encode_www_form_component, URI.decode_www_form
+ # See URI.encode_www_form_component, 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)
end
- # Generate URL-encoded form data from given +enum+.
+ # Generates URL-encoded form data from given +enum+.
#
# This generates application/x-www-form-urlencoded data defined in HTML5
# from given an Enumerable object.
@@ -396,7 +396,7 @@ module URI
# This internally uses URI.encode_www_form_component(str).
#
# This method doesn't convert the encoding of given items, so convert them
- # before call this method if you want to send data as other than original
+ # before calling this method if you want to send data as other than original
# encoding or mixed encoding data. (Strings which are encoded in an HTML5
# ASCII incompatible encoding are converted to UTF-8.)
#
@@ -414,7 +414,7 @@ module URI
# URI.encode_www_form([["q", "ruby"], ["q", "perl"], ["lang", "en"]])
# #=> "q=ruby&q=perl&lang=en"
#
- # See URI.encode_www_form_component, URI.decode_www_form
+ # See URI.encode_www_form_component, URI.decode_www_form.
def self.encode_www_form(enum, enc=nil)
enum.map do |k,v|
if v.nil?
@@ -435,22 +435,22 @@ module URI
end.join('&')
end
- # Decode URL-encoded form data from given +str+.
+ # Decodes URL-encoded form data from given +str+.
#
# This decodes application/x-www-form-urlencoded data
- # and returns array of key-value array.
+ # and returns an array of key-value arrays.
#
- # This refers http://url.spec.whatwg.org/#concept-urlencoded-parser ,
- # so this supports only &-separator, don't support ;-separator.
+ # This refers http://url.spec.whatwg.org/#concept-urlencoded-parser,
+ # so this supports only &-separator, and doesn't support ;-separator.
#
# ary = URI.decode_www_form("a=1&a=2&b=3")
- # p ary #=> [['a', '1'], ['a', '2'], ['b', '3']]
- # p ary.assoc('a').last #=> '1'
- # p ary.assoc('b').last #=> '3'
- # p ary.rassoc('a').last #=> '2'
- # p Hash[ary] # => {"a"=>"2", "b"=>"3"}
+ # ary #=> [['a', '1'], ['a', '2'], ['b', '3']]
+ # ary.assoc('a').last #=> '1'
+ # ary.assoc('b').last #=> '3'
+ # ary.rassoc('a').last #=> '2'
+ # Hash[ary] #=> {"a"=>"2", "b"=>"3"}
#
- # See URI.decode_www_form_component, URI.encode_www_form
+ # See URI.decode_www_form_component, URI.encode_www_form.
def self.decode_www_form(str, enc=Encoding::UTF_8, separator: '&', use__charset_: false, isindex: false)
raise ArgumentError, "the input of #{self.name}.#{__method__} must be ASCII only string" unless str.ascii_only?
ary = []
@@ -462,7 +462,7 @@ module URI
if isindex
if sep.empty?
val = key
- key = ''
+ key = +''
end
isindex = false
end
@@ -476,7 +476,7 @@ module URI
if val
val.gsub!(/\+|%\h\h/, TBLDECWWWCOMP_)
else
- val = ''
+ val = +''
end
ary << [key, val]
@@ -728,7 +728,7 @@ end # module URI
module Kernel
#
- # Returns +uri+ converted to a URI object.
+ # Returns +uri+ converted to an URI object.
#
def URI(uri)
if uri.is_a?(URI::Generic)
diff --git a/lib/uri/file.rb b/lib/uri/file.rb
new file mode 100644
index 0000000000..561ec703c4
--- /dev/null
+++ b/lib/uri/file.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+require_relative 'generic'
+
+module URI
+
+ #
+ # The "file" URI is defined by RFC8089.
+ #
+ class File < Generic
+ # A Default port of nil for URI::File.
+ DEFAULT_PORT = nil
+
+ #
+ # An Array of the available components for URI::File.
+ #
+ COMPONENT = [
+ :scheme,
+ :host,
+ :path
+ ].freeze
+
+ #
+ # == Description
+ #
+ # Creates a new URI::File object from components, with syntax checking.
+ #
+ # The components accepted are +host+ and +path+.
+ #
+ # The components should be provided either as an Array, or as a Hash
+ # with keys formed by preceding the component names with a colon.
+ #
+ # If an Array is used, the components must be passed in the
+ # order <code>[host, path]</code>.
+ #
+ # Examples:
+ #
+ # require 'uri'
+ #
+ # uri1 = URI::File.build(['host.example.com', '/path/file.zip'])
+ # uri1.to_s # => "file://host.example.com/path/file.zip"
+ #
+ # uri2 = URI::File.build({:host => 'host.example.com',
+ # :path => '/ruby/src'})
+ # uri2.to_s # => "file://host.example.com/ruby/src"
+ #
+ def self.build(args)
+ tmp = Util::make_components_hash(self, args)
+ super(tmp)
+ end
+
+ # Protected setter for the host component +v+.
+ #
+ # See also URI::Generic.host=.
+ #
+ def set_host(v)
+ v = "" if v.nil? || v == "localhost"
+ @host = v
+ end
+
+ # do nothing
+ def set_port(v)
+ end
+
+ # raise InvalidURIError
+ def check_userinfo(user)
+ raise URI::InvalidURIError, "can not set userinfo for file URI"
+ end
+
+ # raise InvalidURIError
+ def check_user(user)
+ raise URI::InvalidURIError, "can not set user for file URI"
+ end
+
+ # raise InvalidURIError
+ def check_password(user)
+ raise URI::InvalidURIError, "can not set password for file URI"
+ end
+
+ # do nothing
+ def set_userinfo(v)
+ end
+
+ # do nothing
+ def set_user(v)
+ end
+
+ # do nothing
+ def set_password(v)
+ end
+ end
+
+ @@schemes['FILE'] = File
+end
diff --git a/lib/uri/ftp.rb b/lib/uri/ftp.rb
index e5c00b34da..f57b4b7df9 100644
--- a/lib/uri/ftp.rb
+++ b/lib/uri/ftp.rb
@@ -8,7 +8,7 @@
# See URI for general documentation
#
-require 'uri/generic'
+require_relative 'generic'
module URI
@@ -21,11 +21,11 @@ module URI
# http://tools.ietf.org/html/draft-hoffman-ftp-uri-04
#
class FTP < Generic
- # A Default port of 21 for URI::FTP
+ # A Default port of 21 for URI::FTP.
DEFAULT_PORT = 21
#
- # An Array of the available components for URI::FTP
+ # An Array of the available components for URI::FTP.
#
COMPONENT = [
:scheme,
@@ -34,7 +34,7 @@ module URI
].freeze
#
- # Typecode is "a", "i" or "d".
+ # Typecode is "a", "i", or "d".
#
# * "a" indicates a text file (the FTP command was ASCII)
# * "i" indicates a binary file (FTP command IMAGE)
@@ -42,8 +42,7 @@ module URI
#
TYPECODE = ['a', 'i', 'd'].freeze
- # Typecode prefix
- # ';type='
+ # Typecode prefix ";type=".
TYPECODE_PREFIX = ';type='.freeze
def self.new2(user, password, host, port, path,
@@ -71,27 +70,29 @@ module URI
#
# Creates a new URI::FTP object from components, with syntax checking.
#
- # The components accepted are +userinfo+, +host+, +port+, +path+ and
+ # The components accepted are +userinfo+, +host+, +port+, +path+, and
# +typecode+.
#
# The components should be provided either as an Array, or as a Hash
# with keys formed by preceding the component names with a colon.
#
- # If an Array is used, the components must be passed in the order
- # [userinfo, host, port, path, typecode]
+ # If an Array is used, the components must be passed in the
+ # order <code>[userinfo, host, port, path, typecode]</code>.
#
# If the path supplied is absolute, it will be escaped in order to
- # make it absolute in the URI. Examples:
+ # make it absolute in the URI.
+ #
+ # Examples:
#
# require 'uri'
#
- # uri = URI::FTP.build(['user:password', 'ftp.example.com', nil,
+ # uri1 = URI::FTP.build(['user:password', 'ftp.example.com', nil,
# '/path/file.zip', 'i'])
- # puts uri.to_s -> ftp://user:password@ftp.example.com/%2Fpath/file.zip;type=i
+ # uri1.to_s # => "ftp://user:password@ftp.example.com/%2Fpath/file.zip;type=i"
#
# uri2 = URI::FTP.build({:host => 'ftp.example.com',
# :path => 'ruby/src'})
- # puts uri2.to_s -> ftp://ftp.example.com/ruby/src
+ # uri2.to_s # => "ftp://ftp.example.com/ruby/src"
#
def self.build(args)
@@ -128,7 +129,7 @@ module URI
# required by RFC1738; instead it is treated as per RFC2396.
#
# Arguments are +scheme+, +userinfo+, +host+, +port+, +registry+, +path+,
- # +opaque+, +query+ and +fragment+, in that order.
+ # +opaque+, +query+, and +fragment+, in that order.
#
def initialize(scheme,
userinfo, host, port, registry,
@@ -155,13 +156,13 @@ module URI
end
end
- # typecode accessor
+ # typecode accessor.
#
- # see URI::FTP::COMPONENT
+ # See URI::FTP::COMPONENT.
attr_reader :typecode
- # validates typecode +v+,
- # returns a +true+ or +false+ boolean
+ # Validates typecode +v+,
+ # returns +true+ or +false+.
#
def check_typecode(v)
if TYPECODE.include?(v)
@@ -173,9 +174,9 @@ module URI
end
private :check_typecode
- # Private setter for the typecode +v+
+ # Private setter for the typecode +v+.
#
- # see also URI::FTP.typecode=
+ # See also URI::FTP.typecode=.
#
def set_typecode(v)
@typecode = v
@@ -190,21 +191,20 @@ module URI
#
# == Description
#
- # public setter for the typecode +v+.
- # (with validation)
+ # Public setter for the typecode +v+
+ # (with validation).
#
- # see also URI::FTP.check_typecode
+ # See also URI::FTP.check_typecode.
#
# == Usage
#
# require 'uri'
#
# uri = URI.parse("ftp://john@ftp.example.com/my_file.img")
- # #=> #<URI::FTP:0x00000000923650 URL:ftp://john@ftp.example.com/my_file.img>
+ # #=> #<URI::FTP ftp://john@ftp.example.com/my_file.img>
# uri.typecode = "i"
- # # => "i"
# uri
- # #=> #<URI::FTP:0x00000000923650 URL:ftp://john@ftp.example.com/my_file.img;type=i>
+ # #=> #<URI::FTP ftp://john@ftp.example.com/my_file.img;type=i>
#
def typecode=(typecode)
check_typecode(typecode)
@@ -226,29 +226,29 @@ module URI
# RFC 1738 specifically states that the path for an FTP URI does not
# include the / which separates the URI path from the URI host. Example:
#
- # ftp://ftp.example.com/pub/ruby
+ # <code>ftp://ftp.example.com/pub/ruby</code>
#
# The above URI indicates that the client should connect to
- # ftp.example.com then cd pub/ruby from the initial login directory.
+ # ftp.example.com then cd to pub/ruby from the initial login directory.
#
# If you want to cd to an absolute directory, you must include an
# escaped / (%2F) in the path. Example:
#
- # ftp://ftp.example.com/%2Fpub/ruby
+ # <code>ftp://ftp.example.com/%2Fpub/ruby</code>
#
- # This method will then return "/pub/ruby"
+ # This method will then return "/pub/ruby".
#
def path
return @path.sub(/^\//,'').sub(/^%2F/,'/')
end
- # Private setter for the path of the URI::FTP
+ # Private setter for the path of the URI::FTP.
def set_path(v)
super("/" + v.sub(/^\//, "%2F"))
end
protected :set_path
- # Returns a String representation of the URI::FTP
+ # Returns a String representation of the URI::FTP.
def to_s
save_path = nil
if @typecode
diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb
index 5df9a163fe..ea79e7950a 100644
--- a/lib/uri/generic.rb
+++ b/lib/uri/generic.rb
@@ -9,7 +9,9 @@
# See URI for general documentation
#
-require 'uri/common'
+require_relative 'common'
+autoload :IPSocket, 'socket'
+autoload :IPAddr, 'ipaddr'
module URI
@@ -21,26 +23,26 @@ module URI
include URI
#
- # A Default port of nil for URI::Generic
+ # A Default port of nil for URI::Generic.
#
DEFAULT_PORT = nil
#
- # Returns default port
+ # Returns default port.
#
def self.default_port
self::DEFAULT_PORT
end
#
- # Returns default port
+ # Returns default port.
#
def default_port
self.class.default_port
end
#
- # An Array of the available components for URI::Generic
+ # An Array of the available components for URI::Generic.
#
COMPONENT = [
:scheme,
@@ -66,14 +68,13 @@ module URI
#
# == Synopsis
#
- # See #new
+ # See ::new.
#
# == Description
#
# At first, tries to create a new URI::Generic instance using
# URI::Generic::build. But, if exception URI::InvalidComponentError is raised,
- # then it URI::Escape.escape all URI components and tries again.
- #
+ # then it does URI::Escape.escape all URI components and tries again.
#
def self.build2(args)
begin
@@ -104,14 +105,14 @@ module URI
#
# == Synopsis
#
- # See #new
+ # See ::new.
#
# == Description
#
# Creates a new URI::Generic instance from components of URI::Generic
# with check. Components are: scheme, userinfo, host, port, registry, path,
- # opaque, query and fragment. You can provide arguments either by an Array or a Hash.
- # See #new for hash keys to use or for order of array items.
+ # opaque, query, and fragment. You can provide arguments either by an Array or a Hash.
+ # See ::new for hash keys to use or for order of array items.
#
def self.build(args)
if args.kind_of?(Array) &&
@@ -135,31 +136,32 @@ module URI
tmp << true
return self.new(*tmp)
end
+
#
# == Args
#
# +scheme+::
# Protocol scheme, i.e. 'http','ftp','mailto' and so on.
# +userinfo+::
- # User name and password, i.e. 'sdmitry:bla'
+ # User name and password, i.e. 'sdmitry:bla'.
# +host+::
- # Server host name
+ # Server host name.
# +port+::
- # Server port
+ # Server port.
# +registry+::
# Registry of naming authorities.
# +path+::
- # Path on server
+ # Path on server.
# +opaque+::
- # Opaque part
+ # Opaque part.
# +query+::
- # Query data
+ # Query data.
# +fragment+::
- # A part of URI after '#' sign
+ # Part of the URI after '#' character.
# +parser+::
- # Parser for internal use [URI::DEFAULT_PARSER by default]
+ # Parser for internal use [URI::DEFAULT_PARSER by default].
# +arg_check+::
- # Check arguments [false by default]
+ # Check arguments [false by default].
#
# == Description
#
@@ -207,45 +209,44 @@ module URI
"the scheme #{@scheme} does not accept registry part: #{registry} (or bad hostname?)"
end
- @scheme.freeze if @scheme
+ @scheme&.freeze
self.set_path('') if !@path && !@opaque # (see RFC2396 Section 5.2)
self.set_port(self.default_port) if self.default_port && !@port
end
#
- # returns the scheme component of the URI.
+ # Returns the scheme component of the URI.
#
# URI("http://foo/bar/baz").scheme #=> "http"
#
attr_reader :scheme
- # returns the host component of the URI.
+ # Returns the host component of the URI.
#
# URI("http://foo/bar/baz").host #=> "foo"
#
- # It returns nil if no host component.
+ # It returns nil if no host component exists.
#
# URI("mailto:foo@example.org").host #=> nil
#
- # The component doesn't contains the port number.
+ # The component does not contain the port number.
#
# URI("http://foo:8080/bar/baz").host #=> "foo"
#
- # Since IPv6 addresses are wrapped by brackets in URIs,
- # this method returns IPv6 addresses wrapped by brackets.
- # This form is not appropriate to pass socket methods such as TCPSocket.open.
- # If unwrapped host names are required, use "hostname" method.
+ # Since IPv6 addresses are wrapped with brackets in URIs,
+ # this method returns IPv6 addresses wrapped with brackets.
+ # This form is not appropriate to pass to socket methods such as TCPSocket.open.
+ # If unwrapped host names are required, use the #hostname method.
#
- # URI("http://[::1]/bar/baz").host #=> "[::1]"
+ # URI("http://[::1]/bar/baz").host #=> "[::1]"
# URI("http://[::1]/bar/baz").hostname #=> "::1"
#
attr_reader :host
- # returns the port component of the URI.
+ # Returns the port component of the URI.
#
- # URI("http://foo/bar/baz").port #=> "80"
- #
- # URI("http://foo:8080/bar/baz").port #=> "8080"
+ # URI("http://foo/bar/baz").port #=> 80
+ # URI("http://foo:8080/bar/baz").port #=> 8080
#
attr_reader :port
@@ -253,37 +254,38 @@ module URI
nil
end
- # returns the path component of the URI.
+ # Returns the path component of the URI.
#
# URI("http://foo/bar/baz").path #=> "/bar/baz"
#
attr_reader :path
- # returns the query component of the URI.
+ # Returns the query component of the URI.
#
# URI("http://foo/bar/baz?search=FooBar").query #=> "search=FooBar"
#
attr_reader :query
- # returns the opaque part of the URI.
+ # Returns the opaque part of the URI.
#
# URI("mailto:foo@example.org").opaque #=> "foo@example.org"
+ # URI("http://foo/bar/baz").opaque #=> nil
#
- # Portion of the path that does make use of the slash '/'.
- # The path typically refers to the absolute path and the opaque part.
- # (see RFC2396 Section 3 and 5.2)
+ # The portion of the path that does not make use of the slash '/'.
+ # The path typically refers to an absolute path or an opaque part.
+ # (See RFC2396 Section 3 and 5.2.)
#
attr_reader :opaque
- # returns the fragment component of the URI.
+ # Returns the fragment component of the URI.
#
# URI("http://foo/bar/baz?search=FooBar#ponies").fragment #=> "ponies"
#
attr_reader :fragment
- # returns the parser to be used.
+ # Returns the parser to be used.
#
- # Unless a URI::Parser is defined, then DEFAULT_PARSER is used.
+ # Unless a URI::Parser is defined, DEFAULT_PARSER is used.
#
def parser
if !defined?(@parser) || !@parser
@@ -293,7 +295,8 @@ module URI
end
end
- # replace self by other URI object
+ # Replaces self by other URI object.
+ #
def replace!(oth)
if self.class != oth.class
raise ArgumentError, "expected #{self.class} object"
@@ -313,7 +316,7 @@ module URI
end
#
- # check the scheme +v+ component against the URI::Parser Regexp for :SCHEME
+ # Checks the scheme +v+ component against the URI::Parser Regexp for :SCHEME.
#
def check_scheme(v)
if v && parser.regexp[:SCHEME] !~ v
@@ -325,12 +328,12 @@ module URI
end
private :check_scheme
- # protected setter for the scheme component +v+
+ # Protected setter for the scheme component +v+.
#
- # see also URI::Generic.scheme=
+ # See also URI::Generic.scheme=.
#
def set_scheme(v)
- @scheme = v ? v.downcase : v
+ @scheme = v&.downcase
end
protected :set_scheme
@@ -342,10 +345,10 @@ module URI
#
# == Description
#
- # public setter for the scheme component +v+.
- # (with validation)
+ # Public setter for the scheme component +v+
+ # (with validation).
#
- # see also URI::Generic.check_scheme
+ # See also URI::Generic.check_scheme.
#
# == Usage
#
@@ -353,9 +356,7 @@ module URI
#
# uri = URI.parse("http://my.example.com")
# uri.scheme = "https"
- # # => "https"
- # uri
- # #=> #<URI::HTTP:0x000000008e89e8 URL:https://my.example.com>
+ # uri.to_s #=> "https://my.example.com"
#
def scheme=(v)
check_scheme(v)
@@ -364,13 +365,13 @@ module URI
end
#
- # check the +user+ and +password+.
+ # Checks the +user+ and +password+.
#
# If +password+ is not provided, then +user+ is
# split, using URI::Generic.split_userinfo, to
# pull +user+ and +password.
#
- # see also URI::Generic.check_user, URI::Generic.check_password
+ # See also URI::Generic.check_user, URI::Generic.check_password.
#
def check_userinfo(user, password = nil)
if !password
@@ -384,8 +385,8 @@ module URI
private :check_userinfo
#
- # check the user +v+ component for RFC2396 compliance
- # and against the URI::Parser Regexp for :USERINFO
+ # Checks the user +v+ component for RFC2396 compliance
+ # and against the URI::Parser Regexp for :USERINFO.
#
# Can not have a registry or opaque component defined,
# with a user component defined.
@@ -408,8 +409,8 @@ module URI
private :check_user
#
- # check the password +v+ component for RFC2396 compliance
- # and against the URI::Parser Regexp for :USERINFO
+ # Checks the password +v+ component for RFC2396 compliance
+ # and against the URI::Parser Regexp for :USERINFO.
#
# Can not have a registry or opaque component defined,
# with a user component defined.
@@ -436,7 +437,7 @@ module URI
private :check_password
#
- # Sets userinfo, argument is string like 'name:pass'
+ # Sets userinfo, argument is string like 'name:pass'.
#
def userinfo=(userinfo)
if userinfo.nil?
@@ -455,10 +456,10 @@ module URI
#
# == Description
#
- # public setter for the +user+ component.
- # (with validation)
+ # Public setter for the +user+ component
+ # (with validation).
#
- # see also URI::Generic.check_user
+ # See also URI::Generic.check_user.
#
# == Usage
#
@@ -466,9 +467,7 @@ module URI
#
# uri = URI.parse("http://john:S3nsit1ve@my.example.com")
# uri.user = "sam"
- # # => "sam"
- # uri
- # #=> #<URI::HTTP:0x00000000881d90 URL:http://sam:V3ry_S3nsit1ve@my.example.com>
+ # uri.to_s #=> "http://sam:V3ry_S3nsit1ve@my.example.com"
#
def user=(user)
check_user(user)
@@ -484,10 +483,10 @@ module URI
#
# == Description
#
- # public setter for the +password+ component.
- # (with validation)
+ # Public setter for the +password+ component
+ # (with validation).
#
- # see also URI::Generic.check_password
+ # See also URI::Generic.check_password.
#
# == Usage
#
@@ -495,9 +494,7 @@ module URI
#
# uri = URI.parse("http://john:S3nsit1ve@my.example.com")
# uri.password = "V3ry_S3nsit1ve"
- # # => "V3ry_S3nsit1ve"
- # uri
- # #=> #<URI::HTTP:0x00000000881d90 URL:http://john:V3ry_S3nsit1ve@my.example.com>
+ # uri.to_s #=> "http://john:V3ry_S3nsit1ve@my.example.com"
#
def password=(password)
check_password(password)
@@ -505,10 +502,10 @@ module URI
# returns password
end
- # protect setter for the +user+ component, and +password+ if available.
- # (with validation)
+ # Protected setter for the +user+ component, and +password+ if available
+ # (with validation).
#
- # see also URI::Generic.userinfo=
+ # See also URI::Generic.userinfo=.
#
def set_userinfo(user, password = nil)
unless password
@@ -521,9 +518,9 @@ module URI
end
protected :set_userinfo
- # protected setter for the user component +v+
+ # Protected setter for the user component +v+.
#
- # see also URI::Generic.user=
+ # See also URI::Generic.user=.
#
def set_user(v)
set_userinfo(v, @password)
@@ -531,9 +528,9 @@ module URI
end
protected :set_user
- # protected setter for the password component +v+
+ # Protected setter for the password component +v+.
#
- # see also URI::Generic.password=
+ # See also URI::Generic.password=.
#
def set_password(v)
@password = v
@@ -541,8 +538,8 @@ module URI
end
protected :set_password
- # returns the userinfo +ui+ as user, password
- # if properly formatted as 'user:password'
+ # Returns the userinfo +ui+ as <code>[user, password]</code>
+ # if properly formatted as 'user:password'.
def split_userinfo(ui)
return nil, nil unless ui
user, password = ui.split(':', 2)
@@ -551,13 +548,13 @@ module URI
end
private :split_userinfo
- # escapes 'user:password' +v+ based on RFC 1738 section 3.1
+ # Escapes 'user:password' +v+ based on RFC 1738 section 3.1.
def escape_userpass(v)
parser.escape(v, /[@:\/]/o) # RFC 1738 section 3.1 #/
end
private :escape_userpass
- # returns the userinfo, either as 'user' or 'user:password'
+ # Returns the userinfo, either as 'user' or 'user:password'.
def userinfo
if @user.nil?
nil
@@ -568,19 +565,19 @@ module URI
end
end
- # returns the user component
+ # Returns the user component.
def user
@user
end
- # returns the password component
+ # Returns the password component.
def password
@password
end
#
- # check the host +v+ component for RFC2396 compliance
- # and against the URI::Parser Regexp for :HOST
+ # Checks the host +v+ component for RFC2396 compliance
+ # and against the URI::Parser Regexp for :HOST.
#
# Can not have a registry or opaque component defined,
# with a host component defined.
@@ -600,9 +597,9 @@ module URI
end
private :check_host
- # protected setter for the host component +v+
+ # Protected setter for the host component +v+.
#
- # see also URI::Generic.host=
+ # See also URI::Generic.host=.
#
def set_host(v)
@host = v
@@ -617,10 +614,10 @@ module URI
#
# == Description
#
- # public setter for the host component +v+.
- # (with validation)
+ # Public setter for the host component +v+
+ # (with validation).
#
- # see also URI::Generic.check_host
+ # See also URI::Generic.check_host.
#
# == Usage
#
@@ -628,9 +625,7 @@ module URI
#
# uri = URI.parse("http://my.example.com")
# uri.host = "foo.com"
- # # => "foo.com"
- # uri
- # #=> #<URI::HTTP:0x000000008e89e8 URL:http://foo.com>
+ # uri.to_s #=> "http://foo.com"
#
def host=(v)
check_host(v)
@@ -638,32 +633,31 @@ module URI
v
end
- # extract the host part of the URI and unwrap brackets for IPv6 addresses.
+ # Extract the host part of the URI and unwrap brackets for IPv6 addresses.
#
- # This method is same as URI::Generic#host except
+ # This method is the same as URI::Generic#host except
# brackets for IPv6 (and future IP) addresses are removed.
#
- # u = URI("http://[::1]/bar")
- # p u.hostname #=> "::1"
- # p u.host #=> "[::1]"
+ # uri = URI("http://[::1]/bar")
+ # uri.hostname #=> "::1"
+ # uri.host #=> "[::1]"
#
def hostname
v = self.host
/\A\[(.*)\]\z/ =~ v ? $1 : v
end
- # set the host part of the URI as the argument with brackets for IPv6 addresses.
+ # Sets the host part of the URI as the argument with brackets for IPv6 addresses.
#
- # This method is same as URI::Generic#host= except
- # the argument can be bare IPv6 address.
+ # This method is the same as URI::Generic#host= except
+ # the argument can be a bare IPv6 address.
#
- # u = URI("http://foo/bar")
- # p u.to_s #=> "http://foo/bar"
- # u.hostname = "::1"
- # p u.to_s #=> "http://[::1]/bar"
+ # uri = URI("http://foo/bar")
+ # uri.hostname = "::1"
+ # uri.to_s #=> "http://[::1]/bar"
#
- # If the argument seems IPv6 address,
- # it is wrapped by brackets.
+ # If the argument seems to be an IPv6 address,
+ # it is wrapped with brackets.
#
def hostname=(v)
v = "[#{v}]" if /\A\[.*\]\z/ !~ v && /:/ =~ v
@@ -671,8 +665,8 @@ module URI
end
#
- # check the port +v+ component for RFC2396 compliance
- # and against the URI::Parser Regexp for :PORT
+ # Checks the port +v+ component for RFC2396 compliance
+ # and against the URI::Parser Regexp for :PORT.
#
# Can not have a registry or opaque component defined,
# with a port component defined.
@@ -692,9 +686,9 @@ module URI
end
private :check_port
- # protected setter for the port component +v+
+ # Protected setter for the port component +v+.
#
- # see also URI::Generic.port=
+ # See also URI::Generic.port=.
#
def set_port(v)
v = v.empty? ? nil : v.to_i unless !v || v.kind_of?(Integer)
@@ -710,10 +704,10 @@ module URI
#
# == Description
#
- # public setter for the port component +v+.
- # (with validation)
+ # Public setter for the port component +v+
+ # (with validation).
#
- # see also URI::Generic.check_port
+ # See also URI::Generic.check_port.
#
# == Usage
#
@@ -721,9 +715,7 @@ module URI
#
# uri = URI.parse("http://my.example.com")
# uri.port = 8080
- # # => 8080
- # uri
- # #=> #<URI::HTTP:0x000000008e89e8 URL:http://my.example.com:8080>
+ # uri.to_s #=> "http://my.example.com:8080"
#
def port=(v)
check_port(v)
@@ -746,9 +738,9 @@ module URI
end
#
- # check the path +v+ component for RFC2396 compliance
+ # Checks the path +v+ component for RFC2396 compliance
# and against the URI::Parser Regexp
- # for :ABS_PATH and :REL_PATH
+ # for :ABS_PATH and :REL_PATH.
#
# Can not have a opaque component defined,
# with a path component defined.
@@ -781,9 +773,9 @@ module URI
end
private :check_path
- # protected setter for the path component +v+
+ # Protected setter for the path component +v+.
#
- # see also URI::Generic.path=
+ # See also URI::Generic.path=.
#
def set_path(v)
@path = v
@@ -798,10 +790,10 @@ module URI
#
# == Description
#
- # public setter for the path component +v+.
- # (with validation)
+ # Public setter for the path component +v+
+ # (with validation).
#
- # see also URI::Generic.check_path
+ # See also URI::Generic.check_path.
#
# == Usage
#
@@ -809,9 +801,7 @@ module URI
#
# uri = URI.parse("http://my.example.com/pub/files")
# uri.path = "/faq/"
- # # => "/faq/"
- # uri
- # #=> #<URI::HTTP:0x000000008e89e8 URL:http://my.example.com/faq/>
+ # uri.to_s #=> "http://my.example.com/faq/"
#
def path=(v)
check_path(v)
@@ -827,7 +817,7 @@ module URI
#
# == Description
#
- # public setter for the query component +v+.
+ # Public setter for the query component +v+.
#
# == Usage
#
@@ -835,9 +825,7 @@ module URI
#
# uri = URI.parse("http://my.example.com/?id=25")
# uri.query = "id=1"
- # # => "id=1"
- # uri
- # #=> #<URI::HTTP:0x000000008e89e8 URL:http://my.example.com/?id=1>
+ # uri.to_s #=> "http://my.example.com/?id=1"
#
def query=(v)
return @query = nil unless v
@@ -854,10 +842,10 @@ module URI
end
#
- # check the opaque +v+ component for RFC2396 compliance and
- # against the URI::Parser Regexp for :OPAQUE
+ # Checks the opaque +v+ component for RFC2396 compliance and
+ # against the URI::Parser Regexp for :OPAQUE.
#
- # Can not have a host, port, user or path component defined,
+ # Can not have a host, port, user, or path component defined,
# with an opaque component defined.
#
def check_opaque(v)
@@ -878,9 +866,9 @@ module URI
end
private :check_opaque
- # protected setter for the opaque component +v+
+ # Protected setter for the opaque component +v+.
#
- # see also URI::Generic.opaque=
+ # See also URI::Generic.opaque=.
#
def set_opaque(v)
@opaque = v
@@ -895,10 +883,10 @@ module URI
#
# == Description
#
- # public setter for the opaque component +v+.
- # (with validation)
+ # Public setter for the opaque component +v+
+ # (with validation).
#
- # see also URI::Generic.check_opaque
+ # See also URI::Generic.check_opaque.
#
def opaque=(v)
check_opaque(v)
@@ -907,7 +895,7 @@ module URI
end
#
- # check the fragment +v+ component against the URI::Parser Regexp for :FRAGMENT
+ # Checks the fragment +v+ component against the URI::Parser Regexp for :FRAGMENT.
#
#
# == Args
@@ -917,8 +905,8 @@ module URI
#
# == Description
#
- # public setter for the fragment component +v+.
- # (with validation)
+ # Public setter for the fragment component +v+
+ # (with validation).
#
# == Usage
#
@@ -926,9 +914,7 @@ module URI
#
# uri = URI.parse("http://my.example.com/?id=25#time=1305212049")
# uri.fragment = "time=1305212086"
- # # => "time=1305212086"
- # uri
- # #=> #<URI::HTTP:0x000000007a81f8 URL:http://my.example.com/?id=25#time=1305212086>
+ # uri.to_s #=> "http://my.example.com/?id=25#time=1305212086"
#
def fragment=(v)
return @fragment = nil unless v
@@ -944,7 +930,23 @@ module URI
end
#
- # Checks if URI has a path
+ # Returns true if URI is hierarchical.
+ #
+ # == Description
+ #
+ # URI has components listed in order of decreasing significance from left to right,
+ # see RFC3986 https://tools.ietf.org/html/rfc3986 1.2.3.
+ #
+ # == Usage
+ #
+ # require 'uri'
+ #
+ # uri = URI.parse("http://my.example.com/")
+ # uri.hierarchical?
+ # #=> true
+ # uri = URI.parse("mailto:joe@example.com")
+ # uri.hierarchical?
+ # #=> false
#
def hierarchical?
if @path
@@ -955,7 +957,7 @@ module URI
end
#
- # Checks if URI is an absolute one
+ # Returns true if URI has a scheme (e.g. http:// or https://) specified.
#
def absolute?
if @scheme
@@ -967,17 +969,17 @@ module URI
alias absolute absolute?
#
- # Checks if URI is relative
+ # Returns true if URI does not have a scheme (e.g. http:// or https://) specified.
#
def relative?
!absolute?
end
#
- # returns an Array of the path split on '/'
+ # Returns an Array of the path split on '/'.
#
def split_path(path)
- path.split(%r{/+}, -1)
+ path.split("/", -1)
end
private :split_path
@@ -1056,7 +1058,7 @@ module URI
#
# == Description
#
- # Destructive form of #merge
+ # Destructive form of #merge.
#
# == Usage
#
@@ -1064,8 +1066,7 @@ module URI
#
# uri = URI.parse("http://my.example.com")
# uri.merge!("/main.rbx?page=1")
- # p uri
- # # => #<URI::HTTP:0x2021f3b0 URL:http://my.example.com/main.rbx?page=1>
+ # uri.to_s # => "http://my.example.com/main.rbx?page=1"
#
def merge!(oth)
t = merge(oth)
@@ -1085,15 +1086,15 @@ module URI
#
# == Description
#
- # Merges two URI's.
+ # Merges two URIs.
#
# == Usage
#
# require 'uri'
#
# uri = URI.parse("http://my.example.com")
- # p uri.merge("/main.rbx?page=1")
- # # => #<URI::HTTP:0x2021f3b0 URL:http://my.example.com/main.rbx?page=1>
+ # uri.merge("/main.rbx?page=1")
+ # # => "http://my.example.com/main.rbx?page=1"
#
def merge(oth)
rel = parser.send(:convert_to_uri, oth)
@@ -1152,8 +1153,8 @@ module URI
return dst.dup
end
- src_path = src.scan(%r{(?:\A|[^/]+)/})
- dst_path = dst.scan(%r{(?:\A|[^/]+)/?})
+ src_path = src.scan(%r{[^/]*/})
+ dst_path = dst.scan(%r{[^/]*/?})
# discard same parts
while !dst_path.empty? && dst_path.first == src_path.first
@@ -1238,15 +1239,15 @@ module URI
#
# == Description
#
- # Calculates relative path from oth to self
+ # Calculates relative path from oth to self.
#
# == Usage
#
# require 'uri'
#
# uri = URI.parse('http://my.example.com/main.rbx?page=1')
- # p uri.route_from('http://my.example.com')
- # #=> #<URI::Generic:0x20218858 URL:/main.rbx?page=1>
+ # uri.route_from('http://my.example.com')
+ # #=> #<URI::Generic /main.rbx?page=1>
#
def route_from(oth)
# you can modify `rel', but can not `oth'.
@@ -1278,22 +1279,32 @@ module URI
#
# == Description
#
- # Calculates relative path to oth from self
+ # Calculates relative path to oth from self.
#
# == Usage
#
# require 'uri'
#
# uri = URI.parse('http://my.example.com')
- # p uri.route_to('http://my.example.com/main.rbx?page=1')
- # #=> #<URI::Generic:0x2020c2f6 URL:/main.rbx?page=1>
+ # uri.route_to('http://my.example.com/main.rbx?page=1')
+ # #=> #<URI::Generic /main.rbx?page=1>
#
def route_to(oth)
parser.send(:convert_to_uri, oth).route_from(self)
end
#
- # Returns normalized URI
+ # Returns normalized URI.
+ #
+ # require 'uri'
+ #
+ # URI("HTTP://my.EXAMPLE.com").normalize
+ # #=> #<URI::HTTP http://my.example.com/>
+ #
+ # Normalization here means:
+ #
+ # * scheme and host are converted to lowercase,
+ # * an empty path component is set to "/".
#
def normalize
uri = dup
@@ -1302,7 +1313,7 @@ module URI
end
#
- # Destructive version of #normalize
+ # Destructive version of #normalize.
#
def normalize!
if path&.empty?
@@ -1317,7 +1328,7 @@ module URI
end
#
- # Constructs String from URI
+ # Constructs String from URI.
#
def to_s
str = ''.dup
@@ -1329,7 +1340,7 @@ module URI
if @opaque
str << @opaque
else
- if @host
+ if @host || %w[file postgres].include?(@scheme)
str << '//'
end
if self.userinfo
@@ -1357,7 +1368,7 @@ module URI
end
#
- # Compares to URI's
+ # Compares two URIs.
#
def ==(oth)
if self.class == oth.class
@@ -1390,7 +1401,7 @@ module URI
=end
- # returns an Array of the components defined from the COMPONENT Array
+ # Returns an Array of the components defined from the COMPONENT Array.
def component_ary
component.collect do |x|
self.send(x)
@@ -1401,18 +1412,18 @@ module URI
# == Args
#
# +components+::
- # Multiple Symbol arguments defined in URI::HTTP
+ # Multiple Symbol arguments defined in URI::HTTP.
#
# == Description
#
- # Selects specified components from URI
+ # Selects specified components from URI.
#
# == Usage
#
# require 'uri'
#
# uri = URI.parse('http://myuser:mypass@my.example.com/test.rbx')
- # p uri.select(:userinfo, :host, :path)
+ # uri.select(:userinfo, :host, :path)
# # => ["myuser:mypass", "my.example.com", "/test.rbx"]
#
def select(*components)
@@ -1438,8 +1449,8 @@ module URI
#
# == Description
#
- # attempt to parse other URI +oth+
- # return [parsed_oth, self]
+ # Attempts to parse other URI +oth+,
+ # returns [parsed_oth, self].
#
# == Usage
#
@@ -1447,7 +1458,7 @@ module URI
#
# uri = URI.parse("http://my.example.com")
# uri.coerce("http://foo.com")
- # #=> [#<URI::HTTP:0x00000000bcb028 URL:http://foo.com/>, #<URI::HTTP:0x00000000d92178 URL:http://my.example.com>]
+ # #=> [#<URI::HTTP http://foo.com>, #<URI::HTTP http://my.example.com>]
#
def coerce(oth)
case oth
@@ -1460,15 +1471,15 @@ module URI
return oth, self
end
- # returns a proxy URI.
+ # Returns a proxy URI.
# The proxy URI is obtained from environment variables such as http_proxy,
# ftp_proxy, no_proxy, etc.
# If there is no proper proxy, nil is returned.
#
- # If the optional parameter, +env+, is specified, it is used instead of ENV.
+ # If the optional parameter +env+ is specified, it is used instead of ENV.
#
# Note that capitalized variables (HTTP_PROXY, FTP_PROXY, NO_PROXY, etc.)
- # are examined too.
+ # are examined, too.
#
# But http_proxy and HTTP_PROXY is treated specially under CGI environment.
# It's because HTTP_PROXY may be set by Proxy: header.
@@ -1505,7 +1516,7 @@ module URI
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.'
+ warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.', uplevel: 1
end
end
else
@@ -1517,7 +1528,6 @@ module URI
end
if self.hostname
- require 'socket'
begin
addr = IPSocket.getaddress(self.hostname)
return nil if /\A127\.|\A::1\z/ =~ addr
@@ -1527,23 +1537,31 @@ module URI
name = 'no_proxy'
if no_proxy = env[name] || env[name.upcase]
- no_proxy.scan(/(?!\.)([^:,\s]+)(?::(\d+))?/) {|host, port|
- if (!port || self.port == port.to_i)
- if /(\A|\.)#{Regexp.quote host}\z/i =~ self.host
- return nil
- else
- require 'ipaddr'
- return nil if
- begin
- IPAddr.new(host)
- rescue IPAddr::InvalidAddressError
- next
- end.include?(self.host)
- end
- end
- }
+ return nil unless URI::Generic.use_proxy?(self.hostname, addr, self.port, no_proxy)
end
URI.parse(proxy_uri)
end
+
+ def self.use_proxy?(hostname, addr, port, no_proxy) # :nodoc:
+ hostname = hostname.downcase
+ dothostname = ".#{hostname}"
+ no_proxy.scan(/([^:,\s]+)(?::(\d+))?/) {|p_host, p_port|
+ if !p_port || port == p_port.to_i
+ if p_host.start_with?('.')
+ return false if hostname.end_with?(p_host.downcase)
+ else
+ return false if dothostname.end_with?(".#{p_host.downcase}")
+ end
+ if addr
+ begin
+ return false if IPAddr.new(p_host).include?(addr)
+ rescue IPAddr::InvalidAddressError
+ next
+ end
+ end
+ end
+ }
+ true
+ end
end
end
diff --git a/lib/uri/http.rb b/lib/uri/http.rb
index d4373e73e5..2e2ebcc1d3 100644
--- a/lib/uri/http.rb
+++ b/lib/uri/http.rb
@@ -8,7 +8,7 @@
# See URI for general documentation
#
-require 'uri/generic'
+require_relative 'generic'
module URI
@@ -21,10 +21,10 @@ module URI
# update. See <URL:http://support.microsoft.com/kb/834489>.
#
class HTTP < Generic
- # A Default port of 80 for URI::HTTP
+ # A Default port of 80 for URI::HTTP.
DEFAULT_PORT = 80
- # An Array of the available components for URI::HTTP
+ # An Array of the available components for URI::HTTP.
COMPONENT = %i[
scheme
userinfo host port
@@ -36,22 +36,22 @@ module URI
#
# == Description
#
- # Create a new URI::HTTP object from components, with syntax checking.
+ # Creates a new URI::HTTP object from components, with syntax checking.
#
- # The components accepted are userinfo, host, port, path, query and
+ # The components accepted are userinfo, host, port, path, query, and
# fragment.
#
# The components should be provided either as an Array, or as a Hash
# with keys formed by preceding the component names with a colon.
#
- # If an Array is used, the components must be passed in the order
- # [userinfo, host, port, path, query, fragment].
+ # If an Array is used, the components must be passed in the
+ # order <code>[userinfo, host, port, path, query, fragment]</code>.
#
# Example:
#
- # newuri = URI::HTTP.build(host: 'www.example.com', path: '/foo/bar')
+ # uri = URI::HTTP.build(host: 'www.example.com', path: '/foo/bar')
#
- # newuri = URI::HTTP.build([nil, "www.example.com", nil, "/path",
+ # uri = URI::HTTP.build([nil, "www.example.com", nil, "/path",
# "query", 'fragment'])
#
# Currently, if passed userinfo components this method generates
@@ -62,30 +62,6 @@ module URI
super(tmp)
end
-=begin
- #
- # == Description
- #
- # Create a new URI::HTTP object from generic URI components as per
- # RFC 2396. No HTTP-specific syntax checking (as per RFC 1738) is
- # performed.
- #
- # Arguments are +scheme+, +userinfo+, +host+, +port+, +registry+, +path+,
- # +opaque+, +query+ and +fragment+, in that order.
- #
- # Example:
- #
- # uri = URI::HTTP.new("http", nil, "www.example.com", nil, nil,
- # "/path", nil, "query", "fragment")
- #
- #
- # See also URI::Generic.new
- #
- def initialize(*arg)
- super(*arg)
- end
-=end
-
#
# == Description
#
@@ -96,8 +72,8 @@ module URI
#
# Example:
#
- # newuri = URI::HTTP.build(path: '/foo/bar', query: 'test=true')
- # newuri.request_uri # => "/foo/bar?test=true"
+ # uri = URI::HTTP.build(path: '/foo/bar', query: 'test=true')
+ # uri.request_uri # => "/foo/bar?test=true"
#
def request_uri
return unless @path
diff --git a/lib/uri/https.rb b/lib/uri/https.rb
index 3c8c905cc3..4780ee0a44 100644
--- a/lib/uri/https.rb
+++ b/lib/uri/https.rb
@@ -8,7 +8,7 @@
# See URI for general documentation
#
-require 'uri/http'
+require_relative 'http'
module URI
diff --git a/lib/uri/ldap.rb b/lib/uri/ldap.rb
index 4345875e28..61ec3d3f12 100644
--- a/lib/uri/ldap.rb
+++ b/lib/uri/ldap.rb
@@ -12,20 +12,21 @@
# See URI for general documentation
#
-require 'uri/generic'
+require_relative 'generic'
module URI
#
- # LDAP URI SCHEMA (described in RFC2255)
+ # LDAP URI SCHEMA (described in RFC2255).
+ #--
# ldap://<host>/<dn>[?<attrs>[?<scope>[?<filter>[?<extensions>]]]]
- #
+ #++
class LDAP < Generic
- # A Default port of 389 for URI::LDAP
+ # A Default port of 389 for URI::LDAP.
DEFAULT_PORT = 389
- # An Array of the available components for URI::LDAP
+ # An Array of the available components for URI::LDAP.
COMPONENT = [
:scheme,
:host, :port,
@@ -40,8 +41,8 @@ module URI
#
# * SCOPE_BASE - the Base DN
# * SCOPE_ONE - one level under the Base DN, not including the base DN and
- # not including any entries under this.
- # * SCOPE_SUB - subtress, all entries at all levels
+ # not including any entries under this
+ # * SCOPE_SUB - subtrees, all entries at all levels
#
SCOPE = [
SCOPE_ONE = 'one',
@@ -52,7 +53,7 @@ module URI
#
# == Description
#
- # Create a new URI::LDAP object from components, with syntax checking.
+ # Creates a new URI::LDAP object from components, with syntax checking.
#
# The components accepted are host, port, dn, attributes,
# scope, filter, and extensions.
@@ -60,15 +61,15 @@ module URI
# The components should be provided either as an Array, or as a Hash
# with keys formed by preceding the component names with a colon.
#
- # If an Array is used, the components must be passed in the order
- # [host, port, dn, attributes, scope, filter, extensions].
+ # If an Array is used, the components must be passed in the
+ # order <code>[host, port, dn, attributes, scope, filter, extensions]</code>.
#
# Example:
#
- # newuri = URI::LDAP.build({:host => 'ldap.example.com',
- # :dn> => '/dc=example'})
+ # uri = URI::LDAP.build({:host => 'ldap.example.com',
+ # :dn => '/dc=example'})
#
- # newuri = URI::LDAP.build(["ldap.example.com", nil,
+ # uri = URI::LDAP.build(["ldap.example.com", nil,
# "/dc=example;dc=com", "query", nil, nil, nil])
#
def self.build(args)
@@ -92,19 +93,18 @@ module URI
#
# == Description
#
- # Create a new URI::LDAP object from generic URI components as per
+ # Creates a new URI::LDAP object from generic URI components as per
# RFC 2396. No LDAP-specific syntax checking is performed.
#
# Arguments are +scheme+, +userinfo+, +host+, +port+, +registry+, +path+,
- # +opaque+, +query+ and +fragment+, in that order.
+ # +opaque+, +query+, and +fragment+, in that order.
#
# Example:
#
- # uri = URI::LDAP.new("ldap", nil, "ldap.example.com", nil,
- # "/dc=example;dc=com", "query", nil, nil, nil, nil)
- #
+ # uri = URI::LDAP.new("ldap", nil, "ldap.example.com", nil, nil,
+ # "/dc=example;dc=com", nil, "query", nil)
#
- # See also URI::Generic.new
+ # See also URI::Generic.new.
#
def initialize(*arg)
super(*arg)
@@ -117,14 +117,15 @@ module URI
parse_query
end
- # private method to cleanup +dn+ from using the +path+ component attribute
+ # Private method to cleanup +dn+ from using the +path+ component attribute.
def parse_dn
+ raise InvalidURIError, 'bad LDAP URL' unless @path
@dn = @path[1..-1]
end
private :parse_dn
- # private method to cleanup +attributes+, +scope+, +filter+ and +extensions+,
- # from using the +query+ component attribute
+ # Private method to cleanup +attributes+, +scope+, +filter+, and +extensions+
+ # from using the +query+ component attribute.
def parse_query
@attributes = nil
@scope = nil
@@ -142,7 +143,7 @@ module URI
end
private :parse_query
- # private method to assemble +query+ from +attributes+, +scope+, +filter+ and +extensions+.
+ # Private method to assemble +query+ from +attributes+, +scope+, +filter+, and +extensions+.
def build_path_query
@path = '/' + @dn
@@ -155,12 +156,12 @@ module URI
end
private :build_path_query
- # returns dn.
+ # Returns dn.
def dn
@dn
end
- # private setter for dn +val+
+ # Private setter for dn +val+.
def set_dn(val)
@dn = val
build_path_query
@@ -168,18 +169,18 @@ module URI
end
protected :set_dn
- # setter for dn +val+
+ # Setter for dn +val+.
def dn=(val)
set_dn(val)
val
end
- # returns attributes.
+ # Returns attributes.
def attributes
@attributes
end
- # private setter for attributes +val+
+ # Private setter for attributes +val+.
def set_attributes(val)
@attributes = val
build_path_query
@@ -187,18 +188,18 @@ module URI
end
protected :set_attributes
- # setter for attributes +val+
+ # Setter for attributes +val+.
def attributes=(val)
set_attributes(val)
val
end
- # returns scope.
+ # Returns scope.
def scope
@scope
end
- # private setter for scope +val+
+ # Private setter for scope +val+.
def set_scope(val)
@scope = val
build_path_query
@@ -206,18 +207,18 @@ module URI
end
protected :set_scope
- # setter for scope +val+
+ # Setter for scope +val+.
def scope=(val)
set_scope(val)
val
end
- # returns filter.
+ # Returns filter.
def filter
@filter
end
- # private setter for filter +val+
+ # Private setter for filter +val+.
def set_filter(val)
@filter = val
build_path_query
@@ -225,18 +226,18 @@ module URI
end
protected :set_filter
- # setter for filter +val+
+ # Setter for filter +val+.
def filter=(val)
set_filter(val)
val
end
- # returns extensions.
+ # Returns extensions.
def extensions
@extensions
end
- # private setter for extensions +val+
+ # Private setter for extensions +val+.
def set_extensions(val)
@extensions = val
build_path_query
@@ -244,14 +245,14 @@ module URI
end
protected :set_extensions
- # setter for extensions +val+
+ # Setter for extensions +val+.
def extensions=(val)
set_extensions(val)
val
end
- # Checks if URI has a path
- # For URI::LDAP this will return +false+
+ # Checks if URI has a path.
+ # For URI::LDAP this will return +false+.
def hierarchical?
false
end
diff --git a/lib/uri/ldaps.rb b/lib/uri/ldaps.rb
index d03f8efa2d..227e7fab35 100644
--- a/lib/uri/ldaps.rb
+++ b/lib/uri/ldaps.rb
@@ -6,7 +6,7 @@
# See URI for general documentation
#
-require 'uri/ldap'
+require_relative 'ldap'
module URI
diff --git a/lib/uri/mailto.rb b/lib/uri/mailto.rb
index b9e98e66bb..9c06871c7a 100644
--- a/lib/uri/mailto.rb
+++ b/lib/uri/mailto.rb
@@ -8,20 +8,20 @@
# See URI for general documentation
#
-require 'uri/generic'
+require_relative 'generic'
module URI
#
- # RFC6068, The mailto URL scheme
+ # RFC6068, the mailto URL scheme.
#
class MailTo < Generic
include REGEXP
- # A Default port of nil for URI::MailTo
+ # A Default port of nil for URI::MailTo.
DEFAULT_PORT = nil
- # An Array of the available components for URI::MailTo
+ # An Array of the available components for URI::MailTo.
COMPONENT = [ :scheme, :to, :headers ].freeze
# :stopdoc:
@@ -52,7 +52,7 @@ module URI
# pct-encoded = "%" HEXDIG HEXDIG
HEADER_REGEXP = /\A(?<hfield>(?:%\h\h|[!$'-.0-;@-Z_a-z~])*=(?:%\h\h|[!$'-.0-;@-Z_a-z~])*)(?:&\g<hfield>)*\z/
# practical regexp for email address
- # http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address
+ # https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
EMAIL_REGEXP = /\A[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\z/
# :startdoc:
@@ -62,26 +62,26 @@ module URI
# Creates a new URI::MailTo object from components, with syntax checking.
#
# Components can be provided as an Array or Hash. If an Array is used,
- # the components must be supplied as [to, headers].
+ # the components must be supplied as <code>[to, headers]</code>.
#
# If a Hash is used, the keys are the component names preceded by colons.
#
# The headers can be supplied as a pre-encoded string, such as
- # "subject=subscribe&cc=address", or as an Array of Arrays like
- # [['subject', 'subscribe'], ['cc', 'address']]
+ # <code>"subject=subscribe&cc=address"</code>, or as an Array of Arrays
+ # like <code>[['subject', 'subscribe'], ['cc', 'address']]</code>.
#
# Examples:
#
# require 'uri'
#
# m1 = URI::MailTo.build(['joe@example.com', 'subject=Ruby'])
- # puts m1.to_s -> mailto:joe@example.com?subject=Ruby
+ # m1.to_s # => "mailto:joe@example.com?subject=Ruby"
#
# m2 = URI::MailTo.build(['john@example.com', [['Subject', 'Ruby'], ['Cc', 'jack@example.com']]])
- # puts m2.to_s -> mailto:john@example.com?Subject=Ruby&Cc=jack@example.com
+ # m2.to_s # => "mailto:john@example.com?Subject=Ruby&Cc=jack@example.com"
#
# m3 = URI::MailTo.build({:to => 'listman@example.com', :headers => [['subject', 'subscribe']]})
- # puts m3.to_s -> mailto:listman@example.com?subject=subscribe
+ # m3.to_s # => "mailto:listman@example.com?subject=subscribe"
#
def self.build(args)
tmp = Util.make_components_hash(self, args)
@@ -160,13 +160,13 @@ module URI
end
end
- # The primary e-mail address of the URL, as a String
+ # The primary e-mail address of the URL, as a String.
attr_reader :to
- # E-mail headers set by the URL, as an Array of Arrays
+ # E-mail headers set by the URL, as an Array of Arrays.
attr_reader :headers
- # check the to +v+ component
+ # Checks the to +v+ component.
def check_to(v)
return true unless v
return true if v.size == 0
@@ -191,20 +191,20 @@ module URI
end
private :check_to
- # private setter for to +v+
+ # Private setter for to +v+.
def set_to(v)
@to = v
end
protected :set_to
- # setter for to +v+
+ # Setter for to +v+.
def to=(v)
check_to(v)
set_to(v)
v
end
- # check the headers +v+ component against either
+ # Checks the headers +v+ component against either
# * HEADER_REGEXP
def check_headers(v)
return true unless v
@@ -218,7 +218,7 @@ module URI
end
private :check_headers
- # private setter for headers +v+
+ # Private setter for headers +v+.
def set_headers(v)
@headers = []
if v
@@ -229,14 +229,14 @@ module URI
end
protected :set_headers
- # setter for headers +v+
+ # Setter for headers +v+.
def headers=(v)
check_headers(v)
set_headers(v)
v
end
- # Constructs String from URI
+ # Constructs String from URI.
def to_s
@scheme + ':' +
if @to
@@ -267,18 +267,18 @@ module URI
# # => "To: ruby-list@ruby-lang.org\nSubject: subscribe\nCc: myaddr\n\n\n"
#
def to_mailtext
- to = parser.unescape(@to)
+ to = URI.decode_www_form_component(@to)
head = ''
body = ''
@headers.each do |x|
case x[0]
when 'body'
- body = parser.unescape(x[1])
+ body = URI.decode_www_form_component(x[1])
when 'to'
- to << ', ' + parser.unescape(x[1])
+ to << ', ' + URI.decode_www_form_component(x[1])
else
- head << parser.unescape(x[0]).capitalize + ': ' +
- parser.unescape(x[1]) + "\n"
+ head << URI.decode_www_form_component(x[0]).capitalize + ': ' +
+ URI.decode_www_form_component(x[1]) + "\n"
end
end
diff --git a/lib/uri/rfc2396_parser.rb b/lib/uri/rfc2396_parser.rb
index b9e7b2b26e..843fe124ee 100644
--- a/lib/uri/rfc2396_parser.rb
+++ b/lib/uri/rfc2396_parser.rb
@@ -58,7 +58,7 @@ module URI
# :startdoc:
end # REGEXP
- # class that Parses String's into URI's
+ # Class that parses String's into URI's.
#
# It contains a Hash set of patterns and Regexp's that match and validate.
#
@@ -88,12 +88,12 @@ module URI
# == Examples
#
# p = URI::Parser.new(:ESCAPED => "(?:%[a-fA-F0-9]{2}|%u[a-fA-F0-9]{4})")
- # u = p.parse("http://example.jp/%uABCD") #=> #<URI::HTTP:0xb78cf4f8 URL:http://example.jp/%uABCD>
+ # u = p.parse("http://example.jp/%uABCD") #=> #<URI::HTTP http://example.jp/%uABCD>
# URI.parse(u.to_s) #=> raises URI::InvalidURIError
#
# s = "http://example.com/ABCD"
- # u1 = p.parse(s) #=> #<URI::HTTP:0xb78c3220 URL:http://example.com/ABCD>
- # u2 = URI.parse(s) #=> #<URI::HTTP:0xb78b6d54 URL:http://example.com/ABCD>
+ # u1 = p.parse(s) #=> #<URI::HTTP http://example.com/ABCD>
+ # u2 = URI.parse(s) #=> #<URI::HTTP http://example.com/ABCD>
# u1 == u2 #=> true
# u1.eql?(u2) #=> false
#
@@ -109,15 +109,15 @@ module URI
# The Hash of patterns.
#
- # see also URI::Parser.initialize_pattern
+ # See also URI::Parser.initialize_pattern.
attr_reader :pattern
- # The Hash of Regexp
+ # The Hash of Regexp.
#
- # see also URI::Parser.initialize_regexp
+ # See also URI::Parser.initialize_regexp.
attr_reader :regexp
- # Returns a split URI against regexp[:ABS_URI]
+ # Returns a split URI against regexp[:ABS_URI].
def split(uri)
case uri
when ''
@@ -198,14 +198,14 @@ module URI
#
# == Description
#
- # parses +uri+ and constructs either matching URI scheme object
- # (FTP, HTTP, HTTPS, LDAP, LDAPS, or MailTo) or URI::Generic
+ # Parses +uri+ and constructs either matching URI scheme object
+ # (File, FTP, HTTP, HTTPS, LDAP, LDAPS, or MailTo) or URI::Generic.
#
# == Usage
#
# p = URI::Parser.new
# p.parse("ldap://ldap.example.com/dc=example?user=john")
- # #=> #<URI::LDAP:0x00000000b9e7e8 URL:ldap://ldap.example.com/dc=example?user=john>
+ # #=> #<URI::LDAP ldap://ldap.example.com/dc=example?user=john>
#
def parse(uri)
scheme, userinfo, host, port,
@@ -231,7 +231,7 @@ module URI
#
# == Description
#
- # Attempts to parse and merge a set of URIs
+ # Attempts to parse and merge a set of URIs.
#
def join(*uris)
uris[0] = convert_to_uri(uris[0])
@@ -253,11 +253,11 @@ module URI
#
# == Description
#
- # Attempts to parse and merge a set of URIs
- # If no +block+ given , then returns the result,
+ # Attempts to parse and merge a set of URIs.
+ # If no +block+ given, then returns the result,
# else it calls +block+ for each element in result.
#
- # see also URI::Parser.make_regexp
+ # See also URI::Parser.make_regexp.
#
def extract(str, schemes = nil)
if block_given?
@@ -270,8 +270,8 @@ module 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]
@@ -294,7 +294,7 @@ module URI
#
# == Description
#
- # constructs a safe String from +str+, removing unsafe characters,
+ # Constructs a safe String from +str+, removing unsafe characters,
# replacing them with codes.
#
def escape(str, unsafe = @regexp[:UNSAFE])
@@ -315,21 +315,23 @@ module URI
#
# :call-seq:
# unescape( str )
- # unescape( str, unsafe )
+ # unescape( str, escaped )
#
# == Args
#
# +str+::
# String to remove escapes from
- # +unsafe+::
+ # +escaped+::
# Regexp to apply. Defaults to self.regexp[:ESCAPED]
#
# == Description
#
- # Removes escapes from +str+
+ # Removes escapes from +str+.
#
def unescape(str, escaped = @regexp[:ESCAPED])
- str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(str.encoding)
+ enc = str.encoding
+ enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
+ str.gsub(escaped) { [$&[1, 2]].pack('H2').force_encoding(enc) }
end
@@to_s = Kernel.instance_method(:to_s)
@@ -339,7 +341,7 @@ module URI
private
- # Constructs the default Hash of patterns
+ # Constructs the default Hash of patterns.
def initialize_pattern(opts = {})
ret = {}
ret[:ESCAPED] = escaped = (opts.delete(:ESCAPED) || PATTERN::ESCAPED)
@@ -497,7 +499,7 @@ module URI
ret
end
- # Constructs the default Hash of Regexp's
+ # Constructs the default Hash of Regexp's.
def initialize_regexp(pattern)
ret = {}
diff --git a/lib/uri/rfc3986_parser.rb b/lib/uri/rfc3986_parser.rb
index 871280044a..135e847e88 100644
--- a/lib/uri/rfc3986_parser.rb
+++ b/lib/uri/rfc3986_parser.rb
@@ -15,7 +15,7 @@ module URI
begin
uri = uri.to_str
rescue NoMethodError
- raise InvalidURIError, "bad URI(is not URI?): #{uri}"
+ raise InvalidURIError, "bad URI(is not URI?): #{uri.inspect}"
end
uri.ascii_only? or
raise InvalidURIError, "URI must be ascii only #{uri.dump}"
@@ -64,7 +64,7 @@ module URI
m["fragment".freeze]
]
else
- raise InvalidURIError, "bad URI(is not URI?): #{uri}"
+ raise InvalidURIError, "bad URI(is not URI?): #{uri.inspect}"
end
end
diff --git a/lib/weakref.rb b/lib/weakref.rb
index cdfbe4a679..824d4016e1 100644
--- a/lib/weakref.rb
+++ b/lib/weakref.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require "delegate"
# Weak Reference class that allows a referenced object to be
@@ -15,53 +15,6 @@ require "delegate"
# GC.start # start the garbage collector
# p foo.to_s # should raise exception (recycled)
#
-# == Example
-#
-# With help from WeakRef, we can implement our own rudimentary WeakHash class.
-#
-# We will call it WeakHash, since it's really just a Hash except all of it's
-# keys and values can be garbage collected.
-#
-# require 'weakref'
-#
-# class WeakHash < Hash
-# def []= key, obj
-# super WeakRef.new(key), WeakRef.new(obj)
-# end
-# end
-#
-# This is just a simple implementation, we've opened the Hash class and changed
-# Hash#store to create a new WeakRef object with +key+ and +obj+ parameters
-# before passing them as our key-value pair to the hash.
-#
-# With this you will have to limit your self to String keys, otherwise you
-# will get an ArgumentError because WeakRef cannot create a finalizer for a
-# Symbol. Symbols are immutable and cannot be garbage collected.
-#
-# Let's see it in action:
-#
-# omg = "lol"
-# c = WeakHash.new
-# c['foo'] = "bar"
-# c['baz'] = Object.new
-# c['qux'] = omg
-# puts c.inspect
-# #=> {"foo"=>"bar", "baz"=>#<Object:0x007f4ddfc6cb48>, "qux"=>"lol"}
-#
-# # Now run the garbage collector
-# GC.start
-# c['foo'] #=> nil
-# c['baz'] #=> nil
-# c['qux'] #=> nil
-# omg #=> "lol"
-#
-# puts c.inspect
-# #=> WeakRef::RefError: Invalid Reference - probably recycled
-#
-# You can see the local variable +omg+ stayed, although its reference in our
-# hash object was garbage collected, along with the rest of the keys and
-# values. Also, when we tried to inspect our hash, we got a WeakRef::RefError.
-# This is because these objects were also garbage collected.
class WeakRef < Delegator
@@ -78,7 +31,7 @@ class WeakRef < Delegator
# Creates a weak reference to +orig+
#
# Raises an ArgumentError if the given +orig+ is immutable, such as Symbol,
- # Fixnum, or Float.
+ # Integer, or Float.
def initialize(orig)
case orig
diff --git a/lib/webrick/.document b/lib/webrick/.document
new file mode 100644
index 0000000000..c62f89083b
--- /dev/null
+++ b/lib/webrick/.document
@@ -0,0 +1,6 @@
+# Add files to this as they become documented
+
+*.rb
+
+httpauth
+httpservlet
diff --git a/lib/webrick/cgi.rb b/lib/webrick/cgi.rb
index 94f385f1dd..bb0ae2fc84 100644
--- a/lib/webrick/cgi.rb
+++ b/lib/webrick/cgi.rb
@@ -8,9 +8,9 @@
#
# $Id$
-require "webrick/httprequest"
-require "webrick/httpresponse"
-require "webrick/config"
+require_relative "httprequest"
+require_relative "httpresponse"
+require_relative "config"
require "stringio"
module WEBrick
@@ -265,6 +265,10 @@ module WEBrick
@out_port << data
end
+ def write(data)
+ @out_port.write(data)
+ end
+
def cert
return nil unless defined?(OpenSSL)
if pem = @env["SSL_SERVER_CERT"]
diff --git a/lib/webrick/config.rb b/lib/webrick/config.rb
index 98c9701633..9f2ab44f49 100644
--- a/lib/webrick/config.rb
+++ b/lib/webrick/config.rb
@@ -9,19 +9,25 @@
#
# $IPR: config.rb,v 1.52 2003/07/22 19:20:42 gotoyuzo Exp $
-require 'webrick/version'
-require 'webrick/httpversion'
-require 'webrick/httputils'
-require 'webrick/utils'
-require 'webrick/log'
+require_relative 'version'
+require_relative 'httpversion'
+require_relative 'httputils'
+require_relative 'utils'
+require_relative 'log'
module WEBrick
module Config
LIBDIR = File::dirname(__FILE__) # :nodoc:
# for GenericServer
- General = {
- :ServerName => Utils::getservername,
+ General = Hash.new { |hash, key|
+ case key
+ when :ServerName
+ hash[key] = Utils.getservername
+ else
+ nil
+ end
+ }.update(
:BindAddress => nil, # "0.0.0.0" or "::" or nil
:Port => nil, # users MUST specify this!!
:MaxClients => 100, # maximum number of the concurrent connections
@@ -36,7 +42,7 @@ module WEBrick
:AcceptCallback => nil,
:DoNotReverseLookup => true,
:ShutdownSocketWithoutClose => false,
- }
+ )
# for HTTPServer, HTTPRequest, HTTPResponse ...
HTTP = General.dup.update(
diff --git a/lib/webrick/cookie.rb b/lib/webrick/cookie.rb
index 24bf92ec00..5fd3bfb228 100644
--- a/lib/webrick/cookie.rb
+++ b/lib/webrick/cookie.rb
@@ -10,7 +10,7 @@
# $IPR: cookie.rb,v 1.16 2002/09/21 12:23:35 gotoyuzo Exp $
require 'time'
-require 'webrick/httputils'
+require_relative 'httputils'
module WEBrick
diff --git a/lib/webrick/httpauth.rb b/lib/webrick/httpauth.rb
index bbb6776528..f8bf09a6f1 100644
--- a/lib/webrick/httpauth.rb
+++ b/lib/webrick/httpauth.rb
@@ -9,11 +9,11 @@
#
# $IPR: httpauth.rb,v 1.14 2003/07/22 19:20:42 gotoyuzo Exp $
-require 'webrick/httpauth/basicauth'
-require 'webrick/httpauth/digestauth'
-require 'webrick/httpauth/htpasswd'
-require 'webrick/httpauth/htdigest'
-require 'webrick/httpauth/htgroup'
+require_relative 'httpauth/basicauth'
+require_relative 'httpauth/digestauth'
+require_relative 'httpauth/htpasswd'
+require_relative 'httpauth/htdigest'
+require_relative 'httpauth/htgroup'
module WEBrick
diff --git a/lib/webrick/httpauth/basicauth.rb b/lib/webrick/httpauth/basicauth.rb
index e23420fdfa..7d0a9cfc8f 100644
--- a/lib/webrick/httpauth/basicauth.rb
+++ b/lib/webrick/httpauth/basicauth.rb
@@ -8,9 +8,9 @@
#
# $IPR: basicauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $
-require 'webrick/config'
-require 'webrick/httpstatus'
-require 'webrick/httpauth/authenticator'
+require_relative '../config'
+require_relative '../httpstatus'
+require_relative 'authenticator'
module WEBrick
module HTTPAuth
@@ -24,7 +24,7 @@ module WEBrick
#
# config = { :Realm => 'BasicAuth example realm' }
#
- # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file'
+ # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file', password_hash: :bcrypt
# htpasswd.set_passwd config[:Realm], 'username', 'password'
# htpasswd.flush
#
@@ -81,7 +81,15 @@ module WEBrick
error("%s: the user is not allowed.", userid)
challenge(req, res)
end
- if password.crypt(encpass) != encpass
+
+ case encpass
+ when /\A\$2[aby]\$/
+ password_matches = BCrypt::Password.new(encpass.sub(/\A\$2[aby]\$/, '$2a$')) == password
+ else
+ password_matches = password.crypt(encpass) == encpass
+ end
+
+ unless password_matches
error("%s: password unmatch.", userid)
challenge(req, res)
end
diff --git a/lib/webrick/httpauth/digestauth.rb b/lib/webrick/httpauth/digestauth.rb
index 375ec20154..3cf12899d2 100644
--- a/lib/webrick/httpauth/digestauth.rb
+++ b/lib/webrick/httpauth/digestauth.rb
@@ -12,9 +12,9 @@
#
# $IPR: digestauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $
-require 'webrick/config'
-require 'webrick/httpstatus'
-require 'webrick/httpauth/authenticator'
+require_relative '../config'
+require_relative '../httpstatus'
+require_relative 'authenticator'
require 'digest/md5'
require 'digest/sha1'
@@ -235,9 +235,11 @@ module WEBrick
ha2 = hexdigest(req.request_method, auth_req['uri'])
ha2_res = hexdigest("", auth_req['uri'])
elsif auth_req['qop'] == "auth-int"
- ha2 = hexdigest(req.request_method, auth_req['uri'],
- hexdigest(req.body))
- ha2_res = hexdigest("", auth_req['uri'], hexdigest(res.body))
+ body_digest = @h.new
+ req.body { |chunk| body_digest.update(chunk) }
+ body_digest = body_digest.hexdigest
+ ha2 = hexdigest(req.request_method, auth_req['uri'], body_digest)
+ ha2_res = hexdigest("", auth_req['uri'], body_digest)
end
if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int"
@@ -288,23 +290,8 @@ module WEBrick
def split_param_value(string)
ret = {}
- while string.bytesize != 0
- case string
- when /^\s*([\w\-\.\*\%\!]+)=\s*\"((\\.|[^\"])*)\"\s*,?/
- key = $1
- matched = $2
- string = $'
- ret[key] = matched.gsub(/\\(.)/, "\\1")
- when /^\s*([\w\-\.\*\%\!]+)=\s*([^,\"]*),?/
- key = $1
- matched = $2
- string = $'
- ret[key] = matched.clone
- when /^s*^,/
- string = $'
- else
- break
- end
+ string.scan(/\G\s*([\w\-.*%!]+)=\s*(?:\"((?>\\.|[^\"])*)\"|([^,\"]*))\s*,?/) do
+ ret[$1] = $3 || $2.gsub(/\\(.)/, "\\1")
end
ret
end
diff --git a/lib/webrick/httpauth/htdigest.rb b/lib/webrick/httpauth/htdigest.rb
index 1ef4fdb4aa..93b18e2c75 100644
--- a/lib/webrick/httpauth/htdigest.rb
+++ b/lib/webrick/httpauth/htdigest.rb
@@ -8,8 +8,8 @@
#
# $IPR: htdigest.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
-require 'webrick/httpauth/userdb'
-require 'webrick/httpauth/digestauth'
+require_relative 'userdb'
+require_relative 'digestauth'
require 'tempfile'
module WEBrick
@@ -40,7 +40,7 @@ module WEBrick
@digest = Hash.new
@mutex = Thread::Mutex::new
@auth_type = DigestAuth
- open(@path,"a").close unless File::exist?(@path)
+ File.open(@path,"a").close unless File.exist?(@path)
reload
end
@@ -51,7 +51,7 @@ module WEBrick
mtime = File::mtime(@path)
if mtime > @mtime
@digest.clear
- open(@path){|io|
+ File.open(@path){|io|
while line = io.gets
line.chomp!
user, realm, pass = line.split(/:/, 3)
@@ -79,7 +79,7 @@ module WEBrick
File::rename(tmp.path, output)
renamed = true
ensure
- tmp.close if !tmp.closed?
+ tmp.close
File.unlink(tmp.path) if !renamed
end
end
diff --git a/lib/webrick/httpauth/htgroup.rb b/lib/webrick/httpauth/htgroup.rb
index 832ae8bd04..e06c441b18 100644
--- a/lib/webrick/httpauth/htgroup.rb
+++ b/lib/webrick/httpauth/htgroup.rb
@@ -36,7 +36,7 @@ module WEBrick
@path = path
@mtime = Time.at(0)
@group = Hash.new
- open(@path,"a").close unless File::exist?(@path)
+ File.open(@path,"a").close unless File.exist?(@path)
reload
end
@@ -46,7 +46,7 @@ module WEBrick
def reload
if (mtime = File::mtime(@path)) > @mtime
@group.clear
- open(@path){|io|
+ File.open(@path){|io|
while line = io.gets
line.chomp!
group, members = line.split(/:\s*/)
@@ -63,15 +63,18 @@ module WEBrick
def flush(output=nil)
output ||= @path
- tmp = Tempfile.new("htgroup", File::dirname(output))
+ tmp = Tempfile.create("htgroup", File::dirname(output))
begin
@group.keys.sort.each{|group|
tmp.puts(format("%s: %s", group, self.members(group).join(" ")))
}
+ ensure
tmp.close
- File::rename(tmp.path, output)
- rescue
- tmp.close(true)
+ if $!
+ File.unlink(tmp.path)
+ else
+ return File.rename(tmp.path, output)
+ end
end
end
diff --git a/lib/webrick/httpauth/htpasswd.rb b/lib/webrick/httpauth/htpasswd.rb
index f43fc2c548..abca30532e 100644
--- a/lib/webrick/httpauth/htpasswd.rb
+++ b/lib/webrick/httpauth/htpasswd.rb
@@ -8,8 +8,8 @@
#
# $IPR: htpasswd.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
-require 'webrick/httpauth/userdb'
-require 'webrick/httpauth/basicauth'
+require_relative 'userdb'
+require_relative 'basicauth'
require 'tempfile'
module WEBrick
@@ -35,12 +35,30 @@ module WEBrick
##
# Open a password database at +path+
- def initialize(path)
+ def initialize(path, password_hash: nil)
@path = path
@mtime = Time.at(0)
@passwd = Hash.new
@auth_type = BasicAuth
- open(@path,"a").close unless File::exist?(@path)
+ @password_hash = password_hash
+
+ case @password_hash
+ when nil
+ # begin
+ # require "string/crypt"
+ # rescue LoadError
+ # warn("Unable to load string/crypt, proceeding with deprecated use of String#crypt, consider using password_hash: :bcrypt")
+ # end
+ @password_hash = :crypt
+ when :crypt
+ # require "string/crypt"
+ when :bcrypt
+ require "bcrypt"
+ else
+ raise ArgumentError, "only :crypt and :bcrypt are supported for password_hash keyword argument"
+ end
+
+ File.open(@path,"a").close unless File.exist?(@path)
reload
end
@@ -51,11 +69,19 @@ module WEBrick
mtime = File::mtime(@path)
if mtime > @mtime
@passwd.clear
- open(@path){|io|
+ File.open(@path){|io|
while line = io.gets
line.chomp!
case line
when %r!\A[^:]+:[a-zA-Z0-9./]{13}\z!
+ if @password_hash == :bcrypt
+ raise StandardError, ".htpasswd file contains crypt password, only bcrypt passwords supported"
+ end
+ user, pass = line.split(":")
+ when %r!\A[^:]+:\$2[aby]\$\d{2}\$.{53}\z!
+ if @password_hash == :crypt
+ raise StandardError, ".htpasswd file contains bcrypt password, only crypt passwords supported"
+ end
user, pass = line.split(":")
when /:\$/, /:{SHA}/
raise NotImplementedError,
@@ -84,7 +110,7 @@ module WEBrick
File::rename(tmp.path, output)
renamed = true
ensure
- tmp.close if !tmp.closed?
+ tmp.close
File.unlink(tmp.path) if !renamed
end
end
@@ -102,7 +128,14 @@ module WEBrick
# Sets a password in the database for +user+ in +realm+ to +pass+.
def set_passwd(realm, user, pass)
- @passwd[user] = make_passwd(realm, user, pass)
+ if @password_hash == :bcrypt
+ # Cost of 5 to match Apache default, and because the
+ # bcrypt default of 10 will introduce significant delays
+ # for every request.
+ @passwd[user] = BCrypt::Password.create(pass, :cost=>5)
+ else
+ @passwd[user] = make_passwd(realm, user, pass)
+ end
end
##
diff --git a/lib/webrick/httpproxy.rb b/lib/webrick/httpproxy.rb
index 083720f298..d05d59514c 100644
--- a/lib/webrick/httpproxy.rb
+++ b/lib/webrick/httpproxy.rb
@@ -10,7 +10,7 @@
# $IPR: httpproxy.rb,v 1.18 2003/03/08 18:58:10 gotoyuzo Exp $
# $kNotwork: straw.rb,v 1.3 2002/02/12 15:13:07 gotoken Exp $
-require "webrick/httpserver"
+require_relative "httpserver"
require "net/http"
module WEBrick
@@ -193,13 +193,13 @@ module WEBrick
begin
while fds = IO::select([ua, os])
if fds[0].member?(ua)
- buf = ua.sysread(1024);
+ buf = ua.readpartial(1024);
@logger.debug("CONNECT: #{buf.bytesize} byte from User-Agent")
- os.syswrite(buf)
+ os.write(buf)
elsif fds[0].member?(os)
- buf = os.sysread(1024);
+ buf = os.readpartial(1024);
@logger.debug("CONNECT: #{buf.bytesize} byte from #{host}:#{port}")
- ua.syswrite(buf)
+ ua.write(buf)
end
end
rescue
@@ -211,21 +211,15 @@ module WEBrick
end
def do_GET(req, res)
- perform_proxy_request(req, res) do |http, path, header|
- http.get(path, header)
- end
+ perform_proxy_request(req, res, Net::HTTP::Get)
end
def do_HEAD(req, res)
- perform_proxy_request(req, res) do |http, path, header|
- http.head(path, header)
- end
+ perform_proxy_request(req, res, Net::HTTP::Head)
end
def do_POST(req, res)
- perform_proxy_request(req, res) do |http, path, header|
- http.post(path, req.body || "", header)
- end
+ perform_proxy_request(req, res, Net::HTTP::Post, req.body_reader)
end
def do_OPTIONS(req, res)
@@ -301,38 +295,56 @@ module WEBrick
return FakeProxyURI
end
- def perform_proxy_request(req, res)
+ def perform_proxy_request(req, res, req_class, body_stream = nil)
uri = req.request_uri
path = uri.path.dup
path << "?" << uri.query if uri.query
header = setup_proxy_header(req, res)
upstream = setup_upstream_proxy_authentication(req, res, header)
- response = nil
+ body_tmp = []
http = Net::HTTP.new(uri.host, uri.port, upstream.host, upstream.port)
- http.start do
- if @config[:ProxyTimeout]
- ################################## these issues are
- http.open_timeout = 30 # secs # necessary (maybe because
- http.read_timeout = 60 # secs # Ruby's bug, but why?)
- ##################################
+ req_fib = Fiber.new do
+ http.start do
+ if @config[:ProxyTimeout]
+ ################################## these issues are
+ http.open_timeout = 30 # secs # necessary (maybe because
+ http.read_timeout = 60 # secs # Ruby's bug, but why?)
+ ##################################
+ end
+ if body_stream && req['transfer-encoding'] =~ /\bchunked\b/i
+ header['Transfer-Encoding'] = 'chunked'
+ end
+ http_req = req_class.new(path, header)
+ http_req.body_stream = body_stream if body_stream
+ http.request(http_req) do |response|
+ # Persistent connection requirements are mysterious for me.
+ # So I will close the connection in every response.
+ res['proxy-connection'] = "close"
+ res['connection'] = "close"
+
+ # stream Net::HTTP::HTTPResponse to WEBrick::HTTPResponse
+ res.status = response.code.to_i
+ res.chunked = response.chunked?
+ choose_header(response, res)
+ set_cookie(response, res)
+ set_via(res)
+ response.read_body do |buf|
+ body_tmp << buf
+ Fiber.yield # wait for res.body Proc#call
+ end
+ end # http.request
+ end
+ end
+ req_fib.resume # read HTTP response headers and first chunk of the body
+ res.body = ->(socket) do
+ while buf = body_tmp.shift
+ socket.write(buf)
+ buf.clear
+ req_fib.resume # continue response.read_body
end
- response = yield(http, path, header)
end
-
- # Persistent connection requirements are mysterious for me.
- # So I will close the connection in every response.
- res['proxy-connection'] = "close"
- res['connection'] = "close"
-
- # Convert Net::HTTP::HTTPResponse to WEBrick::HTTPResponse
- res.status = response.code.to_i
- choose_header(response, res)
- set_cookie(response, res)
- set_via(res)
- res.body = response.body
end
-
# :stopdoc:
end
end
diff --git a/lib/webrick/httprequest.rb b/lib/webrick/httprequest.rb
index 10cf72d288..c73f48c6df 100644
--- a/lib/webrick/httprequest.rb
+++ b/lib/webrick/httprequest.rb
@@ -10,10 +10,10 @@
# $IPR: httprequest.rb,v 1.64 2003/07/13 17:18:22 gotoyuzo Exp $
require 'uri'
-require 'webrick/httpversion'
-require 'webrick/httpstatus'
-require 'webrick/httputils'
-require 'webrick/cookie'
+require_relative 'httpversion'
+require_relative 'httpstatus'
+require_relative 'httputils'
+require_relative 'cookie'
module WEBrick
@@ -226,9 +226,9 @@ module WEBrick
raise HTTPStatus::BadRequest, "bad URI `#{@unparsed_uri}'."
end
- if /close/io =~ self["connection"]
+ if /\Aclose\z/io =~ self["connection"]
@keep_alive = false
- elsif /keep-alive/io =~ self["connection"]
+ elsif /\Akeep-alive\z/io =~ self["connection"]
@keep_alive = true
elsif @http_version < "1.1"
@keep_alive = false
@@ -258,6 +258,32 @@ module WEBrick
end
##
+ # Prepares the HTTPRequest object for use as the
+ # source for IO.copy_stream
+
+ def body_reader
+ @body_tmp = []
+ @body_rd = Fiber.new do
+ body do |buf|
+ @body_tmp << buf
+ Fiber.yield
+ end
+ end
+ @body_rd.resume # grab the first chunk and yield
+ self
+ end
+
+ # for IO.copy_stream. Note: we may return a larger string than +size+
+ # here; but IO.copy_stream does not care.
+ def readpartial(size, buf = ''.b) # :nodoc
+ res = @body_tmp.shift or raise EOFError, 'end of file reached'
+ buf.replace(res)
+ res.clear
+ @body_rd.resume # get more chunks
+ buf
+ end
+
+ ##
# Request query as a Hash
def query
@@ -414,13 +440,19 @@ module WEBrick
MAX_URI_LENGTH = 2083 # :nodoc:
+ # same as Mongrel, Thin and Puma
+ MAX_HEADER_LENGTH = (112 * 1024) # :nodoc:
+
def read_request_line(socket)
@request_line = read_line(socket, MAX_URI_LENGTH) if socket
- if @request_line.bytesize >= MAX_URI_LENGTH and @request_line[-1, 1] != LF
+ raise HTTPStatus::EOFError unless @request_line
+
+ @request_bytes = @request_line.bytesize
+ if @request_bytes >= MAX_URI_LENGTH and @request_line[-1, 1] != LF
raise HTTPStatus::RequestURITooLarge
end
+
@request_time = Time.now
- raise HTTPStatus::EOFError unless @request_line
if /^(\S+)\s+(\S++)(?:\s+HTTP\/(\d+\.\d+))?\r?\n/mo =~ @request_line
@request_method = $1
@unparsed_uri = $2
@@ -435,6 +467,9 @@ module WEBrick
if socket
while line = read_line(socket)
break if /\A(#{CRLF}|#{LF})\z/om =~ line
+ if (@request_bytes += line.bytesize) > MAX_HEADER_LENGTH
+ raise HTTPStatus::RequestEntityTooLarge, 'headers too large'
+ end
@raw_header << line
end
end
@@ -468,7 +503,7 @@ module WEBrick
return unless socket
if tc = self['transfer-encoding']
case tc
- when /chunked/io then read_chunked(socket, block)
+ when /\Achunked\z/io then read_chunked(socket, block)
else raise HTTPStatus::NotImplemented, "Transfer-Encoding: #{tc}."
end
elsif self['content-length'] || @remaining_size
@@ -502,12 +537,16 @@ module WEBrick
def read_chunked(socket, block)
chunk_size, = read_chunk_size(socket)
while chunk_size > 0
- data = read_data(socket, chunk_size) # read chunk-data
- if data.nil? || data.bytesize != chunk_size
- raise BadRequest, "bad chunk data size."
- end
+ begin
+ sz = [ chunk_size, @buffer_size ].min
+ data = read_data(socket, sz) # read chunk-data
+ if data.nil? || data.bytesize != sz
+ raise HTTPStatus::BadRequest, "bad chunk data size."
+ end
+ block.call(data)
+ end while (chunk_size -= sz) > 0
+
read_line(socket) # skip CRLF
- block.call(data)
chunk_size, = read_chunk_size(socket)
end
read_header(socket) # trailer + CRLF
diff --git a/lib/webrick/httpresponse.rb b/lib/webrick/httpresponse.rb
index eae14d6597..08f7797c4f 100644
--- a/lib/webrick/httpresponse.rb
+++ b/lib/webrick/httpresponse.rb
@@ -10,10 +10,11 @@
# $IPR: httpresponse.rb,v 1.45 2003/07/11 11:02:25 gotoyuzo Exp $
require 'time'
-require 'webrick/httpversion'
-require 'webrick/htmlutils'
-require 'webrick/httputils'
-require 'webrick/httpstatus'
+require 'uri'
+require_relative 'httpversion'
+require_relative 'htmlutils'
+require_relative 'httputils'
+require_relative 'httpstatus'
module WEBrick
##
@@ -21,6 +22,8 @@ module WEBrick
# WEBrick HTTP Servlet.
class HTTPResponse
+ class InvalidHeader < StandardError
+ end
##
# HTTP Response version
@@ -251,7 +254,7 @@ module WEBrick
@header.delete('content-length')
elsif @header['content-length'].nil?
unless @body.is_a?(IO)
- @header['content-length'] = @body ? @body.bytesize : 0
+ @header['content-length'] = (@body ? @body.bytesize : 0).to_s
end
end
@@ -274,7 +277,7 @@ module WEBrick
# Location is a single absoluteURI.
if location = @header['location']
if @request_uri
- @header['location'] = @request_uri.merge(location)
+ @header['location'] = @request_uri.merge(location).to_s
end
end
end
@@ -287,14 +290,19 @@ module WEBrick
data = status_line()
@header.each{|key, value|
tmp = key.gsub(/\bwww|^te$|\b\w/){ $&.upcase }
- data << "#{tmp}: #{value}" << CRLF
+ data << "#{tmp}: #{check_header(value)}" << CRLF
}
@cookies.each{|cookie|
- data << "Set-Cookie: " << cookie.to_s << CRLF
+ data << "Set-Cookie: " << check_header(cookie.to_s) << CRLF
}
data << CRLF
- _write_data(socket, data)
+ socket.write(data)
end
+ rescue InvalidHeader => e
+ @header.clear
+ @cookies.clear
+ set_error e
+ retry
end
##
@@ -303,6 +311,8 @@ module WEBrick
def send_body(socket) # :nodoc:
if @body.respond_to? :readpartial then
send_body_io(socket)
+ elsif @body.respond_to?(:call) then
+ send_body_proc(socket)
else
send_body_string(socket)
end
@@ -322,8 +332,9 @@ module WEBrick
# res.set_redirect WEBrick::HTTPStatus::TemporaryRedirect
def set_redirect(status, url)
+ url = URI(url).to_s
@body = "<HTML><A HREF=\"#{url}\">#{url}</A>.</HTML>\n"
- @header['location'] = url.to_s
+ @header['location'] = url
raise status
end
@@ -357,6 +368,15 @@ module WEBrick
private
+ def check_header(header_value)
+ header_value = header_value.to_s
+ if /[\r\n]/ =~ header_value
+ raise InvalidHeader
+ else
+ header_value
+ end
+ end
+
# :stopdoc:
def error_body(backtrace, ex, host, port)
@@ -394,24 +414,34 @@ module WEBrick
if @request_method == "HEAD"
# do nothing
elsif chunked?
+ buf = ''
begin
- buf = ''
- data = ''
- while true
- @body.readpartial( @buffer_size, buf ) # there is no need to clear buf?
- data << format("%x", buf.bytesize) << CRLF
- data << buf << CRLF
- _write_data(socket, data)
- data.clear
- @sent_size += buf.bytesize
- end
- rescue EOFError # do nothing
- end
- _write_data(socket, "0#{CRLF}#{CRLF}")
+ @body.readpartial(@buffer_size, buf)
+ size = buf.bytesize
+ data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
+ socket.write(data)
+ data.clear
+ @sent_size += size
+ rescue EOFError
+ break
+ end while true
+ buf.clear
+ socket.write("0#{CRLF}#{CRLF}")
else
- size = @header['content-length'].to_i
- _send_file(socket, @body, 0, size)
- @sent_size = size
+ if %r{\Abytes (\d+)-(\d+)/\d+\z} =~ @header['content-range']
+ offset = $1.to_i
+ size = $2.to_i - offset + 1
+ else
+ offset = nil
+ size = @header['content-length']
+ size = size.to_i if size
+ end
+ begin
+ @sent_size = IO.copy_stream(@body, socket, size, offset)
+ rescue NotImplementedError
+ @body.seek(offset, IO::SEEK_SET)
+ @sent_size = IO.copy_stream(@body, socket, size)
+ end
end
ensure
@body.close
@@ -425,42 +455,60 @@ module WEBrick
body ? @body.bytesize : 0
while buf = @body[@sent_size, @buffer_size]
break if buf.empty?
- data = ""
- data << format("%x", buf.bytesize) << CRLF
- data << buf << CRLF
- _write_data(socket, data)
- @sent_size += buf.bytesize
+ size = buf.bytesize
+ data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
+ buf.clear
+ socket.write(data)
+ @sent_size += size
end
- _write_data(socket, "0#{CRLF}#{CRLF}")
+ socket.write("0#{CRLF}#{CRLF}")
else
if @body && @body.bytesize > 0
- _write_data(socket, @body)
+ socket.write(@body)
@sent_size = @body.bytesize
end
end
end
- def _send_file(output, input, offset, size)
- while offset > 0
- sz = @buffer_size < size ? @buffer_size : size
- buf = input.read(sz)
- offset -= buf.bytesize
+ def send_body_proc(socket)
+ if @request_method == "HEAD"
+ # do nothing
+ elsif chunked?
+ @body.call(ChunkedWrapper.new(socket, self))
+ socket.write("0#{CRLF}#{CRLF}")
+ else
+ size = @header['content-length'].to_i
+ @body.call(socket)
+ @sent_size = size
+ end
+ end
+
+ class ChunkedWrapper
+ def initialize(socket, resp)
+ @socket = socket
+ @resp = resp
end
- if size == 0
- while buf = input.read(@buffer_size)
- _write_data(output, buf)
- end
- else
- while size > 0
- sz = @buffer_size < size ? @buffer_size : size
- buf = input.read(sz)
- _write_data(output, buf)
- size -= buf.bytesize
- end
+ def write(buf)
+ return 0 if buf.empty?
+ socket = @socket
+ @resp.instance_eval {
+ size = buf.bytesize
+ data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
+ socket.write(data)
+ data.clear
+ @sent_size += size
+ size
+ }
+ end
+
+ def <<(*buf)
+ write(buf)
+ self
end
end
+ # preserved for compatibility with some 3rd-party handlers
def _write_data(socket, data)
socket << data
end
diff --git a/lib/webrick/https.rb b/lib/webrick/https.rb
index 73875d7326..b0a49bc40b 100644
--- a/lib/webrick/https.rb
+++ b/lib/webrick/https.rb
@@ -9,7 +9,8 @@
#
# $IPR: https.rb,v 1.15 2003/07/22 19:20:42 gotoyuzo Exp $
-require 'webrick/ssl'
+require_relative 'ssl'
+require_relative 'httpserver'
module WEBrick
module Config
@@ -84,4 +85,68 @@ module WEBrick
# :startdoc:
end
+
+ ##
+ #--
+ # Fake WEBrick::HTTPRequest for lookup_server
+
+ class SNIRequest
+
+ ##
+ # The SNI hostname
+
+ attr_reader :host
+
+ ##
+ # The socket address of the server
+
+ attr_reader :addr
+
+ ##
+ # The port this request is for
+
+ attr_reader :port
+
+ ##
+ # Creates a new SNIRequest.
+
+ def initialize(sslsocket, hostname)
+ @host = hostname
+ @addr = sslsocket.addr
+ @port = @addr[1]
+ end
+ end
+
+
+ ##
+ #--
+ # Adds SSL functionality to WEBrick::HTTPServer
+
+ class HTTPServer < ::WEBrick::GenericServer
+ ##
+ # ServerNameIndication callback
+
+ def ssl_servername_callback(sslsocket, hostname = nil)
+ req = SNIRequest.new(sslsocket, hostname)
+ server = lookup_server(req)
+ server ? server.ssl_context : nil
+ end
+
+ # :stopdoc:
+
+ ##
+ # Check whether +server+ is also SSL server.
+ # Also +server+'s SSL context will be created.
+
+ alias orig_virtual_host virtual_host
+
+ def virtual_host(server)
+ if @config[:SSLEnable] && !server.ssl_context
+ raise ArgumentError, "virtual host must set SSLEnable to true"
+ end
+ orig_virtual_host(server)
+ end
+
+ # :startdoc:
+ end
end
diff --git a/lib/webrick/httpserver.rb b/lib/webrick/httpserver.rb
index b27f2311bd..e85d059319 100644
--- a/lib/webrick/httpserver.rb
+++ b/lib/webrick/httpserver.rb
@@ -10,13 +10,13 @@
# $IPR: httpserver.rb,v 1.63 2002/10/01 17:16:32 gotoyuzo Exp $
require 'io/wait'
-require 'webrick/server'
-require 'webrick/httputils'
-require 'webrick/httpstatus'
-require 'webrick/httprequest'
-require 'webrick/httpresponse'
-require 'webrick/httpservlet'
-require 'webrick/accesslog'
+require_relative 'server'
+require_relative 'httputils'
+require_relative 'httpstatus'
+require_relative 'httprequest'
+require_relative 'httpresponse'
+require_relative 'httpservlet'
+require_relative 'accesslog'
module WEBrick
class HTTPServerError < ServerError; end
@@ -68,8 +68,8 @@ module WEBrick
def run(sock)
while true
- res = HTTPResponse.new(@config)
- req = HTTPRequest.new(@config)
+ req = create_request(@config)
+ res = create_response(@config)
server = self
begin
timeout = @config[:RequestTimeout]
@@ -225,6 +225,20 @@ module WEBrick
end
##
+ # Creates the HTTPRequest used when handling the HTTP
+ # request. Can be overridden by subclasses.
+ def create_request(with_webrick_config)
+ HTTPRequest.new(with_webrick_config)
+ end
+
+ ##
+ # Creates the HTTPResponse used when handling the HTTP
+ # request. Can be overridden by subclasses.
+ def create_response(with_webrick_config)
+ HTTPResponse.new(with_webrick_config)
+ end
+
+ ##
# Mount table for the path a servlet is mounted on in the directory space
# of the server. Users of WEBrick can only access this indirectly via
# WEBrick::HTTPServer#mount, WEBrick::HTTPServer#unmount and
@@ -267,12 +281,12 @@ module WEBrick
k.sort!
k.reverse!
k.collect!{|path| Regexp.escape(path) }
- @scanner = Regexp.new("^(" + k.join("|") +")(?=/|$)")
+ @scanner = Regexp.new("\\A(" + k.join("|") +")(?=/|\\z)")
end
def normalize(dir)
ret = dir ? dir.dup : ""
- ret.sub!(%r|/+$|, "")
+ ret.sub!(%r|/+\z|, "")
ret
end
end
diff --git a/lib/webrick/httpservlet.rb b/lib/webrick/httpservlet.rb
index 1ee04ec86f..da49a1405b 100644
--- a/lib/webrick/httpservlet.rb
+++ b/lib/webrick/httpservlet.rb
@@ -9,11 +9,11 @@
#
# $IPR: httpservlet.rb,v 1.21 2003/02/23 12:24:46 gotoyuzo Exp $
-require 'webrick/httpservlet/abstract'
-require 'webrick/httpservlet/filehandler'
-require 'webrick/httpservlet/cgihandler'
-require 'webrick/httpservlet/erbhandler'
-require 'webrick/httpservlet/prochandler'
+require_relative 'httpservlet/abstract'
+require_relative 'httpservlet/filehandler'
+require_relative 'httpservlet/cgihandler'
+require_relative 'httpservlet/erbhandler'
+require_relative 'httpservlet/prochandler'
module WEBrick
module HTTPServlet
diff --git a/lib/webrick/httpservlet/abstract.rb b/lib/webrick/httpservlet/abstract.rb
index ee558eb026..bccb091861 100644
--- a/lib/webrick/httpservlet/abstract.rb
+++ b/lib/webrick/httpservlet/abstract.rb
@@ -9,11 +9,9 @@
#
# $IPR: abstract.rb,v 1.24 2003/07/11 11:16:46 gotoyuzo Exp $
-require 'thread'
-
-require 'webrick/htmlutils'
-require 'webrick/httputils'
-require 'webrick/httpstatus'
+require_relative '../htmlutils'
+require_relative '../httputils'
+require_relative '../httpstatus'
module WEBrick
module HTTPServlet
diff --git a/lib/webrick/httpservlet/cgi_runner.rb b/lib/webrick/httpservlet/cgi_runner.rb
index 597f48936b..0398c16749 100644
--- a/lib/webrick/httpservlet/cgi_runner.rb
+++ b/lib/webrick/httpservlet/cgi_runner.rb
@@ -23,11 +23,11 @@ STDIN.binmode
len = sysread(STDIN, 8).to_i
out = sysread(STDIN, len)
-STDOUT.reopen(open(out, "w"))
+STDOUT.reopen(File.open(out, "w"))
len = sysread(STDIN, 8).to_i
err = sysread(STDIN, len)
-STDERR.reopen(open(err, "w"))
+STDERR.reopen(File.open(err, "w"))
len = sysread(STDIN, 8).to_i
dump = sysread(STDIN, len)
diff --git a/lib/webrick/httpservlet/cgihandler.rb b/lib/webrick/httpservlet/cgihandler.rb
index ba6b0b6032..981f649750 100644
--- a/lib/webrick/httpservlet/cgihandler.rb
+++ b/lib/webrick/httpservlet/cgihandler.rb
@@ -11,8 +11,8 @@
require 'rbconfig'
require 'tempfile'
-require 'webrick/config'
-require 'webrick/httpservlet/abstract'
+require_relative '../config'
+require_relative 'abstract'
module WEBrick
module HTTPServlet
@@ -65,9 +65,7 @@ module WEBrick
cgi_in.write("%8d" % dump.bytesize)
cgi_in.write(dump)
- if req.body and req.body.bytesize > 0
- cgi_in.write(req.body)
- end
+ req.body { |chunk| cgi_in.write(chunk) }
ensure
cgi_in.close
status = $?.exitstatus
diff --git a/lib/webrick/httpservlet/erbhandler.rb b/lib/webrick/httpservlet/erbhandler.rb
index 9bcec69883..cd09e5f216 100644
--- a/lib/webrick/httpservlet/erbhandler.rb
+++ b/lib/webrick/httpservlet/erbhandler.rb
@@ -9,7 +9,7 @@
#
# $IPR: erbhandler.rb,v 1.25 2003/02/24 19:25:31 gotoyuzo Exp $
-require 'webrick/httpservlet/abstract.rb'
+require_relative 'abstract'
require 'erb'
@@ -53,7 +53,7 @@ module WEBrick
raise HTTPStatus::Forbidden, "ERBHandler cannot work."
end
begin
- data = open(@script_filename){|io| io.read }
+ data = File.open(@script_filename, &:read)
res.body = evaluate(ERB.new(data), req, res)
res['content-type'] ||=
HTTPUtils::mime_type(@script_filename, @config[:MimeTypes])
diff --git a/lib/webrick/httpservlet/filehandler.rb b/lib/webrick/httpservlet/filehandler.rb
index cf37dd6ca5..601882ef4c 100644
--- a/lib/webrick/httpservlet/filehandler.rb
+++ b/lib/webrick/httpservlet/filehandler.rb
@@ -9,12 +9,11 @@
#
# $IPR: filehandler.rb,v 1.44 2003/06/07 01:34:51 gotoyuzo Exp $
-require 'thread'
require 'time'
-require 'webrick/htmlutils'
-require 'webrick/httputils'
-require 'webrick/httpstatus'
+require_relative '../htmlutils'
+require_relative '../httputils'
+require_relative '../httpstatus'
module WEBrick
module HTTPServlet
@@ -56,9 +55,9 @@ module WEBrick
else
mtype = HTTPUtils::mime_type(@local_path, @config[:MimeTypes])
res['content-type'] = mtype
- res['content-length'] = st.size
+ res['content-length'] = st.size.to_s
res['last-modified'] = mtime.httpdate
- res.body = open(@local_path, "rb")
+ res.body = File.open(@local_path, "rb")
end
end
@@ -87,47 +86,66 @@ module WEBrick
return false
end
+ # returns a lambda for webrick/httpresponse.rb send_body_proc
+ def multipart_body(body, parts, boundary, mtype, filesize)
+ lambda do |socket|
+ begin
+ begin
+ first = parts.shift
+ last = parts.shift
+ socket.write(
+ "--#{boundary}#{CRLF}" \
+ "Content-Type: #{mtype}#{CRLF}" \
+ "Content-Range: bytes #{first}-#{last}/#{filesize}#{CRLF}" \
+ "#{CRLF}"
+ )
+
+ begin
+ IO.copy_stream(body, socket, last - first + 1, first)
+ rescue NotImplementedError
+ body.seek(first, IO::SEEK_SET)
+ IO.copy_stream(body, socket, last - first + 1)
+ end
+ socket.write(CRLF)
+ end while parts[0]
+ socket.write("--#{boundary}--#{CRLF}")
+ ensure
+ body.close
+ end
+ end
+ end
+
def make_partial_content(req, res, filename, filesize)
mtype = HTTPUtils::mime_type(filename, @config[:MimeTypes])
unless ranges = HTTPUtils::parse_range_header(req['range'])
raise HTTPStatus::BadRequest,
"Unrecognized range-spec: \"#{req['range']}\""
end
- open(filename, "rb"){|io|
+ File.open(filename, "rb"){|io|
if ranges.size > 1
time = Time.now
boundary = "#{time.sec}_#{time.usec}_#{Process::pid}"
- body = ''
- ranges.each{|range|
- first, last = prepare_range(range, filesize)
- next if first < 0
- io.pos = first
- content = io.read(last-first+1)
- body << "--" << boundary << CRLF
- body << "Content-Type: #{mtype}" << CRLF
- body << "Content-Range: bytes #{first}-#{last}/#{filesize}" << CRLF
- body << CRLF
- body << content
- body << CRLF
+ parts = []
+ ranges.each {|range|
+ prange = prepare_range(range, filesize)
+ next if prange[0] < 0
+ parts.concat(prange)
}
- raise HTTPStatus::RequestRangeNotSatisfiable if body.empty?
- body << "--" << boundary << "--" << CRLF
+ raise HTTPStatus::RequestRangeNotSatisfiable if parts.empty?
res["content-type"] = "multipart/byteranges; boundary=#{boundary}"
- res.body = body
+ if req.http_version < '1.1'
+ res['connection'] = 'close'
+ else
+ res.chunked = true
+ end
+ res.body = multipart_body(io.dup, parts, boundary, mtype, filesize)
elsif range = ranges[0]
first, last = prepare_range(range, filesize)
raise HTTPStatus::RequestRangeNotSatisfiable if first < 0
- if last == filesize - 1
- content = io.dup
- content.pos = first
- else
- io.pos = first
- content = io.read(last-first+1)
- end
res['content-type'] = mtype
res['content-range'] = "bytes #{first}-#{last}/#{filesize}"
- res['content-length'] = last - first + 1
- res.body = content
+ res['content-length'] = (last - first + 1).to_s
+ res.body = io.dup
else
raise HTTPStatus::BadRequest
end
diff --git a/lib/webrick/httpservlet/prochandler.rb b/lib/webrick/httpservlet/prochandler.rb
index c1f454e2f6..599ffc4340 100644
--- a/lib/webrick/httpservlet/prochandler.rb
+++ b/lib/webrick/httpservlet/prochandler.rb
@@ -9,7 +9,7 @@
#
# $IPR: prochandler.rb,v 1.7 2002/09/21 12:23:42 gotoyuzo Exp $
-require 'webrick/httpservlet/abstract.rb'
+require_relative 'abstract'
module WEBrick
module HTTPServlet
diff --git a/lib/webrick/httpstatus.rb b/lib/webrick/httpstatus.rb
index ded5aa2e60..c811f21964 100644
--- a/lib/webrick/httpstatus.rb
+++ b/lib/webrick/httpstatus.rb
@@ -9,7 +9,7 @@
#
# $IPR: httpstatus.rb,v 1.11 2003/03/24 20:18:55 gotoyuzo Exp $
-require 'webrick/accesslog'
+require_relative 'accesslog'
module WEBrick
@@ -23,10 +23,6 @@ module WEBrick
##
# Root of the HTTP status class hierarchy
class Status < StandardError
- def initialize(*args) # :nodoc:
- args[0] = AccessLog.escape(args[0]) unless args.empty?
- super(*args)
- end
class << self
attr_reader :code, :reason_phrase # :nodoc:
end
diff --git a/lib/webrick/httputils.rb b/lib/webrick/httputils.rb
index 28f906ef4d..a4cd3b48ee 100644
--- a/lib/webrick/httputils.rb
+++ b/lib/webrick/httputils.rb
@@ -69,6 +69,7 @@ module WEBrick
"jpeg" => "image/jpeg",
"jpg" => "image/jpeg",
"js" => "application/javascript",
+ "json" => "application/json",
"lha" => "application/octet-stream",
"lzh" => "application/octet-stream",
"mov" => "video/quicktime",
@@ -107,6 +108,8 @@ module WEBrick
# Loads Apache-compatible mime.types in +file+.
def load_mime_types(file)
+ # note: +file+ may be a "| command" for now; some people may
+ # rely on this, but currently we do not use this method by default.
open(file){ |io|
hash = Hash.new
io.each{ |line|
diff --git a/lib/webrick/log.rb b/lib/webrick/log.rb
index 7542d8f79a..2c1fdfe602 100644
--- a/lib/webrick/log.rb
+++ b/lib/webrick/log.rb
@@ -51,7 +51,7 @@ module WEBrick
@level = level || INFO
case log_file
when String
- @log = open(log_file, "a+")
+ @log = File.open(log_file, "a+")
@log.sync = true
@opened = true
when NilClass
@@ -118,10 +118,10 @@ module WEBrick
# * Otherwise it will return +arg+.inspect.
def format(arg)
if arg.is_a?(Exception)
- "#{arg.class}: #{arg.message}\n\t" <<
+ "#{arg.class}: #{AccessLog.escape(arg.message)}\n\t" <<
arg.backtrace.join("\n\t") << "\n"
elsif arg.respond_to?(:to_str)
- arg.to_str
+ AccessLog.escape(arg.to_str)
else
arg.inspect
end
diff --git a/lib/webrick/server.rb b/lib/webrick/server.rb
index 45bd9706d3..4a6e74c4f9 100644
--- a/lib/webrick/server.rb
+++ b/lib/webrick/server.rb
@@ -9,10 +9,9 @@
#
# $IPR: server.rb,v 1.62 2003/07/22 19:20:43 gotoyuzo Exp $
-require 'thread'
require 'socket'
-require 'webrick/config'
-require 'webrick/log'
+require_relative 'config'
+require_relative 'log'
module WEBrick
@@ -44,14 +43,8 @@ module WEBrick
# block, if given.
def Daemon.start
- exit!(0) if fork
- Process::setsid
- exit!(0) if fork
- Dir::chdir("/")
- File::umask(0)
- STDIN.reopen(IO::NULL)
- STDOUT.reopen(IO::NULL, "w")
- STDERR.reopen(IO::NULL, "w")
+ Process.daemon
+ File.umask(0)
yield if block_given?
end
end
@@ -110,7 +103,7 @@ module WEBrick
@shutdown_pipe = nil
unless @config[:DoNotListen]
if @config[:Listen]
- warn(":Listen option is deprecated; use GenericServer#listen")
+ warn(":Listen option is deprecated; use GenericServer#listen", uplevel: 1)
end
listen(@config[:BindAddress], @config[:Port])
if @config[:Port] == 0
@@ -164,17 +157,17 @@ module WEBrick
server_type.start{
@logger.info \
"#{self.class}#start: pid=#{$$} port=#{@config[:Port]}"
+ @status = :Running
call_callback(:StartCallback)
shutdown_pipe = @shutdown_pipe
thgroup = ThreadGroup.new
- @status = :Running
begin
while @status == :Running
begin
sp = shutdown_pipe[0]
- if svrs = IO.select([sp, *@listeners], nil, nil, 2.0)
+ if svrs = IO.select([sp, *@listeners])
if svrs[0].include? sp
# swallow shutdown pipe
buf = String.new
@@ -238,7 +231,7 @@ module WEBrick
def shutdown
stop
- alarm_shutdown_pipe {|f| f.close}
+ alarm_shutdown_pipe(&:close)
end
##
@@ -258,18 +251,26 @@ module WEBrick
# the client socket.
def accept_client(svr)
- sock = nil
- begin
- sock = svr.accept
- sock.sync = true
- Utils::set_non_blocking(sock)
- rescue Errno::ECONNRESET, Errno::ECONNABORTED,
- Errno::EPROTO, Errno::EINVAL
- rescue StandardError => ex
- msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
- @logger.error msg
+ case sock = svr.to_io.accept_nonblock(exception: false)
+ when :wait_readable
+ nil
+ else
+ if svr.respond_to?(:start_immediately)
+ sock = OpenSSL::SSL::SSLSocket.new(sock, ssl_context)
+ sock.sync_close = true
+ # we cannot do OpenSSL::SSL::SSLSocket#accept here because
+ # a slow client can prevent us from accepting connections
+ # from other clients
+ end
+ sock
end
- return sock
+ rescue Errno::ECONNRESET, Errno::ECONNABORTED,
+ Errno::EPROTO, Errno::EINVAL
+ nil
+ rescue StandardError => ex
+ msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
+ @logger.error msg
+ nil
end
##
@@ -292,6 +293,16 @@ module WEBrick
@logger.debug "accept: <address unknown>"
raise
end
+ if sock.respond_to?(:sync_close=) && @config[:SSLStartImmediately]
+ WEBrick::Utils.timeout(@config[:RequestTimeout]) do
+ begin
+ sock.accept # OpenSSL::SSL::SSLSocket#accept
+ rescue Errno::ECONNRESET, Errno::ECONNABORTED,
+ Errno::EPROTO, Errno::EINVAL
+ Thread.exit
+ end
+ end
+ end
call_callback(:AcceptCallback, sock)
block ? block.call(sock) : run(sock)
rescue Errno::ENOTCONN
@@ -309,7 +320,7 @@ module WEBrick
else
@logger.debug "close: <address unknown>"
end
- sock.close unless sock.closed?
+ sock.close
end
}
end
@@ -318,29 +329,16 @@ module WEBrick
# Calls the callback +callback_name+ from the configuration with +args+
def call_callback(callback_name, *args)
- if cb = @config[callback_name]
- cb.call(*args)
- end
+ @config[callback_name]&.call(*args)
end
def setup_shutdown_pipe
- if !@shutdown_pipe
- @shutdown_pipe = IO.pipe
- end
- @shutdown_pipe
+ return @shutdown_pipe ||= IO.pipe
end
def cleanup_shutdown_pipe(shutdown_pipe)
@shutdown_pipe = nil
- return if !shutdown_pipe
- shutdown_pipe.each {|io|
- if !io.closed?
- begin
- io.close
- rescue IOError # another thread closed io.
- end
- end
- }
+ shutdown_pipe&.each(&:close)
end
def alarm_shutdown_pipe
diff --git a/lib/webrick/ssl.rb b/lib/webrick/ssl.rb
index d626e149ec..d125083528 100644
--- a/lib/webrick/ssl.rb
+++ b/lib/webrick/ssl.rb
@@ -48,6 +48,8 @@ module WEBrick
# Number of CA certificates to walk when verifying a certificate chain
# :SSLVerifyCallback ::
# Custom certificate verification callback
+ # :SSLServerNameCallback::
+ # Custom servername indication callback
# :SSLTimeout ::
# Maximum session lifetime
# :SSLOptions ::
@@ -128,7 +130,7 @@ module WEBrick
aki = ef.create_extension("authorityKeyIdentifier",
"keyid:always,issuer:always")
cert.add_extension(aki)
- cert.sign(rsa, OpenSSL::Digest::SHA1.new)
+ cert.sign(rsa, OpenSSL::Digest::SHA256.new)
return [ cert, rsa ]
end
@@ -145,7 +147,13 @@ module WEBrick
# SSL context for the server when run in SSL mode
def ssl_context # :nodoc:
- @ssl_context ||= nil
+ @ssl_context ||= begin
+ if @config[:SSLEnable]
+ ssl_context = setup_ssl_context(@config)
+ @logger.info("\n" + @config[:SSLCertificate].to_text)
+ ssl_context
+ end
+ end
end
undef listen
@@ -156,10 +164,6 @@ module WEBrick
def listen(address, port) # :nodoc:
listeners = Utils::create_listeners(address, port)
if @config[:SSLEnable]
- unless ssl_context
- @ssl_context = setup_ssl_context(@config)
- @logger.info("\n" + @config[:SSLCertificate].to_text)
- end
listeners.collect!{|svr|
ssvr = ::OpenSSL::SSL::SSLServer.new(svr, ssl_context)
ssvr.start_immediately = @config[:SSLStartImmediately]
@@ -177,7 +181,7 @@ module WEBrick
unless config[:SSLCertificate]
cn = config[:SSLCertName]
comment = config[:SSLCertComment]
- cert, key = Utils::create_self_signed_cert(1024, cn, comment)
+ cert, key = Utils::create_self_signed_cert(2048, cn, comment)
config[:SSLCertificate] = cert
config[:SSLPrivateKey] = key
end
@@ -193,10 +197,19 @@ module WEBrick
ctx.verify_mode = config[:SSLVerifyClient]
ctx.verify_depth = config[:SSLVerifyDepth]
ctx.verify_callback = config[:SSLVerifyCallback]
+ ctx.servername_cb = config[:SSLServerNameCallback] || proc { |args| ssl_servername_callback(*args) }
ctx.timeout = config[:SSLTimeout]
ctx.options = config[:SSLOptions]
ctx.ciphers = config[:SSLCiphers]
ctx
end
+
+ ##
+ # ServerNameIndication callback
+
+ def ssl_servername_callback(sslsocket, hostname = nil)
+ # default
+ end
+
end
end
diff --git a/lib/webrick/utils.rb b/lib/webrick/utils.rb
index 8b34b6654f..07044876b9 100644
--- a/lib/webrick/utils.rb
+++ b/lib/webrick/utils.rb
@@ -37,7 +37,7 @@ module WEBrick
Process::Sys::setgid(pw.gid)
Process::Sys::setuid(pw.uid)
else
- warn("WEBrick::Utils::su doesn't work on this platform")
+ warn("WEBrick::Utils::su doesn't work on this platform", uplevel: 1)
end
end
module_function :su
@@ -91,7 +91,6 @@ module WEBrick
###########
- require "thread"
require "timeout"
require "singleton"
diff --git a/lib/webrick/version.rb b/lib/webrick/version.rb
index da5dac94a9..c23df9a912 100644
--- a/lib/webrick/version.rb
+++ b/lib/webrick/version.rb
@@ -14,5 +14,5 @@ module WEBrick
##
# The WEBrick version
- VERSION = "1.3.1"
+ VERSION = "1.4.4"
end
diff --git a/lib/webrick/webrick.gemspec b/lib/webrick/webrick.gemspec
new file mode 100644
index 0000000000..611ec138ce
--- /dev/null
+++ b/lib/webrick/webrick.gemspec
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+begin
+ require_relative 'lib/webrick/version'
+rescue LoadError
+ # for Ruby core repository
+ require_relative 'version'
+end
+
+Gem::Specification.new do |s|
+ s.name = "webrick"
+ s.version = WEBrick::VERSION
+ s.summary = "HTTP server toolkit"
+ s.description = "WEBrick is an HTTP server toolkit that can be configured as an HTTPS server, a proxy server, and a virtual-host server."
+
+ s.require_path = %w{lib}
+ s.files = ["lib/webrick.rb", "lib/webrick/accesslog.rb", "lib/webrick/cgi.rb", "lib/webrick/compat.rb", "lib/webrick/config.rb", "lib/webrick/cookie.rb", "lib/webrick/htmlutils.rb", "lib/webrick/httpauth.rb", "lib/webrick/httpauth/authenticator.rb", "lib/webrick/httpauth/basicauth.rb", "lib/webrick/httpauth/digestauth.rb", "lib/webrick/httpauth/htdigest.rb", "lib/webrick/httpauth/htgroup.rb", "lib/webrick/httpauth/htpasswd.rb", "lib/webrick/httpauth/userdb.rb", "lib/webrick/httpauth.rb", "lib/webrick/httpproxy.rb", "lib/webrick/httprequest.rb", "lib/webrick/httpresponse.rb", "lib/webrick/https.rb", "lib/webrick/httpserver.rb", "lib/webrick/httpservlet.rb", "lib/webrick/httpservlet/abstract.rb", "lib/webrick/httpservlet/cgi_runner.rb", "lib/webrick/httpservlet/cgihandler.rb", "lib/webrick/httpservlet/erbhandler.rb", "lib/webrick/httpservlet/filehandler.rb", "lib/webrick/httpservlet/prochandler.rb", "lib/webrick/httpservlet.rb", "lib/webrick/httpstatus.rb", "lib/webrick/httputils.rb", "lib/webrick/httpversion.rb", "lib/webrick/log.rb", "lib/webrick/server.rb", "lib/webrick/ssl.rb", "lib/webrick/utils.rb", "lib/webrick/version.rb"]
+ s.required_ruby_version = ">= 2.3.0"
+
+ s.authors = ["TAKAHASHI Masayoshi", "GOTOU YUUZOU", "Eric Wong"]
+ s.email = [nil, nil, 'normal@ruby-lang.org']
+ s.homepage = "https://www.ruby-lang.org"
+ s.license = "BSD-2-Clause"
+
+ if s.respond_to?(:metadata=)
+ s.metadata = {
+ "bug_tracker_uri" => "https://bugs.ruby-lang.org/projects/ruby-trunk/issues",
+ "homepage_uri" => "https://www.ruby-lang.org",
+ "source_code_uri" => "https://svn.ruby-lang.org/repos/ruby"
+ }
+ end
+
+ s.add_development_dependency "rake"
+end
diff --git a/lib/yaml.rb b/lib/yaml.rb
index 0c33305e1d..e47e0a7f9b 100644
--- a/lib/yaml.rb
+++ b/lib/yaml.rb
@@ -1,13 +1,11 @@
# frozen_string_literal: false
-##
-# The YAML module is an alias of Psych, the YAML engine for Ruby.
begin
require 'psych'
rescue LoadError
- warn "#{caller[0]}:"
- warn "It seems your ruby installation is missing psych (for YAML output)."
- warn "To eliminate this warning, please install libyaml and reinstall your ruby."
+ warn "It seems your ruby installation is missing psych (for YAML output).\n" \
+ "To eliminate this warning, please install libyaml and reinstall your ruby.\n",
+ uplevel: 1
raise
end
@@ -17,7 +15,7 @@ YAML = Psych # :nodoc:
#
# This module provides a Ruby interface for data serialization in YAML format.
#
-# The underlying implementation is the libyaml wrapper Psych.
+# The YAML module is an alias of Psych, the YAML engine for Ruby.
#
# == Usage
#
@@ -31,6 +29,9 @@ YAML = Psych # :nodoc:
# YAML.dump("foo") # => "--- foo\n...\n"
# { :a => 'b'}.to_yaml # => "---\n:a: b\n"
#
+# As the implementation is provided by the Psych library, detailed documentation
+# can be found in that library's docs (also part of standard library).
+#
# == Security
#
# Do not use YAML to load untrusted data. Doing so is unsafe and could allow
diff --git a/libexec/bundle b/libexec/bundle
new file mode 100755
index 0000000000..aaf773745d
--- /dev/null
+++ b/libexec/bundle
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+# Exit cleanly from an early interrupt
+Signal.trap("INT") do
+ Bundler.ui.debug("\n#{caller.join("\n")}") if defined?(Bundler)
+ exit 1
+end
+
+require "bundler"
+# Check if an older version of bundler is installed
+$LOAD_PATH.each do |path|
+ next unless path =~ %r{/bundler-0\.(\d+)} && $1.to_i < 9
+ err = String.new
+ err << "Looks like you have a version of bundler that's older than 0.9.\n"
+ err << "Please remove your old versions.\n"
+ err << "An easy way to do this is by running `gem cleanup bundler`."
+ abort(err)
+end
+
+require "bundler/friendly_errors"
+Bundler.with_friendly_errors do
+ require "bundler/cli"
+
+ # Allow any command to use --help flag to show help for that command
+ help_flags = %w[--help -h]
+ help_flag_used = ARGV.any? {|a| help_flags.include? a }
+ args = help_flag_used ? Bundler::CLI.reformatted_help_args(ARGV) : ARGV
+
+ Bundler::CLI.start(args, :debug => true)
+end
diff --git a/libexec/bundle_ruby b/libexec/bundle_ruby
new file mode 100755
index 0000000000..df6f8cc8a1
--- /dev/null
+++ b/libexec/bundle_ruby
@@ -0,0 +1,60 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require "bundler/shared_helpers"
+
+Bundler::SharedHelpers.major_deprecation(2, "the bundle_ruby executable has been removed in favor of `bundle platform --ruby`")
+
+Signal.trap("INT") { exit 1 }
+
+require "bundler/errors"
+require "bundler/ruby_version"
+require "bundler/ruby_dsl"
+
+module Bundler
+ class Dsl
+ include RubyDsl
+
+ attr_accessor :ruby_version
+
+ def initialize
+ @ruby_version = nil
+ end
+
+ def eval_gemfile(gemfile, contents = nil)
+ contents ||= File.open(gemfile, "rb", &:read)
+ instance_eval(contents, gemfile.to_s, 1)
+ rescue SyntaxError => e
+ bt = e.message.split("\n")[1..-1]
+ raise GemfileError, ["Gemfile syntax error:", *bt].join("\n")
+ rescue ScriptError, RegexpError, NameError, ArgumentError => e
+ e.backtrace[0] = "#{e.backtrace[0]}: #{e.message} (#{e.class})"
+ STDERR.puts e.backtrace.join("\n ")
+ raise GemfileError, "There was an error in your Gemfile," \
+ " and Bundler cannot continue."
+ end
+
+ def source(source, options = {})
+ end
+
+ def gem(name, *args)
+ end
+
+ def group(*args)
+ end
+ end
+end
+
+dsl = Bundler::Dsl.new
+begin
+ dsl.eval_gemfile(Bundler::SharedHelpers.default_gemfile)
+ ruby_version = dsl.ruby_version
+ if ruby_version
+ puts ruby_version
+ else
+ puts "No ruby version specified"
+ end
+rescue Bundler::GemfileError => e
+ puts e.message
+ exit(-1)
+end
diff --git a/libexec/bundler b/libexec/bundler
new file mode 100755
index 0000000000..d9131fe834
--- /dev/null
+++ b/libexec/bundler
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+load File.expand_path("../bundle", __FILE__)
diff --git a/libexec/irb b/libexec/irb
new file mode 100755
index 0000000000..c64ee85fbd
--- /dev/null
+++ b/libexec/irb
@@ -0,0 +1,11 @@
+#!/usr/bin/env ruby
+#
+# irb.rb - interactive ruby
+# $Release Version: 0.9.6 $
+# $Revision$
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+
+require "irb"
+
+IRB.start(__FILE__)
diff --git a/libexec/rdoc b/libexec/rdoc
new file mode 100755
index 0000000000..aaa23292df
--- /dev/null
+++ b/libexec/rdoc
@@ -0,0 +1,44 @@
+#!/usr/bin/env ruby
+#
+# RDoc: Documentation tool for source code
+# (see lib/rdoc/rdoc.rb for more information)
+#
+# Copyright (c) 2003 Dave Thomas
+# Released under the same terms as Ruby
+
+begin
+ gem 'rdoc'
+rescue NameError => e # --disable-gems
+ raise unless e.name == :gem
+rescue Gem::LoadError
+end
+
+require 'rdoc/rdoc'
+
+begin
+ r = RDoc::RDoc.new
+ r.document ARGV
+rescue Errno::ENOSPC
+ $stderr.puts 'Ran out of space creating documentation'
+ $stderr.puts
+ $stderr.puts 'Please free up some space and try again'
+rescue SystemExit
+ raise
+rescue Exception => e
+ if $DEBUG_RDOC then
+ $stderr.puts e.message
+ $stderr.puts "#{e.backtrace.join "\n\t"}"
+ $stderr.puts
+ elsif Interrupt === e then
+ $stderr.puts
+ $stderr.puts 'Interrupted'
+ else
+ $stderr.puts "uh-oh! RDoc had a problem:"
+ $stderr.puts e.message
+ $stderr.puts
+ $stderr.puts "run with --debug for full backtrace"
+ end
+
+ exit 1
+end
+
diff --git a/libexec/ri b/libexec/ri
new file mode 100755
index 0000000000..7fbed0c099
--- /dev/null
+++ b/libexec/ri
@@ -0,0 +1,12 @@
+#!/usr/bin/env ruby
+
+begin
+ gem 'rdoc'
+rescue NameError => e # --disable-gems
+ raise unless e.name == :gem
+rescue Gem::LoadError
+end
+
+require 'rdoc/ri/driver'
+
+RDoc::RI::Driver.run ARGV
diff --git a/load.c b/load.c
index 3cf9ce0aba..57a9c9c287 100644
--- a/load.c
+++ b/load.c
@@ -2,11 +2,13 @@
* load methods from eval.c
*/
-#include "internal.h"
+#include "ruby/encoding.h"
#include "ruby/util.h"
+#include "internal.h"
#include "dln.h"
#include "eval_intern.h"
#include "probes.h"
+#include "iseq.h"
static VALUE ruby_dln_librefs;
@@ -85,8 +87,8 @@ rb_construct_expanded_load_path(enum expand_type type, int *has_relative, int *h
if (is_string)
rb_str_freeze(path);
as_str = rb_get_path_check_convert(path, as_str, level);
- expanded_path = rb_file_expand_path_fast(as_str, Qnil);
- rb_str_freeze(expanded_path);
+ expanded_path = rb_check_realpath(Qnil, as_str);
+ if (NIL_P(expanded_path)) expanded_path = as_str;
rb_ary_push(ary, rb_fstring(expanded_path));
}
rb_obj_freeze(ary);
@@ -94,15 +96,6 @@ rb_construct_expanded_load_path(enum expand_type type, int *has_relative, int *h
rb_ary_replace(vm->load_path_snapshot, vm->load_path);
}
-static VALUE
-load_path_getcwd(void)
-{
- char *cwd = my_getcwd();
- VALUE cwd_str = rb_filesystem_str_new_cstr(cwd);
- xfree(cwd);
- return cwd_str;
-}
-
VALUE
rb_get_expanded_load_path(void)
{
@@ -114,7 +107,7 @@ rb_get_expanded_load_path(void)
int has_relative = 0, has_non_cache = 0;
rb_construct_expanded_load_path(EXPAND_ALL, &has_relative, &has_non_cache);
if (has_relative) {
- vm->load_path_check_cache = load_path_getcwd();
+ vm->load_path_check_cache = rb_dir_getwd_ospath();
}
else if (has_non_cache) {
/* Non string object. */
@@ -132,7 +125,7 @@ rb_get_expanded_load_path(void)
}
else if (vm->load_path_check_cache) {
int has_relative = 1, has_non_cache = 1;
- VALUE cwd = load_path_getcwd();
+ 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. */
@@ -180,22 +173,27 @@ get_loading_table(void)
return GET_VM()->loading_table;
}
+static st_data_t
+feature_key(const char *str, size_t len)
+{
+ return st_hash(str, len, 0xfea7009e);
+}
+
static void
-features_index_add_single(VALUE short_feature, VALUE offset)
+features_index_add_single(const char* str, size_t len, VALUE offset)
{
struct st_table *features_index;
VALUE this_feature_index = Qnil;
- char *short_feature_cstr;
+ st_data_t short_feature_key;
Check_Type(offset, T_FIXNUM);
- Check_Type(short_feature, T_STRING);
- short_feature_cstr = StringValueCStr(short_feature);
+ short_feature_key = feature_key(str, len);
features_index = get_loaded_features_index_raw();
- st_lookup(features_index, (st_data_t)short_feature_cstr, (st_data_t *)&this_feature_index);
+ st_lookup(features_index, short_feature_key, (st_data_t *)&this_feature_index);
if (NIL_P(this_feature_index)) {
- st_insert(features_index, (st_data_t)ruby_strdup(short_feature_cstr), (st_data_t)offset);
+ st_insert(features_index, short_feature_key, (st_data_t)offset);
}
else if (RB_TYPE_P(this_feature_index, T_FIXNUM)) {
VALUE feature_indexes[2];
@@ -204,7 +202,7 @@ features_index_add_single(VALUE short_feature, VALUE offset)
this_feature_index = (VALUE)xcalloc(1, sizeof(struct RArray));
RBASIC(this_feature_index)->flags = T_ARRAY; /* fake VALUE, do not mark/sweep */
rb_ary_cat(this_feature_index, feature_indexes, numberof(feature_indexes));
- st_insert(features_index, (st_data_t)short_feature_cstr, (st_data_t)this_feature_index);
+ st_insert(features_index, short_feature_key, (st_data_t)this_feature_index);
}
else {
Check_Type(this_feature_index, T_ARRAY);
@@ -223,42 +221,35 @@ features_index_add_single(VALUE short_feature, VALUE offset)
static void
features_index_add(VALUE feature, VALUE offset)
{
- VALUE short_feature;
const char *feature_str, *feature_end, *ext, *p;
feature_str = StringValuePtr(feature);
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;
/* Now `ext` points to the only string matching %r{^\.[^./]*$} that is
at the end of `feature`, or is NULL if there is no such string. */
p = ext ? ext : feature_end;
while (1) {
- long beg;
-
p--;
while (p >= feature_str && *p != '/')
p--;
if (p < feature_str)
break;
/* Now *p == '/'. We reach this point for every '/' in `feature`. */
- beg = p + 1 - feature_str;
- short_feature = rb_str_subseq(feature, beg, feature_end - p - 1);
- features_index_add_single(short_feature, offset);
+ features_index_add_single(p + 1, feature_end - p - 1, offset);
if (ext) {
- short_feature = rb_str_subseq(feature, beg, ext - p - 1);
- features_index_add_single(short_feature, offset);
+ features_index_add_single(p + 1, ext - p - 1, offset);
}
}
- features_index_add_single(feature, offset);
+ features_index_add_single(feature_str, feature_end - feature_str, offset);
if (ext) {
- short_feature = rb_str_subseq(feature, 0, ext - feature_str);
- features_index_add_single(short_feature, offset);
+ features_index_add_single(feature_str, ext - feature_str, offset);
}
}
@@ -268,9 +259,8 @@ loaded_features_index_clear_i(st_data_t key, st_data_t val, st_data_t arg)
VALUE obj = (VALUE)val;
if (!SPECIAL_CONST_P(obj)) {
rb_ary_free(obj);
- xfree((void *)obj);
+ ruby_sized_xfree((void *)obj, sizeof(struct RArray));
}
- xfree((char *)key);
return ST_DELETE;
}
@@ -383,6 +373,7 @@ rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const c
long i, len, elen, n;
st_table *loading_tbl, *features_index;
st_data_t data;
+ st_data_t key;
int type;
if (fn) *fn = 0;
@@ -399,7 +390,8 @@ rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const c
features = get_loaded_features();
features_index = get_loaded_features_index();
- st_lookup(features_index, (st_data_t)feature, (st_data_t *)&this_feature_index);
+ key = feature_key(feature, strlen(feature));
+ st_lookup(features_index, key, (st_data_t *)&this_feature_index);
/* We search `features` for an entry such that either
"#{features[i]}" == "#{load_path[j]}/#{feature}#{e}"
for some j, or
@@ -571,23 +563,23 @@ rb_provide_feature(VALUE feature)
void
rb_provide(const char *feature)
{
- rb_provide_feature(rb_usascii_str_new2(feature));
+ rb_provide_feature(rb_fstring_cstr(feature));
}
NORETURN(static void load_failed(VALUE));
-const rb_iseq_t *rb_iseq_load_iseq(VALUE fname);
static int
-rb_load_internal0(rb_thread_t *th, VALUE fname, int wrap)
+rb_load_internal0(rb_execution_context_t *ec, VALUE fname, int wrap)
{
- int state;
+ enum ruby_tag_type state;
+ rb_thread_t *th = rb_ec_thread_ptr(ec);
volatile VALUE wrapper = th->top_wrapper;
volatile VALUE self = th->top_self;
#if !defined __GNUC__
rb_thread_t *volatile th0 = th;
#endif
- th->errinfo = Qnil; /* ensure */
+ th->ec->errinfo = Qnil; /* ensure */
if (!wrap) {
th->top_wrapper = 0;
@@ -599,10 +591,10 @@ rb_load_internal0(rb_thread_t *th, VALUE fname, int wrap)
rb_extend_object(th->top_self, th->top_wrapper);
}
- TH_PUSH_TAG(th);
- state = EXEC_TAG();
- if (state == 0) {
- NODE *node;
+ EC_PUSH_TAG(th->ec);
+ state = EC_EXEC_TAG();
+ if (state == TAG_NONE) {
+ rb_ast_t *ast;
const rb_iseq_t *iseq;
if ((iseq = rb_iseq_load_iseq(fname)) != NULL) {
@@ -611,12 +603,15 @@ rb_load_internal0(rb_thread_t *th, VALUE fname, int wrap)
else {
VALUE parser = rb_parser_new();
rb_parser_set_context(parser, NULL, FALSE);
- node = (NODE *)rb_parser_load_file(parser, fname);
- iseq = rb_iseq_new_top(node, rb_str_new2("<top (required)>"), fname, rb_realpath_internal(Qnil, fname, 1), NULL);
+ ast = (rb_ast_t *)rb_parser_load_file(parser, 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_iseq_eval(iseq);
+ rb_exec_event_hook_script_compiled(ec, iseq, Qnil);
+ rb_iseq_eval(iseq);
}
- TH_POP_TAG();
+ EC_POP_TAG();
#if !defined __GNUC__
th = th0;
@@ -626,13 +621,15 @@ rb_load_internal0(rb_thread_t *th, VALUE fname, int wrap)
th->top_wrapper = wrapper;
if (state) {
+ /* usually state == TAG_RAISE only, except for
+ * rb_iseq_load_iseq case */
VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef);
if (NIL_P(exc)) return state;
- th->errinfo = exc;
+ th->ec->errinfo = exc;
return TAG_RAISE;
}
- if (!NIL_P(th->errinfo)) {
+ if (!NIL_P(th->ec->errinfo)) {
/* exception during load */
return TAG_RAISE;
}
@@ -642,11 +639,11 @@ rb_load_internal0(rb_thread_t *th, VALUE fname, int wrap)
static void
rb_load_internal(VALUE fname, int wrap)
{
- rb_thread_t *curr_th = GET_THREAD();
- int state = rb_load_internal0(curr_th, fname, wrap);
+ rb_execution_context_t *ec = GET_EC();
+ int state = rb_load_internal0(ec, fname, wrap);
if (state) {
- if (state == TAG_RAISE) rb_exc_raise(curr_th->errinfo);
- TH_JUMP_TAG(curr_th, state);
+ if (state == TAG_RAISE) rb_exc_raise(ec->errinfo);
+ EC_JUMP_TAG(ec, state);
}
}
@@ -665,19 +662,19 @@ rb_load(VALUE fname, int wrap)
}
void
-rb_load_protect(VALUE fname, int wrap, int *state)
+rb_load_protect(VALUE fname, int wrap, int *pstate)
{
- int status;
+ enum ruby_tag_type state;
volatile VALUE path = 0;
- PUSH_TAG();
- if ((status = EXEC_TAG()) == 0) {
+ EC_PUSH_TAG(GET_EC());
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
path = file_to_load(fname);
}
- POP_TAG();
- if (!status) status = rb_load_internal0(GET_THREAD(), path, wrap);
- if (state)
- *state = status;
+ EC_POP_TAG();
+
+ if (state == TAG_NONE) state = rb_load_internal0(GET_EC(), path, wrap);
+ if (state != TAG_NONE) *pstate = state;
}
/*
@@ -701,10 +698,10 @@ rb_f_load(int argc, VALUE *argv)
rb_scan_args(argc, argv, "11", &fname, &wrap);
- RUBY_DTRACE_HOOK(LOAD_ENTRY, StringValuePtr(fname));
-
orig_fname = rb_get_path_check_to_string(fname, rb_safe_level());
fname = rb_str_encode_ospath(orig_fname);
+ RUBY_DTRACE_HOOK(LOAD_ENTRY, RSTRING_PTR(orig_fname));
+
path = rb_find_file(fname);
if (!path) {
if (!rb_file_load_ok(RSTRING_PTR(fname)))
@@ -713,7 +710,7 @@ rb_f_load(int argc, VALUE *argv)
}
rb_load_internal(path, RTEST(wrap));
- RUBY_DTRACE_HOOK(LOAD_RETURN, StringValuePtr(fname));
+ RUBY_DTRACE_HOOK(LOAD_RETURN, RSTRING_PTR(orig_fname));
return Qtrue;
}
@@ -731,7 +728,7 @@ load_lock(const char *ftptr)
st_insert(loading_tbl, (st_data_t)ftptr, data);
return (char *)ftptr;
}
- else if (RB_TYPE_P((VALUE)data, T_IMEMO) && imemo_type((VALUE)data) == imemo_memo) {
+ else if (imemo_type_p(data, imemo_memo)) {
struct MEMO *memo = MEMO_CAST(data);
void (*init)(void) = (void (*)(void))memo->u3.func;
data = (st_data_t)rb_thread_shield_new();
@@ -740,14 +737,12 @@ load_lock(const char *ftptr)
return (char *)"";
}
if (RTEST(ruby_verbose)) {
- rb_warning("loading in progress, circular require considered harmful - %s", ftptr);
- rb_backtrace_print_to(rb_stderr);
+ 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:
- data = (st_data_t)ftptr;
- st_insert(loading_tbl, data, (st_data_t)rb_thread_shield_new());
- return 0;
case Qnil:
return 0;
}
@@ -759,7 +754,12 @@ 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) : rb_thread_shield_release(thread_shield)) {
+ 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. */
+ }
+ else if (rb_thread_shield_release(thread_shield)) {
/* still in-use */
return ST_CONTINUE;
}
@@ -944,6 +944,42 @@ load_ext(VALUE path)
}
/*
+ * call-seq:
+ * RubyVM.resolve_feature_path(feature) -> [:rb or :so, path]
+ *
+ * Identifies the file that will be loaded by "require(feature)".
+ * This API is experimental and just for internal.
+ *
+ * RubyVM.resolve_feature_path("set")
+ * #=> [:rb, "/path/to/feature.rb"]
+ */
+
+VALUE
+rb_resolve_feature_path(VALUE klass, VALUE fname)
+{
+ VALUE path;
+ int found;
+ VALUE sym;
+
+ fname = rb_get_path_check(fname, 0);
+ path = rb_str_encode_ospath(fname);
+ found = search_required(path, &path, 0);
+
+ switch (found) {
+ case 'r':
+ sym = ID2SYM(rb_intern("rb"));
+ break;
+ case 's':
+ sym = ID2SYM(rb_intern("so"));
+ break;
+ default:
+ load_failed(fname);
+ }
+
+ return rb_ary_new_from_args(2, sym, path);
+}
+
+/*
* returns
* 0: if already loaded (false)
* 1: successfully loaded (true)
@@ -954,35 +990,33 @@ int
rb_require_internal(VALUE fname, int safe)
{
volatile int result = -1;
- rb_thread_t *th = GET_THREAD();
- volatile VALUE errinfo = th->errinfo;
- int state;
+ rb_execution_context_t *ec = GET_EC();
+ volatile VALUE errinfo = ec->errinfo;
+ enum ruby_tag_type state;
struct {
int safe;
} volatile saved;
char *volatile ftptr = 0;
+ VALUE path;
- RUBY_DTRACE_HOOK(REQUIRE_ENTRY, StringValuePtr(fname));
+ fname = rb_get_path_check(fname, safe);
+ path = rb_str_encode_ospath(fname);
+ RUBY_DTRACE_HOOK(REQUIRE_ENTRY, RSTRING_PTR(fname));
- TH_PUSH_TAG(th);
+ EC_PUSH_TAG(ec);
saved.safe = rb_safe_level();
- if ((state = EXEC_TAG()) == 0) {
- VALUE path;
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
long handle;
int found;
- rb_set_safe_level_force(safe);
- FilePathValue(fname);
rb_set_safe_level_force(0);
- RUBY_DTRACE_HOOK(FIND_REQUIRE_ENTRY, StringValuePtr(fname));
-
- path = rb_str_encode_ospath(fname);
+ RUBY_DTRACE_HOOK(FIND_REQUIRE_ENTRY, RSTRING_PTR(fname));
found = search_required(path, &path, safe);
+ RUBY_DTRACE_HOOK(FIND_REQUIRE_RETURN, RSTRING_PTR(fname));
- RUBY_DTRACE_HOOK(FIND_REQUIRE_RETURN, StringValuePtr(fname));
if (found) {
- if (!path || !(ftptr = load_lock(RSTRING_PTR(path)))) {
+ if (!path || !(path = rb_fstring(path), ftptr = load_lock(RSTRING_PTR(path)))) {
result = 0;
}
else if (!*ftptr) {
@@ -992,7 +1026,7 @@ rb_require_internal(VALUE fname, int safe)
else {
switch (found) {
case 'r':
- state = rb_load_internal0(th, path, 0);
+ state = rb_load_internal0(ec, path, 0);
break;
case 's':
@@ -1008,18 +1042,19 @@ rb_require_internal(VALUE fname, int safe)
}
}
}
- TH_POP_TAG();
+ EC_POP_TAG();
load_unlock(ftptr, !state);
rb_set_safe_level_force(saved.safe);
if (state) {
+ RB_GC_GUARD(fname);
/* never TAG_RETURN */
return state;
}
- th->errinfo = errinfo;
+ ec->errinfo = errinfo;
- RUBY_DTRACE_HOOK(REQUIRE_RETURN, StringValuePtr(fname));
+ RUBY_DTRACE_HOOK(REQUIRE_RETURN, RSTRING_PTR(fname));
return result;
}
@@ -1041,7 +1076,7 @@ rb_require_safe(VALUE fname, int safe)
if (result > TAG_RETURN) {
if (result == TAG_RAISE) rb_exc_raise(rb_errinfo());
- JUMP_TAG(result);
+ EC_JUMP_TAG(GET_EC(), result);
}
if (result < 0) {
load_failed(fname);
@@ -1053,8 +1088,7 @@ rb_require_safe(VALUE fname, int safe)
VALUE
rb_require(const char *fname)
{
- VALUE fn = rb_str_new2(fname);
- OBJ_FREEZE(fn);
+ VALUE fn = rb_str_new_cstr(fname);
return rb_require_safe(fn, rb_safe_level());
}
@@ -1194,7 +1228,7 @@ Init_load(void)
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_index = st_init_strtable();
+ vm->loaded_features_index = st_init_numtable();
rb_define_global_function("load", rb_f_load, -1);
rb_define_global_function("require", rb_f_require, 1);
diff --git a/loadpath.c b/loadpath.c
index 9160031971..b8969e6998 100644
--- a/loadpath.c
+++ b/loadpath.c
@@ -89,4 +89,3 @@ const char ruby_initial_load_paths[] =
RUBY_ARCH_LIB_FOR(RUBY_ARCH) "\0"
#endif
"";
-
diff --git a/localeinit.c b/localeinit.c
index eeec9adac6..bec29a6d46 100644
--- a/localeinit.c
+++ b/localeinit.c
@@ -9,6 +9,7 @@
**********************************************************************/
+#include "ruby/encoding.h"
#include "internal.h"
#include "encindex.h"
#ifdef __CYGWIN__
@@ -21,35 +22,45 @@
#if defined _WIN32 || defined __CYGWIN__
#define SIZEOF_CP_NAME ((sizeof(UINT) * 8 / 3) + 4)
#define CP_FORMAT(buf, codepage) snprintf(buf, sizeof(buf), "CP%u", (codepage))
+
+extern UINT ruby_w32_codepage[2];
+#endif
+
+#ifndef NO_LOCALE_CHARMAP
+# if defined _WIN32 || defined __CYGWIN__ || defined HAVE_LANGINFO_H
+# define NO_LOCALE_CHARMAP 0
+# else
+# define NO_LOCALE_CHARMAP 1
+# endif
#endif
+#if !NO_LOCALE_CHARMAP
static VALUE
locale_charmap(VALUE (*conv)(const char *))
{
-#if defined NO_LOCALE_CHARMAP
-# error NO_LOCALE_CHARMAP defined
-#elif defined _WIN32 || defined __CYGWIN__
const char *codeset = 0;
+#if defined _WIN32 || defined __CYGWIN__
char cp[SIZEOF_CP_NAME];
# ifdef __CYGWIN__
const char *nl_langinfo_codeset(void);
codeset = nl_langinfo_codeset();
# endif
if (!codeset) {
- UINT codepage = GetConsoleCP();
+ UINT codepage = ruby_w32_codepage[0];
+ if (!codepage) codepage = GetConsoleCP();
if (!codepage) codepage = GetACP();
CP_FORMAT(cp, codepage);
codeset = cp;
}
- return (*conv)(codeset);
#elif defined HAVE_LANGINFO_H
- char *codeset;
codeset = nl_langinfo(CODESET);
- return (*conv)(codeset);
+ ASSUME(codeset);
#else
- return ENCINDEX_US_ASCII;
+# error locale_charmap() is not implemented
#endif
+ return (*conv)(codeset);
}
+#endif
/*
* call-seq:
@@ -79,30 +90,42 @@ locale_charmap(VALUE (*conv)(const char *))
VALUE
rb_locale_charmap(VALUE klass)
{
+#if NO_LOCALE_CHARMAP
+ return rb_usascii_str_new_cstr("US-ASCII");
+#else
return locale_charmap(rb_usascii_str_new_cstr);
+#endif
}
+#if !NO_LOCALE_CHARMAP
static VALUE
enc_find_index(const char *name)
{
return (VALUE)rb_enc_find_index(name);
}
+#endif
int
rb_locale_charmap_index(void)
{
+#if NO_LOCALE_CHARMAP
+ return ENCINDEX_US_ASCII;
+#else
return (int)locale_charmap(enc_find_index);
+#endif
}
int
Init_enc_set_filesystem_encoding(void)
{
int idx;
-#if defined NO_LOCALE_CHARMAP
-# error NO_LOCALE_CHARMAP defined
+#if NO_LOCALE_CHARMAP
+ idx = ENCINDEX_US_ASCII;
#elif defined _WIN32
char cp[SIZEOF_CP_NAME];
- CP_FORMAT(cp, AreFileApisANSI() ? GetACP() : GetOEMCP());
+ const UINT codepage = ruby_w32_codepage[1] ? ruby_w32_codepage[1] :
+ AreFileApisANSI() ? GetACP() : GetOEMCP();
+ CP_FORMAT(cp, codepage);
idx = rb_enc_find_index(cp);
if (idx < 0) idx = ENCINDEX_ASCII;
#elif defined __CYGWIN__
diff --git a/main.c b/main.c
index 16da117732..6e8ab7b9fe 100644
--- a/main.c
+++ b/main.c
@@ -15,6 +15,12 @@
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
+#if RUBY_DEVEL && !defined RUBY_DEBUG_ENV
+# define RUBY_DEBUG_ENV 1
+#endif
+#if defined RUBY_DEBUG_ENV && !RUBY_DEBUG_ENV
+# undef RUBY_DEBUG_ENV
+#endif
#ifdef RUBY_DEBUG_ENV
#include <stdlib.h>
#endif
diff --git a/man/bundle-add.1 b/man/bundle-add.1
new file mode 100644
index 0000000000..3adf3e8163
--- /dev/null
+++ b/man/bundle-add.1
@@ -0,0 +1,58 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-ADD" "1" "November 2018" "" ""
+.
+.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] [\-\-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\.
+.
+.P
+Example:
+.
+.P
+bundle add rails
+.
+.P
+bundle add rails \-\-version "< 3\.0, > 1\.1"
+.
+.P
+bundle add rails \-\-version "~> 5\.0\.0" \-\-source "https://gems\.example\.com" \-\-group "development"
+.
+.P
+bundle add rails \-\-skip\-install
+.
+.P
+bundle add rails \-\-group "development, test"
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-version\fR, \fB\-v\fR
+Specify version requirements(s) for the added gem\.
+.
+.TP
+\fB\-\-group\fR, \fB\-g\fR
+Specify the group(s) for the added gem\. Multiple groups should be separated by commas\.
+.
+.TP
+\fB\-\-source\fR, , \fB\-s\fR
+Specify the source for the added gem\.
+.
+.TP
+\fB\-\-skip\-install\fR
+Adds the gem to the Gemfile but does not install it\.
+.
+.TP
+\fB\-\-optimistic\fR
+Adds optimistic declaration of version
+.
+.TP
+\fB\-\-strict\fR
+Adds strict declaration of version
+
diff --git a/man/bundle-add.1.txt b/man/bundle-add.1.txt
new file mode 100644
index 0000000000..653a166044
--- /dev/null
+++ b/man/bundle-add.1.txt
@@ -0,0 +1,52 @@
+BUNDLE-ADD(1) BUNDLE-ADD(1)
+
+
+
+1mNAME0m
+ 1mbundle-add 22m- Add gem to the Gemfile and run bundle install
+
+1mSYNOPSIS0m
+ 1mbundle add 4m22mGEM_NAME24m [--group=GROUP] [--version=VERSION]
+ [--source=SOURCE] [--skip-install] [--strict] [--optimistic]
+
+1mDESCRIPTION0m
+ Adds the named gem to the Gemfile and run 1mbundle install22m. 1mbundle0m
+ 1minstall 22mcan be avoided by using the flag 1m--skip-install22m.
+
+ Example:
+
+ bundle add rails
+
+ bundle add rails --version "< 3.0, > 1.1"
+
+ bundle add rails --version "~> 5.0.0" --source "https://gems.exam-
+ ple.com" --group "development"
+
+ bundle add rails --skip-install
+
+ bundle add rails --group "development, test"
+
+1mOPTIONS0m
+ 1m--version22m, 1m-v0m
+ Specify version requirements(s) for the added gem.
+
+ 1m--group22m, 1m-g0m
+ Specify the group(s) for the added gem. Multiple groups should
+ be separated by commas.
+
+ 1m--source22m, , 1m-s0m
+ Specify the source for the added gem.
+
+ 1m--skip-install0m
+ Adds the gem to the Gemfile but does not install it.
+
+ 1m--optimistic0m
+ Adds optimistic declaration of version
+
+ 1m--strict0m
+ Adds strict declaration of version
+
+
+
+
+ November 2018 BUNDLE-ADD(1)
diff --git a/man/bundle-add.ronn b/man/bundle-add.ronn
new file mode 100644
index 0000000000..1e2d732ec6
--- /dev/null
+++ b/man/bundle-add.ronn
@@ -0,0 +1,40 @@
+bundle-add(1) -- Add gem to the Gemfile and run bundle install
+================================================================
+
+## SYNOPSIS
+
+`bundle add` <GEM_NAME> [--group=GROUP] [--version=VERSION] [--source=SOURCE] [--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`.
+
+Example:
+
+bundle add rails
+
+bundle add rails --version "< 3.0, > 1.1"
+
+bundle add rails --version "~> 5.0.0" --source "https://gems.example.com" --group "development"
+
+bundle add rails --skip-install
+
+bundle add rails --group "development, test"
+
+## OPTIONS
+* `--version`, `-v`:
+ Specify version requirements(s) for the added gem.
+
+* `--group`, `-g`:
+ Specify the group(s) for the added gem. Multiple groups should be separated by commas.
+
+* `--source`, , `-s`:
+ Specify the source for the added gem.
+
+* `--skip-install`:
+ Adds the gem to the Gemfile but does not install it.
+
+* `--optimistic`:
+ Adds optimistic declaration of version
+
+* `--strict`:
+ Adds strict declaration of version
diff --git a/man/bundle-binstubs.1 b/man/bundle-binstubs.1
new file mode 100644
index 0000000000..583ab83abc
--- /dev/null
+++ b/man/bundle-binstubs.1
@@ -0,0 +1,40 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-BINSTUBS" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-binstubs\fR \- Install the binstubs of the listed gems
+.
+.SH "SYNOPSIS"
+\fBbundle binstubs\fR \fIGEM_NAME\fR [\-\-force] [\-\-path PATH] [\-\-standalone]
+.
+.SH "DESCRIPTION"
+Binstubs are scripts that wrap around executables\. Bundler creates a small Ruby file (a binstub) that loads Bundler, runs the command, and puts it into \fBbin/\fR\. Binstubs are a shortcut\-or alternative\- to always using \fBbundle exec\fR\. This gives you a file that can by run directly, and one that will always run the correct gem version used by the application\.
+.
+.P
+For example, if you run \fBbundle binstubs rspec\-core\fR, Bundler will create the file \fBbin/rspec\fR\. That file will contain enough code to load Bundler, tell it to load the bundled gems, and then run rspec\.
+.
+.P
+This command generates binstubs for executables in \fBGEM_NAME\fR\. Binstubs are put into \fBbin\fR, or the \fB\-\-path\fR directory if one has been set\. Calling binstubs with [GEM [GEM]] will create binstubs for all given gems\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-force\fR
+Overwrite existing binstubs if they exist\.
+.
+.TP
+\fB\-\-path\fR
+The location to install the specified binstubs to\. This defaults to \fBbin\fR\.
+.
+.TP
+\fB\-\-standalone\fR
+Makes binstubs that can work without depending on Rubygems or Bundler at runtime\.
+.
+.TP
+\fB\-\-shebang\fR
+Specify a different shebang executable name than the default (default \'ruby\')
+.
+.SH "BUNDLE INSTALL \-\-BINSTUBS"
+To create binstubs for all the gems in the bundle you can use the \fB\-\-binstubs\fR flag in bundle install(1) \fIbundle\-install\.1\.html\fR\.
diff --git a/man/bundle-binstubs.1.txt b/man/bundle-binstubs.1.txt
new file mode 100644
index 0000000000..f15a875e2d
--- /dev/null
+++ b/man/bundle-binstubs.1.txt
@@ -0,0 +1,48 @@
+BUNDLE-BINSTUBS(1) BUNDLE-BINSTUBS(1)
+
+
+
+1mNAME0m
+ 1mbundle-binstubs 22m- Install the binstubs of the listed gems
+
+1mSYNOPSIS0m
+ 1mbundle binstubs 4m22mGEM_NAME24m [--force] [--path PATH] [--standalone]
+
+1mDESCRIPTION0m
+ Binstubs are scripts that wrap around executables. Bundler creates a
+ small Ruby file (a binstub) that loads Bundler, runs the command, and
+ puts it into 1mbin/22m. Binstubs are a shortcut-or alternative- to always
+ using 1mbundle exec22m. This gives you a file that can by run directly, and
+ one that will always run the correct gem version used by the applica-
+ tion.
+
+ For example, if you run 1mbundle binstubs rspec-core22m, Bundler will create
+ the file 1mbin/rspec22m. That file will contain enough code to load Bundler,
+ tell it to load the bundled gems, and then run rspec.
+
+ This command generates binstubs for executables in 1mGEM_NAME22m. Binstubs
+ are put into 1mbin22m, or the 1m--path 22mdirectory if one has been set. Calling
+ binstubs with [GEM [GEM]] will create binstubs for all given gems.
+
+1mOPTIONS0m
+ 1m--force0m
+ Overwrite existing binstubs if they exist.
+
+ 1m--path 22mThe location to install the specified binstubs to. This defaults
+ to 1mbin22m.
+
+ 1m--standalone0m
+ Makes binstubs that can work without depending on Rubygems or
+ Bundler at runtime.
+
+ 1m--shebang0m
+ Specify a different shebang executable name than the default
+ (default 'ruby')
+
+1mBUNDLE INSTALL --BINSTUBS0m
+ To create binstubs for all the gems in the bundle you can use the
+ 1m--binstubs 22mflag in bundle install(1) 4mbundle-install.1.html24m.
+
+
+
+ November 2018 BUNDLE-BINSTUBS(1)
diff --git a/man/bundle-binstubs.ronn b/man/bundle-binstubs.ronn
new file mode 100644
index 0000000000..c1ae0988cd
--- /dev/null
+++ b/man/bundle-binstubs.ronn
@@ -0,0 +1,43 @@
+bundle-binstubs(1) -- Install the binstubs of the listed gems
+=============================================================
+
+## SYNOPSIS
+
+`bundle binstubs` <GEM_NAME> [--force] [--path PATH] [--standalone]
+
+## DESCRIPTION
+
+Binstubs are scripts that wrap around executables. Bundler creates a
+small Ruby file (a binstub) that loads Bundler, runs the command,
+and puts it into `bin/`. Binstubs are a shortcut-or alternative-
+to always using `bundle exec`. This gives you a file that can by run
+directly, and one that will always run the correct gem version
+used by the application.
+
+For example, if you run `bundle binstubs rspec-core`, Bundler will create
+the file `bin/rspec`. That file will contain enough code to load Bundler,
+tell it to load the bundled gems, and then run rspec.
+
+This command generates binstubs for executables in `GEM_NAME`.
+Binstubs are put into `bin`, or the `--path` directory if one has been set.
+Calling binstubs with [GEM [GEM]] will create binstubs for all given gems.
+
+## OPTIONS
+
+* `--force`:
+ Overwrite existing binstubs if they exist.
+
+* `--path`:
+ The location to install the specified binstubs to. This defaults to `bin`.
+
+* `--standalone`:
+ Makes binstubs that can work without depending on Rubygems or Bundler at
+ runtime.
+
+* `--shebang`:
+ Specify a different shebang executable name than the default (default 'ruby')
+
+## BUNDLE INSTALL --BINSTUBS
+
+To create binstubs for all the gems in the bundle you can use the `--binstubs`
+flag in [bundle install(1)](bundle-install.1.html).
diff --git a/man/bundle-check.1 b/man/bundle-check.1
new file mode 100644
index 0000000000..35431972d5
--- /dev/null
+++ b/man/bundle-check.1
@@ -0,0 +1,31 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-CHECK" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-check\fR \- Verifies if dependencies are satisfied by installed gems
+.
+.SH "SYNOPSIS"
+\fBbundle check\fR [\-\-dry\-run] [\-\-gemfile=FILE] [\-\-path=PATH]
+.
+.SH "DESCRIPTION"
+\fBcheck\fR searches the local machine for each of the gems requested in the Gemfile\. If all gems are found, Bundler prints a success message and exits with a status of 0\.
+.
+.P
+If not, the first missing gem is listed and Bundler exits status 1\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-dry\-run\fR
+Locks the [\fBGemfile(5)\fR][Gemfile(5)] before running the command\.
+.
+.TP
+\fB\-\-gemfile\fR
+Use the specified gemfile instead of the [\fBGemfile(5)\fR][Gemfile(5)]\.
+.
+.TP
+\fB\-\-path\fR
+Specify a different path than the system default (\fB$BUNDLE_PATH\fR or \fB$GEM_HOME\fR)\. Bundler will remember this value for future installs on this machine\.
+
diff --git a/man/bundle-check.1.txt b/man/bundle-check.1.txt
new file mode 100644
index 0000000000..20cfe26446
--- /dev/null
+++ b/man/bundle-check.1.txt
@@ -0,0 +1,33 @@
+BUNDLE-CHECK(1) BUNDLE-CHECK(1)
+
+
+
+1mNAME0m
+ 1mbundle-check 22m- Verifies if dependencies are satisfied by installed gems
+
+1mSYNOPSIS0m
+ 1mbundle check 22m[--dry-run] [--gemfile=FILE] [--path=PATH]
+
+1mDESCRIPTION0m
+ 1mcheck 22msearches the local machine for each of the gems requested in the
+ Gemfile. If all gems are found, Bundler prints a success message and
+ exits with a status of 0.
+
+ If not, the first missing gem is listed and Bundler exits status 1.
+
+1mOPTIONS0m
+ 1m--dry-run0m
+ Locks the [1mGemfile(5)22m][Gemfile(5)] before running the command.
+
+ 1m--gemfile0m
+ Use the specified gemfile instead of the [1mGemfile(5)22m][Gem-
+ file(5)].
+
+ 1m--path 22mSpecify a different path than the system default (1m$BUNDLE_PATH0m
+ or 1m$GEM_HOME22m). Bundler will remember this value for future
+ installs on this machine.
+
+
+
+
+ November 2018 BUNDLE-CHECK(1)
diff --git a/man/bundle-check.ronn b/man/bundle-check.ronn
new file mode 100644
index 0000000000..f2846b8ff2
--- /dev/null
+++ b/man/bundle-check.ronn
@@ -0,0 +1,26 @@
+bundle-check(1) -- Verifies if dependencies are satisfied by installed gems
+===========================================================================
+
+## SYNOPSIS
+
+`bundle check` [--dry-run]
+ [--gemfile=FILE]
+ [--path=PATH]
+
+## DESCRIPTION
+
+`check` searches the local machine for each of the gems requested in the
+Gemfile. If all gems are found, Bundler prints a success message and exits with
+a status of 0.
+
+If not, the first missing gem is listed and Bundler exits status 1.
+
+## OPTIONS
+
+* `--dry-run`:
+ Locks the [`Gemfile(5)`][Gemfile(5)] before running the command.
+* `--gemfile`:
+ Use the specified gemfile instead of the [`Gemfile(5)`][Gemfile(5)].
+* `--path`:
+ Specify a different path than the system default (`$BUNDLE_PATH` or `$GEM_HOME`).
+ Bundler will remember this value for future installs on this machine.
diff --git a/man/bundle-clean.1 b/man/bundle-clean.1
new file mode 100644
index 0000000000..681f41effc
--- /dev/null
+++ b/man/bundle-clean.1
@@ -0,0 +1,24 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-CLEAN" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-clean\fR \- Cleans up unused gems in your bundler directory
+.
+.SH "SYNOPSIS"
+\fBbundle clean\fR [\-\-dry\-run] [\-\-force]
+.
+.SH "DESCRIPTION"
+This command will remove all unused gems in your bundler directory\. This is useful when you have made many changes to your gem dependencies\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-dry\-run\fR
+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\.
+
diff --git a/man/bundle-clean.1.txt b/man/bundle-clean.1.txt
new file mode 100644
index 0000000000..f4b507db65
--- /dev/null
+++ b/man/bundle-clean.1.txt
@@ -0,0 +1,26 @@
+BUNDLE-CLEAN(1) BUNDLE-CLEAN(1)
+
+
+
+1mNAME0m
+ 1mbundle-clean 22m- Cleans up unused gems in your bundler directory
+
+1mSYNOPSIS0m
+ 1mbundle clean 22m[--dry-run] [--force]
+
+1mDESCRIPTION0m
+ This command will remove all unused gems in your bundler directory.
+ This is useful when you have made many changes to your gem dependen-
+ cies.
+
+1mOPTIONS0m
+ 1m--dry-run0m
+ Print the changes, but do not clean the unused gems.
+
+ 1m--force0m
+ Force a clean even if 1m--path 22mis not set.
+
+
+
+
+ November 2018 BUNDLE-CLEAN(1)
diff --git a/man/bundle-clean.ronn b/man/bundle-clean.ronn
new file mode 100644
index 0000000000..de23991782
--- /dev/null
+++ b/man/bundle-clean.ronn
@@ -0,0 +1,18 @@
+bundle-clean(1) -- Cleans up unused gems in your bundler directory
+==================================================================
+
+## SYNOPSIS
+
+`bundle clean` [--dry-run] [--force]
+
+## DESCRIPTION
+
+This command will remove all unused gems in your bundler directory. This is
+useful when you have made many changes to your gem dependencies.
+
+## OPTIONS
+
+* `--dry-run`:
+ Print the changes, but do not clean the unused gems.
+* `--force`:
+ Force a clean even if `--path` is not set.
diff --git a/man/bundle-config.1 b/man/bundle-config.1
new file mode 100644
index 0000000000..80ef0c0715
--- /dev/null
+++ b/man/bundle-config.1
@@ -0,0 +1,497 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-CONFIG" "1" "December 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-config\fR \- Set bundler configuration options
+.
+.SH "SYNOPSIS"
+\fBbundle config\fR [\fIname\fR [\fIvalue\fR]]
+.
+.SH "DESCRIPTION"
+This command allows you to interact with Bundler\'s configuration system\.
+.
+.P
+Bundler loads configuration settings in this order:
+.
+.IP "1." 4
+Local config (\fBapp/\.bundle/config\fR)
+.
+.IP "2." 4
+Environmental variables (\fBENV\fR)
+.
+.IP "3." 4
+Global config (\fB~/\.bundle/config\fR)
+.
+.IP "4." 4
+Bundler default config
+.
+.IP "" 0
+.
+.P
+Executing \fBbundle config\fR with no parameters will print a list of all bundler configuration for the current bundle, and where that configuration was set\.
+.
+.P
+Executing \fBbundle config <name>\fR will print the value of that configuration setting, and where it was set\.
+.
+.P
+Executing \fBbundle config <name> <value>\fR will set that configuration to the value specified for all bundles executed as the current user\. The configuration will be stored in \fB~/\.bundle/config\fR\. If \fIname\fR already is set, \fIname\fR will be overridden and user will be warned\.
+.
+.P
+Executing \fBbundle config \-\-global <name> <value>\fR works the same as above\.
+.
+.P
+Executing \fBbundle config \-\-local <name> <value>\fR will set that configuration to the local application\. The configuration will be stored in \fBapp/\.bundle/config\fR\.
+.
+.P
+Executing \fBbundle config \-\-delete <name>\fR will delete the configuration in both local and global sources\. Not compatible with \-\-global or \-\-local flag\.
+.
+.P
+Executing bundle with the \fBBUNDLE_IGNORE_CONFIG\fR environment variable set will cause it to ignore all configuration\.
+.
+.P
+Executing \fBbundle config disable_multisource true\fR upgrades the warning about the Gemfile containing multiple primary sources to an error\. Executing \fBbundle config \-\-delete disable_multisource\fR downgrades this error to a warning\.
+.
+.SH "REMEMBERING OPTIONS"
+Flags passed to \fBbundle install\fR or the Bundler runtime, such as \fB\-\-path foo\fR or \fB\-\-without production\fR, are not remembered between commands\. If these options must be remembered,they must be set using \fBbundle config\fR (e\.g\., \fBbundle config path foo\fR)\.
+.
+.P
+The options that can be configured are:
+.
+.TP
+\fBbin\fR
+Creates a directory (defaults to \fB~/bin\fR) and place any executables from the gem there\. These executables run in Bundler\'s context\. If used, you might add this directory to your environment\'s \fBPATH\fR variable\. For instance, if the \fBrails\fR gem comes with a \fBrails\fR executable, this flag will create a \fBbin/rails\fR executable that ensures that all referred dependencies will be resolved using the bundled gems\.
+.
+.TP
+\fBdeployment\fR
+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
+\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\.
+.
+.TP
+\fBwithout\fR
+A space\-separated list of groups referencing gems to skip during installation\.
+.
+.TP
+\fBwith\fR
+A space\-separated list of groups referencing gems to include during installation\.
+.
+.SH "BUILD OPTIONS"
+You can use \fBbundle config\fR to give Bundler the flags to pass to the gem installer every time bundler tries to install a particular gem\.
+.
+.P
+A very common example, the \fBmysql\fR gem, requires Snow Leopard users to pass configuration flags to \fBgem install\fR to specify where to find the \fBmysql_config\fR executable\.
+.
+.IP "" 4
+.
+.nf
+
+gem install mysql \-\- \-\-with\-mysql\-config=/usr/local/mysql/bin/mysql_config
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Since the specific location of that executable can change from machine to machine, you can specify these flags on a per\-machine basis\.
+.
+.IP "" 4
+.
+.nf
+
+bundle config build\.mysql \-\-with\-mysql\-config=/usr/local/mysql/bin/mysql_config
+.
+.fi
+.
+.IP "" 0
+.
+.P
+After running this command, every time bundler needs to install the \fBmysql\fR gem, it will pass along the flags you specified\.
+.
+.SH "CONFIGURATION KEYS"
+Configuration keys in bundler have two forms: the canonical form and the environment variable form\.
+.
+.P
+For instance, passing the \fB\-\-without\fR flag to bundle install(1) \fIbundle\-install\.1\.html\fR prevents Bundler from installing certain groups specified in the Gemfile(5)\. Bundler persists this value in \fBapp/\.bundle/config\fR so that calls to \fBBundler\.setup\fR do not try to find gems from the \fBGemfile\fR that you didn\'t install\. Additionally, subsequent calls to bundle install(1) \fIbundle\-install\.1\.html\fR remember this setting and skip those groups\.
+.
+.P
+The canonical form of this configuration is \fB"without"\fR\. To convert the canonical form to the environment variable form, capitalize it, and prepend \fBBUNDLE_\fR\. The environment variable form of \fB"without"\fR is \fBBUNDLE_WITHOUT\fR\.
+.
+.P
+Any periods in the configuration keys must be replaced with two underscores when setting it via environment variables\. The configuration key \fBlocal\.rack\fR becomes the environment variable \fBBUNDLE_LOCAL__RACK\fR\.
+.
+.SH "LIST OF AVAILABLE KEYS"
+The following is a list of all configuration keys and their purpose\. You can learn more about their operation in bundle install(1) \fIbundle\-install\.1\.html\fR\.
+.
+.IP "\(bu" 4
+\fBallow_bundler_dependency_conflicts\fR (\fBBUNDLE_ALLOW_BUNDLER_DEPENDENCY_CONFLICTS\fR): Allow resolving to specifications that have dependencies on \fBbundler\fR that are incompatible with the running Bundler version\.
+.
+.IP "\(bu" 4
+\fBallow_deployment_source_credential_changes\fR (\fBBUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES\fR): When in deployment mode, allow changing the credentials to a gem\'s source\. Ex: \fBhttps://some\.host\.com/gems/path/\fR \-> \fBhttps://user_name:password@some\.host\.com/gems/path\fR
+.
+.IP "\(bu" 4
+\fBallow_offline_install\fR (\fBBUNDLE_ALLOW_OFFLINE_INSTALL\fR): Allow Bundler to use cached data when installing without network access\.
+.
+.IP "\(bu" 4
+\fBauto_clean_without_path\fR (\fBBUNDLE_AUTO_CLEAN_WITHOUT_PATH\fR): Automatically run \fBbundle clean\fR after installing when an explicit \fBpath\fR has not been set and Bundler is not installing into the system gems\.
+.
+.IP "\(bu" 4
+\fBauto_install\fR (\fBBUNDLE_AUTO_INSTALL\fR): Automatically run \fBbundle install\fR when gems are missing\.
+.
+.IP "\(bu" 4
+\fBbin\fR (\fBBUNDLE_BIN\fR): Install executables from gems in the bundle to the specified directory\. Defaults to \fBfalse\fR\.
+.
+.IP "\(bu" 4
+\fBcache_all\fR (\fBBUNDLE_CACHE_ALL\fR): Cache all gems, including path and git gems\.
+.
+.IP "\(bu" 4
+\fBcache_all_platforms\fR (\fBBUNDLE_CACHE_ALL_PLATFORMS\fR): Cache gems for all platforms\.
+.
+.IP "\(bu" 4
+\fBcache_path\fR (\fBBUNDLE_CACHE_PATH\fR): The directory that bundler will place cached gems in when running \fBbundle package\fR, and that bundler will look in when installing gems\. Defaults to \fBvendor/bundle\fR\.
+.
+.IP "\(bu" 4
+\fBclean\fR (\fBBUNDLE_CLEAN\fR): Whether Bundler should run \fBbundle clean\fR automatically after \fBbundle install\fR\.
+.
+.IP "\(bu" 4
+\fBconsole\fR (\fBBUNDLE_CONSOLE\fR): The console that \fBbundle console\fR starts\. Defaults to \fBirb\fR\.
+.
+.IP "\(bu" 4
+\fBdefault_install_uses_path\fR (\fBBUNDLE_DEFAULT_INSTALL_USES_PATH\fR): Whether a \fBbundle install\fR without an explicit \fB\-\-path\fR argument defaults to installing gems in \fB\.bundle\fR\.
+.
+.IP "\(bu" 4
+\fBdeployment\fR (\fBBUNDLE_DEPLOYMENT\fR): Disallow changes to the \fBGemfile\fR\. When the \fBGemfile\fR is changed and the lockfile has not been updated, running Bundler commands will be blocked\.
+.
+.IP "\(bu" 4
+\fBdisable_checksum_validation\fR (\fBBUNDLE_DISABLE_CHECKSUM_VALIDATION\fR): Allow installing gems even if they do not match the checksum provided by RubyGems\.
+.
+.IP "\(bu" 4
+\fBdisable_exec_load\fR (\fBBUNDLE_DISABLE_EXEC_LOAD\fR): Stop Bundler from using \fBload\fR to launch an executable in\-process in \fBbundle exec\fR\.
+.
+.IP "\(bu" 4
+\fBdisable_local_branch_check\fR (\fBBUNDLE_DISABLE_LOCAL_BRANCH_CHECK\fR): Allow Bundler to use a local git override without a branch specified in the Gemfile\.
+.
+.IP "\(bu" 4
+\fBdisable_multisource\fR (\fBBUNDLE_DISABLE_MULTISOURCE\fR): When set, Gemfiles containing multiple sources will produce errors instead of warnings\. Use \fBbundle config \-\-delete disable_multisource\fR to unset\.
+.
+.IP "\(bu" 4
+\fBdisable_platform_warnings\fR (\fBBUNDLE_DISABLE_PLATFORM_WARNINGS\fR): Disable warnings during bundle install when a dependency is unused on the current platform\.
+.
+.IP "\(bu" 4
+\fBdisable_shared_gems\fR (\fBBUNDLE_DISABLE_SHARED_GEMS\fR): Stop Bundler from accessing gems installed to RubyGems\' normal location\.
+.
+.IP "\(bu" 4
+\fBdisable_version_check\fR (\fBBUNDLE_DISABLE_VERSION_CHECK\fR): Stop Bundler from checking if a newer Bundler version is available on rubygems\.org\.
+.
+.IP "\(bu" 4
+\fBerror_on_stderr\fR (\fBBUNDLE_ERROR_ON_STDERR\fR): Print Bundler errors to stderr\.
+.
+.IP "\(bu" 4
+\fBforce_ruby_platform\fR (\fBBUNDLE_FORCE_RUBY_PLATFORM\fR): Ignore the current machine\'s platform and install only \fBruby\fR platform gems\. As a result, gems with native extensions will be compiled from source\.
+.
+.IP "\(bu" 4
+\fBfrozen\fR (\fBBUNDLE_FROZEN\fR): Disallow changes to the \fBGemfile\fR\. When the \fBGemfile\fR is changed and the lockfile has not been updated, running Bundler commands will be blocked\. Defaults to \fBtrue\fR when \fB\-\-deployment\fR is used\.
+.
+.IP "\(bu" 4
+\fBgem\.push_key\fR (\fBBUNDLE_GEM__PUSH_KEY\fR): Sets the \fB\-\-key\fR parameter for \fBgem push\fR when using the \fBrake release\fR command with a private gemstash server\.
+.
+.IP "\(bu" 4
+\fBgemfile\fR (\fBBUNDLE_GEMFILE\fR): The name of the file that bundler should use as the \fBGemfile\fR\. This location of this file also sets the root of the project, which is used to resolve relative paths in the \fBGemfile\fR, among other things\. By default, bundler will search up from the current working directory until it finds a \fBGemfile\fR\.
+.
+.IP "\(bu" 4
+\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
+\fBglobal_path_appends_ruby_scope\fR (\fBBUNDLE_GLOBAL_PATH_APPENDS_RUBY_SCOPE\fR): Whether Bundler should append the Ruby scope (e\.g\. engine and ABI version) to a globally\-configured path\.
+.
+.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
+\fBinit_gems_rb\fR (\fBBUNDLE_INIT_GEMS_RB\fR) Generate a \fBgems\.rb\fR instead of a \fBGemfile\fR when running \fBbundle init\fR\.
+.
+.IP "\(bu" 4
+\fBjobs\fR (\fBBUNDLE_JOBS\fR): The number of gems Bundler can install in parallel\. Defaults to 1\.
+.
+.IP "\(bu" 4
+\fBlist_command\fR (\fBBUNDLE_LIST_COMMAND\fR) Enable new list command feature
+.
+.IP "\(bu" 4
+\fBmajor_deprecations\fR (\fBBUNDLE_MAJOR_DEPRECATIONS\fR): Whether Bundler should print deprecation warnings for behavior that will be changed in the next major version\.
+.
+.IP "\(bu" 4
+\fBno_install\fR (\fBBUNDLE_NO_INSTALL\fR): Whether \fBbundle package\fR should skip installing gems\.
+.
+.IP "\(bu" 4
+\fBno_prune\fR (\fBBUNDLE_NO_PRUNE\fR): Whether Bundler should leave outdated gems unpruned when caching\.
+.
+.IP "\(bu" 4
+\fBonly_update_to_newer_versions\fR (\fBBUNDLE_ONLY_UPDATE_TO_NEWER_VERSIONS\fR): During \fBbundle update\fR, only resolve to newer versions of the gems in the lockfile\.
+.
+.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
+\fBpath\.system\fR (\fBBUNDLE_PATH__SYSTEM\fR): Whether Bundler will install gems into the default system path (\fBGem\.dir\fR)\.
+.
+.IP "\(bu" 4
+\fBpath_relative_to_cwd\fR (\fBPATH_RELATIVE_TO_CWD\fR) Makes \fB\-\-path\fR relative to the CWD instead of the \fBGemfile\fR\.
+.
+.IP "\(bu" 4
+\fBplugins\fR (\fBBUNDLE_PLUGINS\fR): Enable Bundler\'s experimental plugin system\.
+.
+.IP "\(bu" 4
+\fBprefer_gems_rb\fR (\fBBUNDLE_PREFER_GEMS_RB\fR) Prefer \fBgems\.rb\fR to \fBGemfile\fR when Bundler is searching for a Gemfile\.
+.
+.IP "\(bu" 4
+\fBprint_only_version_number\fR (\fBBUNDLE_PRINT_ONLY_VERSION_NUMBER\fR) Print only version number from \fBbundler \-\-version\fR\.
+.
+.IP "\(bu" 4
+\fBredirect\fR (\fBBUNDLE_REDIRECT\fR): The number of redirects allowed for network requests\. Defaults to \fB5\fR\.
+.
+.IP "\(bu" 4
+\fBretry\fR (\fBBUNDLE_RETRY\fR): The number of times to retry failed network requests\. Defaults to \fB3\fR\.
+.
+.IP "\(bu" 4
+\fBsetup_makes_kernel_gem_public\fR (\fBBUNDLE_SETUP_MAKES_KERNEL_GEM_PUBLIC\fR): Have \fBBundler\.setup\fR make the \fBKernel#gem\fR method public, even though RubyGems declares it as private\.
+.
+.IP "\(bu" 4
+\fBshebang\fR (\fBBUNDLE_SHEBANG\fR): The program name that should be invoked for generated binstubs\. Defaults to the ruby install name used to generate the binstub\.
+.
+.IP "\(bu" 4
+\fBsilence_root_warning\fR (\fBBUNDLE_SILENCE_ROOT_WARNING\fR): Silence the warning Bundler prints when installing gems as root\.
+.
+.IP "\(bu" 4
+\fBskip_default_git_sources\fR (\fBBUNDLE_SKIP_DEFAULT_GIT_SOURCES\fR): Whether Bundler should skip adding default git source shortcuts to the Gemfile DSL\.
+.
+.IP "\(bu" 4
+\fBspecific_platform\fR (\fBBUNDLE_SPECIFIC_PLATFORM\fR): Allow bundler to resolve for the specific running platform and store it in the lockfile, instead of only using a generic platform\. A specific platform is the exact platform triple reported by \fBGem::Platform\.local\fR, such as \fBx86_64\-darwin\-16\fR or \fBuniversal\-java\-1\.8\fR\. On the other hand, generic platforms are those such as \fBruby\fR, \fBmswin\fR, or \fBjava\fR\. In this example, \fBx86_64\-darwin\-16\fR would map to \fBruby\fR and \fBuniversal\-java\-1\.8\fR to \fBjava\fR\.
+.
+.IP "\(bu" 4
+\fBssl_ca_cert\fR (\fBBUNDLE_SSL_CA_CERT\fR): Path to a designated CA certificate file or folder containing multiple certificates for trusted CAs in PEM format\.
+.
+.IP "\(bu" 4
+\fBssl_client_cert\fR (\fBBUNDLE_SSL_CLIENT_CERT\fR): Path to a designated file containing a X\.509 client certificate and key in PEM format\.
+.
+.IP "\(bu" 4
+\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
+\fBtimeout\fR (\fBBUNDLE_TIMEOUT\fR): The seconds allowed before timing out for network requests\. Defaults to \fB10\fR\.
+.
+.IP "\(bu" 4
+\fBunlock_source_unlocks_spec\fR (\fBBUNDLE_UNLOCK_SOURCE_UNLOCKS_SPEC\fR): Whether running \fBbundle update \-\-source NAME\fR unlocks a gem with the given name\. Defaults to \fBtrue\fR\.
+.
+.IP "\(bu" 4
+\fBupdate_requires_all_flag\fR (\fBBUNDLE_UPDATE_REQUIRES_ALL_FLAG\fR) Require passing \fB\-\-all\fR to \fBbundle update\fR when everything should be updated, and disallow passing no options to \fBbundle update\fR\.
+.
+.IP "\(bu" 4
+\fBuser_agent\fR (\fBBUNDLE_USER_AGENT\fR): The custom user agent fragment Bundler includes in API requests\.
+.
+.IP "\(bu" 4
+\fBwith\fR (\fBBUNDLE_WITH\fR): A \fB:\fR\-separated list of groups whose gems bundler should install\.
+.
+.IP "\(bu" 4
+\fBwithout\fR (\fBBUNDLE_WITHOUT\fR): A \fB:\fR\-separated list of groups whose gems bundler should not install\.
+.
+.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\.
+.
+.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\.
+.
+.SH "LOCAL GIT REPOS"
+Bundler also allows you to work against a git repository locally instead of using the remote version\. This can be achieved by setting up a local override:
+.
+.IP "" 4
+.
+.nf
+
+bundle config local\.GEM_NAME /path/to/local/git/repository
+.
+.fi
+.
+.IP "" 0
+.
+.P
+For example, in order to use a local Rack repository, a developer could call:
+.
+.IP "" 4
+.
+.nf
+
+bundle config local\.rack ~/Work/git/rack
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Now instead of checking out the remote git repository, the local override will be used\. Similar to a path source, every time the local git repository change, changes will be automatically picked up by Bundler\. This means a commit in the local git repo will update the revision in the \fBGemfile\.lock\fR to the local git repo revision\. This requires the same attention as git submodules\. Before pushing to the remote, you need to ensure the local override was pushed, otherwise you may point to a commit that only exists in your local machine\. You\'ll also need to CGI escape your usernames and passwords as well\.
+.
+.P
+Bundler does many checks to ensure a developer won\'t work with invalid references\. Particularly, we force a developer to specify a branch in the \fBGemfile\fR in order to use this feature\. If the branch specified in the \fBGemfile\fR and the current branch in the local git repository do not match, Bundler will abort\. This ensures that a developer is always working against the correct branches, and prevents accidental locking to a different branch\.
+.
+.P
+Finally, Bundler also ensures that the current revision in the \fBGemfile\.lock\fR exists in the local git repository\. By doing this, Bundler forces you to fetch the latest changes in the remotes\.
+.
+.SH "MIRRORS OF GEM SOURCES"
+Bundler supports overriding gem sources with mirrors\. This allows you to configure rubygems\.org as the gem source in your Gemfile while still using your mirror to fetch gems\.
+.
+.IP "" 4
+.
+.nf
+
+bundle config mirror\.SOURCE_URL MIRROR_URL
+.
+.fi
+.
+.IP "" 0
+.
+.P
+For example, to use a mirror of rubygems\.org hosted at rubygems\-mirror\.org:
+.
+.IP "" 4
+.
+.nf
+
+bundle config mirror\.http://rubygems\.org http://rubygems\-mirror\.org
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Each mirror also provides a fallback timeout setting\. If the mirror does not respond within the fallback timeout, Bundler will try to use the original server instead of the mirror\.
+.
+.IP "" 4
+.
+.nf
+
+bundle config mirror\.SOURCE_URL\.fallback_timeout TIMEOUT
+.
+.fi
+.
+.IP "" 0
+.
+.P
+For example, to fall back to rubygems\.org after 3 seconds:
+.
+.IP "" 4
+.
+.nf
+
+bundle config mirror\.https://rubygems\.org\.fallback_timeout 3
+.
+.fi
+.
+.IP "" 0
+.
+.P
+The default fallback timeout is 0\.1 seconds, but the setting can currently only accept whole seconds (for example, 1, 15, or 30)\.
+.
+.SH "CREDENTIALS FOR GEM SOURCES"
+Bundler allows you to configure credentials for any gem source, which allows you to avoid putting secrets into your Gemfile\.
+.
+.IP "" 4
+.
+.nf
+
+bundle config SOURCE_HOSTNAME USERNAME:PASSWORD
+.
+.fi
+.
+.IP "" 0
+.
+.P
+For example, to save the credentials of user \fBclaudette\fR for the gem source at \fBgems\.longerous\.com\fR, you would run:
+.
+.IP "" 4
+.
+.nf
+
+bundle config gems\.longerous\.com claudette:s00pers3krit
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Or you can set the credentials as an environment variable like this:
+.
+.IP "" 4
+.
+.nf
+
+export BUNDLE_GEMS__LONGEROUS__COM="claudette:s00pers3krit"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+For gems with a git source with HTTP(S) URL you can specify credentials like so:
+.
+.IP "" 4
+.
+.nf
+
+bundle config https://github\.com/bundler/bundler\.git username:password
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Or you can set the credentials as an environment variable like so:
+.
+.IP "" 4
+.
+.nf
+
+export BUNDLE_GITHUB__COM=username:password
+.
+.fi
+.
+.IP "" 0
+.
+.P
+This is especially useful for private repositories on hosts such as Github, where you can use personal OAuth tokens:
+.
+.IP "" 4
+.
+.nf
+
+export BUNDLE_GITHUB__COM=abcd0123generatedtoken:x\-oauth\-basic
+.
+.fi
+.
+.IP "" 0
+.
+.SH "CONFIGURE BUNDLER DIRECTORIES"
+Bundler\'s home, config, cache and plugin directories are able to be configured through environment variables\. The default location for Bundler\'s home directory is \fB~/\.bundle\fR, which all directories inherit from by default\. The following outlines the available environment variables and their default values
+.
+.IP "" 4
+.
+.nf
+
+BUNDLE_USER_HOME : $HOME/\.bundle
+BUNDLE_USER_CACHE : $BUNDLE_USER_HOME/cache
+BUNDLE_USER_CONFIG : $BUNDLE_USER_HOME/config
+BUNDLE_USER_PLUGIN : $BUNDLE_USER_HOME/plugin
+.
+.fi
+.
+.IP "" 0
+
diff --git a/man/bundle-config.1.txt b/man/bundle-config.1.txt
new file mode 100644
index 0000000000..f8f421c3db
--- /dev/null
+++ b/man/bundle-config.1.txt
@@ -0,0 +1,529 @@
+BUNDLE-CONFIG(1) BUNDLE-CONFIG(1)
+
+
+
+1mNAME0m
+ 1mbundle-config 22m- Set bundler configuration options
+
+1mSYNOPSIS0m
+ 1mbundle config 22m[4mname24m [4mvalue24m]]
+
+1mDESCRIPTION0m
+ This command allows you to interact with Bundler's configuration sys-
+ tem.
+
+ Bundler loads configuration settings in this order:
+
+ 1. Local config (1mapp/.bundle/config22m)
+
+ 2. Environmental variables (1mENV22m)
+
+ 3. Global config (1m~/.bundle/config22m)
+
+ 4. Bundler default config
+
+
+
+ Executing 1mbundle config 22mwith no parameters will print a list of all
+ bundler configuration for the current bundle, and where that configura-
+ tion was set.
+
+ Executing 1mbundle config <name> 22mwill print the value of that configura-
+ tion setting, and where it was set.
+
+ Executing 1mbundle config <name> <value> 22mwill set that configuration to
+ the value specified for all bundles executed as the current user. The
+ configuration will be stored in 1m~/.bundle/config22m. If 4mname24m already is
+ set, 4mname24m will be overridden and user will be warned.
+
+ Executing 1mbundle config --global <name> <value> 22mworks the same as
+ above.
+
+ Executing 1mbundle config --local <name> <value> 22mwill set that configura-
+ tion to the local application. The configuration will be stored in
+ 1mapp/.bundle/config22m.
+
+ Executing 1mbundle config --delete <name> 22mwill delete the configuration
+ in both local and global sources. Not compatible with --global or
+ --local flag.
+
+ Executing bundle with the 1mBUNDLE_IGNORE_CONFIG 22menvironment variable set
+ will cause it to ignore all configuration.
+
+ Executing 1mbundle config disable_multisource true 22mupgrades the warning
+ about the Gemfile containing multiple primary sources to an error. Exe-
+ cuting 1mbundle config --delete disable_multisource 22mdowngrades this error
+ to a warning.
+
+1mREMEMBERING OPTIONS0m
+ Flags passed to 1mbundle install 22mor the Bundler runtime, such as 1m--path0m
+ 1mfoo 22mor 1m--without production22m, are not remembered between commands. If
+ these options must be remembered,they must be set using 1mbundle config0m
+ (e.g., 1mbundle config path foo22m).
+
+ The options that can be configured are:
+
+ 1mbin 22mCreates a directory (defaults to 1m~/bin22m) and place any executa-
+ bles from the gem there. These executables run in Bundler's con-
+ text. If used, you might add this directory to your environ-
+ ment's 1mPATH 22mvariable. For instance, if the 1mrails 22mgem comes with
+ a 1mrails 22mexecutable, this flag will create a 1mbin/rails 22mexecutable
+ that ensures that all referred dependencies will be resolved
+ using the bundled gems.
+
+ 1mdeployment0m
+ In deployment mode, Bundler will 'roll-out' the bundle for 1mpro-0m
+ 1mduction 22muse. Please check carefully if you want to have this
+ option enabled in 1mdevelopment 22mor 1mtest 22menvironments.
+
+ 1mpath 22mThe location to install the specified gems to. This defaults to
+ Rubygems' setting. Bundler shares this location with Rubygems,
+ 1mgem install ... 22mwill have gem installed there, too. Therefore,
+ gems installed without a 1m--path ... 22msetting will show up by
+ calling 1mgem list22m. Accordingly, gems installed to other locations
+ will not get listed.
+
+ 1mwithout0m
+ A space-separated list of groups referencing gems to skip during
+ installation.
+
+ 1mwith 22mA space-separated list of groups referencing gems to include
+ during installation.
+
+1mBUILD OPTIONS0m
+ You can use 1mbundle config 22mto give Bundler the flags to pass to the gem
+ installer every time bundler tries to install a particular gem.
+
+ A very common example, the 1mmysql 22mgem, requires Snow Leopard users to
+ pass configuration flags to 1mgem install 22mto specify where to find the
+ 1mmysql_config 22mexecutable.
+
+
+
+ gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
+
+
+
+ Since the specific location of that executable can change from machine
+ to machine, you can specify these flags on a per-machine basis.
+
+
+
+ bundle config build.mysql --with-mysql-config=/usr/local/mysql/bin/mysql_config
+
+
+
+ After running this command, every time bundler needs to install the
+ 1mmysql 22mgem, it will pass along the flags you specified.
+
+1mCONFIGURATION KEYS0m
+ Configuration keys in bundler have two forms: the canonical form and
+ the environment variable form.
+
+ For instance, passing the 1m--without 22mflag to bundle install(1) 4mbun-0m
+ 4mdle-install.1.html24m prevents Bundler from installing certain groups
+ specified in the Gemfile(5). Bundler persists this value in 1mapp/.bun-0m
+ 1mdle/config 22mso that calls to 1mBundler.setup 22mdo not try to find gems from
+ the 1mGemfile 22mthat you didn't install. Additionally, subsequent calls to
+ bundle install(1) 4mbundle-install.1.html24m remember this setting and skip
+ those groups.
+
+ The canonical form of this configuration is 1m"without"22m. To convert the
+ canonical form to the environment variable form, capitalize it, and
+ prepend 1mBUNDLE_22m. The environment variable form of 1m"without" 22mis 1mBUN-0m
+ 1mDLE_WITHOUT22m.
+
+ Any periods in the configuration keys must be replaced with two under-
+ scores when setting it via environment variables. The configuration key
+ 1mlocal.rack 22mbecomes the environment variable 1mBUNDLE_LOCAL__RACK22m.
+
+1mLIST OF AVAILABLE KEYS0m
+ The following is a list of all configuration keys and their purpose.
+ You can learn more about their operation in bundle install(1) 4mbun-0m
+ 4mdle-install.1.html24m.
+
+ o 1mallow_bundler_dependency_conflicts 22m(1mBUNDLE_ALLOW_BUNDLER_DEPEN-0m
+ 1mDENCY_CONFLICTS22m): Allow resolving to specifications that have
+ dependencies on 1mbundler 22mthat are incompatible with the running
+ Bundler version.
+
+ o 1mallow_deployment_source_credential_changes 22m(1mBUNDLE_ALLOW_DEPLOY-0m
+ 1mMENT_SOURCE_CREDENTIAL_CHANGES22m): When in deployment mode, allow
+ changing the credentials to a gem's source. Ex:
+ 1mhttps://some.host.com/gems/path/ 22m-> 1mhttps://user_name:pass-0m
+ 1mword@some.host.com/gems/path0m
+
+ o 1mallow_offline_install 22m(1mBUNDLE_ALLOW_OFFLINE_INSTALL22m): Allow Bundler
+ to use cached data when installing without network access.
+
+ o 1mauto_clean_without_path 22m(1mBUNDLE_AUTO_CLEAN_WITHOUT_PATH22m): Automati-
+ cally run 1mbundle clean 22mafter installing when an explicit 1mpath 22mhas
+ not been set and Bundler is not installing into the system gems.
+
+ o 1mauto_install 22m(1mBUNDLE_AUTO_INSTALL22m): Automatically run 1mbundle0m
+ 1minstall 22mwhen gems are missing.
+
+ o 1mbin 22m(1mBUNDLE_BIN22m): Install executables from gems in the bundle to
+ the specified directory. Defaults to 1mfalse22m.
+
+ o 1mcache_all 22m(1mBUNDLE_CACHE_ALL22m): Cache all gems, including path and
+ git gems.
+
+ o 1mcache_all_platforms 22m(1mBUNDLE_CACHE_ALL_PLATFORMS22m): Cache gems for
+ all platforms.
+
+ o 1mcache_path 22m(1mBUNDLE_CACHE_PATH22m): The directory that bundler will
+ place cached gems in when running 1mbundle package22m, and that bundler
+ will look in when installing gems. Defaults to 1mvendor/bundle22m.
+
+ o 1mclean 22m(1mBUNDLE_CLEAN22m): Whether Bundler should run 1mbundle clean 22mauto-
+ matically after 1mbundle install22m.
+
+ o 1mconsole 22m(1mBUNDLE_CONSOLE22m): The console that 1mbundle console 22mstarts.
+ Defaults to 1mirb22m.
+
+ o 1mdefault_install_uses_path 22m(1mBUNDLE_DEFAULT_INSTALL_USES_PATH22m):
+ Whether a 1mbundle install 22mwithout an explicit 1m--path 22margument
+ defaults to installing gems in 1m.bundle22m.
+
+ o 1mdeployment 22m(1mBUNDLE_DEPLOYMENT22m): Disallow changes to the 1mGemfile22m.
+ When the 1mGemfile 22mis changed and the lockfile has not been updated,
+ running Bundler commands will be blocked.
+
+ o 1mdisable_checksum_validation 22m(1mBUNDLE_DISABLE_CHECKSUM_VALIDATION22m):
+ Allow installing gems even if they do not match the checksum pro-
+ vided by RubyGems.
+
+ o 1mdisable_exec_load 22m(1mBUNDLE_DISABLE_EXEC_LOAD22m): Stop Bundler from
+ using 1mload 22mto launch an executable in-process in 1mbundle exec22m.
+
+ o 1mdisable_local_branch_check 22m(1mBUNDLE_DISABLE_LOCAL_BRANCH_CHECK22m):
+ Allow Bundler to use a local git override without a branch speci-
+ fied in the Gemfile.
+
+ o 1mdisable_multisource 22m(1mBUNDLE_DISABLE_MULTISOURCE22m): When set, Gem-
+ files containing multiple sources will produce errors instead of
+ warnings. Use 1mbundle config --delete disable_multisource 22mto unset.
+
+ o 1mdisable_platform_warnings 22m(1mBUNDLE_DISABLE_PLATFORM_WARNINGS22m): Dis-
+ able warnings during bundle install when a dependency is unused on
+ the current platform.
+
+ o 1mdisable_shared_gems 22m(1mBUNDLE_DISABLE_SHARED_GEMS22m): Stop Bundler from
+ accessing gems installed to RubyGems' normal location.
+
+ o 1mdisable_version_check 22m(1mBUNDLE_DISABLE_VERSION_CHECK22m): Stop Bundler
+ from checking if a newer Bundler version is available on
+ rubygems.org.
+
+ o 1merror_on_stderr 22m(1mBUNDLE_ERROR_ON_STDERR22m): Print Bundler errors to
+ stderr.
+
+ o 1mforce_ruby_platform 22m(1mBUNDLE_FORCE_RUBY_PLATFORM22m): Ignore the cur-
+ rent machine's platform and install only 1mruby 22mplatform gems. As a
+ result, gems with native extensions will be compiled from source.
+
+ o 1mfrozen 22m(1mBUNDLE_FROZEN22m): Disallow changes to the 1mGemfile22m. When the
+ 1mGemfile 22mis changed and the lockfile has not been updated, running
+ Bundler commands will be blocked. Defaults to 1mtrue 22mwhen 1m--deploy-0m
+ 1mment 22mis used.
+
+ o 1mgem.push_key 22m(1mBUNDLE_GEM__PUSH_KEY22m): Sets the 1m--key 22mparameter for
+ 1mgem push 22mwhen using the 1mrake release 22mcommand with a private gem-
+ stash server.
+
+ o 1mgemfile 22m(1mBUNDLE_GEMFILE22m): The name of the file that bundler should
+ use as the 1mGemfile22m. This location of this file also sets the root
+ of the project, which is used to resolve relative paths in the 1mGem-0m
+ 1mfile22m, among other things. By default, bundler will search up from
+ the current working directory until it finds a 1mGemfile22m.
+
+ o 1mglobal_gem_cache 22m(1mBUNDLE_GLOBAL_GEM_CACHE22m): Whether Bundler should
+ cache all gems globally, rather than locally to the installing Ruby
+ installation.
+
+ o 1mglobal_path_appends_ruby_scope 22m(1mBUN-0m
+ 1mDLE_GLOBAL_PATH_APPENDS_RUBY_SCOPE22m): Whether Bundler should append
+ the Ruby scope (e.g. engine and ABI version) to a globally-config-
+ ured path.
+
+ o 1mignore_messages 22m(1mBUNDLE_IGNORE_MESSAGES22m): When set, no post install
+ messages will be printed. To silence a single gem, use dot notation
+ like 1mignore_messages.httparty true22m.
+
+ o 1minit_gems_rb 22m(1mBUNDLE_INIT_GEMS_RB22m) Generate a 1mgems.rb 22minstead of a
+ 1mGemfile 22mwhen running 1mbundle init22m.
+
+ o 1mjobs 22m(1mBUNDLE_JOBS22m): The number of gems Bundler can install in par-
+ allel. Defaults to 1.
+
+ o 1mlist_command 22m(1mBUNDLE_LIST_COMMAND22m) Enable new list command feature
+
+ o 1mmajor_deprecations 22m(1mBUNDLE_MAJOR_DEPRECATIONS22m): Whether Bundler
+ should print deprecation warnings for behavior that will be changed
+ in the next major version.
+
+ o 1mno_install 22m(1mBUNDLE_NO_INSTALL22m): Whether 1mbundle package 22mshould skip
+ installing gems.
+
+ o 1mno_prune 22m(1mBUNDLE_NO_PRUNE22m): Whether Bundler should leave outdated
+ gems unpruned when caching.
+
+ o 1monly_update_to_newer_versions 22m(1mBUNDLE_ONLY_UPDATE_TO_NEWER_VER-0m
+ 1mSIONS22m): During 1mbundle update22m, only resolve to newer versions of the
+ gems in the lockfile.
+
+ o 1mpath 22m(1mBUNDLE_PATH22m): The location on disk where all gems in your
+ bundle will be located regardless of 1m$GEM_HOME 22mor 1m$GEM_PATH 22mvalues.
+ Bundle gems not found in this location will be installed by 1mbundle0m
+ 1minstall22m. Defaults to 1mGem.dir22m. When --deployment is used, defaults
+ to vendor/bundle.
+
+ o 1mpath.system 22m(1mBUNDLE_PATH__SYSTEM22m): Whether Bundler will install
+ gems into the default system path (1mGem.dir22m).
+
+ o 1mpath_relative_to_cwd 22m(1mPATH_RELATIVE_TO_CWD22m) Makes 1m--path 22mrelative
+ to the CWD instead of the 1mGemfile22m.
+
+ o 1mplugins 22m(1mBUNDLE_PLUGINS22m): Enable Bundler's experimental plugin sys-
+ tem.
+
+ o 1mprefer_gems_rb 22m(1mBUNDLE_PREFER_GEMS_RB22m) Prefer 1mgems.rb 22mto 1mGemfile0m
+ when Bundler is searching for a Gemfile.
+
+ o 1mprint_only_version_number 22m(1mBUNDLE_PRINT_ONLY_VERSION_NUMBER22m) Print
+ only version number from 1mbundler --version22m.
+
+ o 1mredirect 22m(1mBUNDLE_REDIRECT22m): The number of redirects allowed for
+ network requests. Defaults to 1m522m.
+
+ o 1mretry 22m(1mBUNDLE_RETRY22m): The number of times to retry failed network
+ requests. Defaults to 1m322m.
+
+ o 1msetup_makes_kernel_gem_public 22m(1mBUNDLE_SETUP_MAKES_KERNEL_GEM_PUB-0m
+ 1mLIC22m): Have 1mBundler.setup 22mmake the 1mKernel#gem 22mmethod public, even
+ though RubyGems declares it as private.
+
+ o 1mshebang 22m(1mBUNDLE_SHEBANG22m): The program name that should be invoked
+ for generated binstubs. Defaults to the ruby install name used to
+ generate the binstub.
+
+ o 1msilence_root_warning 22m(1mBUNDLE_SILENCE_ROOT_WARNING22m): Silence the
+ warning Bundler prints when installing gems as root.
+
+ o 1mskip_default_git_sources 22m(1mBUNDLE_SKIP_DEFAULT_GIT_SOURCES22m): Whether
+ Bundler should skip adding default git source shortcuts to the Gem-
+ file DSL.
+
+ o 1mspecific_platform 22m(1mBUNDLE_SPECIFIC_PLATFORM22m): Allow bundler to
+ resolve for the specific running platform and store it in the lock-
+ file, instead of only using a generic platform. A specific platform
+ is the exact platform triple reported by 1mGem::Platform.local22m, such
+ as 1mx86_64-darwin-16 22mor 1muniversal-java-1.822m. On the other hand,
+ generic platforms are those such as 1mruby22m, 1mmswin22m, or 1mjava22m. In this
+ example, 1mx86_64-darwin-16 22mwould map to 1mruby 22mand 1muniversal-java-1.80m
+ to 1mjava22m.
+
+ o 1mssl_ca_cert 22m(1mBUNDLE_SSL_CA_CERT22m): Path to a designated CA certifi-
+ cate file or folder containing multiple certificates for trusted
+ CAs in PEM format.
+
+ o 1mssl_client_cert 22m(1mBUNDLE_SSL_CLIENT_CERT22m): Path to a designated file
+ containing a X.509 client certificate and key in PEM format.
+
+ o 1mssl_verify_mode 22m(1mBUNDLE_SSL_VERIFY_MODE22m): The SSL verification mode
+ Bundler uses when making HTTPS requests. Defaults to verify peer.
+
+ o 1msuppress_install_using_messages 22m(1mBUNDLE_SUPPRESS_INSTALL_USING_MES-0m
+ 1mSAGES22m): Avoid printing 1mUsing ... 22mmessages during installation when
+ the version of a gem has not changed.
+
+ o 1msystem_bindir 22m(1mBUNDLE_SYSTEM_BINDIR22m): The location where RubyGems
+ installs binstubs. Defaults to 1mGem.bindir22m.
+
+ o 1mtimeout 22m(1mBUNDLE_TIMEOUT22m): The seconds allowed before timing out for
+ network requests. Defaults to 1m1022m.
+
+ o 1munlock_source_unlocks_spec 22m(1mBUNDLE_UNLOCK_SOURCE_UNLOCKS_SPEC22m):
+ Whether running 1mbundle update --source NAME 22munlocks a gem with the
+ given name. Defaults to 1mtrue22m.
+
+ o 1mupdate_requires_all_flag 22m(1mBUNDLE_UPDATE_REQUIRES_ALL_FLAG22m) Require
+ passing 1m--all 22mto 1mbundle update 22mwhen everything should be updated,
+ and disallow passing no options to 1mbundle update22m.
+
+ o 1muser_agent 22m(1mBUNDLE_USER_AGENT22m): The custom user agent fragment
+ Bundler includes in API requests.
+
+ o 1mwith 22m(1mBUNDLE_WITH22m): A 1m:22m-separated list of groups whose gems bundler
+ should install.
+
+ o 1mwithout 22m(1mBUNDLE_WITHOUT22m): A 1m:22m-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) 4mbundle-install.1.html24m or bun-
+ dle package(1) 4mbundle-package.1.html24m command.
+
+ You can set them globally either via environment variables or 1mbundle0m
+ 1mconfig22m, whichever is preferable for your setup. If you use both, envi-
+ ronment variables will take preference over global settings.
+
+1mLOCAL GIT REPOS0m
+ Bundler also allows you to work against a git repository locally
+ instead of using the remote version. This can be achieved by setting up
+ a local override:
+
+
+
+ bundle config local.GEM_NAME /path/to/local/git/repository
+
+
+
+ For example, in order to use a local Rack repository, a developer could
+ call:
+
+
+
+ bundle config local.rack ~/Work/git/rack
+
+
+
+ Now instead of checking out the remote git repository, the local over-
+ ride will be used. Similar to a path source, every time the local git
+ repository change, changes will be automatically picked up by Bundler.
+ This means a commit in the local git repo will update the revision in
+ the 1mGemfile.lock 22mto the local git repo revision. This requires the same
+ attention as git submodules. Before pushing to the remote, you need to
+ ensure the local override was pushed, otherwise you may point to a com-
+ mit that only exists in your local machine. You'll also need to CGI
+ escape your usernames and passwords as well.
+
+ Bundler does many checks to ensure a developer won't work with invalid
+ references. Particularly, we force a developer to specify a branch in
+ the 1mGemfile 22min order to use this feature. If the branch specified in
+ the 1mGemfile 22mand the current branch in the local git repository do not
+ match, Bundler will abort. This ensures that a developer is always
+ working against the correct branches, and prevents accidental locking
+ to a different branch.
+
+ Finally, Bundler also ensures that the current revision in the 1mGem-0m
+ 1mfile.lock 22mexists in the local git repository. By doing this, Bundler
+ forces you to fetch the latest changes in the remotes.
+
+1mMIRRORS OF GEM SOURCES0m
+ Bundler supports overriding gem sources with mirrors. This allows you
+ to configure rubygems.org as the gem source in your Gemfile while still
+ using your mirror to fetch gems.
+
+
+
+ bundle config mirror.SOURCE_URL MIRROR_URL
+
+
+
+ For example, to use a mirror of rubygems.org hosted at rubygems-mir-
+ ror.org:
+
+
+
+ bundle config mirror.http://rubygems.org http://rubygems-mirror.org
+
+
+
+ Each mirror also provides a fallback timeout setting. If the mirror
+ does not respond within the fallback timeout, Bundler will try to use
+ the original server instead of the mirror.
+
+
+
+ bundle config mirror.SOURCE_URL.fallback_timeout TIMEOUT
+
+
+
+ For example, to fall back to rubygems.org after 3 seconds:
+
+
+
+ bundle config mirror.https://rubygems.org.fallback_timeout 3
+
+
+
+ The default fallback timeout is 0.1 seconds, but the setting can cur-
+ rently only accept whole seconds (for example, 1, 15, or 30).
+
+1mCREDENTIALS FOR GEM SOURCES0m
+ Bundler allows you to configure credentials for any gem source, which
+ allows you to avoid putting secrets into your Gemfile.
+
+
+
+ bundle config SOURCE_HOSTNAME USERNAME:PASSWORD
+
+
+
+ For example, to save the credentials of user 1mclaudette 22mfor the gem
+ source at 1mgems.longerous.com22m, you would run:
+
+
+
+ bundle config gems.longerous.com claudette:s00pers3krit
+
+
+
+ Or you can set the credentials as an environment variable like this:
+
+
+
+ export BUNDLE_GEMS__LONGEROUS__COM="claudette:s00pers3krit"
+
+
+
+ For gems with a git source with HTTP(S) URL you can specify credentials
+ like so:
+
+
+
+ bundle config https://github.com/bundler/bundler.git username:password
+
+
+
+ Or you can set the credentials as an environment variable like so:
+
+
+
+ export BUNDLE_GITHUB__COM=username:password
+
+
+
+ This is especially useful for private repositories on hosts such as
+ Github, where you can use personal OAuth tokens:
+
+
+
+ export BUNDLE_GITHUB__COM=abcd0123generatedtoken:x-oauth-basic
+
+
+
+1mCONFIGURE BUNDLER DIRECTORIES0m
+ Bundler's home, config, cache and plugin directories are able to be
+ configured through environment variables. The default location for
+ Bundler's home directory is 1m~/.bundle22m, which all directories inherit
+ from by default. The following outlines the available environment vari-
+ ables and their default values
+
+
+
+ BUNDLE_USER_HOME : $HOME/.bundle
+ BUNDLE_USER_CACHE : $BUNDLE_USER_HOME/cache
+ BUNDLE_USER_CONFIG : $BUNDLE_USER_HOME/config
+ BUNDLE_USER_PLUGIN : $BUNDLE_USER_HOME/plugin
+
+
+
+
+
+
+ December 2018 BUNDLE-CONFIG(1)
diff --git a/man/bundle-config.ronn b/man/bundle-config.ronn
new file mode 100644
index 0000000000..b5c97ae82d
--- /dev/null
+++ b/man/bundle-config.ronn
@@ -0,0 +1,397 @@
+bundle-config(1) -- Set bundler configuration options
+=====================================================
+
+## SYNOPSIS
+
+`bundle config` [<name> [<value>]]
+
+## DESCRIPTION
+
+This command allows you to interact with Bundler's configuration system.
+
+Bundler loads configuration settings in this order:
+
+1. Local config (`app/.bundle/config`)
+2. Environmental variables (`ENV`)
+3. Global config (`~/.bundle/config`)
+4. Bundler default config
+
+Executing `bundle config` with no parameters will print a list of all
+bundler configuration for the current bundle, and where that configuration
+was set.
+
+Executing `bundle config <name>` will print the value of that configuration
+setting, and where it was set.
+
+Executing `bundle config <name> <value>` will set that configuration to the
+value specified for all bundles executed as the current user. The configuration
+will be stored in `~/.bundle/config`. If <name> already is set, <name> will be
+overridden and user will be warned.
+
+Executing `bundle config --global <name> <value>` works the same as above.
+
+Executing `bundle config --local <name> <value>` will set that configuration to
+the local application. The configuration will be stored in `app/.bundle/config`.
+
+Executing `bundle config --delete <name>` will delete the configuration in both
+local and global sources. Not compatible with --global or --local flag.
+
+Executing bundle with the `BUNDLE_IGNORE_CONFIG` environment variable set will
+cause it to ignore all configuration.
+
+Executing `bundle config disable_multisource true` upgrades the warning about
+the Gemfile containing multiple primary sources to an error. Executing `bundle
+config --delete disable_multisource` downgrades this error to a warning.
+
+## REMEMBERING OPTIONS
+
+Flags passed to `bundle install` or the Bundler runtime,
+such as `--path foo` or `--without production`, are not remembered between commands.
+If these options must be remembered,they must be set using `bundle config`
+(e.g., `bundle config path foo`).
+
+The options that can be configured are:
+
+* `bin`:
+ Creates a directory (defaults to `~/bin`) and place any executables from the
+ gem there. These executables run in Bundler's context. If used, you might add
+ this directory to your environment's `PATH` variable. For instance, if the
+ `rails` gem comes with a `rails` executable, this flag will create a
+ `bin/rails` executable that ensures that all referred dependencies will be
+ resolved using the bundled gems.
+
+* `deployment`:
+ In deployment mode, Bundler will 'roll-out' the bundle for
+ `production` use. Please check carefully if you want to have this option
+ enabled in `development` or `test` environments.
+
+* `path`:
+ The location to install the specified gems to. This defaults to Rubygems'
+ setting. Bundler shares this location with Rubygems, `gem install ...` will
+ have gem installed there, too. Therefore, gems installed without a
+ `--path ...` setting will show up by calling `gem list`. Accordingly, gems
+ installed to other locations will not get listed.
+
+* `without`:
+ A space-separated list of groups referencing gems to skip during installation.
+
+* `with`:
+ A space-separated list of groups referencing gems to include during installation.
+
+## BUILD OPTIONS
+
+You can use `bundle config` to give Bundler the flags to pass to the gem
+installer every time bundler tries to install a particular gem.
+
+A very common example, the `mysql` gem, requires Snow Leopard users to
+pass configuration flags to `gem install` to specify where to find the
+`mysql_config` executable.
+
+ gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
+
+Since the specific location of that executable can change from machine
+to machine, you can specify these flags on a per-machine basis.
+
+ bundle config build.mysql --with-mysql-config=/usr/local/mysql/bin/mysql_config
+
+After running this command, every time bundler needs to install the
+`mysql` gem, it will pass along the flags you specified.
+
+## CONFIGURATION KEYS
+
+Configuration keys in bundler have two forms: the canonical form and the
+environment variable form.
+
+For instance, passing the `--without` flag to [bundle install(1)](bundle-install.1.html)
+prevents Bundler from installing certain groups specified in the Gemfile(5). Bundler
+persists this value in `app/.bundle/config` so that calls to `Bundler.setup`
+do not try to find gems from the `Gemfile` that you didn't install. Additionally,
+subsequent calls to [bundle install(1)](bundle-install.1.html) remember this setting
+and skip those groups.
+
+The canonical form of this configuration is `"without"`. To convert the canonical
+form to the environment variable form, capitalize it, and prepend `BUNDLE_`. The
+environment variable form of `"without"` is `BUNDLE_WITHOUT`.
+
+Any periods in the configuration keys must be replaced with two underscores when
+setting it via environment variables. The configuration key `local.rack` becomes
+the environment variable `BUNDLE_LOCAL__RACK`.
+
+## LIST OF AVAILABLE KEYS
+
+The following is a list of all configuration keys and their purpose. You can
+learn more about their operation in [bundle install(1)](bundle-install.1.html).
+
+* `allow_bundler_dependency_conflicts` (`BUNDLE_ALLOW_BUNDLER_DEPENDENCY_CONFLICTS`):
+ Allow resolving to specifications that have dependencies on `bundler` that
+ are incompatible with the running Bundler version.
+* `allow_deployment_source_credential_changes` (`BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES`):
+ When in deployment mode, allow changing the credentials to a gem's source.
+ Ex: `https://some.host.com/gems/path/` -> `https://user_name:password@some.host.com/gems/path`
+* `allow_offline_install` (`BUNDLE_ALLOW_OFFLINE_INSTALL`):
+ Allow Bundler to use cached data when installing without network access.
+* `auto_clean_without_path` (`BUNDLE_AUTO_CLEAN_WITHOUT_PATH`):
+ Automatically run `bundle clean` after installing when an explicit `path`
+ has not been set and Bundler is not installing into the system gems.
+* `auto_install` (`BUNDLE_AUTO_INSTALL`):
+ Automatically run `bundle install` when gems are missing.
+* `bin` (`BUNDLE_BIN`):
+ Install executables from gems in the bundle to the specified directory.
+ Defaults to `false`.
+* `cache_all` (`BUNDLE_CACHE_ALL`):
+ Cache all gems, including path and git gems.
+* `cache_all_platforms` (`BUNDLE_CACHE_ALL_PLATFORMS`):
+ Cache gems for all platforms.
+* `cache_path` (`BUNDLE_CACHE_PATH`):
+ The directory that bundler will place cached gems in when running
+ <code>bundle package</code>, and that bundler will look in when installing gems.
+ Defaults to `vendor/bundle`.
+* `clean` (`BUNDLE_CLEAN`):
+ Whether Bundler should run `bundle clean` automatically after
+ `bundle install`.
+* `console` (`BUNDLE_CONSOLE`):
+ The console that `bundle console` starts. Defaults to `irb`.
+* `default_install_uses_path` (`BUNDLE_DEFAULT_INSTALL_USES_PATH`):
+ Whether a `bundle install` without an explicit `--path` argument defaults
+ to installing gems in `.bundle`.
+* `deployment` (`BUNDLE_DEPLOYMENT`):
+ Disallow changes to the `Gemfile`. When the `Gemfile` is changed and the
+ lockfile has not been updated, running Bundler commands will be blocked.
+* `disable_checksum_validation` (`BUNDLE_DISABLE_CHECKSUM_VALIDATION`):
+ Allow installing gems even if they do not match the checksum provided by
+ RubyGems.
+* `disable_exec_load` (`BUNDLE_DISABLE_EXEC_LOAD`):
+ Stop Bundler from using `load` to launch an executable in-process in
+ `bundle exec`.
+* `disable_local_branch_check` (`BUNDLE_DISABLE_LOCAL_BRANCH_CHECK`):
+ Allow Bundler to use a local git override without a branch specified in the
+ Gemfile.
+* `disable_multisource` (`BUNDLE_DISABLE_MULTISOURCE`):
+ When set, Gemfiles containing multiple sources will produce errors
+ instead of warnings.
+ Use `bundle config --delete disable_multisource` to unset.
+* `disable_platform_warnings` (`BUNDLE_DISABLE_PLATFORM_WARNINGS`):
+ Disable warnings during bundle install when a dependency is unused on the current platform.
+* `disable_shared_gems` (`BUNDLE_DISABLE_SHARED_GEMS`):
+ Stop Bundler from accessing gems installed to RubyGems' normal location.
+* `disable_version_check` (`BUNDLE_DISABLE_VERSION_CHECK`):
+ Stop Bundler from checking if a newer Bundler version is available on
+ rubygems.org.
+* `error_on_stderr` (`BUNDLE_ERROR_ON_STDERR`):
+ Print Bundler errors to stderr.
+* `force_ruby_platform` (`BUNDLE_FORCE_RUBY_PLATFORM`):
+ Ignore the current machine's platform and install only `ruby` platform gems.
+ As a result, gems with native extensions will be compiled from source.
+* `frozen` (`BUNDLE_FROZEN`):
+ Disallow changes to the `Gemfile`. When the `Gemfile` is changed and the
+ lockfile has not been updated, running Bundler commands will be blocked.
+ Defaults to `true` when `--deployment` is used.
+* `gem.push_key` (`BUNDLE_GEM__PUSH_KEY`):
+ Sets the `--key` parameter for `gem push` when using the `rake release`
+ command with a private gemstash server.
+* `gemfile` (`BUNDLE_GEMFILE`):
+ The name of the file that bundler should use as the `Gemfile`. This location
+ of this file also sets the root of the project, which is used to resolve
+ relative paths in the `Gemfile`, among other things. By default, bundler
+ will search up from the current working directory until it finds a
+ `Gemfile`.
+* `global_gem_cache` (`BUNDLE_GLOBAL_GEM_CACHE`):
+ Whether Bundler should cache all gems globally, rather than locally to the
+ installing Ruby installation.
+* `global_path_appends_ruby_scope` (`BUNDLE_GLOBAL_PATH_APPENDS_RUBY_SCOPE`):
+ Whether Bundler should append the Ruby scope (e.g. engine and ABI version)
+ to a globally-configured path.
+* `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`.
+* `init_gems_rb` (`BUNDLE_INIT_GEMS_RB`)
+ Generate a `gems.rb` instead of a `Gemfile` when running `bundle init`.
+* `jobs` (`BUNDLE_JOBS`):
+ The number of gems Bundler can install in parallel. Defaults to 1.
+* `list_command` (`BUNDLE_LIST_COMMAND`)
+ Enable new list command feature
+* `major_deprecations` (`BUNDLE_MAJOR_DEPRECATIONS`):
+ Whether Bundler should print deprecation warnings for behavior that will
+ be changed in the next major version.
+* `no_install` (`BUNDLE_NO_INSTALL`):
+ Whether `bundle package` should skip installing gems.
+* `no_prune` (`BUNDLE_NO_PRUNE`):
+ Whether Bundler should leave outdated gems unpruned when caching.
+* `only_update_to_newer_versions` (`BUNDLE_ONLY_UPDATE_TO_NEWER_VERSIONS`):
+ During `bundle update`, only resolve to newer versions of the gems in the
+ lockfile.
+* `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
+ will be installed by `bundle install`. Defaults to `Gem.dir`. When --deployment
+ is used, defaults to vendor/bundle.
+* `path.system` (`BUNDLE_PATH__SYSTEM`):
+ Whether Bundler will install gems into the default system path (`Gem.dir`).
+* `path_relative_to_cwd` (`PATH_RELATIVE_TO_CWD`)
+ Makes `--path` relative to the CWD instead of the `Gemfile`.
+* `plugins` (`BUNDLE_PLUGINS`):
+ Enable Bundler's experimental plugin system.
+* `prefer_gems_rb` (`BUNDLE_PREFER_GEMS_RB`)
+ Prefer `gems.rb` to `Gemfile` when Bundler is searching for a Gemfile.
+* `print_only_version_number` (`BUNDLE_PRINT_ONLY_VERSION_NUMBER`)
+ Print only version number from `bundler --version`.
+* `redirect` (`BUNDLE_REDIRECT`):
+ The number of redirects allowed for network requests. Defaults to `5`.
+* `retry` (`BUNDLE_RETRY`):
+ The number of times to retry failed network requests. Defaults to `3`.
+* `setup_makes_kernel_gem_public` (`BUNDLE_SETUP_MAKES_KERNEL_GEM_PUBLIC`):
+ Have `Bundler.setup` make the `Kernel#gem` method public, even though
+ RubyGems declares it as private.
+* `shebang` (`BUNDLE_SHEBANG`):
+ The program name that should be invoked for generated binstubs. Defaults to
+ the ruby install name used to generate the binstub.
+* `silence_root_warning` (`BUNDLE_SILENCE_ROOT_WARNING`):
+ Silence the warning Bundler prints when installing gems as root.
+* `skip_default_git_sources` (`BUNDLE_SKIP_DEFAULT_GIT_SOURCES`):
+ Whether Bundler should skip adding default git source shortcuts to the
+ Gemfile DSL.
+* `specific_platform` (`BUNDLE_SPECIFIC_PLATFORM`):
+ Allow bundler to resolve for the specific running platform and store it in
+ the lockfile, instead of only using a generic platform.
+ A specific platform is the exact platform triple reported by
+ `Gem::Platform.local`, such as `x86_64-darwin-16` or `universal-java-1.8`.
+ On the other hand, generic platforms are those such as `ruby`, `mswin`, or
+ `java`. In this example, `x86_64-darwin-16` would map to `ruby` and
+ `universal-java-1.8` to `java`.
+* `ssl_ca_cert` (`BUNDLE_SSL_CA_CERT`):
+ Path to a designated CA certificate file or folder containing multiple
+ certificates for trusted CAs in PEM format.
+* `ssl_client_cert` (`BUNDLE_SSL_CLIENT_CERT`):
+ Path to a designated file containing a X.509 client certificate
+ and key in PEM format.
+* `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`):
+ The seconds allowed before timing out for network requests. Defaults to `10`.
+* `unlock_source_unlocks_spec` (`BUNDLE_UNLOCK_SOURCE_UNLOCKS_SPEC`):
+ Whether running `bundle update --source NAME` unlocks a gem with the given
+ name. Defaults to `true`.
+* `update_requires_all_flag` (`BUNDLE_UPDATE_REQUIRES_ALL_FLAG`)
+ Require passing `--all` to `bundle update` when everything should be updated,
+ and disallow passing no options to `bundle update`.
+* `user_agent` (`BUNDLE_USER_AGENT`):
+ The custom user agent fragment Bundler includes in API requests.
+* `with` (`BUNDLE_WITH`):
+ A `:`-separated list of groups whose gems bundler should install.
+* `without` (`BUNDLE_WITHOUT`):
+ 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.
+
+You can set them globally either via environment variables or `bundle config`,
+whichever is preferable for your setup. If you use both, environment variables
+will take preference over global settings.
+
+## LOCAL GIT REPOS
+
+Bundler also allows you to work against a git repository locally
+instead of using the remote version. This can be achieved by setting
+up a local override:
+
+ bundle config local.GEM_NAME /path/to/local/git/repository
+
+For example, in order to use a local Rack repository, a developer could call:
+
+ bundle config local.rack ~/Work/git/rack
+
+Now instead of checking out the remote git repository, the local
+override will be used. Similar to a path source, every time the local
+git repository change, changes will be automatically picked up by
+Bundler. This means a commit in the local git repo will update the
+revision in the `Gemfile.lock` to the local git repo revision. This
+requires the same attention as git submodules. Before pushing to
+the remote, you need to ensure the local override was pushed, otherwise
+you may point to a commit that only exists in your local machine.
+You'll also need to CGI escape your usernames and passwords as well.
+
+Bundler does many checks to ensure a developer won't work with
+invalid references. Particularly, we force a developer to specify
+a branch in the `Gemfile` in order to use this feature. If the branch
+specified in the `Gemfile` and the current branch in the local git
+repository do not match, Bundler will abort. This ensures that
+a developer is always working against the correct branches, and prevents
+accidental locking to a different branch.
+
+Finally, Bundler also ensures that the current revision in the
+`Gemfile.lock` exists in the local git repository. By doing this, Bundler
+forces you to fetch the latest changes in the remotes.
+
+## MIRRORS OF GEM SOURCES
+
+Bundler supports overriding gem sources with mirrors. This allows you to
+configure rubygems.org as the gem source in your Gemfile while still using your
+mirror to fetch gems.
+
+ bundle config mirror.SOURCE_URL MIRROR_URL
+
+For example, to use a mirror of rubygems.org hosted at rubygems-mirror.org:
+
+ bundle config mirror.http://rubygems.org http://rubygems-mirror.org
+
+Each mirror also provides a fallback timeout setting. If the mirror does not
+respond within the fallback timeout, Bundler will try to use the original
+server instead of the mirror.
+
+ bundle config mirror.SOURCE_URL.fallback_timeout TIMEOUT
+
+For example, to fall back to rubygems.org after 3 seconds:
+
+ bundle config mirror.https://rubygems.org.fallback_timeout 3
+
+The default fallback timeout is 0.1 seconds, but the setting can currently
+only accept whole seconds (for example, 1, 15, or 30).
+
+## CREDENTIALS FOR GEM SOURCES
+
+Bundler allows you to configure credentials for any gem source, which allows
+you to avoid putting secrets into your Gemfile.
+
+ bundle config SOURCE_HOSTNAME USERNAME:PASSWORD
+
+For example, to save the credentials of user `claudette` for the gem source at
+`gems.longerous.com`, you would run:
+
+ bundle config gems.longerous.com claudette:s00pers3krit
+
+Or you can set the credentials as an environment variable like this:
+
+ export BUNDLE_GEMS__LONGEROUS__COM="claudette:s00pers3krit"
+
+For gems with a git source with HTTP(S) URL you can specify credentials like so:
+
+ bundle config https://github.com/bundler/bundler.git username:password
+
+Or you can set the credentials as an environment variable like so:
+
+ export BUNDLE_GITHUB__COM=username:password
+
+This is especially useful for private repositories on hosts such as Github,
+where you can use personal OAuth tokens:
+
+ export BUNDLE_GITHUB__COM=abcd0123generatedtoken:x-oauth-basic
+
+
+## CONFIGURE BUNDLER DIRECTORIES
+
+Bundler's home, config, cache and plugin directories are able to be configured
+through environment variables. The default location for Bundler's home directory is
+`~/.bundle`, which all directories inherit from by default. The following
+outlines the available environment variables and their default values
+
+ BUNDLE_USER_HOME : $HOME/.bundle
+ BUNDLE_USER_CACHE : $BUNDLE_USER_HOME/cache
+ BUNDLE_USER_CONFIG : $BUNDLE_USER_HOME/config
+ BUNDLE_USER_PLUGIN : $BUNDLE_USER_HOME/plugin
+
diff --git a/man/bundle-doctor.1 b/man/bundle-doctor.1
new file mode 100644
index 0000000000..53291ba7b9
--- /dev/null
+++ b/man/bundle-doctor.1
@@ -0,0 +1,44 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-DOCTOR" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-doctor\fR \- Checks the bundle for common problems
+.
+.SH "SYNOPSIS"
+\fBbundle doctor\fR [\-\-quiet] [\-\-gemfile=GEMFILE]
+.
+.SH "DESCRIPTION"
+Checks your Gemfile and gem environment for common problems\. If issues are detected, Bundler prints them and exits status 1\. Otherwise, Bundler prints a success message and exits status 0\.
+.
+.P
+Examples of common problems caught by bundle\-doctor include:
+.
+.IP "\(bu" 4
+Invalid Bundler settings
+.
+.IP "\(bu" 4
+Mismatched Ruby versions
+.
+.IP "\(bu" 4
+Mismatched platforms
+.
+.IP "\(bu" 4
+Uninstalled gems
+.
+.IP "\(bu" 4
+Missing dependencies
+.
+.IP "" 0
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-quiet\fR
+Only output warnings and errors\.
+.
+.TP
+\fB\-\-gemfile=<gemfile>\fR
+The location of the Gemfile(5) which Bundler should use\. This defaults to a Gemfile(5) in the current working directory\. In general, Bundler will assume that the location of the Gemfile(5) is also the project\'s root and will try to find \fBGemfile\.lock\fR and \fBvendor/cache\fR relative to this location\.
+
diff --git a/man/bundle-doctor.1.txt b/man/bundle-doctor.1.txt
new file mode 100644
index 0000000000..b21291ac0f
--- /dev/null
+++ b/man/bundle-doctor.1.txt
@@ -0,0 +1,44 @@
+BUNDLE-DOCTOR(1) BUNDLE-DOCTOR(1)
+
+
+
+1mNAME0m
+ 1mbundle-doctor 22m- Checks the bundle for common problems
+
+1mSYNOPSIS0m
+ 1mbundle doctor 22m[--quiet] [--gemfile=GEMFILE]
+
+1mDESCRIPTION0m
+ Checks your Gemfile and gem environment for common problems. If issues
+ are detected, Bundler prints them and exits status 1. Otherwise,
+ Bundler prints a success message and exits status 0.
+
+ Examples of common problems caught by bundle-doctor include:
+
+ o Invalid Bundler settings
+
+ o Mismatched Ruby versions
+
+ o Mismatched platforms
+
+ o Uninstalled gems
+
+ o Missing dependencies
+
+
+
+1mOPTIONS0m
+ 1m--quiet0m
+ Only output warnings and errors.
+
+ 1m--gemfile=<gemfile>0m
+ The location of the Gemfile(5) which Bundler should use. This
+ defaults to a Gemfile(5) in the current working directory. In
+ general, Bundler will assume that the location of the Gemfile(5)
+ is also the project's root and will try to find 1mGemfile.lock 22mand
+ 1mvendor/cache 22mrelative to this location.
+
+
+
+
+ November 2018 BUNDLE-DOCTOR(1)
diff --git a/man/bundle-doctor.ronn b/man/bundle-doctor.ronn
new file mode 100644
index 0000000000..271ee800ad
--- /dev/null
+++ b/man/bundle-doctor.ronn
@@ -0,0 +1,33 @@
+bundle-doctor(1) -- Checks the bundle for common problems
+=========================================================
+
+## SYNOPSIS
+
+`bundle doctor` [--quiet]
+ [--gemfile=GEMFILE]
+
+## DESCRIPTION
+
+Checks your Gemfile and gem environment for common problems. If issues
+are detected, Bundler prints them and exits status 1. Otherwise,
+Bundler prints a success message and exits status 0.
+
+Examples of common problems caught by bundle-doctor include:
+
+* Invalid Bundler settings
+* Mismatched Ruby versions
+* Mismatched platforms
+* Uninstalled gems
+* Missing dependencies
+
+## OPTIONS
+
+* `--quiet`:
+ Only output warnings and errors.
+
+* `--gemfile=<gemfile>`:
+ The location of the Gemfile(5) which Bundler should use. This defaults
+ to a Gemfile(5) in the current working directory. In general, Bundler
+ will assume that the location of the Gemfile(5) is also the project's
+ root and will try to find `Gemfile.lock` and `vendor/cache` relative
+ to this location.
diff --git a/man/bundle-exec.1 b/man/bundle-exec.1
new file mode 100644
index 0000000000..e742141769
--- /dev/null
+++ b/man/bundle-exec.1
@@ -0,0 +1,165 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-EXEC" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-exec\fR \- Execute a command in the context of the bundle
+.
+.SH "SYNOPSIS"
+\fBbundle exec\fR [\-\-keep\-file\-descriptors] \fIcommand\fR
+.
+.SH "DESCRIPTION"
+This command executes the command, making all gems specified in the [\fBGemfile(5)\fR][Gemfile(5)] available to \fBrequire\fR in Ruby programs\.
+.
+.P
+Essentially, if you would normally have run something like \fBrspec spec/my_spec\.rb\fR, and you want to use the gems specified in the [\fBGemfile(5)\fR][Gemfile(5)] and installed via bundle install(1) \fIbundle\-install\.1\.html\fR, you should run \fBbundle exec rspec spec/my_spec\.rb\fR\.
+.
+.P
+Note that \fBbundle exec\fR does not require that an executable is available on your shell\'s \fB$PATH\fR\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-keep\-file\-descriptors\fR
+Exec in Ruby 2\.0 began discarding non\-standard file descriptors\. When this flag is passed, exec will revert to the 1\.9 behaviour of passing all file descriptors to the new process\.
+.
+.SH "BUNDLE INSTALL \-\-BINSTUBS"
+If you use the \fB\-\-binstubs\fR flag in bundle install(1) \fIbundle\-install\.1\.html\fR, Bundler will automatically create a directory (which defaults to \fBapp_root/bin\fR) containing all of the executables available from gems in the bundle\.
+.
+.P
+After using \fB\-\-binstubs\fR, \fBbin/rspec spec/my_spec\.rb\fR is identical to \fBbundle exec rspec spec/my_spec\.rb\fR\.
+.
+.SH "ENVIRONMENT MODIFICATIONS"
+\fBbundle exec\fR makes a number of changes to the shell environment, then executes the command you specify in full\.
+.
+.IP "\(bu" 4
+make sure that it\'s still possible to shell out to \fBbundle\fR from inside a command invoked by \fBbundle exec\fR (using \fB$BUNDLE_BIN_PATH\fR)
+.
+.IP "\(bu" 4
+put the directory containing executables (like \fBrails\fR, \fBrspec\fR, \fBrackup\fR) for your bundle on \fB$PATH\fR
+.
+.IP "\(bu" 4
+make sure that if bundler is invoked in the subshell, it uses the same \fBGemfile\fR (by setting \fBBUNDLE_GEMFILE\fR)
+.
+.IP "\(bu" 4
+add \fB\-rbundler/setup\fR to \fB$RUBYOPT\fR, which makes sure that Ruby programs invoked in the subshell can see the gems in the bundle
+.
+.IP "" 0
+.
+.P
+It also modifies Rubygems:
+.
+.IP "\(bu" 4
+disallow loading additional gems not in the bundle
+.
+.IP "\(bu" 4
+modify the \fBgem\fR method to be a no\-op if a gem matching the requirements is in the bundle, and to raise a \fBGem::LoadError\fR if it\'s not
+.
+.IP "\(bu" 4
+Define \fBGem\.refresh\fR to be a no\-op, since the source index is always frozen when using bundler, and to prevent gems from the system leaking into the environment
+.
+.IP "\(bu" 4
+Override \fBGem\.bin_path\fR to use the gems in the bundle, making system executables work
+.
+.IP "\(bu" 4
+Add all gems in the bundle into Gem\.loaded_specs
+.
+.IP "" 0
+.
+.P
+Finally, \fBbundle exec\fR also implicitly modifies \fBGemfile\.lock\fR if the lockfile and the Gemfile do not match\. Bundler needs the Gemfile to determine things such as a gem\'s groups, \fBautorequire\fR, and platforms, etc\., and that information isn\'t stored in the lockfile\. The Gemfile and lockfile must be synced in order to \fBbundle exec\fR successfully, so \fBbundle exec\fR updates the lockfile beforehand\.
+.
+.SS "Loading"
+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:
+.
+.IP "" 4
+.
+.nf
+
+Bundler\.with_clean_env do
+ `brew install wget`
+end
+.
+.fi
+.
+.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\.
+.
+.IP "" 4
+.
+.nf
+
+Bundler\.with_clean_env do
+ Dir\.chdir "/other/bundler/project" do
+ `bundle exec \./script`
+ end
+end
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Bundler provides convenience helpers that wrap \fBsystem\fR and \fBexec\fR, and they can be used like this:
+.
+.IP "" 4
+.
+.nf
+
+Bundler\.clean_system(\'brew install wget\')
+Bundler\.clean_exec(\'brew install wget\')
+.
+.fi
+.
+.IP "" 0
+.
+.SH "RUBYGEMS PLUGINS"
+At present, the Rubygems plugin system requires all files named \fBrubygems_plugin\.rb\fR on the load path of \fIany\fR installed gem when any Ruby code requires \fBrubygems\.rb\fR\. This includes executables installed into the system, like \fBrails\fR, \fBrackup\fR, and \fBrspec\fR\.
+.
+.P
+Since Rubygems plugins can contain arbitrary Ruby code, they commonly end up activating themselves or their dependencies\.
+.
+.P
+For instance, the \fBgemcutter 0\.5\fR gem depended on \fBjson_pure\fR\. If you had that version of gemcutter installed (even if you \fIalso\fR had a newer version without this problem), Rubygems would activate \fBgemcutter 0\.5\fR and \fBjson_pure <latest>\fR\.
+.
+.P
+If your Gemfile(5) also contained \fBjson_pure\fR (or a gem with a dependency on \fBjson_pure\fR), the latest version on your system might conflict with the version in your Gemfile(5), or the snapshot version in your \fBGemfile\.lock\fR\.
+.
+.P
+If this happens, bundler will say:
+.
+.IP "" 4
+.
+.nf
+
+You have already activated json_pure 1\.4\.6 but your Gemfile
+requires json_pure 1\.4\.3\. Consider using bundle exec\.
+.
+.fi
+.
+.IP "" 0
+.
+.P
+In this situation, you almost certainly want to remove the underlying gem with the problematic gem plugin\. In general, the authors of these plugins (in this case, the \fBgemcutter\fR gem) have released newer versions that are more careful in their plugins\.
+.
+.P
+You can find a list of all the gems containing gem plugins by running
+.
+.IP "" 4
+.
+.nf
+
+ruby \-rubygems \-e "puts Gem\.find_files(\'rubygems_plugin\.rb\')"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+At the very least, you should remove all but the newest version of each gem plugin, and also remove all gem plugins that you aren\'t using (\fBgem uninstall gem_name\fR)\.
diff --git a/man/bundle-exec.1.txt b/man/bundle-exec.1.txt
new file mode 100644
index 0000000000..fa55d2a0c2
--- /dev/null
+++ b/man/bundle-exec.1.txt
@@ -0,0 +1,178 @@
+BUNDLE-EXEC(1) BUNDLE-EXEC(1)
+
+
+
+1mNAME0m
+ 1mbundle-exec 22m- Execute a command in the context of the bundle
+
+1mSYNOPSIS0m
+ 1mbundle exec 22m[--keep-file-descriptors] 4mcommand0m
+
+1mDESCRIPTION0m
+ This command executes the command, making all gems specified in the
+ [1mGemfile(5)22m][Gemfile(5)] available to 1mrequire 22min Ruby programs.
+
+ Essentially, if you would normally have run something like 1mrspec0m
+ 1mspec/my_spec.rb22m, and you want to use the gems specified in the [1mGem-0m
+ 1mfile(5)22m][Gemfile(5)] and installed via bundle install(1) 4mbun-0m
+ 4mdle-install.1.html24m, you should run 1mbundle exec rspec spec/my_spec.rb22m.
+
+ Note that 1mbundle exec 22mdoes not require that an executable is available
+ on your shell's 1m$PATH22m.
+
+1mOPTIONS0m
+ 1m--keep-file-descriptors0m
+ Exec in Ruby 2.0 began discarding non-standard file descriptors.
+ When this flag is passed, exec will revert to the 1.9 behaviour
+ of passing all file descriptors to the new process.
+
+1mBUNDLE INSTALL --BINSTUBS0m
+ If you use the 1m--binstubs 22mflag in bundle install(1) 4mbun-0m
+ 4mdle-install.1.html24m, Bundler will automatically create a directory
+ (which defaults to 1mapp_root/bin22m) containing all of the executables
+ available from gems in the bundle.
+
+ After using 1m--binstubs22m, 1mbin/rspec spec/my_spec.rb 22mis identical to 1mbun-0m
+ 1mdle exec rspec spec/my_spec.rb22m.
+
+1mENVIRONMENT MODIFICATIONS0m
+ 1mbundle exec 22mmakes a number of changes to the shell environment, then
+ executes the command you specify in full.
+
+ o make sure that it's still possible to shell out to 1mbundle 22mfrom
+ inside a command invoked by 1mbundle exec 22m(using 1m$BUNDLE_BIN_PATH22m)
+
+ o put the directory containing executables (like 1mrails22m, 1mrspec22m,
+ 1mrackup22m) for your bundle on 1m$PATH0m
+
+ o make sure that if bundler is invoked in the subshell, it uses the
+ same 1mGemfile 22m(by setting 1mBUNDLE_GEMFILE22m)
+
+ o add 1m-rbundler/setup 22mto 1m$RUBYOPT22m, which makes sure that Ruby pro-
+ grams invoked in the subshell can see the gems in the bundle
+
+
+
+ It also modifies Rubygems:
+
+ o disallow loading additional gems not in the bundle
+
+ o modify the 1mgem 22mmethod to be a no-op if a gem matching the require-
+ ments is in the bundle, and to raise a 1mGem::LoadError 22mif it's not
+
+ o Define 1mGem.refresh 22mto be a no-op, since the source index is always
+ frozen when using bundler, and to prevent gems from the system
+ leaking into the environment
+
+ o Override 1mGem.bin_path 22mto use the gems in the bundle, making system
+ executables work
+
+ o Add all gems in the bundle into Gem.loaded_specs
+
+
+
+ Finally, 1mbundle exec 22malso implicitly modifies 1mGemfile.lock 22mif the lock-
+ file and the Gemfile do not match. Bundler needs the Gemfile to deter-
+ mine things such as a gem's groups, 1mautorequire22m, and platforms, etc.,
+ and that information isn't stored in the lockfile. The Gemfile and
+ lockfile must be synced in order to 1mbundle exec 22msuccessfully, so 1mbundle0m
+ 1mexec 22mupdates the lockfile beforehand.
+
+ 1mLoading0m
+ By default, when attempting to 1mbundle exec 22mto a file with a ruby she-
+ bang, Bundler will 1mKernel.load 22mthat file instead of using 1mKernel.exec22m.
+ 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 1m$0 22mor 1m__FILE__22m) and the optimiza-
+ tion can be disabled by enabling the 1mdisable_exec_load 22msetting.
+
+ 1mShelling out0m
+ Any Ruby code that opens a subshell (like 1msystem22m, backticks, or 1m%x{}22m)
+ 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 1mwith_clean_env 22mmethod 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
+ `brew install wget`
+ end
+
+
+
+ Using 1mwith_clean_env 22mis 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 1mwith_clean_env22m.
+
+
+
+ Bundler.with_clean_env do
+ Dir.chdir "/other/bundler/project" do
+ `bundle exec ./script`
+ end
+ end
+
+
+
+ Bundler provides convenience helpers that wrap 1msystem 22mand 1mexec22m, and
+ they can be used like this:
+
+
+
+ Bundler.clean_system('brew install wget')
+ Bundler.clean_exec('brew install wget')
+
+
+
+1mRUBYGEMS PLUGINS0m
+ At present, the Rubygems plugin system requires all files named
+ 1mrubygems_plugin.rb 22mon the load path of 4many24m installed gem when any Ruby
+ code requires 1mrubygems.rb22m. This includes executables installed into the
+ system, like 1mrails22m, 1mrackup22m, and 1mrspec22m.
+
+ Since Rubygems plugins can contain arbitrary Ruby code, they commonly
+ end up activating themselves or their dependencies.
+
+ For instance, the 1mgemcutter 0.5 22mgem depended on 1mjson_pure22m. If you had
+ that version of gemcutter installed (even if you 4malso24m had a newer ver-
+ sion without this problem), Rubygems would activate 1mgemcutter 0.5 22mand
+ 1mjson_pure <latest>22m.
+
+ If your Gemfile(5) also contained 1mjson_pure 22m(or a gem with a dependency
+ on 1mjson_pure22m), the latest version on your system might conflict with
+ the version in your Gemfile(5), or the snapshot version in your 1mGem-0m
+ 1mfile.lock22m.
+
+ If this happens, bundler will say:
+
+
+
+ You have already activated json_pure 1.4.6 but your Gemfile
+ requires json_pure 1.4.3. Consider using bundle exec.
+
+
+
+ In this situation, you almost certainly want to remove the underlying
+ gem with the problematic gem plugin. In general, the authors of these
+ plugins (in this case, the 1mgemcutter 22mgem) have released newer versions
+ that are more careful in their plugins.
+
+ You can find a list of all the gems containing gem plugins by running
+
+
+
+ ruby -rubygems -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 that you aren't using (1mgem0m
+ 1muninstall gem_name22m).
+
+
+
+ November 2018 BUNDLE-EXEC(1)
diff --git a/man/bundle-exec.ronn b/man/bundle-exec.ronn
new file mode 100644
index 0000000000..aa680f4c5d
--- /dev/null
+++ b/man/bundle-exec.ronn
@@ -0,0 +1,152 @@
+bundle-exec(1) -- Execute a command in the context of the bundle
+================================================================
+
+## SYNOPSIS
+
+`bundle exec` [--keep-file-descriptors] <command>
+
+## DESCRIPTION
+
+This command executes the command, making all gems specified in the
+[`Gemfile(5)`][Gemfile(5)] available to `require` in Ruby programs.
+
+Essentially, if you would normally have run something like
+`rspec spec/my_spec.rb`, and you want to use the gems specified
+in the [`Gemfile(5)`][Gemfile(5)] and installed via [bundle install(1)](bundle-install.1.html), you
+should run `bundle exec rspec spec/my_spec.rb`.
+
+Note that `bundle exec` does not require that an executable is
+available on your shell's `$PATH`.
+
+## OPTIONS
+
+* `--keep-file-descriptors`:
+ Exec in Ruby 2.0 began discarding non-standard file descriptors. When this
+ flag is passed, exec will revert to the 1.9 behaviour of passing all file
+ descriptors to the new process.
+
+## BUNDLE INSTALL --BINSTUBS
+
+If you use the `--binstubs` flag in [bundle install(1)](bundle-install.1.html), Bundler will
+automatically create a directory (which defaults to `app_root/bin`)
+containing all of the executables available from gems in the bundle.
+
+After using `--binstubs`, `bin/rspec spec/my_spec.rb` is identical
+to `bundle exec rspec spec/my_spec.rb`.
+
+## ENVIRONMENT MODIFICATIONS
+
+`bundle exec` makes a number of changes to the shell environment,
+then executes the command you specify in full.
+
+* make sure that it's still possible to shell out to `bundle`
+ from inside a command invoked by `bundle exec` (using
+ `$BUNDLE_BIN_PATH`)
+* put the directory containing executables (like `rails`, `rspec`,
+ `rackup`) for your bundle on `$PATH`
+* make sure that if bundler is invoked in the subshell, it uses
+ the same `Gemfile` (by setting `BUNDLE_GEMFILE`)
+* add `-rbundler/setup` to `$RUBYOPT`, which makes sure that
+ Ruby programs invoked in the subshell can see the gems in
+ the bundle
+
+It also modifies Rubygems:
+
+* disallow loading additional gems not in the bundle
+* modify the `gem` method to be a no-op if a gem matching
+ the requirements is in the bundle, and to raise a
+ `Gem::LoadError` if it's not
+* Define `Gem.refresh` to be a no-op, since the source
+ index is always frozen when using bundler, and to
+ prevent gems from the system leaking into the environment
+* Override `Gem.bin_path` to use the gems in the bundle,
+ making system executables work
+* Add all gems in the bundle into Gem.loaded_specs
+
+Finally, `bundle exec` also implicitly modifies `Gemfile.lock` if the lockfile
+and the Gemfile do not match. Bundler needs the Gemfile to determine things
+such as a gem's groups, `autorequire`, and platforms, etc., and that
+information isn't stored in the lockfile. The Gemfile and lockfile must be
+synced in order to `bundle exec` successfully, so `bundle exec`
+updates the lockfile beforehand.
+
+### Loading
+
+By default, when attempting to `bundle exec` to a file with a ruby shebang,
+Bundler will `Kernel.load` that file instead of using `Kernel.exec`. 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 `$0` or `__FILE__`) and the optimization can be disabled by enabling
+the `disable_exec_load` setting.
+
+### Shelling out
+
+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
+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
+ `brew install wget`
+ end
+
+Using `with_clean_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`.
+
+ Bundler.with_clean_env do
+ Dir.chdir "/other/bundler/project" do
+ `bundle exec ./script`
+ end
+ end
+
+Bundler provides convenience helpers that wrap `system` and `exec`, and they
+can be used like this:
+
+ Bundler.clean_system('brew install wget')
+ Bundler.clean_exec('brew install wget')
+
+
+## RUBYGEMS PLUGINS
+
+At present, the Rubygems plugin system requires all files
+named `rubygems_plugin.rb` on the load path of _any_ installed
+gem when any Ruby code requires `rubygems.rb`. This includes
+executables installed into the system, like `rails`, `rackup`,
+and `rspec`.
+
+Since Rubygems plugins can contain arbitrary Ruby code, they
+commonly end up activating themselves or their dependencies.
+
+For instance, the `gemcutter 0.5` gem depended on `json_pure`.
+If you had that version of gemcutter installed (even if
+you _also_ had a newer version without this problem), Rubygems
+would activate `gemcutter 0.5` and `json_pure <latest>`.
+
+If your Gemfile(5) also contained `json_pure` (or a gem
+with a dependency on `json_pure`), the latest version on
+your system might conflict with the version in your
+Gemfile(5), or the snapshot version in your `Gemfile.lock`.
+
+If this happens, bundler will say:
+
+ You have already activated json_pure 1.4.6 but your Gemfile
+ requires json_pure 1.4.3. Consider using bundle exec.
+
+In this situation, you almost certainly want to remove the
+underlying gem with the problematic gem plugin. In general,
+the authors of these plugins (in this case, the `gemcutter`
+gem) have released newer versions that are more careful in
+their plugins.
+
+You can find a list of all the gems containing gem plugins
+by running
+
+ ruby -rubygems -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
+that you aren't using (`gem uninstall gem_name`).
diff --git a/man/bundle-gem.1 b/man/bundle-gem.1
new file mode 100644
index 0000000000..1087a0888c
--- /dev/null
+++ b/man/bundle-gem.1
@@ -0,0 +1,80 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-GEM" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-gem\fR \- Generate a project skeleton for creating a rubygem
+.
+.SH "SYNOPSIS"
+\fBbundle gem\fR \fIGEM_NAME\fR \fIOPTIONS\fR
+.
+.SH "DESCRIPTION"
+Generates a directory named \fBGEM_NAME\fR with a \fBRakefile\fR, \fBGEM_NAME\.gemspec\fR, and other supporting files and directories that can be used to develop a rubygem with that name\.
+.
+.P
+Run \fBrake \-T\fR in the resulting project for a list of Rake tasks that can be used to test and publish the gem to rubygems\.org\.
+.
+.P
+The generated project skeleton can be customized with OPTIONS, as explained below\. Note that these options can also be specified via Bundler\'s global configuration file using the following names:
+.
+.IP "\(bu" 4
+\fBgem\.coc\fR
+.
+.IP "\(bu" 4
+\fBgem\.mit\fR
+.
+.IP "\(bu" 4
+\fBgem\.test\fR
+.
+.IP "" 0
+.
+.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\.
+.
+.TP
+\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\.
+.
+.TP
+\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\.
+.
+.TP
+\fB\-\-no\-ext\fR
+Do not add C 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\.
+.
+.TP
+\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
+Specify the test framework that Bundler should use when generating the project\. Acceptable values are \fBminitest\fR and \fBrspec\fR\. The \fBGEM_NAME\.gemspec\fR will be configured and a skeleton test/spec directory will be created based on this option\. 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\. If no option is specified, the default testing framework is RSpec\.
+.
+.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\.
+.
+.SH "SEE ALSO"
+.
+.IP "\(bu" 4
+bundle config(1) \fIbundle\-config\.1\.html\fR
+.
+.IP "" 0
+
diff --git a/man/bundle-gem.1.txt b/man/bundle-gem.1.txt
new file mode 100644
index 0000000000..59e5e0ddcc
--- /dev/null
+++ b/man/bundle-gem.1.txt
@@ -0,0 +1,91 @@
+BUNDLE-GEM(1) BUNDLE-GEM(1)
+
+
+
+1mNAME0m
+ 1mbundle-gem 22m- Generate a project skeleton for creating a rubygem
+
+1mSYNOPSIS0m
+ 1mbundle gem 4m22mGEM_NAME24m 4mOPTIONS0m
+
+1mDESCRIPTION0m
+ Generates a directory named 1mGEM_NAME 22mwith a 1mRakefile22m, 1mGEM_NAME.gemspec22m,
+ and other supporting files and directories that can be used to develop
+ a rubygem with that name.
+
+ Run 1mrake -T 22min the resulting project for a list of Rake tasks that can
+ be used to test and publish the gem to rubygems.org.
+
+ The generated project skeleton can be customized with OPTIONS, as
+ explained below. Note that these options can also be specified via
+ Bundler's global configuration file using the following names:
+
+ o 1mgem.coc0m
+
+ o 1mgem.mit0m
+
+ o 1mgem.test0m
+
+
+
+1mOPTIONS0m
+ 1m--exe 22mor 1m-b 22mor 1m--bin0m
+ Specify that Bundler should create a binary executable (as
+ 1mexe/GEM_NAME22m) in the generated rubygem project. This binary will
+ also be added to the 1mGEM_NAME.gemspec 22mmanifest. This behavior is
+ disabled by default.
+
+ 1m--no-exe0m
+ Do not create a binary (overrides 1m--exe 22mspecified in the global
+ config).
+
+ 1m--coc 22mAdd a 1mCODE_OF_CONDUCT.md 22mfile 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 1mbundle gem 22muse.
+
+ 1m--no-coc0m
+ Do not create a 1mCODE_OF_CONDUCT.md 22m(overrides 1m--coc 22mspecified in
+ the global config).
+
+ 1m--ext 22mAdd boilerplate for C extension code to the generated project.
+ This behavior is disabled by default.
+
+ 1m--no-ext0m
+ Do not add C extension code (overrides 1m--ext 22mspecified in the
+ global config).
+
+ 1m--mit 22mAdd an MIT license to a 1mLICENSE.txt 22mfile in the root of the gen-
+ erated 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 1mbundle gem 22muse.
+
+ 1m--no-mit0m
+ Do not create a 1mLICENSE.txt 22m(overrides 1m--mit 22mspecified in the
+ global config).
+
+ 1m-t22m, 1m--test=minitest22m, 1m--test=rspec0m
+ Specify the test framework that Bundler should use when generat-
+ ing the project. Acceptable values are 1mminitest 22mand 1mrspec22m. The
+ 1mGEM_NAME.gemspec 22mwill be configured and a skeleton test/spec
+ directory will be created based on this option. If this option
+ is unspecified, an interactive prompt will be displayed and the
+ answer will be saved in Bundler's global config for future 1mbun-0m
+ 1mdle gem 22muse. If no option is specified, the default testing
+ framework is RSpec.
+
+ 1m-e22m, 1m--edit[=EDITOR]0m
+ Open the resulting GEM_NAME.gemspec in EDITOR, or the default
+ editor if not specified. The default is 1m$BUNDLER_EDITOR22m, 1m$VIS-0m
+ 1mUAL22m, or 1m$EDITOR22m.
+
+1mSEE ALSO0m
+ o bundle config(1) 4mbundle-config.1.html0m
+
+
+
+
+
+
+ November 2018 BUNDLE-GEM(1)
diff --git a/man/bundle-gem.ronn b/man/bundle-gem.ronn
new file mode 100644
index 0000000000..cf3d037df2
--- /dev/null
+++ b/man/bundle-gem.ronn
@@ -0,0 +1,78 @@
+bundle-gem(1) -- Generate a project skeleton for creating a rubygem
+====================================================================
+
+## SYNOPSIS
+
+`bundle gem` <GEM_NAME> [OPTIONS]
+
+## DESCRIPTION
+
+Generates a directory named `GEM_NAME` with a `Rakefile`, `GEM_NAME.gemspec`,
+and other supporting files and directories that can be used to develop a
+rubygem with that name.
+
+Run `rake -T` in the resulting project for a list of Rake tasks that can be used
+to test and publish the gem to rubygems.org.
+
+The generated project skeleton can be customized with OPTIONS, as explained
+below. Note that these options can also be specified via Bundler's global
+configuration file using the following names:
+
+* `gem.coc`
+* `gem.mit`
+* `gem.test`
+
+## OPTIONS
+
+* `--exe` or `-b` or `--bin`:
+ Specify that Bundler should create a binary executable (as `exe/GEM_NAME`)
+ in the generated rubygem project. This binary will also be added to the
+ `GEM_NAME.gemspec` manifest. This behavior is disabled by default.
+
+* `--no-exe`:
+ Do not create a binary (overrides `--exe` specified in the global config).
+
+* `--coc`:
+ Add a `CODE_OF_CONDUCT.md` 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 `bundle gem` use.
+
+* `--no-coc`:
+ 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
+ is disabled by default.
+
+* `--no-ext`:
+ Do not add C extension code (overrides `--ext` specified in the global
+ config).
+
+* `--mit`:
+ Add an MIT license to a `LICENSE.txt` 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
+ `bundle gem` use.
+
+* `--no-mit`:
+ Do not create a `LICENSE.txt` (overrides `--mit` specified in the global
+ config).
+
+* `-t`, `--test=minitest`, `--test=rspec`:
+ Specify the test framework that Bundler should use when generating the
+ project. Acceptable values are `minitest` and `rspec`. The `GEM_NAME.gemspec`
+ will be configured and a skeleton test/spec directory will be created based
+ on this option. If this option is unspecified, an interactive prompt will be
+ displayed and the answer will be saved in Bundler's global config for future
+ `bundle gem` use.
+ If no option is specified, the default testing framework is RSpec.
+
+* `-e`, `--edit[=EDITOR]`:
+ Open the resulting GEM_NAME.gemspec in EDITOR, or the default editor if not
+ specified. The default is `$BUNDLER_EDITOR`, `$VISUAL`, or `$EDITOR`.
+
+## SEE ALSO
+
+* [bundle config(1)](bundle-config.1.html)
diff --git a/man/bundle-info.1 b/man/bundle-info.1
new file mode 100644
index 0000000000..9140d27c73
--- /dev/null
+++ b/man/bundle-info.1
@@ -0,0 +1,20 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-INFO" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-info\fR \- Show information for the given gem in your bundle
+.
+.SH "SYNOPSIS"
+\fBbundle info\fR [GEM] [\-\-path]
+.
+.SH "DESCRIPTION"
+Print the basic information about the provided GEM such as homepage, version, path and summary\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-path\fR
+Print the path of the given gem
+
diff --git a/man/bundle-info.1.txt b/man/bundle-info.1.txt
new file mode 100644
index 0000000000..4d328416e7
--- /dev/null
+++ b/man/bundle-info.1.txt
@@ -0,0 +1,21 @@
+BUNDLE-INFO(1) BUNDLE-INFO(1)
+
+
+
+1mNAME0m
+ 1mbundle-info 22m- Show information for the given gem in your bundle
+
+1mSYNOPSIS0m
+ 1mbundle info 22m[GEM] [--path]
+
+1mDESCRIPTION0m
+ Print the basic information about the provided GEM such as homepage,
+ version, path and summary.
+
+1mOPTIONS0m
+ 1m--path 22mPrint the path of the given gem
+
+
+
+
+ November 2018 BUNDLE-INFO(1)
diff --git a/man/bundle-info.ronn b/man/bundle-info.ronn
new file mode 100644
index 0000000000..47e457aa3c
--- /dev/null
+++ b/man/bundle-info.ronn
@@ -0,0 +1,17 @@
+bundle-info(1) -- Show information for the given gem in your bundle
+=========================================================================
+
+## SYNOPSIS
+
+`bundle info` [GEM]
+ [--path]
+
+## DESCRIPTION
+
+Print the basic information about the provided GEM such as homepage, version,
+path and summary.
+
+## OPTIONS
+
+* `--path`:
+Print the path of the given gem
diff --git a/man/bundle-init.1 b/man/bundle-init.1
new file mode 100644
index 0000000000..70519fda39
--- /dev/null
+++ b/man/bundle-init.1
@@ -0,0 +1,25 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-INIT" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-init\fR \- Generates a Gemfile into the current working directory
+.
+.SH "SYNOPSIS"
+\fBbundle init\fR [\-\-gemspec=FILE]
+.
+.SH "DESCRIPTION"
+Init generates a default [\fBGemfile(5)\fR][Gemfile(5)] in the current working directory\. When adding a [\fBGemfile(5)\fR][Gemfile(5)] to a gem with a gemspec, the \fB\-\-gemspec\fR option will automatically add each dependency listed in the gemspec file to the newly created [\fBGemfile(5)\fR][Gemfile(5)]\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-gemspec\fR
+Use the specified \.gemspec to create the [\fBGemfile(5)\fR][Gemfile(5)]
+.
+.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\.
+.
+.SH "SEE ALSO"
+Gemfile(5) \fIhttp://bundler\.io/man/gemfile\.5\.html\fR
diff --git a/man/bundle-init.1.txt b/man/bundle-init.1.txt
new file mode 100644
index 0000000000..eb6091bab2
--- /dev/null
+++ b/man/bundle-init.1.txt
@@ -0,0 +1,34 @@
+BUNDLE-INIT(1) BUNDLE-INIT(1)
+
+
+
+1mNAME0m
+ 1mbundle-init 22m- Generates a Gemfile into the current working directory
+
+1mSYNOPSIS0m
+ 1mbundle init 22m[--gemspec=FILE]
+
+1mDESCRIPTION0m
+ Init generates a default [1mGemfile(5)22m][Gemfile(5)] in the current work-
+ ing directory. When adding a [1mGemfile(5)22m][Gemfile(5)] to a gem with a
+ gemspec, the 1m--gemspec 22moption will automatically add each dependency
+ listed in the gemspec file to the newly created [1mGemfile(5)22m][Gem-
+ file(5)].
+
+1mOPTIONS0m
+ 1m--gemspec0m
+ Use the specified .gemspec to create the [1mGemfile(5)22m][Gem-
+ file(5)]
+
+1mFILES0m
+ Included in the default [1mGemfile(5)22m][Gemfile(5)] generated is the line
+ 1m# frozen_string_literal: true22m. 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.
+
+1mSEE ALSO0m
+ Gemfile(5) 4mhttp://bundler.io/man/gemfile.5.html0m
+
+
+
+ November 2018 BUNDLE-INIT(1)
diff --git a/man/bundle-init.ronn b/man/bundle-init.ronn
new file mode 100644
index 0000000000..7504af7bab
--- /dev/null
+++ b/man/bundle-init.ronn
@@ -0,0 +1,29 @@
+bundle-init(1) -- Generates a Gemfile into the current working directory
+========================================================================
+
+## SYNOPSIS
+
+`bundle init` [--gemspec=FILE]
+
+## DESCRIPTION
+
+Init generates a default [`Gemfile(5)`][Gemfile(5)] in the current working directory. When
+adding a [`Gemfile(5)`][Gemfile(5)] to a gem with a gemspec, the `--gemspec` option will
+automatically add each dependency listed in the gemspec file to the newly
+created [`Gemfile(5)`][Gemfile(5)].
+
+## OPTIONS
+
+* `--gemspec`:
+ Use the specified .gemspec to create the [`Gemfile(5)`][Gemfile(5)]
+
+## FILES
+
+Included in the default [`Gemfile(5)`][Gemfile(5)]
+generated is the line `# frozen_string_literal: true`. 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.
+
+## SEE ALSO
+
+[Gemfile(5)](http://bundler.io/man/gemfile.5.html)
diff --git a/man/bundle-inject.1 b/man/bundle-inject.1
new file mode 100644
index 0000000000..4f663e1a85
--- /dev/null
+++ b/man/bundle-inject.1
@@ -0,0 +1,33 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-INJECT" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-inject\fR \- Add named gem(s) with version requirements to Gemfile
+.
+.SH "SYNOPSIS"
+\fBbundle inject\fR [GEM] [VERSION]
+.
+.SH "DESCRIPTION"
+Adds the named gem(s) with their version requirements to the resolved [\fBGemfile(5)\fR][Gemfile(5)]\.
+.
+.P
+This command will add the gem to both your [\fBGemfile(5)\fR][Gemfile(5)] and Gemfile\.lock if it isn\'t listed yet\.
+.
+.P
+Example:
+.
+.IP "" 4
+.
+.nf
+
+bundle install
+bundle inject \'rack\' \'> 0\'
+.
+.fi
+.
+.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
diff --git a/man/bundle-inject.1.txt b/man/bundle-inject.1.txt
new file mode 100644
index 0000000000..0011dea0c1
--- /dev/null
+++ b/man/bundle-inject.1.txt
@@ -0,0 +1,32 @@
+BUNDLE-INJECT(1) BUNDLE-INJECT(1)
+
+
+
+1mNAME0m
+ 1mbundle-inject 22m- Add named gem(s) with version requirements to Gemfile
+
+1mSYNOPSIS0m
+ 1mbundle inject 22m[GEM] [VERSION]
+
+1mDESCRIPTION0m
+ Adds the named gem(s) with their version requirements to the resolved
+ [1mGemfile(5)22m][Gemfile(5)].
+
+ This command will add the gem to both your [1mGemfile(5)22m][Gemfile(5)] and
+ Gemfile.lock if it isn't listed yet.
+
+ Example:
+
+
+
+ bundle install
+ bundle inject 'rack' '> 0'
+
+
+
+ This will inject the 'rack' gem with a version greater than 0 in your
+ [1mGemfile(5)22m][Gemfile(5)] and Gemfile.lock
+
+
+
+ November 2018 BUNDLE-INJECT(1)
diff --git a/man/bundle-inject.ronn b/man/bundle-inject.ronn
new file mode 100644
index 0000000000..f454341896
--- /dev/null
+++ b/man/bundle-inject.ronn
@@ -0,0 +1,22 @@
+bundle-inject(1) -- Add named gem(s) with version requirements to Gemfile
+=========================================================================
+
+## SYNOPSIS
+
+`bundle inject` [GEM] [VERSION]
+
+## DESCRIPTION
+
+Adds the named gem(s) with their version requirements to the resolved
+[`Gemfile(5)`][Gemfile(5)].
+
+This command will add the gem to both your [`Gemfile(5)`][Gemfile(5)] and Gemfile.lock if it
+isn't listed yet.
+
+Example:
+
+ bundle install
+ 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
diff --git a/man/bundle-install.1 b/man/bundle-install.1
new file mode 100644
index 0000000000..c70f92eb25
--- /dev/null
+++ b/man/bundle-install.1
@@ -0,0 +1,308 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-INSTALL" "1" "December 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-install\fR \- Install the dependencies specified in your Gemfile
+.
+.SH "SYNOPSIS"
+\fBbundle install\fR [\-\-binstubs[=DIRECTORY]] [\-\-clean] [\-\-deployment] [\-\-force] [\-\-frozen] [\-\-full\-index] [\-\-gemfile=GEMFILE] [\-\-jobs=NUMBER] [\-\-local] [\-\-no\-cache] [\-\-no\-prune] [\-\-path PATH] [\-\-quiet] [\-\-retry=NUMBER] [\-\-shebang] [\-\-standalone[=GROUP[ GROUP\.\.\.]]] [\-\-system] [\-\-trust\-policy=POLICY] [\-\-with=GROUP[ GROUP\.\.\.]] [\-\-without=GROUP[ GROUP\.\.\.]]
+.
+.SH "DESCRIPTION"
+Install the gems specified in your Gemfile(5)\. If this is the first time you run bundle install (and a \fBGemfile\.lock\fR does not exist), Bundler will fetch all remote sources, resolve dependencies and install all needed gems\.
+.
+.P
+If a \fBGemfile\.lock\fR does exist, and you have not updated your Gemfile(5), Bundler will fetch all remote sources, but use the dependencies specified in the \fBGemfile\.lock\fR instead of resolving dependencies\.
+.
+.P
+If a \fBGemfile\.lock\fR does exist, and you have updated your Gemfile(5), Bundler will use the dependencies in the \fBGemfile\.lock\fR for all gems that you did not update, but will re\-resolve the dependencies of gems that you did update\. You can find more information about this update process below under \fICONSERVATIVE UPDATING\fR\.
+.
+.SH "OPTIONS"
+To apply any of \fB\-\-binstubs\fR, \fB\-\-deployment\fR, \fB\-\-path\fR, or \fB\-\-without\fR every time \fBbundle install\fR is run, use \fBbundle config\fR (see bundle\-config(1))\.
+.
+.TP
+\fB\-\-binstubs[=<directory>]\fR
+Creates a directory (defaults to \fB~/bin\fR) and place any executables from the gem there\. These executables run in Bundler\'s context\. If used, you might add this directory to your environment\'s \fBPATH\fR variable\. For instance, if the \fBrails\fR gem comes with a \fBrails\fR executable, this flag will create a \fBbin/rails\fR executable that ensures that all referred dependencies will be resolved using the bundled gems\.
+.
+.TP
+\fB\-\-clean\fR
+On finishing the installation Bundler is going to remove any gems not present in the current Gemfile(5)\. Don\'t worry, gems currently in use will not be removed\.
+.
+.TP
+\fB\-\-deployment\fR
+In \fIdeployment mode\fR, Bundler will \'roll\-out\' the bundle for production or CI use\. Please check carefully if you want to have this option enabled in your development environment\.
+.
+.TP
+\fB\-\-force\fR
+Force download every gem, even if the required versions are already available locally\. \fB\-\-redownload\fR is an alias of this option\.
+.
+.TP
+\fB\-\-frozen\fR
+Do not allow the Gemfile\.lock to be updated after this install\. Exits non\-zero if there are going to be changes to the Gemfile\.lock\.
+.
+.TP
+\fB\-\-full\-index\fR
+Bundler will not call Rubygems\' API endpoint (default) but download and cache a (currently big) index file of all gems\. Performance can be improved for large bundles that seldom change by enabling this option\.
+.
+.TP
+\fB\-\-gemfile=<gemfile>\fR
+The location of the Gemfile(5) which Bundler should use\. This defaults to a Gemfile(5) in the current working directory\. In general, Bundler will assume that the location of the Gemfile(5) is also the project\'s root and will try to find \fBGemfile\.lock\fR and \fBvendor/cache\fR relative to this location\.
+.
+.TP
+\fB\-\-jobs=[<number>]\fR, \fB\-j[<number>]\fR
+The maximum number of parallel download and install jobs\. The default is \fB1\fR\.
+.
+.TP
+\fB\-\-local\fR
+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 a appropriate platform\-specific gem exists on \fBrubygems\.org\fR it will not be found\.
+.
+.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\.
+.
+.TP
+\fB\-\-no\-prune\fR
+Don\'t remove stale gems from the cache when the installation finishes\.
+.
+.TP
+\fB\-\-path=<path>\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\.
+.
+.TP
+\fB\-\-quiet\fR
+Do not print progress information to the standard output\. Instead, Bundler will exit using a status code (\fB$?\fR)\.
+.
+.TP
+\fB\-\-retry=[<number>]\fR
+Retry failed network or git requests for \fInumber\fR times\.
+.
+.TP
+\fB\-\-shebang=<ruby\-executable>\fR
+Uses the specified ruby executable (usually \fBruby\fR) to execute the scripts created with \fB\-\-binstubs\fR\. In addition, if you use \fB\-\-binstubs\fR together with \fB\-\-shebang jruby\fR these executables will be changed to execute \fBjruby\fR instead\.
+.
+.TP
+\fB\-\-standalone[=<list>]\fR
+Makes a bundle that can work without depending on Rubygems or Bundler at runtime\. A space separated list of groups to install has to be specified\. Bundler creates a directory named \fBbundle\fR and installs the bundle there\. It also generates a \fBbundle/bundler/setup\.rb\fR file to replace Bundler\'s own setup in the manner required\. Using this option implicitly sets \fBpath\fR, which is a [remembered option][REMEMBERED OPTIONS]\.
+.
+.TP
+\fB\-\-system\fR
+Installs the gems specified in the bundle to the system\'s Rubygems location\. This overrides any previous configuration of \fB\-\-path\fR\.
+.
+.TP
+\fB\-\-trust\-policy=[<policy>]\fR
+Apply the Rubygems security policy \fIpolicy\fR, where policy is one of \fBHighSecurity\fR, \fBMediumSecurity\fR, \fBLowSecurity\fR, \fBAlmostNoSecurity\fR, or \fBNoSecurity\fR\. For more details, please see the Rubygems signing documentation linked below in \fISEE ALSO\fR\.
+.
+.TP
+\fB\-\-with=<list>\fR
+A space\-separated list of groups referencing gems to install\. If an optional group is given it is installed\. If a group is given that is in the remembered list of groups given to \-\-without, it is removed from that list\.
+.
+.TP
+\fB\-\-without=<list>\fR
+A space\-separated list of groups referencing gems to skip during installation\. If a group is given that is in the remembered list of groups given to \-\-with, it is removed from that list\.
+.
+.SH "DEPLOYMENT MODE"
+Bundler\'s defaults are optimized for development\. To switch to defaults optimized for deployment and for CI, use the \fB\-\-deployment\fR flag\. Do not activate deployment mode on development machines, as it will cause an error when the Gemfile(5) is modified\.
+.
+.IP "1." 4
+A \fBGemfile\.lock\fR is required\.
+.
+.IP
+To ensure that the same versions of the gems you developed with and tested with are also used in deployments, a \fBGemfile\.lock\fR is required\.
+.
+.IP
+This is mainly to ensure that you remember to check your \fBGemfile\.lock\fR into version control\.
+.
+.IP "2." 4
+The \fBGemfile\.lock\fR must be up to date
+.
+.IP
+In development, you can modify your Gemfile(5) and re\-run \fBbundle install\fR to \fIconservatively update\fR your \fBGemfile\.lock\fR snapshot\.
+.
+.IP
+In deployment, your \fBGemfile\.lock\fR should be up\-to\-date with changes made in your Gemfile(5)\.
+.
+.IP "3." 4
+Gems are installed to \fBvendor/bundle\fR not your default system location
+.
+.IP
+In development, it\'s convenient to share the gems used in your application with other applications and other scripts that run on the system\.
+.
+.IP
+In deployment, isolation is a more important default\. In addition, the user deploying the application may not have permission to install gems to the system, or the web server may not have permission to read them\.
+.
+.IP
+As a result, \fBbundle install \-\-deployment\fR installs gems to the \fBvendor/bundle\fR directory in the application\. This may be overridden using the \fB\-\-path\fR option\.
+.
+.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\.
+.
+.P
+However, you can explicitly tell Bundler to skip installing certain groups with the \fB\-\-without\fR option\. This option takes a space\-separated list of groups\.
+.
+.P
+While the \fB\-\-without\fR option will skip \fIinstalling\fR the gems in the specified groups, it will still \fIdownload\fR those gems and use them to resolve the dependencies of every gem in your Gemfile(5)\.
+.
+.P
+This is so that installing a different set of groups on another machine (such as a production server) will not change the gems and versions that you have already developed and tested against\.
+.
+.P
+\fBBundler offers a rock\-solid guarantee that the third\-party code you are running in development and testing is also the third\-party code you are running in production\. You can choose to exclude some of that code in different environments, but you will never be caught flat\-footed by different versions of third\-party code being used in different environments\.\fR
+.
+.P
+For a simple illustration, consider the following Gemfile(5):
+.
+.IP "" 4
+.
+.nf
+
+source \'https://rubygems\.org\'
+
+gem \'sinatra\'
+
+group :production do
+ gem \'rack\-perftools\-profiler\'
+end
+.
+.fi
+.
+.IP "" 0
+.
+.P
+In this case, \fBsinatra\fR depends on any version of Rack (\fB>= 1\.0\fR), while \fBrack\-perftools\-profiler\fR depends on 1\.x (\fB~> 1\.0\fR)\.
+.
+.P
+When you run \fBbundle install \-\-without production\fR in development, we look at the dependencies of \fBrack\-perftools\-profiler\fR as well\. That way, you do not spend all your time developing against Rack 2\.0, using new APIs unavailable in Rack 1\.x, only to have Bundler switch to Rack 1\.2 when the \fBproduction\fR group \fIis\fR used\.
+.
+.P
+This should not cause any problems in practice, because we do not attempt to \fBinstall\fR the gems in the excluded groups, and only evaluate as part of the dependency resolution process\.
+.
+.P
+This also means that you cannot include different versions of the same gem in different groups, because doing so would result in different sets of dependencies used in development and production\. Because of the vagaries of the dependency resolution process, this usually affects more than the gems you list in your Gemfile(5), and can (surprisingly) radically change the gems you are using\.
+.
+.SH "THE GEMFILE\.LOCK"
+When you run \fBbundle install\fR, Bundler will persist the full names and versions of all gems that you used (including dependencies of the gems specified in the Gemfile(5)) into a file called \fBGemfile\.lock\fR\.
+.
+.P
+Bundler uses this file in all subsequent calls to \fBbundle install\fR, which guarantees that you always use the same exact code, even as your application moves across machines\.
+.
+.P
+Because of the way dependency resolution works, even a seemingly small change (for instance, an update to a point\-release of a dependency of a gem in your Gemfile(5)) can result in radically different gems being needed to satisfy all dependencies\.
+.
+.P
+As a result, you \fBSHOULD\fR check your \fBGemfile\.lock\fR into version control, in both applications and gems\. If you do not, every machine that checks out your repository (including your production server) will resolve all dependencies again, which will result in different versions of third\-party code being used if \fBany\fR of the gems in the Gemfile(5) or any of their dependencies have been updated\.
+.
+.P
+When Bundler first shipped, the \fBGemfile\.lock\fR was included in the \fB\.gitignore\fR file included with generated gems\. Over time, however, it became clear that this practice forces the pain of broken dependencies onto new contributors, while leaving existing contributors potentially unaware of the problem\. Since \fBbundle install\fR is usually the first step towards a contribution, the pain of broken dependencies would discourage new contributors from contributing\. As a result, we have revised our guidance for gem authors to now recommend checking in the lock for gems\.
+.
+.SH "CONSERVATIVE UPDATING"
+When you make a change to the Gemfile(5) and then run \fBbundle install\fR, Bundler will update only the gems that you modified\.
+.
+.P
+In other words, if a gem that you \fBdid not modify\fR worked before you called \fBbundle install\fR, it will continue to use the exact same versions of all dependencies as it used before the update\.
+.
+.P
+Let\'s take a look at an example\. Here\'s your original Gemfile(5):
+.
+.IP "" 4
+.
+.nf
+
+source \'https://rubygems\.org\'
+
+gem \'actionpack\', \'2\.3\.8\'
+gem \'activemerchant\'
+.
+.fi
+.
+.IP "" 0
+.
+.P
+In this case, both \fBactionpack\fR and \fBactivemerchant\fR depend on \fBactivesupport\fR\. The \fBactionpack\fR gem depends on \fBactivesupport 2\.3\.8\fR and \fBrack ~> 1\.1\.0\fR, while the \fBactivemerchant\fR gem depends on \fBactivesupport >= 2\.3\.2\fR, \fBbraintree >= 2\.0\.0\fR, and \fBbuilder >= 2\.0\.0\fR\.
+.
+.P
+When the dependencies are first resolved, Bundler will select \fBactivesupport 2\.3\.8\fR, which satisfies the requirements of both gems in your Gemfile(5)\.
+.
+.P
+Next, you modify your Gemfile(5) to:
+.
+.IP "" 4
+.
+.nf
+
+source \'https://rubygems\.org\'
+
+gem \'actionpack\', \'3\.0\.0\.rc\'
+gem \'activemerchant\'
+.
+.fi
+.
+.IP "" 0
+.
+.P
+The \fBactionpack 3\.0\.0\.rc\fR gem has a number of new dependencies, and updates the \fBactivesupport\fR dependency to \fB= 3\.0\.0\.rc\fR and the \fBrack\fR dependency to \fB~> 1\.2\.1\fR\.
+.
+.P
+When you run \fBbundle install\fR, Bundler notices that you changed the \fBactionpack\fR gem, but not the \fBactivemerchant\fR gem\. It evaluates the gems currently being used to satisfy its requirements:
+.
+.TP
+\fBactivesupport 2\.3\.8\fR
+also used to satisfy a dependency in \fBactivemerchant\fR, which is not being updated
+.
+.TP
+\fBrack ~> 1\.1\.0\fR
+not currently being used to satisfy another dependency
+.
+.P
+Because you did not explicitly ask to update \fBactivemerchant\fR, you would not expect it to suddenly stop working after updating \fBactionpack\fR\. However, satisfying the new \fBactivesupport 3\.0\.0\.rc\fR dependency of actionpack requires updating one of its dependencies\.
+.
+.P
+Even though \fBactivemerchant\fR declares a very loose dependency that theoretically matches \fBactivesupport 3\.0\.0\.rc\fR, Bundler treats gems in your Gemfile(5) that have not changed as an atomic unit together with their dependencies\. In this case, the \fBactivemerchant\fR dependency is treated as \fBactivemerchant 1\.7\.1 + activesupport 2\.3\.8\fR, so \fBbundle install\fR will report that it cannot update \fBactionpack\fR\.
+.
+.P
+To explicitly update \fBactionpack\fR, including its dependencies which other gems in the Gemfile(5) still depend on, run \fBbundle update actionpack\fR (see \fBbundle update(1)\fR)\.
+.
+.P
+\fBSummary\fR: In general, after making a change to the Gemfile(5) , you should first try to run \fBbundle install\fR, which will guarantee that no other gem in the Gemfile(5) is impacted by the change\. If that does not work, run bundle update(1) \fIbundle\-update\.1\.html\fR\.
+.
+.SH "SEE ALSO"
+.
+.IP "\(bu" 4
+Gem install docs \fIhttp://guides\.rubygems\.org/rubygems\-basics/#installing\-gems\fR
+.
+.IP "\(bu" 4
+Rubygems signing docs \fIhttp://guides\.rubygems\.org/security/\fR
+.
+.IP "" 0
+
diff --git a/man/bundle-install.1.txt b/man/bundle-install.1.txt
new file mode 100644
index 0000000000..93118629c7
--- /dev/null
+++ b/man/bundle-install.1.txt
@@ -0,0 +1,396 @@
+BUNDLE-INSTALL(1) BUNDLE-INSTALL(1)
+
+
+
+1mNAME0m
+ 1mbundle-install 22m- Install the dependencies specified in your Gemfile
+
+1mSYNOPSIS0m
+ 1mbundle install 22m[--binstubs[=DIRECTORY]] [--clean] [--deployment]
+ [--force] [--frozen] [--full-index] [--gemfile=GEMFILE] [--jobs=NUMBER]
+ [--local] [--no-cache] [--no-prune] [--path PATH] [--quiet]
+ [--retry=NUMBER] [--shebang] [--standalone[=GROUP[ GROUP...]]] [--sys-
+ tem] [--trust-policy=POLICY] [--with=GROUP[ GROUP...]] [--with-
+ out=GROUP[ GROUP...]]
+
+1mDESCRIPTION0m
+ Install the gems specified in your Gemfile(5). If this is the first
+ time you run bundle install (and a 1mGemfile.lock 22mdoes not exist),
+ Bundler will fetch all remote sources, resolve dependencies and install
+ all needed gems.
+
+ If a 1mGemfile.lock 22mdoes exist, and you have not updated your Gemfile(5),
+ Bundler will fetch all remote sources, but use the dependencies speci-
+ fied in the 1mGemfile.lock 22minstead of resolving dependencies.
+
+ If a 1mGemfile.lock 22mdoes exist, and you have updated your Gemfile(5),
+ Bundler will use the dependencies in the 1mGemfile.lock 22mfor all gems that
+ you did not update, but will re-resolve the dependencies of gems that
+ you did update. You can find more information about this update process
+ below under 4mCONSERVATIVE24m 4mUPDATING24m.
+
+1mOPTIONS0m
+ To apply any of 1m--binstubs22m, 1m--deployment22m, 1m--path22m, or 1m--without 22mevery
+ time 1mbundle install 22mis run, use 1mbundle config 22m(see bundle-config(1)).
+
+ 1m--binstubs[=<directory>]0m
+ Creates a directory (defaults to 1m~/bin22m) and place any executa-
+ bles from the gem there. These executables run in Bundler's con-
+ text. If used, you might add this directory to your environ-
+ ment's 1mPATH 22mvariable. For instance, if the 1mrails 22mgem comes with
+ a 1mrails 22mexecutable, this flag will create a 1mbin/rails 22mexecutable
+ that ensures that all referred dependencies will be resolved
+ using the bundled gems.
+
+ 1m--clean0m
+ On finishing the installation Bundler is going to remove any
+ gems not present in the current Gemfile(5). Don't worry, gems
+ currently in use will not be removed.
+
+ 1m--deployment0m
+ In 4mdeployment24m 4mmode24m, Bundler will 'roll-out' the bundle for pro-
+ duction or CI use. Please check carefully if you want to have
+ this option enabled in your development environment.
+
+ 1m--force0m
+ Force download every gem, even if the required versions are
+ already available locally. 1m--redownload 22mis an alias of this
+ option.
+
+ 1m--frozen0m
+ Do not allow the Gemfile.lock to be updated after this install.
+ Exits non-zero if there are going to be changes to the Gem-
+ file.lock.
+
+ 1m--full-index0m
+ Bundler will not call Rubygems' API endpoint (default) but down-
+ load and cache a (currently big) index file of all gems. Perfor-
+ mance can be improved for large bundles that seldom change by
+ enabling this option.
+
+ 1m--gemfile=<gemfile>0m
+ The location of the Gemfile(5) which Bundler should use. This
+ defaults to a Gemfile(5) in the current working directory. In
+ general, Bundler will assume that the location of the Gemfile(5)
+ is also the project's root and will try to find 1mGemfile.lock 22mand
+ 1mvendor/cache 22mrelative to this location.
+
+ 1m--jobs=[<number>]22m, 1m-j[<number>]0m
+ The maximum number of parallel download and install jobs. The
+ default is 1m122m.
+
+ 1m--local0m
+ Do not attempt to connect to 1mrubygems.org22m. Instead, Bundler will
+ use the gems already present in Rubygems' cache or in 1mven-0m
+ 1mdor/cache22m. Note that if a appropriate platform-specific gem
+ exists on 1mrubygems.org 22mit will not be found.
+
+ 1m--no-cache0m
+ Do not update the cache in 1mvendor/cache 22mwith 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.
+
+ 1m--no-prune0m
+ Don't remove stale gems from the cache when the installation
+ finishes.
+
+ 1m--path=<path>0m
+ The location to install the specified gems to. This defaults to
+ Rubygems' setting. Bundler shares this location with Rubygems,
+ 1mgem install ... 22mwill have gem installed there, too. Therefore,
+ gems installed without a 1m--path ... 22msetting will show up by
+ calling 1mgem list22m. Accordingly, gems installed to other locations
+ will not get listed.
+
+ 1m--quiet0m
+ Do not print progress information to the standard output.
+ Instead, Bundler will exit using a status code (1m$?22m).
+
+ 1m--retry=[<number>]0m
+ Retry failed network or git requests for 4mnumber24m times.
+
+ 1m--shebang=<ruby-executable>0m
+ Uses the specified ruby executable (usually 1mruby22m) to execute the
+ scripts created with 1m--binstubs22m. In addition, if you use 1m--bin-0m
+ 1mstubs 22mtogether with 1m--shebang jruby 22mthese executables will be
+ changed to execute 1mjruby 22minstead.
+
+ 1m--standalone[=<list>]0m
+ Makes a bundle that can work without depending on Rubygems or
+ Bundler at runtime. A space separated list of groups to install
+ has to be specified. Bundler creates a directory named 1mbundle0m
+ and installs the bundle there. It also generates a 1mbun-0m
+ 1mdle/bundler/setup.rb 22mfile to replace Bundler's own setup in the
+ manner required. Using this option implicitly sets 1mpath22m, which
+ is a [remembered option][REMEMBERED OPTIONS].
+
+ 1m--system0m
+ Installs the gems specified in the bundle to the system's
+ Rubygems location. This overrides any previous configuration of
+ 1m--path22m.
+
+ 1m--trust-policy=[<policy>]0m
+ Apply the Rubygems security policy 4mpolicy24m, where policy is one
+ of 1mHighSecurity22m, 1mMediumSecurity22m, 1mLowSecurity22m, 1mAlmostNoSecurity22m,
+ or 1mNoSecurity22m. For more details, please see the Rubygems signing
+ documentation linked below in 4mSEE24m 4mALSO24m.
+
+ 1m--with=<list>0m
+ A space-separated list of groups referencing gems to install. If
+ an optional group is given it is installed. If a group is given
+ that is in the remembered list of groups given to --without, it
+ is removed from that list.
+
+ 1m--without=<list>0m
+ A space-separated list of groups referencing gems to skip during
+ installation. If a group is given that is in the remembered list
+ of groups given to --with, it is removed from that list.
+
+1mDEPLOYMENT MODE0m
+ Bundler's defaults are optimized for development. To switch to defaults
+ optimized for deployment and for CI, use the 1m--deployment 22mflag. Do not
+ activate deployment mode on development machines, as it will cause an
+ error when the Gemfile(5) is modified.
+
+ 1. A 1mGemfile.lock 22mis required.
+
+ To ensure that the same versions of the gems you developed with and
+ tested with are also used in deployments, a 1mGemfile.lock 22mis
+ required.
+
+ This is mainly to ensure that you remember to check your 1mGem-0m
+ 1mfile.lock 22minto version control.
+
+ 2. The 1mGemfile.lock 22mmust be up to date
+
+ In development, you can modify your Gemfile(5) and re-run 1mbundle0m
+ 1minstall 22mto 4mconservatively24m 4mupdate24m your 1mGemfile.lock 22msnapshot.
+
+ In deployment, your 1mGemfile.lock 22mshould be up-to-date with changes
+ made in your Gemfile(5).
+
+ 3. Gems are installed to 1mvendor/bundle 22mnot your default system loca-
+ tion
+
+ In development, it's convenient to share the gems used in your
+ application with other applications and other scripts that run on
+ the system.
+
+ In deployment, isolation is a more important default. In addition,
+ the user deploying the application may not have permission to
+ install gems to the system, or the web server may not have permis-
+ sion to read them.
+
+ As a result, 1mbundle install --deployment 22minstalls gems to the 1mven-0m
+ 1mdor/bundle 22mdirectory in the application. This may be overridden
+ using the 1m--path 22moption.
+
+
+
+1mSUDO USAGE0m
+ By default, Bundler installs gems to the same location as 1mgem install22m.
+
+ 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 1msudo 22mpassword 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 1msudo bundle install22m. This is because several other
+ steps in 1mbundle install 22mmust be performed as the current user:
+
+ o Updating your 1mGemfile.lock0m
+
+ o Updating your 1mvendor/cache22m, if necessary
+
+ o Checking out private git repositories using your user's SSH keys
+
+
+
+ Of these three, the first two could theoretically be performed by
+ 1mchown22ming the resulting files to 1m$SUDO_USER22m. The third, however, can
+ only be performed by invoking the 1mgit 22mcommand as the current user.
+ Therefore, git gems are downloaded and installed into 1m~/.bundle 22mrather
+ than $GEM_HOME or $BUNDLE_PATH.
+
+ As a result, you should run 1mbundle install 22mas the current user, and
+ Bundler will ask for your password if it is needed to put the gems into
+ their final location.
+
+1mINSTALLING GROUPS0m
+ By default, 1mbundle install 22mwill install all gems in all groups in your
+ Gemfile(5), except those declared for a different platform.
+
+ However, you can explicitly tell Bundler to skip installing certain
+ groups with the 1m--without 22moption. This option takes a space-separated
+ list of groups.
+
+ While the 1m--without 22moption will skip 4minstalling24m the gems in the speci-
+ fied groups, it will still 4mdownload24m those gems and use them to resolve
+ the dependencies of every gem in your Gemfile(5).
+
+ This is so that installing a different set of groups on another machine
+ (such as a production server) will not change the gems and versions
+ that you have already developed and tested against.
+
+ 1mBundler offers a rock-solid guarantee that the third-party code you are0m
+ 1mrunning in development and testing is also the third-party code you are0m
+ 1mrunning in production. You can choose to exclude some of that code in0m
+ 1mdifferent environments, but you will never be caught flat-footed by0m
+ 1mdifferent versions of third-party code being used in different environ-0m
+ 1mments.0m
+
+ For a simple illustration, consider the following Gemfile(5):
+
+
+
+ source 'https://rubygems.org'
+
+ gem 'sinatra'
+
+ group :production do
+ gem 'rack-perftools-profiler'
+ end
+
+
+
+ In this case, 1msinatra 22mdepends on any version of Rack (1m>= 1.022m), while
+ 1mrack-perftools-profiler 22mdepends on 1.x (1m~> 1.022m).
+
+ When you run 1mbundle install --without production 22min development, we
+ look at the dependencies of 1mrack-perftools-profiler 22mas well. That way,
+ you do not spend all your time developing against Rack 2.0, using new
+ APIs unavailable in Rack 1.x, only to have Bundler switch to Rack 1.2
+ when the 1mproduction 22mgroup 4mis24m used.
+
+ This should not cause any problems in practice, because we do not
+ attempt to 1minstall 22mthe gems in the excluded groups, and only evaluate
+ as part of the dependency resolution process.
+
+ This also means that you cannot include different versions of the same
+ gem in different groups, because doing so would result in different
+ sets of dependencies used in development and production. Because of the
+ vagaries of the dependency resolution process, this usually affects
+ more than the gems you list in your Gemfile(5), and can (surprisingly)
+ radically change the gems you are using.
+
+1mTHE GEMFILE.LOCK0m
+ When you run 1mbundle install22m, Bundler will persist the full names and
+ versions of all gems that you used (including dependencies of the gems
+ specified in the Gemfile(5)) into a file called 1mGemfile.lock22m.
+
+ Bundler uses this file in all subsequent calls to 1mbundle install22m, which
+ guarantees that you always use the same exact code, even as your appli-
+ cation moves across machines.
+
+ Because of the way dependency resolution works, even a seemingly small
+ change (for instance, an update to a point-release of a dependency of a
+ gem in your Gemfile(5)) can result in radically different gems being
+ needed to satisfy all dependencies.
+
+ As a result, you 1mSHOULD 22mcheck your 1mGemfile.lock 22minto version control,
+ in both applications and gems. If you do not, every machine that checks
+ out your repository (including your production server) will resolve all
+ dependencies again, which will result in different versions of
+ third-party code being used if 1many 22mof the gems in the Gemfile(5) or any
+ of their dependencies have been updated.
+
+ When Bundler first shipped, the 1mGemfile.lock 22mwas included in the 1m.git-0m
+ 1mignore 22mfile included with generated gems. Over time, however, it became
+ clear that this practice forces the pain of broken dependencies onto
+ new contributors, while leaving existing contributors potentially
+ unaware of the problem. Since 1mbundle install 22mis usually the first step
+ towards a contribution, the pain of broken dependencies would discour-
+ age new contributors from contributing. As a result, we have revised
+ our guidance for gem authors to now recommend checking in the lock for
+ gems.
+
+1mCONSERVATIVE UPDATING0m
+ When you make a change to the Gemfile(5) and then run 1mbundle install22m,
+ Bundler will update only the gems that you modified.
+
+ In other words, if a gem that you 1mdid not modify 22mworked before you
+ called 1mbundle install22m, it will continue to use the exact same versions
+ of all dependencies as it used before the update.
+
+ Let's take a look at an example. Here's your original Gemfile(5):
+
+
+
+ source 'https://rubygems.org'
+
+ gem 'actionpack', '2.3.8'
+ gem 'activemerchant'
+
+
+
+ In this case, both 1mactionpack 22mand 1mactivemerchant 22mdepend on 1mactivesup-0m
+ 1mport22m. The 1mactionpack 22mgem depends on 1mactivesupport 2.3.8 22mand 1mrack ~>0m
+ 1m1.1.022m, while the 1mactivemerchant 22mgem depends on 1mactivesupport >= 2.3.222m,
+ 1mbraintree >= 2.0.022m, and 1mbuilder >= 2.0.022m.
+
+ When the dependencies are first resolved, Bundler will select
+ 1mactivesupport 2.3.822m, which satisfies the requirements of both gems in
+ your Gemfile(5).
+
+ Next, you modify your Gemfile(5) to:
+
+
+
+ source 'https://rubygems.org'
+
+ gem 'actionpack', '3.0.0.rc'
+ gem 'activemerchant'
+
+
+
+ The 1mactionpack 3.0.0.rc 22mgem has a number of new dependencies, and
+ updates the 1mactivesupport 22mdependency to 1m= 3.0.0.rc 22mand the 1mrack 22mdepen-
+ dency to 1m~> 1.2.122m.
+
+ When you run 1mbundle install22m, Bundler notices that you changed the
+ 1mactionpack 22mgem, but not the 1mactivemerchant 22mgem. It evaluates the gems
+ currently being used to satisfy its requirements:
+
+ 1mactivesupport 2.3.80m
+ also used to satisfy a dependency in 1mactivemerchant22m, which is
+ not being updated
+
+ 1mrack ~> 1.1.00m
+ not currently being used to satisfy another dependency
+
+ Because you did not explicitly ask to update 1mactivemerchant22m, you would
+ not expect it to suddenly stop working after updating 1mactionpack22m. How-
+ ever, satisfying the new 1mactivesupport 3.0.0.rc 22mdependency of action-
+ pack requires updating one of its dependencies.
+
+ Even though 1mactivemerchant 22mdeclares a very loose dependency that theo-
+ retically matches 1mactivesupport 3.0.0.rc22m, Bundler treats gems in your
+ Gemfile(5) that have not changed as an atomic unit together with their
+ dependencies. In this case, the 1mactivemerchant 22mdependency is treated as
+ 1mactivemerchant 1.7.1 + activesupport 2.3.822m, so 1mbundle install 22mwill
+ report that it cannot update 1mactionpack22m.
+
+ To explicitly update 1mactionpack22m, including its dependencies which other
+ gems in the Gemfile(5) still depend on, run 1mbundle update actionpack0m
+ (see 1mbundle update(1)22m).
+
+ 1mSummary22m: In general, after making a change to the Gemfile(5) , you
+ should first try to run 1mbundle install22m, which will guarantee that no
+ other gem in the Gemfile(5) is impacted by the change. If that does not
+ work, run bundle update(1) 4mbundle-update.1.html24m.
+
+1mSEE ALSO0m
+ o Gem install docs
+ 4mhttp://guides.rubygems.org/rubygems-basics/#installing-gems0m
+
+ o Rubygems signing docs 4mhttp://guides.rubygems.org/security/0m
+
+
+
+
+
+
+ December 2018 BUNDLE-INSTALL(1)
diff --git a/man/bundle-install.ronn b/man/bundle-install.ronn
new file mode 100644
index 0000000000..a9e375c87c
--- /dev/null
+++ b/man/bundle-install.ronn
@@ -0,0 +1,378 @@
+bundle-install(1) -- Install the dependencies specified in your Gemfile
+=======================================================================
+
+## SYNOPSIS
+
+`bundle install` [--binstubs[=DIRECTORY]]
+ [--clean]
+ [--deployment]
+ [--force]
+ [--frozen]
+ [--full-index]
+ [--gemfile=GEMFILE]
+ [--jobs=NUMBER]
+ [--local]
+ [--no-cache]
+ [--no-prune]
+ [--path PATH]
+ [--quiet]
+ [--retry=NUMBER]
+ [--shebang]
+ [--standalone[=GROUP[ GROUP...]]]
+ [--system]
+ [--trust-policy=POLICY]
+ [--with=GROUP[ GROUP...]]
+ [--without=GROUP[ GROUP...]]
+
+## DESCRIPTION
+
+Install the gems specified in your Gemfile(5). If this is the first
+time you run bundle install (and a `Gemfile.lock` does not exist),
+Bundler will fetch all remote sources, resolve dependencies and
+install all needed gems.
+
+If a `Gemfile.lock` does exist, and you have not updated your Gemfile(5),
+Bundler will fetch all remote sources, but use the dependencies
+specified in the `Gemfile.lock` instead of resolving dependencies.
+
+If a `Gemfile.lock` does exist, and you have updated your Gemfile(5),
+Bundler will use the dependencies in the `Gemfile.lock` for all gems
+that you did not update, but will re-resolve the dependencies of
+gems that you did update. You can find more information about this
+update process below under [CONSERVATIVE UPDATING][].
+
+## OPTIONS
+
+To apply any of `--binstubs`, `--deployment`, `--path`, or `--without` every
+time `bundle install` is run, use `bundle config` (see bundle-config(1)).
+
+* `--binstubs[=<directory>]`:
+ Creates a directory (defaults to `~/bin`) and place any executables from the
+ gem there. These executables run in Bundler's context. If used, you might add
+ this directory to your environment's `PATH` variable. For instance, if the
+ `rails` gem comes with a `rails` executable, this flag will create a
+ `bin/rails` executable that ensures that all referred dependencies will be
+ resolved using the bundled gems.
+
+* `--clean`:
+ On finishing the installation Bundler is going to remove any gems not present
+ in the current Gemfile(5). Don't worry, gems currently in use will not be
+ removed.
+
+* `--deployment`:
+ In [deployment mode][DEPLOYMENT MODE], Bundler will 'roll-out' the bundle for
+ production or CI use. Please check carefully if you want to have this option
+ enabled in your development environment.
+
+* `--force`:
+ Force download every gem, even if the required versions are already available
+ locally. `--redownload` is an alias of this option.
+
+* `--frozen`:
+ Do not allow the Gemfile.lock to be updated after this install. Exits
+ non-zero if there are going to be changes to the Gemfile.lock.
+
+* `--full-index`:
+ Bundler will not call Rubygems' API endpoint (default) but download and cache
+ a (currently big) index file of all gems. Performance can be improved for
+ large bundles that seldom change by enabling this option.
+
+* `--gemfile=<gemfile>`:
+ The location of the Gemfile(5) which Bundler should use. This defaults
+ to a Gemfile(5) in the current working directory. In general, Bundler
+ will assume that the location of the Gemfile(5) is also the project's
+ root and will try to find `Gemfile.lock` and `vendor/cache` relative
+ to this location.
+
+* `--jobs=[<number>]`, `-j[<number>]`:
+ The maximum number of parallel download and install jobs. The default
+ is `1`.
+
+* `--local`:
+ Do not attempt to connect to `rubygems.org`. Instead, Bundler will use the
+ gems already present in Rubygems' cache or in `vendor/cache`. Note that if a
+ appropriate platform-specific gem exists on `rubygems.org` it will not be
+ found.
+
+* `--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
+ being cached during the install.
+
+* `--no-prune`:
+ Don't remove stale gems from the cache when the installation finishes.
+
+* `--path=<path>`:
+ The location to install the specified gems to. This defaults to Rubygems'
+ setting. Bundler shares this location with Rubygems, `gem install ...` will
+ have gem installed there, too. Therefore, gems installed without a
+ `--path ...` setting will show up by calling `gem list`. Accordingly, gems
+ installed to other locations will not get listed.
+
+* `--quiet`:
+ Do not print progress information to the standard output. Instead, Bundler
+ will exit using a status code (`$?`).
+
+* `--retry=[<number>]`:
+ Retry failed network or git requests for <number> times.
+
+* `--shebang=<ruby-executable>`:
+ Uses the specified ruby executable (usually `ruby`) to execute the scripts
+ created with `--binstubs`. In addition, if you use `--binstubs` together with
+ `--shebang jruby` these executables will be changed to execute `jruby`
+ instead.
+
+* `--standalone[=<list>]`:
+ Makes a bundle that can work without depending on Rubygems or Bundler at
+ runtime. A space separated list of groups to install has to be specified.
+ Bundler creates a directory named `bundle` and installs the bundle there. It
+ also generates a `bundle/bundler/setup.rb` file to replace Bundler's own setup
+ in the manner required. Using this option implicitly sets `path`, which is a
+ [remembered option][REMEMBERED OPTIONS].
+
+* `--system`:
+ Installs the gems specified in the bundle to the system's Rubygems location.
+ This overrides any previous configuration of `--path`.
+
+* `--trust-policy=[<policy>]`:
+ Apply the Rubygems security policy <policy>, where policy is one of
+ `HighSecurity`, `MediumSecurity`, `LowSecurity`, `AlmostNoSecurity`, or
+ `NoSecurity`. For more details, please see the Rubygems signing documentation
+ linked below in [SEE ALSO][].
+
+* `--with=<list>`:
+ A space-separated list of groups referencing gems to install. If an
+ optional group is given it is installed. If a group is given that is
+ in the remembered list of groups given to --without, it is removed
+ from that list.
+
+* `--without=<list>`:
+ A space-separated list of groups referencing gems to skip during installation.
+ If a group is given that is in the remembered list of groups given
+ to --with, it is removed from that list.
+
+## DEPLOYMENT MODE
+
+Bundler's defaults are optimized for development. To switch to
+defaults optimized for deployment and for CI, use the `--deployment`
+flag. Do not activate deployment mode on development machines, as it
+will cause an error when the Gemfile(5) is modified.
+
+1. A `Gemfile.lock` is required.
+
+ To ensure that the same versions of the gems you developed with
+ and tested with are also used in deployments, a `Gemfile.lock`
+ is required.
+
+ This is mainly to ensure that you remember to check your
+ `Gemfile.lock` into version control.
+
+2. The `Gemfile.lock` must be up to date
+
+ In development, you can modify your Gemfile(5) and re-run
+ `bundle install` to [conservatively update][CONSERVATIVE UPDATING]
+ your `Gemfile.lock` snapshot.
+
+ In deployment, your `Gemfile.lock` should be up-to-date with
+ changes made in your Gemfile(5).
+
+3. Gems are installed to `vendor/bundle` not your default system location
+
+ In development, it's convenient to share the gems used in your
+ application with other applications and other scripts that run on
+ the system.
+
+ In deployment, isolation is a more important default. In addition,
+ the user deploying the application may not have permission to install
+ gems to the system, or the web server may not have permission to
+ read them.
+
+ As a result, `bundle install --deployment` installs gems to
+ 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
+in your Gemfile(5), except those declared for a different platform.
+
+However, you can explicitly tell Bundler to skip installing
+certain groups with the `--without` option. This option takes
+a space-separated list of groups.
+
+While the `--without` option will skip _installing_ the gems in the
+specified groups, it will still _download_ those gems and use them to
+resolve the dependencies of every gem in your Gemfile(5).
+
+This is so that installing a different set of groups on another
+ machine (such as a production server) will not change the
+gems and versions that you have already developed and tested against.
+
+`Bundler offers a rock-solid guarantee that the third-party
+code you are running in development and testing is also the
+third-party code you are running in production. You can choose
+to exclude some of that code in different environments, but you
+will never be caught flat-footed by different versions of
+third-party code being used in different environments.`
+
+For a simple illustration, consider the following Gemfile(5):
+
+ source 'https://rubygems.org'
+
+ gem 'sinatra'
+
+ group :production do
+ gem 'rack-perftools-profiler'
+ end
+
+In this case, `sinatra` depends on any version of Rack (`>= 1.0`), while
+`rack-perftools-profiler` depends on 1.x (`~> 1.0`).
+
+When you run `bundle install --without production` in development, we
+look at the dependencies of `rack-perftools-profiler` as well. That way,
+you do not spend all your time developing against Rack 2.0, using new
+APIs unavailable in Rack 1.x, only to have Bundler switch to Rack 1.2
+when the `production` group _is_ used.
+
+This should not cause any problems in practice, because we do not
+attempt to `install` the gems in the excluded groups, and only evaluate
+as part of the dependency resolution process.
+
+This also means that you cannot include different versions of the same
+gem in different groups, because doing so would result in different
+sets of dependencies used in development and production. Because of
+the vagaries of the dependency resolution process, this usually
+affects more than the gems you list in your Gemfile(5), and can
+(surprisingly) radically change the gems you are using.
+
+## THE GEMFILE.LOCK
+
+When you run `bundle install`, Bundler will persist the full names
+and versions of all gems that you used (including dependencies of
+the gems specified in the Gemfile(5)) into a file called `Gemfile.lock`.
+
+Bundler uses this file in all subsequent calls to `bundle install`,
+which guarantees that you always use the same exact code, even
+as your application moves across machines.
+
+Because of the way dependency resolution works, even a
+seemingly small change (for instance, an update to a point-release
+of a dependency of a gem in your Gemfile(5)) can result in radically
+different gems being needed to satisfy all dependencies.
+
+As a result, you `SHOULD` check your `Gemfile.lock` into version
+control, in both applications and gems. If you do not, every machine that
+checks out your repository (including your production server) will resolve all
+dependencies again, which will result in different versions of
+third-party code being used if `any` of the gems in the Gemfile(5)
+or any of their dependencies have been updated.
+
+When Bundler first shipped, the `Gemfile.lock` was included in the `.gitignore`
+file included with generated gems. Over time, however, it became clear that
+this practice forces the pain of broken dependencies onto new contributors,
+while leaving existing contributors potentially unaware of the problem. Since
+`bundle install` is usually the first step towards a contribution, the pain of
+broken dependencies would discourage new contributors from contributing. As a
+result, we have revised our guidance for gem authors to now recommend checking
+in the lock for gems.
+
+## CONSERVATIVE UPDATING
+
+When you make a change to the Gemfile(5) and then run `bundle install`,
+Bundler will update only the gems that you modified.
+
+In other words, if a gem that you `did not modify` worked before
+you called `bundle install`, it will continue to use the exact
+same versions of all dependencies as it used before the update.
+
+Let's take a look at an example. Here's your original Gemfile(5):
+
+ source 'https://rubygems.org'
+
+ gem 'actionpack', '2.3.8'
+ gem 'activemerchant'
+
+In this case, both `actionpack` and `activemerchant` depend on
+`activesupport`. The `actionpack` gem depends on `activesupport 2.3.8`
+and `rack ~> 1.1.0`, while the `activemerchant` gem depends on
+`activesupport >= 2.3.2`, `braintree >= 2.0.0`, and `builder >= 2.0.0`.
+
+When the dependencies are first resolved, Bundler will select
+`activesupport 2.3.8`, which satisfies the requirements of both
+gems in your Gemfile(5).
+
+Next, you modify your Gemfile(5) to:
+
+ source 'https://rubygems.org'
+
+ gem 'actionpack', '3.0.0.rc'
+ gem 'activemerchant'
+
+The `actionpack 3.0.0.rc` gem has a number of new dependencies,
+and updates the `activesupport` dependency to `= 3.0.0.rc` and
+the `rack` dependency to `~> 1.2.1`.
+
+When you run `bundle install`, Bundler notices that you changed
+the `actionpack` gem, but not the `activemerchant` gem. It
+evaluates the gems currently being used to satisfy its requirements:
+
+ * `activesupport 2.3.8`:
+ also used to satisfy a dependency in `activemerchant`,
+ which is not being updated
+ * `rack ~> 1.1.0`:
+ not currently being used to satisfy another dependency
+
+Because you did not explicitly ask to update `activemerchant`,
+you would not expect it to suddenly stop working after updating
+`actionpack`. However, satisfying the new `activesupport 3.0.0.rc`
+dependency of actionpack requires updating one of its dependencies.
+
+Even though `activemerchant` declares a very loose dependency
+that theoretically matches `activesupport 3.0.0.rc`, Bundler treats
+gems in your Gemfile(5) that have not changed as an atomic unit
+together with their dependencies. In this case, the `activemerchant`
+dependency is treated as `activemerchant 1.7.1 + activesupport 2.3.8`,
+so `bundle install` will report that it cannot update `actionpack`.
+
+To explicitly update `actionpack`, including its dependencies
+which other gems in the Gemfile(5) still depend on, run
+`bundle update actionpack` (see `bundle update(1)`).
+
+`Summary`: In general, after making a change to the Gemfile(5) , you
+should first try to run `bundle install`, which will guarantee that no
+other gem in the Gemfile(5) is impacted by the change. If that
+does not work, run [bundle update(1)](bundle-update.1.html).
+
+## SEE ALSO
+
+* [Gem install docs](http://guides.rubygems.org/rubygems-basics/#installing-gems)
+* [Rubygems signing docs](http://guides.rubygems.org/security/)
diff --git a/man/bundle-list.1 b/man/bundle-list.1
new file mode 100644
index 0000000000..c41948f1b5
--- /dev/null
+++ b/man/bundle-list.1
@@ -0,0 +1,50 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-LIST" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-list\fR \- List all the gems in the bundle
+.
+.SH "SYNOPSIS"
+\fBbundle list\fR [\-\-name\-only] [\-\-paths] [\-\-without\-group=GROUP] [\-\-only\-group=GROUP]
+.
+.SH "DESCRIPTION"
+Prints a list of all the gems in the bundle including their version\.
+.
+.P
+Example:
+.
+.P
+bundle list \-\-name\-only
+.
+.P
+bundle list \-\-paths
+.
+.P
+bundle list \-\-without\-group test
+.
+.P
+bundle list \-\-only\-group dev
+.
+.P
+bundle list \-\-only\-group dev \-\-paths
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-name\-only\fR
+Print only the name of each gem\.
+.
+.TP
+\fB\-\-paths\fR
+Print the path to each gem in the bundle\.
+.
+.TP
+\fB\-\-without\-group\fR
+Print all gems expect from a group\.
+.
+.TP
+\fB\-\-only\-group\fR
+Print gems from a particular group\.
+
diff --git a/man/bundle-list.1.txt b/man/bundle-list.1.txt
new file mode 100644
index 0000000000..aa20d4ebea
--- /dev/null
+++ b/man/bundle-list.1.txt
@@ -0,0 +1,43 @@
+BUNDLE-LIST(1) BUNDLE-LIST(1)
+
+
+
+1mNAME0m
+ 1mbundle-list 22m- List all the gems in the bundle
+
+1mSYNOPSIS0m
+ 1mbundle list 22m[--name-only] [--paths] [--without-group=GROUP]
+ [--only-group=GROUP]
+
+1mDESCRIPTION0m
+ Prints a list of all the gems in the bundle including their version.
+
+ Example:
+
+ bundle list --name-only
+
+ bundle list --paths
+
+ bundle list --without-group test
+
+ bundle list --only-group dev
+
+ bundle list --only-group dev --paths
+
+1mOPTIONS0m
+ 1m--name-only0m
+ Print only the name of each gem.
+
+ 1m--paths0m
+ Print the path to each gem in the bundle.
+
+ 1m--without-group0m
+ Print all gems expect from a group.
+
+ 1m--only-group0m
+ Print gems from a particular group.
+
+
+
+
+ November 2018 BUNDLE-LIST(1)
diff --git a/man/bundle-list.ronn b/man/bundle-list.ronn
new file mode 100644
index 0000000000..120cf5e307
--- /dev/null
+++ b/man/bundle-list.ronn
@@ -0,0 +1,33 @@
+bundle-list(1) -- List all the gems in the bundle
+=========================================================================
+
+## SYNOPSIS
+
+`bundle list` [--name-only] [--paths] [--without-group=GROUP] [--only-group=GROUP]
+
+## DESCRIPTION
+
+Prints a list of all the gems in the bundle including their version.
+
+Example:
+
+bundle list --name-only
+
+bundle list --paths
+
+bundle list --without-group test
+
+bundle list --only-group dev
+
+bundle list --only-group dev --paths
+
+## OPTIONS
+
+* `--name-only`:
+ Print only the name of each gem.
+* `--paths`:
+ Print the path to each gem in the bundle.
+* `--without-group`:
+ Print all gems expect from a group.
+* `--only-group`:
+ Print gems from a particular group.
diff --git a/man/bundle-lock.1 b/man/bundle-lock.1
new file mode 100644
index 0000000000..03b7e8f9cc
--- /dev/null
+++ b/man/bundle-lock.1
@@ -0,0 +1,84 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-LOCK" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-lock\fR \- Creates / Updates a lockfile without installing
+.
+.SH "SYNOPSIS"
+\fBbundle lock\fR [\-\-update] [\-\-local] [\-\-print] [\-\-lockfile=PATH] [\-\-full\-index] [\-\-add\-platform] [\-\-remove\-platform] [\-\-patch] [\-\-minor] [\-\-major] [\-\-strict] [\-\-conservative]
+.
+.SH "DESCRIPTION"
+Lock the gems specified in Gemfile\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-update=<*gems>\fR
+Ignores the existing lockfile\. Resolve then updates lockfile\. Taking a list of gems or updating all gems if no list is given\.
+.
+.TP
+\fB\-\-local\fR
+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 a appropriate platform\-specific gem exists on \fBrubygems\.org\fR it will not be found\.
+.
+.TP
+\fB\-\-print\fR
+Prints the lockfile to STDOUT instead of writing to the file system\.
+.
+.TP
+\fB\-\-lockfile=<path>\fR
+The path where the lockfile should be written to\.
+.
+.TP
+\fB\-\-full\-index\fR
+Fall back to using the single\-file index of all gems\.
+.
+.TP
+\fB\-\-add\-platform\fR
+Add a new platform to the lockfile, re\-resolving for the addition of that platform\.
+.
+.TP
+\fB\-\-remove\-platform\fR
+Remove a platform from the lockfile\.
+.
+.TP
+\fB\-\-patch\fR
+If updating, prefer updating only to next patch version\.
+.
+.TP
+\fB\-\-minor\fR
+If updating, prefer updating only to next minor version\.
+.
+.TP
+\fB\-\-major\fR
+If updating, prefer updating to next major version (default)\.
+.
+.TP
+\fB\-\-strict\fR
+If updating, do not allow any gem to be updated past latest \-\-patch | \-\-minor | \-\-major\.
+.
+.TP
+\fB\-\-conservative\fR
+If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated\.
+.
+.SH "UPDATING ALL GEMS"
+If you run \fBbundle lock\fR with \fB\-\-update\fR option without list of gems, bundler will ignore any previously installed gems and resolve all dependencies again based on the latest versions of all gems available in the sources\.
+.
+.SH "UPDATING A LIST OF GEMS"
+Sometimes, you want to update a single gem in the Gemfile(5), and leave the rest of the gems that you specified locked to the versions in the \fBGemfile\.lock\fR\.
+.
+.P
+For instance, you only want to update \fBnokogiri\fR, run \fBbundle lock \-\-update nokogiri\fR\.
+.
+.P
+Bundler will update \fBnokogiri\fR and any of its dependencies, but leave the rest of the gems that you specified locked to the versions in the \fBGemfile\.lock\fR\.
+.
+.SH "SUPPORTING OTHER PLATFORMS"
+If you want your bundle to support platforms other than the one you\'re running locally, you can run \fBbundle lock \-\-add\-platform PLATFORM\fR to add PLATFORM to the lockfile, force bundler to re\-resolve and consider the new platform when picking gems, all without needing to have a machine that matches PLATFORM handy to install those platform\-specific gems on\.
+.
+.P
+For a full explanation of gem platforms, see \fBgem help platform\fR\.
+.
+.SH "PATCH LEVEL OPTIONS"
+See bundle update(1) \fIbundle\-update\.1\.html\fR for details\.
diff --git a/man/bundle-lock.1.txt b/man/bundle-lock.1.txt
new file mode 100644
index 0000000000..2c757f0201
--- /dev/null
+++ b/man/bundle-lock.1.txt
@@ -0,0 +1,93 @@
+BUNDLE-LOCK(1) BUNDLE-LOCK(1)
+
+
+
+1mNAME0m
+ 1mbundle-lock 22m- Creates / Updates a lockfile without installing
+
+1mSYNOPSIS0m
+ 1mbundle lock 22m[--update] [--local] [--print] [--lockfile=PATH]
+ [--full-index] [--add-platform] [--remove-platform] [--patch] [--minor]
+ [--major] [--strict] [--conservative]
+
+1mDESCRIPTION0m
+ Lock the gems specified in Gemfile.
+
+1mOPTIONS0m
+ 1m--update=<*gems>0m
+ Ignores the existing lockfile. Resolve then updates lockfile.
+ Taking a list of gems or updating all gems if no list is given.
+
+ 1m--local0m
+ Do not attempt to connect to 1mrubygems.org22m. Instead, Bundler will
+ use the gems already present in Rubygems' cache or in 1mven-0m
+ 1mdor/cache22m. Note that if a appropriate platform-specific gem
+ exists on 1mrubygems.org 22mit will not be found.
+
+ 1m--print0m
+ Prints the lockfile to STDOUT instead of writing to the file
+ system.
+
+ 1m--lockfile=<path>0m
+ The path where the lockfile should be written to.
+
+ 1m--full-index0m
+ Fall back to using the single-file index of all gems.
+
+ 1m--add-platform0m
+ Add a new platform to the lockfile, re-resolving for the addi-
+ tion of that platform.
+
+ 1m--remove-platform0m
+ Remove a platform from the lockfile.
+
+ 1m--patch0m
+ If updating, prefer updating only to next patch version.
+
+ 1m--minor0m
+ If updating, prefer updating only to next minor version.
+
+ 1m--major0m
+ If updating, prefer updating to next major version (default).
+
+ 1m--strict0m
+ If updating, do not allow any gem to be updated past latest
+ --patch | --minor | --major.
+
+ 1m--conservative0m
+ If updating, use bundle install conservative update behavior and
+ do not allow shared dependencies to be updated.
+
+1mUPDATING ALL GEMS0m
+ If you run 1mbundle lock 22mwith 1m--update 22moption without list of gems,
+ bundler will ignore any previously installed gems and resolve all
+ dependencies again based on the latest versions of all gems available
+ in the sources.
+
+1mUPDATING A LIST OF GEMS0m
+ Sometimes, you want to update a single gem in the Gemfile(5), and leave
+ the rest of the gems that you specified locked to the versions in the
+ 1mGemfile.lock22m.
+
+ For instance, you only want to update 1mnokogiri22m, run 1mbundle lock0m
+ 1m--update nokogiri22m.
+
+ Bundler will update 1mnokogiri 22mand any of its dependencies, but leave the
+ rest of the gems that you specified locked to the versions in the 1mGem-0m
+ 1mfile.lock22m.
+
+1mSUPPORTING OTHER PLATFORMS0m
+ If you want your bundle to support platforms other than the one you're
+ running locally, you can run 1mbundle lock --add-platform PLATFORM 22mto add
+ PLATFORM to the lockfile, force bundler to re-resolve and consider the
+ new platform when picking gems, all without needing to have a machine
+ that matches PLATFORM handy to install those platform-specific gems on.
+
+ For a full explanation of gem platforms, see 1mgem help platform22m.
+
+1mPATCH LEVEL OPTIONS0m
+ See bundle update(1) 4mbundle-update.1.html24m for details.
+
+
+
+ November 2018 BUNDLE-LOCK(1)
diff --git a/man/bundle-lock.ronn b/man/bundle-lock.ronn
new file mode 100644
index 0000000000..3aa5920f5a
--- /dev/null
+++ b/man/bundle-lock.ronn
@@ -0,0 +1,94 @@
+bundle-lock(1) -- Creates / Updates a lockfile without installing
+=================================================================
+
+## SYNOPSIS
+
+`bundle lock` [--update]
+ [--local]
+ [--print]
+ [--lockfile=PATH]
+ [--full-index]
+ [--add-platform]
+ [--remove-platform]
+ [--patch]
+ [--minor]
+ [--major]
+ [--strict]
+ [--conservative]
+
+## DESCRIPTION
+
+Lock the gems specified in Gemfile.
+
+## OPTIONS
+
+* `--update=<*gems>`:
+ Ignores the existing lockfile. Resolve then updates lockfile. Taking a list
+ of gems or updating all gems if no list is given.
+
+* `--local`:
+ Do not attempt to connect to `rubygems.org`. Instead, Bundler will use the
+ gems already present in Rubygems' cache or in `vendor/cache`. Note that if a
+ appropriate platform-specific gem exists on `rubygems.org` it will not be
+ found.
+
+* `--print`:
+ Prints the lockfile to STDOUT instead of writing to the file system.
+
+* `--lockfile=<path>`:
+ The path where the lockfile should be written to.
+
+* `--full-index`:
+ Fall back to using the single-file index of all gems.
+
+* `--add-platform`:
+ Add a new platform to the lockfile, re-resolving for the addition of that
+ platform.
+
+* `--remove-platform`:
+ Remove a platform from the lockfile.
+
+* `--patch`:
+ If updating, prefer updating only to next patch version.
+
+* `--minor`:
+ If updating, prefer updating only to next minor version.
+
+* `--major`:
+ If updating, prefer updating to next major version (default).
+
+* `--strict`:
+ If updating, do not allow any gem to be updated past latest --patch | --minor | --major.
+
+* `--conservative`:
+ If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated.
+
+## UPDATING ALL GEMS
+
+If you run `bundle lock` with `--update` option without list of gems, bundler will
+ignore any previously installed gems and resolve all dependencies again based
+on the latest versions of all gems available in the sources.
+
+## UPDATING A LIST OF GEMS
+
+Sometimes, you want to update a single gem in the Gemfile(5), and leave the rest of
+the gems that you specified locked to the versions in the `Gemfile.lock`.
+
+For instance, you only want to update `nokogiri`, run `bundle lock --update nokogiri`.
+
+Bundler will update `nokogiri` and any of its dependencies, but leave the rest of the
+gems that you specified locked to the versions in the `Gemfile.lock`.
+
+## SUPPORTING OTHER PLATFORMS
+
+If you want your bundle to support platforms other than the one you're running
+locally, you can run `bundle lock --add-platform PLATFORM` to add PLATFORM to
+the lockfile, force bundler to re-resolve and consider the new platform when
+picking gems, all without needing to have a machine that matches PLATFORM handy
+to install those platform-specific gems on.
+
+For a full explanation of gem platforms, see `gem help platform`.
+
+## PATCH LEVEL OPTIONS
+
+See [bundle update(1)](bundle-update.1.html) for details.
diff --git a/man/bundle-open.1 b/man/bundle-open.1
new file mode 100644
index 0000000000..6301cb1fbc
--- /dev/null
+++ b/man/bundle-open.1
@@ -0,0 +1,32 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-OPEN" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-open\fR \- Opens the source directory for a gem in your bundle
+.
+.SH "SYNOPSIS"
+\fBbundle open\fR [GEM]
+.
+.SH "DESCRIPTION"
+Opens the source directory of the provided GEM in your editor\.
+.
+.P
+For this to work the \fBEDITOR\fR or \fBBUNDLER_EDITOR\fR environment variable has to be set\.
+.
+.P
+Example:
+.
+.IP "" 4
+.
+.nf
+
+bundle open \'rack\'
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Will open the source directory for the \'rack\' gem in your bundle\.
diff --git a/man/bundle-open.1.txt b/man/bundle-open.1.txt
new file mode 100644
index 0000000000..833744ae5e
--- /dev/null
+++ b/man/bundle-open.1.txt
@@ -0,0 +1,29 @@
+BUNDLE-OPEN(1) BUNDLE-OPEN(1)
+
+
+
+1mNAME0m
+ 1mbundle-open 22m- Opens the source directory for a gem in your bundle
+
+1mSYNOPSIS0m
+ 1mbundle open 22m[GEM]
+
+1mDESCRIPTION0m
+ Opens the source directory of the provided GEM in your editor.
+
+ For this to work the 1mEDITOR 22mor 1mBUNDLER_EDITOR 22menvironment variable has
+ to be set.
+
+ Example:
+
+
+
+ bundle open 'rack'
+
+
+
+ Will open the source directory for the 'rack' gem in your bundle.
+
+
+
+ November 2018 BUNDLE-OPEN(1)
diff --git a/man/bundle-open.ronn b/man/bundle-open.ronn
new file mode 100644
index 0000000000..497beac93f
--- /dev/null
+++ b/man/bundle-open.ronn
@@ -0,0 +1,19 @@
+bundle-open(1) -- Opens the source directory for a gem in your bundle
+=====================================================================
+
+## SYNOPSIS
+
+`bundle open` [GEM]
+
+## DESCRIPTION
+
+Opens the source directory of the provided GEM in your editor.
+
+For this to work the `EDITOR` or `BUNDLER_EDITOR` environment variable has to
+be set.
+
+Example:
+
+ bundle open 'rack'
+
+Will open the source directory for the 'rack' gem in your bundle.
diff --git a/man/bundle-outdated.1 b/man/bundle-outdated.1
new file mode 100644
index 0000000000..cde4bb09a1
--- /dev/null
+++ b/man/bundle-outdated.1
@@ -0,0 +1,155 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-OUTDATED" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-outdated\fR \- List installed gems with newer versions available
+.
+.SH "SYNOPSIS"
+\fBbundle outdated\fR [GEM] [\-\-local] [\-\-pre] [\-\-source] [\-\-strict] [\-\-parseable | \-\-porcelain] [\-\-group=GROUP] [\-\-groups] [\-\-update\-strict] [\-\-patch|\-\-minor|\-\-major] [\-\-filter\-major] [\-\-filter\-minor] [\-\-filter\-patch] [\-\-only\-explicit]
+.
+.SH "DESCRIPTION"
+Outdated lists the names and versions of gems that have a newer version available in the given source\. Calling outdated with [GEM [GEM]] will only check for newer versions of the given gems\. Prerelease gems are ignored by default\. If your gems are up to date, Bundler will exit with a status of 0\. Otherwise, it will exit 1\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-local\fR
+Do not attempt to fetch gems remotely and use the gem cache instead\.
+.
+.TP
+\fB\-\-pre\fR
+Check for newer pre\-release gems\.
+.
+.TP
+\fB\-\-source\fR
+Check against a specific source\.
+.
+.TP
+\fB\-\-strict\fR
+Only list newer versions allowed by your Gemfile requirements\.
+.
+.TP
+\fB\-\-parseable\fR, \fB\-\-porcelain\fR
+Use minimal formatting for more parseable output\.
+.
+.TP
+\fB\-\-group\fR
+List gems from a specific group\.
+.
+.TP
+\fB\-\-groups\fR
+List gems organized by groups\.
+.
+.TP
+\fB\-\-update\-strict\fR
+Strict conservative resolution, do not allow any gem to be updated past latest \-\-patch | \-\-minor| \-\-major\.
+.
+.TP
+\fB\-\-minor\fR
+Prefer updating only to next minor version\.
+.
+.TP
+\fB\-\-major\fR
+Prefer updating to next major version (default)\.
+.
+.TP
+\fB\-\-patch\fR
+Prefer updating only to next patch version\.
+.
+.TP
+\fB\-\-filter\-major\fR
+Only list major newer versions\.
+.
+.TP
+\fB\-\-filter\-minor\fR
+Only list minor newer versions\.
+.
+.TP
+\fB\-\-filter\-patch\fR
+Only list patch newer versions\.
+.
+.TP
+\fB\-\-only\-explicit\fR
+Only list gems specified in your Gemfile, not their dependencies\.
+.
+.SH "PATCH LEVEL OPTIONS"
+See bundle update(1) \fIbundle\-update\.1\.html\fR for details\.
+.
+.P
+One difference between the patch level options in \fBbundle update\fR and here is the \fB\-\-strict\fR option\. \fB\-\-strict\fR was already an option on outdated before the patch level options were added\. \fB\-\-strict\fR wasn\'t altered, and the \fB\-\-update\-strict\fR option on \fBoutdated\fR reflects what \fB\-\-strict\fR does on \fBbundle update\fR\.
+.
+.SH "FILTERING OUTPUT"
+The 3 filtering options do not affect the resolution of versions, merely what versions are shown in the output\.
+.
+.P
+If the regular output shows the following:
+.
+.IP "" 4
+.
+.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"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+\fB\-\-filter\-major\fR would only show:
+.
+.IP "" 4
+.
+.nf
+
+* hashie (newest 3\.4\.6, installed 1\.2\.0, requested = 1\.2\.0) in groups "default"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+\fB\-\-filter\-minor\fR would only show:
+.
+.IP "" 4
+.
+.nf
+
+* headless (newest 2\.3\.1, installed 2\.2\.3) in groups "test"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+\fB\-\-filter\-patch\fR would only show:
+.
+.IP "" 4
+.
+.nf
+
+* faker (newest 1\.6\.6, installed 1\.6\.5, requested ~> 1\.4) in groups "development, test"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Filter options can be combined\. \fB\-\-filter\-minor\fR and \fB\-\-filter\-patch\fR would show:
+.
+.IP "" 4
+.
+.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"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Combining all three \fBfilter\fR options would be the same result as providing none of them\.
diff --git a/man/bundle-outdated.1.txt b/man/bundle-outdated.1.txt
new file mode 100644
index 0000000000..00779962fc
--- /dev/null
+++ b/man/bundle-outdated.1.txt
@@ -0,0 +1,131 @@
+BUNDLE-OUTDATED(1) BUNDLE-OUTDATED(1)
+
+
+
+1mNAME0m
+ 1mbundle-outdated 22m- List installed gems with newer versions available
+
+1mSYNOPSIS0m
+ 1mbundle outdated 22m[GEM] [--local] [--pre] [--source] [--strict]
+ [--parseable | --porcelain] [--group=GROUP] [--groups]
+ [--update-strict] [--patch|--minor|--major] [--filter-major] [--fil-
+ ter-minor] [--filter-patch] [--only-explicit]
+
+1mDESCRIPTION0m
+ Outdated lists the names and versions of gems that have a newer version
+ available in the given source. Calling outdated with [GEM [GEM]] will
+ only check for newer versions of the given gems. Prerelease gems are
+ ignored by default. If your gems are up to date, Bundler will exit with
+ a status of 0. Otherwise, it will exit 1.
+
+1mOPTIONS0m
+ 1m--local0m
+ Do not attempt to fetch gems remotely and use the gem cache
+ instead.
+
+ 1m--pre 22mCheck for newer pre-release gems.
+
+ 1m--source0m
+ Check against a specific source.
+
+ 1m--strict0m
+ Only list newer versions allowed by your Gemfile requirements.
+
+ 1m--parseable22m, 1m--porcelain0m
+ Use minimal formatting for more parseable output.
+
+ 1m--group0m
+ List gems from a specific group.
+
+ 1m--groups0m
+ List gems organized by groups.
+
+ 1m--update-strict0m
+ Strict conservative resolution, do not allow any gem to be
+ updated past latest --patch | --minor| --major.
+
+ 1m--minor0m
+ Prefer updating only to next minor version.
+
+ 1m--major0m
+ Prefer updating to next major version (default).
+
+ 1m--patch0m
+ Prefer updating only to next patch version.
+
+ 1m--filter-major0m
+ Only list major newer versions.
+
+ 1m--filter-minor0m
+ Only list minor newer versions.
+
+ 1m--filter-patch0m
+ Only list patch newer versions.
+
+ 1m--only-explicit0m
+ Only list gems specified in your Gemfile, not their dependen-
+ cies.
+
+1mPATCH LEVEL OPTIONS0m
+ See bundle update(1) 4mbundle-update.1.html24m for details.
+
+ One difference between the patch level options in 1mbundle update 22mand
+ here is the 1m--strict 22moption. 1m--strict 22mwas already an option on outdated
+ before the patch level options were added. 1m--strict 22mwasn't altered, and
+ the 1m--update-strict 22moption on 1moutdated 22mreflects what 1m--strict 22mdoes on
+ 1mbundle update22m.
+
+1mFILTERING OUTPUT0m
+ The 3 filtering options do not affect the resolution of versions,
+ merely what versions are shown 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"
+
+
+
+ 1m--filter-major 22mwould only show:
+
+
+
+ * hashie (newest 3.4.6, installed 1.2.0, requested = 1.2.0) in groups "default"
+
+
+
+ 1m--filter-minor 22mwould only show:
+
+
+
+ * headless (newest 2.3.1, installed 2.2.3) in groups "test"
+
+
+
+ 1m--filter-patch 22mwould only show:
+
+
+
+ * faker (newest 1.6.6, installed 1.6.5, requested ~> 1.4) in groups "development, test"
+
+
+
+ Filter options can be combined. 1m--filter-minor 22mand 1m--filter-patch 22mwould
+ 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"
+
+
+
+ Combining all three 1mfilter 22moptions would be the same result as provid-
+ ing none of them.
+
+
+
+ November 2018 BUNDLE-OUTDATED(1)
diff --git a/man/bundle-outdated.ronn b/man/bundle-outdated.ronn
new file mode 100644
index 0000000000..a991d23789
--- /dev/null
+++ b/man/bundle-outdated.ronn
@@ -0,0 +1,111 @@
+bundle-outdated(1) -- List installed gems with newer versions available
+=======================================================================
+
+## SYNOPSIS
+
+`bundle outdated` [GEM] [--local]
+ [--pre]
+ [--source]
+ [--strict]
+ [--parseable | --porcelain]
+ [--group=GROUP]
+ [--groups]
+ [--update-strict]
+ [--patch|--minor|--major]
+ [--filter-major]
+ [--filter-minor]
+ [--filter-patch]
+ [--only-explicit]
+
+## DESCRIPTION
+
+Outdated lists the names and versions of gems that have a newer version available
+in the given source. Calling outdated with [GEM [GEM]] will only check for newer
+versions of the given gems. Prerelease gems are ignored by default. If your gems
+are up to date, Bundler will exit with a status of 0. Otherwise, it will exit 1.
+
+## OPTIONS
+
+* `--local`:
+ Do not attempt to fetch gems remotely and use the gem cache instead.
+
+* `--pre`:
+ Check for newer pre-release gems.
+
+* `--source`:
+ Check against a specific source.
+
+* `--strict`:
+ Only list newer versions allowed by your Gemfile requirements.
+
+* `--parseable`, `--porcelain`:
+ Use minimal formatting for more parseable output.
+
+* `--group`:
+ List gems from a specific group.
+
+* `--groups`:
+ List gems organized by groups.
+
+* `--update-strict`:
+ Strict conservative resolution, do not allow any gem to be updated past latest --patch | --minor| --major.
+
+* `--minor`:
+ Prefer updating only to next minor version.
+
+* `--major`:
+ Prefer updating to next major version (default).
+
+* `--patch`:
+ Prefer updating only to next patch version.
+
+* `--filter-major`:
+ Only list major newer versions.
+
+* `--filter-minor`:
+ Only list minor newer versions.
+
+* `--filter-patch`:
+ Only list patch newer versions.
+
+* `--only-explicit`:
+ Only list gems specified in your Gemfile, not their dependencies.
+
+## PATCH LEVEL OPTIONS
+
+See [bundle update(1)](bundle-update.1.html) for details.
+
+One difference between the patch level options in `bundle update` and here is the `--strict` option.
+`--strict` was already an option on outdated before the patch level options were added. `--strict`
+wasn't altered, and the `--update-strict` option on `outdated` reflects what `--strict` does on
+`bundle update`.
+
+## FILTERING OUTPUT
+
+The 3 filtering options do not affect the resolution of versions, merely what versions are shown
+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"
+
+`--filter-major` would only show:
+
+ * hashie (newest 3.4.6, installed 1.2.0, requested = 1.2.0) in groups "default"
+
+`--filter-minor` would only show:
+
+ * headless (newest 2.3.1, installed 2.2.3) in groups "test"
+
+`--filter-patch` would only show:
+
+ * faker (newest 1.6.6, installed 1.6.5, requested ~> 1.4) in groups "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"
+
+Combining all three `filter` options would be the same result as providing none of them.
diff --git a/man/bundle-package.1 b/man/bundle-package.1
new file mode 100644
index 0000000000..db0447be83
--- /dev/null
+++ b/man/bundle-package.1
@@ -0,0 +1,55 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-PACKAGE" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-package\fR \- Package your needed \fB\.gem\fR files into your application
+.
+.SH "SYNOPSIS"
+\fBbundle package\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\.
+.
+.SH "GIT AND PATH GEMS"
+Since Bundler 1\.2, the \fBbundle package\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\.
+.
+.SH "SUPPORT FOR MULTIPLE PLATFORMS"
+When using gems that have different packages for different platforms, Bundler 1\.8 and newer support 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 package(1) \fIbundle\-package\.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):
+.
+.IP "" 4
+.
+.nf
+
+source "https://rubygems\.org"
+
+gem "nokogiri"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+If you run \fBbundle package\fR under C Ruby, bundler will retrieve the version of \fBnokogiri\fR for the \fB"ruby"\fR platform\. If you deploy to JRuby and run \fBbundle install\fR, bundler is forced to check to see whether a \fB"java"\fR platformed \fBnokogiri\fR exists\.
+.
+.P
+Even though the \fBnokogiri\fR gem for the Ruby platform is \fItechnically\fR acceptable on JRuby, it has a C extension that does not run on JRuby\. As a result, bundler will, by default, still connect to \fBrubygems\.org\fR to check whether it has a version of one of your gems more specific to your platform\.
+.
+.P
+This problem is also not limited to the \fB"java"\fR platform\. A similar (common) problem can happen when developing on Windows and deploying to Linux, or even when developing on OSX and deploying to Linux\.
+.
+.P
+If you know for sure that the gems packaged in \fBvendor/cache\fR are appropriate for the platform you are on, you can run \fBbundle install \-\-local\fR to skip checking for more appropriate gems, and use the ones in \fBvendor/cache\fR\.
+.
+.P
+One way to be sure that you have the right platformed versions of all your gems is to run \fBbundle package\fR on an identical machine and check in the gems\. For instance, you can run \fBbundle package\fR on an identical staging box during your staging process, and check in the \fBvendor/cache\fR before deploying to production\.
+.
+.P
+By default, bundle package(1) \fIbundle\-package\.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 package \-\-no\-install\fR\.
diff --git a/man/bundle-package.1.txt b/man/bundle-package.1.txt
new file mode 100644
index 0000000000..e581e94507
--- /dev/null
+++ b/man/bundle-package.1.txt
@@ -0,0 +1,79 @@
+BUNDLE-PACKAGE(1) BUNDLE-PACKAGE(1)
+
+
+
+1mNAME0m
+ 1mbundle-package 22m- Package your needed 1m.gem 22mfiles into your application
+
+1mSYNOPSIS0m
+ 1mbundle package0m
+
+1mDESCRIPTION0m
+ Copy all of the 1m.gem 22mfiles needed to run the application into the 1mven-0m
+ 1mdor/cache 22mdirectory. In the future, when running [bundle
+ install(1)][bundle-install], use the gems in the cache in preference to
+ the ones on 1mrubygems.org22m.
+
+1mGIT AND PATH GEMS0m
+ Since Bundler 1.2, the 1mbundle package 22mcommand can also package 1m:git 22mand
+ 1m:path 22mdependencies besides .gem files. This needs to be explicitly
+ enabled via the 1m--all 22moption. Once used, the 1m--all 22moption will be
+ remembered.
+
+1mSUPPORT FOR MULTIPLE PLATFORMS0m
+ When using gems that have different packages for different platforms,
+ Bundler 1.8 and newer support caching of gems for other platforms where
+ the Gemfile has been resolved (i.e. present in the lockfile) in 1mven-0m
+ 1mdor/cache22m. This needs to be enabled via the 1m--all-platforms 22moption.
+ This setting will be remembered in your local bundler configuration.
+
+1mREMOTE FETCHING0m
+ By default, if you run 1mbundle install(1)22m](bundle-install.1.html) after
+ running bundle package(1) 4mbundle-package.1.html24m, bundler will still
+ connect to 1mrubygems.org 22mto check whether a platform-specific gem exists
+ for any of the gems in 1mvendor/cache22m.
+
+ For instance, consider this Gemfile(5):
+
+
+
+ source "https://rubygems.org"
+
+ gem "nokogiri"
+
+
+
+ If you run 1mbundle package 22munder C Ruby, bundler will retrieve the ver-
+ sion of 1mnokogiri 22mfor the 1m"ruby" 22mplatform. If you deploy to JRuby and
+ run 1mbundle install22m, bundler is forced to check to see whether a 1m"java"0m
+ platformed 1mnokogiri 22mexists.
+
+ Even though the 1mnokogiri 22mgem for the Ruby platform is 4mtechnically0m
+ acceptable on JRuby, it has a C extension that does not run on JRuby.
+ As a result, bundler will, by default, still connect to 1mrubygems.org 22mto
+ check whether it has a version of one of your gems more specific to
+ your platform.
+
+ This problem is also not limited to the 1m"java" 22mplatform. A similar
+ (common) problem can happen when developing on Windows and deploying to
+ Linux, or even when developing on OSX and deploying to Linux.
+
+ If you know for sure that the gems packaged in 1mvendor/cache 22mare appro-
+ priate for the platform you are on, you can run 1mbundle install --local0m
+ to skip checking for more appropriate gems, and use the ones in 1mven-0m
+ 1mdor/cache22m.
+
+ One way to be sure that you have the right platformed versions of all
+ your gems is to run 1mbundle package 22mon an identical machine and check in
+ the gems. For instance, you can run 1mbundle package 22mon an identical
+ staging box during your staging process, and check in the 1mvendor/cache0m
+ before deploying to production.
+
+ By default, bundle package(1) 4mbundle-package.1.html24m fetches and also
+ installs the gems to the default location. To package the dependencies
+ to 1mvendor/cache 22mwithout installing them to the local install location,
+ you can run 1mbundle package --no-install22m.
+
+
+
+ November 2018 BUNDLE-PACKAGE(1)
diff --git a/man/bundle-package.ronn b/man/bundle-package.ronn
new file mode 100644
index 0000000000..bc137374da
--- /dev/null
+++ b/man/bundle-package.ronn
@@ -0,0 +1,72 @@
+bundle-package(1) -- Package your needed `.gem` files into your application
+===========================================================================
+
+## SYNOPSIS
+
+`bundle package`
+
+## 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],
+use the gems in the cache in preference to the ones on `rubygems.org`.
+
+## GIT AND PATH GEMS
+
+Since Bundler 1.2, the `bundle package` command can also package `:git` and
+`:path` dependencies besides .gem files. This needs to be explicitly enabled
+via the `--all` option. Once used, the `--all` option will be remembered.
+
+## SUPPORT FOR MULTIPLE PLATFORMS
+
+When using gems that have different packages for different platforms, Bundler
+1.8 and newer support caching of gems for other platforms where the Gemfile
+has been resolved (i.e. present in the lockfile) in `vendor/cache`. This needs
+to be enabled via the `--all-platforms` option. This setting will be remembered
+in your local bundler configuration.
+
+## REMOTE FETCHING
+
+By default, if you run `bundle install(1)`](bundle-install.1.html) after running
+[bundle package(1)](bundle-package.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`.
+
+For instance, consider this Gemfile(5):
+
+ source "https://rubygems.org"
+
+ gem "nokogiri"
+
+If you run `bundle package` under C Ruby, bundler will retrieve
+the version of `nokogiri` for the `"ruby"` platform. If you deploy
+to JRuby and run `bundle install`, bundler is forced to check to
+see whether a `"java"` platformed `nokogiri` exists.
+
+Even though the `nokogiri` gem for the Ruby platform is
+_technically_ acceptable on JRuby, it has a C extension
+that does not run on JRuby. As a result, bundler will, by default,
+still connect to `rubygems.org` to check whether it has a version
+of one of your gems more specific to your platform.
+
+This problem is also not limited to the `"java"` platform.
+A similar (common) problem can happen when developing on Windows
+and deploying to Linux, or even when developing on OSX and
+deploying to Linux.
+
+If you know for sure that the gems packaged in `vendor/cache`
+are appropriate for the platform you are on, you can run
+`bundle install --local` to skip checking for more appropriate
+gems, and use the ones in `vendor/cache`.
+
+One way to be sure that you have the right platformed versions
+of all your gems is to run `bundle package` on an identical
+machine and check in the gems. For instance, you can run
+`bundle package` on an identical staging box during your
+staging process, and check in the `vendor/cache` before
+deploying to production.
+
+By default, [bundle package(1)](bundle-package.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 package --no-install`.
diff --git a/man/bundle-platform.1 b/man/bundle-platform.1
new file mode 100644
index 0000000000..94b5e13cc1
--- /dev/null
+++ b/man/bundle-platform.1
@@ -0,0 +1,61 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-PLATFORM" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-platform\fR \- Displays platform compatibility information
+.
+.SH "SYNOPSIS"
+\fBbundle platform\fR [\-\-ruby]
+.
+.SH "DESCRIPTION"
+\fBplatform\fR will display information from your Gemfile, Gemfile\.lock, and Ruby VM about your platform\.
+.
+.P
+For instance, using this Gemfile(5):
+.
+.IP "" 4
+.
+.nf
+
+source "https://rubygems\.org"
+
+ruby "1\.9\.3"
+
+gem "rack"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+If you run \fBbundle platform\fR on Ruby 1\.9\.3, it will display the following output:
+.
+.IP "" 4
+.
+.nf
+
+Your platform is: x86_64\-linux
+
+Your app has gems that work on these platforms:
+* ruby
+
+Your Gemfile specifies a Ruby version requirement:
+* ruby 1\.9\.3
+
+Your current platform satisfies the Ruby version requirement\.
+.
+.fi
+.
+.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\.
+.
+.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)\.
+
diff --git a/man/bundle-platform.1.txt b/man/bundle-platform.1.txt
new file mode 100644
index 0000000000..26079cc3d5
--- /dev/null
+++ b/man/bundle-platform.1.txt
@@ -0,0 +1,57 @@
+BUNDLE-PLATFORM(1) BUNDLE-PLATFORM(1)
+
+
+
+1mNAME0m
+ 1mbundle-platform 22m- Displays platform compatibility information
+
+1mSYNOPSIS0m
+ 1mbundle platform 22m[--ruby]
+
+1mDESCRIPTION0m
+ 1mplatform 22mwill display 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"
+
+ gem "rack"
+
+
+
+ If you run 1mbundle platform 22mon Ruby 1.9.3, it will display the following
+ output:
+
+
+
+ Your platform is: x86_64-linux
+
+ Your app has gems that work on these platforms:
+ * ruby
+
+ Your Gemfile specifies a Ruby version requirement:
+ * ruby 1.9.3
+
+ Your current platform satisfies the Ruby version requirement.
+
+
+
+ 1mplatform 22mwill list all the platforms in your 1mGemfile.lock 22mas well as
+ the 1mruby 22mdirective if applicable from your Gemfile(5). It will also let
+ you know if the 1mruby 22mdirective requirement has been met. If 1mruby 22mdirec-
+ tive doesn't match the running Ruby VM, it will tell you what part does
+ not.
+
+1mOPTIONS0m
+ 1m--ruby 22mIt will display the ruby directive information, so you don't
+ have to parse it from the Gemfile(5).
+
+
+
+
+ November 2018 BUNDLE-PLATFORM(1)
diff --git a/man/bundle-platform.ronn b/man/bundle-platform.ronn
new file mode 100644
index 0000000000..b5d3283fb6
--- /dev/null
+++ b/man/bundle-platform.ronn
@@ -0,0 +1,42 @@
+bundle-platform(1) -- Displays platform compatibility information
+=================================================================
+
+## SYNOPSIS
+
+`bundle platform` [--ruby]
+
+## DESCRIPTION
+
+`platform` will display 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"
+
+ gem "rack"
+
+If you run `bundle platform` on Ruby 1.9.3, it will display the following output:
+
+ Your platform is: x86_64-linux
+
+ Your app has gems that work on these platforms:
+ * ruby
+
+ Your Gemfile specifies a Ruby version requirement:
+ * ruby 1.9.3
+
+ 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
+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.
+
+## OPTIONS
+
+* `--ruby`:
+ It will display the ruby directive information, so you don't have to
+ parse it from the Gemfile(5).
diff --git a/man/bundle-pristine.1 b/man/bundle-pristine.1
new file mode 100644
index 0000000000..d3881ade3d
--- /dev/null
+++ b/man/bundle-pristine.1
@@ -0,0 +1,34 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-PRISTINE" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-pristine\fR \- Restores installed gems to their pristine condition
+.
+.SH "SYNOPSIS"
+\fBbundle pristine\fR
+.
+.SH "DESCRIPTION"
+\fBpristine\fR restores the installed gems in the bundle to their pristine condition using the local gem cache from RubyGems\. For git gems, a forced checkout will be performed\.
+.
+.P
+For further explanation, \fBbundle pristine\fR ignores unpacked files on disk\. In other words, this command utilizes the local \fB\.gem\fR cache or the gem\'s git repository as if one were installing from scratch\.
+.
+.P
+Note: the Bundler gem cannot be restored to its original state with \fBpristine\fR\. One also cannot use \fBbundle pristine\fR on gems with a \'path\' option in the Gemfile, because bundler has no original copy it can restore from\.
+.
+.P
+When is it practical to use \fBbundle pristine\fR?
+.
+.P
+It comes in handy when a developer is debugging a gem\. \fBbundle pristine\fR is a great way to get rid of experimental changes to a gem that one may not want\.
+.
+.P
+Why use \fBbundle pristine\fR over \fBgem pristine \-\-all\fR?
+.
+.P
+Both commands are very similar\. For context: \fBbundle pristine\fR, without arguments, cleans all gems from the lockfile\. Meanwhile, \fBgem pristine \-\-all\fR cleans all installed gems for that Ruby version\.
+.
+.P
+If a developer forgets which gems in their project they might have been debugging, the Rubygems \fBgem pristine [GEMNAME]\fR command may be inconvenient\. One can avoid waiting for \fBgem pristine \-\-all\fR, and instead run \fBbundle pristine\fR\.
diff --git a/man/bundle-pristine.1.txt b/man/bundle-pristine.1.txt
new file mode 100644
index 0000000000..a46f3c830c
--- /dev/null
+++ b/man/bundle-pristine.1.txt
@@ -0,0 +1,44 @@
+BUNDLE-PRISTINE(1) BUNDLE-PRISTINE(1)
+
+
+
+1mNAME0m
+ 1mbundle-pristine 22m- Restores installed gems to their pristine condition
+
+1mSYNOPSIS0m
+ 1mbundle pristine0m
+
+1mDESCRIPTION0m
+ 1mpristine 22mrestores the installed gems in the bundle to their pristine
+ condition using the local gem cache from RubyGems. For git gems, a
+ forced checkout will be performed.
+
+ For further explanation, 1mbundle pristine 22mignores unpacked files on
+ disk. In other words, this command utilizes the local 1m.gem 22mcache or the
+ gem's git repository as if one were installing from scratch.
+
+ Note: the Bundler gem cannot be restored to its original state with
+ 1mpristine22m. One also cannot use 1mbundle pristine 22mon gems with a 'path'
+ option in the Gemfile, because bundler has no original copy it can
+ restore from.
+
+ When is it practical to use 1mbundle pristine22m?
+
+ It comes in handy when a developer is debugging a gem. 1mbundle pristine0m
+ is a great way to get rid of experimental changes to a gem that one may
+ not want.
+
+ Why use 1mbundle pristine 22mover 1mgem pristine --all22m?
+
+ Both commands are very similar. For context: 1mbundle pristine22m, without
+ arguments, cleans all gems from the lockfile. Meanwhile, 1mgem pristine0m
+ 1m--all 22mcleans all installed gems for that Ruby version.
+
+ If a developer forgets which gems in their project they might have been
+ debugging, the Rubygems 1mgem pristine [GEMNAME] 22mcommand may be inconve-
+ nient. One can avoid waiting for 1mgem pristine --all22m, and instead run
+ 1mbundle pristine22m.
+
+
+
+ November 2018 BUNDLE-PRISTINE(1)
diff --git a/man/bundle-pristine.ronn b/man/bundle-pristine.ronn
new file mode 100644
index 0000000000..e2d6b6a348
--- /dev/null
+++ b/man/bundle-pristine.ronn
@@ -0,0 +1,34 @@
+bundle-pristine(1) -- Restores installed gems to their pristine condition
+===========================================================================
+
+## SYNOPSIS
+
+`bundle pristine`
+
+## DESCRIPTION
+
+`pristine` restores the installed gems in the bundle to their pristine condition
+using the local gem cache from RubyGems. For git gems, a forced checkout will be performed.
+
+For further explanation, `bundle pristine` ignores unpacked files on disk. In other
+words, this command utilizes the local `.gem` cache or the gem's git repository
+as if one were installing from scratch.
+
+Note: the Bundler gem cannot be restored to its original state with `pristine`.
+One also cannot use `bundle pristine` on gems with a 'path' option in the Gemfile,
+because bundler has no original copy it can restore from.
+
+When is it practical to use `bundle pristine`?
+
+It comes in handy when a developer is debugging a gem. `bundle pristine` is a
+great way to get rid of experimental changes to a gem that one may not want.
+
+Why use `bundle pristine` over `gem pristine --all`?
+
+Both commands are very similar.
+For context: `bundle pristine`, without arguments, cleans all gems from the lockfile.
+Meanwhile, `gem pristine --all` cleans all installed gems for that Ruby version.
+
+If a developer forgets which gems in their project they might
+have been debugging, the Rubygems `gem pristine [GEMNAME]` command may be inconvenient.
+One can avoid waiting for `gem pristine --all`, and instead run `bundle pristine`.
diff --git a/man/bundle-remove.1 b/man/bundle-remove.1
new file mode 100644
index 0000000000..8232ab2887
--- /dev/null
+++ b/man/bundle-remove.1
@@ -0,0 +1,31 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-REMOVE" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-remove\fR \- Removes gems from the Gemfile
+.
+.SH "SYNOPSIS"
+\fBbundle remove [GEM [GEM \.\.\.]] [\-\-install]\fR
+.
+.SH "DESCRIPTION"
+Removes the given gems from the Gemfile while ensuring that the resulting Gemfile is still valid\. If a gem cannot be removed, a warning is printed\. If a gem is already absent from the Gemfile, and error is raised\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-install\fR
+Runs \fBbundle install\fR after the given gems have been removed from the Gemfile, which ensures that both the lockfile and the installed gems on disk are also updated to remove the given gem(s)\.
+.
+.P
+Example:
+.
+.P
+bundle remove rails
+.
+.P
+bundle remove rails rack
+.
+.P
+bundle remove rails rack \-\-install
diff --git a/man/bundle-remove.1.txt b/man/bundle-remove.1.txt
new file mode 100644
index 0000000000..2cf2ba6010
--- /dev/null
+++ b/man/bundle-remove.1.txt
@@ -0,0 +1,34 @@
+BUNDLE-REMOVE(1) BUNDLE-REMOVE(1)
+
+
+
+1mNAME0m
+ 1mbundle-remove 22m- Removes gems from the Gemfile
+
+1mSYNOPSIS0m
+ 1mbundle remove [GEM [GEM ...]] [--install]0m
+
+1mDESCRIPTION0m
+ Removes the given gems from the Gemfile while ensuring that the result-
+ ing Gemfile is still valid. If a gem cannot be removed, a warning is
+ printed. If a gem is already absent from the Gemfile, and error is
+ raised.
+
+1mOPTIONS0m
+ 1m--install0m
+ Runs 1mbundle install 22mafter the given gems have been removed from
+ the Gemfile, which ensures that both the lockfile and the
+ installed gems on disk are also updated to remove the given
+ gem(s).
+
+ Example:
+
+ bundle remove rails
+
+ bundle remove rails rack
+
+ bundle remove rails rack --install
+
+
+
+ November 2018 BUNDLE-REMOVE(1)
diff --git a/man/bundle-remove.ronn b/man/bundle-remove.ronn
new file mode 100644
index 0000000000..40a239b4a2
--- /dev/null
+++ b/man/bundle-remove.ronn
@@ -0,0 +1,23 @@
+bundle-remove(1) -- Removes gems from the Gemfile
+===========================================================================
+
+## SYNOPSIS
+
+`bundle remove [GEM [GEM ...]] [--install]`
+
+## DESCRIPTION
+
+Removes the given gems from the Gemfile while ensuring that the resulting Gemfile is still valid. If a gem cannot be removed, a warning is printed. If a gem is already absent from the Gemfile, and error is raised.
+
+## OPTIONS
+
+* `--install`:
+ Runs `bundle install` after the given gems have been removed from the Gemfile, which ensures that both the lockfile and the installed gems on disk are also updated to remove the given gem(s).
+
+Example:
+
+bundle remove rails
+
+bundle remove rails rack
+
+bundle remove rails rack --install
diff --git a/man/bundle-show.1 b/man/bundle-show.1
new file mode 100644
index 0000000000..72ce37aedd
--- /dev/null
+++ b/man/bundle-show.1
@@ -0,0 +1,23 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-SHOW" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-show\fR \- Shows all the gems in your bundle, or the path to a gem
+.
+.SH "SYNOPSIS"
+\fBbundle show\fR [GEM] [\-\-paths]
+.
+.SH "DESCRIPTION"
+Without the [GEM] option, \fBshow\fR will print a list of the names and versions of all gems that are required by your [\fBGemfile(5)\fR][Gemfile(5)], sorted by name\.
+.
+.P
+Calling show with [GEM] will list the exact location of that gem on your machine\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-paths\fR
+List the paths of all gems that are required by your [\fBGemfile(5)\fR][Gemfile(5)], sorted by gem name\.
+
diff --git a/man/bundle-show.1.txt b/man/bundle-show.1.txt
new file mode 100644
index 0000000000..542ee55f78
--- /dev/null
+++ b/man/bundle-show.1.txt
@@ -0,0 +1,27 @@
+BUNDLE-SHOW(1) BUNDLE-SHOW(1)
+
+
+
+1mNAME0m
+ 1mbundle-show 22m- Shows all the gems in your bundle, or the path to a gem
+
+1mSYNOPSIS0m
+ 1mbundle show 22m[GEM] [--paths]
+
+1mDESCRIPTION0m
+ Without the [GEM] option, 1mshow 22mwill print a list of the names and ver-
+ sions of all gems that are required by your [1mGemfile(5)22m][Gemfile(5)],
+ sorted by name.
+
+ Calling show with [GEM] will list the exact location of that gem on
+ your machine.
+
+1mOPTIONS0m
+ 1m--paths0m
+ List the paths of all gems that are required by your [1mGem-0m
+ 1mfile(5)22m][Gemfile(5)], sorted by gem name.
+
+
+
+
+ November 2018 BUNDLE-SHOW(1)
diff --git a/man/bundle-show.ronn b/man/bundle-show.ronn
new file mode 100644
index 0000000000..a6a59a1445
--- /dev/null
+++ b/man/bundle-show.ronn
@@ -0,0 +1,21 @@
+bundle-show(1) -- Shows all the gems in your bundle, or the path to a gem
+=========================================================================
+
+## SYNOPSIS
+
+`bundle show` [GEM]
+ [--paths]
+
+## DESCRIPTION
+
+Without the [GEM] option, `show` will print a list of the names and versions of
+all gems that are required by your [`Gemfile(5)`][Gemfile(5)], sorted by name.
+
+Calling show with [GEM] will list the exact location of that gem on your
+machine.
+
+## OPTIONS
+
+* `--paths`:
+ List the paths of all gems that are required by your [`Gemfile(5)`][Gemfile(5)],
+ sorted by gem name.
diff --git a/man/bundle-update.1 b/man/bundle-update.1
new file mode 100644
index 0000000000..1fe205cff0
--- /dev/null
+++ b/man/bundle-update.1
@@ -0,0 +1,394 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-UPDATE" "1" "December 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-update\fR \- Update your gems to the latest available versions
+.
+.SH "SYNOPSIS"
+\fBbundle update\fR \fI*gems\fR [\-\-all] [\-\-group=NAME] [\-\-source=NAME] [\-\-local] [\-\-ruby] [\-\-bundler[=VERSION]] [\-\-full\-index] [\-\-jobs=JOBS] [\-\-quiet] [\-\-force] [\-\-patch|\-\-minor|\-\-major] [\-\-strict] [\-\-conservative]
+.
+.SH "DESCRIPTION"
+Update the gems specified (all gems, if \fB\-\-all\fR flag is used), ignoring the previously installed gems specified in the \fBGemfile\.lock\fR\. In general, you should use bundle install(1) \fIbundle\-install\.1\.html\fR to install the same exact gems and versions across machines\.
+.
+.P
+You would use \fBbundle update\fR to explicitly update the version of a gem\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-all\fR
+Update all gems specified in Gemfile\.
+.
+.TP
+\fB\-\-group=<name>\fR, \fB\-g=[<name>]\fR
+Only update the gems in the specified group\. For instance, you can update all gems in the development group with \fBbundle update \-\-group development\fR\. You can also call \fBbundle update rails \-\-group test\fR to update the rails gem and all gems in the test group, for example\.
+.
+.TP
+\fB\-\-source=<name>\fR
+The name of a \fB:git\fR or \fB:path\fR source used in the Gemfile(5)\. For instance, with a \fB:git\fR source of \fBhttp://github\.com/rails/rails\.git\fR, you would call \fBbundle update \-\-source rails\fR
+.
+.TP
+\fB\-\-local\fR
+Do not attempt to fetch gems remotely and use the gem cache instead\.
+.
+.TP
+\fB\-\-ruby\fR
+Update the locked version of Ruby to the current version of Ruby\.
+.
+.TP
+\fB\-\-bundler\fR
+Update the locked version of bundler to the invoked bundler version\.
+.
+.TP
+\fB\-\-full\-index\fR
+Fall back to using the single\-file index of all gems\.
+.
+.TP
+\fB\-\-jobs=[<number>]\fR, \fB\-j[<number>]\fR
+Specify the number of jobs to run in parallel\. The default is \fB1\fR\.
+.
+.TP
+\fB\-\-retry=[<number>]\fR
+Retry failed network or git requests for \fInumber\fR times\.
+.
+.TP
+\fB\-\-quiet\fR
+Only output warnings and errors\.
+.
+.TP
+\fB\-\-force\fR
+Force downloading every gem\. \fB\-\-redownload\fR is an alias of this option\.
+.
+.TP
+\fB\-\-patch\fR
+Prefer updating only to next patch version\.
+.
+.TP
+\fB\-\-minor\fR
+Prefer updating only to next minor version\.
+.
+.TP
+\fB\-\-major\fR
+Prefer updating to next major version (default)\.
+.
+.TP
+\fB\-\-strict\fR
+Do not allow any gem to be updated past latest \fB\-\-patch\fR | \fB\-\-minor\fR | \fB\-\-major\fR\.
+.
+.TP
+\fB\-\-conservative\fR
+Use bundle install conservative update behavior and do not allow shared dependencies to be updated\.
+.
+.SH "UPDATING ALL GEMS"
+If you run \fBbundle update \-\-all\fR, bundler will ignore any previously installed gems and resolve all dependencies again based on the latest versions of all gems available in the sources\.
+.
+.P
+Consider the following Gemfile(5):
+.
+.IP "" 4
+.
+.nf
+
+source "https://rubygems\.org"
+
+gem "rails", "3\.0\.0\.rc"
+gem "nokogiri"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+When you run bundle install(1) \fIbundle\-install\.1\.html\fR the first time, bundler will resolve all of the dependencies, all the way down, and install what you need:
+.
+.IP "" 4
+.
+.nf
+
+Fetching gem metadata from https://rubygems\.org/\.\.\.\.\.\.\.\.\.
+Resolving dependencies\.\.\.
+Installing builder 2\.1\.2
+Installing abstract 1\.0\.0
+Installing rack 1\.2\.8
+Using bundler 1\.7\.6
+Installing rake 10\.4\.0
+Installing polyglot 0\.3\.5
+Installing mime\-types 1\.25\.1
+Installing i18n 0\.4\.2
+Installing mini_portile 0\.6\.1
+Installing tzinfo 0\.3\.42
+Installing rack\-mount 0\.6\.14
+Installing rack\-test 0\.5\.7
+Installing treetop 1\.4\.15
+Installing thor 0\.14\.6
+Installing activesupport 3\.0\.0\.rc
+Installing erubis 2\.6\.6
+Installing activemodel 3\.0\.0\.rc
+Installing arel 0\.4\.0
+Installing mail 2\.2\.20
+Installing activeresource 3\.0\.0\.rc
+Installing actionpack 3\.0\.0\.rc
+Installing activerecord 3\.0\.0\.rc
+Installing actionmailer 3\.0\.0\.rc
+Installing railties 3\.0\.0\.rc
+Installing rails 3\.0\.0\.rc
+Installing nokogiri 1\.6\.5
+
+Bundle complete! 2 Gemfile dependencies, 26 gems total\.
+Use `bundle show [gemname]` to see where a bundled gem is installed\.
+.
+.fi
+.
+.IP "" 0
+.
+.P
+As you can see, even though you have two gems in the Gemfile(5), your application needs 26 different gems in order to run\. Bundler remembers the exact versions it installed in \fBGemfile\.lock\fR\. The next time you run bundle install(1) \fIbundle\-install\.1\.html\fR, bundler skips the dependency resolution and installs the same gems as it installed last time\.
+.
+.P
+After checking in the \fBGemfile\.lock\fR into version control and cloning it on another machine, running bundle install(1) \fIbundle\-install\.1\.html\fR will \fIstill\fR install the gems that you installed last time\. You don\'t need to worry that a new release of \fBerubis\fR or \fBmail\fR changes the gems you use\.
+.
+.P
+However, from time to time, you might want to update the gems you are using to the newest versions that still match the gems in your Gemfile(5)\.
+.
+.P
+To do this, run \fBbundle update \-\-all\fR, which will ignore the \fBGemfile\.lock\fR, and resolve all the dependencies again\. Keep in mind that this process can result in a significantly different set of the 25 gems, based on the requirements of new gems that the gem authors released since the last time you ran \fBbundle update \-\-all\fR\.
+.
+.SH "UPDATING A LIST OF GEMS"
+Sometimes, you want to update a single gem in the Gemfile(5), and leave the rest of the gems that you specified locked to the versions in the \fBGemfile\.lock\fR\.
+.
+.P
+For instance, in the scenario above, imagine that \fBnokogiri\fR releases version \fB1\.4\.4\fR, and you want to update it \fIwithout\fR updating Rails and all of its dependencies\. To do this, run \fBbundle update nokogiri\fR\.
+.
+.P
+Bundler will update \fBnokogiri\fR and any of its dependencies, but leave alone Rails and its dependencies\.
+.
+.SH "OVERLAPPING DEPENDENCIES"
+Sometimes, multiple gems declared in your Gemfile(5) are satisfied by the same second\-level dependency\. For instance, consider the case of \fBthin\fR and \fBrack\-perftools\-profiler\fR\.
+.
+.IP "" 4
+.
+.nf
+
+source "https://rubygems\.org"
+
+gem "thin"
+gem "rack\-perftools\-profiler"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+The \fBthin\fR gem depends on \fBrack >= 1\.0\fR, while \fBrack\-perftools\-profiler\fR depends on \fBrack ~> 1\.0\fR\. If you run bundle install, you get:
+.
+.IP "" 4
+.
+.nf
+
+Fetching source index for https://rubygems\.org/
+Installing daemons (1\.1\.0)
+Installing eventmachine (0\.12\.10) with native extensions
+Installing open4 (1\.0\.1)
+Installing perftools\.rb (0\.4\.7) with native extensions
+Installing rack (1\.2\.1)
+Installing rack\-perftools_profiler (0\.0\.2)
+Installing thin (1\.2\.7) with native extensions
+Using bundler (1\.0\.0\.rc\.3)
+.
+.fi
+.
+.IP "" 0
+.
+.P
+In this case, the two gems have their own set of dependencies, but they share \fBrack\fR in common\. If you run \fBbundle update thin\fR, bundler will update \fBdaemons\fR, \fBeventmachine\fR and \fBrack\fR, which are dependencies of \fBthin\fR, but not \fBopen4\fR or \fBperftools\.rb\fR, which are dependencies of \fBrack\-perftools_profiler\fR\. Note that \fBbundle update thin\fR will update \fBrack\fR even though it\'s \fIalso\fR a dependency of \fBrack\-perftools_profiler\fR\.
+.
+.P
+In short, by default, when you update a gem using \fBbundle update\fR, bundler will update all dependencies of that gem, including those that are also dependencies of another gem\.
+.
+.P
+To prevent updating shared dependencies, prior to version 1\.14 the only option was the \fBCONSERVATIVE UPDATING\fR behavior in bundle install(1) \fIbundle\-install\.1\.html\fR:
+.
+.P
+In this scenario, updating the \fBthin\fR version manually in the Gemfile(5), and then running bundle install(1) \fIbundle\-install\.1\.html\fR will only update \fBdaemons\fR and \fBeventmachine\fR, but not \fBrack\fR\. For more information, see the \fBCONSERVATIVE UPDATING\fR section of bundle install(1) \fIbundle\-install\.1\.html\fR\.
+.
+.P
+Starting with 1\.14, specifying the \fB\-\-conservative\fR option will also prevent shared dependencies from being updated\.
+.
+.SH "PATCH LEVEL OPTIONS"
+Version 1\.14 introduced 4 patch\-level options that will influence how gem versions are resolved\. One of the following options can be used: \fB\-\-patch\fR, \fB\-\-minor\fR or \fB\-\-major\fR\. \fB\-\-strict\fR can be added to further influence resolution\.
+.
+.TP
+\fB\-\-patch\fR
+Prefer updating only to next patch version\.
+.
+.TP
+\fB\-\-minor\fR
+Prefer updating only to next minor version\.
+.
+.TP
+\fB\-\-major\fR
+Prefer updating to next major version (default)\.
+.
+.TP
+\fB\-\-strict\fR
+Do not allow any gem to be updated past latest \fB\-\-patch\fR | \fB\-\-minor\fR | \fB\-\-major\fR\.
+.
+.P
+When Bundler is resolving what versions to use to satisfy declared requirements in the Gemfile or in parent gems, it looks up all available versions, filters out any versions that don\'t satisfy the requirement, and then, by default, sorts them from newest to oldest, considering them in that order\.
+.
+.P
+Providing one of the patch level options (e\.g\. \fB\-\-patch\fR) changes the sort order of the satisfying versions, causing Bundler to consider the latest \fB\-\-patch\fR or \fB\-\-minor\fR version available before other versions\. Note that versions outside the stated patch level could still be resolved to if necessary to find a suitable dependency graph\.
+.
+.P
+For example, if gem \'foo\' is locked at 1\.0\.2, with no gem requirement defined in the Gemfile, and versions 1\.0\.3, 1\.0\.4, 1\.1\.0, 1\.1\.1, 2\.0\.0 all exist, the default order of preference by default (\fB\-\-major\fR) will be "2\.0\.0, 1\.1\.1, 1\.1\.0, 1\.0\.4, 1\.0\.3, 1\.0\.2"\.
+.
+.P
+If the \fB\-\-patch\fR option is used, the order of preference will change to "1\.0\.4, 1\.0\.3, 1\.0\.2, 1\.1\.1, 1\.1\.0, 2\.0\.0"\.
+.
+.P
+If the \fB\-\-minor\fR option is used, the order of preference will change to "1\.1\.1, 1\.1\.0, 1\.0\.4, 1\.0\.3, 1\.0\.2, 2\.0\.0"\.
+.
+.P
+Combining the \fB\-\-strict\fR option with any of the patch level options will remove any versions beyond the scope of the patch level option, to ensure that no gem is updated that far\.
+.
+.P
+To continue the previous example, if both \fB\-\-patch\fR and \fB\-\-strict\fR options are used, the available versions for resolution would be "1\.0\.4, 1\.0\.3, 1\.0\.2"\. If \fB\-\-minor\fR and \fB\-\-strict\fR are used, it would be "1\.1\.1, 1\.1\.0, 1\.0\.4, 1\.0\.3, 1\.0\.2"\.
+.
+.P
+Gem requirements as defined in the Gemfile will still be the first determining factor for what versions are available\. If the gem requirement for \fBfoo\fR in the Gemfile is \'~> 1\.0\', that will accomplish the same thing as providing the \fB\-\-minor\fR and \fB\-\-strict\fR options\.
+.
+.SH "PATCH LEVEL EXAMPLES"
+Given the following gem specifications:
+.
+.IP "" 4
+.
+.nf
+
+foo 1\.4\.3, requires: ~> bar 2\.0
+foo 1\.4\.4, requires: ~> bar 2\.0
+foo 1\.4\.5, requires: ~> bar 2\.1
+foo 1\.5\.0, requires: ~> bar 2\.1
+foo 1\.5\.1, requires: ~> bar 3\.0
+bar with versions 2\.0\.3, 2\.0\.4, 2\.1\.0, 2\.1\.1, 3\.0\.0
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Gemfile:
+.
+.IP "" 4
+.
+.nf
+
+gem \'foo\'
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Gemfile\.lock:
+.
+.IP "" 4
+.
+.nf
+
+foo (1\.4\.3)
+ bar (~> 2\.0)
+bar (2\.0\.3)
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Cases:
+.
+.IP "" 4
+.
+.nf
+
+# Command Line Result
+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
+1 bundle update \-\-patch \'foo 1\.4\.5\', \'bar 2\.1\.1\'
+2 bundle update \-\-patch foo \'foo 1\.4\.5\', \'bar 2\.1\.1\'
+3 bundle update \-\-minor \'foo 1\.5\.1\', \'bar 3\.0\.0\'
+4 bundle update \-\-minor \-\-strict \'foo 1\.5\.0\', \'bar 2\.1\.1\'
+5 bundle update \-\-patch \-\-strict \'foo 1\.4\.4\', \'bar 2\.0\.4\'
+.
+.fi
+.
+.IP "" 0
+.
+.P
+In case 1, bar is upgraded to 2\.1\.1, a minor version increase, because the dependency from foo 1\.4\.5 required it\.
+.
+.P
+In case 2, only foo is requested to be unlocked, but bar is also allowed to move because it\'s not a declared dependency in the Gemfile\.
+.
+.P
+In case 3, bar goes up a whole major release, because a minor increase is preferred now for foo, and when it goes to 1\.5\.1, it requires 3\.0\.0 of bar\.
+.
+.P
+In case 4, foo is preferred up to a minor version, but 1\.5\.1 won\'t work because the \-\-strict flag removes bar 3\.0\.0 from consideration since it\'s a major increment\.
+.
+.P
+In case 5, both foo and bar have any minor or major increments removed from consideration because of the \-\-strict flag, so the most they can move is up to 1\.4\.4 and 2\.0\.4\.
+.
+.SH "RECOMMENDED WORKFLOW"
+In general, when working with an application managed with bundler, you should use the following workflow:
+.
+.IP "\(bu" 4
+After you create your Gemfile(5) for the first time, run
+.
+.IP
+$ bundle install
+.
+.IP "\(bu" 4
+Check the resulting \fBGemfile\.lock\fR into version control
+.
+.IP
+$ git add Gemfile\.lock
+.
+.IP "\(bu" 4
+When checking out this repository on another development machine, run
+.
+.IP
+$ bundle install
+.
+.IP "\(bu" 4
+When checking out this repository on a deployment machine, run
+.
+.IP
+$ bundle install \-\-deployment
+.
+.IP "\(bu" 4
+After changing the Gemfile(5) to reflect a new or update dependency, run
+.
+.IP
+$ bundle install
+.
+.IP "\(bu" 4
+Make sure to check the updated \fBGemfile\.lock\fR into version control
+.
+.IP
+$ git add Gemfile\.lock
+.
+.IP "\(bu" 4
+If bundle install(1) \fIbundle\-install\.1\.html\fR reports a conflict, manually update the specific gems that you changed in the Gemfile(5)
+.
+.IP
+$ bundle update rails thin
+.
+.IP "\(bu" 4
+If you want to update all the gems to the latest possible versions that still match the gems listed in the Gemfile(5), run
+.
+.IP
+$ bundle update \-\-all
+.
+.IP "" 0
+
diff --git a/man/bundle-update.1.txt b/man/bundle-update.1.txt
new file mode 100644
index 0000000000..1a206e2c49
--- /dev/null
+++ b/man/bundle-update.1.txt
@@ -0,0 +1,391 @@
+BUNDLE-UPDATE(1) BUNDLE-UPDATE(1)
+
+
+
+1mNAME0m
+ 1mbundle-update 22m- Update your gems to the latest available versions
+
+1mSYNOPSIS0m
+ 1mbundle update 4m22m*gems24m [--all] [--group=NAME] [--source=NAME] [--local]
+ [--ruby] [--bundler[=VERSION]] [--full-index] [--jobs=JOBS] [--quiet]
+ [--force] [--patch|--minor|--major] [--strict] [--conservative]
+
+1mDESCRIPTION0m
+ Update the gems specified (all gems, if 1m--all 22mflag is used), ignoring
+ the previously installed gems specified in the 1mGemfile.lock22m. In gen-
+ eral, you should use bundle install(1) 4mbundle-install.1.html24m to install
+ the same exact gems and versions across machines.
+
+ You would use 1mbundle update 22mto explicitly update the version of a gem.
+
+1mOPTIONS0m
+ 1m--all 22mUpdate all gems specified in Gemfile.
+
+ 1m--group=<name>22m, 1m-g=[<name>]0m
+ Only update the gems in the specified group. For instance, you
+ can update all gems in the development group with 1mbundle update0m
+ 1m--group development22m. You can also call 1mbundle update rails0m
+ 1m--group test 22mto update the rails gem and all gems in the test
+ group, for example.
+
+ 1m--source=<name>0m
+ The name of a 1m:git 22mor 1m:path 22msource used in the Gemfile(5). For
+ instance, with a 1m:git 22msource of
+ 1mhttp://github.com/rails/rails.git22m, you would call 1mbundle update0m
+ 1m--source rails0m
+
+ 1m--local0m
+ Do not attempt to fetch gems remotely and use the gem cache
+ instead.
+
+ 1m--ruby 22mUpdate the locked version of Ruby to the current version of
+ Ruby.
+
+ 1m--bundler0m
+ Update the locked version of bundler to the invoked bundler ver-
+ sion.
+
+ 1m--full-index0m
+ Fall back to using the single-file index of all gems.
+
+ 1m--jobs=[<number>]22m, 1m-j[<number>]0m
+ Specify the number of jobs to run in parallel. The default is 1m122m.
+
+ 1m--retry=[<number>]0m
+ Retry failed network or git requests for 4mnumber24m times.
+
+ 1m--quiet0m
+ Only output warnings and errors.
+
+ 1m--force0m
+ Force downloading every gem. 1m--redownload 22mis an alias of this
+ option.
+
+ 1m--patch0m
+ Prefer updating only to next patch version.
+
+ 1m--minor0m
+ Prefer updating only to next minor version.
+
+ 1m--major0m
+ Prefer updating to next major version (default).
+
+ 1m--strict0m
+ Do not allow any gem to be updated past latest 1m--patch 22m| 1m--minor0m
+ | 1m--major22m.
+
+ 1m--conservative0m
+ Use bundle install conservative update behavior and do not allow
+ shared dependencies to be updated.
+
+1mUPDATING ALL GEMS0m
+ If you run 1mbundle update --all22m, bundler will ignore any previously
+ installed gems and resolve all dependencies again based on the latest
+ versions of all gems available in the sources.
+
+ Consider the following Gemfile(5):
+
+
+
+ source "https://rubygems.org"
+
+ gem "rails", "3.0.0.rc"
+ gem "nokogiri"
+
+
+
+ When you run bundle install(1) 4mbundle-install.1.html24m the first time,
+ bundler will resolve all of the dependencies, all the way down, and
+ install what you need:
+
+
+
+ Fetching gem metadata from https://rubygems.org/.........
+ Resolving dependencies...
+ Installing builder 2.1.2
+ Installing abstract 1.0.0
+ Installing rack 1.2.8
+ Using bundler 1.7.6
+ Installing rake 10.4.0
+ Installing polyglot 0.3.5
+ Installing mime-types 1.25.1
+ Installing i18n 0.4.2
+ Installing mini_portile 0.6.1
+ Installing tzinfo 0.3.42
+ Installing rack-mount 0.6.14
+ Installing rack-test 0.5.7
+ Installing treetop 1.4.15
+ Installing thor 0.14.6
+ Installing activesupport 3.0.0.rc
+ Installing erubis 2.6.6
+ Installing activemodel 3.0.0.rc
+ Installing arel 0.4.0
+ Installing mail 2.2.20
+ Installing activeresource 3.0.0.rc
+ Installing actionpack 3.0.0.rc
+ Installing activerecord 3.0.0.rc
+ Installing actionmailer 3.0.0.rc
+ Installing railties 3.0.0.rc
+ Installing rails 3.0.0.rc
+ Installing nokogiri 1.6.5
+
+ Bundle complete! 2 Gemfile dependencies, 26 gems total.
+ Use `bundle show [gemname]` to see where a bundled gem is installed.
+
+
+
+ As you can see, even though you have two gems in the Gemfile(5), your
+ application needs 26 different gems in order to run. Bundler remembers
+ the exact versions it installed in 1mGemfile.lock22m. The next time you run
+ bundle install(1) 4mbundle-install.1.html24m, bundler skips the dependency
+ resolution and installs the same gems as it installed last time.
+
+ After checking in the 1mGemfile.lock 22minto version control and cloning it
+ on another machine, running bundle install(1) 4mbundle-install.1.html0m
+ will 4mstill24m install the gems that you installed last time. You don't
+ need to worry that a new release of 1merubis 22mor 1mmail 22mchanges the gems you
+ use.
+
+ However, from time to time, you might want to update the gems you are
+ using to the newest versions that still match the gems in your Gem-
+ file(5).
+
+ To do this, run 1mbundle update --all22m, which will ignore the 1mGem-0m
+ 1mfile.lock22m, and resolve all the dependencies again. Keep in mind that
+ this process can result in a significantly different set of the 25
+ gems, based on the requirements of new gems that the gem authors
+ released since the last time you ran 1mbundle update --all22m.
+
+1mUPDATING A LIST OF GEMS0m
+ Sometimes, you want to update a single gem in the Gemfile(5), and leave
+ the rest of the gems that you specified locked to the versions in the
+ 1mGemfile.lock22m.
+
+ For instance, in the scenario above, imagine that 1mnokogiri 22mreleases
+ version 1m1.4.422m, and you want to update it 4mwithout24m updating Rails and all
+ of its dependencies. To do this, run 1mbundle update nokogiri22m.
+
+ Bundler will update 1mnokogiri 22mand any of its dependencies, but leave
+ alone Rails and its dependencies.
+
+1mOVERLAPPING DEPENDENCIES0m
+ Sometimes, multiple gems declared in your Gemfile(5) are satisfied by
+ the same second-level dependency. For instance, consider the case of
+ 1mthin 22mand 1mrack-perftools-profiler22m.
+
+
+
+ source "https://rubygems.org"
+
+ gem "thin"
+ gem "rack-perftools-profiler"
+
+
+
+ The 1mthin 22mgem depends on 1mrack >= 1.022m, while 1mrack-perftools-profiler0m
+ depends on 1mrack ~> 1.022m. If you run bundle install, you get:
+
+
+
+ Fetching source index for https://rubygems.org/
+ Installing daemons (1.1.0)
+ Installing eventmachine (0.12.10) with native extensions
+ Installing open4 (1.0.1)
+ Installing perftools.rb (0.4.7) with native extensions
+ Installing rack (1.2.1)
+ Installing rack-perftools_profiler (0.0.2)
+ Installing thin (1.2.7) with native extensions
+ Using bundler (1.0.0.rc.3)
+
+
+
+ In this case, the two gems have their own set of dependencies, but they
+ share 1mrack 22min common. If you run 1mbundle update thin22m, bundler will
+ update 1mdaemons22m, 1meventmachine 22mand 1mrack22m, which are dependencies of 1mthin22m,
+ but not 1mopen4 22mor 1mperftools.rb22m, which are dependencies of
+ 1mrack-perftools_profiler22m. Note that 1mbundle update thin 22mwill update 1mrack0m
+ even though it's 4malso24m a dependency of 1mrack-perftools_profiler22m.
+
+ In short, by default, when you update a gem using 1mbundle update22m,
+ bundler will update all dependencies of that gem, including those that
+ are also dependencies of another gem.
+
+ To prevent updating shared dependencies, prior to version 1.14 the only
+ option was the 1mCONSERVATIVE UPDATING 22mbehavior in bundle install(1) 4mbun-0m
+ 4mdle-install.1.html24m:
+
+ In this scenario, updating the 1mthin 22mversion manually in the Gemfile(5),
+ and then running bundle install(1) 4mbundle-install.1.html24m will only
+ update 1mdaemons 22mand 1meventmachine22m, but not 1mrack22m. For more information,
+ see the 1mCONSERVATIVE UPDATING 22msection of bundle install(1) 4mbun-0m
+ 4mdle-install.1.html24m.
+
+ Starting with 1.14, specifying the 1m--conservative 22moption will also pre-
+ vent shared dependencies from being updated.
+
+1mPATCH LEVEL OPTIONS0m
+ Version 1.14 introduced 4 patch-level options that will influence how
+ gem versions are resolved. One of the following options can be used:
+ 1m--patch22m, 1m--minor 22mor 1m--major22m. 1m--strict 22mcan be added to further influence
+ resolution.
+
+ 1m--patch0m
+ Prefer updating only to next patch version.
+
+ 1m--minor0m
+ Prefer updating only to next minor version.
+
+ 1m--major0m
+ Prefer updating to next major version (default).
+
+ 1m--strict0m
+ Do not allow any gem to be updated past latest 1m--patch 22m| 1m--minor0m
+ | 1m--major22m.
+
+ When Bundler is resolving what versions to use to satisfy declared
+ requirements in the Gemfile or in parent gems, it looks up all avail-
+ able versions, filters out any versions that don't satisfy the require-
+ ment, and then, by default, sorts them from newest to oldest, consider-
+ ing them in that order.
+
+ Providing one of the patch level options (e.g. 1m--patch22m) changes the
+ sort order of the satisfying versions, causing Bundler to consider the
+ latest 1m--patch 22mor 1m--minor 22mversion available before other versions. Note
+ that versions outside the stated patch level could still be resolved to
+ if necessary to find a suitable dependency graph.
+
+ For example, if gem 'foo' is locked at 1.0.2, with no gem requirement
+ defined in the Gemfile, and versions 1.0.3, 1.0.4, 1.1.0, 1.1.1, 2.0.0
+ all exist, the default order of preference by default (1m--major22m) will be
+ "2.0.0, 1.1.1, 1.1.0, 1.0.4, 1.0.3, 1.0.2".
+
+ If the 1m--patch 22moption is used, the order of preference will change to
+ "1.0.4, 1.0.3, 1.0.2, 1.1.1, 1.1.0, 2.0.0".
+
+ If the 1m--minor 22moption is used, the order of preference will change to
+ "1.1.1, 1.1.0, 1.0.4, 1.0.3, 1.0.2, 2.0.0".
+
+ Combining the 1m--strict 22moption with any of the patch level options will
+ remove any versions beyond the scope of the patch level option, to
+ ensure that no gem is updated that far.
+
+ To continue the previous example, if both 1m--patch 22mand 1m--strict 22moptions
+ are used, the available versions for resolution would be "1.0.4, 1.0.3,
+ 1.0.2". If 1m--minor 22mand 1m--strict 22mare used, it would be "1.1.1, 1.1.0,
+ 1.0.4, 1.0.3, 1.0.2".
+
+ Gem requirements as defined in the Gemfile will still be the first
+ determining factor for what versions are available. If the gem require-
+ ment for 1mfoo 22min the Gemfile is '~> 1.0', that will accomplish the same
+ thing as providing the 1m--minor 22mand 1m--strict 22moptions.
+
+1mPATCH LEVEL EXAMPLES0m
+ Given the following gem specifications:
+
+
+
+ foo 1.4.3, requires: ~> bar 2.0
+ foo 1.4.4, requires: ~> bar 2.0
+ foo 1.4.5, requires: ~> bar 2.1
+ foo 1.5.0, requires: ~> bar 2.1
+ foo 1.5.1, requires: ~> bar 3.0
+ bar with versions 2.0.3, 2.0.4, 2.1.0, 2.1.1, 3.0.0
+
+
+
+ Gemfile:
+
+
+
+ gem 'foo'
+
+
+
+ Gemfile.lock:
+
+
+
+ foo (1.4.3)
+ bar (~> 2.0)
+ bar (2.0.3)
+
+
+
+ Cases:
+
+
+
+ # Command Line Result
+ ------------------------------------------------------------
+ 1 bundle update --patch 'foo 1.4.5', 'bar 2.1.1'
+ 2 bundle update --patch foo 'foo 1.4.5', 'bar 2.1.1'
+ 3 bundle update --minor 'foo 1.5.1', 'bar 3.0.0'
+ 4 bundle update --minor --strict 'foo 1.5.0', 'bar 2.1.1'
+ 5 bundle update --patch --strict 'foo 1.4.4', 'bar 2.0.4'
+
+
+
+ In case 1, bar is upgraded to 2.1.1, a minor version increase, because
+ the dependency from foo 1.4.5 required it.
+
+ In case 2, only foo is requested to be unlocked, but bar is also
+ allowed to move because it's not a declared dependency in the Gemfile.
+
+ In case 3, bar goes up a whole major release, because a minor increase
+ is preferred now for foo, and when it goes to 1.5.1, it requires 3.0.0
+ of bar.
+
+ In case 4, foo is preferred up to a minor version, but 1.5.1 won't work
+ because the --strict flag removes bar 3.0.0 from consideration since
+ it's a major increment.
+
+ In case 5, both foo and bar have any minor or major increments removed
+ from consideration because of the --strict flag, so the most they can
+ move is up to 1.4.4 and 2.0.4.
+
+1mRECOMMENDED WORKFLOW0m
+ In general, when working with an application managed with bundler, you
+ should use the following workflow:
+
+ o After you create your Gemfile(5) for the first time, run
+
+ $ bundle install
+
+ o Check the resulting 1mGemfile.lock 22minto version control
+
+ $ git add Gemfile.lock
+
+ o When checking out this repository on another development machine,
+ run
+
+ $ bundle install
+
+ o When checking out this repository on a deployment machine, run
+
+ $ bundle install --deployment
+
+ o After changing the Gemfile(5) to reflect a new or update depen-
+ dency, run
+
+ $ bundle install
+
+ o Make sure to check the updated 1mGemfile.lock 22minto version control
+
+ $ git add Gemfile.lock
+
+ o If bundle install(1) 4mbundle-install.1.html24m reports a conflict, man-
+ ually update the specific gems that you changed in the Gemfile(5)
+
+ $ bundle update rails thin
+
+ o If you want to update all the gems to the latest possible versions
+ that still match the gems listed in the Gemfile(5), run
+
+ $ bundle update --all
+
+
+
+
+
+
+ December 2018 BUNDLE-UPDATE(1)
diff --git a/man/bundle-update.ronn b/man/bundle-update.ronn
new file mode 100644
index 0000000000..481bb5b14e
--- /dev/null
+++ b/man/bundle-update.ronn
@@ -0,0 +1,350 @@
+bundle-update(1) -- Update your gems to the latest available versions
+=====================================================================
+
+## SYNOPSIS
+
+`bundle update` <*gems> [--all]
+ [--group=NAME]
+ [--source=NAME]
+ [--local]
+ [--ruby]
+ [--bundler[=VERSION]]
+ [--full-index]
+ [--jobs=JOBS]
+ [--quiet]
+ [--force]
+ [--patch|--minor|--major]
+ [--strict]
+ [--conservative]
+
+## DESCRIPTION
+
+Update the gems specified (all gems, if `--all` flag is used), ignoring
+the previously installed gems specified in the `Gemfile.lock`. In
+general, you should use [bundle install(1)](bundle-install.1.html) to install the same exact
+gems and versions across machines.
+
+You would use `bundle update` to explicitly update the version of a
+gem.
+
+## OPTIONS
+
+* `--all`:
+ Update all gems specified in Gemfile.
+
+* `--group=<name>`, `-g=[<name>]`:
+ Only update the gems in the specified group. For instance, you can update all gems
+ in the development group with `bundle update --group development`. You can also
+ call `bundle update rails --group test` to update the rails gem and all gems in
+ the test group, for example.
+
+* `--source=<name>`:
+ The name of a `:git` or `:path` source used in the Gemfile(5). For
+ instance, with a `:git` source of `http://github.com/rails/rails.git`,
+ you would call `bundle update --source rails`
+
+* `--local`:
+ Do not attempt to fetch gems remotely and use the gem cache instead.
+
+* `--ruby`:
+ Update the locked version of Ruby to the current version of Ruby.
+
+* `--bundler`:
+ Update the locked version of bundler to the invoked bundler version.
+
+* `--full-index`:
+ Fall back to using the single-file index of all gems.
+
+* `--jobs=[<number>]`, `-j[<number>]`:
+ Specify the number of jobs to run in parallel. The default is `1`.
+
+* `--retry=[<number>]`:
+ Retry failed network or git requests for <number> times.
+
+* `--quiet`:
+ Only output warnings and errors.
+
+* `--force`:
+ Force downloading every gem. `--redownload` is an alias of this option.
+
+* `--patch`:
+ Prefer updating only to next patch version.
+
+* `--minor`:
+ Prefer updating only to next minor version.
+
+* `--major`:
+ Prefer updating to next major version (default).
+
+* `--strict`:
+ Do not allow any gem to be updated past latest `--patch` | `--minor` | `--major`.
+
+* `--conservative`:
+ Use bundle install conservative update behavior and do not allow shared dependencies to be updated.
+
+## UPDATING ALL GEMS
+
+If you run `bundle update --all`, bundler will ignore
+any previously installed gems and resolve all dependencies again
+based on the latest versions of all gems available in the sources.
+
+Consider the following Gemfile(5):
+
+ source "https://rubygems.org"
+
+ gem "rails", "3.0.0.rc"
+ gem "nokogiri"
+
+When you run [bundle install(1)](bundle-install.1.html) the first time, bundler will resolve
+all of the dependencies, all the way down, and install what you need:
+
+ Fetching gem metadata from https://rubygems.org/.........
+ Resolving dependencies...
+ Installing builder 2.1.2
+ Installing abstract 1.0.0
+ Installing rack 1.2.8
+ Using bundler 1.7.6
+ Installing rake 10.4.0
+ Installing polyglot 0.3.5
+ Installing mime-types 1.25.1
+ Installing i18n 0.4.2
+ Installing mini_portile 0.6.1
+ Installing tzinfo 0.3.42
+ Installing rack-mount 0.6.14
+ Installing rack-test 0.5.7
+ Installing treetop 1.4.15
+ Installing thor 0.14.6
+ Installing activesupport 3.0.0.rc
+ Installing erubis 2.6.6
+ Installing activemodel 3.0.0.rc
+ Installing arel 0.4.0
+ Installing mail 2.2.20
+ Installing activeresource 3.0.0.rc
+ Installing actionpack 3.0.0.rc
+ Installing activerecord 3.0.0.rc
+ Installing actionmailer 3.0.0.rc
+ Installing railties 3.0.0.rc
+ Installing rails 3.0.0.rc
+ Installing nokogiri 1.6.5
+
+ Bundle complete! 2 Gemfile dependencies, 26 gems total.
+ Use `bundle show [gemname]` to see where a bundled gem is installed.
+
+As you can see, even though you have two gems in the Gemfile(5), your application
+needs 26 different gems in order to run. Bundler remembers the exact versions
+it installed in `Gemfile.lock`. The next time you run [bundle install(1)](bundle-install.1.html), bundler skips
+the dependency resolution and installs the same gems as it installed last time.
+
+After checking in the `Gemfile.lock` into version control and cloning it on another
+machine, running [bundle install(1)](bundle-install.1.html) will _still_ install the gems that you installed
+last time. You don't need to worry that a new release of `erubis` or `mail` changes
+the gems you use.
+
+However, from time to time, you might want to update the gems you are using to the
+newest versions that still match the gems in your Gemfile(5).
+
+To do this, run `bundle update --all`, which will ignore the `Gemfile.lock`, and resolve
+all the dependencies again. Keep in mind that this process can result in a significantly
+different set of the 25 gems, based on the requirements of new gems that the gem
+authors released since the last time you ran `bundle update --all`.
+
+## UPDATING A LIST OF GEMS
+
+Sometimes, you want to update a single gem in the Gemfile(5), and leave the rest of the
+gems that you specified locked to the versions in the `Gemfile.lock`.
+
+For instance, in the scenario above, imagine that `nokogiri` releases version `1.4.4`, and
+you want to update it _without_ updating Rails and all of its dependencies. To do this,
+run `bundle update nokogiri`.
+
+Bundler will update `nokogiri` and any of its dependencies, but leave alone Rails and
+its dependencies.
+
+## OVERLAPPING DEPENDENCIES
+
+Sometimes, multiple gems declared in your Gemfile(5) are satisfied by the same
+second-level dependency. For instance, consider the case of `thin` and
+`rack-perftools-profiler`.
+
+ source "https://rubygems.org"
+
+ gem "thin"
+ gem "rack-perftools-profiler"
+
+The `thin` gem depends on `rack >= 1.0`, while `rack-perftools-profiler` depends
+on `rack ~> 1.0`. If you run bundle install, you get:
+
+ Fetching source index for https://rubygems.org/
+ Installing daemons (1.1.0)
+ Installing eventmachine (0.12.10) with native extensions
+ Installing open4 (1.0.1)
+ Installing perftools.rb (0.4.7) with native extensions
+ Installing rack (1.2.1)
+ Installing rack-perftools_profiler (0.0.2)
+ Installing thin (1.2.7) with native extensions
+ Using bundler (1.0.0.rc.3)
+
+In this case, the two gems have their own set of dependencies, but they share
+`rack` in common. If you run `bundle update thin`, bundler will update `daemons`,
+`eventmachine` and `rack`, which are dependencies of `thin`, but not `open4` or
+`perftools.rb`, which are dependencies of `rack-perftools_profiler`. Note that
+`bundle update thin` will update `rack` even though it's _also_ a dependency of
+`rack-perftools_profiler`.
+
+In short, by default, when you update a gem using `bundle update`, bundler will
+update all dependencies of that gem, including those that are also dependencies
+of another gem.
+
+To prevent updating shared dependencies, prior to version 1.14 the only option
+was the `CONSERVATIVE UPDATING` behavior in [bundle install(1)](bundle-install.1.html):
+
+In this scenario, updating the `thin` version manually in the Gemfile(5),
+and then running [bundle install(1)](bundle-install.1.html) will only update `daemons` and `eventmachine`,
+but not `rack`. For more information, see the `CONSERVATIVE UPDATING` section
+of [bundle install(1)](bundle-install.1.html).
+
+Starting with 1.14, specifying the `--conservative` option will also prevent shared
+dependencies from being updated.
+
+## PATCH LEVEL OPTIONS
+
+Version 1.14 introduced 4 patch-level options that will influence how gem
+versions are resolved. One of the following options can be used: `--patch`,
+`--minor` or `--major`. `--strict` can be added to further influence resolution.
+
+* `--patch`:
+ Prefer updating only to next patch version.
+
+* `--minor`:
+ Prefer updating only to next minor version.
+
+* `--major`:
+ Prefer updating to next major version (default).
+
+* `--strict`:
+ Do not allow any gem to be updated past latest `--patch` | `--minor` | `--major`.
+
+When Bundler is resolving what versions to use to satisfy declared
+requirements in the Gemfile or in parent gems, it looks up all
+available versions, filters out any versions that don't satisfy
+the requirement, and then, by default, sorts them from newest to
+oldest, considering them in that order.
+
+Providing one of the patch level options (e.g. `--patch`) changes the
+sort order of the satisfying versions, causing Bundler to consider the
+latest `--patch` or `--minor` version available before other versions.
+Note that versions outside the stated patch level could still be
+resolved to if necessary to find a suitable dependency graph.
+
+For example, if gem 'foo' is locked at 1.0.2, with no gem requirement
+defined in the Gemfile, and versions 1.0.3, 1.0.4, 1.1.0, 1.1.1, 2.0.0
+all exist, the default order of preference by default (`--major`) will
+be "2.0.0, 1.1.1, 1.1.0, 1.0.4, 1.0.3, 1.0.2".
+
+If the `--patch` option is used, the order of preference will change to
+"1.0.4, 1.0.3, 1.0.2, 1.1.1, 1.1.0, 2.0.0".
+
+If the `--minor` option is used, the order of preference will change to
+"1.1.1, 1.1.0, 1.0.4, 1.0.3, 1.0.2, 2.0.0".
+
+Combining the `--strict` option with any of the patch level options
+will remove any versions beyond the scope of the patch level option,
+to ensure that no gem is updated that far.
+
+To continue the previous example, if both `--patch` and `--strict`
+options are used, the available versions for resolution would be
+"1.0.4, 1.0.3, 1.0.2". If `--minor` and `--strict` are used, it would
+be "1.1.1, 1.1.0, 1.0.4, 1.0.3, 1.0.2".
+
+Gem requirements as defined in the Gemfile will still be the first
+determining factor for what versions are available. If the gem
+requirement for `foo` in the Gemfile is '~> 1.0', that will accomplish
+the same thing as providing the `--minor` and `--strict` options.
+
+## PATCH LEVEL EXAMPLES
+
+Given the following gem specifications:
+
+ foo 1.4.3, requires: ~> bar 2.0
+ foo 1.4.4, requires: ~> bar 2.0
+ foo 1.4.5, requires: ~> bar 2.1
+ foo 1.5.0, requires: ~> bar 2.1
+ foo 1.5.1, requires: ~> bar 3.0
+ bar with versions 2.0.3, 2.0.4, 2.1.0, 2.1.1, 3.0.0
+
+Gemfile:
+
+ gem 'foo'
+
+Gemfile.lock:
+
+ foo (1.4.3)
+ bar (~> 2.0)
+ bar (2.0.3)
+
+Cases:
+
+ # Command Line Result
+ ------------------------------------------------------------
+ 1 bundle update --patch 'foo 1.4.5', 'bar 2.1.1'
+ 2 bundle update --patch foo 'foo 1.4.5', 'bar 2.1.1'
+ 3 bundle update --minor 'foo 1.5.1', 'bar 3.0.0'
+ 4 bundle update --minor --strict 'foo 1.5.0', 'bar 2.1.1'
+ 5 bundle update --patch --strict 'foo 1.4.4', 'bar 2.0.4'
+
+In case 1, bar is upgraded to 2.1.1, a minor version increase, because
+the dependency from foo 1.4.5 required it.
+
+In case 2, only foo is requested to be unlocked, but bar is also
+allowed to move because it's not a declared dependency in the Gemfile.
+
+In case 3, bar goes up a whole major release, because a minor increase
+is preferred now for foo, and when it goes to 1.5.1, it requires 3.0.0
+of bar.
+
+In case 4, foo is preferred up to a minor version, but 1.5.1 won't work
+because the --strict flag removes bar 3.0.0 from consideration since
+it's a major increment.
+
+In case 5, both foo and bar have any minor or major increments removed
+from consideration because of the --strict flag, so the most they can
+move is up to 1.4.4 and 2.0.4.
+
+## RECOMMENDED WORKFLOW
+
+In general, when working with an application managed with bundler, you should
+use the following workflow:
+
+* After you create your Gemfile(5) for the first time, run
+
+ $ bundle install
+
+* Check the resulting `Gemfile.lock` into version control
+
+ $ git add Gemfile.lock
+
+* When checking out this repository on another development machine, run
+
+ $ bundle install
+
+* When checking out this repository on a deployment machine, run
+
+ $ bundle install --deployment
+
+* After changing the Gemfile(5) to reflect a new or update dependency, run
+
+ $ bundle install
+
+* Make sure to check the updated `Gemfile.lock` into version control
+
+ $ git add Gemfile.lock
+
+* If [bundle install(1)](bundle-install.1.html) reports a conflict, manually update the specific
+ gems that you changed in the Gemfile(5)
+
+ $ bundle update rails thin
+
+* If you want to update all the gems to the latest possible versions that
+ still match the gems listed in the Gemfile(5), run
+
+ $ bundle update --all
diff --git a/man/bundle-viz.1 b/man/bundle-viz.1
new file mode 100644
index 0000000000..70ad6835ee
--- /dev/null
+++ b/man/bundle-viz.1
@@ -0,0 +1,39 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE\-VIZ" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\-viz\fR \- Generates a visual dependency graph for your Gemfile
+.
+.SH "SYNOPSIS"
+\fBbundle viz\fR [\-\-file=FILE] [\-\-format=FORMAT] [\-\-requirements] [\-\-version] [\-\-without=GROUP GROUP]
+.
+.SH "DESCRIPTION"
+\fBviz\fR generates a PNG file of the current \fBGemfile(5)\fR as a dependency graph\. \fBviz\fR requires the ruby\-graphviz gem (and its dependencies)\.
+.
+.P
+The associated gems must also be installed via \fBbundle install(1)\fR \fIbundle\-install\.1\.html\fR\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-file\fR, \fB\-f\fR
+The name to use for the generated file\. See \fB\-\-format\fR option
+.
+.TP
+\fB\-\-format\fR, \fB\-F\fR
+This is output format option\. Supported format is png, jpg, svg, dot \.\.\.
+.
+.TP
+\fB\-\-requirements\fR, \fB\-R\fR
+Set to show the version of each required dependency\.
+.
+.TP
+\fB\-\-version\fR, \fB\-v\fR
+Set to show each gem version\.
+.
+.TP
+\fB\-\-without\fR, \fB\-W\fR
+Exclude gems that are part of the specified named group\.
+
diff --git a/man/bundle-viz.1.txt b/man/bundle-viz.1.txt
new file mode 100644
index 0000000000..ada88d89c6
--- /dev/null
+++ b/man/bundle-viz.1.txt
@@ -0,0 +1,39 @@
+BUNDLE-VIZ(1) BUNDLE-VIZ(1)
+
+
+
+1mNAME0m
+ 1mbundle-viz 22m- Generates a visual dependency graph for your Gemfile
+
+1mSYNOPSIS0m
+ 1mbundle viz 22m[--file=FILE] [--format=FORMAT] [--requirements] [--version]
+ [--without=GROUP GROUP]
+
+1mDESCRIPTION0m
+ 1mviz 22mgenerates a PNG file of the current 1mGemfile(5) 22mas a dependency
+ graph. 1mviz 22mrequires the ruby-graphviz gem (and its dependencies).
+
+ The associated gems must also be installed via 1mbundle install(1) 4m22mbun-0m
+ 4mdle-install.1.html24m.
+
+1mOPTIONS0m
+ 1m--file22m, 1m-f0m
+ The name to use for the generated file. See 1m--format 22moption
+
+ 1m--format22m, 1m-F0m
+ This is output format option. Supported format is png, jpg, svg,
+ dot ...
+
+ 1m--requirements22m, 1m-R0m
+ Set to show the version of each required dependency.
+
+ 1m--version22m, 1m-v0m
+ Set to show each gem version.
+
+ 1m--without22m, 1m-W0m
+ Exclude gems that are part of the specified named group.
+
+
+
+
+ November 2018 BUNDLE-VIZ(1)
diff --git a/man/bundle-viz.ronn b/man/bundle-viz.ronn
new file mode 100644
index 0000000000..701df5415e
--- /dev/null
+++ b/man/bundle-viz.ronn
@@ -0,0 +1,30 @@
+bundle-viz(1) -- Generates a visual dependency graph for your Gemfile
+=====================================================================
+
+## SYNOPSIS
+
+`bundle viz` [--file=FILE]
+ [--format=FORMAT]
+ [--requirements]
+ [--version]
+ [--without=GROUP GROUP]
+
+## DESCRIPTION
+
+`viz` generates a PNG file of the current `Gemfile(5)` as a dependency graph.
+`viz` requires the ruby-graphviz gem (and its dependencies).
+
+The associated gems must also be installed via [`bundle install(1)`](bundle-install.1.html).
+
+## OPTIONS
+
+* `--file`, `-f`:
+ The name to use for the generated file. See `--format` option
+* `--format`, `-F`:
+ This is output format option. Supported format is png, jpg, svg, dot ...
+* `--requirements`, `-R`:
+ Set to show the version of each required dependency.
+* `--version`, `-v`:
+ Set to show each gem version.
+* `--without`, `-W`:
+ Exclude gems that are part of the specified named group.
diff --git a/man/bundle.1 b/man/bundle.1
new file mode 100644
index 0000000000..abb90154d9
--- /dev/null
+++ b/man/bundle.1
@@ -0,0 +1,132 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "BUNDLE" "1" "November 2018" "" ""
+.
+.SH "NAME"
+\fBbundle\fR \- Ruby Dependency Management
+.
+.SH "SYNOPSIS"
+\fBbundle\fR COMMAND [\-\-no\-color] [\-\-verbose] [ARGS]
+.
+.SH "DESCRIPTION"
+Bundler manages an \fBapplication\'s dependencies\fR through its entire life across many machines systematically and repeatably\.
+.
+.P
+See the bundler website \fIhttp://bundler\.io\fR for information on getting started, and Gemfile(5) for more information on the \fBGemfile\fR format\.
+.
+.SH "OPTIONS"
+.
+.TP
+\fB\-\-no\-color\fR
+Print all output without color
+.
+.TP
+\fB\-\-retry\fR, \fB\-r\fR
+Specify the number of times you wish to attempt network commands
+.
+.TP
+\fB\-\-verbose\fR, \fB\-V\fR
+Print out additional logging information
+.
+.SH "BUNDLE COMMANDS"
+We divide \fBbundle\fR subcommands into primary commands and utilities:
+.
+.SH "PRIMARY COMMANDS"
+.
+.TP
+\fBbundle install(1)\fR \fIbundle\-install\.1\.html\fR
+Install the gems specified by the \fBGemfile\fR or \fBGemfile\.lock\fR
+.
+.TP
+\fBbundle update(1)\fR \fIbundle\-update\.1\.html\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
+.
+.TP
+\fBbundle exec(1)\fR \fIbundle\-exec\.1\.html\fR
+Execute a script in the current bundle
+.
+.TP
+\fBbundle config(1)\fR \fIbundle\-config\.1\.html\fR
+Specify and read configuration options for Bundler
+.
+.TP
+\fBbundle help(1)\fR
+Display detailed help for each subcommand
+.
+.SH "UTILITIES"
+.
+.TP
+\fBbundle add(1)\fR \fIbundle\-add\.1\.html\fR
+Add the named gem to the Gemfile and run \fBbundle install\fR
+.
+.TP
+\fBbundle binstubs(1)\fR \fIbundle\-binstubs\.1\.html\fR
+Generate binstubs for executables in a gem
+.
+.TP
+\fBbundle check(1)\fR \fIbundle\-check\.1\.html\fR
+Determine whether the requirements for your application are installed and available to Bundler
+.
+.TP
+\fBbundle show(1)\fR \fIbundle\-show\.1\.html\fR
+Show the source location of a particular gem in the bundle
+.
+.TP
+\fBbundle outdated(1)\fR \fIbundle\-outdated\.1\.html\fR
+Show all of the outdated gems in the current bundle
+.
+.TP
+\fBbundle console(1)\fR
+Start an IRB session in the current bundle
+.
+.TP
+\fBbundle open(1)\fR \fIbundle\-open\.1\.html\fR
+Open an installed gem in the editor
+.
+.TP
+\fBbundle lock(1)\fR \fIbundle\-lock\.1\.hmtl\fR
+Generate a lockfile for your dependencies
+.
+.TP
+\fBbundle viz(1)\fR \fIbundle\-viz\.1\.html\fR
+Generate a visual representation of your dependencies
+.
+.TP
+\fBbundle init(1)\fR \fIbundle\-init\.1\.html\fR
+Generate a simple \fBGemfile\fR, placed in the current directory
+.
+.TP
+\fBbundle gem(1)\fR \fIbundle\-gem\.1\.html\fR
+Create a simple gem, suitable for development with Bundler
+.
+.TP
+\fBbundle platform(1)\fR \fIbundle\-platform\.1\.html\fR
+Display platform compatibility information
+.
+.TP
+\fBbundle clean(1)\fR \fIbundle\-clean\.1\.html\fR
+Clean up unused gems in your Bundler directory
+.
+.TP
+\fBbundle doctor(1)\fR \fIbundle\-doctor\.1\.html\fR
+Display warnings about common problems
+.
+.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\.
+.
+.SH "OBSOLETE"
+These commands are obsolete and should no longer be used:
+.
+.IP "\(bu" 4
+\fBbundle cache(1)\fR
+.
+.IP "\(bu" 4
+\fBbundle show(1)\fR
+.
+.IP "" 0
+
diff --git a/man/bundle.1.txt b/man/bundle.1.txt
new file mode 100644
index 0000000000..0f38628621
--- /dev/null
+++ b/man/bundle.1.txt
@@ -0,0 +1,113 @@
+BUNDLE(1) BUNDLE(1)
+
+
+
+1mNAME0m
+ 1mbundle 22m- Ruby Dependency Management
+
+1mSYNOPSIS0m
+ 1mbundle 22mCOMMAND [--no-color] [--verbose] [ARGS]
+
+1mDESCRIPTION0m
+ Bundler manages an 1mapplication's dependencies 22mthrough its entire life
+ across many machines systematically and repeatably.
+
+ See the bundler website 4mhttp://bundler.io24m for information on getting
+ started, and Gemfile(5) for more information on the 1mGemfile 22mformat.
+
+1mOPTIONS0m
+ 1m--no-color0m
+ Print all output without color
+
+ 1m--retry22m, 1m-r0m
+ Specify the number of times you wish to attempt network commands
+
+ 1m--verbose22m, 1m-V0m
+ Print out additional logging information
+
+1mBUNDLE COMMANDS0m
+ We divide 1mbundle 22msubcommands into primary commands and utilities:
+
+1mPRIMARY COMMANDS0m
+ 1mbundle install(1) 4m22mbundle-install.1.html0m
+ Install the gems specified by the 1mGemfile 22mor 1mGemfile.lock0m
+
+ 1mbundle update(1) 4m22mbundle-update.1.html0m
+ Update dependencies to their latest versions
+
+ 1mbundle package(1) 4m22mbundle-package.1.html0m
+ Package the .gem files required by your application into the
+ 1mvendor/cache 22mdirectory
+
+ 1mbundle exec(1) 4m22mbundle-exec.1.html0m
+ Execute a script in the current bundle
+
+ 1mbundle config(1) 4m22mbundle-config.1.html0m
+ Specify and read configuration options for Bundler
+
+ 1mbundle help(1)0m
+ Display detailed help for each subcommand
+
+1mUTILITIES0m
+ 1mbundle add(1) 4m22mbundle-add.1.html0m
+ Add the named gem to the Gemfile and run 1mbundle install0m
+
+ 1mbundle binstubs(1) 4m22mbundle-binstubs.1.html0m
+ Generate binstubs for executables in a gem
+
+ 1mbundle check(1) 4m22mbundle-check.1.html0m
+ Determine whether the requirements for your application are
+ installed and available to Bundler
+
+ 1mbundle show(1) 4m22mbundle-show.1.html0m
+ Show the source location of a particular gem in the bundle
+
+ 1mbundle outdated(1) 4m22mbundle-outdated.1.html0m
+ Show all of the outdated gems in the current bundle
+
+ 1mbundle console(1)0m
+ Start an IRB session in the current bundle
+
+ 1mbundle open(1) 4m22mbundle-open.1.html0m
+ Open an installed gem in the editor
+
+ 1mbundle lock(1) 4m22mbundle-lock.1.hmtl0m
+ Generate a lockfile for your dependencies
+
+ 1mbundle viz(1) 4m22mbundle-viz.1.html0m
+ Generate a visual representation of your dependencies
+
+ 1mbundle init(1) 4m22mbundle-init.1.html0m
+ Generate a simple 1mGemfile22m, placed in the current directory
+
+ 1mbundle gem(1) 4m22mbundle-gem.1.html0m
+ Create a simple gem, suitable for development with Bundler
+
+ 1mbundle platform(1) 4m22mbundle-platform.1.html0m
+ Display platform compatibility information
+
+ 1mbundle clean(1) 4m22mbundle-clean.1.html0m
+ Clean up unused gems in your Bundler directory
+
+ 1mbundle doctor(1) 4m22mbundle-doctor.1.html0m
+ Display warnings about common problems
+
+1mPLUGINS0m
+ When running a command that isn't listed in PRIMARY COMMANDS or UTILI-
+ TIES, Bundler will try to find an executable on your path named
+ 1mbundler-<command> 22mand execute it, passing down any extra arguments to
+ it.
+
+1mOBSOLETE0m
+ These commands are obsolete and should no longer be used:
+
+ o 1mbundle cache(1)0m
+
+ o 1mbundle show(1)0m
+
+
+
+
+
+
+ November 2018 BUNDLE(1)
diff --git a/man/bundle.ronn b/man/bundle.ronn
new file mode 100644
index 0000000000..c03201a30c
--- /dev/null
+++ b/man/bundle.ronn
@@ -0,0 +1,108 @@
+bundle(1) -- Ruby Dependency Management
+=======================================
+
+## SYNOPSIS
+
+`bundle` COMMAND [--no-color] [--verbose] [ARGS]
+
+## DESCRIPTION
+
+Bundler manages an `application's dependencies` through its entire life
+across many machines systematically and repeatably.
+
+See [the bundler website](http://bundler.io) for information on getting
+started, and Gemfile(5) for more information on the `Gemfile` format.
+
+## OPTIONS
+
+* `--no-color`:
+ Print all output without color
+
+* `--retry`, `-r`:
+ Specify the number of times you wish to attempt network commands
+
+* `--verbose`, `-V`:
+ Print out additional logging information
+
+## BUNDLE COMMANDS
+
+We divide `bundle` subcommands into primary commands and utilities:
+
+## PRIMARY COMMANDS
+
+* [`bundle install(1)`](bundle-install.1.html):
+ Install the gems specified by the `Gemfile` or `Gemfile.lock`
+
+* [`bundle update(1)`](bundle-update.1.html):
+ Update dependencies to their latest versions
+
+* [`bundle package(1)`](bundle-package.1.html):
+ Package the .gem files required by your application into the
+ `vendor/cache` directory
+
+* [`bundle exec(1)`](bundle-exec.1.html):
+ Execute a script in the current bundle
+
+* [`bundle config(1)`](bundle-config.1.html):
+ Specify and read configuration options for Bundler
+
+* `bundle help(1)`:
+ Display detailed help for each subcommand
+
+## UTILITIES
+
+* [`bundle add(1)`](bundle-add.1.html):
+ Add the named gem to the Gemfile and run `bundle install`
+
+* [`bundle binstubs(1)`](bundle-binstubs.1.html):
+ Generate binstubs for executables in a gem
+
+* [`bundle check(1)`](bundle-check.1.html):
+ Determine whether the requirements for your application are installed
+ and available to Bundler
+
+* [`bundle show(1)`](bundle-show.1.html):
+ Show the source location of a particular gem in the bundle
+
+* [`bundle outdated(1)`](bundle-outdated.1.html):
+ Show all of the outdated gems in the current bundle
+
+* `bundle console(1)`:
+ Start an IRB session in the current bundle
+
+* [`bundle open(1)`](bundle-open.1.html):
+ Open an installed gem in the editor
+
+* [`bundle lock(1)`](bundle-lock.1.hmtl):
+ Generate a lockfile for your dependencies
+
+* [`bundle viz(1)`](bundle-viz.1.html):
+ Generate a visual representation of your dependencies
+
+* [`bundle init(1)`](bundle-init.1.html):
+ Generate a simple `Gemfile`, placed in the current directory
+
+* [`bundle gem(1)`](bundle-gem.1.html):
+ Create a simple gem, suitable for development with Bundler
+
+* [`bundle platform(1)`](bundle-platform.1.html):
+ Display platform compatibility information
+
+* [`bundle clean(1)`](bundle-clean.1.html):
+ Clean up unused gems in your Bundler directory
+
+* [`bundle doctor(1)`](bundle-doctor.1.html):
+ Display warnings about common problems
+
+## 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 `bundler-<command>`
+and execute it, passing down any extra arguments to it.
+
+## OBSOLETE
+
+These commands are obsolete and should no longer be used:
+
+* `bundle cache(1)`
+* `bundle show(1)`
diff --git a/man/erb.1 b/man/erb.1
index f98a0273a5..d8739a7639 100644
--- a/man/erb.1
+++ b/man/erb.1
@@ -1,6 +1,6 @@
.\"Ruby is copyrighted by Yukihiro Matsumoto <matz@netlab.jp>.
-.Dd November 15, 2012
-.Dt ERB(1) "" "Ruby Programmers Reference Guide"
+.Dd December 16, 2018
+.Dt ERB \&1 "Ruby Programmer's Reference Guide"
.Os UNIX
.Sh NAME
.Nm erb
@@ -45,9 +45,8 @@ You can omit the one for internal encodings, then the value
.Pf ( Li "Encoding.default_internal" ) will be nil.
.Pp
.It Fl P
-Evaluates lines starting with
-.Li "%"
-as Ruby code and removes the tailing EOLs.
+Disables ruby code evaluation for lines beginning with
+.Li "%" .
.Pp
.It Fl S Ar level
Specifies the safe level in which eRuby script will run.
@@ -77,6 +76,8 @@ And leading whitespaces are removed if the erb directive starts with
.Li "<%-" .
.Pp
.El
+.It Fl r
+Load a library
.Pp
.It Fl U
can be one of
@@ -143,15 +144,17 @@ class.
.Pp
.Sh REPORTING BUGS
.Bl -bullet
-.Li Security vulnerabilities should be reported via an email to
-.Aq security@ruby-lang.org .
+.It
+Security vulnerabilities should be reported via an email to
+.Mt security@ruby-lang.org .
Reported problems will be published after being fixed.
.Pp
-.Li And you can report other bugs and feature requests via the
+.It
+Other bugs and feature requests can be reported via the
Ruby Issue Tracking System
.Pq Lk https://bugs.ruby-lang.org/ .
Do not report security vulnerabilities
-via the system because it publishes the vulnerabilities immediately.
+via this system because it publishes the vulnerabilities immediately.
.El
.Sh AUTHORS
Written by Masatoshi SEKI.
diff --git a/man/gemfile.5 b/man/gemfile.5
new file mode 100644
index 0000000000..ccb258f50f
--- /dev/null
+++ b/man/gemfile.5
@@ -0,0 +1,689 @@
+.\" generated with Ronn/v0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
+.
+.TH "GEMFILE" "5" "November 2018" "" ""
+.
+.SH "NAME"
+\fBGemfile\fR \- A format for describing gem dependencies for Ruby programs
+.
+.SH "SYNOPSIS"
+A \fBGemfile\fR describes the gem dependencies required to execute associated Ruby code\.
+.
+.P
+Place the \fBGemfile\fR in the root of the directory containing the associated code\. For instance, in a Rails application, place the \fBGemfile\fR in the same directory as the \fBRakefile\fR\.
+.
+.SH "SYNTAX"
+A \fBGemfile\fR is evaluated as Ruby code, in a context which makes available a number of methods used to describe the gem requirements\.
+.
+.SH "GLOBAL SOURCES"
+At the top of the \fBGemfile\fR, add a line for the \fBRubygems\fR source that contains the gems listed in the \fBGemfile\fR\.
+.
+.IP "" 4
+.
+.nf
+
+source "https://rubygems\.org"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+It is possible, but not recommended as of Bundler 1\.7, to add multiple global \fBsource\fR lines\. Each of these \fBsource\fRs \fBMUST\fR be a valid Rubygems repository\.
+.
+.P
+Sources are checked for gems following the heuristics described in \fISOURCE PRIORITY\fR\. If a gem is found in more than one global source, Bundler will print a warning after installing the gem indicating which source was used, and listing the other sources where the gem is available\. A specific source can be selected for gems that need to use a non\-standard repository, suppressing this warning, by using the \fI\fB:source\fR option\fR or a \fI\fBsource\fR block\fR\.
+.
+.SS "CREDENTIALS"
+Some gem sources require a username and password\. Use bundle config(1) \fIbundle\-config\.1\.html\fR to set the username and password for any of the sources that need it\. The command must be run once on each computer that will install the Gemfile, but this keeps the credentials from being stored in plain text in version control\.
+.
+.IP "" 4
+.
+.nf
+
+bundle config gems\.example\.com user:password
+.
+.fi
+.
+.IP "" 0
+.
+.P
+For some sources, like a company Gemfury account, it may be easier to include the credentials in the Gemfile as part of the source URL\.
+.
+.IP "" 4
+.
+.nf
+
+source "https://user:password@gems\.example\.com"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Credentials in the source URL will take precedence over credentials set using \fBconfig\fR\.
+.
+.SH "RUBY"
+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\.
+.
+.IP "" 4
+.
+.nf
+
+ruby "1\.9\.3"
+.
+.fi
+.
+.IP "" 0
+.
+.SS "ENGINE"
+Each application \fImay\fR specify a Ruby engine\. If an engine is specified, an engine version \fImust\fR also be specified\.
+.
+.P
+What exactly is an Engine? \- A Ruby engine is an implementation of the Ruby language\.
+.
+.IP "\(bu" 4
+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\.
+.
+.IP "" 0
+.
+.SS "ENGINE VERSION"
+Each application \fImay\fR specify a Ruby engine version\. If an engine version is specified, an engine \fImust\fR also be specified\. If the engine is "ruby" the engine version specified \fImust\fR match the Ruby version\.
+.
+.IP "" 4
+.
+.nf
+
+ruby "1\.8\.7", :engine => "jruby", :engine_version => "1\.6\.7"
+.
+.fi
+.
+.IP "" 0
+.
+.SS "PATCHLEVEL"
+Each application \fImay\fR specify a Ruby patchlevel\.
+.
+.IP "" 4
+.
+.nf
+
+ruby "2\.0\.0", :patchlevel => "247"
+.
+.fi
+.
+.IP "" 0
+.
+.SH "GEMS"
+Specify gem requirements using the \fBgem\fR method, with the following arguments\. All parameters are \fBOPTIONAL\fR unless otherwise specified\.
+.
+.SS "NAME (required)"
+For each gem requirement, list a single \fIgem\fR line\.
+.
+.IP "" 4
+.
+.nf
+
+gem "nokogiri"
+.
+.fi
+.
+.IP "" 0
+.
+.SS "VERSION"
+Each \fIgem\fR \fBMAY\fR have one or more version specifiers\.
+.
+.IP "" 4
+.
+.nf
+
+gem "nokogiri", ">= 1\.4\.2"
+gem "RedCloth", ">= 4\.1\.0", "< 4\.2\.0"
+.
+.fi
+.
+.IP "" 0
+.
+.SS "REQUIRE AS"
+Each \fIgem\fR \fBMAY\fR specify files that should be used when autorequiring via \fBBundler\.require\fR\. You may pass an array with multiple files or \fBtrue\fR if file you want \fBrequired\fR has same name as \fIgem\fR or \fBfalse\fR to prevent any file from being autorequired\.
+.
+.IP "" 4
+.
+.nf
+
+gem "redis", :require => ["redis/connection/hiredis", "redis"]
+gem "webmock", :require => false
+gem "debugger", :require => true
+.
+.fi
+.
+.IP "" 0
+.
+.P
+The argument defaults to the name of the gem\. For example, these are identical:
+.
+.IP "" 4
+.
+.nf
+
+gem "nokogiri"
+gem "nokogiri", :require => "nokogiri"
+gem "nokogiri", :require => true
+.
+.fi
+.
+.IP "" 0
+.
+.SS "GROUPS"
+Each \fIgem\fR \fBMAY\fR specify membership in one or more groups\. Any \fIgem\fR that does not specify membership in any group is placed in the \fBdefault\fR group\.
+.
+.IP "" 4
+.
+.nf
+
+gem "rspec", :group => :test
+gem "wirble", :groups => [:development, :test]
+.
+.fi
+.
+.IP "" 0
+.
+.P
+The Bundler runtime allows its two main methods, \fBBundler\.setup\fR and \fBBundler\.require\fR, to limit their impact to particular groups\.
+.
+.IP "" 4
+.
+.nf
+
+# setup adds gems to Ruby\'s load path
+Bundler\.setup # defaults to all groups
+require "bundler/setup" # same as Bundler\.setup
+Bundler\.setup(:default) # only set up the _default_ group
+Bundler\.setup(:test) # only set up the _test_ group (but `not` _default_)
+Bundler\.setup(:default, :test) # set up the _default_ and _test_ groups, but no others
+
+# require requires all of the gems in the specified groups
+Bundler\.require # defaults to the _default_ group
+Bundler\.require(:default) # identical
+Bundler\.require(:default, :test) # requires the _default_ and _test_ groups
+Bundler\.require(:test) # requires the _test_ group
+.
+.fi
+.
+.IP "" 0
+.
+.P
+The Bundler CLI allows you to specify a list of groups whose gems \fBbundle install\fR should not install with the \fB\-\-without\fR option\. To specify multiple groups to ignore, specify a list of groups separated by spaces\.
+.
+.IP "" 4
+.
+.nf
+
+bundle install \-\-without test
+bundle install \-\-without development test
+.
+.fi
+.
+.IP "" 0
+.
+.P
+After running \fBbundle install \-\-without test\fR, bundler will remember that you excluded the test group in the last installation\. The next time you run \fBbundle install\fR, without any \fB\-\-without option\fR, bundler will recall it\.
+.
+.P
+Also, calling \fBBundler\.setup\fR with no parameters, or calling \fBrequire "bundler/setup"\fR will setup all groups except for the ones you excluded via \fB\-\-without\fR (since they are not available)\.
+.
+.P
+Note that on \fBbundle install\fR, bundler downloads and evaluates all gems, in order to create a single canonical list of all of the required gems and their dependencies\. This means that you cannot list different versions of the same gems in different groups\. For more details, see Understanding Bundler \fIhttp://bundler\.io/rationale\.html\fR\.
+.
+.SS "PLATFORMS"
+If a gem should only be used in a particular platform or set of platforms, you can specify them\. Platforms are essentially identical to groups, except that you do not need to use the \fB\-\-without\fR install\-time flag to exclude groups of gems for other platforms\.
+.
+.P
+There are a number of \fBGemfile\fR platforms:
+.
+.TP
+\fBruby\fR
+C Ruby (MRI), Rubinius or TruffleRuby, but \fBNOT\fR Windows
+.
+.TP
+\fBmri\fR
+Same as \fIruby\fR, but only C Ruby (MRI)
+.
+.TP
+\fBmingw\fR
+Windows 32 bit \'mingw32\' platform (aka RubyInstaller)
+.
+.TP
+\fBx64_mingw\fR
+Windows 64 bit \'mingw32\' platform (aka RubyInstaller x64)
+.
+.TP
+\fBrbx\fR
+Rubinius
+.
+.TP
+\fBjruby\fR
+JRuby
+.
+.TP
+\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:
+.
+.IP "" 4
+.
+.nf
+
+ruby_23
+.
+.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
+.
+.TP
+\fBmri\fR
+1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5
+.
+.TP
+\fBmingw\fR
+1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5
+.
+.TP
+\fBx64_mingw\fR
+2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5
+.
+.P
+As with groups, you can specify one or more platforms:
+.
+.IP "" 4
+.
+.nf
+
+gem "weakling", :platforms => :jruby
+gem "ruby\-debug", :platforms => :mri_18
+gem "nokogiri", :platforms => [:mri_18, :jruby]
+.
+.fi
+.
+.IP "" 0
+.
+.P
+All operations involving groups (\fBbundle install\fR \fIbundle\-install\.1\.html\fR, \fBBundler\.setup\fR, \fBBundler\.require\fR) behave exactly the same as if any groups not matching the current platform were explicitly excluded\.
+.
+.SS "SOURCE"
+You can select an alternate Rubygems repository for a gem using the \':source\' option\.
+.
+.IP "" 4
+.
+.nf
+
+gem "some_internal_gem", :source => "https://gems\.example\.com"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+This forces the gem to be loaded from this source and ignores any global sources declared at the top level of the file\. If the gem does not exist in this source, it will not be installed\.
+.
+.P
+Bundler will search for child dependencies of this gem by first looking in the source selected for the parent, but if they are not found there, it will fall back on global sources using the ordering described in \fISOURCE PRIORITY\fR\.
+.
+.P
+Selecting a specific source repository this way also suppresses the ambiguous gem warning described above in \fIGLOBAL SOURCES (#source)\fR\.
+.
+.P
+Using the \fB:source\fR option for an individual gem will also make that source available as a possible global source for any other gems which do not specify explicit sources\. Thus, when adding gems with explicit sources, it is recommended that you also ensure all other gems in the Gemfile are using explicit sources\.
+.
+.SS "GIT"
+If necessary, you can specify that a gem is located at a particular git repository using the \fB:git\fR parameter\. The repository can be accessed via several protocols:
+.
+.TP
+\fBHTTP(S)\fR
+gem "rails", :git => "https://github\.com/rails/rails\.git"
+.
+.TP
+\fBSSH\fR
+gem "rails", :git => "git@github\.com:rails/rails\.git"
+.
+.TP
+\fBgit\fR
+gem "rails", :git => "git://github\.com/rails/rails\.git"
+.
+.P
+If using SSH, the user that you use to run \fBbundle install\fR \fBMUST\fR have the appropriate keys available in their \fB$HOME/\.ssh\fR\.
+.
+.P
+\fBNOTE\fR: \fBhttp://\fR and \fBgit://\fR URLs should be avoided if at all possible\. These protocols are unauthenticated, so a man\-in\-the\-middle attacker can deliver malicious code and compromise your system\. HTTPS and SSH are strongly preferred\.
+.
+.P
+The \fBgroup\fR, \fBplatforms\fR, and \fBrequire\fR options are available and behave exactly the same as they would for a normal gem\.
+.
+.P
+A git repository \fBSHOULD\fR have at least one file, at the root of the directory containing the gem, with the extension \fB\.gemspec\fR\. This file \fBMUST\fR contain a valid gem specification, as expected by the \fBgem build\fR command\.
+.
+.P
+If a git repository does not have a \fB\.gemspec\fR, bundler will attempt to create one, but it will not contain any dependencies, executables, or C extension compilation instructions\. As a result, it may fail to properly integrate into your application\.
+.
+.P
+If a git repository does have a \fB\.gemspec\fR for the gem you attached it to, a version specifier, if provided, means that the git repository is only valid if the \fB\.gemspec\fR specifies a version matching the version specifier\. If not, bundler will print a warning\.
+.
+.IP "" 4
+.
+.nf
+
+gem "rails", "2\.3\.8", :git => "https://github\.com/rails/rails\.git"
+# bundle install will fail, because the \.gemspec in the rails
+# repository\'s master branch specifies version 3\.0\.0
+.
+.fi
+.
+.IP "" 0
+.
+.P
+If a git repository does \fBnot\fR have a \fB\.gemspec\fR for the gem you attached it to, a version specifier \fBMUST\fR be provided\. Bundler will use this version in the simple \fB\.gemspec\fR it creates\.
+.
+.P
+Git repositories support a number of additional options\.
+.
+.TP
+\fBbranch\fR, \fBtag\fR, and \fBref\fR
+You \fBMUST\fR only specify at most one of these options\. The default is \fB:branch => "master"\fR
+.
+.TP
+For example:
+.
+.IP
+git "https://github\.com/rails/rails\.git", :branch => "5\-0\-stable" do
+.
+.IP
+git "https://github\.com/rails/rails\.git", :tag => "v5\.0\.0" do
+.
+.IP
+git "https://github\.com/rails/rails\.git", :ref => "4aded" do
+.
+.TP
+\fBsubmodules\fR
+For reference, a git submodule \fIhttps://git\-scm\.com/book/en/v2/Git\-Tools\-Submodules\fR lets you have another git repository within a subfolder of your repository\. Specify \fB:submodules => true\fR to cause bundler to expand any submodules included in the git repository
+.
+.P
+If a git repository contains multiple \fB\.gemspecs\fR, each \fB\.gemspec\fR represents a gem located at the same place in the file system as the \fB\.gemspec\fR\.
+.
+.IP "" 4
+.
+.nf
+
+|~rails [git root]
+| |\-rails\.gemspec [rails gem located here]
+|~actionpack
+| |\-actionpack\.gemspec [actionpack gem located here]
+|~activesupport
+| |\-activesupport\.gemspec [activesupport gem located here]
+|\.\.\.
+.
+.fi
+.
+.IP "" 0
+.
+.P
+To install a gem located in a git repository, bundler changes to the directory containing the gemspec, runs \fBgem build name\.gemspec\fR and then installs the resulting gem\. The \fBgem build\fR command, which comes standard with Rubygems, evaluates the \fB\.gemspec\fR in the context of the directory in which it is located\.
+.
+.SS "GIT SOURCE"
+A custom git source can be defined via the \fBgit_source\fR method\. Provide the source\'s name as an argument, and a block which receives a single argument and interpolates it into a string to return the full repo address:
+.
+.IP "" 4
+.
+.nf
+
+git_source(:stash){ |repo_name| "https://stash\.corp\.acme\.pl/#{repo_name}\.git" }
+gem \'rails\', :stash => \'forks/rails\'
+.
+.fi
+.
+.IP "" 0
+.
+.P
+In addition, if you wish to choose a specific branch:
+.
+.IP "" 4
+.
+.nf
+
+gem "rails", :stash => "forks/rails", :branch => "branch_name"
+.
+.fi
+.
+.IP "" 0
+.
+.SS "GITHUB"
+\fBNOTE\fR: This shorthand should be avoided until Bundler 2\.0, since it currently expands to an insecure \fBgit://\fR URL\. This allows a man\-in\-the\-middle attacker to compromise your system\.
+.
+.P
+If the git repository you want to use is hosted on GitHub and is public, you can use the :github shorthand to specify the github username and repository name (without the trailing "\.git"), separated by a slash\. If both the username and repository name are the same, you can omit one\.
+.
+.IP "" 4
+.
+.nf
+
+gem "rails", :github => "rails/rails"
+gem "rails", :github => "rails"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Are both equivalent to
+.
+.IP "" 4
+.
+.nf
+
+gem "rails", :git => "git://github\.com/rails/rails\.git"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Since the \fBgithub\fR method is a specialization of \fBgit_source\fR, it accepts a \fB:branch\fR named argument\.
+.
+.SS "GIST"
+If the git repository you want to use is hosted as a Github Gist and is public, you can use the :gist shorthand to specify the gist identifier (without the trailing "\.git")\.
+.
+.IP "" 4
+.
+.nf
+
+gem "the_hatch", :gist => "4815162342"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Is equivalent to:
+.
+.IP "" 4
+.
+.nf
+
+gem "the_hatch", :git => "https://gist\.github\.com/4815162342\.git"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Since the \fBgist\fR method is a specialization of \fBgit_source\fR, it accepts a \fB:branch\fR named argument\.
+.
+.SS "BITBUCKET"
+If the git repository you want to use is hosted on Bitbucket and is public, you can use the :bitbucket shorthand to specify the bitbucket username and repository name (without the trailing "\.git"), separated by a slash\. If both the username and repository name are the same, you can omit one\.
+.
+.IP "" 4
+.
+.nf
+
+gem "rails", :bitbucket => "rails/rails"
+gem "rails", :bitbucket => "rails"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Are both equivalent to
+.
+.IP "" 4
+.
+.nf
+
+gem "rails", :git => "https://rails@bitbucket\.org/rails/rails\.git"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Since the \fBbitbucket\fR method is a specialization of \fBgit_source\fR, it accepts a \fB:branch\fR named argument\.
+.
+.SS "PATH"
+You can specify that a gem is located in a particular location on the file system\. Relative paths are resolved relative to the directory containing the \fBGemfile\fR\.
+.
+.P
+Similar to the semantics of the \fB:git\fR option, the \fB:path\fR option requires that the directory in question either contains a \fB\.gemspec\fR for the gem, or that you specify an explicit version that bundler should use\.
+.
+.P
+Unlike \fB:git\fR, bundler does not compile C extensions for gems specified as paths\.
+.
+.IP "" 4
+.
+.nf
+
+gem "rails", :path => "vendor/rails"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+If you would like to use multiple local gems directly from the filesystem, you can set a global \fBpath\fR option to the path containing the gem\'s files\. This will automatically load gemspec files from subdirectories\.
+.
+.IP "" 4
+.
+.nf
+
+path \'components\' do
+ gem \'admin_ui\'
+ gem \'public_ui\'
+end
+.
+.fi
+.
+.IP "" 0
+.
+.SH "BLOCK FORM OF SOURCE, GIT, PATH, GROUP and PLATFORMS"
+The \fB:source\fR, \fB:git\fR, \fB:path\fR, \fB:group\fR, and \fB:platforms\fR options may be applied to a group of gems by using block form\.
+.
+.IP "" 4
+.
+.nf
+
+source "https://gems\.example\.com" do
+ gem "some_internal_gem"
+ gem "another_internal_gem"
+end
+
+git "https://github\.com/rails/rails\.git" do
+ gem "activesupport"
+ gem "actionpack"
+end
+
+platforms :ruby do
+ gem "ruby\-debug"
+ gem "sqlite3"
+end
+
+group :development, :optional => true do
+ gem "wirble"
+ gem "faker"
+end
+.
+.fi
+.
+.IP "" 0
+.
+.P
+In the case of the group block form the :optional option can be given to prevent a group from being installed unless listed in the \fB\-\-with\fR option given to the \fBbundle install\fR command\.
+.
+.P
+In the case of the \fBgit\fR block form, the \fB:ref\fR, \fB:branch\fR, \fB:tag\fR, and \fB:submodules\fR options may be passed to the \fBgit\fR method, and all gems in the block will inherit those options\.
+.
+.P
+The presence of a \fBsource\fR block in a Gemfile also makes that source available as a possible global source for any other gems which do not specify explicit sources\. Thus, when defining source blocks, it is recommended that you also ensure all other gems in the Gemfile are using explicit sources, either via source blocks or \fB:source\fR directives on individual gems\.
+.
+.SH "INSTALL_IF"
+The \fBinstall_if\fR method allows gems to be installed based on a proc or lambda\. This is especially useful for optional gems that can only be used if certain software is installed or some other conditions are met\.
+.
+.IP "" 4
+.
+.nf
+
+install_if \-> { RUBY_PLATFORM =~ /darwin/ } do
+ gem "pasteboard"
+end
+.
+.fi
+.
+.IP "" 0
+.
+.SH "GEMSPEC"
+The \fB\.gemspec\fR \fIhttp://guides\.rubygems\.org/specification\-reference/\fR file is where you provide metadata about your gem to Rubygems\. Some required Gemspec attributes include the name, description, and homepage of your gem\. This is also where you specify the dependencies your gem needs to run\.
+.
+.P
+If you wish to use Bundler to help install dependencies for a gem while it is being developed, use the \fBgemspec\fR method to pull in the dependencies listed in the \fB\.gemspec\fR file\.
+.
+.P
+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 (\fB:path => \'\.\'\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\.
+.
+.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\.
+.
+.SH "SOURCE PRIORITY"
+When attempting to locate a gem to satisfy a gem requirement, bundler uses the following priority order:
+.
+.IP "1." 4
+The source explicitly attached to the gem (using \fB:source\fR, \fB:path\fR, or \fB:git\fR)
+.
+.IP "2." 4
+For implicit gems (dependencies of explicit gems), any source, git, or path repository declared on the parent\. This results in bundler prioritizing the ActiveSupport gem from the Rails git repository over ones from \fBrubygems\.org\fR
+.
+.IP "3." 4
+The sources specified via global \fBsource\fR lines, searching each source in your \fBGemfile\fR from last added to first added\.
+.
+.IP "" 0
+
diff --git a/man/gemfile.5.ronn b/man/gemfile.5.ronn
new file mode 100644
index 0000000000..f4772f6d8d
--- /dev/null
+++ b/man/gemfile.5.ronn
@@ -0,0 +1,521 @@
+Gemfile(5) -- A format for describing gem dependencies for Ruby programs
+========================================================================
+
+## SYNOPSIS
+
+A `Gemfile` describes the gem dependencies required to execute associated
+Ruby code.
+
+Place the `Gemfile` in the root of the directory containing the associated
+code. For instance, in a Rails application, place the `Gemfile` in the same
+directory as the `Rakefile`.
+
+## SYNTAX
+
+A `Gemfile` is evaluated as Ruby code, in a context which makes available
+a number of methods used to describe the gem requirements.
+
+## GLOBAL SOURCES
+
+At the top of the `Gemfile`, add a line for the `Rubygems` source that contains
+the gems listed in the `Gemfile`.
+
+ source "https://rubygems.org"
+
+It is possible, but not recommended as of Bundler 1.7, to add multiple global
+`source` lines. Each of these `source`s `MUST` be a valid Rubygems repository.
+
+Sources are checked for gems following the heuristics described in
+[SOURCE PRIORITY][]. If a gem is found in more than one global source, Bundler
+will print a warning after installing the gem indicating which source was used,
+and listing the other sources where the gem is available. A specific source can
+be selected for gems that need to use a non-standard repository, suppressing
+this warning, by using the [`:source` option](#SOURCE) or a
+[`source` block](#BLOCK-FORM-OF-SOURCE-GIT-PATH-GROUP-and-PLATFORMS).
+
+### CREDENTIALS
+
+Some gem sources require a username and password. Use [bundle config(1)](bundle-config.1.html) to set
+the username and password for any of the sources that need it. The command must
+be run once on each computer that will install the Gemfile, but this keeps the
+credentials from being stored in plain text in version control.
+
+ bundle config gems.example.com user:password
+
+For some sources, like a company Gemfury account, it may be easier to
+include the credentials in the Gemfile as part of the source URL.
+
+ source "https://user:password@gems.example.com"
+
+Credentials in the source URL will take precedence over credentials set using
+`config`.
+
+## RUBY
+
+If your application requires a specific Ruby version or engine, specify your
+requirements using the `ruby` method, with the following arguments.
+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
+should be the Ruby version that the engine is compatible with.
+
+ ruby "1.9.3"
+
+### ENGINE
+
+Each application _may_ specify a Ruby engine. If an engine is specified, an
+engine version _must_ also be specified.
+
+What exactly is an Engine?
+ - A Ruby engine is an implementation of the Ruby language.
+
+ - For background: the reference or original implementation of the Ruby
+ programming language is called
+ [Matz's Ruby Interpreter](https://en.wikipedia.org/wiki/Ruby_MRI), 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.
+
+ - [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/).
+ Rubinius is an alternative implementation of Ruby written in Ruby.
+ JRuby is an implementation of Ruby on the JVM, short for Java Virtual Machine.
+
+### ENGINE VERSION
+
+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"
+
+### PATCHLEVEL
+
+Each application _may_ specify a Ruby patchlevel.
+
+ ruby "2.0.0", :patchlevel => "247"
+
+## GEMS
+
+Specify gem requirements using the `gem` method, with the following arguments.
+All parameters are `OPTIONAL` unless otherwise specified.
+
+### NAME (required)
+
+For each gem requirement, list a single _gem_ line.
+
+ gem "nokogiri"
+
+### VERSION
+
+Each _gem_ `MAY` have one or more version specifiers.
+
+ gem "nokogiri", ">= 1.4.2"
+ gem "RedCloth", ">= 4.1.0", "< 4.2.0"
+
+### REQUIRE AS
+
+Each _gem_ `MAY` specify files that should be used when autorequiring via
+`Bundler.require`. You may pass an array with multiple files or `true` if file
+you want `required` has same name as _gem_ or `false` to
+prevent any file from being autorequired.
+
+ gem "redis", :require => ["redis/connection/hiredis", "redis"]
+ gem "webmock", :require => false
+ gem "debugger", :require => true
+
+The argument defaults to the name of the gem. For example, these are identical:
+
+ gem "nokogiri"
+ gem "nokogiri", :require => "nokogiri"
+ gem "nokogiri", :require => true
+
+### GROUPS
+
+Each _gem_ `MAY` specify membership in one or more groups. Any _gem_ that does
+not specify membership in any group is placed in the `default` group.
+
+ gem "rspec", :group => :test
+ gem "wirble", :groups => [:development, :test]
+
+The Bundler runtime allows its two main methods, `Bundler.setup` and
+`Bundler.require`, to limit their impact to particular groups.
+
+ # setup adds gems to Ruby's load path
+ Bundler.setup # defaults to all groups
+ require "bundler/setup" # same as Bundler.setup
+ Bundler.setup(:default) # only set up the _default_ group
+ Bundler.setup(:test) # only set up the _test_ group (but `not` _default_)
+ Bundler.setup(:default, :test) # set up the _default_ and _test_ groups, but no others
+
+ # require requires all of the gems in the specified groups
+ Bundler.require # defaults to the _default_ group
+ Bundler.require(:default) # identical
+ Bundler.require(:default, :test) # requires the _default_ and _test_ groups
+ Bundler.require(:test) # requires the _test_ group
+
+The Bundler CLI allows you to specify a list of groups whose gems `bundle install` should
+not install with the `--without` option. To specify multiple groups to ignore, specify a
+list of groups separated by spaces.
+
+ bundle install --without test
+ bundle install --without development test
+
+After running `bundle install --without test`, bundler will remember that you excluded
+the test group in the last installation. The next time you run `bundle install`,
+without any `--without option`, bundler will recall it.
+
+Also, calling `Bundler.setup` with no parameters, or calling `require "bundler/setup"`
+will setup all groups except for the ones you excluded via `--without` (since they
+are not available).
+
+Note that on `bundle install`, bundler downloads and evaluates all gems, in order to
+create a single canonical list of all of the required gems and their dependencies.
+This means that you cannot list different versions of the same gems in different
+groups. For more details, see [Understanding Bundler](http://bundler.io/rationale.html).
+
+### PLATFORMS
+
+If a gem should only be used in a particular platform or set of platforms, you can
+specify them. Platforms are essentially identical to groups, except that you do not
+need to use the `--without` install-time flag to exclude groups of gems for other
+platforms.
+
+There are a number of `Gemfile` platforms:
+
+ * `ruby`:
+ 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)
+ * `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:
+
+ ruby_23
+
+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
+ * `mri`:
+ 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5
+ * `mingw`:
+ 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5
+ * `x64_mingw`:
+ 2.0, 2.1, 2.2, 2.3, 2.4, 2.5
+
+As with groups, you can specify one or more platforms:
+
+ gem "weakling", :platforms => :jruby
+ gem "ruby-debug", :platforms => :mri_18
+ gem "nokogiri", :platforms => [:mri_18, :jruby]
+
+All operations involving groups ([`bundle install`](bundle-install.1.html), `Bundler.setup`,
+`Bundler.require`) behave exactly the same as if any groups not
+matching the current platform were explicitly excluded.
+
+### SOURCE
+
+You can select an alternate Rubygems repository for a gem using the ':source'
+option.
+
+ gem "some_internal_gem", :source => "https://gems.example.com"
+
+This forces the gem to be loaded from this source and ignores any global sources
+declared at the top level of the file. If the gem does not exist in this source,
+it will not be installed.
+
+Bundler will search for child dependencies of this gem by first looking in the
+source selected for the parent, but if they are not found there, it will fall
+back on global sources using the ordering described in [SOURCE PRIORITY][].
+
+Selecting a specific source repository this way also suppresses the ambiguous
+gem warning described above in
+[GLOBAL SOURCES (#source)](#GLOBAL-SOURCES).
+
+Using the `:source` option for an individual gem will also make that source
+available as a possible global source for any other gems which do not specify
+explicit sources. Thus, when adding gems with explicit sources, it is
+recommended that you also ensure all other gems in the Gemfile are using
+explicit sources.
+
+### GIT
+
+If necessary, you can specify that a gem is located at a particular
+git repository using the `:git` parameter. The repository can be accessed via
+several protocols:
+
+ * `HTTP(S)`:
+ gem "rails", :git => "https://github.com/rails/rails.git"
+ * `SSH`:
+ gem "rails", :git => "git@github.com:rails/rails.git"
+ * `git`:
+ gem "rails", :git => "git://github.com/rails/rails.git"
+
+If using SSH, the user that you use to run `bundle install` `MUST` have the
+appropriate keys available in their `$HOME/.ssh`.
+
+`NOTE`: `http://` and `git://` URLs should be avoided if at all possible. These
+protocols are unauthenticated, so a man-in-the-middle attacker can deliver
+malicious code and compromise your system. HTTPS and SSH are strongly
+preferred.
+
+The `group`, `platforms`, and `require` options are available and behave
+exactly the same as they would for a normal gem.
+
+A git repository `SHOULD` have at least one file, at the root of the
+directory containing the gem, with the extension `.gemspec`. This file
+`MUST` contain a valid gem specification, as expected by the `gem build`
+command.
+
+If a git repository does not have a `.gemspec`, bundler will attempt to
+create one, but it will not contain any dependencies, executables, or
+C extension compilation instructions. As a result, it may fail to properly
+integrate into your application.
+
+If a git repository does have a `.gemspec` for the gem you attached it
+to, a version specifier, if provided, means that the git repository is
+only valid if the `.gemspec` specifies a version matching the version
+specifier. If not, bundler will print a warning.
+
+ gem "rails", "2.3.8", :git => "https://github.com/rails/rails.git"
+ # bundle install will fail, because the .gemspec in the rails
+ # repository's master branch specifies version 3.0.0
+
+If a git repository does `not` have a `.gemspec` for the gem you attached
+it to, a version specifier `MUST` be provided. Bundler will use this
+version in the simple `.gemspec` it creates.
+
+Git repositories support a number of additional options.
+
+ * `branch`, `tag`, and `ref`:
+ You `MUST` only specify at most one of these options. The default
+ is `:branch => "master"`
+ * For example:
+
+ git "https://github.com/rails/rails.git", :branch => "5-0-stable" do
+
+ git "https://github.com/rails/rails.git", :tag => "v5.0.0" do
+
+ git "https://github.com/rails/rails.git", :ref => "4aded" do
+
+ * `submodules`:
+ For reference, a [git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules)
+ lets you have another git repository within a subfolder of your repository.
+ Specify `:submodules => true` to cause bundler to expand any
+ submodules included in the git repository
+
+If a git repository contains multiple `.gemspecs`, each `.gemspec`
+represents a gem located at the same place in the file system as
+the `.gemspec`.
+
+ |~rails [git root]
+ | |-rails.gemspec [rails gem located here]
+ |~actionpack
+ | |-actionpack.gemspec [actionpack gem located here]
+ |~activesupport
+ | |-activesupport.gemspec [activesupport gem located here]
+ |...
+
+To install a gem located in a git repository, bundler changes to
+the directory containing the gemspec, runs `gem build name.gemspec`
+and then installs the resulting gem. The `gem build` command,
+which comes standard with Rubygems, evaluates the `.gemspec` in
+the context of the directory in which it is located.
+
+### GIT SOURCE
+
+A custom git source can be defined via the `git_source` method. Provide the source's name
+as an argument, and a block which receives a single argument and interpolates it into a
+string to return the full repo address:
+
+ git_source(:stash){ |repo_name| "https://stash.corp.acme.pl/#{repo_name}.git" }
+ gem 'rails', :stash => 'forks/rails'
+
+In addition, if you wish to choose a specific branch:
+
+ gem "rails", :stash => "forks/rails", :branch => "branch_name"
+
+### GITHUB
+
+`NOTE`: This shorthand should be avoided until Bundler 2.0, since it
+currently expands to an insecure `git://` URL. This allows a
+man-in-the-middle attacker to compromise your system.
+
+If the git repository you want to use is hosted on GitHub and is public, you can use the
+:github shorthand to specify the github username and repository name (without the
+trailing ".git"), separated by a slash. If both the username and repository name are the
+same, you can omit one.
+
+ gem "rails", :github => "rails/rails"
+ gem "rails", :github => "rails"
+
+Are both equivalent to
+
+ gem "rails", :git => "git://github.com/rails/rails.git"
+
+Since the `github` method is a specialization of `git_source`, it accepts a `:branch` named argument.
+
+### GIST
+
+If the git repository you want to use is hosted as a Github Gist and is public, you can use
+the :gist shorthand to specify the gist identifier (without the trailing ".git").
+
+ gem "the_hatch", :gist => "4815162342"
+
+Is equivalent to:
+
+ gem "the_hatch", :git => "https://gist.github.com/4815162342.git"
+
+Since the `gist` method is a specialization of `git_source`, it accepts a `:branch` named argument.
+
+### BITBUCKET
+
+If the git repository you want to use is hosted on Bitbucket and is public, you can use the
+:bitbucket shorthand to specify the bitbucket username and repository name (without the
+trailing ".git"), separated by a slash. If both the username and repository name are the
+same, you can omit one.
+
+ gem "rails", :bitbucket => "rails/rails"
+ gem "rails", :bitbucket => "rails"
+
+Are both equivalent to
+
+ gem "rails", :git => "https://rails@bitbucket.org/rails/rails.git"
+
+Since the `bitbucket` method is a specialization of `git_source`, it accepts a `:branch` named argument.
+
+### PATH
+
+You can specify that a gem is located in a particular location
+on the file system. Relative paths are resolved relative to the
+directory containing the `Gemfile`.
+
+Similar to the semantics of the `:git` option, the `:path`
+option requires that the directory in question either contains
+a `.gemspec` for the gem, or that you specify an explicit
+version that bundler should use.
+
+Unlike `:git`, bundler does not compile C extensions for
+gems specified as paths.
+
+ gem "rails", :path => "vendor/rails"
+
+If you would like to use multiple local gems directly from the filesystem, you can set a global `path` option to the path containing the gem's files. This will automatically load gemspec files from subdirectories.
+
+ path 'components' do
+ gem 'admin_ui'
+ gem 'public_ui'
+ end
+
+## BLOCK FORM OF SOURCE, GIT, PATH, GROUP and PLATFORMS
+
+The `:source`, `:git`, `:path`, `:group`, and `:platforms` options may be
+applied to a group of gems by using block form.
+
+ source "https://gems.example.com" do
+ gem "some_internal_gem"
+ gem "another_internal_gem"
+ end
+
+ git "https://github.com/rails/rails.git" do
+ gem "activesupport"
+ gem "actionpack"
+ end
+
+ platforms :ruby do
+ gem "ruby-debug"
+ gem "sqlite3"
+ end
+
+ group :development, :optional => true do
+ gem "wirble"
+ gem "faker"
+ end
+
+In the case of the group block form the :optional option can be given
+to prevent a group from being installed unless listed in the `--with`
+option given to the `bundle install` command.
+
+In the case of the `git` block form, the `:ref`, `:branch`, `:tag`,
+and `:submodules` options may be passed to the `git` method, and
+all gems in the block will inherit those options.
+
+The presence of a `source` block in a Gemfile also makes that source
+available as a possible global source for any other gems which do not specify
+explicit sources. Thus, when defining source blocks, it is
+recommended that you also ensure all other gems in the Gemfile are using
+explicit sources, either via source blocks or `:source` directives on
+individual gems.
+
+## INSTALL_IF
+
+The `install_if` method allows gems to be installed based on a proc or lambda.
+This is especially useful for optional gems that can only be used if certain
+software is installed or some other conditions are met.
+
+ install_if -> { RUBY_PLATFORM =~ /darwin/ } do
+ gem "pasteboard"
+ end
+
+## GEMSPEC
+
+The [`.gemspec`](http://guides.rubygems.org/specification-reference/) file is where
+ you provide metadata about your gem to Rubygems. Some required Gemspec
+ attributes include the name, description, and homepage of your gem. This is
+ also where you specify the dependencies your gem needs to run.
+
+If you wish to use Bundler to help install dependencies for a gem while it is
+being developed, use the `gemspec` method to pull in the dependencies listed in
+the `.gemspec` file.
+
+The `gemspec` method adds any runtime dependencies as gem requirements in the
+default group. It also adds development dependencies as gem requirements in the
+`development` group. Finally, it adds a gem requirement on your project (`:path
+=> '.'`). In conjunction with `Bundler.setup`, 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.
+
+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
+(if more than one is present), and which group development dependencies are included in.
+
+When a `gemspec` 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 `gemspec` gem.
+
+## SOURCE PRIORITY
+
+When attempting to locate a gem to satisfy a gem requirement,
+bundler uses the following priority order:
+
+ 1. The source explicitly attached to the gem (using `:source`, `:path`, or
+ `:git`)
+ 2. For implicit gems (dependencies of explicit gems), any source, git, or path
+ repository declared on the parent. This results in bundler prioritizing the
+ ActiveSupport gem from the Rails git repository over ones from
+ `rubygems.org`
+ 3. The sources specified via global `source` lines, searching each source in
+ your `Gemfile` from last added to first added.
diff --git a/man/gemfile.5.txt b/man/gemfile.5.txt
new file mode 100644
index 0000000000..85ef7135d9
--- /dev/null
+++ b/man/gemfile.5.txt
@@ -0,0 +1,653 @@
+GEMFILE(5) GEMFILE(5)
+
+
+
+1mNAME0m
+ 1mGemfile 22m- A format for describing gem dependencies for Ruby programs
+
+1mSYNOPSIS0m
+ A 1mGemfile 22mdescribes the gem dependencies required to execute associated
+ Ruby code.
+
+ Place the 1mGemfile 22min the root of the directory containing the associ-
+ ated code. For instance, in a Rails application, place the 1mGemfile 22min
+ the same directory as the 1mRakefile22m.
+
+1mSYNTAX0m
+ A 1mGemfile 22mis evaluated as Ruby code, in a context which makes available
+ a number of methods used to describe the gem requirements.
+
+1mGLOBAL SOURCES0m
+ At the top of the 1mGemfile22m, add a line for the 1mRubygems 22msource that con-
+ tains the gems listed in the 1mGemfile22m.
+
+
+
+ source "https://rubygems.org"
+
+
+
+ It is possible, but not recommended as of Bundler 1.7, to add multiple
+ global 1msource 22mlines. Each of these 1msource22ms 1mMUST 22mbe a valid Rubygems
+ repository.
+
+ Sources are checked for gems following the heuristics described in
+ 4mSOURCE24m 4mPRIORITY24m. If a gem is found in more than one global source,
+ Bundler will print a warning after installing the gem indicating which
+ source was used, and listing the other sources where the gem is avail-
+ able. A specific source can be selected for gems that need to use a
+ non-standard repository, suppressing this warning, by using the 1m:source0m
+ option or a 1msource 22mblock.
+
+ 1mCREDENTIALS0m
+ Some gem sources require a username and password. Use bundle config(1)
+ 4mbundle-config.1.html24m to set the username and password for any of the
+ sources that need it. The command must be run once on each computer
+ that will install the Gemfile, but this keeps the credentials from
+ being stored in plain text in version control.
+
+
+
+ bundle config gems.example.com user:password
+
+
+
+ For some sources, like a company Gemfury account, it may be easier to
+ include the credentials in the Gemfile as part of the source URL.
+
+
+
+ source "https://user:password@gems.example.com"
+
+
+
+ Credentials in the source URL will take precedence over credentials set
+ using 1mconfig22m.
+
+1mRUBY0m
+ If your application requires a specific Ruby version or engine, specify
+ your requirements using the 1mruby 22mmethod, with the following arguments.
+ All parameters are 1mOPTIONAL 22munless otherwise specified.
+
+ 1mVERSION (required)0m
+ The version of Ruby that your application requires. If your application
+ requires an alternate Ruby engine, such as JRuby, Rubinius or Truf-
+ fleRuby, this should be the Ruby version that the engine is compatible
+ with.
+
+
+
+ ruby "1.9.3"
+
+
+
+ 1mENGINE0m
+ Each application 4mmay24m specify a Ruby engine. If an engine is specified,
+ an engine version 4mmust24m also be specified.
+
+ What exactly is an Engine? - A Ruby engine is an implementation of the
+ Ruby language.
+
+ o For background: the reference or original implementation of the
+ Ruby programming language is called Matz's Ruby Interpreter
+ 4mhttps://en.wikipedia.org/wiki/Ruby_MRI24m, 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.
+
+ o Other implementations 4mhttps://www.ruby-lang.org/en/about/24m of Ruby
+ exist. Some of the more well-known implementations include Rubinius
+ 4mhttps://rubinius.com/24m, and JRuby 4mhttp://jruby.org/24m. Rubinius is an
+ alternative implementation of Ruby written in Ruby. JRuby is an
+ implementation of Ruby on the JVM, short for Java Virtual Machine.
+
+
+
+ 1mENGINE VERSION0m
+ Each application 4mmay24m specify a Ruby engine version. If an engine ver-
+ sion is specified, an engine 4mmust24m also be specified. If the engine is
+ "ruby" the engine version specified 4mmust24m match the Ruby version.
+
+
+
+ ruby "1.8.7", :engine => "jruby", :engine_version => "1.6.7"
+
+
+
+ 1mPATCHLEVEL0m
+ Each application 4mmay24m specify a Ruby patchlevel.
+
+
+
+ ruby "2.0.0", :patchlevel => "247"
+
+
+
+1mGEMS0m
+ Specify gem requirements using the 1mgem 22mmethod, with the following argu-
+ ments. All parameters are 1mOPTIONAL 22munless otherwise specified.
+
+ 1mNAME (required)0m
+ For each gem requirement, list a single 4mgem24m line.
+
+
+
+ gem "nokogiri"
+
+
+
+ 1mVERSION0m
+ Each 4mgem24m 1mMAY 22mhave one or more version specifiers.
+
+
+
+ gem "nokogiri", ">= 1.4.2"
+ gem "RedCloth", ">= 4.1.0", "< 4.2.0"
+
+
+
+ 1mREQUIRE AS0m
+ Each 4mgem24m 1mMAY 22mspecify files that should be used when autorequiring via
+ 1mBundler.require22m. You may pass an array with multiple files or 1mtrue 22mif
+ file you want 1mrequired 22mhas same name as 4mgem24m or 1mfalse 22mto prevent any
+ file from being autorequired.
+
+
+
+ gem "redis", :require => ["redis/connection/hiredis", "redis"]
+ gem "webmock", :require => false
+ gem "debugger", :require => true
+
+
+
+ The argument defaults to the name of the gem. For example, these are
+ identical:
+
+
+
+ gem "nokogiri"
+ gem "nokogiri", :require => "nokogiri"
+ gem "nokogiri", :require => true
+
+
+
+ 1mGROUPS0m
+ Each 4mgem24m 1mMAY 22mspecify membership in one or more groups. Any 4mgem24m that
+ does not specify membership in any group is placed in the 1mdefault0m
+ group.
+
+
+
+ gem "rspec", :group => :test
+ gem "wirble", :groups => [:development, :test]
+
+
+
+ The Bundler runtime allows its two main methods, 1mBundler.setup 22mand
+ 1mBundler.require22m, to limit their impact to particular groups.
+
+
+
+ # setup adds gems to Ruby's load path
+ Bundler.setup # defaults to all groups
+ require "bundler/setup" # same as Bundler.setup
+ Bundler.setup(:default) # only set up the _default_ group
+ Bundler.setup(:test) # only set up the _test_ group (but `not` _default_)
+ Bundler.setup(:default, :test) # set up the _default_ and _test_ groups, but no others
+
+ # require requires all of the gems in the specified groups
+ Bundler.require # defaults to the _default_ group
+ Bundler.require(:default) # identical
+ Bundler.require(:default, :test) # requires the _default_ and _test_ groups
+ Bundler.require(:test) # requires the _test_ group
+
+
+
+ The Bundler CLI allows you to specify a list of groups whose gems 1mbun-0m
+ 1mdle install 22mshould not install with the 1m--without 22moption. To specify
+ multiple groups to ignore, specify a list of groups separated by spa-
+ ces.
+
+
+
+ bundle install --without test
+ bundle install --without development test
+
+
+
+ After running 1mbundle install --without test22m, bundler will remember that
+ you excluded the test group in the last installation. The next time you
+ run 1mbundle install22m, without any 1m--without option22m, bundler will recall
+ it.
+
+ Also, calling 1mBundler.setup 22mwith no parameters, or calling 1mrequire0m
+ 1m"bundler/setup" 22mwill setup all groups except for the ones you excluded
+ via 1m--without 22m(since they are not available).
+
+ Note that on 1mbundle install22m, bundler downloads and evaluates all gems,
+ in order to create a single canonical list of all of the required gems
+ and their dependencies. This means that you cannot list different ver-
+ sions of the same gems in different groups. For more details, see
+ Understanding Bundler 4mhttp://bundler.io/rationale.html24m.
+
+ 1mPLATFORMS0m
+ If a gem should only be used in a particular platform or set of plat-
+ forms, you can specify them. Platforms are essentially identical to
+ groups, except that you do not need to use the 1m--without 22minstall-time
+ flag to exclude groups of gems for other platforms.
+
+ There are a number of 1mGemfile 22mplatforms:
+
+ 1mruby 22mC Ruby (MRI), Rubinius or TruffleRuby, but 1mNOT 22mWindows
+
+ 1mmri 22mSame as 4mruby24m, but only C Ruby (MRI)
+
+ 1mmingw 22mWindows 32 bit 'mingw32' platform (aka RubyInstaller)
+
+ 1mx64_mingw0m
+ Windows 64 bit 'mingw32' platform (aka RubyInstaller x64)
+
+ 1mrbx 22mRubinius
+
+ 1mjruby 22mJRuby
+
+ 1mtruffleruby0m
+ TruffleRuby
+
+ 1mmswin 22mWindows
+
+ You can restrict further by platform and version for all platforms
+ 4mexcept24m for 1mrbx22m, 1mjruby22m, 1mtruffleruby 22mand 1mmswin22m.
+
+ To specify a version in addition to a platform, append the version num-
+ ber without the delimiter to the platform. For example, to specify that
+ a gem should only be used on platforms with Ruby 2.3, use:
+
+
+
+ ruby_23
+
+
+
+ The full list of platforms and supported versions includes:
+
+ 1mruby 22m1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5
+
+ 1mmri 22m1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5
+
+ 1mmingw 22m1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5
+
+ 1mx64_mingw0m
+ 2.0, 2.1, 2.2, 2.3, 2.4, 2.5
+
+ As with groups, you can specify one or more platforms:
+
+
+
+ gem "weakling", :platforms => :jruby
+ gem "ruby-debug", :platforms => :mri_18
+ gem "nokogiri", :platforms => [:mri_18, :jruby]
+
+
+
+ All operations involving groups (1mbundle install 4m22mbundle-install.1.html24m,
+ 1mBundler.setup22m, 1mBundler.require22m) behave exactly the same as if any
+ groups not matching the current platform were explicitly excluded.
+
+ 1mSOURCE0m
+ You can select an alternate Rubygems repository for a gem using the
+ ':source' option.
+
+
+
+ gem "some_internal_gem", :source => "https://gems.example.com"
+
+
+
+ This forces the gem to be loaded from this source and ignores any
+ global sources declared at the top level of the file. If the gem does
+ not exist in this source, it will not be installed.
+
+ Bundler will search for child dependencies of this gem by first looking
+ in the source selected for the parent, but if they are not found there,
+ it will fall back on global sources using the ordering described in
+ 4mSOURCE24m 4mPRIORITY24m.
+
+ Selecting a specific source repository this way also suppresses the
+ ambiguous gem warning described above in 4mGLOBAL24m 4mSOURCES24m 4m(#source)24m.
+
+ Using the 1m:source 22moption for an individual gem will also make that
+ source available as a possible global source for any other gems which
+ do not specify explicit sources. Thus, when adding gems with explicit
+ sources, it is recommended that you also ensure all other gems in the
+ Gemfile are using explicit sources.
+
+ 1mGIT0m
+ If necessary, you can specify that a gem is located at a particular git
+ repository using the 1m:git 22mparameter. The repository can be accessed via
+ several protocols:
+
+ 1mHTTP(S)0m
+ gem "rails", :git => "https://github.com/rails/rails.git"
+
+ 1mSSH 22mgem "rails", :git => "git@github.com:rails/rails.git"
+
+ 1mgit 22mgem "rails", :git => "git://github.com/rails/rails.git"
+
+ If using SSH, the user that you use to run 1mbundle install MUST 22mhave the
+ appropriate keys available in their 1m$HOME/.ssh22m.
+
+ 1mNOTE22m: 1mhttp:// 22mand 1mgit:// 22mURLs should be avoided if at all possible.
+ These protocols are unauthenticated, so a man-in-the-middle attacker
+ can deliver malicious code and compromise your system. HTTPS and SSH
+ are strongly preferred.
+
+ The 1mgroup22m, 1mplatforms22m, and 1mrequire 22moptions are available and behave
+ exactly the same as they would for a normal gem.
+
+ A git repository 1mSHOULD 22mhave at least one file, at the root of the
+ directory containing the gem, with the extension 1m.gemspec22m. This file
+ 1mMUST 22mcontain a valid gem specification, as expected by the 1mgem build0m
+ command.
+
+ If a git repository does not have a 1m.gemspec22m, bundler will attempt to
+ create one, but it will not contain any dependencies, executables, or C
+ extension compilation instructions. As a result, it may fail to prop-
+ erly integrate into your application.
+
+ If a git repository does have a 1m.gemspec 22mfor the gem you attached it
+ to, a version specifier, if provided, means that the git repository is
+ only valid if the 1m.gemspec 22mspecifies a version matching the version
+ specifier. If not, bundler will print a warning.
+
+
+
+ gem "rails", "2.3.8", :git => "https://github.com/rails/rails.git"
+ # bundle install will fail, because the .gemspec in the rails
+ # repository's master branch specifies version 3.0.0
+
+
+
+ If a git repository does 1mnot 22mhave a 1m.gemspec 22mfor the gem you attached
+ it to, a version specifier 1mMUST 22mbe provided. Bundler will use this ver-
+ sion in the simple 1m.gemspec 22mit creates.
+
+ Git repositories support a number of additional options.
+
+ 1mbranch22m, 1mtag22m, and 1mref0m
+ You 1mMUST 22monly specify at most one of these options. The default
+ is 1m:branch => "master"0m
+
+ For example:
+
+ git "https://github.com/rails/rails.git", :branch => "5-0-sta-
+ ble" do
+
+ git "https://github.com/rails/rails.git", :tag => "v5.0.0" do
+
+ git "https://github.com/rails/rails.git", :ref => "4aded" do
+
+ 1msubmodules0m
+ For reference, a git submodule
+ 4mhttps://git-scm.com/book/en/v2/Git-Tools-Submodules24m lets you
+ have another git repository within a subfolder of your reposi-
+ tory. Specify 1m:submodules => true 22mto cause bundler to expand any
+ submodules included in the git repository
+
+ If a git repository contains multiple 1m.gemspecs22m, each 1m.gemspec 22mrepre-
+ sents a gem located at the same place in the file system as the 1m.gem-0m
+ 1mspec22m.
+
+
+
+ |~rails [git root]
+ | |-rails.gemspec [rails gem located here]
+ |~actionpack
+ | |-actionpack.gemspec [actionpack gem located here]
+ |~activesupport
+ | |-activesupport.gemspec [activesupport gem located here]
+ |...
+
+
+
+ To install a gem located in a git repository, bundler changes to the
+ directory containing the gemspec, runs 1mgem build name.gemspec 22mand then
+ installs the resulting gem. The 1mgem build 22mcommand, which comes standard
+ with Rubygems, evaluates the 1m.gemspec 22min the context of the directory
+ in which it is located.
+
+ 1mGIT SOURCE0m
+ A custom git source can be defined via the 1mgit_source 22mmethod. Provide
+ the source's name as an argument, and a block which receives a single
+ argument and interpolates it into a string to return the full repo
+ address:
+
+
+
+ git_source(:stash){ |repo_name| "https://stash.corp.acme.pl/#{repo_name}.git" }
+ gem 'rails', :stash => 'forks/rails'
+
+
+
+ In addition, if you wish to choose a specific branch:
+
+
+
+ gem "rails", :stash => "forks/rails", :branch => "branch_name"
+
+
+
+ 1mGITHUB0m
+ 1mNOTE22m: This shorthand should be avoided until Bundler 2.0, since it cur-
+ rently expands to an insecure 1mgit:// 22mURL. This allows a man-in-the-mid-
+ dle attacker to compromise your system.
+
+ If the git repository you want to use is hosted on GitHub and is pub-
+ lic, you can use the :github shorthand to specify the github username
+ and repository name (without the trailing ".git"), separated by a
+ slash. If both the username and repository name are the same, you can
+ omit one.
+
+
+
+ gem "rails", :github => "rails/rails"
+ gem "rails", :github => "rails"
+
+
+
+ Are both equivalent to
+
+
+
+ gem "rails", :git => "git://github.com/rails/rails.git"
+
+
+
+ Since the 1mgithub 22mmethod is a specialization of 1mgit_source22m, it accepts a
+ 1m:branch 22mnamed argument.
+
+ 1mGIST0m
+ If the git repository you want to use is hosted as a Github Gist and is
+ public, you can use the :gist shorthand to specify the gist identifier
+ (without the trailing ".git").
+
+
+
+ gem "the_hatch", :gist => "4815162342"
+
+
+
+ Is equivalent to:
+
+
+
+ gem "the_hatch", :git => "https://gist.github.com/4815162342.git"
+
+
+
+ Since the 1mgist 22mmethod is a specialization of 1mgit_source22m, it accepts a
+ 1m:branch 22mnamed argument.
+
+ 1mBITBUCKET0m
+ If the git repository you want to use is hosted on Bitbucket and is
+ public, you can use the :bitbucket shorthand to specify the bitbucket
+ username and repository name (without the trailing ".git"), separated
+ by a slash. If both the username and repository name are the same, you
+ can omit one.
+
+
+
+ gem "rails", :bitbucket => "rails/rails"
+ gem "rails", :bitbucket => "rails"
+
+
+
+ Are both equivalent to
+
+
+
+ gem "rails", :git => "https://rails@bitbucket.org/rails/rails.git"
+
+
+
+ Since the 1mbitbucket 22mmethod is a specialization of 1mgit_source22m, it
+ accepts a 1m:branch 22mnamed argument.
+
+ 1mPATH0m
+ You can specify that a gem is located in a particular location on the
+ file system. Relative paths are resolved relative to the directory con-
+ taining the 1mGemfile22m.
+
+ Similar to the semantics of the 1m:git 22moption, the 1m:path 22moption requires
+ that the directory in question either contains a 1m.gemspec 22mfor the gem,
+ or that you specify an explicit version that bundler should use.
+
+ Unlike 1m:git22m, bundler does not compile C extensions for gems specified
+ as paths.
+
+
+
+ gem "rails", :path => "vendor/rails"
+
+
+
+ If you would like to use multiple local gems directly from the filesys-
+ tem, you can set a global 1mpath 22moption to the path containing the gem's
+ files. This will automatically load gemspec files from subdirectories.
+
+
+
+ path 'components' do
+ gem 'admin_ui'
+ gem 'public_ui'
+ end
+
+
+
+1mBLOCK FORM OF SOURCE, GIT, PATH, GROUP and PLATFORMS0m
+ The 1m:source22m, 1m:git22m, 1m:path22m, 1m:group22m, and 1m:platforms 22moptions may be applied
+ to a group of gems by using block form.
+
+
+
+ source "https://gems.example.com" do
+ gem "some_internal_gem"
+ gem "another_internal_gem"
+ end
+
+ git "https://github.com/rails/rails.git" do
+ gem "activesupport"
+ gem "actionpack"
+ end
+
+ platforms :ruby do
+ gem "ruby-debug"
+ gem "sqlite3"
+ end
+
+ group :development, :optional => true do
+ gem "wirble"
+ gem "faker"
+ end
+
+
+
+ In the case of the group block form the :optional option can be given
+ to prevent a group from being installed unless listed in the 1m--with0m
+ option given to the 1mbundle install 22mcommand.
+
+ In the case of the 1mgit 22mblock form, the 1m:ref22m, 1m:branch22m, 1m:tag22m, and 1m:sub-0m
+ 1mmodules 22moptions may be passed to the 1mgit 22mmethod, and all gems in the
+ block will inherit those options.
+
+ The presence of a 1msource 22mblock in a Gemfile also makes that source
+ available as a possible global source for any other gems which do not
+ specify explicit sources. Thus, when defining source blocks, it is rec-
+ ommended that you also ensure all other gems in the Gemfile are using
+ explicit sources, either via source blocks or 1m:source 22mdirectives on
+ individual gems.
+
+1mINSTALL_IF0m
+ The 1minstall_if 22mmethod allows gems to be installed based on a proc or
+ lambda. This is especially useful for optional gems that can only be
+ used if certain software is installed or some other conditions are met.
+
+
+
+ install_if -> { RUBY_PLATFORM =~ /darwin/ } do
+ gem "pasteboard"
+ end
+
+
+
+1mGEMSPEC0m
+ The 1m.gemspec 4m22mhttp://guides.rubygems.org/specification-reference/24m file
+ is where you provide metadata about your gem to Rubygems. Some required
+ Gemspec attributes include the name, description, and homepage of your
+ gem. This is also where you specify the dependencies your gem needs to
+ run.
+
+ If you wish to use Bundler to help install dependencies for a gem while
+ it is being developed, use the 1mgemspec 22mmethod to pull in the dependen-
+ cies listed in the 1m.gemspec 22mfile.
+
+ The 1mgemspec 22mmethod adds any runtime dependencies as gem requirements in
+ the default group. It also adds development dependencies as gem
+ requirements in the 1mdevelopment 22mgroup. Finally, it adds a gem require-
+ ment on your project (1m:path => '.'22m). In conjunction with 1mBundler.setup22m,
+ 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.
+
+ The 1mgemspec 22mmethod supports optional 1m:path22m, 1m:glob22m, 1m:name22m, and 1m:develop-0m
+ 1mment_group 22moptions, which control where bundler looks for the 1m.gemspec22m,
+ the glob it uses to look for the gemspec (defaults to: "{,4m,24m/*}.gem-
+ spec"), what named 1m.gemspec 22mit uses (if more than one is present), and
+ which group development dependencies are included in.
+
+ When a 1mgemspec 22mdependency encounters version conflicts during resolu-
+ tion, the local version under development will always be selected --
+ even if there are remote versions that better match other requirements
+ for the 1mgemspec 22mgem.
+
+1mSOURCE PRIORITY0m
+ When attempting to locate a gem to satisfy a gem requirement, bundler
+ uses the following priority order:
+
+ 1. The source explicitly attached to the gem (using 1m:source22m, 1m:path22m, or
+ 1m:git22m)
+
+ 2. For implicit gems (dependencies of explicit gems), any source, git,
+ or path repository declared on the parent. This results in bundler
+ prioritizing the ActiveSupport gem from the Rails git repository
+ over ones from 1mrubygems.org0m
+
+ 3. The sources specified via global 1msource 22mlines, searching each
+ source in your 1mGemfile 22mfrom last added to first added.
+
+
+
+
+
+
+ November 2018 GEMFILE(5)
diff --git a/man/goruby.1 b/man/goruby.1
index 857874f070..a305a5afce 100644
--- a/man/goruby.1
+++ b/man/goruby.1
@@ -1,6 +1,6 @@
.\"Ruby is copyrighted by Yukihiro Matsumoto <matz@netlab.jp>.
-.Dd November 15, 2012
-.Dt GORUBY(1) "" "Ruby Programmers Reference Guide"
+.Dd April 20, 2017
+.Dt GORUBY \&1 "Ruby Programmer's Reference Guide"
.Os UNIX
.Sh NAME
.Nm goruby
diff --git a/man/irb.1 b/man/irb.1
index fca410c999..3e7ec6ff9d 100644
--- a/man/irb.1
+++ b/man/irb.1
@@ -1,6 +1,6 @@
.\"Ruby is copyrighted by Yukihiro Matsumoto <matz@netlab.jp>.
-.Dd November 15, 2012
-.Dt IRB(1) "" "Ruby Programmers Reference Guide"
+.Dd April 20, 2017
+.Dt IRB \&1 "Ruby Programmer's Reference Guide"
.Os UNIX
.Sh NAME
.Nm irb
@@ -69,9 +69,6 @@ Suppresses read of
.It Fl -help
Prints a summary of the options.
.Pp
-.It Fl m
-Bc mode (load mathn, fraction or matrix are available)
-.Pp
.It Fl r Ar library
Same as `ruby -r'.
Causes irb to load the library using require.
@@ -159,15 +156,17 @@ Personal irb initialization.
.Pp
.Sh REPORTING BUGS
.Bl -bullet
-.Li Security vulnerabilities should be reported via an email to
-.Aq security@ruby-lang.org .
+.It
+Security vulnerabilities should be reported via an email to
+.Mt security@ruby-lang.org .
Reported problems will be published after being fixed.
.Pp
-.Li And you can report other bugs and feature requests via the
+.It
+Other bugs and feature requests can be reported via the
Ruby Issue Tracking System
.Pq Lk https://bugs.ruby-lang.org/ .
Do not report security vulnerabilities
-via the system because it publishes the vulnerabilities immediately.
+via this system because it publishes the vulnerabilities immediately.
.El
.Sh AUTHORS
Written by Keiju ISHITSUKA.
diff --git a/man/ri.1 b/man/ri.1
index 01a37a3a1f..85a6dfcd53 100644
--- a/man/ri.1
+++ b/man/ri.1
@@ -1,47 +1,56 @@
.\"Ruby is copyrighted by Yukihiro Matsumoto <matz@netlab.jp>.
-.Dd July 10, 2013
-.Dt RI(1) "" "Ruby Programmers Reference Guide"
+.Dd April 20, 2017
+.Dt RI \&1 "Ruby Programmer's Reference Guide"
.Os UNIX
.Sh NAME
.Nm ri
.Nd Ruby API reference front end
.Sh SYNOPSIS
.Nm
-.Op Fl Ti
-.Op Fl d Ar directory
-.Op Fl f Ar format
-.Op Fl -list-doc-dirs
+.Op Fl ahilTv
+.Op Fl d Ar DIRNAME
+.Op Fl f Ar FORMAT
+.Op Fl w Ar WIDTH
+.Op Fl - Ns Oo Cm no- Oc Ns Cm pager
+.Op Fl -server Ns Oo = Ns Ar PORT Oc
+.Op Fl - Ns Oo Cm no- Oc Ns Cm list-doc-dirs
.Op Fl -no-standard-docs
.Op Fl - Ns Oo Cm no- Oc Ns Bro Cm system Ns | Ns Cm site Ns | Ns Cm gems Ns | Ns Cm home Brc
-.Op Fl - Ns Oo Cm no- Oc Ns Cm use-cache
-.Op Fl -width Ns = Ns Ar width
-.Op Ar target ...
+.Op Fl - Ns Oo Cm no- Oc Ns Cm profile
+.Op Fl -dump Ns = Ns Ar CACHE
+.Op Ar name ...
.Sh DESCRIPTION
.Nm
-is a CLI front end for the Ruby API reference.
-You can search and read API reference for classes and methods with
+is a command-line front end for the Ruby API reference.
+You can search and read the API reference for classes and methods with
.Nm .
.Pp
.Nm
is a part of Ruby.
.Pp
-.Ar target
-can be one of the following forms:
+.Ar name
+can be:
.Bl -diag -offset indent
-.It Class
-for classes
-.It Class::method
-for class methods
-.It Class#method
-for instance methods
-.It Class.method
-for both class and instance methods
-.It method
-for both class and instance methods
+.It Class | Module | Module::Class
+.Pp
+.It Class::method | Class#method | Class.method | method
+.Pp
+.It gem_name: | gem_name:README | gem_name:History
.El
.Pp
-All class names may be abbreviated to their minimum unambiguous form. If a name
-is ambiguous, all valid options will be listed.
+All class names may be abbreviated to their minimum unambiguous form.
+If a name is ambiguous, all valid options will be listed.
+.Pp
+A
+.Ql \&.
+matches either class or instance methods, while #method
+matches only instance and ::method matches only class methods.
+.Pp
+README and other files may be displayed by prefixing them with the gem name
+they're contained in. If the gem name is followed by a
+.Ql \&:
+all files in the gem will be shown.
+The file name extension may be omitted where it is unambiguous.
.Pp
For example:
.Bd -literal -offset indent
@@ -49,133 +58,190 @@ ri Fil
ri File
ri File.new
ri zip
+ri rdoc:README
.Ed
.Pp
-Note that shell quoting may be required for method names containing
-punctuation:
+Note that shell quoting or escaping may be required for method names
+containing punctuation:
.Bd -literal -offset indent
ri 'Array.[]'
-ri compact\!
+ri compact\e!
+.Ed
+.Pp
+To see the default directories
+.Nm
+will search, run:
+.Bd -literal -offset indent
+ri --list-doc-dirs
.Ed
+.Pp
+Specifying the
+.Fl -system , Fl -site , Fl -home , Fl -gems ,
+or
+.Fl -doc-dir
+options will limit
+.Nm
+to searching only the specified directories.
+.Pp
+.Nm
+options may be set in the
+.Ev RI
+environment variable.
+.Pp
+The
+.Nm
+pager can be set with the
+.Ev RI_PAGER
+environment variable or the
+.Ev PAGER
+environment variable.
+.Pp
.Sh OPTIONS
.Bl -tag -width "1234567890123" -compact
.Pp
-.It Fl -version
-Prints the version of
-.Nm .
+.It Fl i
+.It Fl - Ns Oo Cm no- Oc Ns Cm interactive
+In interactive mode you can repeatedly
+look up methods with autocomplete.
.Pp
-.It Fl T
-.It Fl -no-pager
-Send output directly to stdout, rather than to a pager.
+.It Fl a
+.It Fl - Ns Oo Cm no- Oc Ns Cm all
+Show all documentation for a class or module.
.Pp
-.It Fl d Ar directory
-.It Fl -doc-dir Ns = Ns Ar directory
-List of directories from which to source documentation in addition to the standard
-directories. May be repeated.
+.It Fl l
+.It Fl - Ns Oo Cm no- Oc Ns Cm list
+List classes
+.Nm
+knows about.
.Pp
-.It Fl f Ar FORMAT
-.It Fl -fmt Ar FORMAT
-.It Fl -format Ns = Ns FORMAT
-Format to use when displaying output:
+.It Fl - Ns Oo Cm no- Oc Ns Cm pager
+Send output to a pager,
+rather than directly to stdout.
.Pp
-ansi, bs, html, plain, simple
+.It Fl T
+Synonym for
+.Fl -no-pager .
.Pp
-Use 'bs' (backspace) with most pager programs. To use ANSI, either disable the
-pager or tell the pager to allow control characters.
+.It Fl w Ar WIDTH
+.It Fl -width Ns = Ns Ar WIDTH
+Set the width of the output.
.Pp
-.It Fl i
-.It Fl -interactive
-This makes
-.Nm
-go into interactive mode.
+.It Fl -server Ns Oo = Ns Ar PORT Oc
+Run RDoc server on the given port.
+The default port is\~8214.
+.Pp
+.It Fl f Ar FORMAT
+.It Fl -format Ns = Ns Ar FORMAT
+Use the selected formatter.
+The default formatter is
+.Li bs
+for paged output and
+.Li ansi
+otherwise.
+Valid formatters are:
+.Li ansi , Li bs , Li markdown , Li rdoc .
+.Pp
+.It Fl h
+.It Fl -help
+Show help and exit.
+.Pp
+.It Fl v
+.It Fl -version
+Output version information and exit.
+.El
.Pp
-When
+Data source options:
+.Bl -tag -width "1234567890123" -compact
+.Pp
+.It Fl - Ns Oo Cm no- Oc Ns Cm list-doc-dirs
+List the directories from which
.Nm
-is in interactive mode it will allow the user to disambiguate lists of
-methods in case multiple methods match against a method search string. It also
-will allow the user to enter in a method name (with auto-completion, if readline
-is supported) when viewing a class.
+will source documentation on stdout and exit.
.Pp
-.It Fl -list-doc-dirs
-List the directories from which ri will source documentation on stdout and exit.
+.It Fl d Ar DIRNAME
+.It Fl -doc-dir Ns = Ns Ar DIRNAME
+List of directories from which to source
+documentation in addition to the standard
+directories. May be repeated.
.Pp
.It Fl -no-standard-docs
Do not include documentation from the Ruby standard library,
.Pa site_lib ,
installed gems, or
.Pa ~/.rdoc .
-.Pp
-Equivalent to specifying the options
-.Fl -no-system , Fl -no-site , Fl -no-gems ,
-and
-.Fl -no-home .
+Use with
+.Fl -doc-dir .
.Pp
.It Fl - Ns Oo Cm no- Oc Ns Cm system
Include documentation from Ruby's standard library. Defaults to true.
.Pp
.It Fl - Ns Oo Cm no- Oc Ns Cm site
- Include documentation from libraries installed in site_lib. Defaults to true.
+Include documentation from libraries installed in
+.Pa site_lib .
+Defaults to true.
.Pp
.It Fl - Ns Oo Cm no- Oc Ns Cm gems
-Include documentation from RubyGems. Defaults to true.
+Include documentation from RubyGems. Defaults to true.
.Pp
.It Fl - Ns Oo Cm no- Oc Ns Cm home
-Include documentation stored in ~/.rdoc. Defaults to true.
+Include documentation stored in
+.Pa ~/.rdoc .
+Defaults to true.
+.El
.Pp
-.It Fl - Ns Oo Cm no- Oc Ns Cm use-cache
-Whether or not to use
-.Nm Ns
-.Ns 's cache. True by default.
+Debug options:
+.Bl -tag -width "1234567890123" -compact
.Pp
-.It Fl w Ar width
-.It Fl -width Ns = Ns Ar width
-Set the width of the output.
+.It Fl - Ns Oo Cm no- Oc Ns Cm profile
+Run with the Ruby profiler.
.Pp
+.It Fl -dump Ns = Ns Ar CACHE
+Dump data from an ri cache or data file.
.El
.Pp
.Sh ENVIRONMENT
.Bl -tag -width "USERPROFILE" -compact
.Pp
.It Ev RI
-Additional options.
+Options to prepend to those specified on the command-line.
.Pp
+.It Ev RI_PAGER
.It Ev PAGER
-Used as the name of pager program for displaying.
+Pager program to use for displaying.
.Pp
.It Ev HOME
.It Ev USERPROFILE
.It Ev HOMEPATH
-Path to user's home directory.
+Path to the user's home directory.
.El
.Pp
.Sh FILES
.Bl -tag -width "USERPROFILE" -compact
.Pp
-.It Pa ~/.ri
-Caches recently referenced documents here.
-.Pp
.It Pa ~/.rdoc
-Searches user-wide documents here.
+Path for ri data in the user's home directory.
.Pp
.El
.Pp
.Sh SEE ALSO
-.Xr ruby 1
-.Xr rdoc 1
+.Xr ruby 1 ,
+.Xr rdoc 1 ,
.Xr gem 1
.Pp
.Sh REPORTING BUGS
.Bl -bullet
-.Li Security vulnerabilities should be reported via an email to
-.Aq security@ruby-lang.org .
+.It
+Security vulnerabilities should be reported via an email to
+.Mt security@ruby-lang.org .
Reported problems will be published after being fixed.
.Pp
-.Li And you can report other bugs and feature requests via the
+.It
+Other bugs and feature requests can be reported via the
Ruby Issue Tracking System
.Pq Lk https://bugs.ruby-lang.org/ .
Do not report security vulnerabilities
-via the system because it publishes the vulnerabilities immediately.
+via this system because it publishes the vulnerabilities immediately.
.El
.Sh AUTHORS
-Written by Dave Thomas
-.Aq dave@pragmaticprogrammer.com
+Written by
+.An Dave Thomas Aq dave@pragmaticprogrammer.com .
diff --git a/man/ruby.1 b/man/ruby.1
index 28a73e576a..4dd19054d3 100644
--- a/man/ruby.1
+++ b/man/ruby.1
@@ -1,7 +1,6 @@
.\"Ruby is copyrighted by Yukihiro Matsumoto <matz@netlab.jp>.
-.Dd October 31, 2015
-.Dt RUBY(1) "" "Ruby Programmers Reference Guide"
-.\".Dt RUBY 1
+.Dd April 14, 2018
+.Dt RUBY \&1 "Ruby Programmer's Reference Guide"
.Os UNIX
.Sh NAME
.Nm ruby
@@ -138,16 +137,18 @@ to see how they are being developed and used.
.El
.Pp
.Sh OPTIONS
-Ruby interpreter accepts following command-line options (switches).
+The Ruby interpreter accepts the following command-line options (switches).
They are quite similar to those of
.Xr perl 1 .
.Bl -tag -width "1234567890123" -compact
.Pp
.It Fl -copyright
-Prints the copyright notice.
+Prints the copyright notice, and quits immediately without running any
+script.
.Pp
.It Fl -version
-Prints the version of Ruby interpreter.
+Prints the version of the Ruby interpreter, and quits immediately without
+running any script.
.Pp
.It Fl 0 Ns Op Ar octal
(The digit
@@ -218,6 +219,18 @@ on machines that don't support it, in the following manner:
exec /usr/local/bin/ruby -S $0 $*
.Ed
.Pp
+On some systems
+.Li "$0"
+does not always contain the full pathname, so you need the
+.Fl S
+switch to tell Ruby to search for the script if necessary (to handle embedded
+spaces and such). A better construct than
+.Li "$*"
+would be
+.Li ${1+"$@"} ,
+but it does not work if the script is being interpreted by
+.Xr csh 1 .
+.Pp
.It Fl T Ns Op Ar level=1
Turns on taint checks at the specified level (default 1).
.Pp
@@ -343,18 +356,6 @@ and set the corresponding variable in the script. For example:
print "true\en" if $xyz
.Ed
.Pp
-On some systems
-.Li "$0"
-does not always contain the full pathname, so you need the
-.Fl S
-switch to tell Ruby to search for the script if necessary (to handle embedded
-spaces and such). A better construct than
-.Li "$*"
-would be
-.Li ${1+"$@"} ,
-but it does not work if the script is being interpreted by
-.Xr csh 1 .
-.Pp
.It Fl v
Enables verbose mode. Ruby will print its version at the beginning
and set the variable
@@ -458,8 +459,9 @@ Enables verbose mode without printing version message at the
beginning. It sets the
.Li "$VERBOSE"
variable to true.
-If this switch is given, and no other switches are present, Ruby quits
-after printing its version.
+If this switch is given, and no script arguments (script file or
+.Fl e
+options) are present, Ruby quits immediately.
.El
.Pp
.Sh ENVIRONMENT
@@ -641,15 +643,17 @@ Comprehensive catalog of Ruby libraries.
.Pp
.Sh REPORTING BUGS
.Bl -bullet
-.Li Security vulnerabilities should be reported via an email to
-.Aq security@ruby-lang.org .
-Reported problems will be published after they've been fixed.
+.It
+Security vulnerabilities should be reported via an email to
+.Mt security@ruby-lang.org .
+Reported problems will be published after being fixed.
.Pp
-.Li And you can report other bugs and feature requests via the
+.It
+Other bugs and feature requests can be reported via the
Ruby Issue Tracking System
.Pq Lk https://bugs.ruby-lang.org/ .
Do not report security vulnerabilities
-via the system because it publishes the vulnerabilities immediately.
+via this system because it publishes the vulnerabilities immediately.
.El
.Sh AUTHORS
Ruby is designed and implemented by
diff --git a/marshal.c b/marshal.c
index 3560cb5d80..bb5b1a95da 100644
--- a/marshal.c
+++ b/marshal.c
@@ -9,12 +9,9 @@
**********************************************************************/
-#if defined __GNUC__ && __GNUC__ < 3
-# error too old GCC
-#endif
-
-#include "internal.h"
+#include "ruby/ruby.h"
#include "ruby/io.h"
+#include "internal.h"
#include "ruby/st.h"
#include "ruby/util.h"
#include "encindex.h"
@@ -178,8 +175,22 @@ check_dump_arg(VALUE ret, struct dump_arg *arg, const char *name)
}
return ret;
}
+
+static VALUE
+check_userdump_arg(VALUE obj, ID sym, int argc, const VALUE *argv,
+ 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);
+ }
+ return check_dump_arg(ret, arg, name);
+}
+
#define dump_funcall(arg, obj, sym, argc, argv) \
- check_dump_arg(rb_funcallv(obj, sym, argc, argv), arg, name_##sym)
+ check_userdump_arg(obj, sym, argc, argv, arg, name_##sym)
#define dump_check_funcall(arg, obj, sym, argc, argv) \
check_dump_arg(rb_check_funcall(obj, sym, argc, argv), arg, name_##sym)
@@ -246,7 +257,7 @@ class2path(VALUE klass)
}
static void w_long(long, struct dump_arg*);
-static void w_encoding(VALUE encname, struct dump_call_arg *arg);
+static int w_encoding(VALUE encname, struct dump_call_arg *arg);
static VALUE encoding_name(VALUE obj, struct dump_arg *arg);
static void
@@ -390,8 +401,8 @@ w_float(double d, struct dump_arg *arg)
w_cstr("nan", arg);
}
else if (d == 0.0) {
- if (1.0/d < 0) w_cstr("-0", arg);
- else w_cstr("0", arg);
+ if (signbit(d)) w_cstr("-0", arg);
+ else w_cstr("0", arg);
}
else {
int decpt, sign, digs, len = 0;
@@ -539,14 +550,25 @@ w_uclass(VALUE obj, VALUE super, struct dump_arg *arg)
#define to_be_skipped_id(id) (id == rb_id_encoding() || id == rb_intern("E") || !rb_id2str(id))
+struct w_ivar_arg {
+ struct dump_call_arg *dump;
+ st_data_t num_ivar;
+};
+
static int
w_obj_each(st_data_t key, st_data_t val, st_data_t a)
{
ID id = (ID)key;
VALUE value = (VALUE)val;
- struct dump_call_arg *arg = (struct dump_call_arg *)a;
+ struct w_ivar_arg *ivarg = (struct w_ivar_arg *)a;
+ struct dump_call_arg *arg = ivarg->dump;
if (to_be_skipped_id(id)) 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);
return ST_CONTINUE;
@@ -563,44 +585,53 @@ obj_count_ivars(st_data_t key, st_data_t val, st_data_t a)
static VALUE
encoding_name(VALUE obj, struct dump_arg *arg)
{
- int encidx = rb_enc_get_index(obj);
- rb_encoding *enc = 0;
- st_data_t name;
+ if (rb_enc_capable(obj)) {
+ int encidx = rb_enc_get_index(obj);
+ rb_encoding *enc = 0;
+ st_data_t name;
- if (encidx <= 0 || !(enc = rb_enc_from_index(encidx))) {
- return Qnil;
- }
+ if (encidx <= 0 || !(enc = rb_enc_from_index(encidx))) {
+ return Qnil;
+ }
- /* special treatment for US-ASCII and UTF-8 */
- if (encidx == rb_usascii_encindex()) {
- return Qfalse;
- }
- else if (encidx == rb_utf8_encindex()) {
- return Qtrue;
- }
+ /* special treatment for US-ASCII and UTF-8 */
+ if (encidx == rb_usascii_encindex()) {
+ return Qfalse;
+ }
+ else if (encidx == rb_utf8_encindex()) {
+ return Qtrue;
+ }
- if (arg->encodings ?
- !st_lookup(arg->encodings, (st_data_t)rb_enc_name(enc), &name) :
- (arg->encodings = st_init_strcasetable(), 1)) {
- name = (st_data_t)rb_str_new_cstr(rb_enc_name(enc));
- st_insert(arg->encodings, (st_data_t)rb_enc_name(enc), name);
+ if (arg->encodings ?
+ !st_lookup(arg->encodings, (st_data_t)rb_enc_name(enc), &name) :
+ (arg->encodings = st_init_strcasetable(), 1)) {
+ name = (st_data_t)rb_str_new_cstr(rb_enc_name(enc));
+ st_insert(arg->encodings, (st_data_t)rb_enc_name(enc), name);
+ }
+ return (VALUE)name;
+ }
+ else {
+ return Qnil;
}
- return (VALUE)name;
}
-static void
+static int
w_encoding(VALUE encname, struct dump_call_arg *arg)
{
+ int limit = arg->limit;
+ if (limit >= 0) ++limit;
switch (encname) {
case Qfalse:
case Qtrue:
w_symbol(ID2SYM(rb_intern("E")), arg->arg);
- w_object(encname, arg->arg, arg->limit + 1);
+ w_object(encname, arg->arg, limit);
+ return 1;
case Qnil:
- return;
+ return 0;
}
w_symbol(ID2SYM(rb_id_encoding()), arg->arg);
- w_object(encname, arg->arg, arg->limit + 1);
+ w_object(encname, arg->arg, limit);
+ return 1;
}
static st_index_t
@@ -625,12 +656,24 @@ has_ivars(VALUE obj, VALUE encname, VALUE *ivobj)
}
static void
+w_ivar_each(VALUE obj, st_index_t num, struct dump_call_arg *arg)
+{
+ 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));
+ }
+}
+
+static void
w_ivar(st_index_t num, VALUE ivobj, VALUE encname, struct dump_call_arg *arg)
{
w_long(num, arg->arg);
- w_encoding(encname, arg);
+ num -= w_encoding(encname, arg);
if (ivobj != Qundef) {
- rb_ivar_foreach(ivobj, w_obj_each, (st_data_t)arg);
+ w_ivar_each(ivobj, num, arg);
}
}
@@ -641,9 +684,7 @@ w_objivar(VALUE obj, struct dump_call_arg *arg)
rb_ivar_foreach(obj, obj_count_ivars, (st_data_t)&num);
w_long(num, arg->arg);
- if (num != 0) {
- rb_ivar_foreach(obj, w_obj_each, (st_data_t)arg);
- }
+ w_ivar_each(obj, num, arg);
}
static void
@@ -659,9 +700,10 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
rb_raise(rb_eArgError, "exceed depth limit");
}
- 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);
@@ -799,6 +841,7 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
char sign = BIGNUM_SIGN(obj) ? '+' : '-';
size_t len = BIGNUM_LEN(obj);
size_t slen;
+ size_t j;
BDIGIT *d = BIGNUM_DIGITS(obj);
slen = SHORTLEN(len);
@@ -808,7 +851,7 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
w_byte(sign, arg);
w_long((long)slen, arg);
- while (len--) {
+ for (j = 0; j < len; j++) {
#if SIZEOF_BDIGIT > SIZEOF_SHORT
BDIGIT num = *d;
int i;
@@ -816,7 +859,7 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
for (i=0; i<SIZEOF_BDIGIT; i+=SIZEOF_SHORT) {
w_short(num & SHORTMASK, arg);
num = SHORTDN(num);
- if (len == 0 && num == 0) break;
+ if (j == len - 1 && num == 0) break;
}
#else
w_short(*d, arg);
@@ -869,7 +912,7 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
else {
w_byte(TYPE_HASH_DEF, arg);
}
- w_long(RHASH_SIZE(obj), 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);
@@ -1010,7 +1053,7 @@ rb_marshal_dump_limited(VALUE obj, VALUE port, int limit)
struct dump_arg *arg;
VALUE wrapper; /* used to avoid memory leak in case of exception */
- wrapper = TypedData_Make_Struct(rb_cData, struct dump_arg, &dump_arg_data, arg);
+ wrapper = TypedData_Make_Struct(0, struct dump_arg, &dump_arg_data, arg);
arg->dest = 0;
arg->symbols = st_init_numtable();
arg->data = rb_init_identtable();
@@ -1167,6 +1210,8 @@ r_byte(struct load_arg *arg)
return c;
}
+NORETURN(static void long_toobig(int size));
+
static void
long_toobig(int size)
{
@@ -1467,7 +1512,12 @@ r_ivar(VALUE obj, int *has_encoding, struct load_arg *arg)
VALUE val = r_object(arg);
int idx = sym2encidx(sym, val);
if (idx >= 0) {
- rb_enc_associate_index(obj, idx);
+ 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 {
@@ -1659,13 +1709,13 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
const char *ptr = RSTRING_PTR(str);
if (strcmp(ptr, "nan") == 0) {
- d = NAN;
+ d = nan("");
}
else if (strcmp(ptr, "inf") == 0) {
- d = INFINITY;
+ d = HUGE_VAL;
}
else if (strcmp(ptr, "-inf") == 0) {
- d = -INFINITY;
+ d = -HUGE_VAL;
}
else {
char *e;
@@ -1756,7 +1806,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
{
long len = r_long(arg);
- v = rb_hash_new();
+ v = rb_hash_new_with_size(len);
v = r_entry(v, arg);
arg->readable += (len - 1) * 2;
while (len--) {
@@ -1795,17 +1845,30 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
arg->readable += (len - 1) * 2;
v = r_entry0(v, idx, arg);
values = rb_ary_new2(len);
- 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);
+ {
+ 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_ary_push(values, r_object(arg));
- arg->readable -= 2;
}
rb_struct_initialize(v, values);
v = r_leave(v, arg);
@@ -2037,7 +2100,7 @@ rb_marshal_load_with_proc(VALUE port, VALUE proc)
else {
io_needed();
}
- wrapper = TypedData_Make_Struct(rb_cData, struct load_arg, &load_arg_data, arg);
+ wrapper = TypedData_Make_Struct(0, struct load_arg, &load_arg_data, arg);
arg->infection = infection;
arg->src = port;
arg->offset = 0;
@@ -2224,7 +2287,7 @@ compat_allocator_table(void)
#undef RUBY_UNTYPED_DATA_WARNING
#define RUBY_UNTYPED_DATA_WARNING 0
compat_allocator_tbl_wrapper =
- Data_Wrap_Struct(rb_cData, 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 e1c0e1b1ad..509cd46ef2 100644
--- a/math.c
+++ b/math.c
@@ -63,7 +63,7 @@ VALUE rb_eMathDomainError;
*/
static VALUE
-math_atan2(VALUE obj, VALUE y, VALUE x)
+math_atan2(VALUE unused_obj, VALUE y, VALUE x)
{
double dx, dy;
dx = Get_Double(x);
@@ -108,7 +108,7 @@ math_atan2(VALUE obj, VALUE y, VALUE x)
*/
static VALUE
-math_cos(VALUE obj, VALUE x)
+math_cos(VALUE unused_obj, VALUE x)
{
return DBL2NUM(cos(Get_Double(x)));
}
@@ -129,7 +129,7 @@ math_cos(VALUE obj, VALUE x)
*/
static VALUE
-math_sin(VALUE obj, VALUE x)
+math_sin(VALUE unused_obj, VALUE x)
{
return DBL2NUM(sin(Get_Double(x)));
}
@@ -150,7 +150,7 @@ math_sin(VALUE obj, VALUE x)
*/
static VALUE
-math_tan(VALUE obj, VALUE x)
+math_tan(VALUE unused_obj, VALUE x)
{
return DBL2NUM(tan(Get_Double(x)));
}
@@ -170,7 +170,7 @@ math_tan(VALUE obj, VALUE x)
*/
static VALUE
-math_acos(VALUE obj, VALUE x)
+math_acos(VALUE unused_obj, VALUE x)
{
double d;
@@ -194,7 +194,7 @@ math_acos(VALUE obj, VALUE x)
*/
static VALUE
-math_asin(VALUE obj, VALUE x)
+math_asin(VALUE unused_obj, VALUE x)
{
double d;
@@ -218,7 +218,7 @@ math_asin(VALUE obj, VALUE x)
*/
static VALUE
-math_atan(VALUE obj, VALUE x)
+math_atan(VALUE unused_obj, VALUE x)
{
return DBL2NUM(atan(Get_Double(x)));
}
@@ -246,7 +246,7 @@ cosh(double x)
*/
static VALUE
-math_cosh(VALUE obj, VALUE x)
+math_cosh(VALUE unused_obj, VALUE x)
{
return DBL2NUM(cosh(Get_Double(x)));
}
@@ -274,7 +274,7 @@ sinh(double x)
*/
static VALUE
-math_sinh(VALUE obj, VALUE x)
+math_sinh(VALUE unused_obj, VALUE x)
{
return DBL2NUM(sinh(Get_Double(x)));
}
@@ -309,7 +309,7 @@ tanh(double x)
*/
static VALUE
-math_tanh(VALUE obj, VALUE x)
+math_tanh(VALUE unused_obj, VALUE x)
{
return DBL2NUM(tanh(Get_Double(x)));
}
@@ -329,7 +329,7 @@ math_tanh(VALUE obj, VALUE x)
*/
static VALUE
-math_acosh(VALUE obj, VALUE x)
+math_acosh(VALUE unused_obj, VALUE x)
{
double d;
@@ -354,7 +354,7 @@ math_acosh(VALUE obj, VALUE x)
*/
static VALUE
-math_asinh(VALUE obj, VALUE x)
+math_asinh(VALUE unused_obj, VALUE x)
{
return DBL2NUM(asinh(Get_Double(x)));
}
@@ -374,7 +374,7 @@ math_asinh(VALUE obj, VALUE x)
*/
static VALUE
-math_atanh(VALUE obj, VALUE x)
+math_atanh(VALUE unused_obj, VALUE x)
{
double d;
@@ -382,8 +382,8 @@ math_atanh(VALUE obj, VALUE x)
/* check for domain error */
if (d < -1.0 || +1.0 < d) domain_error("atanh");
/* check for pole error */
- if (d == -1.0) return DBL2NUM(-INFINITY);
- if (d == +1.0) return DBL2NUM(+INFINITY);
+ if (d == -1.0) return DBL2NUM(-HUGE_VAL);
+ if (d == +1.0) return DBL2NUM(+HUGE_VAL);
return DBL2NUM(atanh(d));
}
@@ -404,7 +404,7 @@ math_atanh(VALUE obj, VALUE x)
*/
static VALUE
-math_exp(VALUE obj, VALUE x)
+math_exp(VALUE unused_obj, VALUE x)
{
return DBL2NUM(exp(Get_Double(x)));
}
@@ -426,6 +426,7 @@ math_exp(VALUE obj, VALUE x)
#endif
static double math_log1(VALUE x);
+FUNC_MINIMIZED(static VALUE math_log(int, const VALUE *, VALUE));
/*
* call-seq:
@@ -449,7 +450,13 @@ static double math_log1(VALUE x);
*/
static VALUE
-math_log(int argc, const VALUE *argv, VALUE obj)
+math_log(int argc, const VALUE *argv, VALUE unused_obj)
+{
+ return rb_math_log(argc, argv);
+}
+
+VALUE
+rb_math_log(int argc, const VALUE *argv)
{
VALUE x, base;
double d;
@@ -488,7 +495,7 @@ math_log1(VALUE x)
/* check for domain error */
if (d < 0.0) domain_error("log");
/* check for pole error */
- if (d == 0.0) return -INFINITY;
+ if (d == 0.0) return -HUGE_VAL;
return log(d) + numbits * M_LN2; /* log(d * 2 ** numbits) */
}
@@ -523,7 +530,7 @@ extern double log2(double);
*/
static VALUE
-math_log2(VALUE obj, VALUE x)
+math_log2(VALUE unused_obj, VALUE x)
{
size_t numbits;
double d = get_double_rshift(x, &numbits);
@@ -531,7 +538,7 @@ math_log2(VALUE obj, VALUE x)
/* check for domain error */
if (d < 0.0) domain_error("log2");
/* check for pole error */
- if (d == 0.0) return DBL2NUM(-INFINITY);
+ if (d == 0.0) return DBL2NUM(-HUGE_VAL);
return DBL2NUM(log2(d) + numbits); /* log2(d * 2 ** numbits) */
}
@@ -553,7 +560,7 @@ math_log2(VALUE obj, VALUE x)
*/
static VALUE
-math_log10(VALUE obj, VALUE x)
+math_log10(VALUE unused_obj, VALUE x)
{
size_t numbits;
double d = get_double_rshift(x, &numbits);
@@ -561,9 +568,9 @@ math_log10(VALUE obj, VALUE x)
/* check for domain error */
if (d < 0.0) domain_error("log10");
/* check for pole error */
- if (d == 0.0) return DBL2NUM(-INFINITY);
+ if (d == 0.0) return DBL2NUM(-HUGE_VAL);
- return DBL2NUM(log10(d) + numbits * M_LN2/M_LN10); /* log10(d * 2 ** numbits) */
+ return DBL2NUM(log10(d) + numbits * log10(2)); /* log10(d * 2 ** numbits) */
}
/*
@@ -590,10 +597,17 @@ math_log10(VALUE obj, VALUE x)
* # [8, 2.82842712474619, 8.0]
* # [9, 3.0, 9.0]
* # [10, 3.16227766016838, 10.0]
+ *
+ * Note that the limited precision of floating point arithmetic
+ * might lead to surprising results:
+ *
+ * Math.sqrt(10**46).to_i #=> 99999999999999991611392 (!)
+ *
+ * See also BigDecimal#sqrt and Integer.sqrt.
*/
static VALUE
-math_sqrt(VALUE obj, VALUE x)
+math_sqrt(VALUE unused_obj, VALUE x)
{
return rb_math_sqrt(x);
}
@@ -673,9 +687,16 @@ rb_math_sqrt(VALUE x)
*/
static VALUE
-math_cbrt(VALUE obj, VALUE x)
+math_cbrt(VALUE unused_obj, VALUE x)
{
- return DBL2NUM(cbrt(Get_Double(x)));
+ double f = Get_Double(x);
+ double r = cbrt(f);
+#if defined __GLIBC__
+ if (isfinite(r)) {
+ r = (2.0 * r + (f / r / r)) / 3.0;
+ }
+#endif
+ return DBL2NUM(r);
}
/*
@@ -690,7 +711,7 @@ math_cbrt(VALUE obj, VALUE x)
*/
static VALUE
-math_frexp(VALUE obj, VALUE x)
+math_frexp(VALUE unused_obj, VALUE x)
{
double d;
int exp;
@@ -710,7 +731,7 @@ math_frexp(VALUE obj, VALUE x)
*/
static VALUE
-math_ldexp(VALUE obj, VALUE x, VALUE n)
+math_ldexp(VALUE unused_obj, VALUE x, VALUE n)
{
return DBL2NUM(ldexp(Get_Double(x), NUM2INT(n)));
}
@@ -726,7 +747,7 @@ math_ldexp(VALUE obj, VALUE x, VALUE n)
*/
static VALUE
-math_hypot(VALUE obj, VALUE x, VALUE y)
+math_hypot(VALUE unused_obj, VALUE x, VALUE y)
{
return DBL2NUM(hypot(Get_Double(x), Get_Double(y)));
}
@@ -746,7 +767,7 @@ math_hypot(VALUE obj, VALUE x, VALUE y)
*/
static VALUE
-math_erf(VALUE obj, VALUE x)
+math_erf(VALUE unused_obj, VALUE x)
{
return DBL2NUM(erf(Get_Double(x)));
}
@@ -766,46 +787,11 @@ math_erf(VALUE obj, VALUE x)
*/
static VALUE
-math_erfc(VALUE obj, VALUE x)
+math_erfc(VALUE unused_obj, VALUE x)
{
return DBL2NUM(erfc(Get_Double(x)));
}
-#if defined __MINGW32__
-static inline double
-ruby_tgamma(const double d)
-{
- const double g = tgamma(d);
- if (isinf(g)) {
- if (d == 0.0 && signbit(d)) return -INFINITY;
- }
- if (isnan(g)) {
- if (!signbit(d)) return INFINITY;
- }
- return g;
-}
-#define tgamma(d) ruby_tgamma(d)
-#endif
-
-#if defined LGAMMA_R_PM0_FIX
-static inline double
-ruby_lgamma_r(const double d, int *sign)
-{
- const double g = lgamma_r(d, sign);
- if (isinf(g)) {
- if (d == 0.0 && signbit(d)) {
- *sign = -1;
- return INFINITY;
- } else if (d == 0.0 && !signbit(d)) {
- *sign = 1;
- return INFINITY;
- }
- }
- return g;
-}
-#define lgamma_r(d, sign) ruby_lgamma_r(d, sign)
-#endif
-
/*
* call-seq:
* Math.gamma(x) -> Float
@@ -847,7 +833,7 @@ ruby_lgamma_r(const double d, int *sign)
*/
static VALUE
-math_gamma(VALUE obj, VALUE x)
+math_gamma(VALUE unused_obj, VALUE x)
{
static const double fact_table[] = {
/* fact(0) */ 1.0,
@@ -881,7 +867,13 @@ math_gamma(VALUE obj, VALUE x)
double d;
d = Get_Double(x);
/* check for domain error */
- if (isinf(d) && signbit(d)) domain_error("gamma");
+ if (isinf(d)) {
+ if (signbit(d)) domain_error("gamma");
+ return DBL2NUM(HUGE_VAL);
+ }
+ if (d == 0.0) {
+ return signbit(d) ? DBL2NUM(-HUGE_VAL) : DBL2NUM(HUGE_VAL);
+ }
if (d == floor(d)) {
if (d < 0.0) domain_error("gamma");
if (1.0 <= d && d <= (double)NFACT_TABLE) {
@@ -906,7 +898,7 @@ math_gamma(VALUE obj, VALUE x)
*/
static VALUE
-math_lgamma(VALUE obj, VALUE x)
+math_lgamma(VALUE unused_obj, VALUE x)
{
double d;
int sign=1;
@@ -915,7 +907,11 @@ math_lgamma(VALUE obj, VALUE x)
/* check for domain error */
if (isinf(d)) {
if (signbit(d)) domain_error("lgamma");
- return rb_assoc_new(DBL2NUM(INFINITY), INT2FIX(1));
+ 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);
}
v = DBL2NUM(lgamma_r(d, &sign));
return rb_assoc_new(v, INT2FIX(sign));
@@ -926,14 +922,14 @@ math_lgamma(VALUE obj, VALUE x)
VALUE \
rb_math_##n(VALUE x)\
{\
- return math_##n(rb_mMath, x);\
+ return math_##n(0, x);\
}
#define exp2(n) \
VALUE \
rb_math_##n(VALUE x, VALUE y)\
{\
- return math_##n(rb_mMath, x, y);\
+ return math_##n(0, x, y);\
}
exp2(atan2)
@@ -941,13 +937,6 @@ exp1(cos)
exp1(cosh)
exp1(exp)
exp2(hypot)
-
-VALUE
-rb_math_log(int argc, const VALUE *argv)
-{
- return math_log(argc, argv, rb_mMath);
-}
-
exp1(sin)
exp1(sinh)
#if 0
@@ -989,12 +978,8 @@ InitVM_Math(void)
rb_mMath = rb_define_module("Math");
rb_eMathDomainError = rb_define_class_under(rb_mMath, "DomainError", rb_eStandardError);
-#ifdef M_PI
/* Definition of the mathematical constant PI as a Float number. */
rb_define_const(rb_mMath, "PI", DBL2NUM(M_PI));
-#else
- rb_define_const(rb_mMath, "PI", DBL2NUM(atan(1.0)*4.0));
-#endif
#ifdef M_E
/* Definition of the mathematical constant E (e) as a Float number. */
diff --git a/method.h b/method.h
index 510c9b6f40..771e77e889 100644
--- a/method.h
+++ b/method.h
@@ -33,10 +33,11 @@ typedef enum {
} rb_method_visibility_t;
typedef struct rb_scope_visi_struct {
- rb_method_visibility_t method_visi : 3;
+ BITFIELD(rb_method_visibility_t, method_visi, 3);
unsigned int module_func : 1;
} rb_scope_visibility_t;
+/*! CREF (Class REFerence) */
typedef struct rb_cref_struct {
VALUE flags;
const VALUE refinements;
@@ -98,21 +99,23 @@ METHOD_ENTRY_FLAGS_COPY(rb_method_entry_t *dst, const rb_method_entry_t *src)
}
typedef enum {
- VM_METHOD_TYPE_ISEQ,
- VM_METHOD_TYPE_CFUNC,
- VM_METHOD_TYPE_ATTRSET,
- VM_METHOD_TYPE_IVAR,
+ VM_METHOD_TYPE_ISEQ, /*!< Ruby method */
+ VM_METHOD_TYPE_CFUNC, /*!< C method */
+ VM_METHOD_TYPE_ATTRSET, /*!< attr_writer or attr_accessor */
+ VM_METHOD_TYPE_IVAR, /*!< attr_reader or attr_accessor */
VM_METHOD_TYPE_BMETHOD,
VM_METHOD_TYPE_ZSUPER,
VM_METHOD_TYPE_ALIAS,
VM_METHOD_TYPE_UNDEF,
VM_METHOD_TYPE_NOTIMPLEMENTED,
- VM_METHOD_TYPE_OPTIMIZED, /* Kernel#send, Proc#call, etc */
- VM_METHOD_TYPE_MISSING, /* wrapper for method_missing(id) */
- VM_METHOD_TYPE_REFINED,
+ VM_METHOD_TYPE_OPTIMIZED, /*!< Kernel#send, Proc#call, etc */
+ VM_METHOD_TYPE_MISSING, /*!< wrapper for method_missing(id) */
+ VM_METHOD_TYPE_REFINED, /*!< refinement */
END_OF_ENUMERATION(VM_METHOD_TYPE)
} rb_method_type_t;
+#define VM_METHOD_TYPE_MINIMUM_BITS 4
+/* TODO: STATIC_ASSERT for VM_METHOD_TYPE_MINIMUM_BITS */
#ifndef rb_iseq_t
typedef struct rb_iseq_struct rb_iseq_t;
@@ -120,8 +123,8 @@ typedef struct rb_iseq_struct rb_iseq_t;
#endif
typedef struct rb_method_iseq_struct {
- const rb_iseq_t * const iseqptr; /* should be separated from iseqval */
- rb_cref_t * const cref; /* should be marked */
+ const rb_iseq_t * const iseqptr; /*!< iseq pointer, should be separated from iseqval */
+ rb_cref_t * const cref; /*!< class reference, should be marked */
} rb_method_iseq_t; /* check rb_add_method_iseq() when modify the fields */
typedef struct rb_method_cfunc_struct {
@@ -144,10 +147,22 @@ typedef struct rb_method_refined_struct {
const VALUE owner;
} rb_method_refined_t;
-typedef struct rb_method_definition_struct {
- rb_method_type_t type : 8; /* method type */
- int alias_count : 28;
- int complemented_count: 28;
+typedef struct rb_method_bmethod_struct {
+ const VALUE proc; /* should be marked */
+ struct rb_hook_list_struct *hooks;
+} rb_method_bmethod_t;
+
+enum method_optimized_type {
+ OPTIMIZED_METHOD_TYPE_SEND,
+ OPTIMIZED_METHOD_TYPE_CALL,
+ OPTIMIZED_METHOD_TYPE_BLOCK_CALL,
+ OPTIMIZED_METHOD_TYPE__MAX
+};
+
+PACKED_STRUCT_UNALIGNED(struct rb_method_definition_struct {
+ BITFIELD(rb_method_type_t, type, VM_METHOD_TYPE_MINIMUM_BITS);
+ int alias_count : 28;
+ int complemented_count : 28;
union {
rb_method_iseq_t iseq;
@@ -155,18 +170,15 @@ typedef struct rb_method_definition_struct {
rb_method_attr_t attr;
rb_method_alias_t alias;
rb_method_refined_t refined;
+ rb_method_bmethod_t bmethod;
- const VALUE proc; /* should be marked */
- enum method_optimized_type {
- OPTIMIZED_METHOD_TYPE_SEND,
- OPTIMIZED_METHOD_TYPE_CALL,
-
- OPTIMIZED_METHOD_TYPE__MAX
- } optimize_type;
+ enum method_optimized_type optimize_type;
} body;
ID original_id;
-} rb_method_definition_t;
+});
+
+typedef struct rb_method_definition_struct rb_method_definition_t;
#define UNDEFINED_METHOD_ENTRY_P(me) (!(me) || !(me)->def || (me)->def->type == VM_METHOD_TYPE_UNDEF)
#define UNDEFINED_REFINED_METHOD_P(def) \
@@ -184,14 +196,16 @@ rb_method_entry_t *rb_method_entry_create(ID called_id, VALUE klass, rb_method_v
const rb_method_entry_t *rb_method_entry_at(VALUE obj, ID id);
const rb_method_entry_t *rb_method_entry(VALUE klass, ID id);
-const rb_method_entry_t *rb_method_entry_with_refinements(VALUE klass, ID id);
-const rb_method_entry_t *rb_method_entry_without_refinements(VALUE klass, ID id);
+const rb_method_entry_t *rb_method_entry_without_refinements(VALUE klass, ID id, VALUE *defined_class);
const rb_method_entry_t *rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
+RUBY_SYMBOL_EXPORT_BEGIN
+const rb_callable_method_entry_t *rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me);
+const rb_method_entry_t *rb_resolve_me_location(const rb_method_entry_t *, VALUE[5]);
+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_with_refinements(VALUE klass, ID id);
-const rb_callable_method_entry_t *rb_callable_method_entry_without_refinements(VALUE klass, ID id);
-const rb_callable_method_entry_t *rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me);
+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);
int rb_method_entry_arity(const rb_method_entry_t *me);
int rb_method_entry_eq(const rb_method_entry_t *m1, const rb_method_entry_t *m2);
@@ -210,4 +224,6 @@ void rb_method_entry_copy(rb_method_entry_t *dst, const rb_method_entry_t *src);
void rb_scope_visibility_set(rb_method_visibility_t);
+VALUE rb_unnamed_parameters(int arity);
+
#endif /* RUBY_METHOD_H */
diff --git a/misc/README b/misc/README
index 08a9010f58..1728b42700 100644
--- a/misc/README
+++ b/misc/README
@@ -1,12 +1,6 @@
-README this file
-inf-ruby.el program to run ruby under emacs
-rb_optparse.bash bash completion script
-rb_optparse.zsh zsh completion script
-rdoc-mode.el RDoc mode for emacs
-ruby-mode.el ruby mode for emacs
-ruby-style.el Ruby's C/C++ mode style for emacs
-rubydb2x.el ruby debugger support for emacs 19.2x or before
-rubydb3x.el ruby debugger support for emacs 19.3x or later
-ruby-electric.el emacs minor mode providing electric commands
-
-Check out https://github.com/ruby-debug/ also.
+README this file
+rb_optparse.bash bash completion script
+rb_optparse.zsh zsh completion script
+ruby-style.el Ruby's C/C++ mode style for emacs
+lldb_cruby.py LLDB port of debug utility
+test_lldb_cruby.rb test file for LLDB port
diff --git a/misc/inf-ruby.el b/misc/inf-ruby.el
deleted file mode 100644
index b3f4f10267..0000000000
--- a/misc/inf-ruby.el
+++ /dev/null
@@ -1,418 +0,0 @@
-;;; -*-Emacs-Lisp-*-
-;;;
-;;; $Id$
-;;; $Author$
-;;;
-;;; Inferior Ruby Mode - ruby process in a buffer.
-;;; adapted from cmuscheme.el
-;;;
-;;; Usage:
-;;;
-;;; (0) check ruby-program-name variable that can run your environment.
-;;;
-;;; (1) modify .emacs to use ruby-mode
-;;; for example :
-;;;
-;;; (autoload 'ruby-mode "ruby-mode"
-;;; "Mode for editing ruby source files" t)
-;;; (setq auto-mode-alist
-;;; (append '(("\\.rb$" . ruby-mode)) auto-mode-alist))
-;;; (setq interpreter-mode-alist (append '(("ruby" . ruby-mode))
-;;; interpreter-mode-alist))
-;;;
-;;; (2) set to load inf-ruby and set inf-ruby key definition in ruby-mode.
-;;;
-;;; (autoload 'run-ruby "inf-ruby"
-;;; "Run an inferior Ruby process")
-;;; (autoload 'inf-ruby-keys "inf-ruby"
-;;; "Set local key defs for inf-ruby in ruby-mode")
-;;; (add-hook 'ruby-mode-hook
-;;; '(lambda ()
-;;; (inf-ruby-keys)
-;;; ))
-;;;
-;;; HISTORY
-;;; senda - 8 Apr 1998: Created.
-;;; $Log$
-;;; Revision 1.7 2004/07/27 08:11:36 matz
-;;; * eval.c (rb_eval): copy on write for argument local variable
-;;; assignment.
-;;;
-;;; * eval.c (assign): ditto.
-;;;
-;;; * eval.c (rb_call0): update ruby_frame->argv with the default
-;;; value used for the optional arguments.
-;;;
-;;; * object.c (Init_Object): "===" calls rb_obj_equal() directly.
-;;; [ruby-list:39937]
-;;;
-;;; Revision 1.6 2002/09/07 14:35:46 nobu
-;;; * misc/inf-ruby.el (inferior-ruby-error-regexp-alist): regexp
-;;; alist for error message from ruby.
-;;;
-;;; * misc/inf-ruby.el (inferior-ruby-mode): fixed for Emacs.
-;;;
-;;; * misc/inf-ruby.el (ruby-send-region): compilation-parse-errors
-;;; doesn't parse first line, so insert separators before each
-;;; evaluations.
-;;;
-;;; Revision 1.5 2002/08/19 10:05:47 nobu
-;;; * misc/inf-ruby.el (inf-ruby-keys): ruby-send-definition
-;;; conflicted with ruby-insert-end.
-;;;
-;;; * misc/inf-ruby.el (inferior-ruby-mode): compilation-minor-mode.
-;;;
-;;; * misc/inf-ruby.el (ruby-send-region): send as here document to
-;;; adjust source file/line. [ruby-talk:47113], [ruby-dev:17965]
-;;;
-;;; * misc/inf-ruby.el (ruby-send-terminator): added to make unique
-;;; terminator.
-;;;
-;;; Revision 1.4 2002/01/29 07:16:09 matz
-;;; * file.c (rb_stat_rdev_major): added. [new]
-;;;
-;;; * file.c (rb_stat_rdev_minor): added. [new]
-;;;
-;;; * file.c (rb_stat_inspect): print mode in octal.
-;;;
-;;; Revision 1.3 1999/12/01 09:24:18 matz
-;;; 19991201
-;;;
-;;; Revision 1.2 1999/08/13 05:45:18 matz
-;;; 1.4.0
-;;;
-;;; Revision 1.1.1.1.2.1 1999/07/15 07:59:59 matz
-;;; 990715
-;;;
-;;; Revision 1.1.1.1 1999/01/20 04:59:36 matz
-;;; ruby 1.3 cycle
-;;;
-;;; Revision 1.1.2.1 1998/12/16 07:30:36 matz
-;;; first public release of 1.1d (pre1.2) series
-;;;
-;;; Revision 1.4 1998/05/20 02:45:58 senda
-;;; default program to irb
-;;;
-;;; Revision 1.3 1998/04/10 04:11:30 senda
-;;; modification by Matsumoto san (1.1b9_09)
-;;; remove-in-string defined
-;;; global variable :
-;;; inferior-ruby-first-prompt-pattern
-;;; inferior-ruby-prompt-pattern
-;;; defined
-;;;
-;;; Revision 1.2 1998/04/09 07:53:42 senda
-;;; remove M-C-x in inferior-ruby-mode
-;;;
-;;; Revision 1.1 1998/04/09 07:28:36 senda
-;;; Initial revision
-;;;
-;;;
-
-(require 'comint)
-(require 'compile)
-(require 'ruby-mode)
-
-;;
-;; you may change these variables
-;;
-;(defvar ruby-program-name "rbc --noreadline"
-; "*Program invoked by the run-ruby command")
-;
-;(defvar inferior-ruby-first-prompt-pattern "^rbc0> *"
-; "first prompt regex pattern of ruby interpreter.")
-;
-;(defvar inferior-ruby-prompt-pattern "^\\(rbc.[>*\"'] *\\)+"
-; "prompt regex pattern of ruby interpreter.")
-
-;;;; for irb
-(defvar ruby-program-name "irb --inf-ruby-mode"
- "*Program invoked by the run-ruby command")
-
-(defvar inferior-ruby-first-prompt-pattern "^irb(.*)[0-9:]+0> *"
- "first prompt regex pattern of ruby interpreter.")
-
-(defvar inferior-ruby-prompt-pattern "^\\(irb(.*)[0-9:]+[>*\"'] *\\)+"
- "prompt regex pattern of ruby interpreter.")
-
-;;
-;; mode variables
-;;
-(defvar inferior-ruby-mode-hook nil
- "*Hook for customising inferior-ruby mode.")
-(defvar inferior-ruby-mode-map nil
- "*Mode map for inferior-ruby-mode")
-
-(defconst inferior-ruby-error-regexp-alist
- '(("SyntaxError: compile error\n^\\([^\(].*\\):\\([1-9][0-9]*\\):" 1 2)
- ("^\tfrom \\([^\(].*\\):\\([1-9][0-9]*\\)\\(:in `.*'\\)?$" 1 2)))
-
-(cond ((not inferior-ruby-mode-map)
- (setq inferior-ruby-mode-map
- (copy-keymap comint-mode-map))
-; (define-key inferior-ruby-mode-map "\M-\C-x" ;gnu convention
-; 'ruby-send-definition)
-; (define-key inferior-ruby-mode-map "\C-x\C-e" 'ruby-send-last-sexp)
- (define-key inferior-ruby-mode-map "\C-c\C-l" 'ruby-load-file)
-))
-
-;;;###autoload
-(defun inf-ruby-keys ()
- "Set local key defs for inf-ruby in ruby-mode"
- (define-key ruby-mode-map "\M-\C-x" 'ruby-send-definition)
-; (define-key ruby-mode-map "\C-x\C-e" 'ruby-send-last-sexp)
- (define-key ruby-mode-map "\C-c\C-b" 'ruby-send-block)
- (define-key ruby-mode-map "\C-c\M-b" 'ruby-send-block-and-go)
- (define-key ruby-mode-map "\C-c\C-x" 'ruby-send-definition)
- (define-key ruby-mode-map "\C-c\M-x" 'ruby-send-definition-and-go)
- (define-key ruby-mode-map "\C-c\C-r" 'ruby-send-region)
- (define-key ruby-mode-map "\C-c\M-r" 'ruby-send-region-and-go)
- (define-key ruby-mode-map "\C-c\C-z" 'switch-to-ruby)
- (define-key ruby-mode-map "\C-c\C-l" 'ruby-load-file)
- (define-key ruby-mode-map "\C-c\C-s" 'run-ruby)
-)
-
-(defvar ruby-buffer nil "current ruby (actually irb) process buffer.")
-
-(defun inferior-ruby-mode ()
- "Major mode for interacting with an inferior ruby (irb) process.
-
-The following commands are available:
-\\{inferior-ruby-mode-map}
-
-A ruby process can be fired up with M-x run-ruby.
-
-Customisation: Entry to this mode runs the hooks on comint-mode-hook and
-inferior-ruby-mode-hook (in that order).
-
-You can send text to the inferior ruby process from other buffers containing
-Ruby source.
- switch-to-ruby switches the current buffer to the ruby process buffer.
- ruby-send-definition sends the current definition to the ruby process.
- ruby-send-region sends the current region to the ruby process.
-
- ruby-send-definition-and-go, ruby-send-region-and-go,
- switch to the ruby process buffer after sending their text.
-For information on running multiple processes in multiple buffers, see
-documentation for variable ruby-buffer.
-
-Commands:
-Return after the end of the process' output sends the text from the
- end of process to point.
-Return before the end of the process' output copies the sexp ending at point
- to the end of the process' output, and sends it.
-Delete converts tabs to spaces as it moves back.
-Tab indents for ruby; with argument, shifts rest
- of expression rigidly with the current line.
-C-M-q does Tab on each line starting within following expression.
-Paragraphs are separated only by blank lines. # start comments.
-If you accidentally suspend your process, use \\[comint-continue-subjob]
-to continue it."
- (interactive)
- (comint-mode)
- ;; Customise in inferior-ruby-mode-hook
- ;(setq comint-prompt-regexp "^[^>\n]*>+ *")
- (setq comint-prompt-regexp inferior-ruby-prompt-pattern)
- ;;(scheme-mode-variables)
- (ruby-mode-variables)
- (setq major-mode 'inferior-ruby-mode)
- (setq mode-name "Inferior Ruby")
- (setq mode-line-process '(":%s"))
- (use-local-map inferior-ruby-mode-map)
- (setq comint-input-filter (function ruby-input-filter))
- (setq comint-get-old-input (function ruby-get-old-input))
- (make-local-variable 'compilation-error-regexp-alist)
- (setq compilation-error-regexp-alist inferior-ruby-error-regexp-alist)
- (compilation-shell-minor-mode t)
- (run-hooks 'inferior-ruby-mode-hook))
-
-(defvar inferior-ruby-filter-regexp "\\`\\s *\\S ?\\S ?\\s *\\'"
- "*Input matching this regexp are not saved on the history list.
-Defaults to a regexp ignoring all inputs of 0, 1, or 2 letters.")
-
-(defun ruby-input-filter (str)
- "Don't save anything matching inferior-ruby-filter-regexp"
- (not (string-match inferior-ruby-filter-regexp str)))
-
-;; adapted from replace-in-string in XEmacs (subr.el)
-(defun remove-in-string (str regexp)
- "Remove all matches in STR for REGEXP and returns the new string."
- (let ((rtn-str "") (start 0) match prev-start)
- (while (setq match (string-match regexp str start))
- (setq prev-start start
- start (match-end 0)
- rtn-str (concat rtn-str (substring str prev-start match))))
- (concat rtn-str (substring str start))))
-
-(defun ruby-get-old-input ()
- "Snarf the sexp ending at point"
- (save-excursion
- (let ((end (point)))
- (re-search-backward inferior-ruby-first-prompt-pattern)
- (remove-in-string (buffer-substring (point) end)
- inferior-ruby-prompt-pattern)
- )))
-
-(defun ruby-args-to-list (string)
- (let ((where (string-match "[ \t]" string)))
- (cond ((null where) (list string))
- ((not (= where 0))
- (cons (substring string 0 where)
- (ruby-args-to-list (substring string (+ 1 where)
- (length string)))))
- (t (let ((pos (string-match "[^ \t]" string)))
- (if (null pos)
- nil
- (ruby-args-to-list (substring string pos
- (length string)))))))))
-
-;;;###autoload
-(defun run-ruby (cmd)
- "Run an inferior Ruby process, input and output via buffer *ruby*.
-If there is a process already running in `*ruby*', switch to that buffer.
-With argument, allows you to edit the command line (default is value
-of `ruby-program-name'). Runs the hooks `inferior-ruby-mode-hook'
-\(after the `comint-mode-hook' is run).
-\(Type \\[describe-mode] in the process buffer for a list of commands.)"
-
- (interactive (list (if current-prefix-arg
- (read-string "Run Ruby: " ruby-program-name)
- ruby-program-name)))
- (if (not (comint-check-proc "*ruby*"))
- (let ((cmdlist (ruby-args-to-list cmd)))
- (set-buffer (apply 'make-comint "ruby" (car cmdlist)
- nil (cdr cmdlist)))
- (inferior-ruby-mode)))
- (setq ruby-program-name cmd)
- (setq ruby-buffer "*ruby*")
- (pop-to-buffer "*ruby*"))
-
-(defconst ruby-send-terminator "--inf-ruby-%x-%d-%d-%d--"
- "Template for irb here document terminator.
-Must not contain ruby meta characters.")
-
-(defconst ruby-eval-separator "")
-
-(defun ruby-send-region (start end)
- "Send the current region to the inferior Ruby process."
- (interactive "r")
- (let (term (file (buffer-file-name)) line)
- (save-excursion
- (save-restriction
- (widen)
- (goto-char start)
- (setq line (+ start (forward-line (- start)) 1))
- (goto-char start)
- (while (progn
- (setq term (apply 'format ruby-send-terminator (random) (current-time)))
- (re-search-forward (concat "^" (regexp-quote term) "$") end t)))))
- ;; compilation-parse-errors parses from second line.
- (save-excursion
- (let ((m (process-mark (ruby-proc))))
- (set-buffer (marker-buffer m))
- (goto-char m)
- (insert ruby-eval-separator "\n")
- (set-marker m (point))))
- (comint-send-string (ruby-proc) (format "eval <<'%s', nil, %S, %d\n" term file line))
- (comint-send-region (ruby-proc) start end)
- (comint-send-string (ruby-proc) (concat "\n" term "\n"))))
-
-(defun ruby-send-definition ()
- "Send the current definition to the inferior Ruby process."
- (interactive)
- (save-excursion
- (ruby-end-of-defun)
- (let ((end (point)))
- (ruby-beginning-of-defun)
- (ruby-send-region (point) end))))
-
-;(defun ruby-send-last-sexp ()
-; "Send the previous sexp to the inferior Ruby process."
-; (interactive)
-; (ruby-send-region (save-excursion (backward-sexp) (point)) (point)))
-
-(defun ruby-send-block ()
- "Send the current block to the inferior Ruby process."
- (interactive)
- (save-excursion
- (ruby-end-of-block)
- (end-of-line)
- (let ((end (point)))
- (ruby-beginning-of-block)
- (ruby-send-region (point) end))))
-
-(defun switch-to-ruby (eob-p)
- "Switch to the ruby process buffer.
-With argument, positions cursor at end of buffer."
- (interactive "P")
- (if (get-buffer ruby-buffer)
- (pop-to-buffer ruby-buffer)
- (error "No current process buffer. See variable ruby-buffer."))
- (cond (eob-p
- (push-mark)
- (goto-char (point-max)))))
-
-(defun ruby-send-region-and-go (start end)
- "Send the current region to the inferior Ruby process.
-Then switch to the process buffer."
- (interactive "r")
- (ruby-send-region start end)
- (switch-to-ruby t))
-
-(defun ruby-send-definition-and-go ()
- "Send the current definition to the inferior Ruby.
-Then switch to the process buffer."
- (interactive)
- (ruby-send-definition)
- (switch-to-ruby t))
-
-(defun ruby-send-block-and-go ()
- "Send the current block to the inferior Ruby.
-Then switch to the process buffer."
- (interactive)
- (ruby-send-block)
- (switch-to-ruby t))
-
-(defvar ruby-source-modes '(ruby-mode)
- "*Used to determine if a buffer contains Ruby source code.
-If it's loaded into a buffer that is in one of these major modes, it's
-considered a ruby source file by ruby-load-file.
-Used by these commands to determine defaults.")
-
-(defvar ruby-prev-l/c-dir/file nil
- "Caches the last (directory . file) pair.
-Caches the last pair used in the last ruby-load-file command.
-Used for determining the default in the
-next one.")
-
-(defun ruby-load-file (file-name)
- "Load a Ruby file into the inferior Ruby process."
- (interactive (comint-get-source "Load Ruby file: " ruby-prev-l/c-dir/file
- ruby-source-modes t)) ; T because LOAD
- ; needs an exact name
- (comint-check-source file-name) ; Check to see if buffer needs saved.
- (setq ruby-prev-l/c-dir/file (cons (file-name-directory file-name)
- (file-name-nondirectory file-name)))
- (comint-send-string (ruby-proc) (concat "(load \""
- file-name
- "\"\)\n")))
-
-(defun ruby-proc ()
- "Returns the current ruby process. See variable ruby-buffer."
- (let ((proc (get-buffer-process (if (eq major-mode 'inferior-ruby-mode)
- (current-buffer)
- ruby-buffer))))
- (or proc
- (error "No current process. See variable ruby-buffer"))))
-
-;;; Do the user's customisation...
-
-(defvar inf-ruby-load-hook nil
- "This hook is run when inf-ruby is loaded in.
-This is a good place to put keybindings.")
-
-(run-hooks 'inf-ruby-load-hook)
-
-(provide 'inf-ruby)
-
-;;; inf-ruby.el ends here
diff --git a/misc/lldb_cruby.py b/misc/lldb_cruby.py
new file mode 100755
index 0000000000..0c4b9fc0d1
--- /dev/null
+++ b/misc/lldb_cruby.py
@@ -0,0 +1,257 @@
+#!/usr/bin/env python
+#coding: utf-8
+#
+# Usage: run `command script import -r misc/lldb_cruby.py` on LLDB
+#
+# Test: misc/test_lldb_cruby.rb
+#
+
+import lldb
+import commands
+import os
+import shlex
+
+def lldb_init(debugger):
+ target = debugger.GetSelectedTarget()
+ global SIZEOF_VALUE
+ SIZEOF_VALUE = target.FindFirstType("VALUE").GetByteSize()
+
+ value_types = []
+ g = globals()
+ for enum in target.FindFirstGlobalVariable('ruby_dummy_gdb_enums'):
+ enum = enum.GetType()
+ members = enum.GetEnumMembers()
+ for i in xrange(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 string2cstr(rstring):
+ """Returns the pointer to the C-string in the given String object"""
+ flags = rstring.GetValueForExpressionPath(".basic->flags").unsigned
+ if flags & RUBY_T_MASK != RUBY_T_STRING:
+ raise TypeError("not a string")
+ if flags & RUBY_FL_USER1:
+ cptr = int(rstring.GetValueForExpressionPath(".as.heap.ptr").value, 0)
+ clen = int(rstring.GetValueForExpressionPath(".as.heap.len").value, 0)
+ else:
+ cptr = int(rstring.GetValueForExpressionPath(".as.ary").value, 0)
+ clen = (flags & RSTRING_EMBED_LEN_MASK) >> RSTRING_EMBED_LEN_SHIFT
+ return cptr, clen
+
+def output_string(ctx, rstring):
+ cptr, clen = string2cstr(rstring)
+ expr = 'printf("%%.*s", (size_t)%d, (const char*)%d)' % (clen, cptr)
+ ctx.frame.EvaluateExpression(expr)
+
+def fixnum_p(x):
+ return x & RUBY_FIXNUM_FLAG != 0
+
+def flonum_p(x):
+ return (x&RUBY_FLONUM_MASK) == RUBY_FLONUM_FLAG
+
+def static_sym_p(x):
+ return (x&~(~0<<RUBY_SPECIAL_SHIFT)) == RUBY_SYMBOL_FLAG
+
+def append_command_output(debugger, command, result):
+ output1 = result.GetOutput()
+ debugger.GetCommandInterpreter().HandleCommand(command, result)
+ output2 = result.GetOutput()
+ result.Clear()
+ result.write(output1)
+ result.write(output2)
+
+def lldb_rp(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()
+ if frame.IsValid():
+ val = frame.EvaluateExpression(command)
+ else:
+ val = target.EvaluateExpression(command)
+ error = val.GetError()
+ if error.Fail():
+ print >> result, error
+ return
+ lldb_inspect(debugger, target, result, val)
+
+def lldb_inspect(debugger, target, result, val):
+ num = val.GetValueAsSigned()
+ if num == RUBY_Qfalse:
+ print >> result, 'false'
+ elif num == RUBY_Qtrue:
+ print >> result, 'true'
+ elif num == RUBY_Qnil:
+ print >> result, 'nil'
+ elif num == RUBY_Qundef:
+ print >> result, 'undef'
+ elif fixnum_p(num):
+ print >> result, num >> 1
+ elif flonum_p(num):
+ append_command_output(debugger, "print rb_float_value(%0#x)" % val.GetValueAsUnsigned(), result)
+ elif static_sym_p(num):
+ if num < 128:
+ print >> result, "T_SYMBOL: %c" % num
+ else:
+ print >> result, "T_SYMBOL: (%x)" % num
+ elif num & RUBY_IMMEDIATE_MASK:
+ print >> result, 'immediate(%x)' % num
+ else:
+ tRBasic = target.FindFirstType("struct RBasic").GetPointerType()
+ val = val.Cast(tRBasic)
+ flags = val.GetValueForExpressionPath("->flags").GetValueAsUnsigned()
+ if (flags & RUBY_FL_PROMOTED) == RUBY_FL_PROMOTED:
+ print >> result, "[PROMOTED] "
+ flType = flags & RUBY_T_MASK
+ if flType == RUBY_T_NONE:
+ print >> result, 'T_NONE: %s' % val.Dereference()
+ elif flType == RUBY_T_NIL:
+ print >> result, 'T_NIL: %s' % val.Dereference()
+ elif flType == RUBY_T_OBJECT:
+ tRObject = target.FindFirstType("struct RObject").GetPointerType()
+ val = val.Cast(tRObject)
+ print >> result, 'T_OBJECT: %s' % val.Dereference()
+ elif flType == RUBY_T_CLASS or flType == RUBY_T_MODULE or flType == RUBY_T_ICLASS:
+ tRClass = target.FindFirstType("struct RClass").GetPointerType()
+ val = val.Cast(tRClass)
+ print >> result, 'T_%s: %s' % ('CLASS' if flType == RUBY_T_CLASS else 'MODULE' if flType == RUBY_T_MODULE else 'ICLASS', val.Dereference())
+ elif flType == RUBY_T_STRING:
+ tRString = target.FindFirstType("struct RString").GetPointerType()
+ val = val.Cast(tRString)
+ if flags & RSTRING_NOEMBED:
+ print >> result, val.GetValueForExpressionPath("->as.heap")
+ else:
+ print >> result, val.GetValueForExpressionPath("->as.ary")
+ elif flType == RUBY_T_SYMBOL:
+ tRSymbol = target.FindFirstType("struct RSymbol").GetPointerType()
+ print >> result, val.Cast(tRSymbol).Dereference()
+ elif flType == RUBY_T_ARRAY:
+ tRArray = target.FindFirstType("struct RArray").GetPointerType()
+ val = val.Cast(tRArray)
+ if flags & RUBY_FL_USER1:
+ len = ((flags & (RUBY_FL_USER3|RUBY_FL_USER4)) >> (RUBY_FL_USHIFT+3))
+ ptr = val.GetValueForExpressionPath("->as.ary")
+ else:
+ len = val.GetValueForExpressionPath("->as.heap.len").GetValueAsSigned()
+ ptr = val.GetValueForExpressionPath("->as.heap.ptr")
+ #print >> result, val.GetValueForExpressionPath("->as.heap")
+ result.write("T_ARRAY: len=%d" % len)
+ if flags & RUBY_FL_USER1:
+ result.write(" (embed)")
+ elif flags & RUBY_FL_USER2:
+ shared = val.GetValueForExpressionPath("->as.heap.aux.shared").GetValueAsUnsigned()
+ result.write(" (shared) shared=%016x")
+ else:
+ capa = val.GetValueForExpressionPath("->as.heap.aux.capa").GetValueAsSigned()
+ result.write(" (ownership) capa=%d" % capa)
+ if len == 0:
+ result.write(" {(empty)}")
+ else:
+ result.write("\n")
+ append_command_output(debugger, "expression -Z %d -fx -- (const VALUE*)%0#x" % (len, ptr.GetValueAsUnsigned()), result)
+ elif flType == RUBY_T_HASH:
+ append_command_output(debugger, "p *(struct RHash *) %0#x" % val.GetValueAsUnsigned(), result)
+ elif flType == RUBY_T_BIGNUM:
+ tRBignum = target.FindFirstType("struct RBignum").GetPointerType()
+ val = val.Cast(tRBignum)
+ if flags & RUBY_FL_USER2:
+ len = ((flags & (RUBY_FL_USER3|RUBY_FL_USER4|RUBY_FL_USER5)) >> (RUBY_FL_USHIFT+3))
+ print >> result, "T_BIGNUM: len=%d (embed)" % len
+ append_command_output(debugger, "print ((struct RBignum *) %0#x)->as.ary" % val.GetValueAsUnsigned(), result)
+ else:
+ len = val.GetValueForExpressionPath("->as.heap.len").GetValueAsSigned()
+ print >> result, "T_BIGNUM: len=%d" % len
+ print >> result, val.Dereference()
+ append_command_output(debugger, "expression -Z %x -fx -- (const BDIGIT*)((struct RBignum*)%d)->as.heap.digits" % (len, val.GetValueAsUnsigned()), result)
+ # append_command_output(debugger, "x ((struct RBignum *) %0#x)->as.heap.digits / %d" % (val.GetValueAsUnsigned(), len), result)
+ elif flType == RUBY_T_FLOAT:
+ tRFloat = target.FindFirstType("struct RFloat").GetPointerType()
+ val = val.Cast(tRFloat)
+ append_command_output(debugger, "p *(double *)%0#x" % val.GetValueForExpressionPath("->float_value").GetAddress(), result)
+ elif flType == RUBY_T_RATIONAL:
+ tRRational = target.FindFirstType("struct RRational").GetPointerType()
+ val = val.Cast(tRRational)
+ lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->num"))
+ output = result.GetOutput()
+ result.Clear()
+ result.write("(Rational) " + output.rstrip() + " / ")
+ lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->den"))
+ elif flType == RUBY_T_COMPLEX:
+ tRComplex = target.FindFirstType("struct RComplex").GetPointerType()
+ val = val.Cast(tRComplex)
+ lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->real"))
+ real = result.GetOutput().rstrip()
+ result.Clear()
+ lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->imag"))
+ imag = result.GetOutput().rstrip()
+ result.Clear()
+ if not imag.startswith("-"):
+ imag = "+" + imag
+ print >> result, "(Complex) " + real + imag + "i"
+ elif flType == RUBY_T_DATA:
+ tRTypedData = target.FindFirstType("struct RTypedData").GetPointerType()
+ val = val.Cast(tRTypedData)
+ flag = val.GetValueForExpressionPath("->typed_flag")
+ if flag.GetValueAsUnsigned() == 1:
+ print >> result, "T_DATA: %s" % val.GetValueForExpressionPath("->type->wrap_struct_name")
+ append_command_output(debugger, "p *(struct RTypedData *) %0#x" % val.GetValueAsUnsigned(), result)
+ else:
+ print >> result, "T_DATA:"
+ append_command_output(debugger, "p *(struct RData *) %0#x" % val.GetValueAsUnsigned(), result)
+ else:
+ print >> result, "Not-handled type %0#x" % flType
+ print >> result, val
+
+def count_objects(debugger, command, ctx, result, internal_dict):
+ objspace = ctx.frame.EvaluateExpression("ruby_current_vm->objspace")
+ num_pages = objspace.GetValueForExpressionPath(".heap_pages.allocated_pages").unsigned
+
+ counts = {}
+ total = 0
+ for t in range(0x00, RUBY_T_MASK+1):
+ counts[t] = 0
+
+ for i in range(0, num_pages):
+ print "\rcounting... %d/%d" % (i, num_pages),
+ page = objspace.GetValueForExpressionPath('.heap_pages.sorted[%d]' % i)
+ p = page.GetChildMemberWithName('start')
+ num_slots = page.GetChildMemberWithName('total_slots').unsigned
+ for j in range(0, num_slots):
+ obj = p.GetValueForExpressionPath('[%d]' % j)
+ flags = obj.GetValueForExpressionPath('.as.basic.flags').unsigned
+ obj_type = flags & RUBY_T_MASK
+ counts[obj_type] += 1
+ total += num_slots
+
+ print "\rTOTAL: %d, FREE: %d" % (total, counts[0x00])
+ for sym in value_types:
+ print "%s: %d" % (sym, counts[globals()[sym]])
+
+def stack_dump_raw(debugger, command, ctx, result, internal_dict):
+ ctx.frame.EvaluateExpression("rb_vmdebug_stack_dump_raw_current()")
+
+def dump_node(debugger, command, ctx, result, internal_dict):
+ args = shlex.split(command)
+ if not args:
+ return
+ node = args[0]
+
+ dump = ctx.frame.EvaluateExpression("(struct RString*)rb_parser_dump_tree((NODE*)(%s), 0)" % node)
+ output_string(ctx, dump)
+
+def __lldb_init_module(debugger, internal_dict):
+ 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")
+ lldb_init(debugger)
+ print "lldb scripts for ruby has been installed."
diff --git a/misc/rdoc-mode.el b/misc/rdoc-mode.el
deleted file mode 100644
index c26c2ee564..0000000000
--- a/misc/rdoc-mode.el
+++ /dev/null
@@ -1,166 +0,0 @@
-;;
-;; rdoc-mode.el
-;; Major mode for RDoc editing
-;;
-
-;; Created: Fri Sep 18 09:04:49 JST 2009
-
-;; License: Ruby's
-
-(require 'derived)
-
-;;;###autoload
-(define-derived-mode rdoc-mode text-mode "RDoc"
- "Major mode for RD editing.
-\\{rdoc-mode-map}"
- (make-local-variable 'paragraph-separate)
- (setq paragraph-separate "^\\(=+\\|\\*+\\)[ \t\v\f]*\\|^\\s *$")
- (make-local-variable 'paragraph-start)
- (setq paragraph-start paragraph-separate)
- (make-local-variable 'require-final-newline)
- (setq require-final-newline t)
- (make-local-variable 'font-lock-defaults)
- (setq font-lock-defaults '((rdoc-font-lock-keywords) t nil))
- (make-local-variable 'font-lock-keywords)
- (setq font-lock-keywords rdoc-font-lock-keywords)
- (make-local-variable 'outline-regexp)
- (setq outline-regexp "^\\(=+\\)[ \t\v\f]*")
- (outline-minor-mode t)
- (setq show-trailing-whitespace t)
- (rdoc-setup-keys)
- (setq indent-tabs-mode nil)
- (run-hooks 'rdoc-mode-hook)
- )
-
-(defun rdoc-fill-paragraph (&optional justify region)
- "Fills paragraph, except for cited region"
- (interactive (progn
- (barf-if-buffer-read-only)
- (list (if current-prefix-arg 'full))))
- (save-excursion
- (beginning-of-line)
- (save-restriction
- (let ((pos (point)) beg end indent hanging)
- (cond
- ((looking-at "^ +\\(\\*\\s *\\)")
- (setq indent (- (match-end 0) (match-beginning 0))
- hanging (- (match-end 1) (match-beginning 1))))
- ((looking-at "^ +")
- (setq indent (- (match-end 0) (match-beginning 0)))
- (when (and (re-search-backward "^[^ ]\\|^\\( *\\(\\* *\\)\\)" nil t)
- (match-beginning 1)
- (= indent (- (match-end 1) (match-beginning 1))))
- (setq hanging (- (match-end 2) (match-beginning 2)))
- (setq beg (match-beginning 1))))
- ((setq beg t)))
- (when beg
- (when indent
- (goto-char pos)
- (while (progn (beginning-of-line 2)
- (and (looking-at "^\\( +\\)\\S ")
- (= indent (- (match-end 1) (match-beginning 1))))))
- (setq end (point))
- (when (and beg (not region))
- (setq region (list beg end))
- (narrow-to-region beg end)
- ))
- (goto-char pos)
- (fill-paragraph justify region)
- (when (and indent
- (or (goto-char beg) t)
- (or (beginning-of-line 2) t)
- (looking-at "^\\( +\\)")
- (= (- indent hanging) (- (match-end 0) (match-beginning 0))))
- (insert-char ?\s hanging)
- (beginning-of-line)
- (narrow-to-region (point) end)
- (fill-paragraph justify (list (point) end))))))))
-
-(defun rdoc-setup-keys ()
- (interactive)
- (define-key rdoc-mode-map "\M-q" 'rdoc-fill-paragraph)
- )
-
-(defvar rdoc-heading1-face 'font-lock-keywordoc-face)
-(defvar rdoc-heading2-face 'font-lock-type-face)
-(defvar rdoc-heading3-face 'font-lock-variable-name-face)
-(defvar rdoc-heading4-face 'font-lock-comment-face)
-(defvar rdoc-bold-face 'font-lock-function-name-face)
-(defvar rdoc-emphasis-face 'font-lock-function-name-face)
-(defvar rdoc-code-face 'font-lock-keyword-face)
-(defvar rdoc-description-face 'font-lock-constant-face)
-
-(defvar rdoc-font-lock-keywords
- (list
- (list "^=([^=\r\n].*)?$"
- 0 rdoc-heading1-face)
- (list "^==([^=\r\n].*)?$"
- 0 rdoc-heading2-face)
- (list "^===([^=\r\n].*)?$"
- 0 rdoc-heading3-face)
- (list "^====+.*$"
- 0 rdoc-heading4-face)
- (list "\\(^\\|[ \t\v\f]\\)\\(\\*\\(\\sw\\|[-_:]\\)+\\*\\)\\($\\|[ \t\v\f]\\)"
- 2 rdoc-bold-face) ; *bold*
- (list "\\(^\\|[ \t\v\f]\\)\\(_\\(\\sw\\|[-_:]\\)+_\\)\\($\\|[ \t\v\f]\\)"
- 2 rdoc-emphasis-face) ; _emphasis_
- (list "\\(^\\|[ \t\v\f]\\)\\(\\+\\(\\sw\\|[-_:]\\)+\\+\\)\\($\\|[ \t\v\f]\\)"
- 2 rdoc-code-face) ; +code+
- (list "<em>[^<>]*</em>" 0 rdoc-emphasis-face)
- (list "<i>[^<>]*</i>" 0 rdoc-emphasis-face)
- (list "<b>[^<>]*</b>" 0 rdoc-bold-face)
- (list "<tt>[^<>]*</tt>" 0 rdoc-code-face)
- (list "<code>[^<>]*</code>" 0 rdoc-code-face)
- (list "^\\([-*]\\|[0-9]+\\.\\|[A-Za-z]\\.\\)\\s "
- 1 rdoc-description-face) ; bullet | numbered | alphabetically numbered
- (list "^\\[[^\]]*\\]\\|\\S .*::\\)\\([ \t\v\f]\\|$\\)"
- 1 rdoc-description-face) ; labeled | node
- ;(list "^[ \t\v\f]+\\(.*\\)" 1 rdoc-verbatim-face)
- ))
-
-(defun rdoc-imenu-create-index ()
- (let ((root '(nil . nil))
- cur-alist
- (cur-level 0)
- (pattern (concat outline-regexp "\\(.*?\\)[ \t\v\f]*$"))
- (empty-heading "-")
- (self-heading ".")
- pos level heading alist)
- (save-excursion
- (goto-char (point-min))
- (while (re-search-forward pattern (point-max) t)
- (setq heading (match-string-no-properties 2)
- level (min 6 (length (match-string-no-properties 1)))
- pos (match-beginning 1))
- (if (= (length heading) 0)
- (setq heading empty-heading))
- (setq alist (list (cons heading pos)))
- (cond
- ((= cur-level level) ; new sibling
- (setcdr cur-alist alist)
- (setq cur-alist alist))
- ((< cur-level level) ; first child
- (dotimes (i (- level cur-level 1))
- (setq alist (list (cons empty-heading alist))))
- (if cur-alist
- (let* ((parent (car cur-alist))
- (self-pos (cdr parent)))
- (setcdr parent (cons (cons self-heading self-pos) alist)))
- (setcdr root alist)) ; primogenitor
- (setq cur-alist alist
- cur-level level))
- (t ; new sibling of an ancestor
- (let ((sibling-alist (last (cdr root))))
- (dotimes (i (1- level))
- (setq sibling-alist (last (cdar sibling-alist))))
- (setcdr sibling-alist alist)
- (setq cur-alist alist
- cur-level level))))))
- (cdr root)))
-
-(defun rdoc-set-imenu-create-index-function ()
- (setq imenu-create-index-function 'rdoc-imenu-create-index))
-
-(add-hook 'rdoc-mode-hook 'rdoc-set-imenu-create-index-function)
-
-(provide 'rdoc-mode)
diff --git a/misc/ruby-additional.el b/misc/ruby-additional.el
deleted file mode 100644
index a70aa7d54f..0000000000
--- a/misc/ruby-additional.el
+++ /dev/null
@@ -1,181 +0,0 @@
-;;; ruby-additional.el --- ruby-mode extensions yet to be merged into Emacs
-
-;; Authors: Yukihiro Matsumoto, Nobuyoshi Nakada, Akinori MUSHA
-;; URL: https://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/misc/
-;; Created: 3 Sep 2012
-;; Package-Requires: ((emacs "24.3") (ruby-mode "1.2"))
-;; Keywords: ruby, languages
-
-;;; Commentary:
-;;
-;; This package contains ruby-mode extensions yet to be merged into
-;; the latest released version of Emacs distribution. For older
-;; versions of Emacs, use ruby-mode.el bundled with CRuby.
-
-;;; Code:
-
-(eval-when-compile
- (require 'ruby-mode))
-
-(eval-after-load 'ruby-mode
- '(progn
- (define-key ruby-mode-map "\C-c\C-e" 'ruby-insert-end)
-
- (defun ruby-insert-end ()
- (interactive)
- (if (eq (char-syntax (preceding-char)) ?w)
- (insert " "))
- (insert "end")
- (save-excursion
- (if (eq (char-syntax (following-char)) ?w)
- (insert " "))
- (ruby-indent-line t)
- (end-of-line)))
-
- (defconst ruby-default-encoding-map
- '((us-ascii . nil) ;; Do not put coding: us-ascii
- (utf-8 . nil) ;; Do not put coding: utf-8
- (shift-jis . cp932) ;; Emacs charset name of Shift_JIS
- (shift_jis . cp932) ;; MIME charset name of Shift_JIS
- (japanese-cp932 . cp932)) ;; Emacs charset name of CP932
- )
-
- (custom-set-default 'ruby-encoding-map ruby-default-encoding-map)
-
- (defcustom ruby-encoding-map ruby-default-encoding-map
- "Alist to map encoding name from Emacs to Ruby.
-Associating an encoding name with nil means it needs not be
-explicitly declared in magic comment."
- :type '(repeat (cons (symbol :tag "From") (symbol :tag "To")))
- :group 'ruby)
-
- (defun ruby-mode-set-encoding ()
- "Insert or update a magic comment header with the proper encoding.
-`ruby-encoding-map' is looked up to convert an encoding name from
-Emacs to Ruby."
- (let* ((nonascii
- (save-excursion
- (widen)
- (goto-char (point-min))
- (re-search-forward "[^\0-\177]" nil t)))
- (coding-system
- (or coding-system-for-write
- buffer-file-coding-system))
- (coding-system
- (and coding-system
- (coding-system-change-eol-conversion coding-system nil)))
- (coding-system
- (and coding-system
- (or
- (coding-system-get coding-system :mime-charset)
- (let ((coding-type (coding-system-get coding-system :coding-type)))
- (cond ((eq coding-type 'undecided)
- (if nonascii
- (or (and (coding-system-get coding-system :prefer-utf-8)
- 'utf-8)
- (coding-system-get default-buffer-file-coding-system :coding-type)
- 'ascii-8bit)))
- ((memq coding-type '(utf-8 shift-jis))
- coding-type)
- (t coding-system))))))
- (coding-system
- (or coding-system
- 'us-ascii))
- (coding-system
- (let ((cons (assq coding-system ruby-encoding-map)))
- (if cons (cdr cons) coding-system)))
- (coding-system
- (and coding-system
- (symbol-name coding-system))))
- (if coding-system
- (save-excursion
- (widen)
- (goto-char (point-min))
- (if (looking-at "^#!") (beginning-of-line 2))
- (cond ((looking-at "\\s *#.*-\*-\\s *\\(en\\)?coding\\s *:\\s *\\([-a-z0-9_]*\\)\\s *\\(;\\|-\*-\\)")
- (unless (string= (match-string 2) coding-system)
- (goto-char (match-beginning 2))
- (delete-region (point) (match-end 2))
- (and (looking-at "-\*-")
- (let ((n (skip-chars-backward " ")))
- (cond ((= n 0) (insert " ") (backward-char))
- ((= n -1) (insert " "))
- ((forward-char)))))
- (insert coding-system)))
- ((looking-at "\\s *#.*coding\\s *[:=]"))
- (t (when ruby-insert-encoding-magic-comment
- (insert "# -*- coding: " coding-system " -*-\n"))))))))
-
- (define-key ruby-mode-map "\C-cU" 'ruby-encode-decode-unicode)
-
- (defun ruby-encode-unicode (beg end)
- "Convert non-ascii string in the given region to \\u{} form."
- (interactive "r")
- (setq end (set-marker (make-marker) end))
- (goto-char beg)
- (while (and (< (point) end)
- (re-search-forward "\\([\C-@-\C-I\C-K\C-_\C-?]+\\)\\|[^\C-@-\C-?]+" end t))
- (let ((str (match-string-no-properties 0)) sep b e f)
- (if (match-beginning 1)
- (setq b "" e "" sep ""
- f (lambda (c)
- (cond ((= c ?\t) "\\t")
- ((= c ?\r) "\\r")
- ((= c ?\e) "\\e")
- ((= c ?\f) "\\f")
- ((= c ?\b) "\\b")
- ((= c ?\v) "\\v")
- ((= c ?\C-?) "\\c?")
- ((concat "\\c" (char-to-string (logior c #x40)))))))
- (setq b "\\u{" e "}" sep " " f (lambda (c) (format "%x" c))))
- (setq str (mapconcat f str sep))
- (delete-region (match-beginning 0) (match-end 0))
- (insert b str e))))
-
- (defun ruby-decode-unicode (beg end)
- "Convert escaped Unicode in the given region to raw string."
- (interactive "r")
- (setq end (set-marker (make-marker) end))
- (goto-char beg)
- (while (and (< (point) end)
- (re-search-forward "\\\\u\\([0-9a-fA-F]\\{4\\}\\)\\|\\\\u{\\([0-9a-fA-F \t]+\\)}" end t))
- (let ((b (match-beginning 0)) (e (match-end 0))
- (s (match-string-no-properties 1)))
- (if s
- (setq s (cons s nil))
- (goto-char (match-beginning 2))
- (while (looking-at "[ \t]*\\([0-9a-fA-F]+\\)")
- (setq s (cons (match-string-no-properties 1) s))
- (goto-char (match-end 0))))
- (setq s (mapconcat (lambda (c) (format "%c" (string-to-int c 16)))
- (nreverse s) ""))
- (delete-region b e)
- (insert s))
- ))
-
- (defun ruby-encode-decode-unicode (dec beg end)
- "Convert Unicode <-> \\u{} in the given region."
- (interactive "P\nr")
- (if dec (ruby-decode-unicode beg end) (ruby-encode-unicode beg end)))
-
- (defun ruby-insert-heredoc-code-block (arg)
- "Insert indented here document code block"
- (interactive "P")
- (let ((c (if arg "~" "-")))
- (insert "\"#{<<" c "\"begin\;\"}\\n#{<<" c "\"end;\"}\""))
- (end-of-line)
- (if (eobp) (insert "\n") (forward-char))
- (indent-region (point)
- (progn (insert "begin;\n" "end;\n") (point)))
- (beginning-of-line 0))
- (define-key ruby-mode-map "\C-cH" 'ruby-insert-heredoc-code-block)
- ))
-
-;; monkey-patching ruby-mode.el in Emacs 24, as r49872.
-(when (and (boundp 'ruby-syntax-before-regexp-re)
- (not (string-match ruby-syntax-before-regexp-re "foo {|" 1)))
- (replace-regexp-in-string "\\[\\[" "\\&{|" ruby-syntax-before-regexp-re))
-
-(provide 'ruby-additional)
-
-;;; ruby-additional.el ends here
diff --git a/misc/ruby-electric.el b/misc/ruby-electric.el
deleted file mode 100644
index f54e91ccbc..0000000000
--- a/misc/ruby-electric.el
+++ /dev/null
@@ -1,569 +0,0 @@
-;;; ruby-electric.el --- Minor mode for electrically editing ruby code
-;;
-;; Authors: Dee Zsombor <dee dot zsombor at gmail dot com>
-;; Yukihiro Matsumoto
-;; Nobuyoshi Nakada
-;; Akinori MUSHA <knu@iDaemons.org>
-;; Jakub Kuźma <qoobaa@gmail.com>
-;; Maintainer: Akinori MUSHA <knu@iDaemons.org>
-;; Created: 6 Mar 2005
-;; URL: https://github.com/knu/ruby-electric.el
-;; Keywords: languages ruby
-;; License: The same license terms as Ruby
-;; Version: 2.2.3
-
-;;; Commentary:
-;;
-;; `ruby-electric-mode' accelerates code writing in ruby by making
-;; some keys "electric" and automatically supplying with closing
-;; parentheses and "end" as appropriate.
-;;
-;; This work was originally inspired by a code snippet posted by
-;; [Frederick Ros](https://github.com/sleeper).
-;;
-;; Add the following line to enable ruby-electric-mode under
-;; ruby-mode.
-;;
-;; (eval-after-load "ruby-mode"
-;; '(add-hook 'ruby-mode-hook 'ruby-electric-mode))
-;;
-;; Type M-x customize-group ruby-electric for configuration.
-
-;;; Code:
-
-(require 'ruby-mode)
-
-(eval-when-compile
- (require 'cl))
-
-(defgroup ruby-electric nil
- "Minor mode providing electric editing commands for ruby files"
- :group 'ruby)
-
-(defconst ruby-electric-expandable-bar-re
- "\\s-\\(do\\|{\\)\\s-*|")
-
-(defconst ruby-electric-delimiters-alist
- '((?\{ :name "Curly brace" :handler ruby-electric-curlies :closing ?\})
- (?\[ :name "Square brace" :handler ruby-electric-matching-char :closing ?\])
- (?\( :name "Round brace" :handler ruby-electric-matching-char :closing ?\))
- (?\' :name "Quote" :handler ruby-electric-matching-char)
- (?\" :name "Double quote" :handler ruby-electric-matching-char)
- (?\` :name "Back quote" :handler ruby-electric-matching-char)
- (?\| :name "Vertical bar" :handler ruby-electric-bar)
- (?\# :name "Hash" :handler ruby-electric-hash)))
-
-(defvar ruby-electric-matching-delimeter-alist
- (apply 'nconc
- (mapcar #'(lambda (x)
- (let ((delim (car x))
- (plist (cdr x)))
- (if (eq (plist-get plist :handler) 'ruby-electric-matching-char)
- (list (cons delim (or (plist-get plist :closing)
- delim))))))
- ruby-electric-delimiters-alist)))
-
-(defvar ruby-electric-expandable-keyword-re)
-
-(defmacro ruby-electric--try-insert-and-do (string &rest body)
- (declare (indent 1))
- `(let ((before (point))
- (after (progn
- (insert ,string)
- (point))))
- (unwind-protect
- (progn ,@body)
- (delete-region before after)
- (goto-char before))))
-
-(defconst ruby-modifier-beg-symbol-re
- (regexp-opt ruby-modifier-beg-keywords 'symbols))
-
-(defun ruby-electric--modifier-keyword-at-point-p ()
- "Test if there is a modifier keyword at point."
- (and (looking-at ruby-modifier-beg-symbol-re)
- (let ((end (match-end 1)))
- (not (looking-back "\\."))
- (save-excursion
- (let ((indent1 (ruby-electric--try-insert-and-do "\n"
- (ruby-calculate-indent)))
- (indent2 (save-excursion
- (goto-char end)
- (ruby-electric--try-insert-and-do " x\n"
- (ruby-calculate-indent)))))
- (= indent1 indent2))))))
-
-(defconst ruby-block-mid-symbol-re
- (regexp-opt ruby-block-mid-keywords 'symbols))
-
-(defun ruby-electric--block-mid-keyword-at-point-p ()
- "Test if there is a block mid keyword at point."
- (and (looking-at ruby-block-mid-symbol-re)
- (looking-back "^\\s-*")))
-
-(defconst ruby-block-beg-symbol-re
- (regexp-opt ruby-block-beg-keywords 'symbols))
-
-(defun ruby-electric--block-beg-keyword-at-point-p ()
- "Test if there is a block beginning keyword at point."
- (and (looking-at ruby-block-beg-symbol-re)
- (if (string= (match-string 1) "do")
- (looking-back "\\s-")
- (not (looking-back "\\.")))
- ;; (not (ruby-electric--modifier-keyword-at-point-p)) ;; implicit assumption
- ))
-
-(defcustom ruby-electric-keywords-alist
- '(("begin" . end)
- ("case" . end)
- ("class" . end)
- ("def" . end)
- ("do" . end)
- ("else" . reindent)
- ("elsif" . reindent)
- ("end" . reindent)
- ("ensure" . reindent)
- ("for" . end)
- ("if" . end)
- ("module" . end)
- ("rescue" . reindent)
- ("unless" . end)
- ("until" . end)
- ("when" . reindent)
- ("while" . end))
- "Alist of keywords and actions to define how to react to space
-or return right after each keyword. In each (KEYWORD . ACTION)
-cons, ACTION can be set to one of the following values:
-
- `reindent' Reindent the line.
-
- `end' Reindent the line and auto-close the keyword with
- end if applicable.
-
- `nil' Do nothing.
-"
- :type '(repeat (cons (string :tag "Keyword")
- (choice :tag "Action"
- :menu-tag "Action"
- (const :tag "Auto-close with end"
- :value end)
- (const :tag "Auto-reindent"
- :value reindent)
- (const :tag "None"
- :value nil))))
- :set (lambda (sym val)
- (set sym val)
- (let (keywords)
- (dolist (x val)
- (let ((keyword (car x))
- (action (cdr x)))
- (if action
- (setq keywords (cons keyword keywords)))))
- (setq ruby-electric-expandable-keyword-re
- (concat (regexp-opt keywords 'symbols)
- "$"))))
- :group 'ruby-electric)
-
-(defvar ruby-electric-mode-map
- (let ((map (make-sparse-keymap)))
- (define-key map " " 'ruby-electric-space/return)
- (define-key map [remap delete-backward-char] 'ruby-electric-delete-backward-char)
- (define-key map [remap newline] 'ruby-electric-space/return)
- (define-key map [remap newline-and-indent] 'ruby-electric-space/return)
- (define-key map [remap electric-newline-and-maybe-indent] 'ruby-electric-space/return)
- (dolist (x ruby-electric-delimiters-alist)
- (let* ((delim (car x))
- (plist (cdr x))
- (name (plist-get plist :name))
- (func (plist-get plist :handler))
- (closing (plist-get plist :closing)))
- (define-key map (char-to-string delim) func)
- (if closing
- (define-key map (char-to-string closing) 'ruby-electric-closing-char))))
- map)
- "Keymap used in ruby-electric-mode")
-
-(defcustom ruby-electric-expand-delimiters-list '(all)
- "*List of contexts where matching delimiter should be inserted.
-The word 'all' will do all insertions."
- :type `(set :extra-offset 8
- (const :tag "Everything" all)
- ,@(apply 'list
- (mapcar #'(lambda (x)
- `(const :tag ,(plist-get (cdr x) :name)
- ,(car x)))
- ruby-electric-delimiters-alist)))
- :group 'ruby-electric)
-
-(defcustom ruby-electric-newline-before-closing-bracket nil
- "*Non-nil means a newline should be inserted before an
-automatically inserted closing bracket."
- :type 'boolean :group 'ruby-electric)
-
-(defcustom ruby-electric-autoindent-on-closing-char nil
- "*Non-nil means the current line should be automatically
-indented when a closing character is manually typed in."
- :type 'boolean :group 'ruby-electric)
-
-(defvar ruby-electric-mode-hook nil
- "Called after `ruby-electric-mode' is turned on.")
-
-;;;###autoload
-(define-minor-mode ruby-electric-mode
- "Toggle Ruby Electric minor mode.
-With no argument, this command toggles the mode. Non-null prefix
-argument turns on the mode. Null prefix argument turns off the
-mode.
-
-When Ruby Electric mode is enabled, an indented 'end' is
-heuristicaly inserted whenever typing a word like 'module',
-'class', 'def', 'if', 'unless', 'case', 'until', 'for', 'begin',
-'do' followed by a space. Single, double and back quotes as well
-as braces are paired auto-magically. Expansion does not occur
-inside comments and strings. Note that you must have Font Lock
-enabled."
- ;; initial value.
- nil
- ;;indicator for the mode line.
- " REl"
- ;;keymap
- ruby-electric-mode-map
- (if ruby-electric-mode
- (run-hooks 'ruby-electric-mode-hook)))
-
-(defun ruby-electric-space/return-fallback ()
- (if (or (eq this-original-command 'ruby-electric-space/return)
- (null (ignore-errors
- ;; ac-complete may fail if there is nothing left to complete
- (call-interactively this-original-command)
- (setq this-command this-original-command))))
- ;; fall back to a globally bound command
- (let ((command (global-key-binding (char-to-string last-command-event) t)))
- (and command
- (call-interactively (setq this-command command))))))
-
-(defun ruby-electric-space/return (arg)
- (interactive "*P")
- (and (boundp 'sp-last-operation)
- (setq sp-delayed-pair nil))
- (cond ((or arg
- (region-active-p))
- (or (= last-command-event ?\s)
- (setq last-command-event ?\n))
- (ruby-electric-replace-region-or-insert))
- ((ruby-electric-space/return-can-be-expanded-p)
- (let (action)
- (save-excursion
- (goto-char (match-beginning 0))
- (let* ((keyword (match-string 1))
- (allowed-actions
- (cond ((ruby-electric--modifier-keyword-at-point-p)
- '(reindent)) ;; no end necessary
- ((ruby-electric--block-mid-keyword-at-point-p)
- '(reindent)) ;; ditto
- ((ruby-electric--block-beg-keyword-at-point-p)
- '(end reindent)))))
- (if allowed-actions
- (setq action
- (let ((action (cdr (assoc keyword ruby-electric-keywords-alist))))
- (and (memq action allowed-actions)
- action))))))
- (cond ((eq action 'end)
- (ruby-indent-line)
- (save-excursion
- (newline)
- (ruby-electric-end)))
- ((eq action 'reindent)
- (ruby-indent-line)))
- (ruby-electric-space/return-fallback)))
- ((and (eq this-original-command 'newline-and-indent)
- (ruby-electric-comment-at-point-p))
- (call-interactively (setq this-command 'comment-indent-new-line)))
- (t
- (ruby-electric-space/return-fallback))))
-
-(defun ruby-electric--get-faces-at-point ()
- (let* ((point (point))
- (value (or
- (get-text-property point 'read-face-name)
- (get-text-property point 'face))))
- (if (listp value) value (list value))))
-
-(defun ruby-electric--faces-at-point-include-p (&rest faces)
- (and ruby-electric-mode
- (loop for face in faces
- with pfaces = (ruby-electric--get-faces-at-point)
- thereis (memq face pfaces))))
-
-(defun ruby-electric-code-at-point-p()
- (not (ruby-electric--faces-at-point-include-p
- 'font-lock-string-face
- 'font-lock-comment-face)))
-
-(defun ruby-electric-string-at-point-p()
- (ruby-electric--faces-at-point-include-p
- 'font-lock-string-face))
-
-(defun ruby-electric-comment-at-point-p()
- (ruby-electric--faces-at-point-include-p
- 'font-lock-comment-face))
-
-(defun ruby-electric-escaped-p()
- (let ((f nil))
- (save-excursion
- (while (char-equal ?\\ (preceding-char))
- (backward-char 1)
- (setq f (not f))))
- f))
-
-(defun ruby-electric-command-char-expandable-punct-p(char)
- (or (memq 'all ruby-electric-expand-delimiters-list)
- (memq char ruby-electric-expand-delimiters-list)))
-
-(defun ruby-electric-space/return-can-be-expanded-p()
- (and (ruby-electric-code-at-point-p)
- (looking-back ruby-electric-expandable-keyword-re)))
-
-(defun ruby-electric-replace-region-or-insert ()
- (and (region-active-p)
- (bound-and-true-p delete-selection-mode)
- (fboundp 'delete-selection-helper)
- (delete-selection-helper (get 'self-insert-command 'delete-selection)))
- (insert (make-string (prefix-numeric-value current-prefix-arg)
- last-command-event))
- (setq this-command 'self-insert-command))
-
-(defmacro ruby-electric-insert (arg &rest body)
- `(cond ((and
- (null ,arg)
- (ruby-electric-command-char-expandable-punct-p last-command-event))
- (let ((region-beginning
- (cond ((region-active-p)
- (prog1
- (save-excursion
- (goto-char (region-beginning))
- (insert last-command-event)
- (point))
- (goto-char (region-end))))
- (t
- (insert last-command-event)
- nil))))
- ,@body
- (and region-beginning
- ;; If no extra character is inserted, go back to the
- ;; region beginning.
- (eq this-command 'self-insert-command)
- (goto-char region-beginning))))
- ((ruby-electric-replace-region-or-insert))))
-
-(defun ruby-electric-curlies (arg)
- (interactive "*P")
- (ruby-electric-insert
- arg
- (cond
- ((ruby-electric-code-at-point-p)
- (save-excursion
- (insert "}")
- (font-lock-fontify-region (line-beginning-position) (point)))
- (cond
- ((ruby-electric-string-at-point-p) ;; %w{}, %r{}, etc.
- (if region-beginning
- (forward-char 1)))
- (ruby-electric-newline-before-closing-bracket
- (cond (region-beginning
- (save-excursion
- (goto-char region-beginning)
- (newline))
- (newline)
- (forward-char 1)
- (indent-region region-beginning (line-end-position)))
- (t
- (insert " ")
- (save-excursion
- (newline)
- (ruby-indent-line t)))))
- (t
- (if region-beginning
- (save-excursion
- (goto-char region-beginning)
- (insert " "))
- (insert " "))
- (insert " ")
- (and region-beginning
- (forward-char 1)))))
- ((ruby-electric-string-at-point-p)
- (let ((start-position (1- (or region-beginning (point)))))
- (cond
- ((char-equal ?\# (char-before start-position))
- (unless (save-excursion
- (goto-char (1- start-position))
- (ruby-electric-escaped-p))
- (insert "}")
- (or region-beginning
- (backward-char 1))))
- ((or
- (ruby-electric-command-char-expandable-punct-p ?\#)
- (save-excursion
- (goto-char start-position)
- (ruby-electric-escaped-p)))
- (if region-beginning
- (goto-char region-beginning))
- (setq this-command 'self-insert-command))
- (t
- (save-excursion
- (goto-char start-position)
- (insert "#"))
- (insert "}")
- (or region-beginning
- (backward-char 1))))))
- (t
- (delete-char -1)
- (ruby-electric-replace-region-or-insert)))))
-
-(defun ruby-electric-hash (arg)
- (interactive "*P")
- (ruby-electric-insert
- arg
- (if (ruby-electric-string-at-point-p)
- (let ((start-position (1- (or region-beginning (point)))))
- (cond
- ((char-equal (following-char) ?')) ;; likely to be in ''
- ((save-excursion
- (goto-char start-position)
- (ruby-electric-escaped-p)))
- (region-beginning
- (save-excursion
- (goto-char (1+ start-position))
- (insert "{"))
- (insert "}"))
- (t
- (insert "{")
- (save-excursion
- (insert "}")))))
- (delete-char -1)
- (ruby-electric-replace-region-or-insert))))
-
-(defun ruby-electric-matching-char (arg)
- (interactive "*P")
- (ruby-electric-insert
- arg
- (let ((closing (cdr (assoc last-command-event
- ruby-electric-matching-delimeter-alist))))
- (cond
- ;; quotes
- ((char-equal closing last-command-event)
- (cond ((let ((start-position (or region-beginning (point))))
- ;; check if this quote has just started a string
- (and
- (unwind-protect
- (save-excursion
- (subst-char-in-region (1- start-position) start-position
- last-command-event ?\s)
- (goto-char (1- start-position))
- (save-excursion
- (font-lock-fontify-region (line-beginning-position) (1+ (point))))
- (not (ruby-electric-string-at-point-p)))
- (subst-char-in-region (1- start-position) start-position
- ?\s last-command-event))
- (save-excursion
- (goto-char (1- start-position))
- (save-excursion
- (font-lock-fontify-region (line-beginning-position) (1+ (point))))
- (ruby-electric-string-at-point-p))))
- (if region-beginning
- ;; escape quotes of the same kind, backslash and hash
- (let ((re (format "[%c\\%s]"
- last-command-event
- (if (char-equal last-command-event ?\")
- "#" "")))
- (bound (point)))
- (save-excursion
- (goto-char region-beginning)
- (while (re-search-forward re bound t)
- (let ((end (point)))
- (replace-match "\\\\\\&")
- (setq bound (+ bound (- (point) end))))))))
- (insert closing)
- (or region-beginning
- (backward-char 1)))
- (t
- (and (eq last-command 'ruby-electric-matching-char)
- (char-equal (following-char) closing) ;; repeated quotes
- (delete-char 1))
- (setq this-command 'self-insert-command))))
- ((ruby-electric-code-at-point-p)
- (insert closing)
- (or region-beginning
- (backward-char 1)))))))
-
-(defun ruby-electric-closing-char(arg)
- (interactive "*P")
- (cond
- (arg
- (ruby-electric-replace-region-or-insert))
- ((and
- (eq last-command 'ruby-electric-curlies)
- (= last-command-event ?})
- (not (char-equal (preceding-char) last-command-event))) ;; {}
- (if (char-equal (following-char) ?\n) (delete-char 1))
- (delete-horizontal-space)
- (forward-char))
- ((and
- (= last-command-event (following-char))
- (not (char-equal (preceding-char) last-command-event))
- (memq last-command '(ruby-electric-matching-char
- ruby-electric-closing-char))) ;; ()/[] and (())/[[]]
- (forward-char))
- (t
- (ruby-electric-replace-region-or-insert)
- (if ruby-electric-autoindent-on-closing-char
- (ruby-indent-line)))))
-
-(defun ruby-electric-bar(arg)
- (interactive "*P")
- (ruby-electric-insert
- arg
- (cond ((and (ruby-electric-code-at-point-p)
- (looking-back ruby-electric-expandable-bar-re))
- (save-excursion (insert "|")))
- (t
- (delete-char -1)
- (ruby-electric-replace-region-or-insert)))))
-
-(defun ruby-electric-delete-backward-char(arg)
- (interactive "*p")
- (cond ((memq last-command '(ruby-electric-matching-char
- ruby-electric-bar))
- (delete-char 1))
- ((eq last-command 'ruby-electric-curlies)
- (cond ((eolp)
- (cond ((char-equal (preceding-char) ?\s)
- (setq this-command last-command))
- ((char-equal (preceding-char) ?{)
- (and (looking-at "[ \t\n]*}")
- (delete-char (- (match-end 0) (match-beginning 0)))))))
- ((char-equal (following-char) ?\s)
- (setq this-command last-command)
- (delete-char 1))
- ((char-equal (following-char) ?})
- (delete-char 1))))
- ((eq last-command 'ruby-electric-hash)
- (and (char-equal (preceding-char) ?{)
- (delete-char 1))))
- (delete-char (- arg)))
-
-(put 'ruby-electric-delete-backward-char 'delete-selection 'supersede)
-
-(defun ruby-electric-end ()
- (interactive)
- (if (eq (char-syntax (preceding-char)) ?w)
- (insert " "))
- (insert "end")
- (save-excursion
- (if (eq (char-syntax (following-char)) ?w)
- (insert " "))
- (ruby-indent-line t)))
-
-(provide 'ruby-electric)
-
-;;; ruby-electric.el ends here
diff --git a/misc/ruby-mode.el b/misc/ruby-mode.el
deleted file mode 100644
index b1abd18a9e..0000000000
--- a/misc/ruby-mode.el
+++ /dev/null
@@ -1,1584 +0,0 @@
-;;; ruby-mode.el --- Major mode for editing Ruby files
-
-;; Copyright (C) 1994, 1995, 1996 1997, 1998, 1999, 2000, 2001,
-;; 2002,2003, 2004, 2005, 2006, 2007, 2008
-;; Free Software Foundation, Inc.
-
-;; Authors: Yukihiro Matsumoto, Nobuyoshi Nakada
-;; URL: http://www.emacswiki.org/cgi-bin/wiki/RubyMode
-;; Created: Fri Feb 4 14:49:13 JST 1994
-;; Keywords: languages ruby
-;; Version: 0.9
-
-;; This file is not part of GNU Emacs. However, a newer version of
-;; ruby-mode is included in recent releases of GNU Emacs (version 23
-;; and up), but the new version is not guaranteed to be compatible
-;; with older versions of Emacs or XEmacs. This file is the last
-;; version that aims to keep this compatibility.
-
-;; You can also get the latest version from the Emacs Lisp Package
-;; Archive: http://tromey.com/elpa
-
-;; This file is free software: you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; It is distributed in the hope that it will be useful, but WITHOUT
-;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
-;; License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with it. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; Provides font-locking, indentation support, and navigation for Ruby code.
-;;
-;; If you're installing manually, you should add this to your .emacs
-;; file after putting it on your load path:
-;;
-;; (autoload 'ruby-mode "ruby-mode" "Major mode for ruby files" t)
-;; (add-to-list 'auto-mode-alist '("\\.rb$" . ruby-mode))
-;; (add-to-list 'interpreter-mode-alist '("ruby" . ruby-mode))
-;;
-
-;;; Code:
-
-(defconst ruby-mode-revision "$Revision$"
- "Ruby mode revision string.")
-
-(defconst ruby-mode-version
- (and (string-match "[0-9.]+" ruby-mode-revision)
- (substring ruby-mode-revision (match-beginning 0) (match-end 0)))
- "Ruby mode version number.")
-
-(defconst ruby-keyword-end-re
- (if (string-match "\\_>" "ruby")
- "\\_>"
- "\\>"))
-
-(defconst ruby-block-beg-keywords
- '("class" "module" "def" "if" "unless" "case" "while" "until" "for" "begin" "do")
- "Keywords at the beginning of blocks.")
-
-(defconst ruby-block-beg-re
- (regexp-opt ruby-block-beg-keywords)
- "Regexp to match the beginning of blocks.")
-
-(defconst ruby-non-block-do-re
- (concat (regexp-opt '("while" "until" "for" "rescue") t) ruby-keyword-end-re)
- "Regexp to match")
-
-(defconst ruby-indent-beg-re
- (concat "\\(\\s *" (regexp-opt '("class" "module" "def") t) "\\)\\|"
- (regexp-opt '("if" "unless" "case" "while" "until" "for" "begin")))
- "Regexp to match where the indentation gets deeper.")
-
-(defconst ruby-modifier-beg-keywords
- '("if" "unless" "while" "until")
- "Modifiers that are the same as the beginning of blocks.")
-
-(defconst ruby-modifier-beg-re
- (regexp-opt ruby-modifier-beg-keywords)
- "Regexp to match modifiers same as the beginning of blocks.")
-
-(defconst ruby-modifier-re
- (regexp-opt (cons "rescue" ruby-modifier-beg-keywords))
- "Regexp to match modifiers.")
-
-(defconst ruby-block-mid-keywords
- '("then" "else" "elsif" "when" "rescue" "ensure")
- "Keywords where the indentation gets shallower in middle of block statements.")
-
-(defconst ruby-block-mid-re
- (regexp-opt ruby-block-mid-keywords)
- "Regexp to match where the indentation gets shallower in middle of block statements.")
-
-(defconst ruby-block-op-keywords
- '("and" "or" "not")
- "Block operators.")
-
-(defconst ruby-block-hanging-re
- (regexp-opt (append ruby-modifier-beg-keywords ruby-block-op-keywords))
- "Regexp to match hanging block modifiers.")
-
-(defconst ruby-block-end-re "\\_<end\\_>")
-
-(defconst ruby-here-doc-beg-re
- "\\(<\\)<\\([-~]\\)?\\(\\([a-zA-Z0-9_]+\\)\\|[\"]\\([^\"]+\\)[\"]\\|[']\\([^']+\\)[']\\)")
-
-(defconst ruby-here-doc-end-re
- "^\\([ \t]+\\)?\\(.*\\)\\(.\\)$")
-
-(defun ruby-here-doc-end-match ()
- (concat "^"
- (if (match-string 2) "[ \t]*" nil)
- (regexp-quote
- (or (match-string 4)
- (match-string 5)
- (match-string 6)))))
-
-(defun ruby-here-doc-beg-match ()
- (let ((contents (concat
- (regexp-quote (concat (match-string 2) (match-string 3)))
- (if (string= (match-string 3) "_") "\\B" "\\b"))))
- (concat "<<"
- (let ((match (match-string 1)))
- (if (and match (> (length match) 0))
- (concat "\\(?:[-~]\\([\"']?\\)\\|\\([\"']\\)" (match-string 1) "\\)"
- contents "\\(\\1\\|\\2\\)")
- (concat "[-~]?\\([\"']\\|\\)" contents "\\1"))))))
-
-(defconst ruby-delimiter
- (concat "[?$/%(){}#\"'`.:]\\|<<\\|\\[\\|\\]\\|\\_<\\("
- ruby-block-beg-re
- "\\)\\_>\\|" ruby-block-end-re
- "\\|^=begin\\|" ruby-here-doc-beg-re)
- )
-
-(defconst ruby-negative
- (concat "^[ \t]*\\(\\(" ruby-block-mid-re "\\)\\>\\|"
- ruby-block-end-re "\\|}\\|\\]\\)")
- "Regexp to match where the indentation gets shallower.")
-
-(defconst ruby-operator-chars "-,.+*/%&|^~=<>:")
-(defconst ruby-operator-re (concat "[" ruby-operator-chars "]"))
-
-(defconst ruby-symbol-chars "a-zA-Z0-9_")
-(defconst ruby-symbol-re (concat "[" ruby-symbol-chars "]"))
-
-(defvar ruby-mode-abbrev-table nil
- "Abbrev table in use in ruby-mode buffers.")
-
-(define-abbrev-table 'ruby-mode-abbrev-table ())
-
-(defvar ruby-mode-map nil "Keymap used in ruby mode.")
-
-(if ruby-mode-map
- nil
- (setq ruby-mode-map (make-sparse-keymap))
- (define-key ruby-mode-map "{" 'ruby-electric-brace)
- (define-key ruby-mode-map "}" 'ruby-electric-brace)
- (define-key ruby-mode-map "\e\C-a" 'ruby-beginning-of-defun)
- (define-key ruby-mode-map "\e\C-e" 'ruby-end-of-defun)
- (define-key ruby-mode-map "\e\C-b" 'ruby-backward-sexp)
- (define-key ruby-mode-map "\e\C-f" 'ruby-forward-sexp)
- (define-key ruby-mode-map "\e\C-p" 'ruby-beginning-of-block)
- (define-key ruby-mode-map "\e\C-n" 'ruby-end-of-block)
- (define-key ruby-mode-map "\e\C-h" 'ruby-mark-defun)
- (define-key ruby-mode-map "\e\C-q" 'ruby-indent-exp)
- (define-key ruby-mode-map "\t" 'ruby-indent-command)
- (define-key ruby-mode-map "\C-c\C-e" 'ruby-insert-end)
- (define-key ruby-mode-map "\C-j" 'ruby-reindent-then-newline-and-indent)
- (define-key ruby-mode-map "\C-c{" 'ruby-toggle-block)
- (define-key ruby-mode-map "\C-c\C-u" 'uncomment-region))
-
-(defvar ruby-mode-syntax-table nil
- "Syntax table in use in ruby-mode buffers.")
-
-(if ruby-mode-syntax-table
- ()
- (setq ruby-mode-syntax-table (make-syntax-table))
- (modify-syntax-entry ?\' "\"" ruby-mode-syntax-table)
- (modify-syntax-entry ?\" "\"" ruby-mode-syntax-table)
- (modify-syntax-entry ?\` "\"" ruby-mode-syntax-table)
- (modify-syntax-entry ?# "<" ruby-mode-syntax-table)
- (modify-syntax-entry ?\n ">" ruby-mode-syntax-table)
- (modify-syntax-entry ?\\ "\\" ruby-mode-syntax-table)
- (modify-syntax-entry ?$ "." ruby-mode-syntax-table)
- (modify-syntax-entry ?? "_" ruby-mode-syntax-table)
- (modify-syntax-entry ?_ "_" ruby-mode-syntax-table)
- (modify-syntax-entry ?: "_" ruby-mode-syntax-table)
- (modify-syntax-entry ?< "." ruby-mode-syntax-table)
- (modify-syntax-entry ?> "." ruby-mode-syntax-table)
- (modify-syntax-entry ?& "." ruby-mode-syntax-table)
- (modify-syntax-entry ?| "." ruby-mode-syntax-table)
- (modify-syntax-entry ?% "." ruby-mode-syntax-table)
- (modify-syntax-entry ?= "." ruby-mode-syntax-table)
- (modify-syntax-entry ?/ "." ruby-mode-syntax-table)
- (modify-syntax-entry ?+ "." ruby-mode-syntax-table)
- (modify-syntax-entry ?* "." ruby-mode-syntax-table)
- (modify-syntax-entry ?- "." ruby-mode-syntax-table)
- (modify-syntax-entry ?\; "." ruby-mode-syntax-table)
- (modify-syntax-entry ?\( "()" ruby-mode-syntax-table)
- (modify-syntax-entry ?\) ")(" ruby-mode-syntax-table)
- (modify-syntax-entry ?\{ "(}" ruby-mode-syntax-table)
- (modify-syntax-entry ?\} "){" ruby-mode-syntax-table)
- (modify-syntax-entry ?\[ "(]" ruby-mode-syntax-table)
- (modify-syntax-entry ?\] ")[" ruby-mode-syntax-table)
- )
-
-(defcustom ruby-indent-tabs-mode nil
- "*Indentation can insert tabs in ruby mode if this is non-nil."
- :type 'boolean :group 'ruby)
-(put 'ruby-indent-tabs-mode 'safe-local-variable 'booleanp)
-
-(defcustom ruby-indent-level 2
- "*Indentation of ruby statements."
- :type 'integer :group 'ruby)
-(put 'ruby-indent-level 'safe-local-variable 'integerp)
-
-(defcustom ruby-comment-column 32
- "*Indentation column of comments."
- :type 'integer :group 'ruby)
-(put 'ruby-comment-column 'safe-local-variable 'integerp)
-
-(defcustom ruby-deep-arglist t
- "*Deep indent lists in parenthesis when non-nil.
-Also ignores spaces after parenthesis when 'space."
- :group 'ruby)
-(put 'ruby-deep-arglist 'safe-local-variable 'booleanp)
-
-(defcustom ruby-deep-indent-paren '(?\( ?\[ ?\] t)
- "*Deep indent lists in parenthesis when non-nil. t means continuous line.
-Also ignores spaces after parenthesis when 'space."
- :group 'ruby)
-
-(defcustom ruby-deep-indent-paren-style 'space
- "Default deep indent style."
- :options '(t nil space) :group 'ruby)
-
-(defcustom ruby-encoding-map
- '((us-ascii . nil) ;; Do not put coding: us-ascii
- (utf-8 . nil) ;; Do not put coding: utf-8
- (shift-jis . cp932) ;; Emacs charset name of Shift_JIS
- (shift_jis . cp932) ;; MIME charset name of Shift_JIS
- (japanese-cp932 . cp932)) ;; Emacs charset name of CP932
- "Alist to map encoding name from Emacs to Ruby.
-Associating an encoding name with nil means it needs not be
-explicitly declared in magic comment."
- :type '(repeat (cons (symbol :tag "From") (symbol :tag "To")))
- :group 'ruby)
-
-(defcustom ruby-use-encoding-map t
- "*Use `ruby-encoding-map' to set encoding magic comment if this is non-nil."
- :type 'boolean :group 'ruby)
-
-(defvar ruby-indent-point nil "internal variable")
-
-(eval-when-compile (require 'cl))
-(defun ruby-imenu-create-index-in-block (prefix beg end)
- (let ((index-alist '()) (case-fold-search nil)
- name next pos decl sing)
- (goto-char beg)
- (while (re-search-forward "^\\s *\\(\\(class\\s +\\|\\(class\\s *<<\\s *\\)\\|module\\s +\\)\\([^\(<\n ]+\\)\\|\\(def\\|alias\\)\\s +\\([^\(\n ]+\\)\\)" end t)
- (setq sing (match-beginning 3))
- (setq decl (match-string 5))
- (setq next (match-end 0))
- (setq name (or (match-string 4) (match-string 6)))
- (setq pos (match-beginning 0))
- (cond
- ((string= "alias" decl)
- (if prefix (setq name (concat prefix name)))
- (push (cons name pos) index-alist))
- ((string= "def" decl)
- (if prefix
- (setq name
- (cond
- ((string-match "^self\." name)
- (concat (substring prefix 0 -1) (substring name 4)))
- (t (concat prefix name)))))
- (push (cons name pos) index-alist)
- (ruby-accurate-end-of-block end))
- (t
- (if (string= "self" name)
- (if prefix (setq name (substring prefix 0 -1)))
- (if prefix (setq name (concat (substring prefix 0 -1) "::" name)))
- (push (cons name pos) index-alist))
- (ruby-accurate-end-of-block end)
- (setq beg (point))
- (setq index-alist
- (nconc (ruby-imenu-create-index-in-block
- (concat name (if sing "." "#"))
- next beg) index-alist))
- (goto-char beg))))
- index-alist))
-
-(defun ruby-imenu-create-index ()
- (nreverse (ruby-imenu-create-index-in-block nil (point-min) nil)))
-
-(defun ruby-accurate-end-of-block (&optional end)
- (let (state)
- (or end (setq end (point-max)))
- (while (and (setq state (apply 'ruby-parse-partial end state))
- (>= (nth 2 state) 0) (< (point) end)))))
-
-(defun ruby-mode-variables ()
- (set-syntax-table ruby-mode-syntax-table)
- (setq show-trailing-whitespace t)
- (setq local-abbrev-table ruby-mode-abbrev-table)
- (make-local-variable 'indent-line-function)
- (setq indent-line-function 'ruby-indent-line)
- (make-local-variable 'require-final-newline)
- (setq require-final-newline t)
- (make-local-variable 'comment-start)
- (setq comment-start "# ")
- (make-local-variable 'comment-end)
- (setq comment-end "")
- (make-local-variable 'comment-column)
- (setq comment-column ruby-comment-column)
- (make-local-variable 'comment-start-skip)
- (setq comment-start-skip "#+ *")
- (setq indent-tabs-mode ruby-indent-tabs-mode)
- (make-local-variable 'parse-sexp-ignore-comments)
- (setq parse-sexp-ignore-comments t)
- (make-local-variable 'parse-sexp-lookup-properties)
- (setq parse-sexp-lookup-properties t)
- (make-local-variable 'paragraph-start)
- (setq paragraph-start (concat "$\\|" page-delimiter))
- (make-local-variable 'paragraph-separate)
- (setq paragraph-separate paragraph-start)
- (make-local-variable 'paragraph-ignore-fill-prefix)
- (setq paragraph-ignore-fill-prefix t))
-
-(defun ruby-mode-set-encoding ()
- "Insert or update a magic comment header with the proper encoding.
-`ruby-encoding-map' is looked up to convert an encoding name from
-Emacs to Ruby."
- (let* ((nonascii
- (save-excursion
- (widen)
- (goto-char (point-min))
- (re-search-forward "[^\0-\177]" nil t)))
- (coding-system
- (or coding-system-for-write
- buffer-file-coding-system))
- (coding-system
- (and coding-system
- (coding-system-change-eol-conversion coding-system nil)))
- (coding-system
- (and coding-system
- (or
- (coding-system-get coding-system :mime-charset)
- (let ((coding-type (coding-system-get coding-system :coding-type)))
- (cond ((eq coding-type 'undecided)
- (if nonascii
- (or (and (coding-system-get coding-system :prefer-utf-8)
- 'utf-8)
- (coding-system-get default-buffer-file-coding-system :coding-type)
- 'ascii-8bit)))
- ((memq coding-type '(utf-8 shift-jis))
- coding-type)
- (t coding-system))))))
- (coding-system
- (or coding-system
- 'us-ascii))
- (coding-system
- (let ((cons (assq coding-system ruby-encoding-map)))
- (if cons (cdr cons) coding-system)))
- (coding-system
- (and coding-system
- (symbol-name coding-system))))
- (if coding-system
- (save-excursion
- (widen)
- (goto-char (point-min))
- (if (looking-at "^#!") (beginning-of-line 2))
- (cond ((looking-at "\\s *#.*-\*-\\s *\\(en\\)?coding\\s *:\\s *\\([-a-z0-9_]*\\)\\s *\\(;\\|-\*-\\)")
- (unless (string= (match-string 2) coding-system)
- (goto-char (match-beginning 2))
- (delete-region (point) (match-end 2))
- (and (looking-at "-\*-")
- (let ((n (skip-chars-backward " ")))
- (cond ((= n 0) (insert " ") (backward-char))
- ((= n -1) (insert " "))
- ((forward-char)))))
- (insert coding-system)))
- ((looking-at "\\s *#.*coding\\s *[:=]"))
- (t (when ruby-insert-encoding-magic-comment
- (insert "# -*- coding: " coding-system " -*-\n"))))))))
-
-(defun ruby-current-indentation ()
- (save-excursion
- (beginning-of-line)
- (back-to-indentation)
- (current-column)))
-
-(defun ruby-indent-line (&optional flag)
- "Correct indentation of the current ruby line."
- (ruby-indent-to (ruby-calculate-indent)))
-
-(defun ruby-indent-command ()
- (interactive)
- (ruby-indent-line t))
-
-(defun ruby-indent-to (x)
- (if x
- (let (shift top beg)
- (and (< x 0) (error "invalid nest"))
- (setq shift (current-column))
- (beginning-of-line)
- (setq beg (point))
- (back-to-indentation)
- (setq top (current-column))
- (skip-chars-backward " \t")
- (if (>= shift top) (setq shift (- shift top))
- (setq shift 0))
- (if (and (bolp)
- (= x top))
- (move-to-column (+ x shift))
- (move-to-column top)
- (delete-region beg (point))
- (beginning-of-line)
- (indent-to x)
- (move-to-column (+ x shift))))))
-
-(defun ruby-special-char-p (&optional pnt)
- (setq pnt (or pnt (point)))
- (let ((c (char-before pnt)) (b (and (< (point-min) pnt) (char-before (1- pnt)))))
- (cond ((or (eq c ??) (eq c ?$)))
- ((and (eq c ?:) (or (not b) (eq (char-syntax b) ? ))))
- ((eq c ?\\) (eq b ??)))))
-
-(defun ruby-singleton-class-p ()
- (save-excursion
- (forward-word -1)
- (and (or (bolp) (not (eq (char-before (point)) ?_)))
- (looking-at "class\\s *<<"))))
-
-(defun ruby-expr-beg (&optional option)
- (save-excursion
- (store-match-data nil)
- (let ((space (skip-chars-backward " \t"))
- (start (point)))
- (cond
- ((bolp) t)
- ((progn
- (forward-char -1)
- (and (looking-at "\\?")
- (or (eq (char-syntax (preceding-char)) ?w)
- (ruby-special-char-p))))
- nil)
- ((and (eq option 'heredoc) (< space 0))
- (not (progn (goto-char start) (ruby-singleton-class-p))))
- ((or (looking-at ruby-operator-re)
- (looking-at "[\\[({,;]")
- (and (looking-at "[!?]")
- (or (not (eq option 'modifier))
- (bolp)
- (save-excursion (forward-char -1) (looking-at "\\Sw$"))))
- (and (looking-at ruby-symbol-re)
- (skip-chars-backward ruby-symbol-chars)
- (cond
- ((looking-at (regexp-opt
- (append ruby-block-beg-keywords
- ruby-block-op-keywords
- ruby-block-mid-keywords)
- 'words))
- (goto-char (match-end 0))
- (not (looking-at "\\s_\\|[!?:]")))
- ((eq option 'expr-qstr)
- (looking-at "[a-zA-Z][a-zA-z0-9_]* +%[^ \t]"))
- ((eq option 'expr-re)
- (looking-at "[a-zA-Z][a-zA-z0-9_]* +/[^ \t]"))
- (t nil)))))))))
-
-(defun ruby-forward-string (term &optional end no-error expand)
- (let ((n 1) (c (string-to-char term))
- (re (if expand
- (concat "[^\\]\\(\\\\\\\\\\)*\\([" term "]\\|\\(#{\\)\\)")
- (concat "[^\\]\\(\\\\\\\\\\)*[" term "]"))))
- (while (and (re-search-forward re end no-error)
- (if (match-beginning 3)
- (ruby-forward-string "}{" end no-error nil)
- (> (setq n (if (eq (char-before (point)) c)
- (1- n) (1+ n))) 0)))
- (forward-char -1))
- (cond ((zerop n))
- (no-error nil)
- ((error "unterminated string")))))
-
-(defun ruby-deep-indent-paren-p (c &optional pos)
- (cond ((save-excursion
- (if pos (goto-char pos))
- (ruby-expr-beg))
- nil)
- ((listp ruby-deep-indent-paren)
- (let ((deep (assoc c ruby-deep-indent-paren)))
- (cond (deep
- (or (cdr deep) ruby-deep-indent-paren-style))
- ((memq c ruby-deep-indent-paren)
- ruby-deep-indent-paren-style))))
- ((eq c ruby-deep-indent-paren) ruby-deep-indent-paren-style)
- ((eq c ?\( ) ruby-deep-arglist)))
-
-(defun ruby-parse-partial (&optional end in-string nest depth pcol indent)
- (or depth (setq depth 0))
- (or indent (setq indent 0))
- (when (re-search-forward ruby-delimiter end 'move)
- (let ((pnt (point)) w re expand)
- (goto-char (match-beginning 0))
- (cond
- ((and (memq (char-before) '(?@ ?$)) (looking-at "\\sw"))
- (goto-char pnt))
- ((looking-at "[\"`]") ;skip string
- (cond
- ((and (not (eobp))
- (ruby-forward-string (buffer-substring (point) (1+ (point))) end t t))
- nil)
- (t
- (setq in-string (point))
- (goto-char end))))
- ((looking-at "'")
- (cond
- ((and (not (eobp))
- (re-search-forward "[^\\]\\(\\\\\\\\\\)*'" end t))
- nil)
- (t
- (setq in-string (point))
- (goto-char end))))
- ((looking-at "/=")
- (goto-char pnt))
- ((looking-at "/")
- (cond
- ((and (not (eobp)) (ruby-expr-beg 'expr-re))
- (if (ruby-forward-string "/" end t t)
- nil
- (setq in-string (point))
- (goto-char end)))
- (t
- (goto-char pnt))))
- ((looking-at "%")
- (cond
- ((and (not (eobp))
- (ruby-expr-beg 'expr-qstr)
- (not (looking-at "%="))
- (looking-at "%[QqrxWw]?\\([^a-zA-Z0-9 \t\n]\\)"))
- (goto-char (match-beginning 1))
- (setq expand (not (memq (char-before) '(?q ?w))))
- (setq w (match-string 1))
- (cond
- ((string= w "[") (setq re "]["))
- ((string= w "{") (setq re "}{"))
- ((string= w "(") (setq re ")("))
- ((string= w "<") (setq re "><"))
- ((and expand (string= w "\\"))
- (setq w (concat "\\" w))))
- (unless (cond (re (ruby-forward-string re end t expand))
- (expand (ruby-forward-string w end t t))
- (t (re-search-forward
- (if (string= w "\\")
- "\\\\[^\\]*\\\\"
- (concat "[^\\]\\(\\\\\\\\\\)*" w))
- end t)))
- (setq in-string (point))
- (goto-char end)))
- (t
- (goto-char pnt))))
- ((looking-at "\\?") ;skip ?char
- (cond
- ((and (ruby-expr-beg)
- (looking-at "?\\(\\\\C-\\|\\\\M-\\)*\\\\?."))
- (goto-char (match-end 0)))
- (t
- (goto-char pnt))))
- ((looking-at "\\$") ;skip $char
- (goto-char pnt)
- (forward-char 1))
- ((looking-at "#") ;skip comment
- (forward-line 1)
- (goto-char (point))
- )
- ((looking-at "[\\[{(]")
- (let ((deep (ruby-deep-indent-paren-p (char-after))))
- (if (and deep (or (not (eq (char-after) ?\{)) (ruby-expr-beg)))
- (progn
- (and (eq deep 'space) (looking-at ".\\s +[^# \t\n]")
- (setq pnt (1- (match-end 0))))
- (setq nest (cons (cons (char-after (point)) (point)) nest))
- (setq pcol (cons (cons pnt depth) pcol))
- (setq depth 0))
- (setq nest (cons (cons (char-after (point)) pnt) nest))
- (setq depth (1+ depth))))
- (goto-char pnt)
- )
- ((looking-at "[])}]")
- (if (ruby-deep-indent-paren-p (matching-paren (char-after))
- (if nest
- (cdr (nth 0 nest))
- (save-excursion
- (forward-char)
- (ruby-backward-sexp)
- (point))))
- (setq depth (cdr (car pcol)) pcol (cdr pcol))
- (setq depth (1- depth)))
- (setq nest (cdr nest))
- (goto-char pnt))
- ((looking-at ruby-block-end-re)
- (if (or (and (not (bolp))
- (progn
- (forward-char -1)
- (setq w (char-after (point)))
- (or (eq ?_ w)
- (eq ?. w))))
- (progn
- (goto-char pnt)
- (setq w (char-after (point)))
- (or (eq ?_ w)
- (eq ?! w)
- (eq ?? w))))
- nil
- (setq nest (cdr nest))
- (setq depth (1- depth)))
- (goto-char pnt))
- ((looking-at "def\\s +[^(\n;]*")
- (if (or (bolp)
- (progn
- (forward-char -1)
- (not (eq ?_ (char-after (point))))))
- (progn
- (setq nest (cons (cons nil pnt) nest))
- (setq depth (1+ depth))))
- (goto-char (match-end 0)))
- ((looking-at (concat "\\_<\\(" ruby-block-beg-re "\\)\\_>"))
- (and
- (save-match-data
- (or (not (looking-at (concat "do" ruby-keyword-end-re)))
- (save-excursion
- (back-to-indentation)
- (not (looking-at ruby-non-block-do-re)))))
- (or (bolp)
- (progn
- (forward-char -1)
- (setq w (char-after (point)))
- (not (or (eq ?_ w)
- (eq ?. w)))))
- (goto-char pnt)
- (setq w (char-after (point)))
- (not (eq ?_ w))
- (not (eq ?! w))
- (not (eq ?? w))
- (not (eq ?: w))
- (skip-chars-forward " \t")
- (goto-char (match-beginning 0))
- (or (not (looking-at ruby-modifier-re))
- (ruby-expr-beg 'modifier))
- (goto-char pnt)
- (setq nest (cons (cons nil pnt) nest))
- (setq depth (1+ depth)))
- (goto-char pnt))
- ((looking-at ":\\(['\"]\\)")
- (goto-char (match-beginning 1))
- (ruby-forward-string (buffer-substring (match-beginning 1) (match-end 1)) end))
- ((looking-at ":\\([-,.+*/%&|^~<>]=?\\|===?\\|<=>\\|![~=]?\\)")
- (goto-char (match-end 0)))
- ((looking-at ":\\([a-zA-Z_][a-zA-Z_0-9]*[!?=]?\\)?")
- (goto-char (match-end 0)))
- ((or (looking-at "\\.\\.\\.?")
- (looking-at "\\.[0-9]+")
- (looking-at "\\.[a-zA-Z_0-9]+")
- (looking-at "\\."))
- (goto-char (match-end 0)))
- ((looking-at "^=begin")
- (if (re-search-forward "^=end" end t)
- (forward-line 1)
- (setq in-string (match-end 0))
- (goto-char end)))
- ((looking-at "<<")
- (cond
- ((and (ruby-expr-beg 'heredoc)
- (looking-at "<<\\([-~]\\)?\\(\\([\"'`]\\)\\([^\n]+?\\)\\3\\|\\(?:\\sw\\|\\s_\\)+\\)"))
- (setq re (regexp-quote (or (match-string 4) (match-string 2))))
- (if (match-beginning 1) (setq re (concat "\\s *" re)))
- (let* ((id-end (goto-char (match-end 0)))
- (line-end-position (save-excursion (end-of-line) (point)))
- (state (list in-string nest depth pcol indent)))
- ;; parse the rest of the line
- (while (and (> line-end-position (point))
- (setq state (apply 'ruby-parse-partial
- line-end-position state))))
- (setq in-string (car state)
- nest (nth 1 state)
- depth (nth 2 state)
- pcol (nth 3 state)
- indent (nth 4 state))
- ;; skip heredoc section
- (if (re-search-forward (concat "^" re "$") end 'move)
- (forward-line 1)
- (setq in-string id-end)
- (goto-char end))))
- (t
- (goto-char pnt))))
- ((looking-at "^__END__$")
- (goto-char pnt))
- ((looking-at ruby-here-doc-beg-re)
- (if (re-search-forward (ruby-here-doc-end-match)
- ruby-indent-point t)
- (forward-line 1)
- (setq in-string (match-end 0))
- (goto-char ruby-indent-point)))
- (t
- (error (format "bad string %s"
- (buffer-substring (point) pnt)
- ))))))
- (list in-string nest depth pcol))
-
-(defun ruby-parse-region (start end)
- (let (state)
- (save-excursion
- (if start
- (goto-char start)
- (ruby-beginning-of-indent))
- (save-restriction
- (narrow-to-region (point) end)
- (while (and (> end (point))
- (setq state (apply 'ruby-parse-partial end state))))))
- (list (nth 0 state) ; in-string
- (car (nth 1 state)) ; nest
- (nth 2 state) ; depth
- (car (car (nth 3 state))) ; pcol
- ;(car (nth 5 state)) ; indent
- )))
-
-(defun ruby-indent-size (pos nest)
- (+ pos (* (or nest 1) ruby-indent-level)))
-
-(defun ruby-calculate-indent (&optional parse-start)
- (save-excursion
- (beginning-of-line)
- (let ((ruby-indent-point (point))
- (case-fold-search nil)
- state bol eol begin op-end
- (paren (progn (skip-syntax-forward " ")
- (and (char-after) (matching-paren (char-after)))))
- (indent 0))
- (if parse-start
- (goto-char parse-start)
- (ruby-beginning-of-indent)
- (setq parse-start (point)))
- (back-to-indentation)
- (setq indent (current-column))
- (setq state (ruby-parse-region parse-start ruby-indent-point))
- (cond
- ((nth 0 state) ; within string
- (setq indent nil)) ; do nothing
- ((car (nth 1 state)) ; in paren
- (goto-char (setq begin (cdr (nth 1 state))))
- (let ((deep (ruby-deep-indent-paren-p (car (nth 1 state))
- (1- (cdr (nth 1 state))))))
- (if deep
- (cond ((and (eq deep t) (eq (car (nth 1 state)) paren))
- (skip-syntax-backward " ")
- (setq indent (1- (current-column))))
- ((eq deep 'space)
- (goto-char (cdr (nth 1 state)))
- (setq indent (1+ (current-column))))
- ((let ((s (ruby-parse-region (point) ruby-indent-point)))
- (and (nth 2 s) (> (nth 2 s) 0)
- (or (goto-char (cdr (nth 1 s))) t)))
- (forward-word -1)
- (setq indent (ruby-indent-size (current-column) (nth 2 state))))
- (t
- (setq indent (current-column))
- (cond ((eq deep 'space))
- (paren (setq indent (1- indent)))
- (t (setq indent (ruby-indent-size (1- indent) 1))))))
- (if (nth 3 state) (goto-char (nth 3 state))
- (goto-char parse-start) (back-to-indentation))
- (setq indent (ruby-indent-size (current-column) (nth 2 state))))
- (and (eq (car (nth 1 state)) paren)
- (ruby-deep-indent-paren-p (matching-paren paren)
- (1- (cdr (nth 1 state))))
- (search-backward (char-to-string paren))
- (setq indent (current-column)))))
- ((and (nth 2 state) (> (nth 2 state) 0)) ; in nest
- (if (null (cdr (nth 1 state)))
- (error "invalid nest"))
- (goto-char (cdr (nth 1 state)))
- (forward-word -1) ; skip back a keyword
- (setq begin (point))
- (cond
- ((looking-at "do\\>[^_]") ; iter block is a special case
- (if (nth 3 state) (goto-char (nth 3 state))
- (goto-char parse-start) (back-to-indentation))
- (setq indent (ruby-indent-size (current-column) (nth 2 state))))
- (t
- (setq indent (+ (current-column) ruby-indent-level)))))
-
- ((and (nth 2 state) (< (nth 2 state) 0)) ; in negative nest
- (setq indent (ruby-indent-size (current-column) (nth 2 state)))))
- (when indent
- (goto-char ruby-indent-point)
- (end-of-line)
- (setq eol (point))
- (beginning-of-line)
- (cond
- ((and (not (ruby-deep-indent-paren-p paren
- (and (cdr (nth 1 state))
- (1- (cdr (nth 1 state))))))
- (re-search-forward ruby-negative eol t))
- (and (not (eq ?_ (char-after (match-end 0))))
- (setq indent (- indent ruby-indent-level))))
- ((and
- (save-excursion
- (beginning-of-line)
- (not (bobp)))
- (or (ruby-deep-indent-paren-p t)
- (null (car (nth 1 state)))))
- ;; goto beginning of non-empty no-comment line
- (let (end done)
- (while (not done)
- (skip-chars-backward " \t\n")
- (setq end (point))
- (beginning-of-line)
- (if (re-search-forward "^\\s *#" end t)
- (beginning-of-line)
- (setq done t))))
- (setq bol (point))
- (end-of-line)
- ;; skip the comment at the end
- (skip-chars-backward " \t")
- (let (end (pos (point)))
- (beginning-of-line)
- (while (and (re-search-forward "#" pos t)
- (setq end (1- (point)))
- (or (ruby-special-char-p end)
- (and (setq state (ruby-parse-region parse-start end))
- (nth 0 state))))
- (setq end nil))
- (goto-char (or end pos))
- (skip-chars-backward " \t")
- (setq begin (if (and end (nth 0 state)) pos (cdr (nth 1 state))))
- (setq state (ruby-parse-region parse-start (point))))
- (or (bobp) (forward-char -1))
- (and
- (or (and (looking-at ruby-symbol-re)
- (skip-chars-backward ruby-symbol-chars)
- (looking-at (concat "\\<\\(" ruby-block-hanging-re "\\)\\>"))
- (not (eq (point) (nth 3 state)))
- (save-excursion
- (goto-char (match-end 0))
- (not (looking-at "[a-z_]"))))
- (and (looking-at ruby-operator-re)
- (not (ruby-special-char-p))
- ;; operator at the end of line
- (let ((c (char-after (point))))
- (and
-;; (or (null begin)
-;; (save-excursion
-;; (goto-char begin)
-;; (skip-chars-forward " \t")
-;; (not (or (eolp) (looking-at "#")
-;; (and (eq (car (nth 1 state)) ?{)
-;; (looking-at "|"))))))
- (or (not (eq ?/ c))
- (null (nth 0 (ruby-parse-region (or begin parse-start) (point)))))
- (or (not (eq ?| (char-after (point))))
- (save-excursion
- (or (eolp) (forward-char -1))
- (cond
- ((search-backward "|" nil t)
- (skip-chars-backward " \t\n")
- (and (not (eolp))
- (progn
- (forward-char -1)
- (not (looking-at "{")))
- (progn
- (forward-word -1)
- (not (looking-at "do\\>[^_]")))))
- (t t))))
- (not (eq ?, c))
- (setq op-end t)))))
- (setq indent
- (cond
- ((and
- (null op-end)
- (not (looking-at (concat "\\<\\(" ruby-block-hanging-re "\\)\\>")))
- (eq (ruby-deep-indent-paren-p t) 'space)
- (not (bobp)))
- (widen)
- (goto-char (or begin parse-start))
- (skip-syntax-forward " ")
- (current-column))
- ((car (nth 1 state)) indent)
- (t
- (+ indent ruby-indent-level))))))))
- (goto-char ruby-indent-point)
- (beginning-of-line)
- (skip-syntax-forward " ")
- (if (looking-at "\\.[^.]\\|&\\.")
- (+ indent ruby-indent-level)
- indent))))
-
-(defun ruby-electric-brace (arg)
- (interactive "P")
- (insert-char last-command-event 1)
- (ruby-indent-line t)
- (delete-char -1)
- (self-insert-command (prefix-numeric-value arg)))
-
-(eval-when-compile
- (defmacro defun-region-command (func args &rest body)
- (let ((intr (car body)))
- (when (featurep 'xemacs)
- (if (stringp intr) (setq intr (cadr body)))
- (and (eq (car intr) 'interactive)
- (setq intr (cdr intr))
- (setcar intr (concat "_" (car intr)))))
- (cons 'defun (cons func (cons args body))))))
-
-(defun-region-command ruby-beginning-of-defun (&optional arg)
- "Move backward to next beginning-of-defun.
-With argument, do this that many times.
-Returns t unless search stops due to end of buffer."
- (interactive "p")
- (and (re-search-backward (concat "^\\(" ruby-block-beg-re "\\)\\_>")
- nil 'move (or arg 1))
- (progn (beginning-of-line) t)))
-
-(defun ruby-beginning-of-indent ()
- (and (re-search-backward (concat "^\\(" ruby-indent-beg-re "\\)\\_>")
- nil 'move)
- (progn
- (beginning-of-line)
- t)))
-
-(defun-region-command ruby-end-of-defun (&optional arg)
- "Move forward to next end of defun.
-An end of a defun is found by moving forward from the beginning of one."
- (interactive "p")
- (and (re-search-forward (concat "^\\(" ruby-block-end-re "\\)\\($\\|\\b[^_]\\)")
- nil 'move (or arg 1))
- (progn (beginning-of-line) t))
- (forward-line 1))
-
-(defun ruby-move-to-block (n)
- (let (start pos done down (orig (point)))
- (setq start (ruby-calculate-indent))
- (setq down (looking-at (if (< n 0) ruby-block-end-re
- (concat "\\<\\(" ruby-block-beg-re "\\)\\>"))))
- (while (and (not done) (not (if (< n 0) (bobp) (eobp))))
- (forward-line n)
- (cond
- ((looking-at "^\\s *$"))
- ((looking-at "^\\s *#"))
- ((and (> n 0) (looking-at "^=begin\\>"))
- (re-search-forward "^=end\\>"))
- ((and (< n 0) (looking-at "^=end\\>"))
- (re-search-backward "^=begin\\>"))
- (t
- (setq pos (current-indentation))
- (cond
- ((< start pos)
- (setq down t))
- ((and down (= pos start))
- (setq done t))
- ((> start pos)
- (setq done t)))))
- (if done
- (save-excursion
- (back-to-indentation)
- (if (looking-at (concat "\\<\\(" ruby-block-mid-re "\\)\\>"))
- (setq done nil)))))
- (back-to-indentation)
- (when (< n 0)
- (let ((eol (point-at-eol)) state next)
- (if (< orig eol) (setq eol orig))
- (setq orig (point))
- (while (and (setq next (apply 'ruby-parse-partial eol state))
- (< (point) eol))
- (setq state next))
- (when (cdaadr state)
- (goto-char (cdaadr state)))
- (backward-word)))))
-
-(defun-region-command ruby-beginning-of-block (&optional arg)
- "Move backward to next beginning-of-block"
- (interactive "p")
- (ruby-move-to-block (- (or arg 1))))
-
-(defun-region-command ruby-end-of-block (&optional arg)
- "Move forward to next beginning-of-block"
- (interactive "p")
- (ruby-move-to-block (or arg 1)))
-
-(defun-region-command ruby-forward-sexp (&optional cnt)
- (interactive "p")
- (if (and (numberp cnt) (< cnt 0))
- (ruby-backward-sexp (- cnt))
- (let ((i (or cnt 1)))
- (condition-case nil
- (while (> i 0)
- (skip-syntax-forward " ")
- (if (looking-at ",\\s *") (goto-char (match-end 0)))
- (cond ((looking-at "\\?\\(\\\\[CM]-\\)*\\\\?\\S ")
- (goto-char (match-end 0)))
- ((progn
- (skip-chars-forward ",.:;|&^~=!?\\+\\-\\*")
- (looking-at "\\s("))
- (goto-char (scan-sexps (point) 1)))
- ((and (looking-at (concat "\\<\\(" ruby-block-beg-re "\\)\\>"))
- (not (eq (char-before (point)) ?.))
- (not (eq (char-before (point)) ?:)))
- (ruby-end-of-block)
- (forward-word 1))
- ((looking-at "\\(\\$\\|@@?\\)?\\sw")
- (while (progn
- (while (progn (forward-word 1) (looking-at "_")))
- (cond ((looking-at "::") (forward-char 2) t)
- ((> (skip-chars-forward ".") 0))
- ((looking-at "\\?\\|!\\(=[~=>]\\|[^~=]\\)")
- (forward-char 1) nil)))))
- ((let (state expr)
- (while
- (progn
- (setq expr (or expr (ruby-expr-beg)
- (looking-at "%\\sw?\\Sw\\|[\"'`/]")))
- (nth 1 (setq state (apply 'ruby-parse-partial nil state))))
- (setq expr t)
- (skip-chars-forward "<"))
- (not expr))))
- (setq i (1- i)))
- ((error) (forward-word 1)))
- i)))
-
-(defun-region-command ruby-backward-sexp (&optional cnt)
- (interactive "p")
- (if (and (numberp cnt) (< cnt 0))
- (ruby-forward-sexp (- cnt))
- (let ((i (or cnt 1)))
- (condition-case nil
- (while (> i 0)
- (skip-chars-backward " \t\n,.:;|&^~=!?\\+\\-\\*")
- (forward-char -1)
- (cond ((looking-at "\\s)")
- (goto-char (scan-sexps (1+ (point)) -1))
- (case (char-before)
- (?% (forward-char -1))
- ('(?q ?Q ?w ?W ?r ?x)
- (if (eq (char-before (1- (point))) ?%) (forward-char -2))))
- nil)
- ((looking-at "\\s\"\\|\\\\\\S_")
- (let ((c (char-to-string (char-before (match-end 0)))))
- (while (and (search-backward c)
- (oddp (skip-chars-backward "\\")))))
- nil)
- ((looking-at "\\s.\\|\\s\\")
- (if (ruby-special-char-p) (forward-char -1)))
- ((looking-at "\\s(") nil)
- (t
- (forward-char 1)
- (while (progn (forward-word -1)
- (case (char-before)
- (?_ t)
- (?. (forward-char -1) t)
- ((?$ ?@)
- (forward-char -1)
- (and (eq (char-before) (char-after)) (forward-char -1)))
- (?:
- (forward-char -1)
- (eq (char-before) :)))))
- (if (looking-at ruby-block-end-re)
- (ruby-beginning-of-block))
- nil))
- (setq i (1- i)))
- ((error)))
- i)))
-
-(defun ruby-reindent-then-newline-and-indent ()
- (interactive "*")
- (newline)
- (save-excursion
- (end-of-line 0)
- (indent-according-to-mode)
- (delete-region (point) (progn (skip-chars-backward " \t") (point))))
- (indent-according-to-mode))
-
-(fset 'ruby-encomment-region (symbol-function 'comment-region))
-
-(defun ruby-decomment-region (beg end)
- (interactive "r")
- (save-excursion
- (goto-char beg)
- (while (re-search-forward "^\\([ \t]*\\)#" end t)
- (replace-match "\\1" nil nil)
- (save-excursion
- (ruby-indent-line)))))
-
-(defun ruby-insert-end ()
- (interactive)
- (insert "end")
- (ruby-indent-line t)
- (end-of-line))
-
-(defun ruby-mark-defun ()
- "Put mark at end of this Ruby function, point at beginning."
- (interactive)
- (push-mark (point))
- (ruby-end-of-defun)
- (push-mark (point) nil t)
- (ruby-beginning-of-defun)
- (re-search-backward "^\n" (- (point) 1) t))
-
-(defun ruby-indent-exp (&optional shutup-p)
- "Indent each line in the balanced expression following point syntactically.
-If optional SHUTUP-P is non-nil, no errors are signalled if no
-balanced expression is found."
- (interactive "*P")
- (let ((here (point-marker)) start top column (nest t))
- (set-marker-insertion-type here t)
- (unwind-protect
- (progn
- (beginning-of-line)
- (setq start (point) top (current-indentation))
- (while (and (not (eobp))
- (progn
- (setq column (ruby-calculate-indent start))
- (cond ((> column top)
- (setq nest t))
- ((and (= column top) nest)
- (setq nest nil) t))))
- (ruby-indent-to column)
- (beginning-of-line 2)))
- (goto-char here)
- (set-marker here nil))))
-
-(defun ruby-add-log-current-method ()
- "Return current method string."
- (condition-case nil
- (save-excursion
- (let (mname mlist (indent 0))
- ;; get current method (or class/module)
- (if (re-search-backward
- (concat "^[ \t]*\\(def\\|class\\|module\\)[ \t]+"
- "\\("
- ;; \\. and :: for class method
- "\\([A-Za-z_]" ruby-symbol-re "*\\|\\.\\|::" "\\)"
- "+\\)")
- nil t)
- (progn
- (setq mname (match-string 2))
- (unless (string-equal "def" (match-string 1))
- (setq mlist (list mname) mname nil))
- (goto-char (match-beginning 1))
- (setq indent (current-column))
- (beginning-of-line)))
- ;; nest class/module
- (while (and (> indent 0)
- (re-search-backward
- (concat
- "^[ \t]*\\(class\\|module\\)[ \t]+"
- "\\([A-Z]" ruby-symbol-re "*\\)")
- nil t))
- (goto-char (match-beginning 1))
- (if (< (current-column) indent)
- (progn
- (setq mlist (cons (match-string 2) mlist))
- (setq indent (current-column))
- (beginning-of-line))))
- (when mname
- (let ((mn (split-string mname "\\.\\|::")))
- (if (cdr mn)
- (progn
- (cond
- ((string-equal "" (car mn))
- (setq mn (cdr mn) mlist nil))
- ((string-equal "self" (car mn))
- (setq mn (cdr mn)))
- ((let ((ml (nreverse mlist)))
- (while ml
- (if (string-equal (car ml) (car mn))
- (setq mlist (nreverse (cdr ml)) ml nil))
- (or (setq ml (cdr ml)) (nreverse mlist))))))
- (if mlist
- (setcdr (last mlist) mn)
- (setq mlist mn))
- (setq mn (last mn 2))
- (setq mname (concat "." (cadr mn)))
- (setcdr mn nil))
- (setq mname (concat "#" mname)))))
- ;; generate string
- (if (consp mlist)
- (setq mlist (mapconcat (function identity) mlist "::")))
- (if mname
- (if mlist (concat mlist mname) mname)
- mlist)))))
-
-(defun ruby-brace-to-do-end ()
- (when (looking-at "{")
- (let ((orig (point)) (end (progn (ruby-forward-sexp) (point)))
- oneline (end (make-marker)))
- (setq oneline (and (eolp) (<= (point-at-bol) orig)))
- (when (eq (char-before) ?\})
- (delete-char -1)
- (cond
- (oneline
- (insert "\n")
- (set-marker end (point)))
- ((eq (char-syntax (preceding-char)) ?w)
- (insert " ")))
- (insert "end")
- (if (eq (char-syntax (following-char)) ?w)
- (insert " "))
- (goto-char orig)
- (delete-char 1)
- (if (eq (char-syntax (preceding-char)) ?w)
- (insert " "))
- (insert "do")
- (when (looking-at "\\sw\\||")
- (insert " ")
- (backward-char))
- (when oneline
- (setq orig (point))
- (when (cond
- ((looking-at "\\s *|")
- (goto-char (match-end 0))
- (and (search-forward "|" (point-at-eol) 'move)
- (not (eolp))))
- (t))
- (while (progn
- (insert "\n")
- (ruby-forward-sexp)
- (looking-at "\\s *;\\s *"))
- (delete-char (- (match-end 0) (match-beginning 0))))
- (goto-char orig)
- (beginning-of-line 2)
- (indent-region (point) end))
- (goto-char orig))
- t))))
-
-(defun ruby-do-end-to-brace ()
- (when (and (or (bolp)
- (not (memq (char-syntax (preceding-char)) '(?w ?_))))
- (looking-at "\\<do\\(\\s \\|$\\)"))
- (let ((orig (point)) (end (progn (ruby-forward-sexp) (point)))
- first last)
- (backward-char 3)
- (when (looking-at ruby-block-end-re)
- (delete-char 3)
- (insert "}")
- (setq last (and (eolp)
- (progn (backward-char 1)
- (skip-syntax-backward " ")
- (bolp))
- (1- (point-at-eol -1))))
- (goto-char orig)
- (delete-char 2)
- (insert "{")
- (setq orig (point))
- (when (and last (<= last (point))
- (not (search-forward "#" (setq first (point-at-eol)) t)))
- (goto-char (- end 4))
- (end-of-line 0)
- (if (looking-at "\n\\s *")
- (delete-char (- (match-end 0) (match-beginning 0))) t)
- (goto-char first)
- (if (looking-at "\n\\s *")
- (delete-char (- (match-end 0) (match-beginning 0))) t))
- (goto-char orig)
- (if (looking-at "\\s +|")
- (delete-char (- (match-end 0) (match-beginning 0) 1)))
- t))))
-
-(defun ruby-toggle-block ()
- (interactive)
- (or (ruby-brace-to-do-end)
- (ruby-do-end-to-brace)))
-
-(eval-when-compile
- (if (featurep 'font-lock)
- (defmacro eval-when-font-lock-available (&rest args) (cons 'progn args))
- (defmacro eval-when-font-lock-available (&rest args))))
-
-(eval-when-compile
- (if (featurep 'hilit19)
- (defmacro eval-when-hilit19-available (&rest args) (cons 'progn args))
- (defmacro eval-when-hilit19-available (&rest args))))
-
-(eval-when-font-lock-available
- (or (boundp 'font-lock-variable-name-face)
- (setq font-lock-variable-name-face font-lock-type-face))
-
- (defconst ruby-font-lock-syntactic-keywords
- `(
- ;; #{ }, #$hoge, #@foo are not comments
- ("\\(#\\)[{$@]" 1 (1 . nil))
- ;; the last $', $", $` in the respective string is not variable
- ;; the last ?', ?", ?` in the respective string is not ascii code
- ("\\(^\\|[\[ \t\n<+\(,=]\\)\\(['\"`]\\)\\(\\\\.\\|\\2\\|[^'\"`\n\\\\]\\)*?\\\\?[?$]\\(\\2\\)"
- (2 (7 . nil))
- (4 (7 . nil)))
- ;; $' $" $` .... are variables
- ;; ?' ?" ?` are ascii codes
- ("\\(^\\|[^\\\\]\\)\\(\\\\\\\\\\)*[?$]\\([#\"'`]\\)" 3 (1 . nil))
- ;; regexps
- ("\\(^\\|[[{|=(,~?:;<>]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)"
- (4 (7 . ?/))
- (6 (7 . ?/)))
- ("^\\(=\\)begin\\(\\s \\|$\\)" 1 (7 . nil))
- ("^\\(=\\)end\\(\\s \\|$\\)" 1 (7 . nil))
- (,(concat ruby-here-doc-beg-re ".*\\(\n\\)")
- ,(+ 1 (regexp-opt-depth ruby-here-doc-beg-re))
- (ruby-here-doc-beg-syntax))
- (,ruby-here-doc-end-re 3 (ruby-here-doc-end-syntax))))
-
- (unless (functionp 'syntax-ppss)
- (defun syntax-ppss (&optional pos)
- (parse-partial-sexp (point-min) (or pos (point)))))
-
- (defun ruby-in-ppss-context-p (context &optional ppss)
- (let ((ppss (or ppss (syntax-ppss (point)))))
- (if (cond
- ((eq context 'anything)
- (or (nth 3 ppss)
- (nth 4 ppss)))
- ((eq context 'string)
- (nth 3 ppss))
- ((eq context 'heredoc)
- (and (nth 3 ppss)
- ;; If it's generic string, it's a heredoc and we don't care
- ;; See `parse-partial-sexp'
- (not (numberp (nth 3 ppss)))))
- ((eq context 'non-heredoc)
- (and (ruby-in-ppss-context-p 'anything)
- (not (ruby-in-ppss-context-p 'heredoc))))
- ((eq context 'comment)
- (nth 4 ppss))
- (t
- (error (concat
- "Internal error on `ruby-in-ppss-context-p': "
- "context name `" (symbol-name context) "' is unknown"))))
- t)))
-
- (defun ruby-in-here-doc-p ()
- (save-excursion
- (let ((old-point (point)) (case-fold-search nil))
- (beginning-of-line)
- (catch 'found-beg
- (while (and (re-search-backward ruby-here-doc-beg-re nil t)
- (not (ruby-singleton-class-p)))
- (if (not (or (ruby-in-ppss-context-p 'anything)
- (ruby-here-doc-find-end old-point)))
- (throw 'found-beg t)))))))
-
- (defun ruby-here-doc-find-end (&optional limit)
- "Expects the point to be on a line with one or more heredoc
-openers. Returns the buffer position at which all heredocs on the
-line are terminated, or nil if they aren't terminated before the
-buffer position `limit' or the end of the buffer."
- (save-excursion
- (beginning-of-line)
- (catch 'done
- (let ((eol (save-excursion (end-of-line) (point)))
- (case-fold-search nil)
- ;; Fake match data such that (match-end 0) is at eol
- (end-match-data (progn (looking-at ".*$") (match-data)))
- beg-match-data end-re)
- (while (re-search-forward ruby-here-doc-beg-re eol t)
- (setq beg-match-data (match-data))
- (setq end-re (ruby-here-doc-end-match))
-
- (set-match-data end-match-data)
- (goto-char (match-end 0))
- (unless (re-search-forward end-re limit t) (throw 'done nil))
- (setq end-match-data (match-data))
-
- (set-match-data beg-match-data)
- (goto-char (match-end 0)))
- (set-match-data end-match-data)
- (goto-char (match-end 0))
- (point)))))
-
- (defun ruby-here-doc-beg-syntax ()
- (save-excursion
- (goto-char (match-beginning 0))
- (unless (or (ruby-in-ppss-context-p 'non-heredoc)
- (ruby-in-here-doc-p))
- (string-to-syntax "|"))))
-
- (defun ruby-here-doc-end-syntax ()
- (let ((pss (syntax-ppss)) (case-fold-search nil))
- (when (ruby-in-ppss-context-p 'heredoc pss)
- (save-excursion
- (goto-char (nth 8 pss)) ; Go to the beginning of heredoc.
- (let ((eol (point)))
- (beginning-of-line)
- (if (and (re-search-forward (ruby-here-doc-beg-match) eol t) ; If there is a heredoc that matches this line...
- (not (ruby-in-ppss-context-p 'anything)) ; And that's not inside a heredoc/string/comment...
- (progn (goto-char (match-end 0)) ; And it's the last heredoc on its line...
- (not (re-search-forward ruby-here-doc-beg-re eol t))))
- (string-to-syntax "|")))))))
-
- (eval-when-compile
- (put 'ruby-mode 'font-lock-defaults
- '((ruby-font-lock-keywords)
- nil nil nil
- beginning-of-line
- (font-lock-syntactic-keywords
- . ruby-font-lock-syntactic-keywords))))
-
- (defun ruby-font-lock-docs (limit)
- (if (re-search-forward "^=begin\\(\\s \\|$\\)" limit t)
- (let (beg)
- (beginning-of-line)
- (setq beg (point))
- (forward-line 1)
- (if (re-search-forward "^=end\\(\\s \\|$\\)" limit t)
- (progn
- (set-match-data (list beg (point)))
- t)))))
-
- (defun ruby-font-lock-maybe-docs (limit)
- (let (beg)
- (save-excursion
- (if (and (re-search-backward "^=\\(begin\\|end\\)\\(\\s \\|$\\)" nil t)
- (string= (match-string 1) "begin"))
- (progn
- (beginning-of-line)
- (setq beg (point)))))
- (if (and beg (and (re-search-forward "^=\\(begin\\|end\\)\\(\\s \\|$\\)" nil t)
- (string= (match-string 1) "end")))
- (progn
- (set-match-data (list beg (point)))
- t)
- nil)))
-
- (defvar ruby-font-lock-syntax-table
- (let* ((tbl (copy-syntax-table ruby-mode-syntax-table)))
- (modify-syntax-entry ?_ "w" tbl)
- tbl))
-
- (defconst ruby-font-lock-keywords
- (list
- ;; functions
- '("^\\s *def\\s +\\([^( \t\n]+\\)"
- 1 font-lock-function-name-face)
- ;; keywords
- (cons (concat
- "\\(^\\|[^_:.@$]\\|\\.\\.\\)\\_<\\(defined\\?\\|"
- (regexp-opt
- '("alias"
- "and"
- "begin"
- "break"
- "case"
- "catch"
- "class"
- "def"
- "do"
- "elsif"
- "else"
- "fail"
- "ensure"
- "for"
- "end"
- "if"
- "in"
- "module"
- "next"
- "not"
- "or"
- "raise"
- "redo"
- "rescue"
- "retry"
- "return"
- "then"
- "throw"
- "super"
- "unless"
- "undef"
- "until"
- "when"
- "while"
- "yield"
- )
- t)
- "\\)"
- ruby-keyword-end-re)
- 2)
- ;; here-doc beginnings
- (list ruby-here-doc-beg-re 0 'font-lock-string-face)
- ;; variables
- '("\\(^\\|[^_:.@$]\\|\\.\\.\\)\\_<\\(nil\\|self\\|true\\|false\\)\\>"
- 2 font-lock-variable-name-face)
- ;; variables
- '("\\(\\$\\([^a-zA-Z0-9 \n]\\|[0-9]\\)\\)\\W"
- 1 font-lock-variable-name-face)
- '("\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+"
- 0 font-lock-variable-name-face)
- ;; embedded document
- '(ruby-font-lock-docs
- 0 font-lock-comment-face t)
- '(ruby-font-lock-maybe-docs
- 0 font-lock-comment-face t)
- ;; general delimited string
- '("\\(^\\|[[ \t\n<+(,=]\\)\\(%[xrqQwW]?\\([^<[{(a-zA-Z0-9 \n]\\)[^\n\\\\]*\\(\\\\.[^\n\\\\]*\\)*\\(\\3\\)\\)"
- (2 font-lock-string-face))
- ;; constants
- '("\\(^\\|[^_]\\)\\_<\\([A-Z]+\\(\\w\\|_\\)*\\)"
- 2 font-lock-type-face)
- ;; symbols
- '("\\(^\\|[^:]\\)\\(:\\([-+~]@?\\|[/%&|^`]\\|\\*\\*?\\|<\\(<\\|=>?\\)?\\|>[>=]?\\|===?\\|=~\\|![~=]?\\|\\[\\]=?\\|\\(\\w\\|_\\)+\\([!?=]\\|\\b_*\\)\\|#{[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\)\\)"
- 2 font-lock-reference-face)
- '("\\(^\\s *\\|[\[\{\(,]\\s *\\|\\sw\\s +\\)\\(\\(\\sw\\|_\\)+\\):[^:]" 2 font-lock-reference-face)
- ;; expression expansion
- '("#\\({[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\|\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+\\)"
- 0 font-lock-variable-name-face t)
- ;; warn lower camel case
- ;'("\\<[a-z]+[a-z0-9]*[A-Z][A-Za-z0-9]*\\([!?]?\\|\\>\\)"
- ; 0 font-lock-warning-face)
- )
- "*Additional expressions to highlight in ruby mode."))
-
-(eval-when-hilit19-available
- (hilit-set-mode-patterns
- 'ruby-mode
- '(("[^$\\?]\\(\"[^\\\"]*\\(\\\\\\(.\\|\n\\)[^\\\"]*\\)*\"\\)" 1 string)
- ("[^$\\?]\\('[^\\']*\\(\\\\\\(.\\|\n\\)[^\\']*\\)*'\\)" 1 string)
- ("[^$\\?]\\(`[^\\`]*\\(\\\\\\(.\\|\n\\)[^\\`]*\\)*`\\)" 1 string)
- ("^\\s *#.*$" nil comment)
- ("[^$@?\\]\\(#[^$@{\n].*$\\)" 1 comment)
- ("[^a-zA-Z_]\\(\\?\\(\\\\[CM]-\\)*.\\)" 1 string)
- ("^\\s *\\(require\\|load\\).*$" nil include)
- ("^\\s *\\(include\\|alias\\|undef\\).*$" nil decl)
- ("^\\s *\\<\\(class\\|def\\|module\\)\\>" "[)\n;]" defun)
- ("[^_]\\<\\(begin\\|case\\|else\\|elsif\\|end\\|ensure\\|for\\|if\\|unless\\|rescue\\|then\\|when\\|while\\|until\\|do\\|yield\\)\\>\\([^_]\\|$\\)" 1 defun)
- ("[^_]\\<\\(and\\|break\\|next\\|raise\\|fail\\|in\\|not\\|or\\|redo\\|retry\\|return\\|super\\|yield\\|catch\\|throw\\|self\\|nil\\)\\>\\([^_]\\|$\\)" 1 keyword)
- ("\\$\\(.\\|\\sw+\\)" nil type)
- ("[$@].[a-zA-Z_0-9]*" nil struct)
- ("^__END__" nil label))))
-
-
-;;;###autoload
-(defun ruby-mode ()
- "Major mode for editing ruby scripts.
-\\[ruby-indent-command] properly indents subexpressions of multi-line
-class, module, def, if, while, for, do, and case statements, taking
-nesting into account.
-
-The variable ruby-indent-level controls the amount of indentation.
-\\{ruby-mode-map}"
- (interactive)
- (kill-all-local-variables)
- (use-local-map ruby-mode-map)
- (setq mode-name "Ruby")
- (setq major-mode 'ruby-mode)
- (ruby-mode-variables)
-
- (make-local-variable 'imenu-create-index-function)
- (setq imenu-create-index-function 'ruby-imenu-create-index)
-
- (make-local-variable 'add-log-current-defun-function)
- (setq add-log-current-defun-function 'ruby-add-log-current-method)
-
- (add-hook
- (cond ((boundp 'before-save-hook)
- (make-local-variable 'before-save-hook)
- 'before-save-hook)
- ((boundp 'write-contents-functions) 'write-contents-functions)
- ((boundp 'write-contents-hooks) 'write-contents-hooks))
- 'ruby-mode-set-encoding)
-
- (set (make-local-variable 'font-lock-defaults) '((ruby-font-lock-keywords) nil nil))
- (set (make-local-variable 'font-lock-keywords) ruby-font-lock-keywords)
- (set (make-local-variable 'font-lock-syntax-table) ruby-font-lock-syntax-table)
- (set (make-local-variable 'font-lock-syntactic-keywords) ruby-font-lock-syntactic-keywords)
-
- (if (fboundp 'run-mode-hooks)
- (run-mode-hooks 'ruby-mode-hook)
- (run-hooks 'ruby-mode-hook)))
-
-(provide 'ruby-mode)
diff --git a/misc/ruby-style.el b/misc/ruby-style.el
index b8593b202a..13aad77b3d 100644
--- a/misc/ruby-style.el
+++ b/misc/ruby-style.el
@@ -55,7 +55,7 @@
'("bsd"
(c-basic-offset . 4)
(tab-width . 8)
- (indent-tabs-mode . t)
+ (indent-tabs-mode . nil)
(setq show-trailing-whitespace t)
(c-offsets-alist
(case-label . *)
@@ -75,7 +75,20 @@
(let ((head (progn (forward-line 100) (point)))
(case-fold-search nil))
(goto-char (point-min))
- (re-search-forward "Copyright (C) .* Yukihiro Matsumoto" head t))))
+ (re-search-forward "Copyright (C) .* Yukihiro Matsumoto" head t)))
+ (condition-case ()
+ (with-temp-buffer
+ (when (= 0 (call-process "git" nil t nil "remote" "get-url" "origin"))
+ (goto-char (point-min))
+ (looking-at ".*/ruby\\(\\.git\\)?$")))
+ (error))
+ (condition-case ()
+ (with-temp-buffer
+ (when (= 0 (call-process "svn" nil t nil "info" "--xml"))
+ (goto-char (point-min))
+ (search-forward-regexp "<root>.*/ruby</root>" nil)))
+ (error))
+ nil)
(c-set-style "ruby")))
(provide 'ruby-style)
diff --git a/misc/rubydb2x.el b/misc/rubydb2x.el
deleted file mode 100644
index a74265fb0e..0000000000
--- a/misc/rubydb2x.el
+++ /dev/null
@@ -1,104 +0,0 @@
-(require 'gud)
-(provide 'rubydb)
-
-;; ======================================================================
-;; rubydb functions
-
-;;; History of argument lists passed to rubydb.
-(defvar gud-rubydb-history nil)
-
-(defun gud-rubydb-massage-args (file args)
- (cons "-I" (cons "." (cons "-r" (cons "debug" (cons file args))))))
-
-;; There's no guarantee that Emacs will hand the filter the entire
-;; marker at once; it could be broken up across several strings. We
-;; might even receive a big chunk with several markers in it. If we
-;; receive a chunk of text which looks like it might contain the
-;; beginning of a marker, we save it here between calls to the
-;; filter.
-(defvar gud-rubydb-marker-acc "")
-
-(defun gud-rubydb-marker-filter (string)
- (save-match-data
- (setq gud-marker-acc (concat gud-marker-acc string))
- (let ((output ""))
-
- ;; Process all the complete markers in this chunk.
- (while (string-match "\032\032\\([^:\n]*\\):\\([0-9]*\\):.*\n"
- gud-marker-acc)
- (setq
-
- ;; Extract the frame position from the marker.
- gud-last-frame
- (cons (substring gud-marker-acc (match-beginning 1) (match-end 1))
- (string-to-int (substring gud-marker-acc
- (match-beginning 2)
- (match-end 2))))
-
- ;; Append any text before the marker to the output we're going
- ;; to return - we don't include the marker in this text.
- output (concat output
- (substring gud-marker-acc 0 (match-beginning 0)))
-
- ;; Set the accumulator to the remaining text.
- gud-marker-acc (substring gud-marker-acc (match-end 0))))
-
- ;; Does the remaining text look like it might end with the
- ;; beginning of another marker? If it does, then keep it in
- ;; gud-marker-acc until we receive the rest of it. Since we
- ;; know the full marker regexp above failed, it's pretty simple to
- ;; test for marker starts.
- (if (string-match "\032.*\\'" gud-marker-acc)
- (progn
- ;; Everything before the potential marker start can be output.
- (setq output (concat output (substring gud-marker-acc
- 0 (match-beginning 0))))
-
- ;; Everything after, we save, to combine with later input.
- (setq gud-marker-acc
- (substring gud-marker-acc (match-beginning 0))))
-
- (setq output (concat output gud-marker-acc)
- gud-marker-acc ""))
-
- output)))
-
-(defun gud-rubydb-find-file (f)
- (find-file-noselect f))
-
-(defvar rubydb-command-name "ruby"
- "File name for executing ruby.")
-
-;;;###autoload
-(defun rubydb (command-line)
- "Run rubydb on program FILE in buffer *gud-FILE*.
-The directory containing FILE becomes the initial working directory
-and source-file directory for your debugger."
- (interactive
- (list (read-from-minibuffer "Run rubydb (like this): "
- (if (consp gud-rubydb-history)
- (car gud-rubydb-history)
- (concat rubydb-command-name " "))
- nil nil
- '(gud-rubydb-history . 1))))
-
- (gud-overload-functions '((gud-massage-args . gud-rubydb-massage-args)
- (gud-marker-filter . gud-rubydb-marker-filter)
- (gud-find-file . gud-rubydb-find-file)
- ))
- (gud-common-init command-line)
-
- (gud-def gud-break "b %l" "\C-b" "Set breakpoint at current line.")
-; (gud-def gud-remove "clear %l" "\C-d" "Remove breakpoint at current line")
- (gud-def gud-step "s" "\C-s" "Step one source line with display.")
- (gud-def gud-next "n" "\C-n" "Step one line (skip functions).")
- (gud-def gud-cont "c" "\C-r" "Continue with display.")
- (gud-def gud-finish "finish" "\C-f" "Finish executing current function.")
- (gud-def gud-up "up %p" "<" "Up N stack frames (numeric arg).")
- (gud-def gud-down "down %p" ">" "Down N stack frames (numeric arg).")
- (gud-def gud-print "p %e" "\C-p" "Evaluate ruby expression at point.")
-
- (setq comint-prompt-regexp "^(rdb:-) ")
- (setq paragraph-start comint-prompt-regexp)
- (run-hooks 'rubydb-mode-hook)
- )
diff --git a/misc/rubydb3x.el b/misc/rubydb3x.el
deleted file mode 100644
index 9d6bc57d5a..0000000000
--- a/misc/rubydb3x.el
+++ /dev/null
@@ -1,115 +0,0 @@
-(require 'gud)
-(provide 'rubydb)
-
-;; ======================================================================
-;; rubydb functions
-
-;;; History of argument lists passed to rubydb.
-(defvar gud-rubydb-history nil)
-
-(if (fboundp 'gud-overload-functions)
- (defun gud-rubydb-massage-args (file args)
- (cons "-r" (cons "debug" (cons file args))))
- (defun gud-rubydb-massage-args (file args)
- (cons "-r" (cons "debug" args))))
-
-;; There's no guarantee that Emacs will hand the filter the entire
-;; marker at once; it could be broken up across several strings. We
-;; might even receive a big chunk with several markers in it. If we
-;; receive a chunk of text which looks like it might contain the
-;; beginning of a marker, we save it here between calls to the
-;; filter.
-(defvar gud-rubydb-marker-acc "")
-(make-variable-buffer-local 'gud-rubydb-marker-acc)
-
-(defun gud-rubydb-marker-filter (string)
- (setq gud-rubydb-marker-acc (concat gud-rubydb-marker-acc string))
- (let ((output ""))
-
- ;; Process all the complete markers in this chunk.
- (while (string-match "\032\032\\([^:\n]*\\):\\([0-9]*\\):.*\n"
- gud-rubydb-marker-acc)
- (setq
-
- ;; Extract the frame position from the marker.
- gud-last-frame
- (cons (substring gud-rubydb-marker-acc (match-beginning 1) (match-end 1))
- (string-to-int (substring gud-rubydb-marker-acc
- (match-beginning 2)
- (match-end 2))))
-
- ;; Append any text before the marker to the output we're going
- ;; to return - we don't include the marker in this text.
- output (concat output
- (substring gud-rubydb-marker-acc 0 (match-beginning 0)))
-
- ;; Set the accumulator to the remaining text.
- gud-rubydb-marker-acc (substring gud-rubydb-marker-acc (match-end 0))))
-
- ;; Does the remaining text look like it might end with the
- ;; beginning of another marker? If it does, then keep it in
- ;; gud-rubydb-marker-acc until we receive the rest of it. Since we
- ;; know the full marker regexp above failed, it's pretty simple to
- ;; test for marker starts.
- (if (string-match "\032.*\\'" gud-rubydb-marker-acc)
- (progn
- ;; Everything before the potential marker start can be output.
- (setq output (concat output (substring gud-rubydb-marker-acc
- 0 (match-beginning 0))))
-
- ;; Everything after, we save, to combine with later input.
- (setq gud-rubydb-marker-acc
- (substring gud-rubydb-marker-acc (match-beginning 0))))
-
- (setq output (concat output gud-rubydb-marker-acc)
- gud-rubydb-marker-acc ""))
-
- output))
-
-(defun gud-rubydb-find-file (f)
- (save-excursion
- (let ((buf (find-file-noselect f)))
- (set-buffer buf)
-;; (gud-make-debug-menu)
- buf)))
-
-(defvar rubydb-command-name "ruby"
- "File name for executing ruby.")
-
-;;;###autoload
-(defun rubydb (command-line)
- "Run rubydb on program FILE in buffer *gud-FILE*.
-The directory containing FILE becomes the initial working directory
-and source-file directory for your debugger."
- (interactive
- (list (read-from-minibuffer "Run rubydb (like this): "
- (if (consp gud-rubydb-history)
- (car gud-rubydb-history)
- (concat rubydb-command-name " "))
- nil nil
- '(gud-rubydb-history . 1))))
-
- (if (not (fboundp 'gud-overload-functions))
- (gud-common-init command-line 'gud-rubydb-massage-args
- 'gud-rubydb-marker-filter 'gud-rubydb-find-file)
- (gud-overload-functions '((gud-massage-args . gud-rubydb-massage-args)
- (gud-marker-filter . gud-rubydb-marker-filter)
- (gud-find-file . gud-rubydb-find-file)))
- (gud-common-init command-line rubydb-command-name))
-
- (gud-def gud-break "b %l" "\C-b" "Set breakpoint at current line.")
-; (gud-def gud-remove "clear %l" "\C-d" "Remove breakpoint at current line")
- (gud-def gud-step "s" "\C-s" "Step one source line with display.")
- (gud-def gud-next "n" "\C-n" "Step one line (skip functions).")
- (gud-def gud-cont "c" "\C-r" "Continue with display.")
- (gud-def gud-finish "finish" "\C-f" "Finish executing current function.")
- (gud-def gud-up "up %p" "<" "Up N stack frames (numeric arg).")
- (gud-def gud-down "down %p" ">" "Down N stack frames (numeric arg).")
- (gud-def gud-print "p %e" "\C-p" "Evaluate ruby expression at point.")
-
- (setq comint-prompt-regexp "^(rdb:-) ")
- (if (boundp 'comint-last-output-start)
- (set-marker comint-last-output-start (point)))
- (set (make-local-variable 'paragraph-start) comint-prompt-regexp)
- (run-hooks 'rubydb-mode-hook)
- )
diff --git a/misc/test_lldb_cruby.rb b/misc/test_lldb_cruby.rb
new file mode 100644
index 0000000000..4d1cc499f5
--- /dev/null
+++ b/misc/test_lldb_cruby.rb
@@ -0,0 +1,36 @@
+#!/usr/bin/env ruby
+require 'open3'
+require 'tempfile'
+require 'test/unit'
+
+class TestLLDBInit < Test::Unit::TestCase
+ def assert_rp(expr, pattern, message=nil)
+ Tempfile.create('lldb') do |tf|
+ tf.puts <<eom
+target create ./miniruby
+command script import -r misc/lldb_cruby.py
+b rb_p
+run -e'p #{expr}'
+rp obj
+eom
+ tf.flush
+ o, s = Open3.capture2('lldb', '-b', '-s', tf.path)
+ assert_true s.success?, message
+ assert_match /^\(lldb\) rp obj\n#{pattern}/, o, message
+ end
+ end
+
+ def test_rp_object
+ assert_rp 'Object.new', 'T_OBJECT'
+ end
+
+ def test_rp_symbol
+ assert_rp ':abcde', /immediate\(\h+\)/
+ end
+
+ def test_rp_string
+ assert_rp '"abc"', /\(char \[\d+\]\) ary = "abc"/
+ assert_rp "\"\u3042\"", /\(char \[\d+\]\) ary = "\u3042"/
+ assert_rp '"' + "\u3042"*10 + '"', /\(RString::\(anonymous struct\)\) heap = \{/
+ end
+end
diff --git a/missing/flock.c b/missing/flock.c
index 829f431ddc..71c5e74210 100644
--- a/missing/flock.c
+++ b/missing/flock.c
@@ -2,7 +2,7 @@
#include "ruby/ruby.h"
#if defined _WIN32
-#elif defined HAVE_FCNTL && defined HAVE_FCNTL_H && !defined(__native_client__)
+#elif defined HAVE_FCNTL && defined HAVE_FCNTL_H
/* These are the flock() constants. Since this systems doesn't have
flock(), the values of the constants are probably not available.
diff --git a/missing/nan.c b/missing/nan.c
new file mode 100644
index 0000000000..686c48a336
--- /dev/null
+++ b/missing/nan.c
@@ -0,0 +1,28 @@
+#include "ruby/missing.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+double
+nan(const char *spec)
+{
+#if 0
+ /* FIXME: we have not yet seen any situation this is
+ * necessary. Please write a proper implementation that
+ * covers this branch. */
+ if (spec && spec[0]) {
+ double generated_nan;
+ int len = snprintf(NULL, 0, "NAN(%s)", spec);
+ char *buf = malloc(len + 1); /* +1 for NUL */
+ sprintf(buf, "NAN(%s)", spec);
+ generated_nan = strtod(buf, NULL);
+ free(buf);
+ return generated_nan;
+ }
+ else
+#endif
+ {
+ assert(!spec || !spec[0]);
+ return (double)NAN;
+ }
+}
diff --git a/missing/stdbool.h b/missing/stdbool.h
new file mode 100644
index 0000000000..68c2f3d254
--- /dev/null
+++ b/missing/stdbool.h
@@ -0,0 +1,20 @@
+/*
+ * missing/stdbool.h: Quick alternative of C99 stdbool.h
+ */
+
+#ifndef _MISSING_STDBOOL_H_
+#define _MISSING_STDBOOL_H_
+
+#ifndef __cplusplus
+
+#define bool _Bool
+#define true 1
+#define false 0
+
+#ifndef HAVE__BOOL /* AC_HEADER_STDBOOL in configure.ac */
+typedef int _Bool;
+#endif /* HAVE__BOOL */
+
+#endif /* __cplusplus */
+
+#endif /* _MISSING_STDBOOL_H_ */
diff --git a/missing/strerror.c b/missing/strerror.c
index 907b5aee0d..d3b61c3f12 100644
--- a/missing/strerror.c
+++ b/missing/strerror.c
@@ -13,6 +13,6 @@ strerror(int error)
if (error <= sys_nerr && error > 0) {
return sys_errlist[error];
}
- sprintf(msg, "Unknown error (%d)", error);
+ snprintf(msg, sizeof(msg), "Unknown error (%d)", error);
return msg;
}
diff --git a/missing/strtol.c b/missing/strtol.c
deleted file mode 100644
index 87bd73124c..0000000000
--- a/missing/strtol.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/* public domain rewrite of strtol(3) */
-
-#include "ruby/missing.h"
-#include <ctype.h>
-
-long
-strtol(const char *nptr, char **endptr, int base)
-{
- long result;
- const char *p = nptr;
-
- while (isspace(*p)) {
- p++;
- }
- if (*p == '-') {
- p++;
- result = -strtoul(p, endptr, base);
- }
- else {
- if (*p == '+') p++;
- result = strtoul(p, endptr, base);
- }
- if (endptr != 0 && *endptr == p) {
- *endptr = (char *)nptr;
- }
- return result;
-}
diff --git a/missing/tgamma.c b/missing/tgamma.c
index 5e306fbb43..6260e4f519 100644
--- a/missing/tgamma.c
+++ b/missing/tgamma.c
@@ -10,33 +10,26 @@ reference - Haruhiko Okumura: C-gengo niyoru saishin algorithm jiten
gamma.c -- Gamma function
***********************************************************/
#include "ruby/config.h"
+#include "ruby/missing.h"
#include <math.h>
#include <errno.h>
-#ifdef HAVE_LGAMMA_R
-
-double tgamma(double x)
-{
- int sign;
- double d;
- if (x == 0.0) { /* Pole Error */
- errno = ERANGE;
- return 1/x < 0 ? -HUGE_VAL : HUGE_VAL;
- }
- if (x < 0) {
- static double zero = 0.0;
- double i, f;
- f = modf(-x, &i);
- if (f == 0.0) { /* Domain Error */
- errno = EDOM;
- return zero/zero;
- }
- }
- d = lgamma_r(x, &sign);
- return sign * exp(d);
-}
+#ifdef _WIN32
+# include <float.h>
+# if !defined __MINGW32__ || defined __NO_ISOCEXT
+# ifndef isnan
+# define isnan(x) _isnan(x)
+# endif
+# ifndef isinf
+# define isinf(x) (!_finite(x) && !_isnan(x))
+# endif
+# ifndef finite
+# define finite(x) _finite(x)
+# endif
+# endif
+#endif
-#else
+#ifndef HAVE_LGAMMA_R
#include <errno.h>
#define PI 3.14159265358979324 /* $\pi$ */
@@ -68,25 +61,37 @@ loggamma(double x) /* the natural logarithm of the Gamma function. */
+ (B4 / ( 4 * 3))) * w + (B2 / ( 2 * 1))) / x
+ 0.5 * LOG_2PI - log(v) - x + (x - 0.5) * log(x);
}
+#endif
double tgamma(double x) /* Gamma function */
{
+ int sign;
if (x == 0.0) { /* Pole Error */
errno = ERANGE;
return 1/x < 0 ? -HUGE_VAL : HUGE_VAL;
}
+ if (isinf(x)) {
+ if (x < 0) goto domain_error;
+ return x;
+ }
if (x < 0) {
- int sign;
static double zero = 0.0;
double i, f;
f = modf(-x, &i);
if (f == 0.0) { /* Domain Error */
+ domain_error:
errno = EDOM;
return zero/zero;
}
+#ifndef HAVE_LGAMMA_R
sign = (fmod(i, 2.0) != 0.0) ? 1 : -1;
return sign * PI / (sin(PI * f) * exp(loggamma(1 - x)));
+#endif
}
+#ifndef HAVE_LGAMMA_R
return exp(loggamma(x));
-}
+#else
+ x = lgamma_r(x, &sign);
+ return sign * exp(x);
#endif
+}
diff --git a/mjit.c b/mjit.c
new file mode 100644
index 0000000000..b547277126
--- /dev/null
+++ b/mjit.c
@@ -0,0 +1,887 @@
+/**********************************************************************
+
+ mjit.c - MRI method JIT compiler functions for Ruby's main thread
+
+ Copyright (C) 2017 Vladimir Makarov <vmakarov@redhat.com>.
+
+**********************************************************************/
+
+/* Functions in this file are never executed on MJIT worker thread.
+ So you can safely use Ruby methods and GC in this file. */
+
+/* To share variables privately, include mjit_worker.c instead of linking. */
+
+#include "internal.h"
+
+#if USE_MJIT
+
+#include "mjit_worker.c"
+
+#include "constant.h"
+#include "id_table.h"
+
+/* Copy ISeq's states so that race condition does not happen on compilation. */
+static void
+mjit_copy_job_handler(void *data)
+{
+ mjit_copy_job_t *job = data;
+ const struct rb_iseq_constant_body *body;
+ if (stop_worker_p) { /* check if mutex is still alive, before calling CRITICAL_SECTION_START. */
+ return;
+ }
+
+ CRITICAL_SECTION_START(3, "in mjit_copy_job_handler");
+ /* Make sure that this job is never executed when:
+ 1. job is being modified
+ 2. alloca memory inside job is expired
+ 3. ISeq is GC-ed */
+ if (job->finish_p || job->unit->iseq == NULL) {
+ CRITICAL_SECTION_FINISH(3, "in mjit_copy_job_handler");
+ return;
+ }
+
+ body = job->unit->iseq->body;
+ if (job->cc_entries) {
+ memcpy(job->cc_entries, body->cc_entries, sizeof(struct rb_call_cache) * (body->ci_size + body->ci_kw_size));
+ }
+ if (job->is_entries) {
+ memcpy(job->is_entries, body->is_entries, sizeof(union iseq_inline_storage_entry) * body->is_size);
+ }
+
+ job->finish_p = TRUE;
+ rb_native_cond_broadcast(&mjit_worker_wakeup);
+ CRITICAL_SECTION_FINISH(3, "in mjit_copy_job_handler");
+}
+
+extern int rb_thread_create_mjit_thread(void (*worker_func)(void));
+
+/* Return an unique file name in /tmp with PREFIX and SUFFIX and
+ number ID. Use getpid if ID == 0. The return file name exists
+ until the next function call. */
+static char *
+get_uniq_filename(unsigned long id, const char *prefix, const char *suffix)
+{
+ char buff[70], *str = buff;
+ int size = sprint_uniq_filename(buff, sizeof(buff), id, prefix, suffix);
+ str = 0;
+ ++size;
+ str = xmalloc(size);
+ if (size <= (int)sizeof(buff)) {
+ memcpy(str, buff, size);
+ }
+ else {
+ sprint_uniq_filename(str, size, id, prefix, suffix);
+ }
+ return str;
+}
+
+/* Wait until workers don't compile any iseq. It is called at the
+ start of GC. */
+void
+mjit_gc_start_hook(void)
+{
+ if (!mjit_enabled)
+ return;
+ CRITICAL_SECTION_START(4, "mjit_gc_start_hook");
+ while (in_jit) {
+ verbose(4, "Waiting wakeup from a worker for GC");
+ rb_native_cond_wait(&mjit_client_wakeup, &mjit_engine_mutex);
+ verbose(4, "Getting wakeup from a worker for GC");
+ }
+ in_gc = TRUE;
+ CRITICAL_SECTION_FINISH(4, "mjit_gc_start_hook");
+}
+
+/* Send a signal to workers to continue iseq compilations. It is
+ called at the end of GC. */
+void
+mjit_gc_finish_hook(void)
+{
+ if (!mjit_enabled)
+ return;
+ CRITICAL_SECTION_START(4, "mjit_gc_finish_hook");
+ in_gc = FALSE;
+ verbose(4, "Sending wakeup signal to workers after GC");
+ rb_native_cond_broadcast(&mjit_gc_wakeup);
+ CRITICAL_SECTION_FINISH(4, "mjit_gc_finish_hook");
+}
+
+/* Iseqs can be garbage collected. This function should call when it
+ happens. It removes iseq from the unit. */
+void
+mjit_free_iseq(const rb_iseq_t *iseq)
+{
+ if (!mjit_enabled)
+ return;
+ CRITICAL_SECTION_START(4, "mjit_free_iseq");
+ if (iseq->body->jit_unit) {
+ /* jit_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->jit_unit->iseq = NULL;
+ }
+ CRITICAL_SECTION_FINISH(4, "mjit_free_iseq");
+}
+
+/* Free unit list. This should be called only when worker is finished
+ because node of unit_queue and one of active_units may have the same unit
+ during proceeding unit. */
+static void
+free_list(struct rb_mjit_unit_list *list, int close_handle_p)
+{
+ struct rb_mjit_unit *unit = 0, *next;
+
+ list_for_each_safe(&list->head, unit, next, unode) {
+ list_del(&unit->unode);
+ if (!close_handle_p) unit->handle = NULL; /* Skip dlclose in free_unit() */
+ free_unit(unit);
+ }
+ list->length = 0;
+}
+
+/* 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;
+
+/* Register a new continuation with thread TH. Return MJIT info about
+ the continuation. */
+struct mjit_cont *
+mjit_cont_new(rb_execution_context_t *ec)
+{
+ struct mjit_cont *cont;
+
+ cont = ZALLOC(struct mjit_cont);
+ 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)
+{
+ 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;
+ }
+ CRITICAL_SECTION_FINISH(3, "in mjit_cont_new");
+
+ xfree(cont);
+}
+
+/* Finish work with continuation info. */
+static void
+finish_conts(void)
+{
+ struct mjit_cont *cont, *next;
+
+ for (cont = first_cont; cont != NULL; cont = next) {
+ next = cont->next;
+ xfree(cont);
+ }
+}
+
+/* Create unit for ISEQ. */
+static void
+create_unit(const rb_iseq_t *iseq)
+{
+ struct rb_mjit_unit *unit;
+
+ unit = ZALLOC(struct rb_mjit_unit);
+ if (unit == NULL)
+ return;
+
+ unit->id = current_unit_num++;
+ unit->iseq = iseq;
+ iseq->body->jit_unit = unit;
+}
+
+/* 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->jit_unit) != NULL) {
+ iseq->body->jit_unit->used_code_p = TRUE;
+ }
+
+ if (cfp == ec->cfp)
+ break; /* reached the most recent cfp */
+ }
+}
+
+/* Unload JIT code of some units to satisfy the maximum permitted
+ number of units with a loaded code. */
+static void
+unload_units(void)
+{
+ rb_vm_t *vm = GET_THREAD()->vm;
+ rb_thread_t *th = NULL;
+ struct rb_mjit_unit *unit = 0, *next, *worst;
+ struct mjit_cont *cont;
+ int delete_num, units_num = active_units.length;
+
+ /* For now, we don't unload units when ISeq is GCed. We should
+ unload such ISeqs first here. */
+ 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. */
+ list_for_each(&active_units.head, unit, unode) {
+ assert(unit->iseq != NULL && unit->handle != NULL);
+ unit->used_code_p = FALSE;
+ }
+ list_for_each(&vm->living_threads, th, vmlt_node) {
+ mark_ec_units(th->ec);
+ }
+ for (cont = first_cont; cont != NULL; cont = cont->next) {
+ mark_ec_units(cont->ec);
+ }
+
+ /* Remove 1/10 units more to decrease unloading calls. */
+ /* TODO: Calculate max total_calls in unit_queue and don't unload units
+ whose total_calls are larger than the max. */
+ delete_num = active_units.length / 10;
+ for (; active_units.length > mjit_opts.max_cache_size - delete_num;) {
+ /* Find one unit that has the minimum total_calls. */
+ worst = NULL;
+ list_for_each(&active_units.head, unit, unode) {
+ if (unit->used_code_p) /* We can't unload code on stack. */
+ continue;
+
+ if (worst == NULL || worst->iseq->body->total_calls > unit->iseq->body->total_calls) {
+ worst = unit;
+ }
+ }
+ if (worst == NULL)
+ break;
+
+ /* Unload the worst node. */
+ verbose(2, "Unloading unit %d (calls=%lu)", worst->id, worst->iseq->body->total_calls);
+ assert(worst->handle != NULL);
+ remove_from_list(worst, &active_units);
+ free_unit(worst);
+ }
+
+ if (units_num == active_units.length && mjit_opts.wait) {
+ 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);
+ }
+ else {
+ verbose(1, "Too many JIT code -- %d units unloaded", units_num - active_units.length);
+ }
+}
+
+/* Add ISEQ to be JITed in parallel with the current thread.
+ Unload some JIT codes if there are too many of them. */
+void
+mjit_add_iseq_to_process(const rb_iseq_t *iseq)
+{
+ if (!mjit_enabled || pch_status == PCH_FAILED)
+ return;
+
+ iseq->body->jit_func = (mjit_func_t)NOT_READY_JIT_ISEQ_FUNC;
+ create_unit(iseq);
+ if (iseq->body->jit_unit == NULL)
+ /* Failure in creating the unit. */
+ return;
+
+ CRITICAL_SECTION_START(3, "in add_iseq_to_process");
+ add_to_list(iseq->body->jit_unit, &unit_queue);
+ if (active_units.length >= mjit_opts.max_cache_size) {
+ unload_units();
+ }
+ verbose(3, "Sending wakeup signal to workers in mjit_add_iseq_to_process");
+ rb_native_cond_broadcast(&mjit_worker_wakeup);
+ CRITICAL_SECTION_FINISH(3, "in add_iseq_to_process");
+}
+
+/* For this timeout seconds, --jit-wait will wait for JIT compilation finish. */
+#define MJIT_WAIT_TIMEOUT_SECONDS 60
+
+/* 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
+mjit_wait_call(rb_execution_context_t *ec, struct rb_iseq_constant_body *body)
+{
+ struct timeval tv;
+ int tries = 0;
+ tv.tv_sec = 0;
+ tv.tv_usec = 1000;
+ while (body->jit_func == (mjit_func_t)NOT_READY_JIT_ISEQ_FUNC) {
+ tries++;
+ if (tries / 1000 > MJIT_WAIT_TIMEOUT_SECONDS || pch_status == PCH_FAILED) {
+ CRITICAL_SECTION_START(3, "in mjit_wait_call to set jit_func");
+ body->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC; /* JIT worker seems dead. Give up. */
+ CRITICAL_SECTION_FINISH(3, "in mjit_wait_call to set jit_func");
+ mjit_warning("timed out to wait for JIT finish");
+ break;
+ }
+
+ CRITICAL_SECTION_START(3, "in mjit_wait_call for a client wakeup");
+ rb_native_cond_broadcast(&mjit_worker_wakeup);
+ CRITICAL_SECTION_FINISH(3, "in mjit_wait_call for a client wakeup");
+ rb_thread_wait_for(tv);
+ }
+
+ if ((uintptr_t)body->jit_func <= (uintptr_t)LAST_JIT_ISEQ_FUNC) {
+ return Qundef;
+ }
+ return body->jit_func(ec, ec->cfp);
+}
+
+extern VALUE ruby_archlibdir_path, ruby_prefix_path;
+
+/* Initialize header_file, pch_file, libruby_pathflag. Return TRUE on success. */
+static int
+init_header_filename(void)
+{
+ int fd;
+#ifdef LOAD_RELATIVE
+ /* Root path of the running ruby process. Equal to RbConfig::TOPDIR. */
+ VALUE basedir_val;
+#endif
+ const char *basedir = NULL;
+ 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;
+ basedir = StringValuePtr(basedir_val);
+ baselen = RSTRING_LEN(basedir_val);
+#else
+ if (getenv("MJIT_SEARCH_BUILD_DIR")) {
+ /* This path is not intended to be used on production, but using build directory's
+ header file here because people want to run `make test-all` without running
+ `make install`. Don't use $MJIT_SEARCH_BUILD_DIR except for test-all. */
+
+ struct stat st;
+ const char *hdr = dlsym(RTLD_DEFAULT, "MJIT_HEADER");
+ if (!hdr) {
+ verbose(1, "No MJIT_HEADER");
+ }
+ else if (hdr[0] != '/') {
+ verbose(1, "Non-absolute header file path: %s", hdr);
+ }
+ else if (stat(hdr, &st) || !S_ISREG(st.st_mode)) {
+ verbose(1, "Non-file header file path: %s", hdr);
+ }
+ else if ((st.st_uid != getuid()) || (st.st_mode & 022) ||
+ !rb_path_check(hdr)) {
+ verbose(1, "Unsafe header file: uid=%ld mode=%#o %s",
+ (long)st.st_uid, (unsigned)st.st_mode, hdr);
+ return FALSE;
+ }
+ else {
+ /* Do not pass PRELOADENV to child processes, on
+ * multi-arch environment */
+ verbose(3, "PRELOADENV("PRELOADENV")=%s", getenv(PRELOADENV));
+ /* assume no other PRELOADENV in test-all */
+ unsetenv(PRELOADENV);
+ verbose(3, "MJIT_HEADER: %s", hdr);
+ header_file = ruby_strdup(hdr);
+ if (!header_file) return FALSE;
+ }
+ }
+ 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;
+ const size_t header_name_len = sizeof(header_name) - 1;
+
+ header_file = xmalloc(baselen + header_name_len + 1);
+ p = append_str2(header_file, basedir, baselen);
+ p = append_str2(p, header_name, header_name_len + 1);
+
+ if ((fd = rb_cloexec_open(header_file, O_RDONLY, 0)) < 0) {
+ verbose(1, "Cannot access header file: %s", header_file);
+ xfree(header_file);
+ header_file = NULL;
+ return FALSE;
+ }
+ (void)close(fd);
+ }
+
+ 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;
+}
+
+static enum rb_id_table_iterator_result
+valid_class_serials_add_i(ID key, VALUE v, void *unused)
+{
+ rb_const_entry_t *ce = (rb_const_entry_t *)v;
+ VALUE value = ce->value;
+
+ if (!rb_is_const_id(key)) return ID_TABLE_CONTINUE;
+ if (RB_TYPE_P(value, T_MODULE) || RB_TYPE_P(value, T_CLASS)) {
+ mjit_add_class_serial(RCLASS_SERIAL(value));
+ }
+ return ID_TABLE_CONTINUE;
+}
+
+#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
+ char path[MAXPATHLEN];
+ size_t len = confstr(_CS_DARWIN_USER_TEMP_DIR, path, sizeof(path));
+ if (len > 0) {
+ char *tmpdir = xmalloc(len);
+ if (len > sizeof(path)) {
+ confstr(_CS_DARWIN_USER_TEMP_DIR, tmpdir, len);
+ }
+ else {
+ memcpy(tmpdir, path, len);
+ }
+ return tmpdir;
+ }
+#endif
+ return 0;
+}
+
+static int
+check_tmpdir(const char *dir)
+{
+ struct stat st;
+
+ if (!dir) return FALSE;
+ if (stat(dir, &st)) return FALSE;
+#ifndef S_ISDIR
+# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+ if (!S_ISDIR(st.st_mode)) return FALSE;
+#ifndef _WIN32
+# ifndef S_IWOTH
+# define S_IWOTH 002
+# endif
+ if (st.st_mode & S_IWOTH) {
+# ifdef S_ISVTX
+ if (!(st.st_mode & S_ISVTX)) return FALSE;
+# else
+ return FALSE;
+# endif
+ }
+ if (access(dir, W_OK)) return FALSE;
+#endif
+ return TRUE;
+}
+
+static char *
+system_tmpdir(void)
+{
+ char *tmpdir;
+# define RETURN_ENV(name) \
+ if (check_tmpdir(tmpdir = getenv(name))) return ruby_strdup(tmpdir)
+ RETURN_ENV("TMPDIR");
+ RETURN_ENV("TMP");
+ tmpdir = system_default_tmpdir();
+ if (check_tmpdir(tmpdir)) return tmpdir;
+ return ruby_strdup("/tmp");
+# undef RETURN_ENV
+}
+
+/* Default permitted number of units with a JIT code kept in
+ memory. */
+#define DEFAULT_CACHE_SIZE 1000
+/* A default threshold used to add iseq to JIT. */
+#define DEFAULT_MIN_CALLS_TO_ADD 5
+/* Minimum value for JIT cache size. */
+#define MIN_CACHE_SIZE 10
+
+/* Start MJIT worker. Return TRUE if worker is sucessfully started. */
+static int
+start_worker(void)
+{
+ stop_worker_p = FALSE;
+ worker_stopped = FALSE;
+
+ if (!rb_thread_create_mjit_thread(mjit_worker)) {
+ mjit_enabled = FALSE;
+ 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);
+ verbose(1, "Failure in MJIT thread initialization\n");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Initialize MJIT. Start a thread creating the precompiled header and
+ processing ISeqs. The function should be called first for using MJIT.
+ If everything is successfull, MJIT_INIT_P will be TRUE. */
+void
+mjit_init(struct mjit_options *opts)
+{
+ mjit_opts = *opts;
+ mjit_enabled = TRUE;
+ mjit_call_p = TRUE;
+
+ /* Normalize options */
+ if (mjit_opts.min_calls == 0)
+ mjit_opts.min_calls = DEFAULT_MIN_CALLS_TO_ADD;
+ if (mjit_opts.max_cache_size <= 0)
+ mjit_opts.max_cache_size = DEFAULT_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));
+ memcpy((void *)cc_common_args, CC_COMMON_ARGS, sizeof(CC_COMMON_ARGS));
+#if MJIT_CFLAGS_PIPE
+ { /* eliminate a flag incompatible with `-pipe` */
+ size_t i, j;
+ for (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 */
+ cc_common_args[j] = CC_COMMON_ARGS[i];
+ j++;
+ }
+ }
+#endif
+
+ tmp_dir = system_tmpdir();
+ verbose(2, "MJIT: tmp_dir is %s", tmp_dir);
+
+ if (!init_header_filename()) {
+ mjit_enabled = FALSE;
+ verbose(1, "Failure in MJIT header file name initialization\n");
+ return;
+ }
+ pch_owner_pid = getpid();
+
+ /* 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 root_fiber's saved_ec is scanned by mark_ec_units */
+ rb_fiber_init_mjit_cont(GET_EC()->fiber_ptr);
+
+ /* Initialize class_serials cache for compilation */
+ valid_class_serials = rb_hash_new();
+ rb_obj_hide(valid_class_serials);
+ rb_gc_register_mark_object(valid_class_serials);
+ mjit_add_class_serial(RCLASS_SERIAL(rb_cObject));
+ mjit_add_class_serial(RCLASS_SERIAL(CLASS_OF(rb_vm_top_self())));
+ if (RCLASS_CONST_TBL(rb_cObject)) {
+ rb_id_table_foreach(RCLASS_CONST_TBL(rb_cObject), valid_class_serials_add_i, NULL);
+ }
+
+ /* Initialize worker thread */
+ start_worker();
+}
+
+static void
+stop_worker(void)
+{
+ rb_execution_context_t *ec = GET_EC();
+
+ while (!worker_stopped) {
+ verbose(3, "Sending cancel signal to worker");
+ CRITICAL_SECTION_START(3, "in stop_worker");
+ stop_worker_p = TRUE; /* Setting this inside loop because RUBY_VM_CHECK_INTS may make this FALSE. */
+ rb_native_cond_broadcast(&mjit_worker_wakeup);
+ CRITICAL_SECTION_FINISH(3, "in stop_worker");
+ RUBY_VM_CHECK_INTS(ec);
+ }
+}
+
+/* Stop JIT-compiling methods but compiled code is kept available. */
+VALUE
+mjit_pause(int wait_p)
+{
+ if (!mjit_enabled) {
+ rb_raise(rb_eRuntimeError, "MJIT is not enabled");
+ }
+ if (worker_stopped) {
+ return Qfalse;
+ }
+
+ /* Flush all queued units with no option or `wait: true` */
+ if (wait_p) {
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 1000;
+
+ while (unit_queue.length > 0 && active_units.length < mjit_opts.max_cache_size) { /* inverse of condition that waits for mjit_worker_wakeup */
+ CRITICAL_SECTION_START(3, "in mjit_pause for a worker wakeup");
+ rb_native_cond_broadcast(&mjit_worker_wakeup);
+ CRITICAL_SECTION_FINISH(3, "in mjit_pause for a worker wakeup");
+ rb_thread_wait_for(tv);
+ }
+ }
+
+ stop_worker();
+ return Qtrue;
+}
+
+/* Restart JIT-compiling methods after mjit_pause. */
+VALUE
+mjit_resume(void)
+{
+ if (!mjit_enabled) {
+ rb_raise(rb_eRuntimeError, "MJIT is not enabled");
+ }
+ if (!worker_stopped) {
+ return Qfalse;
+ }
+
+ if (!start_worker()) {
+ rb_raise(rb_eRuntimeError, "Failed to resume MJIT worker");
+ }
+ return Qtrue;
+}
+
+/* Skip calling `clean_object_files` for units which currently exist in the list. */
+static void
+skip_cleaning_object_files(struct rb_mjit_unit_list *list)
+{
+ struct rb_mjit_unit *unit = NULL, *next;
+
+ /* No mutex for list, assuming MJIT worker does not exist yet since it's immediately after fork. */
+ list_for_each_safe(&list->head, unit, next, unode) {
+#ifndef _MSC_VER /* Actually mswin does not reach here since it doesn't have fork */
+ if (unit->o_file) unit->o_file_inherited_p = TRUE;
+#endif
+
+#if defined(_WIN32) /* mswin doesn't reach here either. This is for MinGW. */
+ if (unit->so_file) unit->so_file = NULL;
+#endif
+ }
+}
+
+/* This is called after fork initiated by Ruby's method to launch MJIT worker thread
+ for child Ruby process.
+
+ In multi-process Ruby applications, child Ruby processes do most of the jobs.
+ Thus we want child Ruby processes to enqueue ISeqs to MJIT worker's queue and
+ call the JIT-ed code.
+
+ But unfortunately current MJIT-generated code is process-specific. After the fork,
+ JIT-ed code created by parent Ruby process cannot be used in child Ruby process
+ because the code could rely on inline cache values (ivar's IC, send's CC) which
+ may vary between processes after fork or embed some process-specific addresses.
+
+ So child Ruby process can't request parent process to JIT an ISeq and use the code.
+ Instead of that, MJIT worker thread is created for all child Ruby processes, even
+ while child processes would end up with compiling the same ISeqs.
+ */
+void
+mjit_child_after_fork(void)
+{
+ if (!mjit_enabled)
+ return;
+
+ /* Let parent process delete the already-compiled object files.
+ This must be done before starting MJIT worker on child process. */
+ skip_cleaning_object_files(&active_units);
+
+ /* MJIT worker thread is not inherited on fork. Start it for this child process. */
+ start_worker();
+}
+
+/* Finish the threads processing units and creating PCH, finalize
+ and free MJIT data. It should be called last during MJIT
+ life.
+
+ If close_handle_p is TRUE, it calls dlclose() for JIT-ed code. So it should be FALSE
+ if the code can still be on stack. ...But it means to leak JIT-ed handle forever (FIXME). */
+void
+mjit_finish(int close_handle_p)
+{
+ if (!mjit_enabled)
+ return;
+
+ /* Wait for pch finish */
+ verbose(2, "Stopping worker thread");
+ CRITICAL_SECTION_START(3, "in mjit_finish to wakeup from pch");
+ /* As our threads are detached, we could just cancel them. But it
+ is a bad idea because OS processes (C compiler) started by
+ threads can produce temp files. And even if the temp files are
+ removed, the used C compiler still complaint about their
+ absence. So wait for a clean finish of the threads. */
+ while (pch_status == PCH_NOT_READY) {
+ verbose(3, "Waiting wakeup from make_pch");
+ rb_native_cond_wait(&mjit_pch_wakeup, &mjit_engine_mutex);
+ }
+ CRITICAL_SECTION_FINISH(3, "in mjit_finish to wakeup from pch");
+
+ /* Stop worker */
+ 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);
+
+#ifndef _MSC_VER /* mswin has prebuilt precompiled header */
+ if (!mjit_opts.save_temps && getpid() == pch_owner_pid)
+ remove_file(pch_file);
+
+ xfree(header_file); header_file = NULL;
+#endif
+ xfree((void *)cc_common_args); cc_common_args = NULL;
+ xfree(tmp_dir); tmp_dir = NULL;
+ xfree(pch_file); pch_file = NULL;
+
+ mjit_call_p = FALSE;
+ free_list(&unit_queue, close_handle_p);
+ free_list(&active_units, close_handle_p);
+ free_list(&compact_units, close_handle_p);
+ finish_conts();
+
+ mjit_enabled = FALSE;
+ verbose(1, "Successful MJIT finish");
+}
+
+void
+mjit_mark(void)
+{
+ struct rb_mjit_unit *unit = 0;
+ if (!mjit_enabled)
+ return;
+ RUBY_MARK_ENTER("mjit");
+ CRITICAL_SECTION_START(4, "mjit_mark");
+ list_for_each(&unit_queue.head, unit, unode) {
+ if (unit->iseq) { /* ISeq is still not GCed */
+ VALUE iseq = (VALUE)unit->iseq;
+ CRITICAL_SECTION_FINISH(4, "mjit_mark rb_gc_mark");
+
+ /* Don't wrap critical section with this. This may trigger GC,
+ and in that case mjit_gc_start_hook causes deadlock. */
+ rb_gc_mark(iseq);
+
+ CRITICAL_SECTION_START(4, "mjit_mark rb_gc_mark");
+ }
+ }
+ CRITICAL_SECTION_FINISH(4, "mjit_mark");
+ RUBY_MARK_LEAVE("mjit");
+}
+
+/* A hook to update valid_class_serials. */
+void
+mjit_add_class_serial(rb_serial_t class_serial)
+{
+ if (!mjit_enabled)
+ return;
+
+ /* Do not wrap CRITICAL_SECTION here. This function is only called in main thread
+ and guarded by GVL, and `rb_hash_aset` may cause GC and deadlock in it. */
+ rb_hash_aset(valid_class_serials, LONG2FIX(class_serial), Qtrue);
+}
+
+/* A hook to update valid_class_serials. */
+void
+mjit_remove_class_serial(rb_serial_t class_serial)
+{
+ if (!mjit_enabled)
+ return;
+
+ CRITICAL_SECTION_START(3, "in mjit_remove_class_serial");
+ rb_hash_delete_entry(valid_class_serials, LONG2FIX(class_serial));
+ CRITICAL_SECTION_FINISH(3, "in mjit_remove_class_serial");
+}
+
+#endif
diff --git a/mjit.h b/mjit.h
new file mode 100644
index 0000000000..3aabf514ac
--- /dev/null
+++ b/mjit.h
@@ -0,0 +1,148 @@
+/**********************************************************************
+
+ mjit.h - Interface to MRI method JIT compiler for Ruby's main thread
+
+ Copyright (C) 2017 Vladimir Makarov <vmakarov@redhat.com>.
+
+**********************************************************************/
+
+#ifndef RUBY_MJIT_H
+#define RUBY_MJIT_H 1
+
+#include "ruby.h"
+
+#if USE_MJIT
+
+/* Special address values of a function generated from the
+ corresponding iseq by MJIT: */
+enum rb_mjit_iseq_func {
+ /* ISEQ was not queued yet for the machine code generation */
+ NOT_ADDED_JIT_ISEQ_FUNC = 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,
+ /* 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 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;
+ /* 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;
+ /* Print MJIT warnings to stderr. */
+ char warnings;
+ /* Disable compiler optimization and add debug symbols. It can be
+ very slow. */
+ char debug;
+ /* If not 0, all ISeqs are synchronously compiled. For testing. */
+ unsigned int wait;
+ /* Number of calls to trigger JIT compilation. For testing. */
+ unsigned int min_calls;
+ /* 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;
+};
+
+typedef VALUE (*mjit_func_t)(rb_execution_context_t *, rb_control_frame_t *);
+
+RUBY_SYMBOL_EXPORT_BEGIN
+RUBY_EXTERN struct mjit_options mjit_opts;
+RUBY_EXTERN int mjit_call_p;
+
+extern void mjit_add_iseq_to_process(const rb_iseq_t *iseq);
+extern VALUE mjit_wait_call(rb_execution_context_t *ec, struct rb_iseq_constant_body *body);
+RUBY_SYMBOL_EXPORT_END
+
+extern int mjit_compile(FILE *f, const struct rb_iseq_constant_body *body, const char *funcname, struct rb_call_cache *cc_entries, union iseq_inline_storage_entry *is_entries);
+extern void mjit_init(struct mjit_options *opts);
+extern void mjit_postponed_job_register_start_hook(void);
+extern void mjit_postponed_job_register_finish_hook(void);
+extern void mjit_gc_start_hook(void);
+extern void mjit_gc_finish_hook(void);
+extern void mjit_free_iseq(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_add_class_serial(rb_serial_t class_serial);
+extern void mjit_remove_class_serial(rb_serial_t class_serial);
+
+/* A threshold used to reject long iseqs from JITting as such iseqs
+ takes too much time to be compiled. */
+#define JIT_ISEQ_SIZE_THRESHOLD 1000
+
+/* Return TRUE if given ISeq body should be compiled by MJIT */
+static inline int
+mjit_target_iseq_p(struct rb_iseq_constant_body *body)
+{
+ return (body->type == ISEQ_TYPE_METHOD || body->type == ISEQ_TYPE_BLOCK)
+ && body->iseq_size < JIT_ISEQ_SIZE_THRESHOLD;
+}
+
+/* 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. */
+static inline VALUE
+mjit_exec(rb_execution_context_t *ec)
+{
+ const rb_iseq_t *iseq;
+ struct rb_iseq_constant_body *body;
+ long unsigned total_calls;
+ mjit_func_t func;
+
+ if (!mjit_call_p)
+ return Qundef;
+
+ iseq = ec->cfp->iseq;
+ body = iseq->body;
+ total_calls = ++body->total_calls;
+
+ func = body->jit_func;
+ if (UNLIKELY((uintptr_t)func <= (uintptr_t)LAST_JIT_ISEQ_FUNC)) {
+ switch ((enum rb_mjit_iseq_func)func) {
+ case NOT_ADDED_JIT_ISEQ_FUNC:
+ if (total_calls == mjit_opts.min_calls && mjit_target_iseq_p(body)) {
+ mjit_add_iseq_to_process(iseq);
+ if (UNLIKELY(mjit_opts.wait)) {
+ return mjit_wait_call(ec, body);
+ }
+ }
+ return Qundef;
+ case NOT_READY_JIT_ISEQ_FUNC:
+ case NOT_COMPILED_JIT_ISEQ_FUNC:
+ return Qundef;
+ default: /* to avoid warning with LAST_JIT_ISEQ_FUNC */
+ break;
+ }
+ }
+
+ return func(ec, ec->cfp);
+}
+
+void mjit_child_after_fork(void);
+
+#else /* USE_MJIT */
+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_postponed_job_register_start_hook(void){}
+static inline void mjit_postponed_job_register_finish_hook(void){}
+static inline void mjit_gc_start_hook(void){}
+static inline void mjit_gc_finish_hook(void){}
+static inline void mjit_free_iseq(const rb_iseq_t *iseq){}
+static inline void mjit_mark(void){}
+static inline void mjit_add_class_serial(rb_serial_t class_serial){}
+static inline void mjit_remove_class_serial(rb_serial_t class_serial){}
+static inline VALUE mjit_exec(rb_execution_context_t *ec) { return Qundef; /* unreachable */ }
+static inline void mjit_child_after_fork(void){}
+
+#endif /* USE_MJIT */
+#endif /* RUBY_MJIT_H */
diff --git a/mjit_compile.c b/mjit_compile.c
new file mode 100644
index 0000000000..07e417e75b
--- /dev/null
+++ b/mjit_compile.c
@@ -0,0 +1,254 @@
+/**********************************************************************
+
+ 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 "internal.h"
+
+#if USE_MJIT
+
+#include "vm_core.h"
+#include "vm_exec.h"
+#include "mjit.h"
+#include "insns.inc"
+#include "insns_info.inc"
+#include "vm_insnhelper.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)
+
+/* 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 {
+ int 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. */
+ int local_stack_p;
+ /* Safely-accessible cache entries copied from main thread. */
+ union iseq_inline_storage_entry *is_entries;
+ struct rb_call_cache *cc_entries;
+};
+
+/* 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 */
+ int 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;
+};
+
+/* Returns TRUE if call cache is still not obsoleted and cc->me->def->type is available. */
+static int
+has_valid_method_type(CALL_CACHE cc)
+{
+ extern int mjit_valid_class_serial_p(rb_serial_t class_serial);
+ return GET_GLOBAL_METHOD_STATE() == cc->method_state
+ && mjit_valid_class_serial_p(cc->class_serial) && cc->me;
+}
+
+/* Returns TRUE if iseq is inlinable, 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 int
+inlinable_iseq_p(CALL_INFO ci, CALL_CACHE cc, const rb_iseq_t *iseq)
+{
+ extern int rb_simple_iseq_p(const rb_iseq_t *iseq);
+ return iseq != NULL
+ && rb_simple_iseq_p(iseq) && !(ci->flag & VM_CALL_KW_SPLAT) /* Top of vm_callee_setup_arg. In this case, opt_pc is 0. */
+ && (!IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) && !(METHOD_ENTRY_VISI(cc->me) == METHOD_VISI_PROTECTED)); /* CC_SET_FASTPATH */
+}
+
+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)
+{
+ int insn;
+ 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) {
+#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
+ insn = rb_vm_insn_addr2insn((void *)body->iseq_encoded[pos]);
+#else
+ insn = (int)body->iseq_encoded[pos];
+#endif
+ 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 JIT execution. */
+static void
+compile_cancel_handler(FILE *f, const struct rb_iseq_constant_body *body, struct compile_status *status)
+{
+ unsigned int i;
+ fprintf(f, "\ncancel:\n");
+ if (status->local_stack_p) {
+ for (i = 0; i < body->stack_max; i++) {
+ fprintf(f, " *((VALUE *)reg_cfp->bp + %d) = stack[%d];\n", i + 1, i);
+ }
+ }
+ fprintf(f, " return Qundef;\n");
+}
+
+/* Compile ISeq to C code in F. It returns 1 if it succeeds to compile. */
+int
+mjit_compile(FILE *f, const struct rb_iseq_constant_body *body, const char *funcname, struct rb_call_cache *cc_entries, union iseq_inline_storage_entry *is_entries)
+{
+ struct compile_status status;
+ status.success = TRUE;
+ status.local_stack_p = !body->catch_except_p;
+ status.stack_size_for_pos = (int *)malloc(sizeof(int) * body->iseq_size);
+ if (status.stack_size_for_pos == NULL)
+ return FALSE;
+ memset(status.stack_size_for_pos, NOT_COMPILED_STACK_SIZE, sizeof(int) * body->iseq_size);
+ status.cc_entries = cc_entries;
+ status.is_entries = is_entries;
+
+ /* For performance, we verify stack size only on compilation time (mjit_compile.inc.erb) without --jit-debug */
+ if (!mjit_opts.debug) {
+ fprintf(f, "#undef OPT_CHECKED_RUN\n");
+ fprintf(f, "#define OPT_CHECKED_RUN 0\n\n");
+ }
+
+#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);
+ if (status.local_stack_p) {
+ fprintf(f, " VALUE stack[%d];\n", body->stack_max);
+ }
+ else {
+ fprintf(f, " VALUE *stack = reg_cfp->sp;\n");
+ }
+ fprintf(f, " static const VALUE *const original_body_iseq = (VALUE *)0x%"PRIxVALUE";\n",
+ (VALUE)body->iseq_encoded);
+
+ /* 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 - reg_cfp->iseq->body->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, "\n} /* end of %s */\n", funcname);
+
+ free(status.stack_size_for_pos);
+ return status.success;
+}
+
+#endif /* USE_MJIT */
diff --git a/mjit_worker.c b/mjit_worker.c
new file mode 100644
index 0000000000..d4ff92ed29
--- /dev/null
+++ b/mjit_worker.c
@@ -0,0 +1,1261 @@
+/**********************************************************************
+
+ mjit_worker.c - Worker for MRI method JIT compiler
+
+ Copyright (C) 2017 Vladimir Makarov <vmakarov@redhat.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. */
+
+/* We utilize widely used C compilers (GCC and LLVM Clang) to
+ implement MJIT. We feed them a C code generated from ISEQ. The
+ industrial C compilers are slower than regular JIT engines.
+ Generated code performance of the used C compilers has a higher
+ priority over the compilation speed.
+
+ So our major goal is to minimize the ISEQ compilation time when we
+ use widely optimization level (-O2). It is achieved by
+
+ o Using a precompiled version of the header
+ o Keeping all files in `/tmp`. On modern Linux `/tmp` is a file
+ system in memory. So it is pretty fast
+ o Implementing MJIT as a multi-threaded code because we want to
+ compile ISEQs in parallel with iseq execution to speed up Ruby
+ code execution. MJIT has one thread (*worker*) to do
+ parallel compilations:
+ o It prepares a precompiled code of the minimized header.
+ It starts at the MRI execution start
+ o It generates PIC object files of ISEQs
+ o It takes one JIT unit from a priority queue unless it is empty.
+ o It translates the JIT unit ISEQ into C-code using the precompiled
+ header, calls CC and load PIC code when it is ready
+ o Currently MJIT put ISEQ in the queue when ISEQ is called
+ o MJIT can reorder ISEQs in the queue if some ISEQ has been called
+ many times and its compilation did not start yet
+ o MRI reuses the machine code if it already exists for ISEQ
+ o The machine code we generate can stop and switch to the ISEQ
+ interpretation if some condition is not satisfied as the machine
+ code can be speculative or some exception raises
+ o Speculative machine code can be canceled.
+
+ Here is a diagram showing the MJIT organization:
+
+ _______
+ |header |
+ |_______|
+ | MRI building
+ --------------|----------------------------------------
+ | MRI execution
+ |
+ _____________|_____
+ | | |
+ | ___V__ | CC ____________________
+ | | |----------->| precompiled header |
+ | | | | |____________________|
+ | | | | |
+ | | MJIT | | |
+ | | | | |
+ | | | | ____V___ CC __________
+ | |______|----------->| C code |--->| .so file |
+ | | |________| |__________|
+ | | |
+ | | |
+ | MRI machine code |<-----------------------------
+ |___________________| loading
+
+*/
+
+#ifdef __sun
+#define __EXTENSIONS__ 1
+#endif
+
+#include "vm_core.h"
+#include "mjit.h"
+#include "gc.h"
+#include "ruby_assert.h"
+#include "ruby/debug.h"
+#include "ruby/thread.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>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+#include "dln.h"
+
+#include "ruby/util.h"
+#undef strdup /* ruby_strdup may trigger GC */
+
+#ifndef MAXPATHLEN
+# 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_"
+
+/* The unit structure that holds metadata of ISeq for MJIT. */
+struct rb_mjit_unit {
+ /* Unique order number of unit. */
+ int id;
+ /* Dlopen handle of the loaded object file. */
+ void *handle;
+ const rb_iseq_t *iseq;
+#ifndef _MSC_VER
+ /* This value is always set for `compact_all_jit_code`. Also used for lazy deletion. */
+ char *o_file;
+ /* TRUE if it's inherited from parent Ruby process and lazy deletion should be skipped.
+ `o_file = NULL` can't be used to skip lazy deletion because `o_file` could be used
+ by child for `compact_all_jit_code`. */
+ int o_file_inherited_p;
+#endif
+#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. */
+ char used_code_p;
+ struct list_node unode;
+};
+
+/* Linked list of struct rb_mjit_unit. */
+struct rb_mjit_unit_list {
+ struct 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 rb_pid_t ruby_waitpid_locked(rb_vm_t *, rb_pid_t, int *status, int options, rb_nativethread_cond_t *cond);
+
+/* A copy of MJIT portion of MRI options since MJIT initialization. We
+ need them as MJIT threads still can work when the most MRI data were
+ freed. */
+struct mjit_options mjit_opts;
+
+/* TRUE if MJIT is enabled. */
+int 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. */
+int mjit_call_p = FALSE;
+
+/* Priority queue of iseqs waiting for JIT compilation.
+ This variable is a pointer to head unit of the queue. */
+static struct rb_mjit_unit_list unit_queue = { LIST_HEAD_INIT(unit_queue.head) };
+/* List of units which are successfully compiled. */
+static struct rb_mjit_unit_list active_units = { LIST_HEAD_INIT(active_units.head) };
+/* List of compacted so files which will be cleaned up by `free_list()` in `mjit_finish()`. */
+static struct rb_mjit_unit_list compact_units = { LIST_HEAD_INIT(compact_units.head) };
+/* The number of so far processed ISEQs, used to generate unique id. */
+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;
+/* True when GC is working. */
+static int in_gc;
+/* True when JIT is working. */
+static int in_jit;
+/* Set to TRUE to stop worker. */
+static int stop_worker_p;
+/* Set to TRUE if worker is stopped. */
+static int worker_stopped;
+
+/* Path of "/tmp", which can be changed to $TMP in MinGW. */
+static char *tmp_dir;
+/* Hash like { 1 => true, 2 => true, ... } whose keys are valid `class_serial`s.
+ This is used to invalidate obsoleted CALL_CACHE. */
+static VALUE valid_class_serials;
+
+/* Used C compiler path. */
+static const char *cc_path;
+/* Used C compiler flags. */
+static const char **cc_common_args;
+/* Name of the precompiled header file. */
+static char *pch_file;
+/* The process id which should delete the pch_file on mjit_finish. */
+static rb_pid_t pch_owner_pid;
+/* Status of the precompiled header creation. The status is
+ shared by the workers and the pch thread. */
+static enum {PCH_NOT_READY, PCH_FAILED, PCH_SUCCESS} pch_status;
+
+#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"
+
+#if defined(__GNUC__) && \
+ (!defined(__clang__) || \
+ (defined(__clang__) && (defined(__FreeBSD__) || defined(__GLIBC__))))
+# define GCC_PIC_FLAGS "-Wfatal-errors", "-fPIC", "-shared", "-w", "-pipe",
+# define MJIT_CFLAGS_PIPE 1
+#else
+# define GCC_PIC_FLAGS /* empty */
+# define MJIT_CFLAGS_PIPE 0
+#endif
+
+// Use `-nodefaultlibs -nostdlib` for GCC where possible, which does not work on mingw, 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__)
+# define GCC_NOSTDLIB_FLAGS "-nodefaultlibs", "-nostdlib",
+#else
+# define GCC_NOSTDLIB_FLAGS /* empty */
+#endif
+
+static const char *const CC_COMMON_ARGS[] = {
+ MJIT_CC_COMMON MJIT_CFLAGS GCC_PIC_FLAGS
+ NULL
+};
+
+static const char *const CC_DEBUG_ARGS[] = {MJIT_DEBUGFLAGS NULL};
+static const char *const CC_OPTIMIZE_ARGS[] = {MJIT_OPTFLAGS NULL};
+
+static const char *const CC_LDSHARED_ARGS[] = {MJIT_LDSHARED GCC_PIC_FLAGS NULL};
+static const char *const CC_DLDFLAGS_ARGS[] = {
+ MJIT_DLDFLAGS
+#if defined __GNUC__ && !defined __clang__ && !defined(__OpenBSD__)
+ "-nostartfiles",
+#endif
+ GCC_NOSTDLIB_FLAGS NULL
+};
+
+static const char *const CC_LIBS[] = {
+#if defined(_WIN32) || defined(__CYGWIN__)
+ MJIT_LIBS // mswin, mingw, cygwin
+#endif
+#if defined __GNUC__ && !defined __clang__
+# if defined(_WIN32)
+ "-lmsvcrt", // mingw
+# endif
+ "-lgcc", // mingw, cygwin, and GCC platforms using `-nodefaultlibs -nostdlib`
+#endif
+ NULL
+};
+
+#define CC_CODEFLAG_ARGS (mjit_opts.debug ? CC_DEBUG_ARGS : CC_OPTIMIZE_ARGS)
+
+/* Print the arguments according to FORMAT to stderr only if MJIT
+ verbose option value is more or equal to LEVEL. */
+PRINTF_ARGS(static void, 2, 3)
+verbose(int level, const char *format, ...)
+{
+ if (mjit_opts.verbose >= level) {
+ va_list args;
+ size_t len = strlen(format);
+ char *full_format = alloca(sizeof(char) * (len + 2));
+
+ /* Creating `format + '\n'` to atomically print format and '\n'. */
+ memcpy(full_format, format, len);
+ full_format[len] = '\n';
+ full_format[len+1] = '\0';
+
+ va_start(args, format);
+ vfprintf(stderr, full_format, args);
+ va_end(args);
+ }
+}
+
+PRINTF_ARGS(static void, 1, 2)
+mjit_warning(const char *format, ...)
+{
+ if (mjit_opts.warnings || mjit_opts.verbose) {
+ va_list args;
+
+ fprintf(stderr, "MJIT warning: ");
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ }
+}
+
+/* Add unit node to the tail of doubly linked LIST. It should be not in
+ the list before. */
+static void
+add_to_list(struct rb_mjit_unit *unit, struct rb_mjit_unit_list *list)
+{
+ list_add_tail(&list->head, &unit->unode);
+ list->length++;
+}
+
+static void
+remove_from_list(struct rb_mjit_unit *unit, struct rb_mjit_unit_list *list)
+{
+ list_del(&unit->unode);
+ list->length--;
+}
+
+static void
+remove_file(const char *filename)
+{
+ if (remove(filename)) {
+ mjit_warning("failed to remove \"%s\": %s", filename, strerror(errno));
+ }
+}
+
+/* Lazily delete .o and/or .so files. */
+static void
+clean_object_files(struct rb_mjit_unit *unit)
+{
+#ifndef _MSC_VER
+ if (unit->o_file) {
+ char *o_file = unit->o_file;
+
+ unit->o_file = NULL;
+ /* For compaction, unit->o_file is always set when compilation succeeds.
+ So save_temps needs to be checked here. */
+ if (!mjit_opts.save_temps && !unit->o_file_inherited_p)
+ remove_file(o_file);
+ free(o_file);
+ }
+#endif
+
+#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
+ to prevent the situation that the same methods are continously compiled. */
+static void
+free_unit(struct rb_mjit_unit *unit)
+{
+ if (unit->iseq) { /* ISeq is not GCed */
+ unit->iseq->body->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC;
+ unit->iseq->body->jit_unit = NULL;
+ }
+ 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_object_files(unit);
+ free(unit);
+}
+
+/* Start a critical section. Use message MSG to print debug info at
+ LEVEL. */
+static inline void
+CRITICAL_SECTION_START(int level, const char *msg)
+{
+ verbose(level, "Locking %s", msg);
+ rb_native_mutex_lock(&mjit_engine_mutex);
+ verbose(level, "Locked %s", msg);
+}
+
+/* Finish the current critical section. Use message MSG to print
+ debug info at LEVEL. */
+static inline void
+CRITICAL_SECTION_FINISH(int level, const char *msg)
+{
+ verbose(level, "Unlocked %s", msg);
+ rb_native_mutex_unlock(&mjit_engine_mutex);
+}
+
+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 time in milliseconds as a double. */
+#ifdef __APPLE__
+double ruby_real_ms_time(void);
+# define real_ms_time() ruby_real_ms_time()
+#else
+static double
+real_ms_time(void)
+{
+# ifdef HAVE_CLOCK_GETTIME
+ struct timespec tv;
+# ifdef CLOCK_MONOTONIC
+ const clockid_t c = CLOCK_MONOTONIC;
+# else
+ const clockid_t c = CLOCK_REALTIME;
+# endif
+
+ clock_gettime(c, &tv);
+ return tv.tv_nsec / 1000000.0 + tv.tv_sec * 1000.0;
+# else
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return tv.tv_usec / 1000.0 + tv.tv_sec * 1000.0;
+# endif
+}
+#endif
+
+/* Return TRUE if class_serial is not obsoleted. This is used by mjit_compile.c. */
+int
+mjit_valid_class_serial_p(rb_serial_t class_serial)
+{
+ int found_p;
+
+ CRITICAL_SECTION_START(3, "in valid_class_serial_p");
+ found_p = rb_hash_stlike_lookup(valid_class_serials, LONG2FIX(class_serial), NULL);
+ CRITICAL_SECTION_FINISH(3, "in valid_class_serial_p");
+ return found_p;
+}
+
+/* Return the best unit from list. The best is the first
+ high priority unit or the unit whose iseq has the biggest number
+ of calls so far. */
+static struct rb_mjit_unit *
+get_from_list(struct rb_mjit_unit_list *list)
+{
+ struct rb_mjit_unit *unit = NULL, *next, *best = NULL;
+
+ /* Find iseq with max total_calls */
+ list_for_each_safe(&list->head, unit, next, unode) {
+ if (unit->iseq == NULL) { /* ISeq is GCed. */
+ remove_from_list(unit, list);
+ free_unit(unit);
+ continue;
+ }
+
+ if (best == NULL || best->iseq->body->total_calls < unit->iseq->body->total_calls) {
+ best = unit;
+ }
+ }
+ if (best) {
+ remove_from_list(best, list);
+ }
+ return best;
+}
+
+/* Return length of NULL-terminated array ARGS excluding the NULL
+ marker. */
+static size_t
+args_len(char *const *args)
+{
+ size_t i;
+
+ for (i = 0; (args[i]) != NULL;i++)
+ ;
+ return i;
+}
+
+/* Concatenate NUM passed NULL-terminated arrays of strings, put the
+ result (with NULL end marker) into the heap, and return the
+ result. */
+static char **
+form_args(int num, ...)
+{
+ va_list argp;
+ size_t len, n;
+ int i;
+ char **args, **res, **tmp;
+
+ va_start(argp, num);
+ res = NULL;
+ for (i = len = 0; i < num; i++) {
+ args = va_arg(argp, char **);
+ n = args_len(args);
+ if ((tmp = (char **)realloc(res, sizeof(char *) * (len + n + 1))) == NULL) {
+ free(res);
+ return NULL;
+ }
+ res = tmp;
+ MEMCPY(res + len, args, char *, n + 1);
+ len += n;
+ }
+ va_end(argp);
+ return res;
+}
+
+COMPILER_WARNING_PUSH
+#ifdef __GNUC__
+COMPILER_WARNING_IGNORED(-Wdeprecated-declarations)
+#endif
+/* Start an OS process of absolute executable path with arguments ARGV.
+ Return PID of the process. */
+static pid_t
+start_process(const char *abspath, char *const *argv)
+{
+ pid_t pid;
+ /*
+ * Not calling non-async-signal-safe functions between vfork
+ * and execv for safety
+ */
+ int dev_null = rb_cloexec_open(ruby_null_device, O_WRONLY, 0);
+
+ if (mjit_opts.verbose >= 2) {
+ int i;
+ const char *arg;
+
+ fprintf(stderr, "Starting process: %s", abspath);
+ for (i = 0; (arg = argv[i]) != NULL; i++)
+ fprintf(stderr, " %s", arg);
+ fprintf(stderr, "\n");
+ }
+#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) {
+ /* CC can be started in a thread using a file which has been
+ already removed while MJIT is finishing. Discard the
+ messages about missing files. */
+ dup2(dev_null, STDERR_FILENO);
+ dup2(dev_null, STDOUT_FILENO);
+ }
+ (void)close(dev_null);
+ pid = execv(abspath, argv); /* Pid will be negative on an error */
+ /* Even if we successfully found CC to compile PCH we still can
+ fail with loading the CC in very rare cases for some reasons.
+ Stop the forked process in this case. */
+ verbose(1, "MJIT: Error in execv: %s", abspath);
+ _exit(1);
+ }
+#endif
+ (void)close(dev_null);
+ return pid;
+}
+COMPILER_WARNING_POP
+
+/* Execute an OS process of executable PATH with arguments ARGV.
+ Return -1 or -2 if failed to execute, otherwise exit code of the process.
+ TODO: Use a similar function in process.c */
+static int
+exec_process(const char *path, char *const argv[])
+{
+ int stat, exit_code = -2;
+ pid_t pid;
+ rb_vm_t *vm = WAITPID_USE_SIGCHLD ? GET_VM() : 0;
+ rb_nativethread_cond_t cond;
+
+ if (vm) {
+ rb_native_cond_initialize(&cond);
+ rb_native_mutex_lock(&vm->waitpid_lock);
+ }
+
+ pid = start_process(path, argv);
+ for (;pid > 0;) {
+ pid_t r = vm ? ruby_waitpid_locked(vm, pid, &stat, 0, &cond)
+ : waitpid(pid, &stat, 0);
+ if (r == -1) {
+ if (errno == EINTR) continue;
+ fprintf(stderr, "[%"PRI_PIDT_PREFIX"d] waitpid(%lu): %s (SIGCHLD=%d,%u)\n",
+ getpid(), (unsigned long)pid, strerror(errno),
+ RUBY_SIGCHLD, SIGCHLD_LOSSY);
+ break;
+ }
+ else if (r == pid) {
+ if (WIFEXITED(stat)) {
+ exit_code = WEXITSTATUS(stat);
+ break;
+ } else if (WIFSIGNALED(stat)) {
+ exit_code = -1;
+ break;
+ }
+ }
+ }
+
+ if (vm) {
+ rb_native_mutex_unlock(&vm->waitpid_lock);
+ rb_native_cond_destroy(&cond);
+ }
+ return exit_code;
+}
+
+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_object_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
+}
+
+#define append_str2(p, str, len) ((char *)memcpy((p), str, (len))+(len))
+#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 1 if it succeeds. (mswin) */
+static int
+compile_c_to_so(const char *c_file, const char *so_file)
+{
+ int exit_code;
+ const char *files[] = { NULL, NULL, NULL, NULL, NULL, NULL, "-link", libruby_pathflag, NULL };
+ char **args;
+ char *p, *obj_file;
+
+ /* 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));
+ 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" */
+ files[5] = p = alloca(sizeof(char) * (rb_strlen_lit("-Fd") + strlen(pch_file) + 1));
+ p = append_lit(p, "-Fd");
+ p = append_str2(p, pch_file, strlen(pch_file) - rb_strlen_lit(".pch"));
+ p = append_lit(p, ".pdb");
+ *p = '\0';
+
+ args = form_args(5, CC_LDSHARED_ARGS, CC_CODEFLAG_ARGS,
+ files, CC_LIBS, CC_DLDFLAGS_ARGS);
+ if (args == NULL)
+ return FALSE;
+
+ 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)
+{
+ int exit_code;
+ const char *rest_args[] = {
+# ifdef __clang__
+ "-emit-pch",
+# endif
+ // -nodefaultlibs is a linker flag, but it may affect cc1 behavior on Gentoo, which should NOT be changed on pch:
+ // https://gitweb.gentoo.org/proj/gcc-patches.git/tree/7.3.0/gentoo/13_all_default-ssp-fix.patch
+ GCC_NOSTDLIB_FLAGS
+ "-o", NULL, NULL,
+ NULL,
+ };
+ char **args;
+ int len = sizeof(rest_args) / sizeof(const char *);
+
+ rest_args[len - 2] = header_file;
+ rest_args[len - 3] = pch_file;
+ verbose(2, "Creating precompiled header");
+ args = form_args(3, cc_common_args, CC_CODEFLAG_ARGS, rest_args);
+ if (args == NULL) {
+ mjit_warning("making precompiled header failed on forming args");
+ CRITICAL_SECTION_START(3, "in make_pch");
+ pch_status = PCH_FAILED;
+ CRITICAL_SECTION_FINISH(3, "in make_pch");
+ return;
+ }
+
+ exit_code = exec_process(cc_path, args);
+ free(args);
+
+ CRITICAL_SECTION_START(3, "in make_pch");
+ if (exit_code == 0) {
+ pch_status = PCH_SUCCESS;
+ }
+ else {
+ mjit_warning("Making precompiled header failed on compilation. Stopping MJIT worker...");
+ pch_status = PCH_FAILED;
+ }
+ /* wakeup `mjit_finish` */
+ rb_native_cond_broadcast(&mjit_pch_wakeup);
+ CRITICAL_SECTION_FINISH(3, "in make_pch");
+}
+
+/* Compile .c file to .o file. It returns 1 if it succeeds. (non-mswin) */
+static int
+compile_c_to_o(const char *c_file, const char *o_file)
+{
+ int exit_code;
+ const char *files[] = {
+ "-o", NULL, NULL,
+# ifdef __clang__
+ "-include-pch", NULL,
+# endif
+ "-c", NULL
+ };
+ char **args;
+
+ files[1] = o_file;
+ files[2] = c_file;
+# ifdef __clang__
+ files[4] = pch_file;
+# endif
+ args = form_args(5, cc_common_args, CC_CODEFLAG_ARGS, files, CC_LIBS, CC_DLDFLAGS_ARGS);
+ if (args == NULL)
+ return FALSE;
+
+ exit_code = exec_process(cc_path, args);
+ free(args);
+
+ if (exit_code != 0)
+ verbose(2, "compile_c_to_o: compile error: %d", exit_code);
+ return exit_code == 0;
+}
+
+/* Link .o files to .so file. It returns 1 if it succeeds. (non-mswin) */
+static int
+link_o_to_so(const char **o_files, const char *so_file)
+{
+ int exit_code;
+ const char *options[] = {
+ "-o", NULL,
+# ifdef _WIN32
+ libruby_pathflag,
+# endif
+ NULL
+ };
+ char **args;
+
+ options[1] = so_file;
+ args = form_args(6, CC_LDSHARED_ARGS, CC_CODEFLAG_ARGS,
+ options, o_files, CC_LIBS, CC_DLDFLAGS_ARGS);
+ if (args == NULL)
+ return FALSE;
+
+ exit_code = exec_process(cc_path, args);
+ free(args);
+
+ if (exit_code != 0)
+ verbose(2, "link_o_to_so: link error: %d", exit_code);
+ return exit_code == 0;
+}
+
+/* Link all cached .o files and build a .so file. Reload all JIT func from it. This
+ allows to avoid JIT code fragmentation and improve performance to call JIT-ed code. */
+static void
+compact_all_jit_code(void)
+{
+# ifndef _WIN32 /* This requires header transformation but we don't transform header on Windows for now */
+ struct rb_mjit_unit *unit, *cur = 0;
+ double start_time, end_time;
+ static const char so_ext[] = DLEXT;
+ char so_file[MAXPATHLEN];
+ const char **o_files;
+ int i = 0, success;
+
+ /* Abnormal use case of rb_mjit_unit that doesn't have ISeq */
+ unit = calloc(1, sizeof(struct rb_mjit_unit)); /* To prevent GC, don't use ZALLOC */
+ if (unit == NULL) return;
+ unit->id = current_unit_num++;
+ sprint_uniq_filename(so_file, (int)sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
+
+ /* NULL-ending for form_args */
+ o_files = alloca(sizeof(char *) * (active_units.length + 1));
+ o_files[active_units.length] = NULL;
+ CRITICAL_SECTION_START(3, "in compact_all_jit_code to keep .o files");
+ list_for_each(&active_units.head, cur, unode) {
+ o_files[i] = cur->o_file;
+ i++;
+ }
+
+ start_time = real_ms_time();
+ success = link_o_to_so(o_files, so_file);
+ end_time = real_ms_time();
+
+ /* TODO: Shrink this big critical section. For now, this is needed to prevent failure by missing .o files.
+ This assumes that o -> so link doesn't take long time because the bottleneck, which is compiler optimization,
+ is already done. But actually it takes about 500ms for 5,000 methods on my Linux machine, so it's better to
+ finish this critical section before link_o_to_so by disabling unload_units. */
+ CRITICAL_SECTION_FINISH(3, "in compact_all_jit_code to keep .o files");
+
+ if (success) {
+ 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);
+ return;
+ }
+ unit->handle = handle;
+
+ /* lazily dlclose handle (and .so file for win32) on `mjit_finish()`. */
+ add_to_list(unit, &compact_units);
+
+ if (!mjit_opts.save_temps)
+ remove_so_file(so_file, unit);
+
+ CRITICAL_SECTION_START(3, "in compact_all_jit_code to read list");
+ list_for_each(&active_units.head, cur, unode) {
+ void *func;
+ char funcname[35]; /* TODO: reconsider `35` */
+ sprintf(funcname, "_mjit%d", cur->id);
+
+ if ((func = dlsym(handle, funcname)) == NULL) {
+ mjit_warning("skipping to reload '%s' from '%s': %s", funcname, so_file, dlerror());
+ continue;
+ }
+
+ if (cur->iseq) { /* Check whether GCed or not */
+ /* Usage of jit_code might be not in a critical section. */
+ MJIT_ATOMIC_SET(cur->iseq->body->jit_func, (mjit_func_t)func);
+ }
+ }
+ CRITICAL_SECTION_FINISH(3, "in compact_all_jit_code to read list");
+ verbose(1, "JIT compaction (%.1fms): Compacted %d methods -> %s", end_time - start_time, active_units.length, so_file);
+ }
+ else {
+ free(unit);
+ verbose(1, "JIT compaction failure (%.1fms): Failed to compact methods", end_time - start_time);
+ }
+# endif /* _WIN32 */
+}
+
+#endif /* _MSC_VER */
+
+static void *
+load_func_from_so(const char *so_file, const char *funcname, struct rb_mjit_unit *unit)
+{
+ void *handle, *func;
+
+ handle = dlopen(so_file, RTLD_NOW);
+ if (handle == NULL) {
+ mjit_warning("failure in loading code from '%s': %s", so_file, dlerror());
+ return (void *)NOT_ADDED_JIT_ISEQ_FUNC;
+ }
+
+ func = dlsym(handle, funcname);
+ unit->handle = handle;
+ return func;
+}
+
+static void
+print_jit_result(const char *result, const struct rb_mjit_unit *unit, const double duration, const char *c_file)
+{
+ verbose(1, "JIT %s (%.1fms): %s@%s:%d -> %s", result,
+ duration, RSTRING_PTR(unit->iseq->body->location.label),
+ RSTRING_PTR(rb_iseq_path(unit->iseq)), FIX2INT(unit->iseq->body->location.first_lineno), c_file);
+}
+
+#ifndef __clang__
+static const char *
+header_name_end(const char *s)
+{
+ const char *e = s + strlen(s);
+# ifdef __GNUC__ /* don't chomp .pch for mswin */
+ static const char suffix[] = ".gch";
+
+ /* chomp .gch suffix */
+ if (e > s+sizeof(suffix)-1 && strcmp(e-sizeof(suffix)+1, suffix) == 0) {
+ e -= sizeof(suffix)-1;
+ }
+# endif
+ return e;
+}
+#endif
+
+/* Print platform-specific prerequisites in generated code. */
+static void
+compile_prelude(FILE *f)
+{
+#ifndef __clang__ /* -include-pch is used for Clang */
+ const char *s = pch_file;
+ const char *e = header_name_end(s);
+
+ fprintf(f, "#include \"");
+ /* print pch_file except .gch for gcc, but keep .pch for mswin */
+ for (; s < e; s++) {
+ switch(*s) {
+ case '\\': case '"':
+ fputc('\\', f);
+ }
+ fputc(*s, 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 mjit_func_t
+convert_unit_to_func(struct rb_mjit_unit *unit, struct rb_call_cache *cc_entries, union iseq_inline_storage_entry *is_entries)
+{
+ char c_file_buff[MAXPATHLEN], *c_file = c_file_buff, *so_file, funcname[35]; /* TODO: reconsider `35` */
+ int success;
+ int fd;
+ FILE *f;
+ void *func;
+ double start_time, end_time;
+ int c_file_len = (int)sizeof(c_file_buff);
+ static const char c_ext[] = ".c";
+ static const char so_ext[] = DLEXT;
+ const int access_mode =
+#ifdef O_BINARY
+ O_BINARY|
+#endif
+ O_WRONLY|O_EXCL|O_CREAT;
+#ifndef _MSC_VER
+ static const char o_ext[] = ".o";
+ char *o_file;
+#endif
+
+ c_file_len = sprint_uniq_filename(c_file_buff, c_file_len, unit->id, MJIT_TMP_PREFIX, c_ext);
+ if (c_file_len >= (int)sizeof(c_file_buff)) {
+ ++c_file_len;
+ c_file = alloca(c_file_len);
+ c_file_len = sprint_uniq_filename(c_file, c_file_len, unit->id, MJIT_TMP_PREFIX, c_ext);
+ }
+ ++c_file_len;
+
+#ifndef _MSC_VER
+ o_file = alloca(c_file_len - sizeof(c_ext) + sizeof(o_ext));
+ memcpy(o_file, c_file, c_file_len - sizeof(c_ext));
+ memcpy(&o_file[c_file_len - sizeof(c_ext)], o_ext, sizeof(o_ext));
+#endif
+ so_file = alloca(c_file_len - sizeof(c_ext) + sizeof(so_ext));
+ memcpy(so_file, c_file, c_file_len - sizeof(c_ext));
+ memcpy(&so_file[c_file_len - sizeof(c_ext)], so_ext, sizeof(so_ext));
+
+ sprintf(funcname, "_mjit%d", unit->id);
+
+ fd = rb_cloexec_open(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);
+
+ /* wait until mjit_gc_finish_hook is called */
+ CRITICAL_SECTION_START(3, "before mjit_compile to wait GC finish");
+ while (in_gc) {
+ verbose(3, "Waiting wakeup from GC");
+ rb_native_cond_wait(&mjit_gc_wakeup, &mjit_engine_mutex);
+ }
+
+ /* We need to check again here because we could've waited on GC above */
+ if (unit->iseq == NULL) {
+ fclose(f);
+ if (!mjit_opts.save_temps)
+ remove_file(c_file);
+ free_unit(unit);
+ in_jit = FALSE; /* just being explicit for return */
+ }
+ else {
+ in_jit = TRUE;
+ }
+ CRITICAL_SECTION_FINISH(3, "before mjit_compile to wait GC finish");
+ if (!in_jit) {
+ return (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC;
+ }
+
+ {
+ VALUE s = rb_iseq_path(unit->iseq);
+ const char *label = RSTRING_PTR(unit->iseq->body->location.label);
+ const char *path = RSTRING_PTR(s);
+ int lineno = FIX2INT(unit->iseq->body->location.first_lineno);
+ verbose(2, "start compilation: %s@%s:%d -> %s", label, path, lineno, c_file);
+ fprintf(f, "/* %s@%s:%d */\n\n", label, path, lineno);
+ }
+ success = mjit_compile(f, unit->iseq->body, funcname, cc_entries, is_entries);
+
+ /* release blocking mjit_gc_start_hook */
+ CRITICAL_SECTION_START(3, "after mjit_compile to wakeup client for GC");
+ in_jit = FALSE;
+ verbose(3, "Sending wakeup signal to client in a mjit-worker for GC");
+ rb_native_cond_signal(&mjit_client_wakeup);
+ CRITICAL_SECTION_FINISH(3, "in worker to wakeup client for GC");
+
+ fclose(f);
+ if (!success) {
+ if (!mjit_opts.save_temps)
+ remove_file(c_file);
+ print_jit_result("failure", unit, 0, c_file);
+ return (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC;
+ }
+
+ start_time = real_ms_time();
+#ifdef _MSC_VER
+ success = compile_c_to_so(c_file, so_file);
+#else
+ /* splitting .c -> .o step and .o -> .so step, to cache .o files in the future */
+ if ((success = compile_c_to_o(c_file, o_file)) != 0) {
+ const char *o_files[2] = { NULL, NULL };
+ o_files[0] = o_file;
+ success = link_o_to_so(o_files, so_file);
+
+ /* Always set o_file for compaction. The value is also used for lazy deletion. */
+ unit->o_file = strdup(o_file);
+ if (unit->o_file == NULL) {
+ mjit_warning("failed to allocate memory to remember '%s' (%s), removing it...", o_file, strerror(errno));
+ remove_file(o_file);
+ }
+ }
+#endif
+ end_time = real_ms_time();
+
+ if (!mjit_opts.save_temps)
+ remove_file(c_file);
+ if (!success) {
+ verbose(2, "Failed to generate so: %s", so_file);
+ return (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC;
+ }
+
+ 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) {
+ CRITICAL_SECTION_START(3, "end of jit");
+ add_to_list(unit, &active_units);
+ if (unit->iseq)
+ print_jit_result("success", unit, end_time - start_time, c_file);
+ CRITICAL_SECTION_FINISH(3, "end of jit");
+ }
+ return (mjit_func_t)func;
+}
+
+typedef struct {
+ struct rb_mjit_unit *unit;
+ struct rb_call_cache *cc_entries;
+ union iseq_inline_storage_entry *is_entries;
+ int finish_p;
+} mjit_copy_job_t;
+
+/* Singleton MJIT copy job. This is made global since it needs to be durable even when MJIT worker thread is stopped.
+ (ex: register job -> MJIT pause -> MJIT resume -> dispatch job. Actually this should be just cancelled by finish_p check) */
+static mjit_copy_job_t mjit_copy_job;
+
+static void mjit_copy_job_handler(void *data);
+
+/* vm_trace.c */
+int rb_workqueue_register(unsigned flags, rb_postponed_job_func_t , void *);
+
+/* We're lazily copying cache values from main thread because these cache values
+ could be different between ones on enqueue timing and ones on dequeue timing.
+ Return TRUE if copy succeeds. */
+static int
+copy_cache_from_main_thread(mjit_copy_job_t *job)
+{
+ CRITICAL_SECTION_START(3, "in copy_cache_from_main_thread");
+ job->finish_p = FALSE; /* allow dispatching this job in mjit_copy_job_handler */
+ CRITICAL_SECTION_FINISH(3, "in copy_cache_from_main_thread");
+
+ if (UNLIKELY(mjit_opts.wait)) {
+ mjit_copy_job_handler((void *)job);
+ return job->finish_p;
+ }
+
+ if (!rb_workqueue_register(0, mjit_copy_job_handler, (void *)job))
+ return FALSE;
+ CRITICAL_SECTION_START(3, "in MJIT copy job wait");
+ /* checking `stop_worker_p` too because `RUBY_VM_CHECK_INTS(ec)` may not
+ lush mjit_copy_job_handler when EC_EXEC_TAG() is not TAG_NONE, and then
+ `stop_worker()` could dead lock with this function. */
+ while (!job->finish_p && !stop_worker_p) {
+ rb_native_cond_wait(&mjit_worker_wakeup, &mjit_engine_mutex);
+ verbose(3, "Getting wakeup from client");
+ }
+ CRITICAL_SECTION_FINISH(3, "in MJIT copy job wait");
+ return job->finish_p;
+}
+
+/* The function implementing a worker. It is executed in a separate
+ thread by rb_thread_create_mjit_thread. It compiles precompiled header
+ and then compiles requested ISeqs. */
+void
+mjit_worker(void)
+{
+ mjit_copy_job_t *job = &mjit_copy_job; /* just a shorthand */
+
+#ifndef _MSC_VER
+ if (pch_status == PCH_NOT_READY) {
+ make_pch();
+ }
+#endif
+ if (pch_status == PCH_FAILED) {
+ mjit_enabled = FALSE;
+ CRITICAL_SECTION_START(3, "in worker to update worker_stopped");
+ worker_stopped = TRUE;
+ verbose(3, "Sending wakeup signal to client in a mjit-worker");
+ rb_native_cond_signal(&mjit_client_wakeup);
+ CRITICAL_SECTION_FINISH(3, "in worker to update worker_stopped");
+ return; /* TODO: do the same thing in the latter half of mjit_finish */
+ }
+
+ /* main worker loop */
+ while (!stop_worker_p) {
+ struct rb_mjit_unit *unit;
+
+ /* wait until unit is available */
+ CRITICAL_SECTION_START(3, "in worker dequeue");
+ while ((list_empty(&unit_queue.head) || active_units.length >= mjit_opts.max_cache_size) && !stop_worker_p) {
+ rb_native_cond_wait(&mjit_worker_wakeup, &mjit_engine_mutex);
+ verbose(3, "Getting wakeup from client");
+ }
+ unit = get_from_list(&unit_queue);
+ job->finish_p = TRUE; /* disable dispatching this job in mjit_copy_job_handler while it's being modified */
+ CRITICAL_SECTION_FINISH(3, "in worker dequeue");
+
+ if (unit) {
+ mjit_func_t func;
+ const struct rb_iseq_constant_body *body = unit->iseq->body;
+
+ job->unit = unit;
+ job->cc_entries = NULL;
+ if (body->ci_size > 0 || body->ci_kw_size > 0)
+ job->cc_entries = alloca(sizeof(struct rb_call_cache) * (body->ci_size + body->ci_kw_size));
+ job->is_entries = NULL;
+ if (body->is_size > 0)
+ job->is_entries = alloca(sizeof(union iseq_inline_storage_entry) * body->is_size);
+
+ /* Copy ISeq's inline caches values to avoid race condition. */
+ if (job->cc_entries != NULL || job->is_entries != NULL) {
+ if (copy_cache_from_main_thread(job) == FALSE) {
+ continue; /* retry postponed_job failure, or stop worker */
+ }
+ }
+
+ /* JIT compile */
+ func = convert_unit_to_func(unit, job->cc_entries, job->is_entries);
+
+ CRITICAL_SECTION_START(3, "in jit func replace");
+ while (in_gc) { /* Make sure we're not GC-ing when touching ISeq */
+ verbose(3, "Waiting wakeup from GC");
+ rb_native_cond_wait(&mjit_gc_wakeup, &mjit_engine_mutex);
+ }
+ if (unit->iseq) { /* Check whether GCed or not */
+ /* Usage of jit_code might be not in a critical section. */
+ MJIT_ATOMIC_SET(unit->iseq->body->jit_func, func);
+ }
+ CRITICAL_SECTION_FINISH(3, "in jit func replace");
+
+#ifndef _MSC_VER
+ /* Combine .o files to one .so and reload all jit_func to improve memory locality */
+ if ((!mjit_opts.wait && unit_queue.length == 0 && active_units.length > 1)
+ || active_units.length == mjit_opts.max_cache_size) {
+ compact_all_jit_code();
+ }
+#endif
+ }
+ }
+
+ /* Disable dispatching this job in mjit_copy_job_handler while memory allocated by alloca
+ could be expired after finishing this function. */
+ job->finish_p = TRUE;
+
+ /* To keep mutex unlocked when it is destroyed by mjit_finish, don't wrap CRITICAL_SECTION here. */
+ worker_stopped = TRUE;
+}
diff --git a/nacl/GNUmakefile.in b/nacl/GNUmakefile.in
deleted file mode 100644
index 1fffcee898..0000000000
--- a/nacl/GNUmakefile.in
+++ /dev/null
@@ -1,100 +0,0 @@
-# Copyright 2012 Google Inc. All Rights Reserved.
-# Author: yugui@google.com (Yugui Sonoda)
-
-include Makefile
--include uncommon.mk
-
-NACL_SDK_ROOT=@NACL_SDK_ROOT@
-NACL_TOOLCHAIN=@NACL_TOOLCHAIN@
-NACL_TOOLCHAIN_DIR=$(NACL_SDK_ROOT)/toolchain/$(NACL_TOOLCHAIN)
-CXX=@CXX@
-
-# Don't override CC/LD/etc if they are already set to absolute
-# paths (this is the case when building in the naclports tree).
-ifeq ($(dir $(CC)),./)
-CC:=$(NACL_TOOLCHAIN_DIR)/bin/$(CC)
-endif
-ifeq ($(dir $(CXX)),./)
-CXX:=$(NACL_TOOLCHAIN_DIR)/bin/$(CXX)
-endif
-ifeq ($(dir $(LD)),./)
-LD:=$(NACL_TOOLCHAIN_DIR)/bin/$(LD)
-endif
-ifeq ($(dir $(NM)),./)
-NM:=$(NACL_TOOLCHAIN_DIR)/bin/$(NM)
-endif
-ifeq ($(dir $(AR)),./)
-AR:=$(NACL_TOOLCHAIN_DIR)/bin/$(AR)
-endif
-ifeq ($(dir $(AS)),./)
-AS:=$(NACL_TOOLCHAIN_DIR)/bin/$(AS)
-endif
-ifeq ($(dir $(RANLIB)),./)
-RANLIB:=$(NACL_TOOLCHAIN_DIR)/bin/$(RANLIB)
-endif
-ifeq ($(dir $(OBJDUMP)),./)
-OBJDUMP:=$(NACL_TOOLCHAIN_DIR)/bin/$(OBJDUMP)
-endif
-ifeq ($(dir $(OBJCOPY)),./)
-OBJCOPY:=$(NACL_TOOLCHAIN_DIR)/bin/$(OBJCOPY)
-endif
-PYTHON=@PYTHON@
-
-PPROGRAM=pepper-$(PROGRAM)
-PEPPER_LIBS=-lppapi -lnacl_io
-PROGRAM_NMF=$(PROGRAM:$(EXEEXT)=.nmf)
-PPROGRAM_NMF=$(PPROGRAM:$(EXEEXT)=.nmf)
-
-GNUmakefile: $(srcdir)/nacl/GNUmakefile.in
-$(PPROGRAM): $(PROGRAM) pepper_main.$(OBJEXT)
- $(Q)$(MAKE) $(MFLAGS) PROGRAM=$(PPROGRAM) MAINOBJ="pepper_main.$(OBJEXT)" LIBS="$(LIBS) $(PEPPER_LIBS)" CC="$(CXX)" program
-$(PROGRAM_NMF) $(PPROGRAM_NMF): $(@:.nmf=$(EXEEXT)) nacl/create_nmf.rb
-
-.PHONY: pprogram package show_naclflags
-.SUFFIXES: $(EXEEXT) .nmf
-$(EXEEXT).nmf:
- $(ECHO) generating manifest $@
- $(Q)$(MINIRUBY) $(srcdir)/nacl/create_nmf.rb --verbose=$(V) $(@:.nmf=$(EXEEXT)) $@
-
-pepper_main.$(OBJEXT): $(srcdir)/nacl/pepper_main.c
- @$(ECHO) compiling nacl/pepper_main.c
- $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(srcdir)/nacl/pepper_main.c
-
-.rbconfig.time:
- @$(MAKE) .rbconfig.raw.time RBCONFIG=.rbconfig.raw.time
- @sed \
- -e 's!CONFIG\["CC"\] = .*!CONFIG\["CC"\] = "$(CC)"!' \
- -e 's!CONFIG\["LD"\] = .*!CONFIG\["LD"\] = "$(LD)"!' \
- -e 's!CONFIG\["NM"\] = .*!CONFIG\["NM"\] = "$(NM)"!' \
- -e 's!CONFIG\["AR"\] = .*!CONFIG\["AR"\] = "$(AR)"!' \
- -e 's!CONFIG\["AS"\] = .*!CONFIG\["AS"\] = "$(AS)"!' \
- -e 's!CONFIG\["RANLIB"\] = .*!CONFIG\["RANLIB"\] = "$(RANLIB)"!' \
- -e 's!CONFIG\["OBJDUMP"\] = .*!CONFIG\["OBJDUMP"\] = "$(OBJDUMP)"!' \
- -e 's!CONFIG\["OBJCOPY"\] = .*!CONFIG\["OBJCOPY"\] = "$(OBJCOPY)"!' \
- -i.bak rbconfig.rb
- @touch .rbconfig.time
-
-all: pprogram
-main: $(PROGRAM_NMF)
-pprogram: showflags $(PPROGRAM) $(PPROGRAM_NMF)
-program: $(PROGRAM_NMF)
-prog: pprogram
-
-package: pprogram install-lib install-ext-comm install-ext-arch
- $(INSTALL_DATA) $(srcdir)/nacl/example.html $(prefix)
- $(ECHO) generating manifest $@
- $(Q)$(MINIRUBY) $(srcdir)/nacl/package.rb $(prefix)
-
-showflags: show_naclflags
-
-show_naclflags:
- @echo " NACL_SDK_ROOT = $(NACL_SDK_ROOT)"
- @echo " NM = $(NM)"
- @echo " AR = $(AR)"
- @echo " AS = $(AS)"
- @echo " RANLIB = $(RANLIB)"
- @echo " OBJDUMP = $(OBJDUMP)"
- @echo " OBJCOPY = $(OBJCOPY)"
-
-clean-local::
- -$(RM) $(PPROGRAM) pepper_main.$(OBJEXT) $(PROGRAM_NMF) $(PPRGORAM_NMF)
diff --git a/nacl/README.nacl b/nacl/README.nacl
deleted file mode 100644
index 77140e0f75..0000000000
--- a/nacl/README.nacl
+++ /dev/null
@@ -1,51 +0,0 @@
-=begin
-= Native Client port of Ruby
-
-= How to build
-== Prerequisites
-You need to install the following things before building NaCl port of Ruby.
-* Ruby 1.9.3 or later
-* Python 2.6 or later
-* NativeClient SDK pepper 37 or later
-* GNU make
-
-== Steps
-(1) Extract all files from the tarball:
- $ tar xzf ruby-X.Y.Z.tar.gz
-(2) Set NACL_SDK_ROOT environment variable to the path to the Native Client SDK you installed:
- $ export NACL_SDK_ROOT=/home/yugui/src/nacl_sdk/pepper_37
-(3) Configure
- $ ./configure --prefix=/tmp/nacl-ruby --host=x86_64-nacl --with-baseruby=/path/to/ruby-1.9.3-or-later
-(4) Make
- $ make
- $ make package
-
-Now you have ruby.nexe. You can run it by sel_ldr in NaCl SDK.
-
-"make package" installs "pepper-ruby.nexe", an example Pepper application that
-embeds Ruby", and libraries to $prefix. You can run it by the following steps:
-(5) Publish the $prefix directory on a web server
-(6) Visit the example.html on the web server by a browser that implements Pepper 18 or later.
- -- e.g., Chrome 18 implements Pepper 18, Chrome 19 implements Pepper 19, ...
-
-=== Example Configurations
-* x86_32 Native Client
- $ ./configure --prefix=/tmp/nacl-ruby \
- --host=i686-nacl \
- --with-baseruby=/path/to/ruby-1.9.3-or-later
-* arm Native Client
- $ ./configure --prefix=/tmp/nacl-ruby \
- --host=arm-nacl \
- --with-newlib \
- --with-baseruby=/path/to/ruby-1.9.3-or-later
-* Portable Native Client
- $ ./configure --prefix=/tmp/nacl-ruby \
- --host=le32-nacl \
- --with-newlib \
- --with-static-linked-ext \
- --with-baseruby=/path/to/ruby-1.9.3-or-later
-
-= Copyright
-* Copyright 2012 Google Inc. All Rights Reserved.
-* Author: yugui@google.com (Yugui Sonoda)
-=end
diff --git a/nacl/create_nmf.rb b/nacl/create_nmf.rb
deleted file mode 100644
index cdfe7ad239..0000000000
--- a/nacl/create_nmf.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/ruby
-# Copyright:: Copyright 2012 Google Inc.
-# License:: All Rights Reserved.
-# Original Author:: Yugui Sonoda (mailto:yugui@google.com)
-#
-# Wrapper for create_nmf.py / generate_nmf.py
-
-require File.join(File.dirname(__FILE__), 'nacl-config')
-
-include NaClConfig
-$verbosity = 0
-
-def usage_and_exit
- $stderr.puts "Usage: #{$PROGRAM_NAME} [--verbose=N] path/to/input.nexe path/to/output.nmf"
- exit false
-end
-
-def create_dynamically_linked(nmf, exe)
- cmd = [
- PYTHON, CREATE_NMF,
- '-o', nmf,
- '-D', OBJDUMP,
- '-L', HOST_LIB,
- exe
- ]
- puts cmd.join(' ') if $verbosity > 0
- exec(*cmd)
-end
-
-def create_statically_linked(nmf, exe)
- File.open(nmf, "w") {|f|
- f.write <<-EOS.gsub(/^ {6}/, '')
- {
- "program": {
- "#{ARCH}": {
- "url": "#{exe}"
- }
- }
- }
- EOS
- }
-end
-
-def main
- while m = ARGV.first.match(/--([a-z-]+)(?:=(\S+))?/)
- case m[1]
- when 'verbose'
- usage_and_exit unless m[2][/\A[0-9]+\z/]
- $verbosity = m[2].to_i
- when 'help'
- usage_end_exit
- end
- ARGV.shift
- end
-
- usage_and_exit if ARGV.size < 2
-
- exe, nmf = ARGV[0], ARGV[1]
- if newlib?
- create_statically_linked(nmf, exe)
- else
- create_dynamically_linked(nmf, exe)
- end
-end
-
-if __FILE__ == $0
- main()
-end
-
-
diff --git a/nacl/dirent.h b/nacl/dirent.h
deleted file mode 100644
index 31bdad31b7..0000000000
--- a/nacl/dirent.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright 2011 Google Inc. All Rights Reserved.
- * Author: yugui@google.com (Yugui Sonoda)
- */
-#ifndef RUBY_NACL_DIRENT_H
-#define RUBY_NACL_DIRENT_H
-
-/* NaCl SDK 0.3 has implementations of dir functions but no declaration in
- * dirent.h */
-int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
-void rewinddir(DIR *dirp);
-long telldir(DIR *dirp);
-void seekdir(DIR *dirp, long offset);
-
-#endif
diff --git a/nacl/example.html b/nacl/example.html
deleted file mode 100644
index 3cc33298dd..0000000000
--- a/nacl/example.html
+++ /dev/null
@@ -1,150 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
- <title>Ruby Example</title>
-
- <script type="text/javascript">
- RubyModule = null; // Global application object.
- statusText = 'NO-STATUS';
- rubyReady = false;
-
- // Indicate load success.
- function moduleDidLoad() {
- RubyModule = document.getElementById('ruby');
- form = document.getElementById('source-form');
- form.style.display = "block";
- updateStatus('SUCCESS');
- }
-
- function evalSource() {
- if (rubyReady) {
- RubyModule.postMessage('eval:' + document.getElementById("source").value);
- } else {
- throw "Not yet ready";
- }
- return false;
- }
-
- function RubyError(message) {
- this.message = message;
- this.toString = function() {
- return message;
- }
- }
-
- function FatalError(message) {
- this.message = message;
- }
-
- // The 'message' event handler. This handler is fired when the NaCl module
- // posts a message to the browser by calling PPB_Messaging.PostMessage()
- // (in C) or pp::Instance.PostMessage() (in C++). This implementation
- // simply displays the content of the message in an alert panel.
- function handleMessage(message_event) {
- var raw = message_event.data;
- var components;
- if (raw.indexOf("error") == 0) {
- components = raw.split(":", 2);
- throw new RubyError(components[1]);
- } else if (raw.indexOf("return") == 0) {
- components = raw.split(":", 2);
- document.getElementById("result").value = components[1];
- } else if (raw == "rubyReady") {
- rubyReady = true;
- } else {
- throw new FatalError(raw);
- }
- }
-
- // If the page loads before the Native Client module loads, then set the
- // status message indicating that the module is still loading. Otherwise,
- // do not change the status message.
- function pageDidLoad() {
- if (RubyModule == null) {
- updateStatus('LOADING...');
- } else {
- // It's possible that the Native Client module onload event fired
- // before the page's onload event. In this case, the status message
- // will reflect 'SUCCESS', but won't be displayed. This call will
- // display the current message.
- updateStatus();
- }
- }
-
- // Set the global status message. If the element with id 'statusField'
- // exists, then set its HTML to the status message as well.
- // opt_message The message test. If this is null or undefined, then
- // attempt to set the element with id 'statusField' to the value of
- // |statusText|.
- function updateStatus(opt_message) {
- if (opt_message)
- statusText = opt_message;
- var statusField = document.getElementById('status_field');
- if (statusField) {
- statusField.innerHTML = statusText;
- }
- }
- </script>
-</head>
-<body onload="pageDidLoad()">
-
-<h1>Native Client Module Ruby</h1>
-<p>
- <!-- Load the published .nexe. This includes the 'nacl' attribute which
- shows how to load multi-architecture modules. Each entry in the "nexes"
- object in the .nmf manifest file is a key-value pair: the key is the
- instruction set architecture ('x86-32', 'x86-64', etc.); the value is a URL
- for the desired NaCl module.
- To load the debug versions of your .nexes, set the 'nacl' attribute to the
- _dbg.nmf version of the manifest file.
-
- Note: Since this NaCl module does not use any real-estate in the browser,
- it's width and height are set to 0.
-
- Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
- and a 'message' event listener attached. This wrapping method is used
- instead of attaching the event listeners directly to the <EMBED> element to
- ensure that the listeners are active before the NaCl module 'load' event
- fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
- pp::Instance.PostMessage() (in C++) from within the initialization code in
- your NaCl module.
- -->
- <div id="listener">
- <script type="text/javascript">
- var listener = document.getElementById('listener');
- listener.addEventListener('load', moduleDidLoad, true);
- listener.addEventListener('message', handleMessage, true);
- </script>
-
- <embed name="nacl_module"
- id="ruby"
- width="0" height="0"
- src="ruby.nmf"
- type="application/x-nacl" />
- <form id="source-form" action="#" method="post" style="display:none"
- onsubmit="evalSource(); return false">
- <table>
- <tbody>
- <tr>
- <th>Source</th>
- <td>
- <textarea rows="10" cols="80" id="source"></textarea>
- <input type="submit" />
- </td>
- </tr>
- <tr>
- <th>Result</th>
- <td>
- <textarea rows="10" cols="80" id="result"></textarea>
- </td>
- </tr>
- </tbody>
- </table>
- </form>
- </div>
-</p>
-
-<h2>Status</h2>
-<div id="status_field">NO-STATUS</div>
-</body>
-</html>
diff --git a/nacl/nacl-config.rb b/nacl/nacl-config.rb
deleted file mode 100755
index 66481301f5..0000000000
--- a/nacl/nacl-config.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/ruby
-#
-# Copyright:: Copyright 2012 Google Inc.
-# License:: All Rights Reserved.
-# Original Author:: Yugui Sonoda (mailto:yugui@google.com)
-#
-# Convenient functions/constants for native client specific configurations.
-require 'rbconfig'
-
-module NaClConfig
- config = RbConfig::CONFIG
-
- cpu_nick = config['host_alias'].sub(/-gnu$|-newlib$/, '').sub(/-nacl$/, '').sub(/i.86/, 'x86_32')
- ARCH = cpu_nick.sub('x86_64', 'x86-64').sub('x86_32', 'x86-32')
- HOST = ARCH.sub(/x86-../, 'x86_64') + '-nacl'
-
- lib_suffix = config['host_cpu'][/i.86/] ? '32' : ''
- PYTHON = config['PYTHON']
- OBJDUMP = config['OBJDUMP']
- SDK_ROOT = config['NACL_SDK_ROOT']
- CREATE_NMF = [
- File.join(SDK_ROOT, 'build_tools', 'nacl_sdk_scons', 'site_tools', 'create_nmf.py'),
- File.join(SDK_ROOT, 'tools', 'create_nmf.py')
- ].find{|path| File.exist?(path) } or raise "No create_nmf found"
- HOST_LIB = File.join(SDK_ROOT, 'toolchain', config['NACL_TOOLCHAIN'], HOST, "lib#{lib_suffix}")
-
- NACL_LIB = File.join(SDK_ROOT, 'lib', config['NACL_LIB_PATH'], 'Release')
-
- INSTALL_PROGRAM = config['INSTALL_PROGRAM']
- INSTALL_LIBRARY = config['INSTALL_DATA']
-
- if cpu_nick == 'x86_64' or cpu_nick == 'x86_32'
- SEL_LDR = File.join(SDK_ROOT, 'tools', "sel_ldr_#{cpu_nick}")
- IRT_CORE = File.join(SDK_ROOT, 'tools', "irt_core_#{cpu_nick}.nexe")
- raise "No sel_ldr found" if not File.executable?(SEL_LDR)
- raise "No irt_core found" if not File.exists?(IRT_CORE)
- end
- RUNNABLE_LD = File.join(HOST_LIB, 'runnable-ld.so')
-
- module_function
-
- def newlib?
- RbConfig::CONFIG['NACL_SDK_VARIANT'] == 'newlib'
- end
-
- def self.config(name)
- if NaClConfig::const_defined?(name.upcase)
- NaClConfig::const_get(name.upcase)
- elsif NaClConfig::respond_to?(name) and NaClConfig::method(name).arity == 0
- NaClConfig::send(name)
- else
- raise ArgumentError, "No such config: #{name}"
- end
- end
-end
-
-if $0 == __FILE__
- ARGV.each do |arg|
- puts NaClConfig::config(arg)
- end
-end
diff --git a/nacl/package.rb b/nacl/package.rb
deleted file mode 100644
index 9ba95888c4..0000000000
--- a/nacl/package.rb
+++ /dev/null
@@ -1,113 +0,0 @@
-#!/usr/bin/ruby
-# Copyright:: Copyright 2012 Google Inc.
-# License:: All Rights Reserved.
-# Original Author:: Yugui Sonoda (mailto:yugui@google.com)
-#
-# Generates a runnable package of the pepper API example.
-
-require File.join(File.dirname(__FILE__), 'nacl-config')
-require 'json'
-require 'find'
-require 'fileutils'
-
-include NaClConfig
-
-class Installation
- include NaClConfig
-
- SRC_DIRS = [
- Dir.pwd,
- HOST_LIB,
- NACL_LIB,
- ]
-
- def initialize(destdir)
- @destdir = destdir
- @manifest = {
- "files" => {}
- }
- ruby_libs.each do |path|
- raise "Collision of #{path}" if @manifest['files'].key? path
- @manifest['files'][path] = {
- ARCH => {
- "url" => path
- }
- }
- if path[/\.so$/]
- alternate_path = path.gsub('/', "_")
- raise "Collision of #{alternate_path}" if @manifest['files'].key? alternate_path
- @manifest['files'][alternate_path] = {
- ARCH => {
- "url" => path
- }
- }
- end
- end
- end
-
- def manifest
- @manifest.dup
- end
-
- def install_program(basename)
- do_install_binary(basename, File.join(@destdir, "bin", ARCH))
- @manifest["program"] = {
- ARCH => {
- "url" => File.join("bin", ARCH, basename)
- }
- }
- end
-
- def install_library(name, basename)
- do_install_binary(basename, File.join(@destdir, "lib", ARCH))
- @manifest["files"][name] = {
- ARCH => {
- "url" => File.join("lib", ARCH, basename)
- }
- }
- end
-
- private
- def do_install_binary(basename, dest_dir)
- full_path = nil
- catch(:found) {
- SRC_DIRS.each do |path|
- full_path = File.join(path, basename)
- if File.exist? full_path
- throw :found
- end
- end
- raise Errno::ENOENT, "No such file to install: %s" % basename
- }
- FileUtils.mkdir_p dest_dir
- system("#{INSTALL_PROGRAM} #{full_path} #{dest_dir}")
- end
-
- def ruby_libs
- Find.find(RbConfig::CONFIG['rubylibdir']).select{|path| File.file?(path) }.map{|path| path.sub("#{@destdir}/", "") }
- end
-end
-
-def install(destdir)
- inst = Installation.new(destdir)
- manifest = JSON.parse(File.read("pepper-ruby.nmf"))
-
- program = File.basename(manifest['program'][ARCH]['url'])
- inst.install_program(program)
-
- manifest['files'].each do |name, attr|
- inst.install_library(name, File.basename(attr[ARCH]["url"]))
- end
-
- File.open(File.join(destdir, "ruby.nmf"), "w") {|f|
- f.puts JSON.pretty_generate(inst.manifest)
- }
-end
-
-def main
- install(ARGV[0])
-end
-
-if __FILE__ == $0
- main()
-end
diff --git a/nacl/pepper_main.c b/nacl/pepper_main.c
deleted file mode 100644
index 168e8f9fd4..0000000000
--- a/nacl/pepper_main.c
+++ /dev/null
@@ -1,732 +0,0 @@
-/******************************************************************************
- Copyright 2012 Google Inc. All Rights Reserved.
- Author: yugui@google.com (Yugui Sonoda)
- ******************************************************************************/
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <pthread.h>
-#include <sys/stat.h>
-#include <sys/mount.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include "ppapi/c/pp_errors.h"
-#include "ppapi/c/pp_module.h"
-#include "ppapi/c/pp_var.h"
-#include "ppapi/c/ppb.h"
-#include "ppapi/c/ppb_core.h"
-#include "ppapi/c/ppb_file_ref.h"
-#include "ppapi/c/ppb_instance.h"
-#include "ppapi/c/ppb_messaging.h"
-#include "ppapi/c/ppb_url_loader.h"
-#include "ppapi/c/ppb_url_request_info.h"
-#include "ppapi/c/ppb_url_response_info.h"
-#include "ppapi/c/ppb_var.h"
-#include "ppapi/c/ppp.h"
-#include "ppapi/c/ppp_instance.h"
-#include "ppapi/c/ppp_messaging.h"
-#include "nacl_io/nacl_io.h"
-
-#include "verconf.h"
-#include "ruby/ruby.h"
-#include "version.h"
-#include "gc.h"
-
-#ifdef HAVE_STRUCT_PPB_CORE
-typedef struct PPB_Core PPB_Core;
-#endif
-#ifdef HAVE_STRUCT_PPB_MESSAGING
-typedef struct PPB_Messaging PPB_Messaging;
-#endif
-#ifdef HAVE_STRUCT_PPB_VAR
-typedef struct PPB_Var PPB_Var;
-#endif
-#ifdef HAVE_STRUCT_PPB_URLLOADER
-typedef struct PPB_URLLoader PPB_URLLoader;
-#endif
-#ifdef HAVE_STRUCT_PPB_URLREQUESTINFO
-typedef struct PPB_URLRequestInfo PPB_URLRequestInfo;
-#endif
-#ifdef HAVE_STRUCT_PPB_URLRESPONSEINFO
-typedef struct PPB_URLResponseInfo PPB_URLResponseInfo;
-#endif
-#ifdef HAVE_STRUCT_PPP_INSTANCE
-typedef struct PPP_Instance PPP_Instance;
-#endif
-
-static PP_Module module_id = 0;
-static PPB_GetInterface get_browser_interface = NULL;
-static PPB_Core* core_interface = NULL;
-static PPB_Messaging* messaging_interface = NULL;
-static PPB_Var* var_interface = NULL;
-static PPB_URLLoader* loader_interface = NULL;
-static PPB_URLRequestInfo* request_interface = NULL;
-static PPB_URLResponseInfo* response_interface = NULL;
-static PPB_FileRef* fileref_interface = NULL;
-static struct st_table* instance_data = NULL;
-
-static VALUE instance_table = Qundef;
-
-static PP_Instance current_instance = 0;
-
-/******************************************************************************
- * State of instance
- ******************************************************************************/
-
-static void inst_mark(void *const ptr);
-static void inst_free(void *const ptr);
-static size_t inst_memsize(void *const ptr);
-static const rb_data_type_t pepper_instance_data_type = {
- "PepperInstance",
- { inst_mark, inst_free, inst_memsize }
-};
-
-struct PepperInstance {
- PP_Instance instance;
- PP_Resource url_loader;
- VALUE self;
- void* async_call_args;
- union {
- int32_t as_int;
- const char* as_str;
- VALUE as_value;
- } async_call_result;
- char buf[1000];
-
- pthread_t th;
- pthread_mutex_t mutex;
- pthread_cond_t cond;
-};
-
-struct PepperInstance*
-pruby_get_instance(PP_Instance instance)
-{
- VALUE self = rb_hash_aref(instance_table, INT2FIX(instance));
- if (RTEST(self)) {
- struct PepperInstance *inst;
- TypedData_Get_Struct(self, struct PepperInstance, &pepper_instance_data_type, inst);
- return inst;
- }
- else {
- return NULL;
- }
-}
-
-#define GET_PEPPER_INSTANCE() (pruby_get_instance(current_instance))
-
-struct PepperInstance*
-pruby_register_instance(PP_Instance instance)
-{
- VALUE obj;
- struct PepperInstance *data;
- obj = TypedData_Make_Struct(rb_cData, struct PepperInstance, &pepper_instance_data_type, data);
- data->self = obj;
- data->instance = instance;
- data->url_loader = 0;
-
- pthread_mutex_init(&data->mutex, NULL);
- pthread_cond_init(&data->cond, NULL);
-
- rb_hash_aset(instance_table, INT2FIX(instance), obj);
- return data;
-}
-
-int
-pruby_unregister_instance(PP_Instance instance)
-{
- VALUE inst = rb_hash_delete(instance_table, INT2FIX(instance));
- return RTEST(inst);
-}
-
-static void
-inst_mark(void *const ptr)
-{
- RUBY_MARK_ENTER("PepperInstance"0);
- if (ptr) {
- const struct PepperInstance* inst = (struct PepperInstance*)ptr;
- RUBY_MARK_UNLESS_NULL(inst->async_call_result.as_value);
- }
- RUBY_MARK_LEAVE("PepperInstance"0);
-}
-
-static void
-inst_free(void *const ptr)
-{
- ruby_xfree(ptr);
-}
-
-static size_t
-inst_memsize(void *const ptr)
-{
- if (ptr) {
- const struct PepperInstance* inst = (struct PepperInstance*)ptr;
- return sizeof(*inst);
- } else {
- return 0;
- }
-}
-
-void
-pruby_async_return_int(void* data, int32_t result)
-{
- /* PPAPI main thread */
- struct PepperInstance* const instance = (struct PepperInstance*)data;
- instance->async_call_result.as_int = result;
- if (pthread_cond_signal(&instance->cond)) {
- perror("pepper-ruby:pthread_cond_signal");
- }
-}
-
-void
-pruby_async_return_str(void* data, const char *result)
-{
- /* PPAPI main thread */
- struct PepperInstance* const instance = (struct PepperInstance*)data;
- instance->async_call_result.as_str = result;
- if (pthread_cond_signal(&instance->cond)) {
- perror("pepper-ruby:pthread_cond_signal");
- }
-}
-
-void
-pruby_async_return_value(void* data, VALUE value)
-{
- /* PPAPI main thread */
- struct PepperInstance* const instance = (struct PepperInstance*)data;
- instance->async_call_result.as_value = value;
- if (pthread_cond_signal(&instance->cond)) {
- perror("pepper-ruby:pthread_cond_signal");
- }
-}
-/******************************************************************************
- * Conversion between Ruby's VALUE, Pepper's Var and C string
- ******************************************************************************/
-
-/**
- * Creates a new string PP_Var from C string. The resulting object will be a
- * refcounted string object. It will be AddRef()ed for the caller. When the
- * caller is done with it, it should be Release()d.
- * @param[in] str C string to be converted to PP_Var
- * @return PP_Var containing string.
- */
-static struct PP_Var
-pruby_cstr_to_var(const char* str)
-{
-#ifndef PPB_VAR_INTERFACE_1_1
- if (var_interface != NULL)
- return var_interface->VarFromUtf8(module_id, str, strlen(str));
- return PP_MakeUndefined();
-#else
- return var_interface->VarFromUtf8(str, strlen(str));
-#endif
-}
-
-/**
- * Returns a mutable C string contained in the @a var or NULL if @a var is not
- * string. This makes a copy of the string in the @a var and adds a NULL
- * terminator. Note that VarToUtf8() does not guarantee the NULL terminator on
- * the returned string. See the comments for VarToUtf8() in ppapi/c/ppb_var.h
- * for more info. The caller is responsible for freeing the returned memory.
- * @param[in] var PP_Var containing string.
- * @return a mutable C string representation of @a var.
- * @note The caller is responsible for freeing the returned string.
- */
-static char*
-pruby_var_to_cstr(struct PP_Var var)
-{
- uint32_t len = 0;
- if (var_interface != NULL) {
- const char* var_c_str = var_interface->VarToUtf8(var, &len);
- if (len > 0) {
- char* c_str = (char*)malloc(len + 1);
- memcpy(c_str, var_c_str, len);
- c_str[len] = '\0';
- return c_str;
- }
- }
- return NULL;
-}
-
-static struct PP_Var
-pruby_str_to_var(volatile VALUE str)
-{
- if (!RB_TYPE_P(str, T_STRING)) {
- fprintf(stderr, "[BUG] Unexpected object type: %x\n", TYPE(str));
- exit(EXIT_FAILURE);
- }
-#ifndef PPB_VAR_INTERFACE_1_1
- if (var_interface != NULL) {
- return var_interface->VarFromUtf8(module_id, RSTRING_PTR(str), RSTRING_LEN(str));
- }
-#else
- return var_interface->VarFromUtf8(RSTRING_PTR(str), RSTRING_LEN(str));
-#endif
- return PP_MakeUndefined();
-}
-
-static struct PP_Var
-pruby_obj_to_var(volatile VALUE obj)
-{
- static const char* const error =
- "throw 'Failed to convert the result to a JavaScript object';";
- int state;
- obj = rb_protect(&rb_obj_as_string, obj, &state);
- if (!state) {
- return pruby_str_to_var(obj);
- }
- else {
- return pruby_cstr_to_var(error);
- }
-}
-
-int
-pruby_var_equal_to_cstr_p(struct PP_Var lhs, const char* rhs)
-{
- uint32_t len = 0;
- if (var_interface == NULL) {
- return 0;
- }
- else {
- const char* const cstr = var_interface->VarToUtf8(lhs, &len);
- return strncmp(cstr, rhs, len) == 0;
- }
-}
-
-int
-pruby_var_prefixed_p(struct PP_Var var, const char* prefix)
-{
- uint32_t len = 0;
- if (var_interface == NULL) {
- return 0;
- }
- else {
- const char* const cstr = var_interface->VarToUtf8(var, &len);
- const size_t prefix_len = strlen(prefix);
- return len >= prefix_len && memcmp(cstr, prefix, len) == 0;
- }
-}
-
-
-/******************************************************************************
- * Messaging
- ******************************************************************************/
-
-/* Posts the given C string as a message.
- * @param data pointer to a NULL-terminated string */
-void
-pruby_post_cstr(void* data)
-{
- /* PPAPI main thread */
- struct PepperInstance* const instance = (struct PepperInstance*)data;
- const char* const msg = (const char*)instance->async_call_args;
- messaging_interface->PostMessage(instance->instance,
- pruby_cstr_to_var(msg));
-}
-
-/* Posts the given Ruby VALUE as a message.
- * @param data a VALUE casted to void* */
-void
-pruby_post_value(void* data)
-{
- /* PPAPI main thread */
- struct PepperInstance* const instance = (struct PepperInstance*)data;
- volatile VALUE value = (VALUE)instance->async_call_args;
- messaging_interface->PostMessage(instance->instance, pruby_obj_to_var(value));
-}
-
-
-
-/******************************************************************************
- * Ruby initialization
- ******************************************************************************/
-
-static void
-init_loadpath(void)
-{
- ruby_incpush("lib/ruby/"RUBY_LIB_VERSION);
- ruby_incpush("lib/ruby/"RUBY_LIB_VERSION"/"RUBY_PLATFORM);
- ruby_incpush(".");
-}
-
-static VALUE
-init_libraries_internal(VALUE unused)
-{
- extern void Init_enc();
- extern void Init_ext();
-
- init_loadpath();
- Init_enc();
- Init_ext();
- return Qnil;
-}
-
-static void*
-init_libraries(void* data)
-{
- int state;
- struct PepperInstance* const instance = (struct PepperInstance*)data;
- current_instance = instance->instance;
-
- if (pthread_mutex_lock(&instance->mutex)) {
- perror("pepper-ruby:pthread_mutex_lock");
- return 0;
- }
- rb_protect(&init_libraries_internal, Qnil, &state);
- pthread_mutex_unlock(&instance->mutex);
-
- if (state) {
- volatile VALUE err = rb_errinfo();
- err = rb_obj_as_string(err);
- } else {
- instance->async_call_args = (void*)"rubyReady";
- core_interface->CallOnMainThread(
- 0, PP_MakeCompletionCallback(pruby_post_cstr, instance), 0);
- }
- return NULL;
-}
-
-static int
-init_libraries_if_necessary(void)
-{
- static int initialized = 0;
- if (!initialized) {
- struct PepperInstance* const instance = GET_PEPPER_INSTANCE();
- int err;
- initialized = 1;
- err = pthread_create(&instance->th, NULL, &init_libraries, instance);
- if (err) {
- fprintf(stderr, "pepper_ruby:pthread_create: %s\n", strerror(err));
- exit(EXIT_FAILURE);
- }
- pthread_detach(instance->th);
- }
- return 0;
-}
-
-static int
-reopen_fd(int fd, const char* path, int flags) {
- int fd2 = open(path, flags);
- if (fd2 < 0) {
- perror("open fd");
- return -1;
- }
- if (dup2(fd2, fd) < 0) {
- perror("dup2 fd");
- return -1;
- }
- if (close(fd2)) {
- perror("close old fd");
- return -1;
- }
- return fd;
-}
-
-static int
-pruby_init(void)
-{
- RUBY_INIT_STACK;
- ruby_init();
-
- instance_table = rb_hash_new();
- rb_gc_register_mark_object(instance_table);
-
- return 0;
-}
-
-
-/******************************************************************************
- * Ruby evaluation
- ******************************************************************************/
-
-static void*
-pruby_eval(void* data)
-{
- extern VALUE ruby_eval_string_from_file_protect(const char* src, const char* path, int* state);
- struct PepperInstance* const instance = (struct PepperInstance*)data;
- volatile VALUE src = (VALUE)instance->async_call_args;
- volatile VALUE result = Qnil;
- volatile int state;
-
- RUBY_INIT_STACK;
-
- if (pthread_mutex_lock(&instance->mutex)) {
- perror("pepper-ruby:pthread_mutex_lock");
- return 0;
- }
- result = ruby_eval_string_from_file_protect(
- RSTRING_PTR(src), "(pepper-ruby)", &state);
- pthread_mutex_unlock(&instance->mutex);
-
- if (!state) {
- instance->async_call_args =
- rb_str_concat(rb_usascii_str_new_cstr("return:"),
- rb_obj_as_string(result));
- core_interface->CallOnMainThread(
- 0, PP_MakeCompletionCallback(pruby_post_value, instance), 0);
- return NULL;
- }
- else {
- rb_set_errinfo(Qnil);
- instance->async_call_args =
- rb_str_concat(rb_usascii_str_new_cstr("error:"),
- rb_obj_as_string(result));
- core_interface->CallOnMainThread(
- 0, PP_MakeCompletionCallback(pruby_post_value, instance), 0);
- return NULL;
- }
-}
-
-
-/******************************************************************************
- * Pepper Module callbacks
- ******************************************************************************/
-
-/**
- * Called when the NaCl module is instantiated on the web page. The identifier
- * of the new instance will be passed in as the first argument (this value is
- * generated by the browser and is an opaque handle). This is called for each
- * instantiation of the NaCl module, which is each time the <embed> tag for
- * this module is encountered.
- *
- * If this function reports a failure (by returning @a PP_FALSE), the NaCl
- * module will be deleted and DidDestroy will be called.
- * @param[in] instance The identifier of the new instance representing this
- * NaCl module.
- * @param[in] argc The number of arguments contained in @a argn and @a argv.
- * @param[in] argn An array of argument names. These argument names are
- * supplied in the <embed> tag, for example:
- * <embed id="nacl_module" dimensions="2">
- * will produce two arguments, one named "id" and one named "dimensions".
- * @param[in] argv An array of argument values. These are the values of the
- * arguments listed in the <embed> tag. In the above example, there will
- * be two elements in this array, "nacl_module" and "2". The indices of
- * these values match the indices of the corresponding names in @a argn.
- * @return @a PP_TRUE on success.
- */
-static PP_Bool
-Instance_DidCreate(PP_Instance instance,
- uint32_t argc, const char* argn[], const char* argv[])
-{
- struct PepperInstance* data = pruby_register_instance(instance);
- current_instance = instance;
-
- nacl_io_init_ppapi(instance, get_browser_interface);
-
- if (mount("", "/dev2", "dev", 0, "")) {
- perror("mount dev");
- return PP_FALSE;
- }
- if (reopen_fd(0, "/dev2/stdin", O_RDONLY) < 0) {
- perror("reopen stdin");
- return PP_FALSE;
- }
- if (reopen_fd(1, "/dev2/stdout", O_WRONLY) < 0) {
- perror("reopen stdout");
- return PP_FALSE;
- }
- if (reopen_fd(2, "/dev2/console1", O_WRONLY) < 0) {
- perror("reopen stderr");
- return PP_FALSE;
- }
-
- /* TODO(yugui) Unmount original /dev */
-
- if (mount("/lib", "/lib", "httpfs",
- 0, "allow_cross_origin_requests=false")) {
- perror("mount httpfs");
- return PP_FALSE;
- }
-
- return init_libraries_if_necessary() ? PP_FALSE : PP_TRUE;
-}
-
-/**
- * Called when the NaCl module is destroyed. This will always be called,
- * even if DidCreate returned failure. This routine should deallocate any data
- * associated with the instance.
- * @param[in] instance The identifier of the instance representing this NaCl
- * module.
- */
-static void Instance_DidDestroy(PP_Instance instance) {
- struct PepperInstance* data = pruby_get_instance(instance);
- core_interface->ReleaseResource(data->url_loader);
- pruby_unregister_instance(instance);
-}
-
-/**
- * Called when the position, the size, or the clip rect of the element in the
- * browser that corresponds to this NaCl module has changed.
- * @param[in] instance The identifier of the instance representing this NaCl
- * module.
- * @param[in] position The location on the page of this NaCl module. This is
- * relative to the top left corner of the viewport, which changes as the
- * page is scrolled.
- * @param[in] clip The visible region of the NaCl module. This is relative to
- * the top left of the plugin's coordinate system (not the page). If the
- * plugin is invisible, @a clip will be (0, 0, 0, 0).
- */
-#ifndef PPP_INSTANCE_INTERFACE_1_1
-static void
-Instance_DidChangeView(PP_Instance instance,
- const struct PP_Rect* position,
- const struct PP_Rect* clip)
-{
-}
-#else
-static void
-Instance_DidChangeView(PP_Instance instance, PP_Resource view_resource)
-{
-}
-#endif
-
-/**
- * Notification that the given NaCl module has gained or lost focus.
- * Having focus means that keyboard events will be sent to the NaCl module
- * represented by @a instance. A NaCl module's default condition is that it
- * will not have focus.
- *
- * Note: clicks on NaCl modules will give focus only if you handle the
- * click event. You signal if you handled it by returning @a true from
- * HandleInputEvent. Otherwise the browser will bubble the event and give
- * focus to the element on the page that actually did end up consuming it.
- * If you're not getting focus, check to make sure you're returning true from
- * the mouse click in HandleInputEvent.
- * @param[in] instance The identifier of the instance representing this NaCl
- * module.
- * @param[in] has_focus Indicates whether this NaCl module gained or lost
- * event focus.
- */
-static void
-Instance_DidChangeFocus(PP_Instance instance, PP_Bool has_focus)
-{
-}
-
-/**
- * Handler that gets called after a full-frame module is instantiated based on
- * registered MIME types. This function is not called on NaCl modules. This
- * function is essentially a place-holder for the required function pointer in
- * the PPP_Instance structure.
- * @param[in] instance The identifier of the instance representing this NaCl
- * module.
- * @param[in] url_loader A PP_Resource an open PPB_URLLoader instance.
- * @return PP_FALSE.
- */
-static PP_Bool
-Instance_HandleDocumentLoad(PP_Instance instance, PP_Resource url_loader)
-{
- /* NaCl modules do not need to handle the document load function. */
- return PP_FALSE;
-}
-
-
-/**
- * Handler for messages coming in from the browser via postMessage. The
- * @a var_message can contain anything: a JSON string; a string that encodes
- * method names and arguments; etc. For example, you could use JSON.stringify
- * in the browser to create a message that contains a method name and some
- * parameters, something like this:
- * var json_message = JSON.stringify({ "myMethod" : "3.14159" });
- * nacl_module.postMessage(json_message);
- * On receipt of this message in @a var_message, you could parse the JSON to
- * retrieve the method name, match it to a function call, and then call it with
- * the parameter.
- * @param[in] instance The instance ID.
- * @param[in] message The contents, copied by value, of the message sent from
- * browser via postMessage.
- */
-void
-Messaging_HandleMessage(PP_Instance instance, struct PP_Var var_message)
-{
- char* const message = pruby_var_to_cstr(var_message);
- size_t message_len = strlen(message);
- current_instance = instance;
-
- if (strstr(message, "eval:") != NULL) {
- volatile VALUE src;
- struct PepperInstance* const instance_data = GET_PEPPER_INSTANCE();
- int err;
-#define EVAL_PREFIX_LEN 5
- src = rb_str_new(message + EVAL_PREFIX_LEN, message_len - EVAL_PREFIX_LEN);
- instance_data->async_call_args = (void*)src;
- err = pthread_create(&instance_data->th, NULL, &pruby_eval, instance_data);
- if (err) {
- fprintf(stderr, "pepper_ruby:pthread_create: %s\n", strerror(err));
- exit(EXIT_FAILURE);
- }
- pthread_detach(instance_data->th);
- }
- free(message);
-}
-
-/**
- * Entry points for the module.
- * Initialize instance interface and scriptable object class.
- * @param[in] a_module_id Module ID
- * @param[in] get_browser_interface Pointer to PPB_GetInterface
- * @return PP_OK on success, any other value on failure.
- */
-PP_EXPORT int32_t
-PPP_InitializeModule(PP_Module a_module_id, PPB_GetInterface a_get_browser_interface)
-{
- module_id = a_module_id;
- get_browser_interface = a_get_browser_interface;
- core_interface = (PPB_Core*)(get_browser_interface(PPB_CORE_INTERFACE));
- if (core_interface == NULL) return PP_ERROR_NOINTERFACE;
-
- var_interface = (PPB_Var*)(get_browser_interface(PPB_VAR_INTERFACE));
- if (var_interface == NULL) return PP_ERROR_NOINTERFACE;
-
- messaging_interface = (PPB_Messaging*)(get_browser_interface(PPB_MESSAGING_INTERFACE));
- if (messaging_interface == NULL) return PP_ERROR_NOINTERFACE;
-
- loader_interface = (PPB_URLLoader*)(get_browser_interface(PPB_URLLOADER_INTERFACE));
- if (loader_interface == NULL) return PP_ERROR_NOINTERFACE;
-
- request_interface = (PPB_URLRequestInfo*)(get_browser_interface(PPB_URLREQUESTINFO_INTERFACE));
- if (request_interface == NULL) return PP_ERROR_NOINTERFACE;
-
- response_interface = (PPB_URLResponseInfo*)(get_browser_interface(PPB_URLRESPONSEINFO_INTERFACE));
- if (response_interface == NULL) return PP_ERROR_NOINTERFACE;
-
- fileref_interface = (PPB_FileRef*)(get_browser_interface(PPB_FILEREF_INTERFACE));
- if (fileref_interface == NULL) return PP_ERROR_NOINTERFACE;
-
- return pruby_init() ? PP_ERROR_FAILED : PP_OK;
-}
-
-/**
- * Returns an interface pointer for the interface of the given name, or NULL
- * if the interface is not supported.
- * @param[in] interface_name name of the interface
- * @return pointer to the interface
- */
-PP_EXPORT const void*
-PPP_GetInterface(const char* interface_name)
-{
- if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) {
- static PPP_Instance instance_interface = {
- &Instance_DidCreate,
- &Instance_DidDestroy,
- &Instance_DidChangeView,
- &Instance_DidChangeFocus,
- &Instance_HandleDocumentLoad
- };
- return &instance_interface;
- } else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) {
- static PPP_Messaging messaging_interface = {
- &Messaging_HandleMessage
- };
- return &messaging_interface;
- }
- return NULL;
-}
-
-/**
- * Called before the plugin module is unloaded.
- */
-PP_EXPORT void
-PPP_ShutdownModule(void)
-{
- ruby_cleanup(0);
-}
diff --git a/nacl/resource.h b/nacl/resource.h
deleted file mode 100644
index 57ca53b093..0000000000
--- a/nacl/resource.h
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * Copyright 2011 Google Inc. All Rights Reserved.
- * Author: yugui@google.com (Yugui Sonoda)
- * */
-#ifndef RUBY_NACL_RESOURCE_H
-#define RUBY_NACL_RESOURCE_H
-int getrusage(int who, struct rusage *usage);
-#endif
diff --git a/nacl/select.h b/nacl/select.h
deleted file mode 100644
index 921721a2fd..0000000000
--- a/nacl/select.h
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-// Author: yugui@google.com (Yugui Sonoda)
-#ifndef RUBY_NACL_SELECT_H
-#define RUBY_NACL_SELECT_H
-int select(int num_fds, fd_set *in_fds, fd_set *out_fds,
- fd_set *ex_fds, struct timeval *timeout);
-#endif
diff --git a/nacl/signal.h b/nacl/signal.h
deleted file mode 100644
index 54832de1fe..0000000000
--- a/nacl/signal.h
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-// Author: yugui@google.com (Yugui Sonoda)
-#ifndef RUBY_NACL_SIGNAL_H
-#define RUBY_NACL_SIGNAL_H
-int kill(pid_t pid, int signal);
-#endif
diff --git a/nacl/stat.h b/nacl/stat.h
deleted file mode 100644
index 7be40ada7c..0000000000
--- a/nacl/stat.h
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * Copyright 2011 Google Inc. All Rights Reserved.
- * Author: yugui@google.com (Yugui Sonoda)
- * */
-#ifndef RUBY_NACL_STAT_H
-#define RUBY_NACL_STAT_H
-mode_t umask(mode_t mask);
-struct stat;
-int lstat(const char* path, struct stat* result);
-#endif
diff --git a/nacl/unistd.h b/nacl/unistd.h
deleted file mode 100644
index 1c97390c63..0000000000
--- a/nacl/unistd.h
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-// Author: yugui@google.com (Yugui Sonoda)
-#ifndef RUBY_NACL_UNISTD_H
-#define RUBY_NACL_UNISTD_H
-int seteuid(pid_t pid);
-int setegid(pid_t pid);
-int truncate(const char* path, off_t new_size);
-int ftruncate(int fd, off_t new_size);
-#endif
diff --git a/nacl/utime.h b/nacl/utime.h
deleted file mode 100644
index 96910051e4..0000000000
--- a/nacl/utime.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * Copyright 2011 Google Inc. All Rights Reserved.
- * Author: yugui@google.com (Yugui Sonoda)
- */
-
-#ifndef RUBY_NACL_UTIME_H
-#define RUBY_NACL_UTIME_H
-#include <utime.h>
-int utime(const char *filename, const struct utimbuf *times);
-int utimes(const char *filename, const struct timeval times[2]);
-#endif
diff --git a/node.c b/node.c
index 1fe47844da..d81a7319d3 100644
--- a/node.c
+++ b/node.c
@@ -23,7 +23,11 @@
#define A_LONG(val) rb_str_catf(buf, "%ld", (val))
#define A_LIT(lit) AR(rb_inspect(lit))
#define A_NODE_HEADER(node, term) \
- rb_str_catf(buf, "@ %s (line: %d)"term, ruby_node_name(nd_type(node)), nd_line(node))
+ rb_str_catf(buf, "@ %s (line: %d, location: (%d,%d)-(%d,%d))%s"term, \
+ ruby_node_name(nd_type(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))
@@ -31,18 +35,12 @@
#define D_NULL_NODE (A_INDENT, A("(null node)\n"))
#define D_NODE_HEADER(node) (A_INDENT, A_NODE_HEADER(node, "\n"))
-#define COMPOUND_FIELD(len, name, block) \
- do { \
- D_FIELD_HEADER((len), (name), "\n"); \
- D_INDENT; \
- block; \
- D_DEDENT; \
- } while (0)
+#define COMPOUND_FIELD(len, name) \
+ FIELD_BLOCK((D_FIELD_HEADER((len), (name), "\n"), D_INDENT), D_DEDENT)
-#define COMPOUND_FIELD1(name, ann, block) \
+#define COMPOUND_FIELD1(name, ann) \
COMPOUND_FIELD(FIELD_NAME_LEN(name, ann), \
- FIELD_NAME_DESC(name, ann), \
- block)
+ FIELD_NAME_DESC(name, ann))
#define FIELD_NAME_DESC(name, ann) name " (" ann ")"
#define FIELD_NAME_LEN(name, ann) (int)( \
@@ -50,9 +48,12 @@
rb_strlen_lit(FIELD_NAME_DESC(name, ann)) : \
rb_strlen_lit(name))
#define SIMPLE_FIELD(len, name) \
- for (D_FIELD_HEADER((len), (name), " "), field_flag = 1; \
+ 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 */ \
- A("\n"), field_flag = 0)
+ 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)
@@ -64,9 +65,7 @@
#define F_MSG(name, ann, desc) SIMPLE_FIELD1(#name, ann) A(desc)
#define F_NODE(name, ann) \
- COMPOUND_FIELD1(#name, ann, dump_node(buf, indent, comment, node->name))
-#define F_OPTION(name, ann) \
- COMPOUND_FIELD1(#name, ann, dump_option(buf, indent, node->name))
+ COMPOUND_FIELD1(#name, ann) {dump_node(buf, indent, comment, node->name);}
#define ANN(ann) \
if (comment) { \
@@ -103,42 +102,11 @@ struct add_option_arg {
st_index_t count;
};
-static int
-add_option_i(VALUE key, VALUE val, VALUE args)
-{
- struct add_option_arg *argp = (void *)args;
- VALUE buf = argp->buf;
- VALUE indent = argp->indent;
-
- A_INDENT;
- A("+- ");
- AR(rb_sym2str(key));
- A(": ");
- A_LIT(val);
- A("\n");
- return ST_CONTINUE;
-}
-
-static void
-dump_option(VALUE buf, VALUE indent, VALUE opt)
-{
- struct add_option_arg arg;
-
- if (!RB_TYPE_P(opt, T_HASH)) {
- A_LIT(opt);
- return;
- }
- arg.buf = buf;
- arg.indent = indent;
- arg.count = 0;
- rb_hash_foreach(opt, add_option_i, (VALUE)&arg);
-}
-
-static void dump_node(VALUE, VALUE, int, NODE *);
+static void dump_node(VALUE, VALUE, int, const NODE *);
static const char default_indent[] = "| ";
static void
-dump_array(VALUE buf, VALUE indent, int comment, NODE *node)
+dump_array(VALUE buf, VALUE indent, int comment, const NODE *node)
{
int field_flag;
const char *next_indent = default_indent;
@@ -153,11 +121,12 @@ dump_array(VALUE buf, VALUE indent, int comment, NODE *node)
}
static void
-dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
+dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
{
int field_flag;
int i;
const char *next_indent = default_indent;
+ enum node_type type;
if (!node) {
D_NULL_NODE;
@@ -166,7 +135,8 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
D_NODE_HEADER(node);
- switch (nd_type(node)) {
+ type = nd_type(node);
+ switch (type) {
case NODE_BLOCK:
ANN("statement sequence");
ANN("format: [nd_head]; ...; [nd_next]");
@@ -183,10 +153,11 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
} while (node->nd_next &&
nd_type(node->nd_next) == NODE_BLOCK &&
(node = node->nd_next, 1));
- if (!node->nd_next) break;
- LAST_NODE;
- F_NODE(nd_next, "next block");
- break;
+ if (node->nd_next) {
+ LAST_NODE;
+ F_NODE(nd_next, "next block");
+ }
+ return;
case NODE_IF:
ANN("if statement");
@@ -196,7 +167,17 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_body, "then clause");
LAST_NODE;
F_NODE(nd_else, "else clause");
- break;
+ 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;
case NODE_CASE:
ANN("case statement");
@@ -205,23 +186,26 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_head, "case expr");
LAST_NODE;
F_NODE(nd_body, "when clauses");
- break;
+ 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;
case NODE_WHEN:
- ANN("if statement");
+ 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 clause");
+ F_NODE(nd_body, "when body");
LAST_NODE;
F_NODE(nd_next, "next when clause");
- break;
+ return;
- case NODE_OPT_N:
- ANN("wrapper for -n option");
- ANN("format: ruby -ne '[nd_body]' (nd_cond is `gets')");
- ANN("example: ruby -ne 'p $_'");
- goto loop;
case NODE_WHILE:
ANN("while statement");
ANN("format: while [nd_cond]; [nd_body]; end");
@@ -239,7 +223,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_cond, "condition");
LAST_NODE;
F_NODE(nd_body, "body");
- break;
+ return;
case NODE_ITER:
ANN("method call with block");
@@ -254,10 +238,18 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_iter, "iteration receiver");
LAST_NODE;
F_NODE(nd_body, "body");
- break;
+ 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;
case NODE_BREAK:
- ANN("for statement");
+ ANN("break statement");
ANN("format: break [nd_stts]");
ANN("example: break 1");
goto jump;
@@ -273,19 +265,19 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
jump:
LAST_NODE;
F_NODE(nd_stts, "value");
- break;
+ return;
case NODE_REDO:
ANN("redo statement");
ANN("format: redo");
ANN("example: redo");
- break;
+ return;
case NODE_RETRY:
ANN("retry statement");
ANN("format: retry");
ANN("example: retry");
- break;
+ return;
case NODE_BEGIN:
ANN("begin statement");
@@ -293,7 +285,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
ANN("example: begin; 1; end");
LAST_NODE;
F_NODE(nd_body, "body");
- break;
+ return;
case NODE_RESCUE:
ANN("rescue clause");
@@ -303,7 +295,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_resq, "rescue clause list");
LAST_NODE;
F_NODE(nd_else, "rescue else clause");
- break;
+ return;
case NODE_RESBODY:
ANN("rescue clause (cont'd)");
@@ -313,7 +305,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_body, "rescue clause");
LAST_NODE;
F_NODE(nd_head, "next rescue clause");
- break;
+ return;
case NODE_ENSURE:
ANN("ensure clause");
@@ -322,7 +314,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_head, "body");
LAST_NODE;
F_NODE(nd_ensr, "ensure clause");
- break;
+ return;
case NODE_AND:
ANN("&& operator");
@@ -332,12 +324,17 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
case NODE_OR:
ANN("|| operator");
ANN("format: [nd_1st] || [nd_2nd]");
- ANN("example: foo && bar");
+ ANN("example: foo || bar");
andor:
- F_NODE(nd_1st, "left expr");
+ while (1) {
+ F_NODE(nd_1st, "left expr");
+ if (!node->nd_2nd || nd_type(node->nd_2nd) != (int)type)
+ break;
+ node = node->nd_2nd;
+ }
LAST_NODE;
F_NODE(nd_2nd, "right expr");
- break;
+ return;
case NODE_MASGN:
ANN("multiple assignment");
@@ -345,50 +342,65 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
ANN("example: a, b = foo");
F_NODE(nd_value, "rhsn");
F_NODE(nd_head, "lhsn");
- if ((VALUE)node->nd_args != (VALUE)-1) {
+ if (NODE_NAMED_REST_P(node->nd_args)) {
LAST_NODE;
F_NODE(nd_args, "splatn");
}
else {
- F_MSG(nd_args, "splatn", "-1 (rest argument without name)");
+ F_MSG(nd_args, "splatn", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
}
- break;
+ return;
case NODE_LASGN:
ANN("local variable assignment");
ANN("format: [nd_vid](lvar) = [nd_value]");
ANN("example: x = foo");
- goto asgn;
+ 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 (out of current scope)");
ANN("format: [nd_vid](dvar) = [nd_value]");
ANN("example: x = nil; 1.times { x = foo }");
- goto asgn;
+ F_ID(nd_vid, "local variable");
+ LAST_NODE;
+ F_NODE(nd_value, "rvalue");
+ return;
case NODE_DASGN_CURR:
ANN("dynamic variable assignment (in current scope)");
ANN("format: [nd_vid](current dvar) = [nd_value]");
ANN("example: 1.times { x = foo }");
- goto asgn;
+ 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");
- goto asgn;
+ 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");
- asgn:
- F_ID(nd_vid, "variable");
+ F_ID(nd_vid, "class variable");
LAST_NODE;
- if (node->nd_value == (NODE *)-1) {
- F_MSG(nd_value, "rvalue", "(required keyword argument)");
- }
- else {
- F_NODE(nd_value, "rvalue");
- }
- break;
-
+ F_NODE(nd_value, "rvalue");
+ return;
case NODE_GASGN:
ANN("global variable assignment");
ANN("format: [nd_entry](gvar) = [nd_value]");
@@ -396,38 +408,38 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_GENTRY(nd_entry, "global variable");
LAST_NODE;
F_NODE(nd_value, "rvalue");
- break;
+ 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, "variable");
- F_MSG(nd_else, "extension", "not used");
+ F_ID(nd_vid, "constant");
+ F_MSG(nd_else, "extension", "not used");
}
else {
- F_MSG(nd_vid, "variable", "0 (see extension field)");
- F_NODE(nd_else, "extension");
+ F_MSG(nd_vid, "constant", "0 (see extension field)");
+ F_NODE(nd_else, "extension");
}
LAST_NODE;
F_NODE(nd_value, "rvalue");
- break;
+ return;
case NODE_OP_ASGN1:
ANN("array assignment with operator");
- ANN("format: [nd_value] [ [nd_args->nd_body] ] [nd_vid]= [nd_args->nd_head]");
+ 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_vid, "operator");
- F_NODE(nd_args->nd_body, "index");
+ F_ID(nd_mid, "operator");
+ F_NODE(nd_args->nd_head, "index");
LAST_NODE;
- F_NODE(nd_args->nd_head, "rvalue");
- break;
+ F_NODE(nd_args->nd_body, "rvalue");
+ return;
case NODE_OP_ASGN2:
ANN("attr assignment with operator");
- ANN("format: [nd_value].[attr] [nd_next->nd_mid]= [nd_value]");
+ 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");
@@ -435,16 +447,10 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
if (node->nd_next->nd_aid) A("? ");
A_ID(node->nd_next->nd_vid);
}
- F_CUSTOM1(nd_next->nd_mid, "operator") {
- switch (node->nd_next->nd_mid) {
- case 0: A("0 (||)"); break;
- case 1: A("1 (&&)"); break;
- default: A_ID(node->nd_next->nd_mid);
- }
- }
+ F_ID(nd_next->nd_mid, "operator");
LAST_NODE;
F_NODE(nd_value, "rvalue");
- break;
+ return;
case NODE_OP_ASGN_AND:
ANN("assignment with && operator");
@@ -459,7 +465,17 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_head, "variable");
LAST_NODE;
F_NODE(nd_value, "rvalue");
- break;
+ 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;
case NODE_CALL:
ANN("method invocation");
@@ -469,7 +485,17 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_recv, "receiver");
LAST_NODE;
F_NODE(nd_args, "arguments");
- break;
+ return;
+
+ case NODE_OPCALL:
+ ANN("method invocation");
+ ANN("format: [nd_recv] [nd_mid] [nd_args]");
+ ANN("example: foo + bar");
+ F_ID(nd_mid, "method id");
+ F_NODE(nd_recv, "receiver");
+ LAST_NODE;
+ F_NODE(nd_args, "arguments");
+ return;
case NODE_FCALL:
ANN("function call");
@@ -478,14 +504,14 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_ID(nd_mid, "method id");
LAST_NODE;
F_NODE(nd_args, "arguments");
- break;
+ return;
case NODE_VCALL:
ANN("function call with no argument");
ANN("format: [nd_mid]");
ANN("example: foo");
F_ID(nd_mid, "method id");
- break;
+ return;
case NODE_QCALL:
ANN("safe method invocation");
@@ -495,7 +521,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_recv, "receiver");
LAST_NODE;
F_NODE(nd_args, "arguments");
- break;
+ return;
case NODE_SUPER:
ANN("super invocation");
@@ -503,13 +529,13 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
ANN("example: super 1");
LAST_NODE;
F_NODE(nd_args, "arguments");
- break;
+ return;
case NODE_ZSUPER:
ANN("super invocation with no argument");
ANN("format: super");
ANN("example: super");
- break;
+ return;
case NODE_ARRAY:
ANN("array constructor");
@@ -522,21 +548,34 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
ANN("example: return 1, 2, 3");
ary:
dump_array(buf, indent, comment, node);
- break;
+ return;
case NODE_ZARRAY:
ANN("empty array constructor");
ANN("format: []");
ANN("example: []");
- break;
+ return;
case NODE_HASH:
- ANN("hash constructor");
- ANN("format: { [nd_head] }");
- ANN("example: { 1 => 2, 3 => 4 }");
+ if (!node->nd_alen) {
+ 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_alen, "keyword arguments or hash literal") {
+ switch (node->nd_alen) {
+ case 0: A("0 (keyword argument)"); break;
+ case 1: A("1 (hash literal)"); break;
+ }
+ }
LAST_NODE;
F_NODE(nd_head, "contents");
- break;
+ return;
case NODE_YIELD:
ANN("yield invocation");
@@ -544,49 +583,52 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
ANN("example: yield 1");
LAST_NODE;
F_NODE(nd_head, "arguments");
- break;
+ return;
case NODE_LVAR:
ANN("local variable reference");
ANN("format: [nd_vid](lvar)");
ANN("example: x");
- goto var;
+ 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 }");
- goto var;
+ F_ID(nd_vid, "local variable");
+ return;
case NODE_IVAR:
ANN("instance variable reference");
ANN("format: [nd_vid](ivar)");
ANN("example: @x");
- goto var;
+ F_ID(nd_vid, "instance variable");
+ return;
case NODE_CONST:
ANN("constant reference");
ANN("format: [nd_vid](constant)");
ANN("example: X");
- goto var;
+ F_ID(nd_vid, "constant");
+ return;
case NODE_CVAR:
ANN("class variable reference");
ANN("format: [nd_vid](cvar)");
ANN("example: @@x");
- var:
- F_ID(nd_vid, "local variable");
- break;
+ 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");
- break;
+ 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); }
- break;
+ return;
case NODE_BACK_REF:
ANN("back special variable reference");
@@ -599,14 +641,14 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
name[2] = '\0';
A(name);
}
- break;
+ return;
case NODE_MATCH:
ANN("match expression (against $_ implicitly)");
ANN("format: [nd_lit] (in condition)");
ANN("example: if /foo/; foo; end");
F_LIT(nd_lit, "regexp");
- break;
+ return;
case NODE_MATCH2:
ANN("match expression (regexp first)");
@@ -619,7 +661,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
LAST_NODE;
F_NODE(nd_args, "named captures");
}
- break;
+ return;
case NODE_MATCH3:
ANN("match expression (regexp second)");
@@ -628,7 +670,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_recv, "string (receiver)");
LAST_NODE;
F_NODE(nd_value, "regexp (argument)");
- break;
+ return;
case NODE_LIT:
ANN("literal");
@@ -646,8 +688,15 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
ANN("example: `foo`");
lit:
F_LIT(nd_lit, "literal");
- break;
+ 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;
case NODE_DSTR:
ANN("string literal with interpolation");
ANN("format: [nd_lit]");
@@ -663,11 +712,6 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
ANN("format: [nd_lit]");
ANN("example: /foo#{ bar }baz/");
goto dlit;
- case NODE_DREGX_ONCE:
- ANN("regexp literal with interpolation and once flag");
- ANN("format: [nd_lit]");
- ANN("example: /foo#{ bar }baz/o");
- goto dlit;
case NODE_DSYM:
ANN("symbol literal with interpolation");
ANN("format: [nd_lit]");
@@ -677,7 +721,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_next->nd_head, "interpolation");
LAST_NODE;
F_NODE(nd_next->nd_next, "tailing strings");
- break;
+ return;
case NODE_EVSTR:
ANN("interpolation expression");
@@ -685,7 +729,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
ANN("example: \"foo#{ bar }baz\"");
LAST_NODE;
F_NODE(nd_body, "body");
- break;
+ return;
case NODE_ARGSCAT:
ANN("splat argument following arguments");
@@ -694,7 +738,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_head, "preceding array");
LAST_NODE;
F_NODE(nd_body, "following array");
- break;
+ return;
case NODE_ARGSPUSH:
ANN("splat argument following one argument");
@@ -703,7 +747,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_head, "preceding array");
LAST_NODE;
F_NODE(nd_body, "following element");
- break;
+ return;
case NODE_SPLAT:
ANN("splat argument");
@@ -711,7 +755,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
ANN("example: foo(*ary)");
LAST_NODE;
F_NODE(nd_head, "splat'ed array");
- break;
+ return;
case NODE_BLOCK_PASS:
ANN("arguments with block argument");
@@ -720,51 +764,51 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_head, "other arguments");
LAST_NODE;
F_NODE(nd_body, "block argument");
- break;
+ return;
case NODE_DEFN:
ANN("method definition");
ANN("format: def [nd_mid] [nd_defn]; end");
- ANN("example; def foo; bar; end");
+ ANN("example: def foo; bar; end");
F_ID(nd_mid, "method name");
LAST_NODE;
F_NODE(nd_defn, "method definition");
- break;
+ 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");
+ 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");
- break;
+ return;
case NODE_ALIAS:
ANN("method alias statement");
- ANN("format: alias [u1.node] [u2.node]");
+ ANN("format: alias [nd_1st] [nd_2nd]");
ANN("example: alias bar foo");
- F_NODE(u1.node, "new name");
+ F_NODE(nd_1st, "new name");
LAST_NODE;
- F_NODE(u2.node, "old name");
- break;
+ F_NODE(nd_2nd, "old name");
+ return;
case NODE_VALIAS:
ANN("global variable alias statement");
- ANN("format: alias [u1.id](gvar) [u2.id](gvar)");
+ ANN("format: alias [nd_alias](gvar) [nd_orig](gvar)");
ANN("example: alias $y $x");
- F_ID(u1.id, "new name");
- F_ID(u2.id, "old name");
- break;
+ F_ID(nd_alias, "new name");
+ F_ID(nd_orig, "old name");
+ return;
case NODE_UNDEF:
- ANN("method alias statement");
- ANN("format: undef [u2.node]");
+ ANN("method undef statement");
+ ANN("format: undef [nd_undef]");
ANN("example: undef foo");
LAST_NODE;
- F_NODE(u2.node, "old name");
- break;
+ F_NODE(nd_undef, "old name");
+ return;
case NODE_CLASS:
ANN("class definition");
@@ -774,7 +818,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_super, "superclass");
LAST_NODE;
F_NODE(nd_body, "class definition");
- break;
+ return;
case NODE_MODULE:
ANN("module definition");
@@ -783,7 +827,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_cpath, "module path");
LAST_NODE;
F_NODE(nd_body, "module definition");
- break;
+ return;
case NODE_SCLASS:
ANN("singleton class definition");
@@ -792,7 +836,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_recv, "receiver");
LAST_NODE;
F_NODE(nd_body, "singleton class definition");
- break;
+ return;
case NODE_COLON2:
ANN("scoped constant reference");
@@ -801,14 +845,14 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_ID(nd_mid, "constant name");
LAST_NODE;
F_NODE(nd_head, "receiver");
- break;
+ return;
case NODE_COLON3:
ANN("top-level constant reference");
ANN("format: ::[nd_mid]");
ANN("example: ::Object");
F_ID(nd_mid, "constant name");
- break;
+ return;
case NODE_DOT2:
ANN("range constructor (incl.)");
@@ -833,44 +877,44 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_beg, "begin");
LAST_NODE;
F_NODE(nd_end, "end");
- break;
+ return;
case NODE_SELF:
ANN("self");
ANN("format: self");
ANN("example: self");
- break;
+ return;
case NODE_NIL:
ANN("nil");
ANN("format: nil");
ANN("example: nil");
- break;
+ return;
case NODE_TRUE:
ANN("true");
ANN("format: true");
ANN("example: true");
- break;
+ return;
case NODE_FALSE:
ANN("false");
ANN("format: false");
ANN("example: false");
- break;
+ return;
case NODE_ERRINFO:
ANN("virtual reference to $!");
ANN("format: rescue => id");
ANN("example: rescue => id");
- break;
+ return;
case NODE_DEFINED:
ANN("defined? expression");
ANN("format: defined?([nd_head])");
ANN("example: defined?(foo)");
F_NODE(nd_head, "expr");
- break;
+ return;
case NODE_POSTEXE:
ANN("post-execution");
@@ -878,36 +922,17 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
ANN("example: END { foo }");
LAST_NODE;
F_NODE(nd_body, "END clause");
- break;
+ return;
case NODE_ATTRASGN:
ANN("attr assignment");
ANN("format: [nd_recv].[nd_mid] = [nd_args]");
ANN("example: struct.field = foo");
- if (node->nd_recv == (NODE *) 1) {
- F_MSG(nd_recv, "receiver", "1 (self)");
- }
- else {
- F_NODE(nd_recv, "receiver");
- }
+ F_NODE(nd_recv, "receiver");
F_ID(nd_mid, "method name");
LAST_NODE;
F_NODE(nd_args, "arguments");
- break;
-
- case NODE_PRELUDE:
- ANN("pre-execution");
- ANN("format: BEGIN { [nd_head] }; [nd_body]");
- ANN("example: bar; BEGIN { foo }");
-#define nd_compile_option u3.value
- F_NODE(nd_head, "prelude");
- if (!node->nd_compile_option) LAST_NODE;
- F_NODE(nd_body, "body");
- if (node->nd_compile_option) {
- LAST_NODE;
- F_OPTION(nd_compile_option, "compile_option");
- }
- break;
+ return;
case NODE_LAMBDA:
ANN("lambda expression");
@@ -915,7 +940,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
ANN("example: -> { foo }");
LAST_NODE;
F_NODE(nd_body, "lambda clause");
- break;
+ return;
case NODE_OPT_ARG:
ANN("optional arguments");
@@ -924,7 +949,7 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_body, "body");
LAST_NODE;
F_NODE(nd_next, "next");
- break;
+ return;
case NODE_KW_ARG:
ANN("keyword arguments");
@@ -933,21 +958,21 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_body, "body");
LAST_NODE;
F_NODE(nd_next, "next");
- break;
+ return;
case NODE_POSTARG:
ANN("post arguments");
ANN("format: *[nd_1st], [nd_2nd..] = ..");
ANN("example: a, *rest, z = foo");
- if ((VALUE)node->nd_1st != (VALUE)-1) {
+ if (NODE_NAMED_REST_P(node->nd_1st)) {
F_NODE(nd_1st, "rest argument");
}
else {
- F_MSG(nd_1st, "rest argument", "-1 (rest argument without name)");
+ F_MSG(nd_1st, "rest argument", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)");
}
LAST_NODE;
F_NODE(nd_2nd, "post arguments");
- break;
+ return;
case NODE_ARGS:
ANN("method parameters");
@@ -961,10 +986,10 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_ID(nd_ainfo->rest_arg, "rest argument");
F_ID(nd_ainfo->block_arg, "block argument");
F_NODE(nd_ainfo->opt_args, "optional arguments");
- LAST_NODE;
F_NODE(nd_ainfo->kw_args, "keyword arguments");
+ LAST_NODE;
F_NODE(nd_ainfo->kw_rest_arg, "keyword rest argument");
- break;
+ return;
case NODE_SCOPE:
ANN("new scope");
@@ -981,15 +1006,18 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
F_NODE(nd_args, "arguments");
LAST_NODE;
F_NODE(nd_body, "body");
- break;
+ return;
- default:
- rb_bug("dump_node: unknown node: %s", ruby_node_name(nd_type(node)));
+ case NODE_ARGS_AUX:
+ case NODE_LAST:
+ break;
}
+
+ rb_bug("dump_node: unknown node: %s", ruby_node_name(nd_type(node)));
}
VALUE
-rb_parser_dump_tree(NODE *node, int comment)
+rb_parser_dump_tree(const NODE *node, int comment)
{
VALUE buf = rb_str_new_cstr(
"###########################################################\n"
@@ -1001,178 +1029,120 @@ rb_parser_dump_tree(NODE *node, int comment)
return buf;
}
+/* Setup NODE structure.
+ * NODE is not an object managed by GC, but it imitates an object
+ * so that it can work with `RB_TYPE_P(obj, T_NODE)`.
+ * This dirty hack is needed because Ripper jumbles NODEs and other type
+ * objects.
+ */
void
-rb_gc_free_node(VALUE obj)
+rb_node_init(NODE *n, enum node_type type, VALUE a0, VALUE a1, VALUE a2)
{
- switch (nd_type(obj)) {
- case NODE_SCOPE:
- if (RNODE(obj)->nd_tbl) {
- xfree(RNODE(obj)->nd_tbl);
- }
- break;
- case NODE_ARGS:
- if (RNODE(obj)->nd_ainfo) {
- xfree(RNODE(obj)->nd_ainfo);
- }
- break;
- case NODE_ALLOCA:
- xfree(RNODE(obj)->u1.node);
- break;
+ n->flags = T_NODE;
+ nd_set_type(n, type);
+ n->u1.value = a0;
+ n->u2.value = a1;
+ n->u3.value = a2;
+ n->nd_loc.beg_pos.lineno = 0;
+ n->nd_loc.beg_pos.column = 0;
+ n->nd_loc.end_pos.lineno = 0;
+ n->nd_loc.end_pos.column = 0;
+}
+
+typedef struct node_buffer_elem_struct {
+ struct node_buffer_elem_struct *next;
+ NODE buf[FLEX_ARY_LEN];
+} node_buffer_elem_t;
+
+struct node_buffer_struct {
+ long idx, len;
+ node_buffer_elem_t *head;
+ node_buffer_elem_t *last;
+ VALUE mark_ary;
+};
+
+static node_buffer_t *
+rb_node_buffer_new(void)
+{
+ node_buffer_t *nb = xmalloc(sizeof(node_buffer_t) + offsetof(node_buffer_elem_t, buf) + 16 * sizeof(NODE));
+ nb->idx = 0;
+ nb->len = 16;
+ nb->head = nb->last = (node_buffer_elem_t*) &nb[1];
+ nb->head->next = NULL;
+ nb->mark_ary = rb_ary_tmp_new(0);
+ return nb;
+}
+
+static void
+rb_node_buffer_free(node_buffer_t *nb)
+{
+ node_buffer_elem_t *nbe = nb->head;
+
+ while (nbe != nb->last) {
+ void *buf = nbe;
+ nbe = nbe->next;
+ xfree(buf);
}
+ xfree(nb);
}
-size_t
-rb_node_memsize(VALUE obj)
+NODE *
+rb_ast_newnode(rb_ast_t *ast)
{
- size_t size = 0;
- switch (nd_type(obj)) {
- case NODE_SCOPE:
- if (RNODE(obj)->nd_tbl) {
- size += (RNODE(obj)->nd_tbl[0]+1) * sizeof(*RNODE(obj)->nd_tbl);
- }
- break;
- case NODE_ARGS:
- if (RNODE(obj)->nd_ainfo) {
- size += sizeof(*RNODE(obj)->nd_ainfo);
- }
- break;
- case NODE_ALLOCA:
- size += RNODE(obj)->nd_cnt * sizeof(VALUE);
- break;
+ node_buffer_t *nb = ast->node_buffer;
+ if (nb->idx >= nb->len) {
+ long n = nb->len * 2;
+ node_buffer_elem_t *nbe;
+ nbe = xmalloc(offsetof(node_buffer_elem_t, buf) + n * sizeof(NODE));
+ nb->idx = 0;
+ nb->len = n;
+ nbe->next = nb->head;
+ nb->head = nbe;
}
- return size;
+ return &nb->head->buf[nb->idx++];
}
-VALUE
-rb_gc_mark_node(NODE *obj)
+void
+rb_ast_delete_node(rb_ast_t *ast, NODE *n)
{
- switch (nd_type(obj)) {
- case NODE_IF: /* 1,2,3 */
- case NODE_FOR:
- case NODE_ITER:
- case NODE_WHEN:
- case NODE_MASGN:
- case NODE_RESCUE:
- case NODE_RESBODY:
- case NODE_CLASS:
- case NODE_BLOCK_PASS:
- case NODE_MATCH2:
- rb_gc_mark(RNODE(obj)->u2.value);
- /* fall through */
- case NODE_BLOCK: /* 1,3 */
- case NODE_ARRAY:
- case NODE_DSTR:
- case NODE_DXSTR:
- case NODE_DREGX:
- case NODE_DREGX_ONCE:
- case NODE_ENSURE:
- case NODE_CALL:
- case NODE_DEFS:
- case NODE_OP_ASGN1:
- rb_gc_mark(RNODE(obj)->u1.value);
- /* fall through */
- case NODE_SUPER: /* 3 */
- case NODE_FCALL:
- case NODE_DEFN:
- case NODE_ARGS_AUX:
- return RNODE(obj)->u3.value;
+ (void)ast;
+ (void)n;
+ /* should we implement freelist? */
+}
- case NODE_WHILE: /* 1,2 */
- case NODE_UNTIL:
- case NODE_AND:
- case NODE_OR:
- case NODE_CASE:
- case NODE_SCLASS:
- case NODE_DOT2:
- case NODE_DOT3:
- case NODE_FLIP2:
- case NODE_FLIP3:
- case NODE_MATCH3:
- case NODE_OP_ASGN_OR:
- case NODE_OP_ASGN_AND:
- case NODE_MODULE:
- case NODE_ALIAS:
- case NODE_VALIAS:
- case NODE_ARGSCAT:
- rb_gc_mark(RNODE(obj)->u1.value);
- /* fall through */
- case NODE_GASGN: /* 2 */
- case NODE_LASGN:
- case NODE_DASGN:
- case NODE_DASGN_CURR:
- case NODE_IASGN:
- case NODE_IASGN2:
- case NODE_CVASGN:
- case NODE_COLON3:
- case NODE_OPT_N:
- case NODE_EVSTR:
- case NODE_UNDEF:
- case NODE_POSTEXE:
- return RNODE(obj)->u2.value;
+rb_ast_t *
+rb_ast_new(void)
+{
+ node_buffer_t *nb = rb_node_buffer_new();
+ VALUE mark_ary = nb->mark_ary;
+ rb_ast_t *ast = (rb_ast_t *)rb_imemo_new(imemo_ast, 0, 0, 0, (VALUE)nb);
+ RB_OBJ_WRITTEN(ast, Qnil, mark_ary);
+ return ast;
+}
- case NODE_HASH: /* 1 */
- case NODE_LIT:
- case NODE_STR:
- case NODE_XSTR:
- case NODE_DEFINED:
- case NODE_MATCH:
- case NODE_RETURN:
- case NODE_BREAK:
- case NODE_NEXT:
- case NODE_YIELD:
- case NODE_COLON2:
- case NODE_SPLAT:
- case NODE_TO_ARY:
- return RNODE(obj)->u1.value;
+void
+rb_ast_mark(rb_ast_t *ast)
+{
+ if (ast->node_buffer) rb_gc_mark(ast->node_buffer->mark_ary);
+}
- case NODE_SCOPE: /* 2,3 */
- case NODE_CDECL:
- case NODE_OPT_ARG:
- rb_gc_mark(RNODE(obj)->u3.value);
- return RNODE(obj)->u2.value;
-
- case NODE_ARGS: /* custom */
- {
- struct rb_args_info *args = obj->u3.args;
- if (args) {
- if (args->pre_init) rb_gc_mark((VALUE)args->pre_init);
- if (args->post_init) rb_gc_mark((VALUE)args->post_init);
- if (args->opt_args) rb_gc_mark((VALUE)args->opt_args);
- if (args->kw_args) rb_gc_mark((VALUE)args->kw_args);
- if (args->kw_rest_arg) rb_gc_mark((VALUE)args->kw_rest_arg);
- }
- }
- return RNODE(obj)->u2.value;
+void
+rb_ast_free(rb_ast_t *ast)
+{
+ if (ast->node_buffer) {
+ rb_node_buffer_free(ast->node_buffer);
+ ast->node_buffer = 0;
+ }
+}
- case NODE_ZARRAY: /* - */
- case NODE_ZSUPER:
- case NODE_VCALL:
- case NODE_GVAR:
- case NODE_LVAR:
- case NODE_DVAR:
- case NODE_IVAR:
- case NODE_CVAR:
- case NODE_NTH_REF:
- case NODE_BACK_REF:
- case NODE_REDO:
- case NODE_RETRY:
- case NODE_SELF:
- case NODE_NIL:
- case NODE_TRUE:
- case NODE_FALSE:
- case NODE_ERRINFO:
- case NODE_BLOCK_ARG:
- break;
- case NODE_ALLOCA:
- rb_gc_mark_locations((VALUE*)RNODE(obj)->u1.value,
- (VALUE*)RNODE(obj)->u1.value + RNODE(obj)->u3.cnt);
- rb_gc_mark(RNODE(obj)->u2.value);
- break;
+void
+rb_ast_dispose(rb_ast_t *ast)
+{
+ rb_ast_free(ast);
+}
- default: /* unlisted NODE */
- rb_gc_mark_maybe(RNODE(obj)->u1.value);
- rb_gc_mark_maybe(RNODE(obj)->u2.value);
- rb_gc_mark_maybe(RNODE(obj)->u3.value);
- }
- return 0;
+void
+rb_ast_add_mark_object(rb_ast_t *ast, VALUE obj)
+{
+ rb_ary_push(ast->node_buffer->mark_ary, obj);
}
diff --git a/node.h b/node.h
index 8d2ea53c73..0f5a2a923a 100644
--- a/node.h
+++ b/node.h
@@ -21,225 +21,132 @@ extern "C" {
enum node_type {
NODE_SCOPE,
-#define NODE_SCOPE NODE_SCOPE
NODE_BLOCK,
-#define NODE_BLOCK NODE_BLOCK
NODE_IF,
-#define NODE_IF NODE_IF
+ NODE_UNLESS,
NODE_CASE,
-#define NODE_CASE NODE_CASE
+ NODE_CASE2,
NODE_WHEN,
-#define NODE_WHEN NODE_WHEN
- NODE_OPT_N,
-#define NODE_OPT_N NODE_OPT_N
NODE_WHILE,
-#define NODE_WHILE NODE_WHILE
NODE_UNTIL,
-#define NODE_UNTIL NODE_UNTIL
NODE_ITER,
-#define NODE_ITER NODE_ITER
NODE_FOR,
-#define NODE_FOR NODE_FOR
+ NODE_FOR_MASGN,
NODE_BREAK,
-#define NODE_BREAK NODE_BREAK
NODE_NEXT,
-#define NODE_NEXT NODE_NEXT
NODE_REDO,
-#define NODE_REDO NODE_REDO
NODE_RETRY,
-#define NODE_RETRY NODE_RETRY
NODE_BEGIN,
-#define NODE_BEGIN NODE_BEGIN
NODE_RESCUE,
-#define NODE_RESCUE NODE_RESCUE
NODE_RESBODY,
-#define NODE_RESBODY NODE_RESBODY
NODE_ENSURE,
-#define NODE_ENSURE NODE_ENSURE
NODE_AND,
-#define NODE_AND NODE_AND
NODE_OR,
-#define NODE_OR NODE_OR
NODE_MASGN,
-#define NODE_MASGN NODE_MASGN
NODE_LASGN,
-#define NODE_LASGN NODE_LASGN
NODE_DASGN,
-#define NODE_DASGN NODE_DASGN
NODE_DASGN_CURR,
-#define NODE_DASGN_CURR NODE_DASGN_CURR
NODE_GASGN,
-#define NODE_GASGN NODE_GASGN
NODE_IASGN,
-#define NODE_IASGN NODE_IASGN
- NODE_IASGN2,
-#define NODE_IASGN2 NODE_IASGN2
NODE_CDECL,
-#define NODE_CDECL NODE_CDECL
NODE_CVASGN,
-#define NODE_CVASGN NODE_CVASGN
- NODE_CVDECL,
-#define NODE_CVDECL NODE_CVDECL
NODE_OP_ASGN1,
-#define NODE_OP_ASGN1 NODE_OP_ASGN1
NODE_OP_ASGN2,
-#define NODE_OP_ASGN2 NODE_OP_ASGN2
NODE_OP_ASGN_AND,
-#define NODE_OP_ASGN_AND NODE_OP_ASGN_AND
NODE_OP_ASGN_OR,
-#define NODE_OP_ASGN_OR NODE_OP_ASGN_OR
NODE_OP_CDECL,
-#define NODE_OP_CDECL NODE_OP_CDECL
NODE_CALL,
-#define NODE_CALL NODE_CALL
+ NODE_OPCALL,
NODE_FCALL,
-#define NODE_FCALL NODE_FCALL
NODE_VCALL,
-#define NODE_VCALL NODE_VCALL
NODE_QCALL,
-#define NODE_QCALL NODE_QCALL
NODE_SUPER,
-#define NODE_SUPER NODE_SUPER
NODE_ZSUPER,
-#define NODE_ZSUPER NODE_ZSUPER
NODE_ARRAY,
-#define NODE_ARRAY NODE_ARRAY
NODE_ZARRAY,
-#define NODE_ZARRAY NODE_ZARRAY
NODE_VALUES,
-#define NODE_VALUES NODE_VALUES
NODE_HASH,
-#define NODE_HASH NODE_HASH
NODE_RETURN,
-#define NODE_RETURN NODE_RETURN
NODE_YIELD,
-#define NODE_YIELD NODE_YIELD
NODE_LVAR,
-#define NODE_LVAR NODE_LVAR
NODE_DVAR,
-#define NODE_DVAR NODE_DVAR
NODE_GVAR,
-#define NODE_GVAR NODE_GVAR
NODE_IVAR,
-#define NODE_IVAR NODE_IVAR
NODE_CONST,
-#define NODE_CONST NODE_CONST
NODE_CVAR,
-#define NODE_CVAR NODE_CVAR
NODE_NTH_REF,
-#define NODE_NTH_REF NODE_NTH_REF
NODE_BACK_REF,
-#define NODE_BACK_REF NODE_BACK_REF
NODE_MATCH,
-#define NODE_MATCH NODE_MATCH
NODE_MATCH2,
-#define NODE_MATCH2 NODE_MATCH2
NODE_MATCH3,
-#define NODE_MATCH3 NODE_MATCH3
NODE_LIT,
-#define NODE_LIT NODE_LIT
NODE_STR,
-#define NODE_STR NODE_STR
NODE_DSTR,
-#define NODE_DSTR NODE_DSTR
NODE_XSTR,
-#define NODE_XSTR NODE_XSTR
NODE_DXSTR,
-#define NODE_DXSTR NODE_DXSTR
NODE_EVSTR,
-#define NODE_EVSTR NODE_EVSTR
NODE_DREGX,
-#define NODE_DREGX NODE_DREGX
- NODE_DREGX_ONCE,
-#define NODE_DREGX_ONCE NODE_DREGX_ONCE
+ NODE_ONCE,
NODE_ARGS,
-#define NODE_ARGS NODE_ARGS
NODE_ARGS_AUX,
-#define NODE_ARGS_AUX NODE_ARGS_AUX
NODE_OPT_ARG,
-#define NODE_OPT_ARG NODE_OPT_ARG
NODE_KW_ARG,
-#define NODE_KW_ARG NODE_KW_ARG
NODE_POSTARG,
-#define NODE_POSTARG NODE_POSTARG
NODE_ARGSCAT,
-#define NODE_ARGSCAT NODE_ARGSCAT
NODE_ARGSPUSH,
-#define NODE_ARGSPUSH NODE_ARGSPUSH
NODE_SPLAT,
-#define NODE_SPLAT NODE_SPLAT
- NODE_TO_ARY,
-#define NODE_TO_ARY NODE_TO_ARY
- NODE_BLOCK_ARG,
-#define NODE_BLOCK_ARG NODE_BLOCK_ARG
NODE_BLOCK_PASS,
-#define NODE_BLOCK_PASS NODE_BLOCK_PASS
NODE_DEFN,
-#define NODE_DEFN NODE_DEFN
NODE_DEFS,
-#define NODE_DEFS NODE_DEFS
NODE_ALIAS,
-#define NODE_ALIAS NODE_ALIAS
NODE_VALIAS,
-#define NODE_VALIAS NODE_VALIAS
NODE_UNDEF,
-#define NODE_UNDEF NODE_UNDEF
NODE_CLASS,
-#define NODE_CLASS NODE_CLASS
NODE_MODULE,
-#define NODE_MODULE NODE_MODULE
NODE_SCLASS,
-#define NODE_SCLASS NODE_SCLASS
NODE_COLON2,
-#define NODE_COLON2 NODE_COLON2
NODE_COLON3,
-#define NODE_COLON3 NODE_COLON3
NODE_DOT2,
-#define NODE_DOT2 NODE_DOT2
NODE_DOT3,
-#define NODE_DOT3 NODE_DOT3
NODE_FLIP2,
-#define NODE_FLIP2 NODE_FLIP2
NODE_FLIP3,
-#define NODE_FLIP3 NODE_FLIP3
NODE_SELF,
-#define NODE_SELF NODE_SELF
NODE_NIL,
-#define NODE_NIL NODE_NIL
NODE_TRUE,
-#define NODE_TRUE NODE_TRUE
NODE_FALSE,
-#define NODE_FALSE NODE_FALSE
NODE_ERRINFO,
-#define NODE_ERRINFO NODE_ERRINFO
NODE_DEFINED,
-#define NODE_DEFINED NODE_DEFINED
NODE_POSTEXE,
-#define NODE_POSTEXE NODE_POSTEXE
- NODE_ALLOCA,
-#define NODE_ALLOCA NODE_ALLOCA
- NODE_BMETHOD,
-#define NODE_BMETHOD NODE_BMETHOD
NODE_DSYM,
-#define NODE_DSYM NODE_DSYM
NODE_ATTRASGN,
-#define NODE_ATTRASGN NODE_ATTRASGN
- NODE_PRELUDE,
-#define NODE_PRELUDE NODE_PRELUDE
NODE_LAMBDA,
-#define NODE_LAMBDA NODE_LAMBDA
NODE_LAST
-#define NODE_LAST NODE_LAST
};
+typedef struct rb_code_position_struct {
+ int lineno;
+ int column;
+} rb_code_position_t;
+
+typedef struct rb_code_location_struct {
+ rb_code_position_t beg_pos;
+ rb_code_position_t end_pos;
+} rb_code_location_t;
+
+static inline rb_code_location_t code_loc_gen(rb_code_location_t *loc1, rb_code_location_t *loc2)
+{
+ rb_code_location_t loc;
+ loc.beg_pos = loc1->beg_pos;
+ loc.end_pos = loc2->end_pos;
+ return loc;
+}
+
typedef struct RNode {
VALUE flags;
- VALUE nd_reserved; /* ex nd_file */
union {
struct RNode *node;
ID id;
VALUE value;
- VALUE (*cfunc)(ANYARGS);
ID *tbl;
} u1;
union {
@@ -254,15 +161,16 @@ typedef struct RNode {
long state;
struct rb_global_entry *entry;
struct rb_args_info *args;
- long cnt;
VALUE value;
} u3;
+ rb_code_location_t nd_loc;
+ int node_id;
} NODE;
#define RNODE(obj) (R_CAST(RNode)(obj))
/* FL : 0..4: T_TYPES, 5: KEEP_WB, 6: PROMOTED, 7: FINALIZE, 8: TAINT, 9: UNTRUSTED, 10: EXIVAR, 11: FREEZE */
-/* NODE_FL: 0..4: T_TYPES, 5: KEEP_WB, 6: PROMOTED, 7: NODE_FL_NEWLINE|NODE_FL_CREF_PUSHED_BY_EVAL,
+/* NODE_FL: 0..4: T_TYPES, 5: KEEP_WB, 6: PROMOTED, 7: NODE_FL_NEWLINE,
* 8..14: nd_type,
* 15..: nd_line
*/
@@ -271,17 +179,31 @@ typedef struct RNode {
#define NODE_TYPESHIFT 8
#define NODE_TYPEMASK (((VALUE)0x7f)<<NODE_TYPESHIFT)
-#define nd_type(n) ((int) (((RNODE(n))->flags & NODE_TYPEMASK)>>NODE_TYPESHIFT))
+#define nd_type(n) ((int) (((n)->flags & NODE_TYPEMASK)>>NODE_TYPESHIFT))
#define nd_set_type(n,t) \
- RNODE(n)->flags=((RNODE(n)->flags&~NODE_TYPEMASK)|((((unsigned long)(t))<<NODE_TYPESHIFT)&NODE_TYPEMASK))
+ (n)->flags=(((n)->flags&~NODE_TYPEMASK)|((((unsigned long)(t))<<NODE_TYPESHIFT)&NODE_TYPEMASK))
#define NODE_LSHIFT (NODE_TYPESHIFT+7)
#define NODE_LMASK (((SIGNED_VALUE)1<<(sizeof(VALUE)*CHAR_BIT-NODE_LSHIFT))-1)
-#define nd_line(n) (int)(RNODE(n)->flags>>NODE_LSHIFT)
+#define nd_line(n) (int)(((SIGNED_VALUE)(n)->flags)>>NODE_LSHIFT)
#define nd_set_line(n,l) \
- RNODE(n)->flags=((RNODE(n)->flags&~((VALUE)(-1)<<NODE_LSHIFT))|((VALUE)((l)&NODE_LMASK)<<NODE_LSHIFT))
-
-#define nd_refinements_ nd_reserved
+ (n)->flags=(((n)->flags&~((VALUE)(-1)<<NODE_LSHIFT))|((VALUE)((l)&NODE_LMASK)<<NODE_LSHIFT))
+
+#define nd_first_column(n) ((int)((n)->nd_loc.beg_pos.column))
+#define nd_set_first_column(n, v) ((n)->nd_loc.beg_pos.column = (v))
+#define nd_first_lineno(n) ((int)((n)->nd_loc.beg_pos.lineno))
+#define nd_set_first_lineno(n, v) ((n)->nd_loc.beg_pos.lineno = (v))
+#define nd_first_loc(n) ((n)->nd_loc.beg_pos)
+#define nd_set_first_loc(n, v) (nd_first_loc(n) = (v))
+
+#define nd_last_column(n) ((int)((n)->nd_loc.end_pos.column))
+#define nd_set_last_column(n, v) ((n)->nd_loc.end_pos.column = (v))
+#define nd_last_lineno(n) ((int)((n)->nd_loc.end_pos.lineno))
+#define nd_set_last_lineno(n, v) ((n)->nd_loc.end_pos.lineno = (v))
+#define nd_last_loc(n) ((n)->nd_loc.end_pos)
+#define nd_set_last_loc(n, v) (nd_last_loc(n) = (v))
+#define nd_node_id(n) ((n)->node_id)
+#define nd_set_node_id(n,id) ((n)->node_id = (id))
#define nd_head u1.node
#define nd_alen u2.argc
@@ -291,8 +213,6 @@ typedef struct RNode {
#define nd_body u2.node
#define nd_else u3.node
-#define nd_orig u3.value
-
#define nd_resq u2.node
#define nd_ensr u3.node
@@ -307,7 +227,6 @@ typedef struct RNode {
#define nd_cval u3.value
#define nd_oid u1.id
-#define nd_cnt u3.cnt
#define nd_tbl u1.tbl
#define nd_var u1.node
@@ -318,7 +237,6 @@ typedef struct RNode {
#define nd_lit u1.value
-#define nd_frml u2.argc
#define nd_rest u1.id
#define nd_opt u1.node
#define nd_pid u1.id
@@ -329,18 +247,11 @@ typedef struct RNode {
#define nd_args u3.node
#define nd_ainfo u3.args
-#define nd_noex u3.id
#define nd_defn u3.node
-#define nd_cfnc u1.cfunc
-#define nd_argc u2.argc
-
#define nd_cpath u1.node
#define nd_super u3.node
-#define nd_modl u1.id
-#define nd_clss_ u1.value
-
#define nd_beg u1.node
#define nd_end u2.node
#define nd_state u3.state
@@ -349,137 +260,155 @@ typedef struct RNode {
#define nd_nth u2.argc
#define nd_tag u1.id
-#define nd_tval u2.value
-
-#define nd_visi_ u2.argc
-
-#define NEW_NODE(t,a0,a1,a2) rb_node_newnode((t),(VALUE)(a0),(VALUE)(a1),(VALUE)(a2))
-
-#define NEW_DEFN(i,a,d,p) NEW_NODE(NODE_DEFN,0,i,NEW_SCOPE(a,d))
-#define NEW_DEFS(r,i,a,d) NEW_NODE(NODE_DEFS,r,i,NEW_SCOPE(a,d))
-#define NEW_SCOPE(a,b) NEW_NODE(NODE_SCOPE,local_tbl(),b,a)
-#define NEW_BLOCK(a) NEW_NODE(NODE_BLOCK,a,0,0)
-#define NEW_IF(c,t,e) NEW_NODE(NODE_IF,c,t,e)
-#define NEW_UNLESS(c,t,e) NEW_IF(c,e,t)
-#define NEW_CASE(h,b) NEW_NODE(NODE_CASE,h,b,0)
-#define NEW_WHEN(c,t,e) NEW_NODE(NODE_WHEN,c,t,e)
-#define NEW_OPT_N(b) NEW_NODE(NODE_OPT_N,0,b,0)
-#define NEW_WHILE(c,b,n) NEW_NODE(NODE_WHILE,c,b,n)
-#define NEW_UNTIL(c,b,n) NEW_NODE(NODE_UNTIL,c,b,n)
-#define NEW_FOR(v,i,b) NEW_NODE(NODE_FOR,v,b,i)
-#define NEW_ITER(a,b) NEW_NODE(NODE_ITER,0,NEW_SCOPE(a,b),0)
-#define NEW_LAMBDA(a,b) NEW_NODE(NODE_LAMBDA,0,NEW_SCOPE(a,b),0)
-#define NEW_BREAK(s) NEW_NODE(NODE_BREAK,s,0,0)
-#define NEW_NEXT(s) NEW_NODE(NODE_NEXT,s,0,0)
-#define NEW_REDO() NEW_NODE(NODE_REDO,0,0,0)
-#define NEW_RETRY() NEW_NODE(NODE_RETRY,0,0,0)
-#define NEW_BEGIN(b) NEW_NODE(NODE_BEGIN,0,b,0)
-#define NEW_RESCUE(b,res,e) NEW_NODE(NODE_RESCUE,b,res,e)
-#define NEW_RESBODY(a,ex,n) NEW_NODE(NODE_RESBODY,n,ex,a)
-#define NEW_ENSURE(b,en) NEW_NODE(NODE_ENSURE,b,0,en)
-#define NEW_RETURN(s) NEW_NODE(NODE_RETURN,s,0,0)
-#define NEW_YIELD(a) NEW_NODE(NODE_YIELD,a,0,0)
-#define NEW_LIST(a) NEW_ARRAY(a)
-#define NEW_ARRAY(a) NEW_NODE(NODE_ARRAY,a,1,0)
-#define NEW_ZARRAY() NEW_NODE(NODE_ZARRAY,0,0,0)
-#define NEW_HASH(a) NEW_NODE(NODE_HASH,a,0,0)
-#define NEW_MASGN(l,r) NEW_NODE(NODE_MASGN,l,0,r)
-#define NEW_GASGN(v,val) NEW_NODE(NODE_GASGN,v,val,rb_global_entry(v))
-#define NEW_LASGN(v,val) NEW_NODE(NODE_LASGN,v,val,0)
-#define NEW_DASGN(v,val) NEW_NODE(NODE_DASGN,v,val,0)
-#define NEW_DASGN_CURR(v,val) NEW_NODE(NODE_DASGN_CURR,v,val,0)
-#define NEW_IASGN(v,val) NEW_NODE(NODE_IASGN,v,val,0)
-#define NEW_IASGN2(v,val) NEW_NODE(NODE_IASGN2,v,val,0)
-#define NEW_CDECL(v,val,path) NEW_NODE(NODE_CDECL,v,val,path)
-#define NEW_CVASGN(v,val) NEW_NODE(NODE_CVASGN,v,val,0)
-#define NEW_CVDECL(v,val) NEW_NODE(NODE_CVDECL,v,val,0)
-#define NEW_OP_ASGN1(p,id,a) NEW_NODE(NODE_OP_ASGN1,p,id,a)
-#define NEW_OP_ASGN2(r,t,i,o,val) NEW_NODE(NODE_OP_ASGN2,r,val,NEW_OP_ASGN22(i,o,t))
-#define NEW_OP_ASGN22(i,o,t) NEW_NODE(NODE_OP_ASGN2,i,o,t)
-#define NEW_OP_ASGN_OR(i,val) NEW_NODE(NODE_OP_ASGN_OR,i,val,0)
-#define NEW_OP_ASGN_AND(i,val) NEW_NODE(NODE_OP_ASGN_AND,i,val,0)
-#define NEW_OP_CDECL(v,op,val) NEW_NODE(NODE_OP_CDECL,v,val,op)
-#define NEW_GVAR(v) NEW_NODE(NODE_GVAR,v,0,rb_global_entry(v))
-#define NEW_LVAR(v) NEW_NODE(NODE_LVAR,v,0,0)
-#define NEW_DVAR(v) NEW_NODE(NODE_DVAR,v,0,0)
-#define NEW_IVAR(v) NEW_NODE(NODE_IVAR,v,0,0)
-#define NEW_CONST(v) NEW_NODE(NODE_CONST,v,0,0)
-#define NEW_CVAR(v) NEW_NODE(NODE_CVAR,v,0,0)
-#define NEW_NTH_REF(n) NEW_NODE(NODE_NTH_REF,0,n,0)
-#define NEW_BACK_REF(n) NEW_NODE(NODE_BACK_REF,0,n,0)
-#define NEW_MATCH(c) NEW_NODE(NODE_MATCH,c,0,0)
-#define NEW_MATCH2(n1,n2) NEW_NODE(NODE_MATCH2,n1,n2,0)
-#define NEW_MATCH3(r,n2) NEW_NODE(NODE_MATCH3,r,n2,0)
-#define NEW_LIT(l) NEW_NODE(NODE_LIT,l,0,0)
-#define NEW_STR(s) NEW_NODE(NODE_STR,s,0,0)
-#define NEW_DSTR(s) NEW_NODE(NODE_DSTR,s,1,0)
-#define NEW_XSTR(s) NEW_NODE(NODE_XSTR,s,0,0)
-#define NEW_DXSTR(s) NEW_NODE(NODE_DXSTR,s,0,0)
-#define NEW_DSYM(s) NEW_NODE(NODE_DSYM,s,0,0)
-#define NEW_EVSTR(n) NEW_NODE(NODE_EVSTR,0,(n),0)
-#define NEW_CALL(r,m,a) NEW_NODE(NODE_CALL,r,m,a)
-#define NEW_FCALL(m,a) NEW_NODE(NODE_FCALL,0,m,a)
-#define NEW_VCALL(m) NEW_NODE(NODE_VCALL,0,m,0)
-#define NEW_SUPER(a) NEW_NODE(NODE_SUPER,0,0,a)
-#define NEW_ZSUPER() NEW_NODE(NODE_ZSUPER,0,0,0)
-#define NEW_ARGS_AUX(r,b) NEW_NODE(NODE_ARGS_AUX,r,b,0)
-#define NEW_OPT_ARG(i,v) NEW_NODE(NODE_OPT_ARG,i,v,0)
-#define NEW_KW_ARG(i,v) NEW_NODE(NODE_KW_ARG,i,v,0)
-#define NEW_POSTARG(i,v) NEW_NODE(NODE_POSTARG,i,v,0)
-#define NEW_ARGSCAT(a,b) NEW_NODE(NODE_ARGSCAT,a,b,0)
-#define NEW_ARGSPUSH(a,b) NEW_NODE(NODE_ARGSPUSH,a,b,0)
-#define NEW_SPLAT(a) NEW_NODE(NODE_SPLAT,a,0,0)
-#define NEW_TO_ARY(a) NEW_NODE(NODE_TO_ARY,a,0,0)
-#define NEW_BLOCK_ARG(v) NEW_NODE(NODE_BLOCK_ARG,v,0,local_cnt(v))
-#define NEW_BLOCK_PASS(b) NEW_NODE(NODE_BLOCK_PASS,0,b,0)
-#define NEW_ALIAS(n,o) NEW_NODE(NODE_ALIAS,n,o,0)
-#define NEW_VALIAS(n,o) NEW_NODE(NODE_VALIAS,n,o,0)
-#define NEW_UNDEF(i) NEW_NODE(NODE_UNDEF,0,i,0)
-#define NEW_CLASS(n,b,s) NEW_NODE(NODE_CLASS,n,NEW_SCOPE(0,b),(s))
-#define NEW_SCLASS(r,b) NEW_NODE(NODE_SCLASS,r,NEW_SCOPE(0,b),0)
-#define NEW_MODULE(n,b) NEW_NODE(NODE_MODULE,n,NEW_SCOPE(0,b),0)
-#define NEW_COLON2(c,i) NEW_NODE(NODE_COLON2,c,i,0)
-#define NEW_COLON3(i) NEW_NODE(NODE_COLON3,0,i,0)
-#define NEW_DOT2(b,e) NEW_NODE(NODE_DOT2,b,e,0)
-#define NEW_DOT3(b,e) NEW_NODE(NODE_DOT3,b,e,0)
-#define NEW_SELF() NEW_NODE(NODE_SELF,0,0,0)
-#define NEW_NIL() NEW_NODE(NODE_NIL,0,0,0)
-#define NEW_TRUE() NEW_NODE(NODE_TRUE,0,0,0)
-#define NEW_FALSE() NEW_NODE(NODE_FALSE,0,0,0)
-#define NEW_ERRINFO() NEW_NODE(NODE_ERRINFO,0,0,0)
-#define NEW_DEFINED(e) NEW_NODE(NODE_DEFINED,e,0,0)
-#define NEW_PREEXE(b) NEW_SCOPE(b)
-#define NEW_POSTEXE(b) NEW_NODE(NODE_POSTEXE,0,b,0)
-#define NEW_BMETHOD(b) NEW_NODE(NODE_BMETHOD,0,0,b)
-#define NEW_ATTRASGN(r,m,a) NEW_NODE(NODE_ATTRASGN,r,m,a)
-#define NEW_PRELUDE(p,b,o) NEW_NODE(NODE_PRELUDE,p,b,o)
+
+#define nd_alias u1.id
+#define nd_orig u2.id
+#define nd_undef u2.node
+
+#define NEW_NODE(t,a0,a1,a2,loc) rb_node_newnode((t),(VALUE)(a0),(VALUE)(a1),(VALUE)(a2),loc)
+
+#define NEW_DEFN(i,a,d,loc) NEW_NODE(NODE_DEFN,0,i,NEW_SCOPE(a,d,loc),loc)
+#define NEW_DEFS(r,i,a,d,loc) NEW_NODE(NODE_DEFS,r,i,NEW_SCOPE(a,d,loc),loc)
+#define NEW_SCOPE(a,b,loc) NEW_NODE(NODE_SCOPE,local_tbl(p),b,a,loc)
+#define NEW_BLOCK(a,loc) NEW_NODE(NODE_BLOCK,a,0,0,loc)
+#define NEW_IF(c,t,e,loc) NEW_NODE(NODE_IF,c,t,e,loc)
+#define NEW_UNLESS(c,t,e,loc) NEW_NODE(NODE_UNLESS,c,t,e,loc)
+#define NEW_CASE(h,b,loc) NEW_NODE(NODE_CASE,h,b,0,loc)
+#define NEW_CASE2(b,loc) NEW_NODE(NODE_CASE2,0,b,0,loc)
+#define NEW_WHEN(c,t,e,loc) NEW_NODE(NODE_WHEN,c,t,e,loc)
+#define NEW_WHILE(c,b,n,loc) NEW_NODE(NODE_WHILE,c,b,n,loc)
+#define NEW_UNTIL(c,b,n,loc) NEW_NODE(NODE_UNTIL,c,b,n,loc)
+#define NEW_FOR(i,b,loc) NEW_NODE(NODE_FOR,0,b,i,loc)
+#define NEW_FOR_MASGN(v,loc) NEW_NODE(NODE_FOR_MASGN,v,0,0,loc)
+#define NEW_ITER(a,b,loc) NEW_NODE(NODE_ITER,0,NEW_SCOPE(a,b,loc),0,loc)
+#define NEW_LAMBDA(a,b,loc) NEW_NODE(NODE_LAMBDA,0,NEW_SCOPE(a,b,loc),0,loc)
+#define NEW_BREAK(s,loc) NEW_NODE(NODE_BREAK,s,0,0,loc)
+#define NEW_NEXT(s,loc) NEW_NODE(NODE_NEXT,s,0,0,loc)
+#define NEW_REDO(loc) NEW_NODE(NODE_REDO,0,0,0,loc)
+#define NEW_RETRY(loc) NEW_NODE(NODE_RETRY,0,0,0,loc)
+#define NEW_BEGIN(b,loc) NEW_NODE(NODE_BEGIN,0,b,0,loc)
+#define NEW_RESCUE(b,res,e,loc) NEW_NODE(NODE_RESCUE,b,res,e,loc)
+#define NEW_RESBODY(a,ex,n,loc) NEW_NODE(NODE_RESBODY,n,ex,a,loc)
+#define NEW_ENSURE(b,en,loc) NEW_NODE(NODE_ENSURE,b,0,en,loc)
+#define NEW_RETURN(s,loc) NEW_NODE(NODE_RETURN,s,0,0,loc)
+#define NEW_YIELD(a,loc) NEW_NODE(NODE_YIELD,a,0,0,loc)
+#define NEW_LIST(a,loc) NEW_ARRAY(a,loc)
+#define NEW_ARRAY(a,loc) NEW_NODE(NODE_ARRAY,a,1,0,loc)
+#define NEW_ZARRAY(loc) NEW_NODE(NODE_ZARRAY,0,0,0,loc)
+#define NEW_HASH(a,loc) NEW_NODE(NODE_HASH,a,0,0,loc)
+#define NEW_MASGN(l,r,loc) NEW_NODE(NODE_MASGN,l,0,r,loc)
+#define NEW_GASGN(v,val,loc) NEW_NODE(NODE_GASGN,v,val,rb_global_entry(v),loc)
+#define NEW_LASGN(v,val,loc) NEW_NODE(NODE_LASGN,v,val,0,loc)
+#define NEW_DASGN(v,val,loc) NEW_NODE(NODE_DASGN,v,val,0,loc)
+#define NEW_DASGN_CURR(v,val,loc) NEW_NODE(NODE_DASGN_CURR,v,val,0,loc)
+#define NEW_IASGN(v,val,loc) NEW_NODE(NODE_IASGN,v,val,0,loc)
+#define NEW_CDECL(v,val,path,loc) NEW_NODE(NODE_CDECL,v,val,path,loc)
+#define NEW_CVASGN(v,val,loc) NEW_NODE(NODE_CVASGN,v,val,0,loc)
+#define NEW_OP_ASGN1(p,id,a,loc) NEW_NODE(NODE_OP_ASGN1,p,id,a,loc)
+#define NEW_OP_ASGN2(r,t,i,o,val,loc) NEW_NODE(NODE_OP_ASGN2,r,val,NEW_OP_ASGN22(i,o,t,loc),loc)
+#define NEW_OP_ASGN22(i,o,t,loc) NEW_NODE(NODE_OP_ASGN2,i,o,t,loc)
+#define NEW_OP_ASGN_OR(i,val,loc) NEW_NODE(NODE_OP_ASGN_OR,i,val,0,loc)
+#define NEW_OP_ASGN_AND(i,val,loc) NEW_NODE(NODE_OP_ASGN_AND,i,val,0,loc)
+#define NEW_OP_CDECL(v,op,val,loc) NEW_NODE(NODE_OP_CDECL,v,val,op,loc)
+#define NEW_GVAR(v,loc) NEW_NODE(NODE_GVAR,v,0,rb_global_entry(v),loc)
+#define NEW_LVAR(v,loc) NEW_NODE(NODE_LVAR,v,0,0,loc)
+#define NEW_DVAR(v,loc) NEW_NODE(NODE_DVAR,v,0,0,loc)
+#define NEW_IVAR(v,loc) NEW_NODE(NODE_IVAR,v,0,0,loc)
+#define NEW_CONST(v,loc) NEW_NODE(NODE_CONST,v,0,0,loc)
+#define NEW_CVAR(v,loc) NEW_NODE(NODE_CVAR,v,0,0,loc)
+#define NEW_NTH_REF(n,loc) NEW_NODE(NODE_NTH_REF,0,n,0,loc)
+#define NEW_BACK_REF(n,loc) NEW_NODE(NODE_BACK_REF,0,n,0,loc)
+#define NEW_MATCH(c,loc) NEW_NODE(NODE_MATCH,c,0,0,loc)
+#define NEW_MATCH2(n1,n2,loc) NEW_NODE(NODE_MATCH2,n1,n2,0,loc)
+#define NEW_MATCH3(r,n2,loc) NEW_NODE(NODE_MATCH3,r,n2,0,loc)
+#define NEW_LIT(l,loc) NEW_NODE(NODE_LIT,l,0,0,loc)
+#define NEW_STR(s,loc) NEW_NODE(NODE_STR,s,0,0,loc)
+#define NEW_DSTR(s,loc) NEW_NODE(NODE_DSTR,s,1,0,loc)
+#define NEW_XSTR(s,loc) NEW_NODE(NODE_XSTR,s,0,0,loc)
+#define NEW_DXSTR(s,loc) NEW_NODE(NODE_DXSTR,s,0,0,loc)
+#define NEW_DSYM(s,loc) NEW_NODE(NODE_DSYM,s,0,0,loc)
+#define NEW_EVSTR(n,loc) NEW_NODE(NODE_EVSTR,0,(n),0,loc)
+#define NEW_CALL(r,m,a,loc) NEW_NODE(NODE_CALL,r,m,a,loc)
+#define NEW_OPCALL(r,m,a,loc) NEW_NODE(NODE_OPCALL,r,m,a,loc)
+#define NEW_FCALL(m,a,loc) NEW_NODE(NODE_FCALL,0,m,a,loc)
+#define NEW_VCALL(m,loc) NEW_NODE(NODE_VCALL,0,m,0,loc)
+#define NEW_SUPER(a,loc) NEW_NODE(NODE_SUPER,0,0,a,loc)
+#define NEW_ZSUPER(loc) NEW_NODE(NODE_ZSUPER,0,0,0,loc)
+#define NEW_ARGS_AUX(r,b,loc) NEW_NODE(NODE_ARGS_AUX,r,b,0,loc)
+#define NEW_OPT_ARG(i,v,loc) NEW_NODE(NODE_OPT_ARG,i,v,0,loc)
+#define NEW_KW_ARG(i,v,loc) NEW_NODE(NODE_KW_ARG,i,v,0,loc)
+#define NEW_POSTARG(i,v,loc) NEW_NODE(NODE_POSTARG,i,v,0,loc)
+#define NEW_ARGSCAT(a,b,loc) NEW_NODE(NODE_ARGSCAT,a,b,0,loc)
+#define NEW_ARGSPUSH(a,b,loc) NEW_NODE(NODE_ARGSPUSH,a,b,0,loc)
+#define NEW_SPLAT(a,loc) NEW_NODE(NODE_SPLAT,a,0,0,loc)
+#define NEW_BLOCK_PASS(b,loc) NEW_NODE(NODE_BLOCK_PASS,0,b,0,loc)
+#define NEW_ALIAS(n,o,loc) NEW_NODE(NODE_ALIAS,n,o,0,loc)
+#define NEW_VALIAS(n,o,loc) NEW_NODE(NODE_VALIAS,n,o,0,loc)
+#define NEW_UNDEF(i,loc) NEW_NODE(NODE_UNDEF,0,i,0,loc)
+#define NEW_CLASS(n,b,s,loc) NEW_NODE(NODE_CLASS,n,NEW_SCOPE(0,b,loc),(s),loc)
+#define NEW_SCLASS(r,b,loc) NEW_NODE(NODE_SCLASS,r,NEW_SCOPE(0,b,loc),0,loc)
+#define NEW_MODULE(n,b,loc) NEW_NODE(NODE_MODULE,n,NEW_SCOPE(0,b,loc),0,loc)
+#define NEW_COLON2(c,i,loc) NEW_NODE(NODE_COLON2,c,i,0,loc)
+#define NEW_COLON3(i,loc) NEW_NODE(NODE_COLON3,0,i,0,loc)
+#define NEW_DOT2(b,e,loc) NEW_NODE(NODE_DOT2,b,e,0,loc)
+#define NEW_DOT3(b,e,loc) NEW_NODE(NODE_DOT3,b,e,0,loc)
+#define NEW_SELF(loc) NEW_NODE(NODE_SELF,0,0,0,loc)
+#define NEW_NIL(loc) NEW_NODE(NODE_NIL,0,0,0,loc)
+#define NEW_TRUE(loc) NEW_NODE(NODE_TRUE,0,0,0,loc)
+#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 NODE_SPECIAL_REQUIRED_KEYWORD ((NODE *)-1)
+#define NODE_REQUIRED_KEYWORD_P(node) ((node)->nd_value == NODE_SPECIAL_REQUIRED_KEYWORD)
+#define NODE_SPECIAL_NO_NAME_REST ((NODE *)-1)
+#define NODE_NAMED_REST_P(node) ((node) != NODE_SPECIAL_NO_NAME_REST)
RUBY_SYMBOL_EXPORT_BEGIN
+typedef struct node_buffer_struct node_buffer_t;
+/* T_IMEMO/ast */
+typedef struct rb_ast_body_struct {
+ const NODE *root;
+ VALUE compile_option;
+ int line_count;
+} rb_ast_body_t;
+typedef struct rb_ast_struct {
+ VALUE flags;
+ node_buffer_t *node_buffer;
+ rb_ast_body_t body;
+} rb_ast_t;
+rb_ast_t *rb_ast_new(void);
+void rb_ast_mark(rb_ast_t*);
+void rb_ast_dispose(rb_ast_t*);
+void rb_ast_free(rb_ast_t*);
+void rb_ast_add_mark_object(rb_ast_t*, VALUE);
+NODE *rb_ast_newnode(rb_ast_t*);
+void rb_ast_delete_node(rb_ast_t*, NODE *n);
+
VALUE rb_parser_new(void);
VALUE rb_parser_end_seen_p(VALUE);
VALUE rb_parser_encoding(VALUE);
VALUE rb_parser_get_yydebug(VALUE);
VALUE rb_parser_set_yydebug(VALUE, VALUE);
-VALUE rb_parser_dump_tree(NODE *node, int comment);
-NODE *rb_parser_append_print(VALUE, NODE *);
-NODE *rb_parser_while_loop(VALUE, NODE *, int, int);
-
-NODE *rb_parser_compile_cstr(VALUE, const char*, const char*, int, int);
-NODE *rb_parser_compile_string(VALUE, const char*, VALUE, int);
-NODE *rb_parser_compile_file(VALUE, const char*, VALUE, int);
-NODE *rb_parser_compile_string_path(VALUE vparser, VALUE fname, VALUE src, int line);
-NODE *rb_parser_compile_file_path(VALUE vparser, VALUE fname, VALUE input, int line);
-
-NODE *rb_compile_cstr(const char*, const char*, int, int);
-NODE *rb_compile_string(const char*, VALUE, int);
-NODE *rb_compile_file(const char*, VALUE, int);
-
-NODE *rb_node_newnode(enum node_type,VALUE,VALUE,VALUE);
-NODE *rb_node_newnode_longlife(enum node_type,VALUE,VALUE,VALUE);
-void rb_gc_free_node(VALUE obj);
-size_t rb_node_memsize(VALUE obj);
-VALUE rb_gc_mark_node(NODE *obj);
+VALUE rb_parser_dump_tree(const NODE *node, int comment);
+void rb_parser_set_options(VALUE, int, int, int, int);
+
+rb_ast_t *rb_parser_compile_cstr(VALUE, const char*, const char*, int, int);
+rb_ast_t *rb_parser_compile_string(VALUE, const char*, VALUE, int);
+rb_ast_t *rb_parser_compile_file(VALUE, const char*, VALUE, int);
+rb_ast_t *rb_parser_compile_string_path(VALUE vparser, VALUE fname, VALUE src, int line);
+rb_ast_t *rb_parser_compile_file_path(VALUE vparser, VALUE fname, VALUE input, int line);
+rb_ast_t *rb_parser_compile_generic(VALUE vparser, VALUE (*lex_gets)(VALUE, int), VALUE fname, VALUE input, int line);
+
+rb_ast_t *rb_compile_cstr(const char*, const char*, int, int);
+rb_ast_t *rb_compile_string(const char*, VALUE, int);
+rb_ast_t *rb_compile_file(const char*, VALUE, int);
+
+void rb_node_init(NODE *n, enum node_type type, VALUE a0, VALUE a1, VALUE a2);
const struct kwtable *rb_reserved_word(const char *, unsigned int);
@@ -506,7 +435,7 @@ void *rb_parser_malloc(struct parser_params *, size_t);
void *rb_parser_realloc(struct parser_params *, void *, size_t);
void *rb_parser_calloc(struct parser_params *, size_t, size_t);
void rb_parser_free(struct parser_params *, void *);
-void rb_parser_printf(struct parser_params *parser, const char *fmt, ...);
+PRINTF_ARGS(void rb_parser_printf(struct parser_params *parser, const char *fmt, ...), 2, 3);
RUBY_SYMBOL_EXPORT_END
diff --git a/numeric.c b/numeric.c
index 959626e28d..f5ae09b3fd 100644
--- a/numeric.c
+++ b/numeric.c
@@ -9,8 +9,9 @@
**********************************************************************/
-#include "internal.h"
+#include "ruby/encoding.h"
#include "ruby/util.h"
+#include "internal.h"
#include "id.h"
#include <assert.h>
#include <ctype.h>
@@ -60,14 +61,14 @@
#define DBL_EPSILON 2.2204460492503131e-16
#endif
-#ifdef HAVE_INFINITY
+#ifndef USE_RB_INFINITY
#elif !defined(WORDS_BIGENDIAN) /* BYTE_ORDER == LITTLE_ENDIAN */
const union bytesequence4_or_float rb_infinity = {{0x00, 0x00, 0x80, 0x7f}};
#else
const union bytesequence4_or_float rb_infinity = {{0x7f, 0x80, 0x00, 0x00}};
#endif
-#ifdef HAVE_NAN
+#ifndef USE_RB_NAN
#elif !defined(WORDS_BIGENDIAN) /* BYTE_ORDER == LITTLE_ENDIAN */
const union bytesequence4_or_float rb_nan = {{0x00, 0x00, 0xc0, 0x7f}};
#else
@@ -97,20 +98,13 @@ round_half_up(double x, double s)
{
double f, xs = x * s;
-#ifdef HAVE_ROUND
f = round(xs);
-#endif
+ if (s == 1.0) return f;
if (x > 0) {
-#ifndef HAVE_ROUND
- f = floor(xs);
-#endif
if ((double)((f + 0.5) / s) <= x) f += 1;
x = f;
}
else {
-#ifndef HAVE_ROUND
- f = ceil(xs);
-#endif
if ((double)((f - 0.5) / s) >= x) f -= 1;
x = f;
}
@@ -118,6 +112,23 @@ round_half_up(double x, double s)
}
static double
+round_half_down(double x, double s)
+{
+ double f, xs = x * s;
+
+ f = round(xs);
+ if (x > 0) {
+ if ((double)((f - 0.5) / s) >= x) f -= 1;
+ x = f;
+ }
+ else {
+ if ((double)((f + 0.5) / s) <= x) f += 1;
+ x = f;
+ }
+ return x;
+}
+
+static double
round_half_even(double x, double s)
{
double f, d, xs = x * s;
@@ -152,13 +163,13 @@ static VALUE fix_mul(VALUE x, VALUE y);
static VALUE fix_lshift(long, unsigned long);
static VALUE fix_rshift(long, unsigned long);
static VALUE int_pow(long x, unsigned long y);
-static VALUE int_odd_p(VALUE x);
static VALUE int_even_p(VALUE x);
static int int_round_zero_p(VALUE num, int ndigits);
VALUE rb_int_floor(VALUE num, int ndigits);
VALUE rb_int_ceil(VALUE num, int ndigits);
static VALUE flo_to_i(VALUE num);
-static int float_invariant_round(double number, int ndigits, VALUE *num);
+static int float_round_overflow(int ndigits, int binexp);
+static int float_round_underflow(int ndigits, int binexp);
static ID id_coerce, id_div, id_divmod;
#define id_to_i idTo_i
@@ -188,28 +199,39 @@ rb_num_get_rounding_option(VALUE opts)
{
static ID round_kwds[1];
VALUE rounding;
+ VALUE str;
const char *s;
- long l;
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)) rounding = rb_sym2str(rounding);
- s = StringValueCStr(rounding);
- l = RSTRING_LEN(rounding);
- switch (l) {
+ 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;
+ }
+ s = RSTRING_PTR(str);
+ switch (RSTRING_LEN(str)) {
case 2:
- if (strncasecmp(s, "up", 2) == 0)
+ if (rb_memcicmp(s, "up", 2) == 0)
return RUBY_NUM_ROUND_HALF_UP;
break;
case 4:
- if (strncasecmp(s, "even", 4) == 0)
+ 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;
}
- rb_raise(rb_eArgError, "unknown rounding mode: %"PRIsVALUE, rounding);
+ invalid:
+ rb_raise(rb_eArgError, "invalid rounding mode: % "PRIsVALUE, rounding);
}
noopt:
return RUBY_NUM_ROUND_DEFAULT;
@@ -249,17 +271,6 @@ rb_num_to_uint(VALUE val, unsigned int *ret)
#define method_basic_p(klass) rb_method_basic_definition_p(klass, mid)
-static VALUE
-compare_with_zero(VALUE num, ID mid)
-{
- VALUE zero = INT2FIX(0);
- VALUE r = rb_check_funcall(num, mid, 1, &zero);
- if (r == Qundef) {
- rb_cmperr(num, zero);
- }
- return r;
-}
-
static inline int
int_pos_p(VALUE num)
{
@@ -284,42 +295,22 @@ int_neg_p(VALUE num)
rb_raise(rb_eTypeError, "not an Integer");
}
-static inline int
-positive_int_p(VALUE num)
+int
+rb_int_positive_p(VALUE num)
{
- const ID mid = '>';
-
- if (FIXNUM_P(num)) {
- if (method_basic_p(rb_cInteger))
- return FIXNUM_POSITIVE_P(num);
- }
- else if (RB_TYPE_P(num, T_BIGNUM)) {
- if (method_basic_p(rb_cInteger))
- return BIGNUM_POSITIVE_P(num);
- }
- return RTEST(compare_with_zero(num, mid));
+ return int_pos_p(num);
}
-static inline int
-negative_int_p(VALUE num)
+int
+rb_int_negative_p(VALUE num)
{
- const ID mid = '<';
-
- if (FIXNUM_P(num)) {
- if (method_basic_p(rb_cInteger))
- return FIXNUM_NEGATIVE_P(num);
- }
- else if (RB_TYPE_P(num, T_BIGNUM)) {
- if (method_basic_p(rb_cInteger))
- return BIGNUM_NEGATIVE_P(num);
- }
- return RTEST(compare_with_zero(num, mid));
+ return int_neg_p(num);
}
int
rb_num_negative_p(VALUE num)
{
- return negative_int_p(num);
+ return rb_num_negative_int_p(num);
}
static VALUE
@@ -341,7 +332,7 @@ num_funcall_op_0(VALUE x, VALUE arg, int recursive)
ID2SYM(func), x);
}
}
- return rb_funcall(x, func, 0, 0);
+ return rb_funcallv(x, func, 0, 0);
}
static VALUE
@@ -350,6 +341,8 @@ num_funcall0(VALUE x, ID func)
return rb_exec_recursive(num_funcall_op_0, x, (VALUE)func);
}
+NORETURN(static void num_funcall_op_1_recursion(VALUE x, ID func, VALUE y));
+
static void
num_funcall_op_1_recursion(VALUE x, ID func, VALUE y)
{
@@ -388,9 +381,9 @@ num_funcall1(VALUE x, ID func, VALUE y)
* call-seq:
* num.coerce(numeric) -> array
*
- * If a +numeric+ is the same type as +num+, returns an array containing
- * +numeric+ and +num+. Otherwise, returns an array with both a +numeric+ and
- * +num+ represented as Float objects.
+ * If +numeric+ is the same type as +num+, returns an array
+ * <code>[numeric, num]</code>. Otherwise, returns an array with both
+ * +numeric+ and +num+ represented as Float objects.
*
* This coercion mechanism is used by Ruby to handle mixed-type numeric
* operations: it is intended to find a compatible common type between the two
@@ -411,13 +404,6 @@ num_coerce(VALUE x, VALUE y)
return rb_assoc_new(y, x);
}
-static VALUE
-coerce_body(VALUE arg)
-{
- VALUE *x = (VALUE *)arg;
- return rb_funcall(x[1], id_coerce, 1, x[0]);
-}
-
NORETURN(static void coerce_failed(VALUE x, VALUE y));
static void
coerce_failed(VALUE x, VALUE y)
@@ -432,50 +418,21 @@ coerce_failed(VALUE x, VALUE y)
y, rb_obj_class(x));
}
-static VALUE
-coerce_rescue(VALUE arg, VALUE errinfo)
-{
- VALUE *x = (VALUE *)arg;
- coerce_failed(x[0], x[1]);
- return Qnil; /* dummy */
-}
-
-static VALUE
-coerce_rescue_quiet(VALUE arg, VALUE errinfo)
-{
- return Qundef;
-}
-
static int
do_coerce(VALUE *x, VALUE *y, int err)
{
- VALUE ary;
- VALUE a[2];
-
- a[0] = *x; a[1] = *y;
-
- if (!rb_respond_to(*y, id_coerce)) {
+ VALUE ary = rb_check_funcall(*y, id_coerce, 1, x);
+ if (ary == Qundef) {
if (err) {
coerce_failed(*x, *y);
}
return FALSE;
}
-
- ary = rb_rescue(coerce_body, (VALUE)a, err ? coerce_rescue : coerce_rescue_quiet, (VALUE)a);
- if (ary == Qundef) {
- rb_warn("Numerical comparison operators will no more rescue exceptions of #coerce");
- rb_warn("in the next release. Return nil in #coerce if the coercion is impossible.");
+ if (!err && NIL_P(ary)) {
return FALSE;
}
if (!RB_TYPE_P(ary, T_ARRAY) || RARRAY_LEN(ary) != 2) {
- if (err) {
- rb_raise(rb_eTypeError, "coerce must return [x, y]");
- }
- else if (!NIL_P(ary)) {
- rb_warn("Bad return value for #coerce, called by numerical comparison operators.");
- rb_warn("#coerce must return [x, y]. The next release will raise an error for this.");
- }
- return FALSE;
+ rb_raise(rb_eTypeError, "coerce must return [x, y]");
}
*x = RARRAY_AREF(ary, 0);
@@ -512,6 +469,8 @@ rb_num_coerce_relop(VALUE x, VALUE y, ID func)
}
/*
+ * :nodoc:
+ *
* Trap attempts to add methods to Numeric objects. Always raises a TypeError.
*
* Numerics should be values; singleton_methods should not be added to them.
@@ -528,27 +487,46 @@ num_sadded(VALUE x, VALUE name)
rb_id2str(mid),
rb_obj_class(x));
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
+#if 0
/*
- * Numerics are immutable values, which should not be copied.
+ * call-seq:
+ * num.clone(freeze: true) -> num
*
- * Any attempt to use this method on a Numeric will raise a TypeError.
+ * Returns the receiver. +freeze+ cannot be +false+.
*/
static VALUE
-num_init_copy(VALUE x, VALUE y)
+num_clone(int argc, VALUE *argv, VALUE x)
{
- rb_raise(rb_eTypeError, "can't copy %"PRIsVALUE, rb_obj_class(x));
+ return rb_immutable_obj_clone(argc, argv, x);
+}
+#else
+# define num_clone rb_immutable_obj_clone
+#endif
- UNREACHABLE;
+#if 0
+/*
+ * call-seq:
+ * num.dup -> num
+ *
+ * Returns the receiver.
+ */
+static VALUE
+num_dup(VALUE x)
+{
+ return x;
}
+#else
+# define num_dup num_uplus
+#endif
/*
* call-seq:
* +num -> num
*
- * Unary Plus---Returns the receiver's value.
+ * Unary Plus---Returns the receiver.
*/
static VALUE
@@ -559,10 +537,13 @@ num_uplus(VALUE num)
/*
* call-seq:
- * num.i -> Complex(0,num)
+ * num.i -> Complex(0, num)
*
* Returns the corresponding imaginary number.
* Not available for complex numbers.
+ *
+ * -42.i #=> (0-42i)
+ * 2.0.i #=> (0+2.0i)
*/
static VALUE
@@ -571,12 +552,11 @@ num_imaginary(VALUE num)
return rb_complex_new(INT2FIX(0), num);
}
-
/*
* call-seq:
* -num -> numeric
*
- * Unary Minus---Returns the receiver's value, negated.
+ * Unary Minus---Returns the receiver, negated.
*/
static VALUE
@@ -603,13 +583,12 @@ num_fdiv(VALUE x, VALUE y)
return rb_funcall(rb_Float(x), '/', 1, y);
}
-
/*
* call-seq:
* num.div(numeric) -> integer
*
* Uses +/+ to perform division, then converts the result to an integer.
- * +numeric+ does not define the +/+ operator; this is left to subclasses.
+ * Numeric does not define the +/+ operator; this is left to subclasses.
*
* Equivalent to <code>num.divmod(numeric)[0]</code>.
*
@@ -623,12 +602,11 @@ num_div(VALUE x, VALUE y)
return rb_funcall(num_funcall1(x, '/', y), rb_intern("floor"), 0);
}
-
/*
* call-seq:
* num.modulo(numeric) -> real
*
- * x.modulo(y) means x-y*(x/y).floor
+ * <code>x.modulo(y)</code> means <code>x-y*(x/y).floor</code>.
*
* Equivalent to <code>num.divmod(numeric)[1]</code>.
*
@@ -647,7 +625,7 @@ num_modulo(VALUE x, VALUE y)
* call-seq:
* num.remainder(numeric) -> real
*
- * x.remainder(y) means x-y*(x/y).truncate
+ * <code>x.remainder(y)</code> means <code>x-y*(x/y).truncate</code>.
*
* See Numeric#divmod.
*/
@@ -658,10 +636,10 @@ num_remainder(VALUE x, VALUE y)
VALUE z = num_funcall1(x, '%', y);
if ((!rb_equal(z, INT2FIX(0))) &&
- ((negative_int_p(x) &&
- positive_int_p(y)) ||
- (positive_int_p(x) &&
- 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)))) {
return rb_funcall(z, '-', 1, y);
}
return z;
@@ -674,12 +652,13 @@ num_remainder(VALUE x, VALUE y)
* Returns an array containing the quotient and modulus obtained by dividing
* +num+ by +numeric+.
*
- * If <code>q, r = * x.divmod(y)</code>, then
+ * If <code>q, r = x.divmod(y)</code>, then
*
* q = floor(x/y)
- * x = q*y+r
+ * x = q*y + r
*
- * The quotient is rounded toward -infinity, as shown in the following table:
+ * The quotient is rounded toward negative infinity, as shown in the
+ * following table:
*
* a | b | a.divmod(b) | a/b | a.modulo(b) | a.remainder(b)
* ------+-----+---------------+---------+-------------+---------------
@@ -702,11 +681,11 @@ num_remainder(VALUE x, VALUE y)
*
* Examples
*
- * 11.divmod(3) #=> [3, 2]
- * 11.divmod(-3) #=> [-4, -1]
- * 11.divmod(3.5) #=> [3, 0.5]
- * (-11).divmod(3.5) #=> [-4, 3.0]
- * (11.5).divmod(3.5) #=> [3, 1.0]
+ * 11.divmod(3) #=> [3, 2]
+ * 11.divmod(-3) #=> [-4, -1]
+ * 11.divmod(3.5) #=> [3, 0.5]
+ * (-11).divmod(3.5) #=> [-4, 3.0]
+ * 11.5.divmod(3.5) #=> [3, 1.0]
*/
static VALUE
@@ -719,7 +698,7 @@ num_divmod(VALUE x, VALUE y)
* call-seq:
* num.real? -> true or false
*
- * Returns +true+ if +num+ is a Real number. (i.e. not Complex).
+ * Returns +true+ if +num+ is a real number (i.e. not Complex).
*/
static VALUE
@@ -734,8 +713,8 @@ num_real_p(VALUE num)
*
* Returns +true+ if +num+ is an Integer.
*
- * (1.0).integer? #=> false
- * (1).integer? #=> true
+ * 1.0.integer? #=> false
+ * 1.integer? #=> true
*/
static VALUE
@@ -755,19 +734,18 @@ num_int_p(VALUE num)
* (-34.56).abs #=> 34.56
* -34.56.abs #=> 34.56
*
- * Numeric#magnitude is an alias of Numeric#abs.
+ * Numeric#magnitude is an alias for Numeric#abs.
*/
static VALUE
num_abs(VALUE num)
{
- if (negative_int_p(num)) {
+ if (rb_num_negative_int_p(num)) {
return num_funcall0(num, idUMinus);
}
return num;
}
-
/*
* call-seq:
* num.zero? -> true or false
@@ -795,7 +773,6 @@ num_zero_p(VALUE num)
return Qfalse;
}
-
/*
* call-seq:
* num.nonzero? -> self or nil
@@ -822,7 +799,7 @@ num_nonzero_p(VALUE num)
* call-seq:
* num.finite? -> true or false
*
- * Return true if +num+ is finite number, oterwise returns false.
+ * Returns +true+ if +num+ is a finite number, otherwise returns +false+.
*/
static VALUE
num_finite_p(VALUE num)
@@ -832,13 +809,10 @@ num_finite_p(VALUE num)
/*
* call-seq:
- * num.infinite? -> nil or 1 or -1
- *
- * Returns values corresponding to the value of +num+'s magnitude:
+ * num.infinite? -> -1, 1, or nil
*
- * +finite+:: +nil+
- * +-Infinity+:: +-1+
- * ++Infinity+:: ++1+
+ * Returns +nil+, -1, or 1 depending on whether the value is
+ * finite, <code>-Infinity</code>, or <code>+Infinity</code>.
*/
static VALUE
num_infinite_p(VALUE num)
@@ -852,9 +826,9 @@ num_infinite_p(VALUE num)
*
* Invokes the child class's +to_i+ method to convert +num+ to an integer.
*
- * 1.0.class => Float
- * 1.0.to_int.class => Integer
- * 1.0.to_i.class => Integer
+ * 1.0.class #=> Float
+ * 1.0.to_int.class #=> Integer
+ * 1.0.to_i.class #=> Integer
*/
static VALUE
@@ -865,7 +839,7 @@ num_to_int(VALUE num)
/*
* call-seq:
- * num.positive? -> true or false
+ * num.positive? -> true or false
*
* Returns +true+ if +num+ is greater than 0.
*/
@@ -883,12 +857,12 @@ num_positive_p(VALUE num)
if (method_basic_p(rb_cInteger))
return BIGNUM_POSITIVE_P(num) && !rb_bigzero_p(num) ? Qtrue : Qfalse;
}
- return compare_with_zero(num, mid);
+ return rb_num_compare_with_zero(num, mid);
}
/*
* call-seq:
- * num.negative? -> true or false
+ * num.negative? -> true or false
*
* Returns +true+ if +num+ is less than 0.
*/
@@ -896,7 +870,7 @@ num_positive_p(VALUE num)
static VALUE
num_negative_p(VALUE num)
{
- return negative_int_p(num) ? Qtrue : Qfalse;
+ return rb_num_negative_int_p(num) ? Qtrue : Qfalse;
}
@@ -908,10 +882,10 @@ num_negative_p(VALUE num)
* architecture's double-precision floating point representation.
*
* Floating point has a different arithmetic and is an inexact number.
- * So you should know its esoteric system. see following:
+ * So you should know its esoteric system. See following:
*
* - http://docs.sun.com/source/806-3568/ncg_goldberg.html
- * - http://wiki.github.com/rdp/ruby_tutorials_core/ruby-talk-faq#wiki-floats_imprecise
+ * - https://github.com/rdp/ruby_tutorials_core/wiki/Ruby-Talk-FAQ#floats_imprecise
* - http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems
*/
@@ -929,9 +903,9 @@ rb_float_new_in_heap(double d)
* call-seq:
* float.to_s -> string
*
- * Returns a string containing a representation of self. As well as a fixed or
- * exponential form of the +float+, the call may return +NaN+, +Infinity+, and
- * +-Infinity+.
+ * Returns a string containing a representation of +self+.
+ * As well as a fixed or exponential form of the +float+,
+ * the call may return +NaN+, +Infinity+, and +-Infinity+.
*/
static VALUE
@@ -1009,10 +983,10 @@ flo_to_s(VALUE flt)
* call-seq:
* float.coerce(numeric) -> array
*
- * Returns an array with both a +numeric+ and a +float+ represented as Float
+ * Returns an array with both +numeric+ and +float+ represented as Float
* objects.
*
- * This is achieved by converting a +numeric+ to a Float.
+ * This is achieved by converting +numeric+ to a Float.
*
* 1.2.coerce(3) #=> [3.0, 1.2]
* 2.5.coerce(1.1) #=> [1.1, 2.5]
@@ -1028,11 +1002,11 @@ flo_coerce(VALUE x, VALUE y)
* call-seq:
* -float -> float
*
- * Returns float, negated.
+ * Returns +float+, negated.
*/
-static VALUE
-flo_uminus(VALUE flt)
+VALUE
+rb_float_uminus(VALUE flt)
{
return DBL2NUM(-RFLOAT_VALUE(flt));
}
@@ -1041,11 +1015,11 @@ flo_uminus(VALUE flt)
* call-seq:
* float + other -> float
*
- * Returns a new float which is the sum of +float+ and +other+.
+ * Returns a new Float which is the sum of +float+ and +other+.
*/
-static VALUE
-flo_plus(VALUE x, VALUE y)
+VALUE
+rb_float_plus(VALUE x, VALUE y)
{
if (RB_TYPE_P(y, T_FIXNUM)) {
return DBL2NUM(RFLOAT_VALUE(x) + (double)FIX2LONG(y));
@@ -1065,11 +1039,11 @@ flo_plus(VALUE x, VALUE y)
* call-seq:
* float - other -> float
*
- * Returns a new float which is the difference of +float+ and +other+.
+ * Returns a new Float which is the difference of +float+ and +other+.
*/
-static VALUE
-flo_minus(VALUE x, VALUE y)
+VALUE
+rb_float_minus(VALUE x, VALUE y)
{
if (RB_TYPE_P(y, T_FIXNUM)) {
return DBL2NUM(RFLOAT_VALUE(x) - (double)FIX2LONG(y));
@@ -1089,11 +1063,11 @@ flo_minus(VALUE x, VALUE y)
* call-seq:
* float * other -> float
*
- * Returns a new float which is the product of +float+ and +other+.
+ * Returns a new Float which is the product of +float+ and +other+.
*/
-static VALUE
-flo_mul(VALUE x, VALUE y)
+VALUE
+rb_float_mul(VALUE x, VALUE y)
{
if (RB_TYPE_P(y, T_FIXNUM)) {
return DBL2NUM(RFLOAT_VALUE(x) * (double)FIX2LONG(y));
@@ -1109,39 +1083,71 @@ flo_mul(VALUE x, VALUE y)
}
}
+static bool
+flo_iszero(VALUE f)
+{
+ return RFLOAT_VALUE(f) == 0.0;
+}
+
+static double
+double_div_double(double x, double y)
+{
+ if (LIKELY(y != 0.0)) {
+ return x / y;
+ }
+ else if (x == 0.0) {
+ return nan("");
+ }
+ else {
+ double z = signbit(y) ? -1.0 : 1.0;
+ return x * z * HUGE_VAL;
+ }
+}
+
+MJIT_FUNC_EXPORTED VALUE
+rb_flo_div_flo(VALUE x, VALUE y)
+{
+ double num = RFLOAT_VALUE(x);
+ double den = RFLOAT_VALUE(y);
+ double ret = double_div_double(num, den);
+ return DBL2NUM(ret);
+}
+
/*
* call-seq:
* float / other -> float
*
- * Returns a new float which is the result of dividing +float+ by +other+.
+ * Returns a new Float which is the result of dividing +float+ by +other+.
*/
-static VALUE
-flo_div(VALUE x, VALUE y)
+VALUE
+rb_float_div(VALUE x, VALUE y)
{
- long f_y;
- double d;
+ double num = RFLOAT_VALUE(x);
+ double den;
+ double ret;
if (RB_TYPE_P(y, T_FIXNUM)) {
- f_y = FIX2LONG(y);
- return DBL2NUM(RFLOAT_VALUE(x) / (double)f_y);
+ den = FIX2LONG(y);
}
else if (RB_TYPE_P(y, T_BIGNUM)) {
- d = rb_big2dbl(y);
- return DBL2NUM(RFLOAT_VALUE(x) / d);
+ den = rb_big2dbl(y);
}
else if (RB_TYPE_P(y, T_FLOAT)) {
- return DBL2NUM(RFLOAT_VALUE(x) / RFLOAT_VALUE(y));
+ den = RFLOAT_VALUE(y);
}
else {
return rb_num_coerce_bin(x, y, '/');
}
+
+ ret = double_div_double(num, den);
+ return DBL2NUM(ret);
}
/*
* call-seq:
* float.fdiv(numeric) -> float
- * float.quo(numeric) -> float
+ * float.quo(numeric) -> float
*
* Returns <code>float / numeric</code>, same as Float#/.
*/
@@ -1195,7 +1201,7 @@ flodivmod(double x, double y, double *divp, double *modp)
* An error will be raised if y == 0.
*/
-double
+MJIT_FUNC_EXPORTED double
ruby_float_mod(double x, double y)
{
double mod;
@@ -1203,16 +1209,15 @@ ruby_float_mod(double x, double y)
return mod;
}
-
/*
* call-seq:
* float % other -> float
* float.modulo(other) -> float
*
- * Return the modulo after division of +float+ by +other+.
+ * Returns the modulo after division of +float+ by +other+.
*
- * 6543.21.modulo(137) #=> 104.21
- * 6543.21.modulo(137.24) #=> 92.9299999999996
+ * 6543.21.modulo(137) #=> 104.21000000000004
+ * 6543.21.modulo(137.24) #=> 92.92999999999961
*/
static VALUE
@@ -1250,8 +1255,8 @@ dbl2ival(double d)
*
* See Numeric#divmod.
*
- * 42.0.divmod 6 #=> [7, 0.0]
- * 42.0.divmod 5 #=> [8, 2.0]
+ * 42.0.divmod(6) #=> [7, 0.0]
+ * 42.0.divmod(5) #=> [8, 2.0]
*/
static VALUE
@@ -1280,12 +1285,11 @@ flo_divmod(VALUE x, VALUE y)
/*
* call-seq:
- *
- * float ** other -> float
+ * float ** other -> float
*
* Raises +float+ to the power of +other+.
*
- * 2.0**3 #=> 8.0
+ * 2.0**3 #=> 8.0
*/
VALUE
@@ -1304,7 +1308,7 @@ rb_float_pow(VALUE x, VALUE y)
dx = RFLOAT_VALUE(x);
dy = RFLOAT_VALUE(y);
if (dx < 0 && dy != round(dy))
- return num_funcall1(rb_complex_raw1(x), idPow, y);
+ return rb_dbl_complex_new_polar_pi(pow(-dx, dy), dy);
}
else {
return rb_num_coerce_bin(x, y, idPow);
@@ -1317,13 +1321,11 @@ rb_float_pow(VALUE x, VALUE y)
* num.eql?(numeric) -> true or false
*
* Returns +true+ if +num+ and +numeric+ are the same type and have equal
- * values. Contrast this with <code>Numeric#==</code>, which performs
- * type conversions.
+ * values. Contrast this with Numeric#==, which performs type conversions.
*
- * 1 == 1.0 #=> true
- * 1.eql?(1.0) #=> false
- * (1.0).eql?(1.0) #=> true
- * 68719476736.eql?(68719476736.0) #=> false
+ * 1 == 1.0 #=> true
+ * 1.eql?(1.0) #=> false
+ * 1.0.eql?(1.0) #=> true
*/
static VALUE
@@ -1342,8 +1344,7 @@ num_eql(VALUE x, VALUE y)
* call-seq:
* number <=> other -> 0 or nil
*
- * Returns zero if +number+ equals +other+, otherwise +nil+ is returned if the
- * two values are incomparable.
+ * Returns zero if +number+ equals +other+, otherwise returns +nil+.
*/
static VALUE
@@ -1367,18 +1368,17 @@ num_equal(VALUE x, VALUE y)
* call-seq:
* float == obj -> true or false
*
- * Returns +true+ only if +obj+ has the same value as +float+. Contrast this
- * with Float#eql?, which requires obj to be a Float.
- *
- * The result of <code>NaN == NaN</code> is undefined, so the
- * implementation-dependent value is returned.
+ * Returns +true+ only if +obj+ has the same value as +float+.
+ * Contrast this with Float#eql?, which requires +obj+ to be a Float.
*
* 1.0 == 1 #=> true
*
+ * The result of <code>NaN == NaN</code> is undefined,
+ * so an implementation-dependent value is returned.
*/
-static VALUE
-flo_eq(VALUE x, VALUE y)
+MJIT_FUNC_EXPORTED VALUE
+rb_float_equal(VALUE x, VALUE y)
{
volatile double a, b;
@@ -1401,9 +1401,11 @@ flo_eq(VALUE x, VALUE y)
return (a == b)?Qtrue:Qfalse;
}
+#define flo_eq rb_float_equal
+
/*
* call-seq:
- * float.hash -> integer
+ * float.hash -> integer
*
* Returns a hash code for this float.
*
@@ -1419,7 +1421,7 @@ flo_hash(VALUE num)
VALUE
rb_dbl_hash(double d)
{
- return LONG2FIX(rb_dbl_long_hash (d));
+ return LONG2FIX(rb_dbl_long_hash(d));
}
VALUE
@@ -1434,13 +1436,14 @@ rb_dbl_cmp(double a, double b)
/*
* call-seq:
- * float <=> real -> -1, 0, +1 or nil
+ * float <=> real -> -1, 0, +1, or nil
*
- * Returns -1, 0, +1 or nil depending on whether +float+ is less than, equal
- * to, or greater than +real+. This is the basis for the tests in Comparable.
+ * Returns -1, 0, or +1 depending on whether +float+ is
+ * less than, equal to, or greater than +real+.
+ * This is the basis for the tests in the Comparable module.
*
- * The result of <code>NaN <=> NaN</code> is undefined, so the
- * implementation-dependent value is returned.
+ * The result of <code>NaN <=> NaN</code> is undefined,
+ * so an implementation-dependent value is returned.
*
* +nil+ is returned if the two values are incomparable.
*/
@@ -1477,18 +1480,24 @@ flo_cmp(VALUE x, VALUE y)
return rb_dbl_cmp(a, b);
}
+MJIT_FUNC_EXPORTED int
+rb_float_cmp(VALUE x, VALUE y)
+{
+ return NUM2INT(flo_cmp(x, y));
+}
+
/*
* call-seq:
* float > real -> true or false
*
* Returns +true+ if +float+ is greater than +real+.
*
- * The result of <code>NaN > NaN</code> is undefined, so the
- * implementation-dependent value is returned.
+ * The result of <code>NaN > NaN</code> is undefined,
+ * so an implementation-dependent value is returned.
*/
-static VALUE
-flo_gt(VALUE x, VALUE y)
+VALUE
+rb_float_gt(VALUE x, VALUE y)
{
double a, b;
@@ -1520,8 +1529,8 @@ flo_gt(VALUE x, VALUE y)
*
* Returns +true+ if +float+ is greater than or equal to +real+.
*
- * The result of <code>NaN >= NaN</code> is undefined, so the
- * implementation-dependent value is returned.
+ * The result of <code>NaN >= NaN</code> is undefined,
+ * so an implementation-dependent value is returned.
*/
static VALUE
@@ -1557,8 +1566,8 @@ flo_ge(VALUE x, VALUE y)
*
* Returns +true+ if +float+ is less than +real+.
*
- * The result of <code>NaN < NaN</code> is undefined, so the
- * implementation-dependent value is returned.
+ * The result of <code>NaN < NaN</code> is undefined,
+ * so an implementation-dependent value is returned.
*/
static VALUE
@@ -1594,8 +1603,8 @@ flo_lt(VALUE x, VALUE y)
*
* Returns +true+ if +float+ is less than or equal to +real+.
*
- * The result of <code>NaN <= NaN</code> is undefined, so the
- * implementation-dependent value is returned.
+ * The result of <code>NaN <= NaN</code> is undefined,
+ * so an implementation-dependent value is returned.
*/
static VALUE
@@ -1632,14 +1641,14 @@ flo_le(VALUE x, VALUE y)
* Returns +true+ only if +obj+ is a Float with the same value as +float+.
* Contrast this with Float#==, which performs type conversions.
*
- * The result of <code>NaN.eql?(NaN)</code> is undefined, so the
- * implementation-dependent value is returned.
- *
* 1.0.eql?(1) #=> false
+ *
+ * The result of <code>NaN.eql?(NaN)</code> is undefined,
+ * so an implementation-dependent value is returned.
*/
-static VALUE
-flo_eql(VALUE x, VALUE y)
+MJIT_FUNC_EXPORTED VALUE
+rb_float_eql(VALUE x, VALUE y)
{
if (RB_TYPE_P(y, T_FLOAT)) {
double a = RFLOAT_VALUE(x);
@@ -1653,11 +1662,13 @@ flo_eql(VALUE x, VALUE y)
return Qfalse;
}
+#define flo_eql rb_float_eql
+
/*
* call-seq:
- * float.to_f -> self
+ * float.to_f -> self
*
- * Since +float+ is already a float, returns +self+.
+ * Since +float+ is already a Float, returns +self+.
*/
static VALUE
@@ -1675,11 +1686,13 @@ flo_to_f(VALUE num)
*
* (-34.56).abs #=> 34.56
* -34.56.abs #=> 34.56
+ * 34.56.abs #=> 34.56
*
+ * Float#magnitude is an alias for Float#abs.
*/
-static VALUE
-flo_abs(VALUE flt)
+VALUE
+rb_float_abs(VALUE flt)
{
double val = fabs(RFLOAT_VALUE(flt));
return DBL2NUM(val);
@@ -1690,16 +1703,12 @@ flo_abs(VALUE flt)
* float.zero? -> true or false
*
* Returns +true+ if +float+ is 0.0.
- *
*/
static VALUE
flo_zero_p(VALUE num)
{
- if (RFLOAT_VALUE(num) == 0.0) {
- return Qtrue;
- }
- return Qfalse;
+ return flo_iszero(num) ? Qtrue : Qfalse;
}
/*
@@ -1724,23 +1733,18 @@ flo_is_nan_p(VALUE num)
/*
* call-seq:
- * float.infinite? -> nil, -1, +1
- *
- * Return values corresponding to the value of +float+:
+ * float.infinite? -> -1, 1, or nil
*
- * +finite+:: +nil+
- * +-Infinity+:: +-1+
- * ++Infinity+:: +1+
- *
- * For example:
+ * Returns +nil+, -1, or 1 depending on whether the value is
+ * finite, <code>-Infinity</code>, or <code>+Infinity</code>.
*
* (0.0).infinite? #=> nil
* (-1.0/0.0).infinite? #=> -1
* (+1.0/0.0).infinite? #=> 1
*/
-static VALUE
-flo_is_infinite_p(VALUE num)
+VALUE
+rb_flo_is_infinite_p(VALUE num)
{
double value = RFLOAT_VALUE(num);
@@ -1755,13 +1759,12 @@ flo_is_infinite_p(VALUE num)
* call-seq:
* float.finite? -> true or false
*
- * Returns +true+ if +float+ is a valid IEEE floating point number (it is not
- * infinite, and Float#nan? is +false+).
- *
+ * Returns +true+ if +float+ is a valid IEEE floating point number,
+ * i.e. it is not infinite and Float#nan? is +false+.
*/
-static VALUE
-flo_is_finite_p(VALUE num)
+VALUE
+rb_flo_is_finite_p(VALUE num)
{
double value = RFLOAT_VALUE(num);
@@ -1780,7 +1783,7 @@ flo_is_finite_p(VALUE num)
* call-seq:
* float.next_float -> float
*
- * Returns the next representable floating-point number.
+ * Returns the next representable floating point number.
*
* Float::MAX.next_float and Float::INFINITY.next_float is Float::INFINITY.
*
@@ -1788,13 +1791,13 @@ flo_is_finite_p(VALUE num)
*
* For example:
*
- * p 0.01.next_float #=> 0.010000000000000002
- * p 1.0.next_float #=> 1.0000000000000002
- * p 100.0.next_float #=> 100.00000000000001
+ * 0.01.next_float #=> 0.010000000000000002
+ * 1.0.next_float #=> 1.0000000000000002
+ * 100.0.next_float #=> 100.00000000000001
*
- * p 0.01.next_float - 0.01 #=> 1.734723475976807e-18
- * p 1.0.next_float - 1.0 #=> 2.220446049250313e-16
- * p 100.0.next_float - 100.0 #=> 1.4210854715202004e-14
+ * 0.01.next_float - 0.01 #=> 1.734723475976807e-18
+ * 1.0.next_float - 1.0 #=> 2.220446049250313e-16
+ * 100.0.next_float - 100.0 #=> 1.4210854715202004e-14
*
* f = 0.01; 20.times { printf "%-20a %s\n", f, f.to_s; f = f.next_float }
* #=> 0x1.47ae147ae147bp-7 0.01
@@ -1820,20 +1823,20 @@ flo_is_finite_p(VALUE num)
*
* f = 0.0
* 100.times { f += 0.1 }
- * p f #=> 9.99999999999998 # should be 10.0 in the ideal world.
- * p 10-f #=> 1.9539925233402755e-14 # the floating-point error.
- * p(10.0.next_float-10) #=> 1.7763568394002505e-15 # 1 ulp (units in the last place).
- * p((10-f)/(10.0.next_float-10)) #=> 11.0 # the error is 11 ulp.
- * p((10-f)/(10*Float::EPSILON)) #=> 8.8 # approximation of the above.
- * p "%a" % f #=> "0x1.3fffffffffff5p+3" # the last hex digit is 5. 16 - 5 = 11 ulp.
- *
+ * f #=> 9.99999999999998 # should be 10.0 in the ideal world.
+ * 10-f #=> 1.9539925233402755e-14 # the floating point error.
+ * 10.0.next_float-10 #=> 1.7763568394002505e-15 # 1 ulp (unit in the last place).
+ * (10-f)/(10.0.next_float-10) #=> 11.0 # the error is 11 ulp.
+ * (10-f)/(10*Float::EPSILON) #=> 8.8 # approximation of the above.
+ * "%a" % 10 #=> "0x1.4p+3"
+ * "%a" % f #=> "0x1.3fffffffffff5p+3" # the last hex digit is 5. 16 - 5 = 11 ulp.
*/
static VALUE
flo_next_float(VALUE vx)
{
double x, y;
x = NUM2DBL(vx);
- y = nextafter(x, INFINITY);
+ y = nextafter(x, HUGE_VAL);
return DBL2NUM(y);
}
@@ -1841,7 +1844,7 @@ flo_next_float(VALUE vx)
* call-seq:
* float.prev_float -> float
*
- * Returns the previous representable floating-point number.
+ * Returns the previous representable floating point number.
*
* (-Float::MAX).prev_float and (-Float::INFINITY).prev_float is -Float::INFINITY.
*
@@ -1849,13 +1852,13 @@ flo_next_float(VALUE vx)
*
* For example:
*
- * p 0.01.prev_float #=> 0.009999999999999998
- * p 1.0.prev_float #=> 0.9999999999999999
- * p 100.0.prev_float #=> 99.99999999999999
+ * 0.01.prev_float #=> 0.009999999999999998
+ * 1.0.prev_float #=> 0.9999999999999999
+ * 100.0.prev_float #=> 99.99999999999999
*
- * p 0.01 - 0.01.prev_float #=> 1.734723475976807e-18
- * p 1.0 - 1.0.prev_float #=> 1.1102230246251565e-16
- * p 100.0 - 100.0.prev_float #=> 1.4210854715202004e-14
+ * 0.01 - 0.01.prev_float #=> 1.734723475976807e-18
+ * 1.0 - 1.0.prev_float #=> 1.1102230246251565e-16
+ * 100.0 - 100.0.prev_float #=> 1.4210854715202004e-14
*
* f = 0.01; 20.times { printf "%-20a %s\n", f, f.to_s; f = f.prev_float }
* #=> 0x1.47ae147ae147bp-7 0.01
@@ -1878,104 +1881,127 @@ flo_next_float(VALUE vx)
* # 0x1.47ae147ae146ap-7 0.00999999999999997
* # 0x1.47ae147ae1469p-7 0.009999999999999969
* # 0x1.47ae147ae1468p-7 0.009999999999999967
- *
*/
static VALUE
flo_prev_float(VALUE vx)
{
double x, y;
x = NUM2DBL(vx);
- y = nextafter(x, -INFINITY);
+ y = nextafter(x, -HUGE_VAL);
return DBL2NUM(y);
}
+VALUE
+rb_float_floor(VALUE num, int ndigits)
+{
+ double number, f;
+ number = RFLOAT_VALUE(num);
+ if (number == 0.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 = floor(number * f) / f;
+ return DBL2NUM(f);
+ }
+ else {
+ num = dbl2ival(floor(number));
+ if (ndigits < 0) num = rb_int_floor(num, ndigits);
+ return num;
+ }
+}
+
/*
* call-seq:
* float.floor([ndigits]) -> integer or float
*
- * Returns the largest number less than or equal to +float+ in
- * decimal digits (default 0 digits).
+ * Returns the largest number less than or equal to +float+ with
+ * a precision of +ndigits+ decimal digits (default: 0).
+ *
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
*
- * Precision may be negative. Returns a floating point number when +ndigits+
- * is positive, +self+ for zero, and floor down for negative.
+ * Returns a floating point number when +ndigits+ is positive,
+ * otherwise returns an integer.
*
* 1.2.floor #=> 1
* 2.0.floor #=> 2
* (-1.2).floor #=> -2
* (-2.0).floor #=> -2
*
- * 1.234567.floor(2) #=> 1.23
- * 1.234567.floor(3) #=> 1.234
- * 1.234567.floor(4) #=> 1.2345
- * 1.234567.floor(5) #=> 1.23456
- *
- * 34567.89.floor(-5) #=> 0
- * 34567.89.floor(-4) #=> 30000
- * 34567.89.floor(-3) #=> 34000
- * 34567.89.floor(-2) #=> 34500
- * 34567.89.floor(-1) #=> 34560
- * 34567.89.floor(0) #=> 34567
- * 34567.89.floor(1) #=> 34567.8
- * 34567.89.floor(2) #=> 34567.89
- * 34567.89.floor(3) #=> 34567.89
+ * 1.234567.floor(2) #=> 1.23
+ * 1.234567.floor(3) #=> 1.234
+ * 1.234567.floor(4) #=> 1.2345
+ * 1.234567.floor(5) #=> 1.23456
+ *
+ * 34567.89.floor(-5) #=> 0
+ * 34567.89.floor(-4) #=> 30000
+ * 34567.89.floor(-3) #=> 34000
+ * 34567.89.floor(-2) #=> 34500
+ * 34567.89.floor(-1) #=> 34560
+ * 34567.89.floor(0) #=> 34567
+ * 34567.89.floor(1) #=> 34567.8
+ * 34567.89.floor(2) #=> 34567.89
+ * 34567.89.floor(3) #=> 34567.89
+ *
+ * Note that the limited precision of floating point arithmetic
+ * might lead to surprising results:
+ *
+ * (0.3 / 0.1).floor #=> 2 (!)
*/
static VALUE
flo_floor(int argc, VALUE *argv, VALUE num)
{
- double number, f;
- long val;
int ndigits = 0;
-
if (rb_check_arity(argc, 0, 1)) {
ndigits = NUM2INT(argv[0]);
}
- if (ndigits < 0) {
- return rb_int_floor(flo_to_i(num), ndigits);
- }
- number = RFLOAT_VALUE(num);
- if (ndigits > 0) {
- if (float_invariant_round(number, ndigits, &num)) return num;
- f = pow(10, ndigits);
- f = floor(number * f) / f;
- return DBL2NUM(f);
- }
- f = floor(number);
- if (!FIXABLE(f)) {
- return rb_dbl2big(f);
- }
- val = (long)f;
- return LONG2FIX(val);
+ return rb_float_floor(num, ndigits);
}
/*
* call-seq:
* float.ceil([ndigits]) -> integer or float
*
- * Returns the smallest number greater than or equal to +float+ in decimal
- * digits (default 0 digits).
+ * Returns the smallest number greater than or equal to +float+ with
+ * a precision of +ndigits+ decimal digits (default: 0).
+ *
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
*
- * Precision may be negative. Returns a floating point number when +ndigits+
- * is positive, +self+ for zero, and ceil up for negative.
+ * Returns a floating point number when +ndigits+ is positive,
+ * otherwise returns an integer.
*
* 1.2.ceil #=> 2
* 2.0.ceil #=> 2
* (-1.2).ceil #=> -1
* (-2.0).ceil #=> -2
- * 1.234567.ceil(2) #=> 1.24
- * 1.234567.ceil(3) #=> 1.235
- * 1.234567.ceil(4) #=> 1.2346
- * 1.234567.ceil(5) #=> 1.23457
- *
- * 34567.89.ceil(-5) #=> 100000
- * 34567.89.ceil(-4) #=> 40000
- * 34567.89.ceil(-3) #=> 35000
- * 34567.89.ceil(-2) #=> 34600
- * 34567.89.ceil(-1) #=> 34570
- * 34567.89.ceil(0) #=> 34568
- * 34567.89.ceil(1) #=> 34567.9
- * 34567.89.ceil(2) #=> 34567.89
- * 34567.89.ceil(3) #=> 34567.89
+ *
+ * 1.234567.ceil(2) #=> 1.24
+ * 1.234567.ceil(3) #=> 1.235
+ * 1.234567.ceil(4) #=> 1.2346
+ * 1.234567.ceil(5) #=> 1.23457
+ *
+ * 34567.89.ceil(-5) #=> 100000
+ * 34567.89.ceil(-4) #=> 40000
+ * 34567.89.ceil(-3) #=> 35000
+ * 34567.89.ceil(-2) #=> 34600
+ * 34567.89.ceil(-1) #=> 34570
+ * 34567.89.ceil(0) #=> 34568
+ * 34567.89.ceil(1) #=> 34567.9
+ * 34567.89.ceil(2) #=> 34567.89
+ * 34567.89.ceil(3) #=> 34567.89
+ *
+ * Note that the limited precision of floating point arithmetic
+ * might lead to surprising results:
+ *
+ * (2.1 / 0.7).ceil #=> 4 (!)
*/
static VALUE
@@ -1985,18 +2011,27 @@ flo_ceil(int argc, VALUE *argv, VALUE num)
int ndigits = 0;
if (rb_check_arity(argc, 0, 1)) {
- ndigits = NUM2INT(argv[0]);
+ ndigits = NUM2INT(argv[0]);
}
number = RFLOAT_VALUE(num);
- if (ndigits < 0) {
- return rb_int_ceil(dbl2ival(ceil(number)), ndigits);
+ if (number == 0.0) {
+ return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
}
- if (ndigits == 0) {
- return dbl2ival(ceil(number));
+ 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);
+ }
+ else {
+ num = dbl2ival(ceil(number));
+ if (ndigits < 0) num = rb_int_ceil(num, ndigits);
+ return num;
}
- if (float_invariant_round(number, ndigits, &num)) return num;
- f = pow(10, ndigits);
- return DBL2NUM(ceil(number * f) / f);
}
static int
@@ -2033,6 +2068,30 @@ int_round_half_up(SIGNED_VALUE x, SIGNED_VALUE y)
return (x + y / 2) / y * y;
}
+static SIGNED_VALUE
+int_round_half_down(SIGNED_VALUE x, SIGNED_VALUE y)
+{
+ return (x + y / 2 - 1) / y * y;
+}
+
+static int
+int_half_p_half_even(VALUE num, VALUE n, VALUE f)
+{
+ return (int)rb_int_odd_p(rb_int_idiv(n, f));
+}
+
+static int
+int_half_p_half_up(VALUE num, VALUE n, VALUE f)
+{
+ return int_pos_p(num);
+}
+
+static int
+int_half_p_half_down(VALUE num, VALUE n, VALUE f)
+{
+ return int_neg_p(num);
+}
+
/*
* Assumes num is an Integer, ndigits <= 0
*/
@@ -2050,9 +2109,7 @@ rb_int_round(VALUE num, int ndigits, enum ruby_num_rounding_mode mode)
SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f);
int neg = x < 0;
if (neg) x = -x;
- x = ROUND_TO(mode,
- int_round_half_up(x, y),
- int_round_half_even(x, y));
+ x = ROUND_CALL(mode, int_round, (x, y));
if (neg) x = -x;
return LONG2NUM(x);
}
@@ -2065,10 +2122,7 @@ rb_int_round(VALUE num, int ndigits, enum ruby_num_rounding_mode mode)
n = rb_int_minus(num, r);
r = rb_int_cmp(r, h);
if (FIXNUM_POSITIVE_P(r) ||
- (FIXNUM_ZERO_P(r) &&
- ROUND_TO(mode,
- int_pos_p(num),
- (SIGNED_VALUE) int_odd_p(rb_int_idiv(n, f))))) {
+ (FIXNUM_ZERO_P(r) && ROUND_CALL(mode, int_half_p, (num, n, f)))) {
n = rb_int_plus(n, f);
}
return n;
@@ -2153,33 +2207,54 @@ rb_int_truncate(VALUE num, int ndigits)
/*
* call-seq:
- * float.round([ndigits]) -> integer or float
+ * float.round([ndigits] [, half: mode]) -> integer or float
+ *
+ * Returns +float+ rounded to the nearest value with
+ * a precision of +ndigits+ decimal digits (default: 0).
*
- * Rounds +float+ to a given precision in decimal digits (default 0 digits).
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
*
- * Precision may be negative. Returns a floating point number when +ndigits+
- * is more than zero.
+ * Returns a floating point number when +ndigits+ is positive,
+ * otherwise returns an integer.
*
* 1.4.round #=> 1
* 1.5.round #=> 2
* 1.6.round #=> 2
* (-1.5).round #=> -2
*
- * 1.234567.round(2) #=> 1.23
- * 1.234567.round(3) #=> 1.235
- * 1.234567.round(4) #=> 1.2346
- * 1.234567.round(5) #=> 1.23457
- *
- * 34567.89.round(-5) #=> 0
- * 34567.89.round(-4) #=> 30000
- * 34567.89.round(-3) #=> 35000
- * 34567.89.round(-2) #=> 34600
- * 34567.89.round(-1) #=> 34570
- * 34567.89.round(0) #=> 34568
- * 34567.89.round(1) #=> 34567.9
- * 34567.89.round(2) #=> 34567.89
- * 34567.89.round(3) #=> 34567.89
- *
+ * 1.234567.round(2) #=> 1.23
+ * 1.234567.round(3) #=> 1.235
+ * 1.234567.round(4) #=> 1.2346
+ * 1.234567.round(5) #=> 1.23457
+ *
+ * 34567.89.round(-5) #=> 0
+ * 34567.89.round(-4) #=> 30000
+ * 34567.89.round(-3) #=> 35000
+ * 34567.89.round(-2) #=> 34600
+ * 34567.89.round(-1) #=> 34570
+ * 34567.89.round(0) #=> 34568
+ * 34567.89.round(1) #=> 34567.9
+ * 34567.89.round(2) #=> 34567.89
+ * 34567.89.round(3) #=> 34567.89
+ *
+ * If the optional +half+ keyword argument is given,
+ * numbers that are half-way between two possible rounded values
+ * will be rounded according to the specified tie-breaking +mode+:
+ *
+ * * <code>:up</code> or +nil+: round half away from zero (default)
+ * * <code>:down</code>: round half toward zero
+ * * <code>:even</code>: round half toward the nearest even number
+ *
+ * 2.5.round(half: :up) #=> 3
+ * 2.5.round(half: :down) #=> 2
+ * 2.5.round(half: :even) #=> 2
+ * 3.5.round(half: :up) #=> 4
+ * 3.5.round(half: :down) #=> 3
+ * 3.5.round(half: :even) #=> 4
+ * (-2.5).round(half: :up) #=> -3
+ * (-2.5).round(half: :down) #=> -2
+ * (-2.5).round(half: :even) #=> -2
*/
static VALUE
@@ -2194,29 +2269,33 @@ flo_round(int argc, VALUE *argv, VALUE num)
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);
+ }
if (ndigits < 0) {
return rb_int_round(flo_to_i(num), ndigits, mode);
}
- number = RFLOAT_VALUE(num);
if (ndigits == 0) {
- x = ROUND_TO(mode,
- round(number), round_half_even(number, 1.0));
+ x = ROUND_CALL(mode, round, (number, 1.0));
return dbl2ival(x);
}
- if (float_invariant_round(number, ndigits, &num)) return num;
- f = pow(10, ndigits);
- x = ROUND_TO(mode,
- round_half_up(number, f), round_half_even(number, f));
- return DBL2NUM(x / f);
+ 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);
+ f = pow(10, ndigits);
+ x = ROUND_CALL(mode, round, (number, f));
+ return DBL2NUM(x / f);
+ }
+ return num;
}
static int
-float_invariant_round(double number, int ndigits, VALUE *num)
+float_round_overflow(int ndigits, int binexp)
{
enum {float_dig = DBL_DIG+2};
- int binexp;
-
- frexp(number, &binexp);
/* Let `exp` be such that `number` is written as:"0.#{digits}e#{exp}",
i.e. such that 10 ** (exp - 1) <= |number| < 10 ** exp
@@ -2235,12 +2314,16 @@ float_invariant_round(double number, int ndigits, VALUE *num)
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 (isinf(number) || isnan(number) ||
- (ndigits >= float_dig - (binexp > 0 ? binexp / 4 : binexp / 3 - 1))) {
+ if (ndigits >= float_dig - (binexp > 0 ? binexp / 4 : binexp / 3 - 1)) {
return TRUE;
}
+ return FALSE;
+}
+
+static int
+float_round_underflow(int ndigits, int binexp)
+{
if (ndigits < - (binexp > 0 ? binexp / 3 + 1 : binexp / 4)) {
- *num = DBL2NUM(0);
return TRUE;
}
return FALSE;
@@ -2248,38 +2331,55 @@ float_invariant_round(double number, int ndigits, VALUE *num)
/*
* call-seq:
- * float.to_i -> integer
- * float.to_int -> integer
+ * float.to_i -> integer
+ * float.to_int -> integer
*
* Returns the +float+ truncated to an Integer.
*
- * Synonyms are #to_i and #to_int
+ * 1.2.to_i #=> 1
+ * (-1.2).to_i #=> -1
+ *
+ * Note that the limited precision of floating point arithmetic
+ * might lead to surprising results:
+ *
+ * (0.3 / 0.1).to_i #=> 2 (!)
+ *
+ * #to_int is an alias for #to_i.
*/
static VALUE
flo_to_i(VALUE num)
{
double f = RFLOAT_VALUE(num);
- long val;
if (f > 0.0) f = floor(f);
if (f < 0.0) f = ceil(f);
- if (!FIXABLE(f)) {
- return rb_dbl2big(f);
- }
- val = (long)f;
- return LONG2FIX(val);
+ return dbl2ival(f);
}
/*
* call-seq:
* float.truncate([ndigits]) -> integer or float
*
- * Truncates +float+ to a given precision in decimal digits (default 0 digits).
+ * Returns +float+ truncated (toward zero) to
+ * a precision of +ndigits+ decimal digits (default: 0).
+ *
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
+ *
+ * Returns a floating point number when +ndigits+ is positive,
+ * otherwise returns an integer.
+ *
+ * 2.8.truncate #=> 2
+ * (-2.8).truncate #=> -2
+ * 1.234567.truncate(2) #=> 1.23
+ * 34567.89.truncate(-2) #=> 34500
+ *
+ * Note that the limited precision of floating point arithmetic
+ * might lead to surprising results:
*
- * Precision may be negative. Returns a floating point number when +ndigits+
- * is more than zero.
+ * (0.3 / 0.1).truncate #=> 2 (!)
*/
static VALUE
flo_truncate(int argc, VALUE *argv, VALUE num)
@@ -2292,7 +2392,7 @@ flo_truncate(int argc, VALUE *argv, VALUE num)
/*
* call-seq:
- * float.positive? -> true or false
+ * float.positive? -> true or false
*
* Returns +true+ if +float+ is greater than 0.
*/
@@ -2306,7 +2406,7 @@ flo_positive_p(VALUE num)
/*
* call-seq:
- * float.negative? -> true or false
+ * float.negative? -> true or false
*
* Returns +true+ if +float+ is less than 0.
*/
@@ -2322,13 +2422,11 @@ flo_negative_p(VALUE num)
* call-seq:
* num.floor([ndigits]) -> integer or float
*
- * Returns the largest integer less than or equal to +num+.
+ * Returns the largest number less than or equal to +num+ with
+ * a precision of +ndigits+ decimal digits (default: 0).
*
- * Numeric implements this by converting an Integer to a Float and invoking
- * Float#floor.
- *
- * 1.floor #=> 1
- * (-1).floor #=> -1
+ * Numeric implements this by converting its value to a Float and
+ * invoking Float#floor.
*/
static VALUE
@@ -2337,21 +2435,15 @@ num_floor(int argc, VALUE *argv, VALUE num)
return flo_floor(argc, argv, rb_Float(num));
}
-
/*
* call-seq:
* num.ceil([ndigits]) -> integer or float
*
- * Returns the smallest possible Integer that is greater than or equal to
- * +num+.
- *
- * Numeric achieves this by converting itself to a Float then invoking
- * Float#ceil.
+ * Returns the smallest number greater than or equal to +num+ with
+ * a precision of +ndigits+ decimal digits (default: 0).
*
- * 1.ceil #=> 1
- * 1.2.ceil #=> 2
- * (-1.2).ceil #=> -1
- * (-1.0).ceil #=> -1
+ * Numeric implements this by converting its value to a Float and
+ * invoking Float#ceil.
*/
static VALUE
@@ -2364,13 +2456,11 @@ num_ceil(int argc, VALUE *argv, VALUE num)
* call-seq:
* num.round([ndigits]) -> integer or float
*
- * Rounds +num+ to a given precision in decimal digits (default 0 digits).
- *
- * Precision may be negative. Returns a floating point number when +ndigits+
- * is more than zero.
+ * Returns +num+ rounded to the nearest value with
+ * a precision of +ndigits+ decimal digits (default: 0).
*
- * Numeric implements this by converting itself to a Float and invoking
- * Float#round.
+ * Numeric implements this by converting its value to a Float and
+ * invoking Float#round.
*/
static VALUE
@@ -2383,10 +2473,11 @@ num_round(int argc, VALUE* argv, VALUE num)
* call-seq:
* num.truncate([ndigits]) -> integer or float
*
- * Returns +num+ truncated to an Integer.
+ * Returns +num+ truncated (toward zero) to
+ * a precision of +ndigits+ decimal digits (default: 0).
*
- * Numeric implements this by converting its value to a Float and invoking
- * Float#truncate.
+ * Numeric implements this by converting its value to a Float and
+ * invoking Float#truncate.
*/
static VALUE
@@ -2395,19 +2486,20 @@ num_truncate(int argc, VALUE *argv, VALUE num)
return flo_truncate(argc, argv, rb_Float(num));
}
-static double
+double
ruby_float_step_size(double beg, double end, double unit, int excl)
{
const double epsilon = DBL_EPSILON;
- double n = (end - beg)/unit;
- double err = (fabs(beg) + fabs(end) + fabs(end-beg)) / fabs(unit) * epsilon;
+ double n, err;
+ if (unit == 0) {
+ return HUGE_VAL;
+ }
+ n= (end - beg)/unit;
+ err = (fabs(beg) + fabs(end) + fabs(end-beg)) / fabs(unit) * epsilon;
if (isinf(unit)) {
return unit > 0 ? beg <= end : beg >= end;
}
- if (unit == 0) {
- return INFINITY;
- }
if (err>0.5) err=0.5;
if (excl) {
if (n<=0) return 0;
@@ -2424,11 +2516,11 @@ ruby_float_step_size(double beg, double end, double unit, int excl)
}
int
-ruby_float_step(VALUE from, VALUE to, VALUE step, int excl)
+ruby_float_step(VALUE from, VALUE to, VALUE step, int excl, int allow_endless)
{
if (RB_TYPE_P(from, T_FLOAT) || RB_TYPE_P(to, T_FLOAT) || RB_TYPE_P(step, T_FLOAT)) {
double beg = NUM2DBL(from);
- double end = NUM2DBL(to);
+ double end = (allow_endless && NIL_P(to)) ? HUGE_VAL : NUM2DBL(to);
double unit = NUM2DBL(step);
double n = ruby_float_step_size(beg, end, unit, excl);
long i;
@@ -2462,7 +2554,7 @@ ruby_num_interval_step_size(VALUE from, VALUE to, VALUE step, int excl)
diff = FIX2LONG(step);
if (diff == 0) {
- return DBL2NUM(INFINITY);
+ return DBL2NUM(HUGE_VAL);
}
delta = FIX2LONG(to) - FIX2LONG(from);
if (diff < 0) {
@@ -2488,7 +2580,7 @@ ruby_num_interval_step_size(VALUE from, VALUE to, VALUE step, int excl)
VALUE result;
ID cmp = '>';
switch (rb_cmpint(rb_num_coerce_cmp(step, INT2FIX(0), id_cmp), step, INT2FIX(0))) {
- case 0: return DBL2NUM(INFINITY);
+ case 0: return DBL2NUM(HUGE_VAL);
case -1: cmp = '<'; break;
}
if (RTEST(rb_funcall(from, cmp, 1, to))) return INT2FIX(0);
@@ -2500,17 +2592,11 @@ ruby_num_interval_step_size(VALUE from, VALUE to, VALUE step, int excl)
}
}
-static VALUE
-num_step_compare_with_zero(VALUE num)
-{
- VALUE zero = INT2FIX(0);
- return rb_check_funcall(num, '>', 1, &zero);
-}
-
static int
num_step_negative_p(VALUE num)
{
const ID mid = '<';
+ VALUE zero = INT2FIX(0);
VALUE r;
if (FIXNUM_P(num)) {
@@ -2521,7 +2607,8 @@ num_step_negative_p(VALUE num)
if (method_basic_p(rb_cInteger))
return BIGNUM_NEGATIVE_P(num);
}
- r = rb_rescue(num_step_compare_with_zero, num, coerce_rescue_quiet, Qnil);
+
+ r = rb_check_funcall(num, '>', 1, &zero);
if (r == Qundef) {
coerce_failed(num, INT2FIX(0));
}
@@ -2529,10 +2616,9 @@ num_step_negative_p(VALUE num)
}
static int
-num_step_scan_args(int argc, const VALUE *argv, VALUE *to, VALUE *step)
+num_step_extract_args(int argc, const VALUE *argv, VALUE *to, VALUE *step, VALUE *by)
{
VALUE hash;
- int desc;
argc = rb_scan_args(argc, argv, "02:", to, step, &hash);
if (!NIL_P(hash)) {
@@ -2547,28 +2633,47 @@ num_step_scan_args(int argc, const VALUE *argv, VALUE *to, VALUE *step)
}
if (values[1] != Qundef) {
if (argc > 1) rb_raise(rb_eArgError, "step is given twice");
- *step = values[1];
+ *by = values[1];
}
}
+
+ return argc;
+}
+
+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) {
+ *step = by;
+ }
else {
- /* compatibility */
- if (argc > 1 && NIL_P(*step)) {
- rb_raise(rb_eTypeError, "step must be numeric");
- }
- if (rb_equal(*step, INT2FIX(0))) {
- rb_raise(rb_eArgError, "step can't be 0");
- }
+ /* compatibility */
+ if (argc > 1 && NIL_P(*step)) {
+ rb_raise(rb_eTypeError, "step must be numeric");
+ }
+ if (!allow_zero_step && rb_equal(*step, INT2FIX(0))) {
+ rb_raise(rb_eArgError, "step can't be 0");
+ }
}
if (NIL_P(*step)) {
*step = INT2FIX(1);
}
desc = num_step_negative_p(*step);
- if (NIL_P(*to)) {
- *to = desc ? DBL2NUM(-INFINITY) : DBL2NUM(INFINITY);
+ if (fix_nil && NIL_P(*to)) {
+ *to = desc ? DBL2NUM(-HUGE_VAL) : DBL2NUM(HUGE_VAL);
}
return desc;
}
+static int
+num_step_scan_args(int argc, const VALUE *argv, VALUE *to, VALUE *step, int fix_nil, int allow_zero_step)
+{
+ VALUE by = Qundef;
+ argc = num_step_extract_args(argc, argv, to, step, &by);
+ return num_step_check_fix_args(argc, to, step, by, fix_nil, allow_zero_step);
+}
+
static VALUE
num_step_size(VALUE from, VALUE args, VALUE eobj)
{
@@ -2576,54 +2681,57 @@ num_step_size(VALUE from, VALUE args, VALUE eobj)
int argc = args ? RARRAY_LENINT(args) : 0;
const VALUE *argv = args ? RARRAY_CONST_PTR(args) : 0;
- num_step_scan_args(argc, argv, &to, &step);
+ num_step_scan_args(argc, argv, &to, &step, TRUE, FALSE);
return ruby_num_interval_step_size(from, to, step, FALSE);
}
+
/*
* call-seq:
* num.step(by: step, to: limit) {|i| block } -> self
- * num.step(by: step, to: limit) -> an_enumerator
+ * num.step(by: step, to: limit) -> an_enumerator
+ * num.step(by: step, to: limit) -> an_arithmetic_sequence
* num.step(limit=nil, step=1) {|i| block } -> self
* num.step(limit=nil, step=1) -> an_enumerator
+ * num.step(limit=nil, step=1) -> an_arithmetic_sequence
*
* Invokes the given block with the sequence of numbers starting at +num+,
* incremented by +step+ (defaulted to +1+) on each call.
*
* The loop finishes when the value to be passed to the block is greater than
* +limit+ (if +step+ is positive) or less than +limit+ (if +step+ is
- * negative), where <i>limit</i> is defaulted to infinity.
+ * negative), where +limit+ is defaulted to infinity.
*
* In the recommended keyword argument style, either or both of
* +step+ and +limit+ (default infinity) can be omitted. In the
* fixed position argument style, zero as a step
- * (i.e. num.step(limit, 0)) is not allowed for historical
+ * (i.e. <code>num.step(limit, 0)</code>) is not allowed for historical
* compatibility reasons.
*
* If all the arguments are integers, the loop operates using an integer
* counter.
*
- * If any of the arguments are floating point numbers, all are converted to floats, and the loop is executed the following expression:
- *
- * floor(n + n*epsilon)+ 1
+ * If any of the arguments are floating point numbers, all are converted
+ * to floats, and the loop is executed
+ * <i>floor(n + n*Float::EPSILON) + 1</i> times,
+ * where <i>n = (limit - num)/step</i>.
*
- * Where the +n+ is the following:
- *
- * n = (limit - num)/step
- *
- * Otherwise, the loop starts at +num+, uses either the less-than (<) or
- * greater-than (>) operator to compare the counter against +limit+, and
- * increments itself using the <code>+</code> operator.
+ * Otherwise, the loop starts at +num+, uses either the
+ * less-than (<code><</code>) or greater-than (<code>></code>) operator
+ * to compare the counter against +limit+,
+ * and increments itself using the <code>+</code> operator.
*
* If no block is given, an Enumerator is returned instead.
+ * Especially, the enumerator is an Enumerator::ArithmeticSequence
+ * if both +limit+ and +step+ are kind of Numeric or <code>nil</code>.
*
* For example:
*
* p 1.step.take(4)
* p 10.step(by: -1).take(4)
- * 3.step(to: 5) { |i| print i, " " }
- * 1.step(10, 2) { |i| print i, " " }
- * Math::E.step(to: Math::PI, by: 0.2) { |f| print f, " " }
+ * 3.step(to: 5) {|i| print i, " " }
+ * 1.step(10, 2) {|i| print i, " " }
+ * Math::E.step(to: Math::PI, by: 0.2) {|f| print f, " " }
*
* Will produce:
*
@@ -2631,7 +2739,7 @@ num_step_size(VALUE from, VALUE args, VALUE eobj)
* [10, 9, 8, 7]
* 3 4 5
* 1 3 5 7 9
- * 2.71828182845905 2.91828182845905 3.11828182845905
+ * 2.718281828459045 2.9182818284590453 3.118281828459045
*/
static VALUE
@@ -2640,9 +2748,26 @@ num_step(int argc, VALUE *argv, VALUE from)
VALUE to, step;
int desc, inf;
- RETURN_SIZED_ENUMERATOR(from, argc, argv, num_step_size);
+ if (!rb_block_given_p()) {
+ VALUE by = Qundef;
+
+ num_step_extract_args(argc, argv, &to, &step, &by);
+ if (by != Qundef) {
+ step = by;
+ }
+ if (NIL_P(step)) {
+ step = INT2FIX(1);
+ }
+ if ((NIL_P(to) || rb_obj_is_kind_of(to, rb_cNumeric)) &&
+ rb_obj_is_kind_of(step, rb_cNumeric)) {
+ return rb_arith_seq_new(from, ID2SYM(rb_frame_this_func()), argc, argv,
+ num_step_size, from, to, step, FALSE);
+ }
+
+ RETURN_SIZED_ENUMERATOR(from, argc, argv, num_step_size);
+ }
- desc = num_step_scan_args(argc, argv, &to, &step);
+ desc = num_step_scan_args(argc, argv, &to, &step, TRUE, FALSE);
if (rb_equal(step, INT2FIX(0))) {
inf = 1;
}
@@ -2673,7 +2798,7 @@ num_step(int argc, VALUE *argv, VALUE from)
}
}
}
- else if (!ruby_float_step(from, to, step, FALSE)) {
+ else if (!ruby_float_step(from, to, step, FALSE, FALSE)) {
VALUE i = from;
if (inf) {
@@ -2859,7 +2984,7 @@ rb_fix2uint(VALUE val)
}
num = FIX2ULONG(val);
- check_uint(num, negative_int_p(val));
+ check_uint(num, rb_num_negative_int_p(val));
return num;
}
#else
@@ -2945,7 +3070,7 @@ rb_fix2ushort(VALUE val)
}
num = FIX2ULONG(val);
- check_ushort(num, negative_int_p(val));
+ check_ushort(num, rb_num_negative_int_p(val));
return num;
}
@@ -2985,9 +3110,9 @@ rb_num2ll(VALUE val)
if (FIXNUM_P(val)) return (LONG_LONG)FIX2LONG(val);
else if (RB_TYPE_P(val, T_FLOAT)) {
- if (RFLOAT_VALUE(val) < LLONG_MAX_PLUS_ONE
- && (LLONG_MIN_MINUS_ONE_IS_LESS_THAN(RFLOAT_VALUE(val)))) {
- return (LONG_LONG)(RFLOAT_VALUE(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");
@@ -3017,11 +3142,11 @@ rb_num2ull(VALUE val)
return (LONG_LONG)FIX2LONG(val); /* this is FIX2LONG, intended */
}
else if (RB_TYPE_P(val, T_FLOAT)) {
- if (RFLOAT_VALUE(val) < ULLONG_MAX_PLUS_ONE
- && LLONG_MIN_MINUS_ONE_IS_LESS_THAN(RFLOAT_VALUE(val))) {
- if (0 <= RFLOAT_VALUE(val))
- return (unsigned LONG_LONG)(RFLOAT_VALUE(val));
- return (unsigned LONG_LONG)(LONG_LONG)(RFLOAT_VALUE(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");
@@ -3048,18 +3173,18 @@ rb_num2ull(VALUE val)
* Document-class: Integer
*
* Holds Integer values. You cannot add a singleton method to an
- * Integer. Any attempt to add a singleton method to an Integer object
- * will raise a TypeError.
+ * Integer object, any attempt to do so will raise a TypeError.
*
*/
/*
* call-seq:
- * int.to_i -> integer
+ * int.to_i -> integer
+ * int.to_int -> integer
*
- * As +int+ is already an Integer, all these methods simply return the receiver.
+ * Since +int+ is already an Integer, returns +self+.
*
- * Synonyms is #to_int
+ * #to_int is an alias for #to_i.
*/
static VALUE
@@ -3088,8 +3213,8 @@ int_int_p(VALUE num)
* Returns +true+ if +int+ is an odd number.
*/
-static VALUE
-int_odd_p(VALUE num)
+VALUE
+rb_int_odd_p(VALUE num)
{
if (FIXNUM_P(num)) {
if (num & 2) {
@@ -3130,13 +3255,56 @@ int_even_p(VALUE num)
}
/*
+ * call-seq:
+ * int.allbits?(mask) -> true or false
+ *
+ * Returns +true+ if all bits of <code>+int+ & +mask+</code> are 1.
+ */
+
+static VALUE
+int_allbits_p(VALUE num, VALUE mask)
+{
+ mask = rb_to_int(mask);
+ return rb_int_equal(rb_int_and(num, mask), mask);
+}
+
+/*
+ * call-seq:
+ * int.anybits?(mask) -> true or false
+ *
+ * Returns +true+ if any bits of <code>+int+ & +mask+</code> are 1.
+ */
+
+static VALUE
+int_anybits_p(VALUE num, VALUE mask)
+{
+ mask = rb_to_int(mask);
+ return num_zero_p(rb_int_and(num, mask)) ? Qfalse : Qtrue;
+}
+
+/*
+ * call-seq:
+ * int.nobits?(mask) -> true or false
+ *
+ * Returns +true+ if no bits of <code>+int+ & +mask+</code> are 1.
+ */
+
+static VALUE
+int_nobits_p(VALUE num, VALUE mask)
+{
+ mask = rb_to_int(mask);
+ return num_zero_p(rb_int_and(num, mask));
+}
+
+/*
* Document-method: Integer#succ
* Document-method: Integer#next
* call-seq:
* int.next -> integer
* int.succ -> integer
*
- * Returns the Integer equal to +int+ + 1.
+ * Returns the successor of +int+,
+ * i.e. the Integer equal to <code>int+1</code>.
*
* 1.next #=> 2
* (-1).next #=> 0
@@ -3163,7 +3331,8 @@ rb_int_succ(VALUE num)
* call-seq:
* int.pred -> integer
*
- * Returns the Integer equal to +int+ - 1.
+ * Returns the predecessor of +int+,
+ * i.e. the Integer equal to <code>int-1</code>.
*
* 1.pred #=> 0
* (-1).pred #=> -2
@@ -3193,8 +3362,8 @@ rb_int_pred(VALUE num)
* according to +encoding+.
*
* 65.chr #=> "A"
- * 230.chr #=> "\346"
- * 255.chr(Encoding::UTF_8) #=> "\303\277"
+ * 230.chr #=> "\xE6"
+ * 255.chr(Encoding::UTF_8) #=> "\u00FF"
*/
VALUE
@@ -3269,12 +3438,12 @@ int_chr(int argc, VALUE *argv, VALUE num)
*
* Returns the +int+ itself.
*
- * ?a.ord #=> 97
+ * 97.ord #=> 97
*
- * This method is intended for compatibility to character constant in Ruby
- * 1.9.
+ * This method is intended for compatibility to character literals
+ * in Ruby 1.9.
*
- * For example, ?a.ord returns 97 both in 1.8 and 1.9.
+ * For example, <code>?a.ord</code> returns 97 both in 1.8 and 1.9.
*/
static VALUE
@@ -3291,10 +3460,9 @@ int_ord(VALUE num)
/*
* Document-method: Integer#-@
* call-seq:
- * -int -> integer
+ * -int -> integer
*
- * Negates +int+.
- * (returns an integer whose value is 0-int)
+ * Returns +int+, negated.
*/
static VALUE
@@ -3320,8 +3488,8 @@ rb_int_uminus(VALUE num)
* call-seq:
* int.to_s(base=10) -> string
*
- * Returns a string containing the representation of +int+ radix +base+
- * (between 2 and 36).
+ * Returns a string containing the place-value representation of +int+
+ * with radix +base+ (between 2 and 36).
*
* 12345.to_s #=> "12345"
* 12345.to_s(2) #=> "11000000111001"
@@ -3330,7 +3498,6 @@ rb_int_uminus(VALUE num)
* 12345.to_s(16) #=> "3039"
* 12345.to_s(36) #=> "9ix"
* 78546939656932.to_s(36) #=> "rubyrules"
- *
*/
VALUE
@@ -3350,7 +3517,7 @@ rb_fix2str(VALUE x, int base)
(val < 0 && (x & 0xFFFFFFFF00000000ull) != 0xFFFFFFFF00000000ull)) {
rb_bug("Unnormalized Fixnum value %p", (void *)x);
}
-# elif
+# else
/* should do something like above code, but currently ruby does not know */
/* such platforms */
# endif
@@ -3403,25 +3570,17 @@ rb_int2str(VALUE x, int base)
/*
* Document-method: Integer#+
* call-seq:
- * int + numeric -> numeric_result
+ * int + numeric -> numeric_result
*
- * Performs addition: the class of the resulting object depends on the class of
- * +numeric+ and on the magnitude of the result. It may return a Bignum.
+ * Performs addition: the class of the resulting object depends on
+ * the class of +numeric+.
*/
static VALUE
fix_plus(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- long a, b, c;
- VALUE r;
-
- a = FIX2LONG(x);
- b = FIX2LONG(y);
- c = a + b;
- r = LONG2NUM(c);
-
- return r;
+ return rb_fix_plus_fix(x, y);
}
else if (RB_TYPE_P(y, T_BIGNUM)) {
return rb_big_plus(y, x);
@@ -3458,25 +3617,17 @@ rb_int_plus(VALUE x, VALUE y)
/*
* Document-method: Integer#-
* call-seq:
- * int - numeric -> numeric_result
+ * int - numeric -> numeric_result
*
- * Performs subtraction: the class of the resulting object depends on the class
- * of +numeric+ and on the magnitude of the result. It may return a Bignum.
+ * Performs subtraction: the class of the resulting object depends on
+ * the class of +numeric+.
*/
static VALUE
fix_minus(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- long a, b, c;
- VALUE r;
-
- a = FIX2LONG(x);
- b = FIX2LONG(y);
- c = a - b;
- r = LONG2NUM(c);
-
- return r;
+ return rb_fix_minus_fix(x, y);
}
else if (RB_TYPE_P(y, T_BIGNUM)) {
x = rb_int2big(FIX2LONG(x));
@@ -3503,18 +3654,17 @@ rb_int_minus(VALUE x, VALUE y)
}
-#define SQRT_LONG_MAX ((SIGNED_VALUE)1<<((SIZEOF_LONG*CHAR_BIT-1)/2))
+#define SQRT_LONG_MAX HALF_LONG_MSB
/*tests if N*N would overflow*/
#define FIT_SQRT_LONG(n) (((n)<SQRT_LONG_MAX)&&((n)>=-SQRT_LONG_MAX))
/*
* Document-method: Integer#*
* call-seq:
- * int * numeric -> numeric_result
+ * int * numeric -> numeric_result
*
- * Performs multiplication: the class of the resulting object depends on the
- * class of +numeric+ and on the magnitude of the result. It may return a
- * Bignum.
+ * Performs multiplication: the class of the resulting object depends on
+ * the class of +numeric+.
*/
static VALUE
@@ -3524,6 +3674,10 @@ fix_mul(VALUE x, VALUE y)
return rb_fix_mul_fix(x, y);
}
else if (RB_TYPE_P(y, T_BIGNUM)) {
+ switch (x) {
+ case INT2FIX(0): return x;
+ case INT2FIX(1): return y;
+ }
return rb_big_mul(y, x);
}
else if (RB_TYPE_P(y, T_FLOAT)) {
@@ -3553,44 +3707,50 @@ static double
fix_fdiv_double(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- return (double)FIX2LONG(x) / (double)FIX2LONG(y);
+ return double_div_double(FIX2LONG(x), FIX2LONG(y));
}
else if (RB_TYPE_P(y, T_BIGNUM)) {
return rb_big_fdiv_double(rb_int2big(FIX2LONG(x)), y);
}
else if (RB_TYPE_P(y, T_FLOAT)) {
- return (double)FIX2LONG(x) / RFLOAT_VALUE(y);
+ return double_div_double(FIX2LONG(x), RFLOAT_VALUE(y));
}
else {
- return RFLOAT_VALUE(rb_num_coerce_bin(x, y, rb_intern("fdiv")));
+ return NUM2DBL(rb_num_coerce_bin(x, y, rb_intern("fdiv")));
}
}
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);
+ }
+ }
if (FIXNUM_P(x)) {
return fix_fdiv_double(x, y);
}
else if (RB_TYPE_P(x, T_BIGNUM)) {
return rb_big_fdiv_double(x, y);
}
- return NAN;
+ else {
+ return nan("");
+ }
}
/*
* Document-method: Integer#fdiv
* call-seq:
- * integer.fdiv(numeric) -> float
+ * int.fdiv(numeric) -> float
*
- * Returns the floating point result of dividing +fix+ by +numeric+.
- *
- * 654321.fdiv(13731) #=> 47.6528293642124
- * 654321.fdiv(13731.24) #=> 47.6519964693647
- *
- * -1234567890987654321.fdiv(13731) #=> -89910996357705.5
- * -1234567890987654321.fdiv(13731.24) #=> -89909424858035.7
+ * Returns the floating point result of dividing +int+ by +numeric+.
*
+ * 654321.fdiv(13731) #=> 47.652829364212366
+ * 654321.fdiv(13731.24) #=> 47.65199646936475
+ * -654321.fdiv(13731) #=> -47.652829364212366
*/
VALUE
@@ -3605,10 +3765,10 @@ rb_int_fdiv(VALUE x, VALUE y)
/*
* Document-method: Integer#/
* call-seq:
- * int / numeric -> numeric_result
+ * int / numeric -> numeric_result
*
- * Performs division: the class of the resulting object depends on the class of
- * +numeric+ and on the magnitude of the result. It may return a Bignum.
+ * Performs division: the class of the resulting object depends on
+ * the class of +numeric+.
*/
static VALUE
@@ -3623,19 +3783,16 @@ fix_divide(VALUE x, VALUE y, ID op)
return rb_big_div(x, y);
}
else if (RB_TYPE_P(y, T_FLOAT)) {
- {
- double div;
-
if (op == '/') {
- div = (double)FIX2LONG(x) / RFLOAT_VALUE(y);
- return DBL2NUM(div);
+ double d = FIX2LONG(x);
+ return rb_flo_div_flo(DBL2NUM(d), y);
}
else {
+ VALUE v;
if (RFLOAT_VALUE(y) == 0) rb_num_zerodiv();
- div = (double)FIX2LONG(x) / RFLOAT_VALUE(y);
- return rb_dbl2big(floor(div));
+ v = fix_divide(x, y, '/');
+ return flo_floor(0, 0, v);
}
- }
}
else {
if (RB_TYPE_P(y, T_RATIONAL) &&
@@ -3666,10 +3823,10 @@ rb_int_div(VALUE x, VALUE y)
/*
* Document-method: Integer#div
* call-seq:
- * int.div(numeric) -> integer
+ * int.div(numeric) -> integer
*
- * Performs integer division: returns integer result of dividing +int+ by
- * +numeric+.
+ * Performs integer division: returns the integer result of dividing +int+
+ * by +numeric+.
*/
static VALUE
@@ -3694,8 +3851,8 @@ rb_int_idiv(VALUE x, VALUE y)
* Document-method: Integer#%
* Document-method: Integer#modulo
* call-seq:
- * int % other -> real
- * int.modulo(other) -> real
+ * int % other -> real
+ * int.modulo(other) -> real
*
* Returns +int+ modulo +other+.
*
@@ -3737,25 +3894,20 @@ rb_int_modulo(VALUE x, VALUE y)
* call-seq:
* int.remainder(numeric) -> real
*
+ * Returns the remainder after dividing +int+ by +numeric+.
*
- * Returns the remainder after dividing <i>big</i> by <i>numeric</i> as:
- *
- * x.remainder(y) means x-y*(x/y).truncate
+ * <code>x.remainder(y)</code> means <code>x-y*(x/y).truncate</code>.
*
- * Examples
- *
- * 5.remainder(3) #=> 2
- * -5.remainder(3) #=> -2
- * 5.remainder(-3) #=> 2
- * -5.remainder(-3) #=> -2
- *
- * -1234567890987654321.remainder(13731) #=> -6966
- * -1234567890987654321.remainder(13731.24) #=> -9906.22531493148
+ * 5.remainder(3) #=> 2
+ * -5.remainder(3) #=> -2
+ * 5.remainder(-3) #=> 2
+ * -5.remainder(-3) #=> -2
+ * 5.remainder(1.5) #=> 0.5
*
* See Numeric#divmod.
*/
-VALUE
+static VALUE
int_remainder(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
@@ -3770,9 +3922,9 @@ int_remainder(VALUE x, VALUE y)
/*
* Document-method: Integer#divmod
* call-seq:
- * integer.divmod(numeric) -> array
+ * int.divmod(numeric) -> array
*
- * See <code>Numeric#divmod</code>.
+ * See Numeric#divmod.
*/
static VALUE
fix_divmod(VALUE x, VALUE y)
@@ -3818,20 +3970,20 @@ rb_int_divmod(VALUE x, VALUE y)
/*
* Document-method: Integer#**
* call-seq:
- * integer ** numeric -> numeric_result
+ * int ** numeric -> numeric_result
*
- * Raises +integer+ to the power of +numeric+, which may be negative or
+ * Raises +int+ to the power of +numeric+, which may be negative or
* fractional.
- * The result may be an Integer, or a Float
- *
- * 2 ** 3 #=> 8
- * 2 ** -1 #=> (1/2)
- * 2 ** 0.5 #=> 1.4142135623731
+ * The result may be an Integer, a Float, a Rational, or a complex number.
*
- * 123456789 ** 2 #=> 15241578750190521
- * 123456789 ** 1.2 #=> 5126464716.09932
- * 123456789 ** -2 #=> (1/15241578750190521)
+ * 2 ** 3 #=> 8
+ * 2 ** -1 #=> (1/2)
+ * 2 ** 0.5 #=> 1.4142135623730951
+ * (-1) ** 0.5 #=> (0.0+1.0i)
*
+ * 123456789 ** 2 #=> 15241578750190521
+ * 123456789 ** 1.2 #=> 5126464716.0993185
+ * 123456789 ** -2 #=> (1/15241578750190521)
*/
static VALUE
@@ -3840,6 +3992,8 @@ int_pow(long x, unsigned long y)
int neg = x < 0;
long z = 1;
+ if (y == 0) return INT2FIX(1);
+ if (y == 1) return LONG2NUM(x);
if (neg) x = -x;
if (y & 1)
z = x;
@@ -3852,6 +4006,8 @@ int_pow(long x, unsigned long y)
VALUE v;
bignum:
v = rb_big_pow(rb_int2big(x), LONG2NUM(y));
+ if (RB_FLOAT_TYPE_P(v)) /* infinity due to overflow */
+ return v;
if (z != 1) v = rb_big_mul(rb_int2big(neg ? -z : z), v);
return v;
}
@@ -3890,14 +4046,17 @@ fix_pow(VALUE x, VALUE y)
else
return INT2FIX(-1);
}
- if (b < 0)
- return num_funcall1(rb_rational_raw1(x), idPow, y);
+ if (b < 0) {
+ if (a == 0) rb_num_zerodiv();
+ y = rb_int_pow(x, LONG2NUM(-b));
+ goto inverted;
+ }
if (b == 0) return INT2FIX(1);
if (b == 1) return x;
if (a == 0) {
if (b > 0) return INT2FIX(0);
- return DBL2NUM(INFINITY);
+ return DBL2NUM(HUGE_VAL);
}
return int_pow(a, b);
}
@@ -3907,22 +4066,30 @@ fix_pow(VALUE x, VALUE y)
if (int_even_p(y)) return INT2FIX(1);
else return INT2FIX(-1);
}
- if (negative_int_p(y))
- return num_funcall1(rb_rational_raw1(x), idPow, y);
+ if (BIGNUM_NEGATIVE_P(y)) {
+ if (a == 0) rb_num_zerodiv();
+ y = rb_int_pow(x, rb_big_uminus(y));
+ inverted:
+ if (RB_FLOAT_TYPE_P(y)) {
+ double d = pow((double)a, RFLOAT_VALUE(y));
+ return DBL2NUM(1.0 / d);
+ }
+ return rb_rational_raw(INT2FIX(1), y);
+ }
if (a == 0) return INT2FIX(0);
x = rb_int2big(FIX2LONG(x));
return rb_big_pow(x, y);
}
else if (RB_TYPE_P(y, T_FLOAT)) {
- if (RFLOAT_VALUE(y) == 0.0) return DBL2NUM(1.0);
+ double dy = RFLOAT_VALUE(y);
+ if (dy == 0.0) return DBL2NUM(1.0);
if (a == 0) {
- return DBL2NUM(RFLOAT_VALUE(y) < 0 ? INFINITY : 0.0);
+ return DBL2NUM(dy < 0 ? HUGE_VAL : 0.0);
}
if (a == 1) return DBL2NUM(1.0);
{
- double dy = RFLOAT_VALUE(y);
if (a < 0 && dy != round(dy))
- return num_funcall1(rb_complex_raw1(x), idPow, y);
+ return rb_dbl_complex_new_polar_pi(pow(-(double)a, dy), dy);
return DBL2NUM(pow((double)a, dy));
}
}
@@ -3943,17 +4110,33 @@ rb_int_pow(VALUE x, VALUE y)
return Qnil;
}
+VALUE
+rb_num_pow(VALUE x, VALUE y)
+{
+ VALUE z = rb_int_pow(x, y);
+ if (!NIL_P(z)) return z;
+ if (RB_FLOAT_TYPE_P(x)) return rb_float_pow(x, y);
+ if (SPECIAL_CONST_P(x)) return Qnil;
+ switch (BUILTIN_TYPE(x)) {
+ case T_COMPLEX:
+ return rb_complex_pow(x, y);
+ case T_RATIONAL:
+ return rb_rational_pow(x, y);
+ }
+ return Qnil;
+}
+
/*
* Document-method: Integer#==
+ * Document-method: Integer#===
* call-seq:
- * int == other -> true or false
+ * int == other -> true or false
*
- * Return +true+ if +int+ equals +other+ numerically.
- * Contrast this with <code>Integer#eql?</code>, which
- * requires <i>other</i> to be a <code>Integer</code>.
+ * Returns +true+ if +int+ equals +other+ numerically.
+ * Contrast this with Integer#eql?, which requires +other+ to be an Integer.
*
- * 1 == 2 #=> false
- * 1 == 1.0 #=> true
+ * 1 == 2 #=> false
+ * 1 == 1.0 #=> true
*/
static VALUE
@@ -3987,9 +4170,9 @@ rb_int_equal(VALUE x, VALUE y)
/*
* Document-method: Integer#<=>
* call-seq:
- * int <=> numeric -> -1, 0, +1 or nil
+ * int <=> numeric -> -1, 0, +1, or nil
*
- * Comparison---Returns +-1+, +0+, ++1+ or +nil+ depending on whether +int+ is
+ * Comparison---Returns -1, 0, or +1 depending on whether +int+ is
* less than, equal to, or greater than +numeric+.
*
* This is the basis for the tests in the Comparable module.
@@ -4039,7 +4222,7 @@ rb_int_cmp(VALUE x, VALUE y)
/*
* Document-method: Integer#>
* call-seq:
- * int > real -> true or false
+ * int > real -> true or false
*
* Returns +true+ if the value of +int+ is greater than that of +real+.
*/
@@ -4062,8 +4245,8 @@ fix_gt(VALUE x, VALUE y)
}
}
-static VALUE
-int_gt(VALUE x, VALUE y)
+VALUE
+rb_int_gt(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
return fix_gt(x, y);
@@ -4077,7 +4260,7 @@ int_gt(VALUE x, VALUE y)
/*
* Document-method: Integer#>=
* call-seq:
- * int >= real -> true or false
+ * int >= real -> true or false
*
* Returns +true+ if the value of +int+ is greater than or equal to that of
* +real+.
@@ -4117,7 +4300,7 @@ rb_int_ge(VALUE x, VALUE y)
/*
* Document-method: Integer#<
* call-seq:
- * int < real -> true or false
+ * int < real -> true or false
*
* Returns +true+ if the value of +int+ is less than that of +real+.
*/
@@ -4155,7 +4338,7 @@ int_lt(VALUE x, VALUE y)
/*
* Document-method: Integer#<=
* call-seq:
- * int <= real -> true or false
+ * int <= real -> true or false
*
* Returns +true+ if the value of +int+ is less than or equal to that of
* +real+.
@@ -4195,17 +4378,16 @@ int_le(VALUE x, VALUE y)
/*
* Document-method: Integer#~
* call-seq:
- * ~integer -> integer
+ * ~int -> integer
*
* One's complement: returns a number where each bit is flipped.
*
- * Inverts the bits in an integer. As Integers are conceptually infinite
- * length, the result acts as if it had an infinite number of one
- * bits to the left. In hex representations, this is displayed
+ * Inverts the bits in an Integer. As integers are conceptually of
+ * infinite length, the result acts as if it had an infinite number of
+ * one bits to the left. In hex representations, this is displayed
* as two periods to the left of the digits.
*
* sprintf("%X", ~0x1122334455) #=> "..FEEDDCCBBAA"
- *
*/
static VALUE
@@ -4258,7 +4440,7 @@ rb_num_coerce_bit(VALUE x, VALUE y, ID func)
/*
* Document-method: Integer#&
* call-seq:
- * integer & integer -> integer_result
+ * int & other_int -> integer
*
* Bitwise AND.
*/
@@ -4293,7 +4475,7 @@ rb_int_and(VALUE x, VALUE y)
/*
* Document-method: Integer#|
* call-seq:
- * integer | integer -> integer_result
+ * int | other_int -> integer
*
* Bitwise OR.
*/
@@ -4328,7 +4510,7 @@ int_or(VALUE x, VALUE y)
/*
* Document-method: Integer#^
* call-seq:
- * integer ^ integer -> integer_result
+ * int ^ other_int -> integer
*
* Bitwise EXCLUSIVE OR.
*/
@@ -4365,7 +4547,8 @@ int_xor(VALUE x, VALUE y)
* call-seq:
* int << count -> integer
*
- * Shifts +int+ left +count+ positions, or right if +count+ is negative.
+ * Returns +int+ shifted left +count+ positions, or right if +count+
+ * is negative.
*/
static VALUE
@@ -4410,7 +4593,8 @@ rb_int_lshift(VALUE x, VALUE y)
* call-seq:
* int >> count -> integer
*
- * Shifts +int+ right +count+ positions, or left if +count+ is negative.
+ * Returns +int+ shifted right +count+ positions, or left if +count+
+ * is negative.
*/
static VALUE
@@ -4456,19 +4640,16 @@ rb_int_rshift(VALUE x, VALUE y)
* call-seq:
* int[n] -> 0, 1
*
- * Bit Reference---Returns the +n+th bit in the binary representation of
- * +int+, where <code>int[0]</code> is the least significant bit.
- *
- * For example:
+ * Bit Reference---Returns the <code>n</code>th bit in the
+ * binary representation of +int+, where <code>int[0]</code>
+ * is the least significant bit.
*
* a = 0b11001100101010
- * 30.downto(0) do |n| print a[n] end
+ * 30.downto(0) {|n| print a[n] }
* #=> 0000000000000000011001100101010
*
* a = 9**15
- * 50.downto(0) do |n|
- * print a[n]
- * end
+ * 50.downto(0) {|n| print a[n] }
* #=> 000101110110100000111000011110010100111100010111001
*/
@@ -4516,9 +4697,8 @@ int_aref(VALUE num, VALUE idx)
* call-seq:
* int.to_f -> float
*
- * Converts +int+ to a +Float+. If +int+ doesn't fit in a +Float+,
+ * Converts +int+ to a Float. If +int+ doesn't fit in a Float,
* the result is infinity.
- *
*/
static VALUE
@@ -4548,10 +4728,11 @@ int_to_f(VALUE num)
*
* Returns the absolute value of +int+.
*
- * -12345.abs #=> 12345
- * 12345.abs #=> 12345
- * -1234567890987654321.abs #=> 1234567890987654321
+ * (-12345).abs #=> 12345
+ * -12345.abs #=> 12345
+ * 12345.abs #=> 12345
*
+ * Integer#magnitude is an alias for Integer#abs.
*/
static VALUE
@@ -4581,12 +4762,13 @@ rb_int_abs(VALUE num)
* call-seq:
* int.size -> int
*
- * Returns the number of bytes in the machine representation of +fix+.
+ * Returns the number of bytes in the machine representation of +int+
+ * (machine dependent).
*
- * 1.size #=> 4
- * -1.size #=> 4
- * 2147483647.size #=> 4
- * (256**10 - 1).size #=> 12
+ * 1.size #=> 8
+ * -1.size #=> 8
+ * 2147483647.size #=> 8
+ * (256**10 - 1).size #=> 10
* (256**20 - 1).size #=> 20
* (256**40 - 1).size #=> 40
*/
@@ -4612,20 +4794,17 @@ int_size(VALUE num)
/*
* Document-method: Integer#bit_length
* call-seq:
- * int.bit_length -> integer
+ * int.bit_length -> integer
*
- * Returns the number of bits of the value of <i>int</i>.
+ * Returns the number of bits of the value of +int+.
*
- * "the number of bits" means that
- * the bit position of the highest bit which is different to the sign bit.
- * (The bit position of the bit 2**n is n+1.)
+ * "Number of bits" means the bit position of the highest bit
+ * which is different from the sign bit
+ * (where the least significant bit has bit position 1).
* If there is no such bit (zero or minus one), zero is returned.
*
- * I.e. This method returns ceil(log2(int < 0 ? -int : int+1)).
+ * I.e. this method returns <i>ceil(log2(int < 0 ? -int : int+1))</i>.
*
- * (-2**10000-1).bit_length #=> 10001
- * (-2**10000).bit_length #=> 10000
- * (-2**10000+1).bit_length #=> 10000
* (-2**1000-1).bit_length #=> 1001
* (-2**1000).bit_length #=> 1000
* (-2**1000+1).bit_length #=> 1000
@@ -4647,11 +4826,8 @@ int_size(VALUE num)
* (2**1000-1).bit_length #=> 1000
* (2**1000).bit_length #=> 1001
* (2**1000+1).bit_length #=> 1001
- * (2**10000-1).bit_length #=> 10000
- * (2**10000).bit_length #=> 10001
- * (2**10000+1).bit_length #=> 10001
*
- * This method can be used to detect overflow in Array#pack as follows.
+ * This method can be used to detect overflow in Array#pack as follows:
*
* if n.bit_length < 32
* [n].pack("l") # no overflow
@@ -4684,13 +4860,15 @@ rb_int_bit_length(VALUE num)
/*
* Document-method: Integer#digits
* call-seq:
- * int.digits -> [int]
- * int.digits(base) -> [int]
+ * int.digits -> array
+ * int.digits(base) -> array
*
- * Returns the array including the digits extracted by place-value notation
- * with radix +base+ of +int+.
+ * Returns the digits of +int+'s place-value representation
+ * with radix +base+ (default: 10).
+ * The digits are returned as an array with the least significant digit
+ * as the first array element.
*
- * +base+ should be greater than or equal to 2.
+ * +base+ must be greater than or equal to 2.
*
* 12345.digits #=> [5, 4, 3, 2, 1]
* 12345.digits(7) #=> [4, 6, 6, 0, 5]
@@ -4799,10 +4977,7 @@ rb_int_digits(int argc, VALUE *argv, VALUE num)
*
* If no block is given, an Enumerator is returned instead.
*
- * For example:
- *
- * 5.upto(10) { |i| print i, " " }
- * #=> 5 6 7 8 9 10
+ * 5.upto(10) {|i| print i, " " } #=> 5 6 7 8 9 10
*/
static VALUE
@@ -4841,14 +5016,14 @@ int_upto(VALUE from, VALUE to)
* int.downto(limit) {|i| block } -> self
* int.downto(limit) -> an_enumerator
*
- * Iterates the given block, passing decreasing values from +int+ down to and
- * including +limit+.
+ * Iterates the given block, passing in decreasing values from +int+ down to
+ * and including +limit+.
*
* If no block is given, an Enumerator is returned instead.
*
* 5.downto(1) { |n| print n, ".. " }
- * print " Liftoff!\n"
- * #=> "5.. 4.. 3.. 2.. 1.. Liftoff!"
+ * puts "Liftoff!"
+ * #=> "5.. 4.. 3.. 2.. 1.. Liftoff!"
*/
static VALUE
@@ -4892,10 +5067,7 @@ int_downto(VALUE from, VALUE to)
*
* If no block is given, an Enumerator is returned instead.
*
- * 5.times do |i|
- * print i, " "
- * end
- * #=> 0 1 2 3 4
+ * 5.times {|i| print i, " " } #=> 0 1 2 3 4
*/
static VALUE
@@ -4938,16 +5110,33 @@ int_dotimes(VALUE num)
/*
* Document-method: Integer#round
* call-seq:
- * int.round([ndigits]) -> integer or float
+ * int.round([ndigits] [, half: mode]) -> integer or float
*
- * Rounds +int+ to a given precision in decimal digits (default 0 digits).
+ * Returns +int+ rounded to the nearest value with
+ * a precision of +ndigits+ decimal digits (default: 0).
*
- * Precision may be negative. Returns a floating point number when +ndigits+
- * is positive, +self+ for zero, and round down for negative.
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
*
- * 1.round #=> 1
- * 1.round(2) #=> 1.0
- * 15.round(-1) #=> 20
+ * Returns +self+ when +ndigits+ is zero or positive.
+ *
+ * 1.round #=> 1
+ * 1.round(2) #=> 1
+ * 15.round(-1) #=> 20
+ * (-15).round(-1) #=> -20
+ *
+ * The optional +half+ keyword argument is available
+ * similar to Float#round.
+ *
+ * 25.round(-1, half: :up) #=> 30
+ * 25.round(-1, half: :down) #=> 20
+ * 25.round(-1, half: :even) #=> 20
+ * 35.round(-1, half: :up) #=> 40
+ * 35.round(-1, half: :down) #=> 30
+ * 35.round(-1, half: :even) #=> 40
+ * (-25).round(-1, half: :up) #=> -30
+ * (-25).round(-1, half: :down) #=> -20
+ * (-25).round(-1, half: :even) #=> -20
*/
static VALUE
@@ -4960,10 +5149,7 @@ int_round(int argc, VALUE* argv, VALUE num)
if (!rb_scan_args(argc, argv, "01:", &nd, &opt)) return num;
ndigits = NUM2INT(nd);
mode = rb_num_get_rounding_option(opt);
- if (ndigits > 0) {
- return rb_Float(num);
- }
- if (ndigits == 0) {
+ if (ndigits >= 0) {
return num;
}
return rb_int_round(num, ndigits, mode);
@@ -4974,15 +5160,18 @@ int_round(int argc, VALUE* argv, VALUE num)
* call-seq:
* int.floor([ndigits]) -> integer or float
*
- * Returns the largest number less than or equal to +int+ in decimal
- * digits (default 0 digits).
+ * Returns the largest number less than or equal to +int+ with
+ * a precision of +ndigits+ decimal digits (default: 0).
+ *
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
*
- * Precision may be negative. Returns a floating point number when +ndigits+
- * is positive, +self+ for zero, and floor down for negative.
+ * Returns +self+ when +ndigits+ is zero or positive.
*
- * 1.floor #=> 1
- * 1.floor(2) #=> 1.0
- * 15.floor(-1) #=> 10
+ * 1.floor #=> 1
+ * 1.floor(2) #=> 1
+ * 18.floor(-1) #=> 10
+ * (-18).floor(-1) #=> -20
*/
static VALUE
@@ -4992,10 +5181,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 rb_Float(num);
- }
- if (ndigits == 0) {
+ if (ndigits >= 0) {
return num;
}
return rb_int_floor(num, ndigits);
@@ -5006,15 +5192,18 @@ int_floor(int argc, VALUE* argv, VALUE num)
* call-seq:
* int.ceil([ndigits]) -> integer or float
*
- * Returns the smallest number than or equal to +int+ in decimal
- * digits (default 0 digits).
+ * Returns the smallest number greater than or equal to +int+ with
+ * a precision of +ndigits+ decimal digits (default: 0).
*
- * Precision may be negative. Returns a floating point number when +ndigits+
- * is positive, +self+ for zero, and ceil up for negative.
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
*
- * 1.ceil #=> 1
- * 1.ceil(2) #=> 1.0
- * 15.ceil(-1) #=> 20
+ * Returns +self+ when +ndigits+ is zero or positive.
+ *
+ * 1.ceil #=> 1
+ * 1.ceil(2) #=> 1
+ * 18.ceil(-1) #=> 20
+ * (-18).ceil(-1) #=> -10
*/
static VALUE
@@ -5024,10 +5213,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 rb_Float(num);
- }
- if (ndigits == 0) {
+ if (ndigits >= 0) {
return num;
}
return rb_int_ceil(num, ndigits);
@@ -5038,15 +5224,18 @@ int_ceil(int argc, VALUE* argv, VALUE num)
* call-seq:
* int.truncate([ndigits]) -> integer or float
*
- * Returns the smallest number than or equal to +int+ in decimal
- * digits (default 0 digits).
+ * Returns +int+ truncated (toward zero) to
+ * a precision of +ndigits+ decimal digits (default: 0).
+ *
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
*
- * Precision may be negative. Returns a floating point number when +ndigits+
- * is positive, +self+ for zero, and truncate up for negative.
+ * Returns +self+ when +ndigits+ is zero or positive.
*
- * 1.truncate #=> 1
- * 1.truncate(2) #=> 1.0
- * 15.truncate(-1) #=> 10
+ * 1.truncate #=> 1
+ * 1.truncate(2) #=> 1
+ * 18.truncate(-1) #=> 10
+ * (-18).truncate(-1) #=> -10
*/
static VALUE
@@ -5056,38 +5245,134 @@ 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 rb_Float(num);
- }
- if (ndigits == 0) {
+ if (ndigits >= 0) {
return num;
}
return rb_int_truncate(num, ndigits);
}
+#define DEFINE_INT_SQRT(rettype, prefix, argtype) \
+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; \
+ } \
+ return (rettype)sqrt(argtype##_TO_DOUBLE(n)); \
+}
+
+#if SIZEOF_LONG*CHAR_BIT > DBL_MANT_DIG
+# define RB_ULONG_IN_DOUBLE_P(n) ((n) < (1UL << DBL_MANT_DIG))
+#else
+# define RB_ULONG_IN_DOUBLE_P(n) 1
+#endif
+#define RB_ULONG_TO_DOUBLE(n) (double)(n)
+#define RB_ULONG unsigned long
+DEFINE_INT_SQRT(unsigned long, rb_ulong, RB_ULONG)
+
+#if 2*SIZEOF_BDIGIT > SIZEOF_LONG
+# if 2*SIZEOF_BDIGIT*CHAR_BIT > DBL_MANT_DIG
+# define BDIGIT_DBL_IN_DOUBLE_P(n) ((n) < ((BDIGIT_DBL)1UL << DBL_MANT_DIG))
+# else
+# define BDIGIT_DBL_IN_DOUBLE_P(n) 1
+# endif
+# ifdef ULL_TO_DOUBLE
+# define BDIGIT_DBL_TO_DOUBLE(n) ULL_TO_DOUBLE(n)
+# else
+# define BDIGIT_DBL_TO_DOUBLE(n) (double)(n)
+# endif
+DEFINE_INT_SQRT(BDIGIT, rb_bdigit_dbl, BDIGIT_DBL)
+#endif
+
+#define domain_error(msg) \
+ rb_raise(rb_eMathDomainError, "Numerical argument is out of domain - " #msg)
+
+VALUE rb_big_isqrt(VALUE);
+
+/*
+ * Document-method: Integer::sqrt
+ * call-seq:
+ * Integer.sqrt(n) -> integer
+ *
+ * Returns the integer square root of the non-negative integer +n+,
+ * i.e. the largest non-negative integer less than or equal to the
+ * square root of +n+.
+ *
+ * Integer.sqrt(0) #=> 0
+ * Integer.sqrt(1) #=> 1
+ * Integer.sqrt(24) #=> 4
+ * Integer.sqrt(25) #=> 5
+ * Integer.sqrt(10**400) #=> 10**200
+ *
+ * Equivalent to <code>Math.sqrt(n).floor</code>, except that
+ * the result of the latter code may differ from the true value
+ * due to the limited precision of floating point arithmetic.
+ *
+ * Integer.sqrt(10**46) #=> 100000000000000000000000
+ * Math.sqrt(10**46).floor #=> 99999999999999991611392 (!)
+ *
+ * If +n+ is not an Integer, it is converted to an Integer first.
+ * If +n+ is negative, a Math::DomainError is raised.
+ */
+
+static VALUE
+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);
+ }
+ else {
+ 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);
+ }
+#endif
+ return rb_big_isqrt(num);
+ }
+}
+
/*
* Document-class: ZeroDivisionError
*
* Raised when attempting to divide an integer by 0.
*
- * 42 / 0
- * #=> ZeroDivisionError: divided by 0
+ * 42 / 0 #=> ZeroDivisionError: divided by 0
*
* Note that only division by an exact 0 will raise the exception:
*
- * 42 / 0.0 #=> Float::INFINITY
- * 42 / -0.0 #=> -Float::INFINITY
- * 0 / 0.0 #=> NaN
+ * 42 / 0.0 #=> Float::INFINITY
+ * 42 / -0.0 #=> -Float::INFINITY
+ * 0 / 0.0 #=> NaN
*/
/*
* Document-class: FloatDomainError
*
* Raised when attempting to convert special float values (in particular
- * +infinite+ or +NaN+) to numerical classes which don't support them.
+ * +Infinity+ or +NaN+) to numerical classes which don't support them.
*
- * Float::INFINITY.to_r
- * #=> FloatDomainError: Infinity
+ * Float::INFINITY.to_r #=> FloatDomainError: Infinity
*/
/*
@@ -5100,13 +5385,14 @@ int_truncate(int argc, VALUE* argv, VALUE num)
* object which is always passed by value.
*
* a = 1
- * puts 1.object_id == a.object_id #=> true
+ * 1.object_id == a.object_id #=> true
*
* There can only ever be one instance of the integer +1+, for example. Ruby ensures this
- * by preventing instantiation and duplication.
+ * by preventing instantiation. If duplication is attempted, the same instance is returned.
*
- * Integer.new(1) #=> NoMethodError: undefined method `new' for Integer:Class
- * 1.dup #=> TypeError: can't dup Integer
+ * Integer.new(1) #=> NoMethodError: undefined method `new' for Integer:Class
+ * 1.dup #=> 1
+ * 1.object_id == 1.dup.object_id #=> true
*
* For this reason, Numeric should be used when defining other numeric classes.
*
@@ -5181,8 +5467,9 @@ Init_Numeric(void)
rb_define_method(rb_cNumeric, "singleton_method_added", num_sadded, 1);
rb_include_module(rb_cNumeric, rb_mComparable);
- rb_define_method(rb_cNumeric, "initialize_copy", num_init_copy, 1);
rb_define_method(rb_cNumeric, "coerce", num_coerce, 1);
+ rb_define_method(rb_cNumeric, "clone", num_clone, -1);
+ rb_define_method(rb_cNumeric, "dup", num_dup, 0);
rb_define_method(rb_cNumeric, "i", num_imaginary, 0);
rb_define_method(rb_cNumeric, "+@", num_uplus, 0);
@@ -5217,12 +5504,16 @@ Init_Numeric(void)
rb_cInteger = rb_define_class("Integer", rb_cNumeric);
rb_undef_alloc_func(rb_cInteger);
rb_undef_method(CLASS_OF(rb_cInteger), "new");
+ rb_define_singleton_method(rb_cInteger, "sqrt", rb_int_s_isqrt, 1);
rb_define_method(rb_cInteger, "to_s", int_to_s, -1);
rb_define_alias(rb_cInteger, "inspect", "to_s");
rb_define_method(rb_cInteger, "integer?", int_int_p, 0);
- rb_define_method(rb_cInteger, "odd?", int_odd_p, 0);
+ rb_define_method(rb_cInteger, "odd?", rb_int_odd_p, 0);
rb_define_method(rb_cInteger, "even?", int_even_p, 0);
+ rb_define_method(rb_cInteger, "allbits?", int_allbits_p, 1);
+ rb_define_method(rb_cInteger, "anybits?", int_anybits_p, 1);
+ rb_define_method(rb_cInteger, "nobits?", int_nobits_p, 1);
rb_define_method(rb_cInteger, "upto", int_upto, 1);
rb_define_method(rb_cInteger, "downto", int_downto, 1);
rb_define_method(rb_cInteger, "times", int_dotimes, 0);
@@ -5253,12 +5544,14 @@ Init_Numeric(void)
rb_define_method(rb_cInteger, "fdiv", rb_int_fdiv, 1);
rb_define_method(rb_cInteger, "**", rb_int_pow, 1);
+ rb_define_method(rb_cInteger, "pow", rb_int_powm, -1); /* in bignum.c */
+
rb_define_method(rb_cInteger, "abs", rb_int_abs, 0);
rb_define_method(rb_cInteger, "magnitude", rb_int_abs, 0);
rb_define_method(rb_cInteger, "===", rb_int_equal, 1);
rb_define_method(rb_cInteger, "==", rb_int_equal, 1);
- rb_define_method(rb_cInteger, ">", int_gt, 1);
+ rb_define_method(rb_cInteger, ">", rb_int_gt, 1);
rb_define_method(rb_cInteger, ">=", rb_int_ge, 1);
rb_define_method(rb_cInteger, "<", int_lt, 1);
rb_define_method(rb_cInteger, "<=", int_le, 1);
@@ -5279,6 +5572,7 @@ Init_Numeric(void)
#ifndef RUBY_INTEGER_UNIFICATION
rb_cFixnum = rb_cInteger;
#endif
+ /* An obsolete class, use Integer */
rb_define_const(rb_cObject, "Fixnum", rb_cInteger);
rb_deprecate_constant(rb_cObject, "Fixnum");
@@ -5322,7 +5616,7 @@ Init_Numeric(void)
*/
rb_define_const(rb_cFloat, "DIG", INT2FIX(DBL_DIG));
/*
- * The smallest posable exponent value in a double-precision floating
+ * The smallest possible exponent value in a double-precision floating
* point.
*
* Usually defaults to -1021.
@@ -5376,20 +5670,20 @@ Init_Numeric(void)
/*
* An expression representing positive infinity.
*/
- rb_define_const(rb_cFloat, "INFINITY", DBL2NUM(INFINITY));
+ rb_define_const(rb_cFloat, "INFINITY", DBL2NUM(HUGE_VAL));
/*
* An expression representing a value which is "not a number".
*/
- rb_define_const(rb_cFloat, "NAN", DBL2NUM(NAN));
+ rb_define_const(rb_cFloat, "NAN", DBL2NUM(nan("")));
rb_define_method(rb_cFloat, "to_s", flo_to_s, 0);
rb_define_alias(rb_cFloat, "inspect", "to_s");
rb_define_method(rb_cFloat, "coerce", flo_coerce, 1);
- rb_define_method(rb_cFloat, "-@", flo_uminus, 0);
- rb_define_method(rb_cFloat, "+", flo_plus, 1);
- rb_define_method(rb_cFloat, "-", flo_minus, 1);
- rb_define_method(rb_cFloat, "*", flo_mul, 1);
- rb_define_method(rb_cFloat, "/", flo_div, 1);
+ rb_define_method(rb_cFloat, "-@", rb_float_uminus, 0);
+ rb_define_method(rb_cFloat, "+", rb_float_plus, 1);
+ rb_define_method(rb_cFloat, "-", rb_float_minus, 1);
+ rb_define_method(rb_cFloat, "*", rb_float_mul, 1);
+ rb_define_method(rb_cFloat, "/", rb_float_div, 1);
rb_define_method(rb_cFloat, "quo", flo_quo, 1);
rb_define_method(rb_cFloat, "fdiv", flo_quo, 1);
rb_define_method(rb_cFloat, "%", flo_mod, 1);
@@ -5399,15 +5693,15 @@ Init_Numeric(void)
rb_define_method(rb_cFloat, "==", flo_eq, 1);
rb_define_method(rb_cFloat, "===", flo_eq, 1);
rb_define_method(rb_cFloat, "<=>", flo_cmp, 1);
- rb_define_method(rb_cFloat, ">", flo_gt, 1);
+ rb_define_method(rb_cFloat, ">", rb_float_gt, 1);
rb_define_method(rb_cFloat, ">=", flo_ge, 1);
rb_define_method(rb_cFloat, "<", flo_lt, 1);
rb_define_method(rb_cFloat, "<=", flo_le, 1);
rb_define_method(rb_cFloat, "eql?", flo_eql, 1);
rb_define_method(rb_cFloat, "hash", flo_hash, 0);
rb_define_method(rb_cFloat, "to_f", flo_to_f, 0);
- rb_define_method(rb_cFloat, "abs", flo_abs, 0);
- rb_define_method(rb_cFloat, "magnitude", flo_abs, 0);
+ rb_define_method(rb_cFloat, "abs", rb_float_abs, 0);
+ rb_define_method(rb_cFloat, "magnitude", rb_float_abs, 0);
rb_define_method(rb_cFloat, "zero?", flo_zero_p, 0);
rb_define_method(rb_cFloat, "to_i", flo_to_i, 0);
@@ -5418,8 +5712,8 @@ Init_Numeric(void)
rb_define_method(rb_cFloat, "truncate", flo_truncate, -1);
rb_define_method(rb_cFloat, "nan?", flo_is_nan_p, 0);
- rb_define_method(rb_cFloat, "infinite?", flo_is_infinite_p, 0);
- rb_define_method(rb_cFloat, "finite?", flo_is_finite_p, 0);
+ rb_define_method(rb_cFloat, "infinite?", rb_flo_is_infinite_p, 0);
+ rb_define_method(rb_cFloat, "finite?", rb_flo_is_finite_p, 0);
rb_define_method(rb_cFloat, "next_float", flo_next_float, 0);
rb_define_method(rb_cFloat, "prev_float", flo_prev_float, 0);
rb_define_method(rb_cFloat, "positive?", flo_positive_p, 0);
diff --git a/object.c b/object.c
index 05bef4df28..f746c90d55 100644
--- a/object.c
+++ b/object.c
@@ -11,9 +11,10 @@
**********************************************************************/
-#include "internal.h"
+#include "ruby/encoding.h"
#include "ruby/st.h"
#include "ruby/util.h"
+#include "internal.h"
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
@@ -23,16 +24,23 @@
#include "id.h"
#include "probes.h"
-VALUE rb_cBasicObject;
-VALUE rb_mKernel;
-VALUE rb_cObject;
-VALUE rb_cModule;
-VALUE rb_cClass;
-VALUE rb_cData;
+/*!
+ * \defgroup object Core objects and their operations
+ * \{
+ */
+
+VALUE rb_cBasicObject; /*!< BasicObject class */
+VALUE rb_mKernel; /*!< Kernel module */
+VALUE rb_cObject; /*!< Object class */
+VALUE rb_cModule; /*!< Module class */
+VALUE rb_cClass; /*!< Class class */
+VALUE rb_cData; /*!< Data class */
+
+VALUE rb_cNilClass; /*!< NilClass class */
+VALUE rb_cTrueClass; /*!< TrueClass class */
+VALUE rb_cFalseClass; /*!< FalseClass class */
-VALUE rb_cNilClass;
-VALUE rb_cTrueClass;
-VALUE rb_cFalseClass;
+/*! \cond INTERNAL_MACRO */
#define id_eq idEq
#define id_eql idEqlP
@@ -42,11 +50,26 @@ VALUE rb_cFalseClass;
#define id_init_clone idInitialize_clone
#define id_init_dup idInitialize_dup
#define id_const_missing idConst_missing
+#define id_to_f idTo_f
#define CLASS_OR_MODULE_P(obj) \
(!SPECIAL_CONST_P(obj) && \
(BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE))
+/*! \endcond */
+
+/*!
+ * Make the object invisible from Ruby code.
+ *
+ * It is useful to let Ruby's GC manage your internal data structure --
+ * The object keeps being managed by GC, but \c ObjectSpace.each_object
+ * never yields the object.
+ *
+ * Note that the object also lose a way to call a method on it.
+ *
+ * \param[in] obj a Ruby object
+ * \sa rb_obj_reveal
+ */
VALUE
rb_obj_hide(VALUE obj)
{
@@ -56,6 +79,14 @@ rb_obj_hide(VALUE obj)
return obj;
}
+/*!
+ * Make a hidden object visible again.
+ *
+ * It is the caller's responsibility to pass the right \a klass
+ * which \a obj originally used to belong to.
+ *
+ * \sa rb_obj_hide
+ */
VALUE
rb_obj_reveal(VALUE obj, VALUE klass)
{
@@ -65,6 +96,14 @@ rb_obj_reveal(VALUE obj, VALUE klass)
return obj;
}
+/*!
+ * Fills common (\c RBasic) fields in \a obj.
+ *
+ * \note Prefer rb_newobj_of() to this function.
+ * \param[in,out] obj a Ruby object to be set up.
+ * \param[in] klass \c obj will belong to this class.
+ * \param[in] type one of \c ruby_value_type
+ */
VALUE
rb_obj_setup(VALUE obj, VALUE klass, VALUE type)
{
@@ -73,13 +112,16 @@ rb_obj_setup(VALUE obj, VALUE klass, VALUE type)
return obj;
}
-/*
+/**
* call-seq:
* obj === other -> true or false
*
* Case Equality -- For class Object, effectively the same as calling
* <code>#==</code>, but typically overridden by descendants to provide
* meaningful semantics in +case+ statements.
+ *--
+ * Same as \c Object#===, case equality.
+ *++
*/
VALUE
@@ -88,18 +130,38 @@ rb_equal(VALUE obj1, VALUE obj2)
VALUE result;
if (obj1 == obj2) return Qtrue;
- result = rb_funcall(obj1, id_eq, 1, obj2);
+ result = rb_equal_opt(obj1, obj2);
+ if (result == Qundef) {
+ result = rb_funcall(obj1, id_eq, 1, obj2);
+ }
if (RTEST(result)) return Qtrue;
return Qfalse;
}
+/**
+ * Determines if \a obj1 and \a obj2 are equal in terms of
+ * \c Object#eql?.
+ *
+ * \note It actually calls \c #eql? when necessary.
+ * So you cannot implement \c #eql? with this function.
+ * \retval non-zero if they are eql?
+ * \retval zero if they are not eql?.
+ */
int
rb_eql(VALUE obj1, VALUE obj2)
{
- return RTEST(rb_funcall(obj1, id_eql, 1, obj2));
+ VALUE result;
+
+ if (obj1 == obj2) return Qtrue;
+ result = rb_eql_opt(obj1, obj2);
+ if (result == Qundef) {
+ result = rb_funcall(obj1, id_eql, 1, obj2);
+ }
+ if (RTEST(result)) return Qtrue;
+ return Qfalse;
}
-/*
+/**
* call-seq:
* obj == other -> true or false
* obj.equal?(other) -> true or false
@@ -133,76 +195,60 @@ rb_eql(VALUE obj1, VALUE obj2)
*
* 1 == 1.0 #=> true
* 1.eql? 1.0 #=> false
+ *--
+ * \private
+ *++
*/
-
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_obj_equal(VALUE obj1, VALUE obj2)
{
if (obj1 == obj2) return Qtrue;
return Qfalse;
}
-#if 0
-/*
- * call-seq:
- * obj.hash -> integer
- *
- * Generates an Integer hash value for this object. This function must have the
- * property that <code>a.eql?(b)</code> implies <code>a.hash == b.hash</code>.
- *
- * The hash value is used along with #eql? by the Hash class to determine if
- * two objects reference the same hash key. Any hash value that exceeds the
- * capacity of an Integer will be truncated before being used.
- *
- * The hash value for an object may not be identical across invocations or
- * implementations of Ruby. If you need a stable identifier across Ruby
- * invocations and implementations you will need to generate one with a custom
- * method.
- */
-VALUE
-rb_obj_hash(VALUE obj)
-{
- VALUE oid = rb_obj_id(obj);
-#if SIZEOF_LONG == SIZEOF_VOIDP
- st_index_t index = NUM2LONG(oid);
-#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
- st_index_t index = NUM2LL(oid);
-#else
-# error not supported
-#endif
- return LONG2FIX(rb_objid_hash(index));
-}
-#else
VALUE rb_obj_hash(VALUE obj);
-#endif
-/*
+/**
* call-seq:
* !obj -> true or false
*
* Boolean negate.
+ *--
+ * \private
+ *++
*/
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_obj_not(VALUE obj)
{
return RTEST(obj) ? Qfalse : Qtrue;
}
-/*
+/**
* call-seq:
* obj != other -> true or false
*
* Returns true if two objects are not-equal, otherwise false.
+ *--
+ * \private
+ *++
*/
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_obj_not_equal(VALUE obj1, VALUE obj2)
{
VALUE result = rb_funcall(obj1, id_eq, 1, obj2);
return RTEST(result) ? Qfalse : Qtrue;
}
+/*!
+ * Looks up the nearest ancestor of \a cl, skipping singleton classes or
+ * module inclusions.
+ * It returns the \a cl itself if it is neither a singleton class or a module.
+ *
+ * \param[in] cl a Class object.
+ * \return the ancestor class found, or a falsey value if nothing found.
+ */
VALUE
rb_class_real(VALUE cl)
{
@@ -213,7 +259,7 @@ rb_class_real(VALUE cl)
return cl;
}
-/*
+/**
* call-seq:
* obj.class -> class
*
@@ -223,8 +269,12 @@ rb_class_real(VALUE cl)
*
* 1.class #=> Integer
* self.class #=> Object
+ *--
+ * Equivalent to \c Object\#class in Ruby.
+ *
+ * Returns the class of \c obj, skipping singleton classes or module inclusions.
+ *++
*/
-
VALUE
rb_obj_class(VALUE obj)
{
@@ -254,7 +304,8 @@ rb_obj_singleton_class(VALUE obj)
return rb_singleton_class(obj);
}
-void
+/*! \private */
+MJIT_FUNC_EXPORTED void
rb_obj_copy_ivar(VALUE dest, VALUE obj)
{
if (!(RBASIC(dest)->flags & ROBJECT_EMBED) && ROBJECT_IVPTR(dest)) {
@@ -297,6 +348,27 @@ init_copy(VALUE dest, VALUE obj)
}
}
+static int freeze_opt(int argc, VALUE *argv);
+static VALUE immutable_obj_clone(VALUE obj, int kwfreeze);
+static VALUE mutable_obj_clone(VALUE obj, int kwfreeze);
+PUREFUNC(static inline int special_object_p(VALUE obj)); /*!< \private */
+static inline int
+special_object_p(VALUE obj)
+{
+ if (SPECIAL_CONST_P(obj)) return TRUE;
+ switch (BUILTIN_TYPE(obj)) {
+ case T_BIGNUM:
+ case T_FLOAT:
+ case T_SYMBOL:
+ case T_RATIONAL:
+ case T_COMPLEX:
+ /* not a comprehensive list */
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
/*
* call-seq:
* obj.clone(freeze: true) -> an_object
@@ -325,32 +397,57 @@ init_copy(VALUE dest, VALUE obj)
static VALUE
rb_obj_clone2(int argc, VALUE *argv, VALUE obj)
{
+ int kwfreeze = freeze_opt(argc, argv);
+ if (!special_object_p(obj))
+ return mutable_obj_clone(obj, kwfreeze);
+ return immutable_obj_clone(obj, kwfreeze);
+}
+
+/*! \private */
+VALUE
+rb_immutable_obj_clone(int argc, VALUE *argv, VALUE obj)
+{
+ int kwfreeze = freeze_opt(argc, argv);
+ return immutable_obj_clone(obj, kwfreeze);
+}
+
+static int
+freeze_opt(int argc, VALUE *argv)
+{
static ID keyword_ids[1];
VALUE opt;
- VALUE kwargs[1];
- VALUE clone;
- VALUE singleton;
- VALUE kwfreeze = Qtrue;
+ VALUE kwfreeze;
if (!keyword_ids[0]) {
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, kwargs);
- kwfreeze = kwargs[0];
- if (kwfreeze != Qundef && kwfreeze != Qtrue && kwfreeze != Qfalse) {
- rb_raise(rb_eArgError, "unexpected value for freeze: %s",
- rb_builtin_class_name(kwfreeze));
+ rb_get_kwargs(opt, keyword_ids, 0, 1, &kwfreeze);
+ if (kwfreeze == Qfalse) return FALSE;
+ if (kwfreeze != Qundef && kwfreeze != Qtrue) {
+ rb_raise(rb_eArgError, "unexpected value for freeze: %"PRIsVALUE,
+ rb_obj_class(kwfreeze));
}
}
+ return TRUE;
+}
+
+static VALUE
+immutable_obj_clone(VALUE obj, int kwfreeze)
+{
+ if (!kwfreeze)
+ rb_raise(rb_eArgError, "can't unfreeze %"PRIsVALUE,
+ rb_obj_class(obj));
+ return obj;
+}
+
+static VALUE
+mutable_obj_clone(VALUE obj, int kwfreeze)
+{
+ VALUE clone, singleton;
- if (rb_special_const_p(obj)) {
- rb_raise(rb_eTypeError, "can't clone %s", rb_obj_classname(obj));
- }
clone = rb_obj_alloc(rb_obj_class(obj));
- RBASIC(clone)->flags &= (FL_TAINT|FL_PROMOTED0|FL_PROMOTED1);
- RBASIC(clone)->flags |= RBASIC(obj)->flags & ~(FL_PROMOTED0|FL_PROMOTED1|FL_FREEZE|FL_FINALIZE);
singleton = rb_singleton_class_clone_and_attach(obj, clone);
RBASIC_SET_CLASS(clone, singleton);
@@ -361,20 +458,27 @@ rb_obj_clone2(int argc, VALUE *argv, VALUE obj)
init_copy(clone, obj);
rb_funcall(clone, id_init_clone, 1, obj);
- if (Qfalse != kwfreeze) {
+ if (kwfreeze) {
RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE;
}
return clone;
}
+/**
+ * :nodoc
+ *--
+ * Almost same as \c Object#clone
+ *++
+ */
VALUE
rb_obj_clone(VALUE obj)
{
- return rb_obj_clone2(0, NULL, obj);
+ if (special_object_p(obj)) return obj;
+ return mutable_obj_clone(obj, Qtrue);
}
-/*
+/**
* call-seq:
* obj.dup -> an_object
*
@@ -414,16 +518,17 @@ rb_obj_clone(VALUE obj)
*
* s3 = s1.dup #=> #<Klass:0x401b3a38>
* s3.foo #=> NoMethodError: undefined method `foo' for #<Klass:0x401b3a38>
- *
+ *--
+ * Equivalent to \c Object\#dup in Ruby
+ *++
*/
-
VALUE
rb_obj_dup(VALUE obj)
{
VALUE dup;
- if (rb_special_const_p(obj)) {
- rb_raise(rb_eTypeError, "can't dup %s", rb_obj_classname(obj));
+ if (special_object_p(obj)) {
+ return obj;
}
dup = rb_obj_alloc(rb_obj_class(obj));
init_copy(dup, obj);
@@ -434,12 +539,12 @@ rb_obj_dup(VALUE obj)
/*
* call-seq:
- * obj.itself -> an_object
+ * obj.itself -> obj
*
- * Returns <i>obj</i>.
+ * Returns the receiver.
*
- * string = 'my string' #=> "my string"
- * string.itself.object_id == string.object_id #=> true
+ * string = "my string"
+ * string.itself.object_id == string.object_id #=> true
*
*/
@@ -449,7 +554,57 @@ rb_obj_itself(VALUE obj)
return obj;
}
-/* :nodoc: */
+static VALUE
+rb_obj_size(VALUE self, VALUE args, VALUE obj)
+{
+ return LONG2FIX(1);
+}
+
+/*
+ * call-seq:
+ * obj.then {|x| block } -> an_object
+ * obj.yield_self {|x| block } -> an_object
+ *
+ * Yields self to the block and returns the result of the block.
+ *
+ * 3.next.then {|x| x**x }.to_s #=> "256"
+ * "my string".yield_self {|s| s.upcase } #=> "MY STRING"
+ *
+ * Good usage for +yield_self+ is value piping in method chains:
+ *
+ * require 'open-uri'
+ * require 'json'
+ *
+ * construct_url(arguments).
+ * yield_self {|url| open(url).read }.
+ * yield_self {|response| JSON.parse(response) }
+ *
+ * When called without block, the method returns +Enumerator+,
+ * which can be used, for example, for conditional
+ * circuit-breaking:
+ *
+ * # meets condition, no-op
+ * 1.yield_self.detect(&:odd?) # => 1
+ * # does not meet condition, drop value
+ * 2.yield_self.detect(&:odd?) # => nil
+ *
+ */
+
+static VALUE
+rb_obj_yield_self(VALUE obj)
+{
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, rb_obj_size);
+ return rb_yield_values2(1, &obj);
+}
+
+/**
+ * :nodoc:
+ *--
+ * Default implementation of \c #initialize_copy
+ * \param[in,out] obj the receiver being initialized
+ * \param[in] orig the object to be copied from.
+ *++
+ */
VALUE
rb_obj_init_copy(VALUE obj, VALUE orig)
{
@@ -462,7 +617,15 @@ rb_obj_init_copy(VALUE obj, VALUE orig)
return obj;
}
-/* :nodoc: */
+/*!
+ * :nodoc:
+ *--
+ * Default implementation of \c #initialize_dup and \c #initialize_clone
+ *
+ * \param[in,out] obj the receiver being initialized
+ * \param[in] orig the object to be dup or cloned from.
+ *++
+ **/
VALUE
rb_obj_init_dup_clone(VALUE obj, VALUE orig)
{
@@ -470,7 +633,7 @@ rb_obj_init_dup_clone(VALUE obj, VALUE orig)
return obj;
}
-/*
+/**
* call-seq:
* obj.to_s -> string
*
@@ -478,8 +641,11 @@ rb_obj_init_dup_clone(VALUE obj, VALUE orig)
* <code>to_s</code> prints the object's class and an encoding of the
* object id. As a special case, the top-level object that is the
* initial execution context of Ruby programs returns ``main''.
+ *
+ *--
+ * Default implementation of \c #to_s.
+ *++
*/
-
VALUE
rb_any_to_s(VALUE obj)
{
@@ -493,7 +659,13 @@ rb_any_to_s(VALUE obj)
}
VALUE rb_str_escape(VALUE str);
-/*
+/*!
+ * Convenient wrapper of \c Object#inspect.
+ * Returns a human-readable string representation of \a obj,
+ * similarly to \c Object#inspect.
+ *
+ * Unlike Ruby-level \c #inspect, it escapes characters to keep the
+ * result compatible to the default internal or external encoding.
* If the default internal or external encoding is ASCII compatible,
* the encoding of the inspected result must be compatible with it.
* If the default internal or external encoding is ASCII incompatible,
@@ -503,6 +675,7 @@ VALUE
rb_inspect(VALUE obj)
{
VALUE str = rb_obj_as_string(rb_funcallv(obj, id_inspect, 0, 0));
+
rb_encoding *enc = rb_default_internal_encoding();
if (enc == NULL) enc = rb_default_external_encoding();
if (!rb_enc_asciicompat(enc)) {
@@ -616,7 +789,7 @@ class_or_module_required(VALUE c)
static VALUE class_search_ancestor(VALUE cl, VALUE c);
-/*
+/**
* call-seq:
* obj.instance_of?(class) -> true or false
*
@@ -631,6 +804,13 @@ static VALUE class_search_ancestor(VALUE cl, VALUE c);
* b.instance_of? A #=> false
* b.instance_of? B #=> true
* b.instance_of? C #=> false
+ *--
+ * Determines if \a obj is an instance of \a c.
+ *
+ * Equivalent to \c Object\#is_instance_of in Ruby.
+ * \param[in] obj the object to be determined.
+ * \param[in] c a Class object
+ *++
*/
VALUE
@@ -642,7 +822,7 @@ rb_obj_is_instance_of(VALUE obj, VALUE c)
}
-/*
+/**
* call-seq:
* obj.is_a?(class) -> true or false
* obj.kind_of?(class) -> true or false
@@ -668,6 +848,13 @@ rb_obj_is_instance_of(VALUE obj, VALUE c)
* b.kind_of? B #=> true
* b.kind_of? C #=> false
* b.kind_of? M #=> true
+ *--
+ * Determines if \a obj is a kind of \a c.
+ *
+ * Equivalent to \c Object\#kind_of? in Ruby.
+ * \param[in] obj the object to be determined
+ * \param[in] c a Module object.
+ *++
*/
VALUE
@@ -690,6 +877,7 @@ class_search_ancestor(VALUE cl, VALUE c)
return 0;
}
+/*! \private */
VALUE
rb_class_search_ancestor(VALUE cl, VALUE c)
{
@@ -698,19 +886,22 @@ rb_class_search_ancestor(VALUE cl, VALUE c)
return class_search_ancestor(cl, RCLASS_ORIGIN(c));
}
-/*
+/**
* call-seq:
- * obj.tap{|x|...} -> obj
+ * obj.tap {|x| block } -> obj
*
* Yields self to the block, and then returns self.
* The primary purpose of this method is to "tap into" a method chain,
* in order to perform operations on intermediate results within the chain.
*
- * (1..10) .tap {|x| puts "original: #{x.inspect}"}
- * .to_a .tap {|x| puts "array: #{x.inspect}"}
- * .select {|x| x%2==0} .tap {|x| puts "evens: #{x.inspect}"}
- * .map {|x| x*x} .tap {|x| puts "squares: #{x.inspect}"}
+ * (1..10) .tap {|x| puts "original: #{x}" }
+ * .to_a .tap {|x| puts "array: #{x}" }
+ * .select {|x| x.even? } .tap {|x| puts "evens: #{x}" }
+ * .map {|x| x*x } .tap {|x| puts "squares: #{x}" }
*
+ *--
+ * \private
+ *++
*/
VALUE
@@ -954,13 +1145,21 @@ rb_obj_dummy(void)
return Qnil;
}
-/*
+/**
* call-seq:
* obj.tainted? -> true or false
*
* Returns true if the object is tainted.
*
* See #taint for more information.
+ *--
+ * Determines if \a obj is tainted. Equivalent to \c Object\#tainted? in Ruby.
+ * \param[in] obj the object to be determined
+ * \retval Qtrue if the object is tainted
+ * \retval Qfalse if the object is not tainted
+ * \sa rb_obj_taint
+ * \sa rb_obj_untaint
+ *++
*/
VALUE
@@ -971,7 +1170,7 @@ rb_obj_tainted(VALUE obj)
return Qfalse;
}
-/*
+/**
* call-seq:
* obj.taint -> obj
*
@@ -986,6 +1185,13 @@ rb_obj_tainted(VALUE obj)
*
* You should only untaint a tainted object if your code has inspected it and
* determined that it is safe. To do so use #untaint.
+ *--
+ * Marks the object as tainted. Equivalent to \c Object\#taint in Ruby
+ * \param[in] obj the object to be tainted
+ * \return the object itself
+ * \sa rb_obj_untaint
+ * \sa rb_obj_tainted
+ *++
*/
VALUE
@@ -999,13 +1205,22 @@ rb_obj_taint(VALUE obj)
}
-/*
+/**
* call-seq:
* obj.untaint -> obj
*
* Removes the tainted mark from the object.
*
* See #taint for more information.
+ *--
+ * Removes the tainted mark from the object.
+ * Equivalent to \c Object\#untaint in Ruby.
+ *
+ * \param[in] obj the object to be tainted
+ * \return the object itself
+ * \sa rb_obj_taint
+ * \sa rb_obj_tainted
+ *++
*/
VALUE
@@ -1018,11 +1233,20 @@ rb_obj_untaint(VALUE obj)
return obj;
}
-/*
+/**
* call-seq:
* obj.untrusted? -> true or false
*
* Deprecated method that is equivalent to #tainted?.
+ *--
+ * \deprecated Use rb_obj_tainted.
+ *
+ * Trustiness used to have independent semantics from taintedness.
+ * But now trustiness of objects is obsolete and this function behaves
+ * the same as rb_obj_tainted.
+ *
+ * \sa rb_obj_tainted
+ *++
*/
VALUE
@@ -1032,11 +1256,20 @@ rb_obj_untrusted(VALUE obj)
return rb_obj_tainted(obj);
}
-/*
+/**
* call-seq:
* obj.untrust -> obj
*
* Deprecated method that is equivalent to #taint.
+ *--
+ * \deprecated Use rb_obj_taint(obj)
+ *
+ * Trustiness used to have independent semantics from taintedness.
+ * But now trustiness of objects is obsolete and this function behaves
+ * the same as rb_obj_taint.
+ *
+ * \sa rb_obj_taint
+ *++
*/
VALUE
@@ -1047,11 +1280,20 @@ rb_obj_untrust(VALUE obj)
}
-/*
+/**
* call-seq:
* obj.trust -> obj
*
* Deprecated method that is equivalent to #untaint.
+ *--
+ * \deprecated Use rb_obj_untaint(obj)
+ *
+ * Trustiness used to have independent semantics from taintedness.
+ * But now trustiness of objects is obsolete and this function behaves
+ * the same as rb_obj_untaint.
+ *
+ * \sa rb_obj_untaint
+ *++
*/
VALUE
@@ -1061,13 +1303,21 @@ rb_obj_trust(VALUE obj)
return rb_obj_untaint(obj);
}
+/**
+ * Convenient function to infect \a victim with the taintedness of \a carrier.
+ *
+ * It just keeps the taintedness of \a victim if \a carrier is not tainted.
+ * \param[in,out] victim the object being infected with the taintness of \a carrier
+ * \param[in] carrier a possibly tainted object
+ */
+
void
-rb_obj_infect(VALUE obj1, VALUE obj2)
+rb_obj_infect(VALUE victim, VALUE carrier)
{
- OBJ_INFECT(obj1, obj2);
+ OBJ_INFECT(victim, carrier);
}
-/*
+/**
* call-seq:
* obj.freeze -> obj
*
@@ -1084,11 +1334,16 @@ rb_obj_infect(VALUE obj1, VALUE obj2)
*
* <em>produces:</em>
*
- * prog.rb:3:in `<<': can't modify frozen Array (RuntimeError)
+ * prog.rb:3:in `<<': can't modify frozen Array (FrozenError)
* from prog.rb:3
*
* Objects of the following classes are always frozen: Integer,
* Float, Symbol.
+ *--
+ * Make the object unmodifiable. Equivalent to \c Object\#freeze in Ruby.
+ * \param[in,out] obj the object to be frozen
+ * \return the frozen object
+ *++
*/
VALUE
@@ -1103,7 +1358,7 @@ rb_obj_freeze(VALUE obj)
return obj;
}
-/*
+/**
* call-seq:
* obj.frozen? -> true or false
*
@@ -1112,6 +1367,12 @@ rb_obj_freeze(VALUE obj)
* a = [ "a", "b", "c" ]
* a.freeze #=> ["a", "b", "c"]
* a.frozen? #=> true
+ *--
+ * Determines if the object is frozen. Equivalent to \c Object\#frozen? in Ruby.
+ * \param[in] obj the object to be determines
+ * \retval Qtrue if frozen
+ * \retval Qfalse if not frozen
+ *++
*/
VALUE
@@ -1218,6 +1479,19 @@ nil_inspect(VALUE obj)
return rb_usascii_str_new2("nil");
}
+/*
+ * call-seq:
+ * nil =~ other -> nil
+ *
+ * Dummy pattern matching -- always returns nil.
+ */
+
+static VALUE
+nil_match(VALUE obj1, VALUE obj2)
+{
+ return Qnil;
+}
+
/***********************************************************************
* Document-class: TrueClass
*
@@ -1309,7 +1583,7 @@ true_xor(VALUE obj, VALUE obj2)
* call-seq:
* false.to_s -> "false"
*
- * 'nuf said...
+ * The string representation of <code>false</code> is "false".
*/
static VALUE
@@ -1404,14 +1678,17 @@ rb_false(VALUE obj)
* call-seq:
* obj =~ other -> nil
*
- * Pattern Match---Overridden by descendants (notably
- * <code>Regexp</code> and <code>String</code>) to provide meaningful
- * pattern-match semantics.
+ * This method is deprecated.
+ *
+ * This is not only unuseful but also troublesome because it
+ * may hide a type error.
*/
static VALUE
rb_obj_match(VALUE obj1, VALUE obj2)
{
+ rb_warning("deprecated Object#=~ is called on %"PRIsVALUE
+ "; it always returns nil", rb_obj_class(obj1));
return Qnil;
}
@@ -1561,7 +1838,7 @@ rb_mod_eqq(VALUE mod, VALUE arg)
return rb_obj_is_kind_of(arg, mod);
}
-/*
+/**
* call-seq:
* mod <= other -> true, false, or nil
*
@@ -1570,7 +1847,15 @@ rb_mod_eqq(VALUE mod, VALUE arg)
* <code>nil</code> if there's no relationship between the two.
* (Think of the relationship in terms of the class definition:
* "class A < B" implies "A < B".)
- *
+ *--
+ * Determines if \a mod inherits \a arg. Equivalent to \c Module\#<= in Ruby
+ *
+ * \param[in] mod a Module object
+ * \param[in] arg another Module object or an iclass of a module
+ * \retval Qtrue if \a mod inherits \a arg, or \a mod equals \a arg
+ * \retval Qfalse if \a arg inherits \a mod
+ * \retval Qnil if otherwise
+ *++
*/
VALUE
@@ -1778,11 +2063,11 @@ rb_class_initialize(int argc, VALUE *argv, VALUE klass)
if (RCLASS_SUPER(klass) != 0 || klass == rb_cBasicObject) {
rb_raise(rb_eTypeError, "already initialized class");
}
- if (argc == 0) {
+ if (rb_check_arity(argc, 0, 1) == 0) {
super = rb_cObject;
}
else {
- rb_scan_args(argc, argv, "01", &super);
+ super = argv[0];
rb_check_inheritable(super);
if (super != rb_cBasicObject && !RCLASS_SUPER(super)) {
rb_raise(rb_eTypeError, "can't inherit uninitialized class");
@@ -1796,6 +2081,7 @@ rb_class_initialize(int argc, VALUE *argv, VALUE klass)
return klass;
}
+/*! \private */
void
rb_undefined_alloc(VALUE klass)
{
@@ -1803,6 +2089,9 @@ rb_undefined_alloc(VALUE klass)
klass);
}
+static rb_alloc_func_t class_get_alloc_func(VALUE klass);
+static VALUE class_call_alloc_func(rb_alloc_func_t allocator, VALUE klass);
+
/*
* call-seq:
* class.allocate() -> obj
@@ -1825,10 +2114,27 @@ rb_undefined_alloc(VALUE klass)
*
*/
-VALUE
-rb_obj_alloc(VALUE klass)
+static VALUE
+rb_class_alloc_m(VALUE klass)
+{
+ rb_alloc_func_t allocator = class_get_alloc_func(klass);
+ if (!rb_obj_respond_to(klass, rb_intern("allocate"), 1)) {
+ rb_raise(rb_eTypeError, "calling %"PRIsVALUE".allocate is prohibited",
+ klass);
+ }
+ return class_call_alloc_func(allocator, klass);
+}
+
+static VALUE
+rb_class_alloc(VALUE klass)
+{
+ rb_alloc_func_t allocator = class_get_alloc_func(klass);
+ return class_call_alloc_func(allocator, klass);
+}
+
+static rb_alloc_func_t
+class_get_alloc_func(VALUE klass)
{
- VALUE obj;
rb_alloc_func_t allocator;
if (RCLASS_SUPER(klass) == 0 && klass != rb_cBasicObject) {
@@ -1841,6 +2147,13 @@ rb_obj_alloc(VALUE klass)
if (!allocator) {
rb_undefined_alloc(klass);
}
+ return allocator;
+}
+
+static VALUE
+class_call_alloc_func(rb_alloc_func_t allocator, VALUE klass)
+{
+ VALUE obj;
RUBY_DTRACE_CREATE_HOOK(OBJECT, rb_class2name(klass));
@@ -1852,6 +2165,27 @@ rb_obj_alloc(VALUE klass)
return obj;
}
+/**
+ * Allocates an instance of \a klass
+ *
+ * \note It calls the allocator defined by {rb_define_alloc_func}.
+ * So you cannot use this function to define an allocator.
+ * Use {rb_newobj_of}, {TypedData_Make_Struct} or others, instead.
+ * \note Usually prefer rb_class_new_instance to rb_obj_alloc and rb_obj_call_init
+ * \param[in] klass a Class object
+ * \sa rb_class_new_instance
+ * \sa rb_obj_call_init
+ * \sa rb_define_alloc_func
+ * \sa rb_newobj_of
+ * \sa TypedData_Make_Struct
+ */
+VALUE
+rb_obj_alloc(VALUE klass)
+{
+ Check_Type(klass, T_CLASS);
+ return rb_class_alloc(klass);
+}
+
static VALUE
rb_class_allocate_instance(VALUE klass)
{
@@ -1871,18 +2205,37 @@ rb_class_allocate_instance(VALUE klass)
*
*/
-VALUE
-rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
+static VALUE
+rb_class_s_new(int argc, const VALUE *argv, VALUE klass)
{
VALUE obj;
- obj = rb_obj_alloc(klass);
+ obj = rb_class_alloc(klass);
rb_obj_call_init(obj, argc, argv);
return obj;
}
-/*
+/**
+ * Allocates and initializes an instance of \a klass.
+ *
+ * Equivalent to \c Class\#new in Ruby
+ *
+ * \param[in] argc the number of arguments to \c #initialize
+ * \param[in] argv a pointer to an array of arguments to \c #initialize
+ * \param[in] klass a Class object
+ * \return the new instance of \a klass
+ * \sa rb_obj_call_init
+ * \sa rb_obj_alloc
+ */
+VALUE
+rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
+{
+ Check_Type(klass, T_CLASS);
+ return rb_class_s_new(argc, argv, klass);
+}
+
+/**
* call-seq:
* class.superclass -> a_super_class or nil
*
@@ -1899,6 +2252,14 @@ rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
*
* BasicObject.superclass #=> nil
*
+ *--
+ * Returns the superclass of \a klass. Equivalent to \c Class\#superclass in Ruby.
+ *
+ * It skips modules.
+ * \param[in] klass a Class object
+ * \return the superclass, or \c Qnil if \a klass does not have a parent class.
+ * \sa rb_class_get_superclass
+ *++
*/
VALUE
@@ -1919,14 +2280,23 @@ rb_class_superclass(VALUE klass)
return super;
}
+/**
+ * Returns the superclass of \a klass
+ * The return value might be an iclass of a module, unlike rb_class_superclass.
+ *
+ * Also it returns Qfalse when \a klass does not have a parent class.
+ * \sa rb_class_superclass
+ */
VALUE
rb_class_get_superclass(VALUE klass)
{
return RCLASS(klass)->super;
}
+/*! \private */
#define id_for_var(obj, name, part, type) \
id_for_setter(obj, name, type, "`%1$s' is not allowed as "#part" "#type" variable name")
+/*! \private */
#define id_for_setter(obj, name, type, message) \
check_setter_id(obj, &(name), rb_is_##type##_id, rb_is_##type##_name, message, strlen(message))
static ID
@@ -1991,6 +2361,20 @@ rb_mod_attr_reader(int argc, VALUE *argv, VALUE klass)
return Qnil;
}
+/**
+ * call-seq:
+ * attr(name, ...) -> nil
+ * attr(name, true) -> nil
+ * attr(name, false) -> nil
+ *
+ * The first form is equivalent to <code>attr_reader</code>.
+ * The second form is equivalent to <code>attr_accessor(name)</code> but deprecated.
+ * The last form is equivalent to <code>attr_reader(name)</code> but deprecated.
+ *--
+ * \private
+ * \todo can be static?
+ *++
+ */
VALUE
rb_mod_attr(int argc, VALUE *argv, VALUE klass)
{
@@ -2156,7 +2540,7 @@ rb_mod_const_get(int argc, VALUE *argv, VALUE mod)
if (!id) {
part = rb_str_subseq(name, beglen, len);
OBJ_FREEZE(part);
- if (!ISUPPER(*pbeg) || !rb_is_const_name(part)) {
+ if (!rb_is_const_name(part)) {
name = part;
goto wrong_name;
}
@@ -2173,7 +2557,19 @@ rb_mod_const_get(int argc, VALUE *argv, VALUE mod)
name = ID2SYM(id);
goto wrong_name;
}
- mod = RTEST(recur) ? rb_const_get(mod, id) : rb_const_get_at(mod, id);
+#if 0
+ mod = rb_const_get_0(mod, id, beglen > 0 || !RTEST(recur), RTEST(recur), FALSE);
+#else
+ if (!RTEST(recur)) {
+ mod = rb_const_get_at(mod, id);
+ }
+ else if (beglen == 0) {
+ mod = rb_const_get(mod, id);
+ }
+ else {
+ mod = rb_const_get_from(mod, id);
+ }
+#endif
}
return mod;
@@ -2309,7 +2705,7 @@ rb_mod_const_defined(int argc, VALUE *argv, VALUE mod)
if (!id) {
part = rb_str_subseq(name, beglen, len);
OBJ_FREEZE(part);
- if (!ISUPPER(*pbeg) || !rb_is_const_name(part)) {
+ if (!rb_is_const_name(part)) {
name = part;
goto wrong_name;
}
@@ -2321,17 +2717,30 @@ rb_mod_const_defined(int argc, VALUE *argv, VALUE mod)
name = ID2SYM(id);
goto wrong_name;
}
- if (RTEST(recur)) {
- if (!rb_const_defined(mod, id))
- return Qfalse;
- mod = rb_const_get(mod, id);
- }
- else {
+
+#if 0
+ mod = rb_const_search(mod, id, beglen > 0 || !RTEST(recur), RTEST(recur), FALSE);
+ if (mod == Qundef) return Qfalse;
+#else
+ if (!RTEST(recur)) {
if (!rb_const_defined_at(mod, id))
return Qfalse;
+ if (p == pend) return Qtrue;
mod = rb_const_get_at(mod, id);
}
- recur = Qfalse;
+ else if (beglen == 0) {
+ if (!rb_const_defined(mod, id))
+ return Qfalse;
+ if (p == pend) return Qtrue;
+ mod = rb_const_get(mod, id);
+ }
+ else {
+ if (!rb_const_defined_from(mod, id))
+ return Qfalse;
+ if (p == pend) return Qtrue;
+ mod = rb_const_get_from(mod, id);
+ }
+#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",
@@ -2542,6 +2951,7 @@ rb_mod_singleton_p(VALUE klass)
return Qfalse;
}
+/*! \private */
static const struct conv_method_tbl {
const char method[6];
unsigned short id;
@@ -2557,33 +2967,38 @@ static const struct conv_method_tbl {
M(a),
M(s),
M(i),
+ M(r),
#undef M
};
#define IMPLICIT_CONVERSIONS 7
-static VALUE
-convert_type(VALUE val, const char *tname, const char *method, int raise)
+static int
+conv_method_index(const char *method)
{
- ID m = 0;
- int i = numberof(conv_method_names);
- VALUE r;
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) {
- m = conv_method_names[i].id;
- break;
+ return i;
}
}
}
- if (!m) m = rb_intern(method);
- r = rb_check_funcall(val, m, 0, 0);
+ return numberof(conv_method_names);
+}
+
+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 = i < IMPLICIT_CONVERSIONS ?
+ 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" :
@@ -2600,6 +3015,16 @@ convert_type(VALUE val, const char *tname, const char *method, int raise)
return r;
}
+static VALUE
+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);
+ return convert_type_with_id(val, tname, m, raise, i);
+}
+
+/*! \private */
NORETURN(static void conversion_mismatch(VALUE, const char *, const char *, VALUE));
static void
conversion_mismatch(VALUE val, const char *tname, const char *method, VALUE result)
@@ -2610,6 +3035,19 @@ conversion_mismatch(VALUE val, const char *tname, const char *method, VALUE resu
cname, tname, cname, method, rb_obj_class(result));
}
+/*!
+ * Converts an object into another type.
+ * Calls the specified conversion method if necessary.
+ *
+ * \param[in] val the object to be converted
+ * \param[in] type a value of \c ruby_value_type
+ * \param[in] tname name of the target type.
+ * only used for error messages.
+ * \param[in] method name of the method
+ * \return an object of the specified type
+ * \throw TypeError on failure
+ * \sa rb_check_convert_type
+ */
VALUE
rb_convert_type(VALUE val, int type, const char *tname, const char *method)
{
@@ -2623,6 +3061,34 @@ rb_convert_type(VALUE val, int type, const char *tname, const char *method)
return v;
}
+/*! \private */
+VALUE
+rb_convert_type_with_id(VALUE val, int type, const char *tname, ID method)
+{
+ VALUE v;
+
+ 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);
+ }
+ return v;
+}
+
+/*!
+ * Tries to convert an object into another type.
+ * Calls the specified conversion method if necessary.
+ *
+ * \param[in] val the object to be converted
+ * \param[in] type a value of \c ruby_value_type
+ * \param[in] tname name of the target type.
+ * only used for error messages.
+ * \param[in] method name of the method
+ * \return an object of the specified type, or Qnil if no such conversion method defined.
+ * \throw TypeError if the conversion method returns an unexpected type of value.
+ * \sa rb_convert_type
+ * \sa rb_check_convert_type_with_id
+ */
VALUE
rb_check_convert_type(VALUE val, int type, const char *tname, const char *method)
{
@@ -2638,21 +3104,49 @@ rb_check_convert_type(VALUE val, int type, const char *tname, const char *method
return v;
}
+/*! \private */
+MJIT_FUNC_EXPORTED VALUE
+rb_check_convert_type_with_id(VALUE val, int type, const char *tname, ID method)
+{
+ VALUE v;
-static VALUE
-rb_to_integer(VALUE val, const char *method)
+ /* always convert T_DATA */
+ if (TYPE(val) == type && type != T_DATA) return val;
+ 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);
+ }
+ return v;
+}
+
+#define try_to_int(val, mid, raise) \
+ convert_type_with_id(val, "Integer", mid, raise, -1)
+
+ALWAYS_INLINE(static VALUE rb_to_integer(VALUE val, const char *method, ID mid));
+static inline VALUE
+rb_to_integer(VALUE val, const char *method, ID mid)
{
VALUE v;
- if (FIXNUM_P(val)) return val;
- if (RB_TYPE_P(val, T_BIGNUM)) return val;
- v = convert_type(val, "Integer", method, TRUE);
- if (!rb_obj_is_kind_of(v, rb_cInteger)) {
- conversion_mismatch(val, "Integer", method, v);
+ if (RB_INTEGER_TYPE_P(val)) return val;
+ v = try_to_int(val, mid, TRUE);
+ if (!RB_INTEGER_TYPE_P(v)) {
+ conversion_mismatch(val, "Integer", method, v);
}
return v;
}
+/**
+ * Tries to convert \a val into \c Integer.
+ * It calls the specified conversion method if necessary.
+ *
+ * \param[in] val a Ruby object
+ * \param[in] method a name of a method
+ * \return an \c Integer object on success,
+ * or \c Qnil if no such conversion method defined.
+ * \exception TypeError if the conversion method returns a non-Integer object.
+ */
VALUE
rb_check_to_integer(VALUE val, const char *method)
{
@@ -2661,70 +3155,126 @@ rb_check_to_integer(VALUE val, const char *method)
if (FIXNUM_P(val)) return val;
if (RB_TYPE_P(val, T_BIGNUM)) return val;
v = convert_type(val, "Integer", method, FALSE);
- if (!rb_obj_is_kind_of(v, rb_cInteger)) {
- return Qnil;
+ if (!RB_INTEGER_TYPE_P(v)) {
+ return Qnil;
}
return v;
}
+/**
+ * Converts \a val into \c Integer.
+ * It calls \a #to_int method if necessary.
+ *
+ * \param[in] val a Ruby object
+ * \return an \c Integer object
+ * \exception TypeError on failure
+ */
VALUE
rb_to_int(VALUE val)
{
- return rb_to_integer(val, "to_int");
+ return rb_to_integer(val, "to_int", idTo_int);
}
+/**
+ * Tries to convert \a val into Integer.
+ * It calls \c #to_int method if necessary.
+ *
+ * \param[in] val a Ruby object
+ * \return an Integer object on success,
+ * or \c Qnil if \c #to_int is not defined.
+ * \exception TypeError if \c #to_int returns a non-Integer object.
+ */
VALUE
rb_check_to_int(VALUE val)
{
- return rb_check_to_integer(val, "to_int");
+ if (RB_INTEGER_TYPE_P(val)) return val;
+ val = try_to_int(val, idTo_int, FALSE);
+ if (RB_INTEGER_TYPE_P(val)) return val;
+ return Qnil;
+}
+
+static VALUE
+rb_check_to_i(VALUE val)
+{
+ if (RB_INTEGER_TYPE_P(val)) return val;
+ val = try_to_int(val, idTo_i, FALSE);
+ if (RB_INTEGER_TYPE_P(val)) return val;
+ return Qnil;
}
static VALUE
-rb_convert_to_integer(VALUE val, int base)
+rb_convert_to_integer(VALUE val, int base, int raise_exception)
{
VALUE tmp;
if (RB_FLOAT_TYPE_P(val)) {
- double f;
- if (base != 0) goto arg_error;
- f = RFLOAT_VALUE(val);
- if (FIXABLE(f)) return LONG2FIX((long)f);
- return rb_dbl2big(f);
+ double f;
+ if (base != 0) goto arg_error;
+ f = RFLOAT_VALUE(val);
+ if (!raise_exception && !isfinite(f)) return Qnil;
+ if (FIXABLE(f)) return LONG2FIX((long)f);
+ return rb_dbl2big(f);
}
else if (RB_INTEGER_TYPE_P(val)) {
- if (base != 0) goto arg_error;
- return val;
+ if (base != 0) goto arg_error;
+ return val;
}
else if (RB_TYPE_P(val, T_STRING)) {
- return rb_str_to_inum(val, base, TRUE);
+ return rb_str_convert_to_inum(val, base, TRUE, raise_exception);
}
else if (NIL_P(val)) {
- if (base != 0) goto arg_error;
- rb_raise(rb_eTypeError, "can't convert nil into Integer");
+ if (base != 0) goto arg_error;
+ if (!raise_exception) return Qnil;
+ rb_raise(rb_eTypeError, "can't convert nil into Integer");
}
if (base != 0) {
- tmp = rb_check_string_type(val);
- if (!NIL_P(tmp)) return rb_str_to_inum(tmp, base, TRUE);
+ tmp = rb_check_string_type(val);
+ if (!NIL_P(tmp)) return rb_str_convert_to_inum(tmp, base, TRUE, raise_exception);
arg_error:
- rb_raise(rb_eArgError, "base specified for non string value");
+ if (!raise_exception) return Qnil;
+ rb_raise(rb_eArgError, "base specified for non string value");
}
- tmp = convert_type(val, "Integer", "to_int", FALSE);
- if (NIL_P(tmp)) {
- return rb_to_integer(val, "to_i");
+
+ tmp = rb_protect(rb_check_to_int, val, NULL);
+ if (RB_INTEGER_TYPE_P(tmp)) return tmp;
+ rb_set_errinfo(Qnil);
+
+ if (!raise_exception) {
+ VALUE result = rb_protect(rb_check_to_i, val, NULL);
+ rb_set_errinfo(Qnil);
+ return result;
}
- return tmp;
+ return rb_to_integer(val, "to_i", idTo_i);
}
+/**
+ * Equivalent to \c Kernel\#Integer in Ruby.
+ *
+ * Converts \a val into \c Integer in a slightly more strict manner
+ * than \c #to_i.
+ */
VALUE
rb_Integer(VALUE val)
{
- return rb_convert_to_integer(val, 0);
+ return rb_convert_to_integer(val, 0, TRUE);
+}
+
+static int
+opts_exception_p(VALUE opts)
+{
+ static ID kwds[1];
+ VALUE exception;
+ if (!kwds[0]) {
+ kwds[0] = idException;
+ }
+ rb_get_kwargs(opts, kwds, 0, 1, &exception);
+ return exception != Qfalse;
}
/*
* call-seq:
- * Integer(arg, base=0) -> integer
+ * Integer(arg, base=0, exception: true) -> integer
*
* Converts <i>arg</i> to an <code>Integer</code>.
* Numeric types are converted directly (with floating point numbers
@@ -2735,38 +3285,54 @@ rb_Integer(VALUE val)
* In any case, strings should be strictly conformed to numeric
* representation. This behavior is different from that of
* <code>String#to_i</code>. Non string values will be converted by first
- * trying <code>to_int</code>, then <code>to_i</code>. Passing <code>nil</code>
- * raises a TypeError.
+ * trying <code>to_int</code>, then <code>to_i</code>.
+ *
+ * Passing <code>nil</code> raises a TypeError, while passing a String that
+ * does not conform with numeric representation raises an ArgumentError.
+ * This behavior can be altered by passing <code>exception: false</code>,
+ * in this case a not convertible value will return <code>nil</code>.
*
* Integer(123.999) #=> 123
* Integer("0x1a") #=> 26
* Integer(Time.new) #=> 1204973019
* Integer("0930", 10) #=> 930
* Integer("111", 2) #=> 7
- * Integer(nil) #=> TypeError
+ * Integer(nil) #=> TypeError: can't convert nil into Integer
+ * Integer("x") #=> ArgumentError: invalid value for Integer(): "x"
+ *
+ * Integer("x", exception: false) #=> nil
+ *
*/
static VALUE
rb_f_integer(int argc, VALUE *argv, VALUE obj)
{
- VALUE arg = Qnil;
+ VALUE arg = Qnil, opts = Qnil;
int base = 0;
- switch (argc) {
- case 2:
- base = NUM2INT(argv[1]);
- case 1:
- arg = argv[0];
- break;
- default:
- /* should cause ArgumentError */
- rb_scan_args(argc, argv, "11", NULL, NULL);
+ if (argc > 1) {
+ int narg = 1;
+ VALUE vbase = rb_check_to_int(argv[1]);
+ if (!NIL_P(vbase)) {
+ base = NUM2INT(vbase);
+ narg = 2;
+ }
+ if (argc > narg) {
+ VALUE hash = rb_check_hash_type(argv[argc-1]);
+ if (!NIL_P(hash)) {
+ opts = rb_extract_keywords(&hash);
+ if (!hash) --argc;
+ }
+ }
}
- return rb_convert_to_integer(arg, base);
+ rb_check_arity(argc, 1, 2);
+ arg = argv[0];
+
+ return rb_convert_to_integer(arg, base, opts_exception_p(opts));
}
-double
-rb_cstr_to_dbl(const char *p, int badcheck)
+static double
+rb_cstr_to_dbl_raise(const char *p, int badcheck, int raise, int *error)
{
const char *q;
char *end;
@@ -2775,82 +3341,127 @@ rb_cstr_to_dbl(const char *p, int badcheck)
int w;
enum {max_width = 20};
#define OutOfRange() ((end - p > max_width) ? \
- (w = max_width, ellipsis = "...") : \
- (w = (int)(end - p), ellipsis = ""))
+ (w = max_width, ellipsis = "...") : \
+ (w = (int)(end - p), ellipsis = ""))
if (!p) return 0.0;
q = p;
while (ISSPACE(*p)) p++;
if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
- return 0.0;
+ return 0.0;
}
d = strtod(p, &end);
if (errno == ERANGE) {
- OutOfRange();
- rb_warning("Float %.*s%s out of range", w, p, ellipsis);
- errno = 0;
+ OutOfRange();
+ rb_warning("Float %.*s%s out of range", w, p, ellipsis);
+ errno = 0;
}
if (p == end) {
- if (badcheck) {
- bad:
- rb_invalid_str(q, "Float()");
- }
- return d;
+ if (badcheck) {
+ bad:
+ if (raise)
+ rb_invalid_str(q, "Float()");
+ else {
+ if (error) *error = 1;
+ return 0.0;
+ }
+ }
+ return d;
}
if (*end) {
- char buf[DBL_DIG * 4 + 10];
- char *n = buf;
- char *e = buf + sizeof(buf) - 1;
- char prev = 0;
-
- while (p < end && n < e) prev = *n++ = *p++;
- while (*p) {
- if (*p == '_') {
- /* remove underscores between digits */
- if (badcheck) {
- if (n == buf || !ISDIGIT(prev)) goto bad;
- ++p;
- if (!ISDIGIT(*p)) goto bad;
- }
- else {
- while (*++p == '_');
- continue;
- }
- }
- prev = *p++;
- if (n < e) *n++ = prev;
- }
- *n = '\0';
- p = buf;
-
- if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
- return 0.0;
- }
-
- d = strtod(p, &end);
- if (errno == ERANGE) {
- OutOfRange();
- rb_warning("Float %.*s%s out of range", w, p, ellipsis);
- errno = 0;
- }
- if (badcheck) {
- if (!end || p == end) goto bad;
- while (*end && ISSPACE(*end)) end++;
- if (*end) goto bad;
- }
+ char buf[DBL_DIG * 4 + 10];
+ char *n = buf;
+ char *const init_e = buf + DBL_DIG * 4;
+ char *e = init_e;
+ char prev = 0;
+ int dot_seen = FALSE;
+
+ switch (*p) {case '+': case '-': prev = *n++ = *p++;}
+ if (*p == '0') {
+ prev = *n++ = '0';
+ while (*++p == '0');
+ }
+ while (p < end && n < e) prev = *n++ = *p++;
+ while (*p) {
+ if (*p == '_') {
+ /* remove an underscore between digits */
+ if (n == buf || !ISDIGIT(prev) || (++p, !ISDIGIT(*p))) {
+ if (badcheck) goto bad;
+ break;
+ }
+ }
+ prev = *p++;
+ if (e == init_e && (prev == 'e' || prev == 'E' || prev == 'p' || prev == 'P')) {
+ e = buf + sizeof(buf) - 1;
+ *n++ = prev;
+ switch (*p) {case '+': case '-': prev = *n++ = *p++;}
+ if (*p == '0') {
+ prev = *n++ = '0';
+ while (*++p == '0');
+ }
+ continue;
+ }
+ else if (ISSPACE(prev)) {
+ while (ISSPACE(*p)) ++p;
+ if (*p) {
+ if (badcheck) goto bad;
+ break;
+ }
+ }
+ else if (prev == '.' ? dot_seen++ : !ISDIGIT(prev)) {
+ if (badcheck) goto bad;
+ break;
+ }
+ if (n < e) *n++ = prev;
+ }
+ *n = '\0';
+ p = buf;
+
+ if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
+ return 0.0;
+ }
+
+ d = strtod(p, &end);
+ if (errno == ERANGE) {
+ OutOfRange();
+ rb_warning("Float %.*s%s out of range", w, p, ellipsis);
+ errno = 0;
+ }
+ if (badcheck) {
+ if (!end || p == end) goto bad;
+ while (*end && ISSPACE(*end)) end++;
+ if (*end) goto bad;
+ }
}
if (errno == ERANGE) {
- errno = 0;
- OutOfRange();
- rb_raise(rb_eArgError, "Float %.*s%s out of range", w, q, ellipsis);
+ errno = 0;
+ OutOfRange();
+ rb_raise(rb_eArgError, "Float %.*s%s out of range", w, q, ellipsis);
}
return d;
}
+/*!
+ * Parses a string representation of a floating point number.
+ *
+ * \param[in] p a string representation of a floating number
+ * \param[in] badcheck raises an exception on parse error if \a badcheck is non-zero.
+ * \return the floating point number in the string on success,
+ * 0.0 on parse error and \a badcheck is zero.
+ * \note it always fails to parse a hexadecimal representation like "0xAB.CDp+1" when
+ * \a badcheck is zero, even though it would success if \a badcheck was non-zero.
+ * This inconsistency is coming from a historical compatibility reason. [ruby-dev:40822]
+ */
double
-rb_str_to_dbl(VALUE str, int badcheck)
+rb_cstr_to_dbl(const char *p, int badcheck)
+{
+ return rb_cstr_to_dbl_raise(p, badcheck, TRUE, NULL);
+}
+
+static double
+rb_str_to_dbl_raise(VALUE str, int badcheck, int raise, int *error)
{
char *s;
long len;
@@ -2862,38 +3473,72 @@ rb_str_to_dbl(VALUE str, int badcheck)
len = RSTRING_LEN(str);
if (s) {
if (badcheck && memchr(s, '\0', len)) {
- rb_raise(rb_eArgError, "string for Float contains null byte");
+ 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, len);
+ char *p = ALLOCV(v, (size_t)len + 1);
MEMCPY(p, s, char, len);
p[len] = '\0';
s = p;
}
}
- ret = rb_cstr_to_dbl(s, badcheck);
+ ret = rb_cstr_to_dbl_raise(s, badcheck, raise, error);
if (v)
ALLOCV_END(v);
return ret;
}
+FUNC_MINIMIZED(double rb_str_to_dbl(VALUE str, int badcheck));
+
+/*!
+ * Parses a string representation of a floating point number.
+ *
+ * \param[in] str a \c String object representation of a floating number
+ * \param[in] badcheck raises an exception on parse error if \a badcheck is non-zero.
+ * \return the floating point number in the string on success,
+ * 0.0 on parse error and \a badcheck is zero.
+ * \note it always fails to parse a hexadecimal representation like "0xAB.CDp+1" when
+ * \a badcheck is zero, even though it would success if \a badcheck was non-zero.
+ * This inconsistency is coming from a historical compatibility reason. [ruby-dev:40822]
+ */
+double
+rb_str_to_dbl(VALUE str, int badcheck)
+{
+ return rb_str_to_dbl_raise(str, badcheck, TRUE, NULL);
+}
+
+/*! \cond INTERNAL_MACRO */
#define fix2dbl_without_to_f(x) (double)FIX2LONG(x)
#define big2dbl_without_to_f(x) rb_big2dbl(x)
#define int2dbl_without_to_f(x) \
(FIXNUM_P(x) ? fix2dbl_without_to_f(x) : big2dbl_without_to_f(x))
-#define rat2dbl_without_to_f(x) \
- (int2dbl_without_to_f(rb_rational_num(x)) / \
- int2dbl_without_to_f(rb_rational_den(x)))
+#define num2dbl_without_to_f(x) \
+ (FIXNUM_P(x) ? fix2dbl_without_to_f(x) : \
+ RB_TYPE_P(x, T_BIGNUM) ? big2dbl_without_to_f(x) : \
+ (Check_Type(x, T_FLOAT), RFLOAT_VALUE(x)))
+static inline double
+rat2dbl_without_to_f(VALUE x)
+{
+ VALUE num = rb_rational_num(x);
+ VALUE den = rb_rational_den(x);
+ return num2dbl_without_to_f(num) / num2dbl_without_to_f(den);
+}
#define special_const_to_float(val, pre, post) \
switch (val) { \
case Qnil: \
- rb_raise(rb_eTypeError, pre "nil" post); \
+ rb_raise_static(rb_eTypeError, pre "nil" post); \
case Qtrue: \
- rb_raise(rb_eTypeError, pre "true" post); \
+ rb_raise_static(rb_eTypeError, pre "true" post); \
case Qfalse: \
- rb_raise(rb_eTypeError, pre "false" post); \
+ rb_raise_static(rb_eTypeError, pre "false" post); \
}
+/*! \endcond */
static inline void
conversion_to_float(VALUE val)
@@ -2908,7 +3553,7 @@ implicit_conversion_to_float(VALUE val)
}
static int
-to_float(VALUE *valp)
+to_float(VALUE *valp, int raise_exception)
{
VALUE val = *valp;
if (SPECIAL_CONST_P(val)) {
@@ -2919,7 +3564,7 @@ to_float(VALUE *valp)
else if (FLONUM_P(val)) {
return T_FLOAT;
}
- else {
+ else if (raise_exception) {
conversion_to_float(val);
}
}
@@ -2941,39 +3586,78 @@ to_float(VALUE *valp)
return T_NONE;
}
-VALUE
-rb_Float(VALUE val)
+static VALUE
+convert_type_to_float_protected(VALUE val)
+{
+ return rb_convert_type_with_id(val, T_FLOAT, "Float", id_to_f);
+}
+
+static VALUE
+rb_convert_to_float(VALUE val, int raise_exception)
{
- switch (to_float(&val)) {
+ switch (to_float(&val, raise_exception)) {
case T_FLOAT:
return val;
case T_STRING:
- return DBL2NUM(rb_str_to_dbl(val, TRUE));
+ if (!raise_exception) {
+ int e = 0;
+ double x = rb_str_to_dbl_raise(val, TRUE, raise_exception, &e);
+ return e ? Qnil : DBL2NUM(x);
+ }
+ return DBL2NUM(rb_str_to_dbl(val, TRUE));
+ case T_NONE:
+ if (SPECIAL_CONST_P(val) && !raise_exception)
+ return Qnil;
+ }
+
+ if (!raise_exception) {
+ int state;
+ VALUE result = rb_protect(convert_type_to_float_protected, val, &state);
+ if (state) rb_set_errinfo(Qnil);
+ return result;
}
- return rb_convert_type(val, T_FLOAT, "Float", "to_f");
+
+ return rb_convert_type_with_id(val, T_FLOAT, "Float", id_to_f);
}
-FUNC_MINIMIZED(static VALUE rb_f_float(VALUE obj, VALUE arg));
+FUNC_MINIMIZED(VALUE rb_Float(VALUE val));
+
+/*!
+ * Equivalent to \c Kernel\#Float in Ruby.
+ *
+ * Converts \a val into \c Float in a slightly more strict manner
+ * than \c #to_f.
+ */
+VALUE
+rb_Float(VALUE val)
+{
+ return rb_convert_to_float(val, TRUE);
+}
/*
* call-seq:
- * Float(arg) -> float
+ * Float(arg, exception: true) -> float
*
* Returns <i>arg</i> converted to a float. Numeric types are converted
* directly, and with exception to string and nil the rest are converted using <i>arg</i>.to_f.
* Converting a <code>string</code> with invalid characters will result in a <code>ArgumentError</code>.
* Converting <code>nil</code> generates a <code>TypeError</code>.
+ * Exceptions can be suppressed by passing <code>exception: false</code>.
*
* Float(1) #=> 1.0
* Float("123.456") #=> 123.456
* Float("123.0_badstring") #=> ArgumentError: invalid value for Float(): "123.0_badstring"
* Float(nil) #=> TypeError: can't convert nil into Float
+ * Float("123.0_badstring", exception: false) #=> nil
*/
static VALUE
-rb_f_float(VALUE obj, VALUE arg)
+rb_f_float(int argc, VALUE *argv, VALUE obj)
{
- return rb_Float(arg);
+ VALUE arg = Qnil, opts = Qnil;
+
+ rb_scan_args(argc, argv, "1:", &arg, &opts);
+ return rb_convert_to_float(arg, opts_exception_p(opts));
}
static VALUE
@@ -2983,19 +3667,31 @@ numeric_to_float(VALUE val)
rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into Float",
rb_obj_class(val));
}
- return rb_convert_type(val, T_FLOAT, "Float", "to_f");
+ return rb_convert_type_with_id(val, T_FLOAT, "Float", id_to_f);
}
+/*!
+ * Converts a \c Numeric object into \c Float.
+ * \param[in] val a \c Numeric object
+ * \exception TypeError if \a val is not a \c Numeric or other conversion failures.
+ */
VALUE
rb_to_float(VALUE val)
{
- switch (to_float(&val)) {
+ switch (to_float(&val, TRUE)) {
case T_FLOAT:
return val;
}
return numeric_to_float(val);
}
+/*!
+ * Tries to convert an object into \c Float.
+ * It calls \c #to_f if necessary.
+ *
+ * It returns \c Qnil if the object is not a \c Numeric
+ * or \c #to_f is not defined on the object.
+ */
VALUE
rb_check_to_float(VALUE val)
{
@@ -3003,17 +3699,16 @@ rb_check_to_float(VALUE val)
if (!rb_obj_is_kind_of(val, rb_cNumeric)) {
return Qnil;
}
- return rb_check_convert_type(val, T_FLOAT, "Float", "to_f");
+ return rb_check_convert_type_with_id(val, T_FLOAT, "Float", id_to_f);
}
-static ID id_to_f;
-
static inline int
basic_to_f_p(VALUE klass)
{
return rb_method_basic_definition_p(klass, id_to_f);
}
+/*! \private */
double
rb_num_to_dbl(VALUE val)
{
@@ -3047,6 +3742,13 @@ rb_num_to_dbl(VALUE val)
return RFLOAT_VALUE(val);
}
+/*!
+ * Converts a \c Numeric object to \c double.
+ * \param[in] val a \c Numeric object
+ * \return the converted value
+ * \exception TypeError if \a val is not a \c Numeric or
+ * it does not support conversion to a floating point number.
+ */
double
rb_num2dbl(VALUE val)
{
@@ -3073,16 +3775,22 @@ rb_num2dbl(VALUE val)
rb_raise(rb_eTypeError, "no implicit conversion to float from string");
}
}
- val = rb_convert_type(val, T_FLOAT, "Float", "to_f");
+ val = rb_convert_type_with_id(val, T_FLOAT, "Float", id_to_f);
return RFLOAT_VALUE(val);
}
+/*!
+ * Equivalent to \c Kernel\#String in Ruby.
+ *
+ * Converts \a val into \c String by trying \c #to_str at first and
+ * then trying \c #to_s.
+ */
VALUE
rb_String(VALUE val)
{
VALUE tmp = rb_check_string_type(val);
if (NIL_P(tmp))
- tmp = rb_convert_type(val, T_STRING, "String", "to_s");
+ tmp = rb_convert_type_with_id(val, T_STRING, "String", idTo_s);
return tmp;
}
@@ -3106,13 +3814,16 @@ rb_f_string(VALUE obj, VALUE arg)
return rb_String(arg);
}
+/*!
+ * Equivalent to \c Kernel\#Array in Ruby.
+ */
VALUE
rb_Array(VALUE val)
{
VALUE tmp = rb_check_array_type(val);
if (NIL_P(tmp)) {
- tmp = rb_check_convert_type(val, T_ARRAY, "Array", "to_a");
+ tmp = rb_check_to_array(val);
if (NIL_P(tmp)) {
return rb_ary_new3(1, val);
}
@@ -3127,8 +3838,17 @@ rb_Array(VALUE val)
* Returns +arg+ as an Array.
*
* First tries to call <code>to_ary</code> on +arg+, then <code>to_a</code>.
+ * If +arg+ does not respond to <code>to_ary</code> or <code>to_a</code>,
+ * returns an Array of length 1 containing +arg+.
*
- * Array(1..5) #=> [1, 2, 3, 4, 5]
+ * If <code>to_ary</code> or <code>to_a</code> returns something other than
+ * an Array, raises a <code>TypeError</code>.
+ *
+ * Array(["a", "b"]) #=> ["a", "b"]
+ * Array(1..5) #=> [1, 2, 3, 4, 5]
+ * Array(key: :value) #=> [[:key, :value]]
+ * Array(nil) #=> []
+ * Array(1) #=> [1]
*/
static VALUE
@@ -3137,6 +3857,9 @@ rb_f_array(VALUE obj, VALUE arg)
return rb_Array(arg);
}
+/**
+ * Equivalent to \c Kernel\#Hash in Ruby
+ */
VALUE
rb_Hash(VALUE val)
{
@@ -3172,6 +3895,7 @@ rb_f_hash(VALUE obj, VALUE arg)
return rb_Hash(arg);
}
+/*! \private */
struct dig_method {
VALUE klass;
int basic;
@@ -3199,6 +3923,7 @@ no_dig_method(int found, VALUE recv, ID mid, int argc, const VALUE *argv, VALUE
}
}
+/*! \private */
VALUE
rb_obj_dig(int argc, VALUE *argv, VALUE obj, VALUE notfound)
{
@@ -3297,24 +4022,6 @@ rb_obj_dig(int argc, VALUE *argv, VALUE obj, VALUE notfound)
*/
-/*!
- * Initializes the world of objects and classes.
- *
- * At first, the function bootstraps the class hierarchy.
- * It initializes the most fundamental classes and their metaclasses.
- * - \c BasicObject
- * - \c Object
- * - \c Module
- * - \c Class
- * After the bootstrap step, the class hierarchy becomes as the following
- * diagram.
- *
- * \image html boottime-classes.png
- *
- * Then, the function defines classes, modules and methods as usual.
- * \ingroup class
- */
-
/* Document-class: BasicObject
*
* BasicObject is the parent class of all classes in Ruby. It's an explicit
@@ -3387,6 +4094,27 @@ rb_obj_dig(int argc, VALUE *argv, VALUE obj, VALUE notfound)
* <code>:name</code>).
*/
+/*!
+ *--
+ * \private
+ * Initializes the world of objects and classes.
+ *
+ * At first, the function bootstraps the class hierarchy.
+ * It initializes the most fundamental classes and their metaclasses.
+ * - \c BasicObject
+ * - \c Object
+ * - \c Module
+ * - \c Class
+ * After the bootstrap step, the class hierarchy becomes as the following
+ * diagram.
+ *
+ * \image html boottime-classes.png
+ *
+ * Then, the function defines classes, modules and methods as usual.
+ * \ingroup class
+ *++
+ */
+
void
InitVM_Object(void)
{
@@ -3449,6 +4177,8 @@ InitVM_Object(void)
rb_define_method(rb_mKernel, "clone", rb_obj_clone2, -1);
rb_define_method(rb_mKernel, "dup", rb_obj_dup, 0);
rb_define_method(rb_mKernel, "itself", rb_obj_itself, 0);
+ rb_define_method(rb_mKernel, "yield_self", rb_obj_yield_self, 0);
+ rb_define_method(rb_mKernel, "then", rb_obj_yield_self, 0);
rb_define_method(rb_mKernel, "initialize_copy", rb_obj_init_copy, 1);
rb_define_method(rb_mKernel, "initialize_dup", rb_obj_init_dup_clone, 1);
rb_define_method(rb_mKernel, "initialize_clone", rb_obj_init_dup_clone, 1);
@@ -3485,7 +4215,7 @@ InitVM_Object(void)
rb_define_global_function("format", rb_f_sprintf, -1); /* in sprintf.c */
rb_define_global_function("Integer", rb_f_integer, -1);
- rb_define_global_function("Float", rb_f_float, 1);
+ rb_define_global_function("Float", rb_f_float, -1);
rb_define_global_function("String", rb_f_string, 1);
rb_define_global_function("Array", rb_f_array, 1);
@@ -3498,6 +4228,7 @@ InitVM_Object(void)
rb_define_method(rb_cNilClass, "to_a", nil_to_a, 0);
rb_define_method(rb_cNilClass, "to_h", nil_to_h, 0);
rb_define_method(rb_cNilClass, "inspect", nil_inspect, 0);
+ rb_define_method(rb_cNilClass, "=~", nil_match, 1);
rb_define_method(rb_cNilClass, "&", false_and, 1);
rb_define_method(rb_cNilClass, "|", false_or, 1);
rb_define_method(rb_cNilClass, "^", false_xor, 1);
@@ -3528,10 +4259,10 @@ InitVM_Object(void)
rb_define_method(rb_cModule, "name", rb_mod_name, 0); /* in variable.c */
rb_define_method(rb_cModule, "ancestors", rb_mod_ancestors, 0); /* in class.c */
- rb_define_private_method(rb_cModule, "attr", rb_mod_attr, -1);
- rb_define_private_method(rb_cModule, "attr_reader", rb_mod_attr_reader, -1);
- rb_define_private_method(rb_cModule, "attr_writer", rb_mod_attr_writer, -1);
- rb_define_private_method(rb_cModule, "attr_accessor", rb_mod_attr_accessor, -1);
+ rb_define_method(rb_cModule, "attr", rb_mod_attr, -1);
+ rb_define_method(rb_cModule, "attr_reader", rb_mod_attr_reader, -1);
+ rb_define_method(rb_cModule, "attr_writer", rb_mod_attr_writer, -1);
+ rb_define_method(rb_cModule, "attr_accessor", rb_mod_attr_accessor, -1);
rb_define_alloc_func(rb_cModule, rb_module_s_alloc);
rb_define_method(rb_cModule, "initialize", rb_mod_initialize, 0);
@@ -3564,8 +4295,8 @@ InitVM_Object(void)
rb_define_method(rb_cModule, "deprecate_constant", rb_mod_deprecate_constant, -1); /* in variable.c */
rb_define_method(rb_cModule, "singleton_class?", rb_mod_singleton_p, 0);
- rb_define_method(rb_cClass, "allocate", rb_obj_alloc, 0);
- rb_define_method(rb_cClass, "new", rb_class_new_instance, -1);
+ rb_define_method(rb_cClass, "allocate", rb_class_alloc_m, 0);
+ rb_define_method(rb_cClass, "new", rb_class_s_new, -1);
rb_define_method(rb_cClass, "initialize", rb_class_initialize, -1);
rb_define_method(rb_cClass, "superclass", rb_class_superclass, 0);
rb_define_alloc_func(rb_cClass, rb_class_s_alloc);
@@ -3576,11 +4307,12 @@ InitVM_Object(void)
/*
* Document-class: Data
*
- * This is a recommended base class for C extensions using Data_Make_Struct
- * or Data_Wrap_Struct, see doc/extension.rdoc for details.
+ * This is a deprecated class, base class for C extensions using
+ * Data_Make_Struct or Data_Wrap_Struct.
*/
rb_cData = rb_define_class("Data", rb_cObject);
rb_undef_alloc_func(rb_cData);
+ rb_deprecate_constant(rb_cObject, "Data");
rb_cTrueClass = rb_define_class("TrueClass", rb_cObject);
rb_define_method(rb_cTrueClass, "to_s", true_to_s, 0);
@@ -3616,7 +4348,10 @@ InitVM_Object(void)
void
Init_Object(void)
{
- id_to_f = rb_intern_const("to_f");
id_dig = rb_intern_const("dig");
InitVM(Object);
}
+
+/*!
+ * \}
+ */
diff --git a/pack.c b/pack.c
index 4e1198b635..b1efef54f1 100644
--- a/pack.c
+++ b/pack.c
@@ -9,10 +9,12 @@
**********************************************************************/
+#include "ruby/encoding.h"
#include "internal.h"
#include <sys/types.h>
#include <ctype.h>
#include <errno.h>
+#include <float.h>
/*
* It is intentional that the condition for natstr is HAVE_TRUE_LONG_LONG
@@ -41,20 +43,20 @@ static const char endstr[] = "sSiIlLqQjJ";
#endif
#ifdef DYNAMIC_ENDIAN
- /* for universal binary of NEXTSTEP and MacOS X */
- /* useless since autoconf 2.63? */
- static int
- is_bigendian(void)
- {
- static int init = 0;
- static int endian_value;
- char *p;
-
- if (init) return endian_value;
- init = 1;
- p = (char*)&init;
- return endian_value = p[0]?0:1;
- }
+/* for universal binary of NEXTSTEP and MacOS X */
+/* useless since autoconf 2.63? */
+static int
+is_bigendian(void)
+{
+ static int init = 0;
+ static int endian_value;
+ char *p;
+
+ if (init) return endian_value;
+ init = 1;
+ p = (char*)&init;
+ return endian_value = p[0]?0:1;
+}
# define BIGENDIAN_P() (is_bigendian())
#elif defined(WORDS_BIGENDIAN)
# define BIGENDIAN_P() 1
@@ -126,21 +128,51 @@ str_associated(VALUE str)
return rb_ivar_lookup(str, id_associated, Qfalse);
}
-void
-rb_str_associate(VALUE str, VALUE add)
+static void
+unknown_directive(const char *mode, char type, VALUE fmt)
{
- ONLY_FOR_INTERNAL_USE("rb_str_associate()");
+ VALUE f;
+ char unknown[5];
+
+ if (ISPRINT(type)) {
+ unknown[0] = type;
+ unknown[1] = '\0';
+ }
+ else {
+ snprintf(unknown, sizeof(unknown), "\\x%.2x", type & 0xff);
+ }
+ f = rb_str_quote_unprintable(fmt);
+ if (f != fmt) {
+ fmt = rb_str_subseq(f, 1, RSTRING_LEN(f) - 2);
+ }
+ rb_warning("unknown %s directive '%s' in '%"PRIsVALUE"'",
+ mode, unknown, fmt);
}
-VALUE
-rb_str_associated(VALUE str)
+static float
+VALUE_to_float(VALUE obj)
{
- ONLY_FOR_INTERNAL_USE("rb_str_associated()");
+ VALUE v = rb_to_float(obj);
+ double d = RFLOAT_VALUE(v);
+
+ if (isnan(d)) {
+ return NAN;
+ }
+ else if (d < -FLT_MAX) {
+ return -INFINITY;
+ }
+ else if (d <= FLT_MAX) {
+ return d;
+ }
+ else {
+ return INFINITY;
+ }
}
/*
* call-seq:
- * arr.pack ( aTemplateString ) -> aBinaryString
+ * arr.pack( aTemplateString ) -> aBinaryString
+ * arr.pack( aTemplateString, buffer: aBufferString ) -> aBufferString
*
* Packs the contents of <i>arr</i> into a binary sequence according to
* the directives in <i>aTemplateString</i> (see the table below)
@@ -162,6 +194,18 @@ rb_str_associated(VALUE str)
* 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>''.
+ *
+ * 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 |
@@ -240,7 +284,8 @@ rb_str_associated(VALUE str)
* 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 | 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, count is width)
* | | (if count is 0, no line feed are added, see RFC 4648)
* P | String | pointer to a structure (fixed-length string)
@@ -255,12 +300,12 @@ rb_str_associated(VALUE str)
*/
static VALUE
-pack_pack(VALUE ary, VALUE fmt)
+pack_pack(int argc, VALUE *argv, VALUE ary)
{
static const char nul10[] = "\0\0\0\0\0\0\0\0\0\0";
static const char spc10[] = " ";
const char *p, *pend;
- VALUE res, from, associates = 0;
+ VALUE fmt, opt = Qnil, res, from, associates = 0, buffer = 0;
char type;
long len, idx, plen;
const char *ptr;
@@ -270,10 +315,25 @@ pack_pack(VALUE ary, VALUE fmt)
#endif
int integer_size, bigendian_p;
+ rb_scan_args(argc, argv, "10:", &fmt, &opt);
+
StringValue(fmt);
p = RSTRING_PTR(fmt);
pend = p + RSTRING_LEN(fmt);
- res = rb_str_buf_new(0);
+ if (!NIL_P(opt)) {
+ static ID keyword_ids[1];
+ if (!keyword_ids[0])
+ CONST_ID(keyword_ids[0], "buffer");
+
+ rb_get_kwargs(opt, keyword_ids, 0, 1, &buffer);
+
+ if (buffer != Qundef && !RB_TYPE_P(buffer, T_STRING))
+ rb_raise(rb_eTypeError, "buffer must be String, not %s", rb_obj_classname(buffer));
+ }
+ if (buffer)
+ res = buffer;
+ else
+ res = rb_str_buf_new(0);
idx = 0;
@@ -624,7 +684,7 @@ pack_pack(VALUE ary, VALUE fmt)
float f;
from = NEXTFROM;
- f = (float)RFLOAT_VALUE(rb_to_float(from));
+ f = VALUE_to_float(from);
rb_str_buf_cat(res, (char*)&f, sizeof(float));
}
break;
@@ -634,7 +694,7 @@ pack_pack(VALUE ary, VALUE fmt)
FLOAT_CONVWITH(tmp);
from = NEXTFROM;
- tmp.f = (float)RFLOAT_VALUE(rb_to_float(from));
+ tmp.f = VALUE_to_float(from);
HTOVF(tmp);
rb_str_buf_cat(res, tmp.buf, sizeof(float));
}
@@ -665,7 +725,7 @@ pack_pack(VALUE ary, VALUE fmt)
while (len-- > 0) {
FLOAT_CONVWITH(tmp);
from = NEXTFROM;
- tmp.f = (float)RFLOAT_VALUE(rb_to_float(from));
+ tmp.f = VALUE_to_float(from);
HTONF(tmp);
rb_str_buf_cat(res, tmp.buf, sizeof(float));
}
@@ -733,6 +793,7 @@ pack_pack(VALUE ary, VALUE fmt)
StringValue(from);
ptr = RSTRING_PTR(from);
plen = RSTRING_LEN(from);
+ OBJ_INFECT(res, from);
if (len == 0 && type == 'm') {
encodes(res, ptr, plen, type, 0);
@@ -760,6 +821,7 @@ pack_pack(VALUE ary, VALUE fmt)
case 'M': /* quoted-printable encoded string */
from = rb_obj_as_string(NEXTFROM);
+ OBJ_INFECT(res, from);
if (len <= 1)
len = 72;
qpencode(res, from, len);
@@ -785,6 +847,7 @@ pack_pack(VALUE ary, VALUE fmt)
}
else {
t = StringValuePtr(from);
+ OBJ_INFECT(res, from);
rb_obj_taint(from);
}
if (!associates) {
@@ -818,9 +881,9 @@ pack_pack(VALUE ary, VALUE fmt)
cp = RSTRING_PTR(buf);
while (1 < numbytes) {
- *cp |= 0x80;
- cp++;
- numbytes--;
+ *cp |= 0x80;
+ cp++;
+ numbytes--;
}
rb_str_buf_cat(res, RSTRING_PTR(buf), RSTRING_LEN(buf));
@@ -828,16 +891,7 @@ pack_pack(VALUE ary, VALUE fmt)
break;
default: {
- char unknown[5];
- if (ISPRINT(type)) {
- unknown[0] = type;
- unknown[1] = '\0';
- }
- else {
- snprintf(unknown, sizeof(unknown), "\\x%.2x", type & 0xff);
- }
- rb_warning("unknown pack directive '%s' in '% "PRIsVALUE"'",
- unknown, fmt);
+ unknown_directive("pack", type, fmt);
break;
}
}
@@ -984,21 +1038,22 @@ 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); \
} \
} while (0)
#define PACK_ITEM_ADJUST() do { \
- if (tmp_len > 0 && !block_p) \
+ if (tmp_len > 0 && mode == UNPACK_ARRAY) \
rb_ary_store(ary, RARRAY_LEN(ary)+tmp_len-1, Qnil); \
} while (0)
-/* Workaround for Oracle Solaris Studio 12.4 C compiler optimization bug
+/* Workaround for Oracle Developer Studio (Oracle Solaris Studio)
+ * 12.4/12.5/12.6 C compiler optimization bug
* with "-xO4" optimization option.
*/
-#if defined(__SUNPRO_C) && __SUNPRO_C == 0x5130
+#if defined(__SUNPRO_C) && 0x5130 <= __SUNPRO_C && __SUNPRO_C <= 0x5150
# define AVOID_CC_BUG volatile
#else
# define AVOID_CC_BUG
@@ -1013,128 +1068,13 @@ infected_str_new(const char *ptr, long len, VALUE str)
return s;
}
-/*
- * call-seq:
- * str.unpack(format) -> 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 <code>Array#pack</code>.
- *
- * "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.
- *
- * 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)
- * | | (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 same as "n"
- * J> j> J!> j!> | | "L>" is 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 same as "v"
- * J< j< J!< j!< | | "L<" is 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
- */
+/* unpack mode */
+#define UNPACK_ARRAY 0
+#define UNPACK_BLOCK 1
+#define UNPACK_1 2
static VALUE
-pack_unpack(VALUE str, VALUE fmt)
+pack_unpack_internal(VALUE str, VALUE fmt, int mode)
{
#define hexdigits ruby_hexdigits
char *s, *send;
@@ -1147,16 +1087,18 @@ pack_unpack(VALUE str, VALUE fmt)
#ifdef NATINT_PACK
int natint; /* native integer */
#endif
- int block_p = rb_block_given_p();
int signed_p, integer_size, bigendian_p;
#define UNPACK_PUSH(item) do {\
VALUE item_val = (item);\
- if (block_p) {\
+ if ((mode) == UNPACK_BLOCK) {\
rb_yield(item_val);\
}\
- else {\
+ else if ((mode) == UNPACK_ARRAY) {\
rb_ary_push(ary, item_val);\
}\
+ else /* if ((mode) == UNPACK_1) { */ {\
+ return item_val; \
+ }\
} while (0)
StringValue(str);
@@ -1166,7 +1108,7 @@ pack_unpack(VALUE str, VALUE fmt)
p = RSTRING_PTR(fmt);
pend = p + RSTRING_LEN(fmt);
- ary = block_p ? Qnil : rb_ary_new();
+ ary = mode == UNPACK_ARRAY ? rb_ary_new() : Qnil;
while (p < pend) {
int explicit_endian = 0;
type = *p++;
@@ -1223,7 +1165,7 @@ pack_unpack(VALUE str, VALUE fmt)
else if (ISDIGIT(*p)) {
errno = 0;
len = STRTOUL(p, (char**)&p, 10);
- if (errno) {
+ if (len < 0 || errno) {
rb_raise(rb_eRangeError, "pack length too big");
}
}
@@ -1279,13 +1221,15 @@ pack_unpack(VALUE str, VALUE fmt)
if (p[-1] == '*' || len > (send - s) * 8)
len = (send - s) * 8;
bits = 0;
- UNPACK_PUSH(bitstr = rb_usascii_str_new(0, len));
+ bitstr = rb_usascii_str_new(0, len);
+ OBJ_INFECT(bitstr, str);
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;
@@ -1299,13 +1243,15 @@ pack_unpack(VALUE str, VALUE fmt)
if (p[-1] == '*' || len > (send - s) * 8)
len = (send - s) * 8;
bits = 0;
- UNPACK_PUSH(bitstr = rb_usascii_str_new(0, len));
+ bitstr = rb_usascii_str_new(0, len);
+ OBJ_INFECT(bitstr, str);
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;
@@ -1319,7 +1265,8 @@ pack_unpack(VALUE str, VALUE fmt)
if (p[-1] == '*' || len > (send - s) * 2)
len = (send - s) * 2;
bits = 0;
- UNPACK_PUSH(bitstr = rb_usascii_str_new(0, len));
+ bitstr = rb_usascii_str_new(0, len);
+ OBJ_INFECT(bitstr, str);
t = RSTRING_PTR(bitstr);
for (i=0; i<len; i++) {
if (i & 1)
@@ -1328,6 +1275,7 @@ pack_unpack(VALUE str, VALUE fmt)
bits = (unsigned char)*s++;
*t++ = hexdigits[bits & 15];
}
+ UNPACK_PUSH(bitstr);
}
break;
@@ -1341,7 +1289,8 @@ pack_unpack(VALUE str, VALUE fmt)
if (p[-1] == '*' || len > (send - s) * 2)
len = (send - s) * 2;
bits = 0;
- UNPACK_PUSH(bitstr = rb_usascii_str_new(0, len));
+ bitstr = rb_usascii_str_new(0, len);
+ OBJ_INFECT(bitstr, str);
t = RSTRING_PTR(bitstr);
for (i=0; i<len; i++) {
if (i & 1)
@@ -1350,6 +1299,7 @@ pack_unpack(VALUE str, VALUE fmt)
bits = (unsigned char)*s++;
*t++ = hexdigits[(bits >> 4) & 15];
}
+ UNPACK_PUSH(bitstr);
}
break;
@@ -1690,6 +1640,7 @@ pack_unpack(VALUE str, VALUE fmt)
{
VALUE buf = infected_str_new(0, send - s, str);
char *ptr = RSTRING_PTR(buf), *ss = s;
+ int csum = 0;
int c1, c2;
while (s < send) {
@@ -1701,18 +1652,19 @@ pack_unpack(VALUE str, VALUE fmt)
if ((c1 = hex2num(*s)) == -1) break;
if (++s == send) break;
if ((c2 = hex2num(*s)) == -1) break;
- *ptr++ = castchar(c1 << 4 | c2);
+ csum |= *ptr++ = castchar(c1 << 4 | c2);
}
}
else {
- *ptr++ = *s;
+ csum |= *ptr++ = *s;
}
s++;
ss = s;
}
rb_str_set_len(buf, ptr - RSTRING_PTR(buf));
rb_str_buf_cat(buf, ss, send-ss);
- ENCODING_CODERANGE_SET(buf, rb_ascii8bit_encindex(), ENC_CODERANGE_VALID);
+ csum = ISASCII(csum) ? ENC_CODERANGE_7BIT : ENC_CODERANGE_VALID;
+ ENCODING_CODERANGE_SET(buf, rb_ascii8bit_encindex(), csum);
UNPACK_PUSH(buf);
}
break;
@@ -1829,8 +1781,7 @@ pack_unpack(VALUE str, VALUE fmt)
break;
default:
- rb_warning("unknown unpack directive '%c' in '%s'",
- type, RSTRING_PTR(fmt));
+ unknown_directive("unpack", type, fmt);
break;
}
}
@@ -1838,6 +1789,148 @@ pack_unpack(VALUE str, VALUE fmt)
return ary;
}
+/*
+ * call-seq:
+ * str.unpack(format) -> 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 <code>String#unpack1</code>, <code>Array#pack</code>.
+ *
+ * "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.
+ *
+ * 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 same as "n"
+ * J> j> J!> j!> | | "L>" is 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 same as "v"
+ * J< j< J!< j!< | | "L<" is 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
+ *
+ * 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.
+ */
+
+static VALUE
+pack_unpack(VALUE str, VALUE fmt)
+{
+ int mode = rb_block_given_p() ? UNPACK_BLOCK : UNPACK_ARRAY;
+ return pack_unpack_internal(str, fmt, mode);
+}
+
+/*
+ * call-seq:
+ * str.unpack1(format) -> obj
+ *
+ * Decodes <i>str</i> (which may contain binary data) according to the
+ * format string, returning the first value extracted.
+ * See also <code>String#unpack</code>, <code>Array#pack</code>.
+ */
+
+static VALUE
+pack_unpack1(VALUE str, VALUE fmt)
+{
+ return pack_unpack_internal(str, fmt, UNPACK_1);
+}
+
int
rb_uv_to_utf8(char buf[6], unsigned long uv)
{
@@ -1882,7 +1975,7 @@ rb_uv_to_utf8(char buf[6], unsigned long uv)
}
rb_raise(rb_eRangeError, "pack(U): value out of range");
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
static const unsigned long utf8_limits[] = {
@@ -1948,8 +2041,9 @@ utf8_to_uv(const char *p, long *lenp)
void
Init_pack(void)
{
- rb_define_method(rb_cArray, "pack", pack_pack, 1);
+ rb_define_method(rb_cArray, "pack", pack_pack, -1);
rb_define_method(rb_cString, "unpack", pack_unpack, 1);
+ rb_define_method(rb_cString, "unpack1", pack_unpack1, 1);
id_associated = rb_make_internal_id();
}
diff --git a/parse.y b/parse.y
index f9acacc0fb..2b5a692558 100644
--- a/parse.y
+++ b/parse.y
@@ -14,12 +14,11 @@
#if !YYPURE
# error needs pure parser
#endif
-#ifndef PARSER_DEBUG
-#define PARSER_DEBUG 0
-#endif
#define YYDEBUG 1
#define YYERROR_VERBOSE 1
#define YYSTACK_USE_ALLOCA 0
+#define YYLTYPE rb_code_location_t
+#define YYLTYPE_IS_DECLARED 1
#include "ruby/ruby.h"
#include "ruby/st.h"
@@ -40,25 +39,38 @@
#define TAB_WIDTH 8
-#define YYMALLOC(size) rb_parser_malloc(parser, (size))
-#define YYREALLOC(ptr, size) rb_parser_realloc(parser, (ptr), (size))
-#define YYCALLOC(nelem, size) rb_parser_calloc(parser, (nelem), (size))
-#define YYFREE(ptr) rb_parser_free(parser, (ptr))
+#define yydebug (p->debug) /* disable the global variable definition */
+
+#define YYMALLOC(size) rb_parser_malloc(p, (size))
+#define YYREALLOC(ptr, size) rb_parser_realloc(p, (ptr), (size))
+#define YYCALLOC(nelem, size) rb_parser_calloc(p, (nelem), (size))
+#define YYFREE(ptr) rb_parser_free(p, (ptr))
#define YYFPRINTF rb_parser_printf
-#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
-# define YY_LOCATION_PRINT(File, Loc) \
- rb_parser_printf(parser, "%d.%d-%d.%d", \
- (Loc).first_line, (Loc).first_column, \
- (Loc).last_line, (Loc).last_column)
-#endif
-#undef malloc
-#undef realloc
-#undef calloc
-#undef free
-#define malloc YYMALLOC
-#define realloc YYREALLOC
-#define calloc YYCALLOC
-#define free YYFREE
+#define YYPRINT(out, tok, val) parser_token_value_print(p, (tok), &(val))
+#define YY_LOCATION_PRINT(File, loc) \
+ rb_parser_printf(p, "%d.%d-%d.%d", \
+ (loc).beg_pos.lineno, (loc).beg_pos.column,\
+ (loc).end_pos.lineno, (loc).end_pos.column)
+#define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (N) \
+ { \
+ (Current).beg_pos = YYRHSLOC(Rhs, 1).beg_pos; \
+ (Current).end_pos = YYRHSLOC(Rhs, N).end_pos; \
+ } \
+ else \
+ { \
+ (Current).beg_pos = YYRHSLOC(Rhs, 0).end_pos; \
+ (Current).end_pos = YYRHSLOC(Rhs, 0).end_pos; \
+ } \
+ while (0)
+
+#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) \
+ rb_parser_set_location_of_none(p, &(Current))
+#define RUBY_SET_YYLLOC(Current) \
+ rb_parser_set_location(p, &(Current))
enum lex_state_bits {
EXPR_BEG_bit, /* ignore newline, +/- is a sign. */
@@ -95,37 +107,41 @@ enum lex_state_e {
EXPR_VALUE = EXPR_BEG,
EXPR_BEG_ANY = (EXPR_BEG | EXPR_MID | EXPR_CLASS),
EXPR_ARG_ANY = (EXPR_ARG | EXPR_CMDARG),
- EXPR_END_ANY = (EXPR_END | EXPR_ENDARG | EXPR_ENDFN)
+ EXPR_END_ANY = (EXPR_END | EXPR_ENDARG | EXPR_ENDFN),
+ EXPR_NONE = 0
};
#define IS_lex_state_for(x, ls) ((x) & (ls))
#define IS_lex_state_all_for(x, ls) (((x) & (ls)) == (ls))
-#define IS_lex_state(ls) IS_lex_state_for(lex_state, (ls))
-#define IS_lex_state_all(ls) IS_lex_state_all_for(lex_state, (ls))
+#define IS_lex_state(ls) IS_lex_state_for(p->lex.state, (ls))
+#define IS_lex_state_all(ls) IS_lex_state_all_for(p->lex.state, (ls))
# define SET_LEX_STATE(ls) \
- (lex_state = (yydebug ? trace_lex_state(lex_state, (ls), __LINE__) : \
- (enum lex_state_e)(ls)))
-static enum lex_state_e trace_lex_state(enum lex_state_e from, enum lex_state_e to, int line);
+ (p->lex.state = \
+ (p->debug ? \
+ rb_parser_trace_lex_state(p, p->lex.state, (ls), __LINE__) : \
+ (enum lex_state_e)(ls)))
typedef VALUE stack_type;
-static void show_bitstack(stack_type, const char *, int);
-# define SHOW_BITSTACK(stack, name) (yydebug ? show_bitstack(stack, name, __LINE__) : (void)0)
-# define BITSTACK_PUSH(stack, n) (((stack) = ((stack)<<1)|((n)&1)), SHOW_BITSTACK(stack, #stack"(push)"))
-# define BITSTACK_POP(stack) (((stack) = (stack) >> 1), SHOW_BITSTACK(stack, #stack"(pop)"))
-# define BITSTACK_LEXPOP(stack) (((stack) = ((stack) >> 1) | ((stack) & 1)), SHOW_BITSTACK(stack, #stack"(lexpop)"))
-# define BITSTACK_SET_P(stack) (SHOW_BITSTACK(stack, #stack), (stack)&1)
-# define BITSTACK_SET(stack, n) ((stack)=(n), SHOW_BITSTACK(stack, #stack"(set)"))
+static const rb_code_location_t NULL_LOC = { {0, -1}, {0, -1} };
+# define SHOW_BITSTACK(stack, name) (p->debug ? rb_parser_show_bitstack(p, stack, name, __LINE__) : (void)0)
+# define BITSTACK_PUSH(stack, n) (((p->stack) = ((p->stack)<<1)|((n)&1)), SHOW_BITSTACK(p->stack, #stack"(push)"))
+# define BITSTACK_POP(stack) (((p->stack) = (p->stack) >> 1), SHOW_BITSTACK(p->stack, #stack"(pop)"))
+# define BITSTACK_SET_P(stack) (SHOW_BITSTACK(p->stack, #stack), (p->stack)&1)
+# define BITSTACK_SET(stack, n) ((p->stack)=(n), SHOW_BITSTACK(p->stack, #stack"(set)"))
+
+/* A flag to identify keyword_do_cond, "do" keyword after condition expression.
+ Examples: `while ... do`, `until ... do`, and `for ... in ... do` */
#define COND_PUSH(n) BITSTACK_PUSH(cond_stack, (n))
#define COND_POP() BITSTACK_POP(cond_stack)
-#define COND_LEXPOP() BITSTACK_LEXPOP(cond_stack)
#define COND_P() BITSTACK_SET_P(cond_stack)
#define COND_SET(n) BITSTACK_SET(cond_stack, (n))
+/* A flag to identify keyword_do_block; "do" keyword after command_call.
+ Example: `foo 1, 2 do`. */
#define CMDARG_PUSH(n) BITSTACK_PUSH(cmdarg_stack, (n))
#define CMDARG_POP() BITSTACK_POP(cmdarg_stack)
-#define CMDARG_LEXPOP() BITSTACK_LEXPOP(cmdarg_stack)
#define CMDARG_P() BITSTACK_SET_P(cmdarg_stack)
#define CMDARG_SET(n) BITSTACK_SET(cmdarg_stack, (n))
@@ -144,89 +160,11 @@ struct local_vars {
struct vtable *past;
# endif
struct local_vars *prev;
- stack_type cmdargs;
};
#define DVARS_INHERIT ((void*)1)
#define DVARS_TOPSCOPE NULL
-#define DVARS_SPECIAL_P(tbl) (!POINTER_P(tbl))
-#define POINTER_P(val) ((VALUE)(val) & ~(VALUE)3)
-
-static int
-vtable_size(const struct vtable *tbl)
-{
- if (POINTER_P(tbl)) {
- return tbl->pos;
- }
- else {
- return 0;
- }
-}
-
-#define VTBL_DEBUG 0
-
-static struct vtable *
-vtable_alloc(struct vtable *prev)
-{
- struct vtable *tbl = ALLOC(struct vtable);
- tbl->pos = 0;
- tbl->capa = 8;
- tbl->tbl = ALLOC_N(ID, tbl->capa);
- tbl->prev = prev;
- if (VTBL_DEBUG) printf("vtable_alloc: %p\n", (void *)tbl);
- return tbl;
-}
-
-static void
-vtable_free(struct vtable *tbl)
-{
- if (VTBL_DEBUG)printf("vtable_free: %p\n", (void *)tbl);
- if (POINTER_P(tbl)) {
- if (tbl->tbl) {
- xfree(tbl->tbl);
- }
- xfree(tbl);
- }
-}
-
-static void
-vtable_add(struct vtable *tbl, ID id)
-{
- if (!POINTER_P(tbl)) {
- rb_bug("vtable_add: vtable is not allocated (%p)", (void *)tbl);
- }
- if (VTBL_DEBUG) printf("vtable_add: %p, %"PRIsVALUE"\n", (void *)tbl, rb_id2str(id));
-
- if (tbl->pos == tbl->capa) {
- tbl->capa = tbl->capa * 2;
- REALLOC_N(tbl->tbl, ID, tbl->capa);
- }
- tbl->tbl[tbl->pos++] = id;
-}
-
-#ifndef RIPPER
-static void
-vtable_pop(struct vtable *tbl, int n)
-{
- if (tbl->pos < n) rb_bug("vtable_pop: unreachable");
- tbl->pos -= n;
-}
-#endif
-
-static int
-vtable_included(const struct vtable * tbl, ID id)
-{
- int i;
-
- if (POINTER_P(tbl)) {
- for (i = 0; i < tbl->pos; i++) {
- if (tbl->tbl[i] == id) {
- return i+1;
- }
- }
- }
- return 0;
-}
+#define DVARS_TERMINAL_P(tbl) ((tbl) == DVARS_INHERIT || (tbl) == DVARS_TOPSCOPE)
typedef struct token_info {
const char *token;
@@ -236,33 +174,43 @@ typedef struct token_info {
struct token_info *next;
} token_info;
+typedef struct rb_strterm_struct rb_strterm_t;
+
/*
Structure of Lexer Buffer:
- lex_pbeg tokp lex_p lex_pend
- | | | |
- |-----------+--------------+------------|
- |<------------>|
+ lex.pbeg lex.ptok lex.pcur lex.pend
+ | | | |
+ |------------+------------+------------|
+ |<---------->|
token
*/
struct parser_params {
- NODE *heap;
+ rb_imemo_tmpbuf_t *heap;
YYSTYPE *lval;
struct {
- NODE *strterm;
+ rb_strterm_t *strterm;
VALUE (*gets)(struct parser_params*,VALUE);
VALUE input;
+ VALUE prevline;
VALUE lastline;
VALUE nextline;
const char *pbeg;
const char *pcur;
const char *pend;
- long gets_ptr;
+ const char *ptok;
+ union {
+ long ptr;
+ VALUE (*call)(VALUE, int);
+ } gets_;
enum lex_state_e state;
+ /* track the nest level of any parens "()[]{}" */
int paren_nest;
+ /* keep p->lex.paren_nest at the beginning of lambda "->" to detect tLAMBEG and keyword_do_LAMBDA */
int lpar_beg;
+ /* track the nest level of only braces "{}" */
int brace_nest;
} lex;
stack_type cond_stack;
@@ -277,26 +225,30 @@ struct parser_params {
struct local_vars *lvtbl;
int line_count;
int ruby_sourceline; /* current line no. */
- char *ruby_sourcefile; /* current source file */
+ const char *ruby_sourcefile; /* current source file */
VALUE ruby_sourcefile_string;
rb_encoding *enc;
token_info *token_info;
VALUE compile_option;
VALUE debug_buffer;
+ VALUE debug_output;
ID cur_arg;
+ rb_ast_t *ast;
+ int node_id;
+
unsigned int command_start:1;
unsigned int eofp: 1;
unsigned int ruby__end__seen: 1;
- unsigned int yydebug: 1;
+ unsigned int debug: 1;
unsigned int has_shebang: 1;
unsigned int in_defined: 1;
unsigned int in_main: 1;
unsigned int in_kwarg: 1;
- unsigned int in_single: 1;
unsigned int in_def: 1;
+ unsigned int in_class: 1;
unsigned int token_seen: 1;
unsigned int token_info_enabled: 1;
# if WARN_PAST_SCOPE
@@ -308,16 +260,20 @@ struct parser_params {
#ifndef RIPPER
/* Ruby core only */
+ unsigned int do_print: 1;
+ unsigned int do_loop: 1;
+ unsigned int do_chomp: 1;
+ unsigned int do_split: 1;
+ unsigned int warn_location: 1;
+
NODE *eval_tree_begin;
NODE *eval_tree;
VALUE error_buffer;
VALUE debug_lines;
- VALUE coverage;
const struct rb_block *base_block;
#else
/* Ripper only */
- const char *tokp;
VALUE delayed;
int delayed_line;
int delayed_col;
@@ -328,215 +284,187 @@ struct parser_params {
#endif
};
-#ifdef RIPPER
-#define intern_cstr(n,l,en) rb_intern3(n,l,en)
-#else
+#define new_tmpbuf() \
+ (rb_imemo_tmpbuf_t *)add_mark_object(p, rb_imemo_tmpbuf_auto_free_pointer(NULL))
+
#define intern_cstr(n,l,en) rb_intern3(n,l,en)
-#endif
-#define STR_NEW(p,n) rb_enc_str_new((p),(n),current_enc)
-#define STR_NEW0() rb_enc_str_new(0,0,current_enc)
-#define STR_NEW2(p) rb_enc_str_new((p),strlen(p),current_enc)
-#define STR_NEW3(p,n,e,func) parser_str_new((p),(n),(e),(func),current_enc)
-#define TOK_INTERN() intern_cstr(tok(), toklen(), current_enc)
-
-static int parser_yyerror(struct parser_params*, const char*);
-#define yyerror(msg) parser_yyerror(parser, (msg))
-
-#define lex_strterm (parser->lex.strterm)
-#define lex_state (parser->lex.state)
-#define cond_stack (parser->cond_stack)
-#define cmdarg_stack (parser->cmdarg_stack)
-#define paren_nest (parser->lex.paren_nest)
-#define lpar_beg (parser->lex.lpar_beg)
-#define brace_nest (parser->lex.brace_nest)
-#define in_single (parser->in_single)
-#define in_def (parser->in_def)
-#define in_main (parser->in_main)
-#define in_defined (parser->in_defined)
-#define tokenbuf (parser->tokenbuf)
-#define tokidx (parser->tokidx)
-#define toksiz (parser->toksiz)
-#define tokline (parser->tokline)
-#define lex_input (parser->lex.input)
-#define lex_lastline (parser->lex.lastline)
-#define lex_nextline (parser->lex.nextline)
-#define lex_pbeg (parser->lex.pbeg)
-#define lex_p (parser->lex.pcur)
-#define lex_pend (parser->lex.pend)
-#define heredoc_end (parser->heredoc_end)
-#define heredoc_indent (parser->heredoc_indent)
-#define heredoc_line_indent (parser->heredoc_line_indent)
-#define command_start (parser->command_start)
-#define lex_gets_ptr (parser->lex.gets_ptr)
-#define lex_gets (parser->lex.gets)
-#define lvtbl (parser->lvtbl)
-#define ruby__end__seen (parser->ruby__end__seen)
-#define ruby_sourceline (parser->ruby_sourceline)
-#define ruby_sourcefile (parser->ruby_sourcefile)
-#define ruby_sourcefile_string (parser->ruby_sourcefile_string)
-#define current_enc (parser->enc)
-#define current_arg (parser->cur_arg)
-#define yydebug (parser->yydebug)
+#define STR_NEW(ptr,len) rb_enc_str_new((ptr),(len),p->enc)
+#define STR_NEW0() rb_enc_str_new(0,0,p->enc)
+#define STR_NEW2(ptr) rb_enc_str_new((ptr),strlen(ptr),p->enc)
+#define STR_NEW3(ptr,len,e,func) parser_str_new((ptr),(len),(e),(func),p->enc)
+#define TOK_INTERN() intern_cstr(tok(p), toklen(p), p->enc)
+
+static int parser_yyerror(struct parser_params*, const YYLTYPE *yylloc, const char*);
+#define yyerror0(msg) parser_yyerror(p, NULL, (msg))
+#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)
+
#ifdef RIPPER
#define compile_for_eval (0)
#else
-#define compile_for_eval (parser->base_block != 0 && !in_main)
-#define ruby_eval_tree (parser->eval_tree)
-#define ruby_eval_tree_begin (parser->eval_tree_begin)
-#define ruby_debug_lines (parser->debug_lines)
-#define ruby_coverage (parser->coverage)
+#define compile_for_eval (p->base_block != 0 && !p->in_main)
#endif
-#define CALL_Q_P(q) ((q) == tANDDOT)
+#define token_column ((int)(p->lex.ptok - p->lex.pbeg))
+
+#define CALL_Q_P(q) ((q) == TOKEN2VAL(tANDDOT))
#define NODE_CALL_Q(q) (CALL_Q_P(q) ? NODE_QCALL : NODE_CALL)
-#define NEW_QCALL(q,r,m,a) NEW_NODE(NODE_CALL_Q(q),r,m,a)
+#define NEW_QCALL(q,r,m,a,loc) NEW_NODE(NODE_CALL_Q(q),r,m,a,loc)
-static int yylex(YYSTYPE*, struct parser_params*);
+#define lambda_beginning_p() (p->lex.lpar_beg == p->lex.paren_nest)
+
+static enum yytokentype yylex(YYSTYPE*, YYLTYPE*, struct parser_params*);
#ifndef RIPPER
-#define yyparse ruby_yyparse
+static inline void
+rb_discard_node(struct parser_params *p, NODE *n)
+{
+ rb_ast_delete_node(p->ast, n);
+}
+#endif
-static NODE* node_newnode(struct parser_params *, enum node_type, VALUE, VALUE, VALUE);
-#define rb_node_newnode(type, a1, a2, a3) node_newnode(parser, (type), (a1), (a2), (a3))
+static inline VALUE
+add_mark_object(struct parser_params *p, VALUE obj)
+{
+ if (!SPECIAL_CONST_P(obj)
+#ifdef RIPPER
+ && !RB_TYPE_P(obj, T_NODE) /* Ripper jumbles NODE objects and other objects... */
+#endif
+ ) {
+ rb_ast_add_mark_object(p->ast, obj);
+ }
+ return obj;
+}
+
+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))
+
+static NODE *nd_set_loc(NODE *nd, const YYLTYPE *loc);
-static NODE *cond_gen(struct parser_params*,NODE*,int);
-#define cond(node) cond_gen(parser, (node), FALSE)
-#define method_cond(node) cond_gen(parser, (node), TRUE)
-static NODE *new_if_gen(struct parser_params*,NODE*,NODE*,NODE*);
-#define new_if(cc,left,right) new_if_gen(parser, (cc), (left), (right))
-#define new_unless(cc,left,right) new_if_gen(parser, (cc), (right), (left))
-static NODE *logop_gen(struct parser_params*,enum node_type,NODE*,NODE*);
-#define logop(type,node1,node2) logop_gen(parser, (type), (node1), (node2))
+static int
+parser_get_node_id(struct parser_params *p)
+{
+ int node_id = p->node_id;
+ p->node_id++;
+ return node_id;
+}
+
+#ifndef RIPPER
+static inline void
+set_line_body(NODE *body, int line)
+{
+ if (!body) return;
+ switch (nd_type(body)) {
+ case NODE_RESCUE:
+ case NODE_ENSURE:
+ nd_set_line(body, line);
+ }
+}
+
+#define yyparse ruby_yyparse
+
+static NODE* cond(struct parser_params *p, NODE *node, const YYLTYPE *loc);
+static NODE* method_cond(struct parser_params *p, NODE *node, const YYLTYPE *loc);
+#define new_nil(loc) NEW_NIL(loc)
+static NODE *new_if(struct parser_params*,NODE*,NODE*,NODE*,const YYLTYPE*);
+static NODE *new_unless(struct parser_params*,NODE*,NODE*,NODE*,const YYLTYPE*);
+static NODE *logop(struct parser_params*,ID,NODE*,NODE*,const YYLTYPE*,const YYLTYPE*);
static NODE *newline_node(NODE*);
static void fixpos(NODE*,NODE*);
static int value_expr_gen(struct parser_params*,NODE*);
-static void void_expr_gen(struct parser_params*,NODE*);
+static void void_expr(struct parser_params*,NODE*);
static NODE *remove_begin(NODE*);
static NODE *remove_begin_all(NODE*);
-#define value_expr(node) value_expr_gen(parser, (node) = remove_begin(node))
-#define void_expr0(node) void_expr_gen(parser, (node))
-#define void_expr(node) void_expr0((node) = remove_begin(node))
-static void void_stmts_gen(struct parser_params*,NODE*);
-#define void_stmts(node) void_stmts_gen(parser, (node))
-static void reduce_nodes_gen(struct parser_params*,NODE**);
-#define reduce_nodes(n) reduce_nodes_gen(parser,(n))
-static void block_dup_check_gen(struct parser_params*,NODE*,NODE*);
-#define block_dup_check(n1,n2) block_dup_check_gen(parser,(n1),(n2))
-
-static NODE *block_append_gen(struct parser_params*,NODE*,NODE*);
-#define block_append(h,t) block_append_gen(parser,(h),(t))
-static NODE *list_append_gen(struct parser_params*,NODE*,NODE*);
-#define list_append(l,i) list_append_gen(parser,(l),(i))
+#define value_expr(node) value_expr_gen(p, (node) = remove_begin(node))
+static NODE *void_stmts(struct parser_params*,NODE*);
+static void reduce_nodes(struct parser_params*,NODE**);
+static void block_dup_check(struct parser_params*,NODE*,NODE*);
+
+static NODE *block_append(struct parser_params*,NODE*,NODE*);
+static NODE *list_append(struct parser_params*,NODE*,NODE*);
static NODE *list_concat(NODE*,NODE*);
-static NODE *arg_append_gen(struct parser_params*,NODE*,NODE*);
-#define arg_append(h,t) arg_append_gen(parser,(h),(t))
-static NODE *arg_concat_gen(struct parser_params*,NODE*,NODE*);
-#define arg_concat(h,t) arg_concat_gen(parser,(h),(t))
-static NODE *literal_concat_gen(struct parser_params*,NODE*,NODE*);
-#define literal_concat(h,t) literal_concat_gen(parser,(h),(t))
-static int literal_concat0(struct parser_params *, VALUE, VALUE);
-static NODE *new_evstr_gen(struct parser_params*,NODE*);
-#define new_evstr(n) new_evstr_gen(parser,(n))
-static NODE *evstr2dstr_gen(struct parser_params*,NODE*);
-#define evstr2dstr(n) evstr2dstr_gen(parser,(n))
+static NODE *arg_append(struct parser_params*,NODE*,NODE*,const YYLTYPE*);
+static NODE *last_arg_append(struct parser_params *p, NODE *args, NODE *last_arg, const YYLTYPE *loc);
+static NODE *rest_arg_append(struct parser_params *p, NODE *args, NODE *rest_arg, const YYLTYPE *loc);
+static NODE *literal_concat(struct parser_params*,NODE*,NODE*,const YYLTYPE*);
+static NODE *new_evstr(struct parser_params*,NODE*,const YYLTYPE*);
+static NODE *evstr2dstr(struct parser_params*,NODE*);
static NODE *splat_array(NODE*);
-static NODE *call_bin_op_gen(struct parser_params*,NODE*,ID,NODE*);
-#define call_bin_op(recv,id,arg1) call_bin_op_gen(parser, (recv),(id),(arg1))
-static NODE *call_uni_op_gen(struct parser_params*,NODE*,ID);
-#define call_uni_op(recv,id) call_uni_op_gen(parser, (recv),(id))
+static NODE *call_bin_op(struct parser_params*,NODE*,ID,NODE*,const YYLTYPE*,const YYLTYPE*);
+static NODE *call_uni_op(struct parser_params*,NODE*,ID,const YYLTYPE*,const YYLTYPE*);
+static NODE *new_qcall(struct parser_params* p, ID atype, NODE *recv, ID mid, NODE *args, const YYLTYPE *op_loc, const YYLTYPE *loc);
+static NODE *new_command_qcall(struct parser_params* p, ID atype, NODE *recv, ID mid, NODE *args, NODE *block, const YYLTYPE *op_loc, const YYLTYPE *loc);
+static NODE *method_add_block(struct parser_params*p, NODE *m, NODE *b, const YYLTYPE *loc) {b->nd_iter = m; b->nd_loc = *loc; return b;}
-static NODE *new_args_gen(struct parser_params*,NODE*,NODE*,ID,NODE*,NODE*);
-#define new_args(f,o,r,p,t) new_args_gen(parser, (f),(o),(r),(p),(t))
-static NODE *new_args_tail_gen(struct parser_params*,NODE*,ID,ID);
-#define new_args_tail(k,kr,b) new_args_tail_gen(parser, (k),(kr),(b))
-#define new_kw_arg(k) ((k) ? NEW_KW_ARG(0, (k)) : 0)
+static NODE *new_args(struct parser_params*,NODE*,NODE*,ID,NODE*,NODE*,const YYLTYPE*);
+static NODE *new_args_tail(struct parser_params*,NODE*,ID,ID,const YYLTYPE*);
+static NODE *new_kw_arg(struct parser_params *p, NODE *k, const YYLTYPE *loc);
-static VALUE negate_lit(VALUE);
-static NODE *ret_args_gen(struct parser_params*,NODE*);
-#define ret_args(node) ret_args_gen(parser, (node))
+static VALUE negate_lit(struct parser_params*, VALUE);
+static NODE *ret_args(struct parser_params*,NODE*);
static NODE *arg_blk_pass(NODE*,NODE*);
-static NODE *new_yield_gen(struct parser_params*,NODE*);
-#define new_yield(node) new_yield_gen(parser, (node))
-static NODE *dsym_node_gen(struct parser_params*,NODE*);
-#define dsym_node(node) dsym_node_gen(parser, (node))
-
-static NODE *gettable_gen(struct parser_params*,ID);
-#define gettable(id) gettable_gen(parser,(id))
-static NODE *assignable_gen(struct parser_params*,ID,NODE*);
-#define assignable(id,node) assignable_gen(parser, (id), (node))
-
-static NODE *aryset_gen(struct parser_params*,NODE*,NODE*);
-#define aryset(node1,node2) aryset_gen(parser, (node1), (node2))
-static NODE *attrset_gen(struct parser_params*,NODE*,ID,ID);
-#define attrset(node,q,id) attrset_gen(parser, (node), (q), (id))
-
-static void rb_backref_error_gen(struct parser_params*,NODE*);
-#define rb_backref_error(n) rb_backref_error_gen(parser,(n))
-static NODE *node_assign_gen(struct parser_params*,NODE*,NODE*);
-#define node_assign(node1, node2) node_assign_gen(parser, (node1), (node2))
-
-static NODE *new_op_assign_gen(struct parser_params *parser, NODE *lhs, ID op, NODE *rhs);
-static NODE *new_attr_op_assign_gen(struct parser_params *parser, NODE *lhs, ID atype, ID attr, ID op, NODE *rhs);
-#define new_attr_op_assign(lhs, type, attr, op, rhs) new_attr_op_assign_gen(parser, (lhs), (type), (attr), (op), (rhs))
-static NODE *new_const_op_assign_gen(struct parser_params *parser, NODE *lhs, ID op, NODE *rhs);
-#define new_const_op_assign(lhs, op, rhs) new_const_op_assign_gen(parser, (lhs), (op), (rhs))
-
-#define const_path_field(w, n) NEW_COLON2(w, n)
-#define top_const_field(n) NEW_COLON3(n)
-static NODE *const_decl_gen(struct parser_params *parser, NODE* path);
-#define const_decl(path) const_decl_gen(parser, path)
-
-#define var_field(n) (n)
-#define backref_assign_error(n, a) (rb_backref_error(n), NEW_BEGIN(0))
+static NODE *new_yield(struct parser_params*,NODE*,const YYLTYPE*);
+static NODE *dsym_node(struct parser_params*,NODE*,const YYLTYPE*);
+static NODE *gettable(struct parser_params*,ID,const YYLTYPE*);
+static NODE *assignable(struct parser_params*,ID,NODE*,const YYLTYPE*);
+
+static NODE *aryset(struct parser_params*,NODE*,NODE*,const YYLTYPE*);
+static NODE *attrset(struct parser_params*,NODE*,ID,ID,const YYLTYPE*);
+
+static void rb_backref_error(struct parser_params*,NODE*);
+static NODE *node_assign(struct parser_params*,NODE*,NODE*,const YYLTYPE*);
+
+static NODE *new_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, const YYLTYPE *loc);
+static NODE *new_ary_op_assign(struct parser_params *p, NODE *ary, NODE *args, ID op, NODE *rhs, const YYLTYPE *args_loc, const YYLTYPE *loc);
+static NODE *new_attr_op_assign(struct parser_params *p, NODE *lhs, ID atype, ID attr, ID op, NODE *rhs, const YYLTYPE *loc);
+static NODE *new_const_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, const YYLTYPE *loc);
+static NODE *new_bodystmt(struct parser_params *p, NODE *head, NODE *rescue, NODE *rescue_else, NODE *ensure, const YYLTYPE *loc);
+
+static NODE *const_decl(struct parser_params *p, NODE* path, const YYLTYPE *loc);
+
+static NODE *opt_arg_append(NODE*, NODE*);
static NODE *kwd_append(NODE*, NODE*);
-static NODE *new_hash_gen(struct parser_params *parser, NODE *hash);
-#define new_hash(hash) new_hash_gen(parser, (hash))
+static NODE *new_hash(struct parser_params *p, NODE *hash, const YYLTYPE *loc);
-#define new_defined(expr) NEW_DEFINED(remove_begin_all(expr))
+static NODE *new_defined(struct parser_params *p, NODE *expr, const YYLTYPE *loc);
-static NODE *new_regexp_gen(struct parser_params *, NODE *, int);
-#define new_regexp(node, opt) new_regexp_gen(parser, node, opt)
+static NODE *new_regexp(struct parser_params *, NODE *, int, const YYLTYPE *);
-static NODE *new_xstring_gen(struct parser_params *, NODE *);
-#define new_xstring(node) new_xstring_gen(parser, node)
-#define new_string1(str) (str)
+#define make_array(ary, loc) ((ary) ? (nd_set_loc(ary, loc), ary) : NEW_ZARRAY(loc))
-#define new_brace_body(param, stmt) NEW_ITER(param, stmt)
-#define new_do_body(param, stmt) NEW_ITER(param, stmt)
+static NODE *new_xstring(struct parser_params *, NODE *, const YYLTYPE *loc);
-static NODE *match_op_gen(struct parser_params*,NODE*,NODE*);
-#define match_op(node1,node2) match_op_gen(parser, (node1), (node2))
+static NODE *symbol_append(struct parser_params *p, NODE *symbols, NODE *symbol);
-static ID *local_tbl_gen(struct parser_params*);
-#define local_tbl() local_tbl_gen(parser)
+static NODE *match_op(struct parser_params*,NODE*,NODE*,const YYLTYPE*,const YYLTYPE*);
-static VALUE reg_compile_gen(struct parser_params*, VALUE, int);
-#define reg_compile(str,options) reg_compile_gen(parser, (str), (options))
-static void reg_fragment_setenc_gen(struct parser_params*, VALUE, int);
-#define reg_fragment_setenc(str,options) reg_fragment_setenc_gen(parser, (str), (options))
-static int reg_fragment_check_gen(struct parser_params*, VALUE, int);
-#define reg_fragment_check(str,options) reg_fragment_check_gen(parser, (str), (options))
-static NODE *reg_named_capture_assign_gen(struct parser_params* parser, VALUE regexp);
-#define reg_named_capture_assign(regexp) reg_named_capture_assign_gen(parser,(regexp))
+static ID *local_tbl(struct parser_params*);
-static NODE *parser_heredoc_dedent(struct parser_params*,NODE*);
-# define heredoc_dedent(str) parser_heredoc_dedent(parser, (str))
+static VALUE reg_compile(struct parser_params*, VALUE, int);
+static void reg_fragment_setenc(struct parser_params*, VALUE, int);
+static int reg_fragment_check(struct parser_params*, VALUE, int);
+static NODE *reg_named_capture_assign(struct parser_params* p, VALUE regexp, const YYLTYPE *loc);
+static int literal_concat0(struct parser_params *p, VALUE head, VALUE tail);
+static NODE *heredoc_dedent(struct parser_params*,NODE*);
#define get_id(id) (id)
#define get_value(val) (val)
#else /* RIPPER */
#define NODE_RIPPER NODE_CDECL
static inline VALUE
-ripper_new_yylval(ID a, VALUE b, VALUE c)
+ripper_new_yylval(struct parser_params *p, ID a, VALUE b, VALUE c)
{
- return (VALUE)NEW_CDECL(a, b, c);
+ add_mark_object(p, b);
+ add_mark_object(p, c);
+ return (VALUE)NEW_CDECL(a, b, c, &NULL_LOC);
}
static inline int
@@ -547,89 +475,78 @@ ripper_is_node_yylval(VALUE n)
#define value_expr(node) ((void)(node))
#define remove_begin(node) (node)
+#define void_stmts(p,x) (x)
#define rb_dvar_defined(id, base) 0
#define rb_local_defined(id, base) 0
static ID ripper_get_id(VALUE);
#define get_id(id) ripper_get_id(id)
static VALUE ripper_get_value(VALUE);
#define get_value(val) ripper_get_value(val)
-static VALUE assignable_gen(struct parser_params*,VALUE);
-#define assignable(lhs,node) assignable_gen(parser, (lhs))
-static int id_is_var_gen(struct parser_params *parser, ID id);
-#define id_is_var(id) id_is_var_gen(parser, (id))
-
-#define node_assign(node1, node2) dispatch2(assign, (node1), (node2))
+static VALUE assignable(struct parser_params*,VALUE);
+static int id_is_var(struct parser_params *p, ID id);
-static VALUE new_op_assign_gen(struct parser_params *parser, VALUE lhs, VALUE op, VALUE rhs);
-static VALUE new_attr_op_assign_gen(struct parser_params *parser, VALUE lhs, VALUE type, VALUE attr, VALUE op, VALUE rhs);
-#define new_attr_op_assign(lhs, type, attr, op, rhs) new_attr_op_assign_gen(parser, (lhs), (type), (attr), (op), (rhs))
-#define new_const_op_assign(lhs, op, rhs) new_op_assign(lhs, op, rhs)
+#define method_cond(p,node,loc) (node)
+#define call_bin_op(p, recv,id,arg1,op_loc,loc) dispatch3(binary, (recv), STATIC_ID2SYM(id), (arg1))
+#define match_op(p,node1,node2,op_loc,loc) call_bin_op(0, (node1), idEqTilde, (node2), op_loc, loc)
+#define call_uni_op(p, recv,id,op_loc,loc) dispatch2(unary, STATIC_ID2SYM(id), (recv))
+#define logop(p,id,node1,node2,op_loc,loc) call_bin_op(0, (node1), (id), (node2), op_loc, loc)
-static VALUE new_regexp_gen(struct parser_params *, VALUE, VALUE);
-#define new_regexp(node, opt) new_regexp_gen(parser, node, opt)
+#define new_nil(loc) Qnil
-static VALUE new_xstring_gen(struct parser_params *, VALUE);
-#define new_xstring(str) new_xstring_gen(parser, str)
-#define new_string1(str) dispatch1(string_literal, str)
+static VALUE new_regexp(struct parser_params *, VALUE, VALUE, const YYLTYPE *);
-#define new_brace_body(param, stmt) dispatch2(brace_block, escape_Qundef(param), stmt)
-#define new_do_body(param, stmt) dispatch2(do_block, escape_Qundef(param), stmt)
+static VALUE const_decl(struct parser_params *p, VALUE path);
-#define const_path_field(w, n) dispatch2(const_path_field, (w), (n))
-#define top_const_field(n) dispatch1(top_const_field, (n))
-static VALUE const_decl_gen(struct parser_params *parser, VALUE path);
-#define const_decl(path) const_decl_gen(parser, path)
-
-#define var_field(n) dispatch1(var_field, (n))
-static VALUE assign_error_gen(struct parser_params *parser, VALUE a);
-#define assign_error(a) assign_error_gen(parser, (a))
-#define backref_assign_error(n, a) assign_error(a)
+static VALUE var_field(struct parser_params *p, VALUE a);
+static VALUE assign_error(struct parser_params *p, VALUE a);
static VALUE parser_reg_compile(struct parser_params*, VALUE, int, VALUE *);
#endif /* !RIPPER */
-#define new_op_assign(lhs, op, rhs) new_op_assign_gen(parser, (lhs), (op), (rhs))
-
-RUBY_FUNC_EXPORTED VALUE rb_parser_reg_compile(struct parser_params* parser, VALUE str, int options);
-RUBY_FUNC_EXPORTED int rb_reg_fragment_setenc(struct parser_params*, VALUE, int);
-
-
-static ID formal_argument_gen(struct parser_params*, ID);
-#define formal_argument(id) formal_argument_gen(parser, (id))
-static ID shadowing_lvar_gen(struct parser_params*,ID);
-#define shadowing_lvar(name) shadowing_lvar_gen(parser, (name))
-static void new_bv_gen(struct parser_params*,ID);
-#define new_bv(id) new_bv_gen(parser, (id))
-
-static void local_push_gen(struct parser_params*,int);
-#define local_push(top) local_push_gen(parser,(top))
-static void local_pop_gen(struct parser_params*);
-#define local_pop() local_pop_gen(parser)
-static void local_var_gen(struct parser_params*, ID);
-#define local_var(id) local_var_gen(parser, (id))
-static void arg_var_gen(struct parser_params*, ID);
-#define arg_var(id) arg_var_gen(parser, (id))
-static int local_id_gen(struct parser_params*, ID);
-#define local_id(id) local_id_gen(parser, (id))
-static ID internal_id_gen(struct parser_params*);
-#define internal_id() internal_id_gen(parser)
-
-static const struct vtable *dyna_push_gen(struct parser_params *);
-#define dyna_push() dyna_push_gen(parser)
-static void dyna_pop_gen(struct parser_params*, const struct vtable *);
-#define dyna_pop(node) dyna_pop_gen(parser, (node))
-static int dyna_in_block_gen(struct parser_params*);
-#define dyna_in_block() dyna_in_block_gen(parser)
-#define dyna_var(id) local_var(id)
-static int dvar_defined_gen(struct parser_params*,ID,int);
-#define dvar_defined(id) dvar_defined_gen(parser, (id), 0)
-#define dvar_defined_get(id) dvar_defined_gen(parser, (id), 1)
-static int dvar_curr_gen(struct parser_params*,ID);
-#define dvar_curr(id) dvar_curr_gen(parser, (id))
-
-static int lvar_defined_gen(struct parser_params*, ID);
-#define lvar_defined(id) lvar_defined_gen(parser, (id))
+/* forward declaration */
+typedef struct rb_strterm_heredoc_struct rb_strterm_heredoc_t;
+
+RUBY_SYMBOL_EXPORT_BEGIN
+VALUE rb_parser_reg_compile(struct parser_params* p, VALUE str, int options);
+int rb_reg_fragment_setenc(struct parser_params*, VALUE, int);
+enum lex_state_e rb_parser_trace_lex_state(struct parser_params *, enum lex_state_e, enum lex_state_e, int);
+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);
+void rb_parser_set_location_from_strterm_heredoc(struct parser_params *p, rb_strterm_heredoc_t *here, YYLTYPE *yylloc);
+void rb_parser_set_location_of_none(struct parser_params *p, YYLTYPE *yylloc);
+void rb_parser_set_location(struct parser_params *p, YYLTYPE *yylloc);
+RUBY_SYMBOL_EXPORT_END
+
+static void parser_token_value_print(struct parser_params *p, enum yytokentype type, const YYSTYPE *valp);
+static ID formal_argument(struct parser_params*, ID);
+static ID shadowing_lvar(struct parser_params*,ID);
+static void new_bv(struct parser_params*,ID);
+
+static void local_push(struct parser_params*,int);
+static void local_pop(struct parser_params*);
+static void local_var(struct parser_params*, ID);
+static void arg_var(struct parser_params*, ID);
+static int local_id(struct parser_params *p, ID id);
+static int local_id_ref(struct parser_params*, ID, ID **);
+static ID internal_id(struct parser_params*);
+
+static const struct vtable *dyna_push(struct parser_params *);
+static void dyna_pop(struct parser_params*, const struct vtable *);
+static int dyna_in_block(struct parser_params*);
+#define dyna_var(p, id) local_var(p, id)
+static int dvar_defined(struct parser_params*, ID);
+static int dvar_defined_ref(struct parser_params*, ID, ID**);
+static int dvar_curr(struct parser_params*,ID);
+
+static int lvar_defined(struct parser_params*, ID);
+
+#ifdef RIPPER
+# define METHOD_NOT idNOT
+#else
+# define METHOD_NOT '!'
+#endif
#define RE_OPTION_ONCE (1<<16)
#define RE_OPTION_ENCODING_SHIFT 8
@@ -639,17 +556,67 @@ static int lvar_defined_gen(struct parser_params*, ID);
#define RE_OPTION_MASK 0xff
#define RE_OPTION_ARG_ENCODING_NONE 32
-#define NODE_STRTERM NODE_ZARRAY /* nothing to gc */
-#define NODE_HEREDOC NODE_ARRAY /* 1, 3 to gc */
-#define SIGN_EXTEND(x,n) (((1<<(n)-1)^((x)&~(~0<<(n))))-(1<<(n)-1))
-#define nd_func u1.id
-#if SIZEOF_SHORT == 2
-#define nd_term(node) ((signed short)(node)->u2.id)
-#else
-#define nd_term(node) SIGN_EXTEND((node)->u2.id, CHAR_BIT*2)
+/* structs for managing terminator of string literal and heredocment */
+typedef struct rb_strterm_literal_struct {
+ union {
+ VALUE dummy;
+ long nest;
+ } u0;
+ union {
+ VALUE dummy;
+ long func; /* STR_FUNC_* (e.g., STR_FUNC_ESCAPE and STR_FUNC_EXPAND) */
+ } u1;
+ union {
+ VALUE dummy;
+ long paren; /* '(' of `%q(...)` */
+ } u2;
+ union {
+ VALUE dummy;
+ long term; /* ')' of `%q(...)` */
+ } u3;
+} rb_strterm_literal_t;
+
+struct rb_strterm_heredoc_struct {
+ SIGNED_VALUE sourceline; /* lineno of the line that contains `<<"END"` */
+ VALUE term; /* `"END"` of `<<"END"` */
+ VALUE lastline; /* the string of line that contains `<<"END"` */
+ union {
+ VALUE dummy;
+ long lastidx; /* the column of `<<"END"` */
+ } u3;
+};
+
+#define STRTERM_HEREDOC IMEMO_FL_USER0
+
+struct rb_strterm_struct {
+ VALUE flags;
+ union {
+ rb_strterm_literal_t literal;
+ rb_strterm_heredoc_t heredoc;
+ } u;
+};
+
+#ifndef RIPPER
+void
+rb_strterm_mark(VALUE obj)
+{
+ rb_strterm_t *strterm = (rb_strterm_t*)obj;
+ if (RBASIC(obj)->flags & STRTERM_HEREDOC) {
+ rb_strterm_heredoc_t *heredoc = &strterm->u.heredoc;
+ rb_gc_mark(heredoc->term);
+ rb_gc_mark(heredoc->lastline);
+ }
+}
#endif
-#define nd_paren(node) (char)((node)->u2.id >> CHAR_BIT*2)
-#define nd_nest u3.cnt
+
+#define TOKEN2ID(tok) ( \
+ tTOKEN_LOCAL_BEGIN<(tok)&&(tok)<tTOKEN_LOCAL_END ? TOKEN2LOCALID(tok) : \
+ tTOKEN_INSTANCE_BEGIN<(tok)&&(tok)<tTOKEN_INSTANCE_END ? TOKEN2INSTANCEID(tok) : \
+ tTOKEN_GLOBAL_BEGIN<(tok)&&(tok)<tTOKEN_GLOBAL_END ? TOKEN2GLOBALID(tok) : \
+ tTOKEN_CONST_BEGIN<(tok)&&(tok)<tTOKEN_CONST_END ? TOKEN2CONSTID(tok) : \
+ tTOKEN_CLASS_BEGIN<(tok)&&(tok)<tTOKEN_CLASS_END ? TOKEN2CLASSID(tok) : \
+ tTOKEN_ATTRSET_BEGIN<(tok)&&(tok)<tTOKEN_ATTRSET_END ? TOKEN2ATTRSETID(tok) : \
+ ((tok) / ((tok)<tPRESERVED_ID_END && ((tok)>=128 || rb_ispunct(tok)))))
/****** Ripper *******/
@@ -668,88 +635,62 @@ static VALUE ripper_dispatch3(struct parser_params*,ID,VALUE,VALUE,VALUE);
static VALUE ripper_dispatch4(struct parser_params*,ID,VALUE,VALUE,VALUE,VALUE);
static VALUE ripper_dispatch5(struct parser_params*,ID,VALUE,VALUE,VALUE,VALUE,VALUE);
static VALUE ripper_dispatch7(struct parser_params*,ID,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE);
-static void ripper_error_gen(struct parser_params *parser);
-#define ripper_error() ripper_error_gen(parser)
+static void ripper_error(struct parser_params *p);
-#define dispatch0(n) ripper_dispatch0(parser, TOKEN_PASTE(ripper_id_, n))
-#define dispatch1(n,a) ripper_dispatch1(parser, TOKEN_PASTE(ripper_id_, n), (a))
-#define dispatch2(n,a,b) ripper_dispatch2(parser, TOKEN_PASTE(ripper_id_, n), (a), (b))
-#define dispatch3(n,a,b,c) ripper_dispatch3(parser, TOKEN_PASTE(ripper_id_, n), (a), (b), (c))
-#define dispatch4(n,a,b,c,d) ripper_dispatch4(parser, TOKEN_PASTE(ripper_id_, n), (a), (b), (c), (d))
-#define dispatch5(n,a,b,c,d,e) ripper_dispatch5(parser, TOKEN_PASTE(ripper_id_, n), (a), (b), (c), (d), (e))
-#define dispatch7(n,a,b,c,d,e,f,g) ripper_dispatch7(parser, TOKEN_PASTE(ripper_id_, n), (a), (b), (c), (d), (e), (f), (g))
+#define dispatch0(n) ripper_dispatch0(p, TOKEN_PASTE(ripper_id_, n))
+#define dispatch1(n,a) ripper_dispatch1(p, TOKEN_PASTE(ripper_id_, n), (a))
+#define dispatch2(n,a,b) ripper_dispatch2(p, TOKEN_PASTE(ripper_id_, n), (a), (b))
+#define dispatch3(n,a,b,c) ripper_dispatch3(p, TOKEN_PASTE(ripper_id_, n), (a), (b), (c))
+#define dispatch4(n,a,b,c,d) ripper_dispatch4(p, TOKEN_PASTE(ripper_id_, n), (a), (b), (c), (d))
+#define dispatch5(n,a,b,c,d,e) ripper_dispatch5(p, TOKEN_PASTE(ripper_id_, n), (a), (b), (c), (d), (e))
+#define dispatch7(n,a,b,c,d,e,f,g) ripper_dispatch7(p, TOKEN_PASTE(ripper_id_, n), (a), (b), (c), (d), (e), (f), (g))
#define yyparse ripper_yyparse
-#define ripper_intern(s) ID2SYM(rb_intern(s))
-static VALUE ripper_id2sym(ID);
-#ifdef __GNUC__
-#define ripper_id2sym(id) (rb_ispunct((int)(id)) ? \
- ID2SYM(id) : ripper_id2sym(id))
-#endif
-
-#define arg_new() dispatch0(args_new)
-#define arg_add(l,a) dispatch2(args_add, (l), (a))
-#define arg_add_star(l,a) dispatch2(args_add_star, (l), (a))
-#define arg_add_block(l,b) dispatch2(args_add_block, (l), (b))
-#define arg_add_optblock(l,b) ((b)==Qundef? (l) : dispatch2(args_add_block, (l), (b)))
-#define bare_assoc(v) dispatch1(bare_assoc_hash, (v))
-#define arg_add_assocs(l,b) arg_add((l), bare_assoc(b))
-
-#define args2mrhs(a) dispatch1(mrhs_new_from_args, (a))
-#define mrhs_new() dispatch0(mrhs_new)
-#define mrhs_add(l,a) dispatch2(mrhs_add, (l), (a))
-#define mrhs_add_star(l,a) dispatch2(mrhs_add_star, (l), (a))
-
-#define mlhs_new() dispatch0(mlhs_new)
-#define mlhs_add(l,a) dispatch2(mlhs_add, (l), (a))
-#define mlhs_add_star(l,a) dispatch2(mlhs_add_star, (l), (a))
+#define ID2VAL(id) STATIC_ID2SYM(id)
+#define TOKEN2VAL(t) ID2VAL(TOKEN2ID(t))
+#define KWD2EID(t, v) ripper_new_yylval(p, keyword_##t, get_value(v), 0)
#define params_new(pars, opts, rest, pars2, kws, kwrest, blk) \
dispatch7(params, (pars), (opts), (rest), (pars2), (kws), (kwrest), (blk))
-#define blockvar_new(p,v) dispatch2(block_var, (p), (v))
-#define blockvar_add_star(l,a) dispatch2(block_var_add_star, (l), (a))
-#define blockvar_add_block(l,a) dispatch2(block_var_add_block, (l), (a))
-
-#define method_optarg(m,a) ((a)==Qundef ? (m) : dispatch2(method_add_arg,(m),(a)))
-#define method_arg(m,a) dispatch2(method_add_arg,(m),(a))
-#define method_add_block(m,b) dispatch2(method_add_block, (m), (b))
-
#define escape_Qundef(x) ((x)==Qundef ? Qnil : (x))
static inline VALUE
-new_args_gen(struct parser_params *parser, VALUE f, VALUE o, VALUE r, VALUE p, VALUE tail)
+new_args(struct parser_params *p, VALUE pre_args, VALUE opt_args, VALUE rest_arg, VALUE post_args, VALUE tail, YYLTYPE *loc)
{
NODE *t = (NODE *)tail;
- VALUE k = t->u1.value, kr = t->u2.value, b = t->u3.value;
- return params_new(f, o, r, p, k, kr, escape_Qundef(b));
+ VALUE kw_args = t->u1.value, kw_rest_arg = t->u2.value, block = t->u3.value;
+ return params_new(pre_args, opt_args, rest_arg, post_args, kw_args, kw_rest_arg, escape_Qundef(block));
}
-#define new_args(f,o,r,p,t) new_args_gen(parser, (f),(o),(r),(p),(t))
static inline VALUE
-new_args_tail_gen(struct parser_params *parser, VALUE k, VALUE kr, VALUE b)
+new_args_tail(struct parser_params *p, VALUE kw_args, VALUE kw_rest_arg, VALUE block, YYLTYPE *loc)
{
- return (VALUE)MEMO_NEW(k, kr, b);
+ NODE *t = rb_node_newnode(NODE_ARGS_AUX, kw_args, kw_rest_arg, block, &NULL_LOC);
+ add_mark_object(p, kw_args);
+ add_mark_object(p, kw_rest_arg);
+ add_mark_object(p, block);
+ return (VALUE)t;
}
-#define new_args_tail(k,kr,b) new_args_tail_gen(parser, (k),(kr),(b))
-#define new_defined(expr) dispatch1(defined, (expr))
+#define new_defined(p,expr,loc) dispatch1(defined, (expr))
-static VALUE parser_heredoc_dedent(struct parser_params*,VALUE);
-# define heredoc_dedent(str) parser_heredoc_dedent(parser, (str))
-
-#define FIXME 0
+static VALUE heredoc_dedent(struct parser_params*,VALUE);
#else
-#define ripper_id2sym(id) id
+#define ID2VAL(id) ((VALUE)(id))
+#define TOKEN2VAL(t) ID2VAL(t)
+#define KWD2EID(t, v) keyword_##t
#endif /* RIPPER */
#ifndef RIPPER
# define Qnone 0
+# define Qnull 0
# define ifndef_ripper(x) (x)
#else
# define Qnone Qnil
+# define Qnull Qundef
# define ifndef_ripper(x)
#endif
@@ -774,52 +715,57 @@ static VALUE parser_heredoc_dedent(struct parser_params*,VALUE);
# define rb_warning3L(l,fmt,a,b,c) WARNING_CALL(WARNING_ARGS_L(l, fmt, 4), (a), (b), (c))
# define rb_warning4L(l,fmt,a,b,c,d) WARNING_CALL(WARNING_ARGS_L(l, fmt, 5), (a), (b), (c), (d))
#ifdef RIPPER
-static ID id_warn, id_warning;
+static ID id_warn, id_warning, id_gets;
+# define WARN_S_L(s,l) STR_NEW(s,l)
# define WARN_S(s) STR_NEW2(s)
# define WARN_I(i) INT2NUM(i)
+# define WARN_ID(i) rb_id2str(i)
# define PRIsWARN "s"
-# define WARN_ARGS(fmt,n) parser->value, id_warn, n, rb_usascii_str_new_lit(fmt)
+# define WARN_ARGS(fmt,n) p->value, id_warn, n, rb_usascii_str_new_lit(fmt)
# define WARN_ARGS_L(l,fmt,n) WARN_ARGS(fmt,n)
+# ifdef HAVE_VA_ARGS_MACRO
+# define WARN_CALL(...) rb_funcall(__VA_ARGS__)
+# else
# define WARN_CALL rb_funcall
-# define WARNING_ARGS(fmt,n) parser->value, id_warning, n, rb_usascii_str_new_lit(fmt)
+# endif
+# define WARNING_ARGS(fmt,n) p->value, id_warning, n, rb_usascii_str_new_lit(fmt)
# define WARNING_ARGS_L(l, fmt,n) WARNING_ARGS(fmt,n)
+# ifdef HAVE_VA_ARGS_MACRO
+# define WARNING_CALL(...) rb_funcall(__VA_ARGS__)
+# else
# define WARNING_CALL rb_funcall
-static void ripper_compile_error(struct parser_params*, const char *fmt, ...);
+# endif
+PRINTF_ARGS(static void ripper_compile_error(struct parser_params*, const char *fmt, ...), 2, 3);
# define compile_error ripper_compile_error
-# define PARSER_ARG parser,
#else
+# define WARN_S_L(s,l) s
# define WARN_S(s) s
# define WARN_I(i) i
+# define WARN_ID(i) rb_id2name(i)
# define PRIsWARN PRIsVALUE
-# define WARN_ARGS(fmt,n) WARN_ARGS_L(ruby_sourceline,fmt,n)
-# define WARN_ARGS_L(l,fmt,n) ruby_sourcefile, (l), (fmt)
+# define WARN_ARGS(fmt,n) WARN_ARGS_L(p->ruby_sourceline,fmt,n)
+# define WARN_ARGS_L(l,fmt,n) p->ruby_sourcefile, (l), (fmt)
# define WARN_CALL rb_compile_warn
# define WARNING_ARGS(fmt,n) WARN_ARGS(fmt,n)
# define WARNING_ARGS_L(l,fmt,n) WARN_ARGS_L(l,fmt,n)
# define WARNING_CALL rb_compile_warning
-static void parser_compile_error(struct parser_params*, const char *fmt, ...);
+PRINTF_ARGS(static void parser_compile_error(struct parser_params*, const char *fmt, ...), 2, 3);
# define compile_error parser_compile_error
-# define PARSER_ARG parser,
-#endif
-
-/* Older versions of Yacc set YYMAXDEPTH to a very low value by default (150,
- for instance). This is too low for Ruby to parse some files, such as
- date/format.rb, therefore bump the value up to at least Bison's default. */
-#ifdef OLD_YACC
-#ifndef YYMAXDEPTH
-#define YYMAXDEPTH 10000
-#endif
#endif
-static void token_info_push_gen(struct parser_params*, const char *token, size_t len);
-static void token_info_pop_gen(struct parser_params*, const char *token, size_t len);
-#define token_info_push(token) token_info_push_gen(parser, (token), rb_strlen_lit(token))
-#define token_info_pop(token) token_info_pop_gen(parser, (token), rb_strlen_lit(token))
+static void token_info_push(struct parser_params*, const char *token, const rb_code_location_t *loc);
+static void token_info_pop(struct parser_params*, const char *token, const rb_code_location_t *loc);
+static void token_info_warn(struct parser_params *p, const char *token, token_info *ptinfo_beg, int same, const rb_code_location_t *loc);
%}
-%pure-parser
-%lex-param {struct parser_params *parser}
-%parse-param {struct parser_params *parser}
+%expect 0
+%define api.pure
+%lex-param {struct parser_params *p}
+%parse-param {struct parser_params *p}
+%initial-action
+{
+ RUBY_SET_YYLLOC_OF_NONE(@$);
+};
%union {
VALUE val;
@@ -827,62 +773,59 @@ static void token_info_pop_gen(struct parser_params*, const char *token, size_t
ID id;
int num;
const struct vtable *vars;
-}
-
-/*%%%*/
-%token
-/*%
-%token <val>
-%*/
- keyword_class
- keyword_module
- keyword_def
- keyword_undef
- keyword_begin
- keyword_rescue
- keyword_ensure
- keyword_end
- keyword_if
- keyword_unless
- keyword_then
- keyword_elsif
- keyword_else
- keyword_case
- keyword_when
- keyword_while
- keyword_until
- keyword_for
- keyword_break
- keyword_next
- keyword_redo
- keyword_retry
- keyword_in
- keyword_do
- keyword_do_cond
- keyword_do_block
- keyword_do_LAMBDA
- keyword_return
- keyword_yield
- keyword_super
- keyword_self
- keyword_nil
- keyword_true
- keyword_false
- keyword_and
- keyword_or
- keyword_not
- modifier_if
- modifier_unless
- modifier_while
- modifier_until
- modifier_rescue
- keyword_alias
- keyword_defined
- keyword_BEGIN
- keyword_END
- keyword__LINE__
- keyword__FILE__
- keyword__ENCODING__
+ struct rb_strterm_struct *strterm;
+}
+
+%token <id>
+ keyword_class "class"
+ keyword_module "module"
+ keyword_def "def"
+ keyword_undef "undef"
+ keyword_begin "begin"
+ keyword_rescue "rescue"
+ keyword_ensure "ensure"
+ keyword_end "end"
+ keyword_if "if"
+ keyword_unless "unless"
+ keyword_then "then"
+ keyword_elsif "elsif"
+ keyword_else "else"
+ keyword_case "case"
+ keyword_when "when"
+ keyword_while "while"
+ keyword_until "until"
+ keyword_for "for"
+ keyword_break "break"
+ keyword_next "next"
+ keyword_redo "redo"
+ keyword_retry "retry"
+ keyword_in "in"
+ keyword_do "do"
+ keyword_do_cond "do (for condition)"
+ keyword_do_block "do (for block)"
+ keyword_do_LAMBDA "do (for lambda)"
+ keyword_return "return"
+ keyword_yield "yield"
+ keyword_super "super"
+ keyword_self "self"
+ keyword_nil "nil"
+ keyword_true "true"
+ keyword_false "false"
+ keyword_and "and"
+ keyword_or "or"
+ keyword_not "not"
+ modifier_if "if (modifier)"
+ modifier_unless "unless (modifier)"
+ modifier_while "while (modifier)"
+ modifier_until "until (modifier)"
+ modifier_rescue "rescue (modifier)"
+ keyword_alias "alias"
+ keyword_defined "defined?"
+ keyword_BEGIN "BEGIN"
+ keyword_END "END"
+ keyword__LINE__ "__LINE__"
+ keyword__FILE__ "__FILE__"
+ keyword__ENCODING__ "__ENCODING__"
%token <id> tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL
%token <node> tINTEGER tFLOAT tRATIONAL tIMAGINARY tSTRING_CONTENT tCHAR
@@ -893,9 +836,9 @@ static void token_info_pop_gen(struct parser_params*, const char *token, size_t
%type <node> string_contents xstring_contents regexp_contents string_content
%type <node> words symbols symbol_list qwords qsymbols word_list qword_list qsym_list word
%type <node> literal numeric simple_numeric dsym cpath
-%type <node> top_compstmt top_stmts top_stmt
+%type <node> top_compstmt top_stmts top_stmt begin_block
%type <node> bodystmt compstmt stmts stmt_or_begin stmt expr arg primary command command_call method_call
-%type <node> expr_value arg_value primary_value fcall
+%type <node> expr_value expr_value_do arg_value primary_value fcall rel_expr
%type <node> if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure
%type <node> args call_args opt_call_args
%type <node> paren_args opt_paren_args args_tail opt_args_tail block_args_tail opt_block_args_tail
@@ -913,12 +856,16 @@ static void token_info_pop_gen(struct parser_params*, const char *token, size_t
%type <node> mlhs mlhs_head mlhs_basic mlhs_item mlhs_node mlhs_post mlhs_inner
%type <id> fsym keyword_variable user_variable sym symbol operation operation2 operation3
%type <id> cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_bad_arg
-%type <id> f_kwrest f_label f_arg_asgn call_op call_op2
-/*%%%*/
-/*%
-%type <val> program reswords then do dot_or_colon
-%*/
+%type <id> f_kwrest f_label f_arg_asgn call_op call_op2 reswords relop dot_or_colon
%token END_OF_INPUT 0 "end-of-input"
+%token <id> '.'
+/* escaped chars, should be ignored otherwise */
+%token <id> '\\' "backslash"
+%token tSP "escaped space"
+%token <id> '\t' "escaped horizontal tab"
+%token <id> '\f' "escaped form feed"
+%token <id> '\r' "escaped carriage return"
+%token <id> '\13' "escaped vertical tab"
%token tUPLUS RUBY_TOKEN(UPLUS) "unary+"
%token tUMINUS RUBY_TOKEN(UMINUS) "unary-"
%token tPOW RUBY_TOKEN(POW) "**"
@@ -938,8 +885,8 @@ static void token_info_pop_gen(struct parser_params*, const char *token, size_t
%token tASET RUBY_TOKEN(ASET) "[]="
%token tLSHFT RUBY_TOKEN(LSHFT) "<<"
%token tRSHFT RUBY_TOKEN(RSHFT) ">>"
-%token tANDDOT RUBY_TOKEN(ANDDOT) "&."
-%token tCOLON2 "::"
+%token <id> tANDDOT RUBY_TOKEN(ANDDOT) "&."
+%token <id> tCOLON2 RUBY_TOKEN(COLON2) "::"
%token tCOLON3 ":: at EXPR_BEG"
%token <id> tOP_ASGN /* +=, -= etc. */
%token tASSOC "=>"
@@ -989,69 +936,56 @@ static void token_info_pop_gen(struct parser_params*, const char *token, size_t
%%
program : {
SET_LEX_STATE(EXPR_BEG);
- /*%%%*/
- local_push(compile_for_eval || in_main);
- /*%
- local_push(0);
- %*/
+ local_push(p, ifndef_ripper(1)+0);
}
top_compstmt
{
/*%%%*/
if ($2 && !compile_for_eval) {
+ NODE *node = $2;
/* last expression should not be void */
- if (nd_type($2) != NODE_BLOCK) void_expr($2);
- else {
- NODE *node = $2;
+ if (nd_type(node) == NODE_BLOCK) {
while (node->nd_next) {
node = node->nd_next;
}
- void_expr(node->nd_head);
+ node = node->nd_head;
}
+ node = remove_begin(node);
+ void_expr(p, node);
}
- ruby_eval_tree = NEW_SCOPE(0, block_append(ruby_eval_tree, $2));
- /*%
- $$ = $2;
- parser->result = dispatch1(program, $$);
- %*/
- local_pop();
+ p->eval_tree = NEW_SCOPE(0, block_append(p, p->eval_tree, $2), &@$);
+ /*% %*/
+ /*% ripper[final]: program!($2) %*/
+ local_pop(p);
}
;
top_compstmt : top_stmts opt_terms
{
- /*%%%*/
- void_stmts($1);
- /*%
- %*/
- $$ = $1;
+ $$ = void_stmts(p, $1);
}
;
top_stmts : none
{
/*%%%*/
- $$ = NEW_BEGIN(0);
- /*%
- $$ = dispatch2(stmts_add, dispatch0(stmts_new),
- dispatch0(void_stmt));
- %*/
+ $$ = NEW_BEGIN(0, &@$);
+ /*% %*/
+ /*% ripper: stmts_add!(stmts_new!, void_stmt!) %*/
}
| top_stmt
{
/*%%%*/
$$ = newline_node($1);
- /*%
- $$ = dispatch2(stmts_add, dispatch0(stmts_new), $1);
- %*/
+ /*% %*/
+ /*% ripper: stmts_add!(stmts_new!, $1) %*/
}
| top_stmts terms top_stmt
{
/*%%%*/
- $$ = block_append($1, newline_node($3));
- /*%
- $$ = dispatch2(stmts_add, $1, $3);
- %*/
+ $$ = block_append(p, $1, newline_node($3));
+ /*% %*/
+ /*% ripper: stmts_add!($1, $3) %*/
}
| error top_stmt
{
@@ -1060,94 +994,71 @@ top_stmts : none
;
top_stmt : stmt
- | keyword_BEGIN
+ | keyword_BEGIN begin_block
{
- /*%%%*/
- /* local_push(0); */
- /*%
- %*/
+ $$ = $2;
}
- '{' top_compstmt '}'
+ ;
+
+begin_block : '{' top_compstmt '}'
{
/*%%%*/
- ruby_eval_tree_begin = block_append(ruby_eval_tree_begin,
- $4);
- /* NEW_PREEXE($4)); */
- /* local_pop(); */
- $$ = NEW_BEGIN(0);
- /*%
- $$ = dispatch1(BEGIN, $4);
- %*/
+ p->eval_tree_begin = block_append(p, p->eval_tree_begin,
+ NEW_BEGIN($2, &@$));
+ $$ = NEW_BEGIN(0, &@$);
+ /*% %*/
+ /*% ripper: BEGIN!($2) %*/
}
;
bodystmt : compstmt
opt_rescue
- opt_else
+ k_else {if (!$2) {yyerror1(&@3, "else without rescue is useless");}}
+ compstmt
opt_ensure
{
/*%%%*/
- $$ = $1;
- if ($2) {
- $$ = NEW_RESCUE($1, $2, $3);
- }
- else if ($3) {
- rb_warn0("else without rescue is useless");
- $$ = block_append($$, $3);
- }
- if ($4) {
- if ($$) {
- $$ = NEW_ENSURE($$, $4);
- }
- else {
- $$ = block_append($4, NEW_NIL());
- }
- }
- fixpos($$, $1);
- /*%
- $$ = dispatch4(bodystmt,
- escape_Qundef($1),
- escape_Qundef($2),
- escape_Qundef($3),
- escape_Qundef($4));
- %*/
+ $$ = new_bodystmt(p, $1, $2, $5, $6, &@$);
+ /*% %*/
+ /*% ripper: bodystmt!(escape_Qundef($1), escape_Qundef($2), escape_Qundef($5), escape_Qundef($6)) %*/
+ }
+ | compstmt
+ opt_rescue
+ opt_ensure
+ {
+ /*%%%*/
+ $$ = new_bodystmt(p, $1, $2, 0, $3, &@$);
+ /*% %*/
+ /*% ripper: bodystmt!(escape_Qundef($1), escape_Qundef($2), Qnil, escape_Qundef($3)) %*/
}
;
compstmt : stmts opt_terms
{
- /*%%%*/
- void_stmts($1);
- /*%
- %*/
- $$ = $1;
+ $$ = void_stmts(p, $1);
}
;
stmts : none
{
/*%%%*/
- $$ = NEW_BEGIN(0);
- /*%
- $$ = dispatch2(stmts_add, dispatch0(stmts_new),
- dispatch0(void_stmt));
- %*/
+ $$ = NEW_BEGIN(0, &@$);
+ /*% %*/
+ /*% ripper: stmts_add!(stmts_new!, void_stmt!) %*/
}
| stmt_or_begin
{
/*%%%*/
$$ = newline_node($1);
- /*%
- $$ = dispatch2(stmts_add, dispatch0(stmts_new), $1);
- %*/
+ /*% %*/
+ /*% ripper: stmts_add!(stmts_new!, $1) %*/
}
| stmts terms stmt_or_begin
{
/*%%%*/
- $$ = block_append($1, newline_node($3));
- /*%
- $$ = dispatch2(stmts_add, $1, $3);
- %*/
+ $$ = block_append(p, $1, newline_node($3));
+ /*% %*/
+ /*% ripper: stmts_add!($1, $3) %*/
}
| error stmt
{
@@ -1161,40 +1072,26 @@ stmt_or_begin : stmt
}
| keyword_BEGIN
{
- yyerror("BEGIN is permitted only at toplevel");
- /*%%%*/
- /* local_push(0); */
- /*%
- %*/
+ yyerror1(&@1, "BEGIN is permitted only at toplevel");
}
- '{' top_compstmt '}'
+ begin_block
{
- /*%%%*/
- ruby_eval_tree_begin = block_append(ruby_eval_tree_begin,
- $4);
- /* NEW_PREEXE($4)); */
- /* local_pop(); */
- $$ = NEW_BEGIN(0);
- /*%
- $$ = dispatch1(BEGIN, $4);
- %*/
+ $$ = $3;
}
stmt : keyword_alias fitem {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fitem
{
/*%%%*/
- $$ = NEW_ALIAS($2, $4);
- /*%
- $$ = dispatch2(alias, $2, $4);
- %*/
+ $$ = NEW_ALIAS($2, $4, &@$);
+ /*% %*/
+ /*% ripper: alias!($2, $4) %*/
}
| keyword_alias tGVAR tGVAR
{
/*%%%*/
- $$ = NEW_VALIAS($2, $3);
- /*%
- $$ = dispatch2(var_alias, $2, $3);
- %*/
+ $$ = NEW_VALIAS($2, $3, &@$);
+ /*% %*/
+ /*% ripper: var_alias!($2, $3) %*/
}
| keyword_alias tGVAR tBACK_REF
{
@@ -1202,197 +1099,190 @@ stmt : keyword_alias fitem {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fitem
char buf[2];
buf[0] = '$';
buf[1] = (char)$3->nd_nth;
- $$ = NEW_VALIAS($2, rb_intern2(buf, 2));
- /*%
- $$ = dispatch2(var_alias, $2, $3);
- %*/
+ $$ = NEW_VALIAS($2, rb_intern2(buf, 2), &@$);
+ /*% %*/
+ /*% ripper: var_alias!($2, $3) %*/
}
| keyword_alias tGVAR tNTH_REF
{
/*%%%*/
- yyerror("can't make alias for the number variables");
- $$ = NEW_BEGIN(0);
- /*%
- $$ = dispatch2(var_alias, $2, $3);
- $$ = dispatch1(alias_error, $$);
- ripper_error();
- %*/
+ yyerror1(&@3, "can't make alias for the number variables");
+ $$ = NEW_BEGIN(0, &@$);
+ /*% %*/
+ /*% ripper[error]: alias_error!(var_alias!($2, $3)) %*/
}
| keyword_undef undef_list
{
/*%%%*/
$$ = $2;
- /*%
- $$ = dispatch1(undef, $2);
- %*/
+ /*% %*/
+ /*% ripper: undef!($2) %*/
}
| stmt modifier_if expr_value
{
/*%%%*/
- $$ = new_if($3, remove_begin($1), 0);
+ $$ = new_if(p, $3, remove_begin($1), 0, &@$);
fixpos($$, $3);
- /*%
- $$ = dispatch2(if_mod, $3, $1);
- %*/
+ /*% %*/
+ /*% ripper: if_mod!($3, $1) %*/
}
| stmt modifier_unless expr_value
{
/*%%%*/
- $$ = new_unless($3, remove_begin($1), 0);
+ $$ = new_unless(p, $3, remove_begin($1), 0, &@$);
fixpos($$, $3);
- /*%
- $$ = dispatch2(unless_mod, $3, $1);
- %*/
+ /*% %*/
+ /*% ripper: unless_mod!($3, $1) %*/
}
| stmt modifier_while expr_value
{
/*%%%*/
if ($1 && nd_type($1) == NODE_BEGIN) {
- $$ = NEW_WHILE(cond($3), $1->nd_body, 0);
+ $$ = NEW_WHILE(cond(p, $3, &@3), $1->nd_body, 0, &@$);
}
else {
- $$ = NEW_WHILE(cond($3), $1, 1);
+ $$ = NEW_WHILE(cond(p, $3, &@3), $1, 1, &@$);
}
- /*%
- $$ = dispatch2(while_mod, $3, $1);
- %*/
+ /*% %*/
+ /*% ripper: while_mod!($3, $1) %*/
}
| stmt modifier_until expr_value
{
/*%%%*/
if ($1 && nd_type($1) == NODE_BEGIN) {
- $$ = NEW_UNTIL(cond($3), $1->nd_body, 0);
+ $$ = NEW_UNTIL(cond(p, $3, &@3), $1->nd_body, 0, &@$);
}
else {
- $$ = NEW_UNTIL(cond($3), $1, 1);
+ $$ = NEW_UNTIL(cond(p, $3, &@3), $1, 1, &@$);
}
- /*%
- $$ = dispatch2(until_mod, $3, $1);
- %*/
+ /*% %*/
+ /*% ripper: until_mod!($3, $1) %*/
}
| stmt modifier_rescue stmt
{
/*%%%*/
- NODE *resq = NEW_RESBODY(0, remove_begin($3), 0);
- $$ = NEW_RESCUE(remove_begin($1), resq, 0);
- /*%
- $$ = dispatch2(rescue_mod, $1, $3);
- %*/
+ NODE *resq;
+ YYLTYPE loc = code_loc_gen(&@2, &@3);
+ resq = NEW_RESBODY(0, remove_begin($3), 0, &loc);
+ $$ = NEW_RESCUE(remove_begin($1), resq, 0, &@$);
+ /*% %*/
+ /*% ripper: rescue_mod!($1, $3) %*/
}
| keyword_END '{' compstmt '}'
{
- if (in_def || in_single) {
+ if (p->in_def) {
rb_warn0("END in method; use at_exit");
}
/*%%%*/
- $$ = NEW_POSTEXE(NEW_NODE(
- NODE_SCOPE, 0 /* tbl */, $3 /* body */, 0 /* args */));
- /*%
- $$ = dispatch1(END, $3);
- %*/
+ {
+ NODE *scope = NEW_NODE(
+ NODE_SCOPE, 0 /* tbl */, $3 /* body */, 0 /* args */, &@$);
+ $$ = NEW_POSTEXE(scope, &@$);
+ }
+ /*% %*/
+ /*% ripper: END!($3) %*/
}
| command_asgn
| mlhs '=' command_call
{
/*%%%*/
value_expr($3);
- $1->nd_value = $3;
- $$ = $1;
- /*%
- $$ = dispatch2(massign, $1, $3);
- %*/
+ $$ = node_assign(p, $1, $3, &@$);
+ /*% %*/
+ /*% ripper: massign!($1, $3) %*/
}
| lhs '=' mrhs
{
+ /*%%%*/
value_expr($3);
- $$ = node_assign($1, $3);
+ $$ = node_assign(p, $1, $3, &@$);
+ /*% %*/
+ /*% ripper: assign!($1, $3) %*/
}
| mlhs '=' mrhs_arg
{
/*%%%*/
- $1->nd_value = $3;
- $$ = $1;
- /*%
- $$ = dispatch2(massign, $1, $3);
- %*/
+ $$ = node_assign(p, $1, $3, &@$);
+ /*% %*/
+ /*% ripper: massign!($1, $3) %*/
}
| expr
;
command_asgn : lhs '=' command_rhs
{
- value_expr($3);
- $$ = node_assign($1, $3);
+ /*%%%*/
+ $$ = node_assign(p, $1, $3, &@$);
+ /*% %*/
+ /*% ripper: assign!($1, $3) %*/
}
| var_lhs tOP_ASGN command_rhs
{
- value_expr($3);
- $$ = new_op_assign($1, $2, $3);
+ /*%%%*/
+ $$ = new_op_assign(p, $1, $2, $3, &@$);
+ /*% %*/
+ /*% ripper: opassign!($1, $2, $3) %*/
}
| primary_value '[' opt_call_args rbracket tOP_ASGN command_rhs
{
/*%%%*/
- NODE *args;
+ $$ = new_ary_op_assign(p, $1, $3, $5, $6, &@3, &@$);
+ /*% %*/
+ /*% ripper: opassign!(aref_field!($1, escape_Qundef($3)), $5, $6) %*/
- value_expr($6);
- if (!$3) $3 = NEW_ZARRAY();
- args = arg_concat($3, $6);
- if ($5 == tOROP) {
- $5 = 0;
- }
- else if ($5 == tANDOP) {
- $5 = 1;
- }
- $$ = NEW_OP_ASGN1($1, $5, args);
- fixpos($$, $1);
- /*%
- $$ = dispatch2(aref_field, $1, escape_Qundef($3));
- $$ = dispatch3(opassign, $$, $5, $6);
- %*/
}
| primary_value call_op tIDENTIFIER tOP_ASGN command_rhs
{
- value_expr($5);
- $$ = new_attr_op_assign($1, $2, $3, $4, $5);
+ /*%%%*/
+ $$ = new_attr_op_assign(p, $1, $2, $3, $4, $5, &@$);
+ /*% %*/
+ /*% ripper: opassign!(field!($1, $2, $3), $4, $5) %*/
}
| primary_value call_op tCONSTANT tOP_ASGN command_rhs
{
- value_expr($5);
- $$ = new_attr_op_assign($1, $2, $3, $4, $5);
+ /*%%%*/
+ $$ = new_attr_op_assign(p, $1, $2, $3, $4, $5, &@$);
+ /*% %*/
+ /*% ripper: opassign!(field!($1, $2, $3), $4, $5) %*/
}
| primary_value tCOLON2 tCONSTANT tOP_ASGN command_rhs
{
- $$ = const_path_field($1, $3);
- $$ = new_const_op_assign($$, $4, $5);
+ /*%%%*/
+ YYLTYPE loc = code_loc_gen(&@1, &@3);
+ $$ = new_const_op_assign(p, NEW_COLON2($1, $3, &loc), $4, $5, &@$);
+ /*% %*/
+ /*% ripper: opassign!(const_path_field!($1, $3), $4, $5) %*/
}
| primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_rhs
{
- value_expr($5);
- $$ = new_attr_op_assign($1, ripper_id2sym(idCOLON2), $3, $4, $5);
+ /*%%%*/
+ $$ = new_attr_op_assign(p, $1, ID2VAL(idCOLON2), $3, $4, $5, &@$);
+ /*% %*/
+ /*% ripper: opassign!(field!($1, ID2VAL(idCOLON2), $3), $4, $5) %*/
}
| backref tOP_ASGN command_rhs
{
- $1 = var_field($1);
- $$ = backref_assign_error($1, node_assign($1, $3));
+ /*%%%*/
+ rb_backref_error(p, $1);
+ $$ = NEW_BEGIN(0, &@$);
+ /*% %*/
+ /*% ripper[error]: assign_error!(assign!(var_field(p, $1), $3)) %*/
}
;
command_rhs : command_call %prec tOP_ASGN
{
- /*%%%*/
value_expr($1);
$$ = $1;
- /*%
- %*/
}
| command_call modifier_rescue stmt
{
/*%%%*/
+ YYLTYPE loc = code_loc_gen(&@2, &@3);
value_expr($1);
- $$ = NEW_RESCUE($1, NEW_RESBODY(0, remove_begin($3), 0), 0);
- /*%
- $$ = dispatch2(rescue_mod, $1, $3);
- %*/
+ $$ = NEW_RESCUE($1, NEW_RESBODY(0, remove_begin($3), 0, &loc), 0, &@$);
+ /*% %*/
+ /*% ripper: rescue_mod!($1, $3) %*/
}
| command_asgn
;
@@ -1400,51 +1290,36 @@ command_rhs : command_call %prec tOP_ASGN
expr : command_call
| expr keyword_and expr
{
- /*%%%*/
- $$ = logop(NODE_AND, $1, $3);
- /*%
- $$ = dispatch3(binary, $1, ripper_intern("and"), $3);
- %*/
+ $$ = logop(p, idAND, $1, $3, &@2, &@$);
}
| expr keyword_or expr
{
- /*%%%*/
- $$ = logop(NODE_OR, $1, $3);
- /*%
- $$ = dispatch3(binary, $1, ripper_intern("or"), $3);
- %*/
+ $$ = logop(p, idOR, $1, $3, &@2, &@$);
}
| keyword_not opt_nl expr
{
- /*%%%*/
- $$ = call_uni_op(method_cond($3), '!');
- /*%
- $$ = dispatch2(unary, ripper_intern("not"), $3);
- %*/
+ $$ = call_uni_op(p, method_cond(p, $3, &@3), METHOD_NOT, &@1, &@$);
}
| '!' command_call
{
- /*%%%*/
- $$ = call_uni_op(method_cond($2), '!');
- /*%
- $$ = dispatch2(unary, ripper_id2sym('!'), $2);
- %*/
+ $$ = call_uni_op(p, method_cond(p, $2, &@2), '!', &@1, &@$);
}
| arg
;
expr_value : expr
{
- /*%%%*/
value_expr($1);
$$ = $1;
- if (!$$) $$ = NEW_NIL();
- /*%
- $$ = $1;
- %*/
}
;
+expr_value_do : {COND_PUSH(1);} expr_value do {COND_POP();}
+ {
+ $$ = $2;
+ }
+
+
command_call : command
| block_command
;
@@ -1453,26 +1328,18 @@ block_command : block_call
| block_call call_op2 operation2 command_args
{
/*%%%*/
- $$ = NEW_QCALL($2, $1, $3, $4);
- /*%
- $$ = dispatch3(call, $1, $2, $3);
- $$ = method_arg($$, $4);
- %*/
+ $$ = new_qcall(p, $2, $1, $3, $4, &@3, &@$);
+ /*% %*/
+ /*% ripper: method_add_arg!(call!($1, $2, $3), $4) %*/
}
;
-cmd_brace_block : tLBRACE_ARG
+cmd_brace_block : tLBRACE_ARG brace_body '}'
{
+ $$ = $2;
/*%%%*/
- $<num>$ = ruby_sourceline;
- /*%
- %*/
- }
- brace_body '}'
- {
- $$ = $3;
- /*%%%*/
- nd_set_line($$, $<num>2);
+ $$->nd_body->nd_loc = code_loc_gen(&@1, &@3);
+ nd_set_line($$, @1.end_pos.lineno);
/*% %*/
}
;
@@ -1480,118 +1347,97 @@ cmd_brace_block : tLBRACE_ARG
fcall : operation
{
/*%%%*/
- $$ = NEW_FCALL($1, 0);
- nd_set_line($$, tokline);
- /*%
- %*/
+ $$ = NEW_FCALL($1, 0, &@$);
+ nd_set_line($$, p->tokline);
+ /*% %*/
+ /*% ripper: $1 %*/
}
;
command : fcall command_args %prec tLOWEST
{
/*%%%*/
+ $1->nd_args = $2;
+ nd_set_last_loc($1, @2.end_pos);
$$ = $1;
- $$->nd_args = $2;
- /*%
- $$ = dispatch2(command, $1, $2);
- %*/
+ /*% %*/
+ /*% ripper: command!($1, $2) %*/
}
| fcall command_args cmd_brace_block
{
/*%%%*/
- block_dup_check($2,$3);
+ block_dup_check(p, $2, $3);
$1->nd_args = $2;
- $3->nd_iter = $1;
- $$ = $3;
+ $$ = method_add_block(p, $1, $3, &@$);
fixpos($$, $1);
- /*%
- $$ = dispatch2(command, $1, $2);
- $$ = method_add_block($$, $3);
- %*/
+ nd_set_last_loc($1, @2.end_pos);
+ /*% %*/
+ /*% ripper: method_add_block!(command!($1, $2), $3) %*/
}
| primary_value call_op operation2 command_args %prec tLOWEST
{
/*%%%*/
- $$ = NEW_QCALL($2, $1, $3, $4);
- fixpos($$, $1);
- /*%
- $$ = dispatch4(command_call, $1, $2, $3, $4);
- %*/
+ $$ = new_command_qcall(p, $2, $1, $3, $4, Qnull, &@3, &@$);
+ /*% %*/
+ /*% ripper: command_call!($1, $2, $3, $4) %*/
}
| primary_value call_op operation2 command_args cmd_brace_block
{
/*%%%*/
- block_dup_check($4,$5);
- $5->nd_iter = NEW_QCALL($2, $1, $3, $4);
- $$ = $5;
- fixpos($$, $1);
- /*%
- $$ = dispatch4(command_call, $1, $2, $3, $4);
- $$ = method_add_block($$, $5);
- %*/
- }
+ $$ = new_command_qcall(p, $2, $1, $3, $4, $5, &@3, &@$);
+ /*% %*/
+ /*% ripper: method_add_block!(command_call!($1, $2, $3, $4), $5) %*/
+ }
| primary_value tCOLON2 operation2 command_args %prec tLOWEST
{
/*%%%*/
- $$ = NEW_CALL($1, $3, $4);
- fixpos($$, $1);
- /*%
- $$ = dispatch4(command_call, $1, ID2SYM(idCOLON2), $3, $4);
- %*/
+ $$ = new_command_qcall(p, ID2VAL(idCOLON2), $1, $3, $4, Qnull, &@3, &@$);
+ /*% %*/
+ /*% ripper: command_call!($1, ID2VAL(idCOLON2), $3, $4) %*/
}
| primary_value tCOLON2 operation2 command_args cmd_brace_block
{
/*%%%*/
- block_dup_check($4,$5);
- $5->nd_iter = NEW_CALL($1, $3, $4);
- $$ = $5;
- fixpos($$, $1);
- /*%
- $$ = dispatch4(command_call, $1, ID2SYM(idCOLON2), $3, $4);
- $$ = method_add_block($$, $5);
- %*/
+ $$ = new_command_qcall(p, ID2VAL(idCOLON2), $1, $3, $4, $5, &@3, &@$);
+ /*% %*/
+ /*% ripper: method_add_block!(command_call!($1, ID2VAL(idCOLON2), $3, $4), $5) %*/
}
| keyword_super command_args
{
/*%%%*/
- $$ = NEW_SUPER($2);
+ $$ = NEW_SUPER($2, &@$);
fixpos($$, $2);
- /*%
- $$ = dispatch1(super, $2);
- %*/
+ /*% %*/
+ /*% ripper: super!($2) %*/
}
| keyword_yield command_args
{
/*%%%*/
- $$ = new_yield($2);
+ $$ = new_yield(p, $2, &@$);
fixpos($$, $2);
- /*%
- $$ = dispatch1(yield, $2);
- %*/
+ /*% %*/
+ /*% ripper: yield!($2) %*/
}
- | keyword_return call_args
+ | k_return call_args
{
/*%%%*/
- $$ = NEW_RETURN(ret_args($2));
- /*%
- $$ = dispatch1(return, $2);
- %*/
+ $$ = NEW_RETURN(ret_args(p, $2), &@$);
+ /*% %*/
+ /*% ripper: return!($2) %*/
}
| keyword_break call_args
{
/*%%%*/
- $$ = NEW_BREAK(ret_args($2));
- /*%
- $$ = dispatch1(break, $2);
- %*/
+ $$ = NEW_BREAK(ret_args(p, $2), &@$);
+ /*% %*/
+ /*% ripper: break!($2) %*/
}
| keyword_next call_args
{
/*%%%*/
- $$ = NEW_NEXT(ret_args($2));
- /*%
- $$ = dispatch1(next, $2);
- %*/
+ $$ = NEW_NEXT(ret_args(p, $2), &@$);
+ /*% %*/
+ /*% ripper: next!($2) %*/
}
;
@@ -1600,9 +1446,8 @@ mlhs : mlhs_basic
{
/*%%%*/
$$ = $2;
- /*%
- $$ = dispatch1(mlhs_paren, $2);
- %*/
+ /*% %*/
+ /*% ripper: mlhs_paren!($2) %*/
}
;
@@ -1610,96 +1455,81 @@ mlhs_inner : mlhs_basic
| tLPAREN mlhs_inner rparen
{
/*%%%*/
- $$ = NEW_MASGN(NEW_LIST($2), 0);
- /*%
- $$ = dispatch1(mlhs_paren, $2);
- %*/
+ $$ = NEW_MASGN(NEW_LIST($2, &@$), 0, &@$);
+ /*% %*/
+ /*% ripper: mlhs_paren!($2) %*/
}
;
mlhs_basic : mlhs_head
{
/*%%%*/
- $$ = NEW_MASGN($1, 0);
- /*%
- $$ = $1;
- %*/
+ $$ = NEW_MASGN($1, 0, &@$);
+ /*% %*/
+ /*% ripper: $1 %*/
}
| mlhs_head mlhs_item
{
/*%%%*/
- $$ = NEW_MASGN(list_append($1,$2), 0);
- /*%
- $$ = mlhs_add($1, $2);
- %*/
+ $$ = NEW_MASGN(list_append(p, $1,$2), 0, &@$);
+ /*% %*/
+ /*% ripper: mlhs_add!($1, $2) %*/
}
| mlhs_head tSTAR mlhs_node
{
/*%%%*/
- $$ = NEW_MASGN($1, $3);
- /*%
- $$ = mlhs_add_star($1, $3);
- %*/
+ $$ = NEW_MASGN($1, $3, &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_star!($1, $3) %*/
}
| mlhs_head tSTAR mlhs_node ',' mlhs_post
{
/*%%%*/
- $$ = NEW_MASGN($1, NEW_POSTARG($3,$5));
- /*%
- $1 = mlhs_add_star($1, $3);
- $$ = mlhs_add($1, $5);
- %*/
+ $$ = NEW_MASGN($1, NEW_POSTARG($3,$5,&@$), &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_post!(mlhs_add_star!($1, $3), $5) %*/
}
| mlhs_head tSTAR
{
/*%%%*/
- $$ = NEW_MASGN($1, -1);
- /*%
- $$ = mlhs_add_star($1, Qnil);
- %*/
+ $$ = NEW_MASGN($1, NODE_SPECIAL_NO_NAME_REST, &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_star!($1, Qnil) %*/
}
| mlhs_head tSTAR ',' mlhs_post
{
/*%%%*/
- $$ = NEW_MASGN($1, NEW_POSTARG(-1, $4));
- /*%
- $1 = mlhs_add_star($1, Qnil);
- $$ = mlhs_add($1, $4);
- %*/
+ $$ = NEW_MASGN($1, NEW_POSTARG(NODE_SPECIAL_NO_NAME_REST, $4, &@$), &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_post!(mlhs_add_star!($1, Qnil), $4) %*/
}
| tSTAR mlhs_node
{
/*%%%*/
- $$ = NEW_MASGN(0, $2);
- /*%
- $$ = mlhs_add_star(mlhs_new(), $2);
- %*/
+ $$ = NEW_MASGN(0, $2, &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_star!(mlhs_new!, $2) %*/
}
| tSTAR mlhs_node ',' mlhs_post
{
/*%%%*/
- $$ = NEW_MASGN(0, NEW_POSTARG($2,$4));
- /*%
- $2 = mlhs_add_star(mlhs_new(), $2);
- $$ = mlhs_add($2, $4);
- %*/
+ $$ = NEW_MASGN(0, NEW_POSTARG($2,$4,&@$), &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_post!(mlhs_add_star!(mlhs_new!, $2), $4) %*/
}
| tSTAR
{
/*%%%*/
- $$ = NEW_MASGN(0, -1);
- /*%
- $$ = mlhs_add_star(mlhs_new(), Qnil);
- %*/
+ $$ = NEW_MASGN(0, NODE_SPECIAL_NO_NAME_REST, &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_star!(mlhs_new!, Qnil) %*/
}
| tSTAR ',' mlhs_post
{
/*%%%*/
- $$ = NEW_MASGN(0, NEW_POSTARG(-1, $3));
- /*%
- $$ = mlhs_add_star(mlhs_new(), Qnil);
- $$ = mlhs_add($$, $3);
- %*/
+ $$ = NEW_MASGN(0, NEW_POSTARG(NODE_SPECIAL_NO_NAME_REST, $3, &@$), &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_post!(mlhs_add_star!(mlhs_new!, Qnil), $3) %*/
}
;
@@ -1708,176 +1538,181 @@ mlhs_item : mlhs_node
{
/*%%%*/
$$ = $2;
- /*%
- $$ = dispatch1(mlhs_paren, $2);
- %*/
+ /*% %*/
+ /*% ripper: mlhs_paren!($2) %*/
}
;
mlhs_head : mlhs_item ','
{
/*%%%*/
- $$ = NEW_LIST($1);
- /*%
- $$ = mlhs_add(mlhs_new(), $1);
- %*/
+ $$ = NEW_LIST($1, &@1);
+ /*% %*/
+ /*% ripper: mlhs_add!(mlhs_new!, $1) %*/
}
| mlhs_head mlhs_item ','
{
/*%%%*/
- $$ = list_append($1, $2);
- /*%
- $$ = mlhs_add($1, $2);
- %*/
+ $$ = list_append(p, $1, $2);
+ /*% %*/
+ /*% ripper: mlhs_add!($1, $2) %*/
}
;
mlhs_post : mlhs_item
{
/*%%%*/
- $$ = NEW_LIST($1);
- /*%
- $$ = mlhs_add(mlhs_new(), $1);
- %*/
+ $$ = NEW_LIST($1, &@$);
+ /*% %*/
+ /*% ripper: mlhs_add!(mlhs_new!, $1) %*/
}
| mlhs_post ',' mlhs_item
{
/*%%%*/
- $$ = list_append($1, $3);
- /*%
- $$ = mlhs_add($1, $3);
- %*/
+ $$ = list_append(p, $1, $3);
+ /*% %*/
+ /*% ripper: mlhs_add!($1, $3) %*/
}
;
mlhs_node : user_variable
{
- $$ = assignable($1, 0);
+ /*%%%*/
+ $$ = assignable(p, $1, 0, &@$);
+ /*% %*/
+ /*% ripper: assignable(p, var_field(p, $1)) %*/
}
| keyword_variable
{
- $$ = assignable($1, 0);
+ /*%%%*/
+ $$ = assignable(p, $1, 0, &@$);
+ /*% %*/
+ /*% ripper: assignable(p, var_field(p, $1)) %*/
}
| primary_value '[' opt_call_args rbracket
{
/*%%%*/
- $$ = aryset($1, $3);
- /*%
- $$ = dispatch2(aref_field, $1, escape_Qundef($3));
- %*/
+ $$ = aryset(p, $1, $3, &@$);
+ /*% %*/
+ /*% ripper: aref_field!($1, escape_Qundef($3)) %*/
}
| primary_value call_op tIDENTIFIER
{
/*%%%*/
- $$ = attrset($1, $2, $3);
- /*%
- $$ = dispatch3(field, $1, $2, $3);
- %*/
+ $$ = attrset(p, $1, $2, $3, &@$);
+ /*% %*/
+ /*% ripper: field!($1, $2, $3) %*/
}
| primary_value tCOLON2 tIDENTIFIER
{
/*%%%*/
- $$ = attrset($1, idCOLON2, $3);
- /*%
- $$ = dispatch2(const_path_field, $1, $3);
- %*/
+ $$ = attrset(p, $1, idCOLON2, $3, &@$);
+ /*% %*/
+ /*% ripper: const_path_field!($1, $3) %*/
}
| primary_value call_op tCONSTANT
{
/*%%%*/
- $$ = attrset($1, $2, $3);
- /*%
- $$ = dispatch3(field, $1, $2, $3);
- %*/
+ $$ = attrset(p, $1, $2, $3, &@$);
+ /*% %*/
+ /*% ripper: field!($1, $2, $3) %*/
}
| primary_value tCOLON2 tCONSTANT
{
- $$ = const_decl(const_path_field($1, $3));
+ /*%%%*/
+ $$ = const_decl(p, NEW_COLON2($1, $3, &@$), &@$);
+ /*% %*/
+ /*% ripper: const_decl(p, const_path_field!($1, $3)) %*/
}
| tCOLON3 tCONSTANT
{
- $$ = const_decl(top_const_field($2));
+ /*%%%*/
+ $$ = const_decl(p, NEW_COLON3($2, &@$), &@$);
+ /*% %*/
+ /*% ripper: const_decl(p, top_const_field!($2)) %*/
}
| backref
{
- $1 = var_field($1);
- $$ = backref_assign_error($1, $1);
+ /*%%%*/
+ rb_backref_error(p, $1);
+ $$ = NEW_BEGIN(0, &@$);
+ /*% %*/
+ /*% ripper[error]: assign_error!(var_field(p, $1)) %*/
}
;
lhs : user_variable
{
- $$ = assignable($1, 0);
/*%%%*/
- if (!$$) $$ = NEW_BEGIN(0);
- /*%
- $$ = dispatch1(var_field, $$);
- %*/
+ $$ = assignable(p, $1, 0, &@$);
+ /*% %*/
+ /*% ripper: assignable(p, var_field(p, $1)) %*/
}
| keyword_variable
{
- $$ = assignable($1, 0);
/*%%%*/
- if (!$$) $$ = NEW_BEGIN(0);
- /*%
- $$ = dispatch1(var_field, $$);
- %*/
+ $$ = assignable(p, $1, 0, &@$);
+ /*% %*/
+ /*% ripper: assignable(p, var_field(p, $1)) %*/
}
| primary_value '[' opt_call_args rbracket
{
/*%%%*/
- $$ = aryset($1, $3);
- /*%
- $$ = dispatch2(aref_field, $1, escape_Qundef($3));
- %*/
+ $$ = aryset(p, $1, $3, &@$);
+ /*% %*/
+ /*% ripper: aref_field!($1, escape_Qundef($3)) %*/
}
| primary_value call_op tIDENTIFIER
{
/*%%%*/
- $$ = attrset($1, $2, $3);
- /*%
- $$ = dispatch3(field, $1, $2, $3);
- %*/
+ $$ = attrset(p, $1, $2, $3, &@$);
+ /*% %*/
+ /*% ripper: field!($1, $2, $3) %*/
}
| primary_value tCOLON2 tIDENTIFIER
{
/*%%%*/
- $$ = attrset($1, idCOLON2, $3);
- /*%
- $$ = dispatch3(field, $1, ID2SYM(idCOLON2), $3);
- %*/
+ $$ = attrset(p, $1, idCOLON2, $3, &@$);
+ /*% %*/
+ /*% ripper: field!($1, ID2VAL(idCOLON2), $3) %*/
}
| primary_value call_op tCONSTANT
{
/*%%%*/
- $$ = attrset($1, $2, $3);
- /*%
- $$ = dispatch3(field, $1, $2, $3);
- %*/
+ $$ = attrset(p, $1, $2, $3, &@$);
+ /*% %*/
+ /*% ripper: field!($1, $2, $3) %*/
}
| primary_value tCOLON2 tCONSTANT
{
- $$ = const_decl(const_path_field($1, $3));
+ /*%%%*/
+ $$ = const_decl(p, NEW_COLON2($1, $3, &@$), &@$);
+ /*% %*/
+ /*% ripper: const_decl(p, const_path_field!($1, $3)) %*/
}
| tCOLON3 tCONSTANT
{
- $$ = const_decl(top_const_field($2));
+ /*%%%*/
+ $$ = const_decl(p, NEW_COLON3($2, &@$), &@$);
+ /*% %*/
+ /*% ripper: const_decl(p, top_const_field!($2)) %*/
}
| backref
{
- $1 = var_field($1);
- $$ = backref_assign_error($1, $1);
+ /*%%%*/
+ rb_backref_error(p, $1);
+ $$ = NEW_BEGIN(0, &@$);
+ /*% %*/
+ /*% ripper[error]: assign_error!(var_field(p, $1)) %*/
}
;
cname : tIDENTIFIER
{
/*%%%*/
- yyerror("class/module name must be CONSTANT");
- /*%
- $$ = dispatch1(class_name_error, $1);
- ripper_error();
- %*/
+ yyerror1(&@1, "class/module name must be CONSTANT");
+ /*% %*/
+ /*% ripper[error]: class_name_error!($1) %*/
}
| tCONSTANT
;
@@ -1885,26 +1720,23 @@ cname : tIDENTIFIER
cpath : tCOLON3 cname
{
/*%%%*/
- $$ = NEW_COLON3($2);
- /*%
- $$ = dispatch1(top_const_ref, $2);
- %*/
+ $$ = NEW_COLON3($2, &@$);
+ /*% %*/
+ /*% ripper: top_const_ref!($2) %*/
}
| cname
{
/*%%%*/
- $$ = NEW_COLON2(0, $$);
- /*%
- $$ = dispatch1(const_ref, $1);
- %*/
+ $$ = NEW_COLON2(0, $$, &@$);
+ /*% %*/
+ /*% ripper: const_ref!($1) %*/
}
| primary_value tCOLON2 cname
{
/*%%%*/
- $$ = NEW_COLON2($1, $3);
- /*%
- $$ = dispatch2(const_path_ref, $1, $3);
- %*/
+ $$ = NEW_COLON2($1, $3, &@$);
+ /*% %*/
+ /*% ripper: const_path_ref!($1, $3) %*/
}
;
@@ -1919,11 +1751,7 @@ fname : tIDENTIFIER
| reswords
{
SET_LEX_STATE(EXPR_ENDFN);
- /*%%%*/
- $$ = $<id>1;
- /*%
$$ = $1;
- %*/
}
;
@@ -1934,10 +1762,9 @@ fsym : fname
fitem : fsym
{
/*%%%*/
- $$ = NEW_LIT(ID2SYM($1));
- /*%
- $$ = dispatch1(symbol_literal, $1);
- %*/
+ $$ = NEW_LIT(ID2SYM($1), &@$);
+ /*% %*/
+ /*% ripper: symbol_literal!($1) %*/
}
| dsym
;
@@ -1945,18 +1772,17 @@ fitem : fsym
undef_list : fitem
{
/*%%%*/
- $$ = NEW_UNDEF($1);
- /*%
- $$ = rb_ary_new3(1, $1);
- %*/
+ $$ = NEW_UNDEF($1, &@$);
+ /*% %*/
+ /*% ripper: rb_ary_new3(1, get_value($1)) %*/
}
| undef_list ',' {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fitem
{
/*%%%*/
- $$ = block_append($1, NEW_UNDEF($4));
- /*%
- rb_ary_push($1, $4);
- %*/
+ NODE *undef = NEW_UNDEF($4, &@4);
+ $$ = block_append(p, $1, undef);
+ /*% %*/
+ /*% ripper: rb_ary_push($1, get_value($4)) %*/
}
;
@@ -2008,337 +1834,225 @@ reswords : keyword__LINE__ | keyword__FILE__ | keyword__ENCODING__
arg : lhs '=' arg_rhs
{
- $$ = node_assign($1, $3);
+ /*%%%*/
+ $$ = node_assign(p, $1, $3, &@$);
+ /*% %*/
+ /*% ripper: assign!($1, $3) %*/
}
| var_lhs tOP_ASGN arg_rhs
{
- $$ = new_op_assign($1, $2, $3);
+ /*%%%*/
+ $$ = new_op_assign(p, $1, $2, $3, &@$);
+ /*% %*/
+ /*% ripper: opassign!($1, $2, $3) %*/
}
| primary_value '[' opt_call_args rbracket tOP_ASGN arg_rhs
{
/*%%%*/
- NODE *args;
-
value_expr($6);
- if (!$3) $3 = NEW_ZARRAY();
- if (nd_type($3) == NODE_BLOCK_PASS) {
- args = NEW_ARGSCAT($3, $6);
- }
- else {
- args = arg_concat($3, $6);
- }
- if ($5 == tOROP) {
- $5 = 0;
- }
- else if ($5 == tANDOP) {
- $5 = 1;
- }
- $$ = NEW_OP_ASGN1($1, $5, args);
- fixpos($$, $1);
- /*%
- $1 = dispatch2(aref_field, $1, escape_Qundef($3));
- $$ = dispatch3(opassign, $1, $5, $6);
- %*/
+ $$ = new_ary_op_assign(p, $1, $3, $5, $6, &@3, &@$);
+ /*% %*/
+ /*% ripper: opassign!(aref_field!($1, escape_Qundef($3)), $5, $6) %*/
}
| primary_value call_op tIDENTIFIER tOP_ASGN arg_rhs
{
+ /*%%%*/
value_expr($5);
- $$ = new_attr_op_assign($1, $2, $3, $4, $5);
+ $$ = new_attr_op_assign(p, $1, $2, $3, $4, $5, &@$);
+ /*% %*/
+ /*% ripper: opassign!(field!($1, $2, $3), $4, $5) %*/
}
| primary_value call_op tCONSTANT tOP_ASGN arg_rhs
{
+ /*%%%*/
value_expr($5);
- $$ = new_attr_op_assign($1, $2, $3, $4, $5);
+ $$ = new_attr_op_assign(p, $1, $2, $3, $4, $5, &@$);
+ /*% %*/
+ /*% ripper: opassign!(field!($1, $2, $3), $4, $5) %*/
}
| primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg_rhs
{
+ /*%%%*/
value_expr($5);
- $$ = new_attr_op_assign($1, ripper_id2sym(idCOLON2), $3, $4, $5);
+ $$ = new_attr_op_assign(p, $1, ID2VAL(idCOLON2), $3, $4, $5, &@$);
+ /*% %*/
+ /*% ripper: opassign!(field!($1, ID2VAL(idCOLON2), $3), $4, $5) %*/
}
| primary_value tCOLON2 tCONSTANT tOP_ASGN arg_rhs
{
- $$ = const_path_field($1, $3);
- $$ = new_const_op_assign($$, $4, $5);
+ /*%%%*/
+ YYLTYPE loc = code_loc_gen(&@1, &@3);
+ $$ = new_const_op_assign(p, NEW_COLON2($1, $3, &loc), $4, $5, &@$);
+ /*% %*/
+ /*% ripper: opassign!(const_path_field!($1, $3), $4, $5) %*/
}
| tCOLON3 tCONSTANT tOP_ASGN arg_rhs
{
- $$ = top_const_field($2);
- $$ = new_const_op_assign($$, $3, $4);
+ /*%%%*/
+ $$ = new_const_op_assign(p, NEW_COLON3($2, &@$), $3, $4, &@$);
+ /*% %*/
+ /*% ripper: opassign!(top_const_field!($2), $3, $4) %*/
}
| backref tOP_ASGN arg_rhs
{
- $1 = var_field($1);
- $$ = backref_assign_error($1, new_op_assign($1, $2, $3));
+ /*%%%*/
+ rb_backref_error(p, $1);
+ $$ = NEW_BEGIN(0, &@$);
+ /*% %*/
+ /*% ripper[error]: assign_error!(opassign!(var_field(p, $1), $2, $3)) %*/
}
| arg tDOT2 arg
{
/*%%%*/
value_expr($1);
value_expr($3);
- $$ = NEW_DOT2($1, $3);
- /*%
- $$ = dispatch2(dot2, $1, $3);
- %*/
+ $$ = NEW_DOT2($1, $3, &@$);
+ /*% %*/
+ /*% ripper: dot2!($1, $3) %*/
}
| arg tDOT3 arg
{
/*%%%*/
value_expr($1);
value_expr($3);
- $$ = NEW_DOT3($1, $3);
- /*%
- $$ = dispatch2(dot3, $1, $3);
- %*/
+ $$ = NEW_DOT3($1, $3, &@$);
+ /*% %*/
+ /*% ripper: dot3!($1, $3) %*/
}
- | arg '+' arg
+ | arg tDOT2
{
/*%%%*/
- $$ = call_bin_op($1, '+', $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM('+'), $3);
- %*/
+ YYLTYPE loc;
+ loc.beg_pos = @2.end_pos;
+ loc.end_pos = @2.end_pos;
+
+ value_expr($1);
+ $$ = NEW_DOT2($1, new_nil(&loc), &@$);
+ /*% %*/
+ /*% ripper: dot2!($1, Qnil) %*/
}
- | arg '-' arg
+ | arg tDOT3
{
/*%%%*/
- $$ = call_bin_op($1, '-', $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM('-'), $3);
- %*/
+ YYLTYPE loc;
+ loc.beg_pos = @2.end_pos;
+ loc.end_pos = @2.end_pos;
+
+ value_expr($1);
+ $$ = NEW_DOT3($1, new_nil(&loc), &@$);
+ /*% %*/
+ /*% ripper: dot3!($1, Qnil) %*/
+ }
+ | arg '+' arg
+ {
+ $$ = call_bin_op(p, $1, '+', $3, &@2, &@$);
+ }
+ | arg '-' arg
+ {
+ $$ = call_bin_op(p, $1, '-', $3, &@2, &@$);
}
| arg '*' arg
{
- /*%%%*/
- $$ = call_bin_op($1, '*', $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM('*'), $3);
- %*/
+ $$ = call_bin_op(p, $1, '*', $3, &@2, &@$);
}
| arg '/' arg
{
- /*%%%*/
- $$ = call_bin_op($1, '/', $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM('/'), $3);
- %*/
+ $$ = call_bin_op(p, $1, '/', $3, &@2, &@$);
}
| arg '%' arg
{
- /*%%%*/
- $$ = call_bin_op($1, '%', $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM('%'), $3);
- %*/
+ $$ = call_bin_op(p, $1, '%', $3, &@2, &@$);
}
| arg tPOW arg
{
- /*%%%*/
- $$ = call_bin_op($1, tPOW, $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM(idPow), $3);
- %*/
+ $$ = call_bin_op(p, $1, idPow, $3, &@2, &@$);
}
| tUMINUS_NUM simple_numeric tPOW arg
{
- /*%%%*/
- $$ = NEW_CALL(call_bin_op($2, tPOW, $4), tUMINUS, 0);
- /*%
- $$ = dispatch3(binary, $2, ID2SYM(idPow), $4);
- $$ = dispatch2(unary, ID2SYM(idUMinus), $$);
- %*/
+ $$ = call_uni_op(p, call_bin_op(p, $2, idPow, $4, &@2, &@$), idUMinus, &@1, &@$);
}
| tUPLUS arg
{
- /*%%%*/
- $$ = call_uni_op($2, tUPLUS);
- /*%
- $$ = dispatch2(unary, ID2SYM(idUPlus), $2);
- %*/
+ $$ = call_uni_op(p, $2, idUPlus, &@1, &@$);
}
| tUMINUS arg
{
- /*%%%*/
- $$ = call_uni_op($2, tUMINUS);
- /*%
- $$ = dispatch2(unary, ID2SYM(idUMinus), $2);
- %*/
+ $$ = call_uni_op(p, $2, idUMinus, &@1, &@$);
}
| arg '|' arg
{
- /*%%%*/
- $$ = call_bin_op($1, '|', $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM('|'), $3);
- %*/
+ $$ = call_bin_op(p, $1, '|', $3, &@2, &@$);
}
| arg '^' arg
{
- /*%%%*/
- $$ = call_bin_op($1, '^', $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM('^'), $3);
- %*/
+ $$ = call_bin_op(p, $1, '^', $3, &@2, &@$);
}
| arg '&' arg
{
- /*%%%*/
- $$ = call_bin_op($1, '&', $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM('&'), $3);
- %*/
+ $$ = call_bin_op(p, $1, '&', $3, &@2, &@$);
}
| arg tCMP arg
{
- /*%%%*/
- $$ = call_bin_op($1, tCMP, $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM(idCmp), $3);
- %*/
- }
- | arg '>' arg
- {
- /*%%%*/
- $$ = call_bin_op($1, '>', $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM('>'), $3);
- %*/
- }
- | arg tGEQ arg
- {
- /*%%%*/
- $$ = call_bin_op($1, tGEQ, $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM(idGE), $3);
- %*/
- }
- | arg '<' arg
- {
- /*%%%*/
- $$ = call_bin_op($1, '<', $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM('<'), $3);
- %*/
- }
- | arg tLEQ arg
- {
- /*%%%*/
- $$ = call_bin_op($1, tLEQ, $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM(idLE), $3);
- %*/
+ $$ = call_bin_op(p, $1, idCmp, $3, &@2, &@$);
}
+ | rel_expr %prec tCMP
| arg tEQ arg
{
- /*%%%*/
- $$ = call_bin_op($1, tEQ, $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM(idEq), $3);
- %*/
+ $$ = call_bin_op(p, $1, idEq, $3, &@2, &@$);
}
| arg tEQQ arg
{
- /*%%%*/
- $$ = call_bin_op($1, tEQQ, $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM(idEqq), $3);
- %*/
+ $$ = call_bin_op(p, $1, idEqq, $3, &@2, &@$);
}
| arg tNEQ arg
{
- /*%%%*/
- $$ = call_bin_op($1, tNEQ, $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM(idNeq), $3);
- %*/
+ $$ = call_bin_op(p, $1, idNeq, $3, &@2, &@$);
}
| arg tMATCH arg
{
- /*%%%*/
- $$ = match_op($1, $3);
- if (nd_type($1) == NODE_LIT) {
- VALUE lit = $1->nd_lit;
- if (RB_TYPE_P(lit, T_REGEXP)) {
- $$->nd_args = reg_named_capture_assign(lit);
- }
- }
- /*%
- $$ = dispatch3(binary, $1, ID2SYM(idEqTilde), $3);
- %*/
+ $$ = match_op(p, $1, $3, &@2, &@$);
}
| arg tNMATCH arg
{
- /*%%%*/
- $$ = call_bin_op($1, tNMATCH, $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM(idNeqTilde), $3);
- %*/
+ $$ = call_bin_op(p, $1, idNeqTilde, $3, &@2, &@$);
}
| '!' arg
{
- /*%%%*/
- $$ = call_uni_op(method_cond($2), '!');
- /*%
- $$ = dispatch2(unary, ID2SYM('!'), $2);
- %*/
+ $$ = call_uni_op(p, method_cond(p, $2, &@2), '!', &@1, &@$);
}
| '~' arg
{
- /*%%%*/
- $$ = call_uni_op($2, '~');
- /*%
- $$ = dispatch2(unary, ID2SYM('~'), $2);
- %*/
+ $$ = call_uni_op(p, $2, '~', &@1, &@$);
}
| arg tLSHFT arg
{
- /*%%%*/
- $$ = call_bin_op($1, tLSHFT, $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM(idLTLT), $3);
- %*/
+ $$ = call_bin_op(p, $1, idLTLT, $3, &@2, &@$);
}
| arg tRSHFT arg
{
- /*%%%*/
- $$ = call_bin_op($1, tRSHFT, $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM(idGTGT), $3);
- %*/
+ $$ = call_bin_op(p, $1, idGTGT, $3, &@2, &@$);
}
| arg tANDOP arg
{
- /*%%%*/
- $$ = logop(NODE_AND, $1, $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM(idANDOP), $3);
- %*/
+ $$ = logop(p, idANDOP, $1, $3, &@2, &@$);
}
| arg tOROP arg
{
- /*%%%*/
- $$ = logop(NODE_OR, $1, $3);
- /*%
- $$ = dispatch3(binary, $1, ID2SYM(idOROP), $3);
- %*/
+ $$ = logop(p, idOROP, $1, $3, &@2, &@$);
}
- | keyword_defined opt_nl {in_defined = 1;} arg
+ | keyword_defined opt_nl {p->in_defined = 1;} arg
{
- in_defined = 0;
- /*%%%*/
- $$ = new_defined($4);
- /*%
- $$ = dispatch1(defined, $4);
- %*/
+ p->in_defined = 0;
+ $$ = new_defined(p, $4, &@$);
}
| arg '?' arg opt_nl ':' arg
{
/*%%%*/
value_expr($1);
- $$ = new_if($1, $3, $6);
+ $$ = new_if(p, $1, $3, $6, &@$);
fixpos($$, $1);
- /*%
- $$ = dispatch3(ifop, $1, $3, $6);
- %*/
+ /*% %*/
+ /*% ripper: ifop!($1, $3, $6) %*/
}
| primary
{
@@ -2346,15 +2060,27 @@ arg : lhs '=' arg_rhs
}
;
+relop : '>' {$$ = '>';}
+ | '<' {$$ = '<';}
+ | tGEQ {$$ = idGE;}
+ | tLEQ {$$ = idLE;}
+ ;
+
+rel_expr : arg relop arg %prec '>'
+ {
+ $$ = call_bin_op(p, $1, $2, $3, &@2, &@$);
+ }
+ | rel_expr relop arg %prec '>'
+ {
+ rb_warning1("comparison '%s' after comparison", WARN_ID($2));
+ $$ = call_bin_op(p, $1, $2, $3, &@2, &@$);
+ }
+ ;
+
arg_value : arg
{
- /*%%%*/
value_expr($1);
$$ = $1;
- if (!$$) $$ = NEW_NIL();
- /*%
- $$ = $1;
- %*/
}
;
@@ -2366,37 +2092,32 @@ aref_args : none
| args ',' assocs trailer
{
/*%%%*/
- $$ = $3 ? arg_append($1, new_hash($3)) : $1;
- /*%
- $$ = arg_add_assocs($1, $3);
- %*/
+ $$ = $3 ? arg_append(p, $1, new_hash(p, $3, &@3), &@$) : $1;
+ /*% %*/
+ /*% ripper: args_add!($1, bare_assoc_hash!($3)) %*/
}
| assocs trailer
{
/*%%%*/
- $$ = $1 ? NEW_LIST(new_hash($1)) : 0;
- /*%
- $$ = arg_add_assocs(arg_new(), $1);
- %*/
+ $$ = $1 ? NEW_LIST(new_hash(p, $1, &@1), &@$) : 0;
+ /*% %*/
+ /*% ripper: args_add!(args_new!, bare_assoc_hash!($1)) %*/
}
;
arg_rhs : arg %prec tOP_ASGN
{
- /*%%%*/
value_expr($1);
$$ = $1;
- /*%
- %*/
}
| arg modifier_rescue arg
{
/*%%%*/
+ YYLTYPE loc = code_loc_gen(&@2, &@3);
value_expr($1);
- $$ = NEW_RESCUE($1, NEW_RESBODY(0, remove_begin($3), 0), 0);
- /*%
- $$ = dispatch2(rescue_mod, $1, $3);
- %*/
+ $$ = NEW_RESCUE($1, NEW_RESBODY(0, remove_begin($3), 0, &loc), 0, &@$);
+ /*% %*/
+ /*% ripper: rescue_mod!($1, $3) %*/
}
;
@@ -2404,9 +2125,8 @@ paren_args : '(' opt_call_args rparen
{
/*%%%*/
$$ = $2;
- /*%
- $$ = dispatch1(arg_paren, escape_Qundef($2));
- %*/
+ /*% %*/
+ /*% ripper: arg_paren!(escape_Qundef($2)) %*/
}
;
@@ -2423,18 +2143,16 @@ opt_call_args : none
| args ',' assocs ','
{
/*%%%*/
- $$ = $3 ? arg_append($1, new_hash($3)) : $1;
- /*%
- $$ = arg_add_assocs($1, $3);
- %*/
+ $$ = $3 ? arg_append(p, $1, new_hash(p, $3, &@3), &@$) : $1;
+ /*% %*/
+ /*% ripper: args_add!($1, bare_assoc_hash!($3)) %*/
}
| assocs ','
{
/*%%%*/
- $$ = $1 ? NEW_LIST(new_hash($1)) : 0;
- /*%
- $$ = arg_add_assocs(arg_new(), $1);
- %*/
+ $$ = $1 ? NEW_LIST(new_hash(p, $1, &@1), &@1) : 0;
+ /*% %*/
+ /*% ripper: args_add!(args_new!, bare_assoc_hash!($1)) %*/
}
;
@@ -2442,55 +2160,70 @@ call_args : command
{
/*%%%*/
value_expr($1);
- $$ = NEW_LIST($1);
- /*%
- $$ = arg_add(arg_new(), $1);
- %*/
+ $$ = NEW_LIST($1, &@$);
+ /*% %*/
+ /*% ripper: args_add!(args_new!, $1) %*/
}
| args opt_block_arg
{
/*%%%*/
$$ = arg_blk_pass($1, $2);
- /*%
- $$ = arg_add_optblock($1, $2);
- %*/
+ /*% %*/
+ /*% ripper: args_add_block!($1, $2) %*/
}
| assocs opt_block_arg
{
/*%%%*/
- $$ = NEW_LIST($1 ? new_hash($1) : 0);
+ $$ = $1 ? NEW_LIST(new_hash(p, $1, &@1), &@1) : 0;
$$ = arg_blk_pass($$, $2);
- /*%
- $$ = arg_add_assocs(arg_new(), $1);
- $$ = arg_add_optblock($$, $2);
- %*/
+ /*% %*/
+ /*% ripper: args_add_block!(args_add!(args_new!, bare_assoc_hash!($1)), $2) %*/
}
| args ',' assocs opt_block_arg
{
/*%%%*/
- $$ = $3 ? arg_append($1, new_hash($3)) : $1;
+ $$ = $3 ? arg_append(p, $1, new_hash(p, $3, &@3), &@$) : $1;
$$ = arg_blk_pass($$, $4);
- /*%
- $$ = arg_add_optblock(arg_add_assocs($1, $3), $4);
- %*/
+ /*% %*/
+ /*% ripper: args_add_block!(args_add!($1, bare_assoc_hash!($3)), $4) %*/
}
| block_arg
- /*%c%*/
- /*%c
- {
- $$ = arg_add_block(arg_new(), $1);
- }
- %*/
+ /*% ripper[brace]: args_add_block!(args_new!, $1) %*/
;
command_args : {
- $<val>$ = cmdarg_stack;
+ /* If call_args starts with a open paren '(' or '[',
+ * look-ahead reading of the letters calls CMDARG_PUSH(0),
+ * but the push must be done after CMDARG_PUSH(1).
+ * So this code makes them consistent by first cancelling
+ * the premature CMDARG_PUSH(0), doing CMDARG_PUSH(1),
+ * and finally redoing CMDARG_PUSH(0).
+ */
+ int lookahead = 0;
+ switch (yychar) {
+ case '(': case tLPAREN: case tLPAREN_ARG: case '[': case tLBRACK:
+ lookahead = 1;
+ }
+ if (lookahead) CMDARG_POP();
CMDARG_PUSH(1);
+ if (lookahead) CMDARG_PUSH(0);
}
call_args
{
- /* CMDARG_POP() */
- CMDARG_SET($<val>1);
+ /* call_args can be followed by tLBRACE_ARG (that does CMDARG_PUSH(0) in the lexer)
+ * but the push must be done after CMDARG_POP() in the parser.
+ * So this code does CMDARG_POP() to pop 0 pushed by tLBRACE_ARG,
+ * CMDARG_POP() to pop 1 pushed by command_args,
+ * and CMDARG_PUSH(0) to restore back the flag set by tLBRACE_ARG.
+ */
+ int lookahead = 0;
+ switch (yychar) {
+ case tLBRACE_ARG:
+ lookahead = 1;
+ }
+ if (lookahead) CMDARG_POP();
+ CMDARG_POP();
+ if (lookahead) CMDARG_PUSH(0);
$$ = $2;
}
;
@@ -2498,10 +2231,9 @@ command_args : {
block_arg : tAMPER arg_value
{
/*%%%*/
- $$ = NEW_BLOCK_PASS($2);
- /*%
- $$ = $2;
- %*/
+ $$ = NEW_BLOCK_PASS($2, &@$);
+ /*% %*/
+ /*% ripper: $2 %*/
}
;
@@ -2518,46 +2250,30 @@ opt_block_arg : ',' block_arg
args : arg_value
{
/*%%%*/
- $$ = NEW_LIST($1);
- /*%
- $$ = arg_add(arg_new(), $1);
- %*/
+ $$ = NEW_LIST($1, &@$);
+ /*% %*/
+ /*% ripper: args_add!(args_new!, $1) %*/
}
| tSTAR arg_value
{
/*%%%*/
- $$ = NEW_SPLAT($2);
- /*%
- $$ = arg_add_star(arg_new(), $2);
- %*/
+ $$ = NEW_SPLAT($2, &@$);
+ /*% %*/
+ /*% ripper: args_add_star!(args_new!, $2) %*/
}
| args ',' arg_value
{
/*%%%*/
- NODE *n1;
- if ((n1 = splat_array($1)) != 0) {
- $$ = list_append(n1, $3);
- }
- else {
- $$ = arg_append($1, $3);
- }
- /*%
- $$ = arg_add($1, $3);
- %*/
+ $$ = last_arg_append(p, $1, $3, &@$);
+ /*% %*/
+ /*% ripper: args_add!($1, $3) %*/
}
| args ',' tSTAR arg_value
{
/*%%%*/
- NODE *n1;
- if ((nd_type($4) == NODE_ARRAY) && (n1 = splat_array($1)) != 0) {
- $$ = list_concat(n1, $4);
- }
- else {
- $$ = arg_concat($1, $4);
- }
- /*%
- $$ = arg_add_star($1, $4);
- %*/
+ $$ = rest_arg_append(p, $1, $4, &@$);
+ /*% %*/
+ /*% ripper: args_add_star!($1, $4) %*/
}
;
@@ -2568,39 +2284,23 @@ mrhs_arg : mrhs
mrhs : args ',' arg_value
{
/*%%%*/
- NODE *n1;
- if ((n1 = splat_array($1)) != 0) {
- $$ = list_append(n1, $3);
- }
- else {
- $$ = arg_append($1, $3);
- }
- /*%
- $$ = mrhs_add(args2mrhs($1), $3);
- %*/
+ $$ = last_arg_append(p, $1, $3, &@$);
+ /*% %*/
+ /*% ripper: mrhs_add!(mrhs_new_from_args!($1), $3) %*/
}
| args ',' tSTAR arg_value
{
/*%%%*/
- NODE *n1;
- if (nd_type($4) == NODE_ARRAY &&
- (n1 = splat_array($1)) != 0) {
- $$ = list_concat(n1, $4);
- }
- else {
- $$ = arg_concat($1, $4);
- }
- /*%
- $$ = mrhs_add_star(args2mrhs($1), $4);
- %*/
+ $$ = rest_arg_append(p, $1, $4, &@$);
+ /*% %*/
+ /*% ripper: mrhs_add_star!(mrhs_new_from_args!($1), $4) %*/
}
| tSTAR arg_value
{
/*%%%*/
- $$ = NEW_SPLAT($2);
- /*%
- $$ = mrhs_add_star(mrhs_new(), $2);
- %*/
+ $$ = NEW_SPLAT($2, &@$);
+ /*% %*/
+ /*% ripper: mrhs_add_star!(mrhs_new!, $2) %*/
}
;
@@ -2617,187 +2317,142 @@ primary : literal
| tFID
{
/*%%%*/
- $$ = NEW_FCALL($1, 0);
- /*%
- $$ = method_arg(dispatch1(fcall, $1), arg_new());
- %*/
+ $$ = NEW_FCALL($1, 0, &@$);
+ /*% %*/
+ /*% ripper: method_add_arg!(fcall!($1), args_new!) %*/
}
| k_begin
{
- $<val>1 = cmdarg_stack;
- CMDARG_SET(0);
- /*%%%*/
- $<num>$ = ruby_sourceline;
- /*%
- %*/
+ CMDARG_PUSH(0);
}
bodystmt
k_end
{
- CMDARG_SET($<val>1);
+ CMDARG_POP();
/*%%%*/
- if ($3 == NULL) {
- $$ = NEW_NIL();
- }
- else {
- if (nd_type($3) == NODE_RESCUE ||
- nd_type($3) == NODE_ENSURE)
- nd_set_line($3, $<num>2);
- $$ = NEW_BEGIN($3);
- }
- nd_set_line($$, $<num>2);
- /*%
- $$ = dispatch1(begin, $3);
- %*/
+ set_line_body($3, @1.end_pos.lineno);
+ $$ = NEW_BEGIN($3, &@$);
+ nd_set_line($$, @1.end_pos.lineno);
+ /*% %*/
+ /*% ripper: begin!($3) %*/
}
| tLPAREN_ARG {SET_LEX_STATE(EXPR_ENDARG);} rparen
{
/*%%%*/
- $$ = 0;
- /*%
- $$ = dispatch1(paren, 0);
- %*/
- }
- | tLPAREN_ARG
- {
- $<val>1 = cmdarg_stack;
- CMDARG_SET(0);
+ $$ = NEW_BEGIN(0, &@$);
+ /*% %*/
+ /*% ripper: paren!(0) %*/
}
- stmt {SET_LEX_STATE(EXPR_ENDARG);} rparen
+ | tLPAREN_ARG stmt {SET_LEX_STATE(EXPR_ENDARG);} rparen
{
- CMDARG_SET($<val>1);
/*%%%*/
- $$ = $3;
- /*%
- $$ = dispatch1(paren, $3);
- %*/
+ $$ = $2;
+ /*% %*/
+ /*% ripper: paren!($2) %*/
}
| tLPAREN compstmt ')'
{
/*%%%*/
$$ = $2;
- /*%
- $$ = dispatch1(paren, $2);
- %*/
+ /*% %*/
+ /*% ripper: paren!($2) %*/
}
| primary_value tCOLON2 tCONSTANT
{
/*%%%*/
- $$ = NEW_COLON2($1, $3);
- /*%
- $$ = dispatch2(const_path_ref, $1, $3);
- %*/
+ $$ = NEW_COLON2($1, $3, &@$);
+ /*% %*/
+ /*% ripper: const_path_ref!($1, $3) %*/
}
| tCOLON3 tCONSTANT
{
/*%%%*/
- $$ = NEW_COLON3($2);
- /*%
- $$ = dispatch1(top_const_ref, $2);
- %*/
+ $$ = NEW_COLON3($2, &@$);
+ /*% %*/
+ /*% ripper: top_const_ref!($2) %*/
}
| tLBRACK aref_args ']'
{
/*%%%*/
- if ($2 == 0) {
- $$ = NEW_ZARRAY(); /* zero length array*/
- }
- else {
- $$ = $2;
- }
- /*%
- $$ = dispatch1(array, escape_Qundef($2));
- %*/
+ $$ = make_array($2, &@$);
+ /*% %*/
+ /*% ripper: array!(escape_Qundef($2)) %*/
}
| tLBRACE assoc_list '}'
{
/*%%%*/
- $$ = new_hash($2);
- /*%
- $$ = dispatch1(hash, escape_Qundef($2));
- %*/
+ $$ = new_hash(p, $2, &@$);
+ $$->nd_alen = TRUE;
+ /*% %*/
+ /*% ripper: hash!(escape_Qundef($2)) %*/
}
- | keyword_return
+ | k_return
{
/*%%%*/
- $$ = NEW_RETURN(0);
- /*%
- $$ = dispatch0(return0);
- %*/
+ $$ = NEW_RETURN(0, &@$);
+ /*% %*/
+ /*% ripper: return0! %*/
}
| keyword_yield '(' call_args rparen
{
/*%%%*/
- $$ = new_yield($3);
- /*%
- $$ = dispatch1(yield, dispatch1(paren, $3));
- %*/
+ $$ = new_yield(p, $3, &@$);
+ /*% %*/
+ /*% ripper: yield!(paren!($3)) %*/
}
| keyword_yield '(' rparen
{
/*%%%*/
- $$ = NEW_YIELD(0);
- /*%
- $$ = dispatch1(yield, dispatch1(paren, arg_new()));
- %*/
+ $$ = NEW_YIELD(0, &@$);
+ /*% %*/
+ /*% ripper: yield!(paren!(args_new!)) %*/
}
| keyword_yield
{
/*%%%*/
- $$ = NEW_YIELD(0);
- /*%
- $$ = dispatch0(yield0);
- %*/
+ $$ = NEW_YIELD(0, &@$);
+ /*% %*/
+ /*% ripper: yield0! %*/
}
- | keyword_defined opt_nl '(' {in_defined = 1;} expr rparen
+ | keyword_defined opt_nl '(' {p->in_defined = 1;} expr rparen
{
- in_defined = 0;
- /*%%%*/
- $$ = new_defined($5);
- /*%
- $$ = dispatch1(defined, $5);
- %*/
+ p->in_defined = 0;
+ $$ = new_defined(p, $5, &@$);
}
| keyword_not '(' expr rparen
{
- /*%%%*/
- $$ = call_uni_op(method_cond($3), '!');
- /*%
- $$ = dispatch2(unary, ripper_intern("not"), $3);
- %*/
+ $$ = call_uni_op(p, method_cond(p, $3, &@3), METHOD_NOT, &@1, &@$);
}
| keyword_not '(' rparen
{
- /*%%%*/
- $$ = call_uni_op(method_cond(NEW_NIL()), '!');
- /*%
- $$ = dispatch2(unary, ripper_intern("not"), Qnil);
- %*/
+ $$ = call_uni_op(p, method_cond(p, new_nil(&@2), &@2), METHOD_NOT, &@1, &@$);
}
| fcall brace_block
{
/*%%%*/
- $2->nd_iter = $1;
- $$ = $2;
- /*%
- $$ = method_arg(dispatch1(fcall, $1), arg_new());
- $$ = method_add_block($$, $2);
- %*/
+ $$ = method_add_block(p, $1, $2, &@$);
+ /*% %*/
+ /*% ripper: method_add_block!(method_add_arg!(fcall!($1), args_new!), $2) %*/
}
| method_call
| method_call brace_block
{
/*%%%*/
- block_dup_check($1->nd_args, $2);
- $2->nd_iter = $1;
- $$ = $2;
- /*%
- $$ = method_add_block($1, $2);
- %*/
+ block_dup_check(p, $1->nd_args, $2);
+ $$ = method_add_block(p, $1, $2, &@$);
+ /*% %*/
+ /*% ripper: method_add_block!($1, $2) %*/
}
- | tLAMBDA lambda
+ | tLAMBDA
{
- $$ = $2;
+ token_info_push(p, "->", &@1);
+ }
+ lambda
+ {
+ $$ = $3;
+ /*%%%*/
+ nd_set_first_loc($$, @1.beg_pos);
+ /*% %*/
}
| k_if expr_value then
compstmt
@@ -2805,11 +2460,10 @@ primary : literal
k_end
{
/*%%%*/
- $$ = new_if($2, $4, $5);
+ $$ = new_if(p, $2, $4, $5, &@$);
fixpos($$, $2);
- /*%
- $$ = dispatch3(if, $2, $4, escape_Qundef($5));
- %*/
+ /*% %*/
+ /*% ripper: if!($2, $4, escape_Qundef($5)) %*/
}
| k_unless expr_value then
compstmt
@@ -2817,57 +2471,49 @@ primary : literal
k_end
{
/*%%%*/
- $$ = new_unless($2, $4, $5);
+ $$ = new_unless(p, $2, $4, $5, &@$);
fixpos($$, $2);
- /*%
- $$ = dispatch3(unless, $2, $4, escape_Qundef($5));
- %*/
+ /*% %*/
+ /*% ripper: unless!($2, $4, escape_Qundef($5)) %*/
}
- | k_while {COND_PUSH(1);} expr_value do {COND_POP();}
+ | k_while expr_value_do
compstmt
k_end
{
/*%%%*/
- $$ = NEW_WHILE(cond($3), $6, 1);
- fixpos($$, $3);
- /*%
- $$ = dispatch2(while, $3, $6);
- %*/
+ $$ = NEW_WHILE(cond(p, $2, &@2), $3, 1, &@$);
+ fixpos($$, $2);
+ /*% %*/
+ /*% ripper: while!($2, $3) %*/
}
- | k_until {COND_PUSH(1);} expr_value do {COND_POP();}
+ | k_until expr_value_do
compstmt
k_end
{
/*%%%*/
- $$ = NEW_UNTIL(cond($3), $6, 1);
- fixpos($$, $3);
- /*%
- $$ = dispatch2(until, $3, $6);
- %*/
+ $$ = NEW_UNTIL(cond(p, $2, &@2), $3, 1, &@$);
+ fixpos($$, $2);
+ /*% %*/
+ /*% ripper: until!($2, $3) %*/
}
| k_case expr_value opt_terms
case_body
k_end
{
/*%%%*/
- $$ = NEW_CASE($2, $4);
+ $$ = NEW_CASE($2, $4, &@$);
fixpos($$, $2);
- /*%
- $$ = dispatch2(case, $2, $4);
- %*/
+ /*% %*/
+ /*% ripper: case!($2, $4) %*/
}
| k_case opt_terms case_body k_end
{
/*%%%*/
- $$ = NEW_CASE(0, $3);
- /*%
- $$ = dispatch2(case, Qnil, $3);
- %*/
+ $$ = NEW_CASE2($3, &@$);
+ /*% %*/
+ /*% ripper: case!(Qnil, $3) %*/
}
- | k_for for_var keyword_in
- {COND_PUSH(1);}
- expr_value do
- {COND_POP();}
+ | k_for for_var keyword_in expr_value_do
compstmt
k_end
{
@@ -2881,109 +2527,114 @@ primary : literal
* #=>
* e.each{|x| a, = x}
*/
- ID id = internal_id();
+ ID id = internal_id(p);
+ NODE *m = NEW_ARGS_AUX(0, 0, &NULL_LOC);
+ NODE *args, *scope, *internal_var = NEW_DVAR(id, &@2);
+ rb_imemo_tmpbuf_t *tmpbuf = new_tmpbuf();
ID *tbl = ALLOC_N(ID, 2);
- NODE *m = NEW_ARGS_AUX(0, 0);
- NODE *args, *scope;
+ tbl[0] = 1 /* length of local var table */; tbl[1] = id /* internal id */;
+ tmpbuf->ptr = (VALUE *)tbl;
switch (nd_type($2)) {
- case NODE_MASGN:
- m->nd_next = node_assign($2, NEW_FOR(NEW_DVAR(id), 0, 0));
- args = new_args(m, 0, id, 0, new_args_tail(0, 0, 0));
- break;
case NODE_LASGN:
case NODE_DASGN:
- case NODE_DASGN_CURR:
- $2->nd_value = NEW_DVAR(id);
+ case NODE_DASGN_CURR: /* e.each {|internal_var| a = internal_var; ... } */
+ $2->nd_value = internal_var;
+ id = 0;
m->nd_plen = 1;
m->nd_next = $2;
- args = new_args(m, 0, 0, 0, new_args_tail(0, 0, 0));
break;
- default:
- m->nd_next = node_assign(NEW_MASGN(NEW_LIST($2), 0), NEW_DVAR(id));
- args = new_args(m, 0, id, 0, new_args_tail(0, 0, 0));
+ case NODE_MASGN: /* e.each {|*internal_var| a, b, c = (internal_var.length == 1 && Array === (tmp = internal_var[0]) ? tmp : internal_var); ... } */
+ m->nd_next = node_assign(p, $2, NEW_FOR_MASGN(internal_var, &@2), &@2);
break;
+ default: /* e.each {|*internal_var| @a, B, c[1], d.attr = internal_val; ... } */
+ m->nd_next = node_assign(p, NEW_MASGN(NEW_LIST($2, &@2), 0, &@2), internal_var, &@2);
}
- scope = NEW_NODE(NODE_SCOPE, tbl, $8, args);
- tbl[0] = 1; tbl[1] = id;
- $$ = NEW_FOR(0, $5, scope);
+ /* {|*internal_id| <m> = internal_id; ... } */
+ args = new_args(p, m, 0, id, 0, new_args_tail(p, 0, 0, 0, &@2), &@2);
+ scope = NEW_NODE(NODE_SCOPE, tbl, $5, args, &@$);
+ $$ = NEW_FOR($4, scope, &@$);
fixpos($$, $2);
- /*%
- $$ = dispatch3(for, $2, $5, $8);
- %*/
+ /*% %*/
+ /*% ripper: for!($2, $4, $5) %*/
}
| k_class cpath superclass
{
- if (in_def || in_single)
- yyerror("class definition in method body");
- local_push(0);
- /*%%%*/
- $<num>$ = ruby_sourceline;
- /*%
- %*/
+ if (p->in_def) {
+ YYLTYPE loc = code_loc_gen(&@1, &@2);
+ yyerror1(&loc, "class definition in method body");
+ }
+ $<num>1 = p->in_class;
+ p->in_class = 1;
+ local_push(p, 0);
}
bodystmt
k_end
{
/*%%%*/
- $$ = NEW_CLASS($2, $5, $3);
- nd_set_line($$, $<num>4);
- /*%
- $$ = dispatch3(class, $2, $3, $5);
- %*/
- local_pop();
+ $$ = NEW_CLASS($2, $5, $3, &@$);
+ nd_set_line($$->nd_body, @6.end_pos.lineno);
+ set_line_body($5, @3.end_pos.lineno);
+ nd_set_line($$, @3.end_pos.lineno);
+ /*% %*/
+ /*% ripper: class!($2, $3, $5) %*/
+ local_pop(p);
+ p->in_class = $<num>1 & 1;
}
| k_class tLSHFT expr
{
- $<num>$ = (in_def << 1) | in_single;
- in_def = 0;
- in_single = 0;
- local_push(0);
+ $<num>$ = (p->in_class << 1) | p->in_def;
+ p->in_def = 0;
+ p->in_class = 0;
+ local_push(p, 0);
}
term
bodystmt
k_end
{
/*%%%*/
- $$ = NEW_SCLASS($3, $6);
+ $$ = NEW_SCLASS($3, $6, &@$);
+ nd_set_line($$->nd_body, @7.end_pos.lineno);
+ set_line_body($6, nd_line($3));
fixpos($$, $3);
- /*%
- $$ = dispatch2(sclass, $3, $6);
- %*/
- local_pop();
- in_def = ($<num>4 >> 1) & 1;
- in_single = $<num>4 & 1;
+ /*% %*/
+ /*% ripper: sclass!($3, $6) %*/
+ local_pop(p);
+ p->in_def = $<num>4 & 1;
+ p->in_class = ($<num>4 >> 1) & 1;
}
| k_module cpath
{
- if (in_def || in_single)
- yyerror("module definition in method body");
- local_push(0);
- /*%%%*/
- $<num>$ = ruby_sourceline;
- /*%
- %*/
+ if (p->in_def) {
+ YYLTYPE loc = code_loc_gen(&@1, &@2);
+ yyerror1(&loc, "module definition in method body");
+ }
+ $<num>1 = p->in_class;
+ p->in_class = 1;
+ local_push(p, 0);
}
bodystmt
k_end
{
/*%%%*/
- $$ = NEW_MODULE($2, $4);
- nd_set_line($$, $<num>3);
- /*%
- $$ = dispatch2(module, $2, $4);
- %*/
- local_pop();
+ $$ = NEW_MODULE($2, $4, &@$);
+ nd_set_line($$->nd_body, @5.end_pos.lineno);
+ set_line_body($4, @2.end_pos.lineno);
+ nd_set_line($$, @2.end_pos.lineno);
+ /*% %*/
+ /*% ripper: module!($2, $4) %*/
+ local_pop(p);
+ p->in_class = $<num>1 & 1;
}
| k_def fname
{
- local_push(0);
- $<id>$ = current_arg;
- current_arg = 0;
+ local_push(p, 0);
+ $<id>$ = p->cur_arg;
+ p->cur_arg = 0;
}
{
- $<num>$ = in_def;
- in_def = 1;
+ $<num>$ = p->in_def;
+ p->in_def = 1;
}
f_arglist
bodystmt
@@ -2991,24 +2642,24 @@ primary : literal
{
/*%%%*/
NODE *body = remove_begin($6);
- reduce_nodes(&body);
- $$ = NEW_DEFN($2, $5, body, METHOD_VISI_PRIVATE);
- nd_set_line($$, $<num>1);
- /*%
- $$ = dispatch3(def, $2, $5, $6);
- %*/
- local_pop();
- in_def = $<num>4 & 1;
- current_arg = $<id>3;
+ reduce_nodes(p, &body);
+ $$ = NEW_DEFN($2, $5, body, &@$);
+ nd_set_line($$->nd_defn, @7.end_pos.lineno);
+ set_line_body(body, @1.beg_pos.lineno);
+ /*% %*/
+ /*% ripper: def!($2, $5, $6) %*/
+ local_pop(p);
+ p->in_def = $<num>4 & 1;
+ p->cur_arg = $<id>3;
}
| k_def singleton dot_or_colon {SET_LEX_STATE(EXPR_FNAME);} fname
{
- $<num>4 = in_single;
- in_single = 1;
+ $<num>4 = p->in_def;
+ p->in_def = 1;
SET_LEX_STATE(EXPR_ENDFN|EXPR_LABEL); /* force for args */
- local_push(0);
- $<id>$ = current_arg;
- current_arg = 0;
+ local_push(p, 0);
+ $<id>$ = p->cur_arg;
+ p->cur_arg = 0;
}
f_arglist
bodystmt
@@ -3016,175 +2667,199 @@ primary : literal
{
/*%%%*/
NODE *body = remove_begin($8);
- reduce_nodes(&body);
- $$ = NEW_DEFS($2, $5, $7, body);
- nd_set_line($$, $<num>1);
- /*%
- $$ = dispatch5(defs, $2, $3, $5, $7, $8);
- %*/
- local_pop();
- in_single = $<num>4 & 1;
- current_arg = $<id>6;
+ reduce_nodes(p, &body);
+ $$ = NEW_DEFS($2, $5, $7, body, &@$);
+ nd_set_line($$->nd_defn, @9.end_pos.lineno);
+ set_line_body(body, @1.beg_pos.lineno);
+ /*% %*/
+ /*% ripper: defs!($2, $3, $5, $7, $8) %*/
+ local_pop(p);
+ p->in_def = $<num>4 & 1;
+ p->cur_arg = $<id>6;
}
| keyword_break
{
/*%%%*/
- $$ = NEW_BREAK(0);
- /*%
- $$ = dispatch1(break, arg_new());
- %*/
+ $$ = NEW_BREAK(0, &@$);
+ /*% %*/
+ /*% ripper: break!(args_new!) %*/
}
| keyword_next
{
/*%%%*/
- $$ = NEW_NEXT(0);
- /*%
- $$ = dispatch1(next, arg_new());
- %*/
+ $$ = NEW_NEXT(0, &@$);
+ /*% %*/
+ /*% ripper: next!(args_new!) %*/
}
| keyword_redo
{
/*%%%*/
- $$ = NEW_REDO();
- /*%
- $$ = dispatch0(redo);
- %*/
+ $$ = NEW_REDO(&@$);
+ /*% %*/
+ /*% ripper: redo! %*/
}
| keyword_retry
{
/*%%%*/
- $$ = NEW_RETRY();
- /*%
- $$ = dispatch0(retry);
- %*/
+ $$ = NEW_RETRY(&@$);
+ /*% %*/
+ /*% ripper: retry! %*/
}
;
primary_value : primary
{
- /*%%%*/
value_expr($1);
$$ = $1;
- if (!$$) $$ = NEW_NIL();
- /*%
- $$ = $1;
- %*/
}
;
k_begin : keyword_begin
{
- token_info_push("begin");
+ token_info_push(p, "begin", &@$);
}
;
k_if : keyword_if
{
- token_info_push("if");
+ token_info_push(p, "if", &@$);
}
;
k_unless : keyword_unless
{
- token_info_push("unless");
+ token_info_push(p, "unless", &@$);
}
;
k_while : keyword_while
{
- token_info_push("while");
+ token_info_push(p, "while", &@$);
}
;
k_until : keyword_until
{
- token_info_push("until");
+ token_info_push(p, "until", &@$);
}
;
k_case : keyword_case
{
- token_info_push("case");
+ token_info_push(p, "case", &@$);
}
;
k_for : keyword_for
{
- token_info_push("for");
+ token_info_push(p, "for", &@$);
}
;
k_class : keyword_class
{
- token_info_push("class");
+ token_info_push(p, "class", &@$);
}
;
k_module : keyword_module
{
- token_info_push("module");
+ token_info_push(p, "module", &@$);
}
;
k_def : keyword_def
{
- token_info_push("def");
- /*%%%*/
- $<num>$ = ruby_sourceline;
- /*%
- %*/
+ token_info_push(p, "def", &@$);
+ }
+ ;
+
+k_do : keyword_do
+ {
+ token_info_push(p, "do", &@$);
+ }
+ ;
+
+k_do_block : keyword_do_block
+ {
+ token_info_push(p, "do", &@$);
+ }
+ ;
+
+k_rescue : keyword_rescue
+ {
+ token_info_warn(p, "rescue", p->token_info, 1, &@$);
+ }
+ ;
+
+k_ensure : keyword_ensure
+ {
+ token_info_warn(p, "ensure", p->token_info, 1, &@$);
+ }
+ ;
+
+k_when : keyword_when
+ {
+ token_info_warn(p, "when", p->token_info, 0, &@$);
+ }
+ ;
+
+k_else : keyword_else
+ {
+ token_info *ptinfo_beg = p->token_info;
+ int same = ptinfo_beg && strcmp(ptinfo_beg->token, "case") != 0;
+ token_info_warn(p, "else", p->token_info, same, &@$);
+ }
+ ;
+
+k_elsif : keyword_elsif
+ {
+ token_info_warn(p, "elsif", p->token_info, 1, &@$);
}
;
k_end : keyword_end
{
- token_info_pop("end");
+ token_info_pop(p, "end", &@$);
+ }
+ ;
+
+k_return : keyword_return
+ {
+ if (p->in_class && !p->in_def && !dyna_in_block(p))
+ yyerror1(&@1, "Invalid return in class/module body");
}
;
then : term
- /*%c%*/
- /*%c
- { $$ = Qnil; }
- %*/
| keyword_then
| term keyword_then
- /*%c%*/
- /*%c
- { $$ = $2; }
- %*/
;
do : term
- /*%c%*/
- /*%c
- { $$ = Qnil; }
- %*/
| keyword_do_cond
;
if_tail : opt_else
- | keyword_elsif expr_value then
+ | k_elsif expr_value then
compstmt
if_tail
{
/*%%%*/
- $$ = new_if($2, $4, $5);
+ $$ = new_if(p, $2, $4, $5, &@$);
fixpos($$, $2);
- /*%
- $$ = dispatch3(elsif, $2, $4, escape_Qundef($5));
- %*/
+ /*% %*/
+ /*% ripper: elsif!($2, $4, escape_Qundef($5)) %*/
}
;
opt_else : none
- | keyword_else compstmt
+ | k_else compstmt
{
/*%%%*/
$$ = $2;
- /*%
- $$ = dispatch1(else, $2);
- %*/
+ /*% %*/
+ /*% ripper: else!($2) %*/
}
;
@@ -3194,137 +2869,117 @@ for_var : lhs
f_marg : f_norm_arg
{
- $$ = assignable($1, 0);
/*%%%*/
- /*%
- $$ = dispatch1(mlhs_paren, $$);
- %*/
+ $$ = assignable(p, $1, 0, &@$);
+ /*% %*/
+ /*% ripper: assignable(p, $1) %*/
}
| tLPAREN f_margs rparen
{
/*%%%*/
$$ = $2;
- /*%
- $$ = dispatch1(mlhs_paren, $2);
- %*/
+ /*% %*/
+ /*% ripper: mlhs_paren!($2) %*/
}
;
f_marg_list : f_marg
{
/*%%%*/
- $$ = NEW_LIST($1);
- /*%
- $$ = mlhs_add(mlhs_new(), $1);
- %*/
+ $$ = NEW_LIST($1, &@$);
+ /*% %*/
+ /*% ripper: mlhs_add!(mlhs_new!, $1) %*/
}
| f_marg_list ',' f_marg
{
/*%%%*/
- $$ = list_append($1, $3);
- /*%
- $$ = mlhs_add($1, $3);
- %*/
+ $$ = list_append(p, $1, $3);
+ /*% %*/
+ /*% ripper: mlhs_add!($1, $3) %*/
}
;
f_margs : f_marg_list
{
/*%%%*/
- $$ = NEW_MASGN($1, 0);
- /*%
- $$ = $1;
- %*/
+ $$ = NEW_MASGN($1, 0, &@$);
+ /*% %*/
+ /*% ripper: $1 %*/
}
| f_marg_list ',' tSTAR f_norm_arg
{
- $$ = assignable($4, 0);
/*%%%*/
- $$ = NEW_MASGN($1, $$);
- /*%
- $$ = mlhs_add_star($1, $$);
- %*/
+ $$ = NEW_MASGN($1, assignable(p, $4, 0, &@$), &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_star!($1, assignable(p, $4)) %*/
}
| f_marg_list ',' tSTAR f_norm_arg ',' f_marg_list
{
- $$ = assignable($4, 0);
/*%%%*/
- $$ = NEW_MASGN($1, NEW_POSTARG($$, $6));
- /*%
- $$ = mlhs_add_star($1, $$);
- %*/
+ $$ = NEW_MASGN($1, NEW_POSTARG(assignable(p, $4, 0, &@$), $6, &@$), &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_post!(mlhs_add_star!($1, assignable(p, $4)), $6) %*/
}
| f_marg_list ',' tSTAR
{
/*%%%*/
- $$ = NEW_MASGN($1, -1);
- /*%
- $$ = mlhs_add_star($1, Qnil);
- %*/
+ $$ = NEW_MASGN($1, NODE_SPECIAL_NO_NAME_REST, &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_star!($1, Qnil) %*/
}
| f_marg_list ',' tSTAR ',' f_marg_list
{
/*%%%*/
- $$ = NEW_MASGN($1, NEW_POSTARG(-1, $5));
- /*%
- $$ = mlhs_add_star($1, $5);
- %*/
+ $$ = NEW_MASGN($1, NEW_POSTARG(NODE_SPECIAL_NO_NAME_REST, $5, &@$), &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_post!(mlhs_add_star!($1, Qnil), $5) %*/
}
| tSTAR f_norm_arg
{
- $$ = assignable($2, 0);
/*%%%*/
- $$ = NEW_MASGN(0, $$);
- /*%
- $$ = mlhs_add_star(mlhs_new(), $$);
- %*/
+ $$ = NEW_MASGN(0, assignable(p, $2, 0, &@$), &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_star!(mlhs_new!, assignable(p, $2)) %*/
}
| tSTAR f_norm_arg ',' f_marg_list
{
- $$ = assignable($2, 0);
/*%%%*/
- $$ = NEW_MASGN(0, NEW_POSTARG($$, $4));
- /*%
- #if 0
- TODO: Check me
- #endif
- $$ = mlhs_add_star($$, $4);
- %*/
+ $$ = NEW_MASGN(0, NEW_POSTARG(assignable(p, $2, 0, &@$), $4, &@$), &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_post!(mlhs_add_star!(mlhs_new!, assignable(p, $2)), $4) %*/
}
| tSTAR
{
/*%%%*/
- $$ = NEW_MASGN(0, -1);
- /*%
- $$ = mlhs_add_star(mlhs_new(), Qnil);
- %*/
+ $$ = NEW_MASGN(0, NODE_SPECIAL_NO_NAME_REST, &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_star!(mlhs_new!, Qnil) %*/
}
| tSTAR ',' f_marg_list
{
/*%%%*/
- $$ = NEW_MASGN(0, NEW_POSTARG(-1, $3));
- /*%
- $$ = mlhs_add_star(mlhs_new(), Qnil);
- %*/
+ $$ = NEW_MASGN(0, NEW_POSTARG(NODE_SPECIAL_NO_NAME_REST, $3, &@$), &@$);
+ /*% %*/
+ /*% ripper: mlhs_add_post!(mlhs_add_star!(mlhs_new!, Qnil), $3) %*/
}
;
block_args_tail : f_block_kwarg ',' f_kwrest opt_f_block_arg
{
- $$ = new_args_tail($1, $3, $4);
+ $$ = new_args_tail(p, $1, $3, $4, &@3);
}
| f_block_kwarg opt_f_block_arg
{
- $$ = new_args_tail($1, Qnone, $2);
+ $$ = new_args_tail(p, $1, Qnone, $2, &@1);
}
| f_kwrest opt_f_block_arg
{
- $$ = new_args_tail(Qnone, $1, $2);
+ $$ = new_args_tail(p, Qnone, $1, $2, &@1);
}
| f_block_arg
{
- $$ = new_args_tail(Qnone, Qnone, $1);
+ $$ = new_args_tail(p, Qnone, Qnone, $1, &@1);
}
;
@@ -3334,110 +2989,106 @@ opt_block_args_tail : ',' block_args_tail
}
| /* none */
{
- $$ = new_args_tail(Qnone, Qnone, Qnone);
+ $$ = new_args_tail(p, Qnone, Qnone, Qnone, &@0);
}
;
block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_block_args_tail
{
- $$ = new_args($1, $3, $5, Qnone, $6);
+ $$ = new_args(p, $1, $3, $5, Qnone, $6, &@$);
}
| f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail
{
- $$ = new_args($1, $3, $5, $7, $8);
+ $$ = new_args(p, $1, $3, $5, $7, $8, &@$);
}
| f_arg ',' f_block_optarg opt_block_args_tail
{
- $$ = new_args($1, $3, Qnone, Qnone, $4);
+ $$ = new_args(p, $1, $3, Qnone, Qnone, $4, &@$);
}
| f_arg ',' f_block_optarg ',' f_arg opt_block_args_tail
{
- $$ = new_args($1, $3, Qnone, $5, $6);
+ $$ = new_args(p, $1, $3, Qnone, $5, $6, &@$);
}
| f_arg ',' f_rest_arg opt_block_args_tail
{
- $$ = new_args($1, Qnone, $3, Qnone, $4);
+ $$ = new_args(p, $1, Qnone, $3, Qnone, $4, &@$);
}
| f_arg ','
{
- $$ = new_args($1, Qnone, 1, Qnone, new_args_tail(Qnone, Qnone, Qnone));
/*%%%*/
- /*%
- dispatch1(excessed_comma, $$);
- %*/
+ /* magic number for rest_id in iseq_set_arguments() */
+ const ID excessed_comma = 1;
+ $$ = new_args(p, $1, Qnone, excessed_comma, Qnone, new_args_tail(p, Qnone, Qnone, Qnone, &@1), &@$);
+ /*% %*/
+ /*% ripper: new_args(p, $1, Qnone, excessed_comma!, Qnone, new_args_tail(p, Qnone, Qnone, Qnone, NULL), NULL) %*/
}
| f_arg ',' f_rest_arg ',' f_arg opt_block_args_tail
{
- $$ = new_args($1, Qnone, $3, $5, $6);
+ $$ = new_args(p, $1, Qnone, $3, $5, $6, &@$);
}
| f_arg opt_block_args_tail
{
- $$ = new_args($1, Qnone, Qnone, Qnone, $2);
+ $$ = new_args(p, $1, Qnone, Qnone, Qnone, $2, &@$);
}
| f_block_optarg ',' f_rest_arg opt_block_args_tail
{
- $$ = new_args(Qnone, $1, $3, Qnone, $4);
+ $$ = new_args(p, Qnone, $1, $3, Qnone, $4, &@$);
}
| f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail
{
- $$ = new_args(Qnone, $1, $3, $5, $6);
+ $$ = new_args(p, Qnone, $1, $3, $5, $6, &@$);
}
| f_block_optarg opt_block_args_tail
{
- $$ = new_args(Qnone, $1, Qnone, Qnone, $2);
+ $$ = new_args(p, Qnone, $1, Qnone, Qnone, $2, &@$);
}
| f_block_optarg ',' f_arg opt_block_args_tail
{
- $$ = new_args(Qnone, $1, Qnone, $3, $4);
+ $$ = new_args(p, Qnone, $1, Qnone, $3, $4, &@$);
}
| f_rest_arg opt_block_args_tail
{
- $$ = new_args(Qnone, Qnone, $1, Qnone, $2);
+ $$ = new_args(p, Qnone, Qnone, $1, Qnone, $2, &@$);
}
| f_rest_arg ',' f_arg opt_block_args_tail
{
- $$ = new_args(Qnone, Qnone, $1, $3, $4);
+ $$ = new_args(p, Qnone, Qnone, $1, $3, $4, &@$);
}
| block_args_tail
{
- $$ = new_args(Qnone, Qnone, Qnone, Qnone, $1);
+ $$ = new_args(p, Qnone, Qnone, Qnone, Qnone, $1, &@$);
}
;
opt_block_param : none
| block_param_def
{
- command_start = TRUE;
+ p->command_start = TRUE;
}
;
block_param_def : '|' opt_bv_decl '|'
{
- current_arg = 0;
+ p->cur_arg = 0;
/*%%%*/
$$ = 0;
- /*%
- $$ = blockvar_new(params_new(Qnil,Qnil,Qnil,Qnil,Qnil,Qnil,Qnil),
- escape_Qundef($2));
- %*/
+ /*% %*/
+ /*% ripper: block_var!(params_new(Qnil,Qnil,Qnil,Qnil,Qnil,Qnil,Qnil), escape_Qundef($2)) %*/
}
| tOROP
{
/*%%%*/
$$ = 0;
- /*%
- $$ = blockvar_new(params_new(Qnil,Qnil,Qnil,Qnil,Qnil,Qnil,Qnil),
- Qnil);
- %*/
+ /*% %*/
+ /*% ripper: block_var!(params_new(Qnil,Qnil,Qnil,Qnil,Qnil,Qnil,Qnil), Qnil) %*/
}
| '|' block_param opt_bv_decl '|'
{
- current_arg = 0;
+ p->cur_arg = 0;
/*%%%*/
$$ = $2;
- /*%
- $$ = blockvar_new(escape_Qundef($2), escape_Qundef($3));
- %*/
+ /*% %*/
+ /*% ripper: block_var!(escape_Qundef($2), escape_Qundef($3)) %*/
}
;
@@ -3450,35 +3101,21 @@ opt_bv_decl : opt_nl
{
/*%%%*/
$$ = 0;
- /*%
- $$ = $3;
- %*/
+ /*% %*/
+ /*% ripper: $3 %*/
}
;
bv_decls : bvar
- /*%c%*/
- /*%c
- {
- $$ = rb_ary_new3(1, $1);
- }
- %*/
+ /*% ripper[brace]: rb_ary_new3(1, get_value($1)) %*/
| bv_decls ',' bvar
- /*%c%*/
- /*%c
- {
- rb_ary_push($1, $3);
- }
- %*/
+ /*% ripper[brace]: rb_ary_push($1, get_value($3)) %*/
;
bvar : tIDENTIFIER
{
- new_bv(get_id($1));
- /*%%%*/
- /*%
- $$ = get_value($1);
- %*/
+ new_bv(p, get_id($1));
+ /*% ripper: get_value($1) %*/
}
| f_bad_arg
{
@@ -3487,32 +3124,30 @@ bvar : tIDENTIFIER
;
lambda : {
- $<vars>$ = dyna_push();
+ $<vars>$ = dyna_push(p);
}
{
- $<num>$ = lpar_beg;
- lpar_beg = ++paren_nest;
+ $<num>$ = p->lex.lpar_beg;
+ p->lex.lpar_beg = p->lex.paren_nest;
}
f_larglist
{
- $<num>$ = ruby_sourceline;
- }
- {
- $<val>$ = cmdarg_stack;
- CMDARG_SET(0);
+ CMDARG_PUSH(0);
}
lambda_body
{
- lpar_beg = $<num>2;
- CMDARG_SET($<val>5);
- CMDARG_LEXPOP();
+ p->lex.lpar_beg = $<num>2;
+ CMDARG_POP();
/*%%%*/
- $$ = NEW_LAMBDA($3, $6);
- nd_set_line($$, $<num>4);
- /*%
- $$ = dispatch2(lambda, $3, $6);
- %*/
- dyna_pop($<vars>1);
+ {
+ YYLTYPE loc = code_loc_gen(&@3, &@5);
+ $$ = NEW_LAMBDA($3, $5, &loc);
+ nd_set_line($$->nd_body, @5.end_pos.lineno);
+ nd_set_line($$, @3.end_pos.lineno);
+ }
+ /*% %*/
+ /*% ripper: lambda!($3, $5) %*/
+ dyna_pop(p, $<vars>1);
}
;
@@ -3520,9 +3155,8 @@ f_larglist : '(' f_args opt_bv_decl ')'
{
/*%%%*/
$$ = $2;
- /*%
- $$ = dispatch1(paren, $2);
- %*/
+ /*% %*/
+ /*% ripper: paren!($2) %*/
}
| f_args
{
@@ -3532,26 +3166,21 @@ f_larglist : '(' f_args opt_bv_decl ')'
lambda_body : tLAMBEG compstmt '}'
{
- token_info_pop("}");
+ token_info_pop(p, "}", &@3);
$$ = $2;
}
- | keyword_do_LAMBDA compstmt k_end
+ | keyword_do_LAMBDA bodystmt k_end
{
$$ = $2;
}
;
-do_block : keyword_do_block
+do_block : k_do_block do_body k_end
{
+ $$ = $2;
/*%%%*/
- $<num>$ = ruby_sourceline;
- /*% %*/
- }
- do_body keyword_end
- {
- $$ = $3;
- /*%%%*/
- nd_set_line($$, $<num>2);
+ $$->nd_body->nd_loc = code_loc_gen(&@1, &@3);
+ nd_set_line($$, @1.end_pos.lineno);
/*% %*/
}
;
@@ -3560,50 +3189,36 @@ block_call : command do_block
{
/*%%%*/
if (nd_type($1) == NODE_YIELD) {
- compile_error(PARSER_ARG "block given to yield");
+ compile_error(p, "block given to yield");
}
else {
- block_dup_check($1->nd_args, $2);
+ block_dup_check(p, $1->nd_args, $2);
}
- $2->nd_iter = $1;
- $$ = $2;
+ $$ = method_add_block(p, $1, $2, &@$);
fixpos($$, $1);
- /*%
- $$ = method_add_block($1, $2);
- %*/
+ /*% %*/
+ /*% ripper: method_add_block!($1, $2) %*/
}
| block_call call_op2 operation2 opt_paren_args
{
/*%%%*/
- $$ = NEW_QCALL($2, $1, $3, $4);
- /*%
- $$ = dispatch3(call, $1, $2, $3);
- $$ = method_optarg($$, $4);
- %*/
+ $$ = new_qcall(p, $2, $1, $3, $4, &@3, &@$);
+ /*% %*/
+ /*% ripper: opt_event(:method_add_arg!, call!($1, $2, $3), $4) %*/
}
| block_call call_op2 operation2 opt_paren_args brace_block
{
/*%%%*/
- block_dup_check($4, $5);
- $5->nd_iter = NEW_QCALL($2, $1, $3, $4);
- $$ = $5;
- fixpos($$, $1);
- /*%
- $$ = dispatch4(command_call, $1, $2, $3, $4);
- $$ = method_add_block($$, $5);
- %*/
+ $$ = new_command_qcall(p, $2, $1, $3, $4, $5, &@3, &@$);
+ /*% %*/
+ /*% ripper: opt_event(:method_add_block!, command_call!($1, $2, $3, $4), $5) %*/
}
| block_call call_op2 operation2 command_args do_block
{
/*%%%*/
- block_dup_check($4, $5);
- $5->nd_iter = NEW_QCALL($2, $1, $3, $4);
- $$ = $5;
- fixpos($$, $1);
- /*%
- $$ = dispatch4(command_call, $1, $2, $3, $4);
- $$ = method_add_block($$, $5);
- %*/
+ $$ = new_command_qcall(p, $2, $1, $3, $4, $5, &@3, &@$);
+ /*% %*/
+ /*% ripper: method_add_block!(command_call!($1, $2, $3, $4), $5) %*/
}
;
@@ -3612,170 +3227,127 @@ method_call : fcall paren_args
/*%%%*/
$$ = $1;
$$->nd_args = $2;
- /*%
- $$ = method_arg(dispatch1(fcall, $1), $2);
- %*/
- }
- | primary_value call_op operation2
- {
- /*%%%*/
- $<num>$ = ruby_sourceline;
+ nd_set_last_loc($1, @2.end_pos);
/*% %*/
+ /*% ripper: method_add_arg!(fcall!($1), $2) %*/
}
- opt_paren_args
- {
- /*%%%*/
- $$ = NEW_QCALL($2, $1, $3, $5);
- nd_set_line($$, $<num>4);
- /*%
- $$ = dispatch3(call, $1, $2, $3);
- $$ = method_optarg($$, $5);
- %*/
- }
- | primary_value tCOLON2 operation2
+ | primary_value call_op operation2 opt_paren_args
{
/*%%%*/
- $<num>$ = ruby_sourceline;
+ $$ = new_qcall(p, $2, $1, $3, $4, &@3, &@$);
+ nd_set_line($$, @3.end_pos.lineno);
/*% %*/
+ /*% ripper: opt_event(:method_add_arg!, call!($1, $2, $3), $4) %*/
}
- paren_args
+ | primary_value tCOLON2 operation2 paren_args
{
/*%%%*/
- $$ = NEW_CALL($1, $3, $5);
- nd_set_line($$, $<num>4);
- /*%
- $$ = dispatch3(call, $1, ripper_id2sym(idCOLON2), $3);
- $$ = method_optarg($$, $5);
- %*/
+ $$ = 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) %*/
}
| primary_value tCOLON2 operation3
{
/*%%%*/
- $$ = NEW_CALL($1, $3, 0);
- /*%
- $$ = dispatch3(call, $1, ID2SYM(idCOLON2), $3);
- %*/
- }
- | primary_value call_op
- {
- /*%%%*/
- $<num>$ = ruby_sourceline;
+ $$ = new_qcall(p, ID2VAL(idCOLON2), $1, $3, Qnull, &@3, &@$);
/*% %*/
+ /*% ripper: call!($1, ID2VAL(idCOLON2), $3) %*/
}
- paren_args
+ | primary_value call_op paren_args
{
/*%%%*/
- $$ = NEW_QCALL($2, $1, idCall, $4);
- nd_set_line($$, $<num>3);
- /*%
- $$ = dispatch3(call, $1, $2, ID2SYM(idCall));
- $$ = method_optarg($$, $4);
- %*/
- }
- | primary_value tCOLON2
- {
- /*%%%*/
- $<num>$ = ruby_sourceline;
+ $$ = new_qcall(p, $2, $1, ID2VAL(idCall), $3, &@2, &@$);
+ nd_set_line($$, @2.end_pos.lineno);
/*% %*/
+ /*% ripper: method_add_arg!(call!($1, $2, ID2VAL(idCall)), $3) %*/
}
- paren_args
+ | primary_value tCOLON2 paren_args
{
/*%%%*/
- $$ = NEW_CALL($1, idCall, $4);
- nd_set_line($$, $<num>3);
- /*%
- $$ = dispatch3(call, $1, ID2SYM(idCOLON2),
- ID2SYM(idCall));
- $$ = method_optarg($$, $4);
- %*/
+ $$ = 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) %*/
}
| keyword_super paren_args
{
/*%%%*/
- $$ = NEW_SUPER($2);
- /*%
- $$ = dispatch1(super, $2);
- %*/
+ $$ = NEW_SUPER($2, &@$);
+ /*% %*/
+ /*% ripper: super!($2) %*/
}
| keyword_super
{
/*%%%*/
- $$ = NEW_ZSUPER();
- /*%
- $$ = dispatch0(zsuper);
- %*/
+ $$ = NEW_ZSUPER(&@$);
+ /*% %*/
+ /*% ripper: zsuper! %*/
}
| primary_value '[' opt_call_args rbracket
{
/*%%%*/
if ($1 && nd_type($1) == NODE_SELF)
- $$ = NEW_FCALL(tAREF, $3);
+ $$ = NEW_FCALL(tAREF, $3, &@$);
else
- $$ = NEW_CALL($1, tAREF, $3);
+ $$ = NEW_CALL($1, tAREF, $3, &@$);
fixpos($$, $1);
- /*%
- $$ = dispatch2(aref, $1, escape_Qundef($3));
- %*/
+ /*% %*/
+ /*% ripper: aref!($1, escape_Qundef($3)) %*/
}
;
-brace_block : '{'
- {
- /*%%%*/
- $<num>$ = ruby_sourceline;
- /*% %*/
- }
- brace_body '}'
- {
- $$ = $3;
- /*%%%*/
- nd_set_line($$, $<num>2);
- /*% %*/
- }
- | keyword_do
+brace_block : '{' brace_body '}'
{
+ $$ = $2;
/*%%%*/
- $<num>$ = ruby_sourceline;
+ $$->nd_body->nd_loc = code_loc_gen(&@1, &@3);
+ nd_set_line($$, @1.end_pos.lineno);
/*% %*/
}
- do_body keyword_end
+ | k_do do_body k_end
{
- $$ = $3;
+ $$ = $2;
/*%%%*/
- nd_set_line($$, $<num>2);
+ $$->nd_body->nd_loc = code_loc_gen(&@1, &@3);
+ nd_set_line($$, @1.end_pos.lineno);
/*% %*/
}
;
-brace_body : {$<vars>$ = dyna_push();}
- {$<val>$ = cmdarg_stack >> 1; CMDARG_SET(0);}
+brace_body : {$<vars>$ = dyna_push(p);}
opt_block_param compstmt
{
- $$ = new_brace_body($3, $4);
- dyna_pop($<vars>1);
- CMDARG_SET($<val>2);
+ /*%%%*/
+ $$ = NEW_ITER($2, $3, &@$);
+ /*% %*/
+ /*% ripper: brace_block!(escape_Qundef($2), $3) %*/
+ dyna_pop(p, $<vars>1);
}
;
-do_body : {$<vars>$ = dyna_push();}
- {$<val>$ = cmdarg_stack >> 1; CMDARG_SET(0);}
- opt_block_param compstmt
+do_body : {$<vars>$ = dyna_push(p);}
+ {CMDARG_PUSH(0);}
+ opt_block_param bodystmt
{
- $$ = new_do_body($3, $4);
- dyna_pop($<vars>1);
- CMDARG_SET($<val>2);
+ /*%%%*/
+ $$ = NEW_ITER($3, $4, &@$);
+ /*% %*/
+ /*% ripper: do_block!(escape_Qundef($3), $4) %*/
+ CMDARG_POP();
+ dyna_pop(p, $<vars>1);
}
;
-case_body : keyword_when args then
+case_body : k_when args then
compstmt
cases
{
/*%%%*/
- $$ = NEW_WHEN($2, $4, $5);
- /*%
- $$ = dispatch3(when, $2, $4, escape_Qundef($5));
- %*/
+ $$ = NEW_WHEN($2, $4, $5, &@$);
+ fixpos($$, $2);
+ /*% %*/
+ /*% ripper: when!($2, $4, escape_Qundef($5)) %*/
}
;
@@ -3783,24 +3355,17 @@ cases : opt_else
| case_body
;
-opt_rescue : keyword_rescue exc_list exc_var then
+opt_rescue : k_rescue exc_list exc_var then
compstmt
opt_rescue
{
/*%%%*/
- if ($3) {
- $3 = node_assign($3, NEW_ERRINFO());
- $5 = block_append($3, $5);
- }
- $$ = NEW_RESBODY($2, $5, $6);
+ $$ = NEW_RESBODY($2,
+ $3 ? block_append(p, node_assign(p, $3, NEW_ERRINFO(&@3), &@3), $5) : $5,
+ $6, &@$);
fixpos($$, $2?$2:$5);
- /*%
- $$ = dispatch4(rescue,
- escape_Qundef($2),
- escape_Qundef($3),
- escape_Qundef($5),
- escape_Qundef($6));
- %*/
+ /*% %*/
+ /*% ripper: rescue!(escape_Qundef($2), escape_Qundef($3), escape_Qundef($5), escape_Qundef($6)) %*/
}
| none
;
@@ -3808,18 +3373,16 @@ opt_rescue : keyword_rescue exc_list exc_var then
exc_list : arg_value
{
/*%%%*/
- $$ = NEW_LIST($1);
- /*%
- $$ = rb_ary_new3(1, $1);
- %*/
+ $$ = NEW_LIST($1, &@$);
+ /*% %*/
+ /*% ripper: rb_ary_new3(1, get_value($1)) %*/
}
| mrhs
{
/*%%%*/
if (!($$ = splat_array($1))) $$ = $1;
- /*%
- $$ = $1;
- %*/
+ /*% %*/
+ /*% ripper: $1 %*/
}
| none
;
@@ -3831,13 +3394,12 @@ exc_var : tASSOC lhs
| none
;
-opt_ensure : keyword_ensure compstmt
+opt_ensure : k_ensure compstmt
{
/*%%%*/
$$ = $2;
- /*%
- $$ = dispatch1(ensure, $2);
- %*/
+ /*% %*/
+ /*% ripper: ensure!($2) %*/
}
| none
;
@@ -3846,10 +3408,9 @@ literal : numeric
| symbol
{
/*%%%*/
- $$ = NEW_LIT(ID2SYM($1));
- /*%
- $$ = dispatch1(symbol_literal, $1);
- %*/
+ $$ = NEW_LIT(ID2SYM($1), &@$);
+ /*% %*/
+ /*% ripper: symbol_literal!($1) %*/
}
| dsym
;
@@ -3859,15 +3420,14 @@ strings : string
/*%%%*/
NODE *node = $1;
if (!node) {
- node = NEW_STR(STR_NEW0());
+ node = NEW_STR(add_mark_object(p, STR_NEW0()), &@$);
}
else {
- node = evstr2dstr(node);
+ node = evstr2dstr(p, node);
}
$$ = node;
- /*%
- $$ = $1;
- %*/
+ /*% %*/
+ /*% ripper: $1 %*/
}
;
@@ -3876,47 +3436,43 @@ string : tCHAR
| string string1
{
/*%%%*/
- $$ = literal_concat($1, $2);
- /*%
- $$ = dispatch2(string_concat, $1, $2);
- %*/
+ $$ = literal_concat(p, $1, $2, &@$);
+ /*% %*/
+ /*% ripper: string_concat!($1, $2) %*/
}
;
string1 : tSTRING_BEG string_contents tSTRING_END
{
- $$ = new_string1(heredoc_dedent($2));
+ /*%%%*/
+ $$ = heredoc_dedent(p, $2);
+ if ($$) nd_set_loc($$, &@$);
+ /*% %*/
+ /*% ripper: string_literal!(heredoc_dedent(p, $2)) %*/
}
;
xstring : tXSTRING_BEG xstring_contents tSTRING_END
{
- $$ = new_xstring(heredoc_dedent($2));
+ /*%%%*/
+ $$ = new_xstring(p, heredoc_dedent(p, $2), &@$);
+ /*% %*/
+ /*% ripper: xstring_literal!(heredoc_dedent(p, $2)) %*/
}
;
regexp : tREGEXP_BEG regexp_contents tREGEXP_END
{
- $$ = new_regexp($2, $3);
+ $$ = new_regexp(p, $2, $3, &@$);
}
;
-words : tWORDS_BEG ' ' tSTRING_END
+words : tWORDS_BEG ' ' word_list tSTRING_END
{
/*%%%*/
- $$ = NEW_ZARRAY();
- /*%
- $$ = dispatch0(words_new);
- $$ = dispatch1(array, $$);
- %*/
- }
- | tWORDS_BEG word_list tSTRING_END
- {
- /*%%%*/
- $$ = $2;
- /*%
- $$ = dispatch1(array, $2);
- %*/
+ $$ = make_array($3, &@$);
+ /*% %*/
+ /*% ripper: array!($3) %*/
}
;
@@ -3924,54 +3480,35 @@ word_list : /* none */
{
/*%%%*/
$$ = 0;
- /*%
- $$ = dispatch0(words_new);
- %*/
+ /*% %*/
+ /*% ripper: words_new! %*/
}
| word_list word ' '
{
/*%%%*/
- $$ = list_append($1, evstr2dstr($2));
- /*%
- $$ = dispatch2(words_add, $1, $2);
- %*/
+ $$ = list_append(p, $1, evstr2dstr(p, $2));
+ /*% %*/
+ /*% ripper: words_add!($1, $2) %*/
}
;
word : string_content
- /*%c%*/
- /*%c
- {
- $$ = dispatch0(word_new);
- $$ = dispatch2(word_add, $$, $1);
- }
- %*/
+ /*% ripper[brace]: word_add!(word_new!, $1) %*/
| word string_content
{
/*%%%*/
- $$ = literal_concat($1, $2);
- /*%
- $$ = dispatch2(word_add, $1, $2);
- %*/
+ $$ = literal_concat(p, $1, $2, &@$);
+ /*% %*/
+ /*% ripper: word_add!($1, $2) %*/
}
;
-symbols : tSYMBOLS_BEG ' ' tSTRING_END
+symbols : tSYMBOLS_BEG ' ' symbol_list tSTRING_END
{
/*%%%*/
- $$ = NEW_ZARRAY();
- /*%
- $$ = dispatch0(symbols_new);
- $$ = dispatch1(array, $$);
- %*/
- }
- | tSYMBOLS_BEG symbol_list tSTRING_END
- {
- /*%%%*/
- $$ = $2;
- /*%
- $$ = dispatch1(array, $2);
- %*/
+ $$ = make_array($3, &@$);
+ /*% %*/
+ /*% ripper: array!($3) %*/
}
;
@@ -3979,63 +3516,33 @@ symbol_list : /* none */
{
/*%%%*/
$$ = 0;
- /*%
- $$ = dispatch0(symbols_new);
- %*/
+ /*% %*/
+ /*% ripper: symbols_new! %*/
}
| symbol_list word ' '
{
/*%%%*/
- $2 = evstr2dstr($2);
- if (nd_type($2) == NODE_DSTR) {
- nd_set_type($2, NODE_DSYM);
- }
- else {
- nd_set_type($2, NODE_LIT);
- $2->nd_lit = rb_str_intern($2->nd_lit);
- }
- $$ = list_append($1, $2);
- /*%
- $$ = dispatch2(symbols_add, $1, $2);
- %*/
+ $$ = symbol_append(p, $1, evstr2dstr(p, $2));
+ /*% %*/
+ /*% ripper: symbols_add!($1, $2) %*/
}
;
-qwords : tQWORDS_BEG ' ' tSTRING_END
+qwords : tQWORDS_BEG ' ' qword_list tSTRING_END
{
/*%%%*/
- $$ = NEW_ZARRAY();
- /*%
- $$ = dispatch0(qwords_new);
- $$ = dispatch1(array, $$);
- %*/
- }
- | tQWORDS_BEG qword_list tSTRING_END
- {
- /*%%%*/
- $$ = $2;
- /*%
- $$ = dispatch1(array, $2);
- %*/
+ $$ = make_array($3, &@$);
+ /*% %*/
+ /*% ripper: array!($3) %*/
}
;
-qsymbols : tQSYMBOLS_BEG ' ' tSTRING_END
- {
- /*%%%*/
- $$ = NEW_ZARRAY();
- /*%
- $$ = dispatch0(qsymbols_new);
- $$ = dispatch1(array, $$);
- %*/
- }
- | tQSYMBOLS_BEG qsym_list tSTRING_END
+qsymbols : tQSYMBOLS_BEG ' ' qsym_list tSTRING_END
{
/*%%%*/
- $$ = $2;
- /*%
- $$ = dispatch1(array, $2);
- %*/
+ $$ = make_array($3, &@$);
+ /*% %*/
+ /*% ripper: array!($3) %*/
}
;
@@ -4043,17 +3550,15 @@ qword_list : /* none */
{
/*%%%*/
$$ = 0;
- /*%
- $$ = dispatch0(qwords_new);
- %*/
+ /*% %*/
+ /*% ripper: qwords_new! %*/
}
| qword_list tSTRING_CONTENT ' '
{
/*%%%*/
- $$ = list_append($1, $2);
- /*%
- $$ = dispatch2(qwords_add, $1, $2);
- %*/
+ $$ = list_append(p, $1, $2);
+ /*% %*/
+ /*% ripper: qwords_add!($1, $2) %*/
}
;
@@ -4061,21 +3566,15 @@ qsym_list : /* none */
{
/*%%%*/
$$ = 0;
- /*%
- $$ = dispatch0(qsymbols_new);
- %*/
+ /*% %*/
+ /*% ripper: qsymbols_new! %*/
}
| qsym_list tSTRING_CONTENT ' '
{
/*%%%*/
- VALUE lit;
- lit = $2->nd_lit;
- $2->nd_lit = ID2SYM(rb_intern_str(lit));
- nd_set_type($2, NODE_LIT);
- $$ = list_append($1, $2);
- /*%
- $$ = dispatch2(qsymbols_add, $1, $2);
- %*/
+ $$ = symbol_append(p, $1, $2);
+ /*% %*/
+ /*% ripper: qsymbols_add!($1, $2) %*/
}
;
@@ -4083,17 +3582,15 @@ string_contents : /* none */
{
/*%%%*/
$$ = 0;
- /*%
- $$ = dispatch0(string_content);
- %*/
+ /*% %*/
+ /*% ripper: string_content! %*/
}
| string_contents string_content
{
/*%%%*/
- $$ = literal_concat($1, $2);
- /*%
- $$ = dispatch2(string_add, $1, $2);
- %*/
+ $$ = literal_concat(p, $1, $2, &@$);
+ /*% %*/
+ /*% ripper: string_add!($1, $2) %*/
}
;
@@ -4101,17 +3598,15 @@ xstring_contents: /* none */
{
/*%%%*/
$$ = 0;
- /*%
- $$ = dispatch0(xstring_new);
- %*/
+ /*% %*/
+ /*% ripper: xstring_new! %*/
}
| xstring_contents string_content
{
/*%%%*/
- $$ = literal_concat($1, $2);
- /*%
- $$ = dispatch2(xstring_add, $1, $2);
- %*/
+ $$ = literal_concat(p, $1, $2, &@$);
+ /*% %*/
+ /*% ripper: xstring_add!($1, $2) %*/
}
;
@@ -4119,8 +3614,11 @@ regexp_contents: /* none */
{
/*%%%*/
$$ = 0;
+ /*% %*/
+ /*% ripper: regexp_new! %*/
+ /*%%%*/
/*%
- $$ = ripper_new_yylval(0, dispatch0(regexp_new), 0);
+ $$ = ripper_new_yylval(p, 0, $$, 0);
%*/
}
| regexp_contents string_content
@@ -4141,10 +3639,10 @@ regexp_contents: /* none */
case NODE_DSTR:
break;
default:
- head = list_append(NEW_DSTR(Qnil), head);
+ head = list_append(p, NEW_DSTR(Qnil, &@$), head);
break;
}
- $$ = list_append(head, tail);
+ $$ = list_append(p, head, tail);
}
/*%
VALUE s1 = 1, s2 = 0, n1 = $1, n2 = $2;
@@ -4158,7 +3656,7 @@ regexp_contents: /* none */
}
$$ = dispatch2(regexp_add, n1, n2);
if (!s1 && s2) {
- $$ = ripper_new_yylval(0, $$, s2);
+ $$ = ripper_new_yylval(p, 0, $$, s2);
}
%*/
}
@@ -4167,95 +3665,90 @@ regexp_contents: /* none */
string_content : tSTRING_CONTENT
| tSTRING_DVAR
{
- $<node>$ = lex_strterm;
- lex_strterm = 0;
+ /* need to backup p->lex.strterm so that a string literal `%&foo,#$&,bar&` can be parsed */
+ $<strterm>$ = p->lex.strterm;
+ p->lex.strterm = 0;
SET_LEX_STATE(EXPR_BEG);
}
string_dvar
{
- lex_strterm = $<node>2;
+ p->lex.strterm = $<strterm>2;
/*%%%*/
- $$ = NEW_EVSTR($3);
- /*%
- $$ = dispatch1(string_dvar, $3);
- %*/
+ $$ = NEW_EVSTR($3, &@$);
+ nd_set_line($$, @3.end_pos.lineno);
+ /*% %*/
+ /*% ripper: string_dvar!($3) %*/
}
| tSTRING_DBEG
{
- $<val>1 = cond_stack;
- $<val>$ = cmdarg_stack;
- COND_SET(0);
- CMDARG_SET(0);
+ CMDARG_PUSH(0);
+ COND_PUSH(0);
}
{
- $<node>$ = lex_strterm;
- lex_strterm = 0;
+ /* need to backup p->lex.strterm so that a string literal `%!foo,#{ !0 },bar!` can be parsed */
+ $<strterm>$ = p->lex.strterm;
+ p->lex.strterm = 0;
}
{
- $<num>$ = lex_state;
+ $<num>$ = p->lex.state;
SET_LEX_STATE(EXPR_BEG);
}
{
- $<num>$ = brace_nest;
- brace_nest = 0;
+ $<num>$ = p->lex.brace_nest;
+ p->lex.brace_nest = 0;
}
{
- $<num>$ = heredoc_indent;
- heredoc_indent = 0;
+ $<num>$ = p->heredoc_indent;
+ p->heredoc_indent = 0;
}
compstmt tSTRING_DEND
{
- COND_SET($<val>1);
- CMDARG_SET($<val>2);
- lex_strterm = $<node>3;
+ COND_POP();
+ CMDARG_POP();
+ p->lex.strterm = $<strterm>3;
SET_LEX_STATE($<num>4);
- brace_nest = $<num>5;
- heredoc_indent = $<num>6;
- heredoc_line_indent = -1;
+ p->lex.brace_nest = $<num>5;
+ p->heredoc_indent = $<num>6;
+ p->heredoc_line_indent = -1;
/*%%%*/
if ($7) $7->flags &= ~NODE_FL_NEWLINE;
- $$ = new_evstr($7);
- /*%
- $$ = dispatch1(string_embexpr, $7);
- %*/
+ $$ = new_evstr(p, $7, &@$);
+ /*% %*/
+ /*% ripper: string_embexpr!($7) %*/
}
;
string_dvar : tGVAR
{
/*%%%*/
- $$ = NEW_GVAR($1);
- /*%
- $$ = dispatch1(var_ref, $1);
- %*/
+ $$ = NEW_GVAR($1, &@$);
+ /*% %*/
+ /*% ripper: var_ref!($1) %*/
}
| tIVAR
{
/*%%%*/
- $$ = NEW_IVAR($1);
- /*%
- $$ = dispatch1(var_ref, $1);
- %*/
+ $$ = NEW_IVAR($1, &@$);
+ /*% %*/
+ /*% ripper: var_ref!($1) %*/
}
| tCVAR
{
/*%%%*/
- $$ = NEW_CVAR($1);
- /*%
- $$ = dispatch1(var_ref, $1);
- %*/
+ $$ = NEW_CVAR($1, &@$);
+ /*% %*/
+ /*% ripper: var_ref!($1) %*/
}
| backref
;
symbol : tSYMBEG sym
{
- SET_LEX_STATE(EXPR_ENDARG);
+ SET_LEX_STATE(EXPR_END);
/*%%%*/
$$ = $2;
- /*%
- $$ = dispatch1(symbol, $2);
- %*/
+ /*% %*/
+ /*% ripper: symbol!($2) %*/
}
;
@@ -4265,14 +3758,13 @@ sym : fname
| tCVAR
;
-dsym : tSYMBEG xstring_contents tSTRING_END
+dsym : tSYMBEG string_contents tSTRING_END
{
- SET_LEX_STATE(EXPR_ENDARG);
+ SET_LEX_STATE(EXPR_END);
/*%%%*/
- $$ = dsym_node($2);
- /*%
- $$ = dispatch1(dyna_symbol, $2);
- %*/
+ $$ = dsym_node(p, $2, &@$);
+ /*% %*/
+ /*% ripper: dyna_symbol!($2) %*/
}
;
@@ -4281,10 +3773,9 @@ numeric : simple_numeric
{
/*%%%*/
$$ = $2;
- $$->nd_lit = negate_lit($$->nd_lit);
- /*%
- $$ = dispatch2(unary, ID2SYM(idUMinus), $2);
- %*/
+ add_mark_object(p, $$->nd_lit = negate_lit(p, $$->nd_lit));
+ /*% %*/
+ /*% ripper: unary!(ID2VAL(idUMinus), $2) %*/
}
;
@@ -4301,21 +3792,21 @@ user_variable : tIDENTIFIER
| tCVAR
;
-keyword_variable: keyword_nil {ifndef_ripper($$ = keyword_nil);}
- | keyword_self {ifndef_ripper($$ = keyword_self);}
- | keyword_true {ifndef_ripper($$ = keyword_true);}
- | keyword_false {ifndef_ripper($$ = keyword_false);}
- | keyword__FILE__ {ifndef_ripper($$ = keyword__FILE__);}
- | keyword__LINE__ {ifndef_ripper($$ = keyword__LINE__);}
- | keyword__ENCODING__ {ifndef_ripper($$ = keyword__ENCODING__);}
+keyword_variable: keyword_nil {$$ = KWD2EID(nil, $1);}
+ | keyword_self {$$ = KWD2EID(self, $1);}
+ | keyword_true {$$ = KWD2EID(true, $1);}
+ | keyword_false {$$ = KWD2EID(false, $1);}
+ | keyword__FILE__ {$$ = KWD2EID(_FILE__, $1);}
+ | keyword__LINE__ {$$ = KWD2EID(_LINE__, $1);}
+ | keyword__ENCODING__ {$$ = KWD2EID(_ENCODING__, $1);}
;
var_ref : user_variable
{
/*%%%*/
- if (!($$ = gettable($1))) $$ = NEW_BEGIN(0);
+ if (!($$ = gettable(p, $1, &@$))) $$ = NEW_BEGIN(0, &@$);
/*%
- if (id_is_var(get_id($1))) {
+ if (id_is_var(p, get_id($1))) {
$$ = dispatch1(var_ref, $1);
}
else {
@@ -4326,28 +3817,25 @@ var_ref : user_variable
| keyword_variable
{
/*%%%*/
- if (!($$ = gettable($1))) $$ = NEW_BEGIN(0);
- /*%
- $$ = dispatch1(var_ref, $1);
- %*/
+ if (!($$ = gettable(p, $1, &@$))) $$ = NEW_BEGIN(0, &@$);
+ /*% %*/
+ /*% ripper: var_ref!($1) %*/
}
;
var_lhs : user_variable
{
- $$ = assignable($1, 0);
/*%%%*/
- /*%
- $$ = dispatch1(var_field, $$);
- %*/
+ $$ = assignable(p, $1, 0, &@$);
+ /*% %*/
+ /*% ripper: assignable(p, var_field(p, $1)) %*/
}
| keyword_variable
{
- $$ = assignable($1, 0);
/*%%%*/
- /*%
- $$ = dispatch1(var_field, $$);
- %*/
+ $$ = assignable(p, $1, 0, &@$);
+ /*% %*/
+ /*% ripper: assignable(p, var_field(p, $1)) %*/
}
;
@@ -4358,7 +3846,7 @@ backref : tNTH_REF
superclass : '<'
{
SET_LEX_STATE(EXPR_BEG);
- command_start = TRUE;
+ p->command_start = TRUE;
}
expr_value term
{
@@ -4368,9 +3856,8 @@ superclass : '<'
{
/*%%%*/
$$ = 0;
- /*%
- $$ = Qnil;
- %*/
+ /*% %*/
+ /*% ripper: Qnil %*/
}
;
@@ -4378,41 +3865,40 @@ f_arglist : '(' f_args rparen
{
/*%%%*/
$$ = $2;
- /*%
- $$ = dispatch1(paren, $2);
- %*/
+ /*% %*/
+ /*% ripper: paren!($2) %*/
SET_LEX_STATE(EXPR_BEG);
- command_start = TRUE;
+ p->command_start = TRUE;
}
| {
- $<num>$ = parser->in_kwarg;
- parser->in_kwarg = 1;
- lex_state |= EXPR_LABEL; /* force for args */
+ $<num>$ = p->in_kwarg;
+ p->in_kwarg = 1;
+ SET_LEX_STATE(p->lex.state|EXPR_LABEL); /* force for args */
}
f_args term
{
- parser->in_kwarg = !!$<num>1;
+ p->in_kwarg = !!$<num>1;
$$ = $2;
SET_LEX_STATE(EXPR_BEG);
- command_start = TRUE;
+ p->command_start = TRUE;
}
;
args_tail : f_kwarg ',' f_kwrest opt_f_block_arg
{
- $$ = new_args_tail($1, $3, $4);
+ $$ = new_args_tail(p, $1, $3, $4, &@3);
}
| f_kwarg opt_f_block_arg
{
- $$ = new_args_tail($1, Qnone, $2);
+ $$ = new_args_tail(p, $1, Qnone, $2, &@1);
}
| f_kwrest opt_f_block_arg
{
- $$ = new_args_tail(Qnone, $1, $2);
+ $$ = new_args_tail(p, Qnone, $1, $2, &@1);
}
| f_block_arg
{
- $$ = new_args_tail(Qnone, Qnone, $1);
+ $$ = new_args_tail(p, Qnone, Qnone, $1, &@1);
}
;
@@ -4422,119 +3908,111 @@ opt_args_tail : ',' args_tail
}
| /* none */
{
- $$ = new_args_tail(Qnone, Qnone, Qnone);
+ $$ = new_args_tail(p, Qnone, Qnone, Qnone, &@0);
}
;
f_args : f_arg ',' f_optarg ',' f_rest_arg opt_args_tail
{
- $$ = new_args($1, $3, $5, Qnone, $6);
+ $$ = new_args(p, $1, $3, $5, Qnone, $6, &@$);
}
| f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_args_tail
{
- $$ = new_args($1, $3, $5, $7, $8);
+ $$ = new_args(p, $1, $3, $5, $7, $8, &@$);
}
| f_arg ',' f_optarg opt_args_tail
{
- $$ = new_args($1, $3, Qnone, Qnone, $4);
+ $$ = new_args(p, $1, $3, Qnone, Qnone, $4, &@$);
}
| f_arg ',' f_optarg ',' f_arg opt_args_tail
{
- $$ = new_args($1, $3, Qnone, $5, $6);
+ $$ = new_args(p, $1, $3, Qnone, $5, $6, &@$);
}
| f_arg ',' f_rest_arg opt_args_tail
{
- $$ = new_args($1, Qnone, $3, Qnone, $4);
+ $$ = new_args(p, $1, Qnone, $3, Qnone, $4, &@$);
}
| f_arg ',' f_rest_arg ',' f_arg opt_args_tail
{
- $$ = new_args($1, Qnone, $3, $5, $6);
+ $$ = new_args(p, $1, Qnone, $3, $5, $6, &@$);
}
| f_arg opt_args_tail
{
- $$ = new_args($1, Qnone, Qnone, Qnone, $2);
+ $$ = new_args(p, $1, Qnone, Qnone, Qnone, $2, &@$);
}
| f_optarg ',' f_rest_arg opt_args_tail
{
- $$ = new_args(Qnone, $1, $3, Qnone, $4);
+ $$ = new_args(p, Qnone, $1, $3, Qnone, $4, &@$);
}
| f_optarg ',' f_rest_arg ',' f_arg opt_args_tail
{
- $$ = new_args(Qnone, $1, $3, $5, $6);
+ $$ = new_args(p, Qnone, $1, $3, $5, $6, &@$);
}
| f_optarg opt_args_tail
{
- $$ = new_args(Qnone, $1, Qnone, Qnone, $2);
+ $$ = new_args(p, Qnone, $1, Qnone, Qnone, $2, &@$);
}
| f_optarg ',' f_arg opt_args_tail
{
- $$ = new_args(Qnone, $1, Qnone, $3, $4);
+ $$ = new_args(p, Qnone, $1, Qnone, $3, $4, &@$);
}
| f_rest_arg opt_args_tail
{
- $$ = new_args(Qnone, Qnone, $1, Qnone, $2);
+ $$ = new_args(p, Qnone, Qnone, $1, Qnone, $2, &@$);
}
| f_rest_arg ',' f_arg opt_args_tail
{
- $$ = new_args(Qnone, Qnone, $1, $3, $4);
+ $$ = new_args(p, Qnone, Qnone, $1, $3, $4, &@$);
}
| args_tail
{
- $$ = new_args(Qnone, Qnone, Qnone, Qnone, $1);
+ $$ = new_args(p, Qnone, Qnone, Qnone, Qnone, $1, &@$);
}
| /* none */
{
- $$ = new_args_tail(Qnone, Qnone, Qnone);
- $$ = new_args(Qnone, Qnone, Qnone, Qnone, $$);
+ $$ = new_args_tail(p, Qnone, Qnone, Qnone, &@0);
+ $$ = new_args(p, Qnone, Qnone, Qnone, Qnone, $$, &@0);
}
;
f_bad_arg : tCONSTANT
{
/*%%%*/
- yyerror("formal argument cannot be a constant");
+ yyerror1(&@1, "formal argument cannot be a constant");
$$ = 0;
- /*%
- $$ = dispatch1(param_error, $1);
- ripper_error();
- %*/
+ /*% %*/
+ /*% ripper[error]: param_error!($1) %*/
}
| tIVAR
{
/*%%%*/
- yyerror("formal argument cannot be an instance variable");
+ yyerror1(&@1, "formal argument cannot be an instance variable");
$$ = 0;
- /*%
- $$ = dispatch1(param_error, $1);
- ripper_error();
- %*/
+ /*% %*/
+ /*% ripper[error]: param_error!($1) %*/
}
| tGVAR
{
/*%%%*/
- yyerror("formal argument cannot be a global variable");
+ yyerror1(&@1, "formal argument cannot be a global variable");
$$ = 0;
- /*%
- $$ = dispatch1(param_error, $1);
- ripper_error();
- %*/
+ /*% %*/
+ /*% ripper[error]: param_error!($1) %*/
}
| tCVAR
{
/*%%%*/
- yyerror("formal argument cannot be a class variable");
+ yyerror1(&@1, "formal argument cannot be a class variable");
$$ = 0;
- /*%
- $$ = dispatch1(param_error, $1);
- ripper_error();
- %*/
+ /*% %*/
+ /*% ripper[error]: param_error!($1) %*/
}
;
f_norm_arg : f_bad_arg
| tIDENTIFIER
{
- formal_argument(get_id($1));
+ formal_argument(p, get_id($1));
$$ = $1;
}
;
@@ -4542,57 +4020,54 @@ f_norm_arg : f_bad_arg
f_arg_asgn : f_norm_arg
{
ID id = get_id($1);
- arg_var(id);
- current_arg = id;
+ arg_var(p, id);
+ p->cur_arg = id;
$$ = $1;
}
;
f_arg_item : f_arg_asgn
{
- current_arg = 0;
+ p->cur_arg = 0;
/*%%%*/
- $$ = NEW_ARGS_AUX($1, 1);
- /*%
- $$ = get_value($1);
- %*/
+ $$ = NEW_ARGS_AUX($1, 1, &NULL_LOC);
+ /*% %*/
+ /*% ripper: get_value($1) %*/
}
| tLPAREN f_margs rparen
{
- ID tid = internal_id();
- arg_var(tid);
+ ID tid = internal_id(p);
/*%%%*/
- if (dyna_in_block()) {
- $2->nd_value = NEW_DVAR(tid);
+ YYLTYPE loc;
+ loc.beg_pos = @2.beg_pos;
+ loc.end_pos = @2.beg_pos;
+ /*% %*/
+ arg_var(p, tid);
+ /*%%%*/
+ if (dyna_in_block(p)) {
+ $2->nd_value = NEW_DVAR(tid, &loc);
}
else {
- $2->nd_value = NEW_LVAR(tid);
+ $2->nd_value = NEW_LVAR(tid, &loc);
}
- $$ = NEW_ARGS_AUX(tid, 1);
+ $$ = NEW_ARGS_AUX(tid, 1, &NULL_LOC);
$$->nd_next = $2;
- /*%
- $$ = dispatch1(mlhs_paren, $2);
- %*/
+ /*% %*/
+ /*% ripper: mlhs_paren!($2) %*/
}
;
f_arg : f_arg_item
- /*%c%*/
- /*%c
- {
- $$ = rb_ary_new3(1, $1);
- }
- c%*/
+ /*% ripper[brace]: rb_ary_new3(1, get_value($1)) %*/
| f_arg ',' f_arg_item
{
/*%%%*/
$$ = $1;
$$->nd_plen++;
- $$->nd_next = block_append($$->nd_next, $3->nd_next);
- rb_gc_force_recycle((VALUE)$3);
- /*%
- $$ = rb_ary_push($1, $3);
- %*/
+ $$->nd_next = block_append(p, $$->nd_next, $3->nd_next);
+ rb_discard_node(p, $3);
+ /*% %*/
+ /*% ripper: rb_ary_push($1, get_value($3)) %*/
}
;
@@ -4600,51 +4075,43 @@ f_arg : f_arg_item
f_label : tLABEL
{
ID id = get_id($1);
- arg_var(formal_argument(id));
- current_arg = id;
+ arg_var(p, formal_argument(p, id));
+ p->cur_arg = id;
$$ = $1;
}
;
f_kw : f_label arg_value
{
- current_arg = 0;
- $$ = assignable($1, $2);
+ p->cur_arg = 0;
/*%%%*/
- $$ = new_kw_arg($$);
- /*%
- $$ = rb_assoc_new($$, $2);
- %*/
+ $$ = new_kw_arg(p, assignable(p, $1, $2, &@$), &@$);
+ /*% %*/
+ /*% ripper: rb_assoc_new(get_value(assignable(p, $1)), get_value($2)) %*/
}
| f_label
{
- current_arg = 0;
- $$ = assignable($1, (NODE *)-1);
+ p->cur_arg = 0;
/*%%%*/
- $$ = new_kw_arg($$);
- /*%
- $$ = rb_assoc_new($$, 0);
- %*/
+ $$ = new_kw_arg(p, assignable(p, $1, NODE_SPECIAL_REQUIRED_KEYWORD, &@$), &@$);
+ /*% %*/
+ /*% ripper: rb_assoc_new(get_value(assignable(p, $1)), 0) %*/
}
;
f_block_kw : f_label primary_value
{
- $$ = assignable($1, $2);
/*%%%*/
- $$ = new_kw_arg($$);
- /*%
- $$ = rb_assoc_new($$, $2);
- %*/
+ $$ = new_kw_arg(p, assignable(p, $1, $2, &@$), &@$);
+ /*% %*/
+ /*% ripper: rb_assoc_new(get_value(assignable(p, $1)), get_value($2)) %*/
}
| f_label
{
- $$ = assignable($1, (NODE *)-1);
/*%%%*/
- $$ = new_kw_arg($$);
- /*%
- $$ = rb_assoc_new($$, 0);
- %*/
+ $$ = new_kw_arg(p, assignable(p, $1, NODE_SPECIAL_REQUIRED_KEYWORD, &@$), &@$);
+ /*% %*/
+ /*% ripper: rb_assoc_new(get_value(assignable(p, $1)), 0) %*/
}
;
@@ -4652,17 +4119,15 @@ f_block_kwarg : f_block_kw
{
/*%%%*/
$$ = $1;
- /*%
- $$ = rb_ary_new3(1, $1);
- %*/
+ /*% %*/
+ /*% ripper: rb_ary_new3(1, get_value($1)) %*/
}
| f_block_kwarg ',' f_block_kw
{
/*%%%*/
$$ = kwd_append($1, $3);
- /*%
- $$ = rb_ary_push($1, $3);
- %*/
+ /*% %*/
+ /*% ripper: rb_ary_push($1, get_value($3)) %*/
}
;
@@ -4671,17 +4136,15 @@ f_kwarg : f_kw
{
/*%%%*/
$$ = $1;
- /*%
- $$ = rb_ary_new3(1, $1);
- %*/
+ /*% %*/
+ /*% ripper: rb_ary_new3(1, get_value($1)) %*/
}
| f_kwarg ',' f_kw
{
/*%%%*/
$$ = kwd_append($1, $3);
- /*%
- $$ = rb_ary_push($1, $3);
- %*/
+ /*% %*/
+ /*% ripper: rb_ary_push($1, get_value($3)) %*/
}
;
@@ -4691,37 +4154,39 @@ kwrest_mark : tPOW
f_kwrest : kwrest_mark tIDENTIFIER
{
- shadowing_lvar(get_id($2));
+ arg_var(p, shadowing_lvar(p, get_id($2)));
+ /*%%%*/
$$ = $2;
+ /*% %*/
+ /*% ripper: kwrest_param!($2) %*/
}
| kwrest_mark
{
- $$ = internal_id();
- arg_var($$);
+ /*%%%*/
+ $$ = internal_id(p);
+ arg_var(p, $$);
+ /*% %*/
+ /*% ripper: kwrest_param!(Qnil) %*/
}
;
f_opt : f_arg_asgn '=' arg_value
{
- current_arg = 0;
- $$ = assignable($1, $3);
+ p->cur_arg = 0;
/*%%%*/
- $$ = NEW_OPT_ARG(0, $$);
- /*%
- $$ = rb_assoc_new($$, $3);
- %*/
+ $$ = NEW_OPT_ARG(0, assignable(p, $1, $3, &@$), &@$);
+ /*% %*/
+ /*% ripper: rb_assoc_new(get_value(assignable(p, $1)), get_value($3)) %*/
}
;
f_block_opt : f_arg_asgn '=' primary_value
{
- current_arg = 0;
- $$ = assignable($1, $3);
+ p->cur_arg = 0;
/*%%%*/
- $$ = NEW_OPT_ARG(0, $$);
- /*%
- $$ = rb_assoc_new($$, $3);
- %*/
+ $$ = NEW_OPT_ARG(0, assignable(p, $1, $3, &@$), &@$);
+ /*% %*/
+ /*% ripper: rb_assoc_new(get_value(assignable(p, $1)), get_value($3)) %*/
}
;
@@ -4729,23 +4194,15 @@ f_block_optarg : f_block_opt
{
/*%%%*/
$$ = $1;
- /*%
- $$ = rb_ary_new3(1, $1);
- %*/
+ /*% %*/
+ /*% ripper: rb_ary_new3(1, get_value($1)) %*/
}
| f_block_optarg ',' f_block_opt
{
/*%%%*/
- NODE *opts = $1;
-
- while (opts->nd_next) {
- opts = opts->nd_next;
- }
- opts->nd_next = $3;
- $$ = $1;
- /*%
- $$ = rb_ary_push($1, $3);
- %*/
+ $$ = opt_arg_append($1, $3);
+ /*% %*/
+ /*% ripper: rb_ary_push($1, get_value($3)) %*/
}
;
@@ -4753,23 +4210,15 @@ f_optarg : f_opt
{
/*%%%*/
$$ = $1;
- /*%
- $$ = rb_ary_new3(1, $1);
- %*/
+ /*% %*/
+ /*% ripper: rb_ary_new3(1, get_value($1)) %*/
}
| f_optarg ',' f_opt
{
/*%%%*/
- NODE *opts = $1;
-
- while (opts->nd_next) {
- opts = opts->nd_next;
- }
- opts->nd_next = $3;
- $$ = $1;
- /*%
- $$ = rb_ary_push($1, $3);
- %*/
+ $$ = opt_arg_append($1, $3);
+ /*% %*/
+ /*% ripper: rb_ary_push($1, get_value($3)) %*/
}
;
@@ -4779,25 +4228,19 @@ restarg_mark : '*'
f_rest_arg : restarg_mark tIDENTIFIER
{
- /*%%%*/
- if (!is_local_id($2))
- yyerror("rest argument must be local variable");
- /*% %*/
- arg_var(shadowing_lvar(get_id($2)));
+ arg_var(p, shadowing_lvar(p, get_id($2)));
/*%%%*/
$$ = $2;
- /*%
- $$ = dispatch1(rest_param, $2);
- %*/
+ /*% %*/
+ /*% ripper: rest_param!($2) %*/
}
| restarg_mark
{
/*%%%*/
- $$ = internal_id();
- arg_var($$);
- /*%
- $$ = dispatch1(rest_param, Qnil);
- %*/
+ $$ = internal_id(p);
+ arg_var(p, $$);
+ /*% %*/
+ /*% ripper: rest_param!(Qnil) %*/
}
;
@@ -4807,18 +4250,11 @@ blkarg_mark : '&'
f_block_arg : blkarg_mark tIDENTIFIER
{
- /*%%%*/
- if (!is_local_id($2))
- yyerror("block argument must be local variable");
- else if (!dyna_in_block() && local_id($2))
- yyerror("duplicated block argument name");
- /*% %*/
- arg_var(shadowing_lvar(get_id($2)));
+ arg_var(p, shadowing_lvar(p, get_id($2)));
/*%%%*/
$$ = $2;
- /*%
- $$ = dispatch1(blockarg, $2);
- %*/
+ /*% %*/
+ /*% ripper: blockarg!($2) %*/
}
;
@@ -4830,48 +4266,37 @@ opt_f_block_arg : ',' f_block_arg
{
/*%%%*/
$$ = 0;
- /*%
- $$ = Qundef;
- %*/
+ /*% %*/
+ /*% ripper: Qundef %*/
}
;
singleton : var_ref
{
- /*%%%*/
value_expr($1);
$$ = $1;
- if (!$$) $$ = NEW_NIL();
- /*%
- $$ = $1;
- %*/
}
| '(' {SET_LEX_STATE(EXPR_BEG);} expr rparen
{
/*%%%*/
- if ($3 == 0) {
- yyerror("can't define singleton method for ().");
- }
- else {
- switch (nd_type($3)) {
- case NODE_STR:
- case NODE_DSTR:
- case NODE_XSTR:
- case NODE_DXSTR:
- case NODE_DREGX:
- case NODE_LIT:
- case NODE_ARRAY:
- case NODE_ZARRAY:
- yyerror("can't define singleton method for literals");
- default:
- value_expr($3);
- break;
- }
+ switch (nd_type($3)) {
+ case NODE_STR:
+ case NODE_DSTR:
+ case NODE_XSTR:
+ case NODE_DXSTR:
+ case NODE_DREGX:
+ case NODE_LIT:
+ case NODE_ARRAY:
+ case NODE_ZARRAY:
+ yyerror1(&@3, "can't define singleton method for literals");
+ break;
+ default:
+ value_expr($3);
+ break;
}
$$ = $3;
- /*%
- $$ = dispatch1(paren, $3);
- %*/
+ /*% %*/
+ /*% ripper: paren!($3) %*/
}
;
@@ -4880,19 +4305,13 @@ assoc_list : none
{
/*%%%*/
$$ = $1;
- /*%
- $$ = dispatch1(assoclist_from_args, $1);
- %*/
+ /*% %*/
+ /*% ripper: assoclist_from_args!($1) %*/
}
;
assocs : assoc
- /*%c%*/
- /*%c
- {
- $$ = rb_ary_new3(1, $1);
- }
- %*/
+ /*% ripper[brace]: rb_ary_new3(1, get_value($1)) %*/
| assocs ',' assoc
{
/*%%%*/
@@ -4911,9 +4330,8 @@ assocs : assoc
assocs = list_concat(assocs, tail);
}
$$ = assocs;
- /*%
- $$ = rb_ary_push($1, $3);
- %*/
+ /*% %*/
+ /*% ripper: rb_ary_push($1, get_value($3)) %*/
}
;
@@ -4922,28 +4340,26 @@ assoc : arg_value tASSOC arg_value
/*%%%*/
if (nd_type($1) == NODE_STR) {
nd_set_type($1, NODE_LIT);
- $1->nd_lit = rb_fstring($1->nd_lit);
+ add_mark_object(p, $1->nd_lit = rb_fstring($1->nd_lit));
}
- $$ = list_append(NEW_LIST($1), $3);
- /*%
- $$ = dispatch2(assoc_new, $1, $3);
- %*/
+ $$ = list_append(p, NEW_LIST($1, &@$), $3);
+ /*% %*/
+ /*% ripper: assoc_new!($1, $3) %*/
}
| tLABEL arg_value
{
/*%%%*/
- $$ = list_append(NEW_LIST(NEW_LIT(ID2SYM($1))), $2);
- /*%
- $$ = dispatch2(assoc_new, $1, $2);
- %*/
+ $$ = list_append(p, NEW_LIST(NEW_LIT(ID2SYM($1), &@1), &@$), $2);
+ /*% %*/
+ /*% ripper: assoc_new!($1, $2) %*/
}
| tSTRING_BEG string_contents tLABEL_END arg_value
{
/*%%%*/
- $$ = list_append(NEW_LIST(dsym_node($2)), $4);
- /*%
- $$ = dispatch2(assoc_new, dispatch1(dyna_symbol, $2), $4);
- %*/
+ YYLTYPE loc = code_loc_gen(&@1, &@3);
+ $$ = list_append(p, NEW_LIST(dsym_node(p, $2, &loc), &loc), $4);
+ /*% %*/
+ /*% ripper: assoc_new!(dyna_symbol!($2), $4) %*/
}
| tDSTAR arg_value
{
@@ -4952,10 +4368,9 @@ assoc : arg_value tASSOC arg_value
!($2->nd_head && $2->nd_head->nd_alen))
$$ = 0;
else
- $$ = list_append(NEW_LIST(0), $2);
- /*%
- $$ = dispatch1(assoc_splat, $2);
- %*/
+ $$ = list_append(p, NEW_LIST(0, &@$), $2);
+ /*% %*/
+ /*% ripper: assoc_splat!($2) %*/
}
;
@@ -4976,44 +4391,15 @@ operation3 : tIDENTIFIER
;
dot_or_colon : '.'
- /*%c%*/
- /*%c
- { $$ = $<val>1; }
- %*/
| tCOLON2
- /*%c%*/
- /*%c
- { $$ = $<val>1; }
- %*/
;
call_op : '.'
- {
- /*%%%*/
- $$ = '.';
- /*%
- $$ = ripper_id2sym('.');
- %*/
- }
| tANDDOT
- {
- /*%%%*/
- $$ = tANDDOT;
- /*%
- $$ = ripper_id2sym(idANDDOT);
- %*/
- }
;
call_op2 : call_op
| tCOLON2
- {
- /*%%%*/
- $$ = tCOLON2;
- /*%
- $$ = ripper_id2sym(idCOLON2);
- %*/
- }
;
opt_terms : /* none */
@@ -5035,8 +4421,8 @@ trailer : /* none */
| ','
;
-term : ';' {yyerrok;}
- | '\n'
+term : ';' {yyerrok;token_flush(p);}
+ | '\n' {token_flush(p);}
;
terms : term
@@ -5045,76 +4431,56 @@ terms : term
none : /* none */
{
- /*%%%*/
- $$ = 0;
- /*%
- $$ = Qundef;
- %*/
+ $$ = Qnull;
}
;
%%
-# undef parser
+# undef p
# undef yylex
# undef yylval
-# define yylval (*parser->lval)
-
-static int parser_regx_options(struct parser_params*);
-static int parser_tokadd_string(struct parser_params*,int,int,int,long*,rb_encoding**);
-static void parser_tokaddmbc(struct parser_params *parser, int c, rb_encoding *enc);
-static int parser_parse_string(struct parser_params*,NODE*);
-static int parser_here_document(struct parser_params*,NODE*);
-
-
-# define nextc() parser_nextc(parser)
-# define pushback(c) parser_pushback(parser, (c))
-# define newtok() parser_newtok(parser)
-# define tokspace(n) parser_tokspace(parser, (n))
-# define tokadd(c) parser_tokadd(parser, (c))
-# define tok_hex(numlen) parser_tok_hex(parser, (numlen))
-# define read_escape(flags,e) parser_read_escape(parser, (flags), (e))
-# define tokadd_escape(e) parser_tokadd_escape(parser, (e))
-# define regx_options() parser_regx_options(parser)
-# define tokadd_string(f,t,p,n,e) parser_tokadd_string(parser,(f),(t),(p),(n),(e))
-# define parse_string(n) parser_parse_string(parser,(n))
-# define tokaddmbc(c, enc) parser_tokaddmbc(parser, (c), (enc))
-# define here_document(n) parser_here_document(parser,(n))
-# define heredoc_identifier() parser_heredoc_identifier(parser)
-# define heredoc_restore(n) parser_heredoc_restore(parser,(n))
-# define whole_match_p(e,l,i) parser_whole_match_p(parser,(e),(l),(i))
-# define number_literal_suffix(f) parser_number_literal_suffix(parser, (f))
-# define set_number_literal(v, t, f) parser_set_number_literal(parser, (v), (t), (f))
-# define set_integer_literal(v, f) parser_set_integer_literal(parser, (v), (f))
+# define yylval (*p->lval)
+
+static int regx_options(struct parser_params*);
+static int tokadd_string(struct parser_params*,int,int,int,long*,rb_encoding**,rb_encoding**);
+static void tokaddmbc(struct parser_params *p, int c, rb_encoding *enc);
+static enum yytokentype parse_string(struct parser_params*,rb_strterm_literal_t*);
+static enum yytokentype here_document(struct parser_params*,rb_strterm_heredoc_t*);
#ifndef RIPPER
-# define set_yylval_str(x) (yylval.node = NEW_STR(x))
+# define set_yylval_node(x) { \
+ YYLTYPE _cur_loc; \
+ rb_parser_set_location(p, &_cur_loc); \
+ yylval.node = (x); \
+}
+# define set_yylval_str(x) set_yylval_node(NEW_STR(x, &_cur_loc))
+# define set_yylval_literal(x) set_yylval_node(NEW_LIT(x, &_cur_loc))
# define set_yylval_num(x) (yylval.num = (x))
# define set_yylval_id(x) (yylval.id = (x))
# define set_yylval_name(x) (yylval.id = (x))
-# define set_yylval_literal(x) (yylval.node = NEW_LIT(x))
-# define set_yylval_node(x) (yylval.node = (x))
# define yylval_id() (yylval.id)
#else
static inline VALUE
-ripper_yylval_id(ID x)
+ripper_yylval_id(struct parser_params *p, ID x)
{
- return ripper_new_yylval(x, ID2SYM(x), 0);
+ return ripper_new_yylval(p, x, ID2SYM(x), 0);
}
# define set_yylval_str(x) (yylval.val = (x))
-# define set_yylval_num(x) (yylval.val = ripper_new_yylval((x), 0, 0))
+# define set_yylval_num(x) (yylval.val = ripper_new_yylval(p, (x), 0, 0))
# define set_yylval_id(x) (void)(x)
-# define set_yylval_name(x) (void)(yylval.val = ripper_yylval_id(x))
+# define set_yylval_name(x) (void)(yylval.val = ripper_yylval_id(p, x))
# define set_yylval_literal(x) (void)(x)
# define set_yylval_node(x) (void)(x)
# define yylval_id() yylval.id
+# define _cur_loc NULL_LOC /* dummy */
#endif
#ifndef RIPPER
-#define ripper_flush(p) (void)(p)
-#define dispatch_scan_event(t) ((void)0)
-#define dispatch_delayed_token(t) ((void)0)
-#define has_delayed_token() (0)
+#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)
#else
-#define ripper_flush(p) ((p)->tokp = (p)->lex.pcur)
+#define literal_flush(p, ptr) ((void)(ptr))
#define yylval_rval (*(RB_TYPE_P(yylval.val, T_NODE) ? &yylval.node->nd_rval : &yylval.val))
@@ -5126,191 +4492,355 @@ intern_sym(const char *name)
}
static int
-ripper_has_scan_event(struct parser_params *parser)
+ripper_has_scan_event(struct parser_params *p)
{
-
- if (lex_p < parser->tokp) rb_raise(rb_eRuntimeError, "lex_p < tokp");
- return lex_p > parser->tokp;
+ if (p->lex.pcur < p->lex.ptok) rb_raise(rb_eRuntimeError, "lex.pcur < lex.ptok");
+ return p->lex.pcur > p->lex.ptok;
}
static VALUE
-ripper_scan_event_val(struct parser_params *parser, int t)
+ripper_scan_event_val(struct parser_params *p, int t)
{
- VALUE str = STR_NEW(parser->tokp, lex_p - parser->tokp);
- VALUE rval = ripper_dispatch1(parser, ripper_token2eventid(t), str);
- ripper_flush(parser);
+ VALUE str = STR_NEW(p->lex.ptok, p->lex.pcur - p->lex.ptok);
+ VALUE rval = ripper_dispatch1(p, ripper_token2eventid(t), str);
+ token_flush(p);
return rval;
}
static void
-ripper_dispatch_scan_event(struct parser_params *parser, int t)
+ripper_dispatch_scan_event(struct parser_params *p, int t)
{
- if (!ripper_has_scan_event(parser)) return;
- yylval_rval = ripper_scan_event_val(parser, t);
+ if (!ripper_has_scan_event(p)) return;
+ add_mark_object(p, yylval_rval = ripper_scan_event_val(p, t));
}
-#define dispatch_scan_event(t) ripper_dispatch_scan_event(parser, t)
+#define dispatch_scan_event(p, t) ripper_dispatch_scan_event(p, t)
static void
-ripper_dispatch_delayed_token(struct parser_params *parser, int t)
+ripper_dispatch_delayed_token(struct parser_params *p, int t)
{
- int saved_line = ruby_sourceline;
- const char *saved_tokp = parser->tokp;
+ int saved_line = p->ruby_sourceline;
+ const char *saved_tokp = p->lex.ptok;
- ruby_sourceline = parser->delayed_line;
- parser->tokp = lex_pbeg + parser->delayed_col;
- yylval_rval = ripper_dispatch1(parser, ripper_token2eventid(t), parser->delayed);
- parser->delayed = Qnil;
- ruby_sourceline = saved_line;
- parser->tokp = saved_tokp;
+ p->ruby_sourceline = p->delayed_line;
+ p->lex.ptok = p->lex.pbeg + p->delayed_col;
+ add_mark_object(p, yylval_rval = ripper_dispatch1(p, ripper_token2eventid(t), p->delayed));
+ p->delayed = Qnil;
+ p->ruby_sourceline = saved_line;
+ p->lex.ptok = saved_tokp;
}
-#define dispatch_delayed_token(t) ripper_dispatch_delayed_token(parser, t)
-#define has_delayed_token() (!NIL_P(parser->delayed))
+#define dispatch_delayed_token(p, t) ripper_dispatch_delayed_token(p, t)
+#define has_delayed_token(p) (!NIL_P(p->delayed))
#endif /* RIPPER */
#include "ruby/regex.h"
#include "ruby/util.h"
-#define parser_encoding_name() (current_enc->name)
-#define parser_mbclen() mbclen((lex_p-1),lex_pend,current_enc)
-#define parser_precise_mbclen() rb_enc_precise_mbclen((lex_p-1),lex_pend,current_enc)
-#define is_identchar(p,e,enc) (rb_enc_isalnum((unsigned char)(*(p)),(enc)) || (*(p)) == '_' || !ISASCII(*(p)))
-#define parser_is_identchar() (!parser->eofp && is_identchar((lex_p-1),lex_pend,current_enc))
+static inline int
+is_identchar(const char *ptr, const char *MAYBE_UNUSED(ptr_end), rb_encoding *enc)
+{
+ return rb_enc_isalnum((unsigned char)*ptr, enc) || *ptr == '_' || !ISASCII(*ptr);
+}
-#define parser_isascii() ISASCII(*(lex_p-1))
+static inline int
+parser_is_identchar(struct parser_params *p)
+{
+ return !(p)->eofp && is_identchar(p->lex.pcur-1, p->lex.pend, p->enc);
+}
-static int
-token_info_get_column(struct parser_params *parser, const char *pend)
+static inline int
+parser_isascii(struct parser_params *p)
{
- int column = 1;
- const char *p;
- for (p = lex_pbeg; p < pend; p++) {
- if (*p == '\t') {
- column = (((column - 1) / TAB_WIDTH) + 1) * TAB_WIDTH;
- }
- column++;
- }
- return column;
+ return ISASCII(*(p->lex.pcur-1));
}
-static int
-token_info_has_nonspaces(struct parser_params *parser, const char *pend)
+static void
+setup_token_info(token_info *ptinfo, const char *ptr, const rb_code_location_t *loc)
{
- const char *p;
- for (p = lex_pbeg; p < pend; p++) {
- if (*p != ' ' && *p != '\t') {
- return 1;
+ int column = 1, nonspc = 0, i;
+ for (i = 0; i < loc->beg_pos.column; i++, ptr++) {
+ if (*ptr == '\t') {
+ column = (((column - 1) / TAB_WIDTH) + 1) * TAB_WIDTH;
+ }
+ column++;
+ if (*ptr != ' ' && *ptr != '\t') {
+ nonspc = 1;
}
}
- return 0;
+
+ ptinfo->linenum = loc->beg_pos.lineno;
+ ptinfo->column = column;
+ ptinfo->nonspc = nonspc;
}
static void
-token_info_push_gen(struct parser_params *parser, const char *token, size_t len)
+token_info_push(struct parser_params *p, const char *token, const rb_code_location_t *loc)
{
token_info *ptinfo;
- const char *t = lex_p - len;
- if (!parser->token_info_enabled) return;
+ if (!p->token_info_enabled) return;
ptinfo = ALLOC(token_info);
ptinfo->token = token;
- ptinfo->linenum = ruby_sourceline;
- ptinfo->column = token_info_get_column(parser, t);
- ptinfo->nonspc = token_info_has_nonspaces(parser, t);
- ptinfo->next = parser->token_info;
+ ptinfo->next = p->token_info;
+ setup_token_info(ptinfo, p->lex.pbeg, loc);
- parser->token_info = ptinfo;
+ p->token_info = ptinfo;
}
static void
-token_info_pop_gen(struct parser_params *parser, const char *token, size_t len)
+token_info_pop(struct parser_params *p, const char *token, const rb_code_location_t *loc)
{
- int linenum;
- token_info *ptinfo = parser->token_info;
- const char *t = lex_p - len;
+ token_info *ptinfo_beg = p->token_info;
- if (!ptinfo) return;
- parser->token_info = ptinfo->next;
- linenum = ruby_sourceline;
- if (parser->token_info_enabled &&
- linenum != ptinfo->linenum && !ptinfo->nonspc &&
- !token_info_has_nonspaces(parser, t) &&
- token_info_get_column(parser, t) != ptinfo->column) {
- rb_warn3L(linenum,
- "mismatched indentations at '%s' with '%s' at %d",
- WARN_S(token), WARN_S(ptinfo->token), WARN_I(ptinfo->linenum));
- }
+ if (!ptinfo_beg) return;
+ p->token_info = ptinfo_beg->next;
+
+ /* indentation check of matched keywords (begin..end, if..end, etc.) */
+ token_info_warn(p, token, ptinfo_beg, 1, loc);
+ ruby_sized_xfree(ptinfo_beg, sizeof(*ptinfo_beg));
+}
- xfree(ptinfo);
+static void
+token_info_warn(struct parser_params *p, const char *token, token_info *ptinfo_beg, int same, const rb_code_location_t *loc)
+{
+ token_info ptinfo_end_body, *ptinfo_end = &ptinfo_end_body;
+ if (!p->token_info_enabled) return;
+ if (!ptinfo_beg) return;
+ setup_token_info(ptinfo_end, p->lex.pbeg, loc);
+ if (ptinfo_beg->linenum == ptinfo_end->linenum) return; /* ignore one-line block */
+ if (ptinfo_beg->nonspc || ptinfo_end->nonspc) return; /* ignore keyword in the middle of a line */
+ if (ptinfo_beg->column == ptinfo_end->column) return; /* the indents are matched */
+ if (!same && ptinfo_beg->column < ptinfo_end->column) return;
+ rb_warn3L(ptinfo_end->linenum,
+ "mismatched indentations at '%s' with '%s' at %d",
+ WARN_S(token), WARN_S(ptinfo_beg->token), WARN_I(ptinfo_beg->linenum));
}
static int
-parser_yyerror(struct parser_params *parser, const char *msg)
+parser_precise_mbclen(struct parser_params *p, const char *ptr)
+{
+ int len = rb_enc_precise_mbclen(ptr, p->lex.pend, p->enc);
+ if (!MBCLEN_CHARFOUND_P(len)) {
+ compile_error(p, "invalid multibyte char (%s)", rb_enc_name(p->enc));
+ return -1;
+ }
+ return len;
+}
+
+static int
+parser_yyerror(struct parser_params *p, const YYLTYPE *yylloc, const char *msg)
{
#ifndef RIPPER
const int max_line_margin = 30;
- const char *p, *pe;
- const char *pre = "", *post = "";
+ const char *ptr, *ptr_end, *pt, *pb;
+ const char *pre = "", *post = "", *pend;
const char *code = "", *caret = "", *newline = "";
+ const char *lim;
char *buf;
long len;
int i;
+ YYLTYPE current;
- p = lex_p;
- while (lex_pbeg <= p) {
- if (*p == '\n') break;
- p--;
+ if (!yylloc) {
+ RUBY_SET_YYLLOC(current);
+ yylloc = &current;
+ }
+ else if ((p->ruby_sourceline != yylloc->beg_pos.lineno &&
+ p->ruby_sourceline != yylloc->end_pos.lineno) ||
+ (yylloc->beg_pos.lineno == yylloc->end_pos.lineno &&
+ yylloc->beg_pos.column == yylloc->end_pos.column)) {
+ compile_error(p, "%s", msg);
+ return 0;
}
- p++;
- pe = lex_p;
- while (pe < lex_pend) {
- if (*pe == '\n') break;
- pe++;
+ pend = p->lex.pend;
+ if (pend > p->lex.pbeg && pend[-1] == '\n') {
+ if (--pend > p->lex.pbeg && pend[-1] == '\r') --pend;
}
- len = pe - p;
+ pt = (p->ruby_sourceline == yylloc->end_pos.lineno) ?
+ p->lex.pbeg + yylloc->end_pos.column : p->lex.pend;
+ ptr = ptr_end = pt < pend ? pt : pend;
+ lim = ptr - p->lex.pbeg > max_line_margin ? ptr - max_line_margin : p->lex.pbeg;
+ while ((lim < ptr) && (*(ptr-1) != '\n')) ptr--;
+
+ lim = pend - ptr_end > max_line_margin ? ptr_end + max_line_margin : pend;
+ while ((ptr_end < lim) && (*ptr_end != '\n')) ptr_end++;
+
+ len = ptr_end - ptr;
if (len > 4) {
+ if (ptr > p->lex.pbeg) {
+ ptr = rb_enc_prev_char(p->lex.pbeg, ptr, pt, rb_enc_get(p->lex.lastline));
+ if (ptr > p->lex.pbeg) pre = "...";
+ }
+ if (ptr_end < pend) {
+ ptr_end = rb_enc_prev_char(pt, ptr_end, pend, rb_enc_get(p->lex.lastline));
+ if (ptr_end < pend) post = "...";
+ }
+ }
+ pb = p->lex.pbeg;
+ if (p->ruby_sourceline == yylloc->beg_pos.lineno) {
+ pb += yylloc->beg_pos.column;
+ if (pb > pt) pb = pt;
+ }
+ if (pb < ptr) pb = ptr;
+ if (len <= 4 && yylloc->beg_pos.lineno == yylloc->end_pos.lineno) {
+ compile_error(p, "%s", msg);
+ }
+ else if (!p->error_buffer && rb_stderr_tty_p()) {
+#define CSI_BEGIN "\033["
+#define CSI_SGR "m"
+ compile_error(p, "%s\n"
+ CSI_BEGIN""CSI_SGR"%s" /* pre */
+ CSI_BEGIN"1"CSI_SGR"%.*s"
+ CSI_BEGIN"1;4"CSI_SGR"%.*s"
+ CSI_BEGIN";1"CSI_SGR"%.*s"
+ CSI_BEGIN""CSI_SGR"%s" /* post */,
+ msg, pre,
+ (int)(pb - ptr), ptr,
+ (int)(pt - pb), pb,
+ (int)(ptr_end - pt), pt,
+ post);
+ }
+ else {
char *p2;
- if (len > max_line_margin * 2 + 10) {
- if (lex_p - p > max_line_margin) {
- p = rb_enc_prev_char(p, lex_p - max_line_margin, pe, rb_enc_get(lex_lastline));
- pre = "...";
- }
- if (pe - lex_p > max_line_margin) {
- pe = rb_enc_prev_char(lex_p, lex_p + max_line_margin, pe, rb_enc_get(lex_lastline));
- post = "...";
- }
- len = pe - p;
- }
- i = (int)(lex_p - p);
+ len = ptr_end - ptr;
+ lim = pt < pend ? pt : pend;
+ i = (int)(lim - ptr);
buf = ALLOCA_N(char, i+2);
- code = p;
+ code = ptr;
caret = p2 = buf;
- while (i-- > 0) {
- *p2++ = *p++ == '\t' ? '\t' : ' ';
+ if (ptr <= pb) {
+ while (ptr < pb) {
+ *p2++ = *ptr++ == '\t' ? '\t' : ' ';
+ }
+ *p2++ = '^';
+ ptr++;
+ }
+ if (lim > ptr) {
+ memset(p2, '~', (lim - ptr));
+ p2 += (lim - ptr);
}
- *p2++ = '^';
*p2 = '\0';
newline = "\n";
+ compile_error(p, "%s%s""%s%.*s%s%s""%s%s",
+ msg, newline,
+ pre, (int)len, code, post, newline,
+ pre, caret);
}
- else {
- len = 0;
- }
- compile_error(PARSER_ARG "%s%s""%s%.*s%s%s""%s%s",
- msg, newline,
- pre, (int)len, code, post, newline,
- pre, caret);
#else
dispatch1(parse_error, STR_NEW2(msg));
- ripper_error();
+ ripper_error(p);
#endif /* !RIPPER */
return 0;
}
-static void parser_prepare(struct parser_params *parser);
+static int
+vtable_size(const struct vtable *tbl)
+{
+ if (!DVARS_TERMINAL_P(tbl)) {
+ return tbl->pos;
+ }
+ else {
+ return 0;
+ }
+}
+
+static struct vtable *
+vtable_alloc_gen(struct parser_params *p, int line, struct vtable *prev)
+{
+ struct vtable *tbl = ALLOC(struct vtable);
+ tbl->pos = 0;
+ tbl->capa = 8;
+ tbl->tbl = ALLOC_N(ID, tbl->capa);
+ tbl->prev = prev;
+#ifndef RIPPER
+ if (p->debug) {
+ rb_parser_printf(p, "vtable_alloc:%d: %p\n", line, (void *)tbl);
+ }
+#endif
+ return tbl;
+}
+#define vtable_alloc(prev) vtable_alloc_gen(p, __LINE__, prev)
+
+static void
+vtable_free_gen(struct parser_params *p, int line, const char *name,
+ struct vtable *tbl)
+{
+#ifndef RIPPER
+ if (p->debug) {
+ rb_parser_printf(p, "vtable_free:%d: %s(%p)\n", line, name, (void *)tbl);
+ }
+#endif
+ if (!DVARS_TERMINAL_P(tbl)) {
+ if (tbl->tbl) {
+ ruby_sized_xfree(tbl->tbl, tbl->capa * sizeof(ID));
+ }
+ ruby_sized_xfree(tbl, sizeof(tbl));
+ }
+}
+#define vtable_free(tbl) vtable_free_gen(p, __LINE__, #tbl, tbl)
+
+static void
+vtable_add_gen(struct parser_params *p, int line, const char *name,
+ struct vtable *tbl, ID id)
+{
+#ifndef RIPPER
+ if (p->debug) {
+ rb_parser_printf(p, "vtable_add:%d: %s(%p), %s\n",
+ line, name, (void *)tbl, rb_id2name(id));
+ }
+#endif
+ if (DVARS_TERMINAL_P(tbl)) {
+ rb_parser_fatal(p, "vtable_add: vtable is not allocated (%p)", (void *)tbl);
+ return;
+ }
+ if (tbl->pos == tbl->capa) {
+ tbl->capa = tbl->capa * 2;
+ SIZED_REALLOC_N(tbl->tbl, ID, tbl->capa, tbl->pos);
+ }
+ tbl->tbl[tbl->pos++] = id;
+}
+#define vtable_add(tbl, id) vtable_add_gen(p, __LINE__, #tbl, tbl, id)
+
+#ifndef RIPPER
+static void
+vtable_pop_gen(struct parser_params *p, int line, const char *name,
+ struct vtable *tbl, int n)
+{
+ if (p->debug) {
+ rb_parser_printf(p, "vtable_pop:%d: %s(%p), %d\n",
+ line, name, (void *)tbl, n);
+ }
+ if (tbl->pos < n) {
+ rb_parser_fatal(p, "vtable_pop: unreachable (%d < %d)", tbl->pos, n);
+ return;
+ }
+ tbl->pos -= n;
+}
+#define vtable_pop(tbl, n) vtable_pop_gen(p, __LINE__, #tbl, tbl, n)
+#endif
+
+static int
+vtable_included(const struct vtable * tbl, ID id)
+{
+ int i;
+
+ if (!DVARS_TERMINAL_P(tbl)) {
+ for (i = 0; i < tbl->pos; i++) {
+ if (tbl->tbl[i] == id) {
+ return i+1;
+ }
+ }
+ }
+ return 0;
+}
+
+static void parser_prepare(struct parser_params *p);
#ifndef RIPPER
+static NODE *parser_append_options(struct parser_params *p, NODE *node);
+
static VALUE
debug_lines(VALUE fname)
{
@@ -5327,22 +4857,10 @@ debug_lines(VALUE fname)
return 0;
}
-static VALUE
-coverage(VALUE fname, int n)
-{
- VALUE coverages = rb_get_coverages();
- if (RTEST(coverages) && RBASIC(coverages)->klass == 0) {
- VALUE lines = n > 0 ? rb_ary_tmp_new_fill(n) : rb_ary_tmp_new(0);
- rb_hash_aset(coverages, fname, lines);
- return lines;
- }
- return 0;
-}
-
static int
-e_option_supplied(struct parser_params *parser)
+e_option_supplied(struct parser_params *p)
{
- return strcmp(ruby_sourcefile, "-e") == 0;
+ return strcmp(p->ruby_sourcefile, "-e") == 0;
}
static VALUE
@@ -5350,71 +4868,85 @@ yycompile0(VALUE arg)
{
int n;
NODE *tree;
- struct parser_params *parser = (struct parser_params *)arg;
+ struct parser_params *p = (struct parser_params *)arg;
VALUE cov = Qfalse;
- if (!compile_for_eval && rb_safe_level() == 0) {
- ruby_debug_lines = debug_lines(ruby_sourcefile_string);
- if (ruby_debug_lines && ruby_sourceline > 0) {
+ if (!compile_for_eval && rb_safe_level() == 0 && !NIL_P(p->ruby_sourcefile_string)) {
+ p->debug_lines = debug_lines(p->ruby_sourcefile_string);
+ if (p->debug_lines && p->ruby_sourceline > 0) {
VALUE str = STR_NEW0();
- n = ruby_sourceline;
+ n = p->ruby_sourceline;
do {
- rb_ary_push(ruby_debug_lines, str);
+ rb_ary_push(p->debug_lines, str);
} while (--n);
}
- if (!e_option_supplied(parser)) {
- ruby_coverage = coverage(ruby_sourcefile_string, ruby_sourceline);
+ if (!e_option_supplied(p)) {
cov = Qtrue;
}
}
- parser_prepare(parser);
-#ifndef RIPPER
+ parser_prepare(p);
#define RUBY_DTRACE_PARSE_HOOK(name) \
if (RUBY_DTRACE_PARSE_##name##_ENABLED()) { \
- RUBY_DTRACE_PARSE_##name(ruby_sourcefile, ruby_sourceline); \
+ RUBY_DTRACE_PARSE_##name(p->ruby_sourcefile, p->ruby_sourceline); \
}
RUBY_DTRACE_PARSE_HOOK(BEGIN);
-#endif
- n = yyparse((void*)parser);
-#ifndef RIPPER
+ n = yyparse(p);
RUBY_DTRACE_PARSE_HOOK(END);
-#endif
- ruby_debug_lines = 0;
- ruby_coverage = 0;
-
- lex_strterm = 0;
- lex_p = lex_pbeg = lex_pend = 0;
- lex_lastline = lex_nextline = 0;
- if (parser->error_p) {
- VALUE mesg = parser->error_buffer;
+ p->debug_lines = 0;
+
+ 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 0;
+ return FALSE;
}
- tree = ruby_eval_tree;
+ tree = p->eval_tree;
if (!tree) {
- tree = NEW_NIL();
+ tree = NEW_NIL(&NULL_LOC);
}
else {
- VALUE opt = parser->compile_option;
+ VALUE opt = p->compile_option;
+ NODE *prelude;
+ NODE *body = parser_append_options(p, tree->nd_body);
if (!opt) opt = rb_obj_hide(rb_ident_hash_new());
rb_hash_aset(opt, rb_sym_intern_ascii_cstr("coverage_enabled"), cov);
- tree->nd_body = NEW_PRELUDE(ruby_eval_tree_begin, tree->nd_body, opt);
+ prelude = block_append(p, p->eval_tree_begin, body);
+ add_mark_object(p, opt);
+ tree->nd_body = prelude;
+ p->ast->body.compile_option = opt;
}
- return (VALUE)tree;
+ p->ast->body.root = tree;
+ p->ast->body.line_count = p->line_count;
+ return TRUE;
}
-static NODE*
-yycompile(struct parser_params *parser, VALUE fname, int line)
+static rb_ast_t *
+yycompile(VALUE vparser, struct parser_params *p, VALUE fname, int line)
{
- ruby_sourcefile_string = rb_str_new_frozen(fname);
- ruby_sourcefile = RSTRING_PTR(fname);
- ruby_sourceline = line - 1;
- return (NODE *)rb_suppress_tracing(yycompile0, (VALUE)parser);
+ rb_ast_t *ast;
+ if (NIL_P(fname)) {
+ p->ruby_sourcefile_string = Qnil;
+ p->ruby_sourcefile = "(none)";
+ }
+ else {
+ p->ruby_sourcefile_string = rb_fstring(fname);
+ p->ruby_sourcefile = StringValueCStr(fname);
+ }
+ p->ruby_sourceline = line - 1;
+
+ p->ast = ast = rb_ast_new();
+ rb_suppress_tracing(yycompile0, (VALUE)p);
+ p->ast = 0;
+ RB_GC_GUARD(vparser); /* prohibit tail call optimization */
+
+ return ast;
}
#endif /* !RIPPER */
@@ -5429,7 +4961,7 @@ must_be_ascii_compatible(VALUE s)
}
static VALUE
-lex_get_str(struct parser_params *parser, VALUE s)
+lex_get_str(struct parser_params *p, VALUE s)
{
char *beg, *end, *start;
long len;
@@ -5437,84 +4969,79 @@ lex_get_str(struct parser_params *parser, VALUE s)
beg = RSTRING_PTR(s);
len = RSTRING_LEN(s);
start = beg;
- if (lex_gets_ptr) {
- if (len == lex_gets_ptr) return Qnil;
- beg += lex_gets_ptr;
- len -= lex_gets_ptr;
+ if (p->lex.gets_.ptr) {
+ if (len == p->lex.gets_.ptr) return Qnil;
+ beg += p->lex.gets_.ptr;
+ len -= p->lex.gets_.ptr;
}
end = memchr(beg, '\n', len);
if (end) len = ++end - beg;
- lex_gets_ptr += len;
+ p->lex.gets_.ptr += len;
return rb_str_subseq(s, beg - start, len);
}
static VALUE
-lex_getline(struct parser_params *parser)
+lex_getline(struct parser_params *p)
{
- VALUE line = (*lex_gets)(parser, lex_input);
+ VALUE line = (*p->lex.gets)(p, p->lex.input);
if (NIL_P(line)) return line;
must_be_ascii_compatible(line);
#ifndef RIPPER
- if (ruby_debug_lines) {
- rb_enc_associate(line, current_enc);
- rb_ary_push(ruby_debug_lines, line);
- }
- if (ruby_coverage) {
- rb_ary_push(ruby_coverage, Qnil);
+ if (p->debug_lines) {
+ rb_enc_associate(line, p->enc);
+ rb_ary_push(p->debug_lines, line);
}
#endif
+ p->line_count++;
return line;
}
static const rb_data_type_t parser_data_type;
#ifndef RIPPER
-static NODE*
+static rb_ast_t*
parser_compile_string(VALUE vparser, VALUE fname, VALUE s, int line)
{
- struct parser_params *parser;
- NODE *node;
+ struct parser_params *p;
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, parser);
- lex_gets = lex_get_str;
- lex_gets_ptr = 0;
- lex_input = rb_str_new_frozen(s);
- lex_pbeg = lex_p = lex_pend = 0;
+ TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
- node = yycompile(parser, fname, line);
- RB_GC_GUARD(vparser); /* prohibit tail call optimization */
+ p->lex.gets = lex_get_str;
+ p->lex.gets_.ptr = 0;
+ p->lex.input = rb_str_new_frozen(s);
+ p->lex.pbeg = p->lex.pcur = p->lex.pend = 0;
- return node;
+ return yycompile(vparser, p, fname, line);
}
-NODE*
+rb_ast_t*
rb_compile_string(const char *f, VALUE s, int line)
{
must_be_ascii_compatible(s);
return parser_compile_string(rb_parser_new(), rb_filesystem_str_new_cstr(f), s, line);
}
-NODE*
+rb_ast_t*
rb_parser_compile_string(VALUE vparser, const char *f, VALUE s, int line)
{
return rb_parser_compile_string_path(vparser, rb_filesystem_str_new_cstr(f), s, line);
}
-NODE*
+rb_ast_t*
rb_parser_compile_string_path(VALUE vparser, VALUE f, VALUE s, int line)
{
must_be_ascii_compatible(s);
return parser_compile_string(vparser, f, s, line);
}
-NODE*
+rb_ast_t*
rb_compile_cstr(const char *f, const char *s, int len, int line)
{
VALUE str = rb_str_new(s, len);
return parser_compile_string(rb_parser_new(), rb_filesystem_str_new_cstr(f), str, line);
}
-NODE*
+rb_ast_t*
rb_parser_compile_cstr(VALUE vparser, const char *f, const char *s, int len, int line)
{
VALUE str = rb_str_new(s, len);
@@ -5524,12 +5051,12 @@ rb_parser_compile_cstr(VALUE vparser, const char *f, const char *s, int len, int
VALUE rb_io_gets_internal(VALUE io);
static VALUE
-lex_io_gets(struct parser_params *parser, VALUE io)
+lex_io_gets(struct parser_params *p, VALUE io)
{
return rb_io_gets_internal(io);
}
-NODE*
+rb_ast_t*
rb_compile_file(const char *f, VALUE file, int start)
{
VALUE vparser = rb_parser_new();
@@ -5537,27 +5064,45 @@ rb_compile_file(const char *f, VALUE file, int start)
return rb_parser_compile_file(vparser, f, file, start);
}
-NODE*
+rb_ast_t*
rb_parser_compile_file(VALUE vparser, const char *f, VALUE file, int start)
{
return rb_parser_compile_file_path(vparser, rb_filesystem_str_new_cstr(f), file, start);
}
-NODE*
+rb_ast_t*
rb_parser_compile_file_path(VALUE vparser, VALUE fname, VALUE file, int start)
{
- struct parser_params *parser;
- NODE *node;
+ struct parser_params *p;
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, parser);
- lex_gets = lex_io_gets;
- lex_input = file;
- lex_pbeg = lex_p = lex_pend = 0;
+ TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
- node = yycompile(parser, fname, start);
- RB_GC_GUARD(vparser); /* prohibit tail call optimization */
+ p->lex.gets = lex_io_gets;
+ p->lex.input = file;
+ p->lex.pbeg = p->lex.pcur = p->lex.pend = 0;
- return node;
+ return yycompile(vparser, p, fname, start);
+}
+
+static VALUE
+lex_generic_gets(struct parser_params *p, VALUE input)
+{
+ return (*p->lex.gets_.call)(input, p->line_count);
+}
+
+rb_ast_t*
+rb_parser_compile_generic(VALUE vparser, VALUE (*lex_gets)(VALUE, int), VALUE fname, VALUE input, int start)
+{
+ struct parser_params *p;
+
+ TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
+
+ p->lex.gets = lex_generic_gets;
+ p->lex.gets_.call = lex_gets;
+ p->lex.input = input;
+ p->lex.pbeg = p->lex.pcur = p->lex.pend = 0;
+
+ return yycompile(vparser, p, fname, start);
}
#endif /* !RIPPER */
@@ -5568,7 +5113,8 @@ rb_parser_compile_file_path(VALUE vparser, VALUE fname, VALUE file, int start)
#define STR_FUNC_SYMBOL 0x10
#define STR_FUNC_INDENT 0x20
#define STR_FUNC_LABEL 0x40
-#define STR_TERM_END -1
+#define STR_FUNC_LIST 0x4000
+#define STR_FUNC_TERM 0x8000
enum string_type {
str_label = STR_FUNC_LABEL,
@@ -5576,18 +5122,18 @@ enum string_type {
str_dquote = (STR_FUNC_EXPAND),
str_xquote = (STR_FUNC_EXPAND),
str_regexp = (STR_FUNC_REGEXP|STR_FUNC_ESCAPE|STR_FUNC_EXPAND),
- str_sword = (STR_FUNC_QWORDS),
- str_dword = (STR_FUNC_QWORDS|STR_FUNC_EXPAND),
+ str_sword = (STR_FUNC_QWORDS|STR_FUNC_LIST),
+ str_dword = (STR_FUNC_QWORDS|STR_FUNC_EXPAND|STR_FUNC_LIST),
str_ssym = (STR_FUNC_SYMBOL),
str_dsym = (STR_FUNC_SYMBOL|STR_FUNC_EXPAND)
};
static VALUE
-parser_str_new(const char *p, long n, rb_encoding *enc, int func, rb_encoding *enc0)
+parser_str_new(const char *ptr, long len, rb_encoding *enc, int func, rb_encoding *enc0)
{
VALUE str;
- str = rb_enc_str_new(p, n, enc);
+ str = rb_enc_str_new(ptr, len, enc);
if (!(func & STR_FUNC_REGEXP) && rb_enc_asciicompat(enc)) {
if (rb_enc_str_coderange(str) == ENC_CODERANGE_7BIT) {
}
@@ -5599,164 +5145,211 @@ parser_str_new(const char *p, long n, rb_encoding *enc, int func, rb_encoding *e
return str;
}
-#define lex_goto_eol(parser) ((parser)->lex.pcur = (parser)->lex.pend)
-#define lex_eol_p() (lex_p >= lex_pend)
-#define peek(c) peek_n((c), 0)
-#define peek_n(c,n) (lex_p+(n) < lex_pend && (c) == (unsigned char)lex_p[n])
-#define peekc() peekc_n(0)
-#define peekc_n(n) (lex_p+(n) < lex_pend ? (unsigned char)lex_p[n] : -1)
+#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)
+{
+ if (tok < end) {
+ if (!has_delayed_token(p)) {
+ p->delayed = rb_str_buf_new(1024);
+ rb_enc_associate(p->delayed, p->enc);
+ p->delayed_line = p->ruby_sourceline;
+ p->delayed_col = (int)(tok - p->lex.pbeg);
+ }
+ rb_str_buf_cat(p->delayed, tok, end - tok);
+ p->lex.ptok = end;
+ }
+}
+#else
+#define add_delayed_token(p, tok, end) ((void)(tok), (void)(end))
+#endif
static int
-parser_nextline(struct parser_params *parser)
+nextline(struct parser_params *p)
{
- VALUE v = lex_nextline;
- lex_nextline = 0;
+ VALUE v = p->lex.nextline;
+ p->lex.nextline = 0;
if (!v) {
- if (parser->eofp)
+ if (p->eofp)
return -1;
- if (!lex_input || NIL_P(v = lex_getline(parser))) {
- parser->eofp = 1;
- lex_goto_eol(parser);
+ if (!p->lex.input || NIL_P(v = lex_getline(p))) {
+ p->eofp = 1;
+ lex_goto_eol(p);
return -1;
}
- parser->cr_seen = FALSE;
+ p->cr_seen = FALSE;
}
-#ifdef RIPPER
- if (parser->tokp < lex_pend) {
- if (!has_delayed_token()) {
- parser->delayed = rb_str_buf_new(1024);
- rb_enc_associate(parser->delayed, current_enc);
- rb_str_buf_cat(parser->delayed,
- parser->tokp, lex_pend - parser->tokp);
- parser->delayed_line = ruby_sourceline;
- parser->delayed_col = (int)(parser->tokp - lex_pbeg);
- }
- else {
- rb_str_buf_cat(parser->delayed,
- parser->tokp, lex_pend - parser->tokp);
- }
+ add_delayed_token(p, p->lex.ptok, p->lex.pend);
+ if (p->heredoc_end > 0) {
+ p->ruby_sourceline = p->heredoc_end;
+ p->heredoc_end = 0;
}
-#endif
- if (heredoc_end > 0) {
- ruby_sourceline = heredoc_end;
- heredoc_end = 0;
- }
- ruby_sourceline++;
- parser->line_count++;
- lex_pbeg = lex_p = RSTRING_PTR(v);
- lex_pend = lex_p + RSTRING_LEN(v);
- ripper_flush(parser);
- lex_lastline = v;
+ p->ruby_sourceline++;
+ 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;
}
static int
-parser_cr(struct parser_params *parser, int c)
+parser_cr(struct parser_params *p, int c)
{
- if (peek('\n')) {
- lex_p++;
+ if (peek(p, '\n')) {
+ p->lex.pcur++;
c = '\n';
}
- else if (!parser->cr_seen) {
- parser->cr_seen = TRUE;
- /* carried over with lex_nextline for nextc() */
+ else 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");
}
return c;
}
static inline int
-parser_nextc(struct parser_params *parser)
+nextc(struct parser_params *p)
{
int c;
- if (UNLIKELY(lex_p == lex_pend)) {
- if (parser_nextline(parser)) return -1;
+ if (UNLIKELY((p->lex.pcur == p->lex.pend) || p->eofp || p->lex.nextline)) {
+ if (nextline(p)) return -1;
}
- c = (unsigned char)*lex_p++;
+ c = (unsigned char)*p->lex.pcur++;
if (UNLIKELY(c == '\r')) {
- c = parser_cr(parser, c);
+ c = parser_cr(p, c);
}
return c;
}
static void
-parser_pushback(struct parser_params *parser, int c)
+pushback(struct parser_params *p, int c)
{
if (c == -1) return;
- lex_p--;
- if (lex_p > lex_pbeg && lex_p[0] == '\n' && lex_p[-1] == '\r') {
- lex_p--;
+ p->lex.pcur--;
+ if (p->lex.pcur > p->lex.pbeg && p->lex.pcur[0] == '\n' && p->lex.pcur[-1] == '\r') {
+ p->lex.pcur--;
}
}
-#define was_bol() (lex_p == lex_pbeg + 1)
+#define was_bol(p) ((p)->lex.pcur == (p)->lex.pbeg + 1)
-#define tokfix() (tokenbuf[tokidx]='\0')
-#define tok() tokenbuf
-#define toklen() tokidx
-#define toklast() (tokidx>0?tokenbuf[tokidx-1]:0)
+#define tokfix(p) ((p)->tokenbuf[(p)->tokidx]='\0')
+#define tok(p) (p)->tokenbuf
+#define toklen(p) (p)->tokidx
static char*
-parser_newtok(struct parser_params *parser)
+newtok(struct parser_params *p)
{
- tokidx = 0;
- tokline = ruby_sourceline;
- if (!tokenbuf) {
- toksiz = 60;
- tokenbuf = ALLOC_N(char, 60);
+ p->tokidx = 0;
+ p->tokline = p->ruby_sourceline;
+ if (!p->tokenbuf) {
+ p->toksiz = 60;
+ p->tokenbuf = ALLOC_N(char, 60);
}
- if (toksiz > 4096) {
- toksiz = 60;
- REALLOC_N(tokenbuf, char, 60);
+ if (p->toksiz > 4096) {
+ p->toksiz = 60;
+ REALLOC_N(p->tokenbuf, char, 60);
}
- return tokenbuf;
+ return p->tokenbuf;
}
static char *
-parser_tokspace(struct parser_params *parser, int n)
+tokspace(struct parser_params *p, int n)
{
- tokidx += n;
+ p->tokidx += n;
- if (tokidx >= toksiz) {
- do {toksiz *= 2;} while (toksiz < tokidx);
- REALLOC_N(tokenbuf, char, toksiz);
+ if (p->tokidx >= p->toksiz) {
+ do {p->toksiz *= 2;} while (p->toksiz < p->tokidx);
+ REALLOC_N(p->tokenbuf, char, p->toksiz);
}
- return &tokenbuf[tokidx-n];
+ return &p->tokenbuf[p->tokidx-n];
}
static void
-parser_tokadd(struct parser_params *parser, int c)
+tokadd(struct parser_params *p, int c)
{
- tokenbuf[tokidx++] = (char)c;
- if (tokidx >= toksiz) {
- toksiz *= 2;
- REALLOC_N(tokenbuf, char, toksiz);
+ p->tokenbuf[p->tokidx++] = (char)c;
+ if (p->tokidx >= p->toksiz) {
+ p->toksiz *= 2;
+ REALLOC_N(p->tokenbuf, char, p->toksiz);
}
}
static int
-parser_tok_hex(struct parser_params *parser, size_t *numlen)
+tok_hex(struct parser_params *p, size_t *numlen)
{
int c;
- c = scan_hex(lex_p, 2, numlen);
+ c = scan_hex(p->lex.pcur, 2, numlen);
if (!*numlen) {
- yyerror("invalid hex escape");
+ p->lex.ptok = p->lex.pcur;
+ yyerror0("invalid hex escape");
return 0;
}
- lex_p += *numlen;
+ p->lex.pcur += *numlen;
return c;
}
-#define tokcopy(n) memcpy(tokspace(n), lex_p - (n), (n))
+#define tokcopy(p, n) memcpy(tokspace(p, n), (p)->lex.pcur - (n), (n))
+
+static int
+tokadd_codepoint(struct parser_params *p, rb_encoding **encp,
+ int regexp_literal, int wide)
+{
+ 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 (wide ? (numlen == 0 || numlen > 6) : (numlen < 4)) {
+ yyerror0("invalid Unicode escape");
+ return wide && numlen > 0;
+ }
+ if (codepoint > 0x10ffff) {
+ yyerror0("invalid Unicode codepoint (too large)");
+ return wide;
+ }
+ if ((codepoint & 0xfffff800) == 0xd800) {
+ yyerror0("invalid Unicode codepoint");
+ return wide;
+ }
+ if (regexp_literal) {
+ tokcopy(p, (int)numlen);
+ }
+ else if (codepoint >= 0x80) {
+ rb_encoding *utf8 = rb_utf8_encoding();
+ if (*encp && utf8 != *encp) {
+ static const char mixed_utf8[] = "UTF-8 mixed within %s source";
+ size_t len = sizeof(mixed_utf8) - 2 + strlen(rb_enc_name(*encp));
+ char *mesg = alloca(len);
+ snprintf(mesg, len, mixed_utf8, rb_enc_name(*encp));
+ yyerror0(mesg);
+ return wide;
+ }
+ *encp = utf8;
+ tokaddmbc(p, codepoint, *encp);
+ }
+ else {
+ tokadd(p, codepoint);
+ }
+ return TRUE;
+}
/* return value is for ?\u3042 */
static int
-parser_tokadd_utf8(struct parser_params *parser, rb_encoding **encp,
- int string_literal, int symbol_literal, int regexp_literal)
+parser_tokadd_utf8(struct parser_params *p, rb_encoding **encp,
+ int string_literal, int symbol_literal, int regexp_literal)
{
/*
* If string_literal is true, then we allow multiple codepoints
@@ -5765,78 +5358,54 @@ parser_tokadd_utf8(struct parser_params *parser, rb_encoding **encp,
* codepoint without adding it
*/
- int codepoint;
- size_t numlen;
+ const int open_brace = '{', close_brace = '}';
- if (regexp_literal) { tokadd('\\'); tokadd('u'); }
+ if (regexp_literal) { tokadd(p, '\\'); tokadd(p, 'u'); }
- if (peek('{')) { /* handle \u{...} form */
- do {
- if (regexp_literal) { tokadd(*lex_p); }
- nextc();
- codepoint = scan_hex(lex_p, 6, &numlen);
- if (numlen == 0) {
- yyerror("invalid Unicode escape");
- return 0;
- }
- if (codepoint > 0x10ffff) {
- yyerror("invalid Unicode codepoint (too large)");
- return 0;
- }
- lex_p += numlen;
- if (regexp_literal) {
- tokcopy((int)numlen);
- }
- else if (codepoint >= 0x80) {
- *encp = rb_utf8_encoding();
- if (string_literal) tokaddmbc(codepoint, *encp);
+ if (peek(p, open_brace)) { /* handle \u{...} form */
+ 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 (regexp_literal) tokadd(p, last);
+ if (!tokadd_codepoint(p, encp, regexp_literal, TRUE)) {
+ break;
}
- else if (string_literal) {
- tokadd(codepoint);
+ while (ISSPACE(c = *p->lex.pcur)) {
+ if (++p->lex.pcur >= p->lex.pend) goto unterminated;
+ last = c;
}
- } while (string_literal && (peek(' ') || peek('\t')));
+ }
- if (!peek('}')) {
- yyerror("unterminated Unicode escape");
+ if (c != close_brace) {
+ unterminated:
+ literal_flush(p, p->lex.pcur);
+ yyerror0("unterminated Unicode escape");
return 0;
}
- if (regexp_literal) { tokadd('}'); }
- nextc();
+ if (regexp_literal) tokadd(p, close_brace);
+ nextc(p);
}
else { /* handle \uxxxx form */
- codepoint = scan_hex(lex_p, 4, &numlen);
- if (numlen < 4) {
- yyerror("invalid Unicode escape");
+ if (!tokadd_codepoint(p, encp, regexp_literal, FALSE)) {
return 0;
}
- lex_p += 4;
- if (regexp_literal) {
- tokcopy(4);
- }
- else if (codepoint >= 0x80) {
- *encp = rb_utf8_encoding();
- if (string_literal) tokaddmbc(codepoint, *encp);
- }
- else if (string_literal) {
- tokadd(codepoint);
- }
}
- return codepoint;
+ return TRUE;
}
#define ESCAPE_CONTROL 1
#define ESCAPE_META 2
static int
-parser_read_escape(struct parser_params *parser, int flags,
- rb_encoding **encp)
+read_escape(struct parser_params *p, int flags, rb_encoding **encp)
{
int c;
size_t numlen;
- switch (c = nextc()) {
+ switch (c = nextc(p)) {
case '\\': /* Backslash */
return c;
@@ -5863,13 +5432,13 @@ parser_read_escape(struct parser_params *parser, int flags,
case '0': case '1': case '2': case '3': /* octal constant */
case '4': case '5': case '6': case '7':
- pushback(c);
- c = scan_oct(lex_p, 3, &numlen);
- lex_p += numlen;
+ pushback(p, c);
+ c = scan_oct(p->lex.pcur, 3, &numlen);
+ p->lex.pcur += numlen;
return c;
case 'x': /* hex constant */
- c = tok_hex(&numlen);
+ c = tok_hex(p, &numlen);
if (numlen == 0) return 0;
return c;
@@ -5881,13 +5450,12 @@ parser_read_escape(struct parser_params *parser, int flags,
case 'M':
if (flags & ESCAPE_META) goto eof;
- if ((c = nextc()) != '-') {
- pushback(c);
+ if ((c = nextc(p)) != '-') {
goto eof;
}
- if ((c = nextc()) == '\\') {
- if (peek('u')) goto eof;
- return read_escape(flags|ESCAPE_META, encp) | 0x80;
+ if ((c = nextc(p)) == '\\') {
+ if (peek(p, 'u')) goto eof;
+ return read_escape(p, flags|ESCAPE_META, encp) | 0x80;
}
else if (c == -1 || !ISASCII(c)) goto eof;
else {
@@ -5895,15 +5463,14 @@ parser_read_escape(struct parser_params *parser, int flags,
}
case 'C':
- if ((c = nextc()) != '-') {
- pushback(c);
+ if ((c = nextc(p)) != '-') {
goto eof;
}
case 'c':
if (flags & ESCAPE_CONTROL) goto eof;
- if ((c = nextc())== '\\') {
- if (peek('u')) goto eof;
- c = read_escape(flags|ESCAPE_CONTROL, encp);
+ if ((c = nextc(p))== '\\') {
+ if (peek(p, 'u')) goto eof;
+ c = read_escape(p, flags|ESCAPE_CONTROL, encp);
}
else if (c == '?')
return 0177;
@@ -5912,7 +5479,8 @@ parser_read_escape(struct parser_params *parser, int flags,
eof:
case -1:
- yyerror("Invalid escape character syntax");
+ yyerror0("Invalid escape character syntax");
+ pushback(p, c);
return '\0';
default:
@@ -5921,95 +5489,95 @@ parser_read_escape(struct parser_params *parser, int flags,
}
static void
-parser_tokaddmbc(struct parser_params *parser, int c, rb_encoding *enc)
+tokaddmbc(struct parser_params *p, int c, rb_encoding *enc)
{
int len = rb_enc_codelen(c, enc);
- rb_enc_mbcput(c, tokspace(len), enc);
+ rb_enc_mbcput(c, tokspace(p, len), enc);
}
static int
-parser_tokadd_escape(struct parser_params *parser, rb_encoding **encp)
+tokadd_escape(struct parser_params *p, rb_encoding **encp)
{
int c;
int flags = 0;
size_t numlen;
first:
- switch (c = nextc()) {
+ switch (c = nextc(p)) {
case '\n':
return 0; /* just ignore */
case '0': case '1': case '2': case '3': /* octal constant */
case '4': case '5': case '6': case '7':
{
- ruby_scan_oct(--lex_p, 3, &numlen);
+ ruby_scan_oct(--p->lex.pcur, 3, &numlen);
if (numlen == 0) goto eof;
- lex_p += numlen;
- tokcopy((int)numlen + 1);
+ p->lex.pcur += numlen;
+ tokcopy(p, (int)numlen + 1);
}
return 0;
case 'x': /* hex constant */
{
- tok_hex(&numlen);
+ tok_hex(p, &numlen);
if (numlen == 0) return -1;
- tokcopy((int)numlen + 2);
+ tokcopy(p, (int)numlen + 2);
}
return 0;
case 'M':
if (flags & ESCAPE_META) goto eof;
- if ((c = nextc()) != '-') {
- pushback(c);
+ if ((c = nextc(p)) != '-') {
+ pushback(p, c);
goto eof;
}
- tokcopy(3);
+ tokcopy(p, 3);
flags |= ESCAPE_META;
goto escaped;
case 'C':
if (flags & ESCAPE_CONTROL) goto eof;
- if ((c = nextc()) != '-') {
- pushback(c);
+ if ((c = nextc(p)) != '-') {
+ pushback(p, c);
goto eof;
}
- tokcopy(3);
+ tokcopy(p, 3);
goto escaped;
case 'c':
if (flags & ESCAPE_CONTROL) goto eof;
- tokcopy(2);
+ tokcopy(p, 2);
flags |= ESCAPE_CONTROL;
escaped:
- if ((c = nextc()) == '\\') {
+ if ((c = nextc(p)) == '\\') {
goto first;
}
else if (c == -1) goto eof;
- tokadd(c);
+ tokadd(p, c);
return 0;
eof:
case -1:
- yyerror("Invalid escape character syntax");
+ yyerror0("Invalid escape character syntax");
return -1;
default:
- tokadd('\\');
- tokadd(c);
+ tokadd(p, '\\');
+ tokadd(p, c);
}
return 0;
}
static int
-parser_regx_options(struct parser_params *parser)
+regx_options(struct parser_params *p)
{
int kcode = 0;
int kopt = 0;
int options = 0;
int c, opt, kc;
- newtok();
- while (c = nextc(), ISALPHA(c)) {
+ newtok(p);
+ while (c = nextc(p), ISALPHA(c)) {
if (c == 'o') {
options |= RE_OPTION_ONCE;
}
@@ -6023,42 +5591,30 @@ parser_regx_options(struct parser_params *parser)
}
}
else {
- tokadd(c);
+ tokadd(p, c);
}
}
options |= kopt;
- pushback(c);
- if (toklen()) {
- tokfix();
- compile_error(PARSER_ARG "unknown regexp option%s - %s",
- toklen() > 1 ? "s" : "", tok());
+ pushback(p, c);
+ if (toklen(p)) {
+ tokfix(p);
+ compile_error(p, "unknown regexp option%s - %*s",
+ toklen(p) > 1 ? "s" : "", toklen(p), tok(p));
}
return options | RE_OPTION_ENCODING(kcode);
}
-static void
-dispose_string(VALUE str)
-{
- rb_str_free(str);
- rb_gc_force_recycle(str);
-}
-
static int
-parser_tokadd_mbchar(struct parser_params *parser, int c)
+tokadd_mbchar(struct parser_params *p, int c)
{
- int len = parser_precise_mbclen();
- if (!MBCLEN_CHARFOUND_P(len)) {
- compile_error(PARSER_ARG "invalid multibyte char (%s)", parser_encoding_name());
- return -1;
- }
- tokadd(c);
- lex_p += --len;
- if (len > 0) tokcopy(len);
+ int len = parser_precise_mbclen(p, p->lex.pcur-1);
+ if (len < 0) return -1;
+ tokadd(p, c);
+ p->lex.pcur += --len;
+ if (len > 0) tokcopy(p, len);
return c;
}
-#define tokadd_mbchar(c) parser_tokadd_mbchar(parser, (c))
-
static inline int
simple_re_meta(int c)
{
@@ -6073,62 +5629,67 @@ simple_re_meta(int c)
}
static int
-parser_update_heredoc_indent(struct parser_params *parser, int c)
+parser_update_heredoc_indent(struct parser_params *p, int c)
{
- if (heredoc_line_indent == -1) {
- if (c == '\n') heredoc_line_indent = 0;
+ if (p->heredoc_line_indent == -1) {
+ if (c == '\n') p->heredoc_line_indent = 0;
}
else {
if (c == ' ') {
- heredoc_line_indent++;
+ p->heredoc_line_indent++;
return TRUE;
}
else if (c == '\t') {
- int w = (heredoc_line_indent / TAB_WIDTH) + 1;
- heredoc_line_indent = w * TAB_WIDTH;
+ int w = (p->heredoc_line_indent / TAB_WIDTH) + 1;
+ p->heredoc_line_indent = w * TAB_WIDTH;
return TRUE;
}
else if (c != '\n') {
- if (heredoc_indent > heredoc_line_indent) {
- heredoc_indent = heredoc_line_indent;
+ if (p->heredoc_indent > p->heredoc_line_indent) {
+ p->heredoc_indent = p->heredoc_line_indent;
}
- heredoc_line_indent = -1;
+ p->heredoc_line_indent = -1;
}
}
return FALSE;
}
+static void
+parser_mixed_error(struct parser_params *p, rb_encoding *enc1, rb_encoding *enc2)
+{
+ static const char mixed_msg[] = "%s mixed within %s source";
+ const char *n1 = rb_enc_name(enc1), *n2 = rb_enc_name(enc2);
+ const size_t len = sizeof(mixed_msg) - 4 + strlen(n1) + strlen(n2);
+ char *errbuf = ALLOCA_N(char, len);
+ snprintf(errbuf, len, mixed_msg, n1, n2);
+ yyerror0(errbuf);
+}
+
+static void
+parser_mixed_escape(struct parser_params *p, const char *beg, rb_encoding *enc1, rb_encoding *enc2)
+{
+ const char *pos = p->lex.pcur;
+ p->lex.pcur = beg;
+ parser_mixed_error(p, enc1, enc2);
+ p->lex.pcur = pos;
+}
+
static int
-parser_tokadd_string(struct parser_params *parser,
- int func, int term, int paren, long *nest,
- rb_encoding **encp)
+tokadd_string(struct parser_params *p,
+ int func, int term, int paren, long *nest,
+ rb_encoding **encp, rb_encoding **enc)
{
int c;
- int has_nonascii = 0;
- rb_encoding *enc = *encp;
- char *errbuf = 0;
- static const char mixed_msg[] = "%s mixed within %s source";
+ bool erred = false;
-#define mixed_error(enc1, enc2) if (!errbuf) { \
- size_t len = sizeof(mixed_msg) - 4; \
- len += strlen(rb_enc_name(enc1)); \
- len += strlen(rb_enc_name(enc2)); \
- errbuf = ALLOCA_N(char, len); \
- snprintf(errbuf, len, mixed_msg, \
- rb_enc_name(enc1), \
- rb_enc_name(enc2)); \
- yyerror(errbuf); \
- }
-#define mixed_escape(beg, enc1, enc2) do { \
- const char *pos = lex_p; \
- lex_p = (beg); \
- mixed_error((enc1), (enc2)); \
- lex_p = pos; \
- } while (0)
+#define mixed_error(enc1, enc2) \
+ (void)(erred || (parser_mixed_error(p, enc1, enc2), erred = true))
+#define mixed_escape(beg, enc1, enc2) \
+ (void)(erred || (parser_mixed_escape(p, beg, enc1, enc2), erred = true))
- while ((c = nextc()) != -1) {
- if (heredoc_indent > 0) {
- parser_update_heredoc_indent(parser, c);
+ while ((c = nextc(p)) != -1) {
+ if (p->heredoc_indent > 0) {
+ parser_update_heredoc_indent(p, c);
}
if (paren && c == paren) {
@@ -6136,134 +5697,150 @@ parser_tokadd_string(struct parser_params *parser,
}
else if (c == term) {
if (!nest || !*nest) {
- pushback(c);
+ pushback(p, c);
break;
}
--*nest;
}
- else if ((func & STR_FUNC_EXPAND) && c == '#' && lex_p < lex_pend) {
- int c2 = *lex_p;
+ else if ((func & STR_FUNC_EXPAND) && c == '#' && p->lex.pcur < p->lex.pend) {
+ int c2 = *p->lex.pcur;
if (c2 == '$' || c2 == '@' || c2 == '{') {
- pushback(c);
+ pushback(p, c);
break;
}
}
else if (c == '\\') {
- const char *beg = lex_p - 1;
- c = nextc();
+ literal_flush(p, p->lex.pcur - 1);
+ c = nextc(p);
switch (c) {
case '\n':
if (func & STR_FUNC_QWORDS) break;
- if (func & STR_FUNC_EXPAND) continue;
- tokadd('\\');
+ if (func & STR_FUNC_EXPAND) {
+ if (!(func & STR_FUNC_INDENT) || (p->heredoc_indent < 0))
+ continue;
+ if (c == term) {
+ c = '\\';
+ goto terminate;
+ }
+ }
+ tokadd(p, '\\');
break;
case '\\':
- if (func & STR_FUNC_ESCAPE) tokadd(c);
+ if (func & STR_FUNC_ESCAPE) tokadd(p, c);
break;
case 'u':
if ((func & STR_FUNC_EXPAND) == 0) {
- tokadd('\\');
+ tokadd(p, '\\');
break;
}
- parser_tokadd_utf8(parser, &enc, 1,
- func & STR_FUNC_SYMBOL,
- func & STR_FUNC_REGEXP);
- if (has_nonascii && enc != *encp) {
- mixed_escape(beg, enc, *encp);
+ if (!parser_tokadd_utf8(p, enc, term,
+ func & STR_FUNC_SYMBOL,
+ func & STR_FUNC_REGEXP)) {
+ return -1;
}
continue;
default:
if (c == -1) return -1;
if (!ISASCII(c)) {
- if ((func & STR_FUNC_EXPAND) == 0) tokadd('\\');
+ if ((func & STR_FUNC_EXPAND) == 0) tokadd(p, '\\');
goto non_ascii;
}
if (func & STR_FUNC_REGEXP) {
if (c == term && !simple_re_meta(c)) {
- tokadd(c);
+ tokadd(p, c);
continue;
}
- pushback(c);
- if ((c = tokadd_escape(&enc)) < 0)
+ pushback(p, c);
+ if ((c = tokadd_escape(p, enc)) < 0)
return -1;
- if (has_nonascii && enc != *encp) {
- mixed_escape(beg, enc, *encp);
+ if (*enc && *enc != *encp) {
+ mixed_escape(p->lex.ptok+2, *enc, *encp);
}
continue;
}
else if (func & STR_FUNC_EXPAND) {
- pushback(c);
- if (func & STR_FUNC_ESCAPE) tokadd('\\');
- c = read_escape(0, &enc);
+ pushback(p, c);
+ if (func & STR_FUNC_ESCAPE) tokadd(p, '\\');
+ c = read_escape(p, 0, enc);
}
else if ((func & STR_FUNC_QWORDS) && ISSPACE(c)) {
/* ignore backslashed spaces in %w */
}
else if (c != term && !(paren && c == paren)) {
- tokadd('\\');
- pushback(c);
+ tokadd(p, '\\');
+ pushback(p, c);
continue;
}
}
}
- else if (!parser_isascii()) {
+ else if (!parser_isascii(p)) {
non_ascii:
- has_nonascii = 1;
- if (enc != *encp) {
- mixed_error(enc, *encp);
+ if (!*enc) {
+ *enc = *encp;
+ }
+ else if (*enc != *encp) {
+ mixed_error(*enc, *encp);
continue;
}
- if (tokadd_mbchar(c) == -1) return -1;
+ if (tokadd_mbchar(p, c) == -1) return -1;
continue;
}
else if ((func & STR_FUNC_QWORDS) && ISSPACE(c)) {
- pushback(c);
+ pushback(p, c);
break;
}
if (c & 0x80) {
- has_nonascii = 1;
- if (enc != *encp) {
- mixed_error(enc, *encp);
+ if (!*enc) {
+ *enc = *encp;
+ }
+ else if (*enc != *encp) {
+ mixed_error(*enc, *encp);
continue;
}
}
- tokadd(c);
+ tokadd(p, c);
}
- *encp = enc;
+ terminate:
+ if (*enc) *encp = *enc;
return c;
}
+static inline rb_strterm_t *
+new_strterm(VALUE v1, VALUE v2, VALUE v3, VALUE v0)
+{
+ return (rb_strterm_t*)rb_imemo_new(imemo_parser_strterm, v1, v2, v3, v0);
+}
+
+/* imemo_parser_strterm for literal */
#define NEW_STRTERM(func, term, paren) \
- rb_node_newnode(NODE_STRTERM, (func), (term) | ((paren) << (CHAR_BIT * 2)), 0)
+ new_strterm((VALUE)(func), (VALUE)(paren), (VALUE)(term), 0)
#ifdef RIPPER
static void
-ripper_flush_string_content(struct parser_params *parser, rb_encoding *enc)
+flush_string_content(struct parser_params *p, rb_encoding *enc)
{
VALUE content = yylval.val;
if (!ripper_is_node_yylval(content))
- content = ripper_new_yylval(0, 0, content);
- if (has_delayed_token()) {
- ptrdiff_t len = lex_p - parser->tokp;
+ content = ripper_new_yylval(p, 0, 0, content);
+ if (has_delayed_token(p)) {
+ ptrdiff_t len = p->lex.pcur - p->lex.ptok;
if (len > 0) {
- rb_enc_str_buf_cat(parser->delayed, parser->tokp, len, enc);
+ rb_enc_str_buf_cat(p->delayed, p->lex.ptok, len, enc);
}
- dispatch_delayed_token(tSTRING_CONTENT);
- parser->tokp = lex_p;
+ dispatch_delayed_token(p, tSTRING_CONTENT);
+ p->lex.ptok = p->lex.pcur;
RNODE(content)->nd_rval = yylval.val;
}
- dispatch_scan_event(tSTRING_CONTENT);
+ dispatch_scan_event(p, tSTRING_CONTENT);
if (yylval.val != content)
RNODE(content)->nd_rval = yylval.val;
yylval.val = content;
}
-
-#define flush_string_content(enc) ripper_flush_string_content(parser, (enc))
#else
-#define flush_string_content(enc) ((void)(enc))
+#define flush_string_content(p, enc) ((void)(enc))
#endif
RUBY_FUNC_EXPORTED const unsigned int ruby_global_name_punct_bits[(0x7e - 0x20 + 31) / 32];
@@ -6287,33 +5864,33 @@ const unsigned int ruby_global_name_punct_bits[] = {
#undef SPECIAL_PUNCT
#endif
-static int
-parser_peek_variable_name(struct parser_params *parser)
+static enum yytokentype
+parser_peek_variable_name(struct parser_params *p)
{
int c;
- const char *p = lex_p;
+ const char *ptr = p->lex.pcur;
- if (p + 1 >= lex_pend) return 0;
- c = *p++;
+ if (ptr + 1 >= p->lex.pend) return 0;
+ c = *ptr++;
switch (c) {
case '$':
- if ((c = *p) == '-') {
- if (++p >= lex_pend) return 0;
- c = *p;
+ if ((c = *ptr) == '-') {
+ if (++ptr >= p->lex.pend) return 0;
+ c = *ptr;
}
else if (is_global_name_punct(c) || ISDIGIT(c)) {
return tSTRING_DVAR;
}
break;
case '@':
- if ((c = *p) == '@') {
- if (++p >= lex_pend) return 0;
- c = *p;
+ if ((c = *ptr) == '@') {
+ if (++ptr >= p->lex.pend) return 0;
+ c = *ptr;
}
break;
case '{':
- lex_p = p;
- command_start = TRUE;
+ p->lex.pcur = ptr;
+ p->command_start = TRUE;
return tSTRING_DBEG;
default:
return 0;
@@ -6323,156 +5900,221 @@ parser_peek_variable_name(struct parser_params *parser)
return 0;
}
-static inline int
-parser_string_term(struct parser_params *parser, int func)
+#define IS_ARG() IS_lex_state(EXPR_ARG_ANY)
+#define IS_END() IS_lex_state(EXPR_END_ANY)
+#define IS_BEG() (IS_lex_state(EXPR_BEG_ANY) || IS_lex_state_all(EXPR_ARG|EXPR_LABELED))
+#define IS_SPCARG(c) (IS_ARG() && space_seen && !ISSPACE(c))
+#define IS_LABEL_POSSIBLE() (\
+ (IS_lex_state(EXPR_LABEL|EXPR_ENDFN) && !cmd_state) || \
+ IS_ARG())
+#define IS_LABEL_SUFFIX(n) (peek_n(p, ':',(n)) && !peek_n(p, ':', (n)+1))
+#define IS_AFTER_OPERATOR() IS_lex_state(EXPR_FNAME | EXPR_DOT)
+
+static inline enum yytokentype
+parser_string_term(struct parser_params *p, int func)
{
- if (!(func & STR_FUNC_REGEXP)) return tSTRING_END;
- set_yylval_num(regx_options());
- dispatch_scan_event(tREGEXP_END);
- return tREGEXP_END;
+ p->lex.strterm = 0;
+ if (func & STR_FUNC_REGEXP) {
+ set_yylval_num(regx_options(p));
+ dispatch_scan_event(p, tREGEXP_END);
+ SET_LEX_STATE(EXPR_END);
+ return tREGEXP_END;
+ }
+ if ((func & STR_FUNC_LABEL) && IS_LABEL_SUFFIX(0)) {
+ nextc(p);
+ SET_LEX_STATE(EXPR_BEG|EXPR_LABEL);
+ return tLABEL_END;
+ }
+ SET_LEX_STATE(EXPR_END);
+ return tSTRING_END;
}
-static int
-parser_parse_string(struct parser_params *parser, NODE *quote)
+static enum yytokentype
+parse_string(struct parser_params *p, rb_strterm_literal_t *quote)
{
- int func = (int)quote->nd_func;
- int term = nd_term(quote);
- int paren = nd_paren(quote);
+ int func = (int)quote->u1.func;
+ int term = (int)quote->u3.term;
+ int paren = (int)quote->u2.paren;
int c, space = 0;
- rb_encoding *enc = current_enc;
+ rb_encoding *enc = p->enc;
+ rb_encoding *base_enc = 0;
+ VALUE lit;
- if (term == STR_TERM_END) return tSTRING_END;
- c = nextc();
+ if (func & STR_FUNC_TERM) {
+ if (func & STR_FUNC_QWORDS) nextc(p); /* delayed term */
+ SET_LEX_STATE(EXPR_END);
+ p->lex.strterm = 0;
+ return func & STR_FUNC_REGEXP ? tREGEXP_END : tSTRING_END;
+ }
+ c = nextc(p);
if ((func & STR_FUNC_QWORDS) && ISSPACE(c)) {
- do {c = nextc();} while (ISSPACE(c));
+ do {c = nextc(p);} while (ISSPACE(c));
+ space = 1;
+ }
+ if (func & STR_FUNC_LIST) {
+ quote->u1.func &= ~STR_FUNC_LIST;
space = 1;
}
- if (c == term && !quote->nd_nest) {
+ if (c == term && !quote->u0.nest) {
if (func & STR_FUNC_QWORDS) {
- quote->u2.id = STR_TERM_END;
+ 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);
return ' ';
}
- return parser_string_term(parser, func);
+ return parser_string_term(p, func);
}
if (space) {
- pushback(c);
+ pushback(p, c);
+ add_delayed_token(p, p->lex.ptok, p->lex.pcur);
return ' ';
}
- newtok();
+ newtok(p);
if ((func & STR_FUNC_EXPAND) && c == '#') {
- int t = parser_peek_variable_name(parser);
+ int t = parser_peek_variable_name(p);
if (t) return t;
- tokadd('#');
- c = nextc();
- }
- pushback(c);
- if (tokadd_string(func, term, paren, &quote->nd_nest,
- &enc) == -1) {
- ruby_sourceline = nd_line(quote);
- if (func & STR_FUNC_REGEXP) {
- if (parser->eofp)
- compile_error(PARSER_ARG "unterminated regexp meets end of file");
- return tREGEXP_END;
- }
- else {
- if (parser->eofp)
- compile_error(PARSER_ARG "unterminated string meets end of file");
- return tSTRING_END;
+ tokadd(p, '#');
+ c = nextc(p);
+ }
+ pushback(p, c);
+ if (tokadd_string(p, func, term, paren, &quote->u0.nest,
+ &enc, &base_enc) == -1) {
+ if (p->eofp) {
+#ifndef RIPPER
+# define unterminated_literal(mesg) yyerror0(mesg)
+#else
+# define unterminated_literal(mesg) compile_error(p, mesg)
+#endif
+ literal_flush(p, p->lex.pcur);
+ if (func & STR_FUNC_REGEXP) {
+ unterminated_literal("unterminated regexp meets end of file");
+ }
+ else {
+ unterminated_literal("unterminated string meets end of file");
+ }
+ quote->u1.func |= STR_FUNC_TERM;
}
}
- tokfix();
- set_yylval_str(STR_NEW3(tok(), toklen(), enc, func));
- flush_string_content(enc);
+ tokfix(p);
+ add_mark_object(p, lit = STR_NEW3(tok(p), toklen(p), enc, func));
+ set_yylval_str(lit);
+ flush_string_content(p, enc);
return tSTRING_CONTENT;
}
-static int
-parser_heredoc_identifier(struct parser_params *parser)
+static enum yytokentype
+heredoc_identifier(struct parser_params *p)
{
- int c = nextc(), term, func = 0;
- int token = tSTRING_BEG;
+ /*
+ * term_len is length of `<<"END"` except `END`,
+ * in this case term_len is 4 (<, <, " and ").
+ */
+ int c = nextc(p), term, func = 0, term_len = 2;
+ enum yytokentype token = tSTRING_BEG;
long len;
+ int newline = 0;
+ int indent = 0;
if (c == '-') {
- c = nextc();
+ c = nextc(p);
+ term_len++;
func = STR_FUNC_INDENT;
}
else if (c == '~') {
- c = nextc();
+ c = nextc(p);
+ term_len++;
func = STR_FUNC_INDENT;
- heredoc_indent = INT_MAX;
- heredoc_line_indent = 0;
+ indent = INT_MAX;
}
switch (c) {
case '\'':
+ term_len++;
func |= str_squote; goto quoted;
case '"':
+ term_len++;
func |= str_dquote; goto quoted;
case '`':
+ term_len++;
token = tXSTRING_BEG;
func |= str_xquote; goto quoted;
quoted:
- newtok();
- tokadd(func);
+ term_len++;
+ newtok(p);
+ tokadd(p, term_len);
+ tokadd(p, func);
term = c;
- while ((c = nextc()) != -1 && c != term) {
- if (tokadd_mbchar(c) == -1) return 0;
+ while ((c = nextc(p)) != -1 && c != term) {
+ if (tokadd_mbchar(p, c) == -1) return 0;
+ if (!newline && c == '\n') newline = 1;
+ else if (newline) newline = 2;
}
if (c == -1) {
- compile_error(PARSER_ARG "unterminated here document identifier");
- return 0;
+ yyerror(NULL, p, "unterminated here document identifier");
+ return -1;
+ }
+ switch (newline) {
+ case 1:
+ rb_warn0("here document identifier ends with a newline");
+ if (--p->tokidx > 0 && p->tokenbuf[p->tokidx] == '\r') --p->tokidx;
+ break;
+ case 2:
+ compile_error(p, "here document identifier across newlines, never match");
+ return -1;
}
break;
default:
- if (!parser_is_identchar()) {
- pushback(c);
+ if (!parser_is_identchar(p)) {
+ pushback(p, c);
if (func & STR_FUNC_INDENT) {
- pushback(heredoc_indent > 0 ? '~' : '-');
+ pushback(p, indent > 0 ? '~' : '-');
}
return 0;
}
- newtok();
- tokadd(func |= str_dquote);
+ newtok(p);
+ tokadd(p, term_len);
+ tokadd(p, func |= str_dquote);
do {
- if (tokadd_mbchar(c) == -1) return 0;
- } while ((c = nextc()) != -1 && parser_is_identchar());
- pushback(c);
+ if (tokadd_mbchar(p, c) == -1) return 0;
+ } while ((c = nextc(p)) != -1 && parser_is_identchar(p));
+ pushback(p, c);
break;
}
- tokfix();
- dispatch_scan_event(tHEREDOC_BEG);
- len = lex_p - lex_pbeg;
- lex_goto_eol(parser);
- lex_strterm = rb_node_newnode(NODE_HEREDOC,
- STR_NEW(tok(), toklen()), /* nd_lit */
- len, /* nd_nth */
- lex_lastline); /* nd_orig */
- nd_set_line(lex_strterm, ruby_sourceline);
- ripper_flush(parser);
+ tokfix(p);
+ dispatch_scan_event(p, tHEREDOC_BEG);
+ len = p->lex.pcur - p->lex.pbeg;
+ lex_goto_eol(p);
+
+ p->lex.strterm = new_strterm(STR_NEW(tok(p), toklen(p)), /* term */
+ p->lex.lastline, /* lastline */
+ len, /* lastidx */
+ p->ruby_sourceline);
+ p->lex.strterm->flags |= STRTERM_HEREDOC;
+
+ token_flush(p);
+ p->heredoc_indent = indent;
+ p->heredoc_line_indent = 0;
return token;
}
static void
-parser_heredoc_restore(struct parser_params *parser, NODE *here)
+heredoc_restore(struct parser_params *p, rb_strterm_heredoc_t *here)
{
VALUE line;
- lex_strterm = 0;
- line = here->nd_orig;
- lex_lastline = line;
- lex_pbeg = RSTRING_PTR(line);
- lex_pend = lex_pbeg + RSTRING_LEN(line);
- lex_p = lex_pbeg + here->nd_nth;
- heredoc_end = ruby_sourceline;
- ruby_sourceline = nd_line(here);
- dispose_string(here->nd_lit);
- rb_gc_force_recycle((VALUE)here);
- ripper_flush(parser);
+ p->lex.strterm = 0;
+ line = here->lastline;
+ p->lex.lastline = line;
+ p->lex.pbeg = RSTRING_PTR(line);
+ p->lex.pend = p->lex.pbeg + RSTRING_LEN(line);
+ p->lex.pcur = p->lex.pbeg + here->u3.lastidx;
+ p->heredoc_end = p->ruby_sourceline;
+ p->ruby_sourceline = (int)here->sourceline;
+ token_flush(p);
}
static int
@@ -6496,6 +6138,11 @@ dedent_string(VALUE string, int width)
break;
}
}
+ if (!i) return 0;
+ rb_str_modify(string);
+ str = RSTRING_PTR(string);
+ if (RSTRING_LEN(string) != len)
+ rb_fatal("literal string changed: %+"PRIsVALUE, string);
MEMMOVE(str, str + i, char, len - i);
rb_str_set_len(string, len - i);
return i;
@@ -6503,30 +6150,50 @@ dedent_string(VALUE string, int width)
#ifndef RIPPER
static NODE *
-parser_heredoc_dedent(struct parser_params *parser, NODE *root)
+heredoc_dedent(struct parser_params *p, NODE *root)
{
- NODE *node, *str_node;
- int bol = TRUE;
- int indent = heredoc_indent;
+ NODE *node, *str_node, *prev_node;
+ int indent = p->heredoc_indent;
+ VALUE prev_lit = 0;
if (indent <= 0) return root;
- heredoc_indent = 0;
+ p->heredoc_indent = 0;
if (!root) return root;
- node = str_node = root;
+ prev_node = node = str_node = root;
if (nd_type(root) == NODE_ARRAY) str_node = root->nd_head;
while (str_node) {
VALUE lit = str_node->nd_lit;
- if (bol) dedent_string(lit, indent);
- bol = TRUE;
+ if (str_node->flags & NODE_FL_NEWLINE) {
+ dedent_string(lit, indent);
+ }
+ if (!prev_lit) {
+ prev_lit = lit;
+ }
+ else if (!literal_concat0(p, prev_lit, lit)) {
+ return 0;
+ }
+ else {
+ NODE *end = node->nd_end;
+ node = prev_node->nd_next = node->nd_next;
+ if (!node) {
+ if (nd_type(prev_node) == NODE_DSTR)
+ nd_set_type(prev_node, NODE_STR);
+ break;
+ }
+ node->nd_end = end;
+ goto next_str;
+ }
str_node = 0;
- while ((node = node->nd_next) != 0 && nd_type(node) == NODE_ARRAY) {
+ while ((node = (prev_node = node)->nd_next) != 0) {
+ next_str:
+ if (nd_type(node) != NODE_ARRAY) break;
if ((str_node = node->nd_head) != 0) {
enum node_type type = nd_type(str_node);
if (type == NODE_STR || type == NODE_DSTR) break;
- bol = FALSE;
+ prev_lit = 0;
str_node = 0;
}
}
@@ -6535,16 +6202,25 @@ parser_heredoc_dedent(struct parser_params *parser, NODE *root)
}
#else /* RIPPER */
static VALUE
-parser_heredoc_dedent(struct parser_params *parser, VALUE array)
+heredoc_dedent(struct parser_params *p, VALUE array)
{
- int indent = heredoc_indent;
+ int indent = p->heredoc_indent;
if (indent <= 0) return array;
- heredoc_indent = 0;
+ p->heredoc_indent = 0;
dispatch2(heredoc_dedent, array, INT2NUM(indent));
return array;
}
+/*
+ * call-seq:
+ * Ripper.dedent_string(input, width) -> Integer
+ *
+ * USE OF RIPPER LIBRARY ONLY.
+ *
+ * Strips up to +width+ leading whitespaces from +input+,
+ * and returns the stripped column width.
+ */
static VALUE
parser_dedent_string(VALUE self, VALUE input, VALUE width)
{
@@ -6552,29 +6228,27 @@ parser_dedent_string(VALUE self, VALUE input, VALUE width)
StringValue(input);
wid = NUM2UINT(width);
- rb_str_modify(input);
col = dedent_string(input, wid);
return INT2NUM(col);
}
#endif
static int
-parser_whole_match_p(struct parser_params *parser,
- const char *eos, long len, int indent)
+whole_match_p(struct parser_params *p, const char *eos, long len, int indent)
{
- const char *p = lex_pbeg;
+ const char *ptr = p->lex.pbeg;
long n;
if (indent) {
- while (*p && ISSPACE(*p)) p++;
+ while (*ptr && ISSPACE(*ptr)) ptr++;
}
- n = lex_pend - (p + len);
+ n = p->lex.pend - (ptr + len);
if (n < 0) return FALSE;
- if (n > 0 && p[len] != '\n') {
- if (p[len] != '\r') return FALSE;
- if (n <= 1 || p[len+1] != '\n') return FALSE;
+ if (n > 0 && ptr[len] != '\n') {
+ if (ptr[len] != '\r') return FALSE;
+ if (n <= 1 || ptr[len+1] != '\n') return FALSE;
}
- return strncmp(eos, p, len) == 0;
+ return strncmp(eos, ptr, len) == 0;
}
#define NUM_SUFFIX_R (1<<0)
@@ -6582,12 +6256,12 @@ parser_whole_match_p(struct parser_params *parser,
#define NUM_SUFFIX_ALL 3
static int
-parser_number_literal_suffix(struct parser_params *parser, int mask)
+number_literal_suffix(struct parser_params *p, int mask)
{
int c, result = 0;
- const char *lastp = lex_p;
+ const char *lastp = p->lex.pcur;
- while ((c = nextc()) != -1) {
+ while ((c = nextc(p)) != -1) {
if ((mask & NUM_SUFFIX_I) && c == 'i') {
result |= (mask & NUM_SUFFIX_I);
mask &= ~NUM_SUFFIX_I;
@@ -6601,16 +6275,17 @@ parser_number_literal_suffix(struct parser_params *parser, int mask)
continue;
}
if (!ISASCII(c) || ISALPHA(c) || c == '_') {
- lex_p = lastp;
+ p->lex.pcur = lastp;
+ literal_flush(p, p->lex.pcur);
return 0;
}
- pushback(c);
+ pushback(p, c);
if (c == '.') {
- c = peekc_n(1);
+ c = peekc_n(p, 1);
if (ISDIGIT(c)) {
- yyerror("unexpected fraction part after numeric literal");
- lex_p += 2;
- while (parser_is_identchar()) nextc();
+ yyerror0("unexpected fraction part after numeric literal");
+ p->lex.pcur += 2;
+ while (parser_is_identchar(p)) nextc(p);
}
}
break;
@@ -6618,237 +6293,266 @@ parser_number_literal_suffix(struct parser_params *parser, int mask)
return result;
}
-static int
-parser_set_number_literal(struct parser_params *parser, VALUE v, int type, int suffix)
+static enum yytokentype
+set_number_literal(struct parser_params *p, VALUE v,
+ enum yytokentype type, int suffix)
{
if (suffix & NUM_SUFFIX_I) {
v = rb_complex_raw(INT2FIX(0), v);
type = tIMAGINARY;
}
set_yylval_literal(v);
- SET_LEX_STATE(EXPR_ENDARG);
+ add_mark_object(p, v);
+ SET_LEX_STATE(EXPR_END);
return type;
}
-static int
-parser_set_integer_literal(struct parser_params *parser, VALUE v, int suffix)
+static enum yytokentype
+set_integer_literal(struct parser_params *p, VALUE v, int suffix)
{
- int type = tINTEGER;
+ enum yytokentype type = tINTEGER;
if (suffix & NUM_SUFFIX_R) {
v = rb_rational_raw1(v);
type = tRATIONAL;
}
- return set_number_literal(v, type, suffix);
+ return set_number_literal(p, v, type, suffix);
}
#ifdef RIPPER
static void
-ripper_dispatch_heredoc_end(struct parser_params *parser)
+dispatch_heredoc_end(struct parser_params *p)
{
VALUE str;
- if (has_delayed_token())
- dispatch_delayed_token(tSTRING_CONTENT);
- str = STR_NEW(parser->tokp, lex_pend - parser->tokp);
- ripper_dispatch1(parser, ripper_token2eventid(tHEREDOC_END), str);
- lex_goto_eol(parser);
- ripper_flush(parser);
+ if (has_delayed_token(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);
+ lex_goto_eol(p);
+ token_flush(p);
}
-#define dispatch_heredoc_end() ripper_dispatch_heredoc_end(parser)
#else
-#define dispatch_heredoc_end() ((void)0)
+#define dispatch_heredoc_end(p) ((void)0)
#endif
-static int
-parser_here_document(struct parser_params *parser, NODE *here)
+static enum yytokentype
+here_document(struct parser_params *p, rb_strterm_heredoc_t *here)
{
int c, func, indent = 0;
- const char *eos, *p, *pend;
+ const char *eos, *ptr, *ptr_end;
long len;
VALUE str = 0;
- rb_encoding *enc = current_enc;
+ rb_encoding *enc = p->enc;
+ rb_encoding *base_enc = 0;
+ int bol;
- eos = RSTRING_PTR(here->nd_lit);
- len = RSTRING_LEN(here->nd_lit) - 1;
+ eos = RSTRING_PTR(here->term);
+ len = RSTRING_LEN(here->term) - 2; /* here->term includes term_len and func */
+ eos++; /* skip term_len */
indent = (func = *eos++) & STR_FUNC_INDENT;
- if ((c = nextc()) == -1) {
+ if ((c = nextc(p)) == -1) {
error:
- compile_error(PARSER_ARG "can't find string \"%s\" anywhere before EOF", eos);
+ compile_error(p, "can't find string \"%s\" anywhere before EOF", eos);
#ifdef RIPPER
- if (!has_delayed_token()) {
- dispatch_scan_event(tSTRING_CONTENT);
+ if (!has_delayed_token(p)) {
+ dispatch_scan_event(p, tSTRING_CONTENT);
}
else {
if (str) {
- rb_str_append(parser->delayed, str);
+ rb_str_append(p->delayed, str);
}
- else if ((len = lex_p - parser->tokp) > 0) {
+ else if ((len = p->lex.pcur - p->lex.ptok) > 0) {
if (!(func & STR_FUNC_REGEXP) && rb_enc_asciicompat(enc)) {
int cr = ENC_CODERANGE_UNKNOWN;
- rb_str_coderange_scan_restartable(parser->tokp, lex_p, enc, &cr);
+ rb_str_coderange_scan_restartable(p->lex.ptok, p->lex.pcur, enc, &cr);
if (cr != ENC_CODERANGE_7BIT &&
- current_enc == rb_usascii_encoding() &&
+ p->enc == rb_usascii_encoding() &&
enc != rb_utf8_encoding()) {
enc = rb_ascii8bit_encoding();
}
}
- rb_enc_str_buf_cat(parser->delayed, parser->tokp, len, enc);
+ rb_enc_str_buf_cat(p->delayed, p->lex.ptok, len, enc);
}
- dispatch_delayed_token(tSTRING_CONTENT);
+ dispatch_delayed_token(p, tSTRING_CONTENT);
}
- lex_goto_eol(parser);
+ lex_goto_eol(p);
#endif
restore:
- heredoc_restore(lex_strterm);
+ heredoc_restore(p, &p->lex.strterm->u.heredoc);
+ p->lex.strterm = 0;
return 0;
}
- if (was_bol() && whole_match_p(eos, len, indent)) {
- dispatch_heredoc_end();
- heredoc_restore(lex_strterm);
+ bol = was_bol(p);
+ /* `heredoc_line_indent == -1` means
+ * - "after an interpolation in the same line", or
+ * - "in a continuing line"
+ */
+ if (bol &&
+ (p->heredoc_line_indent != -1 || (p->heredoc_line_indent = 0)) &&
+ whole_match_p(p, eos, len, indent)) {
+ dispatch_heredoc_end(p);
+ heredoc_restore(p, &p->lex.strterm->u.heredoc);
+ p->lex.strterm = 0;
+ SET_LEX_STATE(EXPR_END);
return tSTRING_END;
}
if (!(func & STR_FUNC_EXPAND)) {
do {
- p = RSTRING_PTR(lex_lastline);
- pend = lex_pend;
- if (pend > p) {
- switch (pend[-1]) {
+ ptr = RSTRING_PTR(p->lex.lastline);
+ ptr_end = p->lex.pend;
+ if (ptr_end > ptr) {
+ switch (ptr_end[-1]) {
case '\n':
- if (--pend == p || pend[-1] != '\r') {
- pend++;
+ if (--ptr_end == ptr || ptr_end[-1] != '\r') {
+ ptr_end++;
break;
}
case '\r':
- --pend;
+ --ptr_end;
}
}
- if (heredoc_indent > 0) {
+ if (p->heredoc_indent > 0) {
long i = 0;
- while (p + i < pend && parser_update_heredoc_indent(parser, p[i]))
+ while (ptr + i < ptr_end && parser_update_heredoc_indent(p, ptr[i]))
i++;
- heredoc_line_indent = 0;
+ p->heredoc_line_indent = 0;
}
if (str)
- rb_str_cat(str, p, pend - p);
+ rb_str_cat(str, ptr, ptr_end - ptr);
else
- str = STR_NEW(p, pend - p);
- if (pend < lex_pend) rb_str_cat(str, "\n", 1);
- lex_goto_eol(parser);
- if (heredoc_indent > 0) {
- set_yylval_str(str);
- flush_string_content(enc);
- return tSTRING_CONTENT;
+ str = STR_NEW(ptr, ptr_end - ptr);
+ if (ptr_end < p->lex.pend) rb_str_cat(str, "\n", 1);
+ lex_goto_eol(p);
+ if (p->heredoc_indent > 0) {
+ goto flush_str;
}
- if (nextc() == -1) {
+ if (nextc(p) == -1) {
if (str) {
- dispose_string(str);
str = 0;
}
goto error;
}
- } while (!whole_match_p(eos, len, indent));
+ } while (!whole_match_p(p, eos, len, indent));
}
else {
/* int mb = ENC_CODERANGE_7BIT, *mbp = &mb;*/
- newtok();
+ newtok(p);
if (c == '#') {
- int t = parser_peek_variable_name(parser);
+ int t = parser_peek_variable_name(p);
+ if (p->heredoc_line_indent != -1) {
+ if (p->heredoc_indent > p->heredoc_line_indent) {
+ p->heredoc_indent = p->heredoc_line_indent;
+ }
+ p->heredoc_line_indent = -1;
+ }
if (t) return t;
- tokadd('#');
- c = nextc();
+ tokadd(p, '#');
+ c = nextc(p);
}
do {
- pushback(c);
- if ((c = tokadd_string(func, '\n', 0, NULL, &enc)) == -1) {
- if (parser->eofp) goto error;
+ pushback(p, c);
+ enc = p->enc;
+ if ((c = tokadd_string(p, func, '\n', 0, NULL, &enc, &base_enc)) == -1) {
+ if (p->eofp) goto error;
goto restore;
}
if (c != '\n') {
+ if (c == '\\') p->heredoc_line_indent = -1;
flush:
- set_yylval_str(STR_NEW3(tok(), toklen(), enc, func));
- flush_string_content(enc);
+ str = STR_NEW3(tok(p), toklen(p), enc, func);
+ flush_str:
+ set_yylval_str(str);
+ add_mark_object(p, str);
+#ifndef RIPPER
+ if (bol) yylval.node->flags |= NODE_FL_NEWLINE;
+#endif
+ flush_string_content(p, enc);
return tSTRING_CONTENT;
}
- tokadd(nextc());
- if (heredoc_indent > 0) {
- lex_goto_eol(parser);
+ tokadd(p, nextc(p));
+ if (p->heredoc_indent > 0) {
+ lex_goto_eol(p);
goto flush;
}
/* if (mbp && mb == ENC_CODERANGE_UNKNOWN) mbp = 0;*/
- if ((c = nextc()) == -1) goto error;
- } while (!whole_match_p(eos, len, indent));
- str = STR_NEW3(tok(), toklen(), enc, func);
+ if ((c = nextc(p)) == -1) goto error;
+ } while (!whole_match_p(p, eos, len, indent));
+ str = STR_NEW3(tok(p), toklen(p), enc, func);
}
- dispatch_heredoc_end();
+ dispatch_heredoc_end(p);
#ifdef RIPPER
- str = ripper_new_yylval(ripper_token2eventid(tSTRING_CONTENT),
+ str = ripper_new_yylval(p, ripper_token2eventid(tSTRING_CONTENT),
yylval.val, str);
#endif
- heredoc_restore(lex_strterm);
- lex_strterm = NEW_STRTERM(func, STR_TERM_END, 0);
+ heredoc_restore(p, &p->lex.strterm->u.heredoc);
+ p->lex.strterm = NEW_STRTERM(func | STR_FUNC_TERM, 0, 0);
set_yylval_str(str);
+ add_mark_object(p, str);
+#ifndef RIPPER
+ if (bol) yylval.node->flags |= NODE_FL_NEWLINE;
+#endif
return tSTRING_CONTENT;
}
#include "lex.c"
-static void
-arg_ambiguous_gen(struct parser_params *parser, char c)
+static int
+arg_ambiguous(struct parser_params *p, char c)
{
#ifndef RIPPER
rb_warning1("ambiguous first argument; put parentheses or a space even after `%c' operator", WARN_I(c));
#else
dispatch1(arg_ambiguous, rb_usascii_str_new(&c, 1));
#endif
+ return TRUE;
}
-#define arg_ambiguous(c) (arg_ambiguous_gen(parser, (c)), 1)
static ID
-formal_argument_gen(struct parser_params *parser, ID lhs)
+formal_argument(struct parser_params *p, ID lhs)
{
switch (id_type(lhs)) {
case ID_LOCAL:
break;
#ifndef RIPPER
case ID_CONST:
- yyerror("formal argument cannot be a constant");
+ yyerror0("formal argument cannot be a constant");
return 0;
case ID_INSTANCE:
- yyerror("formal argument cannot be an instance variable");
+ yyerror0("formal argument cannot be an instance variable");
return 0;
case ID_GLOBAL:
- yyerror("formal argument cannot be a global variable");
+ yyerror0("formal argument cannot be a global variable");
return 0;
case ID_CLASS:
- yyerror("formal argument cannot be a class variable");
+ yyerror0("formal argument cannot be a class variable");
return 0;
default:
- yyerror("formal argument must be local variable");
+ yyerror0("formal argument must be local variable");
return 0;
#else
default:
lhs = dispatch1(param_error, lhs);
- ripper_error();
+ ripper_error(p);
return 0;
#endif
}
- shadowing_lvar(lhs);
+ shadowing_lvar(p, lhs);
return lhs;
}
static int
-lvar_defined_gen(struct parser_params *parser, ID id)
+lvar_defined(struct parser_params *p, ID id)
{
- return (dyna_in_block() && dvar_defined_get(id)) || local_id(id);
+ return (dyna_in_block(p) && dvar_defined(p, id)) || local_id(p, id);
}
/* emacsen -*- hack */
static long
-parser_encode_length(struct parser_params *parser, const char *name, long len)
+parser_encode_length(struct parser_params *p, const char *name, long len)
{
long nlen;
@@ -6868,7 +6572,7 @@ parser_encode_length(struct parser_params *parser, const char *name, long len)
}
static void
-parser_set_encode(struct parser_params *parser, const char *name)
+parser_set_encode(struct parser_params *p, const char *name)
{
int idx = rb_enc_find_index(name);
rb_encoding *enc;
@@ -6879,7 +6583,7 @@ parser_set_encode(struct parser_params *parser, const char *name)
error:
excargs[0] = rb_eArgError;
excargs[2] = rb_make_backtrace();
- rb_ary_unshift(excargs[2], rb_sprintf("%"PRIsVALUE":%d", ruby_sourcefile_string, ruby_sourceline));
+ rb_ary_unshift(excargs[2], rb_sprintf("%"PRIsVALUE":%d", p->ruby_sourcefile_string, p->ruby_sourceline));
rb_exc_raise(rb_make_exception(3, excargs));
}
enc = rb_enc_from_index(idx);
@@ -6887,10 +6591,10 @@ parser_set_encode(struct parser_params *parser, const char *name)
excargs[1] = rb_sprintf("%s is not ASCII compatible", rb_enc_name(enc));
goto error;
}
- parser->enc = enc;
+ p->enc = enc;
#ifndef RIPPER
- if (ruby_debug_lines) {
- VALUE lines = ruby_debug_lines;
+ if (p->debug_lines) {
+ VALUE lines = p->debug_lines;
long i, n = RARRAY_LEN(lines);
for (i = 0; i < n; ++i) {
rb_enc_associate_index(RARRAY_AREF(lines, i), idx);
@@ -6900,31 +6604,31 @@ parser_set_encode(struct parser_params *parser, const char *name)
}
static int
-comment_at_top(struct parser_params *parser)
+comment_at_top(struct parser_params *p)
{
- const char *p = lex_pbeg, *pend = lex_p - 1;
- if (parser->line_count != (parser->has_shebang ? 2 : 1)) return 0;
- while (p < pend) {
- if (!ISSPACE(*p)) return 0;
- p++;
+ const char *ptr = p->lex.pbeg, *ptr_end = p->lex.pcur - 1;
+ if (p->line_count != (p->has_shebang ? 2 : 1)) return 0;
+ while (ptr < ptr_end) {
+ if (!ISSPACE(*ptr)) return 0;
+ ptr++;
}
return 1;
}
-typedef long (*rb_magic_comment_length_t)(struct parser_params *parser, const char *name, long len);
-typedef void (*rb_magic_comment_setter_t)(struct parser_params *parser, const char *name, const char *val);
+typedef long (*rb_magic_comment_length_t)(struct parser_params *p, const char *name, long len);
+typedef void (*rb_magic_comment_setter_t)(struct parser_params *p, const char *name, const char *val);
static void
-magic_comment_encoding(struct parser_params *parser, const char *name, const char *val)
+magic_comment_encoding(struct parser_params *p, const char *name, const char *val)
{
- if (!comment_at_top(parser)) {
+ if (!comment_at_top(p)) {
return;
}
- parser_set_encode(parser, val);
+ parser_set_encode(p, val);
}
static int
-parser_get_bool(struct parser_params *parser, const char *name, const char *val)
+parser_get_bool(struct parser_params *p, const char *name, const char *val)
{
switch (*val) {
case 't': case 'T':
@@ -6938,42 +6642,42 @@ parser_get_bool(struct parser_params *parser, const char *name, const char *val)
}
break;
}
- rb_compile_warning(ruby_sourcefile, ruby_sourceline, "invalid value for %s: %s", name, val);
+ rb_compile_warning(p->ruby_sourcefile, p->ruby_sourceline, "invalid value for %s: %s", name, val);
return -1;
}
static void
-parser_set_token_info(struct parser_params *parser, const char *name, const char *val)
+parser_set_token_info(struct parser_params *p, const char *name, const char *val)
{
- int b = parser_get_bool(parser, name, val);
- if (b >= 0) parser->token_info_enabled = b;
+ int b = parser_get_bool(p, name, val);
+ if (b >= 0) p->token_info_enabled = b;
}
static void
-parser_set_compile_option_flag(struct parser_params *parser, const char *name, const char *val)
+parser_set_compile_option_flag(struct parser_params *p, const char *name, const char *val)
{
int b;
- if (parser->token_seen) {
+ if (p->token_seen) {
rb_warning1("`%s' is ignored after any tokens", WARN_S(name));
return;
}
- b = parser_get_bool(parser, name, val);
+ b = parser_get_bool(p, name, val);
if (b < 0) return;
- if (!parser->compile_option)
- parser->compile_option = rb_obj_hide(rb_ident_hash_new());
- rb_hash_aset(parser->compile_option, ID2SYM(rb_intern(name)),
+ if (!p->compile_option)
+ p->compile_option = rb_obj_hide(rb_ident_hash_new());
+ rb_hash_aset(p->compile_option, ID2SYM(rb_intern(name)),
(b ? Qtrue : Qfalse));
}
# if WARN_PAST_SCOPE
static void
-parser_set_past_scope(struct parser_params *parser, const char *name, const char *val)
+parser_set_past_scope(struct parser_params *p, const char *name, const char *val)
{
- int b = parser_get_bool(parser, name, val);
- if (b >= 0) parser->past_scope_enabled = b;
+ int b = parser_get_bool(p, name, val);
+ if (b >= 0) p->past_scope_enabled = b;
}
# endif
@@ -7027,7 +6731,7 @@ magic_comment_marker(const char *str, long len)
}
static int
-parser_magic_comment(struct parser_params *parser, const char *str, long len)
+parser_magic_comment(struct parser_params *p, const char *str, long len)
{
int indicator = 0;
VALUE name = 0, val = 0;
@@ -7048,7 +6752,7 @@ parser_magic_comment(struct parser_params *parser, const char *str, long len)
/* %r"([^\\s\'\":;]+)\\s*:\\s*(\"(?:\\\\.|[^\"])*\"|[^\"\\s;]+)[\\s;]*" */
while (len > 0) {
- const struct magic_comment *p = magic_comments;
+ const struct magic_comment *mc = magic_comments;
char *s;
int i;
long n = 0;
@@ -7111,16 +6815,16 @@ parser_magic_comment(struct parser_params *parser, const char *str, long len)
if (s[i] == '-') s[i] = '_';
}
do {
- if (STRNCASECMP(p->name, s, n) == 0 && !p->name[n]) {
+ if (STRNCASECMP(mc->name, s, n) == 0 && !mc->name[n]) {
n = vend - vbeg;
- if (p->length) {
- n = (*p->length)(parser, vbeg, n);
+ if (mc->length) {
+ n = (*mc->length)(p, vbeg, n);
}
str_copy(val, vbeg, n);
- (*p->func)(parser, p->name, RSTRING_PTR(val));
+ (*mc->func)(p, mc->name, RSTRING_PTR(val));
break;
}
- } while (++p < magic_comments + numberof(magic_comments));
+ } while (++mc < magic_comments + numberof(magic_comments));
#ifdef RIPPER
str_copy(val, vbeg, vend - vbeg);
dispatch2(magic_comment, name, val);
@@ -7131,7 +6835,7 @@ parser_magic_comment(struct parser_params *parser, const char *str, long len)
}
static void
-set_file_encoding(struct parser_params *parser, const char *str, const char *send)
+set_file_encoding(struct parser_params *p, const char *str, const char *send)
{
int sep = 0;
const char *beg = str;
@@ -7168,61 +6872,53 @@ set_file_encoding(struct parser_params *parser, const char *str, const char *sen
}
beg = str;
while ((*str == '-' || *str == '_' || ISALNUM(*str)) && ++str < send);
- s = rb_str_new(beg, parser_encode_length(parser, beg, str - beg));
- parser_set_encode(parser, RSTRING_PTR(s));
+ s = rb_str_new(beg, parser_encode_length(p, beg, str - beg));
+ parser_set_encode(p, RSTRING_PTR(s));
rb_str_resize(s, 0);
}
static void
-parser_prepare(struct parser_params *parser)
+parser_prepare(struct parser_params *p)
{
- int c = nextc();
+ int c = nextc(p);
+ p->token_info_enabled = !compile_for_eval && RTEST(ruby_verbose);
switch (c) {
case '#':
- if (peek('!')) parser->has_shebang = 1;
+ if (peek(p, '!')) p->has_shebang = 1;
break;
case 0xef: /* UTF-8 BOM marker */
- if (lex_pend - lex_p >= 2 &&
- (unsigned char)lex_p[0] == 0xbb &&
- (unsigned char)lex_p[1] == 0xbf) {
- parser->enc = rb_utf8_encoding();
- lex_p += 2;
- lex_pbeg = lex_p;
+ if (p->lex.pend - p->lex.pcur >= 2 &&
+ (unsigned char)p->lex.pcur[0] == 0xbb &&
+ (unsigned char)p->lex.pcur[1] == 0xbf) {
+ p->enc = rb_utf8_encoding();
+ p->lex.pcur += 2;
+ p->lex.pbeg = p->lex.pcur;
return;
}
break;
case EOF:
return;
}
- pushback(c);
- parser->enc = rb_enc_get(lex_lastline);
- parser->token_info_enabled = !compile_for_eval && RTEST(ruby_verbose);
+ pushback(p, c);
+ p->enc = rb_enc_get(p->lex.lastline);
}
-#define IS_ARG() IS_lex_state(EXPR_ARG_ANY)
-#define IS_END() IS_lex_state(EXPR_END_ANY)
-#define IS_BEG() (IS_lex_state(EXPR_BEG_ANY) || IS_lex_state_all(EXPR_ARG|EXPR_LABELED))
-#define IS_SPCARG(c) (IS_ARG() && space_seen && !ISSPACE(c))
-#define IS_LABEL_POSSIBLE() (\
- (IS_lex_state(EXPR_LABEL|EXPR_ENDFN) && !cmd_state) || \
- IS_ARG())
-#define IS_LABEL_SUFFIX(n) (peek_n(':',(n)) && !peek_n(':', (n)+1))
-#define IS_AFTER_OPERATOR() IS_lex_state(EXPR_FNAME | EXPR_DOT)
-
#ifndef RIPPER
-#define ambiguous_operator(op, syn) ( \
+#define ambiguous_operator(tok, op, syn) ( \
rb_warning0("`"op"' after local variable or literal is interpreted as binary operator"), \
rb_warning0("even though it seems like "syn""))
#else
-#define ambiguous_operator(op, syn) dispatch2(operator_ambiguous, ripper_intern(op), rb_str_new_cstr(syn))
+#define ambiguous_operator(tok, op, syn) \
+ dispatch2(operator_ambiguous, TOKEN2VAL(tok), rb_str_new_cstr(syn))
#endif
-#define warn_balanced(op, syn) ((void) \
+#define warn_balanced(tok, op, syn) ((void) \
(!IS_lex_state_for(last_state, EXPR_CLASS|EXPR_DOT|EXPR_FNAME|EXPR_ENDFN) && \
space_seen && !ISSPACE(c) && \
- (ambiguous_operator(op, syn), 0)))
+ (ambiguous_operator(tok, op, syn), 0)), \
+ (enum yytokentype)(tok))
static VALUE
-parse_rational(struct parser_params *parser, char *str, int len, int seen_point)
+parse_rational(struct parser_params *p, char *str, int len, int seen_point)
{
VALUE v;
char *point = &str[seen_point];
@@ -7232,26 +6928,34 @@ parse_rational(struct parser_params *parser, char *str, int len, int seen_point)
return rb_rational_new(v, rb_int_positive_pow(10, fraclen));
}
-static int
-parse_numeric(struct parser_params *parser, int c)
+static enum yytokentype
+no_digits(struct parser_params *p)
+{
+ yyerror0("numeric literal without digits");
+ if (peek(p, '_')) nextc(p);
+ /* dummy 0, for tUMINUS_NUM at numeric */
+ return set_integer_literal(p, INT2FIX(0), 0);
+}
+
+static enum yytokentype
+parse_numeric(struct parser_params *p, int c)
{
int is_float, seen_point, seen_e, nondigit;
int suffix;
is_float = seen_point = seen_e = nondigit = 0;
SET_LEX_STATE(EXPR_END);
- newtok();
+ newtok(p);
if (c == '-' || c == '+') {
- tokadd(c);
- c = nextc();
+ tokadd(p, c);
+ c = nextc(p);
}
if (c == '0') {
-#define no_digits() do {yyerror("numeric literal without digits"); return 0;} while (0)
- int start = toklen();
- c = nextc();
+ int start = toklen(p);
+ c = nextc(p);
if (c == 'x' || c == 'X') {
/* hexadecimal */
- c = nextc();
+ c = nextc(p);
if (c != -1 && ISXDIGIT(c)) {
do {
if (c == '_') {
@@ -7261,21 +6965,21 @@ parse_numeric(struct parser_params *parser, int c)
}
if (!ISXDIGIT(c)) break;
nondigit = 0;
- tokadd(c);
- } while ((c = nextc()) != -1);
+ tokadd(p, c);
+ } while ((c = nextc(p)) != -1);
}
- pushback(c);
- tokfix();
- if (toklen() == start) {
- no_digits();
+ pushback(p, c);
+ tokfix(p);
+ if (toklen(p) == start) {
+ return no_digits(p);
}
else if (nondigit) goto trailing_uc;
- suffix = number_literal_suffix(NUM_SUFFIX_ALL);
- return set_integer_literal(rb_cstr_to_inum(tok(), 16, FALSE), suffix);
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ return set_integer_literal(p, rb_cstr_to_inum(tok(p), 16, FALSE), suffix);
}
if (c == 'b' || c == 'B') {
/* binary */
- c = nextc();
+ c = nextc(p);
if (c == '0' || c == '1') {
do {
if (c == '_') {
@@ -7285,21 +6989,21 @@ parse_numeric(struct parser_params *parser, int c)
}
if (c != '0' && c != '1') break;
nondigit = 0;
- tokadd(c);
- } while ((c = nextc()) != -1);
+ tokadd(p, c);
+ } while ((c = nextc(p)) != -1);
}
- pushback(c);
- tokfix();
- if (toklen() == start) {
- no_digits();
+ pushback(p, c);
+ tokfix(p);
+ if (toklen(p) == start) {
+ return no_digits(p);
}
else if (nondigit) goto trailing_uc;
- suffix = number_literal_suffix(NUM_SUFFIX_ALL);
- return set_integer_literal(rb_cstr_to_inum(tok(), 2, FALSE), suffix);
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ return set_integer_literal(p, rb_cstr_to_inum(tok(p), 2, FALSE), suffix);
}
if (c == 'd' || c == 'D') {
/* decimal */
- c = nextc();
+ c = nextc(p);
if (c != -1 && ISDIGIT(c)) {
do {
if (c == '_') {
@@ -7309,17 +7013,17 @@ parse_numeric(struct parser_params *parser, int c)
}
if (!ISDIGIT(c)) break;
nondigit = 0;
- tokadd(c);
- } while ((c = nextc()) != -1);
+ tokadd(p, c);
+ } while ((c = nextc(p)) != -1);
}
- pushback(c);
- tokfix();
- if (toklen() == start) {
- no_digits();
+ pushback(p, c);
+ tokfix(p);
+ if (toklen(p) == start) {
+ return no_digits(p);
}
else if (nondigit) goto trailing_uc;
- suffix = number_literal_suffix(NUM_SUFFIX_ALL);
- return set_integer_literal(rb_cstr_to_inum(tok(), 10, FALSE), suffix);
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ return set_integer_literal(p, rb_cstr_to_inum(tok(p), 10, FALSE), suffix);
}
if (c == '_') {
/* 0_0 */
@@ -7327,9 +7031,9 @@ parse_numeric(struct parser_params *parser, int c)
}
if (c == 'o' || c == 'O') {
/* prefixed octal */
- c = nextc();
+ c = nextc(p);
if (c == -1 || c == '_' || !ISDIGIT(c)) {
- no_digits();
+ return no_digits(p);
}
}
if (c >= '0' && c <= '7') {
@@ -7344,31 +7048,31 @@ parse_numeric(struct parser_params *parser, int c)
if (c < '0' || c > '9') break;
if (c > '7') goto invalid_octal;
nondigit = 0;
- tokadd(c);
- } while ((c = nextc()) != -1);
- if (toklen() > start) {
- pushback(c);
- tokfix();
+ tokadd(p, c);
+ } while ((c = nextc(p)) != -1);
+ if (toklen(p) > start) {
+ pushback(p, c);
+ tokfix(p);
if (nondigit) goto trailing_uc;
- suffix = number_literal_suffix(NUM_SUFFIX_ALL);
- return set_integer_literal(rb_cstr_to_inum(tok(), 8, FALSE), suffix);
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ return set_integer_literal(p, rb_cstr_to_inum(tok(p), 8, FALSE), suffix);
}
if (nondigit) {
- pushback(c);
+ pushback(p, c);
goto trailing_uc;
}
}
if (c > '7' && c <= '9') {
invalid_octal:
- yyerror("Invalid octal digit");
+ yyerror0("Invalid octal digit");
}
else if (c == '.' || c == 'e' || c == 'E') {
- tokadd('0');
+ tokadd(p, '0');
}
else {
- pushback(c);
- suffix = number_literal_suffix(NUM_SUFFIX_ALL);
- return set_integer_literal(INT2FIX(0), suffix);
+ pushback(p, c);
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ return set_integer_literal(p, INT2FIX(0), suffix);
}
}
@@ -7377,7 +7081,7 @@ parse_numeric(struct parser_params *parser, int c)
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
nondigit = 0;
- tokadd(c);
+ tokadd(p, c);
break;
case '.':
@@ -7386,16 +7090,16 @@ parse_numeric(struct parser_params *parser, int c)
goto decode_num;
}
else {
- int c0 = nextc();
+ int c0 = nextc(p);
if (c0 == -1 || !ISDIGIT(c0)) {
- pushback(c0);
+ pushback(p, c0);
goto decode_num;
}
c = c0;
}
- seen_point = toklen();
- tokadd('.');
- tokadd(c);
+ seen_point = toklen(p);
+ tokadd(p, '.');
+ tokadd(p, c);
is_float++;
nondigit = 0;
break;
@@ -7403,7 +7107,7 @@ parse_numeric(struct parser_params *parser, int c)
case 'e':
case 'E':
if (nondigit) {
- pushback(c);
+ pushback(p, c);
c = nondigit;
goto decode_num;
}
@@ -7411,16 +7115,16 @@ parse_numeric(struct parser_params *parser, int c)
goto decode_num;
}
nondigit = c;
- c = nextc();
+ c = nextc(p);
if (c != '-' && c != '+' && !ISDIGIT(c)) {
- pushback(c);
+ pushback(p, c);
nondigit = 0;
goto decode_num;
}
- tokadd(nondigit);
+ tokadd(p, nondigit);
seen_e++;
is_float++;
- tokadd(c);
+ tokadd(p, c);
nondigit = (c == '-' || c == '+') ? c : 0;
break;
@@ -7432,57 +7136,59 @@ parse_numeric(struct parser_params *parser, int c)
default:
goto decode_num;
}
- c = nextc();
+ c = nextc(p);
}
decode_num:
- pushback(c);
+ pushback(p, c);
if (nondigit) {
char tmp[30];
trailing_uc:
+ literal_flush(p, p->lex.pcur - 1);
snprintf(tmp, sizeof(tmp), "trailing `%c' in number", nondigit);
- yyerror(tmp);
+ yyerror0(tmp);
}
- tokfix();
+ tokfix(p);
if (is_float) {
- int type = tFLOAT;
+ enum yytokentype type = tFLOAT;
VALUE v;
- suffix = number_literal_suffix(seen_e ? NUM_SUFFIX_I : NUM_SUFFIX_ALL);
+ suffix = number_literal_suffix(p, seen_e ? NUM_SUFFIX_I : NUM_SUFFIX_ALL);
if (suffix & NUM_SUFFIX_R) {
type = tRATIONAL;
- v = parse_rational(parser, tok(), toklen(), seen_point);
+ v = parse_rational(p, tok(p), toklen(p), seen_point);
}
else {
- double d = strtod(tok(), 0);
+ double d = strtod(tok(p), 0);
if (errno == ERANGE) {
- rb_warning1("Float %s out of range", WARN_S(tok()));
+ rb_warning1("Float %s out of range", WARN_S(tok(p)));
errno = 0;
}
v = DBL2NUM(d);
}
- return set_number_literal(v, type, suffix);
+ return set_number_literal(p, v, type, suffix);
}
- suffix = number_literal_suffix(NUM_SUFFIX_ALL);
- return set_integer_literal(rb_cstr_to_inum(tok(), 10, FALSE), suffix);
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ return set_integer_literal(p, rb_cstr_to_inum(tok(p), 10, FALSE), suffix);
}
-static int
-parse_qmark(struct parser_params *parser)
+static enum yytokentype
+parse_qmark(struct parser_params *p, int space_seen)
{
rb_encoding *enc;
register int c;
+ VALUE lit;
if (IS_END()) {
SET_LEX_STATE(EXPR_VALUE);
return '?';
}
- c = nextc();
+ c = nextc(p);
if (c == -1) {
- compile_error(PARSER_ARG "incomplete character syntax");
+ compile_error(p, "incomplete character syntax");
return 0;
}
- if (rb_enc_isspace(c, current_enc)) {
+ if (rb_enc_isspace(c, p->enc)) {
if (!IS_ARG()) {
int c2 = 0;
switch (c) {
@@ -7510,50 +7216,58 @@ parse_qmark(struct parser_params *parser)
}
}
ternary:
- pushback(c);
+ pushback(p, c);
SET_LEX_STATE(EXPR_VALUE);
return '?';
}
- newtok();
- enc = current_enc;
- if (!parser_isascii()) {
- if (tokadd_mbchar(c) == -1) return 0;
+ newtok(p);
+ enc = p->enc;
+ if (!parser_isascii(p)) {
+ if (tokadd_mbchar(p, c) == -1) return 0;
}
- else if ((rb_enc_isalnum(c, current_enc) || c == '_') &&
- lex_p < lex_pend && is_identchar(lex_p, lex_pend, current_enc)) {
+ else if ((rb_enc_isalnum(c, p->enc) || c == '_') &&
+ p->lex.pcur < p->lex.pend && is_identchar(p->lex.pcur, p->lex.pend, p->enc)) {
+ if (space_seen) {
+ const char *start = p->lex.pcur - 1, *ptr = start;
+ do {
+ int n = parser_precise_mbclen(p, ptr);
+ if (n < 0) return -1;
+ ptr += n;
+ } while (ptr < p->lex.pend && is_identchar(ptr, p->lex.pend, p->enc));
+ rb_warn2("`?' just followed by `%.*s' is interpreted as" \
+ " a conditional operator, put a space after `?'",
+ WARN_I((int)(ptr - start)), WARN_S_L(start, (ptr - start)));
+ }
goto ternary;
}
else if (c == '\\') {
- if (peek('u')) {
- nextc();
- c = parser_tokadd_utf8(parser, &enc, 0, 0, 0);
- if (0x80 <= c) {
- tokaddmbc(c, enc);
- }
- else {
- tokadd(c);
- }
+ if (peek(p, 'u')) {
+ nextc(p);
+ enc = rb_utf8_encoding();
+ if (!parser_tokadd_utf8(p, &enc, -1, 0, 0))
+ return 0;
}
- else if (!lex_eol_p() && !(c = *lex_p, ISASCII(c))) {
- nextc();
- if (tokadd_mbchar(c) == -1) return 0;
+ else if (!lex_eol_p(p) && !(c = *p->lex.pcur, ISASCII(c))) {
+ nextc(p);
+ if (tokadd_mbchar(p, c) == -1) return 0;
}
else {
- c = read_escape(0, &enc);
- tokadd(c);
+ c = read_escape(p, 0, &enc);
+ tokadd(p, c);
}
}
else {
- tokadd(c);
+ tokadd(p, c);
}
- tokfix();
- set_yylval_str(STR_NEW3(tok(), toklen(), enc, 0));
+ tokfix(p);
+ add_mark_object(p, lit = STR_NEW3(tok(p), toklen(p), enc, 0));
+ set_yylval_str(lit);
SET_LEX_STATE(EXPR_END);
return tCHAR;
}
-static int
-parse_percent(struct parser_params *parser, const int space_seen, const enum lex_state_e last_state)
+static enum yytokentype
+parse_percent(struct parser_params *p, const int space_seen, const enum lex_state_e last_state)
{
register int c;
@@ -7561,21 +7275,21 @@ parse_percent(struct parser_params *parser, const int space_seen, const enum lex
int term;
int paren;
- c = nextc();
+ c = nextc(p);
quotation:
if (c == -1 || !ISALNUM(c)) {
term = c;
c = 'Q';
}
else {
- term = nextc();
- if (rb_enc_isalnum(term, current_enc) || !parser_isascii()) {
- yyerror("unknown type of %string");
+ term = nextc(p);
+ if (rb_enc_isalnum(term, p->enc) || !parser_isascii(p)) {
+ yyerror0("unknown type of %string");
return 0;
}
}
if (c == -1 || term == -1) {
- compile_error(PARSER_ARG "unterminated quoted string meets end of file");
+ compile_error(p, "unterminated quoted string meets end of file");
return 0;
}
paren = term;
@@ -7587,56 +7301,48 @@ parse_percent(struct parser_params *parser, const int space_seen, const enum lex
switch (c) {
case 'Q':
- lex_strterm = NEW_STRTERM(str_dquote, term, paren);
+ p->lex.strterm = NEW_STRTERM(str_dquote, term, paren);
return tSTRING_BEG;
case 'q':
- lex_strterm = NEW_STRTERM(str_squote, term, paren);
+ p->lex.strterm = NEW_STRTERM(str_squote, term, paren);
return tSTRING_BEG;
case 'W':
- lex_strterm = NEW_STRTERM(str_dword, term, paren);
- do {c = nextc();} while (ISSPACE(c));
- pushback(c);
+ p->lex.strterm = NEW_STRTERM(str_dword, term, paren);
return tWORDS_BEG;
case 'w':
- lex_strterm = NEW_STRTERM(str_sword, term, paren);
- do {c = nextc();} while (ISSPACE(c));
- pushback(c);
+ p->lex.strterm = NEW_STRTERM(str_sword, term, paren);
return tQWORDS_BEG;
case 'I':
- lex_strterm = NEW_STRTERM(str_dword, term, paren);
- do {c = nextc();} while (ISSPACE(c));
- pushback(c);
+ p->lex.strterm = NEW_STRTERM(str_dword, term, paren);
return tSYMBOLS_BEG;
case 'i':
- lex_strterm = NEW_STRTERM(str_sword, term, paren);
- do {c = nextc();} while (ISSPACE(c));
- pushback(c);
+ p->lex.strterm = NEW_STRTERM(str_sword, term, paren);
return tQSYMBOLS_BEG;
case 'x':
- lex_strterm = NEW_STRTERM(str_xquote, term, paren);
+ p->lex.strterm = NEW_STRTERM(str_xquote, term, paren);
return tXSTRING_BEG;
case 'r':
- lex_strterm = NEW_STRTERM(str_regexp, term, paren);
+ p->lex.strterm = NEW_STRTERM(str_regexp, term, paren);
return tREGEXP_BEG;
case 's':
- lex_strterm = NEW_STRTERM(str_ssym, term, paren);
+ p->lex.strterm = NEW_STRTERM(str_ssym, term, paren);
SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);
return tSYMBEG;
default:
- yyerror("unknown type of %string");
+ yyerror0("unknown type of %string");
return 0;
}
}
- if ((c = nextc()) == '=') {
+ if ((c = nextc(p)) == '=') {
set_yylval_id('%');
SET_LEX_STATE(EXPR_BEG);
return tOP_ASGN;
@@ -7645,24 +7351,23 @@ parse_percent(struct parser_params *parser, const int space_seen, const enum lex
goto quotation;
}
SET_LEX_STATE(IS_AFTER_OPERATOR() ? EXPR_ARG : EXPR_BEG);
- pushback(c);
- warn_balanced("%%", "string literal");
- return '%';
+ pushback(p, c);
+ return warn_balanced('%', "%%", "string literal");
}
static int
-tokadd_ident(struct parser_params *parser, int c)
+tokadd_ident(struct parser_params *p, int c)
{
do {
- if (tokadd_mbchar(c) == -1) return -1;
- c = nextc();
- } while (parser_is_identchar());
- pushback(c);
+ if (tokadd_mbchar(p, c) == -1) return -1;
+ c = nextc(p);
+ } while (parser_is_identchar(p));
+ pushback(p, c);
return 0;
}
static ID
-tokenize_ident(struct parser_params *parser, const enum lex_state_e last_state)
+tokenize_ident(struct parser_params *p, const enum lex_state_e last_state)
{
ID ident = TOK_INTERN();
@@ -7672,11 +7377,11 @@ tokenize_ident(struct parser_params *parser, const enum lex_state_e last_state)
}
static int
-parse_numvar(struct parser_params *parser)
+parse_numvar(struct parser_params *p)
{
size_t len;
int overflow;
- unsigned long n = ruby_scan_digits(tok()+1, toklen()-1, 10, &len, &overflow);
+ unsigned long n = ruby_scan_digits(tok(p)+1, toklen(p)-1, 10, &len, &overflow);
const unsigned long nth_ref_max =
((FIXNUM_MAX < INT_MAX) ? FIXNUM_MAX : INT_MAX) >> 1;
/* NTH_REF is left-shifted to be ORed with back-ref flag and
@@ -7684,7 +7389,7 @@ parse_numvar(struct parser_params *parser)
if (overflow || n > nth_ref_max) {
/* compile_error()? */
- rb_warn1("`%s' is too big for a number variable, always nil", WARN_S(tok()));
+ rb_warn1("`%s' is too big for a number variable, always nil", WARN_S(tok(p)));
return 0; /* $0 is $PROGRAM_NAME, not NTH_REF */
}
else {
@@ -7692,23 +7397,23 @@ parse_numvar(struct parser_params *parser)
}
}
-static int
-parse_gvar(struct parser_params *parser, const enum lex_state_e last_state)
+static enum yytokentype
+parse_gvar(struct parser_params *p, const enum lex_state_e last_state)
{
register int c;
SET_LEX_STATE(EXPR_END);
- newtok();
- c = nextc();
+ newtok(p);
+ c = nextc(p);
switch (c) {
case '_': /* $_: last read line string */
- c = nextc();
- if (parser_is_identchar()) {
- tokadd('$');
- tokadd('_');
+ c = nextc(p);
+ if (parser_is_identchar(p)) {
+ tokadd(p, '$');
+ tokadd(p, '_');
break;
}
- pushback(c);
+ pushback(p, c);
c = '_';
/* fall through */
case '~': /* $~: match-data */
@@ -7727,20 +7432,20 @@ parse_gvar(struct parser_params *parser, const enum lex_state_e last_state)
case '<': /* $<: reading filename */
case '>': /* $>: default output handle */
case '\"': /* $": already loaded files */
- tokadd('$');
- tokadd(c);
+ tokadd(p, '$');
+ tokadd(p, c);
goto gvar;
case '-':
- tokadd('$');
- tokadd(c);
- c = nextc();
- if (parser_is_identchar()) {
- if (tokadd_mbchar(c) == -1) return 0;
+ tokadd(p, '$');
+ tokadd(p, c);
+ c = nextc(p);
+ if (parser_is_identchar(p)) {
+ if (tokadd_mbchar(p, c) == -1) return 0;
}
else {
- pushback(c);
- pushback('-');
+ pushback(p, c);
+ pushback(p, '-');
return '$';
}
gvar:
@@ -7752,136 +7457,119 @@ parse_gvar(struct parser_params *parser, const enum lex_state_e last_state)
case '\'': /* $': string after last match */
case '+': /* $+: string matches last paren. */
if (IS_lex_state_for(last_state, EXPR_FNAME)) {
- tokadd('$');
- tokadd(c);
+ tokadd(p, '$');
+ tokadd(p, c);
goto gvar;
}
- set_yylval_node(NEW_BACK_REF(c));
+ set_yylval_node(NEW_BACK_REF(c, &_cur_loc));
return tBACK_REF;
case '1': case '2': case '3':
case '4': case '5': case '6':
case '7': case '8': case '9':
- tokadd('$');
+ tokadd(p, '$');
do {
- tokadd(c);
- c = nextc();
+ tokadd(p, c);
+ c = nextc(p);
} while (c != -1 && ISDIGIT(c));
- pushback(c);
+ pushback(p, c);
if (IS_lex_state_for(last_state, EXPR_FNAME)) goto gvar;
- tokfix();
- set_yylval_node(NEW_NTH_REF(parse_numvar(parser)));
+ tokfix(p);
+ set_yylval_node(NEW_NTH_REF(parse_numvar(p), &_cur_loc));
return tNTH_REF;
default:
- if (!parser_is_identchar()) {
+ if (!parser_is_identchar(p)) {
if (c == -1 || ISSPACE(c)) {
- compile_error(PARSER_ARG "`$' without identifiers is not allowed as a global variable name");
+ compile_error(p, "`$' without identifiers is not allowed as a global variable name");
}
else {
- pushback(c);
- compile_error(PARSER_ARG "`$%c' is not allowed as a global variable name", c);
+ pushback(p, c);
+ compile_error(p, "`$%c' is not allowed as a global variable name", c);
}
return 0;
}
case '0':
- tokadd('$');
+ tokadd(p, '$');
}
- if (tokadd_ident(parser, c)) return 0;
+ if (tokadd_ident(p, c)) return 0;
SET_LEX_STATE(EXPR_END);
- tokenize_ident(parser, last_state);
+ tokenize_ident(p, last_state);
return tGVAR;
}
-static int
-parse_atmark(struct parser_params *parser, const enum lex_state_e last_state)
+static enum yytokentype
+parse_atmark(struct parser_params *p, const enum lex_state_e last_state)
{
- int result = tIVAR;
- register int c = nextc();
+ enum yytokentype result = tIVAR;
+ register int c = nextc(p);
- newtok();
- tokadd('@');
+ newtok(p);
+ tokadd(p, '@');
if (c == '@') {
result = tCVAR;
- tokadd('@');
- c = nextc();
+ tokadd(p, '@');
+ c = nextc(p);
}
if (c == -1 || ISSPACE(c)) {
if (result == tIVAR) {
- compile_error(PARSER_ARG "`@' without identifiers is not allowed as an instance variable name");
+ compile_error(p, "`@' without identifiers is not allowed as an instance variable name");
}
else {
- compile_error(PARSER_ARG "`@@' without identifiers is not allowed as a class variable name");
+ compile_error(p, "`@@' without identifiers is not allowed as a class variable name");
}
return 0;
}
- else if (ISDIGIT(c) || !parser_is_identchar()) {
- pushback(c);
+ else if (ISDIGIT(c) || !parser_is_identchar(p)) {
+ pushback(p, c);
if (result == tIVAR) {
- compile_error(PARSER_ARG "`@%c' is not allowed as an instance variable name", c);
+ compile_error(p, "`@%c' is not allowed as an instance variable name", c);
}
else {
- compile_error(PARSER_ARG "`@@%c' is not allowed as a class variable name", c);
+ compile_error(p, "`@@%c' is not allowed as a class variable name", c);
}
return 0;
}
- if (tokadd_ident(parser, c)) return 0;
+ if (tokadd_ident(p, c)) return 0;
SET_LEX_STATE(EXPR_END);
- tokenize_ident(parser, last_state);
+ tokenize_ident(p, last_state);
return result;
}
-static int
-parse_ident(struct parser_params *parser, int c, int cmd_state)
+static enum yytokentype
+parse_ident(struct parser_params *p, int c, int cmd_state)
{
- int result = 0;
+ enum yytokentype result;
int mb = ENC_CODERANGE_7BIT;
- const enum lex_state_e last_state = lex_state;
+ const enum lex_state_e last_state = p->lex.state;
ID ident;
do {
if (!ISASCII(c)) mb = ENC_CODERANGE_UNKNOWN;
- if (tokadd_mbchar(c) == -1) return 0;
- c = nextc();
- } while (parser_is_identchar());
- if ((c == '!' || c == '?') && !peek('=')) {
- tokadd(c);
- }
- else {
- pushback(c);
- }
- tokfix();
-
- if (toklast() == '!' || toklast() == '?') {
+ if (tokadd_mbchar(p, c) == -1) return 0;
+ c = nextc(p);
+ } while (parser_is_identchar(p));
+ if ((c == '!' || c == '?') && !peek(p, '=')) {
result = tFID;
+ tokadd(p, c);
+ }
+ else if (c == '=' && IS_lex_state(EXPR_FNAME) &&
+ (!peek(p, '~') && !peek(p, '>') && (!peek(p, '=') || (peek_n(p, '>', 1))))) {
+ result = tIDENTIFIER;
+ tokadd(p, c);
}
else {
- if (IS_lex_state(EXPR_FNAME)) {
- register int c = nextc();
- if (c == '=' && !peek('~') && !peek('>') &&
- (!peek('=') || (peek_n('>', 1)))) {
- result = tIDENTIFIER;
- tokadd(c);
- tokfix();
- }
- else {
- pushback(c);
- }
- }
- if (result == 0 && ISUPPER(tok()[0])) {
- result = tCONSTANT;
- }
- else {
- result = tIDENTIFIER;
- }
+ result = tCONSTANT; /* assume provisionally */
+ pushback(p, c);
}
+ tokfix(p);
if (IS_LABEL_POSSIBLE()) {
if (IS_LABEL_SUFFIX(0)) {
SET_LEX_STATE(EXPR_ARG|EXPR_LABELED);
- nextc();
+ nextc(p);
set_yylval_name(TOK_INTERN());
return tLABEL;
}
@@ -7890,28 +7578,25 @@ parse_ident(struct parser_params *parser, int c, int cmd_state)
const struct kwtable *kw;
/* See if it is a reserved word. */
- kw = rb_reserved_word(tok(), toklen());
+ kw = rb_reserved_word(tok(p), toklen(p));
if (kw) {
- enum lex_state_e state = lex_state;
+ enum lex_state_e state = p->lex.state;
SET_LEX_STATE(kw->state);
if (IS_lex_state_for(state, EXPR_FNAME)) {
- set_yylval_name(rb_intern2(tok(), toklen()));
+ set_yylval_name(rb_intern2(tok(p), toklen(p)));
return kw->id[0];
}
if (IS_lex_state(EXPR_BEG)) {
- command_start = TRUE;
+ p->command_start = TRUE;
}
if (kw->id[0] == keyword_do) {
- if (lpar_beg && lpar_beg == paren_nest) {
- lpar_beg = 0;
- --paren_nest;
+ if (lambda_beginning_p()) {
+ p->lex.lpar_beg = -1; /* make lambda_beginning_p() == FALSE in the body of "-> do ... end" */
return keyword_do_LAMBDA;
}
if (COND_P()) return keyword_do_cond;
if (CMDARG_P() && !IS_lex_state_for(state, EXPR_CMDARG))
return keyword_do_block;
- if (IS_lex_state_for(state, (EXPR_BEG | EXPR_ENDARG)))
- return keyword_do_block;
return keyword_do;
}
if (IS_lex_state_for(state, (EXPR_BEG | EXPR_LABELED)))
@@ -7932,24 +7617,25 @@ parse_ident(struct parser_params *parser, int c, int cmd_state)
SET_LEX_STATE(EXPR_ARG);
}
}
- else if (lex_state == EXPR_FNAME) {
+ else if (p->lex.state == EXPR_FNAME) {
SET_LEX_STATE(EXPR_ENDFN);
}
else {
SET_LEX_STATE(EXPR_END);
}
- ident = tokenize_ident(parser, last_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(ident)) {
+ lvar_defined(p, ident)) {
SET_LEX_STATE(EXPR_END|EXPR_LABEL);
}
return result;
}
-static int
-parser_yylex(struct parser_params *parser)
+static enum yytokentype
+parser_yylex(struct parser_params *p)
{
register int c;
int space_seen = 0;
@@ -7957,40 +7643,26 @@ parser_yylex(struct parser_params *parser)
int label;
enum lex_state_e last_state;
int fallthru = FALSE;
- int token_seen = parser->token_seen;
-
- if (lex_strterm) {
- int token;
- if (nd_type(lex_strterm) == NODE_HEREDOC) {
- token = here_document(lex_strterm);
- if (token == tSTRING_END) {
- lex_strterm = 0;
- SET_LEX_STATE(EXPR_END);
- }
+ int token_seen = p->token_seen;
+
+ if (p->lex.strterm) {
+ if (p->lex.strterm->flags & STRTERM_HEREDOC) {
+ return here_document(p, &p->lex.strterm->u.heredoc);
}
else {
- token = parse_string(lex_strterm);
- if ((token == tSTRING_END) && (lex_strterm->nd_func & STR_FUNC_LABEL)) {
- if (((IS_lex_state(EXPR_BEG | EXPR_ENDFN) && !COND_P()) || IS_ARG()) &&
- IS_LABEL_SUFFIX(0)) {
- nextc();
- token = tLABEL_END;
- }
- }
- if (token == tSTRING_END || token == tREGEXP_END || token == tLABEL_END) {
- rb_gc_force_recycle((VALUE)lex_strterm);
- lex_strterm = 0;
- SET_LEX_STATE(token == tLABEL_END ? EXPR_BEG|EXPR_LABEL : EXPR_END);
- }
+ token_flush(p);
+ return parse_string(p, &p->lex.strterm->u.literal);
}
- return token;
}
- cmd_state = command_start;
- command_start = FALSE;
- parser->token_seen = TRUE;
+ cmd_state = p->command_start;
+ p->command_start = FALSE;
+ p->token_seen = TRUE;
retry:
- last_state = lex_state;
- switch (c = nextc()) {
+ last_state = p->lex.state;
+#ifndef RIPPER
+ token_flush(p);
+#endif
+ switch (c = nextc(p)) {
case '\0': /* NUL */
case '\004': /* ^D */
case '\032': /* ^Z */
@@ -8002,7 +7674,7 @@ parser_yylex(struct parser_params *parser)
case '\13': /* '\v' */
space_seen = 1;
#ifdef RIPPER
- while ((c = nextc())) {
+ while ((c = nextc(p))) {
switch (c) {
case ' ': case '\t': case '\f': case '\r':
case '\13': /* '\v' */
@@ -8012,78 +7684,84 @@ parser_yylex(struct parser_params *parser)
}
}
outofloop:
- pushback(c);
- dispatch_scan_event(tSP);
+ pushback(p, c);
+ dispatch_scan_event(p, tSP);
#endif
goto retry;
case '#': /* it's a comment */
- parser->token_seen = token_seen;
+ p->token_seen = token_seen;
/* no magic_comment in shebang line */
- if (!parser_magic_comment(parser, lex_p, lex_pend - lex_p)) {
- if (comment_at_top(parser)) {
- set_file_encoding(parser, lex_p, lex_pend);
+ if (!parser_magic_comment(p, p->lex.pcur, p->lex.pend - p->lex.pcur)) {
+ if (comment_at_top(p)) {
+ set_file_encoding(p, p->lex.pcur, p->lex.pend);
}
}
- lex_p = lex_pend;
- dispatch_scan_event(tCOMMENT);
+ lex_goto_eol(p);
+ dispatch_scan_event(p, tCOMMENT);
fallthru = TRUE;
/* fall through */
case '\n':
- parser->token_seen = token_seen;
+ p->token_seen = token_seen;
c = (IS_lex_state(EXPR_BEG|EXPR_CLASS|EXPR_FNAME|EXPR_DOT) &&
!IS_lex_state(EXPR_LABELED));
if (c || IS_lex_state_all(EXPR_ARG|EXPR_LABELED)) {
if (!fallthru) {
- dispatch_scan_event(tIGNORED_NL);
+ dispatch_scan_event(p, tIGNORED_NL);
}
fallthru = FALSE;
- if (!c && parser->in_kwarg) {
+ if (!c && p->in_kwarg) {
goto normal_newline;
}
goto retry;
}
- while ((c = nextc())) {
- switch (c) {
+ while (1) {
+ switch (c = nextc(p)) {
case ' ': case '\t': case '\f': case '\r':
case '\13': /* '\v' */
space_seen = 1;
break;
case '&':
case '.': {
- dispatch_delayed_token(tIGNORED_NL);
- if (peek('.') == (c == '&')) {
- pushback(c);
- dispatch_scan_event(tSP);
+ dispatch_delayed_token(p, tIGNORED_NL);
+ if (peek(p, '.') == (c == '&')) {
+ pushback(p, c);
+ dispatch_scan_event(p, tSP);
goto retry;
}
}
default:
- --ruby_sourceline;
- lex_nextline = lex_lastline;
+ p->ruby_sourceline--;
+ p->lex.nextline = p->lex.lastline;
case -1: /* EOF no decrement*/
- lex_goto_eol(parser);
-#ifdef RIPPER
+#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) {
- parser->tokp = lex_p;
+ p->lex.ptok = p->lex.pcur;
}
#endif
goto normal_newline;
}
}
normal_newline:
- command_start = TRUE;
+ p->command_start = TRUE;
SET_LEX_STATE(EXPR_BEG);
return '\n';
case '*':
- if ((c = nextc()) == '*') {
- if ((c = nextc()) == '=') {
- set_yylval_id(tPOW);
+ if ((c = nextc(p)) == '*') {
+ if ((c = nextc(p)) == '=') {
+ set_yylval_id(idPow);
SET_LEX_STATE(EXPR_BEG);
return tOP_ASGN;
}
- pushback(c);
+ pushback(p, c);
if (IS_SPCARG(c)) {
rb_warning0("`**' interpreted as argument prefix");
c = tDSTAR;
@@ -8092,8 +7770,7 @@ parser_yylex(struct parser_params *parser)
c = tDSTAR;
}
else {
- warn_balanced("**", "argument prefix");
- c = tPOW;
+ c = warn_balanced((enum ruby_method_ids)tPOW, "**", "argument prefix");
}
}
else {
@@ -8102,7 +7779,7 @@ parser_yylex(struct parser_params *parser)
SET_LEX_STATE(EXPR_BEG);
return tOP_ASGN;
}
- pushback(c);
+ pushback(p, c);
if (IS_SPCARG(c)) {
rb_warning0("`*' interpreted as argument prefix");
c = tSTAR;
@@ -8111,15 +7788,14 @@ parser_yylex(struct parser_params *parser)
c = tSTAR;
}
else {
- warn_balanced("*", "argument prefix");
- c = '*';
+ c = warn_balanced('*', "*", "argument prefix");
}
}
SET_LEX_STATE(IS_AFTER_OPERATOR() ? EXPR_ARG : EXPR_BEG);
return c;
case '!':
- c = nextc();
+ c = nextc(p);
if (IS_AFTER_OPERATOR()) {
SET_LEX_STATE(EXPR_ARG);
if (c == '@') {
@@ -8135,46 +7811,46 @@ parser_yylex(struct parser_params *parser)
if (c == '~') {
return tNMATCH;
}
- pushback(c);
+ pushback(p, c);
return '!';
case '=':
- if (was_bol()) {
+ if (was_bol(p)) {
/* skip embedded rd document */
- if (strncmp(lex_p, "begin", 5) == 0 && ISSPACE(lex_p[5])) {
+ if (strncmp(p->lex.pcur, "begin", 5) == 0 && ISSPACE(p->lex.pcur[5])) {
int first_p = TRUE;
- lex_goto_eol(parser);
- dispatch_scan_event(tEMBDOC_BEG);
+ lex_goto_eol(p);
+ dispatch_scan_event(p, tEMBDOC_BEG);
for (;;) {
- lex_goto_eol(parser);
+ lex_goto_eol(p);
if (!first_p) {
- dispatch_scan_event(tEMBDOC);
+ dispatch_scan_event(p, tEMBDOC);
}
first_p = FALSE;
- c = nextc();
+ c = nextc(p);
if (c == -1) {
- compile_error(PARSER_ARG "embedded document meets end of file");
+ compile_error(p, "embedded document meets end of file");
return 0;
}
if (c != '=') continue;
- if (c == '=' && strncmp(lex_p, "end", 3) == 0 &&
- (lex_p + 3 == lex_pend || ISSPACE(lex_p[3]))) {
+ if (c == '=' && strncmp(p->lex.pcur, "end", 3) == 0 &&
+ (p->lex.pcur + 3 == p->lex.pend || ISSPACE(p->lex.pcur[3]))) {
break;
}
}
- lex_goto_eol(parser);
- dispatch_scan_event(tEMBDOC_END);
+ lex_goto_eol(p);
+ dispatch_scan_event(p, tEMBDOC_END);
goto retry;
}
}
SET_LEX_STATE(IS_AFTER_OPERATOR() ? EXPR_ARG : EXPR_BEG);
- if ((c = nextc()) == '=') {
- if ((c = nextc()) == '=') {
+ if ((c = nextc(p)) == '=') {
+ if ((c = nextc(p)) == '=') {
return tEQQ;
}
- pushback(c);
+ pushback(p, c);
return tEQ;
}
if (c == '~') {
@@ -8183,17 +7859,17 @@ parser_yylex(struct parser_params *parser)
else if (c == '>') {
return tASSOC;
}
- pushback(c);
+ pushback(p, c);
return '=';
case '<':
- last_state = lex_state;
- c = nextc();
+ last_state = p->lex.state;
+ c = nextc(p);
if (c == '<' &&
!IS_lex_state(EXPR_DOT | EXPR_CLASS) &&
!IS_END() &&
(!IS_ARG() || IS_lex_state(EXPR_LABELED) || space_seen)) {
- int token = heredoc_identifier();
+ int token = heredoc_identifier(p);
if (token) return token;
}
if (IS_AFTER_OPERATOR()) {
@@ -8201,49 +7877,48 @@ parser_yylex(struct parser_params *parser)
}
else {
if (IS_lex_state(EXPR_CLASS))
- command_start = TRUE;
+ p->command_start = TRUE;
SET_LEX_STATE(EXPR_BEG);
}
if (c == '=') {
- if ((c = nextc()) == '>') {
+ if ((c = nextc(p)) == '>') {
return tCMP;
}
- pushback(c);
+ pushback(p, c);
return tLEQ;
}
if (c == '<') {
- if ((c = nextc()) == '=') {
- set_yylval_id(tLSHFT);
+ if ((c = nextc(p)) == '=') {
+ set_yylval_id(idLTLT);
SET_LEX_STATE(EXPR_BEG);
return tOP_ASGN;
}
- pushback(c);
- warn_balanced("<<", "here document");
- return tLSHFT;
+ pushback(p, c);
+ return warn_balanced((enum ruby_method_ids)tLSHFT, "<<", "here document");
}
- pushback(c);
+ pushback(p, c);
return '<';
case '>':
SET_LEX_STATE(IS_AFTER_OPERATOR() ? EXPR_ARG : EXPR_BEG);
- if ((c = nextc()) == '=') {
+ if ((c = nextc(p)) == '=') {
return tGEQ;
}
if (c == '>') {
- if ((c = nextc()) == '=') {
- set_yylval_id(tRSHFT);
+ if ((c = nextc(p)) == '=') {
+ set_yylval_id(idGTGT);
SET_LEX_STATE(EXPR_BEG);
return tOP_ASGN;
}
- pushback(c);
+ pushback(p, c);
return tRSHFT;
}
- pushback(c);
+ pushback(p, c);
return '>';
case '"':
label = (IS_LABEL_POSSIBLE() ? str_label : 0);
- lex_strterm = NEW_STRTERM(str_dquote | label, '"', 0);
+ p->lex.strterm = NEW_STRTERM(str_dquote | label, '"', 0);
return tSTRING_BEG;
case '`':
@@ -8258,26 +7933,26 @@ parser_yylex(struct parser_params *parser)
SET_LEX_STATE(EXPR_ARG);
return c;
}
- lex_strterm = NEW_STRTERM(str_xquote, '`', 0);
+ p->lex.strterm = NEW_STRTERM(str_xquote, '`', 0);
return tXSTRING_BEG;
case '\'':
label = (IS_LABEL_POSSIBLE() ? str_label : 0);
- lex_strterm = NEW_STRTERM(str_squote | label, '\'', 0);
+ p->lex.strterm = NEW_STRTERM(str_squote | label, '\'', 0);
return tSTRING_BEG;
case '?':
- return parse_qmark(parser);
+ return parse_qmark(p, space_seen);
case '&':
- if ((c = nextc()) == '&') {
+ if ((c = nextc(p)) == '&') {
SET_LEX_STATE(EXPR_BEG);
- if ((c = nextc()) == '=') {
- set_yylval_id(tANDOP);
+ if ((c = nextc(p)) == '=') {
+ set_yylval_id(idANDOP);
SET_LEX_STATE(EXPR_BEG);
return tOP_ASGN;
}
- pushback(c);
+ pushback(p, c);
return tANDOP;
}
else if (c == '=') {
@@ -8286,33 +7961,38 @@ parser_yylex(struct parser_params *parser)
return tOP_ASGN;
}
else if (c == '.') {
+ set_yylval_id(idANDDOT);
SET_LEX_STATE(EXPR_DOT);
return tANDDOT;
}
- pushback(c);
+ pushback(p, c);
if (IS_SPCARG(c)) {
- rb_warning0("`&' interpreted as argument prefix");
+ if ((c != ':') ||
+ (c = peekc_n(p, 1)) == -1 ||
+ !(c == '\'' || c == '"' ||
+ is_identchar((p->lex.pcur+1), p->lex.pend, p->enc))) {
+ rb_warning0("`&' interpreted as argument prefix");
+ }
c = tAMPER;
}
else if (IS_BEG()) {
c = tAMPER;
}
else {
- warn_balanced("&", "argument prefix");
- c = '&';
+ c = warn_balanced('&', "&", "argument prefix");
}
SET_LEX_STATE(IS_AFTER_OPERATOR() ? EXPR_ARG : EXPR_BEG);
return c;
case '|':
- if ((c = nextc()) == '|') {
+ if ((c = nextc(p)) == '|') {
SET_LEX_STATE(EXPR_BEG);
- if ((c = nextc()) == '=') {
- set_yylval_id(tOROP);
+ if ((c = nextc(p)) == '=') {
+ set_yylval_id(idOROP);
SET_LEX_STATE(EXPR_BEG);
return tOP_ASGN;
}
- pushback(c);
+ pushback(p, c);
return tOROP;
}
if (c == '=') {
@@ -8321,17 +8001,17 @@ parser_yylex(struct parser_params *parser)
return tOP_ASGN;
}
SET_LEX_STATE(IS_AFTER_OPERATOR() ? EXPR_ARG : EXPR_BEG|EXPR_LABEL);
- pushback(c);
+ pushback(p, c);
return '|';
case '+':
- c = nextc();
+ c = nextc(p);
if (IS_AFTER_OPERATOR()) {
SET_LEX_STATE(EXPR_ARG);
if (c == '@') {
return tUPLUS;
}
- pushback(c);
+ pushback(p, c);
return '+';
}
if (c == '=') {
@@ -8339,27 +8019,26 @@ parser_yylex(struct parser_params *parser)
SET_LEX_STATE(EXPR_BEG);
return tOP_ASGN;
}
- if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous('+'))) {
+ if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p, '+'))) {
SET_LEX_STATE(EXPR_BEG);
- pushback(c);
+ pushback(p, c);
if (c != -1 && ISDIGIT(c)) {
- return parse_numeric(parser, '+');
+ return parse_numeric(p, '+');
}
return tUPLUS;
}
SET_LEX_STATE(EXPR_BEG);
- pushback(c);
- warn_balanced("+", "unary operator");
- return '+';
+ pushback(p, c);
+ return warn_balanced('+', "+", "unary operator");
case '-':
- c = nextc();
+ c = nextc(p);
if (IS_AFTER_OPERATOR()) {
SET_LEX_STATE(EXPR_ARG);
if (c == '@') {
return tUMINUS;
}
- pushback(c);
+ pushback(p, c);
return '-';
}
if (c == '=') {
@@ -8369,82 +8048,90 @@ parser_yylex(struct parser_params *parser)
}
if (c == '>') {
SET_LEX_STATE(EXPR_ENDFN);
- token_info_push("->");
return tLAMBDA;
}
- if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous('-'))) {
+ if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p, '-'))) {
SET_LEX_STATE(EXPR_BEG);
- pushback(c);
+ pushback(p, c);
if (c != -1 && ISDIGIT(c)) {
return tUMINUS_NUM;
}
return tUMINUS;
}
SET_LEX_STATE(EXPR_BEG);
- pushback(c);
- warn_balanced("-", "unary operator");
- return '-';
+ pushback(p, c);
+ return warn_balanced('-', "-", "unary operator");
case '.':
SET_LEX_STATE(EXPR_BEG);
- if ((c = nextc()) == '.') {
- if ((c = nextc()) == '.') {
+ if ((c = nextc(p)) == '.') {
+ if ((c = nextc(p)) == '.') {
return tDOT3;
}
- pushback(c);
+ pushback(p, c);
return tDOT2;
}
- pushback(c);
+ pushback(p, c);
if (c != -1 && ISDIGIT(c)) {
- yyerror("no .<digit> floating literal anymore; put 0 before dot");
+ yyerror0("no .<digit> floating literal anymore; put 0 before dot");
}
+ set_yylval_id('.');
SET_LEX_STATE(EXPR_DOT);
return '.';
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
- return parse_numeric(parser, c);
+ return parse_numeric(p, c);
case ')':
+ COND_POP();
+ CMDARG_POP();
+ SET_LEX_STATE(EXPR_ENDFN);
+ p->lex.paren_nest--;
+ return c;
+
case ']':
- paren_nest--;
+ COND_POP();
+ CMDARG_POP();
+ SET_LEX_STATE(EXPR_END);
+ p->lex.paren_nest--;
+ return c;
+
case '}':
- COND_LEXPOP();
- CMDARG_LEXPOP();
- if (c == ')')
- SET_LEX_STATE(EXPR_ENDFN);
- else
- SET_LEX_STATE(EXPR_ENDARG);
- if (c == '}') {
- if (!brace_nest--) c = tSTRING_DEND;
- }
+ /* tSTRING_DEND does COND_POP and CMDARG_POP in the yacc's rule */
+ if (!p->lex.brace_nest--) return tSTRING_DEND;
+ COND_POP();
+ CMDARG_POP();
+ SET_LEX_STATE(EXPR_END);
+ p->lex.paren_nest--;
return c;
case ':':
- c = nextc();
+ c = nextc(p);
if (c == ':') {
if (IS_BEG() || IS_lex_state(EXPR_CLASS) || IS_SPCARG(-1)) {
SET_LEX_STATE(EXPR_BEG);
return tCOLON3;
}
+ set_yylval_id(idCOLON2);
SET_LEX_STATE(EXPR_DOT);
return tCOLON2;
}
if (IS_END() || ISSPACE(c) || c == '#') {
- pushback(c);
- warn_balanced(":", "symbol literal");
+ pushback(p, c);
+ c = warn_balanced(':', ":", "symbol literal");
SET_LEX_STATE(EXPR_BEG);
- return ':';
+ return c;
}
switch (c) {
case '\'':
- lex_strterm = NEW_STRTERM(str_ssym, c, 0);
+ p->lex.strterm = NEW_STRTERM(str_ssym, c, 0);
break;
case '"':
- lex_strterm = NEW_STRTERM(str_dsym, c, 0);
+ p->lex.strterm = NEW_STRTERM(str_dsym, c, 0);
break;
default:
- pushback(c);
+ pushback(p, c);
break;
}
SET_LEX_STATE(EXPR_FNAME);
@@ -8452,37 +8139,36 @@ parser_yylex(struct parser_params *parser)
case '/':
if (IS_BEG()) {
- lex_strterm = NEW_STRTERM(str_regexp, '/', 0);
+ p->lex.strterm = NEW_STRTERM(str_regexp, '/', 0);
return tREGEXP_BEG;
}
- if ((c = nextc()) == '=') {
+ if ((c = nextc(p)) == '=') {
set_yylval_id('/');
SET_LEX_STATE(EXPR_BEG);
return tOP_ASGN;
}
- pushback(c);
+ pushback(p, c);
if (IS_SPCARG(c)) {
- (void)arg_ambiguous('/');
- lex_strterm = NEW_STRTERM(str_regexp, '/', 0);
+ arg_ambiguous(p, '/');
+ p->lex.strterm = NEW_STRTERM(str_regexp, '/', 0);
return tREGEXP_BEG;
}
SET_LEX_STATE(IS_AFTER_OPERATOR() ? EXPR_ARG : EXPR_BEG);
- warn_balanced("/", "regexp literal");
- return '/';
+ return warn_balanced('/', "/", "regexp literal");
case '^':
- if ((c = nextc()) == '=') {
+ if ((c = nextc(p)) == '=') {
set_yylval_id('^');
SET_LEX_STATE(EXPR_BEG);
return tOP_ASGN;
}
SET_LEX_STATE(IS_AFTER_OPERATOR() ? EXPR_ARG : EXPR_BEG);
- pushback(c);
+ pushback(p, c);
return '^';
case ';':
SET_LEX_STATE(EXPR_BEG);
- command_start = TRUE;
+ p->command_start = TRUE;
return ';';
case ',':
@@ -8491,8 +8177,8 @@ parser_yylex(struct parser_params *parser)
case '~':
if (IS_AFTER_OPERATOR()) {
- if ((c = nextc()) != '@') {
- pushback(c);
+ if ((c = nextc(p)) != '@') {
+ pushback(p, c);
}
SET_LEX_STATE(EXPR_ARG);
}
@@ -8505,28 +8191,35 @@ parser_yylex(struct parser_params *parser)
if (IS_BEG()) {
c = tLPAREN;
}
- else if (IS_SPCARG(-1)) {
+ else if (!space_seen) {
+ /* foo( ... ) => method call, no ambiguity */
+ }
+ else if (IS_ARG() || IS_lex_state_all(EXPR_END|EXPR_LABEL)) {
c = tLPAREN_ARG;
}
- paren_nest++;
+ else if (IS_lex_state(EXPR_ENDFN) && !lambda_beginning_p()) {
+ rb_warning0("parentheses after method name is interpreted as "
+ "an argument list, not a decomposed argument");
+ }
+ p->lex.paren_nest++;
COND_PUSH(0);
CMDARG_PUSH(0);
SET_LEX_STATE(EXPR_BEG|EXPR_LABEL);
return c;
case '[':
- paren_nest++;
+ p->lex.paren_nest++;
if (IS_AFTER_OPERATOR()) {
- SET_LEX_STATE(EXPR_ARG);
- if ((c = nextc()) == ']') {
- if ((c = nextc()) == '=') {
+ if ((c = nextc(p)) == ']') {
+ SET_LEX_STATE(EXPR_ARG);
+ if ((c = nextc(p)) == '=') {
return tASET;
}
- pushback(c);
+ pushback(p, c);
return tAREF;
}
- pushback(c);
- lex_state |= EXPR_LABEL;
+ pushback(p, c);
+ SET_LEX_STATE(EXPR_ARG|EXPR_LABEL);
return '[';
}
else if (IS_BEG()) {
@@ -8541,15 +8234,15 @@ parser_yylex(struct parser_params *parser)
return c;
case '{':
- ++brace_nest;
- if (lpar_beg && lpar_beg == paren_nest) {
+ ++p->lex.brace_nest;
+ if (lambda_beginning_p()) {
SET_LEX_STATE(EXPR_BEG);
- lpar_beg = 0;
- --paren_nest;
COND_PUSH(0);
CMDARG_PUSH(0);
+ p->lex.paren_nest++;
return tLAMBEG;
}
+ p->lex.paren_nest++;
if (IS_lex_state(EXPR_LABELED))
c = tLBRACE; /* hash */
else if (IS_lex_state(EXPR_ARG_ANY | EXPR_END | EXPR_ENDFN))
@@ -8560,83 +8253,104 @@ parser_yylex(struct parser_params *parser)
c = tLBRACE; /* hash */
COND_PUSH(0);
CMDARG_PUSH(0);
- SET_LEX_STATE(EXPR_BEG);
- if (c != tLBRACE_ARG) lex_state |= EXPR_LABEL;
- if (c != tLBRACE) command_start = TRUE;
+ SET_LEX_STATE(c != tLBRACE ? EXPR_BEG : EXPR_BEG|EXPR_LABEL);
+ if (c != tLBRACE) p->command_start = TRUE;
return c;
case '\\':
- c = nextc();
+ c = nextc(p);
if (c == '\n') {
space_seen = 1;
- dispatch_scan_event(tSP);
+ dispatch_scan_event(p, tSP);
goto retry; /* skip \\n */
}
- pushback(c);
+ if (c == ' ') return tSP;
+ if (ISSPACE(c)) return c;
+ pushback(p, c);
return '\\';
case '%':
- return parse_percent(parser, space_seen, last_state);
+ return parse_percent(p, space_seen, last_state);
case '$':
- return parse_gvar(parser, last_state);
+ return parse_gvar(p, last_state);
case '@':
- return parse_atmark(parser, last_state);
+ return parse_atmark(p, last_state);
case '_':
- if (was_bol() && whole_match_p("__END__", 7, 0)) {
- ruby__end__seen = 1;
- parser->eofp = 1;
+ if (was_bol(p) && whole_match_p(p, "__END__", 7, 0)) {
+ p->ruby__end__seen = 1;
+ p->eofp = 1;
#ifndef RIPPER
return -1;
#else
- lex_goto_eol(parser);
- dispatch_scan_event(k__END__);
+ lex_goto_eol(p);
+ dispatch_scan_event(p, k__END__);
return 0;
#endif
}
- newtok();
+ newtok(p);
break;
default:
- if (!parser_is_identchar()) {
- compile_error(PARSER_ARG "Invalid char `\\x%02X' in expression", c);
+ if (!parser_is_identchar(p)) {
+ compile_error(p, "Invalid char `\\x%02X' in expression", c);
goto retry;
}
- newtok();
+ newtok(p);
break;
}
- return parse_ident(parser, c, cmd_state);
+ return parse_ident(p, c, cmd_state);
}
-static int
-yylex(YYSTYPE *lval, struct parser_params *parser)
+static enum yytokentype
+yylex(YYSTYPE *lval, YYLTYPE *yylloc, struct parser_params *p)
{
- int t;
+ enum yytokentype t;
- parser->lval = lval;
+ p->lval = lval;
lval->val = Qundef;
- t = parser_yylex(parser);
- if (has_delayed_token())
- dispatch_delayed_token(t);
+ t = parser_yylex(p);
+
+ if (p->lex.strterm && (p->lex.strterm->flags & STRTERM_HEREDOC))
+ RUBY_SET_YYLLOC_FROM_STRTERM_HEREDOC(*yylloc);
+ else
+ RUBY_SET_YYLLOC(*yylloc);
+
+ if (has_delayed_token(p))
+ dispatch_delayed_token(p, t);
else if (t != 0)
- dispatch_scan_event(t);
+ dispatch_scan_event(p, t);
return t;
}
-#ifndef RIPPER
+#define LVAR_USED ((ID)1 << (sizeof(ID) * CHAR_BIT - 1))
+
static NODE*
-node_newnode(struct parser_params *parser, enum node_type type, VALUE a0, VALUE a1, VALUE a2)
+node_newnode(struct parser_params *p, enum node_type type, VALUE a0, VALUE a1, VALUE a2, const rb_code_location_t *loc)
{
- NODE *n = (rb_node_newnode)(type, a0, a1, a2);
- nd_set_line(n, ruby_sourceline);
+ NODE *n = rb_ast_newnode(p->ast);
+
+ rb_node_init(n, type, a0, a1, a2);
+
+ nd_set_loc(n, loc);
+ nd_set_node_id(n, parser_get_node_id(p));
return n;
}
+static NODE *
+nd_set_loc(NODE *nd, const YYLTYPE *loc)
+{
+ nd->nd_loc = *loc;
+ nd_set_line(nd, loc->beg_pos.lineno);
+ return nd;
+}
+
+#ifndef RIPPER
static enum node_type
nodetype(NODE *node) /* for debug */
{
@@ -8664,26 +8378,23 @@ fixpos(NODE *node, NODE *orig)
{
if (!node) return;
if (!orig) return;
- if (orig == (NODE*)1) return;
nd_set_line(node, nd_line(orig));
}
static void
-parser_warning(struct parser_params *parser, NODE *node, const char *mesg)
+parser_warning(struct parser_params *p, NODE *node, const char *mesg)
{
- rb_compile_warning(ruby_sourcefile, nd_line(node), "%s", mesg);
+ rb_compile_warning(p->ruby_sourcefile, nd_line(node), "%s", mesg);
}
-#define parser_warning(node, mesg) parser_warning(parser, (node), (mesg))
static void
-parser_warn(struct parser_params *parser, NODE *node, const char *mesg)
+parser_warn(struct parser_params *p, NODE *node, const char *mesg)
{
- rb_compile_warn(ruby_sourcefile, nd_line(node), "%s", mesg);
+ rb_compile_warn(p->ruby_sourcefile, nd_line(node), "%s", mesg);
}
-#define parser_warn(node, mesg) parser_warn(parser, (node), (mesg))
static NODE*
-block_append_gen(struct parser_params *parser, NODE *head, NODE *tail)
+block_append(struct parser_params *p, NODE *head, NODE *tail)
{
NODE *end, *h = head, *nd;
@@ -8697,12 +8408,11 @@ block_append_gen(struct parser_params *parser, NODE *head, NODE *tail)
case NODE_TRUE:
case NODE_FALSE:
case NODE_NIL:
- parser_warning(h, "unused literal ignored");
+ parser_warning(p, h, "unused literal ignored");
return tail;
default:
- h = end = NEW_BLOCK(head);
+ h = end = NEW_BLOCK(head, &head->nd_loc);
end->nd_end = end;
- fixpos(end, head);
head = end;
break;
case NODE_BLOCK:
@@ -8718,7 +8428,7 @@ block_append_gen(struct parser_params *parser, NODE *head, NODE *tail)
case NODE_REDO:
case NODE_RETRY:
if (RTEST(ruby_verbose)) {
- parser_warning(tail, "statement not reached");
+ parser_warning(p, tail, "statement not reached");
}
break;
@@ -8727,21 +8437,22 @@ block_append_gen(struct parser_params *parser, NODE *head, NODE *tail)
}
if (nd_type(tail) != NODE_BLOCK) {
- tail = NEW_BLOCK(tail);
+ tail = NEW_BLOCK(tail, &tail->nd_loc);
tail->nd_end = tail;
}
end->nd_next = tail;
h->nd_end = tail->nd_end;
+ nd_set_last_loc(head, nd_last_loc(tail));
return head;
}
/* append item to the list */
static NODE*
-list_append_gen(struct parser_params *parser, NODE *list, NODE *item)
+list_append(struct parser_params *p, NODE *list, NODE *item)
{
NODE *last;
- if (list == 0) return NEW_LIST(item);
+ if (list == 0) return NEW_LIST(item, &item->nd_loc);
if (list->nd_next) {
last = list->nd_next->nd_end;
}
@@ -8750,8 +8461,11 @@ list_append_gen(struct parser_params *parser, NODE *list, NODE *item)
}
list->nd_alen += 1;
- last->nd_next = NEW_LIST(item);
+ last->nd_next = NEW_LIST(item, &item->nd_loc);
list->nd_next->nd_end = last->nd_next;
+
+ nd_set_last_loc(list, nd_last_loc(item));
+
return list;
}
@@ -8777,15 +8491,17 @@ list_concat(NODE *head, NODE *tail)
head->nd_next->nd_end = tail;
}
+ nd_set_last_loc(head, nd_last_loc(tail));
+
return head;
}
static int
-literal_concat0(struct parser_params *parser, VALUE head, VALUE tail)
+literal_concat0(struct parser_params *p, VALUE head, VALUE tail)
{
if (NIL_P(tail)) return 1;
if (!rb_enc_compatible(head, tail)) {
- compile_error(PARSER_ARG "string literal encodings differ (%s / %s)",
+ compile_error(p, "string literal encodings differ (%s / %s)",
rb_enc_name(rb_enc_get(head)),
rb_enc_name(rb_enc_get(tail)));
rb_str_resize(head, 0);
@@ -8798,7 +8514,7 @@ literal_concat0(struct parser_params *parser, VALUE head, VALUE tail)
/* concat two string literals */
static NODE *
-literal_concat_gen(struct parser_params *parser, NODE *head, NODE *tail)
+literal_concat(struct parser_params *p, NODE *head, NODE *tail, const YYLTYPE *loc)
{
enum node_type htype;
NODE *headlast;
@@ -8809,16 +8525,16 @@ literal_concat_gen(struct parser_params *parser, NODE *head, NODE *tail)
htype = nd_type(head);
if (htype == NODE_EVSTR) {
- NODE *node = NEW_DSTR(STR_NEW0());
- head = list_append(node, head);
+ NODE *node = NEW_DSTR(add_mark_object(p, STR_NEW0()), loc);
+ head = list_append(p, node, head);
htype = NODE_DSTR;
}
- if (heredoc_indent > 0) {
+ if (p->heredoc_indent > 0) {
switch (htype) {
case NODE_STR:
nd_set_type(head, NODE_DSTR);
case NODE_DSTR:
- return list_append(head, tail);
+ return list_append(p, head, tail);
default:
break;
}
@@ -8834,25 +8550,25 @@ literal_concat_gen(struct parser_params *parser, NODE *head, NODE *tail)
lit = head->nd_lit;
}
if (htype == NODE_STR) {
- if (!literal_concat0(parser, lit, tail->nd_lit)) {
+ if (!literal_concat0(p, lit, tail->nd_lit)) {
error:
- rb_gc_force_recycle((VALUE)head);
- rb_gc_force_recycle((VALUE)tail);
+ rb_discard_node(p, head);
+ rb_discard_node(p, tail);
return 0;
}
- rb_gc_force_recycle((VALUE)tail);
+ rb_discard_node(p, tail);
}
else {
- list_append(head, tail);
+ list_append(p, head, tail);
}
break;
case NODE_DSTR:
if (htype == NODE_STR) {
- if (!literal_concat0(parser, head->nd_lit, tail->nd_lit))
+ if (!literal_concat0(p, head->nd_lit, tail->nd_lit))
goto error;
tail->nd_lit = head->nd_lit;
- rb_gc_force_recycle((VALUE)head);
+ rb_discard_node(p, head);
head = tail;
}
else if (NIL_P(tail->nd_lit)) {
@@ -8860,19 +8576,19 @@ literal_concat_gen(struct parser_params *parser, NODE *head, NODE *tail)
head->nd_alen += tail->nd_alen - 1;
head->nd_next->nd_end->nd_next = tail->nd_next;
head->nd_next->nd_end = tail->nd_next->nd_end;
- rb_gc_force_recycle((VALUE)tail);
+ rb_discard_node(p, tail);
}
else if (htype == NODE_DSTR && (headlast = head->nd_next->nd_end->nd_head) &&
nd_type(headlast) == NODE_STR) {
lit = headlast->nd_lit;
- if (!literal_concat0(parser, lit, tail->nd_lit))
+ if (!literal_concat0(p, lit, tail->nd_lit))
goto error;
tail->nd_lit = Qnil;
goto append;
}
else {
nd_set_type(tail, NODE_ARRAY);
- tail->nd_head = NEW_STR(tail->nd_lit);
+ tail->nd_head = NEW_STR(tail->nd_lit, loc);
list_concat(head, tail);
}
break;
@@ -8882,23 +8598,23 @@ literal_concat_gen(struct parser_params *parser, NODE *head, NODE *tail)
nd_set_type(head, NODE_DSTR);
head->nd_alen = 1;
}
- list_append(head, tail);
+ list_append(p, head, tail);
break;
}
return head;
}
static NODE *
-evstr2dstr_gen(struct parser_params *parser, NODE *node)
+evstr2dstr(struct parser_params *p, NODE *node)
{
if (nd_type(node) == NODE_EVSTR) {
- node = list_append(NEW_DSTR(STR_NEW0()), node);
+ node = list_append(p, NEW_DSTR(add_mark_object(p, STR_NEW0()), &node->nd_loc), node);
}
return node;
}
static NODE *
-new_evstr_gen(struct parser_params *parser, NODE *node)
+new_evstr(struct parser_params *p, NODE *node, const YYLTYPE *loc)
{
NODE *head = node;
@@ -8908,63 +8624,102 @@ new_evstr_gen(struct parser_params *parser, NODE *node)
return node;
}
}
- return NEW_EVSTR(head);
+ return NEW_EVSTR(head, loc);
}
static NODE *
-call_bin_op_gen(struct parser_params *parser, NODE *recv, ID id, NODE *arg1)
+call_bin_op(struct parser_params *p, NODE *recv, ID id, NODE *arg1,
+ const YYLTYPE *op_loc, const YYLTYPE *loc)
{
+ NODE *expr;
value_expr(recv);
value_expr(arg1);
- return NEW_CALL(recv, id, NEW_LIST(arg1));
+ expr = NEW_OPCALL(recv, id, NEW_LIST(arg1, &arg1->nd_loc), loc);
+ nd_set_line(expr, op_loc->beg_pos.lineno);
+ return expr;
}
static NODE *
-call_uni_op_gen(struct parser_params *parser, NODE *recv, ID id)
+call_uni_op(struct parser_params *p, NODE *recv, ID id, const YYLTYPE *op_loc, const YYLTYPE *loc)
{
+ NODE *opcall;
value_expr(recv);
- return NEW_CALL(recv, id, 0);
+ opcall = NEW_OPCALL(recv, id, 0, loc);
+ nd_set_line(opcall, op_loc->beg_pos.lineno);
+ return opcall;
+}
+
+static NODE *
+new_qcall(struct parser_params* p, ID atype, NODE *recv, ID mid, NODE *args, const YYLTYPE *op_loc, const YYLTYPE *loc)
+{
+ NODE *qcall = NEW_QCALL(atype, recv, mid, args, loc);
+ nd_set_line(qcall, op_loc->beg_pos.lineno);
+ return qcall;
+}
+
+static NODE*
+new_command_qcall(struct parser_params* p, ID atype, NODE *recv, ID mid, NODE *args, NODE *block, const YYLTYPE *op_loc, const YYLTYPE *loc)
+{
+ NODE *ret;
+ if (block) block_dup_check(p, args, block);
+ ret = new_qcall(p, atype, recv, mid, args, op_loc, loc);
+ if (block) ret = method_add_block(p, ret, block, loc);
+ fixpos(ret, recv);
+ return ret;
}
+#define nd_once_body(node) (nd_type(node) == NODE_ONCE ? (node)->nd_body : node)
static NODE*
-match_op_gen(struct parser_params *parser, NODE *node1, NODE *node2)
+match_op(struct parser_params *p, NODE *node1, NODE *node2, const YYLTYPE *op_loc, const YYLTYPE *loc)
{
+ NODE *n;
+ int line = op_loc->beg_pos.lineno;
+
value_expr(node1);
value_expr(node2);
- if (node1) {
- switch (nd_type(node1)) {
+ if (node1 && (n = nd_once_body(node1)) != 0) {
+ switch (nd_type(n)) {
case NODE_DREGX:
- case NODE_DREGX_ONCE:
- return NEW_MATCH2(node1, node2);
+ {
+ NODE *match = NEW_MATCH2(node1, node2, loc);
+ nd_set_line(match, line);
+ return match;
+ }
case NODE_LIT:
- if (RB_TYPE_P(node1->nd_lit, T_REGEXP)) {
- return NEW_MATCH2(node1, node2);
+ if (RB_TYPE_P(n->nd_lit, T_REGEXP)) {
+ const VALUE lit = n->nd_lit;
+ NODE *match = NEW_MATCH2(node1, node2, loc);
+ match->nd_args = reg_named_capture_assign(p, lit, loc);
+ nd_set_line(match, line);
+ return match;
}
}
}
- if (node2) {
- switch (nd_type(node2)) {
- case NODE_DREGX:
- case NODE_DREGX_ONCE:
- return NEW_MATCH3(node2, node1);
+ if (node2 && (n = nd_once_body(node2)) != 0) {
+ NODE *match3;
+ switch (nd_type(n)) {
case NODE_LIT:
- if (RB_TYPE_P(node2->nd_lit, T_REGEXP)) {
- return NEW_MATCH3(node2, node1);
- }
+ if (!RB_TYPE_P(n->nd_lit, T_REGEXP)) break;
+ /* fallthru */
+ case NODE_DREGX:
+ match3 = NEW_MATCH3(node2, node1, loc);
+ return match3;
}
}
- return NEW_CALL(node1, tMATCH, NEW_LIST(node2));
+ n = NEW_CALL(node1, tMATCH, NEW_LIST(node2, &node2->nd_loc), loc);
+ nd_set_line(n, line);
+ return n;
}
# if WARN_PAST_SCOPE
static int
-past_dvar_p(struct parser_params *parser, ID id)
+past_dvar_p(struct parser_params *p, ID id)
{
- struct vtable *past = lvtbl->past;
+ struct vtable *past = p->lvtbl->past;
while (past) {
if (vtable_included(past, id)) return 1;
past = past->prev;
@@ -8973,66 +8728,106 @@ past_dvar_p(struct parser_params *parser, ID id)
}
# endif
+#define WARN_LOCATION(type) do { \
+ if (p->warn_location) { \
+ rb_warning0(type" in eval may not return location in binding;" \
+ " use Binding#source_location instead"); \
+ } \
+} while (0)
+
static NODE*
-gettable_gen(struct parser_params *parser, ID id)
+gettable(struct parser_params *p, ID id, const YYLTYPE *loc)
{
+ ID *vidp = NULL;
+ NODE *node;
switch (id) {
case keyword_self:
- return NEW_SELF();
+ return NEW_SELF(loc);
case keyword_nil:
- return NEW_NIL();
+ return NEW_NIL(loc);
case keyword_true:
- return NEW_TRUE();
+ return NEW_TRUE(loc);
case keyword_false:
- return NEW_FALSE();
+ return NEW_FALSE(loc);
case keyword__FILE__:
- return NEW_STR(rb_str_dup(ruby_sourcefile_string));
+ WARN_LOCATION("__FILE__");
+ {
+ VALUE file = p->ruby_sourcefile_string;
+ if (NIL_P(file))
+ file = rb_str_new(0, 0);
+ else
+ file = rb_str_dup(file);
+ node = NEW_STR(add_mark_object(p, file), loc);
+ }
+ return node;
case keyword__LINE__:
- return NEW_LIT(INT2FIX(tokline));
+ WARN_LOCATION("__LINE__");
+ return NEW_LIT(INT2FIX(p->tokline), loc);
case keyword__ENCODING__:
- return NEW_LIT(rb_enc_from_encoding(current_enc));
+ return NEW_LIT(add_mark_object(p, rb_enc_from_encoding(p->enc)), loc);
}
switch (id_type(id)) {
case ID_LOCAL:
- if (dyna_in_block() && dvar_defined(id)) {
- if (id == current_arg) {
+ if (dyna_in_block(p) && dvar_defined_ref(p, id, &vidp)) {
+ if (id == p->cur_arg) {
rb_warn1("circular argument reference - %"PRIsWARN, rb_id2str(id));
}
- return NEW_DVAR(id);
+ if (vidp) *vidp |= LVAR_USED;
+ node = NEW_DVAR(id, loc);
+ return node;
}
- if (local_id(id)) {
- if (id == current_arg) {
+ if (local_id_ref(p, id, &vidp)) {
+ if (id == p->cur_arg) {
rb_warn1("circular argument reference - %"PRIsWARN, rb_id2str(id));
}
- return NEW_LVAR(id);
+ if (vidp) *vidp |= LVAR_USED;
+ node = NEW_LVAR(id, loc);
+ return node;
}
# if WARN_PAST_SCOPE
- if (!in_defined && RTEST(ruby_verbose) && past_dvar_p(parser, id)) {
+ if (!p->in_defined && RTEST(ruby_verbose) && past_dvar_p(p, id)) {
rb_warning1("possible reference to past scope - %"PRIsWARN, rb_id2str(id));
}
# endif
/* method call without arguments */
- return NEW_VCALL(id);
+ return NEW_VCALL(id, loc);
case ID_GLOBAL:
- return NEW_GVAR(id);
+ return NEW_GVAR(id, loc);
case ID_INSTANCE:
- return NEW_IVAR(id);
+ return NEW_IVAR(id, loc);
case ID_CONST:
- return NEW_CONST(id);
+ return NEW_CONST(id, loc);
case ID_CLASS:
- return NEW_CVAR(id);
+ return NEW_CVAR(id, loc);
}
- compile_error(PARSER_ARG "identifier %"PRIsVALUE" is not valid to get", rb_id2str(id));
+ compile_error(p, "identifier %"PRIsVALUE" is not valid to get", rb_id2str(id));
return 0;
}
static NODE *
+opt_arg_append(NODE *opt_list, NODE *opt)
+{
+ NODE *opts = opt_list;
+ opts->nd_loc.end_pos = opt->nd_loc.end_pos;
+
+ while (opts->nd_next) {
+ opts = opts->nd_next;
+ opts->nd_loc.end_pos = opt->nd_loc.end_pos;
+ }
+ opts->nd_next = opt;
+
+ return opt_list;
+}
+
+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;
}
@@ -9040,45 +8835,62 @@ kwd_append(NODE *kwlist, NODE *kw)
}
static NODE *
-new_regexp_gen(struct parser_params *parser, NODE *node, int options)
+new_defined(struct parser_params *p, NODE *expr, const YYLTYPE *loc)
+{
+ return NEW_DEFINED(remove_begin_all(expr), loc);
+}
+
+static NODE*
+symbol_append(struct parser_params *p, NODE *symbols, NODE *symbol)
+{
+ if (nd_type(symbol) == NODE_DSTR) {
+ nd_set_type(symbol, NODE_DSYM);
+ }
+ else {
+ nd_set_type(symbol, NODE_LIT);
+ symbol->nd_lit = add_mark_object(p, rb_str_intern(symbol->nd_lit));
+ }
+ return list_append(p, symbols, symbol);
+}
+
+static NODE *
+new_regexp(struct parser_params *p, NODE *node, int options, const YYLTYPE *loc)
{
NODE *list, *prev;
+ VALUE lit;
if (!node) {
- return NEW_LIT(reg_compile(STR_NEW0(), options));
+ return NEW_LIT(add_mark_object(p, reg_compile(p, STR_NEW0(), options)), loc);
}
switch (nd_type(node)) {
case NODE_STR:
{
VALUE src = node->nd_lit;
nd_set_type(node, NODE_LIT);
- node->nd_lit = reg_compile(src, options);
+ nd_set_loc(node, loc);
+ add_mark_object(p, node->nd_lit = reg_compile(p, src, options));
}
break;
default:
- node = NEW_NODE(NODE_DSTR, STR_NEW0(), 1, NEW_LIST(node));
+ add_mark_object(p, lit = STR_NEW0());
+ node = NEW_NODE(NODE_DSTR, lit, 1, NEW_LIST(node, loc), loc);
case NODE_DSTR:
- if (options & RE_OPTION_ONCE) {
- nd_set_type(node, NODE_DREGX_ONCE);
- }
- else {
- nd_set_type(node, NODE_DREGX);
- }
+ nd_set_type(node, NODE_DREGX);
+ nd_set_loc(node, loc);
node->nd_cflag = options & RE_OPTION_MASK;
- if (!NIL_P(node->nd_lit)) reg_fragment_check(node->nd_lit, options);
+ if (!NIL_P(node->nd_lit)) reg_fragment_check(p, node->nd_lit, options);
for (list = (prev = node)->nd_next; list; list = list->nd_next) {
if (nd_type(list->nd_head) == NODE_STR) {
VALUE tail = list->nd_head->nd_lit;
- if (reg_fragment_check(tail, options) && prev && !NIL_P(prev->nd_lit)) {
+ if (reg_fragment_check(p, tail, options) && prev && !NIL_P(prev->nd_lit)) {
VALUE lit = prev == node ? prev->nd_lit : prev->nd_head->nd_lit;
- if (!literal_concat0(parser, lit, tail)) {
- node = 0;
- break;
+ if (!literal_concat0(p, lit, tail)) {
+ return NEW_NIL(loc); /* dummy node on error */
}
rb_str_resize(tail, 0);
prev->nd_next = list->nd_next;
- rb_gc_force_recycle((VALUE)list->nd_head);
- rb_gc_force_recycle((VALUE)list);
+ rb_discard_node(p, list->nd_head);
+ rb_discard_node(p, list);
list = prev;
}
else {
@@ -9092,7 +8904,10 @@ new_regexp_gen(struct parser_params *parser, NODE *node, int options)
if (!node->nd_next) {
VALUE src = node->nd_lit;
nd_set_type(node, NODE_LIT);
- node->nd_lit = reg_compile(src, options);
+ add_mark_object(p, node->nd_lit = reg_compile(p, src, options));
+ }
+ if (options & RE_OPTION_ONCE) {
+ node = NEW_NODE(NODE_ONCE, 0, node, 0, loc);
}
break;
}
@@ -9100,45 +8915,58 @@ new_regexp_gen(struct parser_params *parser, NODE *node, int options)
}
static NODE *
-new_xstring_gen(struct parser_params *parser, NODE *node)
+new_kw_arg(struct parser_params *p, NODE *k, const YYLTYPE *loc)
+{
+ if (!k) return 0;
+ return NEW_KW_ARG(0, (k), loc);
+}
+
+static NODE *
+new_xstring(struct parser_params *p, NODE *node, const YYLTYPE *loc)
{
if (!node) {
- return NEW_XSTR(STR_NEW0());
+ VALUE lit = STR_NEW0();
+ NODE *xstr = NEW_XSTR(lit, loc);
+ add_mark_object(p, lit);
+ return xstr;
}
switch (nd_type(node)) {
case NODE_STR:
nd_set_type(node, NODE_XSTR);
+ nd_set_loc(node, loc);
break;
case NODE_DSTR:
nd_set_type(node, NODE_DXSTR);
+ nd_set_loc(node, loc);
break;
default:
- node = NEW_NODE(NODE_DXSTR, Qnil, 1, NEW_LIST(node));
+ node = NEW_NODE(NODE_DXSTR, Qnil, 1, NEW_LIST(node, loc), loc);
break;
}
return node;
}
+
#else /* !RIPPER */
static int
-id_is_var_gen(struct parser_params *parser, ID id)
+id_is_var(struct parser_params *p, ID id)
{
if (is_notop_id(id)) {
switch (id & ID_SCOPE_MASK) {
case ID_GLOBAL: case ID_INSTANCE: case ID_CONST: case ID_CLASS:
return 1;
case ID_LOCAL:
- if (dyna_in_block() && dvar_defined(id)) return 1;
- if (local_id(id)) return 1;
+ if (dyna_in_block(p) && dvar_defined(p, id)) return 1;
+ if (local_id(p, id)) return 1;
/* method call without arguments */
return 0;
}
}
- compile_error(PARSER_ARG "identifier %s is not valid to get", rb_id2str(id));
+ compile_error(p, "identifier %"PRIsVALUE" is not valid to get", rb_id2str(id));
return 0;
}
static VALUE
-new_regexp_gen(struct parser_params *parser, VALUE re, VALUE opt)
+new_regexp(struct parser_params *p, VALUE re, VALUE opt, const YYLTYPE *loc)
{
VALUE src = 0, err;
int options = 0;
@@ -9150,20 +8978,15 @@ new_regexp_gen(struct parser_params *parser, VALUE re, VALUE opt)
options = (int)RNODE(opt)->nd_tag;
opt = RNODE(opt)->nd_rval;
}
- if (src && NIL_P(parser_reg_compile(parser, src, options, &err))) {
- compile_error(PARSER_ARG "%"PRIsVALUE, err);
+ if (src && NIL_P(parser_reg_compile(p, src, options, &err))) {
+ compile_error(p, "%"PRIsVALUE, err);
}
return dispatch2(regexp_literal, re, opt);
}
-
-static VALUE
-new_xstring_gen(struct parser_params *parser, VALUE str)
-{
- return dispatch1(xstring_literal, str);
-}
#endif /* !RIPPER */
-static const char lex_state_names[][13] = {
+#ifndef RIPPER
+static const char rb_parser_lex_state_names[][13] = {
"EXPR_BEG", "EXPR_END", "EXPR_ENDARG", "EXPR_ENDFN", "EXPR_ARG",
"EXPR_CMDARG", "EXPR_MID", "EXPR_FNAME", "EXPR_DOT", "EXPR_CLASS",
"EXPR_LABEL", "EXPR_LABELED","EXPR_FITEM",
@@ -9182,7 +9005,7 @@ append_lex_state_name(enum lex_state_e state, VALUE buf)
rb_str_cat(buf, "|", 1);
}
sep = 1;
- rb_str_cat_cstr(buf, lex_state_names[i]);
+ rb_str_cat_cstr(buf, rb_parser_lex_state_names[i]);
}
}
if (!sep) {
@@ -9191,8 +9014,23 @@ append_lex_state_name(enum lex_state_e state, VALUE buf)
return buf;
}
-static enum lex_state_e
-trace_lex_state(enum lex_state_e from, enum lex_state_e to, int line)
+static void
+flush_debug_buffer(struct parser_params *p, VALUE out, VALUE str)
+{
+ VALUE mesg = p->debug_buffer;
+
+ if (!NIL_P(mesg) && RSTRING_LEN(mesg)) {
+ p->debug_buffer = Qnil;
+ rb_io_puts(1, &mesg, out);
+ }
+ if (!NIL_P(str) && RSTRING_LEN(str)) {
+ rb_io_write(p->debug_output, str);
+ }
+}
+
+enum lex_state_e
+rb_parser_trace_lex_state(struct parser_params *p, enum lex_state_e from,
+ enum lex_state_e to, int line)
{
VALUE mesg;
mesg = rb_str_new_cstr("lex_state: ");
@@ -9200,14 +9038,19 @@ trace_lex_state(enum lex_state_e from, enum lex_state_e to, int line)
rb_str_cat_cstr(mesg, " -> ");
append_lex_state_name(to, mesg);
rb_str_catf(mesg, " at line %d\n", line);
- rb_io_write(rb_stdout, mesg);
+ flush_debug_buffer(p, p->debug_output, mesg);
return to;
}
+VALUE
+rb_parser_lex_state_name(enum lex_state_e state)
+{
+ return rb_fstring(append_lex_state_name(state, rb_str_new(0, 0)));
+}
+
static void
-show_bitstack(stack_type stack, const char *name, int line)
+append_bitstack_value(stack_type stack, VALUE mesg)
{
- VALUE mesg = rb_sprintf("%s: ", name);
if (stack == 0) {
rb_str_cat_cstr(mesg, "0");
}
@@ -9216,92 +9059,200 @@ show_bitstack(stack_type stack, const char *name, int line)
for (; mask && !(stack & mask); mask >>= 1) continue;
for (; mask; mask >>= 1) rb_str_cat(mesg, stack & mask ? "1" : "0", 1);
}
+}
+
+void
+rb_parser_show_bitstack(struct parser_params *p, stack_type stack,
+ const char *name, int line)
+{
+ VALUE mesg = rb_sprintf("%s: ", name);
+ append_bitstack_value(stack, mesg);
rb_str_catf(mesg, " at line %d\n", line);
- rb_io_write(rb_stdout, mesg);
+ flush_debug_buffer(p, p->debug_output, mesg);
}
-#ifdef RIPPER
-static VALUE
-assignable_gen(struct parser_params *parser, VALUE lhs)
+void
+rb_parser_fatal(struct parser_params *p, const char *fmt, ...)
+{
+ va_list ap;
+ VALUE mesg = rb_str_new_cstr("internal p error: ");
+
+ va_start(ap, fmt);
+ rb_str_vcatf(mesg, fmt, ap);
+ va_end(ap);
+ parser_yyerror(p, NULL, RSTRING_PTR(mesg));
+ RB_GC_GUARD(mesg);
+
+ mesg = rb_str_new(0, 0);
+ append_lex_state_name(p->lex.state, mesg);
+ compile_error(p, "p->lex.state: %"PRIsVALUE, mesg);
+ rb_str_resize(mesg, 0);
+ append_bitstack_value(p->cond_stack, mesg);
+ compile_error(p, "p->cond_stack: %"PRIsVALUE, mesg);
+ rb_str_resize(mesg, 0);
+ append_bitstack_value(p->cmdarg_stack, mesg);
+ compile_error(p, "p->cmdarg_stack: %"PRIsVALUE, mesg);
+ if (p->debug_output == rb_stdout)
+ p->debug_output = rb_stderr;
+ p->debug = TRUE;
+}
+
+void
+rb_parser_set_location_from_strterm_heredoc(struct parser_params *p, rb_strterm_heredoc_t *here, YYLTYPE *yylloc)
+{
+ const char *eos = RSTRING_PTR(here->term);
+ long term_len = RSTRING_LEN(here->term) - 2 + (unsigned char)eos[0];
+
+ yylloc->beg_pos.lineno = (int)here->sourceline;
+ yylloc->beg_pos.column = (int)(here->u3.lastidx - term_len);
+ yylloc->end_pos.lineno = (int)here->sourceline;
+ yylloc->end_pos.column = (int)(here->u3.lastidx);
+}
+
+void
+rb_parser_set_location_of_none(struct parser_params *p, YYLTYPE *yylloc)
+{
+ yylloc->beg_pos.lineno = p->ruby_sourceline;
+ yylloc->beg_pos.column = (int)(p->lex.ptok - p->lex.pbeg);
+ yylloc->end_pos.lineno = p->ruby_sourceline;
+ yylloc->end_pos.column = (int)(p->lex.ptok - p->lex.pbeg);
+}
+
+void
+rb_parser_set_location(struct parser_params *p, YYLTYPE *yylloc)
+{
+ yylloc->beg_pos.lineno = p->ruby_sourceline;
+ yylloc->beg_pos.column = (int)(p->lex.ptok - p->lex.pbeg);
+ yylloc->end_pos.lineno = p->ruby_sourceline;
+ yylloc->end_pos.column = (int)(p->lex.pcur - p->lex.pbeg);
+}
+#endif /* !RIPPER */
+
+static void
+parser_token_value_print(struct parser_params *p, enum yytokentype type, const YYSTYPE *valp)
+{
+ VALUE v;
+
+ switch (type) {
+ case tIDENTIFIER: case tFID: case tGVAR: case tIVAR:
+ case tCONSTANT: case tCVAR: case tLABEL: case tOP_ASGN:
+#ifndef RIPPER
+ v = rb_id2str(valp->id);
#else
-static NODE*
-assignable_gen(struct parser_params *parser, ID id, NODE *val)
+ v = valp->node->nd_rval;
#endif
-{
-#ifdef RIPPER
- ID id = get_id(lhs);
-# define assignable_result(x) get_value(lhs)
-# define parser_yyerror(parser, x) assign_error_gen(parser, lhs)
+ rb_parser_printf(p, "%"PRIsVALUE, v);
+ break;
+ case tINTEGER: case tFLOAT: case tRATIONAL: case tIMAGINARY:
+ case tSTRING_CONTENT: case tCHAR:
+#ifndef RIPPER
+ v = valp->node->nd_lit;
#else
-# define assignable_result(x) (x)
+ v = valp->val;
#endif
- if (!id) return assignable_result(0);
+ rb_parser_printf(p, "%+"PRIsVALUE, v);
+ break;
+ case tNTH_REF:
+#ifndef RIPPER
+ rb_parser_printf(p, "$%ld", valp->node->nd_nth);
+#else
+ rb_parser_printf(p, "%"PRIsVALUE, valp->val);
+#endif
+ break;
+ case tBACK_REF:
+#ifndef RIPPER
+ rb_parser_printf(p, "$%c", (int)valp->node->nd_nth);
+#else
+ rb_parser_printf(p, "%"PRIsVALUE, valp->val);
+#endif
+ break;
+ default:
+ break;
+ }
+}
+
+static int
+assignable0(struct parser_params *p, ID id, const char **err)
+{
+ if (!id) return -1;
switch (id) {
case keyword_self:
- yyerror("Can't change the value of self");
- goto error;
+ *err = "Can't change the value of self";
+ return -1;
case keyword_nil:
- yyerror("Can't assign to nil");
- goto error;
+ *err = "Can't assign to nil";
+ return -1;
case keyword_true:
- yyerror("Can't assign to true");
- goto error;
+ *err = "Can't assign to true";
+ return -1;
case keyword_false:
- yyerror("Can't assign to false");
- goto error;
+ *err = "Can't assign to false";
+ return -1;
case keyword__FILE__:
- yyerror("Can't assign to __FILE__");
- goto error;
+ *err = "Can't assign to __FILE__";
+ return -1;
case keyword__LINE__:
- yyerror("Can't assign to __LINE__");
- goto error;
+ *err = "Can't assign to __LINE__";
+ return -1;
case keyword__ENCODING__:
- yyerror("Can't assign to __ENCODING__");
- goto error;
+ *err = "Can't assign to __ENCODING__";
+ return -1;
}
switch (id_type(id)) {
case ID_LOCAL:
- if (dyna_in_block()) {
- if (dvar_curr(id)) {
- return assignable_result(NEW_DASGN_CURR(id, val));
- }
- else if (dvar_defined(id)) {
- return assignable_result(NEW_DASGN(id, val));
- }
- else if (local_id(id)) {
- return assignable_result(NEW_LASGN(id, val));
- }
- else {
- dyna_var(id);
- return assignable_result(NEW_DASGN_CURR(id, val));
- }
+ if (dyna_in_block(p)) {
+ if (dvar_curr(p, id)) return NODE_DASGN_CURR;
+ if (dvar_defined(p, id)) return NODE_DASGN;
+ if (local_id(p, id)) return NODE_LASGN;
+ dyna_var(p, id);
+ return NODE_DASGN_CURR;
}
else {
- if (!local_id(id)) {
- local_var(id);
- }
- return assignable_result(NEW_LASGN(id, val));
+ if (!local_id(p, id)) local_var(p, id);
+ return NODE_LASGN;
}
break;
- case ID_GLOBAL:
- return assignable_result(NEW_GASGN(id, val));
- case ID_INSTANCE:
- return assignable_result(NEW_IASGN(id, val));
+ case ID_GLOBAL: return NODE_GASGN;
+ case ID_INSTANCE: return NODE_IASGN;
case ID_CONST:
- if (!in_def && !in_single)
- return assignable_result(NEW_CDECL(id, val, 0));
- yyerror("dynamic constant assignment");
- break;
- case ID_CLASS:
- return assignable_result(NEW_CVASGN(id, val));
+ if (!p->in_def) return NODE_CDECL;
+ *err = "dynamic constant assignment";
+ return -1;
+ case ID_CLASS: return NODE_CVASGN;
default:
- compile_error(PARSER_ARG "identifier %"PRIsVALUE" is not valid to set", rb_id2str(id));
+ compile_error(p, "identifier %"PRIsVALUE" is not valid to set", rb_id2str(id));
}
- error:
- return assignable_result(0);
-#undef assignable_result
-#undef parser_yyerror
+ return -1;
+}
+
+#ifndef RIPPER
+static NODE*
+assignable(struct parser_params *p, ID id, NODE *val, const YYLTYPE *loc)
+{
+ const char *err = 0;
+ int node_type = assignable0(p, id, &err);
+ switch (node_type) {
+ case NODE_DASGN_CURR: return NEW_DASGN_CURR(id, val, loc);
+ case NODE_DASGN: return NEW_DASGN(id, val, loc);
+ case NODE_LASGN: return NEW_LASGN(id, val, loc);
+ case NODE_GASGN: return NEW_GASGN(id, val, loc);
+ case NODE_IASGN: return NEW_IASGN(id, val, loc);
+ case NODE_CDECL: return NEW_CDECL(id, val, 0, loc);
+ case NODE_CVASGN: return NEW_CVASGN(id, val, loc);
+ }
+ if (err) yyerror1(loc, err);
+ return NEW_BEGIN(0, loc);
+}
+#else
+static VALUE
+assignable(struct parser_params *p, VALUE lhs)
+{
+ const char *err = 0;
+ assignable0(p, get_id(lhs), &err);
+ if (err) lhs = assign_error(p, lhs);
+ return lhs;
}
+#endif
static int
is_private_local_id(ID name)
@@ -9314,102 +9265,124 @@ is_private_local_id(ID name)
return RSTRING_PTR(s)[0] == '_';
}
-#define LVAR_USED ((ID)1 << (sizeof(ID) * CHAR_BIT - 1))
-
static int
-shadowing_lvar_0(struct parser_params *parser, ID name)
+shadowing_lvar_0(struct parser_params *p, ID name)
{
if (is_private_local_id(name)) return 1;
- if (dyna_in_block()) {
- if (dvar_curr(name)) {
- yyerror("duplicated argument name");
- }
- else if (dvar_defined_get(name) || local_id(name)) {
- rb_warning1("shadowing outer local variable - %"PRIsWARN, rb_id2str(name));
- vtable_add(lvtbl->vars, name);
- if (lvtbl->used) {
- vtable_add(lvtbl->used, (ID)ruby_sourceline | LVAR_USED);
+ if (dyna_in_block(p)) {
+ if (dvar_curr(p, name)) {
+ yyerror0("duplicated argument name");
+ }
+ else if (dvar_defined(p, name) || local_id(p, name)) {
+ vtable_add(p->lvtbl->vars, name);
+ if (p->lvtbl->used) {
+ vtable_add(p->lvtbl->used, (ID)p->ruby_sourceline | LVAR_USED);
}
return 0;
}
}
else {
- if (local_id(name)) {
- yyerror("duplicated argument name");
+ if (local_id(p, name)) {
+ yyerror0("duplicated argument name");
}
}
return 1;
}
static ID
-shadowing_lvar_gen(struct parser_params *parser, ID name)
+shadowing_lvar(struct parser_params *p, ID name)
{
- shadowing_lvar_0(parser, name);
+ shadowing_lvar_0(p, name);
return name;
}
static void
-new_bv_gen(struct parser_params *parser, ID name)
+new_bv(struct parser_params *p, ID name)
{
if (!name) return;
if (!is_local_id(name)) {
- compile_error(PARSER_ARG "invalid local variable - %"PRIsVALUE,
+ compile_error(p, "invalid local variable - %"PRIsVALUE,
rb_id2str(name));
return;
}
- if (!shadowing_lvar_0(parser, name)) return;
- dyna_var(name);
+ if (!shadowing_lvar_0(p, name)) return;
+ dyna_var(p, name);
}
#ifndef RIPPER
static NODE *
-aryset_gen(struct parser_params *parser, NODE *recv, NODE *idx)
+aryset(struct parser_params *p, NODE *recv, NODE *idx, const YYLTYPE *loc)
{
- return NEW_ATTRASGN(recv, tASET, idx);
+ return NEW_ATTRASGN(recv, tASET, idx, loc);
}
static void
-block_dup_check_gen(struct parser_params *parser, NODE *node1, NODE *node2)
+block_dup_check(struct parser_params *p, NODE *node1, NODE *node2)
{
if (node2 && node1 && nd_type(node1) == NODE_BLOCK_PASS) {
- compile_error(PARSER_ARG "both block arg and actual block given");
+ compile_error(p, "both block arg and actual block given");
}
}
static NODE *
-attrset_gen(struct parser_params *parser, NODE *recv, ID atype, ID id)
+attrset(struct parser_params *p, NODE *recv, ID atype, ID id, const YYLTYPE *loc)
{
if (!CALL_Q_P(atype)) id = rb_id_attrset(id);
- return NEW_ATTRASGN(recv, id, 0);
+ return NEW_ATTRASGN(recv, id, 0, loc);
}
static void
-rb_backref_error_gen(struct parser_params *parser, NODE *node)
+rb_backref_error(struct parser_params *p, NODE *node)
{
switch (nd_type(node)) {
case NODE_NTH_REF:
- compile_error(PARSER_ARG "Can't set variable $%ld", node->nd_nth);
+ compile_error(p, "Can't set variable $%ld", node->nd_nth);
break;
case NODE_BACK_REF:
- compile_error(PARSER_ARG "Can't set variable $%c", (int)node->nd_nth);
+ compile_error(p, "Can't set variable $%c", (int)node->nd_nth);
break;
}
}
static NODE *
-arg_concat_gen(struct parser_params *parser, NODE *node1, NODE *node2)
+arg_append(struct parser_params *p, NODE *node1, NODE *node2, const YYLTYPE *loc)
+{
+ if (!node1) return NEW_LIST(node2, &node2->nd_loc);
+ switch (nd_type(node1)) {
+ case NODE_ARRAY:
+ return list_append(p, node1, node2);
+ case NODE_BLOCK_PASS:
+ node1->nd_head = arg_append(p, node1->nd_head, node2, loc);
+ node1->nd_loc.end_pos = node1->nd_head->nd_loc.end_pos;
+ return node1;
+ case NODE_ARGSPUSH:
+ node1->nd_body = list_append(p, NEW_LIST(node1->nd_body, &node1->nd_body->nd_loc), node2);
+ node1->nd_loc.end_pos = node1->nd_body->nd_loc.end_pos;
+ nd_set_type(node1, NODE_ARGSCAT);
+ return node1;
+ case NODE_ARGSCAT:
+ if (nd_type(node1->nd_body) != NODE_ARRAY) break;
+ node1->nd_body = list_append(p, node1->nd_body, node2);
+ node1->nd_loc.end_pos = node1->nd_body->nd_loc.end_pos;
+ return node1;
+ }
+ return NEW_ARGSPUSH(node1, node2, loc);
+}
+
+static NODE *
+arg_concat(struct parser_params *p, NODE *node1, NODE *node2, const YYLTYPE *loc)
{
if (!node2) return node1;
switch (nd_type(node1)) {
case NODE_BLOCK_PASS:
if (node1->nd_head)
- node1->nd_head = arg_concat(node1->nd_head, node2);
+ node1->nd_head = arg_concat(p, node1->nd_head, node2, loc);
else
- node1->nd_head = NEW_LIST(node2);
+ node1->nd_head = NEW_LIST(node2, loc);
return node1;
case NODE_ARGSPUSH:
if (nd_type(node2) != NODE_ARRAY) break;
- node1->nd_body = list_concat(NEW_LIST(node1->nd_body), node2);
+ node1->nd_body = list_concat(NEW_LIST(node1->nd_body, loc), node2);
nd_set_type(node1, NODE_ARGSCAT);
return node1;
case NODE_ARGSCAT:
@@ -9418,25 +9391,27 @@ arg_concat_gen(struct parser_params *parser, NODE *node1, NODE *node2)
node1->nd_body = list_concat(node1->nd_body, node2);
return node1;
}
- return NEW_ARGSCAT(node1, node2);
+ return NEW_ARGSCAT(node1, node2, loc);
}
static NODE *
-arg_append_gen(struct parser_params *parser, NODE *node1, NODE *node2)
+last_arg_append(struct parser_params *p, NODE *args, NODE *last_arg, const YYLTYPE *loc)
{
- if (!node1) return NEW_LIST(node2);
- switch (nd_type(node1)) {
- case NODE_ARRAY:
- return list_append(node1, node2);
- case NODE_BLOCK_PASS:
- node1->nd_head = arg_append(node1->nd_head, node2);
- return node1;
- case NODE_ARGSPUSH:
- node1->nd_body = list_append(NEW_LIST(node1->nd_body), node2);
- nd_set_type(node1, NODE_ARGSCAT);
- return node1;
+ NODE *n1;
+ if ((n1 = splat_array(args)) != 0) {
+ return list_append(p, n1, last_arg);
+ }
+ return arg_append(p, args, last_arg, loc);
+}
+
+static NODE *
+rest_arg_append(struct parser_params *p, NODE *args, NODE *rest_arg, const YYLTYPE *loc)
+{
+ NODE *n1;
+ if ((nd_type(rest_arg) == NODE_ARRAY) && (n1 = splat_array(args)) != 0) {
+ return list_concat(n1, rest_arg);
}
- return NEW_ARGSPUSH(node1, node2);
+ return arg_concat(p, args, rest_arg, loc);
}
static NODE *
@@ -9447,15 +9422,41 @@ splat_array(NODE* node)
return 0;
}
+static void
+mark_lvar_used(struct parser_params *p, NODE *rhs)
+{
+ ID *vidp = NULL;
+ if (!rhs) return;
+ switch (nd_type(rhs)) {
+ case NODE_LASGN:
+ if (local_id_ref(p, rhs->nd_vid, &vidp)) {
+ if (vidp) *vidp |= LVAR_USED;
+ }
+ break;
+ case NODE_DASGN:
+ case NODE_DASGN_CURR:
+ if (dvar_defined_ref(p, rhs->nd_vid, &vidp)) {
+ if (vidp) *vidp |= LVAR_USED;
+ }
+ break;
+#if 0
+ case NODE_MASGN:
+ for (rhs = rhs->nd_head; rhs; rhs = rhs->nd_next) {
+ mark_lvar_used(p, rhs->nd_head);
+ }
+ break;
+#endif
+ }
+}
+
static NODE *
-node_assign_gen(struct parser_params *parser, NODE *lhs, NODE *rhs)
+node_assign(struct parser_params *p, NODE *lhs, NODE *rhs, const YYLTYPE *loc)
{
if (!lhs) return 0;
switch (nd_type(lhs)) {
case NODE_GASGN:
case NODE_IASGN:
- case NODE_IASGN2:
case NODE_LASGN:
case NODE_DASGN:
case NODE_DASGN_CURR:
@@ -9463,11 +9464,12 @@ node_assign_gen(struct parser_params *parser, NODE *lhs, NODE *rhs)
case NODE_CDECL:
case NODE_CVASGN:
lhs->nd_value = rhs;
+ nd_set_loc(lhs, loc);
break;
case NODE_ATTRASGN:
- case NODE_CALL:
- lhs->nd_args = arg_append(lhs->nd_args, rhs);
+ lhs->nd_args = arg_append(p, lhs->nd_args, rhs, loc);
+ nd_set_loc(lhs, loc);
break;
default:
@@ -9478,10 +9480,10 @@ node_assign_gen(struct parser_params *parser, NODE *lhs, NODE *rhs)
return lhs;
}
-static int
-value_expr_gen(struct parser_params *parser, NODE *node)
+static NODE *
+value_expr_check(struct parser_params *p, NODE *node)
{
- int cond = 0;
+ NODE *void_node = 0, *vn;
if (!node) {
rb_warning0("empty expression");
@@ -9493,9 +9495,7 @@ value_expr_gen(struct parser_params *parser, NODE *node)
case NODE_NEXT:
case NODE_REDO:
case NODE_RETRY:
- if (!cond) yyerror("void value expression");
- /* or "control never reach"? */
- return FALSE;
+ return void_node ? void_node : node;
case NODE_BLOCK:
while (node->nd_next) {
@@ -9509,6 +9509,7 @@ value_expr_gen(struct parser_params *parser, NODE *node)
break;
case NODE_IF:
+ case NODE_UNLESS:
if (!node->nd_body) {
node = node->nd_else;
break;
@@ -9517,34 +9518,54 @@ value_expr_gen(struct parser_params *parser, NODE *node)
node = node->nd_body;
break;
}
- if (!value_expr(node->nd_body)) return FALSE;
+ vn = value_expr_check(p, node->nd_body);
+ if (!vn) return NULL;
+ if (!void_node) void_node = vn;
node = node->nd_else;
break;
case NODE_AND:
case NODE_OR:
- cond = 1;
- node = node->nd_2nd;
+ node = node->nd_1st;
break;
+ case NODE_LASGN:
+ case NODE_DASGN:
+ case NODE_DASGN_CURR:
+ case NODE_MASGN:
+ mark_lvar_used(p, node);
+ return NULL;
+
default:
- return TRUE;
+ return NULL;
}
}
+ return NULL;
+}
+
+static int
+value_expr_gen(struct parser_params *p, NODE *node)
+{
+ NODE *void_node = value_expr_check(p, node);
+ if (void_node) {
+ yyerror1(&void_node->nd_loc, "void value expression");
+ /* or "control never reach"? */
+ return FALSE;
+ }
return TRUE;
}
static void
-void_expr_gen(struct parser_params *parser, NODE *node)
+void_expr(struct parser_params *p, NODE *node)
{
const char *useless = 0;
if (!RTEST(ruby_verbose)) return;
- if (!node) return;
+ if (!node || !(node = nd_once_body(node))) return;
switch (nd_type(node)) {
- case NODE_CALL:
+ case NODE_OPCALL:
switch (node->nd_mid) {
case '+':
case '-':
@@ -9585,7 +9606,6 @@ void_expr_gen(struct parser_params *parser, NODE *node)
case NODE_STR:
case NODE_DSTR:
case NODE_DREGX:
- case NODE_DREGX_ONCE:
useless = "a literal";
break;
case NODE_COLON2:
@@ -9620,18 +9640,19 @@ void_expr_gen(struct parser_params *parser, NODE *node)
}
}
-static void
-void_stmts_gen(struct parser_params *parser, NODE *node)
+static NODE *
+void_stmts(struct parser_params *p, NODE *node)
{
- if (!RTEST(ruby_verbose)) return;
- if (!node) return;
- if (nd_type(node) != NODE_BLOCK) return;
+ NODE *const n = node;
+ if (!RTEST(ruby_verbose)) return n;
+ if (!node) return n;
+ if (nd_type(node) != NODE_BLOCK) return n;
- for (;;) {
- if (!node->nd_next) return;
- void_expr0(node->nd_head);
+ while (node->nd_next) {
+ void_expr(p, node->nd_head);
node = node->nd_next;
}
+ return n;
}
static NODE *
@@ -9655,18 +9676,18 @@ remove_begin_all(NODE *node)
}
static void
-reduce_nodes_gen(struct parser_params *parser, NODE **body)
+reduce_nodes(struct parser_params *p, NODE **body)
{
NODE *node = *body;
if (!node) {
- *body = NEW_NIL();
+ *body = NEW_NIL(&NULL_LOC);
return;
}
#define subnodes(n1, n2) \
((!node->n1) ? (node->n2 ? (body = &node->n2, 1) : 0) : \
(!node->n2) ? (body = &node->n1, 1) : \
- (reduce_nodes(&node->n1), body = &node->n2, 1))
+ (reduce_nodes(p, &node->n1), body = &node->n2, 1))
while (node) {
int newline = (int)(node->flags & NODE_FL_NEWLINE);
@@ -9687,6 +9708,7 @@ reduce_nodes_gen(struct parser_params *parser, NODE **body)
body = &node->nd_end->nd_head;
break;
case NODE_IF:
+ case NODE_UNLESS:
if (subnodes(nd_body, nd_else)) break;
return;
case NODE_CASE:
@@ -9740,7 +9762,7 @@ is_static_content(NODE *node)
}
static int
-assign_in_cond(struct parser_params *parser, NODE *node)
+assign_in_cond(struct parser_params *p, NODE *node)
{
switch (nd_type(node)) {
case NODE_MASGN:
@@ -9758,27 +9780,27 @@ assign_in_cond(struct parser_params *parser, NODE *node)
if (!node->nd_value) return 1;
if (is_static_content(node->nd_value)) {
/* reports always */
- parser_warn(node->nd_value, "found = in conditional, should be ==");
+ parser_warn(p, node->nd_value, "found `= literal' in conditional, should be ==");
}
return 1;
}
static void
-warn_unless_e_option(struct parser_params *parser, NODE *node, const char *str)
+warn_unless_e_option(struct parser_params *p, NODE *node, const char *str)
{
- if (!e_option_supplied(parser)) parser_warn(node, str);
+ if (!e_option_supplied(p)) parser_warn(p, node, str);
}
static void
-warning_unless_e_option(struct parser_params *parser, NODE *node, const char *str)
+warning_unless_e_option(struct parser_params *p, NODE *node, const char *str)
{
- if (!e_option_supplied(parser)) parser_warning(node, str);
+ if (!e_option_supplied(p)) parser_warning(p, node, str);
}
-static NODE *cond0(struct parser_params*,NODE*,int);
+static NODE *cond0(struct parser_params*,NODE*,int,const YYLTYPE*);
static NODE*
-range_op(struct parser_params *parser, NODE *node)
+range_op(struct parser_params *p, NODE *node, const YYLTYPE *loc)
{
enum node_type type;
@@ -9787,23 +9809,23 @@ range_op(struct parser_params *parser, NODE *node)
type = nd_type(node);
value_expr(node);
if (type == NODE_LIT && FIXNUM_P(node->nd_lit)) {
- warn_unless_e_option(parser, node, "integer literal in conditional range");
- return NEW_CALL(node, tEQ, NEW_LIST(NEW_GVAR(rb_intern("$."))));
+ warn_unless_e_option(p, node, "integer literal in conditional range");
+ return NEW_CALL(node, tEQ, NEW_LIST(NEW_GVAR(rb_intern("$."), loc), loc), loc);
}
- return cond0(parser, node, FALSE);
+ return cond0(p, node, FALSE, loc);
}
static int
literal_node(NODE *node)
{
if (!node) return 1; /* same as NODE_NIL */
+ if (!(node = nd_once_body(node))) return 1;
switch (nd_type(node)) {
case NODE_LIT:
case NODE_STR:
case NODE_DSTR:
case NODE_EVSTR:
case NODE_DREGX:
- case NODE_DREGX_ONCE:
case NODE_DSYM:
return 2;
case NODE_TRUE:
@@ -9815,10 +9837,11 @@ literal_node(NODE *node)
}
static NODE*
-cond0(struct parser_params *parser, NODE *node, int method_op)
+cond0(struct parser_params *p, NODE *node, int method_op, const YYLTYPE *loc)
{
if (node == 0) return 0;
- assign_in_cond(parser, node);
+ if (!(node = nd_once_body(node))) return 0;
+ assign_in_cond(p, node);
switch (nd_type(node)) {
case NODE_DSTR:
@@ -9828,45 +9851,51 @@ cond0(struct parser_params *parser, NODE *node, int method_op)
break;
case NODE_DREGX:
- case NODE_DREGX_ONCE:
- if (!method_op)
- warning_unless_e_option(parser, node, "regex literal in condition");
- return NEW_MATCH2(node, NEW_GVAR(idLASTLINE));
+ {
+ if (!method_op)
+ warning_unless_e_option(p, node, "regex literal in condition");
+
+ return NEW_MATCH2(node, NEW_GVAR(idLASTLINE, loc), loc);
+ }
case NODE_AND:
case NODE_OR:
- node->nd_1st = cond0(parser, node->nd_1st, FALSE);
- node->nd_2nd = cond0(parser, node->nd_2nd, FALSE);
+ node->nd_1st = cond0(p, node->nd_1st, FALSE, loc);
+ node->nd_2nd = cond0(p, node->nd_2nd, FALSE, loc);
break;
case NODE_DOT2:
case NODE_DOT3:
- node->nd_beg = range_op(parser, node->nd_beg);
- node->nd_end = range_op(parser, node->nd_end);
+ node->nd_beg = range_op(p, node->nd_beg, loc);
+ node->nd_end = range_op(p, node->nd_end, loc);
if (nd_type(node) == NODE_DOT2) nd_set_type(node,NODE_FLIP2);
else if (nd_type(node) == NODE_DOT3) nd_set_type(node, NODE_FLIP3);
- if (!method_op && !e_option_supplied(parser)) {
+ if (!method_op && !e_option_supplied(p)) {
int b = literal_node(node->nd_beg);
int e = literal_node(node->nd_end);
if ((b == 1 && e == 1) || (b + e >= 2 && RTEST(ruby_verbose))) {
- parser_warn(node, "range literal in condition");
+ parser_warn(p, node, "range literal in condition");
}
}
break;
case NODE_DSYM:
- if (!method_op) parser_warning(node, "literal in condition");
+ if (!method_op) parser_warning(p, node, "literal in condition");
break;
case NODE_LIT:
if (RB_TYPE_P(node->nd_lit, T_REGEXP)) {
if (!method_op)
- warn_unless_e_option(parser, node, "regex literal in condition");
+ warn_unless_e_option(p, node, "regex literal in condition");
nd_set_type(node, NODE_MATCH);
}
+ else if (node->nd_lit == Qtrue ||
+ node->nd_lit == Qfalse) {
+ /* booleans are OK, e.g., while true */
+ }
else {
if (!method_op)
- parser_warning(node, "literal in condition");
+ parser_warning(p, node, "literal in condition");
}
default:
break;
@@ -9875,48 +9904,70 @@ cond0(struct parser_params *parser, NODE *node, int method_op)
}
static NODE*
-cond_gen(struct parser_params *parser, NODE *node, int method_op)
+cond(struct parser_params *p, NODE *node, const YYLTYPE *loc)
{
if (node == 0) return 0;
- return cond0(parser, node, method_op);
+ return cond0(p, node, FALSE, loc);
}
static NODE*
-new_if_gen(struct parser_params *parser, NODE *cc, NODE *left, NODE *right)
+method_cond(struct parser_params *p, NODE *node, const YYLTYPE *loc)
+{
+ if (node == 0) return 0;
+ return cond0(p, node, TRUE, loc);
+}
+
+static NODE*
+new_if(struct parser_params *p, NODE *cc, NODE *left, NODE *right, const YYLTYPE *loc)
{
if (!cc) return right;
- cc = cond0(parser, cc, FALSE);
- return newline_node(NEW_IF(cc, left, right));
+ cc = cond0(p, cc, FALSE, loc);
+ return newline_node(NEW_IF(cc, left, right, loc));
}
static NODE*
-logop_gen(struct parser_params *parser, enum node_type type, NODE *left, NODE *right)
+new_unless(struct parser_params *p, NODE *cc, NODE *left, NODE *right, const YYLTYPE *loc)
{
+ if (!cc) return right;
+ cc = cond0(p, cc, FALSE, loc);
+ return newline_node(NEW_UNLESS(cc, left, right, loc));
+}
+
+static NODE*
+logop(struct parser_params *p, ID id, NODE *left, NODE *right,
+ const YYLTYPE *op_loc, const YYLTYPE *loc)
+{
+ enum node_type type = id == idAND || id == idANDOP ? NODE_AND : NODE_OR;
+ NODE *op;
value_expr(left);
if (left && (enum node_type)nd_type(left) == type) {
NODE *node = left, *second;
while ((second = node->nd_2nd) != 0 && (enum node_type)nd_type(second) == type) {
node = second;
}
- node->nd_2nd = NEW_NODE(type, second, right, 0);
+ node->nd_2nd = NEW_NODE(type, second, right, 0, loc);
+ nd_set_line(node->nd_2nd, op_loc->beg_pos.lineno);
+ left->nd_loc.end_pos = loc->end_pos;
return left;
}
- return NEW_NODE(type, left, right, 0);
+ op = NEW_NODE(type, left, right, 0, loc);
+ nd_set_line(op, op_loc->beg_pos.lineno);
+ return op;
}
static void
-no_blockarg(struct parser_params *parser, NODE *node)
+no_blockarg(struct parser_params *p, NODE *node)
{
if (node && nd_type(node) == NODE_BLOCK_PASS) {
- compile_error(PARSER_ARG "block argument should not be given");
+ compile_error(p, "block argument should not be given");
}
}
static NODE *
-ret_args_gen(struct parser_params *parser, NODE *node)
+ret_args(struct parser_params *p, NODE *node)
{
if (node) {
- no_blockarg(parser, node);
+ no_blockarg(p, node);
if (nd_type(node) == NODE_ARRAY) {
if (node->nd_next == 0) {
node = node->nd_head;
@@ -9930,43 +9981,46 @@ ret_args_gen(struct parser_params *parser, NODE *node)
}
static NODE *
-new_yield_gen(struct parser_params *parser, NODE *node)
+new_yield(struct parser_params *p, NODE *node, const YYLTYPE *loc)
{
- if (node) no_blockarg(parser, node);
+ if (node) no_blockarg(p, node);
- return NEW_YIELD(node);
+ return NEW_YIELD(node, loc);
}
static VALUE
-negate_lit(VALUE lit)
+negate_lit(struct parser_params *p, VALUE lit)
{
- int type = TYPE(lit);
- switch (type) {
- case T_FIXNUM:
- lit = LONG2FIX(-FIX2LONG(lit));
- break;
+ if (FIXNUM_P(lit)) {
+ return LONG2FIX(-FIX2LONG(lit));
+ }
+ if (SPECIAL_CONST_P(lit)) {
+#if USE_FLONUM
+ if (FLONUM_P(lit)) {
+ return DBL2NUM(-RFLOAT_VALUE(lit));
+ }
+#endif
+ goto unknown;
+ }
+ switch (BUILTIN_TYPE(lit)) {
case T_BIGNUM:
BIGNUM_NEGATE(lit);
lit = rb_big_norm(lit);
break;
case T_RATIONAL:
- RRATIONAL_SET_NUM(lit, negate_lit(RRATIONAL(lit)->num));
+ RRATIONAL_SET_NUM(lit, negate_lit(p, RRATIONAL(lit)->num));
break;
case T_COMPLEX:
- RCOMPLEX_SET_REAL(lit, negate_lit(RCOMPLEX(lit)->real));
- RCOMPLEX_SET_IMAG(lit, negate_lit(RCOMPLEX(lit)->imag));
+ RCOMPLEX_SET_REAL(lit, negate_lit(p, RCOMPLEX(lit)->real));
+ RCOMPLEX_SET_IMAG(lit, negate_lit(p, RCOMPLEX(lit)->imag));
break;
case T_FLOAT:
-#if USE_FLONUM
- if (FLONUM_P(lit)) {
- lit = DBL2NUM(-RFLOAT_VALUE(lit));
- break;
- }
-#endif
RFLOAT(lit)->float_value = -RFLOAT_VALUE(lit);
break;
+ unknown:
default:
- rb_bug("unknown literal type (%d) passed to negate_lit", type);
+ rb_parser_fatal(p, "unknown literal type (%s) passed to negate_lit",
+ rb_builtin_class_name(lit));
break;
}
return lit;
@@ -9976,7 +10030,10 @@ static NODE *
arg_blk_pass(NODE *node1, NODE *node2)
{
if (node2) {
+ if (!node1) return node2;
node2->nd_head = node1;
+ nd_set_first_lineno(node2, nd_first_lineno(node1));
+ nd_set_first_column(node2, nd_first_column(node1));
return node2;
}
return node1;
@@ -9984,114 +10041,111 @@ arg_blk_pass(NODE *node1, NODE *node2)
static NODE*
-new_args_gen(struct parser_params *parser, NODE *m, NODE *o, ID r, NODE *p, NODE *tail)
+new_args(struct parser_params *p, NODE *pre_args, NODE *opt_args, ID rest_arg, NODE *post_args, NODE *tail, const YYLTYPE *loc)
{
- int saved_line = ruby_sourceline;
+ int saved_line = p->ruby_sourceline;
struct rb_args_info *args = tail->nd_ainfo;
- args->pre_args_num = m ? rb_long2int(m->nd_plen) : 0;
- args->pre_init = m ? m->nd_next : 0;
+ args->pre_args_num = pre_args ? rb_long2int(pre_args->nd_plen) : 0;
+ args->pre_init = pre_args ? pre_args->nd_next : 0;
- args->post_args_num = p ? rb_long2int(p->nd_plen) : 0;
- args->post_init = p ? p->nd_next : 0;
- args->first_post_arg = p ? p->nd_pid : 0;
+ args->post_args_num = post_args ? rb_long2int(post_args->nd_plen) : 0;
+ args->post_init = post_args ? post_args->nd_next : 0;
+ args->first_post_arg = post_args ? post_args->nd_pid : 0;
- args->rest_arg = r;
+ args->rest_arg = rest_arg;
- args->opt_args = o;
+ args->opt_args = opt_args;
- ruby_sourceline = saved_line;
+ p->ruby_sourceline = saved_line;
+ nd_set_loc(tail, loc);
return tail;
}
static NODE*
-new_args_tail_gen(struct parser_params *parser, NODE *k, ID kr, ID b)
+new_args_tail(struct parser_params *p, NODE *kw_args, ID kw_rest_arg, ID block, const YYLTYPE *loc)
{
- int saved_line = ruby_sourceline;
+ int saved_line = p->ruby_sourceline;
struct rb_args_info *args;
NODE *node;
+ rb_imemo_tmpbuf_t *tmpbuf = new_tmpbuf();
args = ZALLOC(struct rb_args_info);
- node = NEW_NODE(NODE_ARGS, 0, 0, args);
+ tmpbuf->ptr = (VALUE *)args;
+ node = NEW_NODE(NODE_ARGS, 0, 0, args, &NULL_LOC);
+ if (p->error_p) return node;
- args->block_arg = b;
- args->kw_args = k;
+ args->block_arg = block;
+ args->kw_args = kw_args;
- if (k) {
+ if (kw_args) {
/*
* def foo(k1: 1, kr1:, k2: 2, **krest, &b)
* variable order: k1, kr1, k2, &b, internal_id, krest
* #=> <reorder>
* variable order: kr1, k1, k2, internal_id, krest, &b
*/
- ID kw_bits;
- NODE *kwn = k;
- struct vtable *required_kw_vars = vtable_alloc(NULL);
- struct vtable *kw_vars = vtable_alloc(NULL);
- int i;
+ ID kw_bits = internal_id(p), *required_kw_vars, *kw_vars;
+ struct vtable *vtargs = p->lvtbl->args;
+ NODE *kwn = kw_args;
+ vtable_pop(vtargs, !!block + !!kw_rest_arg);
+ required_kw_vars = kw_vars = &vtargs->tbl[vtargs->pos];
while (kwn) {
- NODE *val_node = kwn->nd_body->nd_value;
- ID vid = kwn->nd_body->nd_vid;
+ if (!NODE_REQUIRED_KEYWORD_P(kwn->nd_body))
+ --kw_vars;
+ --required_kw_vars;
+ kwn = kwn->nd_next;
+ }
- if (val_node == (NODE *)-1) {
- vtable_add(required_kw_vars, vid);
+ for (kwn = kw_args; kwn; kwn = kwn->nd_next) {
+ ID vid = kwn->nd_body->nd_vid;
+ if (NODE_REQUIRED_KEYWORD_P(kwn->nd_body)) {
+ *required_kw_vars++ = vid;
}
else {
- vtable_add(kw_vars, vid);
+ *kw_vars++ = vid;
}
-
- kwn = kwn->nd_next;
}
- kw_bits = internal_id();
- if (kr && is_junk_id(kr)) vtable_pop(lvtbl->args, 1);
- vtable_pop(lvtbl->args, vtable_size(required_kw_vars) + vtable_size(kw_vars) + (b != 0));
-
- for (i=0; i<vtable_size(required_kw_vars); i++) arg_var(required_kw_vars->tbl[i]);
- for (i=0; i<vtable_size(kw_vars); i++) arg_var(kw_vars->tbl[i]);
- vtable_free(required_kw_vars);
- vtable_free(kw_vars);
+ arg_var(p, kw_bits);
+ if (kw_rest_arg) arg_var(p, kw_rest_arg);
+ if (block) arg_var(p, block);
- arg_var(kw_bits);
- if (kr) arg_var(kr);
- if (b) arg_var(b);
-
- args->kw_rest_arg = NEW_DVAR(kw_bits);
- args->kw_rest_arg->nd_cflag = kr;
+ args->kw_rest_arg = NEW_DVAR(kw_rest_arg, loc);
+ args->kw_rest_arg->nd_cflag = kw_bits;
}
- else if (kr) {
- if (b) vtable_pop(lvtbl->args, 1); /* reorder */
- arg_var(kr);
- if (b) arg_var(b);
- args->kw_rest_arg = NEW_DVAR(kr);
+ else if (kw_rest_arg) {
+ args->kw_rest_arg = NEW_DVAR(kw_rest_arg, loc);
}
- ruby_sourceline = saved_line;
+ p->ruby_sourceline = saved_line;
return node;
}
static NODE*
-dsym_node_gen(struct parser_params *parser, NODE *node)
+dsym_node(struct parser_params *p, NODE *node, const YYLTYPE *loc)
{
VALUE lit;
if (!node) {
- return NEW_LIT(ID2SYM(idNULL));
+ return NEW_LIT(ID2SYM(idNULL), loc);
}
switch (nd_type(node)) {
case NODE_DSTR:
nd_set_type(node, NODE_DSYM);
+ nd_set_loc(node, loc);
break;
case NODE_STR:
lit = node->nd_lit;
- node->nd_lit = ID2SYM(rb_intern_str(lit));
+ add_mark_object(p, node->nd_lit = ID2SYM(rb_intern_str(lit)));
nd_set_type(node, NODE_LIT);
+ nd_set_loc(node, loc);
break;
default:
- node = NEW_NODE(NODE_DSYM, Qnil, 1, NEW_LIST(node));
+ node = NEW_NODE(NODE_DSYM, Qnil, 1, NEW_LIST(node, loc), loc);
break;
}
return node;
@@ -10113,7 +10167,7 @@ append_literal_keys(st_data_t k, st_data_t v, st_data_t h)
}
static NODE *
-remove_duplicate_keys(struct parser_params *parser, NODE *hash)
+remove_duplicate_keys(struct parser_params *p, NODE *hash)
{
st_table *literal_keys = st_init_numtable_with_size(hash->nd_alen / 2);
NODE *result = 0;
@@ -10125,11 +10179,11 @@ remove_duplicate_keys(struct parser_params *parser, NODE *hash)
st_data_t data;
if (nd_type(head) == NODE_LIT &&
st_lookup(literal_keys, (key = head->nd_lit), &data)) {
- rb_compile_warn(ruby_sourcefile, nd_line((NODE *)data),
+ rb_compile_warn(p->ruby_sourcefile, nd_line((NODE *)data),
"key %+"PRIsVALUE" is duplicated and overwritten on line %d",
head->nd_lit, nd_line(head));
head = ((NODE *)data)->nd_next;
- head->nd_head = block_append(head->nd_head, value->nd_head);
+ head->nd_head = block_append(p, head->nd_head, value->nd_head);
}
else {
st_insert(literal_keys, (st_data_t)key, (st_data_t)hash);
@@ -10146,24 +10200,26 @@ remove_duplicate_keys(struct parser_params *parser, NODE *hash)
}
static NODE *
-new_hash_gen(struct parser_params *parser, NODE *hash)
+new_hash(struct parser_params *p, NODE *hash, const YYLTYPE *loc)
{
- if (hash) hash = remove_duplicate_keys(parser, hash);
- return NEW_HASH(hash);
+ if (hash) hash = remove_duplicate_keys(p, hash);
+ return NEW_HASH(hash, loc);
}
#endif /* !RIPPER */
#ifndef RIPPER
static NODE *
-new_op_assign_gen(struct parser_params *parser, NODE *lhs, ID op, NODE *rhs)
+new_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, const YYLTYPE *loc)
{
NODE *asgn;
if (lhs) {
ID vid = lhs->nd_vid;
+ YYLTYPE lhs_loc = lhs->nd_loc;
if (op == tOROP) {
lhs->nd_value = rhs;
- asgn = NEW_OP_ASGN_OR(gettable(vid), lhs);
+ nd_set_loc(lhs, loc);
+ asgn = NEW_OP_ASGN_OR(gettable(p, vid, &lhs_loc), lhs, loc);
if (is_notop_id(vid)) {
switch (id_type(vid)) {
case ID_GLOBAL:
@@ -10175,99 +10231,124 @@ new_op_assign_gen(struct parser_params *parser, NODE *lhs, ID op, NODE *rhs)
}
else if (op == tANDOP) {
lhs->nd_value = rhs;
- asgn = NEW_OP_ASGN_AND(gettable(vid), lhs);
+ nd_set_loc(lhs, loc);
+ asgn = NEW_OP_ASGN_AND(gettable(p, vid, &lhs_loc), lhs, loc);
}
else {
asgn = lhs;
- asgn->nd_value = NEW_CALL(gettable(vid), op, NEW_LIST(rhs));
+ asgn->nd_value = NEW_CALL(gettable(p, vid, &lhs_loc), op, NEW_LIST(rhs, &rhs->nd_loc), loc);
+ nd_set_loc(asgn, loc);
}
}
else {
- asgn = NEW_BEGIN(0);
+ asgn = NEW_BEGIN(0, loc);
}
return asgn;
}
static NODE *
-new_attr_op_assign_gen(struct parser_params *parser, NODE *lhs,
- ID atype, ID attr, ID op, NODE *rhs)
+new_ary_op_assign(struct parser_params *p, NODE *ary,
+ NODE *args, ID op, NODE *rhs, const YYLTYPE *args_loc, const YYLTYPE *loc)
{
NODE *asgn;
- if (op == tOROP) {
- op = 0;
+ args = make_array(args, args_loc);
+ if (nd_type(args) == NODE_BLOCK_PASS) {
+ args = NEW_ARGSCAT(args, rhs, loc);
}
- else if (op == tANDOP) {
- op = 1;
+ else {
+ args = arg_concat(p, args, rhs, loc);
}
- asgn = NEW_OP_ASGN2(lhs, CALL_Q_P(atype), attr, op, rhs);
+ asgn = NEW_OP_ASGN1(ary, op, args, loc);
+ fixpos(asgn, ary);
+ return asgn;
+}
+
+static NODE *
+new_attr_op_assign(struct parser_params *p, NODE *lhs,
+ ID atype, ID attr, ID op, NODE *rhs, const YYLTYPE *loc)
+{
+ NODE *asgn;
+
+ asgn = NEW_OP_ASGN2(lhs, CALL_Q_P(atype), attr, op, rhs, loc);
fixpos(asgn, lhs);
return asgn;
}
static NODE *
-new_const_op_assign_gen(struct parser_params *parser, NODE *lhs, ID op, NODE *rhs)
+new_const_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, const YYLTYPE *loc)
{
NODE *asgn;
- if (op == tOROP) {
- op = 0;
- }
- else if (op == tANDOP) {
- op = 1;
- }
if (lhs) {
- asgn = NEW_OP_CDECL(lhs, op, rhs);
+ asgn = NEW_OP_CDECL(lhs, op, rhs, loc);
}
else {
- asgn = NEW_BEGIN(0);
+ asgn = NEW_BEGIN(0, loc);
}
fixpos(asgn, lhs);
return asgn;
}
static NODE *
-const_decl_gen(struct parser_params *parser, NODE *path)
+const_decl(struct parser_params *p, NODE *path, const YYLTYPE *loc)
{
- if (in_def || in_single) {
- yyerror("dynamic constant assignment");
+ if (p->in_def) {
+ yyerror1(loc, "dynamic constant assignment");
}
- return NEW_CDECL(0, 0, (path));
+ return NEW_CDECL(0, 0, (path), loc);
}
#else
static VALUE
-new_op_assign_gen(struct parser_params *parser, VALUE lhs, VALUE op, VALUE rhs)
+const_decl(struct parser_params *p, VALUE path)
{
- return dispatch3(opassign, lhs, op, rhs);
+ if (p->in_def) {
+ path = dispatch1(assign_error, path);
+ ripper_error(p);
+ }
+ return path;
}
static VALUE
-new_attr_op_assign_gen(struct parser_params *parser, VALUE lhs, VALUE type, VALUE attr, VALUE op, VALUE rhs)
+assign_error(struct parser_params *p, VALUE a)
{
- VALUE recv = dispatch3(field, lhs, type, attr);
- return dispatch3(opassign, recv, op, rhs);
+ a = dispatch1(assign_error, a);
+ ripper_error(p);
+ return a;
}
static VALUE
-const_decl_gen(struct parser_params *parser, VALUE path)
+var_field(struct parser_params *p, VALUE a)
{
- if (in_def || in_single) {
- assign_error(path);
- }
- return path;
+ return ripper_new_yylval(p, get_id(a), dispatch1(var_field, a), 0);
}
+#endif
-static VALUE
-assign_error_gen(struct parser_params *parser, VALUE a)
+#ifndef RIPPER
+static NODE *
+new_bodystmt(struct parser_params *p, NODE *head, NODE *rescue, NODE *rescue_else, NODE *ensure, const YYLTYPE *loc)
{
- a = dispatch1(assign_error, a);
- ripper_error();
- return a;
+ NODE *result = head;
+ if (rescue) {
+ NODE *tmp = rescue_else ? rescue_else : rescue;
+ YYLTYPE rescue_loc = code_loc_gen(&head->nd_loc, &tmp->nd_loc);
+
+ result = NEW_RESCUE(head, rescue, rescue_else, &rescue_loc);
+ nd_set_line(result, rescue->nd_loc.beg_pos.lineno);
+ }
+ else if (rescue_else) {
+ result = block_append(p, result, rescue_else);
+ }
+ if (ensure) {
+ result = NEW_ENSURE(result, ensure, loc);
+ }
+ fixpos(result, head);
+ return result;
}
#endif
static void
-warn_unused_var(struct parser_params *parser, struct local_vars *local)
+warn_unused_var(struct parser_params *p, struct local_vars *local)
{
int i, cnt;
ID *v, *u;
@@ -10277,7 +10358,7 @@ warn_unused_var(struct parser_params *parser, struct local_vars *local)
u = local->used->tbl;
cnt = local->used->pos;
if (cnt != local->vars->pos) {
- rb_bug("local->used->pos != local->vars->pos");
+ rb_parser_fatal(p, "local->used->pos != local->vars->pos");
}
for (i = 0; i < cnt; ++i) {
if (!v[i] || (u[i] & LVAR_USED)) continue;
@@ -10287,136 +10368,151 @@ warn_unused_var(struct parser_params *parser, struct local_vars *local)
}
static void
-local_push_gen(struct parser_params *parser, int inherit_dvars)
+local_push(struct parser_params *p, int toplevel_scope)
{
struct local_vars *local;
+ int inherits_dvars = toplevel_scope && (compile_for_eval || p->in_main /* is p->in_main really needed? */);
+ int warn_unused_vars = RTEST(ruby_verbose);
local = ALLOC(struct local_vars);
- local->prev = lvtbl;
+ local->prev = p->lvtbl;
local->args = vtable_alloc(0);
- local->vars = vtable_alloc(inherit_dvars ? DVARS_INHERIT : DVARS_TOPSCOPE);
- local->used = !(inherit_dvars &&
- (ifndef_ripper(compile_for_eval || e_option_supplied(parser))+0)) &&
- RTEST(ruby_verbose) ? vtable_alloc(0) : 0;
+ local->vars = vtable_alloc(inherits_dvars ? DVARS_INHERIT : DVARS_TOPSCOPE);
+#ifndef RIPPER
+ if (toplevel_scope && compile_for_eval) warn_unused_vars = 0;
+ if (toplevel_scope && e_option_supplied(p)) warn_unused_vars = 0;
+#endif
+ local->used = warn_unused_vars ? vtable_alloc(0) : 0;
+
# if WARN_PAST_SCOPE
local->past = 0;
# endif
- local->cmdargs = cmdarg_stack;
- CMDARG_SET(0);
- lvtbl = local;
+ CMDARG_PUSH(0);
+ COND_PUSH(0);
+ p->lvtbl = local;
}
static void
-local_pop_gen(struct parser_params *parser)
+local_pop(struct parser_params *p)
{
- struct local_vars *local = lvtbl->prev;
- if (lvtbl->used) {
- warn_unused_var(parser, lvtbl);
- vtable_free(lvtbl->used);
+ struct local_vars *local = p->lvtbl->prev;
+ if (p->lvtbl->used) {
+ warn_unused_var(p, p->lvtbl);
+ vtable_free(p->lvtbl->used);
}
# if WARN_PAST_SCOPE
- while (lvtbl->past) {
- struct vtable *past = lvtbl->past;
- lvtbl->past = past->prev;
+ while (p->lvtbl->past) {
+ struct vtable *past = p->lvtbl->past;
+ p->lvtbl->past = past->prev;
vtable_free(past);
}
# endif
- vtable_free(lvtbl->args);
- vtable_free(lvtbl->vars);
- CMDARG_SET(lvtbl->cmdargs);
- xfree(lvtbl);
- lvtbl = local;
+ vtable_free(p->lvtbl->args);
+ vtable_free(p->lvtbl->vars);
+ CMDARG_POP();
+ COND_POP();
+ ruby_sized_xfree(p->lvtbl, sizeof(*p->lvtbl));
+ p->lvtbl = local;
}
#ifndef RIPPER
static ID*
-local_tbl_gen(struct parser_params *parser)
+local_tbl(struct parser_params *p)
{
- int cnt_args = vtable_size(lvtbl->args);
- int cnt_vars = vtable_size(lvtbl->vars);
+ int cnt_args = vtable_size(p->lvtbl->args);
+ int cnt_vars = vtable_size(p->lvtbl->vars);
int cnt = cnt_args + cnt_vars;
int i, j;
ID *buf;
+ rb_imemo_tmpbuf_t *tmpbuf = new_tmpbuf();
if (cnt <= 0) return 0;
buf = ALLOC_N(ID, cnt + 1);
- MEMCPY(buf+1, lvtbl->args->tbl, ID, cnt_args);
+ tmpbuf->ptr = (void *)buf;
+ MEMCPY(buf+1, p->lvtbl->args->tbl, ID, cnt_args);
/* remove IDs duplicated to warn shadowing */
for (i = 0, j = cnt_args+1; i < cnt_vars; ++i) {
- ID id = lvtbl->vars->tbl[i];
- if (!vtable_included(lvtbl->args, id)) {
+ ID id = p->lvtbl->vars->tbl[i];
+ if (!vtable_included(p->lvtbl->args, id)) {
buf[j++] = id;
}
}
- if (--j < cnt) REALLOC_N(buf, ID, (cnt = j) + 1);
+ if (--j < cnt) tmpbuf->ptr = (void *)REALLOC_N(buf, ID, (cnt = j) + 1);
buf[0] = cnt;
+
return buf;
}
#endif
static void
-arg_var_gen(struct parser_params *parser, ID id)
+arg_var(struct parser_params *p, ID id)
{
- vtable_add(lvtbl->args, id);
+ vtable_add(p->lvtbl->args, id);
}
static void
-local_var_gen(struct parser_params *parser, ID id)
+local_var(struct parser_params *p, ID id)
{
- vtable_add(lvtbl->vars, id);
- if (lvtbl->used) {
- vtable_add(lvtbl->used, (ID)ruby_sourceline);
+ vtable_add(p->lvtbl->vars, id);
+ if (p->lvtbl->used) {
+ vtable_add(p->lvtbl->used, (ID)p->ruby_sourceline);
}
}
static int
-local_id_gen(struct parser_params *parser, ID id)
+local_id_ref(struct parser_params *p, ID id, ID **vidrefp)
{
struct vtable *vars, *args, *used;
- vars = lvtbl->vars;
- args = lvtbl->args;
- used = lvtbl->used;
+ vars = p->lvtbl->vars;
+ args = p->lvtbl->args;
+ used = p->lvtbl->used;
- while (vars && POINTER_P(vars->prev)) {
+ while (vars && !DVARS_TERMINAL_P(vars->prev)) {
vars = vars->prev;
args = args->prev;
if (used) used = used->prev;
}
if (vars && vars->prev == DVARS_INHERIT) {
- return rb_local_defined(id, parser->base_block);
+ return rb_local_defined(id, p->base_block);
}
else if (vtable_included(args, id)) {
return 1;
}
else {
int i = vtable_included(vars, id);
- if (i && used) used->tbl[i-1] |= LVAR_USED;
+ if (i && used && vidrefp) *vidrefp = &used->tbl[i-1];
return i != 0;
}
}
+static int
+local_id(struct parser_params *p, ID id)
+{
+ return local_id_ref(p, id, NULL);
+}
+
static const struct vtable *
-dyna_push_gen(struct parser_params *parser)
+dyna_push(struct parser_params *p)
{
- lvtbl->args = vtable_alloc(lvtbl->args);
- lvtbl->vars = vtable_alloc(lvtbl->vars);
- if (lvtbl->used) {
- lvtbl->used = vtable_alloc(lvtbl->used);
+ p->lvtbl->args = vtable_alloc(p->lvtbl->args);
+ p->lvtbl->vars = vtable_alloc(p->lvtbl->vars);
+ if (p->lvtbl->used) {
+ p->lvtbl->used = vtable_alloc(p->lvtbl->used);
}
- return lvtbl->args;
+ return p->lvtbl->args;
}
static void
-dyna_pop_vtable(struct parser_params *parser, struct vtable **vtblp)
+dyna_pop_vtable(struct parser_params *p, struct vtable **vtblp)
{
struct vtable *tmp = *vtblp;
*vtblp = tmp->prev;
# if WARN_PAST_SCOPE
- if (parser->past_scope_enabled) {
- tmp->prev = lvtbl->past;
- lvtbl->past = tmp;
+ if (p->past_scope_enabled) {
+ tmp->prev = p->lvtbl->past;
+ p->lvtbl->past = tmp;
return;
}
# endif
@@ -10424,88 +10520,94 @@ dyna_pop_vtable(struct parser_params *parser, struct vtable **vtblp)
}
static void
-dyna_pop_1(struct parser_params *parser)
+dyna_pop_1(struct parser_params *p)
{
struct vtable *tmp;
- if ((tmp = lvtbl->used) != 0) {
- warn_unused_var(parser, lvtbl);
- lvtbl->used = lvtbl->used->prev;
+ if ((tmp = p->lvtbl->used) != 0) {
+ warn_unused_var(p, p->lvtbl);
+ p->lvtbl->used = p->lvtbl->used->prev;
vtable_free(tmp);
}
- dyna_pop_vtable(parser, &lvtbl->args);
- dyna_pop_vtable(parser, &lvtbl->vars);
+ dyna_pop_vtable(p, &p->lvtbl->args);
+ dyna_pop_vtable(p, &p->lvtbl->vars);
}
static void
-dyna_pop_gen(struct parser_params *parser, const struct vtable *lvargs)
+dyna_pop(struct parser_params *p, const struct vtable *lvargs)
{
- while (lvtbl->args != lvargs) {
- dyna_pop_1(parser);
- if (!lvtbl->args) {
- struct local_vars *local = lvtbl->prev;
- xfree(lvtbl);
- lvtbl = local;
+ while (p->lvtbl->args != lvargs) {
+ dyna_pop_1(p);
+ if (!p->lvtbl->args) {
+ struct local_vars *local = p->lvtbl->prev;
+ ruby_sized_xfree(p->lvtbl, sizeof(*p->lvtbl));
+ p->lvtbl = local;
}
}
- dyna_pop_1(parser);
+ dyna_pop_1(p);
}
static int
-dyna_in_block_gen(struct parser_params *parser)
+dyna_in_block(struct parser_params *p)
{
- return POINTER_P(lvtbl->vars) && lvtbl->vars->prev != DVARS_TOPSCOPE;
+ return !DVARS_TERMINAL_P(p->lvtbl->vars) && p->lvtbl->vars->prev != DVARS_TOPSCOPE;
}
static int
-dvar_defined_gen(struct parser_params *parser, ID id, int get)
+dvar_defined_ref(struct parser_params *p, ID id, ID **vidrefp)
{
struct vtable *vars, *args, *used;
int i;
- args = lvtbl->args;
- vars = lvtbl->vars;
- used = lvtbl->used;
+ args = p->lvtbl->args;
+ vars = p->lvtbl->vars;
+ used = p->lvtbl->used;
- while (POINTER_P(vars)) {
+ while (!DVARS_TERMINAL_P(vars)) {
if (vtable_included(args, id)) {
return 1;
}
if ((i = vtable_included(vars, id)) != 0) {
- if (used) used->tbl[i-1] |= LVAR_USED;
+ if (used && vidrefp) *vidrefp = &used->tbl[i-1];
return 1;
}
args = args->prev;
vars = vars->prev;
- if (get) used = 0;
+ if (!vidrefp) used = 0;
if (used) used = used->prev;
}
if (vars == DVARS_INHERIT) {
- return rb_dvar_defined(id, parser->base_block);
+ return rb_dvar_defined(id, p->base_block);
}
return 0;
}
static int
-dvar_curr_gen(struct parser_params *parser, ID id)
+dvar_defined(struct parser_params *p, ID id)
{
- return (vtable_included(lvtbl->args, id) ||
- vtable_included(lvtbl->vars, id));
+ return dvar_defined_ref(p, id, NULL);
+}
+
+static int
+dvar_curr(struct parser_params *p, ID id)
+{
+ return (vtable_included(p->lvtbl->args, id) ||
+ vtable_included(p->lvtbl->vars, id));
}
static void
-reg_fragment_enc_error(struct parser_params* parser, VALUE str, int c)
+reg_fragment_enc_error(struct parser_params* p, VALUE str, int c)
{
- compile_error(PARSER_ARG
+ compile_error(p,
"regexp encoding option '%c' differs from source encoding '%s'",
c, rb_enc_name(rb_enc_get(str)));
}
#ifndef RIPPER
int
-rb_reg_fragment_setenc(struct parser_params* parser, VALUE str, int options)
+rb_reg_fragment_setenc(struct parser_params* p, VALUE str, int options)
{
int c = RE_OPTION_ENCODING_IDX(options);
@@ -10526,7 +10628,7 @@ rb_reg_fragment_setenc(struct parser_params* parser, VALUE str, int options)
}
rb_enc_associate(str, rb_ascii8bit_encoding());
}
- else if (current_enc == rb_usascii_encoding()) {
+ else if (p->enc == rb_usascii_encoding()) {
if (rb_enc_str_coderange(str) != ENC_CODERANGE_7BIT) {
/* raise in re.c */
rb_enc_associate(str, rb_usascii_encoding());
@@ -10542,21 +10644,21 @@ rb_reg_fragment_setenc(struct parser_params* parser, VALUE str, int options)
}
static void
-reg_fragment_setenc_gen(struct parser_params* parser, VALUE str, int options)
+reg_fragment_setenc(struct parser_params* p, VALUE str, int options)
{
- int c = rb_reg_fragment_setenc(parser, str, options);
- if (c) reg_fragment_enc_error(parser, str, c);
+ int c = rb_reg_fragment_setenc(p, str, options);
+ if (c) reg_fragment_enc_error(p, str, c);
}
static int
-reg_fragment_check_gen(struct parser_params* parser, VALUE str, int options)
+reg_fragment_check(struct parser_params* p, VALUE str, int options)
{
VALUE err;
- reg_fragment_setenc(str, options);
+ reg_fragment_setenc(p, str, options);
err = rb_reg_check_preprocess(str);
if (err != Qnil) {
err = rb_obj_as_string(err);
- compile_error(PARSER_ARG "%"PRIsVALUE, err);
+ compile_error(p, "%"PRIsVALUE, err);
return 0;
}
return 1;
@@ -10566,6 +10668,7 @@ typedef struct {
struct parser_params* parser;
rb_encoding *enc;
NODE *succ_block;
+ const YYLTYPE *loc;
} reg_named_capture_assign_t;
static int
@@ -10573,35 +10676,38 @@ reg_named_capture_assign_iter(const OnigUChar *name, const OnigUChar *name_end,
int back_num, int *back_refs, OnigRegex regex, void *arg0)
{
reg_named_capture_assign_t *arg = (reg_named_capture_assign_t*)arg0;
- struct parser_params* parser = arg->parser;
+ struct parser_params* p = arg->parser;
rb_encoding *enc = arg->enc;
long len = name_end - name;
const char *s = (const char *)name;
ID var;
NODE *node, *succ;
- if (!len || (*name != '_' && ISASCII(*name) && !rb_enc_islower(*name, enc)) ||
- (len < MAX_WORD_LENGTH && rb_reserved_word(s, (int)len)) ||
- !rb_enc_symname2_p(s, len, enc)) {
+ if (!len) return ST_CONTINUE;
+ if (rb_enc_symname_type(s, len, enc, (1U<<ID_LOCAL)) != ID_LOCAL)
return ST_CONTINUE;
- }
+
var = intern_cstr(s, len, enc);
- node = newline_node(node_assign(assignable(var, 0), NEW_LIT(ID2SYM(var))));
+ if (len < MAX_WORD_LENGTH && rb_reserved_word(s, (int)len)) {
+ if (!lvar_defined(p, var)) return ST_CONTINUE;
+ }
+ node = node_assign(p, assignable(p, var, 0, arg->loc), NEW_LIT(ID2SYM(var), arg->loc), arg->loc);
succ = arg->succ_block;
- if (!succ) succ = NEW_BEGIN(0);
- succ = block_append(succ, node);
+ if (!succ) succ = NEW_BEGIN(0, arg->loc);
+ succ = block_append(p, succ, node);
arg->succ_block = succ;
return ST_CONTINUE;
}
static NODE *
-reg_named_capture_assign_gen(struct parser_params* parser, VALUE regexp)
+reg_named_capture_assign(struct parser_params* p, VALUE regexp, const YYLTYPE *loc)
{
reg_named_capture_assign_t arg;
- arg.parser = parser;
+ arg.parser = p;
arg.enc = rb_enc_get(regexp);
arg.succ_block = 0;
+ arg.loc = loc;
onig_foreach_name(RREGEXP_PTR(regexp), reg_named_capture_assign_iter, &arg);
if (!arg.succ_block) return 0;
@@ -10609,43 +10715,43 @@ reg_named_capture_assign_gen(struct parser_params* parser, VALUE regexp)
}
static VALUE
-parser_reg_compile(struct parser_params* parser, VALUE str, int options)
+parser_reg_compile(struct parser_params* p, VALUE str, int options)
{
- reg_fragment_setenc(str, options);
- return rb_parser_reg_compile(parser, str, options);
+ reg_fragment_setenc(p, str, options);
+ return rb_parser_reg_compile(p, str, options);
}
VALUE
-rb_parser_reg_compile(struct parser_params* parser, VALUE str, int options)
+rb_parser_reg_compile(struct parser_params* p, VALUE str, int options)
{
- return rb_reg_compile(str, options & RE_OPTION_MASK, ruby_sourcefile, ruby_sourceline);
+ return rb_reg_compile(str, options & RE_OPTION_MASK, p->ruby_sourcefile, p->ruby_sourceline);
}
static VALUE
-reg_compile_gen(struct parser_params* parser, VALUE str, int options)
+reg_compile(struct parser_params* p, VALUE str, int options)
{
VALUE re;
VALUE err;
err = rb_errinfo();
- re = parser_reg_compile(parser, str, options);
+ re = parser_reg_compile(p, str, options);
if (NIL_P(re)) {
VALUE m = rb_attr_get(rb_errinfo(), idMesg);
rb_set_errinfo(err);
- compile_error(PARSER_ARG "%"PRIsVALUE, m);
+ compile_error(p, "%"PRIsVALUE, m);
return Qnil;
}
return re;
}
#else
static VALUE
-parser_reg_compile(struct parser_params* parser, VALUE str, int options, VALUE *errmsg)
+parser_reg_compile(struct parser_params* p, VALUE str, int options, VALUE *errmsg)
{
VALUE err = rb_errinfo();
VALUE re;
- int c = rb_reg_fragment_setenc(parser, str, options);
- if (c) reg_fragment_enc_error(parser, str, c);
- re = rb_parser_reg_compile(parser, str, options);
+ int c = rb_reg_fragment_setenc(p, str, options);
+ if (c) reg_fragment_enc_error(p, str, c);
+ re = rb_parser_reg_compile(p, str, options);
if (NIL_P(re)) {
*errmsg = rb_attr_get(rb_errinfo(), idMesg);
rb_set_errinfo(err);
@@ -10655,77 +10761,56 @@ parser_reg_compile(struct parser_params* parser, VALUE str, int options, VALUE *
#endif
#ifndef RIPPER
-NODE*
-rb_parser_append_print(VALUE vparser, NODE *node)
+void
+rb_parser_set_options(VALUE vparser, int print, int loop, int chomp, int split)
{
- NODE *prelude = 0;
- NODE *scope = node;
- struct parser_params *parser;
-
- if (!node) return node;
-
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, parser);
-
- node = node->nd_body;
-
- if (nd_type(node) == NODE_PRELUDE) {
- prelude = node;
- node = node->nd_body;
- }
-
- node = block_append(node,
- NEW_FCALL(rb_intern("print"),
- NEW_ARRAY(NEW_GVAR(idLASTLINE))));
- if (prelude) {
- prelude->nd_body = node;
- scope->nd_body = prelude;
- }
- else {
- scope->nd_body = node;
- }
-
- return scope;
+ struct parser_params *p;
+ TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
+ p->do_print = print;
+ p->do_loop = loop;
+ p->do_chomp = chomp;
+ p->do_split = split;
}
-NODE *
-rb_parser_while_loop(VALUE vparser, NODE *node, int chop, int split)
+void
+rb_parser_warn_location(VALUE vparser, int warn)
{
- NODE *prelude = 0;
- NODE *scope = node;
- struct parser_params *parser;
-
- if (!node) return node;
-
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, parser);
+ struct parser_params *p;
+ TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
+ p->warn_location = warn;
+}
- node = node->nd_body;
+static NODE *
+parser_append_options(struct parser_params *p, NODE *node)
+{
+ static const YYLTYPE default_location = {{1, 0}, {1, 0}};
+ const YYLTYPE *const LOC = &default_location;
- if (nd_type(node) == NODE_PRELUDE) {
- prelude = node;
- node = node->nd_body;
- }
- if (split) {
- node = block_append(NEW_GASGN(rb_intern("$F"),
- NEW_CALL(NEW_GVAR(idLASTLINE),
- rb_intern("split"), 0)),
- node);
- }
- if (chop) {
- node = block_append(NEW_CALL(NEW_GVAR(idLASTLINE),
- rb_intern("chop!"), 0), node);
+ if (p->do_print) {
+ NODE *print = NEW_FCALL(rb_intern("print"),
+ NEW_ARRAY(NEW_GVAR(idLASTLINE, LOC), LOC),
+ LOC);
+ node = block_append(p, node, print);
}
- node = NEW_OPT_N(node);
+ if (p->do_loop) {
+ if (p->do_split) {
+ NODE *split = NEW_GASGN(rb_intern("$F"),
+ NEW_CALL(NEW_GVAR(idLASTLINE, LOC),
+ rb_intern("split"), 0, LOC),
+ LOC);
+ node = block_append(p, split, node);
+ }
+ if (p->do_chomp) {
+ NODE *chomp = NEW_CALL(NEW_GVAR(idLASTLINE, LOC),
+ rb_intern("chomp!"), 0, LOC);
+ node = block_append(p, chomp, node);
+ }
- if (prelude) {
- prelude->nd_body = node;
- scope->nd_body = prelude;
- }
- else {
- scope->nd_body = node;
+ node = NEW_WHILE(NEW_VCALL(idGets, LOC), node, 1, LOC);
}
- return scope;
+ return node;
}
void
@@ -10738,28 +10823,32 @@ rb_init_parse(void)
#endif /* !RIPPER */
static ID
-internal_id_gen(struct parser_params *parser)
+internal_id(struct parser_params *p)
{
- ID id = (ID)vtable_size(lvtbl->args) + (ID)vtable_size(lvtbl->vars);
- id += ((tLAST_TOKEN - ID_INTERNAL) >> ID_SCOPE_SHIFT) + 1;
+ const ID max_id = RB_ID_SERIAL_MAX & ~0xffff;
+ ID id = (ID)vtable_size(p->lvtbl->args) + (ID)vtable_size(p->lvtbl->vars);
+ id = max_id - id;
return ID_STATIC_SYM | ID_INTERNAL | (id << ID_SCOPE_SHIFT);
}
static void
-parser_initialize(struct parser_params *parser)
+parser_initialize(struct parser_params *p)
{
/* note: we rely on TypedData_Make_Struct to set most fields to 0 */
- command_start = TRUE;
- ruby_sourcefile_string = Qnil;
+ p->command_start = TRUE;
+ p->ruby_sourcefile_string = Qnil;
+ p->lex.lpar_beg = -1; /* make lambda_beginning_p() == FALSE at first */
+ p->node_id = 0;
#ifdef RIPPER
- parser->delayed = Qnil;
- parser->result = Qnil;
- parser->parsing_thread = Qnil;
+ p->delayed = Qnil;
+ p->result = Qnil;
+ p->parsing_thread = Qnil;
#else
- parser->error_buffer = Qfalse;
+ p->error_buffer = Qfalse;
#endif
- parser->debug_buffer = Qnil;
- parser->enc = rb_utf8_encoding();
+ p->debug_buffer = Qnil;
+ p->debug_output = rb_stdout;
+ p->enc = rb_utf8_encoding();
}
#ifdef RIPPER
@@ -10770,49 +10859,50 @@ parser_initialize(struct parser_params *parser)
static void
parser_mark(void *ptr)
{
- struct parser_params *parser = (struct parser_params*)ptr;
+ struct parser_params *p = (struct parser_params*)ptr;
- rb_gc_mark((VALUE)lex_strterm);
- rb_gc_mark(lex_input);
- rb_gc_mark(lex_lastline);
- rb_gc_mark(lex_nextline);
- rb_gc_mark(ruby_sourcefile_string);
+ 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);
#ifndef RIPPER
- rb_gc_mark((VALUE)ruby_eval_tree_begin);
- rb_gc_mark((VALUE)ruby_eval_tree);
- rb_gc_mark(ruby_debug_lines);
- rb_gc_mark(parser->compile_option);
- rb_gc_mark(parser->error_buffer);
+ rb_gc_mark(p->debug_lines);
+ rb_gc_mark(p->compile_option);
+ rb_gc_mark(p->error_buffer);
#else
- rb_gc_mark(parser->delayed);
- rb_gc_mark(parser->value);
- rb_gc_mark(parser->result);
- rb_gc_mark(parser->parsing_thread);
+ rb_gc_mark(p->delayed);
+ rb_gc_mark(p->value);
+ rb_gc_mark(p->result);
+ rb_gc_mark(p->parsing_thread);
#endif
- rb_gc_mark(parser->debug_buffer);
+ rb_gc_mark(p->debug_buffer);
+ rb_gc_mark(p->debug_output);
#ifdef YYMALLOC
- rb_gc_mark((VALUE)parser->heap);
+ rb_gc_mark((VALUE)p->heap);
#endif
}
static void
parser_free(void *ptr)
{
- struct parser_params *parser = (struct parser_params*)ptr;
+ struct parser_params *p = (struct parser_params*)ptr;
struct local_vars *local, *prev;
- if (tokenbuf) {
- xfree(tokenbuf);
+ if (p->tokenbuf) {
+ ruby_sized_xfree(p->tokenbuf, p->toksiz);
}
- for (local = lvtbl; local; local = prev) {
+ for (local = p->lvtbl; local; local = prev) {
if (local->vars) xfree(local->vars);
prev = local->prev;
xfree(local);
}
{
token_info *ptinfo;
- while ((ptinfo = parser->token_info) != 0) {
- parser->token_info = ptinfo->next;
+ while ((ptinfo = p->token_info) != 0) {
+ p->token_info = ptinfo->next;
xfree(ptinfo);
}
}
@@ -10822,12 +10912,12 @@ parser_free(void *ptr)
static size_t
parser_memsize(const void *ptr)
{
- struct parser_params *parser = (struct parser_params*)ptr;
+ struct parser_params *p = (struct parser_params*)ptr;
struct local_vars *local;
- size_t size = sizeof(*parser);
+ size_t size = sizeof(*p);
- size += toksiz;
- for (local = lvtbl; local; local = local->prev) {
+ size += p->toksiz;
+ for (local = p->lvtbl; local; local = local->prev) {
size += sizeof(*local);
if (local->vars) size += local->vars->capa * sizeof(ID);
}
@@ -10870,12 +10960,12 @@ rb_parser_new(void)
VALUE
rb_parser_set_context(VALUE vparser, const struct rb_block *base, int main)
{
- struct parser_params *parser;
+ struct parser_params *p;
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, parser);
- parser->error_buffer = main ? Qfalse : Qnil;
- parser->base_block = base;
- in_main = main;
+ TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
+ p->error_buffer = main ? Qfalse : Qnil;
+ p->base_block = base;
+ p->in_main = main;
return vparser;
}
#endif
@@ -10892,48 +10982,48 @@ static VALUE ripper_parser_set_yydebug(VALUE self, VALUE flag);
/*
* call-seq:
- * ripper#error? -> Boolean
+ * ripper.error? -> Boolean
*
* Return true if parsed source has errors.
*/
static VALUE
ripper_error_p(VALUE vparser)
{
- struct parser_params *parser;
+ struct parser_params *p;
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, parser);
- return parser->error_p ? Qtrue : Qfalse;
+ TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
+ return p->error_p ? Qtrue : Qfalse;
}
#endif
/*
* call-seq:
- * ripper#end_seen? -> Boolean
+ * ripper.end_seen? -> Boolean
*
* Return true if parsed source ended by +\_\_END\_\_+.
*/
VALUE
rb_parser_end_seen_p(VALUE vparser)
{
- struct parser_params *parser;
+ struct parser_params *p;
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, parser);
- return ruby__end__seen ? Qtrue : Qfalse;
+ TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
+ return p->ruby__end__seen ? Qtrue : Qfalse;
}
/*
* call-seq:
- * ripper#encoding -> encoding
+ * ripper.encoding -> encoding
*
* Return encoding of the source.
*/
VALUE
rb_parser_encoding(VALUE vparser)
{
- struct parser_params *parser;
+ struct parser_params *p;
- TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, parser);
- return rb_enc_from_encoding(current_enc);
+ TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
+ return rb_enc_from_encoding(p->enc);
}
/*
@@ -10945,10 +11035,10 @@ rb_parser_encoding(VALUE vparser)
VALUE
rb_parser_get_yydebug(VALUE self)
{
- struct parser_params *parser;
+ struct parser_params *p;
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, parser);
- return yydebug ? Qtrue : Qfalse;
+ TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
+ return p->debug ? Qtrue : Qfalse;
}
/*
@@ -10960,54 +11050,56 @@ rb_parser_get_yydebug(VALUE self)
VALUE
rb_parser_set_yydebug(VALUE self, VALUE flag)
{
- struct parser_params *parser;
+ struct parser_params *p;
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, parser);
- yydebug = RTEST(flag);
+ TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
+ p->debug = RTEST(flag);
return flag;
}
#ifndef RIPPER
#ifdef YYMALLOC
#define HEAPCNT(n, size) ((n) * (size) / sizeof(YYSTYPE))
-#define NEWHEAP() rb_node_newnode(NODE_ALLOCA, 0, (VALUE)parser->heap, 0)
-#define ADD2HEAP(n, c, p) ((parser->heap = (n))->u1.node = (p), \
- (n)->u3.cnt = (c), (p))
+/* Keep the order; NEWHEAP then xmalloc and ADD2HEAP to get rid of
+ * potential memory leak */
+#define NEWHEAP() rb_imemo_tmpbuf_parser_heap(0, p->heap, 0)
+#define ADD2HEAP(new, cnt, ptr) ((p->heap = (new))->ptr = (ptr), \
+ (new)->cnt = (cnt), (ptr))
void *
-rb_parser_malloc(struct parser_params *parser, size_t size)
+rb_parser_malloc(struct parser_params *p, size_t size)
{
size_t cnt = HEAPCNT(1, size);
- NODE *n = NEWHEAP();
+ rb_imemo_tmpbuf_t *n = NEWHEAP();
void *ptr = xmalloc(size);
return ADD2HEAP(n, cnt, ptr);
}
void *
-rb_parser_calloc(struct parser_params *parser, size_t nelem, size_t size)
+rb_parser_calloc(struct parser_params *p, size_t nelem, size_t size)
{
size_t cnt = HEAPCNT(nelem, size);
- NODE *n = NEWHEAP();
+ rb_imemo_tmpbuf_t *n = NEWHEAP();
void *ptr = xcalloc(nelem, size);
return ADD2HEAP(n, cnt, ptr);
}
void *
-rb_parser_realloc(struct parser_params *parser, void *ptr, size_t size)
+rb_parser_realloc(struct parser_params *p, void *ptr, size_t size)
{
- NODE *n;
+ rb_imemo_tmpbuf_t *n;
size_t cnt = HEAPCNT(1, size);
- if (ptr && (n = parser->heap) != NULL) {
+ if (ptr && (n = p->heap) != NULL) {
do {
- if (n->u1.node == ptr) {
- n->u1.node = ptr = xrealloc(ptr, size);
- if (n->u3.cnt) n->u3.cnt = cnt;
+ if (n->ptr == ptr) {
+ n->ptr = ptr = xrealloc(ptr, size);
+ if (n->cnt) n->cnt = cnt;
return ptr;
}
- } while ((n = n->u2.node) != NULL);
+ } while ((n = n->next) != NULL);
}
n = NEWHEAP();
ptr = xrealloc(ptr, size);
@@ -11015,51 +11107,52 @@ rb_parser_realloc(struct parser_params *parser, void *ptr, size_t size)
}
void
-rb_parser_free(struct parser_params *parser, void *ptr)
+rb_parser_free(struct parser_params *p, void *ptr)
{
- NODE **prev = &parser->heap, *n;
+ rb_imemo_tmpbuf_t **prev = &p->heap, *n;
while ((n = *prev) != NULL) {
- if (n->u1.node == ptr) {
- *prev = n->u2.node;
+ if (n->ptr == ptr) {
+ *prev = n->next;
rb_gc_force_recycle((VALUE)n);
break;
}
- prev = &n->u2.node;
+ prev = &n->next;
}
xfree(ptr);
}
#endif
void
-rb_parser_printf(struct parser_params *parser, const char *fmt, ...)
+rb_parser_printf(struct parser_params *p, const char *fmt, ...)
{
va_list ap;
- VALUE mesg = parser->debug_buffer;
+ VALUE mesg = p->debug_buffer;
- if (NIL_P(mesg)) parser->debug_buffer = mesg = rb_str_new(0, 0);
+ if (NIL_P(mesg)) p->debug_buffer = mesg = rb_str_new(0, 0);
va_start(ap, fmt);
rb_str_vcatf(mesg, fmt, ap);
va_end(ap);
if (RSTRING_END(mesg)[-1] == '\n') {
- rb_io_write(rb_stdout, mesg);
- parser->debug_buffer = Qnil;
+ rb_io_write(p->debug_output, mesg);
+ p->debug_buffer = Qnil;
}
}
static void
-parser_compile_error(struct parser_params *parser, const char *fmt, ...)
+parser_compile_error(struct parser_params *p, const char *fmt, ...)
{
va_list ap;
- parser->error_p = 1;
+ rb_io_flush(p->debug_output);
+ p->error_p = 1;
va_start(ap, fmt);
- parser->error_buffer =
- rb_syntax_error_append(parser->error_buffer,
- ruby_sourcefile_string,
- ruby_sourceline,
- rb_long2int(lex_p - lex_pbeg),
- current_enc, fmt, ap);
+ p->error_buffer =
+ rb_syntax_error_append(p->error_buffer,
+ p->ruby_sourcefile_string,
+ p->ruby_sourceline,
+ rb_long2int(p->lex.pcur - p->lex.pbeg),
+ p->enc, fmt, ap);
va_end(ap);
}
#endif
@@ -11106,58 +11199,58 @@ ripper_validate_object(VALUE self, VALUE x)
#define validate(x) ((x) = get_value(x))
static VALUE
-ripper_dispatch0(struct parser_params *parser, ID mid)
+ripper_dispatch0(struct parser_params *p, ID mid)
{
- return rb_funcall(parser->value, mid, 0);
+ return rb_funcall(p->value, mid, 0);
}
static VALUE
-ripper_dispatch1(struct parser_params *parser, ID mid, VALUE a)
+ripper_dispatch1(struct parser_params *p, ID mid, VALUE a)
{
validate(a);
- return rb_funcall(parser->value, mid, 1, a);
+ return rb_funcall(p->value, mid, 1, a);
}
static VALUE
-ripper_dispatch2(struct parser_params *parser, ID mid, VALUE a, VALUE b)
+ripper_dispatch2(struct parser_params *p, ID mid, VALUE a, VALUE b)
{
validate(a);
validate(b);
- return rb_funcall(parser->value, mid, 2, a, b);
+ return rb_funcall(p->value, mid, 2, a, b);
}
static VALUE
-ripper_dispatch3(struct parser_params *parser, ID mid, VALUE a, VALUE b, VALUE c)
+ripper_dispatch3(struct parser_params *p, ID mid, VALUE a, VALUE b, VALUE c)
{
validate(a);
validate(b);
validate(c);
- return rb_funcall(parser->value, mid, 3, a, b, c);
+ return rb_funcall(p->value, mid, 3, a, b, c);
}
static VALUE
-ripper_dispatch4(struct parser_params *parser, ID mid, VALUE a, VALUE b, VALUE c, VALUE d)
+ripper_dispatch4(struct parser_params *p, ID mid, VALUE a, VALUE b, VALUE c, VALUE d)
{
validate(a);
validate(b);
validate(c);
validate(d);
- return rb_funcall(parser->value, mid, 4, a, b, c, d);
+ return rb_funcall(p->value, mid, 4, a, b, c, d);
}
static VALUE
-ripper_dispatch5(struct parser_params *parser, ID mid, VALUE a, VALUE b, VALUE c, VALUE d, VALUE e)
+ripper_dispatch5(struct parser_params *p, ID mid, VALUE a, VALUE b, VALUE c, VALUE d, VALUE e)
{
validate(a);
validate(b);
validate(c);
validate(d);
validate(e);
- return rb_funcall(parser->value, mid, 5, a, b, c, d, e);
+ return rb_funcall(p->value, mid, 5, a, b, c, d, e);
}
static VALUE
-ripper_dispatch7(struct parser_params *parser, ID mid, VALUE a, VALUE b, VALUE c, VALUE d, VALUE e, VALUE f, VALUE g)
+ripper_dispatch7(struct parser_params *p, ID mid, VALUE a, VALUE b, VALUE c, VALUE d, VALUE e, VALUE f, VALUE g)
{
validate(a);
validate(b);
@@ -11166,95 +11259,7 @@ ripper_dispatch7(struct parser_params *parser, ID mid, VALUE a, VALUE b, VALUE c
validate(e);
validate(f);
validate(g);
- return rb_funcall(parser->value, mid, 7, a, b, c, d, e, f, g);
-}
-
-static const struct kw_assoc {
- ID id;
- const char *name;
-} keyword_to_name[] = {
- {keyword_class, "class"},
- {keyword_module, "module"},
- {keyword_def, "def"},
- {keyword_undef, "undef"},
- {keyword_begin, "begin"},
- {keyword_rescue, "rescue"},
- {keyword_ensure, "ensure"},
- {keyword_end, "end"},
- {keyword_if, "if"},
- {keyword_unless, "unless"},
- {keyword_then, "then"},
- {keyword_elsif, "elsif"},
- {keyword_else, "else"},
- {keyword_case, "case"},
- {keyword_when, "when"},
- {keyword_while, "while"},
- {keyword_until, "until"},
- {keyword_for, "for"},
- {keyword_break, "break"},
- {keyword_next, "next"},
- {keyword_redo, "redo"},
- {keyword_retry, "retry"},
- {keyword_in, "in"},
- {keyword_do, "do"},
- {keyword_do_cond, "do"},
- {keyword_do_block, "do"},
- {keyword_return, "return"},
- {keyword_yield, "yield"},
- {keyword_super, "super"},
- {keyword_self, "self"},
- {keyword_nil, "nil"},
- {keyword_true, "true"},
- {keyword_false, "false"},
- {keyword_and, "and"},
- {keyword_or, "or"},
- {keyword_not, "not"},
- {modifier_if, "if"},
- {modifier_unless, "unless"},
- {modifier_while, "while"},
- {modifier_until, "until"},
- {modifier_rescue, "rescue"},
- {keyword_alias, "alias"},
- {keyword_defined, "defined?"},
- {keyword_BEGIN, "BEGIN"},
- {keyword_END, "END"},
- {keyword__LINE__, "__LINE__"},
- {keyword__FILE__, "__FILE__"},
- {keyword__ENCODING__, "__ENCODING__"},
- {0, NULL}
-};
-
-static const char*
-keyword_id_to_str(ID id)
-{
- const struct kw_assoc *a;
-
- for (a = keyword_to_name; a->id; a++) {
- if (a->id == id)
- return a->name;
- }
- return NULL;
-}
-
-#undef ripper_id2sym
-static VALUE
-ripper_id2sym(ID id)
-{
- const char *name;
- char buf[8];
-
- if (id == (ID)(signed char)id) {
- buf[0] = (char)id;
- buf[1] = '\0';
- return ID2SYM(rb_intern2(buf, 1));
- }
- if ((name = keyword_id_to_str(id))) {
- return ID2SYM(rb_intern(name));
- }
- if (!rb_id2str(id)) {
- rb_bug("cannot convert ID to string: %ld", (unsigned long)id);
- }
- return ID2SYM(id);
+ return rb_funcall(p->value, mid, 7, a, b, c, d, e, f, g);
}
static ID
@@ -11279,13 +11284,13 @@ ripper_get_value(VALUE v)
}
static void
-ripper_error_gen(struct parser_params *parser)
+ripper_error(struct parser_params *p)
{
- parser->error_p = TRUE;
+ p->error_p = TRUE;
}
static void
-ripper_compile_error(struct parser_params *parser, const char *fmt, ...)
+ripper_compile_error(struct parser_params *p, const char *fmt, ...)
{
VALUE str;
va_list args;
@@ -11293,12 +11298,24 @@ ripper_compile_error(struct parser_params *parser, const char *fmt, ...)
va_start(args, fmt);
str = rb_vsprintf(fmt, args);
va_end(args);
- rb_funcall(parser->value, rb_intern("compile_error"), 1, str);
- ripper_error_gen(parser);
+ rb_funcall(p->value, rb_intern("compile_error"), 1, str);
+ ripper_error(p);
+}
+
+static VALUE
+ripper_lex_get_generic(struct parser_params *p, VALUE src)
+{
+ VALUE line = rb_funcallv_public(src, id_gets, 0, 0);
+ if (!NIL_P(line) && !RB_TYPE_P(line, T_STRING)) {
+ rb_raise(rb_eTypeError,
+ "gets returned %"PRIsVALUE" (expected String or nil)",
+ rb_obj_class(line));
+ }
+ return line;
}
static VALUE
-ripper_lex_get_generic(struct parser_params *parser, VALUE src)
+ripper_lex_io_get(struct parser_params *p, VALUE src)
{
return rb_io_gets(src);
}
@@ -11328,94 +11345,94 @@ ripper_s_allocate(VALUE klass)
static VALUE
ripper_initialize(int argc, VALUE *argv, VALUE self)
{
- struct parser_params *parser;
+ struct parser_params *p;
VALUE src, fname, lineno;
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, parser);
+ TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
rb_scan_args(argc, argv, "12", &src, &fname, &lineno);
if (RB_TYPE_P(src, T_FILE)) {
- lex_gets = ripper_lex_get_generic;
+ p->lex.gets = ripper_lex_io_get;
+ }
+ else if (rb_respond_to(src, id_gets)) {
+ p->lex.gets = ripper_lex_get_generic;
}
else {
StringValue(src);
- lex_gets = lex_get_str;
+ p->lex.gets = lex_get_str;
}
- lex_input = src;
- parser->eofp = 0;
+ p->lex.input = src;
+ p->eofp = 0;
if (NIL_P(fname)) {
fname = STR_NEW2("(ripper)");
OBJ_FREEZE(fname);
}
else {
- StringValue(fname);
+ StringValueCStr(fname);
fname = rb_str_new_frozen(fname);
}
- parser_initialize(parser);
+ parser_initialize(p);
- ruby_sourcefile_string = fname;
- ruby_sourcefile = RSTRING_PTR(fname);
- ruby_sourceline = NIL_P(lineno) ? 0 : NUM2INT(lineno) - 1;
+ p->ruby_sourcefile_string = fname;
+ p->ruby_sourcefile = RSTRING_PTR(fname);
+ p->ruby_sourceline = NIL_P(lineno) ? 0 : NUM2INT(lineno) - 1;
return Qnil;
}
-struct ripper_args {
- struct parser_params *parser;
- int argc;
- VALUE *argv;
-};
-
static VALUE
ripper_parse0(VALUE parser_v)
{
- struct parser_params *parser;
+ struct parser_params *p;
- TypedData_Get_Struct(parser_v, struct parser_params, &parser_data_type, parser);
- parser_prepare(parser);
- ripper_yyparse((void*)parser);
- return parser->result;
+ TypedData_Get_Struct(parser_v, struct parser_params, &parser_data_type, p);
+ parser_prepare(p);
+ p->ast = rb_ast_new();
+ ripper_yyparse((void*)p);
+ rb_ast_dispose(p->ast);
+ p->ast = 0;
+ return p->result;
}
static VALUE
ripper_ensure(VALUE parser_v)
{
- struct parser_params *parser;
+ struct parser_params *p;
- TypedData_Get_Struct(parser_v, struct parser_params, &parser_data_type, parser);
- parser->parsing_thread = Qnil;
+ TypedData_Get_Struct(parser_v, struct parser_params, &parser_data_type, p);
+ p->parsing_thread = Qnil;
return Qnil;
}
/*
* call-seq:
- * ripper#parse
+ * ripper.parse
*
* Start parsing and returns the value of the root action.
*/
static VALUE
ripper_parse(VALUE self)
{
- struct parser_params *parser;
+ struct parser_params *p;
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, parser);
- if (!ripper_initialized_p(parser)) {
+ TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
+ if (!ripper_initialized_p(p)) {
rb_raise(rb_eArgError, "method called for uninitialized object");
}
- if (!NIL_P(parser->parsing_thread)) {
- if (parser->parsing_thread == rb_thread_current())
+ if (!NIL_P(p->parsing_thread)) {
+ if (p->parsing_thread == rb_thread_current())
rb_raise(rb_eArgError, "Ripper#parse is not reentrant");
else
rb_raise(rb_eArgError, "Ripper#parse is not multithread-safe");
}
- parser->parsing_thread = rb_thread_current();
+ p->parsing_thread = rb_thread_current();
rb_ensure(ripper_parse0, self, ripper_ensure, self);
- return parser->result;
+ return p->result;
}
/*
* call-seq:
- * ripper#column -> Integer
+ * ripper.column -> Integer
*
* Return column number of current parsing line.
* This number starts from 0.
@@ -11423,39 +11440,39 @@ ripper_parse(VALUE self)
static VALUE
ripper_column(VALUE self)
{
- struct parser_params *parser;
+ struct parser_params *p;
long col;
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, parser);
- if (!ripper_initialized_p(parser)) {
+ TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
+ if (!ripper_initialized_p(p)) {
rb_raise(rb_eArgError, "method called for uninitialized object");
}
- if (NIL_P(parser->parsing_thread)) return Qnil;
- col = parser->tokp - lex_pbeg;
+ if (NIL_P(p->parsing_thread)) return Qnil;
+ col = p->lex.ptok - p->lex.pbeg;
return LONG2NUM(col);
}
/*
* call-seq:
- * ripper#filename -> String
+ * ripper.filename -> String
*
* Return current parsing filename.
*/
static VALUE
ripper_filename(VALUE self)
{
- struct parser_params *parser;
+ struct parser_params *p;
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, parser);
- if (!ripper_initialized_p(parser)) {
+ TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
+ if (!ripper_initialized_p(p)) {
rb_raise(rb_eArgError, "method called for uninitialized object");
}
- return ruby_sourcefile_string;
+ return p->ruby_sourcefile_string;
}
/*
* call-seq:
- * ripper#lineno -> Integer
+ * ripper.lineno -> Integer
*
* Return line number of current parsing line.
* This number starts from 1.
@@ -11463,14 +11480,33 @@ ripper_filename(VALUE self)
static VALUE
ripper_lineno(VALUE self)
{
- struct parser_params *parser;
+ struct parser_params *p;
- TypedData_Get_Struct(self, struct parser_params, &parser_data_type, parser);
- if (!ripper_initialized_p(parser)) {
+ TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
+ if (!ripper_initialized_p(p)) {
rb_raise(rb_eArgError, "method called for uninitialized object");
}
- if (NIL_P(parser->parsing_thread)) return Qnil;
- return INT2NUM(ruby_sourceline);
+ if (NIL_P(p->parsing_thread)) return Qnil;
+ return INT2NUM(p->ruby_sourceline);
+}
+
+/*
+ * call-seq:
+ * ripper.state -> Integer
+ *
+ * Return scanner state of current token.
+ */
+static VALUE
+ripper_state(VALUE self)
+{
+ struct parser_params *p;
+
+ TypedData_Get_Struct(self, struct parser_params, &parser_data_type, p);
+ if (!ripper_initialized_p(p)) {
+ rb_raise(rb_eArgError, "method called for uninitialized object");
+ }
+ if (NIL_P(p->parsing_thread)) return Qnil;
+ return INT2NUM(p->lex.state);
}
#ifdef RIPPER_DEBUG
@@ -11493,6 +11529,17 @@ ripper_value(VALUE self, VALUE obj)
}
#endif
+/*
+ * call-seq:
+ * Ripper.lex_state_name(integer) -> string
+ *
+ * Returns a string representation of lex_state.
+ */
+static VALUE
+ripper_lex_state_name(VALUE self, VALUE state)
+{
+ return rb_parser_lex_state_name(NUM2INT(state));
+}
void
Init_ripper(void)
@@ -11501,6 +11548,7 @@ Init_ripper(void)
ripper_init_eventids2();
id_warn = rb_intern_const("warn");
id_warning = rb_intern_const("warning");
+ id_gets = rb_intern_const("gets");
InitVM(ripper);
}
@@ -11519,6 +11567,7 @@ InitVM_ripper(void)
rb_define_method(Ripper, "column", ripper_column, 0);
rb_define_method(Ripper, "filename", ripper_filename, 0);
rb_define_method(Ripper, "lineno", ripper_lineno, 0);
+ rb_define_method(Ripper, "state", ripper_state, 0);
rb_define_method(Ripper, "end_seen?", rb_parser_end_seen_p, 0);
rb_define_method(Ripper, "encoding", rb_parser_encoding, 0);
rb_define_method(Ripper, "yydebug", rb_parser_get_yydebug, 0);
@@ -11533,6 +11582,12 @@ InitVM_ripper(void)
rb_define_singleton_method(Ripper, "dedent_string", parser_dedent_string, 2);
rb_define_private_method(Ripper, "dedent_string", parser_dedent_string, 2);
+ rb_define_singleton_method(Ripper, "lex_state_name", ripper_lex_state_name, 1);
+
+<% @exprs.each do |expr, desc| -%>
+ /* <%=desc%> */
+ rb_define_const(Ripper, "<%=expr%>", INT2NUM(<%=expr%>));
+<% end %>
ripper_init_eventids1_table(Ripper);
ripper_init_eventids2_table(Ripper);
diff --git a/prelude.rb b/prelude.rb
index f9bb7451f8..1d62c13219 100644
--- a/prelude.rb
+++ b/prelude.rb
@@ -1,18 +1,15 @@
-class Thread
- MUTEX_FOR_THREAD_EXCLUSIVE = Thread::Mutex.new # :nodoc:
- private_constant :MUTEX_FOR_THREAD_EXCLUSIVE
-
+class << Thread
# call-seq:
# Thread.exclusive { block } => obj
#
# Wraps the block in a single, VM-global Mutex.synchronize, returning the
# value of the block. A thread executing inside the exclusive section will
# only block other threads which also use the Thread.exclusive mechanism.
- def self.exclusive
+ def exclusive(&block) end if false
+ mutex = Mutex.new # :nodoc:
+ define_method(:exclusive) do |&block|
warn "Thread.exclusive is deprecated, use Thread::Mutex", caller
- MUTEX_FOR_THREAD_EXCLUSIVE.synchronize{
- yield
- }
+ mutex.synchronize(&block)
end
end
@@ -68,9 +65,10 @@ class IO
# Note that this method is identical to readpartial
# except the non-blocking flag is set.
#
- # By specifying `exception: false`, the options hash allows you to indicate
+ # By specifying a keyword argument _exception_ to +false+, you can indicate
# that read_nonblock should not raise an IO::WaitReadable exception, but
- # return the symbol :wait_readable instead.
+ # return the symbol +:wait_readable+ instead. At EOF, it will return nil
+ # instead of raising EOFError.
def read_nonblock(len, buf = nil, exception: true)
__read_nonblock(len, buf, exception)
end
@@ -99,7 +97,7 @@ class IO
#
# # write_nonblock writes only 65536 bytes and return 65536.
# # (The pipe size is 65536 bytes on this environment.)
- # s = "a" #100000
+ # s = "a" * 100000
# p w.write_nonblock(s) #=> 65536
#
# # write_nonblock cannot write a byte and raise EWOULDBLOCK (EAGAIN).
@@ -126,10 +124,39 @@ class IO
# according to the kind of the IO object.
# In such cases, write_nonblock raises <code>Errno::EBADF</code>.
#
- # By specifying `exception: false`, the options hash allows you to indicate
+ # By specifying a keyword argument _exception_ to +false+, you can indicate
# that write_nonblock should not raise an IO::WaitWritable exception, but
- # return the symbol :wait_writable instead.
+ # return the symbol +:wait_writable+ instead.
def write_nonblock(buf, exception: true)
__write_nonblock(buf, exception)
end
end
+
+class TracePoint
+ def enable target: nil, target_line: nil, &blk
+ self.__enable target, target_line, &blk
+ end
+end
+
+class Binding
+ # :nodoc:
+ def irb
+ require 'irb'
+ irb
+ end
+
+ # suppress redefinition warning
+ alias irb irb # :nodoc:
+end
+
+module Kernel
+ def pp(*objs)
+ require 'pp'
+ pp(*objs)
+ end
+
+ # suppress redefinition warning
+ alias pp pp # :nodoc:
+
+ private :pp
+end
diff --git a/probes_helper.h b/probes_helper.h
index 1becae5a8d..115c78d467 100644
--- a/probes_helper.h
+++ b/probes_helper.h
@@ -2,7 +2,6 @@
#define RUBY_PROBES_HELPER_H
#include "ruby/ruby.h"
-#include "probes.h"
struct ruby_dtrace_method_hook_args {
const char *classname;
@@ -13,13 +12,13 @@ struct ruby_dtrace_method_hook_args {
volatile VALUE name;
};
-NOINLINE(int ruby_th_dtrace_setup(rb_thread_t *, VALUE, ID, struct ruby_dtrace_method_hook_args *));
+NOINLINE(int rb_dtrace_setup(rb_execution_context_t *, VALUE, ID, struct ruby_dtrace_method_hook_args *));
-#define RUBY_DTRACE_METHOD_HOOK(name, th, klazz, id) \
+#define RUBY_DTRACE_METHOD_HOOK(name, ec, klazz, id) \
do { \
if (UNLIKELY(RUBY_DTRACE_##name##_ENABLED())) { \
struct ruby_dtrace_method_hook_args args; \
- if (ruby_th_dtrace_setup(th, klazz, id, &args)) { \
+ if (rb_dtrace_setup(ec, klazz, id, &args)) { \
RUBY_DTRACE_##name(args.classname, \
args.methodname, \
args.filename, \
@@ -28,16 +27,16 @@ do { \
} \
} while (0)
-#define RUBY_DTRACE_METHOD_ENTRY_HOOK(th, klass, id) \
- RUBY_DTRACE_METHOD_HOOK(METHOD_ENTRY, th, klass, id)
+#define RUBY_DTRACE_METHOD_ENTRY_HOOK(ec, klass, id) \
+ RUBY_DTRACE_METHOD_HOOK(METHOD_ENTRY, ec, klass, id)
-#define RUBY_DTRACE_METHOD_RETURN_HOOK(th, klass, id) \
- RUBY_DTRACE_METHOD_HOOK(METHOD_RETURN, th, klass, id)
+#define RUBY_DTRACE_METHOD_RETURN_HOOK(ec, klass, id) \
+ RUBY_DTRACE_METHOD_HOOK(METHOD_RETURN, ec, klass, id)
-#define RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, klass, id) \
- RUBY_DTRACE_METHOD_HOOK(CMETHOD_ENTRY, th, klass, id)
+#define RUBY_DTRACE_CMETHOD_ENTRY_HOOK(ec, klass, id) \
+ RUBY_DTRACE_METHOD_HOOK(CMETHOD_ENTRY, ec, klass, id)
-#define RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, klass, id) \
- RUBY_DTRACE_METHOD_HOOK(CMETHOD_RETURN, th, klass, id)
+#define RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, klass, id) \
+ RUBY_DTRACE_METHOD_HOOK(CMETHOD_RETURN, ec, klass, id)
#endif /* RUBY_PROBES_HELPER_H */
diff --git a/proc.c b/proc.c
index f5037e5118..fc1fdf7b3a 100644
--- a/proc.c
+++ b/proc.c
@@ -12,13 +12,14 @@
#include "eval_intern.h"
#include "internal.h"
#include "gc.h"
+#include "vm_core.h"
#include "iseq.h"
/* Proc.new with no block will raise an exception in the future
* versions */
#define PROC_NEW_REQUIRES_BLOCK 0
-#if !defined(__GNUC__) || __GNUC__ < 5
+#if !defined(__GNUC__) || __GNUC__ < 5 || defined(__MINGW32__)
# define NO_CLOBBERED(v) (*(volatile VALUE *)&(v))
#else
# define NO_CLOBBERED(v) (v)
@@ -29,6 +30,7 @@ const rb_cref_t *rb_vm_cref_in_context(VALUE self, VALUE cbase);
struct METHOD {
const VALUE recv;
const VALUE klass;
+ const VALUE iclass;
const rb_method_entry_t * const me;
/* for bound methods, `me' should be rb_callable_method_entry_t * */
};
@@ -48,8 +50,6 @@ static int method_min_max_arity(VALUE, int *max);
#define IS_METHOD_PROC_IFUNC(ifunc) ((ifunc)->func == bmcall)
-static VALUE proc_to_s_(VALUE self, const rb_proc_t *proc);
-
static void
block_mark(const struct rb_block *block)
{
@@ -124,28 +124,11 @@ rb_obj_is_proc(VALUE proc)
}
}
-VALUE rb_proc_create(VALUE klass, const struct rb_block *block,
- int8_t safe_level, int8_t is_from_method, int8_t is_lambda);
-
-/* :nodoc: */
-static VALUE
-proc_dup(VALUE self)
-{
- VALUE procval;
- rb_proc_t *src;
-
- GetProcPtr(self, src);
- procval = rb_proc_create(rb_cProc, &src->block,
- src->safe_level, src->is_from_method, src->is_lambda);
- RB_GC_GUARD(self); /* for: body = proc_dup(body) */
- return procval;
-}
-
/* :nodoc: */
static VALUE
proc_clone(VALUE self)
{
- VALUE procval = proc_dup(self);
+ VALUE procval = rb_proc_dup(self);
CLONESETUP(procval, self);
return procval;
}
@@ -265,12 +248,8 @@ rb_proc_lambda_p(VALUE procval)
static void
binding_free(void *ptr)
{
- rb_binding_t *bind;
RUBY_FREE_ENTER("binding");
- if (ptr) {
- bind = ptr;
- ruby_xfree(bind);
- }
+ ruby_xfree(ptr);
RUBY_FREE_LEAVE("binding");
}
@@ -280,11 +259,8 @@ binding_mark(void *ptr)
rb_binding_t *bind = ptr;
RUBY_MARK_ENTER("binding");
-
block_mark(&bind->block);
-
- RUBY_MARK_UNLESS_NULL(bind->path);
-
+ rb_gc_mark(bind->pathobj);
RUBY_MARK_LEAVE("binding");
}
@@ -301,7 +277,7 @@ const rb_data_type_t ruby_binding_data_type = {
binding_free,
binding_memsize,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
};
VALUE
@@ -313,6 +289,7 @@ rb_binding_alloc(VALUE klass)
return obj;
}
+
/* :nodoc: */
static VALUE
binding_dup(VALUE self)
@@ -321,8 +298,8 @@ binding_dup(VALUE self)
rb_binding_t *src, *dst;
GetBindingPtr(self, src);
GetBindingPtr(bindval, dst);
- dst->block = src->block;
- dst->path = src->path;
+ rb_vm_block_copy(bindval, &dst->block, &src->block);
+ RB_OBJ_WRITE(bindval, &dst->pathobj, src->pathobj);
dst->first_lineno = src->first_lineno;
return bindval;
}
@@ -339,8 +316,8 @@ binding_clone(VALUE self)
VALUE
rb_binding_new(void)
{
- rb_thread_t *th = GET_THREAD();
- return rb_vm_make_binding(th, th->cfp);
+ rb_execution_context_t *ec = GET_EC();
+ return rb_vm_make_binding(ec, ec->cfp);
}
/*
@@ -353,7 +330,7 @@ rb_binding_new(void)
* environment. See also the description of class +Binding+.
*
* def get_binding(param)
- * return binding
+ * binding
* end
* b = get_binding("hello")
* eval("param", b) #=> "hello"
@@ -375,7 +352,7 @@ rb_f_binding(VALUE self)
* reporting syntax errors.
*
* def get_binding(param)
- * return binding
+ * binding
* end
* b = get_binding("hello")
* b.eval("param") #=> "hello"
@@ -392,8 +369,9 @@ bind_eval(int argc, VALUE *argv, VALUE bindval)
}
static const VALUE *
-get_local_variable_ptr(const rb_env_t *env, ID lid)
+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)) {
const rb_iseq_t *iseq = env->iseq;
@@ -403,15 +381,28 @@ get_local_variable_ptr(const rb_env_t *env, ID lid)
for (i=0; i<iseq->body->local_table_size; i++) {
if (iseq->body->local_table[i] == lid) {
+ if (iseq->body->local_iseq == iseq &&
+ iseq->body->param.flags.has_block &&
+ (unsigned int)iseq->body->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;
}
} while ((env = rb_vm_env_prev_env(env)) != NULL);
+ *envp = NULL;
return NULL;
}
@@ -446,7 +437,7 @@ check_local_id(VALUE bindval, volatile VALUE *pname)
* call-seq:
* binding.local_variables -> Array
*
- * Returns the +symbol+ names of the binding's local variables
+ * Returns the names of the binding's local variables as symbols.
*
* def foo
* a = 1
@@ -455,7 +446,7 @@ check_local_id(VALUE bindval, volatile VALUE *pname)
* end
* end
*
- * This method is short version of the following code.
+ * This method is the short version of the following code:
*
* binding.eval("local_variables")
*
@@ -475,7 +466,7 @@ bind_local_variables(VALUE bindval)
* call-seq:
* binding.local_variable_get(symbol) -> obj
*
- * Returns a +value+ of local variable +symbol+.
+ * Returns the value of the local variable +symbol+.
*
* def foo
* a = 1
@@ -483,7 +474,7 @@ bind_local_variables(VALUE bindval)
* binding.local_variable_get(:b) #=> NameError
* end
*
- * This method is short version of the following code.
+ * This method is the short version of the following code:
*
* binding.eval("#{symbol}")
*
@@ -494,15 +485,17 @@ bind_local_variable_get(VALUE bindval, VALUE sym)
ID lid = check_local_id(bindval, &sym);
const rb_binding_t *bind;
const VALUE *ptr;
+ const rb_env_t *env;
if (!lid) goto undefined;
GetBindingPtr(bindval, bind);
- if ((ptr = get_local_variable_ptr(VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block)), lid)) == NULL) {
+ env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));
+ if ((ptr = get_local_variable_ptr(&env, lid)) == NULL) {
sym = ID2SYM(lid);
undefined:
- rb_name_err_raise("local variable `%1$s' not defined for %2$s",
+ rb_name_err_raise("local variable `%1$s' is not defined for %2$s",
bindval, sym);
}
@@ -520,18 +513,19 @@ bind_local_variable_get(VALUE bindval, VALUE sym)
* bind = binding
* bind.local_variable_set(:a, 2) # set existing local variable `a'
* bind.local_variable_set(:b, 3) # create new local variable `b'
- * # `b' exists only in binding.
- * p bind.local_variable_get(:a) #=> 2
- * p bind.local_variable_get(:b) #=> 3
- * p a #=> 2
- * p b #=> NameError
+ * # `b' exists only in binding
+ *
+ * p bind.local_variable_get(:a) #=> 2
+ * p bind.local_variable_get(:b) #=> 3
+ * p a #=> 2
+ * p b #=> NameError
* end
*
- * This method is a similar behavior of the following code
+ * This method behaves similarly to the following code:
*
* binding.eval("#{symbol} = #{obj}")
*
- * if obj can be dumped in Ruby code.
+ * if +obj+ can be dumped in Ruby code.
*/
static VALUE
bind_local_variable_set(VALUE bindval, VALUE sym, VALUE val)
@@ -545,9 +539,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) {
+ if ((ptr = get_local_variable_ptr(&env, lid)) == NULL) {
/* not found. create new env */
- ptr = rb_binding_add_dynavars(bind, 1, &lid);
+ ptr = rb_binding_add_dynavars(bindval, bind, 1, &lid);
env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));
}
@@ -560,7 +554,7 @@ bind_local_variable_set(VALUE bindval, VALUE sym, VALUE val)
* call-seq:
* binding.local_variable_defined?(symbol) -> obj
*
- * Returns a +true+ if a local variable +symbol+ exists.
+ * Returns +true+ if a local variable +symbol+ exists.
*
* def foo
* a = 1
@@ -568,7 +562,7 @@ bind_local_variable_set(VALUE bindval, VALUE sym, VALUE val)
* binding.local_variable_defined?(:b) #=> false
* end
*
- * This method is short version of the following code.
+ * This method is the short version of the following code:
*
* binding.eval("defined?(#{symbol}) == 'local-variable'")
*
@@ -578,11 +572,13 @@ bind_local_variable_defined_p(VALUE bindval, VALUE sym)
{
ID lid = check_local_id(bindval, &sym);
const rb_binding_t *bind;
+ const rb_env_t *env;
if (!lid) return Qfalse;
GetBindingPtr(bindval, bind);
- return get_local_variable_ptr(VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block)), lid) ? Qtrue : Qfalse;
+ env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));
+ return get_local_variable_ptr(&env, lid) ? Qtrue : Qfalse;
}
/*
@@ -599,6 +595,24 @@ bind_receiver(VALUE bindval)
return vm_block_self(&bind->block);
}
+/*
+ * call-seq:
+ * binding.source_location -> [String, Integer]
+ *
+ * Returns the Ruby source filename and line number of the binding object.
+ */
+static VALUE
+bind_location(VALUE bindval)
+{
+ VALUE loc[2];
+ const rb_binding_t *bind;
+ GetBindingPtr(bindval, bind);
+ loc[0] = pathobj_path(bind->pathobj);
+ loc[1] = INT2FIX(bind->first_lineno);
+
+ return rb_ary_new4(2, loc);
+}
+
static VALUE
cfunc_proc_new(VALUE klass, VALUE ifunc, int8_t is_lambda)
{
@@ -634,16 +648,47 @@ sym_proc_new(VALUE klass, VALUE sym)
return procval;
}
-VALUE
+struct vm_ifunc *
+rb_vm_ifunc_new(VALUE (*func)(ANYARGS), const void *data, int min_argc, int max_argc)
+{
+ union {
+ 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) ||
+#endif
+ 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) ||
+#endif
+ 0) {
+ rb_raise(rb_eRangeError, "maximum argument number out of range: %d",
+ max_argc);
+ }
+ arity.argc.min = min_argc;
+ arity.argc.max = max_argc;
+ return IFUNC_NEW(func, data, arity.packed);
+}
+
+MJIT_FUNC_EXPORTED VALUE
rb_func_proc_new(rb_block_call_func_t func, VALUE val)
{
- return cfunc_proc_new(rb_cProc, (VALUE)IFUNC_NEW(func, val, 0), 0);
+ struct vm_ifunc *ifunc = rb_vm_ifunc_proc_new(func, (void *)val);
+ return cfunc_proc_new(rb_cProc, (VALUE)ifunc, 0);
}
VALUE
-rb_func_lambda_new(rb_block_call_func_t func, VALUE val)
+rb_func_lambda_new(rb_block_call_func_t func, VALUE val, int min_argc, int max_argc)
{
- return cfunc_proc_new(rb_cProc, (VALUE)IFUNC_NEW(func, val, 0), 1);
+ struct vm_ifunc *ifunc = rb_vm_ifunc_new(func, (void *)val, min_argc, max_argc);
+ return cfunc_proc_new(rb_cProc, (VALUE)ifunc, 1);
}
static const char proc_without_block[] = "tried to create Proc object without a block";
@@ -652,8 +697,8 @@ static VALUE
proc_new(VALUE klass, int8_t is_lambda)
{
VALUE procval;
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp;
+ const rb_execution_context_t *ec = GET_EC();
+ rb_control_frame_t *cfp = ec->cfp;
VALUE block_handler;
if ((block_handler = rb_vm_frame_block_handler(cfp)) == VM_BLOCK_HANDLER_NONE) {
@@ -661,13 +706,6 @@ proc_new(VALUE klass, int8_t is_lambda)
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
if ((block_handler = rb_vm_frame_block_handler(cfp)) != VM_BLOCK_HANDLER_NONE) {
- const VALUE *lep = rb_vm_ep_local_ep(cfp->ep);
-
- if (VM_ENV_ESCAPED_P(lep)) {
- procval = VM_ENV_PROCVAL(lep);
- goto return_existing_proc;
- }
-
if (is_lambda) {
rb_warn(proc_without_block);
}
@@ -685,13 +723,12 @@ proc_new(VALUE klass, int8_t is_lambda)
case block_handler_type_proc:
procval = VM_BH_TO_PROC(block_handler);
- return_existing_proc:
if (RBASIC_CLASS(procval) == klass) {
return procval;
}
else {
- VALUE newprocval = proc_dup(procval);
- RBASIC_SET_CLASS(newprocval, klass);
+ VALUE newprocval = rb_proc_dup(procval);
+ RBASIC_SET_CLASS(newprocval, klass);
return newprocval;
}
break;
@@ -704,7 +741,7 @@ proc_new(VALUE klass, int8_t is_lambda)
case block_handler_type_ifunc:
case block_handler_type_iseq:
- return rb_vm_make_proc_lambda(th, 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);
}
VM_UNREACHABLE(proc_new);
return Qnil;
@@ -763,58 +800,55 @@ rb_block_lambda(void)
return proc_new(rb_cProc, TRUE);
}
-/* Document-method: ===
+/* Document-method: Proc#===
*
* call-seq:
* proc === obj -> result_of_proc
*
- * Invokes the block with +obj+ as the proc's parameter like Proc#call. It
- * is to allow a proc object to be a target of +when+ clause in a case
- * statement.
+ * Invokes the block with +obj+ as the proc's parameter like Proc#call.
+ * This allows a proc object to be the target of a +when+ clause
+ * in a case statement.
*/
/* CHECKME: are the argument checking semantics correct? */
/*
- * Document-method: call
- * Document-method: []
- * Document-method: yield
+ * Document-method: Proc#[]
+ * Document-method: Proc#call
+ * Document-method: Proc#yield
*
* call-seq:
* prc.call(params,...) -> obj
* prc[params,...] -> obj
* prc.(params,...) -> obj
+ * prc.yield(params,...) -> obj
*
* Invokes the block, setting the block's parameters to the values in
* <i>params</i> using something close to method calling semantics.
- * Generates a warning if multiple values are passed to a proc that
- * expects just one (previously this silently converted the parameters
- * to an array). Note that <code>prc.()</code> invokes
- * <code>prc.call()</code> with the parameters given. It's a syntax sugar to
- * hide "call".
+ * Returns the value of the last expression evaluated in the block.
*
- * Returns the value of the last expression evaluated in the block. See
- * also Proc#yield.
+ * a_proc = Proc.new {|scalar, *values| values.map {|value| value*scalar } }
+ * a_proc.call(9, 1, 2, 3) #=> [9, 18, 27]
+ * a_proc[9, 1, 2, 3] #=> [9, 18, 27]
+ * a_proc.(9, 1, 2, 3) #=> [9, 18, 27]
+ * a_proc.yield(9, 1, 2, 3) #=> [9, 18, 27]
*
- * a_proc = Proc.new { |scalar, *values| values.collect { |value| value*scalar } }
- * a_proc.call(9, 1, 2, 3) #=> [9, 18, 27]
- * a_proc[9, 1, 2, 3] #=> [9, 18, 27]
- * a_proc.(9, 1, 2, 3) #=> [9, 18, 27]
+ * Note that <code>prc.()</code> invokes <code>prc.call()</code> with
+ * the parameters given. It's syntactic sugar to hide "call".
*
* For procs created using <code>lambda</code> or <code>->()</code> an error
- * is generated if the wrong number of parameters are passed to a Proc with
- * multiple parameters. For procs created using <code>Proc.new</code> or
- * <code>Kernel.proc</code>, extra parameters are silently discarded.
+ * is generated if the wrong number of parameters are passed to the proc.
+ * For procs created using <code>Proc.new</code> or <code>Kernel.proc</code>,
+ * extra parameters are silently discarded and missing parameters are
+ * set to +nil+.
*
- * a_proc = lambda {|a,b| a}
- * a_proc.call(1,2,3)
+ * a_proc = proc {|a,b| [a,b] }
+ * a_proc.call(1) #=> [1, nil]
*
- * <em>produces:</em>
- *
- * prog.rb:4:in `block in <main>': wrong number of arguments (given 3, expected 2) (ArgumentError)
- * from prog.rb:5:in `call'
- * from prog.rb:5:in `<main>'
+ * a_proc = lambda {|a,b| [a,b] }
+ * a_proc.call(1) # ArgumentError: wrong number of arguments (given 1, expected 2)
*
+ * See also Proc#lambda?.
*/
#if 0
static VALUE
@@ -844,7 +878,7 @@ rb_proc_call(VALUE self, VALUE args)
VALUE vret;
rb_proc_t *proc;
GetProcPtr(self, proc);
- vret = rb_vm_invoke_proc(GET_THREAD(), proc,
+ vret = rb_vm_invoke_proc(GET_EC(), proc,
check_argc(RARRAY_LEN(args)), RARRAY_CONST_PTR(args),
VM_BLOCK_HANDLER_NONE);
RB_GC_GUARD(self);
@@ -861,11 +895,11 @@ proc_to_block_handler(VALUE procval)
VALUE
rb_proc_call_with_block(VALUE self, int argc, const VALUE *argv, VALUE passed_procval)
{
- rb_thread_t *th = GET_THREAD();
+ rb_execution_context_t *ec = GET_EC();
VALUE vret;
rb_proc_t *proc;
GetProcPtr(self, proc);
- vret = rb_vm_invoke_proc(th, proc, argc, argv, proc_to_block_handler(passed_procval));
+ vret = rb_vm_invoke_proc(ec, proc, argc, argv, proc_to_block_handler(passed_procval));
RB_GC_GUARD(self);
return vret;
}
@@ -882,7 +916,7 @@ rb_proc_call_with_block(VALUE self, int argc, const VALUE *argv, VALUE passed_pr
* number of mandatory arguments, with the exception for blocks that
* are not lambdas and have only a finite number of optional arguments;
* in this latter case, returns n.
- * Keywords arguments will considered as a single additional argument,
+ * Keyword arguments will be considered as a single additional argument,
* that argument being mandatory if any keyword argument is mandatory.
* A <code>proc</code> with no argument declarations
* is the same as a block declaring <code>||</code> as its arguments.
@@ -898,16 +932,16 @@ rb_proc_call_with_block(VALUE self, int argc, const VALUE *argv, VALUE passed_pr
* proc { |x:, y:, z:0| }.arity #=> 1
* proc { |*a, x:, y:0| }.arity #=> -2
*
- * proc { |x=0| }.arity #=> 0
- * lambda { |x=0| }.arity #=> -1
- * proc { |x=0, y| }.arity #=> 1
- * lambda { |x=0, y| }.arity #=> -2
- * proc { |x=0, y=0| }.arity #=> 0
- * lambda { |x=0, y=0| }.arity #=> -1
- * proc { |x, y=0| }.arity #=> 1
- * lambda { |x, y=0| }.arity #=> -2
- * proc { |(x, y), z=0| }.arity #=> 1
- * lambda { |(x, y), z=0| }.arity #=> -2
+ * proc { |a=0| }.arity #=> 0
+ * lambda { |a=0| }.arity #=> -1
+ * proc { |a=0, b| }.arity #=> 1
+ * lambda { |a=0, b| }.arity #=> -2
+ * proc { |a=0, b=0| }.arity #=> 0
+ * lambda { |a=0, b=0| }.arity #=> -1
+ * proc { |a, b=0| }.arity #=> 1
+ * lambda { |a, b=0| }.arity #=> -2
+ * proc { |(a, b), c=0| }.arity #=> 1
+ * lambda { |(a, b), c=0| }.arity #=> -2
* proc { |a, x:0, y:0| }.arity #=> 1
* lambda { |a, x:0, y:0| }.arity #=> -2
*/
@@ -930,13 +964,15 @@ rb_iseq_min_max_arity(const rb_iseq_t *iseq, int *max)
}
static int
-rb_block_min_max_arity(const struct rb_block *block, int *max)
+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(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:
- return rb_block_min_max_arity(vm_proc_block(block->as.proc), max);
+ block = vm_proc_block(block->as.proc);
+ goto again;
case block_type_ifunc:
{
const struct vm_ifunc *ifunc = block->as.captured.code.ifunc;
@@ -944,8 +980,9 @@ rb_block_min_max_arity(const struct rb_block *block, int *max)
/* e.g. method(:foo).to_proc.arity */
return method_min_max_arity((VALUE)ifunc->data, max);
}
+ *max = ifunc->argc.max;
+ return ifunc->argc.min;
}
- /* fall through */
case block_type_symbol:
break;
}
@@ -964,7 +1001,7 @@ rb_proc_min_max_arity(VALUE self, int *max)
{
rb_proc_t *proc;
GetProcPtr(self, proc);
- return rb_block_min_max_arity(&proc->block, max);
+ return rb_vm_block_min_max_arity(&proc->block, max);
}
int
@@ -973,7 +1010,7 @@ rb_proc_arity(VALUE self)
rb_proc_t *proc;
int max, min;
GetProcPtr(self, proc);
- min = rb_block_min_max_arity(&proc->block, &max);
+ min = rb_vm_block_min_max_arity(&proc->block, &max);
return (proc->is_lambda ? min == max : max != UNLIMITED_ARGUMENTS) ? min : -min-1;
}
@@ -1003,8 +1040,8 @@ int
rb_block_arity(void)
{
int min, max;
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp;
+ const rb_execution_context_t *ec = GET_EC();
+ rb_control_frame_t *cfp = ec->cfp;
VALUE block_handler = rb_vm_frame_block_handler(cfp);
struct rb_block block;
@@ -1013,7 +1050,7 @@ rb_block_arity(void)
}
block_setup(&block, block_handler);
- min = rb_block_min_max_arity(&block, &max);
+ min = rb_vm_block_min_max_arity(&block, &max);
switch (vm_block_type(&block)) {
case block_handler_type_symbol:
@@ -1033,6 +1070,22 @@ rb_block_arity(void)
}
}
+int
+rb_block_min_max_arity(int *max)
+{
+ const rb_execution_context_t *ec = GET_EC();
+ rb_control_frame_t *cfp = ec->cfp;
+ VALUE block_handler = rb_vm_frame_block_handler(cfp);
+ struct rb_block block;
+
+ if (block_handler == VM_BLOCK_HANDLER_NONE) {
+ rb_raise(rb_eArgError, "no block given");
+ }
+
+ block_setup(&block, block_handler);
+ return rb_vm_block_min_max_arity(&block, max);
+}
+
const rb_iseq_t *
rb_proc_get_iseq(VALUE self, int *is_proc)
{
@@ -1075,13 +1128,9 @@ iseq_location(const rb_iseq_t *iseq)
if (!iseq) return Qnil;
rb_iseq_check(iseq);
- loc[0] = iseq->body->location.path;
- if (iseq->body->line_info_table) {
- loc[1] = rb_iseq_first_lineno(iseq);
- }
- else {
- loc[1] = Qnil;
- }
+ loc[0] = rb_iseq_path(iseq);
+ loc[1] = iseq->body->location.first_lineno;
+
return rb_ary_new4(2, loc);
}
@@ -1090,7 +1139,7 @@ iseq_location(const rb_iseq_t *iseq)
* prc.source_location -> [String, Integer]
*
* Returns the Ruby source filename and line number containing this proc
- * or +nil+ if this proc was not defined in Ruby (i.e. native)
+ * or +nil+ if this proc was not defined in Ruby (i.e. native).
*/
VALUE
@@ -1099,8 +1148,8 @@ rb_proc_location(VALUE self)
return iseq_location(rb_proc_get_iseq(self, 0));
}
-static VALUE
-unnamed_parameters(int arity)
+VALUE
+rb_unnamed_parameters(int arity)
{
VALUE a, param = rb_ary_new2((arity < 0) ? -arity : arity);
int n = (arity < 0) ? ~arity : arity;
@@ -1134,7 +1183,7 @@ rb_proc_parameters(VALUE self)
int is_proc;
const rb_iseq_t *iseq = rb_proc_get_iseq(self, &is_proc);
if (!iseq) {
- return unnamed_parameters(rb_proc_arity(self));
+ return rb_unnamed_parameters(rb_proc_arity(self));
}
return rb_iseq_parameters(iseq, is_proc);
}
@@ -1149,7 +1198,7 @@ rb_hash_proc(st_index_t hash, VALUE prc)
return rb_hash_uint(hash, (st_index_t)proc->block.as.captured.ep >> 16);
}
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_sym_to_proc(VALUE sym)
{
static VALUE sym_proc_cache = Qfalse;
@@ -1157,7 +1206,6 @@ rb_sym_to_proc(VALUE sym)
VALUE proc;
long index;
ID id;
- VALUE *aryp;
if (!sym_proc_cache) {
sym_proc_cache = rb_ary_tmp_new(SYM_PROC_CACHE_SIZE * 2);
@@ -1168,14 +1216,13 @@ rb_sym_to_proc(VALUE sym)
id = SYM2ID(sym);
index = (id % SYM_PROC_CACHE_SIZE) << 1;
- aryp = RARRAY_PTR(sym_proc_cache);
- if (aryp[index] == sym) {
- return aryp[index + 1];
+ if (RARRAY_AREF(sym_proc_cache, index) == sym) {
+ return RARRAY_AREF(sym_proc_cache, index + 1);
}
else {
- proc = sym_proc_new(rb_cProc, ID2SYM(id));
- aryp[index] = sym;
- aryp[index + 1] = proc;
+ 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;
}
}
@@ -1199,24 +1246,11 @@ proc_hash(VALUE self)
return ST2FIX(hash);
}
-/*
- * call-seq:
- * prc.to_s -> string
- *
- * Returns the unique identifier for this proc, along with
- * an indication of where the proc was defined.
- */
-
-static VALUE
-proc_to_s_(VALUE self, const rb_proc_t *proc)
+VALUE
+rb_block_to_s(VALUE self, const struct rb_block *block, const char *additional_info)
{
- VALUE str = 0;
- const char *cname = rb_obj_classname(self);
- const struct rb_block *block;
- const char *is_lambda;
-
- block = &proc->block;
- is_lambda = proc->is_lambda ? " (lambda)" : "";
+ VALUE cname = rb_obj_class(self);
+ VALUE str = rb_sprintf("#<%"PRIsVALUE":", cname);
again:
switch (vm_block_type(block)) {
@@ -1226,36 +1260,39 @@ proc_to_s_(VALUE self, const rb_proc_t *proc)
case block_type_iseq:
{
const rb_iseq_t *iseq = rb_iseq_check(block->as.captured.code.iseq);
- int first_lineno = 0;
- if (iseq->body->line_info_table) {
- first_lineno = FIX2INT(rb_iseq_first_lineno(iseq));
- }
- str = rb_sprintf("#<%s:%p@%"PRIsVALUE":%d%s>", cname, (void *)self,
- iseq->body->location.path, first_lineno, is_lambda);
+ rb_str_catf(str, "%p@%"PRIsVALUE":%d", (void *)self,
+ rb_iseq_path(iseq),
+ FIX2INT(iseq->body->location.first_lineno));
}
break;
case block_type_symbol:
- str = rb_sprintf("#<%s:%p(&%+"PRIsVALUE")%s>", cname, (void *)self,
- block->as.symbol, is_lambda);
+ rb_str_catf(str, "%p(&%+"PRIsVALUE")", (void *)self, block->as.symbol);
break;
case block_type_ifunc:
- str = rb_sprintf("#<%s:%p%s>", cname, proc->block.as.captured.code.ifunc,
- is_lambda);
+ rb_str_catf(str, "%p", (void *)block->as.captured.code.ifunc);
break;
}
- if (OBJ_TAINTED(self)) {
- OBJ_TAINT(str);
- }
+ if (additional_info) rb_str_cat_cstr(str, additional_info);
+ rb_str_cat_cstr(str, ">");
+ OBJ_INFECT_RAW(str, self);
return str;
}
+/*
+ * call-seq:
+ * prc.to_s -> string
+ *
+ * Returns the unique identifier for this proc, along with
+ * an indication of where the proc was defined.
+ */
+
static VALUE
proc_to_s(VALUE self)
{
const rb_proc_t *proc;
GetProcPtr(self, proc);
- return proc_to_s_(self, proc);
+ return rb_block_to_s(self, &proc->block, proc->is_lambda ? " (lambda)" : NULL);
}
/*
@@ -1279,6 +1316,7 @@ bm_mark(void *ptr)
struct METHOD *data = ptr;
rb_gc_mark(data->recv);
rb_gc_mark(data->klass);
+ rb_gc_mark(data->iclass);
rb_gc_mark((VALUE)data->me);
}
@@ -1346,7 +1384,7 @@ mnew_missing(VALUE klass, VALUE obj, ID id, VALUE mclass)
}
static VALUE
-mnew_internal(const rb_method_entry_t *me, VALUE klass,
+mnew_internal(const rb_method_entry_t *me, VALUE klass, VALUE iclass,
VALUE obj, ID id, VALUE mclass, int scope, int error)
{
struct METHOD *data;
@@ -1372,24 +1410,21 @@ mnew_internal(const rb_method_entry_t *me, VALUE klass,
if (me->defined_class) {
VALUE klass = RCLASS_SUPER(RCLASS_ORIGIN(me->defined_class));
id = me->def->original_id;
- me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(klass, id);
+ me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(klass, id, &iclass);
}
else {
VALUE klass = RCLASS_SUPER(me->owner);
id = me->def->original_id;
- me = rb_method_entry_without_refinements(klass, id);
+ me = rb_method_entry_without_refinements(klass, id, &iclass);
}
goto again;
}
- while (klass != me->owner && (FL_TEST(klass, FL_SINGLETON) || RB_TYPE_P(klass, T_ICLASS))) {
- klass = RCLASS_SUPER(klass);
- }
-
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);
+ RB_OBJ_WRITE(method, &data->iclass, iclass);
RB_OBJ_WRITE(method, &data->me, me);
OBJ_INFECT(method, klass);
@@ -1397,24 +1432,25 @@ mnew_internal(const rb_method_entry_t *me, VALUE klass,
}
static VALUE
-mnew_from_me(const rb_method_entry_t *me, VALUE klass,
+mnew_from_me(const rb_method_entry_t *me, VALUE klass, VALUE iclass,
VALUE obj, ID id, VALUE mclass, int scope)
{
- return mnew_internal(me, klass, obj, id, mclass, scope, TRUE);
+ return mnew_internal(me, klass, iclass, obj, id, mclass, scope, TRUE);
}
static VALUE
mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope)
{
const rb_method_entry_t *me;
+ VALUE iclass = Qnil;
if (obj == Qundef) { /* UnboundMethod */
- me = rb_method_entry_without_refinements(klass, id);
+ me = rb_method_entry_without_refinements(klass, id, &iclass);
}
else {
- me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(klass, id);
+ me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(klass, id, &iclass);
}
- return mnew_from_me(me, klass, obj, id, mclass, scope);
+ return mnew_from_me(me, klass, iclass, obj, id, mclass, scope);
}
static inline VALUE
@@ -1426,7 +1462,7 @@ method_entry_defined_class(const rb_method_entry_t *me)
/**********************************************************************
*
- * Document-class : Method
+ * Document-class: Method
*
* Method objects are created by <code>Object#method</code>, and are
* associated with a particular object (not just with a class). They
@@ -1446,6 +1482,11 @@ method_entry_defined_class(const rb_method_entry_t *me)
* meth.call(9) #=> 81
* [ 1, 2, 3 ].collect(&meth) #=> [1, 4, 9]
*
+ * [ 1, 2, 3 ].each(&method(:puts)) #=> prints 1, 2, 3
+ *
+ * require 'date'
+ * %w[2017-03-01 2017-03-02].collect(&Date.method(:parse))
+ * #=> [#<Date: 2017-03-01 ((2457814j,0s,0n),+0s,2299161j)>, #<Date: 2017-03-02 ((2457815j,0s,0n),+0s,2299161j)>]
*/
/*
@@ -1506,7 +1547,7 @@ method_hash(VALUE method)
hash = rb_hash_method_entry(hash, m->me);
hash = rb_hash_end(hash);
- return INT2FIX(hash);
+ return ST2FIX(hash);
}
/*
@@ -1540,6 +1581,8 @@ method_unbind(VALUE obj)
* meth.receiver -> object
*
* Returns the bound receiver of the method object.
+ *
+ * (1..3).method(:map).receiver # => 1..3
*/
static VALUE
@@ -1572,6 +1615,12 @@ method_name(VALUE obj)
* meth.original_name -> symbol
*
* Returns the original name of the method.
+ *
+ * class C
+ * def foo; end
+ * alias bar foo
+ * end
+ * C.instance_method(:bar).original_name # => :foo
*/
static VALUE
@@ -1588,6 +1637,9 @@ method_original_name(VALUE obj)
* meth.owner -> class_or_module
*
* Returns the class or module that defines the method.
+ * See also receiver.
+ *
+ * (1..3).method(:map).owner #=> Enumerable
*/
static VALUE
@@ -1601,7 +1653,7 @@ method_owner(VALUE obj)
void
rb_method_name_error(VALUE klass, VALUE str)
{
-#define MSG(s) rb_fstring_cstr("undefined method `%1$s' for"s" `%2$s'")
+#define MSG(s) rb_fstring_lit("undefined method `%1$s' for"s" `%2$s'")
VALUE c = klass;
VALUE s;
@@ -1670,6 +1722,18 @@ obj_method(VALUE obj, VALUE vid, int scope)
* l = Demo.new('Fred')
* m = l.method("hello")
* m.call #=> "Hello, @iv = Fred"
+ *
+ * Note that <code>Method</code> implements <code>to_proc</code> method,
+ * which means it can be used with iterators.
+ *
+ * [ 1, 2, 3 ].each(&method(:puts)) # => prints 3 lines to stdout
+ *
+ * out = File.open('test.txt', 'w')
+ * [ 1, 2, 3 ].each(&out.method(:puts)) # => prints 3 lines to file
+ *
+ * require 'date'
+ * %w[2017-03-01 2017-03-02].collect(&Date.method(:parse))
+ * #=> [#<Date: 2017-03-01 ((2457814j,0s,0n),+0s,2299161j)>, #<Date: 2017-03-02 ((2457815j,0s,0n),+0s,2299161j)>]
*/
VALUE
@@ -1719,26 +1783,28 @@ VALUE
rb_obj_singleton_method(VALUE obj, VALUE vid)
{
const rb_method_entry_t *me;
- VALUE klass;
+ VALUE klass = rb_singleton_class_get(obj);
ID id = rb_check_id(&vid);
+ if (NIL_P(klass) || NIL_P(klass = RCLASS_ORIGIN(klass))) {
+ undef:
+ rb_name_err_raise("undefined singleton method `%1$s' for `%2$s'",
+ obj, vid);
+ }
if (!id) {
- if (!NIL_P(klass = rb_singleton_class_get(obj)) &&
- respond_to_missing_p(klass, obj, vid, FALSE)) {
+ if (respond_to_missing_p(klass, obj, vid, FALSE)) {
id = rb_intern_str(vid);
return mnew_missing(klass, obj, id, rb_cMethod);
}
- undef:
- rb_name_err_raise("undefined singleton method `%1$s' for `%2$s'",
- obj, vid);
+ goto undef;
}
- if (NIL_P(klass = rb_singleton_class_get(obj)) ||
- UNDEFINED_METHOD_ENTRY_P(me = rb_method_entry_at(klass, id)) ||
+ me = rb_method_entry_at(klass, id);
+ if (UNDEFINED_METHOD_ENTRY_P(me) ||
UNDEFINED_REFINED_METHOD_P(me->def)) {
vid = ID2SYM(id);
goto undef;
}
- return mnew_from_me(me, klass, obj, id, rb_cMethod, FALSE);
+ return mnew_from_me(me, klass, klass, obj, id, rb_cMethod, FALSE);
}
/*
@@ -1807,16 +1873,14 @@ rb_mod_public_instance_method(VALUE mod, VALUE vid)
* Defines an instance method in the receiver. The _method_
* parameter can be a +Proc+, a +Method+ or an +UnboundMethod+ object.
* If a block is specified, it is used as the method body. This block
- * is evaluated using <code>instance_eval</code>, a point that is
- * tricky to demonstrate because <code>define_method</code> is private.
- * (This is why we resort to the +send+ hack in this example.)
+ * is evaluated using <code>instance_eval</code>.
*
* class A
* def fred
* puts "In Fred"
* end
* def create_method(name, &block)
- * self.class.send(:define_method, name, &block)
+ * self.class.define_method(name, &block)
* end
* define_method(:wilma) { puts "Charge it!" }
* end
@@ -1858,8 +1922,8 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod)
#if PROC_NEW_REQUIRES_BLOCK
body = rb_block_lambda();
#else
- rb_thread_t *th = GET_THREAD();
- VALUE block_handler = rb_vm_frame_block_handler(th->cfp);
+ const rb_execution_context_t *ec = GET_EC();
+ VALUE block_handler = rb_vm_frame_block_handler(ec->cfp);
if (block_handler == VM_BLOCK_HANDLER_NONE) rb_raise(rb_eArgError, proc_without_block);
switch (vm_block_handler_type(block_handler)) {
@@ -1871,7 +1935,7 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod)
break;
case block_handler_type_iseq:
case block_handler_type_ifunc:
- body = rb_vm_make_proc_lambda(th, VM_BH_TO_CAPT_BLOCK(block_handler), rb_cProc, TRUE);
+ body = rb_vm_make_lambda(ec, VM_BH_TO_CAPT_BLOCK(block_handler), rb_cProc);
}
#endif
}
@@ -1903,7 +1967,7 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod)
else {
rb_raise(rb_eTypeError,
"bind argument must be a subclass of % "PRIsVALUE,
- rb_class_name(method->me->owner));
+ method->me->owner);
}
}
rb_method_entry_set(mod, id, method->me, scope_visi->method_visi);
@@ -1913,7 +1977,7 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod)
RB_GC_GUARD(body);
}
else {
- VALUE procval = proc_dup(body);
+ VALUE procval = rb_proc_dup(body);
if (vm_proc_iseq(procval) != NULL) {
rb_proc_t *proc;
GetProcPtr(procval, proc);
@@ -2018,10 +2082,40 @@ method_clone(VALUE self)
return clone;
}
+/* Document-method: Method#===
+ *
+ * call-seq:
+ * method === obj -> result_of_method
+ *
+ * Invokes the method with +obj+ as the parameter like #call.
+ * This allows a method object to be the target of a +when+ clause
+ * in a case statement.
+ *
+ * require 'prime'
+ *
+ * case 1373
+ * when Prime.method(:prime?)
+ * # ...
+ * end
+ */
+
+
+/* Document-method: Method#[]
+ *
+ * call-seq:
+ * meth[args, ...] -> obj
+ *
+ * Invokes the <i>meth</i> with the specified arguments, returning the
+ * method's return value, like #call.
+ *
+ * m = 12.method("+")
+ * m[3] #=> 15
+ * m[20] #=> 32
+ */
+
/*
* call-seq:
* meth.call(args, ...) -> obj
- * meth[args, ...] -> obj
*
* Invokes the <i>meth</i> with the specified arguments, returning the
* method's return value.
@@ -2046,32 +2140,32 @@ method_callable_method_entry(const struct METHOD *data)
}
static inline VALUE
-call_method_data(rb_thread_t *th, const struct METHOD *data,
+call_method_data(rb_execution_context_t *ec, const struct METHOD *data,
int argc, const VALUE *argv, VALUE passed_procval)
{
- vm_passed_block_handler_set(th, proc_to_block_handler(passed_procval));
- return rb_vm_call(th, data->recv, data->me->called_id, argc, argv,
+ vm_passed_block_handler_set(ec, proc_to_block_handler(passed_procval));
+ return rb_vm_call(ec, data->recv, data->me->called_id, argc, argv,
method_callable_method_entry(data));
}
static VALUE
-call_method_data_safe(rb_thread_t *th, const struct METHOD *data,
+call_method_data_safe(rb_execution_context_t *ec, const struct METHOD *data,
int argc, const VALUE *argv, VALUE passed_procval,
int safe)
{
VALUE result = Qnil; /* OK */
- int state;
+ enum ruby_tag_type state;
- TH_PUSH_TAG(th);
- if ((state = TH_EXEC_TAG()) == 0) {
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
/* result is used only if state == 0, no exceptions is caught. */
/* otherwise it doesn't matter even if clobbered. */
- NO_CLOBBERED(result) = call_method_data(th, data, argc, argv, passed_procval);
+ NO_CLOBBERED(result) = call_method_data(ec, data, argc, argv, passed_procval);
}
- TH_POP_TAG();
+ EC_POP_TAG();
rb_set_safe_level_force(safe);
if (state)
- TH_JUMP_TAG(th, state);
+ EC_JUMP_TAG(ec, state);
return result;
}
@@ -2079,7 +2173,7 @@ VALUE
rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE passed_procval)
{
const struct METHOD *data;
- rb_thread_t *const th = GET_THREAD();
+ rb_execution_context_t *ec = GET_EC();
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
if (data->recv == Qundef) {
@@ -2090,10 +2184,10 @@ rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE passe
int safe = rb_safe_level();
if (safe < safe_level_to_run) {
rb_set_safe_level_force(safe_level_to_run);
- return call_method_data_safe(th, data, argc, argv, passed_procval, safe);
+ return call_method_data_safe(ec, data, argc, argv, passed_procval, safe);
}
}
- return call_method_data(th, data, argc, argv, passed_procval);
+ return call_method_data(ec, data, argc, argv, passed_procval);
}
/**********************************************************************
@@ -2205,7 +2299,7 @@ umethod_bind(VALUE method, VALUE recv)
}
else {
rb_raise(rb_eTypeError, "bind argument must be an instance of % "PRIsVALUE,
- rb_class_name(methclass));
+ methclass);
}
}
@@ -2240,6 +2334,7 @@ rb_method_entry_min_max_arity(const rb_method_entry_t *me, int *max)
{
const rb_method_definition_t *def = me->def;
+ again:
if (!def) return *max = 0;
switch (def->type) {
case VM_METHOD_TYPE_CFUNC:
@@ -2256,13 +2351,12 @@ rb_method_entry_min_max_arity(const rb_method_entry_t *me, int *max)
case VM_METHOD_TYPE_IVAR:
return *max = 0;
case VM_METHOD_TYPE_ALIAS:
- return rb_method_entry_min_max_arity(def->body.alias.original_me, max);
+ def = def->body.alias.original_me->def;
+ goto again;
case VM_METHOD_TYPE_BMETHOD:
- return rb_proc_min_max_arity(def->body.proc, max);
- case VM_METHOD_TYPE_ISEQ: {
- const rb_iseq_t *iseq = rb_iseq_check(def->body.iseq.iseqptr);
- return rb_iseq_min_max_arity(iseq, max);
- }
+ 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);
case VM_METHOD_TYPE_UNDEF:
case VM_METHOD_TYPE_NOTIMPLEMENTED:
return *max = 0;
@@ -2277,6 +2371,9 @@ rb_method_entry_min_max_arity(const rb_method_entry_t *me, int *max)
case OPTIMIZED_METHOD_TYPE_CALL:
*max = UNLIMITED_ARGUMENTS;
return 0;
+ case OPTIMIZED_METHOD_TYPE_BLOCK_CALL:
+ *max = UNLIMITED_ARGUMENTS;
+ return 0;
default:
break;
}
@@ -2287,7 +2384,7 @@ rb_method_entry_min_max_arity(const rb_method_entry_t *me, int *max)
return 0;
}
rb_bug("rb_method_entry_min_max_arity: invalid method entry type (%d)", def->type);
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
int
@@ -2304,8 +2401,10 @@ rb_method_entry_arity(const rb_method_entry_t *me)
* Returns an indication of the number of arguments accepted by a
* method. Returns a nonnegative integer for methods that take a fixed
* number of arguments. For Ruby methods that take a variable number of
- * arguments, returns -n-1, where n is the number of required
- * arguments. For methods written in C, returns -1 if the call takes a
+ * arguments, returns -n-1, where n is the number of required arguments.
+ * Keyword arguments will be considered as a single additional argument,
+ * that argument being mandatory if any keyword argument is mandatory.
+ * For methods written in C, returns -1 if the call takes a
* variable number of arguments.
*
* class C
@@ -2315,6 +2414,10 @@ rb_method_entry_arity(const rb_method_entry_t *me)
* def four(a, b); end
* def five(a, b, *c); end
* def six(a, b, *c, &d); end
+ * def seven(a, b, x:0); end
+ * def eight(x:, y:); end
+ * def nine(x:, y:, **z); end
+ * def ten(*a, x:, y:); end
* end
* c = C.new
* c.method(:one).arity #=> 0
@@ -2323,6 +2426,10 @@ rb_method_entry_arity(const rb_method_entry_t *me)
* c.method(:four).arity #=> 2
* c.method(:five).arity #=> -3
* c.method(:six).arity #=> -3
+ * c.method(:seven).arity #=> -3
+ * c.method(:eight).arity #=> 1
+ * c.method(:nine).arity #=> 1
+ * c.method(:ten).arity #=> -2
*
* "cat".method(:size).arity #=> 0
* "cat".method(:replace).arity #=> 1
@@ -2383,8 +2490,8 @@ rb_obj_method_arity(VALUE obj, ID id)
return rb_mod_method_arity(CLASS_OF(obj), id);
}
-static inline const rb_method_definition_t *
-method_def(VALUE method)
+const rb_method_definition_t *
+rb_method_def(VALUE method)
{
const struct METHOD *data;
@@ -2399,7 +2506,7 @@ method_def_iseq(const rb_method_definition_t *def)
case VM_METHOD_TYPE_ISEQ:
return rb_iseq_check(def->body.iseq.iseqptr);
case VM_METHOD_TYPE_BMETHOD:
- return rb_proc_get_iseq(def->body.proc, 0);
+ 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);
case VM_METHOD_TYPE_CFUNC:
@@ -2419,13 +2526,13 @@ method_def_iseq(const rb_method_definition_t *def)
const rb_iseq_t *
rb_method_iseq(VALUE method)
{
- return method_def_iseq(method_def(method));
+ return method_def_iseq(rb_method_def(method));
}
static const rb_cref_t *
method_cref(VALUE method)
{
- const rb_method_definition_t *def = method_def(method);
+ const rb_method_definition_t *def = rb_method_def(method);
again:
switch (def->type) {
@@ -2475,13 +2582,13 @@ rb_obj_method_location(VALUE obj, ID id)
* meth.source_location -> [String, Integer]
*
* Returns the Ruby source filename and line number containing this method
- * or nil if this method was not defined in Ruby (i.e. native)
+ * or nil if this method was not defined in Ruby (i.e. native).
*/
VALUE
rb_method_location(VALUE method)
{
- return method_def_location(method_def(method));
+ return method_def_location(rb_method_def(method));
}
/*
@@ -2508,7 +2615,7 @@ rb_method_parameters(VALUE method)
{
const rb_iseq_t *iseq = rb_method_iseq(method);
if (!iseq) {
- return unnamed_parameters(method_arity(method));
+ return rb_unnamed_parameters(method_arity(method));
}
return rb_iseq_parameters(iseq, 0);
}
@@ -2518,9 +2625,13 @@ rb_method_parameters(VALUE method)
* meth.to_s -> string
* meth.inspect -> string
*
- * Returns the name of the underlying method.
+ * Returns a human-readable description of the underlying method.
*
* "cat".method(:count).inspect #=> "#<Method: String#count>"
+ * (1..3).method(:map).inspect #=> "#<Method: Range(Enumerable)#map>"
+ *
+ * In the latter case, the method description includes the "owner" of the
+ * original method (+Enumerable+ module, which is included into +Range+).
*/
static VALUE
@@ -2528,18 +2639,16 @@ method_inspect(VALUE method)
{
struct METHOD *data;
VALUE str;
- const char *s;
const char *sharp = "#";
VALUE mklass;
VALUE defined_class;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
- str = rb_str_buf_new2("#<");
- s = rb_obj_classname(method);
- rb_str_buf_cat2(str, s);
- rb_str_buf_cat2(str, ": ");
+ str = rb_sprintf("#<% "PRIsVALUE": ", rb_obj_class(method));
+ OBJ_INFECT_RAW(str, method);
- mklass = data->klass;
+ mklass = data->iclass;
+ if (!mklass) mklass = data->klass;
if (data->me->def->type == VM_METHOD_TYPE_ALIAS) {
defined_class = data->me->def->body.alias.original_me->owner;
@@ -2571,11 +2680,15 @@ method_inspect(VALUE method)
}
}
else {
- rb_str_buf_append(str, rb_class_name(mklass));
+ mklass = data->klass;
+ if (FL_TEST(mklass, FL_SINGLETON)) {
+ do {
+ mklass = RCLASS_SUPER(mklass);
+ } while (RB_TYPE_P(mklass, T_ICLASS));
+ }
+ rb_str_buf_append(str, rb_inspect(mklass));
if (defined_class != mklass) {
- rb_str_buf_cat2(str, "(");
- rb_str_buf_append(str, rb_class_name(defined_class));
- rb_str_buf_cat2(str, ")");
+ rb_str_catf(str, "(% "PRIsVALUE")", defined_class);
}
}
rb_str_buf_cat2(str, sharp);
@@ -2647,6 +2760,8 @@ method_to_proc(VALUE method)
return procval;
}
+extern VALUE rb_find_defined_class_by_owner(VALUE current_class, VALUE target_owner);
+
/*
* call-seq:
* meth.super_method -> method
@@ -2659,15 +2774,26 @@ static VALUE
method_super_method(VALUE method)
{
const struct METHOD *data;
- VALUE super_class;
+ VALUE super_class, iclass;
+ ID mid;
const rb_method_entry_t *me;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
- super_class = RCLASS_SUPER(method_entry_defined_class(data->me));
+ iclass = data->iclass;
+ if (!iclass) return Qnil;
+ if (data->me->def->type == VM_METHOD_TYPE_ALIAS && data->me->defined_class) {
+ super_class = RCLASS_SUPER(rb_find_defined_class_by_owner(data->me->defined_class,
+ data->me->def->body.alias.original_me->owner));
+ mid = data->me->def->body.alias.original_me->def->original_id;
+ }
+ else {
+ super_class = RCLASS_SUPER(RCLASS_ORIGIN(iclass));
+ mid = data->me->def->original_id;
+ }
if (!super_class) return Qnil;
- me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(super_class, data->me->called_id);
+ me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(super_class, mid, &iclass);
if (!me) return Qnil;
- return mnew_internal(me, super_class, data->recv, data->me->called_id, rb_obj_class(method), FALSE, FALSE);
+ return mnew_internal(me, me->owner, iclass, data->recv, mid, rb_obj_class(method), FALSE, FALSE);
}
/*
@@ -2725,9 +2851,7 @@ env_clone(const rb_env_t *env, const rb_cref_t *cref)
* call-seq:
* prc.binding -> binding
*
- * Returns the binding associated with <i>prc</i>. Note that
- * <code>Kernel#eval</code> accepts either a <code>Proc</code> or a
- * <code>Binding</code> object as its second parameter.
+ * Returns the binding associated with <i>prc</i>.
*
* def fred(param)
* proc {}
@@ -2767,12 +2891,15 @@ proc_binding(VALUE self)
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 */
- RB_OBJ_WRITE(env, &env->iseq, rb_iseq_new(NULL, rb_str_new2("<empty iseq>"), rb_str_new2("<empty_iseq>"), Qnil, 0, ISEQ_TYPE_TOP));
+ empty = rb_iseq_new(NULL, name, name, Qnil, 0, ISEQ_TYPE_TOP);
+ RB_OBJ_WRITE(env, &env->iseq, empty);
break;
}
else {
@@ -2785,19 +2912,20 @@ proc_binding(VALUE self)
bindval = rb_binding_alloc(rb_cBinding);
GetBindingPtr(bindval, bind);
-
- bind->block.as.captured.self = binding_self;
- bind->block.as.captured.code.iseq = env->iseq;
- bind->block.as.captured.ep = env->ep;
+ RB_OBJ_WRITE(bindval, &bind->block.as.captured.self, binding_self);
+ RB_OBJ_WRITE(bindval, &bind->block.as.captured.code.iseq, env->iseq);
+ rb_vm_block_ep_update(bindval, &bind->block, env->ep);
+ RB_OBJ_WRITTEN(bindval, Qundef, VM_ENV_ENVVAL(env->ep));
if (iseq) {
rb_iseq_check(iseq);
- bind->path = iseq->body->location.path;
+ RB_OBJ_WRITE(bindval, &bind->pathobj, iseq->body->location.pathobj);
bind->first_lineno = FIX2INT(rb_iseq_first_lineno(iseq));
}
else {
- bind->path = Qnil;
- bind->first_lineno = 0;
+ RB_OBJ_WRITE(bindval, &bind->pathobj,
+ rb_iseq_pathobj_new(rb_fstring_lit("(binding)"), Qnil));
+ bind->first_lineno = 1;
}
return bindval;
@@ -2893,8 +3021,7 @@ proc_curry(int argc, const VALUE *argv, VALUE self)
int sarity, max_arity, min_arity = rb_proc_min_max_arity(self, &max_arity);
VALUE arity;
- rb_scan_args(argc, argv, "01", &arity);
- if (NIL_P(arity)) {
+ if (rb_check_arity(argc, 0, 1) == 0 || NIL_P(arity = argv[0])) {
arity = INT2FIX(min_arity);
}
else {
@@ -2946,6 +3073,136 @@ rb_method_curry(int argc, const VALUE *argv, VALUE self)
return proc_curry(argc, argv, proc);
}
+static VALUE
+compose(VALUE dummy, VALUE args, int argc, VALUE *argv, VALUE passed_proc)
+{
+ VALUE f, g, fargs;
+ f = RARRAY_AREF(args, 0);
+ g = RARRAY_AREF(args, 1);
+
+ if (rb_obj_is_proc(g))
+ fargs = rb_proc_call_with_block(g, argc, argv, passed_proc);
+ else
+ fargs = rb_funcall_with_block(g, idCall, argc, argv, passed_proc);
+
+ if (rb_obj_is_proc(f))
+ return rb_proc_call(f, rb_ary_new3(1, fargs));
+ else
+ return rb_funcallv(f, idCall, 1, &fargs);
+}
+
+/*
+ * call-seq:
+ * prc << g -> a_proc
+ *
+ * Returns a proc that is the composition of this proc and the given <i>g</i>.
+ * The returned proc takes a variable number of arguments, calls <i>g</i> with them
+ * then calls this proc with the result.
+ *
+ * f = proc {|x| x * x }
+ * g = proc {|x| x + x }
+ * p (f << g).call(2) #=> 16
+ */
+static VALUE
+proc_compose_to_left(VALUE self, VALUE g)
+{
+ VALUE proc, args, procs[2];
+ rb_proc_t *procp;
+ int is_lambda;
+
+ procs[0] = self;
+ procs[1] = g;
+ args = rb_ary_tmp_new_from_values(0, 2, procs);
+
+ GetProcPtr(self, procp);
+ is_lambda = procp->is_lambda;
+
+ proc = rb_proc_new(compose, args);
+ GetProcPtr(proc, procp);
+ procp->is_lambda = is_lambda;
+
+ return proc;
+}
+
+/*
+ * call-seq:
+ * prc >> g -> a_proc
+ *
+ * Returns a proc that is the composition of this proc and the given <i>g</i>.
+ * The returned proc takes a variable number of arguments, calls <i>g</i> with them
+ * then calls this proc with the result.
+ *
+ * f = proc {|x| x * x }
+ * g = proc {|x| x + x }
+ * p (f >> g).call(2) #=> 8
+ */
+static VALUE
+proc_compose_to_right(VALUE self, VALUE g)
+{
+ VALUE proc, args, procs[2];
+ rb_proc_t *procp;
+ int is_lambda;
+
+ procs[0] = g;
+ procs[1] = self;
+ args = rb_ary_tmp_new_from_values(0, 2, procs);
+
+ GetProcPtr(self, procp);
+ is_lambda = procp->is_lambda;
+
+ proc = rb_proc_new(compose, args);
+ GetProcPtr(proc, procp);
+ procp->is_lambda = is_lambda;
+
+ return proc;
+}
+
+/*
+ * call-seq:
+ * meth << g -> a_proc
+ *
+ * Returns a proc that is the composition of this method and the given <i>g</i>.
+ * The returned proc takes a variable number of arguments, calls <i>g</i> with them
+ * then calls this method with the result.
+ *
+ * def f(x)
+ * x * x
+ * end
+ *
+ * f = self.method(:f)
+ * g = proc {|x| x + x }
+ * p (f << g).call(2) #=> 16
+ */
+static VALUE
+rb_method_compose_to_left(VALUE self, VALUE g)
+{
+ VALUE proc = method_to_proc(self);
+ return proc_compose_to_left(proc, g);
+}
+
+/*
+ * call-seq:
+ * meth >> g -> a_proc
+ *
+ * Returns a proc that is the composition of this method and the given <i>g</i>.
+ * The returned proc takes a variable number of arguments, calls this method
+ * with them then calls <i>g</i> with the result.
+ *
+ * def f(x)
+ * x * x
+ * end
+ *
+ * f = self.method(:f)
+ * g = proc {|x| x + x }
+ * p (f >> g).call(2) #=> 8
+ */
+static VALUE
+rb_method_compose_to_right(VALUE self, VALUE g)
+{
+ VALUE proc = method_to_proc(self);
+ return proc_compose_to_right(proc, g);
+}
+
/*
* Document-class: LocalJumpError
*
@@ -2990,12 +3247,25 @@ rb_method_curry(int argc, const VALUE *argv, VALUE self)
*/
/*
- * <code>Proc</code> objects are blocks of code that have been bound to
- * a set of local variables. Once bound, the code may be called in
- * different contexts and still access those variables.
+ * Document-class: Proc
+ *
+ * A +Proc+ object is an encapsulation of a block of code, which can be stored
+ * in a local variable, passed to a method or another Proc, and can be called.
+ * Proc is an essential concept in Ruby and a core of its functional
+ * programming features.
+ *
+ * square = Proc.new {|x| x**2 }
+ *
+ * square.call(3) #=> 9
+ * # shorthands:
+ * square.(3) #=> 9
+ * square[3] #=> 9
+ *
+ * Proc objects are _closures_, meaning they remember and can use the entire
+ * context in which they were created.
*
* def gen_times(factor)
- * return Proc.new {|n| n*factor }
+ * Proc.new {|n| n*factor } # remembers the value of factor at the moment of creation
* end
*
* times3 = gen_times(3)
@@ -3005,17 +3275,170 @@ rb_method_curry(int argc, const VALUE *argv, VALUE self)
* times5.call(5) #=> 25
* times3.call(times5.call(4)) #=> 60
*
+ * == Creation
+ *
+ * There are several methods to create a Proc
+ *
+ * * Use the Proc class constructor:
+ *
+ * proc1 = Proc.new {|x| x**2 }
+ *
+ * * Use the Kernel#proc method as a shorthand of Proc.new:
+ *
+ * proc2 = proc {|x| x**2 }
+ *
+ * * Receiving a block of code into proc argument (note the <code>&</code>):
+ *
+ * def make_proc(&block)
+ * block
+ * end
+ *
+ * proc3 = make_proc {|x| x**2 }
+ *
+ * * Construct a proc with lambda semantics using the Kernel#lambda method
+ * (see below for explanations about lambdas):
+ *
+ * lambda1 = lambda {|x| x**2 }
+ *
+ * * Use the Lambda literal syntax (also constructs a proc with lambda semantics):
+ *
+ * lambda2 = ->(x) { x**2 }
+ *
+ * == Lambda and non-lambda semantics
+ *
+ * Procs are coming in two flavors: lambda and non-lambda (regular procs).
+ * Differences are:
+ *
+ * * In lambdas, +return+ means exit from this lambda;
+ * * In regular procs, +return+ means exit from embracing method
+ * (and will throw +LocalJumpError+ if invoked outside the method);
+ * * In lambdas, arguments are treated in the same way as in methods: strict,
+ * with +ArgumentError+ for mismatching argument number,
+ * and no additional argument processing;
+ * * Regular procs accept arguments more generously: missing arguments
+ * are filled with +nil+, single Array arguments are deconstructed if the
+ * proc has multiple arguments, and there is no error raised on extra
+ * arguments.
+ *
+ * Examples:
+ *
+ * p = proc {|x, y| "x=#{x}, y=#{y}" }
+ * p.call(1, 2) #=> "x=1, y=2"
+ * p.call([1, 2]) #=> "x=1, y=2", array deconstructed
+ * p.call(1, 2, 8) #=> "x=1, y=2", extra argument discarded
+ * p.call(1) #=> "x=1, y=", nil substituted instead of error
+ *
+ * l = lambda {|x, y| "x=#{x}, y=#{y}" }
+ * l.call(1, 2) #=> "x=1, y=2"
+ * l.call([1, 2]) # ArgumentError: wrong number of arguments (given 1, expected 2)
+ * l.call(1, 2, 8) # ArgumentError: wrong number of arguments (given 3, expected 2)
+ * l.call(1) # ArgumentError: wrong number of arguments (given 1, expected 2)
+ *
+ * def test_return
+ * -> { return 3 }.call # just returns from lambda into method body
+ * proc { return 4 }.call # returns from method
+ * return 5
+ * end
+ *
+ * test_return # => 4, return from proc
+ *
+ * Lambdas are useful as self-sufficient functions, in particular useful as
+ * arguments to higher-order functions, behaving exactly like Ruby methods.
+ *
+ * Procs are useful for implementing iterators:
+ *
+ * def test
+ * [[1, 2], [3, 4], [5, 6]].map {|a, b| return a if a + b > 10 }
+ * # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * end
+ *
+ * Inside +map+, the block of code is treated as a regular (non-lambda) proc,
+ * which means that the internal arrays will be deconstructed to pairs of
+ * arguments, and +return+ will exit from the method +test+. That would
+ * not be possible with a stricter lambda.
+ *
+ * You can tell a lambda from a regular proc by using the #lambda? instance method.
+ *
+ * Lambda semantics is typically preserved during the proc lifetime, including
+ * <code>&</code>-deconstruction to a block of code:
+ *
+ * p = proc {|x, y| x }
+ * l = lambda {|x, y| x }
+ * [[1, 2], [3, 4]].map(&p) #=> [1, 2]
+ * [[1, 2], [3, 4]].map(&l) # ArgumentError: wrong number of arguments (given 1, expected 2)
+ *
+ * The only exception is dynamic method definition: even if defined by
+ * passing a non-lambda proc, methods still have normal semantics of argument
+ * checking.
+ *
+ * class C
+ * define_method(:e, &proc {})
+ * end
+ * C.new.e(1,2) #=> ArgumentError
+ * C.new.method(:e).to_proc.lambda? #=> true
+ *
+ * This exception ensures that methods never have unusual argument passing
+ * conventions, and makes it easy to have wrappers defining methods that
+ * behave as usual.
+ *
+ * class C
+ * def self.def2(name, &body)
+ * define_method(name, &body)
+ * end
+ *
+ * def2(:f) {}
+ * end
+ * C.new.f(1,2) #=> ArgumentError
+ *
+ * The wrapper <i>def2</i> receives <code>body</code> as a non-lambda proc,
+ * yet defines a method which has normal semantics.
+ *
+ * == Conversion of other objects to procs
+ *
+ * Any object that implements the +to_proc+ method can be converted into
+ * a proc by the <code>&</code> operator, and therefore con be
+ * consumed by iterators.
+ *
+ * class Greater
+ * def initialize(greating)
+ * @greating = greating
+ * end
+ *
+ * def to_proc
+ * proc {|name| "#{@greating}, #{name}!" }
+ * end
+ * end
+ *
+ * hi = Greater.new("Hi")
+ * hey = Greater.new("Hey")
+ * ["Bob", "Jane"].map(&hi) #=> ["Hi, Bob!", "Hi, Jane!"]
+ * ["Bob", "Jane"].map(&hey) #=> ["Hey, Bob!", "Hey, Jane!"]
+ *
+ * Of the Ruby core classes, this method is implemented by Symbol,
+ * Method, and Hash.
+ *
+ * :to_s.to_proc.call(1) #=> "1"
+ * [1, 2].map(&:to_s) #=> ["1", "2"]
+ *
+ * method(:puts).to_proc.call(1) # prints 1
+ * [1, 2].each(&method(:puts)) # prints 1, 2
+ *
+ * {test: 1}.to_proc.call(:test) #=> 1
+ * %i[test many keys].map(&{test: 1}) #=> [1, nil, nil]
+ *
*/
+
void
Init_Proc(void)
{
+#undef rb_intern
/* Proc */
rb_cProc = rb_define_class("Proc", rb_cObject);
rb_undef_alloc_func(rb_cProc);
rb_define_singleton_method(rb_cProc, "new", rb_proc_s_new, -1);
- rb_add_method(rb_cProc, rb_intern("call"), VM_METHOD_TYPE_OPTIMIZED,
+ rb_add_method(rb_cProc, idCall, VM_METHOD_TYPE_OPTIMIZED,
(void *)OPTIMIZED_METHOD_TYPE_CALL, METHOD_VISI_PUBLIC);
rb_add_method(rb_cProc, rb_intern("[]"), VM_METHOD_TYPE_OPTIMIZED,
(void *)OPTIMIZED_METHOD_TYPE_CALL, METHOD_VISI_PUBLIC);
@@ -3034,13 +3457,15 @@ Init_Proc(void)
rb_define_method(rb_cProc, "to_proc", proc_to_proc, 0);
rb_define_method(rb_cProc, "arity", proc_arity, 0);
rb_define_method(rb_cProc, "clone", proc_clone, 0);
- rb_define_method(rb_cProc, "dup", proc_dup, 0);
+ rb_define_method(rb_cProc, "dup", rb_proc_dup, 0);
rb_define_method(rb_cProc, "hash", proc_hash, 0);
rb_define_method(rb_cProc, "to_s", proc_to_s, 0);
rb_define_alias(rb_cProc, "inspect", "to_s");
rb_define_method(rb_cProc, "lambda?", rb_proc_lambda_p, 0);
rb_define_method(rb_cProc, "binding", proc_binding, 0);
rb_define_method(rb_cProc, "curry", proc_curry, -1);
+ rb_define_method(rb_cProc, "<<", proc_compose_to_left, 1);
+ rb_define_method(rb_cProc, ">>", proc_compose_to_right, 1);
rb_define_method(rb_cProc, "source_location", rb_proc_location, 0);
rb_define_method(rb_cProc, "parameters", rb_proc_parameters, 0);
@@ -3065,7 +3490,10 @@ Init_Proc(void)
rb_define_method(rb_cMethod, "hash", method_hash, 0);
rb_define_method(rb_cMethod, "clone", method_clone, 0);
rb_define_method(rb_cMethod, "call", rb_method_call, -1);
+ rb_define_method(rb_cMethod, "===", rb_method_call, -1);
rb_define_method(rb_cMethod, "curry", rb_method_curry, -1);
+ rb_define_method(rb_cMethod, "<<", rb_method_compose_to_left, 1);
+ rb_define_method(rb_cMethod, ">>", rb_method_compose_to_right, 1);
rb_define_method(rb_cMethod, "[]", rb_method_call, -1);
rb_define_method(rb_cMethod, "arity", method_arity_m, 0);
rb_define_method(rb_cMethod, "inspect", method_inspect, 0);
@@ -3105,7 +3533,7 @@ Init_Proc(void)
/* Module#*_method */
rb_define_method(rb_cModule, "instance_method", rb_mod_instance_method, 1);
rb_define_method(rb_cModule, "public_instance_method", rb_mod_public_instance_method, 1);
- rb_define_private_method(rb_cModule, "define_method", rb_mod_define_method, -1);
+ rb_define_method(rb_cModule, "define_method", rb_mod_define_method, -1);
/* Kernel */
rb_define_method(rb_mKernel, "define_singleton_method", rb_obj_define_method, -1);
@@ -3132,7 +3560,7 @@ Init_Proc(void)
* @secret = n
* end
* def get_binding
- * return binding()
+ * binding
* end
* end
*
@@ -3163,5 +3591,6 @@ Init_Binding(void)
rb_define_method(rb_cBinding, "local_variable_set", bind_local_variable_set, 2);
rb_define_method(rb_cBinding, "local_variable_defined?", bind_local_variable_defined_p, 1);
rb_define_method(rb_cBinding, "receiver", bind_receiver, 0);
+ rb_define_method(rb_cBinding, "source_location", bind_location, 0);
rb_define_global_function("binding", rb_f_binding, 0);
}
diff --git a/process.c b/process.c
index d4d15a69b3..863ee31f90 100644
--- a/process.c
+++ b/process.c
@@ -11,11 +11,13 @@
**********************************************************************/
-#include "internal.h"
+#include "ruby/config.h"
#include "ruby/io.h"
+#include "internal.h"
#include "ruby/thread.h"
#include "ruby/util.h"
#include "vm_core.h"
+#include "hrtime.h"
#include <stdio.h>
#include <errno.h>
@@ -61,13 +63,6 @@
#include "ruby/st.h"
#include <sys/stat.h>
-#if defined(__native_client__) && defined(NACL_NEWLIB)
-# include <sys/unistd.h>
-# include "nacl/stat.h"
-# include "nacl/unistd.h"
-# include "nacl/resource.h"
-# undef HAVE_ISSETUGID
-#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
@@ -150,29 +145,55 @@ int setregid(rb_gid_t rgid, rb_gid_t egid);
#endif
#endif
-#define preserving_errno(stmts) \
- do {int saved_errno = errno; stmts; errno = saved_errno;} while (0)
-
static void check_uid_switch(void);
static void check_gid_switch(void);
+static int exec_async_signal_safe(const struct rb_execarg *, char *, size_t);
#if 1
#define p_uid_from_name p_uid_from_name
#define p_gid_from_name p_gid_from_name
#endif
+#if defined(HAVE_UNISTD_H)
+# if defined(HAVE_GETLOGIN_R)
+# define USE_GETLOGIN_R 1
+# define GETLOGIN_R_SIZE_DEFAULT 0x100
+# define GETLOGIN_R_SIZE_LIMIT 0x1000
+# if defined(_SC_LOGIN_NAME_MAX)
+# define GETLOGIN_R_SIZE_INIT sysconf(_SC_LOGIN_NAME_MAX)
+# else
+# define GETLOGIN_R_SIZE_INIT GETLOGIN_R_SIZE_DEFAULT
+# endif
+# elif defined(HAVE_GETLOGIN)
+# define USE_GETLOGIN 1
+# endif
+#endif
+
#if defined(HAVE_PWD_H)
-# if defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX)
+# if defined(HAVE_GETPWUID_R)
+# define USE_GETPWUID_R 1
+# elif defined(HAVE_GETPWUID)
+# define USE_GETPWUID 1
+# endif
+# if defined(HAVE_GETPWNAM_R)
# define USE_GETPWNAM_R 1
-# define GETPW_R_SIZE_INIT sysconf(_SC_GETPW_R_SIZE_MAX)
+# elif defined(HAVE_GETPWNAM)
+# define USE_GETPWNAM 1
+# endif
+# if defined(HAVE_GETPWNAM_R) || defined(HAVE_GETPWUID_R)
# define GETPW_R_SIZE_DEFAULT 0x1000
# define GETPW_R_SIZE_LIMIT 0x10000
+# if defined(_SC_GETPW_R_SIZE_MAX)
+# define GETPW_R_SIZE_INIT sysconf(_SC_GETPW_R_SIZE_MAX)
+# else
+# define GETPW_R_SIZE_INIT GETPW_R_SIZE_DEFAULT
+# endif
# endif
# ifdef USE_GETPWNAM_R
# define PREPARE_GETPWNAM \
VALUE getpw_buf = 0
# define FINISH_GETPWNAM \
- ALLOCV_END(getpw_buf)
+ (getpw_buf ? (void)rb_str_resize(getpw_buf, 0) : (void)0)
# define OBJ2UID1(id) obj2uid((id), &getpw_buf)
# define OBJ2UID(id) obj2uid0(id)
static rb_uid_t obj2uid(VALUE id, VALUE *getpw_buf);
@@ -214,7 +235,7 @@ static rb_uid_t obj2uid(VALUE id);
# define PREPARE_GETGRNAM \
VALUE getgr_buf = 0
# define FINISH_GETGRNAM \
- ALLOCV_END(getgr_buf)
+ (getgr_buf ? (void)rb_str_resize(getgr_buf, 0) : (void)0)
# define OBJ2GID1(id) obj2gid((id), &getgr_buf)
# define OBJ2GID(id) obj2gid0(id)
static rb_gid_t obj2gid(VALUE id, VALUE *getgr_buf);
@@ -257,6 +278,7 @@ typedef unsigned LONG_LONG unsigned_clock_t;
typedef void (*sig_t) (int);
#endif
+#define id_exception idException
static ID id_in, id_out, id_err, id_pid, id_uid, id_gid;
static ID id_close, id_child;
#ifdef HAVE_SETPGID
@@ -281,8 +303,6 @@ static ID id_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID;
static ID id_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC;
#endif
static ID id_hertz;
-extern ID ruby_static_id_status;
-#define id_status ruby_static_id_status
/* execv and execl are async-signal-safe since SUSv4 (POSIX.1-2008, XPG7) */
#if defined(__sun) && !defined(_XPG7) /* Solaris 10, 9, ... */
@@ -293,12 +313,30 @@ extern ID ruby_static_id_status;
#define ALWAYS_NEED_ENVP 0
#endif
+static void
+assert_close_on_exec(int fd)
+{
+#if VM_CHECK_MODE > 0
+#if defined(HAVE_FCNTL) && defined(F_GETFD) && defined(FD_CLOEXEC)
+ int flags = fcntl(fd, F_GETFD);
+ if (flags == -1) {
+ static const char m[] = "reserved FD closed unexpectedly?\n";
+ (void)!write(2, m, sizeof(m) - 1);
+ return;
+ }
+ if (flags & FD_CLOEXEC) return;
+ rb_bug("reserved FD did not have close-on-exec set");
+#else
+ rb_bug("reserved FD without close-on-exec support");
+#endif /* FD_CLOEXEC */
+#endif /* VM_CHECK_MODE */
+}
+
static inline int
close_unless_reserved(int fd)
{
- /* We should not have reserved FDs at this point */
if (rb_reserved_fd_p(fd)) { /* async-signal-safe */
- rb_async_bug_errno("BUG timer thread still running", 0 /* EDOOFUS */);
+ assert_close_on_exec(fd);
return 0;
}
return close(fd); /* async-signal-safe */
@@ -404,6 +442,12 @@ parent_redirect_close(int fd)
#endif
/*
+ * Document-module: Process
+ *
+ * Module to handle processes.
+ */
+
+/*
* call-seq:
* Process.pid -> integer
*
@@ -481,6 +525,27 @@ rb_last_status_get(void)
return GET_THREAD()->last_status;
}
+/*
+ * call-seq:
+ * Process.last_status -> Process::Status or nil
+ *
+ * Returns the status of the last executed child process in the
+ * current thread.
+ *
+ * Process.wait Process.spawn("ruby", "-e", "exit 13")
+ * Process.last_status #=> #<Process::Status: pid 4825 exit 13>
+ *
+ * If no child process has ever been executed in the current
+ * thread, this returns +nil+.
+ *
+ * Process.last_status #=> nil
+ */
+static VALUE
+proc_s_last_status(VALUE mod)
+{
+ return rb_last_status_get();
+}
+
void
rb_last_status_set(int status, rb_pid_t pid)
{
@@ -534,10 +599,18 @@ pst_pid(VALUE st)
return rb_attr_get(st, id_pid);
}
+static VALUE pst_message_status(VALUE str, int status);
+
static void
pst_message(VALUE str, rb_pid_t pid, int status)
{
rb_str_catf(str, "pid %ld", (long)pid);
+ pst_message_status(str, status);
+}
+
+static VALUE
+pst_message_status(VALUE str, int status)
+{
if (WIFSTOPPED(status)) {
int stopsig = WSTOPSIG(status);
const char *signame = ruby_signal_name(stopsig);
@@ -566,6 +639,7 @@ pst_message(VALUE str, rb_pid_t pid, int status)
rb_str_cat2(str, " (core dumped)");
}
#endif
+ return str;
}
@@ -859,12 +933,6 @@ pst_wcoredump(VALUE st)
#endif
}
-struct waitpid_arg {
- rb_pid_t pid;
- int flags;
- int *st;
-};
-
static rb_pid_t
do_waitpid(rb_pid_t pid, int *st, int flags)
{
@@ -877,46 +945,306 @@ do_waitpid(rb_pid_t pid, int *st, int flags)
#endif
}
+#define WAITPID_LOCK_ONLY ((struct waitpid_state *)-1)
+
+struct waitpid_state {
+ struct list_node wnode;
+ rb_execution_context_t *ec;
+ rb_nativethread_cond_t *cond;
+ rb_pid_t ret;
+ rb_pid_t pid;
+ int status;
+ int options;
+ int errnum;
+};
+
+void rb_native_mutex_lock(rb_nativethread_lock_t *);
+void rb_native_mutex_unlock(rb_nativethread_lock_t *);
+void rb_native_cond_signal(rb_nativethread_cond_t *);
+void rb_native_cond_wait(rb_nativethread_cond_t *, rb_nativethread_lock_t *);
+int rb_sigwait_fd_get(const rb_thread_t *);
+void rb_sigwait_sleep(const rb_thread_t *, int fd, const rb_hrtime_t *);
+void rb_sigwait_fd_put(const rb_thread_t *, int fd);
+void rb_thread_sleep_interruptible(void);
+
+static int
+waitpid_signal(struct waitpid_state *w)
+{
+ if (w->ec) { /* rb_waitpid */
+ rb_threadptr_interrupt(rb_ec_thread_ptr(w->ec));
+ return TRUE;
+ }
+ else { /* ruby_waitpid_locked */
+ if (w->cond) {
+ rb_native_cond_signal(w->cond);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * When a thread is done using sigwait_fd and there are other threads
+ * sleeping on waitpid, we must kick one of the threads out of
+ * rb_native_cond_wait so it can switch to rb_sigwait_sleep
+ */
+static void
+sigwait_fd_migrate_sleeper(rb_vm_t *vm)
+{
+ struct waitpid_state *w = 0;
+
+ list_for_each(&vm->waiting_pids, w, wnode) {
+ if (waitpid_signal(w)) return;
+ }
+ list_for_each(&vm->waiting_grps, w, wnode) {
+ if (waitpid_signal(w)) return;
+ }
+}
+
+void
+rb_sigwait_fd_migrate(rb_vm_t *vm)
+{
+ rb_native_mutex_lock(&vm->waitpid_lock);
+ sigwait_fd_migrate_sleeper(vm);
+ rb_native_mutex_unlock(&vm->waitpid_lock);
+}
+
+#if RUBY_SIGCHLD
+extern volatile unsigned int ruby_nocldwait; /* signal.c */
+/* called by timer thread or thread which acquired sigwait_fd */
+static void
+waitpid_each(struct list_head *head)
+{
+ struct waitpid_state *w = 0, *next;
+
+ list_for_each_safe(head, w, next, wnode) {
+ rb_pid_t ret = do_waitpid(w->pid, &w->status, w->options | WNOHANG);
+
+ if (!ret) continue;
+ if (ret == -1) w->errnum = errno;
+
+ w->ret = ret;
+ list_del_init(&w->wnode);
+ waitpid_signal(w);
+ }
+}
+#else
+# define ruby_nocldwait 0
+#endif
+
+void
+ruby_waitpid_all(rb_vm_t *vm)
+{
+#if RUBY_SIGCHLD
+ rb_native_mutex_lock(&vm->waitpid_lock);
+ waitpid_each(&vm->waiting_pids);
+ if (list_empty(&vm->waiting_pids)) {
+ waitpid_each(&vm->waiting_grps);
+ }
+ /* emulate SA_NOCLDWAIT */
+ if (list_empty(&vm->waiting_pids) && list_empty(&vm->waiting_grps)) {
+ while (ruby_nocldwait && do_waitpid(-1, 0, WNOHANG) > 0)
+ ; /* keep looping */
+ }
+ rb_native_mutex_unlock(&vm->waitpid_lock);
+#endif
+}
+
+static void
+waitpid_state_init(struct waitpid_state *w, rb_pid_t pid, int options)
+{
+ w->ret = 0;
+ w->pid = pid;
+ w->options = options;
+}
+
+static const rb_hrtime_t *
+sigwait_sleep_time(void)
+{
+ if (SIGCHLD_LOSSY) {
+ static const rb_hrtime_t busy_wait = 100 * RB_HRTIME_PER_MSEC;
+
+ return &busy_wait;
+ }
+ return 0;
+}
+
+/*
+ * must be called with vm->waitpid_lock held, this is not interruptible
+ */
+rb_pid_t
+ruby_waitpid_locked(rb_vm_t *vm, rb_pid_t pid, int *status, int options,
+ rb_nativethread_cond_t *cond)
+{
+ struct waitpid_state w;
+
+ assert(!ruby_thread_has_gvl_p() && "must not have GVL");
+
+ waitpid_state_init(&w, pid, options);
+ if (w.pid > 0 || list_empty(&vm->waiting_pids))
+ w.ret = do_waitpid(w.pid, &w.status, w.options | WNOHANG);
+ if (w.ret) {
+ if (w.ret == -1) w.errnum = errno;
+ }
+ else {
+ int sigwait_fd = -1;
+
+ w.ec = 0;
+ list_add(w.pid > 0 ? &vm->waiting_pids : &vm->waiting_grps, &w.wnode);
+ do {
+ if (sigwait_fd < 0)
+ sigwait_fd = rb_sigwait_fd_get(0);
+
+ if (sigwait_fd >= 0) {
+ w.cond = 0;
+ rb_native_mutex_unlock(&vm->waitpid_lock);
+ rb_sigwait_sleep(0, sigwait_fd, sigwait_sleep_time());
+ rb_native_mutex_lock(&vm->waitpid_lock);
+ }
+ else {
+ w.cond = cond;
+ rb_native_cond_wait(w.cond, &vm->waitpid_lock);
+ }
+ } while (!w.ret);
+ list_del(&w.wnode);
+
+ /* we're done, maybe other waitpid callers are not: */
+ if (sigwait_fd >= 0) {
+ rb_sigwait_fd_put(0, sigwait_fd);
+ sigwait_fd_migrate_sleeper(vm);
+ }
+ }
+ if (status) {
+ *status = w.status;
+ }
+ if (w.ret == -1) errno = w.errnum;
+ return w.ret;
+}
+
+static VALUE
+waitpid_sleep(VALUE x)
+{
+ struct waitpid_state *w = (struct waitpid_state *)x;
+
+ while (!w->ret) {
+ rb_thread_sleep_interruptible();
+ }
+
+ return Qfalse;
+}
+
+static VALUE
+waitpid_cleanup(VALUE x)
+{
+ struct waitpid_state *w = (struct waitpid_state *)x;
+
+ /*
+ * XXX w->ret is sometimes set but list_del is still needed, here,
+ * Not sure why, so we unconditionally do list_del here:
+ */
+ if (TRUE || w->ret == 0) {
+ rb_vm_t *vm = rb_ec_vm_ptr(w->ec);
+
+ rb_native_mutex_lock(&vm->waitpid_lock);
+ list_del(&w->wnode);
+ rb_native_mutex_unlock(&vm->waitpid_lock);
+ }
+
+ return Qfalse;
+}
+
+static void
+waitpid_wait(struct waitpid_state *w)
+{
+ rb_vm_t *vm = rb_ec_vm_ptr(w->ec);
+ int need_sleep = FALSE;
+
+ /*
+ * Lock here to prevent do_waitpid from stealing work from the
+ * ruby_waitpid_locked done by mjit workers since mjit works
+ * outside of GVL
+ */
+ rb_native_mutex_lock(&vm->waitpid_lock);
+
+ if (w->pid > 0 || list_empty(&vm->waiting_pids))
+ w->ret = do_waitpid(w->pid, &w->status, w->options | WNOHANG);
+ if (w->ret) {
+ if (w->ret == -1) w->errnum = errno;
+ }
+ else if (w->options & WNOHANG) {
+ }
+ else {
+ need_sleep = TRUE;
+ }
+
+ if (need_sleep) {
+ w->cond = 0;
+ /* order matters, favor specified PIDs rather than -1 or 0 */
+ list_add(w->pid > 0 ? &vm->waiting_pids : &vm->waiting_grps, &w->wnode);
+ }
+
+ rb_native_mutex_unlock(&vm->waitpid_lock);
+
+ if (need_sleep) {
+ rb_ensure(waitpid_sleep, (VALUE)w, waitpid_cleanup, (VALUE)w);
+ }
+}
+
static void *
-rb_waitpid_blocking(void *data)
+waitpid_blocking_no_SIGCHLD(void *x)
{
- struct waitpid_arg *arg = data;
- rb_pid_t result = do_waitpid(arg->pid, arg->st, arg->flags);
- return (void *)(VALUE)result;
+ struct waitpid_state *w = x;
+
+ w->ret = do_waitpid(w->pid, &w->status, w->options);
+
+ return 0;
}
-static rb_pid_t
-do_waitpid_nonblocking(rb_pid_t pid, int *st, int flags)
+static void
+waitpid_no_SIGCHLD(struct waitpid_state *w)
{
- void *result;
- struct waitpid_arg arg;
- arg.pid = pid;
- arg.st = st;
- arg.flags = flags;
- result = rb_thread_call_without_gvl(rb_waitpid_blocking, &arg,
- RUBY_UBF_PROCESS, 0);
- return (rb_pid_t)(VALUE)result;
+ if (w->options & WNOHANG) {
+ w->ret = do_waitpid(w->pid, &w->status, w->options);
+ }
+ else {
+ do {
+ rb_thread_call_without_gvl(waitpid_blocking_no_SIGCHLD, w,
+ RUBY_UBF_PROCESS, 0);
+ } while (w->ret < 0 && errno == EINTR && (RUBY_VM_CHECK_INTS(w->ec),1));
+ }
+ if (w->ret == -1)
+ w->errnum = errno;
}
rb_pid_t
rb_waitpid(rb_pid_t pid, int *st, int flags)
{
- rb_pid_t result;
+ struct waitpid_state w;
- if (flags & WNOHANG) {
- result = do_waitpid(pid, st, flags);
+ waitpid_state_init(&w, pid, flags);
+ w.ec = GET_EC();
+
+ if (WAITPID_USE_SIGCHLD) {
+ waitpid_wait(&w);
}
else {
- while ((result = do_waitpid_nonblocking(pid, st, flags)) < 0 &&
- (errno == EINTR)) {
- rb_thread_t *th = GET_THREAD();
- RUBY_VM_CHECK_INTS(th);
- }
+ waitpid_no_SIGCHLD(&w);
}
- if (result > 0) {
- rb_last_status_set(*st, result);
+
+ if (st) *st = w.status;
+ if (w.ret == -1) {
+ errno = w.errnum;
}
- return result;
+ else if (w.ret > 0) {
+ if (ruby_nocldwait) {
+ w.ret = -1;
+ errno = ECHILD;
+ }
+ else {
+ rb_last_status_set(w.status, w.ret);
+ }
+ }
+ return w.ret;
}
@@ -1177,6 +1505,39 @@ before_exec_non_async_signal_safe(void)
rb_thread_stop_timer_thread();
}
+#define WRITE_CONST(fd, str) (void)(write((fd),(str),sizeof(str)-1)<0)
+#ifdef _WIN32
+int rb_w32_set_nonblock2(int fd, int nonblock);
+#endif
+
+static int
+set_blocking(int fd)
+{
+#ifdef _WIN32
+ return rb_w32_set_nonblock2(fd, 0);
+#elif defined(F_GETFL) && defined(F_SETFL)
+ int fl = fcntl(fd, F_GETFL); /* async-signal-safe */
+
+ /* EBADF ought to be possible */
+ if (fl == -1) return fl;
+ if (fl & O_NONBLOCK) {
+ fl &= ~O_NONBLOCK;
+ return fcntl(fd, F_SETFL, fl);
+ }
+ return 0;
+#endif
+}
+
+static void
+stdfd_clear_nonblock(void)
+{
+ /* many programs cannot deal with non-blocking stdin/stdout/stderr */
+ int fd;
+ for (fd = 0; fd < 3; fd++) {
+ (void)set_blocking(fd); /* can't do much about errors anyhow */
+ }
+}
+
static void
before_exec(void)
{
@@ -1204,8 +1565,15 @@ after_exec(void)
after_exec_non_async_signal_safe();
}
+#if defined HAVE_WORKING_FORK || defined HAVE_DAEMON
#define before_fork_ruby() before_exec()
-#define after_fork_ruby() (rb_threadptr_pending_interrupt_clear(GET_THREAD()), after_exec())
+static void
+after_fork_ruby(void)
+{
+ rb_threadptr_pending_interrupt_clear(GET_THREAD());
+ after_exec();
+}
+#endif
#include "dln.h"
@@ -1219,10 +1587,10 @@ security(const char *str)
}
}
-#if defined(HAVE_WORKING_FORK) && !defined(__native_client__)
+#if defined(HAVE_WORKING_FORK)
/* try_with_sh and exec_with_sh should be async-signal-safe. Actually it is.*/
-#define try_with_sh(prog, argv, envp) ((saved_errno == ENOEXEC) ? exec_with_sh((prog), (argv), (envp)) : (void)0)
+#define try_with_sh(err, prog, argv, envp) ((err == ENOEXEC) ? exec_with_sh((prog), (argv), (envp)) : (void)0)
static void
exec_with_sh(const char *prog, char **argv, char **envp)
{
@@ -1235,40 +1603,37 @@ exec_with_sh(const char *prog, char **argv, char **envp)
}
#else
-#define try_with_sh(prog, argv, envp) (void)0
+#define try_with_sh(err, prog, argv, envp) (void)0
#endif
/* This function should be async-signal-safe. Actually it is. */
static int
proc_exec_cmd(const char *prog, VALUE argv_str, VALUE envp_str)
{
-#ifdef __native_client__
- rb_notimplement();
- UNREACHABLE;
-#else
char **argv;
#ifndef _WIN32
char **envp;
+ int err;
#endif
argv = ARGVSTR2ARGV(argv_str);
if (!prog) {
- errno = ENOENT;
- return -1;
+ return ENOENT;
}
#ifdef _WIN32
rb_w32_uaspawn(P_OVERLAY, prog, argv);
+ return errno;
#else
- envp = envp_str ? (char **)RSTRING_PTR(envp_str) : NULL;
+ envp = envp_str ? RB_IMEMO_TMPBUF_PTR(envp_str) : NULL;
if (envp_str)
execve(prog, argv, envp); /* async-signal-safe */
else
execv(prog, argv); /* async-signal-safe (since SUSv4) */
- preserving_errno(try_with_sh(prog, argv, envp)); /* try_with_sh() is async-signal-safe. */
-#endif
- return -1;
+ err = errno;
+ try_with_sh(err, prog, argv, envp); /* try_with_sh() is async-signal-safe. */
+ return err;
#endif
}
@@ -1276,10 +1641,6 @@ proc_exec_cmd(const char *prog, VALUE argv_str, VALUE envp_str)
static int
proc_exec_sh(const char *str, VALUE envp_str)
{
-#ifdef __native_client__
- rb_notimplement();
- UNREACHABLE;
-#else
const char *s;
s = str;
@@ -1287,15 +1648,12 @@ proc_exec_sh(const char *str, VALUE envp_str)
s++;
if (!*s) {
- errno = ENOENT;
- return -1;
+ return ENOENT;
}
#ifdef _WIN32
rb_w32_uspawn(P_OVERLAY, (char *)str, 0);
- return -1;
-#else
-#if defined(__CYGWIN32__)
+#elif defined(__CYGWIN32__)
{
char fbuf[MAXPATHLEN];
char *shell = dln_find_exe_r("sh", 0, fbuf, sizeof(fbuf));
@@ -1309,13 +1667,11 @@ proc_exec_sh(const char *str, VALUE envp_str)
}
#else
if (envp_str)
- execle("/bin/sh", "sh", "-c", str, (char *)NULL, (char **)RSTRING_PTR(envp_str)); /* async-signal-safe */
+ execle("/bin/sh", "sh", "-c", str, (char *)NULL, RB_IMEMO_TMPBUF_PTR(envp_str)); /* async-signal-safe */
else
execl("/bin/sh", "sh", "-c", str, (char *)NULL); /* async-signal-safe (since SUSv4) */
-#endif
- return -1;
#endif /* _WIN32 */
-#endif
+ return errno;
}
int
@@ -1324,8 +1680,9 @@ rb_proc_exec(const char *str)
int ret;
before_exec();
ret = proc_exec_sh(str, Qfalse);
- preserving_errno(after_exec());
- return ret;
+ after_exec();
+ errno = ret;
+ return -1;
}
static void
@@ -1486,7 +1843,7 @@ check_exec_redirect_fd(VALUE v, int iskey)
else
goto wrong;
}
- else if (!NIL_P(tmp = rb_check_convert_type(v, T_FILE, "IO", "to_io"))) {
+ else if (!NIL_P(tmp = rb_check_convert_type_with_id(v, T_FILE, "IO", idTo_io))) {
rb_io_t *fptr;
GetOpenFile(tmp, fptr);
if (fptr->tied_io_for_writing)
@@ -1607,7 +1964,7 @@ check_exec_redirect(VALUE key, VALUE val, struct rb_execarg *eargp)
else if (RB_TYPE_P(key, T_ARRAY)) {
int i;
for (i = 0; i < RARRAY_LEN(key); i++) {
- VALUE v = RARRAY_PTR(key)[i];
+ VALUE v = RARRAY_AREF(key, i);
VALUE fd = check_exec_redirect_fd(v, 1);
if (FIX2INT(fd) != 1 && FIX2INT(fd) != 2) break;
}
@@ -1634,7 +1991,35 @@ check_exec_redirect(VALUE key, VALUE val, struct rb_execarg *eargp)
}
#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
-static int rlimit_type_by_lname(const char *name);
+static int rlimit_type_by_sym(VALUE key);
+
+static void
+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());
+ else
+ 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");
+ }
+ }
+ else {
+ softlim = hardlim = rb_to_int(val);
+ }
+ tmp = hide_obj(rb_ary_new3(3, INT2NUM(rtype), softlim, hardlim));
+ rb_ary_push(ary, tmp);
+}
#endif
int
@@ -1643,12 +2028,19 @@ rb_execarg_addopt(VALUE execarg_obj, VALUE key, VALUE val)
struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
ID id;
-#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
- int rtype;
-#endif
switch (TYPE(key)) {
case T_SYMBOL:
+#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
+ {
+ int rtype = rlimit_type_by_sym(key);
+ if (rtype != -1) {
+ rb_execarg_addopt_rlimit(eargp, rtype, val);
+ RB_GC_GUARD(execarg_obj);
+ return ST_CONTINUE;
+ }
+ }
+#endif
if (!(id = rb_check_id(&key))) return ST_STOP;
#ifdef HAVE_SETPGID
if (id == id_pgroup) {
@@ -1681,35 +2073,6 @@ rb_execarg_addopt(VALUE execarg_obj, VALUE key, VALUE val)
}
else
#endif
-#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
- if (strncmp("rlimit_", rb_id2name(id), 7) == 0 &&
- (rtype = rlimit_type_by_lname(rb_id2name(id)+7)) != -1) {
- VALUE ary = eargp->rlimit_limits;
- VALUE tmp, softlim, hardlim;
- if (eargp->rlimit_limits == Qfalse)
- ary = eargp->rlimit_limits = hide_obj(rb_ary_new());
- else
- 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");
- }
- }
- else {
- softlim = hardlim = rb_to_int(val);
- }
- tmp = hide_obj(rb_ary_new3(3, INT2NUM(rtype), softlim, hardlim));
- rb_ary_push(ary, tmp);
- }
- else
-#endif
if (id == id_unsetenv_others) {
if (eargp->unsetenv_others_given) {
rb_raise(rb_eArgError, "unsetenv_others option specified twice");
@@ -1811,7 +2174,7 @@ check_exec_options_i(st_data_t st_key, st_data_t st_val, st_data_t arg)
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,
+ rb_raise(rb_eArgError, "wrong exec option symbol: % "PRIsVALUE,
key);
rb_raise(rb_eArgError, "wrong exec option");
}
@@ -1915,7 +2278,7 @@ rb_check_exec_options(VALUE opthash, VALUE execarg_obj)
{
if (RHASH_EMPTY_P(opthash))
return;
- st_foreach(rb_hash_tbl_raw(opthash), check_exec_options_i, (st_data_t)execarg_obj);
+ rb_hash_stlike_foreach(opthash, check_exec_options_i, (st_data_t)execarg_obj);
}
VALUE
@@ -1926,7 +2289,7 @@ rb_execarg_extract_options(VALUE execarg_obj, VALUE opthash)
return Qnil;
args[0] = execarg_obj;
args[1] = Qnil;
- st_foreach(rb_hash_tbl_raw(opthash), check_exec_options_i_extract, (st_data_t)args);
+ rb_hash_stlike_foreach(opthash, check_exec_options_i_extract, (st_data_t)args);
return args[1];
}
@@ -1970,7 +2333,7 @@ rb_check_exec_env(VALUE hash, VALUE *path)
env[0] = hide_obj(rb_ary_new());
env[1] = Qfalse;
- st_foreach(rb_hash_tbl_raw(hash), check_exec_env_i, (st_data_t)env);
+ rb_hash_stlike_foreach(hash, check_exec_env_i, (st_data_t)env);
*path = env[1];
return env[0];
@@ -2196,7 +2559,9 @@ rb_exec_fillarg(VALUE prog, int argc, VALUE *argv, VALUE env, VALUE opthash, VAL
}
}
eargp->invoke.cmd.argv_buf = argv_buf;
- eargp->invoke.cmd.command_name = hide_obj(rb_str_new_cstr(RSTRING_PTR(argv_buf)));
+ eargp->invoke.cmd.command_name =
+ hide_obj(rb_str_subseq(argv_buf, 0, strlen(RSTRING_PTR(argv_buf))));
+ rb_enc_copy(eargp->invoke.cmd.command_name, prog);
}
}
#endif
@@ -2242,22 +2607,12 @@ rb_exec_fillarg(VALUE prog, int argc, VALUE *argv, VALUE env, VALUE opthash, VAL
p += strlen(p) + 1;
}
rb_str_buf_cat(argv_str, (char *)&null, sizeof(null)); /* terminator for execve. */
- eargp->invoke.cmd.argv_str = argv_str;
+ eargp->invoke.cmd.argv_str =
+ rb_imemo_tmpbuf_auto_free_pointer_new_from_an_RString(argv_str);
}
RB_GC_GUARD(execarg_obj);
}
-VALUE
-rb_execarg_new(int argc, const VALUE *argv, int accept_shell)
-{
- VALUE execarg_obj;
- struct rb_execarg *eargp;
- execarg_obj = TypedData_Make_Struct(rb_cData, struct rb_execarg, &exec_arg_data_type, eargp);
- hide_obj(execarg_obj);
- rb_execarg_init(argc, argv, accept_shell, execarg_obj);
- return execarg_obj;
-}
-
struct rb_execarg *
rb_execarg_get(VALUE execarg_obj)
{
@@ -2266,23 +2621,40 @@ rb_execarg_get(VALUE execarg_obj)
return eargp;
}
-VALUE
-rb_execarg_init(int argc, const VALUE *orig_argv, int accept_shell, VALUE execarg_obj)
+static VALUE
+rb_execarg_init(int argc, const VALUE *orig_argv, int accept_shell, VALUE execarg_obj, int allow_exc_opt)
{
struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
- VALUE prog, ret;
+ VALUE prog, ret, exception = Qnil;
VALUE env = Qnil, opthash = Qnil;
VALUE argv_buf;
VALUE *argv = ALLOCV_N(VALUE, argv_buf, argc);
MEMCPY(argv, orig_argv, VALUE, argc);
prog = rb_exec_getargs(&argc, &argv, accept_shell, &env, &opthash);
+ if (allow_exc_opt && !NIL_P(opthash) && rb_hash_has_key(opthash, ID2SYM(id_exception))) {
+ opthash = rb_hash_dup(opthash);
+ exception = rb_hash_delete(opthash, ID2SYM(id_exception));
+ }
rb_exec_fillarg(prog, argc, argv, env, opthash, execarg_obj);
+ if (RTEST(exception)) {
+ eargp->exception = 1;
+ }
ALLOCV_END(argv_buf);
ret = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
RB_GC_GUARD(execarg_obj);
return ret;
}
+VALUE
+rb_execarg_new(int argc, const VALUE *argv, int accept_shell, int allow_exc_opt)
+{
+ VALUE execarg_obj;
+ struct rb_execarg *eargp;
+ execarg_obj = TypedData_Make_Struct(0, struct rb_execarg, &exec_arg_data_type, eargp);
+ rb_execarg_init(argc, argv, accept_shell, execarg_obj, allow_exc_opt);
+ return execarg_obj;
+}
+
void
rb_execarg_setenv(VALUE execarg_obj, VALUE env)
{
@@ -2327,6 +2699,14 @@ open_func(void *ptr)
return NULL;
}
+static void
+rb_execarg_allocate_dup2_tmpbuf(struct rb_execarg *eargp, long len)
+{
+ VALUE tmpbuf = rb_imemo_tmpbuf_auto_free_pointer(NULL);
+ ((rb_imemo_tmpbuf_t *)tmpbuf)->ptr = ruby_xmalloc(run_exec_dup2_tmpbuf_size(len));
+ eargp->dup2_tmpbuf = tmpbuf;
+}
+
static VALUE
rb_execarg_parent_start1(VALUE execarg_obj)
{
@@ -2344,7 +2724,7 @@ rb_execarg_parent_start1(VALUE execarg_obj)
VALUE param = RARRAY_AREF(elt, 1);
VALUE vpath = RARRAY_AREF(param, 0);
int flags = NUM2INT(RARRAY_AREF(param, 1));
- int perm = NUM2INT(RARRAY_AREF(param, 2));
+ mode_t perm = NUM2MODET(RARRAY_AREF(param, 2));
VALUE fd2v = RARRAY_AREF(param, 3);
int fd2;
if (NIL_P(fd2v)) {
@@ -2381,10 +2761,7 @@ rb_execarg_parent_start1(VALUE execarg_obj)
ary = eargp->fd_dup2;
if (ary != Qfalse) {
- size_t len = run_exec_dup2_tmpbuf_size(RARRAY_LEN(ary));
- VALUE tmpbuf = hide_obj(rb_str_new(0, len));
- rb_str_set_len(tmpbuf, len);
- eargp->dup2_tmpbuf = tmpbuf;
+ rb_execarg_allocate_dup2_tmpbuf(eargp, RARRAY_LEN(ary));
}
unsetenv_others = eargp->unsetenv_others_given && eargp->unsetenv_others_do;
@@ -2397,7 +2774,7 @@ rb_execarg_parent_start1(VALUE execarg_obj)
}
else {
envtbl = rb_const_get(rb_cObject, id_ENV);
- envtbl = rb_convert_type(envtbl, T_HASH, "Hash", "to_hash");
+ envtbl = rb_to_hash_type(envtbl);
}
hide_obj(envtbl);
if (envopts != Qfalse) {
@@ -2420,7 +2797,7 @@ rb_execarg_parent_start1(VALUE execarg_obj)
}
envp_buf = rb_str_buf_new(0);
hide_obj(envp_buf);
- st_foreach(RHASH_TBL_RAW(envtbl), fill_envp_buf_i, (st_data_t)envp_buf);
+ rb_hash_stlike_foreach(envtbl, fill_envp_buf_i, (st_data_t)envp_buf);
envp_str = rb_str_buf_new(sizeof(char*) * (RHASH_SIZE(envtbl) + 1));
hide_obj(envp_str);
p = RSTRING_PTR(envp_buf);
@@ -2431,7 +2808,8 @@ rb_execarg_parent_start1(VALUE execarg_obj)
}
p = NULL;
rb_str_buf_cat(envp_str, (char *)&p, sizeof(p));
- eargp->envp_str = envp_str;
+ eargp->envp_str =
+ rb_imemo_tmpbuf_auto_free_pointer_new_from_an_RString(envp_str);
eargp->envp_buf = envp_buf;
/*
@@ -2593,23 +2971,29 @@ rb_f_exec(int argc, const VALUE *argv)
struct rb_execarg *eargp;
#define CHILD_ERRMSG_BUFLEN 80
char errmsg[CHILD_ERRMSG_BUFLEN] = { '\0' };
- int err;
+ int err, state;
- execarg_obj = rb_execarg_new(argc, argv, TRUE);
+ execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
eargp = rb_execarg_get(execarg_obj);
+ if (mjit_enabled) mjit_finish(FALSE); /* avoid leaking resources, and do not leave files. XXX: JIT-ed handle can leak after exec error is rescued. */
before_exec(); /* stop timer thread before redirects */
- rb_execarg_parent_start(execarg_obj);
- fail_str = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
- rb_exec_async_signal_safe(eargp, errmsg, sizeof(errmsg));
+ rb_protect(rb_execarg_parent_start1, execarg_obj, &state);
+ if (state) {
+ execarg_parent_end(execarg_obj);
+ after_exec(); /* restart timer thread */
+ rb_jump_tag(state);
+ }
+
+ fail_str = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
- err = errno;
+ err = exec_async_signal_safe(eargp, errmsg, sizeof(errmsg));
after_exec(); /* restart timer thread */
rb_exec_fail(eargp, err, errmsg);
RB_GC_GUARD(execarg_obj);
rb_syserr_fail_str(err, fail_str);
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
#define ERRMSG(str) do { if (errmsg && 0 < errmsg_buflen) strlcpy(errmsg, (str), errmsg_buflen); } while (0)
@@ -2749,10 +3133,10 @@ run_exec_dup2(VALUE ary, VALUE tmpbuf, struct rb_execarg *sargp, char *errmsg, s
long n, i;
int ret;
int extra_fd = -1;
- struct run_exec_dup2_fd_pair *pairs = 0;
+ struct rb_imemo_tmpbuf_struct *buf = (void *)tmpbuf;
+ struct run_exec_dup2_fd_pair *pairs = (void *)buf->ptr;
n = RARRAY_LEN(ary);
- pairs = (struct run_exec_dup2_fd_pair *)RSTRING_PTR(tmpbuf);
/* initialize oldfd and newfd: O(n) */
for (i = 0; i < n; i++) {
@@ -3085,7 +3469,7 @@ rb_execarg_run_options(const struct rb_execarg *eargp, struct rb_execarg *sargp,
}
#ifdef HAVE_WORKING_FORK
- if (!eargp->close_others_given || eargp->close_others_do) {
+ if (eargp->close_others_do) {
rb_close_before_exec(3, eargp->close_others_maxhint, eargp->redirect_fds); /* async-signal-safe */
}
#endif
@@ -3098,7 +3482,7 @@ rb_execarg_run_options(const struct rb_execarg *eargp, struct rb_execarg *sargp,
if (eargp->chdir_given) {
if (sargp) {
- char *cwd = my_getcwd();
+ char *cwd = ruby_getcwd();
sargp->chdir_given = 1;
sargp->chdir_dir = hide_obj(rb_str_new2(cwd));
xfree(cwd);
@@ -3129,12 +3513,14 @@ rb_execarg_run_options(const struct rb_execarg *eargp, struct rb_execarg *sargp,
if (sargp) {
VALUE ary = sargp->fd_dup2;
if (ary != Qfalse) {
- size_t len = run_exec_dup2_tmpbuf_size(RARRAY_LEN(ary));
- VALUE tmpbuf = hide_obj(rb_str_new(0, len));
- rb_str_set_len(tmpbuf, len);
- sargp->dup2_tmpbuf = tmpbuf;
+ rb_execarg_allocate_dup2_tmpbuf(sargp, RARRAY_LEN(ary));
}
}
+ {
+ int preserve = errno;
+ stdfd_clear_nonblock();
+ errno = preserve;
+ }
return 0;
}
@@ -3143,31 +3529,38 @@ rb_execarg_run_options(const struct rb_execarg *eargp, struct rb_execarg *sargp,
int
rb_exec_async_signal_safe(const struct rb_execarg *eargp, char *errmsg, size_t errmsg_buflen)
{
+ errno = exec_async_signal_safe(eargp, errmsg, errmsg_buflen);
+ return -1;
+}
+
+static int
+exec_async_signal_safe(const struct rb_execarg *eargp, char *errmsg, size_t errmsg_buflen)
+{
#if !defined(HAVE_WORKING_FORK)
struct rb_execarg sarg, *const sargp = &sarg;
#else
struct rb_execarg *const sargp = NULL;
#endif
+ int err;
if (rb_execarg_run_options(eargp, sargp, errmsg, errmsg_buflen) < 0) { /* hopefully async-signal-safe */
- goto failure;
+ return errno;
}
if (eargp->use_shell) {
- 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);
- proc_exec_cmd(abspath, eargp->invoke.cmd.argv_str, eargp->envp_str); /* async-signal-safe */
+ err = proc_exec_cmd(abspath, eargp->invoke.cmd.argv_str, eargp->envp_str); /* async-signal-safe */
}
#if !defined(HAVE_WORKING_FORK)
- preserving_errno(rb_execarg_run_options(sargp, NULL, errmsg, errmsg_buflen));
+ rb_execarg_run_options(sargp, NULL, errmsg, errmsg_buflen);
#endif
-failure:
- return -1;
+ return err;
}
#ifdef HAVE_WORKING_FORK
@@ -3237,12 +3630,19 @@ pipe_nocrash(int filedes[2], VALUE fds)
#define O_BINARY 0
#endif
+static VALUE
+rb_thread_sleep_that_takes_VALUE_as_sole_argument(VALUE n)
+{
+ rb_thread_sleep(NUM2INT(n));
+ return Qundef;
+}
+
static int
-handle_fork_error(int *status, int *ep, volatile int *try_gc_p)
+handle_fork_error(int err, int *status, int *ep, volatile int *try_gc_p)
{
int state = 0;
- switch (errno) {
+ switch (err) {
case ENOMEM:
if ((*try_gc_p)-- > 0 && !rb_during_gc()) {
rb_gc();
@@ -3258,14 +3658,16 @@ handle_fork_error(int *status, int *ep, volatile int *try_gc_p)
return 0;
}
else {
- rb_protect((VALUE (*)())rb_thread_sleep, 1, &state);
+ rb_protect(rb_thread_sleep_that_takes_VALUE_as_sole_argument, INT2FIX(1), &state);
if (status) *status = state;
if (!state) return 0;
}
break;
}
if (ep) {
- preserving_errno((close(ep[0]), close(ep[1])));
+ close(ep[0]);
+ close(ep[1]);
+ errno = err;
}
if (state && !status) rb_jump_tag(state);
return -1;
@@ -3319,6 +3721,12 @@ read_retry(int fd, void *buf, size_t len)
{
ssize_t r;
+ if (set_blocking(fd) != 0) {
+#ifndef _WIN32
+ rb_async_bug_errno("set_blocking failed reading child error", errno);
+#endif
+ }
+
do {
r = read(fd, buf, len);
} while (r < 0 && errno == EINTR);
@@ -3464,7 +3872,6 @@ has_privilege(void)
struct child_handler_disabler_state
{
sigset_t sigmask;
- int cancelstate;
};
static void
@@ -3485,13 +3892,6 @@ disable_child_handler_before_fork(struct child_handler_disabler_state *old)
#else
# pragma GCC warning "pthread_sigmask on fork is not available. potentially dangerous"
#endif
-
-#ifdef PTHREAD_CANCEL_DISABLE
- ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old->cancelstate);
- if (ret != 0) {
- rb_syserr_fail(ret, "pthread_setcancelstate");
- }
-#endif
}
static void
@@ -3499,13 +3899,6 @@ disable_child_handler_fork_parent(struct child_handler_disabler_state *old)
{
int ret;
-#ifdef PTHREAD_CANCEL_DISABLE
- ret = pthread_setcancelstate(old->cancelstate, NULL);
- if (ret != 0) {
- rb_syserr_fail(ret, "pthread_setcancelstate");
- }
-#endif
-
#ifdef HAVE_PTHREAD_SIGMASK
ret = pthread_sigmask(SIG_SETMASK, &old->sigmask, NULL); /* not async-signal-safe */
if (ret != 0) {
@@ -3524,26 +3917,28 @@ 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);
+ 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;
- }
+ 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 */
+ sigemptyset(&old->sigmask);
ret = sigprocmask(SIG_SETMASK, &old->sigmask, NULL); /* async-signal-safe */
if (ret != 0) {
ERRMSG("sigprocmask");
@@ -3552,18 +3947,30 @@ disable_child_handler_fork_child(struct child_handler_disabler_state *old, char
return 0;
}
+COMPILER_WARNING_PUSH
+#ifdef __GNUC__
+COMPILER_WARNING_IGNORED(-Wdeprecated-declarations)
+#endif
static rb_pid_t
retry_fork_async_signal_safe(int *status, int *ep,
int (*chfunc)(void*, char *, size_t), void *charg,
- char *errmsg, size_t errmsg_buflen)
+ char *errmsg, size_t errmsg_buflen,
+ struct waitpid_state *w)
{
rb_pid_t pid;
volatile int try_gc = 1;
struct child_handler_disabler_state old;
+ int err;
+ rb_nativethread_lock_t *const waitpid_lock_init =
+ (w && WAITPID_USE_SIGCHLD) ? &GET_VM()->waitpid_lock : 0;
while (1) {
+ rb_nativethread_lock_t *waitpid_lock = waitpid_lock_init;
prefork();
disable_child_handler_before_fork(&old);
+ if (waitpid_lock) {
+ rb_native_mutex_lock(waitpid_lock);
+ }
#ifdef HAVE_WORKING_VFORK
if (!has_privilege())
pid = vfork();
@@ -3587,37 +3994,54 @@ retry_fork_async_signal_safe(int *status, int *ep,
_exit(127);
#endif
}
- preserving_errno(disable_child_handler_fork_parent(&old));
+ err = errno;
+ waitpid_lock = waitpid_lock_init;
+ if (waitpid_lock) {
+ if (pid > 0 && w != WAITPID_LOCK_ONLY) {
+ w->pid = pid;
+ list_add(&GET_VM()->waiting_pids, &w->wnode);
+ }
+ rb_native_mutex_unlock(waitpid_lock);
+ }
+ disable_child_handler_fork_parent(&old);
if (0 < pid) /* fork succeed, parent process */
return pid;
/* fork failed */
- if (handle_fork_error(status, ep, &try_gc))
+ if (handle_fork_error(err, status, ep, &try_gc))
return -1;
}
}
+COMPILER_WARNING_POP
-rb_pid_t
-rb_fork_async_signal_safe(int *status, int (*chfunc)(void*, char *, size_t), void *charg, VALUE fds,
- char *errmsg, size_t errmsg_buflen)
+static rb_pid_t
+fork_check_err(int *status, int (*chfunc)(void*, char *, size_t), void *charg,
+ VALUE fds, char *errmsg, size_t errmsg_buflen,
+ struct rb_execarg *eargp)
{
rb_pid_t pid;
int err;
int ep[2];
int error_occurred;
+ struct waitpid_state *w;
+
+ w = eargp && eargp->waitpid_state ? eargp->waitpid_state : 0;
if (status) *status = 0;
if (pipe_nocrash(ep, fds)) return -1;
- pid = retry_fork_async_signal_safe(status, ep, chfunc, charg, errmsg, errmsg_buflen);
+ pid = retry_fork_async_signal_safe(status, ep, chfunc, charg,
+ errmsg, errmsg_buflen, w);
if (pid < 0)
return pid;
close(ep[1]);
error_occurred = recv_child_error(ep[0], &err, errmsg, errmsg_buflen);
if (error_occurred) {
if (status) {
+ VM_ASSERT((w == 0 || w == WAITPID_LOCK_ONLY) &&
+ "only used by extensions");
rb_protect(proc_syswait, (VALUE)pid, status);
}
- else {
+ else if (!w) {
rb_syswait(pid);
}
errno = err;
@@ -3626,42 +4050,52 @@ rb_fork_async_signal_safe(int *status, int (*chfunc)(void*, char *, size_t), voi
return pid;
}
-static rb_pid_t
-retry_fork_ruby(int *status)
+/*
+ * The "async_signal_safe" name is a lie, but it is used by pty.c and
+ * maybe other exts. fork() is not async-signal-safe due to pthread_atfork
+ * and future POSIX revisions will remove it from a list of signal-safe
+ * functions. rb_waitpid is not async-signal-safe since MJIT, either.
+ * For our purposes, we do not need async-signal-safety, here
+ */
+rb_pid_t
+rb_fork_async_signal_safe(int *status,
+ int (*chfunc)(void*, char *, size_t), void *charg,
+ VALUE fds, char *errmsg, size_t errmsg_buflen)
{
- rb_pid_t pid;
- int try_gc = 1;
-
- while (1) {
- prefork();
- before_fork_ruby();
- pid = fork();
- if (pid == 0) /* fork succeed, child process */
- return pid;
- preserving_errno(after_fork_ruby());
- if (0 < pid) /* fork succeed, parent process */
- return pid;
- /* fork failed */
- if (handle_fork_error(status, NULL, &try_gc))
- return -1;
- }
+ return fork_check_err(status, chfunc, charg, fds, errmsg, errmsg_buflen, 0);
}
+COMPILER_WARNING_PUSH
+#ifdef __GNUC__
+COMPILER_WARNING_IGNORED(-Wdeprecated-declarations)
+#endif
rb_pid_t
rb_fork_ruby(int *status)
{
rb_pid_t pid;
+ int try_gc = 1, err;
+ struct child_handler_disabler_state old;
if (status) *status = 0;
- pid = retry_fork_ruby(status);
- if (pid < 0)
- return pid;
- if (!pid) {
+ while (1) {
+ prefork();
+ if (mjit_enabled) mjit_pause(FALSE); /* Don't leave locked mutex to child. Note: child_handler must be enabled to pause MJIT. */
+ disable_child_handler_before_fork(&old);
+ before_fork_ruby();
+ pid = fork();
+ err = errno;
after_fork_ruby();
+ disable_child_handler_fork_parent(&old); /* yes, bad name */
+ if (mjit_enabled && pid > 0) mjit_resume(); /* child (pid == 0) is cared by rb_thread_atfork */
+ if (pid >= 0) /* fork succeed */
+ return pid;
+ /* fork failed */
+ if (handle_fork_error(err, status, NULL, &try_gc))
+ return -1;
}
- return pid;
}
+COMPILER_WARNING_POP
#endif
@@ -3766,13 +4200,13 @@ rb_f_exit_bang(int argc, VALUE *argv, VALUE obj)
}
_exit(istatus);
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
void
rb_exit(int status)
{
- if (GET_THREAD()->tag) {
+ if (GET_EC()->tag) {
VALUE args[2];
args[0] = INT2NUM(status);
@@ -3837,7 +4271,7 @@ rb_f_exit(int argc, const VALUE *argv)
}
rb_exit(istatus);
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
@@ -3857,8 +4291,10 @@ rb_f_abort(int argc, const VALUE *argv)
{
rb_check_arity(argc, 0, 1);
if (argc == 0) {
- if (!NIL_P(GET_THREAD()->errinfo)) {
- ruby_error_print();
+ rb_execution_context_t *ec = GET_EC();
+ VALUE errinfo = ec->errinfo;
+ if (!NIL_P(errinfo)) {
+ rb_ec_error_print(ec, errinfo);
}
rb_exit(EXIT_FAILURE);
}
@@ -3872,7 +4308,7 @@ rb_f_abort(int argc, const VALUE *argv)
rb_exc_raise(rb_class_new_instance(2, args, rb_eSystemExit));
}
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
void
@@ -3919,7 +4355,8 @@ rb_spawn_process(struct rb_execarg *eargp, char *errmsg, size_t errmsg_buflen)
#endif
#if defined HAVE_WORKING_FORK && !USE_SPAWNV
- pid = rb_fork_async_signal_safe(NULL, rb_exec_atfork, eargp, eargp->redirect_fds, errmsg, errmsg_buflen);
+ pid = fork_check_err(0, rb_exec_atfork, eargp, eargp->redirect_fds,
+ errmsg, errmsg_buflen, eargp);
#else
prog = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
@@ -3946,7 +4383,9 @@ rb_spawn_process(struct rb_execarg *eargp, char *errmsg, size_t errmsg_buflen)
rb_last_status_set((status & 0xff) << 8, 0);
pid = 1; /* dummy */
# endif
-
+ if (eargp->waitpid_state && eargp->waitpid_state != WAITPID_LOCK_ONLY) {
+ eargp->waitpid_state->pid = pid;
+ }
rb_execarg_run_options(&sarg, NULL, errmsg, errmsg_buflen);
#endif
return pid;
@@ -3973,6 +4412,15 @@ static rb_pid_t
rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen)
{
struct spawn_args args;
+ struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
+
+ /*
+ * Prevent a race with MJIT where the compiler process where
+ * can hold an FD of ours in between vfork + execve
+ */
+ if (!eargp->waitpid_state && mjit_enabled) {
+ eargp->waitpid_state = WAITPID_LOCK_ONLY;
+ }
args.execarg = execarg_obj;
args.errmsg.ptr = errmsg;
@@ -3986,7 +4434,7 @@ rb_spawn_internal(int argc, const VALUE *argv, char *errmsg, size_t errmsg_bufle
{
VALUE execarg_obj;
- execarg_obj = rb_execarg_new(argc, argv, TRUE);
+ execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
return rb_execarg_spawn(execarg_obj, errmsg, errmsg_buflen);
}
@@ -4038,37 +4486,59 @@ rb_spawn(int argc, const VALUE *argv)
static VALUE
rb_f_system(int argc, VALUE *argv)
{
- rb_pid_t pid;
- int status;
-
-#if defined(SIGCLD) && !defined(SIGCHLD)
-# define SIGCHLD SIGCLD
-#endif
+ /*
+ * n.b. using alloca for now to simplify future Thread::Light code
+ * when we need to use malloc for non-native Fiber
+ */
+ struct waitpid_state *w = alloca(sizeof(struct waitpid_state));
+ rb_pid_t pid; /* may be different from waitpid_state.pid on exec failure */
+ VALUE execarg_obj;
+ struct rb_execarg *eargp;
+ int exec_errnum;
-#ifdef SIGCHLD
- RETSIGTYPE (*chfunc)(int);
+ execarg_obj = rb_execarg_new(argc, argv, TRUE, TRUE);
+ eargp = rb_execarg_get(execarg_obj);
+ w->ec = GET_EC();
+ waitpid_state_init(w, 0, 0);
+ eargp->waitpid_state = w;
+ pid = rb_execarg_spawn(execarg_obj, 0, 0);
+ exec_errnum = pid < 0 ? errno : 0;
- rb_last_status_clear();
- chfunc = signal(SIGCHLD, SIG_DFL);
-#endif
- pid = rb_spawn_internal(argc, argv, NULL, 0);
#if defined(HAVE_WORKING_FORK) || defined(HAVE_SPAWNV)
- if (pid > 0) {
- int ret, status;
- ret = rb_waitpid(pid, &status, 0);
- if (ret == (rb_pid_t)-1)
- rb_sys_fail("Another thread waited the process started by system().");
+ if (w->pid > 0) {
+ /* `pid' (not w->pid) may be < 0 here if execve failed in child */
+ if (WAITPID_USE_SIGCHLD) {
+ rb_ensure(waitpid_sleep, (VALUE)w, waitpid_cleanup, (VALUE)w);
+ }
+ else {
+ waitpid_no_SIGCHLD(w);
+ }
+ rb_last_status_set(w->status, w->ret);
}
#endif
-#ifdef SIGCHLD
- signal(SIGCHLD, chfunc);
-#endif
- if (pid < 0) {
- return Qnil;
+ if (w->pid < 0 /* fork failure */ || pid < 0 /* exec failure */) {
+ if (eargp->exception) {
+ int err = exec_errnum ? exec_errnum : w->errnum;
+ VALUE command = eargp->invoke.sh.shell_script;
+ RB_GC_GUARD(execarg_obj);
+ rb_syserr_fail_str(err, command);
+ }
+ else {
+ return Qnil;
+ }
+ }
+ if (w->status == EXIT_SUCCESS) return Qtrue;
+ if (eargp->exception) {
+ VALUE command = eargp->invoke.sh.shell_script;
+ VALUE str = rb_str_new_cstr("Command failed with");
+ rb_str_cat_cstr(pst_message_status(str, w->status), ": ");
+ rb_str_append(str, command);
+ RB_GC_GUARD(execarg_obj);
+ rb_exc_raise(rb_exc_new_str(rb_eRuntimeError, str));
+ }
+ else {
+ return Qfalse;
}
- status = PST2INT(rb_last_status_get());
- if (status == EXIT_SUCCESS) return Qtrue;
- return Qfalse;
}
/*
@@ -4140,7 +4610,7 @@ rb_f_system(int argc, VALUE *argv)
* integer : the file descriptor of specified the integer
* io : the file descriptor specified as io.fileno
* file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not
- * :close_others => true : don't inherit
+ * :close_others => false : inherit
* current directory:
* :chdir => str
*
@@ -4299,7 +4769,7 @@ rb_f_system(int argc, VALUE *argv)
* pid = spawn(command, :close_others=>true) # close 3,4,5,... (default)
* pid = spawn(command, :close_others=>false) # don't close 3,4,5,...
*
- * :close_others is true by default for spawn and IO.popen.
+ * :close_others is false by default for spawn and IO.popen.
*
* Note that fds which close-on-exec flag is already set are closed
* regardless of :close_others option.
@@ -4347,7 +4817,7 @@ rb_f_spawn(int argc, VALUE *argv)
VALUE execarg_obj, fail_str;
struct rb_execarg *eargp;
- execarg_obj = rb_execarg_new(argc, argv, TRUE);
+ execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
eargp = rb_execarg_get(execarg_obj);
fail_str = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
@@ -4670,13 +5140,13 @@ proc_setpriority(VALUE obj, VALUE which, VALUE who, VALUE prio)
#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
static int
-rlimit_resource_name2int(const char *name, int casetype)
+rlimit_resource_name2int(const char *name, long len, int casetype)
{
int resource;
const char *p;
#define RESCHECK(r) \
do { \
- if (STRCASECMP(name, #r) == 0) { \
+ if (len == rb_strlen_lit(#r) && STRCASECMP(name, #r) == 0) { \
resource = RLIMIT_##r; \
goto found; \
} \
@@ -4779,21 +5249,40 @@ rlimit_resource_name2int(const char *name, int casetype)
}
static int
-rlimit_type_by_hname(const char *name)
+rlimit_type_by_hname(const char *name, long len)
{
- return rlimit_resource_name2int(name, 0);
+ return rlimit_resource_name2int(name, len, 0);
}
static int
-rlimit_type_by_lname(const char *name)
+rlimit_type_by_lname(const char *name, long len)
{
- return rlimit_resource_name2int(name, 1);
+ return rlimit_resource_name2int(name, len, 1);
+}
+
+static int
+rlimit_type_by_sym(VALUE key)
+{
+ VALUE name = rb_sym2str(key);
+ const char *rname = RSTRING_PTR(name);
+ long len = RSTRING_LEN(name);
+ int rtype = -1;
+ static const char prefix[] = "rlimit_";
+ 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);
+ }
+
+ RB_GC_GUARD(key);
+ return rtype;
}
static int
rlimit_resource_type(VALUE rtype)
{
const char *name;
+ long len;
VALUE v;
int r;
@@ -4801,6 +5290,7 @@ rlimit_resource_type(VALUE rtype)
case T_SYMBOL:
v = rb_sym2str(rtype);
name = RSTRING_PTR(v);
+ len = RSTRING_LEN(v);
break;
default:
@@ -4809,6 +5299,7 @@ rlimit_resource_type(VALUE rtype)
rtype = v;
case T_STRING:
name = StringValueCStr(rtype);
+ len = RSTRING_LEN(rtype);
break;
}
/* fall through */
@@ -4818,13 +5309,13 @@ rlimit_resource_type(VALUE rtype)
return NUM2INT(rtype);
}
- r = rlimit_type_by_hname(name);
+ r = rlimit_type_by_hname(name, len);
if (r != -1)
return r;
- rb_raise(rb_eArgError, "invalid resource name: %"PRIsVALUE, rtype);
+ rb_raise(rb_eArgError, "invalid resource name: % "PRIsVALUE, rtype);
- UNREACHABLE;
+ UNREACHABLE_RETURN(-1);
}
static rlim_t
@@ -4865,7 +5356,7 @@ rlimit_resource_value(VALUE rval)
#endif
rb_raise(rb_eArgError, "invalid resource value: %"PRIsVALUE, rval);
- UNREACHABLE;
+ UNREACHABLE_RETURN((rlim_t)-1);
}
#endif
@@ -4999,6 +5490,239 @@ check_gid_switch(void)
}
+#if defined(HAVE_PWD_H)
+/**
+ * Best-effort attempt to obtain the name of the login user, if any,
+ * associated with the process. Processes not descended from login(1) (or
+ * similar) may not have a logged-in user; returns Qnil in that case.
+ */
+VALUE
+rb_getlogin(void)
+{
+#if ( !defined(USE_GETLOGIN_R) && !defined(USE_GETLOGIN) )
+ return Qnil;
+#else
+ char MAYBE_UNUSED(*login) = NULL;
+
+# ifdef USE_GETLOGIN_R
+
+ long loginsize = GETLOGIN_R_SIZE_INIT; /* maybe -1 */
+
+ if (loginsize < 0)
+ loginsize = GETLOGIN_R_SIZE_DEFAULT;
+
+ VALUE maybe_result = rb_str_buf_new(loginsize);
+
+ login = RSTRING_PTR(maybe_result);
+ loginsize = rb_str_capacity(maybe_result);
+ rb_str_set_len(maybe_result, loginsize);
+
+ int gle;
+ errno = 0;
+ while ((gle = getlogin_r(login, loginsize)) != 0) {
+
+ if (gle == ENOTTY || gle == ENXIO || gle == ENOENT) {
+ rb_str_resize(maybe_result, 0);
+ return Qnil;
+ }
+
+ if (gle != ERANGE || loginsize >= GETLOGIN_R_SIZE_LIMIT) {
+ rb_str_resize(maybe_result, 0);
+ rb_syserr_fail(gle, "getlogin_r");
+ }
+
+ rb_str_modify_expand(maybe_result, loginsize);
+ login = RSTRING_PTR(maybe_result);
+ loginsize = rb_str_capacity(maybe_result);
+ }
+
+ if (login == NULL) {
+ rb_str_resize(maybe_result, 0);
+ return Qnil;
+ }
+
+ return maybe_result;
+
+# elif USE_GETLOGIN
+
+ errno = 0;
+ login = getlogin();
+ if (errno) {
+ if (errno == ENOTTY || errno == ENXIO || errno == ENOENT) {
+ return Qnil;
+ }
+ rb_syserr_fail(errno, "getlogin");
+ }
+
+ return login ? rb_str_new_cstr(login) : Qnil;
+# endif
+
+#endif
+}
+
+VALUE
+rb_getpwdirnam_for_login(VALUE login_name)
+{
+#if ( !defined(USE_GETPWNAM_R) && !defined(USE_GETPWNAM) )
+ return Qnil;
+#else
+
+ if (NIL_P(login_name)) {
+ /* nothing to do; no name with which to query the password database */
+ return Qnil;
+ }
+
+ char *login = RSTRING_PTR(login_name);
+
+ struct passwd *pwptr;
+
+# ifdef USE_GETPWNAM_R
+
+ struct passwd pwdnm;
+ char *bufnm;
+ long bufsizenm = GETPW_R_SIZE_INIT; /* maybe -1 */
+
+ if (bufsizenm < 0)
+ bufsizenm = GETPW_R_SIZE_DEFAULT;
+
+ VALUE getpwnm_tmp = rb_str_tmp_new(bufsizenm);
+
+ bufnm = RSTRING_PTR(getpwnm_tmp);
+ bufsizenm = rb_str_capacity(getpwnm_tmp);
+ rb_str_set_len(getpwnm_tmp, bufsizenm);
+
+ int enm;
+ errno = 0;
+ while ((enm = getpwnam_r(login, &pwdnm, bufnm, bufsizenm, &pwptr)) != 0) {
+
+ if (enm == ENOENT || enm== ESRCH || enm == EBADF || enm == EPERM) {
+ /* not found; non-errors */
+ rb_str_resize(getpwnm_tmp, 0);
+ return Qnil;
+ }
+
+ if (enm != ERANGE || bufsizenm >= GETPW_R_SIZE_LIMIT) {
+ rb_str_resize(getpwnm_tmp, 0);
+ rb_syserr_fail(enm, "getpwnam_r");
+ }
+
+ rb_str_modify_expand(getpwnm_tmp, bufsizenm);
+ bufnm = RSTRING_PTR(getpwnm_tmp);
+ bufsizenm = rb_str_capacity(getpwnm_tmp);
+ }
+
+ if (pwptr == NULL) {
+ /* no record in the password database for the login name */
+ rb_str_resize(getpwnm_tmp, 0);
+ return Qnil;
+ }
+
+ /* found it */
+ VALUE result = rb_str_new_cstr(pwptr->pw_dir);
+ rb_str_resize(getpwnm_tmp, 0);
+ return result;
+
+# elif USE_GETPWNAM
+
+ errno = 0;
+ pwptr = getpwnam(login);
+ if (pwptr) {
+ /* found it */
+ return rb_str_new_cstr(pwptr->pw_dir);
+ }
+ if (errno
+ /* avoid treating as errors errno values that indicate "not found" */
+ && ( errno != ENOENT && errno != ESRCH && errno != EBADF && errno != EPERM)) {
+ rb_syserr_fail(errno, "getpwnam");
+ }
+
+ return Qnil; /* not found */
+# endif
+
+#endif
+}
+
+/**
+ * Look up the user's dflt home dir in the password db, by uid.
+ */
+VALUE
+rb_getpwdiruid(void)
+{
+# if !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID)
+ /* Should never happen... </famous-last-words> */
+ return Qnil;
+# else
+ uid_t ruid = getuid();
+
+ struct passwd *pwptr;
+
+# ifdef USE_GETPWUID_R
+
+ struct passwd pwdid;
+ char *bufid;
+ long bufsizeid = GETPW_R_SIZE_INIT; /* maybe -1 */
+
+ if (bufsizeid < 0)
+ bufsizeid = GETPW_R_SIZE_DEFAULT;
+
+ VALUE getpwid_tmp = rb_str_tmp_new(bufsizeid);
+
+ bufid = RSTRING_PTR(getpwid_tmp);
+ bufsizeid = rb_str_capacity(getpwid_tmp);
+ rb_str_set_len(getpwid_tmp, bufsizeid);
+
+ int eid;
+ errno = 0;
+ while ((eid = getpwuid_r(ruid, &pwdid, bufid, bufsizeid, &pwptr)) != 0) {
+ if (eid == ENOENT || eid== ESRCH || eid == EBADF || eid == EPERM) {
+ /* not found; non-errors */
+ rb_str_resize(getpwid_tmp, 0);
+ return Qnil;
+ }
+
+ if (eid != ERANGE || bufsizeid >= GETPW_R_SIZE_LIMIT) {
+ rb_str_resize(getpwid_tmp, 0);
+ rb_syserr_fail(eid, "getpwuid_r");
+ }
+
+ rb_str_modify_expand(getpwid_tmp, bufsizeid);
+ bufid = RSTRING_PTR(getpwid_tmp);
+ bufsizeid = rb_str_capacity(getpwid_tmp);
+ }
+
+ if (pwptr == NULL) {
+ /* no record in the password database for the uid */
+ rb_str_resize(getpwid_tmp, 0);
+ return Qnil;
+ }
+
+ /* found it */
+ VALUE result = rb_str_new_cstr(pwptr->pw_dir);
+ rb_str_resize(getpwid_tmp, 0);
+ return result;
+
+# elif defined(USE_GETPWUID)
+
+ errno = 0;
+ pwptr = getpwuid(ruid);
+ if (pwptr) {
+ /* found it */
+ return rb_str_new_cstr(pwptr->pw_dir);
+ }
+ if (errno
+ /* avoid treating as errors errno values that indicate "not found" */
+ && ( errno == ENOENT || errno == ESRCH || errno == EBADF || errno == EPERM)) {
+ rb_syserr_fail(errno, "getpwuid");
+ }
+
+ return Qnil; /* not found */
+# endif
+
+#endif /* !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID) */
+}
+#endif /* HAVE_PWD_H */
+
+
/*********************************************************************
* Document-class: Process::Sys
*
@@ -5030,21 +5754,19 @@ obj2uid(VALUE id
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_buf = rb_alloc_tmp_buffer(getpw_tmp, getpw_buf_len);
- }
- else {
- getpw_buf = RSTRING_PTR(*getpw_tmp);
- getpw_buf_len = rb_str_capacity(*getpw_tmp);
+ *getpw_tmp = rb_str_tmp_new(getpw_buf_len);
}
- errno = ERANGE;
- /* gepwnam_r() on MacOS X doesn't set errno if buffer size is insufficient */
- while (getpwnam_r(usrname, &pwbuf, getpw_buf, getpw_buf_len, &pwptr)) {
- int e = errno;
+ 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_free_tmp_buffer(getpw_tmp);
+ rb_str_resize(*getpw_tmp, 0);
rb_syserr_fail(e, "getpwnam_r");
}
rb_str_modify_expand(*getpw_tmp, getpw_buf_len);
@@ -5109,21 +5831,19 @@ obj2gid(VALUE id
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_buf = rb_alloc_tmp_buffer(getgr_tmp, getgr_buf_len);
- }
- else {
- getgr_buf = RSTRING_PTR(*getgr_tmp);
- getgr_buf_len = rb_str_capacity(*getgr_tmp);
+ *getgr_tmp = rb_str_tmp_new(getgr_buf_len);
}
- errno = ERANGE;
- /* gegrnam_r() on MacOS X doesn't set errno if buffer size is insufficient */
- while (getgrnam_r(grpname, &grbuf, getgr_buf, getgr_buf_len, &grptr)) {
- int e = errno;
+ 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_free_tmp_buffer(getgr_tmp);
+ rb_str_resize(*getgr_tmp, 0);
rb_syserr_fail(e, "getgrnam_r");
}
rb_str_modify_expand(*getgr_tmp, getgr_buf_len);
@@ -5812,11 +6532,24 @@ maxgroups(void)
* call-seq:
* Process.groups -> array
*
- * Get an <code>Array</code> of the gids of groups in the
+ * Get an <code>Array</code> of the group IDs in the
* supplemental group access list for this process.
*
* Process.groups #=> [27, 6, 10, 11]
*
+ * Note that this method is just a wrapper of getgroups(2).
+ * This means that the following characteristics of
+ * the result completely depend on your system:
+ *
+ * - the result is sorted
+ * - the result includes effective GIDs
+ * - the result does not include duplicated GIDs
+ *
+ * You can make sure to get a sorted unique GID list of
+ * the current process by this expression:
+ *
+ * Process.groups.uniq.sort
+ *
*/
static VALUE
@@ -5919,7 +6652,7 @@ proc_setgroups(VALUE obj, VALUE ary)
static VALUE
proc_initgroups(VALUE obj, VALUE uname, VALUE base_grp)
{
- if (initgroups(StringValuePtr(uname), OBJ2GID(base_grp)) != 0) {
+ if (initgroups(StringValueCStr(uname), OBJ2GID(base_grp)) != 0) {
rb_sys_fail(0);
}
return proc_getgroups(obj);
@@ -6018,10 +6751,11 @@ rb_daemon(int nochdir, int noclose)
{
int err = 0;
#ifdef HAVE_DAEMON
+ if (mjit_enabled) mjit_pause(FALSE); /* Don't leave locked mutex to child. */
before_fork_ruby();
err = daemon(nochdir, noclose);
after_fork_ruby();
- rb_thread_atfork();
+ rb_thread_atfork(); /* calls mjit_resume() */
#else
int n;
@@ -6708,7 +7442,7 @@ p_uid_switch(VALUE obj)
rb_syserr_fail(EPERM, 0);
}
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
#else
static VALUE
@@ -6821,7 +7555,7 @@ p_gid_switch(VALUE obj)
rb_syserr_fail(EPERM, 0);
}
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
#else
static VALUE
@@ -6860,20 +7594,15 @@ p_gid_switch(VALUE obj)
static long
get_clk_tck(void)
{
- long hertz =
#ifdef HAVE__SC_CLK_TCK
- (double)sysconf(_SC_CLK_TCK);
+ return sysconf(_SC_CLK_TCK);
+#elif defined CLK_TCK
+ return CLK_TCK;
+#elif defined HZ
+ return HZ;
#else
-#ifndef HZ
-# ifdef CLK_TCK
-# define HZ CLK_TCK
-# else
-# define HZ 60
-# endif
-#endif /* HZ */
- HZ;
+ return 60;
#endif
- return hertz;
}
/*
@@ -6891,15 +7620,26 @@ get_clk_tck(void)
VALUE
rb_proc_times(VALUE obj)
{
- const double hertz = get_clk_tck();
- struct tms buf;
VALUE utime, stime, cutime, cstime, ret;
+#if defined(RUSAGE_SELF) && defined(RUSAGE_CHILDREN)
+ struct rusage usage_s, usage_c;
+
+ if (getrusage(RUSAGE_SELF, &usage_s) != 0 || getrusage(RUSAGE_CHILDREN, &usage_c) != 0)
+ 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);
+ cstime = DBL2NUM((double)usage_c.ru_stime.tv_sec + (double)usage_c.ru_stime.tv_usec/1e6);
+#else
+ const double hertz = (double)get_clk_tck();
+ struct tms buf;
times(&buf);
utime = DBL2NUM(buf.tms_utime / hertz);
stime = DBL2NUM(buf.tms_stime / hertz);
cutime = DBL2NUM(buf.tms_cutime / hertz);
cstime = DBL2NUM(buf.tms_cstime / hertz);
+#endif
ret = rb_struct_new(rb_cProcessTms, utime, stime, cutime, cstime);
RB_GC_GUARD(utime);
RB_GC_GUARD(stime);
@@ -6916,11 +7656,13 @@ typedef LONG_LONG timetick_int_t;
#define TIMETICK_INT_MIN LLONG_MIN
#define TIMETICK_INT_MAX LLONG_MAX
#define TIMETICK_INT2NUM(v) LL2NUM(v)
+#define MUL_OVERFLOW_TIMETICK_P(a, b) MUL_OVERFLOW_LONG_LONG_P(a, b)
#else
typedef long timetick_int_t;
#define TIMETICK_INT_MIN LONG_MIN
#define TIMETICK_INT_MAX LONG_MAX
#define TIMETICK_INT2NUM(v) LONG2NUM(v)
+#define MUL_OVERFLOW_TIMETICK_P(a, b) MUL_OVERFLOW_LONG_P(a, b)
#endif
CONSTFUNC(static timetick_int_t gcd_timetick_int(timetick_int_t, timetick_int_t));
@@ -7036,8 +7778,7 @@ timetick2integer(struct timetick *ttp,
timetick_int_t t = ttp->giga_count * 1000000000 + ttp->count;
for (i = 0; i < num_numerators; i++) {
timetick_int_t factor = numerators[i];
- if (MUL_OVERFLOW_SIGNED_INTEGER_P(factor, t,
- TIMETICK_INT_MIN, TIMETICK_INT_MAX))
+ if (MUL_OVERFLOW_TIMETICK_P(factor, t))
goto generic;
t *= factor;
}
@@ -7100,7 +7841,7 @@ make_clock_result(struct timetick *ttp,
}
#ifdef __APPLE__
-static mach_timebase_info_data_t *
+static const mach_timebase_info_data_t *
get_mach_timebase_info(void)
{
static mach_timebase_info_data_t sTimebaseInfo;
@@ -7111,6 +7852,14 @@ get_mach_timebase_info(void)
return &sTimebaseInfo;
}
+
+double
+ruby_real_ms_time(void)
+{
+ const mach_timebase_info_data_t *info = get_mach_timebase_info();
+ uint64_t t = mach_absolute_time();
+ return (double)t * info->numer / info->denom / 1e6;
+}
#endif
/*
@@ -7256,8 +8005,9 @@ rb_clock_gettime(int argc, VALUE *argv)
if (SYMBOL_P(clk_id)) {
/*
* Non-clock_gettime clocks are provided by symbol clk_id.
- *
- * gettimeofday is always available on platforms supported by Ruby.
+ */
+#ifdef HAVE_GETTIMEOFDAY
+ /*
* GETTIMEOFDAY_BASED_CLOCK_REALTIME is used for
* CLOCK_REALTIME if clock_gettime is not available.
*/
@@ -7272,6 +8022,7 @@ rb_clock_gettime(int argc, VALUE *argv)
denominators[num_denominators++] = 1000000000;
goto success;
}
+#endif
#define RUBY_TIME_BASED_CLOCK_REALTIME ID2SYM(id_TIME_BASED_CLOCK_REALTIME)
if (clk_id == RUBY_TIME_BASED_CLOCK_REALTIME) {
@@ -7364,7 +8115,7 @@ rb_clock_gettime(int argc, VALUE *argv)
#ifdef __APPLE__
#define RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC ID2SYM(id_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC)
if (clk_id == RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC) {
- 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;
@@ -7503,7 +8254,7 @@ rb_clock_getres(int argc, VALUE *argv)
#ifdef RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
if (clk_id == RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC) {
- 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;
@@ -7539,9 +8290,9 @@ rb_clock_getres(int argc, VALUE *argv)
}
VALUE rb_mProcess;
-VALUE rb_mProcUID;
-VALUE rb_mProcGID;
-VALUE rb_mProcID_Syscall;
+static VALUE rb_mProcUID;
+static VALUE rb_mProcGID;
+static VALUE rb_mProcID_Syscall;
/*
@@ -7588,6 +8339,7 @@ InitVM_process(void)
rb_define_singleton_method(rb_mProcess, "exit!", rb_f_exit_bang, -1);
rb_define_singleton_method(rb_mProcess, "exit", rb_f_exit, -1);
rb_define_singleton_method(rb_mProcess, "abort", rb_f_abort, -1);
+ rb_define_singleton_method(rb_mProcess, "last_status", proc_s_last_status, 0);
rb_define_module_function(rb_mProcess, "kill", rb_f_kill, -1); /* in signal.c */
rb_define_module_function(rb_mProcess, "wait", proc_wait, -1);
@@ -7597,6 +8349,7 @@ InitVM_process(void)
rb_define_module_function(rb_mProcess, "waitall", proc_waitall, 0);
rb_define_module_function(rb_mProcess, "detach", proc_detach, 1);
+ /* :nodoc: */
rb_cWaiter = rb_define_class_under(rb_mProcess, "Waiter", rb_cThread);
rb_undef_alloc_func(rb_cWaiter);
rb_undef_method(CLASS_OF(rb_cWaiter), "new");
@@ -7804,86 +8557,115 @@ InitVM_process(void)
rb_define_module_function(rb_mProcess, "times", rb_proc_times, 0);
#ifdef CLOCK_REALTIME
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_REALTIME", CLOCKID2NUM(CLOCK_REALTIME));
#elif defined(RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME)
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_REALTIME", RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME);
#endif
#ifdef CLOCK_MONOTONIC
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_MONOTONIC", CLOCKID2NUM(CLOCK_MONOTONIC));
#elif defined(RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC)
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_MONOTONIC", RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC);
#endif
#ifdef CLOCK_PROCESS_CPUTIME_ID
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_PROCESS_CPUTIME_ID", CLOCKID2NUM(CLOCK_PROCESS_CPUTIME_ID));
#elif defined(RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID)
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_PROCESS_CPUTIME_ID", RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID);
#endif
#ifdef CLOCK_THREAD_CPUTIME_ID
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_THREAD_CPUTIME_ID", CLOCKID2NUM(CLOCK_THREAD_CPUTIME_ID));
#endif
#ifdef CLOCK_VIRTUAL
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_VIRTUAL", CLOCKID2NUM(CLOCK_VIRTUAL));
#endif
#ifdef CLOCK_PROF
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_PROF", CLOCKID2NUM(CLOCK_PROF));
#endif
#ifdef CLOCK_REALTIME_FAST
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_REALTIME_FAST", CLOCKID2NUM(CLOCK_REALTIME_FAST));
#endif
#ifdef CLOCK_REALTIME_PRECISE
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_REALTIME_PRECISE", CLOCKID2NUM(CLOCK_REALTIME_PRECISE));
#endif
#ifdef CLOCK_REALTIME_COARSE
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_REALTIME_COARSE", CLOCKID2NUM(CLOCK_REALTIME_COARSE));
#endif
#ifdef CLOCK_REALTIME_ALARM
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_REALTIME_ALARM", CLOCKID2NUM(CLOCK_REALTIME_ALARM));
#endif
#ifdef CLOCK_MONOTONIC_FAST
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_FAST", CLOCKID2NUM(CLOCK_MONOTONIC_FAST));
#endif
#ifdef CLOCK_MONOTONIC_PRECISE
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_PRECISE", CLOCKID2NUM(CLOCK_MONOTONIC_PRECISE));
#endif
#ifdef CLOCK_MONOTONIC_RAW
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_RAW", CLOCKID2NUM(CLOCK_MONOTONIC_RAW));
#endif
#ifdef CLOCK_MONOTONIC_RAW_APPROX
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_RAW_APPROX", CLOCKID2NUM(CLOCK_MONOTONIC_RAW_APPROX));
#endif
#ifdef CLOCK_MONOTONIC_COARSE
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_COARSE", CLOCKID2NUM(CLOCK_MONOTONIC_COARSE));
#endif
#ifdef CLOCK_BOOTTIME
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_BOOTTIME", CLOCKID2NUM(CLOCK_BOOTTIME));
#endif
#ifdef CLOCK_BOOTTIME_ALARM
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_BOOTTIME_ALARM", CLOCKID2NUM(CLOCK_BOOTTIME_ALARM));
#endif
#ifdef CLOCK_UPTIME
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_UPTIME", CLOCKID2NUM(CLOCK_UPTIME));
#endif
#ifdef CLOCK_UPTIME_FAST
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_UPTIME_FAST", CLOCKID2NUM(CLOCK_UPTIME_FAST));
#endif
#ifdef CLOCK_UPTIME_PRECISE
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_UPTIME_PRECISE", CLOCKID2NUM(CLOCK_UPTIME_PRECISE));
#endif
#ifdef CLOCK_UPTIME_RAW
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_UPTIME_RAW", CLOCKID2NUM(CLOCK_UPTIME_RAW));
#endif
#ifdef CLOCK_UPTIME_RAW_APPROX
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_UPTIME_RAW_APPROX", CLOCKID2NUM(CLOCK_UPTIME_RAW_APPROX));
#endif
#ifdef CLOCK_SECOND
+ /* see Process.clock_gettime */
rb_define_const(rb_mProcess, "CLOCK_SECOND", CLOCKID2NUM(CLOCK_SECOND));
#endif
rb_define_module_function(rb_mProcess, "clock_gettime", rb_clock_gettime, -1);
rb_define_module_function(rb_mProcess, "clock_getres", rb_clock_getres, -1);
#if defined(HAVE_TIMES) || defined(_WIN32)
+ /* Placeholder for rusage */
rb_cProcessTms = rb_struct_define_under(rb_mProcess, "Tms", "utime", "stime", "cutime", "cstime", NULL);
- rb_define_const(rb_cStruct, "Tms", rb_cProcessTms); /* for the backward compatibility */
+ /* An obsolete name of Process::Tms for backward compatibility */
+ rb_define_const(rb_cStruct, "Tms", rb_cProcessTms);
+ rb_deprecate_constant(rb_cStruct, "Tms");
#endif
SAVED_USER_ID = geteuid();
diff --git a/random.c b/random.c
index ee91c533aa..38dfaeb272 100644
--- a/random.c
+++ b/random.c
@@ -89,6 +89,11 @@ The original copyright notice follows.
#endif
#include "ruby_atomic.h"
+#ifdef __OpenBSD__
+/* to define OpenBSD for version check */
+#include <sys/param.h>
+#endif
+
typedef int int_must_be_32bit_at_least[sizeof(int) * CHAR_BIT < 32 ? -1 : 1];
/* Period parameters */
@@ -112,6 +117,9 @@ struct MT {
#define genrand_initialized(mt) ((mt)->next != 0)
#define uninit_genrand(mt) ((mt)->next = 0)
+NO_SANITIZE("unsigned-integer-overflow", static void init_genrand(struct MT *mt, unsigned int s));
+NO_SANITIZE("unsigned-integer-overflow", static void init_by_array(struct MT *mt, const uint32_t init_key[], int key_length));
+
/* initializes state[N] with a seed */
static void
init_genrand(struct MT *mt, unsigned int s)
@@ -302,6 +310,7 @@ VALUE rb_cRandom;
#define id_minus '-'
#define id_plus '+'
static ID id_rand, id_bytes;
+NORETURN(static void domain_error(void));
/* :nodoc: */
static void
@@ -338,7 +347,7 @@ get_rnd(VALUE obj)
{
rb_random_t *ptr;
TypedData_Get_Struct(obj, rb_random_t, &random_data_type, ptr);
- return ptr;
+ return rand_start(ptr);
}
static rb_random_t *
@@ -348,7 +357,7 @@ try_get_rnd(VALUE obj)
return rand_start(&default_rand);
}
if (!rb_typeddata_is_kind_of(obj, &random_data_type)) return NULL;
- return DATA_PTR(obj);
+ return rand_start(DATA_PTR(obj));
}
/* :nodoc: */
@@ -447,14 +456,21 @@ fill_random_bytes_urandom(void *seed, size_t size)
O_RDONLY, 0);
struct stat statbuf;
ssize_t ret = 0;
+ size_t offset = 0;
if (fd < 0) return -1;
rb_update_max_fd(fd);
if (fstat(fd, &statbuf) == 0 && S_ISCHR(statbuf.st_mode)) {
- ret = read(fd, seed, 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);
- if (ret < 0 || (size_t)ret < size) return -1;
return 0;
}
#else
@@ -462,12 +478,38 @@ fill_random_bytes_urandom(void *seed, size_t size)
#endif
#if 0
+#elif defined MAC_OS_X_VERSION_10_7 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7
+#include <Security/Security.h>
+
+static int
+fill_random_bytes_syscall(void *seed, size_t size, int unused)
+{
+ int status = SecRandomCopyBytes(kSecRandomDefault, size, seed);
+
+ if (status != errSecSuccess) {
+# if 0
+ CFStringRef s = SecCopyErrorMessageString(status, NULL);
+ const char *m = s ? CFStringGetCStringPtr(s, kCFStringEncodingUTF8) : NULL;
+ fprintf(stderr, "SecRandomCopyBytes failed: %d: %s\n", status,
+ m ? m : "unknown");
+ if (s) CFRelease(s);
+# endif
+ return -1;
+ }
+ return 0;
+}
#elif defined(HAVE_ARC4RANDOM_BUF)
static int
fill_random_bytes_syscall(void *buf, size_t size, int unused)
{
+#if (defined(__OpenBSD__) && OpenBSD >= 201411) || \
+ (defined(__NetBSD__) && __NetBSD_Version__ >= 700000000) || \
+ (defined(__FreeBSD__) && __FreeBSD_version >= 1200079)
arc4random_buf(buf, size);
return 0;
+#else
+ return -1;
+#endif
}
#elif defined(_WIN32)
static void
@@ -489,12 +531,12 @@ fill_random_bytes_syscall(void *seed, size_t size, int unused)
prov = (HCRYPTPROV)INVALID_HANDLE_VALUE;
}
old_prov = (HCRYPTPROV)ATOMIC_PTR_CAS(perm_prov, 0, prov);
- if (LIKELY(!old_prov)) { /* no other threads acquried */
+ if (LIKELY(!old_prov)) { /* no other threads acquired */
if (prov != (HCRYPTPROV)INVALID_HANDLE_VALUE) {
rb_gc_register_mark_object(Data_Wrap_Struct(0, 0, release_crypt, &perm_prov));
}
}
- else { /* another thread acquried */
+ else { /* another thread acquired */
if (prov != (HCRYPTPROV)INVALID_HANDLE_VALUE) {
CryptReleaseContext(prov, 0);
}
@@ -505,7 +547,7 @@ fill_random_bytes_syscall(void *seed, size_t size, int unused)
CryptGenRandom(prov, size, seed);
return 0;
}
-#elif defined __linux__ && defined SYS_getrandom
+#elif defined __linux__ && defined __NR_getrandom
#include <linux/random.h>
# ifndef GRND_NONBLOCK
@@ -518,16 +560,20 @@ fill_random_bytes_syscall(void *seed, size_t size, int need_secure)
static rb_atomic_t try_syscall = 1;
if (try_syscall) {
long ret;
+ size_t offset = 0;
int flags = 0;
if (!need_secure)
flags = GRND_NONBLOCK;
- errno = 0;
- ret = syscall(SYS_getrandom, seed, size, flags);
- if (errno == ENOSYS) {
- ATOMIC_SET(try_syscall, 0);
- return -1;
- }
- if ((size_t)ret == size) return 0;
+ do {
+ errno = 0;
+ ret = syscall(__NR_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;
}
return -1;
}
@@ -535,27 +581,38 @@ fill_random_bytes_syscall(void *seed, size_t size, int need_secure)
# define fill_random_bytes_syscall(seed, size, need_secure) -1
#endif
-static int
-fill_random_bytes(void *seed, size_t size, int need_secure)
+int
+ruby_fill_random_bytes(void *seed, size_t size, int need_secure)
{
int ret = fill_random_bytes_syscall(seed, size, need_secure);
if (ret == 0) return ret;
return fill_random_bytes_urandom(seed, size);
}
+#define fill_random_bytes ruby_fill_random_bytes
+
static void
fill_random_seed(uint32_t *seed, size_t cnt)
{
static int n = 0;
+#if defined HAVE_CLOCK_GETTIME
+ struct timespec tv;
+#elif defined HAVE_GETTIMEOFDAY
struct timeval tv;
+#endif
size_t len = cnt * sizeof(*seed);
memset(seed, 0, len);
- fill_random_bytes(seed, len, TRUE);
+ fill_random_bytes(seed, len, FALSE);
+#if defined HAVE_CLOCK_GETTIME
+ clock_gettime(CLOCK_REALTIME, &tv);
+ seed[0] ^= tv.tv_nsec;
+#elif defined HAVE_GETTIMEOFDAY
gettimeofday(&tv, 0);
seed[0] ^= tv.tv_usec;
+#endif
seed[1] ^= (uint32_t)tv.tv_sec;
#if SIZEOF_TIME_T > SIZEOF_INT
seed[0] ^= (uint32_t)((time_t)tv.tv_sec >> SIZEOF_INT * CHAR_BIT);
@@ -603,11 +660,20 @@ random_seed(void)
}
/*
- * call-seq: Random.raw_seed(size) -> string
+ * call-seq: Random.urandom(size) -> string
*
- * Returns a raw seed string, using platform providing features.
+ * Returns a string, using platform providing features.
+ * Returned value is expected to be a cryptographically secure
+ * pseudo-random number in binary form.
+ * This method raises a RuntimeError if the feature provided by platform
+ * failed to prepare the result.
*
- * Random.raw_seed(8) #=> "\x78\x41\xBA\xAF\x7D\xEA\xD8\xEA"
+ * In 2017, Linux manpage random(7) writes that "no cryptographic
+ * primitive available today can hope to promise more than 256 bits of
+ * security". So it might be questionable to pass size > 32 to this
+ * method.
+ *
+ * Random.urandom(8) #=> "\x78\x41\xBA\xAF\x7D\xEA\xD8\xEA"
*/
static VALUE
random_raw_seed(VALUE self, VALUE size)
@@ -615,7 +681,8 @@ random_raw_seed(VALUE self, VALUE size)
long n = NUM2ULONG(size);
VALUE buf = rb_str_new(0, n);
if (n == 0) return buf;
- if (fill_random_bytes(RSTRING_PTR(buf), n, FALSE)) return Qnil;
+ if (fill_random_bytes(RSTRING_PTR(buf), n, TRUE))
+ rb_raise(rb_eRuntimeError, "failed to get urandom");
return buf;
}
@@ -716,19 +783,17 @@ random_load(VALUE obj, VALUE dump)
rb_random_t *rnd = get_rnd(obj);
struct MT *mt = &rnd->mt;
VALUE state, left = INT2FIX(1), seed = INT2FIX(0);
- const VALUE *ary;
unsigned long x;
rb_check_copyable(obj, dump);
Check_Type(dump, T_ARRAY);
- ary = RARRAY_CONST_PTR(dump);
switch (RARRAY_LEN(dump)) {
case 3:
- seed = ary[2];
+ seed = RARRAY_AREF(dump, 2);
case 2:
- left = ary[1];
+ left = RARRAY_AREF(dump, 1);
case 1:
- state = ary[0];
+ state = RARRAY_AREF(dump, 0);
break;
default:
rb_raise(rb_eArgError, "wrong dump data");
@@ -1018,7 +1083,7 @@ 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(obj, id_rand, 1, &lim));
+ 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);
@@ -1063,7 +1128,7 @@ random_ulong_limited_big(VALUE obj, rb_random_t *rnd, VALUE vmax)
static VALUE genrand_bytes(rb_random_t *rnd, long n);
/*
- * call-seq: prng.bytes(size) -> a_string
+ * call-seq: prng.bytes(size) -> string
*
* Returns a random binary string containing +size+ bytes.
*
@@ -1113,17 +1178,28 @@ rb_random_bytes(VALUE obj, long n)
return genrand_bytes(rnd, n);
}
+/*
+ * call-seq: Random.bytes(size) -> string
+ *
+ * Returns a random binary string.
+ * The argument +size+ specifies the length of the returned string.
+ */
+static VALUE
+random_s_bytes(VALUE obj, VALUE len)
+{
+ rb_random_t *rnd = rand_start(&default_rand);
+ return genrand_bytes(rnd, NUM2LONG(rb_to_int(len)));
+}
+
static VALUE
range_values(VALUE vmax, VALUE *begp, VALUE *endp, int *exclp)
{
- VALUE end, r;
+ VALUE end;
if (!rb_range_values(vmax, begp, &end, exclp)) return Qfalse;
if (endp) *endp = end;
- if (!rb_respond_to(end, id_minus)) return Qfalse;
- r = rb_funcallv(end, id_minus, 1, begp);
- if (NIL_P(r)) return Qfalse;
- return r;
+ if (NIL_P(end)) return Qnil;
+ return rb_check_funcall_default(end, id_minus, 1, begp, Qfalse);
}
static VALUE
@@ -1162,7 +1238,6 @@ rand_int(VALUE obj, rb_random_t *rnd, VALUE vmax, int restrictive)
}
}
-NORETURN(static void domain_error(void));
static void
domain_error(void)
{
@@ -1208,6 +1283,7 @@ rand_range(VALUE obj, rb_random_t* rnd, VALUE range)
if ((v = vmax = range_values(range, &beg, &end, &excl)) == Qfalse)
return Qfalse;
+ if (NIL_P(v)) domain_error();
if (!RB_TYPE_P(vmax, T_FLOAT) && (v = rb_check_to_int(vmax), !NIL_P(v))) {
long max;
vmax = v;
@@ -1344,6 +1420,16 @@ rand_random(int argc, VALUE *argv, VALUE obj, rb_random_t *rnd)
return rand_range(obj, rnd, vmax);
}
+/*
+ * call-seq:
+ * prng.random_number -> float
+ * prng.random_number(max) -> number
+ * prng.rand -> float
+ * prng.rand(max) -> number
+ *
+ * Generates formatted random number from raw random bytes.
+ * See Random#rand.
+ */
static VALUE
rand_random_number(int argc, VALUE *argv, VALUE obj)
{
@@ -1457,7 +1543,7 @@ random_s_rand(int argc, VALUE *argv, VALUE obj)
}
#define SIP_HASH_STREAMING 0
-#define sip_hash24 ruby_sip_hash24
+#define sip_hash13 ruby_sip_hash13
#if !defined _WIN32 && !defined BYTE_ORDER
# ifdef WORDS_BIGENDIAN
# define BYTE_ORDER BIG_ENDIAN
@@ -1473,50 +1559,36 @@ random_s_rand(int argc, VALUE *argv, VALUE obj)
#endif
#include "siphash.c"
-static st_index_t hashseed;
-typedef uint8_t sipseed_keys_t[16];
-static union {
- sipseed_keys_t key;
- uint32_t u32[type_roomof(sipseed_keys_t, uint32_t)];
-} sipseed;
+typedef struct {
+ st_index_t hash;
+ uint8_t sip[16];
+} seed_keys_t;
-static void
-init_hashseed(struct MT *mt)
-{
- hashseed = genrand_int32(mt);
-#if SIZEOF_ST_INDEX_T*CHAR_BIT > 4*8
- hashseed <<= 32;
- hashseed |= genrand_int32(mt);
-#endif
-#if SIZEOF_ST_INDEX_T*CHAR_BIT > 8*8
- hashseed <<= 32;
- hashseed |= genrand_int32(mt);
-#endif
-#if SIZEOF_ST_INDEX_T*CHAR_BIT > 12*8
- hashseed <<= 32;
- hashseed |= genrand_int32(mt);
-#endif
-}
+static union {
+ seed_keys_t key;
+ uint32_t u32[type_roomof(seed_keys_t, uint32_t)];
+} seed;
static void
-init_siphash(struct MT *mt)
+init_seed(struct MT *mt)
{
int i;
- for (i = 0; i < numberof(sipseed.u32); ++i)
- sipseed.u32[i] = genrand_int32(mt);
+ for (i = 0; i < numberof(seed.u32); ++i)
+ seed.u32[i] = genrand_int32(mt);
}
+NO_SANITIZE("unsigned-integer-overflow", extern st_index_t rb_hash_start(st_index_t h));
st_index_t
rb_hash_start(st_index_t h)
{
- return st_hash_start(hashseed + h);
+ return st_hash_start(seed.key.hash + h);
}
st_index_t
rb_memhash(const void *ptr, long len)
{
- sip_uint64_t h = sip_hash24(sipseed.key, ptr, len);
+ sip_uint64_t h = sip_hash13(seed.key.sip, ptr, len);
#ifdef HAVE_UINT64_T
return (st_index_t)h;
#else
@@ -1539,8 +1611,7 @@ Init_RandomSeedCore(void)
fill_random_seed(initial_seed, DEFAULT_SEED_CNT);
init_by_array(&mt, initial_seed, DEFAULT_SEED_CNT);
- init_hashseed(&mt);
- init_siphash(&mt);
+ init_seed(&mt);
explicit_bzero(initial_seed, DEFAULT_SEED_LEN);
}
@@ -1626,19 +1697,24 @@ InitVM_Random(void)
{
/* Direct access to Ruby's Pseudorandom number generator (PRNG). */
VALUE rand_default = Init_Random_default();
+ /* The default Pseudorandom number generator. Used by class
+ * methods of Random. */
rb_define_const(rb_cRandom, "DEFAULT", rand_default);
}
rb_define_singleton_method(rb_cRandom, "srand", rb_f_srand, -1);
rb_define_singleton_method(rb_cRandom, "rand", random_s_rand, -1);
+ rb_define_singleton_method(rb_cRandom, "bytes", random_s_bytes, 1);
rb_define_singleton_method(rb_cRandom, "new_seed", random_seed, 0);
- rb_define_singleton_method(rb_cRandom, "raw_seed", random_raw_seed, 1);
+ rb_define_singleton_method(rb_cRandom, "urandom", random_raw_seed, 1);
rb_define_private_method(CLASS_OF(rb_cRandom), "state", random_s_state, 0);
rb_define_private_method(CLASS_OF(rb_cRandom), "left", random_s_left, 0);
{
+ /* Format raw random number as Random does */
VALUE m = rb_define_module_under(rb_cRandom, "Formatter");
rb_include_module(rb_cRandom, m);
+ rb_extend_object(rb_cRandom, m);
rb_define_method(m, "random_number", rand_random_number, -1);
rb_define_method(m, "rand", rand_random_number, -1);
}
diff --git a/range.c b/range.c
index 422ed29f15..293d9bdbcf 100644
--- a/range.c
+++ b/range.c
@@ -18,15 +18,12 @@
#include <math.h>
VALUE rb_cRange;
-static ID id_beg, id_end, id_excl, id_integer_p, id_div;
+static ID id_beg, id_end, id_excl;
#define id_cmp idCmp
#define id_succ idSucc
static VALUE r_cover_p(VALUE, VALUE, VALUE, VALUE);
-#define RANGE_BEG(r) (RSTRUCT(r)->as.ary[0])
-#define RANGE_END(r) (RSTRUCT(r)->as.ary[1])
-#define RANGE_EXCL(r) (RSTRUCT(r)->as.ary[2])
#define RANGE_SET_BEG(r, v) (RSTRUCT_SET(r, 0, v))
#define RANGE_SET_END(r, v) (RSTRUCT_SET(r, 1, v))
#define RANGE_SET_EXCL(r, v) (RSTRUCT_SET(r, 2, v))
@@ -34,33 +31,15 @@ static VALUE r_cover_p(VALUE, VALUE, VALUE, VALUE);
#define EXCL(r) RTEST(RANGE_EXCL(r))
-static VALUE
-range_failed(void)
-{
- rb_raise(rb_eArgError, "bad value for range");
- return Qnil; /* dummy */
-}
-
-static VALUE
-range_check(VALUE *args)
-{
- return rb_funcall(args[0], id_cmp, 1, args[1]);
-}
-
static void
range_init(VALUE range, VALUE beg, VALUE end, VALUE exclude_end)
{
- VALUE args[2];
-
- args[0] = beg;
- args[1] = end;
-
- if (!FIXNUM_P(beg) || !FIXNUM_P(end)) {
+ if ((!FIXNUM_P(beg) || !FIXNUM_P(end)) && !NIL_P(end)) {
VALUE v;
- v = rb_rescue(range_check, (VALUE)args, range_failed, 0);
+ v = rb_funcall(beg, id_cmp, 1, end);
if (NIL_P(v))
- range_failed();
+ rb_raise(rb_eArgError, "bad value for range");
}
RANGE_SET_EXCL(range, exclude_end);
@@ -92,7 +71,7 @@ range_modify(VALUE range)
* Range.new(begin, end, exclude_end=false) -> rng
*
* Constructs a range using the given +begin+ and +end+. If the +exclude_end+
- * parameter is omitted or is <code>false</code>, the +rng+ will include
+ * parameter is omitted or is <code>false</code>, the range will include
* the end object; otherwise, it will be excluded.
*/
@@ -250,11 +229,11 @@ range_hash(VALUE range)
hash = rb_hash_uint(hash, EXCL(range) << 24);
hash = rb_hash_end(hash);
- return LONG2FIX(hash);
+ return ST2FIX(hash);
}
static void
-range_each_func(VALUE range, rb_block_call_func *func, VALUE arg)
+range_each_func(VALUE range, int (*func)(VALUE, VALUE), VALUE arg)
{
int c;
VALUE b = RANGE_BEG(range);
@@ -263,21 +242,21 @@ range_each_func(VALUE range, rb_block_call_func *func, VALUE arg)
if (EXCL(range)) {
while (r_less(v, e) < 0) {
- (*func) (v, arg, 0, 0, 0);
+ if ((*func)(v, arg)) break;
v = rb_funcallv(v, id_succ, 0, 0);
}
}
else {
while ((c = r_less(v, e)) <= 0) {
- (*func) (v, arg, 0, 0, 0);
+ if ((*func)(v, arg)) break;
if (!c) break;
v = rb_funcallv(v, id_succ, 0, 0);
}
}
}
-static VALUE
-sym_step_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arg))
+static int
+sym_step_i(VALUE i, VALUE arg)
{
VALUE *iter = (VALUE *)arg;
@@ -291,11 +270,11 @@ sym_step_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arg))
rb_yield(rb_str_intern(i));
iter[0] = iter[1];
}
- return Qnil;
+ return 0;
}
-static VALUE
-step_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arg))
+static int
+step_i(VALUE i, VALUE arg)
{
VALUE *iter = (VALUE *)arg;
@@ -309,7 +288,7 @@ step_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arg))
rb_yield(i);
iter[0] = iter[1];
}
- return Qnil;
+ return 0;
}
static int
@@ -368,9 +347,14 @@ range_step_size(VALUE range, VALUE args, VALUE eobj)
}
/*
+ * Document-method: Range#step
+ * Document-method: Range#%
* call-seq:
* rng.step(n=1) {| obj | block } -> rng
* rng.step(n=1) -> an_enumerator
+ * rng.step(n=1) -> an_arithmetic_sequence
+ * rng % n -> an_enumerator
+ * rng % n -> an_arithmetic_sequence
*
* Iterates over the range, passing each <code>n</code>th element to the block.
* If begin and end are numeric, +n+ is added for each iteration.
@@ -378,6 +362,8 @@ range_step_size(VALUE range, VALUE args, VALUE eobj)
* range elements.
*
* If no block is given, an enumerator is returned instead.
+ * Especially, the enumerator is an Enumerator::ArithmeticSequence
+ * if begin and end of the range are numeric.
*
* range = Xs.new(1)..Xs.new(10)
* range.step(2) {|x| puts x}
@@ -406,19 +392,33 @@ range_step(int argc, VALUE *argv, VALUE range)
{
VALUE b, e, step, tmp;
- RETURN_SIZED_ENUMERATOR(range, argc, argv, range_step_size);
-
b = RANGE_BEG(range);
e = RANGE_END(range);
- if (argc == 0) {
- step = INT2FIX(1);
- }
- else {
- rb_scan_args(argc, argv, "01", &step);
- step = check_step_domain(step);
+ step = (!rb_check_arity(argc, 0, 1) ? INT2FIX(1) : argv[0]);
+
+ if (!rb_block_given_p()) {
+ if (rb_obj_is_kind_of(b, rb_cNumeric) && (NIL_P(e) || rb_obj_is_kind_of(e, rb_cNumeric))) {
+ return rb_arith_seq_new(range, ID2SYM(rb_frame_this_func()), argc, argv,
+ range_step_size, b, e, step, EXCL(range));
+ }
+
+ RETURN_SIZED_ENUMERATOR(range, argc, argv, range_step_size);
}
- if (FIXNUM_P(b) && FIXNUM_P(e) && FIXNUM_P(step)) { /* fixnums are special */
+ step = check_step_domain(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);
+
+ 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);
@@ -432,16 +432,20 @@ range_step(int argc, VALUE *argv, VALUE range)
}
}
- else if (SYMBOL_P(b) && SYMBOL_P(e)) { /* symbols are special */
- VALUE args[2], iter[2];
-
- args[0] = rb_sym2str(e);
- args[1] = EXCL(range) ? Qtrue : Qfalse;
+ else if (SYMBOL_P(b) && (NIL_P(e) || SYMBOL_P(e))) { /* symbols are special */
+ VALUE iter[2];
iter[0] = INT2FIX(1);
iter[1] = step;
- rb_block_call(rb_sym2str(b), rb_intern("upto"), 2, args, 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))) {
+ else if (ruby_float_step(b, e, step, EXCL(range), TRUE)) {
/* done */
}
else if (rb_obj_is_kind_of(b, rb_cNumeric) ||
@@ -451,7 +455,7 @@ range_step(int argc, VALUE *argv, VALUE range)
VALUE v = b;
int i = 0;
- while (RTEST(rb_funcall(v, op, 1, e))) {
+ 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));
@@ -461,14 +465,18 @@ range_step(int argc, VALUE *argv, VALUE range)
tmp = rb_check_string_type(b);
if (!NIL_P(tmp)) {
- VALUE args[2], iter[2];
+ VALUE iter[2];
b = tmp;
- args[0] = e;
- args[1] = EXCL(range) ? Qtrue : Qfalse;
iter[0] = INT2FIX(1);
iter[1] = step;
- rb_block_call(b, rb_intern("upto"), 2, args, step_i, (VALUE)iter);
+
+ 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 {
VALUE args[2];
@@ -485,6 +493,12 @@ range_step(int argc, VALUE *argv, VALUE range)
return range;
}
+static VALUE
+range_percent_step(VALUE range, VALUE step)
+{
+ return range_step(1, &step, range);
+}
+
#if SIZEOF_DOUBLE == 8 && defined(HAVE_INT64_T)
union int64_double {
int64_t i;
@@ -517,10 +531,72 @@ double_as_int64(double d)
static int
is_integer_p(VALUE v)
{
- VALUE is_int = rb_check_funcall(v, id_integer_p, 0, 0);
+ ID id_integer_p;
+ 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;
}
+static VALUE
+bsearch_integer_range(VALUE beg, VALUE end, int excl)
+{
+ VALUE satisfied = Qnil;
+ int smaller;
+
+#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 (v == Qfalse || v == Qnil) { \
+ 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);
+ VALUE high = rb_to_int(end);
+ VALUE mid, org_high;
+ ID id_div;
+ CONST_ID(id_div, "div");
+
+ if (excl) high = rb_funcall(high, '-', 1, INT2FIX(1));
+ 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));
+ }
+ }
+ if (rb_equal(low, org_high)) {
+ BSEARCH_CHECK(low);
+ if (!smaller) return Qnil;
+ }
+ return satisfied;
+}
+
/*
* call-seq:
* rng.bsearch {|obj| block } -> value
@@ -593,33 +669,6 @@ range_bsearch(VALUE range)
* (-1...0.0).bsearch to yield -0.0.
*/
-#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 (v == Qfalse || v == Qnil) { \
- 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)
-
#define BSEARCH(conv) \
do { \
RETURN_ENUMERATOR(range, 0, 0); \
@@ -656,34 +705,26 @@ range_bsearch(VALUE range)
#if SIZEOF_DOUBLE == 8 && defined(HAVE_INT64_T)
else if (RB_TYPE_P(beg, T_FLOAT) || RB_TYPE_P(end, T_FLOAT)) {
int64_t low = double_as_int64(RFLOAT_VALUE(rb_Float(beg)));
- int64_t high = double_as_int64(RFLOAT_VALUE(rb_Float(end)));
+ 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)) {
- VALUE low = rb_to_int(beg);
- VALUE high = rb_to_int(end);
- VALUE mid, org_high;
RETURN_ENUMERATOR(range, 0, 0);
- if (EXCL(range)) high = rb_funcall(high, '-', 1, INT2FIX(1));
- 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));
+ 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) {
- high = mid;
- }
- else {
- low = rb_funcall(mid, '+', 1, INT2FIX(1));
+ return bsearch_integer_range(beg, mid, 0);
}
+ diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
}
- if (rb_equal(low, org_high)) {
- BSEARCH_CHECK(low);
- if (!smaller) return Qnil;
- }
- return satisfied;
}
else {
rb_raise(rb_eTypeError, "can't do binary search for %s", rb_obj_classname(beg));
@@ -691,18 +732,18 @@ range_bsearch(VALUE range)
return range;
}
-static VALUE
-each_i(RB_BLOCK_CALL_FUNC_ARGLIST(v, arg))
+static int
+each_i(VALUE v, VALUE arg)
{
rb_yield(v);
- return Qnil;
+ return 0;
}
-static VALUE
-sym_each_i(RB_BLOCK_CALL_FUNC_ARGLIST(v, arg))
+static int
+sym_each_i(VALUE v, VALUE arg)
{
rb_yield(rb_str_intern(v));
- return Qnil;
+ return 0;
}
/*
@@ -721,12 +762,38 @@ static VALUE
range_size(VALUE range)
{
VALUE b = RANGE_BEG(range), e = RANGE_END(range);
- 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, INT2FIX(1), EXCL(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));
+ }
+ if (NIL_P(e)) {
+ return DBL2NUM(HUGE_VAL);
+ }
}
+
return Qnil;
}
+/*
+ * call-seq:
+ * rng.to_a -> array
+ * rng.entries -> array
+ *
+ * Returns an array containing the items in the range.
+ *
+ * (1..7).to_a #=> [1, 2, 3, 4, 5, 6, 7]
+ * (1..).to_a #=> RangeError: cannot convert endless range to an array
+ */
+
+static VALUE
+range_to_a(VALUE range)
+{
+ if (NIL_P(RANGE_END(range))) {
+ rb_raise(rb_eRangeError, "cannot convert endless range to an array");
+ }
+ return rb_call_super(0, 0);
+}
+
static VALUE
range_enum_size(VALUE range, VALUE args, VALUE eobj)
{
@@ -758,45 +825,105 @@ static VALUE
range_each(VALUE range)
{
VALUE beg, end;
+ long i, lim;
RETURN_SIZED_ENUMERATOR(range, 0, 0, range_enum_size);
beg = RANGE_BEG(range);
end = RANGE_END(range);
- if (FIXNUM_P(beg) && FIXNUM_P(end)) { /* fixnums are special */
- long lim = FIX2LONG(end);
- long i;
-
+ if (FIXNUM_P(beg) && NIL_P(end)) {
+ fixnum_endless:
+ i = FIX2LONG(beg);
+ while (FIXABLE(i)) {
+ rb_yield(LONG2FIX(i++));
+ }
+ beg = LONG2NUM(i);
+ bignum_endless:
+ for (;; beg = rb_big_plus(beg, INT2FIX(1)))
+ rb_yield(beg);
+ }
+ else if (FIXNUM_P(beg) && FIXNUM_P(end)) { /* fixnums are special */
+ fixnum_loop:
+ lim = FIX2LONG(end);
if (!EXCL(range))
lim += 1;
for (i = FIX2LONG(beg); i < lim; i++) {
rb_yield(LONG2FIX(i));
}
}
- else if (SYMBOL_P(beg) && SYMBOL_P(end)) { /* symbols are special */
- VALUE args[2];
-
- args[0] = rb_sym2str(end);
- args[1] = EXCL(range) ? Qtrue : Qfalse;
- rb_block_call(rb_sym2str(beg), rb_intern("upto"), 2, args, sym_each_i, 0);
+ 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 (NIL_P(end)) goto fixnum_endless;
+ if (FIXNUM_P(end)) goto fixnum_loop;
+ }
+ else {
+ if (NIL_P(end)) goto bignum_endless;
+ 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);
+ }
}
else {
VALUE tmp = rb_check_string_type(beg);
if (!NIL_P(tmp)) {
- VALUE args[2];
-
- args[0] = end;
- args[1] = EXCL(range) ? Qtrue : Qfalse;
- rb_block_call(tmp, rb_intern("upto"), 2, args, each_i, 0);
+ 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));
}
- range_each_func(range, each_i, 0);
+ 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;
@@ -899,6 +1026,9 @@ range_first(int argc, VALUE *argv, VALUE range)
static VALUE
range_last(int argc, VALUE *argv, VALUE range)
{
+ if (NIL_P(RANGE_END(range))) {
+ rb_raise(rb_eRangeError, "cannot get the last element of endless range");
+ }
if (argc == 0) return RANGE_END(range);
return rb_ary_last(argc, argv, rb_Array(range));
}
@@ -926,15 +1056,19 @@ static VALUE
range_min(int argc, VALUE *argv, VALUE 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);
}
else if (argc != 0) {
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 = rb_cmpint(rb_funcall(b, id_cmp, 1, e), b, e);
+ int c = NIL_P(e) ? -1 : OPTIMIZED_CMP(b, e, cmp_opt);
if (c > 0 || (c == 0 && EXCL(range)))
return Qnil;
@@ -965,29 +1099,34 @@ range_max(int argc, VALUE *argv, VALUE range)
VALUE e = RANGE_END(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");
+ }
+
if (rb_block_given_p() || (EXCL(range) && !nm) || argc) {
- return rb_call_super(argc, argv);
+ return rb_call_super(argc, argv);
}
else {
- VALUE b = RANGE_BEG(range);
- int c = rb_cmpint(rb_funcall(b, id_cmp, 1, e), b, e);
-
- if (c > 0)
- return Qnil;
- if (EXCL(range)) {
- if (!FIXNUM_P(e) && !rb_obj_is_kind_of(e, rb_cInteger)) {
- rb_raise(rb_eTypeError, "cannot exclude non Integer end value");
- }
- if (c == 0) return Qnil;
- if (!FIXNUM_P(b) && !rb_obj_is_kind_of(b,rb_cInteger)) {
- rb_raise(rb_eTypeError, "cannot exclude end value with non Integer begin value");
- }
- if (FIXNUM_P(e)) {
- return LONG2NUM(FIX2LONG(e) - 1);
- }
- return rb_funcall(e, '-', 1, INT2FIX(1));
- }
- return e;
+ struct cmp_opt_data cmp_opt = { 0, 0 };
+ VALUE b = RANGE_BEG(range);
+ int c = OPTIMIZED_CMP(b, e, cmp_opt);
+
+ if (c > 0)
+ return Qnil;
+ if (EXCL(range)) {
+ if (!RB_INTEGER_TYPE_P(e)) {
+ rb_raise(rb_eTypeError, "cannot exclude non Integer end value");
+ }
+ if (c == 0) return Qnil;
+ if (!RB_INTEGER_TYPE_P(b)) {
+ rb_raise(rb_eTypeError, "cannot exclude end value with non Integer begin value");
+ }
+ if (FIXNUM_P(e)) {
+ return LONG2NUM(FIX2LONG(e) - 1);
+ }
+ return rb_funcall(e, '-', 1, INT2FIX(1));
+ }
+ return e;
}
}
@@ -1002,12 +1141,18 @@ rb_range_values(VALUE range, VALUE *begp, VALUE *endp, int *exclp)
e = RANGE_END(range);
excl = EXCL(range);
}
+ else if (RTEST(rb_obj_is_kind_of(range, rb_cArithSeq))) {
+ return (int)Qfalse;
+ }
else {
- if (!rb_respond_to(range, id_beg)) return (int)Qfalse;
- if (!rb_respond_to(range, id_end)) return (int)Qfalse;
- b = rb_funcall(range, id_beg, 0);
- e = rb_funcall(range, id_end, 0);
- excl = RTEST(rb_funcall(range, rb_intern("exclude_end?"), 0));
+ 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);
}
*begp = b;
*endp = e;
@@ -1025,7 +1170,8 @@ rb_range_beg_len(VALUE range, long *begp, long *lenp, long len, int err)
if (!rb_range_values(range, &b, &e, &excl))
return Qfalse;
beg = NUM2LONG(b);
- end = NUM2LONG(e);
+ end = NIL_P(e) ? -1 : NUM2LONG(e);
+ if (NIL_P(e)) excl = 0;
origbeg = beg;
origend = end;
if (beg < 0) {
@@ -1085,16 +1231,16 @@ range_to_s(VALUE range)
static VALUE
inspect_range(VALUE range, VALUE dummy, int recur)
{
- VALUE str, str2;
+ VALUE str, str2 = Qundef;
if (recur) {
return rb_str_new2(EXCL(range) ? "(... ... ...)" : "(... .. ...)");
}
str = rb_inspect(RANGE_BEG(range));
- str2 = rb_inspect(RANGE_END(range));
+ if (!NIL_P(RANGE_END(range))) str2 = rb_inspect(RANGE_END(range));
str = rb_str_dup(str);
rb_str_cat(str, "...", EXCL(range) ? 3 : 2);
- rb_str_append(str, str2);
+ if (str2 != Qundef) rb_str_append(str, str2);
OBJ_INFECT(str, range);
return str;
@@ -1116,6 +1262,8 @@ range_inspect(VALUE range)
return rb_exec_recursive(inspect_range, range, 0);
}
+static VALUE range_include_internal(VALUE range, VALUE val);
+
/*
* call-seq:
* rng === obj -> true or false
@@ -1138,7 +1286,9 @@ range_inspect(VALUE range)
static VALUE
range_eqq(VALUE range, VALUE val)
{
- return rb_funcall(range, rb_intern("include?"), 1, val);
+ VALUE ret = range_include_internal(range, val);
+ if (ret != Qundef) return ret;
+ return r_cover_p(range, RANGE_BEG(range), RANGE_END(range), val);
}
@@ -1159,6 +1309,14 @@ range_eqq(VALUE range, VALUE val)
static VALUE
range_include(VALUE range, VALUE val)
{
+ VALUE ret = range_include_internal(range, val);
+ if (ret != Qundef) return ret;
+ return rb_call_super(1, &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) ||
@@ -1169,18 +1327,27 @@ range_include(VALUE range, VALUE val)
!NIL_P(rb_check_to_integer(end, "to_int"))) {
return r_cover_p(range, beg, end, val);
}
- else if (RB_TYPE_P(beg, T_STRING) && RB_TYPE_P(end, T_STRING)) {
- 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 (RB_TYPE_P(beg, T_STRING)) {
+ if (RB_TYPE_P(end, T_STRING)) {
+ 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 (NIL_P(end)) {
+ VALUE r = rb_funcall(beg, id_cmp, 1, val);
+ if (NIL_P(r)) return Qfalse;
+ if (rb_cmpint(r, beg, val) <= 0) return Qtrue;
+ return Qfalse;
+ }
}
- /* TODO: ruby_frame->this_func = rb_intern("include?"); */
- return rb_call_super(1, &val);
+ return Qundef;
}
+static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
/*
* call-seq:
- * rng.cover?(obj) -> true or false
+ * rng.cover?(obj) -> true or false
+ * rng.cover?(range) -> true or false
*
* Returns <code>true</code> if +obj+ is between the begin and end of
* the range.
@@ -1188,9 +1355,22 @@ range_include(VALUE range, VALUE val)
* This tests <code>begin <= obj <= end</code> when #exclude_end? is +false+
* and <code>begin <= obj < end</code> when #exclude_end? is +true+.
*
- * ("a".."z").cover?("c") #=> true
- * ("a".."z").cover?("5") #=> false
- * ("a".."z").cover?("cc") #=> true
+ * If called with a Range argument, returns <code>true</code> when the
+ * given range is covered by the receiver,
+ * by comparing the begin and end values. If the argument can be treated as
+ * a sequence, this method treats it that way. In the specific case of
+ * <code>(a..b).cover?(c...d)</code> with <code>a <= c && b < d</code>,
+ * the end of the sequence must be calculated, which may exhibit poor
+ * performance if <code>c</code> is non-numeric.
+ * Returns <code>false</code> if the begin value of the
+ * range is larger than the end value.
+ *
+ * ("a".."z").cover?("c") #=> true
+ * ("a".."z").cover?("5") #=> false
+ * ("a".."z").cover?("cc") #=> true
+ * (1..5).cover?(2..3) #=> true
+ * (1..5).cover?(0..6) #=> false
+ * (1..5).cover?(1...6) #=> true
*/
static VALUE
@@ -1200,15 +1380,54 @@ range_cover(VALUE range, VALUE val)
beg = RANGE_BEG(range);
end = RANGE_END(range);
+
+ if (rb_obj_is_kind_of(val, rb_cRange)) {
+ return RBOOL(r_cover_range_p(range, beg, end, val));
+ }
return r_cover_p(range, beg, end, val);
}
static VALUE
+r_call_max(VALUE r)
+{
+ return rb_funcallv(r, rb_intern("max"), 0, 0);
+}
+
+static int
+r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val)
+{
+ VALUE val_beg, val_end, val_max;
+ int cmp_end;
+
+ val_beg = RANGE_BEG(val);
+ val_end = RANGE_END(val);
+
+ if (!NIL_P(end) && NIL_P(val_end)) return FALSE;
+ if (!NIL_P(val_end) && r_less(val_beg, val_end) > -EXCL(val)) return FALSE;
+ if (!r_cover_p(range, beg, end, val_beg)) return FALSE;
+
+ cmp_end = r_less(end, val_end);
+
+ if (EXCL(range) == EXCL(val)) {
+ return cmp_end >= 0;
+ } else if (EXCL(range)) {
+ return cmp_end > 0;
+ } else if (cmp_end >= 0) {
+ return TRUE;
+ }
+
+ val_max = rb_rescue2(r_call_max, val, NULL, Qnil, rb_eTypeError, (VALUE)0);
+ if (val_max == Qnil) return FALSE;
+
+ return r_less(end, val_max) >= 0;
+}
+
+static VALUE
r_cover_p(VALUE range, VALUE beg, VALUE end, VALUE val)
{
if (r_less(beg, val) <= 0) {
int excl = EXCL(range);
- if (r_less(val, end) <= -excl)
+ if (NIL_P(end) || r_less(val, end) <= -excl)
return Qtrue;
}
return Qfalse;
@@ -1269,6 +1488,34 @@ range_alloc(VALUE klass)
* ('a'..'e').to_a #=> ["a", "b", "c", "d", "e"]
* ('a'...'e').to_a #=> ["a", "b", "c", "d"]
*
+ * == Endless Ranges
+ *
+ * An "endless range" represents a semi-infinite range.
+ * Literal notation for an endless range is:
+ *
+ * (1..)
+ * # or similarly
+ * (1...)
+ *
+ * Which is equivalent to
+ *
+ * (1..nil) # or similarly (1...nil)
+ * Range.new(1, nil) # or Range.new(1, nil, true)
+ *
+ * Endless ranges are useful, for example, for idiomatic slicing of
+ * arrays:
+ *
+ * [1, 2, 3, 4, 5][2...] # => [3, 4, 5]
+ *
+ * Some implementation details:
+ *
+ * * +end+ of endless range is +nil+;
+ * * +each+ of endless range enumerates infinite sequence (may be
+ * useful in combination with Enumerable#take_while or similar
+ * methods);
+ * * <code>(1..)</code> and <code>(1...)</code> are not equal,
+ * although technically representing the same sequence.
+ *
* == Custom Objects in Ranges
*
* Ranges can be constructed using any objects that can be compared
@@ -1321,8 +1568,6 @@ Init_Range(void)
id_beg = rb_intern("begin");
id_end = rb_intern("end");
id_excl = rb_intern("excl");
- id_integer_p = rb_intern("integer?");
- id_div = rb_intern("div");
rb_cRange = rb_struct_define_without_accessor(
"Range", rb_cObject, range_alloc,
@@ -1338,6 +1583,7 @@ Init_Range(void)
rb_define_method(rb_cRange, "hash", range_hash, 0);
rb_define_method(rb_cRange, "each", range_each, 0);
rb_define_method(rb_cRange, "step", range_step, -1);
+ rb_define_method(rb_cRange, "%", range_percent_step, 1);
rb_define_method(rb_cRange, "bsearch", range_bsearch, 0);
rb_define_method(rb_cRange, "begin", range_begin, 0);
rb_define_method(rb_cRange, "end", range_end, 0);
@@ -1346,6 +1592,8 @@ Init_Range(void)
rb_define_method(rb_cRange, "min", range_min, -1);
rb_define_method(rb_cRange, "max", range_max, -1);
rb_define_method(rb_cRange, "size", range_size, 0);
+ rb_define_method(rb_cRange, "to_a", range_to_a, 0);
+ rb_define_method(rb_cRange, "entries", range_to_a, 0);
rb_define_method(rb_cRange, "to_s", range_to_s, 0);
rb_define_method(rb_cRange, "inspect", range_inspect, 0);
diff --git a/rational.c b/rational.c
index f8adafe1e0..5058ce1bfa 100644
--- a/rational.c
+++ b/rational.c
@@ -6,6 +6,7 @@
*/
#include "internal.h"
+#include "id.h"
#include <math.h>
#include <float.h>
@@ -28,23 +29,25 @@
#define GMP_GCD_DIGITS 1
#define INT_POSITIVE_P(x) (FIXNUM_P(x) ? FIXNUM_POSITIVE_P(x) : BIGNUM_POSITIVE_P(x))
-#define INT_NEGATIVE_P(x) (FIXNUM_P(x) ? FIXNUM_NEGATIVE_P(x) : BIGNUM_NEGATIVE_P(x))
#define INT_ZERO_P(x) (FIXNUM_P(x) ? FIXNUM_ZERO_P(x) : rb_bigzero_p(x))
VALUE rb_cRational;
-static ID id_abs, id_idiv, id_integer_p, id_negate, id_to_i,
+static ID id_abs, id_idiv, id_integer_p,
id_i_num, id_i_den;
+#define id_to_i idTo_i
#define f_boolcast(x) ((x) ? Qtrue : Qfalse)
#define f_inspect rb_inspect
#define f_to_s rb_obj_as_string
+static VALUE nurat_to_f(VALUE self);
+
#define binop(n,op) \
inline static VALUE \
f_##n(VALUE x, VALUE y)\
{\
- return rb_funcall(x, (op), 1, y);\
+ return rb_funcall(x, (op), 1, y); \
}
#define fun1(n) \
@@ -54,19 +57,12 @@ f_##n(VALUE x)\
return rb_funcall(x, id_##n, 0);\
}
-#define fun2(n) \
-inline static VALUE \
-f_##n(VALUE x, VALUE y)\
-{\
- return rb_funcall(x, id_##n, 1, y);\
-}
-
inline static VALUE
f_add(VALUE x, VALUE y)
{
- if (FIXNUM_P(y) && FIXNUM_ZERO_P(y))
+ if (FIXNUM_ZERO_P(y))
return x;
- else if (FIXNUM_P(x) && FIXNUM_ZERO_P(x))
+ if (FIXNUM_ZERO_P(x))
return y;
return rb_funcall(x, '+', 1, y);
}
@@ -74,7 +70,7 @@ f_add(VALUE x, VALUE y)
inline static VALUE
f_div(VALUE x, VALUE y)
{
- if (FIXNUM_P(y) && FIX2LONG(y) == 1)
+ if (y == ONE)
return x;
if (RB_INTEGER_TYPE_P(x))
return rb_int_div(x, y);
@@ -97,26 +93,13 @@ binop(mod, '%')
inline static VALUE
f_mul(VALUE x, VALUE y)
{
- if (FIXNUM_P(y)) {
- long iy = FIX2LONG(y);
- if (iy == 0) {
- if (RB_INTEGER_TYPE_P(x))
- return ZERO;
- }
- else if (iy == 1)
- return x;
- }
- else if (FIXNUM_P(x)) {
- long ix = FIX2LONG(x);
- if (ix == 0) {
- if (RB_INTEGER_TYPE_P(y))
- return ZERO;
- }
- else if (ix == 1)
- return y;
- return rb_int_mul(x, y);
- }
- else if (RB_TYPE_P(x, T_BIGNUM))
+ if (FIXNUM_ZERO_P(y) && RB_INTEGER_TYPE_P(x))
+ return ZERO;
+ if (y == ONE) return x;
+ if (FIXNUM_ZERO_P(x) && RB_INTEGER_TYPE_P(y))
+ return ZERO;
+ if (x == ONE) return y;
+ else if (RB_INTEGER_TYPE_P(x))
return rb_int_mul(x, y);
return rb_funcall(x, '*', 1, y);
}
@@ -138,7 +121,6 @@ f_abs(VALUE x)
}
fun1(integer_p)
-fun1(negate)
inline static VALUE
f_to_i(VALUE x)
@@ -156,7 +138,13 @@ f_eqeq_p(VALUE x, VALUE y)
return (int)rb_equal(x, y);
}
-fun2(idiv)
+inline static VALUE
+f_idiv(VALUE x, VALUE y)
+{
+ if (RB_INTEGER_TYPE_P(x))
+ return rb_int_idiv(x, y);
+ return rb_funcall(x, id_idiv, 1, y);
+}
#define f_expt10(x) rb_int_pow(INT2FIX(10), x)
@@ -212,8 +200,7 @@ f_minus_one_p(VALUE x)
inline static int
f_kind_of_p(VALUE x, VALUE c)
{
- VALUE ret = rb_obj_is_kind_of(x, c);
- return RTEST(ret);
+ return (int)rb_obj_is_kind_of(x, c);
}
inline static int
@@ -264,10 +251,15 @@ rb_gcd_gmp(VALUE x, VALUE y)
mpz_gcd(mz, mx, my);
+ mpz_clear(mx);
+ mpz_clear(my);
+
zn = (mpz_sizeinbase(mz, 16) + SIZEOF_BDIGIT*2 - 1) / (SIZEOF_BDIGIT*2);
z = rb_big_new(zn, 1);
mpz_export(BIGNUM_DIGITS(z), &count, -1, sizeof(BDIGIT), 0, nails, mz);
+ mpz_clear(mz);
+
return rb_big_norm(z);
}
#endif
@@ -279,6 +271,9 @@ rb_gcd_gmp(VALUE x, VALUE y)
inline static long
i_gcd(long x, long y)
{
+ unsigned long u, v, t;
+ int shift;
+
if (x < 0)
x = -x;
if (y < 0)
@@ -289,12 +284,29 @@ i_gcd(long x, long y)
if (y == 0)
return x;
- while (x > 0) {
- long t = x;
- x = y % x;
- y = t;
+ u = (unsigned long)x;
+ v = (unsigned long)y;
+ for (shift = 0; ((u | v) & 1) == 0; ++shift) {
+ u >>= 1;
+ v >>= 1;
}
- return y;
+
+ while ((u & 1) == 0)
+ u >>= 1;
+
+ do {
+ 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);
}
inline static VALUE
@@ -378,9 +390,6 @@ f_lcm(VALUE x, VALUE y)
#define get_dat2(x,y) \
struct RRational *adat = RRATIONAL(x), *bdat = RRATIONAL(y)
-#define RRATIONAL_SET_NUM(rat, n) RB_OBJ_WRITE((rat), &((struct RRational *)(rat))->num,(n))
-#define RRATIONAL_SET_DEN(rat, d) RB_OBJ_WRITE((rat), &((struct RRational *)(rat))->den,(d))
-
inline static VALUE
nurat_s_new_internal(VALUE klass, VALUE num, VALUE den)
{
@@ -388,6 +397,7 @@ nurat_s_new_internal(VALUE klass, VALUE num, VALUE den)
RRATIONAL_SET_NUM(obj, num);
RRATIONAL_SET_DEN(obj, den);
+ OBJ_FREEZE_RAW(obj);
return (VALUE)obj;
}
@@ -398,38 +408,6 @@ nurat_s_alloc(VALUE klass)
return nurat_s_new_internal(klass, ZERO, ONE);
}
-#if 0
-static VALUE
-nurat_s_new_bang(int argc, VALUE *argv, VALUE klass)
-{
- VALUE num, den;
-
- switch (rb_scan_args(argc, argv, "11", &num, &den)) {
- case 1:
- if (!k_integer_p(num))
- num = f_to_i(num);
- den = ONE;
- break;
- default:
- if (!k_integer_p(num))
- num = f_to_i(num);
- if (!k_integer_p(den))
- den = f_to_i(den);
-
- if (INT_NEGATIVE_P(den)) {
- num = f_negate(num);
- den = f_negate(den);
- }
- else if (INT_ZERO_P(den)) {
- rb_num_zerodiv();
- }
- break;
- }
-
- return nurat_s_new_internal(klass, num, den);
-}
-#endif
-
inline static VALUE
f_rational_new_bang1(VALUE klass, VALUE x)
{
@@ -437,10 +415,6 @@ f_rational_new_bang1(VALUE klass, VALUE x)
}
#ifdef CANONICALIZATION_FOR_MATHN
-#define CANON
-#endif
-
-#ifdef CANON
static int canonicalization = 0;
RUBY_FUNC_EXPORTED void
@@ -448,6 +422,8 @@ nurat_canonicalization(int f)
{
canonicalization = f;
}
+#else
+# define canonicalization 0
#endif
inline static void
@@ -471,29 +447,35 @@ nurat_int_value(VALUE num)
static void
nurat_canonicalize(VALUE *num, VALUE *den)
{
+ assert(num); assert(RB_INTEGER_TYPE_P(*num));
+ assert(den); assert(RB_INTEGER_TYPE_P(*den));
if (INT_NEGATIVE_P(*den)) {
- *num = f_negate(*num);
- *den = f_negate(*den);
+ *num = rb_int_uminus(*num);
+ *den = rb_int_uminus(*den);
}
else if (INT_ZERO_P(*den)) {
rb_num_zerodiv();
}
}
-inline static VALUE
-nurat_s_canonicalize_internal(VALUE klass, VALUE num, VALUE den)
+static void
+nurat_reduce(VALUE *x, VALUE *y)
{
VALUE gcd;
+ if (*x == ONE || *y == ONE) return;
+ gcd = f_gcd(*x, *y);
+ *x = f_idiv(*x, gcd);
+ *y = f_idiv(*y, gcd);
+}
+inline static VALUE
+nurat_s_canonicalize_internal(VALUE klass, VALUE num, VALUE den)
+{
nurat_canonicalize(&num, &den);
- gcd = f_gcd(num, den);
- num = f_idiv(num, gcd);
- den = f_idiv(den, gcd);
+ nurat_reduce(&num, &den);
-#ifdef CANON
- if (f_one_p(den) && canonicalization)
+ if (canonicalization && f_one_p(den))
return num;
-#endif
return nurat_s_new_internal(klass, num, den);
}
@@ -502,10 +484,8 @@ nurat_s_canonicalize_internal_no_reduce(VALUE klass, VALUE num, VALUE den)
{
nurat_canonicalize(&num, &den);
-#ifdef CANON
- if (f_one_p(den) && canonicalization)
+ if (canonicalization && f_one_p(den))
return num;
-#endif
return nurat_s_new_internal(klass, num, den);
}
@@ -544,19 +524,31 @@ f_rational_new_no_reduce2(VALUE klass, VALUE x, VALUE y)
return nurat_s_canonicalize_internal_no_reduce(klass, x, y);
}
+static VALUE nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise);
static VALUE nurat_s_convert(int argc, VALUE *argv, VALUE klass);
+
/*
* call-seq:
- * Rational(x[, y]) -> numeric
+ * Rational(x, y, exception: true) -> rational
+ * Rational(arg, exception: true) -> rational
*
- * Returns x/y;
+ * Returns +x/y+ or +arg+ as a Rational.
*
- * Rational(1, 2) #=> (1/2)
- * Rational('1/2') #=> (1/2)
- * Rational(nil) #=> TypeError
- * Rational(1, nil) #=> TypeError
+ * Rational(2, 3) #=> (2/3)
+ * Rational(5) #=> (5/1)
+ * Rational(0.5) #=> (1/2)
+ * Rational(0.3) #=> (5404319552844595/18014398509481984)
+ *
+ * Rational("2/3") #=> (2/3)
+ * Rational("0.3") #=> (3/10)
+ *
+ * Rational("10 cents") #=> ArgumentError
+ * Rational(nil) #=> TypeError
+ * Rational(1, nil) #=> TypeError
*
- * Syntax of string form:
+ * Rational("10 cents", exception: false) #=> nil
+ *
+ * Syntax of the string form:
*
* string form = extra spaces , rational , extra spaces ;
* rational = [ sign ] , unsigned rational ;
@@ -570,12 +562,27 @@ static VALUE nurat_s_convert(int argc, VALUE *argv, VALUE klass);
* digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
* extra spaces = ? \s* ? ;
*
- * See String#to_r.
+ * See also String#to_r.
*/
static VALUE
nurat_f_rational(int argc, VALUE *argv, VALUE klass)
{
- return nurat_s_convert(argc, argv, rb_cRational);
+ VALUE a1, a2, opts = Qnil;
+ int raise = TRUE;
+
+ if (rb_scan_args(argc, argv, "11:", &a1, &a2, &opts) == 1) {
+ a2 = Qundef;
+ }
+ if (!NIL_P(opts)) {
+ static ID kwds[1];
+ VALUE exception;
+ if (!kwds[0]) {
+ kwds[0] = idException;
+ }
+ rb_get_kwargs(opts, kwds, 0, 1, &exception);
+ raise = (exception != Qfalse);
+ }
+ return nurat_convert(rb_cRational, a1, a2, raise);
}
/*
@@ -606,7 +613,6 @@ nurat_numerator(VALUE self)
* Rational(7, 1).denominator #=> 1
* Rational(9, -4).denominator #=> 4
* Rational(-2, -10).denominator #=> 5
- * rat.numerator.gcd(rat.denominator) #=> 1
*/
static VALUE
nurat_denominator(VALUE self)
@@ -621,10 +627,12 @@ nurat_denominator(VALUE self)
*
* Negates +rat+.
*/
-static VALUE
-nurat_negate(VALUE self)
+VALUE
+rb_rational_uminus(VALUE self)
{
+ const int unused = (assert(RB_TYPE_P(self, T_RATIONAL)), 0);
get_dat1(self);
+ (void)unused;
return f_rational_new2(CLASS_OF(self), rb_int_uminus(dat->num), dat->den);
}
@@ -692,7 +700,8 @@ f_addsub(VALUE self, VALUE anum, VALUE aden, VALUE bnum, VALUE bden, int k)
a = rb_int_idiv(bden, g);
den = rb_int_mul(a, b);
}
- else {
+ 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));
@@ -709,6 +718,12 @@ f_addsub(VALUE self, VALUE anum, VALUE aden, VALUE bnum, VALUE bden, int k)
a = rb_int_idiv(bden, g);
den = rb_int_mul(a, b);
}
+ else {
+ double a = NUM2DBL(anum) / NUM2DBL(aden);
+ double b = NUM2DBL(bnum) / NUM2DBL(bden);
+ double c = k == '+' ? a + b : a - b;
+ return DBL2NUM(c);
+ }
return f_rational_new_no_reduce2(CLASS_OF(self), num, den);
}
@@ -720,7 +735,7 @@ static double nurat_to_double(VALUE self);
* Performs addition.
*
* Rational(2, 3) + Rational(2, 3) #=> (4/3)
- * Rational(900) + Rational(1) #=> (900/1)
+ * Rational(900) + Rational(1) #=> (901/1)
* Rational(-2, 9) + Rational(-9, 2) #=> (-85/18)
* Rational(9, 8) + 4 #=> (41/8)
* Rational(20, 9) + 9.8 #=> 12.022222222222222
@@ -763,11 +778,11 @@ rb_rational_plus(VALUE self, VALUE other)
* Rational(2, 3) - Rational(2, 3) #=> (0/1)
* Rational(900) - Rational(1) #=> (899/1)
* Rational(-2, 9) - Rational(-9, 2) #=> (77/18)
- * Rational(9, 8) - 4 #=> (23/8)
+ * Rational(9, 8) - 4 #=> (-23/8)
* Rational(20, 9) - 9.8 #=> -7.577777777777778
*/
-static VALUE
-nurat_sub(VALUE self, VALUE other)
+VALUE
+rb_rational_minus(VALUE self, VALUE other)
{
if (RB_INTEGER_TYPE_P(other)) {
{
@@ -801,6 +816,16 @@ f_muldiv(VALUE self, VALUE anum, VALUE aden, VALUE bnum, VALUE bden, int k)
VALUE num, den;
assert(RB_TYPE_P(self, T_RATIONAL));
+
+ /* Integer#** can return Rational with Float right now */
+ if (RB_FLOAT_TYPE_P(anum) || RB_FLOAT_TYPE_P(aden) ||
+ RB_FLOAT_TYPE_P(bnum) || RB_FLOAT_TYPE_P(bden)) {
+ double an = NUM2DBL(anum), ad = NUM2DBL(aden);
+ double bn = NUM2DBL(bnum), bd = NUM2DBL(bden);
+ double x = (an * bn) / (ad * bd);
+ return DBL2NUM(x);
+ }
+
assert(RB_INTEGER_TYPE_P(anum));
assert(RB_INTEGER_TYPE_P(aden));
assert(RB_INTEGER_TYPE_P(bnum));
@@ -852,8 +877,8 @@ f_muldiv(VALUE self, VALUE anum, VALUE aden, VALUE bnum, VALUE bden, int k)
* Rational(9, 8) * 4 #=> (9/2)
* Rational(20, 9) * 9.8 #=> 21.77777777777778
*/
-static VALUE
-nurat_mul(VALUE self, VALUE other)
+VALUE
+rb_rational_mul(VALUE self, VALUE other)
{
if (RB_INTEGER_TYPE_P(other)) {
{
@@ -894,8 +919,8 @@ nurat_mul(VALUE self, VALUE other)
* Rational(9, 8) / 4 #=> (9/32)
* Rational(20, 9) / 9.8 #=> 0.22675736961451246
*/
-static VALUE
-nurat_div(VALUE self, VALUE other)
+VALUE
+rb_rational_div(VALUE self, VALUE other)
{
if (RB_INTEGER_TYPE_P(other)) {
if (f_zero_p(other))
@@ -908,8 +933,10 @@ nurat_div(VALUE self, VALUE other)
other, ONE, '/');
}
}
- else if (RB_FLOAT_TYPE_P(other))
- return DBL2NUM(nurat_to_double(self) / RFLOAT_VALUE(other));
+ 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))
rb_num_zerodiv();
@@ -930,13 +957,11 @@ nurat_div(VALUE self, VALUE other)
}
}
-static VALUE nurat_to_f(VALUE self);
-
/*
* call-seq:
* rat.fdiv(numeric) -> float
*
- * Performs division and returns the value as a float.
+ * Performs division and returns the value as a Float.
*
* Rational(2, 3).fdiv(1) #=> 0.6666666666666666
* Rational(2, 3).fdiv(0.5) #=> 1.3333333333333333
@@ -947,15 +972,15 @@ nurat_fdiv(VALUE self, VALUE other)
{
VALUE div;
if (f_zero_p(other))
- return DBL2NUM(nurat_to_double(self) / 0.0);
+ return rb_rational_div(self, rb_float_new(0.0));
if (FIXNUM_P(other) && other == LONG2FIX(1))
return nurat_to_f(self);
- div = nurat_div(self, other);
+ div = rb_rational_div(self, other);
if (RB_TYPE_P(div, T_RATIONAL))
return nurat_to_f(div);
if (RB_FLOAT_TYPE_P(div))
return div;
- return rb_funcall(div, rb_intern("to_f"), 0);
+ return rb_funcall(div, idTo_f, 0);
}
inline static VALUE
@@ -973,15 +998,15 @@ f_odd_p(VALUE integer)
*
* Performs exponentiation.
*
- * Rational(2) ** Rational(3) #=> (8/1)
- * Rational(10) ** -2 #=> (1/100)
- * Rational(10) ** -2.0 #=> 0.01
- * Rational(-4) ** Rational(1,2) #=> (1.2246063538223773e-16+2.0i)
- * Rational(1, 2) ** 0 #=> (1/1)
- * Rational(1, 2) ** 0.0 #=> 1.0
+ * Rational(2) ** Rational(3) #=> (8/1)
+ * Rational(10) ** -2 #=> (1/100)
+ * Rational(10) ** -2.0 #=> 0.01
+ * Rational(-4) ** Rational(1, 2) #=> (0.0+2.0i)
+ * Rational(1, 2) ** 0 #=> (1/1)
+ * Rational(1, 2) ** 0.0 #=> 1.0
*/
-static VALUE
-nurat_expt(VALUE self, VALUE other)
+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);
@@ -1033,6 +1058,15 @@ nurat_expt(VALUE self, VALUE other)
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);
}
}
@@ -1047,23 +1081,27 @@ nurat_expt(VALUE self, VALUE other)
return rb_num_coerce_bin(self, other, rb_intern("**"));
}
}
+#define nurat_expt rb_rational_pow
/*
* call-seq:
- * rational <=> numeric -> -1, 0, +1 or nil
+ * rational <=> numeric -> -1, 0, +1, or nil
*
- * Performs comparison and returns -1, 0, or +1.
+ * Returns -1, 0, or +1 depending on whether +rational+ is
+ * less than, equal to, or greater than +numeric+.
*
* +nil+ is returned if the two values are incomparable.
*
- * Rational(2, 3) <=> Rational(2, 3) #=> 0
- * Rational(5) <=> 5 #=> 0
- * Rational(2,3) <=> Rational(1,3) #=> 1
- * Rational(1,3) <=> 1 #=> -1
- * Rational(1,3) <=> 0.3 #=> 1
+ * Rational(2, 3) <=> Rational(2, 3) #=> 0
+ * Rational(5) <=> 5 #=> 0
+ * Rational(2, 3) <=> Rational(1, 3) #=> 1
+ * Rational(1, 3) <=> 1 #=> -1
+ * Rational(1, 3) <=> 0.3 #=> 1
+ *
+ * Rational(1, 3) <=> "0.3" #=> nil
*/
-static VALUE
-nurat_cmp(VALUE self, VALUE other)
+VALUE
+rb_rational_cmp(VALUE self, VALUE other)
{
if (RB_INTEGER_TYPE_P(other)) {
{
@@ -1106,7 +1144,7 @@ nurat_cmp(VALUE self, VALUE other)
* call-seq:
* rat == object -> true or false
*
- * Returns true if rat equals object numerically.
+ * Returns +true+ if +rat+ equals +object+ numerically.
*
* Rational(2, 3) == Rational(2, 3) #=> true
* Rational(5) == 5 #=> true
@@ -1118,9 +1156,9 @@ static VALUE
nurat_eqeq_p(VALUE self, VALUE other)
{
if (RB_INTEGER_TYPE_P(other)) {
- {
- get_dat1(self);
+ 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;
@@ -1130,6 +1168,10 @@ nurat_eqeq_p(VALUE self, VALUE other)
return Qfalse;
return rb_int_equal(dat->num, other);
}
+ else {
+ const double d = nurat_to_double(self);
+ return f_boolcast(FIXNUM_ZERO_P(rb_dbl_cmp(d, NUM2DBL(other))));
+ }
}
else if (RB_FLOAT_TYPE_P(other)) {
const double d = nurat_to_double(self);
@@ -1177,42 +1219,9 @@ nurat_coerce(VALUE self, VALUE other)
return Qnil;
}
-#if 0
-/* :nodoc: */
-static VALUE
-nurat_idiv(VALUE self, VALUE other)
-{
- return f_idiv(self, other);
-}
-
-/* :nodoc: */
-static VALUE
-nurat_quot(VALUE self, VALUE other)
-{
- return f_truncate(f_div(self, other));
-}
-
-/* :nodoc: */
-static VALUE
-nurat_quotrem(VALUE self, VALUE other)
-{
- VALUE val = f_truncate(f_div(self, other));
- return rb_assoc_new(val, f_sub(self, f_mul(other, val)));
-}
-#endif
-
-#if 0
-/* :nodoc: */
-static VALUE
-nurat_true(VALUE self)
-{
- return Qtrue;
-}
-#endif
-
/*
* call-seq:
- * rat.positive? -> true or false
+ * rat.positive? -> true or false
*
* Returns +true+ if +rat+ is greater than 0.
*/
@@ -1225,7 +1234,7 @@ nurat_positive_p(VALUE self)
/*
* call-seq:
- * rat.negative? -> true or false
+ * rat.negative? -> true or false
*
* Returns +true+ if +rat+ is less than 0.
*/
@@ -1236,6 +1245,30 @@ nurat_negative_p(VALUE self)
return f_boolcast(INT_NEGATIVE_P(dat->num));
}
+/*
+ * call-seq:
+ * rat.abs -> rational
+ * rat.magnitude -> rational
+ *
+ * Returns the absolute value of +rat+.
+ *
+ * (1/2r).abs #=> (1/2)
+ * (-1/2r).abs #=> (1/2)
+ *
+ * Rational#magnitude is an alias for Rational#abs.
+ */
+
+VALUE
+rb_rational_abs(VALUE self)
+{
+ get_dat1(self);
+ if (INT_NEGATIVE_P(dat->num)) {
+ VALUE num = rb_int_abs(dat->num);
+ return nurat_s_canonicalize_internal_no_reduce(CLASS_OF(self), num, dat->den);
+ }
+ return self;
+}
+
static VALUE
nurat_floor(VALUE self)
{
@@ -1256,14 +1289,13 @@ nurat_ceil(VALUE self)
*
* Returns the truncated value as an integer.
*
- * Equivalent to
- * rat.truncate.
+ * Equivalent to Rational#truncate.
*
- * Rational(2, 3).to_i #=> 0
- * Rational(3).to_i #=> 3
- * Rational(300.6).to_i #=> 300
- * Rational(98,71).to_i #=> 1
- * Rational(-30,2).to_i #=> -15
+ * Rational(2, 3).to_i #=> 0
+ * Rational(3).to_i #=> 3
+ * Rational(300.6).to_i #=> 300
+ * Rational(98, 71).to_i #=> 1
+ * Rational(-31, 2).to_i #=> -15
*/
static VALUE
nurat_truncate(VALUE self)
@@ -1299,6 +1331,31 @@ nurat_round_half_up(VALUE self)
}
static VALUE
+nurat_round_half_down(VALUE self)
+{
+ VALUE num, den, neg;
+
+ get_dat1(self);
+
+ num = dat->num;
+ den = dat->den;
+ neg = INT_NEGATIVE_P(num);
+
+ if (neg)
+ num = rb_int_uminus(num);
+
+ num = rb_int_plus(rb_int_mul(num, TWO), den);
+ num = rb_int_minus(num, ONE);
+ den = rb_int_mul(den, TWO);
+ num = rb_int_idiv(num, den);
+
+ if (neg)
+ num = rb_int_uminus(num);
+
+ return num;
+}
+
+static VALUE
nurat_round_half_even(VALUE self)
{
VALUE num, den, neg, qr;
@@ -1330,16 +1387,16 @@ f_round_common(int argc, VALUE *argv, VALUE self, VALUE (*func)(VALUE))
{
VALUE n, b, s;
- if (argc == 0)
+ if (rb_check_arity(argc, 0, 1) == 0)
return (*func)(self);
- rb_scan_args(argc, argv, "01", &n);
+ n = argv[0];
if (!k_integer_p(n))
rb_raise(rb_eTypeError, "not an integer");
b = f_expt10(n);
- s = nurat_mul(self, b);
+ s = rb_rational_mul(self, b);
if (k_float_p(s)) {
if (INT_NEGATIVE_P(n))
@@ -1353,7 +1410,7 @@ f_round_common(int argc, VALUE *argv, VALUE self, VALUE (*func)(VALUE))
s = (*func)(s);
- s = nurat_div(f_rational_new_bang1(CLASS_OF(self), s), b);
+ 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);
@@ -1361,23 +1418,41 @@ f_round_common(int argc, VALUE *argv, VALUE self, VALUE (*func)(VALUE))
return s;
}
+VALUE
+rb_rational_floor(VALUE self, int ndigits)
+{
+ if (ndigits == 0) {
+ return nurat_floor(self);
+ }
+ else {
+ VALUE n = INT2NUM(ndigits);
+ return f_round_common(1, &n, self, nurat_floor);
+ }
+}
+
/*
* call-seq:
- * rat.floor -> integer
- * rat.floor(precision=0) -> rational
+ * rat.floor([ndigits]) -> integer or rational
+ *
+ * Returns the largest number less than or equal to +rat+ with
+ * a precision of +ndigits+ decimal digits (default: 0).
+ *
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
*
- * Returns the truncated value (toward negative infinity).
+ * Returns a rational when +ndigits+ is positive,
+ * otherwise returns an integer.
*
* Rational(3).floor #=> 3
* Rational(2, 3).floor #=> 0
- * Rational(-3, 2).floor #=> -1
+ * Rational(-3, 2).floor #=> -2
*
- * decimal - 1 2 3 . 4 5 6
- * ^ ^ ^ ^ ^ ^
- * precision -3 -2 -1 0 +1 +2
+ * # decimal - 1 2 3 . 4 5 6
+ * # ^ ^ ^ ^ ^ ^
+ * # precision -3 -2 -1 0 +1 +2
*
- * '%f' % Rational('-123.456').floor(+1) #=> "-123.500000"
- * '%f' % Rational('-123.456').floor(-1) #=> "-130.000000"
+ * Rational('-123.456').floor(+1).to_f #=> -123.5
+ * Rational('-123.456').floor(-1) #=> -130
*/
static VALUE
nurat_floor_n(int argc, VALUE *argv, VALUE self)
@@ -1387,21 +1462,27 @@ nurat_floor_n(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * rat.ceil -> integer
- * rat.ceil(precision=0) -> rational
+ * rat.ceil([ndigits]) -> integer or rational
+ *
+ * Returns the smallest number greater than or equal to +rat+ with
+ * a precision of +ndigits+ decimal digits (default: 0).
+ *
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
*
- * Returns the truncated value (toward positive infinity).
+ * Returns a rational when +ndigits+ is positive,
+ * otherwise returns an integer.
*
* Rational(3).ceil #=> 3
* Rational(2, 3).ceil #=> 1
* Rational(-3, 2).ceil #=> -1
*
- * decimal - 1 2 3 . 4 5 6
- * ^ ^ ^ ^ ^ ^
- * precision -3 -2 -1 0 +1 +2
+ * # decimal - 1 2 3 . 4 5 6
+ * # ^ ^ ^ ^ ^ ^
+ * # precision -3 -2 -1 0 +1 +2
*
- * '%f' % Rational('-123.456').ceil(+1) #=> "-123.400000"
- * '%f' % Rational('-123.456').ceil(-1) #=> "-120.000000"
+ * Rational('-123.456').ceil(+1).to_f #=> -123.4
+ * Rational('-123.456').ceil(-1) #=> -120
*/
static VALUE
nurat_ceil_n(int argc, VALUE *argv, VALUE self)
@@ -1411,21 +1492,27 @@ nurat_ceil_n(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * rat.truncate -> integer
- * rat.truncate(precision=0) -> rational
+ * rat.truncate([ndigits]) -> integer or rational
*
- * Returns the truncated value (toward zero).
+ * Returns +rat+ truncated (toward zero) to
+ * a precision of +ndigits+ decimal digits (default: 0).
+ *
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
+ *
+ * Returns a rational when +ndigits+ is positive,
+ * otherwise returns an integer.
*
* Rational(3).truncate #=> 3
* Rational(2, 3).truncate #=> 0
* Rational(-3, 2).truncate #=> -1
*
- * decimal - 1 2 3 . 4 5 6
- * ^ ^ ^ ^ ^ ^
- * precision -3 -2 -1 0 +1 +2
+ * # decimal - 1 2 3 . 4 5 6
+ * # ^ ^ ^ ^ ^ ^
+ * # precision -3 -2 -1 0 +1 +2
*
- * '%f' % Rational('-123.456').truncate(+1) #=> "-123.400000"
- * '%f' % Rational('-123.456').truncate(-1) #=> "-120.000000"
+ * Rational('-123.456').truncate(+1).to_f #=> -123.4
+ * Rational('-123.456').truncate(-1) #=> -120
*/
static VALUE
nurat_truncate_n(int argc, VALUE *argv, VALUE self)
@@ -1435,22 +1522,40 @@ nurat_truncate_n(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * rat.round -> integer
- * rat.round(precision=0) -> rational
+ * rat.round([ndigits] [, half: mode]) -> integer or rational
+ *
+ * Returns +rat+ rounded to the nearest value with
+ * a precision of +ndigits+ decimal digits (default: 0).
*
- * Returns the truncated value (toward the nearest integer;
- * 0.5 => 1; -0.5 => -1).
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
+ *
+ * Returns a rational when +ndigits+ is positive,
+ * otherwise returns an integer.
*
* Rational(3).round #=> 3
* Rational(2, 3).round #=> 1
* Rational(-3, 2).round #=> -2
*
- * decimal - 1 2 3 . 4 5 6
- * ^ ^ ^ ^ ^ ^
- * precision -3 -2 -1 0 +1 +2
- *
- * '%f' % Rational('-123.456').round(+1) #=> "-123.500000"
- * '%f' % Rational('-123.456').round(-1) #=> "-120.000000"
+ * # decimal - 1 2 3 . 4 5 6
+ * # ^ ^ ^ ^ ^ ^
+ * # precision -3 -2 -1 0 +1 +2
+ *
+ * Rational('-123.456').round(+1).to_f #=> -123.5
+ * Rational('-123.456').round(-1) #=> -120
+ *
+ * The optional +half+ keyword argument is available
+ * similar to Float#round.
+ *
+ * Rational(25, 100).round(1, half: :up) #=> (3/10)
+ * Rational(25, 100).round(1, half: :down) #=> (1/5)
+ * Rational(25, 100).round(1, half: :even) #=> (1/5)
+ * Rational(35, 100).round(1, half: :up) #=> (2/5)
+ * Rational(35, 100).round(1, half: :down) #=> (3/10)
+ * Rational(35, 100).round(1, half: :even) #=> (2/5)
+ * Rational(-25, 100).round(1, half: :up) #=> (-3/10)
+ * Rational(-25, 100).round(1, half: :down) #=> (-1/5)
+ * Rational(-25, 100).round(1, half: :even) #=> (-1/5)
*/
static VALUE
nurat_round_n(int argc, VALUE *argv, VALUE self)
@@ -1459,9 +1564,7 @@ nurat_round_n(int argc, VALUE *argv, VALUE self)
enum ruby_num_rounding_mode mode = (
argc = rb_scan_args(argc, argv, "*:", NULL, &opt),
rb_num_get_rounding_option(opt));
- VALUE (*round_func)(VALUE) =
- ROUND_TO(mode,
- nurat_round_half_up, nurat_round_half_even);
+ VALUE (*round_func)(VALUE) = ROUND_FUNC(mode, nurat_round);
return f_round_common(argc, argv, self, round_func);
}
@@ -1469,6 +1572,9 @@ static double
nurat_to_double(VALUE self)
{
get_dat1(self);
+ if (!RB_INTEGER_TYPE_P(dat->num) || !RB_INTEGER_TYPE_P(dat->den)) {
+ return NUM2DBL(dat->num) / NUM2DBL(dat->den);
+ }
return rb_int_fdiv_double(dat->num, dat->den);
}
@@ -1476,7 +1582,7 @@ nurat_to_double(VALUE self)
* call-seq:
* rat.to_f -> float
*
- * Return the value as a float.
+ * Returns the value as a Float.
*
* Rational(2).to_f #=> 2.0
* Rational(9, 4).to_f #=> 2.25
@@ -1606,8 +1712,8 @@ nurat_rationalize_internal(VALUE a, VALUE b, VALUE *p, VALUE *q)
* rat.rationalize(eps) -> rational
*
* Returns a simpler approximation of the value if the optional
- * argument eps is given (rat-|eps| <= result <= rat+|eps|), self
- * otherwise.
+ * argument +eps+ is given (rat-|eps| <= result <= rat+|eps|),
+ * self otherwise.
*
* r = Rational(5033165, 16777216)
* r.rationalize #=> (5033165/16777216)
@@ -1619,14 +1725,13 @@ nurat_rationalize(int argc, VALUE *argv, VALUE self)
{
VALUE e, a, b, p, q;
- if (argc == 0)
+ if (rb_check_arity(argc, 0, 1) == 0)
return self;
if (nurat_negative_p(self))
- return nurat_negate(nurat_rationalize(argc, argv, nurat_negate(self)));
+ return rb_rational_uminus(nurat_rationalize(argc, argv, rb_rational_uminus(self)));
- rb_scan_args(argc, argv, "01", &e);
- e = f_abs(e);
+ e = f_abs(argv[0]);
a = f_sub(self, e);
b = f_add(self, e);
@@ -1650,7 +1755,7 @@ nurat_hash(VALUE self)
n = rb_hash(dat->den);
h[1] = NUM2LONG(n);
v = rb_memhash(h, sizeof(h));
- return LONG2FIX(v);
+ return ST2FIX(v);
}
static VALUE
@@ -1725,6 +1830,7 @@ nurat_loader(VALUE self, VALUE a)
nurat_canonicalize(&num, &den);
RRATIONAL_SET_NUM(dat, num);
RRATIONAL_SET_DEN(dat, den);
+ OBJ_FREEZE_RAW(self);
return self;
}
@@ -1771,16 +1877,17 @@ VALUE
rb_rational_reciprocal(VALUE x)
{
get_dat1(x);
- return f_rational_new_no_reduce2(CLASS_OF(x), dat->den, dat->num);
+ return nurat_convert(CLASS_OF(x), dat->den, dat->num, FALSE);
}
/*
* call-seq:
- * int.gcd(int2) -> integer
+ * int.gcd(other_int) -> integer
*
- * Returns the greatest common divisor (always positive). 0.gcd(x)
- * and x.gcd(0) return abs(x).
+ * Returns the greatest common divisor of the two integers.
+ * The result is always positive. 0.gcd(x) and x.gcd(0) return x.abs.
*
+ * 36.gcd(60) #=> 12
* 2.gcd(2) #=> 2
* 3.gcd(-7) #=> 1
* ((1<<31)-1).gcd((1<<61)-1) #=> 1
@@ -1794,11 +1901,12 @@ rb_gcd(VALUE self, VALUE other)
/*
* call-seq:
- * int.lcm(int2) -> integer
+ * int.lcm(other_int) -> integer
*
- * Returns the least common multiple (always positive). 0.lcm(x) and
- * x.lcm(0) return zero.
+ * Returns the least common multiple of the two integers.
+ * The result is always positive. 0.lcm(x) and x.lcm(0) return zero.
*
+ * 36.lcm(60) #=> 180
* 2.lcm(2) #=> 2
* 3.lcm(-7) #=> 21
* ((1<<31)-1).lcm((1<<61)-1) #=> 4951760154835678088235319297
@@ -1812,10 +1920,12 @@ rb_lcm(VALUE self, VALUE other)
/*
* call-seq:
- * int.gcdlcm(int2) -> array
+ * int.gcdlcm(other_int) -> array
*
- * Returns an array; [int.gcd(int2), int.lcm(int2)].
+ * Returns an array with the greatest common divisor and
+ * the least common multiple of the two integers, [gcd, lcm].
*
+ * 36.gcdlcm(60) #=> [12, 180]
* 2.gcdlcm(2) #=> [2, 2]
* 3.gcdlcm(-7) #=> [1, 21]
* ((1<<31)-1).gcdlcm((1<<61)-1) #=> [1, 4951760154835678088235319297]
@@ -1866,7 +1976,7 @@ rb_rational_den(VALUE rat)
#define id_denominator rb_intern("denominator")
#define f_denominator(x) rb_funcall((x), id_denominator, 0)
-#define id_to_r rb_intern("to_r")
+#define id_to_r idTo_r
#define f_to_r(x) rb_funcall((x), id_to_r, 0)
/*
@@ -1899,28 +2009,34 @@ numeric_denominator(VALUE self)
* num.quo(int_or_rat) -> rat
* num.quo(flo) -> flo
*
- * Returns most exact division (rational for integers, float for floats).
+ * Returns the most exact division (rational for integers, float for floats).
*/
-static VALUE
-numeric_quo(VALUE x, VALUE y)
+VALUE
+rb_numeric_quo(VALUE x, VALUE y)
{
if (RB_FLOAT_TYPE_P(y)) {
return rb_funcall(x, rb_intern("fdiv"), 1, y);
}
-#ifdef CANON
if (canonicalization) {
x = rb_rational_raw1(x);
}
- else
-#endif
- {
+ else {
x = rb_convert_type(x, T_RATIONAL, "Rational", "to_r");
}
- return nurat_div(x, y);
+ return rb_rational_div(x, y);
}
+VALUE
+rb_rational_canonicalize(VALUE x)
+{
+ if (RB_TYPE_P(x, T_RATIONAL)) {
+ get_dat1(x);
+ if (f_one_p(dat->den)) return dat->num;
+ }
+ return x;
+}
/*
* call-seq:
@@ -1956,14 +2072,21 @@ static VALUE float_to_r(VALUE self);
* n = 0.3.numerator #=> 5404319552844595
* d = 0.3.denominator #=> 18014398509481984
* n.fdiv(d) #=> 0.3
+ *
+ * See also Float#denominator.
*/
static VALUE
float_numerator(VALUE self)
{
double d = RFLOAT_VALUE(self);
+ VALUE r;
if (isinf(d) || isnan(d))
return self;
- return nurat_numerator(float_to_r(self));
+ r = float_to_r(self);
+ if (canonicalization && k_integer_p(r)) {
+ return r;
+ }
+ return nurat_numerator(r);
}
/*
@@ -1973,15 +2096,20 @@ float_numerator(VALUE self)
* Returns the denominator (always positive). The result is machine
* dependent.
*
- * See numerator.
+ * See also Float#numerator.
*/
static VALUE
float_denominator(VALUE self)
{
double d = RFLOAT_VALUE(self);
+ VALUE r;
if (isinf(d) || isnan(d))
return INT2FIX(1);
- return nurat_denominator(float_to_r(self));
+ r = float_to_r(self);
+ if (canonicalization && k_integer_p(r)) {
+ return ONE;
+ }
+ return nurat_denominator(r);
}
/*
@@ -2000,13 +2128,13 @@ nilclass_to_r(VALUE self)
* call-seq:
* nil.rationalize([eps]) -> (0/1)
*
- * Returns zero as a rational. The optional argument eps is always
+ * Returns zero as a rational. The optional argument +eps+ is always
* ignored.
*/
static VALUE
nilclass_rationalize(int argc, VALUE *argv, VALUE self)
{
- rb_scan_args(argc, argv, "01", NULL);
+ rb_check_arity(argc, 0, 1);
return nilclass_to_r(self);
}
@@ -2029,13 +2157,13 @@ integer_to_r(VALUE self)
* call-seq:
* int.rationalize([eps]) -> rational
*
- * Returns the value as a rational. The optional argument eps is
+ * Returns the value as a rational. The optional argument +eps+ is
* always ignored.
*/
static VALUE
integer_rationalize(int argc, VALUE *argv, VALUE self)
{
- rb_scan_args(argc, argv, "01", NULL);
+ rb_check_arity(argc, 0, 1);
return integer_to_r(self);
}
@@ -2052,32 +2180,25 @@ float_decode_internal(VALUE self, VALUE *rf, VALUE *rn)
*rn = INT2FIX(n);
}
-#if 0
-static VALUE
-float_decode(VALUE self)
-{
- VALUE f, n;
-
- float_decode_internal(self, &f, &n);
- return rb_assoc_new(f, n);
-}
-#endif
-
/*
* call-seq:
* flt.to_r -> rational
*
* Returns the value as a rational.
*
- * NOTE: 0.3.to_r isn't the same as '0.3'.to_r. The latter is
- * equivalent to '3/10'.to_r, but the former isn't so.
- *
* 2.0.to_r #=> (2/1)
* 2.5.to_r #=> (5/2)
* -0.75.to_r #=> (-3/4)
* 0.0.to_r #=> (0/1)
+ * 0.3.to_r #=> (5404319552844595/18014398509481984)
*
- * See rationalize.
+ * NOTE: 0.3.to_r isn't the same as "0.3".to_r. The latter is
+ * equivalent to "3/10".to_r, but the former isn't so.
+ *
+ * 0.3.to_r == 3/10r #=> false
+ * "0.3".to_r == 3/10r #=> true
+ *
+ * See also Float#rationalize.
*/
static VALUE
float_to_r(VALUE self)
@@ -2163,28 +2284,25 @@ rb_flt_rationalize(VALUE flt)
* flt.rationalize([eps]) -> rational
*
* Returns a simpler approximation of the value (flt-|eps| <= result
- * <= flt+|eps|). if the optional eps is not given, it will be chosen
- * automatically.
+ * <= flt+|eps|). If the optional argument +eps+ is not given,
+ * it will be chosen automatically.
*
* 0.3.rationalize #=> (3/10)
* 1.333.rationalize #=> (1333/1000)
* 1.333.rationalize(0.01) #=> (4/3)
*
- * See to_r.
+ * See also Float#to_r.
*/
static VALUE
float_rationalize(int argc, VALUE *argv, VALUE self)
{
- VALUE e;
double d = RFLOAT_VALUE(self);
if (d < 0.0)
- return nurat_negate(float_rationalize(argc, argv, DBL2NUM(-d)));
-
- rb_scan_args(argc, argv, "01", &e);
+ return rb_rational_uminus(float_rationalize(argc, argv, DBL2NUM(-d)));
- if (argc != 0) {
- return rb_flt_rationalize_with_prec(self, e);
+ if (rb_check_arity(argc, 0, 1)) {
+ return rb_flt_rationalize_with_prec(self, argv[0]);
}
else {
return rb_flt_rationalize(self);
@@ -2200,11 +2318,11 @@ issign(int c)
}
static int
-read_sign(const char **s)
+read_sign(const char **s, const char *const e)
{
int sign = '?';
- if (issign(**s)) {
+ if (*s < e && issign(**s)) {
sign = **s;
(*s)++;
}
@@ -2212,210 +2330,180 @@ read_sign(const char **s)
}
inline static int
-isdecimal(int c)
+islettere(int c)
{
- return isdigit((unsigned char)c);
+ return (c == 'e' || c == 'E');
}
-static int
-read_digits(const char **s, int strict,
- VALUE *num, int *count)
+static VALUE
+negate_num(VALUE num)
{
- char *b, *bb;
- int us = 1, ret = 1;
- VALUE tmp;
-
- if (!isdecimal(**s)) {
- *num = ZERO;
- return 0;
+ if (FIXNUM_P(num)) {
+ return rb_int_uminus(num);
}
-
- bb = b = ALLOCV_N(char, tmp, strlen(*s) + 1);
-
- while (isdecimal(**s) || **s == '_') {
- if (**s == '_') {
- if (strict) {
- if (us) {
- ret = 0;
- goto conv;
- }
- }
- us = 1;
- }
- else {
- if (count)
- (*count)++;
- *b++ = **s;
- us = 0;
- }
- (*s)++;
+ else {
+ BIGNUM_NEGATE(num);
+ return rb_big_norm(num);
}
- if (us)
- do {
- (*s)--;
- } while (**s == '_');
- conv:
- *b = '\0';
- *num = rb_cstr_to_inum(bb, 10, 0);
- ALLOCV_END(tmp);
- return ret;
-}
-
-inline static int
-islettere(int c)
-{
- return (c == 'e' || c == 'E');
}
static int
-read_num(const char **s, int numsign, int strict,
- VALUE *num)
-{
- VALUE ip, fp, exp;
-
- *num = rb_rational_new2(ZERO, ONE);
- exp = Qnil;
-
- if (**s != '.') {
- if (!read_digits(s, strict, &ip, NULL))
+read_num(const char **s, const char *const end, VALUE *num, VALUE *nexp)
+{
+ VALUE fp = ONE, exp, fn = ZERO, n = ZERO;
+ int expsign = 0, ok = 0;
+ char *e;
+
+ *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;
- *num = rb_rational_new2(ip, ONE);
+ *s = e;
+ *num = n;
+ ok = 1;
}
- if (**s == '.') {
- int count = 0;
+ if (*s < end && **s == '.') {
+ size_t count = 0;
(*s)++;
- if (!read_digits(s, strict, &fp, &count))
- return 0;
+ 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(INT2NUM(count));
-#ifdef CANON
- if (canonicalization) {
- *num = rb_int_mul(*num, l);
- *num = rb_int_plus(*num, fp);
- *num = rb_rational_new2(*num, l);
- }
- else
-#endif
- {
- *num = nurat_mul(*num, l);
- *num = rb_rational_plus(*num, fp);
- *num = nurat_div(*num, l);
- }
+ 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;
}
- if (islettere(**s)) {
- int expsign;
-
+ if (ok && *s + 1 < end && islettere(**s)) {
(*s)++;
- expsign = read_sign(s);
- if (!read_digits(s, strict, &exp, NULL))
- return 0;
- if (expsign == '-')
- exp = rb_int_uminus(exp);
+ 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;
+ }
}
- if (numsign == '-')
- *num = nurat_negate(*num);
- if (!NIL_P(exp)) {
- VALUE l = f_expt10(exp);
- *num = nurat_mul(*num, l);
- }
- return 1;
+ return ok;
}
-inline static int
-read_den(const char **s, int strict,
- VALUE *num)
+inline static const char *
+skip_ws(const char *s, const char *e)
{
- if (!read_digits(s, strict, num, NULL))
- return 0;
- return 1;
+ while (s < e && isspace((unsigned char)*s))
+ ++s;
+ return s;
}
-static int
-read_rat_nos(const char **s, int sign, int strict,
- VALUE *num)
+static VALUE
+parse_rat(const char *s, const char *const e, int strict, int raise)
{
- VALUE den;
+ int sign;
+ VALUE num, den, nexp, dexp;
- if (!read_num(s, sign, strict, num))
- return 0;
- if (**s == '/') {
- (*s)++;
- if (!read_den(s, strict, &den))
- return 0;
- if (!(FIXNUM_P(den) && FIX2LONG(den) == 1))
- *num = nurat_div(*num, den);
+ s = skip_ws(s, e);
+ sign = read_sign(&s, e);
+
+ if (!read_num(&s, e, &num, &nexp)) {
+ if (strict) return Qnil;
+ return canonicalization ? ZERO : nurat_s_alloc(rb_cRational);
+ }
+ den = ONE;
+ if (s < e && *s == '/') {
+ s++;
+ if (!read_num(&s, e, &den, &dexp)) {
+ if (strict) return Qnil;
+ den = ONE;
+ }
+ else if (den == ZERO) {
+ if (!raise) return Qnil;
+ rb_num_zerodiv();
+ }
+ else if (strict && skip_ws(s, e) != e) {
+ return Qnil;
+ }
+ else {
+ nexp = rb_int_minus(nexp, dexp);
+ nurat_reduce(&num, &den);
+ }
+ }
+ else if (strict && skip_ws(s, e) != e) {
+ return Qnil;
}
- return 1;
-}
-static int
-read_rat(const char **s, int strict,
- VALUE *num)
-{
- int sign;
+ if (nexp != ZERO) {
+ if (INT_NEGATIVE_P(nexp)) {
+ VALUE mul;
+ if (!FIXNUM_P(nexp)) {
+ overflow:
+ return sign == '-' ? DBL2NUM(-HUGE_VAL) : DBL2NUM(HUGE_VAL);
+ }
+ mul = f_expt10(LONG2NUM(-FIX2LONG(nexp)));
+ if (RB_FLOAT_TYPE_P(mul)) goto overflow;
+ num = rb_int_mul(num, mul);
+ }
+ else {
+ VALUE div;
+ if (!FIXNUM_P(nexp)) {
+ underflow:
+ return sign == '-' ? DBL2NUM(-0.0) : DBL2NUM(+0.0);
+ }
+ div = f_expt10(nexp);
+ if (RB_FLOAT_TYPE_P(div)) goto underflow;
+ den = rb_int_mul(den, div);
+ }
+ nurat_reduce(&num, &den);
+ }
- sign = read_sign(s);
- if (!read_rat_nos(s, sign, strict, num))
- return 0;
- return 1;
-}
+ if (sign == '-') {
+ num = negate_num(num);
+ }
-inline static void
-skip_ws(const char **s)
-{
- while (isspace((unsigned char)**s))
- (*s)++;
+ if (!canonicalization || den != ONE)
+ num = rb_rational_raw(num, den);
+ return num;
}
-static int
-parse_rat(const char *s, int strict,
- VALUE *num)
-{
- skip_ws(&s);
- if (!read_rat(&s, strict, num))
- return 0;
- skip_ws(&s);
-
- if (strict)
- if (*s != '\0')
- return 0;
- return 1;
-}
+#define FLOAT_ZERO_P(x) (rb_float_value(x) == 0.0)
static VALUE
-string_to_r_strict(VALUE self)
+string_to_r_strict(VALUE self, int raise)
{
- char *s;
VALUE num;
rb_must_asciicompat(self);
- s = RSTRING_PTR(self);
-
- if (!s || memchr(s, '\0', RSTRING_LEN(self)))
- 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';
+ num = parse_rat(RSTRING_PTR(self), RSTRING_END(self), 1, raise);
+ if (NIL_P(num)) {
+ if (!raise) return Qnil;
+ rb_raise(rb_eArgError, "invalid value for convert(): %+"PRIsVALUE,
+ self);
}
- if (!s)
- s = (char *)"";
-
- if (!parse_rat(s, 1, &num)) {
- rb_raise(rb_eArgError, "invalid value for convert(): %+"PRIsVALUE,
- self);
+ if (RB_FLOAT_TYPE_P(num) && !FLOAT_ZERO_P(num)) {
+ if (!raise) return Qnil;
+ rb_raise(rb_eFloatDomainError, "Infinity");
}
-
- if (RB_FLOAT_TYPE_P(num))
- rb_raise(rb_eFloatDomainError, "Infinity");
return num;
}
@@ -2423,47 +2511,40 @@ string_to_r_strict(VALUE self)
* call-seq:
* str.to_r -> rational
*
- * Returns a rational which denotes the string form. The parser
- * ignores leading whitespaces and trailing garbage. Any digit
- * sequences can be separated by an underscore. Returns zero for null
- * or garbage string.
- *
- * NOTE: '0.3'.to_r isn't the same as 0.3.to_r. The former is
- * equivalent to '3/10'.to_r, but the latter isn't so.
+ * Returns the result of interpreting leading characters in +str+
+ * as a rational. Leading whitespace and extraneous characters
+ * past the end of a valid number are ignored.
+ * Digit sequences can be separated by an underscore.
+ * If there is not a valid number at the start of +str+,
+ * zero is returned. This method never raises an exception.
*
* ' 2 '.to_r #=> (2/1)
* '300/2'.to_r #=> (150/1)
* '-9.2'.to_r #=> (-46/5)
* '-9.2e2'.to_r #=> (-920/1)
* '1_234_567'.to_r #=> (1234567/1)
- * '21 june 09'.to_r #=> (21/1)
+ * '21 June 09'.to_r #=> (21/1)
* '21/06/09'.to_r #=> (7/2)
- * 'bwv 1079'.to_r #=> (0/1)
+ * 'BWV 1079'.to_r #=> (0/1)
*
- * See Kernel.Rational.
+ * NOTE: "0.3".to_r isn't the same as 0.3.to_r. The former is
+ * equivalent to "3/10".to_r, but the latter isn't so.
+ *
+ * "0.3".to_r == 3/10r #=> true
+ * 0.3.to_r == 3/10r #=> false
+ *
+ * See also Kernel#Rational.
*/
static VALUE
string_to_r(VALUE self)
{
- char *s;
VALUE num;
rb_must_asciicompat(self);
- s = RSTRING_PTR(self);
-
- if (s && s[RSTRING_LEN(self)]) {
- rb_str_modify(self);
- s = RSTRING_PTR(self);
- s[RSTRING_LEN(self)] = '\0';
- }
-
- if (!s)
- s = (char *)"";
+ num = parse_rat(RSTRING_PTR(self), RSTRING_END(self), 0, TRUE);
- (void)parse_rat(s, 0, &num);
-
- if (RB_FLOAT_TYPE_P(num))
+ if (RB_FLOAT_TYPE_P(num) && !FLOAT_ZERO_P(num))
rb_raise(rb_eFloatDomainError, "Infinity");
return num;
}
@@ -2473,82 +2554,138 @@ rb_cstr_to_rat(const char *s, int strict) /* for complex's internal */
{
VALUE num;
- (void)parse_rat(s, strict, &num);
+ num = parse_rat(s, s + strlen(s), strict, TRUE);
- if (RB_FLOAT_TYPE_P(num))
+ if (RB_FLOAT_TYPE_P(num) && !FLOAT_ZERO_P(num))
rb_raise(rb_eFloatDomainError, "Infinity");
return num;
}
static VALUE
-nurat_s_convert(int argc, VALUE *argv, VALUE klass)
+to_rational(VALUE val)
{
- VALUE a1, a2, backref;
+ return rb_convert_type_with_id(val, T_RATIONAL, "Rational", idTo_r);
+}
- rb_scan_args(argc, argv, "11", &a1, &a2);
+static VALUE
+nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise)
+{
+ VALUE a1 = numv, a2 = denv;
+ int state;
- if (NIL_P(a1) || (argc == 2 && NIL_P(a2)))
- rb_raise(rb_eTypeError, "can't convert nil into Rational");
+ if (NIL_P(a1) || NIL_P(a2)) {
+ if (!raise) return Qnil;
+ rb_raise(rb_eTypeError, "can't convert nil into Rational");
+ }
if (RB_TYPE_P(a1, T_COMPLEX)) {
- if (k_exact_zero_p(RCOMPLEX(a1)->imag))
- a1 = RCOMPLEX(a1)->real;
+ if (k_exact_zero_p(RCOMPLEX(a1)->imag))
+ a1 = RCOMPLEX(a1)->real;
}
if (RB_TYPE_P(a2, T_COMPLEX)) {
- if (k_exact_zero_p(RCOMPLEX(a2)->imag))
- a2 = RCOMPLEX(a2)->real;
+ if (k_exact_zero_p(RCOMPLEX(a2)->imag))
+ a2 = RCOMPLEX(a2)->real;
}
- backref = rb_backref_get();
- rb_match_busy(backref);
-
if (RB_FLOAT_TYPE_P(a1)) {
- a1 = float_to_r(a1);
+ a1 = float_to_r(a1);
}
else if (RB_TYPE_P(a1, T_STRING)) {
- a1 = string_to_r_strict(a1);
+ a1 = string_to_r_strict(a1, raise);
+ if (!raise && NIL_P(a1)) return Qnil;
}
if (RB_FLOAT_TYPE_P(a2)) {
- a2 = float_to_r(a2);
+ a2 = float_to_r(a2);
}
else if (RB_TYPE_P(a2, T_STRING)) {
- a2 = string_to_r_strict(a2);
+ a2 = string_to_r_strict(a2, raise);
+ if (!raise && NIL_P(a2)) return Qnil;
}
- rb_backref_set(backref);
-
if (RB_TYPE_P(a1, T_RATIONAL)) {
- if (argc == 1 || (k_exact_one_p(a2)))
- return a1;
+ if (a2 == Qundef || (k_exact_one_p(a2)))
+ return a1;
}
- if (argc == 1) {
- if (!(k_numeric_p(a1) && k_integer_p(a1)))
- return rb_convert_type(a1, T_RATIONAL, "Rational", "to_r");
+ if (a2 == Qundef) {
+ if (!k_integer_p(a1)) {
+ if (!raise) {
+ VALUE result = rb_protect(to_rational, a1, NULL);
+ rb_set_errinfo(Qnil);
+ return result;
+ }
+ return to_rational(a1);
+ }
}
else {
- if ((k_numeric_p(a1) && k_numeric_p(a2)) &&
- (!f_integer_p(a1) || !f_integer_p(a2)))
- return f_div(a1, a2);
+ if (!k_numeric_p(a1)) {
+ if (!raise) {
+ a1 = rb_protect(to_rational, a1, &state);
+ if (state) {
+ rb_set_errinfo(Qnil);
+ return Qnil;
+ }
+ }
+ else {
+ a1 = rb_check_convert_type_with_id(a1, T_RATIONAL, "Rational", idTo_r);
+ }
+ }
+ if (!k_numeric_p(a2)) {
+ if (!raise) {
+ a2 = rb_protect(to_rational, a2, &state);
+ if (state) {
+ rb_set_errinfo(Qnil);
+ return Qnil;
+ }
+ }
+ else {
+ a2 = rb_check_convert_type_with_id(a2, T_RATIONAL, "Rational", idTo_r);
+ }
+ }
+ if ((k_numeric_p(a1) && k_numeric_p(a2)) &&
+ (!f_integer_p(a1) || !f_integer_p(a2)))
+ return f_div(a1, a2);
}
{
- VALUE argv2[2];
- argv2[0] = a1;
- argv2[1] = a2;
- return nurat_s_new(argc, argv2, klass);
+ int argc;
+ VALUE argv2[2];
+ argv2[0] = a1;
+ if (a2 == Qundef) {
+ argv2[1] = Qnil;
+ argc = 1;
+ }
+ else {
+ if (!k_integer_p(a2) && !raise) return Qnil;
+ argv2[1] = a2;
+ argc = 2;
+ }
+ return nurat_s_new(argc, argv2, klass);
}
}
+static VALUE
+nurat_s_convert(int argc, VALUE *argv, VALUE klass)
+{
+ VALUE a1, a2;
+
+ if (rb_scan_args(argc, argv, "11", &a1, &a2) == 1) {
+ a2 = Qundef;
+ }
+
+ return nurat_convert(klass, a1, a2, TRUE);
+}
+
/*
- * A rational number can be represented as a paired integer number;
- * a/b (b>0). Where a is numerator and b is denominator. Integer a
- * equals rational a/1 mathematically.
+ * A rational number can be represented as a pair of integer numbers:
+ * a/b (b>0), where a is the numerator and b is the denominator.
+ * Integer a equals rational a/1 mathematically.
*
- * In ruby, you can create rational object with Rational, to_r,
- * rationalize method or suffixing r to a literal. The return values will be irreducible.
+ * In Ruby, you can create rational objects with the Kernel#Rational,
+ * to_r, or rationalize methods or by suffixing +r+ to a literal.
+ * The return values will be irreducible fractions.
*
* Rational(1) #=> (1/1)
* Rational(2, 3) #=> (2/3)
@@ -2556,7 +2693,7 @@ nurat_s_convert(int argc, VALUE *argv, VALUE klass)
* 3.to_r #=> (3/1)
* 2/3r #=> (2/3)
*
- * You can also create rational object from floating-point numbers or
+ * You can also create rational objects from floating-point numbers or
* strings.
*
* Rational(0.3) #=> (5404319552844595/18014398509481984)
@@ -2569,13 +2706,13 @@ nurat_s_convert(int argc, VALUE *argv, VALUE klass)
* 0.3.rationalize #=> (3/10)
*
* A rational object is an exact number, which helps you to write
- * program without any rounding errors.
+ * programs without any rounding errors.
*
- * 10.times.inject(0){|t,| t + 0.1} #=> 0.9999999999999999
- * 10.times.inject(0){|t,| t + Rational('0.1')} #=> (1/1)
+ * 10.times.inject(0) {|t| t + 0.1 } #=> 0.9999999999999999
+ * 10.times.inject(0) {|t| t + Rational('0.1') } #=> (1/1)
*
- * However, when an expression has inexact factor (numerical value or
- * operation), will produce an inexact result.
+ * However, when an expression includes an inexact component (numerical value
+ * or operation), it will produce an inexact result.
*
* Rational(10) / 3 #=> (10/3)
* Rational(10) / 3.0 #=> 3.3333333333333335
@@ -2590,13 +2727,9 @@ Init_Rational(void)
#undef rb_intern
#define rb_intern(str) rb_intern_const(str)
- assert(fprintf(stderr, "assert() is now active\n"));
-
id_abs = rb_intern("abs");
id_idiv = rb_intern("div");
id_integer_p = rb_intern("integer?");
- id_negate = rb_intern("-@");
- id_to_i = rb_intern("to_i");
id_i_num = rb_intern("@numerator");
id_i_den = rb_intern("@denominator");
@@ -2605,42 +2738,30 @@ Init_Rational(void)
rb_define_alloc_func(rb_cRational, nurat_s_alloc);
rb_undef_method(CLASS_OF(rb_cRational), "allocate");
-#if 0
- rb_define_private_method(CLASS_OF(rb_cRational), "new!", nurat_s_new_bang, -1);
- rb_define_private_method(CLASS_OF(rb_cRational), "new", nurat_s_new, -1);
-#else
rb_undef_method(CLASS_OF(rb_cRational), "new");
-#endif
rb_define_global_function("Rational", nurat_f_rational, -1);
rb_define_method(rb_cRational, "numerator", nurat_numerator, 0);
rb_define_method(rb_cRational, "denominator", nurat_denominator, 0);
- rb_define_method(rb_cRational, "-@", nurat_negate, 0);
+ rb_define_method(rb_cRational, "-@", rb_rational_uminus, 0);
rb_define_method(rb_cRational, "+", rb_rational_plus, 1);
- rb_define_method(rb_cRational, "-", nurat_sub, 1);
- rb_define_method(rb_cRational, "*", nurat_mul, 1);
- rb_define_method(rb_cRational, "/", nurat_div, 1);
- rb_define_method(rb_cRational, "quo", nurat_div, 1);
+ rb_define_method(rb_cRational, "-", rb_rational_minus, 1);
+ rb_define_method(rb_cRational, "*", rb_rational_mul, 1);
+ rb_define_method(rb_cRational, "/", rb_rational_div, 1);
+ rb_define_method(rb_cRational, "quo", rb_rational_div, 1);
rb_define_method(rb_cRational, "fdiv", nurat_fdiv, 1);
rb_define_method(rb_cRational, "**", nurat_expt, 1);
- rb_define_method(rb_cRational, "<=>", nurat_cmp, 1);
+ rb_define_method(rb_cRational, "<=>", rb_rational_cmp, 1);
rb_define_method(rb_cRational, "==", nurat_eqeq_p, 1);
rb_define_method(rb_cRational, "coerce", nurat_coerce, 1);
-#if 0
- rb_define_method(rb_cRational, "quot", nurat_quot, 1);
- rb_define_method(rb_cRational, "quotrem", nurat_quotrem, 1);
-#endif
-
-#if 0
- rb_define_method(rb_cRational, "rational?", nurat_true, 0);
- rb_define_method(rb_cRational, "exact?", nurat_true, 0);
-#endif
rb_define_method(rb_cRational, "positive?", nurat_positive_p, 0);
rb_define_method(rb_cRational, "negative?", nurat_negative_p, 0);
+ rb_define_method(rb_cRational, "abs", rb_rational_abs, 0);
+ rb_define_method(rb_cRational, "magnitude", rb_rational_abs, 0);
rb_define_method(rb_cRational, "floor", nurat_floor_n, -1);
rb_define_method(rb_cRational, "ceil", nurat_ceil_n, -1);
@@ -2658,6 +2779,7 @@ Init_Rational(void)
rb_define_method(rb_cRational, "inspect", nurat_inspect, 0);
rb_define_private_method(rb_cRational, "marshal_dump", nurat_marshal_dump, 0);
+ /* :nodoc: */
compat = rb_define_class_under(rb_cRational, "compatible", rb_cObject);
rb_define_private_method(compat, "marshal_load", nurat_marshal_load, 1);
rb_marshal_define_compat(rb_cRational, compat, nurat_dumper, nurat_loader);
@@ -2670,7 +2792,7 @@ Init_Rational(void)
rb_define_method(rb_cNumeric, "numerator", numeric_numerator, 0);
rb_define_method(rb_cNumeric, "denominator", numeric_denominator, 0);
- rb_define_method(rb_cNumeric, "quo", numeric_quo, 1);
+ 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);
diff --git a/re.c b/re.c
index 0536aa6d8e..a86663034a 100644
--- a/re.c
+++ b/re.c
@@ -9,9 +9,10 @@
**********************************************************************/
-#include "internal.h"
+#include "ruby/encoding.h"
#include "ruby/re.h"
#include "ruby/util.h"
+#include "internal.h"
#include "regint.h"
#include "encindex.h"
#include <ctype.h>
@@ -88,21 +89,13 @@ rb_memcicmp(const void *x, const void *y, long len)
return 0;
}
-#undef rb_memcmp
-
-int
-rb_memcmp(const void *p1, const void *p2, long len)
-{
- return memcmp(p1, p2, len);
-}
-
#ifdef HAVE_MEMMEM
static inline long
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))
+ if ((y = memmem(ys, n, xs, m)) != NULL)
return y - ys;
else
return -1;
@@ -113,13 +106,7 @@ rb_memsearch_ss(const unsigned char *xs, long m, const unsigned char *ys, long n
{
const unsigned char *x = xs, *xe = xs + m;
const unsigned char *y = ys, *ye = ys + n;
-#ifndef VALUE_MAX
-# if SIZEOF_VALUE == 8
-# define VALUE_MAX 0xFFFFFFFFFFFFFFFFULL
-# elif SIZEOF_VALUE == 4
-# define VALUE_MAX 0xFFFFFFFFUL
-# endif
-#endif
+#define VALUE_MAX ((VALUE)~(VALUE)0)
VALUE hx, hy, mask = VALUE_MAX >> ((SIZEOF_VALUE - m) * CHAR_BIT);
if (m > SIZEOF_VALUE)
@@ -364,7 +351,7 @@ rb_reg_check(VALUE re)
static void
rb_reg_expr_str(VALUE str, const char *s, long len,
- rb_encoding *enc, rb_encoding *resenc)
+ rb_encoding *enc, rb_encoding *resenc, int term)
{
const char *p, *pend;
int cr = ENC_CODERANGE_UNKNOWN;
@@ -385,7 +372,7 @@ rb_reg_expr_str(VALUE str, const char *s, long len,
break;
}
}
- else if (c != '/' && rb_enc_isprint(c, enc)) {
+ else if (c != term && rb_enc_isprint(c, enc)) {
p += clen;
}
else {
@@ -412,11 +399,6 @@ rb_reg_expr_str(VALUE str, const char *s, long len,
p += n;
continue;
}
- else if (c == '/') {
- char c = '\\';
- rb_str_buf_cat(str, &c, 1);
- rb_str_buf_cat(str, p, clen);
- }
else if (c == -1) {
clen = rb_enc_precise_mbclen(p, pend, enc);
if (!MBCLEN_CHARFOUND_P(clen)) {
@@ -433,6 +415,11 @@ rb_reg_expr_str(VALUE str, const char *s, long len,
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);
}
@@ -465,7 +452,7 @@ rb_reg_desc(const char *s, long len, VALUE re)
else {
rb_enc_associate(str, rb_usascii_encoding());
}
- rb_reg_expr_str(str, s, len, enc, resenc);
+ rb_reg_expr_str(str, s, len, enc, resenc, '/');
rb_str_buf_cat2(str, "/");
if (re) {
char opts[4];
@@ -526,6 +513,7 @@ rb_reg_inspect(VALUE re)
return rb_reg_desc(RREGEXP_SRC_PTR(re), RREGEXP_SRC_LEN(re), re);
}
+static VALUE rb_reg_str_with_term(VALUE re, int term);
/*
* call-seq:
@@ -550,6 +538,12 @@ rb_reg_inspect(VALUE re)
static VALUE
rb_reg_to_s(VALUE re)
{
+ return rb_reg_str_with_term(re, '/');
+}
+
+static VALUE
+rb_reg_str_with_term(VALUE re, int term)
+{
int options, opt;
const int embeddable = ONIG_OPTION_MULTILINE|ONIG_OPTION_IGNORECASE|ONIG_OPTION_EXTEND;
long len;
@@ -606,7 +600,7 @@ rb_reg_to_s(VALUE re)
++ptr;
len -= 2;
- err = onig_new(&rp, ptr, ptr + len, ONIG_OPTION_DEFAULT,
+ err = onig_new(&rp, ptr, ptr + len, options,
enc, OnigDefaultSyntax, NULL);
onig_free(rp);
ruby_verbose = verbose;
@@ -628,7 +622,7 @@ rb_reg_to_s(VALUE re)
rb_str_buf_cat2(str, ":");
if (rb_enc_asciicompat(enc)) {
- rb_reg_expr_str(str, (char*)ptr, len, enc, NULL);
+ rb_reg_expr_str(str, (char*)ptr, len, enc, NULL, term);
rb_str_buf_cat2(str, ")");
}
else {
@@ -648,7 +642,7 @@ rb_reg_to_s(VALUE re)
memcpy(paren, s, n);
rb_str_resize(str, RSTRING_LEN(str) - n);
- rb_reg_expr_str(str, (char*)ptr, len, enc, NULL);
+ rb_reg_expr_str(str, (char*)ptr, len, enc, NULL, term);
rb_str_buf_cat(str, paren, n);
}
rb_enc_copy(str, re);
@@ -657,6 +651,8 @@ rb_reg_to_s(VALUE re)
return str;
}
+NORETURN(static void rb_reg_raise(const char *s, long len, const char *err, VALUE re));
+
static void
rb_reg_raise(const char *s, long len, const char *err, VALUE re)
{
@@ -675,13 +671,15 @@ rb_enc_reg_error_desc(const char *s, long len, rb_encoding *enc, int options, co
rb_enc_associate(desc, enc);
rb_str_buf_cat2(desc, ": /");
- rb_reg_expr_str(desc, s, len, enc, resenc);
+ rb_reg_expr_str(desc, s, len, enc, resenc, '/');
opts[0] = '/';
option_to_str(opts + 1, options);
rb_str_buf_cat2(desc, opts);
return rb_exc_new3(rb_eRegexpError, desc);
}
+NORETURN(static void rb_enc_reg_raise(const char *s, long len, rb_encoding *enc, int options, const char *err));
+
static void
rb_enc_reg_raise(const char *s, long len, rb_encoding *enc, int options, const char *err)
{
@@ -695,6 +693,8 @@ rb_reg_error_desc(VALUE str, int options, const char *err)
rb_enc_get(str), options, err);
}
+NORETURN(static void rb_reg_raise_str(VALUE str, int options, const char *err));
+
static void
rb_reg_raise_str(VALUE str, int options, const char *err)
{
@@ -828,9 +828,9 @@ reg_named_captures_iter(const OnigUChar *name, const OnigUChar *name_end,
static VALUE
rb_reg_named_captures(VALUE re)
{
- VALUE hash = rb_hash_new();
- rb_reg_check(re);
- onig_foreach_name(RREGEXP_PTR(re), reg_named_captures_iter, (void*)hash);
+ regex_t *reg = (rb_reg_check(re), RREGEXP_PTR(re));
+ VALUE hash = rb_hash_new_with_size(onig_number_of_names(reg));
+ onig_foreach_name(reg, reg_named_captures_iter, (void*)hash);
return hash;
}
@@ -847,7 +847,7 @@ onig_new_with_source(regex_t** reg, const UChar* pattern, const UChar* pattern_e
r = onig_reg_init(*reg, option, ONIGENC_CASE_FOLD_DEFAULT, enc, syntax);
if (r) goto err;
- r = onig_compile(*reg, pattern, pattern_end, einfo, sourcefile, sourceline);
+ r = onig_compile_ruby(*reg, pattern, pattern_end, einfo, sourcefile, sourceline);
if (r) {
err:
onig_free(*reg);
@@ -884,13 +884,51 @@ make_regexp(const char *s, long len, rb_encoding *enc, int flags, onig_errmsg_bu
/*
* Document-class: MatchData
*
- * <code>MatchData</code> is the type of the special variable <code>$~</code>,
- * and is the type of the object returned by <code>Regexp#match</code> and
- * <code>Regexp.last_match</code>. It encapsulates all the results of a pattern
- * match, results normally accessed through the special variables
- * <code>$&</code>, <code>$'</code>, <code>$`</code>, <code>$1</code>,
- * <code>$2</code>, and so on.
- *
+ * <code>MatchData</code> encapsulates the result of matching a Regexp against
+ * string. It is returned by Regexp#match and
+ * String#match, and also stored in a global variable returned by
+ * Regexp.last_match.
+ *
+ * Usage:
+ *
+ * url = 'https://docs.ruby-lang.org/en/2.5.0/MatchData.html'
+ * m = url.match(/(\d\.?)+/) # => #<MatchData "2.5.0" 1:"0">
+ * m.string # => "https://docs.ruby-lang.org/en/2.5.0/MatchData.html"
+ * m.regexp # => /(\d\.?)+/
+ * # entire matched substring:
+ * m[0] # => "2.5.0"
+ *
+ * # Working with unnamed captures
+ * m = url.match(%r{([^/]+)/([^/]+)\.html$})
+ * m.captures # => ["2.5.0", "MatchData"]
+ * m[1] # => "2.5.0"
+ * m.values_at(1, 2) # => ["2.5.0", "MatchData"]
+ *
+ * # Working with named captures
+ * m = url.match(%r{(?<version>[^/]+)/(?<module>[^/]+)\.html$})
+ * m.captures # => ["2.5.0", "MatchData"]
+ * m.named_captures # => {"version"=>"2.5.0", "module"=>"MatchData"}
+ * m[:version] # => "2.5.0"
+ * m.values_at(:version, :module)
+ * # => ["2.5.0", "MatchData"]
+ * # Numerical indexes are working, too
+ * m[1] # => "2.5.0"
+ * m.values_at(1, 2) # => ["2.5.0", "MatchData"]
+ *
+ * == Global variables equivalence
+ *
+ * Parts of last <code>MatchData</code> (returned by Regexp.last_match) are also
+ * aliased as global variables:
+ *
+ * * <code>$~</code> is <code>Regexp.last_match</code>;
+ * * <code>$&</code> is <code>Regexp.last_match[0]</code>;
+ * * <code>$1</code>, <code>$2</code>, and so on are
+ * <code>Regexp.last_match[i]</code> (captures by number);
+ * * <code>$`</code> is <code>Regexp.last_match.pre_match</code>;
+ * * <code>$'</code> is <code>Regexp.last_match.post_match</code>;
+ * * <code>$+</code> is <code>Regexp.last_match[-1]</code> (the last capture).
+ *
+ * See also "Special global variables" section in Regexp documentation.
*/
VALUE rb_cMatch;
@@ -1089,6 +1127,8 @@ static VALUE
match_names(VALUE match)
{
match_check(match);
+ if (NIL_P(RMATCH(match)->regexp))
+ return rb_ary_new_capa(0);
return rb_reg_names(RMATCH(match)->regexp);
}
@@ -1111,6 +1151,8 @@ match_size(VALUE match)
return INT2FIX(RMATCH_REGS(match)->num_regs);
}
+static int name_to_backref_number(struct re_registers *, VALUE, const char*, const char*);
+
static int
match_backref_number(VALUE match, VALUE backref)
{
@@ -1121,23 +1163,15 @@ match_backref_number(VALUE match, VALUE backref)
VALUE regexp = RMATCH(match)->regexp;
match_check(match);
- switch (TYPE(backref)) {
- default:
- return NUM2INT(backref);
-
- case T_SYMBOL:
+ if (SYMBOL_P(backref)) {
backref = rb_sym2str(backref);
- /* fall through */
-
- case T_STRING:
- name = StringValueCStr(backref);
- break;
}
+ else if (!RB_TYPE_P(backref, T_STRING)) {
+ return NUM2INT(backref);
+ }
+ name = StringValueCStr(backref);
- num = onig_name_to_backref_number(RREGEXP_PTR(regexp),
- (const unsigned char*)name,
- (const unsigned char*)name + strlen(name),
- regs);
+ num = name_to_backref_number(regs, regexp, name, name + strlen(name));
if (num < 1) {
rb_raise(rb_eIndexError, "undefined group name reference: %s", name);
@@ -1266,6 +1300,12 @@ rb_match_busy(VALUE match)
FL_SET(match, MATCH_BUSY);
}
+void
+rb_match_unbusy(VALUE match)
+{
+ FL_UNSET(match, MATCH_BUSY);
+}
+
int
rb_match_count(VALUE match)
{
@@ -1337,14 +1377,14 @@ rb_backref_set_string(VALUE string, long pos, long len)
* r.fixed_encoding? #=> true
* r.encoding #=> #<Encoding:UTF-8>
* r =~ "\u{6666} a" #=> 2
- * r =~ "\xa1\xa2".force_encoding("euc-jp") #=> ArgumentError
+ * r =~ "\xa1\xa2".force_encoding("euc-jp") #=> Encoding::CompatibilityError
* r =~ "abc".force_encoding("euc-jp") #=> 0
*
* r = /\u{6666}/
* r.fixed_encoding? #=> true
* r.encoding #=> #<Encoding:UTF-8>
* r =~ "\u{6666} a" #=> 0
- * r =~ "\xa1\xa2".force_encoding("euc-jp") #=> ArgumentError
+ * r =~ "\xa1\xa2".force_encoding("euc-jp") #=> Encoding::CompatibilityError
* r =~ "abc".force_encoding("euc-jp") #=> nil
*/
@@ -1361,6 +1401,7 @@ static VALUE
rb_reg_preprocess(const char *p, const char *end, rb_encoding *enc,
rb_encoding **fixed_enc, onig_errmsg_buffer err);
+NORETURN(static void reg_enc_error(VALUE re, VALUE str));
static void
reg_enc_error(VALUE re, VALUE str)
@@ -1414,7 +1455,7 @@ rb_reg_prepare_enc(VALUE re, VALUE str, int warn)
else if (warn && (RBASIC(re)->flags & REG_ENCODING_NONE) &&
enc != rb_ascii8bit_encoding() &&
cr != ENC_CODERANGE_7BIT) {
- rb_warn("regexp match /.../n against to %s string",
+ rb_warn("historical binary regexp match /.../n against %s string",
rb_enc_name(enc));
}
return enc;
@@ -1592,6 +1633,83 @@ rb_reg_search(VALUE re, VALUE str, long pos, int reverse)
return rb_reg_search0(re, str, pos, reverse, 1);
}
+bool
+rb_reg_start_with_p(VALUE re, VALUE str)
+{
+ long result;
+ VALUE match;
+ struct re_registers regi, *regs = &regi;
+ regex_t *reg;
+ int tmpreg;
+ onig_errmsg_buffer err = "";
+
+ reg = rb_reg_prepare_re0(re, str, err);
+ tmpreg = reg != RREGEXP_PTR(re);
+ if (!tmpreg) RREGEXP(re)->usecnt++;
+
+ match = rb_backref_get();
+ if (!NIL_P(match)) {
+ if (FL_TEST(match, MATCH_BUSY)) {
+ match = Qnil;
+ }
+ else {
+ regs = RMATCH_REGS(match);
+ }
+ }
+ if (NIL_P(match)) {
+ MEMZERO(regs, struct re_registers, 1);
+ }
+ result = onig_match(reg,
+ (UChar*)(RSTRING_PTR(str)),
+ ((UChar*)(RSTRING_PTR(str)) + RSTRING_LEN(str)),
+ (UChar*)(RSTRING_PTR(str)),
+ 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 (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 (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();
+ }
+ else {
+ FL_UNSET(match, FL_TAINT);
+ }
+
+ RMATCH(match)->str = rb_str_new4(str);
+ OBJ_INFECT(match, str);
+
+ RMATCH(match)->regexp = re;
+ RMATCH(match)->rmatch->char_offset_updated = 0;
+ rb_backref_set(match);
+
+ OBJ_INFECT(match, re);
+
+ return true;
+}
+
VALUE
rb_reg_nth_defined(int nth, VALUE match)
{
@@ -1818,8 +1936,9 @@ match_captures(VALUE match)
static int
name_to_backref_number(struct re_registers *regs, VALUE regexp, const char* name, const char* name_end)
{
+ 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);
}
NORETURN(static void name_to_backref_error(VALUE name));
@@ -1830,26 +1949,28 @@ name_to_backref_error(VALUE name)
name);
}
+#define NAME_TO_NUMBER(regs, re, name, name_ptr, name_end) \
+ (NIL_P(re) ? 0 : \
+ !rb_enc_compatible(RREGEXP_SRC(re), (name)) ? 0 : \
+ name_to_backref_number((regs), (re), (name_ptr), (name_end)))
+
static int
namev_to_backref_number(struct re_registers *regs, VALUE re, VALUE name)
{
int num;
- switch (TYPE(name)) {
- case T_SYMBOL:
+ if (SYMBOL_P(name)) {
name = rb_sym2str(name);
- /* fall through */
- case T_STRING:
- if (NIL_P(re) || !rb_enc_compatible(RREGEXP_SRC(re), name) ||
- (num = name_to_backref_number(regs, re,
- RSTRING_PTR(name), RSTRING_END(name))) < 1) {
- name_to_backref_error(name);
- }
- return num;
-
- default:
+ }
+ else if (!RB_TYPE_P(name, T_STRING)) {
return -1;
}
+ num = NAME_TO_NUMBER(regs, re, name,
+ RSTRING_PTR(name), RSTRING_END(name));
+ if (num < 1) {
+ name_to_backref_error(name);
+ }
+ return num;
}
static VALUE
@@ -1961,7 +2082,7 @@ match_aref(int argc, VALUE *argv, VALUE match)
/*
* call-seq:
*
- * mtch.values_at([index]*) -> array
+ * mtch.values_at(index, ...) -> array
*
* Uses each <i>index</i> to access the matching values, returning an array of
* the corresponding matches.
@@ -2082,6 +2203,8 @@ match_named_captures(VALUE match)
struct MEMO *memo;
match_check(match);
+ if (NIL_P(RMATCH(match)->regexp))
+ return rb_hash_new();
hash = rb_hash_new();
memo = MEMO_NEW(hash, match, 0);
@@ -2311,7 +2434,8 @@ unescape_escaped_nonascii(const char **pp, const char *end, rb_encoding *enc,
{
const char *p = *pp;
int chmaxlen = rb_enc_mbmaxlen(enc);
- char *chbuf = ALLOCA_N(char, chmaxlen);
+ unsigned char *area = ALLOCA_N(unsigned char, chmaxlen);
+ char *chbuf = (char *)area;
int chlen = 0;
int byte;
int l;
@@ -2323,14 +2447,14 @@ unescape_escaped_nonascii(const char **pp, const char *end, rb_encoding *enc,
return -1;
}
- chbuf[chlen++] = byte;
+ area[chlen++] = byte;
while (chlen < chmaxlen &&
MBCLEN_NEEDMORE_P(rb_enc_precise_mbclen(chbuf, chbuf+chlen, enc))) {
byte = read_escaped_byte(&p, end, err);
if (byte == -1) {
return -1;
}
- chbuf[chlen++] = byte;
+ area[chlen++] = byte;
}
l = rb_enc_precise_mbclen(chbuf, chbuf+chlen, enc);
@@ -2338,7 +2462,7 @@ unescape_escaped_nonascii(const char **pp, const char *end, rb_encoding *enc,
errcpy(err, "invalid multibyte escape");
return -1;
}
- if (1 < chlen || (chbuf[0] & 0x80)) {
+ if (1 < chlen || (area[0] & 0x80)) {
rb_str_buf_cat(buf, chbuf, chlen);
if (*encp == 0)
@@ -2350,7 +2474,7 @@ unescape_escaped_nonascii(const char **pp, const char *end, rb_encoding *enc,
}
else {
char escbuf[5];
- snprintf(escbuf, sizeof(escbuf), "\\x%02X", chbuf[0]&0xff);
+ snprintf(escbuf, sizeof(escbuf), "\\x%02X", area[0]&0xff);
rb_str_buf_cat(buf, escbuf, 4);
}
*pp = p;
@@ -2460,17 +2584,19 @@ unescape_nonascii(const char *p, const char *end, rb_encoding *enc,
VALUE buf, rb_encoding **encp, int *has_property,
onig_errmsg_buffer err)
{
- char c;
+ unsigned char c;
char smallbuf[2];
while (p < end) {
int chlen = rb_enc_precise_mbclen(p, end, enc);
if (!MBCLEN_CHARFOUND_P(chlen)) {
+ invalid_multibyte:
errcpy(err, "invalid multibyte character");
return -1;
}
chlen = MBCLEN_CHARFOUND_LEN(chlen);
if (1 < chlen || (*p & 0x80)) {
+ multibyte:
rb_str_buf_cat(buf, p, chlen);
p += chlen;
if (*encp == 0)
@@ -2488,6 +2614,16 @@ unescape_nonascii(const char *p, const char *end, rb_encoding *enc,
errcpy(err, "too short escape sequence");
return -1;
}
+ chlen = rb_enc_precise_mbclen(p, end, enc);
+ if (!MBCLEN_CHARFOUND_P(chlen)) {
+ goto invalid_multibyte;
+ }
+ if ((chlen = MBCLEN_CHARFOUND_LEN(chlen)) > 1) {
+ /* include the previous backslash */
+ --p;
+ ++chlen;
+ goto multibyte;
+ }
switch (c = *p++) {
case '1': case '2': case '3':
case '4': case '5': case '6': case '7': /* \O, \OO, \OOO or backref */
@@ -2511,8 +2647,9 @@ unescape_nonascii(const char *p, const char *end, rb_encoding *enc,
p = p-2;
if (enc == rb_usascii_encoding()) {
const char *pbeg = p;
- c = read_escaped_byte(&p, end, err);
- if (c == (char)-1) return -1;
+ int byte = read_escaped_byte(&p, end, err);
+ if (byte == -1) return -1;
+ c = byte;
rb_str_buf_cat(buf, pbeg, p-pbeg);
}
else {
@@ -2561,7 +2698,7 @@ escape_asis:
break;
default:
- rb_str_buf_cat(buf, &c, 1);
+ rb_str_buf_cat(buf, (char *)&c, 1);
break;
}
}
@@ -2820,7 +2957,7 @@ rb_reg_init_str_enc(VALUE re, VALUE s, rb_encoding *enc, int options)
return re;
}
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_reg_new_ary(VALUE ary, int opt)
{
return rb_reg_new_str(rb_reg_preprocess_dregexp(ary, opt), opt);
@@ -2948,9 +3085,11 @@ static VALUE
match_hash(VALUE match)
{
const struct re_registers *regs;
- st_index_t hashval = rb_hash_start(rb_str_hash(RMATCH(match)->str));
+ st_index_t hashval;
- rb_hash_uint(hashval, reg_hash(RMATCH(match)->regexp));
+ match_check(match);
+ hashval = rb_hash_start(rb_str_hash(RMATCH(match)->str));
+ hashval = rb_hash_uint(hashval, reg_hash(match_regexp(match)));
regs = RMATCH_REGS(match);
hashval = rb_hash_uint(hashval, regs->num_regs);
hashval = rb_hash_uint(hashval, rb_memhash(regs->beg, regs->num_regs * sizeof(*regs->beg)));
@@ -2972,10 +3111,12 @@ static VALUE
match_equal(VALUE match1, VALUE match2)
{
const struct re_registers *regs1, *regs2;
+
if (match1 == match2) return Qtrue;
if (!RB_TYPE_P(match2, T_MATCH)) return Qfalse;
+ if (!RMATCH(match1)->regexp || !RMATCH(match2)->regexp) return Qfalse;
if (!rb_str_equal(RMATCH(match1)->str, RMATCH(match2)->str)) return Qfalse;
- if (!rb_reg_equal(RMATCH(match1)->regexp, RMATCH(match2)->regexp)) return Qfalse;
+ if (!rb_reg_equal(match_regexp(match1), match_regexp(match2))) return Qfalse;
regs1 = RMATCH_REGS(match1);
regs2 = RMATCH_REGS(match2);
if (regs1->num_regs != regs2->num_regs) return Qfalse;
@@ -2990,8 +3131,11 @@ reg_operand(VALUE s, int check)
if (SYMBOL_P(s)) {
return rb_sym2str(s);
}
+ else if (RB_TYPE_P(s, T_STRING)) {
+ return s;
+ }
else {
- return (check ? rb_str_to_str : rb_check_string_type)(s);
+ return check ? rb_str_to_str(s) : rb_check_string_type(s);
}
}
@@ -3083,9 +3227,9 @@ rb_reg_match(VALUE re, VALUE str)
*
* a = "HELLO"
* case a
- * when /^[a-z]*$/; print "Lower case\n"
- * when /^[A-Z]*$/; print "Upper case\n"
- * else; print "Mixed case\n"
+ * when /\A[a-z]*\z/; print "Lower case\n"
+ * when /\A[A-Z]*\z/; print "Upper case\n"
+ * else; print "Mixed case\n"
* end
* #=> "Upper case"
*
@@ -3222,26 +3366,29 @@ rb_reg_match_m(int argc, VALUE *argv, VALUE re)
static VALUE
rb_reg_match_m_p(int argc, VALUE *argv, VALUE re)
{
- VALUE str, initpos;
- long pos = 0;
+ long pos = rb_check_arity(argc, 1, 2) > 1 ? NUM2LONG(argv[1]) : 0;
+ return rb_reg_match_p(re, argv[0], pos);
+}
+
+VALUE
+rb_reg_match_p(VALUE re, VALUE str, long pos)
+{
regex_t *reg;
onig_errmsg_buffer err = "";
OnigPosition result;
const UChar *start, *end;
int tmpreg;
- rb_scan_args(argc, argv, "11", &str, &initpos);
if (NIL_P(str)) return Qfalse;
- str = SYMBOL_P(str) ? rb_sym2str(str) : rb_str_to_str(str);
- if (argc == 2) {
- pos = NUM2LONG(initpos);
+ 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 = 0;
- char *beg = rb_str_subpos(str, pos, &len);
+ long len = 1;
+ const char *beg = rb_str_subpos(str, pos, &len);
if (!beg) return Qfalse;
pos = beg - RSTRING_PTR(str);
}
@@ -3561,7 +3708,7 @@ rb_reg_s_union(VALUE self, VALUE args0)
else {
has_asciionly = 1;
}
- v = rb_reg_to_s(v);
+ v = rb_reg_str_with_term(v, -1);
}
else {
rb_encoding *enc;
@@ -3725,8 +3872,7 @@ rb_reg_regsub(VALUE str, VALUE src, struct re_registers *regs, VALUE regexp)
if (name_end < e) {
VALUE n = rb_str_subseq(str, (long)(name - RSTRING_PTR(str)),
(long)(name_end - name));
- if (!rb_enc_compatible(RREGEXP_SRC(regexp), n) ||
- (no = name_to_backref_number(regs, regexp, name, name_end)) < 1) {
+ if ((no = NAME_TO_NUMBER(regs, regexp, n, name, name_end)) < 1) {
name_to_backref_error(n);
}
p = s = name_end + clen;
@@ -3859,13 +4005,11 @@ match_setter(VALUE val)
static VALUE
rb_reg_s_last_match(int argc, VALUE *argv)
{
- VALUE nth;
-
- if (argc > 0 && rb_scan_args(argc, argv, "01", &nth) == 1) {
+ if (rb_check_arity(argc, 0, 1) == 1) {
VALUE match = rb_backref_get();
int n;
if (NIL_P(match)) return Qnil;
- n = match_backref_number(match, nth);
+ n = match_backref_number(match, argv[0]);
return rb_reg_nth_match(n, match);
}
return match_getter();
@@ -3905,7 +4049,6 @@ Init_Regexp(void)
{
rb_eRegexpError = rb_define_class("RegexpError", rb_eStandardError);
- onigenc_set_default_caseconv_table((UChar*)casetable);
onigenc_set_default_encoding(ONIG_ENCODING_ASCII);
onig_set_warn_func(re_warn);
onig_set_verb_warn_func(re_warn);
diff --git a/regcomp.c b/regcomp.c
index 4364beb2cf..df7f73bac5 100644
--- a/regcomp.c
+++ b/regcomp.c
@@ -3,7 +3,7 @@
**********************************************************************/
/*-
* Copyright (c) 2002-2013 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
- * Copyright (c) 2011-2014 K.Takata <kentkt AT csc DOT jp>
+ * Copyright (c) 2011-2016 K.Takata <kentkt AT csc DOT jp>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,15 +30,6 @@
#include "regparse.h"
-#if defined(USE_MULTI_THREAD_SYSTEM) \
- && defined(USE_DEFAULT_MULTI_THREAD_SYSTEM)
-#ifdef _WIN32
-CRITICAL_SECTION gOnigMutex;
-#else
-pthread_mutex_t gOnigMutex;
-#endif
-#endif
-
OnigCaseFoldType OnigDefaultCaseFoldFlag = ONIGENC_CASE_FOLD_MIN;
extern OnigCaseFoldType
@@ -263,6 +254,7 @@ add_mem_num(regex_t* reg, int num)
return 0;
}
+#if 0
static int
add_pointer(regex_t* reg, void* addr)
{
@@ -271,6 +263,7 @@ add_pointer(regex_t* reg, void* addr)
BBUF_ADD(reg, &ptr, SIZE_POINTER);
return 0;
}
+#endif
static int
add_option(regex_t* reg, OnigOptionType option)
@@ -591,11 +584,6 @@ compile_length_cclass_node(CClassNode* cc, regex_t* reg)
{
int len;
- if (IS_NCCLASS_SHARE(cc)) {
- len = SIZE_OPCODE + SIZE_POINTER;
- return len;
- }
-
if (IS_NULL(cc->mbuf)) {
len = SIZE_OPCODE + SIZE_BITSET;
}
@@ -621,12 +609,6 @@ compile_cclass_node(CClassNode* cc, regex_t* reg)
{
int r;
- if (IS_NCCLASS_SHARE(cc)) {
- add_opcode(reg, OP_CCLASS_NODE);
- r = add_pointer(reg, cc);
- return r;
- }
-
if (IS_NULL(cc->mbuf)) {
if (IS_NCCLASS_NOT(cc))
add_opcode(reg, OP_CCLASS_NOT);
@@ -638,17 +620,17 @@ compile_cclass_node(CClassNode* cc, regex_t* reg)
else {
if (ONIGENC_MBC_MINLEN(reg->enc) > 1 || bitset_is_empty(cc->bs)) {
if (IS_NCCLASS_NOT(cc))
- add_opcode(reg, OP_CCLASS_MB_NOT);
+ add_opcode(reg, OP_CCLASS_MB_NOT);
else
- add_opcode(reg, OP_CCLASS_MB);
+ add_opcode(reg, OP_CCLASS_MB);
r = add_multi_byte_cclass(cc->mbuf, reg);
}
else {
if (IS_NCCLASS_NOT(cc))
- add_opcode(reg, OP_CCLASS_MIX_NOT);
+ add_opcode(reg, OP_CCLASS_MIX_NOT);
else
- add_opcode(reg, OP_CCLASS_MIX);
+ add_opcode(reg, OP_CCLASS_MIX);
r = add_bitset(reg, cc->bs);
if (r) return r;
@@ -760,9 +742,9 @@ compile_length_quantifier_node(QtfrNode* qn, regex_t* reg)
if (NTYPE(qn->target) == NT_CANY) {
if (qn->greedy && infinite) {
if (IS_NOT_NULL(qn->next_head_exact) && !CKN_ON)
- return SIZE_OP_ANYCHAR_STAR_PEEK_NEXT + tlen * qn->lower + cklen;
+ return SIZE_OP_ANYCHAR_STAR_PEEK_NEXT + tlen * qn->lower + cklen;
else
- return SIZE_OP_ANYCHAR_STAR + tlen * qn->lower + cklen;
+ return SIZE_OP_ANYCHAR_STAR + tlen * qn->lower + cklen;
}
}
@@ -790,7 +772,7 @@ compile_length_quantifier_node(QtfrNode* qn, regex_t* reg)
}
}
else if (qn->upper == 0) {
- if (qn->is_refered != 0) /* /(?<n>..){0}/ */
+ if (qn->is_referred != 0) /* /(?<n>..){0}/ */
len = SIZE_OP_JUMP + tlen;
else
len = 0;
@@ -919,7 +901,7 @@ compile_quantifier_node(QtfrNode* qn, regex_t* reg)
}
}
else if (qn->upper == 0) {
- if (qn->is_refered != 0) { /* /(?<n>..){0}/ */
+ if (qn->is_referred != 0) { /* /(?<n>..){0}/ */
r = add_opcode_rel_addr(reg, OP_JUMP, tlen);
if (r) return r;
r = compile_tree(qn->target, reg);
@@ -989,9 +971,9 @@ compile_length_quantifier_node(QtfrNode* qn, regex_t* reg)
if (NTYPE(qn->target) == NT_CANY) {
if (qn->greedy && infinite) {
if (IS_NOT_NULL(qn->next_head_exact))
- return SIZE_OP_ANYCHAR_STAR_PEEK_NEXT + tlen * qn->lower;
+ return SIZE_OP_ANYCHAR_STAR_PEEK_NEXT + tlen * qn->lower;
else
- return SIZE_OP_ANYCHAR_STAR + tlen * qn->lower;
+ return SIZE_OP_ANYCHAR_STAR + tlen * qn->lower;
}
}
@@ -1010,9 +992,12 @@ compile_length_quantifier_node(QtfrNode* qn, regex_t* reg)
}
if (qn->greedy) {
+#ifdef USE_OP_PUSH_OR_JUMP_EXACT
if (IS_NOT_NULL(qn->head_exact))
len += SIZE_OP_PUSH_OR_JUMP_EXACT1 + mod_tlen + SIZE_OP_JUMP;
- else if (IS_NOT_NULL(qn->next_head_exact))
+ else
+#endif
+ if (IS_NOT_NULL(qn->next_head_exact))
len += SIZE_OP_PUSH_IF_PEEK_NEXT + mod_tlen + SIZE_OP_JUMP;
else
len += SIZE_OP_PUSH + mod_tlen + SIZE_OP_JUMP;
@@ -1020,7 +1005,7 @@ compile_length_quantifier_node(QtfrNode* qn, regex_t* reg)
else
len += SIZE_OP_JUMP + mod_tlen + SIZE_OP_PUSH;
}
- else if (qn->upper == 0 && qn->is_refered != 0) { /* /(?<n>..){0}/ */
+ else if (qn->upper == 0 && qn->is_referred != 0) { /* /(?<n>..){0}/ */
len = SIZE_OP_JUMP + tlen;
}
else if (!infinite && qn->greedy &&
@@ -1078,9 +1063,12 @@ compile_quantifier_node(QtfrNode* qn, regex_t* reg)
(qn->lower <= 1 || tlen * qn->lower <= QUANTIFIER_EXPAND_LIMIT_SIZE)) {
if (qn->lower == 1 && tlen > QUANTIFIER_EXPAND_LIMIT_SIZE) {
if (qn->greedy) {
+#ifdef USE_OP_PUSH_OR_JUMP_EXACT
if (IS_NOT_NULL(qn->head_exact))
r = add_opcode_rel_addr(reg, OP_JUMP, SIZE_OP_PUSH_OR_JUMP_EXACT1);
- else if (IS_NOT_NULL(qn->next_head_exact))
+ else
+#endif
+ if (IS_NOT_NULL(qn->next_head_exact))
r = add_opcode_rel_addr(reg, OP_JUMP, SIZE_OP_PUSH_IF_PEEK_NEXT);
else
r = add_opcode_rel_addr(reg, OP_JUMP, SIZE_OP_PUSH);
@@ -1096,6 +1084,7 @@ compile_quantifier_node(QtfrNode* qn, regex_t* reg)
}
if (qn->greedy) {
+#ifdef USE_OP_PUSH_OR_JUMP_EXACT
if (IS_NOT_NULL(qn->head_exact)) {
r = add_opcode_rel_addr(reg, OP_PUSH_OR_JUMP_EXACT1,
mod_tlen + SIZE_OP_JUMP);
@@ -1106,7 +1095,9 @@ compile_quantifier_node(QtfrNode* qn, regex_t* reg)
r = add_opcode_rel_addr(reg, OP_JUMP,
-(mod_tlen + (int )SIZE_OP_JUMP + (int )SIZE_OP_PUSH_OR_JUMP_EXACT1));
}
- else if (IS_NOT_NULL(qn->next_head_exact)) {
+ else
+#endif
+ if (IS_NOT_NULL(qn->next_head_exact)) {
r = add_opcode_rel_addr(reg, OP_PUSH_IF_PEEK_NEXT,
mod_tlen + SIZE_OP_JUMP);
if (r) return r;
@@ -1133,7 +1124,7 @@ compile_quantifier_node(QtfrNode* qn, regex_t* reg)
r = add_opcode_rel_addr(reg, OP_PUSH, -(mod_tlen + (int )SIZE_OP_PUSH));
}
}
- else if (qn->upper == 0 && qn->is_refered != 0) { /* /(?<n>..){0}/ */
+ else if (qn->upper == 0 && qn->is_referred != 0) { /* /(?<n>..){0}/ */
r = add_opcode_rel_addr(reg, OP_JUMP, tlen);
if (r) return r;
r = compile_tree(qn->target, reg);
@@ -1243,6 +1234,11 @@ compile_length_enclose_node(EncloseNode* node, regex_t* reg)
len += (IS_ENCLOSE_RECURSION(node)
? SIZE_OP_MEMORY_END_REC : SIZE_OP_MEMORY_END);
}
+ else if (IS_ENCLOSE_RECURSION(node)) {
+ len = SIZE_OP_MEMORY_START_PUSH;
+ len += tlen + (BIT_STATUS_AT(reg->bt_mem_end, node->regnum)
+ ? SIZE_OP_MEMORY_END_PUSH_REC : SIZE_OP_MEMORY_END_REC);
+ }
else
#endif
{
@@ -1290,6 +1286,10 @@ compile_length_enclose_node(EncloseNode* node, regex_t* reg)
}
break;
+ case ENCLOSE_ABSENT:
+ len = SIZE_OP_PUSH_ABSENT_POS + SIZE_OP_ABSENT + tlen + SIZE_OP_ABSENT_END;
+ break;
+
default:
return ONIGERR_TYPE_BUG;
break;
@@ -1354,6 +1354,14 @@ compile_enclose_node(EncloseNode* node, regex_t* reg)
if (r) return r;
r = add_opcode(reg, OP_RETURN);
}
+ else if (IS_ENCLOSE_RECURSION(node)) {
+ if (BIT_STATUS_AT(reg->bt_mem_end, node->regnum))
+ r = add_opcode(reg, OP_MEMORY_END_PUSH_REC);
+ else
+ r = add_opcode(reg, OP_MEMORY_END_REC);
+ if (r) return r;
+ r = add_mem_num(reg, node->regnum);
+ }
else
#endif
{
@@ -1426,6 +1434,19 @@ compile_enclose_node(EncloseNode* node, regex_t* reg)
}
break;
+ case ENCLOSE_ABSENT:
+ len = compile_length_tree(node->target, reg);
+ if (len < 0) return len;
+
+ r = add_opcode(reg, OP_PUSH_ABSENT_POS);
+ if (r) return r;
+ r = add_opcode_rel_addr(reg, OP_ABSENT, len + SIZE_OP_ABSENT_END);
+ if (r) return r;
+ r = compile_tree(node->target, reg);
+ if (r) return r;
+ r = add_opcode(reg, OP_ABSENT_END);
+ break;
+
default:
return ONIGERR_TYPE_BUG;
break;
@@ -1480,9 +1501,6 @@ compile_anchor_node(AnchorNode* node, regex_t* reg)
case ANCHOR_SEMI_END_BUF: r = add_opcode(reg, OP_SEMI_END_BUF); break;
case ANCHOR_BEGIN_POSITION: r = add_opcode(reg, OP_BEGIN_POSITION); break;
- /* used for implicit anchor optimization: /.*a/ ==> /(?:^|\G).*a/ */
- case ANCHOR_ANYCHAR_STAR: r = add_opcode(reg, OP_BEGIN_POS_OR_LINE); break;
-
case ANCHOR_WORD_BOUND:
if (node->ascii_range) r = add_opcode(reg, OP_ASCII_WORD_BOUND);
else r = add_opcode(reg, OP_WORD_BOUND);
@@ -1589,10 +1607,10 @@ compile_length_tree(Node* node, regex_t* reg)
int n = 0;
len = 0;
do {
- r = compile_length_tree(NCAR(node), reg);
- if (r < 0) return r;
- len += r;
- n++;
+ r = compile_length_tree(NCAR(node), reg);
+ if (r < 0) return r;
+ len += r;
+ n++;
} while (IS_NOT_NULL(node = NCDR(node)));
r = len;
r += (SIZE_OP_PUSH + SIZE_OP_JUMP) * (n - 1);
@@ -1621,7 +1639,7 @@ compile_length_tree(Node* node, regex_t* reg)
#ifdef USE_BACKREF_WITH_LEVEL
if (IS_BACKREF_NEST_LEVEL(br)) {
- r = SIZE_OPCODE + SIZE_OPTION + SIZE_LENGTH +
+ r = SIZE_OPCODE + SIZE_OPTION + SIZE_LENGTH +
SIZE_LENGTH + (SIZE_MEMNUM * br->back_num);
}
else
@@ -1785,12 +1803,12 @@ compile_tree(Node* node, regex_t* reg)
int i;
int* p;
- if (IS_IGNORECASE(reg->options)) {
- r = add_opcode(reg, OP_BACKREF_MULTI_IC);
- }
- else {
- r = add_opcode(reg, OP_BACKREF_MULTI);
- }
+ if (IS_IGNORECASE(reg->options)) {
+ r = add_opcode(reg, OP_BACKREF_MULTI_IC);
+ }
+ else {
+ r = add_opcode(reg, OP_BACKREF_MULTI);
+ }
if (r) return r;
#ifdef USE_BACKREF_WITH_LEVEL
@@ -1884,17 +1902,8 @@ noname_disable_map(Node** plink, GroupNumRemap* map, int* counter)
break;
case NT_ANCHOR:
- {
- AnchorNode* an = NANCHOR(node);
- switch (an->type) {
- case ANCHOR_PREC_READ:
- case ANCHOR_PREC_READ_NOT:
- case ANCHOR_LOOK_BEHIND:
- case ANCHOR_LOOK_BEHIND_NOT:
- r = noname_disable_map(&(an->target), map, counter);
- break;
- }
- }
+ if (NANCHOR(node)->target)
+ r = noname_disable_map(&(NANCHOR(node)->target), map, counter);
break;
default:
@@ -1951,7 +1960,7 @@ renumber_by_map(Node* node, GroupNumRemap* map)
{
EncloseNode* en = NENCLOSE(node);
if (en->type == ENCLOSE_CONDITION)
- en->regnum = map[en->regnum].new_val;
+ en->regnum = map[en->regnum].new_val;
r = renumber_by_map(en->target, map);
}
break;
@@ -1961,17 +1970,8 @@ renumber_by_map(Node* node, GroupNumRemap* map)
break;
case NT_ANCHOR:
- {
- AnchorNode* an = NANCHOR(node);
- switch (an->type) {
- case ANCHOR_PREC_READ:
- case ANCHOR_PREC_READ_NOT:
- case ANCHOR_LOOK_BEHIND:
- case ANCHOR_LOOK_BEHIND_NOT:
- r = renumber_by_map(an->target, map);
- break;
- }
- }
+ if (NANCHOR(node)->target)
+ r = renumber_by_map(NANCHOR(node)->target, map);
break;
default:
@@ -2005,6 +2005,11 @@ numbered_ref_check(Node* node)
return ONIGERR_NUMBERED_BACKREF_OR_CALL_NOT_ALLOWED;
break;
+ case NT_ANCHOR:
+ if (NANCHOR(node)->target)
+ r = numbered_ref_check(NANCHOR(node)->target);
+ break;
+
default:
break;
}
@@ -2091,7 +2096,7 @@ quantifiers_memory_node_info(Node* node)
}
break;
-#ifdef USE_SUBEXP_CALL
+# ifdef USE_SUBEXP_CALL
case NT_CALL:
if (IS_CALL_RECURSION(NCALL(node))) {
return NQ_TARGET_IS_EMPTY_REC; /* tiny version */
@@ -2099,7 +2104,7 @@ quantifiers_memory_node_info(Node* node)
else
r = quantifiers_memory_node_info(NCALL(node)->target);
break;
-#endif
+# endif
case NT_QTFR:
{
@@ -2121,6 +2126,7 @@ quantifiers_memory_node_info(Node* node)
case ENCLOSE_OPTION:
case ENCLOSE_STOP_BACKTRACK:
case ENCLOSE_CONDITION:
+ case ENCLOSE_ABSENT:
r = quantifiers_memory_node_info(en->target);
break;
default:
@@ -2238,23 +2244,31 @@ get_min_match_length(Node* node, OnigDistance *min, ScanEnv* env)
EncloseNode* en = NENCLOSE(node);
switch (en->type) {
case ENCLOSE_MEMORY:
-#ifdef USE_SUBEXP_CALL
- if (IS_ENCLOSE_MIN_FIXED(en))
- *min = en->min_len;
- else {
- r = get_min_match_length(en->target, min, env);
- if (r == 0) {
- en->min_len = *min;
- SET_ENCLOSE_STATUS(node, NST_MIN_FIXED);
+ if (IS_ENCLOSE_MIN_FIXED(en))
+ *min = en->min_len;
+ else {
+ if (IS_ENCLOSE_MARK1(NENCLOSE(node)))
+ *min = 0; /* recursive */
+ else {
+ SET_ENCLOSE_STATUS(node, NST_MARK1);
+ r = get_min_match_length(en->target, min, env);
+ CLEAR_ENCLOSE_STATUS(node, NST_MARK1);
+ if (r == 0) {
+ en->min_len = *min;
+ SET_ENCLOSE_STATUS(node, NST_MIN_FIXED);
+ }
}
- }
- break;
-#endif
+ }
+ break;
+
case ENCLOSE_OPTION:
case ENCLOSE_STOP_BACKTRACK:
case ENCLOSE_CONDITION:
r = get_min_match_length(en->target, min, env);
break;
+
+ case ENCLOSE_ABSENT:
+ break;
}
}
break;
@@ -2356,23 +2370,31 @@ get_max_match_length(Node* node, OnigDistance *max, ScanEnv* env)
EncloseNode* en = NENCLOSE(node);
switch (en->type) {
case ENCLOSE_MEMORY:
-#ifdef USE_SUBEXP_CALL
if (IS_ENCLOSE_MAX_FIXED(en))
*max = en->max_len;
else {
- r = get_max_match_length(en->target, max, env);
- if (r == 0) {
- en->max_len = *max;
- SET_ENCLOSE_STATUS(node, NST_MAX_FIXED);
+ if (IS_ENCLOSE_MARK1(NENCLOSE(node)))
+ *max = ONIG_INFINITE_DISTANCE;
+ else {
+ SET_ENCLOSE_STATUS(node, NST_MARK1);
+ r = get_max_match_length(en->target, max, env);
+ CLEAR_ENCLOSE_STATUS(node, NST_MARK1);
+ if (r == 0) {
+ en->max_len = *max;
+ SET_ENCLOSE_STATUS(node, NST_MAX_FIXED);
+ }
}
}
break;
-#endif
+
case ENCLOSE_OPTION:
case ENCLOSE_STOP_BACKTRACK:
case ENCLOSE_CONDITION:
r = get_max_match_length(en->target, max, env);
break;
+
+ case ENCLOSE_ABSENT:
+ break;
}
}
break;
@@ -2496,6 +2518,7 @@ get_char_length_tree1(Node* node, regex_t* reg, int* len, int level)
case ENCLOSE_CONDITION:
r = get_char_length_tree1(en->target, reg, len, level);
break;
+ case ENCLOSE_ABSENT:
default:
break;
}
@@ -2622,10 +2645,10 @@ is_not_included(Node* x, Node* y, regex_t* reg)
for (i = 0; i < SINGLE_BYTE_SIZE; i++) {
v = BITSET_AT(xc->bs, i);
if ((v != 0 && !IS_NCCLASS_NOT(xc)) ||
- (v == 0 && IS_NCCLASS_NOT(xc))) {
+ (v == 0 && IS_NCCLASS_NOT(xc))) {
v = BITSET_AT(yc->bs, i);
if ((v != 0 && !IS_NCCLASS_NOT(yc)) ||
- (v == 0 && IS_NCCLASS_NOT(yc)))
+ (v == 0 && IS_NCCLASS_NOT(yc)))
return 0;
}
}
@@ -2675,24 +2698,24 @@ is_not_included(Node* x, Node* y, regex_t* reg)
break;
case NT_CCLASS:
- {
- CClassNode* cc = NCCLASS(y);
+ {
+ CClassNode* cc = NCCLASS(y);
- code = ONIGENC_MBC_TO_CODE(reg->enc, xs->s,
- xs->s + ONIGENC_MBC_MAXLEN(reg->enc));
- return (onig_is_code_in_cc(reg->enc, code, cc) != 0 ? 0 : 1);
- }
- break;
+ code = ONIGENC_MBC_TO_CODE(reg->enc, xs->s,
+ xs->s + ONIGENC_MBC_MAXLEN(reg->enc));
+ return (onig_is_code_in_cc(reg->enc, code, cc) != 0 ? 0 : 1);
+ }
+ break;
case NT_STR:
- {
- UChar *q;
- StrNode* ys = NSTR(y);
- len = NSTRING_LEN(x);
- if (len > NSTRING_LEN(y)) len = NSTRING_LEN(y);
- if (NSTRING_IS_AMBIG(x) || NSTRING_IS_AMBIG(y)) {
- /* tiny version */
- return 0;
+ {
+ UChar *q;
+ StrNode* ys = NSTR(y);
+ len = NSTRING_LEN(x);
+ if (len > NSTRING_LEN(y)) len = NSTRING_LEN(y);
+ if (NSTRING_IS_AMBIG(x) || NSTRING_IS_AMBIG(y)) {
+ /* tiny version */
+ return 0;
}
else {
for (i = 0, p = ys->s, q = xs->s; (OnigDistance )i < len; i++, p++, q++) {
@@ -2703,7 +2726,7 @@ is_not_included(Node* x, Node* y, regex_t* reg)
break;
default:
- break;
+ break;
}
}
break;
@@ -2760,9 +2783,11 @@ get_head_value_node(Node* node, int exact, regex_t* reg)
{
QtfrNode* qn = NQTFR(node);
if (qn->lower > 0) {
+#ifdef USE_OP_PUSH_OR_JUMP_EXACT
if (IS_NOT_NULL(qn->head_exact))
n = qn->head_exact;
else
+#endif
n = get_head_value_node(qn->target, exact, reg);
}
}
@@ -2787,6 +2812,9 @@ get_head_value_node(Node* node, int exact, regex_t* reg)
case ENCLOSE_CONDITION:
n = get_head_value_node(en->target, exact, reg);
break;
+
+ case ENCLOSE_ABSENT:
+ break;
}
}
break;
@@ -2854,8 +2882,8 @@ check_type_tree(Node* node, int type_mask, int enclose_mask, int anchor_mask)
#ifdef USE_SUBEXP_CALL
-#define RECURSION_EXIST 1
-#define RECURSION_INFINITE 2
+# define RECURSION_EXIST 1
+# define RECURSION_INFINITE 2
static int
subexp_inf_recursive_check(Node* node, ScanEnv* env, int head)
@@ -3055,7 +3083,7 @@ subexp_recursive_check(Node* node)
static int
subexp_recursive_check_trav(Node* node, ScanEnv* env)
{
-#define FOUND_CALLED_NODE 1
+# define FOUND_CALLED_NODE 1
int type;
int r = 0;
@@ -3078,7 +3106,7 @@ subexp_recursive_check_trav(Node* node, ScanEnv* env)
r = subexp_recursive_check_trav(NQTFR(node)->target, env);
if (NQTFR(node)->upper == 0) {
if (r == FOUND_CALLED_NODE)
- NQTFR(node)->is_refered = 1;
+ NQTFR(node)->is_referred = 1;
}
break;
@@ -3156,22 +3184,22 @@ setup_subexp_call(Node* node, ScanEnv* env)
if (cn->group_num != 0) {
int gnum = cn->group_num;
-#ifdef USE_NAMED_GROUP
+# ifdef USE_NAMED_GROUP
if (env->num_named > 0 &&
IS_SYNTAX_BV(env->syntax, ONIG_SYN_CAPTURE_ONLY_NAMED_GROUP) &&
!ONIG_IS_OPTION_ON(env->option, ONIG_OPTION_CAPTURE_GROUP)) {
return ONIGERR_NUMBERED_BACKREF_OR_CALL_NOT_ALLOWED;
}
-#endif
+# endif
if (gnum > env->num_mem) {
onig_scan_env_set_error_string(env,
ONIGERR_UNDEFINED_GROUP_REFERENCE, cn->name, cn->name_end);
return ONIGERR_UNDEFINED_GROUP_REFERENCE;
}
-#ifdef USE_NAMED_GROUP
+# ifdef USE_NAMED_GROUP
set_call_attr:
-#endif
+# endif
cn->target = nodes[cn->group_num];
if (IS_NULL(cn->target)) {
onig_scan_env_set_error_string(env,
@@ -3182,12 +3210,12 @@ setup_subexp_call(Node* node, ScanEnv* env)
BIT_STATUS_ON_AT(env->bt_mem_start, cn->group_num);
cn->unset_addr_list = env->unset_addr_list;
}
-#ifdef USE_NAMED_GROUP
-#ifdef USE_PERL_SUBEXP_CALL
+# ifdef USE_NAMED_GROUP
+# ifdef USE_PERL_SUBEXP_CALL
else if (cn->name == cn->name_end) {
goto set_call_attr;
}
-#endif
+# endif
else {
int *refs;
@@ -3209,7 +3237,7 @@ setup_subexp_call(Node* node, ScanEnv* env)
goto set_call_attr;
}
}
-#endif
+# endif
}
break;
@@ -3292,7 +3320,7 @@ setup_look_behind(Node* node, regex_t* reg, ScanEnv* env)
}
static int
-next_setup(Node* node, Node* next_node, int in_root, regex_t* reg)
+next_setup(Node* node, Node* next_node, regex_t* reg)
{
int type;
@@ -3326,32 +3354,10 @@ next_setup(Node* node, Node* next_node, int in_root, regex_t* reg)
}
}
}
-
-#ifndef ONIG_DONT_OPTIMIZE
- if (NTYPE(node) == NT_QTFR && /* the type may be changed by above block */
- in_root && /* qn->lower == 0 && */
- NTYPE(qn->target) == NT_CANY &&
- ! IS_MULTILINE(reg->options)) {
- /* implicit anchor: /.*a/ ==> /(?:^|\G).*a/ */
- Node *np;
- np = onig_node_new_list(NULL_NODE, NULL_NODE);
- CHECK_NULL_RETURN_MEMERR(np);
- swap_node(node, np);
- NCDR(node) = onig_node_new_list(np, NULL_NODE);
- if (IS_NULL(NCDR(node))) {
- onig_node_free(np);
- return ONIGERR_MEMORY;
- }
- np = onig_node_new_anchor(ANCHOR_ANYCHAR_STAR); /* (?:^|\G) */
- CHECK_NULL_RETURN_MEMERR(np);
- NCAR(node) = np;
- }
-#endif
}
}
else if (type == NT_ENCLOSE) {
EncloseNode* en = NENCLOSE(node);
- in_root = 0;
if (en->type == ENCLOSE_MEMORY) {
node = en->target;
goto retry;
@@ -3398,13 +3404,9 @@ update_string_node_case_fold(regex_t* reg, Node *node)
}
r = onig_node_str_set(node, sbuf, sp);
- if (r != 0) {
- xfree(sbuf);
- return r;
- }
xfree(sbuf);
- return 0;
+ return r;
}
static int
@@ -3512,29 +3514,29 @@ expand_case_fold_string_alt(int item_num, OnigCaseFoldCodeItem items[],
UChar *q = p + items[i].byte_len;
if (q < end) {
- r = expand_case_fold_make_rem_string(&rem, q, end, reg);
- if (r != 0) {
- onig_node_free(an);
- goto mem_err2;
- }
+ r = expand_case_fold_make_rem_string(&rem, q, end, reg);
+ if (r != 0) {
+ onig_node_free(an);
+ goto mem_err2;
+ }
- xnode = onig_node_list_add(NULL_NODE, snode);
- if (IS_NULL(xnode)) {
- onig_node_free(an);
- onig_node_free(rem);
- goto mem_err2;
- }
- if (IS_NULL(onig_node_list_add(xnode, rem))) {
- onig_node_free(an);
- onig_node_free(xnode);
- onig_node_free(rem);
- goto mem_err;
- }
+ xnode = onig_node_list_add(NULL_NODE, snode);
+ if (IS_NULL(xnode)) {
+ onig_node_free(an);
+ onig_node_free(rem);
+ goto mem_err2;
+ }
+ if (IS_NULL(onig_node_list_add(xnode, rem))) {
+ onig_node_free(an);
+ onig_node_free(xnode);
+ onig_node_free(rem);
+ goto mem_err;
+ }
- NCAR(an) = xnode;
+ NCAR(an) = xnode;
}
else {
- NCAR(an) = snode;
+ NCAR(an) = snode;
}
NCDR(var_anode) = an;
@@ -3594,6 +3596,7 @@ expand_case_fold_string(Node* node, regex_t* reg)
if (n == 0 || varlen == 0) {
if (IS_NULL(snode)) {
if (IS_NULL(root) && IS_NOT_NULL(prev_node)) {
+ onig_node_free(top_root);
top_root = root = onig_node_list_add(NULL_NODE, prev_node);
if (IS_NULL(root)) {
onig_node_free(prev_node);
@@ -3625,6 +3628,7 @@ expand_case_fold_string(Node* node, regex_t* reg)
}
}
if (IS_NULL(root) && IS_NOT_NULL(prev_node)) {
+ onig_node_free(top_root);
top_root = root = onig_node_list_add(NULL_NODE, prev_node);
if (IS_NULL(root)) {
onig_node_free(prev_node);
@@ -3675,6 +3679,7 @@ expand_case_fold_string(Node* node, regex_t* reg)
if (r != 0) goto mem_err;
if (IS_NOT_NULL(prev_node) && IS_NULL(root)) {
+ onig_node_free(top_root);
top_root = root = onig_node_list_add(NULL_NODE, prev_node);
if (IS_NULL(root)) {
onig_node_free(srem);
@@ -3711,12 +3716,12 @@ expand_case_fold_string(Node* node, regex_t* reg)
#ifdef USE_COMBINATION_EXPLOSION_CHECK
-#define CEC_THRES_NUM_BIG_REPEAT 512
-#define CEC_INFINITE_NUM 0x7fffffff
+# define CEC_THRES_NUM_BIG_REPEAT 512
+# define CEC_INFINITE_NUM 0x7fffffff
-#define CEC_IN_INFINITE_REPEAT (1<<0)
-#define CEC_IN_FINITE_REPEAT (1<<1)
-#define CEC_CONT_BIG_REPEAT (1<<2)
+# define CEC_IN_INFINITE_REPEAT (1<<0)
+# define CEC_IN_FINITE_REPEAT (1<<1)
+# define CEC_CONT_BIG_REPEAT (1<<2)
static int
setup_comb_exp_check(Node* node, int state, ScanEnv* env)
@@ -3832,14 +3837,14 @@ setup_comb_exp_check(Node* node, int state, ScanEnv* env)
}
break;
-#ifdef USE_SUBEXP_CALL
+# ifdef USE_SUBEXP_CALL
case NT_CALL:
if (IS_CALL_RECURSION(NCALL(node)))
env->has_recursion = 1;
else
r = setup_comb_exp_check(NCALL(node)->target, state, env);
break;
-#endif
+# endif
default:
break;
@@ -3853,7 +3858,8 @@ setup_comb_exp_check(Node* node, int state, ScanEnv* env)
#define IN_NOT (1<<1)
#define IN_REPEAT (1<<2)
#define IN_VAR_REPEAT (1<<3)
-#define IN_ROOT (1<<4)
+#define IN_CALL (1<<4)
+#define IN_RECCALL (1<<5)
/* setup_tree does the following work.
1. check empty loop. (set qn->target_empty_info)
@@ -3868,25 +3874,19 @@ setup_tree(Node* node, regex_t* reg, int state, ScanEnv* env)
{
int type;
int r = 0;
- int in_root = state & IN_ROOT;
- state &= ~IN_ROOT;
restart:
type = NTYPE(node);
switch (type) {
case NT_LIST:
{
Node* prev = NULL_NODE;
- int prev_in_root = 0;
- state |= in_root;
do {
r = setup_tree(NCAR(node), reg, state, env);
if (IS_NOT_NULL(prev) && r == 0) {
- r = next_setup(prev, NCAR(node), prev_in_root, reg);
+ r = next_setup(prev, NCAR(node), reg);
}
prev = NCAR(node);
- prev_in_root = state & IN_ROOT;
- state &= ~IN_ROOT;
} while (r == 0 && IS_NOT_NULL(node = NCDR(node)));
}
break;
@@ -3943,7 +3943,7 @@ restart:
Node* target = qn->target;
if ((state & IN_REPEAT) != 0) {
- qn->state |= NST_IN_REPEAT;
+ qn->state |= NST_IN_REPEAT;
}
if (IS_REPEAT_INFINITE(qn->upper) || qn->upper >= 1) {
@@ -4050,7 +4050,6 @@ restart:
case ENCLOSE_OPTION:
{
OnigOptionType options = reg->options;
- state |= in_root;
reg->options = NENCLOSE(node)->option;
r = setup_tree(NENCLOSE(node)->target, reg, state, env);
reg->options = options;
@@ -4058,12 +4057,18 @@ restart:
break;
case ENCLOSE_MEMORY:
- if ((state & (IN_ALT | IN_NOT | IN_VAR_REPEAT)) != 0) {
+ if ((state & (IN_ALT | IN_NOT | IN_VAR_REPEAT | IN_CALL)) != 0) {
BIT_STATUS_ON_AT(env->bt_mem_start, en->regnum);
/* SET_ENCLOSE_STATUS(node, NST_MEM_IN_ALT_NOT); */
}
- r = setup_tree(en->target, reg, state, env);
- break;
+ if (IS_ENCLOSE_CALLED(en))
+ state |= IN_CALL;
+ if (IS_ENCLOSE_RECURSION(en))
+ state |= IN_RECCALL;
+ else if ((state & IN_RECCALL) != 0)
+ SET_CALL_RECURSION(node);
+ r = setup_tree(en->target, reg, state, env);
+ break;
case ENCLOSE_STOP_BACKTRACK:
{
@@ -4090,6 +4095,12 @@ restart:
return ONIGERR_NUMBERED_BACKREF_OR_CALL_NOT_ALLOWED;
}
#endif
+ if (NENCLOSE(node)->regnum > env->num_mem)
+ return ONIGERR_INVALID_BACKREF;
+ r = setup_tree(NENCLOSE(node)->target, reg, state, env);
+ break;
+
+ case ENCLOSE_ABSENT:
r = setup_tree(NENCLOSE(node)->target, reg, state, env);
break;
}
@@ -4133,10 +4144,10 @@ restart:
ALLOWED_ENCLOSE_IN_LB, ALLOWED_ANCHOR_IN_LB);
if (r < 0) return r;
if (r > 0) return ONIGERR_INVALID_LOOK_BEHIND_PATTERN;
- r = setup_look_behind(node, reg, env);
- if (r != 0) return r;
if (NTYPE(node) != NT_ANCHOR) goto restart;
r = setup_tree(an->target, reg, state, env);
+ if (r != 0) return r;
+ r = setup_look_behind(node, reg, env);
}
break;
@@ -4146,10 +4157,10 @@ restart:
ALLOWED_ENCLOSE_IN_LB_NOT, ALLOWED_ANCHOR_IN_LB_NOT);
if (r < 0) return r;
if (r > 0) return ONIGERR_INVALID_LOOK_BEHIND_PATTERN;
- r = setup_look_behind(node, reg, env);
- if (r != 0) return r;
if (NTYPE(node) != NT_ANCHOR) goto restart;
r = setup_tree(an->target, reg, (state | IN_NOT), env);
+ if (r != 0) return r;
+ r = setup_look_behind(node, reg, env);
}
break;
}
@@ -4186,6 +4197,8 @@ set_bm_skip(UChar* s, UChar* end, regex_t* reg,
n = ONIGENC_GET_CASE_FOLD_CODES_BY_STR(enc, reg->case_fold_flag,
p, end, items);
clen = enclen(enc, p, end);
+ if (p + clen > end)
+ clen = (int )(end - p);
for (j = 0; j < n; j++) {
if ((items[j].code_len != 1) || (items[j].byte_len != clen))
@@ -4203,6 +4216,10 @@ set_bm_skip(UChar* s, UChar* end, regex_t* reg,
}
}
else {
+# if OPT_EXACT_MAXLEN < ONIG_CHAR_TABLE_SIZE
+ /* This should not happen. */
+ return ONIGERR_TYPE_BUG;
+# else
if (IS_NULL(*int_skip)) {
*int_skip = (int* )xmalloc(sizeof(int) * ONIG_CHAR_TABLE_SIZE);
if (IS_NULL(*int_skip)) return ONIGERR_MEMORY;
@@ -4216,6 +4233,8 @@ set_bm_skip(UChar* s, UChar* end, regex_t* reg,
n = ONIGENC_GET_CASE_FOLD_CODES_BY_STR(enc, reg->case_fold_flag,
p, end, items);
clen = enclen(enc, p, end);
+ if (p + clen > end)
+ clen = (int )(end - p);
for (j = 0; j < n; j++) {
if ((items[j].code_len != 1) || (items[j].byte_len != clen))
@@ -4231,6 +4250,7 @@ set_bm_skip(UChar* s, UChar* end, regex_t* reg,
}
}
}
+# endif
}
return 0;
}
@@ -4259,6 +4279,8 @@ set_bm_skip(UChar* s, UChar* end, regex_t* reg,
n = ONIGENC_GET_CASE_FOLD_CODES_BY_STR(enc, reg->case_fold_flag,
p, end, items);
clen = enclen(enc, p, end);
+ if (p + clen > end)
+ clen = (int )(end - p);
for (j = 0; j < n; j++) {
if ((items[j].code_len != 1) || (items[j].byte_len != clen))
@@ -4276,6 +4298,10 @@ set_bm_skip(UChar* s, UChar* end, regex_t* reg,
}
}
else {
+# if OPT_EXACT_MAXLEN < ONIG_CHAR_TABLE_SIZE
+ /* This should not happen. */
+ return ONIGERR_TYPE_BUG;
+# else
if (IS_NULL(*int_skip)) {
*int_skip = (int* )xmalloc(sizeof(int) * ONIG_CHAR_TABLE_SIZE);
if (IS_NULL(*int_skip)) return ONIGERR_MEMORY;
@@ -4289,6 +4315,8 @@ set_bm_skip(UChar* s, UChar* end, regex_t* reg,
n = ONIGENC_GET_CASE_FOLD_CODES_BY_STR(enc, reg->case_fold_flag,
p, end, items);
clen = enclen(enc, p, end);
+ if (p + clen > end)
+ clen = (int )(end - p);
for (j = 0; j < n; j++) {
if ((items[j].code_len != 1) || (items[j].byte_len != clen))
@@ -4304,13 +4332,12 @@ set_bm_skip(UChar* s, UChar* end, regex_t* reg,
}
}
}
+# endif
}
return 0;
}
#endif /* USE_SUNDAY_QUICK_SEARCH */
-#define OPT_EXACT_MAXLEN 24
-
typedef struct {
OnigDistance min; /* min byte length */
OnigDistance max; /* max byte length */
@@ -4980,14 +5007,14 @@ optimize_node_left(Node* node, NodeOptInfo* opt, OptEnv* env)
if (slen > 0) {
add_char_opt_map_info(&opt->map, *(sn->s), env->enc);
}
- set_mml(&opt->len, slen, slen);
+ set_mml(&opt->len, slen, slen);
}
else {
- OnigDistance max;
+ OnigDistance max;
if (NSTRING_IS_DONT_GET_OPT_INFO(node)) {
- int n = onigenc_strlen(env->enc, sn->s, sn->end);
- max = ONIGENC_MBC_MAXLEN_DIST(env->enc) * n;
+ int n = onigenc_strlen(env->enc, sn->s, sn->end);
+ max = ONIGENC_MBC_MAXLEN_DIST(env->enc) * n;
}
else {
concat_opt_exact_info_str(&opt->exb, sn->s, sn->end,
@@ -5003,7 +5030,7 @@ optimize_node_left(Node* node, NodeOptInfo* opt, OptEnv* env)
max = slen;
}
- set_mml(&opt->len, slen, max);
+ set_mml(&opt->len, slen, max);
}
if ((OnigDistance )opt->exb.len == slen)
@@ -5019,18 +5046,18 @@ optimize_node_left(Node* node, NodeOptInfo* opt, OptEnv* env)
/* no need to check ignore case. (set in setup_tree()) */
if (IS_NOT_NULL(cc->mbuf) || IS_NCCLASS_NOT(cc)) {
- OnigDistance min = ONIGENC_MBC_MINLEN(env->enc);
+ OnigDistance min = ONIGENC_MBC_MINLEN(env->enc);
OnigDistance max = ONIGENC_MBC_MAXLEN_DIST(env->enc);
set_mml(&opt->len, min, max);
}
else {
- for (i = 0; i < SINGLE_BYTE_SIZE; i++) {
- z = BITSET_AT(cc->bs, i);
- if ((z && !IS_NCCLASS_NOT(cc)) || (!z && IS_NCCLASS_NOT(cc))) {
- add_char_opt_map_info(&opt->map, (UChar )i, env->enc);
- }
- }
+ for (i = 0; i < SINGLE_BYTE_SIZE; i++) {
+ z = BITSET_AT(cc->bs, i);
+ if ((z && !IS_NCCLASS_NOT(cc)) || (!z && IS_NCCLASS_NOT(cc))) {
+ add_char_opt_map_info(&opt->map, (UChar )i, env->enc);
+ }
+ }
set_mml(&opt->len, 1, 1);
}
}
@@ -5044,7 +5071,7 @@ optimize_node_left(Node* node, NodeOptInfo* opt, OptEnv* env)
max = ONIGENC_MBC_MAXLEN_DIST(env->enc);
if (max == 1) {
- min = 1;
+ min = 1;
maxcode = NCTYPE(node)->ascii_range ? 0x80 : SINGLE_BYTE_SIZE;
switch (NCTYPE(node)->ctype) {
@@ -5067,7 +5094,7 @@ optimize_node_left(Node* node, NodeOptInfo* opt, OptEnv* env)
}
}
else {
- min = ONIGENC_MBC_MINLEN(env->enc);
+ min = ONIGENC_MBC_MINLEN(env->enc);
}
set_mml(&opt->len, min, max);
}
@@ -5186,7 +5213,7 @@ optimize_node_left(Node* node, NodeOptInfo* opt, OptEnv* env)
if (nopt.exb.len > 0) {
if (nopt.exb.reach_end) {
for (i = 2; i <= qn->lower &&
- ! is_full_opt_exact_info(&opt->exb); i++) {
+ ! is_full_opt_exact_info(&opt->exb); i++) {
concat_opt_exact_info(&opt->exb, &nopt.exb, env->enc);
}
if (i < qn->lower) {
@@ -5257,6 +5284,10 @@ optimize_node_left(Node* node, NodeOptInfo* opt, OptEnv* env)
case ENCLOSE_CONDITION:
r = optimize_node_left(en->target, opt, env);
break;
+
+ case ENCLOSE_ABSENT:
+ set_mml(&opt->len, 0, ONIG_INFINITE_DISTANCE);
+ break;
}
}
break;
@@ -5308,11 +5339,14 @@ set_optimize_exact_info(regex_t* reg, OptExactInfo* e)
else {
if (e->len >= 3 || (e->len >= 2 && allow_reverse)) {
r = set_bm_skip(reg->exact, reg->exact_end, reg,
- reg->map, &(reg->int_map), 0);
- if (r) return r;
-
- reg->optimize = (allow_reverse != 0
+ reg->map, &(reg->int_map), 0);
+ if (r == 0) {
+ reg->optimize = (allow_reverse != 0
? ONIG_OPTIMIZE_EXACT_BM : ONIG_OPTIMIZE_EXACT_BM_NOT_REV);
+ }
+ else {
+ reg->optimize = ONIG_OPTIMIZE_EXACT;
+ }
}
else {
reg->optimize = ONIG_OPTIMIZE_EXACT;
@@ -5378,6 +5412,9 @@ set_optimize_info_from_tree(Node* node, regex_t* reg, ScanEnv* scan_env)
ANCHOR_BEGIN_POSITION | ANCHOR_ANYCHAR_STAR | ANCHOR_ANYCHAR_STAR_ML |
ANCHOR_LOOK_BEHIND);
+ if ((opt.anc.left_anchor & (ANCHOR_LOOK_BEHIND | ANCHOR_PREC_READ_NOT)) != 0)
+ reg->anchor &= ~ANCHOR_ANYCHAR_STAR_ML;
+
reg->anchor |= opt.anc.right_anchor & (ANCHOR_END_BUF | ANCHOR_SEMI_END_BUF |
ANCHOR_PREC_READ_NOT);
@@ -5570,14 +5607,14 @@ print_optimize_info(FILE* f, regex_t* reg)
fputc('[', f);
for (i = 0; i < ONIG_CHAR_TABLE_SIZE; i++) {
if (reg->map[i] != 0) {
- if (c > 0) fputs(", ", f);
- c++;
- if (ONIGENC_MBC_MAXLEN(reg->enc) == 1 &&
- ONIGENC_IS_CODE_PRINT(reg->enc, (OnigCodePoint )i))
- fputc(i, f);
- else
- fprintf(f, "%d", i);
- }
+ if (c > 0) fputs(", ", f);
+ c++;
+ if (ONIGENC_MBC_MAXLEN(reg->enc) == 1 &&
+ ONIGENC_IS_CODE_PRINT(reg->enc, (OnigCodePoint )i))
+ fputc(i, f);
+ else
+ fprintf(f, "%d", i);
+ }
}
fprintf(f, "]\n");
}
@@ -5612,6 +5649,7 @@ onig_free(regex_t* reg)
}
}
+#ifdef RUBY
size_t
onig_memsize(const regex_t *reg)
{
@@ -5635,65 +5673,47 @@ onig_region_memsize(const OnigRegion *regs)
size += regs->allocated * (sizeof(*regs->beg) + sizeof(*regs->end));
return size;
}
+#endif
#define REGEX_TRANSFER(to,from) do {\
- (to)->state = ONIG_STATE_MODIFY;\
onig_free_body(to);\
xmemcpy(to, from, sizeof(regex_t));\
xfree(from);\
} while (0)
+#if 0
extern void
onig_transfer(regex_t* to, regex_t* from)
{
- THREAD_ATOMIC_START;
REGEX_TRANSFER(to, from);
- THREAD_ATOMIC_END;
-}
-
-#define REGEX_CHAIN_HEAD(reg) do {\
- while (IS_NOT_NULL((reg)->chain)) {\
- (reg) = (reg)->chain;\
- }\
-} while (0)
-
-extern void
-onig_chain_link_add(regex_t* to, regex_t* add)
-{
- THREAD_ATOMIC_START;
- REGEX_CHAIN_HEAD(to);
- to->chain = add;
- THREAD_ATOMIC_END;
-}
-
-extern void
-onig_chain_reduce(regex_t* reg)
-{
- regex_t *head, *prev;
-
- prev = reg;
- head = prev->chain;
- if (IS_NOT_NULL(head)) {
- reg->state = ONIG_STATE_MODIFY;
- while (IS_NOT_NULL(head->chain)) {
- prev = head;
- head = head->chain;
- }
- prev->chain = (regex_t* )NULL;
- REGEX_TRANSFER(reg, head);
- }
}
+#endif
#ifdef ONIG_DEBUG_COMPILE
-static void print_compiled_byte_code_list P_((FILE* f, regex_t* reg));
+static void print_compiled_byte_code_list(FILE* f, regex_t* reg);
#endif
#ifdef ONIG_DEBUG_PARSE_TREE
-static void print_tree P_((FILE* f, Node* node));
+static void print_tree(FILE* f, Node* node);
#endif
+#ifdef RUBY
extern int
onig_compile(regex_t* reg, const UChar* pattern, const UChar* pattern_end,
+ OnigErrorInfo* einfo)
+{
+ return onig_compile_ruby(reg, pattern, pattern_end, einfo, NULL, 0);
+}
+#endif
+
+#ifdef RUBY
+extern int
+onig_compile_ruby(regex_t* reg, const UChar* pattern, const UChar* pattern_end,
OnigErrorInfo* einfo, const char *sourcefile, int sourceline)
+#else
+extern int
+onig_compile(regex_t* reg, const UChar* pattern, const UChar* pattern_end,
+ OnigErrorInfo* einfo)
+#endif
{
#define COMPILE_INIT_SIZE 20
@@ -5707,9 +5727,10 @@ onig_compile(regex_t* reg, const UChar* pattern, const UChar* pattern_end,
if (IS_NOT_NULL(einfo)) einfo->par = (UChar* )NULL;
+#ifdef RUBY
scan_env.sourcefile = sourcefile;
scan_env.sourceline = sourceline;
- reg->state = ONIG_STATE_COMPILING;
+#endif
#ifdef ONIG_DEBUG
print_enc_string(stderr, reg->enc, pattern, pattern_end);
@@ -5775,7 +5796,7 @@ onig_compile(regex_t* reg, const UChar* pattern, const UChar* pattern_end,
reg->num_call = 0;
#endif
- r = setup_tree(root, reg, IN_ROOT, &scan_env);
+ r = setup_tree(root, reg, 0, &scan_env);
if (r != 0) goto err_unset;
#ifdef ONIG_DEBUG_PARSE_TREE
@@ -5794,17 +5815,17 @@ onig_compile(regex_t* reg, const UChar* pattern, const UChar* pattern_end,
#ifdef USE_COMBINATION_EXPLOSION_CHECK
if (scan_env.backrefed_mem == 0
-#ifdef USE_SUBEXP_CALL
+# ifdef USE_SUBEXP_CALL
|| scan_env.num_call == 0
-#endif
+# endif
) {
setup_comb_exp_check(root, 0, &scan_env);
-#ifdef USE_SUBEXP_CALL
+# ifdef USE_SUBEXP_CALL
if (scan_env.has_recursion != 0) {
scan_env.num_comb_exp_check = 0;
}
else
-#endif
+# endif
if (scan_env.comb_exp_max_regnum > 0) {
int i;
for (i = 1; i <= scan_env.comb_exp_max_regnum; i++) {
@@ -5858,14 +5879,13 @@ onig_compile(regex_t* reg, const UChar* pattern, const UChar* pattern_end,
onig_node_free(root);
#ifdef ONIG_DEBUG_COMPILE
-#ifdef USE_NAMED_GROUP
+# ifdef USE_NAMED_GROUP
onig_print_names(stderr, reg);
-#endif
+# endif
print_compiled_byte_code_list(stderr, reg);
#endif
end:
- reg->state = ONIG_STATE_NORMAL;
return r;
err_unset:
@@ -5889,27 +5909,6 @@ onig_compile(regex_t* reg, const UChar* pattern, const UChar* pattern_end,
return r;
}
-#ifdef USE_RECOMPILE_API
-extern int
-onig_recompile(regex_t* reg, const UChar* pattern, const UChar* pattern_end,
- OnigOptionType option, OnigEncoding enc, OnigSyntaxType* syntax,
- OnigErrorInfo* einfo)
-{
- int r;
- regex_t *new_reg;
-
- r = onig_new(&new_reg, pattern, pattern_end, option, enc, syntax, einfo);
- if (r) return r;
- if (ONIG_STATE(reg) == ONIG_STATE_NORMAL) {
- onig_transfer(reg, new_reg);
- }
- else {
- onig_chain_link_add(reg, new_reg);
- }
- return 0;
-}
-#endif
-
static int onig_inited = 0;
extern int
@@ -5931,8 +5930,6 @@ onig_reg_init(regex_t* reg, OnigOptionType option,
return ONIGERR_INVALID_COMBINATION_OF_OPTIONS;
}
- (reg)->state = ONIG_STATE_MODIFY;
-
if ((option & ONIG_OPTION_NEGATE_SINGLELINE) != 0) {
option |= syntax->options;
option &= ~ONIG_OPTION_SINGLELINE;
@@ -5961,14 +5958,14 @@ onig_reg_init(regex_t* reg, OnigOptionType option,
extern int
onig_new_without_alloc(regex_t* reg, const UChar* pattern,
const UChar* pattern_end, OnigOptionType option, OnigEncoding enc,
- OnigSyntaxType* syntax, OnigErrorInfo* einfo)
+ const OnigSyntaxType* syntax, OnigErrorInfo* einfo)
{
int r;
r = onig_reg_init(reg, option, ONIGENC_CASE_FOLD_DEFAULT, enc, syntax);
if (r) return r;
- r = onig_compile(reg, pattern, pattern_end, einfo, NULL, 0);
+ r = onig_compile(reg, pattern, pattern_end, einfo);
return r;
}
@@ -5985,7 +5982,7 @@ onig_new(regex_t** reg, const UChar* pattern, const UChar* pattern_end,
r = onig_reg_init(*reg, option, ONIGENC_CASE_FOLD_DEFAULT, enc, syntax);
if (r) goto err;
- r = onig_compile(*reg, pattern, pattern_end, einfo, NULL, 0);
+ r = onig_compile(*reg, pattern, pattern_end, einfo);
if (r) {
err:
onig_free(*reg);
@@ -5994,6 +5991,11 @@ onig_new(regex_t** reg, const UChar* pattern, const UChar* pattern_end,
return r;
}
+extern int
+onig_initialize(OnigEncoding encodings[] ARG_UNUSED, int n ARG_UNUSED)
+{
+ return onig_init();
+}
extern int
onig_init(void)
@@ -6001,11 +6003,12 @@ onig_init(void)
if (onig_inited != 0)
return 0;
- THREAD_SYSTEM_INIT;
- THREAD_ATOMIC_START;
-
onig_inited = 1;
+#if defined(ONIG_DEBUG_MEMLEAK) && defined(_MSC_VER)
+ _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
+#endif
+
onigenc_init();
/* onigenc_set_default_caseconv_table((UChar* )0); */
@@ -6013,7 +6016,6 @@ onig_init(void)
onig_statistics_init();
#endif
- THREAD_ATOMIC_END;
return 0;
}
@@ -6052,26 +6054,18 @@ exec_end_call_list(void)
extern int
onig_end(void)
{
- THREAD_ATOMIC_START;
-
exec_end_call_list();
#ifdef ONIG_DEBUG_STATISTICS
onig_print_statistics(stderr);
#endif
-#ifdef USE_SHARED_CCLASS_TABLE
- onig_free_shared_cclass_table();
-#endif
-
-#ifdef USE_PARSE_TREE_NODE_RECYCLE
- onig_free_node_list();
+#if defined(ONIG_DEBUG_MEMLEAK) && defined(_MSC_VER)
+ _CrtDumpMemoryLeaks();
#endif
onig_inited = 0;
- THREAD_ATOMIC_END;
- THREAD_SYSTEM_END;
return 0;
}
@@ -6137,14 +6131,14 @@ onig_is_code_in_cc(OnigEncoding enc, OnigCodePoint code, CClassNode* cc)
#ifdef ONIG_DEBUG
/* arguments type */
-#define ARG_SPECIAL -1
-#define ARG_NON 0
-#define ARG_RELADDR 1
-#define ARG_ABSADDR 2
-#define ARG_LENGTH 3
-#define ARG_MEMNUM 4
-#define ARG_OPTION 5
-#define ARG_STATE_CHECK 6
+# define ARG_SPECIAL -1
+# define ARG_NON 0
+# define ARG_RELADDR 1
+# define ARG_ABSADDR 2
+# define ARG_LENGTH 3
+# define ARG_MEMNUM 4
+# define ARG_OPTION 5
+# define ARG_STATE_CHECK 6
OnigOpInfoType OnigOpInfo[] = {
{ OP_FINISH, "finish", ARG_NON },
@@ -6169,7 +6163,6 @@ OnigOpInfoType OnigOpInfo[] = {
{ OP_CCLASS_NOT, "cclass-not", ARG_SPECIAL },
{ OP_CCLASS_MB_NOT, "cclass-mb-not", ARG_SPECIAL },
{ OP_CCLASS_MIX_NOT, "cclass-mix-not", ARG_SPECIAL },
- { OP_CCLASS_NODE, "cclass-node", ARG_SPECIAL },
{ OP_ANYCHAR, "anychar", ARG_NON },
{ OP_ANYCHAR_ML, "anychar-ml", ARG_NON },
{ OP_ANYCHAR_STAR, "anychar*", ARG_NON },
@@ -6194,7 +6187,6 @@ OnigOpInfoType OnigOpInfo[] = {
{ OP_END_LINE, "end-line", ARG_NON },
{ OP_SEMI_END_BUF, "semi-end-buf", ARG_NON },
{ OP_BEGIN_POSITION, "begin-position", ARG_NON },
- { OP_BEGIN_POS_OR_LINE, "begin-pos-or-line", ARG_NON },
{ OP_BACKREF1, "backref1", ARG_NON },
{ OP_BACKREF2, "backref2", ARG_NON },
{ OP_BACKREFN, "backrefn", ARG_MEMNUM },
@@ -6236,6 +6228,9 @@ OnigOpInfoType OnigOpInfo[] = {
{ OP_LOOK_BEHIND, "look-behind", ARG_SPECIAL },
{ OP_PUSH_LOOK_BEHIND_NOT, "push-look-behind-not", ARG_SPECIAL },
{ OP_FAIL_LOOK_BEHIND_NOT, "fail-look-behind-not", ARG_NON },
+ { OP_PUSH_ABSENT_POS, "push-absent-pos", ARG_NON },
+ { OP_ABSENT, "absent", ARG_RELADDR },
+ { OP_ABSENT_END, "absent-end", ARG_NON },
{ OP_CALL, "call", ARG_ABSADDR },
{ OP_RETURN, "return", ARG_NON },
{ OP_CONDITION, "condition", ARG_SPECIAL },
@@ -6272,14 +6267,14 @@ op2arg_type(int opcode)
return ARG_SPECIAL;
}
-#ifdef ONIG_DEBUG_PARSE_TREE
+# ifdef ONIG_DEBUG_PARSE_TREE
static void
Indent(FILE* f, int indent)
{
int i;
for (i = 0; i < indent; i++) putc(' ', f);
}
-#endif /* ONIG_DEBUG_PARSE_TREE */
+# endif /* ONIG_DEBUG_PARSE_TREE */
static void
p_string(FILE* f, ptrdiff_t len, UChar* s)
@@ -6318,7 +6313,7 @@ onig_print_compiled_byte_code(FILE* f, UChar* bp, UChar* bpend, UChar** nextp,
break;
case ARG_RELADDR:
GET_RELADDR_INC(addr, bp);
- fprintf(f, ":(+%d)", addr);
+ fprintf(f, ":(%s%d)", (addr >= 0) ? "+" : "", addr);
break;
case ARG_ABSADDR:
GET_ABSADDR_INC(addr, bp);
@@ -6423,9 +6418,9 @@ onig_print_compiled_byte_code(FILE* f, UChar* bp, UChar* bpend, UChar** nextp,
case OP_CCLASS_MB_NOT:
GET_LENGTH_INC(len, bp);
q = bp;
-#ifndef PLATFORM_UNALIGNED_WORD_ACCESS
+# ifndef PLATFORM_UNALIGNED_WORD_ACCESS
ALIGNMENT_RIGHT(q);
-#endif
+# endif
GET_CODE_POINT(code, q);
bp += len;
fprintf(f, ":%d:%d", (int )code, len);
@@ -6437,24 +6432,14 @@ onig_print_compiled_byte_code(FILE* f, UChar* bp, UChar* bpend, UChar** nextp,
bp += SIZE_BITSET;
GET_LENGTH_INC(len, bp);
q = bp;
-#ifndef PLATFORM_UNALIGNED_WORD_ACCESS
+# ifndef PLATFORM_UNALIGNED_WORD_ACCESS
ALIGNMENT_RIGHT(q);
-#endif
+# endif
GET_CODE_POINT(code, q);
bp += len;
fprintf(f, ":%d:%d:%d", n, (int )code, len);
break;
- case OP_CCLASS_NODE:
- {
- CClassNode *cc;
-
- GET_POINTER_INC(cc, bp);
- n = bitset_on_num(cc->bs);
- fprintf(f, ":%"PRIuPTR":%d", (uintptr_t )cc, n);
- }
- break;
-
case OP_BACKREFN_IC:
mem = *((MemNumType* )bp);
bp += SIZE_MEMNUM;
@@ -6507,7 +6492,7 @@ onig_print_compiled_byte_code(FILE* f, UChar* bp, UChar* bpend, UChar** nextp,
case OP_PUSH_IF_PEEK_NEXT:
addr = *((RelAddrType* )bp);
bp += SIZE_RELADDR;
- fprintf(f, ":(%d)", addr);
+ fprintf(f, ":(%s%d)", (addr >= 0) ? "+" : "", addr);
p_string(f, 1, bp);
bp += 1;
break;
@@ -6520,7 +6505,7 @@ onig_print_compiled_byte_code(FILE* f, UChar* bp, UChar* bpend, UChar** nextp,
case OP_PUSH_LOOK_BEHIND_NOT:
GET_RELADDR_INC(addr, bp);
GET_LENGTH_INC(len, bp);
- fprintf(f, ":%d:(%d)", len, addr);
+ fprintf(f, ":%d:(%s%d)", len, (addr >= 0) ? "+" : "", addr);
break;
case OP_STATE_CHECK_PUSH:
@@ -6529,25 +6514,25 @@ onig_print_compiled_byte_code(FILE* f, UChar* bp, UChar* bpend, UChar** nextp,
bp += SIZE_STATE_CHECK_NUM;
addr = *((RelAddrType* )bp);
bp += SIZE_RELADDR;
- fprintf(f, ":%d:(%d)", scn, addr);
+ fprintf(f, ":%d:(%s%d)", scn, (addr >= 0) ? "+" : "", addr);
break;
case OP_CONDITION:
GET_MEMNUM_INC(mem, bp);
GET_RELADDR_INC(addr, bp);
- fprintf(f, ":%d:(%d)", mem, addr);
+ fprintf(f, ":%d:(%s%d)", mem, (addr >= 0) ? "+" : "", addr);
break;
default:
fprintf(stderr, "onig_print_compiled_byte_code: undefined code %d\n",
- *--bp);
+ bp[-1]);
}
}
fputs("]", f);
if (nextp) *nextp = bp;
}
-#ifdef ONIG_DEBUG_COMPILE
+# ifdef ONIG_DEBUG_COMPILE
static void
print_compiled_byte_code_list(FILE* f, regex_t* reg)
{
@@ -6569,9 +6554,9 @@ print_compiled_byte_code_list(FILE* f, regex_t* reg)
fprintf(f, "\n");
}
-#endif /* ONIG_DEBUG_COMPILE */
+# endif /* ONIG_DEBUG_COMPILE */
-#ifdef ONIG_DEBUG_PARSE_TREE
+# ifdef ONIG_DEBUG_PARSE_TREE
static void
print_indent_tree(FILE* f, Node* node, int indent)
{
@@ -6618,12 +6603,15 @@ print_indent_tree(FILE* f, Node* node, int indent)
case NT_CCLASS:
fprintf(f, "<cclass:%"PRIxPTR">", (intptr_t )node);
- if (IS_NCCLASS_NOT(NCCLASS(node))) fputs(" not", f);
+ if (IS_NCCLASS_NOT(NCCLASS(node))) fputs("not ", f);
if (NCCLASS(node)->mbuf) {
BBuf* bbuf = NCCLASS(node)->mbuf;
- for (i = 0; i < (int )bbuf->used; i++) {
- if (i > 0) fprintf(f, ",");
- fprintf(f, "%0x", bbuf->p[i]);
+ OnigCodePoint* data = (OnigCodePoint* )bbuf->p;
+ OnigCodePoint* end = (OnigCodePoint* )(bbuf->p + bbuf->used);
+ fprintf(f, "%d", *data++);
+ for (; data < end; data+=2) {
+ fprintf(f, ",");
+ fprintf(f, "%04x-%04x", data[0], data[1]);
}
}
break;
@@ -6657,14 +6645,13 @@ print_indent_tree(FILE* f, Node* node, int indent)
case ANCHOR_END_LINE: fputs("end line", f); break;
case ANCHOR_SEMI_END_BUF: fputs("semi end buf", f); break;
case ANCHOR_BEGIN_POSITION: fputs("begin position", f); break;
- case ANCHOR_ANYCHAR_STAR: fputs("begin position/line", f); break;
case ANCHOR_WORD_BOUND: fputs("word bound", f); break;
case ANCHOR_NOT_WORD_BOUND: fputs("not word bound", f); break;
-#ifdef USE_WORD_BEGIN_END
+# ifdef USE_WORD_BEGIN_END
case ANCHOR_WORD_BEGIN: fputs("word begin", f); break;
case ANCHOR_WORD_END: fputs("word end", f); break;
-#endif
+# endif
case ANCHOR_PREC_READ: fputs("prec read", f); container_p = TRUE; break;
case ANCHOR_PREC_READ_NOT: fputs("prec read not", f); container_p = TRUE; break;
case ANCHOR_LOOK_BEHIND: fputs("look_behind", f); container_p = TRUE; break;
@@ -6690,7 +6677,7 @@ print_indent_tree(FILE* f, Node* node, int indent)
}
break;
-#ifdef USE_SUBEXP_CALL
+# ifdef USE_SUBEXP_CALL
case NT_CALL:
{
CallNode* cn = NCALL(node);
@@ -6698,7 +6685,7 @@ print_indent_tree(FILE* f, Node* node, int indent)
p_string(f, cn->name_end - cn->name, cn->name);
}
break;
-#endif
+# endif
case NT_QTFR:
fprintf(f, "<quantifier:%"PRIxPTR">{%d,%d}%s\n", (intptr_t )node,
@@ -6722,6 +6709,9 @@ print_indent_tree(FILE* f, Node* node, int indent)
case ENCLOSE_CONDITION:
fprintf(f, "condition:%d", NENCLOSE(node)->regnum);
break;
+ case ENCLOSE_ABSENT:
+ fprintf(f, "absent");
+ break;
default:
break;
@@ -6749,5 +6739,5 @@ print_tree(FILE* f, Node* node)
{
print_indent_tree(f, node, 0);
}
-#endif /* ONIG_DEBUG_PARSE_TREE */
+# endif /* ONIG_DEBUG_PARSE_TREE */
#endif /* ONIG_DEBUG */
diff --git a/regenc.c b/regenc.c
index 5cacbdfaa4..16d62fdf40 100644
--- a/regenc.c
+++ b/regenc.c
@@ -3,7 +3,7 @@
**********************************************************************/
/*-
* Copyright (c) 2002-2007 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
- * Copyright (c) 2011 K.Takata <kentkt AT csc DOT jp>
+ * Copyright (c) 2011-2016 K.Takata <kentkt AT csc DOT jp>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -54,11 +54,11 @@ onigenc_set_default_encoding(OnigEncoding enc)
extern int
onigenc_mbclen_approximate(const OnigUChar* p,const OnigUChar* e, OnigEncoding enc)
{
- int ret = ONIGENC_PRECISE_MBC_ENC_LEN(enc,p,e);
+ int ret = ONIGENC_PRECISE_MBC_ENC_LEN(enc, p, e);
if (ONIGENC_MBCLEN_CHARFOUND_P(ret))
return ONIGENC_MBCLEN_CHARFOUND_LEN(ret);
else if (ONIGENC_MBCLEN_NEEDMORE_P(ret))
- return (int)(e-p)+ONIGENC_MBCLEN_NEEDMORE_LEN(ret);
+ return (int )(e - p) + ONIGENC_MBCLEN_NEEDMORE_LEN(ret);
return 1;
}
@@ -364,12 +364,14 @@ const UChar OnigEncISO_8859_1_ToUpperCaseTable[256] = {
};
#endif
+#if 0
extern void
onigenc_set_default_caseconv_table(const UChar* table ARG_UNUSED)
{
/* nothing */
/* obsoleted. */
}
+#endif
extern UChar*
onigenc_get_left_adjust_char_head(OnigEncoding enc, const UChar* start, const UChar* s, const UChar* end)
@@ -631,8 +633,10 @@ onigenc_single_byte_code_to_mbclen(OnigCodePoint code ARG_UNUSED, OnigEncoding e
extern int
onigenc_single_byte_code_to_mbc(OnigCodePoint code, UChar *buf, OnigEncoding enc ARG_UNUSED)
{
+#ifdef RUBY
if (code > 0xff)
rb_raise(rb_eRangeError, "%u out of char range", code);
+#endif
*buf = (UChar )(code & 0xff);
return 1;
}
@@ -892,6 +896,7 @@ onigenc_with_ascii_strnicmp(OnigEncoding enc, const UChar* p, const UChar* end,
return 0;
}
+#if 0
/* Property management */
static int
resize_property_list(int new_size, const OnigCodePoint*** plist, int* psize)
@@ -944,68 +949,64 @@ onigenc_property_list_add_property(UChar* name, const OnigCodePoint* prop,
(hash_data_type )(*pnum + ONIGENC_MAX_STD_CTYPE));
return 0;
}
+#endif
extern int
-onigenc_property_list_init(int (*f)(void))
-{
- int r;
-
- THREAD_ATOMIC_START;
-
- r = f();
-
- THREAD_ATOMIC_END;
- return r;
-}
-
-extern int
-onigenc_ascii_only_case_map (OnigCaseFoldType* flagP, const OnigUChar** pp, const OnigUChar* end,
- OnigUChar* to, OnigUChar* to_end, const struct OnigEncodingTypeST* enc)
+onigenc_ascii_only_case_map(OnigCaseFoldType* flagP, const OnigUChar** pp, const OnigUChar* end,
+ OnigUChar* to, OnigUChar* to_end, const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
int codepoint_length;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
codepoint_length = ONIGENC_PRECISE_MBC_ENC_LEN(enc, *pp, end);
if (codepoint_length < 0)
return codepoint_length; /* encoding invalid */
code = ONIGENC_MBC_TO_CODE(enc, *pp, end);
*pp += codepoint_length;
- if (code>='a' && code<='z' && (flags&ONIGENC_CASE_UPCASE))
- flags |= ONIGENC_CASE_MODIFIED, code += 'A'-'a';
- else if (code>='A' && code<='Z' && (flags&(ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD)))
- flags |= ONIGENC_CASE_MODIFIED, code += 'a'-'A';
+ if (code >= 'a' && code <= 'z' && (flags & ONIGENC_CASE_UPCASE)) {
+ flags |= ONIGENC_CASE_MODIFIED;
+ code += 'A' - 'a';
+ } else if (code >= 'A' && code <= 'Z' &&
+ (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
+ flags |= ONIGENC_CASE_MODIFIED;
+ code += 'a' - 'A';
+ }
to += ONIGENC_CODE_TO_MBC(enc, code, to);
if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
extern int
-onigenc_single_byte_ascii_only_case_map (OnigCaseFoldType* flagP, const OnigUChar** pp,
- const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
- const struct OnigEncodingTypeST* enc)
+onigenc_single_byte_ascii_only_case_map(OnigCaseFoldType* flagP, const OnigUChar** pp,
+ const OnigUChar* end, OnigUChar* to, OnigUChar* to_end,
+ const struct OnigEncodingTypeST* enc)
{
OnigCodePoint code;
OnigUChar *to_start = to;
OnigCaseFoldType flags = *flagP;
- while (*pp<end && to<to_end) {
+ while (*pp < end && to < to_end) {
code = *(*pp)++;
- if (code>='a' && code<='z' && (flags&ONIGENC_CASE_UPCASE))
- flags |= ONIGENC_CASE_MODIFIED, code += 'A'-'a';
- else if (code>='A' && code<='Z' && (flags&(ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_FOLD)))
- flags |= ONIGENC_CASE_MODIFIED, code += 'a'-'A';
+ if (code >= 'a' && code <= 'z' && (flags & ONIGENC_CASE_UPCASE)) {
+ flags |= ONIGENC_CASE_MODIFIED;
+ code += 'A' - 'a';
+ } else if (code >= 'A' && code <= 'Z' &&
+ (flags & (ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_FOLD))) {
+ flags |= ONIGENC_CASE_MODIFIED;
+ code += 'a' - 'A';
+ }
*to++ = code;
if (flags & ONIGENC_CASE_TITLECASE) /* switch from titlecase to lowercase for capitalize */
- flags ^= (ONIGENC_CASE_UPCASE|ONIGENC_CASE_DOWNCASE|ONIGENC_CASE_TITLECASE);
+ flags ^= (ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE | ONIGENC_CASE_TITLECASE);
}
*flagP = flags;
- return (int)(to-to_start);
+ return (int )(to - to_start);
}
diff --git a/regenc.h b/regenc.h
index 2c4c9343c5..16ed6c39da 100644
--- a/regenc.h
+++ b/regenc.h
@@ -1,11 +1,11 @@
-#ifndef ONIGURUMA_REGENC_H
-#define ONIGURUMA_REGENC_H
+#ifndef ONIGMO_REGENC_H
+#define ONIGMO_REGENC_H
/**********************************************************************
regenc.h - Onigmo (Oniguruma-mod) (regular expression library)
**********************************************************************/
/*-
* Copyright (c) 2002-2008 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
- * Copyright (c) 2011 K.Takata <kentkt AT csc DOT jp>
+ * Copyright (c) 2011-2016 K.Takata <kentkt AT csc DOT jp>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,18 +30,32 @@
* SUCH DAMAGE.
*/
-#ifndef REGINT_H
-#ifndef RUBY_EXTERN
-#include "ruby/config.h"
-#include "ruby/defines.h"
-#endif
+#if !defined(RUBY) && (defined(RUBY_EXPORT) || defined(ONIG_ENC_REGISTER))
+# define RUBY
#endif
+#ifdef RUBY
+# ifndef ONIGMO_REGINT_H
+# ifndef RUBY_EXTERN
+# include "ruby/config.h"
+# include "ruby/defines.h"
+# endif
+# endif
+#else /* RUBY */
+# ifndef PACKAGE
+/* PACKAGE is defined in config.h */
+# include "config.h"
+# endif
+#endif /* RUBY */
#ifdef ONIG_ESCAPE_UCHAR_COLLISION
-#undef ONIG_ESCAPE_UCHAR_COLLISION
+# undef ONIG_ESCAPE_UCHAR_COLLISION
#endif
-#include "ruby/oniguruma.h"
+#ifdef RUBY
+# include "ruby/onigmo.h"
+#else
+# include "onigmo.h"
+#endif
RUBY_SYMBOL_EXPORT_BEGIN
@@ -52,23 +66,23 @@ typedef struct {
#ifndef NULL
-#define NULL ((void* )0)
+# define NULL ((void* )0)
#endif
#ifndef TRUE
-#define TRUE 1
+# define TRUE 1
#endif
#ifndef FALSE
-#define FALSE 0
+# define FALSE 0
#endif
#ifndef ARG_UNUSED
-#if defined(__GNUC__)
+# if defined(__GNUC__)
# define ARG_UNUSED __attribute__ ((unused))
-#else
+# else
# define ARG_UNUSED
-#endif
+# endif
#endif
#define ONIG_IS_NULL(p) (((void*)(p)) == (void*)0)
@@ -108,10 +122,10 @@ typedef struct {
} PosixBracketEntryType;
#define POSIX_BRACKET_ENTRY_INIT(name, ctype) \
- {(short int )(sizeof(name) - 1), (name), (ctype)}
+ {(short int )(sizeof(name) - 1), name, (ctype)}
#ifndef numberof
-#define numberof(array) (int )(sizeof(array) / sizeof((array)[0]))
+# define numberof(array) (int )(sizeof(array) / sizeof((array)[0]))
#endif
@@ -125,53 +139,56 @@ typedef struct {
#define ONIG_ENCODING_INIT_DEFAULT ONIG_ENCODING_ASCII
/* for encoding system implementation (internal) */
-ONIG_EXTERN int onigenc_ascii_apply_all_case_fold P_((OnigCaseFoldType flag, OnigApplyAllCaseFoldFunc f, void* arg, OnigEncoding enc));
-ONIG_EXTERN int onigenc_ascii_get_case_fold_codes_by_str P_((OnigCaseFoldType flag, const OnigUChar* p, const OnigUChar* end, OnigCaseFoldCodeItem items[], OnigEncoding enc));
-ONIG_EXTERN int onigenc_apply_all_case_fold_with_map P_((int map_size, const OnigPairCaseFoldCodes map[], int ess_tsett_flag, OnigCaseFoldType flag, OnigApplyAllCaseFoldFunc f, void* arg));
-ONIG_EXTERN int onigenc_get_case_fold_codes_by_str_with_map P_((int map_size, const OnigPairCaseFoldCodes map[], int ess_tsett_flag, OnigCaseFoldType flag, const OnigUChar* p, const OnigUChar* end, OnigCaseFoldCodeItem items[]));
-CONSTFUNC(ONIG_EXTERN int onigenc_not_support_get_ctype_code_range P_((OnigCtype ctype, OnigCodePoint* sb_out, const OnigCodePoint* ranges[], OnigEncoding enc)));
-PUREFUNC(ONIG_EXTERN int onigenc_is_mbc_newline_0x0a P_((const UChar* p, const UChar* end, OnigEncoding enc)));
-ONIG_EXTERN int onigenc_single_byte_ascii_only_case_map P_((OnigCaseFoldType* flagP, const OnigUChar** pp, const OnigUChar* end, OnigUChar* to, OnigUChar* to_end, const struct OnigEncodingTypeST* enc));
+ONIG_EXTERN int onigenc_ascii_apply_all_case_fold(OnigCaseFoldType flag, OnigApplyAllCaseFoldFunc f, void* arg, OnigEncoding enc);
+ONIG_EXTERN int onigenc_ascii_get_case_fold_codes_by_str(OnigCaseFoldType flag, const OnigUChar* p, const OnigUChar* end, OnigCaseFoldCodeItem items[], OnigEncoding enc);
+ONIG_EXTERN int onigenc_apply_all_case_fold_with_map(int map_size, const OnigPairCaseFoldCodes map[], int ess_tsett_flag, OnigCaseFoldType flag, OnigApplyAllCaseFoldFunc f, void* arg);
+ONIG_EXTERN int onigenc_get_case_fold_codes_by_str_with_map(int map_size, const OnigPairCaseFoldCodes map[], int ess_tsett_flag, OnigCaseFoldType flag, const OnigUChar* p, const OnigUChar* end, OnigCaseFoldCodeItem items[]);
+ONIG_EXTERN int onigenc_not_support_get_ctype_code_range(OnigCtype ctype, OnigCodePoint* sb_out, const OnigCodePoint* ranges[], OnigEncoding enc);
+ONIG_EXTERN int onigenc_is_mbc_newline_0x0a(const UChar* p, const UChar* end, OnigEncoding enc);
+ONIG_EXTERN int onigenc_single_byte_ascii_only_case_map(OnigCaseFoldType* flagP, const OnigUChar** pp, const OnigUChar* end, OnigUChar* to, OnigUChar* to_end, const struct OnigEncodingTypeST* enc);
/* methods for single byte encoding */
-ONIG_EXTERN int onigenc_ascii_mbc_case_fold P_((OnigCaseFoldType flag, const UChar** p, const UChar* end, UChar* lower, OnigEncoding enc));
-CONSTFUNC(ONIG_EXTERN int onigenc_single_byte_mbc_enc_len P_((const UChar* p, const UChar* e, OnigEncoding enc)));
-PUREFUNC(ONIG_EXTERN OnigCodePoint onigenc_single_byte_mbc_to_code P_((const UChar* p, const UChar* end, OnigEncoding enc)));
-CONSTFUNC(ONIG_EXTERN int onigenc_single_byte_code_to_mbclen P_((OnigCodePoint code, OnigEncoding enc)));
-ONIG_EXTERN int onigenc_single_byte_code_to_mbc P_((OnigCodePoint code, UChar *buf, OnigEncoding enc));
-CONSTFUNC(ONIG_EXTERN UChar* onigenc_single_byte_left_adjust_char_head P_((const UChar* start, const UChar* s, const OnigUChar* end, OnigEncoding enc)));
-CONSTFUNC(ONIG_EXTERN int onigenc_always_true_is_allowed_reverse_match P_((const UChar* s, const UChar* end, OnigEncoding enc)));
-CONSTFUNC(ONIG_EXTERN int onigenc_always_false_is_allowed_reverse_match P_((const UChar* s, const UChar* end, OnigEncoding enc)));
-CONSTFUNC(ONIG_EXTERN int onigenc_ascii_is_code_ctype P_((OnigCodePoint code, unsigned int ctype, OnigEncoding enc)));
+ONIG_EXTERN int onigenc_ascii_mbc_case_fold(OnigCaseFoldType flag, const UChar** p, const UChar* end, UChar* lower, OnigEncoding enc);
+ONIG_EXTERN int onigenc_single_byte_mbc_enc_len(const UChar* p, const UChar* e, OnigEncoding enc);
+ONIG_EXTERN OnigCodePoint onigenc_single_byte_mbc_to_code(const UChar* p, const UChar* end, OnigEncoding enc);
+ONIG_EXTERN int onigenc_single_byte_code_to_mbclen(OnigCodePoint code, OnigEncoding enc);
+ONIG_EXTERN int onigenc_single_byte_code_to_mbc(OnigCodePoint code, UChar *buf, OnigEncoding enc);
+ONIG_EXTERN UChar* onigenc_single_byte_left_adjust_char_head(const UChar* start, const UChar* s, const OnigUChar* end, OnigEncoding enc);
+ONIG_EXTERN int onigenc_always_true_is_allowed_reverse_match(const UChar* s, const UChar* end, OnigEncoding enc);
+ONIG_EXTERN int onigenc_always_false_is_allowed_reverse_match(const UChar* s, const UChar* end, OnigEncoding enc);
+ONIG_EXTERN int onigenc_ascii_is_code_ctype(OnigCodePoint code, unsigned int ctype, OnigEncoding enc);
/* methods for multi byte encoding */
-ONIG_EXTERN OnigCodePoint onigenc_mbn_mbc_to_code P_((OnigEncoding enc, const UChar* p, const UChar* end));
-ONIG_EXTERN int onigenc_mbn_mbc_case_fold P_((OnigEncoding enc, OnigCaseFoldType flag, const UChar** p, const UChar* end, UChar* lower));
-CONSTFUNC(ONIG_EXTERN int onigenc_mb2_code_to_mbclen P_((OnigCodePoint code, OnigEncoding enc)));
-ONIG_EXTERN int onigenc_mb2_code_to_mbc P_((OnigEncoding enc, OnigCodePoint code, UChar *buf));
-ONIG_EXTERN int onigenc_minimum_property_name_to_ctype P_((OnigEncoding enc, const UChar* p, const UChar* end));
-ONIG_EXTERN int onigenc_unicode_property_name_to_ctype P_((OnigEncoding enc, const UChar* p, const UChar* end));
-ONIG_EXTERN int onigenc_mb2_is_code_ctype P_((OnigEncoding enc, OnigCodePoint code, unsigned int ctype));
-CONSTFUNC(ONIG_EXTERN int onigenc_mb4_code_to_mbclen P_((OnigCodePoint code, OnigEncoding enc)));
-ONIG_EXTERN int onigenc_mb4_code_to_mbc P_((OnigEncoding enc, OnigCodePoint code, UChar *buf));
-ONIG_EXTERN int onigenc_mb4_is_code_ctype P_((OnigEncoding enc, OnigCodePoint code, unsigned int ctype));
+ONIG_EXTERN OnigCodePoint onigenc_mbn_mbc_to_code(OnigEncoding enc, const UChar* p, const UChar* end);
+ONIG_EXTERN int onigenc_mbn_mbc_case_fold(OnigEncoding enc, OnigCaseFoldType flag, const UChar** p, const UChar* end, UChar* lower);
+ONIG_EXTERN int onigenc_mb2_code_to_mbclen(OnigCodePoint code, OnigEncoding enc);
+ONIG_EXTERN int onigenc_mb2_code_to_mbc(OnigEncoding enc, OnigCodePoint code, UChar *buf);
+ONIG_EXTERN int onigenc_minimum_property_name_to_ctype(OnigEncoding enc, const UChar* p, const UChar* end);
+ONIG_EXTERN int onigenc_unicode_property_name_to_ctype(OnigEncoding enc, const UChar* p, const UChar* end);
+ONIG_EXTERN int onigenc_mb2_is_code_ctype(OnigEncoding enc, OnigCodePoint code, unsigned int ctype);
+ONIG_EXTERN int onigenc_mb4_code_to_mbclen(OnigCodePoint code, OnigEncoding enc);
+ONIG_EXTERN int onigenc_mb4_code_to_mbc(OnigEncoding enc, OnigCodePoint code, UChar *buf);
+ONIG_EXTERN int onigenc_mb4_is_code_ctype(OnigEncoding enc, OnigCodePoint code, unsigned int ctype);
-ONIG_EXTERN int onigenc_unicode_case_map P_((OnigCaseFoldType* flagP, const OnigUChar** pp, const OnigUChar* end, OnigUChar* to, OnigUChar* to_end, const struct OnigEncodingTypeST* enc));
+ONIG_EXTERN int onigenc_unicode_case_map(OnigCaseFoldType* flagP, const OnigUChar** pp, const OnigUChar* end, OnigUChar* to, OnigUChar* to_end, const struct OnigEncodingTypeST* enc);
/* in enc/unicode.c */
-ONIG_EXTERN int onigenc_unicode_is_code_ctype P_((OnigCodePoint code, unsigned int ctype, OnigEncoding enc));
-ONIG_EXTERN int onigenc_utf16_32_get_ctype_code_range P_((OnigCtype ctype, OnigCodePoint *sb_out, const OnigCodePoint* ranges[], OnigEncoding enc));
-ONIG_EXTERN int onigenc_unicode_ctype_code_range P_((int ctype, const OnigCodePoint* ranges[]));
-ONIG_EXTERN int onigenc_unicode_get_case_fold_codes_by_str P_((OnigEncoding enc, OnigCaseFoldType flag, const OnigUChar* p, const OnigUChar* end, OnigCaseFoldCodeItem items[]));
-ONIG_EXTERN int onigenc_unicode_mbc_case_fold P_((OnigEncoding enc, OnigCaseFoldType flag, const UChar** pp, const UChar* end, UChar* fold));
-ONIG_EXTERN int onigenc_unicode_apply_all_case_fold P_((OnigCaseFoldType flag, OnigApplyAllCaseFoldFunc f, void* arg, OnigEncoding enc));
+ONIG_EXTERN int onigenc_unicode_is_code_ctype(OnigCodePoint code, unsigned int ctype, OnigEncoding enc);
+ONIG_EXTERN int onigenc_utf16_32_get_ctype_code_range(OnigCtype ctype, OnigCodePoint *sb_out, const OnigCodePoint* ranges[], OnigEncoding enc);
+ONIG_EXTERN int onigenc_unicode_ctype_code_range(int ctype, const OnigCodePoint* ranges[]);
+ONIG_EXTERN int onigenc_unicode_get_case_fold_codes_by_str(OnigEncoding enc, OnigCaseFoldType flag, const OnigUChar* p, const OnigUChar* end, OnigCaseFoldCodeItem items[]);
+ONIG_EXTERN int onigenc_unicode_mbc_case_fold(OnigEncoding enc, OnigCaseFoldType flag, const UChar** pp, const UChar* end, UChar* fold);
+ONIG_EXTERN int onigenc_unicode_apply_all_case_fold(OnigCaseFoldType flag, OnigApplyAllCaseFoldFunc f, void* arg, OnigEncoding enc);
#define UTF16_IS_SURROGATE_FIRST(c) (((c) & 0xfc) == 0xd8)
#define UTF16_IS_SURROGATE_SECOND(c) (((c) & 0xfc) == 0xdc)
#define UTF16_IS_SURROGATE(c) (((c) & 0xf8) == 0xd8)
+#define UNICODE_VALID_CODEPOINT_P(c) ( \
+ ((c) <= 0x10ffff) && \
+ !((c) < 0x10000 && UTF16_IS_SURROGATE((c) >> 8)))
#define ONIGENC_ISO_8859_1_TO_LOWER_CASE(c) \
OnigEncISO_8859_1_ToLowerCaseTable[c]
@@ -182,14 +199,14 @@ ONIG_EXTERN const UChar OnigEncISO_8859_1_ToLowerCaseTable[];
ONIG_EXTERN const UChar OnigEncISO_8859_1_ToUpperCaseTable[];
ONIG_EXTERN int
-onigenc_with_ascii_strncmp P_((OnigEncoding enc, const UChar* p, const UChar* end, const UChar* sascii /* ascii */, int n));
+onigenc_with_ascii_strncmp(OnigEncoding enc, const UChar* p, const UChar* end, const UChar* sascii /* ascii */, int n);
ONIG_EXTERN int
-onigenc_with_ascii_strnicmp P_((OnigEncoding enc, const UChar* p, const UChar* end, const UChar* sascii /* ascii */, int n));
+onigenc_with_ascii_strnicmp(OnigEncoding enc, const UChar* p, const UChar* end, const UChar* sascii /* ascii */, int n);
ONIG_EXTERN UChar*
-onigenc_step P_((OnigEncoding enc, const UChar* p, const UChar* end, int n));
+onigenc_step(OnigEncoding enc, const UChar* p, const UChar* end, int n);
/* defined in regexec.c, but used in enc/xxx.c */
-extern int onig_is_in_code_range P_((const UChar* p, OnigCodePoint code));
+extern int onig_is_in_code_range(const UChar* p, OnigCodePoint code);
ONIG_EXTERN OnigEncoding OnigEncDefaultCharEncoding;
ONIG_EXTERN const UChar OnigEncAsciiToLowerCaseTable[];
@@ -212,9 +229,9 @@ ONIG_EXTERN const unsigned short OnigEncAsciiCtypeTable[];
#ifdef ONIG_ENC_REGISTER
extern int ONIG_ENC_REGISTER(const char *, OnigEncoding);
-#define OnigEncodingName(n) encoding_##n
-#define OnigEncodingDeclare(n) static const OnigEncodingType OnigEncodingName(n)
-#define OnigEncodingDefine(f,n) \
+# define OnigEncodingName(n) encoding_##n
+# define OnigEncodingDeclare(n) static const OnigEncodingType OnigEncodingName(n)
+# define OnigEncodingDefine(f,n) \
OnigEncodingDeclare(n); \
void Init_##f(void) { \
ONIG_ENC_REGISTER(OnigEncodingName(n).name, \
@@ -222,9 +239,9 @@ extern int ONIG_ENC_REGISTER(const char *, OnigEncoding);
} \
OnigEncodingDeclare(n)
#else
-#define OnigEncodingName(n) OnigEncoding##n
-#define OnigEncodingDeclare(n) const OnigEncodingType OnigEncodingName(n)
-#define OnigEncodingDefine(f,n) OnigEncodingDeclare(n)
+# define OnigEncodingName(n) OnigEncoding##n
+# define OnigEncodingDeclare(n) const OnigEncodingType OnigEncodingName(n)
+# define OnigEncodingDefine(f,n) OnigEncodingDeclare(n)
#endif
/* macros for define replica encoding and encoding alias */
@@ -234,4 +251,4 @@ extern int ONIG_ENC_REGISTER(const char *, OnigEncoding);
RUBY_SYMBOL_EXPORT_END
-#endif /* ONIGURUMA_REGENC_H */
+#endif /* ONIGMO_REGENC_H */
diff --git a/regerror.c b/regerror.c
index 9ec3f65f4c..59cf53068e 100644
--- a/regerror.c
+++ b/regerror.c
@@ -3,7 +3,7 @@
**********************************************************************/
/*-
* Copyright (c) 2002-2007 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
- * Copyright (c) 2011-2014 K.Takata <kentkt AT csc DOT jp>
+ * Copyright (c) 2011-2016 K.Takata <kentkt AT csc DOT jp>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,13 +31,7 @@
#include "regint.h"
#include <stdio.h> /* for vsnprintf() */
-#ifdef HAVE_STDARG_PROTOTYPES
#include <stdarg.h>
-#define va_init_list(a,b) va_start(a,b)
-#else
-#include <varargs.h>
-#define va_init_list(a,b) va_start(a)
-#endif
extern UChar*
onig_error_code_to_format(OnigPosition code)
@@ -65,6 +59,8 @@ onig_error_code_to_format(OnigPosition code)
p = "unexpected bytecode (bug)"; break;
case ONIGERR_MATCH_STACK_LIMIT_OVER:
p = "match-stack limit over"; break;
+ case ONIGERR_PARSE_DEPTH_LIMIT_OVER:
+ p = "parse depth limit over"; break;
case ONIGERR_DEFAULT_ENCODING_IS_NOT_SET:
p = "default multibyte-encoding is not set"; break;
case ONIGERR_SPECIFIED_ENCODING_CANT_CONVERT_TO_WIDE_CHAR:
@@ -179,8 +175,6 @@ onig_error_code_to_format(OnigPosition code)
p = "not supported encoding combination"; break;
case ONIGERR_INVALID_COMBINATION_OF_OPTIONS:
p = "invalid combination of options"; break;
- case ONIGERR_OVER_THREAD_PASS_LIMIT_COUNT:
- p = "over thread pass limit count"; break;
default:
p = "undefined error code"; break;
@@ -191,12 +185,12 @@ onig_error_code_to_format(OnigPosition code)
static void sprint_byte(char* s, unsigned int v)
{
- sprintf(s, "%02x", (v & 0377));
+ xsnprintf(s, 3, "%02x", (v & 0377));
}
static void sprint_byte_with_x(char* s, unsigned int v)
{
- sprintf(s, "\\x%02x", (v & 0377));
+ xsnprintf(s, 5, "\\x%02x", (v & 0377));
}
static int to_ascii(OnigEncoding enc, UChar *s, UChar *end,
@@ -248,18 +242,11 @@ static int to_ascii(OnigEncoding enc, UChar *s, UChar *end,
}
-/* for ONIG_MAX_ERROR_MESSAGE_LEN */
-#define MAX_ERROR_PAR_LEN 30
+/* < ONIG_MAX_ERROR_MESSAGE_LEN - max length of messages with %n */
+#define MAX_ERROR_PAR_LEN 50
extern int
-#ifdef HAVE_STDARG_PROTOTYPES
onig_error_code_to_str(UChar* s, OnigPosition code, ...)
-#else
-onig_error_code_to_str(s, code, va_alist)
- UChar* s;
- OnigPosition code;
- va_dcl
-#endif
{
UChar *p, *q;
OnigErrorInfo* einfo;
@@ -268,7 +255,7 @@ onig_error_code_to_str(s, code, va_alist)
UChar parbuf[MAX_ERROR_PAR_LEN];
va_list vargs;
- va_init_list(vargs, code);
+ va_start(vargs, code);
switch (code) {
case ONIGERR_UNDEFINED_NAME_REFERENCE:
@@ -337,26 +324,18 @@ onig_vsnprintf_with_pattern(UChar buf[], int bufsize, OnigEncoding enc,
need = (pat_end - pat) * 4 + 4;
if (n + need < (size_t )bufsize) {
- strcat((char* )buf, ": /");
+ static const char sep[] = ": /";
+ memcpy((char* )buf + n, sep, sizeof(sep));
s = buf + onigenc_str_bytelen_null(ONIG_ENCODING_ASCII, buf);
p = pat;
while (p < pat_end) {
- if (*p == '\\') {
- *s++ = *p++;
- len = enclen(enc, p, pat_end);
- while (len-- > 0) *s++ = *p++;
- }
- else if (*p == '/') {
- *s++ = (unsigned char )'\\';
- *s++ = *p++;
- }
- else if (ONIGENC_IS_MBC_HEAD(enc, p, pat_end)) {
+ if (ONIGENC_IS_MBC_HEAD(enc, p, pat_end)) {
len = enclen(enc, p, pat_end);
if (ONIGENC_MBC_MINLEN(enc) == 1) {
while (len-- > 0) *s++ = *p++;
}
- else { /* for UTF16 */
+ else { /* for UTF16/32 */
int blen;
while (len-- > 0) {
@@ -367,6 +346,15 @@ onig_vsnprintf_with_pattern(UChar buf[], int bufsize, OnigEncoding enc,
}
}
}
+ else if (*p == '\\') {
+ *s++ = *p++;
+ len = enclen(enc, p, pat_end);
+ while (len-- > 0) *s++ = *p++;
+ }
+ else if (*p == '/') {
+ *s++ = (unsigned char )'\\';
+ *s++ = *p++;
+ }
else if (!ONIGENC_IS_CODE_PRINT(enc, *p) &&
!ONIGENC_IS_CODE_SPACE(enc, *p)) {
sprint_byte_with_x((char* )bs, (unsigned int )(*p++));
@@ -384,25 +372,15 @@ onig_vsnprintf_with_pattern(UChar buf[], int bufsize, OnigEncoding enc,
}
}
+#if 0 /* unused */
void
-#ifdef HAVE_STDARG_PROTOTYPES
onig_snprintf_with_pattern(UChar buf[], int bufsize, OnigEncoding enc,
UChar* pat, UChar* pat_end, const UChar *fmt, ...)
-#else
-onig_snprintf_with_pattern(buf, bufsize, enc, pat, pat_end, fmt, va_alist)
- UChar buf[];
- int bufsize;
- OnigEncoding enc;
- UChar* pat;
- UChar* pat_end;
- const UChar *fmt;
- va_dcl
-#endif
{
va_list args;
- va_init_list(args, fmt);
+ va_start(args, fmt);
onig_vsnprintf_with_pattern(buf, bufsize, enc,
pat, pat_end, fmt, args);
va_end(args);
}
-
+#endif
diff --git a/regexec.c b/regexec.c
index f8813875dc..9b6232e30b 100644
--- a/regexec.c
+++ b/regexec.c
@@ -3,7 +3,7 @@
**********************************************************************/
/*-
* Copyright (c) 2002-2008 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
- * Copyright (c) 2011-2014 K.Takata <kentkt AT csc DOT jp>
+ * Copyright (c) 2011-2016 K.Takata <kentkt AT csc DOT jp>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,33 +30,39 @@
#include "regint.h"
-/* #define USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE */
+#ifdef RUBY
+# undef USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE
+#else
+# define USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE
+#endif
-#ifndef USE_DIRECT_THREADED_VM
+#ifndef USE_TOKEN_THREADED_VM
# ifdef __GNUC__
-# define USE_DIRECT_THREADED_VM 1
+# define USE_TOKEN_THREADED_VM 1
# else
-# define USE_DIRECT_THREADED_VM 0
+# define USE_TOKEN_THREADED_VM 0
# endif
#endif
-#define ENC_DUMMY_FLAG (1<<24)
+#ifdef RUBY
+# define ENC_DUMMY_FLAG (1<<24)
static inline int
rb_enc_asciicompat(OnigEncoding enc)
{
- return ONIGENC_MBC_MINLEN(enc)==1 && !((enc)->ruby_encoding_index & ENC_DUMMY_FLAG);
+ return ONIGENC_MBC_MINLEN(enc)==1 && !((enc)->ruby_encoding_index & ENC_DUMMY_FLAG);
}
-#undef ONIGENC_IS_MBC_ASCII_WORD
-#define ONIGENC_IS_MBC_ASCII_WORD(enc,s,end) \
+# undef ONIGENC_IS_MBC_ASCII_WORD
+# define ONIGENC_IS_MBC_ASCII_WORD(enc,s,end) \
(rb_enc_asciicompat(enc) ? (ISALNUM(*s) || *s=='_') : \
onigenc_ascii_is_code_ctype( \
ONIGENC_MBC_TO_CODE(enc,s,end),ONIGENC_CTYPE_WORD,enc))
+#endif /* RUBY */
#ifdef USE_CRNL_AS_LINE_TERMINATOR
-#define ONIGENC_IS_MBC_CRNL(enc,p,end) \
+# define ONIGENC_IS_MBC_CRNL(enc,p,end) \
(ONIGENC_MBC_TO_CODE(enc,p,end) == 13 && \
ONIGENC_MBC_TO_CODE(enc,(p+enclen(enc,p,end)),end) == 10)
-#define ONIGENC_IS_MBC_NEWLINE_EX(enc,p,start,end,option,check_prev) \
+# define ONIGENC_IS_MBC_NEWLINE_EX(enc,p,start,end,option,check_prev) \
is_mbc_newline_ex((enc),(p),(start),(end),(option),(check_prev))
static int
is_mbc_newline_ex(OnigEncoding enc, const UChar *p, const UChar *start,
@@ -90,7 +96,7 @@ is_mbc_newline_ex(OnigEncoding enc, const UChar *p, const UChar *start,
}
}
#else /* USE_CRNL_AS_LINE_TERMINATOR */
-#define ONIGENC_IS_MBC_NEWLINE_EX(enc,p,start,end,option,check_prev) \
+# define ONIGENC_IS_MBC_NEWLINE_EX(enc,p,start,end,option,check_prev) \
ONIGENC_IS_MBC_NEWLINE((enc), (p), (end))
#endif /* USE_CRNL_AS_LINE_TERMINATOR */
@@ -105,7 +111,7 @@ history_tree_clear(OnigCaptureTreeNode* node)
if (IS_NOT_NULL(node)) {
for (i = 0; i < node->num_childs; i++) {
if (IS_NOT_NULL(node->childs[i])) {
- history_tree_free(node->childs[i]);
+ history_tree_free(node->childs[i]);
}
}
for (i = 0; i < node->allocated; i++) {
@@ -156,7 +162,7 @@ history_node_new(void)
static int
history_tree_add_child(OnigCaptureTreeNode* parent, OnigCaptureTreeNode* child)
{
-#define HISTORY_TREE_INIT_ALLOC_SIZE 8
+# define HISTORY_TREE_INIT_ALLOC_SIZE 8
if (parent->num_childs >= parent->allocated) {
int n, i;
@@ -164,15 +170,15 @@ history_tree_add_child(OnigCaptureTreeNode* parent, OnigCaptureTreeNode* child)
if (IS_NULL(parent->childs)) {
n = HISTORY_TREE_INIT_ALLOC_SIZE;
parent->childs =
- (OnigCaptureTreeNode** )xmalloc(sizeof(OnigCaptureTreeNode*) * n);
+ (OnigCaptureTreeNode** )xmalloc(sizeof(OnigCaptureTreeNode*) * n);
CHECK_NULL_RETURN_MEMERR(parent->childs);
}
else {
OnigCaptureTreeNode** tmp;
n = parent->allocated * 2;
tmp =
- (OnigCaptureTreeNode** )xrealloc(parent->childs,
- sizeof(OnigCaptureTreeNode*) * n);
+ (OnigCaptureTreeNode** )xrealloc(parent->childs,
+ sizeof(OnigCaptureTreeNode*) * n);
if (tmp == 0) {
history_tree_clear(parent);
return ONIGERR_MEMORY;
@@ -348,7 +354,7 @@ onig_region_free(OnigRegion* r, int free_self)
}
extern void
-onig_region_copy(OnigRegion* to, OnigRegion* from)
+onig_region_copy(OnigRegion* to, const OnigRegion* from)
{
#define RREGC_SIZE (sizeof(int) * from->num_regs)
int i, r;
@@ -397,6 +403,8 @@ onig_region_copy(OnigRegion* to, OnigRegion* from)
#define STK_CALL_FRAME 0x0800
#define STK_RETURN 0x0900
#define STK_VOID 0x0a00 /* for fill a blank */
+#define STK_ABSENT_POS 0x0b00 /* for absent */
+#define STK_ABSENT 0x0c00 /* absent inner loop marker */
/* stack type check mask */
#define STK_MASK_POP_USED 0x00ff
@@ -404,7 +412,7 @@ onig_region_copy(OnigRegion* to, OnigRegion* from)
#define STK_MASK_MEM_END_OR_MARK 0x8000 /* MEM_END or MEM_END_MARK */
#ifdef USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE
-#define MATCH_ARG_INIT(msa, arg_option, arg_region, arg_start, arg_gpos) do {\
+# define MATCH_ARG_INIT(msa, arg_option, arg_region, arg_start, arg_gpos) do {\
(msa).stack_p = (void* )0;\
(msa).options = (arg_option);\
(msa).region = (arg_region);\
@@ -413,7 +421,7 @@ onig_region_copy(OnigRegion* to, OnigRegion* from)
(msa).best_len = ONIG_MISMATCH;\
} while(0)
#else
-#define MATCH_ARG_INIT(msa, arg_option, arg_region, arg_start, arg_gpos) do {\
+# define MATCH_ARG_INIT(msa, arg_option, arg_region, arg_start, arg_gpos) do {\
(msa).stack_p = (void* )0;\
(msa).options = (arg_option);\
(msa).region = (arg_region);\
@@ -424,9 +432,9 @@ onig_region_copy(OnigRegion* to, OnigRegion* from)
#ifdef USE_COMBINATION_EXPLOSION_CHECK
-#define STATE_CHECK_BUFF_MALLOC_THRESHOLD_SIZE 16
+# define STATE_CHECK_BUFF_MALLOC_THRESHOLD_SIZE 16
-#define STATE_CHECK_BUFF_INIT(msa, str_len, offset, state_num) do { \
+# define STATE_CHECK_BUFF_INIT(msa, str_len, offset, state_num) do { \
if ((state_num) > 0 && str_len >= STATE_CHECK_STRING_THRESHOLD_LEN) {\
unsigned int size = (unsigned int )(((str_len) + 1) * (state_num) + 7) >> 3;\
offset = ((offset) * (state_num)) >> 3;\
@@ -452,14 +460,14 @@ onig_region_copy(OnigRegion* to, OnigRegion* from)
}\
} while(0)
-#define MATCH_ARG_FREE(msa) do {\
+# define MATCH_ARG_FREE(msa) do {\
if ((msa).stack_p) xfree((msa).stack_p);\
if ((msa).state_check_buff_size >= STATE_CHECK_BUFF_MALLOC_THRESHOLD_SIZE) { \
if ((msa).state_check_buff) xfree((msa).state_check_buff);\
}\
} 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) if ((msa).stack_p) xfree((msa).stack_p)
#endif /* USE_COMBINATION_EXPLOSION_CHECK */
@@ -548,9 +556,9 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
n *= 2;
if (limit_size != 0 && n > limit_size) {
if ((unsigned int )(stk_end - stk_base) == limit_size)
- return ONIGERR_MATCH_STACK_LIMIT_OVER;
+ return ONIGERR_MATCH_STACK_LIMIT_OVER;
else
- n = limit_size;
+ n = limit_size;
}
x = (OnigStackType* )xrealloc(stk_base, sizeof(OnigStackType) * n);
if (IS_NULL(x)) {
@@ -587,9 +595,9 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define IS_TO_VOID_TARGET(stk) (((stk)->type & STK_MASK_TO_VOID_TARGET) != 0)
#ifdef USE_COMBINATION_EXPLOSION_CHECK
-#define STATE_CHECK_POS(s,snum) \
+# define STATE_CHECK_POS(s,snum) \
(((s) - str) * num_comb_exp_check + ((snum) - 1))
-#define STATE_CHECK_VAL(v,snum) do {\
+# define STATE_CHECK_VAL(v,snum) do {\
if (state_check_buff != NULL) {\
int x = STATE_CHECK_POS(s,snum);\
(v) = state_check_buff[x/8] & (1<<(x%8));\
@@ -598,13 +606,13 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
} while(0)
-#define ELSE_IF_STATE_CHECK_MARK(stk) \
+# define ELSE_IF_STATE_CHECK_MARK(stk) \
else if ((stk)->type == STK_STATE_CHECK_MARK) { \
int x = STATE_CHECK_POS(stk->u.state.pstr, stk->u.state.state_check);\
state_check_buff[x/8] |= (1<<(x%8)); \
}
-#define STACK_PUSH(stack_type,pat,s,sprev,keep) do {\
+# define STACK_PUSH(stack_type,pat,s,sprev,keep) do {\
STACK_ENSURE(1);\
stk->type = (stack_type);\
stk->u.state.pcode = (pat);\
@@ -615,14 +623,14 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
STACK_INC;\
} while(0)
-#define STACK_PUSH_ENSURED(stack_type,pat) do {\
+# define STACK_PUSH_ENSURED(stack_type,pat) do {\
stk->type = (stack_type);\
stk->u.state.pcode = (pat);\
stk->u.state.state_check = 0;\
STACK_INC;\
} while(0)
-#define STACK_PUSH_ALT_WITH_STATE_CHECK(pat,s,sprev,snum,keep) do {\
+# define STACK_PUSH_ALT_WITH_STATE_CHECK(pat,s,sprev,snum,keep) do {\
STACK_ENSURE(1);\
stk->type = STK_ALT;\
stk->u.state.pcode = (pat);\
@@ -633,7 +641,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
STACK_INC;\
} while(0)
-#define STACK_PUSH_STATE_CHECK(s,snum) do {\
+# define STACK_PUSH_STATE_CHECK(s,snum) do {\
if (state_check_buff != NULL) {\
STACK_ENSURE(1);\
stk->type = STK_STATE_CHECK_MARK;\
@@ -645,9 +653,9 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#else /* USE_COMBINATION_EXPLOSION_CHECK */
-#define ELSE_IF_STATE_CHECK_MARK(stk)
+# define ELSE_IF_STATE_CHECK_MARK(stk)
-#define STACK_PUSH(stack_type,pat,s,sprev,keep) do {\
+# define STACK_PUSH(stack_type,pat,s,sprev,keep) do {\
STACK_ENSURE(1);\
stk->type = (stack_type);\
stk->u.state.pcode = (pat);\
@@ -657,7 +665,7 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
STACK_INC;\
} while(0)
-#define STACK_PUSH_ENSURED(stack_type,pat) do {\
+# define STACK_PUSH_ENSURED(stack_type,pat) do {\
stk->type = (stack_type);\
stk->u.state.pcode = (pat);\
STACK_INC;\
@@ -667,7 +675,8 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
#define STACK_PUSH_ALT(pat,s,sprev,keep) STACK_PUSH(STK_ALT,pat,s,sprev,keep)
#define STACK_PUSH_POS(s,sprev,keep) STACK_PUSH(STK_POS,NULL_UCHARP,s,sprev,keep)
#define STACK_PUSH_POS_NOT(pat,s,sprev,keep) STACK_PUSH(STK_POS_NOT,pat,s,sprev,keep)
-#define STACK_PUSH_STOP_BT STACK_PUSH_TYPE(STK_STOP_BT)
+#define STACK_PUSH_ABSENT STACK_PUSH_TYPE(STK_ABSENT)
+#define STACK_PUSH_STOP_BT STACK_PUSH_TYPE(STK_STOP_BT)
#define STACK_PUSH_LOOK_BEHIND_NOT(pat,s,sprev,keep) \
STACK_PUSH(STK_LOOK_BEHIND_NOT,pat,s,sprev,keep)
@@ -779,15 +788,23 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
STACK_INC;\
} while(0)
+#define STACK_PUSH_ABSENT_POS(start, end) do {\
+ STACK_ENSURE(1);\
+ stk->type = STK_ABSENT_POS;\
+ stk->u.absent_pos.abs_pstr = (start);\
+ stk->u.absent_pos.end_pstr = (end);\
+ STACK_INC;\
+} while(0)
+
#ifdef ONIG_DEBUG
-#define STACK_BASE_CHECK(p, at) \
+# define STACK_BASE_CHECK(p, at) \
if ((p) < stk_base) {\
fprintf(stderr, "at %s\n", at);\
goto stack_error;\
}
#else
-#define STACK_BASE_CHECK(p, at)
+# define STACK_BASE_CHECK(p, at)
#endif
#define STACK_POP_ONE do {\
@@ -879,6 +896,33 @@ stack_double(OnigStackType** arg_stk_base, OnigStackType** arg_stk_end,
}\
} while(0)
+#define STACK_POP_TIL_ABSENT do {\
+ while (1) {\
+ stk--;\
+ STACK_BASE_CHECK(stk, "STACK_POP_TIL_ABSENT"); \
+ if (stk->type == STK_ABSENT) break;\
+ else if (stk->type == STK_MEM_START) {\
+ mem_start_stk[stk->u.mem.num] = stk->u.mem.start;\
+ mem_end_stk[stk->u.mem.num] = stk->u.mem.end;\
+ }\
+ else if (stk->type == STK_REPEAT_INC) {\
+ STACK_AT(stk->u.repeat_inc.si)->u.repeat.count--;\
+ }\
+ else if (stk->type == STK_MEM_END) {\
+ mem_start_stk[stk->u.mem.num] = stk->u.mem.start;\
+ mem_end_stk[stk->u.mem.num] = stk->u.mem.end;\
+ }\
+ ELSE_IF_STATE_CHECK_MARK(stk);\
+ }\
+} while(0)
+
+#define STACK_POP_ABSENT_POS(start, end) do {\
+ stk--;\
+ STACK_BASE_CHECK(stk, "STACK_POP_ABSENT_POS"); \
+ (start) = stk->u.absent_pos.abs_pstr;\
+ (end) = stk->u.absent_pos.end_pstr;\
+} while(0)
+
#define STACK_POS_END(k) do {\
k = stk;\
while (1) {\
@@ -1124,16 +1168,18 @@ static int string_cmp_ic(OnigEncoding enc, int case_fold_flag,
#define IS_EMPTY_STR (str == end)
-#define ON_STR_BEGIN(s) ((s) == str)
-#define ON_STR_END(s) ((s) == end)
+#define ON_STR_BEGIN(s) ((s) == str)
+#define ON_STR_END(s) ((s) == end)
#ifdef USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE
-#define DATA_ENSURE_CHECK1 (s < right_range)
-#define DATA_ENSURE_CHECK(n) (s + (n) <= right_range)
-#define DATA_ENSURE(n) if (s + (n) > right_range) goto fail
+# define DATA_ENSURE_CHECK1 (s < right_range)
+# define DATA_ENSURE_CHECK(n) (s + (n) <= right_range)
+# define DATA_ENSURE(n) if (s + (n) > right_range) goto fail
+# define ABSENT_END_POS right_range
#else
-#define DATA_ENSURE_CHECK1 (s < end)
-#define DATA_ENSURE_CHECK(n) (s + (n) <= end)
-#define DATA_ENSURE(n) if (s + (n) > end) goto fail
+# define DATA_ENSURE_CHECK1 (s < end)
+# define DATA_ENSURE_CHECK(n) (s + (n) <= end)
+# define DATA_ENSURE(n) if (s + (n) > end) goto fail
+# define ABSENT_END_POS end
#endif /* USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE */
@@ -1150,29 +1196,29 @@ make_capture_history_tree(OnigCaptureTreeNode* node, OnigStackType** kp,
if (k->type == STK_MEM_START) {
n = k->u.mem.num;
if (n <= ONIG_MAX_CAPTURE_HISTORY_GROUP &&
- BIT_STATUS_AT(reg->capture_history, n) != 0) {
- child = history_node_new();
- CHECK_NULL_RETURN_MEMERR(child);
- child->group = n;
- child->beg = k->u.mem.pstr - str;
- r = history_tree_add_child(node, child);
- if (r != 0) {
- history_tree_free(child);
- return r;
- }
- *kp = (k + 1);
- r = make_capture_history_tree(child, kp, stk_top, str, reg);
- if (r != 0) return r;
-
- k = *kp;
- child->end = k->u.mem.pstr - str;
+ BIT_STATUS_AT(reg->capture_history, n) != 0) {
+ child = history_node_new();
+ CHECK_NULL_RETURN_MEMERR(child);
+ child->group = n;
+ child->beg = k->u.mem.pstr - str;
+ r = history_tree_add_child(node, child);
+ if (r != 0) {
+ history_tree_free(child);
+ return r;
+ }
+ *kp = (k + 1);
+ r = make_capture_history_tree(child, kp, stk_top, str, reg);
+ if (r != 0) return r;
+
+ k = *kp;
+ child->end = k->u.mem.pstr - str;
}
}
else if (k->type == STK_MEM_END) {
if (k->u.mem.num == node->group) {
- node->end = k->u.mem.pstr - str;
- *kp = k;
- return 0;
+ node->end = k->u.mem.pstr - str;
+ *kp = k;
+ return 0;
}
}
k++;
@@ -1195,10 +1241,10 @@ static int mem_is_in_memp(int mem, int num, UChar* memp)
return 0;
}
-static int backref_match_at_nested_level(regex_t* reg
- , OnigStackType* top, OnigStackType* stk_base
- , int ignore_case, int case_fold_flag
- , int nest, int mem_num, UChar* memp, UChar** s, const UChar* send)
+static int backref_match_at_nested_level(regex_t* reg,
+ OnigStackType* top, OnigStackType* stk_base,
+ int ignore_case, int case_fold_flag,
+ int nest, int mem_num, UChar* memp, UChar** s, const UChar* send)
{
UChar *ss, *p, *pstart, *pend = NULL_UCHARP;
int level;
@@ -1255,27 +1301,37 @@ static int backref_match_at_nested_level(regex_t* reg
#ifdef ONIG_DEBUG_STATISTICS
-#define USE_TIMEOFDAY
-
-#ifdef USE_TIMEOFDAY
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
+# ifdef _WIN32
+# include <windows.h>
+static LARGE_INTEGER ts, te, freq;
+# define GETTIME(t) QueryPerformanceCounter(&(t))
+# define TIMEDIFF(te,ts) (unsigned long )(((te).QuadPart - (ts).QuadPart) \
+ * 1000000 / freq.QuadPart)
+# else /* _WIN32 */
+
+# define USE_TIMEOFDAY
+
+# ifdef USE_TIMEOFDAY
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# endif
+# ifdef HAVE_UNISTD_H
+# include <unistd.h>
+# endif
static struct timeval ts, te;
-#define GETTIME(t) gettimeofday(&(t), (struct timezone* )0)
-#define TIMEDIFF(te,ts) (((te).tv_usec - (ts).tv_usec) + \
- (((te).tv_sec - (ts).tv_sec)*1000000))
-#else /* USE_TIMEOFDAY */
-#ifdef HAVE_SYS_TIMES_H
-#include <sys/times.h>
-#endif
+# define GETTIME(t) gettimeofday(&(t), (struct timezone* )0)
+# define TIMEDIFF(te,ts) (((te).tv_usec - (ts).tv_usec) + \
+ (((te).tv_sec - (ts).tv_sec)*1000000))
+# else /* USE_TIMEOFDAY */
+# ifdef HAVE_SYS_TIMES_H
+# include <sys/times.h>
+# endif
static struct tms ts, te;
-#define GETTIME(t) times(&(t))
-#define TIMEDIFF(te,ts) ((te).tms_utime - (ts).tms_utime)
-#endif /* USE_TIMEOFDAY */
+# define GETTIME(t) times(&(t))
+# define TIMEDIFF(te,ts) ((te).tms_utime - (ts).tms_utime)
+# endif /* USE_TIMEOFDAY */
+
+# endif /* _WIN32 */
static int OpCounter[256];
static int OpPrevCounter[256];
@@ -1284,14 +1340,14 @@ static int OpCurr = OP_FINISH;
static int OpPrevTarget = OP_FAIL;
static int MaxStackDepth = 0;
-#define MOP_IN(opcode) do {\
+# define MOP_IN(opcode) do {\
if (opcode == OpPrevTarget) OpPrevCounter[OpCurr]++;\
OpCurr = opcode;\
OpCounter[opcode]++;\
GETTIME(ts);\
} while(0)
-#define MOP_OUT do {\
+# define MOP_OUT do {\
GETTIME(te);\
OpTime[OpCurr] += TIMEDIFF(te, ts);\
} while(0)
@@ -1304,6 +1360,9 @@ onig_statistics_init(void)
OpCounter[i] = OpPrevCounter[i] = 0; OpTime[i] = 0;
}
MaxStackDepth = 0;
+# ifdef _WIN32
+ QueryPerformanceFrequency(&freq);
+# endif
}
extern void
@@ -1318,28 +1377,47 @@ onig_print_statistics(FILE* f)
fprintf(f, "\nmax stack depth: %d\n", MaxStackDepth);
}
-#define STACK_INC do {\
+# define STACK_INC do {\
stk++;\
if (stk - stk_base > MaxStackDepth) \
MaxStackDepth = stk - stk_base;\
} while(0)
#else /* ONIG_DEBUG_STATISTICS */
-#define STACK_INC stk++
+# define STACK_INC stk++
-#define MOP_IN(opcode)
-#define MOP_OUT
+# define MOP_IN(opcode)
+# define MOP_OUT
#endif /* ONIG_DEBUG_STATISTICS */
-
-/* matching region of POSIX API */
-typedef int regoff_t;
-
-typedef struct {
- regoff_t rm_so;
- regoff_t rm_eo;
-} posix_regmatch_t;
+#ifdef ONIG_DEBUG_MATCH
+static char *
+stack_type_str(int stack_type)
+{
+ switch (stack_type) {
+ case STK_ALT: return "Alt ";
+ case STK_LOOK_BEHIND_NOT: return "LBNot ";
+ case STK_POS_NOT: return "PosNot";
+ case STK_MEM_START: return "MemS ";
+ case STK_MEM_END: return "MemE ";
+ case STK_REPEAT_INC: return "RepInc";
+ case STK_STATE_CHECK_MARK: return "StChMk";
+ case STK_NULL_CHECK_START: return "NulChS";
+ case STK_NULL_CHECK_END: return "NulChE";
+ case STK_MEM_END_MARK: return "MemEMk";
+ case STK_POS: return "Pos ";
+ case STK_STOP_BT: return "StopBt";
+ case STK_REPEAT: return "Rep ";
+ case STK_CALL_FRAME: return "Call ";
+ case STK_RETURN: return "Ret ";
+ case STK_VOID: return "Void ";
+ case STK_ABSENT_POS: return "AbsPos";
+ case STK_ABSENT: return "Absent";
+ default: return " ";
+ }
+}
+#endif
/* match data(str - end) from position (sstart). */
/* if sstart == str then set sprev to NULL. */
@@ -1376,15 +1454,16 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
int num_comb_exp_check = reg->num_comb_exp_check;
#endif
-#if USE_DIRECT_THREADED_VM
-#define VM_LOOP JUMP;
-#define VM_LOOP_END
-#define CASE(x) L_##x: sbegin = s; OPCODE_EXEC_HOOK;
-#define DEFAULT L_DEFAULT:
-#define NEXT sprev = sbegin; JUMP
-#define JUMP goto *oplabels[*p++]
+#if USE_TOKEN_THREADED_VM
+# define OP_OFFSET 1
+# define VM_LOOP JUMP;
+# define VM_LOOP_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++])
- static const void *oplabels[] = {
+ RB_GNUC_EXTENSION static const void *oplabels[] = {
&&L_OP_FINISH, /* matching process terminator (no more alternative) */
&&L_OP_END, /* pattern code terminator (success end) */
@@ -1410,7 +1489,6 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
&&L_OP_CCLASS_NOT,
&&L_OP_CCLASS_MB_NOT,
&&L_OP_CCLASS_MIX_NOT,
- &&L_OP_CCLASS_NODE, /* pointer to CClassNode node */
&&L_OP_ANYCHAR, /* "." */
&&L_OP_ANYCHAR_ML, /* "." multi-line */
@@ -1423,24 +1501,24 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
&&L_OP_NOT_WORD,
&&L_OP_WORD_BOUND,
&&L_OP_NOT_WORD_BOUND,
-#ifdef USE_WORD_BEGIN_END
+# ifdef USE_WORD_BEGIN_END
&&L_OP_WORD_BEGIN,
&&L_OP_WORD_END,
-#else
+# else
&&L_DEFAULT,
&&L_DEFAULT,
-#endif
+# endif
&&L_OP_ASCII_WORD,
&&L_OP_NOT_ASCII_WORD,
&&L_OP_ASCII_WORD_BOUND,
&&L_OP_NOT_ASCII_WORD_BOUND,
-#ifdef USE_WORD_BEGIN_END
+# ifdef USE_WORD_BEGIN_END
&&L_OP_ASCII_WORD_BEGIN,
&&L_OP_ASCII_WORD_END,
-#else
+# else
&&L_DEFAULT,
&&L_DEFAULT,
-#endif
+# endif
&&L_OP_BEGIN_BUF,
&&L_OP_END_BUF,
@@ -1448,7 +1526,6 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
&&L_OP_END_LINE,
&&L_OP_SEMI_END_BUF,
&&L_OP_BEGIN_POSITION,
- &&L_OP_BEGIN_POS_OR_LINE, /* used for implicit anchor optimization */
&&L_OP_BACKREF1,
&&L_OP_BACKREF2,
@@ -1456,25 +1533,25 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
&&L_OP_BACKREFN_IC,
&&L_OP_BACKREF_MULTI,
&&L_OP_BACKREF_MULTI_IC,
-#ifdef USE_BACKREF_WITH_LEVEL
+# ifdef USE_BACKREF_WITH_LEVEL
&&L_OP_BACKREF_WITH_LEVEL, /* \k<xxx+n>, \k<xxx-n> */
-#else
+# else
&&L_DEFAULT,
-#endif
+# endif
&&L_OP_MEMORY_START,
&&L_OP_MEMORY_START_PUSH, /* push back-tracker to stack */
&&L_OP_MEMORY_END_PUSH, /* push back-tracker to stack */
-#ifdef USE_SUBEXP_CALL
+# ifdef USE_SUBEXP_CALL
&&L_OP_MEMORY_END_PUSH_REC, /* push back-tracker to stack */
-#else
+# else
&&L_DEFAULT,
-#endif
+# endif
&&L_OP_MEMORY_END,
-#ifdef USE_SUBEXP_CALL
+# ifdef USE_SUBEXP_CALL
&&L_OP_MEMORY_END_REC, /* push marker to stack */
-#else
+# else
&&L_DEFAULT,
-#endif
+# endif
&&L_OP_KEEP,
@@ -1482,7 +1559,11 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
&&L_OP_JUMP,
&&L_OP_PUSH,
&&L_OP_POP,
+# ifdef USE_OP_PUSH_OR_JUMP_EXACT
&&L_OP_PUSH_OR_JUMP_EXACT1, /* if match exact then push, else jump. */
+# else
+ &&L_DEFAULT,
+# endif
&&L_OP_PUSH_IF_PEEK_NEXT, /* if match exact then push, else none. */
&&L_OP_REPEAT, /* {n,m} */
&&L_OP_REPEAT_NG, /* {n,m}? (non greedy) */
@@ -1492,16 +1573,16 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
&&L_OP_REPEAT_INC_NG_SG, /* search and get in stack (non greedy) */
&&L_OP_NULL_CHECK_START, /* null loop checker start */
&&L_OP_NULL_CHECK_END, /* null loop checker end */
-#ifdef USE_MONOMANIAC_CHECK_CAPTURES_IN_ENDLESS_REPEAT
+# ifdef USE_MONOMANIAC_CHECK_CAPTURES_IN_ENDLESS_REPEAT
&&L_OP_NULL_CHECK_END_MEMST, /* null loop checker end (with capture status) */
-#else
+# else
&&L_DEFAULT,
-#endif
-#ifdef USE_SUBEXP_CALL
+# endif
+# ifdef USE_SUBEXP_CALL
&&L_OP_NULL_CHECK_END_MEMST_PUSH, /* with capture status and push check-end */
-#else
+# else
&&L_DEFAULT,
-#endif
+# endif
&&L_OP_PUSH_POS, /* (?=...) start */
&&L_OP_POP_POS, /* (?=...) end */
@@ -1512,70 +1593,70 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
&&L_OP_LOOK_BEHIND, /* (?<=...) start (no needs end opcode) */
&&L_OP_PUSH_LOOK_BEHIND_NOT, /* (?<!...) start */
&&L_OP_FAIL_LOOK_BEHIND_NOT, /* (?<!...) end */
+ &&L_OP_PUSH_ABSENT_POS, /* (?~...) start */
+ &&L_OP_ABSENT, /* (?~...) start of inner loop */
+ &&L_OP_ABSENT_END, /* (?~...) end */
-#ifdef USE_SUBEXP_CALL
+# ifdef USE_SUBEXP_CALL
&&L_OP_CALL, /* \g<name> */
&&L_OP_RETURN,
-#else
+# else
&&L_DEFAULT,
&&L_DEFAULT,
-#endif
+# endif
&&L_OP_CONDITION,
-#ifdef USE_COMBINATION_EXPLOSION_CHECK
+# ifdef USE_COMBINATION_EXPLOSION_CHECK
&&L_OP_STATE_CHECK_PUSH, /* combination explosion check and push */
&&L_OP_STATE_CHECK_PUSH_OR_JUMP, /* check ok -> push, else jump */
&&L_OP_STATE_CHECK, /* check only */
-#else
+# else
&&L_DEFAULT,
&&L_DEFAULT,
&&L_DEFAULT,
-#endif
-#ifdef USE_COMBINATION_EXPLOSION_CHECK
+# endif
+# ifdef USE_COMBINATION_EXPLOSION_CHECK
&&L_OP_STATE_CHECK_ANYCHAR_STAR,
&&L_OP_STATE_CHECK_ANYCHAR_ML_STAR,
-#else
+# else
&&L_DEFAULT,
&&L_DEFAULT,
-#endif
+# endif
/* no need: IS_DYNAMIC_OPTION() == 0 */
-#if 0 /* no need: IS_DYNAMIC_OPTION() == 0 */
+# if 0 /* no need: IS_DYNAMIC_OPTION() == 0 */
&&L_OP_SET_OPTION_PUSH, /* set option and push recover option */
&&L_OP_SET_OPTION /* set option */
-#else
+# else
&&L_DEFAULT,
&&L_DEFAULT
-#endif
+# endif
};
-#else
+#else /* USE_TOKEN_THREADED_VM */
-#define VM_LOOP \
+# define OP_OFFSET 0
+# define VM_LOOP \
while (1) { \
OPCODE_EXEC_HOOK; \
sbegin = s; \
switch (*p++) {
-#define VM_LOOP_END } sprev = sbegin; }
-#define CASE(x) case x:
-#define DEFAULT default:
-#define NEXT break
-#define JUMP continue; break
-#endif
+# define VM_LOOP_END } sprev = sbegin; }
+# define CASE(x) case x:
+# define DEFAULT default:
+# define NEXT break
+# define JUMP continue; break
+#endif /* USE_TOKEN_THREADED_VM */
#ifdef USE_SUBEXP_CALL
- /* Stack #0 is used to store the pattern itself and used for (?R), \g<0>, etc. */
- n = reg->num_repeat + (reg->num_mem + 1) * 2;
-
- STACK_INIT(alloca_base, xmalloc_base, n, INIT_MATCH_STACK_SIZE);
- pop_level = reg->stack_pop_level;
- num_mem = reg->num_mem;
- repeat_stk = (OnigStackIndex* )alloca_base;
+/* Stack #0 is used to store the pattern itself and used for (?R), \g<0>,
+ etc. Additional space is required. */
+# define ADD_NUMMEM 1
+#else
+/* Stack #0 not is used. */
+# define ADD_NUMMEM 0
+#endif
- mem_start_stk = (OnigStackIndex* )(repeat_stk + reg->num_repeat);
- mem_end_stk = mem_start_stk + (num_mem + 1);
-#else /* USE_SUBEXP_CALL */
- /* Stack #0 not is used. */
- n = reg->num_repeat + reg->num_mem * 2;
+ n = reg->num_repeat + (reg->num_mem + ADD_NUMMEM) * 2;
STACK_INIT(alloca_base, xmalloc_base, n, INIT_MATCH_STACK_SIZE);
pop_level = reg->stack_pop_level;
@@ -1583,25 +1664,27 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
repeat_stk = (OnigStackIndex* )alloca_base;
mem_start_stk = (OnigStackIndex* )(repeat_stk + reg->num_repeat);
- mem_end_stk = mem_start_stk + num_mem;
+ mem_end_stk = mem_start_stk + (num_mem + ADD_NUMMEM);
+ {
+ OnigStackIndex *pp = mem_start_stk;
+ for (; pp < repeat_stk + n; pp += 2) {
+ pp[0] = INVALID_STACK_INDEX;
+ pp[1] = INVALID_STACK_INDEX;
+ }
+ }
+#ifndef USE_SUBEXP_CALL
mem_start_stk--; /* for index start from 1,
mem_start_stk[1]..mem_start_stk[num_mem] */
mem_end_stk--; /* for index start from 1,
mem_end_stk[1]..mem_end_stk[num_mem] */
-#endif /* USE_SUBEXP_CALL */
- {
- OnigStackIndex *pp = mem_start_stk;
- for (; pp < (repeat_stk + n); pp+=2) {
- pp[0] = INVALID_STACK_INDEX;
- pp[1] = INVALID_STACK_INDEX;
- }
- }
+#endif
#ifdef ONIG_DEBUG_MATCH
- fprintf(stderr, "match_at: str: %"PRIdPTR" (%p), end: %"PRIdPTR" (%p), start: %"PRIdPTR" (%p), sprev: %"PRIdPTR" (%p)\n",
- (intptr_t )str, str, (intptr_t )end, end, (intptr_t )sstart, sstart, (intptr_t )sprev, sprev);
+ fprintf(stderr, "match_at: str: %"PRIuPTR" (%p), end: %"PRIuPTR" (%p), start: %"PRIuPTR" (%p), sprev: %"PRIuPTR" (%p)\n",
+ (uintptr_t )str, str, (uintptr_t )end, end, (uintptr_t )sstart, sstart, (uintptr_t )sprev, sprev);
fprintf(stderr, "size: %d, start offset: %d\n",
(int )(end - str), (int )(sstart - str));
+ fprintf(stderr, "\n ofs> str stk:type addr:opcode\n");
#endif
STACK_PUSH_ENSURED(STK_ALT, (UChar* )FinishCode); /* bottom stack */
@@ -1611,31 +1694,34 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
#ifdef ONIG_DEBUG_MATCH
-#define OPCODE_EXEC_HOOK \
+# define OPCODE_EXEC_HOOK \
if (s) { \
UChar *op, *q, *bp, buf[50]; \
int len; \
- op = p - 1; \
+ op = p - OP_OFFSET; \
fprintf(stderr, "%4"PRIdPTR"> \"", (*op == OP_FINISH) ? (ptrdiff_t )-1 : s - str); \
bp = buf; \
q = s; \
if (*op != OP_FINISH) { /* s may not be a valid pointer if OP_FINISH. */ \
for (i = 0; i < 7 && q < end; i++) { \
- len = enclen(encode, q, end); \
+ len = enclen(encode, q, end); \
while (len-- > 0) *bp++ = *q++; \
} \
+ if (q < end) { xmemcpy(bp, "...", 3); bp += 3; } \
} \
- if (q < end) { xmemcpy(bp, "...\"", 4); bp += 4; } \
- else { xmemcpy(bp, "\"", 1); bp += 1; } \
+ xmemcpy(bp, "\"", 1); bp += 1; \
*bp = 0; \
fputs((char* )buf, stderr); \
for (i = 0; i < 20 - (bp - buf); i++) fputc(' ', stderr); \
- fprintf(stderr, "%4"PRIdPTR":", (op == FinishCode) ? (ptrdiff_t )-1 : op - reg->p); \
+ fprintf(stderr, "%4"PRIdPTR":%s %4"PRIdPTR":", \
+ stk - stk_base - 1, \
+ (stk > stk_base) ? stack_type_str(stk[-1].type) : " ", \
+ (op == FinishCode) ? (ptrdiff_t )-1 : op - reg->p); \
onig_print_compiled_byte_code(stderr, op, reg->p+reg->used, NULL, encode); \
fprintf(stderr, "\n"); \
}
#else
-#define OPCODE_EXEC_HOOK ((void) 0)
+# define OPCODE_EXEC_HOOK ((void) 0)
#endif
@@ -1652,83 +1738,56 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
}
else
goto end_best_len;
- }
+ }
#endif
best_len = n;
region = msa->region;
if (region) {
-#ifdef USE_POSIX_API_REGION_OPTION
- if (IS_POSIX_REGION(msa->options)) {
- posix_regmatch_t* rmt = (posix_regmatch_t* )region;
-
- rmt[0].rm_so = (regoff_t )(((pkeep > s) ? s : pkeep) - str);
- rmt[0].rm_eo = (regoff_t )(s - str);
- for (i = 1; i <= num_mem; i++) {
- if (mem_end_stk[i] != INVALID_STACK_INDEX) {
- if (BIT_STATUS_AT(reg->bt_mem_start, i))
- rmt[i].rm_so = (regoff_t )(STACK_AT(mem_start_stk[i])->u.mem.pstr - str);
- else
- rmt[i].rm_so = (regoff_t )((UChar* )((void* )(mem_start_stk[i])) - str);
-
- rmt[i].rm_eo = (regoff_t )((BIT_STATUS_AT(reg->bt_mem_end, i)
+ region->beg[0] = ((pkeep > s) ? s : pkeep) - str;
+ region->end[0] = s - str;
+ for (i = 1; i <= num_mem; i++) {
+ if (mem_end_stk[i] != INVALID_STACK_INDEX) {
+ if (BIT_STATUS_AT(reg->bt_mem_start, i))
+ region->beg[i] = STACK_AT(mem_start_stk[i])->u.mem.pstr - str;
+ else
+ region->beg[i] = (UChar* )((void* )mem_start_stk[i]) - str;
+
+ region->end[i] = (BIT_STATUS_AT(reg->bt_mem_end, i)
? STACK_AT(mem_end_stk[i])->u.mem.pstr
- : (UChar* )((void* )mem_end_stk[i])) - str);
- }
- else {
- rmt[i].rm_so = rmt[i].rm_eo = ONIG_REGION_NOTPOS;
- }
+ : (UChar* )((void* )mem_end_stk[i])) - str;
}
- }
- else {
-#endif /* USE_POSIX_API_REGION_OPTION */
- region->beg[0] = ((pkeep > s) ? s : pkeep) - str;
- region->end[0] = s - str;
- for (i = 1; i <= num_mem; i++) {
- if (mem_end_stk[i] != INVALID_STACK_INDEX) {
- if (BIT_STATUS_AT(reg->bt_mem_start, i))
- region->beg[i] = STACK_AT(mem_start_stk[i])->u.mem.pstr - str;
- else
- region->beg[i] = (UChar* )((void* )mem_start_stk[i]) - str;
-
- region->end[i] = (BIT_STATUS_AT(reg->bt_mem_end, i)
- ? STACK_AT(mem_end_stk[i])->u.mem.pstr
- : (UChar* )((void* )mem_end_stk[i])) - str;
- }
- else {
- region->beg[i] = region->end[i] = ONIG_REGION_NOTPOS;
- }
+ else {
+ region->beg[i] = region->end[i] = ONIG_REGION_NOTPOS;
}
+ }
#ifdef USE_CAPTURE_HISTORY
- if (reg->capture_history != 0) {
- int r;
- OnigCaptureTreeNode* node;
-
- if (IS_NULL(region->history_root)) {
- region->history_root = node = history_node_new();
- CHECK_NULL_RETURN_MEMERR(node);
- }
- else {
- node = region->history_root;
- history_tree_clear(node);
- }
-
- node->group = 0;
- node->beg = ((pkeep > s) ? s : pkeep) - str;
- node->end = s - str;
-
- stkp = stk_base;
- r = make_capture_history_tree(region->history_root, &stkp,
- stk, (UChar* )str, reg);
- if (r < 0) {
- best_len = r; /* error code */
- goto finish;
- }
+ if (reg->capture_history != 0) {
+ int r;
+ OnigCaptureTreeNode* node;
+
+ if (IS_NULL(region->history_root)) {
+ region->history_root = node = history_node_new();
+ CHECK_NULL_RETURN_MEMERR(node);
+ }
+ else {
+ node = region->history_root;
+ history_tree_clear(node);
}
+
+ node->group = 0;
+ node->beg = ((pkeep > s) ? s : pkeep) - str;
+ node->end = s - str;
+
+ stkp = stk_base;
+ r = make_capture_history_tree(region->history_root, &stkp,
+ stk, (UChar* )str, reg);
+ if (r < 0) {
+ best_len = r; /* error code */
+ goto finish;
+ }
+ }
#endif /* USE_CAPTURE_HISTORY */
-#ifdef USE_POSIX_API_REGION_OPTION
- } /* else IS_POSIX_REGION() */
-#endif
} /* if (region) */
} /* n > best_len */
@@ -1752,14 +1811,9 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
NEXT;
CASE(OP_EXACT1) MOP_IN(OP_EXACT1);
-#if 0
DATA_ENSURE(1);
if (*p != *s) goto fail;
p++; s++;
-#endif
- if (*p != *s++) goto fail;
- DATA_ENSURE(0);
- p++;
MOP_OUT;
NEXT;
@@ -1777,8 +1831,8 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
q = lowbuf;
while (len-- > 0) {
if (*p != *q) {
- goto fail;
- }
+ goto fail;
+ }
p++; q++;
}
}
@@ -2036,7 +2090,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
int mb_len = enclen(encode, s, end);
if (! DATA_ENSURE_CHECK(mb_len)) {
- DATA_ENSURE(1);
+ DATA_ENSURE(1);
s = (UChar* )end;
p += tlen;
goto cc_mb_not_success;
@@ -2078,25 +2132,6 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
MOP_OUT;
NEXT;
- CASE(OP_CCLASS_NODE) MOP_IN(OP_CCLASS_NODE);
- {
- OnigCodePoint code;
- void *node;
- int mb_len;
- UChar *ss;
-
- DATA_ENSURE(1);
- GET_POINTER_INC(node, p);
- mb_len = enclen(encode, s, end);
- ss = s;
- s += mb_len;
- DATA_ENSURE(0);
- code = ONIGENC_MBC_TO_CODE(encode, ss, s);
- if (onig_is_code_in_cc_len(mb_len, code, node) == 0) goto fail;
- }
- MOP_OUT;
- NEXT;
-
CASE(OP_ANYCHAR) MOP_IN(OP_ANYCHAR);
DATA_ENSURE(1);
n = enclen(encode, s, end);
@@ -2118,13 +2153,13 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
while (DATA_ENSURE_CHECK1) {
STACK_PUSH_ALT(p, s, sprev, pkeep);
n = enclen(encode, s, end);
- DATA_ENSURE(n);
- if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
- sprev = s;
- s += n;
+ DATA_ENSURE(n);
+ if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
+ sprev = s;
+ s += n;
}
MOP_OUT;
- NEXT;
+ JUMP;
CASE(OP_ANYCHAR_ML_STAR) MOP_IN(OP_ANYCHAR_ML_STAR);
while (DATA_ENSURE_CHECK1) {
@@ -2141,7 +2176,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
}
}
MOP_OUT;
- NEXT;
+ JUMP;
CASE(OP_ANYCHAR_STAR_PEEK_NEXT) MOP_IN(OP_ANYCHAR_STAR_PEEK_NEXT);
while (DATA_ENSURE_CHECK1) {
@@ -2149,10 +2184,10 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
STACK_PUSH_ALT(p + 1, s, sprev, pkeep);
}
n = enclen(encode, s, end);
- DATA_ENSURE(n);
- if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
- sprev = s;
- s += n;
+ DATA_ENSURE(n);
+ if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
+ sprev = s;
+ s += n;
}
p++;
MOP_OUT;
@@ -2187,10 +2222,10 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
STACK_PUSH_ALT_WITH_STATE_CHECK(p, s, sprev, mem, pkeep);
n = enclen(encode, s, end);
- DATA_ENSURE(n);
- if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
- sprev = s;
- s += n;
+ DATA_ENSURE(n);
+ if (ONIGENC_IS_MBC_NEWLINE_EX(encode, s, str, end, option, 0)) goto fail;
+ sprev = s;
+ s += n;
}
MOP_OUT;
NEXT;
@@ -2382,7 +2417,6 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
JUMP;
CASE(OP_BEGIN_LINE) MOP_IN(OP_BEGIN_LINE);
- op_begin_line:
if (ON_STR_BEGIN(s)) {
if (IS_NOTBOL(msa->options)) goto fail;
MOP_OUT;
@@ -2458,13 +2492,6 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
MOP_OUT;
JUMP;
- CASE(OP_BEGIN_POS_OR_LINE) MOP_IN(OP_BEGIN_POS_OR_LINE);
- if (s != msa->gpos)
- goto op_begin_line;
-
- MOP_OUT;
- JUMP;
-
CASE(OP_MEMORY_START_PUSH) MOP_IN(OP_MEMORY_START_PUSH);
GET_MEMNUM_INC(mem, p);
STACK_PUSH_MEM_START(mem, s);
@@ -2474,6 +2501,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_MEMORY_START) MOP_IN(OP_MEMORY_START);
GET_MEMNUM_INC(mem, p);
mem_start_stk[mem] = (OnigStackIndex )((void* )s);
+ mem_end_stk[mem] = INVALID_STACK_INDEX;
MOP_OUT;
JUMP;
@@ -2681,8 +2709,8 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
GET_LENGTH_INC(tlen, p);
sprev = s;
- if (backref_match_at_nested_level(reg, stk, stk_base, ic
- , case_fold_flag, (int )level, (int )tlen, p, &s, end)) {
+ if (backref_match_at_nested_level(reg, stk, stk_base, ic,
+ case_fold_flag, (int )level, (int )tlen, p, &s, end)) {
while (sprev + (len = enclen(encode, sprev, end)) < s)
sprev += len;
@@ -2725,8 +2753,8 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
STACK_NULL_CHECK(isnull, mem, s);
if (isnull) {
#ifdef ONIG_DEBUG_MATCH
- fprintf(stderr, "NULL_CHECK_END: skip id:%d, s:%"PRIdPTR" (%p)\n",
- (int )mem, (intptr_t )s, s);
+ fprintf(stderr, "NULL_CHECK_END: skip id:%d, s:%"PRIuPTR" (%p)\n",
+ (int )mem, (uintptr_t )s, s);
#endif
null_check_found:
/* empty loop founded, skip next instruction */
@@ -2758,10 +2786,10 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
GET_MEMNUM_INC(mem, p); /* mem: null check id */
STACK_NULL_CHECK_MEMST(isnull, mem, s, reg);
if (isnull) {
-#ifdef ONIG_DEBUG_MATCH
- fprintf(stderr, "NULL_CHECK_END_MEMST: skip id:%d, s:%"PRIdPTR" (%p)\n",
- (int )mem, (intptr_t )s, s);
-#endif
+# ifdef ONIG_DEBUG_MATCH
+ fprintf(stderr, "NULL_CHECK_END_MEMST: skip id:%d, s:%"PRIuPTR" (%p)\n",
+ (int )mem, (uintptr_t )s, s);
+# endif
if (isnull == -1) goto fail;
goto null_check_found;
}
@@ -2777,16 +2805,16 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
int isnull;
GET_MEMNUM_INC(mem, p); /* mem: null check id */
-#ifdef USE_MONOMANIAC_CHECK_CAPTURES_IN_ENDLESS_REPEAT
+# ifdef USE_MONOMANIAC_CHECK_CAPTURES_IN_ENDLESS_REPEAT
STACK_NULL_CHECK_MEMST_REC(isnull, mem, s, reg);
-#else
+# else
STACK_NULL_CHECK_REC(isnull, mem, s);
-#endif
+# endif
if (isnull) {
-#ifdef ONIG_DEBUG_MATCH
- fprintf(stderr, "NULL_CHECK_END_MEMST_PUSH: skip id:%d, s:%"PRIdPTR" (%p)\n",
- (int )mem, (intptr_t )s, s);
-#endif
+# ifdef ONIG_DEBUG_MATCH
+ fprintf(stderr, "NULL_CHECK_END_MEMST_PUSH: skip id:%d, s:%"PRIuPTR" (%p)\n",
+ (int )mem, (uintptr_t )s, s);
+# endif
if (isnull == -1) goto fail;
goto null_check_found;
}
@@ -2850,6 +2878,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
MOP_OUT;
JUMP;
+#ifdef USE_OP_PUSH_OR_JUMP_EXACT
CASE(OP_PUSH_OR_JUMP_EXACT1) MOP_IN(OP_PUSH_OR_JUMP_EXACT1);
GET_RELADDR_INC(addr, p);
if (*p == *s && DATA_ENSURE_CHECK1) {
@@ -2861,6 +2890,7 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
p += (addr + 1);
MOP_OUT;
JUMP;
+#endif
CASE(OP_PUSH_IF_PEEK_NEXT) MOP_IN(OP_PUSH_IF_PEEK_NEXT);
GET_RELADDR_INC(addr, p);
@@ -2915,14 +2945,14 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
repeat_inc:
stkp->u.repeat.count++;
if (stkp->u.repeat.count >= reg->repeat_range[mem].upper) {
- /* end of repeat. Nothing to do. */
+ /* end of repeat. Nothing to do. */
}
else if (stkp->u.repeat.count >= reg->repeat_range[mem].lower) {
- STACK_PUSH_ALT(p, s, sprev, pkeep);
- p = STACK_AT(si)->u.repeat.pcode; /* Don't use stkp after PUSH. */
+ STACK_PUSH_ALT(p, s, sprev, pkeep);
+ p = STACK_AT(si)->u.repeat.pcode; /* Don't use stkp after PUSH. */
}
else {
- p = stkp->u.repeat.pcode;
+ p = stkp->u.repeat.pcode;
}
STACK_PUSH_REPEAT_INC(si);
MOP_OUT;
@@ -2944,19 +2974,19 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
repeat_inc_ng:
stkp->u.repeat.count++;
if (stkp->u.repeat.count < reg->repeat_range[mem].upper) {
- if (stkp->u.repeat.count >= reg->repeat_range[mem].lower) {
- UChar* pcode = stkp->u.repeat.pcode;
-
- STACK_PUSH_REPEAT_INC(si);
- STACK_PUSH_ALT(pcode, s, sprev, pkeep);
- }
- else {
- p = stkp->u.repeat.pcode;
- STACK_PUSH_REPEAT_INC(si);
- }
+ if (stkp->u.repeat.count >= reg->repeat_range[mem].lower) {
+ UChar* pcode = stkp->u.repeat.pcode;
+
+ STACK_PUSH_REPEAT_INC(si);
+ STACK_PUSH_ALT(pcode, s, sprev, pkeep);
+ }
+ else {
+ p = stkp->u.repeat.pcode;
+ STACK_PUSH_REPEAT_INC(si);
+ }
}
else if (stkp->u.repeat.count == reg->repeat_range[mem].upper) {
- STACK_PUSH_REPEAT_INC(si);
+ STACK_PUSH_REPEAT_INC(si);
}
MOP_OUT;
CHECK_INTERRUPT_IN_MATCH_AT;
@@ -3035,6 +3065,63 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
goto fail;
NEXT;
+ CASE(OP_PUSH_ABSENT_POS) MOP_IN(OP_PUSH_ABSENT_POS);
+ /* Save the absent-start-pos and the original end-pos. */
+ STACK_PUSH_ABSENT_POS(s, ABSENT_END_POS);
+ MOP_OUT;
+ JUMP;
+
+ CASE(OP_ABSENT) MOP_IN(OP_ABSENT);
+ {
+ const UChar* aend = ABSENT_END_POS;
+ UChar* absent;
+ UChar* selfp = p - 1;
+
+ STACK_POP_ABSENT_POS(absent, ABSENT_END_POS); /* Restore end-pos. */
+ GET_RELADDR_INC(addr, p);
+#ifdef ONIG_DEBUG_MATCH
+ fprintf(stderr, "ABSENT: s:%p, end:%p, absent:%p, aend:%p\n", s, end, absent, aend);
+#endif
+ if ((absent > aend) && (s > absent)) {
+ /* An empty match occurred in (?~...) at the start point.
+ * Never match. */
+ STACK_POP;
+ goto fail;
+ }
+ else if ((s >= aend) && (s > absent)) {
+ if (s > aend) {
+ /* Only one (or less) character matched in the last iteration.
+ * This is not a possible point. */
+ goto fail;
+ }
+ /* All possible points were found. Try matching after (?~...). */
+ DATA_ENSURE(0);
+ p += addr;
+ }
+ else {
+ STACK_PUSH_ALT(p + addr, s, sprev, pkeep); /* Push possible point. */
+ n = enclen(encode, s, end);
+ STACK_PUSH_ABSENT_POS(absent, ABSENT_END_POS); /* Save the original pos. */
+ STACK_PUSH_ALT(selfp, s + n, s, pkeep); /* Next iteration. */
+ STACK_PUSH_ABSENT;
+ ABSENT_END_POS = aend;
+ }
+ }
+ MOP_OUT;
+ JUMP;
+
+ CASE(OP_ABSENT_END) MOP_IN(OP_ABSENT_END);
+ /* The pattern inside (?~...) was matched.
+ * Set the end-pos temporary and go to next iteration. */
+ if (sprev < ABSENT_END_POS)
+ ABSENT_END_POS = sprev;
+#ifdef ONIG_DEBUG_MATCH
+ fprintf(stderr, "ABSENT_END: end:%p\n", ABSENT_END_POS);
+#endif
+ STACK_POP_TIL_ABSENT;
+ goto fail;
+ NEXT;
+
#ifdef USE_SUBEXP_CALL
CASE(OP_CALL) MOP_IN(OP_CALL);
GET_ABSADDR_INC(addr, p);
@@ -3067,9 +3154,9 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_FAIL)
if (0) {
- /* fall */
+ /* fall */
fail:
- MOP_OUT;
+ MOP_OUT;
}
MOP_IN(OP_FAIL);
STACK_POP;
@@ -3080,8 +3167,8 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
#ifdef USE_COMBINATION_EXPLOSION_CHECK
if (stk->u.state.state_check != 0) {
- stk->type = STK_STATE_CHECK_MARK;
- stk++;
+ stk->type = STK_STATE_CHECK_MARK;
+ stk++;
}
#endif
@@ -3158,7 +3245,7 @@ slow_search(OnigEncoding enc, UChar* target, UChar* target_end,
static int
str_lower_case_match(OnigEncoding enc, int case_fold_flag,
- const UChar* t, const UChar* tend,
+ const UChar* t, const UChar* tend,
const UChar* p, const UChar* end)
{
int lowlen;
@@ -3250,7 +3337,7 @@ slow_search_backward_ic(OnigEncoding enc, int case_fold_flag,
while (s >= text) {
if (str_lower_case_match(enc, case_fold_flag,
- target, target_end, s, text_end))
+ target, target_end, s, text_end))
return s;
s = (UChar* )onigenc_get_prev_char_head(enc, adjust_text, s, text_end);
@@ -3270,10 +3357,10 @@ bm_search_notrev(regex_t* reg, const UChar* target, const UChar* target_end,
const UChar *tail;
ptrdiff_t skip, tlen1;
-#ifdef ONIG_DEBUG_SEARCH
+# ifdef ONIG_DEBUG_SEARCH
fprintf(stderr, "bm_search_notrev: text: %"PRIuPTR" (%p), text_end: %"PRIuPTR" (%p), text_range: %"PRIuPTR" (%p)\n",
- text, text, text_end, text_end, text_range, text_range);
-#endif
+ (uintptr_t )text, text, (uintptr_t )text_end, text_end, (uintptr_t )text_range, text_range);
+# endif
tail = target_end - 1;
tlen1 = tail - target;
@@ -3294,11 +3381,12 @@ bm_search_notrev(regex_t* reg, const UChar* target, const UChar* target_end,
skip = reg->map[*se];
t = s;
do {
- s += enclen(reg->enc, s, end);
+ s += enclen(reg->enc, s, end);
} while ((s - t) < skip && s < end);
}
}
else {
+# if OPT_EXACT_MAXLEN >= ONIG_CHAR_TABLE_SIZE
while (s < end) {
p = se = s + tlen1;
t = tail;
@@ -3309,9 +3397,10 @@ bm_search_notrev(regex_t* reg, const UChar* target, const UChar* target_end,
skip = reg->int_map[*se];
t = s;
do {
- s += enclen(reg->enc, s, end);
+ s += enclen(reg->enc, s, end);
} while ((s - t) < skip && s < end);
}
+# endif
}
return (UChar* )NULL;
@@ -3325,10 +3414,10 @@ bm_search(regex_t* reg, const UChar* target, const UChar* target_end,
const UChar *s, *t, *p, *end;
const UChar *tail;
-#ifdef ONIG_DEBUG_SEARCH
- fprintf(stderr, "bm_search: text: %"PRIuPTR", text_end: %"PRIuPTR", text_range: %"PRIuPTR"\n",
- text, text_end, text_range);
-#endif
+# ifdef ONIG_DEBUG_SEARCH
+ fprintf(stderr, "bm_search: text: %"PRIuPTR" (%p), text_end: %"PRIuPTR" (%p), text_range: %"PRIuPTR" (%p)\n",
+ (uintptr_t )text, text, (uintptr_t )text_end, text_end, (uintptr_t )text_range, text_range);
+# endif
end = text_range + (target_end - target) - 1;
if (end > text_end)
@@ -3340,10 +3429,10 @@ bm_search(regex_t* reg, const UChar* target, const UChar* target_end,
while (s < end) {
p = s;
t = tail;
-#ifdef ONIG_DEBUG_SEARCH
+# ifdef ONIG_DEBUG_SEARCH
fprintf(stderr, "bm_search_loop: pos: %"PRIdPTR" %s\n",
(intptr_t )(s - text), s);
-#endif
+# endif
while (*p == *t) {
if (t == target) return (UChar* )p;
p--; t--;
@@ -3352,6 +3441,7 @@ bm_search(regex_t* reg, const UChar* target, const UChar* target_end,
}
}
else { /* see int_map[] */
+# if OPT_EXACT_MAXLEN >= ONIG_CHAR_TABLE_SIZE
while (s < end) {
p = s;
t = tail;
@@ -3361,6 +3451,7 @@ bm_search(regex_t* reg, const UChar* target, const UChar* target_end,
}
s += reg->int_map[*s];
}
+# endif
}
return (UChar* )NULL;
}
@@ -3377,10 +3468,10 @@ bm_search_notrev_ic(regex_t* reg, const UChar* target, const UChar* target_end,
OnigEncoding enc = reg->enc;
int case_fold_flag = reg->case_fold_flag;
-#ifdef ONIG_DEBUG_SEARCH
+# ifdef ONIG_DEBUG_SEARCH
fprintf(stderr, "bm_search_notrev_ic: text: %d (%p), text_end: %d (%p), text_range: %d (%p)\n",
(int )text, text, (int )text_end, text_end, (int )text_range, text_range);
-#endif
+# endif
tail = target_end - 1;
tlen1 = tail - target;
@@ -3399,11 +3490,12 @@ bm_search_notrev_ic(regex_t* reg, const UChar* target, const UChar* target_end,
skip = reg->map[*se];
t = s;
do {
- s += enclen(reg->enc, s, end);
+ s += enclen(reg->enc, s, end);
} while ((s - t) < skip && s < end);
}
}
else {
+# if OPT_EXACT_MAXLEN >= ONIG_CHAR_TABLE_SIZE
while (s < end) {
se = s + tlen1;
if (str_lower_case_match(enc, case_fold_flag, target, target_end,
@@ -3412,9 +3504,10 @@ bm_search_notrev_ic(regex_t* reg, const UChar* target, const UChar* target_end,
skip = reg->int_map[*se];
t = s;
do {
- s += enclen(reg->enc, s, end);
+ s += enclen(reg->enc, s, end);
} while ((s - t) < skip && s < end);
}
+# endif
}
return (UChar* )NULL;
@@ -3430,10 +3523,10 @@ bm_search_ic(regex_t* reg, const UChar* target, const UChar* target_end,
OnigEncoding enc = reg->enc;
int case_fold_flag = reg->case_fold_flag;
-#ifdef ONIG_DEBUG_SEARCH
+# ifdef ONIG_DEBUG_SEARCH
fprintf(stderr, "bm_search_ic: text: %d (%p), text_end: %d (%p), text_range: %d (%p)\n",
(int )text, text, (int )text_end, text_end, (int )text_range, text_range);
-#endif
+# endif
end = text_range + (target_end - target) - 1;
if (end > text_end)
@@ -3451,6 +3544,7 @@ bm_search_ic(regex_t* reg, const UChar* target, const UChar* target_end,
}
}
else { /* see int_map[] */
+# if OPT_EXACT_MAXLEN >= ONIG_CHAR_TABLE_SIZE
while (s < end) {
p = s - (target_end - target) + 1;
if (str_lower_case_match(enc, case_fold_flag, target, target_end,
@@ -3458,6 +3552,7 @@ bm_search_ic(regex_t* reg, const UChar* target, const UChar* target_end,
return (UChar* )p;
s += reg->int_map[*s];
}
+# endif
}
return (UChar* )NULL;
}
@@ -3475,10 +3570,10 @@ bm_search_notrev(regex_t* reg, const UChar* target, const UChar* target_end,
ptrdiff_t skip, tlen1;
OnigEncoding enc = reg->enc;
-#ifdef ONIG_DEBUG_SEARCH
- fprintf(stderr, "bm_search_notrev: text: %"PRIdPTR" (%p), text_end: %"PRIdPTR" (%p), text_range: %"PRIdPTR" (%p)\n",
- (intptr_t )text, text, (intptr_t )text_end, text_end, (intptr_t )text_range, text_range);
-#endif
+# ifdef ONIG_DEBUG_SEARCH
+ fprintf(stderr, "bm_search_notrev: text: %"PRIuPTR" (%p), text_end: %"PRIuPTR" (%p), text_range: %"PRIuPTR" (%p)\n",
+ (uintptr_t )text, text, (uintptr_t )text_end, text_end, (uintptr_t )text_range, text_range);
+# endif
tail = target_end - 1;
tlen1 = tail - target;
@@ -3500,11 +3595,12 @@ bm_search_notrev(regex_t* reg, const UChar* target, const UChar* target_end,
skip = reg->map[se[1]];
t = s;
do {
- s += enclen(enc, s, end);
+ s += enclen(enc, s, end);
} while ((s - t) < skip && s < end);
}
}
else {
+# if OPT_EXACT_MAXLEN >= ONIG_CHAR_TABLE_SIZE
while (s < end) {
p = se = s + tlen1;
t = tail;
@@ -3516,9 +3612,10 @@ bm_search_notrev(regex_t* reg, const UChar* target, const UChar* target_end,
skip = reg->int_map[se[1]];
t = s;
do {
- s += enclen(enc, s, end);
+ s += enclen(enc, s, end);
} while ((s - t) < skip && s < end);
}
+# endif
}
return (UChar* )NULL;
@@ -3533,6 +3630,11 @@ bm_search(regex_t* reg, const UChar* target, const UChar* target_end,
const UChar *tail;
ptrdiff_t tlen1;
+# ifdef ONIG_DEBUG_SEARCH
+ fprintf(stderr, "bm_search: text: %"PRIuPTR" (%p), text_end: %"PRIuPTR" (%p), text_range: %"PRIuPTR" (%p)\n",
+ (uintptr_t )text, text, (uintptr_t )text_end, text_end, (uintptr_t )text_range, text_range);
+# endif
+
tail = target_end - 1;
tlen1 = tail - target;
end = text_range + tlen1;
@@ -3553,6 +3655,7 @@ bm_search(regex_t* reg, const UChar* target, const UChar* target_end,
}
}
else { /* see int_map[] */
+# if OPT_EXACT_MAXLEN >= ONIG_CHAR_TABLE_SIZE
while (s < end) {
p = s;
t = tail;
@@ -3563,6 +3666,7 @@ bm_search(regex_t* reg, const UChar* target, const UChar* target_end,
if (s + 1 >= end) break;
s += reg->int_map[s[1]];
}
+# endif
}
return (UChar* )NULL;
}
@@ -3579,10 +3683,10 @@ bm_search_notrev_ic(regex_t* reg, const UChar* target, const UChar* target_end,
OnigEncoding enc = reg->enc;
int case_fold_flag = reg->case_fold_flag;
-#ifdef ONIG_DEBUG_SEARCH
- fprintf(stderr, "bm_search_notrev_ic: text: %"PRIdPTR" (%p), text_end: %"PRIdPTR" (%p), text_range: %"PRIdPTR" (%p)\n",
- (intptr_t )text, text, (intptr_t )text_end, text_end, (intptr_t )text_range, text_range);
-#endif
+# ifdef ONIG_DEBUG_SEARCH
+ fprintf(stderr, "bm_search_notrev_ic: text: %"PRIuPTR" (%p), text_end: %"PRIuPTR" (%p), text_range: %"PRIuPTR" (%p)\n",
+ (uintptr_t )text, text, (uintptr_t )text_end, text_end, (uintptr_t )text_range, text_range);
+# endif
tail = target_end - 1;
tlen1 = tail - target;
@@ -3602,11 +3706,12 @@ bm_search_notrev_ic(regex_t* reg, const UChar* target, const UChar* target_end,
skip = reg->map[se[1]];
t = s;
do {
- s += enclen(enc, s, end);
+ s += enclen(enc, s, end);
} while ((s - t) < skip && s < end);
}
}
else {
+# if OPT_EXACT_MAXLEN >= ONIG_CHAR_TABLE_SIZE
while (s < end) {
se = s + tlen1;
if (str_lower_case_match(enc, case_fold_flag, target, target_end,
@@ -3616,9 +3721,10 @@ bm_search_notrev_ic(regex_t* reg, const UChar* target, const UChar* target_end,
skip = reg->int_map[se[1]];
t = s;
do {
- s += enclen(enc, s, end);
+ s += enclen(enc, s, end);
} while ((s - t) < skip && s < end);
}
+# endif
}
return (UChar* )NULL;
@@ -3635,10 +3741,10 @@ bm_search_ic(regex_t* reg, const UChar* target, const UChar* target_end,
OnigEncoding enc = reg->enc;
int case_fold_flag = reg->case_fold_flag;
-#ifdef ONIG_DEBUG_SEARCH
- fprintf(stderr, "bm_search_ic: text: %"PRIdPTR" (%p), text_end: %"PRIdPTR" (%p), text_range: %"PRIdPTR" (%p)\n",
- (intptr_t )text, text, (intptr_t )text_end, text_end, (intptr_t )text_range, text_range);
-#endif
+# ifdef ONIG_DEBUG_SEARCH
+ fprintf(stderr, "bm_search_ic: text: %"PRIuPTR" (%p), text_end: %"PRIuPTR" (%p), text_range: %"PRIuPTR" (%p)\n",
+ (uintptr_t )text, text, (uintptr_t )text_end, text_end, (uintptr_t )text_range, text_range);
+# endif
tail = target_end - 1;
tlen1 = tail - target;
@@ -3658,6 +3764,7 @@ bm_search_ic(regex_t* reg, const UChar* target, const UChar* target_end,
}
}
else { /* see int_map[] */
+# if OPT_EXACT_MAXLEN >= ONIG_CHAR_TABLE_SIZE
while (s < end) {
p = s - tlen1;
if (str_lower_case_match(enc, case_fold_flag, target, target_end,
@@ -3666,11 +3773,13 @@ bm_search_ic(regex_t* reg, const UChar* target, const UChar* target_end,
if (s + 1 >= end) break;
s += reg->int_map[s[1]];
}
+# endif
}
return (UChar* )NULL;
}
#endif /* USE_SUNDAY_QUICK_SEARCH */
+#ifdef USE_INT_MAP_BACKWARD
static int
set_bm_backward_skip(UChar* s, UChar* end, OnigEncoding enc ARG_UNUSED,
int** skip)
@@ -3720,6 +3829,7 @@ bm_search_backward(regex_t* reg, const UChar* target, const UChar* target_end,
return (UChar* )NULL;
}
+#endif
static UChar*
map_search(OnigEncoding enc, UChar map[],
@@ -3758,31 +3868,6 @@ onig_match(regex_t* reg, const UChar* str, const UChar* end, const UChar* at, On
UChar *prev;
OnigMatchArg msa;
-#if defined(USE_RECOMPILE_API) && defined(USE_MULTI_THREAD_SYSTEM)
- start:
- THREAD_ATOMIC_START;
- if (ONIG_STATE(reg) >= ONIG_STATE_NORMAL) {
- ONIG_STATE_INC(reg);
- if (IS_NOT_NULL(reg->chain) && ONIG_STATE(reg) == ONIG_STATE_NORMAL) {
- onig_chain_reduce(reg);
- ONIG_STATE_INC(reg);
- }
- }
- else {
- int n;
-
- THREAD_ATOMIC_END;
- n = 0;
- while (ONIG_STATE(reg) < ONIG_STATE_NORMAL) {
- if (++n > THREAD_PASS_LIMIT_COUNT)
- return ONIGERR_OVER_THREAD_PASS_LIMIT_COUNT;
- THREAD_PASS;
- }
- goto start;
- }
- THREAD_ATOMIC_END;
-#endif /* USE_RECOMPILE_API && USE_MULTI_THREAD_SYSTEM */
-
MATCH_ARG_INIT(msa, option, region, at, at);
#ifdef USE_COMBINATION_EXPLOSION_CHECK
{
@@ -3791,11 +3876,7 @@ onig_match(regex_t* reg, const UChar* str, const UChar* end, const UChar* at, On
}
#endif
- if (region
-#ifdef USE_POSIX_API_REGION_OPTION
- && !IS_POSIX_REGION(option)
-#endif
- ) {
+ if (region) {
r = onig_region_resize_clear(region, reg->num_mem + 1);
}
else
@@ -3811,7 +3892,6 @@ onig_match(regex_t* reg, const UChar* str, const UChar* end, const UChar* at, On
}
MATCH_ARG_FREE(msa);
- ONIG_STATE_DEC_THREAD(reg);
return r;
}
@@ -3823,7 +3903,7 @@ forward_search_range(regex_t* reg, const UChar* str, const UChar* end, UChar* s,
#ifdef ONIG_DEBUG_SEARCH
fprintf(stderr, "forward_search_range: str: %"PRIuPTR" (%p), end: %"PRIuPTR" (%p), s: %"PRIuPTR" (%p), range: %"PRIuPTR" (%p)\n",
- (intptr_t )str, str, (intptr_t )end, end, (intptr_t )s, s, (intptr_t )range, range);
+ (uintptr_t )str, str, (uintptr_t )end, end, (uintptr_t )s, s, (uintptr_t )range, range);
#endif
p = s;
@@ -3833,6 +3913,8 @@ forward_search_range(regex_t* reg, const UChar* str, const UChar* end, UChar* s,
}
else {
UChar *q = p + reg->dmin;
+
+ if (q >= end) return 0; /* fail */
while (p < q) p += enclen(reg->enc, p, end);
}
}
@@ -3844,7 +3926,7 @@ forward_search_range(regex_t* reg, const UChar* str, const UChar* end, UChar* s,
break;
case ONIG_OPTIMIZE_EXACT_IC:
p = slow_search_ic(reg->enc, reg->case_fold_flag,
- reg->exact, reg->exact_end, p, end, range);
+ reg->exact, reg->exact_end, p, end, range);
break;
case ONIG_OPTIMIZE_EXACT_BM:
@@ -3916,18 +3998,25 @@ forward_search_range(regex_t* reg, const UChar* str, const UChar* end, UChar* s,
}
else {
if (reg->dmax != ONIG_INFINITE_DISTANCE) {
- *low = p - reg->dmax;
- if (*low > s) {
- *low = onigenc_get_right_adjust_char_head_with_prev(reg->enc, s,
- *low, end, (const UChar** )low_prev);
- if (low_prev && IS_NULL(*low_prev))
- *low_prev = onigenc_get_prev_char_head(reg->enc,
- (pprev ? pprev : s), *low, end);
+ if (p < str + reg->dmax) {
+ *low = (UChar* )str;
+ if (low_prev)
+ *low_prev = onigenc_get_prev_char_head(reg->enc, str, *low, end);
}
else {
- if (low_prev)
- *low_prev = onigenc_get_prev_char_head(reg->enc,
- (pprev ? pprev : str), *low, end);
+ *low = p - reg->dmax;
+ if (*low > s) {
+ *low = onigenc_get_right_adjust_char_head_with_prev(reg->enc, s,
+ *low, end, (const UChar** )low_prev);
+ if (low_prev && IS_NULL(*low_prev))
+ *low_prev = onigenc_get_prev_char_head(reg->enc,
+ (pprev ? pprev : s), *low, end);
+ }
+ else {
+ if (low_prev)
+ *low_prev = onigenc_get_prev_char_head(reg->enc,
+ (pprev ? pprev : str), *low, end);
+ }
}
}
}
@@ -3952,7 +4041,6 @@ backward_search_range(regex_t* reg, const UChar* str, const UChar* end,
UChar* s, const UChar* range, UChar* adjrange,
UChar** low, UChar** high)
{
- int r;
UChar *p;
range += reg->dmin;
@@ -3970,13 +4058,15 @@ backward_search_range(regex_t* reg, const UChar* str, const UChar* end,
case ONIG_OPTIMIZE_EXACT_BM_IC:
case ONIG_OPTIMIZE_EXACT_BM_NOT_REV_IC:
p = slow_search_backward_ic(reg->enc, reg->case_fold_flag,
- reg->exact, reg->exact_end,
- range, adjrange, end, p);
+ reg->exact, reg->exact_end,
+ range, adjrange, end, p);
break;
case ONIG_OPTIMIZE_EXACT_BM:
case ONIG_OPTIMIZE_EXACT_BM_NOT_REV:
+#ifdef USE_INT_MAP_BACKWARD
if (IS_NULL(reg->int_map_backward)) {
+ int r;
if (s - range < BM_BACKWARD_SEARCH_LENGTH_THRESHOLD)
goto exact_method;
@@ -3986,6 +4076,9 @@ backward_search_range(regex_t* reg, const UChar* str, const UChar* end,
}
p = bm_search_backward(reg, reg->exact, reg->exact_end, range, adjrange,
end, p);
+#else
+ goto exact_method;
+#endif
break;
case ONIG_OPTIMIZE_MAP:
@@ -4070,42 +4163,13 @@ onig_search_gpos(regex_t* reg, const UChar* str, const UChar* end,
const UChar *orig_range = range;
#endif
-#if defined(USE_RECOMPILE_API) && defined(USE_MULTI_THREAD_SYSTEM)
- start:
- THREAD_ATOMIC_START;
- if (ONIG_STATE(reg) >= ONIG_STATE_NORMAL) {
- ONIG_STATE_INC(reg);
- if (IS_NOT_NULL(reg->chain) && ONIG_STATE(reg) == ONIG_STATE_NORMAL) {
- onig_chain_reduce(reg);
- ONIG_STATE_INC(reg);
- }
- }
- else {
- int n;
-
- THREAD_ATOMIC_END;
- n = 0;
- while (ONIG_STATE(reg) < ONIG_STATE_NORMAL) {
- if (++n > THREAD_PASS_LIMIT_COUNT)
- return ONIGERR_OVER_THREAD_PASS_LIMIT_COUNT;
- THREAD_PASS;
- }
- goto start;
- }
- THREAD_ATOMIC_END;
-#endif /* USE_RECOMPILE_API && USE_MULTI_THREAD_SYSTEM */
-
#ifdef ONIG_DEBUG_SEARCH
fprintf(stderr,
"onig_search (entry point): str: %"PRIuPTR" (%p), end: %"PRIuPTR", start: %"PRIuPTR", range: %"PRIuPTR"\n",
- (intptr_t )str, str, end - str, start - str, range - str);
+ (uintptr_t )str, str, end - str, start - str, range - str);
#endif
- if (region
-#ifdef USE_POSIX_API_REGION_OPTION
- && !IS_POSIX_REGION(option)
-#endif
- ) {
+ if (region) {
r = onig_region_resize_clear(region, reg->num_mem + 1);
if (r) goto finish_no_msa;
}
@@ -4114,8 +4178,8 @@ onig_search_gpos(regex_t* reg, const UChar* str, const UChar* end,
#ifdef USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE
-#ifdef USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE
-#define MATCH_AND_RETURN_CHECK(upper_range) \
+# ifdef USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE
+# define MATCH_AND_RETURN_CHECK(upper_range) \
r = match_at(reg, str, end, (upper_range), s, prev, &msa); \
if (r != ONIG_MISMATCH) {\
if (r >= 0) {\
@@ -4125,8 +4189,8 @@ onig_search_gpos(regex_t* reg, const UChar* str, const UChar* end,
}\
else goto finish; /* error */ \
}
-#else
-#define MATCH_AND_RETURN_CHECK(upper_range) \
+# else
+# define MATCH_AND_RETURN_CHECK(upper_range) \
r = match_at(reg, str, end, (upper_range), s, prev, &msa); \
if (r != ONIG_MISMATCH) {\
if (r >= 0) {\
@@ -4134,10 +4198,10 @@ onig_search_gpos(regex_t* reg, const UChar* str, const UChar* end,
}\
else goto finish; /* error */ \
}
-#endif /* USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE */
+# endif /* USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE */
#else
-#ifdef USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE
-#define MATCH_AND_RETURN_CHECK(none) \
+# ifdef USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE
+# define MATCH_AND_RETURN_CHECK(none) \
r = match_at(reg, str, end, s, prev, &msa);\
if (r != ONIG_MISMATCH) {\
if (r >= 0) {\
@@ -4147,8 +4211,8 @@ onig_search_gpos(regex_t* reg, const UChar* str, const UChar* end,
}\
else goto finish; /* error */ \
}
-#else
-#define MATCH_AND_RETURN_CHECK(none) \
+# else
+# define MATCH_AND_RETURN_CHECK(none) \
r = match_at(reg, str, end, s, prev, &msa);\
if (r != ONIG_MISMATCH) {\
if (r >= 0) {\
@@ -4156,7 +4220,7 @@ onig_search_gpos(regex_t* reg, const UChar* str, const UChar* end,
}\
else goto finish; /* error */ \
}
-#endif /* USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE */
+# endif /* USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE */
#endif /* USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE */
@@ -4168,7 +4232,15 @@ onig_search_gpos(regex_t* reg, const UChar* str, const UChar* end,
/* search start-position only */
begin_position:
if (range > start)
- range = start + 1;
+ {
+ if (global_pos > start)
+ {
+ if (global_pos < range)
+ range = global_pos + 1;
+ }
+ else
+ range = start + 1;
+ }
else
range = start;
}
@@ -4244,9 +4316,7 @@ onig_search_gpos(regex_t* reg, const UChar* str, const UChar* end,
}
}
else if ((reg->anchor & ANCHOR_ANYCHAR_STAR_ML)) {
- if (! (reg->anchor & ANCHOR_LOOK_BEHIND)) {
- goto begin_position;
- }
+ goto begin_position;
}
}
else if (str == end) { /* empty string */
@@ -4306,7 +4376,7 @@ onig_search_gpos(regex_t* reg, const UChar* str, const UChar* end,
}
if ((end - start) < reg->threshold_len)
- goto mismatch;
+ goto mismatch;
if (reg->dmax != ONIG_INFINITE_DISTANCE) {
do {
@@ -4328,24 +4398,22 @@ onig_search_gpos(regex_t* reg, const UChar* str, const UChar* end,
if (! forward_search_range(reg, str, end, s, sch_range,
&low, &high, (UChar** )NULL)) goto mismatch;
- if ((reg->anchor & ANCHOR_ANYCHAR_STAR) != 0) {
- do {
- if ((reg->anchor & ANCHOR_BEGIN_POSITION) == 0)
- msa.gpos = s; /* move \G position */
- MATCH_AND_RETURN_CHECK(orig_range);
- prev = s;
- s += enclen(reg->enc, s, end);
-
- if ((reg->anchor & (ANCHOR_LOOK_BEHIND | ANCHOR_PREC_READ_NOT)) == 0) {
- while (!ONIGENC_IS_MBC_NEWLINE_EX(reg->enc, prev, str, end, reg->options, 0)
- && s < range) {
- prev = s;
- s += enclen(reg->enc, s, end);
- }
- }
- } while (s < range);
- goto mismatch;
- }
+ if ((reg->anchor & ANCHOR_ANYCHAR_STAR) != 0) {
+ do {
+ MATCH_AND_RETURN_CHECK(orig_range);
+ prev = s;
+ s += enclen(reg->enc, s, end);
+
+ if ((reg->anchor & (ANCHOR_LOOK_BEHIND | ANCHOR_PREC_READ_NOT)) == 0) {
+ while (!ONIGENC_IS_MBC_NEWLINE_EX(reg->enc, prev, str, end, reg->options, 0)
+ && s < range) {
+ prev = s;
+ s += enclen(reg->enc, s, end);
+ }
+ }
+ } while (s < range);
+ goto mismatch;
+ }
}
}
@@ -4428,15 +4496,10 @@ onig_search_gpos(regex_t* reg, const UChar* str, const UChar* end,
finish:
MATCH_ARG_FREE(msa);
- ONIG_STATE_DEC_THREAD(reg);
/* If result is mismatch and no FIND_NOT_EMPTY option,
then the region is not set in match_at(). */
- if (IS_FIND_NOT_EMPTY(reg->options) && region
-#ifdef USE_POSIX_API_REGION_OPTION
- && !IS_POSIX_REGION(option)
-#endif
- ) {
+ if (IS_FIND_NOT_EMPTY(reg->options) && region) {
onig_region_clear(region);
}
@@ -4449,7 +4512,6 @@ onig_search_gpos(regex_t* reg, const UChar* str, const UChar* end,
mismatch_no_msa:
r = ONIG_MISMATCH;
finish_no_msa:
- ONIG_STATE_DEC_THREAD(reg);
#ifdef ONIG_DEBUG
if (r != ONIG_MISMATCH)
fprintf(stderr, "onig_search: error %"PRIdPTRDIFF"\n", r);
@@ -4457,43 +4519,84 @@ onig_search_gpos(regex_t* reg, const UChar* str, const UChar* end,
return r;
match:
- ONIG_STATE_DEC_THREAD(reg);
MATCH_ARG_FREE(msa);
return s - str;
}
+extern OnigPosition
+onig_scan(regex_t* reg, const UChar* str, const UChar* end,
+ OnigRegion* region, OnigOptionType option,
+ int (*scan_callback)(OnigPosition, OnigPosition, OnigRegion*, void*),
+ void* callback_arg)
+{
+ OnigPosition r;
+ OnigPosition n;
+ int rs;
+ const UChar* start;
+
+ n = 0;
+ start = str;
+ while (1) {
+ r = onig_search(reg, str, end, start, end, region, option);
+ if (r >= 0) {
+ rs = scan_callback(n, r, region, callback_arg);
+ n++;
+ if (rs != 0)
+ return rs;
+
+ if (region->end[0] == start - str) {
+ if (start >= end) break;
+ start += enclen(reg->enc, start, end);
+ }
+ else
+ start = str + region->end[0];
+
+ if (start > end)
+ break;
+ }
+ else if (r == ONIG_MISMATCH) {
+ break;
+ }
+ else { /* error */
+ return r;
+ }
+ }
+
+ return n;
+}
+
extern OnigEncoding
-onig_get_encoding(regex_t* reg)
+onig_get_encoding(const regex_t* reg)
{
return reg->enc;
}
extern OnigOptionType
-onig_get_options(regex_t* reg)
+onig_get_options(const regex_t* reg)
{
return reg->options;
}
extern OnigCaseFoldType
-onig_get_case_fold_flag(regex_t* reg)
+onig_get_case_fold_flag(const regex_t* reg)
{
return reg->case_fold_flag;
}
extern const OnigSyntaxType*
-onig_get_syntax(regex_t* reg)
+onig_get_syntax(const regex_t* reg)
{
return reg->syntax;
}
extern int
-onig_number_of_captures(regex_t* reg)
+onig_number_of_captures(const regex_t* reg)
{
return reg->num_mem;
}
extern int
-onig_number_of_capture_histories(regex_t* reg)
+onig_number_of_capture_histories(const regex_t* reg)
{
#ifdef USE_CAPTURE_HISTORY
int i, n;
@@ -4514,4 +4617,3 @@ onig_copy_encoding(OnigEncodingType *to, OnigEncoding from)
{
*to = *from;
}
-
diff --git a/regint.h b/regint.h
index 80d3523126..a2f5bbba1d 100644
--- a/regint.h
+++ b/regint.h
@@ -1,11 +1,11 @@
-#ifndef ONIGURUMA_REGINT_H
-#define ONIGURUMA_REGINT_H
+#ifndef ONIGMO_REGINT_H
+#define ONIGMO_REGINT_H
/**********************************************************************
regint.h - Onigmo (Oniguruma-mod) (regular expression library)
**********************************************************************/
/*-
* Copyright (c) 2002-2013 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
- * Copyright (c) 2011-2014 K.Takata <kentkt AT csc DOT jp>
+ * Copyright (c) 2011-2016 K.Takata <kentkt AT csc DOT jp>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -35,6 +35,7 @@
/* #define ONIG_DEBUG_COMPILE */
/* #define ONIG_DEBUG_SEARCH */
/* #define ONIG_DEBUG_MATCH */
+/* #define ONIG_DEBUG_MEMLEAK */
/* #define ONIG_DONT_OPTIMIZE */
/* for byte-code statistical data. */
@@ -42,25 +43,25 @@
#if defined(ONIG_DEBUG_PARSE_TREE) || defined(ONIG_DEBUG_MATCH) || \
defined(ONIG_DEBUG_SEARCH) || defined(ONIG_DEBUG_COMPILE) || \
- defined(ONIG_DEBUG_STATISTICS)
-#ifndef ONIG_DEBUG
-#define ONIG_DEBUG
-#endif
+ defined(ONIG_DEBUG_STATISTICS) || defined(ONIG_DEBUG_MEMLEAK)
+# ifndef ONIG_DEBUG
+# define ONIG_DEBUG
+# endif
#endif
#ifndef UNALIGNED_WORD_ACCESS
-#if defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
- defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || \
- defined(__powerpc64__) || \
- defined(__mc68020__)
-#define UNALIGNED_WORD_ACCESS 1
-#else
-#define UNALIGNED_WORD_ACCESS 0
-#endif
+# if defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
+ defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || \
+ defined(__powerpc64__) || \
+ defined(__mc68020__)
+# define UNALIGNED_WORD_ACCESS 1
+# else
+# define UNALIGNED_WORD_ACCESS 0
+# endif
#endif
#if UNALIGNED_WORD_ACCESS
-#define PLATFORM_UNALIGNED_WORD_ACCESS
+# define PLATFORM_UNALIGNED_WORD_ACCESS
#endif
/* config */
@@ -73,213 +74,165 @@
#define USE_MONOMANIAC_CHECK_CAPTURES_IN_ENDLESS_REPEAT /* /(?:()|())*\2/ */
#define USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE /* /\n$/ =~ "\n" */
#define USE_WARNING_REDUNDANT_NESTED_REPEAT_OPERATOR
-/* #define USE_RECOMPILE_API */
/* !!! moved to regenc.h. */ /* #define USE_CRNL_AS_LINE_TERMINATOR */
#define USE_NO_INVALID_QUANTIFIER
/* internal config */
-#define USE_PARSE_TREE_NODE_RECYCLE
-#define USE_OP_PUSH_OR_JUMP_EXACT
+/* #define USE_OP_PUSH_OR_JUMP_EXACT */
#define USE_QTFR_PEEK_NEXT
#define USE_ST_LIBRARY
-#define USE_SHARED_CCLASS_TABLE
#define USE_SUNDAY_QUICK_SEARCH
#define INIT_MATCH_STACK_SIZE 160
#define DEFAULT_MATCH_STACK_LIMIT_SIZE 0 /* unlimited */
+#define DEFAULT_PARSE_DEPTH_LIMIT 4096
+
+#define OPT_EXACT_MAXLEN 24
/* check config */
#if defined(USE_PERL_SUBEXP_CALL) || defined(USE_CAPITAL_P_NAMED_GROUP)
-#if !defined(USE_NAMED_GROUP) || !defined(USE_SUBEXP_CALL)
-#error USE_NAMED_GROUP and USE_SUBEXP_CALL must be defined.
-#endif
+# if !defined(USE_NAMED_GROUP) || !defined(USE_SUBEXP_CALL)
+# error USE_NAMED_GROUP and USE_SUBEXP_CALL must be defined.
+# endif
#endif
#if defined(__GNUC__)
-# define ARG_UNUSED __attribute__ ((unused))
+# define ARG_UNUSED __attribute__ ((unused))
#else
-# define ARG_UNUSED
+# define ARG_UNUSED
#endif
-#ifndef RUBY_DEFINES_H
-#include "ruby/ruby.h"
-#undef xmalloc
-#undef xrealloc
-#undef xcalloc
-#undef xfree
+#if !defined(RUBY) && defined(RUBY_EXPORT)
+# define RUBY
#endif
+#ifdef RUBY
+# ifndef RUBY_DEFINES_H
+# include "ruby/ruby.h"
+# undef xmalloc
+# undef xrealloc
+# undef xcalloc
+# undef xfree
+# endif
+#else /* RUBY */
+# include "config.h"
+# if SIZEOF_LONG_LONG > 0
+# define LONG_LONG long long
+# endif
+#endif /* RUBY */
+
+#include <stdarg.h>
/* */
/* escape other system UChar definition */
#ifdef ONIG_ESCAPE_UCHAR_COLLISION
-#undef ONIG_ESCAPE_UCHAR_COLLISION
+# undef ONIG_ESCAPE_UCHAR_COLLISION
#endif
#define USE_WORD_BEGIN_END /* "\<": word-begin, "\>": word-end */
-#undef USE_CAPTURE_HISTORY
+#ifdef RUBY
+# undef USE_CAPTURE_HISTORY
+#else
+# define USE_CAPTURE_HISTORY
+#endif
#define USE_VARIABLE_META_CHARS
-#define USE_POSIX_API_REGION_OPTION /* needed for POSIX API support */
#define USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE
/* #define USE_COMBINATION_EXPLOSION_CHECK */ /* (X*)* */
-/* multithread config */
-/* #define USE_MULTI_THREAD_SYSTEM */
-/* #define USE_DEFAULT_MULTI_THREAD_SYSTEM */
-
-#if defined(USE_MULTI_THREAD_SYSTEM) \
- && defined(USE_DEFAULT_MULTI_THREAD_SYSTEM)
-
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-extern CRITICAL_SECTION gOnigMutex;
-#define THREAD_SYSTEM_INIT InitializeCriticalSection(&gOnigMutex)
-#define THREAD_SYSTEM_END DeleteCriticalSection(&gOnigMutex)
-#define THREAD_ATOMIC_START EnterCriticalSection(&gOnigMutex)
-#define THREAD_ATOMIC_END LeaveCriticalSection(&gOnigMutex)
-#define THREAD_PASS Sleep(0)
-#else /* _WIN32 */
-#include <pthread.h>
-#include <sched.h>
-extern pthread_mutex_t gOnigMutex;
-#define THREAD_SYSTEM_INIT pthread_mutex_init(&gOnigMutex, NULL)
-#define THREAD_SYSTEM_END pthread_mutex_destroy(&gOnigMutex)
-#define THREAD_ATOMIC_START pthread_mutex_lock(&gOnigMutex)
-#define THREAD_ATOMIC_END pthread_mutex_unlock(&gOnigMutex)
-#define THREAD_PASS sched_yield()
-#endif /* _WIN32 */
-
-#else /* USE_DEFAULT_MULTI_THREAD_SYSTEM */
-
-#ifndef THREAD_SYSTEM_INIT
-#define THREAD_SYSTEM_INIT /* depend on thread system */
-#endif
-#ifndef THREAD_SYSTEM_END
-#define THREAD_SYSTEM_END /* depend on thread system */
-#endif
-#ifndef THREAD_ATOMIC_START
-#define THREAD_ATOMIC_START /* depend on thread system */
-#endif
-#ifndef THREAD_ATOMIC_END
-#define THREAD_ATOMIC_END /* depend on thread system */
-#endif
-#ifndef THREAD_PASS
-#define THREAD_PASS /* depend on thread system */
-#endif
-
-#endif /* USE_DEFAULT_MULTI_THREAD_SYSTEM */
#ifndef xmalloc
-#define xmalloc malloc
-#define xrealloc realloc
-#define xcalloc calloc
-#define xfree free
+# define xmalloc malloc
+# define xrealloc realloc
+# define xcalloc calloc
+# define xfree free
#endif
#ifdef RUBY
-#define CHECK_INTERRUPT_IN_MATCH_AT rb_thread_check_ints()
-#define onig_st_init_table st_init_table
-#define onig_st_init_table_with_size st_init_table_with_size
-#define onig_st_init_numtable st_init_numtable
-#define onig_st_init_numtable_with_size st_init_numtable_with_size
-#define onig_st_init_strtable st_init_strtable
-#define onig_st_init_strtable_with_size st_init_strtable_with_size
-#define onig_st_delete st_delete
-#define onig_st_delete_safe st_delete_safe
-#define onig_st_insert st_insert
-#define onig_st_lookup st_lookup
-#define onig_st_foreach st_foreach
-#define onig_st_add_direct st_add_direct
-#define onig_st_free_table st_free_table
-#define onig_st_cleanup_safe st_cleanup_safe
-#define onig_st_copy st_copy
-#define onig_st_nothing_key_clone st_nothing_key_clone
-#define onig_st_nothing_key_free st_nothing_key_free
-#define onig_st_is_member st_is_member
-
-#define USE_UPPER_CASE_TABLE
-#else
-
-#define CHECK_INTERRUPT_IN_MATCH_AT
-
-#define st_init_table onig_st_init_table
-#define st_init_table_with_size onig_st_init_table_with_size
-#define st_init_numtable onig_st_init_numtable
-#define st_init_numtable_with_size onig_st_init_numtable_with_size
-#define st_init_strtable onig_st_init_strtable
-#define st_init_strtable_with_size onig_st_init_strtable_with_size
-#define st_delete onig_st_delete
-#define st_delete_safe onig_st_delete_safe
-#define st_insert onig_st_insert
-#define st_lookup onig_st_lookup
-#define st_foreach onig_st_foreach
-#define st_add_direct onig_st_add_direct
-#define st_free_table onig_st_free_table
-#define st_cleanup_safe onig_st_cleanup_safe
-#define st_copy onig_st_copy
-#define st_nothing_key_clone onig_st_nothing_key_clone
-#define st_nothing_key_free onig_st_nothing_key_free
+# define CHECK_INTERRUPT_IN_MATCH_AT rb_thread_check_ints()
+# define onig_st_init_table st_init_table
+# define onig_st_init_table_with_size st_init_table_with_size
+# define onig_st_init_numtable st_init_numtable
+# define onig_st_init_numtable_with_size st_init_numtable_with_size
+# define onig_st_init_strtable st_init_strtable
+# define onig_st_init_strtable_with_size st_init_strtable_with_size
+# define onig_st_delete st_delete
+# define onig_st_delete_safe st_delete_safe
+# define onig_st_insert st_insert
+# define onig_st_lookup st_lookup
+# define onig_st_foreach st_foreach
+# define onig_st_add_direct st_add_direct
+# define onig_st_free_table st_free_table
+# define onig_st_cleanup_safe st_cleanup_safe
+# define onig_st_copy st_copy
+# define onig_st_nothing_key_clone st_nothing_key_clone
+# define onig_st_nothing_key_free st_nothing_key_free
+# define onig_st_is_member st_is_member
+
+# define USE_UPPER_CASE_TABLE
+#else /* RUBY */
+
+# define CHECK_INTERRUPT_IN_MATCH_AT
+
+# define st_init_table onig_st_init_table
+# define st_init_table_with_size onig_st_init_table_with_size
+# define st_init_numtable onig_st_init_numtable
+# define st_init_numtable_with_size onig_st_init_numtable_with_size
+# define st_init_strtable onig_st_init_strtable
+# define st_init_strtable_with_size onig_st_init_strtable_with_size
+# define st_delete onig_st_delete
+# define st_delete_safe onig_st_delete_safe
+# define st_insert onig_st_insert
+# define st_lookup onig_st_lookup
+# define st_foreach onig_st_foreach
+# define st_add_direct onig_st_add_direct
+# define st_free_table onig_st_free_table
+# define st_cleanup_safe onig_st_cleanup_safe
+# define st_copy onig_st_copy
+# define st_nothing_key_clone onig_st_nothing_key_clone
+# define st_nothing_key_free onig_st_nothing_key_free
/* */
-#define onig_st_is_member st_is_member
+# define onig_st_is_member st_is_member
-#endif
+#endif /* RUBY */
#define STATE_CHECK_STRING_THRESHOLD_LEN 7
#define STATE_CHECK_BUFF_MAX_SIZE 0x4000
-#define THREAD_PASS_LIMIT_COUNT 8
#define xmemset memset
#define xmemcpy memcpy
#define xmemmove memmove
-#if defined(_WIN32) && !defined(__GNUC__)
-#define xalloca _alloca
-#define xvsnprintf _vsnprintf
+#if ((defined(RUBY_MSVCRT_VERSION) && RUBY_MSVCRT_VERSION >= 90) \
+ || (!defined(RUBY_MSVCRT_VERSION) && defined(_WIN32))) \
+ && !defined(__GNUC__)
+# define xalloca _alloca
+# define xvsnprintf(buf,size,fmt,args) _vsnprintf_s(buf,size,_TRUNCATE,fmt,args)
+# define xsnprintf sprintf_s
+# define xstrcat(dest,src,size) strcat_s(dest,size,src)
#else
-#define xalloca alloca
-#define xvsnprintf vsnprintf
+# define xalloca alloca
+# define xvsnprintf vsnprintf
+# define xsnprintf snprintf
+# define xstrcat(dest,src,size) strcat(dest,src)
#endif
+#if defined(ONIG_DEBUG_MEMLEAK) && defined(_MSC_VER)
+# define _CRTDBG_MAP_ALLOC
+# include <malloc.h>
+# include <crtdbg.h>
+#endif
-#if defined(USE_RECOMPILE_API) && defined(USE_MULTI_THREAD_SYSTEM)
-#define ONIG_STATE_INC(reg) (reg)->state++
-#define ONIG_STATE_DEC(reg) (reg)->state--
-
-#define ONIG_STATE_INC_THREAD(reg) do {\
- THREAD_ATOMIC_START;\
- (reg)->state++;\
- THREAD_ATOMIC_END;\
-} while(0)
-#define ONIG_STATE_DEC_THREAD(reg) do {\
- THREAD_ATOMIC_START;\
- (reg)->state--;\
- THREAD_ATOMIC_END;\
-} while(0)
-#else
-#define ONIG_STATE_INC(reg) /* Nothing */
-#define ONIG_STATE_DEC(reg) /* Nothing */
-#define ONIG_STATE_INC_THREAD(reg) /* Nothing */
-#define ONIG_STATE_DEC_THREAD(reg) /* Nothing */
-#endif /* USE_RECOMPILE_API && USE_MULTI_THREAD_SYSTEM */
-
-#ifdef HAVE_STDLIB_H
#include <stdlib.h>
-#endif
#if defined(HAVE_ALLOCA_H) && (defined(_AIX) || !defined(__GNUC__))
-#include <alloca.h>
+# include <alloca.h>
#endif
-#ifdef HAVE_STRING_H
-# include <string.h>
-#else
-# include <strings.h>
-#endif
+#include <string.h>
#include <ctype.h>
#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
+# include <sys/types.h>
#endif
#ifdef HAVE_STDINT_H
@@ -290,12 +243,10 @@ extern pthread_mutex_t gOnigMutex;
# include <inttypes.h>
#endif
-#ifdef STDC_HEADERS
-# include <stddef.h>
-#endif
+#include <stddef.h>
#ifdef _WIN32
-#include <malloc.h> /* for alloca() */
+# include <malloc.h> /* for alloca() */
#endif
#ifdef ONIG_DEBUG
@@ -303,28 +254,32 @@ extern pthread_mutex_t gOnigMutex;
#endif
#ifdef _WIN32
-#if defined(_MSC_VER) && (_MSC_VER < 1300)
-#ifndef _INTPTR_T_DEFINED
-#define _INTPTR_T_DEFINED
+# if defined(_MSC_VER) && (_MSC_VER < 1300)
+# ifndef _INTPTR_T_DEFINED
+# define _INTPTR_T_DEFINED
typedef int intptr_t;
-#endif
-#ifndef _UINTPTR_T_DEFINED
-#define _UINTPTR_T_DEFINED
+# endif
+# ifndef _UINTPTR_T_DEFINED
+# define _UINTPTR_T_DEFINED
typedef unsigned int uintptr_t;
-#endif
-#endif
+# endif
+# endif
#endif /* _WIN32 */
#ifndef PRIdPTR
-#ifdef _WIN64
-#define PRIdPTR "I64d"
-#define PRIuPTR "I64u"
-#define PRIxPTR "I64x"
-#else
-#define PRIdPTR "ld"
-#define PRIuPTR "lu"
-#define PRIxPTR "lx"
+# ifdef _WIN64
+# define PRIdPTR "I64d"
+# define PRIuPTR "I64u"
+# define PRIxPTR "I64x"
+# else
+# define PRIdPTR "ld"
+# define PRIuPTR "lu"
+# define PRIxPTR "lx"
+# endif
#endif
+
+#ifndef PRIdPTRDIFF
+# define PRIdPTRDIFF PRIdPTR
#endif
#include "regenc.h"
@@ -332,10 +287,10 @@ typedef unsigned int uintptr_t;
RUBY_SYMBOL_EXPORT_BEGIN
#ifdef MIN
-#undef MIN
+# undef MIN
#endif
#ifdef MAX
-#undef MAX
+# undef MAX
#endif
#define MIN(a,b) (((a)>(b))?(b):(a))
#define MAX(a,b) (((a)<(b))?(b):(a))
@@ -350,28 +305,28 @@ RUBY_SYMBOL_EXPORT_BEGIN
#ifdef PLATFORM_UNALIGNED_WORD_ACCESS
-#define PLATFORM_GET_INC(val,p,type) do{\
+# define PLATFORM_GET_INC(val,p,type) do{\
val = *(type* )p;\
(p) += sizeof(type);\
} while(0)
#else
-#define PLATFORM_GET_INC(val,p,type) do{\
+# define PLATFORM_GET_INC(val,p,type) do{\
xmemcpy(&val, (p), sizeof(type));\
(p) += sizeof(type);\
} while(0)
/* sizeof(OnigCodePoint) */
-#define WORD_ALIGNMENT_SIZE SIZEOF_LONG
+# define WORD_ALIGNMENT_SIZE SIZEOF_LONG
-#define GET_ALIGNMENT_PAD_SIZE(addr,pad_size) do {\
+# define GET_ALIGNMENT_PAD_SIZE(addr,pad_size) do {\
(pad_size) = WORD_ALIGNMENT_SIZE \
- ((uintptr_t )(addr) % WORD_ALIGNMENT_SIZE);\
if ((pad_size) == WORD_ALIGNMENT_SIZE) (pad_size) = 0;\
} while (0)
-#define ALIGNMENT_RIGHT(addr) do {\
+# define ALIGNMENT_RIGHT(addr) do {\
(addr) += (WORD_ALIGNMENT_SIZE - 1);\
(addr) -= ((uintptr_t )(addr) % WORD_ALIGNMENT_SIZE);\
} while (0)
@@ -435,7 +390,6 @@ typedef unsigned int BitStatusType;
#define IS_NOTEOL(option) ((option) & ONIG_OPTION_NOTEOL)
#define IS_NOTBOS(option) ((option) & ONIG_OPTION_NOTBOS)
#define IS_NOTEOS(option) ((option) & ONIG_OPTION_NOTEOS)
-#define IS_POSIX_REGION(option) ((option) & ONIG_OPTION_POSIX_REGION)
#define IS_ASCII_RANGE(option) ((option) & ONIG_OPTION_ASCII_RANGE)
#define IS_POSIX_BRACKET_ALL_RANGE(option) ((option) & ONIG_OPTION_POSIX_BRACKET_ALL_RANGE)
#define IS_WORD_BOUND_ALL_RANGE(option) ((option) & ONIG_OPTION_WORD_BOUND_ALL_RANGE)
@@ -618,7 +572,6 @@ enum OpCode {
OP_CCLASS_NOT,
OP_CCLASS_MB_NOT,
OP_CCLASS_MIX_NOT,
- OP_CCLASS_NODE, /* pointer to CClassNode node */
OP_ANYCHAR, /* "." */
OP_ANYCHAR_ML, /* "." multi-line */
@@ -647,7 +600,6 @@ enum OpCode {
OP_END_LINE,
OP_SEMI_END_BUF,
OP_BEGIN_POSITION,
- OP_BEGIN_POS_OR_LINE, /* used for implicit anchor optimization */
OP_BACKREF1,
OP_BACKREF2,
@@ -692,6 +644,9 @@ enum OpCode {
OP_LOOK_BEHIND, /* (?<=...) start (no needs end opcode) */
OP_PUSH_LOOK_BEHIND_NOT, /* (?<!...) start */
OP_FAIL_LOOK_BEHIND_NOT, /* (?<!...) end */
+ OP_PUSH_ABSENT_POS, /* (?~...) start */
+ OP_ABSENT, /* (?~...) start of inner loop */
+ OP_ABSENT_END, /* (?~...) end */
OP_CALL, /* \g<name> */
OP_RETURN,
@@ -779,12 +734,15 @@ typedef void* PointerType;
#define SIZE_OP_CALL (SIZE_OPCODE + SIZE_ABSADDR)
#define SIZE_OP_RETURN SIZE_OPCODE
#define SIZE_OP_CONDITION (SIZE_OPCODE + SIZE_MEMNUM + SIZE_RELADDR)
+#define SIZE_OP_PUSH_ABSENT_POS SIZE_OPCODE
+#define SIZE_OP_ABSENT (SIZE_OPCODE + SIZE_RELADDR)
+#define SIZE_OP_ABSENT_END SIZE_OPCODE
#ifdef USE_COMBINATION_EXPLOSION_CHECK
-#define SIZE_OP_STATE_CHECK (SIZE_OPCODE + SIZE_STATE_CHECK_NUM)
-#define SIZE_OP_STATE_CHECK_PUSH (SIZE_OPCODE + SIZE_STATE_CHECK_NUM + SIZE_RELADDR)
-#define SIZE_OP_STATE_CHECK_PUSH_OR_JUMP (SIZE_OPCODE + SIZE_STATE_CHECK_NUM + SIZE_RELADDR)
-#define SIZE_OP_STATE_CHECK_ANYCHAR_STAR (SIZE_OPCODE + SIZE_STATE_CHECK_NUM)
+# define SIZE_OP_STATE_CHECK (SIZE_OPCODE + SIZE_STATE_CHECK_NUM)
+# define SIZE_OP_STATE_CHECK_PUSH (SIZE_OPCODE + SIZE_STATE_CHECK_NUM + SIZE_RELADDR)
+# define SIZE_OP_STATE_CHECK_PUSH_OR_JUMP (SIZE_OPCODE + SIZE_STATE_CHECK_NUM + SIZE_RELADDR)
+# define SIZE_OP_STATE_CHECK_ANYCHAR_STAR (SIZE_OPCODE + SIZE_STATE_CHECK_NUM)
#endif
#define MC_ESC(syn) (syn)->meta_char_table.esc
@@ -832,13 +790,10 @@ typedef void* PointerType;
/* cclass node */
#define FLAG_NCCLASS_NOT (1<<0)
-#define FLAG_NCCLASS_SHARE (1<<1)
#define NCCLASS_SET_NOT(nd) NCCLASS_FLAG_SET(nd, FLAG_NCCLASS_NOT)
-#define NCCLASS_SET_SHARE(nd) NCCLASS_FLAG_SET(nd, FLAG_NCCLASS_SHARE)
#define NCCLASS_CLEAR_NOT(nd) NCCLASS_FLAG_CLEAR(nd, FLAG_NCCLASS_NOT)
#define IS_NCCLASS_NOT(nd) IS_NCCLASS_FLAG_ON(nd, FLAG_NCCLASS_NOT)
-#define IS_NCCLASS_SHARE(nd) IS_NCCLASS_FLAG_ON(nd, FLAG_NCCLASS_SHARE)
typedef struct {
int type;
@@ -893,6 +848,10 @@ typedef struct _OnigStackType {
UChar *pstr; /* string position */
} call_frame;
#endif
+ struct {
+ UChar *abs_pstr; /* absent start position */
+ const UChar *end_pstr; /* end position */
+ } absent_pos;
} u;
} OnigStackType;
@@ -936,60 +895,44 @@ typedef struct {
extern OnigOpInfoType OnigOpInfo[];
-extern void onig_print_compiled_byte_code P_((FILE* f, UChar* bp, UChar* bpend, UChar** nextp, OnigEncoding enc));
+extern void onig_print_compiled_byte_code(FILE* f, UChar* bp, UChar* bpend, UChar** nextp, OnigEncoding enc);
-#ifdef ONIG_DEBUG_STATISTICS
-extern void onig_statistics_init P_((void));
-extern void onig_print_statistics P_((FILE* f));
-#endif
+# ifdef ONIG_DEBUG_STATISTICS
+extern void onig_statistics_init(void);
+extern void onig_print_statistics(FILE* f);
+# endif
#endif
-extern UChar* onig_error_code_to_format P_((OnigPosition code));
-extern void onig_snprintf_with_pattern PV_((UChar buf[], int bufsize, OnigEncoding enc, UChar* pat, UChar* pat_end, const UChar *fmt, ...));
-extern int onig_bbuf_init P_((BBuf* buf, OnigDistance size));
-extern int onig_compile P_((regex_t* reg, const UChar* pattern, const UChar* pattern_end, OnigErrorInfo* einfo, const char *sourcefile, int sourceline));
-extern void onig_chain_reduce P_((regex_t* reg));
-extern void onig_chain_link_add P_((regex_t* to, regex_t* add));
-extern void onig_transfer P_((regex_t* to, regex_t* from));
-extern int onig_is_code_in_cc P_((OnigEncoding enc, OnigCodePoint code, CClassNode* cc));
-extern int onig_is_code_in_cc_len P_((int enclen, OnigCodePoint code, CClassNode* cc));
+extern UChar* onig_error_code_to_format(OnigPosition code);
+extern void onig_vsnprintf_with_pattern(UChar buf[], int bufsize, OnigEncoding enc, UChar* pat, UChar* pat_end, const UChar *fmt, va_list args);
+extern void onig_snprintf_with_pattern(UChar buf[], int bufsize, OnigEncoding enc, UChar* pat, UChar* pat_end, const UChar *fmt, ...);
+extern int onig_bbuf_init(BBuf* buf, OnigDistance size);
+extern int onig_compile(regex_t* reg, const UChar* pattern, const UChar* pattern_end, OnigErrorInfo* einfo);
+#ifdef RUBY
+extern int onig_compile_ruby(regex_t* reg, const UChar* pattern, const UChar* pattern_end, OnigErrorInfo* einfo, const char *sourcefile, int sourceline);
+#endif
+extern void onig_transfer(regex_t* to, regex_t* from);
+extern int onig_is_code_in_cc(OnigEncoding enc, OnigCodePoint code, CClassNode* cc);
+extern int onig_is_code_in_cc_len(int enclen, OnigCodePoint code, CClassNode* cc);
/* strend hash */
typedef void hash_table_type;
#ifdef RUBY
-#include "ruby/st.h"
-typedef st_data_t hash_data_type;
+# include "ruby/st.h"
#else
-#include "st.h"
-typedef uintptr_t hash_data_type;
+# include "st.h"
#endif
+typedef st_data_t hash_data_type;
-extern hash_table_type* onig_st_init_strend_table_with_size P_((st_index_t size));
-extern int onig_st_lookup_strend P_((hash_table_type* table, const UChar* str_key, const UChar* end_key, hash_data_type *value));
-extern int onig_st_insert_strend P_((hash_table_type* table, const UChar* str_key, const UChar* end_key, hash_data_type value));
-
-/* encoding property management */
-#define PROPERTY_LIST_ADD_PROP(Name, CR) \
- r = onigenc_property_list_add_property((UChar* )Name, CR,\
- &PropertyNameTable, &PropertyList, &PropertyListNum,\
- &PropertyListSize);\
- if (r != 0) goto end
-
-#define PROPERTY_LIST_INIT_CHECK \
- if (PropertyInited == 0) {\
- int r = onigenc_property_list_init(init_property_list);\
- if (r != 0) return r;\
- }
-
-extern int onigenc_property_list_add_property P_((UChar* name, const OnigCodePoint* prop, hash_table_type **table, const OnigCodePoint*** plist, int *pnum, int *psize));
-
-typedef int (*ONIGENC_INIT_PROPERTY_LIST_FUNC_TYPE)(void);
-
-extern int onigenc_property_list_init P_((ONIGENC_INIT_PROPERTY_LIST_FUNC_TYPE));
+extern hash_table_type* onig_st_init_strend_table_with_size(st_index_t size);
+extern int onig_st_lookup_strend(hash_table_type* table, const UChar* str_key, const UChar* end_key, hash_data_type *value);
+extern int onig_st_insert_strend(hash_table_type* table, const UChar* str_key, const UChar* end_key, hash_data_type value);
-extern size_t onig_memsize P_((const regex_t *reg));
-extern size_t onig_region_memsize P_((const struct re_registers *regs));
+#ifdef RUBY
+extern size_t onig_memsize(const regex_t *reg);
+extern size_t onig_region_memsize(const struct re_registers *regs);
+#endif
RUBY_SYMBOL_EXPORT_END
-#endif /* ONIGURUMA_REGINT_H */
+#endif /* ONIGMO_REGINT_H */
diff --git a/regparse.c b/regparse.c
index 2924601bc2..574a07e05d 100644
--- a/regparse.c
+++ b/regparse.c
@@ -3,7 +3,7 @@
**********************************************************************/
/*-
* Copyright (c) 2002-2008 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
- * Copyright (c) 2011-2014 K.Takata <kentkt AT csc DOT jp>
+ * Copyright (c) 2011-2016 K.Takata <kentkt AT csc DOT jp>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,7 @@
*/
#include "regparse.h"
+#include <stdarg.h>
#define WARN_BUFSIZE 256
@@ -51,10 +52,14 @@ const OnigSyntaxType OnigSyntaxRuby = {
ONIG_SYN_OP2_CCLASS_SET_OP | ONIG_SYN_OP2_ESC_CAPITAL_C_BAR_CONTROL |
ONIG_SYN_OP2_ESC_CAPITAL_M_BAR_META | ONIG_SYN_OP2_ESC_V_VTAB |
ONIG_SYN_OP2_ESC_H_XDIGIT |
+#ifndef RUBY
+ ONIG_SYN_OP2_ESC_U_HEX4 |
+#endif
ONIG_SYN_OP2_ESC_CAPITAL_X_EXTENDED_GRAPHEME_CLUSTER |
ONIG_SYN_OP2_QMARK_LPAREN_CONDITION |
ONIG_SYN_OP2_ESC_CAPITAL_R_LINEBREAK |
- ONIG_SYN_OP2_ESC_CAPITAL_K_KEEP )
+ ONIG_SYN_OP2_ESC_CAPITAL_K_KEEP |
+ ONIG_SYN_OP2_QMARK_TILDE_ABSENT )
, ( SYN_GNU_REGEX_BV |
ONIG_SYN_ALLOW_INTERVAL_LOW_ABBREV |
ONIG_SYN_DIFFERENT_LEN_ALT_LOOK_BEHIND |
@@ -103,7 +108,27 @@ extern void onig_set_verb_warn_func(OnigWarnFunc f)
onig_verb_warn = f;
}
-static void CC_DUP_WARN(ScanEnv *env);
+static void CC_DUP_WARN(ScanEnv *env, OnigCodePoint from, OnigCodePoint to);
+
+
+static unsigned int ParseDepthLimit = DEFAULT_PARSE_DEPTH_LIMIT;
+
+extern unsigned int
+onig_get_parse_depth_limit(void)
+{
+ return ParseDepthLimit;
+}
+
+extern int
+onig_set_parse_depth_limit(unsigned int depth)
+{
+ if (depth == 0)
+ ParseDepthLimit = DEFAULT_PARSE_DEPTH_LIMIT;
+ else
+ ParseDepthLimit = depth;
+ return 0;
+}
+
static void
bbuf_free(BBuf* bbuf)
@@ -149,7 +174,7 @@ bbuf_clone(BBuf** rto, BBuf* from)
#define BITSET_SET_BIT_CHKDUP(bs, pos) do { \
- if (BITSET_AT(bs, pos)) CC_DUP_WARN(env); \
+ if (BITSET_AT(bs, pos)) CC_DUP_WARN(env, pos, pos); \
BS_ROOM(bs, pos) |= BS_BIT(pos); \
} while (0)
@@ -216,6 +241,7 @@ bitset_copy(BitSetRef dest, BitSetRef bs)
for (i = 0; i < BITSET_SIZE; i++) { dest[i] = bs[i]; }
}
+#if defined(USE_NAMED_GROUP) && !defined(USE_ST_LIBRARY)
extern int
onig_strncmp(const UChar* s1, const UChar* s2, int n)
{
@@ -227,6 +253,7 @@ onig_strncmp(const UChar* s1, const UChar* s2, int n)
}
return 0;
}
+#endif
extern void
onig_strcpy(UChar* dest, const UChar* src, const UChar* end)
@@ -265,9 +292,9 @@ strdup_with_null(OnigEncoding enc, UChar* s, UChar* end)
#ifdef __GNUC__
/* get rid of Wunused-but-set-variable and Wuninitialized */
-#define PFETCH_READY UChar* pfetch_prev = NULL; (void)pfetch_prev
+# define PFETCH_READY UChar* pfetch_prev = NULL; (void)pfetch_prev
#else
-#define PFETCH_READY UChar* pfetch_prev
+# define PFETCH_READY UChar* pfetch_prev
#endif
#define PEND (p < end ? 0 : 1)
#define PUNFETCH p = pfetch_prev
@@ -325,7 +352,11 @@ strcat_capa_from_static(UChar* dest, UChar* dest_end,
#ifdef USE_ST_LIBRARY
-#include "ruby/st.h"
+# ifdef RUBY
+# include "ruby/st.h"
+# else
+# include "st.h"
+# endif
typedef struct {
const UChar* s;
@@ -417,7 +448,7 @@ onig_st_insert_strend(hash_table_type* table, const UChar* str_key,
#ifdef USE_NAMED_GROUP
-#define INIT_NAME_BACKREFS_ALLOC_NUM 8
+# define INIT_NAME_BACKREFS_ALLOC_NUM 8
typedef struct {
UChar* name;
@@ -428,12 +459,12 @@ typedef struct {
int* back_refs;
} NameEntry;
-#ifdef USE_ST_LIBRARY
+# ifdef USE_ST_LIBRARY
typedef st_table NameTable;
typedef st_data_t HashDataType; /* 1.6 st.h doesn't define st_data_t type */
-#ifdef ONIG_DEBUG
+# ifdef ONIG_DEBUG
static int
i_print_name_entry(UChar* key, NameEntry* e, void* arg)
{
@@ -467,7 +498,7 @@ onig_print_names(FILE* fp, regex_t* reg)
}
return 0;
}
-#endif /* ONIG_DEBUG */
+# endif /* ONIG_DEBUG */
static int
i_free_name_entry(UChar* key, NameEntry* e, void* arg ARG_UNUSED)
@@ -530,8 +561,8 @@ static int
i_names(UChar* key ARG_UNUSED, NameEntry* e, INamesArg* arg)
{
int r = (*(arg->func))(e->name,
- e->name + e->name_len,
- e->back_num,
+ e->name + e->name_len,
+ e->back_num,
(e->back_num > 1 ? e->back_refs : &(e->back_ref1)),
arg->reg, arg->arg);
if (r != 0) {
@@ -589,7 +620,7 @@ onig_renumber_name_table(regex_t* reg, GroupNumRemap* map)
extern int
-onig_number_of_names(regex_t* reg)
+onig_number_of_names(const regex_t* reg)
{
NameTable* t = (NameTable* )reg->name_table;
@@ -599,9 +630,9 @@ onig_number_of_names(regex_t* reg)
return 0;
}
-#else /* USE_ST_LIBRARY */
+# else /* USE_ST_LIBRARY */
-#define INIT_NAMES_ALLOC_NUM 8
+# define INIT_NAMES_ALLOC_NUM 8
typedef struct {
NameEntry* e;
@@ -609,7 +640,7 @@ typedef struct {
int alloc;
} NameTable;
-#ifdef ONIG_DEBUG
+# ifdef ONIG_DEBUG
extern int
onig_print_names(FILE* fp, regex_t* reg)
{
@@ -640,7 +671,7 @@ onig_print_names(FILE* fp, regex_t* reg)
}
return 0;
}
-#endif
+# endif
static int
names_clear(regex_t* reg)
@@ -725,7 +756,7 @@ onig_foreach_name(regex_t* reg,
}
extern int
-onig_number_of_names(regex_t* reg)
+onig_number_of_names(const regex_t* reg)
{
NameTable* t = (NameTable* )reg->name_table;
@@ -735,7 +766,7 @@ onig_number_of_names(regex_t* reg)
return 0;
}
-#endif /* else USE_ST_LIBRARY */
+# endif /* else USE_ST_LIBRARY */
static int
name_add(regex_t* reg, UChar* name, UChar* name_end, int backref, ScanEnv* env)
@@ -749,7 +780,7 @@ name_add(regex_t* reg, UChar* name, UChar* name_end, int backref, ScanEnv* env)
e = name_find(reg, name, name_end);
if (IS_NULL(e)) {
-#ifdef USE_ST_LIBRARY
+# ifdef USE_ST_LIBRARY
if (IS_NULL(t)) {
t = onig_st_init_strend_table_with_size(5);
reg->name_table = (void* )t;
@@ -770,7 +801,7 @@ name_add(regex_t* reg, UChar* name, UChar* name_end, int backref, ScanEnv* env)
e->back_alloc = 0;
e->back_refs = (int* )NULL;
-#else
+# else
if (IS_NULL(t)) {
alloc = INIT_NAMES_ALLOC_NUM;
@@ -813,7 +844,7 @@ name_add(regex_t* reg, UChar* name, UChar* name_end, int backref, ScanEnv* env)
e->name = strdup_with_null(reg->enc, name, name_end);
if (IS_NULL(e->name)) return ONIGERR_MEMORY;
e->name_len = name_end - name;
-#endif
+# endif
}
if (e->back_num >= 1 &&
@@ -876,7 +907,7 @@ onig_name_to_group_numbers(regex_t* reg, const UChar* name,
extern int
onig_name_to_backref_number(regex_t* reg, const UChar* name,
- const UChar* name_end, OnigRegion *region)
+ const UChar* name_end, const OnigRegion *region)
{
int i, n, *nums;
@@ -909,7 +940,7 @@ onig_name_to_group_numbers(regex_t* reg, const UChar* name,
extern int
onig_name_to_backref_number(regex_t* reg, const UChar* name,
- const UChar* name_end, OnigRegion* region)
+ const UChar* name_end, const OnigRegion* region)
{
return ONIG_NO_SUPPORT_CONFIG;
}
@@ -922,14 +953,14 @@ onig_foreach_name(regex_t* reg,
}
extern int
-onig_number_of_names(regex_t* reg)
+onig_number_of_names(const regex_t* reg)
{
return 0;
}
#endif /* else USE_NAMED_GROUP */
extern int
-onig_noname_group_capture_is_active(regex_t* reg)
+onig_noname_group_capture_is_active(const regex_t* reg)
{
if (ONIG_IS_OPTION_ON(reg->options, ONIG_OPTION_DONT_CAPTURE_GROUP))
return 0;
@@ -976,6 +1007,7 @@ scan_env_clear(ScanEnv* env)
env->curr_max_regnum = 0;
env->has_recursion = 0;
#endif
+ env->parse_depth = 0;
env->warnings_flag = 0;
}
@@ -993,14 +1025,15 @@ scan_env_add_mem_entry(ScanEnv* env)
if (IS_NULL(env->mem_nodes_dynamic)) {
alloc = INIT_SCANENV_MEMNODES_ALLOC_SIZE;
p = (Node** )xmalloc(sizeof(Node*) * alloc);
+ CHECK_NULL_RETURN_MEMERR(p);
xmemcpy(p, env->mem_nodes_static,
sizeof(Node*) * SCANENV_MEMNODES_SIZE);
}
else {
alloc = env->mem_alloc * 2;
p = (Node** )xrealloc(env->mem_nodes_dynamic, sizeof(Node*) * alloc);
+ CHECK_NULL_RETURN_MEMERR(p);
}
- CHECK_NULL_RETURN_MEMERR(p);
for (i = env->num_mem + 1; i < alloc; i++)
p[i] = NULL_NODE;
@@ -1025,14 +1058,6 @@ scan_env_set_mem_node(ScanEnv* env, int num, Node* node)
}
-#ifdef USE_PARSE_TREE_NODE_RECYCLE
-typedef struct _FreeNode {
- struct _FreeNode* next;
-} FreeNode;
-
-static FreeNode* FreeNodeList = (FreeNode* )NULL;
-#endif
-
extern void
onig_node_free(Node* node)
{
@@ -1053,18 +1078,7 @@ onig_node_free(Node* node)
{
Node* next_node = NCDR(node);
-#ifdef USE_PARSE_TREE_NODE_RECYCLE
- {
- FreeNode* n = (FreeNode* )node;
-
- THREAD_ATOMIC_START;
- n->next = FreeNodeList;
- FreeNodeList = n;
- THREAD_ATOMIC_END;
- }
-#else
xfree(node);
-#endif
node = next_node;
goto start;
}
@@ -1074,9 +1088,8 @@ onig_node_free(Node* node)
{
CClassNode* cc = NCCLASS(node);
- if (IS_NCCLASS_SHARE(cc)) return ;
if (cc->mbuf)
- bbuf_free(cc->mbuf);
+ bbuf_free(cc->mbuf);
}
break;
@@ -1101,78 +1114,19 @@ onig_node_free(Node* node)
break;
}
-#ifdef USE_PARSE_TREE_NODE_RECYCLE
- {
- FreeNode* n = (FreeNode* )node;
-
- THREAD_ATOMIC_START;
- n->next = FreeNodeList;
- FreeNodeList = n;
- THREAD_ATOMIC_END;
- }
-#else
xfree(node);
-#endif
-}
-
-#ifdef USE_PARSE_TREE_NODE_RECYCLE
-extern int
-onig_free_node_list(void)
-{
- FreeNode* n;
-
- /* THREAD_ATOMIC_START; */
- while (IS_NOT_NULL(FreeNodeList)) {
- n = FreeNodeList;
- FreeNodeList = FreeNodeList->next;
- xfree(n);
- }
- /* THREAD_ATOMIC_END; */
- return 0;
}
-#endif
static Node*
node_new(void)
{
Node* node;
-#ifdef USE_PARSE_TREE_NODE_RECYCLE
- THREAD_ATOMIC_START;
- if (IS_NOT_NULL(FreeNodeList)) {
- node = (Node* )FreeNodeList;
- FreeNodeList = FreeNodeList->next;
- THREAD_ATOMIC_END;
- return node;
- }
- THREAD_ATOMIC_END;
-#endif
-
node = (Node* )xmalloc(sizeof(Node));
/* xmemset(node, 0, sizeof(Node)); */
return node;
}
-#if defined(USE_MULTI_THREAD_SYSTEM) && \
- defined(USE_SHARED_CCLASS_TABLE) && \
- defined(USE_PARSE_TREE_NODE_RECYCLE)
-static Node*
-node_new_locked(void)
-{
- Node* node;
-
- if (IS_NOT_NULL(FreeNodeList)) {
- node = (Node* )FreeNodeList;
- FreeNodeList = FreeNodeList->next;
- return node;
- }
-
- node = (Node* )xmalloc(sizeof(Node));
- /* xmemset(node, 0, sizeof(Node)); */
- return node;
-}
-#endif
-
static void
initialize_cclass(CClassNode* cc)
{
@@ -1193,75 +1147,6 @@ node_new_cclass(void)
return node;
}
-#if defined(USE_MULTI_THREAD_SYSTEM) && \
- defined(USE_SHARED_CCLASS_TABLE) && \
- defined(USE_PARSE_TREE_NODE_RECYCLE)
-static Node*
-node_new_cclass_locked(void)
-{
- Node* node = node_new_locked();
- CHECK_NULL_RETURN(node);
-
- SET_NTYPE(node, NT_CCLASS);
- initialize_cclass(NCCLASS(node));
- return node;
-}
-#else
-#define node_new_cclass_locked() node_new_cclass()
-#endif
-
-#ifdef USE_SHARED_CCLASS_TABLE
-static Node*
-node_new_cclass_by_codepoint_range(int not, OnigCodePoint sb_out,
- const OnigCodePoint ranges[])
-{
- int n, i;
- CClassNode* cc;
- OnigCodePoint j;
-
- Node* node = node_new_cclass_locked();
- CHECK_NULL_RETURN(node);
-
- cc = NCCLASS(node);
- if (not != 0) NCCLASS_SET_NOT(cc);
-
- BITSET_CLEAR(cc->bs);
- if (sb_out > 0 && IS_NOT_NULL(ranges)) {
- n = ONIGENC_CODE_RANGE_NUM(ranges);
- for (i = 0; i < n; i++) {
- for (j = ONIGENC_CODE_RANGE_FROM(ranges, i);
- j <= (OnigCodePoint )ONIGENC_CODE_RANGE_TO(ranges, i); j++) {
- if (j >= sb_out) goto sb_end;
-
- BITSET_SET_BIT(cc->bs, j);
- }
- }
- }
-
- sb_end:
- if (IS_NULL(ranges)) {
- is_null:
- cc->mbuf = NULL;
- }
- else {
- BBuf* bbuf;
-
- n = ONIGENC_CODE_RANGE_NUM(ranges);
- if (n == 0) goto is_null;
-
- bbuf = (BBuf* )xmalloc(sizeof(BBuf));
- CHECK_NULL_RETURN(bbuf);
- bbuf->alloc = n + 1;
- bbuf->used = n + 1;
- bbuf->p = (UChar* )((void* )ranges);
-
- cc->mbuf = bbuf;
- }
-
- return node;
-}
-#endif /* USE_SHARED_CCLASS_TABLE */
-
static Node*
node_new_ctype(int type, int not, int ascii_range)
{
@@ -1430,7 +1315,7 @@ node_new_quantifier(int lower, int upper, int by_number)
NQTFR(node)->target_empty_info = NQ_TARGET_ISNOT_EMPTY;
NQTFR(node)->head_exact = NULL_NODE;
NQTFR(node)->next_head_exact = NULL_NODE;
- NQTFR(node)->is_refered = 0;
+ NQTFR(node)->is_referred = 0;
if (by_number != 0)
NQTFR(node)->state |= NST_BY_NUMBER;
@@ -1548,6 +1433,7 @@ node_str_cat_codepoint(Node* node, OnigEncoding enc, OnigCodePoint c)
return onig_node_str_cat(node, buf, buf + num);
}
+#if 0
extern void
onig_node_conv_to_str_node(Node* node, int flag)
{
@@ -1557,6 +1443,7 @@ onig_node_conv_to_str_node(Node* node, int flag)
NSTR(node)->s = NSTR(node)->buf;
NSTR(node)->end = NSTR(node)->buf;
}
+#endif
extern void
onig_node_str_clear(Node* node)
@@ -1715,6 +1602,7 @@ scan_unsigned_hexadecimal_number(UChar** src, UChar* end, int minlen,
}
else {
PUNFETCH;
+ maxlen++;
break;
}
}
@@ -1832,7 +1720,7 @@ add_code_range_to_buf0(BBuf** pbuf, ScanEnv* env, OnigCodePoint from, OnigCodePo
if (inc_n != 1) {
if (checkdup && from <= data[low*2+1]
&& (data[low*2] <= from || data[low*2+1] <= to))
- CC_DUP_WARN(env);
+ CC_DUP_WARN(env, from, to);
if (from > data[low*2])
from = data[low*2];
if (to < data[(high - 1)*2 + 1])
@@ -1886,7 +1774,7 @@ add_code_range0(BBuf** pbuf, ScanEnv* env, OnigCodePoint from, OnigCodePoint to,
static int
add_code_range(BBuf** pbuf, ScanEnv* env, OnigCodePoint from, OnigCodePoint to)
{
- return add_code_range0(pbuf, env, from, to, 1);
+ return add_code_range0(pbuf, env, from, to, 1);
}
static int
@@ -1990,7 +1878,7 @@ or_code_range_buf(OnigEncoding enc, BBuf* bbuf1, int not1,
static int
and_code_range1(BBuf** pbuf, ScanEnv* env, OnigCodePoint from1, OnigCodePoint to1,
- OnigCodePoint* data, int n)
+ OnigCodePoint* data, int n)
{
int i, r;
OnigCodePoint from2, to2;
@@ -2204,8 +2092,8 @@ or_cclass(CClassNode* dest, CClassNode* cc, ScanEnv* env)
static void UNKNOWN_ESC_WARN(ScanEnv *env, int c);
-static int
-conv_backslash_value(int c, ScanEnv* env)
+static OnigCodePoint
+conv_backslash_value(OnigCodePoint c, ScanEnv* env)
{
if (IS_SYNTAX_OP(env->syntax, ONIG_SYN_OP_ESC_CONTROL_CHARS)) {
switch (c) {
@@ -2231,7 +2119,7 @@ conv_backslash_value(int c, ScanEnv* env)
}
#ifdef USE_NO_INVALID_QUANTIFIER
-#define is_invalid_quantifier_target(node) 0
+# define is_invalid_quantifier_target(node) 0
#else
static int
is_invalid_quantifier_target(Node* node)
@@ -2303,6 +2191,7 @@ enum ReduceType {
};
static enum ReduceType const ReduceTypeTable[6][6] = {
+/* '?', '*', '+', '??', '*?', '+?' p / c */
{RQ_DEL, RQ_A, RQ_A, RQ_QQ, RQ_AQ, RQ_ASIS}, /* '?' */
{RQ_DEL, RQ_DEL, RQ_DEL, RQ_P_QQ, RQ_P_QQ, RQ_DEL}, /* '*' */
{RQ_A, RQ_A, RQ_DEL, RQ_ASIS, RQ_P_QQ, RQ_DEL}, /* '+' */
@@ -2505,6 +2394,7 @@ fetch_range_quantifier(UChar** src, UChar* end, OnigToken* tok, ScanEnv* env)
PFETCH(c);
if (IS_SYNTAX_OP(env->syntax, ONIG_SYN_OP_ESC_BRACE_INTERVAL)) {
if (c != MC_ESC(env->syntax)) goto invalid;
+ if (PEND) goto invalid;
PFETCH(c);
}
if (c != '}') goto invalid;
@@ -2528,7 +2418,7 @@ fetch_range_quantifier(UChar** src, UChar* end, OnigToken* tok, ScanEnv* env)
/* \M-, \C-, \c, or \... */
static int
-fetch_escaped_value(UChar** src, UChar* end, ScanEnv* env)
+fetch_escaped_value(UChar** src, UChar* end, ScanEnv* env, OnigCodePoint* val)
{
int v;
OnigCodePoint c;
@@ -2547,9 +2437,8 @@ fetch_escaped_value(UChar** src, UChar* end, ScanEnv* env)
if (PEND) return ONIGERR_END_PATTERN_AT_META;
PFETCH_S(c);
if (c == MC_ESC(env->syntax)) {
- v = fetch_escaped_value(&p, end, env);
- if (v < 0) return v;
- c = (OnigCodePoint )v;
+ v = fetch_escaped_value(&p, end, env, &c);
+ if (v < 0) return v;
}
c = ((c & 0xff) | 0x80);
}
@@ -2573,15 +2462,14 @@ fetch_escaped_value(UChar** src, UChar* end, ScanEnv* env)
if (PEND) return ONIGERR_END_PATTERN_AT_CONTROL;
PFETCH_S(c);
if (c == '?') {
- c = 0177;
+ c = 0177;
}
else {
- if (c == MC_ESC(env->syntax)) {
- v = fetch_escaped_value(&p, end, env);
- if (v < 0) return v;
- c = (OnigCodePoint )v;
- }
- c &= 0x9f;
+ if (c == MC_ESC(env->syntax)) {
+ v = fetch_escaped_value(&p, end, env, &c);
+ if (v < 0) return v;
+ }
+ c &= 0x9f;
}
break;
}
@@ -2596,7 +2484,8 @@ fetch_escaped_value(UChar** src, UChar* end, ScanEnv* env)
}
*src = p;
- return c;
+ *val = c;
+ return 0;
}
static int fetch_token(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env);
@@ -2617,8 +2506,13 @@ get_name_end_code_point(OnigCodePoint start)
}
#ifdef USE_NAMED_GROUP
-#define ONIGENC_IS_CODE_NAME(enc, c) TRUE
-#ifdef USE_BACKREF_WITH_LEVEL
+# ifdef RUBY
+# define ONIGENC_IS_CODE_NAME(enc, c) TRUE
+# else
+# define ONIGENC_IS_CODE_NAME(enc, c) ONIGENC_IS_CODE_WORD(enc, c)
+# endif
+
+# ifdef USE_BACKREF_WITH_LEVEL
/*
\k<name+n>, \k<name-n>
\k<num+n>, \k<num-n>
@@ -2678,11 +2572,11 @@ fetch_name_with_level(OnigCodePoint start_code, UChar** src, UChar* end,
if (is_num != 0) {
if (ONIGENC_IS_CODE_DIGIT(enc, c)) {
- is_num = 1;
+ is_num = 1;
}
else {
- r = ONIGERR_INVALID_GROUP_NAME;
- is_num = 0;
+ r = ONIGERR_INVALID_GROUP_NAME;
+ is_num = 0;
}
}
else if (!ONIGENC_IS_CODE_NAME(enc, c)) {
@@ -2695,6 +2589,10 @@ fetch_name_with_level(OnigCodePoint start_code, UChar** src, UChar* end,
int level;
int flag = (c == '-' ? -1 : 1);
+ if (PEND) {
+ r = ONIGERR_INVALID_CHAR_IN_GROUP_NAME;
+ goto end;
+ }
PFETCH(c);
if (! ONIGENC_IS_CODE_DIGIT(enc, c)) goto err;
PUNFETCH;
@@ -2703,9 +2601,11 @@ fetch_name_with_level(OnigCodePoint start_code, UChar** src, UChar* end,
*rlevel = (level * flag);
exist_level = 1;
- PFETCH(c);
- if (c == end_code)
- goto end;
+ if (!PEND) {
+ PFETCH(c);
+ if (c == end_code)
+ goto end;
+ }
}
err:
@@ -2732,7 +2632,7 @@ fetch_name_with_level(OnigCodePoint start_code, UChar** src, UChar* end,
return r;
}
}
-#endif /* USE_BACKREF_WITH_LEVEL */
+# endif /* USE_BACKREF_WITH_LEVEL */
/*
ref: 0 -> define name (don't allow number name)
@@ -2769,17 +2669,17 @@ fetch_name(OnigCodePoint start_code, UChar** src, UChar* end,
if (ONIGENC_IS_CODE_DIGIT(enc, c)) {
if (ref == 1)
- is_num = 1;
+ is_num = 1;
else {
- r = ONIGERR_INVALID_GROUP_NAME;
- is_num = 0;
+ r = ONIGERR_INVALID_GROUP_NAME;
+ is_num = 0;
}
}
else if (c == '-') {
if (ref == 1) {
- is_num = 2;
- sign = -1;
- pnum_head = p;
+ is_num = 2;
+ sign = -1;
+ pnum_head = p;
}
else {
r = ONIGERR_INVALID_GROUP_NAME;
@@ -2796,30 +2696,30 @@ fetch_name(OnigCodePoint start_code, UChar** src, UChar* end,
name_end = p;
PFETCH_S(c);
if (c == end_code || c == ')') {
- if (is_num == 2) {
- r = ONIGERR_INVALID_GROUP_NAME;
- goto teardown;
- }
- break;
+ if (is_num == 2) {
+ r = ONIGERR_INVALID_GROUP_NAME;
+ goto teardown;
+ }
+ break;
}
if (is_num != 0) {
- if (ONIGENC_IS_CODE_DIGIT(enc, c)) {
- is_num = 1;
- }
- else {
- if (!ONIGENC_IS_CODE_WORD(enc, c))
- r = ONIGERR_INVALID_CHAR_IN_GROUP_NAME;
- else
- r = ONIGERR_INVALID_GROUP_NAME;
- goto teardown;
- }
+ if (ONIGENC_IS_CODE_DIGIT(enc, c)) {
+ is_num = 1;
+ }
+ else {
+ if (!ONIGENC_IS_CODE_WORD(enc, c))
+ r = ONIGERR_INVALID_CHAR_IN_GROUP_NAME;
+ else
+ r = ONIGERR_INVALID_GROUP_NAME;
+ goto teardown;
+ }
}
else {
- if (!ONIGENC_IS_CODE_NAME(enc, c)) {
- r = ONIGERR_INVALID_CHAR_IN_GROUP_NAME;
- goto teardown;
- }
+ if (!ONIGENC_IS_CODE_NAME(enc, c)) {
+ r = ONIGERR_INVALID_CHAR_IN_GROUP_NAME;
+ goto teardown;
+ }
}
}
@@ -2833,8 +2733,8 @@ fetch_name(OnigCodePoint start_code, UChar** src, UChar* end,
*rback_num = onig_scan_unsigned_number(&pnum_head, name_end, enc);
if (*rback_num < 0) return ONIGERR_TOO_BIG_NUMBER;
else if (*rback_num == 0) {
- r = ONIGERR_INVALID_GROUP_NAME;
- goto err;
+ r = ONIGERR_INVALID_GROUP_NAME;
+ goto err;
}
*rback_num *= sign;
@@ -2845,12 +2745,12 @@ fetch_name(OnigCodePoint start_code, UChar** src, UChar* end,
return 0;
}
else {
- teardown:
+teardown:
while (!PEND) {
name_end = p;
PFETCH_S(c);
if (c == end_code || c == ')')
- break;
+ break;
}
if (PEND)
name_end = end;
@@ -2939,8 +2839,6 @@ fetch_name(OnigCodePoint start_code, UChar** src, UChar* end,
}
#endif /* USE_NAMED_GROUP */
-void onig_vsnprintf_with_pattern(UChar buf[], int bufsize, OnigEncoding enc,
- UChar* pat, UChar* pat_end, const UChar *fmt, va_list args);
static void
onig_syntax_warn(ScanEnv *env, const char *fmt, ...)
@@ -2952,10 +2850,14 @@ onig_syntax_warn(ScanEnv *env, const char *fmt, ...)
env->pattern, env->pattern_end,
(const UChar *)fmt, args);
va_end(args);
+#ifdef RUBY
if (env->sourcefile == NULL)
rb_warn("%s", (char *)buf);
else
rb_compile_warn(env->sourcefile, env->sourceline, "%s", (char *)buf);
+#else
+ (*onig_warn)((char* )buf);
+#endif
}
static void
@@ -2979,15 +2881,23 @@ CLOSE_BRACKET_WITHOUT_ESC_WARN(ScanEnv* env, UChar* c)
}
}
+#ifndef RTEST
+# define RTEST(v) 1
+#endif
+
static void
-CC_DUP_WARN(ScanEnv *env)
+CC_DUP_WARN(ScanEnv *env, OnigCodePoint from ARG_UNUSED, OnigCodePoint to ARG_UNUSED)
{
if (onig_warn == onig_null_warn || !RTEST(ruby_verbose)) return ;
if (IS_SYNTAX_BV(env->syntax, ONIG_SYN_WARN_CC_DUP) &&
!(env->warnings_flag & ONIG_SYN_WARN_CC_DUP)) {
+#ifdef WARN_ALL_CC_DUP
+ onig_syntax_warn(env, "character class has duplicated range: %04x-%04x", from, to);
+#else
env->warnings_flag |= ONIG_SYN_WARN_CC_DUP;
onig_syntax_warn(env, "character class has duplicated range");
+#endif
}
}
@@ -3148,6 +3058,8 @@ fetch_token_in_cc(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
case 'p':
case 'P':
+ if (PEND) break;
+
c2 = PPEEK;
if (c2 == '{' &&
IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_P_BRACE_CHAR_PROPERTY)) {
@@ -3155,7 +3067,7 @@ fetch_token_in_cc(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
tok->type = TK_CHAR_PROPERTY;
tok->u.prop.not = (c == 'P' ? 1 : 0);
- if (IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_P_BRACE_CIRCUMFLEX_NOT)) {
+ if (!PEND && IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_P_BRACE_CIRCUMFLEX_NOT)) {
PFETCH(c2);
if (c2 == '^') {
tok->u.prop.not = (tok->u.prop.not == 0 ? 1 : 0);
@@ -3178,10 +3090,10 @@ fetch_token_in_cc(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
num = scan_unsigned_hexadecimal_number(&p, end, 0, 8, enc);
if (num < 0) return ONIGERR_TOO_BIG_WIDE_CHAR_VALUE;
if (!PEND) {
- c2 = PPEEK;
- if (ONIGENC_IS_CODE_XDIGIT(enc, c2))
- return ONIGERR_TOO_LONG_WIDE_CHAR_VALUE;
- }
+ c2 = PPEEK;
+ if (ONIGENC_IS_CODE_XDIGIT(enc, c2))
+ return ONIGERR_TOO_LONG_WIDE_CHAR_VALUE;
+ }
if (p > prev + enclen(enc, prev, end) && !PEND && (PPEEK_IS('}'))) {
PINC;
@@ -3223,6 +3135,33 @@ fetch_token_in_cc(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
}
break;
+ case 'o':
+ if (PEND) break;
+
+ prev = p;
+ if (PPEEK_IS('{') && IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_O_BRACE_OCTAL)) {
+ PINC;
+ num = scan_unsigned_octal_number(&p, end, 11, enc);
+ if (num < 0) return ONIGERR_TOO_BIG_WIDE_CHAR_VALUE;
+ if (!PEND) {
+ c2 = PPEEK;
+ if (ONIGENC_IS_CODE_DIGIT(enc, c2) && c2 < '8')
+ return ONIGERR_TOO_LONG_WIDE_CHAR_VALUE;
+ }
+
+ if (p > prev + enclen(enc, prev, end) && !PEND && (PPEEK_IS('}'))) {
+ PINC;
+ tok->type = TK_CODE_POINT;
+ tok->base = 8;
+ tok->u.code = (OnigCodePoint )num;
+ }
+ else {
+ /* can't read nothing or invalid format */
+ p = prev;
+ }
+ }
+ break;
+
case '0':
case '1': case '2': case '3': case '4': case '5': case '6': case '7':
if (IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_OCTAL3)) {
@@ -3241,10 +3180,10 @@ fetch_token_in_cc(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
default:
PUNFETCH;
- num = fetch_escaped_value(&p, end, env);
+ num = fetch_escaped_value(&p, end, env, &c2);
if (num < 0) return num;
- if (tok->u.c != num) {
- tok->u.code = (OnigCodePoint )num;
+ if ((OnigCodePoint )tok->u.c != c2) {
+ tok->u.code = (OnigCodePoint )c2;
tok->type = TK_CODE_POINT;
}
break;
@@ -3302,15 +3241,15 @@ fetch_named_backref_token(OnigCodePoint c, OnigToken* tok, UChar** src,
prev = p;
-#ifdef USE_BACKREF_WITH_LEVEL
+# ifdef USE_BACKREF_WITH_LEVEL
name_end = NULL_UCHARP; /* no need. escape gcc warning. */
r = fetch_name_with_level(c, &p, end, &name_end,
env, &back_num, &tok->u.backref.level);
if (r == 1) tok->u.backref.exist_level = 1;
else tok->u.backref.exist_level = 0;
-#else
+# else
r = fetch_name(&p, end, &name_end, env, &back_num, 1);
-#endif
+# endif
if (r < 0) return r;
if (back_num != 0) {
@@ -3348,7 +3287,7 @@ fetch_named_backref_token(OnigCodePoint c, OnigToken* tok, UChar** src,
tok->type = TK_BACKREF;
tok->u.backref.by_name = 1;
- if (num == 1) {
+ if (num == 1 || IS_SYNTAX_BV(syn, ONIG_SYN_USE_LEFT_MOST_NAMED_GROUP)) {
tok->u.backref.num = 1;
tok->u.backref.ref1 = backs[0];
}
@@ -3601,9 +3540,9 @@ fetch_token(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
num = scan_unsigned_hexadecimal_number(&p, end, 0, 8, enc);
if (num < 0) return ONIGERR_TOO_BIG_WIDE_CHAR_VALUE;
if (!PEND) {
- if (ONIGENC_IS_CODE_XDIGIT(enc, PPEEK))
- return ONIGERR_TOO_LONG_WIDE_CHAR_VALUE;
- }
+ if (ONIGENC_IS_CODE_XDIGIT(enc, PPEEK))
+ return ONIGERR_TOO_LONG_WIDE_CHAR_VALUE;
+ }
if ((p > prev + enclen(enc, prev, end)) && !PEND && PPEEK_IS('}')) {
PINC;
@@ -3644,13 +3583,39 @@ fetch_token(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
}
break;
+ case 'o':
+ if (PEND) break;
+
+ prev = p;
+ if (PPEEK_IS('{') && IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_O_BRACE_OCTAL)) {
+ PINC;
+ num = scan_unsigned_octal_number(&p, end, 11, enc);
+ if (num < 0) return ONIGERR_TOO_BIG_WIDE_CHAR_VALUE;
+ if (!PEND) {
+ OnigCodePoint c = PPEEK;
+ if (ONIGENC_IS_CODE_DIGIT(enc, c) && c < '8')
+ return ONIGERR_TOO_LONG_WIDE_CHAR_VALUE;
+ }
+
+ if ((p > prev + enclen(enc, prev, end)) && !PEND && PPEEK_IS('}')) {
+ PINC;
+ tok->type = TK_CODE_POINT;
+ tok->u.code = (OnigCodePoint )num;
+ }
+ else {
+ /* can't read nothing or invalid format */
+ p = prev;
+ }
+ }
+ break;
+
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
PUNFETCH;
prev = p;
num = onig_scan_unsigned_number(&p, end, enc);
if (num < 0 || num > ONIG_MAX_BACKREF_NUM) {
- goto skip_backref;
+ goto skip_backref;
}
if (IS_SYNTAX_OP(syn, ONIG_SYN_OP_DECIMAL_BACKREF) &&
@@ -3683,7 +3648,7 @@ fetch_token(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
if (IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_OCTAL3)) {
prev = p;
num = scan_unsigned_octal_number(&p, end, (c == '0' ? 2:3), enc);
- if (num < 0) return ONIGERR_TOO_BIG_NUMBER;
+ if (num < 0 || 0xff < num) return ONIGERR_TOO_BIG_NUMBER;
if (p == prev) { /* can't read nothing. */
num = 0; /* but, it's not error */
}
@@ -3698,7 +3663,7 @@ fetch_token(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
#ifdef USE_NAMED_GROUP
case 'k':
- if (IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_K_NAMED_BACKREF)) {
+ if (!PEND && IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_K_NAMED_BACKREF)) {
PFETCH(c);
if (c == '<' || c == '\'') {
r = fetch_named_backref_token(c, tok, &p, end, env);
@@ -3714,8 +3679,8 @@ fetch_token(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
#if defined(USE_SUBEXP_CALL) || defined(USE_NAMED_GROUP)
case 'g':
-#ifdef USE_NAMED_GROUP
- if (IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_G_BRACE_BACKREF)) {
+# ifdef USE_NAMED_GROUP
+ if (!PEND && IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_G_BRACE_BACKREF)) {
PFETCH(c);
if (c == '{') {
r = fetch_named_backref_token(c, tok, &p, end, env);
@@ -3724,9 +3689,9 @@ fetch_token(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
else
PUNFETCH;
}
-#endif
-#ifdef USE_SUBEXP_CALL
- if (IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_G_SUBEXP_CALL)) {
+# endif
+# ifdef USE_SUBEXP_CALL
+ if (!PEND && IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_G_SUBEXP_CALL)) {
PFETCH(c);
if (c == '<' || c == '\'') {
int gnum = -1, rel = 0;
@@ -3763,7 +3728,7 @@ fetch_token(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
PUNFETCH;
}
}
-#endif
+# endif
break;
#endif
@@ -3781,7 +3746,7 @@ fetch_token(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
tok->type = TK_CHAR_PROPERTY;
tok->u.prop.not = (c == 'P' ? 1 : 0);
- if (IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_P_BRACE_CIRCUMFLEX_NOT)) {
+ if (!PEND && IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_P_BRACE_CIRCUMFLEX_NOT)) {
PFETCH(c);
if (c == '^') {
tok->u.prop.not = (tok->u.prop.not == 0 ? 1 : 0);
@@ -3814,16 +3779,20 @@ fetch_token(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
break;
default:
- PUNFETCH;
- num = fetch_escaped_value(&p, end, env);
- if (num < 0) return num;
- /* set_raw: */
- if (tok->u.c != num) {
- tok->type = TK_CODE_POINT;
- tok->u.code = (OnigCodePoint )num;
- }
- else { /* string */
- p = tok->backp + enclen(enc, tok->backp, end);
+ {
+ OnigCodePoint c2;
+
+ PUNFETCH;
+ num = fetch_escaped_value(&p, end, env, &c2);
+ if (num < 0) return num;
+ /* set_raw: */
+ if ((OnigCodePoint )tok->u.c != c2) {
+ tok->type = TK_CODE_POINT;
+ tok->u.code = (OnigCodePoint )c2;
+ }
+ else { /* string */
+ p = tok->backp + enclen(enc, tok->backp, end);
+ }
}
break;
}
@@ -3913,22 +3882,22 @@ fetch_token(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
case '(':
if (PPEEK_IS('?') &&
- IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_QMARK_GROUP_EFFECT)) {
- PINC;
- if (PPEEK_IS('#')) {
- PFETCH(c);
- while (1) {
- if (PEND) return ONIGERR_END_PATTERN_IN_GROUP;
- PFETCH(c);
- if (c == MC_ESC(syn)) {
- if (!PEND) PFETCH(c);
- }
- else {
- if (c == ')') break;
- }
- }
- goto start;
- }
+ IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_QMARK_GROUP_EFFECT)) {
+ PINC;
+ if (PPEEK_IS('#')) {
+ PFETCH(c);
+ while (1) {
+ if (PEND) return ONIGERR_END_PATTERN_IN_GROUP;
+ PFETCH(c);
+ if (c == MC_ESC(syn)) {
+ if (!PEND) PFETCH(c);
+ }
+ else {
+ if (c == ')') break;
+ }
+ }
+ goto start;
+ }
#ifdef USE_PERL_SUBEXP_CALL
/* (?&name), (?n), (?R), (?0), (?+n), (?-n) */
c = PPEEK;
@@ -3999,6 +3968,7 @@ fetch_token(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
PFETCH_READY;
PINC; /* skip 'P' */
+ if (PEND) return ONIGERR_UNDEFINED_GROUP_OPTION;
PFETCH(c);
if (c == '=') { /* (?P=name): backref */
r = fetch_named_backref_token((OnigCodePoint )'(', tok, &p, end, env);
@@ -4017,10 +3987,9 @@ fetch_token(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
tok->u.call.rel = 0;
break;
}
- PUNFETCH;
}
#endif /* USE_CAPITAL_P_NAMED_GROUP */
- PUNFETCH;
+ PUNFETCH;
}
if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_LPAREN_SUBEXP)) break;
@@ -4098,8 +4067,8 @@ add_ctype_to_cc_by_range(CClassNode* cc, int ctype ARG_UNUSED, int not,
if (not == 0) {
for (i = 0; i < n; i++) {
- for (j = ONIGENC_CODE_RANGE_FROM(mbr, i);
- j <= ONIGENC_CODE_RANGE_TO(mbr, i); j++) {
+ for (j = ONIGENC_CODE_RANGE_FROM(mbr, i);
+ j <= ONIGENC_CODE_RANGE_TO(mbr, i); j++) {
if (j >= sb_out) {
if (j > ONIGENC_CODE_RANGE_FROM(mbr, i)) {
r = add_code_range_to_buf(&(cc->mbuf), env, j,
@@ -4110,7 +4079,7 @@ add_ctype_to_cc_by_range(CClassNode* cc, int ctype ARG_UNUSED, int not,
goto sb_end;
}
- BITSET_SET_BIT_CHKDUP(cc->bs, j);
+ BITSET_SET_BIT_CHKDUP(cc->bs, j);
}
}
@@ -4183,12 +4152,15 @@ add_ctype_to_cc(CClassNode* cc, int ctype, int not, int ascii_range, ScanEnv* en
CClassNode ccascii;
initialize_cclass(&ccascii);
if (ONIGENC_MBC_MINLEN(env->enc) > 1) {
- add_code_range(&(ccascii.mbuf), env, 0x00, 0x7F);
+ r = add_code_range(&(ccascii.mbuf), env, 0x00, 0x7F);
}
else {
bitset_set_range(env, ccascii.bs, 0x00, 0x7F);
+ r = 0;
+ }
+ if (r == 0) {
+ r = and_cclass(&ccwork, &ccascii, env);
}
- r = and_cclass(&ccwork, &ccascii, env);
if (IS_NOT_NULL(ccascii.mbuf)) bbuf_free(ccascii.mbuf);
}
if (r == 0) {
@@ -4244,7 +4216,7 @@ add_ctype_to_cc(CClassNode* cc, int ctype, int not, int ascii_range, ScanEnv* en
BITSET_SET_BIT_CHKDUP(cc->bs, c);
}
if (ascii_range)
- ADD_ALL_MULTI_BYTE_RANGE(enc, cc->mbuf);
+ ADD_ALL_MULTI_BYTE_RANGE(enc, cc->mbuf);
}
else {
for (c = 0; c < maxcode; c++) {
@@ -4252,7 +4224,7 @@ add_ctype_to_cc(CClassNode* cc, int ctype, int not, int ascii_range, ScanEnv* en
BITSET_SET_BIT_CHKDUP(cc->bs, c);
}
if (! ascii_range)
- ADD_ALL_MULTI_BYTE_RANGE(enc, cc->mbuf);
+ ADD_ALL_MULTI_BYTE_RANGE(enc, cc->mbuf);
}
break;
@@ -4262,16 +4234,16 @@ add_ctype_to_cc(CClassNode* cc, int ctype, int not, int ascii_range, ScanEnv* en
if (ONIGENC_IS_CODE_WORD(enc, c)) BITSET_SET_BIT_CHKDUP(cc->bs, c);
}
if (! ascii_range)
- ADD_ALL_MULTI_BYTE_RANGE(enc, cc->mbuf);
+ ADD_ALL_MULTI_BYTE_RANGE(enc, cc->mbuf);
}
else {
for (c = 0; c < SINGLE_BYTE_SIZE; c++) {
- if ((ONIGENC_CODE_TO_MBCLEN(enc, c) > 0) /* check invalid code point */
+ if ((ONIGENC_CODE_TO_MBCLEN(enc, c) > 0) /* check invalid code point */
&& (! ONIGENC_IS_CODE_WORD(enc, c) || c >= maxcode))
BITSET_SET_BIT_CHKDUP(cc->bs, c);
}
if (ascii_range)
- ADD_ALL_MULTI_BYTE_RANGE(enc, cc->mbuf);
+ ADD_ALL_MULTI_BYTE_RANGE(enc, cc->mbuf);
}
break;
@@ -4330,7 +4302,7 @@ parse_posix_bracket(CClassNode* cc, CClassNode* asc_cc,
if (onigenc_with_ascii_strncmp(enc, p, end, pb->name, pb->len) == 0) {
p = (UChar* )onigenc_step(enc, p, end, pb->len);
if (onigenc_with_ascii_strncmp(enc, p, end, (UChar* )":]", 2) != 0)
- return ONIGERR_INVALID_POSIX_BRACKET_TYPE;
+ return ONIGERR_INVALID_POSIX_BRACKET_TYPE;
r = add_ctype_to_cc(cc, pb->ctype, not, ascii_range, env);
if (r != 0) return r;
@@ -4361,7 +4333,7 @@ parse_posix_bracket(CClassNode* cc, CClassNode* asc_cc,
if (! PEND) {
PFETCH_S(c);
if (c == ']')
- return ONIGERR_INVALID_POSIX_BRACKET_TYPE;
+ return ONIGERR_INVALID_POSIX_BRACKET_TYPE;
}
}
@@ -4441,7 +4413,7 @@ enum CCVALTYPE {
static int
next_state_class(CClassNode* cc, CClassNode* asc_cc,
- OnigCodePoint* vs, enum CCVALTYPE* type,
+ OnigCodePoint* vs, enum CCVALTYPE* type,
enum CCSTATE* state, ScanEnv* env)
{
int r;
@@ -4472,8 +4444,8 @@ next_state_class(CClassNode* cc, CClassNode* asc_cc,
static int
next_state_val(CClassNode* cc, CClassNode* asc_cc,
- OnigCodePoint *vs, OnigCodePoint v,
- int* vs_israw, int v_israw,
+ OnigCodePoint *from, OnigCodePoint to,
+ int* from_israw, int to_israw,
enum CCVALTYPE intype, enum CCVALTYPE* type,
enum CCSTATE* state, ScanEnv* env)
{
@@ -4482,15 +4454,15 @@ next_state_val(CClassNode* cc, CClassNode* asc_cc,
switch (*state) {
case CCS_VALUE:
if (*type == CCV_SB) {
- BITSET_SET_BIT_CHKDUP(cc->bs, (int )(*vs));
+ BITSET_SET_BIT_CHKDUP(cc->bs, (int )(*from));
if (IS_NOT_NULL(asc_cc))
- BITSET_SET_BIT(asc_cc->bs, (int )(*vs));
+ BITSET_SET_BIT(asc_cc->bs, (int )(*from));
}
else if (*type == CCV_CODE_POINT) {
- r = add_code_range(&(cc->mbuf), env, *vs, *vs);
+ r = add_code_range(&(cc->mbuf), env, *from, *from);
if (r < 0) return r;
if (IS_NOT_NULL(asc_cc)) {
- r = add_code_range0(&(asc_cc->mbuf), env, *vs, *vs, 0);
+ r = add_code_range0(&(asc_cc->mbuf), env, *from, *from, 0);
if (r < 0) return r;
}
}
@@ -4499,51 +4471,43 @@ next_state_val(CClassNode* cc, CClassNode* asc_cc,
case CCS_RANGE:
if (intype == *type) {
if (intype == CCV_SB) {
- if (*vs > 0xff || v > 0xff)
- return ONIGERR_INVALID_CODE_POINT_VALUE;
+ if (*from > 0xff || to > 0xff)
+ return ONIGERR_INVALID_CODE_POINT_VALUE;
- if (*vs > v) {
+ if (*from > to) {
if (IS_SYNTAX_BV(env->syntax, ONIG_SYN_ALLOW_EMPTY_RANGE_IN_CC))
goto ccs_range_end;
else
return ONIGERR_EMPTY_RANGE_IN_CHAR_CLASS;
}
- bitset_set_range(env, cc->bs, (int )*vs, (int )v);
+ bitset_set_range(env, cc->bs, (int )*from, (int )to);
if (IS_NOT_NULL(asc_cc))
- bitset_set_range(env, asc_cc->bs, (int )*vs, (int )v);
+ bitset_set_range(env, asc_cc->bs, (int )*from, (int )to);
}
else {
- r = add_code_range(&(cc->mbuf), env, *vs, v);
+ r = add_code_range(&(cc->mbuf), env, *from, to);
if (r < 0) return r;
if (IS_NOT_NULL(asc_cc)) {
- r = add_code_range0(&(asc_cc->mbuf), env, *vs, v, 0);
+ r = add_code_range0(&(asc_cc->mbuf), env, *from, to, 0);
if (r < 0) return r;
}
}
}
else {
-#if 0
- if (intype == CCV_CODE_POINT && *type == CCV_SB) {
-#endif
- if (*vs > v) {
- if (IS_SYNTAX_BV(env->syntax, ONIG_SYN_ALLOW_EMPTY_RANGE_IN_CC))
- goto ccs_range_end;
- else
- return ONIGERR_EMPTY_RANGE_IN_CHAR_CLASS;
- }
- bitset_set_range(env, cc->bs, (int )*vs, (int )(v < 0xff ? v : 0xff));
- r = add_code_range(&(cc->mbuf), env, (OnigCodePoint )*vs, v);
+ if (*from > to) {
+ if (IS_SYNTAX_BV(env->syntax, ONIG_SYN_ALLOW_EMPTY_RANGE_IN_CC))
+ goto ccs_range_end;
+ else
+ return ONIGERR_EMPTY_RANGE_IN_CHAR_CLASS;
+ }
+ bitset_set_range(env, cc->bs, (int )*from, (int )(to < 0xff ? to : 0xff));
+ r = add_code_range(&(cc->mbuf), env, (OnigCodePoint )*from, to);
+ if (r < 0) return r;
+ if (IS_NOT_NULL(asc_cc)) {
+ bitset_set_range(env, asc_cc->bs, (int )*from, (int )(to < 0xff ? to : 0xff));
+ r = add_code_range0(&(asc_cc->mbuf), env, (OnigCodePoint )*from, to, 0);
if (r < 0) return r;
- if (IS_NOT_NULL(asc_cc)) {
- bitset_set_range(env, asc_cc->bs, (int )*vs, (int )(v < 0xff ? v : 0xff));
- r = add_code_range0(&(asc_cc->mbuf), env, (OnigCodePoint )*vs, v, 0);
- if (r < 0) return r;
- }
-#if 0
}
- else
- return ONIGERR_MISMATCH_CODE_LENGTH_IN_CLASS_RANGE;
-#endif
}
ccs_range_end:
*state = CCS_COMPLETE;
@@ -4558,9 +4522,9 @@ next_state_val(CClassNode* cc, CClassNode* asc_cc,
break;
}
- *vs_israw = v_israw;
- *vs = v;
- *type = intype;
+ *from_israw = to_israw;
+ *from = to;
+ *type = intype;
return 0;
}
@@ -4604,8 +4568,11 @@ parse_char_class(Node** np, Node** asc_np, OnigToken* tok, UChar** src, UChar* e
enum CCVALTYPE val_type, in_type;
int val_israw, in_israw;
- prev_cc = asc_prev_cc = (CClassNode* )NULL;
*np = *asc_np = NULL_NODE;
+ env->parse_depth++;
+ if (env->parse_depth > ParseDepthLimit)
+ return ONIGERR_PARSE_DEPTH_LIMIT_OVER;
+ prev_cc = asc_prev_cc = (CClassNode* )NULL;
r = fetch_token_in_cc(tok, src, end, env);
if (r == TK_CHAR && tok->u.c == '^' && tok->escaped == 0) {
neg = 1;
@@ -4687,7 +4654,7 @@ parse_char_class(Node** np, Node** asc_np, OnigToken* tok, UChar** src, UChar* e
goto err;
}
- len = enclen(env->enc, buf, buf+i);
+ len = enclen(env->enc, buf, buf + i);
if (i < len) {
r = ONIGERR_TOO_SHORT_MULTI_BYTE_STRING;
goto err;
@@ -4695,7 +4662,8 @@ parse_char_class(Node** np, Node** asc_np, OnigToken* tok, UChar** src, UChar* e
else if (i > len) { /* fetch back */
p = psave;
for (i = 1; i < len; i++) {
- r = fetch_token_in_cc(tok, &p, end, env);
+ (void)fetch_token_in_cc(tok, &p, end, env);
+ /* no need to check the retun value (already checked above) */
}
fetched = 0;
}
@@ -4795,6 +4763,12 @@ parse_char_class(Node** np, Node** asc_np, OnigToken* tok, UChar** src, UChar* e
CC_ESC_WARN(env, (UChar* )"-");
goto range_end_val;
}
+
+ if (val_type == CCV_CLASS) {
+ r = ONIGERR_UNMATCHED_RANGE_SPECIFIER_IN_CHAR_CLASS;
+ goto err;
+ }
+
state = CCS_RANGE;
}
else if (state == CCS_START) {
@@ -4948,16 +4922,17 @@ parse_char_class(Node** np, Node** asc_np, OnigToken* tok, UChar** src, UChar* e
#define NEWLINE_CODE 0x0a
if (ONIGENC_IS_CODE_NEWLINE(env->enc, NEWLINE_CODE)) {
- if (ONIGENC_CODE_TO_MBCLEN(env->enc, NEWLINE_CODE) == 1)
- BITSET_SET_BIT_CHKDUP(cc->bs, NEWLINE_CODE);
- else {
- r = add_code_range(&(cc->mbuf), env, NEWLINE_CODE, NEWLINE_CODE);
- if (r < 0) goto err;
- }
+ if (ONIGENC_CODE_TO_MBCLEN(env->enc, NEWLINE_CODE) == 1)
+ BITSET_SET_BIT_CHKDUP(cc->bs, NEWLINE_CODE);
+ else {
+ r = add_code_range(&(cc->mbuf), env, NEWLINE_CODE, NEWLINE_CODE);
+ if (r < 0) goto err;
+ }
}
}
}
*src = p;
+ env->parse_depth--;
return 0;
err:
@@ -5018,6 +4993,14 @@ parse_enclose(Node** np, OnigToken* tok, int term, UChar** src, UChar* end,
case '>': /* (?>...) stop backtrack */
*np = node_new_enclose(ENCLOSE_STOP_BACKTRACK);
break;
+ case '~': /* (?~...) absent operator */
+ if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_QMARK_TILDE_ABSENT)) {
+ *np = node_new_enclose(ENCLOSE_ABSENT);
+ }
+ else {
+ return ONIGERR_UNDEFINED_GROUP_OPTION;
+ }
+ break;
#ifdef USE_NAMED_GROUP
case '\'':
@@ -5028,18 +5011,20 @@ parse_enclose(Node** np, OnigToken* tok, int term, UChar** src, UChar* end,
return ONIGERR_UNDEFINED_GROUP_OPTION;
break;
-#ifdef USE_CAPITAL_P_NAMED_GROUP
+# ifdef USE_CAPITAL_P_NAMED_GROUP
case 'P': /* (?P<name>...) */
- if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_QMARK_CAPITAL_P_NAMED_GROUP)) {
+ if (!PEND &&
+ IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_QMARK_CAPITAL_P_NAMED_GROUP)) {
PFETCH(c);
if (c == '<') goto named_group1;
}
return ONIGERR_UNDEFINED_GROUP_OPTION;
break;
-#endif
+# endif
#endif
case '<': /* look behind (?<=...), (?<!...) */
+ if (PEND) return ONIGERR_END_PATTERN_WITH_UNMATCHED_PARENTHESIS;
PFETCH(c);
if (c == '=')
*np = onig_node_new_anchor(ANCHOR_LOOK_BEHIND);
@@ -5057,7 +5042,9 @@ parse_enclose(Node** np, OnigToken* tok, int term, UChar** src, UChar* end,
named_group1:
list_capture = 0;
+# ifdef USE_CAPTURE_HISTORY
named_group2:
+# endif
name = p;
r = fetch_name((OnigCodePoint )c, &p, end, &name_end, env, &num, 0);
if (r < 0) return r;
@@ -5087,10 +5074,12 @@ parse_enclose(Node** np, OnigToken* tok, int term, UChar** src, UChar* end,
#endif
break;
+#ifdef USE_CAPTURE_HISTORY
case '@':
if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_ATMARK_CAPTURE_HISTORY)) {
-#ifdef USE_NAMED_GROUP
- if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_QMARK_LT_NAMED_GROUP)) {
+# ifdef USE_NAMED_GROUP
+ if (!PEND &&
+ IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_QMARK_LT_NAMED_GROUP)) {
PFETCH(c);
if (c == '<' || c == '\'') {
list_capture = 1;
@@ -5098,7 +5087,7 @@ parse_enclose(Node** np, OnigToken* tok, int term, UChar** src, UChar* end,
}
PUNFETCH;
}
-#endif
+# endif
*np = node_new_enclose_memory(env->option, 0);
CHECK_NULL_RETURN_MEMERR(*np);
num = scan_env_add_mem_entry(env);
@@ -5113,9 +5102,11 @@ parse_enclose(Node** np, OnigToken* tok, int term, UChar** src, UChar* end,
return ONIGERR_UNDEFINED_GROUP_OPTION;
}
break;
+#endif /* USE_CAPTURE_HISTORY */
case '(': /* conditional expression: (?(cond)yes), (?(cond)yes|no) */
- if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_QMARK_LPAREN_CONDITION)) {
+ if (!PEND &&
+ IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_QMARK_LPAREN_CONDITION)) {
UChar *name = NULL;
UChar *name_end;
PFETCH(c);
@@ -5133,36 +5124,29 @@ parse_enclose(Node** np, OnigToken* tok, int term, UChar** src, UChar* end,
#endif
if (IS_SYNTAX_BV(env->syntax, ONIG_SYN_STRICT_CHECK_BACKREF)) {
if (num > env->num_mem ||
- IS_NULL(SCANENV_MEM_NODES(env)[num]))
+ IS_NULL(SCANENV_MEM_NODES(env)[num]))
return ONIGERR_INVALID_BACKREF;
}
}
#ifdef USE_NAMED_GROUP
else if (c == '<' || c == '\'') { /* (<name>), ('name') */
- int nums;
- int *backs;
-
name = p;
- r = fetch_name((OnigCodePoint )c, &p, end, &name_end, env, &num, 0);
+ r = fetch_named_backref_token(c, tok, &p, end, env);
if (r < 0) return r;
- PFETCH(c);
- if (c != ')') return ONIGERR_UNDEFINED_GROUP_OPTION;
+ if (!PPEEK_IS(')')) return ONIGERR_UNDEFINED_GROUP_OPTION;
+ PINC;
- nums = onig_name_to_group_numbers(env->reg, name, name_end, &backs);
- if (nums <= 0) {
- onig_scan_env_set_error_string(env,
- ONIGERR_UNDEFINED_NAME_REFERENCE, name, name_end);
- return ONIGERR_UNDEFINED_NAME_REFERENCE;
+ if (IS_SYNTAX_BV(env->syntax, ONIG_SYN_USE_LEFT_MOST_NAMED_GROUP)) {
+ num = tok->u.backref.ref1;
}
- if (IS_SYNTAX_BV(env->syntax, ONIG_SYN_STRICT_CHECK_BACKREF)) {
- int i;
- for (i = 0; i < nums; i++) {
- if (backs[i] > env->num_mem ||
- IS_NULL(SCANENV_MEM_NODES(env)[backs[i]]))
- return ONIGERR_INVALID_BACKREF;
- }
+ else {
+ /* FIXME:
+ * Use left most named group for now. This is the same as Perl.
+ * However this should use the same strategy as normal back-
+ * references on Ruby syntax; search right to left. */
+ int len = tok->u.backref.num;
+ num = len > 1 ? tok->u.backref.refs[0] : tok->u.backref.ref1;
}
- num = backs[0]; /* XXX: use left most named group as Perl */
}
#endif
else
@@ -5187,7 +5171,7 @@ parse_enclose(Node** np, OnigToken* tok, int term, UChar** src, UChar* end,
#endif
case '^': /* loads default options */
- if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_OPTION_PERL)) {
+ if (!PEND && IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_OPTION_PERL)) {
/* d-imsx */
ONOFF(option, ONIG_OPTION_ASCII_RANGE, 1);
ONOFF(option, ONIG_OPTION_IGNORECASE, 1);
@@ -5197,7 +5181,7 @@ parse_enclose(Node** np, OnigToken* tok, int term, UChar** src, UChar* end,
PFETCH(c);
}
#if 0
- else if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_OPTION_RUBY)) {
+ else if (!PEND && IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_OPTION_RUBY)) {
/* d-imx */
ONOFF(option, ONIG_OPTION_ASCII_RANGE, 0);
ONOFF(option, ONIG_OPTION_POSIX_BRACKET_ALL_RANGE, 0);
@@ -5255,8 +5239,8 @@ parse_enclose(Node** np, OnigToken* tok, int term, UChar** src, UChar* end,
case 'a': /* limits \d, \s, \w and POSIX brackets to ASCII range */
if ((IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_OPTION_PERL) ||
- IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_OPTION_RUBY)) &&
- (neg == 0)) {
+ IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_OPTION_RUBY)) &&
+ (neg == 0)) {
ONOFF(option, ONIG_OPTION_ASCII_RANGE, 0);
ONOFF(option, ONIG_OPTION_POSIX_BRACKET_ALL_RANGE, 1);
ONOFF(option, ONIG_OPTION_WORD_BOUND_ALL_RANGE, 1);
@@ -5267,8 +5251,8 @@ parse_enclose(Node** np, OnigToken* tok, int term, UChar** src, UChar* end,
case 'u':
if ((IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_OPTION_PERL) ||
- IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_OPTION_RUBY)) &&
- (neg == 0)) {
+ IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_OPTION_RUBY)) &&
+ (neg == 0)) {
ONOFF(option, ONIG_OPTION_ASCII_RANGE, 1);
ONOFF(option, ONIG_OPTION_POSIX_BRACKET_ALL_RANGE, 1);
ONOFF(option, ONIG_OPTION_WORD_BOUND_ALL_RANGE, 1);
@@ -5279,11 +5263,11 @@ parse_enclose(Node** np, OnigToken* tok, int term, UChar** src, UChar* end,
case 'd':
if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_OPTION_PERL) &&
- (neg == 0)) {
+ (neg == 0)) {
ONOFF(option, ONIG_OPTION_ASCII_RANGE, 1);
}
else if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_OPTION_RUBY) &&
- (neg == 0)) {
+ (neg == 0)) {
ONOFF(option, ONIG_OPTION_ASCII_RANGE, 0);
ONOFF(option, ONIG_OPTION_POSIX_BRACKET_ALL_RANGE, 0);
ONOFF(option, ONIG_OPTION_WORD_BOUND_ALL_RANGE, 0);
@@ -5313,9 +5297,12 @@ parse_enclose(Node** np, OnigToken* tok, int term, UChar** src, UChar* end,
else if (c == ':') {
OnigOptionType prev = env->option;
- env->option = option;
+ env->option = option;
r = fetch_token(tok, &p, end, env);
- if (r < 0) return r;
+ if (r < 0) {
+ env->option = prev;
+ return r;
+ }
r = parse_subexp(&target, tok, term, &p, end, env);
env->option = prev;
if (r < 0) return r;
@@ -5430,29 +5417,29 @@ set_quantifier(Node* qnode, Node* target, int group, ScanEnv* env)
int targetq_num = popular_quantifier_num(qnt);
#ifdef USE_WARNING_REDUNDANT_NESTED_REPEAT_OPERATOR
- if (!IS_QUANTIFIER_BY_NUMBER(qn) && !IS_QUANTIFIER_BY_NUMBER(qnt) &&
+ if (nestq_num >= 0 && targetq_num >= 0 &&
IS_SYNTAX_BV(env->syntax, ONIG_SYN_WARN_REDUNDANT_NESTED_REPEAT)) {
- switch (ReduceTypeTable[targetq_num][nestq_num]) {
- case RQ_ASIS:
- break;
-
- case RQ_DEL:
- if (onig_warn != onig_null_warn) {
- onig_syntax_warn(env, "regular expression has redundant nested repeat operator '%s'",
- PopularQStr[targetq_num]);
- }
- goto warn_exit;
- break;
-
- default:
- if (onig_warn != onig_null_warn) {
- onig_syntax_warn(env, "nested repeat operator '%s' and '%s' was replaced with '%s' in regular expression",
- PopularQStr[targetq_num], PopularQStr[nestq_num],
- ReduceQStr[ReduceTypeTable[targetq_num][nestq_num]]);
- }
- goto warn_exit;
- break;
- }
+ switch (ReduceTypeTable[targetq_num][nestq_num]) {
+ case RQ_ASIS:
+ break;
+
+ case RQ_DEL:
+ if (onig_warn != onig_null_warn) {
+ onig_syntax_warn(env, "regular expression has redundant nested repeat operator '%s'",
+ PopularQStr[targetq_num]);
+ }
+ goto warn_exit;
+ break;
+
+ default:
+ if (onig_warn != onig_null_warn) {
+ onig_syntax_warn(env, "nested repeat operator '%s' and '%s' was replaced with '%s' in regular expression",
+ PopularQStr[targetq_num], PopularQStr[nestq_num],
+ ReduceQStr[ReduceTypeTable[targetq_num][nestq_num]]);
+ }
+ goto warn_exit;
+ break;
+ }
}
warn_exit:
@@ -5482,85 +5469,6 @@ set_quantifier(Node* qnode, Node* target, int group, ScanEnv* env)
}
-#ifdef USE_SHARED_CCLASS_TABLE
-
-#define THRESHOLD_RANGE_NUM_FOR_SHARE_CCLASS 8
-
-/* for ctype node hash table */
-
-typedef struct {
- OnigEncoding enc;
- int not;
- int type;
-} type_cclass_key;
-
-static int type_cclass_cmp(type_cclass_key* x, type_cclass_key* y)
-{
- if (x->type != y->type) return 1;
- if (x->enc != y->enc) return 1;
- if (x->not != y->not) return 1;
- return 0;
-}
-
-static st_index_t type_cclass_hash(type_cclass_key* key)
-{
- int i, val;
- UChar *p;
-
- val = 0;
-
- p = (UChar* )&(key->enc);
- for (i = 0; i < (int )sizeof(key->enc); i++) {
- val = val * 997 + (int )*p++;
- }
-
- p = (UChar* )(&key->type);
- for (i = 0; i < (int )sizeof(key->type); i++) {
- val = val * 997 + (int )*p++;
- }
-
- val += key->not;
- return val + (val >> 5);
-}
-
-static const struct st_hash_type type_type_cclass_hash = {
- type_cclass_cmp,
- type_cclass_hash,
-};
-
-static st_table* OnigTypeCClassTable;
-
-
-static int
-i_free_shared_class(type_cclass_key* key, Node* node, void* arg ARG_UNUSED)
-{
- if (IS_NOT_NULL(node)) {
- CClassNode* cc = NCCLASS(node);
- if (IS_NOT_NULL(cc->mbuf)) xfree(cc->mbuf);
- xfree(node);
- }
-
- if (IS_NOT_NULL(key)) xfree(key);
- return ST_DELETE;
-}
-
-extern int
-onig_free_shared_cclass_table(void)
-{
- /* THREAD_ATOMIC_START; */
- if (IS_NOT_NULL(OnigTypeCClassTable)) {
- onig_st_foreach(OnigTypeCClassTable, i_free_shared_class, 0);
- onig_st_free_table(OnigTypeCClassTable);
- OnigTypeCClassTable = NULL;
- }
- /* THREAD_ATOMIC_END; */
-
- return 0;
-}
-
-#endif /* USE_SHARED_CCLASS_TABLE */
-
-
#ifndef CASE_FOLD_IS_APPLIED_INSIDE_NEGATIVE_CCLASS
static int
clear_not_flag_cclass(CClassNode* cc, OnigEncoding enc)
@@ -5603,7 +5511,7 @@ i_apply_case_fold(OnigCodePoint from, OnigCodePoint to[],
CClassNode* cc;
CClassNode* asc_cc;
BitSetRef bs;
- int add_flag;
+ int add_flag, r;
iarg = (IApplyCaseFoldArg* )arg;
env = iarg->env;
@@ -5630,7 +5538,8 @@ i_apply_case_fold(OnigCodePoint from, OnigCodePoint to[],
(is_in == 0 && IS_NCCLASS_NOT(cc))) {
if (add_flag) {
if (ONIGENC_MBC_MINLEN(env->enc) > 1 || *to >= SINGLE_BYTE_SIZE) {
- add_code_range0(&(cc->mbuf), env, *to, *to, 0);
+ r = add_code_range0(&(cc->mbuf), env, *to, *to, 0);
+ if (r < 0) return r;
}
else {
BITSET_SET_BIT(bs, *to);
@@ -5642,7 +5551,8 @@ i_apply_case_fold(OnigCodePoint from, OnigCodePoint to[],
if (add_flag) {
if (ONIGENC_MBC_MINLEN(env->enc) > 1 || *to >= SINGLE_BYTE_SIZE) {
if (IS_NCCLASS_NOT(cc)) clear_not_flag_cclass(cc, env->enc);
- add_code_range0(&(cc->mbuf), env, *to, *to, 0);
+ r = add_code_range0(&(cc->mbuf), env, *to, *to, 0);
+ if (r < 0) return r;
}
else {
if (IS_NCCLASS_NOT(cc)) {
@@ -5732,7 +5642,7 @@ node_linebreak(Node** np, ScanEnv* env)
Node* target1 = NULL;
Node* target2 = NULL;
CClassNode* cc;
- int num1, num2;
+ int num1, num2, r;
UChar buf[ONIGENC_CODE_TO_MBC_MAXLEN * 2];
/* \x0D\x0A */
@@ -5748,7 +5658,8 @@ node_linebreak(Node** np, ScanEnv* env)
if (IS_NULL(right)) goto err;
cc = NCCLASS(right);
if (ONIGENC_MBC_MINLEN(env->enc) > 1) {
- add_code_range(&(cc->mbuf), env, 0x0A, 0x0D);
+ r = add_code_range(&(cc->mbuf), env, 0x0A, 0x0D);
+ if (r != 0) goto err;
}
else {
bitset_set_range(env, cc->bs, 0x0A, 0x0D);
@@ -5757,8 +5668,10 @@ node_linebreak(Node** np, ScanEnv* env)
/* TODO: move this block to enc/unicode.c */
if (ONIGENC_IS_UNICODE(env->enc)) {
/* UTF-8, UTF-16BE/LE, UTF-32BE/LE */
- add_code_range(&(cc->mbuf), env, 0x85, 0x85);
- add_code_range(&(cc->mbuf), env, 0x2028, 0x2029);
+ r = add_code_range(&(cc->mbuf), env, 0x85, 0x85);
+ if (r != 0) goto err;
+ r = add_code_range(&(cc->mbuf), env, 0x2028, 0x2029);
+ if (r != 0) goto err;
}
/* ...|... */
@@ -5785,84 +5698,357 @@ node_linebreak(Node** np, ScanEnv* env)
}
static int
+propname2ctype(ScanEnv* env, const char* propname)
+{
+ UChar* name = (UChar* )propname;
+ UChar* name_end = name + strlen(propname);
+ int ctype = env->enc->property_name_to_ctype(ONIG_ENCODING_ASCII,
+ name, name_end);
+ if (ctype < 0) {
+ onig_scan_env_set_error_string(env, ctype, name, name_end);
+ }
+ return ctype;
+}
+
+static int
+add_property_to_cc(CClassNode* cc, const char* propname, int not, ScanEnv* env)
+{
+ int ctype = propname2ctype(env, propname);
+ if (ctype < 0) return ctype;
+ return add_ctype_to_cc(cc, ctype, not, 0, env);
+}
+
+/*
+ * helper methods for node_extended_grapheme_cluster (/\X/)
+ */
+static int
+create_property_node(Node **np, ScanEnv* env, const char* propname)
+{
+ int r;
+ CClassNode* cc;
+
+ *np = node_new_cclass();
+ if (IS_NULL(*np)) return ONIGERR_MEMORY;
+ cc = NCCLASS(*np);
+ r = add_property_to_cc(cc, propname, 0, env);
+ if (r != 0)
+ onig_node_free(*np);
+ return r;
+}
+
+static int
+quantify_node(Node **np, int lower, int upper)
+{
+ Node* tmp = node_new_quantifier(lower, upper, 0);
+ if (IS_NULL(tmp)) return ONIGERR_MEMORY;
+ NQTFR(tmp)->target = *np;
+ *np = tmp;
+ return 0;
+}
+
+static int
+quantify_property_node(Node **np, ScanEnv* env, const char* propname, char repetitions)
+{
+ int r;
+ int lower = 0;
+ int upper = REPEAT_INFINITE;
+
+ r = create_property_node(np, env, propname);
+ if (r != 0) return r;
+ switch (repetitions) {
+ case '?': upper = 1; break;
+ case '+': lower = 1; break;
+ case '*': break;
+ case '2': lower = upper = 2; break;
+ default : return ONIGERR_PARSER_BUG;
+ }
+ return quantify_node(np, lower, upper);
+}
+
+#define LIST 0
+#define ALT 1
+
+/* IMPORTANT: Make sure node_array ends with NULL_NODE */
+static int
+create_node_from_array(int kind, Node **np, Node **node_array)
+{
+ Node* tmp = NULL_NODE;
+ int i = 0;
+
+ while (node_array[i] != NULL_NODE) i++;
+ while (--i >= 0) {
+ *np = kind==LIST ? node_new_list(node_array[i], tmp)
+ : onig_node_new_alt(node_array[i], tmp);
+ if (IS_NULL(*np)) {
+ while (i >= 0) {
+ onig_node_free(node_array[i]);
+ node_array[i--] = NULL_NODE;
+ }
+ onig_node_free(tmp);
+ return ONIGERR_MEMORY;
+ }
+ else
+ node_array[i] = NULL_NODE;
+ tmp = *np;
+ }
+ return 0;
+}
+
+#define R_ERR(call) r=(call);if(r!=0)goto err
+
+/* Memory layout for common node array:
+ * The main purpose is to be able to easily free all leftover nodes
+ * after an error. As a side effect, we share some memory.
+ *
+ * The layout is as shown below (each line corresponds to one call of
+ * create_node_from_array()). Because create_node_from_array sets all
+ * nodes of the source to NULL_NODE, we can overlap the target array
+ * as long as we do not override the actual target location.
+ *
+ * Target Array name Index
+ *
+ * node_array 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ * top_alts alts[5] 0 1 2 3 4*
+ * alts+1 list[4] 0 1 2 3*
+ * list+1 core_alts[7] 0 1 2 3 4 5 6*
+ * core_alts+0 H_list[4] 0 1 2 3*
+ * H_list+1 H_alt2[4] 0 1 2 3*
+ * h_alt2+1 H_list2[3] 0 1 2*
+ * core_alts+4 XP_list[4] 0 1 2 3*
+ * XP_list+1 Ex_list[4] 0 1 2 3*
+ */
+#define NODE_COMMON_SIZE 16
+
+static int
node_extended_grapheme_cluster(Node** np, ScanEnv* env)
{
- /* same as (?>\P{M}\p{M}*) */
+ Node* tmp = NULL;
Node* np1 = NULL;
- Node* np2 = NULL;
- Node* qn = NULL;
- Node* list1 = NULL;
- Node* list2 = NULL;
+ Node* top_alt = NULL;
int r = 0;
+ int num1;
+ int i;
+ int any_target_position;
+ UChar buf[ONIGENC_CODE_TO_MBC_MAXLEN * 2];
+ OnigOptionType option;
+ /* node_common is function-global so that we can free all nodes
+ * in case of error. Unused slots are set to NULL_NODE at all times. */
+ Node *node_common[NODE_COMMON_SIZE];
+ Node **alts = node_common+0; /* size: 5 */
+
+ for (i=0; i<NODE_COMMON_SIZE; i++)
+ node_common[i] = NULL_NODE;
+
+ /* CRLF, common for both Unicode and non-Unicode */
+ /* \x0D\x0A */
+ r = ONIGENC_CODE_TO_MBC(env->enc, 0x0D, buf);
+ if (r < 0) goto err;
+ num1 = r;
+ r = ONIGENC_CODE_TO_MBC(env->enc, 0x0A, buf + num1);
+ if (r < 0) goto err;
+ alts[0] = node_new_str_raw(buf, buf + num1 + r);
+ if (IS_NULL(alts[0])) goto err;
#ifdef USE_UNICODE_PROPERTIES
- if (ONIGENC_IS_UNICODE(env->enc)) {
- /* UTF-8, UTF-16BE/LE, UTF-32BE/LE */
- CClassNode* cc1;
- CClassNode* cc2;
- UChar* propname = (UChar* )"M";
- int ctype = env->enc->property_name_to_ctype(ONIG_ENCODING_ASCII,
- propname, propname + 1);
- if (ctype >= 0) {
- /* \P{M} */
- np1 = node_new_cclass();
- if (IS_NULL(np1)) goto err;
- cc1 = NCCLASS(np1);
- r = add_ctype_to_cc(cc1, ctype, 0, 0, env);
- if (r != 0) goto err;
- NCCLASS_SET_NOT(cc1);
+ if (ONIGENC_IS_UNICODE(env->enc)) { /* UTF-8, UTF-16BE/LE, UTF-32BE/LE */
+ CClassNode* cc;
+
+ if (propname2ctype(env, "Grapheme_Cluster_Break=Extend") < 0) goto err;
+ /* Unicode 11.0.0
+ * CRLF (already done)
+ * | [Control CR LF]
+ * | precore* core postcore*
+ * | . (to catch invalid stuff, because this seems to be spec for String#grapheme_clusters) */
+
+ /* [Control CR LF] (CR and LF are not in the spec, but this is a conformed fix) */
+ alts[1] = node_new_cclass();
+ if (IS_NULL(alts[1])) goto err;
+ cc = NCCLASS(alts[1]);
+ R_ERR(add_property_to_cc(cc, "Grapheme_Cluster_Break=Control", 0, env));
+ if (ONIGENC_MBC_MINLEN(env->enc) > 1) { /* UTF-16/UTF-32 */
+ R_ERR(add_code_range(&(cc->mbuf), env, 0x000A, 0x000A)); /* CR */
+ R_ERR(add_code_range(&(cc->mbuf), env, 0x000D, 0x000D)); /* LF */
+ }
+ else {
+ BITSET_SET_BIT(cc->bs, 0x0a);
+ BITSET_SET_BIT(cc->bs, 0x0d);
+ }
- /* \p{M}* */
- np2 = node_new_cclass();
- if (IS_NULL(np2)) goto err;
- cc2 = NCCLASS(np2);
- r = add_ctype_to_cc(cc2, ctype, 0, 0, env);
- if (r != 0) goto err;
+ /* precore* core postcore* */
+ {
+ Node **list = alts + 3; /* size: 4 */
- qn = node_new_quantifier(0, REPEAT_INFINITE, 0);
- if (IS_NULL(qn)) goto err;
- NQTFR(qn)->target = np2;
- np2 = NULL;
-
- /* \P{M}\p{M}* */
- list2 = node_new_list(qn, NULL_NODE);
- if (IS_NULL(list2)) goto err;
- qn = NULL;
- list1 = node_new_list(np1, list2);
- if (IS_NULL(list1)) goto err;
- np1 = NULL;
- list2 = NULL;
-
- /* (?>...) */
- *np = node_new_enclose(ENCLOSE_STOP_BACKTRACK);
- if (IS_NULL(*np)) goto err;
- NENCLOSE(*np)->target = list1;
- return ONIG_NORMAL;
+ /* precore*; precore := Prepend */
+ R_ERR(quantify_property_node(list+0, env, "Grapheme_Cluster_Break=Prepend", '*'));
+
+ /* core := hangul-syllable
+ * | ri-sequence
+ * | xpicto-sequence
+ * | [^Control CR LF] */
+ {
+ Node **core_alts = list + 2; /* size: 7 */
+
+ /* hangul-syllable :=
+ * L* (V+ | LV V* | LVT) T*
+ * | L+
+ * | T+ */
+ /* hangul-syllable is an alternative (would be called H_alt)
+ * inside an alternative, but we flatten it into core_alts */
+
+ /* L* (V+ | LV V* | LVT) T* */
+ {
+ Node **H_list = core_alts + 1; /* size: 4 */
+ R_ERR(quantify_property_node(H_list+0, env, "Grapheme_Cluster_Break=L", '*'));
+
+ /* V+ | LV V* | LVT */
+ {
+ Node **H_alt2 = H_list + 2; /* size: 4 */
+ R_ERR(quantify_property_node(H_alt2+0, env, "Grapheme_Cluster_Break=V", '+'));
+
+ /* LV V* */
+ {
+ Node **H_list2 = H_alt2 + 2; /* size: 3 */
+
+ R_ERR(create_property_node(H_list2+0, env, "Grapheme_Cluster_Break=LV"));
+ R_ERR(quantify_property_node(H_list2+1, env, "Grapheme_Cluster_Break=V", '*'));
+ R_ERR(create_node_from_array(LIST, H_alt2+1, H_list2));
+ }
+
+ R_ERR(create_property_node(H_alt2+2, env, "Grapheme_Cluster_Break=LVT"));
+ R_ERR(create_node_from_array(ALT, H_list+1, H_alt2));
+ }
+
+ R_ERR(quantify_property_node(H_list+2, env, "Grapheme_Cluster_Break=T", '*'));
+ R_ERR(create_node_from_array(LIST, core_alts+0, H_list));
+ }
+
+ R_ERR(quantify_property_node(core_alts+1, env, "Grapheme_Cluster_Break=L", '+'));
+ R_ERR(quantify_property_node(core_alts+2, env, "Grapheme_Cluster_Break=T", '+'));
+ /* end of hangul-syllable */
+
+ /* ri-sequence := RI RI */
+ R_ERR(quantify_property_node(core_alts+3, env, "Regional_Indicator", '2'));
+
+ /* xpicto-sequence := \p{Extended_Pictographic} (Extend* ZWJ \p{Extended_Pictographic})* */
+ {
+ Node **XP_list = core_alts + 5; /* size: 3 */
+ R_ERR(create_property_node(XP_list+0, env, "Extended_Pictographic"));
+
+ /* (Extend* ZWJ \p{Extended_Pictographic})* */
+ {
+ Node **Ex_list = XP_list + 2; /* size: 4 */
+ /* assert(Ex_list+4 == node_common+NODE_COMMON_SIZE); */
+ R_ERR(quantify_property_node(Ex_list+0, env, "Grapheme_Cluster_Break=Extend", '*'));
+
+ /* ZWJ (ZERO WIDTH JOINER) */
+ r = ONIGENC_CODE_TO_MBC(env->enc, 0x200D, buf);
+ if (r < 0) goto err;
+ Ex_list[1] = node_new_str_raw(buf, buf + r);
+ if (IS_NULL(Ex_list[1])) goto err;
+
+ R_ERR(create_property_node(Ex_list+2, env, "Extended_Pictographic"));
+ R_ERR(create_node_from_array(LIST, XP_list+1, Ex_list));
+ }
+ R_ERR(quantify_node(XP_list+1, 0, REPEAT_INFINITE)); /* TODO: Check about node freeing */
+
+ R_ERR(create_node_from_array(LIST, core_alts+4, XP_list));
+ }
+
+ /* [^Control CR LF] */
+ core_alts[5] = node_new_cclass();
+ if (IS_NULL(core_alts[5])) goto err;
+ cc = NCCLASS(core_alts[5]);
+ if (ONIGENC_MBC_MINLEN(env->enc) > 1) { /* UTF-16/UTF-32 */
+ BBuf *inverted_buf = NULL;
+
+ /* TODO: fix false warning */
+ const int dup_not_warned = env->warnings_flag | ~ONIG_SYN_WARN_CC_DUP;
+ env->warnings_flag |= ONIG_SYN_WARN_CC_DUP;
+
+ /* Start with a positive buffer and invert at the end.
+ * Otherwise, adding single-character ranges work the wrong way. */
+ R_ERR(add_property_to_cc(cc, "Grapheme_Cluster_Break=Control", 0, 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 */
+
+ env->warnings_flag &= dup_not_warned; /* TODO: fix false warning */
+ }
+ else {
+ R_ERR(add_property_to_cc(cc, "Grapheme_Cluster_Break=Control", 1, env));
+ BITSET_CLEAR_BIT(cc->bs, 0x0a);
+ BITSET_CLEAR_BIT(cc->bs, 0x0d);
+ }
+
+ R_ERR(create_node_from_array(ALT, list+1, core_alts));
+ }
+
+ /* postcore*; postcore = [Extend ZWJ SpacingMark] */
+ R_ERR(create_property_node(list+2, env, "Grapheme_Cluster_Break=Extend"));
+ cc = NCCLASS(list[2]);
+ R_ERR(add_property_to_cc(cc, "Grapheme_Cluster_Break=SpacingMark", 0, env));
+ R_ERR(add_code_range(&(cc->mbuf), env, 0x200D, 0x200D));
+ R_ERR(quantify_node(list+2, 0, REPEAT_INFINITE));
+
+ R_ERR(create_node_from_array(LIST, alts+2, list));
}
+
+ any_target_position = 3;
}
+ else
#endif /* USE_UNICODE_PROPERTIES */
- if (IS_NULL(*np)) {
- /* PerlSyntax: (?s:.), RubySyntax: (?m:.) */
- OnigOptionType option;
- np1 = node_new_anychar();
- if (IS_NULL(np1)) goto err;
+ {
+ any_target_position = 1;
+ }
+ /* PerlSyntax: (?s:.), RubySyntax: (?m:.), common for both Unicode and non-Unicode */
+ /* Not in Unicode spec (UAX #29), but added to catch invalid stuff,
+ * because this is Ruby spec for String#grapheme_clusters. */
+ np1 = node_new_anychar();
+ if (IS_NULL(np1)) goto err;
+
+ option = env->option;
+ ONOFF(option, ONIG_OPTION_MULTILINE, 0);
+ tmp = node_new_option(option);
+ if (IS_NULL(tmp)) goto err;
+ NENCLOSE(tmp)->target = np1;
+ alts[any_target_position] = tmp;
+ np1 = NULL;
+
+ R_ERR(create_node_from_array(ALT, &top_alt, alts));
+
+ /* (?>): For efficiency, because there is no text piece
+ * that is not in a grapheme cluster, and there is only one way
+ * to split a string into grapheme clusters. */
+ tmp = node_new_enclose(ENCLOSE_STOP_BACKTRACK);
+ if (IS_NULL(tmp)) goto err;
+ NENCLOSE(tmp)->target = top_alt;
+ np1 = tmp;
+
+#ifdef USE_UNICODE_PROPERTIES
+ if (ONIGENC_IS_UNICODE(env->enc)) {
+ /* Don't ignore case. */
option = env->option;
- ONOFF(option, ONIG_OPTION_MULTILINE, 0);
+ ONOFF(option, ONIG_OPTION_IGNORECASE, 1);
*np = node_new_option(option);
if (IS_NULL(*np)) goto err;
NENCLOSE(*np)->target = np1;
}
+ else
+#endif
+ {
+ *np = np1;
+ }
return ONIG_NORMAL;
err:
onig_node_free(np1);
- onig_node_free(np2);
- onig_node_free(qn);
- onig_node_free(list1);
- onig_node_free(list2);
+ for (i=0; i<NODE_COMMON_SIZE; i++)
+ onig_node_free(node_common[i]);
return (r == 0) ? ONIGERR_MEMORY : r;
}
+#undef R_ERR
static int
countbits(unsigned int bits)
@@ -5893,7 +6079,7 @@ is_onechar_cclass(CClassNode* cc, OnigCodePoint* code)
/* only one char found in the bbuf, save the code point. */
c = data[0];
if (((c < SINGLE_BYTE_SIZE) && BITSET_AT(cc->bs, c))) {
- /* skip if c is included in the bitset */
+ /* skip if c is included in the bitset */
c = not_found;
}
}
@@ -5907,9 +6093,9 @@ is_onechar_cclass(CClassNode* cc, OnigCodePoint* code)
Bits b1 = cc->bs[i];
if (b1 != 0) {
if (((b1 & (b1 - 1)) == 0) && (c == not_found)) {
- c = BITS_IN_ROOM * i + countbits(b1 - 1);
+ c = BITS_IN_ROOM * i + countbits(b1 - 1);
} else {
- return 0; /* the character class contains multiple chars */
+ return 0; /* the character class contains multiple chars */
}
}
}
@@ -5954,7 +6140,10 @@ parse_exp(Node** np, OnigToken* tok, int term,
env->option = NENCLOSE(*np)->option;
r = fetch_token(tok, src, end, env);
- if (r < 0) return r;
+ if (r < 0) {
+ env->option = prev;
+ return r;
+ }
r = parse_subexp(&target, tok, term, src, end, env);
env->option = prev;
if (r < 0) {
@@ -6107,69 +6296,13 @@ parse_exp(Node** np, OnigToken* tok, int term,
{
CClassNode* cc;
-#ifdef USE_SHARED_CCLASS_TABLE
- const OnigCodePoint *mbr;
- OnigCodePoint sb_out;
-
- r = ONIGENC_GET_CTYPE_CODE_RANGE(env->enc, tok->u.prop.ctype,
- &sb_out, &mbr);
- if (r == 0 &&
- ! IS_ASCII_RANGE(env->option) &&
- ONIGENC_CODE_RANGE_NUM(mbr)
- >= THRESHOLD_RANGE_NUM_FOR_SHARE_CCLASS) {
- type_cclass_key key;
- type_cclass_key* new_key;
-
- key.enc = env->enc;
- key.not = tok->u.prop.not;
- key.type = tok->u.prop.ctype;
-
- THREAD_ATOMIC_START;
-
- if (IS_NULL(OnigTypeCClassTable)) {
- OnigTypeCClassTable
- = onig_st_init_table_with_size(&type_type_cclass_hash, 10);
- if (IS_NULL(OnigTypeCClassTable)) {
- THREAD_ATOMIC_END;
- return ONIGERR_MEMORY;
- }
- }
- else {
- if (onig_st_lookup(OnigTypeCClassTable, (st_data_t )&key,
- (st_data_t* )np)) {
- THREAD_ATOMIC_END;
- break;
- }
- }
-
- *np = node_new_cclass_by_codepoint_range(tok->u.prop.not,
- sb_out, mbr);
- if (IS_NULL(*np)) {
- THREAD_ATOMIC_END;
- return ONIGERR_MEMORY;
- }
-
- cc = NCCLASS(*np);
- NCCLASS_SET_SHARE(cc);
- new_key = (type_cclass_key* )xmalloc(sizeof(type_cclass_key));
- xmemcpy(new_key, &key, sizeof(type_cclass_key));
- onig_st_add_direct(OnigTypeCClassTable, (st_data_t )new_key,
- (st_data_t )*np);
-
- THREAD_ATOMIC_END;
- }
- else {
-#endif
- *np = node_new_cclass();
- CHECK_NULL_RETURN_MEMERR(*np);
- cc = NCCLASS(*np);
- r = add_ctype_to_cc(cc, tok->u.prop.ctype, 0,
- IS_ASCII_RANGE(env->option), env);
- if (r != 0) return r;
- if (tok->u.prop.not != 0) NCCLASS_SET_NOT(cc);
-#ifdef USE_SHARED_CCLASS_TABLE
- }
-#endif
+ *np = node_new_cclass();
+ CHECK_NULL_RETURN_MEMERR(*np);
+ cc = NCCLASS(*np);
+ r = add_ctype_to_cc(cc, tok->u.prop.ctype, 0,
+ IS_ASCII_RANGE(env->option), env);
+ if (r != 0) return r;
+ if (tok->u.prop.not != 0) NCCLASS_SET_NOT(cc);
}
break;
@@ -6399,6 +6532,9 @@ parse_subexp(Node** top, OnigToken* tok, int term,
Node *node, **headp;
*top = NULL;
+ env->parse_depth++;
+ if (env->parse_depth > ParseDepthLimit)
+ return ONIGERR_PARSE_DEPTH_LIMIT_OVER;
r = parse_branch(&node, tok, term, src, end, env);
if (r < 0) {
onig_node_free(node);
@@ -6436,6 +6572,7 @@ parse_subexp(Node** top, OnigToken* tok, int term,
return ONIGERR_PARSER_BUG;
}
+ env->parse_depth--;
return r;
}
diff --git a/regparse.h b/regparse.h
index caf0790b1c..acdd3e2f5c 100644
--- a/regparse.h
+++ b/regparse.h
@@ -1,11 +1,11 @@
-#ifndef ONIGURUMA_REGPARSE_H
-#define ONIGURUMA_REGPARSE_H
+#ifndef ONIGMO_REGPARSE_H
+#define ONIGMO_REGPARSE_H
/**********************************************************************
regparse.h - Onigmo (Oniguruma-mod) (regular expression library)
**********************************************************************/
/*-
* Copyright (c) 2002-2007 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
- * Copyright (c) 2011 K.Takata <kentkt AT csc DOT jp>
+ * Copyright (c) 2011-2016 K.Takata <kentkt AT csc DOT jp>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -95,6 +95,7 @@ RUBY_SYMBOL_EXPORT_BEGIN
#define ENCLOSE_OPTION (1<<1)
#define ENCLOSE_STOP_BACKTRACK (1<<2)
#define ENCLOSE_CONDITION (1<<3)
+#define ENCLOSE_ABSENT (1<<4)
#define NODE_STR_MARGIN 16
#define NODE_STR_BUF_SIZE 24 /* sizeof(CClassNode) - sizeof(int)*4 */
@@ -185,7 +186,7 @@ typedef struct {
int target_empty_info;
struct _Node* head_exact;
struct _Node* next_head_exact;
- int is_refered; /* include called node. don't eliminate even if {0} */
+ int is_referred; /* include called node. don't eliminate even if {0} */
#ifdef USE_COMBINATION_EXPLOSION_CHECK
int comb_exp_check_num; /* 1,2,3...: check, 0: no check */
#endif
@@ -317,9 +318,12 @@ typedef struct {
int curr_max_regnum;
int has_recursion;
#endif
+ unsigned int parse_depth;
int warnings_flag;
+#ifdef RUBY
const char* sourcefile;
int sourceline;
+#endif
} ScanEnv;
@@ -332,36 +336,35 @@ typedef struct {
int new_val;
} GroupNumRemap;
-extern int onig_renumber_name_table P_((regex_t* reg, GroupNumRemap* map));
+extern int onig_renumber_name_table(regex_t* reg, GroupNumRemap* map);
#endif
-extern int onig_strncmp P_((const UChar* s1, const UChar* s2, int n));
-extern void onig_strcpy P_((UChar* dest, const UChar* src, const UChar* end));
-extern void onig_scan_env_set_error_string P_((ScanEnv* env, int ecode, UChar* arg, UChar* arg_end));
-extern int onig_scan_unsigned_number P_((UChar** src, const UChar* end, OnigEncoding enc));
-extern void onig_reduce_nested_quantifier P_((Node* pnode, Node* cnode));
-extern void onig_node_conv_to_str_node P_((Node* node, int raw));
-extern int onig_node_str_cat P_((Node* node, const UChar* s, const UChar* end));
-extern int onig_node_str_set P_((Node* node, const UChar* s, const UChar* end));
-extern void onig_node_free P_((Node* node));
-extern Node* onig_node_new_enclose P_((int type));
-extern Node* onig_node_new_anchor P_((int type));
-extern Node* onig_node_new_str P_((const UChar* s, const UChar* end));
-extern Node* onig_node_new_list P_((Node* left, Node* right));
-extern Node* onig_node_list_add P_((Node* list, Node* x));
-extern Node* onig_node_new_alt P_((Node* left, Node* right));
-extern void onig_node_str_clear P_((Node* node));
-extern int onig_free_node_list P_((void));
-extern int onig_names_free P_((regex_t* reg));
-extern int onig_parse_make_tree P_((Node** root, const UChar* pattern, const UChar* end, regex_t* reg, ScanEnv* env));
-extern int onig_free_shared_cclass_table P_((void));
+extern int onig_strncmp(const UChar* s1, const UChar* s2, int n);
+extern void onig_strcpy(UChar* dest, const UChar* src, const UChar* end);
+extern void onig_scan_env_set_error_string(ScanEnv* env, int ecode, UChar* arg, UChar* arg_end);
+extern int onig_scan_unsigned_number(UChar** src, const UChar* end, OnigEncoding enc);
+extern void onig_reduce_nested_quantifier(Node* pnode, Node* cnode);
+extern void onig_node_conv_to_str_node(Node* node, int raw);
+extern int onig_node_str_cat(Node* node, const UChar* s, const UChar* end);
+extern int onig_node_str_set(Node* node, const UChar* s, const UChar* end);
+extern void onig_node_free(Node* node);
+extern Node* onig_node_new_enclose(int type);
+extern Node* onig_node_new_anchor(int type);
+extern Node* onig_node_new_str(const UChar* s, const UChar* end);
+extern Node* onig_node_new_list(Node* left, Node* right);
+extern Node* onig_node_list_add(Node* list, Node* x);
+extern Node* onig_node_new_alt(Node* left, Node* right);
+extern void onig_node_str_clear(Node* node);
+extern int onig_names_free(regex_t* reg);
+extern int onig_parse_make_tree(Node** root, const UChar* pattern, const UChar* end, regex_t* reg, ScanEnv* env);
+extern int onig_free_shared_cclass_table(void);
#ifdef ONIG_DEBUG
-#ifdef USE_NAMED_GROUP
+# ifdef USE_NAMED_GROUP
extern int onig_print_names(FILE*, regex_t*);
-#endif
+# endif
#endif
RUBY_SYMBOL_EXPORT_END
-#endif /* ONIGURUMA_REGPARSE_H */
+#endif /* ONIGMO_REGPARSE_H */
diff --git a/regsyntax.c b/regsyntax.c
index 7cb98f2d46..657ffcd0f3 100644
--- a/regsyntax.c
+++ b/regsyntax.c
@@ -3,7 +3,7 @@
**********************************************************************/
/*-
* Copyright (c) 2002-2006 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
- * Copyright (c) 2011-2012 K.Takata <kentkt AT csc DOT jp>
+ * Copyright (c) 2011-2016 K.Takata <kentkt AT csc DOT jp>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -229,7 +229,7 @@ const OnigSyntaxType OnigSyntaxPerl = {
(( SYN_GNU_REGEX_OP | ONIG_SYN_OP_QMARK_NON_GREEDY |
ONIG_SYN_OP_ESC_OCTAL3 | ONIG_SYN_OP_ESC_X_HEX2 |
ONIG_SYN_OP_ESC_X_BRACE_HEX8 | ONIG_SYN_OP_ESC_CONTROL_CHARS |
- ONIG_SYN_OP_ESC_C_CONTROL )
+ ONIG_SYN_OP_ESC_O_BRACE_OCTAL | ONIG_SYN_OP_ESC_C_CONTROL )
& ~ONIG_SYN_OP_ESC_LTGT_WORD_BEGIN_END )
, ( ONIG_SYN_OP2_ESC_CAPITAL_Q_QUOTE |
ONIG_SYN_OP2_QMARK_GROUP_EFFECT | ONIG_SYN_OP2_OPTION_PERL |
@@ -248,7 +248,8 @@ const OnigSyntaxType OnigSyntaxPerl = {
ONIG_SYN_OP2_ESC_K_NAMED_BACKREF )
, ( SYN_GNU_REGEX_BV |
ONIG_SYN_ALLOW_MULTIPLEX_DEFINITION_NAME |
- ONIG_SYN_ALLOW_MULTIPLEX_DEFINITION_NAME_CALL )
+ ONIG_SYN_ALLOW_MULTIPLEX_DEFINITION_NAME_CALL |
+ ONIG_SYN_USE_LEFT_MOST_NAMED_GROUP )
, ( ONIG_OPTION_SINGLELINE | ONIG_OPTION_CAPTURE_GROUP )
,
{
@@ -332,25 +333,25 @@ onig_set_syntax_options(OnigSyntaxType* syntax, OnigOptionType options)
}
extern unsigned int
-onig_get_syntax_op(OnigSyntaxType* syntax)
+onig_get_syntax_op(const OnigSyntaxType* syntax)
{
return syntax->op;
}
extern unsigned int
-onig_get_syntax_op2(OnigSyntaxType* syntax)
+onig_get_syntax_op2(const OnigSyntaxType* syntax)
{
return syntax->op2;
}
extern unsigned int
-onig_get_syntax_behavior(OnigSyntaxType* syntax)
+onig_get_syntax_behavior(const OnigSyntaxType* syntax)
{
return syntax->behavior;
}
extern OnigOptionType
-onig_get_syntax_options(OnigSyntaxType* syntax)
+onig_get_syntax_options(const OnigSyntaxType* syntax)
{
return syntax->options;
}
diff --git a/ruby-runner.c b/ruby-runner.c
index c16e7c6829..c991178193 100644
--- a/ruby-runner.c
+++ b/ruby-runner.c
@@ -1,35 +1,98 @@
#define _POSIX_C_SOURCE 200809L
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include "ruby-runner.h"
+#include "ruby/config.h"
+
+#ifdef MAKE_MJIT_BUILD_DIR
+const char MJIT_HEADER[] = BUILDDIR "/" MJIT_MIN_HEADER;
+#else
#define STRINGIZE(expr) STRINGIZE0(expr)
#define STRINGIZE0(expr) #expr
-int
-main(int argc, char **argv)
+static void
+insert_env_path(const char *envname, const char *paths, size_t size, int prepend)
{
- static const char builddir[] = BUILDDIR;
- const char *libpath = getenv(LIBPATHENV);
+ const char *env = getenv(envname);
char c = 0;
+ size_t n = 0;
- if (libpath) {
- while ((c = *libpath) == PATH_SEP) ++libpath;
+ if (env) {
+ while ((c = *env) == PATH_SEP) ++env;
+ n = strlen(env);
+ while (n > 0 && env[n-1] == PATH_SEP) --n;
}
if (c) {
- size_t n = strlen(libpath);
- char *e = malloc(sizeof(builddir)+n+1);
- memcpy(e, builddir, sizeof(builddir)-1);
- e[sizeof(builddir)-1] = PATH_SEP;
- memcpy(e+sizeof(builddir), libpath, n+1);
- libpath = 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 {
- libpath = builddir;
+ env = paths;
+ }
+ setenv(envname, env, 1);
+}
+
+#define EXTOUT_DIR BUILDDIR"/"EXTOUT
+int
+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
+ ;
+#ifndef LOAD_RELATIVE
+ static const char mjit_build_dir[] = BUILDDIR"/mjit_build_dir."SOEXT;
+ struct stat stbuf;
+#endif
+ const size_t dirsize = sizeof(builddir);
+ const size_t namesize = sizeof(rubypath) - dirsize;
+ const char *rubyname = rubypath + dirsize;
+ char *arg0 = argv[0], *p;
+
+ insert_env_path(LIBPATHENV, builddir, dirsize, 1);
+ insert_env_path("RUBYLIB", rubylib, sizeof(rubylib), 0);
+#ifndef LOAD_RELATIVE
+ if (PRELOADENV[0] && stat(mjit_build_dir, &stbuf) == 0) {
+ insert_env_path(PRELOADENV, mjit_build_dir, sizeof(mjit_build_dir), 1);
+ setenv("MJIT_SEARCH_BUILD_DIR", "true", 0);
}
- setenv(LIBPATHENV, libpath, 1);
- execv(BUILDDIR"/"STRINGIZE(RUBY_INSTALL_NAME), argv);
+#endif
+
+ 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);
+ }
+ memcpy(p, rubyname, namesize);
+
+ execv(rubypath, argv);
+ perror(rubypath);
return -1;
}
+
+#endif /* MAKE_MJIT_BUILD_DIR */
diff --git a/ruby.c b/ruby.c
index feae61da64..a169141376 100644
--- a/ruby.c
+++ b/ruby.c
@@ -15,8 +15,10 @@
#include <windows.h>
#include <sys/cygwin.h>
#endif
-#include "internal.h"
+#include "ruby/encoding.h"
#include "ruby/thread.h"
+#include "ruby/version.h"
+#include "internal.h"
#include "eval_intern.h"
#include "dln.h"
#include <stdio.h>
@@ -50,6 +52,10 @@
#include "ruby/util.h"
+#include "mjit.h"
+
+void Init_ruby_description(void);
+
#ifndef HAVE_STDLIB_H
char *getenv();
#endif
@@ -73,6 +79,8 @@ char *getenv();
X(rubyopt) \
SEP \
X(frozen_string_literal) \
+ SEP \
+ X(jit) \
/* END OF FEATURES */
#define EACH_DEBUG_FEATURES(X, SEP) \
X(frozen_string_literal) \
@@ -114,21 +122,14 @@ enum feature_flag_bits {
enum dump_flag_bits {
dump_version_v,
EACH_DUMPS(DEFINE_DUMP, COMMA),
- dump_flag_count
+ dump_exit_bits = (DUMP_BIT(yydebug) | DUMP_BIT(syntax) |
+ DUMP_BIT(parsetree) | DUMP_BIT(parsetree_with_comment) |
+ DUMP_BIT(insns))
};
typedef struct ruby_cmdline_options ruby_cmdline_options_t;
struct ruby_cmdline_options {
- int sflag, xflag;
- int do_loop, do_print;
- int do_line, do_split;
- int do_search;
- unsigned int features;
- int verbose;
- int safe_level;
- unsigned int setids;
- unsigned int dump;
const char *script;
VALUE script_name;
VALUE e_script;
@@ -139,7 +140,21 @@ struct ruby_cmdline_options {
} enc;
} src, ext, intern;
VALUE req_list;
+ unsigned int features;
+ unsigned int dump;
+#if USE_MJIT
+ struct mjit_options mjit;
+#endif
+ int safe_level;
+ int sflag, xflag;
unsigned int warning: 1;
+ unsigned int verbose: 1;
+ unsigned int do_loop: 1;
+ unsigned int do_print: 1;
+ unsigned int do_line: 1;
+ unsigned int do_split: 1;
+ unsigned int do_search: 1;
+ unsigned int setids: 2;
};
static void init_ids(ruby_cmdline_options_t *);
@@ -158,6 +173,7 @@ enum {
& ~FEATURE_BIT(gems)
#endif
& ~FEATURE_BIT(frozen_string_literal)
+ & ~FEATURE_BIT(jit)
)
};
@@ -170,11 +186,16 @@ cmdline_options_init(ruby_cmdline_options_t *opt)
opt->ext.enc.index = -1;
opt->intern.enc.index = -1;
opt->features = DEFAULT_FEATURES;
+#ifdef MJIT_FORCE_ENABLE /* to use with: ./configure cppflags="-DMJIT_FORCE_ENABLE" */
+ opt->features |= FEATURE_BIT(jit);
+#endif
return opt;
}
-static NODE *load_file(VALUE, VALUE, int, ruby_cmdline_options_t *);
-static void forbid_setid(const char *, ruby_cmdline_options_t *);
+static rb_ast_t *load_file(VALUE parser, VALUE fname, VALUE f, int script,
+ 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)
static struct {
@@ -186,7 +207,7 @@ static void
show_usage_line(const char *str, unsigned int namelen, unsigned int secondlen, int help)
{
const unsigned int w = 16;
- const int wrap = help && namelen + secondlen - 2 > w;
+ const int wrap = help && namelen + secondlen - 1 > w;
printf(" %.*s%-*.*s%-*s%s\n", namelen-1, str,
(wrap ? 0 : w - namelen + 1),
(help ? secondlen-1 : 0), str + namelen,
@@ -227,26 +248,47 @@ usage(const char *name, int help)
M("-s", "", "enable some switch parsing for switches after script name"),
M("-S", "", "look for the script using PATH environment variable"),
M("-T[level=1]", "", "turn on tainting checks"),
- M("-v", ", --verbose", "print version number, then turn on verbose mode"),
+ M("-v", "", "print the version number, then turn on verbose mode"),
M("-w", "", "turn warnings on for your script"),
M("-W[level=2]", "", "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 with default options (experimental)"),
+ M("--jit-[option]","", "enable JIT with an option (experimental)"),
M("-h", "", "show this message, --help for more info"),
};
static const struct message help_msg[] = {
- M("--copyright", "", "print the copyright"),
- M("--enable=feature[,...]", ", --disable=feature[,...]",
- "enable or disable features"),
- M("--external-encoding=encoding", ", --internal-encoding=encoding",
+ M("--copyright", "", "print the copyright"),
+ M("--dump={insns|parsetree|...}[,...]", "",
+ "dump debug information. see below for available dump list"),
+ M("--enable={gems|rubyopt|...}[,...]", ", --disable={gems|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("--version", "", "print the version"),
- M("--help", "", "show this message, -h for short message"),
+ 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 message dumps[] = {
+ M("insns", "", "instruction sequences"),
+ M("yydebug", "", "yydebug of yacc parser generator"),
+ M("parsetree", "", "AST"),
+ M("parsetree_with_comment", "", "AST with comments"),
};
static const struct message features[] = {
M("gems", "", "rubygems (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("jit", "", "JIT compiler (default: disabled)"),
+ };
+ static const struct message mjit_options[] = {
+ M("--jit-warnings", "", "Enable printing JIT warnings"),
+ M("--jit-debug", "", "Enable JIT debugging (very slow)"),
+ M("--jit-wait", "", "Wait until JIT compilation is finished everytime (for testing)"),
+ M("--jit-save-temps", "", "Save JIT temporary files in $TMP or /tmp (for testing)"),
+ M("--jit-verbose=num", "", "Print JIT logs of level num or less to stderr (default: 0)"),
+ M("--jit-max-cache=num", "", "Max number of methods to be JIT-ed in a cache (default: 1000)"),
+ M("--jit-min-calls=num", "", "Number of calls to trigger JIT (for testing, default: 5)"),
};
int i;
const int num = numberof(usage_msg) - (help ? 1 : 0);
@@ -260,9 +302,15 @@ usage(const char *name, int help)
for (i = 0; i < numberof(help_msg); ++i)
SHOW(help_msg[i]);
+ puts("Dump List:");
+ for (i = 0; i < numberof(dumps); ++i)
+ SHOW(dumps[i]);
puts("Features:");
for (i = 0; i < numberof(features); ++i)
SHOW(features[i]);
+ puts("JIT options (experimental):");
+ for (i = 0; i < numberof(mjit_options); ++i)
+ SHOW(mjit_options[i]);
}
#define rubylib_path_new rb_str_new
@@ -421,6 +469,8 @@ str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to)
ECONV_UNDEF_REPLACE|ECONV_INVALID_REPLACE,
Qnil);
}
+#else
+# define str_conv_enc(str, from, to) (str)
#endif
void ruby_init_loadpath_safe(int safe_level);
@@ -431,18 +481,63 @@ ruby_init_loadpath(void)
ruby_init_loadpath_safe(0);
}
-#if defined(LOAD_RELATIVE) && defined(HAVE_DLADDR)
+#if defined(LOAD_RELATIVE)
static VALUE
-dladdr_path(const void* addr)
+runtime_libruby_path(void)
{
+#if defined _WIN32 || defined __CYGWIN__
+ DWORD len = RSTRING_EMBED_LEN_MAX, ret;
+ VALUE path;
+ VALUE wsopath = rb_str_new(0, len*sizeof(WCHAR));
+ WCHAR *wlibpath;
+ char *libpath;
+
+ while (wlibpath = (WCHAR *)RSTRING_PTR(wsopath),
+ ret = GetModuleFileNameW(libruby, wlibpath, len),
+ (ret == len))
+ {
+ 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);
+ }
+ }
+#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 */
+ }
+ }
+ }
+ len = WideCharToMultiByte(CP_UTF8, 0, wlibpath, ret, NULL, 0, NULL, NULL);
+ path = rb_utf8_str_new(0, len);
+ libpath = RSTRING_PTR(path);
+ WideCharToMultiByte(CP_UTF8, 0, wlibpath, ret, libpath, len, NULL, NULL);
+#endif
+ rb_str_resize(wsopath, 0);
+ return path;
+#elif defined(HAVE_DLADDR)
Dl_info dli;
VALUE fname, path;
+ const void* addr = (void *)(VALUE)expand_include_path;
- if (!dladdr(addr, &dli)) {
+ if (!dladdr((void *)addr, &dli)) {
return rb_str_new(0, 0);
}
#ifdef __linux__
- else if (dli.dli_fname == origarg.argv[0]) {
+ 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);
}
@@ -453,102 +548,66 @@ dladdr_path(const void* addr)
}
rb_str_resize(fname, 0);
return path;
+#else
+# error relative load path is not supported on this platform.
+#endif
}
#endif
#define INITIAL_LOAD_PATH_MARK rb_intern_const("@gem_prelude_index")
+VALUE ruby_archlibdir_path, ruby_prefix_path;
+
void
ruby_init_loadpath_safe(int safe_level)
{
- VALUE load_path;
+ VALUE load_path, archlibdir = 0;
ID id_initial_load_path_mark;
const char *paths = ruby_initial_load_paths;
#if defined LOAD_RELATIVE
-# if defined HAVE_DLADDR || defined __CYGWIN__ || defined _WIN32
-# define VARIABLE_LIBPATH 1
-# else
-# define VARIABLE_LIBPATH 0
-# endif
-# if VARIABLE_LIBPATH
+#if !defined ENABLE_MULTIARCH
+# define RUBY_ARCH_PATH ""
+#elif defined RUBY_ARCH
+# define RUBY_ARCH_PATH "/"RUBY_ARCH
+#else
+# define RUBY_ARCH_PATH "/"RUBY_PLATFORM
+#endif
char *libpath;
VALUE sopath;
-# else
- char libpath[MAXPATHLEN + 1];
-# endif
size_t baselen;
- char *p;
+ const char *p;
-#if defined _WIN32 || defined __CYGWIN__
- {
- DWORD len = RSTRING_EMBED_LEN_MAX, ret, i;
- VALUE wsopath = rb_str_new(0, len*sizeof(WCHAR));
- WCHAR *wlibpath;
- while (wlibpath = (WCHAR *)RSTRING_PTR(wsopath),
- ret = GetModuleFileNameW(libruby, wlibpath, len),
- (ret == len))
- {
- 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");
- 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);
- sopath = rb_utf8_str_new(0, len);
- libpath = RSTRING_PTR(sopath);
- WideCharToMultiByte(CP_UTF8, 0, wlibpath, ret, libpath, len, NULL, NULL);
- rb_str_resize(wsopath, 0);
- }
-#elif defined(HAVE_DLADDR)
- sopath = dladdr_path((void *)(VALUE)expand_include_path);
+ sopath = runtime_libruby_path();
libpath = RSTRING_PTR(sopath);
-#endif
-#if !VARIABLE_LIBPATH
- libpath[sizeof(libpath) - 1] = '\0';
-#endif
-#if defined DOSISH && !defined _WIN32
- translit_char(libpath, '\\', '/');
-#elif defined __CYGWIN__
- {
- const int win_to_posix = CCP_WIN_A_TO_POSIX | CCP_RELATIVE;
- size_t newsize = cygwin_conv_path(win_to_posix, libpath, 0, 0);
- if (newsize > 0) {
- VALUE rubylib = rb_str_new(0, newsize);
- p = RSTRING_PTR(rubylib);
- if (cygwin_conv_path(win_to_posix, libpath, p, newsize) == 0) {
- rb_str_resize(sopath, 0);
- sopath = rubylib;
- libpath = p;
- }
- }
- }
-#endif
p = strrchr(libpath, '/');
if (p) {
- static const char bindir[] = "/bin";
+ static const char libdir[] = "/"
#ifdef LIBDIR_BASENAME
- static const char libdir[] = "/"LIBDIR_BASENAME;
+ LIBDIR_BASENAME
#else
- static const char libdir[] = "/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;
- const ptrdiff_t libdir_len = (ptrdiff_t)sizeof(libdir) - 1;
-#ifdef ENABLE_MULTIARCH
- 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;
}
#ifdef ENABLE_MULTIARCH
@@ -562,32 +621,25 @@ ruby_init_loadpath_safe(int safe_level)
p = p2;
}
#endif
-#if !VARIABLE_LIBPATH
- *p = 0;
-#endif
- }
-#if !VARIABLE_LIBPATH
- else {
- strlcpy(libpath, ".", sizeof(libpath));
- p = libpath + 1;
}
baselen = p - libpath;
-#define PREFIX_PATH() rb_str_new(libpath, baselen)
-#else
- baselen = p - libpath;
rb_str_resize(sopath, baselen);
libpath = RSTRING_PTR(sopath);
#define PREFIX_PATH() sopath
-#endif
-
#define BASEPATH() rb_str_buf_cat(rb_str_buf_new(baselen+len), libpath, baselen)
-
#define RUBY_RELATIVE(path, len) rb_str_buf_cat(BASEPATH(), (path), (len))
#else
const size_t exec_prefix_len = strlen(ruby_exec_prefix);
#define RUBY_RELATIVE(path, len) rubylib_path_new((path), (len))
#define PREFIX_PATH() RUBY_RELATIVE(ruby_exec_prefix, exec_prefix_len)
#endif
+ rb_gc_register_address(&ruby_prefix_path);
+ ruby_prefix_path = PREFIX_PATH();
+ OBJ_FREEZE_RAW(ruby_prefix_path);
+ if (!archlibdir) archlibdir = ruby_prefix_path;
+ rb_gc_register_address(&ruby_archlibdir_path);
+ ruby_archlibdir_path = archlibdir;
+
load_path = GET_VM()->load_path;
if (safe_level == 0) {
@@ -603,7 +655,7 @@ ruby_init_loadpath_safe(int safe_level)
paths += len + 1;
}
- rb_const_set(rb_cObject, rb_intern_const("TMP_RUBY_PREFIX"), rb_obj_freeze(PREFIX_PATH()));
+ rb_const_set(rb_cObject, rb_intern_const("TMP_RUBY_PREFIX"), ruby_prefix_path);
}
@@ -614,11 +666,9 @@ add_modules(VALUE *req_list, const char *mod)
VALUE feature;
if (!list) {
- *req_list = list = rb_ary_new();
- RBASIC_CLEAR_CLASS(list);
+ *req_list = list = rb_ary_tmp_new(0);
}
- feature = rb_str_new2(mod);
- RBASIC_CLEAR_CLASS(feature);
+ feature = rb_str_cat_cstr(rb_str_tmp_new(0), mod);
rb_ary_push(list, feature);
}
@@ -720,14 +770,15 @@ moreswitches(const char *s, ruby_cmdline_options_t *opt, int envopt)
char **argv, *p;
const char *ap = 0;
VALUE argstr, argary;
+ void *ptr;
while (ISSPACE(*s)) s++;
if (!*s) return;
- argstr = rb_str_tmp_new((len = strlen(s)) + 2);
+ argstr = rb_str_tmp_new((len = strlen(s)) + (envopt!=0));
argary = rb_str_tmp_new(0);
p = RSTRING_PTR(argstr);
- *p++ = ' ';
+ if (envopt) *p++ = ' ';
memcpy(p, s, len + 1);
ap = 0;
rb_str_cat(argary, (char *)&ap, sizeof(ap));
@@ -742,9 +793,10 @@ moreswitches(const char *s, ruby_cmdline_options_t *opt, int envopt)
argc = RSTRING_LEN(argary) / sizeof(ap);
ap = 0;
rb_str_cat(argary, (char *)&ap, sizeof(ap));
- argv = (char **)RSTRING_PTR(argary);
+ argv = ptr = ALLOC_N(char *, argc);
+ MEMMOVE(argv, RSTRING_PTR(argary), char *, argc);
- while ((i = proc_options(argc, argv, opt, envopt)) > 1 && (argc -= i) > 0) {
+ while ((i = proc_options(argc, argv, opt, envopt)) > 1 && envopt && (argc -= i) > 0) {
argv += i;
if (**argv != '-') {
*--*argv = '-';
@@ -755,6 +807,7 @@ moreswitches(const char *s, ruby_cmdline_options_t *opt, int envopt)
}
}
+ ruby_xfree(ptr);
/* get rid of GC */
rb_str_resize(argary, 0);
rb_str_resize(argstr, 0);
@@ -848,12 +901,18 @@ disable_option(const char *str, int len, void *arg)
feature_option(str, len, arg, 0U);
}
+RUBY_EXTERN const int ruby_patchlevel;
+int ruby_env_debug_option(const char *str, int len, void *arg);
+
static void
debug_option(const char *str, int len, void *arg)
{
static const char list[] = EACH_DEBUG_FEATURES(LITERAL_NAME_ELEMENT, ", ");
#define SET_WHEN_DEBUG(bit) SET_WHEN(#bit, DEBUG_BIT(bit), str, len)
EACH_DEBUG_FEATURES(SET_WHEN_DEBUG, ;);
+#ifdef RUBY_DEVEL
+ if (ruby_patchlevel < 0 && ruby_env_debug_option(str, len, 0)) return;
+#endif
rb_warn("unknown argument for --debug: `%.*s'", len, str);
rb_warn("debug features are [%.*s].", (int)strlen(list), list);
}
@@ -891,6 +950,39 @@ set_option_encoding_once(const char *type, VALUE *name, const char *e, long elen
#define set_source_encoding_once(opt, e, elen) \
set_option_encoding_once("source", &(opt)->src.enc.name, (e), (elen))
+#if USE_MJIT
+static void
+setup_mjit_options(const char *s, struct mjit_options *mjit_opt)
+{
+ if (*s == 0) return;
+ else if (strcmp(s, "-warnings") == 0) {
+ mjit_opt->warnings = 1;
+ }
+ else if (strcmp(s, "-debug") == 0) {
+ mjit_opt->debug = 1;
+ }
+ else if (strcmp(s, "-wait") == 0) {
+ mjit_opt->wait = 1;
+ }
+ else if (strcmp(s, "-save-temps") == 0) {
+ mjit_opt->save_temps = 1;
+ }
+ else if (strncmp(s, "-verbose=", 9) == 0) {
+ mjit_opt->verbose = atoi(s + 9);
+ }
+ else if (strncmp(s, "-max-cache=", 11) == 0) {
+ mjit_opt->max_cache_size = atoi(s + 11);
+ }
+ else if (strncmp(s, "-min-calls=", 11) == 0) {
+ mjit_opt->min_calls = atoi(s + 11);
+ }
+ else {
+ rb_raise(rb_eRuntimeError,
+ "invalid MJIT option `%s' (--help will show valid MJIT options)", s + 1);
+ }
+}
+#endif
+
static long
proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
{
@@ -898,7 +990,7 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
const char *s;
int warning = opt->warning;
- if (argc == 0)
+ if (argc <= 0 || !argv)
return 0;
for (argc--, argv++; argc > 0; argc--, argv++) {
@@ -1048,6 +1140,7 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
case 'x':
if (envopt) goto noenvopt;
+ forbid_setid("-x");
opt->xflag = TRUE;
s++;
if (*s && chdir(s) < 0) {
@@ -1147,7 +1240,7 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
if (v > 0377)
rb_rs = Qnil;
else if (v == 0 && numlen >= 2) {
- rb_rs = rb_str_new2("\n\n");
+ rb_rs = rb_str_new2("");
}
else {
c = v & 0xff;
@@ -1242,6 +1335,14 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
opt->verbose = 1;
ruby_verbose = Qtrue;
}
+ else if (strncmp("jit", s, 3) == 0) {
+#if USE_MJIT
+ opt->features |= FEATURE_BIT(jit);
+ setup_mjit_options(s + 3, &opt->mjit);
+#else
+ rb_warn("MJIT support is disabled.");
+#endif
+ }
else if (strcmp("yydebug", s) == 0) {
if (envopt) goto noenvopt_long;
opt->dump |= DUMP_BIT(yydebug);
@@ -1328,6 +1429,7 @@ opt_enc_index(VALUE enc_name)
#define rb_progname (GET_VM()->progname)
#define rb_orig_progname (GET_VM()->orig_progname)
VALUE rb_argv0;
+VALUE rb_e_script;
static VALUE
false_value(void)
@@ -1435,8 +1537,9 @@ rb_f_chomp(int argc, VALUE *argv)
static VALUE
process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
{
- NODE *tree = 0;
+ rb_ast_t *ast = 0;
VALUE parser;
+ VALUE script_name;
const rb_iseq_t *iseq;
rb_encoding *enc, *lenc;
#if UTF8_PATH
@@ -1447,15 +1550,20 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
int i = (int)proc_options(argc, argv, opt, 0);
rb_binding_t *toplevel_binding;
const struct rb_block *base_block;
-
- argc -= i;
- argv += i;
+ unsigned int dump = opt->dump & dump_exit_bits;
if (opt->dump & (DUMP_BIT(usage)|DUMP_BIT(help))) {
- usage(origarg.argv[0], (opt->dump & DUMP_BIT(help)));
+ const char *const progname =
+ (argc > 0 && argv && argv[0] ? argv[0] :
+ origarg.argc > 0 && origarg.argv && origarg.argv[0] ? origarg.argv[0] :
+ ruby_engine);
+ usage(progname, (opt->dump & DUMP_BIT(help)));
return Qtrue;
}
+ argc -= i;
+ argv += i;
+
if ((opt->features & FEATURE_BIT(rubyopt)) &&
opt->safe_level == 0 && (s = getenv("RUBYOPT"))) {
VALUE src_enc_name = opt->src.enc.name;
@@ -1475,7 +1583,15 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
if (opt->src.enc.name)
rb_warning("-K is specified; it is for 1.8 compatibility and may cause odd behavior");
+#if USE_MJIT
+ if (opt->features & FEATURE_BIT(jit)) {
+ opt->mjit.on = TRUE; /* set mjit.on for ruby_show_version() API and check to call mjit_init() */
+ }
+#endif
if (opt->dump & (DUMP_BIT(version) | DUMP_BIT(version_v))) {
+#if USE_MJIT
+ mjit_opts.on = opt->mjit.on; /* used by ruby_show_version(). mjit_init() still can't be called here. */
+#endif
ruby_show_version();
if (opt->dump & DUMP_BIT(version)) return Qtrue;
}
@@ -1485,7 +1601,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
}
if (!opt->e_script) {
- if (argc == 0) { /* no more args */
+ if (argc <= 0) { /* no more args */
if (opt->verbose)
return Qtrue;
opt->script = "-";
@@ -1511,6 +1627,9 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
argc--;
argv++;
}
+ if (opt->script[0] == '-' && !opt->script[1]) {
+ forbid_setid("program input from stdin");
+ }
}
opt->script_name = rb_str_new_cstr(opt->script);
@@ -1524,6 +1643,14 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
ruby_gc_set_params(opt->safe_level);
ruby_init_loadpath_safe(opt->safe_level);
+
+#if USE_MJIT
+ if (opt->mjit.on)
+ /* Using TMP_RUBY_PREFIX created by ruby_init_loadpath_safe(). */
+ mjit_init(&opt->mjit);
+#endif
+
+ Init_ruby_description();
Init_enc();
lenc = rb_locale_encoding();
rb_enc_associate(rb_progname, lenc);
@@ -1557,12 +1684,24 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
ienc = enc;
#endif
}
- rb_enc_associate(opt->script_name, lenc);
+ script_name = opt->script_name;
+ rb_enc_associate(opt->script_name,
+ IF_UTF8_PATH(uenc = rb_utf8_encoding(), 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);
+ }
+#endif
rb_obj_freeze(opt->script_name);
- if (IF_UTF8_PATH((uenc = rb_utf8_encoding()) != lenc, 1)) {
+ if (IF_UTF8_PATH(uenc != lenc, 1)) {
long i;
- VALUE load_path = GET_VM()->load_path;
+ rb_vm_t *vm = GET_VM();
+ VALUE load_path = vm->load_path;
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;
@@ -1574,15 +1713,22 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
path = rb_enc_associate(rb_str_dup(path), lenc);
#endif
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);
}
+ if (modifiable) {
+ rb_ary_replace(vm->load_path_snapshot, load_path);
+ }
}
Init_ext(); /* load statically linked extensions before rubygems */
if (opt->features & FEATURE_BIT(gems)) {
rb_define_module("Gem");
- }
- if (opt->features & FEATURE_BIT(did_you_mean)) {
- rb_define_module("DidYouMean");
+ if (opt->features & FEATURE_BIT(did_you_mean)) {
+ rb_define_module("DidYouMean");
+ }
}
ruby_init_prelude();
if ((opt->features ^ DEFAULT_FEATURES) & COMPILATION_FEATURES) {
@@ -1595,12 +1741,6 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
rb_funcallv(rb_cISeq, rb_intern_const("compile_option="), 1, &option);
#undef SET_COMPILE_OPTION
}
-#if UTF8_PATH
- if (uenc != lenc) {
- opt->script_name = str_conv_enc(opt->script_name, uenc, lenc);
- opt->script = RSTRING_PTR(opt->script_name);
- }
-#endif
ruby_set_argv(argc, argv);
process_sflag(&opt->sflag);
@@ -1609,6 +1749,9 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
/* need to acquire env from toplevel_binding each time, since it
* may update after eval() */
+ base_block = toplevel_context(toplevel_binding);
+ rb_parser_set_context(parser, base_block, TRUE);
+
if (opt->e_script) {
VALUE progname = rb_progname;
rb_encoding *eenc;
@@ -1632,22 +1775,20 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
require_libraries(&opt->req_list);
}
ruby_set_script_name(progname);
-
- base_block = toplevel_context(toplevel_binding);
- rb_parser_set_context(parser, base_block, TRUE);
- tree = 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 {
- if (opt->script[0] == '-' && !opt->script[1]) {
- forbid_setid("program input from stdin");
- }
-
- base_block = toplevel_context(toplevel_binding);
- rb_parser_set_context(parser, base_block, TRUE);
- tree = load_file(parser, opt->script_name, 1, opt);
+ VALUE f;
+ f = open_load_file(script_name, &opt->xflag);
+ ast = load_file(parser, opt->script_name, f, 1, opt);
}
ruby_set_script_name(opt->script_name);
- if (opt->dump & DUMP_BIT(yydebug)) return Qtrue;
+ if (dump & DUMP_BIT(yydebug)) {
+ dump &= ~DUMP_BIT(yydebug);
+ if (!dump) return Qtrue;
+ }
if (opt->ext.enc.index >= 0) {
enc = rb_enc_from_index(opt->ext.enc.index);
@@ -1666,54 +1807,85 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
rb_enc_set_default_internal(Qnil);
rb_stdio_set_default_encoding();
- if (!tree) return Qfalse;
+ if (!ast->body.root) {
+ rb_ast_dispose(ast);
+ return Qfalse;
+ }
process_sflag(&opt->sflag);
opt->xflag = 0;
- if (opt->dump & DUMP_BIT(syntax)) {
+ if (dump & DUMP_BIT(syntax)) {
printf("Syntax OK\n");
- return Qtrue;
+ dump &= ~DUMP_BIT(syntax);
+ if (!dump) return Qtrue;
}
- if (opt->do_print) {
- tree = rb_parser_append_print(parser, tree);
- }
if (opt->do_loop) {
- tree = rb_parser_while_loop(parser, tree, opt->do_line, opt->do_split);
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 (opt->dump & DUMP_BIT(parsetree) || opt->dump & DUMP_BIT(parsetree_with_comment)) {
- rb_io_write(rb_stdout, rb_parser_dump_tree(tree, opt->dump & DUMP_BIT(parsetree_with_comment)));
+ 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);
- return Qtrue;
+ 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, opt->script_name, 1);
+ path = rb_realpath_internal(Qnil, script_name, 1);
+#if UTF8_PATH
+ if (uenc != lenc) {
+ path = str_conv_enc(path, uenc, lenc);
+ }
+#endif
+ if (!ENCODING_GET(path)) { /* ASCII-8BIT */
+ rb_enc_copy(path, opt->script_name);
+ }
}
base_block = toplevel_context(toplevel_binding);
- iseq = rb_iseq_new_main(tree, opt->script_name, path, vm_block_iseq(base_block));
+ iseq = rb_iseq_new_main(&ast->body, opt->script_name, path, vm_block_iseq(base_block));
+ rb_ast_dispose(ast);
}
- if (opt->dump & DUMP_BIT(insns)) {
+ if (dump & DUMP_BIT(insns)) {
rb_io_write(rb_stdout, rb_iseq_disasm((const rb_iseq_t *)iseq));
rb_io_flush(rb_stdout);
- return Qtrue;
+ dump &= ~DUMP_BIT(insns);
+ if (!dump) return Qtrue;
}
+ if (opt->dump & dump_exit_bits) return Qtrue;
rb_define_readonly_boolean("$-p", opt->do_print);
rb_define_readonly_boolean("$-l", opt->do_line);
rb_define_readonly_boolean("$-a", opt->do_split);
+ if ((rb_e_script = opt->e_script) != 0) {
+ rb_gc_register_mark_object(opt->e_script);
+ }
+
rb_set_safe_level(opt->safe_level);
+ {
+ rb_execution_context_t *ec = GET_EC();
+
+ if (opt->e_script) {
+ /* -e */
+ rb_exec_event_hook_script_compiled(ec, iseq, opt->e_script);
+ }
+ else {
+ /* file */
+ rb_exec_event_hook_script_compiled(ec, iseq, Qnil);
+ }
+ }
return (VALUE)iseq;
}
@@ -1722,7 +1894,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 ends with \\r may cause a problem");
+ rb_warn("shebang line ending with \\r may cause problems");
}
}
#else
@@ -1733,7 +1905,6 @@ struct load_file_arg {
VALUE parser;
VALUE fname;
int script;
- int xflag;
ruby_cmdline_options_t *opt;
VALUE f;
};
@@ -1748,12 +1919,10 @@ load_file_internal(VALUE argp_v)
ruby_cmdline_options_t *opt = argp->opt;
VALUE f = argp->f;
int line_start = 1;
- NODE *tree = 0;
+ rb_ast_t *ast = 0;
rb_encoding *enc;
ID set_encoding;
- int xflag = argp->xflag;
- argp->script = 0;
CONST_ID(set_encoding, "set_encoding");
if (script) {
VALUE c = 1; /* something not nil */
@@ -1767,11 +1936,9 @@ load_file_internal(VALUE argp_v)
enc = rb_ascii8bit_encoding();
rb_funcall(f, set_encoding, 1, rb_enc_from_encoding(enc));
- if (xflag || opt->xflag) {
+ if (opt->xflag) {
line_start--;
search_shebang:
- forbid_setid("-x");
- opt->xflag = FALSE;
while (!NIL_P(line = rb_io_gets(f))) {
line_start++;
RSTRING_GETMEM(line, str, len);
@@ -1788,11 +1955,7 @@ load_file_internal(VALUE argp_v)
c = rb_io_getbyte(f);
if (c == INT2FIX('#')) {
c = rb_io_getbyte(f);
- if (c == INT2FIX('!')) {
- line = rb_io_gets(f);
- if (NIL_P(line))
- return 0;
-
+ 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) {
@@ -1832,8 +1995,7 @@ load_file_internal(VALUE argp_v)
rb_io_ungetbyte(f, c);
}
else {
- if (f != rb_stdin) rb_io_close(f);
- f = Qnil;
+ argp->f = f = Qnil;
}
if (!(opt->dump & ~DUMP_BIT(version_v))) {
ruby_set_script_name(opt->script_name);
@@ -1849,22 +2011,40 @@ load_file_internal(VALUE argp_v)
else {
enc = rb_utf8_encoding();
}
+ rb_parser_set_options(parser, opt->do_print, opt->do_loop,
+ 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);
}
rb_funcall(f, set_encoding, 2, rb_enc_from_encoding(enc), rb_str_new_cstr("-"));
- tree = rb_parser_compile_file_path(parser, orig_fname, f, line_start);
+ 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)) argp->script = script;
- return (VALUE)tree;
+ 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;
+ }
+ return (VALUE)ast;
}
static VALUE
open_load_file(VALUE fname_v, int *xflag)
{
- const char *fname = StringValueCStr(fname_v);
+ const char *fname = (fname_v = rb_str_encode_ospath(fname_v),
+ StringValueCStr(fname_v));
long flen = RSTRING_LEN(fname_v);
VALUE f;
int e;
@@ -1899,7 +2079,7 @@ open_load_file(VALUE fname_v, int *xflag)
#endif
if ((fd = rb_cloexec_open(fname, mode, 0)) < 0) {
- int e = errno;
+ e = errno;
if (!rb_gc_for_fd(e)) {
rb_load_fail(fname_v, strerror(e));
}
@@ -1943,39 +2123,23 @@ restore_load_file(VALUE arg)
struct load_file_arg *argp = (struct load_file_arg *)arg;
VALUE f = argp->f;
- if (argp->script) {
- /*
- * 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);
- }
- else if (f != rb_stdin) {
+ if (!NIL_P(f) && f != rb_stdin) {
rb_io_close(f);
}
return Qnil;
}
-static NODE *
-load_file(VALUE parser, VALUE fname, int script, ruby_cmdline_options_t *opt)
+static rb_ast_t *
+load_file(VALUE parser, VALUE fname, VALUE f, int script, ruby_cmdline_options_t *opt)
{
struct load_file_arg arg;
arg.parser = parser;
arg.fname = fname;
arg.script = script;
arg.opt = opt;
- arg.xflag = 0;
- arg.f = open_load_file(rb_str_encode_ospath(fname), &arg.xflag);
- return (NODE *)rb_ensure(load_file_internal, (VALUE)&arg,
- restore_load_file, (VALUE)&arg);
+ arg.f = f;
+ return (rb_ast_t *)rb_ensure(load_file_internal, (VALUE)&arg,
+ restore_load_file, (VALUE)&arg);
}
void *
@@ -1988,17 +2152,15 @@ rb_load_file(const char *fname)
void *
rb_load_file_str(VALUE fname_v)
{
- ruby_cmdline_options_t opt;
-
- return load_file(rb_parser_new(), fname_v, 0, cmdline_options_init(&opt));
+ return rb_parser_load_file(rb_parser_new(), fname_v);
}
void *
rb_parser_load_file(VALUE parser, VALUE fname_v)
{
ruby_cmdline_options_t opt;
-
- return load_file(parser, fname_v, 0, cmdline_options_init(&opt));
+ VALUE f = open_load_file(fname_v, &cmdline_options_init(&opt)->xflag);
+ return load_file(parser, fname_v, f, 0, &opt);
}
/*
@@ -2018,6 +2180,8 @@ proc_argv0(VALUE process)
return rb_orig_progname;
}
+static VALUE ruby_setproctitle(VALUE title);
+
/*
* call-seq:
* Process.setproctitle(string) -> string
@@ -2038,10 +2202,14 @@ proc_argv0(VALUE process)
static VALUE
proc_setproctitle(VALUE process, VALUE title)
{
- StringValue(title);
-
- setproctitle("%.*s", RSTRING_LENINT(title), RSTRING_PTR(title));
+ return ruby_setproctitle(title);
+}
+static VALUE
+ruby_setproctitle(VALUE title)
+{
+ const char *ptr = StringValueCStr(title);
+ setproctitle("%.*s", RSTRING_LENINT(title), ptr);
return title;
}
@@ -2051,7 +2219,7 @@ set_arg0(VALUE val, ID id)
if (origarg.argv == 0)
rb_raise(rb_eRuntimeError, "$0 not initialized");
- rb_progname = rb_str_new_frozen(proc_setproctitle(rb_mProcess, val));
+ rb_progname = rb_str_new_frozen(ruby_setproctitle(val));
}
static inline VALUE
@@ -2059,7 +2227,9 @@ external_str_new_cstr(const char *p)
{
#if UTF8_PATH
VALUE str = rb_utf8_str_new_cstr(p);
- return str_conv_enc(str, NULL, rb_default_external_encoding());
+ str = str_conv_enc(str, NULL, rb_default_external_encoding());
+ OBJ_TAINT_RAW(str);
+ return str;
#else
return rb_external_str_new_cstr(p);
#endif
@@ -2107,7 +2277,7 @@ init_ids(ruby_cmdline_options_t *opt)
#undef forbid_setid
static void
-forbid_setid(const char *s, ruby_cmdline_options_t *opt)
+forbid_setid(const char *s, const ruby_cmdline_options_t *opt)
{
if (opt->setids & 1)
rb_raise(rb_eSecurityError, "no %s allowed while running setuid", s);
@@ -2173,9 +2343,9 @@ ruby_set_argv(int argc, char **argv)
VALUE av = rb_argv;
#if defined(USE_DLN_A_OUT)
- if (origarg.argv)
+ if (origarg.argc > 0 && origarg.argv)
dln_argv0 = origarg.argv[0];
- else
+ else if (argc > 0 && argv)
dln_argv0 = argv[0];
#endif
rb_ary_clear(av);
@@ -2194,6 +2364,10 @@ ruby_process_options(int argc, char **argv)
VALUE iseq;
const char *script_name = (argc > 0 && argv[0]) ? argv[0] : ruby_engine;
+ if (!origarg.argv || origarg.argc <= 0) {
+ origarg.argc = argc;
+ origarg.argv = argv;
+ }
ruby_script(script_name); /* for the time being */
rb_argv0 = rb_str_new4(rb_progname);
rb_gc_register_mark_object(rb_argv0);
@@ -2236,23 +2410,25 @@ fill_standard_fds(void)
}
}
-/*! Initializes the process for ruby(1).
+/*! Initializes the process for libruby.
*
* This function assumes this process is ruby(1) and it has just started.
- * Usually programs that embeds CRuby interpreter should not call this function,
- * and should do their own initialization.
+ * Usually programs that embed CRuby interpreter may not call this function,
+ * and may do their own initialization.
+ * argc and argv cannot be NULL.
*/
void
ruby_sysinit(int *argc, char ***argv)
{
#if defined(_WIN32)
- void rb_w32_sysinit(int *argc, char ***argv);
rb_w32_sysinit(argc, argv);
#endif
- origarg.argc = *argc;
- origarg.argv = *argv;
+ if (*argc >= 0 && *argv) {
+ origarg.argc = *argc;
+ origarg.argv = *argv;
#if defined(USE_DLN_A_OUT)
- dln_argv0 = origarg.argv[0];
+ dln_argv0 = origarg.argv[0];
#endif
+ }
fill_standard_fds();
}
diff --git a/ruby_assert.h b/ruby_assert.h
index 3383e4fc6c..185d2e5f5f 100644
--- a/ruby_assert.h
+++ b/ruby_assert.h
@@ -33,8 +33,14 @@ NORETURN(void rb_assert_failure(const char *, int, const char *, const char *));
#define RUBY_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(!RUBY_NDEBUG+0, expr, #expr)
#define RUBY_ASSERT_WHEN(cond, expr) RUBY_ASSERT_MESG_WHEN(cond, expr, #expr)
+#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
+/* C89 compilers are required to support strings of only 509 chars. */
+/* can't use RUBY_ASSERT for such compilers. */
+#include <assert.h>
+#else
#undef assert
#define assert RUBY_ASSERT
+#endif
#ifndef RUBY_NDEBUG
# ifdef NDEBUG
diff --git a/ruby_atomic.h b/ruby_atomic.h
index 4bc9f37e0d..1b395cd23f 100644
--- a/ruby_atomic.h
+++ b/ruby_atomic.h
@@ -9,10 +9,10 @@ typedef unsigned int rb_atomic_t;
# define ATOMIC_DEC(var) __atomic_fetch_sub(&(var), 1, __ATOMIC_SEQ_CST)
# define ATOMIC_OR(var, val) __atomic_fetch_or(&(var), (val), __ATOMIC_SEQ_CST)
# define ATOMIC_EXCHANGE(var, val) __atomic_exchange_n(&(var), (val), __ATOMIC_SEQ_CST)
-# define ATOMIC_CAS(var, oldval, newval) \
-({ __typeof__(var) oldvaldup = (oldval); /* oldval should not be modified */ \
+# define ATOMIC_CAS(var, oldval, newval) RB_GNUC_EXTENSION_BLOCK( \
+ __typeof__(var) oldvaldup = (oldval); /* oldval should not be modified */ \
__atomic_compare_exchange_n(&(var), &oldvaldup, (newval), 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \
- oldvaldup; })
+ oldvaldup )
# define ATOMIC_SIZE_ADD(var, val) __atomic_fetch_add(&(var), (val), __ATOMIC_SEQ_CST)
# define ATOMIC_SIZE_SUB(var, val) __atomic_fetch_sub(&(var), (val), __ATOMIC_SEQ_CST)
@@ -90,6 +90,10 @@ rb_w32_atomic_cas(volatile rb_atomic_t *var, rb_atomic_t oldval, rb_atomic_t new
# define ATOMIC_SIZE_EXCHANGE(var, val) InterlockedExchange((LONG *)&(var), (val))
# endif
+# ifdef InterlockedExchangePointer
+# define ATOMIC_PTR_EXCHANGE(var, val) InterlockedExchangePointer((PVOID volatile *)&(var), (PVOID)(val))
+# endif /* See below for definitions of other situations */
+
#elif defined(__sun) && defined(HAVE_ATOMIC_H)
#include <atomic.h>
typedef unsigned int rb_atomic_t;
@@ -146,12 +150,15 @@ ruby_atomic_size_exchange(size_t *ptr, size_t val)
#ifndef ATOMIC_SIZE_INC
# define ATOMIC_SIZE_INC(var) ATOMIC_INC(var)
#endif
+
#ifndef ATOMIC_SIZE_DEC
# define ATOMIC_SIZE_DEC(var) ATOMIC_DEC(var)
#endif
+
#ifndef ATOMIC_SIZE_EXCHANGE
# define ATOMIC_SIZE_EXCHANGE(var, val) ATOMIC_EXCHANGE(var, val)
#endif
+
#ifndef ATOMIC_SIZE_CAS
# define ATOMIC_SIZE_CAS(var, oldval, val) ATOMIC_CAS(var, oldval, val)
#endif
@@ -160,6 +167,7 @@ ruby_atomic_size_exchange(size_t *ptr, size_t val)
# ifndef ATOMIC_PTR_EXCHANGE
# define ATOMIC_PTR_EXCHANGE(var, val) ATOMIC_EXCHANGE(var, val)
# endif
+
# ifndef ATOMIC_PTR_CAS
# define ATOMIC_PTR_CAS(var, oldval, newval) ATOMIC_CAS(var, oldval, newval)
# endif
@@ -167,6 +175,7 @@ ruby_atomic_size_exchange(size_t *ptr, size_t val)
# ifndef ATOMIC_VALUE_EXCHANGE
# define ATOMIC_VALUE_EXCHANGE(var, val) ATOMIC_EXCHANGE(var, val)
# endif
+
# ifndef ATOMIC_VALUE_CAS
# define ATOMIC_VALUE_CAS(var, oldval, val) ATOMIC_CAS(var, oldval, val)
# endif
@@ -186,6 +195,7 @@ ruby_atomic_ptr_exchange(const void **ptr, const void *val)
}
# endif
#endif
+
#ifndef ATOMIC_PTR_CAS
# if SIZEOF_VOIDP == SIZEOF_SIZE_T
# define ATOMIC_PTR_CAS(var, oldval, val) (void *)ATOMIC_SIZE_CAS(*(size_t *)&(var), (size_t)(oldval), (size_t)(val))
@@ -215,6 +225,7 @@ ruby_atomic_value_exchange(VALUE *ptr, VALUE val)
}
# endif
#endif
+
#ifndef ATOMIC_VALUE_CAS
# if SIZEOF_VALUE == SIZEOF_SIZE_T
# define ATOMIC_VALUE_CAS(var, oldval, val) ATOMIC_SIZE_CAS(*(size_t *)&(var), (size_t)(oldval), (size_t)(val))
diff --git a/safe.c b/safe.c
index 7aae978272..68ec59689f 100644
--- a/safe.c
+++ b/safe.c
@@ -34,25 +34,34 @@ ruby_safe_level_2_warning(void)
int
rb_safe_level(void)
{
- return GET_THREAD()->safe_level;
+ return GET_VM()->safe_level_;
}
void
rb_set_safe_level_force(int safe)
{
- GET_THREAD()->safe_level = safe;
+ GET_VM()->safe_level_ = safe;
}
void
rb_set_safe_level(int level)
{
- rb_thread_t *th = GET_THREAD();
+ rb_vm_t *vm = GET_VM();
- if (level > th->safe_level) {
- if (level > SAFE_LEVEL_MAX) {
- rb_raise(rb_eArgError, "$SAFE=2 to 4 are obsolete");
- }
- th->safe_level = level;
+ if (level > SAFE_LEVEL_MAX) {
+ rb_raise(rb_eArgError, "$SAFE=2 to 4 are obsolete");
+ }
+ else if (level < 0) {
+ rb_raise(rb_eArgError, "$SAFE should be >= 0");
+ }
+ else {
+ int line;
+ const char *path = rb_source_location_cstr(&line);
+
+ if (0) fprintf(stderr, "%s:%d $SAFE %d -> %d\n",
+ path ? path : "-", line, vm->safe_level_, level);
+
+ vm->safe_level_ = level;
}
}
@@ -66,17 +75,7 @@ static void
safe_setter(VALUE val)
{
int level = NUM2INT(val);
- rb_thread_t *th = GET_THREAD();
-
- if (level < th->safe_level) {
- rb_raise(rb_eSecurityError,
- "tried to downgrade safe level from %d to %d",
- th->safe_level, level);
- }
- if (level > SAFE_LEVEL_MAX) {
- rb_raise(rb_eArgError, "$SAFE=2 to 4 are obsolete");
- }
- th->safe_level = level;
+ rb_set_safe_level(level);
}
void
diff --git a/sample/cbreak.rb b/sample/cbreak.rb
index 76b534a76a..7f1385cce3 100644
--- a/sample/cbreak.rb
+++ b/sample/cbreak.rb
@@ -5,15 +5,15 @@ ECHO = 0x00000008
TIOCGETP = 0x40067408
TIOCSETP = 0x80067409
-def cbreak ()
+def cbreak
set_cbreak(true)
end
-def cooked ()
+def cooked
set_cbreak(false)
end
-def set_cbreak (on)
+def set_cbreak(on)
tty = "\0" * 256
STDIN.ioctl(TIOCGETP, tty)
ttys = tty.unpack("C4 S")
@@ -30,7 +30,7 @@ end
cbreak();
print("this is no-echo line: ");
-readline().print
+readline().display
cooked();
print("this is echo line: ");
readline()
diff --git a/sample/dir.rb b/sample/dir.rb
index b627383946..44733c2cf4 100644
--- a/sample/dir.rb
+++ b/sample/dir.rb
@@ -3,7 +3,7 @@
dirp = Dir.open(".")
for f in dirp
case f
- when /^\./, /~$/, /\.o/
+ when /\A\./, /~\z/, /\.o/
# do not print
else
print f, "\n"
diff --git a/sample/drb/dchats.rb b/sample/drb/dchats.rb
index c07f748e99..58af3cf005 100644
--- a/sample/drb/dchats.rb
+++ b/sample/drb/dchats.rb
@@ -2,7 +2,6 @@
distributed Ruby --- chat server
Copyright (c) 1999-2000 Masatoshi SEKI
=end
-require 'thread'
require 'drb/drb'
class ChatEntry
diff --git a/sample/drb/dhasen.rb b/sample/drb/dhasen.rb
index 9ab8534588..13ff38940e 100644
--- a/sample/drb/dhasen.rb
+++ b/sample/drb/dhasen.rb
@@ -17,7 +17,6 @@
require 'drb/drb'
require 'chasen'
-require 'thread'
class Dhasen
include DRbUndumped
diff --git a/sample/drb/dlogd.rb b/sample/drb/dlogd.rb
index be364511dc..a87e660346 100644
--- a/sample/drb/dlogd.rb
+++ b/sample/drb/dlogd.rb
@@ -4,7 +4,6 @@
=end
require 'drb/drb'
-require 'thread'
class Logger
def initialize(fname)
diff --git a/sample/drb/dqueue.rb b/sample/drb/dqueue.rb
index 1a405f5be5..a9afa8c858 100644
--- a/sample/drb/dqueue.rb
+++ b/sample/drb/dqueue.rb
@@ -3,7 +3,6 @@
Copyright (c) 1999-2000 Masatoshi SEKI
=end
-require 'thread'
require 'drb/drb'
DRb.start_service(nil, Thread::Queue.new)
diff --git a/sample/drb/http0serv.rb b/sample/drb/http0serv.rb
index 049f5a1de5..1a58811fed 100644
--- a/sample/drb/http0serv.rb
+++ b/sample/drb/http0serv.rb
@@ -1,7 +1,6 @@
require 'webrick'
require 'drb/drb'
require 'drb/http0'
-require 'thread'
module DRb
module HTTP0
diff --git a/sample/drb/name.rb b/sample/drb/name.rb
index 30c902b8f7..a8ad28749d 100644
--- a/sample/drb/name.rb
+++ b/sample/drb/name.rb
@@ -35,7 +35,6 @@ How to play.
| 2
=end
-require 'thread.rb'
require 'drb/drb'
module DRbNamedObject
diff --git a/sample/drb/old_tuplespace.rb b/sample/drb/old_tuplespace.rb
index 9c10a34527..8be1542c06 100644
--- a/sample/drb/old_tuplespace.rb
+++ b/sample/drb/old_tuplespace.rb
@@ -3,8 +3,6 @@
# Copyright (c) 1999-2000 Masatoshi SEKI
# You can redistribute it and/or modify it under the same terms as Ruby.
-require 'thread'
-
class TupleSpace
class Template
def initialize(list)
diff --git a/sample/drb/ring_echo.rb b/sample/drb/ring_echo.rb
index 3b743cab8e..c54628b54c 100644
--- a/sample/drb/ring_echo.rb
+++ b/sample/drb/ring_echo.rb
@@ -1,7 +1,6 @@
require 'drb/drb'
require 'drb/eq'
require 'rinda/ring'
-require 'thread'
class RingEcho
include DRbUndumped
diff --git a/sample/drb/simpletuple.rb b/sample/drb/simpletuple.rb
index bfbd86e665..4bb4b1cff9 100644
--- a/sample/drb/simpletuple.rb
+++ b/sample/drb/simpletuple.rb
@@ -3,8 +3,6 @@
# Copyright (c) 1999-2000 Masatoshi SEKI
# You can redistribute it and/or modify it under the same terms as Ruby.
-require 'thread'
-
class SimpleTupleSpace
def initialize
@hash = {}
diff --git a/sample/dualstack-httpd.rb b/sample/dualstack-httpd.rb
index a6d4d3a2c2..ab02e17aea 100644
--- a/sample/dualstack-httpd.rb
+++ b/sample/dualstack-httpd.rb
@@ -3,7 +3,6 @@
# The code demonstrates how a multi-protocol daemon should be written.
require "socket"
-require "thread"
port = 8888
res = Socket.getaddrinfo(nil, port, nil, Socket::SOCK_STREAM, nil, Socket::AI_PASSIVE)
diff --git a/sample/fib.py b/sample/fib.py
index 8318021d24..90dc1e09ed 100644
--- a/sample/fib.py
+++ b/sample/fib.py
@@ -6,5 +6,5 @@ def fib(n):
else:
return fib(n-2)+fib(n-1)
-print fib(20)
+print(fib(20))
diff --git a/sample/iseq_loader.rb b/sample/iseq_loader.rb
index bb2d92ea77..8c271405d6 100644
--- a/sample/iseq_loader.rb
+++ b/sample/iseq_loader.rb
@@ -41,7 +41,7 @@ class RubyVM::InstructionSequence
at_exit{
STDERR.puts "[ISEQ_LOADER] #{Process.pid} time: #{Time.now - LAUNCHED_TIME}, " +
"loaded: #{$ISEQ_LOADER_LOADED}, " +
- "compied: #{$ISEQ_LOADER_COMPILED}, " +
+ "compiled: #{$ISEQ_LOADER_COMPILED}, " +
"ignored: #{$ISEQ_LOADER_IGNORED}"
} if COMPILE_VERBOSE
@@ -141,11 +141,11 @@ class RubyVM::InstructionSequence
end
def read_compiled_iseq fname, iseq_key
- open(iseq_key, 'rb'){|f| f.read}
+ File.open(iseq_key, 'rb'){|f| f.read}
end
def write_compiled_iseq fname, iseq_key, binary
- open(iseq_key, 'wb'){|f| f.write(binary)}
+ File.open(iseq_key, 'wb'){|f| f.write(binary)}
end
end
diff --git a/sample/observ.rb b/sample/observ.rb
index 061e3c6a10..a7ea45271d 100644
--- a/sample/observ.rb
+++ b/sample/observ.rb
@@ -1,6 +1,5 @@
#! /usr/local/bin/ruby
-require "thread"
require "observer"
class Tick
diff --git a/sample/philos.rb b/sample/philos.rb
index 622e58b4bf..c38aa4a1cc 100644
--- a/sample/philos.rb
+++ b/sample/philos.rb
@@ -1,7 +1,6 @@
#
# The Dining Philosophers - thread example
#
-require "thread"
srand
#srand
diff --git a/sample/pty/expect_sample.rb b/sample/pty/expect_sample.rb
index 2f87f21895..199d98b79c 100644
--- a/sample/pty/expect_sample.rb
+++ b/sample/pty/expect_sample.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
#
# sample program of expect.rb
#
@@ -23,8 +24,17 @@ PTY.spawn("ftp ftp.ruby-lang.org") do |r_f,w_f,pid|
username = 'guest'
end
- r_f.expect(/^(Name).*: |(word):|> /) do
- w_f.puts($1 ? "ftp" : $2 ? "#{username}@" : "cd pub/ruby")
+ r_f.expect(/^Name.*: /) do
+ w_f.puts("ftp")
+ end
+ r_f.expect(/word:/) do
+ w_f.puts("#{username}@")
+ end
+ r_f.expect(/> /) do
+ w_f.puts("cd pub/ruby")
+ end
+ r_f.expect("> ") do
+ w_f.print "pass\n"
end
r_f.expect("> ") do
w_f.print "dir\n"
diff --git a/sample/pty/script.rb b/sample/pty/script.rb
index 903a6f75bd..c6659a4807 100644
--- a/sample/pty/script.rb
+++ b/sample/pty/script.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
require 'pty'
if ARGV.size == 0 then
@@ -34,4 +35,3 @@ PTY.spawn("/bin/csh") do |r_pty,w_pty,pid|
end
system "stty echo -raw lnext ^v"
-
diff --git a/sample/pty/shl.rb b/sample/pty/shl.rb
index cdaf8d7398..980748e8f5 100644
--- a/sample/pty/shl.rb
+++ b/sample/pty/shl.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
#
# old-fashioned 'shl' like program
# by A. Ito
@@ -10,39 +11,40 @@
# q quit
require 'pty'
+require 'io/console'
$shells = []
-$n_shells = 0
$r_pty = nil
$w_pty = nil
def writer
- system "stty -echo raw"
+ STDIN.raw!
begin
while true
c = STDIN.getc
- if c == 26 then # C-z
- $reader.raise(nil)
+ if c == ?\C-z then
+ $reader.raise('Suspend')
return 'Suspend'
end
$w_pty.print c.chr
$w_pty.flush
end
rescue
- $reader.raise(nil)
+ $reader.raise('Exit')
return 'Exit'
ensure
- system "stty echo -raw"
+ STDIN.cooked!
end
end
$reader = Thread.new {
while true
begin
- next if $r_pty.nil?
+ Thread.stop unless $r_pty
c = $r_pty.getc
if c.nil? then
+ Thread.main.raise('Exit')
Thread.stop
end
print c.chr
@@ -59,19 +61,14 @@ $reader = Thread.new {
while true
print ">> "
STDOUT.flush
+ n = nil
case gets
when /^c/i
- $shells[$n_shells] = PTY.spawn("/bin/csh")
- $r_pty,$w_pty = $shells[$n_shells]
- $n_shells += 1
- $reader.run
- if writer == 'Exit'
- $n_shells -= 1
- $shells[$n_shells] = nil
- end
+ $shells << PTY.spawn("/bin/csh")
+ n = -1
when /^p/i
- for i in 0..$n_shells
- unless $shells[i].nil?
+ $shells.each_with_index do |s, i|
+ if s
print i,"\n"
end
end
@@ -79,14 +76,18 @@ while true
n = $1.to_i
if $shells[n].nil?
print "\##{i} doesn't exist\n"
- else
- $r_pty,$w_pty = $shells[n]
- $reader.run
- if writer == 'Exit' then
- $shells[n] = nil
- end
+ n = nil
end
when /^q/i
exit
end
+ if n
+ $r_pty, $w_pty, pid = $shells[n]
+ $reader.run
+ if writer == 'Exit' then
+ Process.wait(pid)
+ $shells[n] = nil
+ $shells.pop until $shells.empty? or $shells[-1]
+ end
+ end
end
diff --git a/sample/ripper/ruby2html.rb b/sample/ripper/ruby2html.rb
index 8f64f5a713..1e6b3bf550 100644
--- a/sample/ripper/ruby2html.rb
+++ b/sample/ripper/ruby2html.rb
@@ -73,7 +73,11 @@ class ERB
end
def ruby2html(f, encoding, css, print_line_number)
- erb = ERB.new(TEMPLATE, nil, '>')
+ if RUBY_VERSION >= '2.6'
+ erb = ERB.new(TEMPLATE, trim_mode: '>')
+ else
+ erb = ERB.new(TEMPLATE, nil, '>')
+ end
erb.filename = __FILE__
erb.lineno = TEMPLATE_LINE
erb.result(binding())
diff --git a/sample/timeout.rb b/sample/timeout.rb
index 8d25d72a76..ad4459aff0 100644
--- a/sample/timeout.rb
+++ b/sample/timeout.rb
@@ -1,31 +1,31 @@
require 'timeout'
def progress(n = 5)
- n.times {|i| print i; STDOUT.flush; sleep 1; i+= 1}
+ n.times {|i| print i; STDOUT.flush; sleep 1}
puts "never reach"
end
-p timeout(5) {
+p Timeout.timeout(5) {
45
}
-p timeout(5, Timeout::Error) {
+p Timeout.timeout(5, Timeout::Error) {
45
}
-p timeout(nil) {
+p Timeout.timeout(nil) {
54
}
-p timeout(0) {
+p Timeout.timeout(0) {
54
}
begin
- timeout(5) {progress}
+ Timeout.timeout(5) {progress}
rescue => e
puts e.message
end
begin
- timeout(3) {
+ Timeout.timeout(3) {
begin
- timeout(5) {progress}
+ Timeout.timeout(5) {progress}
rescue => e
puts "never reach"
end
@@ -36,7 +36,7 @@ end
class MyTimeout < StandardError
end
begin
- timeout(2, MyTimeout) {progress}
+ Timeout.timeout(2, MyTimeout) {progress}
rescue MyTimeout => e
puts e.message
end
diff --git a/sample/trick2013/kinaba/remarks.markdown b/sample/trick2013/kinaba/remarks.markdown
index a454a5f0a1..73a4ea9875 100644
--- a/sample/trick2013/kinaba/remarks.markdown
+++ b/sample/trick2013/kinaba/remarks.markdown
@@ -16,7 +16,7 @@ The program contains each ASCII character from 0x20 ' ' to 0x7e '~' exactly once
### Internals
-The algorthim is the obvious loop "32.upto(126){|x| putc x}".
+The algorithm is the obvious loop "32.upto(126){|x| putc x}".
It is not so hard to transform it to use each character *at most once*. The only slight difficulty comes from the constraint that we cannot "declare and then use" variables, because then the code will contain the variable name twice. This restriction is worked around by the $. global variable, the best friend of Ruby golfers.
diff --git a/sample/trick2013/mame/music-box.mp4 b/sample/trick2013/mame/music-box.mp4
deleted file mode 100644
index 6d1e87c01c..0000000000
--- a/sample/trick2013/mame/music-box.mp4
+++ /dev/null
Binary files differ
diff --git a/sample/trick2013/yhara/entry.rb b/sample/trick2013/yhara/entry.rb
index a2deb54399..ce125ed3df 100644
--- a/sample/trick2013/yhara/entry.rb
+++ b/sample/trick2013/yhara/entry.rb
@@ -2,7 +2,7 @@ def _(&b)$><<->(x){x ? (String===x ?x.upcase:
(Class===x ? x : x.class).name[$a?0:($a=5)]):
" "}[ begin b[];rescue Exception;$!;end ] end
-_ { return }
+_ { yield }
_ { method(:p).unbind }
_ { eval "{ " }
_ { Thread.current.join }
diff --git a/sample/trick2015/ksk_1/remarks.markdown b/sample/trick2015/ksk_1/remarks.markdown
index b822dc55c8..a0b8bbcdcc 100644
--- a/sample/trick2015/ksk_1/remarks.markdown
+++ b/sample/trick2015/ksk_1/remarks.markdown
@@ -110,7 +110,7 @@ is simply `/=/` and removing a padding `",,,,,"`. The program no
longer terminates, though.
-### Limination
+### Limitation
The implementation requires to manipulate long strings even for some
small starting numbers. For example, starting from 1,819, the number
diff --git a/sample/trick2018/01-kinaba/authors.markdown b/sample/trick2018/01-kinaba/authors.markdown
new file mode 100644
index 0000000000..d0df0b379d
--- /dev/null
+++ b/sample/trick2018/01-kinaba/authors.markdown
@@ -0,0 +1,3 @@
+* kinaba
+ * twitter.com/kinaba
+ * cctld: jp
diff --git a/sample/trick2018/01-kinaba/entry.rb b/sample/trick2018/01-kinaba/entry.rb
new file mode 100644
index 0000000000..eb8284d5ab
--- /dev/null
+++ b/sample/trick2018/01-kinaba/entry.rb
@@ -0,0 +1,8 @@
+alias BEGIN for unless def class
+super true or return defined? next
+break while begin undef do end
+rescue then retry else undef module
+nil ensure case if yield __LINE__
+self and redo elsif not __FILE__
+alias END in end when __ENCODING__
+end until false end
diff --git a/sample/trick2018/01-kinaba/remarks.markdown b/sample/trick2018/01-kinaba/remarks.markdown
new file mode 100644
index 0000000000..a1a05bfd73
--- /dev/null
+++ b/sample/trick2018/01-kinaba/remarks.markdown
@@ -0,0 +1,55 @@
+### Remarks
+
+Just run it with no argument:
+
+ ruby entry.rb
+
+(Anyway it is just a no-op program. The above command only verifies
+that entry.rb is a valid Ruby program.)
+
+I confirmed the following implementations/platforms:
+
+* ruby 2.5.0p0 (2017-12-25 revision 61468) [x64-mingw32]
+
+### Description
+
+First, look at
+
+https://docs.ruby-lang.org/ja/latest/doc/spec=2flexical.html#reserved
+
+and then, look at entry.rb.
+
+The source code of entry.rb consists only of reserved words of Ruby,
+and all the reserved words are used in the code, in a way that the code
+forms a valid Ruby program. No compile error, no warning, or no runtime error.
+
+
+### Internals
+
+Difficult (and interesting) points of the theme are:
+
+* Since many of the reserved words define program structures, we cannot
+ use them independently. For instance, `retry` must be inside `rescue`,
+ or `break`/`next`/`redo` must be inside a looping construct.
+ Or, jump-out statements cannot occur at a position that requires a
+ value; `if return then true end` is a "void value expression" syntax error.
+* Inserting newlines for each 6 word (to match with the spec html) is also
+ an interseting challenge, since Ruby is sensitive to newlines.
+
+Tricks used in the code are:
+
+* def/alias/undef can take even reserved words as parameters.
+ That is, `def class ... end` defines a method named `class`.
+ The feature is crucial since otherwise `BEGIN` etc inevitably
+ introduces non-reserved tokens (like `{}`).
+* `defined?` can take some reserved words too (which I didn't know
+ until trying to write this program.)
+* "void value expression" can be avoided by using `or` or `and`.
+ `if begin return end then true end` is a syntax error, but
+ `if begin false or return end then true end` is not.
+
+
+### Limitation
+
+Sad to say that it's not a "perfect pangram".
+It uses 'alias' and 'undef' twice, and 'end' 4 times.
diff --git a/sample/trick2018/02-mame/authors.markdown b/sample/trick2018/02-mame/authors.markdown
new file mode 100644
index 0000000000..0e420fdf5d
--- /dev/null
+++ b/sample/trick2018/02-mame/authors.markdown
@@ -0,0 +1,3 @@
+* Yusuke Endoh
+ * mame@ruby-lang.org
+ * cctld: jp
diff --git a/sample/trick2018/02-mame/entry.rb b/sample/trick2018/02-mame/entry.rb
new file mode 100644
index 0000000000..cc4ef9cbc4
--- /dev/null
+++ b/sample/trick2018/02-mame/entry.rb
@@ -0,0 +1,15 @@
+'';eval(r=%q(->z{r="'';eval(r=\
+%q(#{r}))[%q`#{z}`]";i=-040;30.
+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
+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
+!Pss.slice!(0,1)+x;sleep(0.0t;0
+'W=%q"<<95<<$s<<95;o=->n,x{n.'1
+;@[2]}|\e../,%@s="'%trick2018!8
+eval$s=%q_eval($s.gsub!(/#{%@`]
diff --git a/sample/trick2018/02-mame/remarks.markdown b/sample/trick2018/02-mame/remarks.markdown
new file mode 100644
index 0000000000..88b32c205a
--- /dev/null
+++ b/sample/trick2018/02-mame/remarks.markdown
@@ -0,0 +1,16 @@
+This program quines with animation.
+
+```
+$ ruby entry.rb
+```
+
+Of course, the output is executable.
+
+```
+$ ruby entry.rb > output
+$ ruby output
+```
+
+Note, we don't cheat. This program uses escape sequences just for moving the cursor. It doesn't use attribution change nor overwrite to hide any code.
+
+The program is crafted so that it works in two ways; it works as a normal program text, and, it also works when it is rearranged in a spiral order. Some parts of the code are actually overlapped.
diff --git a/sample/trick2018/03-tompng/Gemfile b/sample/trick2018/03-tompng/Gemfile
new file mode 100644
index 0000000000..a24ff779dc
--- /dev/null
+++ b/sample/trick2018/03-tompng/Gemfile
@@ -0,0 +1,2 @@
+source 'https://rubygems.org'
+gem 'chunky_png'
diff --git a/sample/trick2018/03-tompng/Gemfile.lock b/sample/trick2018/03-tompng/Gemfile.lock
new file mode 100644
index 0000000000..467f5c3495
--- /dev/null
+++ b/sample/trick2018/03-tompng/Gemfile.lock
@@ -0,0 +1,13 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ chunky_png (1.3.8)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ chunky_png
+
+BUNDLED WITH
+ 1.16.1
diff --git a/sample/trick2018/03-tompng/authors.markdown b/sample/trick2018/03-tompng/authors.markdown
new file mode 100644
index 0000000000..26ebe24da6
--- /dev/null
+++ b/sample/trick2018/03-tompng/authors.markdown
@@ -0,0 +1,3 @@
+* Tomoya Ishida (tompng)
+ * tomoyapenguin@gmail.com
+ * cctld: jp
diff --git a/sample/trick2018/03-tompng/entry.rb b/sample/trick2018/03-tompng/entry.rb
new file mode 100644
index 0000000000..26416c7019
--- /dev/null
+++ b/sample/trick2018/03-tompng/entry.rb
@@ -0,0 +1,31 @@
+X=[];class String def-@;replace ?-+self end;def-a;X.reject!{|x|x.
+__id__==__id__};a.replace(self+?-+a) end end;at_exit{eval C=(Zlib
+.inflate((X*?-).tr(?-,'').tr('q-z','0-9').to_i(26).digits(0x100).
+pack'C*'))};def method_missing n;(X<<n.to_s)[-1]end;require'zlib'
+fzygtoxyzgntmdmuwvfoffbpmvzojpkhczvjvjdbtscnldwbdoprackddovivvmkz
+ponzmosvtjciwkgaslscxxxwudeesmmqpfhislxuxnnypulxstzgobyaekqqhbjcg
+mvko------------ddkeys----eivhnccaqyiw---bzyccmt-----------ymtnge
+jwhi--------------pjxf------mdarbtumnv---qasda--------------gmwdt
+wrtk---qtpzgnce----fsl-------fkgzgtbpp---gwnm----pxkpqkdiw---owga
+momz---yjjvpnvar---zeo---v-----duvalwu---nsqt---waofemwakivnyqkjd
+fzag---uhvusmkl----kzb---rhc----iutzjr---mqlh---ayijpwativpweaato
+xexs--------------rvgv---pjdz-----lkkg---uiaw---lovitupw-----fwmn
+kfru------------jvjpgv---jskycf----pal---gbuf---hfdnywog-----iuca
+pntn---apmkqroeuzwuwkw---gqnmgof-----b---hlpl---vkkyhfyrqfr--jwrl
+kmdb---dhspujhmtgrkccu---uonfummdt-------rqfw----bpiactehwp--fncq
+yzvz---gdaxebplhfndran---ytfmviryeh------hqwkl---------------nced
+bibu---fnkdthgldhkxxjg---rwnmpudhbqin----gucoyki------------hfura
+cqdgqpyzqfzknvdjoxxhpjulwwyebtocxdrvklbuviwwcatlmdosxfvwntzbijguy
+iglrvvzlxerflupxvsyujfacuwhrvmnecgtewtqkhtdggcltejiyqcluclkycwvzg
+vvxfysvttfbeglvrlngntdngzyhqrmltazwdydxrsvjploembhgxdvfmmhepbschm
+brn--iqrcdb--evv----tqp------lg--uein-wzut--mr------wkh------foqz
+zsf--srjnjp--ampb--pfio--hgtekx--rrr---fwd--jn--xqkezcz--vsb--nya
+khrc--evlr--oioxs--mqce--bqfmag--bwz---xda--qw--jnuzelr--qzi--itx
+mdxd--duso--wxbot--nmon--ugnbdpc--a--c--e--hlg--twxndre--tby--rhg
+evhbn--zb--dtxmiz--dpia------vie--h--i--t--shh------kfn------owna
+ealmt--kb--scxdjy--smvl--dqmgebk--t--s--t--gfd--updcbnc--rh--dwwp
+dvpnxb----wpljjdy--kolc--qflyleok---xkv---usbj--jhrawbn--ewx--bgf
+eaqwrw----ejwxhet--dice--eoczconm---urz---rqyp--hovvvfc--bskj--el
+aocjcts--jtumwxm----mgy------xpaoq-jtwqr-aipay------dhy--iync--hk
+sckddmvuvvuhhqstumaykvczaaujrumqbbqsdvdycplyrlkkojlxnkrhbbrmnjxyf
+cdtcmpfmjvthwkpzucbblttgumomlxnxwjeypfeagaukfzeokzxjebkpigcvlqnso
diff --git a/sample/trick2018/03-tompng/output.txt b/sample/trick2018/03-tompng/output.txt
new file mode 100644
index 0000000000..ed9a4079cc
--- /dev/null
+++ b/sample/trick2018/03-tompng/output.txt
@@ -0,0 +1,44 @@
+undef p;X=[];class String def-@;replace ?-+dup end;def-a;X.reject!{|x|x.__id__==__id__};a.replace(self+?-+a) end end;at_exit{eval C=
+(Zlib.inflate (X*?-).tr(?-,'').tr('q-z','0-9').to_i(26).digits(256).pack'C*')};def method_missing n;(X<<n.to_s)[-1]end;require'zlib'
+gmlztzdculbtzgtjfetuh---k--htf----d-----------------------------------------------------g-b-----s--t-g--------jmuwescmgchftikfjafccs
+ivchcveidpvxdabnvwyga-f--v-------xf----------------------------------------------------q-v---l-------q---------liiNeawriayymwooxgxqw
+rfosepqsmojseyezmwbhi--------------ew--------------------------------------------------m---k-r-----------vwu--hiotltdmczwyjmlvbyfqwq
+uvvykqdjednoqgtcmtfbzs---------f----o--------------------------------------------------t--a------m----x---f-----dldzsakyofetfozfpmrq
+geusutariiiNiulkjbwlm-----d------------------------------------------------------------j---------o---------x--j-uitzrgwpupwhvendhyno
+uubvnssiywkklwwdufhhi-rw----k---v-------------------------------------------------------sty-----yg---l---c-v----wkffpskpumolqmkeryzg
+zrxdaiposwybbzgxdnegh-----g-----ma--n---------------------------------------------------------j----n--b-n-------yqavmscswdogpcgopygt
+axiqfswlhzeamvymdnteo---q-q-w--------------------------fhrmj-----------------hkou-----------f-----d----u-o------evcuxxegekfgivzzujan
+nslioftsvqvtkeigvfgwr-------------lyco-----------------igyvg-----------------okuk---------m--b-u--d--y------s---dadjrlykfhtermzfyktu
+btoxzfpPicxxfligbivvf--------h----yrat---------------------------------------vjwd---------------------d-ki--o--tyqosehopkwttigwwfskp
+komzvnyrvkjcjwbmdwdkp----------vxphiNdtawn--xms-saketo--jnld----ezulntdaz----nzna-----vhjwt------h----x--x--o--saxxsrkgktqotaluylbkk
+sclegratyaarmgmepheml----------hwgglhlrfcx--znvmpfsgjx-onhju---gtxsmzqprlt---mjzy---frhdk-------------v---mj----dzjujmbgldfwoybgicwu
+tfhgnhlzxlwtdtkgzlaca-------------gmex------arlm--------rvmh-ajtgf-----pqal--wcux-zatyi-------------------------xnluwybcugjclmablshn
+tnjohqtqzivgmyutrssil-------------lcwq------jrf--------gcaii-maie------------vvnfjfqwo--------------------------filivosyhkxcvuwdibwj
+tyxjiopiFqypvwdzoatuq-------------tdln------cnx---------ffuf-ajvq------------tyyypglpzmj------------------------vtqzwewqdsijrbymvpwn
+niNffphoehukpvvmzvhyd-------------ahqd------nfr---------jeqk--toap-----mxhyg-tedv---otrwy-----------------------mjxnrktackwxwiajdnuc
+kkxhuwbvibpvgvcampadi-------------ebmencqz--obf--------wfprz---qmrotkijiqv---ggfp-----hlzw----------------------kastwdpxiyftmypuxbtu
+xetudmwzpomktgnjkcsyc---------------fwpdx---xb----j-----se-k------tllakc-----gjoo-------we------mic---lktk------ubtnrxvrjzuqlrfrsnmf
+okdvfvcdbdqkckjialskk---------------------------v---u-------l----------------------------------z--q--qfg--------aaliNbxbjjpxebboneye
+kcbkjmdclwnfawtfnwkeq----------------------------------j---y-------------------------------a---jmbyo-sgef--gf---extljbozuoofgyvsilct
+xzoqmsqgzjxxpjqwkjkdd------------------------o--------m-------f---------------------------------n--de-ajz-rzv---fhnpbkrwdxoozpxeaxaf
+mbcwxuiqdwcmadheiykaa-----------------------q-f------l---i---------------------------------r----zf---k--y---fi--dcnycheytylcgnioauee
+yekiNacriqoevtdjerqbp----------------------------w---yy-----my----------------------------ko--mnbpskr--c-----j--ozyqpbfovhbhyoprzgqr
+czwtuopxkdbphocfawvbk--------------------------q-s----j--b---------------------------------hd-xsb----bfiNp--w---fmwuvfambdqvxtzldwmh
+xysnyrseydlkjcwfbsjnr-------------------------d-d-------------------------------------------f-enpss---qllpwr----almsdidvjwoigvldfqoa
+lrpbixjpofxocxlflscpo------------------------------q-fyu--z-------------------------------------kfd-z---n-------bqxurujnxzurrdgcojks
+jetyfdkcekckxbyosbfws-------------wdfhgwuvejjmf-----sxjubpvgcsl-------tnmixpv---------eurabjsdvstfv-------------qcyiqhonwoyixqeonfvp
+mopPhywsozohitutgmmrb------------zxwtxe--riedeo---mspgpnv--pimlh------jhtzajk--------qqovvq---ldbrh-------------xtooxpayonpcvvtmvpra
+vvuyiunpoeagdzqjecsub------------klrw------snrc---rrct------aajom--------nsyk--------peea-------azq-------------iNjefdkfhnagjicqwmsm
+mbwwbfgehhbdmvvlflmee---------------------hkejn---jtbo-------jdtje-------jcei---------afyz-----smtc-------------kksvfjyuaqtohxiohhlz
+dvfmfrzcmnsfruhqgjuxz------------------dfxdnlk----kkra-------xmmtf-------jwkw----------rdoozxtcho---------------bbwwferxwnnmdzcniicv
+mfneisdlyeqwynldjgonj----------------jgrjvc-------uxga-------ghnpr-------sers--------scbknx----gmjo-------------moedtnlbflhtlkjibrqk
+gobwqshnpbdcpjmjaeczr--------------iscsxs---------zfpo-------hhfwy-------qbba-------vhlxc-------ntod------------ndwzdomaptumzejiwqbn
+snucynymvfpnadyqkzfcv-------------ggze------------kuvfs-----zuhod--------mylo-------jhwyp-----z-pywd------------dqfmpnevmtqcikbrilto
+aotyxkipebdkassogpcbl-----------wgackesmvvsrihhd---orzndjndlzpb----------eobf-------kkayixzyotqfafa-w-----------mjjxoomwdglwvccozzut
+rthesuszfwycsqqrtxlot-----------ejcqlhriilqbtrys------lwbkzmvp-----------zzwm-------l--qijwfllndzb-ik-----------mmokqomjepdcotnsiNig
+nloryyoswwdmefywnnuhph------------------------------------------------------r--r-nd-----h--x--------------------hlgzeqqslwxgtjgghquf
+nssngjtiudsrvfuxjzclhjhj----------------------------------------------------------t----------------k-f-mp-------obhyehqebtpjbkeepqzt
+ezogzsimfynqmkteaipejo-g-yser-----------------------------------------------e------h-------------i---y----------qpgcqnltivmmsximbbsy
+wtjjolwyoselcumgklqwpldkl-ulm-m---------------------------------------------------------------q---u-f--l--------buixfiitufktsqdtnrei
+tgrtitcewseetlpeuuujb-osdokjozc------------------------------------------n---d-----f--------g--------q--g-------jyyqtezuzmcxgpcwuwfx
+dpPayqmzxrwhbswwalygfurtkruw-u-k---------------------------------------------d---h------i----------c----i-------ulowcddvjbxthqlxjzbe
diff --git a/sample/trick2018/03-tompng/remarks.markdown b/sample/trick2018/03-tompng/remarks.markdown
new file mode 100644
index 0000000000..fe9eec5989
--- /dev/null
+++ b/sample/trick2018/03-tompng/remarks.markdown
@@ -0,0 +1,19 @@
+### Remarks
+
+Bundle install
+ this program depends on `gem chunky_png`
+
+Run it with the following command:
+ bundle exec ruby entry.rb trick.png
+ bundle exec ruby entry.rb [other png file]
+
+I confirmed the following implementations/platforms:
+
+* ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin16]
+* ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-darwin16]
+
+### Description
+
+This program is a png image viewer.
+The output is an asciiart of the given png file,
+and it is also a source code of the png viewer itself.
diff --git a/sample/trick2018/03-tompng/trick.png b/sample/trick2018/03-tompng/trick.png
new file mode 100644
index 0000000000..d4bb0bd7c3
--- /dev/null
+++ b/sample/trick2018/03-tompng/trick.png
Binary files differ
diff --git a/sample/trick2018/04-colin/authors.markdown b/sample/trick2018/04-colin/authors.markdown
new file mode 100644
index 0000000000..a846d12535
--- /dev/null
+++ b/sample/trick2018/04-colin/authors.markdown
@@ -0,0 +1,3 @@
+* Colin Fulton
+ * justcolin@gmail.com
+ * cctld: us
diff --git a/sample/trick2018/04-colin/entry.rb b/sample/trick2018/04-colin/entry.rb
new file mode 100644
index 0000000000..442a8ea3a8
--- /dev/null
+++ b/sample/trick2018/04-colin/entry.rb
@@ -0,0 +1,2 @@
+# Copyright 2018. Available for use under the terms of the MIT License.
+$🚀=0;def 🤔 ðŸ·,🤔=0,&b;puts ' '*$🚀+(🤔 ?"":"🚫 ")+ðŸ·;$🚀+=4;b&.[];$🚀-=4;end
diff --git a/sample/trick2018/04-colin/remarks.markdown b/sample/trick2018/04-colin/remarks.markdown
new file mode 100644
index 0000000000..5f4f1a8dfe
--- /dev/null
+++ b/sample/trick2018/04-colin/remarks.markdown
@@ -0,0 +1,62 @@
+### Remarks
+
+Create a Ruby file that requires entry.rb with a series of test in it the run the file using ruby:
+
+```
+ruby name_of_test_file.rb
+```
+
+To create a test, call 🤔 with two arguments. The first is a string describing what this tests, the second argument is the test assertion. If the assertion is truthy, the test passes. If the assertion is falsy, the test fails.
+
+```
+string_1 = "Hello world!"
+string_2 = "This is not the same!"
+
+🤔 "The two strings are equal",
+ string_1 == string_2
+```
+
+To create a group of tests under a label, call 🤔 with a string describing the group and a block containing the tests in that group.
+
+```
+🤔 "This is a group of tests" do
+ # Add other groups and/or tests here.
+end
+```
+
+Here is an example:
+
+```
+require './entry'
+
+🤔 "Math" do
+ 🤔 "Addition" do
+ 🤔 "One plus one equals two.",
+ 1+1 == 2
+ 🤔 "One plus one equals eleven. (This should fail.)",
+ 1+1 == 11
+ end
+
+ 🤔 "Subtraction" do
+ 🤔 "One minus one equals zero.",
+ 1-1 == 0
+ 🤔 "Ten minus one equal nine.",
+ 10-1 == 9
+ end
+end
+```
+
+It has been tested with the following Ruby versions:
+
+* ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
+* ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin15]
+* If you replace `b&.[]` with `b&&b[]` it will work with ruby 2.0.0 as well, but it will be one character longer.
+
+
+### Description
+
+The goal was to create a testing library where the test files looked good and the output looked good in as few characters as possible. The result is 68 characters and has one method to handle everything.
+
+### Limitation
+
+Your terminal program must support Unicode characters for the test output to look correct. If your terminal does not support Unicode, simply replace the 🚫 in the code with whatever character you want to prefix failing tests.
diff --git a/sample/trick2018/05-tompng/authors.markdown b/sample/trick2018/05-tompng/authors.markdown
new file mode 100644
index 0000000000..26ebe24da6
--- /dev/null
+++ b/sample/trick2018/05-tompng/authors.markdown
@@ -0,0 +1,3 @@
+* Tomoya Ishida (tompng)
+ * tomoyapenguin@gmail.com
+ * cctld: jp
diff --git a/sample/trick2018/05-tompng/entry.rb b/sample/trick2018/05-tompng/entry.rb
new file mode 100644
index 0000000000..31522b6de2
--- /dev/null
+++ b/sample/trick2018/05-tompng/entry.rb
@@ -0,0 +1,41 @@
+ X=[];def self.method_missing n;n.to_s.chars;end
+ l=[];def l.-a;X<<a=[nil,*a];a;end;def l.+a;self-a;end
+ class Array;def-@;[]-self;end;def-a;replace [*self,nil,*a
+ ]end;alias +@ -@;alias + -;end;def gen3d f;yield;b=['solid obj'];w,
+ h=X[0].size,X.size;X<<[];a=->r,z,dr,dz{;r-=w/2.0;z*=2;r2,z2=r+dr,z+dz*2;if r>0||r2>
+ 0;r=[0,r].max;r2=[0,r2].max;16.times{|i|m=Math;p=m::PI/8;;c,s=m.cos(t=i*p),m.sin(t)
+ c2,s2=m.cos(t=(i+1)*p),m.sin(t);t-=p/2;[[0,1,2],[0,2,3]].map{|a|b.push [:facet,'n'+
+ + 'ormal',dz*m.cos(t),dz*m.sin(t),-dr]*' ','outer loop',a.map{|i|'v'+
+ ++ "ertex #{[[r*c,r*s,z],[r*c2,r*s2,z],[r2*c2,r2*s2,z2],[r2*
+ +c, r2*s,z2]][i]*' '}"},:endloop,:endfacet}}end};(0...h).
+ map{| y|w.times{|x|[X[y-1][x]||a[x,y,1,0],X[y+1][x]||
+ a[x+1,y+
+ 1,-1,0],X[
+ y][x-+1]||a[
+ x,y+1,0,-1],X[y
+ ][x++1]||a[x+1,y,
+ 0,1]]if X[y][x]}}
+ s=[b,'end'+b[0]]*
+ $/;File.write(f,
+ s);X.replace(
+ []);end
+
+gen3d 'wine_glass.stl' do
+ l--ww------------------ww--l
+ l--ww------------------ww--l
+ l--ww++++++++++++++++++ww--l
+ l--ww++++++++++++++++++ww--l
+ l--ww++++++++++++++++++ww--l
+ l--ww++++++++++++++++++ww--l
+ l---ww++++++++++++++++ww---l
+ l----www++++++++++++www----l
+ l------www++++++++www------l
+ l--------wwwwwwwwww--------l
+ l-----------wwww-----------l
+ l------------ww------------l
+ l------------ww------------l
+ l------------ww------------l
+ l-----------wwww-----------l
+ l---------wwwwwwww---------l
+ l----wwwwwwwwwwwwwwwwww----l
+end
diff --git a/sample/trick2018/05-tompng/preview_of_output.png b/sample/trick2018/05-tompng/preview_of_output.png
new file mode 100644
index 0000000000..db511ee2f3
--- /dev/null
+++ b/sample/trick2018/05-tompng/preview_of_output.png
Binary files differ
diff --git a/sample/trick2018/05-tompng/remarks.markdown b/sample/trick2018/05-tompng/remarks.markdown
new file mode 100644
index 0000000000..17be56b61f
--- /dev/null
+++ b/sample/trick2018/05-tompng/remarks.markdown
@@ -0,0 +1,31 @@
+### Remarks
+
+Just run it with no argument:
+
+ ruby entry.rb
+
+I confirmed the following implementations/platforms:
+
+* ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin16]
+* ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-darwin16]
+* ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin16]
+
+### Description
+
+This program will generate `wine_glass.stl`, a 3D data file(STL format) of a wine glass.
+You can change the shape by modifying the DSL part.
+For sake cup:
+```ruby
+gen3d 'ochoko.stl' do
+ l------------------------l
+ l-ww------------------ww-l
+ l-ww------------------ww-l
+ l-ww++++++++++++++++++ww-l
+ l-ww++++++++++++++++++ww-l
+ l--ww++++++++++++++++ww--l
+ l---wwww++++++++++wwww---l
+ l----wwwwwwwwwwwwwwww----l
+ l----www----------www----l
+end
+```
+`+` and `-` are the same meaning(just for apperance)
diff --git a/sample/trick2018/README.md b/sample/trick2018/README.md
new file mode 100644
index 0000000000..345500b00a
--- /dev/null
+++ b/sample/trick2018/README.md
@@ -0,0 +1,16 @@
+This directory contains the award-winning entries of
+the 3rd Transcendental Ruby Imbroglio Contest for rubyKaigi (TRICK 2018).
+
+THESE ARE BAD EXAMPLES! You must NOT use them as a sample code.
+
+* 01-kinaba/entry.rb: "Most reserved" - **Gold award**
+* 02-mame/entry.rb: "Best spiral" - **Silver award**
+* 03-tompng/entry.rb: "Best png viewer" - **Bronze award**
+* 04-colin/entry.rb: "Best one-liner" - 4th prize
+* 05-tompng/entry.rb: "Most three-dimensional" - 5th prize
+
+These files are licensed under MIT license.
+
+For the contest outline and other winning entries, see:
+
+https://github.com/tric/trick2018
diff --git a/signal.c b/signal.c
index 8ebb6b9c61..631fdae4e0 100644
--- a/signal.c
+++ b/signal.c
@@ -41,13 +41,6 @@
# define VALGRIND_MAKE_MEM_UNDEFINED(p, n) 0
#endif
-#if defined(__native_client__) && defined(NACL_NEWLIB)
-# include "nacl/signal.h"
-#endif
-
-extern ID ruby_static_id_signo;
-#define id_signo ruby_static_id_signo
-
#ifdef NEED_RUBY_ATOMIC_OPS
rb_atomic_t
ruby_atomic_exchange(rb_atomic_t *ptr, rb_atomic_t val)
@@ -69,12 +62,11 @@ ruby_atomic_compare_and_swap(rb_atomic_t *ptr, rb_atomic_t cmp,
}
#endif
-#ifndef NSIG
-# define NSIG (_SIGMAX + 1) /* For QNX */
-#endif
-
+#define FOREACH_SIGNAL(sig, offset) \
+ for (sig = siglist + (offset); sig < siglist + numberof(siglist); ++sig)
+enum { LONGEST_SIGNAME = 7 }; /* MIGRATE and RETRACT */
static const struct signals {
- const char *signm;
+ char signm[LONGEST_SIGNAME + 1];
int signo;
} siglist [] = {
{"EXIT", 0},
@@ -136,15 +128,9 @@ static const struct signals {
#ifdef SIGCONT
{"CONT", SIGCONT},
#endif
-#ifdef SIGCHLD
- {"CHLD", SIGCHLD},
-#endif
-#ifdef SIGCLD
- {"CLD", SIGCLD},
-#else
-# ifdef SIGCHLD
- {"CLD", SIGCHLD},
-# endif
+#if RUBY_SIGCHLD
+ {"CHLD", RUBY_SIGCHLD },
+ {"CLD", RUBY_SIGCHLD },
#endif
#ifdef SIGTTIN
{"TTIN", SIGTTIN},
@@ -209,20 +195,81 @@ static const struct signals {
#ifdef SIGINFO
{"INFO", SIGINFO},
#endif
- {NULL, 0}
};
static const char signame_prefix[3] = "SIG";
+static const int signame_prefix_len = (int)sizeof(signame_prefix);
static int
-signm2signo(const char *nm)
+signm2signo(VALUE *sig_ptr, int negative, int exit, int *prefix_ptr)
{
const struct signals *sigs;
+ VALUE vsig = *sig_ptr;
+ const char *nm;
+ long len, nmlen;
+ int prefix = 0;
- for (sigs = siglist; sigs->signm; sigs++)
- if (strcmp(sigs->signm, nm) == 0)
- return sigs->signo;
- return 0;
+ if (RB_SYMBOL_P(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;
+ }
+
+ rb_must_asciicompat(vsig);
+ RSTRING_GETMEM(vsig, nm, len);
+ if (memchr(nm, '\0', len)) {
+ 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;
+ }
+ else {
+ negative = 0;
+ }
+ if (len >= prefix + signame_prefix_len) {
+ if (memcmp(nm + prefix, signame_prefix, sizeof(signame_prefix)) == 0)
+ prefix += signame_prefix_len;
+ }
+ if (len <= (long)prefix) {
+ unsupported:
+ if (prefix == signame_prefix_len) {
+ prefix = 0;
+ }
+ else if (prefix > signame_prefix_len) {
+ prefix -= signame_prefix_len;
+ len -= prefix;
+ vsig = rb_str_subseq(vsig, prefix, len);
+ prefix = 0;
+ }
+ else {
+ len -= prefix;
+ vsig = rb_str_subseq(vsig, prefix, len);
+ prefix = signame_prefix_len;
+ }
+ rb_raise(rb_eArgError, "unsupported signal `%.*s%"PRIsVALUE"'",
+ prefix, signame_prefix, vsig);
+ }
+
+ if (prefix_ptr) *prefix_ptr = prefix;
+ nmlen = len - prefix;
+ 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;
+ }
+ }
+ goto unsupported;
}
static const char*
@@ -230,9 +277,10 @@ signo2signm(int no)
{
const struct signals *sigs;
- for (sigs = siglist; sigs->signm; sigs++)
+ FOREACH_SIGNAL(sigs, 0) {
if (sigs->signo == no)
return sigs->signm;
+ }
return 0;
}
@@ -291,7 +339,6 @@ esignal_init(int argc, VALUE *argv, VALUE self)
int argnum = 1;
VALUE sig = Qnil;
int signo;
- const char *signm;
if (argc > 0) {
sig = rb_check_to_integer(argv[0], "to_int");
@@ -312,19 +359,11 @@ esignal_init(int argc, VALUE *argv, VALUE self)
}
}
else {
- int len = sizeof(signame_prefix);
- if (SYMBOL_P(sig)) sig = rb_sym2str(sig); else StringValue(sig);
- signm = RSTRING_PTR(sig);
- if (strncmp(signm, signame_prefix, len) == 0) {
- signm += len;
- len = 0;
+ int prefix;
+ signo = signm2signo(&sig, FALSE, FALSE, &prefix);
+ if (prefix != signame_prefix_len) {
+ sig = rb_str_append(rb_str_new_cstr("SIG"), sig);
}
- signo = signm2signo(signm);
- if (!signo) {
- rb_raise(rb_eArgError, "unsupported name `%.*s%"PRIsVALUE"'",
- len, signame_prefix, sig);
- }
- sig = rb_sprintf("SIG%s", signm);
}
rb_call_super(1, &sig);
rb_ivar_set(self, id_signo, INT2NUM(signo));
@@ -352,13 +391,21 @@ interrupt_init(int argc, VALUE *argv, VALUE self)
VALUE args[2];
args[0] = INT2FIX(SIGINT);
- rb_scan_args(argc, argv, "01", &args[1]);
+ args[1] = rb_check_arity(argc, 0, 1) ? argv[0] : Qnil;
return rb_call_super(2, args);
}
+#include "debug_counter.h"
+void rb_malloc_info_show_results(void); /* gc.c */
+
void
ruby_default_signal(int sig)
{
+#if USE_DEBUG_COUNTER
+ rb_debug_counter_show_results("killed by signal.");
+#endif
+ rb_malloc_info_show_results();
+
signal(sig, SIG_DFL);
raise(sig);
}
@@ -409,51 +456,18 @@ rb_f_kill(int argc, const VALUE *argv)
#ifndef HAVE_KILLPG
#define killpg(pg, sig) kill(-(pg), (sig))
#endif
- int negative = 0;
int sig;
int i;
VALUE str;
- const char *s;
rb_check_arity(argc, 2, UNLIMITED_ARGUMENTS);
- switch (TYPE(argv[0])) {
- case T_FIXNUM:
+ if (FIXNUM_P(argv[0])) {
sig = FIX2INT(argv[0]);
- break;
-
- case T_SYMBOL:
- str = rb_sym2str(argv[0]);
- goto str_signal;
-
- case T_STRING:
+ }
+ else {
str = argv[0];
- str_signal:
- s = RSTRING_PTR(str);
- if (s[0] == '-') {
- negative++;
- s++;
- }
- if (strncmp(signame_prefix, s, sizeof(signame_prefix)) == 0)
- s += 3;
- if ((sig = signm2signo(s)) == 0) {
- long ofs = s - RSTRING_PTR(str);
- if (ofs) str = rb_str_subseq(str, ofs, RSTRING_LEN(str)-ofs);
- rb_raise(rb_eArgError, "unsupported name `SIG%"PRIsVALUE"'", str);
- }
-
- if (negative)
- sig = -sig;
- break;
-
- default:
- str = rb_check_string_type(argv[0]);
- if (!NIL_P(str)) {
- goto str_signal;
- }
- rb_raise(rb_eArgError, "bad signal type %s",
- rb_obj_classname(argv[0]));
- break;
+ sig = signm2signo(&str, TRUE, FALSE, NULL);
}
if (argc <= 1) return INT2FIX(0);
@@ -495,7 +509,7 @@ rb_f_kill(int argc, const VALUE *argv)
#ifdef SIGSTOP
case SIGSTOP:
#endif
- ruby_kill(pid, sig);
+ kill(pid, sig);
break;
default:
t = signal_ignored(sig);
@@ -525,6 +539,9 @@ static struct {
rb_atomic_t cnt[RUBY_NSIG];
rb_atomic_t size;
} signal_buff;
+#if RUBY_SIGCHLD
+volatile unsigned int ruby_nocldwait;
+#endif
#ifdef __dietlibc__
#define sighandler_t sh_t
@@ -544,7 +561,7 @@ typedef RETSIGTYPE ruby_sigaction_t(int);
#endif
#ifdef USE_SIGALTSTACK
-int
+static int
rb_sigaltstack_size(void)
{
/* XXX: BSD_vfprintf() uses >1500KiB stack and x86-64 need >5KiB stack. */
@@ -567,19 +584,18 @@ rb_sigaltstack_size(void)
}
/* alternate stack for SIGSEGV */
-void
-rb_register_sigaltstack(rb_thread_t *th)
+void *
+rb_register_sigaltstack(void)
{
stack_t newSS, oldSS;
- if (!th->altstack)
- rb_bug("rb_register_sigaltstack: th->altstack not initialized\n");
-
- newSS.ss_sp = th->altstack;
newSS.ss_size = rb_sigaltstack_size();
+ newSS.ss_sp = xmalloc(newSS.ss_size);
newSS.ss_flags = 0;
sigaltstack(&newSS, &oldSS); /* ignore error. */
+
+ return newSS.ss_sp;
}
#endif /* USE_SIGALTSTACK */
@@ -609,10 +625,25 @@ ruby_signal(int signum, sighandler_t handler)
#endif
switch (signum) {
-#ifdef SA_NOCLDWAIT
- case SIGCHLD:
- if (handler == SIG_IGN)
- sigact.sa_flags |= SA_NOCLDWAIT;
+#if RUBY_SIGCHLD
+ case RUBY_SIGCHLD:
+ 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;
+ }
+# else
+ sigact.sa_handler = handler;
+ sigact.sa_flags = 0;
+# endif
+ }
+ else {
+ ruby_nocldwait = 0;
+ }
break;
#endif
#if defined(SA_ONSTACK) && defined(USE_SIGALTSTACK)
@@ -629,9 +660,11 @@ ruby_signal(int signum, sighandler_t handler)
return SIG_ERR;
}
if (old.sa_flags & SA_SIGINFO)
- return (sighandler_t)old.sa_sigaction;
+ handler = (sighandler_t)old.sa_sigaction;
else
- return old.sa_handler;
+ handler = old.sa_handler;
+ ASSUME(handler != SIG_ERR);
+ return handler;
}
sighandler_t
@@ -691,13 +724,35 @@ signal_enque(int sig)
ATOMIC_INC(signal_buff.size);
}
+#if RUBY_SIGCHLD
+static rb_atomic_t sigchld_hit;
+/* destructive getter than simple predicate */
+# define GET_SIGCHLD_HIT() ATOMIC_EXCHANGE(sigchld_hit, 0)
+#else
+# define GET_SIGCHLD_HIT() 0
+#endif
+
static RETSIGTYPE
sighandler(int sig)
{
int old_errnum = errno;
- signal_enque(sig);
- rb_thread_wakeup_timer_thread();
+ /* the VM always needs to handle SIGCHLD for rb_waitpid */
+ if (sig == RUBY_SIGCHLD) {
+#if RUBY_SIGCHLD
+ rb_vm_t *vm = GET_VM();
+ ATOMIC_EXCHANGE(sigchld_hit, 1);
+
+ /* avoid spurious wakeup in main thread iff nobody uses trap(:CHLD) */
+ if (vm && ACCESS_ONCE(VALUE, vm->trap_list.cmd[sig])) {
+ signal_enque(sig);
+ }
+#endif
+ }
+ else {
+ signal_enque(sig);
+ }
+ rb_thread_wakeup_timer_thread(sig);
#if !defined(BSD_SIGNAL) && !defined(POSIX_SIGNAL)
ruby_signal(sig, sighandler);
#endif
@@ -761,7 +816,7 @@ static const char *received_signal;
#endif
#if defined(USE_SIGALTSTACK) || defined(_WIN32)
-NORETURN(void ruby_thread_stack_overflow(rb_thread_t *th));
+NORETURN(void rb_ec_stack_overflow(rb_execution_context_t *ec, int crit));
# if defined __HAIKU__
# define USE_UCONTEXT_REG 1
# elif !(defined(HAVE_UCONTEXT_H) && (defined __i386__ || defined __x86_64__ || defined __amd64__))
@@ -772,74 +827,112 @@ NORETURN(void ruby_thread_stack_overflow(rb_thread_t *th));
# elif defined __FreeBSD__
# define USE_UCONTEXT_REG 1
# endif
+#if defined(HAVE_PTHREAD_SIGMASK)
+# define ruby_sigunmask pthread_sigmask
+#elif defined(HAVE_SIGPROCMASK)
+# define ruby_sigunmask sigprocmask
+#endif
+static void
+reset_sigmask(int sig)
+{
+#if defined(ruby_sigunmask)
+ sigset_t mask;
+#endif
+ clear_received_signal();
+#if defined(ruby_sigunmask)
+ sigemptyset(&mask);
+ sigaddset(&mask, sig);
+ if (ruby_sigunmask(SIG_UNBLOCK, &mask, NULL)) {
+ rb_bug_errno(STRINGIZE(ruby_sigunmask)":unblock", errno);
+ }
+#endif
+}
+
# ifdef USE_UCONTEXT_REG
static void
-check_stack_overflow(const uintptr_t addr, const ucontext_t *ctx)
+check_stack_overflow(int sig, const uintptr_t addr, const ucontext_t *ctx)
{
const DEFINE_MCONTEXT_PTR(mctx, ctx);
# if defined __linux__
# if defined REG_RSP
const greg_t sp = mctx->gregs[REG_RSP];
+ const greg_t bp = mctx->gregs[REG_RBP];
# else
const greg_t sp = mctx->gregs[REG_ESP];
+ const greg_t bp = mctx->gregs[REG_EBP];
# endif
# elif defined __APPLE__
+# if __DARWIN_UNIX03
+# define MCTX_SS_REG(reg) __ss.__##reg
+# else
+# define MCTX_SS_REG(reg) ss.reg
+# endif
# if defined(__LP64__)
- const uintptr_t sp = mctx->__ss.__rsp;
+ const uintptr_t sp = mctx->MCTX_SS_REG(rsp);
+ const uintptr_t bp = mctx->MCTX_SS_REG(rbp);
# else
- const uintptr_t sp = mctx->__ss.__esp;
+ const uintptr_t sp = mctx->MCTX_SS_REG(esp);
+ const uintptr_t bp = mctx->MCTX_SS_REG(ebp);
# endif
# elif defined __FreeBSD__
# if defined(__amd64__)
const __register_t sp = mctx->mc_rsp;
+ const __register_t bp = mctx->mc_rbp;
# else
const __register_t sp = mctx->mc_esp;
+ const __register_t bp = mctx->mc_ebp;
# endif
# elif defined __HAIKU__
# if defined(__amd64__)
const unsigned long sp = mctx->rsp;
+ const unsigned long bp = mctx->rbp;
# else
const unsigned long sp = mctx->esp;
+ const unsigned long bp = mctx->ebp;
# endif
# endif
enum {pagesize = 4096};
const uintptr_t sp_page = (uintptr_t)sp / pagesize;
+ const uintptr_t bp_page = (uintptr_t)bp / pagesize;
const uintptr_t fault_page = addr / pagesize;
/* SP in ucontext is not decremented yet when `push` failed, so
* the fault page can be the next. */
- if (sp_page == fault_page || sp_page == fault_page + 1) {
- rb_thread_t *th = ruby_current_thread;
- if ((uintptr_t)th->tag->buf / pagesize == sp_page) {
+ 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;
+ if ((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. */
- th->tag = th->tag->prev;
+ ec->tag = ec->tag->prev;
+ crit = TRUE;
}
- clear_received_signal();
- ruby_thread_stack_overflow(th);
+ reset_sigmask(sig);
+ rb_ec_stack_overflow(ec, crit);
}
}
# else
static void
-check_stack_overflow(const void *addr)
+check_stack_overflow(int sig, const void *addr)
{
int ruby_stack_overflowed_p(const rb_thread_t *, const void *);
- rb_thread_t *th = ruby_current_thread;
+ rb_thread_t *th = GET_THREAD();
if (ruby_stack_overflowed_p(th, addr)) {
- clear_received_signal();
- ruby_thread_stack_overflow(th);
+ reset_sigmask(sig);
+ rb_ec_stack_overflow(th->ec, FALSE);
}
}
# endif
# ifdef _WIN32
-# define CHECK_STACK_OVERFLOW() check_stack_overflow(0)
+# define CHECK_STACK_OVERFLOW() check_stack_overflow(sig, 0)
# else
# define FAULT_ADDRESS info->si_addr
# ifdef USE_UCONTEXT_REG
-# define CHECK_STACK_OVERFLOW() check_stack_overflow((uintptr_t)FAULT_ADDRESS, ctx)
+# define CHECK_STACK_OVERFLOW() check_stack_overflow(sig, (uintptr_t)FAULT_ADDRESS, ctx)
# else
-# define CHECK_STACK_OVERFLOW() check_stack_overflow(FAULT_ADDRESS)
+# define CHECK_STACK_OVERFLOW() check_stack_overflow(sig, FAULT_ADDRESS)
# endif
# define MESSAGE_FAULT_ADDRESS " at %p", FAULT_ADDRESS
# endif
@@ -857,6 +950,9 @@ NOINLINE(static void check_reserved_signal_(const char *name, size_t name_len));
#define check_reserved_signal(name) check_reserved_signal_(name, sizeof(name)-1)
#ifdef SIGBUS
+
+NORETURN(static ruby_sigaction_t sigbus);
+
static RETSIGTYPE
sigbus(int sig SIGINFO_ARG)
{
@@ -874,6 +970,8 @@ sigbus(int sig SIGINFO_ARG)
}
#endif
+NORETURN(static void ruby_abort(void));
+
static void
ruby_abort(void)
{
@@ -889,6 +987,9 @@ ruby_abort(void)
}
#ifdef SIGSEGV
+
+NORETURN(static ruby_sigaction_t sigsegv);
+
static RETSIGTYPE
sigsegv(int sig SIGINFO_ARG)
{
@@ -899,6 +1000,9 @@ sigsegv(int sig SIGINFO_ARG)
#endif
#ifdef SIGILL
+
+NORETURN(static ruby_sigaction_t sigill);
+
static RETSIGTYPE
sigill(int sig SIGINFO_ARG)
{
@@ -953,12 +1057,12 @@ sig_do_nothing(int sig)
}
#endif
-static void
+static int
signal_exec(VALUE cmd, int safe, int sig)
{
- rb_thread_t *cur_th = GET_THREAD();
- volatile unsigned long old_interrupt_mask = cur_th->interrupt_mask;
- int state;
+ rb_execution_context_t *ec = GET_EC();
+ volatile rb_atomic_t old_interrupt_mask = ec->interrupt_mask;
+ enum ruby_tag_type state;
/*
* workaround the following race:
@@ -967,42 +1071,54 @@ signal_exec(VALUE cmd, int safe, int sig)
* 3. rb_signal_exec runs on queued signal
*/
if (IMMEDIATE_P(cmd))
- return;
+ return FALSE;
- cur_th->interrupt_mask |= TRAP_INTERRUPT_MASK;
- TH_PUSH_TAG(cur_th);
- if ((state = EXEC_TAG()) == 0) {
+ ec->interrupt_mask |= TRAP_INTERRUPT_MASK;
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
VALUE signum = INT2NUM(sig);
rb_eval_cmd(cmd, rb_ary_new3(1, signum), safe);
}
- TH_POP_TAG();
- cur_th = GET_THREAD();
- cur_th->interrupt_mask = old_interrupt_mask;
+ EC_POP_TAG();
+ ec = GET_EC();
+ ec->interrupt_mask = old_interrupt_mask;
if (state) {
/* XXX: should be replaced with rb_threadptr_pending_interrupt_enque() */
- TH_JUMP_TAG(cur_th, state);
+ EC_JUMP_TAG(ec, state);
}
+ return TRUE;
}
void
rb_trap_exit(void)
{
rb_vm_t *vm = GET_VM();
- VALUE trap_exit = vm->trap_list[0].cmd;
+ VALUE trap_exit = vm->trap_list.cmd[0];
if (trap_exit) {
- vm->trap_list[0].cmd = 0;
- signal_exec(trap_exit, vm->trap_list[0].safe, 0);
+ vm->trap_list.cmd[0] = 0;
+ signal_exec(trap_exit, vm->trap_list.safe[0], 0);
}
}
+void ruby_waitpid_all(rb_vm_t *); /* process.c */
+
void
+ruby_sigchld_handler(rb_vm_t *vm)
+{
+ if (SIGCHLD_LOSSY || GET_SIGCHLD_HIT()) {
+ ruby_waitpid_all(vm);
+ }
+}
+
+/* returns true if a trap handler was run, false otherwise */
+int
rb_signal_exec(rb_thread_t *th, int sig)
{
rb_vm_t *vm = GET_VM();
- VALUE cmd = vm->trap_list[sig].cmd;
- int safe = vm->trap_list[sig].safe;
+ VALUE cmd = vm->trap_list.cmd[sig];
+ int safe = vm->trap_list.safe[sig];
if (cmd == 0) {
switch (sig) {
@@ -1035,8 +1151,9 @@ rb_signal_exec(rb_thread_t *th, int sig)
rb_threadptr_signal_exit(th);
}
else {
- signal_exec(cmd, safe, sig);
+ return signal_exec(cmd, safe, sig);
}
+ return FALSE;
}
static sighandler_t
@@ -1063,6 +1180,9 @@ default_handler(int sig)
#ifdef SIGUSR2
case SIGUSR2:
#endif
+#if RUBY_SIGCHLD
+ case RUBY_SIGCHLD:
+#endif
func = sighandler;
break;
#ifdef SIGBUS
@@ -1120,6 +1240,9 @@ trap_handler(VALUE *cmd, int sig)
break;
case 14:
if (memcmp(cptr, "SYSTEM_DEFAULT", 14) == 0) {
+ if (sig == RUBY_SIGCHLD) {
+ goto sig_dfl;
+ }
func = SIG_DFL;
*cmd = 0;
}
@@ -1165,33 +1288,15 @@ static int
trap_signm(VALUE vsig)
{
int sig = -1;
- const char *s;
- switch (TYPE(vsig)) {
- case T_FIXNUM:
+ if (FIXNUM_P(vsig)) {
sig = FIX2INT(vsig);
if (sig < 0 || sig >= NSIG) {
rb_raise(rb_eArgError, "invalid signal number (%d)", sig);
}
- break;
-
- case T_SYMBOL:
- vsig = rb_sym2str(vsig);
- s = RSTRING_PTR(vsig);
- goto str_signal;
-
- default:
- s = StringValuePtr(vsig);
-
- str_signal:
- if (strncmp(signame_prefix, s, sizeof(signame_prefix)) == 0)
- s += 3;
- sig = signm2signo(s);
- if (sig == 0 && strcmp(s, "EXIT") != 0) {
- long ofs = s - RSTRING_PTR(vsig);
- if (ofs) vsig = rb_str_subseq(vsig, ofs, RSTRING_LEN(vsig)-ofs);
- rb_raise(rb_eArgError, "unsupported signal SIG%"PRIsVALUE"", vsig);
- }
+ }
+ else {
+ sig = signm2signo(&vsig, FALSE, TRUE, NULL);
}
return sig;
}
@@ -1204,7 +1309,7 @@ trap(int sig, sighandler_t func, VALUE command)
rb_vm_t *vm = GET_VM();
/*
- * Be careful. ruby_signal() and trap_list[sig].cmd must be changed
+ * Be careful. ruby_signal() and trap_list.cmd[sig] must be changed
* atomically. In current implementation, we only need to don't call
* RUBY_VM_CHECK_INTS().
*/
@@ -1215,7 +1320,7 @@ trap(int sig, sighandler_t func, VALUE command)
oldfunc = ruby_signal(sig, func);
if (oldfunc == SIG_ERR) rb_sys_fail_str(rb_signo2signm(sig));
}
- oldcmd = vm->trap_list[sig].cmd;
+ oldcmd = vm->trap_list.cmd[sig];
switch (oldcmd) {
case 0:
case Qtrue:
@@ -1231,8 +1336,8 @@ trap(int sig, sighandler_t func, VALUE command)
break;
}
- vm->trap_list[sig].cmd = command;
- vm->trap_list[sig].safe = rb_safe_level();
+ ACCESS_ONCE(VALUE, vm->trap_list.cmd[sig]) = command;
+ vm->trap_list.safe[sig] = rb_safe_level();
return oldcmd;
}
@@ -1347,12 +1452,18 @@ sig_list(void)
VALUE h = rb_hash_new();
const struct signals *sigs;
- for (sigs = siglist; sigs->signm; sigs++) {
+ FOREACH_SIGNAL(sigs, 0) {
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); \
+ } while (0)
static int
install_sighandler(int signum, sighandler_t handler)
{
@@ -1366,29 +1477,27 @@ install_sighandler(int signum, sighandler_t handler)
}
return 0;
}
-#ifndef __native_client__
-# define install_sighandler(signum, handler) (install_sighandler(signum, handler) ? rb_bug(#signum) : (void)0)
-#endif
-#if defined(SIGCLD) || defined(SIGCHLD)
+# define install_sighandler(signum, handler) \
+ INSTALL_SIGHANDLER(install_sighandler(signum, handler), #signum, signum)
+
+#if RUBY_SIGCHLD
static int
init_sigchld(int sig)
{
sighandler_t oldfunc;
+ sighandler_t func = sighandler;
oldfunc = ruby_signal(sig, SIG_DFL);
if (oldfunc == SIG_ERR) return -1;
- if (oldfunc != SIG_DFL && oldfunc != SIG_IGN) {
- ruby_signal(sig, oldfunc);
- }
- else {
- GET_VM()->trap_list[sig].cmd = 0;
- }
+ ruby_signal(sig, func);
+ ACCESS_ONCE(VALUE, GET_VM()->trap_list.cmd[sig]) = 0;
+
return 0;
}
-# ifndef __native_client__
-# define init_sigchld(signum) (init_sigchld(signum) ? rb_bug(#signum) : (void)0)
-# endif
+
+# define init_sigchld(signum) \
+ INSTALL_SIGHANDLER(init_sigchld(signum), #signum, signum)
#endif
void
@@ -1404,9 +1513,6 @@ ruby_sig_finalize(void)
int ruby_enable_coredump = 0;
-#ifndef RUBY_DEBUG_ENV
-#define ruby_enable_coredump 0
-#endif
/*
* Many operating systems allow signals to be sent to running
@@ -1491,9 +1597,7 @@ Init_signal(void)
install_sighandler(SIGILL, (sighandler_t)sigill);
#endif
#ifdef SIGSEGV
-# ifdef USE_SIGALTSTACK
- rb_register_sigaltstack(GET_THREAD());
-# endif
+ RB_ALTSTACK_INIT(GET_VM()->main_altstack);
install_sighandler(SIGSEGV, (sighandler_t)sigsegv);
#endif
}
@@ -1504,11 +1608,55 @@ Init_signal(void)
install_sighandler(SIGSYS, sig_do_nothing);
#endif
-#if defined(SIGCLD)
- init_sigchld(SIGCLD);
-#elif defined(SIGCHLD)
- init_sigchld(SIGCHLD);
+#if RUBY_SIGCHLD
+ init_sigchld(RUBY_SIGCHLD);
#endif
rb_enable_interrupt();
}
+
+#if defined(HAVE_GRANTPT)
+extern int grantpt(int);
+#else
+static int
+fake_grantfd(int masterfd)
+{
+ errno = ENOSYS;
+ return -1;
+}
+#define grantpt(fd) fake_grantfd(fd)
+#endif
+
+int
+rb_grantpt(int masterfd)
+{
+ if (RUBY_SIGCHLD) {
+ rb_vm_t *vm = GET_VM();
+ int ret, e;
+
+ /*
+ * Prevent waitpid calls from Ruby by taking waitpid_lock.
+ * Pedantically, grantpt(3) is undefined if a non-default
+ * SIGCHLD handler is defined, but preventing conflicting
+ * waitpid calls ought to be sufficient.
+ *
+ * We could install the default sighandler temporarily, but that
+ * could cause SIGCHLD to be missed by other threads. Blocking
+ * SIGCHLD won't work here, either, unless we stop and restart
+ * timer-thread (as only timer-thread sees SIGCHLD), but that
+ * seems like overkill.
+ */
+ rb_nativethread_lock_lock(&vm->waitpid_lock);
+ {
+ ret = grantpt(masterfd); /* may spawn `pt_chown' and wait on it */
+ if (ret < 0) e = errno;
+ }
+ rb_nativethread_lock_unlock(&vm->waitpid_lock);
+
+ if (ret < 0) errno = e;
+ return ret;
+ }
+ else {
+ return grantpt(masterfd);
+ }
+}
diff --git a/siphash.c b/siphash.c
index 0df96f8320..153d2c690a 100644
--- a/siphash.c
+++ b/siphash.c
@@ -386,16 +386,15 @@ sip_hash_dump(sip_hash *h)
}
#endif /* SIP_HASH_STREAMING */
-#define SIP_2_ROUND(m, v0, v1, v2, v3) \
+#define SIP_ROUND(m, v0, v1, v2, v3) \
do { \
XOR64_TO((v3), (m)); \
SIP_COMPRESS(v0, v1, v2, v3); \
- SIP_COMPRESS(v0, v1, v2, v3); \
XOR64_TO((v0), (m)); \
} while (0)
uint64_t
-sip_hash24(const uint8_t key[16], const uint8_t *data, size_t len)
+sip_hash13(const uint8_t key[16], const uint8_t *data, size_t len)
{
uint64_t k0, k1;
uint64_t v0, v1, v2, v3;
@@ -415,13 +414,13 @@ sip_hash24(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_2_ROUND(m, v0, v1, v2, v3);
+ SIP_ROUND(m, v0, v1, v2, v3);
}
}
#else
for (; data != end; data += sizeof(uint64_t)) {
m = U8TO64_LE(data);
- SIP_2_ROUND(m, v0, v1, v2, v3);
+ SIP_ROUND(m, v0, v1, v2, v3);
}
#endif
@@ -468,14 +467,13 @@ sip_hash24(const uint8_t key[16], const uint8_t *data, size_t len)
break;
}
- SIP_2_ROUND(last, v0, v1, v2, v3);
+ SIP_ROUND(last, v0, v1, v2, v3);
XOR64_INT(v2, 0xff);
SIP_COMPRESS(v0, v1, v2, v3);
SIP_COMPRESS(v0, v1, v2, v3);
SIP_COMPRESS(v0, v1, v2, v3);
- SIP_COMPRESS(v0, v1, v2, v3);
XOR64_TO(v0, v1);
XOR64_TO(v0, v2);
diff --git a/siphash.h b/siphash.h
index 3f3988408b..f49bc511b1 100644
--- a/siphash.h
+++ b/siphash.h
@@ -43,6 +43,6 @@ int sip_hash_digest_integer(sip_hash *h, const uint8_t *data, size_t data_len, u
void sip_hash_free(sip_hash *h);
void sip_hash_dump(sip_hash *h);
-uint64_t sip_hash24(const uint8_t key[16], const uint8_t *data, size_t len);
+NO_SANITIZE("unsigned-integer-overflow", uint64_t sip_hash13(const uint8_t key[16], const uint8_t *data, size_t len));
#endif
diff --git a/sparc.c b/sparc.c
index dc3779035f..7f3a70fd0e 100644
--- a/sparc.c
+++ b/sparc.c
@@ -26,7 +26,7 @@ rb_sparc_flush_register_windows(void)
__volatile__
#endif
-/* This condition should be in sync with one in configure.in */
+/* This condition should be in sync with one in configure.ac */
#if defined(__sparcv9) || defined(__sparc_v9__) || defined(__arch64__)
# ifdef __GNUC__
("flushw" : : : "%o7")
diff --git a/spec/README b/spec/README
deleted file mode 100644
index c8d17ea427..0000000000
--- a/spec/README
+++ /dev/null
@@ -1,31 +0,0 @@
-= RubySpec
-
-RubySpec (http://ruby.github.io/rubyspec.github.io/) provides the
-annotation of the Ruby implementation in an executable format. The
-make task `update-rubyspec' retrieves the specification and puts it
-into this directory.
-
-== Directory structure
- spec
- +-- mspec driver library for executing the specification.
- +-- rubyspec
- +-- core specification for core libraries
- | +-- array
- | +-- bignum
- | +-- ...
- |
- +-- fixtures example classes for writing specs
- +-- language specification for Ruby language itself
- +-- library specification for standard libraries
- +-- addrev
- +-- ...
-
-== How to run
-:make target
- verifies all specs.
- $ make test-rubyspec
-:mspec command
- verifies the specified spec.
- $ mspec {language|core|library}
- or
- $ mspec spec/path/to/some_spec.rb
diff --git a/spec/README.md b/spec/README.md
new file mode 100644
index 0000000000..59c2c605c5
--- /dev/null
+++ b/spec/README.md
@@ -0,0 +1,100 @@
+# spec/bundler
+
+spec/bundler is rspec examples for bundler library(lib/bundler.rb, lib/bundler/*).
+
+## Running spec/bundler
+
+To run rspec for bundler:
+```bash
+make test-bundler
+```
+
+# spec/ruby
+
+ruby/spec (https://github.com/ruby/spec/) is
+a test suite for the Ruby language.
+
+Once a month, @eregon merges the in-tree copy under spec/ruby
+with the upstream repository, preserving the commits and history.
+The same happens for other implementations such as JRuby and TruffleRuby.
+
+Feel welcome to modify the in-tree spec/ruby.
+This is the purpose of the in-tree copy,
+to facilitate contributions to ruby/spec for MRI developers.
+
+New features, additional tests for existing features and
+regressions tests are all welcome in ruby/spec.
+There is very little behavior that is implementation-specific,
+as in the end user programs tend to rely on every behavior MRI exhibits.
+In other words: If adding a spec might reveal a bug in
+another implementation, then it is worth adding it.
+Currently, the only module which is MRI-specific is `RubyVM`.
+
+Version guards (`ruby_version_is`) must be added for new features or features
+which change behavior or are removed. See `spec/ruby/CONTRIBUTING.md` for details.
+
+To verify specs are compatible with older Ruby versions:
+```
+cd spec/ruby
+$RUBY_MANAGER use 2.3.7
+../mspec/bin/mspec -j
+```
+
+## Running ruby/spec
+
+To run all specs:
+```bash
+make test-spec
+```
+
+Extra arguments can be added via `MSPECOPT`.
+For instance, to show the help:
+```bash
+make test-spec MSPECOPT=-h
+```
+
+You can also run the specs in parallel, which is currently experimental.
+It takes around 10s instead of 60s on a quad-core laptop.
+```bash
+make test-spec MSPECOPT=-j
+```
+
+To run a specific test, add its path to the command:
+```bash
+make test-spec MSPECOPT=spec/ruby/language/for_spec.rb
+```
+
+If ruby trunk is your current `ruby` in `$PATH`, you can also run `mspec` directly:
+```bash
+# change ruby to trunk
+ruby -v # => trunk
+spec/mspec/bin/mspec spec/ruby/language/for_spec.rb
+```
+
+## ruby/spec and test/
+
+The main difference between a "spec" under spec/ruby and
+a test under test/ is that specs are documenting what they test.
+This is extremely valuable when reading these tests, as it
+helps to quickly understand what specific behavior is tested,
+and how a method should behave. Basic English is fine for spec descriptions.
+Specs also tend to have few expectations (assertions) per spec,
+as they specify one aspect of the behavior and not everything at once.
+Beyond that, the syntax is slightly different but it does the same thing:
+`assert_equal 3, 1+2` is just `(1+2).should == 3`.
+
+Example:
+
+```ruby
+describe "The for expression" do
+ it "iterates over an Enumerable passing each element to the block" do
+ j = 0
+ for i in 1..3
+ j += i
+ end
+ j.should == 6
+ end
+end
+```
+
+For more details, see spec/ruby/CONTRIBUTING.md.
diff --git a/spec/bundler/bundler/bundler_spec.rb b/spec/bundler/bundler/bundler_spec.rb
new file mode 100644
index 0000000000..194d6752b2
--- /dev/null
+++ b/spec/bundler/bundler/bundler_spec.rb
@@ -0,0 +1,490 @@
+# encoding: utf-8
+# frozen_string_literal: true
+
+require "bundler"
+require "tmpdir"
+
+RSpec.describe Bundler do
+ describe "#load_gemspec_uncached" do
+ let(:app_gemspec_path) { tmp("test.gemspec") }
+ subject { Bundler.load_gemspec_uncached(app_gemspec_path) }
+
+ context "with incorrect YAML file" do
+ before do
+ File.open(app_gemspec_path, "wb") do |f|
+ f.write strip_whitespace(<<-GEMSPEC)
+ ---
+ {:!00 ao=gu\g1= 7~f
+ GEMSPEC
+ end
+ end
+
+ it "catches YAML syntax errors" do
+ expect { subject }.to raise_error(Bundler::GemspecError, /error while loading `test.gemspec`/)
+ end
+
+ context "on Rubies with a settable YAML engine", :if => defined?(YAML::ENGINE) do
+ context "with Syck as YAML::Engine" do
+ it "raises a GemspecError after YAML load throws ArgumentError" do
+ orig_yamler = YAML::ENGINE.yamler
+ YAML::ENGINE.yamler = "syck"
+
+ expect { subject }.to raise_error(Bundler::GemspecError)
+
+ YAML::ENGINE.yamler = orig_yamler
+ end
+ end
+
+ context "with Psych as YAML::Engine" do
+ it "raises a GemspecError after YAML load throws Psych::SyntaxError" do
+ orig_yamler = YAML::ENGINE.yamler
+ YAML::ENGINE.yamler = "psych"
+
+ expect { subject }.to raise_error(Bundler::GemspecError)
+
+ YAML::ENGINE.yamler = orig_yamler
+ end
+ end
+ end
+ end
+
+ context "with correct YAML file", :if => defined?(Encoding) do
+ it "can load a gemspec with unicode characters with default ruby encoding" do
+ # spec_helper forces the external encoding to UTF-8 but that's not the
+ # default until Ruby 2.0
+ verbose = $VERBOSE
+ $VERBOSE = false
+ encoding = Encoding.default_external
+ Encoding.default_external = "ASCII"
+ $VERBOSE = verbose
+
+ File.open(app_gemspec_path, "wb") do |file|
+ file.puts <<-GEMSPEC.gsub(/^\s+/, "")
+ # -*- encoding: utf-8 -*-
+ Gem::Specification.new do |gem|
+ gem.author = "André the Giant"
+ end
+ GEMSPEC
+ end
+
+ expect(subject.author).to eq("André the Giant")
+
+ verbose = $VERBOSE
+ $VERBOSE = false
+ Encoding.default_external = encoding
+ $VERBOSE = verbose
+ end
+ end
+
+ it "sets loaded_from" do
+ app_gemspec_path.open("w") do |f|
+ f.puts <<-GEMSPEC
+ Gem::Specification.new do |gem|
+ gem.name = "validated"
+ end
+ GEMSPEC
+ end
+
+ expect(subject.loaded_from).to eq(app_gemspec_path.expand_path.to_s)
+ end
+
+ context "validate is true" do
+ subject { Bundler.load_gemspec_uncached(app_gemspec_path, true) }
+
+ it "validates the specification" do
+ app_gemspec_path.open("w") do |f|
+ f.puts <<-GEMSPEC
+ Gem::Specification.new do |gem|
+ gem.name = "validated"
+ end
+ GEMSPEC
+ end
+ expect(Bundler.rubygems).to receive(:validate).with have_attributes(:name => "validated")
+ subject
+ end
+ end
+
+ context "with gemspec containing local variables" do
+ before do
+ File.open(app_gemspec_path, "wb") do |f|
+ f.write strip_whitespace(<<-GEMSPEC)
+ must_not_leak = true
+ Gem::Specification.new do |gem|
+ gem.name = "leak check"
+ end
+ GEMSPEC
+ end
+ end
+
+ it "should not pollute the TOPLEVEL_BINDING" do
+ subject
+ expect(TOPLEVEL_BINDING.eval("local_variables")).to_not include(:must_not_leak)
+ end
+ end
+ end
+
+ describe "#which" do
+ let(:executable) { "executable" }
+ let(:path) { %w[/a /b c ../d /e] }
+ let(:expected) { "executable" }
+
+ before do
+ ENV["PATH"] = path.join(File::PATH_SEPARATOR)
+
+ allow(File).to receive(:file?).and_return(false)
+ allow(File).to receive(:executable?).and_return(false)
+ if expected
+ expect(File).to receive(:file?).with(expected).and_return(true)
+ expect(File).to receive(:executable?).with(expected).and_return(true)
+ end
+ end
+
+ subject { described_class.which(executable) }
+
+ shared_examples_for "it returns the correct executable" do
+ it "returns the expected file" do
+ expect(subject).to eq(expected)
+ end
+ end
+
+ it_behaves_like "it returns the correct executable"
+
+ context "when the executable in inside a quoted path" do
+ let(:expected) { "/e/executable" }
+ it_behaves_like "it returns the correct executable"
+ end
+
+ context "when the executable is not found" do
+ let(:expected) { nil }
+ it_behaves_like "it returns the correct executable"
+ end
+ end
+
+ describe "configuration" do
+ context "disable_shared_gems" do
+ it "should unset GEM_PATH with empty string" do
+ env = {}
+ expect(Bundler).to receive(:use_system_gems?).and_return(false)
+ Bundler.send(:configure_gem_path, env)
+ expect(env.keys).to include("GEM_PATH")
+ expect(env["GEM_PATH"]).to eq ""
+ end
+ end
+ end
+
+ describe "#rm_rf" do
+ context "the directory is world writable" do
+ let(:bundler_ui) { Bundler.ui }
+ it "should raise a friendly error" do
+ allow(File).to receive(:exist?).and_return(true)
+ 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.
+You should probably consider fixing this issue by running `chmod o-w ~` on *nix.
+Please refer to http://ruby-doc.org/stdlib-2.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)
+ end
+ end
+ end
+
+ describe "#mkdir_p" do
+ it "creates a folder at the given path" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ 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
+ context "home directory is set" do
+ it "should return the user home" do
+ path = "/home/oggy"
+ allow(Bundler.rubygems).to receive(:user_home).and_return(path)
+ allow(File).to receive(:directory?).with(path).and_return true
+ allow(File).to receive(:writable?).with(path).and_return true
+ expect(Bundler.user_home).to eq(Pathname(path))
+ end
+
+ context "is not a directory" do
+ it "should issue a warning and return a temporary user home" do
+ path = "/home/oggy"
+ allow(Bundler.rubygems).to receive(:user_home).and_return(path)
+ allow(File).to receive(:directory?).with(path).and_return false
+ allow(Etc).to receive(:getlogin).and_return("USER")
+ allow(Dir).to receive(:tmpdir).and_return("/TMP")
+ allow(FileTest).to receive(:exist?).with("/TMP/bundler/home").and_return(true)
+ expect(FileUtils).to receive(:mkpath).with("/TMP/bundler/home/USER")
+ message = <<EOF
+`/home/oggy` is not a directory.
+Bundler will use `/TMP/bundler/home/USER' as your home directory temporarily.
+EOF
+ expect(Bundler.ui).to receive(:warn).with(message)
+ expect(Bundler.user_home).to eq(Pathname("/TMP/bundler/home/USER"))
+ end
+ end
+
+ context "is not writable" do
+ let(:path) { "/home/oggy" }
+ let(:dotbundle) { "/home/oggy/.bundle" }
+
+ it "should issue a warning and return a temporary user home" do
+ allow(Bundler.rubygems).to receive(:user_home).and_return(path)
+ allow(File).to receive(:directory?).with(path).and_return true
+ allow(File).to receive(:writable?).with(path).and_return false
+ allow(File).to receive(:directory?).with(dotbundle).and_return false
+ allow(Etc).to receive(:getlogin).and_return("USER")
+ allow(Dir).to receive(:tmpdir).and_return("/TMP")
+ allow(FileTest).to receive(:exist?).with("/TMP/bundler/home").and_return(true)
+ expect(FileUtils).to receive(:mkpath).with("/TMP/bundler/home/USER")
+ message = <<EOF
+`/home/oggy` is not writable.
+Bundler will use `/TMP/bundler/home/USER' as your home directory temporarily.
+EOF
+ expect(Bundler.ui).to receive(:warn).with(message)
+ expect(Bundler.user_home).to eq(Pathname("/TMP/bundler/home/USER"))
+ end
+
+ context ".bundle exists and have correct permissions" do
+ it "should return the user home" do
+ allow(Bundler.rubygems).to receive(:user_home).and_return(path)
+ allow(File).to receive(:directory?).with(path).and_return true
+ allow(File).to receive(:writable?).with(path).and_return false
+ allow(File).to receive(:directory?).with(dotbundle).and_return true
+ allow(File).to receive(:writable?).with(dotbundle).and_return true
+ expect(Bundler.user_home).to eq(Pathname(path))
+ end
+ end
+ end
+ end
+
+ context "home directory is not set" do
+ it "should issue warning and return a temporary user home" do
+ allow(Bundler.rubygems).to receive(:user_home).and_return(nil)
+ allow(Etc).to receive(:getlogin).and_return("USER")
+ allow(Dir).to receive(:tmpdir).and_return("/TMP")
+ allow(FileTest).to receive(:exist?).with("/TMP/bundler/home").and_return(true)
+ expect(FileUtils).to receive(:mkpath).with("/TMP/bundler/home/USER")
+ message = <<EOF
+Your home directory is not set.
+Bundler will use `/TMP/bundler/home/USER' as your home directory temporarily.
+EOF
+ expect(Bundler.ui).to receive(:warn).with(message)
+ expect(Bundler.user_home).to eq(Pathname("/TMP/bundler/home/USER"))
+ end
+ end
+ end
+
+ describe "#tmp_home_path" do
+ it "should create temporary user home" do
+ allow(Dir).to receive(:tmpdir).and_return("/TMP")
+ allow(FileTest).to receive(:exist?).with("/TMP/bundler/home").and_return(false)
+ expect(FileUtils).to receive(:mkpath).once.ordered.with("/TMP/bundler/home")
+ expect(FileUtils).to receive(:mkpath).once.ordered.with("/TMP/bundler/home/USER")
+ expect(File).to receive(:chmod).with(0o777, "/TMP/bundler/home")
+ expect(Bundler.tmp_home_path("USER", "")).to eq(Pathname("/TMP/bundler/home/USER"))
+ end
+ end
+
+ describe "#requires_sudo?" do
+ let!(:tmpdir) { Dir.mktmpdir }
+ let(:bundle_path) { Pathname("#{tmpdir}/bundle") }
+
+ def clear_cached_requires_sudo
+ # Private in ruby 1.8.7
+ return unless Bundler.instance_variable_defined?(:@requires_sudo_ran)
+ Bundler.send(:remove_instance_variable, :@requires_sudo_ran)
+ Bundler.send(: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/bundler/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
+ end
+
+ describe "#requires_sudo?" do
+ before do
+ allow(Bundler).to receive(:which).with("sudo").and_return("/usr/bin/sudo")
+ 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")
+ if Bundler.respond_to?(:remove_instance_variable)
+ Bundler.remove_instance_variable(:@requires_sudo_ran)
+ Bundler.remove_instance_variable(:@requires_sudo)
+ else
+ # TODO: Remove these code when Bundler drops Ruby 1.8.7 support
+ Bundler.send(:remove_instance_variable, :@requires_sudo_ran)
+ Bundler.send(:remove_instance_variable, :@requires_sudo)
+ end
+ 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
+
+ context "user cache dir" do
+ let(:home_path) { Pathname.new(ENV["HOME"]) }
+
+ let(:xdg_data_home) { home_path.join(".local") }
+ let(:xdg_cache_home) { home_path.join(".cache") }
+ let(:xdg_config_home) { home_path.join(".config") }
+
+ let(:bundle_user_home_default) { home_path.join(".bundle") }
+ let(:bundle_user_home_custom) { xdg_data_home.join("bundle") }
+
+ let(:bundle_user_cache_default) { bundle_user_home_default.join("cache") }
+ let(:bundle_user_cache_custom) { xdg_cache_home.join("bundle") }
+
+ let(:bundle_user_config_default) { bundle_user_home_default.join("config") }
+ let(:bundle_user_config_custom) { xdg_config_home.join("bundle") }
+
+ let(:bundle_user_plugin_default) { bundle_user_home_default.join("plugin") }
+ let(:bundle_user_plugin_custom) { xdg_data_home.join("bundle").join("plugin") }
+
+ describe "#user_bundle_path" do
+ before do
+ allow(Bundler.rubygems).to receive(:user_home).and_return(home_path)
+ end
+
+ it "should use the default home path" do
+ expect(Bundler.user_bundle_path).to eq(bundle_user_home_default)
+ expect(Bundler.user_bundle_path("home")).to eq(bundle_user_home_default)
+ expect(Bundler.user_bundle_path("cache")).to eq(bundle_user_cache_default)
+ expect(Bundler.user_cache).to eq(bundle_user_cache_default)
+ expect(Bundler.user_bundle_path("config")).to eq(bundle_user_config_default)
+ expect(Bundler.user_bundle_path("plugin")).to eq(bundle_user_plugin_default)
+ end
+
+ it "should use custom home path as root for other paths" do
+ ENV["BUNDLE_USER_HOME"] = bundle_user_home_custom.to_s
+ expect(Bundler.user_bundle_path).to eq(bundle_user_home_custom)
+ expect(Bundler.user_bundle_path("home")).to eq(bundle_user_home_custom)
+ expect(Bundler.user_bundle_path("cache")).to eq(bundle_user_home_custom.join("cache"))
+ expect(Bundler.user_cache).to eq(bundle_user_home_custom.join("cache"))
+ expect(Bundler.user_bundle_path("config")).to eq(bundle_user_home_custom.join("config"))
+ expect(Bundler.user_bundle_path("plugin")).to eq(bundle_user_home_custom.join("plugin"))
+ end
+
+ it "should use all custom paths, except home" do
+ ENV.delete("BUNDLE_USER_HOME")
+ ENV["BUNDLE_USER_CACHE"] = bundle_user_cache_custom.to_s
+ ENV["BUNDLE_USER_CONFIG"] = bundle_user_config_custom.to_s
+ ENV["BUNDLE_USER_PLUGIN"] = bundle_user_plugin_custom.to_s
+ expect(Bundler.user_bundle_path).to eq(bundle_user_home_default)
+ expect(Bundler.user_bundle_path("home")).to eq(bundle_user_home_default)
+ expect(Bundler.user_bundle_path("cache")).to eq(bundle_user_cache_custom)
+ expect(Bundler.user_cache).to eq(bundle_user_cache_custom)
+ expect(Bundler.user_bundle_path("config")).to eq(bundle_user_config_custom)
+ expect(Bundler.user_bundle_path("plugin")).to eq(bundle_user_plugin_custom)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/cli_spec.rb b/spec/bundler/bundler/cli_spec.rb
new file mode 100644
index 0000000000..c82d46587e
--- /dev/null
+++ b/spec/bundler/bundler/cli_spec.rb
@@ -0,0 +1,173 @@
+# frozen_string_literal: true
+
+require "bundler/cli"
+
+RSpec.describe "bundle executable" do
+ it "returns non-zero exit status when passed unrecognized options" do
+ bundle "--invalid_argument"
+ expect(exitstatus).to_not be_zero if exitstatus
+ end
+
+ it "returns non-zero exit status when passed unrecognized task" do
+ bundle "unrecognized-task"
+ expect(exitstatus).to_not be_zero if exitstatus
+ end
+
+ it "looks for a binary and executes it if it's named bundler-<task>" do
+ File.open(tmp("bundler-testtasks"), "w", 0o755) do |f|
+ ruby = ENV["BUNDLE_RUBY"] || "/usr/bin/env ruby"
+ f.puts "#!#{ruby}\nputs 'Hello, world'\n"
+ end
+
+ with_path_added(tmp) do
+ bundle "testtasks"
+ end
+
+ expect(exitstatus).to be_zero if exitstatus
+ expect(out).to eq("Hello, world")
+ end
+
+ context "with no arguments" do
+ it "prints a concise help message", :bundler => "2" do
+ bundle! ""
+ expect(last_command.stderr).to be_empty
+ expect(last_command.stdout).to include("Bundler version #{Bundler::VERSION}").
+ and include("\n\nBundler commands:\n\n").
+ and include("\n\n Primary commands:\n").
+ and include("\n\n Utilities:\n").
+ and include("\n\nOptions:\n")
+ end
+ end
+
+ context "when ENV['BUNDLE_GEMFILE'] is set to an empty string" do
+ it "ignores it" do
+ gemfile bundled_app("Gemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle :install, :env => { "BUNDLE_GEMFILE" => "" }
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ context "when ENV['RUBYGEMS_GEMDEPS'] is set" do
+ it "displays a warning" do
+ gemfile bundled_app("Gemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle :install, :env => { "RUBYGEMS_GEMDEPS" => "foo" }
+ expect(out).to include("RUBYGEMS_GEMDEPS")
+ expect(out).to include("conflict with Bundler")
+
+ bundle :install, :env => { "RUBYGEMS_GEMDEPS" => "" }
+ expect(out).not_to include("RUBYGEMS_GEMDEPS")
+ end
+ end
+
+ context "with --verbose" do
+ it "prints the running command" do
+ gemfile ""
+ bundle! "info bundler", :verbose => true
+ expect(last_command.stdout).to start_with("Running `bundle info bundler --verbose` with bundler #{Bundler::VERSION}")
+ end
+
+ it "doesn't print defaults" do
+ install_gemfile! "", :verbose => true
+ expect(last_command.stdout).to start_with("Running `bundle install --retry 0 --verbose` with bundler #{Bundler::VERSION}")
+ end
+
+ it "doesn't print defaults" do
+ install_gemfile! "", :verbose => true
+ expect(last_command.stdout).to start_with("Running `bundle install --retry 0 --verbose` with bundler #{Bundler::VERSION}")
+ end
+ end
+
+ describe "printing the outdated warning" do
+ shared_examples_for "no warning" do
+ it "prints no warning" do
+ bundle "fail"
+ expect(last_command.stdboth).to eq("Could not find command \"fail\".")
+ end
+ end
+
+ let(:bundler_version) { "1.1" }
+ let(:latest_version) { nil }
+ before do
+ bundle! "config --global disable_version_check false"
+
+ simulate_bundler_version(bundler_version)
+ if latest_version
+ info_path = home(".bundle/cache/compact_index/rubygems.org.443.29b0360b937aa4d161703e6160654e47/info/bundler")
+ info_path.parent.mkpath
+ info_path.open("w") {|f| f.write "#{latest_version}\n" }
+ end
+ end
+
+ context "when there is no latest version" do
+ include_examples "no warning"
+ end
+
+ context "when the latest version is equal to the current version" do
+ let(:latest_version) { bundler_version }
+ include_examples "no warning"
+ end
+
+ context "when the latest version is less than the current version" do
+ let(:latest_version) { "0.9" }
+ include_examples "no warning"
+ end
+
+ context "when the latest version is greater than the current version" do
+ let(:latest_version) { "222.0" }
+ it "prints the version warning" do
+ bundle "fail"
+ expect(last_command.stdout).to start_with(<<-EOS.strip)
+The latest bundler is #{latest_version}, but you are currently running #{bundler_version}.
+To install the latest version, run `gem install bundler`
+ EOS
+ end
+
+ context "and disable_version_check is set" do
+ before { bundle! "config disable_version_check true" }
+ include_examples "no warning"
+ end
+
+ context "running a parseable command" do
+ it "prints no warning" do
+ bundle! "config --parseable foo"
+ expect(last_command.stdboth).to eq ""
+
+ bundle "platform --ruby"
+ expect(last_command.stdboth).to eq "Could not locate Gemfile"
+ end
+ end
+
+ context "and is a pre-release" do
+ let(:latest_version) { "222.0.0.pre.4" }
+ it "prints the version warning" do
+ bundle "fail"
+ expect(last_command.stdout).to start_with(<<-EOS.strip)
+The latest bundler is #{latest_version}, but you are currently running #{bundler_version}.
+To install the latest version, run `gem install bundler --pre`
+ EOS
+ end
+ end
+ end
+ end
+end
+
+RSpec.describe "bundler executable" do
+ it "shows the bundler version just as the `bundle` executable does", :bundler => "< 2" do
+ bundler "--version"
+ expect(out).to eq("Bundler version #{Bundler::VERSION}")
+ end
+
+ it "shows the bundler version just as the `bundle` executable does", :bundler => "2" do
+ bundler "--version"
+ expect(out).to eq(Bundler::VERSION)
+ end
+end
diff --git a/spec/bundler/bundler/compact_index_client/updater_spec.rb b/spec/bundler/bundler/compact_index_client/updater_spec.rb
new file mode 100644
index 0000000000..fd554a7b0d
--- /dev/null
+++ b/spec/bundler/bundler/compact_index_client/updater_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require "net/http"
+require "bundler/compact_index_client"
+require "bundler/compact_index_client/updater"
+
+RSpec.describe Bundler::CompactIndexClient::Updater do
+ let(:fetcher) { double(:fetcher) }
+ let(:local_path) { Pathname("/tmp/localpath") }
+ let(:remote_path) { double(:remote_path) }
+
+ subject(:updater) { described_class.new(fetcher) }
+
+ context "when the ETag header is missing" do
+ # Regression test for https://github.com/bundler/bundler/issues/5463
+
+ let(:response) { double(:response, :body => "") }
+
+ it "MisMatchedChecksumError is raised" do
+ # Twice: #update retries on failure
+ expect(response).to receive(:[]).with("Content-Encoding").twice { "" }
+ expect(response).to receive(:[]).with("ETag").twice { nil }
+ expect(fetcher).to receive(:call).twice { response }
+
+ expect do
+ updater.update(local_path, remote_path)
+ end.to raise_error(Bundler::CompactIndexClient::Updater::MisMatchedChecksumError)
+ end
+ end
+
+ context "when the download is corrupt" do
+ let(:response) { double(:response, :body => "") }
+
+ it "raises HTTPError" do
+ expect(response).to receive(:[]).with("Content-Encoding") { "gzip" }
+ expect(fetcher).to receive(:call) { response }
+
+ expect do
+ updater.update(local_path, remote_path)
+ end.to raise_error(Bundler::HTTPError)
+ end
+ end
+
+ context "when bundler doesn't have permissions on Dir.tmpdir" do
+ let(:response) { double(:response, :body => "") }
+
+ it "Errno::EACCES is raised" do
+ allow(Dir).to receive(:mktmpdir) { raise Errno::EACCES }
+
+ expect do
+ updater.update(local_path, remote_path)
+ end.to raise_error(Bundler::PermissionError)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/definition_spec.rb b/spec/bundler/bundler/definition_spec.rb
new file mode 100644
index 0000000000..ceb7b4bf05
--- /dev/null
+++ b/spec/bundler/bundler/definition_spec.rb
@@ -0,0 +1,360 @@
+# frozen_string_literal: true
+
+require "bundler/definition"
+
+RSpec.describe Bundler::Definition do
+ describe "#lock" do
+ before do
+ allow(Bundler).to receive(:settings) { Bundler::Settings.new(".") }
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile) { Pathname.new("Gemfile") }
+ allow(Bundler).to receive(:ui) { double("UI", :info => "", :debug => "") }
+ end
+ context "when it's not possible to write to the file" do
+ subject { Bundler::Definition.new(nil, [], Bundler::SourceList.new, []) }
+
+ it "raises an PermissionError with explanation" do
+ allow(File).to receive(:open).and_call_original
+ expect(File).to receive(:open).with("Gemfile.lock", "wb").
+ and_raise(Errno::EACCES)
+ expect { subject.lock("Gemfile.lock") }.
+ to raise_error(Bundler::PermissionError, /Gemfile\.lock/)
+ end
+ end
+ context "when a temporary resource access issue occurs" do
+ subject { Bundler::Definition.new(nil, [], Bundler::SourceList.new, []) }
+
+ it "raises a TemporaryResourceError with explanation" do
+ allow(File).to receive(:open).and_call_original
+ expect(File).to receive(:open).with("Gemfile.lock", "wb").
+ and_raise(Errno::EAGAIN)
+ expect { subject.lock("Gemfile.lock") }.
+ to raise_error(Bundler::TemporaryResourceError, /temporarily unavailable/)
+ end
+ end
+ end
+
+ describe "detects changes" do
+ it "for a path gem with changes", :bundler => "< 2" do
+ build_lib "foo", "1.0", :path => lib_path("foo")
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", "1.0"
+ end
+
+ bundle :install, :env => { "DEBUG" => 1 }
+
+ expect(out).to match(/re-resolving dependencies/)
+ lockfile_should_be <<-G
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+ rack (= 1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "for a path gem with changes", :bundler => "2" do
+ build_lib "foo", "1.0", :path => lib_path("foo")
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", "1.0"
+ end
+
+ bundle :install, :env => { "DEBUG" => 1 }
+
+ expect(out).to match(/re-resolving dependencies/)
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+ rack (= 1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "for a path gem with deps and no changes", :bundler => "< 2" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", "1.0"
+ s.add_development_dependency "net-ssh", "1.0"
+ end
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ bundle :check, :env => { "DEBUG" => 1 }
+
+ expect(out).to match(/using resolution from the lockfile/)
+ lockfile_should_be <<-G
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+ rack (= 1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "for a path gem with deps and no changes", :bundler => "2" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", "1.0"
+ s.add_development_dependency "net-ssh", "1.0"
+ end
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ bundle :check, :env => { "DEBUG" => 1 }
+
+ expect(out).to match(/using resolution from the lockfile/)
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+ rack (= 1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "for a rubygems gem" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "foo"
+ G
+
+ bundle :check, :env => { "DEBUG" => 1 }
+
+ expect(out).to match(/using resolution from the lockfile/)
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+ end
+
+ describe "initialize" do
+ context "gem version promoter" do
+ context "with lockfile" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo"
+ G
+ end
+
+ it "should get a locked specs list when updating all" do
+ definition = Bundler::Definition.new(bundled_app("Gemfile.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|
+ source_list.global_rubygems_source = "file://#{gem_repo4}"
+ end
+ end
+
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'isolated_owner'
+
+ gem 'shared_owner_a'
+ gem 'shared_owner_b'
+ G
+
+ lockfile <<-L
+ GEM
+ remote: file://#{gem_repo4}
+ specs:
+ isolated_dep (2.0.1)
+ isolated_owner (1.0.1)
+ isolated_dep (~> 2.0)
+ shared_dep (5.0.1)
+ shared_owner_a (3.0.1)
+ shared_dep (~> 5.0)
+ shared_owner_b (4.0.1)
+ shared_dep (~> 5.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ shared_owner_a
+ shared_owner_b
+ isolated_owner
+
+ BUNDLED WITH
+ 1.13.0
+ L
+ end
+
+ it "should not eagerly unlock shared dependency with bundle install conservative updating behavior" do
+ updated_deps_in_gemfile = [Bundler::Dependency.new("isolated_owner", ">= 0"),
+ Bundler::Dependency.new("shared_owner_a", "3.0.2"),
+ Bundler::Dependency.new("shared_owner_b", ">= 0")]
+ unlock_hash_for_bundle_install = {}
+ definition = Bundler::Definition.new(
+ bundled_app("Gemfile.lock"),
+ updated_deps_in_gemfile,
+ source_list,
+ unlock_hash_for_bundle_install
+ )
+ locked = definition.send(:converge_locked_specs).map(&:name)
+ expect(locked).to include "shared_dep"
+ end
+
+ it "should not eagerly unlock shared dependency with bundle update conservative updating behavior" do
+ updated_deps_in_gemfile = [Bundler::Dependency.new("isolated_owner", ">= 0"),
+ Bundler::Dependency.new("shared_owner_a", ">= 0"),
+ Bundler::Dependency.new("shared_owner_b", ">= 0")]
+ definition = Bundler::Definition.new(
+ bundled_app("Gemfile.lock"),
+ updated_deps_in_gemfile,
+ source_list,
+ :gems => ["shared_owner_a"], :lock_shared_dependencies => true
+ )
+ locked = definition.send(:converge_locked_specs).map(&:name)
+ expect(locked).to eq %w[isolated_dep isolated_owner shared_dep shared_owner_b]
+ expect(locked.include?("shared_dep")).to be_truthy
+ end
+ end
+ end
+ end
+
+ describe "find_resolved_spec" do
+ it "with no platform set in SpecSet" do
+ ss = Bundler::SpecSet.new([build_stub_spec("a", "1.0"), build_stub_spec("b", "1.0")])
+ dfn = Bundler::Definition.new(nil, [], mock_source_list, true)
+ dfn.instance_variable_set("@specs", ss)
+ found = dfn.find_resolved_spec(build_spec("a", "0.9", "ruby").first)
+ expect(found.name).to eq "a"
+ expect(found.version.to_s).to eq "1.0"
+ end
+ end
+
+ describe "find_indexed_specs" do
+ it "with no platform set in indexed specs" do
+ index = Bundler::Index.new
+ %w[1.0.0 1.0.1 1.1.0].each {|v| index << build_stub_spec("foo", v) }
+
+ dfn = Bundler::Definition.new(nil, [], mock_source_list, true)
+ dfn.instance_variable_set("@index", index)
+ found = dfn.find_indexed_specs(build_spec("foo", "0.9", "ruby").first)
+ expect(found.length).to eq 3
+ end
+ end
+
+ def build_stub_spec(name, version)
+ Bundler::StubSpecification.new(name, version, nil, nil)
+ end
+
+ def mock_source_list
+ Class.new do
+ def all_sources
+ []
+ end
+
+ def path_sources
+ []
+ end
+
+ def rubygems_remotes
+ []
+ end
+
+ def replace_sources!(arg)
+ nil
+ end
+ end.new
+ end
+end
diff --git a/spec/bundler/bundler/dep_proxy_spec.rb b/spec/bundler/bundler/dep_proxy_spec.rb
new file mode 100644
index 0000000000..0f8d6b1076
--- /dev/null
+++ b/spec/bundler/bundler/dep_proxy_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::DepProxy do
+ let(:dep) { Bundler::Dependency.new("rake", ">= 0") }
+ subject { described_class.new(dep, Gem::Platform::RUBY) }
+ let(:same) { subject }
+ let(:other) { subject.dup }
+ let(:different) { described_class.new(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 "#hash" do
+ it { expect(subject.hash).to eq(same.hash) }
+ it { expect(subject.hash).to eq(other.hash) }
+ end
+end
diff --git a/spec/bundler/bundler/dsl_spec.rb b/spec/bundler/bundler/dsl_spec.rb
new file mode 100644
index 0000000000..89528eb745
--- /dev/null
+++ b/spec/bundler/bundler/dsl_spec.rb
@@ -0,0 +1,305 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Dsl do
+ before do
+ @rubygems = double("rubygems")
+ allow(Bundler::Source::Rubygems).to receive(:new) { @rubygems }
+ end
+
+ describe "#git_source" do
+ it "registers custom hosts" do
+ subject.git_source(:example) {|repo_name| "git@git.example.com:#{repo_name}.git" }
+ subject.git_source(:foobar) {|repo_name| "git@foobar.com:#{repo_name}.git" }
+ subject.gem("dobry-pies", :example => "strzalek/dobry-pies")
+ example_uri = "git@git.example.com:strzalek/dobry-pies.git"
+ expect(subject.dependencies.first.source.uri).to eq(example_uri)
+ end
+
+ it "raises exception on invalid hostname" do
+ expect do
+ subject.git_source(:group) {|repo_name| "git@git.example.com:#{repo_name}.git" }
+ end.to raise_error(Bundler::InvalidOption)
+ end
+
+ it "expects block passed" do
+ expect { subject.git_source(:example) }.to raise_error(Bundler::InvalidOption)
+ end
+
+ context "github_https feature flag" do
+ it "is true when github.https is true" do
+ bundle "config github.https true"
+ expect(Bundler.feature_flag.github_https?).to eq "true"
+ end
+ end
+
+ context "default hosts (git, gist)", :bundler => "< 2" do
+ context "when github.https config is true" do
+ before { bundle "config github.https true" }
+ it "converts :github to :git using https" do
+ subject.gem("sparks", :github => "indirect/sparks")
+ github_uri = "https://github.com/indirect/sparks.git"
+ expect(subject.dependencies.first.source.uri).to eq(github_uri)
+ end
+ end
+
+ it "converts :github to :git" do
+ subject.gem("sparks", :github => "indirect/sparks")
+ github_uri = "git://github.com/indirect/sparks.git"
+ expect(subject.dependencies.first.source.uri).to eq(github_uri)
+ end
+
+ it "converts numeric :gist to :git" do
+ subject.gem("not-really-a-gem", :gist => 2_859_988)
+ github_uri = "https://gist.github.com/2859988.git"
+ expect(subject.dependencies.first.source.uri).to eq(github_uri)
+ end
+
+ it "converts :gist to :git" do
+ subject.gem("not-really-a-gem", :gist => "2859988")
+ github_uri = "https://gist.github.com/2859988.git"
+ expect(subject.dependencies.first.source.uri).to eq(github_uri)
+ end
+
+ it "converts 'rails' to 'rails/rails'" do
+ subject.gem("rails", :github => "rails")
+ github_uri = "git://github.com/rails/rails.git"
+ expect(subject.dependencies.first.source.uri).to eq(github_uri)
+ end
+
+ it "converts :bitbucket to :git" do
+ subject.gem("not-really-a-gem", :bitbucket => "mcorp/flatlab-rails")
+ bitbucket_uri = "https://mcorp@bitbucket.org/mcorp/flatlab-rails.git"
+ expect(subject.dependencies.first.source.uri).to eq(bitbucket_uri)
+ end
+
+ it "converts 'mcorp' to 'mcorp/mcorp'" do
+ subject.gem("not-really-a-gem", :bitbucket => "mcorp")
+ bitbucket_uri = "https://mcorp@bitbucket.org/mcorp/mcorp.git"
+ expect(subject.dependencies.first.source.uri).to eq(bitbucket_uri)
+ end
+ end
+
+ context "default git sources", :bundler => "2" do
+ it "has none" do
+ expect(subject.instance_variable_get(:@git_sources)).to eq({})
+ end
+ end
+ end
+
+ describe "#method_missing" do
+ it "raises an error for unknown DSL methods" do
+ expect(Bundler).to receive(:read_file).with(bundled_app("Gemfile").to_s).
+ and_return("unknown")
+
+ error_msg = "There was an error parsing `Gemfile`: Undefined local variable or method `unknown' for Gemfile. Bundler cannot continue."
+ expect { subject.eval_gemfile("Gemfile") }.
+ to raise_error(Bundler::GemfileError, Regexp.new(error_msg))
+ end
+ end
+
+ describe "#eval_gemfile" do
+ it "handles syntax errors with a useful message" do
+ expect(Bundler).to receive(:read_file).with(bundled_app("Gemfile").to_s).and_return("}")
+ expect { subject.eval_gemfile("Gemfile") }.
+ to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`: (syntax error, unexpected tSTRING_DEND|(compile error - )?syntax error, unexpected '\}'). Bundler cannot continue./)
+ end
+
+ it "distinguishes syntax errors from evaluation errors" do
+ expect(Bundler).to receive(:read_file).with(bundled_app("Gemfile").to_s).and_return(
+ "ruby '2.1.5', :engine => 'ruby', :engine_version => '1.2.4'"
+ )
+ expect { subject.eval_gemfile("Gemfile") }.
+ to raise_error(Bundler::GemfileError, /There was an error evaluating `Gemfile`: ruby_version must match the :engine_version for MRI/)
+ end
+ end
+
+ describe "#gem" do
+ [:ruby, :ruby_18, :ruby_19, :ruby_20, :ruby_21, :ruby_22, :ruby_23, :ruby_24, :ruby_25, :mri, :mri_18, :mri_19,
+ :mri_20, :mri_21, :mri_22, :mri_23, :mri_24, :mri_25, :jruby, :rbx, :truffleruby].each do |platform|
+ it "allows #{platform} as a valid platform" do
+ subject.gem("foo", :platform => platform)
+ end
+ end
+
+ it "rejects invalid platforms" do
+ expect { subject.gem("foo", :platform => :bogus) }.
+ to raise_error(Bundler::GemfileError, /is not a valid platform/)
+ end
+
+ it "rejects empty gem name" do
+ expect { subject.gem("") }.
+ to raise_error(Bundler::GemfileError, /an empty gem name is not valid/)
+ end
+
+ it "rejects with a leading space in the name" do
+ expect { subject.gem(" foo") }.
+ to raise_error(Bundler::GemfileError, /' foo' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a trailing space in the name" do
+ expect { subject.gem("foo ") }.
+ to raise_error(Bundler::GemfileError, /'foo ' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a space in the gem name" do
+ expect { subject.gem("fo o") }.
+ to raise_error(Bundler::GemfileError, /'fo o' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a tab in the gem name" do
+ expect { subject.gem("fo\to") }.
+ to raise_error(Bundler::GemfileError, /'fo\to' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a newline in the gem name" do
+ expect { subject.gem("fo\no") }.
+ to raise_error(Bundler::GemfileError, /'fo\no' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a carriage return in the gem name" do
+ expect { subject.gem("fo\ro") }.
+ to raise_error(Bundler::GemfileError, /'fo\ro' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a form feed in the gem name" do
+ expect { subject.gem("fo\fo") }.
+ to raise_error(Bundler::GemfileError, /'fo\fo' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects symbols as gem name" do
+ expect { subject.gem(:foo) }.
+ to raise_error(Bundler::GemfileError, /You need to specify gem names as Strings. Use 'gem "foo"' instead/)
+ end
+
+ it "rejects branch option on non-git gems" do
+ expect { subject.gem("foo", :branch => "test") }.
+ to raise_error(Bundler::GemfileError, /The `branch` option for `gem 'foo'` is not allowed. Only gems with a git source can specify a branch/)
+ end
+
+ it "allows specifying a branch on git gems" do
+ subject.gem("foo", :branch => "test", :git => "http://mytestrepo")
+ dep = subject.dependencies.last
+ expect(dep.name).to eq "foo"
+ end
+
+ it "allows specifying a branch on git gems with a git_source" do
+ subject.git_source(:test_source) {|n| "https://github.com/#{n}" }
+ subject.gem("foo", :branch => "test", :test_source => "bundler/bundler")
+ dep = subject.dependencies.last
+ expect(dep.name).to eq "foo"
+ end
+ end
+
+ describe "#gemspec" do
+ let(:spec) do
+ Gem::Specification.new do |gem|
+ gem.name = "example"
+ gem.platform = platform
+ end
+ end
+
+ before do
+ allow(Dir).to receive(:[]).and_return(["spec_path"])
+ allow(Bundler).to receive(:load_gemspec).with("spec_path").and_return(spec)
+ allow(Bundler).to receive(:default_gemfile).and_return(Pathname.new("./Gemfile"))
+ end
+
+ context "with a ruby platform" do
+ let(:platform) { "ruby" }
+
+ it "keeps track of the ruby platforms in the dependency" do
+ subject.gemspec
+ expect(subject.dependencies.last.platforms).to eq(Bundler::Dependency::REVERSE_PLATFORM_MAP[Gem::Platform::RUBY])
+ end
+ end
+
+ context "with a jruby platform" do
+ let(:platform) { "java" }
+
+ it "keeps track of the jruby platforms in the dependency" do
+ allow(Gem::Platform).to receive(:local).and_return(java)
+ subject.gemspec
+ expect(subject.dependencies.last.platforms).to eq(Bundler::Dependency::REVERSE_PLATFORM_MAP[Gem::Platform::JAVA])
+ end
+ end
+ end
+
+ context "can bundle groups of gems with" do
+ # git "https://github.com/rails/rails.git" do
+ # gem "railties"
+ # gem "action_pack"
+ # gem "active_model"
+ # end
+ describe "#git" do
+ it "from a single repo" do
+ rails_gems = %w[railties action_pack active_model]
+ subject.git "https://github.com/rails/rails.git" do
+ rails_gems.each {|rails_gem| subject.send :gem, rails_gem }
+ end
+ expect(subject.dependencies.map(&:name)).to match_array rails_gems
+ end
+ end
+
+ # github 'spree' do
+ # gem 'spree_core'
+ # gem 'spree_api'
+ # gem 'spree_backend'
+ # end
+ describe "#github", :bundler => "< 2" do
+ it "from github" do
+ spree_gems = %w[spree_core spree_api spree_backend]
+ subject.github "spree" do
+ spree_gems.each {|spree_gem| subject.send :gem, spree_gem }
+ end
+
+ subject.dependencies.each do |d|
+ expect(d.source.uri).to eq("git://github.com/spree/spree.git")
+ end
+ end
+ end
+
+ describe "#github", :bundler => "2" do
+ it "from github" do
+ expect do
+ spree_gems = %w[spree_core spree_api spree_backend]
+ subject.github "spree" do
+ spree_gems.each {|spree_gem| subject.send :gem, spree_gem }
+ end
+ end.to raise_error(Bundler::DeprecatedError, /github method has been removed/)
+ end
+ end
+ end
+
+ describe "syntax errors" do
+ it "will raise a Bundler::GemfileError" do
+ gemfile "gem 'foo', :path => /unquoted/string/syntax/error"
+ expect { Bundler::Dsl.evaluate(bundled_app("Gemfile"), nil, true) }.
+ to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`:( compile error -)? unknown regexp options - trg. Bundler cannot continue./)
+ end
+ end
+
+ describe "Runtime errors", :unless => Bundler.current_ruby.on_18? do
+ it "will raise a Bundler::GemfileError" do
+ gemfile "s = 'foo'.freeze; s.strip!"
+ expect { Bundler::Dsl.evaluate(bundled_app("Gemfile"), nil, true) }.
+ to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`: can't modify frozen String. Bundler cannot continue./i)
+ end
+ end
+
+ describe "#with_source" do
+ context "if there was a rubygem source already defined" do
+ it "restores it after it's done" do
+ other_source = double("other-source")
+ allow(Bundler::Source::Rubygems).to receive(:new).and_return(other_source)
+ allow(Bundler).to receive(:default_gemfile).and_return(Pathname.new("./Gemfile"))
+
+ subject.source("https://other-source.org") do
+ subject.gem("dobry-pies", :path => "foo")
+ subject.gem("foo")
+ end
+
+ expect(subject.dependencies.last.source).to eq(other_source)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/endpoint_specification_spec.rb b/spec/bundler/bundler/endpoint_specification_spec.rb
new file mode 100644
index 0000000000..a9371f6617
--- /dev/null
+++ b/spec/bundler/bundler/endpoint_specification_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::EndpointSpecification do
+ let(:name) { "foo" }
+ let(:version) { "1.0.0" }
+ let(:platform) { Gem::Platform::RUBY }
+ let(:dependencies) { [] }
+ let(:metadata) { nil }
+
+ subject(:spec) { described_class.new(name, version, platform, dependencies, metadata) }
+
+ describe "#build_dependency" do
+ let(:name) { "foo" }
+ let(:requirement1) { "~> 1.1" }
+ let(:requirement2) { ">= 1.1.7" }
+
+ it "should return a Gem::Dependency" do
+ expect(subject.send(:build_dependency, name, [requirement1, requirement2])).
+ to eq(Gem::Dependency.new(name, requirement1, requirement2))
+ end
+
+ context "when an ArgumentError occurs" do
+ before do
+ allow(Gem::Dependency).to receive(:new).with(name, [requirement1, requirement2]) {
+ raise ArgumentError.new("Some error occurred")
+ }
+ end
+
+ it "should raise the original error" do
+ expect { subject.send(:build_dependency, name, [requirement1, requirement2]) }.to raise_error(
+ ArgumentError, "Some error occurred"
+ )
+ end
+ end
+
+ context "when there is an ill formed requirement" do
+ before do
+ allow(Gem::Dependency).to receive(:new).with(name, [requirement1, requirement2]) {
+ raise ArgumentError.new("Ill-formed requirement [\"#<YAML::Syck::DefaultKey")
+ }
+ # Eliminate extra line break in rspec output due to `puts` in `#build_dependency`
+ allow(subject).to receive(:puts) {}
+ end
+
+ it "should raise a Bundler::GemspecError with invalid gemspec message" do
+ expect { subject.send(:build_dependency, name, [requirement1, requirement2]) }.to raise_error(
+ Bundler::GemspecError, /Unfortunately, the gem foo \(1\.0\.0\) has an invalid gemspec/
+ )
+ end
+ end
+ end
+
+ describe "#parse_metadata" do
+ context "when the metadata has malformed requirements" do
+ let(:metadata) { { "rubygems" => ">\n" } }
+ it "raises a helpful error message" do
+ expect { subject }.to raise_error(
+ Bundler::GemspecError,
+ a_string_including("There was an error parsing the metadata for the gem foo (1.0.0)").
+ and(a_string_including('The metadata was {"rubygems"=>">\n"}'))
+ )
+ end
+ end
+ end
+
+ it "supports equality comparison" do
+ other_spec = described_class.new("bar", version, platform, dependencies, metadata)
+ expect(spec).to eql(spec)
+ expect(spec).to_not eql(other_spec)
+ end
+end
diff --git a/spec/bundler/bundler/env_spec.rb b/spec/bundler/bundler/env_spec.rb
new file mode 100644
index 0000000000..20bd38b021
--- /dev/null
+++ b/spec/bundler/bundler/env_spec.rb
@@ -0,0 +1,151 @@
+# frozen_string_literal: true
+
+require "bundler/settings"
+
+RSpec.describe Bundler::Env do
+ let(:git_proxy_stub) { Bundler::Source::Git::GitProxy.new(nil, nil, nil) }
+
+ describe "#report" do
+ it "prints the environment" do
+ out = described_class.report
+
+ expect(out).to include("Environment")
+ expect(out).to include(Bundler::VERSION)
+ expect(out).to include(Gem::VERSION)
+ expect(out).to include(described_class.send(:ruby_version))
+ expect(out).to include(described_class.send(:git_version))
+ expect(out).to include(OpenSSL::OPENSSL_VERSION)
+ end
+
+ context "when there is a Gemfile and a lockfile and print_gemfile is true" do
+ before do
+ gemfile "gem 'rack', '1.0.0'"
+
+ lockfile <<-L
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 1.10.0
+ L
+ end
+
+ let(:output) { described_class.report(:print_gemfile => true) }
+
+ it "prints the Gemfile" do
+ expect(output).to include("Gemfile")
+ expect(output).to include("'rack', '1.0.0'")
+ end
+
+ it "prints the lockfile" do
+ expect(output).to include("Gemfile.lock")
+ expect(output).to include("rack (1.0.0)")
+ end
+ end
+
+ context "when there no Gemfile and print_gemfile is true" do
+ let(:output) { described_class.report(:print_gemfile => true) }
+
+ it "prints the environment" do
+ expect(output).to start_with("## Environment")
+ end
+ end
+
+ context "when Gemfile contains a gemspec and print_gemspecs is true" do
+ let(:gemspec) do
+ strip_whitespace(<<-GEMSPEC)
+ Gem::Specification.new do |gem|
+ gem.name = "foo"
+ gem.author = "Fumofu"
+ end
+ GEMSPEC
+ end
+
+ before do
+ gemfile("gemspec")
+
+ File.open(bundled_app.join("foo.gemspec"), "wb") do |f|
+ f.write(gemspec)
+ end
+ end
+
+ it "prints the gemspec" do
+ output = described_class.report(:print_gemspecs => true)
+
+ expect(output).to include("foo.gemspec")
+ expect(output).to include(gemspec)
+ end
+ end
+
+ context "when eval_gemfile is used" do
+ it "prints all gemfiles" do
+ create_file "other/Gemfile-other", "gem 'rack'"
+ create_file "other/Gemfile", "eval_gemfile 'Gemfile-other'"
+ create_file "Gemfile-alt", <<-G
+ source "file:#{gem_repo1}"
+ eval_gemfile "other/Gemfile"
+ G
+ gemfile "eval_gemfile #{File.expand_path("Gemfile-alt").dump}"
+
+ output = described_class.report(:print_gemspecs => true)
+ expect(output).to include(strip_whitespace(<<-ENV))
+ ## Gemfile
+
+ ### Gemfile
+
+ ```ruby
+ eval_gemfile #{File.expand_path("Gemfile-alt").dump}
+ ```
+
+ ### Gemfile-alt
+
+ ```ruby
+ source "file:#{gem_repo1}"
+ eval_gemfile "other/Gemfile"
+ ```
+
+ ### other/Gemfile
+
+ ```ruby
+ eval_gemfile 'Gemfile-other'
+ ```
+
+ ### other/Gemfile-other
+
+ ```ruby
+ gem 'rack'
+ ```
+
+ ### Gemfile.lock
+
+ ```
+ <No #{bundled_app("Gemfile.lock")} found>
+ ```
+ ENV
+ end
+ end
+
+ 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").
+ and_return("git version 1.2.3 (Apple Git-BS)")
+ expect(Bundler::Source::Git::GitProxy).to receive(:new).and_return(git_proxy_stub)
+
+ expect(described_class.report).to include("Git 1.2.3 (Apple Git-BS)")
+ end
+ end
+ end
+
+ describe ".version_of", :ruby_repo do
+ let(:parsed_version) { described_class.send(:version_of, "ruby") }
+
+ it "strips version of new line characters" do
+ expect(parsed_version).to_not include("\n")
+ end
+ end
+end
diff --git a/spec/bundler/bundler/environment_preserver_spec.rb b/spec/bundler/bundler/environment_preserver_spec.rb
new file mode 100644
index 0000000000..530ca6f835
--- /dev/null
+++ b/spec/bundler/bundler/environment_preserver_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::EnvironmentPreserver do
+ let(:preserver) { described_class.new(env, ["foo"]) }
+
+ describe "#backup" do
+ let(:env) { { "foo" => "my-foo", "bar" => "my-bar" } }
+ subject { preserver.backup }
+
+ it "should create backup entries" do
+ expect(subject["BUNDLER_ORIG_foo"]).to eq("my-foo")
+ end
+
+ it "should keep the original entry" do
+ expect(subject["foo"]).to eq("my-foo")
+ end
+
+ it "should not create backup entries for unspecified keys" do
+ expect(subject.key?("BUNDLER_ORIG_bar")).to eq(false)
+ end
+
+ it "should not affect the original env" do
+ subject
+ expect(env.keys.sort).to eq(%w[bar foo])
+ end
+
+ context "when a key is empty" do
+ let(:env) { { "foo" => "" } }
+
+ it "should not create backup entries" do
+ expect(subject).not_to have_key "BUNDLER_ORIG_foo"
+ end
+ end
+
+ context "when an original key is set" do
+ let(:env) { { "foo" => "my-foo", "BUNDLER_ORIG_foo" => "orig-foo" } }
+
+ it "should keep the original value in the BUNDLER_ORIG_ variable" do
+ expect(subject["BUNDLER_ORIG_foo"]).to eq("orig-foo")
+ end
+
+ it "should keep the variable" do
+ expect(subject["foo"]).to eq("my-foo")
+ end
+ end
+ end
+
+ describe "#restore" do
+ subject { preserver.restore }
+
+ context "when an original key is set" do
+ let(:env) { { "foo" => "my-foo", "BUNDLER_ORIG_foo" => "orig-foo" } }
+
+ it "should restore the original value" do
+ expect(subject["foo"]).to eq("orig-foo")
+ end
+
+ it "should delete the backup value" do
+ expect(subject.key?("BUNDLER_ORIG_foo")).to eq(false)
+ end
+ end
+
+ context "when no original key is set" do
+ let(:env) { { "foo" => "my-foo" } }
+
+ it "should keep the current value" do
+ expect(subject["foo"]).to eq("my-foo")
+ end
+ end
+
+ context "when the original key is empty" do
+ let(:env) { { "foo" => "my-foo", "BUNDLER_ORIG_foo" => "" } }
+
+ it "should keep the current value" do
+ expect(subject["foo"]).to eq("my-foo")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/fetcher/base_spec.rb b/spec/bundler/bundler/fetcher/base_spec.rb
new file mode 100644
index 0000000000..df1245d44d
--- /dev/null
+++ b/spec/bundler/bundler/fetcher/base_spec.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Fetcher::Base do
+ let(:downloader) { double(:downloader) }
+ let(:remote) { double(:remote) }
+ let(:display_uri) { "http://sample_uri.com" }
+
+ class TestClass < described_class; end
+
+ subject { TestClass.new(downloader, remote, display_uri) }
+
+ describe "#initialize" do
+ context "with the abstract Base class" do
+ it "should raise an error" do
+ expect { described_class.new(downloader, remote, display_uri) }.to raise_error(RuntimeError, "Abstract class")
+ end
+ end
+
+ context "with a class that inherits the Base class" do
+ it "should set the passed attributes" do
+ expect(subject.downloader).to eq(downloader)
+ expect(subject.remote).to eq(remote)
+ expect(subject.display_uri).to eq("http://sample_uri.com")
+ end
+ end
+ end
+
+ describe "#remote_uri" do
+ let(:remote_uri_obj) { double(:remote_uri_obj) }
+
+ before { allow(remote).to receive(:uri).and_return(remote_uri_obj) }
+
+ it "should return the remote's uri" do
+ expect(subject.remote_uri).to eq(remote_uri_obj)
+ end
+ end
+
+ describe "#fetch_uri" do
+ let(:remote_uri_obj) { URI("http://rubygems.org") }
+
+ before { allow(subject).to receive(:remote_uri).and_return(remote_uri_obj) }
+
+ context "when the remote uri's host is rubygems.org" do
+ it "should create a copy of the remote uri with index.rubygems.org as the host" do
+ fetched_uri = subject.fetch_uri
+ expect(fetched_uri.host).to eq("index.rubygems.org")
+ expect(fetched_uri).to_not be(remote_uri_obj)
+ end
+ end
+
+ context "when the remote uri's host is not rubygems.org" do
+ let(:remote_uri_obj) { URI("http://otherhost.org") }
+
+ it "should return the remote uri" do
+ expect(subject.fetch_uri).to eq(URI("http://otherhost.org"))
+ end
+ end
+
+ it "memoizes the fetched uri" do
+ expect(remote_uri_obj).to receive(:host).once
+ 2.times { subject.fetch_uri }
+ end
+ end
+
+ describe "#available?" do
+ it "should return whether the api is available" do
+ expect(subject.available?).to be_truthy
+ end
+ end
+
+ describe "#api_fetcher?" do
+ it "should return false" do
+ expect(subject.api_fetcher?).to be_falsey
+ end
+ end
+end
diff --git a/spec/bundler/bundler/fetcher/compact_index_spec.rb b/spec/bundler/bundler/fetcher/compact_index_spec.rb
new file mode 100644
index 0000000000..e0f58766ea
--- /dev/null
+++ b/spec/bundler/bundler/fetcher/compact_index_spec.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Fetcher::CompactIndex do
+ let(:downloader) { double(:downloader) }
+ let(:display_uri) { URI("http://sampleuri.com") }
+ let(:remote) { double(:remote, :cache_slug => "lsjdf", :uri => display_uri) }
+ let(:compact_index) { described_class.new(downloader, remote, display_uri) }
+
+ before do
+ allow(compact_index).to receive(:log_specs) {}
+ end
+
+ describe "#specs_for_names" do
+ it "has only one thread open at the end of the run" do
+ compact_index.specs_for_names(["lskdjf"])
+
+ thread_count = Thread.list.count {|thread| thread.status == "run" }
+ expect(thread_count).to eq 1
+ end
+
+ it "calls worker#stop during the run" do
+ expect_any_instance_of(Bundler::Worker).to receive(:stop).at_least(:once)
+
+ compact_index.specs_for_names(["lskdjf"])
+ end
+
+ describe "#available?" do
+ before do
+ allow(compact_index).to receive(:compact_index_client).
+ and_return(double(:compact_index_client, :update_and_parse_checksums! => true))
+ end
+
+ it "returns true" do
+ expect(compact_index).to be_available
+ end
+
+ context "when OpenSSL is not available" do
+ before do
+ allow(compact_index).to receive(:require).with("openssl").and_raise(LoadError)
+ end
+
+ it "returns true" do
+ expect(compact_index).to be_available
+ end
+ end
+
+ context "when OpenSSL is FIPS-enabled", :ruby => ">= 2.0.0" do
+ def remove_cached_md5_availability
+ return unless Bundler::SharedHelpers.instance_variable_defined?(:@md5_available)
+ Bundler::SharedHelpers.remove_instance_variable(:@md5_available)
+ end
+
+ before do
+ remove_cached_md5_availability
+ stub_const("OpenSSL::OPENSSL_FIPS", true)
+ end
+
+ after { remove_cached_md5_availability }
+
+ context "when FIPS-mode is active" do
+ before do
+ allow(OpenSSL::Digest::MD5).to receive(:digest).
+ and_raise(OpenSSL::Digest::DigestError)
+ end
+
+ it "returns false" do
+ expect(compact_index).to_not be_available
+ end
+ end
+
+ it "returns true" do
+ expect(compact_index).to be_available
+ end
+ end
+ end
+
+ context "logging" do
+ before { allow(compact_index).to receive(:log_specs).and_call_original }
+
+ context "with debug on" do
+ before do
+ allow(Bundler).to receive_message_chain(:ui, :debug?).and_return(true)
+ end
+
+ it "should log at info level" do
+ expect(Bundler).to receive_message_chain(:ui, :debug).with('Looking up gems ["lskdjf"]')
+ compact_index.specs_for_names(["lskdjf"])
+ end
+ end
+
+ context "with debug off" do
+ before do
+ allow(Bundler).to receive_message_chain(:ui, :debug?).and_return(false)
+ end
+
+ it "should log at info level" do
+ expect(Bundler).to receive_message_chain(:ui, :info).with(".", false)
+ compact_index.specs_for_names(["lskdjf"])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/fetcher/dependency_spec.rb b/spec/bundler/bundler/fetcher/dependency_spec.rb
new file mode 100644
index 0000000000..081fdff34d
--- /dev/null
+++ b/spec/bundler/bundler/fetcher/dependency_spec.rb
@@ -0,0 +1,287 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Fetcher::Dependency do
+ let(:downloader) { double(:downloader) }
+ let(:remote) { double(:remote, :uri => URI("http://localhost:5000")) }
+ let(:display_uri) { "http://sample_uri.com" }
+
+ subject { described_class.new(downloader, remote, display_uri) }
+
+ describe "#available?" do
+ let(:dependency_api_uri) { double(:dependency_api_uri) }
+ let(:fetched_spec) { double(:fetched_spec) }
+
+ before do
+ allow(subject).to receive(:dependency_api_uri).and_return(dependency_api_uri)
+ allow(downloader).to receive(:fetch).with(dependency_api_uri).and_return(fetched_spec)
+ end
+
+ it "should be truthy" do
+ expect(subject.available?).to be_truthy
+ end
+
+ context "when there is no network access" do
+ before do
+ allow(downloader).to receive(:fetch).with(dependency_api_uri) {
+ raise Bundler::Fetcher::NetworkDownError.new("Network Down Message")
+ }
+ end
+
+ it "should raise an HTTPError with the original message" do
+ expect { subject.available? }.to raise_error(Bundler::HTTPError, "Network Down Message")
+ end
+ end
+
+ context "when authentication is required" do
+ let(:remote_uri) { "http://remote_uri.org" }
+
+ before do
+ allow(downloader).to receive(:fetch).with(dependency_api_uri) {
+ raise Bundler::Fetcher::AuthenticationRequiredError.new(remote_uri)
+ }
+ end
+
+ it "should raise the original error" do
+ expect { subject.available? }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
+ %r{Authentication is required for http://remote_uri.org})
+ end
+ end
+
+ context "when there is an http error" do
+ before { allow(downloader).to receive(:fetch).with(dependency_api_uri) { raise Bundler::HTTPError.new } }
+
+ it "should be falsey" do
+ expect(subject.available?).to be_falsey
+ end
+ end
+ end
+
+ describe "#api_fetcher?" do
+ it "should return true" do
+ expect(subject.api_fetcher?).to be_truthy
+ end
+ end
+
+ describe "#specs" do
+ let(:gem_names) { %w[foo bar] }
+ let(:full_dependency_list) { ["bar"] }
+ let(:last_spec_list) { [["boulder", gem_version1, "ruby", resque]] }
+ let(:fail_errors) { double(:fail_errors) }
+ let(:bundler_retry) { double(:bundler_retry) }
+ let(:gem_version1) { double(:gem_version1) }
+ let(:resque) { double(:resque) }
+ let(:remote_uri) { "http://remote-uri.org" }
+
+ before do
+ stub_const("Bundler::Fetcher::FAIL_ERRORS", fail_errors)
+ allow(Bundler::Retry).to receive(:new).with("dependency api", fail_errors).and_return(bundler_retry)
+ allow(bundler_retry).to receive(:attempts) {|&block| block.call }
+ allow(subject).to receive(:log_specs) {}
+ allow(subject).to receive(:remote_uri).and_return(remote_uri)
+ allow(Bundler).to receive_message_chain(:ui, :debug?)
+ allow(Bundler).to receive_message_chain(:ui, :info)
+ allow(Bundler).to receive_message_chain(:ui, :debug)
+ end
+
+ context "when there are given gem names that are not in the full dependency list" do
+ let(:spec_list) { [["top", gem_version2, "ruby", faraday]] }
+ let(:deps_list) { [] }
+ let(:dependency_specs) { [spec_list, deps_list] }
+ let(:gem_version2) { double(:gem_version2) }
+ let(:faraday) { double(:faraday) }
+
+ before { allow(subject).to receive(:dependency_specs).with(["foo"]).and_return(dependency_specs) }
+
+ it "should return a hash with the remote_uri and the list of specs" do
+ expect(subject.specs(gem_names, full_dependency_list, last_spec_list)).to eq([
+ ["top", gem_version2, "ruby", faraday],
+ ["boulder", gem_version1, "ruby", resque],
+ ])
+ end
+ end
+
+ context "when all given gem names are in the full dependency list" do
+ let(:gem_names) { ["foo"] }
+ let(:full_dependency_list) { %w[foo bar] }
+ let(:last_spec_list) { ["boulder"] }
+
+ it "should return a hash with the remote_uri and the last spec list" do
+ expect(subject.specs(gem_names, full_dependency_list, last_spec_list)).to eq(["boulder"])
+ end
+ end
+
+ context "logging" do
+ before { allow(subject).to receive(:log_specs).and_call_original }
+
+ context "with debug on" do
+ before do
+ allow(Bundler).to receive_message_chain(:ui, :debug?).and_return(true)
+ allow(subject).to receive(:dependency_specs).with(["foo"]).and_return([[], []])
+ end
+
+ it "should log the query list at debug level" do
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("Query List: [\"foo\"]")
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("Query List: []")
+ subject.specs(gem_names, full_dependency_list, last_spec_list)
+ end
+ end
+
+ context "with debug off" do
+ before do
+ allow(Bundler).to receive_message_chain(:ui, :debug?).and_return(false)
+ allow(subject).to receive(:dependency_specs).with(["foo"]).and_return([[], []])
+ end
+
+ it "should log at info level" do
+ expect(Bundler).to receive_message_chain(:ui, :info).with(".", false)
+ expect(Bundler).to receive_message_chain(:ui, :info).with(".", false)
+ subject.specs(gem_names, full_dependency_list, last_spec_list)
+ end
+ end
+ end
+
+ shared_examples_for "the error is properly handled" do
+ it "should return nil" do
+ expect(subject.specs(gem_names, full_dependency_list, last_spec_list)).to be_nil
+ end
+
+ context "debug logging is not on" do
+ before { allow(Bundler).to receive_message_chain(:ui, :debug?).and_return(false) }
+
+ it "should log a new line to info" do
+ expect(Bundler).to receive_message_chain(:ui, :info).with("")
+ subject.specs(gem_names, full_dependency_list, last_spec_list)
+ end
+ 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`")
+ subject.specs(gem_names, full_dependency_list, last_spec_list)
+ end
+ end
+
+ context "when an HTTPError occurs" 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"
+ 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"
+ 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
+ end
+ end
+
+ describe "#dependency_specs" do
+ let(:gem_names) { [%w[foo bar], %w[bundler rubocop]] }
+ let(:gem_list) { double(:gem_list) }
+ let(:formatted_specs_and_deps) { double(:formatted_specs_and_deps) }
+
+ before do
+ allow(subject).to receive(:unmarshalled_dep_gems).with(gem_names).and_return(gem_list)
+ allow(subject).to receive(:get_formatted_specs_and_deps).with(gem_list).and_return(formatted_specs_and_deps)
+ end
+
+ it "should log the query list at debug level" do
+ expect(Bundler).to receive_message_chain(:ui, :debug).with(
+ "Query Gemcutter Dependency Endpoint API: foo,bar,bundler,rubocop"
+ )
+ subject.dependency_specs(gem_names)
+ end
+
+ it "should return formatted specs and a unique list of dependencies" do
+ expect(subject.dependency_specs(gem_names)).to eq(formatted_specs_and_deps)
+ end
+ end
+
+ describe "#unmarshalled_dep_gems" do
+ let(:gem_names) { [%w[foo bar], %w[bundler rubocop]] }
+ let(:dep_api_uri) { double(:dep_api_uri) }
+ let(:unmarshalled_gems) { double(:unmarshalled_gems) }
+ let(:fetch_response) { double(:fetch_response, :body => double(:body)) }
+ let(:rubygems_limit) { 50 }
+
+ before { allow(subject).to receive(:dependency_api_uri).with(gem_names).and_return(dep_api_uri) }
+
+ 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(subject.unmarshalled_dep_gems(gem_names)).to eq([unmarshalled_gems])
+ end
+ end
+
+ describe "#get_formatted_specs_and_deps" do
+ let(:gem_list) do
+ [
+ {
+ :dependencies => {
+ "resque" => "req3,req4",
+ },
+ :name => "typhoeus",
+ :number => "1.0.1",
+ :platform => "ruby",
+ },
+ {
+ :dependencies => {
+ "faraday" => "req1,req2",
+ },
+ :name => "grape",
+ :number => "2.0.2",
+ :platform => "jruby",
+ },
+ ]
+ end
+
+ it "should return formatted specs and a unique list of dependencies" do
+ spec_list, deps_list = subject.get_formatted_specs_and_deps(gem_list)
+ expect(spec_list).to eq([["typhoeus", "1.0.1", "ruby", [["resque", ["req3,req4"]]]],
+ ["grape", "2.0.2", "jruby", [["faraday", ["req1,req2"]]]]])
+ expect(deps_list).to eq(%w[resque faraday])
+ end
+ end
+
+ describe "#dependency_api_uri" do
+ let(:uri) { URI("http://gem-api.com") }
+
+ context "with gem names" do
+ let(:gem_names) { %w[foo bar bundler rubocop] }
+
+ before { allow(subject).to receive(:fetch_uri).and_return(uri) }
+
+ it "should return an api calling uri with the gems in the query" do
+ expect(subject.dependency_api_uri(gem_names).to_s).to eq(
+ "http://gem-api.com/api/v1/dependencies?gems=bar%2Cbundler%2Cfoo%2Crubocop"
+ )
+ end
+ end
+
+ context "with no gem names" do
+ let(:gem_names) { [] }
+
+ before { allow(subject).to receive(:fetch_uri).and_return(uri) }
+
+ it "should return an api calling uri with no query" do
+ expect(subject.dependency_api_uri(gem_names).to_s).to eq(
+ "http://gem-api.com/api/v1/dependencies"
+ )
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/fetcher/downloader_spec.rb b/spec/bundler/bundler/fetcher/downloader_spec.rb
new file mode 100644
index 0000000000..c9b4fa662a
--- /dev/null
+++ b/spec/bundler/bundler/fetcher/downloader_spec.rb
@@ -0,0 +1,250 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Fetcher::Downloader do
+ let(:connection) { double(:connection) }
+ let(:redirect_limit) { 5 }
+ let(:uri) { URI("http://www.uri-to-fetch.com/api/v2/endpoint") }
+ let(:options) { double(:options) }
+
+ subject { described_class.new(connection, redirect_limit) }
+
+ describe "fetch" do
+ let(:counter) { 0 }
+ let(:httpv) { "1.1" }
+ let(:http_response) { double(:response) }
+
+ before do
+ allow(subject).to receive(:request).with(uri, options).and_return(http_response)
+ allow(http_response).to receive(:body).and_return("Body with info")
+ end
+
+ context "when the # requests counter is greater than the redirect limit" do
+ let(:counter) { redirect_limit + 1 }
+
+ it "should raise a Bundler::HTTPError specifying too many redirects" do
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::HTTPError, "Too many redirects")
+ end
+ end
+
+ context "logging" do
+ let(:http_response) { Net::HTTPSuccess.new("1.1", 200, "Success") }
+
+ it "should log the HTTP response code and message to debug" do
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("HTTP 200 Success #{uri}")
+ subject.fetch(uri, options, counter)
+ end
+ end
+
+ context "when the request response is a Net::HTTPRedirection" do
+ let(:http_response) { Net::HTTPRedirection.new(httpv, 308, "Moved") }
+
+ before { http_response["location"] = "http://www.redirect-uri.com/api/v2/endpoint" }
+
+ it "should try to fetch the redirect uri and iterate the # requests counter" do
+ expect(subject).to receive(:fetch).with(URI("http://www.uri-to-fetch.com/api/v2/endpoint"), options, 0).and_call_original
+ expect(subject).to receive(:fetch).with(URI("http://www.redirect-uri.com/api/v2/endpoint"), options, 1)
+ subject.fetch(uri, options, counter)
+ end
+
+ context "when the redirect uri and original uri are the same" do
+ let(:uri) { URI("ssh://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
+
+ before { http_response["location"] = "ssh://www.uri-to-fetch.com/api/v1/endpoint" }
+
+ it "should set the same user and password for the redirect uri" do
+ expect(subject).to receive(:fetch).with(URI("ssh://username:password@www.uri-to-fetch.com/api/v2/endpoint"), options, 0).and_call_original
+ expect(subject).to receive(:fetch).with(URI("ssh://username:password@www.uri-to-fetch.com/api/v1/endpoint"), options, 1)
+ subject.fetch(uri, options, counter)
+ end
+ end
+ end
+
+ context "when the request response is a Net::HTTPSuccess" do
+ let(:http_response) { Net::HTTPSuccess.new("1.1", 200, "Success") }
+
+ it "should return the response body" do
+ expect(subject.fetch(uri, options, counter)).to eq(http_response)
+ end
+ end
+
+ context "when the request response is a Net::HTTPRequestEntityTooLarge" do
+ let(:http_response) { Net::HTTPRequestEntityTooLarge.new("1.1", 413, "Too Big") }
+
+ it "should raise a Bundler::Fetcher::FallbackError with the response body" do
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::FallbackError, "Body with info")
+ end
+ end
+
+ context "when the request response is a Net::HTTPUnauthorized" do
+ let(:http_response) { Net::HTTPUnauthorized.new("1.1", 401, "Unauthorized") }
+
+ it "should raise a Bundler::Fetcher::AuthenticationRequiredError with the uri host" do
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
+ /Authentication is required 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") }
+
+ it "should raise a Bundler::Fetcher::FallbackError with Net::HTTPNotFound" do
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::FallbackError, "Net::HTTPNotFound")
+ end
+ end
+
+ context "when the request response is some other type" do
+ let(:http_response) { Net::HTTPBadGateway.new("1.1", 500, "Fatal Error") }
+
+ it "should raise a Bundler::HTTPError with the response class and body" do
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::HTTPError, "Net::HTTPBadGateway: Body with info")
+ end
+ end
+ end
+
+ describe "request" do
+ let(:net_http_get) { double(:net_http_get) }
+ let(:response) { double(:response) }
+
+ before do
+ allow(Net::HTTP::Get).to receive(:new).with("/api/v2/endpoint", options).and_return(net_http_get)
+ allow(connection).to receive(:request).with(uri, net_http_get).and_return(response)
+ end
+
+ it "should log the HTTP GET request to debug" do
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("HTTP GET http://www.uri-to-fetch.com/api/v2/endpoint")
+ subject.request(uri, options)
+ end
+
+ context "when there is a user provided in the request" do
+ context "and there is also a password provided" do
+ context "that contains cgi escaped characters" do
+ let(:uri) { URI("http://username:password%24@www.uri-to-fetch.com/api/v2/endpoint") }
+
+ it "should request basic authentication with the username and password" do
+ expect(net_http_get).to receive(:basic_auth).with("username", "password$")
+ subject.request(uri, options)
+ end
+ end
+
+ context "that is all unescaped characters" do
+ let(:uri) { URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
+ it "should request basic authentication with the username and proper cgi compliant password" do
+ expect(net_http_get).to receive(:basic_auth).with("username", "password")
+ subject.request(uri, options)
+ end
+ end
+ end
+
+ context "and there is no password provided" do
+ let(:uri) { URI("http://username@www.uri-to-fetch.com/api/v2/endpoint") }
+
+ it "should request basic authentication with just the user" do
+ expect(net_http_get).to receive(:basic_auth).with("username", nil)
+ subject.request(uri, options)
+ end
+ end
+
+ context "that contains cgi escaped characters" do
+ let(:uri) { URI("http://username%24@www.uri-to-fetch.com/api/v2/endpoint") }
+
+ it "should request basic authentication with the proper cgi compliant password user" do
+ expect(net_http_get).to receive(:basic_auth).with("username$", nil)
+ subject.request(uri, options)
+ end
+ 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 } }
+
+ it "should raise a LoadError about openssl" do
+ expect { subject.request(uri, options) }.to raise_error(Bundler::Fetcher::CertificateFailureError,
+ %r{Could not verify the SSL certificate for http://www.uri-to-fetch.com/api/v2/endpoint})
+ end
+ end
+
+ context "when the request response causes an error included in HTTP_ERRORS" do
+ let(:message) { nil }
+ let(:error) { RuntimeError.new(message) }
+
+ before do
+ stub_const("Bundler::Fetcher::HTTP_ERRORS", [RuntimeError])
+ allow(connection).to receive(:request).with(uri, net_http_get) { raise error }
+ end
+
+ it "should trace log the error" do
+ allow(Bundler).to receive_message_chain(:ui, :debug)
+ expect(Bundler).to receive_message_chain(:ui, :trace).with(error)
+ expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError)
+ end
+
+ context "when error message is about the host being down" do
+ let(:message) { "host down: http://www.uri-to-fetch.com" }
+
+ it "should raise a Bundler::Fetcher::NetworkDownError" do
+ expect { subject.request(uri, options) }.to raise_error(Bundler::Fetcher::NetworkDownError,
+ /Could not reach host www.uri-to-fetch.com/)
+ end
+ end
+
+ context "when error message is about getaddrinfo issues" do
+ let(:message) { "getaddrinfo: nodename nor servname provided for http://www.uri-to-fetch.com" }
+
+ it "should raise a Bundler::Fetcher::NetworkDownError" do
+ expect { subject.request(uri, options) }.to raise_error(Bundler::Fetcher::NetworkDownError,
+ /Could not reach host www.uri-to-fetch.com/)
+ end
+ end
+
+ context "when error message is about neither host down or getaddrinfo" do
+ let(:message) { "other error about network" }
+
+ it "should raise a Bundler::HTTPError" do
+ expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError,
+ "Network error while fetching http://www.uri-to-fetch.com/api/v2/endpoint (other error about network)")
+ end
+
+ context "when the there are credentials provided in the request" do
+ let(:uri) { URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
+ before do
+ allow(net_http_get).to receive(:basic_auth).with("username", "password")
+ end
+
+ it "should raise a Bundler::HTTPError that doesn't contain the password" do
+ expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError,
+ "Network error while fetching http://username@www.uri-to-fetch.com/api/v2/endpoint (other error about network)")
+ end
+ end
+ end
+
+ context "when error message is about no route to host" do
+ let(:message) { "Failed to open TCP connection to www.uri-to-fetch.com:443 " }
+
+ it "should raise a Bundler::Fetcher::HTTPError" do
+ expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError,
+ "Network error while fetching http://www.uri-to-fetch.com/api/v2/endpoint (#{message})")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/fetcher/index_spec.rb b/spec/bundler/bundler/fetcher/index_spec.rb
new file mode 100644
index 0000000000..0cf0ae764e
--- /dev/null
+++ b/spec/bundler/bundler/fetcher/index_spec.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Fetcher::Index do
+ let(:downloader) { nil }
+ let(:remote) { nil }
+ let(:display_uri) { "http://sample_uri.com" }
+ let(:rubygems) { double(:rubygems) }
+ let(:gem_names) { %w[foo bar] }
+
+ subject { described_class.new(downloader, remote, display_uri) }
+
+ before { allow(Bundler).to receive(:rubygems).and_return(rubygems) }
+
+ it "fetches and returns the list of remote specs" do
+ expect(rubygems).to receive(:fetch_all_remote_specs) { nil }
+ subject.specs(gem_names)
+ end
+
+ context "error handling" do
+ shared_examples_for "the error is properly handled" do
+ let(:remote_uri) { URI("http://remote-uri.org") }
+ before do
+ allow(subject).to receive(:remote_uri).and_return(remote_uri)
+ end
+
+ context "when certificate verify failed" do
+ let(:error_message) { "certificate verify failed" }
+
+ it "should raise a Bundler::Fetcher::CertificateFailureError" do
+ expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::CertificateFailureError,
+ %r{Could not verify the SSL certificate for http://sample_uri.com})
+ end
+ end
+
+ context "when a 401 response occurs" do
+ let(:error_message) { "401" }
+
+ 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
+ end
+
+ 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
+ end
+ end
+
+ context "any other message is returned" do
+ let(:error_message) { "You get an error, you get an error!" }
+
+ before { allow(Bundler).to receive(:ui).and_return(double(:trace => nil)) }
+
+ it "should raise a Bundler::HTTPError" do
+ expect { subject.specs(gem_names) }.to raise_error(Bundler::HTTPError, "Could not fetch specs from http://sample_uri.com")
+ end
+ end
+ end
+
+ context "when a Gem::RemoteFetcher::FetchError occurs" do
+ before { allow(rubygems).to receive(:fetch_all_remote_specs) { raise Gem::RemoteFetcher::FetchError.new(error_message, nil) } }
+
+ it_behaves_like "the error is properly handled"
+ end
+
+ context "when a OpenSSL::SSL::SSLError occurs" do
+ before { allow(rubygems).to receive(:fetch_all_remote_specs) { raise OpenSSL::SSL::SSLError.new(error_message) } }
+
+ it_behaves_like "the error is properly handled"
+ end
+
+ context "when a Net::HTTPFatalError occurs" do
+ before { allow(rubygems).to receive(:fetch_all_remote_specs) { raise Net::HTTPFatalError.new(error_message, 404) } }
+
+ it_behaves_like "the error is properly handled"
+ end
+ end
+end
diff --git a/spec/bundler/bundler/fetcher_spec.rb b/spec/bundler/bundler/fetcher_spec.rb
new file mode 100644
index 0000000000..184b9efa64
--- /dev/null
+++ b/spec/bundler/bundler/fetcher_spec.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+
+require "bundler/fetcher"
+
+RSpec.describe Bundler::Fetcher do
+ let(:uri) { URI("https://example.com") }
+ let(:remote) { double("remote", :uri => uri, :original_uri => nil) }
+
+ subject(:fetcher) { Bundler::Fetcher.new(remote) }
+
+ before do
+ allow(Bundler).to receive(:root) { Pathname.new("root") }
+ end
+
+ describe "#connection" do
+ context "when Gem.configuration doesn't specify http_proxy" do
+ it "specify no http_proxy" do
+ expect(fetcher.http_proxy).to be_nil
+ end
+ it "consider environment vars when determine proxy" do
+ with_env_vars("HTTP_PROXY" => "http://proxy-example.com") do
+ expect(fetcher.http_proxy).to match("http://proxy-example.com")
+ end
+ end
+ end
+ context "when Gem.configuration specifies http_proxy " do
+ let(:proxy) { "http://proxy-example2.com" }
+ before do
+ allow(Bundler.rubygems.configuration).to receive(:[]).with(:http_proxy).and_return(proxy)
+ end
+ it "consider Gem.configuration when determine proxy" do
+ expect(fetcher.http_proxy).to match("http://proxy-example2.com")
+ end
+ it "consider Gem.configuration when determine proxy" do
+ with_env_vars("HTTP_PROXY" => "http://proxy-example.com") do
+ expect(fetcher.http_proxy).to match("http://proxy-example2.com")
+ end
+ end
+ context "when the proxy is :no_proxy" do
+ let(:proxy) { :no_proxy }
+ it "does not set a proxy" do
+ expect(fetcher.http_proxy).to be_nil
+ end
+ end
+ end
+
+ context "when a rubygems source mirror is set" do
+ let(:orig_uri) { URI("http://zombo.com") }
+ let(:remote_with_mirror) do
+ double("remote", :uri => uri, :original_uri => orig_uri, :anonymized_uri => uri)
+ end
+
+ let(:fetcher) { Bundler::Fetcher.new(remote_with_mirror) }
+
+ it "sets the 'X-Gemfile-Source' header containing the original source" do
+ expect(
+ fetcher.send(:connection).override_headers["X-Gemfile-Source"]
+ ).to eq("http://zombo.com")
+ end
+ end
+
+ context "when there is no rubygems source mirror set" do
+ let(:remote_no_mirror) do
+ double("remote", :uri => uri, :original_uri => nil, :anonymized_uri => uri)
+ end
+
+ let(:fetcher) { Bundler::Fetcher.new(remote_no_mirror) }
+
+ it "does not set the 'X-Gemfile-Source' header" do
+ expect(fetcher.send(:connection).override_headers["X-Gemfile-Source"]).to be_nil
+ end
+ end
+
+ context "when there are proxy environment variable(s) set" do
+ it "consider http_proxy" do
+ with_env_vars("HTTP_PROXY" => "http://proxy-example3.com") do
+ expect(fetcher.http_proxy).to match("http://proxy-example3.com")
+ end
+ end
+ it "consider no_proxy" do
+ with_env_vars("HTTP_PROXY" => "http://proxy-example4.com", "NO_PROXY" => ".example.com,.example.net") do
+ expect(
+ fetcher.send(:connection).no_proxy
+ ).to eq([".example.com", ".example.net"])
+ end
+ end
+ end
+
+ context "when no ssl configuration is set" do
+ it "no cert" do
+ expect(fetcher.send(:connection).cert).to be_nil
+ expect(fetcher.send(:connection).key).to be_nil
+ end
+ end
+
+ context "when bunder ssl ssl configuration is set" do
+ before do
+ cert = File.join(Spec::Path.tmpdir, "cert")
+ File.open(cert, "w") {|f| f.write "PEM" }
+ allow(Bundler.settings).to receive(:[]).and_return(nil)
+ allow(Bundler.settings).to receive(:[]).with(:ssl_client_cert).and_return(cert)
+ expect(OpenSSL::X509::Certificate).to receive(:new).with("PEM").and_return("cert")
+ expect(OpenSSL::PKey::RSA).to receive(:new).with("PEM").and_return("key")
+ end
+ after do
+ FileUtils.rm File.join(Spec::Path.tmpdir, "cert")
+ end
+ it "use bundler configuration" do
+ expect(fetcher.send(:connection).cert).to eq("cert")
+ expect(fetcher.send(:connection).key).to eq("key")
+ end
+ end
+
+ context "when gem ssl configuration is set" do
+ before do
+ allow(Bundler.rubygems.configuration).to receive_messages(
+ :http_proxy => nil,
+ :ssl_client_cert => "cert",
+ :ssl_ca_cert => "ca"
+ )
+ expect(File).to receive(:read).and_return("")
+ expect(OpenSSL::X509::Certificate).to receive(:new).and_return("cert")
+ expect(OpenSSL::PKey::RSA).to receive(:new).and_return("key")
+ store = double("ca store")
+ expect(store).to receive(:add_file)
+ expect(OpenSSL::X509::Store).to receive(:new).and_return(store)
+ end
+ it "use gem configuration" do
+ expect(fetcher.send(:connection).cert).to eq("cert")
+ expect(fetcher.send(:connection).key).to eq("key")
+ end
+ end
+ end
+
+ describe "#user_agent" do
+ it "builds user_agent with current ruby version and Bundler settings" do
+ allow(Bundler.settings).to receive(:all).and_return(%w[foo bar])
+ expect(fetcher.user_agent).to match(%r{bundler/(\d.)})
+ expect(fetcher.user_agent).to match(%r{rubygems/(\d.)})
+ expect(fetcher.user_agent).to match(%r{ruby/(\d.)})
+ expect(fetcher.user_agent).to match(%r{options/foo,bar})
+ end
+
+ describe "include CI information" do
+ it "from one CI" do
+ with_env_vars("JENKINS_URL" => "foo") do
+ ci_part = fetcher.user_agent.split(" ").find {|x| x.match(%r{\Aci/}) }
+ expect(ci_part).to match("jenkins")
+ end
+ end
+
+ it "from many CI" do
+ with_env_vars("TRAVIS" => "foo", "CI_NAME" => "my_ci") do
+ ci_part = fetcher.user_agent.split(" ").find {|x| x.match(%r{\Aci/}) }
+ expect(ci_part).to match("travis")
+ expect(ci_part).to match("my_ci")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/friendly_errors_spec.rb b/spec/bundler/bundler/friendly_errors_spec.rb
new file mode 100644
index 0000000000..2a1be491ef
--- /dev/null
+++ b/spec/bundler/bundler/friendly_errors_spec.rb
@@ -0,0 +1,270 @@
+# frozen_string_literal: true
+
+require "bundler"
+require "bundler/friendly_errors"
+require "cgi"
+
+RSpec.describe Bundler, "friendly errors" do
+ context "with invalid YAML in .gemrc" do
+ before do
+ File.open(Gem.configuration.config_file_name, "w") do |f|
+ f.write "invalid: yaml: hah"
+ end
+ end
+
+ after do
+ FileUtils.rm(Gem.configuration.config_file_name)
+ end
+
+ it "reports a relevant friendly error message", :ruby => ">= 1.9", :rubygems => "< 2.5.0" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle :install, :env => { "DEBUG" => true }
+
+ expect(out).to include("Your RubyGems configuration")
+ expect(out).to include("invalid YAML syntax")
+ expect(out).to include("Psych::SyntaxError")
+ expect(out).not_to include("ERROR REPORT TEMPLATE")
+ expect(exitstatus).to eq(25) if exitstatus
+ end
+
+ it "reports a relevant friendly error message", :ruby => ">= 1.9", :rubygems => ">= 2.5.0" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle :install, :env => { "DEBUG" => true }
+
+ expect(last_command.stderr).to include("Failed to load #{home(".gemrc")}")
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+ end
+
+ it "calls log_error in case of exception" do
+ exception = Exception.new
+ expect(Bundler::FriendlyErrors).to receive(:exit_status).with(exception).and_return(1)
+ expect do
+ Bundler.with_friendly_errors do
+ raise exception
+ end
+ end.to raise_error(SystemExit)
+ end
+
+ it "calls exit_status on exception" do
+ exception = Exception.new
+ expect(Bundler::FriendlyErrors).to receive(:log_error).with(exception)
+ expect do
+ Bundler.with_friendly_errors do
+ raise exception
+ end
+ end.to raise_error(SystemExit)
+ end
+
+ describe "#log_error" do
+ shared_examples "Bundler.ui receive error" do |error, message|
+ it "" do
+ expect(Bundler.ui).to receive(:error).with(message || error.message)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ end
+
+ shared_examples "Bundler.ui receive trace" do |error|
+ it "" do
+ expect(Bundler.ui).to receive(:trace).with(error)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ end
+
+ context "YamlSyntaxError" do
+ it_behaves_like "Bundler.ui receive error", Bundler::YamlSyntaxError.new(StandardError.new, "sample_message")
+
+ it "Bundler.ui receive trace" do
+ std_error = StandardError.new
+ exception = Bundler::YamlSyntaxError.new(std_error, "sample_message")
+ expect(Bundler.ui).to receive(:trace).with(std_error)
+ Bundler::FriendlyErrors.log_error(exception)
+ end
+ end
+
+ context "Dsl::DSLError, GemspecError" do
+ it_behaves_like "Bundler.ui receive error", Bundler::Dsl::DSLError.new("description", "dsl_path", "backtrace")
+ it_behaves_like "Bundler.ui receive error", Bundler::GemspecError.new
+ end
+
+ context "GemRequireError" do
+ let(:orig_error) { StandardError.new }
+ let(:error) { Bundler::GemRequireError.new(orig_error, "sample_message") }
+
+ before do
+ allow(orig_error).to receive(:backtrace).and_return([])
+ end
+
+ it "Bundler.ui receive error" do
+ expect(Bundler.ui).to receive(:error).with(error.message)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+
+ it "writes to Bundler.ui.trace" do
+ expect(Bundler.ui).to receive(:trace).with(orig_error, nil, true)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ end
+
+ context "BundlerError" do
+ it "Bundler.ui receive error" do
+ error = Bundler::BundlerError.new
+ expect(Bundler.ui).to receive(:error).with(error.message, :wrap => true)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ it_behaves_like "Bundler.ui receive trace", Bundler::BundlerError.new
+ end
+
+ context "Thor::Error" 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") }
+
+ it "Bundler.ui receive error" do
+ expect(Bundler.ui).to receive(:error).with("\nCould not load OpenSSL.")
+ Bundler::FriendlyErrors.log_error(error)
+ end
+
+ it "Bundler.ui receive warn" do
+ expect(Bundler.ui).to receive(:warn).with(any_args, :wrap => true)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+
+ it "Bundler.ui receive trace" do
+ expect(Bundler.ui).to receive(:trace).with(error)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ end
+
+ context "Interrupt" do
+ it "Bundler.ui receive error" do
+ expect(Bundler.ui).to receive(:error).with("\nQuitting...")
+ Bundler::FriendlyErrors.log_error(Interrupt.new)
+ end
+ it_behaves_like "Bundler.ui receive trace", Interrupt.new
+ end
+
+ context "Gem::InvalidSpecificationException" do
+ it "Bundler.ui receive error" do
+ error = Gem::InvalidSpecificationException.new
+ expect(Bundler.ui).to receive(:error).with(error.message, :wrap => true)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ end
+
+ context "SystemExit" do
+ # Does nothing
+ end
+
+ context "Java::JavaLang::OutOfMemoryError" do
+ module Java
+ module JavaLang
+ class OutOfMemoryError < StandardError; end
+ end
+ end
+
+ it "Bundler.ui receive error" do
+ error = Java::JavaLang::OutOfMemoryError.new
+ expect(Bundler.ui).to receive(:error).with(/JVM has run out of memory/)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ end
+
+ context "unexpected error" do
+ it "calls request_issue_report_for with error" do
+ error = StandardError.new
+ expect(Bundler::FriendlyErrors).to receive(:request_issue_report_for).with(error)
+ Bundler::FriendlyErrors.log_error(error)
+ end
+ end
+ end
+
+ describe "#exit_status" do
+ it "calls status_code for BundlerError" do
+ error = Bundler::BundlerError.new
+ expect(error).to receive(:status_code).and_return("sample_status_code")
+ expect(Bundler::FriendlyErrors.exit_status(error)).to eq("sample_status_code")
+ end
+
+ it "returns 15 for Thor::Error" do
+ error = Bundler::Thor::Error.new
+ expect(Bundler::FriendlyErrors.exit_status(error)).to eq(15)
+ end
+
+ it "calls status for SystemExit" do
+ error = SystemExit.new
+ expect(error).to receive(:status).and_return("sample_status")
+ expect(Bundler::FriendlyErrors.exit_status(error)).to eq("sample_status")
+ end
+
+ it "returns 1 in other cases" do
+ error = StandardError.new
+ expect(Bundler::FriendlyErrors.exit_status(error)).to eq(1)
+ end
+ end
+
+ describe "#request_issue_report_for" do
+ it "calls relevant methods for Bundler.ui" do
+ expect(Bundler.ui).to receive(:info)
+ expect(Bundler.ui).to receive(:error)
+ expect(Bundler.ui).to receive(:warn)
+ Bundler::FriendlyErrors.request_issue_report_for(StandardError.new)
+ end
+
+ it "includes error class, message and backlog" do
+ error = StandardError.new
+ allow(Bundler::FriendlyErrors).to receive(:issues_url).and_return("")
+
+ expect(error).to receive(:class).at_least(:once)
+ expect(error).to receive(:message).at_least(:once)
+ expect(error).to receive(:backtrace).at_least(:once)
+ Bundler::FriendlyErrors.request_issue_report_for(error)
+ end
+ end
+
+ describe "#issues_url" do
+ it "generates a search URL for the exception message" do
+ exception = Exception.new("Exception message")
+
+ expect(Bundler::FriendlyErrors.issues_url(exception)).to eq("https://github.com/bundler/bundler/search?q=Exception+message&type=Issues")
+ end
+
+ it "generates a search URL for only the first line of a multi-line exception message" do
+ exception = Exception.new(<<END)
+First line of the exception message
+Second line of the exception message
+END
+
+ expect(Bundler::FriendlyErrors.issues_url(exception)).to eq("https://github.com/bundler/bundler/search?q=First+line+of+the+exception+message&type=Issues")
+ end
+
+ it "generates the url without colons" do
+ exception = Exception.new(<<END)
+Exception ::: with ::: colons :::
+END
+ issues_url = Bundler::FriendlyErrors.issues_url(exception)
+ expect(issues_url).not_to include("%3A")
+ expect(issues_url).to eq("https://github.com/bundler/bundler/search?q=#{CGI.escape("Exception with colons ")}&type=Issues")
+ end
+
+ it "removes information after - for Errono::EACCES" do
+ exception = Exception.new(<<END)
+Errno::EACCES: Permission denied @ dir_s_mkdir - /Users/foo/bar/
+END
+ allow(exception).to receive(:is_a?).with(Errno).and_return(true)
+ issues_url = Bundler::FriendlyErrors.issues_url(exception)
+ expect(issues_url).not_to include("/Users/foo/bar")
+ expect(issues_url).to eq("https://github.com/bundler/bundler/search?q=#{CGI.escape("Errno EACCES Permission denied @ dir_s_mkdir ")}&type=Issues")
+ end
+ end
+end
diff --git a/spec/bundler/bundler/gem_helper_spec.rb b/spec/bundler/bundler/gem_helper_spec.rb
new file mode 100644
index 0000000000..a627129fe3
--- /dev/null
+++ b/spec/bundler/bundler/gem_helper_spec.rb
@@ -0,0 +1,351 @@
+# frozen_string_literal: true
+
+require "rake"
+require "bundler/gem_helper"
+
+RSpec.describe Bundler::GemHelper do
+ let(:app_name) { "lorem__ipsum" }
+ let(:app_path) { bundled_app app_name }
+ let(:app_gemspec_path) { app_path.join("#{app_name}.gemspec") }
+
+ before(:each) do
+ global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false"
+ bundle "gem #{app_name}"
+ end
+
+ context "determining gemspec" do
+ subject { Bundler::GemHelper.new(app_path) }
+
+ context "fails" do
+ it "when there is no gemspec" do
+ FileUtils.rm app_gemspec_path
+ expect { subject }.to raise_error(/Unable to determine name/)
+ end
+
+ it "when there are two gemspecs and the name isn't specified" do
+ FileUtils.touch app_path.join("#{app_name}-2.gemspec")
+ expect { subject }.to raise_error(/Unable to determine name/)
+ end
+ end
+
+ context "interpolates the name" do
+ before do
+ # Remove exception that prevents public pushes on older RubyGems versions
+ if Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.0")
+ content = File.read(app_gemspec_path)
+ content.sub!(/raise "RubyGems 2\.0 or newer.*/, "")
+ File.open(app_gemspec_path, "w") {|f| f.write(content) }
+ end
+ end
+
+ it "when there is only one gemspec" do
+ expect(subject.gemspec.name).to eq(app_name)
+ end
+
+ it "for a hidden gemspec" do
+ FileUtils.mv app_gemspec_path, app_path.join(".gemspec")
+ expect(subject.gemspec.name).to eq(app_name)
+ end
+ end
+
+ it "handles namespaces and converts them to CamelCase" do
+ bundle "gem #{app_name}-foo_bar"
+ underscore_path = bundled_app "#{app_name}-foo_bar"
+
+ lib = underscore_path.join("lib/#{app_name}/foo_bar.rb").read
+ expect(lib).to include("module LoremIpsum")
+ expect(lib).to include("module FooBar")
+ end
+ end
+
+ context "gem management" do
+ def mock_confirm_message(message)
+ expect(Bundler.ui).to receive(:confirm).with(message)
+ end
+
+ def mock_build_message(name, version)
+ message = "#{name} #{version} built to pkg/#{name}-#{version}.gem."
+ mock_confirm_message message
+ end
+
+ subject! { Bundler::GemHelper.new(app_path) }
+ let(:app_version) { "0.1.0" }
+ let(:app_gem_dir) { app_path.join("pkg") }
+ let(:app_gem_path) { app_gem_dir.join("#{app_name}-#{app_version}.gem") }
+ let(:app_gemspec_content) { remove_push_guard(File.read(app_gemspec_path)) }
+
+ before(:each) do
+ content = app_gemspec_content.gsub("TODO: ", "")
+ content.sub!(/homepage\s+= ".*"/, 'homepage = ""')
+ content.gsub!(/spec\.metadata.+\n/, "")
+ File.open(app_gemspec_path, "w") {|file| file << content }
+ end
+
+ def remove_push_guard(gemspec_content)
+ # Remove exception that prevents public pushes on older RubyGems versions
+ if Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.0")
+ gemspec_content.sub!(/raise "RubyGems 2\.0 or newer.*/, "")
+ end
+ gemspec_content
+ end
+
+ it "uses a shell UI for output" do
+ expect(Bundler.ui).to be_a(Bundler::UI::Shell)
+ end
+
+ describe "#install" do
+ let!(:rake_application) { Rake.application }
+
+ before(:each) do
+ Rake.application = Rake::Application.new
+ end
+
+ after(:each) do
+ Rake.application = rake_application
+ end
+
+ context "defines Rake tasks" do
+ let(:task_names) do
+ %w[build install release release:guard_clean
+ release:source_control_push release:rubygem_push]
+ end
+
+ context "before installation" do
+ it "raises an error with appropriate message" do
+ task_names.each do |name|
+ expect { Rake.application[name] }.
+ to raise_error(/^Don't know how to build task '#{name}'/)
+ end
+ end
+ end
+
+ context "after installation" do
+ before do
+ subject.install
+ end
+
+ it "adds Rake tasks successfully" do
+ task_names.each do |name|
+ expect { Rake.application[name] }.not_to raise_error
+ expect(Rake.application[name]).to be_instance_of Rake::Task
+ end
+ end
+
+ it "provides a way to access the gemspec object" do
+ expect(subject.gemspec.name).to eq(app_name)
+ end
+ end
+ end
+ end
+
+ describe "#build_gem" do
+ context "when build failed" do
+ it "raises an error with appropriate message" do
+ # break the gemspec by adding back the TODOs
+ File.open(app_gemspec_path, "w") {|file| file << app_gemspec_content }
+ expect { subject.build_gem }.to raise_error(/TODO/)
+ end
+ end
+
+ context "when build was successful" do
+ it "creates .gem file" do
+ mock_build_message app_name, app_version
+ subject.build_gem
+ expect(app_gem_path).to exist
+ end
+ end
+ end
+
+ describe "#install_gem" do
+ context "when installation was successful" do
+ it "gem is installed" do
+ mock_build_message app_name, app_version
+ mock_confirm_message "#{app_name} (#{app_version}) installed."
+ subject.install_gem(nil, :local)
+ expect(app_gem_path).to exist
+ gem_command! :list
+ expect(out).to include("#{app_name} (#{app_version})")
+ end
+ end
+
+ context "when installation fails" do
+ it "raises an error with appropriate message" do
+ # create empty gem file in order to simulate install failure
+ allow(subject).to receive(:build_gem) do
+ FileUtils.mkdir_p(app_gem_dir)
+ FileUtils.touch app_gem_path
+ app_gem_path
+ end
+ expect { subject.install_gem }.to raise_error(/Couldn't install gem/)
+ end
+ end
+ end
+
+ describe "rake release" do
+ let!(:rake_application) { Rake.application }
+
+ before(:each) do
+ Rake.application = Rake::Application.new
+ subject.install
+ end
+
+ after(:each) do
+ Rake.application = rake_application
+ end
+
+ before do
+ Dir.chdir(app_path) do
+ `git init`
+ `git config user.email "you@example.com"`
+ `git config user.name "name"`
+ `git config push.default simple`
+ end
+
+ # silence messages
+ allow(Bundler.ui).to receive(:confirm)
+ allow(Bundler.ui).to receive(:error)
+ end
+
+ context "fails" do
+ it "when there are unstaged files" do
+ expect { Rake.application["release"].invoke }.
+ to raise_error("There are files that need to be committed first.")
+ end
+
+ it "when there are uncommitted files" do
+ Dir.chdir(app_path) { `git add .` }
+ expect { Rake.application["release"].invoke }.
+ to raise_error("There are files that need to be committed first.")
+ end
+
+ it "when there is no git remote" do
+ Dir.chdir(app_path) { `git commit -a -m "initial commit"` }
+ expect { Rake.application["release"].invoke }.to raise_error(RuntimeError)
+ end
+ end
+
+ context "succeeds" do
+ before do
+ Dir.chdir(gem_repo1) { `git init --bare` }
+ Dir.chdir(app_path) do
+ `git remote add origin file://#{gem_repo1}`
+ `git commit -a -m "initial commit"`
+ end
+ end
+
+ it "on releasing" do
+ mock_build_message app_name, app_version
+ mock_confirm_message "Tagged v#{app_version}."
+ mock_confirm_message "Pushed git commits and tags."
+ expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
+
+ Dir.chdir(app_path) { sys_exec("git push -u origin master") }
+
+ Rake.application["release"].invoke
+ end
+
+ it "even if tag already exists" do
+ mock_build_message app_name, app_version
+ mock_confirm_message "Tag v#{app_version} has already been created."
+ expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
+
+ Dir.chdir(app_path) do
+ `git tag -a -m \"Version #{app_version}\" v#{app_version}`
+ end
+
+ Rake.application["release"].invoke
+ end
+ end
+ end
+
+ describe "release:rubygem_push" do
+ let!(:rake_application) { Rake.application }
+
+ before(:each) do
+ Rake.application = Rake::Application.new
+ subject.install
+ allow(subject).to receive(:sh)
+ end
+
+ after(:each) do
+ Rake.application = rake_application
+ end
+
+ before do
+ Dir.chdir(app_path) do
+ `git init`
+ `git config user.email "you@example.com"`
+ `git config user.name "name"`
+ `git config push.default simple`
+ end
+
+ # silence messages
+ allow(Bundler.ui).to receive(:confirm)
+ allow(Bundler.ui).to receive(:error)
+
+ credentials = double("credentials", "file?" => true)
+ allow(Bundler.user_home).to receive(:join).
+ with(".gem/credentials").and_return(credentials)
+ end
+
+ describe "success messaging" do
+ context "No allowed_push_host set" do
+ before do
+ allow(subject).to receive(:allowed_push_host).and_return(nil)
+ end
+
+ around do |example|
+ orig_host = ENV["RUBYGEMS_HOST"]
+ ENV["RUBYGEMS_HOST"] = rubygems_host_env
+
+ example.run
+
+ ENV["RUBYGEMS_HOST"] = orig_host
+ end
+
+ context "RUBYGEMS_HOST env var is set" do
+ let(:rubygems_host_env) { "https://custom.env.gemhost.com" }
+
+ it "should report successful push to the host from the environment" do
+ mock_confirm_message "Pushed #{app_name} #{app_version} to #{rubygems_host_env}"
+
+ Rake.application["release:rubygem_push"].invoke
+ end
+ end
+
+ context "RUBYGEMS_HOST env var is not set" do
+ let(:rubygems_host_env) { nil }
+
+ it "should report successful push to rubygems.org" do
+ mock_confirm_message "Pushed #{app_name} #{app_version} to rubygems.org"
+
+ Rake.application["release:rubygem_push"].invoke
+ end
+ end
+
+ context "RUBYGEMS_HOST env var is an empty string" do
+ let(:rubygems_host_env) { "" }
+
+ it "should report successful push to rubygems.org" do
+ mock_confirm_message "Pushed #{app_name} #{app_version} to rubygems.org"
+
+ Rake.application["release:rubygem_push"].invoke
+ end
+ end
+ end
+
+ context "allowed_push_host set in gemspec" do
+ before do
+ allow(subject).to receive(:allowed_push_host).and_return("https://my.gemhost.com")
+ end
+
+ it "should report successful push to the allowed gem host" do
+ mock_confirm_message "Pushed #{app_name} #{app_version} to https://my.gemhost.com"
+
+ Rake.application["release:rubygem_push"].invoke
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/gem_version_promoter_spec.rb b/spec/bundler/bundler/gem_version_promoter_spec.rb
new file mode 100644
index 0000000000..01e0232fba
--- /dev/null
+++ b/spec/bundler/bundler/gem_version_promoter_spec.rb
@@ -0,0 +1,179 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::GemVersionPromoter do
+ context "conservative resolver" do
+ def versions(result)
+ result.flatten.map(&:version).map(&:to_s)
+ 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
+ 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
+ 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
+ end
+
+ def build_spec_groups(name, versions)
+ versions.map do |v|
+ Bundler::Resolver::SpecGroup.new(build_spec(name, v))
+ 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
+
+ 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
+
+ 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]
+ 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]
+ 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]
+ 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
+
+ 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]
+ 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]
+ 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]
+ 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]
+ end
+ end
+
+ context "level error handling" do
+ subject { Bundler::GemVersionPromoter.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 symbols" do
+ [:major, :minor, :patch].each do |value|
+ subject.level = value
+ expect(subject.level).to eq value
+ 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
+ end
+ end
+
+ context "debug output" do
+ it "should not kerblooie on its own debug output" do
+ gvp = unlocking(:level => :patch)
+ dep = Bundler::DepProxy.new(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
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/index_spec.rb b/spec/bundler/bundler/index_spec.rb
new file mode 100644
index 0000000000..0f3f6e4944
--- /dev/null
+++ b/spec/bundler/bundler/index_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Index do
+ let(:specs) { [] }
+ subject { described_class.build {|i| i.use(specs) } }
+
+ context "specs with a nil platform" do
+ let(:spec) do
+ Gem::Specification.new do |s|
+ s.name = "json"
+ s.version = "1.8.3"
+ allow(s).to receive(:platform).and_return(nil)
+ end
+ end
+ let(:specs) { [spec] }
+
+ describe "#search_by_spec" do
+ it "finds the spec when a nil platform is specified" do
+ expect(subject.search(spec)).to eq([spec])
+ end
+
+ it "finds the spec when a ruby platform is specified" do
+ query = spec.dup.tap {|s| s.platform = "ruby" }
+ expect(subject.search(query)).to eq([spec])
+ end
+ end
+ end
+
+ context "with specs that include development dependencies" do
+ let(:specs) { [*build_spec("a", "1.0.0") {|s| s.development("b", "~> 1.0") }] }
+
+ it "does not include b in #dependency_names" do
+ expect(subject.dependency_names).not_to include("b")
+ end
+ end
+end
diff --git a/spec/bundler/bundler/installer/gem_installer_spec.rb b/spec/bundler/bundler/installer/gem_installer_spec.rb
new file mode 100644
index 0000000000..7340a3acc0
--- /dev/null
+++ b/spec/bundler/bundler/installer/gem_installer_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require "bundler/installer/gem_installer"
+
+RSpec.describe Bundler::GemInstaller do
+ let(:installer) { instance_double("Installer") }
+ let(:spec_source) { instance_double("SpecSource") }
+ let(:spec) { instance_double("Specification", :name => "dummy", :version => "0.0.1", :loaded_from => "dummy", :source => spec_source) }
+
+ subject { described_class.new(spec, installer) }
+
+ context "spec_settings is nil" do
+ it "invokes install method with empty build_args", :rubygems => ">= 2" do
+ allow(spec_source).to receive(:install).with(spec, :force => false, :ensure_builtin_gems_cached => false, :build_args => [])
+ subject.install_from_spec
+ end
+ end
+
+ context "spec_settings is build option" do
+ it "invokes install method with build_args", :rubygems => ">= 2" do
+ allow(Bundler.settings).to receive(:[]).with(:bin)
+ 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"])
+ subject.install_from_spec
+ end
+ end
+end
diff --git a/spec/bundler/bundler/installer/parallel_installer_spec.rb b/spec/bundler/bundler/installer/parallel_installer_spec.rb
new file mode 100644
index 0000000000..ace5c1a23a
--- /dev/null
+++ b/spec/bundler/bundler/installer/parallel_installer_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require "bundler/installer/parallel_installer"
+
+RSpec.describe Bundler::ParallelInstaller do
+ let(:installer) { instance_double("Installer") }
+ let(:all_specs) { [] }
+ let(:size) { 1 }
+ let(:standalone) { false }
+ let(:force) { false }
+
+ 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
+end
diff --git a/spec/bundler/bundler/installer/spec_installation_spec.rb b/spec/bundler/bundler/installer/spec_installation_spec.rb
new file mode 100644
index 0000000000..a9cf09a372
--- /dev/null
+++ b/spec/bundler/bundler/installer/spec_installation_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require "bundler/installer/parallel_installer"
+
+RSpec.describe Bundler::ParallelInstaller::SpecInstallation do
+ let!(:dep) do
+ a_spec = Object.new
+ def a_spec.name
+ "I like tests"
+ end
+ a_spec
+ end
+
+ describe "#ready_to_enqueue?" do
+ context "when in enqueued state" do
+ it "is falsey" do
+ spec = described_class.new(dep)
+ spec.state = :enqueued
+ expect(spec.ready_to_enqueue?).to be_falsey
+ end
+ end
+
+ context "when in installed state" do
+ it "returns falsey" do
+ spec = described_class.new(dep)
+ spec.state = :installed
+ expect(spec.ready_to_enqueue?).to be_falsey
+ end
+ end
+
+ it "returns truthy" do
+ spec = described_class.new(dep)
+ expect(spec.ready_to_enqueue?).to be_truthy
+ end
+ end
+
+ describe "#dependencies_installed?" do
+ context "when all dependencies are installed" do
+ it "returns true" do
+ dependencies = []
+ dependencies << instance_double("SpecInstallation", :spec => "alpha", :name => "alpha", :installed? => true, :all_dependencies => [], :type => :production)
+ dependencies << instance_double("SpecInstallation", :spec => "beta", :name => "beta", :installed? => true, :all_dependencies => [], :type => :production)
+ all_specs = dependencies + [instance_double("SpecInstallation", :spec => "gamma", :name => "gamma", :installed? => false, :all_dependencies => [], :type => :production)]
+ spec = described_class.new(dep)
+ allow(spec).to receive(:all_dependencies).and_return(dependencies)
+ expect(spec.dependencies_installed?(all_specs)).to be_truthy
+ end
+ end
+
+ context "when all dependencies are not installed" do
+ it "returns false" do
+ dependencies = []
+ dependencies << instance_double("SpecInstallation", :spec => "alpha", :name => "alpha", :installed? => false, :all_dependencies => [], :type => :production)
+ dependencies << instance_double("SpecInstallation", :spec => "beta", :name => "beta", :installed? => true, :all_dependencies => [], :type => :production)
+ all_specs = dependencies + [instance_double("SpecInstallation", :spec => "gamma", :name => "gamma", :installed? => false, :all_dependencies => [], :type => :production)]
+ spec = described_class.new(dep)
+ allow(spec).to receive(:all_dependencies).and_return(dependencies)
+ expect(spec.dependencies_installed?(all_specs)).to be_falsey
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/lockfile_parser_spec.rb b/spec/bundler/bundler/lockfile_parser_spec.rb
new file mode 100644
index 0000000000..3a6d61336f
--- /dev/null
+++ b/spec/bundler/bundler/lockfile_parser_spec.rb
@@ -0,0 +1,153 @@
+# frozen_string_literal: true
+
+require "bundler/lockfile_parser"
+
+RSpec.describe Bundler::LockfileParser do
+ let(:lockfile_contents) { strip_whitespace(<<-L) }
+ GIT
+ remote: https://github.com/alloy/peiji-san.git
+ revision: eca485d8dc95f12aaec1a434b49d295c7e91844b
+ specs:
+ peiji-san (1.2.0)
+
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ rake (10.3.2)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ peiji-san!
+ rake
+
+ RUBY VERSION
+ ruby 2.1.3p242
+
+ BUNDLED WITH
+ 1.12.0.rc.2
+ L
+
+ describe ".sections_in_lockfile" do
+ it "returns the attributes" do
+ attributes = described_class.sections_in_lockfile(lockfile_contents)
+ expect(attributes).to contain_exactly(
+ "BUNDLED WITH", "DEPENDENCIES", "GEM", "GIT", "PLATFORMS", "RUBY VERSION"
+ )
+ end
+ end
+
+ describe ".unknown_sections_in_lockfile" do
+ let(:lockfile_contents) { strip_whitespace(<<-L) }
+ UNKNOWN ATTR
+
+ UNKNOWN ATTR 2
+ random contents
+ L
+
+ it "returns the unknown attributes" do
+ attributes = described_class.unknown_sections_in_lockfile(lockfile_contents)
+ expect(attributes).to contain_exactly("UNKNOWN ATTR", "UNKNOWN ATTR 2")
+ end
+ end
+
+ describe ".sections_to_ignore" do
+ subject { described_class.sections_to_ignore(base_version) }
+
+ context "with a nil base version" do
+ let(:base_version) { nil }
+
+ it "returns the same as > 1.0" do
+ expect(subject).to contain_exactly(
+ described_class::BUNDLED, described_class::RUBY, described_class::PLUGIN
+ )
+ end
+ end
+
+ context "with a prerelease base version" do
+ let(:base_version) { Gem::Version.create("1.11.0.rc.1") }
+
+ it "returns the same as for the release version" do
+ expect(subject).to contain_exactly(
+ described_class::RUBY, described_class::PLUGIN
+ )
+ end
+ end
+
+ context "with a current version" do
+ let(:base_version) { Gem::Version.create(Bundler::VERSION) }
+
+ it "returns an empty array" do
+ expect(subject).to eq([])
+ end
+ end
+
+ context "with a future version" do
+ let(:base_version) { Gem::Version.create("5.5.5") }
+
+ it "returns an empty array" do
+ expect(subject).to eq([])
+ end
+ end
+ end
+
+ describe "#initialize" do
+ before { allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app("gems.rb")) }
+ subject { described_class.new(lockfile_contents) }
+
+ let(:sources) do
+ [Bundler::Source::Git.new("uri" => "https://github.com/alloy/peiji-san.git", "revision" => "eca485d8dc95f12aaec1a434b49d295c7e91844b"),
+ Bundler::Source::Rubygems.new("remotes" => ["https://rubygems.org"])]
+ end
+ let(:dependencies) do
+ {
+ "peiji-san" => Bundler::Dependency.new("peiji-san", ">= 0"),
+ "rake" => Bundler::Dependency.new("rake", ">= 0"),
+ }
+ end
+ let(:specs) do
+ [
+ Bundler::LazySpecification.new("peiji-san", v("1.2.0"), rb),
+ Bundler::LazySpecification.new("rake", v("10.3.2"), rb),
+ ]
+ end
+ let(:platforms) { [rb] }
+ let(:bundler_version) { Gem::Version.new("1.12.0.rc.2") }
+ let(:ruby_version) { "ruby 2.1.3p242" }
+
+ shared_examples_for "parsing" do
+ it "parses correctly" do
+ expect(subject.sources).to eq sources
+ expect(subject.dependencies).to eq dependencies
+ expect(subject.specs).to eq specs
+ expect(Hash[subject.specs.map {|s| [s, s.dependencies] }]).to eq Hash[subject.specs.map {|s| [s, s.dependencies] }]
+ expect(subject.platforms).to eq platforms
+ expect(subject.bundler_version).to eq bundler_version
+ expect(subject.ruby_version).to eq ruby_version
+ end
+ end
+
+ include_examples "parsing"
+
+ context "when an extra section is at the end" do
+ let(:lockfile_contents) { super() + "\n\nFOO BAR\n baz\n baa\n qux\n" }
+ include_examples "parsing"
+ end
+
+ context "when an extra section is at the start" do
+ let(:lockfile_contents) { "FOO BAR\n baz\n baa\n qux\n\n" + super() }
+ include_examples "parsing"
+ end
+
+ context "when an extra section is in the middle" do
+ let(:lockfile_contents) { super().split(/(?=GEM)/).insert(1, "FOO BAR\n baz\n baa\n qux\n\n").join }
+ include_examples "parsing"
+ end
+
+ context "when a dependency has options" do
+ let(:lockfile_contents) { super().sub("peiji-san!", "peiji-san!\n foo: bar") }
+ include_examples "parsing"
+ end
+ end
+end
diff --git a/spec/bundler/bundler/mirror_spec.rb b/spec/bundler/bundler/mirror_spec.rb
new file mode 100644
index 0000000000..acd0895f2f
--- /dev/null
+++ b/spec/bundler/bundler/mirror_spec.rb
@@ -0,0 +1,329 @@
+# frozen_string_literal: true
+
+require "bundler/mirror"
+
+RSpec.describe Bundler::Settings::Mirror do
+ let(:mirror) { Bundler::Settings::Mirror.new }
+
+ it "returns zero when fallback_timeout is not set" do
+ expect(mirror.fallback_timeout).to eq(0)
+ end
+
+ it "takes a number as a fallback_timeout" do
+ mirror.fallback_timeout = 1
+ expect(mirror.fallback_timeout).to eq(1)
+ end
+
+ it "takes truthy as a default fallback timeout" do
+ mirror.fallback_timeout = true
+ expect(mirror.fallback_timeout).to eq(0.1)
+ end
+
+ it "takes falsey as a zero fallback timeout" do
+ mirror.fallback_timeout = false
+ expect(mirror.fallback_timeout).to eq(0)
+ end
+
+ it "takes a string with 'true' as a default fallback timeout" do
+ mirror.fallback_timeout = "true"
+ expect(mirror.fallback_timeout).to eq(0.1)
+ end
+
+ it "takes a string with 'false' as a zero fallback timeout" do
+ mirror.fallback_timeout = "false"
+ expect(mirror.fallback_timeout).to eq(0)
+ end
+
+ it "takes a string for the uri but returns an uri object" do
+ mirror.uri = "http://localhost:9292"
+ expect(mirror.uri).to eq(URI("http://localhost:9292"))
+ end
+
+ it "takes an uri object for the uri" do
+ mirror.uri = URI("http://localhost:9293")
+ expect(mirror.uri).to eq(URI("http://localhost:9293"))
+ end
+
+ context "without a uri" do
+ it "invalidates the mirror" do
+ mirror.validate!
+ expect(mirror.valid?).to be_falsey
+ end
+ end
+
+ context "with an uri" do
+ before { mirror.uri = "http://localhost:9292" }
+
+ context "without a fallback timeout" do
+ it "is not valid by default" do
+ expect(mirror.valid?).to be_falsey
+ end
+
+ context "when probed" do
+ let(:probe) { double }
+
+ context "with a replying mirror" do
+ before do
+ allow(probe).to receive(:replies?).and_return(true)
+ mirror.validate!(probe)
+ end
+
+ it "is valid" do
+ expect(mirror.valid?).to be_truthy
+ end
+ end
+
+ context "with a non replying mirror" do
+ before do
+ allow(probe).to receive(:replies?).and_return(false)
+ mirror.validate!(probe)
+ end
+
+ it "is still valid" do
+ expect(mirror.valid?).to be_truthy
+ end
+ end
+ end
+ end
+
+ context "with a fallback timeout" do
+ before { mirror.fallback_timeout = 1 }
+
+ it "is not valid by default" do
+ expect(mirror.valid?).to be_falsey
+ end
+
+ context "when probed" do
+ let(:probe) { double }
+
+ context "with a replying mirror" do
+ before do
+ allow(probe).to receive(:replies?).and_return(true)
+ mirror.validate!(probe)
+ end
+
+ it "is valid" do
+ expect(mirror.valid?).to be_truthy
+ end
+
+ it "is validated only once" do
+ allow(probe).to receive(:replies?).and_raise("Only once!")
+ mirror.validate!(probe)
+ expect(mirror.valid?).to be_truthy
+ end
+ end
+
+ context "with a non replying mirror" do
+ before do
+ allow(probe).to receive(:replies?).and_return(false)
+ mirror.validate!(probe)
+ end
+
+ it "is not valid" do
+ expect(mirror.valid?).to be_falsey
+ end
+
+ it "is validated only once" do
+ allow(probe).to receive(:replies?).and_raise("Only once!")
+ mirror.validate!(probe)
+ expect(mirror.valid?).to be_falsey
+ end
+ end
+ end
+ end
+
+ describe "#==" do
+ it "returns true if uri and fallback timeout are the same" do
+ uri = "https://ruby.taobao.org"
+ mirror = Bundler::Settings::Mirror.new(uri, 1)
+ another_mirror = Bundler::Settings::Mirror.new(uri, 1)
+
+ expect(mirror == another_mirror).to be true
+ end
+ end
+ end
+end
+
+RSpec.describe Bundler::Settings::Mirrors do
+ let(:localhost_uri) { URI("http://localhost:9292") }
+
+ context "with a just created mirror" do
+ let(:mirrors) do
+ probe = double
+ allow(probe).to receive(:replies?).and_return(true)
+ Bundler::Settings::Mirrors.new(probe)
+ end
+
+ it "returns a mirror that contains the source uri for an unknown uri" do
+ mirror = mirrors.for("http://rubygems.org/")
+ expect(mirror).to eq(Bundler::Settings::Mirror.new("http://rubygems.org/"))
+ end
+
+ it "parses a mirror key and returns a mirror for the parsed uri" do
+ mirrors.parse("mirror.http://rubygems.org/", localhost_uri)
+ expect(mirrors.for("http://rubygems.org/").uri).to eq(localhost_uri)
+ end
+
+ it "parses a relative mirror key and returns a mirror for the parsed http uri" do
+ mirrors.parse("mirror.rubygems.org", localhost_uri)
+ expect(mirrors.for("http://rubygems.org/").uri).to eq(localhost_uri)
+ end
+
+ it "parses a relative mirror key and returns a mirror for the parsed https uri" do
+ mirrors.parse("mirror.rubygems.org", localhost_uri)
+ expect(mirrors.for("https://rubygems.org/").uri).to eq(localhost_uri)
+ end
+
+ context "with a uri parsed already" do
+ before { mirrors.parse("mirror.http://rubygems.org/", localhost_uri) }
+
+ it "takes a mirror fallback_timeout and assigns the timeout" do
+ mirrors.parse("mirror.http://rubygems.org.fallback_timeout", "2")
+ expect(mirrors.for("http://rubygems.org/").fallback_timeout).to eq(2)
+ end
+
+ it "parses a 'true' fallback timeout and sets the default timeout" do
+ mirrors.parse("mirror.http://rubygems.org.fallback_timeout", "true")
+ expect(mirrors.for("http://rubygems.org/").fallback_timeout).to eq(0.1)
+ end
+
+ it "parses a 'false' fallback timeout and sets it to zero" do
+ mirrors.parse("mirror.http://rubygems.org.fallback_timeout", "false")
+ expect(mirrors.for("http://rubygems.org/").fallback_timeout).to eq(0)
+ end
+ end
+ end
+
+ context "with a mirror prober that replies on time" do
+ let(:mirrors) do
+ probe = double
+ allow(probe).to receive(:replies?).and_return(true)
+ Bundler::Settings::Mirrors.new(probe)
+ end
+
+ context "with a default fallback_timeout for rubygems.org" do
+ before do
+ mirrors.parse("mirror.http://rubygems.org/", localhost_uri)
+ mirrors.parse("mirror.http://rubygems.org.fallback_timeout", "true")
+ end
+
+ it "returns localhost" do
+ expect(mirrors.for("http://rubygems.org").uri).to eq(localhost_uri)
+ end
+ end
+
+ context "with a mirror for all" do
+ before do
+ mirrors.parse("mirror.all", localhost_uri)
+ end
+
+ context "without a fallback timeout" do
+ it "returns localhost uri for rubygems" do
+ expect(mirrors.for("http://rubygems.org").uri).to eq(localhost_uri)
+ end
+
+ it "returns localhost for any other url" do
+ expect(mirrors.for("http://whatever.com/").uri).to eq(localhost_uri)
+ end
+ end
+ context "with a fallback timeout" do
+ before { mirrors.parse("mirror.all.fallback_timeout", "1") }
+
+ it "returns localhost uri for rubygems" do
+ expect(mirrors.for("http://rubygems.org").uri).to eq(localhost_uri)
+ end
+
+ it "returns localhost for any other url" do
+ expect(mirrors.for("http://whatever.com/").uri).to eq(localhost_uri)
+ end
+ end
+ end
+ end
+
+ context "with a mirror prober that does not reply on time" do
+ let(:mirrors) do
+ probe = double
+ allow(probe).to receive(:replies?).and_return(false)
+ Bundler::Settings::Mirrors.new(probe)
+ end
+
+ context "with a localhost mirror for all" do
+ before { mirrors.parse("mirror.all", localhost_uri) }
+
+ context "without a fallback timeout" do
+ it "returns localhost" do
+ expect(mirrors.for("http://whatever.com").uri).to eq(localhost_uri)
+ end
+ end
+
+ context "with a fallback timeout" do
+ before { mirrors.parse("mirror.all.fallback_timeout", "true") }
+
+ it "returns the source uri, not localhost" do
+ expect(mirrors.for("http://whatever.com").uri).to eq(URI("http://whatever.com/"))
+ end
+ end
+ end
+
+ context "with localhost as a mirror for rubygems.org" do
+ before { mirrors.parse("mirror.http://rubygems.org/", localhost_uri) }
+
+ context "without a fallback timeout" do
+ it "returns the uri that is not mirrored" do
+ expect(mirrors.for("http://whatever.com").uri).to eq(URI("http://whatever.com/"))
+ end
+
+ it "returns localhost for rubygems.org" do
+ expect(mirrors.for("http://rubygems.org/").uri).to eq(localhost_uri)
+ end
+ end
+
+ context "with a fallback timeout" do
+ before { mirrors.parse("mirror.http://rubygems.org/.fallback_timeout", "true") }
+
+ it "returns the uri that is not mirrored" do
+ expect(mirrors.for("http://whatever.com").uri).to eq(URI("http://whatever.com/"))
+ end
+
+ it "returns rubygems.org for rubygems.org" do
+ expect(mirrors.for("http://rubygems.org/").uri).to eq(URI("http://rubygems.org/"))
+ end
+ end
+ end
+ end
+end
+
+RSpec.describe Bundler::Settings::TCPSocketProbe do
+ let(:probe) { Bundler::Settings::TCPSocketProbe.new }
+
+ context "with a listening TCP Server" do
+ def with_server_and_mirror
+ server = TCPServer.new("127.0.0.1", 0)
+ mirror = Bundler::Settings::Mirror.new("http://localhost:#{server.addr[1]}", 1)
+ yield server, mirror
+ server.close unless server.closed?
+ end
+
+ it "probes the server correctly", :ruby_repo do
+ with_server_and_mirror do |server, mirror|
+ expect(server.closed?).to be_falsey
+ expect(probe.replies?(mirror)).to be_truthy
+ end
+ end
+
+ it "probes falsey when the server is down" do
+ with_server_and_mirror do |server, mirror|
+ server.close
+ expect(probe.replies?(mirror)).to be_falsey
+ end
+ end
+ end
+
+ context "with an invalid mirror" do
+ let(:mirror) { Bundler::Settings::Mirror.new("http://127.0.0.127:9292", true) }
+
+ it "fails with a timeout when there is nothing to tcp handshake" do
+ expect(probe.replies?(mirror)).to be_falsey
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/api/source_spec.rb b/spec/bundler/bundler/plugin/api/source_spec.rb
new file mode 100644
index 0000000000..2c50ff56a4
--- /dev/null
+++ b/spec/bundler/bundler/plugin/api/source_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin::API::Source do
+ let(:uri) { "uri://to/test" }
+ let(:type) { "spec_type" }
+
+ subject(:source) do
+ klass = Class.new
+ klass.send :include, Bundler::Plugin::API::Source
+ klass.new("uri" => uri, "type" => type)
+ end
+
+ describe "attributes" do
+ it "allows access to uri" do
+ expect(source.uri).to eq("uri://to/test")
+ end
+
+ it "allows access to name" do
+ expect(source.name).to eq("spec_type at uri://to/test")
+ end
+ end
+
+ context "post_install" do
+ let(:installer) { double(:installer) }
+
+ before do
+ allow(Bundler::Source::Path::Installer).to receive(:new) { installer }
+ end
+
+ it "calls Path::Installer's post_install" do
+ expect(installer).to receive(:post_install).once
+
+ source.post_install(double(:spec))
+ end
+ end
+
+ context "install_path" do
+ let(:uri) { "uri://to/a/repository-name" }
+ let(:hash) { Digest(:SHA1).hexdigest(uri) }
+ let(:install_path) { Pathname.new "/bundler/install/path" }
+
+ before do
+ allow(Bundler).to receive(:install_path) { install_path }
+ end
+
+ it "returns basename with uri_hash" do
+ expected = Pathname.new "#{install_path}/repository-name-#{hash[0..11]}"
+ expect(source.install_path).to eq(expected)
+ end
+ end
+
+ context "to_lock" do
+ it "returns the string with remote and type" do
+ expected = strip_whitespace <<-L
+ PLUGIN SOURCE
+ remote: #{uri}
+ type: #{type}
+ specs:
+ L
+
+ expect(source.to_lock).to eq(expected)
+ end
+
+ context "with additional options to lock" do
+ before do
+ allow(source).to receive(:options_to_lock) { { "first" => "option" } }
+ end
+
+ it "includes them" do
+ expected = strip_whitespace <<-L
+ PLUGIN SOURCE
+ remote: #{uri}
+ type: #{type}
+ first: option
+ specs:
+ L
+
+ expect(source.to_lock).to eq(expected)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/api_spec.rb b/spec/bundler/bundler/plugin/api_spec.rb
new file mode 100644
index 0000000000..58fb908572
--- /dev/null
+++ b/spec/bundler/bundler/plugin/api_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin::API do
+ context "plugin declarations" do
+ before do
+ stub_const "UserPluginClass", Class.new(Bundler::Plugin::API)
+ end
+
+ describe "#command" do
+ it "declares a command plugin with same class as handler" do
+ expect(Bundler::Plugin).
+ to receive(:add_command).with("meh", UserPluginClass).once
+
+ UserPluginClass.command "meh"
+ end
+
+ it "accepts another class as argument that handles the command" do
+ stub_const "NewClass", Class.new
+ expect(Bundler::Plugin).to receive(:add_command).with("meh", NewClass).once
+
+ UserPluginClass.command "meh", NewClass
+ end
+ end
+
+ describe "#source" do
+ it "declares a source plugin with same class as handler" do
+ expect(Bundler::Plugin).
+ to receive(:add_source).with("a_source", UserPluginClass).once
+
+ UserPluginClass.source "a_source"
+ end
+
+ it "accepts another class as argument that handles the command" do
+ stub_const "NewClass", Class.new
+ expect(Bundler::Plugin).to receive(:add_source).with("a_source", NewClass).once
+
+ UserPluginClass.source "a_source", NewClass
+ end
+ end
+
+ describe "#hook" do
+ it "accepts a block and passes it to Plugin module" do
+ foo = double("tester")
+ expect(foo).to receive(:called)
+
+ expect(Bundler::Plugin).to receive(:add_hook).with("post-foo").and_yield
+
+ Bundler::Plugin::API.hook("post-foo") { foo.called }
+ end
+ end
+ end
+
+ context "bundler interfaces provided" do
+ before do
+ stub_const "UserPluginClass", Class.new(Bundler::Plugin::API)
+ end
+
+ subject(:api) { UserPluginClass.new }
+
+ # A test of delegation
+ it "provides the Bundler's functions" do
+ expect(Bundler).to receive(:an_unknown_function).once
+
+ api.an_unknown_function
+ end
+
+ it "includes Bundler::SharedHelpers' functions" do
+ expect(Bundler::SharedHelpers).to receive(:an_unknown_helper).once
+
+ api.an_unknown_helper
+ end
+
+ context "#tmp" do
+ it "provides a tmp dir" do
+ expect(api.tmp("mytmp")).to be_directory
+ end
+
+ it "accepts multiple names for suffix" do
+ expect(api.tmp("myplugin", "download")).to be_directory
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/dsl_spec.rb b/spec/bundler/bundler/plugin/dsl_spec.rb
new file mode 100644
index 0000000000..be23db3bba
--- /dev/null
+++ b/spec/bundler/bundler/plugin/dsl_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin::DSL do
+ DSL = Bundler::Plugin::DSL
+
+ subject(:dsl) { Bundler::Plugin::DSL.new }
+
+ before do
+ allow(Bundler).to receive(:root) { Pathname.new "/" }
+ end
+
+ describe "it ignores only the methods defined in Bundler::Dsl" do
+ it "doesn't raises error for Dsl methods" do
+ expect { dsl.install_if }.not_to raise_error
+ end
+
+ it "raises error for other methods" do
+ expect { dsl.no_method }.to raise_error(DSL::PluginGemfileError)
+ end
+ end
+
+ describe "source block" do
+ it "adds #source with :type to list and also inferred_plugins list" do
+ expect(dsl).to receive(:plugin).with("bundler-source-news").once
+
+ dsl.source("some_random_url", :type => "news") {}
+
+ expect(dsl.inferred_plugins).to eq(["bundler-source-news"])
+ end
+
+ it "registers a source type plugin only once for multiple declataions" do
+ expect(dsl).to receive(:plugin).with("bundler-source-news").and_call_original.once
+
+ dsl.source("some_random_url", :type => "news") {}
+ dsl.source("another_random_url", :type => "news") {}
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/events_spec.rb b/spec/bundler/bundler/plugin/events_spec.rb
new file mode 100644
index 0000000000..b09e915682
--- /dev/null
+++ b/spec/bundler/bundler/plugin/events_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin::Events do
+ context "plugin events" do
+ describe "#define" do
+ it "raises when redefining a constant" do
+ expect do
+ Bundler::Plugin::Events.send(:define, :GEM_BEFORE_INSTALL_ALL, "another-value")
+ end.to raise_error(ArgumentError)
+ end
+
+ it "can define a new constant" do
+ Bundler::Plugin::Events.send(:define, :NEW_CONSTANT, "value")
+ expect(Bundler::Plugin::Events::NEW_CONSTANT).to eq("value")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/index_spec.rb b/spec/bundler/bundler/plugin/index_spec.rb
new file mode 100644
index 0000000000..ca3476ea2a
--- /dev/null
+++ b/spec/bundler/bundler/plugin/index_spec.rb
@@ -0,0 +1,186 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin::Index do
+ Index = Bundler::Plugin::Index
+
+ before do
+ gemfile ""
+ path = lib_path(plugin_name)
+ index.register_plugin("new-plugin", path.to_s, [path.join("lib").to_s], commands, sources, hooks)
+ end
+
+ let(:plugin_name) { "new-plugin" }
+ let(:commands) { [] }
+ let(:sources) { [] }
+ let(:hooks) { [] }
+
+ subject(:index) { Index.new }
+
+ describe "#register plugin" do
+ it "is available for retrieval" do
+ expect(index.plugin_path(plugin_name)).to eq(lib_path(plugin_name))
+ end
+
+ it "load_paths is available for retrival" do
+ expect(index.load_paths(plugin_name)).to eq([lib_path(plugin_name).join("lib").to_s])
+ end
+
+ it "is persistent" do
+ new_index = Index.new
+ expect(new_index.plugin_path(plugin_name)).to eq(lib_path(plugin_name))
+ end
+
+ it "load_paths are persistent" do
+ new_index = Index.new
+ expect(new_index.load_paths(plugin_name)).to eq([lib_path(plugin_name).join("lib").to_s])
+ end
+ end
+
+ describe "commands" do
+ let(:commands) { ["newco"] }
+
+ it "returns the plugins name on query" do
+ expect(index.command_plugin("newco")).to eq(plugin_name)
+ end
+
+ it "raises error on conflict" do
+ expect do
+ index.register_plugin("aplugin", lib_path("aplugin").to_s, lib_path("aplugin").join("lib").to_s, ["newco"], [], [])
+ end.to raise_error(Index::CommandConflict)
+ end
+
+ it "is persistent" do
+ new_index = Index.new
+ expect(new_index.command_plugin("newco")).to eq(plugin_name)
+ end
+ end
+
+ describe "source" do
+ let(:sources) { ["new_source"] }
+
+ it "returns the plugins name on query" do
+ expect(index.source_plugin("new_source")).to eq(plugin_name)
+ end
+
+ it "raises error on conflict" do
+ expect do
+ index.register_plugin("aplugin", lib_path("aplugin").to_s, lib_path("aplugin").join("lib").to_s, [], ["new_source"], [])
+ end.to raise_error(Index::SourceConflict)
+ end
+
+ it "is persistent" do
+ new_index = Index.new
+ expect(new_index.source_plugin("new_source")).to eq(plugin_name)
+ end
+ end
+
+ describe "hook" do
+ let(:hooks) { ["after-bar"] }
+
+ it "returns the plugins name on query" do
+ expect(index.hook_plugins("after-bar")).to include(plugin_name)
+ end
+
+ it "is persistent" do
+ new_index = Index.new
+ expect(new_index.hook_plugins("after-bar")).to eq([plugin_name])
+ end
+
+ context "that are not registered", :focused do
+ let(:file) { double("index-file") }
+
+ before do
+ index.hook_plugins("not-there")
+ allow(File).to receive(:open).and_yield(file)
+ end
+
+ it "should not save it with next registered hook" do
+ expect(file).to receive(:puts) do |content|
+ expect(content).not_to include("not-there")
+ end
+
+ index.register_plugin("aplugin", lib_path("aplugin").to_s, lib_path("aplugin").join("lib").to_s, [], [], [])
+ end
+ end
+ end
+
+ describe "global index" do
+ before do
+ Dir.chdir(tmp) do
+ Bundler::Plugin.reset!
+ path = lib_path("gplugin")
+ index.register_plugin("gplugin", path.to_s, [path.join("lib").to_s], [], ["glb_source"], [])
+ end
+ end
+
+ it "skips sources" do
+ new_index = Index.new
+ expect(new_index.source_plugin("glb_source")).to be_falsy
+ end
+ end
+
+ describe "after conflict" do
+ let(:commands) { ["foo"] }
+ let(:sources) { ["bar"] }
+ let(:hooks) { ["hoook"] }
+
+ shared_examples "it cleans up" do
+ it "the path" do
+ expect(index.installed?("cplugin")).to be_falsy
+ end
+
+ it "the command" do
+ expect(index.command_plugin("xfoo")).to be_falsy
+ end
+
+ it "the source" do
+ expect(index.source_plugin("xbar")).to be_falsy
+ end
+
+ it "the hook" do
+ expect(index.hook_plugins("xhoook")).to be_empty
+ end
+ end
+
+ context "on command conflict it cleans up" do
+ before do
+ expect do
+ path = lib_path("cplugin")
+ index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["xbar"], ["xhoook"])
+ end.to raise_error(Index::CommandConflict)
+ end
+
+ include_examples "it cleans up"
+ end
+
+ context "on source conflict it cleans up" do
+ before do
+ expect do
+ path = lib_path("cplugin")
+ index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["xfoo"], ["bar"], ["xhoook"])
+ end.to raise_error(Index::SourceConflict)
+ end
+
+ include_examples "it cleans up"
+ end
+
+ context "on command and source conflict it cleans up" do
+ before do
+ expect do
+ path = lib_path("cplugin")
+ index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["bar"], ["xhoook"])
+ end.to raise_error(Index::CommandConflict)
+ end
+
+ include_examples "it cleans up"
+ end
+ end
+
+ describe "readonly disk without home" do
+ it "ignores being unable to create temp home dir" do
+ expect_any_instance_of(Bundler::Plugin::Index).to receive(:global_index_file).
+ and_raise(Bundler::GenericSystemCallError.new("foo", "bar"))
+ Bundler::Plugin::Index.new
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/installer_spec.rb b/spec/bundler/bundler/plugin/installer_spec.rb
new file mode 100644
index 0000000000..f8bf8450c9
--- /dev/null
+++ b/spec/bundler/bundler/plugin/installer_spec.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin::Installer do
+ subject(:installer) { Bundler::Plugin::Installer.new }
+
+ before do
+ # allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(Pathname.new("/Gemfile"))
+ end
+
+ describe "cli install" do
+ it "uses Gem.sources when non of the source is provided" do
+ sources = double(:sources)
+ Bundler.settings # initialize it before we have to touch rubygems.ext_lock
+ allow(Bundler).to receive_message_chain("rubygems.sources") { sources }
+
+ allow(installer).to receive(:install_rubygems).
+ with("new-plugin", [">= 0"], sources).once
+
+ installer.install("new-plugin", {})
+ end
+
+ describe "with mocked installers" do
+ let(:spec) { double(:spec) }
+ it "returns the installed spec after installing git plugins" do
+ allow(installer).to receive(:install_git).
+ and_return("new-plugin" => spec)
+
+ expect(installer.install(["new-plugin"], :git => "https://some.ran/dom")).
+ to eq("new-plugin" => spec)
+ end
+
+ it "returns the installed spec after installing rubygems plugins" do
+ allow(installer).to receive(:install_rubygems).
+ and_return("new-plugin" => spec)
+
+ expect(installer.install(["new-plugin"], :source => "https://some.ran/dom")).
+ to eq("new-plugin" => spec)
+ end
+ end
+
+ describe "with actual installers" do
+ before do
+ build_repo2 do
+ build_plugin "re-plugin"
+ build_plugin "ma-plugin"
+ end
+ end
+
+ context "git plugins" do
+ before do
+ build_git "ga-plugin", :path => lib_path("ga-plugin") do |s|
+ s.write "plugins.rb"
+ end
+ end
+
+ let(:result) do
+ installer.install(["ga-plugin"], :git => "file://#{lib_path("ga-plugin")}")
+ end
+
+ it "returns the installed spec after installing" do
+ spec = result["ga-plugin"]
+ expect(spec.full_name).to eq "ga-plugin-1.0"
+ end
+
+ it "has expected full gem path" do
+ rev = revision_for(lib_path("ga-plugin"))
+ expect(result["ga-plugin"].full_gem_path).
+ to eq(Bundler::Plugin.root.join("bundler", "gems", "ga-plugin-#{rev[0..11]}").to_s)
+ end
+ end
+
+ context "rubygems plugins" do
+ let(:result) do
+ installer.install(["re-plugin"], :source => "file://#{gem_repo2}")
+ end
+
+ it "returns the installed spec after installing " do
+ expect(result["re-plugin"]).to be_kind_of(Bundler::RemoteSpecification)
+ end
+
+ it "has expected full_gem)path" do
+ expect(result["re-plugin"].full_gem_path).
+ to eq(global_plugin_gem("re-plugin-1.0").to_s)
+ end
+ end
+
+ context "multiple plugins" do
+ let(:result) do
+ installer.install(["re-plugin", "ma-plugin"], :source => "file://#{gem_repo2}")
+ end
+
+ it "returns the installed spec after installing " do
+ expect(result["re-plugin"]).to be_kind_of(Bundler::RemoteSpecification)
+ expect(result["ma-plugin"]).to be_kind_of(Bundler::RemoteSpecification)
+ end
+
+ it "has expected full_gem)path" do
+ expect(result["re-plugin"].full_gem_path).to eq(global_plugin_gem("re-plugin-1.0").to_s)
+ expect(result["ma-plugin"].full_gem_path).to eq(global_plugin_gem("ma-plugin-1.0").to_s)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/source_list_spec.rb b/spec/bundler/bundler/plugin/source_list_spec.rb
new file mode 100644
index 0000000000..64a1233dd1
--- /dev/null
+++ b/spec/bundler/bundler/plugin/source_list_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin::SourceList do
+ SourceList = Bundler::Plugin::SourceList
+
+ before do
+ allow(Bundler).to receive(:root) { Pathname.new "/" }
+ end
+
+ subject(:source_list) { SourceList.new }
+
+ describe "adding sources uses classes for plugin" do
+ it "uses Plugin::Installer::Rubygems for rubygems sources" do
+ source = source_list.
+ add_rubygems_source("remotes" => ["https://existing-rubygems.org"])
+ expect(source).to be_instance_of(Bundler::Plugin::Installer::Rubygems)
+ end
+
+ it "uses Plugin::Installer::Git for git sources" do
+ source = source_list.
+ add_git_source("uri" => "git://existing-git.org/path.git")
+ expect(source).to be_instance_of(Bundler::Plugin::Installer::Git)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin_spec.rb b/spec/bundler/bundler/plugin_spec.rb
new file mode 100644
index 0000000000..9266fad1eb
--- /dev/null
+++ b/spec/bundler/bundler/plugin_spec.rb
@@ -0,0 +1,309 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Plugin do
+ Plugin = Bundler::Plugin
+
+ let(:installer) { double(:installer) }
+ let(:index) { double(:index) }
+ let(:spec) { double(:spec) }
+ let(:spec2) { double(:spec2) }
+
+ before do
+ build_lib "new-plugin", :path => lib_path("new-plugin") do |s|
+ s.write "plugins.rb"
+ end
+
+ build_lib "another-plugin", :path => lib_path("another-plugin") do |s|
+ s.write "plugins.rb"
+ end
+
+ allow(spec).to receive(:full_gem_path).
+ and_return(lib_path("new-plugin").to_s)
+ allow(spec).to receive(:load_paths).
+ and_return([lib_path("new-plugin").join("lib").to_s])
+
+ allow(spec2).to receive(:full_gem_path).
+ and_return(lib_path("another-plugin").to_s)
+ allow(spec2).to receive(:load_paths).
+ and_return([lib_path("another-plugin").join("lib").to_s])
+
+ allow(Plugin::Installer).to receive(:new) { installer }
+ allow(Plugin).to receive(:index) { index }
+ allow(index).to receive(:register_plugin)
+ end
+
+ describe "install command" do
+ let(:opts) { { "version" => "~> 1.0", "source" => "foo" } }
+
+ before do
+ allow(installer).to receive(:install).with(["new-plugin"], opts) do
+ { "new-plugin" => spec }
+ end
+ end
+
+ it "passes the name and options to installer" do
+ allow(installer).to receive(:install).with(["new-plugin"], opts) do
+ { "new-plugin" => spec }
+ end.once
+
+ subject.install ["new-plugin"], opts
+ end
+
+ it "validates the installed plugin" do
+ allow(subject).
+ to receive(:validate_plugin!).with(lib_path("new-plugin")).once
+
+ subject.install ["new-plugin"], opts
+ end
+
+ it "registers the plugin with index" do
+ allow(index).to receive(:register_plugin).
+ with("new-plugin", lib_path("new-plugin").to_s, [lib_path("new-plugin").join("lib").to_s], []).once
+ subject.install ["new-plugin"], opts
+ end
+
+ context "multiple plugins" do
+ it do
+ allow(installer).to receive(:install).
+ with(["new-plugin", "another-plugin"], opts) do
+ {
+ "new-plugin" => spec,
+ "another-plugin" => spec2,
+ }
+ end.once
+
+ allow(subject).to receive(:validate_plugin!).twice
+ allow(index).to receive(:register_plugin).twice
+ subject.install ["new-plugin", "another-plugin"], opts
+ end
+ end
+ end
+
+ describe "evaluate gemfile for plugins" do
+ let(:definition) { double("definition") }
+ let(:builder) { double("builder") }
+ let(:gemfile) { bundled_app("Gemfile") }
+
+ before do
+ allow(Plugin::DSL).to receive(:new) { builder }
+ allow(builder).to receive(:eval_gemfile).with(gemfile)
+ allow(builder).to receive(:to_definition) { definition }
+ allow(builder).to receive(:inferred_plugins) { [] }
+ end
+
+ it "doesn't calls installer without any plugins" do
+ allow(definition).to receive(:dependencies) { [] }
+ allow(installer).to receive(:install_definition).never
+
+ subject.gemfile_install(gemfile)
+ end
+
+ context "with dependencies" do
+ let(:plugin_specs) do
+ {
+ "new-plugin" => spec,
+ "another-plugin" => spec2,
+ }
+ end
+
+ before do
+ allow(index).to receive(:installed?) { nil }
+ allow(definition).to receive(:dependencies) { [Bundler::Dependency.new("new-plugin", ">=0"), Bundler::Dependency.new("another-plugin", ">=0")] }
+ allow(installer).to receive(:install_definition) { plugin_specs }
+ end
+
+ it "should validate and register the plugins" do
+ expect(subject).to receive(:validate_plugin!).twice
+ expect(subject).to receive(:register_plugin).twice
+
+ subject.gemfile_install(gemfile)
+ end
+
+ it "should pass the optional plugins to #register_plugin" do
+ allow(builder).to receive(:inferred_plugins) { ["another-plugin"] }
+
+ expect(subject).to receive(:register_plugin).
+ with("new-plugin", spec, false).once
+
+ expect(subject).to receive(:register_plugin).
+ with("another-plugin", spec2, true).once
+
+ subject.gemfile_install(gemfile)
+ end
+ end
+ end
+
+ describe "#command?" do
+ it "returns true value for commands in index" do
+ allow(index).
+ to receive(:command_plugin).with("newcommand") { "my-plugin" }
+ result = subject.command? "newcommand"
+ expect(result).to be_truthy
+ end
+
+ it "returns false value for commands not in index" do
+ allow(index).to receive(:command_plugin).with("newcommand") { nil }
+ result = subject.command? "newcommand"
+ expect(result).to be_falsy
+ end
+ end
+
+ describe "#exec_command" do
+ it "raises UndefinedCommandError when command is not found" do
+ allow(index).to receive(:command_plugin).with("newcommand") { nil }
+ expect { subject.exec_command("newcommand", []) }.
+ to raise_error(Plugin::UndefinedCommandError)
+ end
+ end
+
+ describe "#source?" do
+ it "returns true value for sources in index" do
+ allow(index).
+ to receive(:command_plugin).with("foo-source") { "my-plugin" }
+ result = subject.command? "foo-source"
+ expect(result).to be_truthy
+ end
+
+ it "returns false value for source not in index" do
+ allow(index).to receive(:command_plugin).with("foo-source") { nil }
+ result = subject.command? "foo-source"
+ expect(result).to be_falsy
+ end
+ end
+
+ describe "#source" do
+ it "raises UnknownSourceError when source is not found" do
+ allow(index).to receive(:source_plugin).with("bar") { nil }
+ expect { subject.source("bar") }.
+ to raise_error(Plugin::UnknownSourceError)
+ end
+
+ it "loads the plugin, if not loaded" do
+ allow(index).to receive(:source_plugin).with("foo-bar") { "plugin_name" }
+
+ expect(subject).to receive(:load_plugin).with("plugin_name")
+ subject.source("foo-bar")
+ end
+
+ it "returns the class registered with #add_source" do
+ allow(index).to receive(:source_plugin).with("foo") { "plugin_name" }
+ stub_const "NewClass", Class.new
+
+ subject.add_source("foo", NewClass)
+ expect(subject.source("foo")).to be(NewClass)
+ end
+ end
+
+ describe "#source_from_lock" do
+ it "returns instance of registered class initialized with locked opts" do
+ opts = { "type" => "l_source", "remote" => "xyz", "other" => "random" }
+ allow(index).to receive(:source_plugin).with("l_source") { "plugin_name" }
+
+ stub_const "SClass", Class.new
+ s_instance = double(:s_instance)
+ subject.add_source("l_source", SClass)
+
+ expect(SClass).to receive(:new).
+ with(hash_including("type" => "l_source", "uri" => "xyz", "other" => "random")) { s_instance }
+ expect(subject.source_from_lock(opts)).to be(s_instance)
+ end
+ end
+
+ describe "#root" do
+ context "in app dir" do
+ before do
+ gemfile ""
+ end
+
+ it "returns plugin dir in app .bundle path" do
+ expect(subject.root).to eq(bundled_app.join(".bundle/plugin"))
+ end
+ end
+
+ context "outside app dir" do
+ it "returns plugin dir in global bundle path" do
+ Dir.chdir tmp
+ expect(subject.root).to eq(home.join(".bundle/plugin"))
+ end
+ end
+ end
+
+ describe "#add_hook" do
+ it "raises an ArgumentError on an unregistered event" do
+ ran = false
+ expect do
+ Plugin.add_hook("unregistered-hook") { ran = true }
+ end.to raise_error(ArgumentError)
+ expect(ran).to be(false)
+ end
+ end
+
+ describe "#hook" do
+ before do
+ path = lib_path("foo-plugin")
+ build_lib "foo-plugin", :path => path do |s|
+ s.write "plugins.rb", code
+ end
+
+ Bundler::Plugin::Events.send(:reset)
+ Bundler::Plugin::Events.send(:define, :EVENT_1, "event-1")
+ Bundler::Plugin::Events.send(:define, :EVENT_2, "event-2")
+
+ allow(index).to receive(:hook_plugins).with(Bundler::Plugin::Events::EVENT_1).
+ and_return(["foo-plugin"])
+ allow(index).to receive(:hook_plugins).with(Bundler::Plugin::Events::EVENT_2).
+ and_return(["foo-plugin"])
+ allow(index).to receive(:plugin_path).with("foo-plugin").and_return(path)
+ allow(index).to receive(:load_paths).with("foo-plugin").and_return([])
+ end
+
+ let(:code) { <<-RUBY }
+ Bundler::Plugin::API.hook("event-1") { puts "hook for event 1" }
+ RUBY
+
+ it "raises an ArgumentError on an unregistered event" do
+ expect do
+ Plugin.hook("unregistered-hook")
+ end.to raise_error(ArgumentError)
+ end
+
+ it "executes the hook" do
+ out = capture(:stdout) do
+ Plugin.hook(Bundler::Plugin::Events::EVENT_1)
+ end.strip
+
+ expect(out).to eq("hook for event 1")
+ end
+
+ context "single plugin declaring more than one hook" do
+ let(:code) { <<-RUBY }
+ Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT_1) {}
+ Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT_2) {}
+ puts "loaded"
+ RUBY
+
+ it "evals plugins.rb once" do
+ out = capture(:stdout) do
+ Plugin.hook(Bundler::Plugin::Events::EVENT_1)
+ Plugin.hook(Bundler::Plugin::Events::EVENT_2)
+ end.strip
+
+ expect(out).to eq("loaded")
+ end
+ end
+
+ context "a block is passed" do
+ let(:code) { <<-RUBY }
+ Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT_1) { |&blk| blk.call }
+ RUBY
+
+ it "is passed to the hook" do
+ out = capture(:stdout) do
+ Plugin.hook(Bundler::Plugin::Events::EVENT_1) { puts "win" }
+ end.strip
+
+ expect(out).to eq("win")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/psyched_yaml_spec.rb b/spec/bundler/bundler/psyched_yaml_spec.rb
new file mode 100644
index 0000000000..d5d68c5cc3
--- /dev/null
+++ b/spec/bundler/bundler/psyched_yaml_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require "bundler/psyched_yaml"
+
+RSpec.describe "Bundler::YamlLibrarySyntaxError" do
+ it "is raised on YAML parse errors" do
+ expect { YAML.parse "{foo" }.to raise_error(Bundler::YamlLibrarySyntaxError)
+ end
+end
diff --git a/spec/bundler/bundler/remote_specification_spec.rb b/spec/bundler/bundler/remote_specification_spec.rb
new file mode 100644
index 0000000000..8115e026d8
--- /dev/null
+++ b/spec/bundler/bundler/remote_specification_spec.rb
@@ -0,0 +1,187 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::RemoteSpecification do
+ let(:name) { "foo" }
+ let(:version) { Gem::Version.new("1.0.0") }
+ let(:platform) { Gem::Platform::RUBY }
+ let(:spec_fetcher) { double(:spec_fetcher) }
+
+ subject { described_class.new(name, version, platform, spec_fetcher) }
+
+ it "is Comparable" do
+ expect(described_class.ancestors).to include(Comparable)
+ end
+
+ it "can match platforms" do
+ expect(described_class.ancestors).to include(Bundler::MatchPlatform)
+ end
+
+ describe "#fetch_platform" do
+ let(:remote_spec) { double(:remote_spec, :platform => "jruby") }
+
+ before { allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec) }
+
+ it "should return the spec platform" do
+ expect(subject.fetch_platform).to eq("jruby")
+ end
+ end
+
+ describe "#full_name" do
+ context "when platform is ruby" do
+ it "should return the spec name and version" do
+ expect(subject.full_name).to eq("foo-1.0.0")
+ end
+ end
+
+ context "when platform is nil" do
+ let(:platform) { nil }
+
+ it "should return the spec name and version" do
+ expect(subject.full_name).to eq("foo-1.0.0")
+ end
+ end
+
+ context "when platform is a non-ruby platform" 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")
+ end
+ end
+ end
+
+ describe "#<=>" do
+ let(:other_name) { name }
+ let(:other_version) { version }
+ let(:other_platform) { platform }
+ let(:other_spec_fetcher) { spec_fetcher }
+
+ shared_examples_for "a comparison" do
+ context "which exactly matches" do
+ it "returns 0" do
+ expect(subject <=> other).to eq(0)
+ end
+ end
+
+ context "which is different by name" do
+ let(:other_name) { "a" }
+ it "returns 1" do
+ expect(subject <=> other).to eq(1)
+ end
+ end
+
+ context "which has a lower version" do
+ let(:other_version) { Gem::Version.new("0.9.0") }
+ it "returns 1" do
+ expect(subject <=> other).to eq(1)
+ end
+ end
+
+ context "which has a higher version" do
+ let(:other_version) { Gem::Version.new("1.1.0") }
+ it "returns -1" do
+ expect(subject <=> other).to eq(-1)
+ end
+ end
+
+ context "which has a different platform" do
+ let(:other_platform) { Gem::Platform.new("x86-mswin32") }
+ it "returns -1" do
+ expect(subject <=> other).to eq(-1)
+ end
+ end
+ end
+
+ context "comparing another Bundler::RemoteSpecification" do
+ let(:other) do
+ Bundler::RemoteSpecification.new(other_name, other_version,
+ other_platform, nil)
+ end
+
+ it_should_behave_like "a comparison"
+ end
+
+ context "comparing a Gem::Specification" do
+ let(:other) do
+ Gem::Specification.new(other_name, other_version).tap do |s|
+ s.platform = other_platform
+ end
+ end
+
+ it_should_behave_like "a comparison"
+ end
+
+ context "comparing a non sortable object" do
+ let(:other) { Object.new }
+ let(:remote_spec) { double(:remote_spec, :platform => "jruby") }
+
+ before do
+ allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
+ allow(remote_spec).to receive(:<=>).and_return(nil)
+ end
+
+ it "should use default object comparison" do
+ expect(subject <=> other).to eq(nil)
+ end
+ end
+ end
+
+ describe "#__swap__" do
+ let(:spec) { double(:spec, :dependencies => []) }
+ let(:new_spec) { double(:new_spec, :dependencies => [], :runtime_dependencies => []) }
+
+ before { subject.instance_variable_set(:@_remote_specification, spec) }
+
+ it "should replace remote specification with the passed spec" do
+ expect(subject.instance_variable_get(:@_remote_specification)).to be(spec)
+ subject.__swap__(new_spec)
+ expect(subject.instance_variable_get(:@_remote_specification)).to be(new_spec)
+ end
+ end
+
+ describe "#sort_obj" do
+ context "when platform is ruby" do
+ it "should return a sorting delegate array with name, version, and -1" do
+ expect(subject.sort_obj).to match_array(["foo", version, -1])
+ end
+ end
+
+ context "when platform is not ruby" do
+ let(:platform) { "jruby" }
+
+ it "should return a sorting delegate array with name, version, and 1" do
+ expect(subject.sort_obj).to match_array(["foo", version, 1])
+ end
+ end
+ end
+
+ describe "method missing" do
+ context "and is present in Gem::Specification" do
+ let(:remote_spec) { double(:remote_spec, :authors => "abcd") }
+
+ before do
+ allow(subject).to receive(:_remote_specification).and_return(remote_spec)
+ expect(subject.methods.map(&:to_sym)).not_to include(:authors)
+ end
+
+ it "should send through to Gem::Specification" do
+ expect(subject.authors).to eq("abcd")
+ end
+ end
+ end
+
+ describe "respond to missing?" do
+ context "and is present in Gem::Specification" do
+ let(:remote_spec) { double(:remote_spec, :authors => "abcd") }
+
+ before do
+ allow(subject).to receive(:_remote_specification).and_return(remote_spec)
+ expect(subject.methods.map(&:to_sym)).not_to include(:authors)
+ end
+
+ it "should send through to Gem::Specification" do
+ expect(subject.respond_to?(:authors)).to be_truthy
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/retry_spec.rb b/spec/bundler/bundler/retry_spec.rb
new file mode 100644
index 0000000000..b893580d72
--- /dev/null
+++ b/spec/bundler/bundler/retry_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Retry do
+ it "return successful result if no errors" do
+ attempts = 0
+ result = Bundler::Retry.new(nil, nil, 3).attempt do
+ attempts += 1
+ :success
+ end
+ expect(result).to eq(:success)
+ expect(attempts).to eq(1)
+ end
+
+ it "returns the first valid result" do
+ jobs = [proc { raise "foo" }, proc { :bar }, proc { raise "foo" }]
+ attempts = 0
+ result = Bundler::Retry.new(nil, nil, 3).attempt do
+ attempts += 1
+ jobs.shift.call
+ end
+ expect(result).to eq(:bar)
+ expect(attempts).to eq(2)
+ end
+
+ it "raises the last error" do
+ errors = [StandardError, StandardError, StandardError, Bundler::GemfileNotFound]
+ attempts = 0
+ expect do
+ Bundler::Retry.new(nil, nil, 3).attempt do
+ attempts += 1
+ raise errors.shift
+ end
+ end.to raise_error(Bundler::GemfileNotFound)
+ expect(attempts).to eq(4)
+ end
+
+ it "raises exceptions" do
+ error = Bundler::GemfileNotFound
+ attempts = 0
+ expect do
+ Bundler::Retry.new(nil, error).attempt do
+ attempts += 1
+ raise error
+ end
+ end.to raise_error(error)
+ expect(attempts).to eq(1)
+ end
+
+ context "logging" do
+ let(:error) { Bundler::GemfileNotFound }
+ let(:failure_message) { "Retrying test due to error (2/2): #{error} #{error}" }
+
+ context "with debugging on" do
+ it "print error message with newline" do
+ allow(Bundler.ui).to receive(:debug?).and_return(true)
+ expect(Bundler.ui).to_not receive(:info)
+ expect(Bundler.ui).to receive(:warn).with(failure_message, true)
+
+ expect do
+ Bundler::Retry.new("test", [], 1).attempt do
+ raise error
+ end
+ end.to raise_error(error)
+ end
+ end
+
+ context "with debugging off" do
+ it "print error message with newlines" do
+ allow(Bundler.ui).to receive(:debug?).and_return(false)
+ expect(Bundler.ui).to receive(:info).with("").twice
+ expect(Bundler.ui).to receive(:warn).with(failure_message, false)
+
+ expect do
+ Bundler::Retry.new("test", [], 1).attempt do
+ raise error
+ end
+ end.to raise_error(error)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/ruby_dsl_spec.rb b/spec/bundler/bundler/ruby_dsl_spec.rb
new file mode 100644
index 0000000000..bc1ca98457
--- /dev/null
+++ b/spec/bundler/bundler/ruby_dsl_spec.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+require "bundler/ruby_dsl"
+
+RSpec.describe Bundler::RubyDsl do
+ class MockDSL
+ include Bundler::RubyDsl
+
+ attr_reader :ruby_version
+ end
+
+ let(:dsl) { MockDSL.new }
+ let(:ruby_version) { "2.0.0" }
+ let(:version) { "2.0.0" }
+ let(:engine) { "jruby" }
+ let(:engine_version) { "9000" }
+ let(:patchlevel) { "100" }
+ let(:options) do
+ { :patchlevel => patchlevel,
+ :engine => engine,
+ :engine_version => engine_version }
+ end
+
+ let(:invoke) do
+ proc do
+ args = Array(ruby_version) + [options]
+ dsl.ruby(*args)
+ end
+ end
+
+ subject do
+ invoke.call
+ dsl.ruby_version
+ end
+
+ describe "#ruby_version" do
+ shared_examples_for "it stores the ruby version" do
+ it "stores the version" do
+ expect(subject.versions).to eq(Array(ruby_version))
+ expect(subject.gem_version.version).to eq(version)
+ end
+
+ it "stores the engine details" do
+ expect(subject.engine).to eq(engine)
+ expect(subject.engine_versions).to eq(Array(engine_version))
+ end
+
+ it "stores the patchlevel" do
+ expect(subject.patchlevel).to eq(patchlevel)
+ end
+ end
+
+ context "with a plain version" do
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with a single requirement" do
+ let(:ruby_version) { ">= 2.0.0" }
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with two requirements in the same string" do
+ let(:ruby_version) { ">= 2.0.0, < 3.0" }
+ it "raises an error" do
+ expect { subject }.to raise_error(ArgumentError)
+ end
+ end
+
+ context "with two requirements" do
+ let(:ruby_version) { ["~> 2.0.0", "> 2.0.1"] }
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with multiple engine versions" do
+ let(:engine_version) { ["> 200", "< 300"] }
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with no options hash" do
+ let(:invoke) { proc { dsl.ruby(ruby_version) } }
+
+ let(:patchlevel) { nil }
+ let(:engine) { "ruby" }
+ let(:engine_version) { version }
+
+ it_behaves_like "it stores the ruby version"
+
+ context "and with multiple requirements" do
+ let(:ruby_version) { ["~> 2.0.0", "> 2.0.1"] }
+ let(:engine_version) { ruby_version }
+ it_behaves_like "it stores the ruby version"
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/ruby_version_spec.rb b/spec/bundler/bundler/ruby_version_spec.rb
new file mode 100644
index 0000000000..46a1b2918b
--- /dev/null
+++ b/spec/bundler/bundler/ruby_version_spec.rb
@@ -0,0 +1,524 @@
+# frozen_string_literal: true
+
+require "bundler/ruby_version"
+
+RSpec.describe "Bundler::RubyVersion and its subclasses" do
+ let(:version) { "2.0.0" }
+ let(:patchlevel) { "645" }
+ let(:engine) { "jruby" }
+ let(:engine_version) { "2.0.1" }
+
+ describe Bundler::RubyVersion do
+ subject { Bundler::RubyVersion.new(version, patchlevel, engine, engine_version) }
+
+ let(:ruby_version) { subject }
+ let(:other_version) { version }
+ let(:other_patchlevel) { patchlevel }
+ let(:other_engine) { engine }
+ let(:other_engine_version) { engine_version }
+ let(:other_ruby_version) { Bundler::RubyVersion.new(other_version, other_patchlevel, other_engine, other_engine_version) }
+
+ describe "#initialize" do
+ context "no engine is passed" do
+ let(:engine) { nil }
+
+ it "should set ruby as the engine" do
+ expect(subject.engine).to eq("ruby")
+ end
+ end
+
+ context "no engine_version is passed" do
+ let(:engine_version) { nil }
+
+ it "should set engine version as the passed version" do
+ expect(subject.engine_versions).to eq(["2.0.0"])
+ end
+ end
+
+ context "with engine in symbol" do
+ let(:engine) { :jruby }
+
+ it "should coerce engine to string" do
+ expect(subject.engine).to eq("jruby")
+ end
+ end
+
+ context "is called with multiple requirements" do
+ let(:version) { ["<= 2.0.0", "> 1.9.3"] }
+ let(:engine_version) { nil }
+
+ it "sets the versions" do
+ expect(subject.versions).to eq(version)
+ end
+
+ it "sets the engine versions" do
+ expect(subject.engine_versions).to eq(version)
+ end
+ end
+
+ context "is called with multiple engine requirements" do
+ let(:engine_version) { [">= 2.0", "< 2.3"] }
+
+ it "sets the engine versions" do
+ expect(subject.engine_versions).to eq(engine_version)
+ end
+ end
+ end
+
+ describe ".from_string" do
+ shared_examples_for "returning" do
+ it "returns the original RubyVersion" do
+ expect(described_class.from_string(subject.to_s)).to eq(subject)
+ end
+ end
+
+ include_examples "returning"
+
+ context "no patchlevel" do
+ let(:patchlevel) { nil }
+
+ include_examples "returning"
+ end
+
+ context "engine is ruby" do
+ let(:engine) { "ruby" }
+ let(:engine_version) { version }
+
+ include_examples "returning"
+ end
+
+ context "with multiple requirements" do
+ let(:engine_version) { ["> 9", "< 11"] }
+ let(:version) { ["> 8", "< 10"] }
+ let(:patchlevel) { nil }
+
+ it "returns nil" do
+ expect(described_class.from_string(subject.to_s)).to be_nil
+ end
+ end
+ end
+
+ describe "#to_s" do
+ it "should return info string with the ruby version, patchlevel, engine, and engine version" do
+ expect(subject.to_s).to eq("ruby 2.0.0p645 (jruby 2.0.1)")
+ end
+
+ context "no patchlevel" do
+ let(:patchlevel) { nil }
+
+ it "should return info string with the version, engine, and engine version" do
+ expect(subject.to_s).to eq("ruby 2.0.0 (jruby 2.0.1)")
+ end
+ end
+
+ context "engine is ruby" do
+ let(:engine) { "ruby" }
+
+ it "should return info string with the ruby version and patchlevel" do
+ expect(subject.to_s).to eq("ruby 2.0.0p645")
+ end
+ end
+
+ context "with multiple requirements" do
+ let(:engine_version) { ["> 9", "< 11"] }
+ let(:version) { ["> 8", "< 10"] }
+ let(:patchlevel) { nil }
+
+ it "should return info string with all requirements" do
+ expect(subject.to_s).to eq("ruby > 8, < 10 (jruby > 9, < 11)")
+ end
+ end
+ end
+
+ describe "#==" do
+ shared_examples_for "two ruby versions are not equal" do
+ it "should return false" do
+ expect(subject).to_not eq(other_ruby_version)
+ end
+ end
+
+ context "the versions, pathlevels, engines, and engine_versions match" do
+ it "should return true" do
+ expect(subject).to eq(other_ruby_version)
+ end
+ end
+
+ context "the versions do not match" do
+ let(:other_version) { "1.21.6" }
+
+ it_behaves_like "two ruby versions are not equal"
+ end
+
+ context "the patchlevels do not match" do
+ let(:other_patchlevel) { "21" }
+
+ it_behaves_like "two ruby versions are not equal"
+ end
+
+ context "the engines do not match" do
+ let(:other_engine) { "ruby" }
+
+ it_behaves_like "two ruby versions are not equal"
+ end
+
+ context "the engine versions do not match" do
+ let(:other_engine_version) { "1.11.2" }
+
+ it_behaves_like "two ruby versions are not equal"
+ end
+ end
+
+ describe "#host" do
+ before do
+ allow(RbConfig::CONFIG).to receive(:[]).with("host_cpu").and_return("x86_64")
+ allow(RbConfig::CONFIG).to receive(:[]).with("host_vendor").and_return("apple")
+ allow(RbConfig::CONFIG).to receive(:[]).with("host_os").and_return("darwin14.5.0")
+ end
+
+ it "should return an info string with the host cpu, vendor, and os" do
+ expect(subject.host).to eq("x86_64-apple-darwin14.5.0")
+ end
+
+ it "memoizes the info string with the host cpu, vendor, and os" do
+ expect(RbConfig::CONFIG).to receive(:[]).with("host_cpu").once.and_call_original
+ expect(RbConfig::CONFIG).to receive(:[]).with("host_vendor").once.and_call_original
+ expect(RbConfig::CONFIG).to receive(:[]).with("host_os").once.and_call_original
+ 2.times { ruby_version.host }
+ end
+ end
+
+ describe "#gem_version" do
+ let(:gem_version) { "2.0.0" }
+ let(:gem_version_obj) { Gem::Version.new(gem_version) }
+
+ shared_examples_for "it parses the version from the requirement string" do |version|
+ let(:version) { version }
+ it "should return the underlying version" do
+ expect(ruby_version.gem_version).to eq(gem_version_obj)
+ expect(ruby_version.gem_version.version).to eq(gem_version)
+ end
+ end
+
+ it_behaves_like "it parses the version from the requirement string", "2.0.0"
+ it_behaves_like "it parses the version from the requirement string", ">= 2.0.0"
+ it_behaves_like "it parses the version from the requirement string", "~> 2.0.0"
+ it_behaves_like "it parses the version from the requirement string", "< 2.0.0"
+ it_behaves_like "it parses the version from the requirement string", "= 2.0.0"
+ it_behaves_like "it parses the version from the requirement string", ["> 2.0.0", "< 2.4.5"]
+ end
+
+ describe "#diff" do
+ let(:engine) { "ruby" }
+
+ shared_examples_for "there is a difference in the engines" do
+ it "should return a tuple with :engine and the two different engines" do
+ expect(ruby_version.diff(other_ruby_version)).to eq([:engine, engine, other_engine])
+ end
+ end
+
+ shared_examples_for "there is a difference in the versions" do
+ it "should return a tuple with :version and the two different versions" do
+ expect(ruby_version.diff(other_ruby_version)).to eq([:version, Array(version).join(", "), Array(other_version).join(", ")])
+ end
+ end
+
+ shared_examples_for "there is a difference in the engine versions" do
+ it "should return a tuple with :engine_version and the two different engine versions" do
+ expect(ruby_version.diff(other_ruby_version)).to eq([:engine_version, Array(engine_version).join(", "), Array(other_engine_version).join(", ")])
+ end
+ end
+
+ shared_examples_for "there is a difference in the patchlevels" do
+ it "should return a tuple with :patchlevel and the two different patchlevels" do
+ expect(ruby_version.diff(other_ruby_version)).to eq([:patchlevel, patchlevel, other_patchlevel])
+ end
+ end
+
+ shared_examples_for "there are no differences" do
+ it "should return nil" do
+ expect(ruby_version.diff(other_ruby_version)).to be_nil
+ end
+ end
+
+ context "all things match exactly" do
+ it_behaves_like "there are no differences"
+ end
+
+ context "detects engine discrepancies first" do
+ let(:other_version) { "2.0.1" }
+ let(:other_patchlevel) { "643" }
+ let(:other_engine) { "rbx" }
+ let(:other_engine_version) { "2.0.0" }
+
+ it_behaves_like "there is a difference in the engines"
+ end
+
+ context "detects version discrepancies second" do
+ let(:other_version) { "2.0.1" }
+ let(:other_patchlevel) { "643" }
+ let(:other_engine_version) { "2.0.0" }
+
+ it_behaves_like "there is a difference in the versions"
+ end
+
+ context "detects version discrepancies with multiple requirements second" do
+ let(:other_version) { "2.0.1" }
+ let(:other_patchlevel) { "643" }
+ let(:other_engine_version) { "2.0.0" }
+
+ let(:version) { ["> 2.0.0", "< 1.0.0"] }
+
+ it_behaves_like "there is a difference in the versions"
+ end
+
+ context "detects engine version discrepancies third" do
+ let(:other_patchlevel) { "643" }
+ let(:other_engine_version) { "2.0.0" }
+
+ it_behaves_like "there is a difference in the engine versions"
+ end
+
+ context "detects engine version discrepancies with multiple requirements third" do
+ let(:other_patchlevel) { "643" }
+ let(:other_engine_version) { "2.0.0" }
+
+ let(:engine_version) { ["> 2.0.0", "< 1.0.0"] }
+
+ it_behaves_like "there is a difference in the engine versions"
+ end
+
+ context "detects patchlevel discrepancies last" do
+ let(:other_patchlevel) { "643" }
+
+ it_behaves_like "there is a difference in the patchlevels"
+ end
+
+ context "successfully matches gem requirements" do
+ let(:version) { ">= 2.0.0" }
+ let(:patchlevel) { "< 643" }
+ let(:engine) { "ruby" }
+ let(:engine_version) { "~> 2.0.1" }
+ let(:other_version) { "2.0.0" }
+ let(:other_patchlevel) { "642" }
+ let(:other_engine) { "ruby" }
+ let(:other_engine_version) { "2.0.5" }
+
+ it_behaves_like "there are no differences"
+ end
+
+ context "successfully matches multiple gem requirements" do
+ let(:version) { [">= 2.0.0", "< 2.4.5"] }
+ let(:patchlevel) { "< 643" }
+ let(:engine) { "ruby" }
+ let(:engine_version) { ["~> 2.0.1", "< 2.4.5"] }
+ let(:other_version) { "2.0.0" }
+ let(:other_patchlevel) { "642" }
+ let(:other_engine) { "ruby" }
+ let(:other_engine_version) { "2.0.5" }
+
+ it_behaves_like "there are no differences"
+ end
+
+ context "successfully detects bad gem requirements with versions with multiple requirements" do
+ let(:version) { ["~> 2.0.0", "< 2.0.5"] }
+ let(:patchlevel) { "< 643" }
+ let(:engine) { "ruby" }
+ let(:engine_version) { "~> 2.0.1" }
+ let(:other_version) { "2.0.5" }
+ let(:other_patchlevel) { "642" }
+ let(:other_engine) { "ruby" }
+ let(:other_engine_version) { "2.0.5" }
+
+ it_behaves_like "there is a difference in the versions"
+ end
+
+ context "successfully detects bad gem requirements with versions" do
+ let(:version) { "~> 2.0.0" }
+ let(:patchlevel) { "< 643" }
+ let(:engine) { "ruby" }
+ let(:engine_version) { "~> 2.0.1" }
+ let(:other_version) { "2.1.0" }
+ let(:other_patchlevel) { "642" }
+ let(:other_engine) { "ruby" }
+ let(:other_engine_version) { "2.0.5" }
+
+ it_behaves_like "there is a difference in the versions"
+ end
+
+ context "successfully detects bad gem requirements with patchlevels" do
+ let(:version) { ">= 2.0.0" }
+ let(:patchlevel) { "< 643" }
+ let(:engine) { "ruby" }
+ let(:engine_version) { "~> 2.0.1" }
+ let(:other_version) { "2.0.0" }
+ let(:other_patchlevel) { "645" }
+ let(:other_engine) { "ruby" }
+ let(:other_engine_version) { "2.0.5" }
+
+ it_behaves_like "there is a difference in the patchlevels"
+ end
+
+ context "successfully detects bad gem requirements with engine versions" do
+ let(:version) { ">= 2.0.0" }
+ let(:patchlevel) { "< 643" }
+ let(:engine) { "ruby" }
+ let(:engine_version) { "~> 2.0.1" }
+ let(:other_version) { "2.0.0" }
+ let(:other_patchlevel) { "642" }
+ let(:other_engine) { "ruby" }
+ let(:other_engine_version) { "2.1.0" }
+
+ it_behaves_like "there is a difference in the engine versions"
+ end
+
+ context "with a patchlevel of -1" do
+ let(:version) { ">= 2.0.0" }
+ let(:patchlevel) { "-1" }
+ let(:engine) { "ruby" }
+ let(:engine_version) { "~> 2.0.1" }
+ let(:other_version) { version }
+ let(:other_engine) { engine }
+ let(:other_engine_version) { engine_version }
+
+ context "and comparing with another patchlevel of -1" do
+ let(:other_patchlevel) { patchlevel }
+
+ it_behaves_like "there are no differences"
+ end
+
+ context "and comparing with a patchlevel that is not -1" do
+ let(:other_patchlevel) { "642" }
+
+ it_behaves_like "there is a difference in the patchlevels"
+ end
+ end
+ end
+
+ describe "#system" do
+ subject { Bundler::RubyVersion.system }
+
+ let(:bundler_system_ruby_version) { subject }
+
+ before do
+ Bundler::RubyVersion.instance_variable_set("@ruby_version", nil)
+ end
+
+ it "should return an instance of Bundler::RubyVersion" do
+ expect(subject).to be_kind_of(Bundler::RubyVersion)
+ end
+
+ it "memoizes the instance of Bundler::RubyVersion" do
+ expect(Bundler::RubyVersion).to receive(:new).once.and_call_original
+ 2.times { subject }
+ 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)
+ end
+ end
+
+ describe "#engine" do
+ context "RUBY_ENGINE is defined" do
+ before { stub_const("RUBY_ENGINE", "jruby") }
+ before { stub_const("JRUBY_VERSION", "2.1.1") }
+
+ it "should return a copy of the value of RUBY_ENGINE" do
+ expect(subject.engine).to eq("jruby")
+ expect(subject.engine).to_not be(RUBY_ENGINE)
+ end
+ end
+
+ context "RUBY_ENGINE is not defined" do
+ before { stub_const("RUBY_ENGINE", nil) }
+
+ it "should return the string 'ruby'" do
+ expect(subject.engine).to eq("ruby")
+ end
+ end
+ end
+
+ describe "#engine_version" do
+ context "engine is ruby" do
+ before do
+ stub_const("RUBY_VERSION", "2.2.4")
+ stub_const("RUBY_ENGINE", "ruby")
+ end
+
+ it "should return a copy of the value of RUBY_VERSION" 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_VERSION)
+ end
+ end
+
+ context "engine is rbx" do
+ before do
+ stub_const("RUBY_ENGINE", "rbx")
+ stub_const("Rubinius::VERSION", "2.0.0")
+ end
+
+ it "should return a copy of the value of Rubinius::VERSION" do
+ expect(bundler_system_ruby_version.engine_versions).to eq(["2.0.0"])
+ expect(bundler_system_ruby_version.engine_versions.first).to_not be(Rubinius::VERSION)
+ end
+ end
+
+ context "engine is jruby" do
+ before do
+ stub_const("RUBY_ENGINE", "jruby")
+ stub_const("JRUBY_VERSION", "2.1.1")
+ end
+
+ it "should return a copy of the value of JRUBY_VERSION" do
+ expect(subject.engine_versions).to eq(["2.1.1"])
+ expect(bundler_system_ruby_version.engine_versions.first).to_not be(JRUBY_VERSION)
+ end
+ end
+
+ context "engine is some other ruby engine" do
+ before do
+ stub_const("RUBY_ENGINE", "not_supported_ruby_engine")
+ stub_const("RUBY_ENGINE_VERSION", "1.2.3")
+ end
+
+ it "returns RUBY_ENGINE_VERSION" do
+ expect(bundler_system_ruby_version.engine_versions).to eq(["1.2.3"])
+ end
+ end
+ end
+
+ describe "#patchlevel" do
+ it "should return a string with the value of RUBY_PATCHLEVEL" do
+ expect(subject.patchlevel).to eq(RUBY_PATCHLEVEL.to_s)
+ end
+ end
+ end
+
+ describe "#to_gem_version_with_patchlevel" do
+ shared_examples_for "the patchlevel is omitted" do
+ it "does not include a patch level" do
+ expect(subject.to_gem_version_with_patchlevel.to_s).to eq(version)
+ end
+ end
+
+ context "with nil patch number" do
+ let(:patchlevel) { nil }
+
+ it_behaves_like "the patchlevel is omitted"
+ end
+
+ context "with negative patch number" do
+ let(:patchlevel) { -1 }
+
+ it_behaves_like "the patchlevel is omitted"
+ end
+
+ context "with a valid patch number" do
+ it "uses the specified patchlevel as patchlevel" do
+ expect(subject.to_gem_version_with_patchlevel.to_s).to eq("#{version}.#{patchlevel}")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/rubygems_integration_spec.rb b/spec/bundler/bundler/rubygems_integration_spec.rb
new file mode 100644
index 0000000000..b1b15d9e5d
--- /dev/null
+++ b/spec/bundler/bundler/rubygems_integration_spec.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::RubygemsIntegration do
+ it "uses the same chdir lock as rubygems", :rubygems => "2.1" do
+ expect(Bundler.rubygems.ext_lock).to eq(Gem::Ext::Builder::CHDIR_MONITOR)
+ end
+
+ context "#validate" do
+ let(:spec) do
+ Gem::Specification.new do |s|
+ s.name = "to-validate"
+ s.version = "1.0.0"
+ s.loaded_from = __FILE__
+ end
+ end
+ subject { Bundler.rubygems.validate(spec) }
+
+ it "skips overly-strict gemspec validation", :rubygems => "< 1.7" do
+ expect(spec).to_not receive(:validate)
+ subject
+ end
+
+ it "validates with packaging mode disabled", :rubygems => "1.7" do
+ expect(spec).to receive(:validate).with(false)
+ subject
+ end
+
+ it "should set a summary to avoid an overly-strict error", :rubygems => "~> 1.7.0" do
+ spec.summary = nil
+ expect { subject }.not_to raise_error
+ expect(spec.summary).to eq("")
+ end
+
+ context "with an invalid spec" do
+ before do
+ expect(spec).to receive(:validate).with(false).
+ and_raise(Gem::InvalidSpecificationException.new("TODO is not an author"))
+ end
+
+ it "should raise a Gem::InvalidSpecificationException and produce a helpful warning message",
+ :rubygems => "1.7" do
+ expect { subject }.to raise_error(Gem::InvalidSpecificationException,
+ "The gemspec at #{__FILE__} is not valid. "\
+ "Please fix this gemspec.\nThe validation error was 'TODO is not an author'\n")
+ end
+ end
+ end
+
+ describe "#configuration" do
+ it "handles Gem::SystemExitException errors" do
+ allow(Gem).to receive(:configuration) { raise Gem::SystemExitException.new(1) }
+ expect { Bundler.rubygems.configuration }.to raise_error(Gem::SystemExitException)
+ end
+ end
+
+ describe "#download_gem", :rubygems => ">= 2.0" do
+ let(:bundler_retry) { double(Bundler::Retry) }
+ let(:retry) { double("Bundler::Retry") }
+ let(:uri) { URI.parse("https://foo.bar") }
+ let(:path) { Gem.path.first }
+ let(:spec) do
+ spec = Bundler::RemoteSpecification.new("Foo", Gem::Version.new("2.5.2"),
+ Gem::Platform::RUBY, nil)
+ spec.remote = Bundler::Source::Rubygems::Remote.new(uri.to_s)
+ spec
+ end
+ let(:fetcher) { double("gem_remote_fetcher") }
+
+ it "successfully downloads gem with retries" do
+ expect(Bundler.rubygems).to receive(:gem_remote_fetcher).and_return(fetcher)
+ expect(fetcher).to receive(:headers=).with("X-Gemfile-Source" => "https://foo.bar")
+ expect(Bundler::Retry).to receive(:new).with("download gem from #{uri}/").
+ and_return(bundler_retry)
+ expect(bundler_retry).to receive(:attempts).and_yield
+ expect(fetcher).to receive(:download).with(spec, uri, path)
+
+ Bundler.rubygems.download_gem(spec, uri, path)
+ end
+ end
+
+ describe "#fetch_all_remote_specs", :rubygems => ">= 2.0" do
+ let(:uri) { URI("https://example.com") }
+ let(:fetcher) { double("gem_remote_fetcher") }
+ let(:specs_response) { Marshal.dump(["specs"]) }
+ let(:prerelease_specs_response) { Marshal.dump(["prerelease_specs"]) }
+
+ context "when a rubygems source mirror is set" do
+ let(:orig_uri) { URI("http://zombo.com") }
+ let(:remote_with_mirror) { double("remote", :uri => uri, :original_uri => orig_uri) }
+
+ it "sets the 'X-Gemfile-Source' header containing the original source" do
+ expect(Bundler.rubygems).to receive(:gem_remote_fetcher).twice.and_return(fetcher)
+ expect(fetcher).to receive(:headers=).with("X-Gemfile-Source" => "http://zombo.com").twice
+ expect(fetcher).to receive(:fetch_path).with(uri + "specs.4.8.gz").and_return(specs_response)
+ expect(fetcher).to receive(:fetch_path).with(uri + "prerelease_specs.4.8.gz").and_return(prerelease_specs_response)
+ result = Bundler.rubygems.fetch_all_remote_specs(remote_with_mirror)
+ expect(result).to eq(%w[specs prerelease_specs])
+ end
+ end
+
+ context "when there is no rubygems source mirror set" do
+ let(:remote_no_mirror) { double("remote", :uri => uri, :original_uri => nil) }
+
+ it "does not set the 'X-Gemfile-Source' header" do
+ expect(Bundler.rubygems).to receive(:gem_remote_fetcher).twice.and_return(fetcher)
+ expect(fetcher).to_not receive(:headers=)
+ expect(fetcher).to receive(:fetch_path).with(uri + "specs.4.8.gz").and_return(specs_response)
+ expect(fetcher).to receive(:fetch_path).with(uri + "prerelease_specs.4.8.gz").and_return(prerelease_specs_response)
+ result = Bundler.rubygems.fetch_all_remote_specs(remote_no_mirror)
+ expect(result).to eq(%w[specs prerelease_specs])
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/settings/validator_spec.rb b/spec/bundler/bundler/settings/validator_spec.rb
new file mode 100644
index 0000000000..e4ffd89435
--- /dev/null
+++ b/spec/bundler/bundler/settings/validator_spec.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Settings::Validator do
+ describe ".validate!" do
+ def validate!(key, value, settings)
+ transformed_key = Bundler.settings.key_for(key)
+ if value.nil?
+ settings.delete(transformed_key)
+ else
+ settings[transformed_key] = value
+ end
+ described_class.validate!(key, value, settings)
+ settings
+ end
+
+ it "path and path.system are mutually exclusive" do
+ expect(validate!("path", "bundle", {})).to eq("BUNDLE_PATH" => "bundle")
+ expect(validate!("path", "bundle", "BUNDLE_PATH__SYSTEM" => false)).to eq("BUNDLE_PATH" => "bundle")
+ expect(validate!("path", "bundle", "BUNDLE_PATH__SYSTEM" => true)).to eq("BUNDLE_PATH" => "bundle")
+ expect(validate!("path", nil, "BUNDLE_PATH__SYSTEM" => true)).to eq("BUNDLE_PATH__SYSTEM" => true)
+ expect(validate!("path", nil, "BUNDLE_PATH__SYSTEM" => false)).to eq("BUNDLE_PATH__SYSTEM" => false)
+ expect(validate!("path", nil, {})).to eq({})
+
+ expect(validate!("path.system", true, "BUNDLE_PATH" => "bundle")).to eq("BUNDLE_PATH__SYSTEM" => true)
+ expect(validate!("path.system", false, "BUNDLE_PATH" => "bundle")).to eq("BUNDLE_PATH" => "bundle", "BUNDLE_PATH__SYSTEM" => false)
+ expect(validate!("path.system", nil, "BUNDLE_PATH" => "bundle")).to eq("BUNDLE_PATH" => "bundle")
+ expect(validate!("path.system", true, {})).to eq("BUNDLE_PATH__SYSTEM" => true)
+ expect(validate!("path.system", false, {})).to eq("BUNDLE_PATH__SYSTEM" => false)
+ expect(validate!("path.system", nil, {})).to eq({})
+ end
+
+ it "a group cannot be in both `with` & `without` simultaneously" do
+ expect do
+ validate!("with", "", {})
+ validate!("with", nil, {})
+ validate!("with", "", "BUNDLE_WITHOUT" => "a")
+ validate!("with", nil, "BUNDLE_WITHOUT" => "a")
+ validate!("with", "b:c", "BUNDLE_WITHOUT" => "a")
+
+ validate!("without", "", {})
+ validate!("without", nil, {})
+ validate!("without", "", "BUNDLE_WITH" => "a")
+ validate!("without", nil, "BUNDLE_WITH" => "a")
+ validate!("without", "b:c", "BUNDLE_WITH" => "a")
+ end.not_to raise_error
+
+ expect { validate!("with", "b:c", "BUNDLE_WITHOUT" => "c:d") }.to raise_error Bundler::InvalidOption, strip_whitespace(<<-EOS).strip
+ Setting `with` to "b:c" failed:
+ - a group cannot be in both `with` & `without` simultaneously
+ - `without` is current set to [:c, :d]
+ - the `c` groups conflict
+ EOS
+
+ expect { validate!("without", "b:c", "BUNDLE_WITH" => "c:d") }.to raise_error Bundler::InvalidOption, strip_whitespace(<<-EOS).strip
+ Setting `without` to "b:c" failed:
+ - a group cannot be in both `with` & `without` simultaneously
+ - `with` is current set to [:c, :d]
+ - the `c` groups conflict
+ EOS
+ end
+ end
+
+ describe described_class::Rule do
+ let(:keys) { %w[key] }
+ let(:description) { "rule description" }
+ let(:validate) { proc { raise "validate called!" } }
+ subject(:rule) { described_class.new(keys, description, &validate) }
+
+ describe "#validate!" do
+ it "calls the block" do
+ expect { rule.validate!("key", nil, {}) }.to raise_error(RuntimeError, /validate called!/)
+ end
+ end
+
+ describe "#fail!" do
+ it "raises with a helpful message" do
+ expect { subject.fail!("key", "value", "reason1", "reason2") }.to raise_error Bundler::InvalidOption, strip_whitespace(<<-EOS).strip
+ Setting `key` to "value" failed:
+ - rule description
+ - reason1
+ - reason2
+ EOS
+ end
+ end
+
+ describe "#set" do
+ it "works when the value has not changed" do
+ allow(Bundler.ui).to receive(:info).never
+
+ subject.set({}, "key", nil)
+ subject.set({ "BUNDLE_KEY" => "value" }, "key", "value")
+ end
+
+ it "prints out when the value is changing" do
+ settings = {}
+
+ expect(Bundler.ui).to receive(:info).with("Setting `key` to \"value\", since rule description, reason1")
+ subject.set(settings, "key", "value", "reason1")
+ expect(settings).to eq("BUNDLE_KEY" => "value")
+
+ expect(Bundler.ui).to receive(:info).with("Setting `key` to \"value2\", since rule description, reason2")
+ subject.set(settings, "key", "value2", "reason2")
+ expect(settings).to eq("BUNDLE_KEY" => "value2")
+
+ expect(Bundler.ui).to receive(:info).with("Setting `key` to nil, since rule description, reason3")
+ subject.set(settings, "key", nil, "reason3")
+ expect(settings).to eq({})
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/settings_spec.rb b/spec/bundler/bundler/settings_spec.rb
new file mode 100644
index 0000000000..1a31493e20
--- /dev/null
+++ b/spec/bundler/bundler/settings_spec.rb
@@ -0,0 +1,326 @@
+# frozen_string_literal: true
+
+require "bundler/settings"
+
+RSpec.describe Bundler::Settings do
+ subject(:settings) { described_class.new(bundled_app) }
+
+ describe "#set_local" do
+ context "when the local config file is not found" do
+ subject(:settings) { described_class.new(nil) }
+
+ it "raises a GemfileNotFound error with explanation" do
+ expect { subject.set_local("foo", "bar") }.
+ to raise_error(Bundler::GemfileNotFound, "Could not locate Gemfile")
+ end
+ end
+ end
+
+ describe "load_config" do
+ let(:hash) do
+ {
+ "build.thrift" => "--with-cppflags=-D_FORTIFY_SOURCE=0",
+ "build.libv8" => "--with-system-v8",
+ "build.therubyracer" => "--with-v8-dir",
+ "build.pg" => "--with-pg-config=/usr/local/Cellar/postgresql92/9.2.8_1/bin/pg_config",
+ "gem.coc" => "false",
+ "gem.mit" => "false",
+ "gem.test" => "minitest",
+ "thingy" => <<-EOS.tr("\n", " "),
+--asdf --fdsa --ty=oh man i hope this doesnt 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
+--very-important-option=DontDeleteRoo
+--very-important-option=DontDeleteRoo
+ EOS
+ "xyz" => "zyx",
+ }
+ end
+
+ before do
+ hash.each do |key, value|
+ settings.set_local key, value
+ end
+ end
+
+ it "can load the config" do
+ loaded = settings.send(:load_config, bundled_app("config"))
+ expected = Hash[hash.map do |k, v|
+ [settings.send(:key_for, k), v.to_s]
+ end]
+ expect(loaded).to eq(expected)
+ end
+
+ context "when BUNDLE_IGNORE_CONFIG is set" do
+ before { ENV["BUNDLE_IGNORE_CONFIG"] = "TRUE" }
+
+ it "ignores the config" do
+ loaded = settings.send(:load_config, bundled_app("config"))
+ expect(loaded).to eq({})
+ end
+ end
+ end
+
+ describe "#global_config_file" do
+ context "when $HOME is not accessible" do
+ context "when $TMPDIR is not writable" do
+ it "does not raise" do
+ expect(Bundler.rubygems).to receive(:user_home).twice.and_return(nil)
+ expect(FileUtils).to receive(:mkpath).twice.with(File.join(Dir.tmpdir, "bundler", "home")).and_raise(Errno::EROFS, "Read-only file system @ dir_s_mkdir - /tmp/bundler")
+
+ expect(subject.send(:global_config_file)).to be_nil
+ end
+ end
+ end
+ end
+
+ describe "#[]" do
+ context "when the local config file is not found" do
+ subject(:settings) { described_class.new }
+
+ it "does not raise" do
+ expect do
+ subject["foo"]
+ end.not_to raise_error
+ end
+ end
+
+ context "when not set" do
+ context "when default value present" do
+ it "retrieves value" do
+ expect(settings[:retry]).to be 3
+ end
+ end
+
+ it "returns nil" do
+ expect(settings[:buttermilk]).to be nil
+ end
+ end
+
+ context "when is boolean" do
+ it "returns a boolean" do
+ settings.set_local :frozen, "true"
+ expect(settings[:frozen]).to be true
+ end
+ context "when specific gem is configured" do
+ it "returns a boolean" do
+ settings.set_local "ignore_messages.foobar", "true"
+ expect(settings["ignore_messages.foobar"]).to be true
+ end
+ end
+ end
+
+ context "when is number" do
+ it "returns a number" do
+ settings.set_local :ssl_verify_mode, "1"
+ expect(settings[:ssl_verify_mode]).to be 1
+ end
+ end
+
+ context "when it's not possible to write to the file" do
+ it "raises an PermissionError with explanation" do
+ expect(bundler_fileutils).to receive(:mkdir_p).with(settings.send(:local_config_file).dirname).
+ and_raise(Errno::EACCES)
+ expect { settings.set_local :frozen, "1" }.
+ to raise_error(Bundler::PermissionError, /config/)
+ end
+ end
+ end
+
+ describe "#temporary" do
+ it "reset after used" do
+ Bundler.settings.set_local :no_install, true
+
+ Bundler.settings.temporary(:no_install => false) do
+ expect(Bundler.settings[:no_install]).to eq false
+ end
+
+ expect(Bundler.settings[:no_install]).to eq true
+ end
+
+ it "returns the return value of the block" do
+ ret = Bundler.settings.temporary({}) { :ret }
+ expect(ret).to eq :ret
+ end
+
+ context "when called without a block" do
+ it "leaves the setting changed" do
+ Bundler.settings.temporary(:foo => :random)
+ expect(Bundler.settings[:foo]).to eq "random"
+ end
+
+ it "returns nil" do
+ expect(Bundler.settings.temporary(:foo => :bar)).to be_nil
+ end
+ end
+ end
+
+ describe "#set_global" do
+ context "when it's not possible to write to the file" do
+ it "raises an PermissionError with explanation" do
+ expect(bundler_fileutils).to receive(:mkdir_p).with(settings.send(:global_config_file).dirname).
+ and_raise(Errno::EACCES)
+ expect { settings.set_global(:frozen, "1") }.
+ to raise_error(Bundler::PermissionError, %r{\.bundle/config})
+ end
+ end
+ end
+
+ describe "#pretty_values_for" do
+ it "prints the converted value rather than the raw string" do
+ bool_key = described_class::BOOL_KEYS.first
+ settings.set_local(bool_key, "false")
+ expect(subject.pretty_values_for(bool_key)).to eq [
+ "Set for your local app (#{bundled_app("config")}): false",
+ ]
+ end
+ end
+
+ describe "#mirror_for" do
+ let(:uri) { URI("https://rubygems.org/") }
+
+ context "with no configured mirror" do
+ it "returns the original URI" do
+ expect(settings.mirror_for(uri)).to eq(uri)
+ end
+
+ it "converts a string parameter to a URI" do
+ expect(settings.mirror_for("https://rubygems.org/")).to eq(uri)
+ end
+ end
+
+ context "with a configured mirror" do
+ let(:mirror_uri) { URI("https://rubygems-mirror.org/") }
+
+ before { settings.set_local "mirror.https://rubygems.org/", mirror_uri.to_s }
+
+ it "returns the mirror URI" do
+ expect(settings.mirror_for(uri)).to eq(mirror_uri)
+ end
+
+ it "converts a string parameter to a URI" do
+ expect(settings.mirror_for("https://rubygems.org/")).to eq(mirror_uri)
+ end
+
+ it "normalizes the URI" do
+ expect(settings.mirror_for("https://rubygems.org")).to eq(mirror_uri)
+ end
+
+ it "is case insensitive" do
+ expect(settings.mirror_for("HTTPS://RUBYGEMS.ORG/")).to eq(mirror_uri)
+ end
+
+ context "with a file URI" do
+ let(:mirror_uri) { URI("file:/foo/BAR/baz/qUx/") }
+
+ it "returns the mirror URI" do
+ expect(settings.mirror_for(uri)).to eq(mirror_uri)
+ end
+
+ it "converts a string parameter to a URI" do
+ expect(settings.mirror_for("file:/foo/BAR/baz/qUx/")).to eq(mirror_uri)
+ end
+
+ it "normalizes the URI" do
+ expect(settings.mirror_for("file:/foo/BAR/baz/qUx")).to eq(mirror_uri)
+ end
+ end
+ end
+ end
+
+ describe "#credentials_for" do
+ let(:uri) { URI("https://gemserver.example.org/") }
+ let(:credentials) { "username:password" }
+
+ context "with no configured credentials" do
+ it "returns nil" do
+ expect(settings.credentials_for(uri)).to be_nil
+ end
+ end
+
+ context "with credentials configured by URL" do
+ before { settings.set_local "https://gemserver.example.org/", credentials }
+
+ it "returns the configured credentials" do
+ expect(settings.credentials_for(uri)).to eq(credentials)
+ end
+ end
+
+ context "with credentials configured by hostname" do
+ before { settings.set_local "gemserver.example.org", credentials }
+
+ it "returns the configured credentials" do
+ expect(settings.credentials_for(uri)).to eq(credentials)
+ end
+ end
+ end
+
+ describe "URI normalization" do
+ it "normalizes HTTP URIs in credentials configuration" do
+ settings.set_local "http://gemserver.example.org", "username:password"
+ expect(settings.all).to include("http://gemserver.example.org/")
+ end
+
+ it "normalizes HTTPS URIs in credentials configuration" do
+ settings.set_local "https://gemserver.example.org", "username:password"
+ expect(settings.all).to include("https://gemserver.example.org/")
+ end
+
+ it "normalizes HTTP URIs in mirror configuration" do
+ settings.set_local "mirror.http://rubygems.org", "http://rubygems-mirror.org"
+ expect(settings.all).to include("mirror.http://rubygems.org/")
+ end
+
+ it "normalizes HTTPS URIs in mirror configuration" do
+ settings.set_local "mirror.https://rubygems.org", "http://rubygems-mirror.org"
+ expect(settings.all).to include("mirror.https://rubygems.org/")
+ end
+
+ it "does not normalize other config keys that happen to contain 'http'" do
+ settings.set_local "local.httparty", home("httparty")
+ expect(settings.all).to include("local.httparty")
+ end
+
+ it "does not normalize other config keys that happen to contain 'https'" do
+ settings.set_local "local.httpsmarty", home("httpsmarty")
+ expect(settings.all).to include("local.httpsmarty")
+ end
+
+ it "reads older keys without trailing slashes" do
+ settings.set_local "mirror.https://rubygems.org", "http://rubygems-mirror.org"
+ expect(settings.mirror_for("https://rubygems.org/")).to eq(
+ URI("http://rubygems-mirror.org/")
+ )
+ end
+
+ it "normalizes URIs with a fallback_timeout option" do
+ settings.set_local "mirror.https://rubygems.org/.fallback_timeout", "true"
+ expect(settings.all).to include("mirror.https://rubygems.org/.fallback_timeout")
+ end
+
+ it "normalizes URIs with a fallback_timeout option without a trailing slash" do
+ settings.set_local "mirror.https://rubygems.org.fallback_timeout", "true"
+ expect(settings.all).to include("mirror.https://rubygems.org/.fallback_timeout")
+ end
+ end
+
+ describe "BUNDLE_ keys format" do
+ let(:settings) { described_class.new(bundled_app(".bundle")) }
+
+ it "converts older keys without double dashes" do
+ config("BUNDLE_MY__PERSONAL.RACK" => "~/Work/git/rack")
+ expect(settings["my.personal.rack"]).to eq("~/Work/git/rack")
+ end
+
+ it "converts older keys without trailing slashes and double dashes" do
+ config("BUNDLE_MIRROR__HTTPS://RUBYGEMS.ORG" => "http://rubygems-mirror.org")
+ expect(settings["mirror.https://rubygems.org/"]).to eq("http://rubygems-mirror.org")
+ end
+
+ it "reads newer keys format properly" do
+ config("BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/" => "http://rubygems-mirror.org")
+ expect(settings["mirror.https://rubygems.org/"]).to eq("http://rubygems-mirror.org")
+ end
+ end
+end
diff --git a/spec/bundler/bundler/shared_helpers_spec.rb b/spec/bundler/bundler/shared_helpers_spec.rb
new file mode 100644
index 0000000000..fcac37b398
--- /dev/null
+++ b/spec/bundler/bundler/shared_helpers_spec.rb
@@ -0,0 +1,513 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::SharedHelpers do
+ let(:ext_lock_double) { double(:ext_lock) }
+
+ before do
+ allow(Bundler.rubygems).to receive(:ext_lock).and_return(ext_lock_double)
+ allow(ext_lock_double).to receive(:synchronize) {|&block| block.call }
+ end
+
+ subject { Bundler::SharedHelpers }
+
+ describe "#default_gemfile" do
+ before { ENV["BUNDLE_GEMFILE"] = "/path/Gemfile" }
+
+ context "Gemfile is present" do
+ let(:expected_gemfile_path) { Pathname.new("/path/Gemfile") }
+
+ it "returns the Gemfile path" do
+ expect(subject.default_gemfile).to eq(expected_gemfile_path)
+ end
+ end
+
+ context "Gemfile is not present" do
+ before { ENV["BUNDLE_GEMFILE"] = nil }
+
+ it "raises a GemfileNotFound error" do
+ expect { subject.default_gemfile }.to raise_error(
+ Bundler::GemfileNotFound, "Could not locate Gemfile"
+ )
+ end
+ end
+
+ context "Gemfile is not an absolute path" do
+ before { ENV["BUNDLE_GEMFILE"] = "Gemfile" }
+
+ let(:expected_gemfile_path) { Pathname.new("Gemfile").expand_path }
+
+ it "returns the Gemfile path" do
+ expect(subject.default_gemfile).to eq(expected_gemfile_path)
+ end
+ end
+ end
+
+ describe "#default_lockfile" do
+ context "gemfile is gems.rb" do
+ let(:gemfile_path) { Pathname.new("/path/gems.rb") }
+ let(:expected_lockfile_path) { Pathname.new("/path/gems.locked") }
+
+ before { allow(subject).to receive(:default_gemfile).and_return(gemfile_path) }
+
+ it "returns the gems.locked path" do
+ expect(subject.default_lockfile).to eq(expected_lockfile_path)
+ end
+ end
+
+ context "is a regular Gemfile" do
+ let(:gemfile_path) { Pathname.new("/path/Gemfile") }
+ let(:expected_lockfile_path) { Pathname.new("/path/Gemfile.lock") }
+
+ before { allow(subject).to receive(:default_gemfile).and_return(gemfile_path) }
+
+ it "returns the lock file path" do
+ expect(subject.default_lockfile).to eq(expected_lockfile_path)
+ end
+ end
+ end
+
+ describe "#default_bundle_dir" do
+ context ".bundle does not exist" do
+ it "returns nil" do
+ expect(subject.default_bundle_dir).to be_nil
+ end
+ end
+
+ context ".bundle is global .bundle" do
+ let(:global_rubygems_dir) { Pathname.new("#{bundled_app}") }
+
+ before do
+ Dir.mkdir ".bundle"
+ allow(Bundler.rubygems).to receive(:user_home).and_return(global_rubygems_dir)
+ end
+
+ it "returns nil" do
+ expect(subject.default_bundle_dir).to be_nil
+ end
+ end
+
+ context ".bundle is not global .bundle" do
+ let(:global_rubygems_dir) { Pathname.new("/path/rubygems") }
+ let(:expected_bundle_dir_path) { Pathname.new("#{bundled_app}/.bundle") }
+
+ before do
+ Dir.mkdir ".bundle"
+ allow(Bundler.rubygems).to receive(:user_home).and_return(global_rubygems_dir)
+ end
+
+ it "returns the .bundle path" do
+ expect(subject.default_bundle_dir).to eq(expected_bundle_dir_path)
+ end
+ end
+ end
+
+ describe "#in_bundle?" do
+ it "calls the find_gemfile method" do
+ expect(subject).to receive(:find_gemfile)
+ subject.in_bundle?
+ end
+
+ shared_examples_for "correctly determines whether to return a Gemfile path" do
+ context "currently in directory with a Gemfile" do
+ before { File.new("Gemfile", "w") }
+
+ it "returns path of the bundle Gemfile" do
+ expect(subject.in_bundle?).to eq("#{bundled_app}/Gemfile")
+ end
+ end
+
+ context "currently in directory without a Gemfile" do
+ it "returns nil" do
+ expect(subject.in_bundle?).to be_nil
+ end
+ end
+ end
+
+ context "ENV['BUNDLE_GEMFILE'] set" do
+ before { ENV["BUNDLE_GEMFILE"] = "/path/Gemfile" }
+
+ it "returns ENV['BUNDLE_GEMFILE']" do
+ expect(subject.in_bundle?).to eq("/path/Gemfile")
+ end
+ end
+
+ context "ENV['BUNDLE_GEMFILE'] not set" do
+ before { ENV["BUNDLE_GEMFILE"] = nil }
+
+ it_behaves_like "correctly determines whether to return a Gemfile path"
+ end
+
+ context "ENV['BUNDLE_GEMFILE'] is blank" do
+ before { ENV["BUNDLE_GEMFILE"] = "" }
+
+ it_behaves_like "correctly determines whether to return a Gemfile path"
+ end
+ end
+
+ describe "#chdir" do
+ let(:op_block) { proc { Dir.mkdir "nested_dir" } }
+
+ before { Dir.mkdir "chdir_test_dir" }
+
+ it "executes the passed block while in the specified directory" do
+ subject.chdir("chdir_test_dir", &op_block)
+ expect(Pathname.new("chdir_test_dir/nested_dir")).to exist
+ end
+ end
+
+ describe "#pwd" do
+ it "returns the current absolute path" do
+ expect(subject.pwd).to eq(bundled_app)
+ end
+ end
+
+ describe "#with_clean_git_env" do
+ let(:with_clean_git_env_block) { proc { Dir.mkdir "with_clean_git_env_test_dir" } }
+
+ before do
+ ENV["GIT_DIR"] = "ORIGINAL_ENV_GIT_DIR"
+ ENV["GIT_WORK_TREE"] = "ORIGINAL_ENV_GIT_WORK_TREE"
+ end
+
+ it "executes the passed block" do
+ subject.with_clean_git_env(&with_clean_git_env_block)
+ expect(Pathname.new("with_clean_git_env_test_dir")).to exist
+ end
+
+ context "when a block is passed" do
+ let(:with_clean_git_env_block) do
+ proc do
+ Dir.mkdir "git_dir_test_dir" unless ENV["GIT_DIR"].nil?
+ Dir.mkdir "git_work_tree_test_dir" unless ENV["GIT_WORK_TREE"].nil?
+ end end
+
+ it "uses a fresh git env for execution" do
+ subject.with_clean_git_env(&with_clean_git_env_block)
+ expect(Pathname.new("git_dir_test_dir")).to_not exist
+ expect(Pathname.new("git_work_tree_test_dir")).to_not exist
+ end
+ end
+
+ context "passed block does not throw errors" do
+ let(:with_clean_git_env_block) do
+ proc do
+ ENV["GIT_DIR"] = "NEW_ENV_GIT_DIR"
+ ENV["GIT_WORK_TREE"] = "NEW_ENV_GIT_WORK_TREE"
+ end end
+
+ it "restores the git env after" do
+ subject.with_clean_git_env(&with_clean_git_env_block)
+ expect(ENV["GIT_DIR"]).to eq("ORIGINAL_ENV_GIT_DIR")
+ expect(ENV["GIT_WORK_TREE"]).to eq("ORIGINAL_ENV_GIT_WORK_TREE")
+ end
+ end
+
+ context "passed block throws errors" do
+ let(:with_clean_git_env_block) do
+ proc do
+ ENV["GIT_DIR"] = "NEW_ENV_GIT_DIR"
+ ENV["GIT_WORK_TREE"] = "NEW_ENV_GIT_WORK_TREE"
+ raise RuntimeError.new
+ end end
+
+ it "restores the git env after" do
+ expect { subject.with_clean_git_env(&with_clean_git_env_block) }.to raise_error(RuntimeError)
+ expect(ENV["GIT_DIR"]).to eq("ORIGINAL_ENV_GIT_DIR")
+ expect(ENV["GIT_WORK_TREE"]).to eq("ORIGINAL_ENV_GIT_WORK_TREE")
+ end
+ end
+ end
+
+ describe "#set_bundle_environment" do
+ before do
+ ENV["BUNDLE_GEMFILE"] = "Gemfile"
+ end
+
+ shared_examples_for "ENV['PATH'] gets set correctly" do
+ before { Dir.mkdir ".bundle" }
+
+ it "ensures bundle bin path is in ENV['PATH']" do
+ subject.set_bundle_environment
+ paths = ENV["PATH"].split(File::PATH_SEPARATOR)
+ expect(paths).to include("#{Bundler.bundle_path}/bin")
+ end
+ end
+
+ shared_examples_for "ENV['RUBYOPT'] gets set correctly" do
+ it "ensures -rbundler/setup is at the beginning of ENV['RUBYOPT']" do
+ subject.set_bundle_environment
+ expect(ENV["RUBYOPT"].split(" ")).to start_with("-rbundler/setup")
+ end
+ end
+
+ shared_examples_for "ENV['RUBYLIB'] gets set correctly" do
+ let(:ruby_lib_path) { "stubbed_ruby_lib_dir" }
+
+ before do
+ allow(Bundler::SharedHelpers).to receive(:bundler_ruby_lib).and_return(ruby_lib_path)
+ end
+
+ it "ensures bundler's ruby version lib path is in ENV['RUBYLIB']" do
+ subject.set_bundle_environment
+ paths = (ENV["RUBYLIB"]).split(File::PATH_SEPARATOR)
+ expect(paths).to include(ruby_lib_path)
+ end
+ end
+
+ it "calls the appropriate set methods" do
+ expect(subject).to receive(:set_path)
+ expect(subject).to receive(:set_rubyopt)
+ expect(subject).to receive(:set_rubylib)
+ subject.set_bundle_environment
+ end
+
+ it "ignores if bundler_ruby_lib is same as rubylibdir" do
+ allow(Bundler::SharedHelpers).to receive(:bundler_ruby_lib).and_return(RbConfig::CONFIG["rubylibdir"])
+
+ subject.set_bundle_environment
+
+ paths = (ENV["RUBYLIB"]).split(File::PATH_SEPARATOR)
+ expect(paths.count(RbConfig::CONFIG["rubylibdir"])).to eq(0)
+ end
+
+ it "exits if bundle path contains the unix-like path separator" do
+ if Gem.respond_to?(:path_separator)
+ allow(Gem).to receive(:path_separator).and_return(":")
+ else
+ stub_const("File::PATH_SEPARATOR", ":".freeze)
+ end
+ allow(Bundler).to receive(:bundle_path) { Pathname.new("so:me/dir/bin") }
+ expect { subject.send(:validate_bundle_path) }.to raise_error(
+ Bundler::PathError,
+ "Your bundle path contains text matching \":\", which is the " \
+ "path separator for your system. Bundler cannot " \
+ "function correctly when the Bundle path contains the " \
+ "system's PATH separator. Please change your " \
+ "bundle path to not match \":\".\nYour current bundle " \
+ "path is '#{Bundler.bundle_path}'."
+ )
+ end
+
+ context "with a jruby path_separator regex", :ruby => "1.9" do
+ # In versions of jruby that supported ruby 1.8, the path separator was the standard File::PATH_SEPARATOR
+ let(:regex) { Regexp.new("(?<!jar:file|jar|file|classpath|uri:classloader|uri|http|https):") }
+ it "does not exit if bundle path is the standard uri path" do
+ allow(Bundler.rubygems).to receive(:path_separator).and_return(regex)
+ allow(Bundler).to receive(:bundle_path) { Pathname.new("uri:classloader:/WEB-INF/gems") }
+ expect { subject.send(:validate_bundle_path) }.not_to raise_error
+ end
+
+ it "exits if bundle path contains another directory" do
+ allow(Bundler.rubygems).to receive(:path_separator).and_return(regex)
+ allow(Bundler).to receive(:bundle_path) {
+ Pathname.new("uri:classloader:/WEB-INF/gems:other/dir")
+ }
+
+ expect { subject.send(:validate_bundle_path) }.to raise_error(
+ Bundler::PathError,
+ "Your bundle path contains text matching " \
+ "/(?<!jar:file|jar|file|classpath|uri:classloader|uri|http|https):/, which is the " \
+ "path separator for your system. Bundler cannot " \
+ "function correctly when the Bundle path contains the " \
+ "system's PATH separator. Please change your " \
+ "bundle path to not match " \
+ "/(?<!jar:file|jar|file|classpath|uri:classloader|uri|http|https):/." \
+ "\nYour current bundle path is '#{Bundler.bundle_path}'."
+ )
+ end
+ end
+
+ context "ENV['PATH'] does not exist" do
+ before { ENV.delete("PATH") }
+
+ it_behaves_like "ENV['PATH'] gets set correctly"
+ end
+
+ context "ENV['PATH'] is empty" do
+ before { ENV["PATH"] = "" }
+
+ it_behaves_like "ENV['PATH'] gets set correctly"
+ end
+
+ context "ENV['PATH'] exists" do
+ before { ENV["PATH"] = "/some_path/bin" }
+
+ it_behaves_like "ENV['PATH'] gets set correctly"
+ end
+
+ context "ENV['PATH'] already contains the bundle bin path" do
+ let(:bundle_path) { "#{Bundler.bundle_path}/bin" }
+
+ before do
+ ENV["PATH"] = bundle_path
+ end
+
+ it_behaves_like "ENV['PATH'] gets set correctly"
+
+ it "ENV['PATH'] should only contain one instance of bundle bin path" do
+ subject.set_bundle_environment
+ paths = (ENV["PATH"]).split(File::PATH_SEPARATOR)
+ expect(paths.count(bundle_path)).to eq(1)
+ end
+ end
+
+ context "ENV['RUBYOPT'] does not exist" do
+ before { ENV.delete("RUBYOPT") }
+
+ it_behaves_like "ENV['RUBYOPT'] gets set correctly"
+ end
+
+ context "ENV['RUBYOPT'] exists without -rbundler/setup" do
+ before { ENV["RUBYOPT"] = "-I/some_app_path/lib" }
+
+ it_behaves_like "ENV['RUBYOPT'] gets set correctly"
+ end
+
+ context "ENV['RUBYOPT'] exists and contains -rbundler/setup" do
+ before { ENV["RUBYOPT"] = "-rbundler/setup" }
+
+ it_behaves_like "ENV['RUBYOPT'] gets set correctly"
+ end
+
+ context "ENV['RUBYLIB'] does not exist" do
+ before { ENV.delete("RUBYLIB") }
+
+ it_behaves_like "ENV['RUBYLIB'] gets set correctly"
+ end
+
+ context "ENV['RUBYLIB'] is empty" do
+ before { ENV["PATH"] = "" }
+
+ it_behaves_like "ENV['RUBYLIB'] gets set correctly"
+ end
+
+ context "ENV['RUBYLIB'] exists" do
+ before { ENV["PATH"] = "/some_path/bin" }
+
+ it_behaves_like "ENV['RUBYLIB'] gets set correctly"
+ end
+
+ context "bundle executable in ENV['BUNDLE_BIN_PATH'] does not exist" do
+ before { ENV["BUNDLE_BIN_PATH"] = "/does/not/exist" }
+ before { Bundler.rubygems.replace_bin_path [], [] }
+
+ it "sets BUNDLE_BIN_PATH to the bundle executable file" do
+ subject.set_bundle_environment
+ bundle_exe = ruby_core? ? "../../../../exe/bundle" : "../../../exe/bundle"
+ expect(ENV["BUNDLE_BIN_PATH"]).to eq(File.expand_path(bundle_exe, __FILE__))
+ end
+ end
+
+ context "ENV['RUBYLIB'] already contains the bundler's ruby version lib path" do
+ let(:ruby_lib_path) { "stubbed_ruby_lib_dir" }
+
+ before do
+ ENV["RUBYLIB"] = ruby_lib_path
+ end
+
+ it_behaves_like "ENV['RUBYLIB'] gets set correctly"
+
+ it "ENV['RUBYLIB'] should only contain one instance of bundler's ruby version lib path" do
+ subject.set_bundle_environment
+ paths = (ENV["RUBYLIB"]).split(File::PATH_SEPARATOR)
+ expect(paths.count(ruby_lib_path)).to eq(1)
+ end
+ end
+ end
+
+ describe "#filesystem_access" do
+ context "system has proper permission access" do
+ let(:file_op_block) { proc {|path| FileUtils.mkdir_p(path) } }
+
+ it "performs the operation in the passed block" do
+ subject.filesystem_access("./test_dir", &file_op_block)
+ expect(Pathname.new("test_dir")).to exist
+ end
+ end
+
+ context "system throws Errno::EACESS" do
+ let(:file_op_block) { proc {|_path| raise Errno::EACCES } }
+
+ it "raises a PermissionError" do
+ expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
+ Bundler::PermissionError
+ )
+ end
+ end
+
+ context "system throws Errno::EAGAIN" do
+ let(:file_op_block) { proc {|_path| raise Errno::EAGAIN } }
+
+ it "raises a TemporaryResourceError" do
+ expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
+ Bundler::TemporaryResourceError
+ )
+ end
+ end
+
+ context "system throws Errno::EPROTO" do
+ let(:file_op_block) { proc {|_path| raise Errno::EPROTO } }
+
+ it "raises a VirtualProtocolError" do
+ expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
+ Bundler::VirtualProtocolError
+ )
+ end
+ end
+
+ context "system throws Errno::ENOTSUP", :ruby => "1.9" do
+ let(:file_op_block) { proc {|_path| raise Errno::ENOTSUP } }
+
+ it "raises a OperationNotSupportedError" do
+ expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
+ Bundler::OperationNotSupportedError
+ )
+ end
+ end
+
+ context "system throws Errno::ENOSPC" do
+ let(:file_op_block) { proc {|_path| raise Errno::ENOSPC } }
+
+ it "raises a NoSpaceOnDeviceError" do
+ expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
+ Bundler::NoSpaceOnDeviceError
+ )
+ end
+ end
+
+ context "system throws an unhandled SystemCallError" do
+ let(:error) { SystemCallError.new("Shields down", 1337) }
+ let(:file_op_block) { proc {|_path| raise error } }
+
+ it "raises a GenericSystemCallError" do
+ expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
+ Bundler::GenericSystemCallError, /error accessing.+underlying.+Shields down/m
+ )
+ end
+ end
+ end
+
+ describe "#const_get_safely" do
+ module TargetNamespace
+ VALID_CONSTANT = 1
+ end
+
+ context "when the namespace does have the requested constant" do
+ it "returns the value of the requested constant" do
+ expect(subject.const_get_safely(:VALID_CONSTANT, TargetNamespace)).to eq(1)
+ end
+ end
+
+ context "when the requested constant is passed as a string" do
+ it "returns the value of the requested constant" do
+ expect(subject.const_get_safely("VALID_CONSTANT", TargetNamespace)).to eq(1)
+ end
+ end
+
+ context "when the namespace does not have the requested constant" do
+ it "returns nil" do
+ expect(subject.const_get_safely("INVALID_CONSTANT", TargetNamespace)).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/source/git/git_proxy_spec.rb b/spec/bundler/bundler/source/git/git_proxy_spec.rb
new file mode 100644
index 0000000000..3a29c97461
--- /dev/null
+++ b/spec/bundler/bundler/source/git/git_proxy_spec.rb
@@ -0,0 +1,140 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Source::Git::GitProxy do
+ let(:path) { Pathname("path") }
+ let(:uri) { "https://github.com/bundler/bundler.git" }
+ let(:ref) { "HEAD" }
+ let(:revision) { nil }
+ let(:git_source) { nil }
+ 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")
+ expect(subject).to receive(:git_retry).with(match("https://u:p@github.com/bundler/bundler.git"))
+ subject.checkout
+ end
+
+ it "adds username and password to URI for host" do
+ Bundler.settings.temporary("github.com" => "u:p")
+ expect(subject).to receive(:git_retry).with(match("https://u:p@github.com/bundler/bundler.git"))
+ subject.checkout
+ end
+
+ it "does not add username and password to mismatched URI" do
+ Bundler.settings.temporary("https://u:p@github.com/bundler/bundler-mismatch.git" => "u:p")
+ expect(subject).to receive(:git_retry).with(match(uri))
+ subject.checkout
+ end
+
+ it "keeps original userinfo" do
+ Bundler.settings.temporary("github.com" => "u:p")
+ original = "https://orig:info@github.com/bundler/bundler.git"
+ subject = described_class.new(Pathname("path"), original, "HEAD")
+ expect(subject).to receive(:git_retry).with(match(original))
+ subject.checkout
+ end
+ end
+
+ describe "#version" do
+ context "with a normal version number" do
+ before do
+ expect(subject).to receive(:git).with("--version").
+ and_return("git version 1.2.3")
+ end
+
+ it "returns the git version number" do
+ expect(subject.version).to eq("1.2.3")
+ end
+
+ it "does not raise an error when passed into Gem::Version.create" do
+ expect { Gem::Version.create subject.version }.not_to raise_error
+ end
+ end
+
+ context "with a OSX version number" do
+ before do
+ expect(subject).to receive(:git).with("--version").
+ and_return("git version 1.2.3 (Apple Git-BS)")
+ end
+
+ it "strips out OSX specific additions in the version string" do
+ expect(subject.version).to eq("1.2.3")
+ end
+
+ it "does not raise an error when passed into Gem::Version.create" do
+ expect { Gem::Version.create subject.version }.not_to raise_error
+ end
+ end
+
+ context "with a msysgit version number" do
+ before do
+ expect(subject).to receive(:git).with("--version").
+ and_return("git version 1.2.3.msysgit.0")
+ end
+
+ it "strips out msysgit specific additions in the version string" do
+ expect(subject.version).to eq("1.2.3")
+ end
+
+ it "does not raise an error when passed into Gem::Version.create" do
+ expect { Gem::Version.create subject.version }.not_to raise_error
+ end
+ end
+ end
+
+ describe "#full_version" do
+ context "with a normal version number" do
+ before do
+ expect(subject).to receive(:git).with("--version").
+ and_return("git version 1.2.3")
+ end
+
+ it "returns the git version number" do
+ expect(subject.full_version).to eq("1.2.3")
+ end
+ end
+
+ context "with a OSX version number" do
+ before do
+ expect(subject).to receive(:git).with("--version").
+ and_return("git version 1.2.3 (Apple Git-BS)")
+ end
+
+ it "does not strip out OSX specific additions in the version string" do
+ expect(subject.full_version).to eq("1.2.3 (Apple Git-BS)")
+ end
+ end
+
+ context "with a msysgit version number" do
+ before do
+ expect(subject).to receive(:git).with("--version").
+ and_return("git version 1.2.3.msysgit.0")
+ end
+
+ it "does not strip out msysgit specific additions in the version string" do
+ expect(subject.full_version).to eq("1.2.3.msysgit.0")
+ end
+ end
+ end
+
+ describe "#copy_to" do
+ let(:destination) { tmpdir("copy_to_path") }
+ let(:submodules) { false }
+
+ context "when given a SHA as a revision" do
+ let(:revision) { "abcd" * 10 }
+
+ it "fails gracefully when resetting to the revision fails" do
+ expect(subject).to receive(:git_retry).with(start_with("clone ")) { destination.mkpath }
+ expect(subject).to receive(:git_retry).with(start_with("fetch "))
+ expect(subject).to receive(:git).with("reset --hard #{revision}").and_raise(Bundler::Source::Git::GitCommandError, "command")
+ expect(subject).not_to receive(:git)
+
+ expect { subject.copy_to(destination, submodules) }.
+ to raise_error(Bundler::Source::Git::MissingGitRevisionError,
+ "Revision #{revision} does not exist in the repository #{uri}. Maybe you misspelled it?")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/source/git_spec.rb b/spec/bundler/bundler/source/git_spec.rb
new file mode 100644
index 0000000000..f7475a35aa
--- /dev/null
+++ b/spec/bundler/bundler/source/git_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Source::Git do
+ before do
+ allow(Bundler).to receive(:root) { Pathname.new("root") }
+ end
+
+ let(:uri) { "https://github.com/foo/bar.git" }
+ let(:options) do
+ { "uri" => uri }
+ end
+
+ subject { described_class.new(options) }
+
+ describe "#to_s" do
+ it "returns a description" do
+ expect(subject.to_s).to eq "https://github.com/foo/bar.git (at master)"
+ end
+
+ context "when the URI contains credentials" do
+ let(:uri) { "https://my-secret-token:x-oauth-basic@github.com/foo/bar.git" }
+
+ it "filters credentials" do
+ expect(subject.to_s).to eq "https://x-oauth-basic@github.com/foo/bar.git (at master)"
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/source/path_spec.rb b/spec/bundler/bundler/source/path_spec.rb
new file mode 100644
index 0000000000..1d13e03ec1
--- /dev/null
+++ b/spec/bundler/bundler/source/path_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Source::Path do
+ before do
+ allow(Bundler).to receive(:root) { Pathname.new("root") }
+ end
+
+ describe "#eql?" do
+ subject { described_class.new("path" => "gems/a") }
+
+ context "with two equivalent relative paths from different roots" do
+ let(:a_gem_opts) { { "path" => "../gems/a", "root_path" => Bundler.root.join("nested") } }
+ let(:a_gem) { described_class.new a_gem_opts }
+
+ it "returns true" do
+ expect(subject).to eq a_gem
+ end
+ end
+
+ context "with the same (but not equivalent) relative path from different roots" do
+ subject { described_class.new("path" => "gems/a") }
+
+ let(:a_gem_opts) { { "path" => "gems/a", "root_path" => Bundler.root.join("nested") } }
+ let(:a_gem) { described_class.new a_gem_opts }
+
+ it "returns false" do
+ expect(subject).to_not eq a_gem
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/source/rubygems/remote_spec.rb b/spec/bundler/bundler/source/rubygems/remote_spec.rb
new file mode 100644
index 0000000000..9a7ab42128
--- /dev/null
+++ b/spec/bundler/bundler/source/rubygems/remote_spec.rb
@@ -0,0 +1,162 @@
+# frozen_string_literal: true
+
+require "bundler/source/rubygems/remote"
+
+RSpec.describe Bundler::Source::Rubygems::Remote do
+ def remote(uri)
+ Bundler::Source::Rubygems::Remote.new(uri)
+ end
+
+ before do
+ allow(Digest(:MD5)).to receive(:hexdigest).with(duck_type(:to_s)) {|string| "MD5HEX(#{string})" }
+ end
+
+ let(:uri_no_auth) { URI("https://gems.example.com") }
+ let(:uri_with_auth) { URI("https://#{credentials}@gems.example.com") }
+ let(:credentials) { "username:password" }
+
+ context "when the original URI has no credentials" do
+ describe "#uri" do
+ it "returns the original URI" do
+ expect(remote(uri_no_auth).uri).to eq(uri_no_auth)
+ end
+
+ it "applies configured credentials" do
+ Bundler.settings.temporary(uri_no_auth.to_s => credentials)
+ expect(remote(uri_no_auth).uri).to eq(uri_with_auth)
+ end
+ end
+
+ describe "#anonymized_uri" do
+ it "returns the original URI" do
+ expect(remote(uri_no_auth).anonymized_uri).to eq(uri_no_auth)
+ end
+
+ it "does not apply given credentials" do
+ Bundler.settings.temporary(uri_no_auth.to_s => credentials)
+ expect(remote(uri_no_auth).anonymized_uri).to eq(uri_no_auth)
+ end
+ end
+
+ describe "#cache_slug" do
+ it "returns the correct slug" do
+ expect(remote(uri_no_auth).cache_slug).to eq("gems.example.com.443.MD5HEX(gems.example.com.443./)")
+ end
+
+ it "only applies the given user" do
+ Bundler.settings.temporary(uri_no_auth.to_s => credentials)
+ expect(remote(uri_no_auth).cache_slug).to eq("gems.example.com.username.443.MD5HEX(gems.example.com.username.443./)")
+ end
+ end
+ end
+
+ context "when the original URI has a username and password" do
+ describe "#uri" do
+ it "returns the original URI" do
+ expect(remote(uri_with_auth).uri).to eq(uri_with_auth)
+ end
+
+ it "does not apply configured credentials" do
+ Bundler.settings.temporary(uri_no_auth.to_s => "other:stuff")
+ expect(remote(uri_with_auth).uri).to eq(uri_with_auth)
+ end
+ end
+
+ describe "#anonymized_uri" do
+ it "returns the URI without username and password" do
+ expect(remote(uri_with_auth).anonymized_uri).to eq(uri_no_auth)
+ end
+
+ it "does not apply given credentials" do
+ Bundler.settings.temporary(uri_no_auth.to_s => "other:stuff")
+ expect(remote(uri_with_auth).anonymized_uri).to eq(uri_no_auth)
+ end
+ end
+
+ describe "#cache_slug" do
+ it "returns the correct slug" do
+ expect(remote(uri_with_auth).cache_slug).to eq("gems.example.com.username.443.MD5HEX(gems.example.com.username.443./)")
+ end
+
+ it "does not apply given credentials" do
+ Bundler.settings.temporary(uri_with_auth.to_s => credentials)
+ expect(remote(uri_with_auth).cache_slug).to eq("gems.example.com.username.443.MD5HEX(gems.example.com.username.443./)")
+ end
+ end
+ end
+
+ context "when the original URI has only a username" do
+ let(:uri) { URI("https://SeCrEt-ToKeN@gem.fury.io/me/") }
+
+ describe "#anonymized_uri" do
+ it "returns the URI without username and password" do
+ expect(remote(uri).anonymized_uri).to eq(URI("https://gem.fury.io/me/"))
+ end
+ end
+
+ describe "#cache_slug" do
+ it "returns the correct slug" do
+ expect(remote(uri).cache_slug).to eq("gem.fury.io.SeCrEt-ToKeN.443.MD5HEX(gem.fury.io.SeCrEt-ToKeN.443./me/)")
+ end
+ end
+ end
+
+ context "when a mirror with inline credentials is configured for the URI" do
+ let(:uri) { URI("https://rubygems.org/") }
+ let(:mirror_uri_with_auth) { URI("https://username:password@rubygems-mirror.org/") }
+ let(:mirror_uri_no_auth) { URI("https://rubygems-mirror.org/") }
+
+ before { Bundler.settings.set_local("mirror.https://rubygems.org/", mirror_uri_with_auth.to_s) }
+
+ specify "#uri returns the mirror URI with credentials" do
+ expect(remote(uri).uri).to eq(mirror_uri_with_auth)
+ end
+
+ specify "#anonymized_uri returns the mirror URI without credentials" do
+ expect(remote(uri).anonymized_uri).to eq(mirror_uri_no_auth)
+ end
+
+ specify "#original_uri returns the original source" do
+ expect(remote(uri).original_uri).to eq(uri)
+ end
+
+ specify "#cache_slug returns the correct slug" do
+ expect(remote(uri).cache_slug).to eq("rubygems.org.443.MD5HEX(rubygems.org.443./)")
+ end
+ end
+
+ context "when a mirror with configured credentials is configured for the URI" do
+ let(:uri) { URI("https://rubygems.org/") }
+ let(:mirror_uri_with_auth) { URI("https://#{credentials}@rubygems-mirror.org/") }
+ let(:mirror_uri_no_auth) { URI("https://rubygems-mirror.org/") }
+
+ before do
+ Bundler.settings.temporary("mirror.https://rubygems.org/" => mirror_uri_no_auth.to_s)
+ Bundler.settings.temporary(mirror_uri_no_auth.to_s => credentials)
+ end
+
+ specify "#uri returns the mirror URI with credentials" do
+ expect(remote(uri).uri).to eq(mirror_uri_with_auth)
+ end
+
+ specify "#anonymized_uri returns the mirror URI without credentials" do
+ expect(remote(uri).anonymized_uri).to eq(mirror_uri_no_auth)
+ end
+
+ specify "#original_uri returns the original source" do
+ expect(remote(uri).original_uri).to eq(uri)
+ end
+
+ specify "#cache_slug returns the original source" do
+ expect(remote(uri).cache_slug).to eq("rubygems.org.443.MD5HEX(rubygems.org.443./)")
+ end
+ end
+
+ context "when there is no mirror set" do
+ describe "#original_uri" do
+ it "is not set" do
+ expect(remote(uri_no_auth).original_uri).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/source/rubygems_spec.rb b/spec/bundler/bundler/source/rubygems_spec.rb
new file mode 100644
index 0000000000..7c457a7265
--- /dev/null
+++ b/spec/bundler/bundler/source/rubygems_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Source::Rubygems do
+ before do
+ allow(Bundler).to receive(:root) { Pathname.new("root") }
+ end
+
+ describe "caches" do
+ it "includes Bundler.app_cache" do
+ expect(subject.caches).to include(Bundler.app_cache)
+ end
+
+ it "includes GEM_PATH entries" do
+ Gem.path.each do |path|
+ expect(subject.caches).to include(File.expand_path("#{path}/cache"))
+ end
+ end
+
+ it "is an array of strings or pathnames" do
+ subject.caches.each do |cache|
+ expect([String, Pathname]).to include(cache.class)
+ end
+ end
+ end
+
+ describe "#add_remote" do
+ context "when the source is an HTTP(s) URI with no host" do
+ it "raises error" do
+ expect { subject.add_remote("https:rubygems.org") }.to raise_error(ArgumentError)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/source_list_spec.rb b/spec/bundler/bundler/source_list_spec.rb
new file mode 100644
index 0000000000..ce3353012c
--- /dev/null
+++ b/spec/bundler/bundler/source_list_spec.rb
@@ -0,0 +1,463 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::SourceList do
+ before do
+ allow(Bundler).to receive(:root) { Pathname.new "./tmp/bundled_app" }
+
+ stub_const "ASourcePlugin", Class.new(Bundler::Plugin::API)
+ ASourcePlugin.source "new_source"
+ allow(Bundler::Plugin).to receive(:source?).with("new_source").and_return(true)
+ end
+
+ subject(:source_list) { Bundler::SourceList.new }
+
+ let(:rubygems_aggregate) { Bundler::Source::Rubygems.new }
+ let(:metadata_source) { Bundler::Source::Metadata.new }
+
+ describe "adding sources" do
+ before do
+ source_list.add_path_source("path" => "/existing/path/to/gem")
+ source_list.add_git_source("uri" => "git://existing-git.org/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://existing-rubygems.org"])
+ source_list.add_plugin_source("new_source", "uri" => "https://some.url/a")
+ end
+
+ describe "#add_path_source" do
+ before do
+ @duplicate = source_list.add_path_source("path" => "/path/to/gem")
+ @new_source = source_list.add_path_source("path" => "/path/to/gem")
+ end
+
+ it "returns the new path source" do
+ expect(@new_source).to be_instance_of(Bundler::Source::Path)
+ end
+
+ it "passes the provided options to the new source" do
+ expect(@new_source.options).to eq("path" => "/path/to/gem")
+ end
+
+ it "adds the source to the beginning of path_sources" do
+ expect(source_list.path_sources.first).to equal(@new_source)
+ end
+
+ it "removes existing duplicates" do
+ expect(source_list.path_sources).not_to include equal(@duplicate)
+ end
+ end
+
+ describe "#add_git_source" do
+ before do
+ @duplicate = source_list.add_git_source("uri" => "git://host/path.git")
+ @new_source = source_list.add_git_source("uri" => "git://host/path.git")
+ end
+
+ it "returns the new git source" do
+ expect(@new_source).to be_instance_of(Bundler::Source::Git)
+ end
+
+ it "passes the provided options to the new source" do
+ @new_source = source_list.add_git_source("uri" => "git://host/path.git")
+ expect(@new_source.options).to eq("uri" => "git://host/path.git")
+ end
+
+ it "adds the source to the beginning of git_sources" do
+ @new_source = source_list.add_git_source("uri" => "git://host/path.git")
+ expect(source_list.git_sources.first).to equal(@new_source)
+ end
+
+ it "removes existing duplicates" do
+ @duplicate = source_list.add_git_source("uri" => "git://host/path.git")
+ @new_source = source_list.add_git_source("uri" => "git://host/path.git")
+ expect(source_list.git_sources).not_to include equal(@duplicate)
+ end
+
+ context "with the git: protocol" do
+ let(:msg) do
+ "The git source `git://existing-git.org/path.git` " \
+ "uses the `git` protocol, which transmits data without encryption. " \
+ "Disable this warning with `bundle config git.allow_insecure true`, " \
+ "or switch to the `https` protocol to keep your data secure."
+ end
+
+ it "warns about git protocols" do
+ expect(Bundler.ui).to receive(:warn).with(msg)
+ source_list.add_git_source("uri" => "git://existing-git.org/path.git")
+ end
+
+ it "ignores git protocols on request" do
+ Bundler.settings.temporary(:"git.allow_insecure" => true)
+ expect(Bundler.ui).to_not receive(:warn).with(msg)
+ source_list.add_git_source("uri" => "git://existing-git.org/path.git")
+ end
+ end
+ end
+
+ describe "#add_rubygems_source" do
+ before do
+ @duplicate = source_list.add_rubygems_source("remotes" => ["https://rubygems.org/"])
+ @new_source = source_list.add_rubygems_source("remotes" => ["https://rubygems.org/"])
+ end
+
+ it "returns the new rubygems source" do
+ expect(@new_source).to be_instance_of(Bundler::Source::Rubygems)
+ end
+
+ it "passes the provided options to the new source" do
+ expect(@new_source.options).to eq("remotes" => ["https://rubygems.org/"])
+ end
+
+ it "adds the source to the beginning of rubygems_sources" do
+ expect(source_list.rubygems_sources.first).to equal(@new_source)
+ end
+
+ it "removes duplicates" do
+ expect(source_list.rubygems_sources).not_to include equal(@duplicate)
+ end
+ end
+
+ describe "#add_rubygems_remote", :bundler => "< 2" do
+ let!(:returned_source) { source_list.add_rubygems_remote("https://rubygems.org/") }
+
+ it "returns the aggregate rubygems source" do
+ expect(returned_source).to be_instance_of(Bundler::Source::Rubygems)
+ end
+
+ it "adds the provided remote to the beginning of the aggregate source" do
+ source_list.add_rubygems_remote("https://othersource.org")
+ expect(returned_source.remotes).to eq [
+ URI("https://othersource.org/"),
+ URI("https://rubygems.org/"),
+ ]
+ end
+ end
+
+ describe "#add_plugin_source" do
+ before do
+ @duplicate = source_list.add_plugin_source("new_source", "uri" => "http://host/path.")
+ @new_source = source_list.add_plugin_source("new_source", "uri" => "http://host/path.")
+ end
+
+ it "returns the new plugin source" do
+ expect(@new_source).to be_a(Bundler::Plugin::API::Source)
+ end
+
+ it "passes the provided options to the new source" do
+ expect(@new_source.options).to eq("uri" => "http://host/path.")
+ end
+
+ it "adds the source to the beginning of git_sources" do
+ expect(source_list.plugin_sources.first).to equal(@new_source)
+ end
+
+ it "removes existing duplicates" do
+ expect(source_list.plugin_sources).not_to include equal(@duplicate)
+ end
+ end
+ end
+
+ describe "#all_sources" do
+ it "includes the aggregate rubygems source when rubygems sources have been added" do
+ source_list.add_git_source("uri" => "git://host/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://rubygems.org"])
+ source_list.add_path_source("path" => "/path/to/gem")
+ source_list.add_plugin_source("new_source", "uri" => "https://some.url/a")
+
+ expect(source_list.all_sources).to include rubygems_aggregate
+ end
+
+ it "includes the aggregate rubygems source when no rubygems sources have been added" do
+ source_list.add_git_source("uri" => "git://host/path.git")
+ source_list.add_path_source("path" => "/path/to/gem")
+ source_list.add_plugin_source("new_source", "uri" => "https://some.url/a")
+
+ expect(source_list.all_sources).to include rubygems_aggregate
+ end
+
+ it "returns sources of the same type in the reverse order that they were added" do
+ source_list.add_git_source("uri" => "git://third-git.org/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://fifth-rubygems.org"])
+ source_list.add_path_source("path" => "/third/path/to/gem")
+ source_list.add_plugin_source("new_source", "uri" => "https://some.url/b")
+ source_list.add_rubygems_source("remotes" => ["https://fourth-rubygems.org"])
+ source_list.add_path_source("path" => "/second/path/to/gem")
+ source_list.add_rubygems_source("remotes" => ["https://third-rubygems.org"])
+ source_list.add_plugin_source("new_source", "uri" => "https://some.o.url/")
+ source_list.add_git_source("uri" => "git://second-git.org/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://second-rubygems.org"])
+ source_list.add_path_source("path" => "/first/path/to/gem")
+ source_list.add_plugin_source("new_source", "uri" => "https://some.url/c")
+ source_list.add_rubygems_source("remotes" => ["https://first-rubygems.org"])
+ source_list.add_git_source("uri" => "git://first-git.org/path.git")
+
+ expect(source_list.all_sources).to eq [
+ Bundler::Source::Path.new("path" => "/first/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/second/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/third/path/to/gem"),
+ Bundler::Source::Git.new("uri" => "git://first-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://second-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://third-git.org/path.git"),
+ ASourcePlugin.new("uri" => "https://some.url/c"),
+ ASourcePlugin.new("uri" => "https://some.o.url/"),
+ ASourcePlugin.new("uri" => "https://some.url/b"),
+ Bundler::Source::Rubygems.new("remotes" => ["https://first-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://second-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://third-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://fourth-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://fifth-rubygems.org"]),
+ rubygems_aggregate,
+ metadata_source,
+ ]
+ end
+ end
+
+ describe "#path_sources" do
+ it "returns an empty array when no path sources have been added" do
+ source_list.add_rubygems_remote("https://rubygems.org")
+ source_list.add_git_source("uri" => "git://host/path.git")
+ expect(source_list.path_sources).to be_empty
+ end
+
+ it "returns path sources in the reverse order that they were added" do
+ source_list.add_git_source("uri" => "git://third-git.org/path.git")
+ source_list.add_rubygems_remote("https://fifth-rubygems.org")
+ source_list.add_path_source("path" => "/third/path/to/gem")
+ source_list.add_rubygems_remote("https://fourth-rubygems.org")
+ source_list.add_path_source("path" => "/second/path/to/gem")
+ source_list.add_rubygems_remote("https://third-rubygems.org")
+ source_list.add_git_source("uri" => "git://second-git.org/path.git")
+ source_list.add_rubygems_remote("https://second-rubygems.org")
+ source_list.add_path_source("path" => "/first/path/to/gem")
+ source_list.add_rubygems_remote("https://first-rubygems.org")
+ source_list.add_git_source("uri" => "git://first-git.org/path.git")
+
+ expect(source_list.path_sources).to eq [
+ Bundler::Source::Path.new("path" => "/first/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/second/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/third/path/to/gem"),
+ ]
+ end
+ end
+
+ describe "#git_sources" do
+ it "returns an empty array when no git sources have been added" do
+ source_list.add_rubygems_remote("https://rubygems.org")
+ source_list.add_path_source("path" => "/path/to/gem")
+
+ expect(source_list.git_sources).to be_empty
+ end
+
+ it "returns git sources in the reverse order that they were added" do
+ source_list.add_git_source("uri" => "git://third-git.org/path.git")
+ source_list.add_rubygems_remote("https://fifth-rubygems.org")
+ source_list.add_path_source("path" => "/third/path/to/gem")
+ source_list.add_rubygems_remote("https://fourth-rubygems.org")
+ source_list.add_path_source("path" => "/second/path/to/gem")
+ source_list.add_rubygems_remote("https://third-rubygems.org")
+ source_list.add_git_source("uri" => "git://second-git.org/path.git")
+ source_list.add_rubygems_remote("https://second-rubygems.org")
+ source_list.add_path_source("path" => "/first/path/to/gem")
+ source_list.add_rubygems_remote("https://first-rubygems.org")
+ source_list.add_git_source("uri" => "git://first-git.org/path.git")
+
+ expect(source_list.git_sources).to eq [
+ Bundler::Source::Git.new("uri" => "git://first-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://second-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://third-git.org/path.git"),
+ ]
+ end
+ end
+
+ describe "#plugin_sources" do
+ it "returns an empty array when no plugin sources have been added" do
+ source_list.add_rubygems_remote("https://rubygems.org")
+ source_list.add_path_source("path" => "/path/to/gem")
+
+ expect(source_list.plugin_sources).to be_empty
+ end
+
+ it "returns plugin sources in the reverse order that they were added" do
+ source_list.add_plugin_source("new_source", "uri" => "https://third-git.org/path.git")
+ source_list.add_git_source("https://new-git.org")
+ source_list.add_path_source("path" => "/third/path/to/gem")
+ source_list.add_rubygems_remote("https://fourth-rubygems.org")
+ source_list.add_path_source("path" => "/second/path/to/gem")
+ source_list.add_rubygems_remote("https://third-rubygems.org")
+ source_list.add_plugin_source("new_source", "uri" => "git://second-git.org/path.git")
+ source_list.add_rubygems_remote("https://second-rubygems.org")
+ source_list.add_path_source("path" => "/first/path/to/gem")
+ source_list.add_rubygems_remote("https://first-rubygems.org")
+ source_list.add_plugin_source("new_source", "uri" => "git://first-git.org/path.git")
+
+ expect(source_list.plugin_sources).to eq [
+ ASourcePlugin.new("uri" => "git://first-git.org/path.git"),
+ ASourcePlugin.new("uri" => "git://second-git.org/path.git"),
+ ASourcePlugin.new("uri" => "https://third-git.org/path.git"),
+ ]
+ end
+ end
+
+ describe "#rubygems_sources" do
+ it "includes the aggregate rubygems source when rubygems sources have been added" do
+ source_list.add_git_source("uri" => "git://host/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://rubygems.org"])
+ source_list.add_path_source("path" => "/path/to/gem")
+
+ expect(source_list.rubygems_sources).to include rubygems_aggregate
+ end
+
+ it "returns only the aggregate rubygems source when no rubygems sources have been added" do
+ source_list.add_git_source("uri" => "git://host/path.git")
+ source_list.add_path_source("path" => "/path/to/gem")
+
+ expect(source_list.rubygems_sources).to eq [rubygems_aggregate]
+ end
+
+ it "returns rubygems sources in the reverse order that they were added" do
+ source_list.add_git_source("uri" => "git://third-git.org/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://fifth-rubygems.org"])
+ source_list.add_path_source("path" => "/third/path/to/gem")
+ source_list.add_rubygems_source("remotes" => ["https://fourth-rubygems.org"])
+ source_list.add_path_source("path" => "/second/path/to/gem")
+ source_list.add_rubygems_source("remotes" => ["https://third-rubygems.org"])
+ source_list.add_git_source("uri" => "git://second-git.org/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://second-rubygems.org"])
+ source_list.add_path_source("path" => "/first/path/to/gem")
+ source_list.add_rubygems_source("remotes" => ["https://first-rubygems.org"])
+ source_list.add_git_source("uri" => "git://first-git.org/path.git")
+
+ expect(source_list.rubygems_sources).to eq [
+ Bundler::Source::Rubygems.new("remotes" => ["https://first-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://second-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://third-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://fourth-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://fifth-rubygems.org"]),
+ rubygems_aggregate,
+ ]
+ end
+ end
+
+ describe "#get" do
+ context "when it includes an equal source" do
+ let(:rubygems_source) { Bundler::Source::Rubygems.new("remotes" => ["https://rubygems.org"]) }
+ before { @equal_source = source_list.add_rubygems_remote("https://rubygems.org") }
+
+ it "returns the equal source" do
+ expect(source_list.get(rubygems_source)).to be @equal_source
+ end
+ end
+
+ context "when it does not include an equal source" do
+ let(:path_source) { Bundler::Source::Path.new("path" => "/path/to/gem") }
+
+ it "returns nil" do
+ expect(source_list.get(path_source)).to be_nil
+ end
+ end
+ end
+
+ describe "#lock_sources" do
+ before do
+ source_list.add_git_source("uri" => "git://third-git.org/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://duplicate-rubygems.org"])
+ source_list.add_plugin_source("new_source", "uri" => "https://third-bar.org/foo")
+ source_list.add_path_source("path" => "/third/path/to/gem")
+ source_list.add_rubygems_source("remotes" => ["https://third-rubygems.org"])
+ source_list.add_path_source("path" => "/second/path/to/gem")
+ source_list.add_rubygems_source("remotes" => ["https://second-rubygems.org"])
+ source_list.add_git_source("uri" => "git://second-git.org/path.git")
+ source_list.add_rubygems_source("remotes" => ["https://first-rubygems.org"])
+ source_list.add_plugin_source("new_source", "uri" => "https://second-plugin.org/random")
+ source_list.add_path_source("path" => "/first/path/to/gem")
+ source_list.add_rubygems_source("remotes" => ["https://duplicate-rubygems.org"])
+ source_list.add_git_source("uri" => "git://first-git.org/path.git")
+ end
+
+ it "combines the rubygems sources into a single instance, removing duplicate remotes from the end", :bundler => "< 2" do
+ expect(source_list.lock_sources).to eq [
+ Bundler::Source::Git.new("uri" => "git://first-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://second-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://third-git.org/path.git"),
+ ASourcePlugin.new("uri" => "https://second-plugin.org/random"),
+ ASourcePlugin.new("uri" => "https://third-bar.org/foo"),
+ Bundler::Source::Path.new("path" => "/first/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/second/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/third/path/to/gem"),
+ Bundler::Source::Rubygems.new("remotes" => [
+ "https://duplicate-rubygems.org",
+ "https://first-rubygems.org",
+ "https://second-rubygems.org",
+ "https://third-rubygems.org",
+ ]),
+ ]
+ end
+
+ it "returns all sources, without combining rubygems sources", :bundler => "2" do
+ expect(source_list.lock_sources).to eq [
+ Bundler::Source::Rubygems.new,
+ Bundler::Source::Rubygems.new("remotes" => ["https://duplicate-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://first-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://second-rubygems.org"]),
+ Bundler::Source::Rubygems.new("remotes" => ["https://third-rubygems.org"]),
+ Bundler::Source::Git.new("uri" => "git://first-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://second-git.org/path.git"),
+ Bundler::Source::Git.new("uri" => "git://third-git.org/path.git"),
+ Bundler::Source::Path.new("path" => "/first/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/second/path/to/gem"),
+ Bundler::Source::Path.new("path" => "/third/path/to/gem"),
+ ASourcePlugin.new("uri" => "https://second-plugin.org/random"),
+ ASourcePlugin.new("uri" => "https://third-bar.org/foo"),
+ ]
+ end
+ end
+
+ describe "replace_sources!" do
+ let(:existing_locked_source) { Bundler::Source::Path.new("path" => "/existing/path") }
+ let(:removed_locked_source) { Bundler::Source::Path.new("path" => "/removed/path") }
+
+ let(:locked_sources) { [existing_locked_source, removed_locked_source] }
+
+ before do
+ @existing_source = source_list.add_path_source("path" => "/existing/path")
+ @new_source = source_list.add_path_source("path" => "/new/path")
+ source_list.replace_sources!(locked_sources)
+ end
+
+ it "maintains the order and number of sources" do
+ expect(source_list.path_sources).to eq [@new_source, @existing_source]
+ end
+
+ it "retains the same instance of the new source" do
+ expect(source_list.path_sources[0]).to be @new_source
+ end
+
+ it "replaces the instance of the existing source" do
+ expect(source_list.path_sources[1]).to be existing_locked_source
+ end
+ end
+
+ describe "#cached!" do
+ let(:rubygems_source) { source_list.add_rubygems_source("remotes" => ["https://rubygems.org"]) }
+ let(:git_source) { source_list.add_git_source("uri" => "git://host/path.git") }
+ let(:path_source) { source_list.add_path_source("path" => "/path/to/gem") }
+
+ it "calls #cached! on all the sources" do
+ expect(rubygems_source).to receive(:cached!)
+ expect(git_source).to receive(:cached!)
+ expect(path_source).to receive(:cached!)
+ source_list.cached!
+ end
+ end
+
+ describe "#remote!" do
+ let(:rubygems_source) { source_list.add_rubygems_source("remotes" => ["https://rubygems.org"]) }
+ let(:git_source) { source_list.add_git_source("uri" => "git://host/path.git") }
+ let(:path_source) { source_list.add_path_source("path" => "/path/to/gem") }
+
+ it "calls #remote! on all the sources" do
+ expect(rubygems_source).to receive(:remote!)
+ expect(git_source).to receive(:remote!)
+ expect(path_source).to receive(:remote!)
+ source_list.remote!
+ end
+ end
+end
diff --git a/spec/bundler/bundler/source_spec.rb b/spec/bundler/bundler/source_spec.rb
new file mode 100644
index 0000000000..9ef8e7e50f
--- /dev/null
+++ b/spec/bundler/bundler/source_spec.rb
@@ -0,0 +1,154 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Source do
+ class ExampleSource < Bundler::Source
+ end
+
+ subject { ExampleSource.new }
+
+ describe "#unmet_deps" do
+ let(:specs) { double(:specs) }
+ let(:unmet_dependency_names) { double(:unmet_dependency_names) }
+
+ before do
+ allow(subject).to receive(:specs).and_return(specs)
+ allow(specs).to receive(:unmet_dependency_names).and_return(unmet_dependency_names)
+ end
+
+ it "should return the names of unmet dependencies" do
+ expect(subject.unmet_deps).to eq(unmet_dependency_names)
+ end
+ end
+
+ describe "#version_message" do
+ let(:spec) { double(:spec, :name => "nokogiri", :version => ">= 1.6", :platform => rb) }
+
+ shared_examples_for "the lockfile specs are not relevant" do
+ it "should return a string with the spec name and version" do
+ expect(subject.version_message(spec)).to eq("nokogiri >= 1.6")
+ end
+ end
+
+ context "when there are locked gems" do
+ let(:locked_gems) { double(:locked_gems) }
+
+ before { allow(Bundler).to receive(:locked_gems).and_return(locked_gems) }
+
+ context "that contain the relevant gem spec" do
+ before do
+ specs = double(:specs)
+ allow(locked_gems).to receive(:specs).and_return(specs)
+ allow(specs).to receive(:find).and_return(locked_gem)
+ end
+
+ context "without a version" do
+ let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => nil) }
+
+ it_behaves_like "the lockfile specs are not relevant"
+ end
+
+ context "with the same version" do
+ let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => ">= 1.6") }
+
+ it_behaves_like "the lockfile specs are not relevant"
+ end
+
+ context "with a different version" do
+ let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => "< 1.5") }
+
+ context "with color" do
+ before { Bundler.ui = Bundler::UI::Shell.new }
+
+ it "should return a string with the spec name and version and locked spec version" do
+ expect(subject.version_message(spec)).to eq("nokogiri >= 1.6\e[32m (was < 1.5)\e[0m")
+ end
+ end
+
+ context "without color" do
+ it "should return a string with the spec name and version and locked spec version" do
+ expect(subject.version_message(spec)).to eq("nokogiri >= 1.6 (was < 1.5)")
+ end
+ end
+ end
+
+ context "with a more recent version" do
+ let(:spec) { double(:spec, :name => "nokogiri", :version => "1.6.1", :platform => rb) }
+ let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => "1.7.0") }
+
+ context "with color" do
+ before { Bundler.ui = Bundler::UI::Shell.new }
+
+ it "should return a string with the locked spec version in yellow" do
+ expect(subject.version_message(spec)).to eq("nokogiri 1.6.1\e[33m (was 1.7.0)\e[0m")
+ end
+ end
+ end
+
+ context "with an older version" do
+ let(:spec) { double(:spec, :name => "nokogiri", :version => "1.7.1", :platform => rb) }
+ let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => "1.7.0") }
+
+ context "with color" do
+ before { Bundler.ui = Bundler::UI::Shell.new }
+
+ it "should return a string with the locked spec version in green" do
+ expect(subject.version_message(spec)).to eq("nokogiri 1.7.1\e[32m (was 1.7.0)\e[0m")
+ end
+ end
+ end
+ end
+
+ context "that do not contain the relevant gem spec" do
+ before do
+ specs = double(:specs)
+ allow(locked_gems).to receive(:specs).and_return(specs)
+ allow(specs).to receive(:find).and_return(nil)
+ end
+
+ it_behaves_like "the lockfile specs are not relevant"
+ end
+ end
+
+ context "when there are no locked gems" do
+ before { allow(Bundler).to receive(:locked_gems).and_return(nil) }
+
+ it_behaves_like "the lockfile specs are not relevant"
+ end
+ end
+
+ describe "#can_lock?" do
+ context "when the passed spec's source is equivalent" do
+ let(:spec) { double(:spec, :source => subject) }
+
+ it "should return true" do
+ expect(subject.can_lock?(spec)).to be_truthy
+ end
+ end
+
+ context "when the passed spec's source is not equivalent" do
+ let(:spec) { double(:spec, :source => double(:other_source)) }
+
+ it "should return false" do
+ expect(subject.can_lock?(spec)).to be_falsey
+ end
+ end
+ end
+
+ describe "#include?" do
+ context "when the passed source is equivalent" do
+ let(:source) { subject }
+
+ it "should return true" do
+ expect(subject).to include(source)
+ end
+ end
+
+ context "when the passed source is not equivalent" do
+ let(:source) { double(:source) }
+
+ it "should return false" do
+ expect(subject).to_not include(source)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/spec_set_spec.rb b/spec/bundler/bundler/spec_set_spec.rb
new file mode 100644
index 0000000000..6fedd38b50
--- /dev/null
+++ b/spec/bundler/bundler/spec_set_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::SpecSet do
+ let(:specs) do
+ [
+ build_spec("a", "1.0"),
+ build_spec("b", "1.0"),
+ build_spec("c", "1.1") do |s|
+ s.dep "a", "< 2.0"
+ s.dep "e", "> 0"
+ end,
+ build_spec("d", "2.0") do |s|
+ s.dep "a", "1.0"
+ s.dep "c", "~> 1.0"
+ end,
+ build_spec("e", "1.0.0.pre.1"),
+ ].flatten
+ end
+
+ subject { described_class.new(specs) }
+
+ context "enumerable methods" do
+ it "has a length" do
+ expect(subject.length).to eq(5)
+ end
+
+ it "has a size" do
+ expect(subject.size).to eq(5)
+ end
+ end
+
+ describe "#find_by_name_and_platform" do
+ let(:platform) { Gem::Platform.new("universal-darwin-64") }
+ let(:platform_spec) { build_spec("b", "2.0", platform).first }
+ let(:specs) do
+ [
+ build_spec("a", "1.0"),
+ platform_spec,
+ ].flatten
+ end
+
+ it "finds spec with given name and platform" do
+ spec = described_class.new(specs).find_by_name_and_platform("b", platform)
+ expect(spec).to eq platform_spec
+ end
+ end
+
+ describe "#merge" do
+ let(:other_specs) do
+ [
+ build_spec("f", "1.0"),
+ build_spec("g", "2.0"),
+ ].flatten
+ end
+
+ let(:other_spec_set) { described_class.new(other_specs) }
+
+ it "merges the items in each gemspec" do
+ new_spec_set = subject.merge(other_spec_set)
+ specs = new_spec_set.to_a.map(&:full_name)
+ expect(specs).to include("a-1.0")
+ expect(specs).to include("f-1.0")
+ end
+ end
+
+ describe "#to_a" do
+ it "returns the specs in order" do
+ expect(subject.to_a.map(&:full_name)).to eq %w[
+ a-1.0
+ b-1.0
+ e-1.0.0.pre.1
+ c-1.1
+ d-2.0
+ ]
+ end
+ end
+end
diff --git a/spec/bundler/bundler/ssl_certs/certificate_manager_spec.rb b/spec/bundler/bundler/ssl_certs/certificate_manager_spec.rb
new file mode 100644
index 0000000000..56606a830f
--- /dev/null
+++ b/spec/bundler/bundler/ssl_certs/certificate_manager_spec.rb
@@ -0,0 +1,140 @@
+# frozen_string_literal: true
+
+require "bundler/ssl_certs/certificate_manager"
+
+RSpec.describe Bundler::SSLCerts::CertificateManager do
+ let(:rubygems_path) { root }
+ let(:stub_cert) { File.join(root.to_s, "lib", "rubygems", "ssl_certs", "rubygems.org", "ssl-cert.pem") }
+ let(:rubygems_certs_dir) { File.join(root.to_s, "lib", "rubygems", "ssl_certs", "rubygems.org") }
+
+ subject { described_class.new(rubygems_path) }
+
+ # Pretend bundler root is rubygems root
+ before do
+ # Backing up rubygems ceriticates
+ FileUtils.mv(rubygems_certs_dir, rubygems_certs_dir + ".back") if ruby_core?
+
+ FileUtils.mkdir_p(rubygems_certs_dir)
+ FileUtils.touch(stub_cert)
+ end
+
+ after do
+ FileUtils.rm_rf(rubygems_certs_dir)
+
+ # Restore rubygems certificates
+ FileUtils.mv(rubygems_certs_dir + ".back", rubygems_certs_dir) if ruby_core?
+ end
+
+ describe "#update_from" do
+ let(:cert_manager) { double(:cert_manager) }
+
+ before { allow(described_class).to receive(:new).with(rubygems_path).and_return(cert_manager) }
+
+ it "should update the certs through a new certificate manager" do
+ allow(cert_manager).to receive(:update!)
+ expect(described_class.update_from!(rubygems_path)).to be_nil
+ end
+ end
+
+ describe "#initialize" do
+ it "should set bundler_cert_path as path of the subdir with bundler ssl certs" do
+ expect(subject.bundler_cert_path).to eq(File.join(root, "lib/bundler/ssl_certs"))
+ end
+
+ it "should set bundler_certs as the paths of the bundler ssl certs" do
+ expect(subject.bundler_certs).to include(File.join(root, "lib/bundler/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem"))
+ expect(subject.bundler_certs).to include(File.join(root, "lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem"))
+ end
+
+ context "when rubygems_path is not nil" do
+ it "should set rubygems_certs" do
+ expect(subject.rubygems_certs).to include(File.join(root, "lib", "rubygems", "ssl_certs", "rubygems.org", "ssl-cert.pem"))
+ end
+ end
+ end
+
+ describe "#up_to_date?" do
+ context "when bundler certs and rubygems certs are the same" do
+ before do
+ bundler_certs = Dir[File.join(root.to_s, "lib", "bundler", "ssl_certs", "**", "*.pem")]
+ FileUtils.rm(stub_cert)
+ FileUtils.cp(bundler_certs, rubygems_certs_dir)
+ end
+
+ it "should return true" do
+ expect(subject).to be_up_to_date
+ end
+ end
+
+ context "when bundler certs and rubygems certs are not the same" do
+ it "should return false" do
+ expect(subject).to_not be_up_to_date
+ end
+ end
+ end
+
+ describe "#update!" do
+ context "when certificate manager is not up to date" do
+ before do
+ allow(subject).to receive(:up_to_date?).and_return(false)
+ allow(bundler_fileutils).to receive(:rm)
+ allow(bundler_fileutils).to receive(:cp)
+ end
+
+ it "should remove the current bundler certs" do
+ expect(bundler_fileutils).to receive(:rm).with(subject.bundler_certs)
+ subject.update!
+ end
+
+ it "should copy the rubygems certs into bundler certs" do
+ expect(bundler_fileutils).to receive(:cp).with(subject.rubygems_certs, subject.bundler_cert_path)
+ subject.update!
+ end
+
+ it "should return nil" do
+ expect(subject.update!).to be_nil
+ end
+ end
+
+ context "when certificate manager is up to date" do
+ before { allow(subject).to receive(:up_to_date?).and_return(true) }
+
+ it "should return nil" do
+ expect(subject.update!).to be_nil
+ end
+ end
+ end
+
+ describe "#connect_to" do
+ let(:host) { "http://www.host.com" }
+ let(:http) { Net::HTTP.new(host, 443) }
+ let(:cert_store) { OpenSSL::X509::Store.new }
+ let(:http_header_response) { double(:http_header_response) }
+
+ before do
+ allow(Net::HTTP).to receive(:new).with(host, 443).and_return(http)
+ allow(OpenSSL::X509::Store).to receive(:new).and_return(cert_store)
+ allow(http).to receive(:head).with("/").and_return(http_header_response)
+ end
+
+ it "should use ssl for the http request" do
+ expect(http).to receive(:use_ssl=).with(true)
+ subject.connect_to(host)
+ end
+
+ it "use verify peer mode" do
+ expect(http).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
+ subject.connect_to(host)
+ end
+
+ it "set its cert store as a OpenSSL::X509::Store populated with bundler certs" do
+ expect(cert_store).to receive(:add_file).at_least(:once)
+ expect(http).to receive(:cert_store=).with(cert_store)
+ subject.connect_to(host)
+ end
+
+ it "return the headers of the request response" do
+ expect(subject.connect_to(host)).to eq(http_header_response)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/stub_specification_spec.rb b/spec/bundler/bundler/stub_specification_spec.rb
new file mode 100644
index 0000000000..5521d83769
--- /dev/null
+++ b/spec/bundler/bundler/stub_specification_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::StubSpecification do
+ let(:gemspec) do
+ Gem::Specification.new do |s|
+ s.name = "gemname"
+ s.version = "1.0.0"
+ s.loaded_from = __FILE__
+ end
+ end
+
+ let(:with_bundler_stub_spec) do
+ described_class.from_stub(gemspec)
+ end
+
+ if Bundler.rubygems.provides?(">= 2.1")
+ describe "#from_stub" do
+ it "returns the same stub if already a Bundler::StubSpecification" do
+ stub = described_class.from_stub(with_bundler_stub_spec)
+ expect(stub).to be(with_bundler_stub_spec)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/ui/shell_spec.rb b/spec/bundler/bundler/ui/shell_spec.rb
new file mode 100644
index 0000000000..951a446aff
--- /dev/null
+++ b/spec/bundler/bundler/ui/shell_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::UI::Shell do
+ subject { described_class.new }
+
+ before { subject.level = "debug" }
+
+ describe "#info" do
+ before { subject.level = "info" }
+ it "prints to stdout" do
+ expect { subject.info("info") }.to output("info\n").to_stdout
+ end
+ end
+
+ describe "#confirm" do
+ before { subject.level = "confirm" }
+ it "prints to stdout" do
+ expect { subject.confirm("confirm") }.to output("confirm\n").to_stdout
+ end
+ end
+
+ describe "#warn" do
+ before { subject.level = "warn" }
+ it "prints to stdout", :bundler => "< 2" do
+ expect { subject.warn("warning") }.to output("warning\n").to_stdout
+ end
+
+ it "prints to stderr", :bundler => "2" do
+ expect { subject.warn("warning") }.to output("warning\n").to_stderr
+ end
+
+ context "when stderr flag is enabled" do
+ before { Bundler.settings.temporary(:error_on_stderr => true) }
+ it "prints to stderr" do
+ expect { subject.warn("warning!") }.to output("warning!\n").to_stderr
+ end
+ end
+ end
+
+ describe "#debug" do
+ it "prints to stdout" do
+ expect { subject.debug("debug") }.to output("debug\n").to_stdout
+ end
+ end
+
+ describe "#error" do
+ before { subject.level = "error" }
+
+ it "prints to stdout", :bundler => "< 2" do
+ expect { subject.error("error!!!") }.to output("error!!!\n").to_stdout
+ end
+
+ it "prints to stderr", :bundler => "2" do
+ expect { subject.error("error!!!") }.to output("error!!!\n").to_stderr
+ end
+
+ context "when stderr flag is enabled" do
+ before { Bundler.settings.temporary(:error_on_stderr => true) }
+ it "prints to stderr" do
+ expect { subject.error("error!!!") }.to output("error!!!\n").to_stderr
+ end
+
+ context "when stderr is closed" do
+ it "doesn't report anything" do
+ output = capture(:stderr, :closed => true) do
+ subject.error("Something went wrong")
+ end
+ expect(output).to_not eq("Something went wrong\n")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/ui_spec.rb b/spec/bundler/bundler/ui_spec.rb
new file mode 100644
index 0000000000..6ef8729277
--- /dev/null
+++ b/spec/bundler/bundler/ui_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::UI do
+ describe Bundler::UI::Silent do
+ it "has the same instance methods as Shell", :ruby => ">= 1.9" do
+ shell = Bundler::UI::Shell
+ methods = proc do |cls|
+ cls.instance_methods.map do |i|
+ m = shell.instance_method(i)
+ [i, m.parameters]
+ end.sort_by(&:first)
+ end
+ expect(methods.call(described_class)).to eq(methods.call(shell))
+ end
+
+ it "has the same instance class as Shell", :ruby => ">= 1.9" do
+ shell = Bundler::UI::Shell
+ methods = proc do |cls|
+ cls.methods.map do |i|
+ m = shell.method(i)
+ [i, m.parameters]
+ end.sort_by(&:first)
+ end
+ expect(methods.call(described_class)).to eq(methods.call(shell))
+ end
+ end
+
+ describe Bundler::UI::Shell do
+ let(:options) { {} }
+ subject { described_class.new(options) }
+ describe "debug?" do
+ it "returns a boolean" do
+ subject.level = :debug
+ expect(subject.debug?).to eq(true)
+
+ subject.level = :error
+ expect(subject.debug?).to eq(false)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/uri_credentials_filter_spec.rb b/spec/bundler/bundler/uri_credentials_filter_spec.rb
new file mode 100644
index 0000000000..fe52d16306
--- /dev/null
+++ b/spec/bundler/bundler/uri_credentials_filter_spec.rb
@@ -0,0 +1,127 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::URICredentialsFilter do
+ subject { described_class }
+
+ describe "#credential_filtered_uri" do
+ shared_examples_for "original type of uri is maintained" do
+ it "maintains same type for return value as uri input type" do
+ expect(subject.credential_filtered_uri(uri)).to be_kind_of(uri.class)
+ end
+ end
+
+ shared_examples_for "sensitive credentials in uri are filtered out" do
+ context "authentication using oauth credentials" do
+ context "specified via 'x-oauth-basic'" do
+ let(:credentials) { "oauth_token:x-oauth-basic@" }
+
+ it "returns the uri without the oauth token" do
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(URI("https://x-oauth-basic@github.com/company/private-repo").to_s)
+ end
+
+ it_behaves_like "original type of uri is maintained"
+ end
+
+ context "specified via 'x'" do
+ let(:credentials) { "oauth_token:x@" }
+
+ it "returns the uri without the oauth token" do
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(URI("https://x@github.com/company/private-repo").to_s)
+ end
+
+ it_behaves_like "original type of uri is maintained"
+ end
+ end
+
+ context "authentication using login credentials" do
+ let(:credentials) { "username1:hunter3@" }
+
+ it "returns the uri without the password" do
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(URI("https://username1@github.com/company/private-repo").to_s)
+ end
+
+ it_behaves_like "original type of uri is maintained"
+ end
+
+ context "authentication without credentials" do
+ let(:credentials) { "" }
+
+ it "returns the same uri" do
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(uri.to_s)
+ end
+
+ it_behaves_like "original type of uri is maintained"
+ end
+ end
+
+ context "uri is a uri object" do
+ let(:uri) { URI("https://#{credentials}github.com/company/private-repo") }
+
+ it_behaves_like "sensitive credentials in uri are filtered out"
+ end
+
+ context "uri is a uri string" do
+ let(:uri) { "https://#{credentials}github.com/company/private-repo" }
+
+ it_behaves_like "sensitive credentials in uri are filtered out"
+ end
+
+ context "uri is a non-uri format string (ex. path)" do
+ let(:uri) { "/path/to/repo" }
+
+ it "returns the same uri" do
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(uri.to_s)
+ end
+
+ it_behaves_like "original type of uri is maintained"
+ end
+
+ context "uri is nil" do
+ let(:uri) { nil }
+
+ it "returns nil" do
+ expect(subject.credential_filtered_uri(uri)).to be_nil
+ end
+
+ it_behaves_like "original type of uri is maintained"
+ end
+ end
+
+ describe "#credential_filtered_string" do
+ let(:str_to_filter) { "This is a git message containing a uri #{uri}!" }
+ let(:credentials) { "" }
+ let(:uri) { URI("https://#{credentials}github.com/company/private-repo") }
+
+ context "with a uri that contains credentials" do
+ let(:credentials) { "oauth_token:x-oauth-basic@" }
+
+ it "returns the string without the sensitive credentials" do
+ expect(subject.credential_filtered_string(str_to_filter, uri)).to eq(
+ "This is a git message containing a uri https://x-oauth-basic@github.com/company/private-repo!"
+ )
+ end
+ end
+
+ context "that does not contains credentials" do
+ it "returns the same string" do
+ expect(subject.credential_filtered_string(str_to_filter, uri)).to eq(str_to_filter)
+ end
+ end
+
+ context "string to filter is nil" do
+ let(:str_to_filter) { nil }
+
+ it "returns nil" do
+ expect(subject.credential_filtered_string(str_to_filter, uri)).to be_nil
+ end
+ end
+
+ context "uri to filter out is nil" do
+ let(:uri) { nil }
+
+ it "returns the same string" do
+ expect(subject.credential_filtered_string(str_to_filter, uri)).to eq(str_to_filter)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/vendored_persistent_spec.rb b/spec/bundler/bundler/vendored_persistent_spec.rb
new file mode 100644
index 0000000000..338431c4a6
--- /dev/null
+++ b/spec/bundler/bundler/vendored_persistent_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+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(subject.http_class) }
+ let(:tls_version) { "TLSv1.2" }
+ let(:socket) { double("Socket") }
+ let(:socket_io) { double("SocketIO") }
+
+ before do
+ allow(connection).to receive(:use_ssl?).and_return(!tls_version.nil?)
+ allow(socket).to receive(:io).and_return(socket_io)
+ 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(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(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
new file mode 100644
index 0000000000..ccbb9285d5
--- /dev/null
+++ b/spec/bundler/bundler/version_ranges_spec.rb
@@ -0,0 +1,37 @@
+# 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, "!= 1", "< 2", "> 2"
+ include_examples "empty?", true, "!= 1", "<= 1", ">= 1"
+ include_examples "empty?", true, "< 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/bundler/worker_spec.rb b/spec/bundler/bundler/worker_spec.rb
new file mode 100644
index 0000000000..2e5642709d
--- /dev/null
+++ b/spec/bundler/bundler/worker_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require "bundler/worker"
+
+RSpec.describe Bundler::Worker do
+ let(:size) { 5 }
+ let(:name) { "Spec Worker" }
+ let(:function) { proc {|object, worker_number| [object, worker_number] } }
+ subject { described_class.new(size, name, function) }
+
+ after { subject.stop }
+
+ describe "#initialize" do
+ context "when Thread.start raises ThreadError" do
+ it "raises when no threads can be created" do
+ allow(Thread).to receive(:start).and_raise(ThreadError, "error creating thread")
+
+ expect { subject.enq "a" }.to raise_error(Bundler::ThreadCreationError, "Failed to create threads for the Spec Worker worker: error creating thread")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/yaml_serializer_spec.rb b/spec/bundler/bundler/yaml_serializer_spec.rb
new file mode 100644
index 0000000000..1241c74bbf
--- /dev/null
+++ b/spec/bundler/bundler/yaml_serializer_spec.rb
@@ -0,0 +1,194 @@
+# frozen_string_literal: true
+
+require "bundler/yaml_serializer"
+
+RSpec.describe Bundler::YAMLSerializer do
+ subject(:serializer) { Bundler::YAMLSerializer }
+
+ describe "#dump" do
+ it "works for simple hash" do
+ hash = { "Q" => "Where does Thursday come before Wednesday? In the dictionary. :P" }
+
+ expected = strip_whitespace <<-YAML
+ ---
+ Q: "Where does Thursday come before Wednesday? In the dictionary. :P"
+ YAML
+
+ expect(serializer.dump(hash)).to eq(expected)
+ end
+
+ it "handles nested hash" do
+ hash = {
+ "nice-one" => {
+ "read_ahead" => "All generalizations are false, including this one",
+ },
+ }
+
+ expected = strip_whitespace <<-YAML
+ ---
+ nice-one:
+ read_ahead: "All generalizations are false, including this one"
+ YAML
+
+ expect(serializer.dump(hash)).to eq(expected)
+ end
+
+ it "array inside an hash" do
+ hash = {
+ "nested_hash" => {
+ "contains_array" => [
+ "Jack and Jill went up the hill",
+ "To fetch a pail of water.",
+ "Jack fell down and broke his crown,",
+ "And Jill came tumbling after.",
+ ],
+ },
+ }
+
+ expected = strip_whitespace <<-YAML
+ ---
+ nested_hash:
+ contains_array:
+ - "Jack and Jill went up the hill"
+ - "To fetch a pail of water."
+ - "Jack fell down and broke his crown,"
+ - "And Jill came tumbling after."
+ YAML
+
+ expect(serializer.dump(hash)).to eq(expected)
+ end
+ end
+
+ describe "#load" do
+ it "works for simple hash" do
+ yaml = strip_whitespace <<-YAML
+ ---
+ Jon: "Air is free dude!"
+ Jack: "Yes.. until you buy a bag of chips!"
+ YAML
+
+ hash = {
+ "Jon" => "Air is free dude!",
+ "Jack" => "Yes.. until you buy a bag of chips!",
+ }
+
+ expect(serializer.load(yaml)).to eq(hash)
+ end
+
+ it "works for nested hash" do
+ yaml = strip_whitespace <<-YAML
+ ---
+ baa:
+ baa: "black sheep"
+ have: "you any wool?"
+ yes: "merry have I"
+ three: "bags full"
+ YAML
+
+ hash = {
+ "baa" => {
+ "baa" => "black sheep",
+ "have" => "you any wool?",
+ "yes" => "merry have I",
+ },
+ "three" => "bags full",
+ }
+
+ expect(serializer.load(yaml)).to eq(hash)
+ end
+
+ it "handles colon in key/value" do
+ yaml = strip_whitespace <<-YAML
+ BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/: http://rubygems-mirror.org
+ YAML
+
+ expect(serializer.load(yaml)).to eq("BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/" => "http://rubygems-mirror.org")
+ end
+
+ it "handles arrays inside hashes" do
+ yaml = strip_whitespace <<-YAML
+ ---
+ nested_hash:
+ contains_array:
+ - "Why shouldn't you write with a broken pencil?"
+ - "Because it's pointless!"
+ YAML
+
+ hash = {
+ "nested_hash" => {
+ "contains_array" => [
+ "Why shouldn't you write with a broken pencil?",
+ "Because it's pointless!",
+ ],
+ },
+ }
+
+ expect(serializer.load(yaml)).to eq(hash)
+ end
+
+ it "handles windows-style CRLF line endings" do
+ yaml = strip_whitespace(<<-YAML).gsub("\n", "\r\n")
+ ---
+ nested_hash:
+ contains_array:
+ - "Why shouldn't you write with a broken pencil?"
+ - "Because it's pointless!"
+ - oh so silly
+ YAML
+
+ hash = {
+ "nested_hash" => {
+ "contains_array" => [
+ "Why shouldn't you write with a broken pencil?",
+ "Because it's pointless!",
+ "oh so silly",
+ ],
+ },
+ }
+
+ expect(serializer.load(yaml)).to eq(hash)
+ end
+ end
+
+ describe "against yaml lib" do
+ let(:hash) do
+ {
+ "a_joke" => {
+ "my-stand" => "I can totally keep secrets",
+ "but" => "The people I tell them to can't :P",
+ "wouldn't it be funny if this string were empty?" => "",
+ },
+ "more" => {
+ "first" => [
+ "Can a kangaroo jump higher than a house?",
+ "Of course, a house doesn't jump at all.",
+ ],
+ "second" => [
+ "What did the sea say to the sand?",
+ "Nothing, it simply waved.",
+ ],
+ "array with empty string" => [""],
+ },
+ "sales" => {
+ "item" => "A Parachute",
+ "description" => "Only used once, never opened.",
+ },
+ "one-more" => "I'd tell you a chemistry joke but I know I wouldn't get a reaction.",
+ }
+ end
+
+ context "#load" do
+ it "retrieves the original hash" do
+ require "yaml"
+ expect(serializer.load(YAML.dump(hash))).to eq(hash)
+ end
+ end
+
+ context "#dump" do
+ it "retrieves the original hash" do
+ require "yaml"
+ expect(YAML.load(serializer.dump(hash))).to eq(hash)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/cache/cache_path_spec.rb b/spec/bundler/cache/cache_path_spec.rb
new file mode 100644
index 0000000000..69d3809964
--- /dev/null
+++ b/spec/bundler/cache/cache_path_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle package" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ context "with --cache-path" do
+ it "caches gems at given path" do
+ bundle :package, "cache-path" => "vendor/cache-foo"
+ expect(bundled_app("vendor/cache-foo/rack-1.0.0.gem")).to exist
+ end
+ end
+
+ context "with config cache_path" do
+ it "caches gems at given path" do
+ bundle "config cache_path vendor/cache-foo"
+ bundle :package
+ expect(bundled_app("vendor/cache-foo/rack-1.0.0.gem")).to exist
+ end
+ end
+
+ context "with absolute --cache-path" do
+ it "caches gems at given path" do
+ bundle :package, "cache-path" => "/tmp/cache-foo"
+ expect(bundled_app("/tmp/cache-foo/rack-1.0.0.gem")).to exist
+ end
+ end
+end
diff --git a/spec/bundler/cache/gems_spec.rb b/spec/bundler/cache/gems_spec.rb
new file mode 100644
index 0000000000..4a0b953830
--- /dev/null
+++ b/spec/bundler/cache/gems_spec.rb
@@ -0,0 +1,304 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle cache" do
+ shared_examples_for "when there are only gemsources" do
+ before :each do
+ gemfile <<-G
+ gem 'rack'
+ G
+
+ system_gems "rack-1.0.0", :path => :bundle_path
+ bundle! :cache
+ end
+
+ it "copies the .gem file to vendor/cache" do
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ end
+
+ it "uses the cache as a source when installing gems" do
+ build_gem "omg", :path => bundled_app("vendor/cache")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "omg"
+ G
+
+ expect(the_bundle).to include_gems "omg 1.0.0"
+ end
+
+ it "uses the cache as a source when installing gems with --local" do
+ system_gems [], :path => :bundle_path
+ bundle "install --local"
+
+ expect(the_bundle).to include_gems("rack 1.0.0")
+ end
+
+ it "does not reinstall gems from the cache if they exist on the system" do
+ build_gem "rack", "1.0.0", :path => bundled_app("vendor/cache") do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems("rack 1.0.0")
+ end
+
+ it "does not reinstall gems from the cache if they exist in the bundle" do
+ system_gems "rack-1.0.0", :path => :bundle_path
+
+ gemfile <<-G
+ gem "rack"
+ G
+
+ build_gem "rack", "1.0.0", :path => bundled_app("vendor/cache") do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+
+ bundle! :install, :local => true
+ expect(the_bundle).to include_gems("rack 1.0.0")
+ end
+
+ it "creates a lockfile" do
+ cache_gems "rack-1.0.0"
+
+ gemfile <<-G
+ gem "rack"
+ G
+
+ bundle "cache"
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+ end
+
+ context "using system gems" do
+ before { bundle! "config path.system true" }
+ it_behaves_like "when there are only gemsources"
+ end
+
+ context "installing into a local path" do
+ before { bundle! "config path ./.bundle" }
+ it_behaves_like "when there are only gemsources"
+ end
+
+ describe "when there is a built-in gem", :ruby => "2.0" do
+ before :each do
+ build_repo2 do
+ build_gem "builtin_gem", "1.0.2"
+ end
+
+ build_gem "builtin_gem", "1.0.2", :to_system => true do |s|
+ s.summary = "This builtin_gem is bundled with Ruby"
+ end
+
+ FileUtils.rm("#{system_gem_path}/cache/builtin_gem-1.0.2.gem")
+ end
+
+ it "uses builtin gems when installing to system gems" do
+ bundle! "config path.system true"
+ install_gemfile %(gem 'builtin_gem', '1.0.2')
+ expect(the_bundle).to include_gems("builtin_gem 1.0.2")
+ end
+
+ it "caches remote and builtin gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'builtin_gem', '1.0.2'
+ gem 'rack', '1.0.0'
+ G
+
+ bundle :cache
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/builtin_gem-1.0.2.gem")).to exist
+ end
+
+ it "doesn't make remote request after caching the gem" do
+ build_gem "builtin_gem_2", "1.0.2", :path => bundled_app("vendor/cache") do |s|
+ s.summary = "This builtin_gem is bundled with Ruby"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'builtin_gem_2', '1.0.2'
+ G
+
+ bundle "install --local"
+ expect(the_bundle).to include_gems("builtin_gem_2 1.0.2")
+ end
+
+ it "errors if the builtin gem isn't available to cache" do
+ bundle! "config path.system true"
+
+ install_gemfile <<-G
+ gem 'builtin_gem', '1.0.2'
+ G
+
+ bundle :cache
+ expect(exitstatus).to_not eq(0) if exitstatus
+ expect(out).to include("builtin_gem-1.0.2 is built in to Ruby, and can't be cached")
+ end
+ end
+
+ describe "when there are also git sources" do
+ before do
+ build_git "foo"
+ system_gems "rack-1.0.0"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ gem 'rack'
+ G
+ end
+
+ it "still works" do
+ bundle :cache
+
+ system_gems []
+ bundle "install --local"
+
+ expect(the_bundle).to include_gems("rack 1.0.0", "foo 1.0")
+ end
+
+ it "should not explode if the lockfile is not present" do
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ bundle :cache
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+ end
+
+ describe "when previously cached" do
+ before :each do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ gem "actionpack"
+ G
+ bundle :cache
+ expect(cached_gem("rack-1.0.0")).to exist
+ expect(cached_gem("actionpack-2.3.2")).to exist
+ expect(cached_gem("activesupport-2.3.2")).to exist
+ end
+
+ it "re-caches during install" do
+ cached_gem("rack-1.0.0").rmtree
+ bundle :install
+ expect(out).to include("Updating files in vendor/cache")
+ expect(cached_gem("rack-1.0.0")).to exist
+ end
+
+ it "adds and removes when gems are updated" do
+ update_repo2
+ bundle "update", :all => bundle_update_requires_all?
+ expect(cached_gem("rack-1.2")).to exist
+ expect(cached_gem("rack-1.0.0")).not_to exist
+ end
+
+ it "adds new gems and dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails"
+ G
+ expect(cached_gem("rails-2.3.2")).to exist
+ expect(cached_gem("activerecord-2.3.2")).to exist
+ end
+
+ it "removes .gems for removed gems and dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ G
+ expect(cached_gem("rack-1.0.0")).to exist
+ expect(cached_gem("actionpack-2.3.2")).not_to exist
+ expect(cached_gem("activesupport-2.3.2")).not_to exist
+ end
+
+ it "removes .gems when gem changes to git source" do
+ build_git "rack"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack", :git => "#{lib_path("rack-1.0")}"
+ gem "actionpack"
+ G
+ expect(cached_gem("rack-1.0.0")).not_to exist
+ expect(cached_gem("actionpack-2.3.2")).to exist
+ expect(cached_gem("activesupport-2.3.2")).to exist
+ end
+
+ it "doesn't remove gems that are for another platform" do
+ simulate_platform "java" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+
+ bundle :cache
+ expect(cached_gem("platform_specific-1.0-java")).to exist
+ end
+
+ simulate_new_machine
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+
+ expect(cached_gem("platform_specific-1.0-#{Bundler.local_platform}")).to exist
+ expect(cached_gem("platform_specific-1.0-java")).to exist
+ end
+
+ it "doesn't remove gems with mismatched :rubygems_version or :date" do
+ cached_gem("rack-1.0.0").rmtree
+ build_gem "rack", "1.0.0",
+ :path => bundled_app("vendor/cache"),
+ :rubygems_version => "1.3.2"
+ simulate_new_machine
+
+ bundle :install
+ expect(cached_gem("rack-1.0.0")).to exist
+ end
+
+ it "handles directories and non .gem files in the cache" do
+ bundled_app("vendor/cache/foo").mkdir
+ File.open(bundled_app("vendor/cache/bar"), "w") {|f| f.write("not a gem") }
+ bundle :cache
+ end
+
+ it "does not say that it is removing gems when it isn't actually doing so" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundle "cache"
+ bundle "install"
+ expect(out).not_to match(/removing/i)
+ end
+
+ it "does not warn about all if it doesn't have any git/path dependency" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundle "cache"
+ expect(out).not_to match(/\-\-all/)
+ end
+
+ it "should install gems with the name bundler in them (that aren't bundler)" do
+ build_gem "foo-bundler", "1.0",
+ :path => bundled_app("vendor/cache")
+
+ install_gemfile <<-G
+ gem "foo-bundler"
+ G
+
+ expect(the_bundle).to include_gems "foo-bundler 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/cache/git_spec.rb b/spec/bundler/cache/git_spec.rb
new file mode 100644
index 0000000000..33387dbbb2
--- /dev/null
+++ b/spec/bundler/cache/git_spec.rb
@@ -0,0 +1,214 @@
+# frozen_string_literal: true
+
+RSpec.describe "git base name" do
+ it "base_name should strip private repo uris" do
+ source = Bundler::Source::Git.new("uri" => "git@github.com:bundler.git")
+ expect(source.send(:base_name)).to eq("bundler")
+ end
+
+ it "base_name should strip network share paths" do
+ source = Bundler::Source::Git.new("uri" => "//MachineName/ShareFolder")
+ expect(source.send(:base_name)).to eq("ShareFolder")
+ end
+end
+
+%w[cache package].each do |cmd|
+ RSpec.describe "bundle #{cmd} with git" do
+ it "copies repository to vendor cache and uses it" do
+ git = build_git "foo"
+ ref = git.ref_for("master", 11)
+
+ install_gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.git")).not_to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.bundlecache")).to be_file
+
+ FileUtils.rm_rf lib_path("foo-1.0")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "copies repository to vendor cache and uses it even when installed with bundle --path" do
+ git = build_git "foo"
+ ref = git.ref_for("master", 11)
+
+ install_gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle "install --path vendor/bundle"
+ bundle "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.git")).not_to exist
+
+ FileUtils.rm_rf lib_path("foo-1.0")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "runs twice without exploding" do
+ build_git "foo"
+
+ install_gemfile! <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle! "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+ bundle! "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(last_command.stdout).to include "Updating files in vendor/cache"
+ FileUtils.rm_rf lib_path("foo-1.0")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "tracks updates" do
+ git = build_git "foo"
+ old_ref = git.ref_for("master", 11)
+
+ install_gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+
+ update_git "foo" do |s|
+ s.write "lib/foo.rb", "puts :CACHE"
+ end
+
+ ref = git.ref_for("master", 11)
+ expect(ref).not_to eq(old_ref)
+
+ bundle! "update", :all => bundle_update_requires_all?
+ bundle! "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{old_ref}")).not_to exist
+
+ FileUtils.rm_rf lib_path("foo-1.0")
+ run! "require 'foo'"
+ expect(out).to eq("CACHE")
+ end
+
+ it "tracks updates when specifying the gem" do
+ git = build_git "foo"
+ old_ref = git.ref_for("master", 11)
+
+ install_gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle! cmd, forgotten_command_line_options([:all, :cache_all] => true)
+
+ update_git "foo" do |s|
+ s.write "lib/foo.rb", "puts :CACHE"
+ end
+
+ ref = git.ref_for("master", 11)
+ expect(ref).not_to eq(old_ref)
+
+ bundle "update foo"
+
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{old_ref}")).not_to exist
+
+ FileUtils.rm_rf lib_path("foo-1.0")
+ run "require 'foo'"
+ expect(out).to eq("CACHE")
+ end
+
+ it "uses the local repository to generate the cache" do
+ git = build_git "foo"
+ ref = git.ref_for("master", 11)
+
+ gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-invalid")}', :branch => :master
+ G
+
+ bundle %(config local.foo #{lib_path("foo-1.0")})
+ bundle "install"
+ bundle "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/foo-invalid-#{ref}")).to exist
+
+ # Updating the local still uses the local.
+ update_git "foo" do |s|
+ s.write "lib/foo.rb", "puts :LOCAL"
+ end
+
+ run "require 'foo'"
+ expect(out).to eq("LOCAL")
+ end
+
+ it "copies repository to vendor cache, including submodules" do
+ build_git "submodule", "1.0"
+
+ git = build_git "has_submodule", "1.0" do |s|
+ s.add_dependency "submodule"
+ end
+
+ Dir.chdir(lib_path("has_submodule-1.0")) do
+ sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0"
+ `git commit -m "submodulator"`
+ end
+
+ install_gemfile <<-G
+ git "#{lib_path("has_submodule-1.0")}", :submodules => true do
+ gem "has_submodule"
+ end
+ G
+
+ ref = git.ref_for("master", 11)
+ bundle "#{cmd}", forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/has_submodule-1.0-#{ref}")).to exist
+ expect(bundled_app("vendor/cache/has_submodule-1.0-#{ref}/submodule-1.0")).to exist
+ expect(the_bundle).to include_gems "has_submodule 1.0"
+ end
+
+ it "displays warning message when detecting git repo in Gemfile", :bundler => "< 2" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle "#{cmd}"
+
+ expect(out).to include("Your Gemfile contains path and git dependencies.")
+ end
+
+ it "does not display warning message if cache_all is set in bundle config" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+ bundle cmd
+
+ expect(out).not_to include("Your Gemfile contains path and git dependencies.")
+ end
+
+ it "caches pre-evaluated gemspecs" do
+ git = build_git "foo"
+
+ # Insert a gemspec method that shells out
+ spec_lines = lib_path("foo-1.0/foo.gemspec").read.split("\n")
+ spec_lines.insert(-2, "s.description = `echo bob`")
+ update_git("foo") {|s| s.write "foo.gemspec", spec_lines.join("\n") }
+
+ install_gemfile <<-G
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+
+ ref = git.ref_for("master", 11)
+ gemspec = bundled_app("vendor/cache/foo-1.0-#{ref}/foo.gemspec").read
+ expect(gemspec).to_not match("`echo bob`")
+ end
+ end
+end
diff --git a/spec/bundler/cache/path_spec.rb b/spec/bundler/cache/path_spec.rb
new file mode 100644
index 0000000000..8c6a843476
--- /dev/null
+++ b/spec/bundler/cache/path_spec.rb
@@ -0,0 +1,139 @@
+# frozen_string_literal: true
+
+%w[cache package].each do |cmd|
+ RSpec.describe "bundle #{cmd} with path" do
+ it "is no-op when the path is within the bundle" do
+ build_lib "foo", :path => bundled_app("lib/foo")
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{bundled_app("lib/foo")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+ expect(bundled_app("vendor/cache/foo-1.0")).not_to exist
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "copies when the path is outside the bundle " do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+ expect(bundled_app("vendor/cache/foo-1.0")).to exist
+ expect(bundled_app("vendor/cache/foo-1.0/.bundlecache")).to be_file
+
+ FileUtils.rm_rf lib_path("foo-1.0")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "copies when the path is outside the bundle and the paths intersect" do
+ libname = File.basename(Dir.pwd) + "_gem"
+ libpath = File.join(File.dirname(Dir.pwd), libname)
+
+ build_lib libname, :path => libpath
+
+ install_gemfile <<-G
+ gem "#{libname}", :path => '#{libpath}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+ expect(bundled_app("vendor/cache/#{libname}")).to exist
+ expect(bundled_app("vendor/cache/#{libname}/.bundlecache")).to be_file
+
+ FileUtils.rm_rf libpath
+ expect(the_bundle).to include_gems "#{libname} 1.0"
+ end
+
+ it "updates the path on each cache" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+
+ build_lib "foo" do |s|
+ s.write "lib/foo.rb", "puts :CACHE"
+ end
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/foo-1.0")).to exist
+ FileUtils.rm_rf lib_path("foo-1.0")
+
+ run "require 'foo'"
+ expect(out).to eq("CACHE")
+ end
+
+ it "removes stale entries cache" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+
+ install_gemfile <<-G
+ gem "bar", :path => '#{lib_path("bar-1.0")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+ expect(bundled_app("vendor/cache/bar-1.0")).not_to exist
+ end
+
+ it "raises a warning without --all", :bundler => "< 2" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle cmd
+ expect(out).to match(/please pass the \-\-all flag/)
+ expect(bundled_app("vendor/cache/foo-1.0")).not_to exist
+ end
+
+ it "stores the given flag" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+ build_lib "bar"
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ gem "bar", :path => '#{lib_path("bar-1.0")}'
+ G
+
+ bundle cmd
+ expect(bundled_app("vendor/cache/bar-1.0")).to exist
+ end
+
+ it "can rewind chosen configuration" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle cmd, forgotten_command_line_options([:all, :cache_all] => true)
+ build_lib "baz"
+
+ gemfile <<-G
+ gem "foo", :path => '#{lib_path("foo-1.0")}'
+ gem "baz", :path => '#{lib_path("baz-1.0")}'
+ G
+
+ bundle "#{cmd} --no-all"
+ expect(bundled_app("vendor/cache/baz-1.0")).not_to exist
+ end
+ end
+end
diff --git a/spec/bundler/cache/platform_spec.rb b/spec/bundler/cache/platform_spec.rb
new file mode 100644
index 0000000000..c0622a3c94
--- /dev/null
+++ b/spec/bundler/cache/platform_spec.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle cache with multiple platforms" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ platforms :mri, :rbx do
+ gem "rack", "1.0.0"
+ end
+
+ platforms :jruby do
+ gem "activesupport", "2.3.5"
+ end
+ G
+
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ activesupport (2.3.5)
+
+ PLATFORMS
+ ruby
+ java
+
+ DEPENDENCIES
+ rack (1.0.0)
+ activesupport (2.3.5)
+ G
+
+ cache_gems "rack-1.0.0", "activesupport-2.3.5"
+ end
+
+ it "ensures that a successful bundle install does not delete gems for other platforms" do
+ bundle! "install"
+
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/activesupport-2.3.5.gem")).to exist
+ end
+
+ it "ensures that a successful bundle update does not delete gems for other platforms" do
+ bundle! "update", :all => bundle_update_requires_all?
+
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/activesupport-2.3.5.gem")).to exist
+ end
+end
diff --git a/spec/bundler/commands/add_spec.rb b/spec/bundler/commands/add_spec.rb
new file mode 100644
index 0000000000..9f11adbcf8
--- /dev/null
+++ b/spec/bundler/commands/add_spec.rb
@@ -0,0 +1,217 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle add" do
+ before :each do
+ build_repo2 do
+ build_gem "foo", "1.1"
+ build_gem "foo", "2.0"
+ build_gem "baz", "1.2.3"
+ build_gem "bar", "0.12.3"
+ build_gem "cat", "0.12.3.pre"
+ build_gem "dog", "1.1.3.pre"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "weakling", "~> 0.0.1"
+ G
+ end
+
+ context "when no gems are specified" do
+ it "shows error" do
+ bundle "add"
+
+ expect(last_command.bundler_err).to include("Please specify gems to add")
+ end
+ end
+
+ describe "without version specified" do
+ it "version requirement becomes ~> major.minor.patch when resolved version is < 1.0" do
+ bundle "add 'bar'"
+ expect(bundled_app("Gemfile").read).to match(/gem "bar", "~> 0.12.3"/)
+ expect(the_bundle).to include_gems "bar 0.12.3"
+ end
+
+ it "version requirement becomes ~> major.minor when resolved version is > 1.0" do
+ bundle "add 'baz'"
+ expect(bundled_app("Gemfile").read).to match(/gem "baz", "~> 1.2"/)
+ expect(the_bundle).to include_gems "baz 1.2.3"
+ end
+
+ it "version requirement becomes ~> major.minor.patch.pre when resolved version is < 1.0" do
+ bundle "add 'cat'"
+ expect(bundled_app("Gemfile").read).to match(/gem "cat", "~> 0.12.3.pre"/)
+ expect(the_bundle).to include_gems "cat 0.12.3.pre"
+ end
+
+ it "version requirement becomes ~> major.minor.pre when resolved version is > 1.0.pre" do
+ bundle "add 'dog'"
+ expect(bundled_app("Gemfile").read).to match(/gem "dog", "~> 1.1.pre"/)
+ expect(the_bundle).to include_gems "dog 1.1.3.pre"
+ end
+ end
+
+ describe "with --version" do
+ it "adds dependency of specified version and runs install" do
+ bundle "add 'foo' --version='~> 1.0'"
+ expect(bundled_app("Gemfile").read).to match(/gem "foo", "~> 1.0"/)
+ expect(the_bundle).to include_gems "foo 1.1"
+ end
+
+ it "adds multiple version constraints when specified" do
+ requirements = ["< 3.0", "> 1.0"]
+ bundle "add 'foo' --version='#{requirements.join(", ")}'"
+ expect(bundled_app("Gemfile").read).to match(/gem "foo", #{Gem::Requirement.new(requirements).as_list.map(&:dump).join(', ')}/)
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --group" do
+ it "adds dependency for the specified group" do
+ bundle "add 'foo' --group='development'"
+ expect(bundled_app("Gemfile").read).to match(/gem "foo", "~> 2.0", :group => :development/)
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+
+ it "adds dependency to more than one group" do
+ bundle "add 'foo' --group='development, test'"
+ expect(bundled_app("Gemfile").read).to match(/gem "foo", "~> 2.0", :groups => \[:development, :test\]/)
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --source" do
+ it "adds dependency with specified source" do
+ bundle "add 'foo' --source='file://#{gem_repo2}'"
+
+ expect(bundled_app("Gemfile").read).to match(%r{gem "foo", "~> 2.0", :source => "file:\/\/#{gem_repo2}"})
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --skip-install" do
+ it "adds gem to Gemfile but is not installed" do
+ bundle "add foo --skip-install --version=2.0"
+
+ expect(bundled_app("Gemfile").read).to match(/gem "foo", "= 2.0"/)
+ expect(the_bundle).to_not include_gems "foo 2.0"
+ end
+ end
+
+ it "using combination of short form options works like long form" do
+ bundle "add 'foo' -s='file://#{gem_repo2}' -g='development' -v='~>1.0'"
+ expect(bundled_app("Gemfile").read).to include %(gem "foo", "~> 1.0", :group => :development, :source => "file://#{gem_repo2}")
+ expect(the_bundle).to include_gems "foo 1.1"
+ end
+
+ it "shows error message when version is not formatted correctly" do
+ bundle "add 'foo' -v='~>1 . 0'"
+ expect(out).to match("Invalid gem requirement pattern '~>1 . 0'")
+ end
+
+ it "shows error message when gem cannot be found" do
+ bundle "add 'werk_it'"
+ expect(out).to match("Could not find gem 'werk_it' in")
+
+ bundle "add 'werk_it' -s='file://#{gem_repo2}'"
+ expect(out).to match("Could not find gem 'werk_it' in rubygems repository")
+ end
+
+ it "shows error message when source cannot be reached" do
+ bundle "add 'baz' --source='http://badhostasdf'"
+ expect(out).to include("Could not reach host badhostasdf. Check your network connection and try again.")
+
+ bundle "add 'baz' --source='file://does/not/exist'"
+ expect(out).to include("Could not fetch specs from file://does/not/exist/")
+ end
+
+ describe "with --optimistic" do
+ it "adds optimistic version" do
+ bundle! "add 'foo' --optimistic"
+ expect(bundled_app("Gemfile").read).to include %(gem "foo", ">= 2.0")
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --strict option" do
+ it "adds strict version" do
+ bundle! "add 'foo' --strict"
+ expect(bundled_app("Gemfile").read).to include %(gem "foo", "= 2.0")
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with no option" do
+ it "adds pessimistic version" do
+ bundle! "add 'foo'"
+ expect(bundled_app("Gemfile").read).to include %(gem "foo", "~> 2.0")
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --optimistic and --strict" do
+ it "throws error" do
+ bundle "add 'foo' --strict --optimistic"
+
+ expect(out).to include("You can not specify `--strict` and `--optimistic` at the same time")
+ end
+ end
+
+ context "multiple gems" do
+ it "adds multiple gems to gemfile" do
+ bundle! "add bar baz"
+
+ expect(bundled_app("Gemfile").read).to match(/gem "bar", "~> 0.12.3"/)
+ expect(bundled_app("Gemfile").read).to match(/gem "baz", "~> 1.2"/)
+ end
+
+ it "throws error if any of the specified gems are present in the gemfile with different version" do
+ bundle "add weakling bar"
+
+ expect(out).to include("You cannot specify the same gem twice with different version requirements")
+ expect(out).to include("You specified: weakling (~> 0.0.1) and weakling (>= 0).")
+ end
+ end
+
+ describe "when a gem is added which is already specified in Gemfile with version" do
+ it "shows an error when added with different version requirement" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack", "1.0"
+ G
+
+ bundle "add 'rack' --version=1.1"
+
+ expect(out).to include("You cannot specify the same gem twice with different version requirements")
+ expect(out).to include("If you want to update the gem version, run `bundle update rack`. You may also need to change the version requirement specified in the Gemfile if it's too restrictive")
+ end
+
+ it "shows error when added without version requirements" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack", "1.0"
+ G
+
+ bundle "add 'rack'"
+
+ expect(out).to include("Gem already added.")
+ expect(out).to include("You cannot specify the same gem twice with different version requirements")
+ expect(out).not_to include("If you want to update the gem version, run `bundle update rack`. You may also need to change the version requirement specified in the Gemfile if it's too restrictive")
+ end
+ end
+
+ describe "when a gem is added which is already specified in Gemfile without version" do
+ it "shows an error when added with different version requirement" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ G
+
+ bundle "add 'rack' --version=1.1"
+
+ expect(out).to include("You cannot specify the same gem twice with different version requirements")
+ expect(out).to include("If you want to update the gem version, run `bundle update rack`.")
+ expect(out).not_to include("You may also need to change the version requirement specified in the Gemfile if it's too restrictive")
+ end
+ end
+end
diff --git a/spec/bundler/commands/binstubs_spec.rb b/spec/bundler/commands/binstubs_spec.rb
new file mode 100644
index 0000000000..6a705d3423
--- /dev/null
+++ b/spec/bundler/commands/binstubs_spec.rb
@@ -0,0 +1,453 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle binstubs <gem>" do
+ context "when the gem exists in the lockfile" do
+ it "sets up the binstub" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack"
+
+ expect(bundled_app("bin/rackup")).to exist
+ end
+
+ it "does not install other binstubs" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rails"
+ G
+
+ bundle "binstubs rails"
+
+ expect(bundled_app("bin/rackup")).not_to exist
+ expect(bundled_app("bin/rails")).to exist
+ end
+
+ it "does install multiple binstubs" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rails"
+ G
+
+ bundle "binstubs rails rack"
+
+ expect(bundled_app("bin/rackup")).to exist
+ expect(bundled_app("bin/rails")).to exist
+ end
+
+ it "allows installing all binstubs" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ bundle! :binstubs, :all => true
+
+ expect(bundled_app("bin/rails")).to exist
+ expect(bundled_app("bin/rake")).to exist
+ end
+
+ it "displays an error when used without any gem" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs"
+ expect(exitstatus).to eq(1) if exitstatus
+ expect(out).to include("`bundle binstubs` needs at least one gem to run.")
+ end
+
+ it "displays an error when used with --all and gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack", :all => true
+ expect(last_command).to be_failure
+ expect(last_command.bundler_err).to include("Cannot specify --all with specific gems")
+ end
+
+ context "when generating bundle binstub outside bundler" do
+ it "should abort" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack"
+
+ File.open("bin/bundle", "wb") do |file|
+ file.print "OMG"
+ end
+
+ sys_exec "bin/rackup"
+
+ expect(last_command.stderr).to include("was not generated by Bundler")
+ end
+ end
+
+ context "the bundle binstub" do
+ before do
+ if system_bundler_version == :bundler
+ system_gems :bundler
+ elsif system_bundler_version
+ build_repo4 do
+ build_gem "bundler", system_bundler_version do |s|
+ s.executables = "bundle"
+ s.bindir = "exe"
+ s.write "exe/bundle", "puts %(system bundler #{system_bundler_version}\\n\#{ARGV.inspect})"
+ end
+ end
+ system_gems "bundler-#{system_bundler_version}", :gem_repo => gem_repo4
+ end
+ build_repo2 do
+ build_gem "prints_loaded_gems", "1.0" do |s|
+ s.executables = "print_loaded_gems"
+ s.bindir = "exe"
+ s.write "exe/print_loaded_gems", <<-R
+ specs = Gem.loaded_specs.values.reject {|s| Bundler.rubygems.spec_default_gem?(s) }
+ puts specs.map(&:full_name).sort.inspect
+ R
+ end
+ end
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ gem "prints_loaded_gems"
+ G
+ bundle! "binstubs bundler rack prints_loaded_gems"
+ end
+
+ # When environment has a same version of bundler as default gems.
+ # `system_gems "bundler-x.y.z"` will detect system binstub.
+ # We need to avoid it by virtual version of bundler.
+ let(:system_bundler_version) { Gem::Version.new(Bundler::VERSION).bump.to_s }
+
+ context "when system bundler was used" do
+ # Support master branch of bundler
+ if ENV["BUNDLER_SPEC_SUB_VERSION"]
+ let(:system_bundler_version) { Bundler::VERSION }
+ end
+ it "runs bundler" do
+ sys_exec! "#{bundled_app("bin/bundle")} install"
+ expect(out).to eq %(system bundler #{system_bundler_version}\n["install"])
+ end
+ end
+
+ context "when BUNDLER_VERSION is set" do
+ let(:system_bundler_version) { Bundler::VERSION }
+
+ it "runs the correct version of bundler" do
+ sys_exec "BUNDLER_VERSION='999.999.999' #{bundled_app("bin/bundle")} install"
+ expect(exitstatus).to eq(42) if exitstatus
+ expect(last_command.stderr).to include("Activating bundler (999.999.999) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`")
+ end
+ end
+
+ context "when a lockfile exists with a locked bundler version" do
+ let(:system_bundler_version) { Bundler::VERSION }
+
+ it "runs the correct version of bundler when the version is newer" do
+ lockfile lockfile.gsub(system_bundler_version, "999.999.999")
+ sys_exec "#{bundled_app("bin/bundle")} install"
+ expect(exitstatus).to eq(42) if exitstatus
+ expect(last_command.stderr).to include("Activating bundler (999.999.999) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`")
+ end
+
+ it "runs the correct version of bundler when the version is older" do
+ simulate_bundler_version "55"
+ lockfile lockfile.gsub(system_bundler_version, "44.0")
+ sys_exec "#{bundled_app("bin/bundle")} install"
+ expect(exitstatus).to eq(42) if exitstatus
+ expect(last_command.stderr).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
+
+ it "runs the correct version of bundler when the version is a pre-release" do
+ simulate_bundler_version "55"
+ lockfile lockfile.gsub(system_bundler_version, "2.12.0.a")
+ sys_exec "#{bundled_app("bin/bundle")} install"
+ expect(exitstatus).to eq(42) if exitstatus
+ expect(last_command.stderr).to include("Activating bundler (2.12.0.a) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '2.12.0.a'`")
+ end
+ end
+
+ 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
+ sys_exec! "#{bundled_app("bin/bundle")} update --bundler"
+ expect(last_command.stdout).to eq %(system bundler #{system_bundler_version}\n["update", "--bundler"])
+ end
+
+ it "calls through to the explicit bundler version" do
+ sys_exec "#{bundled_app("bin/bundle")} update --bundler=999.999.999"
+ expect(exitstatus).to eq(42) if exitstatus
+ expect(last_command.stderr).to include("Activating bundler (999.999.999) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`")
+ end
+ end
+
+ context "without a lockfile" do
+ it "falls back to the latest installed bundler" do
+ FileUtils.rm bundled_app("Gemfile.lock")
+ sys_exec! bundled_app("bin/bundle").to_s
+ expect(out).to eq "system bundler #{system_bundler_version}\n[]"
+ end
+ end
+
+ context "using another binstub" do
+ let(:system_bundler_version) { :bundler }
+ it "loads all gems" do
+ sys_exec! bundled_app("bin/print_loaded_gems").to_s
+ # RG < 2.0.14 didn't have a `Gem::Specification#default_gem?`
+ # This is dirty detection for old RG versions.
+ if File.dirname(Bundler.load.specs["bundler"][0].loaded_from) =~ %r{specifications/default}
+ expect(out).to eq %(["prints_loaded_gems-1.0", "rack-1.2"])
+ else
+ expect(out).to eq %(["bundler-#{Bundler::VERSION}", "prints_loaded_gems-1.0", "rack-1.2"])
+ end
+ end
+
+ context "when requesting a different bundler version" do
+ before { lockfile lockfile.gsub(Bundler::VERSION, "999.999.999") }
+
+ it "attempts to load that version", :ruby_repo do
+ sys_exec bundled_app("bin/rackup").to_s
+ expect(exitstatus).to eq(42) if exitstatus
+ expect(last_command.stderr).to include("Activating bundler (999.999.999) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`")
+ end
+ end
+ end
+ end
+
+ it "installs binstubs from git gems" do
+ FileUtils.mkdir_p(lib_path("foo/bin"))
+ FileUtils.touch(lib_path("foo/bin/foo"))
+ build_git "foo", "1.0", :path => lib_path("foo") do |s|
+ s.executables = %w[foo]
+ end
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo")}"
+ G
+
+ bundle "binstubs foo"
+
+ expect(bundled_app("bin/foo")).to exist
+ end
+
+ it "installs binstubs from path gems" do
+ FileUtils.mkdir_p(lib_path("foo/bin"))
+ FileUtils.touch(lib_path("foo/bin/foo"))
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.executables = %w[foo]
+ end
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ bundle "binstubs foo"
+
+ expect(bundled_app("bin/foo")).to exist
+ end
+
+ it "sets correct permissions for binstubs" do
+ with_umask(0o002) do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack"
+ binary = bundled_app("bin/rackup")
+ expect(File.stat(binary).mode.to_s(8)).to eq("100775")
+ end
+ end
+
+ context "when using --shebang" do
+ it "sets the specified shebang for the the binstub" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack --shebang jruby"
+
+ expect(File.open("bin/rackup").gets).to eq("#!/usr/bin/env jruby\n")
+ end
+ end
+ end
+
+ context "when the gem doesn't exist" do
+ it "displays an error with correct status" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ G
+
+ bundle "binstubs doesnt_exist"
+
+ expect(exitstatus).to eq(7) if exitstatus
+ expect(out).to include("Could not find gem 'doesnt_exist'.")
+ end
+ end
+
+ context "--path" do
+ it "sets the binstubs dir" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack --path exec"
+
+ expect(bundled_app("exec/rackup")).to exist
+ end
+
+ it "setting is saved for bundle install", :bundler => "< 2" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rails"
+ G
+
+ bundle! "binstubs rack", forgotten_command_line_options([:path, :bin] => "exec")
+ bundle! :install
+
+ expect(bundled_app("exec/rails")).to exist
+ end
+ end
+
+ context "with --standalone option" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "generates a standalone binstub" do
+ bundle! "binstubs rack --standalone"
+ expect(bundled_app("bin/rackup")).to exist
+ end
+
+ it "generates a binstub that does not depend on rubygems or bundler" do
+ bundle! "binstubs rack --standalone"
+ expect(File.read(bundled_app("bin/rackup"))).to_not include("Gem.bin_path")
+ end
+
+ context "when specified --path option" do
+ it "generates a standalone binstub at the given path" do
+ bundle! "binstubs rack --standalone --path foo"
+ expect(bundled_app("foo/rackup")).to exist
+ end
+ end
+ end
+
+ context "when the bin already exists" do
+ it "doesn't overwrite and warns" do
+ FileUtils.mkdir_p(bundled_app("bin"))
+ File.open(bundled_app("bin/rackup"), "wb") do |file|
+ file.print "OMG"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack"
+
+ expect(bundled_app("bin/rackup")).to exist
+ expect(File.read(bundled_app("bin/rackup"))).to eq("OMG")
+ expect(out).to include("Skipped rackup")
+ expect(out).to include("overwrite skipped stubs, use --force")
+ end
+
+ context "when using --force" do
+ it "overwrites the binstub" do
+ FileUtils.mkdir_p(bundled_app("bin"))
+ File.open(bundled_app("bin/rackup"), "wb") do |file|
+ file.print "OMG"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack --force"
+
+ expect(bundled_app("bin/rackup")).to exist
+ expect(File.read(bundled_app("bin/rackup"))).not_to eq("OMG")
+ end
+ end
+ end
+
+ context "when the gem has no bins" do
+ it "suggests child gems if they have bins" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack-obama"
+ G
+
+ bundle "binstubs rack-obama"
+ expect(out).to include("rack-obama has no executables")
+ expect(out).to include("rack has: rackup")
+ end
+
+ it "works if child gems don't have bins" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "actionpack"
+ G
+
+ bundle "binstubs actionpack"
+ expect(out).to include("no executables for the gem actionpack")
+ end
+
+ it "works if the gem has development dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "with_development_dependency"
+ G
+
+ bundle "binstubs with_development_dependency"
+ expect(out).to include("no executables for the gem with_development_dependency")
+ end
+ end
+
+ context "when BUNDLE_INSTALL is specified" do
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "config auto_install 1"
+ bundle "binstubs rack"
+ expect(out).to include("Installing rack 1.0.0")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "does nothing when already up to date" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "config auto_install 1"
+ bundle "binstubs rack", :env => { "BUNDLE_INSTALL" => 1 }
+ expect(out).not_to include("Installing rack 1.0.0")
+ end
+ end
+end
diff --git a/spec/bundler/commands/check_spec.rb b/spec/bundler/commands/check_spec.rb
new file mode 100644
index 0000000000..f2af446fbf
--- /dev/null
+++ b/spec/bundler/commands/check_spec.rb
@@ -0,0 +1,354 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle check" do
+ it "returns success when the Gemfile is satisfied" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ bundle :check
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "works with the --gemfile flag when not in the directory" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ Dir.chdir tmp
+ bundle "check --gemfile bundled_app/Gemfile"
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "creates a Gemfile.lock by default if one does not exist" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ FileUtils.rm("Gemfile.lock")
+
+ bundle "check"
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ it "does not create a Gemfile.lock if --dry-run was passed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ FileUtils.rm("Gemfile.lock")
+
+ bundle "check --dry-run"
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ end
+
+ it "prints a generic error if the missing gems are unresolvable" do
+ system_gems ["rails-2.3.2"]
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ bundle :check
+ expect(out).to include("Bundler can't satisfy your Gemfile's dependencies.")
+ end
+
+ it "prints a generic error if a Gemfile.lock does not exist and a toplevel dependency does not exist" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ bundle :check
+ expect(exitstatus).to be > 0 if exitstatus
+ expect(out).to include("Bundler can't satisfy your Gemfile's dependencies.")
+ end
+
+ it "prints a generic message if you changed your lockfile" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rails'
+ G
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rails_fail'
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ gem "rails_fail"
+ G
+
+ bundle :check
+ expect(out).to include("Bundler can't satisfy your Gemfile's dependencies.")
+ end
+
+ it "remembers --without option from install", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ group :foo do
+ gem "rack"
+ end
+ G
+
+ bundle! "install --without foo"
+ bundle! "check"
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "uses the without setting" do
+ bundle! "config without foo"
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ group :foo do
+ gem "rack"
+ end
+ G
+
+ bundle! "check"
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "ensures that gems are actually installed and not just cached" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :group => :foo
+ G
+
+ bundle :install, forgotten_command_line_options(:without => "foo")
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "check"
+ expect(out).to include("* rack (1.0.0)")
+ expect(exitstatus).to eq(1) if exitstatus
+ end
+
+ it "ignores missing gems restricted to other platforms" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ platforms :#{not_local_tag} do
+ gem "activesupport"
+ end
+ G
+
+ system_gems "rack-1.0.0", :path => :bundle_path
+
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ activesupport (2.3.5)
+ rack (1.0.0)
+
+ PLATFORMS
+ #{local}
+ #{not_local}
+
+ DEPENDENCIES
+ rack
+ activesupport
+ G
+
+ bundle :check
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "works with env conditionals" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ env :NOT_GOING_TO_BE_SET do
+ gem "activesupport"
+ end
+ G
+
+ system_gems "rack-1.0.0", :path => :bundle_path
+
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ activesupport (2.3.5)
+ rack (1.0.0)
+
+ PLATFORMS
+ #{local}
+ #{not_local}
+
+ DEPENDENCIES
+ rack
+ activesupport
+ G
+
+ bundle :check
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "outputs an error when the default Gemfile is not found" do
+ bundle :check
+ expect(exitstatus).to eq(10) if exitstatus
+ expect(out).to include("Could not locate Gemfile")
+ end
+
+ it "does not output fatal error message" do
+ bundle :check
+ expect(exitstatus).to eq(10) if exitstatus
+ expect(out).not_to include("Unfortunately, a fatal error has occurred. ")
+ end
+
+ it "should not crash when called multiple times on a new machine" do
+ gemfile <<-G
+ gem 'rails', '3.0.0.beta3'
+ gem 'paperclip', :git => 'git://github.com/thoughtbot/paperclip.git'
+ G
+
+ simulate_new_machine
+ bundle "check"
+ last_out = out
+ 3.times do
+ bundle :check
+ expect(out).to eq(last_out)
+ end
+ end
+
+ it "fails when there's no lock file and frozen is set" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "foo"
+ G
+
+ bundle! "install", forgotten_command_line_options(:deployment => true)
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ bundle :check
+ expect(last_command).to be_failure
+ end
+
+ context "--path", :bundler => "< 2" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ bundle "install --path vendor/bundle"
+
+ FileUtils.rm_rf(bundled_app(".bundle"))
+ end
+
+ it "returns success" do
+ bundle! "check --path vendor/bundle"
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "should write to .bundle/config", :bundler => "< 2" do
+ bundle "check --path vendor/bundle"
+ bundle! "check"
+ end
+ end
+
+ context "--path vendor/bundle after installing gems in the default directory" do
+ it "returns false" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ bundle "check --path vendor/bundle"
+ expect(exitstatus).to eq(1) if exitstatus
+ expect(out).to match(/The following gems are missing/)
+ end
+ end
+
+ describe "when locked" do
+ before :each do
+ system_gems "rack-1.0.0"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0"
+ G
+ end
+
+ it "returns success when the Gemfile is satisfied" do
+ bundle :install
+ bundle :check
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "shows what is missing with the current Gemfile if it is not satisfied" do
+ simulate_new_machine
+ bundle :check
+ expect(out).to match(/The following gems are missing/)
+ expect(out).to include("* rack (1.0")
+ end
+ end
+
+ describe "BUNDLED WITH" do
+ def lock_with(bundler_version = nil)
+ lock = <<-L
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+ L
+
+ if bundler_version
+ lock += "\n BUNDLED WITH\n #{bundler_version}\n"
+ end
+
+ lock
+ end
+
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ context "is not present" do
+ it "does not change the lock" do
+ lockfile lock_with(nil)
+ bundle :check
+ lockfile_should_be lock_with(nil)
+ end
+ end
+
+ context "is newer" do
+ it "does not change the lock but warns" do
+ lockfile lock_with(Bundler::VERSION.succ)
+ bundle! :check
+ expect(last_command.bundler_err).to include("the running version of Bundler (#{Bundler::VERSION}) is older than the version that created the lockfile (#{Bundler::VERSION.succ})")
+ lockfile_should_be lock_with(Bundler::VERSION.succ)
+ end
+ end
+
+ context "is older" do
+ it "does not change the lock" do
+ lockfile lock_with("1.10.1")
+ bundle :check
+ lockfile_should_be lock_with("1.10.1")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/commands/clean_spec.rb b/spec/bundler/commands/clean_spec.rb
new file mode 100644
index 0000000000..37cbeeb4e7
--- /dev/null
+++ b/spec/bundler/commands/clean_spec.rb
@@ -0,0 +1,771 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle clean" do
+ def should_have_gems(*gems)
+ gems.each do |g|
+ expect(vendored_gems("gems/#{g}")).to exist
+ expect(vendored_gems("specifications/#{g}.gemspec")).to exist
+ expect(vendored_gems("cache/#{g}.gem")).to exist
+ end
+ end
+
+ def should_not_have_gems(*gems)
+ gems.each do |g|
+ expect(vendored_gems("gems/#{g}")).not_to exist
+ expect(vendored_gems("specifications/#{g}.gemspec")).not_to exist
+ expect(vendored_gems("cache/#{g}.gem")).not_to exist
+ end
+ end
+
+ it "removes unused gems that are different" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "foo"
+ G
+
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => false)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ G
+ bundle! "install"
+
+ bundle! :clean
+
+ expect(out).to include("Removing foo (1.0)")
+
+ should_have_gems "thin-1.0", "rack-1.0.0"
+ should_not_have_gems "foo-1.0"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "removes old version of gem if unused" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "0.9.1"
+ gem "foo"
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => false)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ gem "foo"
+ G
+ bundle "install"
+
+ bundle :clean
+
+ expect(out).to include("Removing rack (0.9.1)")
+
+ should_have_gems "foo-1.0", "rack-1.0.0"
+ should_not_have_gems "rack-0.9.1"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "removes new version of gem if unused" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ gem "foo"
+ G
+
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => false)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "0.9.1"
+ gem "foo"
+ G
+ bundle! "update rack"
+
+ bundle! :clean
+
+ expect(out).to include("Removing rack (1.0.0)")
+
+ should_have_gems "foo-1.0", "rack-0.9.1"
+ should_not_have_gems "rack-1.0.0"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "removes gems in bundle without groups" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+
+ group :test_group do
+ gem "rack", "1.0.0"
+ end
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+ bundle "install", forgotten_command_line_options(:without => "test_group")
+ bundle :clean
+
+ expect(out).to include("Removing rack (1.0.0)")
+
+ should_have_gems "foo-1.0"
+ should_not_have_gems "rack-1.0.0"
+
+ expect(vendored_gems("bin/rackup")).to_not exist
+ end
+
+ it "does not remove cached git dir if it's being used" do
+ build_git "foo"
+ revision = revision_for(lib_path("foo-1.0"))
+ git_path = lib_path("foo-1.0")
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ git "#{git_path}", :ref => "#{revision}" do
+ gem "foo"
+ end
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ bundle :clean
+
+ digest = Digest(:SHA1).hexdigest(git_path.to_s)
+ cache_path = Bundler::VERSION.start_with?("1.") ? vendored_gems("cache/bundler/git/foo-1.0-#{digest}") : home(".bundle/cache/git/foo-1.0-#{digest}")
+ expect(cache_path).to exist
+ end
+
+ it "removes unused git gems" do
+ build_git "foo", :path => lib_path("foo")
+ git_path = lib_path("foo")
+ revision = revision_for(git_path)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ git "#{git_path}", :ref => "#{revision}" do
+ gem "foo"
+ end
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ G
+ bundle "install"
+
+ bundle :clean
+
+ expect(out).to include("Removing foo (#{revision[0..11]})")
+
+ expect(vendored_gems("gems/rack-1.0.0")).to exist
+ expect(vendored_gems("bundler/gems/foo-#{revision[0..11]}")).not_to exist
+ digest = Digest(:SHA1).hexdigest(git_path.to_s)
+ expect(vendored_gems("cache/bundler/git/foo-#{digest}")).not_to exist
+
+ expect(vendored_gems("specifications/rack-1.0.0.gemspec")).to exist
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "removes old git gems" do
+ build_git "foo-bar", :path => lib_path("foo-bar")
+ revision = revision_for(lib_path("foo-bar"))
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ git "#{lib_path("foo-bar")}" do
+ gem "foo-bar"
+ end
+ G
+
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ update_git "foo", :path => lib_path("foo-bar")
+ revision2 = revision_for(lib_path("foo-bar"))
+
+ bundle! "update", :all => bundle_update_requires_all?
+ bundle! :clean
+
+ expect(out).to include("Removing foo-bar (#{revision[0..11]})")
+
+ expect(vendored_gems("gems/rack-1.0.0")).to exist
+ expect(vendored_gems("bundler/gems/foo-bar-#{revision[0..11]}")).not_to exist
+ expect(vendored_gems("bundler/gems/foo-bar-#{revision2[0..11]}")).to exist
+
+ expect(vendored_gems("specifications/rack-1.0.0.gemspec")).to exist
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "does not remove nested gems in a git repo" do
+ build_lib "activesupport", "3.0", :path => lib_path("rails/activesupport")
+ build_git "rails", "3.0", :path => lib_path("rails") do |s|
+ s.add_dependency "activesupport", "= 3.0"
+ end
+ revision = revision_for(lib_path("rails"))
+
+ gemfile <<-G
+ gem "activesupport", :git => "#{lib_path("rails")}", :ref => '#{revision}'
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+ bundle :clean
+ expect(out).to include("")
+
+ expect(vendored_gems("bundler/gems/rails-#{revision[0..11]}")).to exist
+ end
+
+ it "does not remove git sources that are in without groups" do
+ build_git "foo", :path => lib_path("foo")
+ git_path = lib_path("foo")
+ revision = revision_for(git_path)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ group :test do
+ git "#{git_path}", :ref => "#{revision}" do
+ gem "foo"
+ end
+ end
+ G
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle", :without => "test")
+
+ bundle :clean
+
+ expect(out).to include("")
+ expect(vendored_gems("bundler/gems/foo-#{revision[0..11]}")).to exist
+ digest = Digest(:SHA1).hexdigest(git_path.to_s)
+ expect(vendored_gems("cache/bundler/git/foo-#{digest}")).to_not exist
+ end
+
+ it "does not blow up when using without groups" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+
+ group :development do
+ gem "foo"
+ end
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle", :without => "development")
+
+ bundle :clean
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "displays an error when used without --path" do
+ bundle! "config path.system true"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ G
+
+ bundle :clean
+
+ expect(exitstatus).to eq(15) if exitstatus
+ expect(out).to include("--force")
+ end
+
+ # handling bundle clean upgrade path from the pre's
+ it "removes .gem/.gemspec file even if there's no corresponding gem dir" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "foo"
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+ G
+ bundle "install"
+
+ FileUtils.rm(vendored_gems("bin/rackup"))
+ FileUtils.rm_rf(vendored_gems("gems/thin-1.0"))
+ FileUtils.rm_rf(vendored_gems("gems/rack-1.0.0"))
+
+ bundle :clean
+
+ should_not_have_gems "thin-1.0", "rack-1.0"
+ should_have_gems "foo-1.0"
+
+ expect(vendored_gems("bin/rackup")).not_to exist
+ end
+
+ it "does not call clean automatically when using system gems" do
+ bundle! "config path.system true"
+
+ bundle! :config
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "rack"
+ G
+
+ bundle! "info thin"
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ sys_exec! "gem list"
+ expect(out).to include("rack (1.0.0)").and include("thin (1.0)")
+ end
+
+ it "--clean should override the bundle setting on install", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "rack"
+ G
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => true)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+ bundle "install"
+
+ should_have_gems "rack-1.0.0"
+ should_not_have_gems "thin-1.0"
+ end
+
+ it "--clean should override the bundle setting on update", :bundler => "< 2" do
+ build_repo2
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "foo"
+ G
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => true)
+
+ update_repo2 do
+ build_gem "foo", "1.0.1"
+ end
+
+ bundle! "update", :all => bundle_update_requires_all?
+
+ should_have_gems "foo-1.0.1"
+ should_not_have_gems "foo-1.0"
+ end
+
+ it "automatically cleans when path has not been set", :bundler => "2" do
+ build_repo2
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+
+ gem "foo"
+ G
+
+ update_repo2 do
+ build_gem "foo", "1.0.1"
+ end
+
+ bundle! "update", :all => true
+
+ files = Pathname.glob(bundled_app(".bundle", Bundler.ruby_scope, "*", "*"))
+ files.map! {|f| f.to_s.sub(bundled_app(".bundle", Bundler.ruby_scope).to_s, "") }
+ expect(files.sort).to eq %w[
+ /cache/foo-1.0.1.gem
+ /gems/foo-1.0.1
+ /specifications/foo-1.0.1.gemspec
+ ]
+ end
+
+ it "does not clean automatically on --path" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "rack"
+ G
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+ bundle "install"
+
+ should_have_gems "rack-1.0.0", "thin-1.0"
+ end
+
+ it "does not clean on bundle update with --path" do
+ build_repo2
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "foo"
+ G
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ update_repo2 do
+ build_gem "foo", "1.0.1"
+ end
+
+ bundle! :update, :all => bundle_update_requires_all?
+ should_have_gems "foo-1.0", "foo-1.0.1"
+ end
+
+ it "does not clean on bundle update when using --system" do
+ bundle! "config path.system true"
+
+ build_repo2
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "foo"
+ G
+ bundle! "install"
+
+ update_repo2 do
+ build_gem "foo", "1.0.1"
+ end
+ bundle! :update, :all => bundle_update_requires_all?
+
+ sys_exec! "gem list"
+ expect(out).to include("foo (1.0.1, 1.0)")
+ end
+
+ it "cleans system gems when --force is used" do
+ bundle! "config path.system true"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+ gem "rack"
+ G
+ bundle :install
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+ bundle :install
+ bundle "clean --force"
+
+ expect(out).to include("Removing foo (1.0)")
+ sys_exec "gem list"
+ expect(out).not_to include("foo (1.0)")
+ expect(out).to include("rack (1.0.0)")
+ end
+
+ describe "when missing permissions" do
+ before { ENV["BUNDLE_PATH__SYSTEM"] = "true" }
+ let(:system_cache_path) { system_gem_path("cache") }
+ after do
+ FileUtils.chmod(0o755, system_cache_path)
+ end
+ it "returns a helpful error message" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+ gem "rack"
+ G
+ bundle :install
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+ bundle :install
+
+ FileUtils.chmod(0o500, system_cache_path)
+
+ bundle :clean, :force => true
+
+ expect(out).to include(system_gem_path.to_s)
+ expect(out).to include("grant write permissions")
+
+ sys_exec "gem list"
+ expect(out).to include("foo (1.0)")
+ expect(out).to include("rack (1.0.0)")
+ end
+ end
+
+ it "cleans git gems with a 7 length git revision" do
+ build_git "foo"
+ revision = revision_for(lib_path("foo-1.0"))
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ # mimic 7 length git revisions in Gemfile.lock
+ gemfile_lock = File.read(bundled_app("Gemfile.lock")).split("\n")
+ gemfile_lock.each_with_index do |line, index|
+ gemfile_lock[index] = line[0..(11 + 7)] if line.include?(" revision:")
+ end
+ File.open(bundled_app("Gemfile.lock"), "w") do |file|
+ file.print gemfile_lock.join("\n")
+ end
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ bundle :clean
+
+ expect(out).not_to include("Removing foo (1.0 #{revision[0..6]})")
+
+ expect(vendored_gems("bundler/gems/foo-1.0-#{revision[0..6]}")).to exist
+ end
+
+ it "when using --force on system gems, it doesn't remove binaries" do
+ bundle! "config path.system true"
+
+ build_repo2
+ update_repo2 do
+ build_gem "bindir" do |s|
+ s.bindir = "exe"
+ s.executables = "foo"
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "bindir"
+ G
+ bundle :install
+
+ bundle "clean --force"
+
+ sys_exec "foo"
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(out).to eq("1.0")
+ end
+
+ it "doesn't blow up on path gems without a .gempsec" do
+ relative_path = "vendor/private_gems/bar-1.0"
+ absolute_path = bundled_app(relative_path)
+ FileUtils.mkdir_p("#{absolute_path}/lib/bar")
+ File.open("#{absolute_path}/lib/bar/bar.rb", "wb") do |file|
+ file.puts "module Bar; end"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+ gem "bar", "1.0", :path => "#{relative_path}"
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+ bundle! :clean
+ end
+
+ it "doesn't remove gems in dry-run mode with path set" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "foo"
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => false)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ G
+
+ bundle :install
+
+ bundle "clean --dry-run"
+
+ expect(out).not_to include("Removing foo (1.0)")
+ expect(out).to include("Would have removed foo (1.0)")
+
+ should_have_gems "thin-1.0", "rack-1.0.0", "foo-1.0"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "doesn't remove gems in dry-run mode with no path set" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "foo"
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => false)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ G
+
+ bundle :install
+
+ bundle "configuration --delete path"
+
+ bundle "clean --dry-run"
+
+ expect(out).not_to include("Removing foo (1.0)")
+ expect(out).to include("Would have removed foo (1.0)")
+
+ should_have_gems "thin-1.0", "rack-1.0.0", "foo-1.0"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "doesn't store dry run as a config setting" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "foo"
+ G
+
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => false)
+ bundle "config dry_run false"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ G
+
+ bundle :install
+
+ bundle "clean"
+
+ expect(out).to include("Removing foo (1.0)")
+ expect(out).not_to include("Would have removed foo (1.0)")
+
+ should_have_gems "thin-1.0", "rack-1.0.0"
+ should_not_have_gems "foo-1.0"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "foo"
+ G
+
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle", :clean => false)
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "weakling"
+ G
+
+ bundle! "config auto_install 1"
+ bundle! :clean
+ expect(out).to include("Installing weakling 0.0.3")
+ should_have_gems "thin-1.0", "rack-1.0.0", "weakling-0.0.3"
+ should_not_have_gems "foo-1.0"
+ end
+
+ it "doesn't remove extensions artifacts from bundled git gems after clean", :ruby_repo, :rubygems => "2.2" do
+ build_git "very_simple_git_binary", &:add_c_extension
+
+ revision = revision_for(lib_path("very_simple_git_binary-1.0"))
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "very_simple_git_binary", :git => "#{lib_path("very_simple_git_binary-1.0")}", :ref => "#{revision}"
+ G
+
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle")
+ expect(vendored_gems("bundler/gems/extensions")).to exist
+ expect(vendored_gems("bundler/gems/very_simple_git_binary-1.0-#{revision[0..11]}")).to exist
+
+ bundle! :clean
+ expect(out).to eq("")
+
+ expect(vendored_gems("bundler/gems/extensions")).to exist
+ expect(vendored_gems("bundler/gems/very_simple_git_binary-1.0-#{revision[0..11]}")).to exist
+ end
+
+ it "removes extension directories", :ruby_repo, :rubygems => "2.2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "very_simple_binary"
+ gem "simple_binary"
+ G
+
+ bundle! "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ very_simple_binary_extensions_dir =
+ Pathname.glob("#{vendored_gems}/extensions/*/*/very_simple_binary-1.0").first
+
+ simple_binary_extensions_dir =
+ Pathname.glob("#{vendored_gems}/extensions/*/*/simple_binary-1.0").first
+
+ expect(very_simple_binary_extensions_dir).to exist
+ expect(simple_binary_extensions_dir).to exist
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "simple_binary"
+ G
+
+ bundle! "install"
+ bundle! :clean
+ expect(out).to eq("Removing very_simple_binary (1.0)")
+
+ expect(very_simple_binary_extensions_dir).not_to exist
+ expect(simple_binary_extensions_dir).to exist
+ end
+end
diff --git a/spec/bundler/commands/config_spec.rb b/spec/bundler/commands/config_spec.rb
new file mode 100644
index 0000000000..9e49357465
--- /dev/null
+++ b/spec/bundler/commands/config_spec.rb
@@ -0,0 +1,384 @@
+# frozen_string_literal: true
+
+RSpec.describe ".bundle/config" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0.0"
+ G
+ end
+
+ describe "config" do
+ before { bundle "config foo bar" }
+
+ it "prints a detailed report of local and user configuration" do
+ bundle "config"
+
+ expect(out).to include("Settings are listed in order of priority. The top value will be used")
+ expect(out).to include("foo\nSet for the current user")
+ expect(out).to include(": \"bar\"")
+ end
+
+ context "given --parseable flag" do
+ it "prints a minimal report of local and user configuration" do
+ bundle "config --parseable"
+ expect(out).to include("foo=bar")
+ end
+
+ context "with global config" do
+ it "prints config assigned to local scope" do
+ bundle "config --local foo bar2"
+ bundle "config --parseable"
+ expect(out).to include("foo=bar2")
+ end
+ end
+
+ context "with env overwrite" do
+ it "prints config with env" do
+ bundle "config --parseable", :env => { "BUNDLE_FOO" => "bar3" }
+ expect(out).to include("foo=bar3")
+ end
+ end
+ end
+ end
+
+ describe "BUNDLE_APP_CONFIG" do
+ it "can be moved with an environment variable" do
+ ENV["BUNDLE_APP_CONFIG"] = tmp("foo/bar").to_s
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ expect(bundled_app(".bundle")).not_to exist
+ expect(tmp("foo/bar/config")).to exist
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "can provide a relative path with the environment variable" do
+ FileUtils.mkdir_p bundled_app("omg")
+ Dir.chdir bundled_app("omg")
+
+ ENV["BUNDLE_APP_CONFIG"] = "../foo"
+ bundle "install", forgotten_command_line_options(:path => "vendor/bundle")
+
+ expect(bundled_app(".bundle")).not_to exist
+ expect(bundled_app("../foo/config")).to exist
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ describe "global" do
+ before(:each) { bundle :install }
+
+ it "is the default" do
+ bundle "config foo global"
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("global")
+ end
+
+ it "can also be set explicitly" do
+ bundle! "config --global foo global"
+ run! "puts Bundler.settings[:foo]"
+ expect(out).to eq("global")
+ end
+
+ it "has lower precedence than local" do
+ bundle "config --local foo local"
+
+ bundle "config --global foo global"
+ expect(out).to match(/Your application has set foo to "local"/)
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("local")
+ end
+
+ it "has lower precedence than env" do
+ begin
+ ENV["BUNDLE_FOO"] = "env"
+
+ bundle "config --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
+ end
+
+ it "can be deleted" do
+ bundle "config --global foo global"
+ bundle "config --delete foo"
+
+ run "puts Bundler.settings[:foo] == nil"
+ expect(out).to eq("true")
+ end
+
+ it "warns when overriding" do
+ bundle "config --global foo previous"
+ bundle "config --global foo global"
+ expect(out).to match(/You are replacing the current global value of foo/)
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("global")
+ end
+
+ it "does not warn when using the same value twice" do
+ bundle "config --global foo value"
+ bundle "config --global foo value"
+ expect(out).not_to match(/You are replacing the current global value of foo/)
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("value")
+ end
+
+ it "expands the path at time of setting" do
+ bundle "config --global local.foo .."
+ run "puts Bundler.settings['local.foo']"
+ expect(out).to eq(File.expand_path(Dir.pwd + "/.."))
+ end
+
+ it "saves with parseable option" do
+ bundle "config --global --parseable foo value"
+ expect(out).to eq("foo=value")
+ run "puts Bundler.settings['foo']"
+ expect(out).to eq("value")
+ end
+
+ context "when replacing a current value with the parseable flag" do
+ before { bundle "config --global foo value" }
+ it "prints the current value in a parseable format" do
+ bundle "config --global --parseable foo value2"
+ expect(out).to eq "foo=value2"
+ run "puts Bundler.settings['foo']"
+ expect(out).to eq("value2")
+ end
+ end
+ end
+
+ describe "local" do
+ before(:each) { bundle :install }
+
+ it "can also be set explicitly" do
+ bundle "config --local foo local"
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("local")
+ end
+
+ it "has higher precedence than env" do
+ begin
+ ENV["BUNDLE_FOO"] = "env"
+ bundle "config --local foo local"
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("local")
+ ensure
+ ENV.delete("BUNDLE_FOO")
+ end
+ end
+
+ it "can be deleted" do
+ bundle "config --local foo local"
+ bundle "config --delete foo"
+
+ run "puts Bundler.settings[:foo] == nil"
+ expect(out).to eq("true")
+ end
+
+ it "warns when overriding" do
+ bundle "config --local foo previous"
+ bundle "config --local foo local"
+ expect(out).to match(/You are replacing the current local value of foo/)
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("local")
+ end
+
+ it "expands the path at time of setting" do
+ bundle "config --local local.foo .."
+ run "puts Bundler.settings['local.foo']"
+ expect(out).to eq(File.expand_path(Dir.pwd + "/.."))
+ end
+
+ it "can be deleted with parseable option" do
+ bundle "config --local foo value"
+ bundle "config --delete --parseable foo"
+ expect(out).to eq ""
+ run "puts Bundler.settings['foo'] == nil"
+ expect(out).to eq("true")
+ end
+ end
+
+ describe "env" do
+ before(:each) { bundle :install }
+
+ it "can set boolean properties via the environment" do
+ ENV["BUNDLE_FROZEN"] = "true"
+
+ run "if Bundler.settings[:frozen]; puts 'true' else puts 'false' end"
+ expect(out).to eq("true")
+ end
+
+ it "can set negative boolean properties via the environment" do
+ run "if Bundler.settings[:frozen]; puts 'true' else puts 'false' end"
+ expect(out).to eq("false")
+
+ ENV["BUNDLE_FROZEN"] = "false"
+
+ run "if Bundler.settings[:frozen]; puts 'true' else puts 'false' end"
+ expect(out).to eq("false")
+
+ ENV["BUNDLE_FROZEN"] = "0"
+
+ run "if Bundler.settings[:frozen]; puts 'true' else puts 'false' end"
+ expect(out).to eq("false")
+
+ ENV["BUNDLE_FROZEN"] = ""
+
+ run "if Bundler.settings[:frozen]; puts 'true' else puts 'false' end"
+ expect(out).to eq("false")
+ end
+
+ it "can set properties with periods via the environment" do
+ ENV["BUNDLE_FOO__BAR"] = "baz"
+
+ run "puts Bundler.settings['foo.bar']"
+ expect(out).to eq("baz")
+ end
+ end
+
+ describe "parseable option" do
+ it "prints an empty string" do
+ bundle "config foo --parseable"
+
+ expect(out).to eq ""
+ end
+
+ it "only prints the value of the config" do
+ bundle "config foo local"
+ bundle "config foo --parseable"
+
+ expect(out).to eq "foo=local"
+ end
+
+ it "can print global config" do
+ bundle "config --global bar value"
+ bundle "config bar --parseable"
+
+ expect(out).to eq "bar=value"
+ end
+
+ it "prefers local config over global" do
+ bundle "config --local bar value2"
+ bundle "config --global bar value"
+ bundle "config bar --parseable"
+
+ expect(out).to eq "bar=value2"
+ end
+ end
+
+ describe "gem mirrors" do
+ before(:each) { bundle :install }
+
+ it "configures mirrors using keys with `mirror.`" do
+ bundle "config --local mirror.http://gems.example.org http://gem-mirror.example.org"
+ run(<<-E)
+Bundler.settings.gem_mirrors.each do |k, v|
+ puts "\#{k} => \#{v}"
+end
+E
+ expect(out).to eq("http://gems.example.org/ => http://gem-mirror.example.org/")
+ end
+ end
+
+ describe "quoting" do
+ before(:each) { gemfile "# no gems" }
+ let(:long_string) do
+ "--with-xml2-include=/usr/pkg/include/libxml2 --with-xml2-lib=/usr/pkg/lib " \
+ "--with-xslt-dir=/usr/pkg"
+ end
+
+ it "saves quotes" do
+ bundle "config foo something\\'"
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("something'")
+ end
+
+ it "doesn't return quotes around values", :ruby => "1.9" do
+ bundle "config foo '1'"
+ run "puts Bundler.settings.send(:global_config_file).read"
+ expect(out).to include('"1"')
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("1")
+ end
+
+ it "doesn't duplicate quotes around values", :if => (RUBY_VERSION >= "2.1") do
+ bundled_app(".bundle").mkpath
+ File.open(bundled_app(".bundle/config"), "w") do |f|
+ f.write 'BUNDLE_FOO: "$BUILD_DIR"'
+ end
+
+ bundle "config bar baz"
+ run "puts Bundler.settings.send(:local_config_file).read"
+
+ # Starting in Ruby 2.1, YAML automatically adds double quotes
+ # around some values, including $ and newlines.
+ expect(out).to include('BUNDLE_FOO: "$BUILD_DIR"')
+ end
+
+ it "doesn't duplicate quotes around long wrapped values" do
+ bundle "config foo #{long_string}"
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq(long_string)
+
+ bundle "config bar baz"
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq(long_string)
+ end
+ end
+
+ describe "very long lines" do
+ before(:each) { bundle :install }
+
+ let(:long_string) do
+ "--with-xml2-include=/usr/pkg/include/libxml2 --with-xml2-lib=/usr/pkg/lib " \
+ "--with-xslt-dir=/usr/pkg"
+ end
+
+ let(:long_string_without_special_characters) do
+ "here is quite a long string that will wrap to a second line but will not be " \
+ "surrounded by quotes"
+ end
+
+ it "doesn't wrap values" do
+ bundle "config foo #{long_string}"
+ run "puts Bundler.settings[:foo]"
+ expect(out).to match(long_string)
+ end
+
+ it "can read wrapped unquoted values" do
+ bundle "config foo #{long_string_without_special_characters}"
+ run "puts Bundler.settings[:foo]"
+ expect(out).to match(long_string_without_special_characters)
+ end
+ end
+end
+
+RSpec.describe "setting gemfile via config" do
+ context "when only the non-default Gemfile exists" do
+ it "persists the gemfile location to .bundle/config" do
+ File.open(bundled_app("NotGemfile"), "w") do |f|
+ f.write <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+ end
+
+ bundle "config --local gemfile #{bundled_app("NotGemfile")}"
+ expect(File.exist?(".bundle/config")).to eq(true)
+
+ bundle "config"
+ expect(out).to include("NotGemfile")
+ end
+ end
+end
diff --git a/spec/bundler/commands/console_spec.rb b/spec/bundler/commands/console_spec.rb
new file mode 100644
index 0000000000..9bf66e8f5b
--- /dev/null
+++ b/spec/bundler/commands/console_spec.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle console", :bundler => "< 2" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+ G
+ end
+
+ it "starts IRB with the default group loaded" do
+ bundle "console" do |input, _, _|
+ input.puts("puts RACK")
+ input.puts("exit")
+ end
+ expect(out).to include("0.9.1")
+ end
+
+ it "uses IRB as default console" do
+ bundle "console" do |input, _, _|
+ input.puts("__method__")
+ input.puts("exit")
+ end
+ expect(out).to include(":irb_binding")
+ end
+
+ it "starts another REPL if configured as such" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "pry"
+ G
+ bundle "config console pry"
+
+ bundle "console" do |input, _, _|
+ input.puts("__method__")
+ input.puts("exit")
+ end
+ expect(out).to include(":__pry__")
+ end
+
+ it "falls back to IRB if the other REPL isn't available" do
+ bundle "config console pry"
+ # make sure pry isn't there
+
+ bundle "console" do |input, _, _|
+ input.puts("__method__")
+ input.puts("exit")
+ end
+ expect(out).to include(":irb_binding")
+ end
+
+ it "doesn't load any other groups" do
+ bundle "console" do |input, _, _|
+ input.puts("puts ACTIVESUPPORT")
+ input.puts("exit")
+ end
+ expect(out).to include("NameError")
+ end
+
+ describe "when given a group" do
+ it "loads the given group" do
+ bundle "console test" do |input, _, _|
+ input.puts("puts ACTIVESUPPORT")
+ input.puts("exit")
+ end
+ expect(out).to include("2.3.5")
+ end
+
+ it "loads the default group" do
+ bundle "console test" do |input, _, _|
+ input.puts("puts RACK")
+ input.puts("exit")
+ end
+ expect(out).to include("0.9.1")
+ end
+
+ it "doesn't load other groups" do
+ bundle "console test" do |input, _, _|
+ input.puts("puts RACK_MIDDLEWARE")
+ input.puts("exit")
+ end
+ expect(out).to include("NameError")
+ end
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle :console do |input, _, _|
+ input.puts("puts 'hello'")
+ input.puts("exit")
+ end
+ expect(out).to include("Installing foo 1.0")
+ expect(out).to include("hello")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+end
diff --git a/spec/bundler/commands/doctor_spec.rb b/spec/bundler/commands/doctor_spec.rb
new file mode 100644
index 0000000000..5260e6cb36
--- /dev/null
+++ b/spec/bundler/commands/doctor_spec.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+
+require "find"
+require "stringio"
+require "bundler/cli"
+require "bundler/cli/doctor"
+
+RSpec.describe "bundle doctor" do
+ before(:each) do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ @stdout = StringIO.new
+
+ [:error, :warn].each do |method|
+ allow(Bundler.ui).to receive(method).and_wrap_original do |m, message|
+ m.call message
+ @stdout.puts message
+ end
+ end
+ end
+
+ context "when all files in home are readable/writable" do
+ before(:each) do
+ stat = double("stat")
+ unwritable_file = double("file")
+ allow(Find).to receive(:find).with(Bundler.home.to_s) { [unwritable_file] }
+ allow(File).to receive(:stat).with(unwritable_file) { stat }
+ allow(stat).to receive(:uid) { Process.uid }
+ allow(File).to receive(:writable?).with(unwritable_file) { true }
+ allow(File).to receive(:readable?).with(unwritable_file) { true }
+ end
+
+ it "exits with no message if the installed gem has no C extensions" do
+ expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ expect(@stdout.string).to be_empty
+ end
+
+ it "exits with no message if the installed gem's C extension dylib breakage is fine" do
+ doctor = Bundler::CLI::Doctor.new({})
+ expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"]
+ expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/lib/libSystem.dylib"]
+ allow(File).to receive(:exist?).and_call_original
+ allow(File).to receive(:exist?).with("/usr/lib/libSystem.dylib").and_return(true)
+ expect { doctor.run }.not_to(raise_error, @stdout.string)
+ expect(@stdout.string).to be_empty
+ end
+
+ it "exits with a message if one of the linked libraries is missing" do
+ doctor = Bundler::CLI::Doctor.new({})
+ expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"]
+ expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib"]
+ allow(File).to receive(:exist?).and_call_original
+ allow(File).to receive(:exist?).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_return(false)
+ expect { doctor.run }.to raise_error(Bundler::ProductionError, strip_whitespace(<<-E).strip), @stdout.string
+ The following gems are missing OS dependencies:
+ * bundler: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib
+ * rack: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib
+ E
+ end
+ end
+
+ context "when home contains files that are not readable/writable" do
+ before(:each) do
+ @stat = double("stat")
+ @unwritable_file = double("file")
+ allow(Find).to receive(:find).with(Bundler.home.to_s) { [@unwritable_file] }
+ allow(File).to receive(:stat).with(@unwritable_file) { @stat }
+ end
+
+ it "exits with an error if home contains files that are not readable/writable" do
+ allow(@stat).to receive(:uid) { Process.uid }
+ allow(File).to receive(:writable?).with(@unwritable_file) { false }
+ allow(File).to receive(:readable?).with(@unwritable_file) { false }
+ expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ expect(@stdout.string).to include(
+ "Files exist in the Bundler home that are not readable/writable by the current user. These files are:\n - #{@unwritable_file}"
+ )
+ expect(@stdout.string).not_to include("No issues")
+ end
+
+ context "when home contains files that are not owned by the current process" do
+ before(:each) do
+ allow(@stat).to receive(:uid) { 0o0000 }
+ end
+
+ it "exits with an error if home contains files that are not readable/writable and are not owned by the current user" do
+ allow(File).to receive(:writable?).with(@unwritable_file) { false }
+ allow(File).to receive(:readable?).with(@unwritable_file) { false }
+ expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ expect(@stdout.string).to include(
+ "Files exist in the Bundler home that are owned by another user, and are not readable/writable. These files are:\n - #{@unwritable_file}"
+ )
+ expect(@stdout.string).not_to include("No issues")
+ end
+
+ it "exits with a warning if home contains files that are read/write but not owned by current user" do
+ allow(File).to receive(:writable?).with(@unwritable_file) { true }
+ allow(File).to receive(:readable?).with(@unwritable_file) { true }
+ expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ expect(@stdout.string).to include(
+ "Files exist in the Bundler home that are owned by another user, but are still readable/writable. These files are:\n - #{@unwritable_file}"
+ )
+ expect(@stdout.string).not_to include("No issues")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb
new file mode 100644
index 0000000000..6835305d55
--- /dev/null
+++ b/spec/bundler/commands/exec_spec.rb
@@ -0,0 +1,858 @@
+# frozen_string_literal: true
+
+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 => :bundle_path)
+ end
+
+ it "works with --gemfile flag" do
+ create_file "CustomGemfile", <<-G
+ gem "rack", "1.0.0"
+ G
+
+ bundle "exec --gemfile CustomGemfile rackup"
+ expect(out).to eq("1.0.0")
+ end
+
+ it "activates the correct gem" do
+ gemfile <<-G
+ gem "rack", "0.9.1"
+ G
+
+ bundle "exec rackup"
+ expect(out).to eq("0.9.1")
+ end
+
+ it "works when the bins are in ~/.bundle" do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ bundle "exec rackup"
+ expect(out).to eq("1.0.0")
+ end
+
+ it "works when running from a random directory", :ruby_repo do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ bundle "exec 'cd #{tmp("gems")} && rackup'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+
+ expect(out).to include("1.0.0")
+ end
+
+ it "works when exec'ing something else" do
+ install_gemfile 'gem "rack"'
+ bundle "exec echo exec"
+ expect(out).to eq("exec")
+ end
+
+ it "works when exec'ing to ruby" do
+ install_gemfile 'gem "rack"'
+ bundle "exec ruby -e 'puts %{hi}'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to eq("hi")
+ end
+
+ it "accepts --verbose" do
+ install_gemfile 'gem "rack"'
+ bundle "exec --verbose echo foobar"
+ expect(out).to eq("foobar")
+ end
+
+ it "passes --verbose to command if it is given after the command" do
+ install_gemfile 'gem "rack"'
+ bundle "exec echo --verbose"
+ expect(out).to eq("--verbose")
+ end
+
+ it "handles --keep-file-descriptors" do
+ require "tempfile"
+
+ command = Tempfile.new("io-test")
+ command.sync = true
+ command.write <<-G
+ if ARGV[0]
+ IO.for_fd(ARGV[0].to_i)
+ else
+ require 'tempfile'
+ io = Tempfile.new("io-test-fd")
+ args = %W[#{Gem.ruby} -I#{lib} #{bindir.join("bundle")} exec --keep-file-descriptors #{Gem.ruby} #{command.path} \#{io.to_i}]
+ args << { io.to_i => io } if RUBY_VERSION >= "2.0"
+ exec(*args)
+ end
+ G
+
+ install_gemfile ""
+ with_env_vars "RUBYOPT" => "-r#{spec_dir.join("support/hax")}" do
+ sys_exec "#{Gem.ruby} #{command.path}"
+ end
+
+ if Bundler.current_ruby.ruby_2?
+ expect(out).to eq("")
+ else
+ expect(out).to eq("Ruby version #{RUBY_VERSION} defaults to keeping non-standard file descriptors on Kernel#exec.")
+ end
+
+ expect(err).to lack_errors
+ end
+
+ it "accepts --keep-file-descriptors" do
+ install_gemfile ""
+ bundle "exec --keep-file-descriptors echo foobar"
+
+ expect(err).to lack_errors
+ end
+
+ it "can run a command named --verbose" do
+ install_gemfile 'gem "rack"'
+ File.open("--verbose", "w") do |f|
+ f.puts "#!/bin/sh"
+ f.puts "echo foobar"
+ end
+ File.chmod(0o744, "--verbose")
+ with_path_as(".") do
+ bundle "exec -- --verbose"
+ end
+ expect(out).to eq("foobar")
+ end
+
+ it "handles different versions in different bundles" do
+ build_repo2 do
+ build_gem "rack_two", "1.0.0" do |s|
+ s.executables = "rackup"
+ end
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ G
+
+ Dir.chdir bundled_app2 do
+ install_gemfile bundled_app2("Gemfile"), <<-G
+ source "file://#{gem_repo2}"
+ gem "rack_two", "1.0.0"
+ G
+ end
+
+ bundle! "exec rackup"
+
+ expect(out).to eq("0.9.1")
+
+ Dir.chdir bundled_app2 do
+ bundle! "exec rackup"
+ expect(out).to eq("1.0.0")
+ end
+ end
+
+ it "handles gems installed with --without" do
+ install_gemfile <<-G, forgotten_command_line_options(:without => "middleware")
+ source "file://#{gem_repo1}"
+ gem "rack" # rack 0.9.1 and 1.0 exist
+
+ group :middleware do
+ gem "rack_middleware" # rack_middleware depends on rack 0.9.1
+ end
+ G
+
+ bundle "exec rackup"
+
+ expect(out).to eq("0.9.1")
+ expect(the_bundle).not_to include_gems "rack_middleware 1.0"
+ end
+
+ it "does not duplicate already exec'ed RUBYOPT" do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ rubyopt = ENV["RUBYOPT"]
+ rubyopt = "-rbundler/setup #{rubyopt}"
+
+ bundle "exec 'echo $RUBYOPT'"
+ expect(out).to have_rubyopts(rubyopt)
+
+ bundle "exec 'echo $RUBYOPT'", :env => { "RUBYOPT" => rubyopt }
+ expect(out).to have_rubyopts(rubyopt)
+ end
+
+ it "does not duplicate already exec'ed RUBYLIB" do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ rubylib = ENV["RUBYLIB"]
+ rubylib = "#{rubylib}".split(File::PATH_SEPARATOR).unshift "#{bundler_path}"
+ rubylib = rubylib.uniq.join(File::PATH_SEPARATOR)
+
+ bundle "exec 'echo $RUBYLIB'"
+ expect(out).to include(rubylib)
+
+ bundle "exec 'echo $RUBYLIB'", :env => { "RUBYLIB" => rubylib }
+ expect(out).to include(rubylib)
+ end
+
+ it "errors nicely when the argument doesn't exist" do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ bundle "exec foobarbaz"
+ expect(exitstatus).to eq(127) if exitstatus
+ expect(out).to include("bundler: command not found: foobarbaz")
+ expect(out).to include("Install missing gem executables with `bundle install`")
+ end
+
+ it "errors nicely when the argument is not executable" do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ bundle "exec touch foo"
+ bundle "exec ./foo"
+ expect(exitstatus).to eq(126) if exitstatus
+ expect(out).to include("bundler: not executable: ./foo")
+ end
+
+ it "errors nicely when no arguments are passed" do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ bundle "exec"
+ expect(exitstatus).to eq(128) if exitstatus
+ expect(out).to include("bundler: exec needs a command to run")
+ end
+
+ it "raises a helpful error when exec'ing to something outside of the bundle", :ruby_repo, :rubygems => ">= 2.5.2" do
+ bundle! "config clean false" # want to keep the rackup binstub
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "with_license"
+ G
+ [true, false].each do |l|
+ bundle! "config disable_exec_load #{l}"
+ bundle "exec rackup"
+ expect(last_command.stderr).to include "can't find executable rackup for gem rack. rack is not currently included in the bundle, perhaps you meant to add it to your Gemfile?"
+ end
+ end
+
+ # Different error message on old RG versions (before activate_bin_path) because they
+ # called `Kernel#gem` directly
+ it "raises a helpful error when exec'ing to something outside of the bundle", :rubygems => "< 2.5.2" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "with_license"
+ G
+ [true, false].each do |l|
+ bundle! "config disable_exec_load #{l}"
+ bundle "exec rackup", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(last_command.stderr).to include "rack is not part of the bundle. Add it to your Gemfile."
+ end
+ end
+
+ describe "with help flags" do
+ each_prefix = proc do |string, &blk|
+ 1.upto(string.length) {|l| blk.call(string[0, l]) }
+ end
+ each_prefix.call("exec") do |exec|
+ describe "when #{exec} is used" do
+ before(:each) do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ create_file("print_args", <<-'RUBY')
+ #!/usr/bin/env ruby
+ puts "args: #{ARGV.inspect}"
+ RUBY
+ bundled_app("print_args").chmod(0o755)
+ end
+
+ it "shows executable's man page when --help is after the executable" do
+ bundle "#{exec} print_args --help"
+ expect(out).to eq('args: ["--help"]')
+ end
+
+ it "shows executable's man page when --help is after the executable and an argument" do
+ bundle "#{exec} print_args foo --help"
+ expect(out).to eq('args: ["foo", "--help"]')
+
+ bundle "#{exec} print_args foo bar --help"
+ expect(out).to eq('args: ["foo", "bar", "--help"]')
+
+ bundle "#{exec} print_args foo --help bar"
+ expect(out).to eq('args: ["foo", "--help", "bar"]')
+ end
+
+ it "shows executable's man page when the executable has a -" do
+ FileUtils.mv(bundled_app("print_args"), bundled_app("docker-template"))
+ bundle "#{exec} docker-template build discourse --help"
+ expect(out).to eq('args: ["build", "discourse", "--help"]')
+ end
+
+ it "shows executable's man page when --help is after another flag" do
+ bundle "#{exec} print_args --bar --help"
+ expect(out).to eq('args: ["--bar", "--help"]')
+ end
+
+ it "uses executable's original behavior for -h" do
+ bundle "#{exec} print_args -h"
+ expect(out).to eq('args: ["-h"]')
+ end
+
+ it "shows bundle-exec's man page when --help is between exec and the executable" do
+ with_fake_man do
+ bundle "#{exec} --help cat"
+ end
+ expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
+ end
+
+ it "shows bundle-exec's man page when --help is before exec" do
+ with_fake_man do
+ bundle "--help #{exec}"
+ end
+ expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
+ end
+
+ it "shows bundle-exec's man page when -h is before exec" do
+ with_fake_man do
+ bundle "-h #{exec}"
+ end
+ expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
+ end
+
+ it "shows bundle-exec's man page when --help is after exec" do
+ with_fake_man do
+ bundle "#{exec} --help"
+ end
+ expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
+ end
+
+ it "shows bundle-exec's man page when -h is after exec" do
+ with_fake_man do
+ bundle "#{exec} -h"
+ end
+ expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
+ end
+ end
+ end
+ end
+
+ describe "with gem executables" do
+ describe "run from a random directory", :ruby_repo do
+ before(:each) do
+ install_gemfile <<-G
+ gem "rack"
+ G
+ end
+
+ it "works when unlocked" do
+ bundle "exec 'cd #{tmp("gems")} && rackup'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to eq("1.0.0")
+ expect(out).to include("1.0.0")
+ end
+
+ it "works when locked" do
+ expect(the_bundle).to be_locked
+ bundle "exec 'cd #{tmp("gems")} && rackup'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to include("1.0.0")
+ end
+ end
+
+ describe "from gems bundled via :path" do
+ before(:each) do
+ build_lib "fizz", :path => home("fizz") do |s|
+ s.executables = "fizz"
+ end
+
+ install_gemfile <<-G
+ gem "fizz", :path => "#{File.expand_path(home("fizz"))}"
+ G
+ end
+
+ it "works when unlocked" do
+ bundle "exec fizz"
+ expect(out).to eq("1.0")
+ end
+
+ it "works when locked" do
+ expect(the_bundle).to be_locked
+
+ bundle "exec fizz"
+ expect(out).to eq("1.0")
+ end
+ end
+
+ describe "from gems bundled via :git" do
+ before(:each) do
+ build_git "fizz_git" do |s|
+ s.executables = "fizz_git"
+ end
+
+ install_gemfile <<-G
+ gem "fizz_git", :git => "#{lib_path("fizz_git-1.0")}"
+ G
+ end
+
+ it "works when unlocked" do
+ bundle "exec fizz_git"
+ expect(out).to eq("1.0")
+ end
+
+ it "works when locked" do
+ expect(the_bundle).to be_locked
+ bundle "exec fizz_git"
+ expect(out).to eq("1.0")
+ end
+ end
+
+ describe "from gems bundled via :git with no gemspec" do
+ before(:each) do
+ build_git "fizz_no_gemspec", :gemspec => false do |s|
+ s.executables = "fizz_no_gemspec"
+ end
+
+ install_gemfile <<-G
+ gem "fizz_no_gemspec", "1.0", :git => "#{lib_path("fizz_no_gemspec-1.0")}"
+ G
+ end
+
+ it "works when unlocked" do
+ bundle "exec fizz_no_gemspec"
+ expect(out).to eq("1.0")
+ end
+
+ it "works when locked" do
+ expect(the_bundle).to be_locked
+ bundle "exec fizz_no_gemspec"
+ expect(out).to eq("1.0")
+ end
+ end
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle "exec rackup"
+ expect(out).to include("Installing foo 1.0")
+ end
+
+ describe "with gems bundled via :path with invalid gemspecs", :ruby_repo do
+ it "outputs the gemspec validation errors", :rubygems => ">= 1.7.2" do
+ build_lib "foo"
+
+ gemspec = lib_path("foo-1.0").join("foo.gemspec").to_s
+ File.open(gemspec, "w") do |f|
+ f.write <<-G
+ Gem::Specification.new do |s|
+ s.name = 'foo'
+ s.version = '1.0'
+ s.summary = 'TODO: Add summary'
+ s.authors = 'Me'
+ end
+ G
+ end
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle "exec irb"
+
+ expect(err).to match("The gemspec at #{lib_path("foo-1.0").join("foo.gemspec")} is not valid")
+ expect(err).to match('"TODO" is not a summary')
+ end
+ end
+
+ describe "with gems bundled for deployment" do
+ it "works when calling bundler from another script" do
+ gemfile <<-G
+ module Monkey
+ def bin_path(a,b,c)
+ raise Gem::GemNotFoundException.new('Fail')
+ end
+ end
+ Bundler.rubygems.extend(Monkey)
+ G
+ bundle "install --deployment"
+ bundle "exec ruby -e '`#{bindir.join("bundler")} -v`; puts $?.success?'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to match("true")
+ end
+ end
+
+ context "`load`ing a ruby file instead of `exec`ing" do
+ let(:path) { bundled_app("ruby_executable") }
+ let(:shebang) { "#!/usr/bin/env ruby" }
+ let(:executable) { <<-RUBY.gsub(/^ */, "").strip }
+ #{shebang}
+
+ require "rack"
+ puts "EXEC: \#{caller.grep(/load/).empty? ? 'exec' : 'load'}"
+ puts "ARGS: \#{$0} \#{ARGV.join(' ')}"
+ puts "RACK: \#{RACK}"
+ process_title = `ps -o args -p \#{Process.pid}`.split("\n", 2).last.strip
+ puts "PROCESS: \#{process_title}"
+ RUBY
+
+ before do
+ path.open("w") {|f| f << executable }
+ path.chmod(0o755)
+
+ install_gemfile <<-G
+ gem "rack"
+ G
+ end
+
+ let(:exec) { "EXEC: load" }
+ let(:args) { "ARGS: #{path} arg1 arg2" }
+ let(:rack) { "RACK: 1.0.0" }
+ let(:process) do
+ title = "PROCESS: #{path}"
+ title += " arg1 arg2" if RUBY_VERSION >= "2.1"
+ title
+ end
+ let(:exit_code) { 0 }
+ let(:expected) { [exec, args, rack, process].join("\n") }
+ let(:expected_err) { "" }
+
+ subject { bundle "exec #{path} arg1 arg2", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" } }
+
+ shared_examples_for "it runs" do
+ it "like a normally executed executable" do
+ subject
+ expect(exitstatus).to eq(exit_code) if exitstatus
+ expect(last_command.stderr).to eq(expected_err)
+ expect(last_command.stdout).to eq(expected)
+ end
+ end
+
+ it_behaves_like "it runs"
+
+ context "the executable exits explicitly" do
+ let(:executable) { super() << "\nexit #{exit_code}\nputs 'POST_EXIT'\n" }
+
+ context "with exit 0" do
+ it_behaves_like "it runs"
+ end
+
+ context "with exit 99" do
+ let(:exit_code) { 99 }
+ it_behaves_like "it runs"
+ end
+ end
+
+ context "the executable exits by SignalException" do
+ let(:executable) do
+ ex = super()
+ ex << "\n"
+ if LessThanProc.with(RUBY_VERSION).call("1.9")
+ # Ruby < 1.9 needs a flush for a exit by signal, later
+ # rubies do not
+ ex << "STDOUT.flush\n"
+ end
+ ex << "raise SignalException, 'SIGTERM'\n"
+ ex
+ end
+ let(:expected_err) { ENV["TRAVIS"] ? "Terminated" : "" }
+ let(:exit_code) do
+ # signal mask 128 + plus signal 15 -> TERM
+ # this is specified by C99
+ 128 + 15
+ end
+ it_behaves_like "it runs"
+ end
+
+ context "the executable is empty", :bundler => "< 2" do
+ let(:executable) { "" }
+
+ let(:exit_code) { 0 }
+ let(:expected) { "#{path} is empty" }
+ let(:expected_err) { "" }
+ if LessThanProc.with(RUBY_VERSION).call("1.9")
+ # Kernel#exec in ruby < 1.9 will raise Errno::ENOEXEC if the command content is empty,
+ # even if the command is set as an executable.
+ pending "Kernel#exec is different"
+ else
+ it_behaves_like "it runs"
+ end
+ end
+
+ context "the executable is empty", :bundler => "2" do
+ let(:executable) { "" }
+
+ let(:exit_code) { 0 }
+ let(:expected_err) { "#{path} is empty" }
+ let(:expected) { "" }
+ it_behaves_like "it runs"
+ end
+
+ context "the executable raises", :bundler => "< 2" do
+ let(:executable) { super() << "\nraise 'ERROR'" }
+ let(:exit_code) { 1 }
+ let(:expected) { super() << "\nbundler: failed to load command: #{path} (#{path})" }
+ let(:expected_err) do
+ "RuntimeError: ERROR\n #{path}:10" +
+ (Bundler.current_ruby.ruby_18? ? "" : ":in `<top (required)>'")
+ end
+ it_behaves_like "it runs"
+ end
+
+ context "the executable raises", :bundler => "2" do
+ let(:executable) { super() << "\nraise 'ERROR'" }
+ let(:exit_code) { 1 }
+ let(:expected_err) do
+ "bundler: failed to load command: #{path} (#{path})" \
+ "\nRuntimeError: ERROR\n #{path}:10:in `<top (required)>'"
+ end
+ it_behaves_like "it runs"
+ end
+
+ context "the executable raises an error without a backtrace", :bundler => "< 2" do
+ let(:executable) { super() << "\nclass Err < Exception\ndef backtrace; end;\nend\nraise Err" }
+ let(:exit_code) { 1 }
+ let(:expected) { super() << "\nbundler: failed to load command: #{path} (#{path})" }
+ let(:expected_err) { "Err: Err" }
+
+ it_behaves_like "it runs"
+ end
+
+ context "the executable raises an error without a backtrace", :bundler => "2" do
+ let(:executable) { super() << "\nclass Err < Exception\ndef backtrace; end;\nend\nraise Err" }
+ let(:exit_code) { 1 }
+ let(:expected_err) { "bundler: failed to load command: #{path} (#{path})\nErr: Err" }
+ let(:expected) { super() }
+
+ it_behaves_like "it runs"
+ end
+
+ context "when the file uses the current ruby shebang", :ruby_repo do
+ let(:shebang) { "#!#{Gem.ruby}" }
+ it_behaves_like "it runs"
+ end
+
+ context "when Bundler.setup fails", :bundler => "< 2" do
+ before do
+ gemfile <<-G
+ gem 'rack', '2'
+ G
+ ENV["BUNDLER_FORCE_TTY"] = "true"
+ end
+
+ let(:exit_code) { Bundler::GemNotFound.new.status_code }
+ let(:expected) { <<-EOS.strip }
+\e[31mCould not find gem 'rack (= 2)' in any of the gem sources listed in your Gemfile.\e[0m
+\e[33mRun `bundle install` to install missing gems.\e[0m
+ EOS
+
+ it_behaves_like "it runs"
+ end
+
+ context "when Bundler.setup fails", :bundler => "2" do
+ before do
+ gemfile <<-G
+ gem 'rack', '2'
+ G
+ ENV["BUNDLER_FORCE_TTY"] = "true"
+ end
+
+ let(:exit_code) { Bundler::GemNotFound.new.status_code }
+ let(:expected) { <<-EOS.strip }
+\e[31mCould not find gem 'rack (= 2)' in locally installed gems.
+The source contains 'rack' at: 1.0.0\e[0m
+\e[33mRun `bundle install` to install missing gems.\e[0m
+ EOS
+
+ it_behaves_like "it runs"
+ 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 }
+
+ it_behaves_like "it runs"
+ end
+
+ context "when disable_exec_load is set" do
+ let(:exec) { "EXEC: exec" }
+ let(:process) { "PROCESS: ruby #{path} arg1 arg2" }
+
+ before do
+ bundle "config disable_exec_load true"
+ end
+
+ it_behaves_like "it runs"
+ end
+
+ context "regarding $0 and __FILE__" do
+ let(:executable) { super() + <<-'RUBY' }
+
+ puts "$0: #{$0.inspect}"
+ puts "__FILE__: #{__FILE__.inspect}"
+ RUBY
+
+ let(:expected) { super() + <<-EOS.chomp }
+
+$0: #{path.to_s.inspect}
+__FILE__: #{path.to_s.inspect}
+ EOS
+
+ it_behaves_like "it runs"
+
+ context "when the path is relative" do
+ let(:path) { super().relative_path_from(bundled_app) }
+
+ if LessThanProc.with(RUBY_VERSION).call("1.9")
+ pending "relative paths have ./ __FILE__"
+ else
+ it_behaves_like "it runs"
+ end
+ end
+
+ context "when the path is relative with a leading ./" do
+ let(:path) { Pathname.new("./#{super().relative_path_from(Pathname.pwd)}") }
+
+ if LessThanProc.with(RUBY_VERSION).call("< 1.9")
+ pending "relative paths with ./ have absolute __FILE__"
+ else
+ it_behaves_like "it runs"
+ end
+ end
+ end
+
+ context "signal handling" do
+ let(:test_signals) do
+ open3_reserved_signals = %w[CHLD CLD PIPE]
+ reserved_signals = %w[SEGV BUS ILL FPE VTALRM KILL STOP EXIT]
+ bundler_signals = %w[INT]
+
+ Signal.list.keys - (bundler_signals + reserved_signals + open3_reserved_signals)
+ end
+
+ context "signals being trapped by bundler" do
+ let(:executable) { strip_whitespace <<-RUBY }
+ #{shebang}
+ begin
+ Thread.new do
+ puts 'Started' # For process sync
+ STDOUT.flush
+ sleep 1 # ignore quality_spec
+ raise "Didn't receive INT at all"
+ end.join
+ rescue Interrupt
+ puts "foo"
+ end
+ RUBY
+
+ it "receives the signal", :ruby => ">= 1.9.3" do
+ bundle!("exec #{path}") do |_, o, thr|
+ o.gets # Consumes 'Started' and ensures that thread has started
+ Process.kill("INT", thr.pid)
+ end
+
+ expect(out).to eq("foo")
+ end
+ end
+
+ context "signals not being trapped by bunder" do
+ let(:executable) { strip_whitespace <<-RUBY }
+ #{shebang}
+
+ signals = #{test_signals.inspect}
+ result = signals.map do |sig|
+ Signal.trap(sig, "IGNORE")
+ end
+ puts result.select { |ret| ret == "IGNORE" }.count
+ RUBY
+
+ it "makes sure no unexpected signals are restored to DEFAULT" do
+ test_signals.each do |n|
+ Signal.trap(n, "IGNORE")
+ end
+
+ bundle!("exec #{path}")
+
+ expect(out).to eq(test_signals.count.to_s)
+ end
+ end
+ end
+ end
+
+ context "nested bundle exec" do
+ let(:system_gems_to_install) { super() << :bundler }
+
+ context "with shared gems disabled" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundle :install, :system_bundler => true, :path => "vendor/bundler"
+ end
+
+ it "overrides disable_shared_gems so bundler can be found" do
+ skip "bundler 1.16.x is not support with Ruby 2.6 on Travis CI" if RUBY_VERSION >= "2.6"
+
+ file = bundled_app("file_that_bundle_execs.rb")
+ create_file(file, <<-RB)
+ #!#{Gem.ruby}
+ puts `bundle exec echo foo`
+ RB
+ file.chmod(0o777)
+ bundle! "exec #{file}", :system_bundler => true
+ expect(out).to eq("foo")
+ end
+ end
+
+ context "with a system gem that shadows a default gem" do
+ let(:openssl_version) { "99.9.9" }
+ let(:expected) { ruby "gem 'openssl', '< 999999'; require 'openssl'; puts OpenSSL::VERSION", :artifice => nil }
+
+ it "only leaves the default gem in the stdlib available" do
+ skip "openssl isn't a default gem" if expected.empty?
+
+ install_gemfile! "" # must happen before installing the broken system gem
+
+ build_repo4 do
+ build_gem "openssl", openssl_version do |s|
+ s.write("lib/openssl.rb", <<-RB)
+ raise "custom openssl should not be loaded, it's not in the gemfile!"
+ RB
+ end
+ end
+
+ system_gems(:bundler, "openssl-#{openssl_version}", :gem_repo => gem_repo4)
+
+ file = bundled_app("require_openssl.rb")
+ create_file(file, <<-RB)
+ #!/usr/bin/env ruby
+ require "openssl"
+ puts OpenSSL::VERSION
+ warn Gem.loaded_specs.values.map(&:full_name)
+ RB
+ file.chmod(0o777)
+
+ aggregate_failures do
+ expect(bundle!("exec #{file}", :artifice => nil)).to eq(expected)
+ expect(bundle!("exec bundle exec #{file}", :artifice => nil)).to eq(expected)
+ expect(bundle!("exec ruby #{file}", :artifice => nil)).to eq(expected)
+ # Ignore expectaion for default bundler gem conflict.
+ unless ENV["BUNDLER_SPEC_SUB_VERSION"]
+ expect(run!(file.read, :artifice => nil)).to eq(expected)
+ end
+ end
+
+ # sanity check that we get the newer, custom version without bundler
+ sys_exec("#{Gem.ruby} #{file}")
+ expect(last_command.stderr).to include("custom openssl should not be loaded")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/commands/help_spec.rb b/spec/bundler/commands/help_spec.rb
new file mode 100644
index 0000000000..56b1b6f722
--- /dev/null
+++ b/spec/bundler/commands/help_spec.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle help" do
+ # RubyGems 1.4+ no longer load gem plugins so this test is no longer needed
+ it "complains if older versions of bundler are installed", :rubygems => "< 1.4" do
+ system_gems "bundler-0.8.1"
+
+ bundle "help"
+ expect(err).to include("older than 0.9")
+ expect(err).to include("running `gem cleanup bundler`.")
+ end
+
+ it "uses mann when available" do
+ with_fake_man do
+ bundle "help gemfile"
+ end
+ expect(out).to eq(%(["#{root}/man/gemfile.5"]))
+ end
+
+ it "prefixes bundle commands with bundle- when finding the groff files" do
+ with_fake_man do
+ bundle "help install"
+ end
+ expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
+ end
+
+ it "simply outputs the txt file when there is no man on the path" do
+ with_path_as("") do
+ bundle "help install"
+ end
+ expect(out).to match(/BUNDLE-INSTALL/)
+ 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")
+ end
+
+ it "looks for a binary and executes it with --help option if it's named bundler-<task>" do
+ File.open(tmp("bundler-testtasks"), "w", 0o755) do |f|
+ f.puts "#!/usr/bin/env ruby\nputs ARGV.join(' ')\n"
+ end
+
+ with_path_added(tmp) do
+ bundle "help testtasks"
+ end
+
+ expect(exitstatus).to be_zero if exitstatus
+ expect(out).to eq("--help")
+ end
+
+ it "is called when the --help flag is used after the command" do
+ with_fake_man do
+ bundle "install --help"
+ end
+ expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
+ end
+
+ it "is called when the --help flag is used before the command" do
+ with_fake_man do
+ bundle "--help install"
+ end
+ expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
+ end
+
+ it "is called when the -h flag is used before the command" do
+ with_fake_man do
+ bundle "-h install"
+ end
+ expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
+ end
+
+ it "is called when the -h flag is used after the command" do
+ with_fake_man do
+ bundle "install -h"
+ end
+ expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
+ end
+
+ it "has helpful output when using --help flag for a non-existent command" do
+ with_fake_man do
+ bundle "instill -h"
+ end
+ expect(out).to include('Could not find command "instill".')
+ end
+
+ it "is called when only using the --help flag" do
+ with_fake_man do
+ bundle "--help"
+ end
+ expect(out).to eq(%(["#{root}/man/bundle.1"]))
+
+ with_fake_man do
+ bundle "-h"
+ end
+ expect(out).to eq(%(["#{root}/man/bundle.1"]))
+ end
+end
diff --git a/spec/bundler/commands/info_spec.rb b/spec/bundler/commands/info_spec.rb
new file mode 100644
index 0000000000..a9ab8fc210
--- /dev/null
+++ b/spec/bundler/commands/info_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle info" do
+ context "info from specific gem in gemfile" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ end
+
+ it "prints information about the current gem" do
+ bundle "info rails"
+ expect(out).to include "* rails (2.3.2)
+\tSummary: This is just a fake gem for testing
+\tHomepage: http://example.com"
+ expect(out).to match(%r{Path\: .*\/rails\-2\.3\.2})
+ end
+
+ context "given a gem that is not installed" do
+ it "prints missing gem error" do
+ bundle "info foo"
+ expect(out).to eq "Could not find gem 'foo'."
+ end
+ end
+
+ context "given a default gem shippped in ruby", :ruby_repo do
+ it "prints information about the default gem", :if => (RUBY_VERSION >= "2.0") do
+ bundle "info rdoc"
+ expect(out).to include("* rdoc")
+ expect(out).to include("Default Gem: yes")
+ end
+ end
+
+ context "when gem does not have homepage" do
+ before do
+ build_repo1 do
+ build_gem "rails", "2.3.2" do |s|
+ s.executables = "rails"
+ s.summary = "Just another test gem"
+ end
+ end
+ end
+
+ it "excludes the homepage field from the output" do
+ expect(out).to_not include("Homepage:")
+ end
+ end
+
+ context "given --path option" do
+ it "prints the path to the gem" do
+ bundle "info rails"
+ expect(out).to match(%r{.*\/rails\-2\.3\.2})
+ end
+ end
+ end
+end
diff --git a/spec/bundler/commands/init_spec.rb b/spec/bundler/commands/init_spec.rb
new file mode 100644
index 0000000000..9b5bd95814
--- /dev/null
+++ b/spec/bundler/commands/init_spec.rb
@@ -0,0 +1,181 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle init" do
+ it "generates a Gemfile", :bundler => "< 2" do
+ bundle! :init
+ expect(out).to include("Writing new Gemfile")
+ expect(bundled_app("Gemfile")).to be_file
+ end
+
+ it "generates a gems.rb", :bundler => "2" do
+ bundle! :init
+ expect(out).to include("Writing new gems.rb")
+ expect(bundled_app("gems.rb")).to be_file
+ end
+
+ context "when a Gemfile already exists", :bundler => "< 2" do
+ before do
+ create_file "Gemfile", <<-G
+ gem "rails"
+ G
+ end
+
+ it "does not change existing Gemfiles" do
+ expect { bundle :init }.not_to change { File.read(bundled_app("Gemfile")) }
+ end
+
+ it "notifies the user that an existing Gemfile already exists" do
+ bundle :init
+ expect(out).to include("Gemfile already exists")
+ end
+ end
+
+ context "when gems.rb already exists", :bundler => ">= 2" do
+ before do
+ create_file("gems.rb", <<-G)
+ gem "rails"
+ G
+ end
+
+ it "does not change existing Gemfiles" do
+ expect { bundle :init }.not_to change { File.read(bundled_app("gems.rb")) }
+ end
+
+ it "notifies the user that an existing gems.rb already exists" do
+ bundle :init
+ expect(out).to include("gems.rb already exists")
+ end
+ end
+
+ context "when a Gemfile exists in a parent directory", :bundler => "< 2" do
+ let(:subdir) { "child_dir" }
+
+ it "lets users generate a Gemfile in a child directory" do
+ bundle! :init
+
+ FileUtils.mkdir bundled_app(subdir)
+
+ Dir.chdir bundled_app(subdir) do
+ bundle! :init
+ end
+
+ expect(out).to include("Writing new Gemfile")
+ expect(bundled_app("#{subdir}/Gemfile")).to be_file
+ end
+ end
+
+ 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
+ FileUtils.mkdir bundled_app(subdir)
+ # chmod a-w it
+ mode = File.stat(bundled_app(subdir)).mode ^ 0o222
+ FileUtils.chmod mode, bundled_app(subdir)
+
+ Dir.chdir bundled_app(subdir) do
+ bundle :init
+ end
+
+ expect(out).to include("directory is not writable")
+ expect(Dir[bundled_app("#{subdir}/*")]).to be_empty
+ end
+ end
+
+ context "when a gems.rb file exists in a parent directory", :bundler => ">= 2" do
+ let(:subdir) { "child_dir" }
+
+ it "lets users generate a Gemfile in a child directory" do
+ bundle! :init
+
+ FileUtils.mkdir bundled_app(subdir)
+
+ Dir.chdir bundled_app(subdir) do
+ bundle! :init
+ end
+
+ expect(out).to include("Writing new gems.rb")
+ expect(bundled_app("#{subdir}/gems.rb")).to be_file
+ end
+ end
+
+ context "given --gemspec option", :bundler => "< 2" do
+ let(:spec_file) { tmp.join("test.gemspec") }
+
+ it "should generate from an existing gemspec" do
+ File.open(spec_file, "w") do |file|
+ file << <<-S
+ Gem::Specification.new do |s|
+ s.name = 'test'
+ s.add_dependency 'rack', '= 1.0.1'
+ s.add_development_dependency 'rspec', '1.2'
+ end
+ S
+ end
+
+ bundle :init, :gemspec => spec_file
+
+ gemfile = if Bundler::VERSION[0, 2] == "1."
+ bundled_app("Gemfile").read
+ else
+ bundled_app("gems.rb").read
+ end
+ expect(gemfile).to match(%r{source 'https://rubygems.org'})
+ expect(gemfile.scan(/gem "rack", "= 1.0.1"/).size).to eq(1)
+ expect(gemfile.scan(/gem "rspec", "= 1.2"/).size).to eq(1)
+ expect(gemfile.scan(/group :development/).size).to eq(1)
+ end
+
+ context "when gemspec file is invalid" do
+ it "notifies the user that specification is invalid" do
+ File.open(spec_file, "w") do |file|
+ file << <<-S
+ Gem::Specification.new do |s|
+ s.name = 'test'
+ s.invalid_method_name
+ end
+ S
+ end
+
+ bundle :init, :gemspec => spec_file
+ expect(last_command.bundler_err).to include("There was an error while loading `test.gemspec`")
+ end
+ end
+ end
+
+ context "when init_gems_rb setting is enabled" do
+ before { bundle "config init_gems_rb true" }
+
+ context "given --gemspec option", :bundler => "< 2" do
+ let(:spec_file) { tmp.join("test.gemspec") }
+
+ before do
+ File.open(spec_file, "w") do |file|
+ file << <<-S
+ Gem::Specification.new do |s|
+ s.name = 'test'
+ s.add_dependency 'rack', '= 1.0.1'
+ s.add_development_dependency 'rspec', '1.2'
+ end
+ S
+ end
+ end
+
+ it "should generate from an existing gemspec" do
+ bundle :init, :gemspec => spec_file
+
+ gemfile = bundled_app("gems.rb").read
+ expect(gemfile).to match(%r{source 'https://rubygems.org'})
+ expect(gemfile.scan(/gem "rack", "= 1.0.1"/).size).to eq(1)
+ expect(gemfile.scan(/gem "rspec", "= 1.2"/).size).to eq(1)
+ expect(gemfile.scan(/group :development/).size).to eq(1)
+ end
+
+ it "prints message to user" do
+ bundle :init, :gemspec => spec_file
+
+ expect(out).to include("Writing new gems.rb")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/commands/inject_spec.rb b/spec/bundler/commands/inject_spec.rb
new file mode 100644
index 0000000000..b7ffc89a34
--- /dev/null
+++ b/spec/bundler/commands/inject_spec.rb
@@ -0,0 +1,117 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle inject", :bundler => "< 2" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ context "without a lockfile" do
+ it "locks with the injected gems" do
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ bundle "inject 'rack-obama' '> 0'"
+ expect(bundled_app("Gemfile.lock").read).to match(/rack-obama/)
+ end
+ end
+
+ context "with a lockfile" do
+ before do
+ bundle "install"
+ end
+
+ it "adds the injected gems to the Gemfile" do
+ expect(bundled_app("Gemfile").read).not_to match(/rack-obama/)
+ bundle "inject 'rack-obama' '> 0'"
+ expect(bundled_app("Gemfile").read).to match(/rack-obama/)
+ end
+
+ it "locks with the injected gems" do
+ expect(bundled_app("Gemfile.lock").read).not_to match(/rack-obama/)
+ bundle "inject 'rack-obama' '> 0'"
+ expect(bundled_app("Gemfile.lock").read).to match(/rack-obama/)
+ end
+ end
+
+ context "with injected gems already in the Gemfile" do
+ it "doesn't add existing gems" do
+ bundle "inject 'rack' '> 0'"
+ expect(out).to match(/cannot specify the same gem twice/i)
+ end
+ end
+
+ context "incorrect arguments" do
+ it "fails when more than 2 arguments are passed" do
+ bundle "inject gem_name 1 v"
+ expect(out).to eq(<<-E.strip)
+ERROR: "bundle inject" was called with arguments ["gem_name", "1", "v"]
+Usage: "bundle inject GEM VERSION"
+ E
+ end
+ end
+
+ context "with source option" do
+ it "add gem with source option in gemfile" do
+ bundle "inject 'foo' '>0' --source file://#{gem_repo1}"
+ gemfile = bundled_app("Gemfile").read
+ str = "gem \"foo\", \"> 0\", :source => \"file://#{gem_repo1}\""
+ expect(gemfile).to include str
+ end
+ end
+
+ context "with group option" do
+ it "add gem with group option in gemfile" do
+ bundle "inject 'rack-obama' '>0' --group=development"
+ gemfile = bundled_app("Gemfile").read
+ str = "gem \"rack-obama\", \"> 0\", :group => :development"
+ expect(gemfile).to include str
+ end
+
+ it "add gem with multiple groups in gemfile" do
+ bundle "inject 'rack-obama' '>0' --group=development,test"
+ gemfile = bundled_app("Gemfile").read
+ str = "gem \"rack-obama\", \"> 0\", :groups => [:development, :test]"
+ expect(gemfile).to include str
+ end
+ end
+
+ context "when frozen" do
+ before do
+ bundle "install"
+ if Bundler.feature_flag.bundler_2_mode?
+ bundle! "config --local deployment true"
+ else
+ bundle! "config --local frozen true"
+ end
+ end
+
+ it "injects anyway" do
+ bundle "inject 'rack-obama' '> 0'"
+ expect(bundled_app("Gemfile").read).to match(/rack-obama/)
+ end
+
+ it "locks with the injected gems" do
+ expect(bundled_app("Gemfile.lock").read).not_to match(/rack-obama/)
+ bundle "inject 'rack-obama' '> 0'"
+ expect(bundled_app("Gemfile.lock").read).to match(/rack-obama/)
+ end
+
+ it "restores frozen afterwards" do
+ bundle "inject 'rack-obama' '> 0'"
+ config = YAML.load(bundled_app(".bundle/config").read)
+ expect(config["BUNDLE_DEPLOYMENT"] || config["BUNDLE_FROZEN"]).to eq("true")
+ end
+
+ it "doesn't allow Gemfile changes" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack-obama"
+ G
+ bundle "inject 'rack' '> 0'"
+ expect(out).to match(/trying to install in deployment mode after changing/)
+
+ expect(bundled_app("Gemfile.lock").read).not_to match(/rack-obama/)
+ end
+ end
+end
diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb
new file mode 100644
index 0000000000..394f672fef
--- /dev/null
+++ b/spec/bundler/commands/install_spec.rb
@@ -0,0 +1,587 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with gem sources" do
+ describe "the simple case" do
+ it "prints output and returns if no dependencies are specified" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ G
+
+ bundle :install
+ expect(out).to match(/no dependencies/)
+ end
+
+ it "does not make a lockfile if the install fails" do
+ install_gemfile <<-G
+ raise StandardError, "FAIL"
+ G
+
+ expect(last_command.bundler_err).to include('StandardError, "FAIL"')
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ end
+
+ it "creates a Gemfile.lock" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ it "does not create ./.bundle by default", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle! :install # can't use install_gemfile since it sets retry
+ expect(bundled_app(".bundle")).not_to exist
+ end
+
+ it "does not create ./.bundle by default when installing to system gems" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle! :install, :env => { "BUNDLE_PATH__SYSTEM" => true } # can't use install_gemfile since it sets retry
+ expect(bundled_app(".bundle")).not_to exist
+ end
+
+ it "creates lock files based on the Gemfile name" do
+ gemfile bundled_app("OmgFile"), <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0"
+ G
+
+ bundle "install --gemfile OmgFile"
+
+ expect(bundled_app("OmgFile.lock")).to exist
+ end
+
+ it "doesn't delete the lockfile if one already exists" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ lockfile = File.read(bundled_app("Gemfile.lock"))
+
+ install_gemfile <<-G
+ raise StandardError, "FAIL"
+ G
+
+ expect(File.read(bundled_app("Gemfile.lock"))).to eq(lockfile)
+ end
+
+ it "does not touch the lockfile if nothing changed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ expect { run "1" }.not_to change { File.mtime(bundled_app("Gemfile.lock")) }
+ end
+
+ it "fetches gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ expect(default_bundle_path("gems/rack-1.0.0")).to exist
+ expect(the_bundle).to include_gems("rack 1.0.0")
+ end
+
+ it "fetches gems when multiple versions are specified" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack', "> 0.9", "< 1.0"
+ G
+
+ expect(default_bundle_path("gems/rack-0.9.1")).to exist
+ expect(the_bundle).to include_gems("rack 0.9.1")
+ end
+
+ it "fetches gems when multiple versions are specified take 2" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack', "< 1.0", "> 0.9"
+ G
+
+ expect(default_bundle_path("gems/rack-0.9.1")).to exist
+ expect(the_bundle).to include_gems("rack 0.9.1")
+ end
+
+ it "raises an appropriate error when gems are specified using symbols" do
+ install_gemfile(<<-G)
+ source "file://#{gem_repo1}"
+ gem :rack
+ G
+ expect(exitstatus).to eq(4) if exitstatus
+ end
+
+ it "pulls in dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ expect(the_bundle).to include_gems "actionpack 2.3.2", "rails 2.3.2"
+ end
+
+ it "does the right version" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ G
+
+ expect(the_bundle).to include_gems "rack 0.9.1"
+ end
+
+ it "does not install the development dependency" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "with_development_dependency"
+ G
+
+ expect(the_bundle).to include_gems("with_development_dependency 1.0.0").
+ and not_include_gems("activesupport 2.3.5")
+ end
+
+ it "resolves correctly" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activemerchant"
+ gem "rails"
+ G
+
+ expect(the_bundle).to include_gems "activemerchant 1.0", "activesupport 2.3.2", "actionpack 2.3.2"
+ end
+
+ it "activates gem correctly according to the resolved gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport", "2.3.5"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activemerchant"
+ gem "rails"
+ G
+
+ expect(the_bundle).to include_gems "activemerchant 1.0", "activesupport 2.3.2", "actionpack 2.3.2"
+ end
+
+ it "does not reinstall any gem that is already available locally" do
+ system_gems "activesupport-2.3.2", :path => :bundle_path
+
+ build_repo2 do
+ build_gem "activesupport", "2.3.2" do |s|
+ s.write "lib/activesupport.rb", "ACTIVESUPPORT = 'fail'"
+ end
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activerecord", "2.3.2"
+ G
+
+ expect(the_bundle).to include_gems "activesupport 2.3.2"
+ end
+
+ it "works when the gemfile specifies gems that only exist in the system" do
+ build_gem "foo", :to_bundle => true
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "foo"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "foo 1.0.0"
+ end
+
+ it "prioritizes local gems over remote gems" do
+ build_gem "rack", "1.0.0", :to_bundle => true do |s|
+ s.add_dependency "activesupport", "2.3.5"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
+ end
+
+ describe "with a gem that installs multiple platforms" do
+ it "installs gems for the local platform as first choice" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+
+ run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
+ expect(out).to eq("1.0.0 #{Bundler.local_platform}")
+ end
+
+ it "falls back on plain ruby" do
+ simulate_platform "foo-bar-baz"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+
+ run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
+ expect(out).to eq("1.0.0 RUBY")
+ end
+
+ it "installs gems for java" do
+ simulate_platform "java"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+
+ run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
+ expect(out).to eq("1.0.0 JAVA")
+ end
+
+ it "installs gems for windows" do
+ simulate_platform mswin
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+
+ run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
+ expect(out).to eq("1.0.0 MSWIN")
+ end
+ end
+
+ describe "doing bundle install foo" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "works" do
+ bundle "install", forgotten_command_line_options(:path => "vendor")
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "allows running bundle install --system without deleting foo", :bundler => "< 2" do
+ bundle "install", forgotten_command_line_options(:path => "vendor")
+ bundle "install", forgotten_command_line_options(:system => true)
+ FileUtils.rm_rf(bundled_app("vendor"))
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "allows running bundle install --system after deleting foo", :bundler => "< 2" do
+ bundle "install", forgotten_command_line_options(:path => "vendor")
+ FileUtils.rm_rf(bundled_app("vendor"))
+ bundle "install", forgotten_command_line_options(:system => true)
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ it "finds gems in multiple sources", :bundler => "< 2" do
+ build_repo2
+ update_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ source "file://#{gem_repo2}"
+
+ gem "activesupport", "1.2.3"
+ gem "rack", "1.2"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.2", "activesupport 1.2.3"
+ end
+
+ it "gives a useful error if no sources are set" do
+ install_gemfile <<-G
+ gem "rack"
+ G
+
+ bundle :install
+ expect(out).to include("Your Gemfile has no gem server sources")
+ end
+
+ it "creates a Gemfile.lock on a blank Gemfile" do
+ install_gemfile <<-G
+ G
+
+ expect(File.exist?(bundled_app("Gemfile.lock"))).to eq(true)
+ end
+
+ context "throws a warning if a gem is added twice in Gemfile" do
+ it "without version requirements" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ gem "rack"
+ G
+
+ expect(out).to include("Your Gemfile lists the gem rack (>= 0) more than once.")
+ expect(out).to include("Remove any duplicate entries and specify the gem only once (per group).")
+ expect(out).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.")
+ end
+
+ it "with same versions" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack", "1.0"
+ gem "rack", "1.0"
+ G
+
+ expect(out).to include("Your Gemfile lists the gem rack (= 1.0) more than once.")
+ expect(out).to include("Remove any duplicate entries and specify the gem only once (per group).")
+ expect(out).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.")
+ end
+ end
+
+ context "throws an error if a gem is added twice in Gemfile" do
+ it "when version of one dependency is not specified" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ gem "rack", "1.0"
+ G
+
+ expect(out).to include("You cannot specify the same gem twice with different version requirements")
+ expect(out).to include("You specified: rack (>= 0) and rack (= 1.0).")
+ end
+
+ it "when different versions of both dependencies are specified" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack", "1.0"
+ gem "rack", "1.1"
+ G
+
+ expect(out).to include("You cannot specify the same gem twice with different version requirements")
+ expect(out).to include("You specified: rack (= 1.0) and rack (= 1.1).")
+ end
+ end
+
+ it "gracefully handles error when rubygems server is unavailable" do
+ install_gemfile <<-G, :artifice => nil
+ source "file://#{gem_repo1}"
+ source "http://localhost:9384" do
+ gem 'foo'
+ end
+ G
+
+ bundle :install, :artifice => nil
+ expect(out).to include("Could not fetch specs from http://localhost:9384/")
+ expect(out).not_to include("file://")
+ end
+
+ it "fails gracefully when downloading an invalid specification from the full index", :rubygems => "2.5" do
+ build_repo2 do
+ build_gem "ajp-rails", "0.0.0", :gemspec => false, :skip_validation => true do |s|
+ bad_deps = [["ruby-ajp", ">= 0.2.0"], ["rails", ">= 0.14"]]
+ s.
+ instance_variable_get(:@spec).
+ instance_variable_set(:@dependencies, bad_deps)
+
+ raise "failed to set bad deps" unless s.dependencies == bad_deps
+ end
+ build_gem "ruby-ajp", "1.0.0"
+ end
+
+ install_gemfile <<-G, :full_index => true
+ source "file:\/\/localhost#{gem_repo2}"
+
+ gem "ajp-rails", "0.0.0"
+ G
+
+ expect(last_command.stdboth).not_to match(/Error Report/i)
+ expect(last_command.bundler_err).to include("An error occurred while installing ajp-rails (0.0.0), and Bundler cannot continue.").
+ and include(normalize_uri_file("Make sure that `gem install ajp-rails -v '0.0.0' --source 'file://localhost#{gem_repo2}/'` succeeds before bundling."))
+ end
+
+ it "doesn't blow up when the local .bundle/config is empty" do
+ FileUtils.mkdir_p(bundled_app(".bundle"))
+ FileUtils.touch(bundled_app(".bundle/config"))
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo1}"
+
+ gem 'foo'
+ G
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "doesn't blow up when the global .bundle/config is empty" do
+ FileUtils.mkdir_p("#{Bundler.rubygems.user_home}/.bundle")
+ FileUtils.touch("#{Bundler.rubygems.user_home}/.bundle/config")
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo1}"
+
+ gem 'foo'
+ G
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+ end
+
+ describe "Ruby version in Gemfile.lock" do
+ include Bundler::GemHelpers
+
+ context "and using an unsupported Ruby version" do
+ it "prints an error" do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '2.0.1'
+ ruby '~> 2.2'
+ G
+ expect(out).to include("Your Ruby version is 2.0.1, but your Gemfile specified ~> 2.2")
+ end
+ end
+
+ context "and using a supported Ruby version" do
+ before do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '2.1.3'
+ ::RUBY_PATCHLEVEL = 100
+ ruby '~> 2.1.0'
+ G
+ end
+
+ it "writes current Ruby version to Gemfile.lock" do
+ lockfile_should_be <<-L
+ GEM
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+
+ RUBY VERSION
+ ruby 2.1.3p100
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "updates Gemfile.lock with updated incompatible ruby version" do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '2.2.3'
+ ::RUBY_PATCHLEVEL = 100
+ ruby '~> 2.2.0'
+ G
+
+ lockfile_should_be <<-L
+ GEM
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+
+ RUBY VERSION
+ ruby 2.2.3p100
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+ end
+
+ describe "when Bundler root contains regex chars" do
+ before do
+ root_dir = tmp("foo[]bar")
+
+ FileUtils.mkdir_p(root_dir)
+ in_app_root_custom(root_dir)
+ end
+
+ it "doesn't blow up" do
+ build_lib "foo"
+ gemfile = <<-G
+ gem 'foo', :path => "#{lib_path("foo-1.0")}"
+ G
+ File.open("Gemfile", "w") do |file|
+ file.puts gemfile
+ end
+
+ bundle :install
+
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+ end
+
+ describe "when requesting a quiet install via --quiet" do
+ it "should be quiet" do
+ gemfile <<-G
+ gem 'rack'
+ G
+
+ bundle :install, :quiet => true
+ expect(out).to include("Could not find gem 'rack'")
+ expect(out).to_not include("Your Gemfile has no gem server sources")
+ end
+ end
+
+ describe "when bundle path does not have write access" do
+ before do
+ FileUtils.mkdir_p(bundled_app("vendor"))
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+ end
+
+ it "should display a proper message to explain the problem" do
+ FileUtils.chmod(0o500, bundled_app("vendor"))
+
+ bundle :install, forgotten_command_line_options(:path => "vendor")
+ expect(out).to include(bundled_app("vendor").to_s)
+ expect(out).to include("grant write permissions")
+ end
+ end
+
+ context "after installing with --standalone" do
+ before do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ forgotten_command_line_options(:path => "bundle")
+ bundle! "install", :standalone => true
+ end
+
+ it "includes the standalone path" do
+ bundle! "binstubs rack", :standalone => true
+ standalone_line = File.read(bundled_app("bin/rackup")).each_line.find {|line| line.include? "$:.unshift" }.strip
+ expect(standalone_line).to eq %($:.unshift File.expand_path "../../bundle", path.realpath)
+ end
+ end
+
+ describe "when bundle install is executed with unencoded authentication" do
+ before do
+ gemfile <<-G
+ source 'https://rubygems.org/'
+ gem "."
+ G
+ end
+
+ it "should display a helpful messag explaining how to fix it" do
+ bundle :install, :env => { "BUNDLE_RUBYGEMS__ORG" => "user:pass{word" }
+ expect(exitstatus).to eq(17) if exitstatus
+ expect(out).to eq("Please CGI escape your usernames and passwords before " \
+ "setting them for authentication.")
+ end
+ end
+end
diff --git a/spec/bundler/commands/issue_spec.rb b/spec/bundler/commands/issue_spec.rb
new file mode 100644
index 0000000000..04c575130e
--- /dev/null
+++ b/spec/bundler/commands/issue_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle issue" do
+ it "exits with a message" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ bundle "issue"
+ expect(out).to include "Did you find an issue with Bundler?"
+ expect(out).to include "## Environment"
+ expect(out).to include "## Gemfile"
+ expect(out).to include "## Bundle Doctor"
+ end
+end
diff --git a/spec/bundler/commands/licenses_spec.rb b/spec/bundler/commands/licenses_spec.rb
new file mode 100644
index 0000000000..d61d3492f3
--- /dev/null
+++ b/spec/bundler/commands/licenses_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle licenses" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ gem "with_license"
+ G
+ end
+
+ it "prints license information for all gems in the bundle" do
+ bundle "licenses"
+
+ loaded_bundler_spec = Bundler.load.specs["bundler"]
+ expected = if !loaded_bundler_spec.empty?
+ loaded_bundler_spec[0].license
+ else
+ "Unknown"
+ end
+
+ expect(out).to include("bundler: #{expected}")
+ expect(out).to include("with_license: MIT")
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ gem "with_license"
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle :licenses
+ expect(out).to include("Installing foo 1.0")
+ end
+end
diff --git a/spec/bundler/commands/list_spec.rb b/spec/bundler/commands/list_spec.rb
new file mode 100644
index 0000000000..5305176c65
--- /dev/null
+++ b/spec/bundler/commands/list_spec.rb
@@ -0,0 +1,131 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle list", :bundler => "2" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ gem "rspec", :group => [:test]
+ G
+ end
+
+ context "with name-only and paths option" do
+ it "raises an error" do
+ bundle "list --name-only --paths"
+
+ expect(out).to eq "The `--name-only` and `--paths` options cannot be used together"
+ end
+ end
+
+ context "with without-group and only-group option" do
+ it "raises an error" do
+ bundle "list --without-group dev --only-group test"
+
+ expect(out).to eq "The `--only-group` and `--without-group` options cannot be used together"
+ end
+ end
+
+ describe "with without-group option" do
+ context "when group is present" do
+ it "prints the gems not in the specified group" do
+ bundle! "list --without-group test"
+
+ expect(out).to include(" * rack (1.0.0)")
+ expect(out).not_to include(" * rspec (1.2.7)")
+ end
+ end
+
+ context "when group is not found" do
+ it "raises an error" do
+ bundle "list --without-group random"
+
+ expect(out).to eq "`random` group could not be found."
+ end
+ end
+ end
+
+ describe "with only-group option" do
+ context "when group is present" do
+ it "prints the gems in the specified group" do
+ bundle! "list --only-group default"
+
+ expect(out).to include(" * rack (1.0.0)")
+ expect(out).not_to include(" * rspec (1.2.7)")
+ end
+ end
+
+ context "when group is not found" do
+ it "raises an error" do
+ bundle "list --only-group random"
+
+ expect(out).to eq "`random` group could not be found."
+ end
+ end
+ end
+
+ context "with name-only option" do
+ it "prints only the name of the gems in the bundle" do
+ bundle "list --name-only"
+
+ expect(out).to include("rack")
+ expect(out).to include("rspec")
+ end
+ end
+
+ context "with paths option" do
+ before do
+ build_repo2 do
+ build_gem "bar"
+ end
+
+ build_git "git_test", "1.0.0", :path => lib_path("git_test")
+
+ build_lib("gemspec_test", :path => tmp.join("gemspec_test")) do |s|
+ s.write("Gemfile", "source :rubygems\ngemspec")
+ s.add_dependency "bar", "=1.0.0"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ gem "rails"
+ gem "git_test", :git => "#{lib_path("git_test")}"
+ gemspec :path => "#{tmp.join("gemspec_test")}"
+ G
+
+ bundle! "install"
+ end
+
+ it "prints the path of each gem in the bundle" do
+ bundle "list --paths"
+ expect(out).to match(%r{.*\/rails\-2\.3\.2})
+ expect(out).to match(%r{.*\/rack\-1\.2})
+ expect(out).to match(%r{.*\/git_test\-\w})
+ expect(out).to match(%r{.*\/gemspec_test})
+ end
+ end
+
+ context "when no gems are in the gemfile" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+
+ it "prints message saying no gems are in the bundle" do
+ bundle "list"
+ expect(out).to include("No gems in the Gemfile")
+ end
+ end
+
+ it "lists gems installed in the bundle" do
+ bundle "list"
+ expect(out).to include(" * rack (1.0.0)")
+ end
+
+ it "aliases the ls command to list" do
+ bundle "ls"
+ expect(out).to include("Gems included by the bundle")
+ end
+end
diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb
new file mode 100644
index 0000000000..0b77605f01
--- /dev/null
+++ b/spec/bundler/commands/lock_spec.rb
@@ -0,0 +1,322 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle lock" do
+ def strip_lockfile(lockfile)
+ strip_whitespace(lockfile).sub(/\n\Z/, "")
+ end
+
+ def read_lockfile(file = "Gemfile.lock")
+ strip_lockfile bundled_app(file).read
+ end
+
+ let(:repo) { gem_repo1 }
+
+ before :each do
+ gemfile <<-G
+ source "file://localhost#{repo}"
+ gem "rails"
+ gem "with_license"
+ gem "foo"
+ G
+
+ @lockfile = strip_lockfile(normalize_uri_file(<<-L))
+ GEM
+ remote: file://localhost#{repo}/
+ specs:
+ actionmailer (2.3.2)
+ activesupport (= 2.3.2)
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activerecord (2.3.2)
+ activesupport (= 2.3.2)
+ activeresource (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ foo (1.0)
+ rails (2.3.2)
+ actionmailer (= 2.3.2)
+ actionpack (= 2.3.2)
+ activerecord (= 2.3.2)
+ activeresource (= 2.3.2)
+ rake (= 10.0.2)
+ rake (10.0.2)
+ with_license (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ rails
+ with_license
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "prints a lockfile when there is no existing lockfile with --print" do
+ bundle "lock --print"
+
+ expect(out).to eq(@lockfile)
+ end
+
+ it "prints a lockfile when there is an existing lockfile with --print" do
+ lockfile @lockfile
+
+ bundle "lock --print"
+
+ expect(out).to eq(@lockfile)
+ end
+
+ it "writes a lockfile when there is no existing lockfile" do
+ bundle "lock"
+
+ expect(read_lockfile).to eq(@lockfile)
+ end
+
+ it "writes a lockfile when there is an outdated lockfile using --update" do
+ lockfile @lockfile.gsub("2.3.2", "2.3.1")
+
+ bundle! "lock --update"
+
+ expect(read_lockfile).to eq(@lockfile)
+ end
+
+ it "does not fetch remote specs when using the --local option" do
+ bundle "lock --update --local"
+
+ expect(out).to match(/sources listed in your Gemfile|installed locally/)
+ end
+
+ it "writes to a custom location using --lockfile" do
+ bundle "lock --lockfile=lock"
+
+ expect(out).to match(/Writing lockfile to.+lock/)
+ expect(read_lockfile "lock").to eq(@lockfile)
+ expect { read_lockfile }.to raise_error(Errno::ENOENT)
+ end
+
+ it "writes to custom location using --lockfile when a default lockfile is present" do
+ bundle "install"
+ bundle "lock --lockfile=lock"
+
+ expect(out).to match(/Writing lockfile to.+lock/)
+ expect(read_lockfile("lock")).to eq(@lockfile)
+ end
+
+ it "update specific gems using --update" do
+ lockfile @lockfile.gsub("2.3.2", "2.3.1").gsub("10.0.2", "10.0.1")
+
+ bundle "lock --update rails rake"
+
+ expect(read_lockfile).to eq(@lockfile)
+ end
+
+ it "errors when updating a missing specific gems using --update" do
+ lockfile @lockfile
+
+ bundle "lock --update blahblah"
+ expect(out).to eq("Could not find gem 'blahblah'.")
+
+ expect(read_lockfile).to eq(@lockfile)
+ end
+
+ # see update_spec for more coverage on same options. logic is shared so it's not necessary
+ # to repeat coverage here.
+ context "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.0.1 1.1.0 2.0.0]
+ end
+
+ # establish a lockfile set to 1.4.3
+ install_gemfile <<-G
+ source "file://#{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://#{gem_repo4}"
+ gem 'foo'
+ gem 'qux'
+ G
+ end
+
+ it "single gem updates dependent gem to minor" do
+ bundle "lock --update foo --patch"
+
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.4.5 bar-2.1.1 qux-1.0.0].sort)
+ end
+
+ it "minor preferred with strict" do
+ bundle "lock --update --minor --strict"
+
+ 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
+ end
+
+ it "supports adding new platforms" do
+ bundle! "lock --add-platform java x86-mingw32"
+
+ lockfile = Bundler::LockfileParser.new(read_lockfile)
+ expect(lockfile.platforms).to match_array(local_platforms.unshift(java, mingw).uniq)
+ end
+
+ it "supports adding the `ruby` platform" do
+ bundle! "lock --add-platform ruby"
+ lockfile = Bundler::LockfileParser.new(read_lockfile)
+ expect(lockfile.platforms).to match_array(local_platforms.unshift("ruby").uniq)
+ end
+
+ it "warns when adding an unknown platform" do
+ bundle "lock --add-platform foobarbaz"
+ expect(out).to include("The platform `foobarbaz` is unknown to RubyGems and adding it will likely lead to resolution errors")
+ end
+
+ it "allows removing platforms" do
+ bundle! "lock --add-platform java x86-mingw32"
+
+ lockfile = Bundler::LockfileParser.new(read_lockfile)
+ expect(lockfile.platforms).to match_array(local_platforms.unshift(java, mingw).uniq)
+
+ bundle! "lock --remove-platform java"
+
+ lockfile = Bundler::LockfileParser.new(read_lockfile)
+ expect(lockfile.platforms).to match_array(local_platforms.unshift(mingw).uniq)
+ end
+
+ it "errors when removing all platforms" do
+ bundle "lock --remove-platform #{local_platforms.join(" ")}"
+ expect(last_command.bundler_err).to include("Removing all platforms from the bundle is not allowed")
+ end
+
+ # from https://github.com/bundler/bundler/issues/4896
+ it "properly adds platforms when platform requirements come from different dependencies" do
+ build_repo4 do
+ build_gem "ffi", "1.9.14"
+ build_gem "ffi", "1.9.14" do |s|
+ s.platform = mingw
+ end
+
+ build_gem "gssapi", "0.1"
+ build_gem "gssapi", "0.2"
+ build_gem "gssapi", "0.3"
+ build_gem "gssapi", "1.2.0" do |s|
+ s.add_dependency "ffi", ">= 1.0.1"
+ end
+
+ build_gem "mixlib-shellout", "2.2.6"
+ build_gem "mixlib-shellout", "2.2.6" do |s|
+ s.platform = "universal-mingw32"
+ s.add_dependency "win32-process", "~> 0.8.2"
+ end
+
+ # we need all these versions to get the sorting the same as it would be
+ # pulling from rubygems.org
+ %w[0.8.3 0.8.2 0.8.1 0.8.0].each do |v|
+ build_gem "win32-process", v do |s|
+ s.add_dependency "ffi", ">= 1.0.0"
+ end
+ end
+ end
+
+ gemfile <<-G
+ source "file://localhost#{gem_repo4}"
+
+ gem "mixlib-shellout"
+ gem "gssapi"
+ G
+
+ simulate_platform(mingw) { bundle! :lock }
+
+ expect(the_bundle.lockfile).to read_as(normalize_uri_file(strip_whitespace(<<-G)))
+ GEM
+ remote: file://localhost#{gem_repo4}/
+ specs:
+ ffi (1.9.14-x86-mingw32)
+ gssapi (1.2.0)
+ ffi (>= 1.0.1)
+ mixlib-shellout (2.2.6-universal-mingw32)
+ win32-process (~> 0.8.2)
+ win32-process (0.8.3)
+ ffi (>= 1.0.0)
+
+ PLATFORMS
+ x86-mingw32
+
+ DEPENDENCIES
+ gssapi
+ mixlib-shellout
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ simulate_platform(rb) { bundle! :lock }
+
+ expect(the_bundle.lockfile).to read_as(normalize_uri_file(strip_whitespace(<<-G)))
+ GEM
+ remote: file://localhost#{gem_repo4}/
+ specs:
+ ffi (1.9.14)
+ ffi (1.9.14-x86-mingw32)
+ gssapi (1.2.0)
+ ffi (>= 1.0.1)
+ mixlib-shellout (2.2.6)
+ mixlib-shellout (2.2.6-universal-mingw32)
+ win32-process (~> 0.8.2)
+ win32-process (0.8.3)
+ ffi (>= 1.0.0)
+
+ PLATFORMS
+ ruby
+ x86-mingw32
+
+ DEPENDENCIES
+ gssapi
+ mixlib-shellout
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ context "when an update is available" do
+ let(:repo) { gem_repo2 }
+
+ before do
+ lockfile(@lockfile)
+ build_repo2 do
+ build_gem "foo", "2.0"
+ end
+ end
+
+ it "does not implicitly update" do
+ bundle! "lock"
+
+ expect(read_lockfile).to eq(@lockfile)
+ end
+
+ it "accounts for changes in the gemfile" do
+ gemfile gemfile.gsub('"foo"', '"foo", "2.0"')
+ bundle! "lock"
+
+ expect(read_lockfile).to eq(@lockfile.sub("foo (1.0)", "foo (2.0)").sub(/foo$/, "foo (= 2.0)"))
+ end
+ end
+end
diff --git a/spec/bundler/commands/newgem_spec.rb b/spec/bundler/commands/newgem_spec.rb
new file mode 100644
index 0000000000..e6d6e19122
--- /dev/null
+++ b/spec/bundler/commands/newgem_spec.rb
@@ -0,0 +1,912 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle gem" do
+ def reset!
+ super
+ global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false"
+ end
+
+ def remove_push_guard(gem_name)
+ # Remove exception that prevents public pushes on older RubyGems versions
+ if Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.0")
+ path = "#{gem_name}/#{gem_name}.gemspec"
+ content = File.read(path).sub(/raise "RubyGems 2\.0 or newer.*/, "")
+ File.open(path, "w") {|f| f.write(content) }
+ end
+ end
+
+ def execute_bundle_gem(gem_name, flag = "", to_remove_push_guard = true)
+ bundle! "gem #{gem_name} #{flag}"
+ remove_push_guard(gem_name) if to_remove_push_guard
+ # reset gemspec cache for each test because of commit 3d4163a
+ Bundler.clear_gemspec_cache
+ end
+
+ def gem_skeleton_assertions(gem_name)
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec")).to exist
+ expect(bundled_app("#{gem_name}/README.md")).to exist
+ expect(bundled_app("#{gem_name}/Gemfile")).to exist
+ expect(bundled_app("#{gem_name}/Rakefile")).to exist
+ expect(bundled_app("#{gem_name}/lib/test/gem.rb")).to exist
+ expect(bundled_app("#{gem_name}/lib/test/gem/version.rb")).to exist
+ end
+
+ before do
+ git_config_content = <<-EOF
+ [user]
+ name = "Bundler User"
+ email = user@example.com
+ [github]
+ user = bundleuser
+ EOF
+ @git_config_location = ENV["GIT_CONFIG"]
+ path = "#{File.expand_path(tmp, File.dirname(__FILE__))}/test_git_config.txt"
+ File.open(path, "w") {|f| f.write(git_config_content) }
+ ENV["GIT_CONFIG"] = path
+ end
+
+ after do
+ FileUtils.rm(ENV["GIT_CONFIG"]) if File.exist?(ENV["GIT_CONFIG"])
+ ENV["GIT_CONFIG"] = @git_config_location
+ end
+
+ shared_examples_for "git config is present" do
+ context "git config user.{name,email} present" do
+ it "sets gemspec author to git user.name if available" do
+ expect(generated_gem.gemspec.authors.first).to eq("Bundler User")
+ end
+
+ it "sets gemspec email to git user.email if available" do
+ expect(generated_gem.gemspec.email.first).to eq("user@example.com")
+ end
+ end
+ end
+
+ shared_examples_for "git config is absent" do
+ it "sets gemspec author to default message if git user.name is not set or empty" do
+ expect(generated_gem.gemspec.authors.first).to eq("TODO: Write your name")
+ end
+
+ it "sets gemspec email to default message if git user.email is not set or empty" do
+ expect(generated_gem.gemspec.email.first).to eq("TODO: Write your email address")
+ end
+ end
+
+ shared_examples_for "--mit flag" do
+ before do
+ execute_bundle_gem(gem_name, "--mit")
+ end
+ it "generates a gem skeleton with MIT license" do
+ gem_skeleton_assertions(gem_name)
+ expect(bundled_app("test-gem/LICENSE.txt")).to exist
+ skel = Bundler::GemHelper.new(bundled_app(gem_name).to_s)
+ expect(skel.gemspec.license).to eq("MIT")
+ end
+ end
+
+ shared_examples_for "--no-mit flag" do
+ before do
+ execute_bundle_gem(gem_name, "--no-mit")
+ end
+ it "generates a gem skeleton without MIT license" do
+ gem_skeleton_assertions(gem_name)
+ expect(bundled_app("test-gem/LICENSE.txt")).to_not exist
+ end
+ end
+
+ shared_examples_for "--coc flag" do
+ before do
+ execute_bundle_gem(gem_name, "--coc", false)
+ end
+ it "generates a gem skeleton with MIT license" do
+ gem_skeleton_assertions(gem_name)
+ expect(bundled_app("test-gem/CODE_OF_CONDUCT.md")).to exist
+ end
+
+ describe "README additions" do
+ it "generates the README with a section for the Code of Conduct" do
+ expect(bundled_app("test-gem/README.md").read).to include("## Code of Conduct")
+ expect(bundled_app("test-gem/README.md").read).to include("https://github.com/bundleuser/#{gem_name}/blob/master/CODE_OF_CONDUCT.md")
+ end
+ end
+ end
+
+ shared_examples_for "--no-coc flag" do
+ before do
+ execute_bundle_gem(gem_name, "--no-coc", false)
+ end
+ it "generates a gem skeleton without Code of Conduct" do
+ gem_skeleton_assertions(gem_name)
+ expect(bundled_app("test-gem/CODE_OF_CONDUCT.md")).to_not exist
+ end
+
+ describe "README additions" do
+ it "generates the README without a section for the Code of Conduct" do
+ expect(bundled_app("test-gem/README.md").read).not_to include("## Code of Conduct")
+ expect(bundled_app("test-gem/README.md").read).not_to include("https://github.com/bundleuser/#{gem_name}/blob/master/CODE_OF_CONDUCT.md")
+ end
+ end
+ end
+
+ context "README.md" do
+ let(:gem_name) { "test_gem" }
+ let(:generated_gem) { Bundler::GemHelper.new(bundled_app(gem_name).to_s) }
+
+ context "git config github.user present" do
+ before do
+ execute_bundle_gem(gem_name)
+ end
+
+ it "contribute URL set to git username" do
+ expect(bundled_app("test_gem/README.md").read).not_to include("[USERNAME]")
+ expect(bundled_app("test_gem/README.md").read).to include("github.com/bundleuser")
+ end
+ end
+
+ context "git config github.user is absent" do
+ before do
+ sys_exec("git config --unset github.user")
+ reset!
+ in_app_root
+ bundle "gem #{gem_name}"
+ remove_push_guard(gem_name)
+ end
+
+ it "contribute URL set to [USERNAME]" do
+ expect(bundled_app("test_gem/README.md").read).to include("[USERNAME]")
+ expect(bundled_app("test_gem/README.md").read).not_to include("github.com/bundleuser")
+ end
+ end
+ end
+
+ it "creates a new git repository" do
+ in_app_root
+ bundle "gem test_gem"
+ expect(bundled_app("test_gem/.git")).to exist
+ end
+
+ context "when git is not available" do
+ let(:gem_name) { "test_gem" }
+
+ # This spec cannot have `git` available in the test env
+ before do
+ load_paths = [lib, spec]
+ load_path_str = "-I#{load_paths.join(File::PATH_SEPARATOR)}"
+
+ sys_exec "PATH=\"\" #{Gem.ruby} #{load_path_str} #{bindir.join("bundle")} gem #{gem_name}"
+ end
+
+ it "creates the gem without the need for git" do
+ expect(bundled_app("#{gem_name}/README.md")).to exist
+ end
+
+ it "doesn't create a git repo" do
+ expect(bundled_app("#{gem_name}/.git")).to_not exist
+ end
+
+ it "doesn't create a .gitignore file" do
+ expect(bundled_app("#{gem_name}/.gitignore")).to_not exist
+ end
+ end
+
+ it "generates a valid gemspec" do
+ in_app_root
+ bundle! "gem newgem --bin"
+
+ process_file(bundled_app("newgem", "newgem.gemspec")) do |line|
+ # Simulate replacing TODOs with real values
+ case line
+ when /spec\.metadata\["(?:allowed_push_host|homepage_uri|source_code_uri|changelog_uri)"\]/, /spec\.homepage/
+ line.gsub(/\=.*$/, "= 'http://example.org'")
+ when /spec\.summary/
+ line.gsub(/\=.*$/, "= %q{A short summary of my new gem.}")
+ when /spec\.description/
+ line.gsub(/\=.*$/, "= %q{A longer description of my new gem.}")
+ # Remove exception that prevents public pushes on older RubyGems versions
+ when /raise "RubyGems 2.0 or newer/
+ line.gsub(/.*/, "") if Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.0")
+ else
+ line
+ end
+ end
+
+ Dir.chdir(bundled_app("newgem")) do
+ gems = ["rake-10.0.2", :bundler]
+ # for Ruby core repository, Ruby 2.6+ has bundler as standard library.
+ gems.delete(:bundler) if ruby_core?
+ system_gems gems, :path => :bundle_path
+ bundle! "exec rake build"
+ end
+
+ expect(last_command.stdboth).not_to include("ERROR")
+ end
+
+ context "gem naming with relative paths" do
+ before do
+ reset!
+ in_app_root
+ end
+
+ it "resolves ." do
+ create_temporary_dir("tmp")
+
+ bundle "gem ."
+
+ expect(bundled_app("tmp/lib/tmp.rb")).to exist
+ end
+
+ it "resolves .." do
+ create_temporary_dir("temp/empty_dir")
+
+ bundle "gem .."
+
+ expect(bundled_app("temp/lib/temp.rb")).to exist
+ end
+
+ it "resolves relative directory" do
+ create_temporary_dir("tmp/empty/tmp")
+
+ bundle "gem ../../empty"
+
+ expect(bundled_app("tmp/empty/lib/empty.rb")).to exist
+ end
+
+ def create_temporary_dir(dir)
+ FileUtils.mkdir_p(dir)
+ Dir.chdir(dir)
+ end
+ end
+
+ context "gem naming with underscore" do
+ let(:gem_name) { "test_gem" }
+
+ before do
+ execute_bundle_gem(gem_name)
+ end
+
+ let(:generated_gem) { Bundler::GemHelper.new(bundled_app(gem_name).to_s) }
+
+ it "generates a gem skeleton" do
+ expect(bundled_app("test_gem/test_gem.gemspec")).to exist
+ expect(bundled_app("test_gem/Gemfile")).to exist
+ expect(bundled_app("test_gem/Rakefile")).to exist
+ expect(bundled_app("test_gem/lib/test_gem.rb")).to exist
+ expect(bundled_app("test_gem/lib/test_gem/version.rb")).to exist
+ expect(bundled_app("test_gem/.gitignore")).to exist
+
+ expect(bundled_app("test_gem/bin/setup")).to exist
+ expect(bundled_app("test_gem/bin/console")).to exist
+ expect(bundled_app("test_gem/bin/setup")).to be_executable
+ expect(bundled_app("test_gem/bin/console")).to be_executable
+ end
+
+ it "starts with version 0.1.0" do
+ expect(bundled_app("test_gem/lib/test_gem/version.rb").read).to match(/VERSION = "0.1.0"/)
+ end
+
+ it "does not nest constants" do
+ expect(bundled_app("test_gem/lib/test_gem/version.rb").read).to match(/module TestGem/)
+ expect(bundled_app("test_gem/lib/test_gem.rb").read).to match(/module TestGem/)
+ end
+
+ it_should_behave_like "git config is present"
+
+ context "git config user.{name,email} is not set" do
+ before do
+ `git config --unset user.name`
+ `git config --unset user.email`
+ reset!
+ in_app_root
+ bundle "gem #{gem_name}"
+ remove_push_guard(gem_name)
+ end
+
+ it_should_behave_like "git config is absent"
+ end
+
+ it "sets gemspec metadata['allowed_push_host']", :rubygems => "2.0" do
+ expect(generated_gem.gemspec.metadata["allowed_push_host"]).
+ to match(/mygemserver\.com/)
+ end
+
+ it "requires the version file" do
+ expect(bundled_app("test_gem/lib/test_gem.rb").read).to match(%r{require "test_gem/version"})
+ end
+
+ it "creates a base error class" do
+ expect(bundled_app("test_gem/lib/test_gem.rb").read).to match(/class Error < StandardError; end$/)
+ end
+
+ it "runs rake without problems" do
+ system_gems ["rake-10.0.2"]
+
+ rakefile = strip_whitespace <<-RAKEFILE
+ task :default do
+ puts 'SUCCESS'
+ end
+ RAKEFILE
+ File.open(bundled_app("test_gem/Rakefile"), "w") do |file|
+ file.puts rakefile
+ end
+
+ Dir.chdir(bundled_app(gem_name)) do
+ sys_exec(rake)
+ expect(out).to include("SUCCESS")
+ end
+ end
+
+ context "--exe parameter set" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --exe"
+ end
+
+ it "builds exe skeleton" do
+ expect(bundled_app("test_gem/exe/test_gem")).to exist
+ end
+
+ it "requires 'test-gem'" do
+ expect(bundled_app("test_gem/exe/test_gem").read).to match(/require "test_gem"/)
+ end
+ end
+
+ context "--bin parameter set" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --bin"
+ end
+
+ it "builds exe skeleton" do
+ expect(bundled_app("test_gem/exe/test_gem")).to exist
+ end
+
+ it "requires 'test-gem'" do
+ expect(bundled_app("test_gem/exe/test_gem").read).to match(/require "test_gem"/)
+ end
+ end
+
+ context "no --test parameter" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name}"
+ end
+
+ it "doesn't create any spec/test file" do
+ expect(bundled_app("test_gem/.rspec")).to_not exist
+ expect(bundled_app("test_gem/spec/test_gem_spec.rb")).to_not exist
+ expect(bundled_app("test_gem/spec/spec_helper.rb")).to_not exist
+ expect(bundled_app("test_gem/test/test_test_gem.rb")).to_not exist
+ expect(bundled_app("test_gem/test/minitest_helper.rb")).to_not exist
+ end
+ end
+
+ context "--test parameter set to rspec" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --test=rspec"
+ end
+
+ it "builds spec skeleton" do
+ expect(bundled_app("test_gem/.rspec")).to exist
+ expect(bundled_app("test_gem/spec/test_gem_spec.rb")).to exist
+ expect(bundled_app("test_gem/spec/spec_helper.rb")).to exist
+ end
+
+ it "depends on a specific version of rspec", :rubygems => ">= 1.8.1" do
+ remove_push_guard(gem_name)
+ rspec_dep = generated_gem.gemspec.development_dependencies.find {|d| d.name == "rspec" }
+ expect(rspec_dep).to be_specific
+ end
+
+ it "requires 'test-gem'" do
+ expect(bundled_app("test_gem/spec/spec_helper.rb").read).to include(%(require "test_gem"))
+ end
+
+ it "creates a default test which fails" do
+ expect(bundled_app("test_gem/spec/test_gem_spec.rb").read).to include("expect(false).to eq(true)")
+ end
+ end
+
+ context "gem.test setting set to rspec" do
+ before do
+ reset!
+ in_app_root
+ bundle "config gem.test rspec"
+ bundle "gem #{gem_name}"
+ end
+
+ it "builds spec skeleton" do
+ expect(bundled_app("test_gem/.rspec")).to exist
+ expect(bundled_app("test_gem/spec/test_gem_spec.rb")).to exist
+ expect(bundled_app("test_gem/spec/spec_helper.rb")).to exist
+ end
+ end
+
+ context "gem.test setting set to rspec and --test is set to minitest" do
+ before do
+ reset!
+ in_app_root
+ bundle "config gem.test rspec"
+ bundle "gem #{gem_name} --test=minitest"
+ end
+
+ it "builds spec skeleton" do
+ expect(bundled_app("test_gem/test/test_gem_test.rb")).to exist
+ expect(bundled_app("test_gem/test/test_helper.rb")).to exist
+ end
+ end
+
+ context "--test parameter set to minitest" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --test=minitest"
+ end
+
+ it "depends on a specific version of minitest", :rubygems => ">= 1.8.1" do
+ remove_push_guard(gem_name)
+ rspec_dep = generated_gem.gemspec.development_dependencies.find {|d| d.name == "minitest" }
+ expect(rspec_dep).to be_specific
+ end
+
+ it "builds spec skeleton" do
+ expect(bundled_app("test_gem/test/test_gem_test.rb")).to exist
+ expect(bundled_app("test_gem/test/test_helper.rb")).to exist
+ end
+
+ it "requires 'test-gem'" do
+ expect(bundled_app("test_gem/test/test_helper.rb").read).to include(%(require "test_gem"))
+ end
+
+ it "requires 'minitest_helper'" do
+ expect(bundled_app("test_gem/test/test_gem_test.rb").read).to include(%(require "test_helper"))
+ end
+
+ it "creates a default test which fails" do
+ expect(bundled_app("test_gem/test/test_gem_test.rb").read).to include("assert false")
+ end
+ end
+
+ context "gem.test setting set to minitest" do
+ before do
+ reset!
+ in_app_root
+ bundle "config gem.test minitest"
+ bundle "gem #{gem_name}"
+ end
+
+ it "creates a default rake task to run the test suite" do
+ rakefile = strip_whitespace <<-RAKEFILE
+ require "bundler/gem_tasks"
+ require "rake/testtask"
+
+ Rake::TestTask.new(:test) do |t|
+ t.libs << "test"
+ t.libs << "lib"
+ t.test_files = FileList["test/**/*_test.rb"]
+ end
+
+ task :default => :test
+ RAKEFILE
+
+ expect(bundled_app("test_gem/Rakefile").read).to eq(rakefile)
+ end
+ end
+
+ context "--test with no arguments" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --test"
+ end
+
+ it "defaults to rspec" do
+ expect(bundled_app("test_gem/spec/spec_helper.rb")).to exist
+ expect(bundled_app("test_gem/test/minitest_helper.rb")).to_not exist
+ end
+
+ it "creates a .travis.yml file to test the library against the current Ruby version on Travis CI" do
+ expect(bundled_app("test_gem/.travis.yml").read).to match(/- #{RUBY_VERSION}/)
+ end
+ end
+
+ context "--edit option" do
+ it "opens the generated gemspec in the user's text editor" do
+ reset!
+ in_app_root
+ output = bundle "gem #{gem_name} --edit=echo"
+ gemspec_path = File.join(Dir.pwd, gem_name, "#{gem_name}.gemspec")
+ expect(output).to include("echo \"#{gemspec_path}\"")
+ end
+ end
+ end
+
+ context "testing --mit and --coc options against bundle config settings" do
+ let(:gem_name) { "test-gem" }
+
+ context "with mit option in bundle config settings set to true" do
+ before do
+ global_config "BUNDLE_GEM__MIT" => "true", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false"
+ end
+ after { reset! }
+ it_behaves_like "--mit flag"
+ it_behaves_like "--no-mit flag"
+ end
+
+ context "with mit option in bundle config settings set to false" do
+ it_behaves_like "--mit flag"
+ it_behaves_like "--no-mit flag"
+ end
+
+ context "with coc option in bundle config settings set to true" do
+ before do
+ global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "true"
+ end
+ after { reset! }
+ it_behaves_like "--coc flag"
+ it_behaves_like "--no-coc flag"
+ end
+
+ context "with coc option in bundle config settings set to false" do
+ it_behaves_like "--coc flag"
+ it_behaves_like "--no-coc flag"
+ end
+ end
+
+ context "gem naming with dashed" do
+ let(:gem_name) { "test-gem" }
+
+ before do
+ execute_bundle_gem(gem_name)
+ end
+
+ let(:generated_gem) { Bundler::GemHelper.new(bundled_app(gem_name).to_s) }
+
+ it "generates a gem skeleton" do
+ expect(bundled_app("test-gem/test-gem.gemspec")).to exist
+ expect(bundled_app("test-gem/Gemfile")).to exist
+ expect(bundled_app("test-gem/Rakefile")).to exist
+ expect(bundled_app("test-gem/lib/test/gem.rb")).to exist
+ expect(bundled_app("test-gem/lib/test/gem/version.rb")).to exist
+ end
+
+ it "starts with version 0.1.0" do
+ expect(bundled_app("test-gem/lib/test/gem/version.rb").read).to match(/VERSION = "0.1.0"/)
+ end
+
+ it "nests constants so they work" do
+ expect(bundled_app("test-gem/lib/test/gem/version.rb").read).to match(/module Test\n module Gem/)
+ expect(bundled_app("test-gem/lib/test/gem.rb").read).to match(/module Test\n module Gem/)
+ end
+
+ it_should_behave_like "git config is present"
+
+ context "git config user.{name,email} is not set" do
+ before do
+ `git config --unset user.name`
+ `git config --unset user.email`
+ reset!
+ in_app_root
+ bundle "gem #{gem_name}"
+ remove_push_guard(gem_name)
+ end
+
+ it_should_behave_like "git config is absent"
+ end
+
+ it "requires the version file" do
+ expect(bundled_app("test-gem/lib/test/gem.rb").read).to match(%r{require "test/gem/version"})
+ end
+
+ it "runs rake without problems" do
+ system_gems ["rake-10.0.2"]
+
+ rakefile = strip_whitespace <<-RAKEFILE
+ task :default do
+ puts 'SUCCESS'
+ end
+ RAKEFILE
+ File.open(bundled_app("test-gem/Rakefile"), "w") do |file|
+ file.puts rakefile
+ end
+
+ Dir.chdir(bundled_app(gem_name)) do
+ sys_exec(rake)
+ expect(out).to include("SUCCESS")
+ end
+ end
+
+ context "--bin parameter set" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --bin"
+ end
+
+ it "builds bin skeleton" do
+ expect(bundled_app("test-gem/exe/test-gem")).to exist
+ end
+
+ it "requires 'test/gem'" do
+ expect(bundled_app("test-gem/exe/test-gem").read).to match(%r{require "test/gem"})
+ end
+ end
+
+ context "no --test parameter" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name}"
+ end
+
+ it "doesn't create any spec/test file" do
+ expect(bundled_app("test-gem/.rspec")).to_not exist
+ expect(bundled_app("test-gem/spec/test/gem_spec.rb")).to_not exist
+ expect(bundled_app("test-gem/spec/spec_helper.rb")).to_not exist
+ expect(bundled_app("test-gem/test/test_test/gem.rb")).to_not exist
+ expect(bundled_app("test-gem/test/minitest_helper.rb")).to_not exist
+ end
+ end
+
+ context "--test parameter set to rspec" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --test=rspec"
+ end
+
+ it "builds spec skeleton" do
+ expect(bundled_app("test-gem/.rspec")).to exist
+ expect(bundled_app("test-gem/spec/test/gem_spec.rb")).to exist
+ expect(bundled_app("test-gem/spec/spec_helper.rb")).to exist
+ end
+
+ it "requires 'test/gem'" do
+ expect(bundled_app("test-gem/spec/spec_helper.rb").read).to include(%(require "test/gem"))
+ end
+
+ it "creates a default test which fails" do
+ expect(bundled_app("test-gem/spec/test/gem_spec.rb").read).to include("expect(false).to eq(true)")
+ end
+
+ it "creates a default rake task to run the specs" do
+ rakefile = strip_whitespace <<-RAKEFILE
+ require "bundler/gem_tasks"
+ require "rspec/core/rake_task"
+
+ RSpec::Core::RakeTask.new(:spec)
+
+ task :default => :spec
+ RAKEFILE
+
+ expect(bundled_app("test-gem/Rakefile").read).to eq(rakefile)
+ end
+ end
+
+ context "--test parameter set to minitest" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --test=minitest"
+ end
+
+ it "builds spec skeleton" do
+ expect(bundled_app("test-gem/test/test/gem_test.rb")).to exist
+ expect(bundled_app("test-gem/test/test_helper.rb")).to exist
+ end
+
+ it "requires 'test/gem'" do
+ expect(bundled_app("test-gem/test/test_helper.rb").read).to match(%r{require "test/gem"})
+ end
+
+ it "requires 'test_helper'" do
+ expect(bundled_app("test-gem/test/test/gem_test.rb").read).to match(/require "test_helper"/)
+ end
+
+ it "creates a default test which fails" do
+ expect(bundled_app("test-gem/test/test/gem_test.rb").read).to match(/assert false/)
+ end
+
+ it "creates a default rake task to run the test suite" do
+ rakefile = strip_whitespace <<-RAKEFILE
+ require "bundler/gem_tasks"
+ require "rake/testtask"
+
+ Rake::TestTask.new(:test) do |t|
+ t.libs << "test"
+ t.libs << "lib"
+ t.test_files = FileList["test/**/*_test.rb"]
+ end
+
+ task :default => :test
+ RAKEFILE
+
+ expect(bundled_app("test-gem/Rakefile").read).to eq(rakefile)
+ end
+ end
+
+ context "--test with no arguments" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem #{gem_name} --test"
+ end
+
+ it "defaults to rspec" do
+ expect(bundled_app("test-gem/spec/spec_helper.rb")).to exist
+ expect(bundled_app("test-gem/test/minitest_helper.rb")).to_not exist
+ end
+ end
+
+ context "--ext parameter set" do
+ before do
+ reset!
+ in_app_root
+ bundle "gem test_gem --ext"
+ end
+
+ it "builds ext skeleton" do
+ expect(bundled_app("test_gem/ext/test_gem/extconf.rb")).to exist
+ expect(bundled_app("test_gem/ext/test_gem/test_gem.h")).to exist
+ expect(bundled_app("test_gem/ext/test_gem/test_gem.c")).to exist
+ end
+
+ it "includes rake-compiler" do
+ expect(bundled_app("test_gem/test_gem.gemspec").read).to include('spec.add_development_dependency "rake-compiler"')
+ end
+
+ it "depends on compile task for build" do
+ rakefile = strip_whitespace <<-RAKEFILE
+ require "bundler/gem_tasks"
+ require "rake/extensiontask"
+
+ task :build => :compile
+
+ Rake::ExtensionTask.new("test_gem") do |ext|
+ ext.lib_dir = "lib/test_gem"
+ end
+
+ task :default => [:clobber, :compile, :spec]
+ RAKEFILE
+
+ expect(bundled_app("test_gem/Rakefile").read).to eq(rakefile)
+ end
+ end
+ end
+
+ describe "uncommon gem names" do
+ it "can deal with two dashes" do
+ bundle "gem a--a"
+ Bundler.clear_gemspec_cache
+
+ expect(bundled_app("a--a/a--a.gemspec")).to exist
+ end
+
+ it "fails gracefully with a ." do
+ bundle "gem foo.gemspec"
+ expect(last_command.bundler_err).to end_with("Invalid gem name foo.gemspec -- `Foo.gemspec` is an invalid constant name")
+ end
+
+ it "fails gracefully with a ^" do
+ bundle "gem ^"
+ expect(last_command.bundler_err).to end_with("Invalid gem name ^ -- `^` is an invalid constant name")
+ end
+
+ it "fails gracefully with a space" do
+ bundle "gem 'foo bar'"
+ expect(last_command.bundler_err).to end_with("Invalid gem name foo bar -- `Foo bar` is an invalid constant name")
+ end
+
+ it "fails gracefully when multiple names are passed" do
+ bundle "gem foo bar baz"
+ expect(last_command.bundler_err).to eq(<<-E.strip)
+ERROR: "bundle gem" was called with arguments ["foo", "bar", "baz"]
+Usage: "bundle gem NAME [OPTIONS]"
+ E
+ end
+ end
+
+ describe "#ensure_safe_gem_name" do
+ before do
+ bundle "gem #{subject}"
+ end
+ after do
+ Bundler.clear_gemspec_cache
+ end
+
+ context "with an existing const name" do
+ subject { "gem" }
+ it { expect(out).to include("Invalid gem name #{subject}") }
+ end
+
+ context "with an existing hyphenated const name" do
+ subject { "gem-specification" }
+ it { expect(out).to include("Invalid gem name #{subject}") }
+ end
+
+ context "starting with an existing const name" do
+ subject { "gem-somenewconstantname" }
+ it { expect(out).not_to include("Invalid gem name #{subject}") }
+ end
+
+ context "ending with an existing const name" do
+ subject { "somenewconstantname-gem" }
+ it { expect(out).not_to include("Invalid gem name #{subject}") }
+ end
+ end
+
+ context "on first run" do
+ before do
+ in_app_root
+ end
+
+ it "asks about test framework" do
+ global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__COC" => "false"
+
+ bundle "gem foobar" do |input, _, _|
+ input.puts "rspec"
+ end
+
+ expect(bundled_app("foobar/spec/spec_helper.rb")).to exist
+ rakefile = strip_whitespace <<-RAKEFILE
+ require "bundler/gem_tasks"
+ require "rspec/core/rake_task"
+
+ RSpec::Core::RakeTask.new(:spec)
+
+ task :default => :spec
+ RAKEFILE
+
+ expect(bundled_app("foobar/Rakefile").read).to eq(rakefile)
+ expect(bundled_app("foobar/foobar.gemspec").read).to include('spec.add_development_dependency "rspec"')
+ end
+
+ it "asks about MIT license" do
+ global_config "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false"
+
+ bundle :config
+
+ bundle "gem foobar" do |input, _, _|
+ input.puts "yes"
+ end
+
+ expect(bundled_app("foobar/LICENSE.txt")).to exist
+ end
+
+ it "asks about CoC" do
+ global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false"
+
+ bundle "gem foobar" do |input, _, _|
+ input.puts "yes"
+ end
+
+ expect(bundled_app("foobar/CODE_OF_CONDUCT.md")).to exist
+ end
+ end
+
+ context "on conflicts with a previously created file" do
+ it "should fail gracefully" do
+ in_app_root do
+ FileUtils.touch("conflict-foobar")
+ end
+ bundle "gem conflict-foobar"
+ expect(last_command.bundler_err).to include("Errno::ENOTDIR")
+ expect(exitstatus).to eql(32) if exitstatus
+ end
+ end
+
+ context "on conflicts with a previously created directory" do
+ it "should succeed" do
+ in_app_root do
+ FileUtils.mkdir_p("conflict-foobar/Gemfile")
+ end
+ bundle! "gem conflict-foobar"
+ expect(last_command.stdout).to include("file_clash conflict-foobar/Gemfile").
+ and include "Initializing git repo in #{bundled_app("conflict-foobar")}"
+ end
+ end
+end
diff --git a/spec/bundler/commands/open_spec.rb b/spec/bundler/commands/open_spec.rb
new file mode 100644
index 0000000000..5cab846fb5
--- /dev/null
+++ b/spec/bundler/commands/open_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle open" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ end
+
+ it "opens the gem with BUNDLER_EDITOR as highest priority" do
+ bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ expect(out).to include("bundler_editor #{default_bundle_path("gems", "rails-2.3.2")}")
+ end
+
+ it "opens the gem with VISUAL as 2nd highest priority" do
+ bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("visual #{default_bundle_path("gems", "rails-2.3.2")}")
+ end
+
+ it "opens the gem with EDITOR as 3rd highest priority" do
+ bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "rails-2.3.2")}")
+ end
+
+ it "complains if no EDITOR is set" do
+ bundle "open rails", :env => { "EDITOR" => "", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to eq("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR")
+ end
+
+ it "complains if gem not in bundle" do
+ bundle "open missing", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to match(/could not find gem 'missing'/i)
+ end
+
+ 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)
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle "open foo", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to match("editor #{default_bundle_path.join("bundler/gems/foo-1.0-#{ref}")}")
+ end
+
+ it "suggests alternatives for similar-sounding gems" do
+ bundle "open Rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to match(/did you mean rails\?/i)
+ end
+
+ it "opens the gem with short words" do
+ bundle "open rec", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+
+ expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2")}")
+ end
+
+ it "select the gem from many match gems" do
+ env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle "open active", :env => env do |input, _, _|
+ input.puts "2"
+ end
+
+ expect(out).to match(/bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}\z/)
+ end
+
+ it "allows selecting exit from many match gems" do
+ env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle! "open active", :env => env do |input, _, _|
+ input.puts "0"
+ end
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("Installing foo 1.0")
+ end
+
+ it "opens the editor with a clean env" do
+ bundle "open", :env => { "EDITOR" => "sh -c 'env'", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).not_to include("BUNDLE_GEMFILE=")
+ end
+end
diff --git a/spec/bundler/commands/outdated_spec.rb b/spec/bundler/commands/outdated_spec.rb
new file mode 100644
index 0000000000..fc1f1772e7
--- /dev/null
+++ b/spec/bundler/commands/outdated_spec.rb
@@ -0,0 +1,782 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle outdated" do
+ before :each do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
+ end
+
+ describe "with no arguments" do
+ it "returns a sorted list of outdated gems" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ build_gem "weakling", "0.2"
+ update_git "foo", :path => lib_path("foo")
+ update_git "zebra", :path => lib_path("zebra")
+ end
+
+ bundle "outdated"
+
+ expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5)")
+ expect(out).to include("weakling (newest 0.2, installed 0.0.3, requested ~> 0.0.1)")
+ expect(out).to include("foo (newest 1.0")
+
+ # Gem names are one per-line, between "*" and their parenthesized version.
+ gem_list = out.split("\n").map {|g| g[/\* (.*) \(/, 1] }.compact
+ expect(gem_list).to eq(gem_list.sort)
+ end
+
+ it "returns non zero exit status if outdated gems present" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ bundle "outdated"
+
+ expect(exitstatus).to_not be_zero if exitstatus
+ end
+
+ it "returns success exit status if no outdated gems present" do
+ bundle "outdated"
+
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "adds gem group to dependency output when repo is updated" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ group :development, :test do
+ gem 'activesupport', '2.3.5'
+ end
+ G
+
+ update_repo2 { build_gem "activesupport", "3.0" }
+
+ bundle "outdated --verbose"
+ expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5) in groups \"development, test\"")
+ end
+ end
+
+ describe "with --group option" do
+ def test_group_option(group = nil, gems_list_size = 1)
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "weakling", "~> 0.0.1"
+ gem "terranova", '8'
+ group :development, :test do
+ gem "duradura", '7.0'
+ gem 'activesupport', '2.3.5'
+ end
+ G
+
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ build_gem "terranova", "9"
+ build_gem "duradura", "8.0"
+ end
+
+ bundle "outdated --group #{group}"
+
+ # Gem names are one per-line, between "*" and their parenthesized version.
+ gem_list = out.split("\n").map {|g| g[/\* (.*) \(/, 1] }.compact
+ expect(gem_list).to eq(gem_list.sort)
+ expect(gem_list.size).to eq gems_list_size
+ end
+
+ it "not outdated gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "weakling", "~> 0.0.1"
+ gem "terranova", '8'
+ group :development, :test do
+ gem 'activesupport', '2.3.5'
+ gem "duradura", '7.0'
+ end
+ G
+
+ bundle "outdated --group"
+ expect(out).to include("Bundle up to date!")
+ end
+
+ it "returns a sorted list of outdated gems from one group => 'default'" do
+ test_group_option("default")
+
+ expect(out).to include("===== Group default =====")
+ expect(out).to include("terranova (")
+
+ expect(out).not_to include("===== Group development, test =====")
+ expect(out).not_to include("activesupport")
+ expect(out).not_to include("duradura")
+ end
+
+ it "returns a sorted list of outdated gems from one group => 'development'" do
+ test_group_option("development", 2)
+
+ expect(out).not_to include("===== Group default =====")
+ expect(out).not_to include("terranova (")
+
+ expect(out).to include("===== Group development, test =====")
+ expect(out).to include("activesupport")
+ expect(out).to include("duradura")
+ end
+ end
+
+ describe "with --groups option" do
+ it "not outdated gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "weakling", "~> 0.0.1"
+ gem "terranova", '8'
+ group :development, :test do
+ gem 'activesupport', '2.3.5'
+ gem "duradura", '7.0'
+ end
+ G
+
+ bundle "outdated --groups"
+ expect(out).to include("Bundle up to date!")
+ end
+
+ it "returns a sorted list of outdated gems by groups" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "weakling", "~> 0.0.1"
+ gem "terranova", '8'
+ group :development, :test do
+ gem 'activesupport', '2.3.5'
+ gem "duradura", '7.0'
+ end
+ G
+
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ build_gem "terranova", "9"
+ build_gem "duradura", "8.0"
+ end
+
+ bundle "outdated --groups"
+ expect(out).to include("===== Group default =====")
+ expect(out).to include("terranova (newest 9, installed 8, requested = 8)")
+ expect(out).to include("===== Group development, test =====")
+ expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5)")
+ expect(out).to include("duradura (newest 8.0, installed 7.0, requested = 7.0)")
+
+ expect(out).not_to include("weakling (")
+
+ # TODO: check gems order inside the group
+ end
+ end
+
+ describe "with --local option" do
+ it "uses local cache to return a list of outdated gems" do
+ update_repo2 do
+ build_gem "activesupport", "2.3.4"
+ end
+
+ bundle! "config clean false"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.4"
+ G
+
+ bundle "outdated --local"
+
+ expect(out).to include("activesupport (newest 2.3.5, installed 2.3.4, requested = 2.3.4)")
+ end
+
+ it "doesn't hit repo2" do
+ FileUtils.rm_rf(gem_repo2)
+
+ bundle "outdated --local"
+ expect(out).not_to match(/Fetching (gem|version|dependency) metadata from/)
+ end
+ end
+
+ shared_examples_for "a minimal output is desired" do
+ context "and gems are outdated" do
+ before do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ build_gem "weakling", "0.2"
+ end
+ end
+
+ it "outputs a sorted list of outdated gems with a more minimal format" do
+ minimal_output = "activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5)\n" \
+ "weakling (newest 0.2, installed 0.0.3, requested ~> 0.0.1)"
+ subject
+ expect(out).to eq(minimal_output)
+ end
+ end
+
+ context "and no gems are outdated" do
+ it "has empty output" do
+ subject
+ expect(out).to eq("")
+ end
+ end
+ end
+
+ describe "with --parseable option" do
+ subject { bundle "outdated --parseable" }
+
+ it_behaves_like "a minimal output is desired"
+ end
+
+ describe "with aliased --porcelain option" do
+ subject { bundle "outdated --porcelain" }
+
+ it_behaves_like "a minimal output is desired"
+ end
+
+ describe "with specified gems" do
+ it "returns list of outdated gems" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ bundle "outdated foo"
+ expect(out).not_to include("activesupport (newest")
+ expect(out).to include("foo (newest 1.0")
+ end
+ end
+
+ describe "pre-release gems" do
+ context "without the --pre option" do
+ it "ignores pre-release versions" do
+ update_repo2 do
+ build_gem "activesupport", "3.0.0.beta"
+ end
+
+ bundle "outdated"
+ expect(out).not_to include("activesupport (3.0.0.beta > 2.3.5)")
+ end
+ end
+
+ context "with the --pre option" do
+ it "includes pre-release versions" do
+ update_repo2 do
+ build_gem "activesupport", "3.0.0.beta"
+ end
+
+ bundle "outdated --pre"
+ expect(out).to include("activesupport (newest 3.0.0.beta, installed 2.3.5, requested = 2.3.5)")
+ end
+ end
+
+ context "when current gem is a pre-release" do
+ it "includes the gem" do
+ update_repo2 do
+ build_gem "activesupport", "3.0.0.beta.1"
+ build_gem "activesupport", "3.0.0.beta.2"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "3.0.0.beta.1"
+ G
+
+ bundle "outdated"
+ expect(out).to include("(newest 3.0.0.beta.2, installed 3.0.0.beta.1, requested = 3.0.0.beta.1)")
+ end
+ end
+ end
+
+ describe "with --strict option" do
+ it "only reports gems that have a newer version that matches the specified dependency version requirements" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ build_gem "weakling", "0.0.5"
+ end
+
+ bundle "outdated --strict"
+
+ expect(out).to_not include("activesupport (newest")
+ expect(out).to include("(newest 0.0.5, installed 0.0.3, requested ~> 0.0.1)")
+ end
+
+ it "only reports gem dependencies when they can actually be updated" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack_middleware", "1.0"
+ G
+
+ bundle "outdated --strict"
+
+ expect(out).to_not include("rack (1.2")
+ end
+
+ describe "and filter options" do
+ it "only reports gems that match requirement and patch filter level" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "~> 2.3"
+ gem "weakling", ">= 0.0.1"
+ G
+
+ update_repo2 do
+ build_gem "activesupport", %w[2.4.0 3.0.0]
+ build_gem "weakling", "0.0.5"
+ end
+
+ bundle "outdated --strict --filter-patch"
+
+ expect(out).to_not include("activesupport (newest")
+ expect(out).to include("(newest 0.0.5, installed 0.0.3")
+ end
+
+ it "only reports gems that match requirement and minor filter level" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "~> 2.3"
+ gem "weakling", ">= 0.0.1"
+ G
+
+ update_repo2 do
+ build_gem "activesupport", %w[2.3.9]
+ build_gem "weakling", "0.1.5"
+ end
+
+ bundle "outdated --strict --filter-minor"
+
+ expect(out).to_not include("activesupport (newest")
+ expect(out).to include("(newest 0.1.5, installed 0.0.3")
+ end
+
+ it "only reports gems that match requirement and major filter level" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "~> 2.3"
+ gem "weakling", ">= 0.0.1"
+ G
+
+ update_repo2 do
+ build_gem "activesupport", %w[2.4.0 2.5.0]
+ build_gem "weakling", "1.1.5"
+ end
+
+ bundle "outdated --strict --filter-major"
+
+ expect(out).to_not include("activesupport (newest")
+ expect(out).to include("(newest 1.1.5, installed 0.0.3")
+ end
+ end
+ end
+
+ describe "with invalid gem name" do
+ it "returns could not find gem name" do
+ bundle "outdated invalid_gem_name"
+ expect(out).to include("Could not find gem 'invalid_gem_name'.")
+ end
+
+ it "returns non-zero exit code" do
+ bundle "outdated invalid_gem_name"
+ expect(exitstatus).to_not be_zero if exitstatus
+ end
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle :outdated
+ expect(out).to include("Installing foo 1.0")
+ end
+
+ context "after bundle install --deployment", :bundler => "< 2" do
+ before do
+ install_gemfile <<-G, forgotten_command_line_options(:deployment => true)
+ source "file://#{gem_repo2}"
+
+ gem "rack"
+ gem "foo"
+ G
+ end
+
+ it "outputs a helpful message about being in deployment mode" do
+ update_repo2 { build_gem "activesupport", "3.0" }
+
+ bundle "outdated"
+ expect(last_command).to be_failure
+ expect(out).to include("You are trying to check outdated gems in deployment mode.")
+ expect(out).to include("Run `bundle outdated` elsewhere.")
+ expect(out).to include("If this is a development machine, remove the ")
+ expect(out).to include("Gemfile freeze\nby running `bundle install --no-deployment`.")
+ end
+ end
+
+ context "after bundle config deployment true" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "rack"
+ gem "foo"
+ G
+ bundle! "config deployment true"
+ end
+
+ it "outputs a helpful message about being in deployment mode" do
+ update_repo2 { build_gem "activesupport", "3.0" }
+
+ bundle "outdated"
+ expect(last_command).to be_failure
+ expect(out).to include("You are trying to check outdated gems in deployment mode.")
+ expect(out).to include("Run `bundle outdated` elsewhere.")
+ expect(out).to include("If this is a development machine, remove the ")
+ expect(out).to include("Gemfile freeze\nby running `bundle config --delete deployment`.")
+ end
+ end
+
+ context "update available for a gem on a different platform" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "laduradura", '= 5.15.2'
+ G
+ end
+
+ it "reports that no updates are available" do
+ bundle "outdated"
+ expect(out).to include("Bundle up to date!")
+ end
+ end
+
+ context "update available for a gem on the same platform while multiple platforms used for gem" do
+ it "reports that updates are available if the Ruby platform is used" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "laduradura", '= 5.15.2', :platforms => [:ruby, :jruby]
+ G
+
+ bundle "outdated"
+ expect(out).to include("Bundle up to date!")
+ end
+
+ it "reports that updates are available if the JRuby platform is used" do
+ simulate_ruby_engine "jruby", "1.6.7" do
+ simulate_platform "jruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "laduradura", '= 5.15.2', :platforms => [:ruby, :jruby]
+ G
+
+ bundle "outdated"
+ expect(out).to include("Outdated gems included in the bundle:")
+ expect(out).to include("laduradura (newest 5.15.3, installed 5.15.2, requested = 5.15.2)")
+ end
+ end
+ end
+ end
+
+ shared_examples_for "version update is detected" do
+ it "reports that a gem has a newer version" do
+ subject
+ expect(out).to include("Outdated gems included in the bundle:")
+ expect(out).to include("activesupport (newest")
+ expect(out).to_not include("ERROR REPORT TEMPLATE")
+ end
+ end
+
+ shared_examples_for "major version updates are detected" do
+ before do
+ update_repo2 do
+ build_gem "activesupport", "3.3.5"
+ build_gem "weakling", "0.8.0"
+ end
+ end
+
+ it_behaves_like "version update is detected"
+ end
+
+ context "when on a new machine" do
+ before do
+ simulate_new_machine
+
+ update_git "foo", :path => lib_path("foo")
+ update_repo2 do
+ build_gem "activesupport", "3.3.5"
+ build_gem "weakling", "0.8.0"
+ end
+ end
+
+ subject { bundle "outdated" }
+ it_behaves_like "version update is detected"
+ end
+
+ shared_examples_for "minor version updates are detected" do
+ before do
+ update_repo2 do
+ build_gem "activesupport", "2.7.5"
+ build_gem "weakling", "2.0.1"
+ end
+ end
+
+ it_behaves_like "version update is detected"
+ end
+
+ shared_examples_for "patch version updates are detected" do
+ before do
+ update_repo2 do
+ build_gem "activesupport", "2.3.7"
+ build_gem "weakling", "0.3.1"
+ end
+ end
+
+ it_behaves_like "version update is detected"
+ end
+
+ shared_examples_for "no version updates are detected" do
+ it "does not detect any version updates" do
+ subject
+ expect(out).to include("updates to display.")
+ expect(out).to_not include("ERROR REPORT TEMPLATE")
+ expect(out).to_not include("activesupport (newest")
+ expect(out).to_not include("weakling (newest")
+ end
+ end
+
+ shared_examples_for "major version is ignored" do
+ before do
+ update_repo2 do
+ build_gem "activesupport", "3.3.5"
+ build_gem "weakling", "1.0.1"
+ end
+ end
+
+ it_behaves_like "no version updates are detected"
+ end
+
+ shared_examples_for "minor version is ignored" do
+ before do
+ update_repo2 do
+ build_gem "activesupport", "2.4.5"
+ build_gem "weakling", "0.3.1"
+ end
+ end
+
+ it_behaves_like "no version updates are detected"
+ end
+
+ shared_examples_for "patch version is ignored" do
+ before do
+ update_repo2 do
+ build_gem "activesupport", "2.3.6"
+ build_gem "weakling", "0.0.4"
+ end
+ end
+
+ it_behaves_like "no version updates are detected"
+ end
+
+ describe "with --filter-major option" do
+ subject { bundle "outdated --filter-major" }
+
+ it_behaves_like "major version updates are detected"
+ it_behaves_like "minor version is ignored"
+ it_behaves_like "patch version is ignored"
+ end
+
+ describe "with --filter-minor option" do
+ subject { bundle "outdated --filter-minor" }
+
+ it_behaves_like "minor version updates are detected"
+ it_behaves_like "major version is ignored"
+ it_behaves_like "patch version is ignored"
+ end
+
+ describe "with --filter-patch option" do
+ subject { bundle "outdated --filter-patch" }
+
+ it_behaves_like "patch version updates are detected"
+ it_behaves_like "major version is ignored"
+ it_behaves_like "minor version is ignored"
+ end
+
+ describe "with --filter-minor --filter-patch options" do
+ subject { bundle "outdated --filter-minor --filter-patch" }
+
+ it_behaves_like "minor version updates are detected"
+ it_behaves_like "patch version updates are detected"
+ it_behaves_like "major version is ignored"
+ end
+
+ describe "with --filter-major --filter-minor options" do
+ subject { bundle "outdated --filter-major --filter-minor" }
+
+ it_behaves_like "major version updates are detected"
+ it_behaves_like "minor version updates are detected"
+ it_behaves_like "patch version is ignored"
+ end
+
+ describe "with --filter-major --filter-patch options" do
+ subject { bundle "outdated --filter-major --filter-patch" }
+
+ it_behaves_like "major version updates are detected"
+ it_behaves_like "patch version updates are detected"
+ it_behaves_like "minor version is ignored"
+ end
+
+ describe "with --filter-major --filter-minor --filter-patch options" do
+ subject { bundle "outdated --filter-major --filter-minor --filter-patch" }
+
+ it_behaves_like "major version updates are detected"
+ it_behaves_like "minor version updates are detected"
+ it_behaves_like "patch version updates are detected"
+ end
+
+ context "conservative updates" do
+ context "without update-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
+
+ # establish a lockfile set to 1.0.0
+ install_gemfile <<-G
+ source "file://#{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://#{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"
+
+ expect(out).to include("No minor updates to display.")
+ expect(out).not_to include("patch (newest")
+ expect(out).not_to include("minor (newest")
+ expect(out).not_to include("major (newest")
+ end
+
+ it "shows all gems when patching and filtering to patch" do
+ bundle "outdated --patch --filter-patch"
+
+ expect(out).to include("patch (newest 1.0.1")
+ expect(out).to include("minor (newest 1.0.1")
+ expect(out).to include("major (newest 1.0.1")
+ end
+
+ it "shows minor and major when updating to minor and filtering to patch and minor" do
+ bundle "outdated --minor --filter-minor"
+
+ expect(out).not_to include("patch (newest")
+ expect(out).to include("minor (newest 1.1.0")
+ expect(out).to include("major (newest 1.1.0")
+ end
+
+ it "shows minor when updating to major and filtering to minor with parseable" do
+ bundle "outdated --major --filter-minor --parseable"
+
+ expect(out).not_to include("patch (newest")
+ expect(out).to include("minor (newest")
+ expect(out).not_to include("major (newest")
+ end
+ end
+
+ context "with update-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]
+ end
+
+ # establish a lockfile set to 1.4.3
+ install_gemfile <<-G
+ source "file://#{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://#{gem_repo4}"
+ gem 'foo'
+ gem 'qux'
+ G
+ end
+
+ it "shows gems with update-strict updating to patch and filtering to patch" do
+ bundle "outdated --patch --update-strict --filter-patch"
+
+ expect(out).to include("foo (newest 1.4.4")
+ expect(out).to include("bar (newest 2.0.5")
+ expect(out).not_to include("qux (newest")
+ end
+ end
+ end
+
+ describe "with --only-explicit" do
+ it "does not report outdated dependent gems" do
+ build_repo4 do
+ build_gem "weakling", %w[0.2 0.3] do |s|
+ s.add_dependency "bar", "~> 2.1"
+ end
+ build_gem "bar", %w[2.1 2.2]
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'weakling', '0.2'
+ gem 'bar', '2.1'
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'weakling'
+ G
+
+ bundle "outdated --only-explicit"
+
+ expect(out).to include("weakling (newest 0.3")
+ expect(out).not_to include("bar (newest 2.2")
+ end
+ end
+end
diff --git a/spec/bundler/commands/package_spec.rb b/spec/bundler/commands/package_spec.rb
new file mode 100644
index 0000000000..6351909bc7
--- /dev/null
+++ b/spec/bundler/commands/package_spec.rb
@@ -0,0 +1,306 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle package" do
+ context "with --gemfile" do
+ it "finds the gemfile" do
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle "package --gemfile=NotGemfile"
+
+ ENV["BUNDLE_GEMFILE"] = "NotGemfile"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ context "with --all" do
+ context "without a gemspec" do
+ it "caches all dependencies except bundler itself" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ gem 'bundler'
+ D
+
+ bundle :package, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/bundler-0.9.gem")).to_not exist
+ end
+ end
+
+ context "with a gemspec" do
+ context "that has the same name as the gem" do
+ before do
+ File.open(bundled_app("mygem.gemspec"), "w") do |f|
+ f.write <<-G
+ Gem::Specification.new do |s|
+ s.name = "mygem"
+ s.version = "0.1.1"
+ s.summary = ""
+ s.authors = ["gem author"]
+ s.add_development_dependency "nokogiri", "=1.4.2"
+ end
+ G
+ end
+ end
+
+ it "caches all dependencies except bundler and the gemspec specified gem" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ gemspec
+ D
+
+ bundle! :package, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/nokogiri-1.4.2.gem")).to exist
+ expect(bundled_app("vendor/cache/mygem-0.1.1.gem")).to_not exist
+ expect(bundled_app("vendor/cache/bundler-0.9.gem")).to_not exist
+ end
+ end
+
+ context "that has a different name as the gem" do
+ before do
+ File.open(bundled_app("mygem_diffname.gemspec"), "w") do |f|
+ f.write <<-G
+ Gem::Specification.new do |s|
+ s.name = "mygem"
+ s.version = "0.1.1"
+ s.summary = ""
+ s.authors = ["gem author"]
+ s.add_development_dependency "nokogiri", "=1.4.2"
+ end
+ G
+ end
+ end
+
+ it "caches all dependencies except bundler and the gemspec specified gem" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ gemspec
+ D
+
+ bundle! :package, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/nokogiri-1.4.2.gem")).to exist
+ expect(bundled_app("vendor/cache/mygem-0.1.1.gem")).to_not exist
+ expect(bundled_app("vendor/cache/bundler-0.9.gem")).to_not exist
+ end
+ end
+ end
+
+ context "with multiple gemspecs" do
+ before do
+ File.open(bundled_app("mygem.gemspec"), "w") do |f|
+ f.write <<-G
+ Gem::Specification.new do |s|
+ s.name = "mygem"
+ s.version = "0.1.1"
+ s.summary = ""
+ s.authors = ["gem author"]
+ s.add_development_dependency "nokogiri", "=1.4.2"
+ end
+ G
+ end
+ File.open(bundled_app("mygem_client.gemspec"), "w") do |f|
+ f.write <<-G
+ Gem::Specification.new do |s|
+ s.name = "mygem_test"
+ s.version = "0.1.1"
+ s.summary = ""
+ s.authors = ["gem author"]
+ s.add_development_dependency "weakling", "=0.0.3"
+ end
+ G
+ end
+ end
+
+ it "caches all dependencies except bundler and the gemspec specified gems" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ gemspec :name => 'mygem'
+ gemspec :name => 'mygem_test'
+ D
+
+ bundle! :package, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/nokogiri-1.4.2.gem")).to exist
+ expect(bundled_app("vendor/cache/weakling-0.0.3.gem")).to exist
+ expect(bundled_app("vendor/cache/mygem-0.1.1.gem")).to_not exist
+ expect(bundled_app("vendor/cache/mygem_test-0.1.1.gem")).to_not exist
+ expect(bundled_app("vendor/cache/bundler-0.9.gem")).to_not exist
+ end
+ end
+ end
+
+ context "with --path", :bundler => "< 2" do
+ it "sets root directory for gems" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ D
+
+ bundle! :package, forgotten_command_line_options(:path => bundled_app("test"))
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(bundled_app("test/vendor/cache/")).to exist
+ end
+ end
+
+ context "with --no-install" do
+ it "puts the gems in vendor/cache but does not install them" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ D
+
+ bundle! "package --no-install"
+
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ end
+
+ it "does not prevent installing gems with bundle install" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ D
+
+ bundle! "package --no-install"
+ bundle! "install"
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ context "with --all-platforms" do
+ it "puts the gems in vendor/cache even for other rubies", :ruby => "2.1" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack', :platforms => :ruby_19
+ D
+
+ bundle "package --all-platforms"
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ end
+ end
+
+ context "with --frozen" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundle "install"
+ end
+
+ subject { bundle :package, forgotten_command_line_options(:frozen => true) }
+
+ it "tries to install with frozen" do
+ bundle! "config deployment true"
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+ subject
+ expect(exitstatus).to eq(16) if exitstatus
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile")
+ expect(out).to include("* rack-obama")
+ bundle "env"
+ expect(out).to include("frozen").or include("deployment")
+ end
+ end
+end
+
+RSpec.describe "bundle install with gem sources" do
+ describe "when cached and locked" do
+ it "does not hit the remote at all" do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ G
+
+ bundle :pack
+ simulate_new_machine
+ FileUtils.rm_rf gem_repo2
+
+ bundle "install --local"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "does not hit the remote at all" do
+ build_repo2
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ G
+
+ bundle! :pack
+ simulate_new_machine
+ FileUtils.rm_rf gem_repo2
+
+ bundle! :install, forgotten_command_line_options(:deployment => true, :path => "vendor/bundle")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "does not reinstall already-installed gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundle :pack
+
+ build_gem "rack", "1.0.0", :path => bundled_app("vendor/cache") do |s|
+ s.write "lib/rack.rb", "raise 'omg'"
+ end
+
+ bundle :install
+ expect(err).to lack_errors
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "ignores cached gems for the wrong platform" do
+ simulate_platform "java" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+ bundle :pack
+ end
+
+ simulate_new_machine
+
+ simulate_platform "ruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "platform_specific"
+ G
+ run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
+ expect(out).to eq("1.0.0 RUBY")
+ end
+ end
+
+ it "does not update the cache if --no-cache is passed" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundled_app("vendor/cache").mkpath
+ expect(bundled_app("vendor/cache").children).to be_empty
+
+ bundle "install --no-cache"
+ expect(bundled_app("vendor/cache").children).to be_empty
+ end
+ end
+end
diff --git a/spec/bundler/commands/pristine_spec.rb b/spec/bundler/commands/pristine_spec.rb
new file mode 100644
index 0000000000..0bfc37560a
--- /dev/null
+++ b/spec/bundler/commands/pristine_spec.rb
@@ -0,0 +1,192 @@
+# frozen_string_literal: true
+
+require "bundler/vendored_fileutils"
+
+RSpec.describe "bundle pristine", :ruby_repo do
+ before :each do
+ build_lib "baz", :path => bundled_app do |s|
+ s.version = "1.0.0"
+ s.add_development_dependency "baz-dev", "=1.0.0"
+ end
+
+ build_repo2 do
+ build_gem "weakling"
+ build_gem "baz-dev", "1.0.0"
+ build_gem "very_simple_binary", &:add_c_extension
+ build_git "foo", :path => lib_path("foo")
+ build_git "git_with_ext", :path => lib_path("git_with_ext"), &:add_c_extension
+ build_lib "bar", :path => lib_path("bar")
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "weakling"
+ gem "very_simple_binary"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "git_with_ext", :git => "#{lib_path("git_with_ext")}"
+ gem "bar", :path => "#{lib_path("bar")}"
+
+ gemspec
+ G
+ end
+
+ context "when sourced from RubyGems" do
+ it "reverts using cached .gem file" do
+ spec = Bundler.definition.specs["weakling"].first
+ changes_txt = Pathname.new(spec.full_gem_path).join("lib/changes.txt")
+
+ FileUtils.touch(changes_txt)
+ expect(changes_txt).to be_file
+
+ bundle "pristine"
+ expect(changes_txt).to_not be_file
+ end
+
+ it "does not delete the bundler gem" do
+ ENV["BUNDLER_SPEC_KEEP_DEFAULT_BUNDLER_GEM"] = "true"
+ system_gems :bundler
+ bundle! "install"
+ bundle! "pristine", :system_bundler => true
+ bundle! "-v", :system_bundler => true
+
+ expected = if Bundler::VERSION < "2.0"
+ "Bundler version"
+ else
+ Bundler::VERSION
+ end
+
+ expect(out).to start_with(expected)
+ end
+ end
+
+ context "when sourced from git repo" do
+ it "reverts by resetting to current revision`" do
+ spec = Bundler.definition.specs["foo"].first
+ changed_file = Pathname.new(spec.full_gem_path).join("lib/foo.rb")
+ diff = "#Pristine spec changes"
+
+ File.open(changed_file, "a") {|f| f.puts diff }
+ expect(File.read(changed_file)).to include(diff)
+
+ bundle! "pristine"
+ expect(File.read(changed_file)).to_not include(diff)
+ end
+
+ it "removes added files" do
+ spec = Bundler.definition.specs["foo"].first
+ changes_txt = Pathname.new(spec.full_gem_path).join("lib/changes.txt")
+
+ FileUtils.touch(changes_txt)
+ expect(changes_txt).to be_file
+
+ bundle! "pristine"
+ expect(changes_txt).not_to be_file
+ end
+ end
+
+ context "when sourced from gemspec" do
+ it "displays warning and ignores changes when sourced from gemspec" do
+ spec = Bundler.definition.specs["baz"].first
+ changed_file = Pathname.new(spec.full_gem_path).join("lib/baz.rb")
+ diff = "#Pristine spec changes"
+
+ File.open(changed_file, "a") {|f| f.puts diff }
+ expect(File.read(changed_file)).to include(diff)
+
+ bundle "pristine"
+ expect(File.read(changed_file)).to include(diff)
+ expect(out).to include("Cannot pristine #{spec.name} (#{spec.version}#{spec.git_version}). Gem is sourced from local path.")
+ end
+
+ it "reinstall gemspec dependency" do
+ spec = Bundler.definition.specs["baz-dev"].first
+ changed_file = Pathname.new(spec.full_gem_path).join("lib/baz-dev.rb")
+ diff = "#Pristine spec changes"
+
+ File.open(changed_file, "a") {|f| f.puts "#Pristine spec changes" }
+ expect(File.read(changed_file)).to include(diff)
+
+ bundle "pristine"
+ expect(File.read(changed_file)).to_not include(diff)
+ end
+ end
+
+ context "when sourced from path" do
+ it "displays warning and ignores changes when sourced from local path" do
+ spec = Bundler.definition.specs["bar"].first
+ changes_txt = Pathname.new(spec.full_gem_path).join("lib/changes.txt")
+ FileUtils.touch(changes_txt)
+ expect(changes_txt).to be_file
+ bundle "pristine"
+ expect(out).to include("Cannot pristine #{spec.name} (#{spec.version}#{spec.git_version}). Gem is sourced from local path.")
+ expect(changes_txt).to be_file
+ end
+ end
+
+ context "when passing a list of gems to pristine" do
+ it "resets them" do
+ foo = Bundler.definition.specs["foo"].first
+ foo_changes_txt = Pathname.new(foo.full_gem_path).join("lib/changes.txt")
+ FileUtils.touch(foo_changes_txt)
+ expect(foo_changes_txt).to be_file
+
+ bar = Bundler.definition.specs["bar"].first
+ bar_changes_txt = Pathname.new(bar.full_gem_path).join("lib/changes.txt")
+ FileUtils.touch(bar_changes_txt)
+ expect(bar_changes_txt).to be_file
+
+ weakling = Bundler.definition.specs["weakling"].first
+ weakling_changes_txt = Pathname.new(weakling.full_gem_path).join("lib/changes.txt")
+ FileUtils.touch(weakling_changes_txt)
+ expect(weakling_changes_txt).to be_file
+
+ bundle! "pristine foo bar weakling"
+
+ expect(out).to include("Cannot pristine bar (1.0). Gem is sourced from local path.").
+ and include("Installing weakling 1.0")
+
+ expect(weakling_changes_txt).not_to be_file
+ expect(foo_changes_txt).not_to be_file
+ expect(bar_changes_txt).to be_file
+ end
+
+ it "raises when one of them is not in the lockfile" do
+ bundle "pristine abcabcabc"
+ expect(out).to include("Could not find gem 'abcabcabc'.")
+ end
+ end
+
+ context "when a build config exists for one of the gems" do
+ let(:very_simple_binary) { Bundler.definition.specs["very_simple_binary"].first }
+ let(:c_ext_dir) { Pathname.new(very_simple_binary.full_gem_path).join("ext") }
+ let(:build_opt) { "--with-ext-lib=#{c_ext_dir}" }
+ before { bundle "config build.very_simple_binary -- #{build_opt}" }
+
+ # This just verifies that the generated Makefile from the c_ext gem makes
+ # use of the build_args from the bundle config
+ it "applies the config when installing the gem" do
+ bundle! "pristine"
+
+ makefile_contents = File.read(c_ext_dir.join("Makefile").to_s)
+ expect(makefile_contents).to match(/libpath =.*#{c_ext_dir}/)
+ expect(makefile_contents).to match(/LIBPATH =.*-L#{c_ext_dir}/)
+ end
+ end
+
+ context "when a build config exists for a git sourced gem" do
+ let(:git_with_ext) { Bundler.definition.specs["git_with_ext"].first }
+ let(:c_ext_dir) { Pathname.new(git_with_ext.full_gem_path).join("ext") }
+ let(:build_opt) { "--with-ext-lib=#{c_ext_dir}" }
+ before { bundle "config build.git_with_ext -- #{build_opt}" }
+
+ # This just verifies that the generated Makefile from the c_ext gem makes
+ # use of the build_args from the bundle config
+ it "applies the config when installing the gem" do
+ bundle! "pristine"
+
+ makefile_contents = File.read(c_ext_dir.join("Makefile").to_s)
+ expect(makefile_contents).to match(/libpath =.*#{c_ext_dir}/)
+ expect(makefile_contents).to match(/LIBPATH =.*-L#{c_ext_dir}/)
+ end
+ end
+end
diff --git a/spec/bundler/commands/remove_spec.rb b/spec/bundler/commands/remove_spec.rb
new file mode 100644
index 0000000000..faeb654b14
--- /dev/null
+++ b/spec/bundler/commands/remove_spec.rb
@@ -0,0 +1,583 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle remove" do
+ context "when no gems are specified" do
+ it "throws error" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ G
+
+ bundle "remove"
+
+ expect(out).to include("Please specify gems to remove.")
+ end
+ end
+
+ context "when --install flag is specified" do
+ it "removes gems from .bundle" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ bundle! "remove rack --install"
+
+ expect(out).to include("rack was removed.")
+ expect(the_bundle).to_not include_gems "rack"
+ end
+ end
+
+ describe "remove single gem from gemfile" do
+ context "when gem is present in gemfile" do
+ it "shows success for removed gem" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ bundle! "remove rack"
+
+ expect(out).to include("rack was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "when gem is not present in gemfile" do
+ it "shows warning for gem that could not be removed" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ G
+
+ bundle "remove rack"
+
+ expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile")} so it could not be removed.")
+ end
+ end
+ end
+
+ describe "remove mutiple gems from gemfile" do
+ context "when all gems are present in gemfile" do
+ it "shows success fir all removed gems" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ gem "rails"
+ G
+
+ bundle! "remove rack rails"
+
+ expect(out).to include("rack was removed.")
+ expect(out).to include("rails was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "when some gems are not present in the gemfile" do
+ it "shows warning for those not present and success for those that can be removed" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rails"
+ gem "minitest"
+ gem "rspec"
+ G
+
+ bundle "remove rails rack minitest"
+
+ expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile")} so it could not be removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rails"
+ gem "minitest"
+ gem "rspec"
+ G
+ end
+ end
+ end
+
+ context "with inline groups" do
+ it "removes the specified gem" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", :group => [:dev]
+ G
+
+ bundle! "remove rack"
+
+ expect(out).to include("rack was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ describe "with group blocks" do
+ context "when single group block with gem to be removed is present" do
+ it "removes the group block" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ group :test do
+ gem "rspec"
+ end
+ G
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "when an empty block is also present" do
+ it "removes all empty blocks" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ group :test do
+ gem "rspec"
+ end
+
+ group :dev do
+ end
+ G
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "when the gem belongs to mutiple groups" do
+ it "removes the groups" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ group :test, :serioustest do
+ gem "rspec"
+ end
+ G
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "when the gem is present in mutiple groups" do
+ it "removes all empty blocks" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ group :one do
+ gem "rspec"
+ end
+
+ group :two do
+ gem "rspec"
+ end
+ G
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+ end
+
+ describe "nested group blocks" do
+ context "when all the groups will be empty after removal" do
+ it "removes the empty nested blocks" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ group :test do
+ group :serioustest do
+ gem "rspec"
+ end
+ end
+ G
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "when outer group will not be empty after removal" do
+ it "removes only empty blocks" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ group :test do
+ gem "rack-test"
+
+ group :serioustest do
+ gem "rspec"
+ end
+ end
+ G
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+
+ group :test do
+ gem "rack-test"
+
+ end
+ G
+ end
+ end
+
+ context "when inner group will not be empty after removal" do
+ it "removes only empty blocks" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ group :test do
+ group :serioustest do
+ gem "rspec"
+ gem "rack-test"
+ end
+ end
+ G
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+
+ group :test do
+ group :serioustest do
+ gem "rack-test"
+ end
+ end
+ G
+ end
+ end
+ end
+
+ describe "arbitrary gemfile" do
+ context "when mutiple gems are present in same line" do
+ it "shows warning for gems not removed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"; gem "rails"
+ G
+
+ bundle "remove rails"
+
+ if Gem::VERSION >= "1.6.0"
+ expect(out).to include("Gems could not be removed. rack (>= 0) would also have been removed.")
+ else
+ expect(out).to include("Gems could not be removed. rack (>= 0, runtime) would also have been removed.")
+ end
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"; gem "rails"
+ G
+ end
+ end
+
+ context "when some gems could not be removed" do
+ it "shows warning for gems not removed and success for those removed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem"rack"
+ gem"rspec"
+ gem "rails"
+ gem "minitest"
+ G
+
+ bundle! "remove rails rack rspec minitest"
+
+ expect(out).to include("rails was removed.")
+ expect(out).to include("minitest was removed.")
+ expect(out).to include("rack, rspec could not be removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ gem"rack"
+ gem"rspec"
+ G
+ end
+ end
+ end
+
+ context "with sources" do
+ before do
+ build_repo gem_repo3 do
+ build_gem "rspec"
+ end
+ end
+
+ it "removes gems and empty source blocks" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+
+ source "file://#{gem_repo3}" do
+ gem "rspec"
+ end
+ G
+
+ bundle! "install"
+
+ bundle! "remove rspec"
+
+ expect(out).to include("rspec was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+ end
+ end
+
+ describe "with eval_gemfile" do
+ context "when gems are present in both gemfiles" do
+ it "removes the gems" do
+ create_file "Gemfile-other", <<-G
+ gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+
+ gem "rack"
+ G
+
+ bundle! "remove rack"
+
+ expect(out).to include("rack was removed.")
+ end
+ end
+
+ context "when gems are present in other gemfile" do
+ it "removes the gems" do
+ create_file "Gemfile-other", <<-G
+ gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ G
+
+ bundle! "remove rack"
+
+ expect(bundled_app("Gemfile-other").read).to_not include("gem \"rack\"")
+ expect(out).to include("rack was removed.")
+ end
+ end
+
+ context "when gems to be removed are not specified in any of the gemfiles" do
+ it "throws error for the gems not present" do
+ # an empty gemfile
+ # indicating the gem is not present in the gemfile
+ create_file "Gemfile-other", <<-G
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ G
+
+ bundle "remove rack"
+
+ expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile")} so it could not be removed.")
+ end
+ end
+
+ context "when the gem is present in parent file but not in gemfile specified by eval_gemfile" do
+ it "removes the gem" do
+ create_file "Gemfile-other", <<-G
+ gem "rails"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ gem "rack"
+ G
+
+ bundle "remove rack"
+
+ expect(out).to include("rack was removed.")
+ expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile-other")} so it could not be removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ G
+ end
+ end
+
+ context "when gems can not be removed from other gemfile" do
+ it "shows error" do
+ create_file "Gemfile-other", <<-G
+ gem "rails"; gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ gem "rack"
+ G
+
+ bundle "remove rack"
+
+ expect(out).to include("rack was removed.")
+ if Gem::VERSION >= "1.6.0"
+ expect(out).to include("Gems could not be removed. rails (>= 0) would also have been removed.")
+ else
+ expect(out).to include("Gems could not be removed. rails (>= 0, runtime) would also have been removed.")
+ end
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ G
+ end
+ end
+
+ context "when gems could not be removed from parent gemfile" do
+ it "shows error" do
+ create_file "Gemfile-other", <<-G
+ gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ gem "rails"; gem "rack"
+ G
+
+ bundle "remove rack"
+
+ if Gem::VERSION >= "1.6.0"
+ expect(out).to include("Gems could not be removed. rails (>= 0) would also have been removed.")
+ else
+ expect(out).to include("Gems could not be removed. rails (>= 0, runtime) would also have been removed.")
+ end
+ expect(bundled_app("Gemfile-other").read).to include("gem \"rack\"")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ gem "rails"; gem "rack"
+ G
+ end
+ 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
+ create_file "Gemfile-other", <<-G
+ gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ eval_gemfile "Gemfile-other"
+ gem"rack"
+ G
+
+ bundle! "remove rack"
+
+ expect(out).to include("rack was removed.")
+ expect(bundled_app("Gemfile-other").read).to_not include("gem \"rack\"")
+ end
+ end
+ end
+
+ context "with install_if" do
+ it "removes gems inside blocks and empty blocks" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ install_if(lambda { false }) do
+ gem "rack"
+ end
+ G
+
+ bundle! "remove rack"
+
+ expect(out).to include("rack was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "with env" do
+ it "removes gems inside blocks and empty blocks" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ env "BUNDLER_TEST" do
+ gem "rack"
+ end
+ G
+
+ bundle! "remove rack"
+
+ expect(out).to include("rack was removed.")
+ gemfile_should_be <<-G
+ source "file://#{gem_repo1}"
+ G
+ end
+ end
+
+ context "with gemspec" do
+ it "should not remove the gem" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("foo.gemspec", "")
+ s.add_dependency "rack"
+ end
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo1}"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ bundle! "remove foo"
+
+ expect(out).to include("foo could not be removed.")
+ end
+ end
+end
diff --git a/spec/bundler/commands/show_spec.rb b/spec/bundler/commands/show_spec.rb
new file mode 100644
index 0000000000..efbe4b13fb
--- /dev/null
+++ b/spec/bundler/commands/show_spec.rb
@@ -0,0 +1,244 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle show", :bundler => "< 2", :ruby => ">= 2.0" do
+ context "with a standard Gemfile" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ end
+
+ it "creates a Gemfile.lock if one did not exist" do
+ FileUtils.rm("Gemfile.lock")
+
+ bundle "show"
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ it "creates a Gemfile.lock when invoked with a gem name" do
+ FileUtils.rm("Gemfile.lock")
+
+ bundle "show rails"
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ it "prints path if gem exists in bundle" do
+ bundle "show rails"
+ expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
+ end
+
+ context "when show command deprecation is enabled" do
+ before { bundle "config major_deprecations yes" }
+
+ it "prints path if gem exists in bundle" do
+ bundle "show rails"
+ expect(out).to eq(
+ "[DEPRECATED FOR 2.0] use `bundle info rails` instead of `bundle show rails`\n" +
+ default_bundle_path("gems", "rails-2.3.2").to_s
+ )
+ end
+
+ it "prints the path to the running bundler" do
+ bundle "show bundler"
+ expect(out).to eq(
+ "[DEPRECATED FOR 2.0] use `bundle info bundler` instead of `bundle show bundler`\n" +
+ root.to_s
+ )
+ end
+
+ it "prints path if gem exists in bundle (with --paths option)" do
+ bundle "show rails --paths"
+ expect(out).to eq(
+ "[DEPRECATED FOR 2.0] use `bundle info rails --path` instead of `bundle show rails --paths`\n" +
+ default_bundle_path("gems", "rails-2.3.2").to_s
+ )
+ end
+
+ it "prints path of all gems in bundle sorted by name" do
+ bundle "show --paths"
+
+ expect(out).to include(default_bundle_path("gems", "rake-10.0.2").to_s)
+ expect(out).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
+
+ out_lines = out.split("\n")
+ expect(out_lines[0]).to eq("[DEPRECATED FOR 2.0] use `bundle list` instead of `bundle show --paths`")
+
+ # Gem names are the last component of their path.
+ gem_list = out_lines[1..-1].map {|p| p.split("/").last }
+ expect(gem_list).to eq(gem_list.sort)
+ end
+ end
+
+ it "prints path if gem exists in bundle (with --paths option)" do
+ bundle "show rails --paths"
+ expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
+ end
+
+ it "warns if path no longer exists on disk" do
+ FileUtils.rm_rf(default_bundle_path("gems", "rails-2.3.2"))
+
+ bundle "show rails"
+
+ expect(out).to match(/has been deleted/i).
+ and include(default_bundle_path("gems", "rails-2.3.2").to_s)
+ end
+
+ it "prints the path to the running bundler" do
+ bundle "show bundler"
+ expect(out).to eq(root.to_s)
+ end
+
+ it "complains if gem not in bundle" do
+ bundle "show missing"
+ expect(out).to match(/could not find gem 'missing'/i)
+ end
+
+ it "prints path of all gems in bundle sorted by name" do
+ bundle "show --paths"
+
+ expect(out).to include(default_bundle_path("gems", "rake-10.0.2").to_s)
+ expect(out).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
+
+ # Gem names are the last component of their path.
+ gem_list = out.split.map {|p| p.split("/").last }
+ expect(gem_list).to eq(gem_list.sort)
+ end
+
+ it "prints summary of gems" do
+ bundle "show --verbose"
+
+ loaded_bundler_spec = Bundler.load.specs["bundler"]
+ expected = if !loaded_bundler_spec.empty?
+ loaded_bundler_spec[0].homepage
+ else
+ "No website available."
+ end
+
+ expect(out).to include("* actionmailer (2.3.2)")
+ expect(out).to include("\tSummary: This is just a fake gem for testing")
+ expect(out).to include("\tHomepage: #{expected}")
+ expect(out).to include("\tStatus: Up to date")
+ end
+ end
+
+ context "with a git repo in the Gemfile" do
+ before :each do
+ @git = build_git "foo", "1.0"
+ end
+
+ it "prints out git info" do
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+ expect(the_bundle).to include_gems "foo 1.0"
+
+ bundle :show
+ expect(out).to include("foo (1.0 #{@git.ref_for("master", 6)}")
+ end
+
+ it "prints out branch names other than master" do
+ update_git "foo", :branch => "omg" do |s|
+ s.write "lib/foo.rb", "FOO = '1.0.omg'"
+ end
+ @revision = revision_for(lib_path("foo-1.0"))[0...6]
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :branch => "omg"
+ G
+ expect(the_bundle).to include_gems "foo 1.0.omg"
+
+ bundle :show
+ expect(out).to include("foo (1.0 #{@git.ref_for("omg", 6)}")
+ end
+
+ it "doesn't print the branch when tied to a ref" do
+ sha = revision_for(lib_path("foo-1.0"))
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :ref => "#{sha}"
+ G
+
+ bundle :show
+ expect(out).to include("foo (1.0 #{sha[0..6]})")
+ end
+
+ it "handles when a version is a '-' prerelease", :rubygems => "2.1" do
+ @git = build_git("foo", "1.0.0-beta.1", :path => lib_path("foo"))
+ install_gemfile <<-G
+ gem "foo", "1.0.0-beta.1", :git => "#{lib_path("foo")}"
+ G
+ expect(the_bundle).to include_gems "foo 1.0.0.pre.beta.1"
+
+ bundle! :show
+ expect(out).to include("foo (1.0.0.pre.beta.1")
+ end
+ end
+
+ context "in a fresh gem in a blank git repo" do
+ before :each do
+ build_git "foo", :path => lib_path("foo")
+ in_app_root_custom lib_path("foo")
+ File.open("Gemfile", "w") {|f| f.puts "gemspec" }
+ sys_exec "rm -rf .git && git init"
+ end
+
+ it "does not output git errors" do
+ bundle :show
+ expect(err).to lack_errors
+ end
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle :show
+ expect(out).to include("Installing foo 1.0")
+ end
+
+ context "with an invalid regexp for gem name" do
+ it "does not find the gem" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ invalid_regexp = "[]"
+
+ bundle "show #{invalid_regexp}"
+ expect(out).to include("Could not find gem '#{invalid_regexp}'.")
+ end
+ end
+
+ context "--outdated option" do
+ # Regression test for https://github.com/bundler/bundler/issues/5375
+ before do
+ build_repo2
+ end
+
+ it "doesn't update gems to newer versions" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "rails"
+ G
+
+ expect(the_bundle).to include_gem("rails 2.3.2")
+
+ update_repo2 do
+ build_gem "rails", "3.0.0" do |s|
+ s.executables = "rails"
+ end
+ end
+
+ bundle! "show --outdated"
+
+ bundle! "install"
+ expect(the_bundle).to include_gem("rails 2.3.2")
+ end
+ end
+end
diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb
new file mode 100644
index 0000000000..6eb49d3acd
--- /dev/null
+++ b/spec/bundler/commands/update_spec.rb
@@ -0,0 +1,943 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle update" do
+ before :each do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+ G
+ end
+
+ describe "with no arguments", :bundler => "< 2" do
+ it "updates the entire bundle" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle "update"
+ expect(out).to include("Bundle updated!")
+ expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0"
+ end
+
+ it "doesn't delete the Gemfile.lock file if something goes wrong" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+ exit!
+ G
+ bundle "update"
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+ end
+
+ describe "with --all", :bundler => "2" do
+ it "updates the entire bundle" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle! "update", :all => true
+ expect(out).to include("Bundle updated!")
+ expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0"
+ end
+
+ it "doesn't delete the Gemfile.lock file if something goes wrong" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+ exit!
+ G
+ bundle "update", :all => true
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+ end
+
+ describe "with --gemfile" do
+ it "creates lock files based on the Gemfile name" do
+ gemfile bundled_app("OmgFile"), <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0"
+ G
+
+ bundle! "update --gemfile OmgFile", :all => bundle_update_requires_all?
+
+ expect(bundled_app("OmgFile.lock")).to exist
+ end
+ end
+
+ context "when update_requires_all_flag is set" do
+ before { bundle! "config update_requires_all_flag true" }
+
+ it "errors when passed nothing" do
+ install_gemfile! ""
+ bundle :update
+ expect(out).to eq("To update everything, pass the `--all` flag.")
+ end
+
+ it "errors when passed --all and another option" do
+ install_gemfile! ""
+ bundle "update --all foo"
+ expect(out).to eq("Cannot specify --all along with specific options.")
+ end
+
+ it "updates everything when passed --all" do
+ install_gemfile! ""
+ bundle "update --all"
+ expect(out).to include("Bundle updated!")
+ end
+ end
+
+ describe "--quiet argument" do
+ it "hides UI messages" do
+ bundle "update --quiet"
+ expect(out).not_to include("Bundle updated!")
+ end
+ end
+
+ describe "with a top level dependency" do
+ it "unlocks all child dependencies that are unrelated to other locked dependencies" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle "update rack-obama"
+ expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 2.3.5"
+ end
+ end
+
+ describe "with an unknown dependency" do
+ it "should inform the user" do
+ bundle "update halting-problem-solver"
+ expect(out).to include "Could not find gem 'halting-problem-solver'"
+ end
+ it "should suggest alternatives" do
+ bundle "update active-support"
+ expect(out).to include "Did you mean activesupport?"
+ end
+ end
+
+ describe "with a child dependency" do
+ it "should update the child dependency" do
+ update_repo2
+ bundle "update rack"
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+ end
+
+ describe "when a possible resolve requires an older version of a locked gem" do
+ context "and only_update_to_newer_versions is set" do
+ before do
+ bundle! "config only_update_to_newer_versions true"
+ end
+
+ it "does not go to an older version" do
+ build_repo4 do
+ build_gem "tilt", "2.0.8"
+ build_gem "slim", "3.0.9" do |s|
+ s.add_dependency "tilt", [">= 1.3.3", "< 2.1"]
+ end
+ build_gem "slim_lint", "0.16.1" do |s|
+ s.add_dependency "slim", [">= 3.0", "< 5.0"]
+ end
+ build_gem "slim-rails", "0.2.1" do |s|
+ s.add_dependency "slim", ">= 0.9.2"
+ end
+ build_gem "slim-rails", "3.1.3" do |s|
+ s.add_dependency "slim", "~> 3.0"
+ end
+ end
+
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ gem "slim-rails"
+ gem "slim_lint"
+ G
+
+ expect(the_bundle).to include_gems("slim 3.0.9", "slim-rails 3.1.3", "slim_lint 0.16.1")
+
+ update_repo4 do
+ build_gem "slim", "4.0.0" do |s|
+ s.add_dependency "tilt", [">= 2.0.6", "< 2.1"]
+ end
+ end
+
+ bundle! "update", :all => bundle_update_requires_all?
+
+ expect(the_bundle).to include_gems("slim 3.0.9", "slim-rails 3.1.3", "slim_lint 0.16.1")
+ end
+
+ it "should still downgrade if forced by the Gemfile" do
+ build_repo4 do
+ build_gem "a"
+ build_gem "b", "1.0"
+ build_gem "b", "2.0"
+ end
+
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ gem "a"
+ gem "b"
+ G
+
+ expect(the_bundle).to include_gems("a 1.0", "b 2.0")
+
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem "a"
+ gem "b", "1.0"
+ G
+
+ bundle! "update b"
+
+ expect(the_bundle).to include_gems("a 1.0", "b 1.0")
+ end
+ end
+ end
+
+ describe "with --local option" do
+ it "doesn't hit repo2" do
+ FileUtils.rm_rf(gem_repo2)
+
+ bundle "update --local --all"
+ expect(out).not_to include("Fetching source index")
+ end
+ end
+
+ describe "with --group option" do
+ it "should update only specified group gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", :group => :development
+ gem "rack"
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+ bundle "update --group development"
+ expect(the_bundle).to include_gems "activesupport 3.0"
+ expect(the_bundle).not_to include_gems "rack 1.2"
+ end
+
+ context "when conservatively updating a group with non-group sub-deps" do
+ it "should update only specified group gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activemerchant", :group => :development
+ gem "activesupport"
+ G
+ update_repo2 do
+ build_gem "activemerchant", "2.0"
+ build_gem "activesupport", "3.0"
+ end
+ bundle "update --conservative --group development"
+ expect(the_bundle).to include_gems "activemerchant 2.0"
+ expect(the_bundle).not_to include_gems "activesupport 3.0"
+ end
+ end
+
+ context "when there is a source with the same name as a gem in a group" do
+ before :each do
+ build_git "foo", :path => lib_path("activesupport")
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", :group => :development
+ gem "foo", :git => "#{lib_path("activesupport")}"
+ G
+ end
+
+ it "should not update the gems from that source" do
+ update_repo2 { build_gem "activesupport", "3.0" }
+ update_git "foo", "2.0", :path => lib_path("activesupport")
+
+ bundle "update --group development"
+ expect(the_bundle).to include_gems "activesupport 3.0"
+ expect(the_bundle).not_to include_gems "foo 2.0"
+ end
+ end
+
+ context "when bundler itself is a transitive dependency" do
+ it "executes without error" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport", :group => :development
+ gem "rack"
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+ bundle "update --group development"
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ expect(the_bundle).to include_gems "bundler #{Bundler::VERSION}"
+ expect(the_bundle).not_to include_gems "rack 1.2"
+ end
+ end
+ end
+
+ describe "in a frozen bundle" do
+ it "should fail loudly", :bundler => "< 2" do
+ bundle! "install --deployment"
+ bundle "update", :all => bundle_update_requires_all?
+
+ expect(last_command).to be_failure
+ expect(out).to match(/You are trying to install in deployment mode after changing.your Gemfile/m)
+ expect(out).to match(/freeze \nby running `bundle install --no-deployment`./m)
+ end
+
+ it "should suggest different command when frozen is set globally", :bundler => "< 2" do
+ bundle! "config --global frozen 1"
+ bundle "update", :all => bundle_update_requires_all?
+ expect(out).to match(/You are trying to install in deployment mode after changing.your Gemfile/m).
+ and match(/freeze \nby running `bundle config --delete frozen`./m)
+ end
+
+ it "should suggest different command when frozen is set globally", :bundler => "2" do
+ bundle! "config --global deployment true"
+ bundle "update", :all => bundle_update_requires_all?
+ expect(out).to match(/You are trying to install in deployment mode after changing.your Gemfile/m).
+ and match(/freeze \nby running `bundle config --delete deployment`./m)
+ end
+ end
+
+ describe "with --source option" do
+ it "should not update gems not included in the source that happen to have the same name", :bundler => "< 2" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ G
+ update_repo2 { build_gem "activesupport", "3.0" }
+
+ bundle! "update --source activesupport"
+ expect(the_bundle).to include_gem "activesupport 3.0"
+ end
+
+ it "should not update gems not included in the source that happen to have the same name", :bundler => "2" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ G
+ update_repo2 { build_gem "activesupport", "3.0" }
+
+ bundle! "update --source activesupport"
+ expect(the_bundle).not_to include_gem "activesupport 3.0"
+ end
+
+ context "with unlock_source_unlocks_spec set to false" do
+ before { bundle! "config unlock_source_unlocks_spec false" }
+
+ it "should not update gems not included in the source that happen to have the same name" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ G
+ update_repo2 { build_gem "activesupport", "3.0" }
+
+ bundle "update --source activesupport"
+ expect(the_bundle).not_to include_gems "activesupport 3.0"
+ end
+ end
+ end
+
+ context "when there is a child dependency that is also in the gemfile" do
+ before do
+ build_repo2 do
+ build_gem "fred", "1.0"
+ build_gem "harry", "1.0" do |s|
+ s.add_dependency "fred"
+ end
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "harry"
+ gem "fred"
+ G
+ end
+
+ it "should not update the child dependencies of a gem that has the same name as the source", :bundler => "< 2" do
+ update_repo2 do
+ build_gem "fred", "2.0"
+ build_gem "harry", "2.0" do |s|
+ s.add_dependency "fred"
+ end
+ end
+
+ bundle "update --source harry"
+ expect(the_bundle).to include_gems "harry 2.0"
+ expect(the_bundle).to include_gems "fred 1.0"
+ end
+
+ it "should not update the child dependencies of a gem that has the same name as the source", :bundler => "2" do
+ update_repo2 do
+ build_gem "fred", "2.0"
+ build_gem "harry", "2.0" do |s|
+ s.add_dependency "fred"
+ end
+ end
+
+ bundle "update --source harry"
+ expect(the_bundle).to include_gems "harry 1.0", "fred 1.0"
+ end
+ end
+
+ context "when there is a child dependency that appears elsewhere in the dependency graph" do
+ before do
+ build_repo2 do
+ build_gem "fred", "1.0" do |s|
+ s.add_dependency "george"
+ end
+ build_gem "george", "1.0"
+ build_gem "harry", "1.0" do |s|
+ s.add_dependency "george"
+ end
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "harry"
+ gem "fred"
+ G
+ end
+
+ it "should not update the child dependencies of a gem that has the same name as the source", :bundler => "< 2" do
+ update_repo2 do
+ build_gem "george", "2.0"
+ build_gem "harry", "2.0" do |s|
+ s.add_dependency "george"
+ end
+ end
+
+ bundle "update --source harry"
+ expect(the_bundle).to include_gems "harry 2.0"
+ expect(the_bundle).to include_gems "fred 1.0"
+ expect(the_bundle).to include_gems "george 1.0"
+ end
+
+ it "should not update the child dependencies of a gem that has the same name as the source", :bundler => "2" do
+ update_repo2 do
+ build_gem "george", "2.0"
+ build_gem "harry", "2.0" do |s|
+ s.add_dependency "george"
+ end
+ end
+
+ bundle "update --source harry"
+ expect(the_bundle).to include_gems "harry 1.0", "fred 1.0", "george 1.0"
+ end
+ end
+end
+
+RSpec.describe "bundle update in more complicated situations" do
+ before :each do
+ build_repo2
+ end
+
+ it "will eagerly unlock dependencies of a specified gem" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "thin"
+ gem "rack-obama"
+ G
+
+ update_repo2 do
+ build_gem "thin", "2.0" do |s|
+ s.add_dependency "rack"
+ end
+ end
+
+ bundle "update thin"
+ expect(the_bundle).to include_gems "thin 2.0", "rack 1.2", "rack-obama 1.0"
+ end
+
+ it "will warn when some explicitly updated gems are not updated" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo2}"
+
+ gem "thin"
+ gem "rack-obama"
+ G
+
+ update_repo2 do
+ build_gem("thin", "2.0") {|s| s.add_dependency "rack" }
+ build_gem "rack", "10.0"
+ end
+
+ bundle! "update thin rack-obama"
+ expect(last_command.stdboth).to include "Bundler attempted to update rack-obama but its version stayed the same"
+ expect(the_bundle).to include_gems "thin 2.0", "rack 10.0", "rack-obama 1.0"
+ end
+
+ it "will update only from pinned source" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ source "file://#{gem_repo1}" do
+ gem "thin"
+ end
+ G
+
+ update_repo2 do
+ build_gem "thin", "2.0"
+ end
+
+ bundle "update"
+ expect(the_bundle).to include_gems "thin 1.0"
+ end
+
+ context "when the lockfile is for a different platform" do
+ before do
+ build_repo4 do
+ build_gem("a", "0.9")
+ build_gem("a", "0.9") {|s| s.platform = "java" }
+ build_gem("a", "1.1")
+ build_gem("a", "1.1") {|s| s.platform = "java" }
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem "a"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: file://#{gem_repo4}
+ specs:
+ a (0.9-java)
+
+ PLATFORMS
+ java
+
+ DEPENDENCIES
+ a
+ L
+
+ simulate_platform linux
+ end
+
+ it "allows updating" do
+ bundle! :update, :all => true
+ expect(the_bundle).to include_gem "a 1.1"
+ end
+
+ it "allows updating a specific gem" do
+ bundle! "update a"
+ expect(the_bundle).to include_gem "a 1.1"
+ end
+ end
+end
+
+RSpec.describe "bundle update without a Gemfile.lock" do
+ it "should not explode" do
+ build_repo2
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "rack", "1.0"
+ G
+
+ bundle "update", :all => bundle_update_requires_all?
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+end
+
+RSpec.describe "bundle update when a gem depends on a newer version of bundler" do
+ before(:each) do
+ build_repo2 do
+ build_gem "rails", "3.0.1" do |s|
+ s.add_dependency "bundler", Bundler::VERSION.succ
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0.1"
+ G
+ end
+
+ it "should explain that bundler conflicted", :bundler => "< 2" do
+ bundle "update", :all => bundle_update_requires_all?
+ expect(last_command.stdboth).not_to match(/in snapshot/i)
+ expect(last_command.bundler_err).to match(/current Bundler version/i).
+ and match(/perhaps you need to update bundler/i)
+ end
+
+ it "should warn that the newer version of Bundler would conflict", :bundler => "2" do
+ bundle! "update", :all => true
+ expect(last_command.bundler_err).to include("rails (3.0.1) has dependency bundler").
+ and include("so the dependency is being ignored")
+ expect(the_bundle).to include_gem "rails 3.0.1"
+ end
+end
+
+RSpec.describe "bundle update" do
+ it "shows the previous version of the gem when updated from rubygems source", :bundler => "< 2" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ G
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(out).to include("Using activesupport 2.3.5")
+
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(out).to include("Installing activesupport 3.0 (was 2.3.5)")
+ end
+
+ context "with suppress_install_using_messages set" do
+ before { bundle! "config 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://#{gem_repo4}"
+ gem "bar"
+ gem "foo"
+ G
+
+ bundle! "update", :all => bundle_update_requires_all?
+ out.gsub!(/RubyGems [\d\.]+ is not threadsafe.*\n?/, "")
+ expect(out).to include "Resolving dependencies...\nBundle updated!"
+
+ update_repo4 do
+ build_gem "foo", "2.0"
+ end
+
+ bundle! "update", :all => bundle_update_requires_all?
+ out.sub!("Removing foo (1.0)\n", "")
+ out.gsub!(/RubyGems [\d\.]+ is not threadsafe.*\n?/, "")
+ expect(out).to include strip_whitespace(<<-EOS).strip
+ Resolving dependencies...
+ Fetching foo 2.0 (was 1.0)
+ Installing foo 2.0 (was 1.0)
+ Bundle updated
+ EOS
+ end
+ end
+
+ it "shows error message when Gemfile.lock is not preset and gem is specified" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ G
+
+ bundle "update nonexisting"
+ expect(out).to include("This Bundle hasn't been installed yet. Run `bundle install` to update and install the bundled gems.")
+ expect(exitstatus).to eq(22) if exitstatus
+ end
+end
+
+RSpec.describe "bundle update --ruby" do
+ before do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '2.1.3'
+ ::RUBY_PATCHLEVEL = 100
+ ruby '~> 2.1.0'
+ G
+ bundle "update --ruby"
+ end
+
+ context "when the Gemfile removes the ruby" do
+ before do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '2.1.4'
+ ::RUBY_PATCHLEVEL = 222
+ G
+ end
+ it "removes the Ruby from the Gemfile.lock" do
+ bundle "update --ruby"
+
+ lockfile_should_be <<-L
+ GEM
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "when the Gemfile specified an updated Ruby version" do
+ before do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '2.1.4'
+ ::RUBY_PATCHLEVEL = 222
+ ruby '~> 2.1.0'
+ G
+ end
+ it "updates the Gemfile.lock with the latest version" do
+ bundle "update --ruby"
+
+ lockfile_should_be <<-L
+ GEM
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+
+ RUBY VERSION
+ ruby 2.1.4p222
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "when a different Ruby is being used than has been versioned" do
+ before do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '2.2.2'
+ ::RUBY_PATCHLEVEL = 505
+ ruby '~> 2.1.0'
+ G
+ end
+ it "shows a helpful error message" do
+ bundle "update --ruby"
+
+ expect(out).to include("Your Ruby version is 2.2.2, but your Gemfile specified ~> 2.1.0")
+ end
+ end
+
+ context "when updating Ruby version and Gemfile `ruby`" do
+ before do
+ install_gemfile <<-G
+ ::RUBY_VERSION = '1.8.3'
+ ::RUBY_PATCHLEVEL = 55
+ ruby '~> 1.8.0'
+ G
+ end
+ it "updates the Gemfile.lock with the latest version" do
+ bundle "update --ruby"
+
+ lockfile_should_be <<-L
+ GEM
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+
+ RUBY VERSION
+ ruby 1.8.3p55
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+end
+
+RSpec.describe "bundle update --bundler" do
+ it "updates the bundler version in the lockfile without re-resolving" do
+ build_repo4 do
+ build_gem "rack", "1.0"
+ end
+
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ gem "rack"
+ G
+ lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2')
+
+ FileUtils.rm_r gem_repo4
+
+ bundle! :update, :bundler => true, :verbose => true
+ expect(the_bundle).to include_gem "rack 1.0"
+
+ expect(the_bundle.locked_gems.bundler_version).to eq v(Bundler::VERSION)
+ end
+end
+
+# these specs are slow and focus on integration and therefore are not exhaustive. unit specs elsewhere handle that.
+RSpec.describe "bundle update conservative" do
+ context "patch and minor options" 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.0.1 1.1.0 2.0.0]
+ end
+
+ # establish a lockfile set to 1.4.3
+ install_gemfile <<-G
+ source "file://#{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://#{gem_repo4}"
+ gem 'foo'
+ gem 'qux'
+ G
+ end
+
+ context "patch preferred" do
+ it "single gem updates dependent gem to minor" do
+ bundle! "update --patch foo"
+
+ expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.0"
+ end
+
+ it "update all" do
+ bundle! "update --patch", :all => bundle_update_requires_all?
+
+ expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.1"
+ end
+ end
+
+ context "minor preferred" do
+ it "single gem updates dependent gem to major" do
+ bundle! "update --minor foo"
+
+ expect(the_bundle).to include_gems "foo 1.5.1", "bar 3.0.0", "qux 1.0.0"
+ end
+ end
+
+ context "strict" do
+ it "patch preferred" do
+ bundle! "update --patch foo bar --strict"
+
+ expect(the_bundle).to include_gems "foo 1.4.4", "bar 2.0.5", "qux 1.0.0"
+ end
+
+ it "minor preferred" do
+ bundle! "update --minor --strict", :all => bundle_update_requires_all?
+
+ expect(the_bundle).to include_gems "foo 1.5.0", "bar 2.1.1", "qux 1.1.0"
+ end
+ end
+ end
+
+ context "eager unlocking" do
+ before do
+ build_repo4 do
+ build_gem "isolated_owner", %w[1.0.1 1.0.2] do |s|
+ s.add_dependency "isolated_dep", "~> 2.0"
+ end
+ build_gem "isolated_dep", %w[2.0.1 2.0.2]
+
+ build_gem "shared_owner_a", %w[3.0.1 3.0.2] do |s|
+ s.add_dependency "shared_dep", "~> 5.0"
+ end
+ build_gem "shared_owner_b", %w[4.0.1 4.0.2] do |s|
+ s.add_dependency "shared_dep", "~> 5.0"
+ end
+ build_gem "shared_dep", %w[5.0.1 5.0.2]
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'isolated_owner'
+
+ gem 'shared_owner_a'
+ gem 'shared_owner_b'
+ G
+
+ lockfile <<-L
+ GEM
+ remote: file://#{gem_repo4}
+ specs:
+ isolated_dep (2.0.1)
+ isolated_owner (1.0.1)
+ isolated_dep (~> 2.0)
+ shared_dep (5.0.1)
+ shared_owner_a (3.0.1)
+ shared_dep (~> 5.0)
+ shared_owner_b (4.0.1)
+ shared_dep (~> 5.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ shared_owner_a
+ shared_owner_b
+ isolated_owner
+
+ BUNDLED WITH
+ 1.13.0
+ L
+ end
+
+ it "should eagerly unlock isolated dependency" do
+ bundle "update isolated_owner"
+
+ expect(the_bundle).to include_gems "isolated_owner 1.0.2", "isolated_dep 2.0.2", "shared_dep 5.0.1", "shared_owner_a 3.0.1", "shared_owner_b 4.0.1"
+ end
+
+ it "should eagerly unlock shared dependency" do
+ bundle "update shared_owner_a"
+
+ expect(the_bundle).to include_gems "isolated_owner 1.0.1", "isolated_dep 2.0.1", "shared_dep 5.0.2", "shared_owner_a 3.0.2", "shared_owner_b 4.0.1"
+ end
+
+ it "should not eagerly unlock with --conservative" do
+ bundle "update --conservative shared_owner_a isolated_owner"
+
+ expect(the_bundle).to include_gems "isolated_owner 1.0.2", "isolated_dep 2.0.2", "shared_dep 5.0.1", "shared_owner_a 3.0.2", "shared_owner_b 4.0.1"
+ end
+
+ it "should match bundle install conservative update behavior when not eagerly unlocking" do
+ gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem 'isolated_owner', '1.0.2'
+
+ gem 'shared_owner_a', '3.0.2'
+ gem 'shared_owner_b'
+ G
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "isolated_owner 1.0.2", "isolated_dep 2.0.2", "shared_dep 5.0.1", "shared_owner_a 3.0.2", "shared_owner_b 4.0.1"
+ end
+ end
+
+ context "error handling" do
+ before do
+ gemfile ""
+ end
+
+ it "raises if too many flags are provided" do
+ bundle "update --patch --minor", :all => bundle_update_requires_all?
+
+ expect(last_command.bundler_err).to eq "Provide only one of the following options: minor, patch"
+ end
+ end
+end
diff --git a/spec/bundler/commands/version_spec.rb b/spec/bundler/commands/version_spec.rb
new file mode 100644
index 0000000000..b919c25e0f
--- /dev/null
+++ b/spec/bundler/commands/version_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle version" do
+ context "with -v" do
+ it "outputs the version", :bundler => "< 2" do
+ bundle! "-v"
+ expect(out).to eq("Bundler version #{Bundler::VERSION}")
+ end
+
+ it "outputs the version", :bundler => "2" do
+ bundle! "-v"
+ expect(out).to eq(Bundler::VERSION)
+ end
+ end
+
+ context "with --version" do
+ it "outputs the version", :bundler => "< 2" do
+ bundle! "--version"
+ expect(out).to eq("Bundler version #{Bundler::VERSION}")
+ end
+
+ it "outputs the version", :bundler => "2" do
+ bundle! "--version"
+ expect(out).to eq(Bundler::VERSION)
+ end
+ end
+
+ context "with version" do
+ it "outputs the version with build metadata", :bundler => "< 2" do
+ bundle! "version"
+ expect(out).to match(/\ABundler version #{Regexp.escape(Bundler::VERSION)} \(\d{4}-\d{2}-\d{2} commit [a-fA-F0-9]{7,}\)\z/)
+ end
+
+ it "outputs the version with build metadata", :bundler => "2" do
+ bundle! "version"
+ expect(out).to match(/\A#{Regexp.escape(Bundler::VERSION)} \(\d{4}-\d{2}-\d{2} commit [a-fA-F0-9]{7,}\)\z/)
+ end
+ end
+end
diff --git a/spec/bundler/commands/viz_spec.rb b/spec/bundler/commands/viz_spec.rb
new file mode 100644
index 0000000000..3804d3561c
--- /dev/null
+++ b/spec/bundler/commands/viz_spec.rb
@@ -0,0 +1,149 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle viz", :ruby => "1.9.3", :bundler => "< 2", :if => Bundler.which("dot") do
+ let(:ruby_graphviz) do
+ graphviz_glob = base_system_gems.join("cache/ruby-graphviz*")
+ Pathname.glob(graphviz_glob).first
+ end
+
+ before do
+ system_gems ruby_graphviz
+ end
+
+ it "graphs gems from the Gemfile" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ bundle! "viz"
+ expect(out).to include("gem_graph.png")
+
+ bundle! "viz", :format => "debug"
+ expect(out).to eq(strip_whitespace(<<-DOT).strip)
+ digraph Gemfile {
+ concentrate = "true";
+ normalize = "true";
+ nodesep = "0.55";
+ edge[ weight = "2"];
+ node[ fontname = "Arial, Helvetica, SansSerif"];
+ edge[ fontname = "Arial, Helvetica, SansSerif" , fontsize = "12"];
+ default [style = "filled", fillcolor = "#B9B9D5", shape = "box3d", fontsize = "16", label = "default"];
+ rack [style = "filled", fillcolor = "#B9B9D5", label = "rack"];
+ default -> rack [constraint = "false"];
+ "rack-obama" [style = "filled", fillcolor = "#B9B9D5", label = "rack-obama"];
+ default -> "rack-obama" [constraint = "false"];
+ "rack-obama" -> rack;
+ }
+ debugging bundle viz...
+ DOT
+ end
+
+ it "graphs gems that are prereleases" do
+ build_repo2 do
+ build_gem "rack", "1.3.pre"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack", "= 1.3.pre"
+ gem "rack-obama"
+ G
+
+ bundle! "viz"
+ expect(out).to include("gem_graph.png")
+
+ bundle! "viz", :format => :debug, :version => true
+ expect(out).to eq(strip_whitespace(<<-EOS).strip)
+ digraph Gemfile {
+ concentrate = "true";
+ normalize = "true";
+ nodesep = "0.55";
+ edge[ weight = "2"];
+ node[ fontname = "Arial, Helvetica, SansSerif"];
+ edge[ fontname = "Arial, Helvetica, SansSerif" , fontsize = "12"];
+ default [style = "filled", fillcolor = "#B9B9D5", shape = "box3d", fontsize = "16", label = "default"];
+ rack [style = "filled", fillcolor = "#B9B9D5", label = "rack\\n1.3.pre"];
+ default -> rack [constraint = "false"];
+ "rack-obama" [style = "filled", fillcolor = "#B9B9D5", label = "rack-obama\\n1.0"];
+ default -> "rack-obama" [constraint = "false"];
+ "rack-obama" -> rack;
+ }
+ debugging bundle viz...
+ EOS
+ end
+
+ context "with another gem that has a graphviz file" do
+ before do
+ build_repo4 do
+ build_gem "graphviz", "999" do |s|
+ s.write("lib/graphviz.rb", "abort 'wrong graphviz gem loaded'")
+ end
+ end
+
+ system_gems ruby_graphviz, "graphviz-999", :gem_repo => gem_repo4
+ end
+
+ it "loads the correct ruby-graphviz gem" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ bundle! "viz", :format => "debug"
+ expect(out).to eq(strip_whitespace(<<-DOT).strip)
+ digraph Gemfile {
+ concentrate = "true";
+ normalize = "true";
+ nodesep = "0.55";
+ edge[ weight = "2"];
+ node[ fontname = "Arial, Helvetica, SansSerif"];
+ edge[ fontname = "Arial, Helvetica, SansSerif" , fontsize = "12"];
+ default [style = "filled", fillcolor = "#B9B9D5", shape = "box3d", fontsize = "16", label = "default"];
+ rack [style = "filled", fillcolor = "#B9B9D5", label = "rack"];
+ default -> rack [constraint = "false"];
+ "rack-obama" [style = "filled", fillcolor = "#B9B9D5", label = "rack-obama"];
+ default -> "rack-obama" [constraint = "false"];
+ "rack-obama" -> rack;
+ }
+ debugging bundle viz...
+ DOT
+ end
+ end
+
+ context "--without option" do
+ it "one group" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+
+ group :rails do
+ gem "rails"
+ end
+ G
+
+ bundle! "viz --without=rails"
+ expect(out).to include("gem_graph.png")
+ end
+
+ it "two groups" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+
+ group :rack do
+ gem "rack"
+ end
+
+ group :rails do
+ gem "rails"
+ end
+ G
+
+ bundle! "viz --without=rails:rack"
+ expect(out).to include("gem_graph.png")
+ end
+ end
+end
diff --git a/spec/bundler/install/allow_offline_install_spec.rb b/spec/bundler/install/allow_offline_install_spec.rb
new file mode 100644
index 0000000000..d4bb595771
--- /dev/null
+++ b/spec/bundler/install/allow_offline_install_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with :allow_offline_install" do
+ before do
+ bundle "config allow_offline_install true"
+ end
+
+ context "with no cached data locally" do
+ it "still installs" do
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "http://testgemserver.local"
+ gem "rack-obama"
+ G
+ expect(the_bundle).to include_gem("rack 1.0")
+ end
+
+ it "still fails when the network is down" do
+ install_gemfile <<-G, :artifice => "fail"
+ source "http://testgemserver.local"
+ gem "rack-obama"
+ G
+ expect(out).to include("Could not reach host testgemserver.local.")
+ expect(the_bundle).to_not be_locked
+ end
+ end
+
+ context "with cached data locally" do
+ it "will install from the compact index" do
+ system_gems ["rack-1.0.0"], :path => :bundle_path
+
+ bundle! "config clean false"
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "http://testgemserver.local"
+ gem "rack-obama"
+ gem "rack", "< 1.0"
+ G
+
+ expect(the_bundle).to include_gems("rack-obama 1.0", "rack 0.9.1")
+
+ gemfile <<-G
+ source "http://testgemserver.local"
+ gem "rack-obama"
+ G
+
+ bundle! :update, :artifice => "fail", :all => true
+ expect(last_command.stdboth).to include "Using the cached data for the new index because of a network error"
+
+ expect(the_bundle).to include_gems("rack-obama 1.0", "rack 1.0.0")
+ end
+
+ def break_git_remote_ops!
+ FileUtils.mkdir_p(tmp("broken_path"))
+ 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?
+ warn "git remote ops have been disabled"
+ exit 1
+ end
+ ENV["PATH"] = ENV["PATH"].sub(/^.*?:/, "")
+ exec("git", *ARGV)
+ RUBY
+ end
+
+ old_path = ENV["PATH"]
+ ENV["PATH"] = "#{tmp("broken_path")}:#{ENV["PATH"]}"
+ yield if block_given?
+ ensure
+ ENV["PATH"] = old_path if block_given?
+ end
+
+ it "will install from a cached git repo" do
+ git = build_git "a", "1.0.0", :path => lib_path("a")
+ update_git("a", :path => git.path, :branch => "new_branch")
+ install_gemfile! <<-G
+ gem "a", :git => #{git.path.to_s.dump}
+ G
+
+ break_git_remote_ops! { bundle! :update, :all => true }
+ expect(out).to include("Using cached git data because of network errors")
+ expect(the_bundle).to be_locked
+
+ break_git_remote_ops! do
+ install_gemfile! <<-G
+ gem "a", :git => #{git.path.to_s.dump}, :branch => "new_branch"
+ G
+ end
+ expect(out).to include("Using cached git data because of network errors")
+ expect(the_bundle).to be_locked
+ end
+ end
+end
diff --git a/spec/bundler/install/binstubs_spec.rb b/spec/bundler/install/binstubs_spec.rb
new file mode 100644
index 0000000000..23eb691ab8
--- /dev/null
+++ b/spec/bundler/install/binstubs_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install", :bundler => "< 2" 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
+ gemfile <<-G
+ require 'rubygems'
+ def Gem.bindir; "/usr/bin"; end
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ config "BUNDLE_SYSTEM_BINDIR" => system_gem_path("altbin").to_s
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(system_gem_path("altbin/rackup")).to exist
+ end
+ end
+
+ describe "when multiple gems contain the same exe", :bundler => "< 2" do
+ before do
+ build_repo2 do
+ build_gem "fake", "14" do |s|
+ s.executables = "rackup"
+ end
+ end
+
+ install_gemfile <<-G, :binstubs => true
+ source "file://#{gem_repo2}"
+ gem "fake"
+ gem "rack"
+ G
+ end
+
+ it "loads the correct spec's executable" do
+ gembin("rackup")
+ expect(out).to eq("1.2")
+ end
+ end
+end
diff --git a/spec/bundler/install/bundler_spec.rb b/spec/bundler/install/bundler_spec.rb
new file mode 100644
index 0000000000..42863ed89b
--- /dev/null
+++ b/spec/bundler/install/bundler_spec.rb
@@ -0,0 +1,177 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ describe "with bundler dependencies" do
+ before(:each) do
+ build_repo2 do
+ build_gem "rails", "3.0" do |s|
+ s.add_dependency "bundler", ">= 0.9.0.pre"
+ end
+ build_gem "bundler", "0.9.1"
+ build_gem "bundler", Bundler::VERSION
+ end
+ end
+
+ it "are forced to the current bundler version" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0"
+ G
+
+ expect(the_bundle).to include_gems "bundler #{Bundler::VERSION}"
+ end
+
+ it "are not added if not already present" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ expect(the_bundle).not_to include_gems "bundler #{Bundler::VERSION}"
+ end
+
+ it "causes a conflict if explicitly requesting a different version" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0"
+ gem "bundler", "0.9.2"
+ G
+
+ nice_error = <<-E.strip.gsub(/^ {8}/, "")
+ Bundler could not find compatible versions for gem "bundler":
+ In Gemfile:
+ bundler (= 0.9.2)
+
+ Current Bundler version:
+ bundler (#{Bundler::VERSION})
+ This Gemfile requires a different version of Bundler.
+ Perhaps you need to update Bundler by running `gem install bundler`?
+
+ Could not find gem 'bundler (= 0.9.2)' in any
+ E
+ expect(last_command.bundler_err).to include(nice_error)
+ end
+
+ it "works for gems with multiple versions in its dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "multiple_versioned_deps"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "multiple_versioned_deps"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "multiple_versioned_deps 1.0.0"
+ end
+
+ it "includes bundler in the bundle when it's a child dependency" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0"
+ G
+
+ run "begin; gem 'bundler'; puts 'WIN'; rescue Gem::LoadError; puts 'FAIL'; end"
+ expect(out).to eq("WIN")
+ end
+
+ it "allows gem 'bundler' when Bundler is not in the Gemfile or its dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ G
+
+ run "begin; gem 'bundler'; puts 'WIN'; rescue Gem::LoadError => e; puts e.backtrace; end"
+ expect(out).to eq("WIN")
+ end
+
+ it "causes a conflict if child dependencies conflict" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activemerchant"
+ gem "rails_fail"
+ 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_fail was resolved to 1.0, which depends on
+ activesupport (= 1.2.3)
+ E
+ expect(last_command.bundler_err).to include(nice_error)
+ end
+
+ it "causes a conflict if a child dependency conflicts with the Gemfile" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails_fail"
+ gem "activesupport", "2.3.5"
+ G
+
+ nice_error = <<-E.strip.gsub(/^ {8}/, "")
+ Bundler could not find compatible versions for gem "activesupport":
+ In Gemfile:
+ activesupport (= 2.3.5)
+
+ rails_fail was resolved to 1.0, which depends on
+ activesupport (= 1.2.3)
+ E
+ expect(last_command.bundler_err).to include(nice_error)
+ end
+
+ it "can install dependencies with newer bundler version with system gems", :ruby => "> 2" do
+ bundle! "config path.system true"
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0"
+ G
+
+ simulate_bundler_version "99999999.99.1"
+
+ bundle! "check", :env => { "BUNDLER_SPEC_IGNORE_COMPATIBILITY_GUARD" => "1" }
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ it "can install dependencies with newer bundler version with a local path", :ruby => "> 2" do
+ bundle! "config path .bundle"
+ bundle! "config global_path_appends_ruby_scope true"
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0"
+ G
+
+ simulate_bundler_version "99999999.99.1"
+
+ bundle! "check", :env => { "BUNDLER_SPEC_IGNORE_COMPATIBILITY_GUARD" => "1" }
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+
+ context "with allow_bundler_dependency_conflicts set" do
+ before { bundle! "config allow_bundler_dependency_conflicts true" }
+
+ it "are forced to the current bundler version with warnings when no compatible version is found" do
+ build_repo4 do
+ build_gem "requires_nonexistant_bundler" do |s|
+ s.add_runtime_dependency "bundler", "99.99.99.99"
+ end
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo4}"
+ gem "requires_nonexistant_bundler"
+ G
+
+ expect(out).to include "requires_nonexistant_bundler (1.0) has dependency bundler (= 99.99.99.99), " \
+ "which is unsatisfied by the current bundler version #{Bundler::VERSION}, so the dependency is being ignored"
+
+ expect(the_bundle).to include_gems "bundler #{Bundler::VERSION}", "requires_nonexistant_bundler 1.0"
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb
new file mode 100644
index 0000000000..3b9d68982a
--- /dev/null
+++ b/spec/bundler/install/deploy_spec.rb
@@ -0,0 +1,423 @@
+# frozen_string_literal: true
+
+RSpec.describe "install with --deployment or --frozen" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ context "with CLI flags", :bundler => "< 2" do
+ it "fails without a lockfile and says that --deployment requires a lock" do
+ bundle "install --deployment"
+ expect(out).to include("The --deployment flag requires a Gemfile.lock")
+ end
+
+ it "fails without a lockfile and says that --frozen requires a lock" do
+ bundle "install --frozen"
+ expect(out).to include("The --frozen flag requires a Gemfile.lock")
+ end
+
+ it "disallows --deployment --system" do
+ bundle "install --deployment --system"
+ expect(out).to include("You have specified both --deployment")
+ expect(out).to include("Please choose only one option")
+ expect(exitstatus).to eq(15) if exitstatus
+ end
+
+ it "disallows --deployment --path --system" do
+ bundle "install --deployment --path . --system"
+ expect(out).to include("You have specified both --path")
+ expect(out).to include("as well as --system")
+ expect(out).to include("Please choose only one option")
+ expect(exitstatus).to eq(15) if exitstatus
+ end
+
+ it "works after you try to deploy without a lock" do
+ bundle "install --deployment"
+ bundle! :install
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ it "still works if you are not in the app directory and specify --gemfile" do
+ bundle "install"
+ Dir.chdir tmp do
+ simulate_new_machine
+ bundle! :install,
+ forgotten_command_line_options(:gemfile => "#{tmp}/bundled_app/Gemfile",
+ :deployment => true,
+ :path => "vendor/bundle")
+ end
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "works if you exclude a group with a git gem" do
+ build_git "foo"
+ gemfile <<-G
+ group :test do
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ end
+ G
+ bundle :install
+ bundle! :install, forgotten_command_line_options(:deployment => true, :without => "test")
+ end
+
+ it "works when you bundle exec bundle", :ruby_repo do
+ bundle :install
+ bundle "install --deployment"
+ bundle! "exec bundle check"
+ end
+
+ it "works when using path gems from the same path and the version is specified" do
+ build_lib "foo", :path => lib_path("nested/foo")
+ build_lib "bar", :path => lib_path("nested/bar")
+ gemfile <<-G
+ gem "foo", "1.0", :path => "#{lib_path("nested")}"
+ gem "bar", :path => "#{lib_path("nested")}"
+ G
+
+ bundle! :install
+ bundle! :install, forgotten_command_line_options(:deployment => true)
+ end
+
+ it "works when there are credentials in the source URL" do
+ install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true)
+ source "http://user:pass@localgemserver.test/"
+
+ gem "rack-obama", ">= 1.0"
+ G
+
+ bundle! :install, forgotten_command_line_options(:deployment => true).merge(:artifice => "endpoint_strict_basic_authentication")
+ end
+
+ it "works with sources given by a block" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}" do
+ gem "rack"
+ end
+ G
+
+ bundle! :install, forgotten_command_line_options(:deployment => true)
+
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ describe "with an existing lockfile" do
+ before do
+ bundle "install"
+ end
+
+ it "works with the --deployment flag if you didn't change anything", :bundler => "< 2" do
+ bundle! "install --deployment"
+ end
+
+ it "works with the --frozen flag if you didn't change anything", :bundler => "< 2" do
+ bundle! "install --frozen"
+ 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
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ bundle :install, forgotten_command_line_options(:deployment => true)
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile")
+ expect(out).to include("* rack-obama")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "works if a path gem is missing but is in a without group" do
+ build_lib "path_gem"
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rake"
+ gem "path_gem", :path => "#{lib_path("path_gem-1.0")}", :group => :development
+ G
+ expect(the_bundle).to include_gems "path_gem 1.0"
+ FileUtils.rm_r lib_path("path_gem-1.0")
+
+ bundle! :install, forgotten_command_line_options(:path => ".bundle", :without => "development", :deployment => true).merge(:env => { :DEBUG => "1" })
+ run! "puts :WIN"
+ expect(out).to eq("WIN")
+ end
+
+ it "explodes if a path gem is missing" do
+ build_lib "path_gem"
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rake"
+ gem "path_gem", :path => "#{lib_path("path_gem-1.0")}", :group => :development
+ G
+ expect(the_bundle).to include_gems "path_gem 1.0"
+ FileUtils.rm_r lib_path("path_gem-1.0")
+
+ bundle :install, forgotten_command_line_options(:path => ".bundle", :deployment => true)
+ expect(out).to include("The path `#{lib_path("path_gem-1.0")}` does not exist.")
+ end
+
+ it "can have --frozen set via an environment variable", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ ENV["BUNDLE_FROZEN"] = "1"
+ bundle "install"
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile")
+ expect(out).to include("* rack-obama")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "can have --deployment set via an environment variable" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ ENV["BUNDLE_DEPLOYMENT"] = "true"
+ bundle "install"
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile")
+ expect(out).to include("* rack-obama")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "can have --frozen set to false via an environment variable" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ ENV["BUNDLE_FROZEN"] = "false"
+ ENV["BUNDLE_DEPLOYMENT"] = "false"
+ bundle "install"
+ expect(out).not_to include("deployment mode")
+ expect(out).not_to include("You have added to the Gemfile")
+ expect(out).not_to include("* rack-obama")
+ end
+
+ it "explodes with the --frozen flag if you make a change and don't check in the lockfile", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama", "1.1"
+ G
+
+ bundle :install, forgotten_command_line_options(:frozen => true)
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile")
+ expect(out).to include("* rack-obama (= 1.1)")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you remove a gem and don't check in the lockfile" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ G
+
+ bundle :install, forgotten_command_line_options(:deployment => true)
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile:\n* activesupport\n\n")
+ expect(out).to include("You have deleted from the Gemfile:\n* rack")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you add a source" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "git://hubz.com"
+ G
+
+ bundle :install, forgotten_command_line_options(:deployment => true)
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile:\n* source: git://hubz.com (at master)")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you unpin a source" do
+ build_git "rack"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-1.0")}"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle :install, forgotten_command_line_options(:deployment => true)
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have deleted from the Gemfile:\n* source: #{lib_path("rack-1.0")} (at master@#{revision_for(lib_path("rack-1.0"))[0..6]}")
+ expect(out).not_to include("You have added to the Gemfile")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you unpin a source, leaving it pinned somewhere else" do
+ build_lib "foo", :path => lib_path("rack/foo")
+ build_git "rack", :path => lib_path("rack")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack")}"
+ gem "foo", :git => "#{lib_path("rack")}"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "foo", :git => "#{lib_path("rack")}"
+ G
+
+ bundle :install, forgotten_command_line_options(:deployment => true)
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have changed in the Gemfile:\n* rack from `no specified source` to `#{lib_path("rack")} (at master@#{revision_for(lib_path("rack"))[0..6]})`")
+ expect(out).not_to include("You have added to the Gemfile")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ end
+
+ context "when replacing a host with the same host with credentials" do
+ let(:success_message) do
+ if Bundler::VERSION.split(".", 2).first == "1"
+ "Could not reach host localgemserver.test"
+ else
+ "Bundle complete!"
+ end
+ end
+
+ before do
+ install_gemfile <<-G
+ source "http://user_name:password@localgemserver.test/"
+ gem "rack"
+ G
+
+ lockfile <<-G
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{local}
+
+ DEPENDENCIES
+ rack
+ G
+ end
+
+ it "prevents the replace by default" do
+ bundle :install, forgotten_command_line_options(:deployment => true)
+
+ expect(out).to match(/The list of sources changed/)
+ end
+
+ context "when allow_deployment_source_credential_changes is true" do
+ before { bundle! "config allow_deployment_source_credential_changes true" }
+
+ it "allows the replace" do
+ bundle :install, forgotten_command_line_options(:deployment => true)
+
+ expect(out).to match(/#{success_message}/)
+ end
+ end
+
+ context "when allow_deployment_source_credential_changes is false" do
+ before { bundle! "config allow_deployment_source_credential_changes false" }
+
+ it "prevents the replace" do
+ bundle :install, forgotten_command_line_options(:deployment => true)
+
+ expect(out).to match(/The list of sources changed/)
+ end
+ end
+
+ context "when BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES env var is true" do
+ before { ENV["BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES"] = "true" }
+
+ it "allows the replace" do
+ bundle :install, forgotten_command_line_options(:deployment => true)
+
+ expect(out).to match(/#{success_message}/)
+ end
+ end
+
+ context "when BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES env var is false" do
+ before { ENV["BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES"] = "false" }
+
+ it "prevents the replace" do
+ bundle :install, forgotten_command_line_options(:deployment => true)
+
+ expect(out).to match(/The list of sources changed/)
+ end
+ end
+ end
+
+ it "remembers that the bundle is frozen at runtime" do
+ bundle! :lock
+
+ bundle! "config deployment true"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0.0"
+ gem "rack-obama"
+ G
+
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(err).to include strip_whitespace(<<-E).strip
+The dependencies in your gemfile changed
+
+You have added to the Gemfile:
+* rack (= 1.0.0)
+* rack-obama
+
+You have deleted from the Gemfile:
+* rack
+ E
+ end
+ end
+
+ context "with path in Gemfile and packed" do
+ it "works fine after bundle package and bundle install --local" do
+ build_lib "foo", :path => lib_path("foo")
+ install_gemfile! <<-G
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ bundle! :install
+ expect(the_bundle).to include_gems "foo 1.0"
+ bundle! :package, forgotten_command_line_options([:all, :cache_all] => true)
+ expect(bundled_app("vendor/cache/foo")).to be_directory
+
+ bundle! "install --local"
+ expect(out).to include("Updating files in vendor/cache")
+
+ simulate_new_machine
+ bundle! "install --verbose", forgotten_command_line_options(:deployment => true)
+ expect(out).not_to include("You are trying to install in deployment mode after changing your Gemfile")
+ 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")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/failure_spec.rb b/spec/bundler/install/failure_spec.rb
new file mode 100644
index 0000000000..b4cdf13857
--- /dev/null
+++ b/spec/bundler/install/failure_spec.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ context "installing a gem fails" do
+ it "prints out why that gem was being installed" do
+ build_repo2 do
+ build_gem "activesupport", "2.3.2" do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ abort "make installing activesupport-2.3.2 fail"
+ end
+ RUBY
+ end
+ end
+
+ install_gemfile <<-G
+ source "file:\/\/localhost#{gem_repo2}"
+ gem "rails"
+ G
+ expect(last_command.bundler_err).to end_with(normalize_uri_file(<<-M.strip))
+An error occurred while installing activesupport (2.3.2), and Bundler cannot continue.
+Make sure that `gem install activesupport -v '2.3.2' --source 'file://localhost#{gem_repo2}/'` succeeds before bundling.
+
+In Gemfile:
+ rails was resolved to 2.3.2, which depends on
+ actionmailer was resolved to 2.3.2, which depends on
+ activesupport
+ M
+ end
+
+ context "when installing a git gem" do
+ it "does not tell the user to run 'gem install'" do
+ build_git "activesupport", "2.3.2", :path => lib_path("activesupport") do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ abort "make installing activesupport-2.3.2 fail"
+ end
+ RUBY
+ end
+
+ install_gemfile <<-G
+ source "file:\/\/localhost#{gem_repo1}"
+ gem "rails"
+ gem "activesupport", :git => "#{lib_path("activesupport")}"
+ G
+
+ expect(last_command.bundler_err).to end_with(<<-M.strip)
+An error occurred while installing activesupport (2.3.2), and Bundler cannot continue.
+
+In Gemfile:
+ rails was resolved to 2.3.2, which depends on
+ actionmailer was resolved to 2.3.2, which depends on
+ activesupport
+ M
+ end
+ end
+
+ context "when installing a gem using a git block" do
+ it "does not tell the user to run 'gem install'" do
+ build_git "activesupport", "2.3.2", :path => lib_path("activesupport") do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ abort "make installing activesupport-2.3.2 fail"
+ end
+ RUBY
+ end
+
+ install_gemfile <<-G
+ source "file:\/\/localhost#{gem_repo1}"
+ gem "rails"
+
+ git "#{lib_path("activesupport")}" do
+ gem "activesupport"
+ end
+ G
+
+ expect(last_command.bundler_err).to end_with(<<-M.strip)
+An error occurred while installing activesupport (2.3.2), and Bundler cannot continue.
+
+
+In Gemfile:
+ rails was resolved to 2.3.2, which depends on
+ actionmailer was resolved to 2.3.2, which depends on
+ activesupport
+ M
+ end
+ end
+
+ it "prints out the hint for the remote source when available" do
+ build_repo2 do
+ build_gem "activesupport", "2.3.2" do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ abort "make installing activesupport-2.3.2 fail"
+ end
+ RUBY
+ end
+ end
+
+ build_repo4 do
+ build_gem "a"
+ end
+
+ install_gemfile <<-G
+ source "file:\/\/localhost#{gem_repo4}"
+ source "file:\/\/localhost#{gem_repo2}" do
+ gem "rails"
+ end
+ G
+ expect(last_command.bundler_err).to end_with(normalize_uri_file(<<-M.strip))
+An error occurred while installing activesupport (2.3.2), and Bundler cannot continue.
+Make sure that `gem install activesupport -v '2.3.2' --source 'file://localhost#{gem_repo2}/'` succeeds before bundling.
+
+In Gemfile:
+ rails was resolved to 2.3.2, which depends on
+ actionmailer was resolved to 2.3.2, which depends on
+ activesupport
+ M
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/eval_gemfile_spec.rb b/spec/bundler/install/gemfile/eval_gemfile_spec.rb
new file mode 100644
index 0000000000..035d3692aa
--- /dev/null
+++ b/spec/bundler/install/gemfile/eval_gemfile_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
+ before do
+ build_lib("gunks", :path => bundled_app.join("gems/gunks")) do |s|
+ s.name = "gunks"
+ s.version = "0.0.1"
+ end
+ end
+
+ context "eval-ed Gemfile points to an internal gemspec" do
+ before do
+ create_file "Gemfile-other", <<-G
+ gemspec :path => 'gems/gunks'
+ G
+ end
+
+ it "installs the gemspec specified gem" do
+ install_gemfile <<-G
+ eval_gemfile 'Gemfile-other'
+ G
+ expect(out).to include("Resolving dependencies")
+ expect(out).to include("Bundle complete")
+
+ expect(the_bundle).to include_gem "gunks 0.0.1", :source => "path@#{bundled_app("gems", "gunks")}"
+ end
+ end
+
+ context "eval-ed Gemfile has relative-path gems" do
+ before do
+ build_lib("a", :path => "gems/a")
+ create_file "nested/Gemfile-nested", <<-G
+ gem "a", :path => "../gems/a"
+ G
+
+ gemfile <<-G
+ eval_gemfile "nested/Gemfile-nested"
+ G
+ end
+
+ it "installs the path gem" do
+ bundle! :install
+ expect(the_bundle).to include_gem("a 1.0")
+ end
+
+ # Make sure that we are properly comparing path based gems between the
+ # parsed lockfile and the evaluated gemfile.
+ it "bundles with --deployment" do
+ bundle! :install
+ bundle! :install, forgotten_command_line_options(:deployment => true)
+ end
+ end
+
+ context "Gemfile uses gemspec paths after eval-ing a Gemfile" do
+ before { create_file "other/Gemfile-other" }
+
+ it "installs the gemspec specified gem" do
+ install_gemfile <<-G
+ eval_gemfile 'other/Gemfile-other'
+ gemspec :path => 'gems/gunks'
+ G
+ expect(out).to include("Resolving dependencies")
+ expect(out).to include("Bundle complete")
+
+ expect(the_bundle).to include_gem "gunks 0.0.1", :source => "path@#{bundled_app("gems", "gunks")}"
+ end
+ end
+
+ context "eval-ed Gemfile references other gemfiles" do
+ it "works with relative paths" do
+ create_file "other/Gemfile-other", "gem 'rack'"
+ create_file "other/Gemfile", "eval_gemfile 'Gemfile-other'"
+ create_file "Gemfile-alt", <<-G
+ source "file:#{gem_repo1}"
+ eval_gemfile "other/Gemfile"
+ G
+ install_gemfile! "eval_gemfile File.expand_path('Gemfile-alt')"
+
+ expect(the_bundle).to include_gem "rack 1.0.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb
new file mode 100644
index 0000000000..7ce037730e
--- /dev/null
+++ b/spec/bundler/install/gemfile/gemspec_spec.rb
@@ -0,0 +1,672 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install from an existing gemspec" do
+ before(:each) do
+ build_repo2 do
+ build_gem "bar"
+ build_gem "bar-dev"
+ end
+ end
+
+ it "should install runtime and development dependencies" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("Gemfile", "source :rubygems\ngemspec")
+ s.add_dependency "bar", "=1.0.0"
+ s.add_development_dependency "bar-dev", "=1.0.0"
+ end
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0.0"
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development
+ end
+
+ it "that is hidden should install runtime and development dependencies" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("Gemfile", "source :rubygems\ngemspec")
+ s.add_dependency "bar", "=1.0.0"
+ s.add_development_dependency "bar-dev", "=1.0.0"
+ end
+ FileUtils.mv tmp.join("foo", "foo.gemspec"), tmp.join("foo", ".gemspec")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0.0"
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development
+ end
+
+ it "should handle a list of requirements" do
+ update_repo2 do
+ build_gem "baz", "1.0"
+ build_gem "baz", "1.1"
+ end
+
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("Gemfile", "source :rubygems\ngemspec")
+ s.add_dependency "baz", ">= 1.0", "< 1.1"
+ end
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ expect(the_bundle).to include_gems "baz 1.0"
+ end
+
+ it "should raise if there are no gemspecs available" do
+ build_lib("foo", :path => tmp.join("foo"), :gemspec => false)
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+ expect(last_command.bundler_err).to match(/There are no gemspecs at #{tmp.join('foo')}/)
+ end
+
+ it "should raise if there are too many gemspecs available" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("foo2.gemspec", build_spec("foo", "4.0").first.to_ruby)
+ end
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+ expect(last_command.bundler_err).to match(/There are multiple gemspecs at #{tmp.join('foo')}/)
+ end
+
+ it "should pick a specific gemspec" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("foo2.gemspec", "")
+ s.add_dependency "bar", "=1.0.0"
+ s.add_development_dependency "bar-dev", "=1.0.0"
+ end
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0.0"
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development
+ end
+
+ it "should use a specific group for development dependencies" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("foo2.gemspec", "")
+ s.add_dependency "bar", "=1.0.0"
+ s.add_development_dependency "bar-dev", "=1.0.0"
+ end
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo', :development_group => :dev
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0.0"
+ expect(the_bundle).not_to include_gems "bar-dev 1.0.0", :groups => :development
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :dev
+ end
+
+ it "should match a lockfile even if the gemspec defines development dependencies" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("Gemfile", "source 'file://#{gem_repo1}'\ngemspec")
+ s.add_dependency "actionpack", "=2.3.2"
+ s.add_development_dependency "rake", "=10.0.2"
+ end
+
+ Dir.chdir(tmp.join("foo")) do
+ bundle "install"
+ # This should really be able to rely on $stderr, but, it's not written
+ # right, so we can't. In fact, this is a bug negation test, and so it'll
+ # ghost pass in future, and will only catch a regression if the message
+ # doesn't change. Exit codes should be used correctly (they can be more
+ # than just 0 and 1).
+ output = bundle("install --deployment")
+ 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/)
+ end
+ end
+
+ it "should match a lockfile without needing to re-resolve" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.add_dependency "rack"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ bundle! "install", :verbose => true
+
+ message = "Found no changes, using resolution from the lockfile"
+ expect(out.scan(message).size).to eq(1)
+ end
+
+ it "should match a lockfile without needing to re-resolve with development dependencies" do
+ simulate_platform java
+
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.add_dependency "rack"
+ s.add_development_dependency "thin"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ bundle! "install", :verbose => true
+
+ message = "Found no changes, using resolution from the lockfile"
+ expect(out.scan(message).size).to eq(1)
+ end
+
+ it "should match a lockfile on non-ruby platforms with a transitive platform dependency" do
+ simulate_platform java
+ simulate_ruby_engine "jruby"
+
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.add_dependency "platform_specific"
+ end
+
+ system_gems "platform_specific-1.0-java", :path => :bundle_path, :keep_path => true
+
+ install_gemfile! <<-G
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ bundle! "update --bundler", :verbose => true
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 JAVA"
+ end
+
+ it "should evaluate the gemspec in its directory" do
+ build_lib("foo", :path => tmp.join("foo"))
+ File.open(tmp.join("foo/foo.gemspec"), "w") do |s|
+ s.write "raise 'ahh' unless Dir.pwd == '#{tmp.join("foo")}'"
+ end
+
+ install_gemfile <<-G
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+ expect(last_command.stdboth).not_to include("ahh")
+ end
+
+ it "allows the gemspec to activate other gems" do
+ ENV["BUNDLE_PATH__SYSTEM"] = "true"
+ # see https://github.com/bundler/bundler/issues/5409
+ #
+ # issue was caused by rubygems having an unresolved gem during a require,
+ # so emulate that
+ system_gems %w[rack-1.0.0 rack-0.9.1 rack-obama-1.0]
+
+ build_lib("foo", :path => bundled_app)
+ gemspec = bundled_app("foo.gemspec").read
+ bundled_app("foo.gemspec").open("w") do |f|
+ f.write "#{gemspec.strip}.tap { gem 'rack-obama'; require 'rack-obama' }"
+ end
+
+ install_gemfile! <<-G
+ gemspec
+ G
+
+ expect(the_bundle).to include_gem "foo 1.0"
+ end
+
+ it "allows conflicts" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.version = "1.0.0"
+ s.add_dependency "bar", "= 1.0.0"
+ end
+ build_gem "deps", :to_bundle => true do |s|
+ s.add_dependency "foo", "= 0.0.1"
+ end
+ build_gem "foo", "0.0.1", :to_bundle => true
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "deps"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0.0"
+ end
+
+ it "does not break Gem.finish_resolve with conflicts", :rubygems => ">= 2" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.version = "1.0.0"
+ s.add_dependency "bar", "= 1.0.0"
+ end
+ update_repo2 do
+ build_gem "deps" do |s|
+ s.add_dependency "foo", "= 0.0.1"
+ end
+ build_gem "foo", "0.0.1"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "deps"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0.0"
+
+ run! "Gem.finish_resolve; puts 'WIN'"
+ expect(out).to eq("WIN")
+ end
+
+ context "in deployment mode" do
+ context "when the lockfile was not updated after a change to the gemspec's dependencies" do
+ it "reports that installation failed" do
+ build_lib "cocoapods", :path => bundled_app do |s|
+ s.add_dependency "activesupport", ">= 1"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gemspec
+ G
+
+ expect(the_bundle).to include_gems("cocoapods 1.0", "activesupport 2.3.5")
+
+ build_lib "cocoapods", :path => bundled_app do |s|
+ s.add_dependency "activesupport", ">= 1.0.1"
+ end
+
+ bundle :install, forgotten_command_line_options(:deployment => true)
+
+ expect(out).to include("changed")
+ end
+ end
+ end
+
+ context "when child gemspecs conflict with a released gemspec" do
+ before do
+ # build the "parent" gem that depends on another gem in the same repo
+ build_lib "source_conflict", :path => bundled_app do |s|
+ s.add_dependency "rack_middleware"
+ end
+
+ # build the "child" gem that is the same version as a released gem, but
+ # has completely different and conflicting dependency requirements
+ build_lib "rack_middleware", "1.0", :path => bundled_app("rack_middleware") do |s|
+ s.add_dependency "rack", "1.0" # anything other than 0.9.1
+ end
+ end
+
+ it "should install the child gemspec's deps" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gemspec
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ context "with a lockfile and some missing dependencies" do
+ let(:source_uri) { "http://localgemserver.test" }
+
+ context "previously bundled for Ruby" do
+ let(:platform) { "ruby" }
+ let(:explicit_platform) { false }
+
+ before do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.add_dependency "rack", "=1.0.0"
+ end
+
+ if explicit_platform
+ create_file(
+ tmp.join("foo", "foo-#{platform}.gemspec"),
+ build_spec("foo", "1.0", platform) do
+ dep "rack", "=1.0.0"
+ @spec.authors = "authors"
+ @spec.summary = "summary"
+ end.first.to_ruby
+ )
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gemspec :path => "../foo"
+ G
+
+ lockfile <<-L
+ PATH
+ remote: ../foo
+ specs:
+ foo (1.0)
+ rack (= 1.0.0)
+
+ GEM
+ remote: #{source_uri}
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ context "using JRuby with explicit platform" do
+ let(:platform) { "java" }
+ let(:explicit_platform) { true }
+
+ it "should install" do
+ simulate_ruby_engine "jruby" do
+ simulate_platform "java" do
+ results = bundle "install", :artifice => "endpoint"
+ expect(results).to include("Installing rack 1.0.0")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+ end
+
+ context "using JRuby" do
+ let(:platform) { "java" }
+
+ it "should install" do
+ simulate_ruby_engine "jruby" do
+ simulate_platform "java" do
+ results = bundle "install", :artifice => "endpoint"
+ expect(results).to include("Installing rack 1.0.0")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+ end
+
+ context "using Windows" do
+ it "should install" do
+ simulate_windows do
+ results = bundle "install", :artifice => "endpoint"
+ expect(results).to include("Installing rack 1.0.0")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+ end
+
+ context "bundled for ruby and jruby" do
+ let(:platform_specific_type) { :runtime }
+ let(:dependency) { "platform_specific" }
+ before do
+ build_repo2 do
+ build_gem "indirect_platform_specific" do |s|
+ s.add_runtime_dependency "platform_specific"
+ end
+ end
+
+ build_lib "foo", :path => "." do |s|
+ if platform_specific_type == :runtime
+ s.add_runtime_dependency dependency
+ elsif platform_specific_type == :development
+ s.add_development_dependency dependency
+ else
+ raise "wrong dependency type #{platform_specific_type}, can only be :development or :runtime"
+ end
+ end
+
+ %w[ruby jruby].each do |platform|
+ simulate_platform(platform) do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo2}"
+ gemspec
+ G
+ end
+ end
+ end
+
+ context "on ruby", :bundler => "< 2" do
+ before do
+ simulate_platform("ruby")
+ bundle :install
+ end
+
+ context "as a runtime dependency" do
+ it "keeps java dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
+ expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L))
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+ platform_specific
+
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "as a development dependency" do
+ let(:platform_specific_type) { :development }
+
+ it "keeps java dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
+ expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L))
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ foo!
+ platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "with an indirect platform-specific development dependency" do
+ let(:platform_specific_type) { :development }
+ let(:dependency) { "indirect_platform_specific" }
+
+ it "keeps java dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "indirect_platform_specific 1.0", "platform_specific 1.0 RUBY"
+ expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L))
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ indirect_platform_specific (1.0)
+ platform_specific
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ foo!
+ indirect_platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+ end
+
+ context "on ruby", :bundler => "2" do
+ before do
+ simulate_platform("ruby")
+ bundle :install
+ end
+
+ context "as a runtime dependency" do
+ it "keeps java dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
+ expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L))
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+ platform_specific
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "as a development dependency" do
+ let(:platform_specific_type) { :development }
+
+ it "keeps java dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
+ expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L))
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ foo!
+ platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "with an indirect platform-specific development dependency" do
+ let(:platform_specific_type) { :development }
+ let(:dependency) { "indirect_platform_specific" }
+
+ it "keeps java dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "indirect_platform_specific 1.0", "platform_specific 1.0 RUBY"
+ expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L))
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ indirect_platform_specific (1.0)
+ platform_specific
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ foo!
+ indirect_platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+ end
+ end
+ end
+
+ context "with multiple platforms" do
+ before do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.version = "1.0.0"
+ s.add_development_dependency "rack"
+ s.write "foo-universal-java.gemspec", build_spec("foo", "1.0.0", "universal-java") {|sj| sj.runtime "rack", "1.0.0" }.first.to_ruby
+ end
+ end
+
+ it "installs the ruby platform gemspec" do
+ simulate_platform "ruby"
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0.0", "rack 1.0.0"
+ end
+
+ it "installs the ruby platform gemspec and skips dev deps with --without development" do
+ simulate_platform "ruby"
+
+ install_gemfile! <<-G, forgotten_command_line_options(:without => "development")
+ source "file://#{gem_repo1}"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ expect(the_bundle).to include_gem "foo 1.0.0"
+ expect(the_bundle).not_to include_gem "rack"
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb
new file mode 100644
index 0000000000..57d83a5295
--- /dev/null
+++ b/spec/bundler/install/gemfile/git_spec.rb
@@ -0,0 +1,1351 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with git sources" do
+ describe "when floating on master" do
+ before :each do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+ end
+
+ it "fetches gems" do
+ expect(the_bundle).to include_gems("foo 1.0")
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" unless defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "caches the git repo", :bundler => "< 2" do
+ expect(Dir["#{default_bundle_path}/cache/bundler/git/foo-1.0-*"]).to have_attributes :size => 1
+ end
+
+ it "caches the git repo globally" do
+ simulate_new_machine
+ bundle! "config global_gem_cache true"
+ bundle! :install
+ expect(Dir["#{home}/.bundle/cache/git/foo-1.0-*"]).to have_attributes :size => 1
+ end
+
+ it "caches the evaluated gemspec" do
+ git = update_git "foo" do |s|
+ s.executables = ["foobar"] # we added this the first time, so keep it now
+ s.files = ["bin/foobar"] # updating git nukes the files list
+ foospec = s.to_ruby.gsub(/s\.files.*/, 's.files = `git ls-files -z`.split("\x0")')
+ s.write "foo.gemspec", foospec
+ end
+
+ 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
+ file_code = File.read(spec_file)
+ expect(file_code).to eq(ruby_code)
+ end
+
+ it "does not update the git source implicitly" do
+ update_git "foo"
+
+ in_app_root2 do
+ install_gemfile bundled_app2("Gemfile"), <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+ end
+
+ in_app_root do
+ run <<-RUBY
+ require 'foo'
+ puts "fail" if defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to be_empty
+ end
+ end
+
+ it "sets up git gem executables on the path" do
+ bundle "exec foobar"
+ expect(out).to eq("1.0")
+ end
+
+ it "complains if pinned specs don't exist in the git repo" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", "1.1", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(out).to include("The source contains 'foo' at: 1.0")
+ end
+
+ it "complains with version and platform if pinned specs don't exist in the git repo" do
+ simulate_platform "java"
+
+ build_git "only_java" do |s|
+ s.platform = "java"
+ end
+
+ install_gemfile <<-G
+ platforms :jruby do
+ gem "only_java", "1.2", :git => "#{lib_path("only_java-1.0-java")}"
+ end
+ G
+
+ expect(out).to include("The source contains 'only_java' at: 1.0 java")
+ end
+
+ it "complains with multiple versions and platforms if pinned specs don't exist in the git repo" do
+ simulate_platform "java"
+
+ build_git "only_java", "1.0" do |s|
+ s.platform = "java"
+ end
+
+ build_git "only_java", "1.1" do |s|
+ s.platform = "java"
+ s.write "only_java1-0.gemspec", File.read("#{lib_path("only_java-1.0-java")}/only_java.gemspec")
+ end
+
+ install_gemfile <<-G
+ platforms :jruby do
+ gem "only_java", "1.2", :git => "#{lib_path("only_java-1.1-java")}"
+ end
+ G
+
+ expect(out).to include("The source contains 'only_java' at: 1.0 java, 1.1 java")
+ end
+
+ it "still works after moving the application directory" do
+ bundle "install --path vendor/bundle"
+ FileUtils.mv bundled_app, tmp("bundled_app.bck")
+
+ Dir.chdir tmp("bundled_app.bck")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "can still install after moving the application directory" do
+ bundle "install --path vendor/bundle"
+ FileUtils.mv bundled_app, tmp("bundled_app.bck")
+
+ update_git "foo", "1.1", :path => lib_path("foo-1.0")
+
+ Dir.chdir tmp("bundled_app.bck")
+ gemfile tmp("bundled_app.bck/Gemfile"), <<-G
+ source "file://#{gem_repo1}"
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+
+ gem "rack", "1.0"
+ G
+
+ bundle "update foo"
+
+ expect(the_bundle).to include_gems "foo 1.1", "rack 1.0"
+ end
+ end
+
+ describe "with an empty git block" do
+ before do
+ build_git "foo"
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ git "#{lib_path("foo-1.0")}" do
+ # this page left intentionally blank
+ end
+ G
+ end
+
+ it "does not explode" do
+ bundle "install"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ describe "when specifying a revision" do
+ before(:each) do
+ build_git "foo"
+ @revision = revision_for(lib_path("foo-1.0"))
+ update_git "foo"
+ end
+
+ it "works" do
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}", :ref => "#{@revision}" do
+ gem "foo"
+ end
+ G
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" unless defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "works when the revision is a symbol" do
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}", :ref => #{@revision.to_sym.inspect} do
+ gem "foo"
+ end
+ G
+ expect(err).to lack_errors
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" unless defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "works when the revision is a non-head ref" do
+ # want to ensure we don't fallback to master
+ update_git "foo", :path => lib_path("foo-1.0") do |s|
+ s.write("lib/foo.rb", "raise 'FAIL'")
+ end
+
+ Dir.chdir(lib_path("foo-1.0")) do
+ `git update-ref -m 'Bundler Spec!' refs/bundler/1 master~1`
+ end
+
+ # want to ensure we don't fallback to HEAD
+ update_git "foo", :path => lib_path("foo-1.0"), :branch => "rando" do |s|
+ s.write("lib/foo.rb", "raise 'FAIL'")
+ end
+
+ install_gemfile! <<-G
+ git "#{lib_path("foo-1.0")}", :ref => "refs/bundler/1" do
+ gem "foo"
+ end
+ G
+ expect(err).to lack_errors
+
+ run! <<-RUBY
+ require 'foo'
+ puts "WIN" if defined?(FOO)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "works when the revision is a non-head ref and it was previously downloaded" do
+ install_gemfile! <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem "foo"
+ end
+ G
+
+ # want to ensure we don't fallback to master
+ update_git "foo", :path => lib_path("foo-1.0") do |s|
+ s.write("lib/foo.rb", "raise 'FAIL'")
+ end
+
+ Dir.chdir(lib_path("foo-1.0")) do
+ `git update-ref -m 'Bundler Spec!' refs/bundler/1 master~1`
+ end
+
+ # want to ensure we don't fallback to HEAD
+ update_git "foo", :path => lib_path("foo-1.0"), :branch => "rando" do |s|
+ s.write("lib/foo.rb", "raise 'FAIL'")
+ end
+
+ install_gemfile! <<-G
+ git "#{lib_path("foo-1.0")}", :ref => "refs/bundler/1" do
+ gem "foo"
+ end
+ G
+ expect(err).to lack_errors
+
+ run! <<-RUBY
+ require 'foo'
+ puts "WIN" if defined?(FOO)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "does not download random non-head refs" do
+ Dir.chdir(lib_path("foo-1.0")) do
+ sys_exec!("git update-ref -m 'Bundler Spec!' refs/bundler/1 master~1")
+ end
+
+ bundle! "config global_gem_cache true"
+
+ install_gemfile! <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem "foo"
+ end
+ G
+
+ # ensure we also git fetch after cloning
+ bundle! :update, :all => bundle_update_requires_all?
+
+ Dir.chdir(Dir[home(".bundle/cache/git/foo-*")].first) do
+ sys_exec("git ls-remote .")
+ end
+
+ expect(out).not_to include("refs/bundler/1")
+ end
+ end
+
+ describe "when specifying a branch" do
+ let(:branch) { "branch" }
+ let(:repo) { build_git("foo").path }
+ before(:each) do
+ update_git("foo", :path => repo, :branch => branch)
+ end
+
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :branch => #{branch.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ context "when the branch starts with a `#`" do
+ let(:branch) { "#149/redirect-url-fragment" }
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :branch => #{branch.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+ end
+
+ context "when the branch includes quotes" do
+ let(:branch) { %('") }
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :branch => #{branch.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+ end
+ end
+
+ describe "when specifying a tag" do
+ let(:tag) { "tag" }
+ let(:repo) { build_git("foo").path }
+ before(:each) do
+ update_git("foo", :path => repo, :tag => tag)
+ end
+
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :tag => #{tag.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ context "when the tag starts with a `#`" do
+ let(:tag) { "#149/redirect-url-fragment" }
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :tag => #{tag.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+ end
+
+ context "when the tag includes quotes" do
+ let(:tag) { %('") }
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :tag => #{tag.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+ end
+ end
+
+ describe "when specifying local override" do
+ it "uses the local repository instead of checking a new one out" do
+ # We don't generate it because we actually don't need it
+ # build_git "rack", "0.8"
+
+ build_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle! %(config local.rack #{lib_path("local-rack")})
+ bundle! :install
+
+ run "require 'rack'"
+ expect(out).to eq("LOCAL")
+ end
+
+ it "chooses the local repository on runtime" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ update_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ run "require 'rack'"
+ expect(out).to eq("LOCAL")
+ end
+
+ it "unlocks the source when the dependencies have changed while switching to the local" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ update_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.write "rack.gemspec", build_spec("rack", "0.8") { runtime "rspec", "> 0" }.first.to_ruby
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle! %(config local.rack #{lib_path("local-rack")})
+ bundle! :install
+ run! "require 'rack'"
+ expect(out).to eq("LOCAL")
+ end
+
+ it "updates specs on runtime" do
+ system_gems "nokogiri-1.4.2"
+
+ build_git "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ lockfile0 = File.read(bundled_app("Gemfile.lock"))
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+ update_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.add_dependency "nokogiri", "1.4.2"
+ end
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ run "require 'rack'"
+
+ lockfile1 = File.read(bundled_app("Gemfile.lock"))
+ expect(lockfile1).not_to eq(lockfile0)
+ end
+
+ it "updates ref on install" do
+ build_git "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ lockfile0 = File.read(bundled_app("Gemfile.lock"))
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+ update_git "rack", "0.8", :path => lib_path("local-rack")
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+
+ lockfile1 = File.read(bundled_app("Gemfile.lock"))
+ expect(lockfile1).not_to eq(lockfile0)
+ end
+
+ it "explodes if given path does not exist on install" do
+ build_git "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+ expect(out).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path('local-rack').to_s)} does not exist/)
+ end
+
+ it "explodes if branch is not given on install" do
+ build_git "rack", "0.8"
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+ expect(out).to match(/cannot use local override/i)
+ end
+
+ it "does not explode if disable_local_branch_check is given" do
+ build_git "rack", "0.8"
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle %(config disable_local_branch_check true)
+ bundle :install
+ expect(out).to match(/Bundle complete!/)
+ end
+
+ it "explodes on different branches on install" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ update_git "rack", "0.8", :path => lib_path("local-rack"), :branch => "another" do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+ expect(out).to match(/is using branch another but Gemfile specifies master/)
+ end
+
+ it "explodes on invalid revision on install" do
+ build_git "rack", "0.8"
+
+ build_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+ expect(out).to match(/The Gemfile lock is pointing to revision \w+/)
+ end
+ end
+
+ describe "specified inline" do
+ # TODO: Figure out how to write this test so that it is not flaky depending
+ # on the current network situation.
+ # it "supports private git URLs" do
+ # gemfile <<-G
+ # gem "thingy", :git => "git@notthere.fallingsnow.net:somebody/thingy.git"
+ # G
+ #
+ # bundle :install
+ #
+ # # p out
+ # # p err
+ # puts err unless err.empty? # This spec fails randomly every so often
+ # err.should include("notthere.fallingsnow.net")
+ # err.should include("ssh")
+ # end
+
+ it "installs from git even if a newer gem is available elsewhere" do
+ build_git "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}"
+ G
+
+ expect(the_bundle).to include_gems "rack 0.8"
+ end
+
+ it "installs dependencies from git even if a newer gem is available elsewhere" do
+ system_gems "rack-1.0.0"
+
+ build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s|
+ s.write "lib/rack.rb", "puts 'WIN OVERRIDE'"
+ end
+
+ build_git "foo", :path => lib_path("nested") do |s|
+ s.add_dependency "rack", "= 1.0"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("nested")}"
+ G
+
+ run "require 'rack'"
+ expect(out).to eq("WIN OVERRIDE")
+ end
+
+ it "correctly unlocks when changing to a git source" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ G
+
+ build_git "rack", :path => lib_path("rack")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0.0", :git => "#{lib_path("rack")}"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "correctly unlocks when changing to a git source without versions" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ build_git "rack", "1.2", :path => lib_path("rack")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack")}"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+ end
+
+ describe "block syntax" do
+ it "pulls all gems from a git block" do
+ build_lib "omg", :path => lib_path("hi2u/omg")
+ build_lib "hi2u", :path => lib_path("hi2u")
+
+ install_gemfile <<-G
+ path "#{lib_path("hi2u")}" do
+ gem "omg"
+ gem "hi2u"
+ end
+ G
+
+ expect(the_bundle).to include_gems "omg 1.0", "hi2u 1.0"
+ end
+ end
+
+ it "uses a ref if specified" do
+ build_git "foo"
+ @revision = revision_for(lib_path("foo-1.0"))
+ update_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :ref => "#{@revision}"
+ G
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" unless defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "correctly handles cases with invalid gemspecs" do
+ build_git "foo" do |s|
+ s.summary = nil
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ gem "rails", "2.3.2"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ expect(the_bundle).to include_gems "rails 2.3.2"
+ end
+
+ it "runs the gemspec in the context of its parent directory" do
+ build_lib "bar", :path => lib_path("foo/bar"), :gemspec => false do |s|
+ s.write lib_path("foo/bar/lib/version.rb"), %(BAR_VERSION = '1.0')
+ s.write "bar.gemspec", <<-G
+ $:.unshift Dir.pwd # For 1.9
+ require 'lib/version'
+ Gem::Specification.new do |s|
+ s.name = 'bar'
+ s.author = 'no one'
+ s.version = BAR_VERSION
+ s.summary = 'Bar'
+ s.files = Dir["lib/**/*.rb"]
+ end
+ G
+ end
+
+ build_git "foo", :path => lib_path("foo") do |s|
+ s.write "bin/foo", ""
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :git => "#{lib_path("foo")}"
+ gem "rails", "2.3.2"
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0"
+ expect(the_bundle).to include_gems "rails 2.3.2"
+ end
+
+ it "installs from git even if a rubygems gem is present" do
+ build_gem "foo", "1.0", :path => lib_path("fake_foo"), :to_system => true do |s|
+ s.write "lib/foo.rb", "raise 'FAIL'"
+ end
+
+ build_git "foo", "1.0"
+
+ install_gemfile <<-G
+ gem "foo", "1.0", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "fakes the gem out if there is no gemspec" do
+ build_git "foo", :gemspec => false
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", "1.0", :git => "#{lib_path("foo-1.0")}"
+ gem "rails", "2.3.2"
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ expect(the_bundle).to include_gems("rails 2.3.2")
+ end
+
+ it "catches git errors and spits out useful output" do
+ gemfile <<-G
+ gem "foo", "1.0", :git => "omgomg"
+ G
+
+ bundle :install
+
+ expect(out).to include("Git error:")
+ expect(err).to include("fatal")
+ expect(err).to include("omgomg")
+ end
+
+ it "works when the gem path has spaces in it" do
+ build_git "foo", :path => lib_path("foo space-1.0")
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo space-1.0")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "handles repos that have been force-pushed" do
+ build_git "forced", "1.0"
+
+ install_gemfile <<-G
+ git "#{lib_path("forced-1.0")}" do
+ gem 'forced'
+ end
+ G
+ expect(the_bundle).to include_gems "forced 1.0"
+
+ update_git "forced" do |s|
+ s.write "lib/forced.rb", "FORCED = '1.1'"
+ end
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(the_bundle).to include_gems "forced 1.1"
+
+ Dir.chdir(lib_path("forced-1.0")) do
+ `git reset --hard HEAD^`
+ end
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(the_bundle).to include_gems "forced 1.0"
+ end
+
+ it "ignores submodules if :submodule is not passed" do
+ build_git "submodule", "1.0"
+ build_git "has_submodule", "1.0" do |s|
+ s.add_dependency "submodule"
+ end
+ Dir.chdir(lib_path("has_submodule-1.0")) do
+ sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0"
+ `git commit -m "submodulator"`
+ end
+
+ install_gemfile <<-G
+ git "#{lib_path("has_submodule-1.0")}" do
+ gem "has_submodule"
+ end
+ G
+ expect(out).to match(/could not find gem 'submodule/i)
+
+ expect(the_bundle).not_to include_gems "has_submodule 1.0"
+ end
+
+ it "handles repos with submodules" do
+ build_git "submodule", "1.0"
+ build_git "has_submodule", "1.0" do |s|
+ s.add_dependency "submodule"
+ end
+ Dir.chdir(lib_path("has_submodule-1.0")) do
+ sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0"
+ `git commit -m "submodulator"`
+ end
+
+ install_gemfile <<-G
+ git "#{lib_path("has_submodule-1.0")}", :submodules => true do
+ gem "has_submodule"
+ end
+ G
+
+ expect(the_bundle).to include_gems "has_submodule 1.0"
+ end
+
+ it "handles implicit updates when modifying the source info" do
+ git = build_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem "foo"
+ end
+ G
+
+ update_git "foo"
+ update_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}", :ref => "#{git.ref_for("HEAD^")}" do
+ gem "foo"
+ end
+ G
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" if FOO_PREV_REF == '#{git.ref_for("HEAD^^")}'
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "does not to a remote fetch if the revision is cached locally" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ FileUtils.rm_rf(lib_path("foo-1.0"))
+
+ bundle "install"
+ expect(out).not_to match(/updating/i)
+ end
+
+ it "doesn't blow up if bundle install is run twice in a row" do
+ build_git "foo"
+
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle "install"
+ bundle "install"
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "prints a friendly error if a file blocks the git repo" do
+ build_git "foo"
+
+ FileUtils.mkdir_p(default_bundle_path)
+ FileUtils.touch(default_bundle_path("bundler"))
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(exitstatus).to_not eq(0) if exitstatus
+ expect(out).to include("Bundler could not install a gem because it " \
+ "needs to create a directory, but a file exists " \
+ "- #{default_bundle_path("bundler")}")
+ end
+
+ it "does not duplicate git gem sources" do
+ build_lib "foo", :path => lib_path("nested/foo")
+ build_lib "bar", :path => lib_path("nested/bar")
+
+ build_git "foo", :path => lib_path("nested")
+ build_git "bar", :path => lib_path("nested")
+
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("nested")}"
+ gem "bar", :git => "#{lib_path("nested")}"
+ G
+
+ bundle "install"
+ expect(File.read(bundled_app("Gemfile.lock")).scan("GIT").size).to eq(1)
+ end
+
+ describe "switching sources" do
+ it "doesn't explode when switching Path to Git sources" do
+ build_gem "foo", "1.0", :to_system => true do |s|
+ s.write "lib/foo.rb", "raise 'fail'"
+ end
+ build_lib "foo", "1.0", :path => lib_path("bar/foo")
+ build_git "bar", "1.0", :path => lib_path("bar") do |s|
+ s.add_dependency "foo"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :path => "#{lib_path("bar")}"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :git => "#{lib_path("bar")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0", "bar 1.0"
+ end
+
+ it "doesn't explode when switching Gem to Git source" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack-obama"
+ gem "rack", "1.0.0"
+ G
+
+ build_git "rack", "1.0" do |s|
+ s.write "lib/new_file.rb", "puts 'USING GIT'"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack-obama"
+ gem "rack", "1.0.0", :git => "#{lib_path("rack-1.0")}"
+ G
+
+ run "require 'new_file'"
+ expect(out).to eq("USING GIT")
+ end
+ end
+
+ describe "bundle install after the remote has been updated" do
+ it "installs" do
+ build_git "valim"
+
+ install_gemfile <<-G
+ gem "valim", :git => "file://#{lib_path("valim-1.0")}"
+ G
+
+ old_revision = revision_for(lib_path("valim-1.0"))
+ update_git "valim"
+ new_revision = revision_for(lib_path("valim-1.0"))
+
+ lockfile = File.read(bundled_app("Gemfile.lock"))
+ File.open(bundled_app("Gemfile.lock"), "w") do |file|
+ file.puts lockfile.gsub(/revision: #{old_revision}/, "revision: #{new_revision}")
+ end
+
+ bundle "install"
+
+ run <<-R
+ require "valim"
+ puts VALIM_PREV_REF
+ R
+
+ expect(out).to eq(old_revision)
+ end
+
+ it "gives a helpful error message when the remote ref no longer exists" do
+ build_git "foo"
+ revision = revision_for(lib_path("foo-1.0"))
+
+ install_gemfile <<-G
+ gem "foo", :git => "file://#{lib_path("foo-1.0")}", :ref => "#{revision}"
+ G
+ bundle "install"
+ expect(out).to_not match(/Revision.*does not exist/)
+
+ install_gemfile <<-G
+ gem "foo", :git => "file://#{lib_path("foo-1.0")}", :ref => "deadbeef"
+ G
+ bundle "install"
+ expect(out).to include("Revision deadbeef does not exist in the repository")
+ end
+ end
+
+ describe "bundle install --deployment with git sources" do
+ it "works" do
+ build_git "valim", :path => lib_path("valim")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "valim", "= 1.0", :git => "#{lib_path("valim")}"
+ G
+
+ simulate_new_machine
+
+ bundle! :install, forgotten_command_line_options(:deployment => true)
+ end
+ end
+
+ describe "gem install hooks" do
+ it "runs pre-install hooks" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.pre_install_hooks << lambda do |inst|
+ STDERR.puts "Ran pre-install hook: \#{inst.spec.full_name}"
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(err).to eq_err("Ran pre-install hook: foo-1.0")
+ end
+
+ it "runs post-install hooks" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.post_install_hooks << lambda do |inst|
+ STDERR.puts "Ran post-install hook: \#{inst.spec.full_name}"
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(err).to eq_err("Ran post-install hook: foo-1.0")
+ end
+
+ it "complains if the install hook fails" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.pre_install_hooks << lambda do |inst|
+ false
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(out).to include("failed for foo-1.0")
+ end
+ end
+
+ context "with an extension" do
+ it "installs the extension", :ruby_repo do
+ build_git "foo" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/foo.rb", "w") do |f|
+ f.puts "FOO = 'YES'"
+ end
+ end
+ RUBY
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ run <<-R
+ require 'foo'
+ puts FOO
+ R
+ expect(out).to eq("YES")
+
+ run! <<-R
+ puts $:.grep(/ext/)
+ R
+ expect(out).to eq(Pathname.glob(default_bundle_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s)
+ end
+
+ it "does not use old extension after ref changes", :ruby_repo do
+ git_reader = build_git "foo", :no_default => true do |s|
+ s.extensions = ["ext/extconf.rb"]
+ s.write "ext/extconf.rb", <<-RUBY
+ require "mkmf"
+ create_makefile("foo")
+ RUBY
+ s.write "ext/foo.c", "void Init_foo() {}"
+ end
+
+ 2.times do |i|
+ Dir.chdir(git_reader.path) do
+ File.open("ext/foo.c", "w") do |file|
+ file.write <<-C
+ #include "ruby.h"
+ VALUE foo() { return INT2FIX(#{i}); }
+ void Init_foo() { rb_define_global_function("foo", &foo, 0); }
+ C
+ end
+ `git commit -m 'commit for iteration #{i}' ext/foo.c`
+ end
+ git_commit_sha = git_reader.ref_for("HEAD")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :ref => "#{git_commit_sha}"
+ G
+
+ run <<-R
+ require 'foo'
+ puts foo
+ R
+
+ expect(out).to eq(i.to_s)
+ end
+ end
+
+ it "does not prompt to gem install if extension fails" do
+ build_git "foo" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ raise
+ end
+ RUBY
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(last_command.bundler_err).to end_with(<<-M.strip)
+An error occurred while installing foo (1.0), and Bundler cannot continue.
+
+In Gemfile:
+ foo
+ M
+ expect(out).not_to include("gem install foo")
+ end
+
+ it "does not reinstall the extension", :ruby_repo, :rubygems => ">= 2.3.0" do
+ build_git "foo" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ cur_time = Time.now.to_f.to_s
+ File.open("\#{path}/foo.rb", "w") do |f|
+ f.puts "FOO = \#{cur_time}"
+ end
+ end
+ RUBY
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ run! <<-R
+ require 'foo'
+ puts FOO
+ R
+
+ installed_time = out
+ expect(installed_time).to match(/\A\d+\.\d+\z/)
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ run! <<-R
+ require 'foo'
+ puts FOO
+ R
+ expect(out).to eq(installed_time)
+ end
+ end
+
+ it "ignores git environment variables" do
+ build_git "xxxxxx" do |s|
+ s.executables = "xxxxxxbar"
+ end
+
+ Bundler::SharedHelpers.with_clean_git_env do
+ ENV["GIT_DIR"] = "bar"
+ ENV["GIT_WORK_TREE"] = "bar"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ git "#{lib_path("xxxxxx-1.0")}" do
+ gem 'xxxxxx'
+ end
+ G
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(ENV["GIT_DIR"]).to eq("bar")
+ expect(ENV["GIT_WORK_TREE"]).to eq("bar")
+ end
+ end
+
+ describe "without git installed" do
+ it "prints a better error message" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+
+ with_path_as("") do
+ bundle "update", :all => bundle_update_requires_all?
+ end
+ expect(last_command.bundler_err).
+ to include("You need to install git to be able to use gems from git repositories. For help installing git, please refer to GitHub's tutorial at https://help.github.com/articles/set-up-git")
+ end
+
+ it "installs a packaged git gem successfully" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+ bundle :package, forgotten_command_line_options([:all, :cache_all] => true)
+ simulate_new_machine
+
+ bundle! "install", :env => { "PATH" => "" }
+ expect(out).to_not include("You need to install git to be able to use gems from git repositories.")
+ end
+ end
+
+ describe "when the git source is overridden with a local git repo" do
+ before do
+ bundle! "config --global local.foo #{lib_path("foo")}"
+ end
+
+ describe "and git output is colorized" do
+ before do
+ File.open("#{ENV["HOME"]}/.gitconfig", "w") do |f|
+ f.write("[color]\n\tui = always\n")
+ end
+ end
+
+ it "installs successfully" do
+ build_git "foo", "1.0", :path => lib_path("foo")
+
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo")}", :branch => "master"
+ G
+
+ bundle :install
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+ end
+ end
+
+ context "git sources that include credentials" do
+ context "that are username and password" do
+ let(:credentials) { "user1:password1" }
+
+ it "does not display the password" do
+ install_gemfile <<-G
+ git "https://#{credentials}@github.com/company/private-repo" do
+ gem "foo"
+ end
+ G
+
+ bundle :install
+ expect(last_command.stdboth).to_not include("password1")
+ expect(last_command.stdout).to include("Fetching https://user1@github.com/company/private-repo")
+ end
+ end
+
+ context "that is an oauth token" do
+ let(:credentials) { "oauth_token" }
+
+ it "displays the oauth scheme but not the oauth token" do
+ install_gemfile <<-G
+ git "https://#{credentials}:x-oauth-basic@github.com/company/private-repo" do
+ gem "foo"
+ end
+ G
+
+ bundle :install
+ expect(last_command.stdboth).to_not include("oauth_token")
+ expect(last_command.stdout).to include("Fetching https://x-oauth-basic@github.com/company/private-repo")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/groups_spec.rb b/spec/bundler/install/gemfile/groups_spec.rb
new file mode 100644
index 0000000000..19c379e188
--- /dev/null
+++ b/spec/bundler/install/gemfile/groups_spec.rb
@@ -0,0 +1,384 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with groups" do
+ describe "installing with no options" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ group :emo do
+ gem "activesupport", "2.3.5"
+ end
+ gem "thin", :groups => [:emo]
+ G
+ end
+
+ it "installs gems in the default group" do
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs gems in a group block into that group" do
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+
+ load_error_run <<-R, "activesupport", :default
+ require 'activesupport'
+ puts ACTIVESUPPORT
+ R
+
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+
+ it "installs gems with inline :groups into those groups" do
+ expect(the_bundle).to include_gems "thin 1.0"
+
+ load_error_run <<-R, "thin", :default
+ require 'thin'
+ puts THIN
+ R
+
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+
+ it "sets up everything if Bundler.setup is used with no groups" do
+ output = run("require 'rack'; puts RACK")
+ expect(output).to eq("1.0.0")
+
+ output = run("require 'activesupport'; puts ACTIVESUPPORT")
+ expect(output).to eq("2.3.5")
+
+ output = run("require 'thin'; puts THIN")
+ expect(output).to eq("1.0")
+ end
+
+ it "removes old groups when new groups are set up" do
+ load_error_run <<-RUBY, "thin", :emo
+ Bundler.setup(:default)
+ require 'thin'
+ puts THIN
+ RUBY
+
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+
+ it "sets up old groups when they have previously been removed" do
+ output = run <<-RUBY, :emo
+ Bundler.setup(:default)
+ Bundler.setup(:default, :emo)
+ require 'thin'; puts THIN
+ RUBY
+ expect(output).to eq("1.0")
+ end
+ end
+
+ describe "installing --without" do
+ describe "with gems assigned to a single group" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ group :emo do
+ gem "activesupport", "2.3.5"
+ end
+ group :debugging, :optional => true do
+ gem "thin"
+ end
+ G
+ end
+
+ it "installs gems in the default group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo")
+ expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
+ end
+
+ it "does not install gems from the excluded group" do
+ bundle :install, :without => "emo"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default]
+ end
+
+ it "does not install gems from the previously excluded group" do
+ bundle :install, forgotten_command_line_options(:without => "emo")
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ bundle :install
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+
+ it "does not say it installed gems from the excluded group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo")
+ expect(out).not_to include("activesupport")
+ end
+
+ it "allows Bundler.setup for specific groups" do
+ bundle :install, forgotten_command_line_options(:without => "emo")
+ run!("require 'rack'; puts RACK", :default)
+ expect(out).to eq("1.0.0")
+ end
+
+ it "does not effect the resolve" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ group :emo do
+ gem "rails", "2.3.2"
+ end
+ G
+
+ bundle :install, forgotten_command_line_options(:without => "emo")
+ expect(the_bundle).to include_gems "activesupport 2.3.2", :groups => [:default]
+ end
+
+ it "still works on a different machine and excludes gems" do
+ bundle :install, forgotten_command_line_options(:without => "emo")
+
+ simulate_new_machine
+ bundle :install, forgotten_command_line_options(:without => "emo")
+
+ expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default]
+ end
+
+ it "still works when BUNDLE_WITHOUT is set" do
+ ENV["BUNDLE_WITHOUT"] = "emo"
+
+ bundle :install
+ expect(out).not_to include("activesupport")
+
+ expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default]
+
+ ENV["BUNDLE_WITHOUT"] = nil
+ end
+
+ it "clears without when passed an empty list" do
+ bundle :install, forgotten_command_line_options(:without => "emo")
+
+ bundle :install, forgotten_command_line_options(:without => "")
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+
+ it "doesn't clear without when nothing is passed" do
+ bundle :install, forgotten_command_line_options(:without => "emo")
+
+ bundle :install
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+
+ it "does not install gems from the optional group" do
+ bundle :install
+ expect(the_bundle).not_to include_gems "thin 1.0"
+ end
+
+ it "does install gems from the optional group when requested" do
+ bundle :install, forgotten_command_line_options(:with => "debugging")
+ expect(the_bundle).to include_gems "thin 1.0"
+ end
+
+ it "does install gems from the previously requested group" do
+ bundle :install, forgotten_command_line_options(:with => "debugging")
+ expect(the_bundle).to include_gems "thin 1.0"
+ bundle :install
+ expect(the_bundle).to include_gems "thin 1.0"
+ end
+
+ it "does install gems from the optional groups requested with BUNDLE_WITH" do
+ ENV["BUNDLE_WITH"] = "debugging"
+ bundle :install
+ expect(the_bundle).to include_gems "thin 1.0"
+ ENV["BUNDLE_WITH"] = nil
+ end
+
+ it "clears with when passed an empty list" do
+ bundle :install, forgotten_command_line_options(:with => "debugging")
+ bundle :install, forgotten_command_line_options(:with => "")
+ expect(the_bundle).not_to include_gems "thin 1.0"
+ end
+
+ it "does remove groups from without when passed at --with", :bundler => "< 2" do
+ bundle :install, forgotten_command_line_options(:without => "emo")
+ bundle :install, forgotten_command_line_options(:with => "emo")
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+
+ it "does remove groups from with when passed at --without", :bundler => "< 2" do
+ bundle :install, forgotten_command_line_options(:with => "debugging")
+ bundle :install, forgotten_command_line_options(:without => "debugging")
+ expect(the_bundle).not_to include_gem "thin 1.0"
+ end
+
+ it "errors out when passing a group to with and without via CLI flags", :bundler => "< 2" do
+ bundle :install, forgotten_command_line_options(:with => "emo debugging", :without => "emo")
+ expect(last_command).to be_failure
+ expect(out).to include("The offending groups are: emo")
+ end
+
+ it "allows the BUNDLE_WITH setting to override BUNDLE_WITHOUT" do
+ ENV["BUNDLE_WITH"] = "debugging"
+
+ bundle! :install
+ expect(the_bundle).to include_gem "thin 1.0"
+
+ ENV["BUNDLE_WITHOUT"] = "debugging"
+ expect(the_bundle).to include_gem "thin 1.0"
+
+ bundle! :install
+ expect(the_bundle).to include_gem "thin 1.0"
+ end
+
+ it "can add and remove a group at the same time" do
+ bundle :install, forgotten_command_line_options(:with => "debugging", :without => "emo")
+ expect(the_bundle).to include_gems "thin 1.0"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+
+ it "does have no effect when listing a not optional group in with" do
+ bundle :install, forgotten_command_line_options(:with => "emo")
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+
+ it "does have no effect when listing an optional group in without" do
+ bundle :install, forgotten_command_line_options(:without => "debugging")
+ expect(the_bundle).not_to include_gems "thin 1.0"
+ end
+ end
+
+ describe "with gems assigned to multiple groups" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ group :emo, :lolercoaster do
+ gem "activesupport", "2.3.5"
+ end
+ G
+ end
+
+ it "installs gems in the default group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo lolercoaster")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs the gem if any of its groups are installed" do
+ bundle! :install, forgotten_command_line_options(:without => "emo")
+ expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
+ end
+
+ describe "with a gem defined multiple times in different groups" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ group :emo do
+ gem "activesupport", "2.3.5"
+ end
+
+ group :lolercoaster do
+ gem "activesupport", "2.3.5"
+ end
+ G
+ end
+
+ it "installs the gem w/ option --without emo" do
+ bundle :install, forgotten_command_line_options(:without => "emo")
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+
+ it "installs the gem w/ option --without lolercoaster" do
+ bundle :install, forgotten_command_line_options(:without => "lolercoaster")
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+
+ it "does not install the gem w/ option --without emo lolercoaster" do
+ bundle :install, forgotten_command_line_options(:without => "emo lolercoaster")
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+
+ it "does not install the gem w/ option --without 'emo lolercoaster'" do
+ bundle :install, forgotten_command_line_options(:without => "'emo lolercoaster'")
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+ end
+ end
+
+ describe "nesting groups" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ group :emo do
+ group :lolercoaster do
+ gem "activesupport", "2.3.5"
+ end
+ end
+ G
+ end
+
+ it "installs gems in the default group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo lolercoaster")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs the gem if any of its groups are installed" do
+ bundle! :install, forgotten_command_line_options(:without => "emo")
+ expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
+ end
+ end
+ end
+
+ describe "when loading only the default group" do
+ it "should not load all groups" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :groups => :development
+ G
+
+ ruby <<-R
+ require "bundler"
+ Bundler.setup :default
+ Bundler.require :default
+ puts RACK
+ begin
+ require "activesupport"
+ rescue LoadError
+ puts "no activesupport"
+ end
+ R
+
+ expect(out).to include("1.0")
+ expect(out).to include("no activesupport")
+ end
+ end
+
+ describe "when locked and installed with --without" do
+ before(:each) do
+ build_repo2
+ system_gems "rack-0.9.1" do
+ install_gemfile <<-G, forgotten_command_line_options(:without => "rack")
+ source "file://#{gem_repo2}"
+ gem "rack"
+
+ group :rack do
+ gem "rack_middleware"
+ end
+ G
+ end
+ end
+
+ it "uses the correct versions even if --without was used on the original" do
+ expect(the_bundle).to include_gems "rack 0.9.1"
+ expect(the_bundle).not_to include_gems "rack_middleware 1.0"
+ simulate_new_machine
+
+ bundle :install
+
+ expect(the_bundle).to include_gems "rack 0.9.1"
+ expect(the_bundle).to include_gems "rack_middleware 1.0"
+ end
+
+ it "does not hit the remote a second time" do
+ FileUtils.rm_rf gem_repo2
+ bundle! :install, forgotten_command_line_options(:without => "rack").merge(:verbose => true)
+ expect(last_command.stdboth).not_to match(/fetching/i)
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/install_if.rb b/spec/bundler/install/gemfile/install_if.rb
new file mode 100644
index 0000000000..1319051fdb
--- /dev/null
+++ b/spec/bundler/install/gemfile/install_if.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+describe "bundle install with install_if conditionals" do
+ it "follows the install_if DSL" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ install_if(lambda { true }) do
+ gem "activesupport", "2.3.5"
+ end
+ gem "thin", :install_if => false
+ install_if(lambda { false }) do
+ gem "foo"
+ end
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems("rack 1.0", "activesupport 2.3.5")
+ expect(the_bundle).not_to include_gems("thin")
+ expect(the_bundle).not_to include_gems("foo")
+
+ lockfile_should_be <<-L
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ activesupport (2.3.5)
+ foo (1.0)
+ rack (1.0.0)
+ thin (1.0)
+ rack
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ activesupport (= 2.3.5)
+ foo
+ rack
+ thin
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+end
diff --git a/spec/bundler/install/gemfile/lockfile_spec.rb b/spec/bundler/install/gemfile/lockfile_spec.rb
new file mode 100644
index 0000000000..dc1baca6ea
--- /dev/null
+++ b/spec/bundler/install/gemfile/lockfile_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with a lockfile present" do
+ let(:gf) { <<-G }
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ G
+
+ subject do
+ install_gemfile(gf)
+ end
+
+ context "gemfile evaluation" do
+ let(:gf) { super() + "\n\n File.open('evals', 'a') {|f| f << %(1\n) } unless ENV['BUNDLER_SPEC_NO_APPEND']" }
+
+ context "with plugins disabled" do
+ before do
+ bundle! "config plugins false"
+ subject
+ end
+
+ it "does not evaluate the gemfile twice" do
+ bundle! :install
+
+ with_env_vars("BUNDLER_SPEC_NO_APPEND" => "1") { expect(the_bundle).to include_gem "rack 1.0.0" }
+
+ # The first eval is from the initial install, we're testing that the
+ # second install doesn't double-eval
+ expect(bundled_app("evals").read.lines.to_a.size).to eq(2)
+ end
+
+ context "when the gem is not installed" do
+ before { FileUtils.rm_rf ".bundle" }
+
+ it "does not evaluate the gemfile twice" do
+ bundle! :install
+
+ with_env_vars("BUNDLER_SPEC_NO_APPEND" => "1") { expect(the_bundle).to include_gem "rack 1.0.0" }
+
+ # The first eval is from the initial install, we're testing that the
+ # second install doesn't double-eval
+ expect(bundled_app("evals").read.lines.to_a.size).to eq(2)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/path_spec.rb b/spec/bundler/install/gemfile/path_spec.rb
new file mode 100644
index 0000000000..f7789e7ea5
--- /dev/null
+++ b/spec/bundler/install/gemfile/path_spec.rb
@@ -0,0 +1,630 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with explicit source paths" do
+ it "fetches gems with a global path source", :bundler => "< 2" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ path "#{lib_path("foo-1.0")}"
+ gem 'foo'
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "fetches gems" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ path "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "supports pinned paths" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem 'foo', :path => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "supports relative paths" do
+ build_lib "foo"
+
+ relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new(Dir.pwd))
+
+ install_gemfile <<-G
+ gem 'foo', :path => "#{relative_path}"
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "expands paths" do
+ build_lib "foo"
+
+ relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new("~").expand_path)
+
+ install_gemfile <<-G
+ gem 'foo', :path => "~/#{relative_path}"
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "expands paths raise error with not existing user's home dir" do
+ build_lib "foo"
+ username = "some_unexisting_user"
+ relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new("/home/#{username}").expand_path)
+
+ install_gemfile <<-G
+ gem 'foo', :path => "~#{username}/#{relative_path}"
+ G
+ expect(out).to match("There was an error while trying to use the path `~#{username}/#{relative_path}`.")
+ expect(out).to match("user #{username} doesn't exist")
+ end
+
+ it "expands paths relative to Bundler.root" do
+ build_lib "foo", :path => bundled_app("foo-1.0")
+
+ install_gemfile <<-G
+ gem 'foo', :path => "./foo-1.0"
+ G
+
+ bundled_app("subdir").mkpath
+ Dir.chdir(bundled_app("subdir")) do
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+ end
+
+ it "expands paths when comparing locked paths to Gemfile paths" do
+ build_lib "foo", :path => bundled_app("foo-1.0")
+
+ install_gemfile <<-G
+ gem 'foo', :path => File.expand_path("../foo-1.0", __FILE__)
+ G
+
+ bundle! :install, forgotten_command_line_options(:frozen => true)
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "installs dependencies from the path even if a newer gem is available elsewhere" do
+ system_gems "rack-1.0.0"
+
+ build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s|
+ s.write "lib/rack.rb", "puts 'WIN OVERRIDE'"
+ end
+
+ build_lib "foo", :path => lib_path("nested") do |s|
+ s.add_dependency "rack", "= 1.0"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("nested")}"
+ G
+
+ run "require 'rack'"
+ expect(out).to eq("WIN OVERRIDE")
+ end
+
+ it "works" do
+ build_gem "foo", "1.0.0", :to_system => true do |s|
+ s.write "lib/foo.rb", "puts 'FAIL'"
+ end
+
+ build_lib "omg", "1.0", :path => lib_path("omg") do |s|
+ s.add_dependency "foo"
+ end
+
+ build_lib "foo", "1.0.0", :path => lib_path("omg/foo")
+
+ install_gemfile <<-G
+ gem "omg", :path => "#{lib_path("omg")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "prefers gemspecs closer to the path root" do
+ build_lib "premailer", "1.0.0", :path => lib_path("premailer") do |s|
+ s.write "gemfiles/ruby187.gemspec", <<-G
+ Gem::Specification.new do |s|
+ s.name = 'premailer'
+ s.version = '1.0.0'
+ s.summary = 'Hi'
+ s.authors = 'Me'
+ end
+ G
+ end
+
+ install_gemfile <<-G
+ gem "premailer", :path => "#{lib_path("premailer")}"
+ G
+
+ # Installation of the 'gemfiles' gemspec would fail since it will be unable
+ # to require 'premailer.rb'
+ expect(the_bundle).to include_gems "premailer 1.0.0"
+ end
+
+ it "warns on invalid specs", :rubygems => "1.7" do
+ build_lib "foo"
+
+ gemspec = lib_path("foo-1.0").join("foo.gemspec").to_s
+ File.open(gemspec, "w") do |f|
+ f.write <<-G
+ Gem::Specification.new do |s|
+ s.name = "foo"
+ end
+ G
+ end
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(out).to_not include("ERROR REPORT")
+ expect(out).to_not include("Your Gemfile has no gem server sources.")
+ expect(out).to match(/is not valid. Please fix this gemspec./)
+ expect(out).to match(/The validation error was 'missing value for attribute version'/)
+ expect(out).to match(/You have one or more invalid gemspecs that need to be fixed/)
+ end
+
+ it "supports gemspec syntax" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", "1.0"
+ end
+
+ gemfile = <<-G
+ source "file://#{gem_repo1}"
+ gemspec
+ G
+
+ File.open(lib_path("foo/Gemfile"), "w") {|f| f.puts gemfile }
+
+ Dir.chdir(lib_path("foo")) do
+ bundle "install"
+ expect(the_bundle).to include_gems "foo 1.0"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ 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"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gemspec :path => "#{lib_path("foo")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "doesn't automatically unlock dependencies when using the gemspec syntax" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", ">= 1.0"
+ end
+
+ Dir.chdir lib_path("foo")
+
+ install_gemfile lib_path("foo/Gemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gemspec
+ G
+
+ build_gem "rack", "1.0.1", :to_system => true
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "doesn't automatically unlock dependencies when using the gemspec syntax and the gem has development dependencies" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", ">= 1.0"
+ s.add_development_dependency "activesupport"
+ end
+
+ Dir.chdir lib_path("foo")
+
+ install_gemfile lib_path("foo/Gemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gemspec
+ G
+
+ build_gem "rack", "1.0.1", :to_system => true
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "raises if there are multiple gemspecs" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.write "bar.gemspec", build_spec("bar", "1.0").first.to_ruby
+ end
+
+ install_gemfile <<-G
+ gemspec :path => "#{lib_path("foo")}"
+ G
+
+ expect(exitstatus).to eq(15) if exitstatus
+ expect(out).to match(/There are multiple gemspecs/)
+ end
+
+ it "allows :name to be specified to resolve ambiguity" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.write "bar.gemspec"
+ end
+
+ install_gemfile <<-G
+ gemspec :path => "#{lib_path("foo")}", :name => "foo"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "sets up executables" do
+ build_lib "foo" do |s|
+ s.executables = "foobar"
+ end
+
+ install_gemfile <<-G
+ path "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+ expect(the_bundle).to include_gems "foo 1.0"
+
+ bundle "exec foobar"
+ expect(out).to eq("1.0")
+ end
+
+ it "handles directories in bin/" do
+ build_lib "foo"
+ lib_path("foo-1.0").join("foo.gemspec").rmtree
+ lib_path("foo-1.0").join("bin/performance").mkpath
+
+ install_gemfile <<-G
+ gem 'foo', '1.0', :path => "#{lib_path("foo-1.0")}"
+ G
+ expect(err).to lack_errors
+ end
+
+ it "removes the .gem file after installing" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem 'foo', :path => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(lib_path("foo-1.0").join("foo-1.0.gem")).not_to exist
+ end
+
+ describe "block syntax" do
+ it "pulls all gems from a path block" do
+ build_lib "omg"
+ build_lib "hi2u"
+
+ install_gemfile <<-G
+ path "#{lib_path}" do
+ gem "omg"
+ gem "hi2u"
+ end
+ G
+
+ expect(the_bundle).to include_gems "omg 1.0", "hi2u 1.0"
+ end
+ end
+
+ it "keeps source pinning" do
+ build_lib "foo", "1.0", :path => lib_path("foo")
+ build_lib "omg", "1.0", :path => lib_path("omg")
+ build_lib "foo", "1.0", :path => lib_path("omg/foo") do |s|
+ s.write "lib/foo.rb", "puts 'FAIL'"
+ end
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo")}"
+ gem "omg", :path => "#{lib_path("omg")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "works when the path does not have a gemspec" do
+ build_lib "foo", :gemspec => false
+
+ gemfile <<-G
+ gem "foo", "1.0", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "works when the path does not have a gemspec but there is a lockfile" do
+ lockfile <<-L
+ PATH
+ remote: vendor/bar
+ specs:
+
+ GEM
+ remote: http://rubygems.org
+ L
+
+ in_app_root { FileUtils.mkdir_p("vendor/bar") }
+
+ install_gemfile <<-G
+ gem "bar", "1.0.0", path: "vendor/bar", require: "bar/nyard"
+ G
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ context "existing lockfile" do
+ it "rubygems gems don't re-resolve without changes" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack-obama', '1.0'
+ gem 'net-ssh', '1.0'
+ G
+
+ bundle :check, :env => { "DEBUG" => 1 }
+ expect(out).to match(/using resolution from the lockfile/)
+ expect(the_bundle).to include_gems "rack-obama 1.0", "net-ssh 1.0"
+ end
+
+ it "source path gems w/deps don't re-resolve without changes" do
+ build_lib "rack-obama", "1.0", :path => lib_path("omg") do |s|
+ s.add_dependency "yard"
+ end
+
+ build_lib "net-ssh", "1.0", :path => lib_path("omg") do |s|
+ s.add_dependency "yard"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack-obama', :path => "#{lib_path("omg")}"
+ gem 'net-ssh', :path => "#{lib_path("omg")}"
+ G
+
+ bundle :check, :env => { "DEBUG" => 1 }
+ expect(out).to match(/using resolution from the lockfile/)
+ expect(the_bundle).to include_gems "rack-obama 1.0", "net-ssh 1.0"
+ end
+ end
+
+ it "installs executable stubs" do
+ build_lib "foo" do |s|
+ s.executables = ["foo"]
+ end
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle "exec foo"
+ expect(out).to eq("1.0")
+ end
+
+ describe "when the gem version in the path is updated" do
+ before :each do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "bar"
+ end
+ build_lib "bar", "1.0", :path => lib_path("foo/bar")
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+ end
+
+ it "unlocks all gems when the top level gem is updated" do
+ build_lib "foo", "2.0", :path => lib_path("foo") do |s|
+ s.add_dependency "bar"
+ end
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "foo 2.0", "bar 1.0"
+ end
+
+ it "unlocks all gems when a child dependency gem is updated" do
+ build_lib "bar", "2.0", :path => lib_path("foo/bar")
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "foo 1.0", "bar 2.0"
+ end
+ end
+
+ describe "when dependencies in the path are updated" do
+ before :each do
+ build_lib "foo", "1.0", :path => lib_path("foo")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+ end
+
+ it "gets dependencies that are updated in the path" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack"
+ end
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ describe "switching sources" do
+ it "doesn't switch pinned git sources to rubygems when pinning the parent gem to a path source" do
+ build_gem "foo", "1.0", :to_system => true do |s|
+ s.write "lib/foo.rb", "raise 'fail'"
+ end
+ build_lib "foo", "1.0", :path => lib_path("bar/foo")
+ build_git "bar", "1.0", :path => lib_path("bar") do |s|
+ s.add_dependency "foo"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :git => "#{lib_path("bar")}"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :path => "#{lib_path("bar")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0", "bar 1.0"
+ end
+
+ it "switches the source when the gem existed in rubygems and the path was already being used for another gem" do
+ build_lib "foo", "1.0", :path => lib_path("foo")
+ build_gem "bar", "1.0", :to_system => true do |s|
+ s.write "lib/bar.rb", "raise 'fail'"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar"
+ path "#{lib_path("foo")}" do
+ gem "foo"
+ end
+ G
+
+ build_lib "bar", "1.0", :path => lib_path("foo/bar")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ path "#{lib_path("foo")}" do
+ gem "foo"
+ gem "bar"
+ end
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0"
+ end
+ end
+
+ 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
+ source "http://localgemserver.test"
+ gemspec
+ gem 'rack'
+ G
+ File.open(lib_path("private_lib/Gemfile"), "w") {|f| f.puts gemfile }
+
+ Dir.chdir(lib_path("private_lib")) do
+ bundle :install, :env => { "DEBUG" => 1 }, :artifice => "endpoint"
+ expect(out).to match(%r{^HTTP GET http://localgemserver\.test/api/v1/dependencies\?gems=rack$})
+ expect(out).not_to match(/^HTTP GET.*private_lib/)
+ expect(the_bundle).to include_gems "private_lib 2.2"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+ end
+
+ describe "gem install hooks" do
+ it "runs pre-install hooks" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.pre_install_hooks << lambda do |inst|
+ STDERR.puts "Ran pre-install hook: \#{inst.spec.full_name}"
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(err).to eq_err("Ran pre-install hook: foo-1.0")
+ end
+
+ it "runs post-install hooks" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.post_install_hooks << lambda do |inst|
+ STDERR.puts "Ran post-install hook: \#{inst.spec.full_name}"
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(err).to eq_err("Ran post-install hook: foo-1.0")
+ end
+
+ it "complains if the install hook fails" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.pre_install_hooks << lambda do |inst|
+ false
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(out).to include("failed for foo-1.0")
+ end
+
+ it "loads plugins from the path gem" do
+ foo_file = home("foo_plugin_loaded")
+ bar_file = home("bar_plugin_loaded")
+ expect(foo_file).not_to be_file
+ expect(bar_file).not_to be_file
+
+ build_lib "foo" do |s|
+ s.write("lib/rubygems_plugin.rb", "FileUtils.touch('#{foo_file}')")
+ end
+
+ build_git "bar" do |s|
+ s.write("lib/rubygems_plugin.rb", "FileUtils.touch('#{bar_file}')")
+ end
+
+ install_gemfile! <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ gem "bar", :path => "#{lib_path("bar-1.0")}"
+ G
+
+ expect(foo_file).to be_file
+ expect(bar_file).to be_file
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb
new file mode 100644
index 0000000000..bfdf9b68c8
--- /dev/null
+++ b/spec/bundler/install/gemfile/platform_spec.rb
@@ -0,0 +1,426 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install across platforms" do
+ it "maintains the same lockfile if all gems are compatible across platforms" do
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ #{not_local}
+
+ DEPENDENCIES
+ rack
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 0.9.1"
+ end
+
+ it "pulls in the correct platform specific gem" do
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}
+ specs:
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+ platform_specific (1.0-x86-mswin32)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ platform_specific
+ G
+
+ simulate_platform "java"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "platform_specific"
+ G
+
+ expect(the_bundle).to include_gems "platform_specific 1.0 JAVA"
+ end
+
+ it "works with gems that have different dependencies" do
+ simulate_platform "java"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "nokogiri"
+ G
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA", "weakling 0.0.3"
+
+ simulate_new_machine
+
+ simulate_platform "ruby"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "nokogiri"
+ G
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ expect(the_bundle).not_to include_gems "weakling"
+ end
+
+ it "does not keep unneeded platforms for gems that are used" do
+ build_repo4 do
+ build_gem "empyrean", "0.1.0"
+ build_gem "coderay", "1.1.2"
+ build_gem "method_source", "0.9.0"
+ build_gem("spoon", "0.0.6") {|s| s.add_runtime_dependency "ffi" }
+ build_gem "pry", "0.11.3" do |s|
+ s.platform = "java"
+ s.add_runtime_dependency "coderay", "~> 1.1.0"
+ s.add_runtime_dependency "method_source", "~> 0.9.0"
+ s.add_runtime_dependency "spoon", "~> 0.0"
+ end
+ build_gem "pry", "0.11.3" do |s|
+ s.add_runtime_dependency "coderay", "~> 1.1.0"
+ s.add_runtime_dependency "method_source", "~> 0.9.0"
+ end
+ build_gem("ffi", "1.9.23") {|s| s.platform = "java" }
+ build_gem("ffi", "1.9.23")
+ end
+
+ simulate_platform java
+
+ install_gemfile! <<-G
+ source "file://localhost/#{gem_repo4}"
+
+ gem "empyrean", "0.1.0"
+ gem "pry"
+ G
+
+ expect(the_bundle.lockfile).to read_as normalize_uri_file(strip_whitespace(<<-L))
+ GEM
+ remote: file://localhost/#{gem_repo4}/
+ specs:
+ coderay (1.1.2)
+ empyrean (0.1.0)
+ ffi (1.9.23-java)
+ method_source (0.9.0)
+ pry (0.11.3-java)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ spoon (~> 0.0)
+ spoon (0.0.6)
+ ffi
+
+ PLATFORMS
+ java
+
+ DEPENDENCIES
+ empyrean (= 0.1.0)
+ pry
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle! "lock --add-platform ruby"
+
+ good_lockfile = strip_whitespace(<<-L)
+ GEM
+ remote: file://localhost/#{gem_repo4}/
+ specs:
+ coderay (1.1.2)
+ empyrean (0.1.0)
+ ffi (1.9.23-java)
+ method_source (0.9.0)
+ pry (0.11.3)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ pry (0.11.3-java)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ spoon (~> 0.0)
+ spoon (0.0.6)
+ ffi
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ empyrean (= 0.1.0)
+ pry
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ expect(the_bundle.lockfile).to read_as normalize_uri_file(good_lockfile)
+
+ bad_lockfile = strip_whitespace <<-L
+ GEM
+ remote: file://localhost/#{gem_repo4}/
+ specs:
+ coderay (1.1.2)
+ empyrean (0.1.0)
+ ffi (1.9.23)
+ ffi (1.9.23-java)
+ method_source (0.9.0)
+ pry (0.11.3)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ pry (0.11.3-java)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ spoon (~> 0.0)
+ spoon (0.0.6)
+ ffi
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ empyrean (= 0.1.0)
+ pry
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ aggregate_failures do
+ lockfile bad_lockfile
+ bundle! :install
+ expect(the_bundle.lockfile).to read_as normalize_uri_file(good_lockfile)
+
+ lockfile bad_lockfile
+ bundle! :update, :all => true
+ expect(the_bundle.lockfile).to read_as normalize_uri_file(good_lockfile)
+
+ lockfile bad_lockfile
+ bundle! "update ffi"
+ expect(the_bundle.lockfile).to read_as normalize_uri_file(good_lockfile)
+
+ lockfile bad_lockfile
+ bundle! "update empyrean"
+ expect(the_bundle.lockfile).to read_as normalize_uri_file(good_lockfile)
+
+ lockfile bad_lockfile
+ bundle! :lock
+ expect(the_bundle.lockfile).to read_as normalize_uri_file(good_lockfile)
+ end
+ end
+
+ it "works the other way with gems that have different dependencies" do
+ simulate_platform "ruby"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "nokogiri"
+ G
+
+ simulate_platform "java"
+ bundle "install"
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA", "weakling 0.0.3"
+ end
+
+ it "works with gems that have extra platform-specific runtime dependencies", :bundler => "< 2" do
+ simulate_platform x64_mac
+
+ update_repo2 do
+ build_gem "facter", "2.4.6"
+ build_gem "facter", "2.4.6" do |s|
+ s.platform = "universal-darwin"
+ s.add_runtime_dependency "CFPropertyList"
+ end
+ build_gem "CFPropertyList"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+
+ gem "facter"
+ G
+
+ expect(out).to include "Unable to use the platform-specific (universal-darwin) version of facter (2.4.6) " \
+ "because it has different dependencies from the ruby version. " \
+ "To use the platform-specific version of the gem, run `bundle config specific_platform true` and install again."
+
+ expect(the_bundle).to include_gem "facter 2.4.6"
+ expect(the_bundle).not_to include_gem "CFPropertyList"
+ end
+
+ it "fetches gems again after changing the version of Ruby" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ G
+
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+
+ new_version = Gem::ConfigMap[:ruby_version] == "1.8" ? "1.9.1" : "1.8"
+ FileUtils.mv(vendored_gems, bundled_app("vendor/bundle", Gem.ruby_engine, new_version))
+
+ bundle! :install
+ expect(vendored_gems("gems/rack-1.0.0")).to exist
+ end
+end
+
+RSpec.describe "bundle install with platform conditionals" do
+ it "installs gems tagged w/ the current platforms" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ platforms :#{local_tag} do
+ gem "nokogiri"
+ end
+ G
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ end
+
+ it "does not install gems tagged w/ another platforms" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ platforms :#{not_local_tag} do
+ gem "nokogiri"
+ end
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).not_to include_gems "nokogiri 1.4.2"
+ end
+
+ it "installs gems tagged w/ the current platforms inline" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri", :platforms => :#{local_tag}
+ G
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ end
+
+ it "does not install gems tagged w/ another platforms inline" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "nokogiri", :platforms => :#{not_local_tag}
+ G
+ expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).not_to include_gems "nokogiri 1.4.2"
+ end
+
+ it "installs gems tagged w/ the current platform inline" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri", :platform => :#{local_tag}
+ G
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ end
+
+ it "doesn't install gems tagged w/ another platform inline" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri", :platform => :#{not_local_tag}
+ G
+ expect(the_bundle).not_to include_gems "nokogiri 1.4.2"
+ end
+
+ it "does not blow up on sources with all platform-excluded specs" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ platform :#{not_local_tag} do
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ end
+ G
+
+ bundle :list
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "does not attempt to install gems from :rbx when using --local" do
+ simulate_platform "ruby"
+ simulate_ruby_engine "ruby"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "some_gem", :platform => :rbx
+ G
+
+ bundle "install --local"
+ expect(out).not_to match(/Could not find gem 'some_gem/)
+ end
+
+ it "does not attempt to install gems from other rubies when using --local" do
+ simulate_platform "ruby"
+ simulate_ruby_engine "ruby"
+ other_ruby_version_tag = RUBY_VERSION =~ /^1\.8/ ? :ruby_19 : :ruby_18
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "some_gem", platform: :#{other_ruby_version_tag}
+ G
+
+ bundle "install --local"
+ expect(out).not_to match(/Could not find gem 'some_gem/)
+ end
+
+ it "prints a helpful warning when a dependency is unused on any platform" do
+ simulate_platform "ruby"
+ simulate_ruby_engine "ruby"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", :platform => [:mingw, :mswin, :x64_mingw, :jruby]
+ G
+
+ bundle! "install"
+
+ expect(out).to include <<-O.strip
+The dependency #{Gem::Dependency.new("rack", ">= 0")} will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
+ O
+ end
+
+ context "when disable_platform_warnings is true" do
+ before { bundle! "config disable_platform_warnings true" }
+
+ it "does not print the warning when a dependency is unused on any platform" do
+ simulate_platform "ruby"
+ simulate_ruby_engine "ruby"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", :platform => [:mingw, :mswin, :x64_mingw, :jruby]
+ G
+
+ bundle! "install"
+
+ expect(out).not_to match(/The dependency (.*) will be unused/)
+ end
+ end
+end
+
+RSpec.describe "when a gem has no architecture" do
+ it "still installs correctly" do
+ simulate_platform mswin
+
+ gemfile <<-G
+ # Try to install gem with nil arch
+ source "http://localgemserver.test/"
+ gem "rcov"
+ G
+
+ bundle :install, :artifice => "windows"
+ expect(the_bundle).to include_gems "rcov 1.0.0"
+ end
+end
diff --git a/spec/bundler/install/gemfile/ruby_spec.rb b/spec/bundler/install/gemfile/ruby_spec.rb
new file mode 100644
index 0000000000..24fe021fa3
--- /dev/null
+++ b/spec/bundler/install/gemfile/ruby_spec.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+RSpec.describe "ruby requirement" do
+ def locked_ruby_version
+ Bundler::RubyVersion.from_string(Bundler::LockfileParser.new(lockfile).ruby_version)
+ end
+
+ # As discovered by https://github.com/bundler/bundler/issues/4147, there is
+ # no test coverage to ensure that adding a gem is possible with a ruby
+ # requirement. This test verifies the fix, committed in bfbad5c5.
+ it "allows adding gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "#{RUBY_VERSION}"
+ gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "#{RUBY_VERSION}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems "rack-obama 1.0"
+ end
+
+ it "allows removing the ruby version requirement" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "~> #{RUBY_VERSION}"
+ gem "rack"
+ G
+
+ expect(lockfile).to include("RUBY VERSION")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(lockfile).not_to include("RUBY VERSION")
+ end
+
+ it "allows changing the ruby version requirement to something compatible" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby ">= 1.0.0"
+ gem "rack"
+ G
+
+ expect(locked_ruby_version).to eq(Bundler::RubyVersion.system)
+
+ simulate_ruby_version "5100"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby ">= 1.0.1"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(locked_ruby_version).to eq(Bundler::RubyVersion.system)
+ end
+
+ it "allows changing the ruby version requirement to something incompatible" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby ">= 1.0.0"
+ gem "rack"
+ G
+
+ expect(locked_ruby_version).to eq(Bundler::RubyVersion.system)
+
+ simulate_ruby_version "5100"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby ">= 5000.0"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(locked_ruby_version.versions).to eq(["5100"])
+ end
+
+ it "allows requirements with trailing whitespace" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ ruby "#{RUBY_VERSION}\\n \t\\n"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "fails gracefully with malformed requirements" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby ">= 0", "-.\\0"
+ gem "rack"
+ G
+
+ expect(out).to include("There was an error parsing") # i.e. DSL error, not error template
+ end
+end
diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb
new file mode 100644
index 0000000000..c814d0de76
--- /dev/null
+++ b/spec/bundler/install/gemfile/sources_spec.rb
@@ -0,0 +1,619 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with gems on multiple sources" do
+ # repo1 is built automatically before all of the specs run
+ # it contains rack-obama 1.0.0 and rack 0.9.1 & 1.0.0 amongst other gems
+
+ context "without source affinity" do
+ before do
+ # Oh no! Someone evil is trying to hijack rack :(
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo3 do
+ build_gem "rack", repo3_rack_version do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+ end
+ end
+
+ context "with multiple toplevel sources" do
+ let(:repo3_rack_version) { "1.0.0" }
+
+ before do
+ gemfile <<-G
+ source "file://localhost#{gem_repo3}"
+ source "file://localhost#{gem_repo1}"
+ gem "rack-obama"
+ gem "rack"
+ G
+ bundle "config major_deprecations true"
+ end
+
+ it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first", :bundler => "< 2" do
+ bundle :install
+
+ expect(out).to have_major_deprecation a_string_including("Your Gemfile contains multiple primary sources.")
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).to include(normalize_uri_file("Installed from: file://localhost#{gem_repo1}"))
+ expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1")
+ end
+
+ it "errors when disable_multisource is set" do
+ bundle "config disable_multisource true"
+ bundle :install
+ expect(out).to include("Each source after the first must include a block")
+ expect(exitstatus).to eq(4) if exitstatus
+ end
+ end
+
+ context "when different versions of the same gem are in multiple sources" do
+ let(:repo3_rack_version) { "1.2" }
+
+ before do
+ gemfile <<-G
+ source "file://localhost#{gem_repo3}"
+ source "file://localhost#{gem_repo1}"
+ gem "rack-obama"
+ gem "rack", "1.0.0" # force it to install the working version in repo1
+ G
+ bundle "config major_deprecations true"
+ end
+
+ it "warns about ambiguous gems, but installs anyway", :bundler => "< 2" do
+ bundle :install
+
+ expect(out).to have_major_deprecation a_string_including("Your Gemfile contains multiple primary sources.")
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).to include(normalize_uri_file("Installed from: file://localhost#{gem_repo1}"))
+ expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1")
+ end
+ end
+ end
+
+ context "with source affinity" do
+ context "with sources given by a block" do
+ before do
+ # Oh no! Someone evil is trying to hijack rack :(
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo3 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+
+ build_gem "rack-obama" do |s|
+ s.add_dependency "rack"
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo3}"
+ source "file://#{gem_repo1}" do
+ gem "thin" # comes first to test name sorting
+ gem "rack"
+ end
+ gem "rack-obama" # shoud come from repo3!
+ G
+ end
+
+ it "installs the gems without any warning" do
+ bundle! :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("rack-obama 1.0.0")
+ expect(the_bundle).to include_gems("rack 1.0.0", :source => "remote1")
+ end
+
+ it "can cache and deploy" do
+ bundle! :package
+
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/rack-obama-1.0.gem")).to exist
+
+ bundle! :install, forgotten_command_line_options(:deployment => true)
+
+ expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0")
+ end
+ end
+
+ context "with sources set by an option" do
+ before do
+ # Oh no! Someone evil is trying to hijack rack :(
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo3 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+
+ build_gem "rack-obama" do |s|
+ s.add_dependency "rack"
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo3}"
+ gem "rack-obama" # should come from repo3!
+ gem "rack", :source => "file://#{gem_repo1}"
+ G
+ end
+
+ it "installs the gems without any warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0")
+ end
+ end
+
+ context "when a pinned gem has an indirect dependency" do
+ before do
+ build_repo gem_repo3 do
+ build_gem "depends_on_rack", "1.0.1" do |s|
+ s.add_dependency "rack"
+ end
+ end
+ end
+
+ context "when the indirect dependency is in the pinned source" do
+ before do
+ # we need a working rack gem in repo3
+ update_repo gem_repo3 do
+ build_gem "rack", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ source "file://#{gem_repo3}" do
+ gem "depends_on_rack"
+ end
+ G
+ end
+
+ context "and not in any other sources" do
+ before do
+ build_repo(gem_repo2) {}
+ end
+
+ it "installs from the same source without any warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+
+ context "and in another source" do
+ before do
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo2 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+ end
+ end
+
+ context "when lockfile_uses_separate_rubygems_sources is set" do
+ before do
+ bundle! "config lockfile_uses_separate_rubygems_sources true"
+ bundle! "config disable_multisource true"
+ end
+
+ it "installs from the same source without any warning" do
+ bundle! :install
+
+ expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+
+ # when there is already a lock file, and the gems are missing, so try again
+ system_gems []
+ bundle! :install
+
+ expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+ end
+ end
+
+ context "when the indirect dependency is in a different source" do
+ before do
+ # In these tests, we need a working rack gem in repo2 and not repo3
+ build_repo gem_repo2 do
+ build_gem "rack", "1.0.0"
+ end
+ end
+
+ context "and not in any other sources" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ source "file://#{gem_repo3}" do
+ gem "depends_on_rack"
+ end
+ G
+ end
+
+ it "installs from the other source without any warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+
+ context "and in yet another source" do
+ before do
+ gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ source "file://localhost#{gem_repo2}"
+ source "file://localhost#{gem_repo3}" do
+ gem "depends_on_rack"
+ end
+ G
+ end
+
+ it "installs from the other source and warns about ambiguous gems", :bundler => "< 2" do
+ bundle "config major_deprecations true"
+ bundle :install
+ expect(out).to have_major_deprecation a_string_including("Your Gemfile contains multiple primary sources.")
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).to include(normalize_uri_file("Installed from: file://localhost#{gem_repo2}"))
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+
+ context "and only the dependency is pinned" do
+ before do
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo2 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo3}" # contains depends_on_rack
+ source "file://#{gem_repo2}" # contains broken rack
+
+ gem "depends_on_rack" # installed from gem_repo3
+ gem "rack", :source => "file://#{gem_repo1}"
+ G
+ end
+
+ it "installs the dependency from the pinned source without warning", :bundler => "< 2" do
+ bundle :install
+
+ expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+
+ # In https://github.com/bundler/bundler/issues/3585 this failed
+ # when there is already a lock file, and the gems are missing, so try again
+ system_gems []
+ bundle :install
+
+ expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+ end
+ end
+
+ context "when a top-level gem has an indirect dependency" do
+ context "when lockfile_uses_separate_rubygems_sources is set" do
+ before do
+ bundle! "config lockfile_uses_separate_rubygems_sources true"
+ bundle! "config disable_multisource true"
+ end
+
+ before do
+ build_repo gem_repo2 do
+ build_gem "depends_on_rack", "1.0.1" do |s|
+ s.add_dependency "rack"
+ end
+ end
+
+ build_repo gem_repo3 do
+ build_gem "unrelated_gem", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "depends_on_rack"
+
+ source "file://#{gem_repo3}" do
+ gem "unrelated_gem"
+ end
+ G
+ end
+
+ context "and the dependency is only in the top-level source" do
+ before do
+ update_repo gem_repo2 do
+ build_gem "rack", "1.0.0"
+ end
+ end
+
+ it "installs all gems without warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", "unrelated_gem 1.0.0")
+ end
+ end
+
+ context "and the dependency is only in a pinned source" do
+ before do
+ update_repo gem_repo3 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+ end
+ end
+
+ it "does not find the dependency" do
+ bundle :install
+ expect(out).to include("Could not find gem 'rack', which is required by gem 'depends_on_rack', in any of the relevant sources")
+ end
+ end
+
+ context "and the dependency is in both the top-level and a pinned source" do
+ before do
+ update_repo gem_repo2 do
+ build_gem "rack", "1.0.0"
+ end
+
+ update_repo gem_repo3 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+ end
+ end
+
+ it "installs the dependency from the top-level source without warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", "unrelated_gem 1.0.0")
+ end
+ end
+ end
+ end
+
+ context "with a gem that is only found in the wrong source" do
+ before do
+ build_repo gem_repo3 do
+ build_gem "not_in_repo1", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo3}"
+ gem "not_in_repo1", :source => "file://#{gem_repo1}"
+ G
+ end
+
+ it "does not install the gem" do
+ bundle :install
+ expect(out).to include("Could not find gem 'not_in_repo1'")
+ end
+ end
+
+ context "with an existing lockfile" do
+ before do
+ system_gems "rack-0.9.1", "rack-1.0.0", :path => :bundle_path
+
+ lockfile <<-L
+ GEM
+ remote: file:#{gem_repo1}
+ remote: file:#{gem_repo3}
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack!
+ L
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ source "file://#{gem_repo3}" do
+ gem 'rack'
+ end
+ G
+ end
+
+ # Reproduction of https://github.com/bundler/bundler/issues/3298
+ it "does not unlock the installed gem on exec" do
+ expect(the_bundle).to include_gems("rack 0.9.1")
+ end
+ end
+
+ context "with a path gem in the same Gemfile" do
+ before do
+ build_lib "foo"
+
+ gemfile <<-G
+ gem "rack", :source => "file://#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+ end
+
+ it "does not unlock the non-path gem after install" do
+ bundle! :install
+
+ bundle! %(exec ruby -e 'puts "OK"'), :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+
+ expect(out).to include("OK")
+ end
+ end
+ end
+
+ context "when an older version of the same gem also ships with Ruby" do
+ before do
+ system_gems "rack-0.9.1"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack" # shoud come from repo1!
+ G
+ end
+
+ it "installs the gems without any warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("rack 1.0.0")
+ end
+ end
+
+ context "when a single source contains multiple locked gems" do
+ before do
+ # 1. With these gems,
+ build_repo4 do
+ build_gem "foo", "0.1"
+ build_gem "bar", "0.1"
+ end
+
+ # 2. Installing this gemfile will produce...
+ gemfile <<-G
+ source 'file://#{gem_repo1}'
+ gem 'rack'
+ gem 'foo', '~> 0.1', :source => 'file://#{gem_repo4}'
+ gem 'bar', '~> 0.1', :source => 'file://#{gem_repo4}'
+ G
+
+ # 3. this lockfile.
+ lockfile <<-L
+ GEM
+ remote: file:/Users/andre/src/bundler/bundler/tmp/gems/remote1/
+ remote: file:/Users/andre/src/bundler/bundler/tmp/gems/remote4/
+ specs:
+ bar (0.1)
+ foo (0.1)
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ bar (~> 0.1)!
+ foo (~> 0.1)!
+ rack
+ L
+
+ bundle! :install, forgotten_command_line_options(:path => "../gems/system")
+
+ # 4. Then we add some new versions...
+ update_repo4 do
+ build_gem "foo", "0.2"
+ build_gem "bar", "0.3"
+ end
+ end
+
+ it "allows them to be unlocked separately" do
+ # 5. and install this gemfile, updating only foo.
+ install_gemfile <<-G
+ source 'file://#{gem_repo1}'
+ gem 'rack'
+ gem 'foo', '~> 0.2', :source => 'file://#{gem_repo4}'
+ gem 'bar', '~> 0.1', :source => 'file://#{gem_repo4}'
+ G
+
+ # 6. Which should update foo to 0.2, but not the (locked) bar 0.1
+ expect(the_bundle).to include_gems("foo 0.2", "bar 0.1")
+ end
+ end
+
+ context "re-resolving" do
+ context "when there is a mix of sources in the gemfile" do
+ before do
+ build_repo3
+ build_lib "path1"
+ build_lib "path2"
+ build_git "git1"
+ build_git "git2"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ source "file://#{gem_repo3}" do
+ gem "rack"
+ end
+
+ gem "path1", :path => "#{lib_path("path1-1.0")}"
+ gem "path2", :path => "#{lib_path("path2-1.0")}"
+ gem "git1", :git => "#{lib_path("git1-1.0")}"
+ gem "git2", :git => "#{lib_path("git2-1.0")}"
+ G
+ end
+
+ it "does not re-resolve" do
+ bundle :install, :verbose => true
+ expect(out).to include("using resolution from the lockfile")
+ expect(out).not_to include("re-resolving dependencies")
+ end
+ end
+ end
+
+ context "when a gem is installed to system gems" do
+ before do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ context "and the gemfile changes" do
+ it "is still able to find that gem from remote sources" do
+ source_uri = "file://#{gem_repo1}"
+ second_uri = "file://#{gem_repo4}"
+
+ build_repo4 do
+ build_gem "rack", "2.0.1.1.forked"
+ build_gem "thor", "0.19.1.1.forked"
+ end
+
+ # When this gemfile is installed...
+ gemfile <<-G
+ source "#{source_uri}"
+
+ source "#{second_uri}" do
+ gem "rack", "2.0.1.1.forked"
+ gem "thor"
+ end
+ gem "rack-obama"
+ G
+
+ # It creates this lockfile.
+ lockfile <<-L
+ GEM
+ remote: #{source_uri}/
+ remote: #{second_uri}/
+ specs:
+ rack (2.0.1.1.forked)
+ rack-obama (1.0)
+ rack
+ thor (0.19.1.1.forked)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack (= 2.0.1.1.forked)!
+ rack-obama
+ thor!
+ L
+
+ # Then we change the Gemfile by adding a version to thor
+ gemfile <<-G
+ source "#{source_uri}"
+
+ source "#{second_uri}" do
+ gem "rack", "2.0.1.1.forked"
+ gem "thor", "0.19.1.1.forked"
+ end
+ gem "rack-obama"
+ G
+
+ # But we should still be able to find rack 2.0.1.1.forked and install it
+ bundle! :install
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb
new file mode 100644
index 0000000000..9c725416d5
--- /dev/null
+++ b/spec/bundler/install/gemfile/specific_platform_spec.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with specific_platform enabled" do
+ before do
+ bundle "config specific_platform true"
+
+ build_repo2 do
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1")
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86_64-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x64-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "universal-darwin" }
+
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86_64-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x64-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5")
+
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "universal-darwin" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86_64-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x64-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4")
+
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3")
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86_64-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x64-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "universal-darwin" }
+
+ build_gem("google-protobuf", "3.0.0.alpha.4.0")
+ build_gem("google-protobuf", "3.0.0.alpha.3.1.pre")
+ build_gem("google-protobuf", "3.0.0.alpha.3")
+ build_gem("google-protobuf", "3.0.0.alpha.2.0")
+ build_gem("google-protobuf", "3.0.0.alpha.1.1")
+ build_gem("google-protobuf", "3.0.0.alpha.1.0")
+
+ build_gem("facter", "2.4.6")
+ build_gem("facter", "2.4.6") do |s|
+ s.platform = "universal-darwin"
+ s.add_runtime_dependency "CFPropertyList"
+ end
+ build_gem("CFPropertyList")
+ end
+ end
+
+ let(:google_protobuf) { <<-G }
+ source "file:#{gem_repo2}"
+ gem "google-protobuf"
+ G
+
+ context "when on a darwin machine" do
+ before { simulate_platform "x86_64-darwin-15" }
+
+ it "locks to both the specific darwin platform and ruby" do
+ install_gemfile!(google_protobuf)
+ expect(the_bundle.locked_gems.platforms).to eq([pl("ruby"), pl("x86_64-darwin-15")])
+ expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[
+ google-protobuf-3.0.0.alpha.5.0.5.1
+ google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
+ ])
+ end
+
+ it "caches both the universal-darwin and ruby gems when --all-platforms is passed" do
+ gemfile(google_protobuf)
+ bundle! "package --all-platforms"
+ expect([cached_gem("google-protobuf-3.0.0.alpha.5.0.5.1"), cached_gem("google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin")]).
+ to all(exist)
+ end
+
+ it "uses the platform-specific gem with extra dependencies" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo2}"
+ gem "facter"
+ G
+
+ expect(the_bundle.locked_gems.platforms).to eq([pl("ruby"), pl("x86_64-darwin-15")])
+ expect(the_bundle).to include_gems("facter 2.4.6 universal-darwin", "CFPropertyList 1.0")
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(["CFPropertyList-1.0",
+ "facter-2.4.6",
+ "facter-2.4.6-universal-darwin"])
+ end
+
+ context "when adding a platform via lock --add_platform" do
+ it "adds the foreign platform" do
+ install_gemfile!(google_protobuf)
+ bundle! "lock --add-platform=#{x64_mingw}"
+
+ expect(the_bundle.locked_gems.platforms).to eq([rb, x64_mingw, 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
+ 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
+
+ it "falls back on plain ruby when that version doesnt have a platform-specific gem" do
+ install_gemfile!(google_protobuf)
+ bundle! "lock --add-platform=#{java}"
+
+ expect(the_bundle.locked_gems.platforms).to eq([java, rb, 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
+ google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
+ ])
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile_spec.rb b/spec/bundler/install/gemfile_spec.rb
new file mode 100644
index 0000000000..e74c5ffe59
--- /dev/null
+++ b/spec/bundler/install/gemfile_spec.rb
@@ -0,0 +1,145 @@
+# encoding: utf-8
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ context "with duplicated gems" do
+ it "will display a warning" do
+ install_gemfile <<-G
+ gem 'rails', '~> 4.0.0'
+ gem 'rails', '~> 4.0.0'
+ G
+ expect(out).to include("more than once")
+ end
+ end
+
+ context "with --gemfile" do
+ it "finds the gemfile" do
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle :install, :gemfile => bundled_app("NotGemfile")
+
+ # Specify BUNDLE_GEMFILE for `the_bundle`
+ # to retrieve the proper Gemfile
+ ENV["BUNDLE_GEMFILE"] = "NotGemfile"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ context "with gemfile set via config" do
+ before do
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle "config --local gemfile #{bundled_app("NotGemfile")}"
+ end
+ it "uses the gemfile to install" do
+ bundle "install"
+ bundle "list"
+
+ expect(out).to include("rack (1.0.0)")
+ end
+ it "uses the gemfile while in a subdirectory" do
+ bundled_app("subdir").mkpath
+ Dir.chdir(bundled_app("subdir")) do
+ bundle "install"
+ bundle "list"
+
+ expect(out).to include("rack (1.0.0)")
+ end
+ end
+ end
+
+ context "with deprecated features" do
+ before :each do
+ in_app_root
+ end
+
+ it "reports that lib is an invalid option" do
+ gemfile <<-G
+ gem "rack", :lib => "rack"
+ G
+
+ bundle :install
+ expect(out).to match(/You passed :lib as an option for gem 'rack', but it is invalid/)
+ end
+ end
+
+ context "with prefer_gems_rb set" do
+ before { bundle! "config prefer_gems_rb true" }
+
+ it "prefers gems.rb to Gemfile" do
+ create_file("gems.rb", "gem 'bundler'")
+ create_file("Gemfile", "raise 'wrong Gemfile!'")
+
+ bundle! :install
+
+ expect(bundled_app("gems.rb")).to be_file
+ expect(bundled_app("Gemfile.lock")).not_to be_file
+
+ expect(the_bundle).to include_gem "bundler #{Bundler::VERSION}"
+ end
+ end
+
+ context "with engine specified in symbol" do
+ it "does not raise any error parsing Gemfile" do
+ simulate_ruby_version "2.3.0" do
+ simulate_ruby_engine "jruby", "9.1.2.0" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ ruby "2.3.0", :engine => :jruby, :engine_version => "9.1.2.0"
+ G
+
+ expect(out).to match(/Bundle complete!/)
+ end
+ end
+ end
+
+ it "installation succeeds" do
+ simulate_ruby_version "2.3.0" do
+ simulate_ruby_engine "jruby", "9.1.2.0" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ ruby "2.3.0", :engine => :jruby, :engine_version => "9.1.2.0"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+ end
+
+ context "with a Gemfile containing non-US-ASCII characters" do
+ it "reads the Gemfile with the UTF-8 encoding by default" do
+ skip "Ruby 1.8 has no encodings" if RUBY_VERSION < "1.9"
+
+ install_gemfile <<-G
+ str = "Il était une fois ..."
+ puts "The source encoding is: " + str.encoding.name
+ G
+
+ expect(out).to include("The source encoding is: UTF-8")
+ expect(out).not_to include("The source encoding is: ASCII-8BIT")
+ expect(out).to include("Bundle complete!")
+ end
+
+ it "respects the magic encoding comment" do
+ skip "Ruby 1.8 has no encodings" if RUBY_VERSION < "1.9"
+
+ # NOTE: This works thanks to #eval interpreting the magic encoding comment
+ install_gemfile <<-G
+ # encoding: iso-8859-1
+ str = "Il #{"\xE9".dup.force_encoding("binary")}tait une fois ..."
+ puts "The source encoding is: " + str.encoding.name
+ G
+
+ expect(out).to include("The source encoding is: ISO-8859-1")
+ expect(out).to include("Bundle complete!")
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/compact_index_spec.rb b/spec/bundler/install/gems/compact_index_spec.rb
new file mode 100644
index 0000000000..02a37a77d5
--- /dev/null
+++ b/spec/bundler/install/gems/compact_index_spec.rb
@@ -0,0 +1,940 @@
+# frozen_string_literal: true
+
+RSpec.describe "compact index api" do
+ let(:source_hostname) { "localgemserver.test" }
+ let(:source_uri) { "http://#{source_hostname}" }
+
+ it "should use the API" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should URI encode gem names" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem " sinatra"
+ G
+
+ bundle :install, :artifice => "compact_index"
+ expect(out).to include("' sinatra' is not a valid gem name because it contains whitespace.")
+ end
+
+ it "should handle nested dependencies" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rails"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems(
+ "rails 2.3.2",
+ "actionpack 2.3.2",
+ "activerecord 2.3.2",
+ "actionmailer 2.3.2",
+ "activeresource 2.3.2",
+ "activesupport 2.3.2"
+ )
+ end
+
+ it "should handle case sensitivity conflicts" do
+ build_repo4 do
+ build_gem "rack", "1.0" do |s|
+ s.add_runtime_dependency("Rack", "0.1")
+ end
+ build_gem "Rack", "0.1"
+ end
+
+ install_gemfile! <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4 }
+ source "#{source_uri}"
+ gem "rack", "1.0"
+ gem "Rack", "0.1"
+ G
+
+ # can't use `include_gems` here since the `require` will conflict on a
+ # case-insensitive FS
+ run! "Bundler.require; puts Gem.loaded_specs.values_at('rack', 'Rack').map(&:full_name)"
+ expect(last_command.stdout).to eq("rack-1.0\nRack-0.1")
+ end
+
+ it "should handle multiple gem dependencies on the same gem" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "net-sftp"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(the_bundle).to include_gems "net-sftp 1.1.1"
+ end
+
+ it "should use the endpoint when using --deployment" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+ bundle! :install, :artifice => "compact_index"
+
+ bundle! :install, forgotten_command_line_options(:deployment => true, :path => "vendor/bundle").merge(:artifice => "compact_index")
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles git dependencies that are in rubygems" do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ s.add_dependency "rails", "2.3.2"
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ git "file:///#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+
+ bundle! :install, :artifice => "compact_index"
+
+ expect(the_bundle).to include_gems("rails 2.3.2")
+ end
+
+ it "handles git dependencies that are in rubygems using --deployment" do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ s.add_dependency "rails", "2.3.2"
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'foo', :git => "file:///#{lib_path("foo-1.0")}"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+
+ bundle "install --deployment", :artifice => "compact_index"
+
+ expect(the_bundle).to include_gems("rails 2.3.2")
+ end
+
+ it "doesn't fail if you only have a git gem with no deps when using --deployment" do
+ build_git "foo"
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'foo', :git => "file:///#{lib_path("foo-1.0")}"
+ G
+
+ bundle "install", :artifice => "compact_index"
+ bundle! :install, forgotten_command_line_options(:deployment => true).merge(:artifice => "compact_index")
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "falls back when the API errors out" do
+ simulate_platform mswin
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rcov"
+ G
+
+ bundle! :install, :artifice => "windows"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rcov 1.0.0"
+ end
+
+ it "falls back when the API URL returns 403 Forbidden" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :verbose => true, :artifice => "compact_index_forbidden"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "falls back when the versions endpoint has a checksum mismatch" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :verbose => true, :artifice => "compact_index_checksum_mismatch"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(out).to include <<-'WARN'
+The checksum of /versions does not match the checksum provided by the server! Something is wrong (local checksum is "\"d41d8cd98f00b204e9800998ecf8427e\"", was expecting "\"123\"").
+ WARN
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "falls back when the user's home directory does not exist or is not writable" do
+ ENV["HOME"] = tmp("missing_home").to_s
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles host redirects" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index_host_redirect"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles host redirects without Net::HTTP::Persistent" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ FileUtils.mkdir_p lib_path
+ File.open(lib_path("disable_net_http_persistent.rb"), "w") do |h|
+ h.write <<-H
+ module Kernel
+ alias require_without_disabled_net_http require
+ def require(*args)
+ raise LoadError, 'simulated' if args.first == 'openssl' && !caller.grep(/vendored_persistent/).empty?
+ require_without_disabled_net_http(*args)
+ end
+ end
+ H
+ end
+
+ bundle! :install, :artifice => "compact_index_host_redirect", :requires => [lib_path("disable_net_http_persistent.rb")]
+ expect(out).to_not match(/Too many redirects/)
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "times out when Bundler::Fetcher redirects too much" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "compact_index_redirects"
+ expect(out).to match(/Too many redirects/)
+ end
+
+ context "when --full-index is specified" do
+ it "should use the modern index for install" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --full-index", :artifice => "compact_index"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should use the modern index for update" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! "update --full-index", :artifice => "compact_index", :all => bundle_update_requires_all?
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ it "does not double check for gems that are only installed locally" do
+ system_gems %w[rack-1.0.0 thin-1.0 net_a-1.0]
+ bundle! "config --local path.system true"
+ ENV["BUNDLER_SPEC_ALL_REQUESTS"] = strip_whitespace(<<-EOS).strip
+ #{source_uri}/versions
+ #{source_uri}/info/rack
+ EOS
+
+ install_gemfile! <<-G, :artifice => "compact_index", :verbose => true
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ expect(last_command.stdboth).not_to include "Double checking"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources", :bundler => "< 2" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle! :install, :artifice => "compact_index_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources with source blocks" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ install_gemfile! <<-G, :artifice => "compact_index_extra", :verbose => true
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
+ end
+
+ it "fetches gem versions even when those gems are already installed" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack", "1.0.0"
+ G
+ bundle! :install, :artifice => "compact_index_extra_api"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+
+ build_repo4 do
+ build_gem "rack", "1.2" do |s|
+ s.executables = "rackup"
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}" do; end
+ source "#{source_uri}/extra"
+ gem "rack", "1.2"
+ G
+ bundle! :install, :artifice => "compact_index_extra_api"
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+
+ it "considers all possible versions of dependencies from all api gem sources", :bundler => "< 2" do
+ # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
+ # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
+ # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
+ # repo and installs it.
+ build_repo4 do
+ build_gem "activesupport", "1.2.0"
+ build_gem "somegem", "1.0.0" do |s|
+ s.add_dependency "activesupport", "1.2.3" # This version exists only in repo1
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem 'somegem', '1.0.0'
+ G
+
+ bundle! :install, :artifice => "compact_index_extra_api"
+
+ expect(the_bundle).to include_gems "somegem 1.0.0"
+ expect(the_bundle).to include_gems "activesupport 1.2.3"
+ end
+
+ it "considers all possible versions of dependencies from all api gem sources when using blocks", :bundler => "< 2" do
+ # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
+ # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
+ # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
+ # repo and installs it.
+ build_repo4 do
+ build_gem "activesupport", "1.2.0"
+ build_gem "somegem", "1.0.0" do |s|
+ s.add_dependency "activesupport", "1.2.3" # This version exists only in repo1
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem 'somegem', '1.0.0'
+ end
+ G
+
+ bundle! :install, :artifice => "compact_index_extra_api"
+
+ expect(the_bundle).to include_gems "somegem 1.0.0"
+ expect(the_bundle).to include_gems "activesupport 1.2.3"
+ end
+
+ it "prints API output properly with back deps" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ bundle! :install, :artifice => "compact_index_extra"
+
+ expect(out).to include("Fetching gem metadata from http://localgemserver.test/")
+ 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
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ build_gem "missing"
+ # need to hit the limit
+ 1.upto(Bundler::Source::Rubygems::API_REQUEST_LIMIT) do |i|
+ build_gem "gem#{i}"
+ end
+
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ bundle! :install, :artifice => "compact_index_extra_missing"
+ 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
+ build_repo4 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ build_gem "missing"
+ # need to hit the limit
+ 1.upto(Bundler::Source::Rubygems::API_REQUEST_LIMIT) do |i|
+ build_gem "gem#{i}"
+ end
+
+ FileUtils.rm_rf Dir[gem_repo4("gems/foo-*.gem")]
+ end
+
+ install_gemfile! <<-G, :artifice => "compact_index_extra_api_missing"
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ expect(the_bundle).to include_gem "back_deps 1.0"
+ end
+
+ it "uses the endpoint if all sources support it" do
+ gemfile <<-G
+ source "#{source_uri}"
+
+ gem 'foo'
+ G
+
+ bundle! :install, :artifice => "compact_index_api_missing"
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources using --deployment", :bundler => "< 2" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle! :install, :artifice => "compact_index_extra"
+
+ bundle "install --deployment", :artifice => "compact_index_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources using --deployment with blocks" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ bundle! :install, :artifice => "compact_index_extra"
+
+ bundle "install --deployment", :artifice => "compact_index_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "does not refetch if the only unmet dependency is bundler" do
+ gemfile <<-G
+ source "#{source_uri}"
+
+ gem "bundler_dep"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ 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 => "< 2" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --binstubs", :artifice => "compact_index"
+
+ gembin "rackup"
+ expect(out).to eq("1.0.0")
+ end
+
+ it "installs the bins when using --path and uses autoclean", :bundler => "< 2" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --path vendor/bundle", :artifice => "compact_index"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "installs the bins when using --path and uses bundle clean", :bundler => "< 2" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --path vendor/bundle --no-clean", :artifice => "compact_index"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "prints post_install_messages" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack-obama'
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Post-install message from rack:")
+ end
+
+ it "should display the post install message for a dependency" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack_middleware'
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ end
+
+ context "when using basic authentication" do
+ let(:user) { "user" }
+ let(:password) { "pass" }
+ let(:basic_auth_source_uri) do
+ uri = URI.parse(source_uri)
+ uri.user = user
+ uri.password = password
+
+ uri
+ end
+
+ it "passes basic authentication details and strips out creds" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index_basic_authentication"
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "strips http basic authentication creds for modern index" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "endopint_marshal_fail_basic_authentication"
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "strips http basic auth creds when it can't reach the server" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_500"
+ expect(out).not_to include("#{user}:#{password}")
+ end
+
+ it "strips http basic auth creds when warning about ambiguous sources", :bundler => "< 2" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index_basic_authentication"
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "does not pass the user / password to different hosts on redirect" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index_creds_diff_host"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ describe "with authentication details in bundle config" do
+ before do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+ end
+
+ it "reads authentication details by host name from bundle config" do
+ bundle "config #{source_hostname} #{user}:#{password}"
+
+ bundle! :install, :artifice => "compact_index_strict_basic_authentication"
+
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "reads authentication details by full url from bundle config" do
+ # The trailing slash is necessary here; Fetcher canonicalizes the URI.
+ bundle "config #{source_uri}/ #{user}:#{password}"
+
+ bundle! :install, :artifice => "compact_index_strict_basic_authentication"
+
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should use the API" do
+ bundle "config #{source_hostname} #{user}:#{password}"
+ bundle! :install, :artifice => "compact_index_strict_basic_authentication"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "prefers auth supplied in the source uri" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle "config #{source_hostname} otheruser:wrong"
+
+ bundle! :install, :artifice => "compact_index_strict_basic_authentication"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "shows instructions if auth is not provided for the source" do
+ bundle :install, :artifice => "compact_index_strict_basic_authentication"
+ expect(out).to include("bundle config #{source_hostname} username:password")
+ end
+
+ it "fails if authentication has already been provided, but failed" do
+ bundle "config #{source_hostname} #{user}:wrong"
+
+ bundle :install, :artifice => "compact_index_strict_basic_authentication"
+ expect(out).to include("Bad username or password")
+ end
+ end
+
+ describe "with no password" do
+ let(:password) { nil }
+
+ it "passes basic authentication details" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index_basic_authentication"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+
+ context "when ruby is compiled without openssl" do
+ before do
+ # Install a monkeypatch that reproduces the effects of openssl being
+ # missing when the fetcher runs, as happens in real life. The reason
+ # we can't just overwrite openssl.rb is that Artifice uses it.
+ bundled_app("broken_ssl").mkpath
+ bundled_app("broken_ssl/openssl.rb").open("w") do |f|
+ f.write <<-RUBY
+ raise LoadError, "cannot load such file -- openssl"
+ RUBY
+ end
+ end
+
+ it "explains what to do to get it" do
+ gemfile <<-G
+ source "#{source_uri.gsub(/http/, "https")}"
+ gem "rack"
+ G
+
+ bundle :install, :env => { "RUBYOPT" => "-I#{bundled_app("broken_ssl")}" }
+ expect(out).to include("OpenSSL")
+ end
+ end
+
+ context "when SSL certificate verification fails" do
+ it "explains what happened" do
+ # Install a monkeypatch that reproduces the effects of openssl raising
+ # a certificate validation error when RubyGems tries to connect.
+ gemfile <<-G
+ class Net::HTTP
+ def start
+ raise OpenSSL::SSL::SSLError, "certificate verify failed"
+ end
+ end
+
+ source "#{source_uri.gsub(/http/, "https")}"
+ gem "rack"
+ G
+
+ bundle :install
+ expect(out).to match(/could not verify the SSL certificate/i)
+ end
+ end
+
+ context ".gemrc with sources is present" do
+ before do
+ File.open(home(".gemrc"), "w") do |file|
+ file.puts({ :sources => ["https://rubygems.org"] }.to_yaml)
+ end
+ end
+
+ after do
+ home(".gemrc").rmtree
+ end
+
+ it "uses other sources declared in the Gemfile" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack'
+ G
+
+ bundle! :install, :artifice => "compact_index_forbidden"
+ end
+ end
+
+ it "performs partial update with a non-empty range" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack', '0.9.1'
+ G
+
+ # Initial install creates the cached versions file
+ bundle! :install, :artifice => "compact_index"
+
+ # Update the Gemfile so we can check subsequent install was successful
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack', '1.0.0'
+ G
+
+ # Second install should make only a partial request to /versions
+ bundle! :install, :artifice => "compact_index_partial_update"
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "performs partial update while local cache is updated by another process" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack'
+ G
+
+ # Create an empty file to trigger a partial download
+ versions = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions")
+ FileUtils.mkdir_p(File.dirname(versions))
+ FileUtils.touch(versions)
+
+ bundle! :install, :artifice => "compact_index_concurrent_download"
+
+ expect(File.read(versions)).to start_with("created_at")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "performs full update of compact index info cache if range is not satisfiable" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack', '0.9.1'
+ G
+
+ rake_info_path = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "info", "rack")
+
+ bundle! :install, :artifice => "compact_index"
+
+ expected_rack_info_content = File.read(rake_info_path)
+
+ # Modify the cache files. We expect them to be reset to the normal ones when we re-run :install
+ File.open(rake_info_path, "w") {|f| f << (expected_rack_info_content + "this is different") }
+
+ # Update the Gemfile so the next install does its normal things
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack', '1.0.0'
+ G
+
+ # The cache files now being longer means the requested range is going to be not satisfiable
+ # Bundler must end up requesting the whole file to fix things up.
+ bundle! :install, :artifice => "compact_index_range_not_satisfiable"
+
+ resulting_rack_info_content = File.read(rake_info_path)
+
+ expect(resulting_rack_info_content).to eq(expected_rack_info_content)
+ end
+
+ it "fails gracefully when the source URI has an invalid scheme" do
+ install_gemfile <<-G
+ source "htps://rubygems.org"
+ gem "rack"
+ G
+ expect(exitstatus).to eq(15) if exitstatus
+ expect(out).to end_with(<<-E.strip)
+ The request uri `htps://index.rubygems.org/versions` has an invalid scheme (`htps`). Did you mean `http` or `https`?
+ E
+ end
+
+ describe "checksum validation", :rubygems => ">= 2.3.0" do
+ it "raises when the checksum does not match" do
+ install_gemfile <<-G, :artifice => "compact_index_wrong_gem_checksum"
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ expect(exitstatus).to eq(19) if exitstatus
+ expect(out).
+ to include("Bundler cannot continue installing rack (1.0.0).").
+ and include("The checksum for the downloaded `rack-1.0.0.gem` does not match the checksum given by the server.").
+ and include("This means the contents of the downloaded gem is different from what was uploaded to the server, and could be a potential security issue.").
+ and include("To resolve this issue:").
+ and include("1. delete the downloaded gem located at: `#{default_bundle_path}/gems/rack-1.0.0/rack-1.0.0.gem`").
+ and include("2. run `bundle install`").
+ and include("If you wish to continue installing the downloaded gem, and are certain it does not pose a security issue despite the mismatching checksum, do the following:").
+ and include("1. run `bundle config disable_checksum_validation true` to turn off checksum verification").
+ and include("2. run `bundle install`").
+ and match(/\(More info: The expected SHA256 checksum was "#{"ab" * 22}", but the checksum for the downloaded gem was ".+?"\.\)/)
+ end
+
+ it "raises when the checksum is the wrong length" do
+ install_gemfile <<-G, :artifice => "compact_index_wrong_gem_checksum", :env => { "BUNDLER_SPEC_RACK_CHECKSUM" => "checksum!" }
+ source "#{source_uri}"
+ gem "rack"
+ G
+ expect(exitstatus).to eq(5) if exitstatus
+ expect(out).to include("The given checksum for rack-1.0.0 (\"checksum!\") is not a valid SHA256 hexdigest nor base64digest")
+ end
+
+ it "does not raise when disable_checksum_validation is set" do
+ bundle! "config disable_checksum_validation true"
+ install_gemfile! <<-G, :artifice => "compact_index_wrong_gem_checksum"
+ source "#{source_uri}"
+ gem "rack"
+ G
+ end
+ end
+
+ it "works when cache dir is world-writable" do
+ install_gemfile! <<-G, :artifice => "compact_index"
+ File.umask(0000)
+ source "#{source_uri}"
+ gem "rack"
+ G
+ end
+
+ it "doesn't explode when the API dependencies are wrong" do
+ install_gemfile <<-G, :artifice => "compact_index_wrong_dependencies", :env => { "DEBUG" => "true" }
+ source "#{source_uri}"
+ gem "rails"
+ G
+ deps = [Gem::Dependency.new("rake", "= 10.0.2"),
+ Gem::Dependency.new("actionpack", "= 2.3.2"),
+ Gem::Dependency.new("activerecord", "= 2.3.2"),
+ Gem::Dependency.new("actionmailer", "= 2.3.2"),
+ Gem::Dependency.new("activeresource", "= 2.3.2")]
+ expect(out).to include(<<-E.strip).and include("rails-2.3.2 from rubygems remote at #{source_uri}/ has either corrupted API or lockfile dependencies")
+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.
+ E
+ end
+
+ it "does not duplicate specs in the lockfile when updating and a dependency is not installed" do
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "#{source_uri}" do
+ gem "rails"
+ gem "activemerchant"
+ end
+ G
+ gem_command! :uninstall, "activemerchant"
+ bundle! "update rails", :artifice => "compact_index"
+ expect(lockfile.scan(/activemerchant \(/).size).to eq(1)
+ end
+end
diff --git a/spec/bundler/install/gems/dependency_api_spec.rb b/spec/bundler/install/gems/dependency_api_spec.rb
new file mode 100644
index 0000000000..2ffe4b62d7
--- /dev/null
+++ b/spec/bundler/install/gems/dependency_api_spec.rb
@@ -0,0 +1,760 @@
+# frozen_string_literal: true
+
+RSpec.describe "gemcutter's dependency API" do
+ let(:source_hostname) { "localgemserver.test" }
+ let(:source_uri) { "http://#{source_hostname}" }
+
+ it "should use the API" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should URI encode gem names" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem " sinatra"
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("' sinatra' is not a valid gem name because it contains whitespace.")
+ end
+
+ it "should handle nested dependencies" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rails"
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("Fetching gem metadata from #{source_uri}/...")
+ expect(the_bundle).to include_gems(
+ "rails 2.3.2",
+ "actionpack 2.3.2",
+ "activerecord 2.3.2",
+ "actionmailer 2.3.2",
+ "activeresource 2.3.2",
+ "activesupport 2.3.2"
+ )
+ end
+
+ it "should handle multiple gem dependencies on the same gem" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "net-sftp"
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(the_bundle).to include_gems "net-sftp 1.1.1"
+ end
+
+ it "should use the endpoint when using --deployment" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+ bundle :install, :artifice => "endpoint"
+
+ bundle! :install, forgotten_command_line_options(:deployment => true, :path => "vendor/bundle").merge(:artifice => "endpoint")
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles git dependencies that are in rubygems" do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ s.add_dependency "rails", "2.3.2"
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ git "file:///#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+
+ bundle :install, :artifice => "endpoint"
+
+ expect(the_bundle).to include_gems("rails 2.3.2")
+ end
+
+ it "handles git dependencies that are in rubygems using --deployment" do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ s.add_dependency "rails", "2.3.2"
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'foo', :git => "file:///#{lib_path("foo-1.0")}"
+ G
+
+ bundle :install, :artifice => "endpoint"
+
+ bundle "install --deployment", :artifice => "endpoint"
+
+ expect(the_bundle).to include_gems("rails 2.3.2")
+ end
+
+ it "doesn't fail if you only have a git gem with no deps when using --deployment" do
+ build_git "foo"
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'foo', :git => "file:///#{lib_path("foo-1.0")}"
+ G
+
+ bundle "install", :artifice => "endpoint"
+ bundle! :install, forgotten_command_line_options(:deployment => true).merge(:artifice => "endpoint")
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "falls back when the API errors out" do
+ simulate_platform mswin
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rcov"
+ G
+
+ bundle :install, :artifice => "windows"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rcov 1.0.0"
+ end
+
+ it "falls back when hitting the Gemcutter Dependency Limit" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "activesupport"
+ gem "actionpack"
+ gem "actionmailer"
+ gem "activeresource"
+ gem "thin"
+ gem "rack"
+ gem "rails"
+ G
+ bundle :install, :artifice => "endpoint_fallback"
+ expect(out).to include("Fetching source index from #{source_uri}")
+
+ expect(the_bundle).to include_gems(
+ "activesupport 2.3.2",
+ "actionpack 2.3.2",
+ "actionmailer 2.3.2",
+ "activeresource 2.3.2",
+ "activesupport 2.3.2",
+ "thin 1.0.0",
+ "rack 1.0.0",
+ "rails 2.3.2"
+ )
+ end
+
+ it "falls back when Gemcutter API doesn't return proper Marshal format" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :verbose => true, :artifice => "endpoint_marshal_fail"
+ expect(out).to include("could not fetch from the dependency API, trying the full index")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "falls back when the API URL returns 403 Forbidden" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :verbose => true, :artifice => "endpoint_api_forbidden"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles host redirects" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_host_redirect"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles host redirects without Net::HTTP::Persistent" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ FileUtils.mkdir_p lib_path
+ File.open(lib_path("disable_net_http_persistent.rb"), "w") do |h|
+ h.write <<-H
+ module Kernel
+ alias require_without_disabled_net_http require
+ def require(*args)
+ raise LoadError, 'simulated' if args.first == 'openssl' && !caller.grep(/vendored_persistent/).empty?
+ require_without_disabled_net_http(*args)
+ end
+ end
+ H
+ end
+
+ bundle :install, :artifice => "endpoint_host_redirect", :requires => [lib_path("disable_net_http_persistent.rb")]
+ expect(out).to_not match(/Too many redirects/)
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "timeouts when Bundler::Fetcher redirects too much" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_redirect"
+ expect(out).to match(/Too many redirects/)
+ end
+
+ context "when --full-index is specified" do
+ it "should use the modern index for install" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --full-index", :artifice => "endpoint"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should use the modern index for update" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! "update --full-index", :artifice => "endpoint", :all => bundle_update_requires_all?
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources", :bundler => "< 2" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle :install, :artifice => "endpoint_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources using blocks" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ bundle :install, :artifice => "endpoint_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
+ end
+
+ it "fetches gem versions even when those gems are already installed" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack", "1.0.0"
+ G
+ bundle :install, :artifice => "endpoint_extra_api"
+
+ build_repo4 do
+ build_gem "rack", "1.2" do |s|
+ s.executables = "rackup"
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}" do; end
+ source "#{source_uri}/extra"
+ gem "rack", "1.2"
+ G
+ bundle :install, :artifice => "endpoint_extra_api"
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+
+ it "considers all possible versions of dependencies from all api gem sources", :bundler => "< 2" do
+ # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
+ # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
+ # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
+ # repo and installs it.
+ build_repo4 do
+ build_gem "activesupport", "1.2.0"
+ build_gem "somegem", "1.0.0" do |s|
+ s.add_dependency "activesupport", "1.2.3" # This version exists only in repo1
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem 'somegem', '1.0.0'
+ G
+
+ bundle! :install, :artifice => "endpoint_extra_api"
+
+ expect(the_bundle).to include_gems "somegem 1.0.0"
+ expect(the_bundle).to include_gems "activesupport 1.2.3"
+ end
+
+ it "considers all possible versions of dependencies from all api gem sources using blocks" do
+ # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
+ # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
+ # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
+ # repo and installs it.
+ build_repo4 do
+ build_gem "activesupport", "1.2.0"
+ build_gem "somegem", "1.0.0" do |s|
+ s.add_dependency "activesupport", "1.2.3" # This version exists only in repo1
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem 'somegem', '1.0.0'
+ end
+ G
+
+ bundle :install, :artifice => "endpoint_extra_api"
+
+ expect(the_bundle).to include_gems "somegem 1.0.0"
+ expect(the_bundle).to include_gems "activesupport 1.2.3"
+ end
+
+ it "prints API output properly with back deps" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ bundle :install, :artifice => "endpoint_extra"
+
+ expect(out).to include("Fetching gem metadata from http://localgemserver.test/.")
+ 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 => "< 2" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ build_gem "missing"
+ # need to hit the limit
+ 1.upto(Bundler::Source::Rubygems::API_REQUEST_LIMIT) do |i|
+ build_gem "gem#{i}"
+ end
+
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle :install, :artifice => "endpoint_extra_missing"
+ 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
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ build_gem "missing"
+ # need to hit the limit
+ 1.upto(Bundler::Source::Rubygems::API_REQUEST_LIMIT) do |i|
+ build_gem "gem#{i}"
+ end
+
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ bundle :install, :artifice => "endpoint_extra_missing"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "uses the endpoint if all sources support it" do
+ gemfile <<-G
+ source "#{source_uri}"
+
+ gem 'foo'
+ G
+
+ bundle :install, :artifice => "endpoint_api_missing"
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources using --deployment", :bundler => "< 2" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle :install, :artifice => "endpoint_extra"
+
+ bundle "install --deployment", :artifice => "endpoint_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources using --deployment with blocks" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
+ G
+
+ bundle :install, :artifice => "endpoint_extra"
+
+ bundle "install --deployment", :artifice => "endpoint_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "does not refetch if the only unmet dependency is bundler" do
+ gemfile <<-G
+ source "#{source_uri}"
+
+ gem "bundler_dep"
+ G
+
+ bundle :install, :artifice => "endpoint"
+ 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 => "< 2" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --binstubs", :artifice => "endpoint"
+
+ gembin "rackup"
+ expect(out).to eq("1.0.0")
+ end
+
+ it "installs the bins when using --path and uses autoclean", :bundler => "< 2" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --path vendor/bundle", :artifice => "endpoint"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "installs the bins when using --path and uses bundle clean", :bundler => "< 2" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --path vendor/bundle --no-clean", :artifice => "endpoint"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "prints post_install_messages" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack-obama'
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("Post-install message from rack:")
+ end
+
+ it "should display the post install message for a dependency" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack_middleware'
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ end
+
+ context "when using basic authentication" do
+ let(:user) { "user" }
+ let(:password) { "pass" }
+ let(:basic_auth_source_uri) do
+ uri = URI.parse(source_uri)
+ uri.user = user
+ uri.password = password
+
+ uri
+ end
+
+ it "passes basic authentication details and strips out creds" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_basic_authentication"
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "strips http basic authentication creds for modern index" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endopint_marshal_fail_basic_authentication"
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "strips http basic auth creds when it can't reach the server" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_500"
+ expect(out).not_to include("#{user}:#{password}")
+ end
+
+ it "strips http basic auth creds when warning about ambiguous sources", :bundler => "< 2" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_basic_authentication"
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "does not pass the user / password to different hosts on redirect" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_creds_diff_host"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ describe "with authentication details in bundle config" do
+ before do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+ end
+
+ it "reads authentication details by host name from bundle config" do
+ bundle "config #{source_hostname} #{user}:#{password}"
+
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "reads authentication details by full url from bundle config" do
+ # The trailing slash is necessary here; Fetcher canonicalizes the URI.
+ bundle "config #{source_uri}/ #{user}:#{password}"
+
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should use the API" do
+ bundle "config #{source_hostname} #{user}:#{password}"
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "prefers auth supplied in the source uri" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle "config #{source_hostname} otheruser:wrong"
+
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "shows instructions if auth is not provided for the source" do
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ expect(out).to include("bundle config #{source_hostname} username:password")
+ end
+
+ it "fails if authentication has already been provided, but failed" do
+ bundle "config #{source_hostname} #{user}:wrong"
+
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ expect(out).to include("Bad username or password")
+ end
+ end
+
+ describe "with no password" do
+ let(:password) { nil }
+
+ it "passes basic authentication details" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_basic_authentication"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+
+ context "when ruby is compiled without openssl" do
+ before do
+ # Install a monkeypatch that reproduces the effects of openssl being
+ # missing when the fetcher runs, as happens in real life. The reason
+ # we can't just overwrite openssl.rb is that Artifice uses it.
+ bundled_app("broken_ssl").mkpath
+ bundled_app("broken_ssl/openssl.rb").open("w") do |f|
+ f.write <<-RUBY
+ raise LoadError, "cannot load such file -- openssl"
+ RUBY
+ end
+ end
+
+ it "explains what to do to get it" do
+ gemfile <<-G
+ source "#{source_uri.gsub(/http/, "https")}"
+ gem "rack"
+ G
+
+ bundle :install, :env => { "RUBYOPT" => "-I#{bundled_app("broken_ssl")}" }
+ expect(out).to include("OpenSSL")
+ end
+ end
+
+ context "when SSL certificate verification fails" do
+ it "explains what happened" do
+ # Install a monkeypatch that reproduces the effects of openssl raising
+ # a certificate validation error when RubyGems tries to connect.
+ gemfile <<-G
+ class Net::HTTP
+ def start
+ raise OpenSSL::SSL::SSLError, "certificate verify failed"
+ end
+ end
+
+ source "#{source_uri.gsub(/http/, "https")}"
+ gem "rack"
+ G
+
+ bundle :install
+ expect(out).to match(/could not verify the SSL certificate/i)
+ end
+ end
+
+ context ".gemrc with sources is present" do
+ before do
+ File.open(home(".gemrc"), "w") do |file|
+ file.puts({ :sources => ["https://rubygems.org"] }.to_yaml)
+ end
+ end
+
+ after do
+ home(".gemrc").rmtree
+ end
+
+ it "uses other sources declared in the Gemfile" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack'
+ G
+
+ bundle "install", :artifice => "endpoint_marshal_fail"
+
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/env_spec.rb b/spec/bundler/install/gems/env_spec.rb
new file mode 100644
index 0000000000..0dccbbfd24
--- /dev/null
+++ b/spec/bundler/install/gems/env_spec.rb
@@ -0,0 +1,107 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with ENV conditionals" do
+ describe "when just setting an ENV key as a string" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ env "BUNDLER_TEST" do
+ gem "rack"
+ end
+ G
+ end
+
+ it "excludes the gems when the ENV variable is not set" do
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "includes the gems when the ENV variable is set" do
+ ENV["BUNDLER_TEST"] = "1"
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ describe "when just setting an ENV key as a symbol" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ env :BUNDLER_TEST do
+ gem "rack"
+ end
+ G
+ end
+
+ it "excludes the gems when the ENV variable is not set" do
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "includes the gems when the ENV variable is set" do
+ ENV["BUNDLER_TEST"] = "1"
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ describe "when setting a string to match the env" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ env "BUNDLER_TEST" => "foo" do
+ gem "rack"
+ end
+ G
+ end
+
+ it "excludes the gems when the ENV variable is not set" do
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "excludes the gems when the ENV variable is set but does not match the condition" do
+ ENV["BUNDLER_TEST"] = "1"
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "includes the gems when the ENV variable is set and matches the condition" do
+ ENV["BUNDLER_TEST"] = "foo"
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ describe "when setting a regex to match the env" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ env "BUNDLER_TEST" => /foo/ do
+ gem "rack"
+ end
+ G
+ end
+
+ it "excludes the gems when the ENV variable is not set" do
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "excludes the gems when the ENV variable is set but does not match the condition" do
+ ENV["BUNDLER_TEST"] = "fo"
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "includes the gems when the ENV variable is set and matches the condition" do
+ ENV["BUNDLER_TEST"] = "foobar"
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/flex_spec.rb b/spec/bundler/install/gems/flex_spec.rb
new file mode 100644
index 0000000000..37d2e4958a
--- /dev/null
+++ b/spec/bundler/install/gems/flex_spec.rb
@@ -0,0 +1,351 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle flex_install" do
+ it "installs the gems as expected" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to be_locked
+ end
+
+ it "installs even when the lockfile is invalid" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to be_locked
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack', '1.0'
+ G
+
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to be_locked
+ end
+
+ it "keeps child dependencies at the same version" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack-obama"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0.0"
+
+ update_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack-obama", "1.0"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0.0"
+ end
+
+ describe "adding new gems" do
+ it "installs added gems without updating previously installed gems" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ G
+
+ update_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ gem 'activesupport', '2.3.5'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
+ end
+
+ it "keeps child dependencies pinned" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack-obama"
+ G
+
+ update_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack-obama"
+ gem "thin"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0", "thin 1.0"
+ end
+ end
+
+ describe "removing gems" do
+ it "removes gems without changing the versions of remaining gems" do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ gem 'activesupport', '2.3.5'
+ G
+
+ update_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ gem 'activesupport', '2.3.2'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.2"
+ end
+
+ it "removes top level dependencies when removed from the Gemfile while leaving other dependencies intact" do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ gem 'activesupport', '2.3.5'
+ G
+
+ update_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ G
+
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+
+ it "removes child dependencies" do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack-obama'
+ gem 'activesupport'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0.0", "activesupport 2.3.5"
+
+ update_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'activesupport'
+ G
+
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ expect(the_bundle).not_to include_gems "rack-obama", "rack"
+ end
+ end
+
+ describe "when Gemfile conflicts with lockfile" do
+ before(:each) do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack_middleware"
+ G
+
+ expect(the_bundle).to include_gems "rack_middleware 1.0", "rack 0.9.1"
+
+ build_repo2
+ update_repo2 do
+ build_gem "rack-obama", "2.0" do |s|
+ s.add_dependency "rack", "=1.2"
+ end
+ build_gem "rack_middleware", "2.0" do |s|
+ s.add_dependency "rack", ">=1.0"
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack-obama", "2.0"
+ gem "rack_middleware"
+ G
+ end
+
+ it "does not install gems whose dependencies are not met" do
+ bundle :install
+ ruby <<-RUBY
+ require 'bundler/setup'
+ RUBY
+ expect(err).to match(/could not find gem 'rack-obama/i)
+ end
+
+ it "suggests bundle update when the Gemfile requires different versions than the lock" do
+ nice_error = <<-E.strip.gsub(/^ {8}/, "")
+ Bundler could not find compatible versions for gem "rack":
+ In snapshot (Gemfile.lock):
+ rack (= 0.9.1)
+
+ 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)
+
+ Running `bundle update` will rebuild your snapshot from scratch, using only
+ the gems in your Gemfile, which may resolve the conflict.
+ E
+
+ bundle :install, :retry => 0
+ expect(last_command.bundler_err).to end_with(nice_error)
+ end
+ end
+
+ describe "subtler cases" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ gem "rack-obama"
+ G
+ end
+
+ it "does something" do
+ expect do
+ bundle "install"
+ end.not_to change { File.read(bundled_app("Gemfile.lock")) }
+
+ expect(out).to include("rack = 0.9.1")
+ expect(out).to include("locked at 1.0.0")
+ expect(out).to include("bundle update rack")
+ end
+
+ it "should work when you update" do
+ bundle "update rack"
+ end
+ end
+
+ describe "when adding a new source" do
+ it "updates the lockfile", :bundler => "< 2" do
+ build_repo2
+ install_gemfile! <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ G
+ install_gemfile! <<-G
+ source "file://localhost#{gem_repo1}"
+ source "file://localhost#{gem_repo2}"
+ gem "rack"
+ G
+
+ lockfile_should_be <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "updates the lockfile", :bundler => "2" do
+ build_repo2
+ install_gemfile! <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ G
+
+ install_gemfile! <<-G
+ source "file://localhost#{gem_repo1}"
+ source "file://localhost#{gem_repo2}" do
+ end
+ gem "rack"
+ G
+
+ lockfile_should_be <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ # This was written to test github issue #636
+ describe "when a locked child dependency conflicts" do
+ before(:each) do
+ build_repo2 do
+ build_gem "capybara", "0.3.9" do |s|
+ s.add_dependency "rack", ">= 1.0.0"
+ end
+
+ build_gem "rack", "1.1.0"
+ build_gem "rails", "3.0.0.rc4" do |s|
+ s.add_dependency "rack", "~> 1.1.0"
+ end
+
+ build_gem "rack", "1.2.1"
+ build_gem "rails", "3.0.0" do |s|
+ s.add_dependency "rack", "~> 1.2.1"
+ end
+ end
+ end
+
+ it "prints the correct error message" do
+ # install Rails 3.0.0.rc
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0.0.rc4"
+ gem "capybara", "0.3.9"
+ G
+
+ # upgrade Rails to 3.0.0 and then install again
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0.0"
+ gem "capybara", "0.3.9"
+ G
+
+ expect(out).to include("Gemfile.lock")
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/mirror_spec.rb b/spec/bundler/install/gems/mirror_spec.rb
new file mode 100644
index 0000000000..4c35b8f206
--- /dev/null
+++ b/spec/bundler/install/gems/mirror_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with a mirror configured" do
+ describe "when the mirror does not match the gem source" do
+ before :each do
+ gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+ bundle "config --local mirror.http://gems.example.org http://gem-mirror.example.org"
+ end
+
+ it "installs from the normal location" do
+ bundle :install
+ expect(out).to include(normalize_uri_file("Fetching source index from file://localhost#{gem_repo1}"))
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ describe "when the gem source matches a configured mirror" do
+ before :each do
+ gemfile <<-G
+ # This source is bogus and doesn't have the gem we're looking for
+ source "file://localhost#{gem_repo2}"
+
+ gem "rack"
+ G
+ bundle "config --local mirror.file://localhost#{gem_repo2} file://localhost#{gem_repo1}"
+ end
+
+ it "installs the gem from the mirror" do
+ bundle :install
+ expect(out).to include(normalize_uri_file("Fetching source index from file://localhost#{gem_repo1}"))
+ expect(out).not_to include(normalize_uri_file("Fetching source index from file://localhost#{gem_repo2}"))
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/native_extensions_spec.rb b/spec/bundler/install/gems/native_extensions_spec.rb
new file mode 100644
index 0000000000..ea616f60d3
--- /dev/null
+++ b/spec/bundler/install/gems/native_extensions_spec.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+RSpec.describe "installing a gem with native extensions", :ruby_repo do
+ it "installs" do
+ build_repo2 do
+ build_gem "c_extension" do |s|
+ s.extensions = ["ext/extconf.rb"]
+ s.write "ext/extconf.rb", <<-E
+ require "mkmf"
+ name = "c_extension_bundle"
+ dir_config(name)
+ raise "OMG" unless with_config("c_extension") == "hello"
+ create_makefile(name)
+ E
+
+ s.write "ext/c_extension.c", <<-C
+ #include "ruby.h"
+
+ VALUE c_extension_true(VALUE self) {
+ return Qtrue;
+ }
+
+ void Init_c_extension_bundle() {
+ VALUE c_Extension = rb_define_class("CExtension", rb_cObject);
+ rb_define_method(c_Extension, "its_true", c_extension_true, 0);
+ }
+ C
+
+ s.write "lib/c_extension.rb", <<-C
+ require "c_extension_bundle"
+ C
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "c_extension"
+ G
+
+ bundle "config build.c_extension --with-c_extension=hello"
+ bundle "install"
+
+ expect(out).not_to include("extconf.rb failed")
+ expect(out).to include("Installing c_extension 1.0 with native extensions")
+
+ run "Bundler.require; puts CExtension.new.its_true"
+ expect(out).to eq("true")
+ end
+
+ it "installs from git" do
+ build_git "c_extension" do |s|
+ s.extensions = ["ext/extconf.rb"]
+ s.write "ext/extconf.rb", <<-E
+ require "mkmf"
+ name = "c_extension_bundle"
+ dir_config(name)
+ raise "OMG" unless with_config("c_extension") == "hello"
+ create_makefile(name)
+ E
+
+ s.write "ext/c_extension.c", <<-C
+ #include "ruby.h"
+
+ VALUE c_extension_true(VALUE self) {
+ return Qtrue;
+ }
+
+ void Init_c_extension_bundle() {
+ VALUE c_Extension = rb_define_class("CExtension", rb_cObject);
+ rb_define_method(c_Extension, "its_true", c_extension_true, 0);
+ }
+ C
+
+ s.write "lib/c_extension.rb", <<-C
+ require "c_extension_bundle"
+ C
+ end
+
+ bundle! "config build.c_extension --with-c_extension=hello"
+
+ install_gemfile! <<-G
+ gem "c_extension", :git => #{lib_path("c_extension-1.0").to_s.dump}
+ G
+
+ expect(out).not_to include("extconf.rb failed")
+
+ run! "Bundler.require; puts CExtension.new.its_true"
+ expect(out).to eq("true")
+ end
+end
diff --git a/spec/bundler/install/gems/post_install_spec.rb b/spec/bundler/install/gems/post_install_spec.rb
new file mode 100644
index 0000000000..c6e348fb65
--- /dev/null
+++ b/spec/bundler/install/gems/post_install_spec.rb
@@ -0,0 +1,150 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ context "with gem sources" do
+ context "when gems include post install messages" do
+ it "should display the post-install messages after installing" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ gem 'thin'
+ gem 'rack-obama'
+ G
+
+ bundle :install
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ expect(out).to include("Post-install message from thin:")
+ expect(out).to include("Thin's post install message")
+ expect(out).to include("Post-install message from rack-obama:")
+ expect(out).to include("Rack-obama's post install message")
+ end
+ end
+
+ context "when gems do not include post install messages" do
+ it "should not display any post-install messages" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ G
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
+ end
+
+ context "when a dependecy includes a post install message" do
+ it "should display the post install message" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack_middleware'
+ G
+
+ bundle :install
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ end
+ end
+ end
+
+ context "with git sources" do
+ context "when gems include post install messages" do
+ it "should display the post-install messages after installing" do
+ build_git "foo" do |s|
+ s.post_install_message = "Foo's post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle :install
+ expect(out).to include("Post-install message from foo:")
+ expect(out).to include("Foo's post install message")
+ end
+
+ it "should display the post-install messages if repo is updated" do
+ build_git "foo" do |s|
+ s.post_install_message = "Foo's post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+ bundle :install
+
+ build_git "foo", "1.1" do |s|
+ s.post_install_message = "Foo's 1.1 post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.1")}'
+ G
+ bundle :install
+
+ expect(out).to include("Post-install message from foo:")
+ expect(out).to include("Foo's 1.1 post install message")
+ end
+
+ it "should not display the post-install messages if repo is not updated" do
+ build_git "foo" do |s|
+ s.post_install_message = "Foo's post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle :install
+ expect(out).to include("Post-install message from foo:")
+ expect(out).to include("Foo's post install message")
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
+ end
+
+ context "when gems do not include post install messages" do
+ it "should not display any post-install messages" do
+ build_git "foo" do |s|
+ s.post_install_message = nil
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
+ end
+ end
+
+ context "when ignore post-install messages for gem is set" do
+ it "doesn't display any post-install messages" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "config ignore_messages.rack true"
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
+ end
+
+ context "when ignore post-install messages for all gems" do
+ it "doesn't display any post-install messages" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "config ignore_messages true"
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb
new file mode 100644
index 0000000000..01c03ac793
--- /dev/null
+++ b/spec/bundler/install/gems/resolving_spec.rb
@@ -0,0 +1,195 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with install-time dependencies" do
+ it "installs gems with implicit rake dependencies", :ruby_repo do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "with_implicit_rake_dep"
+ gem "another_implicit_rake_dep"
+ gem "rake"
+ G
+
+ run <<-R
+ require 'implicit_rake_dep'
+ require 'another_implicit_rake_dep'
+ puts IMPLICIT_RAKE_DEP
+ puts ANOTHER_IMPLICIT_RAKE_DEP
+ R
+ expect(out).to eq("YES\nYES")
+ end
+
+ it "installs gems with a dependency with no type" do
+ build_repo2
+
+ path = "#{gem_repo2}/#{Gem::MARSHAL_SPEC_DIR}/actionpack-2.3.2.gemspec.rz"
+ spec = Marshal.load(Bundler.rubygems.inflate(File.read(path)))
+ spec.dependencies.each do |d|
+ d.instance_variable_set(:@type, :fail)
+ end
+ File.open(path, "w") do |f|
+ f.write Gem.deflate(Marshal.dump(spec))
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "actionpack", "2.3.2"
+ G
+
+ expect(the_bundle).to include_gems "actionpack 2.3.2", "activesupport 2.3.2"
+ end
+
+ describe "with crazy rubygem plugin stuff" do
+ it "installs plugins" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "net_b"
+ G
+
+ expect(the_bundle).to include_gems "net_b 1.0"
+ end
+
+ it "installs plugins depended on by other plugins", :ruby_repo do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "net_a"
+ G
+
+ expect(the_bundle).to include_gems "net_a 1.0", "net_b 1.0"
+ end
+
+ it "installs multiple levels of dependencies", :ruby_repo do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "net_c"
+ gem "net_e"
+ G
+
+ expect(the_bundle).to include_gems "net_a 1.0", "net_b 1.0", "net_c 1.0", "net_d 1.0", "net_e 1.0"
+ end
+
+ context "with ENV['DEBUG_RESOLVER'] set" do
+ it "produces debug output" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "net_c"
+ gem "net_e"
+ G
+
+ bundle :install, :env => { "DEBUG_RESOLVER" => "1" }
+
+ expect(err).to include("Creating possibility state for net_c")
+ end
+ end
+
+ context "with ENV['DEBUG_RESOLVER_TREE'] set" do
+ it "produces debug output" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "net_c"
+ gem "net_e"
+ G
+
+ bundle :install, :env => { "DEBUG_RESOLVER_TREE" => "1" }
+
+ expect(err).to include(" net_b").
+ and include("Starting resolution").
+ and include("Finished resolution").
+ and include("Attempting to activate")
+ end
+ end
+ end
+
+ describe "when a required ruby version" do
+ context "allows only an older version" do
+ it "installs the older version" do
+ build_repo2 do
+ build_gem "rack", "9001.0.0" do |s|
+ s.required_ruby_version = "> 9000"
+ end
+ end
+
+ install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2 }
+ ruby "#{RUBY_VERSION}"
+ source "http://localgemserver.test/"
+ gem 'rack'
+ G
+
+ expect(out).to_not include("rack-9001.0.0 requires ruby version > 9000")
+ expect(the_bundle).to include_gems("rack 1.2")
+ end
+ end
+
+ context "allows no gems" do
+ before do
+ build_repo2 do
+ build_gem "require_ruby" do |s|
+ s.required_ruby_version = "> 9000"
+ end
+ end
+ end
+
+ let(:ruby_requirement) { %("#{RUBY_VERSION}") }
+ let(:error_message_requirement) { "~> #{RUBY_VERSION}.0" }
+
+ shared_examples_for "ruby version conflicts" do
+ it "raises an error during resolution" do
+ install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2 }
+ source "http://localgemserver.test/"
+ ruby #{ruby_requirement}
+ gem 'require_ruby'
+ G
+
+ expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000")
+
+ nice_error = strip_whitespace(<<-E).strip
+ Bundler could not find compatible versions for gem "ruby\0":
+ In Gemfile:
+ ruby\0 (#{error_message_requirement})
+
+ require_ruby was resolved to 1.0, which depends on
+ ruby\0 (> 9000)
+
+ Could not find gem 'ruby\0 (> 9000)', which is required by gem 'require_ruby', in any of the relevant sources:
+ the local ruby installation
+ E
+ expect(last_command.bundler_err).to end_with(nice_error)
+ end
+ end
+
+ it_behaves_like "ruby version conflicts"
+
+ describe "with a < requirement" do
+ let(:ruby_requirement) { %("< 5000") }
+ let(:error_message_requirement) { "< 5000" }
+
+ it_behaves_like "ruby version conflicts"
+ end
+
+ describe "with a compound requirement" do
+ let(:reqs) { ["> 0.1", "< 5000"] }
+ let(:ruby_requirement) { reqs.map(&:dump).join(", ") }
+ let(:error_message_requirement) { Gem::Requirement.new(reqs).to_s }
+
+ it_behaves_like "ruby version conflicts"
+ end
+ end
+ end
+
+ describe "when a required rubygems version disallows a gem" do
+ it "does not try to install those gems" do
+ build_repo2 do
+ build_gem "require_rubygems" do |s|
+ s.required_rubygems_version = "> 9000"
+ end
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'require_rubygems'
+ G
+
+ expect(out).to_not include("Gem::InstallError: require_rubygems requires RubyGems version > 9000")
+ expect(out).to include("require_rubygems-1.0 requires rubygems version > 9000, which is incompatible with the current version, #{Gem::VERSION}")
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/standalone_spec.rb b/spec/bundler/install/gems/standalone_spec.rb
new file mode 100644
index 0000000000..10ce589eef
--- /dev/null
+++ b/spec/bundler/install/gems/standalone_spec.rb
@@ -0,0 +1,337 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples "bundle install --standalone" do
+ shared_examples "common functionality" do
+ it "still makes the gems available to normal bundler" do
+ args = expected_gems.map {|k, v| "#{k} #{v}" }
+ expect(the_bundle).to include_gems(*args)
+ end
+
+ it "generates a bundle/bundler/setup.rb" do
+ expect(bundled_app("bundle/bundler/setup.rb")).to exist
+ end
+
+ it "makes the gems available without bundler" do
+ testrb = String.new <<-RUBY
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ RUBY
+ expected_gems.each do |k, _|
+ testrb << "\nrequire \"#{k}\""
+ testrb << "\nputs #{k.upcase}"
+ end
+ Dir.chdir(bundled_app) do
+ ruby testrb, :no_lib => true
+ end
+
+ expect(out).to eq(expected_gems.values.join("\n"))
+ end
+
+ it "works on a different system" do
+ FileUtils.mv(bundled_app, "#{bundled_app}2")
+
+ testrb = String.new <<-RUBY
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ RUBY
+ expected_gems.each do |k, _|
+ testrb << "\nrequire \"#{k}\""
+ testrb << "\nputs #{k.upcase}"
+ end
+ Dir.chdir("#{bundled_app}2") do
+ ruby testrb, :no_lib => true
+ end
+
+ expect(out).to eq(expected_gems.values.join("\n"))
+ end
+ end
+
+ describe "with simple gems" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true)
+ end
+
+ let(:expected_gems) do
+ {
+ "actionpack" => "2.3.2",
+ "rails" => "2.3.2",
+ }
+ end
+
+ include_examples "common functionality"
+ end
+
+ describe "with gems with native extension", :ruby_repo do
+ before do
+ install_gemfile <<-G, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true)
+ source "file://#{gem_repo1}"
+ gem "very_simple_binary"
+ G
+ end
+
+ it "generates a bundle/bundler/setup.rb with the proper paths", :rubygems => "2.4" do
+ expected_path = bundled_app("bundle/bundler/setup.rb")
+ extension_line = File.read(expected_path).each_line.find {|line| line.include? "/extensions/" }.strip
+ expect(extension_line).to start_with '$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/'
+ expect(extension_line).to end_with '/very_simple_binary-1.0"'
+ end
+ end
+
+ describe "with gem that has an invalid gemspec" do
+ before do
+ build_git "bar", :gemspec => false do |s|
+ s.write "lib/bar/version.rb", %(BAR_VERSION = '1.0')
+ s.write "bar.gemspec", <<-G
+ lib = File.expand_path('../lib/', __FILE__)
+ $:.unshift lib unless $:.include?(lib)
+ require 'bar/version'
+
+ Gem::Specification.new do |s|
+ s.name = 'bar'
+ s.version = BAR_VERSION
+ s.summary = 'Bar'
+ s.files = Dir["lib/**/*.rb"]
+ s.author = 'Anonymous'
+ s.require_path = [1,2]
+ end
+ G
+ end
+ install_gemfile <<-G, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true)
+ gem "bar", :git => "#{lib_path("bar-1.0")}"
+ G
+ end
+
+ it "outputs a helpful error message" do
+ expect(out).to include("You have one or more invalid gemspecs that need to be fixed.")
+ expect(out).to include("bar 1.0 has an invalid gemspec")
+ end
+ end
+
+ describe "with a combination of gems and git repos" do
+ before do
+ build_git "devise", "1.0"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ gem "devise", :git => "#{lib_path("devise-1.0")}"
+ G
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true)
+ end
+
+ let(:expected_gems) do
+ {
+ "actionpack" => "2.3.2",
+ "devise" => "1.0",
+ "rails" => "2.3.2",
+ }
+ end
+
+ include_examples "common functionality"
+ end
+
+ describe "with groups" do
+ before do
+ build_git "devise", "1.0"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ group :test do
+ gem "rspec"
+ gem "rack-test"
+ end
+ G
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true)
+ end
+
+ let(:expected_gems) do
+ {
+ "actionpack" => "2.3.2",
+ "rails" => "2.3.2",
+ }
+ end
+
+ include_examples "common functionality"
+
+ it "allows creating a standalone file with limited groups" do
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => "default")
+
+ Dir.chdir(bundled_app) do
+ load_error_ruby <<-RUBY, "spec", :no_lib => true
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ require "actionpack"
+ puts ACTIONPACK
+ require "spec"
+ RUBY
+ end
+
+ expect(last_command.stdout).to eq("2.3.2")
+ expect(last_command.stderr).to eq("ZOMG LOAD ERROR")
+ end
+
+ it "allows --without to limit the groups used in a standalone" do
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle"), :without => "test").merge(:standalone => true)
+
+ Dir.chdir(bundled_app) do
+ load_error_ruby <<-RUBY, "spec", :no_lib => true
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ require "actionpack"
+ puts ACTIONPACK
+ require "spec"
+ RUBY
+ end
+
+ expect(last_command.stdout).to eq("2.3.2")
+ expect(last_command.stderr).to eq("ZOMG LOAD ERROR")
+ end
+
+ it "allows --path to change the location of the standalone bundle", :bundler => "< 2" do
+ bundle! "install", forgotten_command_line_options(:path => "path/to/bundle").merge(:standalone => true)
+
+ Dir.chdir(bundled_app) do
+ ruby <<-RUBY, :no_lib => true
+ $:.unshift File.expand_path("path/to/bundle")
+ require "bundler/setup"
+
+ require "actionpack"
+ puts ACTIONPACK
+ RUBY
+ end
+
+ expect(last_command.stdout).to eq("2.3.2")
+ end
+
+ it "allows --path to change the location of the standalone bundle", :bundler => "2" do
+ bundle! "install", forgotten_command_line_options(:path => "path/to/bundle").merge(:standalone => true)
+ path = File.expand_path("path/to/bundle")
+
+ Dir.chdir(bundled_app) do
+ ruby <<-RUBY, :no_lib => true
+ $:.unshift File.expand_path(#{path.dump})
+ require "bundler/setup"
+
+ require "actionpack"
+ puts ACTIONPACK
+ RUBY
+ end
+
+ expect(last_command.stdout).to eq("2.3.2")
+ end
+
+ it "allows remembered --without to limit the groups used in a standalone" do
+ bundle! :install, forgotten_command_line_options(:without => "test")
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true)
+
+ Dir.chdir(bundled_app) do
+ load_error_ruby <<-RUBY, "spec", :no_lib => true
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ require "actionpack"
+ puts ACTIONPACK
+ require "spec"
+ RUBY
+ end
+
+ expect(last_command.stdout).to eq("2.3.2")
+ expect(last_command.stderr).to eq("ZOMG LOAD ERROR")
+ end
+ end
+
+ describe "with gemcutter's dependency API" do
+ let(:source_uri) { "http://localgemserver.test" }
+
+ describe "simple gems" do
+ before do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rails"
+ G
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true, :artifice => "endpoint")
+ end
+
+ let(:expected_gems) do
+ {
+ "actionpack" => "2.3.2",
+ "rails" => "2.3.2",
+ }
+ end
+
+ include_examples "common functionality"
+ end
+ end
+
+ describe "with --binstubs", :bundler => "< 2" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("bundle")).merge(:standalone => true, :binstubs => true)
+ end
+
+ let(:expected_gems) do
+ {
+ "actionpack" => "2.3.2",
+ "rails" => "2.3.2",
+ }
+ end
+
+ include_examples "common functionality"
+
+ it "creates stubs that use the standalone load path" do
+ Dir.chdir(bundled_app) do
+ expect(`bin/rails -v`.chomp).to eql "2.3.2"
+ end
+ end
+
+ it "creates stubs that can be executed from anywhere" do
+ require "tmpdir"
+ Dir.chdir(Dir.tmpdir) do
+ sys_exec!(%(#{bundled_app("bin/rails")} -v))
+ expect(out).to eq("2.3.2")
+ end
+ end
+
+ it "creates stubs that can be symlinked" do
+ pending "File.symlink is unsupported on Windows" if Bundler::WINDOWS
+
+ symlink_dir = tmp("symlink")
+ FileUtils.mkdir_p(symlink_dir)
+ symlink = File.join(symlink_dir, "rails")
+
+ File.symlink(bundled_app("bin/rails"), symlink)
+ sys_exec!("#{symlink} -v")
+ expect(out).to eq("2.3.2")
+ end
+
+ it "creates stubs with the correct load path" do
+ extension_line = File.read(bundled_app("bin/rails")).each_line.find {|line| line.include? "$:.unshift" }.strip
+ expect(extension_line).to eq %($:.unshift File.expand_path "../../bundle", path.realpath)
+ end
+ end
+end
+
+RSpec.describe "bundle install --standalone" do
+ include_examples("bundle install --standalone")
+end
+
+RSpec.describe "bundle install --standalone run in a subdirectory" do
+ before do
+ Dir.chdir(bundled_app("bob").tap(&:mkpath))
+ end
+
+ include_examples("bundle install --standalone")
+end
diff --git a/spec/bundler/install/gems/sudo_spec.rb b/spec/bundler/install/gems/sudo_spec.rb
new file mode 100644
index 0000000000..1781451c98
--- /dev/null
+++ b/spec/bundler/install/gems/sudo_spec.rb
@@ -0,0 +1,178 @@
+# 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
+ before do
+ bundle! "config path.system true"
+ subdir = system_gem_path("cache")
+ subdir.mkpath
+ sudo "chmod u-w #{subdir}"
+ end
+
+ it "installs" do
+ install_gemfile <<-G
+ source "file://#{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 path.system true"
+ chown_system_gems_to_root
+ end
+
+ it "installs" do
+ install_gemfile <<-G
+ source "file://#{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
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ 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! "config global_path_appends_ruby_scope false" # consistency in tests between 1.x and 2.x modes
+
+ 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://#{gem_repo1}"
+ gem "rack", '1.0'
+ G
+
+ expect(bundle_path.join("gems/rack-1.0.0")).to exist
+ expect(bundle_path.join("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
+ bundle! "config global_path_appends_ruby_scope false" # consistency in tests between 1.x and 2.x modes
+
+ 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://#{gem_repo1}"
+ gem "rack", '1.0'
+ G
+
+ expect(bundle_path.join("gems/rack-1.0.0")).to exist
+ expect(bundle_path.join("gems/rack-1.0.0").stat.uid).to eq(0)
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "installs extensions/ compiled by RubyGems 2.2", :rubygems => "2.2" do
+ install_gemfile <<-G
+ source "file://#{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
+ sudo "chmod ugo-w #{default_bundle_path}"
+ end
+
+ it "installs" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", '1.0'
+ G
+
+ expect(default_bundle_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://#{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 path.system true"
+ gem_home = tmp("sudo_gem_home")
+ sudo "mkdir -p #{gem_home}"
+ sudo "chmod ugo-w #{gem_home}"
+
+ gemfile <<-G
+ source "file://#{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 }
+ end
+ end
+
+ describe "and root runs install" do
+ let(:warning) { "Don't run Bundler as root." }
+
+ before do
+ gemfile %(source "file://#{gem_repo1}")
+ end
+
+ it "warns against that" do
+ bundle :install, :sudo => true
+ expect(out).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(out).to_not include(warning)
+ end
+ end
+
+ context "when silence_root_warning = false" do
+ it "warns against that" do
+ bundle :install, :sudo => true, :env => { "BUNDLE_SILENCE_ROOT_WARNING" => "false" }
+ expect(out).to include(warning)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/win32_spec.rb b/spec/bundler/install/gems/win32_spec.rb
new file mode 100644
index 0000000000..ad758b94fa
--- /dev/null
+++ b/spec/bundler/install/gems/win32_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with win32-generated lockfile" do
+ it "should read lockfile" do
+ File.open(bundled_app("Gemfile.lock"), "wb") do |f|
+ f << "GEM\r\n"
+ f << " remote: file:#{gem_repo1}/\r\n"
+ f << " specs:\r\n"
+ f << "\r\n"
+ f << " rack (1.0.0)\r\n"
+ f << "\r\n"
+ f << "PLATFORMS\r\n"
+ f << " ruby\r\n"
+ f << "\r\n"
+ f << "DEPENDENCIES\r\n"
+ f << " rack\r\n"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+end
diff --git a/spec/bundler/install/gemspecs_spec.rb b/spec/bundler/install/gemspecs_spec.rb
new file mode 100644
index 0000000000..666707b295
--- /dev/null
+++ b/spec/bundler/install/gemspecs_spec.rb
@@ -0,0 +1,154 @@
+# encoding: utf-8
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ describe "when a gem has a YAML gemspec" do
+ before :each do
+ build_repo2 do
+ build_gem "yaml_spec", :gemspec => :yaml
+ end
+ end
+
+ it "still installs correctly" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "yaml_spec"
+ G
+ bundle :install
+ expect(err).to lack_errors
+ end
+
+ it "still installs correctly when using path" do
+ build_lib "yaml_spec", :gemspec => :yaml
+
+ install_gemfile <<-G
+ gem 'yaml_spec', :path => "#{lib_path("yaml_spec-1.0")}"
+ G
+ expect(err).to lack_errors
+ end
+ end
+
+ it "should use gemspecs in the system cache when available" do
+ gemfile <<-G
+ source "http://localtestserver.gem"
+ gem 'rack'
+ G
+
+ FileUtils.mkdir_p "#{default_bundle_path}/specifications"
+ File.open("#{default_bundle_path}/specifications/rack-1.0.0.gemspec", "w+") do |f|
+ spec = Gem::Specification.new do |s|
+ s.name = "rack"
+ s.version = "1.0.0"
+ s.add_runtime_dependency "activesupport", "2.3.2"
+ end
+ f.write spec.to_ruby
+ end
+ bundle :install, :artifice => "endpoint_marshal_fail" # force gemspec load
+ expect(the_bundle).to include_gems "activesupport 2.3.2"
+ end
+
+ it "does not hang when gemspec has incompatible encoding" do
+ create_file "foo.gemspec", <<-G
+ Gem::Specification.new do |gem|
+ gem.name = "pry-byebug"
+ gem.version = "3.4.2"
+ gem.author = "David Rodriguez"
+ gem.summary = "Good stuff"
+ end
+ G
+
+ install_gemfile <<-G, :env => { "LANG" => "C" }
+ gemspec
+ G
+
+ expect(out).to include("Bundle complete!")
+ end
+
+ it "reads gemspecs respecting their encoding" do
+ skip "Unicode is not supported on Ruby 1.x without extra work" if RUBY_VERSION < "2.0"
+
+ create_file "version.rb", <<-RUBY
+ module Persistent💎
+ VERSION = "0.0.1"
+ end
+ RUBY
+
+ create_file "persistent-dmnd.gemspec", <<-G
+ require_relative "version"
+
+ Gem::Specification.new do |gem|
+ gem.name = "persistent-dmnd"
+ gem.version = Persistent💎::VERSION
+ gem.author = "Ivo Anjo"
+ gem.summary = "Unscratchable stuff"
+ end
+ G
+
+ install_gemfile <<-G
+ gemspec
+ G
+
+ expect(out).to include("Bundle complete!")
+ 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
+ build_lib("foo", :path => bundled_app) do |s|
+ s.required_ruby_version = "~> #{RUBY_VERSION}.0"
+ end
+
+ install_gemfile <<-G
+ ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby'
+ gemspec
+ G
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "installs when patch level is specified and the version still matches the current version",
+ :if => RUBY_PATCHLEVEL >= 0 do
+ build_lib("foo", :path => bundled_app) do |s|
+ s.required_ruby_version = "#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}"
+ end
+
+ install_gemfile <<-G
+ ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby', :patchlevel => '#{RUBY_PATCHLEVEL}'
+ gemspec
+ G
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "fails and complains about patchlevel on patchlevel mismatch",
+ :if => RUBY_PATCHLEVEL >= 0 do
+ patchlevel = RUBY_PATCHLEVEL.to_i + 1
+ build_lib("foo", :path => bundled_app) do |s|
+ s.required_ruby_version = "#{RUBY_VERSION}.#{patchlevel}"
+ end
+
+ install_gemfile <<-G
+ ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby', :patchlevel => '#{patchlevel}'
+ gemspec
+ G
+
+ expect(out).to include("Ruby patchlevel")
+ expect(out).to include("but your Gemfile specified")
+ expect(exitstatus).to eq(18) if exitstatus
+ end
+
+ it "fails and complains about version on version mismatch" do
+ version = Gem::Requirement.create(RUBY_VERSION).requirements.first.last.bump.version
+
+ build_lib("foo", :path => bundled_app) do |s|
+ s.required_ruby_version = version
+ end
+
+ install_gemfile <<-G
+ ruby '#{version}', :engine_version => '#{version}', :engine => 'ruby'
+ gemspec
+ G
+
+ expect(out).to include("Ruby version")
+ expect(out).to include("but your Gemfile specified")
+ expect(exitstatus).to eq(18) if exitstatus
+ end
+ end
+end
diff --git a/spec/bundler/install/git_spec.rb b/spec/bundler/install/git_spec.rb
new file mode 100644
index 0000000000..6ae718c2a4
--- /dev/null
+++ b/spec/bundler/install/git_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ context "git sources" do
+ it "displays the revision hash of the gem repository", :bundler => "< 2" do
+ build_git "foo", "1.0", :path => lib_path("foo")
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo")}"
+ G
+
+ bundle! :install
+ expect(out).to include("Using foo 1.0 from #{lib_path("foo")} (at master@#{revision_for(lib_path("foo"))[0..6]})")
+ expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}"
+ end
+
+ it "displays the ref of the gem repository when using branch~num as a ref", :bundler => "< 2" do
+ build_git "foo", "1.0", :path => lib_path("foo")
+ rev = revision_for(lib_path("foo"))[0..6]
+ update_git "foo", "2.0", :path => lib_path("foo"), :gemspec => true
+ rev2 = revision_for(lib_path("foo"))[0..6]
+ update_git "foo", "3.0", :path => lib_path("foo"), :gemspec => true
+
+ install_gemfile! <<-G
+ gem "foo", :git => "#{lib_path("foo")}", :ref => "master~2"
+ G
+
+ bundle! :install
+ expect(out).to include("Using foo 1.0 from #{lib_path("foo")} (at master~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 => bundle_update_requires_all?
+ expect(out).to include("Using foo 2.0 (was 1.0) from #{lib_path("foo")} (at master~2@#{rev2})")
+ expect(the_bundle).to include_gems "foo 2.0", :source => "git@#{lib_path("foo")}"
+ end
+
+ it "should allows git repos that are missing but not being installed" do
+ revision = build_git("foo").ref_for("HEAD")
+
+ gemfile <<-G
+ gem "foo", :git => "file://#{lib_path("foo-1.0")}", :group => :development
+ G
+
+ lockfile <<-L
+ GIT
+ remote: file://#{lib_path("foo-1.0")}
+ revision: #{revision}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ foo!
+ L
+
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle", :without => "development")
+
+ 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
new file mode 100644
index 0000000000..e41e7e0157
--- /dev/null
+++ b/spec/bundler/install/global_cache_spec.rb
@@ -0,0 +1,235 @@
+# frozen_string_literal: true
+
+RSpec.describe "global gem caching" do
+ before { bundle! "config global_gem_cache true" }
+
+ describe "using the cross-application user cache" do
+ let(:source) { "http://localgemserver.test" }
+ let(:source2) { "http://gemserver.example.org" }
+
+ def source_global_cache(*segments)
+ home(".bundle", "cache", "gems", "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", *segments)
+ end
+
+ def source2_global_cache(*segments)
+ home(".bundle", "cache", "gems", "gemserver.example.org.80.1ae1663619ffe0a3c9d97712f44c705b", *segments)
+ end
+
+ it "caches gems into the global cache on download" do
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "#{source}"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ end
+
+ it "uses globally cached gems if they exist" do
+ source_global_cache.mkpath
+ FileUtils.cp(gem_repo1("gems/rack-1.0.0.gem"), source_global_cache("rack-1.0.0.gem"))
+
+ install_gemfile! <<-G, :artifice => "compact_index_no_gem"
+ source "#{source}"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ describe "when the same gem from different sources is installed" do
+ it "should use the appropriate one from the global cache" do
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "#{source}"
+ gem "rack"
+ G
+
+ FileUtils.rm_r(default_bundle_path)
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ # rack 1.0.0 is not installed and it is in the global cache
+
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "#{source2}"
+ gem "rack", "0.9.1"
+ G
+
+ FileUtils.rm_r(default_bundle_path)
+ expect(the_bundle).not_to include_gems "rack 0.9.1"
+ expect(source2_global_cache("rack-0.9.1.gem")).to exist
+ # rack 0.9.1 is not installed and it is in the global cache
+
+ gemfile <<-G
+ source "#{source}"
+ gem "rack", "1.0.0"
+ G
+
+ bundle! :install, :artifice => "compact_index_no_gem"
+ # rack 1.0.0 is installed and rack 0.9.1 is not
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).not_to include_gems "rack 0.9.1"
+ FileUtils.rm_r(default_bundle_path)
+
+ gemfile <<-G
+ source "#{source2}"
+ gem "rack", "0.9.1"
+ G
+
+ bundle! :install, :artifice => "compact_index_no_gem"
+ # rack 0.9.1 is installed and rack 1.0.0 is not
+ expect(the_bundle).to include_gems "rack 0.9.1"
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ end
+
+ it "should not install if the wrong source is provided" do
+ gemfile <<-G
+ source "#{source}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ FileUtils.rm_r(default_bundle_path)
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ # rack 1.0.0 is not installed and it is in the global cache
+
+ gemfile <<-G
+ source "#{source2}"
+ gem "rack", "0.9.1"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ FileUtils.rm_r(default_bundle_path)
+ expect(the_bundle).not_to include_gems "rack 0.9.1"
+ expect(source2_global_cache("rack-0.9.1.gem")).to exist
+ # rack 0.9.1 is not installed and it is in the global cache
+
+ gemfile <<-G
+ source "#{source2}"
+ gem "rack", "1.0.0"
+ G
+
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(source2_global_cache("rack-0.9.1.gem")).to exist
+ bundle :install, :artifice => "compact_index_no_gem"
+ expect(out).to include("Internal Server Error 500")
+ # rack 1.0.0 is not installed and rack 0.9.1 is not
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(the_bundle).not_to include_gems "rack 0.9.1"
+
+ gemfile <<-G
+ source "#{source}"
+ gem "rack", "0.9.1"
+ G
+
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(source2_global_cache("rack-0.9.1.gem")).to exist
+ bundle :install, :artifice => "compact_index_no_gem"
+ expect(out).to include("Internal Server Error 500")
+ # rack 0.9.1 is not installed and rack 1.0.0 is not
+ expect(the_bundle).not_to include_gems "rack 0.9.1"
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ end
+ end
+
+ describe "when installing gems from a different directory" do
+ it "uses the global cache as a source" do
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "#{source}"
+ gem "rack"
+ gem "activesupport"
+ G
+
+ # Both gems are installed and in the global cache
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(source_global_cache("activesupport-2.3.5.gem")).to exist
+ FileUtils.rm_r(default_bundle_path)
+ # Both gems are now only in the global cache
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+
+ install_gemfile! <<-G, :artifice => "compact_index_no_gem"
+ source "#{source}"
+ gem "rack"
+ G
+
+ # rack is installed and both are in the global cache
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(source_global_cache("activesupport-2.3.5.gem")).to exist
+
+ Dir.chdir bundled_app2 do
+ create_file bundled_app2("gems.rb"), <<-G
+ source "#{source}"
+ gem "activesupport"
+ G
+
+ # Neither gem is installed and both are in the global cache
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(source_global_cache("activesupport-2.3.5.gem")).to exist
+
+ # Install using the global cache instead of by downloading the .gem
+ # from the server
+ bundle! :install, :artifice => "compact_index_no_gem"
+
+ # activesupport is installed and both are in the global cache
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(source_global_cache("activesupport-2.3.5.gem")).to exist
+ end
+ end
+ end
+ end
+
+ describe "extension caching", :ruby_repo, :rubygems => "2.2" do
+ it "works" do
+ build_git "very_simple_git_binary", &:add_c_extension
+ build_lib "very_simple_path_binary", &:add_c_extension
+ revision = revision_for(lib_path("very_simple_git_binary-1.0"))[0, 12]
+
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+
+ gem "very_simple_binary"
+ gem "very_simple_git_binary", :git => "#{lib_path("very_simple_git_binary-1.0")}"
+ 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,
+ 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,
+ "very_simple_git_binary-1.0-#{revision}", "very_simple_git_binary-1.0")
+
+ cached_extensions = Pathname.glob(home(".bundle", "cache", "extensions", "*", "*", "*", "*", "*")).sort
+ expect(cached_extensions).to eq [gem_binary_cache, git_binary_cache].sort
+
+ run! <<-R
+ require 'very_simple_binary_c'; puts ::VERY_SIMPLE_BINARY_IN_C
+ require 'very_simple_git_binary_c'; puts ::VERY_SIMPLE_GIT_BINARY_IN_C
+ R
+ expect(out).to eq "VERY_SIMPLE_BINARY_IN_C\nVERY_SIMPLE_GIT_BINARY_IN_C"
+
+ FileUtils.rm Dir[home(".bundle", "cache", "extensions", "**", "*binary_c*")]
+
+ gem_binary_cache.join("very_simple_binary_c.rb").open("w") {|f| f << "puts File.basename(__FILE__)" }
+ git_binary_cache.join("very_simple_git_binary_c.rb").open("w") {|f| f << "puts File.basename(__FILE__)" }
+
+ bundle! "config --local path different_path"
+ bundle! :install
+
+ expect(Dir[home(".bundle", "cache", "extensions", "**", "*binary_c*")]).to all(end_with(".rb"))
+
+ run! <<-R
+ require 'very_simple_binary_c'
+ require 'very_simple_git_binary_c'
+ R
+ expect(out).to eq "very_simple_binary_c.rb\nvery_simple_git_binary_c.rb"
+ end
+ end
+end
diff --git a/spec/bundler/install/path_spec.rb b/spec/bundler/install/path_spec.rb
new file mode 100644
index 0000000000..44439c275e
--- /dev/null
+++ b/spec/bundler/install/path_spec.rb
@@ -0,0 +1,256 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ describe "with --path" do
+ before :each do
+ build_gem "rack", "1.0.0", :to_system => true do |s|
+ s.write "lib/rack.rb", "puts 'FAIL'"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "does not use available system gems with bundle --path vendor/bundle", :bundler => "< 2" do
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles paths with regex characters in them" do
+ dir = bundled_app("bun++dle")
+ dir.mkpath
+
+ Dir.chdir(dir) do
+ bundle! :install, forgotten_command_line_options(:path => dir.join("vendor/bundle"))
+ expect(out).to include("installed into `./vendor/bundle`")
+ end
+
+ dir.rmtree
+ end
+
+ it "prints a warning to let the user know what has happened with bundle --path vendor/bundle" do
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+ expect(out).to include("gems are installed into `./vendor/bundle`")
+ end
+
+ it "disallows --path vendor/bundle --system", :bundler => "< 2" do
+ bundle "install --path vendor/bundle --system"
+ expect(out).to include("Please choose only one option.")
+ expect(exitstatus).to eq(15) if exitstatus
+ end
+
+ it "remembers to disable system gems after the first time with bundle --path vendor/bundle", :bundler => "< 2" do
+ bundle "install --path vendor/bundle"
+ FileUtils.rm_rf bundled_app("vendor")
+ bundle "install"
+
+ expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ context "with path_relative_to_cwd set to true" do
+ before { bundle! "config path_relative_to_cwd true" }
+
+ it "installs the bundle relatively to current working directory", :bundler => "< 2" do
+ Dir.chdir(bundled_app.parent) do
+ bundle! "install --gemfile='#{bundled_app}/Gemfile' --path vendor/bundle"
+ expect(out).to include("installed into `./vendor/bundle`")
+ expect(bundled_app("../vendor/bundle")).to be_directory
+ end
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs the standalone bundle relative to the cwd" do
+ Dir.chdir(bundled_app.parent) do
+ bundle! :install, :gemfile => bundled_app("Gemfile"), :standalone => true
+ expect(out).to include("installed into `./bundled_app/bundle`")
+ expect(bundled_app("bundle")).to be_directory
+ expect(bundled_app("bundle/ruby")).to be_directory
+ end
+
+ bundle! "config unset path"
+
+ Dir.chdir(bundled_app("subdir").tap(&:mkpath)) do
+ bundle! :install, :gemfile => bundled_app("Gemfile"), :standalone => true
+ expect(out).to include("installed into `../bundle`")
+ expect(bundled_app("bundle")).to be_directory
+ expect(bundled_app("bundle/ruby")).to be_directory
+ end
+ end
+ end
+ end
+
+ describe "when BUNDLE_PATH or the global path config is set" do
+ before :each do
+ build_lib "rack", "1.0.0", :to_system => true do |s|
+ s.write "lib/rack.rb", "raise 'FAIL'"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ def set_bundle_path(type, location)
+ if type == :env
+ ENV["BUNDLE_PATH"] = location
+ elsif type == :global
+ bundle! "config path #{location}", "no-color" => nil
+ end
+ end
+
+ [:env, :global].each do |type|
+ context "when set via #{type}" do
+ it "installs gems to a path if one is specified" do
+ set_bundle_path(type, bundled_app("vendor2").to_s)
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+
+ expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(bundled_app("vendor2")).not_to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ context "with global_path_appends_ruby_scope set", :bundler => "2" do
+ it "installs gems to ." do
+ set_bundle_path(type, ".")
+ bundle! "config --global disable_shared_gems true"
+
+ bundle! :install
+
+ paths_to_exist = %w[cache/rack-1.0.0.gem gems/rack-1.0.0 specifications/rack-1.0.0.gemspec].map {|path| bundled_app(Bundler.ruby_scope, path) }
+ expect(paths_to_exist).to all exist
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs gems to the path" do
+ set_bundle_path(type, bundled_app("vendor").to_s)
+
+ bundle! :install
+
+ expect(bundled_app("vendor", Bundler.ruby_scope, "gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs gems to the path relative to root when relative" do
+ set_bundle_path(type, "vendor")
+
+ FileUtils.mkdir_p bundled_app("lol")
+ Dir.chdir(bundled_app("lol")) do
+ bundle! :install
+ end
+
+ expect(bundled_app("vendor", Bundler.ruby_scope, "gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ context "with global_path_appends_ruby_scope unset", :bundler => "< 2" do
+ it "installs gems to ." do
+ set_bundle_path(type, ".")
+ bundle! "config --global disable_shared_gems true"
+
+ bundle! :install
+
+ expect([bundled_app("cache/rack-1.0.0.gem"), bundled_app("gems/rack-1.0.0"), bundled_app("specifications/rack-1.0.0.gemspec")]).to all exist
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs gems to BUNDLE_PATH with #{type}" do
+ set_bundle_path(type, bundled_app("vendor").to_s)
+
+ bundle :install
+
+ expect(bundled_app("vendor/gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs gems to BUNDLE_PATH relative to root when relative" do
+ set_bundle_path(type, "vendor")
+
+ FileUtils.mkdir_p bundled_app("lol")
+ Dir.chdir(bundled_app("lol")) do
+ bundle :install
+ end
+
+ expect(bundled_app("vendor/gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+ end
+
+ it "installs gems to BUNDLE_PATH from .bundle/config" do
+ config "BUNDLE_PATH" => bundled_app("vendor/bundle").to_s
+
+ bundle :install
+
+ expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "sets BUNDLE_PATH as the first argument to bundle install" do
+ bundle! :install, forgotten_command_line_options(:path => "./vendor/bundle")
+
+ expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "disables system gems when passing a path to install" do
+ # This is so that vendored gems can be distributed to others
+ build_gem "rack", "1.1.0", :to_system => true
+ bundle! :install, forgotten_command_line_options(:path => "./vendor/bundle")
+
+ expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "re-installs gems whose extensions have been deleted", :ruby_repo, :rubygems => ">= 2.3" do
+ build_lib "very_simple_binary", "1.0.0", :to_system => true do |s|
+ s.write "lib/very_simple_binary.rb", "raise 'FAIL'"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "very_simple_binary"
+ G
+
+ bundle! :install, forgotten_command_line_options(:path => "./vendor/bundle")
+
+ expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory
+ expect(vendored_gems("extensions")).to be_directory
+ expect(the_bundle).to include_gems "very_simple_binary 1.0", :source => "remote1"
+
+ vendored_gems("extensions").rmtree
+
+ run "require 'very_simple_binary_c'"
+ expect(err).to include("Bundler::GemNotFound")
+
+ bundle :install, forgotten_command_line_options(:path => "./vendor/bundle")
+
+ expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory
+ expect(vendored_gems("extensions")).to be_directory
+ expect(the_bundle).to include_gems "very_simple_binary 1.0", :source => "remote1"
+ end
+ end
+
+ describe "to a file" do
+ before do
+ in_app_root do
+ FileUtils.touch "bundle"
+ end
+ end
+
+ it "reports the file exists" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle :install, forgotten_command_line_options(:path => "bundle")
+ expect(out).to include("file already exists")
+ end
+ end
+end
diff --git a/spec/bundler/install/post_bundle_message_spec.rb b/spec/bundler/install/post_bundle_message_spec.rb
new file mode 100644
index 0000000000..eadc8a4d85
--- /dev/null
+++ b/spec/bundler/install/post_bundle_message_spec.rb
@@ -0,0 +1,206 @@
+# frozen_string_literal: true
+
+RSpec.describe "post bundle message" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", "2.3.5", :group => [:emo, :test]
+ group :test do
+ gem "rspec"
+ end
+ gem "rack-obama", :group => :obama
+ G
+ end
+
+ let(:bundle_path) { "./.bundle" }
+ let(:bundle_show_system_message) { "Use `bundle info [gemname]` to see where a bundled gem is installed." }
+ let(:bundle_show_path_message) { "Bundled gems are installed into `#{bundle_path}`" }
+ let(:bundle_complete_message) { "Bundle complete!" }
+ let(:bundle_updated_message) { "Bundle updated!" }
+ let(:installed_gems_stats) { "4 Gemfile dependencies, 5 gems now installed." }
+ let(:bundle_show_message) { Bundler::VERSION.split(".").first.to_i < 2 ? bundle_show_system_message : bundle_show_path_message }
+
+ describe "for fresh bundle install" do
+ it "without any options" do
+ bundle :install
+ expect(out).to include(bundle_show_message)
+ expect(out).not_to include("Gems in the group")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include(installed_gems_stats)
+ end
+
+ it "with --without one group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo")
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the group emo were not installed")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include(installed_gems_stats)
+ end
+
+ it "with --without two groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo test")
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the groups emo and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include("4 Gemfile dependencies, 3 gems now installed.")
+ end
+
+ it "with --without more groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo obama test")
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the groups emo, obama and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include("4 Gemfile dependencies, 2 gems now installed.")
+ end
+
+ describe "with --path and" do
+ let(:bundle_path) { "./vendor" }
+
+ it "without any options" do
+ bundle! :install, forgotten_command_line_options(:path => "vendor")
+ expect(out).to include(bundle_show_path_message)
+ expect(out).to_not include("Gems in the group")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with --without one group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo", :path => "vendor")
+ expect(out).to include(bundle_show_path_message)
+ expect(out).to include("Gems in the group emo were not installed")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with --without two groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo test", :path => "vendor")
+ expect(out).to include(bundle_show_path_message)
+ expect(out).to include("Gems in the groups emo and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with --without more groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo obama test", :path => "vendor")
+ expect(out).to include(bundle_show_path_message)
+ expect(out).to include("Gems in the groups emo, obama and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with an absolute --path inside the cwd" do
+ bundle! :install, forgotten_command_line_options(:path => bundled_app("cache"))
+ expect(out).to include("Bundled gems are installed into `./cache`")
+ expect(out).to_not include("Gems in the group")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with an absolute --path outside the cwd" do
+ bundle! :install, forgotten_command_line_options(:path => tmp("not_bundled_app"))
+ expect(out).to include("Bundled gems are installed into `#{tmp("not_bundled_app")}`")
+ expect(out).to_not include("Gems in the group")
+ expect(out).to include(bundle_complete_message)
+ end
+ end
+
+ describe "with misspelled or non-existent gem name" do
+ it "should report a helpful error message", :bundler => "< 2" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ gem "not-a-gem", :group => :development
+ G
+ expect(out).to include("Could not find gem 'not-a-gem' in any of the gem sources listed in your Gemfile.")
+ end
+
+ it "should report a helpful error message", :bundler => "2" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ gem "not-a-gem", :group => :development
+ G
+ expect(out).to include normalize_uri_file(<<-EOS.strip)
+Could not find gem 'not-a-gem' in rubygems repository file://localhost#{gem_repo1}/ or installed locally.
+The source does not contain any versions of 'not-a-gem'
+ EOS
+ end
+
+ it "should report a helpful error message with reference to cache if available" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ G
+ bundle :cache
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ gem "not-a-gem", :group => :development
+ G
+ expect(out).to include("Could not find gem 'not-a-gem' in").
+ and include("or in gems cached in vendor/cache.")
+ end
+ end
+ end
+
+ describe "for second bundle install run" do
+ it "without any options" do
+ 2.times { bundle :install }
+ expect(out).to include(bundle_show_message)
+ expect(out).to_not include("Gems in the groups")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include(installed_gems_stats)
+ end
+
+ it "with --without one group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo")
+ bundle! :install
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the group emo were not installed")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include(installed_gems_stats)
+ end
+
+ it "with --without two groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo test")
+ bundle! :install
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the groups emo and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with --without more groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo obama test")
+ bundle :install
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the groups emo, obama and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ end
+ end
+
+ describe "for bundle update" do
+ it "without any options" do
+ bundle! :update, :all => bundle_update_requires_all?
+ expect(out).not_to include("Gems in the groups")
+ expect(out).to include(bundle_updated_message)
+ end
+
+ it "with --without one group" do
+ bundle! :install, forgotten_command_line_options(:without => "emo")
+ bundle! :update, :all => bundle_update_requires_all?
+ expect(out).to include("Gems in the group emo were not installed")
+ expect(out).to include(bundle_updated_message)
+ end
+
+ it "with --without two groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo test")
+ bundle! :update, :all => bundle_update_requires_all?
+ expect(out).to include("Gems in the groups emo and test were not installed")
+ expect(out).to include(bundle_updated_message)
+ end
+
+ it "with --without more groups" do
+ bundle! :install, forgotten_command_line_options(:without => "emo obama test")
+ bundle! :update, :all => bundle_update_requires_all?
+ expect(out).to include("Gems in the groups emo, obama and test were not installed")
+ expect(out).to include(bundle_updated_message)
+ end
+ end
+end
diff --git a/spec/bundler/install/prereleases_spec.rb b/spec/bundler/install/prereleases_spec.rb
new file mode 100644
index 0000000000..7af8c3b304
--- /dev/null
+++ b/spec/bundler/install/prereleases_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ describe "when prerelease gems are available" do
+ it "finds prereleases" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "not_released"
+ G
+ expect(the_bundle).to include_gems "not_released 1.0.pre"
+ end
+
+ it "uses regular releases if available" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "has_prerelease"
+ G
+ expect(the_bundle).to include_gems "has_prerelease 1.0"
+ end
+
+ it "uses prereleases if requested" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "has_prerelease", "1.1.pre"
+ G
+ expect(the_bundle).to include_gems "has_prerelease 1.1.pre"
+ end
+ end
+
+ describe "when prerelease gems are not available" do
+ it "still works" do
+ build_repo3
+ install_gemfile <<-G
+ source "file://#{gem_repo3}"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/process_lock_spec.rb b/spec/bundler/install/process_lock_spec.rb
new file mode 100644
index 0000000000..be8fd04fdd
--- /dev/null
+++ b/spec/bundler/install/process_lock_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+RSpec.describe "process lock spec" do
+ describe "when an install operation is already holding a process lock" do
+ before { FileUtils.mkdir_p(default_bundle_path) }
+
+ it "will not run a second concurrent bundle install until the lock is released" do
+ thread = Thread.new do
+ Bundler::ProcessLock.lock(default_bundle_path) do
+ sleep 1 # ignore quality_spec
+ expect(the_bundle).not_to include_gems "rack 1.0"
+ end
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ thread.join
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ context "when creating a lock raises Errno::ENOTSUP", :ruby => ">= 1.9" do
+ before { allow(File).to receive(:open).and_raise(Errno::ENOTSUP) }
+
+ it "skips creating the lock file and yields" do
+ processed = false
+ Bundler::ProcessLock.lock(default_bundle_path) { processed = true }
+
+ expect(processed).to eq true
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/redownload_spec.rb b/spec/bundler/install/redownload_spec.rb
new file mode 100644
index 0000000000..665c64d49a
--- /dev/null
+++ b/spec/bundler/install/redownload_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install", :bundler => "< 2", :ruby => ">= 2.0" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ before { bundle "config major_deprecations yes" }
+
+ shared_examples_for "an option to force redownloading gems" do
+ it "re-installs installed gems" do
+ rack_lib = default_bundle_path("gems/rack-1.0.0/lib/rack.rb")
+
+ bundle! :install
+ rack_lib.open("w") {|f| f.write("blah blah blah") }
+ bundle! :install, flag => true
+
+ expect(out).to include "Installing rack 1.0.0"
+ expect(rack_lib.open(&:read)).to eq("RACK = '1.0.0'\n")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "works on first bundle install" do
+ bundle! :install, flag => true
+
+ expect(out).to include "Installing rack 1.0.0"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ context "with a git gem" do
+ let!(:ref) { build_git("foo", "1.0").ref_for("HEAD", 11) }
+
+ before do
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+ end
+
+ it "re-installs installed gems" do
+ foo_lib = default_bundle_path("bundler/gems/foo-1.0-#{ref}/lib/foo.rb")
+
+ bundle! :install
+ foo_lib.open("w") {|f| f.write("blah blah blah") }
+ bundle! :install, flag => true
+
+ expect(foo_lib.open(&:read)).to eq("FOO = '1.0'\n")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "works on first bundle install" do
+ bundle! :install, flag => true
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+ end
+ end
+
+ describe "with --force" do
+ it_behaves_like "an option to force redownloading gems" do
+ let(:flag) { "force" }
+ end
+
+ it "shows a deprecation when single flag passed" do
+ bundle! "install --force"
+ expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+
+ it "shows a deprecation when multiple flags passed" do
+ bundle! "install --no-color --force"
+ expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+ end
+
+ describe "with --redownload" do
+ it_behaves_like "an option to force redownloading gems" do
+ let(:flag) { "redownload" }
+ end
+
+ it "does not show a deprecation when single flag passed" do
+ bundle! "install --redownload"
+ expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+
+ it "does not show a deprecation when single multiple flags passed" do
+ bundle! "install --no-color --redownload"
+ expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+ end
+end
diff --git a/spec/bundler/install/security_policy_spec.rb b/spec/bundler/install/security_policy_spec.rb
new file mode 100644
index 0000000000..7be09d6bd4
--- /dev/null
+++ b/spec/bundler/install/security_policy_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require "rubygems/security"
+
+# unfortunately, testing signed gems with a provided CA is extremely difficult
+# as 'gem cert' is currently the only way to add CAs to the system.
+
+RSpec.describe "policies with unsigned gems" do
+ before do
+ build_security_repo
+ gemfile <<-G
+ source "file://#{security_repo}"
+ gem "rack"
+ gem "signed_gem"
+ G
+ end
+
+ it "will work after you try to deploy without a lock" do
+ bundle "install --deployment"
+ bundle :install
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems "rack 1.0", "signed_gem 1.0"
+ end
+
+ it "will fail when given invalid security policy" do
+ bundle "install --trust-policy=InvalidPolicyName"
+ expect(out).to include("RubyGems doesn't know about trust policy")
+ end
+
+ it "will fail with High Security setting due to presence of unsigned gem" do
+ bundle "install --trust-policy=HighSecurity"
+ expect(out).to include("security policy didn't allow")
+ end
+
+ # This spec will fail on RubyGems 2 rc1 due to a bug in policy.rb. the bug is fixed in rc3.
+ it "will fail with Medium Security setting due to presence of unsigned gem", :unless => ENV["RGV"] == "v2.0.0.rc.1" do
+ bundle "install --trust-policy=MediumSecurity"
+ expect(out).to include("security policy didn't allow")
+ end
+
+ it "will succeed with no policy" do
+ bundle "install"
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+end
+
+RSpec.describe "policies with signed gems and no CA" do
+ before do
+ build_security_repo
+ gemfile <<-G
+ source "file://#{security_repo}"
+ gem "signed_gem"
+ G
+ end
+
+ it "will fail with High Security setting, gem is self-signed" do
+ bundle "install --trust-policy=HighSecurity"
+ expect(out).to include("security policy didn't allow")
+ end
+
+ it "will fail with Medium Security setting, gem is self-signed" do
+ bundle "install --trust-policy=MediumSecurity"
+ expect(out).to include("security policy didn't allow")
+ end
+
+ it "will succeed with Low Security setting, low security accepts self signed gem" do
+ bundle "install --trust-policy=LowSecurity"
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems "signed_gem 1.0"
+ end
+
+ it "will succeed with no policy" do
+ bundle "install"
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems "signed_gem 1.0"
+ end
+end
diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb
new file mode 100644
index 0000000000..7c4b98bfdf
--- /dev/null
+++ b/spec/bundler/install/yanked_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+RSpec.context "when installing a bundle that includes yanked gems" do
+ before(:each) do
+ build_repo4 do
+ build_gem "foo", "9.0.0"
+ end
+ end
+
+ it "throws an error when the original gem version is yanked" do
+ lockfile <<-L
+ GEM
+ remote: file://#{gem_repo4}
+ specs:
+ foo (10.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ foo (= 10.0.0)
+
+ L
+
+ install_gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem "foo", "10.0.0"
+ G
+
+ expect(out).to include("Your bundle is locked to foo (10.0.0)")
+ end
+
+ it "throws the original error when only the Gemfile specifies a gem version that doesn't exist" do
+ install_gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem "foo", "10.0.0"
+ G
+
+ expect(out).not_to include("Your bundle is locked to foo (10.0.0)")
+ expect(out).to include("Could not find gem 'foo (= 10.0.0)' in")
+ end
+end
+
+RSpec.context "when using gem before installing" do
+ it "does not suggest the author has yanked the gem" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: file://#{gem_repo1}
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack (= 0.9.1)
+ L
+
+ bundle :list
+
+ expect(out).to include("Could not find rack-0.9.1 in any of the sources")
+ expect(out).to_not include("Your bundle is locked to rack (0.9.1), but that version could not be found in any of the sources listed in your Gemfile.")
+ expect(out).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.")
+ expect(out).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.")
+ end
+end
diff --git a/spec/bundler/lock/git_spec.rb b/spec/bundler/lock/git_spec.rb
new file mode 100644
index 0000000000..14b80483ee
--- /dev/null
+++ b/spec/bundler/lock/git_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle lock with git gems" do
+ before :each do
+ build_git "foo"
+
+ install_gemfile <<-G
+ gem 'foo', :git => "#{lib_path("foo-1.0")}"
+ G
+ end
+
+ it "doesn't break right after running lock" do
+ expect(the_bundle).to include_gems "foo 1.0.0"
+ end
+
+ it "locks a git source to the current ref" do
+ update_git "foo"
+ bundle :install
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" unless defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "provides correct #full_gem_path" do
+ run <<-RUBY
+ puts Bundler.rubygems.find_name('foo').first.full_gem_path
+ RUBY
+ expect(out).to eq(bundle("info foo --path"))
+ end
+end
diff --git a/spec/bundler/lock/lockfile_bundler_1_spec.rb b/spec/bundler/lock/lockfile_bundler_1_spec.rb
new file mode 100644
index 0000000000..fcdf6ebf0d
--- /dev/null
+++ b/spec/bundler/lock/lockfile_bundler_1_spec.rb
@@ -0,0 +1,1386 @@
+# frozen_string_literal: true
+
+RSpec.describe "the lockfile format", :bundler => "< 2" do
+ include Bundler::GemHelpers
+
+ before { ENV["BUNDLER_SPEC_IGNORE_COMPATIBILITY_GUARD"] = "TRUE" }
+
+ it "generates a simple lockfile for a single source, gem" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "updates the lockfile's bundler version if current ver. is newer" do
+ lockfile <<-L
+ GIT
+ remote: git://github.com/nex3/haml.git
+ revision: 8a2271f
+ specs:
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ omg!
+ rack
+
+ BUNDLED WITH
+ 1.8.2
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not update the lockfile's bundler version if nothing changed during bundle install", :ruby_repo do
+ version = "#{Bundler::VERSION.split(".").first}.0.0.0.a"
+
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{version}
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{version}
+ G
+ end
+
+ it "updates the lockfile's bundler version if not present" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack", "> 0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack (> 0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "outputs a warning if the current is older than lockfile's bundler version" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.1.0
+ L
+
+ simulate_bundler_version "9999999.0.0" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+ end
+
+ warning_message = "the running version of Bundler (9999999.0.0) is older " \
+ "than the version that created the lockfile (9999999.1.0)"
+ expect(out.scan(warning_message).size).to eq(1)
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.1.0
+ G
+ end
+
+ it "errors if the current is a major version older than lockfile's bundler version" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.0.0
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ expect(exitstatus > 0) if exitstatus
+ expect(out).to include("You must use Bundler 9999999 or greater with this lockfile.")
+ end
+
+ it "shows a friendly error when running with a new bundler 2 lockfile" do
+ lockfile <<-L
+ GEM
+ remote: https://rails-assets.org/
+ specs:
+ rails-assets-bootstrap (3.3.4)
+ rails-assets-jquery (>= 1.9.1)
+ rails-assets-jquery (2.1.4)
+
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ rake (10.4.2)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rails-assets-bootstrap!
+ rake
+
+ BUNDLED WITH
+ 9999999.0.0
+ L
+
+ install_gemfile <<-G
+ source 'https://rubygems.org'
+ gem 'rake'
+
+ source 'https://rails-assets.org' do
+ gem 'rails-assets-bootstrap'
+ end
+ G
+
+ expect(exitstatus > 0) if exitstatus
+ expect(out).to include("You must use Bundler 9999999 or greater with this lockfile.")
+ end
+
+ it "warns when updating bundler major version" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 1.10.0
+ L
+
+ simulate_bundler_version "9999999.0.0" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+ end
+
+ expect(out).to include("Warning: the lockfile is being updated to Bundler " \
+ "9999999, after which you will be unable to return to Bundler 1.")
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.0.0
+ G
+ end
+
+ it "generates a simple lockfile for a single source, gem with dependencies" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack-obama"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack-obama
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a simple lockfile for a single source, gem with a version requirement" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack-obama", ">= 1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a lockfile wihout credentials for a configured source" do
+ bundle "config http://localgemserver.test/ user:pass"
+
+ install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true)
+ source "http://localgemserver.test/"
+ source "http://user:pass@othergemserver.test/"
+
+ gem "rack-obama", ">= 1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: http://localgemserver.test/
+ remote: http://user:pass@othergemserver.test/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates lockfiles with multiple requirements" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "net-sftp"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ net-sftp (1.1.1)
+ net-ssh (>= 1.0.0, < 1.99.0)
+ net-ssh (1.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ net-sftp
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ expect(the_bundle).to include_gems "net-sftp 1.1.1", "net-ssh 1.0.0"
+ end
+
+ it "generates a simple lockfile for a single pinned source, gem with a version requirement" do
+ git = build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile_should_be <<-G
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("master")}
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not asplode when a platform specific dependency is present and the Gemfile has not been resolved on that platform" do
+ build_lib "omg", :path => lib_path("omg")
+
+ gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ platforms :#{not_local_tag} do
+ gem "omg", :path => "#{lib_path("omg")}"
+ end
+
+ gem "rack"
+ G
+
+ lockfile <<-L
+ GIT
+ remote: git://github.com/nex3/haml.git
+ revision: 8a2271f
+ specs:
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{not_local}
+
+ DEPENDENCIES
+ omg!
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "serializes global git sources" do
+ git = build_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem "foo"
+ end
+ G
+
+ lockfile_should_be <<-G
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("master")}
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a lockfile with a ref for a single pinned source, git gem with a branch requirement" do
+ git = build_git "foo"
+ update_git "foo", :branch => "omg"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :branch => "omg"
+ G
+
+ lockfile_should_be <<-G
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("omg")}
+ branch: omg
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a lockfile with a ref for a single pinned source, git gem with a tag requirement" do
+ git = build_git "foo"
+ update_git "foo", :tag => "omg"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :tag => "omg"
+ G
+
+ lockfile_should_be <<-G
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("omg")}
+ tag: omg
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "serializes pinned path sources to the lockfile" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile_should_be <<-G
+ PATH
+ remote: #{lib_path("foo-1.0")}
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "serializes pinned path sources to the lockfile even when packaging" do
+ build_lib "foo"
+
+ install_gemfile! <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle! :package, forgotten_command_line_options([:all, :cache_all] => true)
+ bundle! :install, :local => true
+
+ lockfile_should_be <<-G
+ PATH
+ remote: #{lib_path("foo-1.0")}
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "sorts serialized sources by type" do
+ build_lib "foo"
+ bar = build_git "bar"
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ gem "bar", :git => "#{lib_path("bar-1.0")}"
+ G
+
+ lockfile_should_be <<-G
+ GIT
+ remote: #{lib_path("bar-1.0")}
+ revision: #{bar.ref_for("master")}
+ specs:
+ bar (1.0)
+
+ PATH
+ remote: #{lib_path("foo-1.0")}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ bar!
+ foo!
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "lists gems alphabetically" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "thin"
+ gem "actionpack"
+ gem "rack-obama"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+ thin (1.0)
+ rack
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ actionpack
+ rack-obama
+ thin
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "orders dependencies' dependencies in alphabetical order" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rails"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ actionmailer (2.3.2)
+ activesupport (= 2.3.2)
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activerecord (2.3.2)
+ activesupport (= 2.3.2)
+ activeresource (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ rails (2.3.2)
+ actionmailer (= 2.3.2)
+ actionpack (= 2.3.2)
+ activerecord (= 2.3.2)
+ activeresource (= 2.3.2)
+ rake (= 10.0.2)
+ rake (10.0.2)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rails
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "orders dependencies by version" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem 'double_deps'
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ double_deps (1.0)
+ net-ssh
+ net-ssh (>= 1.0.0)
+ net-ssh (1.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ double_deps
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add the :require option to the lockfile" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack-obama", ">= 1.0", :require => "rack/obama"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add the :group option to the lockfile" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack-obama", ">= 1.0", :group => :test
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided in a relative fashion and in Gemfile dir" do
+ build_lib "foo", :path => bundled_app("foo")
+
+ install_gemfile <<-G
+ path "foo"
+ gem "foo"
+ G
+
+ lockfile_should_be <<-G
+ PATH
+ remote: foo
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided in a relative fashion and is above Gemfile dir" do
+ build_lib "foo", :path => bundled_app(File.join("..", "foo"))
+
+ install_gemfile <<-G
+ path "../foo"
+ gem "foo"
+ G
+
+ lockfile_should_be <<-G
+ PATH
+ remote: ../foo
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided in an absolute fashion but is relative" do
+ build_lib "foo", :path => bundled_app("foo")
+
+ install_gemfile <<-G
+ path File.expand_path("../foo", __FILE__)
+ gem "foo"
+ G
+
+ lockfile_should_be <<-G
+ PATH
+ remote: foo
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided for gemspec" do
+ build_lib("foo", :path => tmp.join("foo"))
+
+ install_gemfile <<-G
+ gemspec :path => "../foo"
+ G
+
+ lockfile_should_be <<-G
+ PATH
+ remote: ../foo
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "keeps existing platforms in the lockfile" do
+ lockfile <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ java
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ platforms = ["java", generic_local_platform.to_s].sort
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{platforms[0]}
+ #{platforms[1]}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "persists the spec's platform to the lockfile" do
+ build_gem "platform_specific", "1.0.0", :to_system => true do |s|
+ s.platform = Gem::Platform.new("universal-java-16")
+ end
+
+ simulate_platform "universal-java-16"
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "platform_specific"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ platform_specific (1.0-java)
+
+ PLATFORMS
+ java
+
+ DEPENDENCIES
+ platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate gems" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ gem "activesupport"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ activesupport (2.3.5)
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ activesupport
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate dependencies" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate dependencies with versions" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack", "1.0"
+ gem "rack", "1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack (= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate dependencies in different groups" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack", "1.0", :group => :one
+ gem "rack", "1.0", :group => :two
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack (= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "raises if two different versions are used" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack", "1.0"
+ gem "rack", "1.1"
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ expect(out).to include "rack (= 1.0) and rack (= 1.1)"
+ end
+
+ it "raises if two different sources are used" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ gem "rack", :git => "git://hubz.com"
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ expect(out).to include "rack (>= 0) should come from an unspecified source and git://hubz.com (at master)"
+ end
+
+ it "works correctly with multiple version dependencies" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack", "> 0.9", "< 1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack (> 0.9, < 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "captures the Ruby version in the lockfile" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ ruby '#{RUBY_VERSION}'
+ gem "rack", "> 0.9", "< 1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack (> 0.9, < 1.0)
+
+ RUBY VERSION
+ ruby #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ # Some versions of the Bundler 1.1 RC series introduced corrupted
+ # lockfiles. There were two major problems:
+ #
+ # * multiple copies of the same GIT section appeared in the lockfile
+ # * when this happened, those sections got multiple copies of gems
+ # in those sections.
+ it "fixes corrupted lockfiles" do
+ build_git "omg", :path => lib_path("omg")
+ revision = revision_for(lib_path("omg"))
+
+ gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "omg", :git => "#{lib_path("omg")}", :branch => 'master'
+ G
+
+ bundle "install --path vendor"
+ expect(the_bundle).to include_gems "omg 1.0"
+
+ # Create a Gemfile.lock that has duplicate GIT sections
+ lockfile <<-L
+ GIT
+ remote: #{lib_path("omg")}
+ revision: #{revision}
+ branch: master
+ specs:
+ omg (1.0)
+
+ GIT
+ remote: #{lib_path("omg")}
+ revision: #{revision}
+ branch: master
+ specs:
+ omg (1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+
+ PLATFORMS
+ #{local}
+
+ DEPENDENCIES
+ omg!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ FileUtils.rm_rf(bundled_app("vendor"))
+ bundle "install"
+ expect(the_bundle).to include_gems "omg 1.0"
+
+ # Confirm that duplicate specs do not appear
+ lockfile_should_be(<<-L)
+ GIT
+ remote: #{lib_path("omg")}
+ revision: #{revision}
+ branch: master
+ specs:
+ omg (1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+
+ PLATFORMS
+ #{local}
+
+ DEPENDENCIES
+ omg!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "raises a helpful error message when the lockfile is missing deps" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack_middleware (1.0)
+
+ PLATFORMS
+ #{local}
+
+ DEPENDENCIES
+ rack_middleware
+ L
+
+ install_gemfile <<-G
+ source "file:#{gem_repo1}"
+ gem "rack_middleware"
+ G
+
+ expect(out).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.")
+ end
+
+ describe "a line ending" do
+ def set_lockfile_mtime_to_known_value
+ time = Time.local(2000, 1, 1, 0, 0, 0)
+ File.utime(time, time, bundled_app("Gemfile.lock"))
+ end
+ before(:each) do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo2}"
+ gem "rack"
+ G
+ set_lockfile_mtime_to_known_value
+ end
+
+ it "generates Gemfile.lock with \\n line endings" do
+ expect(File.read(bundled_app("Gemfile.lock"))).not_to match("\r\n")
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ context "during updates" do
+ it "preserves Gemfile.lock \\n line endings" do
+ update_repo2
+
+ expect { bundle "update", :all => true }.to change { File.mtime(bundled_app("Gemfile.lock")) }
+ expect(File.read(bundled_app("Gemfile.lock"))).not_to match("\r\n")
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+
+ it "preserves Gemfile.lock \\n\\r line endings" do
+ update_repo2
+ win_lock = File.read(bundled_app("Gemfile.lock")).gsub(/\n/, "\r\n")
+ File.open(bundled_app("Gemfile.lock"), "wb") {|f| f.puts(win_lock) }
+ set_lockfile_mtime_to_known_value
+
+ expect { bundle "update", :all => true }.to change { File.mtime(bundled_app("Gemfile.lock")) }
+ expect(File.read(bundled_app("Gemfile.lock"))).to match("\r\n")
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+ end
+
+ context "when nothing changes" do
+ it "preserves Gemfile.lock \\n line endings" do
+ expect do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+ RUBY
+ end.not_to change { File.mtime(bundled_app("Gemfile.lock")) }
+ end
+
+ it "preserves Gemfile.lock \\n\\r line endings" do
+ win_lock = File.read(bundled_app("Gemfile.lock")).gsub(/\n/, "\r\n")
+ File.open(bundled_app("Gemfile.lock"), "wb") {|f| f.puts(win_lock) }
+ set_lockfile_mtime_to_known_value
+
+ expect do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+ RUBY
+ end.not_to change { File.mtime(bundled_app("Gemfile.lock")) }
+ end
+ end
+ end
+
+ it "refuses to install if Gemfile.lock contains conflict markers" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ <<<<<<<
+ rack (1.0.0)
+ =======
+ rack (1.0.1)
+ >>>>>>>
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ install_gemfile(<<-G)
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ G
+
+ expect(last_command.bundler_err).to match(/your Gemfile.lock contains merge conflicts/i)
+ expect(last_command.bundler_err).to match(/git checkout HEAD -- Gemfile.lock/i)
+ end
+end
diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb
new file mode 100644
index 0000000000..53c832445f
--- /dev/null
+++ b/spec/bundler/lock/lockfile_spec.rb
@@ -0,0 +1,1425 @@
+# frozen_string_literal: true
+
+RSpec.describe "the lockfile format", :bundler => "2" do
+ include Bundler::GemHelpers
+
+ before { ENV["BUNDLER_SPEC_IGNORE_COMPATIBILITY_GUARD"] = "TRUE" }
+
+ it "generates a simple lockfile for a single source, gem" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "updates the lockfile's bundler version if current ver. is newer" do
+ lockfile <<-L
+ GIT
+ remote: git://github.com/nex3/haml.git
+ revision: 8a2271f
+ specs:
+
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ omg!
+ rack
+
+ BUNDLED WITH
+ 1.8.2
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not update the lockfile's bundler version if nothing changed during bundle install" do
+ version = "#{Bundler::VERSION.split(".").first}.0.0.0.a"
+
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{version}
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{version}
+ G
+ end
+
+ it "updates the lockfile's bundler version if not present" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack", "> 0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack (> 0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "outputs a warning if the current is older than lockfile's bundler version" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.1.0
+ L
+
+ simulate_bundler_version "9999999.0.0" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+
+ gem "rack"
+ G
+ end
+
+ warning_message = "the running version of Bundler (9999999.0.0) is older " \
+ "than the version that created the lockfile (9999999.1.0)"
+ expect(last_command.bundler_err).to include warning_message
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.1.0
+ G
+ end
+
+ it "errors if the current is a major version older than lockfile's bundler version" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.0.0
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack"
+ G
+
+ expect(last_command).to be_failure
+ expect(last_command.bundler_err).to include("You must use Bundler 9999999 or greater with this lockfile.")
+ end
+
+ it "shows a friendly error when running with a new bundler 2 lockfile" do
+ lockfile <<-L
+ GEM
+ remote: https://rails-assets.org/
+ specs:
+ rails-assets-bootstrap (3.3.4)
+ rails-assets-jquery (>= 1.9.1)
+ rails-assets-jquery (2.1.4)
+
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ rake (10.4.2)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rails-assets-bootstrap!
+ rake
+
+ BUNDLED WITH
+ 9999999.0.0
+ L
+
+ install_gemfile <<-G
+ source 'https://rubygems.org'
+ gem 'rake'
+
+ source 'https://rails-assets.org' do
+ gem 'rails-assets-bootstrap'
+ end
+ G
+
+ expect(last_command).to be_failure
+ expect(out).to include("You must use Bundler 9999999 or greater with this lockfile.")
+ end
+
+ it "warns when updating bundler major version" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 1.10.0
+ L
+
+ simulate_bundler_version "9999999.0.0" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack"
+ G
+ end
+
+ expect(out).to include("Warning: the lockfile is being updated to Bundler " \
+ "9999999, after which you will be unable to return to Bundler 1.")
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ 9999999.0.0
+ G
+ end
+
+ it "generates a simple lockfile for a single source, gem with dependencies" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack-obama"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack-obama
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a simple lockfile for a single source, gem with a version requirement" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack-obama", ">= 1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a lockfile without credentials for a configured source" do
+ bundle "config http://localgemserver.test/ user:pass"
+
+ install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true)
+ source "http://localgemserver.test/" do
+
+ end
+
+ source "http://user:pass@othergemserver.test/" do
+ gem "rack-obama", ">= 1.0"
+ end
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+
+ GEM
+ remote: http://user:pass@othergemserver.test/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates lockfiles with multiple requirements" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "net-sftp"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ net-sftp (1.1.1)
+ net-ssh (>= 1.0.0, < 1.99.0)
+ net-ssh (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ net-sftp
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ expect(the_bundle).to include_gems "net-sftp 1.1.1", "net-ssh 1.0.0"
+ end
+
+ it "generates a simple lockfile for a single pinned source, gem with a version requirement", :bundler => "< 2" do
+ git = build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile_should_be <<-G
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("master")}
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a simple lockfile for a single pinned source, gem with a version requirement" do
+ git = build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("master")}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not asplode when a platform specific dependency is present and the Gemfile has not been resolved on that platform" do
+ build_lib "omg", :path => lib_path("omg")
+
+ gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ platforms :#{not_local_tag} do
+ gem "omg", :path => "#{lib_path("omg")}"
+ end
+
+ gem "rack"
+ G
+
+ lockfile <<-L
+ GIT
+ remote: git://github.com/nex3/haml.git
+ revision: 8a2271f
+ specs:
+
+ GEM
+ remote: file://localhost#{gem_repo1}//
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{not_local}
+
+ DEPENDENCIES
+ omg!
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle! "install"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "serializes global git sources" do
+ git = build_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem "foo"
+ end
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("master")}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a lockfile with a ref for a single pinned source, git gem with a branch requirement" do
+ git = build_git "foo"
+ update_git "foo", :branch => "omg"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :branch => "omg"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("omg")}
+ branch: omg
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "generates a lockfile with a ref for a single pinned source, git gem with a tag requirement" do
+ git = build_git "foo"
+ update_git "foo", :tag => "omg"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :tag => "omg"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("omg")}
+ tag: omg
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "serializes pinned path sources to the lockfile" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ PATH
+ remote: #{lib_path("foo-1.0")}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "serializes pinned path sources to the lockfile even when packaging" do
+ build_lib "foo"
+
+ install_gemfile! <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle! :package, forgotten_command_line_options([:all, :cache_all] => true)
+ bundle! :install, :local => true
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ PATH
+ remote: #{lib_path("foo-1.0")}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "sorts serialized sources by type" do
+ build_lib "foo"
+ bar = build_git "bar"
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack"
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ gem "bar", :git => "#{lib_path("bar-1.0")}"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ GIT
+ remote: #{lib_path("bar-1.0")}
+ revision: #{bar.ref_for("master")}
+ specs:
+ bar (1.0)
+
+ PATH
+ remote: #{lib_path("foo-1.0")}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ bar!
+ foo!
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "lists gems alphabetically" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "thin"
+ gem "actionpack"
+ gem "rack-obama"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+ thin (1.0)
+ rack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ actionpack
+ rack-obama
+ thin
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "orders dependencies' dependencies in alphabetical order" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rails"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ actionmailer (2.3.2)
+ activesupport (= 2.3.2)
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activerecord (2.3.2)
+ activesupport (= 2.3.2)
+ activeresource (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ rails (2.3.2)
+ actionmailer (= 2.3.2)
+ actionpack (= 2.3.2)
+ activerecord (= 2.3.2)
+ activeresource (= 2.3.2)
+ rake (= 10.0.2)
+ rake (10.0.2)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rails
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "orders dependencies by version" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem 'double_deps'
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ double_deps (1.0)
+ net-ssh
+ net-ssh (>= 1.0.0)
+ net-ssh (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ double_deps
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add the :require option to the lockfile" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack-obama", ">= 1.0", :require => "rack/obama"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add the :group option to the lockfile" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack-obama", ">= 1.0", :group => :test
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack-obama (>= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided in a relative fashion and in Gemfile dir" do
+ build_lib "foo", :path => bundled_app("foo")
+
+ install_gemfile <<-G
+ path "foo" do
+ gem "foo"
+ end
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ PATH
+ remote: foo
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided in a relative fashion and is above Gemfile dir" do
+ build_lib "foo", :path => bundled_app(File.join("..", "foo"))
+
+ install_gemfile <<-G
+ path "../foo" do
+ gem "foo"
+ end
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ PATH
+ remote: ../foo
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided in an absolute fashion but is relative" do
+ build_lib "foo", :path => bundled_app("foo")
+
+ install_gemfile <<-G
+ path File.expand_path("../foo", __FILE__) do
+ gem "foo"
+ end
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ PATH
+ remote: foo
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "stores relative paths when the path is provided for gemspec" do
+ build_lib("foo", :path => tmp.join("foo"))
+
+ install_gemfile <<-G
+ gemspec :path => "../foo"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ specs:
+
+ PATH
+ remote: ../foo
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "keeps existing platforms in the lockfile" do
+ lockfile <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ java
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms "java", generic_local_platform, specific_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "persists the spec's platform to the lockfile" do
+ build_repo2 do
+ build_gem "platform_specific", "1.0" do |s|
+ s.platform = Gem::Platform.new("universal-java-16")
+ end
+ end
+
+ simulate_platform "universal-java-16"
+
+ install_gemfile! <<-G
+ source "file://localhost#{gem_repo2}"
+ gem "platform_specific"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ platform_specific (1.0-java)
+ platform_specific (1.0-universal-java-16)
+
+ PLATFORMS
+ java
+ universal-java-16
+
+ DEPENDENCIES
+ platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate gems" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack"
+ gem "activesupport"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ activesupport (2.3.5)
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ activesupport
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate dependencies" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack"
+ gem "rack"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate dependencies with versions" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack", "1.0"
+ gem "rack", "1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack (= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "does not add duplicate dependencies in different groups" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack", "1.0", :group => :one
+ gem "rack", "1.0", :group => :two
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack (= 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "raises if two different versions are used" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack", "1.0"
+ gem "rack", "1.1"
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ expect(out).to include "rack (= 1.0) and rack (= 1.1)"
+ end
+
+ it "raises if two different sources are used" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack"
+ gem "rack", :git => "git://hubz.com"
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ expect(out).to include "rack (>= 0) should come from an unspecified source and git://hubz.com (at master)"
+ end
+
+ it "works correctly with multiple version dependencies" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "rack", "> 0.9", "< 1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack (> 0.9, < 1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "captures the Ruby version in the lockfile" do
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ ruby '#{RUBY_VERSION}'
+ gem "rack", "> 0.9", "< 1.0"
+ G
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack (> 0.9, < 1.0)
+
+ RUBY VERSION
+ ruby #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ # Some versions of the Bundler 1.1 RC series introduced corrupted
+ # lockfiles. There were two major problems:
+ #
+ # * multiple copies of the same GIT section appeared in the lockfile
+ # * when this happened, those sections got multiple copies of gems
+ # in those sections.
+ it "fixes corrupted lockfiles" do
+ build_git "omg", :path => lib_path("omg")
+ revision = revision_for(lib_path("omg"))
+
+ gemfile <<-G
+ source "file://localhost#{gem_repo1}/"
+ gem "omg", :git => "#{lib_path("omg")}", :branch => 'master'
+ G
+
+ bundle! :install, forgotten_command_line_options(:path => "vendor")
+ expect(the_bundle).to include_gems "omg 1.0"
+
+ # Create a Gemfile.lock that has duplicate GIT sections
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+
+ GIT
+ remote: #{lib_path("omg")}
+ revision: #{revision}
+ branch: master
+ specs:
+ omg (1.0)
+
+ GIT
+ remote: #{lib_path("omg")}
+ revision: #{revision}
+ branch: master
+ specs:
+ omg (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ omg!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ FileUtils.rm_rf(bundled_app("vendor"))
+ bundle "install"
+ expect(the_bundle).to include_gems "omg 1.0"
+
+ # Confirm that duplicate specs do not appear
+ lockfile_should_be(<<-L)
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+
+ GIT
+ remote: #{lib_path("omg")}
+ revision: #{revision}
+ branch: master
+ specs:
+ omg (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ omg!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "raises a helpful error message when the lockfile is missing deps" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack_middleware (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack_middleware
+ L
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo1}"
+ gem "rack_middleware"
+ G
+
+ expect(out).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.")
+ end
+
+ describe "a line ending" do
+ def set_lockfile_mtime_to_known_value
+ time = Time.local(2000, 1, 1, 0, 0, 0)
+ File.utime(time, time, bundled_app("Gemfile.lock"))
+ end
+ before(:each) do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo2}"
+ gem "rack"
+ G
+ set_lockfile_mtime_to_known_value
+ end
+
+ it "generates Gemfile.lock with \\n line endings" do
+ expect(File.read(bundled_app("Gemfile.lock"))).not_to match("\r\n")
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ context "during updates" do
+ it "preserves Gemfile.lock \\n line endings" do
+ update_repo2
+
+ expect { bundle "update", :all => true }.to change { File.mtime(bundled_app("Gemfile.lock")) }
+ expect(File.read(bundled_app("Gemfile.lock"))).not_to match("\r\n")
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+
+ it "preserves Gemfile.lock \\n\\r line endings" do
+ update_repo2
+ win_lock = File.read(bundled_app("Gemfile.lock")).gsub(/\n/, "\r\n")
+ File.open(bundled_app("Gemfile.lock"), "wb") {|f| f.puts(win_lock) }
+ set_lockfile_mtime_to_known_value
+
+ expect { bundle "update", :all => true }.to change { File.mtime(bundled_app("Gemfile.lock")) }
+ expect(File.read(bundled_app("Gemfile.lock"))).to match("\r\n")
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+ end
+
+ context "when nothing changes" do
+ it "preserves Gemfile.lock \\n line endings" do
+ expect do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+ RUBY
+ end.not_to change { File.mtime(bundled_app("Gemfile.lock")) }
+ end
+
+ it "preserves Gemfile.lock \\n\\r line endings" do
+ win_lock = File.read(bundled_app("Gemfile.lock")).gsub(/\n/, "\r\n")
+ File.open(bundled_app("Gemfile.lock"), "wb") {|f| f.puts(win_lock) }
+ set_lockfile_mtime_to_known_value
+
+ expect do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+ RUBY
+ end.not_to change { File.mtime(bundled_app("Gemfile.lock")) }
+ end
+ end
+ end
+
+ it "refuses to install if Gemfile.lock contains conflict markers" do
+ lockfile <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}//
+ specs:
+ <<<<<<<
+ rack (1.0.0)
+ =======
+ rack (1.0.1)
+ >>>>>>>
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ install_gemfile(<<-G)
+ source "file://localhost#{gem_repo1}/"
+ gem "rack"
+ G
+
+ expect(last_command.bundler_err).to match(/your Gemfile.lock contains merge conflicts/i)
+ expect(last_command.bundler_err).to match(/git checkout HEAD -- Gemfile.lock/i)
+ end
+end
diff --git a/spec/bundler/other/bundle_ruby_spec.rb b/spec/bundler/other/bundle_ruby_spec.rb
new file mode 100644
index 0000000000..a7da9cbec9
--- /dev/null
+++ b/spec/bundler/other/bundle_ruby_spec.rb
@@ -0,0 +1,155 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle_ruby", :bundler => "< 2" do
+ context "without patchlevel" do
+ it "returns the ruby version" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.9.3", :engine => 'ruby', :engine_version => '1.9.3'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("ruby 1.9.3")
+ end
+
+ it "engine defaults to MRI" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.9.3"
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("ruby 1.9.3")
+ end
+
+ it "handles jruby" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'jruby', :engine_version => '1.6.5'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("ruby 1.8.7 (jruby 1.6.5)")
+ end
+
+ it "handles rbx" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'rbx', :engine_version => '1.2.4'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("ruby 1.8.7 (rbx 1.2.4)")
+ end
+
+ it "handles truffleruby", :rubygems => ">= 2.1.0" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "2.5.1", :engine => 'truffleruby', :engine_version => '1.0.0-rc6'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("ruby 2.5.1 (truffleruby 1.0.0-rc6)")
+ end
+
+ it "raises an error if engine is used but engine version is not" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'rbx'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+ expect(exitstatus).not_to eq(0) if exitstatus
+
+ bundle_ruby
+ expect(out).to include("Please define :engine_version")
+ end
+
+ it "raises an error if engine_version is used but engine is not" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine_version => '1.2.4'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+ expect(exitstatus).not_to eq(0) if exitstatus
+
+ bundle_ruby
+ expect(out).to include("Please define :engine")
+ end
+
+ it "raises an error if engine version doesn't match ruby version for MRI" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'ruby', :engine_version => '1.2.4'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+ expect(exitstatus).not_to eq(0) if exitstatus
+
+ bundle_ruby
+ expect(out).to include("ruby_version must match the :engine_version for MRI")
+ end
+
+ it "should print if no ruby version is specified" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("No ruby version specified")
+ end
+ end
+
+ context "when using patchlevel" do
+ it "returns the ruby version" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.9.3", :patchlevel => '429', :engine => 'ruby', :engine_version => '1.9.3'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("ruby 1.9.3p429")
+ end
+
+ it "handles an engine" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.9.3", :patchlevel => '392', :engine => 'jruby', :engine_version => '1.7.4'
+
+ gem "foo"
+ G
+
+ bundle_ruby
+
+ expect(out).to include("ruby 1.9.3p392 (jruby 1.7.4)")
+ end
+ end
+end
diff --git a/spec/bundler/other/cli_dispatch_spec.rb b/spec/bundler/other/cli_dispatch_spec.rb
new file mode 100644
index 0000000000..d17819b394
--- /dev/null
+++ b/spec/bundler/other/cli_dispatch_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle command names" do
+ it "work when given fully" do
+ bundle "install"
+ expect(last_command.bundler_err).to eq("Could not locate Gemfile")
+ expect(last_command.stdboth).not_to include("Ambiguous command")
+ end
+
+ it "work when not ambiguous" do
+ bundle "ins"
+ expect(last_command.bundler_err).to eq("Could not locate Gemfile")
+ expect(last_command.stdboth).not_to include("Ambiguous command")
+ end
+
+ it "print a friendly error when ambiguous" do
+ bundle "in"
+ expect(last_command.bundler_err).to eq("Ambiguous command in matches [info, init, inject, install]")
+ end
+
+ context "when cache_command_is_package is set" do
+ before { bundle! "config cache_command_is_package true" }
+
+ it "dispatches `bundle cache` to the package command" do
+ bundle "cache --verbose"
+ expect(last_command.stdout).to start_with "Running `bundle package --verbose`"
+ end
+ end
+end
diff --git a/spec/bundler/other/compatibility_guard_spec.rb b/spec/bundler/other/compatibility_guard_spec.rb
new file mode 100644
index 0000000000..ac05ebd918
--- /dev/null
+++ b/spec/bundler/other/compatibility_guard_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundler compatibility guard" do
+ context "when the bundler version is 2+" do
+ before { simulate_bundler_version "2.0.a" }
+
+ context "when running on Ruby < 2.3", :ruby => "< 2.3" do
+ before { simulate_rubygems_version "2.6.11" }
+
+ it "raises a friendly error" do
+ bundle :version
+ expect(err).to eq("Bundler 2 requires Ruby 2.3 or later. Either install bundler 1 or update to a supported Ruby version.")
+ end
+ end
+
+ context "when running on RubyGems < 2.5", :ruby => ">= 2.5" do
+ before { simulate_rubygems_version "1.3.6" }
+
+ it "raises a friendly error" do
+ bundle :version
+ expect(err).to eq("Bundler 2 requires RubyGems 2.5 or later. Either install bundler 1 or update to a supported RubyGems version.")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/other/ext_spec.rb b/spec/bundler/other/ext_spec.rb
new file mode 100644
index 0000000000..3f6f8b4928
--- /dev/null
+++ b/spec/bundler/other/ext_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+RSpec.describe "Gem::Specification#match_platform" do
+ it "does not match platforms other than the gem platform" do
+ darwin = gem "lol", "1.0", "platform_specific-1.0-x86-darwin-10"
+ expect(darwin.match_platform(pl("java"))).to eq(false)
+ end
+
+ context "when platform is a string" do
+ it "matches when platform is a string" do
+ lazy_spec = Bundler::LazySpecification.new("lol", "1.0", "universal-mingw32")
+ expect(lazy_spec.match_platform(pl("x86-mingw32"))).to eq(true)
+ expect(lazy_spec.match_platform(pl("x64-mingw32"))).to eq(true)
+ end
+ end
+end
+
+RSpec.describe "Bundler::GemHelpers#generic" do
+ include Bundler::GemHelpers
+
+ it "converts non-windows platforms into ruby" do
+ expect(generic(pl("x86-darwin-10"))).to eq(pl("ruby"))
+ expect(generic(pl("ruby"))).to eq(pl("ruby"))
+ end
+
+ it "converts java platform variants into java" do
+ expect(generic(pl("universal-java-17"))).to eq(pl("java"))
+ expect(generic(pl("java"))).to eq(pl("java"))
+ end
+
+ it "converts mswin platform variants into x86-mswin32" do
+ expect(generic(pl("mswin32"))).to eq(pl("x86-mswin32"))
+ expect(generic(pl("i386-mswin32"))).to eq(pl("x86-mswin32"))
+ expect(generic(pl("x86-mswin32"))).to eq(pl("x86-mswin32"))
+ end
+
+ it "converts 32-bit mingw platform variants into x86-mingw32" do
+ expect(generic(pl("mingw32"))).to eq(pl("x86-mingw32"))
+ expect(generic(pl("i386-mingw32"))).to eq(pl("x86-mingw32"))
+ expect(generic(pl("x86-mingw32"))).to eq(pl("x86-mingw32"))
+ end
+
+ it "converts 64-bit mingw platform variants into x64-mingw32" do
+ expect(generic(pl("x64-mingw32"))).to eq(pl("x64-mingw32"))
+ expect(generic(pl("x86_64-mingw32"))).to eq(pl("x64-mingw32"))
+ end
+end
+
+RSpec.describe "Gem::SourceIndex#refresh!" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "does not explode when called", :rubygems => "1.7" do
+ run "Gem.source_index.refresh!"
+ run "Gem::SourceIndex.new([]).refresh!"
+ end
+
+ it "does not explode when called", :rubygems => "< 1.7" do
+ run "Gem.source_index.refresh!"
+ run "Gem::SourceIndex.from_gems_in([]).refresh!"
+ end
+end
diff --git a/spec/bundler/other/major_deprecation_spec.rb b/spec/bundler/other/major_deprecation_spec.rb
new file mode 100644
index 0000000000..fba177b497
--- /dev/null
+++ b/spec/bundler/other/major_deprecation_spec.rb
@@ -0,0 +1,282 @@
+# frozen_string_literal: true
+
+RSpec.describe "major deprecations", :bundler => "< 2" do
+ let(:warnings) { last_command.bundler_err } # change to err in 2.0
+ let(:warnings_without_version_messages) { warnings.gsub(/#{Spec::Matchers::MAJOR_DEPRECATION}Bundler will only support ruby(gems)? >= .*/, "") }
+
+ context "in a .99 version" do
+ before do
+ simulate_bundler_version "1.99.1"
+ bundle "config --delete major_deprecations"
+ end
+
+ it "prints major deprecations without being configured" do
+ ruby <<-R
+ require "bundler"
+ Bundler::SharedHelpers.major_deprecation(Bundler::VERSION)
+ R
+
+ expect(warnings).to have_major_deprecation("1.99.1")
+ end
+ end
+
+ before do
+ bundle "config major_deprecations true"
+
+ create_file "gems.rb", <<-G
+ source "file:#{gem_repo1}"
+ ruby #{RUBY_VERSION.dump}
+ gem "rack"
+ G
+ bundle! "install"
+ end
+
+ describe "bundle_ruby" do
+ it "prints a deprecation" do
+ bundle_ruby
+ warnings.gsub! "\nruby #{RUBY_VERSION}", ""
+ expect(warnings).to have_major_deprecation "the bundle_ruby executable has been removed in favor of `bundle platform --ruby`"
+ end
+ end
+
+ describe "Bundler" do
+ describe ".clean_env" do
+ it "is deprecated in favor of .original_env" do
+ source = "Bundler.clean_env"
+ bundle "exec ruby -e #{source.dump}"
+ expect(warnings).to have_major_deprecation "`Bundler.clean_env` has weird edge cases, use `.original_env` instead"
+ end
+ end
+
+ describe ".environment" do
+ it "is deprecated in favor of .load" do
+ source = "Bundler.environment"
+ bundle "exec ruby -e #{source.dump}"
+ expect(warnings).to have_major_deprecation "Bundler.environment has been removed in favor of Bundler.load"
+ end
+ end
+
+ shared_examples_for "environmental deprecations" do |trigger|
+ describe "ruby version", :ruby => "< 2.0" do
+ it "requires a newer ruby version" do
+ instance_eval(&trigger)
+ expect(warnings).to have_major_deprecation "Bundler will only support ruby >= 2.0, you are running #{RUBY_VERSION}"
+ end
+ end
+
+ describe "rubygems version", :rubygems => "< 2.0" do
+ it "requires a newer rubygems version" do
+ instance_eval(&trigger)
+ expect(warnings).to have_major_deprecation "Bundler will only support rubygems >= 2.0, you are running #{Gem::VERSION}"
+ end
+ end
+ end
+
+ describe "-rbundler/setup" do
+ it_behaves_like "environmental deprecations", proc { ruby "require 'bundler/setup'" }
+ end
+
+ describe "Bundler.setup" do
+ it_behaves_like "environmental deprecations", proc { ruby "require 'bundler'; Bundler.setup" }
+ end
+
+ describe "bundle check" do
+ it_behaves_like "environmental deprecations", proc { bundle :check }
+ end
+
+ describe "bundle update --quiet" do
+ it "does not print any deprecations" do
+ bundle :update, :quiet => true
+ expect(warnings_without_version_messages).not_to have_major_deprecation
+ end
+ end
+
+ describe "bundle update" do
+ before do
+ create_file("gems.rb", "")
+ bundle! "install"
+ end
+
+ it "warns when no options are given" do
+ bundle! "update"
+ expect(warnings).to have_major_deprecation a_string_including("Pass --all to `bundle update` to update everything")
+ end
+
+ it "does not warn when --all is passed" do
+ bundle! "update --all"
+ expect(warnings_without_version_messages).not_to have_major_deprecation
+ end
+ end
+
+ describe "bundle install --binstubs" do
+ it "should output a deprecation warning" do
+ gemfile <<-G
+ gem 'rack'
+ G
+
+ bundle :install, :binstubs => true
+ expect(warnings).to have_major_deprecation a_string_including("The --binstubs option will be removed")
+ end
+ end
+ end
+
+ context "when bundle is run" do
+ it "should not warn about gems.rb" do
+ create_file "gems.rb", <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle :install
+ expect(warnings_without_version_messages).not_to have_major_deprecation
+ end
+
+ it "should print a Gemfile deprecation warning" do
+ create_file "gems.rb"
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ expect(the_bundle).to include_gem "rack 1.0"
+
+ expect(warnings).to have_major_deprecation a_string_including("gems.rb and gems.locked will be preferred to Gemfile and Gemfile.lock.")
+ end
+
+ context "with flags" do
+ it "should print a deprecation warning about autoremembering flags" do
+ install_gemfile <<-G, :path => "vendor/bundle"
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ expect(warnings).to have_major_deprecation a_string_including(
+ "flags passed to commands will no longer be automatically remembered."
+ )
+ end
+ end
+ end
+
+ context "when Bundler.setup is run in a ruby script" do
+ it "should print a single deprecation warning" do
+ create_file "gems.rb"
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :group => :test
+ G
+
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ require 'bundler/vendored_thor'
+
+ Bundler.ui = Bundler::UI::Shell.new
+ Bundler.setup
+ Bundler.setup
+ RUBY
+
+ expect(warnings_without_version_messages).to have_major_deprecation("gems.rb and gems.locked will be preferred to Gemfile and Gemfile.lock.")
+ end
+ end
+
+ context "when `bundler/deployment` is required in a ruby script" do
+ it "should print a capistrano deprecation warning" do
+ ruby(<<-RUBY)
+ require 'bundler/deployment'
+ RUBY
+
+ expect(warnings).to have_major_deprecation("Bundler no longer integrates " \
+ "with Capistrano, but Capistrano provides " \
+ "its own integration with Bundler via the " \
+ "capistrano-bundler gem. Use it instead.")
+ end
+ end
+
+ describe Bundler::Dsl do
+ before do
+ @rubygems = double("rubygems")
+ allow(Bundler::Source::Rubygems).to receive(:new) { @rubygems }
+ end
+
+ context "with github gems" do
+ it "warns about the https change" do
+ msg = <<-EOS
+The :github git source is deprecated, and will be removed in Bundler 2.0. Change any "reponame" :github sources to "username/reponame". Add this code to the top of your Gemfile to ensure it continues to work:
+
+ git_source(:github) {|repo_name| "https://github.com/\#{repo_name}.git" }
+
+ EOS
+ expect(Bundler::SharedHelpers).to receive(:major_deprecation).with(2, msg)
+ subject.gem("sparks", :github => "indirect/sparks")
+ end
+
+ it "upgrades to https on request" do
+ Bundler.settings.temporary "github.https" => true
+ msg = <<-EOS
+The :github git source is deprecated, and will be removed in Bundler 2.0. Change any "reponame" :github sources to "username/reponame". Add this code to the top of your Gemfile to ensure it continues to work:
+
+ git_source(:github) {|repo_name| "https://github.com/\#{repo_name}.git" }
+
+ EOS
+ expect(Bundler::SharedHelpers).to receive(:major_deprecation).with(2, msg)
+ expect(Bundler::SharedHelpers).to receive(:major_deprecation).with(2, "The `github.https` setting will be removed")
+ subject.gem("sparks", :github => "indirect/sparks")
+ github_uri = "https://github.com/indirect/sparks.git"
+ expect(subject.dependencies.first.source.uri).to eq(github_uri)
+ end
+ end
+
+ context "with bitbucket gems" do
+ it "warns about removal" do
+ allow(Bundler.ui).to receive(:deprecate)
+ msg = <<-EOS
+The :bitbucket git source is deprecated, and will be removed in Bundler 2.0. Add this code to the top of your Gemfile to ensure it continues to work:
+
+ git_source(:bitbucket) do |repo_name|
+ user_name, repo_name = repo_name.split("/")
+ repo_name ||= user_name
+ "https://\#{user_name}@bitbucket.org/\#{user_name}/\#{repo_name}.git"
+ end
+
+ EOS
+ expect(Bundler::SharedHelpers).to receive(:major_deprecation).with(2, msg)
+ subject.gem("not-really-a-gem", :bitbucket => "mcorp/flatlab-rails")
+ end
+ end
+
+ context "with gist gems" do
+ it "warns about removal" do
+ allow(Bundler.ui).to receive(:deprecate)
+ msg = "The :gist git source is deprecated, and will be removed " \
+ "in Bundler 2.0. Add this code to the top of your Gemfile to ensure it " \
+ "continues to work:\n\n git_source(:gist) {|repo_name| " \
+ "\"https://gist.github.com/\#{repo_name}.git\" }\n\n"
+ expect(Bundler::SharedHelpers).to receive(:major_deprecation).with(2, msg)
+ subject.gem("not-really-a-gem", :gist => "1234")
+ end
+ end
+ end
+
+ context "bundle show" do
+ it "prints a deprecation warning" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle! :show
+
+ warnings.gsub!(/gems included.*?\[DEPRECATED/im, "[DEPRECATED")
+
+ expect(warnings).to have_major_deprecation a_string_including("use `bundle list` instead of `bundle show`")
+ end
+ end
+
+ context "bundle console" do
+ it "prints a deprecation warning" do
+ bundle "console"
+
+ expect(warnings).to have_major_deprecation \
+ a_string_including("bundle console will be replaced by `bin/console` generated by `bundle gem <name>`")
+ end
+ end
+end
diff --git a/spec/bundler/other/platform_spec.rb b/spec/bundler/other/platform_spec.rb
new file mode 100644
index 0000000000..ca74945563
--- /dev/null
+++ b/spec/bundler/other/platform_spec.rb
@@ -0,0 +1,1312 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle platform" do
+ context "without flags" do
+ let(:bundle_platform_platforms_string) do
+ platforms = [rb]
+ platforms.unshift(specific_local_platform) if Bundler.feature_flag.bundler_2_mode?
+ platforms.map {|pl| "* #{pl}" }.join("\n")
+ end
+
+ it "returns all the output" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ #{ruby_version_correct}
+
+ gem "foo"
+ G
+
+ bundle "platform"
+ expect(out).to eq(<<-G.chomp)
+Your platform is: #{RUBY_PLATFORM}
+
+Your app has gems that work on these platforms:
+#{bundle_platform_platforms_string}
+
+Your Gemfile specifies a Ruby version requirement:
+* ruby #{RUBY_VERSION}
+
+Your current platform satisfies the Ruby version requirement.
+G
+ end
+
+ it "returns all the output including the patchlevel" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ #{ruby_version_correct_patchlevel}
+
+ gem "foo"
+ G
+
+ bundle "platform"
+ expect(out).to eq(<<-G.chomp)
+Your platform is: #{RUBY_PLATFORM}
+
+Your app has gems that work on these platforms:
+#{bundle_platform_platforms_string}
+
+Your Gemfile specifies a Ruby version requirement:
+* ruby #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
+
+Your current platform satisfies the Ruby version requirement.
+G
+ end
+
+ it "doesn't print ruby version requirement if it isn't specified" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+ G
+
+ bundle "platform"
+ expect(out).to eq(<<-G.chomp)
+Your platform is: #{RUBY_PLATFORM}
+
+Your app has gems that work on these platforms:
+#{bundle_platform_platforms_string}
+
+Your Gemfile does not specify a Ruby version requirement.
+G
+ end
+
+ it "doesn't match the ruby version requirement" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ #{ruby_version_incorrect}
+
+ gem "foo"
+ G
+
+ bundle "platform"
+ expect(out).to eq(<<-G.chomp)
+Your platform is: #{RUBY_PLATFORM}
+
+Your app has gems that work on these platforms:
+#{bundle_platform_platforms_string}
+
+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}
+G
+ end
+ end
+
+ context "--ruby" do
+ it "returns ruby version when explicit" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.9.3", :engine => 'ruby', :engine_version => '1.9.3'
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("ruby 1.9.3")
+ end
+
+ it "defaults to MRI" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.9.3"
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("ruby 1.9.3")
+ end
+
+ it "handles jruby" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'jruby', :engine_version => '1.6.5'
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("ruby 1.8.7 (jruby 1.6.5)")
+ end
+
+ it "handles rbx" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'rbx', :engine_version => '1.2.4'
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("ruby 1.8.7 (rbx 1.2.4)")
+ end
+
+ it "handles truffleruby", :rubygems => ">= 2.1.0" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "2.5.1", :engine => 'truffleruby', :engine_version => '1.0.0-rc6'
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("ruby 2.5.1 (truffleruby 1.0.0-rc6)")
+ end
+
+ it "raises an error if engine is used but engine version is not" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'rbx'
+
+ gem "foo"
+ G
+
+ bundle "platform"
+
+ expect(exitstatus).not_to eq(0) if exitstatus
+ end
+
+ it "raises an error if engine_version is used but engine is not" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine_version => '1.2.4'
+
+ gem "foo"
+ G
+
+ bundle "platform"
+
+ expect(exitstatus).not_to eq(0) if exitstatus
+ end
+
+ it "raises an error if engine version doesn't match ruby version for MRI" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "1.8.7", :engine => 'ruby', :engine_version => '1.2.4'
+
+ gem "foo"
+ G
+
+ bundle "platform"
+
+ expect(exitstatus).not_to eq(0) if exitstatus
+ end
+
+ it "should print if no ruby version is specified" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("No ruby version specified")
+ end
+
+ it "handles when there is a locked requirement" do
+ gemfile <<-G
+ ruby "< 1.8.7"
+ G
+
+ lockfile <<-L
+ GEM
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+
+ RUBY VERSION
+ ruby 1.0.0p127
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle! "platform --ruby"
+ expect(out).to eq("ruby 1.0.0p127")
+ end
+
+ it "handles when there is a requirement in the gemfile" do
+ gemfile <<-G
+ ruby ">= 1.8.7"
+ G
+
+ bundle! "platform --ruby"
+ expect(out).to eq("ruby 1.8.7")
+ end
+
+ it "handles when there are multiple requirements in the gemfile" do
+ gemfile <<-G
+ ruby ">= 1.8.7", "< 2.0.0"
+ G
+
+ bundle! "platform --ruby"
+ expect(out).to eq("ruby 1.8.7")
+ 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_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(: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) if exitstatus
+ expect(out).to be_include("Your Ruby version is #{RUBY_VERSION}, but your Gemfile specified #{not_local_ruby_version}")
+ end
+
+ def should_be_engine_incorrect
+ expect(exitstatus).to eq(18) if exitstatus
+ expect(out).to be_include("Your Ruby engine is #{local_ruby_engine}, but your Gemfile specified #{not_local_tag}")
+ end
+
+ def should_be_engine_version_incorrect
+ expect(exitstatus).to eq(18) if exitstatus
+ expect(out).to be_include("Your #{local_ruby_engine} version is #{local_engine_version}, but your Gemfile specified #{local_ruby_engine} #{not_local_engine_version}")
+ end
+
+ def should_be_patchlevel_incorrect
+ expect(exitstatus).to eq(18) if exitstatus
+ expect(out).to be_include("Your Ruby patchlevel is #{RUBY_PATCHLEVEL}, but your Gemfile specified #{not_local_patchlevel}")
+ end
+
+ def should_be_patchlevel_fixnum
+ expect(exitstatus).to eq(18) if exitstatus
+ expect(out).to be_include("The Ruby patchlevel in your Gemfile must be a string")
+ end
+
+ context "bundle install" do
+ it "installs fine when the ruby version matches" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{ruby_version_correct}
+ G
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ it "installs fine with any engine" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+ end
+
+ it "installs fine when the patchlevel matches" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{ruby_version_correct_patchlevel}
+ G
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ it "doesn't install when the ruby version doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{ruby_version_incorrect}
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_ruby_version_incorrect
+ end
+
+ it "doesn't install when engine doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{engine_incorrect}
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_engine_incorrect
+ end
+
+ it "doesn't install when engine version doesn't match" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{engine_version_incorrect}
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "doesn't install when patchlevel doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle check" do
+ it "checks fine when the ruby version matches" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{ruby_version_correct}
+ G
+
+ bundle :check
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(out).to eq("Resolving dependencies...\nThe Gemfile's dependencies are satisfied")
+ end
+
+ it "checks fine with any engine" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle :check
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(out).to eq("Resolving dependencies...\nThe Gemfile's dependencies are satisfied")
+ end
+ end
+
+ it "fails when ruby version doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle :check
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when engine doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{engine_incorrect}
+ G
+
+ bundle :check
+ should_be_engine_incorrect
+ end
+
+ it "fails when engine version doesn't match" do
+ simulate_ruby_engine "ruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{engine_version_incorrect}
+ G
+
+ bundle :check
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when patchlevel doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle :check
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle update" do
+ before do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+ G
+ end
+
+ it "updates successfully when the ruby version matches" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+
+ #{ruby_version_correct}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0"
+ end
+
+ it "updates fine with any engine" do
+ simulate_ruby_engine "jruby" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+
+ #{ruby_version_correct_engineless}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0"
+ end
+ end
+
+ it "fails when ruby version doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+
+ #{ruby_version_incorrect}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle :update, :all => bundle_update_requires_all?
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when ruby engine doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+
+ #{engine_incorrect}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle :update, :all => bundle_update_requires_all?
+ should_be_engine_incorrect
+ end
+
+ it "fails when ruby engine version doesn't match" do
+ simulate_ruby_engine "jruby" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport"
+ gem "rack-obama"
+
+ #{engine_version_incorrect}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle :update, :all => bundle_update_requires_all?
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when patchlevel doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle :update, :all => bundle_update_requires_all?
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle info" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ end
+
+ it "prints path if ruby version is correct" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ #{ruby_version_correct}
+ G
+
+ bundle "info rails --path"
+ expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
+ end
+
+ it "prints path if ruby version is correct for any engine" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle "info rails --path"
+ expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
+ end
+ end
+
+ it "fails if ruby version doesn't match", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle "show rails"
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails if engine doesn't match", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ #{engine_incorrect}
+ G
+
+ bundle "show rails"
+ should_be_engine_incorrect
+ end
+
+ it "fails if engine version doesn't match", :bundler => "< 2" do
+ simulate_ruby_engine "jruby" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ #{engine_version_incorrect}
+ G
+
+ bundle "show rails"
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when patchlevel doesn't match", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle "show rails"
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle cache" do
+ before do
+ install_gemfile <<-G
+ source "file:#{gem_repo1}"
+ gem 'rack'
+ G
+ end
+
+ it "copies the .gem file to vendor/cache when ruby version matches" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{ruby_version_correct}
+ G
+
+ bundle :cache
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ end
+
+ it "copies the .gem file to vendor/cache when ruby version matches for any engine" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+ gem 'rack'
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle! :cache
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ end
+ end
+
+ it "fails if the ruby version doesn't match" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle :cache
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails if the engine doesn't match" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{engine_incorrect}
+ G
+
+ bundle :cache
+ should_be_engine_incorrect
+ end
+
+ it "fails if the engine version doesn't match" do
+ simulate_ruby_engine "jruby" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{engine_version_incorrect}
+ G
+
+ bundle :cache
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when patchlevel doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle :cache
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle pack" do
+ before do
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+ gem 'rack'
+ G
+ end
+
+ it "copies the .gem file to vendor/cache when ruby version matches" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{ruby_version_correct}
+ G
+
+ bundle :pack
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ end
+
+ it "copies the .gem file to vendor/cache when ruby version matches any engine" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+ gem 'rack'
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle :pack
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ end
+ end
+
+ it "fails if the ruby version doesn't match" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle :pack
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails if the engine doesn't match" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{engine_incorrect}
+ G
+
+ bundle :pack
+ should_be_engine_incorrect
+ end
+
+ it "fails if the engine version doesn't match" do
+ simulate_ruby_engine "jruby" do
+ gemfile <<-G
+ gem 'rack'
+
+ #{engine_version_incorrect}
+ G
+
+ bundle :pack
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when patchlevel doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle :pack
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle exec" do
+ before do
+ ENV["BUNDLER_FORCE_TTY"] = "true"
+ system_gems "rack-1.0.0", "rack-0.9.1", :path => :bundle_path
+ end
+
+ it "activates the correct gem when ruby version matches" do
+ gemfile <<-G
+ gem "rack", "0.9.1"
+
+ #{ruby_version_correct}
+ G
+
+ bundle "exec rackup"
+ expect(out).to eq("0.9.1")
+ end
+
+ it "activates the correct gem when ruby version matches any engine" do
+ simulate_ruby_engine "jruby" do
+ system_gems "rack-1.0.0", "rack-0.9.1", :path => :bundle_path
+ gemfile <<-G
+ gem "rack", "0.9.1"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle "exec rackup"
+ expect(out).to eq("0.9.1")
+ end
+ end
+
+ it "fails when the ruby version doesn't match" do
+ gemfile <<-G
+ gem "rack", "0.9.1"
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle "exec rackup"
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when the engine doesn't match" do
+ gemfile <<-G
+ gem "rack", "0.9.1"
+
+ #{engine_incorrect}
+ G
+
+ bundle "exec rackup"
+ should_be_engine_incorrect
+ end
+
+ # it "fails when the engine version doesn't match" do
+ # simulate_ruby_engine "jruby" do
+ # gemfile <<-G
+ # gem "rack", "0.9.1"
+ #
+ # #{engine_version_incorrect}
+ # G
+ #
+ # bundle "exec rackup"
+ # should_be_engine_version_incorrect
+ # end
+ # end
+
+ it "fails when patchlevel doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle "exec rackup"
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle console", :bundler => "< 2" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+ G
+ end
+
+ it "starts IRB with the default group loaded when ruby version matches" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+
+ #{ruby_version_correct}
+ G
+
+ bundle "console" do |input, _, _|
+ input.puts("puts RACK")
+ input.puts("exit")
+ end
+ expect(out).to include("0.9.1")
+ end
+
+ it "starts IRB with the default group loaded when ruby version matches any engine" do
+ simulate_ruby_engine "jruby" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle "console" do |input, _, _|
+ input.puts("puts RACK")
+ input.puts("exit")
+ end
+ expect(out).to include("0.9.1")
+ end
+ end
+
+ it "fails when ruby version doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle "console"
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when engine doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+
+ #{engine_incorrect}
+ G
+
+ bundle "console"
+ should_be_engine_incorrect
+ end
+
+ it "fails when engine version doesn't match" do
+ simulate_ruby_engine "jruby" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+
+ #{engine_version_incorrect}
+ G
+
+ bundle "console"
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when patchlevel doesn't match" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle "console"
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "Bundler.setup" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack", :group => :test
+ G
+
+ ENV["BUNDLER_FORCE_TTY"] = "true"
+ end
+
+ it "makes a Gemfile.lock if setup succeeds" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack"
+
+ #{ruby_version_correct}
+ G
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ run "1"
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ it "makes a Gemfile.lock if setup succeeds for any engine" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ run "1"
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+ end
+
+ it "fails when ruby version doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack"
+
+ #{ruby_version_incorrect}
+ G
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler/setup'
+ R
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when engine doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack"
+
+ #{engine_incorrect}
+ G
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler/setup'
+ R
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_engine_incorrect
+ end
+
+ it "fails when engine version doesn't match" do
+ simulate_ruby_engine "jruby" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack"
+
+ #{engine_version_incorrect}
+ G
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler/setup'
+ R
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when patchlevel doesn't match" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack"
+
+ #{patchlevel_incorrect}
+ G
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler/setup'
+ R
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ context "bundle outdated" do
+ before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+ G
+ end
+
+ it "returns list of outdated gems when the ruby version matches" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{ruby_version_correct}
+ G
+
+ bundle "outdated"
+ expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5")
+ expect(out).to include("foo (newest 1.0")
+ end
+
+ it "returns list of outdated gems when the ruby version matches for any engine" do
+ simulate_ruby_engine "jruby" do
+ bundle! :install
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle "outdated"
+ expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5)")
+ expect(out).to include("foo (newest 1.0")
+ end
+ end
+
+ it "fails when the ruby version doesn't match" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle "outdated"
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when the engine doesn't match" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{engine_incorrect}
+ G
+
+ bundle "outdated"
+ should_be_engine_incorrect
+ end
+
+ it "fails when the engine version doesn't match" do
+ simulate_ruby_engine "jruby" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{engine_version_incorrect}
+ G
+
+ bundle "outdated"
+ should_be_engine_version_incorrect
+ end
+ end
+
+ it "fails when the patchlevel doesn't match" do
+ simulate_ruby_engine "jruby" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle "outdated"
+ should_be_patchlevel_incorrect
+ end
+ end
+
+ it "fails when the patchlevel is a fixnum" do
+ simulate_ruby_engine "jruby" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", :path => lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{patchlevel_fixnum}
+ G
+
+ bundle "outdated"
+ should_be_patchlevel_fixnum
+ end
+ end
+ end
+end
diff --git a/spec/bundler/other/ssl_cert_spec.rb b/spec/bundler/other/ssl_cert_spec.rb
new file mode 100644
index 0000000000..6d957276fc
--- /dev/null
+++ b/spec/bundler/other/ssl_cert_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require "bundler/ssl_certs/certificate_manager"
+
+RSpec.describe "SSL Certificates", :rubygems_master do
+ hosts = %w[
+ rubygems.org
+ index.rubygems.org
+ rubygems.global.ssl.fastly.net
+ staging.rubygems.org
+ ]
+
+ hosts.each do |host|
+ it "can securely connect to #{host}", :realworld do
+ Bundler::SSLCerts::CertificateManager.new.connect_to(host)
+ end
+ end
+end
diff --git a/spec/bundler/plugins/command_spec.rb b/spec/bundler/plugins/command_spec.rb
new file mode 100644
index 0000000000..999d8b722b
--- /dev/null
+++ b/spec/bundler/plugins/command_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+RSpec.describe "command plugins" do
+ before do
+ build_repo2 do
+ build_plugin "command-mah" do |s|
+ s.write "plugins.rb", <<-RUBY
+ module Mah
+ class Plugin < Bundler::Plugin::API
+ command "mahcommand" # declares the command
+
+ def exec(command, args)
+ puts "MahHello"
+ end
+ end
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install command-mah --source file://#{gem_repo2}"
+ end
+
+ it "executes without arguments" do
+ expect(out).to include("Installed plugin command-mah")
+
+ bundle "mahcommand"
+ expect(out).to eq("MahHello")
+ end
+
+ it "accepts the arguments" do
+ build_repo2 do
+ build_plugin "the-echoer" do |s|
+ s.write "plugins.rb", <<-RUBY
+ module Resonance
+ class Echoer
+ # Another method to declare the command
+ Bundler::Plugin::API.command "echo", self
+
+ def exec(command, args)
+ puts "You gave me \#{args.join(", ")}"
+ end
+ end
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install the-echoer --source file://#{gem_repo2}"
+ expect(out).to include("Installed plugin the-echoer")
+
+ bundle "echo tacos tofu lasange"
+ expect(out).to eq("You gave me tacos, tofu, lasange")
+ end
+
+ it "raises error on redeclaration of command" do
+ build_repo2 do
+ build_plugin "copycat" do |s|
+ s.write "plugins.rb", <<-RUBY
+ module CopyCat
+ class Cheater < Bundler::Plugin::API
+ command "mahcommand", self
+
+ def exec(command, args)
+ end
+ end
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install copycat --source file://#{gem_repo2}"
+
+ expect(out).not_to include("Installed plugin copycat")
+
+ expect(out).to include("Failed to install plugin")
+
+ expect(out).to include("Command(s) `mahcommand` declared by copycat are already registered.")
+ end
+end
diff --git a/spec/bundler/plugins/hook_spec.rb b/spec/bundler/plugins/hook_spec.rb
new file mode 100644
index 0000000000..53062095e2
--- /dev/null
+++ b/spec/bundler/plugins/hook_spec.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+RSpec.describe "hook plugins" do
+ context "before-install-all hook" do
+ before do
+ build_repo2 do
+ build_plugin "before-install-all-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_INSTALL_ALL do |deps|
+ puts "gems to be installed \#{deps.map(&:name).join(", ")}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install before-install-all-plugin --source file://#{gem_repo2}"
+ end
+
+ it "runs before all rubygems are installed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rake"
+ gem "rack"
+ G
+
+ expect(out).to include "gems to be installed rake, rack"
+ end
+ end
+
+ context "before-install hook" do
+ before do
+ build_repo2 do
+ build_plugin "before-install-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_INSTALL do |spec_install|
+ puts "installing gem \#{spec_install.name}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install before-install-plugin --source file://#{gem_repo2}"
+ end
+
+ it "runs before each rubygem is installed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rake"
+ gem "rack"
+ G
+
+ expect(out).to include "installing gem rake"
+ expect(out).to include "installing gem rack"
+ end
+ end
+
+ context "after-install-all hook" do
+ before do
+ build_repo2 do
+ build_plugin "after-install-all-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_INSTALL_ALL do |deps|
+ puts "installed gems \#{deps.map(&:name).join(", ")}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install after-install-all-plugin --source file://#{gem_repo2}"
+ end
+
+ it "runs after each rubygem is installed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rake"
+ gem "rack"
+ G
+
+ expect(out).to include "installed gems rake, rack"
+ end
+ end
+
+ context "after-install hook" do
+ before do
+ build_repo2 do
+ build_plugin "after-install-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_INSTALL do |spec_install|
+ puts "installed gem \#{spec_install.name} : \#{spec_install.state}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install after-install-plugin --source file://#{gem_repo2}"
+ end
+
+ it "runs after each rubygem is installed" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rake"
+ gem "rack"
+ G
+
+ expect(out).to include "installed gem rake : installed"
+ expect(out).to include "installed gem rack : installed"
+ end
+ end
+end
diff --git a/spec/bundler/plugins/install_spec.rb b/spec/bundler/plugins/install_spec.rb
new file mode 100644
index 0000000000..9304d78062
--- /dev/null
+++ b/spec/bundler/plugins/install_spec.rb
@@ -0,0 +1,257 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundler plugin install" do
+ before do
+ build_repo2 do
+ build_plugin "foo"
+ build_plugin "kung-foo"
+ end
+ end
+
+ it "shows proper message when gem in not found in the source" do
+ bundle "plugin install no-foo --source file://#{gem_repo1}"
+
+ expect(out).to include("Could not find")
+ plugin_should_not_be_installed("no-foo")
+ end
+
+ it "installs from rubygems source" do
+ bundle "plugin install foo --source file://#{gem_repo2}"
+
+ expect(out).to include("Installed plugin foo")
+ plugin_should_be_installed("foo")
+ end
+
+ it "installs multiple plugins" do
+ bundle "plugin install foo kung-foo --source file://#{gem_repo2}"
+
+ expect(out).to include("Installed plugin foo")
+ expect(out).to include("Installed plugin kung-foo")
+
+ plugin_should_be_installed("foo", "kung-foo")
+ end
+
+ it "uses the same version for multiple plugins" do
+ update_repo2 do
+ build_plugin "foo", "1.1"
+ build_plugin "kung-foo", "1.1"
+ end
+
+ bundle "plugin install foo kung-foo --version '1.0' --source file://#{gem_repo2}"
+
+ expect(out).to include("Installing foo 1.0")
+ expect(out).to include("Installing kung-foo 1.0")
+ plugin_should_be_installed("foo", "kung-foo")
+ end
+
+ it "works with different load paths" do
+ build_repo2 do
+ build_plugin "testing" do |s|
+ s.write "plugins.rb", <<-RUBY
+ require "fubar"
+ class Test < Bundler::Plugin::API
+ command "check2"
+
+ def exec(command, args)
+ puts "mate"
+ end
+ end
+ RUBY
+ s.require_paths = %w[lib src]
+ s.write("src/fubar.rb")
+ end
+ end
+ bundle "plugin install testing --source file://#{gem_repo2}"
+
+ bundle "check2", "no-color" => false
+ expect(out).to eq("mate")
+ end
+
+ context "malformatted plugin" do
+ it "fails when plugins.rb is missing" do
+ update_repo2 do
+ build_plugin "foo", "1.1"
+ build_plugin "kung-foo", "1.1"
+ end
+
+ bundle "plugin install foo kung-foo --version '1.0' --source file://#{gem_repo2}"
+
+ expect(out).to include("Installing foo 1.0")
+ expect(out).to include("Installing kung-foo 1.0")
+ plugin_should_be_installed("foo", "kung-foo")
+
+ build_repo2 do
+ build_gem "charlie"
+ end
+
+ bundle "plugin install charlie --source file://#{gem_repo2}"
+
+ expect(out).to include("plugins.rb was not found")
+
+ expect(global_plugin_gem("charlie-1.0")).not_to be_directory
+
+ plugin_should_be_installed("foo", "kung-foo")
+ plugin_should_not_be_installed("charlie")
+ end
+
+ it "fails when plugins.rb throws exception on load" do
+ build_repo2 do
+ build_plugin "chaplin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ raise "I got you man"
+ RUBY
+ end
+ end
+
+ bundle "plugin install chaplin --source file://#{gem_repo2}"
+
+ expect(global_plugin_gem("chaplin-1.0")).not_to be_directory
+
+ plugin_should_not_be_installed("chaplin")
+ end
+ end
+
+ context "git plugins" do
+ it "installs form a git source" do
+ build_git "foo" do |s|
+ s.write "plugins.rb"
+ end
+
+ bundle "plugin install foo --git file://#{lib_path("foo-1.0")}"
+
+ expect(out).to include("Installed plugin foo")
+ plugin_should_be_installed("foo")
+ end
+ end
+
+ context "Gemfile eval" do
+ it "installs plugins listed in gemfile" do
+ gemfile <<-G
+ source 'file://#{gem_repo2}'
+ plugin 'foo'
+ gem 'rack', "1.0.0"
+ G
+
+ bundle "install"
+
+ expect(out).to include("Installed plugin foo")
+
+ expect(out).to include("Bundle complete!")
+
+ expect(the_bundle).to include_gems("rack 1.0.0")
+ plugin_should_be_installed("foo")
+ end
+
+ it "accepts plugin version" do
+ update_repo2 do
+ build_plugin "foo", "1.1.0"
+ end
+
+ install_gemfile <<-G
+ source 'file://#{gem_repo2}'
+ plugin 'foo', "1.0"
+ G
+
+ bundle "install"
+
+ expect(out).to include("Installing foo 1.0")
+
+ plugin_should_be_installed("foo")
+
+ expect(out).to include("Bundle complete!")
+ end
+
+ it "accepts git sources" do
+ build_git "ga-plugin" do |s|
+ s.write "plugins.rb"
+ end
+
+ install_gemfile <<-G
+ plugin 'ga-plugin', :git => "#{lib_path("ga-plugin-1.0")}"
+ G
+
+ expect(out).to include("Installed plugin ga-plugin")
+ plugin_should_be_installed("ga-plugin")
+ end
+ end
+
+ context "inline gemfiles" do
+ it "installs the listed plugins" do
+ code = <<-RUBY
+ require "bundler/inline"
+
+ gemfile do
+ source 'file://#{gem_repo2}'
+ plugin 'foo'
+ end
+ RUBY
+
+ ruby code
+ expect(local_plugin_gem("foo-1.0", "plugins.rb")).to exist
+ end
+ end
+
+ describe "local plugin" do
+ it "is installed when inside an app" do
+ gemfile ""
+ bundle "plugin install foo --source file://#{gem_repo2}"
+
+ plugin_should_be_installed("foo")
+ expect(local_plugin_gem("foo-1.0")).to be_directory
+ end
+
+ context "conflict with global plugin" do
+ before do
+ update_repo2 do
+ build_plugin "fubar" do |s|
+ s.write "plugins.rb", <<-RUBY
+ class Fubar < Bundler::Plugin::API
+ command "shout"
+
+ def exec(command, args)
+ puts "local_one"
+ end
+ end
+ RUBY
+ end
+ end
+
+ # inside the app
+ gemfile "source 'file://#{gem_repo2}'\nplugin 'fubar'"
+ bundle "install"
+
+ update_repo2 do
+ build_plugin "fubar", "1.1" do |s|
+ s.write "plugins.rb", <<-RUBY
+ class Fubar < Bundler::Plugin::API
+ command "shout"
+
+ def exec(command, args)
+ puts "global_one"
+ end
+ end
+ RUBY
+ end
+ end
+
+ # outside the app
+ Dir.chdir tmp
+ bundle "plugin install fubar --source file://#{gem_repo2}"
+ end
+
+ it "inside the app takes precedence over global plugin" do
+ Dir.chdir bundled_app
+
+ bundle "shout"
+ expect(out).to eq("local_one")
+ end
+
+ it "outside the app global plugin is used" do
+ Dir.chdir tmp
+
+ bundle "shout"
+ expect(out).to eq("global_one")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/plugins/source/example_spec.rb b/spec/bundler/plugins/source/example_spec.rb
new file mode 100644
index 0000000000..fd30892f63
--- /dev/null
+++ b/spec/bundler/plugins/source/example_spec.rb
@@ -0,0 +1,505 @@
+# frozen_string_literal: true
+
+RSpec.describe "real source plugins" do
+ context "with a minimal source plugin" do
+ before do
+ build_repo2 do
+ build_plugin "bundler-source-mpath" do |s|
+ s.write "plugins.rb", <<-RUBY
+ require "bundler/vendored_fileutils"
+ require "bundler-source-mpath"
+
+ class MPath < Bundler::Plugin::API
+ source "mpath"
+
+ attr_reader :path
+
+ def initialize(opts)
+ super
+
+ @path = Pathname.new options["uri"]
+ end
+
+ def fetch_gemspec_files
+ @spec_files ||= begin
+ glob = "{,*,*/*}.gemspec"
+ if installed?
+ search_path = install_path
+ else
+ search_path = path
+ end
+ Dir["\#{search_path.to_s}/\#{glob}"]
+ end
+ end
+
+ def install(spec, opts)
+ mkdir_p(install_path.parent)
+ FileUtils.cp_r(path, install_path)
+
+ spec_path = install_path.join("\#{spec.full_name}.gemspec")
+ spec_path.open("wb") {|f| f.write spec.to_ruby }
+ spec.loaded_from = spec_path.to_s
+
+ post_install(spec)
+
+ nil
+ end
+ end
+ RUBY
+ end # build_plugin
+ end
+
+ build_lib "a-path-gem"
+
+ gemfile <<-G
+ source "file://localhost#{gem_repo2}" # plugin source
+ source "#{lib_path("a-path-gem-1.0")}", :type => :mpath do
+ gem "a-path-gem"
+ end
+ G
+ end
+
+ it "installs" do
+ bundle "install"
+
+ expect(out).to include("Bundle complete!")
+
+ expect(the_bundle).to include_gems("a-path-gem 1.0")
+ end
+
+ it "writes to lock file", :bundler => "< 2" do
+ bundle "install"
+
+ lockfile_should_be <<-G
+ PLUGIN SOURCE
+ remote: #{lib_path("a-path-gem-1.0")}
+ type: mpath
+ specs:
+ a-path-gem (1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ a-path-gem!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "writes to lock file", :bundler => "2" do
+ bundle "install"
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+
+ PLUGIN SOURCE
+ remote: #{lib_path("a-path-gem-1.0")}
+ type: mpath
+ specs:
+ a-path-gem (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ a-path-gem!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "provides correct #full_gem_path" do
+ bundle "install"
+ run <<-RUBY
+ puts Bundler.rubygems.find_name('a-path-gem').first.full_gem_path
+ RUBY
+ expect(out).to eq(bundle("info a-path-gem --path"))
+ end
+
+ it "installs the gem executables" do
+ build_lib "gem-with-bin" do |s|
+ s.executables = ["foo"]
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}" # plugin source
+ source "#{lib_path("gem-with-bin-1.0")}", :type => :mpath do
+ gem "gem-with-bin"
+ end
+ G
+
+ bundle "exec foo"
+ expect(out).to eq("1.0")
+ end
+
+ describe "bundle cache/package" do
+ let(:uri_hash) { Digest(:SHA1).hexdigest(lib_path("a-path-gem-1.0").to_s) }
+ it "copies repository to vendor cache and uses it" do
+ bundle "install"
+ bundle :cache, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}")).to exist
+ expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}/.git")).not_to exist
+ expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}/.bundlecache")).to be_file
+
+ FileUtils.rm_rf lib_path("a-path-gem-1.0")
+ expect(the_bundle).to include_gems("a-path-gem 1.0")
+ end
+
+ it "copies repository to vendor cache and uses it even when installed with bundle --path" do
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+ bundle! :cache, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}")).to exist
+
+ FileUtils.rm_rf lib_path("a-path-gem-1.0")
+ expect(the_bundle).to include_gems("a-path-gem 1.0")
+ end
+
+ it "bundler package copies repository to vendor cache" do
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+ bundle! :package, forgotten_command_line_options([:all, :cache_all] => true)
+
+ expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}")).to exist
+
+ FileUtils.rm_rf lib_path("a-path-gem-1.0")
+ expect(the_bundle).to include_gems("a-path-gem 1.0")
+ end
+ end
+
+ context "with lockfile" do
+ before do
+ lockfile <<-G
+ PLUGIN SOURCE
+ remote: #{lib_path("a-path-gem-1.0")}
+ type: mpath
+ specs:
+ a-path-gem (1.0)
+
+ GEM
+ remote: file:#{gem_repo2}/
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ a-path-gem!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "installs" do
+ bundle! "install"
+
+ expect(the_bundle).to include_gems("a-path-gem 1.0")
+ end
+ end
+ end
+
+ context "with a more elaborate source plugin" do
+ before do
+ build_repo2 do
+ build_plugin "bundler-source-gitp" do |s|
+ s.write "plugins.rb", <<-RUBY
+ class SPlugin < Bundler::Plugin::API
+ source "gitp"
+
+ attr_reader :ref
+
+ def initialize(opts)
+ super
+
+ @ref = options["ref"] || options["branch"] || options["tag"] || "master"
+ @unlocked = false
+ end
+
+ def eql?(other)
+ other.is_a?(self.class) && uri == other.uri && ref == other.ref
+ end
+
+ alias_method :==, :eql?
+
+ def fetch_gemspec_files
+ @spec_files ||= begin
+ glob = "{,*,*/*}.gemspec"
+ if !cached?
+ cache_repo
+ end
+
+ if installed? && !@unlocked
+ path = install_path
+ else
+ path = cache_path
+ end
+
+ Dir["\#{path}/\#{glob}"]
+ end
+ end
+
+ def install(spec, opts)
+ mkdir_p(install_path.dirname)
+ rm_rf(install_path)
+ `git clone --no-checkout --quiet "\#{cache_path}" "\#{install_path}"`
+ Dir.chdir install_path do
+ `git reset --hard \#{revision}`
+ end
+
+ spec_path = install_path.join("\#{spec.full_name}.gemspec")
+ spec_path.open("wb") {|f| f.write spec.to_ruby }
+ spec.loaded_from = spec_path.to_s
+
+ post_install(spec)
+
+ nil
+ end
+
+ def options_to_lock
+ opts = {"revision" => revision}
+ opts["ref"] = ref if ref != "master"
+ opts
+ end
+
+ def unlock!
+ @unlocked = true
+ @revision = latest_revision
+ end
+
+ def app_cache_dirname
+ "\#{base_name}-\#{shortref_for_path(revision)}"
+ end
+
+ private
+
+ def cache_path
+ @cache_path ||= cache_dir.join("gitp", base_name)
+ end
+
+ def cache_repo
+ `git clone --quiet \#{@options["uri"]} \#{cache_path}`
+ end
+
+ def cached?
+ File.directory?(cache_path)
+ end
+
+ def locked_revision
+ options["revision"]
+ end
+
+ def revision
+ @revision ||= locked_revision || latest_revision
+ end
+
+ def latest_revision
+ if !cached? || @unlocked
+ rm_rf(cache_path)
+ cache_repo
+ end
+
+ Dir.chdir cache_path do
+ `git rev-parse --verify \#{@ref}`.strip
+ end
+ end
+
+ def base_name
+ File.basename(uri.sub(%r{^(\w+://)?([^/:]+:)?(//\w*/)?(\w*/)*}, ""), ".git")
+ end
+
+ def shortref_for_path(ref)
+ ref[0..11]
+ end
+
+ def install_path
+ @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
+ end
+ end
+
+ def installed?
+ File.directory?(install_path)
+ end
+ end
+ RUBY
+ end
+ end
+
+ build_git "ma-gitp-gem"
+
+ gemfile <<-G
+ source "file://localhost#{gem_repo2}" # plugin source
+ source "file://#{lib_path("ma-gitp-gem-1.0")}", :type => :gitp do
+ gem "ma-gitp-gem"
+ end
+ G
+ end
+
+ it "handles the source option" do
+ bundle "install"
+ expect(out).to include("Bundle complete!")
+ expect(the_bundle).to include_gems("ma-gitp-gem 1.0")
+ end
+
+ it "writes to lock file", :bundler => "< 2" do
+ revision = revision_for(lib_path("ma-gitp-gem-1.0"))
+ bundle "install"
+
+ lockfile_should_be <<-G
+ PLUGIN SOURCE
+ remote: file://#{lib_path("ma-gitp-gem-1.0")}
+ type: gitp
+ revision: #{revision}
+ specs:
+ ma-gitp-gem (1.0)
+
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ ma-gitp-gem!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "writes to lock file", :bundler => "2" do
+ revision = revision_for(lib_path("ma-gitp-gem-1.0"))
+ bundle "install"
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+
+ PLUGIN SOURCE
+ remote: file://#{lib_path("ma-gitp-gem-1.0")}
+ type: gitp
+ revision: #{revision}
+ specs:
+ ma-gitp-gem (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ma-gitp-gem!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ context "with lockfile" do
+ before do
+ revision = revision_for(lib_path("ma-gitp-gem-1.0"))
+ lockfile <<-G
+ PLUGIN SOURCE
+ remote: file://#{lib_path("ma-gitp-gem-1.0")}
+ type: gitp
+ revision: #{revision}
+ specs:
+ ma-gitp-gem (1.0)
+
+ GEM
+ remote: file:#{gem_repo2}/
+ specs:
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ ma-gitp-gem!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "installs" do
+ bundle "install"
+ expect(the_bundle).to include_gems("ma-gitp-gem 1.0")
+ end
+
+ it "uses the locked ref" do
+ update_git "ma-gitp-gem"
+ bundle "install"
+
+ run <<-RUBY
+ require 'ma-gitp-gem'
+ puts "WIN" unless defined?(MAGITPGEM_PREV_REF)
+ RUBY
+ expect(out).to eq("WIN")
+ end
+
+ it "updates the deps on bundler update" do
+ update_git "ma-gitp-gem"
+ bundle "update ma-gitp-gem"
+
+ run <<-RUBY
+ require 'ma-gitp-gem'
+ puts "WIN" if defined?(MAGITPGEM_PREV_REF)
+ RUBY
+ expect(out).to eq("WIN")
+ end
+
+ it "updates the deps on change in gemfile" do
+ update_git "ma-gitp-gem", "1.1", :path => lib_path("ma-gitp-gem-1.0"), :gemspec => true
+ gemfile <<-G
+ source "file://#{gem_repo2}" # plugin source
+ source "file://#{lib_path("ma-gitp-gem-1.0")}", :type => :gitp do
+ gem "ma-gitp-gem", "1.1"
+ end
+ G
+ bundle "install"
+
+ expect(the_bundle).to include_gems("ma-gitp-gem 1.1")
+ end
+ end
+
+ 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)
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}" # plugin source
+ source '#{lib_path("foo-1.0")}', :type => :gitp do
+ gem "foo"
+ end
+ G
+
+ bundle :cache, forgotten_command_line_options([:all, :cache_all] => true)
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.git")).not_to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.bundlecache")).to be_file
+
+ FileUtils.rm_rf lib_path("foo-1.0")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+ end
+ end
+end
diff --git a/spec/bundler/plugins/source_spec.rb b/spec/bundler/plugins/source_spec.rb
new file mode 100644
index 0000000000..543e90eb60
--- /dev/null
+++ b/spec/bundler/plugins/source_spec.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundler source plugin" do
+ describe "plugins dsl eval for #source with :type option" do
+ before do
+ update_repo2 do
+ build_plugin "bundler-source-psource" do |s|
+ s.write "plugins.rb", <<-RUBY
+ class OPSource < Bundler::Plugin::API
+ source "psource"
+ end
+ RUBY
+ end
+ end
+ end
+
+ it "installs bundler-source-* gem when no handler for source is present" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ source "file://#{lib_path("gitp")}", :type => :psource do
+ end
+ G
+
+ plugin_should_be_installed("bundler-source-psource")
+ end
+
+ it "enables the plugin to require a lib path" do
+ update_repo2 do
+ build_plugin "bundler-source-psource" do |s|
+ s.write "plugins.rb", <<-RUBY
+ require "bundler-source-psource"
+ class PSource < Bundler::Plugin::API
+ source "psource"
+ end
+ RUBY
+ end
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ source "file://#{lib_path("gitp")}", :type => :psource do
+ end
+ G
+
+ expect(out).to include("Bundle complete!")
+ end
+
+ context "with an explicit handler" do
+ before do
+ update_repo2 do
+ build_plugin "another-psource" do |s|
+ s.write "plugins.rb", <<-RUBY
+ class Cheater < Bundler::Plugin::API
+ source "psource"
+ end
+ RUBY
+ end
+ end
+ end
+
+ context "explicit presence in gemfile" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ plugin "another-psource"
+
+ source "file://#{lib_path("gitp")}", :type => :psource do
+ end
+ G
+ end
+
+ it "completes successfully" do
+ expect(out).to include("Bundle complete!")
+ end
+
+ it "installs the explicit one" do
+ plugin_should_be_installed("another-psource")
+ end
+
+ it "doesn't install the default one" do
+ plugin_should_not_be_installed("bundler-source-psource")
+ end
+ end
+
+ context "explicit default source" do
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ plugin "bundler-source-psource"
+
+ source "file://#{lib_path("gitp")}", :type => :psource do
+ end
+ G
+ end
+
+ it "completes successfully" do
+ expect(out).to include("Bundle complete!")
+ end
+
+ it "installs the default one" do
+ plugin_should_be_installed("bundler-source-psource")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/quality_spec.rb b/spec/bundler/quality_spec.rb
new file mode 100644
index 0000000000..14d6bb99b9
--- /dev/null
+++ b/spec/bundler/quality_spec.rb
@@ -0,0 +1,266 @@
+# frozen_string_literal: true
+
+if defined?(Encoding) && Encoding.default_external.name != "UTF-8"
+ # Poor man's ruby -E UTF-8, since it works on 1.8.7
+ Encoding.default_external = Encoding.find("UTF-8")
+end
+
+RSpec.describe "The library itself" do
+ def check_for_debugging_mechanisms(filename)
+ debugging_mechanisms_regex = /
+ (binding\.pry)|
+ (debugger)|
+ (sleep\s*\(?\d+)|
+ (fit\s*\(?("|\w))
+ /x
+
+ failing_lines = []
+ File.readlines(filename).each_with_index do |line, number|
+ if line =~ debugging_mechanisms_regex && !line.end_with?("# ignore quality_spec\n")
+ failing_lines << number + 1
+ end
+ end
+
+ return if failing_lines.empty?
+ "#{filename} has debugging mechanisms (like binding.pry, sleep, debugger, rspec focusing, etc.) on lines #{failing_lines.join(", ")}"
+ end
+
+ def check_for_git_merge_conflicts(filename)
+ merge_conflicts_regex = /
+ <<<<<<<|
+ =======|
+ >>>>>>>
+ /x
+
+ failing_lines = []
+ File.readlines(filename).each_with_index do |line, number|
+ failing_lines << number + 1 if line =~ merge_conflicts_regex
+ end
+
+ return if failing_lines.empty?
+ "#{filename} has unresolved git merge conflicts on lines #{failing_lines.join(", ")}"
+ end
+
+ def check_for_tab_characters(filename)
+ failing_lines = []
+ File.readlines(filename).each_with_index do |line, number|
+ failing_lines << number + 1 if line =~ /\t/
+ end
+
+ return if failing_lines.empty?
+ "#{filename} has tab characters on lines #{failing_lines.join(", ")}"
+ end
+
+ def check_for_extra_spaces(filename)
+ failing_lines = []
+ File.readlines(filename).each_with_index do |line, number|
+ next if line =~ /^\s+#.*\s+\n$/
+ next if %w[LICENCE.md].include?(line)
+ failing_lines << number + 1 if line =~ /\s+\n$/
+ end
+
+ return if failing_lines.empty?
+ "#{filename} has spaces on the EOL on lines #{failing_lines.join(", ")}"
+ end
+
+ def check_for_expendable_words(filename)
+ failing_line_message = []
+ useless_words = %w[
+ actually
+ basically
+ clearly
+ just
+ obviously
+ really
+ simply
+ ]
+ pattern = /\b#{Regexp.union(useless_words)}\b/i
+
+ File.readlines(filename).each_with_index do |line, number|
+ next unless word_found = pattern.match(line)
+ failing_line_message << "#{filename}:#{number.succ} has '#{word_found}'. Avoid using these kinds of weak modifiers."
+ end
+
+ failing_line_message unless failing_line_message.empty?
+ end
+
+ def check_for_specific_pronouns(filename)
+ failing_line_message = []
+ specific_pronouns = /\b(he|she|his|hers|him|her|himself|herself)\b/i
+
+ File.readlines(filename).each_with_index do |line, number|
+ next unless word_found = specific_pronouns.match(line)
+ failing_line_message << "#{filename}:#{number.succ} has '#{word_found}'. Use more generic pronouns in documentation."
+ end
+
+ failing_line_message unless failing_line_message.empty?
+ end
+
+ RSpec::Matchers.define :be_well_formed do
+ match(&:empty?)
+
+ failure_message do |actual|
+ actual.join("\n")
+ end
+ end
+
+ it "has no malformed whitespace" do
+ exempt = /\.gitmodules|\.marshal|fixtures|vendor|ssl_certs|LICENSE|vcr_cassettes/
+ error_messages = []
+ Dir.chdir(root) do
+ lib_files = ruby_core? ? `git ls-files -z -- lib/bundler lib/bundler.rb spec/bundler` : `git ls-files -z -- lib`
+ lib_files.split("\x0").each do |filename|
+ next if filename =~ exempt
+ error_messages << check_for_tab_characters(filename)
+ error_messages << check_for_extra_spaces(filename)
+ end
+ end
+ expect(error_messages.compact).to be_well_formed
+ end
+
+ it "does not include any leftover debugging or development mechanisms" do
+ exempt = %r{quality_spec.rb|support/helpers|vcr_cassettes|\.md|\.ronn}
+ error_messages = []
+ Dir.chdir(root) do
+ lib_files = ruby_core? ? `git ls-files -z -- lib/bundler lib/bundler.rb spec/bundler` : `git ls-files -z -- lib`
+ lib_files.split("\x0").each do |filename|
+ next if filename =~ exempt
+ error_messages << check_for_debugging_mechanisms(filename)
+ end
+ end
+ expect(error_messages.compact).to be_well_formed
+ end
+
+ it "does not include any unresolved merge conflicts" do
+ error_messages = []
+ exempt = %r{lock/lockfile_(bundler_1_)?spec|quality_spec|vcr_cassettes|\.ronn|lockfile_parser\.rb}
+ Dir.chdir(root) do
+ lib_files = ruby_core? ? `git ls-files -z -- lib/bundler lib/bundler.rb spec/bundler` : `git ls-files -z -- lib`
+ lib_files.split("\x0").each do |filename|
+ next if filename =~ exempt
+ error_messages << check_for_git_merge_conflicts(filename)
+ end
+ end
+ expect(error_messages.compact).to be_well_formed
+ end
+
+ it "maintains language quality of the documentation" do
+ included = /ronn/
+ error_messages = []
+ Dir.chdir(root) do
+ `git ls-files -z -- man`.split("\x0").each do |filename|
+ next unless filename =~ included
+ error_messages << check_for_expendable_words(filename)
+ error_messages << check_for_specific_pronouns(filename)
+ end
+ end
+ expect(error_messages.compact).to be_well_formed
+ end
+
+ it "maintains language quality of sentences used in source code" do
+ error_messages = []
+ exempt = /vendor/
+ Dir.chdir(root) do
+ lib_files = ruby_core? ? `git ls-files -z -- lib/bundler lib/bundler.rb` : `git ls-files -z -- lib`
+ lib_files.split("\x0").each do |filename|
+ next if filename =~ exempt
+ error_messages << check_for_expendable_words(filename)
+ error_messages << check_for_specific_pronouns(filename)
+ end
+ end
+ expect(error_messages.compact).to be_well_formed
+ end
+
+ it "documents all used settings" do
+ exemptions = %w[
+ auto_config_jobs
+ cache_command_is_package
+ console_command
+ deployment_means_frozen
+ forget_cli_options
+ gem.coc
+ gem.mit
+ inline
+ lockfile_uses_separate_rubygems_sources
+ use_gem_version_promoter_for_major_updates
+ viz_command
+ ]
+
+ all_settings = Hash.new {|h, k| h[k] = [] }
+ documented_settings = []
+
+ Bundler::Settings::BOOL_KEYS.each {|k| all_settings[k] << "in Bundler::Settings::BOOL_KEYS" }
+ Bundler::Settings::NUMBER_KEYS.each {|k| all_settings[k] << "in Bundler::Settings::NUMBER_KEYS" }
+ Bundler::Settings::ARRAY_KEYS.each {|k| all_settings[k] << "in Bundler::Settings::ARRAY_KEYS" }
+
+ Dir.chdir(root) do
+ key_pattern = /([a-z\._-]+)/i
+ lib_files = ruby_core? ? `git ls-files -z -- lib/bundler lib/bundler.rb` : `git ls-files -z -- lib`
+ lib_files.split("\x0").each do |filename|
+ File.readlines(filename).each_with_index do |line, number|
+ line.scan(/Bundler\.settings\[:#{key_pattern}\]/).flatten.each {|s| all_settings[s] << "referenced at `#{filename}:#{number.succ}`" }
+ end
+ end
+ documented_settings = File.read("man/bundle-config.ronn")[/LIST OF AVAILABLE KEYS.*/m].scan(/^\* `#{key_pattern}`/).flatten
+ end
+
+ documented_settings.each do |s|
+ all_settings.delete(s)
+ expect(exemptions.delete(s)).to be_nil, "setting #{s} was exempted but was actually documented"
+ end
+
+ exemptions.each do |s|
+ expect(all_settings.delete(s)).to be_truthy, "setting #{s} was exempted but unused"
+ end
+ error_messages = all_settings.map do |setting, refs|
+ "The `#{setting}` setting is undocumented\n\t- #{refs.join("\n\t- ")}\n"
+ end
+
+ expect(error_messages.sort).to be_well_formed
+
+ expect(documented_settings).to be_sorted
+ end
+
+ it "can still be built" do
+ Dir.chdir(root) do
+ begin
+ gem_command! :build, gemspec
+ if Bundler.rubygems.provides?(">= 2.4")
+ # there's no way aroudn this warning
+ last_command.stderr.sub!(/^YAML safe loading.*/, "")
+
+ # older rubygems have weird warnings, and we won't actually be using them
+ # to build the gem for releases anyways
+ expect(last_command.stderr).to be_empty, "bundler should build as a gem without warnings, but\n#{err}"
+ end
+ ensure
+ # clean up the .gem generated
+ path_prefix = ruby_core? ? "lib/" : "./"
+ FileUtils.rm("#{path_prefix}bundler-#{Bundler::VERSION}.gem")
+ end
+ end
+ end
+
+ it "does not contain any warnings" do
+ Dir.chdir(root) do
+ exclusions = %w[
+ lib/bundler/capistrano.rb
+ lib/bundler/deployment.rb
+ lib/bundler/gem_tasks.rb
+ lib/bundler/vlad.rb
+ lib/bundler/templates/gems.rb
+ ]
+ lib_files = ruby_core? ? `git ls-files -z -- lib/bundler lib/bundler.rb` : `git ls-files -z -- lib`
+ lib_files = lib_files.split("\x0").grep(/\.rb$/) - exclusions
+ lib_files.reject! {|f| f.start_with?("lib/bundler/vendor") }
+ lib_files.map! {|f| f.chomp(".rb") }
+ sys_exec!("ruby -w -Ilib") do |input, _, _|
+ lib_files.each do |f|
+ input.puts "require '#{f.sub(%r{\Alib/}, "")}'"
+ end
+ end
+
+ expect(last_command.stdboth.split("\n")).to be_well_formed
+ end
+ end
+end
diff --git a/spec/bundler/realworld/dependency_api_spec.rb b/spec/bundler/realworld/dependency_api_spec.rb
new file mode 100644
index 0000000000..13527ce5d1
--- /dev/null
+++ b/spec/bundler/realworld/dependency_api_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+RSpec.describe "gemcutter's dependency API", :realworld => true do
+ context "when Gemcutter API takes too long to respond" do
+ before do
+ require_rack
+
+ port = find_unused_port
+ @server_uri = "http://127.0.0.1:#{port}"
+
+ require File.expand_path("../../support/artifice/endpoint_timeout", __FILE__)
+ require "thread"
+ @t = Thread.new do
+ server = Rack::Server.start(:app => EndpointTimeout,
+ :Host => "0.0.0.0",
+ :Port => port,
+ :server => "webrick",
+ :AccessLog => [],
+ :Logger => Spec::SilentLogger.new)
+ server.start
+ end
+ @t.run
+
+ wait_for_server("127.0.0.1", port)
+ bundle! "config timeout 1"
+ end
+
+ after do
+ Artifice.deactivate
+ @t.kill
+ @t.join
+ end
+
+ it "times out and falls back on the modern index" do
+ install_gemfile! <<-G, :artifice => nil
+ source "#{@server_uri}"
+ gem "rack"
+ G
+
+ expect(out).to include("Fetching source index from #{@server_uri}/")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+end
diff --git a/spec/bundler/realworld/double_check_spec.rb b/spec/bundler/realworld/double_check_spec.rb
new file mode 100644
index 0000000000..94ab49ba2a
--- /dev/null
+++ b/spec/bundler/realworld/double_check_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+RSpec.describe "double checking sources", :realworld => true do
+ it "finds already-installed gems", :ruby => ">= 2.2" do
+ create_file("rails.gemspec", <<-RUBY)
+ Gem::Specification.new do |s|
+ s.name = "rails"
+ s.version = "5.1.4"
+ s.summary = ""
+ s.description = ""
+ s.author = ""
+ s.add_dependency "actionpack", "5.1.4"
+ end
+ RUBY
+
+ create_file("actionpack.gemspec", <<-RUBY)
+ Gem::Specification.new do |s|
+ s.name = "actionpack"
+ s.version = "5.1.4"
+ s.summary = ""
+ s.description = ""
+ s.author = ""
+ s.add_dependency "rack", "~> 2.0.0"
+ end
+ RUBY
+
+ cmd = <<-RUBY
+ require "bundler"
+ require #{File.expand_path("../../support/artifice/vcr.rb", __FILE__).dump}
+ require "bundler/inline"
+ gemfile(true) do
+ source "https://rubygems.org"
+ gem "rails", path: "."
+ end
+ RUBY
+
+ ruby! cmd
+ ruby! cmd
+ end
+end
diff --git a/spec/bundler/realworld/edgecases_spec.rb b/spec/bundler/realworld/edgecases_spec.rb
new file mode 100644
index 0000000000..aa60e20b8a
--- /dev/null
+++ b/spec/bundler/realworld/edgecases_spec.rb
@@ -0,0 +1,382 @@
+# frozen_string_literal: true
+
+RSpec.describe "real world edgecases", :realworld => true, :sometimes => true do
+ def rubygems_version(name, requirement)
+ ruby! <<-RUBY
+ require #{File.expand_path("../../support/artifice/vcr.rb", __FILE__).dump}
+ require "bundler"
+ require "bundler/source/rubygems/remote"
+ require "bundler/fetcher"
+ source = Bundler::Source::Rubygems::Remote.new(URI("https://rubygems.org"))
+ fetcher = Bundler::Fetcher.new(source)
+ index = fetcher.specs([#{name.dump}], nil)
+ rubygem = index.search(Gem::Dependency.new(#{name.dump}, #{requirement.dump})).last
+ if rubygem.nil?
+ raise "Could not find #{name} (#{requirement}) on rubygems.org!\n" \
+ "Found specs:\n\#{index.send(:specs).inspect}"
+ end
+ "#{name} (\#{rubygem.version})"
+ RUBY
+ end
+
+ # there is no rbx-relative-require gem that will install on 1.9
+ it "ignores extra gems with bad platforms", :ruby => "~> 1.8.7" do
+ gemfile <<-G
+ source "https://rubygems.org"
+ gem "linecache", "0.46"
+ G
+ bundle :lock
+ expect(err).to lack_errors
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ # https://github.com/bundler/bundler/issues/1202
+ it "bundle cache works with rubygems 1.3.7 and pre gems",
+ :ruby => "~> 1.8.7", :rubygems => "~> 1.3.7" do
+ install_gemfile <<-G
+ source "https://rubygems.org"
+ gem "rack", "1.3.0.beta2"
+ gem "will_paginate", "3.0.pre2"
+ G
+ bundle :cache
+ expect(out).not_to include("Removing outdated .gem files from vendor/cache")
+ end
+
+ # https://github.com/bundler/bundler/issues/1486
+ # this is a hash collision that only manifests on 1.8.7
+ it "finds the correct child versions", :ruby => "~> 1.8.7" do
+ gemfile <<-G
+ source "https://rubygems.org"
+
+ gem 'i18n', '~> 0.6.0'
+ gem 'activesupport', '~> 3.0.5'
+ gem 'activerecord', '~> 3.0.5'
+ gem 'builder', '~> 2.1.2'
+ G
+ bundle :lock
+ expect(lockfile).to include("activemodel (3.0.5)")
+ end
+
+ it "resolves dependencies correctly", :ruby => "1.9.3" do
+ gemfile <<-G
+ source "https://rubygems.org"
+
+ gem 'rails', '~> 3.0'
+ gem 'capybara', '~> 2.2.0'
+ gem 'rack-cache', '1.2.0' # last version that works on Ruby 1.9
+ G
+ bundle! :lock
+ expect(lockfile).to include(rubygems_version("rails", "~> 3.0"))
+ expect(lockfile).to include("capybara (2.2.1)")
+ end
+
+ it "installs the latest version of gxapi_rails", :ruby => "1.9.3" do
+ gemfile <<-G
+ source "https://rubygems.org"
+
+ gem "sass-rails"
+ gem "rails", "~> 3"
+ gem "gxapi_rails", "< 0.1.0" # 0.1.0 was released way after the test was written
+ gem 'rack-cache', '1.2.0' # last version that works on Ruby 1.9
+ G
+ bundle! :lock
+ expect(lockfile).to include("gxapi_rails (0.0.6)")
+ end
+
+ it "installs the latest version of i18n" do
+ gemfile <<-G
+ source "https://rubygems.org"
+
+ gem "i18n", "~> 0.6.0"
+ gem "activesupport", "~> 3.0"
+ gem "activerecord", "~> 3.0"
+ gem "builder", "~> 2.1.2"
+ G
+ bundle! :lock
+ expect(lockfile).to include(rubygems_version("i18n", "~> 0.6.0"))
+ expect(lockfile).to include(rubygems_version("activesupport", "~> 3.0"))
+ end
+
+ it "is able to update a top-level dependency when there is a conflict on a shared transitive child", :ruby => "2.1" do
+ # from https://github.com/bundler/bundler/issues/5031
+
+ gemfile <<-G
+ source "https://rubygems.org"
+ gem 'rails', '~> 4.2.7.1'
+ gem 'paperclip', '~> 5.1.0'
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ actionmailer (4.2.7.1)
+ actionpack (= 4.2.7.1)
+ actionview (= 4.2.7.1)
+ activejob (= 4.2.7.1)
+ mail (~> 2.5, >= 2.5.4)
+ rails-dom-testing (~> 1.0, >= 1.0.5)
+ actionpack (4.2.7.1)
+ actionview (= 4.2.7.1)
+ activesupport (= 4.2.7.1)
+ rack (~> 1.6)
+ rack-test (~> 0.6.2)
+ rails-dom-testing (~> 1.0, >= 1.0.5)
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
+ actionview (4.2.7.1)
+ activesupport (= 4.2.7.1)
+ builder (~> 3.1)
+ erubis (~> 2.7.0)
+ rails-dom-testing (~> 1.0, >= 1.0.5)
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
+ activejob (4.2.7.1)
+ activesupport (= 4.2.7.1)
+ globalid (>= 0.3.0)
+ activemodel (4.2.7.1)
+ activesupport (= 4.2.7.1)
+ builder (~> 3.1)
+ activerecord (4.2.7.1)
+ activemodel (= 4.2.7.1)
+ activesupport (= 4.2.7.1)
+ arel (~> 6.0)
+ activesupport (4.2.7.1)
+ i18n (~> 0.7)
+ json (~> 1.7, >= 1.7.7)
+ minitest (~> 5.1)
+ thread_safe (~> 0.3, >= 0.3.4)
+ tzinfo (~> 1.1)
+ arel (6.0.3)
+ builder (3.2.2)
+ climate_control (0.0.3)
+ activesupport (>= 3.0)
+ cocaine (0.5.8)
+ climate_control (>= 0.0.3, < 1.0)
+ concurrent-ruby (1.0.2)
+ erubis (2.7.0)
+ globalid (0.3.7)
+ activesupport (>= 4.1.0)
+ i18n (0.7.0)
+ json (1.8.3)
+ loofah (2.0.3)
+ nokogiri (>= 1.5.9)
+ mail (2.6.4)
+ mime-types (>= 1.16, < 4)
+ mime-types (3.1)
+ mime-types-data (~> 3.2015)
+ mime-types-data (3.2016.0521)
+ mimemagic (0.3.2)
+ mini_portile2 (2.1.0)
+ minitest (5.9.1)
+ nokogiri (1.6.8)
+ mini_portile2 (~> 2.1.0)
+ pkg-config (~> 1.1.7)
+ paperclip (5.1.0)
+ activemodel (>= 4.2.0)
+ activesupport (>= 4.2.0)
+ cocaine (~> 0.5.5)
+ mime-types
+ mimemagic (~> 0.3.0)
+ pkg-config (1.1.7)
+ rack (1.6.4)
+ rack-test (0.6.3)
+ rack (>= 1.0)
+ rails (4.2.7.1)
+ actionmailer (= 4.2.7.1)
+ actionpack (= 4.2.7.1)
+ actionview (= 4.2.7.1)
+ activejob (= 4.2.7.1)
+ activemodel (= 4.2.7.1)
+ activerecord (= 4.2.7.1)
+ activesupport (= 4.2.7.1)
+ bundler (>= 1.3.0, < 2.0)
+ railties (= 4.2.7.1)
+ sprockets-rails
+ rails-deprecated_sanitizer (1.0.3)
+ activesupport (>= 4.2.0.alpha)
+ rails-dom-testing (1.0.7)
+ activesupport (>= 4.2.0.beta, < 5.0)
+ nokogiri (~> 1.6.0)
+ rails-deprecated_sanitizer (>= 1.0.1)
+ rails-html-sanitizer (1.0.3)
+ loofah (~> 2.0)
+ railties (4.2.7.1)
+ actionpack (= 4.2.7.1)
+ activesupport (= 4.2.7.1)
+ rake (>= 0.8.7)
+ thor (>= 0.18.1, < 2.0)
+ rake (11.3.0)
+ sprockets (3.7.0)
+ concurrent-ruby (~> 1.0)
+ rack (> 1, < 3)
+ sprockets-rails (3.2.0)
+ actionpack (>= 4.0)
+ activesupport (>= 4.0)
+ sprockets (>= 3.0.0)
+ thor (0.19.1)
+ thread_safe (0.3.5)
+ tzinfo (1.2.2)
+ thread_safe (~> 0.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ paperclip (~> 5.1.0)
+ rails (~> 4.2.7.1)
+ L
+
+ bundle! "lock --update paperclip"
+
+ expect(lockfile).to include(rubygems_version("paperclip", "~> 5.1.0"))
+ end
+
+ # https://github.com/bundler/bundler/issues/1500
+ it "does not fail install because of gem plugins" do
+ realworld_system_gems("open_gem --version 1.4.2", "rake --version 0.9.2")
+ gemfile <<-G
+ source "https://rubygems.org"
+
+ gem 'rack', '1.0.1'
+ G
+
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+ expect(err).not_to include("Could not find rake")
+ expect(err).to lack_errors
+ end
+
+ it "checks out git repos when the lockfile is corrupted" do
+ gemfile <<-G
+ source "https://rubygems.org"
+ git_source(:github) {|repo| "https://github.com/\#{repo}.git" }
+
+ gem 'activerecord', :github => 'carlhuda/rails-bundler-test', :branch => 'master'
+ gem 'activesupport', :github => 'carlhuda/rails-bundler-test', :branch => 'master'
+ gem 'actionpack', :github => 'carlhuda/rails-bundler-test', :branch => 'master'
+ G
+
+ lockfile <<-L
+ GIT
+ remote: https://github.com/carlhuda/rails-bundler-test.git
+ revision: 369e28a87419565f1940815219ea9200474589d4
+ branch: master
+ specs:
+ actionpack (3.2.2)
+ activemodel (= 3.2.2)
+ activesupport (= 3.2.2)
+ builder (~> 3.0.0)
+ erubis (~> 2.7.0)
+ journey (~> 1.0.1)
+ rack (~> 1.4.0)
+ rack-cache (~> 1.2)
+ rack-test (~> 0.6.1)
+ sprockets (~> 2.1.2)
+ activemodel (3.2.2)
+ activesupport (= 3.2.2)
+ builder (~> 3.0.0)
+ activerecord (3.2.2)
+ activemodel (= 3.2.2)
+ activesupport (= 3.2.2)
+ arel (~> 3.0.2)
+ tzinfo (~> 0.3.29)
+ activesupport (3.2.2)
+ i18n (~> 0.6)
+ multi_json (~> 1.0)
+
+ GIT
+ remote: https://github.com/carlhuda/rails-bundler-test.git
+ revision: 369e28a87419565f1940815219ea9200474589d4
+ branch: master
+ specs:
+ actionpack (3.2.2)
+ activemodel (= 3.2.2)
+ activesupport (= 3.2.2)
+ builder (~> 3.0.0)
+ erubis (~> 2.7.0)
+ journey (~> 1.0.1)
+ rack (~> 1.4.0)
+ rack-cache (~> 1.2)
+ rack-test (~> 0.6.1)
+ sprockets (~> 2.1.2)
+ activemodel (3.2.2)
+ activesupport (= 3.2.2)
+ builder (~> 3.0.0)
+ activerecord (3.2.2)
+ activemodel (= 3.2.2)
+ activesupport (= 3.2.2)
+ arel (~> 3.0.2)
+ tzinfo (~> 0.3.29)
+ activesupport (3.2.2)
+ i18n (~> 0.6)
+ multi_json (~> 1.0)
+
+ GIT
+ remote: https://github.com/carlhuda/rails-bundler-test.git
+ revision: 369e28a87419565f1940815219ea9200474589d4
+ branch: master
+ specs:
+ actionpack (3.2.2)
+ activemodel (= 3.2.2)
+ activesupport (= 3.2.2)
+ builder (~> 3.0.0)
+ erubis (~> 2.7.0)
+ journey (~> 1.0.1)
+ rack (~> 1.4.0)
+ rack-cache (~> 1.2)
+ rack-test (~> 0.6.1)
+ sprockets (~> 2.1.2)
+ activemodel (3.2.2)
+ activesupport (= 3.2.2)
+ builder (~> 3.0.0)
+ activerecord (3.2.2)
+ activemodel (= 3.2.2)
+ activesupport (= 3.2.2)
+ arel (~> 3.0.2)
+ tzinfo (~> 0.3.29)
+ activesupport (3.2.2)
+ i18n (~> 0.6)
+ multi_json (~> 1.0)
+
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ arel (3.0.2)
+ builder (3.0.0)
+ erubis (2.7.0)
+ hike (1.2.1)
+ i18n (0.6.0)
+ journey (1.0.3)
+ multi_json (1.1.0)
+ rack (1.4.1)
+ rack-cache (1.2)
+ rack (>= 0.4)
+ rack-test (0.6.1)
+ rack (>= 1.0)
+ sprockets (2.1.2)
+ hike (~> 1.2)
+ rack (~> 1.0)
+ tilt (~> 1.1, != 1.3.0)
+ tilt (1.3.3)
+ tzinfo (0.3.32)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ actionpack!
+ activerecord!
+ activesupport!
+ L
+
+ bundle! :lock
+ expect(last_command.stderr).to lack_errors
+ end
+
+ it "outputs a helpful error message when gems have invalid gemspecs" do
+ install_gemfile <<-G, :standalone => true
+ source 'https://rubygems.org'
+ gem "resque-scheduler", "2.2.0"
+ G
+ expect(out).to include("You have one or more invalid gemspecs that need to be fixed.")
+ expect(out).to include("resque-scheduler 2.2.0 has an invalid gemspec")
+ end
+end
diff --git a/spec/bundler/realworld/gemfile_source_header_spec.rb b/spec/bundler/realworld/gemfile_source_header_spec.rb
new file mode 100644
index 0000000000..59c1916874
--- /dev/null
+++ b/spec/bundler/realworld/gemfile_source_header_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require "thread"
+
+RSpec.describe "fetching dependencies with a mirrored source", :realworld => true, :rubygems => ">= 2.0" do
+ let(:mirror) { "https://server.example.org" }
+ let(:original) { "http://127.0.0.1:#{@port}" }
+
+ before do
+ setup_server
+ bundle "config --local mirror.#{mirror} #{original}"
+ end
+
+ after do
+ Artifice.deactivate
+ @t.kill
+ @t.join
+ end
+
+ it "sets the 'X-Gemfile-Source' header and bundles successfully" do
+ gemfile <<-G
+ source "#{mirror}"
+ gem 'weakling'
+ G
+
+ bundle :install, :artifice => nil
+
+ expect(out).to include("Installing weakling")
+ expect(out).to include("Bundle complete")
+ expect(the_bundle).to include_gems "weakling 0.0.3"
+ end
+
+ private
+
+ def setup_server
+ require_rack
+ @port = find_unused_port
+ @server_uri = "http://127.0.0.1:#{@port}"
+
+ require File.expand_path("../../support/artifice/endpoint_mirror_source", __FILE__)
+
+ @t = Thread.new do
+ Rack::Server.start(:app => EndpointMirrorSource,
+ :Host => "0.0.0.0",
+ :Port => @port,
+ :server => "webrick",
+ :AccessLog => [],
+ :Logger => Spec::SilentLogger.new)
+ end.run
+
+ wait_for_server("127.0.0.1", @port)
+ end
+end
diff --git a/spec/bundler/realworld/mirror_probe_spec.rb b/spec/bundler/realworld/mirror_probe_spec.rb
new file mode 100644
index 0000000000..ab74886329
--- /dev/null
+++ b/spec/bundler/realworld/mirror_probe_spec.rb
@@ -0,0 +1,144 @@
+# frozen_string_literal: true
+
+require "thread"
+
+RSpec.describe "fetching dependencies with a not available mirror", :realworld => true do
+ let(:mirror) { @mirror_uri }
+ let(:original) { @server_uri }
+ let(:server_port) { @server_port }
+ let(:host) { "127.0.0.1" }
+
+ before do
+ require_rack
+ setup_server
+ setup_mirror
+ end
+
+ after do
+ Artifice.deactivate
+ @server_thread.kill
+ @server_thread.join
+ end
+
+ context "with a specific fallback timeout" do
+ before do
+ global_config("BUNDLE_MIRROR__HTTP://127__0__0__1:#{server_port}/__FALLBACK_TIMEOUT/" => "true",
+ "BUNDLE_MIRROR__HTTP://127__0__0__1:#{server_port}/" => mirror)
+ end
+
+ it "install a gem using the original uri when the mirror is not responding" do
+ gemfile <<-G
+ source "#{original}"
+ gem 'weakling'
+ G
+
+ bundle :install, :artifice => nil
+
+ expect(out).to include("Installing weakling")
+ expect(out).to include("Bundle complete")
+ expect(the_bundle).to include_gems "weakling 0.0.3"
+ end
+ end
+
+ context "with a global fallback timeout" do
+ before do
+ global_config("BUNDLE_MIRROR__ALL__FALLBACK_TIMEOUT/" => "1",
+ "BUNDLE_MIRROR__ALL" => mirror)
+ end
+
+ it "install a gem using the original uri when the mirror is not responding" do
+ gemfile <<-G
+ source "#{original}"
+ gem 'weakling'
+ G
+
+ bundle :install, :artifice => nil
+
+ expect(out).to include("Installing weakling")
+ expect(out).to include("Bundle complete")
+ expect(the_bundle).to include_gems "weakling 0.0.3"
+ end
+ end
+
+ context "with a specific mirror without a fallback timeout" do
+ before do
+ global_config("BUNDLE_MIRROR__HTTP://127__0__0__1:#{server_port}/" => mirror)
+ end
+
+ it "fails to install the gem with a timeout error" do
+ gemfile <<-G
+ source "#{original}"
+ gem 'weakling'
+ G
+
+ bundle :install, :artifice => nil
+
+ expect(out).to include("Fetching source index from #{mirror}")
+ expect(out).to include("Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
+ expect(out).to include("Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
+ expect(out).to include("Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
+ expect(out).to include("Could not fetch specs from #{mirror}")
+ end
+
+ it "prints each error and warning on a new line" do
+ gemfile <<-G
+ source "#{original}"
+ gem 'weakling'
+ G
+
+ bundle :install, :artifice => nil
+
+ expect(last_command.stdout).to include "Fetching source index from #{mirror}/"
+ expect(last_command.bundler_err).to include <<-EOS.strip
+Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from #{mirror}/
+Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from #{mirror}/
+Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from #{mirror}/
+Could not fetch specs from #{mirror}/
+ EOS
+ end
+ end
+
+ context "with a global mirror without a fallback timeout" do
+ before do
+ global_config("BUNDLE_MIRROR__ALL" => mirror)
+ end
+
+ it "fails to install the gem with a timeout error" do
+ gemfile <<-G
+ source "#{original}"
+ gem 'weakling'
+ G
+
+ bundle :install, :artifice => nil
+
+ expect(out).to include("Fetching source index from #{mirror}")
+ expect(out).to include("Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
+ expect(out).to include("Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
+ expect(out).to include("Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
+ expect(out).to include("Could not fetch specs from #{mirror}")
+ end
+ end
+
+ def setup_server
+ @server_port = find_unused_port
+ @server_uri = "http://#{host}:#{@server_port}"
+
+ require File.expand_path("../../support/artifice/endpoint", __FILE__)
+
+ @server_thread = Thread.new do
+ Rack::Server.start(:app => Endpoint,
+ :Host => host,
+ :Port => @server_port,
+ :server => "webrick",
+ :AccessLog => [],
+ :Logger => Spec::SilentLogger.new)
+ end.run
+
+ wait_for_server(host, @server_port)
+ end
+
+ def setup_mirror
+ mirror_port = find_unused_port
+ @mirror_uri = "http://#{host}:#{mirror_port}"
+ end
+end
diff --git a/spec/bundler/realworld/parallel_spec.rb b/spec/bundler/realworld/parallel_spec.rb
new file mode 100644
index 0000000000..ed4430c68b
--- /dev/null
+++ b/spec/bundler/realworld/parallel_spec.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+RSpec.describe "parallel", :realworld => true, :sometimes => true do
+ it "installs" do
+ gemfile <<-G
+ source "https://rubygems.org"
+ gem 'activesupport', '~> 3.2.13'
+ gem 'faker', '~> 1.1.2'
+ gem 'i18n', '~> 0.6.0' # Because 0.7+ requires Ruby 1.9.3+
+ G
+
+ bundle :install, :jobs => 4, :env => { "DEBUG" => "1" }
+
+ if Bundler.rubygems.provides?(">= 2.1.0")
+ expect(out).to match(/[1-3]: /)
+ else
+ expect(out).to include("is not threadsafe")
+ end
+
+ bundle "info activesupport --path"
+ expect(out).to match(/activesupport/)
+
+ bundle "info faker --path"
+ expect(out).to match(/faker/)
+ end
+
+ it "updates" do
+ install_gemfile <<-G
+ source "https://rubygems.org"
+ gem 'activesupport', '3.2.12'
+ gem 'faker', '~> 1.1.2'
+ G
+
+ gemfile <<-G
+ source "https://rubygems.org"
+ gem 'activesupport', '~> 3.2.12'
+ gem 'faker', '~> 1.1.2'
+ gem 'i18n', '~> 0.6.0' # Because 0.7+ requires Ruby 1.9.3+
+ G
+
+ bundle :update, :jobs => 4, :env => { "DEBUG" => "1" }, :all => bundle_update_requires_all?
+
+ if Bundler.rubygems.provides?(">= 2.1.0")
+ expect(out).to match(/[1-3]: /)
+ else
+ expect(out).to include("is not threadsafe")
+ end
+
+ bundle "info activesupport --path"
+ expect(out).to match(/activesupport-3\.2\.\d+/)
+
+ bundle "info faker --path"
+ expect(out).to match(/faker/)
+ end
+
+ it "works with --standalone" do
+ gemfile <<-G, :standalone => true
+ source "https://rubygems.org"
+ gem "diff-lcs"
+ G
+
+ bundle :install, :standalone => true, :jobs => 4
+
+ ruby <<-RUBY, :no_lib => true
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ require "diff/lcs"
+ puts Diff::LCS
+ RUBY
+
+ expect(out).to eq("Diff::LCS")
+ end
+end
diff --git a/spec/bundler/resolver/basic_spec.rb b/spec/bundler/resolver/basic_spec.rb
new file mode 100644
index 0000000000..c023f5d7aa
--- /dev/null
+++ b/spec/bundler/resolver/basic_spec.rb
@@ -0,0 +1,308 @@
+# frozen_string_literal: true
+
+RSpec.describe "Resolving" do
+ before :each do
+ @index = an_awesome_index
+ end
+
+ it "resolves a single gem" do
+ dep "rack"
+
+ should_resolve_as %w[rack-1.1]
+ end
+
+ it "resolves a gem with dependencies" do
+ dep "actionpack"
+
+ should_resolve_as %w[actionpack-2.3.5 activesupport-2.3.5 rack-1.0]
+ end
+
+ it "resolves a conflicting index" do
+ @index = a_conflict_index
+ dep "my_app"
+ should_resolve_as %w[activemodel-3.2.11 builder-3.0.4 grape-0.2.6 my_app-1.0.0]
+ end
+
+ it "resolves a complex conflicting index" do
+ @index = a_complex_conflict_index
+ dep "my_app"
+ should_resolve_as %w[a-1.4.0 b-0.3.5 c-3.2 d-0.9.8 my_app-1.1.0]
+ end
+
+ it "resolves a index with conflict on child" do
+ @index = index_with_conflict_on_child
+ dep "chef_app"
+ should_resolve_as %w[berkshelf-2.0.7 chef-10.26 chef_app-1.0.0 json-1.7.7]
+ end
+
+ it "prefers expicitly requested dependencies when resolving an index which would otherwise be ambiguous" do
+ @index = an_ambiguous_index
+ dep "a"
+ dep "b"
+ should_resolve_as %w[a-1.0.0 b-2.0.0 c-1.0.0 d-1.0.0]
+ end
+
+ it "prefers non-prerelease resolutions in sort order" do
+ @index = optional_prereleases_index
+ dep "a"
+ dep "b"
+ should_resolve_as %w[a-1.0.0 b-1.5.0]
+ end
+
+ it "resolves a index with root level conflict on child" do
+ @index = a_index_with_root_conflict_on_child
+ dep "i18n", "~> 0.4"
+ dep "activesupport", "~> 3.0"
+ dep "activerecord", "~> 3.0"
+ dep "builder", "~> 2.1.2"
+ should_resolve_as %w[activesupport-3.0.5 i18n-0.4.2 builder-2.1.2 activerecord-3.0.5 activemodel-3.0.5]
+ end
+
+ it "resolves a gem specified with a pre-release version" do
+ dep "activesupport", "~> 3.0.0.beta"
+ dep "activemerchant"
+ should_resolve_as %w[activemerchant-2.3.5 activesupport-3.0.0.beta1]
+ end
+
+ it "doesn't select a pre-release if not specified in the Gemfile" do
+ dep "activesupport"
+ dep "reform"
+ should_resolve_as %w[reform-1.0.0 activesupport-2.3.5]
+ end
+
+ it "doesn't select a pre-release for sub-dependencies" do
+ dep "reform"
+ should_resolve_as %w[reform-1.0.0 activesupport-2.3.5]
+ end
+
+ it "selects a pre-release for sub-dependencies if it's the only option" do
+ dep "need-pre"
+ should_resolve_as %w[need-pre-1.0.0 activesupport-3.0.0.beta1]
+ end
+
+ it "selects a pre-release if it's specified in the Gemfile" do
+ dep "activesupport", "= 3.0.0.beta"
+ dep "actionpack"
+
+ should_resolve_as %w[activesupport-3.0.0.beta actionpack-3.0.0.beta rack-1.1 rack-mount-0.6]
+ end
+
+ it "prefers non-pre-releases when doing conservative updates" do
+ @index = build_index do
+ gem "mail", "2.7.0"
+ gem "mail", "2.7.1.rc1"
+ gem "RubyGems\0", Gem::VERSION
+ end
+ dep "mail"
+ @locked = locked ["mail", "2.7.0"]
+ @base = locked
+ should_conservative_resolve_and_include [:patch], [], ["mail-2.7.0"]
+ end
+
+ it "raises an exception if a child dependency is not resolved" do
+ @index = a_unresovable_child_index
+ dep "chef_app_error"
+ expect do
+ resolve
+ end.to raise_error(Bundler::VersionConflict)
+ end
+
+ it "raises an exception with the minimal set of conflicting dependencies" do
+ @index = build_index do
+ %w[0.9 1.0 2.0].each {|v| gem("a", v) }
+ gem("b", "1.0") { dep "a", ">= 2" }
+ gem("c", "1.0") { dep "a", "< 1" }
+ end
+ dep "a"
+ dep "b"
+ 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)
+ E
+ end
+
+ it "should throw error in case of circular dependencies" do
+ @index = a_circular_index
+ dep "circular_app"
+
+ expect do
+ resolve
+ end.to raise_error(Bundler::CyclicDependencyError, /please remove either gem 'bar' or gem 'foo'/i)
+ end
+
+ # Issue #3459
+ it "should install the latest possible version of a direct requirement with no constraints given" do
+ @index = a_complicated_index
+ dep "foo"
+ should_resolve_and_include %w[foo-3.0.5]
+ end
+
+ # Issue #3459
+ it "should install the latest possible version of a direct requirement with constraints given" do
+ @index = a_complicated_index
+ dep "foo", ">= 3.0.0"
+ should_resolve_and_include %w[foo-3.0.5]
+ end
+
+ it "takes into account required_ruby_version" do
+ @index = build_index do
+ gem "foo", "1.0.0" do
+ dep "bar", ">= 0"
+ end
+
+ gem "foo", "2.0.0" do |s|
+ dep "bar", ">= 0"
+ s.required_ruby_version = "~> 2.0.0"
+ end
+
+ gem "bar", "1.0.0"
+
+ gem "bar", "2.0.0" do |s|
+ s.required_ruby_version = "~> 2.0.0"
+ end
+
+ gem "ruby\0", "1.8.7"
+ end
+ dep "foo"
+ dep "ruby\0", "1.8.7"
+
+ deps = []
+ @deps.each do |d|
+ deps << Bundler::DepProxy.new(d, "ruby")
+ end
+
+ should_resolve_and_include %w[foo-1.0.0 bar-1.0.0], [[]]
+ end
+
+ context "conservative" do
+ before :each do
+ @index = build_index do
+ gem("foo", "1.3.7") { dep "bar", "~> 2.0" }
+ gem("foo", "1.3.8") { dep "bar", "~> 2.0" }
+ gem("foo", "1.4.3") { dep "bar", "~> 2.0" }
+ gem("foo", "1.4.4") { dep "bar", "~> 2.0" }
+ gem("foo", "1.4.5") { dep "bar", "~> 2.1" }
+ gem("foo", "1.5.0") { dep "bar", "~> 2.1" }
+ gem("foo", "1.5.1") { dep "bar", "~> 3.0" }
+ gem("foo", "2.0.0") { dep "bar", "~> 3.0" }
+ gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0]
+ end
+ dep "foo"
+
+ # base represents declared dependencies in the Gemfile that are still satisfied by the lockfile
+ @base = Bundler::SpecSet.new([])
+
+ # locked represents versions in lockfile
+ @locked = locked(%w[foo 1.4.3], %w[bar 2.0.3])
+ end
+
+ it "resolves all gems to latest patch" do
+ # strict is not set, so bar goes up a minor version due to dependency from foo 1.4.5
+ should_conservative_resolve_and_include :patch, [], %w[foo-1.4.5 bar-2.1.1]
+ end
+
+ it "resolves all gems to latest patch strict" do
+ # strict is set, so foo can only go up to 1.4.4 to avoid bar going up a minor version, and bar can go up to 2.0.5
+ should_conservative_resolve_and_include [:patch, :strict], [], %w[foo-1.4.4 bar-2.0.5]
+ end
+
+ it "resolves foo only to latest patch - same dependency case" do
+ @locked = locked(%w[foo 1.3.7], %w[bar 2.0.3])
+ # bar is locked, and the lock holds here because the dependency on bar doesn't change on the matching foo version.
+ should_conservative_resolve_and_include :patch, ["foo"], %w[foo-1.3.8 bar-2.0.3]
+ end
+
+ it "resolves foo only to latest patch - changing dependency not declared case" do
+ # foo is the only gem being requested for update, therefore bar is locked, but bar is NOT
+ # declared as a dependency in the Gemfile. In this case, locks don't apply to _changing_
+ # dependencies and since the dependency of the selected foo gem changes, the latest matching
+ # dependency of "bar", "~> 2.1" -- bar-2.1.1 -- is selected. This is not a bug and follows
+ # the long-standing documented Conservative Updating behavior of bundle install.
+ # http://bundler.io/v1.12/man/bundle-install.1.html#CONSERVATIVE-UPDATING
+ should_conservative_resolve_and_include :patch, ["foo"], %w[foo-1.4.5 bar-2.1.1]
+ end
+
+ it "resolves foo only to latest patch - changing dependency declared case" do
+ # bar is locked AND a declared dependency in the Gemfile, so it will not move, and therefore
+ # foo can only move up to 1.4.4.
+ @base << build_spec("bar", "2.0.3").first
+ should_conservative_resolve_and_include :patch, ["foo"], %w[foo-1.4.4 bar-2.0.3]
+ end
+
+ it "resolves foo only to latest patch strict" do
+ # adding strict helps solve the possibly unexpected behavior of bar changing in the prior test case,
+ # because no versions will be returned for bar ~> 2.1, so the engine falls back to ~> 2.0 (turn on
+ # debugging to see this happen).
+ should_conservative_resolve_and_include [:patch, :strict], ["foo"], %w[foo-1.4.4 bar-2.0.3]
+ end
+
+ it "resolves bar only to latest patch" do
+ # bar is locked, so foo can only go up to 1.4.4
+ should_conservative_resolve_and_include :patch, ["bar"], %w[foo-1.4.3 bar-2.0.5]
+ end
+
+ it "resolves all gems to latest minor" do
+ # strict is not set, so bar goes up a major version due to dependency from foo 1.4.5
+ should_conservative_resolve_and_include :minor, [], %w[foo-1.5.1 bar-3.0.0]
+ end
+
+ it "resolves all gems to latest minor strict" do
+ # strict is set, so foo can only go up to 1.5.0 to avoid bar going up a major version
+ should_conservative_resolve_and_include [:minor, :strict], [], %w[foo-1.5.0 bar-2.1.1]
+ end
+
+ it "resolves all gems to latest major" do
+ should_conservative_resolve_and_include :major, [], %w[foo-2.0.0 bar-3.0.0]
+ end
+
+ it "resolves all gems to latest major strict" do
+ should_conservative_resolve_and_include [:major, :strict], [], %w[foo-2.0.0 bar-3.0.0]
+ end
+
+ # Why would this happen in real life? If bar 2.2 has a bug that the author of foo wants to bypass
+ # by reverting the dependency, the author of foo could release a new gem with an older requirement.
+ context "revert to previous" do
+ before :each do
+ @index = build_index do
+ gem("foo", "1.4.3") { dep "bar", "~> 2.2" }
+ gem("foo", "1.4.4") { dep "bar", "~> 2.1.0" }
+ gem("foo", "1.5.0") { dep "bar", "~> 2.0.0" }
+ gem "bar", %w[2.0.5 2.1.1 2.2.3]
+ end
+ dep "foo"
+
+ # base represents declared dependencies in the Gemfile that are still satisfied by the lockfile
+ @base = Bundler::SpecSet.new([])
+
+ # locked represents versions in lockfile
+ @locked = locked(%w[foo 1.4.3], %w[bar 2.2.3])
+ end
+
+ it "could revert to a previous version level patch" do
+ should_conservative_resolve_and_include :patch, [], %w[foo-1.4.4 bar-2.1.1]
+ end
+
+ it "cannot revert to a previous version in strict mode level patch" do
+ # fall back to the locked resolution since strict means we can't regress either version
+ should_conservative_resolve_and_include [:patch, :strict], [], %w[foo-1.4.3 bar-2.2.3]
+ end
+
+ it "could revert to a previous version level minor" do
+ should_conservative_resolve_and_include :minor, [], %w[foo-1.5.0 bar-2.0.5]
+ end
+
+ it "cannot revert to a previous version in strict mode level minor" do
+ # fall back to the locked resolution since strict means we can't regress either version
+ should_conservative_resolve_and_include [:minor, :strict], [], %w[foo-1.4.3 bar-2.2.3]
+ end
+ end
+ end
+end
diff --git a/spec/bundler/resolver/platform_spec.rb b/spec/bundler/resolver/platform_spec.rb
new file mode 100644
index 0000000000..fee0cf1f1c
--- /dev/null
+++ b/spec/bundler/resolver/platform_spec.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+RSpec.describe "Resolving platform craziness" do
+ describe "with cross-platform gems" do
+ before :each do
+ @index = an_awesome_index
+ end
+
+ it "resolves a simple multi platform gem" do
+ dep "nokogiri"
+ platforms "ruby", "java"
+
+ should_resolve_as %w[nokogiri-1.4.2 nokogiri-1.4.2-java weakling-0.0.3]
+ end
+
+ it "doesn't pull gems that don't exist for the current platform" do
+ dep "nokogiri"
+ platforms "ruby"
+
+ should_resolve_as %w[nokogiri-1.4.2]
+ end
+
+ it "doesn't pull gems when the version is available for all requested platforms" do
+ dep "nokogiri"
+ platforms "mswin32"
+
+ should_resolve_as %w[nokogiri-1.4.2.1-x86-mswin32]
+ end
+ end
+
+ describe "with mingw32" do
+ before :each do
+ @index = build_index do
+ platforms "mingw32 mswin32 x64-mingw32" do |platform|
+ gem "thin", "1.2.7", platform
+ end
+ gem "win32-api", "1.5.1", "universal-mingw32"
+ end
+ end
+
+ it "finds mswin gems" do
+ # win32 is hardcoded to get CPU x86 in rubygems
+ platforms "mswin32"
+ dep "thin"
+ should_resolve_as %w[thin-1.2.7-x86-mswin32]
+ end
+
+ it "finds mingw gems" do
+ # mingw is _not_ hardcoded to add CPU x86 in rubygems
+ platforms "x86-mingw32"
+ dep "thin"
+ should_resolve_as %w[thin-1.2.7-mingw32]
+ end
+
+ it "finds x64-mingw gems" do
+ platforms "x64-mingw32"
+ dep "thin"
+ should_resolve_as %w[thin-1.2.7-x64-mingw32]
+ end
+
+ it "finds universal-mingw gems on x86-mingw" do
+ platform "x86-mingw32"
+ dep "win32-api"
+ should_resolve_as %w[win32-api-1.5.1-universal-mingw32]
+ end
+
+ it "finds universal-mingw gems on x64-mingw" do
+ platform "x64-mingw32"
+ dep "win32-api"
+ should_resolve_as %w[win32-api-1.5.1-universal-mingw32]
+ end
+ end
+
+ describe "with conflicting cases" do
+ before :each do
+ @index = build_index do
+ gem "foo", "1.0.0" do
+ dep "bar", ">= 0"
+ end
+
+ gem "bar", "1.0.0" do
+ dep "baz", "~> 1.0.0"
+ end
+
+ gem "bar", "1.0.0", "java" do
+ dep "baz", " ~> 1.1.0"
+ end
+
+ gem "baz", %w[1.0.0 1.1.0 1.2.0]
+ end
+ end
+
+ it "reports on the conflict" do
+ platforms "ruby", "java"
+ dep "foo"
+
+ should_conflict_on "baz"
+ end
+ end
+end
diff --git a/spec/bundler/runtime/executable_spec.rb b/spec/bundler/runtime/executable_spec.rb
new file mode 100644
index 0000000000..dcee234e15
--- /dev/null
+++ b/spec/bundler/runtime/executable_spec.rb
@@ -0,0 +1,190 @@
+# frozen_string_literal: true
+
+RSpec.describe "Running bin/* commands" do
+ before :each do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "runs the bundled command when in the bundle" do
+ bundle! "binstubs rack"
+
+ build_gem "rack", "2.0", :to_system => true do |s|
+ s.executables = "rackup"
+ end
+
+ gembin "rackup"
+ expect(out).to eq("1.0.0")
+ end
+
+ it "allows the location of the gem stubs to be specified" do
+ bundle! "binstubs rack", :path => "gbin"
+
+ expect(bundled_app("bin")).not_to exist
+ expect(bundled_app("gbin/rackup")).to exist
+
+ gembin bundled_app("gbin/rackup")
+ expect(out).to eq("1.0.0")
+ end
+
+ it "allows absolute paths as a specification of where to install bin stubs" do
+ bundle! "binstubs rack", :path => tmp("bin")
+
+ gembin tmp("bin/rackup")
+ expect(out).to eq("1.0.0")
+ end
+
+ it "uses the default ruby install name when shebang is not specified" do
+ bundle! "binstubs rack"
+ expect(File.open("bin/rackup").gets).to eq("#!/usr/bin/env #{RbConfig::CONFIG["ruby_install_name"]}\n")
+ end
+
+ it "allows the name of the shebang executable to be specified" do
+ bundle! "binstubs rack", :shebang => "ruby-foo"
+ expect(File.open("bin/rackup").gets).to eq("#!/usr/bin/env ruby-foo\n")
+ end
+
+ it "runs the bundled command when out of the bundle" do
+ bundle! "binstubs rack"
+
+ build_gem "rack", "2.0", :to_system => true do |s|
+ s.executables = "rackup"
+ end
+
+ Dir.chdir(tmp) do
+ gembin "rackup"
+ expect(out).to eq("1.0.0")
+ end
+ end
+
+ it "works with gems in path" do
+ build_lib "rack", :path => lib_path("rack") do |s|
+ s.executables = "rackup"
+ end
+
+ gemfile <<-G
+ gem "rack", :path => "#{lib_path("rack")}"
+ G
+
+ bundle! "binstubs rack"
+
+ build_gem "rack", "2.0", :to_system => true do |s|
+ s.executables = "rackup"
+ end
+
+ gembin "rackup"
+ expect(out).to eq("1.0")
+ end
+
+ it "creates a bundle binstub" do
+ build_gem "bundler", Bundler::VERSION, :to_system => true do |s|
+ s.executables = "bundle"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bundler"
+ G
+
+ bundle! "binstubs bundler"
+
+ expect(bundled_app("bin/bundle")).to exist
+ end
+
+ it "does not generate bin stubs if the option was not specified" do
+ bundle! "install"
+
+ expect(bundled_app("bin/rackup")).not_to exist
+ end
+
+ it "allows you to stop installing binstubs", :bundler => "< 2" do
+ bundle! "install --binstubs bin/"
+ bundled_app("bin/rackup").rmtree
+ bundle! "install --binstubs \"\""
+
+ expect(bundled_app("bin/rackup")).not_to exist
+
+ bundle! "config bin"
+ expect(out).to include("You have not configured a value for `bin`")
+ end
+
+ it "remembers that the option was specified", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ G
+
+ bundle! :install, forgotten_command_line_options([:binstubs, :bin] => "bin")
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ gem "rack"
+ G
+
+ bundle "install"
+
+ expect(bundled_app("bin/rackup")).to exist
+ end
+
+ it "rewrites bins on --binstubs (to maintain backwards compatibility)", :bundler => "< 2" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle! :install, forgotten_command_line_options([:binstubs, :bin] => "bin")
+
+ File.open(bundled_app("bin/rackup"), "wb") do |file|
+ file.print "OMG"
+ end
+
+ bundle "install"
+
+ expect(bundled_app("bin/rackup").read).to_not eq("OMG")
+ end
+
+ it "rewrites bins on binstubs (to maintain backwards compatibility)" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ create_file("bin/rackup", "OMG")
+
+ bundle! "binstubs rack"
+
+ expect(bundled_app("bin/rackup").read).to_not eq("OMG")
+ end
+
+ it "use BUNDLE_GEMFILE gemfile for binstub" do
+ # context with bin/bunlder w/ default Gemfile
+ bundle! "binstubs bundler"
+
+ # generate other Gemfile with executable gem
+ build_repo2 do
+ build_gem("bindir") {|s| s.executables = "foo" }
+ end
+
+ create_file("OtherGemfile", <<-G)
+ source "file://#{gem_repo2}"
+ gem 'bindir'
+ G
+
+ # generate binstub for executable from non default Gemfile (other then bin/bundler version)
+ ENV["BUNDLE_GEMFILE"] = "OtherGemfile"
+ bundle "install"
+ bundle! "binstubs bindir"
+
+ # remove user settings
+ ENV["BUNDLE_GEMFILE"] = nil
+
+ # run binstub for non default Gemfile
+ gembin "foo"
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(out).to eq("1.0")
+ end
+end
diff --git a/spec/bundler/runtime/gem_tasks_spec.rb b/spec/bundler/runtime/gem_tasks_spec.rb
new file mode 100644
index 0000000000..de72869dc3
--- /dev/null
+++ b/spec/bundler/runtime/gem_tasks_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+RSpec.describe "require 'bundler/gem_tasks'", :ruby_repo do
+ before :each do
+ bundled_app("foo.gemspec").open("w") do |f|
+ f.write <<-GEMSPEC
+ Gem::Specification.new do |s|
+ s.name = "foo"
+ end
+ GEMSPEC
+ end
+ bundled_app("Rakefile").open("w") do |f|
+ f.write <<-RAKEFILE
+ $:.unshift("#{bundler_path}")
+ require "bundler/gem_tasks"
+ RAKEFILE
+ end
+ end
+
+ it "includes the relevant tasks" do
+ with_gem_path_as(Spec::Path.base_system_gems.to_s) do
+ sys_exec "#{rake} -T"
+ end
+
+ expect(err).to eq("")
+ expected_tasks = [
+ "rake build",
+ "rake clean",
+ "rake clobber",
+ "rake install",
+ "rake release[remote]",
+ ]
+ tasks = out.lines.to_a.map {|s| s.split("#").first.strip }
+ expect(tasks & expected_tasks).to eq(expected_tasks)
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "adds 'pkg' to rake/clean's CLOBBER" do
+ with_gem_path_as(Spec::Path.base_system_gems.to_s) do
+ sys_exec! %(#{rake} -e 'load "Rakefile"; puts CLOBBER.inspect')
+ end
+ expect(last_command.stdout).to eq '["pkg"]'
+ end
+end
diff --git a/spec/bundler/runtime/inline_spec.rb b/spec/bundler/runtime/inline_spec.rb
new file mode 100644
index 0000000000..18ca246199
--- /dev/null
+++ b/spec/bundler/runtime/inline_spec.rb
@@ -0,0 +1,266 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundler/inline#gemfile" do
+ def script(code, options = {})
+ requires = ["bundler/inline"]
+ requires.unshift File.expand_path("../../support/artifice/" + options.delete(:artifice) + ".rb", __FILE__) if options.key?(:artifice)
+ requires = requires.map {|r| "require '#{r}'" }.join("\n")
+ @out = ruby("#{requires}\n\n" + code, options)
+ end
+
+ before :each do
+ build_lib "one", "1.0.0" do |s|
+ s.write "lib/baz.rb", "puts 'baz'"
+ s.write "lib/qux.rb", "puts 'qux'"
+ end
+
+ build_lib "two", "1.0.0" do |s|
+ s.write "lib/two.rb", "puts 'two'"
+ s.add_dependency "three", "= 1.0.0"
+ end
+
+ build_lib "three", "1.0.0" do |s|
+ s.write "lib/three.rb", "puts 'three'"
+ s.add_dependency "seven", "= 1.0.0"
+ end
+
+ build_lib "four", "1.0.0" do |s|
+ s.write "lib/four.rb", "puts 'four'"
+ end
+
+ build_lib "five", "1.0.0", :no_default => true do |s|
+ s.write "lib/mofive.rb", "puts 'five'"
+ end
+
+ build_lib "six", "1.0.0" do |s|
+ s.write "lib/six.rb", "puts 'six'"
+ end
+
+ build_lib "seven", "1.0.0" do |s|
+ s.write "lib/seven.rb", "puts 'seven'"
+ end
+
+ build_lib "eight", "1.0.0" do |s|
+ s.write "lib/eight.rb", "puts 'eight'"
+ end
+ end
+
+ it "requires the gems" do
+ script <<-RUBY
+ gemfile do
+ path "#{lib_path}" do
+ gem "two"
+ end
+ end
+ RUBY
+
+ expect(out).to eq("two")
+ expect(exitstatus).to be_zero if exitstatus
+
+ script <<-RUBY
+ gemfile do
+ path "#{lib_path}" do
+ gem "eleven"
+ end
+ end
+
+ puts "success"
+ RUBY
+
+ expect(err).to include "Could not find gem 'eleven'"
+ expect(out).not_to include "success"
+
+ script <<-RUBY
+ gemfile(true) do
+ source "file://#{gem_repo1}"
+ gem "rack"
+ end
+ RUBY
+
+ expect(out).to include("Rack's post install message")
+ expect(exitstatus).to be_zero if exitstatus
+
+ script <<-RUBY, :artifice => "endpoint"
+ gemfile(true) do
+ source "https://notaserver.com"
+ gem "activesupport", :require => true
+ end
+ RUBY
+
+ expect(out).to include("Installing activesupport")
+ err.gsub! %r{.*lib/sinatra/base\.rb:\d+: warning: constant ::Fixnum is deprecated$}, ""
+ err.strip!
+ expect(err).to lack_errors
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "lets me use my own ui object" do
+ script <<-RUBY, :artifice => "endpoint"
+ require 'bundler'
+ class MyBundlerUI < Bundler::UI::Silent
+ def confirm(msg, newline = nil)
+ puts "CONFIRMED!"
+ end
+ end
+ gemfile(true, :ui => MyBundlerUI.new) do
+ source "https://notaserver.com"
+ gem "activesupport", :require => true
+ end
+ RUBY
+
+ expect(out).to eq("CONFIRMED!\nCONFIRMED!")
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "raises an exception if passed unknown arguments" do
+ script <<-RUBY
+ gemfile(true, :arglebargle => true) do
+ path "#{lib_path}"
+ gem "two"
+ end
+
+ puts "success"
+ RUBY
+ expect(err).to include "Unknown options: arglebargle"
+ expect(out).not_to include "success"
+ end
+
+ it "does not mutate the option argument" do
+ script <<-RUBY
+ require 'bundler'
+ options = { :ui => Bundler::UI::Shell.new }
+ gemfile(false, options) do
+ path "#{lib_path}" do
+ gem "two"
+ end
+ end
+ puts "OKAY" if options.key?(:ui)
+ RUBY
+
+ expect(out).to match("OKAY")
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "installs quietly if necessary when the install option is not set" do
+ script <<-RUBY
+ gemfile do
+ source "file://#{gem_repo1}"
+ gem "rack"
+ end
+
+ puts RACK
+ RUBY
+
+ expect(out).to eq("1.0.0")
+ expect(err).to be_empty
+ expect(exitstatus).to be_zero if exitstatus
+ 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")
+ script <<-RUBY
+ gemfile do
+ gem "foo", :git => #{lib_path("foo-1.0.0").to_s.dump}
+ gem "baz", :git => #{lib_path("baz-2.0.0").to_s.dump}, :ref => #{baz_ref.dump}
+ end
+
+ puts FOO
+ puts BAZ
+ RUBY
+
+ expect(out).to eq("1.0.0\n2.0.0")
+ expect(err).to be_empty
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "allows calling gemfile twice" do
+ script <<-RUBY
+ gemfile do
+ path "#{lib_path}" do
+ gem "two"
+ end
+ end
+
+ gemfile do
+ path "#{lib_path}" do
+ gem "four"
+ end
+ end
+ RUBY
+
+ expect(out).to eq("two\nfour")
+ expect(err).to be_empty
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "installs inline gems when a Gemfile.lock is present" do
+ gemfile <<-G
+ source "https://notaserver.com"
+ gem "rake"
+ G
+
+ lockfile <<-G
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ rake (11.3.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rake
+
+ BUNDLED WITH
+ 1.13.6
+ G
+
+ in_app_root do
+ script <<-RUBY
+ gemfile do
+ source "file://#{gem_repo1}"
+ gem "rack"
+ end
+
+ puts RACK
+ RUBY
+ end
+
+ expect(err).to be_empty
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "installs inline gems when BUNDLE_GEMFILE is set to an empty string" do
+ ENV["BUNDLE_GEMFILE"] = ""
+
+ in_app_root do
+ script <<-RUBY
+ gemfile do
+ source "file://#{gem_repo1}"
+ gem "rack"
+ end
+
+ puts RACK
+ RUBY
+ end
+
+ expect(err).to be_empty
+ expect(exitstatus).to be_zero if exitstatus
+ end
+
+ it "installs inline gems when BUNDLE_BIN is set" do
+ ENV["BUNDLE_BIN"] = "/usr/local/bundle/bin"
+
+ script <<-RUBY
+ gemfile do
+ source "file://#{gem_repo1}"
+ gem "rack" # has the rackup executable
+ end
+
+ puts RACK
+ RUBY
+ expect(last_command).to be_success
+ expect(last_command.stdout).to eq "1.0.0"
+ end
+end
diff --git a/spec/bundler/runtime/load_spec.rb b/spec/bundler/runtime/load_spec.rb
new file mode 100644
index 0000000000..b74dbde3f6
--- /dev/null
+++ b/spec/bundler/runtime/load_spec.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+RSpec.describe "Bundler.load" do
+ describe "with a gemfile" do
+ before(:each) do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "provides a list of the env dependencies" do
+ expect(Bundler.load.dependencies).to have_dep("rack", ">= 0")
+ end
+
+ it "provides a list of the resolved gems" do
+ expect(Bundler.load.gems).to have_gem("rack-1.0.0", "bundler-#{Bundler::VERSION}")
+ end
+
+ it "ignores blank BUNDLE_GEMFILEs" do
+ expect do
+ ENV["BUNDLE_GEMFILE"] = ""
+ Bundler.load
+ end.not_to raise_error
+ end
+ end
+
+ describe "with a gems.rb file" do
+ before(:each) do
+ create_file "gems.rb", <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundle! :install
+ end
+
+ it "provides a list of the env dependencies" do
+ expect(Bundler.load.dependencies).to have_dep("rack", ">= 0")
+ end
+
+ it "provides a list of the resolved gems" do
+ expect(Bundler.load.gems).to have_gem("rack-1.0.0", "bundler-#{Bundler::VERSION}")
+ end
+ end
+
+ describe "without a gemfile" do
+ it "raises an exception if the default gemfile is not found" do
+ expect do
+ Bundler.load
+ end.to raise_error(Bundler::GemfileNotFound, /could not locate gemfile/i)
+ end
+
+ it "raises an exception if a specified gemfile is not found" do
+ expect do
+ ENV["BUNDLE_GEMFILE"] = "omg.rb"
+ Bundler.load
+ end.to raise_error(Bundler::GemfileNotFound, /omg\.rb/)
+ end
+
+ it "does not find a Gemfile above the testing directory" do
+ bundler_gemfile = tmp.join("../Gemfile")
+ unless File.exist?(bundler_gemfile)
+ FileUtils.touch(bundler_gemfile)
+ @remove_bundler_gemfile = true
+ end
+ begin
+ expect { Bundler.load }.to raise_error(Bundler::GemfileNotFound)
+ ensure
+ bundler_gemfile.rmtree if @remove_bundler_gemfile
+ end
+ end
+ end
+
+ describe "when called twice" do
+ it "doesn't try to load the runtime twice" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ G
+
+ ruby! <<-RUBY
+ require "bundler"
+ Bundler.setup :default
+ Bundler.require :default
+ puts RACK
+ begin
+ require "activesupport"
+ rescue LoadError
+ puts "no activesupport"
+ end
+ RUBY
+
+ expect(out.split("\n")).to eq(["1.0.0", "no activesupport"])
+ end
+ end
+
+ describe "not hurting brittle rubygems" do
+ it "does not inject #source into the generated YAML of the gem specs" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+ gem "activerecord"
+ G
+
+ Bundler.load.specs.each do |spec|
+ expect(spec.to_yaml).not_to match(/^\s+source:/)
+ expect(spec.to_yaml).not_to match(/^\s+groups:/)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/runtime/platform_spec.rb b/spec/bundler/runtime/platform_spec.rb
new file mode 100644
index 0000000000..eecf162427
--- /dev/null
+++ b/spec/bundler/runtime/platform_spec.rb
@@ -0,0 +1,150 @@
+# frozen_string_literal: true
+
+RSpec.describe "Bundler.setup with multi platform stuff" do
+ it "raises a friendly error when gems are missing locally" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (1.0)
+
+ PLATFORMS
+ #{local_tag}
+
+ DEPENDENCIES
+ rack
+ G
+
+ ruby <<-R
+ begin
+ require 'bundler'
+ Bundler.setup
+ rescue Bundler::GemNotFound => e
+ puts "WIN"
+ end
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "will resolve correctly on the current platform when the lockfile was targeted for a different one" do
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ nokogiri (1.4.2-java)
+ weakling (= 0.0.3)
+ weakling (0.0.3)
+
+ PLATFORMS
+ java
+
+ DEPENDENCIES
+ nokogiri
+ G
+
+ simulate_platform "x86-darwin-10"
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri"
+ G
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ end
+
+ it "will add the resolve for the current platform" do
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ nokogiri (1.4.2-java)
+ weakling (= 0.0.3)
+ weakling (0.0.3)
+
+ PLATFORMS
+ java
+
+ DEPENDENCIES
+ nokogiri
+ G
+
+ simulate_platform "x86-darwin-100"
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri"
+ gem "platform_specific"
+ G
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 x86-darwin-100"
+ end
+
+ it "allows specifying only-ruby-platform" do
+ simulate_platform "java"
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri"
+ gem "platform_specific"
+ G
+
+ bundle! "config force_ruby_platform true"
+
+ bundle! "install"
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 RUBY"
+ end
+
+ it "allows specifying only-ruby-platform on windows with dependency platforms" do
+ simulate_windows do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri", :platforms => [:mingw, :mswin, :x64_mingw, :jruby]
+ gem "platform_specific"
+ G
+
+ bundle! "config force_ruby_platform true"
+
+ bundle! "install"
+
+ expect(the_bundle).to include_gems "platform_specific 1.0 RUBY"
+ end
+ end
+
+ it "recovers when the lockfile is missing a platform-specific gem" do
+ build_repo2 do
+ build_gem "requires_platform_specific" do |s|
+ s.add_dependency "platform_specific"
+ end
+ end
+ simulate_windows x64_mingw do
+ lockfile <<-L
+ GEM
+ remote: file:#{gem_repo2}/
+ specs:
+ platform_specific (1.0-x86-mingw32)
+ requires_platform_specific (1.0)
+ platform_specific
+
+ PLATFORMS
+ x64-mingw32
+ x86-mingw32
+
+ DEPENDENCIES
+ requires_platform_specific
+ L
+
+ install_gemfile! <<-G, :verbose => true
+ source "file://#{gem_repo2}"
+ gem "requires_platform_specific"
+ G
+
+ expect(the_bundle).to include_gem "platform_specific 1.0 x64-mingw32"
+ end
+ end
+end
diff --git a/spec/bundler/runtime/require_spec.rb b/spec/bundler/runtime/require_spec.rb
new file mode 100644
index 0000000000..0484e38845
--- /dev/null
+++ b/spec/bundler/runtime/require_spec.rb
@@ -0,0 +1,452 @@
+# frozen_string_literal: true
+
+RSpec.describe "Bundler.require" do
+ before :each do
+ build_lib "one", "1.0.0" do |s|
+ s.write "lib/baz.rb", "puts 'baz'"
+ s.write "lib/qux.rb", "puts 'qux'"
+ end
+
+ build_lib "two", "1.0.0" do |s|
+ s.write "lib/two.rb", "puts 'two'"
+ s.add_dependency "three", "= 1.0.0"
+ end
+
+ build_lib "three", "1.0.0" do |s|
+ s.write "lib/three.rb", "puts 'three'"
+ s.add_dependency "seven", "= 1.0.0"
+ end
+
+ build_lib "four", "1.0.0" do |s|
+ s.write "lib/four.rb", "puts 'four'"
+ end
+
+ build_lib "five", "1.0.0", :no_default => true do |s|
+ s.write "lib/mofive.rb", "puts 'five'"
+ end
+
+ build_lib "six", "1.0.0" do |s|
+ s.write "lib/six.rb", "puts 'six'"
+ end
+
+ build_lib "seven", "1.0.0" do |s|
+ s.write "lib/seven.rb", "puts 'seven'"
+ end
+
+ build_lib "eight", "1.0.0" do |s|
+ s.write "lib/eight.rb", "puts 'eight'"
+ end
+
+ build_lib "nine", "1.0.0" do |s|
+ s.write "lib/nine.rb", "puts 'nine'"
+ end
+
+ build_lib "ten", "1.0.0" do |s|
+ s.write "lib/ten.rb", "puts 'ten'"
+ end
+
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "one", :group => :bar, :require => %w[baz qux]
+ gem "two"
+ gem "three", :group => :not
+ gem "four", :require => false
+ gem "five"
+ gem "six", :group => "string"
+ gem "seven", :group => :not
+ gem "eight", :require => true, :group => :require_true
+ env "BUNDLER_TEST" => "nine" do
+ gem "nine", :require => true
+ end
+ gem "ten", :install_if => lambda { ENV["BUNDLER_TEST"] == "ten" }
+ end
+ G
+ end
+
+ it "requires the gems" do
+ # default group
+ run "Bundler.require"
+ expect(out).to eq("two")
+
+ # specific group
+ run "Bundler.require(:bar)"
+ expect(out).to eq("baz\nqux")
+
+ # default and specific group
+ run "Bundler.require(:default, :bar)"
+ expect(out).to eq("baz\nqux\ntwo")
+
+ # specific group given as a string
+ run "Bundler.require('bar')"
+ expect(out).to eq("baz\nqux")
+
+ # specific group declared as a string
+ run "Bundler.require(:string)"
+ expect(out).to eq("six")
+
+ # required in resolver order instead of gemfile order
+ run("Bundler.require(:not)")
+ expect(out.split("\n").sort).to eq(%w[seven three])
+
+ # test require: true
+ run "Bundler.require(:require_true)"
+ expect(out).to eq("eight")
+ end
+
+ it "allows requiring gems with non standard names explicitly" do
+ run "Bundler.require ; require 'mofive'"
+ expect(out).to eq("two\nfive")
+ end
+
+ it "allows requiring gems which are scoped by env" do
+ ENV["BUNDLER_TEST"] = "nine"
+ run "Bundler.require"
+ expect(out).to eq("two\nnine")
+ end
+
+ it "allows requiring gems which are scoped by install_if" do
+ ENV["BUNDLER_TEST"] = "ten"
+ run "Bundler.require"
+ expect(out).to eq("two\nten")
+ end
+
+ it "raises an exception if a require is specified but the file does not exist" do
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "two", :require => 'fail'
+ end
+ G
+
+ load_error_run <<-R, "fail"
+ Bundler.require
+ R
+
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+
+ it "displays a helpful message if the required gem throws an error" do
+ build_lib "faulty", "1.0.0" do |s|
+ s.write "lib/faulty.rb", "raise RuntimeError.new(\"Gem Internal Error Message\")"
+ end
+
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "faulty"
+ end
+ G
+
+ run "Bundler.require"
+ expect(err).to match("error while trying to load the gem 'faulty'")
+ expect(err).to match("Gem Internal Error Message")
+ end
+
+ it "doesn't swallow the error when the library has an unrelated error" do
+ build_lib "loadfuuu", "1.0.0" do |s|
+ s.write "lib/loadfuuu.rb", "raise LoadError.new(\"cannot load such file -- load-bar\")"
+ end
+
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "loadfuuu"
+ end
+ G
+
+ cmd = <<-RUBY
+ begin
+ Bundler.require
+ rescue LoadError => e
+ $stderr.puts "ZOMG LOAD ERROR: \#{e.message}"
+ end
+ RUBY
+ run(cmd)
+
+ expect(err).to eq_err("ZOMG LOAD ERROR: cannot load such file -- load-bar")
+ end
+
+ describe "with namespaced gems" do
+ before :each do
+ build_lib "jquery-rails", "1.0.0" do |s|
+ s.write "lib/jquery/rails.rb", "puts 'jquery/rails'"
+ end
+ lib_path("jquery-rails-1.0.0/lib/jquery-rails.rb").rmtree
+ end
+
+ it "requires gem names that are namespaced" do
+ gemfile <<-G
+ path '#{lib_path}' do
+ gem 'jquery-rails'
+ end
+ G
+
+ run "Bundler.require"
+ expect(out).to eq("jquery/rails")
+ end
+
+ it "silently passes if the require fails" do
+ build_lib "bcrypt-ruby", "1.0.0", :no_default => true do |s|
+ s.write "lib/brcrypt.rb", "BCrypt = '1.0.0'"
+ end
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "bcrypt-ruby"
+ end
+ G
+
+ cmd = <<-RUBY
+ require 'bundler'
+ Bundler.require
+ RUBY
+ ruby(cmd)
+
+ expect(err).to lack_errors
+ end
+
+ it "does not mangle explicitly given requires" do
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem 'jquery-rails', :require => 'jquery-rails'
+ end
+ G
+
+ load_error_run <<-R, "jquery-rails"
+ Bundler.require
+ R
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+
+ it "handles the case where regex fails" do
+ build_lib "load-fuuu", "1.0.0" do |s|
+ s.write "lib/load-fuuu.rb", "raise LoadError.new(\"Could not open library 'libfuuu-1.0': libfuuu-1.0: cannot open shared object file: No such file or directory.\")"
+ end
+
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "load-fuuu"
+ end
+ G
+
+ cmd = <<-RUBY
+ begin
+ Bundler.require
+ rescue LoadError => e
+ $stderr.puts "ZOMG LOAD ERROR" if e.message.include?("Could not open library 'libfuuu-1.0'")
+ end
+ RUBY
+ run(cmd)
+
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+
+ it "doesn't swallow the error when the library has an unrelated error" do
+ build_lib "load-fuuu", "1.0.0" do |s|
+ s.write "lib/load/fuuu.rb", "raise LoadError.new(\"cannot load such file -- load-bar\")"
+ end
+ lib_path("load-fuuu-1.0.0/lib/load-fuuu.rb").rmtree
+
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "load-fuuu"
+ end
+ G
+
+ cmd = <<-RUBY
+ begin
+ Bundler.require
+ rescue LoadError => e
+ $stderr.puts "ZOMG LOAD ERROR: \#{e.message}"
+ end
+ RUBY
+ run(cmd)
+
+ expect(err).to eq_err("ZOMG LOAD ERROR: cannot load such file -- load-bar")
+ end
+ end
+
+ describe "using bundle exec" do
+ it "requires the locked gems" do
+ bundle "exec ruby -e 'Bundler.require'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to eq("two")
+
+ bundle "exec ruby -e 'Bundler.require(:bar)'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to eq("baz\nqux")
+
+ bundle "exec ruby -e 'Bundler.require(:default, :bar)'", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to eq("baz\nqux\ntwo")
+ end
+ end
+
+ describe "order" do
+ before(:each) do
+ build_lib "one", "1.0.0" do |s|
+ s.write "lib/one.rb", <<-ONE
+ if defined?(Two)
+ Two.two
+ else
+ puts "two_not_loaded"
+ end
+ puts 'one'
+ ONE
+ end
+
+ build_lib "two", "1.0.0" do |s|
+ s.write "lib/two.rb", <<-TWO
+ module Two
+ def self.two
+ puts 'module_two'
+ end
+ end
+ puts 'two'
+ TWO
+ end
+ end
+
+ it "works when the gems are in the Gemfile in the correct order" do
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "two"
+ gem "one"
+ end
+ G
+
+ run "Bundler.require"
+ expect(out).to eq("two\nmodule_two\none")
+ end
+
+ describe "a gem with different requires for different envs" do
+ before(:each) do
+ build_gem "multi_gem", :to_bundle => true do |s|
+ s.write "lib/one.rb", "puts 'ONE'"
+ s.write "lib/two.rb", "puts 'TWO'"
+ end
+
+ install_gemfile <<-G
+ gem "multi_gem", :require => "one", :group => :one
+ gem "multi_gem", :require => "two", :group => :two
+ G
+ end
+
+ it "requires both with Bundler.require(both)" do
+ run "Bundler.require(:one, :two)"
+ expect(out).to eq("ONE\nTWO")
+ end
+
+ it "requires one with Bundler.require(:one)" do
+ run "Bundler.require(:one)"
+ expect(out).to eq("ONE")
+ end
+
+ it "requires :two with Bundler.require(:two)" do
+ run "Bundler.require(:two)"
+ expect(out).to eq("TWO")
+ end
+ end
+
+ it "fails when the gems are in the Gemfile in the wrong order" do
+ gemfile <<-G
+ path "#{lib_path}" do
+ gem "one"
+ gem "two"
+ end
+ G
+
+ run "Bundler.require"
+ expect(out).to eq("two_not_loaded\none\ntwo")
+ end
+
+ describe "with busted gems" do
+ it "should be busted" do
+ build_gem "busted_require", :to_bundle => true do |s|
+ s.write "lib/busted_require.rb", "require 'no_such_file_omg'"
+ end
+
+ install_gemfile <<-G
+ gem "busted_require"
+ G
+
+ load_error_run <<-R, "no_such_file_omg"
+ Bundler.require
+ R
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+ end
+ end
+
+ it "does not load rubygems gemspecs that are used", :rubygems => ">= 2.5.2" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ run! <<-R
+ path = File.join(Gem.dir, "specifications", "rack-1.0.0.gemspec")
+ contents = File.read(path)
+ contents = contents.lines.to_a.insert(-2, "\n raise 'broken gemspec'\n").join
+ File.open(path, "w") do |f|
+ f.write contents
+ end
+ R
+
+ run! <<-R
+ Bundler.require
+ puts "WIN"
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "does not load git gemspecs that are used", :rubygems => ">= 2.5.2" do
+ build_git "foo"
+
+ install_gemfile! <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ run! <<-R
+ path = Gem.loaded_specs["foo"].loaded_from
+ contents = File.read(path)
+ contents = contents.lines.to_a.insert(-2, "\n raise 'broken gemspec'\n").join
+ File.open(path, "w") do |f|
+ f.write contents
+ end
+ R
+
+ run! <<-R
+ Bundler.require
+ puts "WIN"
+ R
+
+ expect(out).to eq("WIN")
+ end
+end
+
+RSpec.describe "Bundler.require with platform specific dependencies" do
+ it "does not require the gems that are pinned to other platforms" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ platforms :#{not_local_tag} do
+ gem "fail", :require => "omgomg"
+ end
+
+ gem "rack", "1.0.0"
+ G
+
+ run "Bundler.require"
+ expect(err).to lack_errors
+ end
+
+ it "requires gems pinned to multiple platforms, including the current one" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ platforms :#{not_local_tag}, :#{local_tag} do
+ gem "rack", :require => "rack"
+ end
+ G
+
+ run "Bundler.require; puts RACK"
+
+ expect(out).to eq("1.0.0")
+ expect(err).to lack_errors
+ end
+end
diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb
new file mode 100644
index 0000000000..646227d931
--- /dev/null
+++ b/spec/bundler/runtime/setup_spec.rb
@@ -0,0 +1,1445 @@
+# frozen_string_literal: true
+
+RSpec.describe "Bundler.setup" do
+ describe "with no arguments" do
+ it "makes all groups available" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :group => :test
+ G
+
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+
+ require 'rack'
+ puts RACK
+ RUBY
+ expect(err).to lack_errors
+ expect(out).to eq("1.0.0")
+ end
+ end
+
+ describe "when called with groups" do
+ before(:each) do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ gem "rack", :group => :test
+ G
+ end
+
+ it "doesn't make all groups available" do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup(:default)
+
+ begin
+ require 'rack'
+ rescue LoadError
+ puts "WIN"
+ end
+ RUBY
+ expect(err).to lack_errors
+ expect(out).to eq("WIN")
+ end
+
+ it "accepts string for group name" do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup(:default, 'test')
+
+ require 'rack'
+ puts RACK
+ RUBY
+ expect(err).to lack_errors
+ expect(out).to eq("1.0.0")
+ end
+
+ it "leaves all groups available if they were already" do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+ Bundler.setup(:default)
+
+ require 'rack'
+ puts RACK
+ RUBY
+ expect(err).to lack_errors
+ expect(out).to eq("1.0.0")
+ end
+
+ it "leaves :default available if setup is called twice" do
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup(:default)
+ Bundler.setup(:default, :test)
+
+ begin
+ require 'yard'
+ puts "WIN"
+ rescue LoadError
+ puts "FAIL"
+ end
+ RUBY
+ expect(err).to lack_errors
+ expect(out).to match("WIN")
+ end
+
+ it "handles multiple non-additive invocations" do
+ ruby <<-RUBY
+ require 'bundler'
+ Bundler.setup(:default, :test)
+ Bundler.setup(:default)
+ require 'rack'
+
+ puts "FAIL"
+ RUBY
+
+ expect(err).to match("rack")
+ expect(err).to match("LoadError")
+ expect(out).not_to match("FAIL")
+ end
+ end
+
+ context "load order" do
+ def clean_load_path(lp)
+ without_bundler_load_path = ruby!("puts $LOAD_PATH").split("\n")
+ lp = lp - [
+ bundler_path.to_s,
+ bundler_path.join("gems/bundler-#{Bundler::VERSION}/lib").to_s,
+ tmp("rubygems/lib").to_s,
+ root.join("../lib").expand_path.to_s,
+ ] - without_bundler_load_path
+ lp.map! {|p| p.sub(/^#{Regexp.union system_gem_path.to_s, default_bundle_path.to_s}/i, "") }
+ end
+
+ it "puts loaded gems after -I and RUBYLIB", :ruby_repo do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ ENV["RUBYOPT"] = "-Idash_i_dir"
+ ENV["RUBYLIB"] = "rubylib_dir"
+
+ ruby <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+ puts $LOAD_PATH
+ RUBY
+
+ load_path = out.split("\n")
+ rack_load_order = load_path.index {|path| path.include?("rack") }
+
+ expect(err).to eq("")
+ expect(load_path[1]).to include "dash_i_dir"
+ expect(load_path[2]).to include "rubylib_dir"
+ expect(rack_load_order).to be > 0
+ end
+
+ it "orders the load path correctly when there are dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ ruby! <<-RUBY
+ require 'rubygems'
+ require 'bundler'
+ Bundler.setup
+ puts $LOAD_PATH
+ RUBY
+
+ load_path = clean_load_path(out.split("\n"))
+
+ unless Bundler.load.specs["bundler"].empty?
+ load_path.delete_if {|path| path =~ /bundler/ }
+ end
+
+ expect(load_path).to start_with(
+ "/gems/rails-2.3.2/lib",
+ "/gems/activeresource-2.3.2/lib",
+ "/gems/activerecord-2.3.2/lib",
+ "/gems/actionpack-2.3.2/lib",
+ "/gems/actionmailer-2.3.2/lib",
+ "/gems/activesupport-2.3.2/lib",
+ "/gems/rake-10.0.2/lib"
+ )
+ end
+
+ it "falls back to order the load path alphabetically for backwards compatibility" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "weakling"
+ gem "duradura"
+ gem "terranova"
+ G
+
+ ruby! <<-RUBY
+ require 'rubygems'
+ require 'bundler/setup'
+ puts $LOAD_PATH
+ RUBY
+
+ load_path = clean_load_path(out.split("\n"))
+
+ expect(load_path).to start_with(
+ "/gems/weakling-0.0.3/lib",
+ "/gems/terranova-8/lib",
+ "/gems/duradura-7.0/lib"
+ )
+ end
+ end
+
+ it "raises if the Gemfile was not yet installed" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler'
+
+ begin
+ Bundler.setup
+ puts "FAIL"
+ rescue Bundler::GemNotFound
+ puts "WIN"
+ end
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "doesn't create a Gemfile.lock if the setup fails" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler'
+
+ Bundler.setup
+ R
+
+ expect(bundled_app("Gemfile.lock")).not_to exist
+ end
+
+ it "doesn't change the Gemfile.lock if the setup fails" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ lockfile = File.read(bundled_app("Gemfile.lock"))
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "nosuchgem", "10.0"
+ G
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler'
+
+ Bundler.setup
+ R
+
+ expect(File.read(bundled_app("Gemfile.lock"))).to eq(lockfile)
+ end
+
+ it "makes a Gemfile.lock if setup succeeds" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ File.read(bundled_app("Gemfile.lock"))
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ run "1"
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+
+ describe "$BUNDLE_GEMFILE" do
+ context "user provides an absolute path" do
+ it "uses BUNDLE_GEMFILE to locate the gemfile if present" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ gemfile bundled_app("4realz"), <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport", "2.3.5"
+ G
+
+ ENV["BUNDLE_GEMFILE"] = bundled_app("4realz").to_s
+ bundle :install
+
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+ end
+
+ context "an absolute path is not provided" do
+ it "uses BUNDLE_GEMFILE to locate the gemfile if present" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ G
+
+ bundle "install"
+ bundle "install --deployment"
+
+ ENV["BUNDLE_GEMFILE"] = "Gemfile"
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler'
+
+ begin
+ Bundler.setup
+ puts "WIN"
+ rescue ArgumentError => e
+ puts "FAIL"
+ end
+ R
+
+ expect(out).to eq("WIN")
+ end
+ end
+ end
+
+ it "prioritizes gems in BUNDLE_PATH over gems in GEM_HOME" do
+ ENV["BUNDLE_PATH"] = bundled_app(".bundle").to_s
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0.0"
+ G
+
+ build_gem "rack", "1.0", :to_system => true do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ describe "integrate with rubygems" do
+ describe "by replacing #gem" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ G
+ end
+
+ it "replaces #gem but raises when the gem is missing" do
+ run <<-R
+ begin
+ gem "activesupport"
+ puts "FAIL"
+ rescue LoadError
+ puts "WIN"
+ end
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "version_requirement is now deprecated in rubygems 1.4.0+ when gem is missing" do
+ run <<-R
+ begin
+ gem "activesupport"
+ puts "FAIL"
+ rescue LoadError
+ puts "WIN"
+ end
+ R
+
+ expect(err).to lack_errors
+ end
+
+ it "replaces #gem but raises when the version is wrong" do
+ run <<-R
+ begin
+ gem "rack", "1.0.0"
+ puts "FAIL"
+ rescue LoadError
+ puts "WIN"
+ end
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "version_requirement is now deprecated in rubygems 1.4.0+ when the version is wrong" do
+ run <<-R
+ begin
+ gem "rack", "1.0.0"
+ puts "FAIL"
+ rescue LoadError
+ puts "WIN"
+ end
+ R
+
+ expect(err).to lack_errors
+ end
+ end
+
+ describe "by hiding system gems" do
+ before :each do
+ system_gems "activesupport-2.3.5"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "yard"
+ G
+ end
+
+ it "removes system gems from Gem.source_index" do
+ run "require 'yard'"
+ expect(out).to eq("bundler-#{Bundler::VERSION}\nyard-1.0")
+ end
+
+ 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]
+ run "puts 'worked!'", :env => { "GEM_PATH" => substring }
+ expect(out).to eq("worked!")
+ end
+ end
+ end
+ end
+
+ describe "with paths" do
+ it "activates the gems in the path source" do
+ system_gems "rack-1.0.0"
+
+ build_lib "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "puts 'WIN'"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ path "#{lib_path("rack-1.0.0")}" do
+ gem "rack"
+ end
+ G
+
+ run "require 'rack'"
+ expect(out).to eq("WIN")
+ end
+ end
+
+ describe "with git" do
+ before do
+ build_git "rack", "1.0.0"
+
+ gemfile <<-G
+ gem "rack", :git => "#{lib_path("rack-1.0.0")}"
+ G
+ end
+
+ it "provides a useful exception when the git repo is not checked out yet" do
+ run "1"
+ expect(err).to match(/the git source #{lib_path('rack-1.0.0')} is not yet checked out. Please run `bundle install`/i)
+ end
+
+ it "does not hit the git binary if the lockfile is available and up to date" do
+ bundle "install"
+
+ break_git!
+
+ ruby <<-R
+ require 'rubygems'
+ require 'bundler'
+
+ begin
+ Bundler.setup
+ puts "WIN"
+ rescue Exception => e
+ puts "FAIL"
+ end
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "provides a good exception if the lockfile is unavailable" do
+ bundle "install"
+
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ break_git!
+
+ ruby <<-R
+ require "rubygems"
+ require "bundler"
+
+ begin
+ Bundler.setup
+ puts "FAIL"
+ rescue Bundler::GitError => e
+ puts e.message
+ end
+ R
+
+ run "puts 'FAIL'"
+
+ expect(err).not_to include "This is not the git you are looking for"
+ end
+
+ it "works even when the cache directory has been deleted" do
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+ FileUtils.rm_rf vendored_gems("cache")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "does not randomly change the path when specifying --path and the bundle directory becomes read only" do
+ bundle! :install, forgotten_command_line_options(:path => "vendor/bundle")
+
+ with_read_only("**/*") do
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ it "finds git gem when default bundle path becomes read only" do
+ bundle "install"
+
+ with_read_only("#{Bundler.bundle_path}/**/*") do
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+
+ describe "when specifying local override" do
+ it "explodes if given path does not exist on runtime" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle! :install
+
+ FileUtils.rm_rf(lib_path("local-rack"))
+ run "require 'rack'"
+ expect(err).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path('local-rack').to_s)} does not exist/)
+ end
+
+ it "explodes if branch is not given on runtime" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle! :install
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}"
+ G
+
+ run "require 'rack'"
+ expect(err).to match(/because :branch is not specified in Gemfile/)
+ end
+
+ it "explodes on different branches on runtime" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle! :install
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "changed"
+ G
+
+ run "require 'rack'"
+ expect(err).to match(/is using branch master but Gemfile specifies changed/)
+ end
+
+ it "explodes on refs with different branches on runtime" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "master"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "nonexistant"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ run "require 'rack'"
+ expect(err).to match(/is using branch master but Gemfile specifies nonexistant/)
+ end
+ end
+
+ describe "when excluding groups" do
+ it "doesn't change the resolve if --without is used" do
+ install_gemfile <<-G, forgotten_command_line_options(:without => :rails)
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+
+ group :rails do
+ gem "rails", "2.3.2"
+ end
+ G
+
+ install_gems "activesupport-2.3.5"
+
+ expect(the_bundle).to include_gems "activesupport 2.3.2", :groups => :default
+ end
+
+ it "remembers --without and does not bail on bare Bundler.setup" do
+ install_gemfile <<-G, forgotten_command_line_options(:without => :rails)
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+
+ group :rails do
+ gem "rails", "2.3.2"
+ end
+ G
+
+ install_gems "activesupport-2.3.5"
+
+ expect(the_bundle).to include_gems "activesupport 2.3.2"
+ end
+
+ it "remembers --without and does not include groups passed to Bundler.setup" do
+ install_gemfile <<-G, forgotten_command_line_options(:without => :rails)
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+
+ group :rack do
+ gem "rack"
+ end
+
+ group :rails do
+ gem "rails", "2.3.2"
+ end
+ G
+
+ expect(the_bundle).not_to include_gems "activesupport 2.3.2", :groups => :rack
+ expect(the_bundle).to include_gems "rack 1.0.0", :groups => :rack
+ end
+ end
+
+ # Unfortunately, gem_prelude does not record the information about
+ # activated gems, so this test cannot work on 1.9 :(
+ if RUBY_VERSION < "1.9"
+ describe "preactivated gems" do
+ it "raises an exception if a pre activated gem conflicts with the bundle" do
+ system_gems "thin-1.0", "rack-1.0.0"
+ build_gem "thin", "1.1", :to_system => true do |s|
+ s.add_dependency "rack"
+ end
+
+ gemfile <<-G
+ gem "thin", "1.0"
+ G
+
+ ruby <<-R
+ require 'rubygems'
+ gem "thin"
+ require 'bundler'
+ begin
+ Bundler.setup
+ puts "FAIL"
+ rescue Gem::LoadError => e
+ puts e.message
+ end
+ R
+
+ expect(out).to eq("You have already activated thin 1.1, but your Gemfile requires thin 1.0. Prepending `bundle exec` to your command may solve this.")
+ end
+
+ it "version_requirement is now deprecated in rubygems 1.4.0+" do
+ system_gems "thin-1.0", "rack-1.0.0"
+ build_gem "thin", "1.1", :to_system => true do |s|
+ s.add_dependency "rack"
+ end
+
+ gemfile <<-G
+ gem "thin", "1.0"
+ G
+
+ ruby <<-R
+ require 'rubygems'
+ gem "thin"
+ require 'bundler'
+ begin
+ Bundler.setup
+ puts "FAIL"
+ rescue Gem::LoadError => e
+ puts e.message
+ end
+ R
+
+ expect(err).to lack_errors
+ end
+ end
+ end
+
+ # RubyGems returns loaded_from as a string
+ it "has loaded_from as a string on all specs" do
+ build_git "foo"
+ build_git "no-gemspec", :gemspec => false
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ gem "no-gemspec", "1.0", :git => "#{lib_path("no-gemspec-1.0")}"
+ G
+
+ run <<-R
+ Gem.loaded_specs.each do |n, s|
+ puts "FAIL" unless s.loaded_from.is_a?(String)
+ end
+ R
+
+ expect(out).to be_empty
+ end
+
+ it "does not load all gemspecs", :rubygems => ">= 2.3" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ run! <<-R
+ File.open(File.join(Gem.dir, "specifications", "broken.gemspec"), "w") do |f|
+ f.write <<-RUBY
+# -*- encoding: utf-8 -*-
+# stub: broken 1.0.0 ruby lib
+
+Gem::Specification.new do |s|
+ s.name = "broken"
+ s.version = "1.0.0"
+ raise "BROKEN GEMSPEC"
+end
+ RUBY
+ end
+ R
+
+ run! <<-R
+ puts "WIN"
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "ignores empty gem paths" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ ENV["GEM_HOME"] = ""
+ bundle %(exec ruby -e "require 'set'"), :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+
+ expect(err).to lack_errors
+ end
+
+ describe "$MANPATH" do
+ before do
+ build_repo4 do
+ build_gem "with_man" do |s|
+ s.write("man/man1/page.1", "MANPAGE")
+ end
+ end
+ end
+
+ context "when the user has one set" do
+ before { ENV["MANPATH"] = "/foo:" }
+
+ it "adds the gem's man dir to the MANPATH" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ gem "with_man"
+ G
+
+ run! "puts ENV['MANPATH']"
+ expect(out).to eq("#{default_bundle_path("gems/with_man-1.0/man")}:/foo")
+ end
+ end
+
+ context "when the user does not have one set" do
+ before { ENV.delete("MANPATH") }
+
+ it "adds the gem's man dir to the MANPATH" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ gem "with_man"
+ G
+
+ run! "puts ENV['MANPATH']"
+ expect(out).to eq(default_bundle_path("gems/with_man-1.0/man").to_s)
+ end
+ end
+ end
+
+ it "should prepend gemspec require paths to $LOAD_PATH in order" do
+ update_repo2 do
+ build_gem("requirepaths") do |s|
+ s.write("lib/rq.rb", "puts 'yay'")
+ s.write("src/rq.rb", "puts 'nooo'")
+ s.require_paths = %w[lib src]
+ end
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "requirepaths", :require => nil
+ G
+
+ run "require 'rq'"
+ expect(out).to eq("yay")
+ end
+
+ it "should clean $LOAD_PATH properly", :ruby_repo do
+ gem_name = "very_simple_binary"
+ full_gem_name = gem_name + "-1.0"
+ ext_dir = File.join(tmp "extenstions", full_gem_name)
+
+ install_gem full_gem_name
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ G
+
+ ruby <<-R
+ if Gem::Specification.method_defined? :extension_dir
+ s = Gem::Specification.find_by_name '#{gem_name}'
+ s.extension_dir = '#{ext_dir}'
+
+ # Don't build extensions.
+ s.class.send(:define_method, :build_extensions) { nil }
+ end
+
+ require 'bundler'
+ gem '#{gem_name}'
+
+ puts $LOAD_PATH.count {|path| path =~ /#{gem_name}/} >= 2
+
+ Bundler.setup
+
+ puts $LOAD_PATH.count {|path| path =~ /#{gem_name}/} == 0
+ R
+
+ expect(out).to eq("true\ntrue")
+ end
+
+ context "with bundler is located in symlinked GEM_HOME" do
+ let(:gem_home) { Dir.mktmpdir }
+ let(:symlinked_gem_home) { Tempfile.new("gem_home") }
+ let(:bundler_dir) { ruby_core? ? File.expand_path("../../../..", __FILE__) : File.expand_path("../../..", __FILE__) }
+ let(:bundler_lib) { File.join(bundler_dir, "lib") }
+
+ before do
+ FileUtils.ln_sf(gem_home, symlinked_gem_home.path)
+ gems_dir = File.join(gem_home, "gems")
+ specifications_dir = File.join(gem_home, "specifications")
+ Dir.mkdir(gems_dir)
+ Dir.mkdir(specifications_dir)
+
+ FileUtils.ln_s(bundler_dir, File.join(gems_dir, "bundler-#{Bundler::VERSION}"))
+
+ gemspec_file = ruby_core? ? "#{bundler_dir}/lib/bundler.gemspec" : "#{bundler_dir}/bundler.gemspec"
+ gemspec = File.read(gemspec_file).
+ sub("Bundler::VERSION", %("#{Bundler::VERSION}"))
+ gemspec = gemspec.lines.reject {|line| line =~ %r{lib/bundler/version} }.join
+
+ File.open(File.join(specifications_dir, "bundler.gemspec"), "wb") do |f|
+ f.write(gemspec)
+ end
+ end
+
+ it "should succesfully require 'bundler/setup'", :ruby_repo do
+ install_gemfile ""
+
+ ENV["GEM_PATH"] = symlinked_gem_home.path
+
+ ruby <<-R
+ if $LOAD_PATH.include?("#{bundler_lib}")
+ # We should use bundler from GEM_PATH for this test, so we should
+ # remove path to the bundler source tree
+ $LOAD_PATH.delete("#{bundler_lib}")
+ else
+ raise "We don't have #{bundler_lib} in $LOAD_PATH"
+ end
+ puts (require 'bundler/setup')
+ R
+
+ expect(out).to eql("true")
+ end
+ end
+
+ it "stubs out Gem.refresh so it does not reveal system gems" do
+ system_gems "rack-1.0.0"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ G
+
+ run <<-R
+ puts Bundler.rubygems.find_name("rack").inspect
+ Gem.refresh
+ puts Bundler.rubygems.find_name("rack").inspect
+ R
+
+ expect(out).to eq("[]\n[]")
+ end
+
+ describe "when a vendored gem specification uses the :path option" do
+ it "should resolve paths relative to the Gemfile" do
+ path = bundled_app(File.join("vendor", "foo"))
+ build_lib "foo", :path => path
+
+ # If the .gemspec exists, then Bundler handles the path differently.
+ # See Source::Path.load_spec_files for details.
+ FileUtils.rm(File.join(path, "foo.gemspec"))
+
+ install_gemfile <<-G
+ gem 'foo', '1.2.3', :path => 'vendor/foo'
+ G
+
+ Dir.chdir(bundled_app.parent) do
+ run <<-R, :env => { "BUNDLE_GEMFILE" => bundled_app("Gemfile") }
+ require 'foo'
+ R
+ end
+ expect(err).to lack_errors
+ end
+
+ it "should make sure the Bundler.root is really included in the path relative to the Gemfile" do
+ relative_path = File.join("vendor", Dir.pwd[1..-1], "foo")
+ absolute_path = bundled_app(relative_path)
+ FileUtils.mkdir_p(absolute_path)
+ build_lib "foo", :path => absolute_path
+
+ # If the .gemspec exists, then Bundler handles the path differently.
+ # See Source::Path.load_spec_files for details.
+ FileUtils.rm(File.join(absolute_path, "foo.gemspec"))
+
+ gemfile <<-G
+ gem 'foo', '1.2.3', :path => '#{relative_path}'
+ G
+
+ bundle :install
+
+ Dir.chdir(bundled_app.parent) do
+ run <<-R, :env => { "BUNDLE_GEMFILE" => bundled_app("Gemfile") }
+ require 'foo'
+ R
+ end
+
+ expect(err).to lack_errors
+ end
+ end
+
+ describe "with git gems that don't have gemspecs" do
+ before :each do
+ build_git "no-gemspec", :gemspec => false
+
+ install_gemfile <<-G
+ gem "no-gemspec", "1.0", :git => "#{lib_path("no-gemspec-1.0")}"
+ G
+ end
+
+ it "loads the library via a virtual spec" do
+ run <<-R
+ require 'no-gemspec'
+ puts NOGEMSPEC
+ R
+
+ expect(out).to eq("1.0")
+ end
+ end
+
+ describe "with bundled and system gems" do
+ before :each do
+ system_gems "rack-1.0.0"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "activesupport", "2.3.5"
+ G
+ end
+
+ it "does not pull in system gems" do
+ run <<-R
+ require 'rubygems'
+
+ begin;
+ require 'rack'
+ rescue LoadError
+ puts 'WIN'
+ end
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "provides a gem method" do
+ run <<-R
+ gem 'activesupport'
+ require 'activesupport'
+ puts ACTIVESUPPORT
+ R
+
+ expect(out).to eq("2.3.5")
+ end
+
+ it "raises an exception if gem is used to invoke a system gem not in the bundle" do
+ run <<-R
+ begin
+ gem 'rack'
+ rescue LoadError => e
+ puts e.message
+ end
+ R
+
+ expect(out).to eq("rack is not part of the bundle. Add it to your Gemfile.")
+ end
+
+ it "sets GEM_HOME appropriately" do
+ run "puts ENV['GEM_HOME']"
+ expect(out).to eq(default_bundle_path.to_s)
+ end
+ end
+
+ describe "with system gems in the bundle" do
+ before :each do
+ bundle! "config path.system true"
+ system_gems "rack-1.0.0"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0.0"
+ gem "activesupport", "2.3.5"
+ G
+ end
+
+ it "sets GEM_PATH appropriately" do
+ run "puts Gem.path"
+ paths = out.split("\n")
+ expect(paths).to include(system_gem_path.to_s)
+ end
+ end
+
+ describe "with a gemspec that requires other files" do
+ before :each do
+ build_git "bar", :gemspec => false do |s|
+ s.write "lib/bar/version.rb", %(BAR_VERSION = '1.0')
+ s.write "bar.gemspec", <<-G
+ lib = File.expand_path('../lib/', __FILE__)
+ $:.unshift lib unless $:.include?(lib)
+ require 'bar/version'
+
+ Gem::Specification.new do |s|
+ s.name = 'bar'
+ s.version = BAR_VERSION
+ s.summary = 'Bar'
+ s.files = Dir["lib/**/*.rb"]
+ s.author = 'no one'
+ end
+ G
+ end
+
+ gemfile <<-G
+ gem "bar", :git => "#{lib_path("bar-1.0")}"
+ G
+ end
+
+ it "evals each gemspec in the context of its parent directory" do
+ bundle :install
+ run "require 'bar'; puts BAR"
+ expect(out).to eq("1.0")
+ end
+
+ it "error intelligently if the gemspec has a LoadError" do
+ ref = update_git "bar", :gemspec => false do |s|
+ s.write "bar.gemspec", "require 'foobarbaz'"
+ end.ref_for("HEAD")
+ bundle :install
+
+ expect(out.lines.map(&:chomp)).to include(
+ a_string_starting_with("[!] There was an error while loading `bar.gemspec`:"),
+ RUBY_VERSION >= "1.9" ? a_string_starting_with("Does it try to require a relative path? That's been removed in Ruby 1.9.") : "",
+ " # from #{default_bundle_path "bundler", "gems", "bar-1.0-#{ref[0, 12]}", "bar.gemspec"}:1",
+ " > require 'foobarbaz'"
+ )
+ end
+
+ it "evals each gemspec with a binding from the top level" do
+ bundle "install"
+
+ ruby <<-RUBY
+ require 'bundler'
+ def Bundler.require(path)
+ raise "LOSE"
+ end
+ Bundler.load
+ RUBY
+
+ expect(err).to lack_errors
+ expect(out).to eq("")
+ end
+ end
+
+ describe "when Bundler is bundled" do
+ it "doesn't blow up" do
+ install_gemfile <<-G
+ gem "bundler", :path => "#{File.expand_path("..", lib)}"
+ G
+
+ bundle %(exec ruby -e "require 'bundler'; Bundler.setup"), :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(err).to lack_errors
+ end
+ end
+
+ describe "when BUNDLED WITH" do
+ def lock_with(bundler_version = nil)
+ lock = <<-L
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+ L
+
+ if bundler_version
+ lock += "\n BUNDLED WITH\n #{bundler_version}\n"
+ end
+
+ lock
+ end
+
+ before do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ context "is not present" do
+ it "does not change the lock" do
+ lockfile lock_with(nil)
+ ruby "require 'bundler/setup'"
+ lockfile_should_be lock_with(nil)
+ end
+ end
+
+ context "is newer" do
+ it "does not change the lock or warn" do
+ lockfile lock_with(Bundler::VERSION.succ)
+ ruby "require 'bundler/setup'"
+ expect(out).to eq("")
+ expect(err).to eq("")
+ lockfile_should_be lock_with(Bundler::VERSION.succ)
+ end
+ end
+
+ context "is older" do
+ it "does not change the lock" do
+ lockfile lock_with("1.10.1")
+ ruby "require 'bundler/setup'"
+ lockfile_should_be lock_with("1.10.1")
+ end
+ end
+ end
+
+ describe "when RUBY VERSION" do
+ let(:ruby_version) { nil }
+
+ def lock_with(ruby_version = nil)
+ lock = <<-L
+ GEM
+ remote: file://localhost#{gem_repo1}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+ L
+
+ if ruby_version
+ lock += "\n RUBY VERSION\n ruby #{ruby_version}\n"
+ end
+
+ lock += <<-L
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ normalize_uri_file(lock)
+ end
+
+ before do
+ install_gemfile <<-G
+ ruby ">= 0"
+ source "file://localhost#{gem_repo1}"
+ gem "rack"
+ G
+ lockfile lock_with(ruby_version)
+ end
+
+ context "is not present" do
+ it "does not change the lock" do
+ expect { ruby! "require 'bundler/setup'" }.not_to change { lockfile }
+ end
+ end
+
+ context "is newer" do
+ let(:ruby_version) { "5.5.5" }
+ it "does not change the lock or warn" do
+ expect { ruby! "require 'bundler/setup'" }.not_to change { lockfile }
+ expect(out).to eq("")
+ expect(err).to eq("")
+ end
+ end
+
+ context "is older" do
+ let(:ruby_version) { "1.0.0" }
+ it "does not change the lock" do
+ expect { ruby! "require 'bundler/setup'" }.not_to change { lockfile }
+ end
+ end
+ end
+
+ describe "with gemified standard libraries" do
+ it "does not load Psych", :ruby => "~> 2.2" do
+ gemfile ""
+ ruby <<-RUBY
+ require 'bundler/setup'
+ puts defined?(Psych::VERSION) ? Psych::VERSION : "undefined"
+ require 'psych'
+ puts Psych::VERSION
+ RUBY
+ pre_bundler, post_bundler = out.split("\n")
+ expect(pre_bundler).to eq("undefined")
+ expect(post_bundler).to match(/\d+\.\d+\.\d+/)
+ end
+
+ it "does not load openssl" do
+ install_gemfile! ""
+ ruby! <<-RUBY
+ require "bundler/setup"
+ puts defined?(OpenSSL) || "undefined"
+ require "openssl"
+ puts defined?(OpenSSL) || "undefined"
+ RUBY
+ expect(out).to eq("undefined\nconstant")
+ end
+
+ describe "default gem activation" do
+ let(:exemptions) do
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("2.7") || ENV["RGV"] == "master"
+ []
+ else
+ %w[io-console openssl]
+ end << "bundler"
+ end
+
+ let(:activation_warning_hack) { strip_whitespace(<<-RUBY) }
+ require #{spec_dir.join("support/hax").to_s.dump}
+ require "rubygems"
+
+ if Gem::Specification.instance_methods.map(&:to_sym).include?(:activate)
+ Gem::Specification.send(:alias_method, :bundler_spec_activate, :activate)
+ Gem::Specification.send(:define_method, :activate) do
+ unless #{exemptions.inspect}.include?(name)
+ warn '-' * 80
+ warn "activating \#{full_name}"
+ warn *caller
+ warn '*' * 80
+ end
+ bundler_spec_activate
+ end
+ end
+ RUBY
+
+ let(:activation_warning_hack_rubyopt) do
+ create_file("activation_warning_hack.rb", activation_warning_hack)
+ "-r#{bundled_app("activation_warning_hack.rb")} #{ENV["RUBYOPT"]}"
+ end
+
+ let(:code) { strip_whitespace(<<-RUBY) }
+ require "bundler/setup"
+ require "pp"
+ loaded_specs = Gem.loaded_specs.dup
+ #{exemptions.inspect}.each {|s| loaded_specs.delete(s) }
+ pp loaded_specs
+
+ # not a default gem, but harmful to have loaded
+ open_uri = $LOADED_FEATURES.grep(/open.uri/)
+ unless open_uri.empty?
+ warn "open_uri: \#{open_uri}"
+ end
+ RUBY
+
+ it "activates no gems with -rbundler/setup" do
+ install_gemfile! ""
+ ruby! code, :env => { :RUBYOPT => activation_warning_hack_rubyopt }
+ expect(last_command.stdout).to eq("{}")
+ end
+
+ it "activates no gems with bundle exec" do
+ install_gemfile! ""
+ # ensure we clean out the default gems, bceause bundler's allowed to be activated
+ create_file("script.rb", code)
+ bundle! "exec ruby ./script.rb", :env => { :RUBYOPT => activation_warning_hack_rubyopt + " -rbundler/setup" }
+ expect(last_command.stdout).to eq("{}")
+ end
+
+ it "activates no gems with bundle exec that is loaded" do
+ # TODO: remove once https://github.com/erikhuda/thor/pull/539 is released
+ exemptions << "io-console"
+
+ install_gemfile! ""
+ create_file("script.rb", "#!/usr/bin/env ruby\n\n#{code}")
+ FileUtils.chmod(0o777, bundled_app("script.rb"))
+ bundle! "exec ./script.rb", :artifice => nil, :env => { :RUBYOPT => activation_warning_hack_rubyopt }
+ expect(last_command.stdout).to eq("{}")
+ end
+
+ let(:default_gems) do
+ ruby!(<<-RUBY).split("\n")
+ if Gem::Specification.is_a?(Enumerable)
+ puts Gem::Specification.select(&:default_gem?).map(&:name)
+ end
+ RUBY
+ end
+
+ it "activates newer versions of default gems" do
+ build_repo4 do
+ default_gems.each do |g|
+ build_gem g, "999999"
+ end
+ end
+
+ default_gems.reject! {|g| exemptions.include?(g) }
+
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ #{default_gems}.each do |g|
+ gem g, "999999"
+ end
+ G
+
+ expect(the_bundle).to include_gems(*default_gems.map {|g| "#{g} 999999" })
+ end
+
+ it "activates older versions of default gems" do
+ build_repo4 do
+ default_gems.each do |g|
+ build_gem g, "0.0.0.a"
+ end
+ end
+
+ default_gems.reject! {|g| exemptions.include?(g) }
+
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ #{default_gems}.each do |g|
+ gem g, "0.0.0.a"
+ end
+ G
+
+ expect(the_bundle).to include_gems(*default_gems.map {|g| "#{g} 0.0.0.a" })
+ end
+ end
+ end
+
+ describe "after setup" do
+ it "allows calling #gem on random objects", :bundler => "< 2" do
+ install_gemfile <<-G
+ source "file:#{gem_repo1}"
+ gem "rack"
+ G
+
+ ruby! <<-RUBY
+ require "bundler/setup"
+ Object.new.gem "rack"
+ puts Gem.loaded_specs["rack"].full_name
+ RUBY
+
+ expect(out).to eq("rack-1.0.0")
+ end
+
+ it "keeps Kernel#gem private", :bundler => "2" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+ gem "rack"
+ G
+
+ ruby <<-RUBY
+ require "bundler/setup"
+ Object.new.gem "rack"
+ puts "FAIL"
+ RUBY
+
+ expect(last_command.stdboth).not_to include "FAIL"
+ expect(last_command.stderr).to include "private method `gem'"
+ end
+
+ it "keeps Kernel#require private" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo1}"
+ gem "rack"
+ G
+
+ ruby <<-RUBY
+ require "bundler/setup"
+ Object.new.require "rack"
+ puts "FAIL"
+ RUBY
+
+ expect(last_command.stdboth).not_to include "FAIL"
+ expect(last_command.stderr).to include "private method `require'"
+ end
+ end
+end
diff --git a/spec/bundler/runtime/with_clean_env_spec.rb b/spec/bundler/runtime/with_clean_env_spec.rb
new file mode 100644
index 0000000000..321f5b6415
--- /dev/null
+++ b/spec/bundler/runtime/with_clean_env_spec.rb
@@ -0,0 +1,151 @@
+# frozen_string_literal: true
+
+RSpec.describe "Bundler.with_env helpers" do
+ def bundle_exec_ruby!(code, *args)
+ opts = args.last.is_a?(Hash) ? args.pop : {}
+ env = opts[:env] ||= {}
+ env[:RUBYOPT] ||= "-r#{spec_dir.join("support/hax")}"
+ args.push opts
+ bundle! "exec '#{Gem.ruby}' -e #{code}", *args
+ end
+
+ describe "Bundler.original_env" do
+ before do
+ bundle "config path vendor/bundle"
+ gemfile ""
+ bundle "install"
+ end
+
+ it "should return the PATH present before bundle was activated" do
+ code = "print Bundler.original_env['PATH']"
+ path = `getconf PATH`.strip + "#{File::PATH_SEPARATOR}/foo"
+ with_path_as(path) do
+ bundle_exec_ruby!(code.dump)
+ expect(last_command.stdboth).to eq(path)
+ end
+ end
+
+ it "should return the GEM_PATH present before bundle was activated" do
+ code = "print Bundler.original_env['GEM_PATH']"
+ gem_path = ENV["GEM_PATH"] + ":/foo"
+ with_gem_path_as(gem_path) do
+ bundle_exec_ruby!(code.dump)
+ expect(last_command.stdboth).to eq(gem_path)
+ end
+ end
+
+ it "works with nested bundle exec invocations", :ruby_repo do
+ create_file("exe.rb", <<-'RB')
+ count = ARGV.first.to_i
+ exit if count < 0
+ STDERR.puts "#{count} #{ENV["PATH"].end_with?(":/foo")}"
+ if count == 2
+ ENV["PATH"] = "#{ENV["PATH"]}:/foo"
+ end
+ exec(Gem.ruby, __FILE__, (count - 1).to_s)
+ RB
+ path = `getconf PATH`.strip + File::PATH_SEPARATOR + File.dirname(Gem.ruby)
+ with_path_as(path) do
+ bundle! "exec '#{Gem.ruby}' #{bundled_app("exe.rb")} 2", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ end
+ expect(err).to eq <<-EOS.strip
+2 false
+1 true
+0 true
+ EOS
+ end
+
+ it "removes variables that bundler added", :ruby_repo do
+ original = ruby!('puts ENV.to_a.map {|e| e.join("=") }.sort.join("\n")', :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" })
+ code = 'puts Bundler.original_env.to_a.map {|e| e.join("=") }.sort.join("\n")'
+ bundle! "exec '#{Gem.ruby}' -e #{code.dump}", :env => { :RUBYOPT => "-r#{spec_dir.join("support/hax")}" }
+ expect(out).to eq original
+ end
+ end
+
+ describe "Bundler.clean_env", :bundler => "< 2" do
+ before do
+ bundle "config path vendor/bundle"
+ gemfile ""
+ bundle "install"
+ end
+
+ it "should delete BUNDLE_PATH" do
+ code = "print Bundler.clean_env.has_key?('BUNDLE_PATH')"
+ ENV["BUNDLE_PATH"] = "./foo"
+ bundle_exec_ruby! code.dump
+ expect(last_command.stdboth).to eq "false"
+ end
+
+ it "should remove '-rbundler/setup' from RUBYOPT" do
+ code = "print Bundler.clean_env['RUBYOPT']"
+ ENV["RUBYOPT"] = "-W2 -rbundler/setup"
+ bundle_exec_ruby! code.dump
+ expect(last_command.stdboth).not_to include("-rbundler/setup")
+ end
+
+ it "should clean up RUBYLIB", :ruby_repo do
+ code = "print Bundler.clean_env['RUBYLIB']"
+ ENV["RUBYLIB"] = root.join("lib").to_s + File::PATH_SEPARATOR + "/foo"
+ bundle_exec_ruby! code.dump
+ expect(last_command.stdboth).to eq("/foo")
+ end
+
+ it "should restore the original MANPATH" do
+ code = "print Bundler.clean_env['MANPATH']"
+ ENV["MANPATH"] = "/foo"
+ ENV["BUNDLER_ORIG_MANPATH"] = "/foo-original"
+ bundle_exec_ruby! code.dump
+ expect(last_command.stdboth).to eq("/foo-original")
+ end
+ end
+
+ describe "Bundler.with_original_env" do
+ it "should set ENV to original_env in the block" do
+ expected = Bundler.original_env
+ actual = Bundler.with_original_env { ENV.to_hash }
+ expect(actual).to eq(expected)
+ end
+
+ it "should restore the environment after execution" do
+ Bundler.with_original_env do
+ ENV["FOO"] = "hello"
+ end
+
+ expect(ENV).not_to have_key("FOO")
+ end
+ end
+
+ describe "Bundler.with_clean_env", :bundler => "< 2" do
+ it "should set ENV to clean_env in the block" do
+ expected = Bundler.clean_env
+ actual = Bundler.with_clean_env { ENV.to_hash }
+ expect(actual).to eq(expected)
+ end
+
+ it "should restore the environment after execution" do
+ Bundler.with_clean_env do
+ ENV["FOO"] = "hello"
+ end
+
+ expect(ENV).not_to have_key("FOO")
+ end
+ end
+
+ describe "Bundler.clean_system", :ruby => ">= 1.9", :bundler => "< 2" do
+ it "runs system inside with_clean_env" do
+ Bundler.clean_system(%(echo 'if [ "$BUNDLE_PATH" = "" ]; then exit 42; else exit 1; fi' | /bin/sh))
+ expect($?.exitstatus).to eq(42)
+ end
+ end
+
+ describe "Bundler.clean_exec", :ruby => ">= 1.9", :bundler => "< 2" do
+ it "runs exec inside with_clean_env" do
+ pid = Kernel.fork do
+ Bundler.clean_exec(%(echo 'if [ "$BUNDLE_PATH" = "" ]; then exit 42; else exit 1; fi' | /bin/sh))
+ end
+ Process.wait(pid)
+ expect($?.exitstatus).to eq(42)
+ end
+ end
+end
diff --git a/spec/bundler/spec_helper.rb b/spec/bundler/spec_helper.rb
new file mode 100644
index 0000000000..0646719c4c
--- /dev/null
+++ b/spec/bundler/spec_helper.rb
@@ -0,0 +1,170 @@
+# frozen_string_literal: true
+
+$:.unshift File.expand_path("..", __FILE__)
+$:.unshift File.expand_path("../../lib", __FILE__)
+
+require "rubygems"
+module Gem
+ if defined?(@path_to_default_spec_map)
+ @path_to_default_spec_map.delete_if do |_path, spec|
+ spec.name == "bundler"
+ end
+ end
+end
+
+begin
+ require File.expand_path("../support/path.rb", __FILE__)
+ spec = Gem::Specification.load(Spec::Path.gemspec.to_s)
+ rspec = spec.dependencies.find {|d| d.name == "rspec" }
+ gem "rspec", rspec.requirement.to_s
+ require "rspec"
+ require "diff/lcs"
+rescue LoadError
+ abort "Run rake spec:deps to install development dependencies"
+end
+
+require "bundler/psyched_yaml"
+require "bundler/vendored_fileutils"
+require "uri"
+require "digest"
+
+# Delete the default copy of Bundler that RVM installs for us when running in CI
+require "fileutils"
+if ENV.select {|k, _v| k =~ /TRAVIS/ }.any? && Gem::Version.new(Gem::VERSION) > Gem::Version.new("2.0")
+ Dir.glob(File.join(Gem::Specification.default_specifications_dir, "bundler*.gemspec")).each do |file|
+ FileUtils.rm_rf(file)
+ end
+
+ Dir.glob(File.join(RbConfig::CONFIG["sitelibdir"], "bundler*")).each do |file|
+ FileUtils.rm_rf(file)
+ end
+end
+
+if File.expand_path(__FILE__) =~ %r{([^\w/\.-])}
+ abort "The bundler specs cannot be run from a path that contains special characters (particularly #{$1.inspect})"
+end
+
+require "bundler"
+
+Dir["#{File.expand_path("../support", __FILE__)}/*.rb"].each do |file|
+ file = file.gsub(%r{\A#{Regexp.escape File.expand_path("..", __FILE__)}/}, "")
+ require file unless file.end_with?("hax.rb")
+end
+
+$debug = false
+
+Spec::Manpages.setup
+Spec::Rubygems.setup
+FileUtils.rm_rf(Spec::Path.gem_repo1)
+ENV["RUBYOPT"] = "#{ENV["RUBYOPT"]} -r#{Spec::Path.spec_dir}/support/hax.rb"
+ENV["BUNDLE_SPEC_RUN"] = "true"
+
+# Don't wrap output in tests
+ENV["THOR_COLUMNS"] = "10000"
+
+Spec::CodeClimate.setup
+
+module Gem
+ def self.ruby=(ruby)
+ @ruby = ruby
+ end
+end
+
+RSpec.configure do |config|
+ config.include Spec::Builders
+ config.include Spec::Helpers
+ config.include Spec::Indexes
+ config.include Spec::Matchers
+ config.include Spec::Path
+ config.include Spec::Rubygems
+ config.include Spec::Platforms
+ config.include Spec::Sudo
+ config.include Spec::Permissions
+
+ # Enable flags like --only-failures and --next-failure
+ config.example_status_persistence_file_path = ".rspec_status"
+
+ config.disable_monkey_patching!
+
+ # Since failures cause us to keep a bunch of long strings in memory, stop
+ # once we have a large number of failures (indicative of core pieces of
+ # bundler being broken) so that running the full test suite doesn't take
+ # forever due to memory constraints
+ config.fail_fast ||= 25 if ENV["CI"]
+
+ if ENV["BUNDLER_SUDO_TESTS"] && Spec::Sudo.present?
+ config.filter_run :sudo => true
+ else
+ config.filter_run_excluding :sudo => true
+ end
+
+ if ENV["BUNDLER_REALWORLD_TESTS"]
+ config.filter_run :realworld => true
+ else
+ config.filter_run_excluding :realworld => true
+ end
+
+ git_version = Bundler::Source::Git::GitProxy.new(nil, nil, nil).version
+
+ config.filter_run_excluding :ruby => LessThanProc.with(RUBY_VERSION)
+ config.filter_run_excluding :rubygems => LessThanProc.with(Gem::VERSION)
+ config.filter_run_excluding :git => LessThanProc.with(git_version)
+ config.filter_run_excluding :rubygems_master => (ENV["RGV"] != "master")
+ config.filter_run_excluding :bundler => LessThanProc.with(Bundler::VERSION.split(".")[0, 2].join("."))
+ config.filter_run_excluding :ruby_repo => !(ENV["BUNDLE_RUBY"] && ENV["BUNDLE_GEM"]).nil?
+
+ config.filter_run_when_matching :focus unless ENV["CI"]
+
+ original_wd = Dir.pwd
+ original_env = ENV.to_hash.delete_if {|k, _v| k.start_with?(Bundler::EnvironmentPreserver::BUNDLER_PREFIX) }
+
+ config.expect_with :rspec do |c|
+ c.syntax = :expect
+ end
+
+ config.around :each do |example|
+ if ENV["BUNDLE_RUBY"]
+ orig_ruby = Gem.ruby
+ Gem.ruby = ENV["BUNDLE_RUBY"]
+ end
+ example.run
+ Gem.ruby = orig_ruby if ENV["BUNDLE_RUBY"]
+ end
+
+ config.before :suite do
+ if ENV["BUNDLE_RUBY"]
+ FileUtils.cp_r Spec::Path.bindir, File.join(Spec::Path.root, "lib", "exe")
+ end
+ end
+
+ config.before :all do
+ build_repo1
+ end
+
+ config.before :each do
+ reset!
+ system_gems []
+ in_app_root
+ @command_executions = []
+ end
+
+ config.after :each do |example|
+ all_output = @command_executions.map(&:to_s_verbose).join("\n\n")
+ if example.exception && !all_output.empty?
+ warn all_output unless config.formatters.grep(RSpec::Core::Formatters::DocumentationFormatter).empty?
+ message = example.exception.message + "\n\nCommands:\n#{all_output}"
+ (class << example.exception; self; end).send(:define_method, :message) do
+ message
+ end
+ end
+
+ Dir.chdir(original_wd)
+ ENV.replace(original_env)
+ end
+
+ config.after :suite do
+ if ENV["BUNDLE_RUBY"]
+ FileUtils.rm_rf File.join(Spec::Path.root, "lib", "exe")
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/compact_index.rb b/spec/bundler/support/artifice/compact_index.rb
new file mode 100644
index 0000000000..01e8eb7837
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index.rb
@@ -0,0 +1,122 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+$LOAD_PATH.unshift Dir[base_system_gems.join("gems/compact_index*/lib")].first.to_s
+require "compact_index"
+
+class CompactIndexAPI < Endpoint
+ helpers do
+ def load_spec(name, version, platform, gem_repo)
+ full_name = "#{name}-#{version}"
+ full_name += "-#{platform}" if platform != "ruby"
+ Marshal.load(Bundler.rubygems.inflate(File.open(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz")).read))
+ 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 => 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)
+ if body.respond_to?(:byteslice)
+ body.byteslice(range)
+ else # pre-1.9.3
+ body.unpack("@#{range.first}a#{range.end + 1}").first
+ end
+ end
+
+ def gems(gem_repo = 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
+ nil
+ end
+ CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil,
+ deps, spec.required_ruby_version, spec.required_rubygems_version)
+ 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")
+ file.delete if file.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
+
+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
new file mode 100644
index 0000000000..d4e68c38e8
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_api_missing.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexApiMissing < CompactIndexAPI
+ get "/fetch/actual/gem/:id" do
+ $stderr.puts params[:id]
+ if params[:id] == "rack-1.0.gemspec.rz"
+ halt 404
+ else
+ File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+ end
+end
+
+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
new file mode 100644
index 0000000000..97aa6cbd84
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_basic_authentication.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexBasicAuthentication < CompactIndexAPI
+ before do
+ unless env["HTTP_AUTHORIZATION"]
+ halt 401, "Authentication info not supplied"
+ end
+ end
+end
+
+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
new file mode 100644
index 0000000000..62feb9f164
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexChecksumMismatch < CompactIndexAPI
+ get "/versions" do
+ headers "ETag" => quote("123")
+ headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
+ content_type "text/plain"
+ body ""
+ end
+end
+
+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
new file mode 100644
index 0000000000..972ecb88b7
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_concurrent_download.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexConcurrentDownload < CompactIndexAPI
+ get "/versions" do
+ versions = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions")
+
+ # Verify the original (empty) content hasn't been deleted, e.g. on a retry
+ File.read(versions) == "" || raise("Original file should be present and empty")
+
+ # Verify this is only requested once for a partial download
+ env["HTTP_RANGE"] || raise("Missing Range header for expected partial download")
+
+ # Overwrite the file in parallel, which should be then overwritten
+ # after a successful download to prevent corruption
+ File.open(versions, "w") {|f| f.puts "another process" }
+
+ etag_response do
+ file = tmp("versions.list")
+ file.delete if file.file?
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems)
+ file.contents
+ end
+ end
+end
+
+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
new file mode 100644
index 0000000000..0d349bcc1e
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_creds_diff_host.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexCredsDiffHost < CompactIndexAPI
+ helpers do
+ def auth
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
+ end
+
+ def authorized?
+ auth.provided? && auth.basic? && auth.credentials && auth.credentials == %w[user pass]
+ end
+
+ def protected!
+ return if authorized?
+ response["WWW-Authenticate"] = %(Basic realm="Testing HTTP Auth")
+ throw(:halt, [401, "Not authorized\n"])
+ end
+ end
+
+ before do
+ protected! unless request.path_info.include?("/no/creds/")
+ end
+
+ get "/gems/:id" do
+ redirect "http://diffhost.com/no/creds/#{params[:id]}"
+ end
+
+ get "/no/creds/:id" do
+ if request.host.include?("diffhost") && !auth.provided?
+ File.read("#{gem_repo1}/gems/#{params[:id]}")
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexCredsDiffHost)
diff --git a/spec/bundler/support/artifice/compact_index_extra.rb b/spec/bundler/support/artifice/compact_index_extra.rb
new file mode 100644
index 0000000000..84d1859235
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_extra.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+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.read("#{gem_repo2}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.read("#{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.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.read("#{gem_repo2}/gems/#{params[:id]}")
+ end
+end
+
+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
new file mode 100644
index 0000000000..903aa900fb
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_extra_api.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+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")
+ file.delete if file.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.read("#{gem_repo4}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.read("#{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.read("#{gem_repo4}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.read("#{gem_repo4}/gems/#{params[:id]}")
+ end
+end
+
+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
new file mode 100644
index 0000000000..e72040f604
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_extra_api_missing.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index_extra_api", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexExtraAPIMissing < CompactIndexExtraApi
+ get "/extra/fetch/actual/gem/:id" do
+ if params[:id] == "missing-1.0.gemspec.rz"
+ halt 404
+ else
+ File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+ end
+end
+
+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
new file mode 100644
index 0000000000..67a9d23691
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_extra_missing.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index_extra", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexExtraMissing < CompactIndexExtra
+ get "/extra/fetch/actual/gem/:id" do
+ if params[:id] == "missing-1.0.gemspec.rz"
+ halt 404
+ else
+ File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexExtraMissing)
diff --git a/spec/bundler/support/artifice/compact_index_forbidden.rb b/spec/bundler/support/artifice/compact_index_forbidden.rb
new file mode 100644
index 0000000000..0a4dfdb2e8
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_forbidden.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexForbidden < CompactIndexAPI
+ get "/versions" do
+ halt 403
+ end
+end
+
+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
new file mode 100644
index 0000000000..ab371117de
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_host_redirect.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexHostRedirect < CompactIndexAPI
+ get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do
+ redirect "http://bundler.localgemserver.test#{request.path_info}"
+ end
+
+ get "/versions" do
+ status 404
+ end
+
+ get "/api/v1/dependencies" do
+ status 404
+ end
+end
+
+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
new file mode 100644
index 0000000000..01c5be1b3d
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_no_gem.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexNoGem < CompactIndexAPI
+ get "/gems/:id" do
+ halt 500
+ end
+end
+
+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
new file mode 100644
index 0000000000..eaedff5105
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_partial_update.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexPartialUpdate < CompactIndexAPI
+ # Stub the server to never return 304s. This simulates the behaviour of
+ # Fastly / Rubygems ignoring ETag headers.
+ def not_modified?(_checksum)
+ false
+ end
+
+ get "/versions" do
+ cached_versions_path = File.join(
+ Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
+ )
+
+ # Verify a cached copy of the versions file exists
+ unless File.read(cached_versions_path).start_with?("created_at: ")
+ raise("Cached versions file should be present and have content")
+ end
+
+ # Verify that a partial request is made, starting from the index of the
+ # final byte of the cached file.
+ unless env["HTTP_RANGE"] == "bytes=#{File.read(cached_versions_path).bytesize - 1}-"
+ raise("Range header should be present, and start from the index of the final byte of the cache.")
+ end
+
+ etag_response do
+ # Return the exact contents of the cache.
+ File.read(cached_versions_path)
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexPartialUpdate)
diff --git a/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb b/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb
new file mode 100644
index 0000000000..487be4771a
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexRangeNotSatisfiable < CompactIndexAPI
+ get "/versions" do
+ if env["HTTP_RANGE"]
+ status 416
+ else
+ etag_response do
+ file = tmp("versions.list")
+ file.delete if file.file?
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems)
+ file.contents
+ end
+ end
+ end
+
+ get "/info/:name" do
+ if env["HTTP_RANGE"]
+ status 416
+ else
+ etag_response do
+ gem = gems.find {|g| g.name == params[:name] }
+ CompactIndex.info(gem ? gem.versions : [])
+ end
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexRangeNotSatisfiable)
diff --git a/spec/bundler/support/artifice/compact_index_redirects.rb b/spec/bundler/support/artifice/compact_index_redirects.rb
new file mode 100644
index 0000000000..e83451b5b6
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_redirects.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexRedirect < CompactIndexAPI
+ get "/fetch/actual/gem/:id" do
+ redirect "/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/versions" do
+ status 404
+ end
+
+ get "/api/v1/dependencies" do
+ status 404
+ end
+end
+
+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
new file mode 100644
index 0000000000..abbf3258e7
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexStrictBasicAuthentication < CompactIndexAPI
+ before do
+ unless env["HTTP_AUTHORIZATION"]
+ halt 401, "Authentication info not supplied"
+ end
+
+ # Only accepts password == "password"
+ unless env["HTTP_AUTHORIZATION"] == "Basic dXNlcjpwYXNz"
+ halt 403, "Authentication failed"
+ end
+ end
+end
+
+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
new file mode 100644
index 0000000000..7e1d3686e2
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexWrongDependencies < CompactIndexAPI
+ get "/info/:name" do
+ etag_response do
+ gem = gems.find {|g| g.name == params[:name] }
+ gem.versions.each {|gv| gv.dependencies.clear } if gem
+ CompactIndex.info(gem ? gem.versions : [])
+ end
+ end
+end
+
+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
new file mode 100644
index 0000000000..db4d8e3974
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require File.expand_path("../compact_index", __FILE__)
+
+Artifice.deactivate
+
+class CompactIndexWrongGemChecksum < CompactIndexAPI
+ get "/info/:name" do
+ etag_response do
+ name = params[:name]
+ gem = gems.find {|g| g.name == name }
+ checksum = ENV.fetch("BUNDLER_SPEC_#{name.upcase}_CHECKSUM") { "ab" * 22 }
+ versions = gem ? gem.versions : []
+ versions.each {|v| v.checksum = checksum }
+ CompactIndex.info(versions)
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexWrongGemChecksum)
diff --git a/spec/bundler/support/artifice/endopint_marshal_fail_basic_authentication.rb b/spec/bundler/support/artifice/endopint_marshal_fail_basic_authentication.rb
new file mode 100644
index 0000000000..12a6fa153f
--- /dev/null
+++ b/spec/bundler/support/artifice/endopint_marshal_fail_basic_authentication.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint_marshal_fail", __FILE__)
+
+Artifice.deactivate
+
+class EndpointMarshalFailBasicAuthentication < EndpointMarshalFail
+ before do
+ unless env["HTTP_AUTHORIZATION"]
+ halt 401, "Authentication info not supplied"
+ end
+ end
+end
+
+Artifice.activate_with(EndpointMarshalFailBasicAuthentication)
diff --git a/spec/bundler/support/artifice/endpoint.rb b/spec/bundler/support/artifice/endpoint.rb
new file mode 100644
index 0000000000..9a0cfae8a2
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require File.expand_path("../../path.rb", __FILE__)
+require Spec::Path.root.join("lib/bundler/deprecate")
+include Spec::Path
+
+$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gems.join("gems/{artifice,rack,tilt,sinatra}-*/lib")].map(&:to_s))
+require "artifice"
+require "sinatra/base"
+
+ALL_REQUESTS = [] # rubocop:disable Style/MutableConstant
+ALL_REQUESTS_MUTEX = 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
+
+ GEM_REPO = Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"] || Spec::Path.gem_repo1)
+ 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
+ def dependencies_for(gem_names, gem_repo = GEM_REPO)
+ return [] if gem_names.nil? || gem_names.empty?
+
+ require "rubygems"
+ require "bundler"
+ Bundler::Deprecate.skip_during do
+ 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
+ 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.open(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz")).read))
+ end
+ end
+
+ get "/quick/Marshal.4.8/:id" do
+ redirect "/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/fetch/actual/gem/:id" do
+ File.read("#{GEM_REPO}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/gems/:id" do
+ File.read("#{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.read("#{GEM_REPO}/specs.4.8.gz")
+ end
+
+ get "/prerelease_specs.4.8.gz" do
+ File.read("#{GEM_REPO}/prerelease_specs.4.8.gz")
+ end
+end
+
+Artifice.activate_with(Endpoint)
diff --git a/spec/bundler/support/artifice/endpoint_500.rb b/spec/bundler/support/artifice/endpoint_500.rb
new file mode 100644
index 0000000000..202ccfc829
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_500.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require File.expand_path("../../path.rb", __FILE__)
+include Spec::Path
+
+$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gems.join("gems/{artifice,rack,tilt,sinatra}-*/lib")].map(&:to_s))
+
+require "artifice"
+require "sinatra/base"
+
+Artifice.deactivate
+
+class Endpoint500 < Sinatra::Base
+ before do
+ halt 500
+ end
+end
+
+Artifice.activate_with(Endpoint500)
diff --git a/spec/bundler/support/artifice/endpoint_api_forbidden.rb b/spec/bundler/support/artifice/endpoint_api_forbidden.rb
new file mode 100644
index 0000000000..bb89747adc
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_api_forbidden.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointApiForbidden < Endpoint
+ get "/api/v1/dependencies" do
+ halt 403
+ end
+end
+
+Artifice.activate_with(EndpointApiForbidden)
diff --git a/spec/bundler/support/artifice/endpoint_api_missing.rb b/spec/bundler/support/artifice/endpoint_api_missing.rb
new file mode 100644
index 0000000000..95db8e2a7e
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_api_missing.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointApiMissing < Endpoint
+ get "/fetch/actual/gem/:id" do
+ $stderr.puts params[:id]
+ if params[:id] == "rack-1.0.gemspec.rz"
+ halt 404
+ else
+ File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+ end
+end
+
+Artifice.activate_with(EndpointApiMissing)
diff --git a/spec/bundler/support/artifice/endpoint_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_basic_authentication.rb
new file mode 100644
index 0000000000..223671bc29
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_basic_authentication.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointBasicAuthentication < Endpoint
+ before do
+ unless env["HTTP_AUTHORIZATION"]
+ halt 401, "Authentication info not supplied"
+ end
+ end
+end
+
+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
new file mode 100644
index 0000000000..925954b12d
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_creds_diff_host.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointCredsDiffHost < Endpoint
+ helpers do
+ def auth
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
+ end
+
+ def authorized?
+ auth.provided? && auth.basic? && auth.credentials && auth.credentials == %w[user pass]
+ end
+
+ def protected!
+ return if authorized?
+ response["WWW-Authenticate"] = %(Basic realm="Testing HTTP Auth")
+ throw(:halt, [401, "Not authorized\n"])
+ end
+ end
+
+ before do
+ protected! unless request.path_info.include?("/no/creds/")
+ end
+
+ get "/gems/:id" do
+ redirect "http://diffhost.com/no/creds/#{params[:id]}"
+ end
+
+ get "/no/creds/:id" do
+ if request.host.include?("diffhost") && !auth.provided?
+ File.read("#{gem_repo1}/gems/#{params[:id]}")
+ end
+ end
+end
+
+Artifice.activate_with(EndpointCredsDiffHost)
diff --git a/spec/bundler/support/artifice/endpoint_extra.rb b/spec/bundler/support/artifice/endpoint_extra.rb
new file mode 100644
index 0000000000..422f65401b
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_extra.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointExtra < Endpoint
+ get "/extra/api/v1/dependencies" do
+ halt 404
+ end
+
+ get "/extra/specs.4.8.gz" do
+ File.read("#{gem_repo2}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.read("#{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.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.read("#{gem_repo2}/gems/#{params[:id]}")
+ end
+end
+
+Artifice.activate_with(EndpointExtra)
diff --git a/spec/bundler/support/artifice/endpoint_extra_api.rb b/spec/bundler/support/artifice/endpoint_extra_api.rb
new file mode 100644
index 0000000000..62e2c2bb93
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_extra_api.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointExtraApi < Endpoint
+ get "/extra/api/v1/dependencies" do
+ deps = dependencies_for(params[:gems], gem_repo4)
+ Marshal.dump(deps)
+ end
+
+ get "/extra/specs.4.8.gz" do
+ File.read("#{gem_repo4}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.read("#{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.read("#{gem_repo4}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.read("#{gem_repo4}/gems/#{params[:id]}")
+ end
+end
+
+Artifice.activate_with(EndpointExtraApi)
diff --git a/spec/bundler/support/artifice/endpoint_extra_missing.rb b/spec/bundler/support/artifice/endpoint_extra_missing.rb
new file mode 100644
index 0000000000..038a12610a
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_extra_missing.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint_extra", __FILE__)
+
+Artifice.deactivate
+
+class EndpointExtraMissing < EndpointExtra
+ get "/extra/fetch/actual/gem/:id" do
+ if params[:id] == "missing-1.0.gemspec.rz"
+ halt 404
+ else
+ File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+ end
+end
+
+Artifice.activate_with(EndpointExtraMissing)
diff --git a/spec/bundler/support/artifice/endpoint_fallback.rb b/spec/bundler/support/artifice/endpoint_fallback.rb
new file mode 100644
index 0000000000..554c08f0a2
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_fallback.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+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
+
+Artifice.activate_with(EndpointFallback)
diff --git a/spec/bundler/support/artifice/endpoint_host_redirect.rb b/spec/bundler/support/artifice/endpoint_host_redirect.rb
new file mode 100644
index 0000000000..cda5664be2
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_host_redirect.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointHostRedirect < Endpoint
+ get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do
+ redirect "http://bundler.localgemserver.test#{request.path_info}"
+ end
+
+ get "/api/v1/dependencies" do
+ status 404
+ end
+end
+
+Artifice.activate_with(EndpointHostRedirect)
diff --git a/spec/bundler/support/artifice/endpoint_marshal_fail.rb b/spec/bundler/support/artifice/endpoint_marshal_fail.rb
new file mode 100644
index 0000000000..2a5dcdc2fd
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_marshal_fail.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint_fallback", __FILE__)
+
+Artifice.deactivate
+
+class EndpointMarshalFail < EndpointFallback
+ get "/api/v1/dependencies" do
+ "f0283y01hasf"
+ end
+end
+
+Artifice.activate_with(EndpointMarshalFail)
diff --git a/spec/bundler/support/artifice/endpoint_mirror_source.rb b/spec/bundler/support/artifice/endpoint_mirror_source.rb
new file mode 100644
index 0000000000..64452f198d
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_mirror_source.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+class EndpointMirrorSource < Endpoint
+ get "/gems/:id" do
+ if request.env["HTTP_X_GEMFILE_SOURCE"] == "https://server.example.org/"
+ File.read("#{gem_repo1}/gems/#{params[:id]}")
+ else
+ halt 500
+ end
+ end
+end
+
+Artifice.activate_with(EndpointMirrorSource)
diff --git a/spec/bundler/support/artifice/endpoint_redirect.rb b/spec/bundler/support/artifice/endpoint_redirect.rb
new file mode 100644
index 0000000000..ebf01458ba
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_redirect.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointRedirect < Endpoint
+ get "/fetch/actual/gem/:id" do
+ redirect "/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/api/v1/dependencies" do
+ status 404
+ end
+end
+
+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
new file mode 100644
index 0000000000..905a519f3f
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointStrictBasicAuthentication < Endpoint
+ before do
+ unless env["HTTP_AUTHORIZATION"]
+ halt 401, "Authentication info not supplied"
+ end
+
+ # Only accepts password == "password"
+ unless env["HTTP_AUTHORIZATION"] == "Basic dXNlcjpwYXNz"
+ halt 403, "Authentication failed"
+ end
+ end
+end
+
+Artifice.activate_with(EndpointStrictBasicAuthentication)
diff --git a/spec/bundler/support/artifice/endpoint_timeout.rb b/spec/bundler/support/artifice/endpoint_timeout.rb
new file mode 100644
index 0000000000..3f60471c90
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_timeout.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require File.expand_path("../endpoint_fallback", __FILE__)
+
+Artifice.deactivate
+
+class EndpointTimeout < EndpointFallback
+ SLEEP_TIMEOUT = 3
+
+ get "/api/v1/dependencies" do
+ sleep(SLEEP_TIMEOUT)
+ end
+end
+
+Artifice.activate_with(EndpointTimeout)
diff --git a/spec/bundler/support/artifice/fail.rb b/spec/bundler/support/artifice/fail.rb
new file mode 100644
index 0000000000..1059c6df4e
--- /dev/null
+++ b/spec/bundler/support/artifice/fail.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require "net/http"
+begin
+ require "net/https"
+rescue LoadError
+ nil # net/https or openssl
+end
+
+# 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
+ # Net::HTTP, we must set it
+ @newimpl = true
+
+ def request(req, body = nil, &block)
+ raise(exception(req))
+ end
+
+ # Ensure we don't start a connect here
+ def connect
+ end
+
+ def exception(req)
+ name = ENV.fetch("BUNDLER_SPEC_EXCEPTION") { "Errno::ENETUNREACH" }
+ const = name.split("::").reduce(Object) {|mod, sym| mod.const_get(sym) }
+ const.new("host down: Bundler spec artifice fail! #{req["PATH_INFO"]}")
+ end
+end
+
+# Replace Net::HTTP with our failing subclass
+::Net.class_eval do
+ remove_const(:HTTP)
+ const_set(:HTTP, ::Fail)
+end
diff --git a/spec/bundler/support/artifice/vcr.rb b/spec/bundler/support/artifice/vcr.rb
new file mode 100644
index 0000000000..edd2f49a91
--- /dev/null
+++ b/spec/bundler/support/artifice/vcr.rb
@@ -0,0 +1,158 @@
+# frozen_string_literal: true
+
+require "net/http"
+if RUBY_VERSION < "1.9"
+ begin
+ require "net/https"
+ rescue LoadError
+ nil # net/https or openssl
+ end
+end # but only for 1.8
+
+CASSETTE_PATH = File.expand_path("../vcr_cassettes", __FILE__)
+CASSETTE_NAME = ENV.fetch("BUNDLER_SPEC_VCR_CASSETTE_NAME") { "realworld" }
+
+class BundlerVCRHTTP < Net::HTTP
+ class RequestHandler
+ attr_reader :http, :request, :body, :response_block
+ def initialize(http, request, body = nil, &response_block)
+ @http = http
+ @request = request
+ @body = body
+ @response_block = response_block
+ end
+
+ def handle_request
+ handler = self
+ request.instance_eval do
+ @__vcr_request_handler = handler
+ end
+
+ if recorded_response?
+ recorded_response
+ else
+ record_response
+ end
+ end
+
+ def recorded_response?
+ return true if ENV["BUNDLER_SPEC_PRE_RECORDED"]
+ return false if ENV["BUNDLER_SPEC_FORCE_RECORD"]
+ request_pair_paths.all? {|f| File.exist?(f) }
+ end
+
+ def recorded_response
+ File.open(request_pair_paths.last, "rb:ASCII-8BIT") do |response_file|
+ response_io = ::Net::BufferedIO.new(response_file)
+ ::Net::HTTPResponse.read_new(response_io).tap do |response|
+ response.decode_content = request.decode_content if request.respond_to?(:decode_content)
+ response.uri = request.uri if request.respond_to?(:uri)
+
+ response.reading_body(response_io, request.response_body_permitted?) do
+ response_block.call(response) if response_block
+ end
+ end
+ end
+ end
+
+ def record_response
+ request_path, response_path = *request_pair_paths
+
+ @recording = true
+
+ response = http.request_without_vcr(request, body, &response_block)
+ @recording = false
+ unless @recording
+ FileUtils.mkdir_p(File.dirname(request_path))
+ binwrite(request_path, request_to_string(request))
+ binwrite(response_path, response_to_string(response))
+ end
+ response
+ end
+
+ def key
+ [request["host"] || http.address, request.path, request.method].compact
+ end
+
+ def file_name_for_key(key)
+ key.join("/").gsub(/[\:*?"<>|]/, "-")
+ end
+
+ 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.read(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
+ 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)
+ request_string = []
+ request_string << "> #{request.method.upcase} #{request.path}"
+ request.to_hash.each do |key, value|
+ request_string << "> #{key}: #{Array(value).first}"
+ end
+ request << "" << request.body if request.body
+ request_string.join("\n")
+ end
+
+ def response_to_string(response)
+ headers = response.to_hash
+ body = response.body
+
+ response_string = []
+ response_string << "HTTP/1.1 #{response.code} #{response.message}"
+
+ headers["content-length"] = [body.bytesize.to_s] if body
+
+ headers.each do |header, value|
+ response_string << "#{header}: #{value.join(", ")}"
+ end
+
+ response_string << "" << body
+
+ response_string = response_string.join("\n")
+ if response_string.respond_to?(:force_encoding)
+ response_string.force_encoding("ASCII-8BIT")
+ else
+ response_string
+ end
+ end
+
+ def binwrite(path, contents)
+ File.open(path, "wb:ASCII-8BIT") {|f| f.write(contents) }
+ end
+ end
+
+ def request_with_vcr(request, *args, &block)
+ handler = request.instance_eval do
+ remove_instance_variable(:@__vcr_request_handler) if defined?(@__vcr_request_handler)
+ end || RequestHandler.new(self, request, *args, &block)
+
+ handler.handle_request
+ end
+
+ alias_method :request_without_vcr, :request
+ alias_method :request, :request_with_vcr
+end
+
+# Replace Net::HTTP with our VCR subclass
+::Net.class_eval do
+ remove_const(:HTTP)
+ const_set(:HTTP, BundlerVCRHTTP)
+end
diff --git a/spec/bundler/support/artifice/windows.rb b/spec/bundler/support/artifice/windows.rb
new file mode 100644
index 0000000000..f39b2c6d53
--- /dev/null
+++ b/spec/bundler/support/artifice/windows.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require File.expand_path("../../path.rb", __FILE__)
+include Spec::Path
+
+$LOAD_PATH.unshift Dir[base_system_gems.join("gems/artifice*/lib")].first.to_s
+$LOAD_PATH.unshift(*Dir[base_system_gems.join("gems/rack-*/lib")])
+$LOAD_PATH.unshift Dir[base_system_gems.join("gems/tilt*/lib")].first.to_s
+$LOAD_PATH.unshift Dir[base_system_gems.join("gems/sinatra*/lib")].first.to_s
+require "artifice"
+require "sinatra/base"
+
+Artifice.deactivate
+
+class Windows < Sinatra::Base
+ set :raise_errors, true
+ set :show_exceptions, false
+
+ helpers do
+ def gem_repo
+ Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"] || Spec::Path.gem_repo1)
+ end
+ end
+
+ files = ["specs.4.8.gz",
+ "prerelease_specs.4.8.gz",
+ "quick/Marshal.4.8/rcov-1.0-mswin32.gemspec.rz",
+ "gems/rcov-1.0-mswin32.gem"]
+
+ files.each do |file|
+ get "/#{file}" do
+ File.read gem_repo.join(file)
+ end
+ end
+
+ get "/gems/rcov-1.0-x86-mswin32.gem" do
+ halt 404
+ end
+
+ get "/api/v1/dependencies" do
+ halt 404
+ end
+
+ get "/versions" do
+ halt 500
+ end
+end
+
+Artifice.activate_with(Windows)
diff --git a/spec/bundler/support/builders.rb b/spec/bundler/support/builders.rb
new file mode 100644
index 0000000000..97134a045a
--- /dev/null
+++ b/spec/bundler/support/builders.rb
@@ -0,0 +1,819 @@
+# frozen_string_literal: true
+
+require "bundler/shared_helpers"
+require "shellwords"
+
+module Spec
+ module Builders
+ def self.constantize(name)
+ name.delete("-").upcase
+ end
+
+ def v(version)
+ Gem::Version.new(version)
+ end
+
+ def pl(platform)
+ Gem::Platform.new(platform)
+ end
+
+ def build_repo1
+ build_repo gem_repo1 do
+ build_gem "rack", %w[0.9.1 1.0.0] do |s|
+ s.executables = "rackup"
+ s.post_install_message = "Rack's post install message"
+ end
+
+ build_gem "thin" do |s|
+ s.add_dependency "rack"
+ s.post_install_message = "Thin's post install message"
+ end
+
+ build_gem "rack-obama" do |s|
+ s.add_dependency "rack"
+ s.post_install_message = "Rack-obama's post install message"
+ end
+
+ build_gem "rack_middleware", "1.0" do |s|
+ s.add_dependency "rack", "0.9.1"
+ end
+
+ build_gem "rails", "2.3.2" do |s|
+ s.executables = "rails"
+ s.add_dependency "rake", "10.0.2"
+ s.add_dependency "actionpack", "2.3.2"
+ s.add_dependency "activerecord", "2.3.2"
+ s.add_dependency "actionmailer", "2.3.2"
+ s.add_dependency "activeresource", "2.3.2"
+ end
+ build_gem "actionpack", "2.3.2" do |s|
+ s.add_dependency "activesupport", "2.3.2"
+ end
+ build_gem "activerecord", ["2.3.1", "2.3.2"] do |s|
+ s.add_dependency "activesupport", "2.3.2"
+ end
+ build_gem "actionmailer", "2.3.2" do |s|
+ s.add_dependency "activesupport", "2.3.2"
+ end
+ build_gem "activeresource", "2.3.2" do |s|
+ s.add_dependency "activesupport", "2.3.2"
+ end
+ build_gem "activesupport", %w[1.2.3 2.3.2 2.3.5]
+
+ build_gem "activemerchant" do |s|
+ s.add_dependency "activesupport", ">= 2.0.0"
+ end
+
+ build_gem "rails_fail" do |s|
+ s.add_dependency "activesupport", "= 1.2.3"
+ end
+
+ build_gem "missing_dep" do |s|
+ s.add_dependency "not_here"
+ end
+
+ build_gem "rspec", "1.2.7", :no_default => true do |s|
+ s.write "lib/spec.rb", "SPEC = '1.2.7'"
+ end
+
+ build_gem "rack-test", :no_default => true do |s|
+ s.write "lib/rack/test.rb", "RACK_TEST = '1.0'"
+ 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}'"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "java"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 JAVA'"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "ruby"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 RUBY'"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "x86-mswin32"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 MSWIN'"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "x86-mingw32"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "x64-mingw32"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "x86-darwin-100"
+ s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 x86-darwin-100'"
+ end
+
+ build_gem "only_java", "1.0" do |s|
+ s.platform = "java"
+ s.write "lib/only_java.rb", "ONLY_JAVA = '1.0.0 JAVA'"
+ end
+
+ build_gem "only_java", "1.1" do |s|
+ s.platform = "java"
+ s.write "lib/only_java.rb", "ONLY_JAVA = '1.1.0 JAVA'"
+ end
+
+ build_gem "nokogiri", "1.4.2"
+ build_gem "nokogiri", "1.4.2" do |s|
+ s.platform = "java"
+ s.write "lib/nokogiri.rb", "NOKOGIRI = '1.4.2 JAVA'"
+ s.add_dependency "weakling", ">= 0.0.3"
+ end
+
+ build_gem "laduradura", "5.15.2"
+ build_gem "laduradura", "5.15.2" do |s|
+ s.platform = "java"
+ s.write "lib/laduradura.rb", "LADURADURA = '5.15.2 JAVA'"
+ end
+ build_gem "laduradura", "5.15.3" do |s|
+ s.platform = "java"
+ s.write "lib/laduradura.rb", "LADURADURA = '5.15.2 JAVA'"
+ end
+
+ build_gem "weakling", "0.0.3"
+
+ build_gem "terranova", "8"
+
+ build_gem "duradura", "7.0"
+
+ build_gem "multiple_versioned_deps" do |s|
+ s.add_dependency "weakling", ">= 0.0.1", "< 0.1"
+ end
+
+ build_gem "not_released", "1.0.pre"
+
+ build_gem "has_prerelease", "1.0"
+ build_gem "has_prerelease", "1.1.pre"
+
+ build_gem "with_development_dependency" do |s|
+ s.add_development_dependency "activesupport", "= 2.3.5"
+ end
+
+ build_gem "with_license" do |s|
+ s.license = "MIT"
+ end
+
+ build_gem "with_implicit_rake_dep" do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/implicit_rake_dep.rb", "w") do |f|
+ f.puts "IMPLICIT_RAKE_DEP = 'YES'"
+ end
+ end
+ RUBY
+ end
+
+ build_gem "another_implicit_rake_dep" do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ 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
+
+ build_gem "very_simple_binary", &:add_c_extension
+ build_gem "simple_binary", &:add_c_extension
+
+ build_gem "bundler", "0.9" do |s|
+ s.executables = "bundle"
+ s.write "bin/bundle", "puts 'FAIL'"
+ end
+
+ # The bundler 0.8 gem has a rubygems plugin that always loads :(
+ build_gem "bundler", "0.8.1" do |s|
+ s.write "lib/bundler/omg.rb", ""
+ s.write "lib/rubygems_plugin.rb", "require 'bundler/omg' ; puts 'FAIL'"
+ end
+
+ build_gem "bundler_dep" do |s|
+ s.add_dependency "bundler"
+ end
+
+ # The yard gem iterates over Gem.source_index looking for plugins
+ build_gem "yard" do |s|
+ s.write "lib/yard.rb", <<-Y
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("1.8.10")
+ specs = Gem::Specification
+ else
+ specs = Gem.source_index.find_name('')
+ end
+ specs.sort_by(&:name).each do |gem|
+ puts gem.full_name
+ end
+ Y
+ end
+
+ # The rcov gem is platform mswin32, but has no arch
+ build_gem "rcov" do |s|
+ s.platform = Gem::Platform.new([nil, "mswin32", nil])
+ s.write "lib/rcov.rb", "RCOV = '1.0.0'"
+ end
+
+ build_gem "net-ssh"
+ build_gem "net-sftp", "1.1.1" do |s|
+ s.add_dependency "net-ssh", ">= 1.0.0", "< 1.99.0"
+ end
+
+ # Test complicated gem dependencies for install
+ build_gem "net_a" do |s|
+ s.add_dependency "net_b"
+ s.add_dependency "net_build_extensions"
+ end
+
+ build_gem "net_b"
+
+ build_gem "net_build_extensions" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/net_build_extensions.rb", "w") do |f|
+ f.puts "NET_BUILD_EXTENSIONS = 'YES'"
+ end
+ end
+ RUBY
+ end
+
+ build_gem "net_c" do |s|
+ s.add_dependency "net_a"
+ s.add_dependency "net_d"
+ end
+
+ build_gem "net_d"
+
+ build_gem "net_e" do |s|
+ s.add_dependency "net_d"
+ end
+
+ # Capistrano did this (at least until version 2.5.10)
+ # RubyGems 2.2 doesn't allow the specifying of a dependency twice
+ # See https://github.com/rubygems/rubygems/commit/03dbac93a3396a80db258d9bc63500333c25bd2f
+ build_gem "double_deps", "1.0", :skip_validation => true do |s|
+ s.add_dependency "net-ssh", ">= 1.0.0"
+ s.add_dependency "net-ssh"
+ end
+
+ build_gem "foo"
+
+ # A minimal fake pry console
+ build_gem "pry" do |s|
+ s.write "lib/pry.rb", <<-RUBY
+ class Pry
+ class << self
+ def toplevel_binding
+ unless defined?(@toplevel_binding) && @toplevel_binding
+ TOPLEVEL_BINDING.eval %{
+ def self.__pry__; binding; end
+ Pry.instance_variable_set(:@toplevel_binding, __pry__)
+ class << self; undef __pry__; end
+ }
+ end
+ @toplevel_binding.eval('private')
+ @toplevel_binding
+ end
+
+ def __pry__
+ while line = gets
+ begin
+ puts eval(line, toplevel_binding).inspect.sub(/^"(.*)"$/, '=> \\1')
+ rescue Exception => e
+ puts "\#{e.class}: \#{e.message}"
+ puts e.backtrace.first
+ end
+ end
+ end
+ alias start __pry__
+ end
+ end
+ RUBY
+ end
+ end
+ end
+
+ def build_repo2(&blk)
+ FileUtils.rm_rf gem_repo2
+ FileUtils.cp_r gem_repo1, gem_repo2
+ update_repo2(&blk) if block_given?
+ end
+
+ def build_repo3
+ build_repo gem_repo3 do
+ build_gem "rack"
+ end
+ FileUtils.rm_rf Dir[gem_repo3("prerelease*")]
+ end
+
+ # A repo that has no pre-installed gems included. (The caller completely
+ # determines the contents with the block.)
+ def build_repo4(&blk)
+ FileUtils.rm_rf gem_repo4
+ build_repo(gem_repo4, &blk)
+ end
+
+ def update_repo4(&blk)
+ update_repo(gem_repo4, &blk)
+ end
+
+ def update_repo2
+ update_repo gem_repo2 do
+ build_gem "rack", "1.2" do |s|
+ s.executables = "rackup"
+ end
+ yield if block_given?
+ end
+ end
+
+ def build_security_repo
+ build_repo security_repo do
+ build_gem "rack"
+
+ build_gem "signed_gem" do |s|
+ cert = "signing-cert.pem"
+ pkey = "signing-pkey.pem"
+ s.write cert, TEST_CERT
+ s.write pkey, TEST_PKEY
+ s.signing_key = pkey
+ s.cert_chain = [cert]
+ end
+ end
+ end
+
+ def build_repo(path, &blk)
+ return if File.directory?(path)
+ rake_path = Dir["#{Path.base_system_gems}/**/rake*.gem"].first
+
+ if rake_path.nil?
+ Spec::Path.base_system_gems.rmtree
+ Spec::Rubygems.setup
+ rake_path = Dir["#{Path.base_system_gems}/**/rake*.gem"].first
+ end
+
+ if rake_path
+ FileUtils.mkdir_p("#{path}/gems")
+ FileUtils.cp rake_path, "#{path}/gems/"
+ else
+ abort "Your test gems are missing! Run `rm -rf #{tmp}` and try again."
+ end
+
+ update_repo(path, &blk)
+ end
+
+ def update_repo(path)
+ if path == gem_repo1 && caller.first.split(" ").last == "`build_repo`"
+ raise "Updating gem_repo1 is unsupported -- use gem_repo2 instead"
+ end
+ return unless block_given?
+ @_build_path = "#{path}/gems"
+ @_build_repo = File.basename(path)
+ yield
+ with_gem_path_as Path.base_system_gems do
+ Dir.chdir(path) { gem_command! :generate_index }
+ end
+ ensure
+ @_build_path = nil
+ @_build_repo = nil
+ end
+
+ def build_index(&block)
+ index = Bundler::Index.new
+ IndexBuilder.run(index, &block) if block_given?
+ index
+ end
+
+ def build_spec(name, version = "0.0.1", platform = nil, &block)
+ Array(version).map do |v|
+ Gem::Specification.new do |s|
+ s.name = name
+ s.version = Gem::Version.new(v)
+ s.platform = platform
+ s.authors = ["no one in particular"]
+ s.summary = "a gemspec used only for testing"
+ DepBuilder.run(s, &block) if block_given?
+ end
+ 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
+
+ def build_gem(name, *args, &blk)
+ build_with(GemBuilder, name, args, &blk)
+ end
+
+ def build_git(name, *args, &block)
+ opts = args.last.is_a?(Hash) ? args.last : {}
+ builder = opts[:bare] ? GitBareBuilder : GitBuilder
+ spec = build_with(builder, name, args, &block)
+ GitReader.new(opts[:path] || lib_path(spec.full_name))
+ end
+
+ def update_git(name, *args, &block)
+ opts = args.last.is_a?(Hash) ? args.last : {}
+ spec = build_with(GitUpdater, name, args, &block)
+ GitReader.new(opts[:path] || lib_path(spec.full_name))
+ end
+
+ def build_plugin(name, *args, &blk)
+ build_with(PluginBuilder, name, args, &blk)
+ end
+
+ private
+
+ def build_with(builder, name, args, &blk)
+ @_build_path ||= nil
+ @_build_repo ||= nil
+ options = args.last.is_a?(Hash) ? args.pop : {}
+ versions = args.last || "1.0"
+ spec = nil
+
+ options[:path] ||= @_build_path
+ options[:source] ||= @_build_repo
+
+ Array(versions).each do |version|
+ spec = builder.new(self, name, version)
+ spec.authors = ["no one"] if !spec.authors || spec.authors.empty?
+ yield spec if block_given?
+ spec._build(options)
+ end
+
+ spec
+ end
+
+ class IndexBuilder
+ include Builders
+
+ def self.run(index, &block)
+ new(index).run(&block)
+ end
+
+ def initialize(index)
+ @index = index
+ end
+
+ def run(&block)
+ instance_eval(&block)
+ end
+
+ def gem(*args, &block)
+ build_spec(*args, &block).each do |s|
+ @index << s
+ end
+ end
+
+ def platforms(platforms)
+ platforms.split(/\s+/).each do |platform|
+ platform.gsub!(/^(mswin32)$/, 'x86-\1')
+ yield Gem::Platform.new(platform)
+ end
+ end
+
+ def versions(versions)
+ versions.split(/\s+/).each {|version| yield v(version) }
+ end
+ end
+
+ class DepBuilder
+ include Builders
+
+ def self.run(spec, &block)
+ new(spec).run(&block)
+ end
+
+ def initialize(spec)
+ @spec = spec
+ end
+
+ def run(&block)
+ instance_eval(&block)
+ end
+
+ def runtime(name, requirements)
+ @spec.add_runtime_dependency(name, requirements)
+ end
+
+ def development(name, requirements)
+ @spec.add_development_dependency(name, requirements)
+ end
+
+ def required_ruby_version=(*reqs)
+ @spec.required_ruby_version = *reqs
+ end
+
+ alias_method :dep, :runtime
+ end
+
+ class LibBuilder
+ def initialize(context, name, version)
+ @context = context
+ @name = name
+ @spec = Gem::Specification.new do |s|
+ s.name = name
+ s.version = version
+ s.summary = "This is just a fake gem for testing"
+ s.description = "This is a completely fake gem, for testing purposes."
+ s.author = "no one"
+ s.email = "foo@bar.baz"
+ s.homepage = "http://example.com"
+ s.license = "MIT"
+ end
+ @files = {}
+ end
+
+ def method_missing(*args, &blk)
+ @spec.send(*args, &blk)
+ end
+
+ def write(file, source = "")
+ @files[file] = source
+ end
+
+ def executables=(val)
+ @spec.executables = Array(val)
+ @spec.executables.each do |file|
+ executable = "#{@spec.bindir}/#{file}"
+ shebang = if Bundler.current_ruby.jruby?
+ "#!/usr/bin/env jruby\n"
+ else
+ "#!/usr/bin/env ruby\n"
+ end
+ @spec.files << executable
+ write executable, "#{shebang}require '#{@name}' ; puts #{Builders.constantize(@name)}"
+ end
+ end
+
+ def add_c_extension
+ require_paths << "ext"
+ extensions << "ext/extconf.rb"
+ write "ext/extconf.rb", <<-RUBY
+ require "mkmf"
+
+
+ # exit 1 unless with_config("simple")
+
+ extension_name = "#{name}_c"
+ if extra_lib_dir = with_config("ext-lib")
+ # add extra libpath if --with-ext-lib is
+ # passed in as a build_arg
+ dir_config extension_name, nil, extra_lib_dir
+ else
+ dir_config extension_name
+ end
+ create_makefile extension_name
+ RUBY
+ write "ext/#{name}.c", <<-C
+ #include "ruby.h"
+
+ void Init_#{name}_c() {
+ rb_define_module("#{Builders.constantize(name)}_IN_C");
+ }
+ C
+ end
+
+ def _build(options)
+ path = options[:path] || _default_path
+
+ if options[:rubygems_version]
+ @spec.rubygems_version = options[:rubygems_version]
+ def @spec.mark_version; end
+
+ def @spec.validate(*); end
+ end
+
+ case options[:gemspec]
+ when false
+ # do nothing
+ when :yaml
+ @files["#{name}.gemspec"] = @spec.to_yaml
+ else
+ @files["#{name}.gemspec"] = @spec.to_ruby
+ end
+
+ unless options[:no_default]
+ gem_source = options[:source] || "path@#{path}"
+ @files = _default_files.
+ merge("lib/#{name}/source.rb" => "#{Builders.constantize(name)}_SOURCE = #{gem_source.to_s.dump}").
+ merge(@files)
+ end
+
+ @spec.authors = ["no one"]
+
+ @files.each do |file, source|
+ file = Pathname.new(path).join(file)
+ FileUtils.mkdir_p(file.dirname)
+ File.open(file, "w") {|f| f.puts source }
+ end
+ @spec.files = @files.keys
+ path
+ end
+
+ def _default_files
+ @_default_files ||= begin
+ platform_string = " #{@spec.platform}" unless @spec.platform == Gem::Platform::RUBY
+ { "lib/#{name}.rb" => "#{Builders.constantize(name)} = '#{version}#{platform_string}'" }
+ end
+ end
+
+ def _default_path
+ @context.tmp("libs", @spec.full_name)
+ end
+ end
+
+ class GitBuilder < LibBuilder
+ def _build(options)
+ path = options[:path] || _default_path
+ source = options[:source] || "git@#{path}"
+ super(options.merge(:path => path, :source => source))
+ Dir.chdir(path) do
+ `git init`
+ `git add *`
+ `git config user.email "lol@wut.com"`
+ `git config user.name "lolwut"`
+ `git commit -m 'OMG INITIAL COMMIT'`
+ end
+ end
+ end
+
+ class GitBareBuilder < LibBuilder
+ def _build(options)
+ path = options[:path] || _default_path
+ super(options.merge(:path => path))
+ Dir.chdir(path) do
+ `git init --bare`
+ end
+ end
+ end
+
+ class GitUpdater < LibBuilder
+ def silently(str)
+ `#{str} 2>#{Bundler::NULL}`
+ end
+
+ def _build(options)
+ libpath = options[:path] || _default_path
+ update_gemspec = options[:gemspec] || false
+ source = options[:source] || "git@#{libpath}"
+
+ Dir.chdir(libpath) do
+ silently "git checkout master"
+
+ if branch = options[:branch]
+ raise "You can't specify `master` as the branch" if branch == "master"
+ escaped_branch = Shellwords.shellescape(branch)
+
+ if `git branch | grep #{escaped_branch}`.empty?
+ silently("git branch #{escaped_branch}")
+ end
+
+ silently("git checkout #{escaped_branch}")
+ elsif tag = options[:tag]
+ `git tag #{Shellwords.shellescape(tag)}`
+ elsif options[:remote]
+ silently("git remote add origin file://#{options[:remote]}")
+ elsif options[:push]
+ silently("git push origin #{options[:push]}")
+ end
+
+ current_ref = `git rev-parse HEAD`.strip
+ _default_files.keys.each do |path|
+ _default_files[path] += "\n#{Builders.constantize(name)}_PREV_REF = '#{current_ref}'"
+ end
+ super(options.merge(:path => libpath, :gemspec => update_gemspec, :source => source))
+ `git add *`
+ `git commit -m "BUMP"`
+ end
+ end
+ end
+
+ class GitReader
+ attr_reader :path
+
+ def initialize(path)
+ @path = path
+ end
+
+ def ref_for(ref, len = nil)
+ ref = git "rev-parse #{ref}"
+ ref = ref[0..len] if len
+ ref
+ end
+
+ private
+
+ def git(cmd)
+ Bundler::SharedHelpers.with_clean_git_env do
+ Dir.chdir(@path) { `git #{cmd}`.strip }
+ end
+ end
+ end
+
+ class GemBuilder < LibBuilder
+ def _build(opts)
+ lib_path = super(opts.merge(:path => @context.tmp(".tmp/#{@spec.full_name}"), :no_default => opts[:no_default]))
+ destination = opts[:path] || _default_path
+ Dir.chdir(lib_path) do
+ FileUtils.mkdir_p(destination)
+
+ @spec.authors = ["that guy"] if !@spec.authors || @spec.authors.empty?
+
+ Bundler.rubygems.build(@spec, opts[:skip_validation])
+ end
+ gem_path = File.expand_path("#{@spec.full_name}.gem", lib_path)
+ if opts[:to_system]
+ @context.system_gems gem_path, :keep_path => true
+ elsif opts[:to_bundle]
+ @context.system_gems gem_path, :path => :bundle_path, :keep_path => true
+ else
+ FileUtils.mv(gem_path, destination)
+ end
+ end
+
+ def _default_path
+ @context.gem_repo1("gems")
+ end
+ end
+
+ class PluginBuilder < GemBuilder
+ def _default_files
+ @_default_files ||= super.merge("plugins.rb" => "")
+ end
+ end
+
+ TEST_CERT = <<-CERT.gsub(/^\s*/, "")
+ -----BEGIN CERTIFICATE-----
+ MIIDMjCCAhqgAwIBAgIBATANBgkqhkiG9w0BAQUFADAnMQwwCgYDVQQDDAN5b3Ux
+ FzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMB4XDTE1MDIwODAwMTIyM1oXDTQyMDYy
+ NTAwMTIyM1owJzEMMAoGA1UEAwwDeW91MRcwFQYKCZImiZPyLGQBGRYHZXhhbXBs
+ ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANlvFdpN43c4DMS9Jo06
+ m0a7k3bQ3HWQ1yrYhZMi77F1F73NpBknYHIzDktQpGn6hs/4QFJT4m4zNEBF47UL
+ jHU5nTK5rjkS3niGYUjvh3ZEzVeo9zHUlD/UwflDo4ALl3TSo2KY/KdPS/UTdLXL
+ ajkQvaVJtEDgBPE3DPhlj5whp+Ik3mDHej7qpV6F502leAwYaFyOtlEG/ZGNG+nZ
+ L0clH0j77HpP42AylHDi+vakEM3xcjo9BeWQ6Vkboic93c9RTt6CWBWxMQP7Nol1
+ MOebz9XOSQclxpxWteXNfPRtMdAhmRl76SMI8ywzThNPpa4EH/yz34ftebVOgKyM
+ nd0CAwEAAaNpMGcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFA7D
+ n9qo0np23qi3aOYuAAPn/5IdMBYGA1UdEQQPMA2BC3lvdUBleGFtcGxlMBYGA1Ud
+ EgQPMA2BC3lvdUBleGFtcGxlMA0GCSqGSIb3DQEBBQUAA4IBAQA7Gyk62sWOUX/N
+ vk4tJrgKESph6Ns8+E36A7n3jt8zCep8ldzMvwTWquf9iqhsC68FilEoaDnUlWw7
+ d6oNuaFkv7zfrWGLlvqQJC+cu2X5EpcCksg5oRp8VNbwJysJ6JgwosxzROII8eXc
+ R+j1j6mDvQYqig2QOnzf480pjaqbP+tspfDFZbhKPrgM3Blrb3ZYuFpv4zkqI7aB
+ 6fuk2DUhNO1CuwrJA84TqC+jGo73bDKaT5hrIDiaJRrN5+zcWja2uEWrj5jSbep4
+ oXdEdyH73hOHMBP40uds3PqnUsxEJhzjB2sCCe1geV24kw9J4m7EQXPVkUKDgKrt
+ LlpDmOoo
+ -----END CERTIFICATE-----
+ CERT
+
+ TEST_PKEY = <<-PKEY.gsub(/^\s*/, "")
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIEowIBAAKCAQEA2W8V2k3jdzgMxL0mjTqbRruTdtDcdZDXKtiFkyLvsXUXvc2k
+ GSdgcjMOS1CkafqGz/hAUlPibjM0QEXjtQuMdTmdMrmuORLeeIZhSO+HdkTNV6j3
+ MdSUP9TB+UOjgAuXdNKjYpj8p09L9RN0tctqORC9pUm0QOAE8TcM+GWPnCGn4iTe
+ YMd6PuqlXoXnTaV4DBhoXI62UQb9kY0b6dkvRyUfSPvsek/jYDKUcOL69qQQzfFy
+ Oj0F5ZDpWRuiJz3dz1FO3oJYFbExA/s2iXUw55vP1c5JByXGnFa15c189G0x0CGZ
+ GXvpIwjzLDNOE0+lrgQf/LPfh+15tU6ArIyd3QIDAQABAoIBACbDqz20TS1gDMa2
+ gj0DidNedbflHKjJHdNBru7Ad8NHgOgR1YO2hXdWquG6itVqGMbTF4SV9/R1pIcg
+ 7qvEV1I+50u31tvOBWOvcYCzU48+TO2n7gowQA3xPHPYHzog1uu48fAOHl0lwgD7
+ av9OOK3b0jO5pC08wyTOD73pPWU0NrkTh2+N364leIi1pNuI1z4V+nEuIIm7XpVd
+ 5V4sXidMTiEMJwE6baEDfTjHKaoRndXrrPo3ryIXmcX7Ag1SwAQwF5fBCRToCgIx
+ dszEZB1bJD5gA6r+eGnJLB/F60nK607az5o3EdguoB2LKa6q6krpaRCmZU5svvoF
+ J7xgBPECgYEA8RIzHAQ3zbaibKdnllBLIgsqGdSzebTLKheFuigRotEV3Or/z5Lg
+ k/nVnThWVkTOSRqXTNpJAME6a4KTdcVSxYP+SdZVO1esazHrGb7xPVb7MWSE1cqp
+ WEk3Yy8OUOPoPQMc4dyGzd30Mi8IBB6gnFIYOTrpUo0XtkBv8rGGhfsCgYEA5uYn
+ 6QgL4NqNT84IXylmMb5ia3iBt6lhxI/A28CDtQvfScl4eYK0IjBwdfG6E1vJgyzg
+ nJzv3xEVo9bz+Kq7CcThWpK5JQaPnsV0Q74Wjk0ShHet15txOdJuKImnh5F6lylC
+ GTLR9gnptytfMH/uuw4ws0Q2kcg4l5NHKOWOnAcCgYEAvAwIVkhsB0n59Wu4gCZu
+ FUZENxYWUk/XUyQ6KnZrG2ih90xQ8+iMyqFOIm/52R2fFKNrdoWoALC6E3ct8+ZS
+ pMRLrelFXx8K3it4SwMJR2H8XBEfFW4bH0UtsW7Zafv+AunUs9LETP5gKG1LgXsq
+ qgXX43yy2LQ61O365YPZfdUCgYBVbTvA3MhARbvYldrFEnUL3GtfZbNgdxuD9Mee
+ xig0eJMBIrgfBLuOlqtVB70XYnM4xAbKCso4loKSHnofO1N99siFkRlM2JOUY2tz
+ kMWZmmxKdFjuF0WZ5f/5oYxI/QsFGC+rUQEbbWl56mMKd5qkvEhKWudxoklF0yiV
+ ufC8SwKBgDWb8iWqWN5a/kfvKoxFcDM74UHk/SeKMGAL+ujKLf58F+CbweM5pX9C
+ EUsxeoUEraVWTiyFVNqD81rCdceus9TdBj0ZIK1vUttaRZyrMAwF0uQSfjtxsOpd
+ l69BkyvzjgDPkmOHVGiSZDLi3YDvypbUpo6LOy4v5rVg5U2F/A0v
+ -----END RSA PRIVATE KEY-----
+ PKEY
+ end
+end
diff --git a/spec/bundler/support/code_climate.rb b/spec/bundler/support/code_climate.rb
new file mode 100644
index 0000000000..a15442cabe
--- /dev/null
+++ b/spec/bundler/support/code_climate.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Spec
+ module CodeClimate
+ def self.setup
+ require "codeclimate-test-reporter"
+ ::CodeClimate::TestReporter.start
+ configure_exclusions
+ rescue LoadError
+ # it's fine if CodeClimate isn't set up
+ nil
+ end
+
+ def self.configure_exclusions
+ SimpleCov.start do
+ add_filter "/bin/"
+ add_filter "/lib/bundler/man/"
+ add_filter "/lib/bundler/vendor/"
+ add_filter "/man/"
+ add_filter "/pkg/"
+ add_filter "/spec/"
+ add_filter "/tmp/"
+ end
+ end
+ end
+end
diff --git a/spec/bundler/support/command_execution.rb b/spec/bundler/support/command_execution.rb
new file mode 100644
index 0000000000..556285ac52
--- /dev/null
+++ b/spec/bundler/support/command_execution.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require "support/helpers"
+require "support/path"
+
+module Spec
+ CommandExecution = Struct.new(:command, :working_directory, :exitstatus, :stdout, :stderr) do
+ include RSpec::Matchers::Composable
+
+ def to_s
+ c = Shellwords.shellsplit(command.strip).map {|s| s.include?("\n") ? " \\\n <<EOS\n#{s.gsub(/^/, " ").chomp}\nEOS" : Shellwords.shellescape(s) }
+ c = c.reduce("") do |acc, elem|
+ concat = acc + " " + elem
+
+ last_line = concat.match(/.*\z/)[0]
+ if last_line.size >= 100
+ acc + " \\\n " + elem
+ else
+ concat
+ end
+ end
+ "$ #{c.strip}"
+ end
+ alias_method :inspect, :to_s
+
+ def stdboth
+ @stdboth ||= [stderr, stdout].join("\n").strip
+ end
+
+ def bundler_err
+ if Bundler::VERSION.start_with?("1.")
+ stdout
+ else
+ stderr
+ end
+ end
+
+ def to_s_verbose
+ [
+ to_s,
+ stdout,
+ stderr,
+ exitstatus ? "# $? => #{exitstatus}" : "",
+ ].reject(&:empty?).join("\n")
+ end
+
+ def success?
+ return true unless exitstatus
+ exitstatus == 0
+ end
+
+ def failure?
+ return true unless exitstatus
+ exitstatus > 0
+ end
+ end
+end
diff --git a/spec/bundler/support/hax.rb b/spec/bundler/support/hax.rb
new file mode 100644
index 0000000000..b14e4a5943
--- /dev/null
+++ b/spec/bundler/support/hax.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+require "rubygems"
+
+module Gem
+ if version = ENV["BUNDLER_SPEC_RUBYGEMS_VERSION"]
+ remove_const(:VERSION) if const_defined?(:VERSION)
+ VERSION = version
+ end
+
+ class Platform
+ @local = new(ENV["BUNDLER_SPEC_PLATFORM"]) if ENV["BUNDLER_SPEC_PLATFORM"]
+ end
+ @platforms = [Gem::Platform::RUBY, Gem::Platform.local]
+
+ if defined?(@path_to_default_spec_map) && !ENV["BUNDLER_SPEC_KEEP_DEFAULT_BUNDLER_GEM"]
+ @path_to_default_spec_map.delete_if do |_path, spec|
+ spec.name == "bundler"
+ end
+ end
+end
+
+if ENV["BUNDLER_SPEC_VERSION"]
+ module Bundler
+ remove_const(:VERSION) if const_defined?(:VERSION)
+ VERSION = ENV["BUNDLER_SPEC_VERSION"].dup
+ end
+end
+
+if ENV["BUNDLER_SPEC_WINDOWS"] == "true"
+ require "bundler/constants"
+
+ module Bundler
+ remove_const :WINDOWS if defined?(WINDOWS)
+ WINDOWS = true
+ end
+end
+
+class Object
+ if ENV["BUNDLER_SPEC_RUBY_ENGINE"]
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE != "jruby" && ENV["BUNDLER_SPEC_RUBY_ENGINE"] == "jruby"
+ begin
+ # this has to be done up front because psych will try to load a .jar
+ # if it thinks its on jruby
+ require "psych"
+ rescue LoadError
+ nil
+ end
+ end
+
+ remove_const :RUBY_ENGINE if defined?(RUBY_ENGINE)
+ RUBY_ENGINE = ENV["BUNDLER_SPEC_RUBY_ENGINE"]
+
+ if RUBY_ENGINE == "jruby"
+ remove_const :JRUBY_VERSION if defined?(JRUBY_VERSION)
+ JRUBY_VERSION = ENV["BUNDLER_SPEC_RUBY_ENGINE_VERSION"]
+ end
+ end
+end
+
+if ENV["BUNDLER_SPEC_IGNORE_COMPATIBILITY_GUARD"]
+ $LOADED_FEATURES << File.expand_path("../../../bundler/compatibility_guard.rb", __FILE__)
+ $LOADED_FEATURES << File.expand_path("../../../bundler/compatibility_guard", __FILE__)
+ $LOADED_FEATURES << "bundler/compatibility_guard.rb"
+ $LOADED_FEATURES << "bundler/compatibility_guard"
+ require "bundler/compatibility_guard"
+end
diff --git a/spec/bundler/support/helpers.rb b/spec/bundler/support/helpers.rb
new file mode 100644
index 0000000000..b027e7a922
--- /dev/null
+++ b/spec/bundler/support/helpers.rb
@@ -0,0 +1,600 @@
+# frozen_string_literal: true
+
+require "open3"
+
+module Spec
+ module Helpers
+ def reset!
+ Dir.glob("#{tmp}/{gems/*,*}", File::FNM_DOTMATCH).each do |dir|
+ next if %w[base remote1 gems rubygems . ..].include?(File.basename(dir))
+ if ENV["BUNDLER_SUDO_TESTS"]
+ `sudo rm -rf "#{dir}"`
+ else
+ FileUtils.rm_rf(dir)
+ end
+ end
+ FileUtils.mkdir_p(home)
+ FileUtils.mkdir_p(tmpdir)
+ Bundler.reset!
+ Bundler.ui = nil
+ Bundler.ui # force it to initialize
+ end
+
+ def self.bang(method)
+ define_method("#{method}!") do |*args, &blk|
+ send(method, *args, &blk).tap do
+ unless last_command.success?
+ raise RuntimeError,
+ "Invoking #{method}!(#{args.map(&:inspect).join(", ")}) failed:\n#{last_command.stdboth}",
+ caller.drop_while {|bt| bt.start_with?(__FILE__) }
+ end
+ end
+ end
+ end
+
+ def the_bundle(*args)
+ TheBundle.new(*args)
+ end
+
+ def last_command
+ @command_executions.last || raise("There is no last command")
+ end
+
+ def out
+ last_command.stdboth
+ end
+
+ def err
+ last_command.stderr
+ end
+
+ def exitstatus
+ last_command.exitstatus
+ end
+
+ def bundle_update_requires_all?
+ Bundler::VERSION.start_with?("1.") ? nil : true
+ end
+
+ def in_app_root(&blk)
+ Dir.chdir(bundled_app, &blk)
+ end
+
+ def in_app_root2(&blk)
+ Dir.chdir(bundled_app2, &blk)
+ end
+
+ def in_app_root_custom(root, &blk)
+ Dir.chdir(root, &blk)
+ end
+
+ def run(cmd, *args)
+ opts = args.last.is_a?(Hash) ? args.pop : {}
+ groups = args.map(&:inspect).join(", ")
+ setup = "require 'rubygems' ; require 'bundler' ; Bundler.setup(#{groups})\n"
+ ruby(setup + cmd, opts)
+ end
+ bang :run
+
+ def load_error_run(ruby, name, *args)
+ cmd = <<-RUBY
+ begin
+ #{ruby}
+ rescue LoadError => e
+ $stderr.puts "ZOMG LOAD ERROR" if e.message.include?("-- #{name}")
+ end
+ RUBY
+ opts = args.last.is_a?(Hash) ? args.pop : {}
+ args += [opts]
+ run(cmd, *args)
+ end
+
+ def lib
+ root.join("lib")
+ end
+
+ def spec
+ spec_dir.to_s
+ end
+
+ def bundle(cmd, options = {})
+ with_sudo = options.delete(:sudo)
+ sudo = with_sudo == :preserve_env ? "sudo -E" : "sudo" if with_sudo
+
+ bundle_bin = options.delete("bundle_bin") || bindir.join("bundle")
+
+ if system_bundler = options.delete(:system_bundler)
+ bundle_bin = "-S bundle"
+ end
+
+ env = options.delete(:env) || {}
+ env["PATH"].gsub!("#{Path.root}/exe", "") if env["PATH"] && system_bundler
+
+ requires = options.delete(:requires) || []
+ requires << "support/hax"
+
+ artifice = options.delete(:artifice) do
+ if RSpec.current_example.metadata[:realworld]
+ "vcr"
+ else
+ "fail"
+ end
+ end
+ if artifice
+ requires << File.expand_path("../artifice/#{artifice}", __FILE__)
+ end
+
+ requires_str = requires.map {|r| "-r#{r}" }.join(" ")
+
+ load_path = []
+ load_path << lib unless system_bundler
+ load_path << spec
+ load_path_str = "-I#{load_path.join(File::PATH_SEPARATOR)}"
+
+ env = env.map {|k, v| "#{k}='#{v}'" }.join(" ")
+
+ args = options.map do |k, v|
+ case v
+ when nil
+ next
+ when true
+ " --#{k}"
+ when false
+ " --no-#{k}"
+ else
+ " --#{k} #{v}"
+ end
+ end.join
+
+ cmd = "#{env} #{sudo} #{Gem.ruby} #{load_path_str} #{requires_str} #{bundle_bin} #{cmd}#{args}"
+ sys_exec(cmd) {|i, o, thr| yield i, o, thr if block_given? }
+ end
+ bang :bundle
+
+ def forgotten_command_line_options(options)
+ remembered = Bundler::VERSION.split(".", 2).first == "1"
+ options = options.map do |k, v|
+ k = Array(k)[remembered ? 0 : -1]
+ v = '""' if v && v.to_s.empty?
+ [k, v]
+ end
+ return Hash[options] if remembered
+ options.each do |k, v|
+ if v.nil?
+ bundle! "config --delete #{k}"
+ else
+ bundle! "config --local #{k} #{v}"
+ end
+ end
+ {}
+ end
+
+ def bundler(cmd, options = {})
+ options["bundle_bin"] = bindir.join("bundler")
+ bundle(cmd, options)
+ end
+
+ def bundle_ruby(options = {})
+ options["bundle_bin"] = bindir.join("bundle_ruby")
+ bundle("", options)
+ end
+
+ def ruby(ruby, options = {})
+ env = (options.delete(:env) || {}).map {|k, v| "#{k}='#{v}' " }.join
+ ruby = ruby.gsub(/["`\$]/) {|m| "\\#{m}" }
+ lib_option = options[:no_lib] ? "" : " -I#{lib}"
+ sys_exec(%(#{env}#{Gem.ruby}#{lib_option} -e "#{ruby}"))
+ end
+ bang :ruby
+
+ def load_error_ruby(ruby, name, opts = {})
+ ruby(<<-R)
+ begin
+ #{ruby}
+ rescue LoadError => e
+ $stderr.puts "ZOMG LOAD ERROR"# if e.message.include?("-- #{name}")
+ end
+ R
+ end
+
+ def gembin(cmd)
+ lib = File.expand_path("../../../lib", __FILE__)
+ old = ENV["RUBYOPT"]
+ ENV["RUBYOPT"] = "#{ENV["RUBYOPT"]} -I#{lib}"
+ cmd = bundled_app("bin/#{cmd}") unless cmd.to_s.include?("/")
+ sys_exec(cmd.to_s)
+ ensure
+ ENV["RUBYOPT"] = old
+ end
+
+ def gem_command(command, args = "", options = {})
+ if command == :exec && !options[:no_quote]
+ args = args.gsub(/(?=")/, "\\")
+ args = %("#{args}")
+ end
+ gem = ENV["BUNDLE_GEM"] || "#{Gem.ruby} -rrubygems -S gem --backtrace"
+ sys_exec("#{gem} #{command} #{args}")
+ end
+ bang :gem_command
+
+ def rake
+ "#{Gem.ruby} -S #{ENV["GEM_PATH"]}/bin/rake"
+ end
+
+ def sys_exec(cmd)
+ command_execution = CommandExecution.new(cmd.to_s, Dir.pwd)
+
+ Open3.popen3(cmd.to_s) do |stdin, stdout, stderr, wait_thr|
+ yield stdin, stdout, wait_thr if block_given?
+ stdin.close
+
+ command_execution.exitstatus = wait_thr && wait_thr.value.exitstatus
+ command_execution.stdout = Thread.new { stdout.read }.value.strip
+ command_execution.stderr = Thread.new { stderr.read }.value.strip
+ end
+
+ (@command_executions ||= []) << command_execution
+
+ command_execution.stdout
+ end
+ bang :sys_exec
+
+ def config(config = nil, path = bundled_app(".bundle/config"))
+ return YAML.load_file(path) unless config
+ FileUtils.mkdir_p(File.dirname(path))
+ File.open(path, "w") do |f|
+ f.puts config.to_yaml
+ end
+ config
+ end
+
+ def global_config(config = nil)
+ config(config, home(".bundle/config"))
+ end
+
+ def create_file(*args)
+ path = bundled_app(args.shift)
+ path = args.shift if args.first.is_a?(Pathname)
+ str = args.shift || ""
+ path.dirname.mkpath
+ File.open(path.to_s, "w") do |f|
+ f.puts strip_whitespace(str)
+ end
+ end
+
+ def gemfile(*args)
+ if args.empty?
+ File.open("Gemfile", "r", &:read)
+ else
+ create_file("Gemfile", *args)
+ end
+ end
+
+ def lockfile(*args)
+ if args.empty?
+ File.open("Gemfile.lock", "r", &:read)
+ else
+ create_file("Gemfile.lock", *args)
+ end
+ end
+
+ def strip_whitespace(str)
+ # Trim the leading spaces
+ spaces = str[/\A\s+/, 0] || ""
+ str.gsub(/^#{spaces}/, "")
+ end
+
+ def normalize_uri_file(str)
+ # URI::File of Ruby 2.6 normalize localhost variable with file protocol.
+ if defined?(URI::File)
+ str.gsub(%r{file:\/\/localhost}, "file://")
+ else
+ str
+ end
+ end
+
+ def install_gemfile(*args)
+ gemfile(*args)
+ opts = args.last.is_a?(Hash) ? args.last : {}
+ opts[:retry] ||= 0
+ bundle :install, opts
+ end
+ bang :install_gemfile
+
+ def lock_gemfile(*args)
+ gemfile(*args)
+ opts = args.last.is_a?(Hash) ? args.last : {}
+ opts[:retry] ||= 0
+ bundle :lock, opts
+ end
+
+ def install_gems(*gems)
+ options = gems.last.is_a?(Hash) ? gems.pop : {}
+ gem_repo = options.fetch(:gem_repo) { gem_repo1 }
+ gems.each do |g|
+ path = if g == :bundler
+ Dir.chdir(root) { gem_command! :build, gemspec.to_s }
+ bundler_path = if ruby_core?
+ root + "lib/bundler-#{Bundler::VERSION}.gem"
+ else
+ root + "bundler-#{Bundler::VERSION}.gem"
+ end
+ elsif g.to_s =~ %r{\A/.*\.gem\z}
+ g
+ else
+ "#{gem_repo}/gems/#{g}.gem"
+ end
+
+ raise "OMG `#{path}` does not exist!" unless File.exist?(path)
+
+ if Gem::VERSION < "2.0.0"
+ gem_command! :install, "--no-rdoc --no-ri --ignore-dependencies '#{path}'"
+ else
+ gem_command! :install, "--no-document --ignore-dependencies '#{path}'"
+ end
+ bundler_path && bundler_path.rmtree
+ end
+ end
+
+ alias_method :install_gem, :install_gems
+
+ def with_gem_path_as(path)
+ backup = ENV.to_hash
+ ENV["GEM_HOME"] = path.to_s
+ ENV["GEM_PATH"] = path.to_s
+ ENV["BUNDLER_ORIG_GEM_PATH"] = nil
+ yield
+ ensure
+ ENV.replace(backup)
+ end
+
+ def with_path_as(path)
+ backup = ENV.to_hash
+ ENV["PATH"] = path.to_s
+ ENV["BUNDLER_ORIG_PATH"] = nil
+ yield
+ ensure
+ ENV.replace(backup)
+ end
+
+ def with_path_added(path)
+ with_path_as(path.to_s + ":" + ENV["PATH"]) do
+ yield
+ end
+ end
+
+ def break_git!
+ FileUtils.mkdir_p(tmp("broken_path"))
+ File.open(tmp("broken_path/git"), "w", 0o755) do |f|
+ f.puts "#!/usr/bin/env ruby\nSTDERR.puts 'This is not the git you are looking for'\nexit 1"
+ end
+
+ ENV["PATH"] = "#{tmp("broken_path")}:#{ENV["PATH"]}"
+ end
+
+ def with_fake_man
+ FileUtils.mkdir_p(tmp("fake_man"))
+ File.open(tmp("fake_man/man"), "w", 0o755) do |f|
+ f.puts "#!/usr/bin/env ruby\nputs ARGV.inspect\n"
+ end
+ with_path_added(tmp("fake_man")) { yield }
+ end
+
+ def system_gems(*gems)
+ opts = gems.last.is_a?(Hash) ? gems.last : {}
+ path = opts.fetch(:path, system_gem_path)
+ if path == :bundle_path
+ path = ruby!(<<-RUBY)
+ require "bundler"
+ begin
+ puts Bundler.bundle_path
+ rescue Bundler::GemfileNotFound
+ ENV["BUNDLE_GEMFILE"] = "Gemfile"
+ retry
+ end
+
+ RUBY
+ end
+ gems = gems.flatten
+
+ unless opts[:keep_path]
+ FileUtils.rm_rf(path)
+ FileUtils.mkdir_p(path)
+ end
+
+ Gem.clear_paths
+
+ env_backup = ENV.to_hash
+ ENV["GEM_HOME"] = path.to_s
+ ENV["GEM_PATH"] = path.to_s
+ ENV["BUNDLER_ORIG_GEM_PATH"] = nil
+
+ install_gems(*gems)
+ return unless block_given?
+ begin
+ yield
+ ensure
+ ENV.replace(env_backup)
+ end
+ end
+
+ def realworld_system_gems(*gems)
+ gems = gems.flatten
+
+ FileUtils.rm_rf(system_gem_path)
+ FileUtils.mkdir_p(system_gem_path)
+
+ Gem.clear_paths
+
+ gem_home = ENV["GEM_HOME"]
+ gem_path = ENV["GEM_PATH"]
+ path = ENV["PATH"]
+ ENV["GEM_HOME"] = system_gem_path.to_s
+ ENV["GEM_PATH"] = system_gem_path.to_s
+
+ gems.each do |gem|
+ gem_command :install, "--no-rdoc --no-ri #{gem}"
+ end
+ return unless block_given?
+ begin
+ yield
+ ensure
+ ENV["GEM_HOME"] = gem_home
+ ENV["GEM_PATH"] = gem_path
+ ENV["PATH"] = path
+ end
+ end
+
+ def cache_gems(*gems)
+ 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"
+ raise "OMG `#{path}` does not exist!" unless File.exist?(path)
+ FileUtils.cp(path, "#{bundled_app}/vendor/cache")
+ end
+ end
+
+ def simulate_new_machine
+ system_gems []
+ FileUtils.rm_rf system_gem_path
+ FileUtils.rm_rf bundled_app(".bundle")
+ end
+
+ def simulate_platform(platform)
+ old = ENV["BUNDLER_SPEC_PLATFORM"]
+ ENV["BUNDLER_SPEC_PLATFORM"] = platform.to_s
+ yield if block_given?
+ ensure
+ ENV["BUNDLER_SPEC_PLATFORM"] = old if block_given?
+ end
+
+ def simulate_ruby_version(version)
+ return if version == RUBY_VERSION
+ old = ENV["BUNDLER_SPEC_RUBY_VERSION"]
+ ENV["BUNDLER_SPEC_RUBY_VERSION"] = version
+ yield if block_given?
+ ensure
+ ENV["BUNDLER_SPEC_RUBY_VERSION"] = old if block_given?
+ end
+
+ def simulate_ruby_engine(engine, version = "1.6.0")
+ return if engine == local_ruby_engine
+
+ old = ENV["BUNDLER_SPEC_RUBY_ENGINE"]
+ ENV["BUNDLER_SPEC_RUBY_ENGINE"] = engine
+ old_version = ENV["BUNDLER_SPEC_RUBY_ENGINE_VERSION"]
+ ENV["BUNDLER_SPEC_RUBY_ENGINE_VERSION"] = version
+ yield if block_given?
+ ensure
+ ENV["BUNDLER_SPEC_RUBY_ENGINE"] = old if block_given?
+ ENV["BUNDLER_SPEC_RUBY_ENGINE_VERSION"] = old_version if block_given?
+ end
+
+ def simulate_bundler_version(version)
+ old = ENV["BUNDLER_SPEC_VERSION"]
+ ENV["BUNDLER_SPEC_VERSION"] = version.to_s
+ yield if block_given?
+ ensure
+ ENV["BUNDLER_SPEC_VERSION"] = old if block_given?
+ end
+
+ def simulate_rubygems_version(version)
+ old = ENV["BUNDLER_SPEC_RUBYGEMS_VERSION"]
+ ENV["BUNDLER_SPEC_RUBYGEMS_VERSION"] = version.to_s
+ yield if block_given?
+ ensure
+ ENV["BUNDLER_SPEC_RUBYGEMS_VERSION"] = old if block_given?
+ end
+
+ def simulate_windows(platform = mswin)
+ old = ENV["BUNDLER_SPEC_WINDOWS"]
+ ENV["BUNDLER_SPEC_WINDOWS"] = "true"
+ simulate_platform platform do
+ yield
+ end
+ ensure
+ ENV["BUNDLER_SPEC_WINDOWS"] = old
+ end
+
+ def revision_for(path)
+ Dir.chdir(path) { `git rev-parse HEAD`.strip }
+ end
+
+ def capture_output
+ capture(:stdout)
+ end
+
+ def with_read_only(pattern)
+ chmod = lambda do |dirmode, filemode|
+ lambda do |f|
+ mode = File.directory?(f) ? dirmode : filemode
+ File.chmod(mode, f)
+ end
+ end
+
+ Dir[pattern].each(&chmod[0o555, 0o444])
+ yield
+ ensure
+ Dir[pattern].each(&chmod[0o755, 0o644])
+ end
+
+ def process_file(pathname)
+ changed_lines = pathname.readlines.map do |line|
+ yield line
+ end
+ File.open(pathname, "w") {|file| file.puts(changed_lines.join) }
+ end
+
+ def with_env_vars(env_hash, &block)
+ current_values = {}
+ env_hash.each do |k, v|
+ current_values[k] = ENV[k]
+ ENV[k] = v
+ end
+ block.call if block_given?
+ env_hash.each do |k, _|
+ ENV[k] = current_values[k]
+ end
+ end
+
+ def require_rack
+ # need to hack, so we can require rack
+ old_gem_home = ENV["GEM_HOME"]
+ ENV["GEM_HOME"] = Spec::Path.base_system_gems.to_s
+ require "rack"
+ ENV["GEM_HOME"] = old_gem_home
+ end
+
+ def wait_for_server(host, port, seconds = 15)
+ tries = 0
+ sleep 0.5
+ TCPSocket.new(host, port)
+ rescue => e
+ raise(e) if tries > (seconds * 2)
+ tries += 1
+ retry
+ end
+
+ def find_unused_port
+ port = 21_453
+ begin
+ port += 1 while TCPSocket.new("127.0.0.1", port)
+ rescue
+ false
+ end
+ port
+ end
+
+ def bundler_fileutils
+ if RUBY_VERSION >= "2.4"
+ ::Bundler::FileUtils
+ else
+ ::FileUtils
+ end
+ end
+ end
+end
diff --git a/spec/bundler/support/indexes.rb b/spec/bundler/support/indexes.rb
new file mode 100644
index 0000000000..69f8d9f679
--- /dev/null
+++ b/spec/bundler/support/indexes.rb
@@ -0,0 +1,421 @@
+# frozen_string_literal: true
+
+module Spec
+ module Indexes
+ def dep(name, reqs = nil)
+ @deps ||= []
+ @deps << Bundler::Dependency.new(name, reqs)
+ end
+
+ def platform(*args)
+ @platforms ||= []
+ @platforms.concat args.map {|p| Gem::Platform.new(p) }
+ end
+
+ alias_method :platforms, :platform
+
+ def resolve(args = [])
+ @platforms ||= ["ruby"]
+ deps = []
+ default_source = instance_double("Bundler::Source::Rubygems", :specs => @index)
+ source_requirements = { :default => default_source }
+ @deps.each do |d|
+ @platforms.each do |p|
+ source_requirements[d.name] = d.source = default_source
+ deps << Bundler::DepProxy.new(d, p)
+ end
+ end
+ source_requirements ||= {}
+ Bundler::Resolver.resolve(deps, @index, source_requirements, *args)
+ end
+
+ def should_resolve_as(specs)
+ got = resolve
+ got = got.map(&:full_name).sort
+ expect(got).to eq(specs.sort)
+ end
+
+ def should_resolve_and_include(specs, args = [])
+ got = resolve(args)
+ got = got.map(&:full_name).sort
+ specs.each do |s|
+ expect(got).to include(s)
+ end
+ end
+
+ def should_conflict_on(names)
+ got = resolve
+ flunk "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
+
+ def locked(*args)
+ Bundler::SpecSet.new(args.map do |name, version|
+ gem(name, version)
+ end)
+ end
+
+ 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|
+ 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]
+ end
+
+ def an_awesome_index
+ build_index do
+ gem "rack", %w[0.8 0.9 0.9.1 0.9.2 1.0 1.1]
+ gem "rack-mount", %w[0.4 0.5 0.5.1 0.5.2 0.6]
+
+ # --- Pre-release support
+ gem "rubygems\0", ["1.3.2"]
+
+ # --- Rails
+ versions "1.2.3 2.2.3 2.3.5 3.0.0.beta 3.0.0.beta1" do |version|
+ gem "activesupport", version
+ gem "actionpack", version do
+ dep "activesupport", version
+ if version >= v("3.0.0.beta")
+ dep "rack", "~> 1.1"
+ dep "rack-mount", ">= 0.5"
+ elsif version > v("2.3") then dep "rack", "~> 1.0.0"
+ elsif version > v("2.0.0") then dep "rack", "~> 0.9.0"
+ end
+ end
+ gem "activerecord", version do
+ dep "activesupport", version
+ dep "arel", ">= 0.2" if version >= v("3.0.0.beta")
+ end
+ gem "actionmailer", version do
+ dep "activesupport", version
+ dep "actionmailer", version
+ end
+ if version < v("3.0.0.beta")
+ gem "railties", version do
+ dep "activerecord", version
+ dep "actionpack", version
+ dep "actionmailer", version
+ dep "activesupport", version
+ end
+ else
+ gem "railties", version
+ gem "rails", version do
+ dep "activerecord", version
+ dep "actionpack", version
+ dep "actionmailer", version
+ dep "activesupport", version
+ dep "railties", version
+ end
+ end
+ end
+
+ versions "1.0 1.2 1.2.1 1.2.2 1.3 1.3.0.1 1.3.5 1.4.0 1.4.2 1.4.2.1" do |version|
+ platforms "ruby java mswin32 mingw32 x64-mingw32" do |platform|
+ 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")
+ end
+ end
+ end
+
+ versions "0.0.1 0.0.2 0.0.3" do |version|
+ gem "weakling", version
+ end
+
+ # --- Rails related
+ versions "1.2.3 2.2.3 2.3.5" do |version|
+ gem "activemerchant", version do
+ dep "activesupport", ">= #{version}"
+ end
+ end
+
+ gem "reform", ["1.0.0"] do
+ dep "activesupport", ">= 1.0.0.beta1"
+ end
+
+ gem "need-pre", ["1.0.0"] do
+ dep "activesupport", "~> 3.0.0.beta1"
+ end
+ end
+ end
+
+ # Builder 3.1.4 will activate first, but if all
+ # goes well, it should resolve to 3.0.4
+ def a_conflict_index
+ build_index do
+ gem "builder", %w[3.0.4 3.1.4]
+ gem("grape", "0.2.6") do
+ dep "builder", ">= 0"
+ end
+
+ versions "3.2.8 3.2.9 3.2.10 3.2.11" do |version|
+ gem("activemodel", version) do
+ dep "builder", "~> 3.0.0"
+ end
+ end
+
+ gem("my_app", "1.0.0") do
+ dep "activemodel", ">= 0"
+ dep "grape", ">= 0"
+ end
+ end
+ end
+
+ def a_complex_conflict_index
+ build_index do
+ gem("a", %w[1.0.2 1.1.4 1.2.0 1.4.0]) do
+ dep "d", ">= 0"
+ end
+
+ gem("d", %w[1.3.0 1.4.1]) do
+ dep "x", ">= 0"
+ end
+
+ gem "d", "0.9.8"
+
+ gem("b", "0.3.4") do
+ dep "a", ">= 1.5.0"
+ end
+
+ gem("b", "0.3.5") do
+ dep "a", ">= 1.2"
+ end
+
+ gem("b", "0.3.3") do
+ dep "a", "> 1.0"
+ end
+
+ versions "3.2 3.3" do |version|
+ gem("c", version) do
+ dep "a", "~> 1.0"
+ end
+ end
+
+ gem("my_app", "1.3.0") do
+ dep "c", ">= 4.0"
+ dep "b", ">= 0"
+ end
+
+ gem("my_app", "1.2.0") do
+ dep "c", "~> 3.3.0"
+ dep "b", "0.3.4"
+ end
+
+ gem("my_app", "1.1.0") do
+ dep "c", "~> 3.2.0"
+ dep "b", "0.3.5"
+ end
+ end
+ end
+
+ def index_with_conflict_on_child
+ build_index do
+ gem "json", %w[1.6.5 1.7.7 1.8.0]
+
+ gem("chef", "10.26") do
+ dep "json", [">= 1.4.4", "<= 1.7.7"]
+ end
+
+ gem("berkshelf", "2.0.7") do
+ dep "json", ">= 1.7.7"
+ end
+
+ gem("chef_app", "1.0.0") do
+ dep "berkshelf", "~> 2.0"
+ dep "chef", "~> 10.26"
+ end
+ end
+ end
+
+ # Issue #3459
+ def a_complicated_index
+ build_index do
+ gem "foo", %w[3.0.0 3.0.5] do
+ dep "qux", ["~> 3.1"]
+ dep "baz", ["< 9.0", ">= 5.0"]
+ dep "bar", ["~> 1.0"]
+ dep "grault", ["~> 3.1"]
+ end
+
+ gem "foo", "1.2.1" do
+ dep "baz", ["~> 4.2"]
+ dep "bar", ["~> 1.0"]
+ dep "qux", ["~> 3.1"]
+ dep "grault", ["~> 2.0"]
+ end
+
+ gem "bar", "1.0.5" do
+ dep "grault", ["~> 3.1"]
+ dep "baz", ["< 9", ">= 4.2"]
+ end
+
+ gem "bar", "1.0.3" do
+ dep "baz", ["< 9", ">= 4.2"]
+ dep "grault", ["~> 2.0"]
+ end
+
+ gem "baz", "8.2.10" do
+ dep "grault", ["~> 3.0"]
+ dep "garply", [">= 0.5.1", "~> 0.5"]
+ end
+
+ gem "baz", "5.0.2" do
+ dep "grault", ["~> 2.0"]
+ dep "garply", [">= 0.3.1"]
+ end
+
+ gem "baz", "4.2.0" do
+ dep "grault", ["~> 2.0"]
+ dep "garply", [">= 0.3.1"]
+ end
+
+ gem "grault", %w[2.6.3 3.1.1]
+
+ gem "garply", "0.5.1" do
+ dep "waldo", ["~> 0.1.3"]
+ end
+
+ gem "waldo", "0.1.5" do
+ dep "plugh", ["~> 0.6.0"]
+ end
+
+ gem "plugh", %w[0.6.3 0.6.11 0.7.0]
+
+ gem "qux", "3.2.21" do
+ dep "plugh", [">= 0.6.4", "~> 0.6"]
+ dep "corge", ["~> 1.0"]
+ end
+
+ gem "corge", "1.10.1"
+ end
+ end
+
+ def a_unresovable_child_index
+ build_index do
+ gem "json", %w[1.8.0]
+
+ gem("chef", "10.26") do
+ dep "json", [">= 1.4.4", "<= 1.7.7"]
+ end
+
+ gem("berkshelf", "2.0.7") do
+ dep "json", ">= 1.7.7"
+ end
+
+ gem("chef_app_error", "1.0.0") do
+ dep "berkshelf", "~> 2.0"
+ dep "chef", "~> 10.26"
+ end
+ end
+ end
+
+ def a_index_with_root_conflict_on_child
+ build_index do
+ gem "builder", %w[2.1.2 3.0.1 3.1.3]
+ gem "i18n", %w[0.4.1 0.4.2]
+
+ gem "activesupport", %w[3.0.0 3.0.1 3.0.5 3.1.7]
+
+ gem("activemodel", "3.0.5") do
+ dep "activesupport", "= 3.0.5"
+ dep "builder", "~> 2.1.2"
+ dep "i18n", "~> 0.4"
+ end
+
+ gem("activemodel", "3.0.0") do
+ dep "activesupport", "= 3.0.0"
+ dep "builder", "~> 2.1.2"
+ dep "i18n", "~> 0.4.1"
+ end
+
+ gem("activemodel", "3.1.3") do
+ dep "activesupport", "= 3.1.3"
+ dep "builder", "~> 2.1.2"
+ dep "i18n", "~> 0.5"
+ end
+
+ gem("activerecord", "3.0.0") do
+ dep "activesupport", "= 3.0.0"
+ dep "activemodel", "= 3.0.0"
+ end
+
+ gem("activerecord", "3.0.5") do
+ dep "activesupport", "= 3.0.5"
+ dep "activemodel", "= 3.0.5"
+ end
+
+ gem("activerecord", "3.0.9") do
+ dep "activesupport", "= 3.1.5"
+ dep "activemodel", "= 3.1.5"
+ end
+ end
+ end
+
+ def a_circular_index
+ build_index do
+ gem "rack", "1.0.1"
+ gem("foo", "0.2.6") do
+ dep "bar", ">= 0"
+ end
+
+ gem("bar", "1.0.0") do
+ dep "foo", ">= 0"
+ end
+
+ gem("circular_app", "1.0.0") do
+ dep "foo", ">= 0"
+ dep "bar", ">= 0"
+ end
+ end
+ end
+
+ def an_ambiguous_index
+ build_index do
+ gem("a", "1.0.0") do
+ dep "c", ">= 0"
+ end
+
+ gem("b", %w[0.5.0 1.0.0])
+
+ gem("b", "2.0.0") do
+ dep "c", "< 2.0.0"
+ end
+
+ gem("c", "1.0.0") do
+ dep "d", "1.0.0"
+ end
+
+ gem("c", "2.0.0") do
+ dep "d", "2.0.0"
+ end
+
+ gem("d", %w[1.0.0 2.0.0])
+ end
+ end
+
+ def optional_prereleases_index
+ build_index do
+ gem("a", %w[1.0.0])
+
+ gem("a", "2.0.0") do
+ dep "b", ">= 2.0.0.pre"
+ end
+
+ gem("b", %w[0.9.0 1.5.0 2.0.0.pre])
+
+ # --- Pre-release support
+ gem "rubygems\0", ["1.3.2"]
+ end
+ end
+ end
+end
diff --git a/spec/bundler/support/less_than_proc.rb b/spec/bundler/support/less_than_proc.rb
new file mode 100644
index 0000000000..ddac5458b7
--- /dev/null
+++ b/spec/bundler/support/less_than_proc.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class LessThanProc < Proc
+ attr_accessor :present
+
+ def self.with(present)
+ provided = Gem::Version.new(present.dup)
+ new do |required|
+ if required =~ /[=><~]/
+ !Gem::Requirement.new(required).satisfied_by?(provided)
+ else
+ provided < Gem::Version.new(required)
+ end
+ end.tap {|l| l.present = present }
+ end
+
+ def inspect
+ "\"=< #{present}\""
+ end
+end
diff --git a/spec/bundler/support/manpages.rb b/spec/bundler/support/manpages.rb
new file mode 100644
index 0000000000..ce1f72cc49
--- /dev/null
+++ b/spec/bundler/support/manpages.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Spec
+ module Manpages
+ def self.setup
+ man_path = Spec::Path.root.join("man")
+ return if man_path.children(false).select {|file| file.extname == ".ronn" }.all? do |man|
+ Dir[man_path.join("#{man.to_s[0..-6]}*.txt").to_s].any?
+ end
+
+ system(Spec::Path.root.join("bin", "rake").to_s, "man:build") || raise("Failed building man pages")
+ end
+ end
+end
diff --git a/spec/bundler/support/matchers.rb b/spec/bundler/support/matchers.rb
new file mode 100644
index 0000000000..8e17be3a02
--- /dev/null
+++ b/spec/bundler/support/matchers.rb
@@ -0,0 +1,246 @@
+# frozen_string_literal: true
+
+require "forwardable"
+require "support/the_bundle"
+module Spec
+ module Matchers
+ extend RSpec::Matchers
+
+ class Precondition
+ include RSpec::Matchers::Composable
+ extend Forwardable
+ def_delegators :failing_matcher,
+ :failure_message,
+ :actual,
+ :description,
+ :diffable?,
+ :expected,
+ :failure_message_when_negated
+
+ def initialize(matcher, preconditions)
+ @matcher = with_matchers_cloned(matcher)
+ @preconditions = with_matchers_cloned(preconditions)
+ @failure_index = nil
+ end
+
+ def matches?(target, &blk)
+ return false if @failure_index = @preconditions.index {|pc| !pc.matches?(target, &blk) }
+ @matcher.matches?(target, &blk)
+ end
+
+ def does_not_match?(target, &blk)
+ return false if @failure_index = @preconditions.index {|pc| !pc.matches?(target, &blk) }
+ if @matcher.respond_to?(:does_not_match?)
+ @matcher.does_not_match?(target, &blk)
+ else
+ !@matcher.matches?(target, &blk)
+ end
+ end
+
+ def expects_call_stack_jump?
+ @matcher.expects_call_stack_jump? || @preconditions.any?(&:expects_call_stack_jump)
+ end
+
+ def supports_block_expectations?
+ @matcher.supports_block_expectations? || @preconditions.any?(&:supports_block_expectations)
+ end
+
+ def failing_matcher
+ @failure_index ? @preconditions[@failure_index] : @matcher
+ end
+ end
+
+ def self.define_compound_matcher(matcher, preconditions, &declarations)
+ raise "Must have preconditions to define a compound matcher" if preconditions.empty?
+ define_method(matcher) do |*expected, &block_arg|
+ Precondition.new(
+ RSpec::Matchers::DSL::Matcher.new(matcher, declarations, self, *expected, &block_arg),
+ preconditions
+ )
+ end
+ end
+
+ MAJOR_DEPRECATION = /^\[DEPRECATED FOR 2\.0\]\s*/
+
+ RSpec::Matchers.define :lack_errors do
+ diffable
+ match do |actual|
+ actual.gsub(/#{MAJOR_DEPRECATION}.+[\n]?/, "") == ""
+ end
+ end
+
+ RSpec::Matchers.define :eq_err do |expected|
+ diffable
+ match do |actual|
+ actual.gsub(/#{MAJOR_DEPRECATION}.+[\n]?/, "") == expected
+ end
+ end
+
+ RSpec::Matchers.define :have_major_deprecation do |expected|
+ diffable
+ match do |actual|
+ deprecations = actual.split(MAJOR_DEPRECATION)
+
+ return !expected.nil? if deprecations.size <= 1
+ return true if expected.nil?
+
+ deprecations.any? do |d|
+ !d.empty? && values_match?(expected, d.strip)
+ end
+ end
+ end
+
+ RSpec::Matchers.define :have_dep do |*args|
+ dep = Bundler::Dependency.new(*args)
+
+ match do |actual|
+ actual.length == 1 && actual.all? {|d| d == dep }
+ end
+ end
+
+ RSpec::Matchers.define :have_gem do |*args|
+ match do |actual|
+ actual.length == args.length && actual.all? {|a| args.include?(a.full_name) }
+ end
+ end
+
+ RSpec::Matchers.define :have_rubyopts do |*args|
+ args = args.flatten
+ args = args.first.split(/\s+/) if args.size == 1
+
+ match do |actual|
+ actual = actual.split(/\s+/) if actual.is_a?(String)
+ args.all? {|arg| actual.include?(arg) } && actual.uniq.size == actual.size
+ end
+ end
+
+ RSpec::Matchers.define :be_sorted do
+ diffable
+ attr_reader :expected
+ match do |actual|
+ expected = block_arg ? actual.sort_by(&block_arg) : actual.sort
+ actual.==(expected).tap do
+ # HACK: since rspec won't show a diff when everything is a string
+ differ = RSpec::Support::Differ.new
+ @actual = differ.send(:object_to_string, actual)
+ @expected = differ.send(:object_to_string, expected)
+ end
+ end
+ end
+
+ define_compound_matcher :read_as, [exist] do |file_contents|
+ diffable
+
+ match do |actual|
+ @actual = Bundler.read_file(actual)
+ values_match?(file_contents, @actual)
+ end
+ end
+
+ def indent(string, padding = 4, indent_character = " ")
+ string.to_s.gsub(/^/, indent_character * padding).gsub("\t", " ")
+ end
+
+ define_compound_matcher :include_gems, [be_an_instance_of(Spec::TheBundle)] do |*names|
+ match do
+ opts = names.last.is_a?(Hash) ? names.pop : {}
+ source = opts.delete(:source)
+ groups = Array(opts[:groups])
+ groups << opts
+ @errors = names.map do |name|
+ name, version, platform = name.split(/\s+/)
+ version_const = name == "bundler" ? "Bundler::VERSION" : Spec::Builders.constantize(name)
+ begin
+ run! "require '#{name}.rb'; puts #{version_const}", *groups
+ rescue => e
+ next "#{name} is not installed:\n#{indent(e)}"
+ end
+ last_command.stdout.gsub!(/#{MAJOR_DEPRECATION}.*$/, "")
+ actual_version, actual_platform = last_command.stdout.strip.split(/\s+/, 2)
+ unless Gem::Version.new(actual_version) == Gem::Version.new(version)
+ next "#{name} was expected to be at version #{version} but was #{actual_version}"
+ end
+ unless actual_platform == platform
+ next "#{name} was expected to be of platform #{platform} but was #{actual_platform}"
+ end
+ next unless source
+ begin
+ source_const = "#{Spec::Builders.constantize(name)}_SOURCE"
+ run! "require '#{name}/source'; puts #{source_const}", *groups
+ rescue
+ next "#{name} does not have a source defined:\n#{indent(e)}"
+ end
+ last_command.stdout.gsub!(/#{MAJOR_DEPRECATION}.*$/, "")
+ unless last_command.stdout.strip == source
+ next "Expected #{name} (#{version}) to be installed from `#{source}`, was actually from `#{out}`"
+ end
+ end.compact
+
+ @errors.empty?
+ end
+
+ match_when_negated do
+ opts = names.last.is_a?(Hash) ? names.pop : {}
+ groups = Array(opts[:groups]) || []
+ @errors = names.map do |name|
+ name, version = name.split(/\s+/, 2)
+ begin
+ run <<-R, *(groups + [opts])
+ begin
+ require '#{name}'
+ puts #{Spec::Builders.constantize(name)}
+ rescue LoadError, NameError
+ puts "WIN"
+ end
+ R
+ rescue => e
+ next "checking for #{name} failed:\n#{e}"
+ end
+ next if last_command.stdout == "WIN"
+ next "expected #{name} to not be installed, but it was" if version.nil?
+ if Gem::Version.new(last_command.stdout) == Gem::Version.new(version)
+ next "expected #{name} (#{version}) not to be installed, but it was"
+ end
+ end.compact
+
+ @errors.empty?
+ end
+
+ failure_message do
+ super() + " but:\n" + @errors.map {|e| indent(e) }.join("\n")
+ end
+
+ failure_message_when_negated do
+ super() + " but:\n" + @errors.map {|e| indent(e) }.join("\n")
+ end
+ end
+ RSpec::Matchers.define_negated_matcher :not_include_gems, :include_gems
+ RSpec::Matchers.alias_matcher :include_gem, :include_gems
+
+ def have_lockfile(expected)
+ read_as(strip_whitespace(expected))
+ end
+
+ def plugin_should_be_installed(*names)
+ names.each do |name|
+ expect(Bundler::Plugin).to be_installed(name)
+ path = Pathname.new(Bundler::Plugin.installed?(name))
+ expect(path + "plugins.rb").to exist
+ end
+ end
+
+ def plugin_should_not_be_installed(*names)
+ names.each do |name|
+ expect(Bundler::Plugin).not_to be_installed(name)
+ end
+ end
+
+ def lockfile_should_be(expected)
+ expect(bundled_app("Gemfile.lock")).to read_as(normalize_uri_file(strip_whitespace(expected)))
+ end
+
+ def gemfile_should_be(expected)
+ expect(bundled_app("Gemfile")).to read_as(strip_whitespace(expected))
+ end
+ end
+end
diff --git a/spec/bundler/support/path.rb b/spec/bundler/support/path.rb
new file mode 100644
index 0000000000..76fa89d3cc
--- /dev/null
+++ b/spec/bundler/support/path.rb
@@ -0,0 +1,126 @@
+# frozen_string_literal: true
+
+require "pathname"
+
+module Spec
+ module Path
+ def root
+ @root ||= Pathname.new(ruby_core? ? "../../../.." : "../../..").expand_path(__FILE__)
+ end
+
+ def gemspec
+ @gemspec ||= root.join(ruby_core? ? "lib/bundler.gemspec" : "bundler.gemspec")
+ end
+
+ def bindir
+ @bindir ||= root.join(ruby_core? ? "libexec" : "exe")
+ end
+
+ def spec_dir
+ @spec_dir ||= root.join(ruby_core? ? "spec/bundler" : "spec")
+ end
+
+ def tmp(*path)
+ root.join("tmp", *path)
+ end
+
+ def home(*path)
+ tmp.join("home", *path)
+ end
+
+ def default_bundle_path(*path)
+ if Bundler::VERSION.split(".").first.to_i < 2
+ system_gem_path(*path)
+ else
+ bundled_app(*[".bundle", ENV.fetch("BUNDLER_SPEC_RUBY_ENGINE", Gem.ruby_engine), Gem::ConfigMap[:ruby_version], *path].compact)
+ end
+ end
+
+ def bundled_app(*path)
+ root = tmp.join("bundled_app")
+ FileUtils.mkdir_p(root)
+ root.join(*path)
+ end
+
+ alias_method :bundled_app1, :bundled_app
+
+ def bundled_app2(*path)
+ root = tmp.join("bundled_app2")
+ FileUtils.mkdir_p(root)
+ root.join(*path)
+ end
+
+ def vendored_gems(path = nil)
+ bundled_app(*["vendor/bundle", Gem.ruby_engine, Gem::ConfigMap[:ruby_version], path].compact)
+ end
+
+ def cached_gem(path)
+ bundled_app("vendor/cache/#{path}.gem")
+ end
+
+ def base_system_gems
+ tmp.join("gems/base")
+ end
+
+ def gem_repo1(*args)
+ tmp("gems/remote1", *args)
+ end
+
+ def gem_repo_missing(*args)
+ tmp("gems/missing", *args)
+ end
+
+ def gem_repo2(*args)
+ tmp("gems/remote2", *args)
+ end
+
+ def gem_repo3(*args)
+ tmp("gems/remote3", *args)
+ end
+
+ def gem_repo4(*args)
+ tmp("gems/remote4", *args)
+ end
+
+ def security_repo(*args)
+ tmp("gems/security_repo", *args)
+ end
+
+ def system_gem_path(*path)
+ tmp("gems/system", *path)
+ end
+
+ def lib_path(*args)
+ tmp("libs", *args)
+ end
+
+ def bundler_path
+ Pathname.new(File.expand_path(root.join("lib"), __FILE__))
+ end
+
+ def global_plugin_gem(*args)
+ home ".bundle", "plugin", "gems", *args
+ end
+
+ def local_plugin_gem(*args)
+ bundled_app ".bundle", "plugin", "gems", *args
+ end
+
+ def tmpdir(*args)
+ tmp "tmpdir", *args
+ end
+
+ def ruby_core?
+ # avoid to wornings
+ @ruby_core ||= nil
+
+ if @ruby_core.nil?
+ @ruby_core = true & (ENV["BUNDLE_RUBY"] && ENV["BUNDLE_GEM"])
+ else
+ @ruby_core
+ end
+ end
+
+ extend self
+ end
+end
diff --git a/spec/bundler/support/permissions.rb b/spec/bundler/support/permissions.rb
new file mode 100644
index 0000000000..b21ce3848d
--- /dev/null
+++ b/spec/bundler/support/permissions.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Spec
+ module Permissions
+ def with_umask(new_umask)
+ old_umask = File.umask(new_umask)
+ yield if block_given?
+ ensure
+ File.umask(old_umask)
+ end
+ end
+end
diff --git a/spec/bundler/support/platforms.rb b/spec/bundler/support/platforms.rb
new file mode 100644
index 0000000000..39040a61bd
--- /dev/null
+++ b/spec/bundler/support/platforms.rb
@@ -0,0 +1,116 @@
+# frozen_string_literal: true
+
+module Spec
+ module Platforms
+ include Bundler::GemHelpers
+
+ def rb
+ Gem::Platform::RUBY
+ end
+
+ def mac
+ Gem::Platform.new("x86-darwin-10")
+ end
+
+ def x64_mac
+ Gem::Platform.new("x86_64-darwin-15")
+ end
+
+ def java
+ Gem::Platform.new([nil, "java", nil])
+ end
+
+ def linux
+ Gem::Platform.new(["x86", "linux", nil])
+ end
+
+ def mswin
+ Gem::Platform.new(["x86", "mswin32", nil])
+ end
+
+ def mingw
+ Gem::Platform.new(["x86", "mingw32", nil])
+ end
+
+ def x64_mingw
+ Gem::Platform.new(["x64", "mingw32", nil])
+ end
+
+ def all_platforms
+ [rb, java, linux, mswin, mingw, x64_mingw]
+ end
+
+ def local
+ generic_local_platform
+ end
+
+ def specific_local_platform
+ Bundler.local_platform
+ end
+
+ def not_local
+ all_platforms.find {|p| p != generic_local_platform }
+ end
+
+ def local_tag
+ if RUBY_PLATFORM == "java"
+ :jruby
+ else
+ :ruby
+ end
+ end
+
+ def not_local_tag
+ [:ruby, :jruby].find {|tag| tag != local_tag }
+ end
+
+ def local_ruby_engine
+ ENV["BUNDLER_SPEC_RUBY_ENGINE"] || (defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby")
+ end
+
+ def local_engine_version
+ return ENV["BUNDLER_SPEC_RUBY_ENGINE_VERSION"] if ENV["BUNDLER_SPEC_RUBY_ENGINE_VERSION"]
+
+ case local_ruby_engine
+ when "ruby"
+ RUBY_VERSION
+ when "rbx"
+ Rubinius::VERSION
+ when "jruby"
+ JRUBY_VERSION
+ else
+ RUBY_ENGINE_VERSION
+ end
+ end
+
+ def not_local_engine_version
+ case not_local_tag
+ when :ruby
+ not_local_ruby_version
+ when :jruby
+ "1.6.1"
+ end
+ end
+
+ def not_local_ruby_version
+ "1.12"
+ end
+
+ def not_local_patchlevel
+ 9999
+ end
+
+ def lockfile_platforms(*platforms)
+ platforms = local_platforms if platforms.empty?
+ platforms.map(&:to_s).sort.join("\n ")
+ end
+
+ def local_platforms
+ if Bundler::VERSION.split(".").first.to_i > 1
+ [local, specific_local_platform]
+ else
+ [local]
+ end
+ end
+ end
+end
diff --git a/spec/bundler/support/rubygems_ext.rb b/spec/bundler/support/rubygems_ext.rb
new file mode 100644
index 0000000000..c18f7650fc
--- /dev/null
+++ b/spec/bundler/support/rubygems_ext.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require "rubygems/user_interaction"
+require "support/path" unless defined?(Spec::Path)
+
+module Spec
+ module Rubygems
+ DEPS = begin
+ deps = {
+ # rack 2.x requires Ruby version >= 2.2.2.
+ # artifice doesn't support rack 2.x now.
+ # TODO: revert to `< 2` once https://github.com/rack/rack/issues/1168 is
+ # addressed
+ "rack" => "1.6.6",
+ # rack-test 0.7.0 dropped 1.8.7 support
+ # https://github.com/rack-test/rack-test/issues/193#issuecomment-314230318
+ "rack-test" => "< 0.7.0",
+ "artifice" => "~> 0.6.0",
+ "compact_index" => "~> 0.11.0",
+ "sinatra" => "~> 1.4.7",
+ # Rake version has to be consistent for tests to pass
+ "rake" => "10.0.2",
+ # 3.0.0 breaks 1.9.2 specs
+ "builder" => "2.1.2",
+ }
+ # ruby-graphviz is used by the viz tests
+ deps["ruby-graphviz"] = nil if RUBY_VERSION >= "1.9.3"
+ deps
+ end
+
+ def self.setup
+ Gem.clear_paths
+
+ ENV["BUNDLE_PATH"] = nil
+ ENV["GEM_HOME"] = ENV["GEM_PATH"] = Path.base_system_gems.to_s
+ ENV["PATH"] = [Path.bindir, "#{Path.system_gem_path}/bin", ENV["PATH"]].join(File::PATH_SEPARATOR)
+
+ manifest = DEPS.to_a.sort_by(&:first).map {|k, v| "#{k} => #{v}\n" }
+ manifest_path = "#{Path.base_system_gems}/manifest.txt"
+ # it's OK if there are extra gems
+ if !File.exist?(manifest_path) || !(manifest - File.readlines(manifest_path)).empty?
+ FileUtils.rm_rf(Path.base_system_gems)
+ FileUtils.mkdir_p(Path.base_system_gems)
+ puts "installing gems for the tests to use..."
+ install_gems(DEPS)
+ File.open(manifest_path, "w") {|f| f << manifest.join }
+ end
+
+ ENV["HOME"] = Path.home.to_s
+ ENV["TMPDIR"] = Path.tmpdir.to_s
+
+ Gem::DefaultUserInteraction.ui = Gem::SilentUI.new
+ end
+
+ def self.install_gems(gems)
+ reqs, no_reqs = gems.partition {|_, req| !req.nil? && !req.split(" ").empty? }
+ # TODO: remove when we drop ruby 1.8.7-2.2.2 support
+ reqs = reqs.sort_by {|name, _| name == "rack" ? 0 : 1 }.sort_by {|name, _| name =~ /rack/ ? 0 : 1 }
+ no_reqs.map!(&:first)
+ reqs.map! {|name, req| "'#{name}:#{req}'" }
+ deps = reqs.concat(no_reqs).join(" ")
+ cmd = if Gem::VERSION < "2.0.0"
+ "gem install #{deps} --no-rdoc --no-ri --conservative"
+ else
+ "gem install #{deps} --no-document --conservative"
+ end
+ puts cmd
+ system(cmd) || raise("Installing gems #{deps} for the tests to use failed!")
+ end
+ end
+end
diff --git a/spec/bundler/support/silent_logger.rb b/spec/bundler/support/silent_logger.rb
new file mode 100644
index 0000000000..8665beb2c9
--- /dev/null
+++ b/spec/bundler/support/silent_logger.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require "logger"
+module Spec
+ class SilentLogger
+ (::Logger.instance_methods - Object.instance_methods).each do |logger_instance_method|
+ define_method(logger_instance_method) {|*args, &blk| }
+ end
+ end
+end
diff --git a/spec/bundler/support/sometimes.rb b/spec/bundler/support/sometimes.rb
new file mode 100644
index 0000000000..65a95ed59c
--- /dev/null
+++ b/spec/bundler/support/sometimes.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module Sometimes
+ def run_with_retries(example_to_run, retries)
+ example = RSpec.current_example
+ example.metadata[:retries] ||= retries
+
+ retries.times do |t|
+ example.metadata[:retried] = t + 1
+ example.instance_variable_set(:@exception, nil)
+ example_to_run.run
+ break unless example.exception
+ end
+
+ if e = example.exception
+ new_exception = e.exception(e.message + "[Retried #{retries} times]")
+ new_exception.set_backtrace e.backtrace
+ example.instance_variable_set(:@exception, new_exception)
+ end
+ end
+end
+
+RSpec.configure do |config|
+ config.include Sometimes
+ config.alias_example_to :sometimes, :sometimes => true
+ config.add_setting :sometimes_retry_count, :default => 5
+
+ config.around(:each, :sometimes => true) do |example|
+ retries = example.metadata[:retries] || RSpec.configuration.sometimes_retry_count
+ run_with_retries(example, retries)
+ end
+
+ config.after(:suite) do
+ message = proc do |color, text|
+ colored = RSpec::Core::Formatters::ConsoleCodes.wrap(text, color)
+ notification = RSpec::Core::Notifications::MessageNotification.new(colored)
+ formatter = RSpec.configuration.formatters.first
+ formatter.message(notification) if formatter.respond_to?(:message)
+ end
+
+ retried_examples = RSpec.world.example_groups.map do |g|
+ g.descendants.map do |d|
+ d.filtered_examples.select do |e|
+ e.metadata[:sometimes] && e.metadata.fetch(:retried, 1) > 1
+ end
+ end
+ end.flatten
+
+ message.call(retried_examples.empty? ? :green : :yellow, "\n\nRetried examples: #{retried_examples.count}")
+
+ retried_examples.each do |e|
+ message.call(:cyan, " #{e.full_description}")
+ path = RSpec::Core::Metadata.relative_path(e.location)
+ message.call(:cyan, " [#{e.metadata[:retried]}/#{e.metadata[:retries]}] " + path)
+ end
+ end
+end
diff --git a/spec/bundler/support/streams.rb b/spec/bundler/support/streams.rb
new file mode 100644
index 0000000000..a947eebf6f
--- /dev/null
+++ b/spec/bundler/support/streams.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require "stringio"
+
+def capture(*args)
+ opts = args.pop if args.last.is_a?(Hash)
+ opts ||= {}
+
+ args.map!(&:to_s)
+ begin
+ result = StringIO.new
+ result.close if opts[:closed]
+ args.each {|stream| eval "$#{stream} = result" }
+ yield
+ ensure
+ args.each {|stream| eval("$#{stream} = #{stream.upcase}") }
+ end
+ result.string
+end
diff --git a/spec/bundler/support/sudo.rb b/spec/bundler/support/sudo.rb
new file mode 100644
index 0000000000..04e9443945
--- /dev/null
+++ b/spec/bundler/support/sudo.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Spec
+ module Sudo
+ def self.present?
+ @which_sudo ||= Bundler.which("sudo")
+ 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/support/the_bundle.rb b/spec/bundler/support/the_bundle.rb
new file mode 100644
index 0000000000..c994eaae78
--- /dev/null
+++ b/spec/bundler/support/the_bundle.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require "support/helpers"
+require "support/path"
+
+module Spec
+ class TheBundle
+ include Spec::Helpers
+ include Spec::Path
+
+ attr_accessor :bundle_dir
+
+ def initialize(opts = {})
+ opts = opts.dup
+ @bundle_dir = Pathname.new(opts.delete(:bundle_dir) { bundled_app })
+ raise "Too many options! #{opts}" unless opts.empty?
+ end
+
+ def to_s
+ "the bundle"
+ end
+ alias_method :inspect, :to_s
+
+ def locked?
+ lockfile.file?
+ end
+
+ def lockfile
+ bundle_dir.join("Gemfile.lock")
+ end
+
+ def locked_gems
+ raise "Cannot read lockfile if it doesn't exist" unless locked?
+ Bundler::LockfileParser.new(lockfile.read)
+ end
+ end
+end
diff --git a/spec/bundler/update/gemfile_spec.rb b/spec/bundler/update/gemfile_spec.rb
new file mode 100644
index 0000000000..f59f3a2d32
--- /dev/null
+++ b/spec/bundler/update/gemfile_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle update" do
+ context "with --gemfile" do
+ it "finds the gemfile" do
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle! :install, :gemfile => bundled_app("NotGemfile")
+ bundle! :update, :gemfile => bundled_app("NotGemfile"), :all => bundle_update_requires_all?
+
+ # Specify BUNDLE_GEMFILE for `the_bundle`
+ # to retrieve the proper Gemfile
+ ENV["BUNDLE_GEMFILE"] = "NotGemfile"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ context "with gemfile set via config" do
+ before do
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle "config --local gemfile #{bundled_app("NotGemfile")}"
+ bundle! :install
+ end
+
+ it "uses the gemfile to update" do
+ bundle! "update", :all => bundle_update_requires_all?
+ bundle "list"
+
+ expect(out).to include("rack (1.0.0)")
+ end
+
+ it "uses the gemfile while in a subdirectory" do
+ bundled_app("subdir").mkpath
+ Dir.chdir(bundled_app("subdir")) do
+ bundle! "update", :all => bundle_update_requires_all?
+ bundle "list"
+
+ expect(out).to include("rack (1.0.0)")
+ end
+ end
+ end
+
+ context "with prefer_gems_rb set" do
+ before { bundle! "config prefer_gems_rb true" }
+
+ it "prefers gems.rb to Gemfile" do
+ create_file("gems.rb", "gem 'bundler'")
+ create_file("Gemfile", "raise 'wrong Gemfile!'")
+
+ bundle! :install
+ bundle! :update, :all => bundle_update_requires_all?
+
+ expect(bundled_app("gems.rb")).to be_file
+ expect(bundled_app("Gemfile.lock")).not_to be_file
+
+ expect(the_bundle).to include_gem "bundler #{Bundler::VERSION}"
+ end
+ end
+end
diff --git a/spec/bundler/update/gems/post_install_spec.rb b/spec/bundler/update/gems/post_install_spec.rb
new file mode 100644
index 0000000000..2fb3547806
--- /dev/null
+++ b/spec/bundler/update/gems/post_install_spec.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle update" do
+ let(:config) {}
+
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack', "< 1.0"
+ gem 'thin'
+ G
+
+ bundle! "config #{config}" if config
+
+ bundle! :install
+ end
+
+ shared_examples "a config observer" do
+ context "when ignore post-install messages for gem is set" do
+ let(:config) { "ignore_messages.rack true" }
+
+ it "doesn't display gem's post-install message" do
+ expect(out).not_to include("Rack's post install message")
+ end
+ end
+
+ context "when ignore post-install messages for all gems" do
+ let(:config) { "ignore_messages true" }
+
+ it "doesn't display any post-install messages" do
+ expect(out).not_to include("Post-install message")
+ end
+ end
+ end
+
+ shared_examples "a post-install message outputter" do
+ it "should display post-install messages for updated gems" do
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ end
+
+ it "should not display the post-install message for non-updated gems" do
+ expect(out).not_to include("Thin's post install message")
+ end
+ end
+
+ context "when listed gem is updated" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ gem 'thin'
+ G
+
+ bundle! :update, :all => bundle_update_requires_all?
+ end
+
+ it_behaves_like "a post-install message outputter"
+ it_behaves_like "a config observer"
+ end
+
+ context "when dependency triggers update" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack-obama'
+ gem 'thin'
+ G
+
+ bundle! :update, :all => bundle_update_requires_all?
+ end
+
+ it_behaves_like "a post-install message outputter"
+ it_behaves_like "a config observer"
+ end
+end
diff --git a/spec/bundler/update/git_spec.rb b/spec/bundler/update/git_spec.rb
new file mode 100644
index 0000000000..b4cbb79434
--- /dev/null
+++ b/spec/bundler/update/git_spec.rb
@@ -0,0 +1,374 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle update" do
+ describe "git sources" do
+ it "floats on a branch when :branch is used" do
+ build_git "foo", "1.0"
+ update_git "foo", :branch => "omg"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}", :branch => "omg" do
+ gem 'foo'
+ end
+ G
+
+ update_git "foo", :branch => "omg" do |s|
+ s.write "lib/foo.rb", "FOO = '1.1'"
+ end
+
+ bundle "update", :all => bundle_update_requires_all?
+
+ expect(the_bundle).to include_gems "foo 1.1"
+ end
+
+ it "updates correctly when you have like craziness" do
+ build_lib "activesupport", "3.0", :path => lib_path("rails/activesupport")
+ build_git "rails", "3.0", :path => lib_path("rails") do |s|
+ s.add_dependency "activesupport", "= 3.0"
+ end
+
+ install_gemfile! <<-G
+ gem "rails", :git => "#{lib_path("rails")}"
+ G
+
+ bundle! "update rails"
+ expect(the_bundle).to include_gems "rails 3.0", "activesupport 3.0"
+ end
+
+ it "floats on a branch when :branch is used and the source is specified in the update" do
+ build_git "foo", "1.0", :path => lib_path("foo")
+ update_git "foo", :branch => "omg", :path => lib_path("foo")
+
+ install_gemfile <<-G
+ git "#{lib_path("foo")}", :branch => "omg" do
+ gem 'foo'
+ end
+ G
+
+ update_git "foo", :branch => "omg", :path => lib_path("foo") do |s|
+ s.write "lib/foo.rb", "FOO = '1.1'"
+ end
+
+ bundle "update --source foo"
+
+ 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
+ build_git "foo", :path => lib_path("foo")
+ build_gem "bar", :to_bundle => true do |s|
+ s.add_dependency "foo"
+ end
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "bar"
+ G
+
+ update_git "foo", :path => lib_path("foo") do |s|
+ s.write "lib/foo.rb", "FOO = '1.1'"
+ end
+
+ bundle "update foo"
+
+ expect(the_bundle).to include_gems "foo 1.1"
+ end
+
+ it "notices when you change the repo url in the Gemfile" do
+ build_git "foo", :path => lib_path("foo_one")
+ build_git "foo", :path => lib_path("foo_two")
+
+ install_gemfile <<-G
+ gem "foo", "1.0", :git => "#{lib_path("foo_one")}"
+ G
+
+ FileUtils.rm_rf lib_path("foo_one")
+
+ install_gemfile <<-G
+ gem "foo", "1.0", :git => "#{lib_path("foo_two")}"
+ G
+
+ expect(err).to lack_errors
+ expect(out).to include("Fetching #{lib_path}/foo_two")
+ expect(out).to include("Bundle complete!")
+ end
+
+ it "fetches tags from the remote" do
+ build_git "foo"
+ @remote = build_git("bar", :bare => true)
+ update_git "foo", :remote => @remote.path
+ update_git "foo", :push => "master"
+
+ install_gemfile <<-G
+ gem 'foo', :git => "#{@remote.path}"
+ G
+
+ # Create a new tag on the remote that needs fetching
+ update_git "foo", :tag => "fubar"
+ update_git "foo", :push => "fubar"
+
+ gemfile <<-G
+ gem 'foo', :git => "#{@remote.path}", :tag => "fubar"
+ G
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ describe "with submodules" do
+ before :each do
+ build_repo4 do
+ build_gem "submodule" do |s|
+ s.write "lib/submodule.rb", "puts 'GEM'"
+ end
+ end
+
+ build_git "submodule", "1.0" do |s|
+ s.write "lib/submodule.rb", "puts 'GIT'"
+ end
+
+ build_git "has_submodule", "1.0" do |s|
+ s.add_dependency "submodule"
+ end
+
+ Dir.chdir(lib_path("has_submodule-1.0")) do
+ sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0"
+ `git commit -m "submodulator"`
+ end
+ end
+
+ it "it unlocks the source when submodules are added to a git source" do
+ install_gemfile <<-G
+ source "file:#{gem_repo4}"
+ git "#{lib_path("has_submodule-1.0")}" do
+ gem "has_submodule"
+ end
+ G
+
+ run "require 'submodule'"
+ expect(out).to eq("GEM")
+
+ install_gemfile <<-G
+ source "file:#{gem_repo4}"
+ git "#{lib_path("has_submodule-1.0")}", :submodules => true do
+ gem "has_submodule"
+ end
+ G
+
+ run "require 'submodule'"
+ expect(out).to eq("GIT")
+ end
+
+ it "unlocks the source when submodules are removed from git source", :git => ">= 2.9.0" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ git "#{lib_path("has_submodule-1.0")}", :submodules => true do
+ gem "has_submodule"
+ end
+ G
+
+ run! "require 'submodule'"
+ expect(out).to eq("GIT")
+
+ install_gemfile! <<-G
+ source "file:#{gem_repo4}"
+ git "#{lib_path("has_submodule-1.0")}" do
+ gem "has_submodule"
+ end
+ G
+
+ run! "require 'submodule'"
+ expect(out).to eq("GEM")
+ end
+ end
+
+ it "errors with a message when the .git repo is gone" do
+ build_git "foo", "1.0"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ lib_path("foo-1.0").join(".git").rmtree
+
+ bundle :update, :all => bundle_update_requires_all?
+ expect(last_command.bundler_err).to include(lib_path("foo-1.0").to_s).
+ and match(/Git error: command `git fetch.+has failed/)
+ end
+
+ it "should not explode on invalid revision on update of gem by name" do
+ build_git "rack", "0.8"
+
+ build_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle "update rack"
+ expect(out).to include("Bundle updated!")
+ end
+
+ it "shows the previous version of the gem" do
+ build_git "rails", "3.0", :path => lib_path("rails")
+
+ install_gemfile <<-G
+ gem "rails", :git => "#{lib_path("rails")}"
+ G
+
+ lockfile <<-G
+ GIT
+ remote: #{lib_path("rails")}
+ specs:
+ rails (2.3.2)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ rails!
+ G
+
+ bundle "update", :all => bundle_update_requires_all?
+ expect(out).to include("Using rails 3.0 (was 2.3.2) from #{lib_path("rails")} (at master@#{revision_for(lib_path("rails"))[0..6]})")
+ end
+ end
+
+ describe "with --source flag" do
+ before :each do
+ build_repo2
+ @git = build_git "foo", :path => lib_path("foo") do |s|
+ s.executables = "foobar"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ git "#{lib_path("foo")}" do
+ gem 'foo'
+ end
+ gem 'rack'
+ G
+ end
+
+ it "updates the source" do
+ update_git "foo", :path => @git.path
+
+ bundle "update --source foo"
+
+ in_app_root do
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" if defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+ end
+
+ it "unlocks gems that were originally pulled in by the source" do
+ update_git "foo", "2.0", :path => @git.path
+
+ bundle "update --source foo"
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+
+ it "leaves all other gems frozen" do
+ update_repo2
+ update_git "foo", :path => @git.path
+
+ bundle "update --source foo"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ context "when the gem and the repository have different names" do
+ before :each do
+ build_repo2
+ @git = build_git "foo", :path => lib_path("bar")
+
+ install_gemfile <<-G
+ source "file://localhost#{gem_repo2}"
+ git "#{lib_path("bar")}" do
+ gem 'foo'
+ end
+ gem 'rack'
+ G
+ end
+
+ it "the --source flag updates version of gems that were originally pulled in by the source", :bundler => "< 2" do
+ spec_lines = lib_path("bar/foo.gemspec").read.split("\n")
+ spec_lines[5] = "s.version = '2.0'"
+
+ update_git "foo", "2.0", :path => @git.path do |s|
+ s.write "foo.gemspec", spec_lines.join("\n")
+ end
+
+ ref = @git.ref_for "master"
+
+ bundle "update --source bar"
+
+ lockfile_should_be <<-G
+ GIT
+ remote: #{@git.path}
+ revision: #{ref}
+ specs:
+ foo (2.0)
+
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ foo!
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "the --source flag updates version of gems that were originally pulled in by the source", :bundler => "2" do
+ spec_lines = lib_path("bar/foo.gemspec").read.split("\n")
+ spec_lines[5] = "s.version = '2.0'"
+
+ update_git "foo", "2.0", :path => @git.path do |s|
+ s.write "foo.gemspec", spec_lines.join("\n")
+ end
+
+ ref = @git.ref_for "master"
+
+ bundle "update --source bar"
+
+ lockfile_should_be <<-G
+ GEM
+ remote: file://localhost#{gem_repo2}/
+ specs:
+ rack (1.0.0)
+
+ GIT
+ remote: #{@git.path}
+ revision: #{ref}
+ specs:
+ foo (2.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+ end
+end
diff --git a/spec/bundler/update/path_spec.rb b/spec/bundler/update/path_spec.rb
new file mode 100644
index 0000000000..38c125e04b
--- /dev/null
+++ b/spec/bundler/update/path_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+RSpec.describe "path sources" do
+ describe "bundle update --source" do
+ it "shows the previous version of the gem when updated from path source" do
+ build_lib "activesupport", "2.3.5", :path => lib_path("rails/activesupport")
+
+ install_gemfile <<-G
+ gem "activesupport", :path => "#{lib_path("rails/activesupport")}"
+ G
+
+ build_lib "activesupport", "3.0", :path => lib_path("rails/activesupport")
+
+ bundle "update --source activesupport"
+ expect(out).to include("Using activesupport 3.0 (was 2.3.5) from source at `#{lib_path("rails/activesupport")}`")
+ end
+ end
+end
diff --git a/spec/bundler/update/redownload_spec.rb b/spec/bundler/update/redownload_spec.rb
new file mode 100644
index 0000000000..018d3ed2e9
--- /dev/null
+++ b/spec/bundler/update/redownload_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle update", :bundler => "< 2", :ruby => ">= 2.0" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ before { bundle "config major_deprecations yes" }
+
+ describe "with --force" do
+ it "shows a deprecation when single flag passed" do
+ bundle! "update rack --force"
+ expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+
+ it "shows a deprecation when multiple flags passed" do
+ bundle! "update rack --no-color --force"
+ expect(out).to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+ end
+
+ describe "with --redownload" do
+ it "does not show a deprecation when single flag passed" do
+ bundle! "update rack --redownload"
+ expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+
+ it "does not show a deprecation when single multiple flags passed" do
+ bundle! "update rack --no-color --redownload"
+ expect(out).not_to include "[DEPRECATED FOR 2.0] The `--force` option has been renamed to `--redownload`"
+ end
+ end
+end
diff --git a/spec/default.mspec b/spec/default.mspec
index 4f2c062386..8791058f95 100644
--- a/spec/default.mspec
+++ b/spec/default.mspec
@@ -1,7 +1,11 @@
# -*- ruby -*-
-load "./rbconfig.rb"
-load File.dirname(__FILE__) + '/rubyspec/default.mspec'
-OBJDIR = File.expand_path("spec/rubyspec/optional/capi/ext")
+$VERBOSE = false
+if (opt = ENV["RUBYOPT"]) and (opt = opt.dup).sub!(/(?:\A|\s)-w(?=\z|\s)/, '')
+ ENV["RUBYOPT"] = opt
+end
+require "./rbconfig" unless defined?(RbConfig)
+load File.dirname(__FILE__) + '/ruby/default.mspec'
+OBJDIR = File.expand_path("spec/ruby/optional/capi/ext")
class MSpecScript
builddir = Dir.pwd
srcdir = ENV['SRCDIR']
@@ -15,12 +19,49 @@ class MSpecScript
# The default implementation to run the specs.
set :target, File.join(builddir, "miniruby#{config['exeext']}")
- set :prefix, File.expand_path('rubyspec', File.dirname(__FILE__))
+ set :prefix, File.expand_path('ruby', File.dirname(__FILE__))
set :flags, %W[
-I#{srcdir}/lib
- -I#{srcdir}
- -I#{srcdir}/#{config['EXTOUT']}/common
#{srcdir}/tool/runruby.rb --archdir=#{Dir.pwd} --extout=#{config['EXTOUT']}
--
]
end
+
+module MSpecScript::JobServer
+ def cores(max = 1)
+ if max > 1 and /(?:\A|\s)--jobserver-(?:auth|fds)=(\d+),(\d+)/ =~ ENV["MAKEFLAGS"]
+ cores = 1
+ begin
+ r = IO.for_fd($1.to_i(10), "rb", autoclose: false)
+ w = IO.for_fd($2.to_i(10), "wb", autoclose: false)
+ jobtokens = r.read_nonblock(max - 1)
+ cores = jobtokens.size
+ if cores > 0
+ cores += 1
+ jobserver = w
+ w = nil
+ at_exit {
+ jobserver.print(jobtokens)
+ jobserver.close
+ }
+ MSpecScript::JobServer.module_eval do
+ remove_method :cores
+ define_method(:cores) do
+ cores
+ end
+ end
+ return cores
+ end
+ rescue Errno::EBADF
+ ensure
+ r&.close
+ w&.close
+ end
+ end
+ super
+ end
+end
+
+class MSpecScript
+ prepend JobServer
+end
diff --git a/spec/mspec/Gemfile b/spec/mspec/Gemfile
new file mode 100644
index 0000000000..3dc3c4145b
--- /dev/null
+++ b/spec/mspec/Gemfile
@@ -0,0 +1,4 @@
+source 'https://rubygems.org'
+
+gem "rake", "~> 10.0"
+gem "rspec", "~> 2.14.1"
diff --git a/spec/mspec/Gemfile.lock b/spec/mspec/Gemfile.lock
new file mode 100644
index 0000000000..e977989f8c
--- /dev/null
+++ b/spec/mspec/Gemfile.lock
@@ -0,0 +1,24 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ diff-lcs (1.3)
+ rake (10.5.0)
+ rspec (2.14.1)
+ rspec-core (~> 2.14.0)
+ rspec-expectations (~> 2.14.0)
+ rspec-mocks (~> 2.14.0)
+ rspec-core (2.14.8)
+ rspec-expectations (2.14.5)
+ diff-lcs (>= 1.1.3, < 2.0)
+ rspec-mocks (2.14.6)
+
+PLATFORMS
+ java
+ ruby
+
+DEPENDENCIES
+ rake (~> 10.0)
+ rspec (~> 2.14.1)
+
+BUNDLED WITH
+ 1.16.1
diff --git a/spec/mspec/LICENSE b/spec/mspec/LICENSE
new file mode 100644
index 0000000000..d581dd1c9f
--- /dev/null
+++ b/spec/mspec/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2008 Engine Yard, Inc. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/spec/mspec/README.md b/spec/mspec/README.md
new file mode 100644
index 0000000000..23986c92e4
--- /dev/null
+++ b/spec/mspec/README.md
@@ -0,0 +1,91 @@
+[![Build Status](https://travis-ci.org/ruby/mspec.svg?branch=master)](https://travis-ci.org/ruby/mspec)
+
+## Overview
+
+MSpec is a specialized framework that is syntax-compatible with RSpec for
+basic things like 'describe', 'it' blocks and 'before', 'after' actions. MSpec
+contains additional features that assist in writing the RubySpecs used by
+multiple Ruby implementations.
+
+MSpec attempts to use the simplest Ruby language features so that beginning
+Ruby implementations can run the Ruby specs.
+
+MSpec is not intended as a replacement for RSpec. MSpec attempts to provide a
+subset of RSpec's features in some cases and a superset in others. It does not
+provide all the matchers, for instance.
+
+However, MSpec provides several extensions to facilitate writing the Ruby
+specs in a manner compatible with multiple Ruby implementations.
+
+ 1. MSpec offers a set of guards to control execution of the specs. These
+ guards not only enable or disable execution but also annotate the specs
+ with additional information about why they are run or not run.
+
+ 2. MSpec provides a different shared spec implementation specifically
+ designed to ease writing specs for the numerous aliased methods in Ruby.
+ The MSpec shared spec implementation should not conflict with RSpec's own
+ shared behavior facility.
+
+ 3. MSpec provides various helper methods to simplify some specs, for
+ example, creating temporary file names.
+
+ 4. MSpec has several specialized runner scripts that includes a
+ configuration facility with a default project file and user-specific
+ overrides.
+
+## Requirements
+
+MSpec requires Ruby 2.3 or more recent.
+
+## Bundler
+
+A Gemfile is provided. Use Bundler to install gem dependencies. To install
+Bundler, run the following:
+
+```bash
+gem install bundler
+```
+
+To install the gem dependencies with Bundler, run the following:
+
+```bash
+ruby -S bundle install
+```
+
+## Running Specs
+
+Use RSpec to run the MSpec specs. There are no plans currently to make the
+MSpec specs runnable by MSpec.
+
+After installing the gem dependencies, the specs can be run as follows:
+
+```bash
+ruby -S bundle exec rspec
+```
+
+Or
+
+```bash
+ruby -S rake
+```
+
+To run an individual spec file, use the following example:
+
+```bash
+ruby -S bundle exec rspec spec/helpers/ruby_exe_spec.rb
+```
+
+
+## Documentation
+
+See http://ruby.github.io/rubyspec.github.io/
+
+
+## Source Code
+
+See https://github.com/ruby/mspec
+
+
+## License
+
+See the LICENSE in the source code.
diff --git a/spec/mspec/Rakefile b/spec/mspec/Rakefile
new file mode 100644
index 0000000000..6a9de7a95e
--- /dev/null
+++ b/spec/mspec/Rakefile
@@ -0,0 +1,6 @@
+require 'bundler/setup'
+require 'rspec/core/rake_task'
+
+RSpec::Core::RakeTask.new(:spec)
+
+task :default => :spec
diff --git a/spec/mspec/bin/mkspec b/spec/mspec/bin/mkspec
new file mode 100755
index 0000000000..00f1fdff47
--- /dev/null
+++ b/spec/mspec/bin/mkspec
@@ -0,0 +1,7 @@
+#!/usr/bin/env ruby
+
+$:.unshift File.expand_path('../../lib', __FILE__)
+
+require 'mspec/commands/mkspec'
+
+MkSpec.main
diff --git a/spec/mspec/bin/mkspec.bat b/spec/mspec/bin/mkspec.bat
new file mode 100755
index 0000000000..1073d20a9b
--- /dev/null
+++ b/spec/mspec/bin/mkspec.bat
@@ -0,0 +1 @@
+@"ruby.exe" "%~dpn0" %*
diff --git a/spec/mspec/bin/mspec b/spec/mspec/bin/mspec
new file mode 100755
index 0000000000..f833257bb0
--- /dev/null
+++ b/spec/mspec/bin/mspec
@@ -0,0 +1,7 @@
+#!/usr/bin/env ruby
+
+$:.unshift File.expand_path('../../lib', __FILE__)
+
+require 'mspec/commands/mspec'
+
+MSpecMain.main
diff --git a/spec/mspec/bin/mspec-ci b/spec/mspec/bin/mspec-ci
new file mode 100755
index 0000000000..d7cd50a827
--- /dev/null
+++ b/spec/mspec/bin/mspec-ci
@@ -0,0 +1,7 @@
+#!/usr/bin/env ruby
+
+$:.unshift File.expand_path('../../lib', __FILE__)
+
+require 'mspec/commands/mspec-ci'
+
+MSpecCI.main
diff --git a/spec/mspec/bin/mspec-ci.bat b/spec/mspec/bin/mspec-ci.bat
new file mode 100755
index 0000000000..1073d20a9b
--- /dev/null
+++ b/spec/mspec/bin/mspec-ci.bat
@@ -0,0 +1 @@
+@"ruby.exe" "%~dpn0" %*
diff --git a/spec/mspec/bin/mspec-run b/spec/mspec/bin/mspec-run
new file mode 100755
index 0000000000..010ecefe35
--- /dev/null
+++ b/spec/mspec/bin/mspec-run
@@ -0,0 +1,7 @@
+#!/usr/bin/env ruby
+
+$:.unshift File.expand_path('../../lib', __FILE__)
+
+require 'mspec/commands/mspec-run'
+
+MSpecRun.main
diff --git a/spec/mspec/bin/mspec-run.bat b/spec/mspec/bin/mspec-run.bat
new file mode 100755
index 0000000000..1073d20a9b
--- /dev/null
+++ b/spec/mspec/bin/mspec-run.bat
@@ -0,0 +1 @@
+@"ruby.exe" "%~dpn0" %*
diff --git a/spec/mspec/bin/mspec-tag b/spec/mspec/bin/mspec-tag
new file mode 100755
index 0000000000..a5f9fffaaa
--- /dev/null
+++ b/spec/mspec/bin/mspec-tag
@@ -0,0 +1,7 @@
+#!/usr/bin/env ruby
+
+$:.unshift File.expand_path('../../lib', __FILE__)
+
+require 'mspec/commands/mspec-tag'
+
+MSpecTag.main
diff --git a/spec/mspec/bin/mspec-tag.bat b/spec/mspec/bin/mspec-tag.bat
new file mode 100755
index 0000000000..1073d20a9b
--- /dev/null
+++ b/spec/mspec/bin/mspec-tag.bat
@@ -0,0 +1 @@
+@"ruby.exe" "%~dpn0" %*
diff --git a/spec/mspec/bin/mspec.bat b/spec/mspec/bin/mspec.bat
new file mode 100755
index 0000000000..1073d20a9b
--- /dev/null
+++ b/spec/mspec/bin/mspec.bat
@@ -0,0 +1 @@
+@"ruby.exe" "%~dpn0" %*
diff --git a/spec/mspec/lib/mspec.rb b/spec/mspec/lib/mspec.rb
new file mode 100644
index 0000000000..42d590c99a
--- /dev/null
+++ b/spec/mspec/lib/mspec.rb
@@ -0,0 +1,20 @@
+require 'mspec/matchers'
+require 'mspec/expectations'
+require 'mspec/mocks'
+require 'mspec/runner'
+require 'mspec/guards'
+require 'mspec/helpers'
+require 'mspec/version'
+
+# If the implementation on which the specs are run cannot
+# load pp from the standard library, add a pp.rb file that
+# defines the #pretty_inspect method on Object or Kernel.
+begin
+ require 'pp'
+rescue LoadError
+ module Kernel
+ def pretty_inspect
+ inspect
+ end
+ end
+end
diff --git a/spec/mspec/lib/mspec/commands/mkspec.rb b/spec/mspec/lib/mspec/commands/mkspec.rb
new file mode 100755
index 0000000000..49a2e6b616
--- /dev/null
+++ b/spec/mspec/lib/mspec/commands/mkspec.rb
@@ -0,0 +1,155 @@
+#!/usr/bin/env ruby
+
+require 'rbconfig'
+require 'mspec/version'
+require 'mspec/utils/options'
+require 'mspec/utils/name_map'
+require 'mspec/helpers/fs'
+
+class MkSpec
+ attr_reader :config
+
+ def initialize
+ @config = {
+ :constants => [],
+ :requires => [],
+ :base => "core",
+ :version => nil
+ }
+ @map = NameMap.new true
+ end
+
+ def options(argv=ARGV)
+ options = MSpecOptions.new "mkspec [options]", 32
+
+ options.on("-c", "--constant", "CONSTANT",
+ "Class or Module to generate spec stubs for") do |name|
+ config[:constants] << name
+ end
+ options.on("-b", "--base", "DIR",
+ "Directory to generate specs into") do |directory|
+ config[:base] = File.expand_path directory
+ end
+ options.on("-r", "--require", "LIBRARY",
+ "A library to require") do |file|
+ config[:requires] << file
+ end
+ options.on("-V", "--version-guard", "VERSION",
+ "Specify version for ruby_version_is guards") do |version|
+ config[:version] = version
+ end
+ options.version MSpec::VERSION
+ options.help
+
+ options.doc "\n How might this work in the real world?\n"
+ options.doc " 1. To create spec stubs for every class or module in Object\n"
+ options.doc " $ mkspec\n"
+ options.doc " 2. To create spec stubs for Fixnum\n"
+ options.doc " $ mkspec -c Fixnum\n"
+ options.doc " 3. To create spec stubs for Complex in 'superspec/complex'\n"
+ options.doc " $ mkspec -c Complex -r complex -b superspec"
+ options.doc ""
+
+ options.parse argv
+ end
+
+ def create_directory(mod)
+ subdir = @map.dir_name mod, config[:base]
+
+ if File.exist? subdir
+ unless File.directory? subdir
+ puts "#{subdir} already exists and is not a directory."
+ return nil
+ end
+ else
+ mkdir_p subdir
+ end
+
+ subdir
+ end
+
+ def write_requires(dir, file)
+ prefix = config[:base] + '/'
+ raise dir unless dir.start_with? prefix
+ sub = dir[prefix.size..-1]
+ parents = '../' * (sub.split('/').length + 1)
+
+ File.open(file, 'w') do |f|
+ f.puts "require_relative '#{parents}spec_helper'"
+ config[:requires].each do |lib|
+ f.puts "require '#{lib}'"
+ end
+ end
+ end
+
+ def write_version(f)
+ f.puts ""
+ if version = config[:version]
+ f.puts "ruby_version_is #{version} do"
+ yield " "
+ f.puts "end"
+ else
+ yield ""
+ end
+ end
+
+ def write_spec(file, meth, exists)
+ if exists
+ out = `#{ruby} #{MSPEC_HOME}/bin/mspec-run --dry-run --unguarded -fs -e '#{meth}' #{file}`
+ return if out.include?(meth)
+ end
+
+ File.open file, 'a' do |f|
+ write_version(f) do |indent|
+ f.puts <<-EOS
+#{indent}describe "#{meth}" do
+#{indent} it "needs to be reviewed for spec completeness"
+#{indent}end
+EOS
+ end
+ end
+
+ puts file
+ end
+
+ def create_file(dir, mod, meth, name)
+ file = File.join dir, @map.file_name(meth, mod)
+ exists = File.exist? file
+
+ write_requires dir, file unless exists
+ write_spec file, name, exists
+ end
+
+ def run
+ config[:requires].each { |lib| require lib }
+ constants = config[:constants]
+ constants = Object.constants if constants.empty?
+
+ @map.map({}, constants).each do |mod, methods|
+ name = mod.chop
+ next unless dir = create_directory(name)
+
+ methods.each { |method| create_file dir, name, method, mod + method }
+ 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'
+
+ script = new
+ script.options
+ script.run
+ end
+end
diff --git a/spec/mspec/lib/mspec/commands/mspec-ci.rb b/spec/mspec/lib/mspec/commands/mspec-ci.rb
new file mode 100644
index 0000000000..cb0193f42d
--- /dev/null
+++ b/spec/mspec/lib/mspec/commands/mspec-ci.rb
@@ -0,0 +1,78 @@
+#!/usr/bin/env ruby
+
+$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
+
+require 'mspec/version'
+require 'mspec/utils/options'
+require 'mspec/utils/script'
+
+
+class MSpecCI < MSpecScript
+ def options(argv=ARGV)
+ options = MSpecOptions.new "mspec ci [options] (FILE|DIRECTORY|GLOB)+", 30, config
+
+ options.doc " Ask yourself:"
+ options.doc " 1. How to run the specs?"
+ options.doc " 2. How to modify the guard behavior?"
+ options.doc " 2. How to display the output?"
+ options.doc " 3. What action to perform?"
+ options.doc " 4. When to perform it?"
+
+ options.doc "\n How to run the specs"
+ options.chdir
+ options.prefix
+ options.configure { |f| load f }
+ options.pretend
+ options.interrupt
+
+ options.doc "\n How to modify the guard behavior"
+ options.unguarded
+ options.verify
+
+ options.doc "\n How to display their output"
+ options.formatters
+ options.verbose
+
+ options.doc "\n What action to perform"
+ options.actions
+
+ options.doc "\n When to perform it"
+ options.action_filters
+
+ options.doc "\n Help!"
+ options.debug
+ options.version MSpec::VERSION
+ options.help
+
+ options.doc "\n Custom options"
+ custom_options options
+
+ options.doc "\n How might this work in the real world?"
+ options.doc "\n 1. To simply run the known good specs"
+ options.doc "\n $ mspec ci"
+ options.doc "\n 2. To run a subset of the known good specs"
+ options.doc "\n $ mspec ci path/to/specs"
+ options.doc "\n 3. To start the debugger before the spec matching 'this crashes'"
+ options.doc "\n $ mspec ci --spec-debug -S 'this crashes'"
+ options.doc ""
+
+ patterns = options.parse argv
+ patterns = config[:ci_files] if patterns.empty?
+ @files = files patterns
+ end
+
+ def run
+ MSpec.register_tags_patterns config[:tags_patterns]
+ MSpec.register_files @files
+
+ tags = ["fails", "critical", "unstable", "incomplete", "unsupported"]
+ tags += Array(config[:ci_xtags])
+
+ require 'mspec/runner/filters/tag'
+ filter = TagFilter.new(:exclude, *tags)
+ filter.register
+
+ MSpec.process
+ exit MSpec.exit_code
+ end
+end
diff --git a/spec/mspec/lib/mspec/commands/mspec-run.rb b/spec/mspec/lib/mspec/commands/mspec-run.rb
new file mode 100644
index 0000000000..249f9f5771
--- /dev/null
+++ b/spec/mspec/lib/mspec/commands/mspec-run.rb
@@ -0,0 +1,86 @@
+#!/usr/bin/env ruby
+
+$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
+
+require 'mspec/version'
+require 'mspec/utils/options'
+require 'mspec/utils/script'
+
+
+class MSpecRun < MSpecScript
+ def initialize
+ super
+
+ config[:files] = []
+ end
+
+ def options(argv=ARGV)
+ options = MSpecOptions.new "mspec run [options] (FILE|DIRECTORY|GLOB)+", 30, config
+
+ options.doc " Ask yourself:"
+ options.doc " 1. What specs to run?"
+ options.doc " 2. How to modify the execution?"
+ options.doc " 3. How to modify the guard behavior?"
+ options.doc " 4. How to display the output?"
+ options.doc " 5. What action to perform?"
+ options.doc " 6. When to perform it?"
+
+ options.doc "\n What specs to run"
+ options.filters
+
+ options.doc "\n How to modify the execution"
+ options.chdir
+ options.prefix
+ options.configure { |f| load f }
+ options.randomize
+ options.repeat
+ options.pretend
+ options.interrupt
+
+ options.doc "\n How to modify the guard behavior"
+ options.unguarded
+ options.verify
+
+ options.doc "\n How to display their output"
+ options.formatters
+ options.verbose
+
+ options.doc "\n What action to perform"
+ options.actions
+
+ options.doc "\n When to perform it"
+ options.action_filters
+
+ options.doc "\n Help!"
+ options.debug
+ options.version MSpec::VERSION
+ options.help
+
+ options.doc "\n Custom options"
+ custom_options options
+
+ options.doc "\n How might this work in the real world?"
+ options.doc "\n 1. To simply run some specs"
+ options.doc "\n $ mspec path/to/the/specs"
+ options.doc " mspec path/to/the_file_spec.rb"
+ options.doc "\n 2. To run specs tagged with 'fails'"
+ options.doc "\n $ mspec -g fails path/to/the_file_spec.rb"
+ options.doc "\n 3. To start the debugger before the spec matching 'this crashes'"
+ options.doc "\n $ mspec --spec-debug -S 'this crashes' path/to/the_file_spec.rb"
+ options.doc "\n 4. To run some specs matching 'this crashes'"
+ options.doc "\n $ mspec -e 'this crashes' path/to/the_file_spec.rb"
+
+ options.doc ""
+
+ patterns = options.parse argv
+ @files = files_from_patterns(patterns)
+ end
+
+ def run
+ MSpec.register_tags_patterns config[:tags_patterns]
+ MSpec.register_files @files
+
+ MSpec.process
+ exit MSpec.exit_code
+ end
+end
diff --git a/spec/mspec/lib/mspec/commands/mspec-tag.rb b/spec/mspec/lib/mspec/commands/mspec-tag.rb
new file mode 100644
index 0000000000..8bc3382e91
--- /dev/null
+++ b/spec/mspec/lib/mspec/commands/mspec-tag.rb
@@ -0,0 +1,132 @@
+#!/usr/bin/env ruby
+
+require 'mspec/version'
+require 'mspec/utils/options'
+require 'mspec/utils/script'
+
+
+class MSpecTag < MSpecScript
+ def initialize
+ super
+
+ config[:tagger] = :add
+ config[:tag] = 'fails:'
+ config[:outcome] = :fail
+ config[:ltags] = []
+ end
+
+ def options(argv=ARGV)
+ options = MSpecOptions.new "mspec tag [options] (FILE|DIRECTORY|GLOB)+", 30, config
+
+ options.doc " Ask yourself:"
+ options.doc " 1. What specs to run?"
+ options.doc " 2. How to modify the execution?"
+ options.doc " 3. How to display the output?"
+ options.doc " 4. What tag action to perform?"
+ options.doc " 5. When to perform it?"
+
+ options.doc "\n What specs to run"
+ options.filters
+
+ options.doc "\n How to modify the execution"
+ options.configure { |f| load f }
+ options.pretend
+ options.unguarded
+ options.interrupt
+
+ options.doc "\n How to display their output"
+ options.formatters
+ options.verbose
+
+ options.doc "\n What action to perform and when to perform it"
+ options.on("-N", "--add", "TAG",
+ "Add TAG with format 'tag' or 'tag(comment)' (see -Q, -F, -L)") do |o|
+ config[:tagger] = :add
+ config[:tag] = "#{o}:"
+ end
+ options.on("-R", "--del", "TAG",
+ "Delete TAG (see -Q, -F, -L)") do |o|
+ config[:tagger] = :del
+ config[:tag] = "#{o}:"
+ config[:outcome] = :pass
+ end
+ options.on("-Q", "--pass", "Apply action to specs that pass (default for --del)") do
+ config[:outcome] = :pass
+ end
+ options.on("-F", "--fail", "Apply action to specs that fail (default for --add)") do
+ config[:outcome] = :fail
+ end
+ options.on("-L", "--all", "Apply action to all specs") do
+ config[:outcome] = :all
+ end
+ options.on("--list", "TAG", "Display descriptions of any specs tagged with TAG") do |t|
+ config[:tagger] = :list
+ config[:ltags] << t
+ end
+ options.on("--list-all", "Display descriptions of any tagged specs") do
+ config[:tagger] = :list_all
+ end
+ options.on("--purge", "Remove all tags not matching any specs") do
+ config[:tagger] = :purge
+ end
+
+ options.doc "\n Help!"
+ options.debug
+ options.version MSpec::VERSION
+ options.help
+
+ options.doc "\n Custom options"
+ custom_options options
+
+ options.doc "\n How might this work in the real world?"
+ options.doc "\n 1. To add the 'fails' tag to failing specs"
+ options.doc "\n $ mspec tag path/to/the_file_spec.rb"
+ options.doc "\n 2. To remove the 'fails' tag from passing specs"
+ options.doc "\n $ mspec tag --del fails path/to/the_file_spec.rb"
+ options.doc "\n 3. To display the descriptions for all specs tagged with 'fails'"
+ options.doc "\n $ mspec tag --list fails path/to/the/specs"
+ options.doc ""
+
+ patterns = options.parse argv
+ if patterns.empty?
+ puts options
+ puts "No files specified."
+ exit 1
+ end
+ @files = files patterns
+ end
+
+ def register
+ require 'mspec/runner/actions'
+
+ case config[:tagger]
+ when :add, :del
+ tag = SpecTag.new config[:tag]
+ tagger = TagAction.new(config[:tagger], config[:outcome], tag.tag, tag.comment,
+ config[:atags], config[:astrings])
+ when :list, :list_all
+ tagger = TagListAction.new config[:tagger] == :list_all ? nil : config[:ltags]
+ MSpec.register_mode :pretend
+ config[:formatter] = false
+ when :purge
+ tagger = TagPurgeAction.new
+ MSpec.register_mode :pretend
+ MSpec.register_mode :unguarded
+ config[:formatter] = false
+ else
+ raise ArgumentError, "No recognized action given"
+ end
+ tagger.register
+
+ super
+ end
+
+ def run
+ MSpec.register_tags_patterns config[:tags_patterns]
+ MSpec.register_files @files
+
+ MSpec.process
+ exit MSpec.exit_code
+ end
+end
+
diff --git a/spec/mspec/lib/mspec/commands/mspec.rb b/spec/mspec/lib/mspec/commands/mspec.rb
new file mode 100755
index 0000000000..0ccea82a5c
--- /dev/null
+++ b/spec/mspec/lib/mspec/commands/mspec.rb
@@ -0,0 +1,119 @@
+#!/usr/bin/env ruby
+
+require 'mspec/version'
+require 'mspec/utils/options'
+require 'mspec/utils/script'
+require 'mspec/helpers/tmp'
+require 'mspec/runner/actions/filter'
+require 'mspec/runner/actions/timer'
+
+
+class MSpecMain < MSpecScript
+ def initialize
+ super
+
+ config[:loadpath] = []
+ config[:requires] = []
+ config[:target] = ENV['RUBY'] || 'ruby'
+ config[:flags] = []
+ config[:command] = nil
+ config[:options] = []
+ config[:launch] = []
+ end
+
+ def options(argv=ARGV)
+ config[:command] = argv.shift if ["ci", "run", "tag"].include?(argv[0])
+
+ options = MSpecOptions.new "mspec [COMMAND] [options] (FILE|DIRECTORY|GLOB)+", 30, config
+
+ options.doc " The mspec command sets up and invokes the sub-commands"
+ options.doc " (see below) to enable, for instance, running the specs"
+ options.doc " with different implementations like ruby, jruby, rbx, etc.\n"
+
+ options.configure do |f|
+ load f
+ config[:options] << '-B' << f
+ end
+
+ options.targets
+
+ options.on("--warnings", "Don't suppress warnings") do
+ config[:flags] << '-w'
+ ENV['OUTPUT_WARNINGS'] = '1'
+ end
+
+ options.on("-j", "--multi", "Run multiple (possibly parallel) subprocesses") do
+ config[:multi] = true
+ end
+
+ options.version MSpec::VERSION do
+ if config[:command]
+ config[:options] << "-v"
+ else
+ puts "#{File.basename $0} #{MSpec::VERSION}"
+ exit
+ end
+ end
+
+ options.help do
+ if config[:command]
+ config[:options] << "-h"
+ else
+ puts options
+ exit 1
+ end
+ end
+
+ options.doc "\n Custom options"
+ custom_options options
+
+ # The rest of the help output
+ options.doc "\n where COMMAND is one of:\n"
+ options.doc " run - Run the specified specs (default)"
+ options.doc " ci - Run the known good specs"
+ options.doc " tag - Add or remove tags\n"
+ options.doc " mspec COMMAND -h for more options\n"
+ options.doc " example: $ mspec run -h\n"
+
+ options.on_extra { |o| config[:options] << o }
+ options.parse(argv)
+
+ if config[:multi]
+ options = MSpecOptions.new "mspec", 30, config
+ options.all
+ patterns = options.parse(config[:options])
+ @files = files_from_patterns(patterns)
+ end
+ end
+
+ def register; end
+
+ def multi_exec(argv)
+ require 'mspec/runner/formatters/multi'
+ formatter = MultiFormatter.new
+ warn "formatter options is ignored due to multi option" if config[:formatter]
+
+ require 'mspec/runner/parallel'
+ processes = cores(@files.size)
+ ParallelRunner.new(@files, processes, formatter, argv).run
+ end
+
+ def run
+ argv = config[:target].split(/\s+/)
+
+ argv.concat config[:launch]
+ argv.concat config[:flags]
+ argv.concat config[:loadpath]
+ argv.concat config[:requires]
+ argv << "#{MSPEC_HOME}/bin/mspec-#{config[:command] || 'run'}"
+ argv.concat config[:options]
+
+ if config[:multi]
+ exit multi_exec(argv)
+ else
+ $stderr.puts "$ #{argv.join(' ')}"
+ $stderr.flush
+ exec(*argv, close_others: false)
+ end
+ end
+end
diff --git a/spec/mspec/lib/mspec/expectations.rb b/spec/mspec/lib/mspec/expectations.rb
new file mode 100644
index 0000000000..d07f959b27
--- /dev/null
+++ b/spec/mspec/lib/mspec/expectations.rb
@@ -0,0 +1,2 @@
+require 'mspec/expectations/expectations'
+require 'mspec/expectations/should'
diff --git a/spec/mspec/lib/mspec/expectations/expectations.rb b/spec/mspec/lib/mspec/expectations/expectations.rb
new file mode 100644
index 0000000000..cfdc2b63a3
--- /dev/null
+++ b/spec/mspec/lib/mspec/expectations/expectations.rb
@@ -0,0 +1,21 @@
+class SpecExpectationNotMetError < StandardError
+end
+
+class SpecExpectationNotFoundError < StandardError
+ def message
+ "No behavior expectation was found in the example"
+ end
+end
+
+class SpecExpectation
+ def self.fail_with(expected, actual)
+ expected_to_s = expected.to_s
+ actual_to_s = actual.to_s
+ if expected_to_s.size + actual_to_s.size > 80
+ message = "#{expected_to_s.chomp}\n#{actual_to_s}"
+ else
+ message = "#{expected_to_s} #{actual_to_s}"
+ end
+ Kernel.raise SpecExpectationNotMetError, message
+ end
+end
diff --git a/spec/mspec/lib/mspec/expectations/should.rb b/spec/mspec/lib/mspec/expectations/should.rb
new file mode 100644
index 0000000000..f6d83053f5
--- /dev/null
+++ b/spec/mspec/lib/mspec/expectations/should.rb
@@ -0,0 +1,29 @@
+class Object
+ NO_MATCHER_GIVEN = Object.new
+
+ def should(matcher = NO_MATCHER_GIVEN)
+ MSpec.expectation
+ MSpec.actions :expectation, MSpec.current.state
+ unless matcher.equal? NO_MATCHER_GIVEN
+ unless matcher.matches? self
+ expected, actual = matcher.failure_message
+ SpecExpectation.fail_with(expected, actual)
+ end
+ else
+ SpecPositiveOperatorMatcher.new(self)
+ end
+ end
+
+ def should_not(matcher = NO_MATCHER_GIVEN)
+ MSpec.expectation
+ MSpec.actions :expectation, MSpec.current.state
+ unless matcher.equal? NO_MATCHER_GIVEN
+ if matcher.matches? self
+ expected, actual = matcher.negative_failure_message
+ SpecExpectation.fail_with(expected, actual)
+ end
+ else
+ SpecNegativeOperatorMatcher.new(self)
+ end
+ end
+end
diff --git a/spec/mspec/lib/mspec/guards.rb b/spec/mspec/lib/mspec/guards.rb
new file mode 100644
index 0000000000..454ac0c776
--- /dev/null
+++ b/spec/mspec/lib/mspec/guards.rb
@@ -0,0 +1,11 @@
+require 'mspec/guards/block_device'
+require 'mspec/guards/bug'
+require 'mspec/guards/conflict'
+require 'mspec/guards/endian'
+require 'mspec/guards/feature'
+require 'mspec/guards/guard'
+require 'mspec/guards/platform'
+require 'mspec/guards/quarantine'
+require 'mspec/guards/support'
+require 'mspec/guards/superuser'
+require 'mspec/guards/version'
diff --git a/spec/mspec/lib/mspec/guards/block_device.rb b/spec/mspec/lib/mspec/guards/block_device.rb
new file mode 100644
index 0000000000..ae736a2d4e
--- /dev/null
+++ b/spec/mspec/lib/mspec/guards/block_device.rb
@@ -0,0 +1,16 @@
+require 'mspec/guards/guard'
+
+class BlockDeviceGuard < SpecGuard
+ def match?
+ platform_is_not :freebsd, :windows, :opal do
+ block = `find /dev /devices -type b 2> /dev/null`
+ return !(block.nil? || block.empty?)
+ end
+
+ false
+ end
+end
+
+def with_block_device(&block)
+ BlockDeviceGuard.new.run_if(:with_block_device, &block)
+end
diff --git a/spec/mspec/lib/mspec/guards/bug.rb b/spec/mspec/lib/mspec/guards/bug.rb
new file mode 100644
index 0000000000..b1bfc6413e
--- /dev/null
+++ b/spec/mspec/lib/mspec/guards/bug.rb
@@ -0,0 +1,28 @@
+require 'mspec/guards/version'
+
+class BugGuard < VersionGuard
+ def initialize(bug, version)
+ @bug = bug
+ if String === version
+ MSpec.deprecate "ruby_bug with a single version", 'an exclusive range ("2.1"..."2.3")'
+ @version = SpecVersion.new version, true
+ else
+ super(version)
+ end
+ @parameters = [@bug, @version]
+ end
+
+ def match?
+ return false if MSpec.mode? :no_ruby_bug
+ return false unless PlatformGuard.standard?
+ if Range === @version
+ super
+ else
+ FULL_RUBY_VERSION <= @version
+ end
+ end
+end
+
+def ruby_bug(bug, version, &block)
+ BugGuard.new(bug, version).run_unless(:ruby_bug, &block)
+end
diff --git a/spec/mspec/lib/mspec/guards/conflict.rb b/spec/mspec/lib/mspec/guards/conflict.rb
new file mode 100644
index 0000000000..4930e5734d
--- /dev/null
+++ b/spec/mspec/lib/mspec/guards/conflict.rb
@@ -0,0 +1,23 @@
+require 'mspec/guards/guard'
+require 'mspec/utils/deprecate'
+
+class ConflictsGuard < SpecGuard
+ def initialize(*args)
+ MSpec.deprecate 'conflicts_with', 'guard -> { condition } do'
+ super(*args)
+ end
+
+ def match?
+ # Always convert constants to symbols regardless of version.
+ constants = Object.constants.map { |x| x.to_sym }
+ @parameters.any? { |mod| constants.include? mod }
+ end
+end
+
+# In some cases, libraries will modify another Ruby method's
+# behavior. The specs for the method's behavior will then fail
+# if that library is loaded. This guard will not run if any of
+# the specified constants exist in Object.constants.
+def conflicts_with(*modules, &block)
+ ConflictsGuard.new(*modules).run_unless(:conflicts_with, &block)
+end
diff --git a/spec/mspec/lib/mspec/guards/endian.rb b/spec/mspec/lib/mspec/guards/endian.rb
new file mode 100644
index 0000000000..79335a8933
--- /dev/null
+++ b/spec/mspec/lib/mspec/guards/endian.rb
@@ -0,0 +1,25 @@
+require 'mspec/guards/guard'
+
+# Despite that these are inverses, the two classes are
+# used to simplify MSpec guard reporting modes
+
+class EndianGuard < SpecGuard
+ def pattern
+ @pattern ||= [1].pack('L')
+ end
+ private :pattern
+end
+
+class BigEndianGuard < EndianGuard
+ def match?
+ pattern[-1] == ?\001
+ end
+end
+
+def big_endian(&block)
+ BigEndianGuard.new.run_if(:big_endian, &block)
+end
+
+def little_endian(&block)
+ BigEndianGuard.new.run_unless(:little_endian, &block)
+end
diff --git a/spec/mspec/lib/mspec/guards/feature.rb b/spec/mspec/lib/mspec/guards/feature.rb
new file mode 100644
index 0000000000..d4c6dd1cde
--- /dev/null
+++ b/spec/mspec/lib/mspec/guards/feature.rb
@@ -0,0 +1,45 @@
+require 'mspec/guards/guard'
+
+class FeatureGuard < SpecGuard
+ def self.enabled?(*features)
+ new(*features).match?
+ end
+
+ def match?
+ @parameters.all? { |f| MSpec.feature_enabled? f }
+ end
+end
+
+# Provides better documentation in the specs by
+# naming sets of features that work together as
+# a whole. Examples include :encoding, :fiber,
+# :continuation, :fork.
+#
+# Usage example:
+#
+# with_feature :encoding do
+# # specs for a method that provides aspects
+# # of the encoding feature
+# end
+#
+# Multiple features must all be enabled for the
+# guard to run:
+#
+# with_feature :one, :two do
+# # these specs will run if features :one AND
+# # :two are enabled.
+# end
+#
+# The implementation must explicitly enable a feature
+# by adding code like the following to the .mspec
+# configuration file:
+#
+# MSpec.enable_feature :encoding
+#
+def with_feature(*features, &block)
+ FeatureGuard.new(*features).run_if(:with_feature, &block)
+end
+
+def without_feature(*features, &block)
+ FeatureGuard.new(*features).run_unless(:without_feature, &block)
+end
diff --git a/spec/mspec/lib/mspec/guards/guard.rb b/spec/mspec/lib/mspec/guards/guard.rb
new file mode 100644
index 0000000000..322a08145d
--- /dev/null
+++ b/spec/mspec/lib/mspec/guards/guard.rb
@@ -0,0 +1,141 @@
+require 'mspec/runner/mspec'
+require 'mspec/runner/actions/tally'
+
+class SpecGuard
+ def self.report
+ @report ||= Hash.new { |h,k| h[k] = [] }
+ end
+
+ def self.clear
+ @report = nil
+ end
+
+ def self.finish
+ report.keys.sort.each do |key|
+ desc = report[key]
+ size = desc.size
+ spec = size == 1 ? "spec" : "specs"
+ print "\n\n#{size} #{spec} omitted by guard: #{key}:\n"
+ desc.each { |description| print "\n", description; }
+ end
+
+ print "\n\n"
+ end
+
+ def self.guards
+ @guards ||= []
+ end
+
+ def self.clear_guards
+ @guards = []
+ end
+
+ # Returns a partial Ruby version string based on +which+.
+ # For example, if RUBY_VERSION = 8.2.3:
+ #
+ # :major => "8"
+ # :minor => "8.2"
+ # :tiny => "8.2.3"
+ # :teeny => "8.2.3"
+ # :full => "8.2.3"
+ def self.ruby_version(which = :minor)
+ case which
+ when :major
+ n = 1
+ when :minor
+ n = 2
+ when :tiny, :teeny, :full
+ n = 3
+ end
+
+ RUBY_VERSION.split('.')[0,n].join('.')
+ end
+
+ attr_accessor :name
+
+ def initialize(*args)
+ @parameters = args
+ end
+
+ def yield?(invert = false)
+ return true if MSpec.mode? :unguarded
+
+ allow = match? ^ invert
+
+ if !allow and reporting?
+ MSpec.guard
+ MSpec.register :finish, SpecGuard
+ MSpec.register :add, self
+ return true
+ elsif MSpec.mode? :verify
+ return true
+ end
+
+ allow
+ end
+
+ def run_if(name, &block)
+ @name = name
+ if block
+ yield if yield?(false)
+ else
+ yield?(false)
+ end
+ ensure
+ unregister
+ end
+
+ def run_unless(name, &block)
+ @name = name
+ if block
+ yield if yield?(true)
+ else
+ yield?(true)
+ end
+ ensure
+ unregister
+ end
+
+ def reporting?
+ MSpec.mode?(:report) or
+ (MSpec.mode?(:report_on) and SpecGuard.guards.include?(name))
+ end
+
+ def report_key
+ "#{name} #{@parameters.join(", ")}"
+ end
+
+ def record(description)
+ SpecGuard.report[report_key] << description
+ end
+
+ def add(example)
+ record example.description
+ MSpec.retrieve(:formatter).tally.counter.guards!
+ end
+
+ def unregister
+ MSpec.unguard
+ MSpec.unregister :add, self
+ end
+
+ def match?
+ raise "must be implemented by the subclass"
+ end
+end
+
+# Combined guards
+
+def guard(condition, &block)
+ raise "condition must be a Proc" unless condition.is_a?(Proc)
+ raise LocalJumpError, "no block given" unless block
+ return yield if MSpec.mode? :unguarded or MSpec.mode? :verify or MSpec.mode? :report
+ yield if condition.call
+end
+
+def guard_not(condition, &block)
+ raise "condition must be a Proc" unless condition.is_a?(Proc)
+ raise LocalJumpError, "no block given" unless block
+ return yield if MSpec.mode? :unguarded or MSpec.mode? :verify or MSpec.mode? :report
+ yield unless condition.call
+end
diff --git a/spec/mspec/lib/mspec/guards/platform.rb b/spec/mspec/lib/mspec/guards/platform.rb
new file mode 100644
index 0000000000..9543b1dd05
--- /dev/null
+++ b/spec/mspec/lib/mspec/guards/platform.rb
@@ -0,0 +1,91 @@
+require 'mspec/guards/guard'
+
+class PlatformGuard < SpecGuard
+ def self.implementation?(*args)
+ args.any? do |name|
+ case name
+ when :rubinius
+ RUBY_ENGINE.start_with?('rbx')
+ when :ruby, :jruby, :truffleruby, :ironruby, :macruby, :maglev, :topaz, :opal
+ RUBY_ENGINE.start_with?(name.to_s)
+ else
+ raise "unknown implementation #{name}"
+ end
+ end
+ end
+
+ def self.standard?
+ implementation? :ruby
+ end
+
+ HOST_OS = begin
+ require 'rbconfig'
+ RbConfig::CONFIG['host_os'] || RUBY_PLATFORM
+ rescue LoadError
+ RUBY_PLATFORM
+ end.downcase
+
+ def self.os?(*oses)
+ oses.any? do |os|
+ raise ":java is not a valid OS" if os == :java
+ if os == :windows
+ HOST_OS =~ /(mswin|mingw)/
+ else
+ HOST_OS.include?(os.to_s)
+ end
+ end
+ end
+
+ def self.windows?
+ os?(:windows)
+ end
+
+ WORD_SIZE = 1.size * 8
+
+ POINTER_SIZE = begin
+ require 'rbconfig/sizeof'
+ RbConfig::SIZEOF["void*"] * 8
+ rescue LoadError
+ WORD_SIZE
+ end
+
+ def self.wordsize?(size)
+ size == WORD_SIZE
+ end
+
+ def self.pointer_size?(size)
+ size == POINTER_SIZE
+ end
+
+ def initialize(*args)
+ if args.last.is_a?(Hash)
+ @options, @platforms = args.last, args[0..-2]
+ else
+ @options, @platforms = {}, args
+ end
+ @parameters = args
+ end
+
+ def match?
+ match = @platforms.empty? ? true : PlatformGuard.os?(*@platforms)
+ @options.each do |key, value|
+ case key
+ when :os
+ match &&= PlatformGuard.os?(*value)
+ when :wordsize
+ match &&= PlatformGuard.wordsize? value
+ when :pointer_size
+ match &&= PlatformGuard.pointer_size? value
+ end
+ end
+ match
+ end
+end
+
+def platform_is(*args, &block)
+ PlatformGuard.new(*args).run_if(:platform_is, &block)
+end
+
+def platform_is_not(*args, &block)
+ PlatformGuard.new(*args).run_unless(:platform_is_not, &block)
+end
diff --git a/spec/mspec/lib/mspec/guards/quarantine.rb b/spec/mspec/lib/mspec/guards/quarantine.rb
new file mode 100644
index 0000000000..ec4d01f9ea
--- /dev/null
+++ b/spec/mspec/lib/mspec/guards/quarantine.rb
@@ -0,0 +1,11 @@
+require 'mspec/guards/guard'
+
+class QuarantineGuard < SpecGuard
+ def match?
+ true
+ end
+end
+
+def quarantine!(&block)
+ QuarantineGuard.new.run_unless(:quarantine!, &block)
+end
diff --git a/spec/mspec/lib/mspec/guards/superuser.rb b/spec/mspec/lib/mspec/guards/superuser.rb
new file mode 100644
index 0000000000..e92ea7e862
--- /dev/null
+++ b/spec/mspec/lib/mspec/guards/superuser.rb
@@ -0,0 +1,15 @@
+require 'mspec/guards/guard'
+
+class SuperUserGuard < SpecGuard
+ def match?
+ Process.euid == 0
+ end
+end
+
+def as_superuser(&block)
+ SuperUserGuard.new.run_if(:as_superuser, &block)
+end
+
+def as_user(&block)
+ SuperUserGuard.new.run_unless(:as_user, &block)
+end
diff --git a/spec/mspec/lib/mspec/guards/support.rb b/spec/mspec/lib/mspec/guards/support.rb
new file mode 100644
index 0000000000..790bea1077
--- /dev/null
+++ b/spec/mspec/lib/mspec/guards/support.rb
@@ -0,0 +1,14 @@
+require 'mspec/guards/platform'
+
+class SupportedGuard < SpecGuard
+ def match?
+ if @parameters.include? :ruby
+ raise Exception, "improper use of not_supported_on guard"
+ end
+ !PlatformGuard.standard? and PlatformGuard.implementation?(*@parameters)
+ end
+end
+
+def not_supported_on(*args, &block)
+ SupportedGuard.new(*args).run_unless(:not_supported_on, &block)
+end
diff --git a/spec/mspec/lib/mspec/guards/version.rb b/spec/mspec/lib/mspec/guards/version.rb
new file mode 100644
index 0000000000..cb08fdac73
--- /dev/null
+++ b/spec/mspec/lib/mspec/guards/version.rb
@@ -0,0 +1,37 @@
+require 'mspec/utils/deprecate'
+require 'mspec/utils/version'
+require 'mspec/guards/guard'
+
+class VersionGuard < SpecGuard
+ FULL_RUBY_VERSION = SpecVersion.new SpecGuard.ruby_version(:full)
+
+ def initialize(version)
+ case version
+ when String
+ @version = SpecVersion.new version
+ when Range
+ MSpec.deprecate "an empty version range end", 'a specific version' if version.end.empty?
+ a = SpecVersion.new version.begin
+ b = SpecVersion.new version.end
+ unless version.exclude_end?
+ MSpec.deprecate "ruby_version_is with an inclusive range", 'an exclusive range ("2.1"..."2.3")'
+ end
+ @version = version.exclude_end? ? a...b : a..b
+ else
+ raise "version must be a String or Range but was a #{version.class}"
+ end
+ @parameters = [version]
+ end
+
+ def match?
+ if Range === @version
+ @version.include? FULL_RUBY_VERSION
+ else
+ FULL_RUBY_VERSION >= @version
+ end
+ end
+end
+
+def ruby_version_is(*args, &block)
+ VersionGuard.new(*args).run_if(:ruby_version_is, &block)
+end
diff --git a/spec/mspec/lib/mspec/helpers.rb b/spec/mspec/lib/mspec/helpers.rb
new file mode 100644
index 0000000000..b7ac9f4e85
--- /dev/null
+++ b/spec/mspec/lib/mspec/helpers.rb
@@ -0,0 +1,14 @@
+require 'mspec/helpers/argf'
+require 'mspec/helpers/argv'
+require 'mspec/helpers/datetime'
+require 'mspec/helpers/fixture'
+require 'mspec/helpers/flunk'
+require 'mspec/helpers/frozen_error_class'
+require 'mspec/helpers/fs'
+require 'mspec/helpers/io'
+require 'mspec/helpers/mock_to_path'
+require 'mspec/helpers/numeric'
+require 'mspec/helpers/ruby_exe'
+require 'mspec/helpers/scratch'
+require 'mspec/helpers/tmp'
+require 'mspec/helpers/warning'
diff --git a/spec/mspec/lib/mspec/helpers/argf.rb b/spec/mspec/lib/mspec/helpers/argf.rb
new file mode 100644
index 0000000000..4d3e0f46b3
--- /dev/null
+++ b/spec/mspec/lib/mspec/helpers/argf.rb
@@ -0,0 +1,35 @@
+# Convenience helper for specs using ARGF.
+# Set @argf to an instance of ARGF.class with the given +argv+.
+# That instance must be used instead of ARGF as ARGF is global
+# and it is not always possible to reset its state correctly.
+#
+# The helper yields to the block and then close
+# the files open by the instance. Example:
+#
+# describe "That" do
+# it "does something" do
+# argf ['a', 'b'] do
+# # do something
+# end
+# end
+# end
+def argf(argv)
+ if argv.empty? or argv.length > 2
+ raise "Only 1 or 2 filenames are allowed for the argf helper so files can be properly closed: #{argv.inspect}"
+ end
+ @argf ||= nil
+ raise "Cannot nest calls to the argf helper" if @argf
+
+ @argf = ARGF.class.new(*argv)
+ @__mspec_saved_argf_file__ = @argf.file
+ begin
+ yield
+ ensure
+ file1 = @__mspec_saved_argf_file__
+ file2 = @argf.file # Either the first file or the second
+ file1.close if !file1.closed? and file1 != STDIN
+ file2.close if !file2.closed? and file2 != STDIN
+ @argf = nil
+ @__mspec_saved_argf_file__ = nil
+ end
+end
diff --git a/spec/mspec/lib/mspec/helpers/argv.rb b/spec/mspec/lib/mspec/helpers/argv.rb
new file mode 100644
index 0000000000..9dac384dbd
--- /dev/null
+++ b/spec/mspec/lib/mspec/helpers/argv.rb
@@ -0,0 +1,44 @@
+# Convenience helper for altering ARGV. Saves the
+# value of ARGV and sets it to +args+. If a block
+# is given, yields to the block and then restores
+# the value of ARGV. The previously saved value of
+# ARGV can be restored by passing +:restore+. The
+# former is useful in a single spec. The latter is
+# useful in before/after actions. For example:
+#
+# describe "This" do
+# before do
+# argv ['a', 'b']
+# end
+#
+# after do
+# argv :restore
+# end
+#
+# it "does something" do
+# # do something
+# end
+# end
+#
+# describe "That" do
+# it "does something" do
+# argv ['a', 'b'] do
+# # do something
+# end
+# end
+# end
+def argv(args)
+ if args == :restore
+ ARGV.replace(@__mspec_saved_argv__ || [])
+ else
+ @__mspec_saved_argv__ = ARGV.dup
+ ARGV.replace args
+ if block_given?
+ begin
+ yield
+ ensure
+ argv :restore
+ end
+ end
+ end
+end
diff --git a/spec/mspec/lib/mspec/helpers/datetime.rb b/spec/mspec/lib/mspec/helpers/datetime.rb
new file mode 100644
index 0000000000..1520b971ea
--- /dev/null
+++ b/spec/mspec/lib/mspec/helpers/datetime.rb
@@ -0,0 +1,47 @@
+# The new_datetime helper makes writing DateTime specs more simple by
+# providing default constructor values and accepting a Hash of only the
+# constructor values needed for the particular spec. For example:
+#
+# new_datetime :hour => 1, :minute => 20
+#
+# Possible keys are:
+# :year, :month, :day, :hour, :minute, :second, :offset and :sg.
+def new_datetime(opts={})
+ require 'date'
+
+ value = {
+ :year => -4712,
+ :month => 1,
+ :day => 1,
+ :hour => 0,
+ :minute => 0,
+ :second => 0,
+ :offset => 0,
+ :sg => Date::ITALY
+ }.merge opts
+
+ DateTime.new value[:year], value[:month], value[:day], value[:hour],
+ value[:minute], value[:second], value[:offset], value[:sg]
+end
+
+def with_timezone(name, offset = nil, daylight_saving_zone = "")
+ zone = name.dup
+
+ if offset
+ # TZ convention is backwards
+ offset = -offset
+
+ zone += offset.to_s
+ zone += ":00:00"
+ end
+ zone += daylight_saving_zone
+
+ old = ENV["TZ"]
+ ENV["TZ"] = zone
+
+ begin
+ yield
+ ensure
+ ENV["TZ"] = old
+ end
+end
diff --git a/spec/mspec/lib/mspec/helpers/fixture.rb b/spec/mspec/lib/mspec/helpers/fixture.rb
new file mode 100644
index 0000000000..f3bbe423bd
--- /dev/null
+++ b/spec/mspec/lib/mspec/helpers/fixture.rb
@@ -0,0 +1,24 @@
+# Returns the name of a fixture file by adjoining the directory
+# of the +file+ argument with "fixtures" and the contents of the
+# +args+ array. For example,
+#
+# +file+ == "some/example_spec.rb"
+#
+# and
+#
+# +args+ == ["subdir", "file.txt"]
+#
+# then the result is the expanded path of
+#
+# "some/fixtures/subdir/file.txt".
+def fixture(file, *args)
+ path = File.dirname(file)
+ path = path[0..-7] if path[-7..-1] == "/shared"
+ fixtures = path[-9..-1] == "/fixtures" ? "" : "fixtures"
+ if File.respond_to?(:realpath)
+ path = File.realpath(path)
+ else
+ path = File.expand_path(path)
+ end
+ File.join(path, fixtures, args)
+end
diff --git a/spec/mspec/lib/mspec/helpers/flunk.rb b/spec/mspec/lib/mspec/helpers/flunk.rb
new file mode 100644
index 0000000000..68fb3cadac
--- /dev/null
+++ b/spec/mspec/lib/mspec/helpers/flunk.rb
@@ -0,0 +1,3 @@
+def flunk(msg="This example is a failure")
+ SpecExpectation.fail_with "Failed:", msg
+end
diff --git a/spec/mspec/lib/mspec/helpers/frozen_error_class.rb b/spec/mspec/lib/mspec/helpers/frozen_error_class.rb
new file mode 100644
index 0000000000..07cc2b4ba2
--- /dev/null
+++ b/spec/mspec/lib/mspec/helpers/frozen_error_class.rb
@@ -0,0 +1,17 @@
+require 'mspec/guards/version'
+
+# This helper makes it easy to write version independent
+# specs for frozen objects.
+unless respond_to? :frozen_error_class, true
+ ruby_version_is "2.5" do
+ def frozen_error_class
+ FrozenError
+ end
+ end
+
+ ruby_version_is ""..."2.5" do
+ def frozen_error_class
+ RuntimeError
+ end
+ end
+end
diff --git a/spec/mspec/lib/mspec/helpers/fs.rb b/spec/mspec/lib/mspec/helpers/fs.rb
new file mode 100644
index 0000000000..26dd821162
--- /dev/null
+++ b/spec/mspec/lib/mspec/helpers/fs.rb
@@ -0,0 +1,64 @@
+# Copies a file
+def cp(source, dest)
+ IO.copy_stream source, dest
+end
+
+# Creates each directory in path that does not exist.
+def mkdir_p(path)
+ parts = File.expand_path(path).split %r[/|\\]
+ name = parts.shift
+ parts.each do |part|
+ name = File.join name, part
+
+ if File.file? name
+ raise ArgumentError, "path component of #{path} is a file"
+ end
+
+ unless File.directory? name
+ begin
+ Dir.mkdir name
+ rescue Errno::EEXIST => e
+ if File.directory? name
+ # OK, another process/thread created the same directory
+ else
+ raise e
+ end
+ end
+ end
+ end
+end
+
+# Recursively removes all files and directories in +path+
+# if +path+ is a directory. Removes the file if +path+ is
+# a file.
+def rm_r(*paths)
+ paths.each do |path|
+ path = File.expand_path path
+
+ prefix = SPEC_TEMP_DIR
+ unless path[0, prefix.size] == prefix
+ raise ArgumentError, "#{path} is not prefixed by #{prefix}"
+ end
+
+ # File.symlink? needs to be checked first as
+ # File.exist? returns false for dangling symlinks
+ if File.symlink? path
+ File.unlink path
+ elsif File.directory? path
+ Dir.entries(path).each { |x| rm_r "#{path}/#{x}" unless x =~ /^\.\.?$/ }
+ Dir.rmdir path
+ elsif File.exist? path
+ File.delete path
+ end
+ end
+end
+
+# Creates a file +name+. Creates the directory for +name+
+# if it does not exist.
+def touch(name, mode="w")
+ mkdir_p File.dirname(name)
+
+ File.open(name, mode) do |f|
+ yield f if block_given?
+ end
+end
diff --git a/spec/mspec/lib/mspec/helpers/io.rb b/spec/mspec/lib/mspec/helpers/io.rb
new file mode 100644
index 0000000000..57dc0d53a4
--- /dev/null
+++ b/spec/mspec/lib/mspec/helpers/io.rb
@@ -0,0 +1,111 @@
+require 'mspec/guards/feature'
+
+class IOStub
+ def initialize
+ @buffer = []
+ @output = ''
+ end
+
+ def write(*str)
+ self << str.join
+ end
+
+ def << str
+ @buffer << str
+ self
+ end
+
+ def print(*str)
+ write(str.join + $\.to_s)
+ end
+
+ def method_missing(name, *args, &block)
+ to_s.send(name, *args, &block)
+ end
+
+ def == other
+ to_s == other
+ end
+
+ def =~ other
+ to_s =~ other
+ end
+
+ def puts(*str)
+ if str.empty?
+ write "\n"
+ else
+ write(str.collect { |s| s.to_s.chomp }.concat([nil]).join("\n"))
+ end
+ end
+
+ def printf(format, *args)
+ self << sprintf(format, *args)
+ end
+
+ def flush
+ @output += @buffer.join('')
+ @buffer.clear
+ self
+ end
+
+ def to_s
+ flush
+ @output
+ end
+
+ alias_method :to_str, :to_s
+
+ def inspect
+ to_s.inspect
+ end
+end
+
+# Creates a "bare" file descriptor (i.e. one that is not associated
+# with any Ruby object). The file descriptor can safely be passed
+# to IO.new without creating a Ruby object alias to the fd.
+def new_fd(name, mode="w:utf-8")
+ mode = options_or_mode(mode)
+
+ if mode.kind_of? Hash
+ if mode.key? :mode
+ mode = mode[:mode]
+ else
+ raise ArgumentError, "new_fd options Hash must include :mode"
+ end
+ end
+
+ IO.sysopen name, fmode(mode)
+end
+
+# Creates an IO instance for a temporary file name. The file
+# must be deleted.
+def new_io(name, mode="w:utf-8")
+ IO.new new_fd(name, options_or_mode(mode)), options_or_mode(mode)
+end
+
+# This helper simplifies passing file access modes regardless of
+# whether the :encoding feature is enabled. Only the access specifier
+# itself will be returned if :encoding is not enabled. Otherwise,
+# the full mode string will be returned (i.e. the helper is a no-op).
+def fmode(mode)
+ if FeatureGuard.enabled? :encoding
+ mode
+ else
+ mode.split(':').first
+ end
+end
+
+# This helper simplifies passing file access modes or options regardless of
+# whether the :encoding feature is enabled. Only the access specifier itself
+# will be returned if :encoding is not enabled. Otherwise, the full mode
+# string or option will be returned (i.e. the helper is a no-op).
+def options_or_mode(oom)
+ return fmode(oom) if oom.kind_of? String
+
+ if FeatureGuard.enabled? :encoding
+ oom
+ else
+ fmode(oom[:mode] || "r:utf-8")
+ end
+end
diff --git a/spec/mspec/lib/mspec/helpers/mock_to_path.rb b/spec/mspec/lib/mspec/helpers/mock_to_path.rb
new file mode 100644
index 0000000000..2780afc54a
--- /dev/null
+++ b/spec/mspec/lib/mspec/helpers/mock_to_path.rb
@@ -0,0 +1,6 @@
+def mock_to_path(path)
+ # Cannot use our Object#mock here since it conflicts with RSpec
+ obj = MockObject.new('path')
+ obj.should_receive(:to_path).and_return(path)
+ obj
+end
diff --git a/spec/mspec/lib/mspec/helpers/numeric.rb b/spec/mspec/lib/mspec/helpers/numeric.rb
new file mode 100644
index 0000000000..312aafae35
--- /dev/null
+++ b/spec/mspec/lib/mspec/helpers/numeric.rb
@@ -0,0 +1,70 @@
+require 'mspec/guards/platform'
+
+def nan_value
+ 0/0.0
+end
+
+def infinity_value
+ 1/0.0
+end
+
+def bignum_value(plus=0)
+ 0x8000_0000_0000_0000 + plus
+end
+
+# This is a bit hairy, but we need to be able to write specs that cover the
+# boundary between Fixnum and Bignum for operations like Fixnum#<<. Since
+# this boundary is implementation-dependent, we use these helpers to write
+# specs based on the relationship between values rather than specific
+# values.
+if PlatformGuard.standard? or PlatformGuard.implementation? :topaz
+ if PlatformGuard.wordsize? 32
+ def fixnum_max
+ (2**30) - 1
+ end
+
+ def fixnum_min
+ -(2**30)
+ end
+ elsif PlatformGuard.wordsize? 64
+ def fixnum_max
+ (2**62) - 1
+ end
+
+ def fixnum_min
+ -(2**62)
+ end
+ end
+elsif PlatformGuard.implementation? :opal
+ def fixnum_max
+ Integer::MAX
+ end
+
+ def fixnum_min
+ Integer::MIN
+ end
+elsif PlatformGuard.implementation? :rubinius
+ def fixnum_max
+ Fixnum::MAX
+ end
+
+ def fixnum_min
+ Fixnum::MIN
+ end
+elsif PlatformGuard.implementation?(:jruby) || PlatformGuard.implementation?(:truffleruby)
+ def fixnum_max
+ 9223372036854775807
+ end
+
+ def fixnum_min
+ -9223372036854775808
+ end
+else
+ def fixnum_max
+ raise "unknown implementation for fixnum_max() helper"
+ end
+
+ def fixnum_min
+ raise "unknown implementation for fixnum_min() helper"
+ end
+end
diff --git a/spec/mspec/lib/mspec/helpers/ruby_exe.rb b/spec/mspec/lib/mspec/helpers/ruby_exe.rb
new file mode 100644
index 0000000000..f74ed014ce
--- /dev/null
+++ b/spec/mspec/lib/mspec/helpers/ruby_exe.rb
@@ -0,0 +1,186 @@
+require 'mspec/guards/platform'
+require 'mspec/helpers/tmp'
+
+# The ruby_exe helper provides a wrapper for invoking the
+# same Ruby interpreter with the same falgs as the one running
+# the specs and getting the output from running the code.
+# If +code+ is a file that exists, it will be run.
+# Otherwise, +code+ should be Ruby code that will be run with
+# the -e command line option. For example:
+#
+# ruby_exe('path/to/some/file.rb')
+#
+# will be executed as
+#
+# `#{RUBY_EXE} 'path/to/some/file.rb'`
+#
+# while
+#
+# ruby_exe('puts "hello, world."')
+#
+# will be executed as
+#
+# `#{RUBY_EXE} -e 'puts "hello, world."'`
+#
+# The ruby_exe helper also accepts an options hash with three
+# keys: :options, :args and :env. For example:
+#
+# ruby_exe('file.rb', :options => "-w",
+# :args => "> file.txt",
+# :env => { :FOO => "bar" })
+#
+# will be executed as
+#
+# `#{RUBY_EXE} -w #{'file.rb'} > file.txt`
+#
+# with access to ENV["FOO"] with value "bar".
+#
+# If +nil+ is passed for the first argument, the command line
+# will be built only from the options hash.
+#
+# If no arguments are passed to ruby_exe, it returns an Array
+# containing the interpreter executable and the flags:
+#
+# spawn(*ruby_exe, "-e", "puts :hello")
+#
+# This avoids spawning an extra shell, and ensure the pid returned by spawn
+# corresponds to the ruby process and not the shell.
+#
+# The RUBY_EXE constant is setup by mspec automatically
+# and is used by ruby_exe and ruby_cmd. The mspec runner script
+# will set ENV['RUBY_EXE'] to the name of the executable used
+# to invoke the mspec-run script. The value of RUBY_EXE will be
+# constructed as follows:
+#
+# 1. the value of ENV['RUBY_EXE']
+# 2. an explicit value based on RUBY_ENGINE
+# 3. cwd/(RUBY_ENGINE + $(EXEEXT) || $(exeext) || '')
+# 4. $(bindir)/$(RUBY_INSTALL_NAME)
+#
+# The value will only be used if the file exists and is executable.
+# The flags will then be appended to the resulting value.
+#
+# These 4 ways correspond to the following scenarios:
+#
+# 1. Using the MSpec runner scripts, the name of the
+# executable is explicitly passed by ENV['RUBY_EXE']
+# so there is no ambiguity.
+#
+# Otherwise, if using RSpec (or something else)
+#
+# 2. Running the specs while developing an alternative
+# Ruby implementation. This explicitly names the
+# executable in the development directory based on
+# the value of RUBY_ENGINE.
+# 3. Running the specs within the source directory for
+# some implementation. (E.g. a local build directory.)
+# 4. Running the specs against some installed Ruby
+# implementation.
+#
+# Additionally, the flags passed to mspec
+# (with -T on the command line or in the config with set :flags)
+# will be appended to RUBY_EXE so that the interpreter
+# is always called with those flags.
+
+def ruby_exe_options(option)
+ case option
+ when :env
+ ENV['RUBY_EXE']
+ when :engine
+ case RUBY_ENGINE
+ when 'rbx'
+ "bin/rbx"
+ when 'jruby'
+ "bin/jruby"
+ when 'maglev'
+ "maglev-ruby"
+ when 'topaz'
+ "topaz"
+ when 'ironruby'
+ "ir"
+ end
+ when :name
+ require 'rbconfig'
+ bin = RUBY_ENGINE + (RbConfig::CONFIG['EXEEXT'] || RbConfig::CONFIG['exeext'] || '')
+ File.join(".", bin)
+ when :install_name
+ require 'rbconfig'
+ bin = RbConfig::CONFIG["RUBY_INSTALL_NAME"] || RbConfig::CONFIG["ruby_install_name"]
+ bin << (RbConfig::CONFIG['EXEEXT'] || RbConfig::CONFIG['exeext'] || '')
+ File.join(RbConfig::CONFIG['bindir'], bin)
+ end
+end
+
+def resolve_ruby_exe
+ [:env, :engine, :name, :install_name].each do |option|
+ next unless exe = ruby_exe_options(option)
+
+ if File.file?(exe) and File.executable?(exe)
+ exe = File.expand_path(exe)
+ exe = exe.tr('/', '\\') if PlatformGuard.windows?
+ flags = ENV['RUBY_FLAGS']
+ if flags and !flags.empty?
+ return exe + ' ' + flags
+ else
+ return exe
+ end
+ end
+ end
+ raise Exception, "Unable to find a suitable ruby executable."
+end
+
+def ruby_exe(code = :not_given, opts = {})
+ if opts[:dir]
+ raise "ruby_exe(..., dir: dir) is no longer supported, use Dir.chdir"
+ end
+
+ if code == :not_given
+ return RUBY_EXE.split(' ')
+ end
+
+ env = opts[:env] || {}
+ saved_env = {}
+ env.each do |key, value|
+ key = key.to_s
+ saved_env[key] = ENV[key] if ENV.key? key
+ ENV[key] = value
+ end
+
+ escape = opts.delete(:escape)
+ if code and !File.exist?(code) and escape != false
+ tmpfile = tmp("rubyexe.rb")
+ File.open(tmpfile, "w") { |f| f.write(code) }
+ code = tmpfile
+ end
+
+ begin
+ platform_is_not :opal do
+ `#{ruby_cmd(code, opts)}`
+ end
+ ensure
+ saved_env.each { |key, value| ENV[key] = value }
+ env.keys.each do |key|
+ key = key.to_s
+ ENV.delete key unless saved_env.key? key
+ end
+ File.delete tmpfile if tmpfile
+ end
+end
+
+def ruby_cmd(code, opts = {})
+ body = code
+
+ if opts[:escape]
+ raise "escape: true is no longer supported in ruby_cmd, use ruby_exe or a fixture"
+ end
+
+ if code and !File.exist?(code)
+ body = "-e #{code.inspect}"
+ end
+
+ [RUBY_EXE, opts[:options], body, opts[:args]].compact.join(' ')
+end
+
+unless Object.const_defined?(:RUBY_EXE) and RUBY_EXE
+ RUBY_EXE = resolve_ruby_exe
+end
diff --git a/spec/mspec/lib/mspec/helpers/scratch.rb b/spec/mspec/lib/mspec/helpers/scratch.rb
new file mode 100644
index 0000000000..a6b0c02748
--- /dev/null
+++ b/spec/mspec/lib/mspec/helpers/scratch.rb
@@ -0,0 +1,17 @@
+module ScratchPad
+ def self.clear
+ @record = nil
+ end
+
+ def self.record(arg)
+ @record = arg
+ end
+
+ def self.<<(arg)
+ @record << arg
+ end
+
+ def self.recorded
+ @record
+ end
+end
diff --git a/spec/mspec/lib/mspec/helpers/tmp.rb b/spec/mspec/lib/mspec/helpers/tmp.rb
new file mode 100644
index 0000000000..4e1273dcfe
--- /dev/null
+++ b/spec/mspec/lib/mspec/helpers/tmp.rb
@@ -0,0 +1,43 @@
+# Creates a temporary directory in the current working directory
+# for temporary files created while running the specs. All specs
+# should clean up any temporary files created so that the temp
+# directory is empty when the process exits.
+
+SPEC_TEMP_DIR = File.expand_path(ENV["SPEC_TEMP_DIR"] || "rubyspec_temp")
+
+SPEC_TEMP_UNIQUIFIER = "0"
+
+SPEC_TEMP_DIR_PID = Process.pid
+
+at_exit do
+ begin
+ if SPEC_TEMP_DIR_PID == Process.pid
+ Dir.delete SPEC_TEMP_DIR if File.directory? SPEC_TEMP_DIR
+ end
+ rescue SystemCallError
+ STDERR.puts <<-EOM
+
+-----------------------------------------------------
+The rubyspec temp directory is not empty. Ensure that
+all specs are cleaning up temporary files:
+ #{SPEC_TEMP_DIR}
+-----------------------------------------------------
+
+ EOM
+ rescue Object => e
+ STDERR.puts "failed to remove spec temp directory"
+ STDERR.puts e.message
+ end
+end
+
+def tmp(name, uniquify=true)
+ Dir.mkdir SPEC_TEMP_DIR unless Dir.exist? SPEC_TEMP_DIR
+
+ if uniquify and !name.empty?
+ slash = name.rindex "/"
+ index = slash ? slash + 1 : 0
+ name.insert index, "#{SPEC_TEMP_UNIQUIFIER.succ!}-"
+ end
+
+ File.join SPEC_TEMP_DIR, name
+end
diff --git a/spec/mspec/lib/mspec/helpers/warning.rb b/spec/mspec/lib/mspec/helpers/warning.rb
new file mode 100644
index 0000000000..9e093074e5
--- /dev/null
+++ b/spec/mspec/lib/mspec/helpers/warning.rb
@@ -0,0 +1,7 @@
+def suppress_warning
+ verbose = $VERBOSE
+ $VERBOSE = nil
+ yield
+ensure
+ $VERBOSE = verbose
+end
diff --git a/spec/mspec/lib/mspec/matchers.rb b/spec/mspec/lib/mspec/matchers.rb
new file mode 100644
index 0000000000..f3e8e7bb73
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers.rb
@@ -0,0 +1,36 @@
+require 'mspec/matchers/base'
+require 'mspec/matchers/be_an_instance_of'
+require 'mspec/matchers/be_ancestor_of'
+require 'mspec/matchers/be_close'
+require 'mspec/matchers/be_computed_by'
+require 'mspec/matchers/be_empty'
+require 'mspec/matchers/be_false'
+require 'mspec/matchers/be_kind_of'
+require 'mspec/matchers/be_nan'
+require 'mspec/matchers/be_nil'
+require 'mspec/matchers/be_true'
+require 'mspec/matchers/be_true_or_false'
+require 'mspec/matchers/complain'
+require 'mspec/matchers/eql'
+require 'mspec/matchers/equal'
+require 'mspec/matchers/equal_element'
+require 'mspec/matchers/have_constant'
+require 'mspec/matchers/have_class_variable'
+require 'mspec/matchers/have_instance_method'
+require 'mspec/matchers/have_instance_variable'
+require 'mspec/matchers/have_method'
+require 'mspec/matchers/have_private_instance_method'
+require 'mspec/matchers/have_private_method'
+require 'mspec/matchers/have_protected_instance_method'
+require 'mspec/matchers/have_public_instance_method'
+require 'mspec/matchers/have_singleton_method'
+require 'mspec/matchers/include'
+require 'mspec/matchers/include_any_of'
+require 'mspec/matchers/infinity'
+require 'mspec/matchers/match_yaml'
+require 'mspec/matchers/raise_error'
+require 'mspec/matchers/output'
+require 'mspec/matchers/output_to_fd'
+require 'mspec/matchers/respond_to'
+require 'mspec/matchers/signed_zero'
+require 'mspec/matchers/block_caller'
diff --git a/spec/mspec/lib/mspec/matchers/base.rb b/spec/mspec/lib/mspec/matchers/base.rb
new file mode 100644
index 0000000000..fc2d36c84a
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/base.rb
@@ -0,0 +1,107 @@
+module MSpecMatchers
+end
+
+class MSpecEnv
+ include MSpecMatchers
+end
+
+# Expectations are sometimes used in a module body
+class Module
+ include MSpecMatchers
+end
+
+class SpecPositiveOperatorMatcher
+ def initialize(actual)
+ @actual = actual
+ end
+
+ def ==(expected)
+ unless @actual == expected
+ SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
+ "to equal #{expected.pretty_inspect}")
+ end
+ end
+
+ def <(expected)
+ unless @actual < expected
+ SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
+ "to be less than #{expected.pretty_inspect}")
+ end
+ end
+
+ def <=(expected)
+ unless @actual <= expected
+ SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
+ "to be less than or equal to #{expected.pretty_inspect}")
+ end
+ end
+
+ def >(expected)
+ unless @actual > expected
+ SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
+ "to be greater than #{expected.pretty_inspect}")
+ end
+ end
+
+ def >=(expected)
+ unless @actual >= expected
+ SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
+ "to be greater than or equal to #{expected.pretty_inspect}")
+ end
+ end
+
+ def =~(expected)
+ unless @actual =~ expected
+ SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
+ "to match #{expected.pretty_inspect}")
+ end
+ end
+end
+
+class SpecNegativeOperatorMatcher
+ def initialize(actual)
+ @actual = actual
+ end
+
+ def ==(expected)
+ if @actual == expected
+ SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
+ "not to equal #{expected.pretty_inspect}")
+ end
+ end
+
+ def <(expected)
+ if @actual < expected
+ SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
+ "not to be less than #{expected.pretty_inspect}")
+ end
+ end
+
+ def <=(expected)
+ if @actual <= expected
+ SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
+ "not to be less than or equal to #{expected.pretty_inspect}")
+ end
+ end
+
+ def >(expected)
+ if @actual > expected
+ SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
+ "not to be greater than #{expected.pretty_inspect}")
+ end
+ end
+
+ def >=(expected)
+ if @actual >= expected
+ SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
+ "not to be greater than or equal to #{expected.pretty_inspect}")
+ end
+ end
+
+ def =~(expected)
+ if @actual =~ expected
+ SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
+ "not to match #{expected.pretty_inspect}")
+ end
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/be_an_instance_of.rb b/spec/mspec/lib/mspec/matchers/be_an_instance_of.rb
new file mode 100644
index 0000000000..fdf3736ac2
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/be_an_instance_of.rb
@@ -0,0 +1,26 @@
+class BeAnInstanceOfMatcher
+ def initialize(expected)
+ @expected = expected
+ end
+
+ def matches?(actual)
+ @actual = actual
+ @actual.instance_of?(@expected)
+ end
+
+ def failure_message
+ ["Expected #{@actual.inspect} (#{@actual.class})",
+ "to be an instance of #{@expected}"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual.inspect} (#{@actual.class})",
+ "not to be an instance of #{@expected}"]
+ end
+end
+
+module MSpecMatchers
+ private def be_an_instance_of(expected)
+ BeAnInstanceOfMatcher.new(expected)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/be_ancestor_of.rb b/spec/mspec/lib/mspec/matchers/be_ancestor_of.rb
new file mode 100644
index 0000000000..05f72099e4
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/be_ancestor_of.rb
@@ -0,0 +1,24 @@
+class BeAncestorOfMatcher
+ def initialize(expected)
+ @expected = expected
+ end
+
+ def matches?(actual)
+ @actual = actual
+ @expected.ancestors.include? @actual
+ end
+
+ def failure_message
+ ["Expected #{@actual}", "to be an ancestor of #{@expected}"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual}", "not to be an ancestor of #{@expected}"]
+ end
+end
+
+module MSpecMatchers
+ private def be_ancestor_of(expected)
+ BeAncestorOfMatcher.new(expected)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/be_close.rb b/spec/mspec/lib/mspec/matchers/be_close.rb
new file mode 100644
index 0000000000..ea9e7f5496
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/be_close.rb
@@ -0,0 +1,27 @@
+TOLERANCE = 0.00003 unless Object.const_defined?(:TOLERANCE)
+
+class BeCloseMatcher
+ def initialize(expected, tolerance)
+ @expected = expected
+ @tolerance = tolerance
+ end
+
+ def matches?(actual)
+ @actual = actual
+ (@actual - @expected).abs <= @tolerance
+ end
+
+ def failure_message
+ ["Expected #{@actual}", "to be within #{@expected} +/- #{@tolerance}"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual}", "not to be within #{@expected} +/- #{@tolerance}"]
+ end
+end
+
+module MSpecMatchers
+ private def be_close(expected, tolerance)
+ BeCloseMatcher.new(expected, tolerance)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/be_computed_by.rb b/spec/mspec/lib/mspec/matchers/be_computed_by.rb
new file mode 100644
index 0000000000..2e31bc93af
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/be_computed_by.rb
@@ -0,0 +1,37 @@
+class BeComputedByMatcher
+ def initialize(sym, *args)
+ @method = sym
+ @args = args
+ end
+
+ def matches?(array)
+ array.each do |line|
+ @receiver = line.shift
+ @value = line.pop
+ @arguments = line
+ @arguments += @args
+ @actual = @receiver.send(@method, *@arguments)
+ return false unless @actual == @value
+ end
+
+ return true
+ end
+
+ def method_call
+ method_call = "#{@receiver.inspect}.#{@method}"
+ unless @arguments.empty?
+ method_call = "#{method_call} from #{@arguments.map { |x| x.inspect }.join(", ")}"
+ end
+ method_call
+ end
+
+ def failure_message
+ ["Expected #{@value.inspect}", "to be computed by #{method_call} (computed #{@actual.inspect} instead)"]
+ end
+end
+
+module MSpecMatchers
+ private def be_computed_by(sym, *args)
+ BeComputedByMatcher.new(sym, *args)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/be_empty.rb b/spec/mspec/lib/mspec/matchers/be_empty.rb
new file mode 100644
index 0000000000..5abd5c9485
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/be_empty.rb
@@ -0,0 +1,20 @@
+class BeEmptyMatcher
+ def matches?(actual)
+ @actual = actual
+ @actual.empty?
+ end
+
+ def failure_message
+ ["Expected #{@actual.inspect}", "to be empty"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual.inspect}", "not to be empty"]
+ end
+end
+
+module MSpecMatchers
+ private def be_empty
+ BeEmptyMatcher.new
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/be_false.rb b/spec/mspec/lib/mspec/matchers/be_false.rb
new file mode 100644
index 0000000000..9e9a2608e1
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/be_false.rb
@@ -0,0 +1,20 @@
+class BeFalseMatcher
+ def matches?(actual)
+ @actual = actual
+ @actual == false
+ end
+
+ def failure_message
+ ["Expected #{@actual.inspect}", "to be false"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual.inspect}", "not to be false"]
+ end
+end
+
+module MSpecMatchers
+ private def be_false
+ BeFalseMatcher.new
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/be_kind_of.rb b/spec/mspec/lib/mspec/matchers/be_kind_of.rb
new file mode 100644
index 0000000000..a69906f210
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/be_kind_of.rb
@@ -0,0 +1,24 @@
+class BeKindOfMatcher
+ def initialize(expected)
+ @expected = expected
+ end
+
+ def matches?(actual)
+ @actual = actual
+ @actual.is_a?(@expected)
+ end
+
+ def failure_message
+ ["Expected #{@actual.inspect} (#{@actual.class})", "to be kind of #{@expected}"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual.inspect} (#{@actual.class})", "not to be kind of #{@expected}"]
+ end
+end
+
+module MSpecMatchers
+ private def be_kind_of(expected)
+ BeKindOfMatcher.new(expected)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/be_nan.rb b/spec/mspec/lib/mspec/matchers/be_nan.rb
new file mode 100644
index 0000000000..b279d8f1cf
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/be_nan.rb
@@ -0,0 +1,20 @@
+class BeNaNMatcher
+ def matches?(actual)
+ @actual = actual
+ @actual.kind_of?(Float) && @actual.nan?
+ end
+
+ def failure_message
+ ["Expected #{@actual}", "to be NaN"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual}", "not to be NaN"]
+ end
+end
+
+module MSpecMatchers
+ private def be_nan
+ BeNaNMatcher.new
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/be_nil.rb b/spec/mspec/lib/mspec/matchers/be_nil.rb
new file mode 100644
index 0000000000..049b1e3a53
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/be_nil.rb
@@ -0,0 +1,20 @@
+class BeNilMatcher
+ def matches?(actual)
+ @actual = actual
+ @actual.nil?
+ end
+
+ def failure_message
+ ["Expected #{@actual.inspect}", "to be nil"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual.inspect}", "not to be nil"]
+ end
+end
+
+module MSpecMatchers
+ private def be_nil
+ BeNilMatcher.new
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/be_true.rb b/spec/mspec/lib/mspec/matchers/be_true.rb
new file mode 100644
index 0000000000..52f5013752
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/be_true.rb
@@ -0,0 +1,20 @@
+class BeTrueMatcher
+ def matches?(actual)
+ @actual = actual
+ @actual == true
+ end
+
+ def failure_message
+ ["Expected #{@actual.inspect}", "to be true"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual.inspect}", "not to be true"]
+ end
+end
+
+module MSpecMatchers
+ private def be_true
+ BeTrueMatcher.new
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/be_true_or_false.rb b/spec/mspec/lib/mspec/matchers/be_true_or_false.rb
new file mode 100644
index 0000000000..4294b08d1b
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/be_true_or_false.rb
@@ -0,0 +1,20 @@
+class BeTrueOrFalseMatcher
+ def matches?(actual)
+ @actual = actual
+ @actual == true || @actual == false
+ end
+
+ def failure_message
+ ["Expected #{@actual.inspect}", "to be true or false"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual.inspect}", "not to be true or false"]
+ end
+end
+
+module MSpecMatchers
+ private def be_true_or_false
+ BeTrueOrFalseMatcher.new
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/block_caller.rb b/spec/mspec/lib/mspec/matchers/block_caller.rb
new file mode 100644
index 0000000000..30fab4fc68
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/block_caller.rb
@@ -0,0 +1,37 @@
+class BlockingMatcher
+ def matches?(block)
+ t = Thread.new do
+ block.call
+ end
+
+ loop do
+ case t.status
+ when "sleep" # blocked
+ t.kill
+ t.join
+ return true
+ when false # terminated normally, so never blocked
+ t.join
+ return false
+ when nil # terminated exceptionally
+ t.value
+ else
+ Thread.pass
+ end
+ end
+ end
+
+ def failure_message
+ ['Expected the given Proc', 'to block the caller']
+ end
+
+ def negative_failure_message
+ ['Expected the given Proc', 'to not block the caller']
+ end
+end
+
+module MSpecMatchers
+ private def block_caller
+ BlockingMatcher.new
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/complain.rb b/spec/mspec/lib/mspec/matchers/complain.rb
new file mode 100644
index 0000000000..4bcb255040
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/complain.rb
@@ -0,0 +1,60 @@
+require 'mspec/helpers/io'
+
+class ComplainMatcher
+ def initialize(complaint)
+ @complaint = complaint
+ end
+
+ def matches?(proc)
+ @saved_err = $stderr
+ @verbose = $VERBOSE
+ begin
+ err = $stderr = IOStub.new
+ $VERBOSE = false
+ Thread.current[:in_mspec_complain_matcher] = true
+ proc.call
+ ensure
+ $VERBOSE = @verbose
+ $stderr = @saved_err
+ Thread.current[:in_mspec_complain_matcher] = false
+ end
+
+ @warning = err.to_s
+ unless @complaint.nil?
+ case @complaint
+ when Regexp
+ return false unless @warning =~ @complaint
+ else
+ return false unless @warning == @complaint
+ end
+ end
+
+ return @warning.empty? ? false : true
+ end
+
+ def failure_message
+ if @complaint.nil?
+ ["Expected a warning", "but received none"]
+ elsif @complaint.kind_of? Regexp
+ ["Expected warning to match: #{@complaint.inspect}", "but got: #{@warning.chomp.inspect}"]
+ else
+ ["Expected warning: #{@complaint.inspect}", "but got: #{@warning.chomp.inspect}"]
+ end
+ end
+
+ def negative_failure_message
+ if @complaint.nil?
+ ["Unexpected warning: ", @warning.chomp.inspect]
+ elsif @complaint.kind_of? Regexp
+ ["Expected warning not to match: #{@complaint.inspect}", "but got: #{@warning.chomp.inspect}"]
+ else
+ ["Expected warning: #{@complaint.inspect}", "but got: #{@warning.chomp.inspect}"]
+ end
+ end
+end
+
+module MSpecMatchers
+ private def complain(complaint=nil)
+ ComplainMatcher.new(complaint)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/eql.rb b/spec/mspec/lib/mspec/matchers/eql.rb
new file mode 100644
index 0000000000..a855789550
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/eql.rb
@@ -0,0 +1,26 @@
+class EqlMatcher
+ def initialize(expected)
+ @expected = expected
+ end
+
+ def matches?(actual)
+ @actual = actual
+ @actual.eql?(@expected)
+ end
+
+ def failure_message
+ ["Expected #{@actual.pretty_inspect}",
+ "to have same value and type as #{@expected.pretty_inspect}"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual.pretty_inspect}",
+ "not to have same value or type as #{@expected.pretty_inspect}"]
+ end
+end
+
+module MSpecMatchers
+ private def eql(expected)
+ EqlMatcher.new(expected)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/equal.rb b/spec/mspec/lib/mspec/matchers/equal.rb
new file mode 100644
index 0000000000..5dc77d27ea
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/equal.rb
@@ -0,0 +1,26 @@
+class EqualMatcher
+ def initialize(expected)
+ @expected = expected
+ end
+
+ def matches?(actual)
+ @actual = actual
+ @actual.equal?(@expected)
+ end
+
+ def failure_message
+ ["Expected #{@actual.pretty_inspect}",
+ "to be identical to #{@expected.pretty_inspect}"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual.pretty_inspect}",
+ "not to be identical to #{@expected.pretty_inspect}"]
+ end
+end
+
+module MSpecMatchers
+ private def equal(expected)
+ EqualMatcher.new(expected)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/equal_element.rb b/spec/mspec/lib/mspec/matchers/equal_element.rb
new file mode 100644
index 0000000000..1e9dfbcca1
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/equal_element.rb
@@ -0,0 +1,78 @@
+class EqualElementMatcher
+ def initialize(element, attributes = nil, content = nil, options = {})
+ @element = element
+ @attributes = attributes
+ @content = content
+ @options = options
+ end
+
+ def matches?(actual)
+ @actual = actual
+
+ matched = true
+
+ if @options[:not_closed]
+ matched &&= actual =~ /^#{Regexp.quote("<" + @element)}.*#{Regexp.quote(">" + (@content || ''))}$/
+ else
+ matched &&= actual =~ /^#{Regexp.quote("<" + @element)}/
+ matched &&= actual =~ /#{Regexp.quote("</" + @element + ">")}$/
+ matched &&= actual =~ /#{Regexp.quote(">" + @content + "</")}/ if @content
+ end
+
+ if @attributes
+ if @attributes.empty?
+ matched &&= actual.scan(/\w+\=\"(.*)\"/).size == 0
+ else
+ @attributes.each do |key, value|
+ if value == true
+ matched &&= (actual.scan(/#{Regexp.quote(key)}(\s|>)/).size == 1)
+ else
+ matched &&= (actual.scan(%Q{ #{key}="#{value}"}).size == 1)
+ end
+ end
+ end
+ end
+
+ !!matched
+ end
+
+ def failure_message
+ ["Expected #{@actual.pretty_inspect}",
+ "to be a '#{@element}' element with #{attributes_for_failure_message} and #{content_for_failure_message}"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual.pretty_inspect}",
+ "not to be a '#{@element}' element with #{attributes_for_failure_message} and #{content_for_failure_message}"]
+ end
+
+ def attributes_for_failure_message
+ if @attributes
+ if @attributes.empty?
+ "no attributes"
+ else
+ @attributes.inject([]) { |memo, n| memo << %Q{#{n[0]}="#{n[1]}"} }.join(" ")
+ end
+ else
+ "any attributes"
+ end
+ end
+
+ def content_for_failure_message
+ if @content
+ if @content.empty?
+ "no content"
+ else
+ "#{@content.inspect} as content"
+ end
+ else
+ "any content"
+ end
+ end
+end
+
+module MSpecMatchers
+ private def equal_element(*args)
+ EqualElementMatcher.new(*args)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/have_class_variable.rb b/spec/mspec/lib/mspec/matchers/have_class_variable.rb
new file mode 100644
index 0000000000..dd43ced621
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/have_class_variable.rb
@@ -0,0 +1,12 @@
+require 'mspec/matchers/variable'
+
+class HaveClassVariableMatcher < VariableMatcher
+ self.variables_method = :class_variables
+ self.description = 'class variable'
+end
+
+module MSpecMatchers
+ private def have_class_variable(variable)
+ HaveClassVariableMatcher.new(variable)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/have_constant.rb b/spec/mspec/lib/mspec/matchers/have_constant.rb
new file mode 100644
index 0000000000..6ec7c75b85
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/have_constant.rb
@@ -0,0 +1,12 @@
+require 'mspec/matchers/variable'
+
+class HaveConstantMatcher < VariableMatcher
+ self.variables_method = :constants
+ self.description = 'constant'
+end
+
+module MSpecMatchers
+ private def have_constant(variable)
+ HaveConstantMatcher.new(variable)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/have_instance_method.rb b/spec/mspec/lib/mspec/matchers/have_instance_method.rb
new file mode 100644
index 0000000000..636aaf3e47
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/have_instance_method.rb
@@ -0,0 +1,24 @@
+require 'mspec/matchers/method'
+
+class HaveInstanceMethodMatcher < MethodMatcher
+ def matches?(mod)
+ @mod = mod
+ mod.instance_methods(@include_super).include? @method
+ end
+
+ def failure_message
+ ["Expected #{@mod} to have instance method '#{@method.to_s}'",
+ "but it does not"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@mod} NOT to have instance method '#{@method.to_s}'",
+ "but it does"]
+ end
+end
+
+module MSpecMatchers
+ private def have_instance_method(method, include_super=true)
+ HaveInstanceMethodMatcher.new method, include_super
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/have_instance_variable.rb b/spec/mspec/lib/mspec/matchers/have_instance_variable.rb
new file mode 100644
index 0000000000..de51b3209d
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/have_instance_variable.rb
@@ -0,0 +1,12 @@
+require 'mspec/matchers/variable'
+
+class HaveInstanceVariableMatcher < VariableMatcher
+ self.variables_method = :instance_variables
+ self.description = 'instance variable'
+end
+
+module MSpecMatchers
+ private def have_instance_variable(variable)
+ HaveInstanceVariableMatcher.new(variable)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/have_method.rb b/spec/mspec/lib/mspec/matchers/have_method.rb
new file mode 100644
index 0000000000..35dae03af0
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/have_method.rb
@@ -0,0 +1,24 @@
+require 'mspec/matchers/method'
+
+class HaveMethodMatcher < MethodMatcher
+ def matches?(mod)
+ @mod = mod
+ @mod.methods(@include_super).include? @method
+ end
+
+ def failure_message
+ ["Expected #{@mod} to have method '#{@method.to_s}'",
+ "but it does not"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@mod} NOT to have method '#{@method.to_s}'",
+ "but it does"]
+ end
+end
+
+module MSpecMatchers
+ private def have_method(method, include_super=true)
+ HaveMethodMatcher.new method, include_super
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/have_private_instance_method.rb b/spec/mspec/lib/mspec/matchers/have_private_instance_method.rb
new file mode 100644
index 0000000000..4eb7133055
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/have_private_instance_method.rb
@@ -0,0 +1,24 @@
+require 'mspec/matchers/method'
+
+class HavePrivateInstanceMethodMatcher < MethodMatcher
+ def matches?(mod)
+ @mod = mod
+ mod.private_instance_methods(@include_super).include? @method
+ end
+
+ def failure_message
+ ["Expected #{@mod} to have private instance method '#{@method.to_s}'",
+ "but it does not"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@mod} NOT to have private instance method '#{@method.to_s}'",
+ "but it does"]
+ end
+end
+
+module MSpecMatchers
+ private def have_private_instance_method(method, include_super=true)
+ HavePrivateInstanceMethodMatcher.new method, include_super
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/have_private_method.rb b/spec/mspec/lib/mspec/matchers/have_private_method.rb
new file mode 100644
index 0000000000..3433d982cc
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/have_private_method.rb
@@ -0,0 +1,24 @@
+require 'mspec/matchers/method'
+
+class HavePrivateMethodMatcher < MethodMatcher
+ def matches?(mod)
+ @mod = mod
+ mod.private_methods(@include_super).include? @method
+ end
+
+ def failure_message
+ ["Expected #{@mod} to have private method '#{@method.to_s}'",
+ "but it does not"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@mod} NOT to have private method '#{@method.to_s}'",
+ "but it does"]
+ end
+end
+
+module MSpecMatchers
+ private def have_private_method(method, include_super=true)
+ HavePrivateMethodMatcher.new method, include_super
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/have_protected_instance_method.rb b/spec/mspec/lib/mspec/matchers/have_protected_instance_method.rb
new file mode 100644
index 0000000000..641d4d0dc2
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/have_protected_instance_method.rb
@@ -0,0 +1,24 @@
+require 'mspec/matchers/method'
+
+class HaveProtectedInstanceMethodMatcher < MethodMatcher
+ def matches?(mod)
+ @mod = mod
+ mod.protected_instance_methods(@include_super).include? @method
+ end
+
+ def failure_message
+ ["Expected #{@mod} to have protected instance method '#{@method.to_s}'",
+ "but it does not"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@mod} NOT to have protected instance method '#{@method.to_s}'",
+ "but it does"]
+ end
+end
+
+module MSpecMatchers
+ private def have_protected_instance_method(method, include_super=true)
+ HaveProtectedInstanceMethodMatcher.new method, include_super
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/have_public_instance_method.rb b/spec/mspec/lib/mspec/matchers/have_public_instance_method.rb
new file mode 100644
index 0000000000..501c0a418e
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/have_public_instance_method.rb
@@ -0,0 +1,24 @@
+require 'mspec/matchers/method'
+
+class HavePublicInstanceMethodMatcher < MethodMatcher
+ def matches?(mod)
+ @mod = mod
+ mod.public_instance_methods(@include_super).include? @method
+ end
+
+ def failure_message
+ ["Expected #{@mod} to have public instance method '#{@method.to_s}'",
+ "but it does not"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@mod} NOT to have public instance method '#{@method.to_s}'",
+ "but it does"]
+ end
+end
+
+module MSpecMatchers
+ private def have_public_instance_method(method, include_super=true)
+ HavePublicInstanceMethodMatcher.new method, include_super
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/have_singleton_method.rb b/spec/mspec/lib/mspec/matchers/have_singleton_method.rb
new file mode 100644
index 0000000000..95d78709ff
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/have_singleton_method.rb
@@ -0,0 +1,24 @@
+require 'mspec/matchers/method'
+
+class HaveSingletonMethodMatcher < MethodMatcher
+ def matches?(obj)
+ @obj = obj
+ obj.singleton_methods(@include_super).include? @method
+ end
+
+ def failure_message
+ ["Expected #{@obj} to have singleton method '#{@method.to_s}'",
+ "but it does not"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@obj} NOT to have singleton method '#{@method.to_s}'",
+ "but it does"]
+ end
+end
+
+module MSpecMatchers
+ private def have_singleton_method(method, include_super=true)
+ HaveSingletonMethodMatcher.new method, include_super
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/include.rb b/spec/mspec/lib/mspec/matchers/include.rb
new file mode 100644
index 0000000000..0b7eaf3ce2
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/include.rb
@@ -0,0 +1,31 @@
+class IncludeMatcher
+ def initialize(*expected)
+ @expected = expected
+ end
+
+ def matches?(actual)
+ @actual = actual
+ @expected.each do |e|
+ @element = e
+ unless @actual.include?(e)
+ return false
+ end
+ end
+ return true
+ end
+
+ def failure_message
+ ["Expected #{@actual.inspect}", "to include #{@element.inspect}"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual.inspect}", "not to include #{@element.inspect}"]
+ end
+end
+
+# Cannot override #include at the toplevel in MRI
+module MSpecMatchers
+ private def include(*expected)
+ IncludeMatcher.new(*expected)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/include_any_of.rb b/spec/mspec/lib/mspec/matchers/include_any_of.rb
new file mode 100644
index 0000000000..ce097ccf0f
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/include_any_of.rb
@@ -0,0 +1,29 @@
+class IncludeAnyOfMatcher
+ def initialize(*expected)
+ @expected = expected
+ end
+
+ def matches?(actual)
+ @actual = actual
+ @expected.each do |e|
+ if @actual.include?(e)
+ return true
+ end
+ end
+ return false
+ end
+
+ def failure_message
+ ["Expected #{@actual.inspect}", "to include any of #{@expected.inspect}"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual.inspect}", "not to include any of #{@expected.inspect}"]
+ end
+end
+
+module MSpecMatchers
+ private def include_any_of(*expected)
+ IncludeAnyOfMatcher.new(*expected)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/infinity.rb b/spec/mspec/lib/mspec/matchers/infinity.rb
new file mode 100644
index 0000000000..8bfa6dbd10
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/infinity.rb
@@ -0,0 +1,28 @@
+class InfinityMatcher
+ def initialize(expected_sign)
+ @expected_sign = expected_sign
+ end
+
+ def matches?(actual)
+ @actual = actual
+ @actual.kind_of?(Float) && @actual.infinite? == @expected_sign
+ end
+
+ def failure_message
+ ["Expected #{@actual}", "to be #{"-" if @expected_sign == -1}Infinity"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual}", "not to be #{"-" if @expected_sign == -1}Infinity"]
+ end
+end
+
+module MSpecMatchers
+ private def be_positive_infinity
+ InfinityMatcher.new(1)
+ end
+
+ private def be_negative_infinity
+ InfinityMatcher.new(-1)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/match_yaml.rb b/spec/mspec/lib/mspec/matchers/match_yaml.rb
new file mode 100644
index 0000000000..920d85a14f
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/match_yaml.rb
@@ -0,0 +1,46 @@
+class MatchYAMLMatcher
+
+ def initialize(expected)
+ if valid_yaml?(expected)
+ @expected = expected
+ else
+ @expected = expected.to_yaml
+ end
+ end
+
+ def matches?(actual)
+ @actual = actual
+ clean_yaml(@actual) == clean_yaml(@expected)
+ end
+
+ def failure_message
+ ["Expected #{@actual.inspect}", " to match #{@expected.inspect}"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual.inspect}", " to match #{@expected.inspect}"]
+ end
+
+ protected
+
+ def clean_yaml(yaml)
+ yaml.gsub(/([^-]|^---)\s+\n/, "\\1\n").sub(/\n\.\.\.\n$/, "\n")
+ end
+
+ def valid_yaml?(obj)
+ require 'yaml'
+ begin
+ YAML.load(obj)
+ rescue
+ false
+ else
+ true
+ end
+ end
+end
+
+module MSpecMatchers
+ private def match_yaml(expected)
+ MatchYAMLMatcher.new(expected)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/method.rb b/spec/mspec/lib/mspec/matchers/method.rb
new file mode 100644
index 0000000000..e8cdfa62ff
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/method.rb
@@ -0,0 +1,10 @@
+class MethodMatcher
+ def initialize(method, include_super=true)
+ @include_super = include_super
+ @method = method.to_sym
+ end
+
+ def matches?(mod)
+ raise Exception, "define #matches? in the subclass"
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/output.rb b/spec/mspec/lib/mspec/matchers/output.rb
new file mode 100644
index 0000000000..b89b6ca0f6
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/output.rb
@@ -0,0 +1,67 @@
+require 'mspec/helpers/io'
+
+class OutputMatcher
+ def initialize(stdout, stderr)
+ @out = stdout
+ @err = stderr
+ end
+
+ def matches?(proc)
+ @saved_out = $stdout
+ @saved_err = $stderr
+ @stdout = $stdout = IOStub.new
+ @stderr = $stderr = IOStub.new
+
+ proc.call
+
+ unless @out.nil?
+ case @out
+ when Regexp
+ return false unless $stdout =~ @out
+ else
+ return false unless $stdout == @out
+ end
+ end
+
+ unless @err.nil?
+ case @err
+ when Regexp
+ return false unless $stderr =~ @err
+ else
+ return false unless $stderr == @err
+ end
+ end
+
+ return true
+ ensure
+ $stdout = @saved_out
+ $stderr = @saved_err
+ end
+
+ def failure_message
+ expected_out = "\n"
+ actual_out = "\n"
+ unless @out.nil?
+ expected_out += " $stdout: #{@out.inspect}\n"
+ actual_out += " $stdout: #{@stdout.inspect}\n"
+ end
+ unless @err.nil?
+ expected_out += " $stderr: #{@err.inspect}\n"
+ actual_out += " $stderr: #{@stderr.inspect}\n"
+ end
+ ["Expected:#{expected_out}", " got:#{actual_out}"]
+ end
+
+ def negative_failure_message
+ out = ""
+ out += " $stdout: #{@stdout.chomp.dump}\n" unless @out.nil?
+ out += " $stderr: #{@stderr.chomp.dump}\n" unless @err.nil?
+ ["Expected output not to be:\n", out]
+ end
+end
+
+module MSpecMatchers
+ private def output(stdout=nil, stderr=nil)
+ OutputMatcher.new(stdout, stderr)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/output_to_fd.rb b/spec/mspec/lib/mspec/matchers/output_to_fd.rb
new file mode 100644
index 0000000000..f4d7b4ea1f
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/output_to_fd.rb
@@ -0,0 +1,71 @@
+require 'mspec/helpers/tmp'
+
+# Lower-level output speccing mechanism for a single
+# output stream. Unlike OutputMatcher which provides
+# methods to capture the output, we actually replace
+# the FD itself so that there is no reliance on a
+# certain method being used.
+class OutputToFDMatcher
+ def initialize(expected, to)
+ @to, @expected = to, expected
+
+ case @to
+ when STDOUT
+ @to_name = "STDOUT"
+ when STDERR
+ @to_name = "STDERR"
+ when IO
+ @to_name = @to.object_id.to_s
+ else
+ raise ArgumentError, "#{@to.inspect} is not a supported output target"
+ end
+ end
+
+ def with_tmp
+ path = tmp("mspec_output_to_#{$$}_#{Time.now.to_i}")
+ File.open(path, 'w+') { |io|
+ yield(io)
+ }
+ ensure
+ File.delete path if path
+ end
+
+ def matches?(block)
+ old_to = @to.dup
+ with_tmp do |out|
+ # Replacing with a file handle so that Readline etc. work
+ @to.reopen out
+ begin
+ block.call
+ ensure
+ @to.reopen old_to
+ old_to.close
+ end
+
+ out.rewind
+ @actual = out.read
+
+ case @expected
+ when Regexp
+ !(@actual =~ @expected).nil?
+ else
+ @actual == @expected
+ end
+ end
+ end
+
+ def failure_message()
+ ["Expected (#{@to_name}): #{@expected.inspect}\n",
+ "#{'but got'.rjust(@to_name.length + 10)}: #{@actual.inspect}\nBacktrace"]
+ end
+
+ def negative_failure_message()
+ ["Expected output (#{@to_name}) to NOT be:\n", @actual.inspect]
+ end
+end
+
+module MSpecMatchers
+ private def output_to_fd(what, where = STDOUT)
+ OutputToFDMatcher.new what, where
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/raise_error.rb b/spec/mspec/lib/mspec/matchers/raise_error.rb
new file mode 100644
index 0000000000..a051ea12f7
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/raise_error.rb
@@ -0,0 +1,85 @@
+class RaiseErrorMatcher
+ def initialize(exception, message, &block)
+ @exception = exception
+ @message = message
+ @block = block
+ @actual = nil
+ end
+
+ def matches?(proc)
+ @result = proc.call
+ return false
+ rescue Exception => actual
+ @actual = actual
+ if matching_exception?(actual)
+ return true
+ else
+ raise actual
+ end
+ end
+
+ def matching_exception?(exc)
+ return false unless @exception === exc
+ if @message then
+ case @message
+ when String
+ return false if @message != exc.message
+ when Regexp
+ return false if @message !~ exc.message
+ end
+ end
+
+ # The block has its own expectations and will throw an exception if it fails
+ @block[exc] if @block
+
+ return true
+ end
+
+ def exception_class_and_message(exception_class, message)
+ if message
+ "#{exception_class} (#{message})"
+ else
+ "#{exception_class}"
+ end
+ end
+
+ def format_expected_exception
+ exception_class_and_message(@exception, @message)
+ end
+
+ def format_exception(exception)
+ exception_class_and_message(exception.class, exception.message)
+ end
+
+ def format_result(result)
+ result.pretty_inspect.chomp
+ rescue => e
+ "#pretty_inspect raised #{e.class}; A #<#{result.class}>"
+ end
+
+ def failure_message
+ message = ["Expected #{format_expected_exception}"]
+
+ if @actual
+ message << "but got #{format_exception(@actual)}"
+ else
+ message << "but no exception was raised (#{format_result(@result)} was returned)"
+ end
+
+ message
+ end
+
+ def negative_failure_message
+ message = ["Expected to not get #{format_expected_exception}", ""]
+ unless @actual.class == @exception
+ message[1] = "but got #{format_exception(@actual)}"
+ end
+ message
+ end
+end
+
+module MSpecMatchers
+ private def raise_error(exception=Exception, message=nil, &block)
+ RaiseErrorMatcher.new(exception, message, &block)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/respond_to.rb b/spec/mspec/lib/mspec/matchers/respond_to.rb
new file mode 100644
index 0000000000..6b35ae2d3c
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/respond_to.rb
@@ -0,0 +1,24 @@
+class RespondToMatcher
+ def initialize(expected)
+ @expected = expected
+ end
+
+ def matches?(actual)
+ @actual = actual
+ @actual.respond_to?(@expected)
+ end
+
+ def failure_message
+ ["Expected #{@actual.inspect} (#{@actual.class})", "to respond to #{@expected}"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual.inspect} (#{@actual.class})", "not to respond to #{@expected}"]
+ end
+end
+
+module MSpecMatchers
+ private def respond_to(expected)
+ RespondToMatcher.new(expected)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/signed_zero.rb b/spec/mspec/lib/mspec/matchers/signed_zero.rb
new file mode 100644
index 0000000000..2ff90f4994
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/signed_zero.rb
@@ -0,0 +1,28 @@
+class SignedZeroMatcher
+ def initialize(expected_sign)
+ @expected_sign = expected_sign
+ end
+
+ def matches?(actual)
+ @actual = actual
+ (1.0/actual).infinite? == @expected_sign
+ end
+
+ def failure_message
+ ["Expected #{@actual}", "to be #{"-" if @expected_sign == -1}0.0"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@actual}", "not to be #{"-" if @expected_sign == -1}0.0"]
+ end
+end
+
+module MSpecMatchers
+ private def be_positive_zero
+ SignedZeroMatcher.new(1)
+ end
+
+ private def be_negative_zero
+ SignedZeroMatcher.new(-1)
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/variable.rb b/spec/mspec/lib/mspec/matchers/variable.rb
new file mode 100644
index 0000000000..4d801ea337
--- /dev/null
+++ b/spec/mspec/lib/mspec/matchers/variable.rb
@@ -0,0 +1,24 @@
+class VariableMatcher
+ class << self
+ attr_accessor :variables_method, :description
+ end
+
+ def initialize(variable)
+ @variable = variable.to_sym
+ end
+
+ def matches?(object)
+ @object = object
+ @object.send(self.class.variables_method).include? @variable
+ end
+
+ def failure_message
+ ["Expected #{@object} to have #{self.class.description} '#{@variable}'",
+ "but it does not"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@object} NOT to have #{self.class.description} '#{@variable}'",
+ "but it does"]
+ end
+end
diff --git a/spec/mspec/lib/mspec/mocks.rb b/spec/mspec/lib/mspec/mocks.rb
new file mode 100644
index 0000000000..6a029c7b53
--- /dev/null
+++ b/spec/mspec/lib/mspec/mocks.rb
@@ -0,0 +1,3 @@
+require 'mspec/mocks/mock'
+require 'mspec/mocks/proxy'
+require 'mspec/mocks/object'
diff --git a/spec/mspec/lib/mspec/mocks/mock.rb b/spec/mspec/lib/mspec/mocks/mock.rb
new file mode 100644
index 0000000000..cb4875bbd6
--- /dev/null
+++ b/spec/mspec/lib/mspec/mocks/mock.rb
@@ -0,0 +1,212 @@
+require 'mspec/expectations/expectations'
+require 'mspec/helpers/warning'
+
+module Mock
+ def self.reset
+ @mocks = @stubs = @objects = nil
+ end
+
+ def self.objects
+ @objects ||= {}
+ end
+
+ def self.mocks
+ @mocks ||= Hash.new { |h,k| h[k] = [] }
+ end
+
+ def self.stubs
+ @stubs ||= Hash.new { |h,k| h[k] = [] }
+ end
+
+ def self.replaced_name(obj, sym)
+ :"__mspec_#{obj.__id__}_#{sym}__"
+ end
+
+ def self.replaced_key(obj, sym)
+ [replaced_name(obj, sym), sym]
+ end
+
+ def self.has_key?(keys, sym)
+ !!keys.find { |k| k.first == sym }
+ end
+
+ def self.replaced?(sym)
+ has_key?(mocks.keys, sym) or has_key?(stubs.keys, sym)
+ end
+
+ def self.clear_replaced(key)
+ mocks.delete key
+ stubs.delete key
+ end
+
+ def self.mock_respond_to?(obj, sym, include_private = false)
+ name = replaced_name(obj, :respond_to?)
+ if replaced? name
+ obj.__send__ name, sym, include_private
+ else
+ obj.respond_to? sym, include_private
+ end
+ end
+
+ def self.install_method(obj, sym, type=nil)
+ meta = obj.singleton_class
+
+ key = replaced_key obj, sym
+ sym = sym.to_sym
+
+ if type == :stub and mocks.key?(key)
+ # Defining a stub and there is already a mock, ignore the stub
+ return
+ end
+
+ if (sym == :respond_to? or mock_respond_to?(obj, sym, true)) and !replaced?(key.first)
+ meta.__send__ :alias_method, key.first, sym
+ end
+
+ suppress_warning {
+ meta.class_eval {
+ define_method(sym) do |*args, &block|
+ Mock.verify_call self, sym, *args, &block
+ end
+ }
+ }
+
+ proxy = MockProxy.new type
+
+ if proxy.mock?
+ MSpec.expectation
+ MSpec.actions :expectation, MSpec.current.state
+ end
+
+ if proxy.mock? and stubs.key?(key)
+ # Defining a mock and there is already a stub, remove the stub
+ stubs.delete key
+ end
+
+ if proxy.stub?
+ stubs[key].unshift proxy
+ else
+ mocks[key] << proxy
+ end
+ objects[key] = obj
+
+ proxy
+ end
+
+ def self.name_or_inspect(obj)
+ obj.instance_variable_get(:@name) || obj.inspect
+ end
+
+ def self.inspect_args(args)
+ "(#{Array(args).map(&:inspect).join(', ')})"
+ end
+
+ def self.verify_count
+ mocks.each do |key, proxies|
+ obj = objects[key]
+ proxies.each do |proxy|
+ qualifier, count = proxy.count
+ pass = case qualifier
+ when :at_least
+ proxy.calls >= count
+ when :at_most
+ proxy.calls <= count
+ when :exactly
+ proxy.calls == count
+ when :any_number_of_times
+ true
+ else
+ false
+ end
+ unless pass
+ SpecExpectation.fail_with(
+ "Mock '#{name_or_inspect obj}' expected to receive #{key.last}#{inspect_args proxy.arguments} " + \
+ "#{qualifier.to_s.sub('_', ' ')} #{count} times",
+ "but received it #{proxy.calls} times")
+ end
+ end
+ end
+ end
+
+ def self.verify_call(obj, sym, *args, &block)
+ compare = *args
+ compare = compare.first if compare.length <= 1
+
+ key = replaced_key obj, sym
+ [mocks, stubs].each do |proxies|
+ proxies.fetch(key, []).each do |proxy|
+ pass = case proxy.arguments
+ when :any_args
+ true
+ when :no_args
+ compare.nil?
+ else
+ proxy.arguments == compare
+ end
+
+ if proxy.yielding?
+ if block
+ proxy.yielding.each do |args_to_yield|
+ if block.arity == -1 || block.arity == args_to_yield.size
+ block.call(*args_to_yield)
+ else
+ SpecExpectation.fail_with(
+ "Mock '#{name_or_inspect obj}' asked to yield " + \
+ "|#{proxy.yielding.join(', ')}| on #{sym}\n",
+ "but a block with arity #{block.arity} was passed")
+ end
+ end
+ else
+ SpecExpectation.fail_with(
+ "Mock '#{name_or_inspect obj}' asked to yield " + \
+ "|[#{proxy.yielding.join('], [')}]| on #{sym}\n",
+ "but no block was passed")
+ end
+ end
+
+ if pass
+ proxy.called
+
+ if proxy.raising?
+ raise proxy.raising
+ else
+ return proxy.returning
+ end
+ end
+ end
+ end
+
+ if sym.to_sym == :respond_to?
+ mock_respond_to? obj, *args
+ else
+ SpecExpectation.fail_with("Mock '#{name_or_inspect obj}': method #{sym}\n",
+ "called with unexpected arguments #{inspect_args compare}")
+ end
+ end
+
+ def self.cleanup
+ objects.each do |key, obj|
+ if obj.kind_of? MockIntObject
+ clear_replaced key
+ next
+ end
+
+ replaced = key.first
+ sym = key.last
+ meta = obj.singleton_class
+
+ if mock_respond_to? obj, replaced, true
+ suppress_warning do
+ meta.__send__ :alias_method, sym, replaced
+ end
+ meta.__send__ :remove_method, replaced
+ else
+ meta.__send__ :remove_method, sym
+ end
+
+ clear_replaced key
+ end
+ ensure
+ reset
+ end
+end
diff --git a/spec/mspec/lib/mspec/mocks/object.rb b/spec/mspec/lib/mspec/mocks/object.rb
new file mode 100644
index 0000000000..19a50ac4e1
--- /dev/null
+++ b/spec/mspec/lib/mspec/mocks/object.rb
@@ -0,0 +1,28 @@
+require 'mspec/mocks/proxy'
+
+class Object
+ def should_receive(sym)
+ Mock.install_method self, sym
+ end
+
+ def should_not_receive(sym)
+ proxy = Mock.install_method self, sym
+ proxy.exactly(0).times
+ end
+
+ def stub!(sym)
+ Mock.install_method self, sym, :stub
+ end
+end
+
+def mock(name, options={})
+ MockObject.new name, options
+end
+
+def mock_int(val)
+ MockIntObject.new(val)
+end
+
+def mock_numeric(name, options={})
+ NumericMockObject.new name, options
+end
diff --git a/spec/mspec/lib/mspec/mocks/proxy.rb b/spec/mspec/lib/mspec/mocks/proxy.rb
new file mode 100644
index 0000000000..f5acc89d62
--- /dev/null
+++ b/spec/mspec/lib/mspec/mocks/proxy.rb
@@ -0,0 +1,186 @@
+class MockObject
+ def initialize(name, options={})
+ @name = name
+ @null = options[:null_object]
+ end
+
+ def method_missing(sym, *args, &block)
+ @null ? self : super
+ end
+ private :method_missing
+end
+
+class NumericMockObject < Numeric
+ def initialize(name, options={})
+ @name = name
+ @null = options[:null_object]
+ end
+
+ def method_missing(sym, *args, &block)
+ @null ? self : super
+ end
+
+ def singleton_method_added(val)
+ end
+end
+
+class MockIntObject
+ def initialize(val)
+ @value = val
+ @calls = 0
+
+ key = [self, :to_int]
+
+ Mock.objects[key] = self
+ Mock.mocks[key] << self
+ end
+
+ attr_reader :calls
+
+ def to_int
+ @calls += 1
+ @value.to_int
+ end
+
+ def count
+ [:at_least, 1]
+ end
+end
+
+class MockProxy
+ attr_reader :raising, :yielding
+
+ def initialize(type=nil)
+ @multiple_returns = nil
+ @returning = nil
+ @raising = nil
+ @yielding = []
+ @arguments = :any_args
+ @type = type || :mock
+ end
+
+ def mock?
+ @type == :mock
+ end
+
+ def stub?
+ @type == :stub
+ end
+
+ def count
+ @count ||= mock? ? [:exactly, 1] : [:any_number_of_times, 0]
+ end
+
+ def arguments
+ @arguments
+ end
+
+ def returning
+ if @multiple_returns
+ if @returning.size == 1
+ @multiple_returns = false
+ return @returning = @returning.shift
+ end
+ return @returning.shift
+ end
+ @returning
+ end
+
+ def times
+ self
+ end
+
+ def calls
+ @calls ||= 0
+ end
+
+ def called
+ @calls = calls + 1
+ end
+
+ def exactly(n)
+ @count = [:exactly, n_times(n)]
+ self
+ end
+
+ def at_least(n)
+ @count = [:at_least, n_times(n)]
+ self
+ end
+
+ def at_most(n)
+ @count = [:at_most, n_times(n)]
+ self
+ end
+
+ def once
+ exactly 1
+ end
+
+ def twice
+ exactly 2
+ end
+
+ def any_number_of_times
+ @count = [:any_number_of_times, 0]
+ self
+ end
+
+ def with(*args)
+ raise ArgumentError, "you must specify the expected arguments" if args.empty?
+ if args.length == 1
+ @arguments = args.first
+ else
+ @arguments = args
+ end
+ self
+ end
+
+ def and_return(*args)
+ case args.size
+ when 0
+ @returning = nil
+ when 1
+ @returning = args[0]
+ else
+ @multiple_returns = true
+ @returning = args
+ count[1] = args.size if count[1] < args.size
+ end
+ self
+ end
+
+ def and_raise(exception)
+ if exception.kind_of? String
+ @raising = RuntimeError.new exception
+ else
+ @raising = exception
+ end
+ end
+
+ def raising?
+ @raising != nil
+ end
+
+ def and_yield(*args)
+ @yielding << args
+ self
+ end
+
+ def yielding?
+ !@yielding.empty?
+ end
+
+ private
+
+ def n_times(n)
+ case n
+ when :once
+ 1
+ when :twice
+ 2
+ else
+ Integer n
+ end
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner.rb b/spec/mspec/lib/mspec/runner.rb
new file mode 100644
index 0000000000..df57b9f69b
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner.rb
@@ -0,0 +1,12 @@
+require 'mspec/mocks'
+require 'mspec/runner/mspec'
+require 'mspec/runner/context'
+require 'mspec/runner/evaluate'
+require 'mspec/runner/example'
+require 'mspec/runner/exception'
+require 'mspec/runner/object'
+require 'mspec/runner/formatters'
+require 'mspec/runner/actions'
+require 'mspec/runner/filters'
+require 'mspec/runner/shared'
+require 'mspec/runner/tag'
diff --git a/spec/mspec/lib/mspec/runner/actions.rb b/spec/mspec/lib/mspec/runner/actions.rb
new file mode 100644
index 0000000000..0a5a05fbd1
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/actions.rb
@@ -0,0 +1,6 @@
+require 'mspec/runner/actions/tally'
+require 'mspec/runner/actions/timer'
+require 'mspec/runner/actions/filter'
+require 'mspec/runner/actions/tag'
+require 'mspec/runner/actions/taglist'
+require 'mspec/runner/actions/tagpurge'
diff --git a/spec/mspec/lib/mspec/runner/actions/filter.rb b/spec/mspec/lib/mspec/runner/actions/filter.rb
new file mode 100644
index 0000000000..35899c8dc8
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/actions/filter.rb
@@ -0,0 +1,40 @@
+require 'mspec/runner/filters/match'
+
+# ActionFilter is a base class for actions that are triggered by
+# specs that match the filter. The filter may be specified by
+# strings that match spec descriptions or by tags for strings
+# that match spec descriptions.
+#
+# Unlike TagFilter and RegexpFilter, ActionFilter instances do
+# not affect the specs that are run. The filter is only used to
+# trigger the action.
+
+class ActionFilter
+ def initialize(tags=nil, descs=nil)
+ @tags = Array(tags)
+ descs = Array(descs)
+ @sfilter = descs.empty? ? nil : MatchFilter.new(nil, *descs)
+ @tfilter = nil
+ end
+
+ def ===(string)
+ @sfilter === string or @tfilter === string
+ end
+
+ def load
+ return if @tags.empty?
+
+ desc = MSpec.read_tags(@tags).map { |t| t.description }
+ return if desc.empty?
+
+ @tfilter = MatchFilter.new(nil, *desc)
+ end
+
+ def register
+ MSpec.register :load, self
+ end
+
+ def unregister
+ MSpec.unregister :load, self
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/actions/leakchecker.rb b/spec/mspec/lib/mspec/runner/actions/leakchecker.rb
new file mode 100644
index 0000000000..ec17a156bf
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/actions/leakchecker.rb
@@ -0,0 +1,301 @@
+# Adapted from ruby's test/lib/leakchecker.rb.
+# Ruby's 2-clause BSDL follows.
+
+# Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+class LeakChecker
+ def initialize
+ @fd_info = find_fds
+ @tempfile_info = find_tempfiles
+ @thread_info = find_threads
+ @env_info = find_env
+ @argv_info = find_argv
+ @encoding_info = find_encodings
+ end
+
+ def check(test_name)
+ @no_leaks = true
+ leaks = [
+ check_fd_leak(test_name),
+ check_tempfile_leak(test_name),
+ check_thread_leak(test_name),
+ check_process_leak(test_name),
+ check_env(test_name),
+ check_argv(test_name),
+ check_encodings(test_name)
+ ]
+ GC.start if leaks.any?
+ return leaks.none?
+ end
+
+ private
+ def find_fds
+ fd_dir = "/proc/self/fd"
+ if File.directory?(fd_dir)
+ fds = Dir.open(fd_dir) {|d|
+ a = d.grep(/\A\d+\z/, &:to_i)
+ if d.respond_to? :fileno
+ a -= [d.fileno]
+ end
+ a
+ }
+ fds.sort
+ else
+ []
+ end
+ end
+
+ def check_fd_leak(test_name)
+ leaked = false
+ live1 = @fd_info
+ if IO.respond_to?(:console) and (m = IO.method(:console)).arity.nonzero?
+ m[:close]
+ end
+ live2 = find_fds
+ fd_closed = live1 - live2
+ if !fd_closed.empty?
+ fd_closed.each {|fd|
+ puts "Closed file descriptor: #{test_name}: #{fd}"
+ }
+ end
+ fd_leaked = live2 - live1
+ if !fd_leaked.empty?
+ leaked = true
+ h = {}
+ ObjectSpace.each_object(IO) {|io|
+ inspect = io.inspect
+ begin
+ autoclose = io.autoclose?
+ fd = io.fileno
+ rescue IOError # closed IO object
+ next
+ end
+ (h[fd] ||= []) << [io, autoclose, inspect]
+ }
+ fd_leaked.each {|fd|
+ str = ''
+ if h[fd]
+ str << ' :'
+ h[fd].map {|io, autoclose, inspect|
+ s = ' ' + inspect
+ s << "(not-autoclose)" if !autoclose
+ s
+ }.sort.each {|s|
+ str << s
+ }
+ end
+ puts "Leaked file descriptor: #{test_name}: #{fd}#{str}"
+ }
+ #system("lsof -p #$$") if !fd_leaked.empty?
+ h.each {|fd, list|
+ next if list.length <= 1
+ if 1 < list.count {|io, autoclose, inspect| autoclose }
+ str = list.map {|io, autoclose, inspect| " #{inspect}" + (autoclose ? "(autoclose)" : "") }.sort.join
+ puts "Multiple autoclose IO object for a file descriptor:#{str}"
+ end
+ }
+ end
+ @fd_info = live2
+ return leaked
+ end
+
+ def extend_tempfile_counter
+ return if defined? LeakChecker::TempfileCounter
+ m = Module.new {
+ @count = 0
+ class << self
+ attr_accessor :count
+ end
+
+ def new(data)
+ LeakChecker::TempfileCounter.count += 1
+ super(data)
+ end
+ }
+ LeakChecker.const_set(:TempfileCounter, m)
+
+ class << Tempfile::Remover
+ prepend LeakChecker::TempfileCounter
+ end
+ end
+
+ def find_tempfiles(prev_count=-1)
+ return [prev_count, []] unless defined? Tempfile
+ extend_tempfile_counter
+ count = TempfileCounter.count
+ if prev_count == count
+ [prev_count, []]
+ else
+ tempfiles = ObjectSpace.each_object(Tempfile).find_all {|t| t.path }
+ [count, tempfiles]
+ end
+ end
+
+ def check_tempfile_leak(test_name)
+ return false unless defined? Tempfile
+ count1, initial_tempfiles = @tempfile_info
+ count2, current_tempfiles = find_tempfiles(count1)
+ leaked = false
+ tempfiles_leaked = current_tempfiles - initial_tempfiles
+ if !tempfiles_leaked.empty?
+ leaked = true
+ list = tempfiles_leaked.map {|t| t.inspect }.sort
+ list.each {|str|
+ puts "Leaked tempfile: #{test_name}: #{str}"
+ }
+ tempfiles_leaked.each {|t| t.close! }
+ end
+ @tempfile_info = [count2, initial_tempfiles]
+ return leaked
+ end
+
+ def find_threads
+ Thread.list.find_all {|t|
+ t != Thread.current && t.alive?
+ }
+ end
+
+ def check_thread_leak(test_name)
+ live1 = @thread_info
+ live2 = find_threads
+ thread_finished = live1 - live2
+ leaked = false
+ if !thread_finished.empty?
+ list = thread_finished.map {|t| t.inspect }.sort
+ list.each {|str|
+ puts "Finished thread: #{test_name}: #{str}"
+ }
+ end
+ thread_leaked = live2 - live1
+ if !thread_leaked.empty?
+ leaked = true
+ list = thread_leaked.map {|t| t.inspect }.sort
+ list.each {|str|
+ puts "Leaked thread: #{test_name}: #{str}"
+ }
+ end
+ @thread_info = live2
+ return leaked
+ end
+
+ def check_process_leak(test_name)
+ subprocesses_leaked = Process.waitall
+ subprocesses_leaked.each { |pid, status|
+ puts "Leaked subprocess: #{pid}: #{status}"
+ }
+ return !subprocesses_leaked.empty?
+ end
+
+ def find_env
+ ENV.to_h
+ end
+
+ def check_env(test_name)
+ old_env = @env_info
+ new_env = find_env
+ return false if old_env == new_env
+ (old_env.keys | new_env.keys).sort.each {|k|
+ if old_env.has_key?(k)
+ if new_env.has_key?(k)
+ if old_env[k] != new_env[k]
+ puts "Environment variable changed: #{test_name} : #{k.inspect} changed : #{old_env[k].inspect} -> #{new_env[k].inspect}"
+ end
+ else
+ puts "Environment variable changed: #{test_name} : #{k.inspect} deleted"
+ end
+ else
+ if new_env.has_key?(k)
+ puts "Environment variable changed: #{test_name} : #{k.inspect} added"
+ else
+ flunk "unreachable"
+ end
+ end
+ }
+ @env_info = new_env
+ return true
+ end
+
+ def find_argv
+ ARGV.map { |e| e.dup }
+ end
+
+ def check_argv(test_name)
+ old_argv = @argv_info
+ new_argv = find_argv
+ leaked = false
+ if new_argv != old_argv
+ puts "ARGV changed: #{test_name} : #{old_argv.inspect} to #{new_argv.inspect}"
+ @argv_info = new_argv
+ leaked = true
+ end
+ return leaked
+ end
+
+ def find_encodings
+ [Encoding.default_internal, Encoding.default_external]
+ end
+
+ def check_encodings(test_name)
+ old_internal, old_external = @encoding_info
+ new_internal, new_external = find_encodings
+ leaked = false
+ if new_internal != old_internal
+ leaked = true
+ puts "Encoding.default_internal changed: #{test_name} : #{old_internal.inspect} to #{new_internal.inspect}"
+ end
+ if new_external != old_external
+ leaked = true
+ puts "Encoding.default_external changed: #{test_name} : #{old_external.inspect} to #{new_external.inspect}"
+ end
+ @encoding_info = [new_internal, new_external]
+ return leaked
+ end
+
+ def puts(*args)
+ if @no_leaks
+ @no_leaks = false
+ print "\n"
+ end
+ super(*args)
+ end
+end
+
+class LeakCheckerAction
+ def register
+ MSpec.register :start, self
+ MSpec.register :after, self
+ end
+
+ def start
+ @checker = LeakChecker.new
+ end
+
+ def after(state)
+ unless @checker.check(state.description)
+ if state.example
+ puts state.example.source_location.join(':')
+ end
+ end
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/actions/tag.rb b/spec/mspec/lib/mspec/runner/actions/tag.rb
new file mode 100644
index 0000000000..760152b2a3
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/actions/tag.rb
@@ -0,0 +1,133 @@
+require 'mspec/runner/actions/filter'
+
+# TagAction - Write tagged spec description string to a
+# tag file associated with each spec file.
+#
+# The action is triggered by specs whose descriptions
+# match the filter created with 'tags' and/or 'desc'
+#
+# The action fires in the :after event, after the spec
+# had been run. The action fires if the outcome of
+# running the spec matches 'outcome'.
+#
+# The arguments are:
+#
+# action: :add, :del
+# outcome: :pass, :fail, :all
+# tag: the tag to create/delete
+# comment: the comment to create
+# tags: zero or more tags to get matching
+# spec description strings from
+# desc: zero or more strings to match the
+# spec description strings
+
+class TagAction < ActionFilter
+ def initialize(action, outcome, tag, comment, tags=nil, descs=nil)
+ super tags, descs
+ @action = action
+ @outcome = outcome
+ @tag = tag
+ @comment = comment
+ @report = []
+ @exception = false
+ end
+
+ # Returns true if there are no _tag_ or _description_ filters. This
+ # means that a TagAction matches any example by default. Otherwise,
+ # returns true if either the _tag_ or the _description_ filter
+ # matches +string+.
+ def ===(string)
+ return true unless @sfilter or @tfilter
+ @sfilter === string or @tfilter === string
+ end
+
+ # Callback for the MSpec :before event. Resets the +#exception?+
+ # flag to false.
+ def before(state)
+ @exception = false
+ end
+
+ # Callback for the MSpec :exception event. Sets the +#exception?+
+ # flag to true.
+ def exception(exception)
+ @exception = true
+ end
+
+ # Callback for the MSpec :after event. Performs the tag action
+ # depending on the type of action and the outcome of evaluating
+ # the example. See +TagAction+ for a description of the actions.
+ def after(state)
+ if self === state.description and outcome?
+ tag = SpecTag.new
+ tag.tag = @tag
+ tag.comment = @comment
+ tag.description = state.description
+
+ case @action
+ when :add
+ changed = MSpec.write_tag tag
+ when :del
+ changed = MSpec.delete_tag tag
+ end
+
+ @report << state.description if changed
+ end
+ end
+
+ # Returns true if the result of evaluating the example matches
+ # the _outcome_ registered for this tag action. See +TagAction+
+ # for a description of the _outcome_ types.
+ def outcome?
+ @outcome == :all or
+ (@outcome == :pass and not exception?) or
+ (@outcome == :fail and exception?)
+ end
+
+ # Returns true if an exception was raised while evaluating the
+ # current example.
+ def exception?
+ @exception
+ end
+
+ def report
+ @report.join("\n") + "\n"
+ end
+ private :report
+
+ # Callback for the MSpec :finish event. Prints the actions
+ # performed while evaluating the examples.
+ def finish
+ case @action
+ when :add
+ if @report.empty?
+ print "\nTagAction: no specs were tagged with '#{@tag}'\n"
+ else
+ print "\nTagAction: specs tagged with '#{@tag}':\n\n"
+ print report
+ end
+ when :del
+ if @report.empty?
+ print "\nTagAction: no tags '#{@tag}' were deleted\n"
+ else
+ print "\nTagAction: tag '#{@tag}' deleted for specs:\n\n"
+ print report
+ end
+ end
+ end
+
+ def register
+ super
+ MSpec.register :before, self
+ MSpec.register :exception, self
+ MSpec.register :after, self
+ MSpec.register :finish, self
+ end
+
+ def unregister
+ super
+ MSpec.unregister :before, self
+ MSpec.unregister :exception, self
+ MSpec.unregister :after, self
+ MSpec.unregister :finish, self
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/actions/taglist.rb b/spec/mspec/lib/mspec/runner/actions/taglist.rb
new file mode 100644
index 0000000000..c1aba53794
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/actions/taglist.rb
@@ -0,0 +1,56 @@
+require 'mspec/runner/actions/filter'
+
+# TagListAction - prints out the descriptions for any specs
+# tagged with +tags+. If +tags+ is an empty list, prints out
+# descriptions for any specs that are tagged.
+class TagListAction
+ def initialize(tags=nil)
+ @tags = tags.nil? || tags.empty? ? nil : Array(tags)
+ @filter = nil
+ end
+
+ # Returns true. This enables us to match any tag when loading
+ # tags from the file.
+ def include?(arg)
+ true
+ end
+
+ # Returns true if any tagged descriptions matches +string+.
+ def ===(string)
+ @filter === string
+ end
+
+ # Prints a banner about matching tagged specs.
+ def start
+ if @tags
+ print "\nListing specs tagged with #{@tags.map { |t| "'#{t}'" }.join(", ") }\n\n"
+ else
+ print "\nListing all tagged specs\n\n"
+ end
+ end
+
+ # Creates a MatchFilter for specific tags or for all tags.
+ def load
+ @filter = nil
+ desc = MSpec.read_tags(@tags || self).map { |t| t.description }
+ @filter = MatchFilter.new(nil, *desc) unless desc.empty?
+ end
+
+ # Prints the spec description if it matches the filter.
+ def after(state)
+ return unless self === state.description
+ print state.description, "\n"
+ end
+
+ def register
+ MSpec.register :start, self
+ MSpec.register :load, self
+ MSpec.register :after, self
+ end
+
+ def unregister
+ MSpec.unregister :start, self
+ MSpec.unregister :load, self
+ MSpec.unregister :after, self
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/actions/tagpurge.rb b/spec/mspec/lib/mspec/runner/actions/tagpurge.rb
new file mode 100644
index 0000000000..f4587de6bc
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/actions/tagpurge.rb
@@ -0,0 +1,56 @@
+require 'mspec/runner/actions/filter'
+require 'mspec/runner/actions/taglist'
+
+# TagPurgeAction - removes all tags not matching any spec
+# descriptions.
+class TagPurgeAction < TagListAction
+ attr_reader :matching
+
+ def initialize
+ @matching = []
+ @filter = nil
+ @tags = nil
+ end
+
+ # Prints a banner about purging tags.
+ def start
+ print "\nRemoving tags not matching any specs\n\n"
+ end
+
+ # Creates a MatchFilter for all tags.
+ def load
+ @filter = nil
+ @tags = MSpec.read_tags self
+ desc = @tags.map { |t| t.description }
+ @filter = MatchFilter.new(nil, *desc) unless desc.empty?
+ end
+
+ # Saves any matching tags
+ def after(state)
+ @matching << state.description if self === state.description
+ end
+
+ # Rewrites any matching tags. Prints non-matching tags.
+ # Deletes the tag file if there were no tags (this cleans
+ # up empty or malformed tag files).
+ def unload
+ if @filter
+ matched = @tags.select { |t| @matching.any? { |s| s == t.description } }
+ MSpec.write_tags matched
+
+ (@tags - matched).each { |t| print t.description, "\n" }
+ else
+ MSpec.delete_tags
+ end
+ end
+
+ def register
+ super
+ MSpec.register :unload, self
+ end
+
+ def unregister
+ super
+ MSpec.unregister :unload, self
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/actions/tally.rb b/spec/mspec/lib/mspec/runner/actions/tally.rb
new file mode 100644
index 0000000000..33f937293c
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/actions/tally.rb
@@ -0,0 +1,133 @@
+class Tally
+ attr_accessor :files, :examples, :expectations, :failures, :errors, :guards, :tagged
+
+ def initialize
+ @files = @examples = @expectations = @failures = @errors = @guards = @tagged = 0
+ end
+
+ def files!(add=1)
+ @files += add
+ end
+
+ def examples!(add=1)
+ @examples += add
+ end
+
+ def expectations!(add=1)
+ @expectations += add
+ end
+
+ def failures!(add=1)
+ @failures += add
+ end
+
+ def errors!(add=1)
+ @errors += add
+ end
+
+ def guards!(add=1)
+ @guards += add
+ end
+
+ def tagged!(add=1)
+ @tagged += add
+ end
+
+ def file
+ pluralize files, "file"
+ end
+
+ def example
+ pluralize examples, "example"
+ end
+
+ def expectation
+ pluralize expectations, "expectation"
+ end
+
+ def failure
+ pluralize failures, "failure"
+ end
+
+ def error
+ pluralize errors, "error"
+ end
+
+ def guard
+ pluralize guards, "guard"
+ end
+
+ def tag
+ "#{tagged} tagged"
+ end
+
+ def format
+ results = [ file, example, expectation, failure, error, tag ]
+ if [:report, :report_on, :verify].any? { |m| MSpec.mode? m }
+ results << guard
+ end
+ results.join(", ")
+ end
+
+ alias_method :to_s, :format
+
+ def pluralize(count, singular)
+ "#{count} #{singular}#{'s' unless count == 1}"
+ end
+ private :pluralize
+end
+
+class TallyAction
+ attr_reader :counter
+
+ def initialize
+ @counter = Tally.new
+ end
+
+ def register
+ MSpec.register :load, self
+ MSpec.register :exception, self
+ MSpec.register :example, self
+ MSpec.register :tagged, self
+ MSpec.register :expectation, self
+ end
+
+ def unregister
+ MSpec.unregister :load, self
+ MSpec.unregister :exception, self
+ MSpec.unregister :example, self
+ MSpec.unregister :tagged, self
+ MSpec.unregister :expectation, self
+ end
+
+ def load
+ @counter.files!
+ end
+
+ # Callback for the MSpec :expectation event. Increments the
+ # tally of expectations (e.g. #should, #should_receive, etc.).
+ def expectation(state)
+ @counter.expectations!
+ end
+
+ # Callback for the MSpec :exception event. Increments the
+ # tally of errors and failures.
+ def exception(exception)
+ exception.failure? ? @counter.failures! : @counter.errors!
+ end
+
+ # Callback for the MSpec :example event. Increments the tally
+ # of examples.
+ def example(state, block)
+ @counter.examples!
+ end
+
+ def tagged(state)
+ @counter.examples!
+ @counter.tagged!
+ end
+
+ def format
+ @counter.format
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/actions/timer.rb b/spec/mspec/lib/mspec/runner/actions/timer.rb
new file mode 100644
index 0000000000..e7ebfebe0d
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/actions/timer.rb
@@ -0,0 +1,22 @@
+class TimerAction
+ def register
+ MSpec.register :start, self
+ MSpec.register :finish, self
+ end
+
+ def start
+ @start = Time.now
+ end
+
+ def finish
+ @stop = Time.now
+ end
+
+ def elapsed
+ @stop - @start
+ end
+
+ def format
+ "Finished in %f seconds" % elapsed
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/context.rb b/spec/mspec/lib/mspec/runner/context.rb
new file mode 100644
index 0000000000..30d8a4ad1b
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/context.rb
@@ -0,0 +1,239 @@
+# Holds the state of the +describe+ block that is being
+# evaluated. Every example (i.e. +it+ block) is evaluated
+# in a context, which may include state set up in <tt>before
+# :each</tt> or <tt>before :all</tt> blocks.
+#
+#--
+# A note on naming: this is named _ContextState_ rather
+# than _DescribeState_ because +describe+ is the keyword
+# in the DSL for referring to the context in which an example
+# is evaluated, just as +it+ refers to the example itself.
+#++
+class ContextState
+ attr_reader :state, :parent, :parents, :children, :examples, :to_s
+
+ def initialize(mod, options=nil)
+ @to_s = mod.to_s
+ if options.is_a? Hash
+ @options = options
+ else
+ @to_s += "#{".:#".include?(options[0,1]) ? "" : " "}#{options}" if options
+ @options = { }
+ end
+ @options[:shared] ||= false
+
+ @parsed = false
+ @before = { :all => [], :each => [] }
+ @after = { :all => [], :each => [] }
+ @pre = {}
+ @post = {}
+ @examples = []
+ @parent = nil
+ @parents = [self]
+ @children = []
+
+ @mock_verify = Proc.new { Mock.verify_count }
+ @mock_cleanup = Proc.new { Mock.cleanup }
+ @expectation_missing = Proc.new { raise SpecExpectationNotFoundError }
+ end
+
+ # Remove caching when a ContextState is dup'd for shared specs.
+ def initialize_copy(other)
+ @pre = {}
+ @post = {}
+ end
+
+ # Returns true if this is a shared +ContextState+. Essentially, when
+ # created with: describe "Something", :shared => true { ... }
+ def shared?
+ return @options[:shared]
+ end
+
+ # Set the parent (enclosing) +ContextState+ for this state. Creates
+ # the +parents+ list.
+ def parent=(parent)
+ @description = nil
+
+ if shared?
+ @parent = nil
+ else
+ @parent = parent
+ parent.child self if parent
+
+ @parents = [self]
+ state = parent
+ while state
+ @parents.unshift state
+ state = state.parent
+ end
+ end
+ end
+
+ # Add the ContextState instance +child+ to the list of nested
+ # describe blocks.
+ def child(child)
+ @children << child
+ end
+
+ # Adds a nested ContextState in a shared ContextState to a containing
+ # ContextState.
+ #
+ # Normal adoption is from the parent's perspective. But adopt is a good
+ # verb and it's reasonable for the child to adopt the parent as well. In
+ # this case, manipulating state from inside the child avoids needlessly
+ # exposing the state to manipulate it externally in the dup. (See
+ # #it_should_behave_like)
+ def adopt(parent)
+ self.parent = parent
+
+ @examples = @examples.map do |example|
+ example = example.dup
+ example.context = self
+ example
+ end
+
+ children = @children
+ @children = []
+
+ children.each { |child| child.dup.adopt self }
+ end
+
+ # Returns a list of all before(+what+) blocks from self and any parents.
+ def pre(what)
+ @pre[what] ||= parents.inject([]) { |l, s| l.push(*s.before(what)) }
+ end
+
+ # Returns a list of all after(+what+) blocks from self and any parents.
+ # The list is in reverse order. In other words, the blocks defined in
+ # inner describes are in the list before those defined in outer describes,
+ # and in a particular describe block those defined later are in the list
+ # before those defined earlier.
+ def post(what)
+ @post[what] ||= parents.inject([]) { |l, s| l.unshift(*s.after(what)) }
+ end
+
+ # Records before(:each) and before(:all) blocks.
+ def before(what, &block)
+ return if MSpec.guarded?
+ block ? @before[what].push(block) : @before[what]
+ end
+
+ # Records after(:each) and after(:all) blocks.
+ def after(what, &block)
+ return if MSpec.guarded?
+ block ? @after[what].unshift(block) : @after[what]
+ end
+
+ # Creates an ExampleState instance for the block and stores it
+ # in a list of examples to evaluate unless the example is filtered.
+ def it(desc, &block)
+ example = ExampleState.new(self, desc, block)
+ MSpec.actions :add, example
+ return if MSpec.guarded?
+ @examples << example
+ end
+
+ # Evaluates the block and resets the toplevel +ContextState+ to #parent.
+ def describe(&block)
+ @parsed = protect @to_s, block, false
+ MSpec.register_current parent
+ MSpec.register_shared self if shared?
+ end
+
+ # Returns a description string generated from self and all parents
+ def description
+ @description ||= parents.map { |p| p.to_s }.compact.join(" ")
+ end
+
+ # Injects the before/after blocks and examples from the shared
+ # describe block into this +ContextState+ instance.
+ def it_should_behave_like(desc)
+ return if MSpec.guarded?
+
+ unless state = MSpec.retrieve_shared(desc)
+ raise Exception, "Unable to find shared 'describe' for #{desc}"
+ end
+
+ state.before(:all).each { |b| before :all, &b }
+ state.before(:each).each { |b| before :each, &b }
+ state.after(:each).each { |b| after :each, &b }
+ state.after(:all).each { |b| after :all, &b }
+
+ state.examples.each do |example|
+ example = example.dup
+ example.context = self
+ @examples << example
+ end
+
+ state.children.each do |child|
+ child.dup.adopt self
+ end
+ end
+
+ # Evaluates each block in +blocks+ using the +MSpec.protect+ method
+ # so that exceptions are handled and tallied. Returns true and does
+ # NOT evaluate any blocks if +check+ is true and
+ # <tt>MSpec.mode?(:pretend)</tt> is true.
+ def protect(what, blocks, check=true)
+ return true if check and MSpec.mode? :pretend
+ Array(blocks).all? { |block| MSpec.protect what, &block }
+ end
+
+ # Removes filtered examples. Returns true if there are examples
+ # left to evaluate.
+ def filter_examples
+ filtered, @examples = @examples.partition do |ex|
+ ex.filtered?
+ end
+
+ filtered.each do |ex|
+ MSpec.actions :tagged, ex
+ end
+
+ !@examples.empty?
+ end
+
+ # Evaluates the examples in a +ContextState+. Invokes the MSpec events
+ # for :enter, :before, :after, :leave.
+ def process
+ MSpec.register_current self
+
+ if @parsed and filter_examples
+ MSpec.shuffle @examples if MSpec.randomize?
+ MSpec.actions :enter, description
+
+ if protect "before :all", pre(:all)
+ @examples.each do |state|
+ MSpec.repeat do
+ @state = state
+ example = state.example
+ MSpec.actions :before, state
+
+ if protect "before :each", pre(:each)
+ MSpec.clear_expectations
+ if example
+ passed = protect nil, example
+ MSpec.actions :example, state, example
+ protect nil, @expectation_missing unless MSpec.expectation? or !passed
+ end
+ end
+ protect "after :each", post(:each)
+ protect "Mock.verify_count", @mock_verify
+
+ protect "Mock.cleanup", @mock_cleanup
+ MSpec.actions :after, state
+ @state = nil
+ end
+ end
+ protect "after :all", post(:all)
+ else
+ protect "Mock.cleanup", @mock_cleanup
+ end
+
+ MSpec.actions :leave
+ end
+
+ MSpec.register_current nil
+ children.each { |child| child.process }
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/evaluate.rb b/spec/mspec/lib/mspec/runner/evaluate.rb
new file mode 100644
index 0000000000..ecf7460a90
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/evaluate.rb
@@ -0,0 +1,54 @@
+class SpecEvaluate
+ include MSpecMatchers
+
+ def self.desc=(desc)
+ @desc = desc
+ end
+
+ def self.desc
+ @desc ||= "evaluates "
+ end
+
+ def initialize(ruby, desc)
+ @ruby = ruby.rstrip
+ @desc = desc || self.class.desc
+ end
+
+ # Formats the Ruby source code for reabable output in the -fs formatter
+ # option. If the source contains no newline characters, wraps the source in
+ # single quotes to set if off from the rest of the description string. If
+ # the source does contain newline characters, sets the indent level to four
+ # characters.
+ def format(ruby, newline=true)
+ if ruby.include?("\n")
+ lines = ruby.each_line.to_a
+ if /( *)/ =~ lines.first
+ if $1.size > 4
+ dedent = $1.size - 4
+ ruby = lines.map { |l| l[dedent..-1] }.join
+ else
+ indent = " " * (4 - $1.size)
+ ruby = lines.map { |l| "#{indent}#{l}" }.join
+ end
+ end
+ "\n#{ruby}"
+ else
+ "'#{ruby.lstrip}'"
+ end
+ end
+
+ def define(&block)
+ ruby = @ruby
+ desc = @desc
+ evaluator = self
+
+ specify "#{desc} #{format ruby}" do
+ evaluator.instance_eval(ruby)
+ evaluator.instance_eval(&block)
+ end
+ end
+end
+
+def evaluate(str, desc=nil, &block)
+ SpecEvaluate.new(str, desc).define(&block)
+end
diff --git a/spec/mspec/lib/mspec/runner/example.rb b/spec/mspec/lib/mspec/runner/example.rb
new file mode 100644
index 0000000000..19eb29b079
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/example.rb
@@ -0,0 +1,34 @@
+require 'mspec/runner/mspec'
+
+# Holds some of the state of the example (i.e. +it+ block) that is
+# being evaluated. See also +ContextState+.
+class ExampleState
+ attr_reader :context, :it, :example
+
+ def initialize(context, it, example=nil)
+ @context = context
+ @it = it
+ @example = example
+ end
+
+ def context=(context)
+ @description = nil
+ @context = context
+ end
+
+ def describe
+ @context.description
+ end
+
+ def description
+ @description ||= "#{describe} #{@it}"
+ end
+
+ def filtered?
+ incl = MSpec.retrieve(:include) || []
+ excl = MSpec.retrieve(:exclude) || []
+ included = incl.empty? || incl.any? { |f| f === description }
+ included &&= excl.empty? || !excl.any? { |f| f === description }
+ !included
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/exception.rb b/spec/mspec/lib/mspec/runner/exception.rb
new file mode 100644
index 0000000000..0d9bb43105
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/exception.rb
@@ -0,0 +1,43 @@
+# Initialize $MSPEC_DEBUG
+$MSPEC_DEBUG ||= false
+
+class ExceptionState
+ attr_reader :description, :describe, :it, :exception
+
+ def initialize(state, location, exception)
+ @exception = exception
+
+ @description = location ? "An exception occurred during: #{location}" : ""
+ if state
+ @description += "\n" unless @description.empty?
+ @description += state.description
+ @describe = state.describe
+ @it = state.it
+ else
+ @describe = @it = ""
+ end
+ end
+
+ def failure?
+ [SpecExpectationNotMetError, SpecExpectationNotFoundError].any? { |e| @exception.is_a? e }
+ end
+
+ def message
+ if @exception.message.empty?
+ "<No message>"
+ elsif @exception.class == SpecExpectationNotMetError ||
+ @exception.class == SpecExpectationNotFoundError
+ @exception.message
+ else
+ "#{@exception.class}: #{@exception.message}"
+ end
+ end
+
+ def backtrace
+ @backtrace_filter ||= MSpecScript.config[:backtrace_filter]
+
+ bt = @exception.backtrace || []
+
+ bt.select { |line| $MSPEC_DEBUG or @backtrace_filter !~ line }.join("\n")
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/filters.rb b/spec/mspec/lib/mspec/runner/filters.rb
new file mode 100644
index 0000000000..d0420faca6
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/filters.rb
@@ -0,0 +1,4 @@
+require 'mspec/runner/filters/match'
+require 'mspec/runner/filters/regexp'
+require 'mspec/runner/filters/tag'
+require 'mspec/runner/filters/profile'
diff --git a/spec/mspec/lib/mspec/runner/filters/match.rb b/spec/mspec/lib/mspec/runner/filters/match.rb
new file mode 100644
index 0000000000..539fd02d01
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/filters/match.rb
@@ -0,0 +1,18 @@
+class MatchFilter
+ def initialize(what, *strings)
+ @what = what
+ @strings = strings
+ end
+
+ def ===(string)
+ @strings.any? { |s| string.include?(s) }
+ end
+
+ def register
+ MSpec.register @what, self
+ end
+
+ def unregister
+ MSpec.unregister @what, self
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/filters/profile.rb b/spec/mspec/lib/mspec/runner/filters/profile.rb
new file mode 100644
index 0000000000..a59722c451
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/filters/profile.rb
@@ -0,0 +1,54 @@
+class ProfileFilter
+ def initialize(what, *files)
+ @what = what
+ @methods = load(*files)
+ @pattern = /([^ .#]+[.#])([^ ]+)/
+ end
+
+ def find(name)
+ return name if File.exist?(File.expand_path(name))
+
+ ["spec/profiles", "spec", "profiles", "."].each do |dir|
+ file = File.join dir, name
+ return file if File.exist? file
+ end
+ end
+
+ def parse(file)
+ pattern = /(\S+):\s*/
+ key = ""
+ file.inject(Hash.new { |h,k| h[k] = [] }) do |hash, line|
+ line.chomp!
+ if line[0,2] == "- "
+ hash[key] << line[2..-1].gsub(/[ '"]/, "")
+ elsif m = pattern.match(line)
+ key = m[1]
+ end
+ hash
+ end
+ end
+
+ def load(*files)
+ files.inject({}) do |hash, file|
+ next hash unless name = find(file)
+
+ File.open name, "r" do |f|
+ hash.merge parse(f)
+ end
+ end
+ end
+
+ def ===(string)
+ return false unless m = @pattern.match(string)
+ return false unless l = @methods[m[1]]
+ l.include? m[2]
+ end
+
+ def register
+ MSpec.register @what, self
+ end
+
+ def unregister
+ MSpec.unregister @what, self
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/filters/regexp.rb b/spec/mspec/lib/mspec/runner/filters/regexp.rb
new file mode 100644
index 0000000000..097ec6a755
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/filters/regexp.rb
@@ -0,0 +1,23 @@
+class RegexpFilter
+ def initialize(what, *regexps)
+ @what = what
+ @regexps = to_regexp(*regexps)
+ end
+
+ def ===(string)
+ @regexps.any? { |regexp| regexp === string }
+ end
+
+ def register
+ MSpec.register @what, self
+ end
+
+ def unregister
+ MSpec.unregister @what, self
+ end
+
+ def to_regexp(*regexps)
+ regexps.map { |str| Regexp.new str }
+ end
+ private :to_regexp
+end
diff --git a/spec/mspec/lib/mspec/runner/filters/tag.rb b/spec/mspec/lib/mspec/runner/filters/tag.rb
new file mode 100644
index 0000000000..c641c01606
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/filters/tag.rb
@@ -0,0 +1,29 @@
+class TagFilter
+ def initialize(what, *tags)
+ @what = what
+ @tags = tags
+ end
+
+ def load
+ @descriptions = MSpec.read_tags(@tags).map { |t| t.description }
+ MSpec.register @what, self
+ end
+
+ def unload
+ MSpec.unregister @what, self
+ end
+
+ def ===(string)
+ @descriptions.include?(string)
+ end
+
+ def register
+ MSpec.register :load, self
+ MSpec.register :unload, self
+ end
+
+ def unregister
+ MSpec.unregister :load, self
+ MSpec.unregister :unload, self
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/formatters.rb b/spec/mspec/lib/mspec/runner/formatters.rb
new file mode 100644
index 0000000000..d085031a12
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/formatters.rb
@@ -0,0 +1,12 @@
+require 'mspec/runner/formatters/describe'
+require 'mspec/runner/formatters/dotted'
+require 'mspec/runner/formatters/file'
+require 'mspec/runner/formatters/specdoc'
+require 'mspec/runner/formatters/html'
+require 'mspec/runner/formatters/summary'
+require 'mspec/runner/formatters/unit'
+require 'mspec/runner/formatters/spinner'
+require 'mspec/runner/formatters/method'
+require 'mspec/runner/formatters/yaml'
+require 'mspec/runner/formatters/profile'
+require 'mspec/runner/formatters/junit'
diff --git a/spec/mspec/lib/mspec/runner/formatters/describe.rb b/spec/mspec/lib/mspec/runner/formatters/describe.rb
new file mode 100644
index 0000000000..176bd79279
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/formatters/describe.rb
@@ -0,0 +1,24 @@
+require 'mspec/runner/formatters/dotted'
+require 'mspec/runner/actions/tally'
+
+class DescribeFormatter < DottedFormatter
+ # Callback for the MSpec :finish event. Prints a summary of
+ # the number of errors and failures for each +describe+ block.
+ def finish
+ describes = Hash.new { |h,k| h[k] = Tally.new }
+
+ @exceptions.each do |exc|
+ desc = describes[exc.describe]
+ exc.failure? ? desc.failures! : desc.errors!
+ end
+
+ print "\n"
+ describes.each do |d, t|
+ text = d.size > 40 ? "#{d[0,37]}..." : d.ljust(40)
+ print "\n#{text} #{t.failure}, #{t.error}"
+ end
+ print "\n" unless describes.empty?
+
+ print "\n#{@timer.format}\n\n#{@tally.format}\n"
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/formatters/dotted.rb b/spec/mspec/lib/mspec/runner/formatters/dotted.rb
new file mode 100644
index 0000000000..61c8e4c27c
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/formatters/dotted.rb
@@ -0,0 +1,117 @@
+require 'mspec/expectations/expectations'
+require 'mspec/runner/actions/timer'
+require 'mspec/runner/actions/tally'
+require 'mspec/runner/actions/leakchecker' if ENV['CHECK_LEAKS']
+
+class DottedFormatter
+ attr_reader :exceptions, :timer, :tally
+
+ def initialize(out=nil)
+ @exception = @failure = false
+ @exceptions = []
+ @count = 0 # For subclasses
+ if out.nil?
+ @out = $stdout
+ else
+ @out = File.open out, "w"
+ end
+
+ @current_state = nil
+ end
+
+ # Creates the +TimerAction+ and +TallyAction+ instances and
+ # registers them. Registers +self+ for the +:exception+,
+ # +:before+, +:after+, and +:finish+ actions.
+ def register
+ (@timer = TimerAction.new).register
+ (@tally = TallyAction.new).register
+ LeakCheckerAction.new.register if ENV['CHECK_LEAKS']
+ @counter = @tally.counter
+
+ MSpec.register :exception, self
+ MSpec.register :before, self
+ MSpec.register :after, self
+ MSpec.register :finish, self
+ MSpec.register :abort, self
+ end
+
+ def abort
+ if @current_state
+ puts "\naborting example: #{@current_state.description}"
+ end
+ end
+
+ # Returns true if any exception is raised while running
+ # an example. This flag is reset before each example
+ # is evaluated.
+ def exception?
+ @exception
+ end
+
+ # Returns true if all exceptions during the evaluation
+ # of an example are failures rather than errors. See
+ # <tt>ExceptionState#failure</tt>. This flag is reset
+ # before each example is evaluated.
+ def failure?
+ @failure
+ end
+
+ # Callback for the MSpec :before event. Resets the
+ # +#exception?+ and +#failure+ flags.
+ def before(state=nil)
+ @current_state = state
+ @failure = @exception = false
+ end
+
+ # Callback for the MSpec :exception event. Stores the
+ # +ExceptionState+ object to generate the list of backtraces
+ # after all the specs are run. Also updates the internal
+ # +#exception?+ and +#failure?+ flags.
+ def exception(exception)
+ @count += 1
+ @failure = @exception ? @failure && exception.failure? : exception.failure?
+ @exception = true
+ @exceptions << exception
+ end
+
+ # Callback for the MSpec :after event. Prints an indicator
+ # for the result of evaluating this example as follows:
+ # . = No failure or error
+ # F = An SpecExpectationNotMetError was raised
+ # E = Any exception other than SpecExpectationNotMetError
+ def after(state = nil)
+ @current_state = nil
+
+ unless exception?
+ print "."
+ else
+ print failure? ? "F" : "E"
+ end
+ end
+
+ # Callback for the MSpec :finish event. Prints a description
+ # and backtrace for every exception that occurred while
+ # evaluating the examples.
+ def finish
+ print "\n"
+ count = 0
+ @exceptions.each do |exc|
+ count += 1
+ print_exception(exc, count)
+ end
+ print "\n#{@timer.format}\n\n#{@tally.format}\n"
+ end
+
+ def print_exception(exc, count)
+ outcome = exc.failure? ? "FAILED" : "ERROR"
+ print "\n#{count})\n#{exc.description} #{outcome}\n"
+ print exc.message, "\n"
+ print exc.backtrace, "\n"
+ end
+
+ # A convenience method to allow printing to different outputs.
+ def print(*args)
+ @out.print(*args)
+ @out.flush
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/formatters/file.rb b/spec/mspec/lib/mspec/runner/formatters/file.rb
new file mode 100644
index 0000000000..6db72af4ff
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/formatters/file.rb
@@ -0,0 +1,19 @@
+require 'mspec/runner/formatters/dotted'
+
+class FileFormatter < DottedFormatter
+ # Unregisters DottedFormatter#before, #after methods and
+ # registers #load, #unload, which perform the same duties
+ # as #before, #after in DottedFormatter.
+ def register
+ super
+
+ MSpec.unregister :before, self
+ MSpec.unregister :after, self
+
+ MSpec.register :load, self
+ MSpec.register :unload, self
+ end
+
+ alias_method :load, :before
+ alias_method :unload, :after
+end
diff --git a/spec/mspec/lib/mspec/runner/formatters/html.rb b/spec/mspec/lib/mspec/runner/formatters/html.rb
new file mode 100644
index 0000000000..fd64cd0d20
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/formatters/html.rb
@@ -0,0 +1,81 @@
+require 'mspec/expectations/expectations'
+require 'mspec/runner/formatters/dotted'
+
+class HtmlFormatter < DottedFormatter
+ def register
+ super
+ MSpec.register :start, self
+ MSpec.register :enter, self
+ MSpec.register :leave, self
+ end
+
+ def start
+ print <<-EOH
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>Spec Output For #{RUBY_ENGINE} (#{RUBY_VERSION})</title>
+<style type="text/css">
+ul {
+ list-style: none;
+}
+.fail {
+ color: red;
+}
+.pass {
+ color: green;
+}
+#details :target {
+ background-color: #ffffe0;
+}
+</style>
+</head>
+<body>
+EOH
+ end
+
+ def enter(describe)
+ print "<div><p>#{describe}</p>\n<ul>\n"
+ end
+
+ def leave
+ print "</ul>\n</div>\n"
+ end
+
+ def exception(exception)
+ super
+ outcome = exception.failure? ? "FAILED" : "ERROR"
+ print %[<li class="fail">- #{exception.it} (<a href="#details-#{@count}">]
+ print %[#{outcome} - #{@count}</a>)</li>\n]
+ end
+
+ def after(state)
+ print %[<li class="pass">- #{state.it}</li>\n] unless exception?
+ end
+
+ def finish
+ success = @exceptions.empty?
+ unless success
+ print "<hr>\n"
+ print %[<ol id="details">]
+ count = 0
+ @exceptions.each do |exc|
+ outcome = exc.failure? ? "FAILED" : "ERROR"
+ print %[\n<li id="details-#{count += 1}"><p>#{escape(exc.description)} #{outcome}</p>\n<p>]
+ print escape(exc.message)
+ print "</p>\n<pre>\n"
+ print escape(exc.backtrace)
+ print "</pre>\n</li>\n"
+ end
+ print "</ol>\n"
+ end
+ print %[<p>#{@timer.format}</p>\n]
+ print %[<p class="#{success ? "pass" : "fail"}">#{@tally.format}</p>\n]
+ print "</body>\n</html>\n"
+ end
+
+ def escape(string)
+ string.gsub("&", "&nbsp;").gsub("<", "&lt;").gsub(">", "&gt;")
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/formatters/junit.rb b/spec/mspec/lib/mspec/runner/formatters/junit.rb
new file mode 100644
index 0000000000..76d46c2414
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/formatters/junit.rb
@@ -0,0 +1,88 @@
+require 'mspec/expectations/expectations'
+require 'mspec/runner/formatters/yaml'
+
+class JUnitFormatter < YamlFormatter
+ def initialize(out=nil)
+ super
+ @tests = []
+ end
+
+ def after(state = nil)
+ super
+ @tests << {:test => state, :exception => false} unless exception?
+ end
+
+ def exception(exception)
+ super
+ @tests << {:test => exception, :exception => true}
+ end
+
+ def finish
+ switch
+
+ time = @timer.elapsed
+ tests = @tally.counter.examples
+ errors = @tally.counter.errors
+ failures = @tally.counter.failures
+
+ printf <<-XML
+
+<?xml version="1.0" encoding="UTF-8" ?>
+ <testsuites
+ testCount="#{tests}"
+ errorCount="#{errors}"
+ failureCount="#{failures}"
+ timeCount="#{time}" time="#{time}">
+ <testsuite
+ tests="#{tests}"
+ errors="#{errors}"
+ failures="#{failures}"
+ time="#{time}"
+ name="Spec Output For #{::RUBY_ENGINE} (#{::RUBY_VERSION})">
+ XML
+ @tests.each do |h|
+ description = encode_for_xml h[:test].description
+
+ printf <<-XML, "Spec", description, 0.0
+ <testcase classname="%s" name="%s" time="%f">
+ XML
+ if h[:exception]
+ outcome = h[:test].failure? ? "failure" : "error"
+ message = encode_for_xml h[:test].message
+ backtrace = encode_for_xml h[:test].backtrace
+ print <<-XML
+ <#{outcome} message="error in #{description}" type="#{outcome}">
+ #{message}
+ #{backtrace}
+ </#{outcome}>
+ XML
+ end
+ print <<-XML
+ </testcase>
+ XML
+ end
+
+ print <<-XML
+ </testsuite>
+ </testsuites>
+ XML
+ end
+
+ private
+ LT = "&lt;"
+ GT = "&gt;"
+ QU = "&quot;"
+ AP = "&apos;"
+ AM = "&amp;"
+ TARGET_ENCODING = "ISO-8859-1"
+
+ def encode_for_xml(str)
+ encode_as_latin1(str).gsub("<", LT).gsub(">", GT).
+ gsub('"', QU).gsub("'", AP).gsub("&", AM).
+ tr("\x00-\x08", "?")
+ end
+
+ def encode_as_latin1(str)
+ str.encode(TARGET_ENCODING, :undef => :replace, :invalid => :replace)
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/formatters/method.rb b/spec/mspec/lib/mspec/runner/formatters/method.rb
new file mode 100644
index 0000000000..ff115193fd
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/formatters/method.rb
@@ -0,0 +1,93 @@
+require 'mspec/runner/formatters/dotted'
+
+class MethodFormatter < DottedFormatter
+ attr_accessor :methods
+
+ def initialize(out=nil)
+ super
+ @methods = Hash.new do |h, k|
+ hash = {}
+ hash[:examples] = 0
+ hash[:expectations] = 0
+ hash[:failures] = 0
+ hash[:errors] = 0
+ hash[:exceptions] = []
+ h[k] = hash
+ end
+ end
+
+ # Returns the type of method as a "class", "instance",
+ # or "unknown".
+ def method_type(sep)
+ case sep
+ when '.', '::'
+ "class"
+ when '#'
+ "instance"
+ else
+ "unknown"
+ end
+ end
+
+ # Callback for the MSpec :before event. Parses the
+ # describe string into class and method if possible.
+ # Resets the tallies so the counts are only for this
+ # example.
+ def before(state)
+ super
+
+ # The pattern for a method name is not correctly
+ # restrictive but it is simplistic and useful
+ # for our purpose.
+ /^([A-Za-z_]+\w*)(\.|#|::)([^ ]+)/ =~ state.describe
+ @key = $1 && $2 && $3 ? "#{$1}#{$2}#{$3}" : state.describe
+
+ unless methods.key? @key
+ h = methods[@key]
+ h[:class] = "#{$1}"
+ h[:method] = "#{$3}"
+ h[:type] = method_type $2
+ h[:description] = state.description
+ end
+
+ tally.counter.examples = 0
+ tally.counter.expectations = 0
+ tally.counter.failures = 0
+ tally.counter.errors = 0
+
+ @exceptions = []
+ end
+
+ # Callback for the MSpec :after event. Sets or adds to
+ # tallies for the example block.
+ def after(state)
+ h = methods[@key]
+ h[:examples] += tally.counter.examples
+ h[:expectations] += tally.counter.expectations
+ h[:failures] += tally.counter.failures
+ h[:errors] += tally.counter.errors
+ @exceptions.each do |exc|
+ h[:exceptions] << "#{exc.message}\n#{exc.backtrace}\n"
+ end
+ end
+
+ # Callback for the MSpec :finish event. Prints out the
+ # summary information in YAML format for all the methods.
+ def finish
+ print "---\n"
+
+ methods.each do |key, hash|
+ print key.inspect, ":\n"
+ print " class: ", hash[:class].inspect, "\n"
+ print " method: ", hash[:method].inspect, "\n"
+ print " type: ", hash[:type], "\n"
+ print " description: ", hash[:description].inspect, "\n"
+ print " examples: ", hash[:examples], "\n"
+ print " expectations: ", hash[:expectations], "\n"
+ print " failures: ", hash[:failures], "\n"
+ print " errors: ", hash[:errors], "\n"
+ print " exceptions:\n"
+ hash[:exceptions].each { |exc| print " - ", exc.inspect, "\n" }
+ end
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/formatters/multi.rb b/spec/mspec/lib/mspec/runner/formatters/multi.rb
new file mode 100644
index 0000000000..5932eeef62
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/formatters/multi.rb
@@ -0,0 +1,40 @@
+require 'mspec/runner/formatters/spinner'
+
+class MultiFormatter < SpinnerFormatter
+ def initialize(out=nil)
+ super(out)
+ @counter = @tally = Tally.new
+ @timer = TimerAction.new
+ @timer.start
+ end
+
+ def aggregate_results(files)
+ require 'yaml'
+
+ @timer.finish
+ @exceptions = []
+
+ files.each do |file|
+ contents = File.read(file)
+ d = YAML.load(contents)
+ File.delete file
+
+ if d # The file might be empty if the child process died
+ @exceptions += Array(d['exceptions'])
+ @tally.files! d['files']
+ @tally.examples! d['examples']
+ @tally.expectations! d['expectations']
+ @tally.errors! d['errors']
+ @tally.failures! d['failures']
+ end
+ end
+ end
+
+ def print_exception(exc, count)
+ print "\n#{count})\n#{exc}\n"
+ end
+
+ def finish
+ super(false)
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/formatters/profile.rb b/spec/mspec/lib/mspec/runner/formatters/profile.rb
new file mode 100644
index 0000000000..498cd4a3b7
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/formatters/profile.rb
@@ -0,0 +1,70 @@
+require 'mspec/expectations/expectations'
+require 'mspec/runner/formatters/dotted'
+
+class ProfileFormatter < DottedFormatter
+ def initialize(out=nil)
+ super
+
+ @describe_name = nil
+ @describe_time = nil
+ @describes = []
+ @its = []
+ end
+
+ def register
+ super
+ MSpec.register :enter, self
+ end
+
+ # Callback for the MSpec :enter event. Prints the
+ # +describe+ block string.
+ def enter(describe)
+ if @describe_time
+ @describes << [@describe_name, Time.now.to_f - @describe_time]
+ end
+
+ @describe_name = describe
+ @describe_time = Time.now.to_f
+ end
+
+ # Callback for the MSpec :before event. Prints the
+ # +it+ block string.
+ def before(state)
+ super
+
+ @it_name = state.it
+ @it_time = Time.now.to_f
+ end
+
+ # Callback for the MSpec :after event. Prints a
+ # newline to finish the description string output.
+ def after(state)
+ @its << [@describe_name, @it_name, Time.now.to_f - @it_time]
+ super
+ end
+
+ def finish
+ puts "\nProfiling info:"
+
+ desc = @describes.sort { |a,b| b.last <=> a.last }
+ desc.delete_if { |a| a.last <= 0.001 }
+ show = desc[0, 100]
+
+ puts "Top #{show.size} describes:"
+
+ show.each do |des, time|
+ printf "%3.3f - %s\n", time, des
+ end
+
+ its = @its.sort { |a,b| b.last <=> a.last }
+ its.delete_if { |a| a.last <= 0.001 }
+ show = its[0, 100]
+
+ puts "\nTop #{show.size} its:"
+ show.each do |des, it, time|
+ printf "%3.3f - %s %s\n", time, des, it
+ end
+
+ super
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/formatters/specdoc.rb b/spec/mspec/lib/mspec/runner/formatters/specdoc.rb
new file mode 100644
index 0000000000..29adde3c5c
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/formatters/specdoc.rb
@@ -0,0 +1,41 @@
+require 'mspec/expectations/expectations'
+require 'mspec/runner/formatters/dotted'
+
+class SpecdocFormatter < DottedFormatter
+ def register
+ super
+ MSpec.register :enter, self
+ end
+
+ # Callback for the MSpec :enter event. Prints the
+ # +describe+ block string.
+ def enter(describe)
+ print "\n#{describe}\n"
+ end
+
+ # Callback for the MSpec :before event. Prints the
+ # +it+ block string.
+ def before(state)
+ super
+ print "- #{state.it}"
+ end
+
+ # Callback for the MSpec :exception event. Prints
+ # either 'ERROR - X' or 'FAILED - X' where _X_ is
+ # the sequential number of the exception raised. If
+ # there has already been an exception raised while
+ # evaluating this example, it prints another +it+
+ # block description string so that each discription
+ # string has an associated 'ERROR' or 'FAILED'
+ def exception(exception)
+ print "\n- #{exception.it}" if exception?
+ super
+ print " (#{exception.failure? ? 'FAILED' : 'ERROR'} - #{@count})"
+ end
+
+ # Callback for the MSpec :after event. Prints a
+ # newline to finish the description string output.
+ def after(state)
+ print "\n"
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/formatters/spinner.rb b/spec/mspec/lib/mspec/runner/formatters/spinner.rb
new file mode 100644
index 0000000000..f6f35cc476
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/formatters/spinner.rb
@@ -0,0 +1,117 @@
+require 'mspec/expectations/expectations'
+require 'mspec/runner/formatters/dotted'
+
+class SpinnerFormatter < DottedFormatter
+ attr_reader :length
+
+ Spins = %w!| / - \\!
+ HOUR = 3600
+ MIN = 60
+
+ def initialize(out=nil)
+ super(nil)
+
+ @which = 0
+ @loaded = 0
+ self.length = 40
+ @percent = 0
+ @start = Time.now
+
+ term = ENV['TERM']
+ @color = (term != "dumb")
+ @fail_color = "32"
+ @error_color = "32"
+ end
+
+ def register
+ super
+
+ MSpec.register :start, self
+ MSpec.register :unload, self
+ MSpec.unregister :before, self
+ end
+
+ def length=(length)
+ @length = length
+ @ratio = 100.0 / length
+ @position = length / 2 - 2
+ end
+
+ def compute_etr
+ return @etr = "00:00:00" if @percent == 0
+ elapsed = Time.now - @start
+ remain = (100 * elapsed / @percent) - elapsed
+
+ hour = remain >= HOUR ? (remain / HOUR).to_i : 0
+ remain -= hour * HOUR
+ min = remain >= MIN ? (remain / MIN).to_i : 0
+ sec = remain - min * MIN
+
+ @etr = "%02d:%02d:%02d" % [hour, min, sec]
+ end
+
+ def compute_percentage
+ @percent = @loaded * 100 / @total
+ bar = ("=" * (@percent / @ratio)).ljust @length
+ label = "%d%%" % @percent
+ bar[@position, label.size] = label
+ @bar = bar
+ end
+
+ def compute_progress
+ compute_percentage
+ compute_etr
+ end
+
+ def progress_line
+ @which = (@which + 1) % Spins.size
+ data = [Spins[@which], @bar, @etr, @counter.failures, @counter.errors]
+ if @color
+ "\r[%s | %s | %s] \e[0;#{@fail_color}m%6dF \e[0;#{@error_color}m%6dE\e[0m " % data
+ else
+ "\r[%s | %s | %s] %6dF %6dE " % data
+ end
+ end
+
+ def clear_progress_line
+ print "\r#{' '*progress_line.length}"
+ end
+
+ # Callback for the MSpec :start event. Stores the total
+ # number of files that will be processed.
+ def start
+ @total = MSpec.retrieve(:files).size
+ compute_progress
+ print progress_line
+ end
+
+ # Callback for the MSpec :unload event. Increments the number
+ # of files that have been run.
+ def unload
+ @loaded += 1
+ compute_progress
+ print progress_line
+ end
+
+ # Callback for the MSpec :exception event. Changes the color
+ # used to display the tally of errors and failures
+ def exception(exception)
+ super
+ @fail_color = "31" if exception.failure?
+ @error_color = "33" unless exception.failure?
+
+ clear_progress_line
+ print_exception(exception, @count)
+ end
+
+ # Callback for the MSpec :after event. Updates the spinner.
+ def after(state)
+ print progress_line
+ end
+
+ def finish(printed_exceptions = true)
+ # We already printed the exceptions
+ @exceptions = [] if printed_exceptions
+ super()
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/formatters/summary.rb b/spec/mspec/lib/mspec/runner/formatters/summary.rb
new file mode 100644
index 0000000000..0c9207194c
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/formatters/summary.rb
@@ -0,0 +1,11 @@
+require 'mspec/expectations/expectations'
+require 'mspec/runner/formatters/dotted'
+
+class SummaryFormatter < DottedFormatter
+ # Callback for the MSpec :after event. Overrides the
+ # callback provided by +DottedFormatter+ and does not
+ # print any output for each example evaluated.
+ def after(state)
+ # do nothing
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/formatters/unit.rb b/spec/mspec/lib/mspec/runner/formatters/unit.rb
new file mode 100644
index 0000000000..cebc18a49b
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/formatters/unit.rb
@@ -0,0 +1,21 @@
+require 'mspec/expectations/expectations'
+require 'mspec/runner/formatters/dotted'
+
+class UnitdiffFormatter < DottedFormatter
+ def finish
+ print "\n\n#{@timer.format}\n"
+ count = 0
+ @exceptions.each do |exc|
+ outcome = exc.failure? ? "FAILED" : "ERROR"
+ print "\n#{count += 1})\n#{exc.description} #{outcome}\n"
+ print exc.message, ":\n"
+ print exc.backtrace, "\n"
+ end
+ print "\n#{@tally.format}\n"
+ end
+
+ def backtrace(exc)
+ exc.backtrace && exc.backtrace.join("\n")
+ end
+ private :backtrace
+end
diff --git a/spec/mspec/lib/mspec/runner/formatters/yaml.rb b/spec/mspec/lib/mspec/runner/formatters/yaml.rb
new file mode 100644
index 0000000000..090a9b1b9d
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/formatters/yaml.rb
@@ -0,0 +1,42 @@
+require 'mspec/expectations/expectations'
+require 'mspec/runner/formatters/dotted'
+
+class YamlFormatter < DottedFormatter
+ def initialize(out=nil)
+ super(nil)
+
+ if out.nil?
+ @finish = $stdout
+ else
+ @finish = File.open out, "w"
+ end
+ end
+
+ def switch
+ @out = @finish
+ end
+
+ def after(state)
+ end
+
+ def finish
+ switch
+
+ print "---\n"
+ print "exceptions:\n"
+ @exceptions.each do |exc|
+ outcome = exc.failure? ? "FAILED" : "ERROR"
+ str = "#{exc.description} #{outcome}\n"
+ str << exc.message << "\n" << exc.backtrace
+ print "- ", str.inspect, "\n"
+ end
+
+ print "time: ", @timer.elapsed, "\n"
+ print "files: ", @tally.counter.files, "\n"
+ print "examples: ", @tally.counter.examples, "\n"
+ print "expectations: ", @tally.counter.expectations, "\n"
+ print "failures: ", @tally.counter.failures, "\n"
+ print "errors: ", @tally.counter.errors, "\n"
+ print "tagged: ", @tally.counter.tagged, "\n"
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/mspec.rb b/spec/mspec/lib/mspec/runner/mspec.rb
new file mode 100644
index 0000000000..cfc11840ac
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/mspec.rb
@@ -0,0 +1,408 @@
+require 'mspec/runner/context'
+require 'mspec/runner/exception'
+require 'mspec/runner/tag'
+
+module MSpec
+end
+
+class MSpecEnv
+ include MSpec
+end
+
+module MSpec
+
+ @exit = nil
+ @abort = nil
+ @start = nil
+ @enter = nil
+ @before = nil
+ @add = nil
+ @after = nil
+ @leave = nil
+ @finish = nil
+ @exclude = nil
+ @include = nil
+ @leave = nil
+ @load = nil
+ @unload = nil
+ @tagged = nil
+ @current = nil
+ @example = nil
+ @modes = []
+ @shared = {}
+ @guarded = []
+ @features = {}
+ @exception = nil
+ @randomize = nil
+ @repeat = nil
+ @expectation = nil
+ @expectations = false
+
+ def self.describe(mod, options=nil, &block)
+ state = ContextState.new mod, options
+ state.parent = current
+
+ MSpec.register_current state
+ state.describe(&block)
+
+ state.process unless state.shared? or current
+ end
+
+ def self.process
+ STDOUT.puts RUBY_DESCRIPTION
+ STDOUT.flush
+
+ actions :start
+ files
+ actions :finish
+ end
+
+ def self.each_file(&block)
+ if ENV["MSPEC_MULTI"]
+ while file = STDIN.gets
+ file = file.chomp
+ return if file == "QUIT"
+ yield file
+ begin
+ STDOUT.print "."
+ STDOUT.flush
+ rescue Errno::EPIPE
+ # The parent died
+ exit 1
+ end
+ end
+ # The parent closed the connection without QUIT
+ abort "the parent did not send QUIT"
+ else
+ return unless files = retrieve(:files)
+ shuffle files if randomize?
+ files.each(&block)
+ end
+ end
+
+ def self.files
+ each_file do |file|
+ setup_env
+ store :file, file
+ actions :load
+ protect("loading #{file}") { Kernel.load file }
+ actions :unload
+ end
+ end
+
+ def self.setup_env
+ @env = MSpecEnv.new
+ end
+
+ def self.actions(action, *args)
+ actions = retrieve(action)
+ actions.each { |obj| obj.send action, *args } if actions
+ end
+
+ def self.protect(location, &block)
+ begin
+ @env.instance_eval(&block)
+ return true
+ rescue SystemExit => e
+ raise e
+ rescue Exception => exc
+ register_exit 1
+ actions :exception, ExceptionState.new(current && current.state, location, exc)
+ return false
+ end
+ end
+
+ # Guards can be nested, so a stack is necessary to know when we have
+ # exited the toplevel guard.
+ def self.guard
+ @guarded << true
+ end
+
+ def self.unguard
+ @guarded.pop
+ end
+
+ def self.guarded?
+ !@guarded.empty?
+ end
+
+ # Sets the toplevel ContextState to +state+.
+ def self.register_current(state)
+ store :current, state
+ end
+
+ # Sets the toplevel ContextState to +nil+.
+ def self.clear_current
+ store :current, nil
+ end
+
+ # Returns the toplevel ContextState.
+ def self.current
+ retrieve :current
+ end
+
+ # Stores the shared ContextState keyed by description.
+ def self.register_shared(state)
+ @shared[state.to_s] = state
+ end
+
+ # Returns the shared ContextState matching description.
+ def self.retrieve_shared(desc)
+ @shared[desc.to_s]
+ end
+
+ # Stores the exit code used by the runner scripts.
+ def self.register_exit(code)
+ store :exit, code
+ end
+
+ # Retrieves the stored exit code.
+ def self.exit_code
+ retrieve(:exit).to_i
+ end
+
+ # Stores the list of files to be evaluated.
+ def self.register_files(files)
+ store :files, files
+ end
+
+ # Stores one or more substitution patterns for transforming
+ # a spec filename into a tags filename, where each pattern
+ # has the form:
+ #
+ # [Regexp, String]
+ #
+ # See also +tags_file+.
+ def self.register_tags_patterns(patterns)
+ store :tags_patterns, patterns
+ end
+
+ # Registers an operating mode. Modes recognized by MSpec:
+ #
+ # :pretend - actions execute but specs are not run
+ # :verify - specs are run despite guards and the result is
+ # verified to match the expectation of the guard
+ # :report - specs that are guarded are reported
+ # :unguarded - all guards are forced off
+ def self.register_mode(mode)
+ modes = retrieve :modes
+ modes << mode unless modes.include? mode
+ end
+
+ # Clears all registered modes.
+ def self.clear_modes
+ store :modes, []
+ end
+
+ # Returns +true+ if +mode+ is registered.
+ def self.mode?(mode)
+ retrieve(:modes).include? mode
+ end
+
+ def self.enable_feature(feature)
+ retrieve(:features)[feature] = true
+ end
+
+ def self.disable_feature(feature)
+ retrieve(:features)[feature] = false
+ end
+
+ def self.feature_enabled?(feature)
+ retrieve(:features)[feature] || false
+ end
+
+ def self.retrieve(symbol)
+ instance_variable_get :"@#{symbol}"
+ end
+
+ def self.store(symbol, value)
+ instance_variable_set :"@#{symbol}", value
+ end
+
+ # This method is used for registering actions that are
+ # run at particular points in the spec cycle:
+ # :start before any specs are run
+ # :load before a spec file is loaded
+ # :enter before a describe block is run
+ # :before before a single spec is run
+ # :add while a describe block is adding examples to run later
+ # :expectation before a 'should', 'should_receive', etc.
+ # :example after an example block is run, passed the block
+ # :exception after an exception is rescued
+ # :after after a single spec is run
+ # :leave after a describe block is run
+ # :unload after a spec file is run
+ # :finish after all specs are run
+ #
+ # Objects registered as actions above should respond to
+ # a method of the same name. For example, if an object
+ # is registered as a :start action, it should respond to
+ # a #start method call.
+ #
+ # Additionally, there are two "action" lists for
+ # filtering specs:
+ # :include return true if the spec should be run
+ # :exclude return true if the spec should NOT be run
+ #
+ def self.register(symbol, action)
+ unless value = retrieve(symbol)
+ value = store symbol, []
+ end
+ value << action unless value.include? action
+ end
+
+ def self.unregister(symbol, action)
+ if value = retrieve(symbol)
+ value.delete action
+ end
+ end
+
+ def self.randomize(flag=true)
+ @randomize = flag
+ end
+
+ def self.randomize?
+ @randomize == true
+ end
+
+ def self.repeat=(times)
+ @repeat = times
+ end
+
+ def self.repeat
+ (@repeat || 1).times do
+ yield
+ end
+ end
+
+ def self.shuffle(ary)
+ return if ary.empty?
+
+ size = ary.size
+ size.times do |i|
+ r = rand(size - i - 1)
+ ary[i], ary[r] = ary[r], ary[i]
+ end
+ end
+
+ # Records that an expectation has been encountered in an example.
+ def self.expectation
+ store :expectations, true
+ end
+
+ # Returns true if an expectation has been encountered
+ def self.expectation?
+ retrieve :expectations
+ end
+
+ # Resets the flag that an expectation has been encountered in an example.
+ def self.clear_expectations
+ store :expectations, false
+ end
+
+ # Transforms a spec filename into a tags filename by applying each
+ # substitution pattern in :tags_pattern. The default patterns are:
+ #
+ # [%r(/spec/), '/spec/tags/'], [/_spec.rb$/, '_tags.txt']
+ #
+ # which will perform the following transformation:
+ #
+ # path/to/spec/class/method_spec.rb => path/to/spec/tags/class/method_tags.txt
+ #
+ # See also +register_tags_patterns+.
+ def self.tags_file
+ patterns = retrieve(:tags_patterns) ||
+ [[%r(spec/), 'spec/tags/'], [/_spec.rb$/, '_tags.txt']]
+ patterns.inject(retrieve(:file).dup) do |file, pattern|
+ file.gsub(*pattern)
+ end
+ end
+
+ # Returns a list of tags matching any tag string in +keys+ based
+ # on the return value of <tt>keys.include?("tag_name")</tt>
+ def self.read_tags(keys)
+ tags = []
+ file = tags_file
+ if File.exist? file
+ File.open(file, "r:utf-8") do |f|
+ f.each_line do |line|
+ line.chomp!
+ next if line.empty?
+ tag = SpecTag.new line
+ tags << tag if keys.include? tag.tag
+ end
+ end
+ end
+ tags
+ end
+
+ def self.make_tag_dir(path)
+ parent = File.dirname(path)
+ return if File.exist? parent
+ begin
+ Dir.mkdir(parent)
+ rescue SystemCallError
+ make_tag_dir(parent)
+ Dir.mkdir(parent)
+ end
+ end
+
+ # Writes each tag in +tags+ to the tag file. Overwrites the
+ # tag file if it exists.
+ def self.write_tags(tags)
+ file = tags_file
+ make_tag_dir(file)
+ File.open(file, "w:utf-8") do |f|
+ tags.each { |t| f.puts t }
+ end
+ end
+
+ # Writes +tag+ to the tag file if it does not already exist.
+ # Returns +true+ if the tag is written, +false+ otherwise.
+ def self.write_tag(tag)
+ tags = read_tags([tag.tag])
+ tags.each do |t|
+ if t.tag == tag.tag and t.description == tag.description
+ return false
+ end
+ end
+
+ file = tags_file
+ make_tag_dir(file)
+ File.open(file, "a:utf-8") { |f| f.puts tag.to_s }
+ return true
+ end
+
+ # Deletes +tag+ from the tag file if it exists. Returns +true+
+ # if the tag is deleted, +false+ otherwise. Deletes the tag
+ # file if it is empty.
+ def self.delete_tag(tag)
+ deleted = false
+ desc = tag.escape(tag.description)
+ file = tags_file
+ if File.exist? file
+ lines = IO.readlines(file)
+ File.open(file, "w:utf-8") do |f|
+ lines.each do |line|
+ line = line.chomp
+ if line.start_with?(tag.tag) and line.end_with?(desc)
+ deleted = true
+ else
+ f.puts line unless line.empty?
+ end
+ end
+ end
+ File.delete file unless File.size? file
+ end
+ return deleted
+ end
+
+ # Removes the tag file associated with a spec file.
+ def self.delete_tags
+ file = tags_file
+ File.delete file if File.exist? file
+ end
+
+ # Initialize @env
+ setup_env
+end
diff --git a/spec/mspec/lib/mspec/runner/object.rb b/spec/mspec/lib/mspec/runner/object.rb
new file mode 100644
index 0000000000..2ea8197165
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/object.rb
@@ -0,0 +1,26 @@
+class Object
+ private def before(at=:each, &block)
+ MSpec.current.before at, &block
+ end
+
+ private def after(at=:each, &block)
+ MSpec.current.after at, &block
+ end
+
+ private def describe(mod, msg=nil, options=nil, &block)
+ MSpec.describe mod, msg, &block
+ end
+
+ private def it(msg, &block)
+ MSpec.current.it msg, &block
+ end
+
+ private def it_should_behave_like(desc)
+ MSpec.current.it_should_behave_like desc
+ end
+
+ alias_method :context, :describe
+ private :context
+ alias_method :specify, :it
+ private :specify
+end
diff --git a/spec/mspec/lib/mspec/runner/parallel.rb b/spec/mspec/lib/mspec/runner/parallel.rb
new file mode 100644
index 0000000000..7428b33682
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/parallel.rb
@@ -0,0 +1,98 @@
+class ParallelRunner
+ def initialize(files, processes, formatter, argv)
+ @files = files
+ @processes = processes
+ @formatter = formatter
+ @argv = argv
+ @last_files = {}
+ @output_files = []
+ @success = true
+ end
+
+ def launch_children
+ @children = @processes.times.map { |i|
+ name = tmp "mspec-multi-#{i}"
+ @output_files << name
+
+ env = {
+ "SPEC_TEMP_DIR" => "rubyspec_temp_#{i}",
+ "MSPEC_MULTI" => i.to_s
+ }
+ command = @argv + ["-fy", "-o", name]
+ $stderr.puts "$ #{command.join(' ')}" if $MSPEC_DEBUG
+ IO.popen([env, *command, close_others: false], "rb+")
+ }
+ end
+
+ def handle(child, message)
+ case message
+ when '.'
+ @formatter.unload
+ send_new_file_or_quit(child)
+ else
+ if message == nil
+ msg = "A child mspec-run process died unexpectedly"
+ else
+ msg = "A child mspec-run process printed unexpected output on STDOUT"
+ while chunk = (child.read_nonblock(4096) rescue nil)
+ message += chunk
+ end
+ message.chomp!('.')
+ msg += ": #{message.inspect}"
+ end
+
+ if last_file = @last_files[child]
+ msg += " while running #{last_file}"
+ end
+
+ @success = false
+ quit(child)
+ abort "\n#{msg}"
+ end
+ end
+
+ def quit(child)
+ begin
+ child.puts "QUIT"
+ rescue Errno::EPIPE
+ # The child process already died
+ end
+ _pid, status = Process.wait2(child.pid)
+ @success &&= status.success?
+ child.close
+ @children.delete(child)
+ end
+
+ def send_new_file_or_quit(child)
+ if @files.empty?
+ quit(child)
+ else
+ file = @files.shift
+ @last_files[child] = file
+ child.puts file
+ end
+ end
+
+ def run
+ MSpec.register_files @files
+ launch_children
+
+ puts @children.map { |child| child.gets }.uniq
+ @formatter.start
+ begin
+ @children.each { |child| send_new_file_or_quit(child) }
+
+ until @children.empty?
+ IO.select(@children)[0].each { |child|
+ handle(child, child.read(1))
+ }
+ end
+ ensure
+ @children.dup.each { |child| quit(child) }
+ @formatter.aggregate_results(@output_files)
+ @formatter.finish
+ end
+
+ @success
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/shared.rb b/spec/mspec/lib/mspec/runner/shared.rb
new file mode 100644
index 0000000000..b606de473b
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/shared.rb
@@ -0,0 +1,10 @@
+require 'mspec/runner/mspec'
+
+def it_behaves_like(desc, meth, obj=nil)
+ send :before, :all do
+ @method = meth
+ @object = obj
+ end
+
+ send :it_should_behave_like, desc.to_s
+end
diff --git a/spec/mspec/lib/mspec/runner/tag.rb b/spec/mspec/lib/mspec/runner/tag.rb
new file mode 100644
index 0000000000..e2275ad3a6
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/tag.rb
@@ -0,0 +1,38 @@
+class SpecTag
+ attr_accessor :tag, :comment, :description
+
+ def initialize(string=nil)
+ parse(string) if string
+ end
+
+ def parse(string)
+ m = /^([^()#:]+)(\(([^)]+)?\))?:(.*)$/.match string
+ @tag, @comment, description = m.values_at(1, 3, 4) if m
+ @description = unescape description
+ end
+
+ def unescape(str)
+ return unless str
+ if str[0] == ?" and str[-1] == ?"
+ str[1..-2].gsub('\n', "\n")
+ else
+ str
+ end
+ end
+
+ def escape(str)
+ if str.include? "\n"
+ %["#{str.gsub("\n", '\n')}"]
+ else
+ str
+ end
+ end
+
+ def to_s
+ "#{@tag}#{ "(#{@comment})" if @comment }:#{escape @description}"
+ end
+
+ def ==(o)
+ @tag == o.tag and @comment == o.comment and @description == o.description
+ end
+end
diff --git a/spec/mspec/lib/mspec/utils/deprecate.rb b/spec/mspec/lib/mspec/utils/deprecate.rb
new file mode 100644
index 0000000000..1db843b329
--- /dev/null
+++ b/spec/mspec/lib/mspec/utils/deprecate.rb
@@ -0,0 +1,6 @@
+module MSpec
+ def self.deprecate(what, replacement)
+ user_caller = caller.find { |line| !line.include?('lib/mspec') }
+ $stderr.puts "\n#{what} is deprecated, use #{replacement} instead.\nfrom #{user_caller}"
+ end
+end
diff --git a/spec/mspec/lib/mspec/utils/name_map.rb b/spec/mspec/lib/mspec/utils/name_map.rb
new file mode 100644
index 0000000000..a93b0d001e
--- /dev/null
+++ b/spec/mspec/lib/mspec/utils/name_map.rb
@@ -0,0 +1,121 @@
+class NameMap
+ MAP = {
+ '`' => 'backtick',
+ '+' => 'plus',
+ '-' => 'minus',
+ '+@' => 'uplus',
+ '-@' => 'uminus',
+ '*' => 'multiply',
+ '/' => 'divide',
+ '%' => 'modulo',
+ '<<' => {'Integer' => 'left_shift',
+ 'IO' => 'output',
+ :default => 'append' },
+ '>>' => 'right_shift',
+ '<' => 'lt',
+ '<=' => 'lte',
+ '>' => 'gt',
+ '>=' => 'gte',
+ '=' => 'assignment',
+ '==' => 'equal_value',
+ '===' => 'case_compare',
+ '<=>' => 'comparison',
+ '[]' => 'element_reference',
+ '[]=' => 'element_set',
+ '**' => 'exponent',
+ '!' => 'not',
+ '~' => {'Integer' => 'complement',
+ :default => 'match' },
+ '!=' => 'not_equal',
+ '!~' => 'not_match',
+ '=~' => 'match',
+ '&' => {'Integer' => 'bit_and',
+ 'Array' => 'intersection',
+ 'Set' => 'intersection',
+ :default => 'and' },
+ '|' => {'Integer' => 'bit_or',
+ 'Array' => 'union',
+ 'Set' => 'union',
+ :default => 'or' },
+ '^' => {'Integer' => 'bit_xor',
+ 'Set' => 'exclusion',
+ :default => 'xor' },
+ }
+
+ EXCLUDED = %w[
+ MSpecScript
+ MkSpec
+ MSpecOption
+ MSpecOptions
+ NameMap
+ SpecVersion
+ ]
+
+ def initialize(filter=false)
+ @seen = {}
+ @filter = filter
+ end
+
+ def exception?(name)
+ return false unless c = class_or_module(name)
+ c == Errno or c.ancestors.include? Exception
+ end
+
+ def class_or_module(c)
+ const = Object.const_get(c, false)
+ filtered = @filter && EXCLUDED.include?(const.name)
+ return const if Module === const and !filtered
+ rescue NameError
+ end
+
+ def namespace(mod, const)
+ return const.to_s if mod.nil? or %w[Object Class Module].include? mod
+ "#{mod}::#{const}"
+ end
+
+ def map(hash, constants, mod=nil)
+ @seen = {} unless mod
+
+ constants.each do |const|
+ name = namespace mod, const
+ m = class_or_module name
+ next unless m and !@seen[m]
+ @seen[m] = true
+
+ ms = m.methods(false).map { |x| x.to_s }
+ hash["#{name}."] = ms.sort unless ms.empty?
+
+ ms = m.public_instance_methods(false) +
+ m.protected_instance_methods(false)
+ ms.map! { |x| x.to_s }
+ hash["#{name}#"] = ms.sort unless ms.empty?
+
+ map hash, m.constants(false), name
+ end
+
+ hash
+ end
+
+ def dir_name(c, base)
+ return File.join(base, 'exception') if exception? c
+
+ c.split('::').inject(base) do |dir, name|
+ name.gsub!(/Class/, '') unless name == 'Class'
+ File.join dir, name.downcase
+ end
+ end
+
+ def file_name(m, c)
+ if MAP.key?(m)
+ mapping = MAP[m]
+ if mapping.is_a?(Hash)
+ name = mapping[c.split('::').last] || mapping.fetch(:default)
+ else
+ name = mapping
+ end
+ else
+ name = m.gsub(/[?!=]/, '')
+ end
+ "#{name}_spec.rb"
+ end
+end
diff --git a/spec/mspec/lib/mspec/utils/options.rb b/spec/mspec/lib/mspec/utils/options.rb
new file mode 100644
index 0000000000..9f8dd01dbf
--- /dev/null
+++ b/spec/mspec/lib/mspec/utils/options.rb
@@ -0,0 +1,481 @@
+require 'mspec/version'
+
+MSPEC_HOME = File.expand_path('../../../..', __FILE__)
+
+class MSpecOption
+ attr_reader :short, :long, :arg, :description, :block
+
+ def initialize(short, long, arg, description, block)
+ @short = short
+ @long = long
+ @arg = arg
+ @description = description
+ @block = block
+ end
+
+ def arg?
+ @arg != nil
+ end
+
+ def match?(opt)
+ opt == @short or opt == @long
+ end
+end
+
+# MSpecOptions provides a parser for command line options. It also
+# provides a composable set of options from which the runner scripts
+# can select for their particular functionality.
+class MSpecOptions
+ # Raised if incorrect or incomplete formats are passed to #on.
+ class OptionError < Exception; end
+
+ # Raised if an unrecognized option is encountered.
+ class ParseError < Exception; end
+
+ attr_accessor :config, :banner, :width, :options
+
+ def initialize(banner="", width=30, config=nil)
+ @banner = banner
+ @config = config
+ @width = width
+ @options = []
+ @doc = []
+ @extra = []
+ @on_extra = lambda { |x|
+ raise ParseError, "Unrecognized option: #{x}" if x[0] == ?-
+ @extra << x
+ }
+
+ yield self if block_given?
+ end
+
+ # Registers an option. Acceptable formats for arguments are:
+ #
+ # on "-a", "description"
+ # on "-a", "--abdc", "description"
+ # on "-a", "ARG", "description"
+ # on "--abdc", "ARG", "description"
+ # on "-a", "--abdc", "ARG", "description"
+ #
+ # If an block is passed, it will be invoked when the option is
+ # matched. Not passing a block is permitted, but nonsensical.
+ def on(*args, &block)
+ raise OptionError, "option and description are required" if args.size < 2
+
+ description = args.pop
+ short, long, argument = nil
+ args.each do |arg|
+ if arg[0] == ?-
+ if arg[1] == ?-
+ long = arg
+ else
+ short = arg
+ end
+ else
+ argument = arg
+ end
+ end
+
+ add short, long, argument, description, block
+ end
+
+ # Adds documentation text for an option and adds an +MSpecOption+
+ # instance to the list of registered options.
+ def add(short, long, arg, description, block)
+ s = short ? short.dup : " "
+ s += (short ? ", " : " ") if long
+ doc " #{s}#{long} #{arg}".ljust(@width-1) + " #{description}"
+ @options << MSpecOption.new(short, long, arg, description, block)
+ end
+
+ # Searches all registered options to find a match for +opt+. Returns
+ # +nil+ if no registered options match.
+ def match?(opt)
+ @options.find { |o| o.match? opt }
+ end
+
+ # Processes an option. Calles the #on_extra block (or default) for
+ # unrecognized options. For registered options, possibly fetches an
+ # argument and invokes the option's block if it is not nil.
+ def process(argv, entry, opt, arg)
+ unless option = match?(opt)
+ @on_extra[entry]
+ else
+ if option.arg?
+ arg = argv.shift if arg.nil?
+ raise ParseError, "No argument provided for #{opt}" unless arg
+ option.block[arg] if option.block
+ else
+ option.block[] if option.block
+ end
+ end
+ option
+ end
+
+ # Splits a string at +n+ characters into the +opt+ and the +rest+.
+ # The +arg+ is set to +nil+ if +rest+ is an empty string.
+ def split(str, n)
+ opt = str[0, n]
+ rest = str[n, str.size]
+ arg = rest == "" ? nil : rest
+ return opt, arg, rest
+ end
+
+ # Parses an array of command line entries, calling blocks for
+ # registered options.
+ def parse(argv=ARGV)
+ argv = Array(argv).dup
+
+ while entry = argv.shift
+ # collect everything that is not an option
+ if entry[0] != ?- or entry.size < 2
+ @on_extra[entry]
+ next
+ end
+
+ # this is a long option
+ if entry[1] == ?-
+ opt, arg = entry.split "="
+ process argv, entry, opt, arg
+ next
+ end
+
+ # disambiguate short option group from short option with argument
+ opt, arg, rest = split entry, 2
+
+ # process first option
+ option = process argv, entry, opt, arg
+ next unless option and !option.arg?
+
+ # process the rest of the options
+ while rest.size > 0
+ opt, arg, rest = split rest, 1
+ opt = "-" + opt
+ option = process argv, opt, opt, arg
+ break if !option or option.arg?
+ end
+ end
+
+ @extra
+ rescue ParseError => e
+ puts self
+ puts e
+ exit 1
+ end
+
+ # Adds a string of documentation text inline in the text generated
+ # from the options. See #on and #add.
+ def doc(str)
+ @doc << str
+ end
+
+ # Convenience method for providing -v, --version options.
+ def version(version, &block)
+ show = block || lambda { puts "#{File.basename $0} #{version}"; exit }
+ on "-v", "--version", "Show version", &show
+ end
+
+ # Convenience method for providing -h, --help options.
+ def help(&block)
+ help = block || lambda { puts self; exit 1 }
+ on "-h", "--help", "Show this message", &help
+ end
+
+ # Stores a block that will be called with unrecognized options
+ def on_extra(&block)
+ @on_extra = block
+ end
+
+ # Returns a string representation of the options and doc strings.
+ def to_s
+ @banner + "\n\n" + @doc.join("\n") + "\n"
+ end
+
+ # The methods below provide groups of options that
+ # are composed by the particular runners to provide
+ # their functionality
+
+ def configure(&block)
+ on("-B", "--config", "FILE",
+ "Load FILE containing configuration options", &block)
+ end
+
+ def targets
+ on("-t", "--target", "TARGET",
+ "Implementation to run the specs, where TARGET is:") do |t|
+ case t
+ when 'r', 'ruby'
+ config[:target] = 'ruby'
+ when 'x', 'rubinius'
+ config[:target] = './bin/rbx'
+ when 'X', 'rbx'
+ config[:target] = 'rbx'
+ when 'j', 'jruby'
+ config[:target] = 'jruby'
+ when 'i','ironruby'
+ config[:target] = 'ir'
+ when 'm','maglev'
+ config[:target] = 'maglev-ruby'
+ when 't','topaz'
+ config[:target] = 'topaz'
+ when 'o','opal'
+ mspec_lib = File.expand_path('../../../', __FILE__)
+ config[:target] = "./bin/opal -syaml -sfileutils -rnodejs -rnodejs/require -rnodejs/yaml -rprocess -Derror -I#{mspec_lib} -I./lib/ -I. "
+ else
+ config[:target] = t
+ end
+ end
+
+ doc ""
+ doc " r or ruby invokes ruby in PATH"
+ doc " x or rubinius invokes ./bin/rbx"
+ doc " X or rbx invokes rbx in PATH"
+ doc " j or jruby invokes jruby in PATH"
+ doc " i or ironruby invokes ir in PATH"
+ doc " m or maglev invokes maglev-ruby in PATH"
+ doc " t or topaz invokes topaz in PATH"
+ doc " o or opal invokes ./bin/opal with options"
+ doc " full path to EXE invokes EXE directly\n"
+
+ on("-T", "--target-opt", "OPT",
+ "Pass OPT as a flag to the target implementation") do |t|
+ config[:flags] << t
+ end
+ on("-I", "--include", "DIR",
+ "Pass DIR through as the -I option to the target") do |d|
+ config[:loadpath] << "-I#{d}"
+ end
+ on("-r", "--require", "LIBRARY",
+ "Pass LIBRARY through as the -r option to the target") do |f|
+ config[:requires] << "-r#{f}"
+ end
+ end
+
+ def formatters
+ on("-f", "--format", "FORMAT",
+ "Formatter for reporting, where FORMAT is one of:") do |o|
+ require 'mspec/runner/formatters'
+ case o
+ when 's', 'spec', 'specdoc'
+ config[:formatter] = SpecdocFormatter
+ when 'h', 'html'
+ config[:formatter] = HtmlFormatter
+ when 'd', 'dot', 'dotted'
+ config[:formatter] = DottedFormatter
+ when 'b', 'describe'
+ config[:formatter] = DescribeFormatter
+ when 'f', 'file'
+ config[:formatter] = FileFormatter
+ when 'u', 'unit', 'unitdiff'
+ config[:formatter] = UnitdiffFormatter
+ when 'm', 'summary'
+ config[:formatter] = SummaryFormatter
+ when 'a', '*', 'spin'
+ config[:formatter] = SpinnerFormatter
+ when 't', 'method'
+ config[:formatter] = MethodFormatter
+ when 'y', 'yaml'
+ config[:formatter] = YamlFormatter
+ when 'p', 'profile'
+ config[:formatter] = ProfileFormatter
+ when 'j', 'junit'
+ config[:formatter] = JUnitFormatter
+ else
+ abort "Unknown format: #{o}\n#{@parser}" unless File.exist?(o)
+ require File.expand_path(o)
+ if Object.const_defined?(:CUSTOM_MSPEC_FORMATTER)
+ config[:formatter] = CUSTOM_MSPEC_FORMATTER
+ else
+ abort "You must define CUSTOM_MSPEC_FORMATTER in your custom formatter file"
+ end
+ end
+ end
+
+ doc ""
+ doc " s, spec, specdoc SpecdocFormatter"
+ doc " h, html, HtmlFormatter"
+ doc " d, dot, dotted DottedFormatter"
+ doc " f, file FileFormatter"
+ doc " u, unit, unitdiff UnitdiffFormatter"
+ doc " m, summary SummaryFormatter"
+ doc " a, *, spin SpinnerFormatter"
+ doc " t, method MethodFormatter"
+ doc " y, yaml YamlFormatter"
+ doc " p, profile ProfileFormatter"
+ doc " j, junit JUnitFormatter\n"
+
+ on("-o", "--output", "FILE",
+ "Write formatter output to FILE") do |f|
+ config[:output] = f
+ end
+ end
+
+ def filters
+ on("-e", "--example", "STR",
+ "Run examples with descriptions matching STR") do |o|
+ config[:includes] << o
+ end
+ on("-E", "--exclude", "STR",
+ "Exclude examples with descriptions matching STR") do |o|
+ config[:excludes] << o
+ end
+ on("-p", "--pattern", "PATTERN",
+ "Run examples with descriptions matching PATTERN") do |o|
+ config[:patterns] << Regexp.new(o)
+ end
+ on("-P", "--excl-pattern", "PATTERN",
+ "Exclude examples with descriptions matching PATTERN") do |o|
+ config[:xpatterns] << Regexp.new(o)
+ end
+ on("-g", "--tag", "TAG",
+ "Run examples with descriptions matching ones tagged with TAG") do |o|
+ config[:tags] << o
+ end
+ on("-G", "--excl-tag", "TAG",
+ "Exclude examples with descriptions matching ones tagged with TAG") do |o|
+ config[:xtags] << o
+ end
+ on("-w", "--profile", "FILE",
+ "Run examples for methods listed in the profile FILE") do |f|
+ config[:profiles] << f
+ end
+ on("-W", "--excl-profile", "FILE",
+ "Exclude examples for methods listed in the profile FILE") do |f|
+ config[:xprofiles] << f
+ end
+ end
+
+ def chdir
+ on("-C", "--chdir", "DIR",
+ "Change the working directory to DIR before running specs") do |d|
+ Dir.chdir d
+ end
+ end
+
+ def prefix
+ on("--prefix", "STR", "Prepend STR when resolving spec file names") do |p|
+ config[:prefix] = p
+ end
+ end
+
+ def pretend
+ on("-Z", "--dry-run",
+ "Invoke formatters and other actions, but don't execute the specs") do
+ MSpec.register_mode :pretend
+ end
+ end
+
+ def unguarded
+ on("--unguarded", "Turn off all guards") do
+ MSpec.register_mode :unguarded
+ end
+ on("--no-ruby_bug", "Turn off the ruby_bug guard") do
+ MSpec.register_mode :no_ruby_bug
+ end
+ end
+
+ def randomize
+ on("-H", "--random",
+ "Randomize the list of spec files") do
+ MSpec.randomize
+ end
+ end
+
+ def repeat
+ on("-R", "--repeat", "NUMBER",
+ "Repeatedly run an example NUMBER times") do |o|
+ MSpec.repeat = o.to_i
+ end
+ end
+
+ def verbose
+ on("-V", "--verbose", "Output the name of each file processed") do
+ obj = Object.new
+ def obj.start
+ @width = MSpec.retrieve(:files).inject(0) { |max, f| f.size > max ? f.size : max }
+ end
+ def obj.load
+ file = MSpec.retrieve :file
+ STDERR.print "\n#{file.ljust(@width)}"
+ end
+ MSpec.register :start, obj
+ MSpec.register :load, obj
+ end
+
+ on("-m", "--marker", "MARKER",
+ "Output MARKER for each file processed") do |o|
+ obj = Object.new
+ obj.instance_variable_set :@marker, o
+ def obj.load
+ STDERR.print @marker
+ end
+ MSpec.register :load, obj
+ end
+ end
+
+ def interrupt
+ on("--int-spec", "Control-C interupts the current spec only") do
+ config[:abort] = false
+ end
+ end
+
+ def verify
+ on("--report-on", "GUARD", "Report specs guarded by GUARD") do |g|
+ MSpec.register_mode :report_on
+ SpecGuard.guards << g.to_sym
+ end
+ on("-O", "--report", "Report guarded specs") do
+ MSpec.register_mode :report
+ end
+ on("-Y", "--verify",
+ "Verify that guarded specs pass and fail as expected") do
+ MSpec.register_mode :verify
+ end
+ end
+
+ def action_filters
+ on("-K", "--action-tag", "TAG",
+ "Spec descriptions marked with TAG will trigger the specified action") do |o|
+ config[:atags] << o
+ end
+ on("-S", "--action-string", "STR",
+ "Spec descriptions matching STR will trigger the specified action") do |o|
+ config[:astrings] << o
+ end
+ end
+
+ def actions
+ on("--spec-debug",
+ "Invoke the debugger when a spec description matches (see -K, -S)") do
+ config[:debugger] = true
+ end
+ end
+
+ def debug
+ on("-d", "--debug",
+ "Set MSpec debugging flag for more verbose output") do
+ $MSPEC_DEBUG = true
+ end
+ end
+
+ def all
+ # Generated with:
+ # puts File.read(__FILE__).scan(/def (\w+).*\n\s*on\(/)
+ configure {}
+ targets
+ formatters
+ filters
+ chdir
+ prefix
+ pretend
+ unguarded
+ randomize
+ repeat
+ verbose
+ interrupt
+ verify
+ action_filters
+ actions
+ debug
+ end
+end
diff --git a/spec/mspec/lib/mspec/utils/script.rb b/spec/mspec/lib/mspec/utils/script.rb
new file mode 100644
index 0000000000..2816618b0f
--- /dev/null
+++ b/spec/mspec/lib/mspec/utils/script.rb
@@ -0,0 +1,278 @@
+require 'mspec/guards/guard'
+require 'mspec/guards/version'
+require 'mspec/utils/warnings'
+
+# MSpecScript provides a skeleton for all the MSpec runner scripts.
+
+class MSpecScript
+ # Returns the config object. Maintained at the class
+ # level to easily enable simple config files. See the
+ # class method +set+.
+ def self.config
+ @config ||= {
+ :path => ['.', 'spec'],
+ :config_ext => '.mspec'
+ }
+ end
+
+ # Associates +value+ with +key+ in the config object. Enables
+ # simple config files of the form:
+ #
+ # class MSpecScript
+ # set :target, "ruby"
+ # set :files, ["one_spec.rb", "two_spec.rb"]
+ # end
+ def self.set(key, value)
+ config[key] = value
+ end
+
+ # Gets the value of +key+ from the config object. Simplifies
+ # getting values in a config file:
+ #
+ # class MSpecScript
+ # set :a, 1
+ # set :b, 2
+ # set :c, get(:a) + get(:b)
+ # end
+ def self.get(key)
+ config[key]
+ end
+
+ def initialize
+ ruby_version_is ""..."2.3" do
+ abort "MSpec needs Ruby 2.3 or more recent"
+ end
+
+ config[:formatter] = nil
+ config[:includes] = []
+ config[:excludes] = []
+ config[:patterns] = []
+ config[:xpatterns] = []
+ config[:tags] = []
+ config[:xtags] = []
+ config[:profiles] = []
+ config[:xprofiles] = []
+ config[:atags] = []
+ config[:astrings] = []
+ config[:ltags] = []
+ config[:abort] = true
+ @loaded = []
+ end
+
+ # Returns the config object maintained by the instance's class.
+ # See the class methods +set+ and +config+.
+ def config
+ MSpecScript.config
+ end
+
+ # Returns +true+ if the file was located in +config[:path]+,
+ # possibly appending +config[:config_ext]. Returns +false+
+ # otherwise.
+ def try_load(target)
+ names = [target]
+ unless target[-6..-1] == config[:config_ext]
+ names << target + config[:config_ext]
+ end
+
+ names.each do |name|
+ config[:path].each do |dir|
+ file = File.expand_path name, dir
+ if @loaded.include?(file)
+ return true
+ elsif File.exist? file
+ value = Kernel.load(file)
+ @loaded << file
+ return value
+ end
+ end
+ end
+
+ false
+ end
+
+ def load(target)
+ try_load(target) or abort "Could not load config file #{target}"
+ end
+
+ # Attempts to load a default config file. First tries to load
+ # 'default.mspec'. If that fails, attempts to load a config
+ # file name constructed from the value of RUBY_ENGINE and the
+ # first two numbers in RUBY_VERSION. For example, on MRI 1.8.6,
+ # the file name would be 'ruby.1.8.mspec'.
+ def load_default
+ try_load 'default.mspec'
+
+ if Object.const_defined?(:RUBY_ENGINE)
+ engine = RUBY_ENGINE
+ else
+ engine = 'ruby'
+ end
+ try_load "#{engine}.#{SpecGuard.ruby_version}.mspec"
+ try_load "#{engine}.mspec"
+ end
+
+ # Callback for enabling custom options. This version is a no-op.
+ # Provide an implementation specific version in a config file.
+ # Called by #options after the MSpec-provided options are added.
+ def custom_options(options)
+ options.doc " No custom options registered"
+ end
+
+ # Registers all filters and actions.
+ def register
+ require 'mspec/runner/formatters/dotted'
+ require 'mspec/runner/formatters/spinner'
+ require 'mspec/runner/formatters/file'
+ require 'mspec/runner/filters'
+
+ if config[:formatter].nil?
+ config[:formatter] = STDOUT.tty? ? SpinnerFormatter : @files.size < 50 ? DottedFormatter : FileFormatter
+ end
+
+ if config[:formatter]
+ formatter = config[:formatter].new(config[:output])
+ formatter.register
+ MSpec.store :formatter, formatter
+ end
+
+ MatchFilter.new(:include, *config[:includes]).register unless config[:includes].empty?
+ MatchFilter.new(:exclude, *config[:excludes]).register unless config[:excludes].empty?
+ RegexpFilter.new(:include, *config[:patterns]).register unless config[:patterns].empty?
+ RegexpFilter.new(:exclude, *config[:xpatterns]).register unless config[:xpatterns].empty?
+ TagFilter.new(:include, *config[:tags]).register unless config[:tags].empty?
+ TagFilter.new(:exclude, *config[:xtags]).register unless config[:xtags].empty?
+ ProfileFilter.new(:include, *config[:profiles]).register unless config[:profiles].empty?
+ ProfileFilter.new(:exclude, *config[:xprofiles]).register unless config[:xprofiles].empty?
+
+ DebugAction.new(config[:atags], config[:astrings]).register if config[:debugger]
+
+ custom_register
+ end
+
+ # Callback for enabling custom actions, etc. This version is a
+ # no-op. Provide an implementation specific version in a config
+ # file. Called by #register.
+ def custom_register
+ end
+
+ # Sets up signal handlers. Only a handler for SIGINT is
+ # registered currently.
+ def signals
+ if config[:abort]
+ Signal.trap "INT" do
+ MSpec.actions :abort
+ puts "\nProcess aborted!"
+ exit! 1
+ end
+ end
+ end
+
+ # Attempts to resolve +partial+ as a file or directory name in the
+ # following order:
+ #
+ # 1. +partial+
+ # 2. +partial+ + "_spec.rb"
+ # 3. <tt>File.join(config[:prefix], partial)</tt>
+ # 4. <tt>File.join(config[:prefix], partial + "_spec.rb")</tt>
+ #
+ # If it is a file name, returns the name as an entry in an array.
+ # If it is a directory, returns all *_spec.rb files in the
+ # directory and subdirectories.
+ #
+ # If unable to resolve +partial+, +Kernel.abort+ is called.
+ def entries(partial)
+ file = partial + "_spec.rb"
+ patterns = [partial, file]
+ if config[:prefix]
+ patterns << File.join(config[:prefix], partial)
+ patterns << File.join(config[:prefix], file)
+ end
+
+ patterns.each do |pattern|
+ begin
+ expanded = File.realpath(pattern)
+ rescue Errno::ENOENT
+ next
+ end
+ if File.file?(expanded) && expanded.end_with?('.rb')
+ return [expanded]
+ elsif File.directory?(expanded)
+ specs = Dir["#{expanded}/**/*_spec.rb"].sort
+ return specs unless specs.empty?
+ end
+ end
+
+ abort "Could not find spec file #{partial}"
+ end
+
+ # Resolves each entry in +patterns+ to a set of files.
+ #
+ # If the pattern has a leading '^' character, the list of files
+ # is subtracted from the list of files accumulated to that point.
+ #
+ # If the entry has a leading ':' character, the corresponding
+ # key is looked up in the config object and the entries in the
+ # value retrieved are processed through #entries.
+ def files(patterns)
+ list = []
+ patterns.each do |pattern|
+ case pattern[0]
+ when ?^
+ list -= entries(pattern[1..-1])
+ when ?:
+ key = pattern[1..-1].to_sym
+ value = config[key]
+ abort "Key #{pattern} not found in mspec config." unless value
+ list += files(Array(value))
+ else
+ list += entries(pattern)
+ end
+ end
+ list
+ end
+
+ def files_from_patterns(patterns)
+ unless $0.end_with?("_spec.rb")
+ if patterns.empty?
+ patterns = config[:files]
+ end
+ if patterns.empty? and File.directory? "./spec"
+ patterns = ["spec/"]
+ end
+ end
+ list = files(patterns)
+ abort "No files specified." if list.empty?
+ list
+ end
+
+ def cores(max)
+ require 'etc'
+ [Etc.nprocessors, max].min
+ end
+
+ def setup_env
+ ENV['MSPEC_RUNNER'] = '1'
+
+ unless ENV['RUBY_EXE']
+ ENV['RUBY_EXE'] = config[:target] if config[:target]
+ end
+
+ unless ENV['RUBY_FLAGS']
+ ENV['RUBY_FLAGS'] = config[:flags].join(" ") if config[:flags]
+ end
+ end
+
+ # Instantiates an instance and calls the series of methods to
+ # invoke the script.
+ def self.main
+ script = new
+ script.load_default
+ script.try_load '~/.mspecrc'
+ script.options
+ script.signals
+ script.register
+ script.setup_env
+ require 'mspec'
+ script.run
+ end
+end
diff --git a/spec/mspec/lib/mspec/utils/version.rb b/spec/mspec/lib/mspec/utils/version.rb
new file mode 100644
index 0000000000..787a76b053
--- /dev/null
+++ b/spec/mspec/lib/mspec/utils/version.rb
@@ -0,0 +1,52 @@
+class SpecVersion
+ # If beginning implementations have a problem with this include, we can
+ # manually implement the relational operators that are needed.
+ include Comparable
+
+ # SpecVersion handles comparison correctly for the context by filling in
+ # missing version parts according to the value of +ceil+. If +ceil+ is
+ # +false+, 0 digits fill in missing version parts. If +ceil+ is +true+, 9
+ # digits fill in missing parts. (See e.g. VersionGuard and BugGuard.)
+ def initialize(version, ceil = false)
+ @version = version
+ @ceil = ceil
+ @integer = nil
+ end
+
+ def to_s
+ @version
+ end
+
+ def to_str
+ to_s
+ end
+
+ # Converts a string representation of a version major.minor.tiny
+ # to an integer representation so that comparisons can be made. For example,
+ # "2.2.10" < "2.2.2" would be false if compared as strings.
+ def to_i
+ unless @integer
+ major, minor, tiny = @version.split "."
+ if @ceil
+ tiny = 99 unless tiny
+ end
+ parts = [major, minor, tiny].map { |x| x.to_i }
+ @integer = ("1%02d%02d%02d" % parts).to_i
+ end
+ @integer
+ end
+
+ def to_int
+ to_i
+ end
+
+ def <=>(other)
+ if other.respond_to? :to_int
+ other = Integer other
+ else
+ other = SpecVersion.new(String(other)).to_i
+ end
+
+ self.to_i <=> other
+ end
+end
diff --git a/spec/mspec/lib/mspec/utils/warnings.rb b/spec/mspec/lib/mspec/utils/warnings.rb
new file mode 100644
index 0000000000..4d23474236
--- /dev/null
+++ b/spec/mspec/lib/mspec/utils/warnings.rb
@@ -0,0 +1,61 @@
+require 'mspec/guards/version'
+
+if RUBY_ENGINE == "ruby" and ruby_version_is("2.4")
+ ruby_version_is "2.4"..."2.5" do
+ # Kernel#warn does not delegate to Warning.warn in 2.4
+ module Kernel
+ remove_method :warn
+ def warn(*messages)
+ return if $VERBOSE == nil or messages.empty?
+ msg = messages.join("\n")
+ msg += "\n" unless msg.end_with?("\n")
+ Warning.warn(msg)
+ end
+ private :warn
+ end
+ end
+
+ def Warning.warn(message)
+ if Thread.current[:in_mspec_complain_matcher]
+ return $stderr.write(message)
+ end
+
+ case message
+ # $VERBOSE = true warnings
+ when /(.+\.rb):(\d+):.+possibly useless use of (<|<=|==|>=|>) in void context/
+ # Make sure there is a .should otherwise it is missing
+ line_nb = Integer($2)
+ unless File.exist?($1) and /\.should(_not)? (<|<=|==|>=|>)/ === File.readlines($1)[line_nb-1]
+ $stderr.write message
+ end
+ when /possibly useless use of (\+|-) in void context/
+ when /assigned but unused variable/
+ when /method redefined/
+ when /previous definition of/
+ when /instance variable @.+ not initialized/
+ when /statement not reached/
+ when /shadowing outer local variable/
+ when /setting Encoding.default_(in|ex)ternal/
+ when /unknown (un)?pack directive/
+ when /(un)?trust(ed\?)? is deprecated/
+ when /\.exists\? is a deprecated name/
+ when /Float .+ out of range/
+ when /passing a block to String#(bytes|chars|codepoints|lines) is deprecated/
+ when /core\/string\/modulo_spec\.rb:\d+: warning: too many arguments for format string/
+ when /regexp\/shared\/new_ascii(_8bit)?\.rb:\d+: warning: Unknown escape .+ is ignored/
+
+ # $VERBOSE = false warnings
+ when /constant ::(Fixnum|Bignum) is deprecated/
+ when /\/(argf|io|stringio)\/.+(ARGF|IO)#(lines|chars|bytes|codepoints) is deprecated/
+ when /Thread\.exclusive is deprecated.+\n.+thread\/exclusive_spec\.rb/
+ when /hash\/shared\/index\.rb:\d+: warning: Hash#index is deprecated; use Hash#key/
+ when /env\/shared\/key\.rb:\d+: warning: ENV\.index is deprecated; use ENV\.key/
+ when /exponent(_spec)?\.rb:\d+: warning: in a\*\*b, b may be too big/
+ when /enumerator\/(new|initialize_spec)\.rb:\d+: warning: Enumerator\.new without a block is deprecated/
+ else
+ $stderr.write message
+ end
+ end
+else
+ $VERBOSE = nil unless ENV['OUTPUT_WARNINGS']
+end
diff --git a/spec/mspec/lib/mspec/version.rb b/spec/mspec/lib/mspec/version.rb
new file mode 100644
index 0000000000..9126f5366e
--- /dev/null
+++ b/spec/mspec/lib/mspec/version.rb
@@ -0,0 +1,5 @@
+require 'mspec/utils/version'
+
+module MSpec
+ VERSION = SpecVersion.new "1.8.0"
+end
diff --git a/spec/mspec/spec/commands/fixtures/four.txt b/spec/mspec/spec/commands/fixtures/four.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/spec/mspec/spec/commands/fixtures/four.txt
diff --git a/spec/mspec/spec/commands/fixtures/level2/three_spec.rb b/spec/mspec/spec/commands/fixtures/level2/three_spec.rb
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/spec/mspec/spec/commands/fixtures/level2/three_spec.rb
@@ -0,0 +1 @@
+
diff --git a/spec/mspec/spec/commands/fixtures/one_spec.rb b/spec/mspec/spec/commands/fixtures/one_spec.rb
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/spec/mspec/spec/commands/fixtures/one_spec.rb
@@ -0,0 +1 @@
+
diff --git a/spec/mspec/spec/commands/fixtures/three.rb b/spec/mspec/spec/commands/fixtures/three.rb
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/spec/mspec/spec/commands/fixtures/three.rb
@@ -0,0 +1 @@
+
diff --git a/spec/mspec/spec/commands/fixtures/two_spec.rb b/spec/mspec/spec/commands/fixtures/two_spec.rb
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/spec/mspec/spec/commands/fixtures/two_spec.rb
@@ -0,0 +1 @@
+
diff --git a/spec/mspec/spec/commands/mkspec_spec.rb b/spec/mspec/spec/commands/mkspec_spec.rb
new file mode 100644
index 0000000000..1b959dcb78
--- /dev/null
+++ b/spec/mspec/spec/commands/mkspec_spec.rb
@@ -0,0 +1,363 @@
+require 'spec_helper'
+require 'mspec/commands/mkspec'
+
+
+describe "The -c, --constant CONSTANT option" do
+ before :each do
+ @options = MSpecOptions.new
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MkSpec.new
+ @config = @script.config
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-c", "--constant", "CONSTANT",
+ an_instance_of(String))
+ @script.options []
+ end
+
+ it "adds CONSTANT to the list of constants" do
+ ["-c", "--constant"].each do |opt|
+ @config[:constants] = []
+ @script.options [opt, "Object"]
+ @config[:constants].should include("Object")
+ end
+ end
+end
+
+describe "The -b, --base DIR option" do
+ before :each do
+ @options = MSpecOptions.new
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MkSpec.new
+ @config = @script.config
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-b", "--base", "DIR",
+ an_instance_of(String))
+ @script.options []
+ end
+
+ it "sets the base directory relative to which the spec directories are created" do
+ ["-b", "--base"].each do |opt|
+ @config[:base] = nil
+ @script.options [opt, "superspec"]
+ @config[:base].should == File.expand_path("superspec")
+ end
+ end
+end
+
+describe "The -r, --require LIBRARY option" do
+ before :each do
+ @options = MSpecOptions.new
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MkSpec.new
+ @config = @script.config
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-r", "--require", "LIBRARY",
+ an_instance_of(String))
+ @script.options []
+ end
+
+ it "adds CONSTANT to the list of constants" do
+ ["-r", "--require"].each do |opt|
+ @config[:requires] = []
+ @script.options [opt, "libspec"]
+ @config[:requires].should include("libspec")
+ end
+ end
+end
+
+describe "The -V, --version-guard VERSION option" do
+ before :each do
+ @options = MSpecOptions.new
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MkSpec.new
+ @config = @script.config
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-V", "--version-guard", "VERSION",
+ an_instance_of(String))
+ @script.options []
+ end
+
+ it "sets the version for the ruby_version_is guards to VERSION" do
+ ["-r", "--require"].each do |opt|
+ @config[:requires] = []
+ @script.options [opt, "libspec"]
+ @config[:requires].should include("libspec")
+ end
+ end
+end
+
+describe MkSpec, "#options" do
+ before :each do
+ @options = MSpecOptions.new
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MkSpec.new
+ end
+
+ it "parses the command line options" do
+ @options.should_receive(:parse).with(["--this", "and", "--that"])
+ @script.options ["--this", "and", "--that"]
+ end
+
+ it "parses ARGV unless passed other options" do
+ @options.should_receive(:parse).with(ARGV)
+ @script.options
+ end
+
+ it "prints help and exits if passed an unrecognized option" do
+ @options.should_receive(:raise).with(MSpecOptions::ParseError, an_instance_of(String))
+ @options.stub(:puts)
+ @options.stub(:exit)
+ @script.options ["--iunknown"]
+ end
+end
+
+describe MkSpec, "#create_directory" do
+ before :each do
+ @script = MkSpec.new
+ @script.config[:base] = "spec"
+ end
+
+ it "prints a warning if a file with the directory name exists" do
+ File.should_receive(:exist?).and_return(true)
+ File.should_receive(:directory?).and_return(false)
+ FileUtils.should_not_receive(:mkdir_p)
+ @script.should_receive(:puts).with("spec/class already exists and is not a directory.")
+ @script.create_directory("Class").should == nil
+ end
+
+ it "does nothing if the directory already exists" do
+ File.should_receive(:exist?).and_return(true)
+ File.should_receive(:directory?).and_return(true)
+ FileUtils.should_not_receive(:mkdir_p)
+ @script.create_directory("Class").should == "spec/class"
+ end
+
+ it "creates the directory if it does not exist" do
+ File.should_receive(:exist?).and_return(false)
+ @script.should_receive(:mkdir_p).with("spec/class")
+ @script.create_directory("Class").should == "spec/class"
+ end
+
+ it "creates the directory for a namespaced module if it does not exist" do
+ File.should_receive(:exist?).and_return(false)
+ @script.should_receive(:mkdir_p).with("spec/struct/tms")
+ @script.create_directory("Struct::Tms").should == "spec/struct/tms"
+ end
+end
+
+describe MkSpec, "#write_requires" do
+ before :each do
+ @script = MkSpec.new
+ @script.config[:base] = "spec"
+
+ @file = double("file")
+ File.stub(:open).and_yield(@file)
+ end
+
+ it "writes the spec_helper require line" do
+ @file.should_receive(:puts).with("require_relative '../../../spec_helper'")
+ @script.write_requires("spec/core/tcejbo", "spec/core/tcejbo/inspect_spec.rb")
+ end
+
+ it "writes require lines for each library specified on the command line" do
+ @file.stub(:puts)
+ @file.should_receive(:puts).with("require_relative '../../../spec_helper'")
+ @file.should_receive(:puts).with("require 'complex'")
+ @script.config[:requires] << 'complex'
+ @script.write_requires("spec/core/tcejbo", "spec/core/tcejbo/inspect_spec.rb")
+ end
+end
+
+describe MkSpec, "#write_spec" do
+ before :each do
+ @file = IOStub.new
+ File.stub(:open).and_yield(@file)
+
+ @script = MkSpec.new
+ @script.stub(:puts)
+
+ @response = double("system command response")
+ @response.stub(:include?).and_return(false)
+ @script.stub(:`).and_return(@response)
+ end
+
+ it "checks if specs exist for the method if the spec file exists" do
+ name = Regexp.escape(@script.ruby)
+ @script.should_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)
+ end
+
+ it "checks for the method name in the spec file output" do
+ @response.should_receive(:include?).with("Array#[]=")
+ @script.write_spec("spec/core/yarra/element_set_spec.rb", "Array#[]=", true)
+ end
+
+ it "returns nil if the spec file exists and contains a spec for the method" do
+ @response.stub(:include?).and_return(true)
+ @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true).should == nil
+ end
+
+ it "does not print the spec file name if it exists and contains a spec for the method" do
+ @response.stub(:include?).and_return(true)
+ @script.should_not_receive(:puts)
+ @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
+ end
+
+ it "prints the spec file name if a template spec is written" do
+ @script.should_receive(:puts).with("spec/core/tcejbo/inspect_spec.rb")
+ @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
+ end
+
+ it "writes a template spec to the file if the spec file does not exist" do
+ @file.should_receive(:puts).twice
+ @script.should_receive(:puts).with("spec/core/tcejbo/inspect_spec.rb")
+ @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", false)
+ end
+
+ it "writes a template spec to the file if it exists but contains no spec for the method" do
+ @response.should_receive(:include?).and_return(false)
+ @file.should_receive(:puts).twice
+ @script.should_receive(:puts).with("spec/core/tcejbo/inspect_spec.rb")
+ @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
+ end
+
+ it "writes a template spec" do
+ @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
+ @file.should == <<EOS
+
+describe "Object#inspect" do
+ it "needs to be reviewed for spec completeness"
+end
+EOS
+ end
+
+ it "writes a template spec with version guard" do
+ @script.config[:version] = '""..."1.9"'
+ @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
+ @file.should == <<EOS
+
+ruby_version_is ""..."1.9" do
+ describe "Object#inspect" do
+ it "needs to be reviewed for spec completeness"
+ end
+end
+EOS
+
+ end
+end
+
+describe MkSpec, "#create_file" do
+ before :each do
+ @script = MkSpec.new
+ @script.stub(:write_requires)
+ @script.stub(:write_spec)
+
+ File.stub(:exist?).and_return(false)
+ end
+
+ it "generates a file name based on the directory, class/module, and method" do
+ File.should_receive(:join).with("spec/tcejbo", "inspect_spec.rb"
+ ).and_return("spec/tcejbo/inspect_spec.rb")
+ @script.create_file("spec/tcejbo", "Object", "inspect", "Object#inspect")
+ end
+
+ it "does not call #write_requires if the spec file already exists" do
+ File.should_receive(:exist?).and_return(true)
+ @script.should_not_receive(:write_requires)
+ @script.create_file("spec/tcejbo", "Object", "inspect", "Object#inspect")
+ end
+
+ it "calls #write_requires if the spec file does not exist" do
+ File.should_receive(:exist?).and_return(false)
+ @script.should_receive(:write_requires).with(
+ "spec/tcejbo", "spec/tcejbo/inspect_spec.rb")
+ @script.create_file("spec/tcejbo", "Object", "inspect", "Object#inspect")
+ end
+
+ it "calls #write_spec with the file, method name" do
+ @script.should_receive(:write_spec).with(
+ "spec/tcejbo/inspect_spec.rb", "Object#inspect", false)
+ @script.create_file("spec/tcejbo", "Object", "inspect", "Object#inspect")
+ end
+end
+
+describe MkSpec, "#run" do
+ before :each do
+ @options = MSpecOptions.new
+ MSpecOptions.stub(:new).and_return(@options)
+
+ @map = NameMap.new
+ NameMap.stub(:new).and_return(@map)
+
+ @script = MkSpec.new
+ @script.stub(:create_directory).and_return("spec/mkspec")
+ @script.stub(:create_file)
+ @script.config[:constants] = [MkSpec]
+ end
+
+ it "loads files in the requires list" do
+ @script.stub(:require)
+ @script.should_receive(:require).with("alib")
+ @script.should_receive(:require).with("blib")
+ @script.config[:requires] = ["alib", "blib"]
+ @script.run
+ end
+
+ it "creates a map of constants to methods" do
+ @map.should_receive(:map).with({}, @script.config[:constants]).and_return({})
+ @script.run
+ end
+
+ it "calls #create_directory for each class/module in the map" do
+ @script.should_receive(:create_directory).with("MkSpec").twice
+ @script.run
+ end
+
+ it "calls #create_file for each method on each class/module in the map" do
+ @map.should_receive(:map).with({}, @script.config[:constants]
+ ).and_return({"MkSpec#" => ["run"]})
+ @script.should_receive(:create_file).with("spec/mkspec", "MkSpec", "run", "MkSpec#run")
+ @script.run
+ end
+end
+
+describe MkSpec, ".main" do
+ before :each do
+ @script = double("MkSpec").as_null_object
+ MkSpec.stub(:new).and_return(@script)
+ end
+
+ it "sets MSPEC_RUNNER = '1' in the environment" do
+ ENV["MSPEC_RUNNER"] = "0"
+ MkSpec.main
+ ENV["MSPEC_RUNNER"].should == "1"
+ end
+
+ it "creates an instance of MSpecScript" do
+ MkSpec.should_receive(:new).and_return(@script)
+ MkSpec.main
+ end
+
+ it "calls the #options method on the script" do
+ @script.should_receive(:options)
+ MkSpec.main
+ end
+
+ it "calls the #run method on the script" do
+ @script.should_receive(:run)
+ MkSpec.main
+ end
+end
diff --git a/spec/mspec/spec/commands/mspec_ci_spec.rb b/spec/mspec/spec/commands/mspec_ci_spec.rb
new file mode 100644
index 0000000000..a90cbd8d0d
--- /dev/null
+++ b/spec/mspec/spec/commands/mspec_ci_spec.rb
@@ -0,0 +1,150 @@
+require 'spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/runner/filters/tag'
+require 'mspec/commands/mspec-ci'
+
+describe MSpecCI, "#options" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+
+ @script = MSpecCI.new
+ @script.stub(:config).and_return(@config)
+ @script.stub(:files).and_return([])
+ end
+
+ it "enables the chdir option" do
+ @options.should_receive(:chdir)
+ @script.options []
+ end
+
+ it "enables the prefix option" do
+ @options.should_receive(:prefix)
+ @script.options []
+ end
+
+ it "enables the config option" do
+ @options.should_receive(:configure)
+ @script.options []
+ end
+
+ it "provides a custom action (block) to the config option" do
+ @script.should_receive(:load).with("cfg.mspec")
+ @script.options ["-B", "cfg.mspec"]
+ end
+
+ it "enables the dry run option" do
+ @options.should_receive(:pretend)
+ @script.options []
+ end
+
+ it "enables the unguarded option" do
+ @options.should_receive(:unguarded)
+ @script.options []
+ end
+
+ it "enables the interrupt single specs option" do
+ @options.should_receive(:interrupt)
+ @script.options []
+ end
+
+ it "enables the formatter options" do
+ @options.should_receive(:formatters)
+ @script.options []
+ end
+
+ it "enables the verbose option" do
+ @options.should_receive(:verbose)
+ @script.options []
+ end
+
+ it "enables the action options" do
+ @options.should_receive(:actions)
+ @script.options []
+ end
+
+ it "enables the action filter options" do
+ @options.should_receive(:action_filters)
+ @script.options []
+ end
+
+ it "enables the version option" do
+ @options.should_receive(:version)
+ @script.options []
+ end
+
+ it "enables the help option" do
+ @options.should_receive(:help)
+ @script.options []
+ end
+
+ it "calls #custom_options" do
+ @script.should_receive(:custom_options).with(@options)
+ @script.options []
+ end
+end
+
+describe MSpecCI, "#run" do
+ before :each do
+ MSpec.stub(:process)
+
+ @filter = double("TagFilter")
+ TagFilter.stub(:new).and_return(@filter)
+ @filter.stub(:register)
+
+ @tags = ["fails", "critical", "unstable", "incomplete", "unsupported"]
+
+ @config = { :ci_files => ["one", "two"] }
+ @script = MSpecCI.new
+ @script.stub(:exit)
+ @script.stub(:config).and_return(@config)
+ @script.stub(:files).and_return(["one", "two"])
+ @script.options []
+ end
+
+ it "registers the tags patterns" do
+ @config[:tags_patterns] = [/spec/, "tags"]
+ MSpec.should_receive(:register_tags_patterns).with([/spec/, "tags"])
+ @script.run
+ end
+
+ it "registers the files to process" do
+ MSpec.should_receive(:register_files).with(["one", "two"])
+ @script.run
+ end
+
+ it "registers a tag filter for 'fails', 'unstable', 'incomplete', 'critical', 'unsupported'" do
+ filter = double("fails filter")
+ TagFilter.should_receive(:new).with(:exclude, *@tags).and_return(filter)
+ filter.should_receive(:register)
+ @script.run
+ end
+
+ it "registers an additional exclude tag specified by :ci_xtags" do
+ @config[:ci_xtags] = "windows"
+ filter = double("fails filter")
+ TagFilter.should_receive(:new).with(:exclude, *(@tags + ["windows"])).and_return(filter)
+ filter.should_receive(:register)
+ @script.run
+ end
+
+ it "registers additional exclude tags specified by a :ci_xtags array" do
+ @config[:ci_xtags] = ["windows", "windoze"]
+ filter = double("fails filter")
+ TagFilter.should_receive(:new).with(:exclude,
+ *(@tags + ["windows", "windoze"])).and_return(filter)
+ filter.should_receive(:register)
+ @script.run
+ end
+
+ it "processes the files" do
+ MSpec.should_receive(:process)
+ @script.run
+ end
+
+ it "exits with the exit code registered with MSpec" do
+ MSpec.stub(:exit_code).and_return(7)
+ @script.should_receive(:exit).with(7)
+ @script.run
+ end
+end
diff --git a/spec/mspec/spec/commands/mspec_run_spec.rb b/spec/mspec/spec/commands/mspec_run_spec.rb
new file mode 100644
index 0000000000..fcb44ad5a9
--- /dev/null
+++ b/spec/mspec/spec/commands/mspec_run_spec.rb
@@ -0,0 +1,173 @@
+require 'spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/commands/mspec-run'
+
+one_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/one_spec.rb'
+two_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/two_spec.rb'
+
+describe MSpecRun, ".new" do
+ before :each do
+ @script = MSpecRun.new
+ end
+
+ it "sets config[:files] to an empty list" do
+ @script.config[:files].should == []
+ end
+end
+
+describe MSpecRun, "#options" do
+ before :each do
+ @argv = [one_spec, two_spec]
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+
+ @script = MSpecRun.new
+ @script.stub(:config).and_return(@config)
+ end
+
+ it "enables the filter options" do
+ @options.should_receive(:filters)
+ @script.options @argv
+ end
+
+ it "enables the chdir option" do
+ @options.should_receive(:chdir)
+ @script.options @argv
+ end
+
+ it "enables the prefix option" do
+ @options.should_receive(:prefix)
+ @script.options @argv
+ end
+
+ it "enables the configure option" do
+ @options.should_receive(:configure)
+ @script.options @argv
+ end
+
+ it "provides a custom action (block) to the config option" do
+ @script.should_receive(:load).with("cfg.mspec")
+ @script.options ["-B", "cfg.mspec", one_spec]
+ end
+
+ it "enables the randomize option to runs specs in random order" do
+ @options.should_receive(:randomize)
+ @script.options @argv
+ end
+
+ it "enables the dry run option" do
+ @options.should_receive(:pretend)
+ @script.options @argv
+ end
+
+ it "enables the unguarded option" do
+ @options.should_receive(:unguarded)
+ @script.options @argv
+ end
+
+ it "enables the interrupt single specs option" do
+ @options.should_receive(:interrupt)
+ @script.options @argv
+ end
+
+ it "enables the formatter options" do
+ @options.should_receive(:formatters)
+ @script.options @argv
+ end
+
+ it "enables the verbose option" do
+ @options.should_receive(:verbose)
+ @script.options @argv
+ end
+
+ it "enables the verify options" do
+ @options.should_receive(:verify)
+ @script.options @argv
+ end
+
+ it "enables the action options" do
+ @options.should_receive(:actions)
+ @script.options @argv
+ end
+
+ it "enables the action filter options" do
+ @options.should_receive(:action_filters)
+ @script.options @argv
+ end
+
+ it "enables the version option" do
+ @options.should_receive(:version)
+ @script.options @argv
+ end
+
+ it "enables the help option" do
+ @options.should_receive(:help)
+ @script.options @argv
+ end
+
+ it "exits if there are no files to process and './spec' is not a directory" do
+ File.should_receive(:directory?).with("./spec").and_return(false)
+ @options.should_receive(:parse).and_return([])
+ @script.should_receive(:abort).with("No files specified.")
+ @script.options
+ end
+
+ it "process 'spec/' if it is a directory and no files were specified" do
+ File.should_receive(:directory?).with("./spec").and_return(true)
+ @options.should_receive(:parse).and_return([])
+ @script.should_receive(:files).with(["spec/"]).and_return(["spec/a_spec.rb"])
+ @script.options
+ end
+
+ it "calls #custom_options" do
+ @script.should_receive(:custom_options).with(@options)
+ @script.options @argv
+ end
+end
+
+describe MSpecRun, "#run" do
+ before :each do
+ @script = MSpecRun.new
+ @script.stub(:exit)
+ @spec_dir = File.expand_path(File.dirname(__FILE__)+"/fixtures")
+ @file_patterns = [
+ @spec_dir+"/level2",
+ @spec_dir+"/one_spec.rb",
+ @spec_dir+"/two_spec.rb"]
+ @files = [
+ @spec_dir+"/level2/three_spec.rb",
+ @spec_dir+"/one_spec.rb",
+ @spec_dir+"/two_spec.rb"]
+ @script.options @file_patterns
+ MSpec.stub :process
+ end
+
+ it "registers the tags patterns" do
+ @script.config[:tags_patterns] = [/spec/, "tags"]
+ MSpec.should_receive(:register_tags_patterns).with([/spec/, "tags"])
+ @script.run
+ end
+
+ it "registers the files to process" do
+ MSpec.should_receive(:register_files).with(@files)
+ @script.run
+ end
+
+ it "uses config[:files] if no files are given on the command line" do
+ @script.config[:files] = @file_patterns
+ MSpec.should_receive(:register_files).with(@files)
+ @script.options []
+ @script.run
+ end
+
+ it "processes the files" do
+ MSpec.should_receive(:process)
+ @script.run
+ end
+
+ it "exits with the exit code registered with MSpec" do
+ MSpec.stub(:exit_code).and_return(7)
+ @script.should_receive(:exit).with(7)
+ @script.run
+ end
+end
diff --git a/spec/mspec/spec/commands/mspec_spec.rb b/spec/mspec/spec/commands/mspec_spec.rb
new file mode 100644
index 0000000000..b01af6b41b
--- /dev/null
+++ b/spec/mspec/spec/commands/mspec_spec.rb
@@ -0,0 +1,207 @@
+require 'spec_helper'
+require 'yaml'
+require 'mspec/commands/mspec'
+
+describe MSpecMain, "#options" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+
+ @script = MSpecMain.new
+ @script.stub(:config).and_return(@config)
+ @script.stub(:load)
+ end
+
+ it "enables the configure option" do
+ @options.should_receive(:configure)
+ @script.options
+ end
+
+ it "provides a custom action (block) to the config option" do
+ @script.options ["-B", "config"]
+ @config[:options].should include("-B", "config")
+ end
+
+ it "loads the file specified by the config option" do
+ @script.should_receive(:load).with("config")
+ @script.options ["-B", "config"]
+ end
+
+ it "enables the target options" do
+ @options.should_receive(:targets)
+ @script.options
+ end
+
+ it "sets config[:options] to all argv entries that are not registered options" do
+ @options.on "-X", "--exclude", "ARG", "description"
+ @script.options [".", "-G", "fail", "-X", "ARG", "--list", "unstable", "some/file.rb"]
+ @config[:options].should == [".", "-G", "fail", "--list", "unstable", "some/file.rb"]
+ end
+
+ it "calls #custom_options" do
+ @script.should_receive(:custom_options).with(@options)
+ @script.options
+ end
+end
+
+describe MSpecMain, "#run" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MSpecMain.new
+ @script.stub(:config).and_return(@config)
+ @script.stub(:exec)
+ @err = $stderr
+ $stderr = IOStub.new
+ end
+
+ after :each do
+ $stderr = @err
+ end
+
+ it "uses exec to invoke the runner script" do
+ @script.should_receive(:exec).with("ruby", "#{MSPEC_HOME}/bin/mspec-run", close_others: false)
+ @script.options []
+ @script.run
+ end
+
+ it "shows the command line on stderr" do
+ @script.should_receive(:exec).with("ruby", "#{MSPEC_HOME}/bin/mspec-run", close_others: false)
+ @script.options []
+ @script.run
+ $stderr.to_s.should == "$ ruby #{Dir.pwd}/bin/mspec-run\n"
+ end
+
+ it "adds config[:launch] to the exec options" do
+ @script.should_receive(:exec).with("ruby",
+ "-Xlaunch.option", "#{MSPEC_HOME}/bin/mspec-run", close_others: false)
+ @config[:launch] << "-Xlaunch.option"
+ @script.options []
+ @script.run
+ $stderr.to_s.should == "$ ruby -Xlaunch.option #{Dir.pwd}/bin/mspec-run\n"
+ end
+
+ it "calls #multi_exec if the command is 'ci' and the multi option is passed" do
+ @script.should_receive(:multi_exec).and_return do |argv|
+ argv.should == ["ruby", "#{MSPEC_HOME}/bin/mspec-ci"]
+ end
+ @script.options ["ci", "-j"]
+ lambda do
+ @script.run
+ end.should raise_error(SystemExit)
+ end
+end
+
+describe "The --warnings option" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MSpecMain.new
+ @script.stub(:config).and_return(@config)
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("--warnings", an_instance_of(String))
+ @script.options
+ end
+
+ it "sets flags to -w" do
+ @config[:flags] = []
+ @script.options ["--warnings"]
+ @config[:flags].should include("-w")
+ end
+
+ it "set OUTPUT_WARNINGS = '1' in the environment" do
+ ENV['OUTPUT_WARNINGS'] = '0'
+ @script.options ["--warnings"]
+ ENV['OUTPUT_WARNINGS'].should == '1'
+ end
+end
+
+describe "The -j, --multi option" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MSpecMain.new
+ @script.stub(:config).and_return(@config)
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-j", "--multi", an_instance_of(String))
+ @script.options
+ end
+
+ it "sets the multiple process option" do
+ ["-j", "--multi"].each do |opt|
+ @config[:multi] = nil
+ @script.options [opt]
+ @config[:multi].should == true
+ end
+ end
+end
+
+describe "The -h, --help option" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MSpecMain.new
+ @script.stub(:config).and_return(@config)
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-h", "--help", an_instance_of(String))
+ @script.options
+ end
+
+ it "passes the option to the subscript" do
+ ["-h", "--help"].each do |opt|
+ @config[:options] = []
+ @script.options ["ci", opt]
+ @config[:options].sort.should == ["-h"]
+ end
+ end
+
+ it "prints help and exits" do
+ @script.should_receive(:puts).twice
+ @script.should_receive(:exit).twice
+ ["-h", "--help"].each do |opt|
+ @script.options [opt]
+ end
+ end
+end
+
+describe "The -v, --version option" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MSpecMain.new
+ @script.stub(:config).and_return(@config)
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-v", "--version", an_instance_of(String))
+ @script.options
+ end
+
+ it "passes the option to the subscripts" do
+ ["-v", "--version"].each do |opt|
+ @config[:options] = []
+ @script.options ["ci", opt]
+ @config[:options].sort.should == ["-v"]
+ end
+ end
+
+ it "prints the version and exits if no subscript is invoked" do
+ @config[:command] = nil
+ File.stub(:basename).and_return("mspec")
+ @script.should_receive(:puts).twice.with("mspec #{MSpec::VERSION}")
+ @script.should_receive(:exit).twice
+ ["-v", "--version"].each do |opt|
+ @script.options [opt]
+ end
+ end
+end
diff --git a/spec/mspec/spec/commands/mspec_tag_spec.rb b/spec/mspec/spec/commands/mspec_tag_spec.rb
new file mode 100644
index 0000000000..cdb3ac1a60
--- /dev/null
+++ b/spec/mspec/spec/commands/mspec_tag_spec.rb
@@ -0,0 +1,414 @@
+require 'spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/commands/mspec-tag'
+require 'mspec/runner/actions/tag'
+require 'mspec/runner/actions/taglist'
+require 'mspec/runner/actions/tagpurge'
+
+one_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/one_spec.rb'
+two_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/two_spec.rb'
+
+describe MSpecTag, ".new" do
+ before :each do
+ @script = MSpecTag.new
+ end
+
+ it "sets config[:ltags] to an empty list" do
+ @script.config[:ltags].should == []
+ end
+
+ it "sets config[:tagger] to :add" do
+ @script.config[:tagger] = :add
+ end
+
+ it "sets config[:tag] to 'fails:'" do
+ @script.config[:tag] = 'fails:'
+ end
+
+ it "sets config[:outcome] to :fail" do
+ @script.config[:outcome] = :fail
+ end
+end
+
+describe MSpecTag, "#options" do
+ before :each do
+ @stdout, $stdout = $stdout, IOStub.new
+
+ @argv = [one_spec, two_spec]
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+
+ @script = MSpecTag.new
+ @script.stub(:config).and_return(@config)
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it "enables the filter options" do
+ @options.should_receive(:filters)
+ @script.options @argv
+ end
+
+ it "enables the configure option" do
+ @options.should_receive(:configure)
+ @script.options @argv
+ end
+
+ it "provides a custom action (block) to the config option" do
+ @script.should_receive(:load).with("cfg.mspec")
+ @script.options ["-B", "cfg.mspec", one_spec]
+ end
+
+ it "enables the dry run option" do
+ @options.should_receive(:pretend)
+ @script.options @argv
+ end
+
+ it "enables the unguarded option" do
+ @options.should_receive(:unguarded)
+ @script.options @argv
+ end
+
+ it "enables the interrupt single specs option" do
+ @options.should_receive(:interrupt)
+ @script.options @argv
+ end
+
+ it "enables the formatter options" do
+ @options.should_receive(:formatters)
+ @script.options @argv
+ end
+
+ it "enables the verbose option" do
+ @options.should_receive(:verbose)
+ @script.options @argv
+ end
+
+ it "enables the version option" do
+ @options.should_receive(:version)
+ @script.options @argv
+ end
+
+ it "enables the help option" do
+ @options.should_receive(:help)
+ @script.options @argv
+ end
+
+ it "calls #custom_options" do
+ @script.should_receive(:custom_options).with(@options)
+ @script.options @argv
+ end
+
+ it "exits if there are no files to process" do
+ @options.should_receive(:parse).and_return([])
+ @script.should_receive(:exit)
+ @script.options
+ $stdout.should include "No files specified"
+ end
+end
+
+describe MSpecTag, "options" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MSpecTag.new
+ @script.stub(:config).and_return(@config)
+ end
+
+ describe "-N, --add TAG" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-N", "--add", "TAG", an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "sets the mode to :add and sets the tag to TAG" do
+ ["-N", "--add"].each do |opt|
+ @config[:tagger] = nil
+ @config[:tag] = nil
+ @script.options [opt, "taggit", one_spec]
+ @config[:tagger].should == :add
+ @config[:tag].should == "taggit:"
+ end
+ end
+ end
+
+ describe "-R, --del TAG" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-R", "--del", "TAG",
+ an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "it sets the mode to :del, the tag to TAG, and the outcome to :pass" do
+ ["-R", "--del"].each do |opt|
+ @config[:tagger] = nil
+ @config[:tag] = nil
+ @config[:outcome] = nil
+ @script.options [opt, "taggit", one_spec]
+ @config[:tagger].should == :del
+ @config[:tag].should == "taggit:"
+ @config[:outcome].should == :pass
+ end
+ end
+ end
+
+ describe "-Q, --pass" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-Q", "--pass", an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "sets the outcome to :pass" do
+ ["-Q", "--pass"].each do |opt|
+ @config[:outcome] = nil
+ @script.options [opt, one_spec]
+ @config[:outcome].should == :pass
+ end
+ end
+ end
+
+ describe "-F, --fail" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-F", "--fail", an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "sets the outcome to :fail" do
+ ["-F", "--fail"].each do |opt|
+ @config[:outcome] = nil
+ @script.options [opt, one_spec]
+ @config[:outcome].should == :fail
+ end
+ end
+ end
+
+ describe "-L, --all" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-L", "--all", an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "sets the outcome to :all" do
+ ["-L", "--all"].each do |opt|
+ @config[:outcome] = nil
+ @script.options [opt, one_spec]
+ @config[:outcome].should == :all
+ end
+ end
+ end
+
+ describe "--list TAG" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("--list", "TAG", an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "sets the mode to :list" do
+ @config[:tagger] = nil
+ @script.options ["--list", "TAG", one_spec]
+ @config[:tagger].should == :list
+ end
+
+ it "sets ltags to include TAG" do
+ @config[:tag] = nil
+ @script.options ["--list", "TAG", one_spec]
+ @config[:ltags].should == ["TAG"]
+ end
+ end
+
+ describe "--list-all" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("--list-all", an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "sets the mode to :list_all" do
+ @config[:tagger] = nil
+ @script.options ["--list-all", one_spec]
+ @config[:tagger].should == :list_all
+ end
+ end
+
+ describe "--purge" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("--purge", an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "sets the mode to :purge" do
+ @config[:tagger] = nil
+ @script.options ["--purge", one_spec]
+ @config[:tagger].should == :purge
+ end
+ end
+end
+
+describe MSpecTag, "#run" do
+ before :each do
+ MSpec.stub(:process)
+
+ options = double("MSpecOptions").as_null_object
+ options.stub(:parse).and_return(["one", "two"])
+ MSpecOptions.stub(:new).and_return(options)
+
+ @config = { }
+ @script = MSpecTag.new
+ @script.stub(:exit)
+ @script.stub(:config).and_return(@config)
+ @script.stub(:files).and_return(["one", "two"])
+ @script.options
+ end
+
+ it "registers the tags patterns" do
+ @config[:tags_patterns] = [/spec/, "tags"]
+ MSpec.should_receive(:register_tags_patterns).with([/spec/, "tags"])
+ @script.run
+ end
+
+ it "registers the files to process" do
+ MSpec.should_receive(:register_files).with(["one", "two"])
+ @script.run
+ end
+
+ it "processes the files" do
+ MSpec.should_receive(:process)
+ @script.run
+ end
+
+ it "exits with the exit code registered with MSpec" do
+ MSpec.stub(:exit_code).and_return(7)
+ @script.should_receive(:exit).with(7)
+ @script.run
+ end
+end
+
+describe MSpecTag, "#register" do
+ before :each do
+ @script = MSpecTag.new
+ @config = @script.config
+ @config[:tag] = "fake:"
+ @config[:atags] = []
+ @config[:astrings] = []
+ @config[:ltags] = ["fails", "unstable"]
+
+ @script.stub(:files).and_return([])
+ @script.options "fake"
+
+ @t = double("TagAction")
+ @t.stub(:register)
+
+ @tl = double("TagListAction")
+ @tl.stub(:register)
+ end
+
+ it "raises an ArgumentError if no recognized action is given" do
+ @config[:tagger] = :totally_whack
+ lambda { @script.register }.should raise_error(ArgumentError)
+ end
+
+ describe "when config[:tagger] is the default (:add)" do
+ before :each do
+ @config[:formatter] = false
+ end
+
+ it "creates a TagAction" do
+ TagAction.should_receive(:new).and_return(@t)
+ @script.register
+ end
+
+ it "creates a TagAction if config[:tagger] is :del" do
+ @config[:tagger] = :del
+ @config[:outcome] = :pass
+ TagAction.should_receive(:new).with(:del, :pass, "fake", nil, [], []).and_return(@t)
+ @script.register
+ end
+
+ it "calls #register on the TagAction instance" do
+ TagAction.should_receive(:new).and_return(@t)
+ @t.should_receive(:register)
+ @script.register
+ end
+ end
+
+ describe "when config[:tagger] is :list" do
+ before :each do
+ TagListAction.should_receive(:new).with(@config[:ltags]).and_return(@tl)
+ @config[:tagger] = :list
+ end
+
+ it "creates a TagListAction" do
+ @tl.should_receive(:register)
+ @script.register
+ end
+
+ it "registers MSpec pretend mode" do
+ MSpec.should_receive(:register_mode).with(:pretend)
+ @script.register
+ end
+
+ it "sets config[:formatter] to false" do
+ @script.register
+ @config[:formatter].should be_false
+ end
+ end
+
+ describe "when config[:tagger] is :list_all" do
+ before :each do
+ TagListAction.should_receive(:new).with(nil).and_return(@tl)
+ @config[:tagger] = :list_all
+ end
+
+ it "creates a TagListAction" do
+ @tl.should_receive(:register)
+ @script.register
+ end
+
+ it "registers MSpec pretend mode" do
+ MSpec.should_receive(:register_mode).with(:pretend)
+ @script.register
+ end
+
+ it "sets config[:formatter] to false" do
+ @script.register
+ @config[:formatter].should be_false
+ end
+ end
+
+ describe "when config[:tagger] is :purge" do
+ before :each do
+ TagPurgeAction.should_receive(:new).and_return(@tl)
+ MSpec.stub(:register_mode)
+ @config[:tagger] = :purge
+ end
+
+ it "creates a TagPurgeAction" do
+ @tl.should_receive(:register)
+ @script.register
+ end
+
+ it "registers MSpec in pretend mode" do
+ MSpec.should_receive(:register_mode).with(:pretend)
+ @script.register
+ end
+
+ it "registers MSpec in unguarded mode" do
+ MSpec.should_receive(:register_mode).with(:unguarded)
+ @script.register
+ end
+
+ it "sets config[:formatter] to false" do
+ @script.register
+ @config[:formatter].should be_false
+ end
+ end
+end
diff --git a/spec/mspec/spec/expectations/expectations_spec.rb b/spec/mspec/spec/expectations/expectations_spec.rb
new file mode 100644
index 0000000000..fea692f3e3
--- /dev/null
+++ b/spec/mspec/spec/expectations/expectations_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+
+describe SpecExpectationNotMetError do
+ it "is a subclass of StandardError" do
+ SpecExpectationNotMetError.ancestors.should include(StandardError)
+ end
+end
+
+describe SpecExpectationNotFoundError do
+ it "is a subclass of StandardError" do
+ SpecExpectationNotFoundError.ancestors.should include(StandardError)
+ end
+end
+
+describe SpecExpectationNotFoundError, "#message" do
+ it "returns 'No behavior expectation was found in the example'" do
+ m = SpecExpectationNotFoundError.new.message
+ m.should == "No behavior expectation was found in the example"
+ end
+end
+
+describe SpecExpectation, "#fail_with" do
+ it "raises an SpecExpectationNotMetError" do
+ lambda {
+ SpecExpectation.fail_with "expected this", "to equal that"
+ }.should raise_error(SpecExpectationNotMetError, "expected this to equal that")
+ end
+end
diff --git a/spec/mspec/spec/expectations/should.rb b/spec/mspec/spec/expectations/should.rb
new file mode 100644
index 0000000000..24b1cf2bf8
--- /dev/null
+++ b/spec/mspec/spec/expectations/should.rb
@@ -0,0 +1,73 @@
+$: << File.dirname(__FILE__) + '/../../lib'
+require 'mspec'
+require 'mspec/utils/script'
+
+# The purpose of these specs is to confirm that the #should
+# and #should_not methods are functioning appropriately. We
+# use a separate spec file that is invoked from the MSpec
+# specs but is run by MSpec. This avoids conflicting with
+# RSpec's #should and #should_not methods.
+
+class ShouldSpecsMonitor
+ def initialize
+ @called = 0
+ end
+
+ def expectation(state)
+ @called += 1
+ end
+
+ def finish
+ puts "I was called #{@called} times"
+ end
+end
+
+# Simplistic runner
+formatter = DottedFormatter.new
+formatter.register
+
+monitor = ShouldSpecsMonitor.new
+MSpec.register :expectation, monitor
+MSpec.register :finish, monitor
+
+at_exit { MSpec.actions :finish }
+
+MSpec.actions :start
+MSpec.setup_env
+
+# Specs
+describe "MSpec expectation method #should" do
+ it "accepts a matcher" do
+ :sym.should be_kind_of(Symbol)
+ end
+
+ it "causes a failue to be recorded" do
+ 1.should == 2
+ end
+
+ it "registers that an expectation has been encountered" do
+ # an empty example block causes an exception because
+ # no expectation was encountered
+ end
+
+ it "invokes the MSpec :expectation actions" do
+ 1.should == 1
+ end
+end
+
+describe "MSpec expectation method #should_not" do
+ it "accepts a matcher" do
+ "sym".should_not be_kind_of(Symbol)
+ end
+
+ it "causes a failure to be recorded" do
+ 1.should_not == 1
+ end
+
+ it "registers that an expectation has been encountered" do
+ end
+
+ it "invokes the MSpec :expectation actions" do
+ 1.should_not == 2
+ end
+end
diff --git a/spec/mspec/spec/expectations/should_spec.rb b/spec/mspec/spec/expectations/should_spec.rb
new file mode 100644
index 0000000000..3258caf13c
--- /dev/null
+++ b/spec/mspec/spec/expectations/should_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+require 'rbconfig'
+
+describe "MSpec" do
+ before :all do
+ path = RbConfig::CONFIG['bindir']
+ exe = RbConfig::CONFIG['ruby_install_name']
+ file = File.dirname(__FILE__) + '/should.rb'
+ @out = `#{path}/#{exe} #{file}`
+ end
+
+ describe "#should" do
+ it "records failures" do
+ @out.should include <<-EOS
+1)
+MSpec expectation method #should causes a failue to be recorded FAILED
+Expected 1
+ to equal 2
+EOS
+ end
+
+ it "raises exceptions for examples with no expectations" do
+ @out.should include <<-EOS
+2)
+MSpec expectation method #should registers that an expectation has been encountered FAILED
+No behavior expectation was found in the example
+EOS
+ end
+ end
+
+ describe "#should_not" do
+ it "records failures" do
+ @out.should include <<-EOS
+3)
+MSpec expectation method #should_not causes a failure to be recorded FAILED
+Expected 1
+ not to equal 1
+EOS
+ end
+
+ it "raises exceptions for examples with no expectations" do
+ @out.should include <<-EOS
+4)
+MSpec expectation method #should_not registers that an expectation has been encountered FAILED
+No behavior expectation was found in the example
+EOS
+ end
+ end
+
+ it "prints status information" do
+ @out.should include ".FF..FF."
+ end
+
+ it "prints out a summary" do
+ @out.should include "0 files, 8 examples, 6 expectations, 4 failures, 0 errors"
+ end
+
+ it "records expectations" do
+ @out.should include "I was called 6 times"
+ end
+end
diff --git a/spec/mspec/spec/fixtures/a_spec.rb b/spec/mspec/spec/fixtures/a_spec.rb
new file mode 100644
index 0000000000..17a7e8b664
--- /dev/null
+++ b/spec/mspec/spec/fixtures/a_spec.rb
@@ -0,0 +1,15 @@
+unless defined?(RSpec)
+ describe "Foo#bar" do
+ it "passes" do
+ 1.should == 1
+ end
+
+ it "errors" do
+ 1.should == 2
+ end
+
+ it "fails" do
+ raise "failure"
+ end
+ end
+end
diff --git a/spec/mspec/spec/fixtures/b_spec.rb b/spec/mspec/spec/fixtures/b_spec.rb
new file mode 100644
index 0000000000..f1f63317cb
--- /dev/null
+++ b/spec/mspec/spec/fixtures/b_spec.rb
@@ -0,0 +1,7 @@
+unless defined?(RSpec)
+ describe "Bar#baz" do
+ it "works" do
+ 1.should == 1
+ end
+ end
+end
diff --git a/spec/mspec/spec/fixtures/chatty_spec.rb b/spec/mspec/spec/fixtures/chatty_spec.rb
new file mode 100644
index 0000000000..2d110d8ce4
--- /dev/null
+++ b/spec/mspec/spec/fixtures/chatty_spec.rb
@@ -0,0 +1,8 @@
+unless defined?(RSpec)
+ describe "Chatty#spec" do
+ it "prints too much" do
+ STDOUT.puts "Hello\nIt's me!"
+ 1.should == 1
+ end
+ end
+end
diff --git a/spec/mspec/spec/fixtures/config.mspec b/spec/mspec/spec/fixtures/config.mspec
new file mode 100644
index 0000000000..4a069e2eb0
--- /dev/null
+++ b/spec/mspec/spec/fixtures/config.mspec
@@ -0,0 +1,10 @@
+class MSpecScript
+ set :target, 'ruby'
+
+ set :backtrace_filter, /lib\/mspec\//
+
+ set :tags_patterns, [
+ [%r(spec/fixtures/), 'spec/fixtures/tags/'],
+ [/_spec.rb$/, '_tags.txt']
+ ]
+end
diff --git a/spec/mspec/spec/fixtures/die_spec.rb b/spec/mspec/spec/fixtures/die_spec.rb
new file mode 100644
index 0000000000..0f66793274
--- /dev/null
+++ b/spec/mspec/spec/fixtures/die_spec.rb
@@ -0,0 +1,7 @@
+unless defined?(RSpec)
+ describe "Deadly#spec" do
+ it "dies" do
+ abort "DEAD"
+ end
+ end
+end
diff --git a/spec/mspec/spec/fixtures/my_ruby b/spec/mspec/spec/fixtures/my_ruby
new file mode 100755
index 0000000000..4d552f27fb
--- /dev/null
+++ b/spec/mspec/spec/fixtures/my_ruby
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+echo $RUBY_EXE
+ruby "$@"
diff --git a/spec/mspec/spec/fixtures/object_methods_spec.rb b/spec/mspec/spec/fixtures/object_methods_spec.rb
new file mode 100644
index 0000000000..9b7c1523e5
--- /dev/null
+++ b/spec/mspec/spec/fixtures/object_methods_spec.rb
@@ -0,0 +1,8 @@
+unless defined?(RSpec)
+ describe "Object" do
+ it ".public_instance_methods(false) is empty" do
+ Object.public_instance_methods(false).sort.should ==
+ [:should, :should_not, :should_not_receive, :should_receive, :stub!]
+ end
+ end
+end
diff --git a/spec/mspec/spec/fixtures/print_interpreter_spec.rb b/spec/mspec/spec/fixtures/print_interpreter_spec.rb
new file mode 100644
index 0000000000..a662346d0a
--- /dev/null
+++ b/spec/mspec/spec/fixtures/print_interpreter_spec.rb
@@ -0,0 +1,4 @@
+unless defined?(RSpec)
+ puts ENV["RUBY_EXE"]
+ puts ruby_cmd(nil).split.first
+end
diff --git a/spec/mspec/spec/fixtures/tagging_spec.rb b/spec/mspec/spec/fixtures/tagging_spec.rb
new file mode 100644
index 0000000000..0097fd1808
--- /dev/null
+++ b/spec/mspec/spec/fixtures/tagging_spec.rb
@@ -0,0 +1,16 @@
+# encoding: utf-8
+unless defined?(RSpec)
+ describe "Tag#me" do
+ it "passes" do
+ 1.should == 1
+ end
+
+ it "errors" do
+ 1.should == 2
+ end
+
+ it "érròrs in unicode" do
+ 1.should == 2
+ end
+ end
+end
diff --git a/spec/mspec/spec/guards/block_device_spec.rb b/spec/mspec/spec/guards/block_device_spec.rb
new file mode 100644
index 0000000000..3b437b6d74
--- /dev/null
+++ b/spec/mspec/spec/guards/block_device_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe Object, "#with_block_device" do
+ before :each do
+ ScratchPad.clear
+
+ @guard = BlockDeviceGuard.new
+ BlockDeviceGuard.stub(:new).and_return(@guard)
+ end
+
+ platform_is_not :freebsd, :windows do
+ it "yields if block device is available" do
+ @guard.should_receive(:`).and_return("block devices")
+ with_block_device { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield if block device is not available" do
+ @guard.should_receive(:`).and_return(nil)
+ with_block_device { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+ end
+
+ platform_is :freebsd, :windows do
+ it "does not yield, since platform does not support block devices" do
+ @guard.should_not_receive(:`)
+ with_block_device { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+ end
+
+ it "sets the name of the guard to :with_block_device" do
+ with_block_device { }
+ @guard.name.should == :with_block_device
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(true)
+ @guard.should_receive(:unregister)
+ lambda do
+ with_block_device { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
diff --git a/spec/mspec/spec/guards/bug_spec.rb b/spec/mspec/spec/guards/bug_spec.rb
new file mode 100644
index 0000000000..c8529a49f7
--- /dev/null
+++ b/spec/mspec/spec/guards/bug_spec.rb
@@ -0,0 +1,151 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe BugGuard, "#match? when #implementation? is 'ruby'" do
+ before :all do
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ after :all do
+ $VERBOSE = @verbose
+ end
+
+ before :each do
+ hide_deprecation_warnings
+ stub_const "VersionGuard::FULL_RUBY_VERSION", SpecVersion.new('1.8.6')
+ @ruby_engine = Object.const_get :RUBY_ENGINE
+ Object.const_set :RUBY_ENGINE, 'ruby'
+ end
+
+ after :each do
+ Object.const_set :RUBY_ENGINE, @ruby_engine
+ end
+
+ it "returns false when version argument is less than RUBY_VERSION" do
+ BugGuard.new("#1", "1.8.5").match?.should == false
+ end
+
+ it "returns true when version argument is equal to RUBY_VERSION" do
+ BugGuard.new("#1", "1.8.6").match?.should == true
+ end
+
+ it "returns true when version argument is greater than RUBY_VERSION" do
+ BugGuard.new("#1", "1.8.7").match?.should == true
+ end
+
+ it "returns true when version argument implicitly includes RUBY_VERSION" do
+ BugGuard.new("#1", "1.8").match?.should == true
+ BugGuard.new("#1", "1.8.6").match?.should == true
+ end
+
+ it "returns true when the argument range includes RUBY_VERSION" do
+ BugGuard.new("#1", '1.8.5'..'1.8.7').match?.should == true
+ BugGuard.new("#1", '1.8'..'1.9').match?.should == true
+ BugGuard.new("#1", '1.8'...'1.9').match?.should == true
+ BugGuard.new("#1", '1.8'..'1.8.6').match?.should == true
+ BugGuard.new("#1", '1.8.5'..'1.8.6').match?.should == true
+ BugGuard.new("#1", ''...'1.8.7').match?.should == true
+ end
+
+ it "returns false when the argument range does not include RUBY_VERSION" do
+ BugGuard.new("#1", '1.8.7'..'1.8.9').match?.should == false
+ BugGuard.new("#1", '1.8.4'..'1.8.5').match?.should == false
+ BugGuard.new("#1", '1.8.4'...'1.8.6').match?.should == false
+ BugGuard.new("#1", '1.8.5'...'1.8.6').match?.should == false
+ BugGuard.new("#1", ''...'1.8.6').match?.should == false
+ end
+
+ it "returns false when MSpec.mode?(:no_ruby_bug) is true" do
+ MSpec.should_receive(:mode?).with(:no_ruby_bug).twice.and_return(:true)
+ BugGuard.new("#1", "1.8.5").match?.should == false
+ BugGuard.new("#1", "1.8").match?.should == false
+ end
+end
+
+describe BugGuard, "#match? when #implementation? is not 'ruby'" do
+ before :all do
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ after :all do
+ $VERBOSE = @verbose
+ end
+
+ before :each do
+ hide_deprecation_warnings
+ @ruby_version = Object.const_get :RUBY_VERSION
+ @ruby_engine = Object.const_get :RUBY_ENGINE
+
+ Object.const_set :RUBY_VERSION, '1.8.6'
+ Object.const_set :RUBY_ENGINE, 'jruby'
+ end
+
+ after :each do
+ Object.const_set :RUBY_VERSION, @ruby_version
+ Object.const_set :RUBY_ENGINE, @ruby_engine
+ end
+
+ it "returns false when version argument is less than RUBY_VERSION" do
+ BugGuard.new("#1", "1.8").match?.should == false
+ BugGuard.new("#1", "1.8.6").match?.should == false
+ end
+
+ it "returns false when version argument is equal to RUBY_VERSION" do
+ BugGuard.new("#1", "1.8.6").match?.should == false
+ end
+
+ it "returns false when version argument is greater than RUBY_VERSION" do
+ BugGuard.new("#1", "1.8.7").match?.should == false
+ end
+
+ it "returns false no matter if the argument range includes RUBY_VERSION" do
+ BugGuard.new("#1", '1.8'...'1.9').match?.should == false
+ BugGuard.new("#1", '1.8.5'...'1.8.7').match?.should == false
+ BugGuard.new("#1", '1.8.4'...'1.8.6').match?.should == false
+ end
+
+ it "returns false when MSpec.mode?(:no_ruby_bug) is true" do
+ MSpec.stub(:mode?).and_return(:true)
+ BugGuard.new("#1", "1.8.6").match?.should == false
+ end
+end
+
+describe Object, "#ruby_bug" do
+ before :each do
+ hide_deprecation_warnings
+ @guard = BugGuard.new "#1234", "x.x.x"
+ BugGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "yields when #match? returns false" do
+ @guard.stub(:match?).and_return(false)
+ ruby_bug("#1234", "1.8.6") { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield when #match? returns true" do
+ @guard.stub(:match?).and_return(true)
+ ruby_bug("#1234", "1.8.6") { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "requires a bug tracker number and a version number" do
+ lambda { ruby_bug { } }.should raise_error(ArgumentError)
+ lambda { ruby_bug("#1234") { } }.should raise_error(ArgumentError)
+ end
+
+ it "sets the name of the guard to :ruby_bug" do
+ ruby_bug("#1234", "1.8.6") { }
+ @guard.name.should == :ruby_bug
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:unregister)
+ lambda do
+ ruby_bug("", "") { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
diff --git a/spec/mspec/spec/guards/conflict_spec.rb b/spec/mspec/spec/guards/conflict_spec.rb
new file mode 100644
index 0000000000..deada96821
--- /dev/null
+++ b/spec/mspec/spec/guards/conflict_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe Object, "#conflicts_with" do
+ before :each do
+ hide_deprecation_warnings
+ ScratchPad.clear
+ end
+
+ it "does not yield if Object.constants includes any of the arguments" do
+ Object.stub(:constants).and_return(["SomeClass", "OtherClass"])
+ conflicts_with(:SomeClass, :AClass, :BClass) { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "does not yield if Object.constants (as Symbols) includes any of the arguments" do
+ Object.stub(:constants).and_return([:SomeClass, :OtherClass])
+ conflicts_with(:SomeClass, :AClass, :BClass) { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "yields if Object.constants does not include any of the arguments" do
+ Object.stub(:constants).and_return(["SomeClass", "OtherClass"])
+ conflicts_with(:AClass, :BClass) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "yields if Object.constants (as Symbols) does not include any of the arguments" do
+ Object.stub(:constants).and_return([:SomeClass, :OtherClass])
+ conflicts_with(:AClass, :BClass) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+end
+
+describe Object, "#conflicts_with" do
+ before :each do
+ hide_deprecation_warnings
+ @guard = ConflictsGuard.new
+ ConflictsGuard.stub(:new).and_return(@guard)
+ end
+
+ it "sets the name of the guard to :conflicts_with" do
+ conflicts_with(:AClass, :BClass) { }
+ @guard.name.should == :conflicts_with
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:unregister)
+ lambda do
+ conflicts_with(:AClass, :BClass) { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
diff --git a/spec/mspec/spec/guards/endian_spec.rb b/spec/mspec/spec/guards/endian_spec.rb
new file mode 100644
index 0000000000..5b40c203ab
--- /dev/null
+++ b/spec/mspec/spec/guards/endian_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe Object, "#big_endian" do
+ before :each do
+ @guard = BigEndianGuard.new
+ BigEndianGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "yields on big-endian platforms" do
+ @guard.stub(:pattern).and_return([?\001])
+ big_endian { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield on little-endian platforms" do
+ @guard.stub(:pattern).and_return([?\000])
+ big_endian { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "sets the name of the guard to :big_endian" do
+ big_endian { }
+ @guard.name.should == :big_endian
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.stub(:pattern).and_return([?\001])
+ @guard.should_receive(:unregister)
+ lambda do
+ big_endian { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
+
+describe Object, "#little_endian" do
+ before :each do
+ @guard = BigEndianGuard.new
+ BigEndianGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "yields on little-endian platforms" do
+ @guard.stub(:pattern).and_return([?\000])
+ little_endian { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield on big-endian platforms" do
+ @guard.stub(:pattern).and_return([?\001])
+ little_endian { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+end
diff --git a/spec/mspec/spec/guards/feature_spec.rb b/spec/mspec/spec/guards/feature_spec.rb
new file mode 100644
index 0000000000..8761cb2fbb
--- /dev/null
+++ b/spec/mspec/spec/guards/feature_spec.rb
@@ -0,0 +1,120 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe FeatureGuard, ".enabled?" do
+ it "returns true if the feature is enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(true)
+ FeatureGuard.enabled?(:encoding).should be_true
+ end
+
+ it "returns false if the feature is not enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(false)
+ FeatureGuard.enabled?(:encoding).should be_false
+ end
+
+ it "returns true if all the features are enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:one).and_return(true)
+ MSpec.should_receive(:feature_enabled?).with(:two).and_return(true)
+ FeatureGuard.enabled?(:one, :two).should be_true
+ end
+
+ it "returns false if any of the features are not enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:one).and_return(true)
+ MSpec.should_receive(:feature_enabled?).with(:two).and_return(false)
+ FeatureGuard.enabled?(:one, :two).should be_false
+ end
+end
+
+describe Object, "#with_feature" do
+ before :each do
+ ScratchPad.clear
+
+ @guard = FeatureGuard.new :encoding
+ FeatureGuard.stub(:new).and_return(@guard)
+ end
+
+ it "sets the name of the guard to :with_feature" do
+ with_feature(:encoding) { }
+ @guard.name.should == :with_feature
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(true)
+ @guard.should_receive(:unregister)
+ lambda do
+ with_feature { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
+
+describe Object, "#with_feature" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "yields if the feature is enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(true)
+ with_feature(:encoding) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "yields if all the features are enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:one).and_return(true)
+ MSpec.should_receive(:feature_enabled?).with(:two).and_return(true)
+ with_feature(:one, :two) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield if the feature is not enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(false)
+ with_feature(:encoding) { ScratchPad.record :yield }
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "does not yield if any of the features are not enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:one).and_return(true)
+ MSpec.should_receive(:feature_enabled?).with(:two).and_return(false)
+ with_feature(:one, :two) { ScratchPad.record :yield }
+ ScratchPad.recorded.should be_nil
+ end
+end
+
+describe Object, "#without_feature" do
+ before :each do
+ ScratchPad.clear
+
+ @guard = FeatureGuard.new :encoding
+ FeatureGuard.stub(:new).and_return(@guard)
+ end
+
+ it "sets the name of the guard to :without_feature" do
+ without_feature(:encoding) { }
+ @guard.name.should == :without_feature
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(false)
+ @guard.should_receive(:unregister)
+ lambda do
+ without_feature { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
+
+describe Object, "#without_feature" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "does not yield if the feature is enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(true)
+ without_feature(:encoding) { ScratchPad.record :yield }
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "yields if the feature is disabled" do
+ MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(false)
+ without_feature(:encoding) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+end
diff --git a/spec/mspec/spec/guards/guard_spec.rb b/spec/mspec/spec/guards/guard_spec.rb
new file mode 100644
index 0000000000..5c3dae4b3f
--- /dev/null
+++ b/spec/mspec/spec/guards/guard_spec.rb
@@ -0,0 +1,421 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'rbconfig'
+
+describe SpecGuard, ".ruby_version" do
+ before :each do
+ stub_const "RUBY_VERSION", "8.2.3"
+ end
+
+ it "returns the full version for :full" do
+ SpecGuard.ruby_version(:full).should == "8.2.3"
+ end
+
+ it "returns major.minor.tiny for :tiny" do
+ SpecGuard.ruby_version(:tiny).should == "8.2.3"
+ end
+
+ it "returns major.minor.tiny for :teeny" do
+ SpecGuard.ruby_version(:tiny).should == "8.2.3"
+ end
+
+ it "returns major.minor for :minor" do
+ SpecGuard.ruby_version(:minor).should == "8.2"
+ end
+
+ it "defaults to :minor" do
+ SpecGuard.ruby_version.should == "8.2"
+ end
+
+ it "returns major for :major" do
+ SpecGuard.ruby_version(:major).should == "8"
+ end
+end
+
+describe SpecGuard, "#yield?" do
+ before :each do
+ MSpec.clear_modes
+ @guard = SpecGuard.new
+ @guard.stub(:match?).and_return(false)
+ end
+
+ after :each do
+ MSpec.unregister :add, @guard
+ MSpec.clear_modes
+ SpecGuard.clear_guards
+ end
+
+ it "returns true if MSpec.mode?(:unguarded) is true" do
+ MSpec.register_mode :unguarded
+ @guard.yield?.should == true
+ end
+
+ it "returns true if MSpec.mode?(:verify) is true" do
+ MSpec.register_mode :verify
+ @guard.yield?.should == true
+ end
+
+ it "returns true if MSpec.mode?(:verify) is true regardless of invert being true" do
+ MSpec.register_mode :verify
+ @guard.yield?(true).should == true
+ end
+
+ it "returns true if MSpec.mode?(:report) is true" do
+ MSpec.register_mode :report
+ @guard.yield?.should == true
+ end
+
+ it "returns true if MSpec.mode?(:report) is true regardless of invert being true" do
+ MSpec.register_mode :report
+ @guard.yield?(true).should == true
+ end
+
+ it "returns true if MSpec.mode?(:report_on) is true and SpecGuards.guards contains the named guard" do
+ MSpec.register_mode :report_on
+ SpecGuard.guards << :guard_name
+ @guard.yield?.should == false
+ @guard.name = :guard_name
+ @guard.yield?.should == true
+ end
+
+ it "returns #match? if neither report nor verify mode are true" do
+ @guard.stub(:match?).and_return(false)
+ @guard.yield?.should == false
+ @guard.stub(:match?).and_return(true)
+ @guard.yield?.should == true
+ end
+
+ it "returns #match? if invert is true and neither report nor verify mode are true" do
+ @guard.stub(:match?).and_return(false)
+ @guard.yield?(true).should == true
+ @guard.stub(:match?).and_return(true)
+ @guard.yield?(true).should == false
+ end
+end
+
+describe SpecGuard, "#match?" do
+ before :each do
+ @guard = SpecGuard.new
+ end
+
+ it "must be implemented in subclasses" do
+ lambda {
+ @guard.match?
+ }.should raise_error("must be implemented by the subclass")
+ end
+end
+
+describe SpecGuard, "#unregister" do
+ before :each do
+ MSpec.stub(:unregister)
+ @guard = SpecGuard.new
+ end
+
+ it "unregisters from MSpec :add actions" do
+ MSpec.should_receive(:unregister).with(:add, @guard)
+ @guard.unregister
+ end
+end
+
+describe SpecGuard, "#record" do
+ after :each do
+ SpecGuard.clear
+ end
+
+ it "saves the name of the guarded spec under the name of the guard" do
+ guard = SpecGuard.new "a", "1.8"..."1.9"
+ guard.name = :named_guard
+ guard.record "SomeClass#action returns true"
+ SpecGuard.report.should == {
+ 'named_guard a, 1.8...1.9' => ["SomeClass#action returns true"]
+ }
+ end
+end
+
+describe SpecGuard, ".guards" do
+ it "returns an Array" do
+ SpecGuard.guards.should be_kind_of(Array)
+ end
+end
+
+describe SpecGuard, ".clear_guards" do
+ it "resets the array to empty" do
+ SpecGuard.guards << :guard
+ SpecGuard.guards.should == [:guard]
+ SpecGuard.clear_guards
+ SpecGuard.guards.should == []
+ end
+end
+
+describe SpecGuard, ".finish" do
+ before :each do
+ $stdout = @out = IOStub.new
+ end
+
+ after :each do
+ $stdout = STDOUT
+ SpecGuard.clear
+ end
+
+ it "prints the descriptions of the guarded specs" do
+ guard = SpecGuard.new "a", "1.8"..."1.9"
+ guard.name = :named_guard
+ guard.record "SomeClass#action returns true"
+ guard.record "SomeClass#reverse returns false"
+ SpecGuard.finish
+ $stdout.should == %[
+
+2 specs omitted by guard: named_guard a, 1.8...1.9:
+
+SomeClass#action returns true
+SomeClass#reverse returns false
+
+]
+ end
+end
+
+describe SpecGuard, ".run_if" do
+ before :each do
+ @guard = SpecGuard.new
+ ScratchPad.clear
+ end
+
+ it "yields if match? returns true" do
+ @guard.stub(:match?).and_return(true)
+ @guard.run_if(:name) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield if match? returns false" do
+ @guard.stub(:match?).and_return(false)
+ @guard.run_if(:name) { fail }
+ end
+
+ it "returns the result of the block if match? is true" do
+ @guard.stub(:match?).and_return(true)
+ @guard.run_if(:name) { 42 }.should == 42
+ end
+
+ it "returns nil if given a block and match? is false" do
+ @guard.stub(:match?).and_return(false)
+ @guard.run_if(:name) { 42 }.should == nil
+ end
+
+ it "returns what #match? returns when no block is given" do
+ @guard.stub(:match?).and_return(true)
+ @guard.run_if(:name).should == true
+ @guard.stub(:match?).and_return(false)
+ @guard.run_if(:name).should == false
+ end
+end
+
+describe SpecGuard, ".run_unless" do
+ before :each do
+ @guard = SpecGuard.new
+ ScratchPad.clear
+ end
+
+ it "yields if match? returns false" do
+ @guard.stub(:match?).and_return(false)
+ @guard.run_unless(:name) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield if match? returns true" do
+ @guard.stub(:match?).and_return(true)
+ @guard.run_unless(:name) { fail }
+ end
+
+ it "returns the result of the block if match? is false" do
+ @guard.stub(:match?).and_return(false)
+ @guard.run_unless(:name) { 42 }.should == 42
+ end
+
+ it "returns nil if given a block and match? is true" do
+ @guard.stub(:match?).and_return(true)
+ @guard.run_unless(:name) { 42 }.should == nil
+ end
+
+ it "returns the opposite of what #match? returns when no block is given" do
+ @guard.stub(:match?).and_return(true)
+ @guard.run_unless(:name).should == false
+ @guard.stub(:match?).and_return(false)
+ @guard.run_unless(:name).should == true
+ end
+end
+
+describe Object, "#guard" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ after :each do
+ MSpec.clear_modes
+ end
+
+ it "allows to combine guards" do
+ guard1 = VersionGuard.new 'x.x.x'
+ VersionGuard.stub(:new).and_return(guard1)
+ guard2 = PlatformGuard.new :dummy
+ PlatformGuard.stub(:new).and_return(guard2)
+
+ guard1.stub(:match?).and_return(true)
+ guard2.stub(:match?).and_return(true)
+ guard -> { ruby_version_is "2.4" and platform_is :linux } do
+ ScratchPad.record :yield
+ end
+ ScratchPad.recorded.should == :yield
+
+ guard1.stub(:match?).and_return(false)
+ guard2.stub(:match?).and_return(true)
+ guard -> { ruby_version_is "2.4" and platform_is :linux } do
+ fail
+ end
+
+ guard1.stub(:match?).and_return(true)
+ guard2.stub(:match?).and_return(false)
+ guard -> { ruby_version_is "2.4" and platform_is :linux } do
+ fail
+ end
+
+ guard1.stub(:match?).and_return(false)
+ guard2.stub(:match?).and_return(false)
+ guard -> { ruby_version_is "2.4" and platform_is :linux } do
+ fail
+ end
+ end
+
+ it "yields when the Proc returns true" do
+ guard -> { true } do
+ ScratchPad.record :yield
+ end
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield when the Proc returns false" do
+ guard -> { false } do
+ fail
+ end
+ end
+
+ it "yields if MSpec.mode?(:unguarded) is true" do
+ MSpec.register_mode :unguarded
+
+ guard -> { false } do
+ ScratchPad.record :yield1
+ end
+ ScratchPad.recorded.should == :yield1
+
+ guard -> { true } do
+ ScratchPad.record :yield2
+ end
+ ScratchPad.recorded.should == :yield2
+ end
+
+ it "yields if MSpec.mode?(:verify) is true" do
+ MSpec.register_mode :verify
+
+ guard -> { false } do
+ ScratchPad.record :yield1
+ end
+ ScratchPad.recorded.should == :yield1
+
+ guard -> { true } do
+ ScratchPad.record :yield2
+ end
+ ScratchPad.recorded.should == :yield2
+ end
+
+ it "yields if MSpec.mode?(:report) is true" do
+ MSpec.register_mode :report
+
+ guard -> { false } do
+ ScratchPad.record :yield1
+ end
+ ScratchPad.recorded.should == :yield1
+
+ guard -> { true } do
+ ScratchPad.record :yield2
+ end
+ ScratchPad.recorded.should == :yield2
+ end
+
+ it "raises an error if no Proc is given" do
+ -> { guard :foo }.should raise_error(RuntimeError)
+ end
+
+ it "requires a block" do
+ -> {
+ guard(-> { true })
+ }.should raise_error(LocalJumpError)
+ -> {
+ guard(-> { false })
+ }.should raise_error(LocalJumpError)
+ end
+end
+
+describe Object, "#guard_not" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "allows to combine guards" do
+ guard1 = VersionGuard.new 'x.x.x'
+ VersionGuard.stub(:new).and_return(guard1)
+ guard2 = PlatformGuard.new :dummy
+ PlatformGuard.stub(:new).and_return(guard2)
+
+ guard1.stub(:match?).and_return(true)
+ guard2.stub(:match?).and_return(true)
+ guard_not -> { ruby_version_is "2.4" and platform_is :linux } do
+ fail
+ end
+
+ guard1.stub(:match?).and_return(false)
+ guard2.stub(:match?).and_return(true)
+ guard_not -> { ruby_version_is "2.4" and platform_is :linux } do
+ ScratchPad.record :yield1
+ end
+ ScratchPad.recorded.should == :yield1
+
+ guard1.stub(:match?).and_return(true)
+ guard2.stub(:match?).and_return(false)
+ guard_not -> { ruby_version_is "2.4" and platform_is :linux } do
+ ScratchPad.record :yield2
+ end
+ ScratchPad.recorded.should == :yield2
+
+ guard1.stub(:match?).and_return(false)
+ guard2.stub(:match?).and_return(false)
+ guard_not -> { ruby_version_is "2.4" and platform_is :linux } do
+ ScratchPad.record :yield3
+ end
+ ScratchPad.recorded.should == :yield3
+ end
+
+ it "yields when the Proc returns false" do
+ guard_not -> { false } do
+ ScratchPad.record :yield
+ end
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield when the Proc returns true" do
+ guard_not -> { true } do
+ fail
+ end
+ end
+
+ it "raises an error if no Proc is given" do
+ -> { guard_not :foo }.should raise_error(RuntimeError)
+ end
+
+ it "requires a block" do
+ -> {
+ guard_not(-> { true })
+ }.should raise_error(LocalJumpError)
+ -> {
+ guard_not(-> { false })
+ }.should raise_error(LocalJumpError)
+ end
+end
diff --git a/spec/mspec/spec/guards/platform_spec.rb b/spec/mspec/spec/guards/platform_spec.rb
new file mode 100644
index 0000000000..749963d3db
--- /dev/null
+++ b/spec/mspec/spec/guards/platform_spec.rb
@@ -0,0 +1,328 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe Object, "#platform_is" do
+ before :each do
+ @guard = PlatformGuard.new :dummy
+ PlatformGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "does not yield when #os? returns false" do
+ PlatformGuard.stub(:os?).and_return(false)
+ platform_is(:ruby) { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "yields when #os? returns true" do
+ PlatformGuard.stub(:os?).and_return(true)
+ platform_is(:solarce) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "returns what #os? returns when no block is given" do
+ PlatformGuard.stub(:os?).and_return(true)
+ platform_is(:solarce).should == true
+ PlatformGuard.stub(:os?).and_return(false)
+ platform_is(:solarce).should == false
+ end
+
+ it "sets the name of the guard to :platform_is" do
+ platform_is(:solarce) { }
+ @guard.name.should == :platform_is
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(true)
+ @guard.should_receive(:unregister)
+ lambda do
+ platform_is(:solarce) { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
+
+describe Object, "#platform_is_not" do
+ before :each do
+ @guard = PlatformGuard.new :dummy
+ PlatformGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "does not yield when #os? returns true" do
+ PlatformGuard.stub(:os?).and_return(true)
+ platform_is_not(:ruby) { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "yields when #os? returns false" do
+ PlatformGuard.stub(:os?).and_return(false)
+ platform_is_not(:solarce) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "returns the opposite of what #os? returns when no block is given" do
+ PlatformGuard.stub(:os?).and_return(true)
+ platform_is_not(:solarce).should == false
+ PlatformGuard.stub(:os?).and_return(false)
+ platform_is_not(:solarce).should == true
+ end
+
+ it "sets the name of the guard to :platform_is_not" do
+ platform_is_not(:solarce) { }
+ @guard.name.should == :platform_is_not
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(false)
+ @guard.should_receive(:unregister)
+ lambda do
+ platform_is_not(:solarce) { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
+
+describe Object, "#platform_is :wordsize => SIZE_SPEC" do
+ before :each do
+ @guard = PlatformGuard.new :darwin, :wordsize => 32
+ PlatformGuard.stub(:os?).and_return(true)
+ PlatformGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "yields when #wordsize? returns true" do
+ PlatformGuard.stub(:wordsize?).and_return(true)
+ platform_is(:wordsize => 32) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "doesn not yield when #wordsize? returns false" do
+ PlatformGuard.stub(:wordsize?).and_return(false)
+ platform_is(:wordsize => 32) { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+end
+
+describe Object, "#platform_is_not :wordsize => SIZE_SPEC" do
+ before :each do
+ @guard = PlatformGuard.new :darwin, :wordsize => 32
+ PlatformGuard.stub(:os?).and_return(true)
+ PlatformGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "yields when #wordsize? returns false" do
+ PlatformGuard.stub(:wordsize?).and_return(false)
+ platform_is_not(:wordsize => 32) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "doesn not yield when #wordsize? returns true" do
+ PlatformGuard.stub(:wordsize?).and_return(true)
+ platform_is_not(:wordsize => 32) { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+end
+
+describe PlatformGuard, ".implementation?" do
+ it "returns true if passed :ruby and RUBY_ENGINE == 'ruby'" do
+ stub_const 'RUBY_ENGINE', 'ruby'
+ PlatformGuard.implementation?(:ruby).should == true
+ end
+
+ it "returns true if passed :rubinius and RUBY_ENGINE == 'rbx'" do
+ stub_const 'RUBY_ENGINE', 'rbx'
+ PlatformGuard.implementation?(:rubinius).should == true
+ end
+
+ it "returns true if passed :jruby and RUBY_ENGINE == 'jruby'" do
+ stub_const 'RUBY_ENGINE', 'jruby'
+ PlatformGuard.implementation?(:jruby).should == true
+ end
+
+ it "returns true if passed :ironruby and RUBY_ENGINE == 'ironruby'" do
+ stub_const 'RUBY_ENGINE', 'ironruby'
+ PlatformGuard.implementation?(:ironruby).should == true
+ end
+
+ it "returns true if passed :maglev and RUBY_ENGINE == 'maglev'" do
+ stub_const 'RUBY_ENGINE', 'maglev'
+ PlatformGuard.implementation?(:maglev).should == true
+ end
+
+ it "returns true if passed :topaz and RUBY_ENGINE == 'topaz'" do
+ stub_const 'RUBY_ENGINE', 'topaz'
+ PlatformGuard.implementation?(:topaz).should == true
+ end
+
+ it "returns true if passed :ruby and RUBY_ENGINE matches /^ruby/" do
+ stub_const 'RUBY_ENGINE', 'ruby'
+ PlatformGuard.implementation?(:ruby).should == true
+
+ stub_const 'RUBY_ENGINE', 'ruby1.8'
+ PlatformGuard.implementation?(:ruby).should == true
+
+ stub_const 'RUBY_ENGINE', 'ruby1.9'
+ PlatformGuard.implementation?(:ruby).should == true
+ end
+
+ it "raises an error when passed an unrecognized name" do
+ stub_const 'RUBY_ENGINE', 'ruby'
+ lambda {
+ PlatformGuard.implementation?(:python)
+ }.should raise_error(/unknown implementation/)
+ end
+end
+
+describe PlatformGuard, ".standard?" do
+ it "returns true if implementation? returns true" do
+ PlatformGuard.should_receive(:implementation?).with(:ruby).and_return(true)
+ PlatformGuard.standard?.should be_true
+ end
+
+ it "returns false if implementation? returns false" do
+ PlatformGuard.should_receive(:implementation?).with(:ruby).and_return(false)
+ PlatformGuard.standard?.should be_false
+ end
+end
+
+describe PlatformGuard, ".wordsize?" do
+ it "returns true when arg is 32 and 1.size is 4" do
+ PlatformGuard.wordsize?(32).should == (1.size == 4)
+ end
+
+ it "returns true when arg is 64 and 1.size is 8" do
+ PlatformGuard.wordsize?(64).should == (1.size == 8)
+ end
+end
+
+describe PlatformGuard, ".os?" do
+ before :each do
+ stub_const 'PlatformGuard::HOST_OS', 'solarce'
+ end
+
+ it "returns false when arg does not match the platform" do
+ PlatformGuard.os?(:ruby).should == false
+ end
+
+ it "returns false when no arg matches the platform" do
+ PlatformGuard.os?(:ruby, :jruby, :rubinius, :maglev).should == false
+ end
+
+ it "returns true when arg matches the platform" do
+ PlatformGuard.os?(:solarce).should == true
+ end
+
+ it "returns true when any arg matches the platform" do
+ PlatformGuard.os?(:ruby, :jruby, :solarce, :rubinius, :maglev).should == true
+ end
+
+ it "returns true when arg is :windows and the platform contains 'mswin'" do
+ stub_const 'PlatformGuard::HOST_OS', 'mswin32'
+ PlatformGuard.os?(:windows).should == true
+ end
+
+ it "returns true when arg is :windows and the platform contains 'mingw'" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32'
+ PlatformGuard.os?(:windows).should == true
+ end
+
+ it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mswin'" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mswin32'
+ PlatformGuard.os?(:linux).should == false
+ end
+
+ it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mingw'" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32'
+ PlatformGuard.os?(:linux).should == false
+ end
+end
+
+describe PlatformGuard, ".os? on JRuby" do
+ before :all do
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ after :all do
+ $VERBOSE = @verbose
+ end
+
+ before :each do
+ @ruby_platform = Object.const_get :RUBY_PLATFORM
+ Object.const_set :RUBY_PLATFORM, 'java'
+ end
+
+ after :each do
+ Object.const_set :RUBY_PLATFORM, @ruby_platform
+ end
+
+ it "raises an error when testing for a :java platform" do
+ lambda {
+ PlatformGuard.os?(:java)
+ }.should raise_error(":java is not a valid OS")
+ end
+
+ it "returns true when arg is :windows and RUBY_PLATFORM contains 'java' and os?(:windows) is true" do
+ stub_const 'PlatformGuard::HOST_OS', 'mswin32'
+ PlatformGuard.os?(:windows).should == true
+ end
+
+ it "returns true when RUBY_PLATFORM contains 'java' and os?(argument) is true" do
+ stub_const 'PlatformGuard::HOST_OS', 'amiga'
+ PlatformGuard.os?(:amiga).should == true
+ end
+end
+
+describe PlatformGuard, ".os?" do
+ before :each do
+ stub_const 'PlatformGuard::HOST_OS', 'unreal'
+ end
+
+ it "returns true if argument matches RbConfig::CONFIG['host_os']" do
+ PlatformGuard.os?(:unreal).should == true
+ end
+
+ it "returns true if any argument matches RbConfig::CONFIG['host_os']" do
+ PlatformGuard.os?(:bsd, :unreal, :amiga).should == true
+ end
+
+ it "returns false if no argument matches RbConfig::CONFIG['host_os']" do
+ PlatformGuard.os?(:bsd, :netbsd, :amiga, :msdos).should == false
+ end
+
+ it "returns false if argument does not match RbConfig::CONFIG['host_os']" do
+ PlatformGuard.os?(:amiga).should == false
+ end
+
+ it "returns true when arg is :windows and RbConfig::CONFIG['host_os'] contains 'mswin'" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mswin32'
+ PlatformGuard.os?(:windows).should == true
+ end
+
+ it "returns true when arg is :windows and RbConfig::CONFIG['host_os'] contains 'mingw'" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32'
+ PlatformGuard.os?(:windows).should == true
+ end
+
+ it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mswin'" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32'
+ PlatformGuard.os?(:linux).should == false
+ end
+
+ it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mingw'" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32'
+ PlatformGuard.os?(:linux).should == false
+ end
+end
+
+describe PlatformGuard, ".windows?" do
+ it "returns true on windows" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32'
+ PlatformGuard.windows?.should == true
+ end
+
+ it "returns false on non-windows" do
+ stub_const 'PlatformGuard::HOST_OS', 'i586-linux'
+ PlatformGuard.windows?.should == false
+ end
+end
diff --git a/spec/mspec/spec/guards/quarantine_spec.rb b/spec/mspec/spec/guards/quarantine_spec.rb
new file mode 100644
index 0000000000..e5c7da7939
--- /dev/null
+++ b/spec/mspec/spec/guards/quarantine_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe QuarantineGuard, "#match?" do
+ it "returns true" do
+ QuarantineGuard.new.match?.should == true
+ end
+end
+
+describe Object, "#quarantine!" do
+ before :each do
+ ScratchPad.clear
+
+ @guard = QuarantineGuard.new
+ QuarantineGuard.stub(:new).and_return(@guard)
+ end
+
+ it "does not yield" do
+ quarantine! { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "sets the name of the guard to :quarantine!" do
+ quarantine! { }
+ @guard.name.should == :quarantine!
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(false)
+ @guard.should_receive(:unregister)
+ lambda do
+ quarantine! { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
diff --git a/spec/mspec/spec/guards/superuser_spec.rb b/spec/mspec/spec/guards/superuser_spec.rb
new file mode 100644
index 0000000000..f8815057e1
--- /dev/null
+++ b/spec/mspec/spec/guards/superuser_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe Object, "#as_superuser" do
+ before :each do
+ @guard = SuperUserGuard.new
+ SuperUserGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "does not yield when Process.euid is not 0" do
+ Process.stub(:euid).and_return(501)
+ as_superuser { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "yields when Process.euid is 0" do
+ Process.stub(:euid).and_return(0)
+ as_superuser { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "sets the name of the guard to :as_superuser" do
+ as_superuser { }
+ @guard.name.should == :as_superuser
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(true)
+ @guard.should_receive(:unregister)
+ lambda do
+ as_superuser { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
diff --git a/spec/mspec/spec/guards/support_spec.rb b/spec/mspec/spec/guards/support_spec.rb
new file mode 100644
index 0000000000..38414abebd
--- /dev/null
+++ b/spec/mspec/spec/guards/support_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe Object, "#not_supported_on" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "raises an Exception when passed :ruby" do
+ stub_const "RUBY_ENGINE", "jruby"
+ lambda {
+ not_supported_on(:ruby) { ScratchPad.record :yield }
+ }.should raise_error(Exception)
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "does not yield when #implementation? returns true" do
+ stub_const "RUBY_ENGINE", "jruby"
+ not_supported_on(:jruby) { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "yields when #standard? returns true" do
+ stub_const "RUBY_ENGINE", "ruby"
+ not_supported_on(:rubinius) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "yields when #implementation? returns false" do
+ stub_const "RUBY_ENGINE", "jruby"
+ not_supported_on(:rubinius) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+end
+
+describe Object, "#not_supported_on" do
+ before :each do
+ @guard = SupportedGuard.new
+ SupportedGuard.stub(:new).and_return(@guard)
+ end
+
+ it "sets the name of the guard to :not_supported_on" do
+ not_supported_on(:rubinius) { }
+ @guard.name.should == :not_supported_on
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(false)
+ @guard.should_receive(:unregister)
+ lambda do
+ not_supported_on(:rubinius) { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
diff --git a/spec/mspec/spec/guards/user_spec.rb b/spec/mspec/spec/guards/user_spec.rb
new file mode 100644
index 0000000000..2de4db7390
--- /dev/null
+++ b/spec/mspec/spec/guards/user_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe Object, "#as_user" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "yields when the Process.euid is not 0" do
+ Process.stub(:euid).and_return(501)
+ as_user { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield when the Process.euid is 0" do
+ Process.stub(:euid).and_return(0)
+ as_user { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+end
diff --git a/spec/mspec/spec/guards/version_spec.rb b/spec/mspec/spec/guards/version_spec.rb
new file mode 100644
index 0000000000..07eb451ec9
--- /dev/null
+++ b/spec/mspec/spec/guards/version_spec.rb
@@ -0,0 +1,90 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+# The VersionGuard specifies a version of Ruby with a String of
+# the form: v = 'major.minor.tiny'.
+#
+# A VersionGuard instance can be created with a single String,
+# which means any version >= each component of v.
+# Or, the guard can be created with a Range, a..b, or a...b,
+# where a, b are of the same form as v. The meaning of the Range
+# is as typically understood: a..b means v >= a and v <= b;
+# a...b means v >= a and v < b.
+
+describe VersionGuard, "#match?" do
+ before :each do
+ hide_deprecation_warnings
+ stub_const "VersionGuard::FULL_RUBY_VERSION", SpecVersion.new('1.8.6')
+ end
+
+ it "returns true when the argument is equal to RUBY_VERSION" do
+ VersionGuard.new('1.8.6').match?.should == true
+ end
+
+ it "returns true when the argument is less than RUBY_VERSION" do
+ VersionGuard.new('1.8').match?.should == true
+ VersionGuard.new('1.8.5').match?.should == true
+ end
+
+ it "returns false when the argument is greater than RUBY_VERSION" do
+ VersionGuard.new('1.8.7').match?.should == false
+ VersionGuard.new('1.9.2').match?.should == false
+ end
+
+ it "returns true when the argument range includes RUBY_VERSION" do
+ VersionGuard.new('1.8.5'..'1.8.7').match?.should == true
+ VersionGuard.new('1.8'..'1.9').match?.should == true
+ VersionGuard.new('1.8'...'1.9').match?.should == true
+ VersionGuard.new('1.8'..'1.8.6').match?.should == true
+ VersionGuard.new('1.8.5'..'1.8.6').match?.should == true
+ VersionGuard.new(''...'1.8.7').match?.should == true
+ end
+
+ it "returns false when the argument range does not include RUBY_VERSION" do
+ VersionGuard.new('1.8.7'..'1.8.9').match?.should == false
+ VersionGuard.new('1.8.4'..'1.8.5').match?.should == false
+ VersionGuard.new('1.8.4'...'1.8.6').match?.should == false
+ VersionGuard.new('1.8.5'...'1.8.6').match?.should == false
+ VersionGuard.new(''...'1.8.6').match?.should == false
+ end
+end
+
+describe Object, "#ruby_version_is" do
+ before :each do
+ @guard = VersionGuard.new 'x.x.x'
+ VersionGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "yields when #match? returns true" do
+ @guard.stub(:match?).and_return(true)
+ ruby_version_is('x.x.x') { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield when #match? returns false" do
+ @guard.stub(:match?).and_return(false)
+ ruby_version_is('x.x.x') { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "returns what #match? returns when no block is given" do
+ @guard.stub(:match?).and_return(true)
+ ruby_version_is('x.x.x').should == true
+ @guard.stub(:match?).and_return(false)
+ ruby_version_is('x.x.x').should == false
+ end
+
+ it "sets the name of the guard to :ruby_version_is" do
+ ruby_version_is("") { }
+ @guard.name.should == :ruby_version_is
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(true)
+ @guard.should_receive(:unregister)
+ lambda do
+ ruby_version_is("") { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
diff --git a/spec/mspec/spec/helpers/argf_spec.rb b/spec/mspec/spec/helpers/argf_spec.rb
new file mode 100644
index 0000000000..cf5eb0fe88
--- /dev/null
+++ b/spec/mspec/spec/helpers/argf_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#argf" do
+ before :each do
+ @saved_argv = ARGV.dup
+ @argv = [__FILE__]
+ end
+
+ it "sets @argf to an instance of ARGF.class with the given argv" do
+ argf @argv do
+ @argf.should be_an_instance_of ARGF.class
+ @argf.filename.should == @argv.first
+ end
+ @argf.should be_nil
+ end
+
+ it "does not alter ARGV nor ARGF" do
+ argf @argv do
+ end
+ ARGV.should == @saved_argv
+ ARGF.argv.should == @saved_argv
+ end
+
+ it "does not close STDIN" do
+ argf ['-'] do
+ end
+ STDIN.should_not be_closed
+ end
+
+ it "disallows nested calls" do
+ argf @argv do
+ lambda { argf @argv }.should raise_error
+ end
+ end
+end
diff --git a/spec/mspec/spec/helpers/argv_spec.rb b/spec/mspec/spec/helpers/argv_spec.rb
new file mode 100644
index 0000000000..c3b21c7639
--- /dev/null
+++ b/spec/mspec/spec/helpers/argv_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#argv" do
+ before :each do
+ ScratchPad.clear
+
+ @saved_argv = ARGV.dup
+ @argv = ["a", "b"]
+ end
+
+ it "replaces and restores the value of ARGV" do
+ argv @argv
+ ARGV.should == @argv
+ argv :restore
+ ARGV.should == @saved_argv
+ end
+
+ it "yields to the block after setting ARGV" do
+ argv @argv do
+ ScratchPad.record ARGV.dup
+ end
+ ScratchPad.recorded.should == @argv
+ ARGV.should == @saved_argv
+ end
+end
diff --git a/spec/mspec/spec/helpers/datetime_spec.rb b/spec/mspec/spec/helpers/datetime_spec.rb
new file mode 100644
index 0000000000..8696c0c9c7
--- /dev/null
+++ b/spec/mspec/spec/helpers/datetime_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#new_datetime" do
+ it "returns a default DateTime instance" do
+ new_datetime.should == DateTime.new
+ end
+
+ it "returns a DateTime instance with the specified year value" do
+ d = new_datetime :year => 1970
+ d.year.should == 1970
+ end
+
+ it "returns a DateTime instance with the specified month value" do
+ d = new_datetime :month => 11
+ d.mon.should == 11
+ end
+
+ it "returns a DateTime instance with the specified day value" do
+ d = new_datetime :day => 23
+ d.day.should == 23
+ end
+
+ it "returns a DateTime instance with the specified hour value" do
+ d = new_datetime :hour => 10
+ d.hour.should == 10
+ end
+
+ it "returns a DateTime instance with the specified minute value" do
+ d = new_datetime :minute => 10
+ d.min.should == 10
+ end
+
+ it "returns a DateTime instance with the specified second value" do
+ d = new_datetime :second => 2
+ d.sec.should == 2
+ end
+
+ it "returns a DateTime instance with the specified offset value" do
+ d = new_datetime :offset => Rational(3,24)
+ d.offset.should == Rational(3,24)
+ end
+end
diff --git a/spec/mspec/spec/helpers/fixture_spec.rb b/spec/mspec/spec/helpers/fixture_spec.rb
new file mode 100644
index 0000000000..4dbdd092f1
--- /dev/null
+++ b/spec/mspec/spec/helpers/fixture_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#fixture" do
+ before :each do
+ @dir = File.realpath("..", __FILE__)
+ end
+
+ it "returns the expanded path to a fixture file" do
+ name = fixture(__FILE__, "subdir", "file.txt")
+ name.should == "#{@dir}/fixtures/subdir/file.txt"
+ end
+
+ it "omits '/shared' if it is the suffix of the directory string" do
+ name = fixture("#{@dir}/shared/file.rb", "subdir", "file.txt")
+ name.should == "#{@dir}/fixtures/subdir/file.txt"
+ end
+
+ it "does not append '/fixtures' if it is the suffix of the directory string" do
+ commands_dir = "#{File.dirname(@dir)}/commands"
+ name = fixture("#{commands_dir}/fixtures/file.rb", "subdir", "file.txt")
+ name.should == "#{commands_dir}/fixtures/subdir/file.txt"
+ end
+end
diff --git a/spec/mspec/spec/helpers/flunk_spec.rb b/spec/mspec/spec/helpers/flunk_spec.rb
new file mode 100644
index 0000000000..7b1216d3f7
--- /dev/null
+++ b/spec/mspec/spec/helpers/flunk_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/runner/mspec'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#flunk" do
+ before :each do
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ end
+
+ it "raises an SpecExpectationNotMetError unconditionally" do
+ lambda { flunk }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "accepts on argument for an optional message" do
+ lambda {flunk "test"}.should raise_error(SpecExpectationNotMetError)
+ end
+end
diff --git a/spec/mspec/spec/helpers/fs_spec.rb b/spec/mspec/spec/helpers/fs_spec.rb
new file mode 100644
index 0000000000..e40c6c5607
--- /dev/null
+++ b/spec/mspec/spec/helpers/fs_spec.rb
@@ -0,0 +1,195 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#cp" do
+ before :each do
+ @source = tmp("source.txt")
+ @copy = tmp("copied.txt")
+
+ @contents = "This is a copy."
+ File.open(@source, "w") { |f| f.write @contents }
+ end
+
+ after :each do
+ File.delete @source if File.exist? @source
+ File.delete @copy if File.exist? @copy
+ end
+
+ it "copies a file" do
+ cp @source, @copy
+ data = IO.read(@copy)
+ data.should == @contents
+ data.should == IO.read(@source)
+ end
+end
+
+describe Object, "#touch" do
+ before :all do
+ @name = tmp("touched.txt")
+ end
+
+ after :each do
+ File.delete @name if File.exist? @name
+ end
+
+ it "creates a file" do
+ touch @name
+ File.exist?(@name).should be_true
+ end
+
+ it "accepts an optional mode argument" do
+ touch @name, "wb"
+ File.exist?(@name).should be_true
+ end
+
+ it "overwrites an existing file" do
+ File.open(@name, "w") { |f| f.puts "used" }
+ File.size(@name).should > 0
+
+ touch @name
+ File.size(@name).should == 0
+ end
+
+ it "yields the open file if passed a block" do
+ touch(@name) { |f| f.write "touching" }
+ IO.read(@name).should == "touching"
+ end
+end
+
+describe Object, "#touch" do
+ before :all do
+ @name = tmp("subdir/touched.txt")
+ end
+
+ after :each do
+ rm_r File.dirname(@name)
+ end
+
+ it "creates all the directories in the path to the file" do
+ touch @name
+ File.exist?(@name).should be_true
+ end
+end
+
+describe Object, "#mkdir_p" do
+ before :all do
+ @dir1 = tmp("/nested")
+ @dir2 = @dir1 + "/directory"
+ @paths = [ @dir2, @dir1 ]
+ end
+
+ after :each do
+ File.delete @dir1 if File.file? @dir1
+ @paths.each { |path| Dir.rmdir path if File.directory? path }
+ end
+
+ it "creates all the directories in a path" do
+ mkdir_p @dir2
+ File.directory?(@dir2).should be_true
+ end
+
+ it "raises an ArgumentError if a path component is a file" do
+ File.open(@dir1, "w") { |f| }
+ lambda { mkdir_p @dir2 }.should raise_error(ArgumentError)
+ end
+
+ it "works if multiple processes try to create the same directory concurrently" do
+ original = File.method(:directory?)
+ File.should_receive(:directory?).at_least(:once) { |dir|
+ ret = original.call(dir)
+ if !ret and dir == @dir1
+ Dir.mkdir(dir) # Simulate race
+ end
+ ret
+ }
+ mkdir_p @dir1
+ original.call(@dir1).should be_true
+ end
+end
+
+describe Object, "#rm_r" do
+ before :all do
+ @topdir = tmp("rm_r_tree")
+ @topfile = @topdir + "/file.txt"
+ @link = @topdir + "/file.lnk"
+ @socket = @topdir + "/socket.sck"
+ @subdir1 = @topdir + "/subdir1"
+ @subdir2 = @subdir1 + "/subdir2"
+ @subfile = @subdir1 + "/subfile.txt"
+ end
+
+ before :each do
+ mkdir_p @subdir2
+ touch @topfile
+ touch @subfile
+ end
+
+ after :each do
+ File.delete @link if File.exist? @link or File.symlink? @link
+ File.delete @socket if File.exist? @socket
+ File.delete @subfile if File.exist? @subfile
+ File.delete @topfile if File.exist? @topfile
+
+ Dir.rmdir @subdir2 if File.directory? @subdir2
+ Dir.rmdir @subdir1 if File.directory? @subdir1
+ Dir.rmdir @topdir if File.directory? @topdir
+ end
+
+ it "raises an ArgumentError if the path is not prefixed by MSPEC_RM_PREFIX" do
+ lambda { rm_r "some_file.txt" }.should raise_error(ArgumentError)
+ end
+
+ it "removes a single file" do
+ rm_r @subfile
+ File.exist?(@subfile).should be_false
+ end
+
+ it "removes multiple files" do
+ rm_r @topfile, @subfile
+ File.exist?(@topfile).should be_false
+ File.exist?(@subfile).should be_false
+ end
+
+ platform_is_not :windows do
+ it "removes a symlink to a file" do
+ File.symlink @topfile, @link
+ rm_r @link
+ File.exist?(@link).should be_false
+ end
+
+ it "removes a symlink to a directory" do
+ File.symlink @subdir1, @link
+ rm_r @link
+ lambda do
+ File.lstat(@link)
+ end.should raise_error(Errno::ENOENT)
+ File.exist?(@subdir1).should be_true
+ end
+
+ it "removes a dangling symlink" do
+ File.symlink "non_existent_file", @link
+ rm_r @link
+ lambda do
+ File.lstat(@link)
+ end.should raise_error(Errno::ENOENT)
+ end
+
+ it "removes a socket" do
+ require 'socket'
+ UNIXServer.new(@socket).close
+ rm_r @socket
+ File.exist?(@socket).should be_false
+ end
+ end
+
+ it "removes a single directory" do
+ rm_r @subdir2
+ File.directory?(@subdir2).should be_false
+ end
+
+ it "recursively removes a directory tree" do
+ rm_r @topdir
+ File.directory?(@topdir).should be_false
+ end
+end
diff --git a/spec/mspec/spec/helpers/io_spec.rb b/spec/mspec/spec/helpers/io_spec.rb
new file mode 100644
index 0000000000..3219f59947
--- /dev/null
+++ b/spec/mspec/spec/helpers/io_spec.rb
@@ -0,0 +1,174 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe IOStub do
+ before :each do
+ @out = IOStub.new
+ @sep = $\
+ end
+
+ after :each do
+ $\ = @sep
+ end
+
+ it "provides a write method" do
+ @out.write "this"
+ @out.should == "this"
+ end
+
+ it "concatenates the arguments sent to write" do
+ @out.write "flim ", "flam"
+ @out.should == "flim flam"
+ end
+
+ it "provides a print method that appends the default separator" do
+ $\ = " [newline] "
+ @out.print "hello"
+ @out.print "world"
+ @out.should == "hello [newline] world [newline] "
+ end
+
+ it "provides a puts method that appends the default separator" do
+ @out.puts "hello", 1, 2, 3
+ @out.should == "hello\n1\n2\n3\n"
+ end
+
+ it "provides a puts method that appends separator if argument not given" do
+ @out.puts
+ @out.should == "\n"
+ end
+
+ it "provides a printf method" do
+ @out.printf "%-10s, %03d, %2.1f", "test", 42, 4.2
+ @out.should == "test , 042, 4.2"
+ end
+
+ it "provides a flush method that does nothing and returns self" do
+ @out.flush.should == @out
+ end
+end
+
+describe Object, "#new_fd" do
+ before :each do
+ @name = tmp("io_specs")
+ @io = nil
+ end
+
+ after :each do
+ @io.close if @io and not @io.closed?
+ rm_r @name
+ end
+
+ it "returns a Integer that can be used to create an IO instance" do
+ fd = new_fd @name
+ fd.should be_kind_of(Integer)
+
+ @io = IO.new fd, fmode('w:utf-8')
+ @io.sync = true
+ @io.print "io data"
+
+ IO.read(@name).should == "io data"
+ end
+
+ it "accepts an options Hash" do
+ FeatureGuard.stub(:enabled?).and_return(true)
+ fd = new_fd @name, { :mode => 'w:utf-8' }
+ fd.should be_kind_of(Integer)
+
+ @io = IO.new fd, fmode('w:utf-8')
+ @io.sync = true
+ @io.print "io data"
+
+ IO.read(@name).should == "io data"
+ end
+
+ it "raises an ArgumentError if the options Hash does not include :mode" do
+ FeatureGuard.stub(:enabled?).and_return(true)
+ lambda { new_fd @name, { :encoding => "utf-8" } }.should raise_error(ArgumentError)
+ end
+end
+
+describe Object, "#new_io" do
+ before :each do
+ @name = tmp("io_specs.txt")
+ end
+
+ after :each do
+ @io.close if @io and !@io.closed?
+ rm_r @name
+ end
+
+ it "returns an IO instance" do
+ @io = new_io @name
+ @io.should be_an_instance_of(IO)
+ end
+
+ it "opens the IO for reading if passed 'r'" do
+ touch(@name) { |f| f.print "io data" }
+ @io = new_io @name, "r"
+ @io.read.should == "io data"
+ lambda { @io.puts "more data" }.should raise_error(IOError)
+ end
+
+ it "opens the IO for writing if passed 'w'" do
+ @io = new_io @name, "w"
+ @io.sync = true
+
+ @io.print "io data"
+ IO.read(@name).should == "io data"
+ end
+
+ it "opens the IO for reading if passed { :mode => 'r' }" do
+ touch(@name) { |f| f.print "io data" }
+ @io = new_io @name, { :mode => "r" }
+ @io.read.should == "io data"
+ lambda { @io.puts "more data" }.should raise_error(IOError)
+ end
+
+ it "opens the IO for writing if passed { :mode => 'w' }" do
+ @io = new_io @name, { :mode => "w" }
+ @io.sync = true
+
+ @io.print "io data"
+ IO.read(@name).should == "io data"
+ end
+end
+
+describe Object, "#fmode" do
+ it "returns the argument unmodified if :encoding feature is enabled" do
+ FeatureGuard.should_receive(:enabled?).with(:encoding).and_return(true)
+ fmode("rb:binary:utf-8").should == "rb:binary:utf-8"
+ end
+
+ it "returns only the file access mode if :encoding feature is not enabled" do
+ FeatureGuard.should_receive(:enabled?).with(:encoding).and_return(false)
+ fmode("rb:binary:utf-8").should == "rb"
+ end
+end
+
+describe Object, "#options_or_mode" do
+ describe "if passed a Hash" do
+ it "returns a mode string if :encoding feature is not enabled" do
+ FeatureGuard.should_receive(:enabled?).with(:encoding).twice.and_return(false)
+ options_or_mode(:mode => "rb:binary").should == "rb"
+ end
+
+ it "returns a Hash if :encoding feature is enabled" do
+ FeatureGuard.should_receive(:enabled?).with(:encoding).and_return(true)
+ options_or_mode(:mode => "rb:utf-8").should == { :mode => "rb:utf-8" }
+ end
+ end
+
+ describe "if passed a String" do
+ it "returns only the file access mode if :encoding feature is not enabled" do
+ FeatureGuard.should_receive(:enabled?).with(:encoding).and_return(false)
+ options_or_mode("rb:binary:utf-8").should == "rb"
+ end
+
+ it "returns the argument unmodified if :encoding feature is enabled" do
+ FeatureGuard.should_receive(:enabled?).with(:encoding).and_return(true)
+ options_or_mode("rb:binary:utf-8").should == "rb:binary:utf-8"
+ end
+ end
+end
diff --git a/spec/mspec/spec/helpers/mock_to_path_spec.rb b/spec/mspec/spec/helpers/mock_to_path_spec.rb
new file mode 100644
index 0000000000..464e7e5440
--- /dev/null
+++ b/spec/mspec/spec/helpers/mock_to_path_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#mock_to_path" do
+ it "returns an object that responds to #to_path" do
+ obj = mock_to_path("foo")
+ obj.should be_a(MockObject)
+ obj.should respond_to(:to_path)
+ obj.to_path
+ end
+
+ it "returns the provided path when #to_path is called" do
+ obj = mock_to_path("/tmp/foo")
+ obj.to_path.should == "/tmp/foo"
+ end
+end
diff --git a/spec/mspec/spec/helpers/numeric_spec.rb b/spec/mspec/spec/helpers/numeric_spec.rb
new file mode 100644
index 0000000000..2ea56e5961
--- /dev/null
+++ b/spec/mspec/spec/helpers/numeric_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#bignum_value" do
+ it "returns a value that is an instance of Bignum on any platform" do
+ bignum_value.should == 0x8000_0000_0000_0000
+ end
+
+ it "returns the default value incremented by the argument" do
+ bignum_value(42).should == 0x8000_0000_0000_002a
+ end
+end
+
+describe Object, "#nan_value" do
+ it "returns NaN" do
+ nan_value.nan?.should be_true
+ end
+end
+
+describe Object, "#infinity_value" do
+ it "returns Infinity" do
+ infinity_value.infinite?.should == 1
+ end
+end
diff --git a/spec/mspec/spec/helpers/ruby_exe_spec.rb b/spec/mspec/spec/helpers/ruby_exe_spec.rb
new file mode 100644
index 0000000000..8036043578
--- /dev/null
+++ b/spec/mspec/spec/helpers/ruby_exe_spec.rb
@@ -0,0 +1,197 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+require 'rbconfig'
+
+class RubyExeSpecs
+ public :ruby_exe_options
+ public :resolve_ruby_exe
+ public :ruby_cmd
+ public :ruby_exe
+end
+
+describe "#ruby_exe_options" do
+ before :each do
+ @ruby_exe_env = ENV['RUBY_EXE']
+ @script = RubyExeSpecs.new
+ end
+
+ after :each do
+ ENV['RUBY_EXE'] = @ruby_exe_env
+ end
+
+ it "returns ENV['RUBY_EXE'] when passed :env" do
+ ENV['RUBY_EXE'] = "kowabunga"
+ @script.ruby_exe_options(:env).should == "kowabunga"
+ end
+
+ it "returns 'bin/jruby' when passed :engine and RUBY_ENGINE is 'jruby'" do
+ stub_const "RUBY_ENGINE", 'jruby'
+ @script.ruby_exe_options(:engine).should == 'bin/jruby'
+ end
+
+ it "returns 'bin/rbx' when passed :engine, RUBY_ENGINE is 'rbx'" do
+ stub_const "RUBY_ENGINE", 'rbx'
+ @script.ruby_exe_options(:engine).should == 'bin/rbx'
+ end
+
+ it "returns 'ir' when passed :engine and RUBY_ENGINE is 'ironruby'" do
+ stub_const "RUBY_ENGINE", 'ironruby'
+ @script.ruby_exe_options(:engine).should == 'ir'
+ end
+
+ it "returns 'maglev-ruby' when passed :engine and RUBY_ENGINE is 'maglev'" do
+ stub_const "RUBY_ENGINE", 'maglev'
+ @script.ruby_exe_options(:engine).should == 'maglev-ruby'
+ end
+
+ it "returns 'topaz' when passed :engine and RUBY_ENGINE is 'topaz'" do
+ stub_const "RUBY_ENGINE", 'topaz'
+ @script.ruby_exe_options(:engine).should == 'topaz'
+ end
+
+ it "returns RUBY_ENGINE + $(EXEEXT) when passed :name" do
+ bin = RUBY_ENGINE + (RbConfig::CONFIG['EXEEXT'] || RbConfig::CONFIG['exeext'] || '')
+ name = File.join ".", bin
+ @script.ruby_exe_options(:name).should == name
+ end
+
+ it "returns $(bindir)/$(RUBY_INSTALL_NAME) + $(EXEEXT) when passed :install_name" do
+ bin = RbConfig::CONFIG['RUBY_INSTALL_NAME'] + (RbConfig::CONFIG['EXEEXT'] || RbConfig::CONFIG['exeext'] || '')
+ name = File.join RbConfig::CONFIG['bindir'], bin
+ @script.ruby_exe_options(:install_name).should == name
+ end
+end
+
+describe "#resolve_ruby_exe" do
+ before :each do
+ @name = "ruby_spec_exe"
+ @script = RubyExeSpecs.new
+ end
+
+ it "returns the value returned by #ruby_exe_options if it exists and is executable" do
+ @script.should_receive(:ruby_exe_options).and_return(@name)
+ File.should_receive(:file?).with(@name).and_return(true)
+ File.should_receive(:executable?).with(@name).and_return(true)
+ File.should_receive(:expand_path).with(@name).and_return(@name)
+ @script.resolve_ruby_exe.should == @name
+ end
+
+ it "expands the path portion of the result of #ruby_exe_options" do
+ @script.should_receive(:ruby_exe_options).and_return("#{@name}")
+ File.should_receive(:file?).with(@name).and_return(true)
+ File.should_receive(:executable?).with(@name).and_return(true)
+ File.should_receive(:expand_path).with(@name).and_return("/usr/bin/#{@name}")
+ @script.resolve_ruby_exe.should == "/usr/bin/#{@name}"
+ end
+
+ it "adds the flags after the executable" do
+ @name = 'bin/rbx'
+ @script.should_receive(:ruby_exe_options).and_return(@name)
+ File.should_receive(:file?).with(@name).and_return(true)
+ File.should_receive(:executable?).with(@name).and_return(true)
+ File.should_receive(:expand_path).with(@name).and_return(@name)
+
+ ENV.should_receive(:[]).with("RUBY_FLAGS").and_return('-X19')
+ @script.resolve_ruby_exe.should == 'bin/rbx -X19'
+ end
+
+ it "raises an exception if no exe is found" do
+ File.should_receive(:file?).at_least(:once).and_return(false)
+ lambda {
+ @script.resolve_ruby_exe
+ }.should raise_error(Exception)
+ end
+end
+
+describe Object, "#ruby_cmd" do
+ before :each do
+ stub_const 'RUBY_EXE', 'ruby_spec_exe -w -Q'
+
+ @file = "some/ruby/file.rb"
+ @code = %(some "real" 'ruby' code)
+
+ @script = RubyExeSpecs.new
+ end
+
+ it "returns a command that runs the given file if it is a file that exists" do
+ File.should_receive(:exist?).with(@file).and_return(true)
+ @script.ruby_cmd(@file).should == "ruby_spec_exe -w -Q some/ruby/file.rb"
+ end
+
+ it "includes the given options and arguments with a file" do
+ File.should_receive(:exist?).with(@file).and_return(true)
+ @script.ruby_cmd(@file, :options => "-w -Cdir", :args => "< file.txt").should ==
+ "ruby_spec_exe -w -Q -w -Cdir some/ruby/file.rb < file.txt"
+ end
+
+ it "includes the given options and arguments with -e" do
+ File.should_receive(:exist?).with(@code).and_return(false)
+ @script.ruby_cmd(@code, :options => "-W0 -Cdir", :args => "< file.txt").should ==
+ %(ruby_spec_exe -w -Q -W0 -Cdir -e "some \\"real\\" 'ruby' code" < file.txt)
+ end
+
+ it "returns a command with options and arguments but without code or file" do
+ @script.ruby_cmd(nil, :options => "-c", :args => "> file.txt").should ==
+ "ruby_spec_exe -w -Q -c > file.txt"
+ end
+end
+
+describe Object, "#ruby_exe" do
+ before :each do
+ stub_const 'RUBY_EXE', 'ruby_spec_exe -w -Q'
+
+ @script = RubyExeSpecs.new
+ @script.stub(:`)
+ end
+
+ it "returns an Array containing the interpreter executable and flags when given no arguments" do
+ @script.ruby_exe.should == ['ruby_spec_exe', '-w', '-Q']
+ end
+
+ it "executes (using `) the result of calling #ruby_cmd with the given arguments" do
+ code = "code"
+ options = {}
+ @script.should_receive(:ruby_cmd).and_return("ruby_cmd")
+ @script.should_receive(:`).with("ruby_cmd")
+ @script.ruby_exe(code, options)
+ end
+
+ describe "with :dir option" do
+ it "is deprecated" do
+ lambda {
+ @script.ruby_exe nil, :dir => "tmp"
+ }.should raise_error(/no longer supported, use Dir\.chdir/)
+ end
+ end
+
+ describe "with :env option" do
+ it "preserves the values of existing ENV keys" do
+ ENV["ABC"] = "123"
+ ENV.stub(:[])
+ ENV.should_receive(:[]).with("ABC")
+ @script.ruby_exe nil, :env => { :ABC => "xyz" }
+ end
+
+ it "adds the :env entries to ENV" do
+ ENV.should_receive(:[]=).with("ABC", "xyz")
+ @script.ruby_exe nil, :env => { :ABC => "xyz" }
+ end
+
+ it "deletes the :env entries in ENV when an exception is raised" do
+ ENV.should_receive(:delete).with("XYZ")
+ @script.ruby_exe nil, :env => { :XYZ => "xyz" }
+ end
+
+ it "resets the values of existing ENV keys when an exception is raised" do
+ ENV["ABC"] = "123"
+ ENV.should_receive(:[]=).with("ABC", "xyz")
+ ENV.should_receive(:[]=).with("ABC", "123")
+
+ @script.should_receive(:`).and_raise(Exception)
+ lambda do
+ @script.ruby_exe nil, :env => { :ABC => "xyz" }
+ end.should raise_error(Exception)
+ end
+ end
+end
diff --git a/spec/mspec/spec/helpers/scratch_spec.rb b/spec/mspec/spec/helpers/scratch_spec.rb
new file mode 100644
index 0000000000..6a9eb2cf73
--- /dev/null
+++ b/spec/mspec/spec/helpers/scratch_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe ScratchPad do
+ it "records an object and returns a previously recorded object" do
+ ScratchPad.record :this
+ ScratchPad.recorded.should == :this
+ end
+
+ it "clears the recorded object" do
+ ScratchPad.record :that
+ ScratchPad.recorded.should == :that
+ ScratchPad.clear
+ ScratchPad.recorded.should == nil
+ end
+
+ it "provides a convenience shortcut to append to a previously recorded object" do
+ ScratchPad.record []
+ ScratchPad << :new
+ ScratchPad << :another
+ ScratchPad.recorded.should == [:new, :another]
+ end
+end
diff --git a/spec/mspec/spec/helpers/suppress_warning_spec.rb b/spec/mspec/spec/helpers/suppress_warning_spec.rb
new file mode 100644
index 0000000000..d16361fa55
--- /dev/null
+++ b/spec/mspec/spec/helpers/suppress_warning_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#suppress_warning" do
+ it "hides warnings" do
+ suppress_warning do
+ warn "should not be shown"
+ end
+ end
+
+ it "yields the block" do
+ a = 0
+ suppress_warning do
+ a = 1
+ end
+ a.should == 1
+ end
+end
diff --git a/spec/mspec/spec/helpers/tmp_spec.rb b/spec/mspec/spec/helpers/tmp_spec.rb
new file mode 100644
index 0000000000..afadc7f51c
--- /dev/null
+++ b/spec/mspec/spec/helpers/tmp_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#tmp" do
+ before :all do
+ @dir = "#{File.expand_path(Dir.pwd)}/rubyspec_temp"
+ end
+
+ it "returns a name relative to the current working directory" do
+ tmp("test.txt").should == "#{@dir}/#{SPEC_TEMP_UNIQUIFIER}-test.txt"
+ end
+
+ it "returns a 'unique' name on repeated calls" do
+ a = tmp("text.txt")
+ b = tmp("text.txt")
+ a.should_not == b
+ end
+
+ it "does not 'uniquify' the name if requested not to" do
+ tmp("test.txt", false).should == "#{@dir}/test.txt"
+ end
+
+ it "returns the name of the temporary directory when passed an empty string" do
+ tmp("").should == "#{@dir}/"
+ end
+end
diff --git a/spec/mspec/spec/integration/interpreter_spec.rb b/spec/mspec/spec/integration/interpreter_spec.rb
new file mode 100644
index 0000000000..b6fa6859d1
--- /dev/null
+++ b/spec/mspec/spec/integration/interpreter_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+describe "The interpreter passed with -t" do
+ it "is used in subprocess" do
+ fixtures = "spec/fixtures"
+ interpreter = "#{fixtures}/my_ruby"
+ out, ret = run_mspec("run", "#{fixtures}/print_interpreter_spec.rb -t #{interpreter}")
+ out = out.lines.map(&:chomp).reject { |line|
+ line == 'RUBY_DESCRIPTION'
+ }.take(3)
+ out.should == [
+ interpreter,
+ interpreter,
+ "CWD/#{interpreter}"
+ ]
+ ret.success?.should == true
+ end
+end
diff --git a/spec/mspec/spec/integration/object_methods_spec.rb b/spec/mspec/spec/integration/object_methods_spec.rb
new file mode 100644
index 0000000000..3be704beb6
--- /dev/null
+++ b/spec/mspec/spec/integration/object_methods_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+expected_output = <<EOS
+RUBY_DESCRIPTION
+.
+
+Finished in D.DDDDDD seconds
+
+1 file, 1 example, 1 expectation, 0 failures, 0 errors, 0 tagged
+EOS
+
+describe "MSpec" do
+ it "does not define public methods on Object" do
+ out, ret = run_mspec("run", "spec/fixtures/object_methods_spec.rb")
+ out.should == expected_output
+ ret.success?.should == true
+ end
+end
diff --git a/spec/mspec/spec/integration/run_spec.rb b/spec/mspec/spec/integration/run_spec.rb
new file mode 100644
index 0000000000..dc2a9933f9
--- /dev/null
+++ b/spec/mspec/spec/integration/run_spec.rb
@@ -0,0 +1,68 @@
+require 'spec_helper'
+
+describe "Running mspec" do
+ a_spec_output = <<EOS
+
+1)
+Foo#bar errors FAILED
+Expected 1
+ to equal 2
+
+CWD/spec/fixtures/a_spec.rb:8:in `block (2 levels) in <top (required)>'
+CWD/spec/fixtures/a_spec.rb:2:in `<top (required)>'
+CWD/bin/mspec-run:7:in `<main>'
+
+2)
+Foo#bar fails ERROR
+RuntimeError: failure
+CWD/spec/fixtures/a_spec.rb:12:in `block (2 levels) in <top (required)>'
+CWD/spec/fixtures/a_spec.rb:2:in `<top (required)>'
+CWD/bin/mspec-run:7:in `<main>'
+
+Finished in D.DDDDDD seconds
+EOS
+
+ a_stats = "1 file, 3 examples, 2 expectations, 1 failure, 1 error, 0 tagged\n"
+ ab_stats = "2 files, 4 examples, 3 expectations, 1 failure, 1 error, 0 tagged\n"
+ fixtures = "spec/fixtures"
+
+ it "runs the specs" do
+ out, ret = run_mspec("run", "#{fixtures}/a_spec.rb")
+ out.should == "RUBY_DESCRIPTION\n.FE\n#{a_spec_output}\n#{a_stats}"
+ ret.success?.should == false
+ end
+
+ it "directly with mspec-run runs the specs" do
+ out, ret = run_mspec("-run", "#{fixtures}/a_spec.rb")
+ out.should == "RUBY_DESCRIPTION\n.FE\n#{a_spec_output}\n#{a_stats}"
+ ret.success?.should == false
+ end
+
+ it "runs the specs in parallel with -j" do
+ out, ret = run_mspec("run", "-j #{fixtures}/a_spec.rb #{fixtures}/b_spec.rb")
+ progress_bar =
+ "\r[/ | 0% | 00:00:00] \e[0;32m 0F \e[0;32m 0E\e[0m " +
+ "\r[- | ==================50% | 00:00:00] \e[0;32m 0F \e[0;32m 0E\e[0m " +
+ "\r[\\ | ==================100%================== | 00:00:00] \e[0;32m 0F \e[0;32m 0E\e[0m "
+ out.should == "RUBY_DESCRIPTION\n#{progress_bar}\n#{a_spec_output}\n#{ab_stats}"
+ ret.success?.should == false
+ end
+
+ it "gives a useful error message when a subprocess dies in parallel mode" do
+ out, ret = run_mspec("run", "-j #{fixtures}/b_spec.rb #{fixtures}/die_spec.rb")
+ lines = out.lines
+ lines.should include "A child mspec-run process died unexpectedly while running CWD/spec/fixtures/die_spec.rb\n"
+ lines.should include "Finished in D.DDDDDD seconds\n"
+ lines.last.should =~ /^\d files?, \d examples?, \d expectations?, 0 failures, 0 errors, 0 tagged$/
+ ret.success?.should == false
+ end
+
+ it "gives a useful error message when a subprocess prints unexpected output on STDOUT in parallel mode" do
+ out, ret = run_mspec("run", "-j #{fixtures}/b_spec.rb #{fixtures}/chatty_spec.rb")
+ lines = out.lines
+ lines.should include "A child mspec-run process printed unexpected output on STDOUT: #{'"Hello\nIt\'s me!\n"'} while running CWD/spec/fixtures/chatty_spec.rb\n"
+ lines.should include "Finished in D.DDDDDD seconds\n"
+ lines.last.should == "2 files, 2 examples, 2 expectations, 0 failures, 0 errors, 0 tagged\n"
+ ret.success?.should == false
+ end
+end
diff --git a/spec/mspec/spec/integration/tag_spec.rb b/spec/mspec/spec/integration/tag_spec.rb
new file mode 100644
index 0000000000..d980769043
--- /dev/null
+++ b/spec/mspec/spec/integration/tag_spec.rb
@@ -0,0 +1,63 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe "Running mspec tag" do
+ before :all do
+ FileUtils.rm_rf 'spec/fixtures/tags'
+ end
+
+ after :all do
+ FileUtils.rm_rf 'spec/fixtures/tags'
+ end
+
+ it "tags the failing specs" do
+ fixtures = "spec/fixtures"
+ out, ret = run_mspec("tag", "--add fails --fail #{fixtures}/tagging_spec.rb")
+ out.should == <<EOS
+RUBY_DESCRIPTION
+.FF
+TagAction: specs tagged with 'fails':
+
+Tag#me errors
+Tag#me érròrs in unicode
+
+
+1)
+Tag#me errors FAILED
+Expected 1
+ to equal 2
+
+CWD/spec/fixtures/tagging_spec.rb:9:in `block (2 levels) in <top (required)>'
+CWD/spec/fixtures/tagging_spec.rb:3:in `<top (required)>'
+CWD/bin/mspec-tag:7:in `<main>'
+
+2)
+Tag#me érròrs in unicode FAILED
+Expected 1
+ to equal 2
+
+CWD/spec/fixtures/tagging_spec.rb:13:in `block (2 levels) in <top (required)>'
+CWD/spec/fixtures/tagging_spec.rb:3:in `<top (required)>'
+CWD/bin/mspec-tag:7:in `<main>'
+
+Finished in D.DDDDDD seconds
+
+1 file, 3 examples, 3 expectations, 2 failures, 0 errors, 0 tagged
+EOS
+ ret.success?.should == false
+ end
+
+ it "does not run already tagged specs" do
+ fixtures = "spec/fixtures"
+ out, ret = run_mspec("run", "--excl-tag fails #{fixtures}/tagging_spec.rb")
+ out.should == <<EOS
+RUBY_DESCRIPTION
+.
+
+Finished in D.DDDDDD seconds
+
+1 file, 3 examples, 1 expectation, 0 failures, 0 errors, 2 tagged
+EOS
+ ret.success?.should == true
+ end
+end
diff --git a/spec/mspec/spec/matchers/base_spec.rb b/spec/mspec/spec/matchers/base_spec.rb
new file mode 100644
index 0000000000..cc13c29d1d
--- /dev/null
+++ b/spec/mspec/spec/matchers/base_spec.rb
@@ -0,0 +1,225 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+require 'time'
+
+describe SpecPositiveOperatorMatcher, "== operator" do
+ it "raises an SpecExpectationNotMetError when expected == actual returns false" do
+ lambda {
+ SpecPositiveOperatorMatcher.new(1) == 2
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x to equal y'" do
+ SpecExpectation.should_receive(:fail_with).with("Expected 1\n", "to equal 2\n")
+ SpecPositiveOperatorMatcher.new(1) == 2
+ end
+
+ it "does not raise an exception when expected == actual returns true" do
+ SpecPositiveOperatorMatcher.new(1) == 1
+ end
+end
+
+describe SpecPositiveOperatorMatcher, "=~ operator" do
+ it "raises an SpecExpectationNotMetError when expected =~ actual returns false" do
+ lambda {
+ SpecPositiveOperatorMatcher.new('real') =~ /fake/
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected \"x\" to match y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected \"real\"\n", "to match /fake/\n")
+ SpecPositiveOperatorMatcher.new('real') =~ /fake/
+ end
+
+ it "does not raise an exception when expected =~ actual returns true" do
+ SpecPositiveOperatorMatcher.new('real') =~ /real/
+ end
+end
+
+describe SpecPositiveOperatorMatcher, "> operator" do
+ it "raises an SpecExpectationNotMetError when expected > actual returns false" do
+ lambda {
+ SpecPositiveOperatorMatcher.new(4) > 5
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x to be greater than y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected 4\n", "to be greater than 5\n")
+ SpecPositiveOperatorMatcher.new(4) > 5
+ end
+
+ it "does not raise an exception when expected > actual returns true" do
+ SpecPositiveOperatorMatcher.new(5) > 4
+ end
+end
+
+describe SpecPositiveOperatorMatcher, ">= operator" do
+ it "raises an SpecExpectationNotMetError when expected >= actual returns false" do
+ lambda {
+ SpecPositiveOperatorMatcher.new(4) >= 5
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x to be greater than or equal to y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected 4\n", "to be greater than or equal to 5\n")
+ SpecPositiveOperatorMatcher.new(4) >= 5
+ end
+
+ it "does not raise an exception when expected > actual returns true" do
+ SpecPositiveOperatorMatcher.new(5) >= 4
+ SpecPositiveOperatorMatcher.new(5) >= 5
+ end
+end
+
+describe SpecPositiveOperatorMatcher, "< operater" do
+ it "raises an SpecExpectationNotMetError when expected < actual returns false" do
+ lambda {
+ SpecPositiveOperatorMatcher.new(5) < 4
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x to be less than y'" do
+ SpecExpectation.should_receive(:fail_with).with("Expected 5\n", "to be less than 4\n")
+ SpecPositiveOperatorMatcher.new(5) < 4
+ end
+
+ it "does not raise an exception when expected < actual returns true" do
+ SpecPositiveOperatorMatcher.new(4) < 5
+ end
+end
+
+describe SpecPositiveOperatorMatcher, "<= operater" do
+ it "raises an SpecExpectationNotMetError when expected < actual returns false" do
+ lambda {
+ SpecPositiveOperatorMatcher.new(5) <= 4
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x to be less than or equal to y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected 5\n", "to be less than or equal to 4\n")
+ SpecPositiveOperatorMatcher.new(5) <= 4
+ end
+
+ it "does not raise an exception when expected < actual returns true" do
+ SpecPositiveOperatorMatcher.new(4) <= 5
+ SpecPositiveOperatorMatcher.new(4) <= 4
+ end
+end
+
+describe SpecNegativeOperatorMatcher, "== operator" do
+ it "raises an SpecExpectationNotMetError when expected == actual returns true" do
+ lambda {
+ SpecNegativeOperatorMatcher.new(1) == 1
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x not to equal y'" do
+ SpecExpectation.should_receive(:fail_with).with("Expected 1\n", "not to equal 1\n")
+ SpecNegativeOperatorMatcher.new(1) == 1
+ end
+
+ it "does not raise an exception when expected == actual returns false" do
+ SpecNegativeOperatorMatcher.new(1) == 2
+ end
+end
+
+describe SpecNegativeOperatorMatcher, "=~ operator" do
+ it "raises an SpecExpectationNotMetError when expected =~ actual returns true" do
+ lambda {
+ SpecNegativeOperatorMatcher.new('real') =~ /real/
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected \"x\" not to match /y/'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected \"real\"\n", "not to match /real/\n")
+ SpecNegativeOperatorMatcher.new('real') =~ /real/
+ end
+
+ it "does not raise an exception when expected =~ actual returns false" do
+ SpecNegativeOperatorMatcher.new('real') =~ /fake/
+ end
+end
+
+describe SpecNegativeOperatorMatcher, "< operator" do
+ it "raises an SpecExpectationNotMetError when expected < actual returns true" do
+ lambda {
+ SpecNegativeOperatorMatcher.new(4) < 5
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x not to be less than y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected 4\n", "not to be less than 5\n")
+ SpecNegativeOperatorMatcher.new(4) < 5
+ end
+
+ it "does not raise an exception when expected < actual returns false" do
+ SpecNegativeOperatorMatcher.new(5) < 4
+ end
+end
+
+describe SpecNegativeOperatorMatcher, "<= operator" do
+ it "raises an SpecExpectationNotMetError when expected <= actual returns true" do
+ lambda {
+ SpecNegativeOperatorMatcher.new(4) <= 5
+ }.should raise_error(SpecExpectationNotMetError)
+ lambda {
+ SpecNegativeOperatorMatcher.new(5) <= 5
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x not to be less than or equal to y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected 4\n", "not to be less than or equal to 5\n")
+ SpecNegativeOperatorMatcher.new(4) <= 5
+ end
+
+ it "does not raise an exception when expected <= actual returns false" do
+ SpecNegativeOperatorMatcher.new(5) <= 4
+ end
+end
+
+describe SpecNegativeOperatorMatcher, "> operator" do
+ it "raises an SpecExpectationNotMetError when expected > actual returns true" do
+ lambda {
+ SpecNegativeOperatorMatcher.new(5) > 4
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x not to be greater than y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected 5\n", "not to be greater than 4\n")
+ SpecNegativeOperatorMatcher.new(5) > 4
+ end
+
+ it "does not raise an exception when expected > actual returns false" do
+ SpecNegativeOperatorMatcher.new(4) > 5
+ end
+end
+
+describe SpecNegativeOperatorMatcher, ">= operator" do
+ it "raises an SpecExpectationNotMetError when expected >= actual returns true" do
+ lambda {
+ SpecNegativeOperatorMatcher.new(5) >= 4
+ }.should raise_error(SpecExpectationNotMetError)
+ lambda {
+ SpecNegativeOperatorMatcher.new(5) >= 5
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x not to be greater than or equal to y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected 5\n", "not to be greater than or equal to 4\n")
+ SpecNegativeOperatorMatcher.new(5) >= 4
+ end
+
+ it "does not raise an exception when expected >= actual returns false" do
+ SpecNegativeOperatorMatcher.new(4) >= 5
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_an_instance_of_spec.rb b/spec/mspec/spec/matchers/be_an_instance_of_spec.rb
new file mode 100644
index 0000000000..7f2126df7d
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_an_instance_of_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+module BeAnInOfSpecs
+ class A
+ end
+
+ class B < A
+ end
+
+ class C < B
+ end
+end
+
+describe BeAnInstanceOfMatcher do
+ it "matches when actual is an instance_of? expected" do
+ a = BeAnInOfSpecs::A.new
+ BeAnInstanceOfMatcher.new(BeAnInOfSpecs::A).matches?(a).should be_true
+
+ b = BeAnInOfSpecs::B.new
+ BeAnInstanceOfMatcher.new(BeAnInOfSpecs::B).matches?(b).should be_true
+ end
+
+ it "does not match when actual is not an instance_of? expected" do
+ a = BeAnInOfSpecs::A.new
+ BeAnInstanceOfMatcher.new(BeAnInOfSpecs::B).matches?(a).should be_false
+
+ b = BeAnInOfSpecs::B.new
+ BeAnInstanceOfMatcher.new(BeAnInOfSpecs::A).matches?(b).should be_false
+
+ c = BeAnInOfSpecs::C.new
+ BeAnInstanceOfMatcher.new(BeAnInOfSpecs::A).matches?(c).should be_false
+ BeAnInstanceOfMatcher.new(BeAnInOfSpecs::B).matches?(c).should be_false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeAnInstanceOfMatcher.new(Numeric)
+ matcher.matches?("string")
+ matcher.failure_message.should == [
+ "Expected \"string\" (String)", "to be an instance of Numeric"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeAnInstanceOfMatcher.new(Numeric)
+ matcher.matches?(4.0)
+ matcher.negative_failure_message.should == [
+ "Expected 4.0 (Float)", "not to be an instance of Numeric"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_ancestor_of_spec.rb b/spec/mspec/spec/matchers/be_ancestor_of_spec.rb
new file mode 100644
index 0000000000..c6bd1a26c7
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_ancestor_of_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class Parent; end
+class Child < Parent; end
+
+describe BeAncestorOfMatcher do
+ it "matches when actual is an ancestor of expected" do
+ BeAncestorOfMatcher.new(Child).matches?(Parent).should == true
+ end
+
+ it "does not match when actual is not an ancestor of expected" do
+ BeAncestorOfMatcher.new(Parent).matches?(Child).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeAncestorOfMatcher.new(Parent)
+ matcher.matches?(Child)
+ matcher.failure_message.should == ["Expected Child", "to be an ancestor of Parent"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeAncestorOfMatcher.new(Child)
+ matcher.matches?(Parent)
+ matcher.negative_failure_message.should == ["Expected Parent", "not to be an ancestor of Child"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_close_spec.rb b/spec/mspec/spec/matchers/be_close_spec.rb
new file mode 100644
index 0000000000..6edff98e4a
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_close_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+# Adapted from RSpec 1.0.8
+describe BeCloseMatcher do
+ it "matches when actual == expected" do
+ BeCloseMatcher.new(5.0, 0.5).matches?(5.0).should == true
+ end
+
+ it "matches when actual < (expected + tolerance)" do
+ BeCloseMatcher.new(5.0, 0.5).matches?(5.49).should == true
+ end
+
+ it "matches when actual > (expected - tolerance)" do
+ BeCloseMatcher.new(5.0, 0.5).matches?(4.51).should == true
+ end
+
+ it "matches when actual == (expected + tolerance)" do
+ BeCloseMatcher.new(5.0, 0.5).matches?(5.5).should == true
+ BeCloseMatcher.new(3, 2).matches?(5).should == true
+ end
+
+ it "matches when actual == (expected - tolerance)" do
+ BeCloseMatcher.new(5.0, 0.5).matches?(4.5).should == true
+ BeCloseMatcher.new(3, 2).matches?(1).should == true
+ end
+
+ it "does not match when actual < (expected - tolerance)" do
+ BeCloseMatcher.new(5.0, 0.5).matches?(4.49).should == false
+ end
+
+ it "does not match when actual > (expected + tolerance)" do
+ BeCloseMatcher.new(5.0, 0.5).matches?(5.51).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeCloseMatcher.new(5.0, 0.5)
+ matcher.matches?(6.5)
+ matcher.failure_message.should == ["Expected 6.5", "to be within 5.0 +/- 0.5"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeCloseMatcher.new(5.0, 0.5)
+ matcher.matches?(4.9)
+ matcher.negative_failure_message.should == ["Expected 4.9", "not to be within 5.0 +/- 0.5"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_computed_by_spec.rb b/spec/mspec/spec/matchers/be_computed_by_spec.rb
new file mode 100644
index 0000000000..9833e211a4
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_computed_by_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+require 'mspec/matchers'
+
+describe BeComputedByMatcher do
+ it "matches when all entries in the Array compute" do
+ array = [ [65, "A"],
+ [90, "Z"] ]
+ BeComputedByMatcher.new(:chr).matches?(array).should be_true
+ end
+
+ it "matches when all entries in the Array with arguments compute" do
+ array = [ [1, 2, 3],
+ [2, 4, 6] ]
+ BeComputedByMatcher.new(:+).matches?(array).should be_true
+ end
+
+ it "does not match when any entry in the Array does not compute" do
+ array = [ [65, "A" ],
+ [91, "Z" ] ]
+ BeComputedByMatcher.new(:chr).matches?(array).should be_false
+ end
+
+ it "accepts an argument list to apply to each method call" do
+ array = [ [65, "1000001" ],
+ [90, "1011010" ] ]
+ BeComputedByMatcher.new(:to_s, 2).matches?(array).should be_true
+ end
+
+ it "does not match when any entry in the Array with arguments does not compute" do
+ array = [ [1, 2, 3],
+ [2, 4, 7] ]
+ BeComputedByMatcher.new(:+).matches?(array).should be_false
+ end
+
+ it "provides a useful failure message" do
+ array = [ [65, "A" ],
+ [91, "Z" ] ]
+ matcher = BeComputedByMatcher.new(:chr)
+ matcher.matches?(array)
+ matcher.failure_message.should == ["Expected \"Z\"", "to be computed by 91.chr (computed \"[\" instead)"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_empty_spec.rb b/spec/mspec/spec/matchers/be_empty_spec.rb
new file mode 100644
index 0000000000..cb8663f5ee
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_empty_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe BeEmptyMatcher do
+ it "matches when actual is empty" do
+ BeEmptyMatcher.new.matches?("").should == true
+ end
+
+ it "does not match when actual is not empty" do
+ BeEmptyMatcher.new.matches?([10]).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeEmptyMatcher.new
+ matcher.matches?("not empty string")
+ matcher.failure_message.should == ["Expected \"not empty string\"", "to be empty"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeEmptyMatcher.new
+ matcher.matches?("")
+ matcher.negative_failure_message.should == ["Expected \"\"", "not to be empty"]
+ end
+end
+
diff --git a/spec/mspec/spec/matchers/be_false_spec.rb b/spec/mspec/spec/matchers/be_false_spec.rb
new file mode 100644
index 0000000000..31afd24ebc
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_false_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe BeFalseMatcher do
+ it "matches when actual is false" do
+ BeFalseMatcher.new.matches?(false).should == true
+ end
+
+ it "does not match when actual is not false" do
+ BeFalseMatcher.new.matches?("").should == false
+ BeFalseMatcher.new.matches?(true).should == false
+ BeFalseMatcher.new.matches?(nil).should == false
+ BeFalseMatcher.new.matches?(0).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeFalseMatcher.new
+ matcher.matches?("some string")
+ matcher.failure_message.should == ["Expected \"some string\"", "to be false"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeFalseMatcher.new
+ matcher.matches?(false)
+ matcher.negative_failure_message.should == ["Expected false", "not to be false"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_kind_of_spec.rb b/spec/mspec/spec/matchers/be_kind_of_spec.rb
new file mode 100644
index 0000000000..7c4a59f7b9
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_kind_of_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe BeKindOfMatcher do
+ it "matches when actual is a kind_of? expected" do
+ BeKindOfMatcher.new(Numeric).matches?(1).should == true
+ BeKindOfMatcher.new(Integer).matches?(2).should == true
+ BeKindOfMatcher.new(Regexp).matches?(/m/).should == true
+ end
+
+ it "does not match when actual is not a kind_of? expected" do
+ BeKindOfMatcher.new(Integer).matches?(1.5).should == false
+ BeKindOfMatcher.new(String).matches?(:a).should == false
+ BeKindOfMatcher.new(Hash).matches?([]).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeKindOfMatcher.new(Numeric)
+ matcher.matches?('string')
+ matcher.failure_message.should == [
+ "Expected \"string\" (String)", "to be kind of Numeric"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeKindOfMatcher.new(Numeric)
+ matcher.matches?(4.0)
+ matcher.negative_failure_message.should == [
+ "Expected 4.0 (Float)", "not to be kind of Numeric"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_nan_spec.rb b/spec/mspec/spec/matchers/be_nan_spec.rb
new file mode 100644
index 0000000000..2062763a92
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_nan_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/guards'
+require 'mspec/helpers'
+require 'mspec/matchers'
+
+describe BeNaNMatcher do
+ it "matches when actual is NaN" do
+ BeNaNMatcher.new.matches?(nan_value).should == true
+ end
+
+ it "does not match when actual is not NaN" do
+ BeNaNMatcher.new.matches?(1.0).should == false
+ BeNaNMatcher.new.matches?(0).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeNaNMatcher.new
+ matcher.matches?(0)
+ matcher.failure_message.should == ["Expected 0", "to be NaN"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeNaNMatcher.new
+ matcher.matches?(nan_value)
+ matcher.negative_failure_message.should == ["Expected NaN", "not to be NaN"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_nil_spec.rb b/spec/mspec/spec/matchers/be_nil_spec.rb
new file mode 100644
index 0000000000..6551feb5de
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_nil_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe BeNilMatcher do
+ it "matches when actual is nil" do
+ BeNilMatcher.new.matches?(nil).should == true
+ end
+
+ it "does not match when actual is not nil" do
+ BeNilMatcher.new.matches?("").should == false
+ BeNilMatcher.new.matches?(false).should == false
+ BeNilMatcher.new.matches?(0).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeNilMatcher.new
+ matcher.matches?("some string")
+ matcher.failure_message.should == ["Expected \"some string\"", "to be nil"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeNilMatcher.new
+ matcher.matches?(nil)
+ matcher.negative_failure_message.should == ["Expected nil", "not to be nil"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_true_or_false_spec.rb b/spec/mspec/spec/matchers/be_true_or_false_spec.rb
new file mode 100644
index 0000000000..3edffcb1b1
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_true_or_false_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe BeTrueOrFalseMatcher do
+ it "matches when actual is true" do
+ BeTrueOrFalseMatcher.new.matches?(true).should == true
+ end
+
+ it "matches when actual is false" do
+ BeTrueOrFalseMatcher.new.matches?(false).should == true
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeTrueOrFalseMatcher.new
+ matcher.matches?("some string")
+ matcher.failure_message.should == ["Expected \"some string\"", "to be true or false"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_true_spec.rb b/spec/mspec/spec/matchers/be_true_spec.rb
new file mode 100644
index 0000000000..90c89b3911
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_true_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe BeTrueMatcher do
+ it "matches when actual is true" do
+ BeTrueMatcher.new.matches?(true).should == true
+ end
+
+ it "does not match when actual is not true" do
+ BeTrueMatcher.new.matches?("").should == false
+ BeTrueMatcher.new.matches?(false).should == false
+ BeTrueMatcher.new.matches?(nil).should == false
+ BeTrueMatcher.new.matches?(0).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeTrueMatcher.new
+ matcher.matches?("some string")
+ matcher.failure_message.should == ["Expected \"some string\"", "to be true"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeTrueMatcher.new
+ matcher.matches?(true)
+ matcher.negative_failure_message.should == ["Expected true", "not to be true"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/block_caller_spec.rb b/spec/mspec/spec/matchers/block_caller_spec.rb
new file mode 100644
index 0000000000..d6793b9779
--- /dev/null
+++ b/spec/mspec/spec/matchers/block_caller_spec.rb
@@ -0,0 +1,13 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe BlockingMatcher do
+ it 'matches when a Proc blocks the caller' do
+ BlockingMatcher.new.matches?(proc { sleep }).should == true
+ end
+
+ it 'does not match when a Proc does not block the caller' do
+ BlockingMatcher.new.matches?(proc { 1 }).should == false
+ end
+end
diff --git a/spec/mspec/spec/matchers/complain_spec.rb b/spec/mspec/spec/matchers/complain_spec.rb
new file mode 100644
index 0000000000..709b57be6c
--- /dev/null
+++ b/spec/mspec/spec/matchers/complain_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe ComplainMatcher do
+ it "matches when executing the proc results in output to $stderr" do
+ proc = lambda { warn "I'm gonna tell yo mama" }
+ ComplainMatcher.new(nil).matches?(proc).should == true
+ end
+
+ it "maches when executing the proc results in the expected output to $stderr" do
+ proc = lambda { warn "Que haces?" }
+ ComplainMatcher.new("Que haces?\n").matches?(proc).should == true
+ ComplainMatcher.new("Que pasa?\n").matches?(proc).should == false
+ ComplainMatcher.new(/Que/).matches?(proc).should == true
+ ComplainMatcher.new(/Quoi/).matches?(proc).should == false
+ end
+
+ it "does not match when there is no output to $stderr" do
+ ComplainMatcher.new(nil).matches?(lambda {}).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = ComplainMatcher.new(nil)
+ matcher.matches?(lambda { })
+ matcher.failure_message.should == ["Expected a warning", "but received none"]
+ matcher = ComplainMatcher.new("listen here")
+ matcher.matches?(lambda { warn "look out" })
+ matcher.failure_message.should ==
+ ["Expected warning: \"listen here\"", "but got: \"look out\""]
+ matcher = ComplainMatcher.new(/talk/)
+ matcher.matches?(lambda { warn "listen up" })
+ matcher.failure_message.should ==
+ ["Expected warning to match: /talk/", "but got: \"listen up\""]
+ end
+
+ it "provides a useful negative failure message" do
+ proc = lambda { warn "ouch" }
+ matcher = ComplainMatcher.new(nil)
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Unexpected warning: ", "\"ouch\""]
+ matcher = ComplainMatcher.new("ouchy")
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected warning: \"ouchy\"", "but got: \"ouch\""]
+ matcher = ComplainMatcher.new(/ou/)
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected warning not to match: /ou/", "but got: \"ouch\""]
+ end
+end
diff --git a/spec/mspec/spec/matchers/eql_spec.rb b/spec/mspec/spec/matchers/eql_spec.rb
new file mode 100644
index 0000000000..711ebdb679
--- /dev/null
+++ b/spec/mspec/spec/matchers/eql_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe EqlMatcher do
+ it "matches when actual is eql? to expected" do
+ EqlMatcher.new(1).matches?(1).should == true
+ EqlMatcher.new(1.5).matches?(1.5).should == true
+ EqlMatcher.new("red").matches?("red").should == true
+ EqlMatcher.new(:blue).matches?(:blue).should == true
+ EqlMatcher.new(Object).matches?(Object).should == true
+
+ o = Object.new
+ EqlMatcher.new(o).matches?(o).should == true
+ end
+
+ it "does not match when actual is not eql? to expected" do
+ EqlMatcher.new(1).matches?(1.0).should == false
+ EqlMatcher.new(Hash).matches?(Object).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = EqlMatcher.new("red")
+ matcher.matches?("red")
+ matcher.failure_message.should == ["Expected \"red\"\n", "to have same value and type as \"red\"\n"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = EqlMatcher.new(1)
+ matcher.matches?(1.0)
+ matcher.negative_failure_message.should == ["Expected 1.0\n", "not to have same value or type as 1\n"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/equal_element_spec.rb b/spec/mspec/spec/matchers/equal_element_spec.rb
new file mode 100644
index 0000000000..45b8390364
--- /dev/null
+++ b/spec/mspec/spec/matchers/equal_element_spec.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe EqualElementMatcher do
+ it "matches if it finds an element with the passed name, no matter what attributes/content" do
+ EqualElementMatcher.new("A").matches?('<A></A>').should be_true
+ EqualElementMatcher.new("A").matches?('<A HREF="http://example.com"></A>').should be_true
+ EqualElementMatcher.new("A").matches?('<A HREF="http://example.com"></A>').should be_true
+
+ EqualElementMatcher.new("BASE").matches?('<BASE></A>').should be_false
+ EqualElementMatcher.new("BASE").matches?('<A></BASE>').should be_false
+ EqualElementMatcher.new("BASE").matches?('<A></A>').should be_false
+ EqualElementMatcher.new("BASE").matches?('<A HREF="http://example.com"></A>').should be_false
+ EqualElementMatcher.new("BASE").matches?('<A HREF="http://example.com"></A>').should be_false
+ end
+
+ it "matches if it finds an element with the passed name and the passed attributes" do
+ EqualElementMatcher.new("A", {}).matches?('<A></A>').should be_true
+ EqualElementMatcher.new("A", nil).matches?('<A HREF="http://example.com"></A>').should be_true
+ EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A HREF="http://example.com"></A>').should be_true
+
+ EqualElementMatcher.new("A", {}).matches?('<A HREF="http://example.com"></A>').should be_false
+ EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A></A>').should be_false
+ EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A HREF="http://test.com"></A>').should be_false
+ EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A HREF="http://example.com" HREF="http://example.com"></A>').should be_false
+ end
+
+ it "matches if it finds an element with the passed name, the passed attributes and the passed content" do
+ EqualElementMatcher.new("A", {}, "").matches?('<A></A>').should be_true
+ EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('<A HREF="http://example.com">Example</A>').should be_true
+
+ EqualElementMatcher.new("A", {}, "Test").matches?('<A></A>').should be_false
+ EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('<A HREF="http://example.com"></A>').should be_false
+ EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('<A HREF="http://example.com">Test</A>').should be_false
+ end
+
+ it "can match unclosed elements" do
+ EqualElementMatcher.new("BASE", nil, nil, :not_closed => true).matches?('<BASE>').should be_true
+ EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, nil, :not_closed => true).matches?('<BASE HREF="http://example.com">').should be_true
+ EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "Example", :not_closed => true).matches?('<BASE HREF="http://example.com">Example').should be_true
+
+ EqualElementMatcher.new("BASE", {}, nil, :not_closed => true).matches?('<BASE HREF="http://example.com">').should be_false
+ EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "", :not_closed => true).matches?('<BASE HREF="http://example.com">Example').should be_false
+ EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "Test", :not_closed => true).matches?('<BASE HREF="http://example.com">Example').should be_false
+ end
+
+ it "provides a useful failure message" do
+ equal_element = EqualElementMatcher.new("A", {}, "Test")
+ equal_element.matches?('<A></A>').should be_false
+ equal_element.failure_message.should == [%{Expected "<A></A>"\n}, %{to be a 'A' element with no attributes and "Test" as content}]
+
+ equal_element = EqualElementMatcher.new("A", {}, "")
+ equal_element.matches?('<A>Test</A>').should be_false
+ equal_element.failure_message.should == [%{Expected "<A>Test</A>"\n}, %{to be a 'A' element with no attributes and no content}]
+
+ equal_element = EqualElementMatcher.new("A", "HREF" => "http://www.example.com")
+ equal_element.matches?('<A>Test</A>').should be_false
+ equal_element.failure_message.should == [%{Expected "<A>Test</A>"\n}, %{to be a 'A' element with HREF="http://www.example.com" and any content}]
+ end
+
+ it "provides a useful negative failure message" do
+ equal_element = EqualElementMatcher.new("A", {}, "Test")
+ equal_element.matches?('<A></A>').should be_false
+ equal_element.negative_failure_message.should == [%{Expected "<A></A>"\n}, %{not to be a 'A' element with no attributes and "Test" as content}]
+
+ equal_element = EqualElementMatcher.new("A", {}, "")
+ equal_element.matches?('<A>Test</A>').should be_false
+ equal_element.negative_failure_message.should == [%{Expected "<A>Test</A>"\n}, %{not to be a 'A' element with no attributes and no content}]
+
+ equal_element = EqualElementMatcher.new("A", "HREF" => "http://www.example.com")
+ equal_element.matches?('<A>Test</A>').should be_false
+ equal_element.negative_failure_message.should == [%{Expected "<A>Test</A>"\n}, %{not to be a 'A' element with HREF="http://www.example.com" and any content}]
+ end
+end
diff --git a/spec/mspec/spec/matchers/equal_spec.rb b/spec/mspec/spec/matchers/equal_spec.rb
new file mode 100644
index 0000000000..ca7bf83fdd
--- /dev/null
+++ b/spec/mspec/spec/matchers/equal_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe EqualMatcher do
+ it "matches when actual is equal? to expected" do
+ EqualMatcher.new(1).matches?(1).should == true
+ EqualMatcher.new(:blue).matches?(:blue).should == true
+ EqualMatcher.new(Object).matches?(Object).should == true
+
+ o = Object.new
+ EqualMatcher.new(o).matches?(o).should == true
+ end
+
+ it "does not match when actual is not a equal? to expected" do
+ EqualMatcher.new(1).matches?(1.0).should == false
+ EqualMatcher.new("blue").matches?("blue").should == false
+ EqualMatcher.new(Hash).matches?(Object).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = EqualMatcher.new("red")
+ matcher.matches?("red")
+ matcher.failure_message.should == ["Expected \"red\"\n", "to be identical to \"red\"\n"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = EqualMatcher.new(1)
+ matcher.matches?(1)
+ matcher.negative_failure_message.should == ["Expected 1\n", "not to be identical to 1\n"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_class_variable_spec.rb b/spec/mspec/spec/matchers/have_class_variable_spec.rb
new file mode 100644
index 0000000000..01ba9d0f57
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_class_variable_spec.rb
@@ -0,0 +1,49 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class IVarModMock
+ def self.class_variables
+ [:@foo]
+ end
+end
+
+describe HaveClassVariableMatcher, "on RUBY_VERSION >= 1.9" do
+ it "matches when mod has the class variable, given as string" do
+ matcher = HaveClassVariableMatcher.new('@foo')
+ matcher.matches?(IVarModMock).should be_true
+ end
+
+ it "matches when mod has the class variable, given as symbol" do
+ matcher = HaveClassVariableMatcher.new(:@foo)
+ matcher.matches?(IVarModMock).should be_true
+ end
+
+ it "does not match when mod hasn't got the class variable, given as string" do
+ matcher = HaveClassVariableMatcher.new('@bar')
+ matcher.matches?(IVarModMock).should be_false
+ end
+
+ it "does not match when mod hasn't got the class variable, given as symbol" do
+ matcher = HaveClassVariableMatcher.new(:@bar)
+ matcher.matches?(IVarModMock).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HaveClassVariableMatcher.new(:@bar)
+ matcher.matches?(IVarModMock)
+ matcher.failure_message.should == [
+ "Expected IVarModMock to have class variable '@bar'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure messoge for #should_not" do
+ matcher = HaveClassVariableMatcher.new(:@bar)
+ matcher.matches?(IVarModMock)
+ matcher.negative_failure_message.should == [
+ "Expected IVarModMock NOT to have class variable '@bar'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_constant_spec.rb b/spec/mspec/spec/matchers/have_constant_spec.rb
new file mode 100644
index 0000000000..20c5f161d4
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_constant_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HCMSpecs
+ X = :x
+end
+
+describe HaveConstantMatcher do
+ it "matches when mod has the constant" do
+ matcher = HaveConstantMatcher.new :X
+ matcher.matches?(HCMSpecs).should be_true
+ end
+
+ it "does not match when mod does not have the constant" do
+ matcher = HaveConstantMatcher.new :A
+ matcher.matches?(HCMSpecs).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HaveConstantMatcher.new :A
+ matcher.matches?(HCMSpecs)
+ matcher.failure_message.should == [
+ "Expected HCMSpecs to have constant 'A'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure messoge for #should_not" do
+ matcher = HaveConstantMatcher.new :X
+ matcher.matches?(HCMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HCMSpecs NOT to have constant 'X'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_instance_method_spec.rb b/spec/mspec/spec/matchers/have_instance_method_spec.rb
new file mode 100644
index 0000000000..738f5f875d
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_instance_method_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HIMMSpecs
+ def instance_method
+ end
+
+ class Subclass < HIMMSpecs
+ def instance_sub_method
+ end
+ end
+end
+
+describe HaveInstanceMethodMatcher do
+ it "inherits from MethodMatcher" do
+ HaveInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ end
+
+ it "matches when mod has the instance method" do
+ matcher = HaveInstanceMethodMatcher.new :instance_method
+ matcher.matches?(HIMMSpecs).should be_true
+ matcher.matches?(HIMMSpecs::Subclass).should be_true
+ end
+
+ it "does not match when mod does not have the instance method" do
+ matcher = HaveInstanceMethodMatcher.new :another_method
+ matcher.matches?(HIMMSpecs).should be_false
+ end
+
+ it "does not match if the method is in a superclass and include_super is false" do
+ matcher = HaveInstanceMethodMatcher.new :instance_method, false
+ matcher.matches?(HIMMSpecs::Subclass).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HaveInstanceMethodMatcher.new :some_method
+ matcher.matches?(HIMMSpecs)
+ matcher.failure_message.should == [
+ "Expected HIMMSpecs to have instance method 'some_method'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure messoge for #should_not" do
+ matcher = HaveInstanceMethodMatcher.new :some_method
+ matcher.matches?(HIMMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HIMMSpecs NOT to have instance method 'some_method'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_instance_variable_spec.rb b/spec/mspec/spec/matchers/have_instance_variable_spec.rb
new file mode 100644
index 0000000000..4122c6551b
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_instance_variable_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe HaveInstanceVariableMatcher do
+ before :each do
+ @object = Object.new
+ def @object.instance_variables
+ [:@foo]
+ end
+ end
+
+ it "matches when object has the instance variable, given as string" do
+ matcher = HaveInstanceVariableMatcher.new('@foo')
+ matcher.matches?(@object).should be_true
+ end
+
+ it "matches when object has the instance variable, given as symbol" do
+ matcher = HaveInstanceVariableMatcher.new(:@foo)
+ matcher.matches?(@object).should be_true
+ end
+
+ it "does not match when object hasn't got the instance variable, given as string" do
+ matcher = HaveInstanceVariableMatcher.new('@bar')
+ matcher.matches?(@object).should be_false
+ end
+
+ it "does not match when object hasn't got the instance variable, given as symbol" do
+ matcher = HaveInstanceVariableMatcher.new(:@bar)
+ matcher.matches?(@object).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HaveInstanceVariableMatcher.new(:@bar)
+ matcher.matches?(@object)
+ matcher.failure_message.should == [
+ "Expected #{@object.inspect} to have instance variable '@bar'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure messoge for #should_not" do
+ matcher = HaveInstanceVariableMatcher.new(:@bar)
+ matcher.matches?(@object)
+ matcher.negative_failure_message.should == [
+ "Expected #{@object.inspect} NOT to have instance variable '@bar'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_method_spec.rb b/spec/mspec/spec/matchers/have_method_spec.rb
new file mode 100644
index 0000000000..41bd485119
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_method_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HMMSpecs
+ def instance_method
+ end
+
+ class Subclass < HMMSpecs
+ def instance_sub_method
+ end
+ end
+end
+
+describe HaveMethodMatcher do
+ it "inherits from MethodMatcher" do
+ HaveMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ end
+
+ it "matches when mod has the method" do
+ matcher = HaveMethodMatcher.new :instance_method
+ matcher.matches?(HMMSpecs).should be_true
+ matcher.matches?(HMMSpecs.new).should be_true
+ matcher.matches?(HMMSpecs::Subclass).should be_true
+ matcher.matches?(HMMSpecs::Subclass.new).should be_true
+ end
+
+ it "does not match when mod does not have the method" do
+ matcher = HaveMethodMatcher.new :another_method
+ matcher.matches?(HMMSpecs).should be_false
+ end
+
+ it "does not match if the method is in a superclass and include_super is false" do
+ matcher = HaveMethodMatcher.new :instance_method, false
+ matcher.matches?(HMMSpecs::Subclass).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HaveMethodMatcher.new :some_method
+ matcher.matches?(HMMSpecs)
+ matcher.failure_message.should == [
+ "Expected HMMSpecs to have method 'some_method'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure messoge for #should_not" do
+ matcher = HaveMethodMatcher.new :some_method
+ matcher.matches?(HMMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HMMSpecs NOT to have method 'some_method'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_private_instance_method_spec.rb b/spec/mspec/spec/matchers/have_private_instance_method_spec.rb
new file mode 100644
index 0000000000..827c6b6034
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_private_instance_method_spec.rb
@@ -0,0 +1,57 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HPIMMSpecs
+ private
+
+ def private_method
+ end
+
+ class Subclass < HPIMMSpecs
+ private
+
+ def private_sub_method
+ end
+ end
+end
+
+describe HavePrivateInstanceMethodMatcher do
+ it "inherits from MethodMatcher" do
+ HavePrivateInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ end
+
+ it "matches when mod has the private instance method" do
+ matcher = HavePrivateInstanceMethodMatcher.new :private_method
+ matcher.matches?(HPIMMSpecs).should be_true
+ matcher.matches?(HPIMMSpecs::Subclass).should be_true
+ end
+
+ it "does not match when mod does not have the private instance method" do
+ matcher = HavePrivateInstanceMethodMatcher.new :another_method
+ matcher.matches?(HPIMMSpecs).should be_false
+ end
+
+ it "does not match if the method is in a superclass and include_super is false" do
+ matcher = HavePrivateInstanceMethodMatcher.new :private_method, false
+ matcher.matches?(HPIMMSpecs::Subclass).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HavePrivateInstanceMethodMatcher.new :some_method
+ matcher.matches?(HPIMMSpecs)
+ matcher.failure_message.should == [
+ "Expected HPIMMSpecs to have private instance method 'some_method'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure message for #should_not" do
+ matcher = HavePrivateInstanceMethodMatcher.new :some_method
+ matcher.matches?(HPIMMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HPIMMSpecs NOT to have private instance method 'some_method'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_private_method_spec.rb b/spec/mspec/spec/matchers/have_private_method_spec.rb
new file mode 100644
index 0000000000..e63a9a3c2f
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_private_method_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HPMMSpecs
+ def self.private_method
+ end
+
+ private_class_method :private_method
+end
+
+describe HavePrivateMethodMatcher do
+ it "inherits from MethodMatcher" do
+ HavePrivateMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ end
+
+ it "matches when mod has the private method" do
+ matcher = HavePrivateMethodMatcher.new :private_method
+ matcher.matches?(HPMMSpecs).should be_true
+ end
+
+ it "does not match when mod does not have the private method" do
+ matcher = HavePrivateMethodMatcher.new :another_method
+ matcher.matches?(HPMMSpecs).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HavePrivateMethodMatcher.new :some_method
+ matcher.matches?(HPMMSpecs)
+ matcher.failure_message.should == [
+ "Expected HPMMSpecs to have private method 'some_method'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure message for #should_not" do
+ matcher = HavePrivateMethodMatcher.new :private_method
+ matcher.matches?(HPMMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HPMMSpecs NOT to have private method 'private_method'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_protected_instance_method_spec.rb b/spec/mspec/spec/matchers/have_protected_instance_method_spec.rb
new file mode 100644
index 0000000000..460d0368fb
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_protected_instance_method_spec.rb
@@ -0,0 +1,57 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HPIMMSpecs
+ protected
+
+ def protected_method
+ end
+
+ class Subclass < HPIMMSpecs
+ protected
+
+ def protected_sub_method
+ end
+ end
+end
+
+describe HaveProtectedInstanceMethodMatcher do
+ it "inherits from MethodMatcher" do
+ HaveProtectedInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ end
+
+ it "matches when mod has the protected instance method" do
+ matcher = HaveProtectedInstanceMethodMatcher.new :protected_method
+ matcher.matches?(HPIMMSpecs).should be_true
+ matcher.matches?(HPIMMSpecs::Subclass).should be_true
+ end
+
+ it "does not match when mod does not have the protected instance method" do
+ matcher = HaveProtectedInstanceMethodMatcher.new :another_method
+ matcher.matches?(HPIMMSpecs).should be_false
+ end
+
+ it "does not match if the method is in a superclass and include_super is false" do
+ matcher = HaveProtectedInstanceMethodMatcher.new :protected_method, false
+ matcher.matches?(HPIMMSpecs::Subclass).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HaveProtectedInstanceMethodMatcher.new :some_method
+ matcher.matches?(HPIMMSpecs)
+ matcher.failure_message.should == [
+ "Expected HPIMMSpecs to have protected instance method 'some_method'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure messoge for #should_not" do
+ matcher = HaveProtectedInstanceMethodMatcher.new :some_method
+ matcher.matches?(HPIMMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HPIMMSpecs NOT to have protected instance method 'some_method'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_public_instance_method_spec.rb b/spec/mspec/spec/matchers/have_public_instance_method_spec.rb
new file mode 100644
index 0000000000..bff1046f04
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_public_instance_method_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HPIMMSpecs
+ def public_method
+ end
+
+ class Subclass < HPIMMSpecs
+ def public_sub_method
+ end
+ end
+end
+
+describe HavePublicInstanceMethodMatcher do
+ it "inherits from MethodMatcher" do
+ HavePublicInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ end
+
+ it "matches when mod has the public instance method" do
+ matcher = HavePublicInstanceMethodMatcher.new :public_method
+ matcher.matches?(HPIMMSpecs).should be_true
+ matcher.matches?(HPIMMSpecs::Subclass).should be_true
+ end
+
+ it "does not match when mod does not have the public instance method" do
+ matcher = HavePublicInstanceMethodMatcher.new :another_method
+ matcher.matches?(HPIMMSpecs).should be_false
+ end
+
+ it "does not match if the method is in a superclass and include_super is false" do
+ matcher = HavePublicInstanceMethodMatcher.new :public_method, false
+ matcher.matches?(HPIMMSpecs::Subclass).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HavePublicInstanceMethodMatcher.new :some_method
+ matcher.matches?(HPIMMSpecs)
+ matcher.failure_message.should == [
+ "Expected HPIMMSpecs to have public instance method 'some_method'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure messoge for #should_not" do
+ matcher = HavePublicInstanceMethodMatcher.new :some_method
+ matcher.matches?(HPIMMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HPIMMSpecs NOT to have public instance method 'some_method'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_singleton_method_spec.rb b/spec/mspec/spec/matchers/have_singleton_method_spec.rb
new file mode 100644
index 0000000000..57c37e01d9
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_singleton_method_spec.rb
@@ -0,0 +1,45 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HSMMSpecs
+ def self.singleton_method
+ end
+end
+
+describe HaveSingletonMethodMatcher do
+ it "inherits from MethodMatcher" do
+ HaveSingletonMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ end
+
+ it "matches when the class has a singleton method" do
+ matcher = HaveSingletonMethodMatcher.new :singleton_method
+ matcher.matches?(HSMMSpecs).should be_true
+ end
+
+ it "matches when the object has a singleton method" do
+ obj = double("HSMMSpecs")
+ def obj.singleton_method; end
+
+ matcher = HaveSingletonMethodMatcher.new :singleton_method
+ matcher.matches?(obj).should be_true
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HaveSingletonMethodMatcher.new :some_method
+ matcher.matches?(HSMMSpecs)
+ matcher.failure_message.should == [
+ "Expected HSMMSpecs to have singleton method 'some_method'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure message for #should_not" do
+ matcher = HaveSingletonMethodMatcher.new :singleton_method
+ matcher.matches?(HSMMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HSMMSpecs NOT to have singleton method 'singleton_method'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/include_any_of_spec.rb b/spec/mspec/spec/matchers/include_any_of_spec.rb
new file mode 100644
index 0000000000..697c8d8886
--- /dev/null
+++ b/spec/mspec/spec/matchers/include_any_of_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe IncludeAnyOfMatcher do
+ it "matches when actual includes expected" do
+ IncludeAnyOfMatcher.new(2).matches?([1,2,3]).should == true
+ IncludeAnyOfMatcher.new("b").matches?("abc").should == true
+ end
+
+ it "does not match when actual does not include expected" do
+ IncludeAnyOfMatcher.new(4).matches?([1,2,3]).should == false
+ IncludeAnyOfMatcher.new("d").matches?("abc").should == false
+ end
+
+ it "matches when actual includes all expected" do
+ IncludeAnyOfMatcher.new(3, 2, 1).matches?([1,2,3]).should == true
+ IncludeAnyOfMatcher.new("a", "b", "c").matches?("abc").should == true
+ end
+
+ it "matches when actual includes any expected" do
+ IncludeAnyOfMatcher.new(3, 4, 5).matches?([1,2,3]).should == true
+ IncludeAnyOfMatcher.new("c", "d", "e").matches?("abc").should == true
+ end
+
+ it "does not match when actual does not include any expected" do
+ IncludeAnyOfMatcher.new(4, 5).matches?([1,2,3]).should == false
+ IncludeAnyOfMatcher.new("de").matches?("abc").should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = IncludeAnyOfMatcher.new(5, 6)
+ matcher.matches?([1,2,3])
+ matcher.failure_message.should == ["Expected [1, 2, 3]", "to include any of [5, 6]"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = IncludeAnyOfMatcher.new(1, 2, 3)
+ matcher.matches?([1,2])
+ matcher.negative_failure_message.should == ["Expected [1, 2]", "not to include any of [1, 2, 3]"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/include_spec.rb b/spec/mspec/spec/matchers/include_spec.rb
new file mode 100644
index 0000000000..f045c5e0cb
--- /dev/null
+++ b/spec/mspec/spec/matchers/include_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe IncludeMatcher do
+ it "matches when actual includes expected" do
+ IncludeMatcher.new(2).matches?([1,2,3]).should == true
+ IncludeMatcher.new("b").matches?("abc").should == true
+ end
+
+ it "does not match when actual does not include expected" do
+ IncludeMatcher.new(4).matches?([1,2,3]).should == false
+ IncludeMatcher.new("d").matches?("abc").should == false
+ end
+
+ it "matches when actual includes all expected" do
+ IncludeMatcher.new(3, 2, 1).matches?([1,2,3]).should == true
+ IncludeMatcher.new("a", "b", "c").matches?("abc").should == true
+ end
+
+ it "does not match when actual does not include all expected" do
+ IncludeMatcher.new(3, 2, 4).matches?([1,2,3]).should == false
+ IncludeMatcher.new("a", "b", "c", "d").matches?("abc").should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = IncludeMatcher.new(5, 2)
+ matcher.matches?([1,2,3])
+ matcher.failure_message.should == ["Expected [1, 2, 3]", "to include 5"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = IncludeMatcher.new(1, 2, 3)
+ matcher.matches?([1,2,3])
+ matcher.negative_failure_message.should == ["Expected [1, 2, 3]", "not to include 3"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/infinity_spec.rb b/spec/mspec/spec/matchers/infinity_spec.rb
new file mode 100644
index 0000000000..6eb8ac2940
--- /dev/null
+++ b/spec/mspec/spec/matchers/infinity_spec.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/guards'
+require 'mspec/helpers'
+require 'mspec/matchers'
+
+describe InfinityMatcher do
+ it "matches when actual is infinite and has the correct sign" do
+ InfinityMatcher.new(1).matches?(infinity_value).should == true
+ InfinityMatcher.new(-1).matches?(-infinity_value).should == true
+ end
+
+ it "does not match when actual is not infinite" do
+ InfinityMatcher.new(1).matches?(1.0).should == false
+ InfinityMatcher.new(-1).matches?(-1.0).should == false
+ end
+
+ it "does not match when actual is infinite but has the incorrect sign" do
+ InfinityMatcher.new(1).matches?(-infinity_value).should == false
+ InfinityMatcher.new(-1).matches?(infinity_value).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = InfinityMatcher.new(-1)
+ matcher.matches?(0)
+ matcher.failure_message.should == ["Expected 0", "to be -Infinity"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = InfinityMatcher.new(1)
+ matcher.matches?(infinity_value)
+ matcher.negative_failure_message.should == ["Expected Infinity", "not to be Infinity"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/match_yaml_spec.rb b/spec/mspec/spec/matchers/match_yaml_spec.rb
new file mode 100644
index 0000000000..4f16aee0ec
--- /dev/null
+++ b/spec/mspec/spec/matchers/match_yaml_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe MatchYAMLMatcher do
+ before :each do
+ @matcher = MatchYAMLMatcher.new("--- \nfoo: bar\n")
+ end
+
+ it "compares YAML documents and matches if they're equivalent" do
+ @matcher.matches?("--- \nfoo: bar\n").should == true
+ end
+
+ it "compares YAML documents and does not match if they're not equivalent" do
+ @matcher.matches?("--- \nbar: foo\n").should == false
+ @matcher.matches?("--- \nfoo: \nbar\n").should == false
+ end
+
+ it "also receives objects that respond_to to_yaml" do
+ matcher = MatchYAMLMatcher.new("some string")
+ matcher.matches?("some string").should == true
+
+ matcher = MatchYAMLMatcher.new(['a', 'b'])
+ matcher.matches?("--- \n- a\n- b\n").should == true
+
+ matcher = MatchYAMLMatcher.new("foo" => "bar")
+ matcher.matches?("--- \nfoo: bar\n").should == true
+ end
+
+ it "matches documents with trailing whitespace" do
+ @matcher.matches?("--- \nfoo: bar \n").should == true
+ @matcher.matches?("--- \nfoo: bar \n").should == true
+ end
+
+ it "fails with a descriptive error message" do
+ @matcher.matches?("foo").should == false
+ @matcher.failure_message.should == ["Expected \"foo\"", " to match \"--- \\nfoo: bar\\n\""]
+ end
+end
diff --git a/spec/mspec/spec/matchers/output_spec.rb b/spec/mspec/spec/matchers/output_spec.rb
new file mode 100644
index 0000000000..264da3b569
--- /dev/null
+++ b/spec/mspec/spec/matchers/output_spec.rb
@@ -0,0 +1,74 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe OutputMatcher do
+ it "matches when executing the proc results in the expected output to $stdout" do
+ proc = Proc.new { puts "bang!" }
+ OutputMatcher.new("bang!\n", nil).matches?(proc).should == true
+ OutputMatcher.new("pop", nil).matches?(proc).should == false
+ OutputMatcher.new(/bang/, nil).matches?(proc).should == true
+ OutputMatcher.new(/po/, nil).matches?(proc).should == false
+ end
+
+ it "matches when executing the proc results in the expected output to $stderr" do
+ proc = Proc.new { $stderr.write "boom!" }
+ OutputMatcher.new(nil, "boom!").matches?(proc).should == true
+ OutputMatcher.new(nil, "fizzle").matches?(proc).should == false
+ OutputMatcher.new(nil, /boom/).matches?(proc).should == true
+ OutputMatcher.new(nil, /fizzl/).matches?(proc).should == false
+ end
+
+ it "provides a useful failure message" do
+ proc = Proc.new { print "unexpected"; $stderr.print "unerror" }
+ matcher = OutputMatcher.new("expected", "error")
+ matcher.matches?(proc)
+ matcher.failure_message.should ==
+ ["Expected:\n $stdout: \"expected\"\n $stderr: \"error\"\n",
+ " got:\n $stdout: \"unexpected\"\n $stderr: \"unerror\"\n"]
+ matcher = OutputMatcher.new("expected", nil)
+ matcher.matches?(proc)
+ matcher.failure_message.should ==
+ ["Expected:\n $stdout: \"expected\"\n",
+ " got:\n $stdout: \"unexpected\"\n"]
+ matcher = OutputMatcher.new(nil, "error")
+ matcher.matches?(proc)
+ matcher.failure_message.should ==
+ ["Expected:\n $stderr: \"error\"\n",
+ " got:\n $stderr: \"unerror\"\n"]
+ matcher = OutputMatcher.new(/base/, nil)
+ matcher.matches?(proc)
+ matcher.failure_message.should ==
+ ["Expected:\n $stdout: /base/\n",
+ " got:\n $stdout: \"unexpected\"\n"]
+ matcher = OutputMatcher.new(nil, /octave/)
+ matcher.matches?(proc)
+ matcher.failure_message.should ==
+ ["Expected:\n $stderr: /octave/\n",
+ " got:\n $stderr: \"unerror\"\n"]
+ end
+
+ it "provides a useful negative failure message" do
+ proc = Proc.new { puts "expected"; $stderr.puts "error" }
+ matcher = OutputMatcher.new("expected", "error")
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected output not to be:\n", " $stdout: \"expected\"\n $stderr: \"error\"\n"]
+ matcher = OutputMatcher.new("expected", nil)
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected output not to be:\n", " $stdout: \"expected\"\n"]
+ matcher = OutputMatcher.new(nil, "error")
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected output not to be:\n", " $stderr: \"error\"\n"]
+ matcher = OutputMatcher.new(/expect/, nil)
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected output not to be:\n", " $stdout: \"expected\"\n"]
+ matcher = OutputMatcher.new(nil, /err/)
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected output not to be:\n", " $stderr: \"error\"\n"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/output_to_fd_spec.rb b/spec/mspec/spec/matchers/output_to_fd_spec.rb
new file mode 100644
index 0000000000..e584c4e003
--- /dev/null
+++ b/spec/mspec/spec/matchers/output_to_fd_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe OutputToFDMatcher do
+ # Figure out how in the hell to achieve this
+ it "matches when running the block produces the expected output to the given FD" do
+ OutputToFDMatcher.new("Hi\n", STDERR).matches?(lambda { $stderr.print "Hi\n" }).should == true
+ end
+
+ it "does not match if running the block does not produce the expected output to the FD" do
+ OutputToFDMatcher.new("Hi\n", STDERR).matches?(lambda { $stderr.puts("Hello\n") }).should == false
+ end
+
+ it "propagate the exception if one is thrown while matching" do
+ exc = RuntimeError.new("propagates")
+ lambda {
+ OutputToFDMatcher.new("Hi\n", STDERR).matches?(lambda {
+ raise exc
+ }).should == false
+ }.should raise_error(exc)
+ end
+
+ it "defaults to matching against STDOUT" do
+ object = Object.new
+ object.extend MSpecMatchers
+ object.send(:output_to_fd, "Hi\n").matches?(lambda { $stdout.print "Hi\n" }).should == true
+ end
+
+ it "accepts any IO instance" do
+ io = IO.new STDOUT.fileno
+ OutputToFDMatcher.new("Hi\n", io).matches?(lambda { io.print "Hi\n" }).should == true
+ end
+
+ it "allows matching with a Regexp" do
+ s = "Hi there\n"
+ OutputToFDMatcher.new(/Hi/, STDERR).matches?(lambda { $stderr.print s }).should == true
+ OutputToFDMatcher.new(/Hi?/, STDERR).matches?(lambda { $stderr.print s }).should == true
+ OutputToFDMatcher.new(/[hH]i?/, STDERR).matches?(lambda { $stderr.print s }).should == true
+ OutputToFDMatcher.new(/.*/, STDERR).matches?(lambda { $stderr.print s }).should == true
+ OutputToFDMatcher.new(/H.*?here/, STDERR).matches?(lambda { $stderr.print s }).should == true
+ OutputToFDMatcher.new(/Ahoy/, STDERR).matches?(lambda { $stderr.print s }).should == false
+ end
+end
diff --git a/spec/mspec/spec/matchers/raise_error_spec.rb b/spec/mspec/spec/matchers/raise_error_spec.rb
new file mode 100644
index 0000000000..7c93f0f64c
--- /dev/null
+++ b/spec/mspec/spec/matchers/raise_error_spec.rb
@@ -0,0 +1,120 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class ExpectedException < Exception; end
+class UnexpectedException < Exception; end
+
+describe RaiseErrorMatcher do
+ it "matches when the proc raises the expected exception" do
+ proc = Proc.new { raise ExpectedException }
+ matcher = RaiseErrorMatcher.new(ExpectedException, nil)
+ matcher.matches?(proc).should == true
+ end
+
+ it "executes it's optional block if matched" do
+ run = false
+ proc = Proc.new { raise ExpectedException }
+ matcher = RaiseErrorMatcher.new(ExpectedException, nil) { |error|
+ run = true
+ error.class.should == ExpectedException
+ }
+
+ matcher.matches?(proc).should == true
+ run.should == true
+ end
+
+ it "matches when the proc raises the expected exception with the expected message" do
+ proc = Proc.new { raise ExpectedException, "message" }
+ matcher = RaiseErrorMatcher.new(ExpectedException, "message")
+ matcher.matches?(proc).should == true
+ end
+
+ it "matches when the proc raises the expected exception with a matching message" do
+ proc = Proc.new { raise ExpectedException, "some message" }
+ matcher = RaiseErrorMatcher.new(ExpectedException, /some/)
+ matcher.matches?(proc).should == true
+ end
+
+ it "does not match when the proc does not raise the expected exception" do
+ exc = UnexpectedException.new
+ matcher = RaiseErrorMatcher.new(ExpectedException, nil)
+
+ matcher.matching_exception?(exc).should == false
+ lambda {
+ matcher.matches?(Proc.new { raise exc })
+ }.should raise_error(UnexpectedException)
+ end
+
+ it "does not match when the proc raises the expected exception with an unexpected message" do
+ exc = ExpectedException.new("unexpected")
+ matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
+
+ matcher.matching_exception?(exc).should == false
+ lambda {
+ matcher.matches?(Proc.new { raise exc })
+ }.should raise_error(ExpectedException)
+ end
+
+ it "does not match when the proc does not raise an exception" do
+ proc = Proc.new {}
+ matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
+ matcher.matches?(proc).should == false
+ end
+
+ it "provides a useful failure message" do
+ exc = UnexpectedException.new("unexpected")
+ matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
+
+ matcher.matching_exception?(exc).should == false
+ lambda {
+ matcher.matches?(Proc.new { raise exc })
+ }.should raise_error(UnexpectedException)
+ matcher.failure_message.should ==
+ ["Expected ExpectedException (expected)", "but got UnexpectedException (unexpected)"]
+ end
+
+ it "provides a useful failure message when no exception is raised" do
+ proc = Proc.new { 120 }
+ matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
+ matcher.matches?(proc)
+ matcher.failure_message.should ==
+ ["Expected ExpectedException (expected)", "but no exception was raised (120 was returned)"]
+ end
+
+ it "provides a useful failure message when no exception is raised and nil is returned" do
+ proc = Proc.new { nil }
+ matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
+ matcher.matches?(proc)
+ matcher.failure_message.should ==
+ ["Expected ExpectedException (expected)", "but no exception was raised (nil was returned)"]
+ end
+
+ it "provides a useful failure message when no exception is raised and the result raises in #pretty_inspect" do
+ result = Object.new
+ def result.pretty_inspect
+ raise ArgumentError, "bad"
+ end
+ proc = Proc.new { result }
+ matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
+ matcher.matches?(proc)
+ matcher.failure_message.should ==
+ ["Expected ExpectedException (expected)", "but no exception was raised (#pretty_inspect raised ArgumentError; A #<Object> was returned)"]
+ end
+
+ it "provides a useful negative failure message" do
+ proc = Proc.new { raise ExpectedException, "expected" }
+ matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected to not get ExpectedException (expected)", ""]
+ end
+
+ it "provides a useful negative failure message for strict subclasses of the matched exception class" do
+ proc = Proc.new { raise UnexpectedException, "unexpected" }
+ matcher = RaiseErrorMatcher.new(Exception, nil)
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected to not get Exception", "but got UnexpectedException (unexpected)"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/respond_to_spec.rb b/spec/mspec/spec/matchers/respond_to_spec.rb
new file mode 100644
index 0000000000..988caf4dff
--- /dev/null
+++ b/spec/mspec/spec/matchers/respond_to_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe RespondToMatcher do
+ it "matches when actual does respond_to? expected" do
+ RespondToMatcher.new(:to_s).matches?(Object.new).should == true
+ RespondToMatcher.new(:inject).matches?([]).should == true
+ RespondToMatcher.new(:[]).matches?(1).should == true
+ RespondToMatcher.new(:[]=).matches?("string").should == true
+ end
+
+ it "does not match when actual does not respond_to? expected" do
+ RespondToMatcher.new(:to_i).matches?(Object.new).should == false
+ RespondToMatcher.new(:inject).matches?(1).should == false
+ RespondToMatcher.new(:non_existent_method).matches?([]).should == false
+ RespondToMatcher.new(:[]=).matches?(1).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = RespondToMatcher.new(:non_existent_method)
+ matcher.matches?('string')
+ matcher.failure_message.should == [
+ "Expected \"string\" (String)", "to respond to non_existent_method"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = RespondToMatcher.new(:to_i)
+ matcher.matches?(4.0)
+ matcher.negative_failure_message.should == [
+ "Expected 4.0 (Float)", "not to respond to to_i"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/signed_zero_spec.rb b/spec/mspec/spec/matchers/signed_zero_spec.rb
new file mode 100644
index 0000000000..9c5c50c602
--- /dev/null
+++ b/spec/mspec/spec/matchers/signed_zero_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe SignedZeroMatcher do
+ it "matches when actual is zero and has the correct sign" do
+ SignedZeroMatcher.new(1).matches?(0.0).should == true
+ SignedZeroMatcher.new(-1).matches?(-0.0).should == true
+ end
+
+ it "does not match when actual is non-zero" do
+ SignedZeroMatcher.new(1).matches?(1.0).should == false
+ SignedZeroMatcher.new(-1).matches?(-1.0).should == false
+ end
+
+ it "does not match when actual is zero but has the incorrect sign" do
+ SignedZeroMatcher.new(1).matches?(-0.0).should == false
+ SignedZeroMatcher.new(-1).matches?(0.0).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = SignedZeroMatcher.new(-1)
+ matcher.matches?(0.0)
+ matcher.failure_message.should == ["Expected 0.0", "to be -0.0"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = SignedZeroMatcher.new(-1)
+ matcher.matches?(-0.0)
+ matcher.negative_failure_message.should == ["Expected -0.0", "not to be -0.0"]
+ end
+end
diff --git a/spec/mspec/spec/mocks/mock_spec.rb b/spec/mspec/spec/mocks/mock_spec.rb
new file mode 100644
index 0000000000..8cf04cf462
--- /dev/null
+++ b/spec/mspec/spec/mocks/mock_spec.rb
@@ -0,0 +1,530 @@
+# This is a bit awkward. Currently the way to verify that the
+# opposites are true (for example a failure when the specified
+# arguments are NOT provided) is to simply alter the particular
+# spec to a failure condition.
+require 'spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/mocks/mock'
+require 'mspec/mocks/proxy'
+
+describe Mock, ".mocks" do
+ it "returns a Hash" do
+ Mock.mocks.should be_kind_of(Hash)
+ end
+end
+
+describe Mock, ".stubs" do
+ it "returns a Hash" do
+ Mock.stubs.should be_kind_of(Hash)
+ end
+end
+
+describe Mock, ".replaced_name" do
+ it "returns the name for a method that is being replaced by a mock method" do
+ m = double('a fake id')
+ Mock.replaced_name(m, :method_call).should == :"__mspec_#{m.object_id}_method_call__"
+ end
+end
+
+describe Mock, ".replaced_key" do
+ it "returns a key used internally by Mock" do
+ m = double('a fake id')
+ Mock.replaced_key(m, :method_call).should == [:"__mspec_#{m.object_id}_method_call__", :method_call]
+ end
+end
+
+describe Mock, ".replaced?" do
+ before :each do
+ @mock = double('install_method')
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ end
+
+ it "returns true if a method has been stubbed on an object" do
+ Mock.install_method @mock, :method_call
+ Mock.replaced?(Mock.replaced_name(@mock, :method_call)).should be_true
+ end
+
+ it "returns true if a method has been mocked on an object" do
+ Mock.install_method @mock, :method_call, :stub
+ Mock.replaced?(Mock.replaced_name(@mock, :method_call)).should be_true
+ end
+
+ it "returns false if a method has not been stubbed or mocked" do
+ Mock.replaced?(Mock.replaced_name(@mock, :method_call)).should be_false
+ end
+end
+
+describe Mock, ".name_or_inspect" do
+ before :each do
+ @mock = double("I have a #name")
+ end
+
+ it "returns the value of @name if set" do
+ @mock.instance_variable_set(:@name, "Myself")
+ Mock.name_or_inspect(@mock).should == "Myself"
+ end
+end
+
+describe Mock, ".install_method for mocks" do
+ before :each do
+ @mock = double('install_method')
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ end
+
+ after :each do
+ Mock.cleanup
+ end
+
+ it "returns a MockProxy instance" do
+ Mock.install_method(@mock, :method_call).should be_an_instance_of(MockProxy)
+ end
+
+ it "does not override a previously mocked method with the same name" do
+ Mock.install_method(@mock, :method_call).with(:a, :b).and_return(1)
+ Mock.install_method(@mock, :method_call).with(:c).and_return(2)
+ @mock.method_call(:a, :b)
+ @mock.method_call(:c)
+ lambda { @mock.method_call(:d) }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ # This illustrates RSpec's behavior. This spec fails in mock call count verification
+ # on RSpec (i.e. Mock 'foo' expected :foo with (any args) once, but received it 0 times)
+ # and we mimic the behavior of RSpec.
+ #
+ # describe "A mock receiving multiple calls to #should_receive" do
+ # it "returns the first value mocked" do
+ # m = mock 'multiple #should_receive'
+ # m.should_receive(:foo).and_return(true)
+ # m.foo.should == true
+ # m.should_receive(:foo).and_return(false)
+ # m.foo.should == true
+ # end
+ # end
+ #
+ it "does not override a previously mocked method having the same arguments" do
+ Mock.install_method(@mock, :method_call).with(:a).and_return(true)
+ @mock.method_call(:a).should == true
+ Mock.install_method(@mock, :method_call).with(:a).and_return(false)
+ @mock.method_call(:a).should == true
+ lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "properly sends #respond_to? calls to the aliased respond_to? method when not matching mock expectations" do
+ Mock.install_method(@mock, :respond_to?).with(:to_str).and_return('mock to_str')
+ Mock.install_method(@mock, :respond_to?).with(:to_int).and_return('mock to_int')
+ @mock.respond_to?(:to_str).should == 'mock to_str'
+ @mock.respond_to?(:to_int).should == 'mock to_int'
+ @mock.respond_to?(:to_s).should == true
+ @mock.respond_to?(:not_really_a_real_method_seriously).should == false
+ end
+
+ it "adds to the expectation tally" do
+ state = double("run state").as_null_object
+ state.stub(:state).and_return(double("spec state"))
+ MSpec.should_receive(:current).and_return(state)
+ MSpec.should_receive(:actions).with(:expectation, state.state)
+ Mock.install_method(@mock, :method_call).and_return(1)
+ @mock.method_call.should == 1
+ end
+
+ it "registers that an expectation has been encountered" do
+ state = double("run state").as_null_object
+ state.stub(:state).and_return(double("spec state"))
+ MSpec.should_receive(:expectation)
+ Mock.install_method(@mock, :method_call).and_return(1)
+ @mock.method_call.should == 1
+ end
+end
+
+describe Mock, ".install_method for stubs" do
+ before :each do
+ @mock = double('install_method')
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ end
+
+ after :each do
+ Mock.cleanup
+ end
+
+ it "returns a MockProxy instance" do
+ Mock.install_method(@mock, :method_call, :stub).should be_an_instance_of(MockProxy)
+ end
+
+ # This illustrates RSpec's behavior. This spec passes on RSpec and we mimic it
+ #
+ # describe "A mock receiving multiple calls to #stub" do
+ # it "returns the last value stubbed" do
+ # m = mock 'multiple #stub'
+ # m.stub(:foo).and_return(true)
+ # m.foo.should == true
+ # m.stub(:foo).and_return(false)
+ # m.foo.should == false
+ # end
+ # end
+ it "inserts new stubs before old stubs" do
+ Mock.install_method(@mock, :method_call, :stub).with(:a).and_return(true)
+ @mock.method_call(:a).should == true
+ Mock.install_method(@mock, :method_call, :stub).with(:a).and_return(false)
+ @mock.method_call(:a).should == false
+ Mock.verify_count
+ end
+
+ it "does not add to the expectation tally" do
+ state = double("run state").as_null_object
+ state.stub(:state).and_return(double("spec state"))
+ MSpec.should_not_receive(:actions)
+ Mock.install_method(@mock, :method_call, :stub).and_return(1)
+ @mock.method_call.should == 1
+ end
+end
+
+describe Mock, ".install_method" do
+ before :each do
+ @mock = double('install_method')
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ end
+
+ after :each do
+ Mock.cleanup
+ end
+
+ it "does not alias a mocked or stubbed method when installing a new mock or stub" do
+ @mock.should_not respond_to(:method_call)
+
+ Mock.install_method @mock, :method_call
+ @mock.should respond_to(:method_call)
+ @mock.should_not respond_to(Mock.replaced_name(@mock, :method_call))
+
+ Mock.install_method @mock, :method_call, :stub
+ @mock.should respond_to(:method_call)
+ @mock.should_not respond_to(Mock.replaced_name(@mock, :method_call))
+ end
+end
+
+class MockAndRaiseError < Exception; end
+
+describe Mock, ".verify_call" do
+ before :each do
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+
+ @mock = double('verify_call')
+ @proxy = Mock.install_method @mock, :method_call
+ end
+
+ after :each do
+ ScratchPad.clear
+ Mock.cleanup
+ end
+
+ it "does not raise an exception when the mock method receives the expected arguments" do
+ @proxy.with(1, 'two', :three)
+ Mock.verify_call @mock, :method_call, 1, 'two', :three
+ end
+
+ it "raises an SpecExpectationNotMetError when the mock method does not receive the expected arguments" do
+ @proxy.with(4, 2)
+ lambda {
+ Mock.verify_call @mock, :method_call, 42
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "raises an SpecExpectationNotMetError when the mock method is called with arguments but expects none" do
+ lambda {
+ @proxy.with(:no_args)
+ Mock.verify_call @mock, :method_call, "hello"
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "raises an SpecExpectationNotMetError when the mock method is called with no arguments but expects some" do
+ @proxy.with("hello", "beautiful", "world")
+ lambda {
+ Mock.verify_call @mock, :method_call
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "does not raise an exception when the mock method is called with arguments and is expecting :any_args" do
+ @proxy.with(:any_args)
+ Mock.verify_call @mock, :method_call, 1, 2, 3
+ end
+
+ it "yields a passed block when it is expected to" do
+ @proxy.and_yield()
+ Mock.verify_call @mock, :method_call do
+ ScratchPad.record true
+ end
+ ScratchPad.recorded.should == true
+ end
+
+ it "does not yield a passed block when it is not expected to" do
+ Mock.verify_call @mock, :method_call do
+ ScratchPad.record true
+ end
+ ScratchPad.recorded.should == nil
+ end
+
+ it "can yield subsequently" do
+ @proxy.and_yield(1).and_yield(2).and_yield(3)
+
+ ScratchPad.record []
+ Mock.verify_call @mock, :method_call do |arg|
+ ScratchPad << arg
+ end
+ ScratchPad.recorded.should == [1, 2, 3]
+ end
+
+ it "can yield and return an expected value" do
+ @proxy.and_yield(1).and_return(3)
+
+ Mock.verify_call(@mock, :method_call) { |arg| ScratchPad.record arg }.should == 3
+ ScratchPad.recorded.should == 1
+ end
+
+ it "raises an exception when it is expected to yield but no block is given" do
+ @proxy.and_yield(1, 2, 3)
+ lambda {
+ Mock.verify_call(@mock, :method_call)
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "raises an exception when it is expected to yield more arguments than the block can take" do
+ @proxy.and_yield(1, 2, 3)
+ lambda {
+ Mock.verify_call(@mock, :method_call) {|a, b|}
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "does not raise an exception when it is expected to yield to a block that can take any number of arguments" do
+ @proxy.and_yield(1, 2, 3)
+ expect {
+ Mock.verify_call(@mock, :method_call) {|*a|}
+ }.not_to raise_error
+ end
+
+ it "raises an exception when expected to" do
+ @proxy.and_raise(MockAndRaiseError)
+ lambda {
+ Mock.verify_call @mock, :method_call
+ }.should raise_error(MockAndRaiseError)
+ end
+end
+
+describe Mock, ".verify_call mixing mocks and stubs" do
+ before :each do
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+
+ @mock = double('verify_call')
+ end
+
+ after :each do
+ ScratchPad.clear
+ Mock.cleanup
+ end
+
+ it "checks the mock arguments when a mock is defined after a stub" do
+ Mock.install_method @mock, :method_call, :stub
+ Mock.install_method(@mock, :method_call, :mock).with("arg")
+
+ -> {
+ @mock.method_call
+ }.should raise_error(SpecExpectationNotMetError, /called with unexpected arguments \(\)/)
+
+ -> {
+ @mock.method_call("a", "b")
+ }.should raise_error(SpecExpectationNotMetError, /called with unexpected arguments \("a", "b"\)/)
+
+ -> {
+ @mock.method_call("foo")
+ }.should raise_error(SpecExpectationNotMetError, /called with unexpected arguments \("foo"\)/)
+
+ @mock.method_call("arg")
+ end
+
+ it "checks the mock arguments when a stub is defined after a mock" do
+ Mock.install_method(@mock, :method_call, :mock).with("arg")
+ Mock.install_method @mock, :method_call, :stub
+
+ -> {
+ @mock.method_call
+ }.should raise_error(SpecExpectationNotMetError, /called with unexpected arguments \(\)/)
+
+ -> {
+ @mock.method_call("a", "b")
+ }.should raise_error(SpecExpectationNotMetError, /called with unexpected arguments \("a", "b"\)/)
+
+ -> {
+ @mock.method_call("foo")
+ }.should raise_error(SpecExpectationNotMetError, /called with unexpected arguments \("foo"\)/)
+
+ @mock.method_call("arg")
+ end
+end
+
+describe Mock, ".verify_count" do
+ before :each do
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+
+ @mock = double('verify_count')
+ @proxy = Mock.install_method @mock, :method_call
+ end
+
+ after :each do
+ Mock.cleanup
+ end
+
+ it "does not raise an exception when the mock receives at least the expected number of calls" do
+ @proxy.at_least(2)
+ @mock.method_call
+ @mock.method_call
+ Mock.verify_count
+ end
+
+ it "raises an SpecExpectationNotMetError when the mock receives less than at least the expected number of calls" do
+ @proxy.at_least(2)
+ @mock.method_call
+ lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "does not raise an exception when the mock receives at most the expected number of calls" do
+ @proxy.at_most(2)
+ @mock.method_call
+ @mock.method_call
+ Mock.verify_count
+ end
+
+ it "raises an SpecExpectationNotMetError when the mock receives more than at most the expected number of calls" do
+ @proxy.at_most(2)
+ @mock.method_call
+ @mock.method_call
+ @mock.method_call
+ lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "does not raise an exception when the mock receives exactly the expected number of calls" do
+ @proxy.exactly(2)
+ @mock.method_call
+ @mock.method_call
+ Mock.verify_count
+ end
+
+ it "raises an SpecExpectationNotMetError when the mock receives less than exactly the expected number of calls" do
+ @proxy.exactly(2)
+ @mock.method_call
+ lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "raises an SpecExpectationNotMetError when the mock receives more than exactly the expected number of calls" do
+ @proxy.exactly(2)
+ @mock.method_call
+ @mock.method_call
+ @mock.method_call
+ lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError)
+ end
+end
+
+describe Mock, ".verify_count mixing mocks and stubs" do
+ before :each do
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+
+ @mock = double('verify_count')
+ end
+
+ after :each do
+ Mock.cleanup
+ end
+
+ it "does not raise an exception for a stubbed method that is never called" do
+ Mock.install_method @mock, :method_call, :stub
+ Mock.verify_count
+ end
+
+ it "verifies the calls to the mocked method when a mock is defined after a stub" do
+ Mock.install_method @mock, :method_call, :stub
+ Mock.install_method @mock, :method_call, :mock
+
+ -> {
+ Mock.verify_count
+ }.should raise_error(SpecExpectationNotMetError, /received it 0 times/)
+
+ @mock.method_call
+ Mock.verify_count
+ end
+
+ it "verifies the calls to the mocked method when a mock is defined before a stub" do
+ Mock.install_method @mock, :method_call, :mock
+ Mock.install_method @mock, :method_call, :stub
+
+ -> {
+ Mock.verify_count
+ }.should raise_error(SpecExpectationNotMetError, /received it 0 times/)
+
+ @mock.method_call
+ Mock.verify_count
+ end
+end
+
+describe Mock, ".cleanup" do
+ before :each do
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+
+ @mock = double('cleanup')
+ @proxy = Mock.install_method @mock, :method_call
+ end
+
+ after :each do
+ Mock.cleanup
+ end
+
+ it "removes the mock method call if it did not override an existing method" do
+ @mock.should respond_to(:method_call)
+
+ Mock.cleanup
+ @mock.should_not respond_to(:method_call)
+ end
+
+ it "removes the replaced method if the mock method overrides an existing method" do
+ def @mock.already_here() :hey end
+ @mock.should respond_to(:already_here)
+ replaced_name = Mock.replaced_name(@mock, :already_here)
+ Mock.install_method @mock, :already_here
+ @mock.should respond_to(replaced_name)
+
+ Mock.cleanup
+ @mock.should_not respond_to(replaced_name)
+ @mock.should respond_to(:already_here)
+ @mock.already_here.should == :hey
+ end
+
+ it "removes all mock expectations" do
+ Mock.mocks.should == { Mock.replaced_key(@mock, :method_call) => [@proxy] }
+ Mock.cleanup
+ Mock.mocks.should == {}
+ end
+
+ it "removes all stubs" do
+ Mock.cleanup # remove @proxy
+ @stub = Mock.install_method @mock, :method_call, :stub
+ Mock.stubs.should == { Mock.replaced_key(@mock, :method_call) => [@stub] }
+ Mock.cleanup
+ Mock.stubs.should == {}
+ end
+
+ it "removes the replaced name for mocks" do
+ replaced_key = Mock.replaced_key(@mock, :method_call)
+ Mock.should_receive(:clear_replaced).with(replaced_key)
+
+ replaced_name = Mock.replaced_name(@mock, :method_call)
+ Mock.replaced?(replaced_name).should be_true
+
+ Mock.cleanup
+ Mock.replaced?(replaced_name).should be_false
+ end
+end
diff --git a/spec/mspec/spec/mocks/proxy_spec.rb b/spec/mspec/spec/mocks/proxy_spec.rb
new file mode 100644
index 0000000000..d9e754b972
--- /dev/null
+++ b/spec/mspec/spec/mocks/proxy_spec.rb
@@ -0,0 +1,405 @@
+require 'spec_helper'
+require 'mspec/mocks/proxy'
+
+describe MockObject, ".new" do
+ it "creates a new mock object" do
+ m = MockObject.new('not a null object')
+ lambda { m.not_a_method }.should raise_error(NoMethodError)
+ end
+
+ it "creates a new mock object that follows the NullObject pattern" do
+ m = MockObject.new('null object', :null_object => true)
+ m.not_really_a_method.should equal(m)
+ end
+end
+
+describe MockProxy, ".new" do
+ it "creates a mock proxy by default" do
+ MockProxy.new.mock?.should be_true
+ end
+
+ it "creates a stub proxy by request" do
+ MockProxy.new(:stub).stub?.should be_true
+ end
+
+ it "sets the call expectation to 1 call for a mock" do
+ MockProxy.new.count.should == [:exactly, 1]
+ end
+
+ it "sets the call expectation to any number of times for a stub" do
+ MockProxy.new(:stub).count.should == [:any_number_of_times, 0]
+ end
+end
+
+describe MockProxy, "#count" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns the expected number of calls the mock should receive" do
+ @proxy.count.should == [:exactly, 1]
+ @proxy.at_least(3).count.should == [:at_least, 3]
+ end
+end
+
+describe MockProxy, "#arguments" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns the expected arguments" do
+ @proxy.arguments.should == :any_args
+ end
+end
+
+describe MockProxy, "#with" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.with(:a).should be_equal(@proxy)
+ end
+
+ it "raises an ArgumentError if no arguments are given" do
+ lambda { @proxy.with }.should raise_error(ArgumentError)
+ end
+
+ it "accepts any number of arguments" do
+ @proxy.with(1, 2, 3).should be_an_instance_of(MockProxy)
+ @proxy.arguments.should == [1,2,3]
+ end
+end
+
+describe MockProxy, "#once" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.once.should be_equal(@proxy)
+ end
+
+ it "sets the expected calls to 1" do
+ @proxy.once
+ @proxy.count.should == [:exactly, 1]
+ end
+
+ it "accepts no arguments" do
+ lambda { @proxy.once(:a) }.should raise_error
+ end
+end
+
+describe MockProxy, "#twice" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.twice.should be_equal(@proxy)
+ end
+
+ it "sets the expected calls to 2" do
+ @proxy.twice
+ @proxy.count.should == [:exactly, 2]
+ end
+
+ it "accepts no arguments" do
+ lambda { @proxy.twice(:b) }.should raise_error
+ end
+end
+
+describe MockProxy, "#exactly" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.exactly(2).should be_equal(@proxy)
+ end
+
+ it "sets the expected calls to exactly n" do
+ @proxy.exactly(5)
+ @proxy.count.should == [:exactly, 5]
+ end
+
+ it "does not accept an argument that Integer() cannot convert" do
+ lambda { @proxy.exactly('x') }.should raise_error
+ end
+end
+
+describe MockProxy, "#at_least" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.at_least(3).should be_equal(@proxy)
+ end
+
+ it "sets the expected calls to at least n" do
+ @proxy.at_least(3)
+ @proxy.count.should == [:at_least, 3]
+ end
+
+ it "accepts :once :twice" do
+ @proxy.at_least(:once)
+ @proxy.count.should == [:at_least, 1]
+ @proxy.at_least(:twice)
+ @proxy.count.should == [:at_least, 2]
+ end
+
+ it "does not accept an argument that Integer() cannot convert" do
+ lambda { @proxy.at_least('x') }.should raise_error
+ end
+end
+
+describe MockProxy, "#at_most" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.at_most(2).should be_equal(@proxy)
+ end
+
+ it "sets the expected calls to at most n" do
+ @proxy.at_most(2)
+ @proxy.count.should == [:at_most, 2]
+ end
+
+ it "accepts :once, :twice" do
+ @proxy.at_most(:once)
+ @proxy.count.should == [:at_most, 1]
+ @proxy.at_most(:twice)
+ @proxy.count.should == [:at_most, 2]
+ end
+
+ it "does not accept an argument that Integer() cannot convert" do
+ lambda { @proxy.at_most('x') }.should raise_error
+ end
+end
+
+describe MockProxy, "#any_number_of_times" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.any_number_of_times.should be_equal(@proxy)
+ end
+
+ it "sets the expected calls to any number of times" do
+ @proxy.any_number_of_times
+ @proxy.count.should == [:any_number_of_times, 0]
+ end
+
+ it "does not accept an argument" do
+ lambda { @proxy.any_number_of_times(2) }.should raise_error
+ end
+end
+
+describe MockProxy, "#and_return" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.and_return(false).should equal(@proxy)
+ end
+
+ it "sets the expected return value" do
+ @proxy.and_return(false)
+ @proxy.returning.should == false
+ end
+
+ it "accepts any number of return values" do
+ @proxy.and_return(1, 2, 3)
+ @proxy.returning.should == 1
+ @proxy.returning.should == 2
+ @proxy.returning.should == 3
+ end
+
+ it "implicitly sets the expected number of calls" do
+ @proxy.and_return(1, 2, 3)
+ @proxy.count.should == [:exactly, 3]
+ end
+
+ it "only sets the expected number of calls if it is higher than what is already set" do
+ @proxy.at_least(5).times.and_return(1, 2, 3)
+ @proxy.count.should == [:at_least, 5]
+
+ @proxy.at_least(2).times.and_return(1, 2, 3)
+ @proxy.count.should == [:at_least, 3]
+ end
+end
+
+describe MockProxy, "#returning" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns nil by default" do
+ @proxy.returning.should be_nil
+ end
+
+ it "returns the value set by #and_return" do
+ @proxy.and_return(2)
+ @proxy.returning.should == 2
+ @proxy.returning.should == 2
+ end
+
+ it "returns a sequence of values set by #and_return" do
+ @proxy.and_return(1,2,3,4)
+ @proxy.returning.should == 1
+ @proxy.returning.should == 2
+ @proxy.returning.should == 3
+ @proxy.returning.should == 4
+ @proxy.returning.should == 4
+ @proxy.returning.should == 4
+ end
+end
+
+describe MockProxy, "#calls" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns the number of times the proxy is called" do
+ @proxy.calls.should == 0
+ end
+end
+
+describe MockProxy, "#called" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "increments the number of times the proxy is called" do
+ @proxy.called
+ @proxy.called
+ @proxy.calls.should == 2
+ end
+end
+
+describe MockProxy, "#times" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "is a no-op" do
+ @proxy.times.should == @proxy
+ end
+end
+
+describe MockProxy, "#stub?" do
+ it "returns true if the proxy is created as a stub" do
+ MockProxy.new(:stub).stub?.should be_true
+ end
+
+ it "returns false if the proxy is created as a mock" do
+ MockProxy.new(:mock).stub?.should be_false
+ end
+end
+
+describe MockProxy, "#mock?" do
+ it "returns true if the proxy is created as a mock" do
+ MockProxy.new(:mock).mock?.should be_true
+ end
+
+ it "returns false if the proxy is created as a stub" do
+ MockProxy.new(:stub).mock?.should be_false
+ end
+end
+
+describe MockProxy, "#and_yield" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.and_yield(false).should equal(@proxy)
+ end
+
+ it "sets the expected values to yield" do
+ @proxy.and_yield(1).yielding.should == [[1]]
+ end
+
+ it "accepts multiple values to yield" do
+ @proxy.and_yield(1, 2, 3).yielding.should == [[1, 2, 3]]
+ end
+end
+
+describe MockProxy, "#raising" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns nil by default" do
+ @proxy.raising.should be_nil
+ end
+
+ it "returns the exception object passed to #and_raise" do
+ exc = double("exception")
+ @proxy.and_raise(exc)
+ @proxy.raising.should equal(exc)
+ end
+
+ it "returns an instance of RuntimeError when a String is passed to #and_raise" do
+ @proxy.and_raise("an error")
+ exc = @proxy.raising
+ exc.should be_an_instance_of(RuntimeError)
+ exc.message.should == "an error"
+ end
+end
+
+describe MockProxy, "#yielding" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns an empty array by default" do
+ @proxy.yielding.should == []
+ end
+
+ it "returns an array of arrays of values the proxy should yield" do
+ @proxy.and_yield(3)
+ @proxy.yielding.should == [[3]]
+ end
+
+ it "returns an accumulation of arrays of values the proxy should yield" do
+ @proxy.and_yield(1).and_yield(2, 3)
+ @proxy.yielding.should == [[1], [2, 3]]
+ end
+end
+
+describe MockProxy, "#yielding?" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns false if the proxy is not yielding" do
+ @proxy.yielding?.should be_false
+ end
+
+ it "returns true if the proxy is yielding" do
+ @proxy.and_yield(1)
+ @proxy.yielding?.should be_true
+ end
+end
+
+describe MockIntObject, "#to_int" do
+ before :each do
+ @int = MockIntObject.new(10)
+ end
+
+ it "returns the number if to_int is called" do
+ @int.to_int.should == 10
+ @int.count.should == [:at_least, 1]
+ end
+
+ it "tries to convert the target to int if to_int is called" do
+ MockIntObject.new(@int).to_int.should == 10
+ @int.count.should == [:at_least, 1]
+ end
+end
diff --git a/spec/mspec/spec/runner/actions/filter_spec.rb b/spec/mspec/spec/runner/actions/filter_spec.rb
new file mode 100644
index 0000000000..d185781757
--- /dev/null
+++ b/spec/mspec/spec/runner/actions/filter_spec.rb
@@ -0,0 +1,84 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/actions/filter'
+require 'mspec/runner/mspec'
+require 'mspec/runner/tag'
+
+describe ActionFilter do
+ it "creates a filter when not passed a description" do
+ MatchFilter.should_not_receive(:new)
+ ActionFilter.new(nil, nil)
+ end
+
+ it "creates a filter from a single description" do
+ MatchFilter.should_receive(:new).with(nil, "match me")
+ ActionFilter.new(nil, "match me")
+ end
+
+ it "creates a filter from an array of descriptions" do
+ MatchFilter.should_receive(:new).with(nil, "match me", "again")
+ ActionFilter.new(nil, ["match me", "again"])
+ end
+end
+
+describe ActionFilter, "#===" do
+ before :each do
+ MSpec.stub(:read_tags).and_return(["match"])
+ @action = ActionFilter.new(nil, ["catch", "if you"])
+ end
+
+ it "returns false if there are no filters" do
+ action = ActionFilter.new
+ action.===("anything").should == false
+ end
+
+ it "returns true if the argument matches any of the descriptions" do
+ @action.===("catch").should == true
+ @action.===("if you can").should == true
+ end
+
+ it "returns false if the argument does not match any of the descriptions" do
+ @action.===("patch me").should == false
+ @action.===("if I can").should == false
+ end
+end
+
+describe ActionFilter, "#load" do
+ before :each do
+ @tag = SpecTag.new "tag(comment):description"
+ end
+
+ it "creates a filter from a single tag" do
+ MSpec.should_receive(:read_tags).with(["tag"]).and_return([@tag])
+ MatchFilter.should_receive(:new).with(nil, "description")
+ ActionFilter.new("tag", nil).load
+ end
+
+ it "creates a filter from an array of tags" do
+ MSpec.should_receive(:read_tags).with(["tag", "key"]).and_return([@tag])
+ MatchFilter.should_receive(:new).with(nil, "description")
+ ActionFilter.new(["tag", "key"], nil).load
+ end
+
+ it "creates a filter from both tags and descriptions" do
+ MSpec.should_receive(:read_tags).and_return([@tag])
+ filter = ActionFilter.new("tag", ["match me", "again"])
+ MatchFilter.should_receive(:new).with(nil, "description")
+ filter.load
+ end
+end
+
+describe ActionFilter, "#register" do
+ it "registers itself with MSpec for the :load actions" do
+ filter = ActionFilter.new
+ MSpec.should_receive(:register).with(:load, filter)
+ filter.register
+ end
+end
+
+describe ActionFilter, "#unregister" do
+ it "unregisters itself with MSpec for the :load actions" do
+ filter = ActionFilter.new
+ MSpec.should_receive(:unregister).with(:load, filter)
+ filter.unregister
+ end
+end
diff --git a/spec/mspec/spec/runner/actions/tag_spec.rb b/spec/mspec/spec/runner/actions/tag_spec.rb
new file mode 100644
index 0000000000..92df362d02
--- /dev/null
+++ b/spec/mspec/spec/runner/actions/tag_spec.rb
@@ -0,0 +1,315 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/actions/tag'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+require 'mspec/runner/tag'
+
+describe TagAction, ".new" do
+ it "creates an MatchFilter with its tag and desc arguments" do
+ filter = double('action filter').as_null_object
+ MatchFilter.should_receive(:new).with(nil, "some", "thing").and_return(filter)
+ TagAction.new :add, :all, nil, nil, ["tag", "key"], ["some", "thing"]
+ end
+end
+
+describe TagAction, "#===" do
+ before :each do
+ MSpec.stub(:read_tags).and_return(["match"])
+ @action = TagAction.new :add, :fail, nil, nil, nil, ["catch", "if you"]
+ end
+
+ it "returns true if there are no filters" do
+ action = TagAction.new :add, :all, nil, nil
+ action.===("anything").should == true
+ end
+
+ it "returns true if the argument matches any of the descriptions" do
+ @action.===("catch").should == true
+ @action.===("if you can").should == true
+ end
+
+ it "returns false if the argument does not match any of the descriptions" do
+ @action.===("patch me").should == false
+ @action.===("if I can").should == false
+ end
+end
+
+describe TagAction, "#exception?" do
+ before :each do
+ @action = TagAction.new :add, :fail, nil, nil, nil, nil
+ end
+
+ it "returns false if no exception has been raised while evaluating an example" do
+ @action.exception?.should be_false
+ end
+
+ it "returns true if an exception was raised while evaluating an example" do
+ @action.exception ExceptionState.new nil, nil, Exception.new("failed")
+ @action.exception?.should be_true
+ end
+end
+
+describe TagAction, "#outcome?" do
+ before :each do
+ MSpec.stub(:read_tags).and_return([])
+ @exception = ExceptionState.new nil, nil, Exception.new("failed")
+ end
+
+ it "returns true if outcome is :fail and the spec fails" do
+ action = TagAction.new :add, :fail, nil, nil, nil, nil
+ action.exception @exception
+ action.outcome?.should == true
+ end
+
+ it "returns false if the outcome is :fail and the spec passes" do
+ action = TagAction.new :add, :fail, nil, nil, nil, nil
+ action.outcome?.should == false
+ end
+
+ it "returns true if the outcome is :pass and the spec passes" do
+ action = TagAction.new :del, :pass, nil, nil, nil, nil
+ action.outcome?.should == true
+ end
+
+ it "returns false if the outcome is :pass and the spec fails" do
+ action = TagAction.new :del, :pass, nil, nil, nil, nil
+ action.exception @exception
+ action.outcome?.should == false
+ end
+
+ it "returns true if the outcome is :all" do
+ action = TagAction.new :add, :all, nil, nil, nil, nil
+ action.exception @exception
+ action.outcome?.should == true
+ end
+end
+
+describe TagAction, "#before" do
+ it "resets the #exception? flag to false" do
+ action = TagAction.new :add, :fail, nil, nil, nil, nil
+ action.exception?.should be_false
+ action.exception ExceptionState.new(nil, nil, Exception.new("Fail!"))
+ action.exception?.should be_true
+ action.before(ExampleState.new(ContextState.new("describe"), "it"))
+ action.exception?.should be_false
+ end
+end
+
+describe TagAction, "#exception" do
+ it "sets the #exception? flag" do
+ action = TagAction.new :add, :fail, nil, nil, nil, nil
+ action.exception?.should be_false
+ action.exception ExceptionState.new(nil, nil, Exception.new("Fail!"))
+ action.exception?.should be_true
+ end
+end
+
+describe TagAction, "#after when action is :add" do
+ before :each do
+ MSpec.stub(:read_tags).and_return([])
+ context = ContextState.new "Catch#me"
+ @state = ExampleState.new context, "if you can"
+ @tag = SpecTag.new "tag(comment):Catch#me if you can"
+ SpecTag.stub(:new).and_return(@tag)
+ @exception = ExceptionState.new nil, nil, Exception.new("failed")
+ end
+
+ it "does not write a tag if the description does not match" do
+ MSpec.should_not_receive(:write_tag)
+ action = TagAction.new :add, :all, "tag", "comment", nil, "match"
+ action.after @state
+ end
+
+ it "does not write a tag if outcome is :fail and the spec passed" do
+ MSpec.should_not_receive(:write_tag)
+ action = TagAction.new :add, :fail, "tag", "comment", nil, "can"
+ action.after @state
+ end
+
+ it "writes a tag if the outcome is :fail and the spec failed" do
+ MSpec.should_receive(:write_tag).with(@tag)
+ action = TagAction.new :add, :fail, "tag", "comment", nil, "can"
+ action.exception @exception
+ action.after @state
+ end
+
+ it "does not write a tag if outcome is :pass and the spec failed" do
+ MSpec.should_not_receive(:write_tag)
+ action = TagAction.new :add, :pass, "tag", "comment", nil, "can"
+ action.exception @exception
+ action.after @state
+ end
+
+ it "writes a tag if the outcome is :pass and the spec passed" do
+ MSpec.should_receive(:write_tag).with(@tag)
+ action = TagAction.new :add, :pass, "tag", "comment", nil, "can"
+ action.after @state
+ end
+
+ it "writes a tag if the outcome is :all" do
+ MSpec.should_receive(:write_tag).with(@tag)
+ action = TagAction.new :add, :all, "tag", "comment", nil, "can"
+ action.after @state
+ end
+end
+
+describe TagAction, "#after when action is :del" do
+ before :each do
+ MSpec.stub(:read_tags).and_return([])
+ context = ContextState.new "Catch#me"
+ @state = ExampleState.new context, "if you can"
+ @tag = SpecTag.new "tag(comment):Catch#me if you can"
+ SpecTag.stub(:new).and_return(@tag)
+ @exception = ExceptionState.new nil, nil, Exception.new("failed")
+ end
+
+ it "does not delete a tag if the description does not match" do
+ MSpec.should_not_receive(:delete_tag)
+ action = TagAction.new :del, :all, "tag", "comment", nil, "match"
+ action.after @state
+ end
+
+ it "does not delete a tag if outcome is :fail and the spec passed" do
+ MSpec.should_not_receive(:delete_tag)
+ action = TagAction.new :del, :fail, "tag", "comment", nil, "can"
+ action.after @state
+ end
+
+ it "deletes a tag if the outcome is :fail and the spec failed" do
+ MSpec.should_receive(:delete_tag).with(@tag)
+ action = TagAction.new :del, :fail, "tag", "comment", nil, "can"
+ action.exception @exception
+ action.after @state
+ end
+
+ it "does not delete a tag if outcome is :pass and the spec failed" do
+ MSpec.should_not_receive(:delete_tag)
+ action = TagAction.new :del, :pass, "tag", "comment", nil, "can"
+ action.exception @exception
+ action.after @state
+ end
+
+ it "deletes a tag if the outcome is :pass and the spec passed" do
+ MSpec.should_receive(:delete_tag).with(@tag)
+ action = TagAction.new :del, :pass, "tag", "comment", nil, "can"
+ action.after @state
+ end
+
+ it "deletes a tag if the outcome is :all" do
+ MSpec.should_receive(:delete_tag).with(@tag)
+ action = TagAction.new :del, :all, "tag", "comment", nil, "can"
+ action.after @state
+ end
+end
+
+describe TagAction, "#finish" do
+ before :each do
+ $stdout = @out = IOStub.new
+ context = ContextState.new "Catch#me"
+ @state = ExampleState.new context, "if you can"
+ MSpec.stub(:write_tag).and_return(true)
+ MSpec.stub(:delete_tag).and_return(true)
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "reports no specs tagged if none where tagged" do
+ action = TagAction.new :add, :fail, "tag", "comment", nil, "can"
+ action.stub(:outcome?).and_return(false)
+ action.after @state
+ action.finish
+ @out.should == "\nTagAction: no specs were tagged with 'tag'\n"
+ end
+
+ it "reports no specs tagged if none where tagged" do
+ action = TagAction.new :del, :fail, "tag", "comment", nil, "can"
+ action.stub(:outcome?).and_return(false)
+ action.after @state
+ action.finish
+ @out.should == "\nTagAction: no tags 'tag' were deleted\n"
+ end
+
+ it "reports the spec descriptions that were tagged" do
+ action = TagAction.new :add, :fail, "tag", "comment", nil, "can"
+ action.stub(:outcome?).and_return(true)
+ action.after @state
+ action.finish
+ @out.should ==
+%[
+TagAction: specs tagged with 'tag':
+
+Catch#me if you can
+]
+ end
+
+ it "reports the spec descriptions for the tags that were deleted" do
+ action = TagAction.new :del, :fail, "tag", "comment", nil, "can"
+ action.stub(:outcome?).and_return(true)
+ action.after @state
+ action.finish
+ @out.should ==
+%[
+TagAction: tag 'tag' deleted for specs:
+
+Catch#me if you can
+]
+ end
+end
+
+describe TagAction, "#register" do
+ before :each do
+ MSpec.stub(:register)
+ MSpec.stub(:read_tags).and_return([])
+ @action = TagAction.new :add, :all, nil, nil, nil, nil
+ end
+
+ it "registers itself with MSpec for the :before event" do
+ MSpec.should_receive(:register).with(:before, @action)
+ @action.register
+ end
+
+ it "registers itself with MSpec for the :after event" do
+ MSpec.should_receive(:register).with(:after, @action)
+ @action.register
+ end
+
+ it "registers itself with MSpec for the :exception event" do
+ MSpec.should_receive(:register).with(:exception, @action)
+ @action.register
+ end
+
+ it "registers itself with MSpec for the :finish event" do
+ MSpec.should_receive(:register).with(:finish, @action)
+ @action.register
+ end
+end
+
+describe TagAction, "#unregister" do
+ before :each do
+ MSpec.stub(:unregister)
+ MSpec.stub(:read_tags).and_return([])
+ @action = TagAction.new :add, :all, nil, nil, nil, nil
+ end
+
+ it "unregisters itself with MSpec for the :before event" do
+ MSpec.should_receive(:unregister).with(:before, @action)
+ @action.unregister
+ end
+
+ it "unregisters itself with MSpec for the :after event" do
+ MSpec.should_receive(:unregister).with(:after, @action)
+ @action.unregister
+ end
+
+ it "unregisters itself with MSpec for the :exception event" do
+ MSpec.should_receive(:unregister).with(:exception, @action)
+ @action.unregister
+ end
+
+ it "unregisters itself with MSpec for the :finish event" do
+ MSpec.should_receive(:unregister).with(:finish, @action)
+ @action.unregister
+ end
+end
diff --git a/spec/mspec/spec/runner/actions/taglist_spec.rb b/spec/mspec/spec/runner/actions/taglist_spec.rb
new file mode 100644
index 0000000000..418c761c2d
--- /dev/null
+++ b/spec/mspec/spec/runner/actions/taglist_spec.rb
@@ -0,0 +1,152 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/actions/taglist'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+require 'mspec/runner/tag'
+
+describe TagListAction, "#include?" do
+ it "returns true" do
+ TagListAction.new.include?(:anything).should be_true
+ end
+end
+
+describe TagListAction, "#===" do
+ before :each do
+ tag = SpecTag.new "fails:description"
+ MSpec.stub(:read_tags).and_return([tag])
+ @filter = double("MatchFilter").as_null_object
+ MatchFilter.stub(:new).and_return(@filter)
+ @action = TagListAction.new
+ @action.load
+ end
+
+ it "returns true if filter === string returns true" do
+ @filter.should_receive(:===).with("str").and_return(true)
+ @action.===("str").should be_true
+ end
+
+ it "returns false if filter === string returns false" do
+ @filter.should_receive(:===).with("str").and_return(false)
+ @action.===("str").should be_false
+ end
+end
+
+describe TagListAction, "#start" do
+ before :each do
+ @stdout = $stdout
+ $stdout = IOStub.new
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it "prints a banner for specific tags" do
+ action = TagListAction.new ["fails", "unstable"]
+ action.start
+ $stdout.should == "\nListing specs tagged with 'fails', 'unstable'\n\n"
+ end
+
+ it "prints a banner for all tags" do
+ action = TagListAction.new
+ action.start
+ $stdout.should == "\nListing all tagged specs\n\n"
+ end
+end
+
+describe TagListAction, "#load" do
+ before :each do
+ @t1 = SpecTag.new "fails:I fail"
+ @t2 = SpecTag.new "unstable:I'm unstable"
+ end
+
+ it "creates a MatchFilter for matching tags" do
+ MSpec.should_receive(:read_tags).with(["fails"]).and_return([@t1])
+ MatchFilter.should_receive(:new).with(nil, "I fail")
+ TagListAction.new(["fails"]).load
+ end
+
+ it "creates a MatchFilter for all tags" do
+ MSpec.should_receive(:read_tags).and_return([@t1, @t2])
+ MatchFilter.should_receive(:new).with(nil, "I fail", "I'm unstable")
+ TagListAction.new.load
+ end
+
+ it "does not create a MatchFilter if there are no matching tags" do
+ MSpec.stub(:read_tags).and_return([])
+ MatchFilter.should_not_receive(:new)
+ TagListAction.new(["fails"]).load
+ end
+end
+
+describe TagListAction, "#after" do
+ before :each do
+ @stdout = $stdout
+ $stdout = IOStub.new
+
+ @state = double("ExampleState")
+ @state.stub(:description).and_return("str")
+
+ @action = TagListAction.new
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it "prints nothing if the filter does not match" do
+ @action.should_receive(:===).with("str").and_return(false)
+ @action.after(@state)
+ $stdout.should == ""
+ end
+
+ it "prints the example description if the filter matches" do
+ @action.should_receive(:===).with("str").and_return(true)
+ @action.after(@state)
+ $stdout.should == "str\n"
+ end
+end
+
+describe TagListAction, "#register" do
+ before :each do
+ MSpec.stub(:register)
+ @action = TagListAction.new
+ end
+
+ it "registers itself with MSpec for the :start event" do
+ MSpec.should_receive(:register).with(:start, @action)
+ @action.register
+ end
+
+ it "registers itself with MSpec for the :load event" do
+ MSpec.should_receive(:register).with(:load, @action)
+ @action.register
+ end
+
+ it "registers itself with MSpec for the :after event" do
+ MSpec.should_receive(:register).with(:after, @action)
+ @action.register
+ end
+end
+
+describe TagListAction, "#unregister" do
+ before :each do
+ MSpec.stub(:unregister)
+ @action = TagListAction.new
+ end
+
+ it "unregisters itself with MSpec for the :start event" do
+ MSpec.should_receive(:unregister).with(:start, @action)
+ @action.unregister
+ end
+
+ it "unregisters itself with MSpec for the :load event" do
+ MSpec.should_receive(:unregister).with(:load, @action)
+ @action.unregister
+ end
+
+ it "unregisters itself with MSpec for the :after event" do
+ MSpec.should_receive(:unregister).with(:after, @action)
+ @action.unregister
+ end
+end
diff --git a/spec/mspec/spec/runner/actions/tagpurge_spec.rb b/spec/mspec/spec/runner/actions/tagpurge_spec.rb
new file mode 100644
index 0000000000..27ad2a1470
--- /dev/null
+++ b/spec/mspec/spec/runner/actions/tagpurge_spec.rb
@@ -0,0 +1,154 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/actions/tagpurge'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+require 'mspec/runner/tag'
+
+describe TagPurgeAction, "#start" do
+ before :each do
+ @stdout = $stdout
+ $stdout = IOStub.new
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it "prints a banner" do
+ action = TagPurgeAction.new
+ action.start
+ $stdout.should == "\nRemoving tags not matching any specs\n\n"
+ end
+end
+
+describe TagPurgeAction, "#load" do
+ before :each do
+ @t1 = SpecTag.new "fails:I fail"
+ @t2 = SpecTag.new "unstable:I'm unstable"
+ end
+
+ it "creates a MatchFilter for all tags" do
+ MSpec.should_receive(:read_tags).and_return([@t1, @t2])
+ MatchFilter.should_receive(:new).with(nil, "I fail", "I'm unstable")
+ TagPurgeAction.new.load
+ end
+end
+
+describe TagPurgeAction, "#after" do
+ before :each do
+ @state = double("ExampleState")
+ @state.stub(:description).and_return("str")
+
+ @action = TagPurgeAction.new
+ end
+
+ it "does not save the description if the filter does not match" do
+ @action.should_receive(:===).with("str").and_return(false)
+ @action.after @state
+ @action.matching.should == []
+ end
+
+ it "saves the description if the filter matches" do
+ @action.should_receive(:===).with("str").and_return(true)
+ @action.after @state
+ @action.matching.should == ["str"]
+ end
+end
+
+describe TagPurgeAction, "#unload" do
+ before :each do
+ @stdout = $stdout
+ $stdout = IOStub.new
+
+ @t1 = SpecTag.new "fails:I fail"
+ @t2 = SpecTag.new "unstable:I'm unstable"
+ @t3 = SpecTag.new "fails:I'm unstable"
+
+ MSpec.stub(:read_tags).and_return([@t1, @t2, @t3])
+ MSpec.stub(:write_tags)
+
+ @state = double("ExampleState")
+ @state.stub(:description).and_return("I'm unstable")
+
+ @action = TagPurgeAction.new
+ @action.load
+ @action.after @state
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it "does not rewrite any tags if there were no tags for the specs" do
+ MSpec.should_receive(:read_tags).and_return([])
+ MSpec.should_receive(:delete_tags)
+ MSpec.should_not_receive(:write_tags)
+
+ @action.load
+ @action.after @state
+ @action.unload
+
+ $stdout.should == ""
+ end
+
+ it "rewrites tags that were matched" do
+ MSpec.should_receive(:write_tags).with([@t2, @t3])
+ @action.unload
+ end
+
+ it "prints tags that were not matched" do
+ @action.unload
+ $stdout.should == "I fail\n"
+ end
+end
+
+describe TagPurgeAction, "#unload" do
+ before :each do
+ @stdout = $stdout
+ $stdout = IOStub.new
+
+ MSpec.stub(:read_tags).and_return([])
+
+ @state = double("ExampleState")
+ @state.stub(:description).and_return("I'm unstable")
+
+ @action = TagPurgeAction.new
+ @action.load
+ @action.after @state
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it "deletes the tag file if no tags were found" do
+ MSpec.should_not_receive(:write_tags)
+ MSpec.should_receive(:delete_tags)
+ @action.unload
+ $stdout.should == ""
+ end
+end
+
+describe TagPurgeAction, "#register" do
+ before :each do
+ MSpec.stub(:register)
+ @action = TagPurgeAction.new
+ end
+
+ it "registers itself with MSpec for the :unload event" do
+ MSpec.should_receive(:register).with(:unload, @action)
+ @action.register
+ end
+end
+
+describe TagPurgeAction, "#unregister" do
+ before :each do
+ MSpec.stub(:unregister)
+ @action = TagPurgeAction.new
+ end
+
+ it "unregisters itself with MSpec for the :unload event" do
+ MSpec.should_receive(:unregister).with(:unload, @action)
+ @action.unregister
+ end
+end
diff --git a/spec/mspec/spec/runner/actions/tally_spec.rb b/spec/mspec/spec/runner/actions/tally_spec.rb
new file mode 100644
index 0000000000..be4635ffeb
--- /dev/null
+++ b/spec/mspec/spec/runner/actions/tally_spec.rb
@@ -0,0 +1,352 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/runner/actions/tally'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+
+describe Tally, "#files!" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "increments the count returned by #files" do
+ @tally.files! 3
+ @tally.files.should == 3
+ @tally.files!
+ @tally.files.should == 4
+ end
+end
+
+describe Tally, "#examples!" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "increments the count returned by #examples" do
+ @tally.examples! 2
+ @tally.examples.should == 2
+ @tally.examples! 2
+ @tally.examples.should == 4
+ end
+end
+
+describe Tally, "#expectations!" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "increments the count returned by #expectations" do
+ @tally.expectations!
+ @tally.expectations.should == 1
+ @tally.expectations! 3
+ @tally.expectations.should == 4
+ end
+end
+
+describe Tally, "#failures!" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "increments the count returned by #failures" do
+ @tally.failures! 1
+ @tally.failures.should == 1
+ @tally.failures!
+ @tally.failures.should == 2
+ end
+end
+
+describe Tally, "#errors!" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "increments the count returned by #errors" do
+ @tally.errors!
+ @tally.errors.should == 1
+ @tally.errors! 2
+ @tally.errors.should == 3
+ end
+end
+
+describe Tally, "#guards!" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "increments the count returned by #guards" do
+ @tally.guards!
+ @tally.guards.should == 1
+ @tally.guards! 2
+ @tally.guards.should == 3
+ end
+end
+
+describe Tally, "#file" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "returns a formatted string of the number of #files" do
+ @tally.file.should == "0 files"
+ @tally.files!
+ @tally.file.should == "1 file"
+ @tally.files!
+ @tally.file.should == "2 files"
+ end
+end
+
+describe Tally, "#example" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "returns a formatted string of the number of #examples" do
+ @tally.example.should == "0 examples"
+ @tally.examples!
+ @tally.example.should == "1 example"
+ @tally.examples!
+ @tally.example.should == "2 examples"
+ end
+end
+
+describe Tally, "#expectation" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "returns a formatted string of the number of #expectations" do
+ @tally.expectation.should == "0 expectations"
+ @tally.expectations!
+ @tally.expectation.should == "1 expectation"
+ @tally.expectations!
+ @tally.expectation.should == "2 expectations"
+ end
+end
+
+describe Tally, "#failure" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "returns a formatted string of the number of #failures" do
+ @tally.failure.should == "0 failures"
+ @tally.failures!
+ @tally.failure.should == "1 failure"
+ @tally.failures!
+ @tally.failure.should == "2 failures"
+ end
+end
+
+describe Tally, "#error" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "returns a formatted string of the number of #errors" do
+ @tally.error.should == "0 errors"
+ @tally.errors!
+ @tally.error.should == "1 error"
+ @tally.errors!
+ @tally.error.should == "2 errors"
+ end
+end
+
+describe Tally, "#guard" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "returns a formatted string of the number of #guards" do
+ @tally.guard.should == "0 guards"
+ @tally.guards!
+ @tally.guard.should == "1 guard"
+ @tally.guards!
+ @tally.guard.should == "2 guards"
+ end
+end
+
+describe Tally, "#format" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ after :each do
+ MSpec.clear_modes
+ end
+
+ it "returns a formatted string of counts" do
+ @tally.files!
+ @tally.examples! 2
+ @tally.expectations! 4
+ @tally.errors!
+ @tally.tagged!
+ @tally.format.should == "1 file, 2 examples, 4 expectations, 0 failures, 1 error, 1 tagged"
+ end
+
+ it "includes guards if MSpec is in verify mode" do
+ MSpec.register_mode :verify
+ @tally.files!
+ @tally.examples! 2
+ @tally.expectations! 4
+ @tally.errors!
+ @tally.tagged!
+ @tally.guards!
+ @tally.format.should ==
+ "1 file, 2 examples, 4 expectations, 0 failures, 1 error, 1 tagged, 1 guard"
+ end
+
+ it "includes guards if MSpec is in report mode" do
+ MSpec.register_mode :report
+ @tally.files!
+ @tally.examples! 2
+ @tally.expectations! 4
+ @tally.errors!
+ @tally.tagged!
+ @tally.guards! 2
+ @tally.format.should ==
+ "1 file, 2 examples, 4 expectations, 0 failures, 1 error, 1 tagged, 2 guards"
+ end
+
+ it "includes guards if MSpec is in report_on mode" do
+ MSpec.register_mode :report_on
+ @tally.files!
+ @tally.examples! 2
+ @tally.expectations! 4
+ @tally.errors!
+ @tally.guards! 2
+ @tally.format.should ==
+ "1 file, 2 examples, 4 expectations, 0 failures, 1 error, 0 tagged, 2 guards"
+ end
+end
+
+describe TallyAction, "#counter" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "returns the Tally object" do
+ @tally.counter.should be_kind_of(Tally)
+ end
+end
+
+describe TallyAction, "#load" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "increments the count returned by Tally#files" do
+ @tally.load
+ @tally.counter.files.should == 1
+ end
+end
+
+describe TallyAction, "#expectation" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "increments the count returned by Tally#expectations" do
+ @tally.expectation @state
+ @tally.counter.expectations.should == 1
+ end
+end
+
+describe TallyAction, "#example" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "increments counts returned by Tally#examples" do
+ @tally.example @state, nil
+ @tally.counter.examples.should == 1
+ @tally.counter.expectations.should == 0
+ @tally.counter.failures.should == 0
+ @tally.counter.errors.should == 0
+ end
+end
+
+describe TallyAction, "#exception" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "increments counts returned by Tally#failures" do
+ exc = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("Failed!")
+ @tally.exception exc
+ @tally.counter.examples.should == 0
+ @tally.counter.expectations.should == 0
+ @tally.counter.failures.should == 1
+ @tally.counter.errors.should == 0
+ end
+end
+
+describe TallyAction, "#exception" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "increments counts returned by Tally#errors" do
+ exc = ExceptionState.new nil, nil, Exception.new("Error!")
+ @tally.exception exc
+ @tally.counter.examples.should == 0
+ @tally.counter.expectations.should == 0
+ @tally.counter.failures.should == 0
+ @tally.counter.errors.should == 1
+ end
+end
+
+describe TallyAction, "#format" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "returns a readable string of counts" do
+ @tally.load
+ @tally.example @state, nil
+ @tally.expectation @state
+ @tally.expectation @state
+ exc = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("Failed!")
+ @tally.exception exc
+ @tally.format.should == "1 file, 1 example, 2 expectations, 1 failure, 0 errors, 0 tagged"
+ end
+end
+
+describe TallyAction, "#register" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "registers itself with MSpec for appropriate actions" do
+ MSpec.should_receive(:register).with(:load, @tally)
+ MSpec.should_receive(:register).with(:exception, @tally)
+ MSpec.should_receive(:register).with(:example, @tally)
+ MSpec.should_receive(:register).with(:tagged, @tally)
+ MSpec.should_receive(:register).with(:expectation, @tally)
+ @tally.register
+ end
+end
+
+describe TallyAction, "#unregister" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "unregisters itself with MSpec for appropriate actions" do
+ MSpec.should_receive(:unregister).with(:load, @tally)
+ MSpec.should_receive(:unregister).with(:exception, @tally)
+ MSpec.should_receive(:unregister).with(:example, @tally)
+ MSpec.should_receive(:unregister).with(:tagged, @tally)
+ MSpec.should_receive(:unregister).with(:expectation, @tally)
+ @tally.unregister
+ end
+end
diff --git a/spec/mspec/spec/runner/actions/timer_spec.rb b/spec/mspec/spec/runner/actions/timer_spec.rb
new file mode 100644
index 0000000000..417367d5a2
--- /dev/null
+++ b/spec/mspec/spec/runner/actions/timer_spec.rb
@@ -0,0 +1,44 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/actions/timer'
+require 'mspec/runner/mspec'
+require 'time'
+
+describe TimerAction do
+ before :each do
+ @timer = TimerAction.new
+ @start_time = Time.utc(2009, 3, 30, 14, 5, 19)
+ @stop_time = Time.utc(2009, 3, 30, 14, 5, 52)
+ end
+
+ it "responds to #start by recording the current time" do
+ Time.should_receive(:now)
+ @timer.start
+ end
+
+ it "responds to #finish by recording the current time" do
+ Time.should_receive(:now)
+ @timer.finish
+ end
+
+ it "responds to #elapsed by returning the difference between stop and start" do
+ Time.stub(:now).and_return(@start_time)
+ @timer.start
+ Time.stub(:now).and_return(@stop_time)
+ @timer.finish
+ @timer.elapsed.should == 33
+ end
+
+ it "responds to #format by returning a readable string of elapsed time" do
+ Time.stub(:now).and_return(@start_time)
+ @timer.start
+ Time.stub(:now).and_return(@stop_time)
+ @timer.finish
+ @timer.format.should == "Finished in 33.000000 seconds"
+ end
+
+ it "responds to #register by registering itself with MSpec for appropriate actions" do
+ MSpec.should_receive(:register).with(:start, @timer)
+ MSpec.should_receive(:register).with(:finish, @timer)
+ @timer.register
+ end
+end
diff --git a/spec/mspec/spec/runner/context_spec.rb b/spec/mspec/spec/runner/context_spec.rb
new file mode 100644
index 0000000000..d9c20aa0cf
--- /dev/null
+++ b/spec/mspec/spec/runner/context_spec.rb
@@ -0,0 +1,1041 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers/base'
+require 'mspec/runner/mspec'
+require 'mspec/mocks/mock'
+require 'mspec/runner/context'
+require 'mspec/runner/example'
+
+describe ContextState, "#describe" do
+ before :each do
+ @state = ContextState.new "C#m"
+ @proc = proc { ScratchPad.record :a }
+ ScratchPad.clear
+ end
+
+ it "evaluates the passed block" do
+ @state.describe(&@proc)
+ ScratchPad.recorded.should == :a
+ end
+
+ it "evaluates the passed block via #protect" do
+ @state.should_receive(:protect).with("C#m", @proc, false)
+ @state.describe(&@proc)
+ end
+
+ it "registers #parent as the current MSpec ContextState" do
+ parent = ContextState.new ""
+ @state.parent = parent
+ MSpec.should_receive(:register_current).with(parent)
+ @state.describe { }
+ end
+
+ it "registers self with MSpec when #shared? is true" do
+ state = ContextState.new "something shared", :shared => true
+ MSpec.should_receive(:register_shared).with(state)
+ state.describe { }
+ end
+end
+
+describe ContextState, "#shared?" do
+ it "returns false when the ContextState is not shared" do
+ ContextState.new("").shared?.should be_false
+ end
+
+ it "returns true when the ContextState is shared" do
+ ContextState.new("", {:shared => true}).shared?.should be_true
+ end
+end
+
+describe ContextState, "#to_s" do
+ it "returns a description string for self when passed a Module" do
+ ContextState.new(Object).to_s.should == "Object"
+ end
+
+ it "returns a description string for self when passed a String" do
+ ContextState.new("SomeClass").to_s.should == "SomeClass"
+ end
+
+ it "returns a description string for self when passed a Module, String" do
+ ContextState.new(Object, "when empty").to_s.should == "Object when empty"
+ end
+
+ it "returns a description string for self when passed a Module and String beginning with '#'" do
+ ContextState.new(Object, "#to_s").to_s.should == "Object#to_s"
+ end
+
+ it "returns a description string for self when passed a Module and String beginning with '.'" do
+ ContextState.new(Object, ".to_s").to_s.should == "Object.to_s"
+ end
+
+ it "returns a description string for self when passed a Module and String beginning with '::'" do
+ ContextState.new(Object, "::to_s").to_s.should == "Object::to_s"
+ end
+end
+
+describe ContextState, "#description" do
+ before :each do
+ @state = ContextState.new "when empty"
+ @parent = ContextState.new "Toplevel"
+ end
+
+ it "returns a composite description string from self and all parents" do
+ @parent.description.should == "Toplevel"
+ @state.description.should == "when empty"
+ @state.parent = @parent
+ @state.description.should == "Toplevel when empty"
+ end
+end
+
+describe ContextState, "#it" do
+ before :each do
+ @state = ContextState.new ""
+ @proc = lambda {|*| }
+
+ @ex = ExampleState.new("", "", &@proc)
+ end
+
+ it "creates an ExampleState instance for the block" do
+ ExampleState.should_receive(:new).with(@state, "it", @proc).and_return(@ex)
+ @state.describe(&@proc)
+ @state.it("it", &@proc)
+ end
+
+ it "calls registered :add actions" do
+ ExampleState.should_receive(:new).with(@state, "it", @proc).and_return(@ex)
+
+ add_action = double("add")
+ add_action.should_receive(:add).with(@ex).and_return { ScratchPad.record :add }
+ MSpec.register :add, add_action
+
+ @state.it("it", &@proc)
+ ScratchPad.recorded.should == :add
+ MSpec.unregister :add, add_action
+ end
+end
+
+describe ContextState, "#examples" do
+ before :each do
+ @state = ContextState.new ""
+ end
+
+ it "returns a list of all examples in this ContextState" do
+ @state.it("first") { }
+ @state.it("second") { }
+ @state.examples.size.should == 2
+ end
+end
+
+describe ContextState, "#before" do
+ before :each do
+ @state = ContextState.new ""
+ @proc = lambda {|*| }
+ end
+
+ it "records the block for :each" do
+ @state.before(:each, &@proc)
+ @state.before(:each).should == [@proc]
+ end
+
+ it "records the block for :all" do
+ @state.before(:all, &@proc)
+ @state.before(:all).should == [@proc]
+ end
+end
+
+describe ContextState, "#after" do
+ before :each do
+ @state = ContextState.new ""
+ @proc = lambda {|*| }
+ end
+
+ it "records the block for :each" do
+ @state.after(:each, &@proc)
+ @state.after(:each).should == [@proc]
+ end
+
+ it "records the block for :all" do
+ @state.after(:all, &@proc)
+ @state.after(:all).should == [@proc]
+ end
+end
+
+describe ContextState, "#pre" do
+ before :each do
+ @a = lambda {|*| }
+ @b = lambda {|*| }
+ @c = lambda {|*| }
+
+ parent = ContextState.new ""
+ parent.before(:each, &@c)
+ parent.before(:all, &@c)
+
+ @state = ContextState.new ""
+ @state.parent = parent
+ end
+
+ it "returns before(:each) actions in the order they were defined" do
+ @state.before(:each, &@a)
+ @state.before(:each, &@b)
+ @state.pre(:each).should == [@c, @a, @b]
+ end
+
+ it "returns before(:all) actions in the order they were defined" do
+ @state.before(:all, &@a)
+ @state.before(:all, &@b)
+ @state.pre(:all).should == [@c, @a, @b]
+ end
+end
+
+describe ContextState, "#post" do
+ before :each do
+ @a = lambda {|*| }
+ @b = lambda {|*| }
+ @c = lambda {|*| }
+
+ parent = ContextState.new ""
+ parent.after(:each, &@c)
+ parent.after(:all, &@c)
+
+ @state = ContextState.new ""
+ @state.parent = parent
+ end
+
+ it "returns after(:each) actions in the reverse order they were defined" do
+ @state.after(:each, &@a)
+ @state.after(:each, &@b)
+ @state.post(:each).should == [@b, @a, @c]
+ end
+
+ it "returns after(:all) actions in the reverse order they were defined" do
+ @state.after(:all, &@a)
+ @state.after(:all, &@b)
+ @state.post(:all).should == [@b, @a, @c]
+ end
+end
+
+describe ContextState, "#protect" do
+ before :each do
+ ScratchPad.record []
+ @a = lambda {|*| ScratchPad << :a }
+ @b = lambda {|*| ScratchPad << :b }
+ @c = lambda {|*| raise Exception, "Fail!" }
+ end
+
+ it "returns true and does execute any blocks if check and MSpec.mode?(:pretend) are true" do
+ MSpec.should_receive(:mode?).with(:pretend).and_return(true)
+ ContextState.new("").protect("message", [@a, @b]).should be_true
+ ScratchPad.recorded.should == []
+ end
+
+ it "executes the blocks if MSpec.mode?(:pretend) is false" do
+ MSpec.should_receive(:mode?).with(:pretend).and_return(false)
+ ContextState.new("").protect("message", [@a, @b])
+ ScratchPad.recorded.should == [:a, :b]
+ end
+
+ it "executes the blocks if check is false" do
+ ContextState.new("").protect("message", [@a, @b], false)
+ ScratchPad.recorded.should == [:a, :b]
+ end
+
+ it "returns true if none of the blocks raise an exception" do
+ ContextState.new("").protect("message", [@a, @b]).should be_true
+ end
+
+ it "returns false if any of the blocks raise an exception" do
+ ContextState.new("").protect("message", [@a, @c, @b]).should be_false
+ end
+end
+
+describe ContextState, "#parent=" do
+ before :each do
+ @state = ContextState.new ""
+ @parent = double("describe")
+ @parent.stub(:parent).and_return(nil)
+ @parent.stub(:child)
+ end
+
+ it "does not set self as a child of parent if shared" do
+ @parent.should_not_receive(:child)
+ state = ContextState.new "", :shared => true
+ state.parent = @parent
+ end
+
+ it "does not set parents if shared" do
+ state = ContextState.new "", :shared => true
+ state.parent = @parent
+ state.parents.should == [state]
+ end
+
+ it "sets self as a child of parent" do
+ @parent.should_receive(:child).with(@state)
+ @state.parent = @parent
+ end
+
+ it "creates the list of parents" do
+ @state.parent = @parent
+ @state.parents.should == [@parent, @state]
+ end
+end
+
+describe ContextState, "#parent" do
+ before :each do
+ @state = ContextState.new ""
+ @parent = double("describe")
+ @parent.stub(:parent).and_return(nil)
+ @parent.stub(:child)
+ end
+
+ it "returns nil if parent has not been set" do
+ @state.parent.should be_nil
+ end
+
+ it "returns the parent" do
+ @state.parent = @parent
+ @state.parent.should == @parent
+ end
+end
+
+describe ContextState, "#parents" do
+ before :each do
+ @first = ContextState.new ""
+ @second = ContextState.new ""
+ @parent = double("describe")
+ @parent.stub(:parent).and_return(nil)
+ @parent.stub(:child)
+ end
+
+ it "returns a list of all enclosing ContextState instances" do
+ @first.parent = @parent
+ @second.parent = @first
+ @second.parents.should == [@parent, @first, @second]
+ end
+end
+
+describe ContextState, "#child" do
+ before :each do
+ @first = ContextState.new ""
+ @second = ContextState.new ""
+ @parent = double("describe")
+ @parent.stub(:parent).and_return(nil)
+ @parent.stub(:child)
+ end
+
+ it "adds the ContextState to the list of contained ContextStates" do
+ @first.child @second
+ @first.children.should == [@second]
+ end
+end
+
+describe ContextState, "#children" do
+ before :each do
+ @parent = ContextState.new ""
+ @first = ContextState.new ""
+ @second = ContextState.new ""
+ end
+
+ it "returns the list of directly contained ContextStates" do
+ @first.parent = @parent
+ @second.parent = @first
+ @parent.children.should == [@first]
+ @first.children.should == [@second]
+ end
+end
+
+describe ContextState, "#state" do
+ before :each do
+ MSpec.store :before, []
+ MSpec.store :after, []
+
+ @state = ContextState.new ""
+ end
+
+ it "returns nil if no spec is being executed" do
+ @state.state.should == nil
+ end
+
+ it "returns a ExampleState instance if an example is being executed" do
+ ScratchPad.record @state
+ @state.describe { }
+ @state.it("") { ScratchPad.record ScratchPad.recorded.state }
+ @state.process
+ @state.state.should == nil
+ ScratchPad.recorded.should be_kind_of(ExampleState)
+ end
+end
+
+describe ContextState, "#process" do
+ before :each do
+ MSpec.store :before, []
+ MSpec.store :after, []
+ MSpec.stub(:register_current)
+
+ @state = ContextState.new ""
+ @state.describe { }
+
+ @a = lambda {|*| ScratchPad << :a }
+ @b = lambda {|*| ScratchPad << :b }
+ ScratchPad.record []
+ end
+
+ it "calls each before(:all) block" do
+ @state.before(:all, &@a)
+ @state.before(:all, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == [:a, :b]
+ end
+
+ it "calls each after(:all) block" do
+ @state.after(:all, &@a)
+ @state.after(:all, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == [:b, :a]
+ end
+
+ it "calls each it block" do
+ @state.it("one", &@a)
+ @state.it("two", &@b)
+ @state.process
+ ScratchPad.recorded.should == [:a, :b]
+ end
+
+ it "does not call the #it block if #filtered? returns true" do
+ @state.it("one", &@a)
+ @state.it("two", &@b)
+ @state.examples.first.stub(:filtered?).and_return(true)
+ @state.process
+ ScratchPad.recorded.should == [:b]
+ end
+
+ it "calls each before(:each) block" do
+ @state.before(:each, &@a)
+ @state.before(:each, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == [:a, :b]
+ end
+
+ it "calls each after(:each) block" do
+ @state.after(:each, &@a)
+ @state.after(:each, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == [:b, :a]
+ end
+
+ it "calls Mock.cleanup for each it block" do
+ @state.it("") { }
+ @state.it("") { }
+ Mock.should_receive(:cleanup).twice
+ @state.process
+ end
+
+ it "calls Mock.verify_count for each it block" do
+ @state.it("") { }
+ @state.it("") { }
+ Mock.should_receive(:verify_count).twice
+ @state.process
+ end
+
+ it "calls the describe block" do
+ ScratchPad.record []
+ @state.describe { ScratchPad << :a }
+ @state.process
+ ScratchPad.recorded.should == [:a]
+ end
+
+ it "creates a new ExampleState instance for each example" do
+ ScratchPad.record @state
+ @state.describe { }
+ @state.it("it") { ScratchPad.record ScratchPad.recorded.state }
+ @state.process
+ ScratchPad.recorded.should be_kind_of(ExampleState)
+ end
+
+ it "clears the expectations flag before evaluating the #it block" do
+ MSpec.clear_expectations
+ MSpec.should_receive(:clear_expectations)
+ @state.it("it") { ScratchPad.record MSpec.expectation? }
+ @state.process
+ ScratchPad.recorded.should be_false
+ end
+
+ it "shuffles the spec list if MSpec.randomize? is true" do
+ MSpec.randomize
+ MSpec.should_receive(:shuffle)
+ @state.it("") { }
+ @state.process
+ MSpec.randomize false
+ end
+
+ it "sets the current MSpec ContextState" do
+ MSpec.should_receive(:register_current).with(@state)
+ @state.process
+ end
+
+ it "resets the current MSpec ContextState to nil when there are examples" do
+ MSpec.should_receive(:register_current).with(nil)
+ @state.it("") { }
+ @state.process
+ end
+
+ it "resets the current MSpec ContextState to nil when there are no examples" do
+ MSpec.should_receive(:register_current).with(nil)
+ @state.process
+ end
+
+ it "call #process on children when there are examples" do
+ child = ContextState.new ""
+ child.should_receive(:process)
+ @state.child child
+ @state.it("") { }
+ @state.process
+ end
+
+ it "call #process on children when there are no examples" do
+ child = ContextState.new ""
+ child.should_receive(:process)
+ @state.child child
+ @state.process
+ end
+end
+
+describe ContextState, "#process" do
+ before :each do
+ MSpec.store :exception, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+
+ action = double("action")
+ def action.exception(exc)
+ ScratchPad.record :exception if exc.exception.is_a? SpecExpectationNotFoundError
+ end
+ MSpec.register :exception, action
+
+ MSpec.clear_expectations
+ ScratchPad.clear
+ end
+
+ after :each do
+ MSpec.store :exception, nil
+ end
+
+ it "raises an SpecExpectationNotFoundError if an #it block does not contain an expectation" do
+ @state.it("it") { }
+ @state.process
+ ScratchPad.recorded.should == :exception
+ end
+
+ it "does not raise an SpecExpectationNotFoundError if an #it block does contain an expectation" do
+ @state.it("it") { MSpec.expectation }
+ @state.process
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "does not raise an SpecExpectationNotFoundError if the #it block causes a failure" do
+ @state.it("it") { raise Exception, "Failed!" }
+ @state.process
+ ScratchPad.recorded.should be_nil
+ end
+end
+
+describe ContextState, "#process" do
+ before :each do
+ MSpec.store :example, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+
+ example = double("example")
+ def example.example(state, spec)
+ ScratchPad << state << spec
+ end
+ MSpec.register :example, example
+
+ ScratchPad.record []
+ end
+
+ after :each do
+ MSpec.store :example, nil
+ end
+
+ it "calls registered :example actions with the current ExampleState and block" do
+ @state.it("") { MSpec.expectation }
+ @state.process
+
+ ScratchPad.recorded.first.should be_kind_of(ExampleState)
+ ScratchPad.recorded.last.should be_kind_of(Proc)
+ end
+
+ it "does not call registered example actions if the example has no block" do
+ @state.it("empty example")
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+end
+
+describe ContextState, "#process" do
+ before :each do
+ MSpec.store :before, []
+ MSpec.store :after, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+ @state.it("") { MSpec.expectation }
+ end
+
+ after :each do
+ MSpec.store :before, nil
+ MSpec.store :after, nil
+ end
+
+ it "calls registered :before actions with the current ExampleState instance" do
+ before = double("before")
+ before.should_receive(:before).and_return {
+ ScratchPad.record :before
+ @spec_state = @state.state
+ }
+ MSpec.register :before, before
+ @state.process
+ ScratchPad.recorded.should == :before
+ @spec_state.should be_kind_of(ExampleState)
+ end
+
+ it "calls registered :after actions with the current ExampleState instance" do
+ after = double("after")
+ after.should_receive(:after).and_return {
+ ScratchPad.record :after
+ @spec_state = @state.state
+ }
+ MSpec.register :after, after
+ @state.process
+ ScratchPad.recorded.should == :after
+ @spec_state.should be_kind_of(ExampleState)
+ end
+end
+
+describe ContextState, "#process" do
+ before :each do
+ MSpec.store :enter, []
+ MSpec.store :leave, []
+
+ @state = ContextState.new "C#m"
+ @state.describe { }
+ @state.it("") { MSpec.expectation }
+ end
+
+ after :each do
+ MSpec.store :enter, nil
+ MSpec.store :leave, nil
+ end
+
+ it "calls registered :enter actions with the current #describe string" do
+ enter = double("enter")
+ enter.should_receive(:enter).with("C#m").and_return { ScratchPad.record :enter }
+ MSpec.register :enter, enter
+ @state.process
+ ScratchPad.recorded.should == :enter
+ end
+
+ it "calls registered :leave actions" do
+ leave = double("leave")
+ leave.should_receive(:leave).and_return { ScratchPad.record :leave }
+ MSpec.register :leave, leave
+ @state.process
+ ScratchPad.recorded.should == :leave
+ end
+end
+
+describe ContextState, "#process when an exception is raised in before(:all)" do
+ before :each do
+ MSpec.store :before, []
+ MSpec.store :after, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+
+ @a = lambda {|*| ScratchPad << :a }
+ @b = lambda {|*| ScratchPad << :b }
+ ScratchPad.record []
+
+ @state.before(:all) { raise Exception, "Fail!" }
+ end
+
+ after :each do
+ MSpec.store :before, nil
+ MSpec.store :after, nil
+ end
+
+ it "does not call before(:each)" do
+ @state.before(:each, &@a)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call the it block" do
+ @state.it("one", &@a)
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call after(:each)" do
+ @state.after(:each, &@a)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call after(:each)" do
+ @state.after(:all, &@a)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call Mock.verify_count" do
+ @state.it("") { }
+ Mock.should_not_receive(:verify_count)
+ @state.process
+ end
+
+ it "calls Mock.cleanup" do
+ @state.it("") { }
+ Mock.should_receive(:cleanup)
+ @state.process
+ end
+end
+
+describe ContextState, "#process when an exception is raised in before(:each)" do
+ before :each do
+ MSpec.store :before, []
+ MSpec.store :after, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+
+ @a = lambda {|*| ScratchPad << :a }
+ @b = lambda {|*| ScratchPad << :b }
+ ScratchPad.record []
+
+ @state.before(:each) { raise Exception, "Fail!" }
+ end
+
+ after :each do
+ MSpec.store :before, nil
+ MSpec.store :after, nil
+ end
+
+ it "does not call the it block" do
+ @state.it("one", &@a)
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does call after(:each)" do
+ @state.after(:each, &@a)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == [:a]
+ end
+
+ it "does not call Mock.verify_count" do
+ @state.it("") { }
+ Mock.should_not_receive(:verify_count)
+ @state.process
+ end
+end
+
+describe ContextState, "#process in pretend mode" do
+ before :all do
+ MSpec.register_mode :pretend
+ end
+
+ after :all do
+ MSpec.clear_modes
+ end
+
+ before :each do
+ ScratchPad.clear
+ MSpec.store :before, []
+ MSpec.store :after, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+ @state.it("") { }
+ end
+
+ after :each do
+ MSpec.store :before, nil
+ MSpec.store :after, nil
+ end
+
+ it "calls registered :before actions with the current ExampleState instance" do
+ before = double("before")
+ before.should_receive(:before).and_return {
+ ScratchPad.record :before
+ @spec_state = @state.state
+ }
+ MSpec.register :before, before
+ @state.process
+ ScratchPad.recorded.should == :before
+ @spec_state.should be_kind_of(ExampleState)
+ end
+
+ it "calls registered :after actions with the current ExampleState instance" do
+ after = double("after")
+ after.should_receive(:after).and_return {
+ ScratchPad.record :after
+ @spec_state = @state.state
+ }
+ MSpec.register :after, after
+ @state.process
+ ScratchPad.recorded.should == :after
+ @spec_state.should be_kind_of(ExampleState)
+ end
+end
+
+describe ContextState, "#process in pretend mode" do
+ before :all do
+ MSpec.register_mode :pretend
+ end
+
+ after :all do
+ MSpec.clear_modes
+ end
+
+ before :each do
+ MSpec.store :before, []
+ MSpec.store :after, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+
+ @a = lambda {|*| ScratchPad << :a }
+ @b = lambda {|*| ScratchPad << :b }
+ ScratchPad.record []
+ end
+
+ it "calls the describe block" do
+ ScratchPad.record []
+ @state.describe { ScratchPad << :a }
+ @state.process
+ ScratchPad.recorded.should == [:a]
+ end
+
+ it "does not call any before(:all) block" do
+ @state.before(:all, &@a)
+ @state.before(:all, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call any after(:all) block" do
+ @state.after(:all, &@a)
+ @state.after(:all, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call any it block" do
+ @state.it("one", &@a)
+ @state.it("two", &@b)
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call any before(:each) block" do
+ @state.before(:each, &@a)
+ @state.before(:each, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call any after(:each) block" do
+ @state.after(:each, &@a)
+ @state.after(:each, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call Mock.cleanup" do
+ @state.it("") { }
+ @state.it("") { }
+ Mock.should_not_receive(:cleanup)
+ @state.process
+ end
+end
+
+describe ContextState, "#process in pretend mode" do
+ before :all do
+ MSpec.register_mode :pretend
+ end
+
+ after :all do
+ MSpec.clear_modes
+ end
+
+ before :each do
+ MSpec.store :enter, []
+ MSpec.store :leave, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+ @state.it("") { }
+ end
+
+ after :each do
+ MSpec.store :enter, nil
+ MSpec.store :leave, nil
+ end
+
+ it "calls registered :enter actions with the current #describe string" do
+ enter = double("enter")
+ enter.should_receive(:enter).and_return { ScratchPad.record :enter }
+ MSpec.register :enter, enter
+ @state.process
+ ScratchPad.recorded.should == :enter
+ end
+
+ it "calls registered :leave actions" do
+ leave = double("leave")
+ leave.should_receive(:leave).and_return { ScratchPad.record :leave }
+ MSpec.register :leave, leave
+ @state.process
+ ScratchPad.recorded.should == :leave
+ end
+end
+
+describe ContextState, "#it_should_behave_like" do
+ before :each do
+ @shared_desc = :shared_context
+ @shared = ContextState.new(@shared_desc, :shared => true)
+ MSpec.stub(:retrieve_shared).and_return(@shared)
+
+ @state = ContextState.new "Top level"
+ @a = lambda {|*| }
+ @b = lambda {|*| }
+ end
+
+ it "raises an Exception if unable to find the shared ContextState" do
+ MSpec.should_receive(:retrieve_shared).and_return(nil)
+ lambda { @state.it_should_behave_like "this" }.should raise_error(Exception)
+ end
+
+ describe "for nested ContextState instances" do
+ before :each do
+ @nested = ContextState.new "nested context"
+ @nested.parents.unshift @shared
+
+ @shared.children << @nested
+
+ @nested_dup = @nested.dup
+ @nested.stub(:dup).and_return(@nested_dup)
+ end
+
+ it "duplicates the nested ContextState" do
+ @state.it_should_behave_like @shared_desc
+ @state.children.first.should equal(@nested_dup)
+ end
+
+ it "sets the parent of the nested ContextState to the containing ContextState" do
+ @state.it_should_behave_like @shared_desc
+ @nested_dup.parent.should equal(@state)
+ end
+
+ it "sets the context for nested examples to the nested ContextState's dup" do
+ @shared.it "an example", &@a
+ @shared.it "another example", &@b
+ @state.it_should_behave_like @shared_desc
+ @nested_dup.examples.each { |x| x.context.should equal(@nested_dup) }
+ end
+
+ it "omits the shored ContextState's description" do
+ @nested.it "an example", &@a
+ @nested.it "another example", &@b
+ @state.it_should_behave_like @shared_desc
+
+ @nested_dup.description.should == "Top level nested context"
+ @nested_dup.examples.first.description.should == "Top level nested context an example"
+ @nested_dup.examples.last.description.should == "Top level nested context another example"
+ end
+ end
+
+ it "adds duped examples from the shared ContextState" do
+ @shared.it "some method", &@a
+ ex_dup = @shared.examples.first.dup
+ @shared.examples.first.stub(:dup).and_return(ex_dup)
+
+ @state.it_should_behave_like @shared_desc
+ @state.examples.should == [ex_dup]
+ end
+
+ it "sets the context for examples to the containing ContextState" do
+ @shared.it "an example", &@a
+ @shared.it "another example", &@b
+ @state.it_should_behave_like @shared_desc
+ @state.examples.each { |x| x.context.should equal(@state) }
+ end
+
+ it "adds before(:all) blocks from the shared ContextState" do
+ @shared.before :all, &@a
+ @shared.before :all, &@b
+ @state.it_should_behave_like @shared_desc
+ @state.before(:all).should include(*@shared.before(:all))
+ end
+
+ it "adds before(:each) blocks from the shared ContextState" do
+ @shared.before :each, &@a
+ @shared.before :each, &@b
+ @state.it_should_behave_like @shared_desc
+ @state.before(:each).should include(*@shared.before(:each))
+ end
+
+ it "adds after(:each) blocks from the shared ContextState" do
+ @shared.after :each, &@a
+ @shared.after :each, &@b
+ @state.it_should_behave_like @shared_desc
+ @state.after(:each).should include(*@shared.after(:each))
+ end
+
+ it "adds after(:all) blocks from the shared ContextState" do
+ @shared.after :all, &@a
+ @shared.after :all, &@b
+ @state.it_should_behave_like @shared_desc
+ @state.after(:all).should include(*@shared.after(:all))
+ end
+end
+
+describe ContextState, "#filter_examples" do
+ before :each do
+ @state = ContextState.new ""
+ @state.it("one") { }
+ @state.it("two") { }
+ end
+
+ it "removes examples that are filtered" do
+ @state.examples.first.stub(:filtered?).and_return(true)
+ @state.examples.size.should == 2
+ @state.filter_examples
+ @state.examples.size.should == 1
+ end
+
+ it "returns true if there are remaining examples to evaluate" do
+ @state.examples.first.stub(:filtered?).and_return(true)
+ @state.filter_examples.should be_true
+ end
+
+ it "returns false if there are no remaining examples to evaluate" do
+ @state.examples.first.stub(:filtered?).and_return(true)
+ @state.examples.last.stub(:filtered?).and_return(true)
+ @state.filter_examples.should be_false
+ end
+end
diff --git a/spec/mspec/spec/runner/example_spec.rb b/spec/mspec/spec/runner/example_spec.rb
new file mode 100644
index 0000000000..b4391f802d
--- /dev/null
+++ b/spec/mspec/spec/runner/example_spec.rb
@@ -0,0 +1,117 @@
+require 'spec_helper'
+require 'mspec/matchers/base'
+require 'mspec/runner/mspec'
+require 'mspec/mocks/mock'
+require 'mspec/runner/example'
+
+describe ExampleState do
+ it "is initialized with the ContextState, #it string, and #it block" do
+ prc = lambda { }
+ context = ContextState.new ""
+ ExampleState.new(context, "does", prc).should be_kind_of(ExampleState)
+ end
+end
+
+describe ExampleState, "#describe" do
+ before :each do
+ @context = ContextState.new Object, "#to_s"
+ @state = ExampleState.new @context, "it"
+ end
+
+ it "returns the ContextState#description" do
+ @state.describe.should == @context.description
+ end
+end
+
+describe ExampleState, "#it" do
+ before :each do
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ end
+
+ it "returns the argument to the #it block" do
+ @state.it.should == "it"
+ end
+end
+
+describe ExampleState, "#context=" do
+ before :each do
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ @context = ContextState.new "New#context"
+ end
+
+ it "sets the containing ContextState" do
+ @state.context = @context
+ @state.context.should == @context
+ end
+
+ it "resets the description" do
+ @state.description.should == "describe it"
+ @state.context = @context
+ @state.description.should == "New#context it"
+ end
+end
+
+describe ExampleState, "#example" do
+ before :each do
+ @proc = lambda { }
+ @state = ExampleState.new ContextState.new("describe"), "it", @proc
+ end
+
+ it "returns the #it block" do
+ @state.example.should == @proc
+ end
+end
+
+describe ExampleState, "#filtered?" do
+ before :each do
+ MSpec.store :include, nil
+ MSpec.store :exclude, nil
+
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ @filter = double("filter")
+ end
+
+ after :each do
+ MSpec.store :include, nil
+ MSpec.store :exclude, nil
+ end
+
+ it "returns false if MSpec include filters list is empty" do
+ @state.filtered?.should == false
+ end
+
+ it "returns false if MSpec include filters match this spec" do
+ @filter.should_receive(:===).and_return(true)
+ MSpec.register :include, @filter
+ @state.filtered?.should == false
+ end
+
+ it "returns true if MSpec include filters do not match this spec" do
+ @filter.should_receive(:===).and_return(false)
+ MSpec.register :include, @filter
+ @state.filtered?.should == true
+ end
+
+ it "returns false if MSpec exclude filters list is empty" do
+ @state.filtered?.should == false
+ end
+
+ it "returns false if MSpec exclude filters do not match this spec" do
+ @filter.should_receive(:===).and_return(false)
+ MSpec.register :exclude, @filter
+ @state.filtered?.should == false
+ end
+
+ it "returns true if MSpec exclude filters match this spec" do
+ @filter.should_receive(:===).and_return(true)
+ MSpec.register :exclude, @filter
+ @state.filtered?.should == true
+ end
+
+ it "returns true if MSpec include and exclude filters match this spec" do
+ @filter.should_receive(:===).twice.and_return(true)
+ MSpec.register :include, @filter
+ MSpec.register :exclude, @filter
+ @state.filtered?.should == true
+ end
+end
diff --git a/spec/mspec/spec/runner/exception_spec.rb b/spec/mspec/spec/runner/exception_spec.rb
new file mode 100644
index 0000000000..309442435c
--- /dev/null
+++ b/spec/mspec/spec/runner/exception_spec.rb
@@ -0,0 +1,146 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/runner/example'
+require 'mspec/runner/exception'
+require 'mspec/utils/script'
+
+describe ExceptionState, "#initialize" do
+ it "takes a state, location (e.g. before :each), and exception" do
+ context = ContextState.new "Class#method"
+ state = ExampleState.new context, "does something"
+ exc = Exception.new "Fail!"
+ ExceptionState.new(state, "location", exc).should be_kind_of(ExceptionState)
+ end
+end
+
+describe ExceptionState, "#description" do
+ before :each do
+ context = ContextState.new "Class#method"
+ @state = ExampleState.new context, "does something"
+ end
+
+ it "returns the state description if state was not nil" do
+ exc = ExceptionState.new(@state, nil, nil)
+ exc.description.should == "Class#method does something"
+ end
+
+ it "returns the location if it is not nil and description is nil" do
+ exc = ExceptionState.new(nil, "location", nil)
+ exc.description.should == "An exception occurred during: location"
+ end
+
+ it "returns both description and location if neither are nil" do
+ exc = ExceptionState.new(@state, "location", nil)
+ exc.description.should == "An exception occurred during: location\nClass#method does something"
+ end
+end
+
+describe ExceptionState, "#describe" do
+ before :each do
+ context = ContextState.new "Class#method"
+ @state = ExampleState.new context, "does something"
+ end
+
+ it "returns the ExampleState#describe string if created with a non-nil state" do
+ ExceptionState.new(@state, nil, nil).describe.should == @state.describe
+ end
+
+ it "returns an empty string if created with a nil state" do
+ ExceptionState.new(nil, nil, nil).describe.should == ""
+ end
+end
+
+describe ExceptionState, "#it" do
+ before :each do
+ context = ContextState.new "Class#method"
+ @state = ExampleState.new context, "does something"
+ end
+
+ it "returns the ExampleState#it string if created with a non-nil state" do
+ ExceptionState.new(@state, nil, nil).it.should == @state.it
+ end
+
+ it "returns an empty string if created with a nil state" do
+ ExceptionState.new(nil, nil, nil).it.should == ""
+ end
+end
+
+describe ExceptionState, "#failure?" do
+ before :each do
+ @state = ExampleState.new ContextState.new("C#m"), "works"
+ end
+
+ it "returns true if the exception is an SpecExpectationNotMetError" do
+ exc = ExceptionState.new @state, "", SpecExpectationNotMetError.new("Fail!")
+ exc.failure?.should be_true
+ end
+
+ it "returns true if the exception is an SpecExpectationNotFoundError" do
+ exc = ExceptionState.new @state, "", SpecExpectationNotFoundError.new("Fail!")
+ exc.failure?.should be_true
+ end
+
+ it "returns false if the exception is not an SpecExpectationNotMetError or an SpecExpectationNotFoundError" do
+ exc = ExceptionState.new @state, "", Exception.new("Fail!")
+ exc.failure?.should be_false
+ end
+end
+
+describe ExceptionState, "#message" do
+ before :each do
+ @state = ExampleState.new ContextState.new("C#m"), "works"
+ end
+
+ it "returns <No message> if the exception message is empty" do
+ exc = ExceptionState.new @state, "", Exception.new("")
+ exc.message.should == "<No message>"
+ end
+
+ it "returns the message without exception class when the exception is an SpecExpectationNotMetError" do
+ exc = ExceptionState.new @state, "", SpecExpectationNotMetError.new("Fail!")
+ exc.message.should == "Fail!"
+ end
+
+ it "returns SpecExpectationNotFoundError#message when the exception is an SpecExpectationNotFoundError" do
+ e = SpecExpectationNotFoundError.new
+ exc = ExceptionState.new @state, "", e
+ exc.message.should == e.message
+ end
+
+ it "returns the message with exception class when the exception is not an SpecExpectationNotMetError or an SpecExpectationNotFoundError" do
+ exc = ExceptionState.new @state, "", Exception.new("Fail!")
+ exc.message.should == "Exception: Fail!"
+ end
+end
+
+describe ExceptionState, "#backtrace" do
+ before :each do
+ @state = ExampleState.new ContextState.new("C#m"), "works"
+ begin
+ raise Exception
+ rescue Exception => @exception
+ @exc = ExceptionState.new @state, "", @exception
+ end
+ end
+
+ after :each do
+ $MSPEC_DEBUG = nil
+ end
+
+ it "returns a string representation of the exception backtrace" do
+ @exc.backtrace.should be_kind_of(String)
+ end
+
+ it "does not filter files from the backtrace if $MSPEC_DEBUG is true" do
+ $MSPEC_DEBUG = true
+ @exc.backtrace.should == @exception.backtrace.join("\n")
+ end
+
+ it "filters files matching config[:backtrace_filter]" do
+ MSpecScript.set :backtrace_filter, %r[mspec/lib]
+ $MSPEC_DEBUG = nil
+ @exc.backtrace.split("\n").each do |line|
+ line.should_not =~ %r[mspec/lib]
+ end
+ end
+end
diff --git a/spec/mspec/spec/runner/filters/a.yaml b/spec/mspec/spec/runner/filters/a.yaml
new file mode 100644
index 0000000000..1940e3cba6
--- /dev/null
+++ b/spec/mspec/spec/runner/filters/a.yaml
@@ -0,0 +1,4 @@
+---
+A#:
+- a
+- aa
diff --git a/spec/mspec/spec/runner/filters/b.yaml b/spec/mspec/spec/runner/filters/b.yaml
new file mode 100644
index 0000000000..a24bdb2f19
--- /dev/null
+++ b/spec/mspec/spec/runner/filters/b.yaml
@@ -0,0 +1,11 @@
+---
+B.:
+- b
+- bb
+B::C#:
+- b!
+- b=
+- b?
+- "-"
+- "[]"
+- "[]="
diff --git a/spec/mspec/spec/runner/filters/match_spec.rb b/spec/mspec/spec/runner/filters/match_spec.rb
new file mode 100644
index 0000000000..f2c665c495
--- /dev/null
+++ b/spec/mspec/spec/runner/filters/match_spec.rb
@@ -0,0 +1,34 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/runner/filters/match'
+
+describe MatchFilter, "#===" do
+ before :each do
+ @filter = MatchFilter.new nil, 'a', 'b', 'c'
+ end
+
+ it "returns true if the argument matches any of the #initialize strings" do
+ @filter.===('aaa').should == true
+ @filter.===('bccb').should == true
+ end
+
+ it "returns false if the argument matches none of the #initialize strings" do
+ @filter.===('d').should == false
+ end
+end
+
+describe MatchFilter, "#register" do
+ it "registers itself with MSpec for the designated action list" do
+ filter = MatchFilter.new :include
+ MSpec.should_receive(:register).with(:include, filter)
+ filter.register
+ end
+end
+
+describe MatchFilter, "#unregister" do
+ it "unregisters itself with MSpec for the designated action list" do
+ filter = MatchFilter.new :exclude
+ MSpec.should_receive(:unregister).with(:exclude, filter)
+ filter.unregister
+ end
+end
diff --git a/spec/mspec/spec/runner/filters/profile_spec.rb b/spec/mspec/spec/runner/filters/profile_spec.rb
new file mode 100644
index 0000000000..89d0ad1911
--- /dev/null
+++ b/spec/mspec/spec/runner/filters/profile_spec.rb
@@ -0,0 +1,117 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/runner/filters/profile'
+
+describe ProfileFilter, "#find" do
+ before :each do
+ @filter = ProfileFilter.new nil
+ File.stub(:exist?).and_return(false)
+ @file = "rails.yaml"
+ end
+
+ it "attempts to locate the file through the expanded path name" do
+ File.should_receive(:expand_path).with(@file).and_return(@file)
+ File.should_receive(:exist?).with(@file).and_return(true)
+ @filter.find(@file).should == @file
+ end
+
+ it "attempts to locate the file in 'spec/profiles'" do
+ path = File.join "spec/profiles", @file
+ File.should_receive(:exist?).with(path).and_return(true)
+ @filter.find(@file).should == path
+ end
+
+ it "attempts to locate the file in 'spec'" do
+ path = File.join "spec", @file
+ File.should_receive(:exist?).with(path).and_return(true)
+ @filter.find(@file).should == path
+ end
+
+ it "attempts to locate the file in 'profiles'" do
+ path = File.join "profiles", @file
+ File.should_receive(:exist?).with(path).and_return(true)
+ @filter.find(@file).should == path
+ end
+
+ it "attempts to locate the file in '.'" do
+ path = File.join ".", @file
+ File.should_receive(:exist?).with(path).and_return(true)
+ @filter.find(@file).should == path
+ end
+end
+
+describe ProfileFilter, "#parse" do
+ before :each do
+ @filter = ProfileFilter.new nil
+ @file = File.open(File.dirname(__FILE__) + "/b.yaml", "r")
+ end
+
+ after :each do
+ @file.close
+ end
+
+ it "creates a Hash of the contents of the YAML file" do
+ @filter.parse(@file).should == {
+ "B." => ["b", "bb"],
+ "B::C#" => ["b!", "b=", "b?", "-", "[]", "[]="]
+ }
+ end
+end
+
+describe ProfileFilter, "#load" do
+ before :each do
+ @filter = ProfileFilter.new nil
+ @files = [
+ File.dirname(__FILE__) + "/a.yaml",
+ File.dirname(__FILE__) + "/b.yaml"
+ ]
+ end
+
+ it "generates a composite hash from multiple YAML files" do
+ @filter.load(*@files).should == {
+ "A#" => ["a", "aa"],
+ "B." => ["b", "bb"],
+ "B::C#" => ["b!", "b=", "b?", "-", "[]", "[]="]
+ }
+ end
+end
+
+describe ProfileFilter, "#===" do
+ before :each do
+ @filter = ProfileFilter.new nil
+ @filter.stub(:load).and_return({ "A#" => ["[]=", "a", "a!", "a?", "aa="]})
+ @filter.send :initialize, nil
+ end
+
+ it "returns true if the spec description is for a method in the profile" do
+ @filter.===("The A#[]= method").should == true
+ @filter.===("A#a returns").should == true
+ @filter.===("A#a! replaces").should == true
+ @filter.===("A#a? returns").should == true
+ @filter.===("A#aa= raises").should == true
+ end
+
+ it "returns false if the spec description is for a method not in the profile" do
+ @filter.===("The A#[] method").should == false
+ @filter.===("B#a returns").should == false
+ @filter.===("A.a! replaces").should == false
+ @filter.===("AA#a? returns").should == false
+ @filter.===("A#aa raises").should == false
+ end
+end
+
+describe ProfileFilter, "#register" do
+ it "registers itself with MSpec for the designated action list" do
+ filter = ProfileFilter.new :include
+ MSpec.should_receive(:register).with(:include, filter)
+ filter.register
+ end
+end
+
+describe ProfileFilter, "#unregister" do
+ it "unregisters itself with MSpec for the designated action list" do
+ filter = ProfileFilter.new :exclude
+ MSpec.should_receive(:unregister).with(:exclude, filter)
+ filter.unregister
+ end
+end
diff --git a/spec/mspec/spec/runner/filters/regexp_spec.rb b/spec/mspec/spec/runner/filters/regexp_spec.rb
new file mode 100644
index 0000000000..8e9b0ec7e8
--- /dev/null
+++ b/spec/mspec/spec/runner/filters/regexp_spec.rb
@@ -0,0 +1,31 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/runner/filters/regexp'
+
+describe MatchFilter, "#===" do
+ before :each do
+ @filter = RegexpFilter.new nil, 'a(b|c)', 'b[^ab]', 'cc?'
+ end
+
+ it "returns true if the argument matches any of the #initialize strings" do
+ @filter.===('ab').should == true
+ @filter.===('bc suffix').should == true
+ @filter.===('prefix cc').should == true
+ end
+
+ it "returns false if the argument matches none of the #initialize strings" do
+ @filter.===('aa').should == false
+ @filter.===('ba').should == false
+ @filter.===('prefix d suffix').should == false
+ end
+end
+
+describe RegexpFilter, "#to_regexp" do
+ before :each do
+ @filter = RegexpFilter.new nil
+ end
+
+ it "converts its arguments to Regexp instances" do
+ @filter.send(:to_regexp, 'a(b|c)', 'b[^ab]', 'cc?').should == [/a(b|c)/, /b[^ab]/, /cc?/]
+ end
+end
diff --git a/spec/mspec/spec/runner/filters/tag_spec.rb b/spec/mspec/spec/runner/filters/tag_spec.rb
new file mode 100644
index 0000000000..fe1f3df039
--- /dev/null
+++ b/spec/mspec/spec/runner/filters/tag_spec.rb
@@ -0,0 +1,92 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/runner/filters/match'
+require 'mspec/runner/filters/tag'
+
+describe TagFilter, "#load" do
+ before :each do
+ @match = double("match filter").as_null_object
+ @filter = TagFilter.new :include, "tag", "key"
+ @tag = SpecTag.new "tag(comment):description"
+ MSpec.stub(:read_tags).and_return([@tag])
+ MSpec.stub(:register)
+ end
+
+ it "loads tags from the tag file" do
+ MSpec.should_receive(:read_tags).with(["tag", "key"]).and_return([])
+ @filter.load
+ end
+
+
+ it "registers itself with MSpec for the :include action" do
+ filter = TagFilter.new(:include)
+ MSpec.should_receive(:register).with(:include, filter)
+ filter.load
+ end
+
+ it "registers itself with MSpec for the :exclude action" do
+ filter = TagFilter.new(:exclude)
+ MSpec.should_receive(:register).with(:exclude, filter)
+ filter.load
+ end
+end
+
+describe TagFilter, "#unload" do
+ before :each do
+ @filter = TagFilter.new :include, "tag", "key"
+ @tag = SpecTag.new "tag(comment):description"
+ MSpec.stub(:read_tags).and_return([@tag])
+ MSpec.stub(:register)
+ end
+
+ it "unregisters itself" do
+ @filter.load
+ MSpec.should_receive(:unregister).with(:include, @filter)
+ @filter.unload
+ end
+end
+
+describe TagFilter, "#register" do
+ before :each do
+ MSpec.stub(:register)
+ end
+
+ it "registers itself with MSpec for the :load, :unload actions" do
+ filter = TagFilter.new(nil)
+ MSpec.should_receive(:register).with(:load, filter)
+ MSpec.should_receive(:register).with(:unload, filter)
+ filter.register
+ end
+end
+
+describe TagFilter, "#unregister" do
+ before :each do
+ MSpec.stub(:unregister)
+ end
+
+ it "unregisters itself with MSpec for the :load, :unload actions" do
+ filter = TagFilter.new(nil)
+ MSpec.should_receive(:unregister).with(:load, filter)
+ MSpec.should_receive(:unregister).with(:unload, filter)
+ filter.unregister
+ end
+end
+
+describe TagFilter, "#===" do
+ before :each do
+ @filter = TagFilter.new nil, "tag", "key"
+ @tag = SpecTag.new "tag(comment):description"
+ MSpec.stub(:read_tags).and_return([@tag])
+ MSpec.stub(:register)
+ @filter.load
+ end
+
+ it "returns true if the argument matches any of the descriptions" do
+ @filter.===('description').should == true
+ end
+
+ it "returns false if the argument matches none of the descriptions" do
+ @filter.===('descriptionA').should == false
+ @filter.===('adescription').should == false
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/describe_spec.rb b/spec/mspec/spec/runner/formatters/describe_spec.rb
new file mode 100644
index 0000000000..415ced71fb
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/describe_spec.rb
@@ -0,0 +1,67 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/describe'
+require 'mspec/runner/example'
+
+describe DescribeFormatter, "#finish" do
+ before :each do
+ MSpec.stub(:register)
+ MSpec.stub(:unregister)
+
+ @timer = double("timer").as_null_object
+ TimerAction.stub(:new).and_return(@timer)
+ @timer.stub(:format).and_return("Finished in 2.0 seconds")
+
+ $stdout = @out = IOStub.new
+ context = ContextState.new "Class#method"
+ @state = ExampleState.new(context, "runs")
+
+ @formatter = DescribeFormatter.new
+ @formatter.register
+
+ @tally = @formatter.tally
+ @counter = @tally.counter
+
+ @counter.files!
+ @counter.examples!
+ @counter.expectations! 2
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a summary of elapsed time" do
+ @formatter.finish
+ @out.should =~ /^Finished in 2.0 seconds$/
+ end
+
+ it "prints a tally of counts" do
+ @formatter.finish
+ @out.should =~ /^1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged$/
+ end
+
+ it "does not print exceptions" do
+ @formatter.finish
+ @out.should == %[
+
+Finished in 2.0 seconds
+
+1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged
+]
+ end
+
+ it "prints a summary of failures and errors for each describe block" do
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @formatter.finish
+ @out.should == %[
+
+Class#method 0 failures, 1 error
+
+Finished in 2.0 seconds
+
+1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged
+]
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/dotted_spec.rb b/spec/mspec/spec/runner/formatters/dotted_spec.rb
new file mode 100644
index 0000000000..1e9b06f6e1
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/dotted_spec.rb
@@ -0,0 +1,285 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/dotted'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+require 'mspec/utils/script'
+
+describe DottedFormatter, "#initialize" do
+ it "permits zero arguments" do
+ DottedFormatter.new
+ end
+
+ it "accepts one argument" do
+ DottedFormatter.new nil
+ end
+end
+
+describe DottedFormatter, "#register" do
+ before :each do
+ @formatter = DottedFormatter.new
+ MSpec.stub(:register)
+ end
+
+ it "registers self with MSpec for appropriate actions" do
+ MSpec.should_receive(:register).with(:exception, @formatter)
+ MSpec.should_receive(:register).with(:before, @formatter)
+ MSpec.should_receive(:register).with(:after, @formatter)
+ MSpec.should_receive(:register).with(:finish, @formatter)
+ @formatter.register
+ end
+
+ it "creates TimerAction and TallyAction" do
+ timer = double("timer")
+ tally = double("tally")
+ timer.should_receive(:register)
+ tally.should_receive(:register)
+ tally.should_receive(:counter)
+ TimerAction.should_receive(:new).and_return(timer)
+ TallyAction.should_receive(:new).and_return(tally)
+ @formatter.register
+ end
+end
+
+describe DottedFormatter, "#print" do
+ before :each do
+ $stdout = IOStub.new
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "writes to $stdout by default" do
+ formatter = DottedFormatter.new
+ formatter.print "begonias"
+ $stdout.should == "begonias"
+ end
+
+ it "writes to the file specified when the formatter was created" do
+ out = IOStub.new
+ File.should_receive(:open).with("some/file", "w").and_return(out)
+ formatter = DottedFormatter.new "some/file"
+ formatter.print "begonias"
+ out.should == "begonias"
+ end
+
+ it "flushes the IO output" do
+ $stdout.should_receive(:flush)
+ formatter = DottedFormatter.new
+ formatter.print "begonias"
+ end
+end
+
+describe DottedFormatter, "#exception" do
+ before :each do
+ @formatter = DottedFormatter.new
+ @failure = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("failed")
+ @error = ExceptionState.new nil, nil, MSpecExampleError.new("boom!")
+ end
+
+ it "sets the #failure? flag" do
+ @formatter.exception @failure
+ @formatter.failure?.should be_true
+ @formatter.exception @error
+ @formatter.failure?.should be_false
+ end
+
+ it "sets the #exception? flag" do
+ @formatter.exception @error
+ @formatter.exception?.should be_true
+ @formatter.exception @failure
+ @formatter.exception?.should be_true
+ end
+
+ it "addes the exception to the list of exceptions" do
+ @formatter.exceptions.should == []
+ @formatter.exception @error
+ @formatter.exception @failure
+ @formatter.exceptions.should == [@error, @failure]
+ end
+end
+
+describe DottedFormatter, "#exception?" do
+ before :each do
+ @formatter = DottedFormatter.new
+ @failure = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("failed")
+ @error = ExceptionState.new nil, nil, MSpecExampleError.new("boom!")
+ end
+
+ it "returns false if there have been no exceptions" do
+ @formatter.exception?.should be_false
+ end
+
+ it "returns true if any exceptions are errors" do
+ @formatter.exception @failure
+ @formatter.exception @error
+ @formatter.exception?.should be_true
+ end
+
+ it "returns true if all exceptions are failures" do
+ @formatter.exception @failure
+ @formatter.exception @failure
+ @formatter.exception?.should be_true
+ end
+
+ it "returns true if all exceptions are errors" do
+ @formatter.exception @error
+ @formatter.exception @error
+ @formatter.exception?.should be_true
+ end
+end
+
+describe DottedFormatter, "#failure?" do
+ before :each do
+ @formatter = DottedFormatter.new
+ @failure = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("failed")
+ @error = ExceptionState.new nil, nil, MSpecExampleError.new("boom!")
+ end
+
+ it "returns false if there have been no exceptions" do
+ @formatter.failure?.should be_false
+ end
+
+ it "returns false if any exceptions are errors" do
+ @formatter.exception @failure
+ @formatter.exception @error
+ @formatter.failure?.should be_false
+ end
+
+ it "returns true if all exceptions are failures" do
+ @formatter.exception @failure
+ @formatter.exception @failure
+ @formatter.failure?.should be_true
+ end
+end
+
+describe DottedFormatter, "#before" do
+ before :each do
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ @formatter = DottedFormatter.new
+ @formatter.exception ExceptionState.new(nil, nil, SpecExpectationNotMetError.new("Failed!"))
+ end
+
+ it "resets the #failure? flag to false" do
+ @formatter.failure?.should be_true
+ @formatter.before @state
+ @formatter.failure?.should be_false
+ end
+
+ it "resets the #exception? flag to false" do
+ @formatter.exception?.should be_true
+ @formatter.before @state
+ @formatter.exception?.should be_false
+ end
+end
+
+describe DottedFormatter, "#after" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = DottedFormatter.new
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a '.' if there was no exception raised" do
+ @formatter.after(@state)
+ @out.should == "."
+ end
+
+ it "prints an 'F' if there was an expectation failure" do
+ exc = SpecExpectationNotMetError.new "failed"
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ @formatter.after(@state)
+ @out.should == "F"
+ end
+
+ it "prints an 'E' if there was an exception other than expectation failure" do
+ exc = MSpecExampleError.new("boom!")
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ @formatter.after(@state)
+ @out.should == "E"
+ end
+
+ it "prints an 'E' if there are mixed exceptions and exepctation failures" do
+ exc = SpecExpectationNotMetError.new "failed"
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ exc = MSpecExampleError.new("boom!")
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ @formatter.after(@state)
+ @out.should == "E"
+ end
+end
+
+describe DottedFormatter, "#finish" do
+ before :each do
+ @tally = double("tally").as_null_object
+ TallyAction.stub(:new).and_return(@tally)
+ @timer = double("timer").as_null_object
+ TimerAction.stub(:new).and_return(@timer)
+
+ $stdout = @out = IOStub.new
+ context = ContextState.new "Class#method"
+ @state = ExampleState.new(context, "runs")
+ MSpec.stub(:register)
+ @formatter = DottedFormatter.new
+ @formatter.register
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a failure message for an exception" do
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
+ @formatter.exception exc
+ @formatter.after @state
+ @formatter.finish
+ @out.should =~ /^1\)\nClass#method runs ERROR$/
+ end
+
+ it "prints a backtrace for an exception" do
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @formatter.after @state
+ @formatter.finish
+ @out.should =~ %r[path/to/some/file.rb:35:in method$]
+ end
+
+ it "prints a summary of elapsed time" do
+ @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
+ @formatter.finish
+ @out.should =~ /^Finished in 2.0 seconds$/
+ end
+
+ it "prints a tally of counts" do
+ @tally.should_receive(:format).and_return("1 example, 0 failures")
+ @formatter.finish
+ @out.should =~ /^1 example, 0 failures$/
+ end
+
+ it "prints errors, backtraces, elapsed time, and tallies" do
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
+ @tally.should_receive(:format).and_return("1 example, 1 failure")
+ @formatter.after @state
+ @formatter.finish
+ @out.should ==
+%[E
+
+1)
+Class#method runs ERROR
+MSpecExampleError: broken
+path/to/some/file.rb:35:in method
+
+Finished in 2.0 seconds
+
+1 example, 1 failure
+]
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/file_spec.rb b/spec/mspec/spec/runner/formatters/file_spec.rb
new file mode 100644
index 0000000000..946683ad58
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/file_spec.rb
@@ -0,0 +1,84 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/file'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+
+describe FileFormatter, "#register" do
+ before :each do
+ @formatter = FileFormatter.new
+ MSpec.stub(:register)
+ MSpec.stub(:unregister)
+ end
+
+ it "registers self with MSpec for :load, :unload actions" do
+ MSpec.should_receive(:register).with(:load, @formatter)
+ MSpec.should_receive(:register).with(:unload, @formatter)
+ @formatter.register
+ end
+
+ it "unregisters self with MSpec for :before, :after actions" do
+ MSpec.should_receive(:unregister).with(:before, @formatter)
+ MSpec.should_receive(:unregister).with(:after, @formatter)
+ @formatter.register
+ end
+end
+
+describe FileFormatter, "#load" do
+ before :each do
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ @formatter = FileFormatter.new
+ @formatter.exception ExceptionState.new(nil, nil, SpecExpectationNotMetError.new("Failed!"))
+ end
+
+ it "resets the #failure? flag to false" do
+ @formatter.failure?.should be_true
+ @formatter.load @state
+ @formatter.failure?.should be_false
+ end
+
+ it "resets the #exception? flag to false" do
+ @formatter.exception?.should be_true
+ @formatter.load @state
+ @formatter.exception?.should be_false
+ end
+end
+
+describe FileFormatter, "#unload" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = FileFormatter.new
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a '.' if there was no exception raised" do
+ @formatter.unload(@state)
+ @out.should == "."
+ end
+
+ it "prints an 'F' if there was an expectation failure" do
+ exc = SpecExpectationNotMetError.new "failed"
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ @formatter.unload(@state)
+ @out.should == "F"
+ end
+
+ it "prints an 'E' if there was an exception other than expectation failure" do
+ exc = MSpecExampleError.new("boom!")
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ @formatter.unload(@state)
+ @out.should == "E"
+ end
+
+ it "prints an 'E' if there are mixed exceptions and exepctation failures" do
+ exc = SpecExpectationNotMetError.new "failed"
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ exc = MSpecExampleError.new("boom!")
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ @formatter.unload(@state)
+ @out.should == "E"
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/html_spec.rb b/spec/mspec/spec/runner/formatters/html_spec.rb
new file mode 100644
index 0000000000..3783ab6a89
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/html_spec.rb
@@ -0,0 +1,216 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/guards/guard'
+require 'mspec/runner/formatters/html'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+require 'mspec/utils/script'
+
+describe HtmlFormatter do
+ before :each do
+ @formatter = HtmlFormatter.new
+ end
+
+ it "responds to #register by registering itself with MSpec for appropriate actions" do
+ MSpec.stub(:register)
+ MSpec.should_receive(:register).with(:start, @formatter)
+ MSpec.should_receive(:register).with(:enter, @formatter)
+ MSpec.should_receive(:register).with(:leave, @formatter)
+ @formatter.register
+ end
+end
+
+describe HtmlFormatter, "#start" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = HtmlFormatter.new
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints the HTML head" do
+ @formatter.start
+ ruby_engine = RUBY_ENGINE
+ ruby_engine.should =~ /^#{ruby_engine}/
+ @out.should ==
+%[<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>Spec Output For #{ruby_engine} (#{RUBY_VERSION})</title>
+<style type="text/css">
+ul {
+ list-style: none;
+}
+.fail {
+ color: red;
+}
+.pass {
+ color: green;
+}
+#details :target {
+ background-color: #ffffe0;
+}
+</style>
+</head>
+<body>
+]
+ end
+end
+
+describe HtmlFormatter, "#enter" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = HtmlFormatter.new
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints the #describe string" do
+ @formatter.enter "describe"
+ @out.should == "<div><p>describe</p>\n<ul>\n"
+ end
+end
+
+describe HtmlFormatter, "#leave" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = HtmlFormatter.new
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints the closing tags for the #describe string" do
+ @formatter.leave
+ @out.should == "</ul>\n</div>\n"
+ end
+end
+
+describe HtmlFormatter, "#exception" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = HtmlFormatter.new
+ @formatter.register
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints the #it string once for each exception raised" do
+ exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing")
+ @formatter.exception exc
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful")
+ @formatter.exception exc
+ @out.should ==
+%[<li class="fail">- it (<a href="#details-1">FAILED - 1</a>)</li>
+<li class="fail">- it (<a href="#details-2">ERROR - 2</a>)</li>
+]
+ end
+end
+
+describe HtmlFormatter, "#after" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = HtmlFormatter.new
+ @formatter.register
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints the #it once when there are no exceptions raised" do
+ @formatter.after @state
+ @out.should == %[<li class="pass">- it</li>\n]
+ end
+
+ it "does not print any output if an exception is raised" do
+ exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing")
+ @formatter.exception exc
+ out = @out.dup
+ @formatter.after @state
+ @out.should == out
+ end
+end
+
+describe HtmlFormatter, "#finish" do
+ before :each do
+ @tally = double("tally").as_null_object
+ TallyAction.stub(:new).and_return(@tally)
+ @timer = double("timer").as_null_object
+ TimerAction.stub(:new).and_return(@timer)
+
+ $stdout = @out = IOStub.new
+ context = ContextState.new "describe"
+ @state = ExampleState.new(context, "it")
+ MSpec.stub(:register)
+ @formatter = HtmlFormatter.new
+ @formatter.register
+ @exception = MSpecExampleError.new("broken")
+ @exception.stub(:backtrace).and_return(["file.rb:1", "file.rb:2"])
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a failure message for an exception" do
+ exc = ExceptionState.new @state, nil, @exception
+ @formatter.exception exc
+ @formatter.finish
+ @out.should include "<p>describe it ERROR</p>"
+ end
+
+ it "prints a backtrace for an exception" do
+ exc = ExceptionState.new @state, nil, @exception
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @formatter.finish
+ @out.should =~ %r[<pre>.*path/to/some/file.rb:35:in method.*</pre>]m
+ end
+
+ it "prints a summary of elapsed time" do
+ @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
+ @formatter.finish
+ @out.should include "<p>Finished in 2.0 seconds</p>\n"
+ end
+
+ it "prints a tally of counts" do
+ @tally.should_receive(:format).and_return("1 example, 0 failures")
+ @formatter.finish
+ @out.should include '<p class="pass">1 example, 0 failures</p>'
+ end
+
+ it "prints errors, backtraces, elapsed time, and tallies" do
+ exc = ExceptionState.new @state, nil, @exception
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+
+ @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
+ @tally.should_receive(:format).and_return("1 example, 1 failures")
+ @formatter.finish
+ @out.should ==
+%[<li class=\"fail\">- it (<a href=\"#details-1\">ERROR - 1</a>)</li>
+<hr>
+<ol id="details">
+<li id="details-1"><p>describe it ERROR</p>
+<p>MSpecExampleError: broken</p>
+<pre>
+path/to/some/file.rb:35:in method</pre>
+</li>
+</ol>
+<p>Finished in 2.0 seconds</p>
+<p class="fail">1 example, 1 failures</p>
+</body>
+</html>
+]
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/junit_spec.rb b/spec/mspec/spec/runner/formatters/junit_spec.rb
new file mode 100644
index 0000000000..66e7d70e92
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/junit_spec.rb
@@ -0,0 +1,147 @@
+# -*- coding: utf-8 -*-
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/junit'
+require 'mspec/runner/example'
+
+describe JUnitFormatter, "#initialize" do
+ it "permits zero arguments" do
+ lambda { JUnitFormatter.new }.should_not raise_error
+ end
+
+ it "accepts one argument" do
+ lambda { JUnitFormatter.new nil }.should_not raise_error
+ end
+end
+
+describe JUnitFormatter, "#print" do
+ before :each do
+ $stdout = IOStub.new
+ @out = IOStub.new
+ File.stub(:open).and_return(@out)
+ @formatter = JUnitFormatter.new "some/file"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "writes to $stdout if #switch has not been called" do
+ @formatter.print "begonias"
+ $stdout.should == "begonias"
+ @out.should == ""
+ end
+
+ it "writes to the file passed to #initialize once #switch has been called" do
+ @formatter.switch
+ @formatter.print "begonias"
+ $stdout.should == ""
+ @out.should == "begonias"
+ end
+
+ it "writes to $stdout once #switch is called if no file was passed to #initialize" do
+ formatter = JUnitFormatter.new
+ formatter.switch
+ formatter.print "begonias"
+ $stdout.should == "begonias"
+ @out.should == ""
+ end
+end
+
+describe JUnitFormatter, "#finish" do
+ before :each do
+ @tally = double("tally").as_null_object
+ @counter = double("counter").as_null_object
+ @tally.stub(:counter).and_return(@counter)
+ TallyAction.stub(:new).and_return(@tally)
+
+ @timer = double("timer").as_null_object
+ TimerAction.stub(:new).and_return(@timer)
+
+ $stdout = IOStub.new
+ context = ContextState.new "describe"
+ @state = ExampleState.new(context, "it")
+
+ @formatter = JUnitFormatter.new
+ @formatter.stub(:backtrace).and_return("")
+ MSpec.stub(:register)
+ @formatter.register
+
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @formatter.after @state
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "calls #switch" do
+ @formatter.should_receive(:switch)
+ @formatter.finish
+ end
+
+ it "outputs a failure message and backtrace" do
+ @formatter.finish
+ $stdout.should include 'message="error in describe it" type="error"'
+ $stdout.should include "MSpecExampleError: broken\n"
+ $stdout.should include "path/to/some/file.rb:35:in method"
+ end
+
+ it "encodes message and backtrace in latin1 for jenkins" do
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken…")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in methød")
+ @formatter.exception exc
+ @formatter.finish
+ $stdout.should =~ /MSpecExampleError: broken((\.\.\.)|\?)\n/
+ $stdout.should =~ /path\/to\/some\/file\.rb:35:in meth(\?|o)d/
+ end
+
+ it "outputs an elapsed time" do
+ @timer.should_receive(:elapsed).and_return(4.2)
+ @formatter.finish
+ $stdout.should include 'time="4.2"'
+ end
+
+ it "outputs overall elapsed time" do
+ @timer.should_receive(:elapsed).and_return(4.2)
+ @formatter.finish
+ $stdout.should include 'timeCount="4.2"'
+ end
+
+ it "outputs the number of examples as test count" do
+ @counter.should_receive(:examples).and_return(9)
+ @formatter.finish
+ $stdout.should include 'tests="9"'
+ end
+
+ it "outputs overall number of examples as test count" do
+ @counter.should_receive(:examples).and_return(9)
+ @formatter.finish
+ $stdout.should include 'testCount="9"'
+ end
+
+ it "outputs a failure count" do
+ @counter.should_receive(:failures).and_return(2)
+ @formatter.finish
+ $stdout.should include 'failureCount="2"'
+ end
+
+ it "outputs overall failure count" do
+ @counter.should_receive(:failures).and_return(2)
+ @formatter.finish
+ $stdout.should include 'failures="2"'
+ end
+
+ it "outputs an error count" do
+ @counter.should_receive(:errors).and_return(1)
+ @formatter.finish
+ $stdout.should include 'errors="1"'
+ end
+
+ it "outputs overall error count" do
+ @counter.should_receive(:errors).and_return(1)
+ @formatter.finish
+ $stdout.should include 'errorCount="1"'
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/method_spec.rb b/spec/mspec/spec/runner/formatters/method_spec.rb
new file mode 100644
index 0000000000..77204f74c5
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/method_spec.rb
@@ -0,0 +1,178 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/method'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+require 'mspec/utils/script'
+
+describe MethodFormatter, "#method_type" do
+ before :each do
+ @formatter = MethodFormatter.new
+ end
+
+ it "returns 'class' if the separator is '.' or '::'" do
+ @formatter.method_type('.').should == "class"
+ @formatter.method_type('::').should == "class"
+ end
+
+ it "returns 'instance' if the separator is '#'" do
+ @formatter.method_type('#').should == "instance"
+ end
+
+ it "returns 'unknown' for all other cases" do
+ @formatter.method_type(nil).should == "unknown"
+ end
+end
+
+describe MethodFormatter, "#before" do
+ before :each do
+ @formatter = MethodFormatter.new
+ MSpec.stub(:register)
+ @formatter.register
+ end
+
+ it "resets the tally counters to 0" do
+ @formatter.tally.counter.examples = 3
+ @formatter.tally.counter.expectations = 4
+ @formatter.tally.counter.failures = 2
+ @formatter.tally.counter.errors = 1
+
+ state = ExampleState.new ContextState.new("describe"), "it"
+ @formatter.before state
+ @formatter.tally.counter.examples.should == 0
+ @formatter.tally.counter.expectations.should == 0
+ @formatter.tally.counter.failures.should == 0
+ @formatter.tally.counter.errors.should == 0
+ end
+
+ it "records the class, method if available" do
+ state = ExampleState.new ContextState.new("Some#method"), "it"
+ @formatter.before state
+ key = "Some#method"
+ @formatter.methods.keys.should include(key)
+ h = @formatter.methods[key]
+ h[:class].should == "Some"
+ h[:method].should == "method"
+ h[:description].should == "Some#method it"
+ end
+
+ it "does not record class, method unless both are available" do
+ state = ExampleState.new ContextState.new("Some method"), "it"
+ @formatter.before state
+ key = "Some method"
+ @formatter.methods.keys.should include(key)
+ h = @formatter.methods[key]
+ h[:class].should == ""
+ h[:method].should == ""
+ h[:description].should == "Some method it"
+ end
+
+ it "sets the method type to unknown if class and method are not available" do
+ state = ExampleState.new ContextState.new("Some method"), "it"
+ @formatter.before state
+ key = "Some method"
+ h = @formatter.methods[key]
+ h[:type].should == "unknown"
+ end
+
+ it "sets the method type based on the class, method separator" do
+ [["C#m", "instance"], ["C.m", "class"], ["C::m", "class"]].each do |k, t|
+ state = ExampleState.new ContextState.new(k), "it"
+ @formatter.before state
+ h = @formatter.methods[k]
+ h[:type].should == t
+ end
+ end
+
+ it "clears the list of exceptions" do
+ state = ExampleState.new ContextState.new("describe"), "it"
+ @formatter.exceptions << "stuff"
+ @formatter.before state
+ @formatter.exceptions.should be_empty
+ end
+end
+
+describe MethodFormatter, "#after" do
+ before :each do
+ @formatter = MethodFormatter.new
+ MSpec.stub(:register)
+ @formatter.register
+ end
+
+ it "sets the tally counts" do
+ state = ExampleState.new ContextState.new("Some#method"), "it"
+ @formatter.before state
+
+ @formatter.tally.counter.examples = 3
+ @formatter.tally.counter.expectations = 4
+ @formatter.tally.counter.failures = 2
+ @formatter.tally.counter.errors = 1
+
+ @formatter.after state
+ h = @formatter.methods["Some#method"]
+ h[:examples].should == 3
+ h[:expectations].should == 4
+ h[:failures].should == 2
+ h[:errors].should == 1
+ end
+
+ it "renders the list of exceptions" do
+ state = ExampleState.new ContextState.new("Some#method"), "it"
+ @formatter.before state
+
+ exc = SpecExpectationNotMetError.new "failed"
+ @formatter.exception ExceptionState.new(state, nil, exc)
+ @formatter.exception ExceptionState.new(state, nil, exc)
+
+ @formatter.after state
+ h = @formatter.methods["Some#method"]
+ h[:exceptions].should == [
+ %[failed\n\n],
+ %[failed\n\n]
+ ]
+ end
+end
+
+describe MethodFormatter, "#after" do
+ before :each do
+ $stdout = IOStub.new
+ context = ContextState.new "Class#method"
+ @state = ExampleState.new(context, "runs")
+ @formatter = MethodFormatter.new
+ MSpec.stub(:register)
+ @formatter.register
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a summary of the results of an example in YAML format" do
+ @formatter.before @state
+ @formatter.tally.counter.examples = 3
+ @formatter.tally.counter.expectations = 4
+ @formatter.tally.counter.failures = 2
+ @formatter.tally.counter.errors = 1
+
+ exc = SpecExpectationNotMetError.new "failed"
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+
+ @formatter.after @state
+ @formatter.finish
+ $stdout.should ==
+%[---
+"Class#method":
+ class: "Class"
+ method: "method"
+ type: instance
+ description: "Class#method runs"
+ examples: 3
+ expectations: 4
+ failures: 2
+ errors: 1
+ exceptions:
+ - "failed\\n\\n"
+ - "failed\\n\\n"
+]
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/multi_spec.rb b/spec/mspec/spec/runner/formatters/multi_spec.rb
new file mode 100644
index 0000000000..72bf629f71
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/multi_spec.rb
@@ -0,0 +1,68 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/multi'
+require 'mspec/runner/example'
+
+describe MultiFormatter, "#aggregate_results" do
+ before :each do
+ @stdout, $stdout = $stdout, IOStub.new
+
+ @file = double("file").as_null_object
+
+ File.stub(:delete)
+ File.stub(:read)
+
+ @hash = { "files"=>1, "examples"=>1, "expectations"=>2, "failures"=>0, "errors"=>0 }
+ YAML.stub(:load).and_return(@hash)
+
+ @formatter = MultiFormatter.new
+ @formatter.timer.stub(:format).and_return("Finished in 42 seconds")
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it "outputs a summary without errors" do
+ @formatter.aggregate_results(["a", "b"])
+ @formatter.finish
+ $stdout.should ==
+%[
+
+Finished in 42 seconds
+
+2 files, 2 examples, 4 expectations, 0 failures, 0 errors, 0 tagged
+]
+ end
+
+ it "outputs a summary with errors" do
+ @hash["exceptions"] = [
+ "Some#method works real good FAILED\nExpected real good\n to equal fail\n\nfoo.rb:1\nfoo.rb:2",
+ "Some#method never fails ERROR\nExpected 5\n to equal 3\n\nfoo.rb:1\nfoo.rb:2"
+ ]
+ @formatter.aggregate_results(["a"])
+ @formatter.finish
+ $stdout.should ==
+%[
+
+1)
+Some#method works real good FAILED
+Expected real good
+ to equal fail
+
+foo.rb:1
+foo.rb:2
+
+2)
+Some#method never fails ERROR
+Expected 5
+ to equal 3
+
+foo.rb:1
+foo.rb:2
+
+Finished in 42 seconds
+
+1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged
+]
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/specdoc_spec.rb b/spec/mspec/spec/runner/formatters/specdoc_spec.rb
new file mode 100644
index 0000000000..edb439fc11
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/specdoc_spec.rb
@@ -0,0 +1,106 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/specdoc'
+require 'mspec/runner/example'
+
+describe SpecdocFormatter do
+ before :each do
+ @formatter = SpecdocFormatter.new
+ end
+
+ it "responds to #register by registering itself with MSpec for appropriate actions" do
+ MSpec.stub(:register)
+ MSpec.should_receive(:register).with(:enter, @formatter)
+ @formatter.register
+ end
+end
+
+describe SpecdocFormatter, "#enter" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = SpecdocFormatter.new
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints the #describe string" do
+ @formatter.enter("describe")
+ @out.should == "\ndescribe\n"
+ end
+end
+
+describe SpecdocFormatter, "#before" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = SpecdocFormatter.new
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints the #it string" do
+ @formatter.before @state
+ @out.should == "- it"
+ end
+
+ it "resets the #exception? flag" do
+ exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing")
+ @formatter.exception exc
+ @formatter.exception?.should be_true
+ @formatter.before @state
+ @formatter.exception?.should be_false
+ end
+end
+
+describe SpecdocFormatter, "#exception" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = SpecdocFormatter.new
+ context = ContextState.new "describe"
+ @state = ExampleState.new context, "it"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints 'ERROR' if an exception is not an SpecExpectationNotMetError" do
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful")
+ @formatter.exception exc
+ @out.should == " (ERROR - 1)"
+ end
+
+ it "prints 'FAILED' if an exception is an SpecExpectationNotMetError" do
+ exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing")
+ @formatter.exception exc
+ @out.should == " (FAILED - 1)"
+ end
+
+ it "prints the #it string if an exception has already been raised" do
+ exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing")
+ @formatter.exception exc
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful")
+ @formatter.exception exc
+ @out.should == " (FAILED - 1)\n- it (ERROR - 2)"
+ end
+end
+
+describe SpecdocFormatter, "#after" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = SpecdocFormatter.new
+ @state = ExampleState.new "describe", "it"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a newline character" do
+ @formatter.after @state
+ @out.should == "\n"
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/spinner_spec.rb b/spec/mspec/spec/runner/formatters/spinner_spec.rb
new file mode 100644
index 0000000000..a122620e39
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/spinner_spec.rb
@@ -0,0 +1,83 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/spinner'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+
+describe SpinnerFormatter, "#initialize" do
+ it "permits zero arguments" do
+ SpinnerFormatter.new
+ end
+
+ it "accepts one argument" do
+ SpinnerFormatter.new nil
+ end
+end
+
+describe SpinnerFormatter, "#register" do
+ before :each do
+ @formatter = SpinnerFormatter.new
+ MSpec.stub(:register)
+ end
+
+ it "registers self with MSpec for appropriate actions" do
+ MSpec.should_receive(:register).with(:start, @formatter)
+ MSpec.should_receive(:register).with(:unload, @formatter)
+ MSpec.should_receive(:register).with(:after, @formatter)
+ MSpec.should_receive(:register).with(:finish, @formatter)
+ @formatter.register
+ end
+
+ it "creates TimerAction and TallyAction" do
+ timer = double("timer")
+ tally = double("tally")
+ timer.should_receive(:register)
+ tally.should_receive(:register)
+ tally.should_receive(:counter)
+ TimerAction.should_receive(:new).and_return(timer)
+ TallyAction.should_receive(:new).and_return(tally)
+ @formatter.register
+ end
+end
+
+describe SpinnerFormatter, "#print" do
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "ignores the argument to #initialize and writes to $stdout" do
+ $stdout = IOStub.new
+ formatter = SpinnerFormatter.new "some/file"
+ formatter.print "begonias"
+ $stdout.should == "begonias"
+ end
+end
+
+describe SpinnerFormatter, "#after" do
+ before :each do
+ $stdout = IOStub.new
+ MSpec.store(:files, ["a", "b", "c", "d"])
+ @formatter = SpinnerFormatter.new
+ @formatter.register
+ @state = ExampleState.new("describe", "it")
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "updates the spinner" do
+ @formatter.start
+ @formatter.after @state
+ @formatter.unload
+
+ if ENV["TERM"] != "dumb"
+ green = "\e[0;32m"
+ reset = "\e[0m"
+ end
+
+ output = "\r[/ | 0% | 00:00:00] #{green} 0F #{green} 0E#{reset} " \
+ "\r[- | 0% | 00:00:00] #{green} 0F #{green} 0E#{reset} " \
+ "\r[\\ | ========== 25% | 00:00:00] #{green} 0F #{green} 0E#{reset} "
+ $stdout.should == output
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/summary_spec.rb b/spec/mspec/spec/runner/formatters/summary_spec.rb
new file mode 100644
index 0000000000..16a156b695
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/summary_spec.rb
@@ -0,0 +1,26 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/summary'
+require 'mspec/runner/example'
+
+describe SummaryFormatter, "#after" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = SummaryFormatter.new
+ @formatter.register
+ context = ContextState.new "describe"
+ @state = ExampleState.new(context, "it")
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "does not print anything" do
+ exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing")
+ @formatter.exception exc
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful")
+ @formatter.exception exc
+ @formatter.after(@state)
+ @out.should == ""
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/unit_spec.rb b/spec/mspec/spec/runner/formatters/unit_spec.rb
new file mode 100644
index 0000000000..18167a32b8
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/unit_spec.rb
@@ -0,0 +1,74 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/unit'
+require 'mspec/runner/example'
+require 'mspec/utils/script'
+
+describe UnitdiffFormatter, "#finish" do
+ before :each do
+ @tally = double("tally").as_null_object
+ TallyAction.stub(:new).and_return(@tally)
+ @timer = double("timer").as_null_object
+ TimerAction.stub(:new).and_return(@timer)
+
+ $stdout = @out = IOStub.new
+ context = ContextState.new "describe"
+ @state = ExampleState.new(context, "it")
+ MSpec.stub(:register)
+ @formatter = UnitdiffFormatter.new
+ @formatter.register
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a failure message for an exception" do
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
+ @formatter.exception exc
+ @formatter.after @state
+ @formatter.finish
+ @out.should =~ /^1\)\ndescribe it ERROR$/
+ end
+
+ it "prints a backtrace for an exception" do
+ exc = ExceptionState.new @state, nil, Exception.new("broken")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @formatter.finish
+ @out.should =~ %r[path/to/some/file.rb:35:in method$]
+ end
+
+ it "prints a summary of elapsed time" do
+ @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
+ @formatter.finish
+ @out.should =~ /^Finished in 2.0 seconds$/
+ end
+
+ it "prints a tally of counts" do
+ @tally.should_receive(:format).and_return("1 example, 0 failures")
+ @formatter.finish
+ @out.should =~ /^1 example, 0 failures$/
+ end
+
+ it "prints errors, backtraces, elapsed time, and tallies" do
+ exc = ExceptionState.new @state, nil, Exception.new("broken")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @formatter.after @state
+ @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
+ @tally.should_receive(:format).and_return("1 example, 0 failures")
+ @formatter.finish
+ @out.should ==
+%[E
+
+Finished in 2.0 seconds
+
+1)
+describe it ERROR
+Exception: broken:
+path/to/some/file.rb:35:in method
+
+1 example, 0 failures
+]
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/yaml_spec.rb b/spec/mspec/spec/runner/formatters/yaml_spec.rb
new file mode 100644
index 0000000000..eb4d99f74c
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/yaml_spec.rb
@@ -0,0 +1,125 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/yaml'
+require 'mspec/runner/example'
+
+describe YamlFormatter, "#initialize" do
+ it "permits zero arguments" do
+ YamlFormatter.new
+ end
+
+ it "accepts one argument" do
+ YamlFormatter.new nil
+ end
+end
+
+describe YamlFormatter, "#print" do
+ before :each do
+ $stdout = IOStub.new
+ @out = IOStub.new
+ File.stub(:open).and_return(@out)
+ @formatter = YamlFormatter.new "some/file"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "writes to $stdout if #switch has not been called" do
+ @formatter.print "begonias"
+ $stdout.should == "begonias"
+ @out.should == ""
+ end
+
+ it "writes to the file passed to #initialize once #switch has been called" do
+ @formatter.switch
+ @formatter.print "begonias"
+ $stdout.should == ""
+ @out.should == "begonias"
+ end
+
+ it "writes to $stdout once #switch is called if no file was passed to #initialize" do
+ formatter = YamlFormatter.new
+ formatter.switch
+ formatter.print "begonias"
+ $stdout.should == "begonias"
+ @out.should == ""
+ end
+end
+
+describe YamlFormatter, "#finish" do
+ before :each do
+ @tally = double("tally").as_null_object
+ @counter = double("counter").as_null_object
+ @tally.stub(:counter).and_return(@counter)
+ TallyAction.stub(:new).and_return(@tally)
+
+ @timer = double("timer").as_null_object
+ TimerAction.stub(:new).and_return(@timer)
+
+ $stdout = IOStub.new
+ context = ContextState.new "describe"
+ @state = ExampleState.new(context, "it")
+
+ @formatter = YamlFormatter.new
+ @formatter.stub(:backtrace).and_return("")
+ MSpec.stub(:register)
+ @formatter.register
+
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @formatter.after @state
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "calls #switch" do
+ @formatter.should_receive(:switch)
+ @formatter.finish
+ end
+
+ it "outputs a failure message and backtrace" do
+ @formatter.finish
+ $stdout.should include "describe it ERROR"
+ $stdout.should include "MSpecExampleError: broken\\n"
+ $stdout.should include "path/to/some/file.rb:35:in method"
+ end
+
+ it "outputs an elapsed time" do
+ @timer.should_receive(:elapsed).and_return(4.2)
+ @formatter.finish
+ $stdout.should include "time: 4.2"
+ end
+
+ it "outputs a file count" do
+ @counter.should_receive(:files).and_return(3)
+ @formatter.finish
+ $stdout.should include "files: 3"
+ end
+
+ it "outputs an example count" do
+ @counter.should_receive(:examples).and_return(3)
+ @formatter.finish
+ $stdout.should include "examples: 3"
+ end
+
+ it "outputs an expectation count" do
+ @counter.should_receive(:expectations).and_return(9)
+ @formatter.finish
+ $stdout.should include "expectations: 9"
+ end
+
+ it "outputs a failure count" do
+ @counter.should_receive(:failures).and_return(2)
+ @formatter.finish
+ $stdout.should include "failures: 2"
+ end
+
+ it "outputs an error count" do
+ @counter.should_receive(:errors).and_return(1)
+ @formatter.finish
+ $stdout.should include "errors: 1"
+ end
+end
diff --git a/spec/mspec/spec/runner/mspec_spec.rb b/spec/mspec/spec/runner/mspec_spec.rb
new file mode 100644
index 0000000000..91338c6ddb
--- /dev/null
+++ b/spec/mspec/spec/runner/mspec_spec.rb
@@ -0,0 +1,595 @@
+require 'spec_helper'
+require 'mspec/helpers/tmp'
+require 'mspec/helpers/fs'
+require 'mspec/matchers/base'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+
+describe MSpec, ".register_files" do
+ it "records which spec files to run" do
+ MSpec.register_files [:one, :two, :three]
+ MSpec.retrieve(:files).should == [:one, :two, :three]
+ end
+end
+
+describe MSpec, ".register_mode" do
+ before :each do
+ MSpec.clear_modes
+ end
+
+ it "sets execution mode flags" do
+ MSpec.register_mode :verify
+ MSpec.retrieve(:modes).should == [:verify]
+ end
+end
+
+describe MSpec, ".register_tags_patterns" do
+ it "records the patterns for generating a tag file from a spec file" do
+ MSpec.register_tags_patterns [[/spec\/ruby/, "spec/tags"], [/frozen/, "ruby"]]
+ MSpec.retrieve(:tags_patterns).should == [[/spec\/ruby/, "spec/tags"], [/frozen/, "ruby"]]
+ end
+end
+
+describe MSpec, ".register_exit" do
+ before :each do
+ MSpec.store :exit, 0
+ end
+
+ it "records the exit code" do
+ MSpec.exit_code.should == 0
+ MSpec.register_exit 1
+ MSpec.exit_code.should == 1
+ end
+end
+
+describe MSpec, ".exit_code" do
+ it "retrieves the code set with .register_exit" do
+ MSpec.register_exit 99
+ MSpec.exit_code.should == 99
+ end
+end
+
+describe MSpec, ".store" do
+ it "records data for MSpec settings" do
+ MSpec.store :anything, :value
+ MSpec.retrieve(:anything).should == :value
+ end
+end
+
+describe MSpec, ".retrieve" do
+ it "accesses .store'd data" do
+ MSpec.register :retrieve, :first
+ MSpec.retrieve(:retrieve).should == [:first]
+ end
+end
+
+describe MSpec, ".randomize" do
+ it "sets the flag to randomize spec execution order" do
+ MSpec.randomize?.should == false
+ MSpec.randomize
+ MSpec.randomize?.should == true
+ MSpec.randomize false
+ MSpec.randomize?.should == false
+ end
+end
+
+describe MSpec, ".register" do
+ it "is the gateway behind the register(symbol, action) facility" do
+ MSpec.register :bonus, :first
+ MSpec.register :bonus, :second
+ MSpec.register :bonus, :second
+ MSpec.retrieve(:bonus).should == [:first, :second]
+ end
+end
+
+describe MSpec, ".unregister" do
+ it "is the gateway behind the unregister(symbol, actions) facility" do
+ MSpec.register :unregister, :first
+ MSpec.register :unregister, :second
+ MSpec.unregister :unregister, :second
+ MSpec.retrieve(:unregister).should == [:first]
+ end
+end
+
+describe MSpec, ".protect" do
+ before :each do
+ MSpec.clear_current
+ @cs = ContextState.new "C#m"
+ @cs.parent = MSpec.current
+
+ @es = ExampleState.new @cs, "runs"
+ ScratchPad.record Exception.new("Sharp!")
+ end
+
+ it "returns true if no exception is raised" do
+ MSpec.protect("passed") { 1 }.should be_true
+ end
+
+ it "returns false if an exception is raised" do
+ MSpec.protect("testing") { raise ScratchPad.recorded }.should be_false
+ end
+
+ it "rescues any exceptions raised when evaluating the block argument" do
+ MSpec.protect("") { raise Exception, "Now you see me..." }
+ end
+
+ it "does not rescue SystemExit" do
+ begin
+ MSpec.protect("") { exit 1 }
+ rescue SystemExit
+ ScratchPad.record :system_exit
+ end
+ ScratchPad.recorded.should == :system_exit
+ end
+
+ it "calls all the exception actions" do
+ exc = ExceptionState.new @es, "testing", ScratchPad.recorded
+ ExceptionState.stub(:new).and_return(exc)
+ action = double("exception")
+ action.should_receive(:exception).with(exc)
+ MSpec.register :exception, action
+ MSpec.protect("testing") { raise ScratchPad.recorded }
+ MSpec.unregister :exception, action
+ end
+
+ it "registers a non-zero exit code when an exception is raised" do
+ MSpec.should_receive(:register_exit).with(1)
+ MSpec.protect("testing") { raise ScratchPad.recorded }
+ end
+end
+
+describe MSpec, ".register_current" do
+ before :each do
+ MSpec.clear_current
+ end
+
+ it "sets the value returned by MSpec.current" do
+ MSpec.current.should be_nil
+ MSpec.register_current :a
+ MSpec.current.should == :a
+ end
+end
+
+describe MSpec, ".clear_current" do
+ it "sets the value returned by MSpec.current to nil" do
+ MSpec.register_current :a
+ MSpec.current.should_not be_nil
+ MSpec.clear_current
+ MSpec.current.should be_nil
+ end
+end
+
+describe MSpec, ".current" do
+ before :each do
+ MSpec.clear_current
+ end
+
+ it "returns nil if no ContextState has been registered" do
+ MSpec.current.should be_nil
+ end
+
+ it "returns the most recently registered ContextState" do
+ first = ContextState.new ""
+ second = ContextState.new ""
+ MSpec.register_current first
+ MSpec.current.should == first
+ MSpec.register_current second
+ MSpec.current.should == second
+ end
+end
+
+describe MSpec, ".actions" do
+ before :each do
+ MSpec.store :start, []
+ ScratchPad.record []
+ start_one = double("one")
+ start_one.stub(:start).and_return { ScratchPad << :one }
+ start_two = double("two")
+ start_two.stub(:start).and_return { ScratchPad << :two }
+ MSpec.register :start, start_one
+ MSpec.register :start, start_two
+ end
+
+ it "does not attempt to run any actions if none have been registered" do
+ MSpec.store :finish, nil
+ lambda { MSpec.actions :finish }.should_not raise_error
+ end
+
+ it "runs each action registered as a start action" do
+ MSpec.actions :start
+ ScratchPad.recorded.should == [:one, :two]
+ end
+end
+
+describe MSpec, ".mode?" do
+ before :each do
+ MSpec.clear_modes
+ end
+
+ it "returns true if the mode has been set" do
+ MSpec.mode?(:verify).should == false
+ MSpec.register_mode :verify
+ MSpec.mode?(:verify).should == true
+ end
+end
+
+describe MSpec, ".clear_modes" do
+ it "clears all registered modes" do
+ MSpec.register_mode(:pretend)
+ MSpec.register_mode(:verify)
+
+ MSpec.mode?(:pretend).should == true
+ MSpec.mode?(:verify).should == true
+
+ MSpec.clear_modes
+
+ MSpec.mode?(:pretend).should == false
+ MSpec.mode?(:verify).should == false
+ end
+end
+
+describe MSpec, ".guarded?" do
+ before :each do
+ MSpec.instance_variable_set :@guarded, []
+ end
+
+ it "returns false if no guard has run" do
+ MSpec.guarded?.should == false
+ end
+
+ it "returns true if a single guard has run" do
+ MSpec.guard
+ MSpec.guarded?.should == true
+ end
+
+ it "returns true if more than one guard has run" do
+ MSpec.guard
+ MSpec.guard
+ MSpec.guarded?.should == true
+ end
+
+ it "returns true until all guards have finished" do
+ MSpec.guard
+ MSpec.guard
+ MSpec.guarded?.should == true
+ MSpec.unguard
+ MSpec.guarded?.should == true
+ MSpec.unguard
+ MSpec.guarded?.should == false
+ end
+end
+
+describe MSpec, ".describe" do
+ before :each do
+ MSpec.clear_current
+ @cs = ContextState.new ""
+ ContextState.stub(:new).and_return(@cs)
+ MSpec.stub(:current).and_return(nil)
+ MSpec.stub(:register_current)
+ end
+
+ it "creates a new ContextState for the block" do
+ ContextState.should_receive(:new).and_return(@cs)
+ MSpec.describe(Object) { }
+ end
+
+ it "accepts an optional second argument" do
+ ContextState.should_receive(:new).and_return(@cs)
+ MSpec.describe(Object, "msg") { }
+ end
+
+ it "registers the newly created ContextState" do
+ MSpec.should_receive(:register_current).with(@cs).twice
+ MSpec.describe(Object) { }
+ end
+
+ it "invokes the ContextState#describe method" do
+ prc = lambda { }
+ @cs.should_receive(:describe).with(&prc)
+ MSpec.describe(Object, "msg", &prc)
+ end
+end
+
+describe MSpec, ".process" do
+ before :each do
+ MSpec.stub(:files)
+ MSpec.store :start, []
+ MSpec.store :finish, []
+ STDOUT.stub(:puts)
+ end
+
+ it "prints the RUBY_DESCRIPTION" do
+ STDOUT.should_receive(:puts).with(RUBY_DESCRIPTION)
+ MSpec.process
+ end
+
+ it "calls all start actions" do
+ start = double("start")
+ start.stub(:start).and_return { ScratchPad.record :start }
+ MSpec.register :start, start
+ MSpec.process
+ ScratchPad.recorded.should == :start
+ end
+
+ it "calls all finish actions" do
+ finish = double("finish")
+ finish.stub(:finish).and_return { ScratchPad.record :finish }
+ MSpec.register :finish, finish
+ MSpec.process
+ ScratchPad.recorded.should == :finish
+ end
+
+ it "calls the files method" do
+ MSpec.should_receive(:files)
+ MSpec.process
+ end
+end
+
+describe MSpec, ".files" do
+ before :each do
+ MSpec.store :load, []
+ MSpec.store :unload, []
+ MSpec.register_files [:one, :two, :three]
+ Kernel.stub(:load)
+ end
+
+ it "calls load actions before each file" do
+ load = double("load")
+ load.stub(:load).and_return { ScratchPad.record :load }
+ MSpec.register :load, load
+ MSpec.files
+ ScratchPad.recorded.should == :load
+ end
+
+ it "shuffles the file list if .randomize? is true" do
+ MSpec.randomize
+ MSpec.should_receive(:shuffle)
+ MSpec.files
+ MSpec.randomize false
+ end
+
+ it "registers the current file" do
+ MSpec.should_receive(:store).with(:file, :one)
+ MSpec.should_receive(:store).with(:file, :two)
+ MSpec.should_receive(:store).with(:file, :three)
+ MSpec.files
+ end
+end
+
+describe MSpec, ".shuffle" do
+ before :each do
+ @base = (0..100).to_a
+ @list = @base.clone
+ MSpec.shuffle @list
+ end
+
+ it "does not alter the elements in the list" do
+ @base.each do |elt|
+ @list.should include(elt)
+ end
+ end
+
+ it "changes the order of the list" do
+ # obviously, this spec has a certain probability
+ # of failing. If it fails, run it again.
+ @list.should_not == @base
+ end
+end
+
+describe MSpec, ".tags_file" do
+ before :each do
+ MSpec.store :file, "path/to/spec/something/some_spec.rb"
+ MSpec.store :tags_patterns, nil
+ end
+
+ it "returns the default tags file for the current spec file" do
+ MSpec.tags_file.should == "path/to/spec/tags/something/some_tags.txt"
+ end
+
+ it "returns the tags file for the current spec file with custom tags_patterns" do
+ MSpec.register_tags_patterns [[/^(.*)\/spec/, '\1/tags'], [/_spec.rb/, "_tags.txt"]]
+ MSpec.tags_file.should == "path/to/tags/something/some_tags.txt"
+ end
+
+ it "performs multiple substitutions" do
+ MSpec.register_tags_patterns [
+ [%r(/spec/something/), "/spec/other/"],
+ [%r(/spec/), "/spec/tags/"],
+ [/_spec.rb/, "_tags.txt"]
+ ]
+ MSpec.tags_file.should == "path/to/spec/tags/other/some_tags.txt"
+ end
+
+ it "handles cases where no substitution is performed" do
+ MSpec.register_tags_patterns [[/nothing/, "something"]]
+ MSpec.tags_file.should == "path/to/spec/something/some_spec.rb"
+ end
+end
+
+describe MSpec, ".read_tags" do
+ before :each do
+ MSpec.stub(:tags_file).and_return(File.dirname(__FILE__) + '/tags.txt')
+ end
+
+ it "returns a list of tag instances for matching tag names found" do
+ one = SpecTag.new "fail(broken):Some#method? works"
+ MSpec.read_tags(["fail", "pass"]).should == [one]
+ end
+
+ it "returns [] if no tags names match" do
+ MSpec.read_tags("super").should == []
+ end
+end
+
+describe MSpec, ".read_tags" do
+ before :each do
+ @tag = SpecTag.new "fails:Some#method"
+ File.open(tmp("tags.txt", false), "w") do |f|
+ f.puts ""
+ f.puts @tag
+ f.puts ""
+ end
+ MSpec.stub(:tags_file).and_return(tmp("tags.txt", false))
+ end
+
+ it "does not return a tag object for empty lines" do
+ MSpec.read_tags(["fails"]).should == [@tag]
+ end
+end
+
+describe MSpec, ".write_tags" do
+ before :each do
+ FileUtils.cp File.dirname(__FILE__) + "/tags.txt", tmp("tags.txt", false)
+ MSpec.stub(:tags_file).and_return(tmp("tags.txt", false))
+ @tag1 = SpecTag.new "check(broken):Tag#rewrite works"
+ @tag2 = SpecTag.new "broken:Tag#write_tags fails"
+ end
+
+ after :all do
+ rm_r tmp("tags.txt", false)
+ end
+
+ it "overwrites the tags in the tag file" do
+ IO.read(tmp("tags.txt", false)).should == %[fail(broken):Some#method? works
+incomplete(20%):The#best method ever
+benchmark(0.01825):The#fastest method today
+extended():\"Multi-line\\ntext\\ntag\"
+]
+ MSpec.write_tags [@tag1, @tag2]
+ IO.read(tmp("tags.txt", false)).should == %[check(broken):Tag#rewrite works
+broken:Tag#write_tags fails
+]
+ end
+end
+
+describe MSpec, ".write_tag" do
+ before :each do
+ FileUtils.stub(:mkdir_p)
+ MSpec.stub(:tags_file).and_return(tmp("tags.txt", false))
+ @tag = SpecTag.new "fail(broken):Some#method works"
+ end
+
+ after :all do
+ rm_r tmp("tags.txt", false)
+ end
+
+ it "writes a tag to the tags file for the current spec file" do
+ MSpec.write_tag @tag
+ IO.read(tmp("tags.txt", false)).should == "fail(broken):Some#method works\n"
+ end
+
+ it "does not write a duplicate tag" do
+ File.open(tmp("tags.txt", false), "w") { |f| f.puts @tag }
+ MSpec.write_tag @tag
+ IO.read(tmp("tags.txt", false)).should == "fail(broken):Some#method works\n"
+ end
+end
+
+describe MSpec, ".delete_tag" do
+ before :each do
+ FileUtils.cp File.dirname(__FILE__) + "/tags.txt", tmp("tags.txt", false)
+ MSpec.stub(:tags_file).and_return(tmp("tags.txt", false))
+ @tag = SpecTag.new "fail(Comments don't matter):Some#method? works"
+ end
+
+ after :each do
+ rm_r tmp("tags.txt", false)
+ end
+
+ it "deletes the tag if it exists" do
+ MSpec.delete_tag(@tag).should == true
+ IO.read(tmp("tags.txt", false)).should == %[incomplete(20%):The#best method ever
+benchmark(0.01825):The#fastest method today
+extended():\"Multi-line\\ntext\\ntag\"
+]
+ end
+
+ it "deletes a tag with escaped newlines" do
+ MSpec.delete_tag(SpecTag.new('extended:"Multi-line\ntext\ntag"')).should == true
+ IO.read(tmp("tags.txt", false)).should == %[fail(broken):Some#method? works
+incomplete(20%):The#best method ever
+benchmark(0.01825):The#fastest method today
+]
+ end
+
+ it "does not change the tags file contents if the tag doesn't exist" do
+ @tag.tag = "failed"
+ MSpec.delete_tag(@tag).should == false
+ IO.read(tmp("tags.txt", false)).should == %[fail(broken):Some#method? works
+incomplete(20%):The#best method ever
+benchmark(0.01825):The#fastest method today
+extended():\"Multi-line\\ntext\\ntag\"
+]
+ end
+
+ it "deletes the tag file if it is empty" do
+ MSpec.delete_tag(@tag).should == true
+ MSpec.delete_tag(SpecTag.new("incomplete:The#best method ever")).should == true
+ MSpec.delete_tag(SpecTag.new("benchmark:The#fastest method today")).should == true
+ MSpec.delete_tag(SpecTag.new('extended:"Multi-line\ntext\ntag"')).should == true
+ File.exist?(tmp("tags.txt", false)).should == false
+ end
+end
+
+describe MSpec, ".delete_tags" do
+ before :each do
+ @tags = tmp("tags.txt", false)
+ FileUtils.cp File.dirname(__FILE__) + "/tags.txt", @tags
+ MSpec.stub(:tags_file).and_return(@tags)
+ end
+
+ it "deletes the tag file" do
+ MSpec.delete_tags
+ File.exist?(@tags).should be_false
+ end
+end
+
+describe MSpec, ".expectation" do
+ it "sets the flag that an expectation has been reported" do
+ MSpec.clear_expectations
+ MSpec.expectation?.should be_false
+ MSpec.expectation
+ MSpec.expectation?.should be_true
+ end
+end
+
+describe MSpec, ".expectation?" do
+ it "returns true if an expectation has been reported" do
+ MSpec.expectation
+ MSpec.expectation?.should be_true
+ end
+
+ it "returns false if an expectation has not been reported" do
+ MSpec.clear_expectations
+ MSpec.expectation?.should be_false
+ end
+end
+
+describe MSpec, ".clear_expectations" do
+ it "clears the flag that an expectation has been reported" do
+ MSpec.expectation
+ MSpec.expectation?.should be_true
+ MSpec.clear_expectations
+ MSpec.expectation?.should be_false
+ end
+end
+
+describe MSpec, ".register_shared" do
+ it "stores a shared ContextState by description" do
+ parent = ContextState.new "container"
+ state = ContextState.new "shared"
+ state.parent = parent
+ prc = lambda { }
+ state.describe(&prc)
+ MSpec.register_shared(state)
+ MSpec.retrieve(:shared)["shared"].should == state
+ end
+end
+
+describe MSpec, ".retrieve_shared" do
+ it "retrieves the shared ContextState matching description" do
+ state = ContextState.new ""
+ MSpec.retrieve(:shared)["shared"] = state
+ MSpec.retrieve_shared(:shared).should == state
+ end
+end
diff --git a/spec/mspec/spec/runner/shared_spec.rb b/spec/mspec/spec/runner/shared_spec.rb
new file mode 100644
index 0000000000..b91800b7db
--- /dev/null
+++ b/spec/mspec/spec/runner/shared_spec.rb
@@ -0,0 +1,90 @@
+require 'spec_helper'
+require 'mspec/runner/shared'
+require 'mspec/runner/context'
+require 'mspec/runner/example'
+
+describe Object, "#it_behaves_like" do
+ before :each do
+ ScratchPad.clear
+
+ MSpec.setup_env
+
+ @state = ContextState.new "Top level"
+ @state.instance_variable_set :@parsed, true
+ @state.singleton_class.send(:public, :it_behaves_like)
+
+ @shared = ContextState.new :shared_spec, :shared => true
+ MSpec.stub(:retrieve_shared).and_return(@shared)
+ end
+
+ it "creates @method set to the name of the aliased method" do
+ @shared.it("an example") { ScratchPad.record @method }
+ @state.it_behaves_like :shared_spec, :some_method
+ @state.process
+ ScratchPad.recorded.should == :some_method
+ end
+
+ it "creates @object if the passed object" do
+ object = Object.new
+ @shared.it("an example") { ScratchPad.record @object }
+ @state.it_behaves_like :shared_spec, :some_method, object
+ @state.process
+ ScratchPad.recorded.should == object
+ end
+
+ it "creates @object if the passed false" do
+ object = false
+ @shared.it("an example") { ScratchPad.record @object }
+ @state.it_behaves_like :shared_spec, :some_method, object
+ @state.process
+ ScratchPad.recorded.should == object
+ end
+
+ it "sends :it_should_behave_like" do
+ @state.should_receive(:it_should_behave_like)
+ @state.it_behaves_like :shared_spec, :some_method
+ end
+
+ describe "with multiple shared contexts" do
+ before :each do
+ @obj = Object.new
+ @obj2 = Object.new
+
+ @state2 = ContextState.new "Second top level"
+ @state2.instance_variable_set :@parsed, true
+ @state2.singleton_class.send(:public, :it_behaves_like)
+ end
+
+ it "ensures the shared spec state is distinct" do
+ @shared.it("an example") { ScratchPad.record [@method, @object] }
+
+ @state.it_behaves_like :shared_spec, :some_method, @obj
+
+ @state.process
+ ScratchPad.recorded.should == [:some_method, @obj]
+
+ @state2.it_behaves_like :shared_spec, :another_method, @obj2
+
+ @state2.process
+ ScratchPad.recorded.should == [:another_method, @obj2]
+ end
+
+ it "ensures the shared spec state is distinct for nested shared specs" do
+ nested = ContextState.new "nested context"
+ nested.instance_variable_set :@parsed, true
+ nested.parent = @shared
+
+ nested.it("another example") { ScratchPad.record [:shared, @method, @object] }
+
+ @state.it_behaves_like :shared_spec, :some_method, @obj
+
+ @state.process
+ ScratchPad.recorded.should == [:shared, :some_method, @obj]
+
+ @state2.it_behaves_like :shared_spec, :another_method, @obj2
+
+ @state2.process
+ ScratchPad.recorded.should == [:shared, :another_method, @obj2]
+ end
+ end
+end
diff --git a/spec/mspec/spec/runner/tag_spec.rb b/spec/mspec/spec/runner/tag_spec.rb
new file mode 100644
index 0000000000..db55a1b186
--- /dev/null
+++ b/spec/mspec/spec/runner/tag_spec.rb
@@ -0,0 +1,123 @@
+require 'spec_helper'
+require 'mspec/runner/tag'
+
+describe SpecTag do
+ it "accepts an optional string to parse into fields" do
+ tag = SpecTag.new "tag(comment):description"
+ tag.tag.should == "tag"
+ tag.comment.should == "comment"
+ tag.description.should == "description"
+ end
+end
+
+describe SpecTag, "#parse" do
+ before :each do
+ @tag = SpecTag.new
+ end
+
+ it "accepts 'tag(comment):description'" do
+ @tag.parse "tag(I'm real):Some#method returns a value"
+ @tag.tag.should == "tag"
+ @tag.comment.should == "I'm real"
+ @tag.description.should == "Some#method returns a value"
+ end
+
+ it "accepts 'tag:description'" do
+ @tag.parse "tag:Another#method"
+ @tag.tag.should == "tag"
+ @tag.comment.should == nil
+ @tag.description.should == "Another#method"
+ end
+
+ it "accepts 'tag():description'" do
+ @tag.parse "tag():Another#method"
+ @tag.tag.should == "tag"
+ @tag.comment.should == nil
+ @tag.description.should == "Another#method"
+ end
+
+ it "accepts 'tag:'" do
+ @tag.parse "tag:"
+ @tag.tag.should == "tag"
+ @tag.comment.should == nil
+ @tag.description.should == ""
+ end
+
+ it "accepts 'tag(bug:555):Another#method'" do
+ @tag.parse "tag(bug:555):Another#method"
+ @tag.tag.should == "tag"
+ @tag.comment.should == "bug:555"
+ @tag.description.should == "Another#method"
+ end
+
+ it "accepts 'tag(http://someplace.com/neato):Another#method'" do
+ @tag.parse "tag(http://someplace.com/neato):Another#method"
+ @tag.tag.should == "tag"
+ @tag.comment.should == "http://someplace.com/neato"
+ @tag.description.should == "Another#method"
+ end
+
+ it "accepts 'tag(comment):\"Multi-line\\ntext\"'" do
+ @tag.parse 'tag(comment):"Multi-line\ntext"'
+ @tag.tag.should == "tag"
+ @tag.comment.should == "comment"
+ @tag.description.should == "Multi-line\ntext"
+ end
+
+ it "ignores '#anything'" do
+ @tag.parse "# this could be a comment"
+ @tag.tag.should == nil
+ @tag.comment.should == nil
+ @tag.description.should == nil
+ end
+end
+
+describe SpecTag, "#to_s" do
+ it "formats itself as 'tag(comment):description'" do
+ txt = "tag(comment):description"
+ tag = SpecTag.new txt
+ tag.tag.should == "tag"
+ tag.comment.should == "comment"
+ tag.description.should == "description"
+ tag.to_s.should == txt
+ end
+
+ it "formats itself as 'tag:description" do
+ txt = "tag:description"
+ tag = SpecTag.new txt
+ tag.tag.should == "tag"
+ tag.comment.should == nil
+ tag.description.should == "description"
+ tag.to_s.should == txt
+ end
+
+ it "formats itself as 'tag(comment):\"multi-line\\ntext\\ntag\"'" do
+ txt = 'tag(comment):"multi-line\ntext\ntag"'
+ tag = SpecTag.new txt
+ tag.tag.should == "tag"
+ tag.comment.should == "comment"
+ tag.description.should == "multi-line\ntext\ntag"
+ tag.to_s.should == txt
+ end
+end
+
+describe SpecTag, "#==" do
+ it "returns true if the tags have the same fields" do
+ one = SpecTag.new "tag(this):unicorn"
+ two = SpecTag.new "tag(this):unicorn"
+ one.==(two).should == true
+ [one].==([two]).should == true
+ end
+end
+
+describe SpecTag, "#unescape" do
+ it "replaces \\n by LF when the description is quoted" do
+ tag = SpecTag.new 'tag:"desc with\nnew line"'
+ tag.description.should == "desc with\nnew line"
+ end
+
+ it "does not replaces \\n by LF when the description is not quoted " do
+ tag = SpecTag.new 'tag:desc with\nnew line'
+ tag.description.should == "desc with\\nnew line"
+ end
+end
diff --git a/spec/mspec/spec/runner/tags.txt b/spec/mspec/spec/runner/tags.txt
new file mode 100644
index 0000000000..f4eb6ad034
--- /dev/null
+++ b/spec/mspec/spec/runner/tags.txt
@@ -0,0 +1,4 @@
+fail(broken):Some#method? works
+incomplete(20%):The#best method ever
+benchmark(0.01825):The#fastest method today
+extended():"Multi-line\ntext\ntag"
diff --git a/spec/mspec/spec/spec_helper.rb b/spec/mspec/spec/spec_helper.rb
new file mode 100644
index 0000000000..0d497f6627
--- /dev/null
+++ b/spec/mspec/spec/spec_helper.rb
@@ -0,0 +1,55 @@
+require 'pp'
+require 'mspec/helpers/io'
+require 'mspec/helpers/scratch'
+
+# Remove this when MRI has intelligent warnings
+$VERBOSE = nil unless $VERBOSE
+
+class MOSConfig < Hash
+ def initialize
+ self[:loadpath] = []
+ self[:requires] = []
+ self[:flags] = []
+ self[:options] = []
+ self[:includes] = []
+ self[:excludes] = []
+ self[:patterns] = []
+ self[:xpatterns] = []
+ self[:tags] = []
+ self[:xtags] = []
+ self[:atags] = []
+ self[:astrings] = []
+ self[:target] = 'ruby'
+ self[:command] = nil
+ self[:ltags] = []
+ self[:files] = []
+ self[:launch] = []
+ end
+end
+
+def new_option
+ config = MOSConfig.new
+ return MSpecOptions.new("spec", 20, config), config
+end
+
+# Just to have an exception name output not be "Exception"
+class MSpecExampleError < Exception
+end
+
+def hide_deprecation_warnings
+ MSpec.stub(:deprecate)
+end
+
+def run_mspec(command, args)
+ cwd = Dir.pwd
+ command = " #{command}" unless command.start_with?('-')
+ cmd = "#{cwd}/bin/mspec#{command} -B spec/fixtures/config.mspec #{args}"
+ out = `#{cmd} 2>&1`
+ ret = $?
+ out = out.sub(/\A\$.+\n/, '') # Remove printed command line
+ out = out.sub(RUBY_DESCRIPTION, "RUBY_DESCRIPTION")
+ out = out.gsub(/\d+\.\d{6}/, "D.DDDDDD") # Specs total time
+ out = out.gsub(/\d{2}:\d{2}:\d{2}/, "00:00:00") # Progress bar time
+ out = out.gsub(cwd, "CWD")
+ return out, ret
+end
diff --git a/spec/mspec/spec/utils/deprecate_spec.rb b/spec/mspec/spec/utils/deprecate_spec.rb
new file mode 100644
index 0000000000..7fa60df26a
--- /dev/null
+++ b/spec/mspec/spec/utils/deprecate_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+require 'mspec/utils/deprecate'
+
+describe MSpec, "#deprecate" do
+ it "warns when using a deprecated method" do
+ warning = nil
+ $stderr.stub(:puts) { |str| warning = str }
+ MSpec.deprecate(:some_method, :other_method)
+ warning.should start_with(<<-EOS.chomp)
+
+some_method is deprecated, use other_method instead.
+from
+EOS
+ warning.should include(__FILE__)
+ warning.should include('8')
+ end
+end
diff --git a/spec/mspec/spec/utils/name_map_spec.rb b/spec/mspec/spec/utils/name_map_spec.rb
new file mode 100644
index 0000000000..d5d2cca84a
--- /dev/null
+++ b/spec/mspec/spec/utils/name_map_spec.rb
@@ -0,0 +1,175 @@
+require 'spec_helper'
+require 'mspec/utils/name_map'
+
+module NameMapSpecs
+ class A
+ A = self
+
+ def self.a; end
+ def a; end
+ def c; end
+
+ class B
+ def b; end
+ end
+ end
+
+ class Error
+ end
+
+ class Fixnum
+ def f; end
+ end
+
+ def self.n; end
+ def n; end
+end
+
+describe NameMap, "#exception?" do
+ before :each do
+ @map = NameMap.new
+ end
+
+ it "returns true if the constant is Errno" do
+ @map.exception?("Errno").should == true
+ end
+
+ it "returns true if the constant is a kind of Exception" do
+ @map.exception?("Errno::EBADF").should == true
+ @map.exception?("LoadError").should == true
+ @map.exception?("SystemExit").should == true
+ end
+
+ it "returns false if the constant is not a kind of Exception" do
+ @map.exception?("NameMapSpecs::Error").should == false
+ @map.exception?("NameMapSpecs").should == false
+ end
+
+ it "returns false if the constant does not exist" do
+ @map.exception?("Nonexistent").should == false
+ end
+end
+
+describe NameMap, "#class_or_module" do
+ before :each do
+ @map = NameMap.new true
+ end
+
+ it "returns the constant specified by the string" do
+ @map.class_or_module("NameMapSpecs").should == NameMapSpecs
+ end
+
+ it "returns the constant specified by the 'A::B' string" do
+ @map.class_or_module("NameMapSpecs::A").should == NameMapSpecs::A
+ end
+
+ it "returns nil if the constant is not a class or module" do
+ @map.class_or_module("Float::MAX").should == nil
+ end
+
+ it "returns nil if the constant is in the set of excluded constants" do
+ excluded = %w[
+ MSpecScript
+ MkSpec
+ NameMap
+ ]
+
+ excluded.each do |const|
+ @map.class_or_module(const).should == nil
+ end
+ end
+
+ it "returns nil if the constant does not exist" do
+ @map.class_or_module("Heaven").should == nil
+ @map.class_or_module("Hell").should == nil
+ @map.class_or_module("Bush::Brain").should == nil
+ end
+end
+
+describe NameMap, "#dir_name" do
+ before :each do
+ @map = NameMap.new
+ end
+
+ it "returns a directory name from the base name and constant" do
+ @map.dir_name("NameMapSpecs", 'spec/core').should == 'spec/core/namemapspecs'
+ end
+
+ it "returns a directory name from the components in the constants name" do
+ @map.dir_name("NameMapSpecs::A", 'spec').should == 'spec/namemapspecs/a'
+ @map.dir_name("NameMapSpecs::A::B", 'spec').should == 'spec/namemapspecs/a/b'
+ end
+
+ it "returns a directory name without 'class' for constants like TrueClass" do
+ @map.dir_name("TrueClass", 'spec').should == 'spec/true'
+ @map.dir_name("FalseClass", 'spec').should == 'spec/false'
+ end
+
+ it "returns 'exception' for the directory name of any Exception subclass" do
+ @map.dir_name("SystemExit", 'spec').should == 'spec/exception'
+ @map.dir_name("Errno::EBADF", 'spec').should == 'spec/exception'
+ end
+
+ it "returns 'class' for Class" do
+ @map.dir_name("Class", 'spec').should == 'spec/class'
+ end
+end
+
+# These specs do not cover all the mappings, but only describe how the
+# name is derived when the hash item maps to a single value, a hash with
+# a specific item, or a hash with a :default item.
+describe NameMap, "#file_name" do
+ before :each do
+ @map = NameMap.new
+ end
+
+ it "returns the name of the spec file based on the constant and method" do
+ @map.file_name("[]=", "Array").should == "element_set_spec.rb"
+ end
+
+ it "returns the name of the spec file based on the special entry for the method" do
+ @map.file_name("~", "Regexp").should == "match_spec.rb"
+ @map.file_name("~", "Integer").should == "complement_spec.rb"
+ end
+
+ it "returns the name of the spec file based on the default entry for the method" do
+ @map.file_name("<<", "NameMapSpecs").should == "append_spec.rb"
+ end
+
+ it "uses the last component of the constant to look up the method name" do
+ @map.file_name("^", "NameMapSpecs::Integer").should == "bit_xor_spec.rb"
+ end
+end
+
+describe NameMap, "#namespace" do
+ before :each do
+ @map = NameMap.new
+ end
+
+ it "prepends the module to the constant name" do
+ @map.namespace("SubModule", Integer).should == "SubModule::Integer"
+ end
+
+ it "does not prepend Object, Class, or Module to the constant name" do
+ @map.namespace("Object", String).should == "String"
+ @map.namespace("Module", Integer).should == "Integer"
+ @map.namespace("Class", Float).should == "Float"
+ end
+end
+
+describe NameMap, "#map" do
+ before :each do
+ @map = NameMap.new
+ end
+
+ it "flattens an object hierarchy into a single Hash" do
+ @map.map({}, [NameMapSpecs]).should == {
+ "NameMapSpecs." => ["n"],
+ "NameMapSpecs#" => ["n"],
+ "NameMapSpecs::A." => ["a"],
+ "NameMapSpecs::A#" => ["a", "c"],
+ "NameMapSpecs::A::B#" => ["b"],
+ "NameMapSpecs::Fixnum#" => ["f"]
+ }
+ end
+end
diff --git a/spec/mspec/spec/utils/options_spec.rb b/spec/mspec/spec/utils/options_spec.rb
new file mode 100644
index 0000000000..face909286
--- /dev/null
+++ b/spec/mspec/spec/utils/options_spec.rb
@@ -0,0 +1,1285 @@
+require 'spec_helper'
+require 'mspec/utils/options'
+require 'mspec/version'
+require 'mspec/guards/guard'
+require 'mspec/runner/mspec'
+require 'mspec/runner/formatters'
+
+describe MSpecOption, ".new" do
+ before :each do
+ @opt = MSpecOption.new("-a", "--bdc", "ARG", "desc", :block)
+ end
+
+ it "sets the short attribute" do
+ @opt.short.should == "-a"
+ end
+
+ it "sets the long attribute" do
+ @opt.long.should == "--bdc"
+ end
+
+ it "sets the arg attribute" do
+ @opt.arg.should == "ARG"
+ end
+
+ it "sets the description attribute" do
+ @opt.description.should == "desc"
+ end
+
+ it "sets the block attribute" do
+ @opt.block.should == :block
+ end
+end
+
+describe MSpecOption, "#arg?" do
+ it "returns true if arg attribute is not nil" do
+ MSpecOption.new(nil, nil, "ARG", nil, nil).arg?.should be_true
+ end
+
+ it "returns false if arg attribute is nil" do
+ MSpecOption.new(nil, nil, nil, nil, nil).arg?.should be_false
+ end
+end
+
+describe MSpecOption, "#match?" do
+ before :each do
+ @opt = MSpecOption.new("-a", "--bdc", "ARG", "desc", :block)
+ end
+
+ it "returns true if the argument matches the short option" do
+ @opt.match?("-a").should be_true
+ end
+
+ it "returns true if the argument matches the long option" do
+ @opt.match?("--bdc").should be_true
+ end
+
+ it "returns false if the argument matches neither the short nor long option" do
+ @opt.match?("-b").should be_false
+ @opt.match?("-abdc").should be_false
+ end
+end
+
+describe MSpecOptions, ".new" do
+ before :each do
+ @opt = MSpecOptions.new("cmd", 20, :config)
+ end
+
+ it "sets the banner attribute" do
+ @opt.banner.should == "cmd"
+ end
+
+ it "sets the config attribute" do
+ @opt.config.should == :config
+ end
+
+ it "sets the width attribute" do
+ @opt.width.should == 20
+ end
+
+ it "sets the default width attribute" do
+ MSpecOptions.new.width.should == 30
+ end
+end
+
+describe MSpecOptions, "#on" do
+ before :each do
+ @opt = MSpecOptions.new
+ end
+
+ it "adds a short option" do
+ @opt.should_receive(:add).with("-a", nil, nil, "desc", nil)
+ @opt.on("-a", "desc")
+ end
+
+ it "adds a short option taking an argument" do
+ @opt.should_receive(:add).with("-a", nil, "ARG", "desc", nil)
+ @opt.on("-a", "ARG", "desc")
+ end
+
+ it "adds a long option" do
+ @opt.should_receive(:add).with("-a", nil, nil, "desc", nil)
+ @opt.on("-a", "desc")
+ end
+
+ it "adds a long option taking an argument" do
+ @opt.should_receive(:add).with("-a", nil, nil, "desc", nil)
+ @opt.on("-a", "desc")
+ end
+
+ it "adds a short and long option" do
+ @opt.should_receive(:add).with("-a", nil, nil, "desc", nil)
+ @opt.on("-a", "desc")
+ end
+
+ it "adds a short and long option taking an argument" do
+ @opt.should_receive(:add).with("-a", nil, nil, "desc", nil)
+ @opt.on("-a", "desc")
+ end
+
+ it "raises MSpecOptions::OptionError if pass less than 2 arguments" do
+ lambda { @opt.on }.should raise_error(MSpecOptions::OptionError)
+ lambda { @opt.on "" }.should raise_error(MSpecOptions::OptionError)
+ end
+end
+
+describe MSpecOptions, "#add" do
+ before :each do
+ @opt = MSpecOptions.new "cmd", 20
+ @prc = lambda { }
+ end
+
+ it "adds documentation for an option" do
+ @opt.should_receive(:doc).with(" -t, --typo ARG Correct typo ARG")
+ @opt.add("-t", "--typo", "ARG", "Correct typo ARG", @prc)
+ end
+
+ it "leaves spaces in the documentation for a missing short option" do
+ @opt.should_receive(:doc).with(" --typo ARG Correct typo ARG")
+ @opt.add(nil, "--typo", "ARG", "Correct typo ARG", @prc)
+ end
+
+ it "handles a short option with argument but no long argument" do
+ @opt.should_receive(:doc).with(" -t ARG Correct typo ARG")
+ @opt.add("-t", nil, "ARG", "Correct typo ARG", @prc)
+ end
+
+ it "registers an option" do
+ option = MSpecOption.new "-t", "--typo", "ARG", "Correct typo ARG", @prc
+ MSpecOption.should_receive(:new).with(
+ "-t", "--typo", "ARG", "Correct typo ARG", @prc).and_return(option)
+ @opt.add("-t", "--typo", "ARG", "Correct typo ARG", @prc)
+ @opt.options.should == [option]
+ end
+end
+
+describe MSpecOptions, "#match?" do
+ before :each do
+ @opt = MSpecOptions.new
+ end
+
+ it "returns the MSpecOption instance matching the argument" do
+ @opt.on "-a", "--abdc", "desc"
+ option = @opt.match? "-a"
+ @opt.match?("--abdc").should be(option)
+ option.should be_kind_of(MSpecOption)
+ option.short.should == "-a"
+ option.long.should == "--abdc"
+ option.description.should == "desc"
+ end
+end
+
+describe MSpecOptions, "#process" do
+ before :each do
+ @opt = MSpecOptions.new
+ ScratchPad.clear
+ end
+
+ it "calls the on_extra block if the argument does not match any option" do
+ @opt.on_extra { ScratchPad.record :extra }
+ @opt.process ["-a"], "-a", "-a", nil
+ ScratchPad.recorded.should == :extra
+ end
+
+ it "returns the matching option" do
+ @opt.on "-a", "ARG", "desc"
+ option = @opt.process [], "-a", "-a", "ARG"
+ option.should be_kind_of(MSpecOption)
+ option.short.should == "-a"
+ option.arg.should == "ARG"
+ option.description.should == "desc"
+ end
+
+ it "raises an MSpecOptions::ParseError if arg is nil and there are no more entries in argv" do
+ @opt.on "-a", "ARG", "desc"
+ lambda { @opt.process [], "-a", "-a", nil }.should raise_error(MSpecOptions::ParseError)
+ end
+
+ it "fetches the argument for the option from argv if arg is nil" do
+ @opt.on("-a", "ARG", "desc") { |o| ScratchPad.record o }
+ @opt.process ["ARG"], "-a", "-a", nil
+ ScratchPad.recorded.should == "ARG"
+ end
+
+ it "calls the option's block" do
+ @opt.on("-a", "ARG", "desc") { ScratchPad.record :option }
+ @opt.process [], "-a", "-a", "ARG"
+ ScratchPad.recorded.should == :option
+ end
+
+ it "does not call the option's block if it is nil" do
+ @opt.on "-a", "ARG", "desc"
+ lambda { @opt.process [], "-a", "-a", "ARG" }.should_not raise_error
+ end
+end
+
+describe MSpecOptions, "#split" do
+ before :each do
+ @opt = MSpecOptions.new
+ end
+
+ it "breaks a string at the nth character" do
+ opt, arg, rest = @opt.split "-bdc", 2
+ opt.should == "-b"
+ arg.should == "dc"
+ rest.should == "dc"
+ end
+
+ it "returns nil for arg if there are no characters left" do
+ opt, arg, rest = @opt.split "-b", 2
+ opt.should == "-b"
+ arg.should == nil
+ rest.should == ""
+ end
+end
+
+describe MSpecOptions, "#parse" do
+ before :each do
+ @opt = MSpecOptions.new
+ @prc = lambda { ScratchPad.record :parsed }
+ @arg_prc = lambda { |o| ScratchPad.record [:parsed, o] }
+ ScratchPad.clear
+ end
+
+ it "parses a short option" do
+ @opt.on "-a", "desc", &@prc
+ @opt.parse ["-a"]
+ ScratchPad.recorded.should == :parsed
+ end
+
+ it "parse a long option" do
+ @opt.on "--abdc", "desc", &@prc
+ @opt.parse ["--abdc"]
+ ScratchPad.recorded.should == :parsed
+ end
+
+ it "parses a short option group" do
+ @opt.on "-a", "ARG", "desc", &@arg_prc
+ @opt.parse ["-a", "ARG"]
+ ScratchPad.recorded.should == [:parsed, "ARG"]
+ end
+
+ it "parses a short option with an argument" do
+ @opt.on "-a", "ARG", "desc", &@arg_prc
+ @opt.parse ["-a", "ARG"]
+ ScratchPad.recorded.should == [:parsed, "ARG"]
+ end
+
+ it "parses a short option with connected argument" do
+ @opt.on "-a", "ARG", "desc", &@arg_prc
+ @opt.parse ["-aARG"]
+ ScratchPad.recorded.should == [:parsed, "ARG"]
+ end
+
+ it "parses a long option with an argument" do
+ @opt.on "--abdc", "ARG", "desc", &@arg_prc
+ @opt.parse ["--abdc", "ARG"]
+ ScratchPad.recorded.should == [:parsed, "ARG"]
+ end
+
+ it "parses a long option with an '=' argument" do
+ @opt.on "--abdc", "ARG", "desc", &@arg_prc
+ @opt.parse ["--abdc=ARG"]
+ ScratchPad.recorded.should == [:parsed, "ARG"]
+ end
+
+ it "parses a short option group with the final option taking an argument" do
+ ScratchPad.record []
+ @opt.on("-a", "desc") { |o| ScratchPad << :a }
+ @opt.on("-b", "ARG", "desc") { |o| ScratchPad << [:b, o] }
+ @opt.parse ["-ab", "ARG"]
+ ScratchPad.recorded.should == [:a, [:b, "ARG"]]
+ end
+
+ it "parses a short option group with a connected argument" do
+ ScratchPad.record []
+ @opt.on("-a", "desc") { |o| ScratchPad << :a }
+ @opt.on("-b", "ARG", "desc") { |o| ScratchPad << [:b, o] }
+ @opt.on("-c", "desc") { |o| ScratchPad << :c }
+ @opt.parse ["-acbARG"]
+ ScratchPad.recorded.should == [:a, :c, [:b, "ARG"]]
+ end
+
+ it "returns the unprocessed entries" do
+ @opt.on "-a", "ARG", "desc", &@arg_prc
+ @opt.parse(["abdc", "-a", "ilny"]).should == ["abdc"]
+ end
+
+ it "calls the on_extra handler with unrecognized options" do
+ ScratchPad.record []
+ @opt.on_extra { |o| ScratchPad << o }
+ @opt.on "-a", "desc"
+ @opt.parse ["-a", "-b"]
+ ScratchPad.recorded.should == ["-b"]
+ end
+
+ it "does not attempt to call the block if it is nil" do
+ @opt.on "-a", "ARG", "desc"
+ @opt.parse(["-a", "ARG"]).should == []
+ end
+
+ it "raises MSpecOptions::ParseError if passed an unrecognized option" do
+ @opt.should_receive(:raise).with(MSpecOptions::ParseError, an_instance_of(String))
+ @opt.stub(:puts)
+ @opt.stub(:exit)
+ @opt.parse "-u"
+ end
+end
+
+describe MSpecOptions, "#banner=" do
+ before :each do
+ @opt = MSpecOptions.new
+ end
+
+ it "sets the banner attribute" do
+ @opt.banner.should == ""
+ @opt.banner = "banner"
+ @opt.banner.should == "banner"
+ end
+end
+
+describe MSpecOptions, "#width=" do
+ before :each do
+ @opt = MSpecOptions.new
+ end
+
+ it "sets the width attribute" do
+ @opt.width.should == 30
+ @opt.width = 20
+ @opt.width.should == 20
+ end
+end
+
+describe MSpecOptions, "#config=" do
+ before :each do
+ @opt = MSpecOptions.new
+ end
+
+ it "sets the config attribute" do
+ @opt.config.should be_nil
+ @opt.config = :config
+ @opt.config.should == :config
+ end
+end
+
+describe MSpecOptions, "#doc" do
+ before :each do
+ @opt = MSpecOptions.new "command"
+ end
+
+ it "adds text to be displayed with #to_s" do
+ @opt.doc "Some message"
+ @opt.doc "Another message"
+ @opt.to_s.should == <<-EOD
+command
+
+Some message
+Another message
+EOD
+ end
+end
+
+describe MSpecOptions, "#version" do
+ before :each do
+ @opt = MSpecOptions.new
+ ScratchPad.clear
+ end
+
+ it "installs a basic -v, --version option" do
+ @opt.should_receive(:puts)
+ @opt.should_receive(:exit)
+ @opt.version "1.0.0"
+ @opt.parse "-v"
+ end
+
+ it "accepts a block instead of using the default block" do
+ @opt.version("1.0.0") { |o| ScratchPad.record :version }
+ @opt.parse "-v"
+ ScratchPad.recorded.should == :version
+ end
+end
+
+describe MSpecOptions, "#help" do
+ before :each do
+ @opt = MSpecOptions.new
+ ScratchPad.clear
+ end
+
+ it "installs a basic -h, --help option" do
+ @opt.should_receive(:puts)
+ @opt.should_receive(:exit).with(1)
+ @opt.help
+ @opt.parse "-h"
+ end
+
+ it "accepts a block instead of using the default block" do
+ @opt.help { |o| ScratchPad.record :help }
+ @opt.parse "-h"
+ ScratchPad.recorded.should == :help
+ end
+end
+
+describe MSpecOptions, "#on_extra" do
+ before :each do
+ @opt = MSpecOptions.new
+ ScratchPad.clear
+ end
+
+ it "registers a block to be called when an option is not recognized" do
+ @opt.on_extra { ScratchPad.record :extra }
+ @opt.parse "-g"
+ ScratchPad.recorded.should == :extra
+ end
+end
+
+describe MSpecOptions, "#to_s" do
+ before :each do
+ @opt = MSpecOptions.new "command"
+ end
+
+ it "returns the banner and descriptive strings for all registered options" do
+ @opt.on "-t", "--this ARG", "Adds this ARG to the list"
+ @opt.to_s.should == <<-EOD
+command
+
+ -t, --this ARG Adds this ARG to the list
+EOD
+ end
+end
+
+describe "The -B, --config FILE option" do
+ before :each do
+ @options, @config = new_option
+ end
+
+ it "is enabled with #configure { }" do
+ @options.should_receive(:on).with("-B", "--config", "FILE",
+ an_instance_of(String))
+ @options.configure {}
+ end
+
+ it "calls the passed block" do
+ ["-B", "--config"].each do |opt|
+ ScratchPad.clear
+
+ @options.configure { |x| ScratchPad.record x }
+ @options.parse [opt, "file"]
+ ScratchPad.recorded.should == "file"
+ end
+ end
+end
+
+describe "The -C, --chdir DIR option" do
+ before :each do
+ @options, @config = new_option
+ @options.chdir
+ end
+
+ it "is enabled with #chdir" do
+ @options.should_receive(:on).with("-C", "--chdir", "DIR",
+ an_instance_of(String))
+ @options.chdir
+ end
+
+ it "changes the working directory to DIR" do
+ Dir.should_receive(:chdir).with("dir").twice
+ ["-C", "--chdir"].each do |opt|
+ @options.parse [opt, "dir"]
+ end
+ end
+end
+
+describe "The --prefix STR option" do
+ before :each do
+ @options, @config = new_option
+ end
+
+ it "is enabled with #prefix" do
+ @options.should_receive(:on).with("--prefix", "STR",
+ an_instance_of(String))
+ @options.prefix
+ end
+
+ it "sets the prefix config value" do
+ @options.prefix
+ @options.parse ["--prefix", "some/dir"]
+ @config[:prefix].should == "some/dir"
+ end
+end
+
+describe "The -t, --target TARGET option" do
+ before :each do
+ @options, @config = new_option
+ @options.targets
+ end
+
+ it "is enabled with #targets" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-t", "--target", "TARGET",
+ an_instance_of(String))
+ @options.targets
+ end
+
+ it "sets the target to 'ruby' and flags to verbose with TARGET 'r' or 'ruby'" do
+ ["-t", "--target"].each do |opt|
+ ["r", "ruby"].each do |t|
+ @config[:target] = nil
+ @options.parse [opt, t]
+ @config[:target].should == "ruby"
+ end
+ end
+ end
+
+ it "sets the target to 'jruby' with TARGET 'j' or 'jruby'" do
+ ["-t", "--target"].each do |opt|
+ ["j", "jruby"].each do |t|
+ @config[:target] = nil
+ @options.parse [opt, t]
+ @config[:target].should == "jruby"
+ end
+ end
+ end
+
+ it "sets the target to 'shotgun/rubinius' with TARGET 'x' or 'rubinius'" do
+ ["-t", "--target"].each do |opt|
+ ["x", "rubinius"].each do |t|
+ @config[:target] = nil
+ @options.parse [opt, t]
+ @config[:target].should == "./bin/rbx"
+ end
+ end
+ end
+
+ it "set the target to 'rbx' with TARGET 'rbx'" do
+ ["-t", "--target"].each do |opt|
+ ["X", "rbx"].each do |t|
+ @config[:target] = nil
+ @options.parse [opt, t]
+ @config[:target].should == "rbx"
+ end
+ end
+ end
+
+ it "sets the target to 'maglev' with TARGET 'm' or 'maglev'" do
+ ["-t", "--target"].each do |opt|
+ ["m", "maglev"].each do |t|
+ @config[:target] = nil
+ @options.parse [opt, t]
+ @config[:target].should == "maglev-ruby"
+ end
+ end
+ end
+
+ it "sets the target to 'topaz' with TARGET 't' or 'topaz'" do
+ ["-t", "--target"].each do |opt|
+ ["t", "topaz"].each do |t|
+ @config[:target] = nil
+ @options.parse [opt, t]
+ @config[:target].should == "topaz"
+ end
+ end
+ end
+
+ it "sets the target to TARGET" do
+ ["-t", "--target"].each do |opt|
+ @config[:target] = nil
+ @options.parse [opt, "whateva"]
+ @config[:target].should == "whateva"
+ end
+ end
+end
+
+describe "The -T, --target-opt OPT option" do
+ before :each do
+ @options, @config = new_option
+ @options.targets
+ end
+
+ it "is enabled with #targets" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-T", "--target-opt", "OPT",
+ an_instance_of(String))
+ @options.targets
+ end
+
+ it "adds OPT to flags" do
+ ["-T", "--target-opt"].each do |opt|
+ @config[:flags].delete "--whateva"
+ @options.parse [opt, "--whateva"]
+ @config[:flags].should include("--whateva")
+ end
+ end
+end
+
+describe "The -I, --include DIR option" do
+ before :each do
+ @options, @config = new_option
+ @options.targets
+ end
+
+ it "is enabled with #targets" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-I", "--include", "DIR",
+ an_instance_of(String))
+ @options.targets
+ end
+
+ it "add DIR to the load path" do
+ ["-I", "--include"].each do |opt|
+ @config[:loadpath].delete "-Ipackage"
+ @options.parse [opt, "package"]
+ @config[:loadpath].should include("-Ipackage")
+ end
+ end
+end
+
+describe "The -r, --require LIBRARY option" do
+ before :each do
+ @options, @config = new_option
+ @options.targets
+ end
+
+ it "is enabled with #targets" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-r", "--require", "LIBRARY",
+ an_instance_of(String))
+ @options.targets
+ end
+
+ it "adds LIBRARY to the requires list" do
+ ["-r", "--require"].each do |opt|
+ @config[:requires].delete "-rlibrick"
+ @options.parse [opt, "librick"]
+ @config[:requires].should include("-rlibrick")
+ end
+ end
+end
+
+describe "The -f, --format FORMAT option" do
+ before :each do
+ @options, @config = new_option
+ @options.formatters
+ end
+
+ it "is enabled with #formatters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-f", "--format", "FORMAT",
+ an_instance_of(String))
+ @options.formatters
+ end
+
+ it "sets the SpecdocFormatter with FORMAT 's' or 'specdoc'" do
+ ["-f", "--format"].each do |opt|
+ ["s", "specdoc"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == SpecdocFormatter
+ end
+ end
+ end
+
+ it "sets the HtmlFormatter with FORMAT 'h' or 'html'" do
+ ["-f", "--format"].each do |opt|
+ ["h", "html"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == HtmlFormatter
+ end
+ end
+ end
+
+ it "sets the DottedFormatter with FORMAT 'd', 'dot' or 'dotted'" do
+ ["-f", "--format"].each do |opt|
+ ["d", "dot", "dotted"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == DottedFormatter
+ end
+ end
+ end
+
+ it "sets the DescribeFormatter with FORMAT 'b' or 'describe'" do
+ ["-f", "--format"].each do |opt|
+ ["b", "describe"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == DescribeFormatter
+ end
+ end
+ end
+
+ it "sets the FileFormatter with FORMAT 'f', 'file'" do
+ ["-f", "--format"].each do |opt|
+ ["f", "file"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == FileFormatter
+ end
+ end
+ end
+
+ it "sets the UnitdiffFormatter with FORMAT 'u', 'unit', or 'unitdiff'" do
+ ["-f", "--format"].each do |opt|
+ ["u", "unit", "unitdiff"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == UnitdiffFormatter
+ end
+ end
+ end
+
+ it "sets the SummaryFormatter with FORMAT 'm' or 'summary'" do
+ ["-f", "--format"].each do |opt|
+ ["m", "summary"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == SummaryFormatter
+ end
+ end
+ end
+
+ it "sets the SpinnerFormatter with FORMAT 'a', '*', or 'spin'" do
+ ["-f", "--format"].each do |opt|
+ ["a", "*", "spin"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == SpinnerFormatter
+ end
+ end
+ end
+
+ it "sets the MethodFormatter with FORMAT 't' or 'method'" do
+ ["-f", "--format"].each do |opt|
+ ["t", "method"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == MethodFormatter
+ end
+ end
+ end
+
+ it "sets the YamlFormatter with FORMAT 'y' or 'yaml'" do
+ ["-f", "--format"].each do |opt|
+ ["y", "yaml"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == YamlFormatter
+ end
+ end
+ end
+
+ it "sets the JUnitFormatter with FORMAT 'j' or 'junit'" do
+ ["-f", "--format"].each do |opt|
+ ["j", "junit"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == JUnitFormatter
+ end
+ end
+ end
+end
+
+describe "The -o, --output FILE option" do
+ before :each do
+ @options, @config = new_option
+ @options.formatters
+ end
+
+ it "is enabled with #formatters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-o", "--output", "FILE",
+ an_instance_of(String))
+ @options.formatters
+ end
+
+ it "sets the output to FILE" do
+ ["-o", "--output"].each do |opt|
+ @config[:output] = nil
+ @options.parse [opt, "some/file"]
+ @config[:output].should == "some/file"
+ end
+ end
+end
+
+describe "The -e, --example STR" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-e", "--example", "STR",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds STR to the includes list" do
+ ["-e", "--example"].each do |opt|
+ @config[:includes] = []
+ @options.parse [opt, "this spec"]
+ @config[:includes].should include("this spec")
+ end
+ end
+end
+
+describe "The -E, --exclude STR" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-E", "--exclude", "STR",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds STR to the excludes list" do
+ ["-E", "--exclude"].each do |opt|
+ @config[:excludes] = []
+ @options.parse [opt, "this spec"]
+ @config[:excludes].should include("this spec")
+ end
+ end
+end
+
+describe "The -p, --pattern PATTERN" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-p", "--pattern", "PATTERN",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds PATTERN to the included patterns list" do
+ ["-p", "--pattern"].each do |opt|
+ @config[:patterns] = []
+ @options.parse [opt, "this spec"]
+ @config[:patterns].should include(/this spec/)
+ end
+ end
+end
+
+describe "The -P, --excl-pattern PATTERN" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-P", "--excl-pattern", "PATTERN",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds PATTERN to the excluded patterns list" do
+ ["-P", "--excl-pattern"].each do |opt|
+ @config[:xpatterns] = []
+ @options.parse [opt, "this spec"]
+ @config[:xpatterns].should include(/this spec/)
+ end
+ end
+end
+
+describe "The -g, --tag TAG" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-g", "--tag", "TAG",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds TAG to the included tags list" do
+ ["-g", "--tag"].each do |opt|
+ @config[:tags] = []
+ @options.parse [opt, "this spec"]
+ @config[:tags].should include("this spec")
+ end
+ end
+end
+
+describe "The -G, --excl-tag TAG" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-G", "--excl-tag", "TAG",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds TAG to the excluded tags list" do
+ ["-G", "--excl-tag"].each do |opt|
+ @config[:xtags] = []
+ @options.parse [opt, "this spec"]
+ @config[:xtags].should include("this spec")
+ end
+ end
+end
+
+describe "The -w, --profile FILE option" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-w", "--profile", "FILE",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds FILE to the included profiles list" do
+ ["-w", "--profile"].each do |opt|
+ @config[:profiles] = []
+ @options.parse [opt, "spec/profiles/rails.yaml"]
+ @config[:profiles].should include("spec/profiles/rails.yaml")
+ end
+ end
+end
+
+describe "The -W, --excl-profile FILE option" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-W", "--excl-profile", "FILE",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds FILE to the excluded profiles list" do
+ ["-W", "--excl-profile"].each do |opt|
+ @config[:xprofiles] = []
+ @options.parse [opt, "spec/profiles/rails.yaml"]
+ @config[:xprofiles].should include("spec/profiles/rails.yaml")
+ end
+ end
+end
+
+describe "The -Z, --dry-run option" do
+ before :each do
+ @options, @config = new_option
+ @options.pretend
+ end
+
+ it "is enabled with #pretend" do
+ @options.should_receive(:on).with("-Z", "--dry-run", an_instance_of(String))
+ @options.pretend
+ end
+
+ it "registers the MSpec pretend mode" do
+ MSpec.should_receive(:register_mode).with(:pretend).twice
+ ["-Z", "--dry-run"].each do |opt|
+ @options.parse opt
+ end
+ end
+end
+
+describe "The --unguarded option" do
+ before :each do
+ @options, @config = new_option
+ @options.unguarded
+ end
+
+ it "is enabled with #unguarded" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("--unguarded", an_instance_of(String))
+ @options.unguarded
+ end
+
+ it "registers the MSpec unguarded mode" do
+ MSpec.should_receive(:register_mode).with(:unguarded)
+ @options.parse "--unguarded"
+ end
+end
+
+describe "The --no-ruby_guard option" do
+ before :each do
+ @options, @config = new_option
+ @options.unguarded
+ end
+
+ it "is enabled with #unguarded" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("--no-ruby_bug", an_instance_of(String))
+ @options.unguarded
+ end
+
+ it "registers the MSpec no_ruby_bug mode" do
+ MSpec.should_receive(:register_mode).with(:no_ruby_bug)
+ @options.parse "--no-ruby_bug"
+ end
+end
+
+describe "The -H, --random option" do
+ before :each do
+ @options, @config = new_option
+ @options.randomize
+ end
+
+ it "is enabled with #randomize" do
+ @options.should_receive(:on).with("-H", "--random", an_instance_of(String))
+ @options.randomize
+ end
+
+ it "registers the MSpec randomize mode" do
+ MSpec.should_receive(:randomize).twice
+ ["-H", "--random"].each do |opt|
+ @options.parse opt
+ end
+ end
+end
+
+describe "The -R, --repeat option" do
+ before :each do
+ @options, @config = new_option
+ @options.repeat
+ end
+
+ it "is enabled with #repeat" do
+ @options.should_receive(:on).with("-R", "--repeat", "NUMBER", an_instance_of(String))
+ @options.repeat
+ end
+
+ it "registers the MSpec repeat mode" do
+ ["-R", "--repeat"].each do |opt|
+ MSpec.repeat = 1
+ @options.parse [opt, "10"]
+ repeat_count = 0
+ MSpec.repeat do
+ repeat_count += 1
+ end
+ repeat_count.should == 10
+ end
+ end
+end
+
+describe "The -V, --verbose option" do
+ before :each do
+ @options, @config = new_option
+ @options.verbose
+ end
+
+ it "is enabled with #verbose" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-V", "--verbose", an_instance_of(String))
+ @options.verbose
+ end
+
+ it "registers a verbose output object with MSpec" do
+ MSpec.should_receive(:register).with(:start, anything()).twice
+ MSpec.should_receive(:register).with(:load, anything()).twice
+ ["-V", "--verbose"].each do |opt|
+ @options.parse opt
+ end
+ end
+end
+
+describe "The -m, --marker MARKER option" do
+ before :each do
+ @options, @config = new_option
+ @options.verbose
+ end
+
+ it "is enabled with #verbose" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-m", "--marker", "MARKER",
+ an_instance_of(String))
+ @options.verbose
+ end
+
+ it "registers a marker output object with MSpec" do
+ MSpec.should_receive(:register).with(:load, anything()).twice
+ ["-m", "--marker"].each do |opt|
+ @options.parse [opt, ","]
+ end
+ end
+end
+
+describe "The --int-spec option" do
+ before :each do
+ @options, @config = new_option
+ @options.interrupt
+ end
+
+ it "is enabled with #interrupt" do
+ @options.should_receive(:on).with("--int-spec", an_instance_of(String))
+ @options.interrupt
+ end
+
+ it "sets the abort config option to false to only abort the running spec with ^C" do
+ @config[:abort] = true
+ @options.parse "--int-spec"
+ @config[:abort].should == false
+ end
+end
+
+describe "The -Y, --verify option" do
+ before :each do
+ @options, @config = new_option
+ @options.verify
+ end
+
+ it "is enabled with #interrupt" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-Y", "--verify", an_instance_of(String))
+ @options.verify
+ end
+
+ it "sets the MSpec mode to :verify" do
+ MSpec.should_receive(:register_mode).with(:verify).twice
+ ["-Y", "--verify"].each do |m|
+ @options.parse m
+ end
+ end
+end
+
+describe "The -O, --report option" do
+ before :each do
+ @options, @config = new_option
+ @options.verify
+ end
+
+ it "is enabled with #interrupt" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-O", "--report", an_instance_of(String))
+ @options.verify
+ end
+
+ it "sets the MSpec mode to :report" do
+ MSpec.should_receive(:register_mode).with(:report).twice
+ ["-O", "--report"].each do |m|
+ @options.parse m
+ end
+ end
+end
+
+describe "The --report-on GUARD option" do
+ before :all do
+ MSpec.stub(:register_mode)
+ end
+
+ before :each do
+ @options, @config = new_option
+ @options.verify
+
+ SpecGuard.clear_guards
+ end
+
+ after :each do
+ SpecGuard.clear_guards
+ end
+
+ it "is enabled with #interrupt" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("--report-on", "GUARD", an_instance_of(String))
+ @options.verify
+ end
+
+ it "sets the MSpec mode to :report_on" do
+ MSpec.should_receive(:register_mode).with(:report_on)
+ @options.parse ["--report-on", "ruby_bug"]
+ end
+
+ it "converts the guard name to a symbol" do
+ name = double("ruby_bug")
+ name.should_receive(:to_sym)
+ @options.parse ["--report-on", name]
+ end
+
+ it "saves the name of the guard" do
+ @options.parse ["--report-on", "ruby_bug"]
+ SpecGuard.guards.should == [:ruby_bug]
+ end
+end
+
+describe "The -K, --action-tag TAG option" do
+ before :each do
+ @options, @config = new_option
+ @options.action_filters
+ end
+
+ it "is enabled with #action_filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-K", "--action-tag", "TAG",
+ an_instance_of(String))
+ @options.action_filters
+ end
+
+ it "adds TAG to the list of tags that trigger actions" do
+ ["-K", "--action-tag"].each do |opt|
+ @config[:atags] = []
+ @options.parse [opt, "action-tag"]
+ @config[:atags].should include("action-tag")
+ end
+ end
+end
+
+describe "The -S, --action-string STR option" do
+ before :each do
+ @options, @config = new_option
+ @options.action_filters
+ end
+
+ it "is enabled with #action_filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-S", "--action-string", "STR",
+ an_instance_of(String))
+ @options.action_filters
+ end
+
+ it "adds STR to the list of spec descriptions that trigger actions" do
+ ["-S", "--action-string"].each do |opt|
+ @config[:astrings] = []
+ @options.parse [opt, "action-str"]
+ @config[:astrings].should include("action-str")
+ end
+ end
+end
+
+describe "The -d, --debug option" do
+ before :each do
+ @options, @config = new_option
+ @options.debug
+ end
+
+ after :each do
+ $MSPEC_DEBUG = nil
+ end
+
+ it "is enabled with #debug" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-d", "--debug", an_instance_of(String))
+ @options.debug
+ end
+
+ it "sets $MSPEC_DEBUG to true" do
+ ["-d", "--debug"].each do |opt|
+ $MSPEC_DEBUG.should_not be_true
+ @options.parse opt
+ $MSPEC_DEBUG.should be_true
+ $MSPEC_DEBUG = nil
+ end
+ end
+end
diff --git a/spec/mspec/spec/utils/script_spec.rb b/spec/mspec/spec/utils/script_spec.rb
new file mode 100644
index 0000000000..e3188ab5ff
--- /dev/null
+++ b/spec/mspec/spec/utils/script_spec.rb
@@ -0,0 +1,475 @@
+require 'spec_helper'
+require 'mspec/utils/script'
+require 'mspec/runner/mspec'
+require 'mspec/runner/filters'
+require 'mspec/runner/actions/filter'
+
+describe MSpecScript, ".config" do
+ it "returns a Hash" do
+ MSpecScript.config.should be_kind_of(Hash)
+ end
+end
+
+describe MSpecScript, ".set" do
+ it "sets the config hash key, value" do
+ MSpecScript.set :a, 10
+ MSpecScript.config[:a].should == 10
+ end
+end
+
+describe MSpecScript, ".get" do
+ it "gets the config hash value for a key" do
+ MSpecScript.set :a, 10
+ MSpecScript.get(:a).should == 10
+ end
+end
+
+describe MSpecScript, "#config" do
+ it "returns the MSpecScript config hash" do
+ MSpecScript.set :b, 5
+ MSpecScript.new.config[:b].should == 5
+ end
+
+ it "returns the MSpecScript config hash from subclasses" do
+ class MSSClass < MSpecScript; end
+ MSpecScript.set :b, 5
+ MSSClass.new.config[:b].should == 5
+ end
+end
+
+describe MSpecScript, "#load_default" do
+ before :all do
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ after :all do
+ $VERBOSE = @verbose
+ end
+
+ before :each do
+ @version = RUBY_VERSION
+ if Object.const_defined? :RUBY_ENGINE
+ @engine = Object.const_get :RUBY_ENGINE
+ end
+ @script = MSpecScript.new
+ MSpecScript.stub(:new).and_return(@script)
+ end
+
+ after :each do
+ Object.const_set :RUBY_VERSION, @version
+ Object.const_set :RUBY_ENGINE, @engine if @engine
+ end
+
+ it "attempts to load 'default.mspec'" do
+ @script.stub(:try_load)
+ @script.should_receive(:try_load).with('default.mspec').and_return(true)
+ @script.load_default
+ end
+
+ it "attempts to load a config file based on RUBY_ENGINE and RUBY_VERSION" do
+ Object.const_set :RUBY_ENGINE, "ybur"
+ Object.const_set :RUBY_VERSION, "1.8.9"
+ default = "ybur.1.8.mspec"
+ @script.should_receive(:try_load).with('default.mspec').and_return(false)
+ @script.should_receive(:try_load).with(default)
+ @script.should_receive(:try_load).with('ybur.mspec')
+ @script.load_default
+ end
+end
+
+describe MSpecScript, ".main" do
+ before :each do
+ @script = double("MSpecScript").as_null_object
+ MSpecScript.stub(:new).and_return(@script)
+ # Do not require full mspec as it would conflict with RSpec
+ MSpecScript.should_receive(:require).with('mspec')
+ end
+
+ it "creates an instance of MSpecScript" do
+ MSpecScript.should_receive(:new).and_return(@script)
+ MSpecScript.main
+ end
+
+ it "attempts to load the default config" do
+ @script.should_receive(:load_default)
+ MSpecScript.main
+ end
+
+ it "attempts to load the '~/.mspecrc' script" do
+ @script.should_receive(:try_load).with('~/.mspecrc')
+ MSpecScript.main
+ end
+
+ it "calls the #options method on the script" do
+ @script.should_receive(:options)
+ MSpecScript.main
+ end
+
+ it "calls the #signals method on the script" do
+ @script.should_receive(:signals)
+ MSpecScript.main
+ end
+
+ it "calls the #register method on the script" do
+ @script.should_receive(:register)
+ MSpecScript.main
+ end
+
+ it "calls the #setup_env method on the script" do
+ @script.should_receive(:setup_env)
+ MSpecScript.main
+ end
+
+ it "calls the #run method on the script" do
+ @script.should_receive(:run)
+ MSpecScript.main
+ end
+end
+
+describe MSpecScript, "#initialize" do
+ before :each do
+ @config = MSpecScript.new.config
+ end
+
+ it "sets the default config values" do
+ @config[:formatter].should == nil
+ @config[:includes].should == []
+ @config[:excludes].should == []
+ @config[:patterns].should == []
+ @config[:xpatterns].should == []
+ @config[:tags].should == []
+ @config[:xtags].should == []
+ @config[:atags].should == []
+ @config[:astrings].should == []
+ @config[:abort].should == true
+ @config[:config_ext].should == '.mspec'
+ end
+end
+
+describe MSpecScript, "#load" do
+ before :each do
+ File.stub(:exist?).and_return(false)
+ @script = MSpecScript.new
+ @file = "default.mspec"
+ @base = "default"
+ end
+
+ it "attempts to locate the file through the expanded path name" do
+ File.should_receive(:expand_path).with(@file, ".").and_return(@file)
+ File.should_receive(:exist?).with(@file).and_return(true)
+ Kernel.should_receive(:load).with(@file).and_return(:loaded)
+ @script.load(@file).should == :loaded
+ end
+
+ it "appends config[:config_ext] to the name and attempts to locate the file through the expanded path name" do
+ File.should_receive(:expand_path).with(@base, ".").and_return(@base)
+ File.should_receive(:expand_path).with(@base, "spec").and_return(@base)
+ File.should_receive(:expand_path).with(@file, ".").and_return(@file)
+ File.should_receive(:exist?).with(@base).and_return(false)
+ File.should_receive(:exist?).with(@file).and_return(true)
+ Kernel.should_receive(:load).with(@file).and_return(:loaded)
+ @script.load(@base).should == :loaded
+ end
+
+ it "attempts to locate the file in '.'" do
+ path = File.expand_path @file, "."
+ File.should_receive(:exist?).with(path).and_return(true)
+ Kernel.should_receive(:load).with(path).and_return(:loaded)
+ @script.load(@file).should == :loaded
+ end
+
+ it "appends config[:config_ext] to the name and attempts to locate the file in '.'" do
+ path = File.expand_path @file, "."
+ File.should_receive(:exist?).with(path).and_return(true)
+ Kernel.should_receive(:load).with(path).and_return(:loaded)
+ @script.load(@base).should == :loaded
+ end
+
+ it "attempts to locate the file in 'spec'" do
+ path = File.expand_path @file, "spec"
+ File.should_receive(:exist?).with(path).and_return(true)
+ Kernel.should_receive(:load).with(path).and_return(:loaded)
+ @script.load(@file).should == :loaded
+ end
+
+ it "appends config[:config_ext] to the name and attempts to locate the file in 'spec'" do
+ path = File.expand_path @file, "spec"
+ File.should_receive(:exist?).with(path).and_return(true)
+ Kernel.should_receive(:load).with(path).and_return(:loaded)
+ @script.load(@base).should == :loaded
+ end
+
+ it "loads a given file only once" do
+ path = File.expand_path @file, "spec"
+ File.should_receive(:exist?).with(path).and_return(true)
+ Kernel.should_receive(:load).once.with(path).and_return(:loaded)
+ @script.load(@base).should == :loaded
+ @script.load(@base).should == true
+ end
+end
+
+describe MSpecScript, "#custom_options" do
+ before :each do
+ @script = MSpecScript.new
+ end
+
+ after :each do
+ end
+
+ it "prints 'None'" do
+ options = double("options")
+ options.should_receive(:doc).with(" No custom options registered")
+ @script.custom_options options
+ end
+end
+
+describe MSpecScript, "#register" do
+ before :each do
+ @script = MSpecScript.new
+
+ @formatter = double("formatter").as_null_object
+ @script.config[:formatter] = @formatter
+ end
+
+ it "creates and registers the formatter" do
+ @formatter.should_receive(:new).and_return(@formatter)
+ @formatter.should_receive(:register)
+ @script.register
+ end
+
+ it "does not register the formatter if config[:formatter] is false" do
+ @script.config[:formatter] = false
+ @script.register
+ end
+
+ it "calls #custom_register" do
+ @script.should_receive(:custom_register)
+ @script.register
+ end
+
+ it "registers :formatter with the formatter instance" do
+ @formatter.stub(:new).and_return(@formatter)
+ MSpec.should_receive(:store).with(:formatter, @formatter)
+ @script.register
+ end
+
+ it "does not register :formatter if config[:formatter] is false" do
+ @script.config[:formatter] = false
+ MSpec.should_not_receive(:store)
+ @script.register
+ end
+end
+
+describe MSpecScript, "#register" do
+ before :each do
+ @script = MSpecScript.new
+
+ @formatter = double("formatter").as_null_object
+ @script.config[:formatter] = @formatter
+
+ @filter = double("filter")
+ @filter.should_receive(:register)
+
+ @ary = ["some", "spec"]
+ end
+
+ it "creates and registers a MatchFilter for include specs" do
+ MatchFilter.should_receive(:new).with(:include, *@ary).and_return(@filter)
+ @script.config[:includes] = @ary
+ @script.register
+ end
+
+ it "creates and registers a MatchFilter for excluded specs" do
+ MatchFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter)
+ @script.config[:excludes] = @ary
+ @script.register
+ end
+
+ it "creates and registers a RegexpFilter for include specs" do
+ RegexpFilter.should_receive(:new).with(:include, *@ary).and_return(@filter)
+ @script.config[:patterns] = @ary
+ @script.register
+ end
+
+ it "creates and registers a RegexpFilter for excluded specs" do
+ RegexpFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter)
+ @script.config[:xpatterns] = @ary
+ @script.register
+ end
+
+ it "creates and registers a TagFilter for include specs" do
+ TagFilter.should_receive(:new).with(:include, *@ary).and_return(@filter)
+ @script.config[:tags] = @ary
+ @script.register
+ end
+
+ it "creates and registers a TagFilter for excluded specs" do
+ TagFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter)
+ @script.config[:xtags] = @ary
+ @script.register
+ end
+
+ it "creates and registers a ProfileFilter for include specs" do
+ ProfileFilter.should_receive(:new).with(:include, *@ary).and_return(@filter)
+ @script.config[:profiles] = @ary
+ @script.register
+ end
+
+ it "creates and registers a ProfileFilter for excluded specs" do
+ ProfileFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter)
+ @script.config[:xprofiles] = @ary
+ @script.register
+ end
+end
+
+describe MSpecScript, "#signals" do
+ before :each do
+ @script = MSpecScript.new
+ @abort = @script.config[:abort]
+ end
+
+ after :each do
+ @script.config[:abort] = @abort
+ end
+
+ it "traps the INT signal if config[:abort] is true" do
+ Signal.should_receive(:trap).with("INT")
+ @script.config[:abort] = true
+ @script.signals
+ end
+
+ it "does not trap the INT signal if config[:abort] is not true" do
+ Signal.should_not_receive(:trap).with("INT")
+ @script.config[:abort] = false
+ @script.signals
+ end
+end
+
+describe MSpecScript, "#entries" do
+ before :each do
+ @script = MSpecScript.new
+
+ File.stub(:realpath).and_return("name")
+ File.stub(:file?).and_return(false)
+ File.stub(:directory?).and_return(false)
+ end
+
+ it "returns the pattern in an array if it is a file" do
+ File.should_receive(:realpath).with("file").and_return("file/expanded.rb")
+ File.should_receive(:file?).with("file/expanded.rb").and_return(true)
+ @script.entries("file").should == ["file/expanded.rb"]
+ end
+
+ it "returns Dir['pattern/**/*_spec.rb'] if pattern is a directory" do
+ File.should_receive(:directory?).with("name").and_return(true)
+ File.stub(:realpath).and_return("name", "name/**/*_spec.rb")
+ Dir.should_receive(:[]).with("name/**/*_spec.rb").and_return(["dir1", "dir2"])
+ @script.entries("name").should == ["dir1", "dir2"]
+ end
+
+ it "aborts if pattern cannot be resolved to a file nor a directory" do
+ @script.should_receive(:abort)
+ @script.entries("pattern")
+ end
+
+ describe "with config[:prefix] set" do
+ before :each do
+ prefix = "prefix/dir"
+ @script.config[:prefix] = prefix
+ @name = prefix + "/name"
+ end
+
+ it "returns the pattern in an array if it is a file" do
+ name = "#{@name}.rb"
+ File.should_receive(:realpath).with(name).and_return(name)
+ File.should_receive(:file?).with(name).and_return(true)
+ @script.entries("name.rb").should == [name]
+ end
+
+ it "returns Dir['pattern/**/*_spec.rb'] if pattern is a directory" do
+ File.stub(:realpath).and_return(@name, @name+"/**/*_spec.rb")
+ File.should_receive(:directory?).with(@name).and_return(true)
+ Dir.should_receive(:[]).with(@name + "/**/*_spec.rb").and_return(["dir1", "dir2"])
+ @script.entries("name").should == ["dir1", "dir2"]
+ end
+
+ it "aborts if pattern cannot be resolved to a file nor a directory" do
+ @script.should_receive(:abort)
+ @script.entries("pattern")
+ end
+ end
+end
+
+describe MSpecScript, "#files" do
+ before :each do
+ @script = MSpecScript.new
+ end
+
+ it "accumlates the values returned by #entries" do
+ @script.should_receive(:entries).and_return(["file1"], ["file2"])
+ @script.files(["a", "b"]).should == ["file1", "file2"]
+ end
+
+ it "strips a leading '^' and removes the values returned by #entries" do
+ @script.should_receive(:entries).and_return(["file1"], ["file2"], ["file1"])
+ @script.files(["a", "b", "^a"]).should == ["file2"]
+ end
+
+ it "processes the array elements in order" do
+ @script.should_receive(:entries).and_return(["file1"], ["file1"], ["file2"])
+ @script.files(["^a", "a", "b"]).should == ["file1", "file2"]
+ end
+end
+
+describe MSpecScript, "#files" do
+ before :each do
+ MSpecScript.set :files, ["file1", "file2"]
+
+ @script = MSpecScript.new
+ end
+
+ after :each do
+ MSpecScript.config.delete :files
+ end
+
+ it "looks up items with leading ':' in the config object" do
+ @script.should_receive(:entries).and_return(["file1"], ["file2"])
+ @script.files([":files"]).should == ["file1", "file2"]
+ end
+
+ it "aborts if the config key is not set" do
+ @script.should_receive(:abort).with("Key :all_files not found in mspec config.")
+ @script.files([":all_files"])
+ end
+end
+
+describe MSpecScript, "#setup_env" do
+ before :each do
+ @script = MSpecScript.new
+ @options, @config = new_option
+ @script.stub(:config).and_return(@config)
+ end
+
+ after :each do
+ end
+
+ it "sets MSPEC_RUNNER = '1' in the environment" do
+ ENV["MSPEC_RUNNER"] = "0"
+ @script.setup_env
+ ENV["MSPEC_RUNNER"].should == "1"
+ end
+
+ it "sets RUBY_EXE = config[:target] in the environment" do
+ ENV["RUBY_EXE"] = nil
+ @script.setup_env
+ ENV["RUBY_EXE"].should == @config[:target]
+ end
+
+ it "sets RUBY_FLAGS = config[:flags] in the environment" do
+ ENV["RUBY_FLAGS"] = nil
+ @config[:flags] = ["-w", "-Q"]
+ @script.setup_env
+ ENV["RUBY_FLAGS"].should == "-w -Q"
+ end
+end
diff --git a/spec/mspec/spec/utils/version_spec.rb b/spec/mspec/spec/utils/version_spec.rb
new file mode 100644
index 0000000000..0b2d383c6d
--- /dev/null
+++ b/spec/mspec/spec/utils/version_spec.rb
@@ -0,0 +1,45 @@
+require 'spec_helper'
+require 'mspec/utils/version'
+
+describe SpecVersion, "#to_s" do
+ it "returns the string with which it was initialized" do
+ SpecVersion.new("1.8").to_s.should == "1.8"
+ SpecVersion.new("2.118.9").to_s.should == "2.118.9"
+ end
+end
+
+describe SpecVersion, "#to_str" do
+ it "returns the same string as #to_s" do
+ version = SpecVersion.new("2.118.9")
+ version.to_str.should == version.to_s
+ end
+end
+
+describe SpecVersion, "#to_i with ceil = false" do
+ it "returns an integer representation of the version string" do
+ SpecVersion.new("2.23.10").to_i.should == 1022310
+ end
+
+ it "replaces missing version parts with zeros" do
+ SpecVersion.new("1.8").to_i.should == 1010800
+ SpecVersion.new("1.8.6").to_i.should == 1010806
+ end
+end
+
+describe SpecVersion, "#to_i with ceil = true" do
+ it "returns an integer representation of the version string" do
+ SpecVersion.new("1.8.6", true).to_i.should == 1010806
+ end
+
+ it "fills in 9s for missing tiny values" do
+ SpecVersion.new("1.8", true).to_i.should == 1010899
+ SpecVersion.new("1.8.6", true).to_i.should == 1010806
+ end
+end
+
+describe SpecVersion, "#to_int" do
+ it "returns the same value as #to_i" do
+ version = SpecVersion.new("4.16.87")
+ version.to_int.should == version.to_i
+ end
+end
diff --git a/spec/mspec/tool/find.rb b/spec/mspec/tool/find.rb
new file mode 100755
index 0000000000..322b023f15
--- /dev/null
+++ b/spec/mspec/tool/find.rb
@@ -0,0 +1,10 @@
+#!/usr/bin/env ruby
+Dir.chdir('../rubyspec') do
+ regexp = Regexp.new(ARGV[0])
+ Dir.glob('**/*.rb') do |file|
+ contents = File.read(file)
+ if regexp =~ contents
+ puts file
+ end
+ end
+end
diff --git a/spec/mspec/tool/pull-latest-mspec-spec b/spec/mspec/tool/pull-latest-mspec-spec
new file mode 100755
index 0000000000..62c1f8fefa
--- /dev/null
+++ b/spec/mspec/tool/pull-latest-mspec-spec
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Assumes all commits have been synchronized to https://github.com/ruby/spec
+# See spec/mspec/tool/sync/sync-rubyspec.rb
+
+rm -rf spec/mspec
+git clone --depth 1 https://github.com/ruby/mspec.git spec/mspec
+commit=$(git -C spec/mspec log -n 1 --format='%h')
+rm -rf spec/mspec/.git
+rm -f spec/mspec/.travis.yml
+git add spec/mspec
+git commit -m "Update to ruby/mspec@${commit}"
+
+rm -rf spec/ruby
+git clone --depth 1 https://github.com/ruby/spec.git spec/ruby
+commit=$(git -C spec/ruby log -n 1 --format='%h')
+rm -rf spec/ruby/.git
+git add spec/ruby
+git commit -m "Update to ruby/spec@${commit}"
diff --git a/spec/mspec/tool/remove_old_guards.rb b/spec/mspec/tool/remove_old_guards.rb
new file mode 100644
index 0000000000..8b036d07f5
--- /dev/null
+++ b/spec/mspec/tool/remove_old_guards.rb
@@ -0,0 +1,41 @@
+# Remove old version guards in ruby/spec
+
+def dedent(line)
+ if line.start_with?(" ")
+ line[2..-1]
+ else
+ line
+ end
+end
+
+def remove_guards(guard, keep)
+ Dir["*/**/*.rb"].each do |file|
+ contents = File.read(file)
+ if contents =~ guard
+ puts file
+ lines = contents.lines.to_a
+ while first = lines.find_index { |line| line =~ guard }
+ indent = lines[first][/^(\s*)/, 1].length
+ last = (first+1...lines.size).find { |i|
+ space = lines[i][/^(\s*)end$/, 1] and space.length == indent
+ }
+ raise file unless last
+ if keep
+ lines[first..last] = lines[first+1..last-1].map { |l| dedent(l) }
+ else
+ if first > 0 and lines[first-1] == "\n"
+ first -= 1
+ elsif lines[last+1] == "\n"
+ last += 1
+ end
+ lines[first..last] = []
+ end
+ end
+ File.write file, lines.join
+ end
+ end
+end
+
+version = (ARGV[0] || "2.3")
+remove_guards(/ruby_version_is ["']#{version}["'] do/, true)
+remove_guards(/ruby_version_is ["'][0-9.]*["']...["']#{version}["'] do/, false)
diff --git a/spec/mspec/tool/sync/.gitignore b/spec/mspec/tool/sync/.gitignore
new file mode 100644
index 0000000000..e64f1e8542
--- /dev/null
+++ b/spec/mspec/tool/sync/.gitignore
@@ -0,0 +1,4 @@
+/jruby
+/rubinius
+/ruby
+/truffleruby
diff --git a/spec/mspec/tool/sync/sync-rubyspec.rb b/spec/mspec/tool/sync/sync-rubyspec.rb
new file mode 100644
index 0000000000..120a4ad64c
--- /dev/null
+++ b/spec/mspec/tool/sync/sync-rubyspec.rb
@@ -0,0 +1,229 @@
+IMPLS = {
+ truffleruby: {
+ git: "https://github.com/oracle/truffleruby.git",
+ from_commit: "f10ab6988d",
+ },
+ jruby: {
+ git: "https://github.com/jruby/jruby.git",
+ from_commit: "f10ab6988d",
+ },
+ rbx: {
+ git: "https://github.com/rubinius/rubinius.git",
+ },
+ mri: {
+ git: "https://github.com/ruby/ruby.git",
+ master: "trunk",
+ },
+}
+
+MSPEC = ARGV.delete('--mspec')
+
+MSPEC_REPO = File.expand_path("../../..", __FILE__)
+raise MSPEC_REPO if !Dir.exist?(MSPEC_REPO) or !Dir.exist?("#{MSPEC_REPO}/.git")
+
+# Assuming the rubyspec repo is a sibling of the mspec repo
+RUBYSPEC_REPO = File.expand_path("../rubyspec", MSPEC_REPO)
+raise RUBYSPEC_REPO unless Dir.exist?(RUBYSPEC_REPO)
+
+SOURCE_REPO = MSPEC ? MSPEC_REPO : RUBYSPEC_REPO
+
+NOW = Time.now
+
+BRIGHT_RED = "\e[31;1m"
+BRIGHT_YELLOW = "\e[33;1m"
+RESET = "\e[0m"
+
+class RubyImplementation
+ attr_reader :name
+
+ def initialize(name, data)
+ @name = name.to_s
+ @data = data
+ end
+
+ def git_url
+ @data[:git]
+ end
+
+ def default_branch
+ @data[:master] || "master"
+ end
+
+ def repo_name
+ File.basename(git_url, ".git")
+ end
+
+ def repo_org
+ File.basename(File.dirname(git_url))
+ end
+
+ def from_commit
+ from = @data[:from_commit]
+ "#{from}..." if from
+ end
+
+ def last_merge_message
+ message = @data[:merge_message] || "Update to ruby/spec@"
+ message.gsub!("ruby/spec", "ruby/mspec") if MSPEC
+ message
+ end
+
+ def prefix
+ MSPEC ? "spec/mspec" : "spec/ruby"
+ end
+
+ def rebased_branch
+ "#{@name}-rebased"
+ end
+end
+
+def sh(*args)
+ puts args.join(' ')
+ system(*args)
+ raise unless $?.success?
+end
+
+def branch?(name)
+ branches = `git branch`.sub('*', '').lines.map(&:strip)
+ branches.include?(name)
+end
+
+def update_repo(impl)
+ unless File.directory? impl.repo_name
+ sh "git", "clone", impl.git_url
+ end
+
+ Dir.chdir(impl.repo_name) do
+ puts Dir.pwd
+
+ sh "git", "checkout", impl.default_branch
+ sh "git", "pull"
+ end
+end
+
+def filter_commits(impl)
+ Dir.chdir(impl.repo_name) do
+ date = NOW.strftime("%F")
+ branch = "#{MSPEC ? :mspec : :specs}-#{date}"
+
+ unless branch?(branch)
+ sh "git", "checkout", "-b", branch
+ sh "git", "filter-branch", "-f", "--subdirectory-filter", impl.prefix, *impl.from_commit
+ sh "git", "push", "-f", SOURCE_REPO, "#{branch}:#{impl.name}"
+ end
+ end
+end
+
+def rebase_commits(impl)
+ Dir.chdir(SOURCE_REPO) do
+ sh "git", "checkout", "master"
+ sh "git", "pull"
+
+ rebased = impl.rebased_branch
+ if branch?(rebased)
+ last_commit = Time.at(Integer(`git log -n 1 --format='%ct' #{rebased}`))
+ days_since_last_commit = (NOW-last_commit) / 86400
+ if days_since_last_commit > 7
+ abort "#{BRIGHT_RED}#{rebased} exists but last commit is old (#{last_commit}), delete the branch if it was merged#{RESET}"
+ else
+ puts "#{BRIGHT_YELLOW}#{rebased} already exists, last commit on #{last_commit}, assuming it correct#{RESET}"
+ sh "git", "checkout", rebased
+ end
+ else
+ sh "git", "checkout", impl.name
+
+ if ENV["LAST_MERGE"]
+ last_merge = `git log -n 1 --format='%H %ct' #{ENV["LAST_MERGE"]}`
+ else
+ last_merge = `git log --grep='^#{impl.last_merge_message}' -n 1 --format='%H %ct'`
+ end
+ last_merge, commit_timestamp = last_merge.chomp.split(' ')
+
+ raise "Could not find last merge" unless last_merge
+ puts "Last merge is #{last_merge}"
+
+ commit_date = Time.at(Integer(commit_timestamp))
+ days_since_last_merge = (NOW-commit_date) / 86400
+ if days_since_last_merge > 60
+ raise "#{days_since_last_merge.floor} days since last merge, probably wrong commit"
+ end
+
+ puts "Rebasing..."
+ sh "git", "branch", "-D", rebased if branch?(rebased)
+ sh "git", "checkout", "-b", rebased, impl.name
+ sh "git", "rebase", "--onto", "master", last_merge
+ end
+ end
+end
+
+def test_new_specs
+ require "yaml"
+ Dir.chdir(SOURCE_REPO) do
+ versions = YAML.load_file(".travis.yml")
+ versions = versions["matrix"]["include"].map { |job| job["rvm"] }
+ versions.delete "ruby-head"
+ versions.delete "system"
+ min_version, max_version = versions.minmax
+
+ test_command = MSPEC ? "bundle exec rspec" : "../mspec/bin/mspec -j"
+
+ run_test = -> version {
+ command = "chruby #{version} && #{test_command}"
+ sh ENV["SHELL"], "-c", command
+ }
+
+ run_test[min_version]
+ run_test[max_version]
+ run_test["trunk"]
+ end
+end
+
+def verify_commits(impl)
+ puts
+ Dir.chdir(SOURCE_REPO) do
+ puts "Manually check commit messages:"
+ print "Press enter >"
+ STDIN.gets
+ sh "git", "log", "master..."
+ end
+end
+
+def fast_forward_master(impl)
+ Dir.chdir(SOURCE_REPO) do
+ sh "git", "checkout", "master"
+ sh "git", "merge", "--ff-only", "#{impl.name}-rebased"
+ sh "git", "branch", "--delete", "#{impl.name}-rebased"
+ end
+end
+
+def check_ci
+ puts
+ puts <<-EOS
+ Push to master, and check that the CI passes:
+ https://github.com/ruby/#{:m if MSPEC}spec/commits/master
+
+ EOS
+end
+
+def main(impls)
+ impls.each_pair do |impl, data|
+ impl = RubyImplementation.new(impl, data)
+ update_repo(impl)
+ filter_commits(impl)
+ rebase_commits(impl)
+ test_new_specs
+ verify_commits(impl)
+ fast_forward_master(impl)
+ check_ci
+ end
+end
+
+if ARGV == ["all"]
+ impls = IMPLS
+else
+ args = ARGV.map { |arg| arg.to_sym }
+ raise ARGV.to_s unless (args - IMPLS.keys).empty?
+ impls = IMPLS.select { |impl| args.include?(impl) }
+end
+
+main(impls)
diff --git a/spec/ruby/.gitignore b/spec/ruby/.gitignore
new file mode 100644
index 0000000000..3f1206a16e
--- /dev/null
+++ b/spec/ruby/.gitignore
@@ -0,0 +1,5 @@
+/Gemfile.lock
+/rubyspec_temp
+/ext
+/.ruby-version
+/.ruby-gemset
diff --git a/spec/ruby/.rubocop.yml b/spec/ruby/.rubocop.yml
new file mode 100644
index 0000000000..58529f15b2
--- /dev/null
+++ b/spec/ruby/.rubocop.yml
@@ -0,0 +1,98 @@
+inherit_from: .rubocop_todo.yml
+
+AllCops:
+ TargetRubyVersion: 2.4
+ DisplayCopNames: true
+ Exclude:
+ - command_line/fixtures/bad_syntax.rb
+ DisabledByDefault: true
+
+Layout/TrailingWhitespace:
+ Enabled: true
+
+Layout/TrailingBlankLines:
+ Enabled: true
+ Exclude:
+ - library/coverage/fixtures/some_class.rb
+
+Lint:
+ Enabled: true
+
+# {...} has higher precedence than do ... end, on purpose
+Lint/AmbiguousBlockAssociation:
+ Enabled: false
+
+Lint/AssignmentInCondition:
+ Enabled: false
+
+Lint/BooleanSymbol:
+ Enabled: false
+
+Lint/InterpolationCheck:
+ Enabled: false
+
+Lint/LiteralAsCondition:
+ Enabled: false
+
+Lint/UnneededRequireStatement:
+ Enabled: false
+
+Lint/UnneededSplatExpansion:
+ Enabled: false
+
+Lint/UnifiedInteger:
+ Enabled: false
+
+Lint/UnusedBlockArgument:
+ Enabled: false
+
+Lint/UnusedMethodArgument:
+ Enabled: false
+
+Lint/UselessAssignment:
+ Enabled: false
+
+Lint/UselessComparison:
+ Enabled: false
+
+Lint/Void:
+ Enabled: false
+
+Lint/EmptyExpression:
+ Exclude:
+ - 'language/**/*.rb'
+
+Lint/EmptyWhen:
+ Exclude:
+ - language/case_spec.rb
+ - optional/capi/spec_helper.rb
+
+Lint/FormatParameterMismatch:
+ Exclude:
+ - 'core/kernel/shared/sprintf.rb'
+ - 'core/string/modulo_spec.rb'
+
+Lint/NestedMethodDefinition:
+ Exclude:
+ - language/def_spec.rb
+ - language/fixtures/def.rb
+
+Lint/UnreachableCode:
+ Exclude:
+ - 'core/enumerator/lazy/fixtures/classes.rb'
+ - 'core/kernel/catch_spec.rb'
+ - 'core/kernel/throw_spec.rb'
+ - 'language/break_spec.rb'
+ - 'language/fixtures/break.rb'
+ - 'language/fixtures/break_lambda_toplevel.rb'
+ - 'language/fixtures/break_lambda_toplevel_block.rb'
+ - 'language/fixtures/break_lambda_toplevel_method.rb'
+ - 'language/fixtures/return.rb'
+ - 'language/next_spec.rb'
+ - 'language/return_spec.rb'
+ - 'optional/capi/kernel_spec.rb'
+ - 'shared/kernel/raise.rb'
+
+Lint/UriRegexp:
+ Exclude:
+ - 'library/uri/regexp_spec.rb'
diff --git a/spec/ruby/.rubocop_todo.yml b/spec/ruby/.rubocop_todo.yml
new file mode 100644
index 0000000000..25c67ade02
--- /dev/null
+++ b/spec/ruby/.rubocop_todo.yml
@@ -0,0 +1,153 @@
+# This configuration was generated by
+# `rubocop --auto-gen-config`
+# on 2018-04-03 22:23:59 +0900 using RuboCop version 0.54.0.
+# The point is for the user to remove these configuration records
+# one by one as the offenses are removed from the code base.
+# Note that changes in the inspected code, or installation of new
+# versions of RuboCop, may require this file to be generated again.
+
+# Offense count: 2
+Lint/DuplicateCaseCondition:
+ Exclude:
+ - 'language/case_spec.rb'
+
+# Offense count: 6
+Lint/DuplicateMethods:
+ Exclude:
+ - 'core/array/fixtures/encoded_strings.rb'
+ - 'core/method/fixtures/classes.rb'
+ - 'core/module/fixtures/classes.rb'
+ - 'core/unboundmethod/fixtures/classes.rb'
+ - 'fixtures/class.rb'
+
+# Offense count: 5
+Lint/EnsureReturn:
+ Exclude:
+ - 'language/fixtures/ensure.rb'
+ - 'language/fixtures/return.rb'
+ - 'language/return_spec.rb'
+
+# Offense count: 10
+Lint/FloatOutOfRange:
+ Exclude:
+ - 'core/string/modulo_spec.rb'
+
+# Offense count: 29
+Lint/HandleExceptions:
+ Enabled: false
+
+# Offense count: 2
+Lint/ImplicitStringConcatenation:
+ Exclude:
+ - 'language/string_spec.rb'
+
+# Offense count: 4
+Lint/IneffectiveAccessModifier:
+ Exclude:
+ - 'core/kernel/fixtures/classes.rb'
+ - 'core/module/fixtures/classes.rb'
+ - 'language/fixtures/private.rb'
+
+# Offense count: 6
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: runtime_error, standard_error
+Lint/InheritException:
+ Exclude:
+ - 'core/enumerator/lazy/fixtures/classes.rb'
+ - 'core/exception/fixtures/common.rb'
+ - 'core/module/fixtures/autoload_ex1.rb'
+
+# Offense count: 5
+# Cop supports --auto-correct.
+Lint/LiteralInInterpolation:
+ Exclude:
+ - 'core/module/refine_spec.rb'
+ - 'language/defined_spec.rb'
+ - 'language/fixtures/squiggly_heredoc.rb'
+
+# Offense count: 16
+Lint/Loop:
+ Exclude:
+ - 'language/until_spec.rb'
+ - 'language/while_spec.rb'
+
+# Offense count: 8
+# Cop supports --auto-correct.
+Lint/MultipleCompare:
+ Exclude:
+ - 'language/precedence_spec.rb'
+
+# Offense count: 12
+Lint/ParenthesesAsGroupedExpression:
+ Exclude:
+ - 'core/string/fixtures/freeze_magic_comment.rb'
+ - 'language/block_spec.rb'
+ - 'language/fixtures/send.rb'
+ - 'language/method_spec.rb'
+
+# Offense count: 1
+# Cop supports --auto-correct.
+Lint/RedundantWithIndex:
+ Exclude:
+ - 'core/enumerator/with_index_spec.rb'
+
+# Offense count: 26
+Lint/RescueException:
+ Exclude:
+ - 'command_line/fixtures/debug_info.rb'
+ - 'core/dir/fileno_spec.rb'
+ - 'core/exception/cause_spec.rb'
+ - 'core/exception/no_method_error_spec.rb'
+ - 'core/kernel/fixtures/autoload_frozen.rb'
+ - 'core/module/autoload_spec.rb'
+ - 'core/mutex/sleep_spec.rb'
+ - 'core/process/euid_spec.rb'
+ - 'core/process/setsid_spec.rb'
+ - 'core/process/uid_spec.rb'
+ - 'core/thread/abort_on_exception_spec.rb'
+ - 'core/thread/shared/exit.rb'
+ - 'language/rescue_spec.rb'
+ - 'library/erb/filename_spec.rb'
+
+# Offense count: 2
+# Configuration parameters: IgnoreImplicitReferences.
+Lint/ShadowedArgument:
+ Exclude:
+ - 'language/fixtures/super.rb'
+
+# Offense count: 10
+Lint/ShadowingOuterLocalVariable:
+ Exclude:
+ - 'core/binding/local_variables_spec.rb'
+ - 'language/block_spec.rb'
+ - 'language/proc_spec.rb'
+
+# Offense count: 2
+# Cop supports --auto-correct.
+Lint/StringConversionInInterpolation:
+ Exclude:
+ - 'core/io/print_spec.rb'
+
+# Offense count: 9
+Lint/UnderscorePrefixedVariableName:
+ Exclude:
+ - 'core/io/pipe_spec.rb'
+ - 'core/io/popen_spec.rb'
+ - 'language/block_spec.rb'
+
+# Offense count: 7
+# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods.
+Lint/UselessAccessModifier:
+ Exclude:
+ - 'core/module/define_method_spec.rb'
+ - 'core/module/fixtures/classes.rb'
+ - 'core/module/module_function_spec.rb'
+ - 'core/module/private_class_method_spec.rb'
+ - 'language/fixtures/send.rb'
+
+# Offense count: 6186
+# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
+# URISchemes: http, https
+Metrics/LineLength:
+ Max: 588
diff --git a/spec/ruby/.travis.yml b/spec/ruby/.travis.yml
new file mode 100644
index 0000000000..35fea82656
--- /dev/null
+++ b/spec/ruby/.travis.yml
@@ -0,0 +1,30 @@
+sudo: false
+language: ruby
+install:
+ - git clone https://github.com/ruby/mspec.git ../mspec
+script:
+ - ../mspec/bin/mspec $MSPEC_OPTS
+matrix:
+ include:
+ - rvm: 2.5.3
+ env: MSPEC_OPTS="-R2 -ff"
+ - rvm: 2.3.8
+ - rvm: 2.4.5
+ - rvm: 2.5.3
+ env: CHECK_LEAKS=true
+ - rvm: ruby-head
+ - env: RUBOCOP=true
+ rvm: 2.4.5
+ script:
+ - gem install rubocop -v 0.54.0
+ - rubocop
+ allow_failures:
+ - rvm: ruby-head
+branches:
+ only:
+ - master
+ - /^try/
+notifications:
+ email:
+ on_success: change
+ on_failure: change
diff --git a/spec/ruby/CHANGES.before-2008-05-10 b/spec/ruby/CHANGES.before-2008-05-10
new file mode 100644
index 0000000000..18778bc146
--- /dev/null
+++ b/spec/ruby/CHANGES.before-2008-05-10
@@ -0,0 +1,17796 @@
+ Changelog
+===========
+
+This file contains the entire revision history of the specs from
+December 2006 onwards, when the spec project got started more or
+less officially by converting the remaining Test::Unit style tests
+in Rubinius to the spec style. The history is not preserved in the
+git repository history itself, so this data is here for reference.
+All the commit hashes are from the Rubinius repository.
+
+It still misses quite a few of the earlier, disparate specs and
+tests because up to that point the organisation was much looser
+and gathering an exhaustive accounting of the entire history of
+TDD/BDD would be time-consuming, particularly with the few full
+directory moves in there and such. All of the data is preserved
+in the Rubinius repository if someone is interested in that bit
+of history.
+
+Be aware that the history contains some Rubinius-specific specs
+by necessity. If you find any commits listed that were _solely_
+for Rubinius, feel free to strip them out.
+
+Thanks to everyone committing up to this point--over 2600 commits
+in just this incomplete version. Keep it up.
+
+
+
+ Revision History
+------------------
+
+
+commit 2b24a1e84c350810817885eeb6532f43c698a95c
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Fri May 9 16:45:07 2008 -0700
+
+ Fixed up pack for base64 and uuencode to be MUCH MUCH cleaner and 2x faster
+
+commit 022bc5dbfafcf1f9fd5e25820104718bd4d45661
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri May 9 23:51:47 2008 +0200
+
+ Share common specs for BigDecimal's #mult and #*.
+
+commit 414e7eedce9d0cea982e24f1031c407daccc648b
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri May 9 23:19:38 2008 +0200
+
+ New rubyspecs for BigDecimal#mult
+
+ * Verifies that proper signs are calculated when
+ zero is involved.
+
+commit 6883d7d0c67f7be84e7ea1703912452eaecaac6c
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri May 9 22:54:49 2008 +0200
+
+ New rubyspec for Module#new with block.
+
+commit f8bd3e34014a7351470685676b6b168abd787794
+Author: Phil Hagelberg <technomancy@gmail.com>
+Date: Fri May 9 12:53:00 2008 -0700
+
+ Added specs for OpenSSL::HMAC.hexdigest and .digest
+
+commit 686c28493d42b9c798aa791823395d1000423225
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri May 9 20:20:13 2008 +0200
+
+ Some more rubyspecs for BigDecimal's #floor and #ceil.
+
+commit aba022a6620ec8d3a09067e9677f0f9c5d8078ee
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri May 9 17:51:35 2008 +0200
+
+ New rubyspecs for BigDecimal's #floor and #ceil.
+
+commit e4d844ba5851a798b7acb684cf68fdcef353d13c
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu May 8 22:13:58 2008 -0700
+
+ Excluded stdlib specs from default CI run. Added spec/full.mspec.
+
+commit 6a133574617cb435ad1684f208430112ff6839f6
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Thu May 8 16:19:50 2008 -0700
+
+ String#unpack overhaul. NO extra methods littered through Fixnum/Integer/String. NO procs. More readable, but still messy.
+
+commit 11dd3ae2c4e0dd81304e85ba662db41196f1ce4c
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed May 7 23:59:31 2008 -0700
+
+ Fixed constant type clash for ModuleSpecs modules.
+
+commit 4e702d10b32fdba62cdeae476b8217019839c3b0
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed May 7 21:42:11 2008 -0700
+
+ Some specs for Kernel#__add_method__ and Module.__add_method__.
+
+commit 819649f24f59819be185b0562b94f9089f8c000c
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed May 7 14:48:01 2008 -0700
+
+ Added spec for Kernel#eval with binding from method defined by #eval.
+
+commit d73b17b88b6084fdf7cab764b0fbdd3b3882dd81
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed May 7 10:06:26 2008 -0700
+
+ Use literals in Bignum#to_f specs (alternate fix for #535).
+
+commit ee211770eb8792b3f58f78ff60eec6d5289caa20
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Wed May 7 02:38:00 2008 -0700
+
+ Added specs for big uncovered areas, still not 100%
+
+commit 7ce9bc2d7edc64f6886c3d34836bc0394414ed66
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Tue May 6 03:56:19 2008 -0700
+
+ Fixed typo
+
+commit af3407251ee0f287ec80232c354153af169636e4
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Tue May 6 22:01:23 2008 +1000
+
+ Fix bug in Debugger::Output.wrap
+
+commit d9322306ea70f2b847b0f806bdb13ea02f2d6b4d
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Mon May 5 13:09:32 2008 -0400
+
+ Fix some bugs in BigDecimal#/. More may yet lurk.
+
+commit 2f3a4cc14433858b13caa932c8a50c31e024c7e8
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Mon May 5 12:04:26 2008 -0500
+
+ Adding more specs for REXML::Element
+
+ * Covers REXML::Element#{add_attribute, add_attributes, add_namespace, add_text, clone, comments}
+
+commit 7db8c2b563ea474cf2db5fa14bb2a6345c8c469f
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Mon May 5 10:54:00 2008 -0500
+
+ One more case for YAML.load specs
+
+commit 098decdf510b05f82ff9a6cc6769cf478a3236ab
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sun May 4 22:29:35 2008 -0400
+
+ Define BigDecimal#ver.
+
+commit f6f1fe6a667570e4c1521649b964dca1352d1c32
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sun May 4 22:12:08 2008 -0400
+
+ BigDecimal#new: Make space between '-' and 'Infinity' unparsable, as per spec.
+
+commit 503aae7cdbb208da8f25080762e17f0866845c4d
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Mon May 5 06:19:40 2008 -0400
+
+ Method call parsing spec from Jim Kingdon with minor addition.
+
+ * Moved the SyntaxError producing code into an #eval because the file
+ cannot be compiled to run otherwise.
+
+commit 398d5de0a0ffaf746e39e5f6a6ded02483fd1842
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Mon May 5 02:26:39 2008 -0400
+
+ Spec for :match node, implicit Regexp matches against $_.
+
+ * Compiler and Language specs.
+
+commit 206cea31c6a93fe434948dcb79321e2c119edf21
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sat May 3 11:40:17 2008 -0400
+
+ Implement BigDecimal#power and #**, fix some bugs in #mult.
+
+commit a197099d9be6e48ad32480ae323302c83146147b
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat May 3 02:18:35 2008 -0400
+
+ Fixed a logic and syntax error in BigDecimal#mult specs.
+
+ * Removed some parentheses too.
+
+commit 081afd58a29ccd5025b806f53e9d7679b9296a7f
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sat May 3 02:25:45 2008 -0400
+
+ Make sure subclasses that implement their own Hash#default work (Merb)
+
+commit 203ca288175416fadb110b2aa9cdf8cfbf13215d
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sat May 3 01:49:35 2008 -0400
+
+ Specs and implementation for module include order (fixes abstract.rb)
+
+commit c788a9f2d9c4561a2837bbf78f68a6885d626917
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri May 2 18:14:32 2008 -0400
+
+ Implement BigDecimal#*, as well as #mult without precision support.
+
+commit 57d78528ff4cf249d906785ffbfdde1fda4aa3cc
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri May 2 17:36:15 2008 -0400
+
+ Implement BigDecimal#/ and #quo. Not perfect; still relies on #/.
+
+commit c42cc2cacc347d8284650c7046d4dadf94d7d4a5
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri May 2 16:13:07 2008 -0400
+
+ Fix a typo in specs.
+
+commit ae179b410665da18628f249e6796f1e07ab83763
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri May 2 16:10:43 2008 -0400
+
+ Get BigDecimal#floor basically working.
+ * The failing specs depend on #/, which isn't implemented yet.
+
+commit f8221117d174b91affe406c8089ed25e887232b3
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri May 2 16:06:32 2008 -0400
+
+ Fix bugs in BigDecimal#add and #+. This also affects #sub, #-, and #ceil.
+
+commit cdd196daf7643e846b7f3582b1e441b883e02aba
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri May 2 15:41:29 2008 -0400
+
+ More specs to fix bugs in BigDecimal#add and #+.
+
+commit c1c52a2a531b570fa1025d99e464d93c570cf59e
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri May 2 15:22:33 2008 -0400
+
+ Write another spec for BigDecimal#ceil.
+
+commit 71b65cdbfa5aae461fc52c997df9fca3bee9c8d5
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri May 2 14:47:34 2008 -0400
+
+ Write tests for a bug in BigDecimal#add and #+ where 0 + 1 = 0.1.
+
+commit 55988ef53879c1c489c570b3f37717365c7f8e2b
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sat May 3 01:04:11 2008 -0400
+
+ Fix use of alias keyword inside instance_eval
+
+commit d4011595a0077e91665f85410d458c57367cf50b
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri May 2 20:38:15 2008 +0200
+
+ Added news specs for BigDecimal#mult.
+
+commit b6771644d35b6b8f3c87f7f4461bcaba99cd976f
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri May 2 19:41:09 2008 +0200
+
+ More BigDecimal#divmod rubyspecs.
+
+ MRI-specific bug is hidden behind ruby-bug guard.
+
+commit 854a011324ce717cfd47ddec6389a9e9abb0db18
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri May 2 18:45:29 2008 +0200
+
+ New BigDecimal#divmod specs.
+
+commit b9806e0efb2a8e51d70f6d51733df7bed88152d9
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri May 2 16:45:10 2008 +0200
+
+ A couple of test cases for BigDecimal's #quo, #div, #/.
+
+commit 3cf6c1e03001ba1dda966e3392b665f5b08a1b9d
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri May 2 15:03:37 2008 +0200
+
+ More tests for BigDecimal#floor.
+
+commit b70023978562af89cf4349e14e9443adb37ecbbe
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu May 1 21:15:29 2008 -0400
+
+ Improved a spec description for String#index.
+
+ * The description looks exactly like we had the wrong implementation
+ relying on % 256 and someone wrote a spec to make sure that did not
+ happen. However, the description was more or less meaningless to
+ what was actually being specced.
+
+commit 6e6aa411ff4c7a837d5d4adb9ab893719cf9e122
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu May 1 10:48:20 2008 -0700
+
+ Fix a number of things to pass all def specs
+
+ This is the result of ping-pong between Evan and Wilson. It refactors
+ out enclosing_class from being used, and instead information is always
+ pulled directly from the StaticScope object. This lets us inject proper
+ scoping changes in ruby.
+
+commit 2db27aef88e2ca7752beba846d172ede276275e0
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Thu May 1 19:03:50 2008 +0200
+
+ Implemented Socket.unpack_sockaddr_un
+
+commit d515221698e02b52ed4661113d659744fbfae36f
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Thu May 1 18:18:32 2008 +0200
+
+ Forgot to update spec tags for TCPSocket.gethostbyname
+
+commit bf839a99c3a5b773b6b96c6d5a1fcc5056511e7a
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Thu May 1 14:03:30 2008 +0200
+
+ Implement File#mtime specs
+
+commit b8c713e6b972b464788c740b4283a5b4226c123c
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Thu May 1 13:55:36 2008 +0200
+
+ Implemented File.lchmod and initial specs
+
+commit 059c926d7280c2e7c9f8bf710c5aef70cde3e777
+Author: Adam Wiggins <adam@heroku.com>
+Date: Sun Apr 27 15:03:31 2008 -0700
+
+ IO.popen read/write pipes
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit d9a050aa45efd00a40395b7ac7ac069f4be1fd1c
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Thu May 1 16:25:18 2008 +1000
+
+ Spec fixes for Tuple#to_a
+
+commit 0b610359fbfe8137fdba95d90b659238168d6788
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Wed Apr 30 17:45:51 2008 -0400
+
+ Update spectags.
+
+commit 024ebfdf3fa9c54b8a81134edb52fe10b09e4b91
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Apr 30 22:56:44 2008 +0200
+
+ Added BigDecimal#divmod excludes.
+
+commit e12d21a90760df723c0f48265cb49a9c4463db7c
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Apr 30 20:51:06 2008 +0200
+
+ More tests for BigDecimal#divmod.
+
+commit 68cfef604f9b5411ca9e0349883bac4f59541f0d
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Wed Apr 30 16:47:31 2008 -0400
+
+ Make BigDecimal#finite? handle NaN correctly, and refactor accordingly.
+
+commit 5066bcb8881241caf6d13be625b32633bda6567e
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Wed Apr 30 15:01:12 2008 -0400
+
+ Make BigDecimal#<= and #>= pass Vladimir's new specs.
+
+commit 49601aff01c394fe2168f5f221a987be63a9ebc7
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Apr 30 20:20:18 2008 +0200
+
+ Various improvements to BigDecimal rubyspecs.
+
+ * Corrected comparison specs (properly add arrays there)
+ * New reminder specs
+ * New modulo and % specs
+ * Tagged rbx failures
+
+commit dd1700b747ba26b27eff0b249623aca559db06e1
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Apr 30 17:48:20 2008 +0200
+
+ More test cases for BigDecimal#modulo and #%.
+
+commit 8eb9dc1b0aee3587f4da8b9cbe306fd431159d79
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Apr 30 16:57:16 2008 +0200
+
+ New specs for BigDecimal#modulo and #%.
+
+commit 4a846f807fe2c4c12d8719bc5c9ccb4ab696aff9
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Tue Apr 29 15:29:34 2008 -0500
+
+ Fixes REXML::Element#namespaces specs
+
+ * Use sort on the arrays to make sure the specs pass on JRuby too.
+
+commit 823683a864072ef6a81e808dbf792dee45d29c52
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Tue Apr 29 14:54:08 2008 -0500
+
+ Adds more specs for REXML.
+
+ * Specs for REXML#{inspect, namespace, namespaces, prefixes, text and text=}.
+
+commit a11a10760ce92ee373e04a5445234521a27874cc
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Mon Apr 28 17:55:55 2008 -0400
+
+ Committing so we can bisect.
+
+commit df94214b1d132b02e3dd5b166d1c7c5cd5d50a21
+Author: Drew Olson <olsonas@gmail.com>
+Date: Mon Apr 28 19:21:07 2008 -0700
+
+ Added spec for Array#remove_outer_arrays
+
+commit ec4ece9c06b42c257b4ffce2cf319f0ad23f65e8
+Author: Drew Olson <olsonas@gmail.com>
+Date: Sun Apr 27 20:15:47 2008 -0500
+
+ Added more edge cases for recursive arrays to spec for File#join
+ * an empty array containing an empty array which contains a recursive array should return
+ '[...]' when File#join is called on it.
+
+commit 698a5d291cf63e56e9a3508a8850c77fa2c23430
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Mon Apr 28 16:17:05 2008 -0400
+
+ Implement BigDecimal#=== as alias of #eql?.
+
+commit 18f515e735eecc519be55a6e3253db7135a137ad
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Mon Apr 28 16:09:43 2008 -0400
+
+ Implement BigDecimal#sub.
+
+commit b331faa567dc1d98163c6447897221877cf756eb
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Mon Apr 28 16:04:06 2008 -0400
+
+ Implement BigDecimal#add.
+
+commit f3f94c9b53045ddde335981897e2f6087dab7ef2
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Mon Apr 28 12:01:41 2008 -0500
+
+ hack to fix DRb.start_service spec to at least test start_service
+
+commit 4c8d6d90c69615386e26c71633e242f4e1f19342
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Mon Apr 28 11:56:47 2008 -0500
+
+ spec for DRb.stop_service to see if it clears the socket correctly
+
+commit 03cb539f42f0b558fa29911c1dfc71ec5f2b183f
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Mon Apr 28 11:20:17 2008 -0500
+
+ Revert "Revert "Made DRb spec depend partially on PID so multiple runs don't clash.""
+
+ Apparently this is a supposed fix for concurrent spec runs, not for the spec failure
+
+ This reverts commit 08695d9a6940ab74f6eb8965e449a417002a42a6.
+
+commit 2172e2ac20b69a97c2ad66551b3620a43bfda700
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Mon Apr 28 02:14:18 2008 -0400
+
+ Make BigDecimal#exponent return Bignums as necessary, not just Fixnums.
+
+commit dc93d06163e80cdf89a67532654a850828119287
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Mon Apr 28 01:23:19 2008 -0400
+
+ Correct implementation of BigDecimal#+ and #-. There's still a lot of repetition to be factored out, but this algorithm is more correct than the last try.
+
+commit 1da58bb7f0afbba4f8412e06983304dc7d887ac9
+Author: Luis Lavena <luislavena@gmail.com>
+Date: Thu Apr 24 16:37:59 2008 -0300
+
+ Corrected small typo on File#join specs under Windows.
+
+commit b287619579ad11535722a2374b6f849d88fe9931
+Author: Drew Olson <olsonas@gmail.com>
+Date: Thu Apr 24 14:24:10 2008 -0700
+
+ Spec for File#join now describes correct behavior for arrays with recursive sub-arrays.
+
+commit 5830380895c0bec16c6af39d0f29d8d70268028d
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Sun Apr 27 14:53:47 2008 -0500
+
+ DRb.start_service spec fails because of a timing bug in DRb
+
+ See http://jira.codehaus.org/browse/JRUBY-2347
+
+commit 08695d9a6940ab74f6eb8965e449a417002a42a6
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Sun Apr 27 14:51:16 2008 -0500
+
+ Revert "Made DRb spec depend partially on PID so multiple runs don't clash."
+
+ The spec is designed for sane behavior, if Rubinius or the
+ implementation of DRb is causing problems then they should be
+ fixed, not the spec in this case. Fixing the spec will only
+ hide the bug.
+
+ See http://jira.codehaus.org/browse/JRUBY-2347 for more commentary on the problem.
+
+ This reverts commit f89bd8c6c425c9d9bcc3e589b8d3b05ce3ccbced.
+
+commit 94ba0884c8e7f398b6fe8d6736834f62f6a49815
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sun Apr 27 21:23:47 2008 +0200
+
+ More checks for BigDecimal#abs specs.
+
+commit 80932d25ca95e2e8c803d244a7636e3004525ade
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sun Apr 27 21:10:26 2008 +0200
+
+ More test cases for BigDecimal#finite? specs.
+
+commit 4b541ed23ccac65f6f4b2ef8aad56e9aa7a69e12
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sun Apr 27 21:04:08 2008 +0200
+
+ Added testcase for BigDecimal#infinite? for NaN.
+
+commit 4a1f39426fc60ae7c2ed0470259fa0752a46d030
+Author: Adam Wiggins <adam@heroku.com>
+Date: Sat Apr 26 22:57:09 2008 -0700
+
+ IO#write returns 0 when writing a blank string, to match behavior of MRI
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit 56c0088f9b075769933c8c87e3c2d256cff3a3e8
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sun Apr 27 00:28:47 2008 -0400
+
+ Typo.
+
+commit c11410654b9046cdb58dba1d116f58ce74f4c263
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sun Apr 27 00:24:32 2008 -0400
+
+ Finish implementing #@- and #infinite?. Update spectags, of course
+
+commit dc9f427ecb9d55559d800af70f9c1a3f2f2123b5
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sun Apr 27 00:07:31 2008 -0400
+
+ Amplify a comment.
+
+commit b9776b953ae67f2088e44b640145af464a1cf942
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sun Apr 27 00:02:48 2008 -0400
+
+ Get BigDecimal#+ working. I hate this algorithm, but it works without running out of memory.
+ * Update spec tags.
+
+commit b87ff5c22891f19ad0b956e7e02cc3a3d1adcc93
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sat Apr 26 14:27:00 2008 -0400
+
+ Implement BigDecimal#coerce.
+ * Update spectags.
+ * Rewrite one spec so it doesn't depend on BigDecimal#-, which is not yet implemented.
+
+commit f89bd8c6c425c9d9bcc3e589b8d3b05ce3ccbced
+Author: Brian Ford <bford@engineyard.com>
+Date: Sat Apr 26 15:52:49 2008 -0700
+
+ Made DRb spec depend partially on PID so multiple runs don't clash.
+
+commit 3c49a1d16f20726c4ee2d7eb5f5c671537aa59d5
+Author: Brian Ford <bford@engineyard.com>
+Date: Sat Apr 26 15:13:47 2008 -0700
+
+ Added wordsize guard for BigDecimal#exponent spec.
+
+commit 3aac5f6d64f4cbbca70ecf01b7ed9be596fa5b76
+Author: Brian Ford <bford@engineyard.com>
+Date: Sat Apr 26 15:09:40 2008 -0700
+
+ Updated spec_helper and renamed CaptureOutput to IOStub.
+
+commit 94322a6a95770a030d28925cc7213a38c5687ea1
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sat Apr 26 23:16:59 2008 +0200
+
+ A bit more test cases for BigDecimal#-@.
+
+commit 9919c5e3be59562532c967b479c959cf6270046e
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sat Apr 26 21:01:44 2008 +0200
+
+ New specs for BigDecimal#uminus.
+
+commit c3e74531f1ca1e70671f529671c0fa474968dc87
+Author: Marius Nuennerich <marius@nuenneri.ch>
+Date: Sat Apr 26 13:08:04 2008 +0200
+
+ FreeBSD seems to work like the rest, not darwin
+
+ Tested on FreeBSD/i386 7-STABLE
+
+commit c06a091b285f388f09b11037975921662759eea2
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sat Apr 26 12:14:33 2008 -0400
+
+ Implement BigDecimal#exponent, update spectags. Looks like parts of #** have accidentally stopped failing too. :)
+
+commit e5b753b7e659b29f5ed4aa57018f922111b238f5
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sat Apr 26 11:53:55 2008 -0400
+
+ Specify return type of BigDecimal#ceil as BigDecimal, as per library documentation.
+
+commit 0ca3b9ceb6ef5ca1898250b89f75c0194b5da481
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sat Apr 26 11:50:24 2008 -0400
+
+ Fix BigDecimal#inspect output, update spectags.
+
+commit ca99aa062afe9106ec614e2d8969d3491803c9a2
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sat Apr 26 11:48:00 2008 -0400
+
+ Specify return type of BigDecimal#ceil as BigDecimal, as per library documentation.
+
+commit 587a5cdbbfa4cccdbfe98339ca999f1d63bd66cf
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sat Apr 26 15:02:33 2008 +0200
+
+ Corrected one Array#hash test case.
+
+ Now Array#hash pass MRI 1.8.6, 1.8.7, 1.9 and JRuby.
+
+commit f86bdb98b8b9f5ea878c5d142f3a694e5278db77
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sat Apr 26 14:19:14 2008 +0200
+
+ Quarantined couple of specs that fail on *ALL* implmenetations.
+
+ Probably, we need a better way to do that, but quarantine
+ is a quick and simple way, easily detectable later on.
+
+commit 7ca928211180c66b9879afbc382c376a7649e1b0
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sat Apr 26 00:51:39 2008 -0400
+
+ Implement BigDecimal#to_f, update tags. Will this need more work?
+
+commit 69dec41f6b5b532c5de7f46e97f97c9e102305c7
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sat Apr 26 00:44:40 2008 -0400
+
+ Implement BigDecimal#truncate.
+ * Update spec tags.
+ * Reorganize variables slightly to remove duplication.
+
+commit c823e62c3a6776b62f65c34b16bdca5748d1add9
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sat Apr 26 00:36:43 2008 -0400
+
+ Implement BigDecimal#truncate.
+ * Update spec tags.
+ * Reorganize variables slightly to remove duplication.
+
+commit 3f4e5dc78de5bf3e81ae1ce7a0d14852a32aeade
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Sat Apr 26 00:16:38 2008 -0400
+
+ Rewrite spec description to bring it in line with what the spec actually does. :)
+
+commit 15d87e8a983d08d99fc3ec6bfbb7f36ed0cd4c4e
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 25 18:09:33 2008 -0400
+
+ Implement BigDecimal#to_i, update spec tags.
+
+commit 59873b144ea836e2f9bbef7d5186a1287155e76a
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri Apr 25 20:46:17 2008 -0400
+
+ Specs for autoload path normalization
+
+commit 71fe2d45d147fe2c41937ae5ef6dbb8814f491c4
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri Apr 25 17:05:17 2008 -0400
+
+ Use a separate class in Singleton 'new' specs to avoid contamination
+
+commit 2dc8f9eb9c6db014bd6cc132d987fdb4612816f8
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri Apr 25 16:45:28 2008 -0400
+
+ Handle more nightmare Autoload edge cases by hooking into Kernel#require
+
+commit 5c1a375a15adbe20a9bf3d1b95e1f2d30feaa90e
+Author: Michael Fellinger <m.fellinger@gmail.com>
+Date: Sat Apr 26 04:26:52 2008 +0900
+
+ Spec for Module#autoload when the load path has already been required
+
+ Signed-off-by: Wilson Bilkovich <wilson@supremetyrant.com>
+
+commit ee47a0cc0da787599479fc8dd085b7481b591176
+Author: Charles Oliver Nutter <charles.nutter@sun.com>
+Date: Fri Apr 25 15:41:19 2008 -0500
+
+ Enabled another $_ spec and added a proc dispatch scoping test to $~ and $_
+
+commit eabc4609758dc99727c77493c58f187782ea957f
+Author: Charles Oliver Nutter <charles.nutter@sun.com>
+Date: Fri Apr 25 15:32:03 2008 -0500
+
+ Added some basic specs for $_: implicit assign, explicit assign, scoping
+
+commit 61194dec429a9f288791156639f058e45a4e72e9
+Author: Charles Oliver Nutter <charles.nutter@sun.com>
+Date: Fri Apr 25 14:49:53 2008 -0500
+
+ Add some specs for $~ scoping and assignment.
+
+commit 4c5cec4f6e10864c68b140e71cc2559e7a7d636b
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Apr 24 18:28:08 2008 -0700
+
+ Added incomplete tags for CSV spec stubs.
+
+commit 6e231caef62e678413e86317881aaab200d0802e
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Apr 24 18:15:06 2008 -0700
+
+ Reprocessed library CSV specs with new mkspec.
+
+commit 4cdc61a76cce73b52f05f53f820838cc7e3c2823
+Author: Michael Fellinger <m.fellinger@gmail.com>
+Date: Fri Apr 25 08:46:32 2008 +0900
+
+ Updating specs for the module #included calling #extend issue.
+
+ Signed-off-by: Brian Ford <bford@engineyard.com>
+
+commit 40e775bf036aa59e69268708f8c78b8a56e0f9ce
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Apr 24 17:10:55 2008 -0700
+
+ Moved #bignum_value helper to MSpec.
+
+commit 9b52edbb14ff2fc18faa429daf4ceaff5b87db11
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Thu Apr 24 14:37:53 2008 -0400
+
+ Implement BigDecimal#fix, make #frac trap for a few common cases without running out of memory for big numbers.
+
+ * Update spec tags.
+
+commit 6a604c0a9863073cfd7540ff755e7ca035a7dff5
+Author: Marius Nuennerich <marius@nuenneri.ch>
+Date: Thu Apr 24 19:32:14 2008 +0200
+
+ Don't run Process.setpriority spec on FreeBSD
+
+commit 0ab639af500d947c5b5feb1d8f00f5fbc97a0edc
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Apr 24 13:37:23 2008 +0200
+
+ Adjusted IO specs to supply blocks for each-like methods.
+
+ See [ruby-core:16557] for more details.
+
+commit a7b603a9ce6bfb570785e803bdb89ae36bb6253d
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Apr 24 13:14:40 2008 +0200
+
+ Fixed IO, Process, Regexp specs ('should' was missing).
+
+commit 26de6c05c050d0dbcb073c407abda47f964bfd29
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Apr 24 12:50:46 2008 +0200
+
+ Fixed File specs ('should' is misspelled)
+
+commit 99a2b23d8fb42cb377cb3fb9ab2569c555aec8bf
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Apr 24 12:47:57 2008 +0200
+
+ Fixed Array specs ('should' was missing).
+
+commit 67b301a03fd6f7f0fa38ce106ab05825f2cbb15c
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Apr 24 12:43:39 2008 +0200
+
+ Fixed specs ('should' was missing), some new test cases for BigDecimal.
+
+commit aecbea57de7ee1b50bd4b06871dd08e762a6ccb8
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Apr 24 12:35:54 2008 +0200
+
+ More test cases for BigDecimal#nan?
+
+commit 7aaf8fa137b8961ca122eb92e7447936ad7a44cc
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Apr 24 12:26:20 2008 +0200
+
+ More test cases for BigDecimal#zero?
+
+commit 58ecee694f191aa05e7867544cf8d63129558447
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Apr 24 12:19:11 2008 +0200
+
+ A bit more test cases for BigDecimas#-.
+
+commit e946dd03d590e29a1d344e7579d5ff047df4a76b
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Apr 24 12:17:16 2008 +0200
+
+ New and updated specs for BigDecimal#-.
+
+commit 01d82db424b4e447b98e5f2eb3e162b991dece8a
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Apr 24 01:25:20 2008 -0700
+
+ Tag for new private setter method spec.
+
+commit c0ee2e133a4e5fc179b96329ffd3934dd9263c2b
+Merge: 374ab81... e9826b9...
+Author: Tony Arcieri <tony@medioh.com>
+Date: Thu Apr 24 02:19:00 2008 -0600
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit 374ab81e2c01ea5ac48cda2004ae92a989d7f3d7
+Author: Tony Arcieri <tony@medioh.com>
+Date: Thu Apr 24 02:18:26 2008 -0600
+
+ Specs for calling a private setter method on self
+
+ * Not presently working under rbx, works under MRI
+ * I don't entirely know the process for this, but this is expected to break
+
+commit 39505393f330b5f622788f1d98ea8ff3781499c7
+Author: Luis Lavena <luislavena@gmail.com>
+Date: Thu Apr 24 04:04:32 2008 -0300
+
+ Fixes Dir fixtures and specs for Windows.
+
+ Usage of special characters *, ?, | and : is not allowed under Windows
+ * and ? represent wildcards, | is pipe tunelling and : is drive letter
+ separator.
+
+ Files or Directories cannot contain slashes (\/), wildcards, double-
+ quotes, pipe tunelling or stream redirectors (<>).
+
+commit 2ecc076e488ed1a519fc5b6876c68a3d91d55c87
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Apr 23 22:55:44 2008 -0700
+
+ Update tags for newly passing File.join specs.
+
+commit ff3756e179920b84d5a55fc7bbc2688706df044f
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Apr 24 01:09:17 2008 -0400
+
+ Add specs for nested method definitions and other complex scenarios
+
+commit 24785f7c28cde09ce0400e5d80f832ae11cddefa
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed Apr 23 20:56:55 2008 -0400
+
+ Spec for using ||= to initialize a class variable
+
+commit 98b0c44057cb827107cae0f0174b5e81ac2064fd
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed Apr 23 19:49:21 2008 -0400
+
+ Rewrite descriptions of language/def specs
+
+commit 598c287cc36179644a1bbf2a303a56fc85bb1b12
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Apr 23 12:39:04 2008 -0700
+
+ Replaced use of :mswin with :windows in platform_is[_not] guards.
+
+commit 01fe417f27ad43495327a522ece2f02769064df7
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Apr 23 21:30:34 2008 +0200
+
+ Added excludes for BigDecimal#div specs.
+
+commit 46f022d49c394b027491295e7fd5cb305af33404
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Apr 23 21:27:42 2008 +0200
+
+ More specs for BigDecimal#div
+
+commit 72433091c6a845c5f550b27111748e29fb5eac09
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Apr 23 10:56:57 2008 -0700
+
+ Added #tmp helper to MSpec for returning a temp file name.
+
+commit f4e975e5255fb36bb8e9be7d310850135ce3515f
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Wed Apr 23 10:54:42 2008 -0400
+
+ Implement BigDecimal#frac, update spec tags.
+
+commit b60deba2368a1212d6acd3e49481ba9495de7f2f
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Wed Apr 23 10:49:39 2008 -0400
+
+ Correct a spec error.
+
+commit e19cf9401c029f90e117b1c17083c928b0d1c9ca
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Wed Apr 23 10:24:50 2008 -0400
+
+ Implement BigDecimal#-@, update spec tags.
+
+commit c3fc05389c75aca3150038814b324266501fdb8f
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Apr 23 16:12:24 2008 +0200
+
+ A bit more test cases for BigDecimal#sqrt.
+
+commit b2a220f86887bfe6030a34bc8cd1b748c88cc2b8
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Wed Apr 23 01:43:16 2008 -0400
+
+ Get BigDecimal#to_s working according to spec.
+
+ * Implement #to_s.
+ * Update spec tags.
+
+commit 82638601be12e410413047779f01840d6d0db3d8
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Wed Apr 23 09:56:48 2008 +1000
+
+ Refactor Debugger to remove dependencies on Debugger::Interface
+
+ Also:
+ - Add List#inspect to show number of items in list
+ - Fix decode output to show original instructions in place of
+ yield_debugger
+ - Improve regex used to match method names to handle more
+ operators
+
+commit 41c64f2825d347fbe2ef9edc33dd8f1e84773251
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue Apr 22 18:12:06 2008 -0400
+
+ Spec and implementation for NilClass#dup
+
+commit d3e313ed38a847e29225ba814a956d0929ea6460
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Apr 22 22:03:47 2008 +0200
+
+ New and updated specs for Bigdecimal's #abs and #sqrt.
+
+commit 2013e106181879b886f2e1cb78e81f52cd284666
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue Apr 22 15:52:10 2008 -0400
+
+ Re-implement Module#autoload and autoload?. Now passing all autoload specs.
+
+commit 9156271e2b12138e2b2b712a76f0110f20a757b7
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue Apr 22 15:34:43 2008 -0400
+
+ Add (failing) spec for toplevel autoloaded constant access
+
+commit 8eb5451f88a37dc247e42913c1d72d072a9b02ef
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Apr 22 19:06:00 2008 +0200
+
+ One more test case, for BigDecimal#sqrt with nil.
+
+commit e7894fb78cf92b53e9bdc6dcf023d8dd2d66b2ed
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Apr 22 18:52:24 2008 +0200
+
+ More detailed specs for BigDecimal#sqrt and fixes for old ones.
+
+commit 527a4b663c487cd9222ee2e6917e330ff9a130a1
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue Apr 22 12:38:53 2008 -0400
+
+ Rename ambiguously-worded autoload spec
+
+commit 3e6f16c41569dbba291bc3cececf137fc8952ee2
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Tue Apr 22 12:31:17 2008 -0400
+
+ Change to a significand-and-exponent implementation.
+
+ * Update spec tags.
+
+commit f1b2bf51042ca563ca74a9cf83db0e46a1bfabce
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Tue Apr 22 09:54:53 2008 -0400
+
+ Fix BigDecimal#zero, update spec tags. Also make #precs deal correctly with lowercase exponents.
+
+commit d0171de114e777f07a3e62972663475dd7747b05
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Tue Apr 22 09:24:06 2008 -0400
+
+ Implement BigDecimal#precs.
+
+ * Get #precs working. This will be less tortured once I implement a significand-and-exponent format.
+ * Update spec tags.
+
+commit e1fc7c6dc4c02c1763947c34d05f894661a84525
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Tue Apr 22 01:30:10 2008 -0400
+
+ Continue implementing bits of #inspect and updating spec tags.
+
+commit e4371f120c9c5c3c88a26d5f24f0d3ab888c954f
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Tue Apr 22 01:24:14 2008 -0400
+
+ Implement BigDecimal#==/eql? and the beginnings of #inspect.
+
+ * Find a way of implementing the equality test that satisfies the specs.
+ * Don't be so baroque in parsing strings in constructor.
+ * Update spec tags.
+ * Fix regression in abs_spec.
+ * Start implementing #inspect. Not really ready for prime time yet.
+
+commit 0494c1c35582381345194c76f7384eb9044797fc
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Tue Apr 22 00:41:03 2008 -0400
+
+ Start implementing BigDecimal#sign and #zero?.
+
+ * Clean up specs for #sign.
+ * Write some initial code to get these working. Not all there yet.
+
+commit 3c071b5f921898d87437803a500535b639d465ef
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Mon Apr 21 23:43:10 2008 -0400
+
+ Get BigDecimal#abs working.
+
+ * Implement the function.
+ * Improve the spec.
+
+commit 054582f3b89d757f033cd5f09cbf90fa08ad81d6
+Author: MenTaLguY <mental@rydia.net>
+Date: Mon Apr 21 22:32:42 2008 -0400
+
+ fix linked actors spec (sort of)
+
+commit d7a7d0c4d0d83d7e69216c96a249c4091fe75323
+Author: MenTaLguY <mental@rydia.net>
+Date: Mon Apr 21 22:28:29 2008 -0400
+
+ fix up registration spec
+
+commit 645784c3d39f776f583874e7c9244ff3de64cfe7
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Mon Apr 21 19:56:51 2008 -0400
+
+ Update tags on failing specs.
+
+commit 960faf5382d90db376ff14bb836463f1860a4b62
+Merge: 2e2150f... 046ba62...
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Mon Apr 21 18:46:45 2008 -0400
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit 046ba622836321f487f241c145a3bdf0968f0a67
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Mon Apr 21 18:44:50 2008 -0400
+
+ Specs for failing Module#autoload case (replicates a scenario from Merb)
+
+commit 18a2a26fa511d4943a724e27ce09e5855a257e90
+Merge: 1f5f4b5... 991c6e6...
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Mon Apr 21 14:15:07 2008 -0400
+
+ Merge branch 'master' of git://git.rubini.us/code
+
+ Conflicts:
+
+ lib/bigdecimal.rb
+ spec/ruby/1.8/library/matrix/diagonal_spec.rb
+ spec/ruby/1.8/library/matrix/element_reference_spec.rb
+ spec/ruby/1.8/library/matrix/shared/identity.rb
+ spec/ruby/1.8/library/matrix/shared/transpose.rb
+
+commit edd397c82a924e406eabbcd7e84243d94f8e8067
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Sun Apr 20 18:11:23 2008 -0500
+
+ Adds MinGW to the IO#popen spec guard
+
+commit 10df9f89189637b2c5a54b01a88eca6c9fbb4601
+Author: Adam Wiggins <adam@heroku.com>
+Date: Sun Apr 20 14:53:41 2008 -0700
+
+ IO.popen specs for reading and writing to pipes
+
+commit 3f70eceb3b9415a14f602c5b96121a459dca1e67
+Author: Brian Ford <bford@engineyard.com>
+Date: Sat Apr 19 22:32:28 2008 -0700
+
+ Fix silly typo in Numeric#quo specs.
+
+commit 6101a4992ddc15c0140f4d7702cf88d2d3a2ac53
+Author: Brian Ford <bford@engineyard.com>
+Date: Sat Apr 19 22:23:14 2008 -0700
+
+ Guard affected specs with conflicts_with :Rational.
+
+commit 354445f4d20ec66f207d65d1ccceb681bba7fff0
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Sat Apr 19 14:23:14 2008 -0500
+
+ Clarifying some of the Matrix specs
+
+ * Fixes two errors introduced by 28700c5cf7
+
+commit 2f5ca541fc08f0c033bc6541c72962228ea607de
+Author: Eero Saynatkari <projects@kittensoft.org>
+Date: Sat Apr 19 15:07:49 2008 -0400
+
+ Compiler specs' TestGenerator relies on broken #=== semantics, comply.
+
+commit 37cc9d4d6eb3442814ecc51845f025f464da64f7
+Author: Eero Saynatkari <projects@kittensoft.org>
+Date: Sat Apr 19 15:05:30 2008 -0400
+
+ Specs for default #=== and its relationship with #== and #equal?
+
+ * Rubinius deviates to not check object id directly.
+
+commit 28700c5cf7630be59877122e6470c42622b7365a
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Sat Apr 19 13:53:33 2008 -0500
+
+ Additional specs for Matrix
+
+ * Some of the constructors in Matrix keep referencing the original arguments after creation, these specs cover those cases.
+
+commit 12b0bc93e5a6b328ad0968c03c47af71f671aae2
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Sat Apr 19 13:06:04 2008 -0500
+
+ Replace object_id for equal? in Matrix specs
+
+commit ae377f0e56b8f31356935b3ac0800f561b2d1b2c
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sat Apr 19 14:49:34 2008 +0200
+
+ Fix File::Stat#uid specs
+
+commit 2e01a86a3977fe87f4f0734e50598b41f66f29d7
+Author: MenTaLguY <mental@rydia.net>
+Date: Sat Apr 19 03:24:32 2008 -0400
+
+ Gutted and reworked Actor, following Erlang more closely.
+
+commit de40303e17e2de1e7980564b43ee162c5080afa6
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Sat Apr 19 01:05:43 2008 -0500
+
+ Fixes Matrix#clone specs for MRI
+
+ * Makes sure the values (not the references) of the original rows are copied.
+
+commit 2b3a44158ae93ab5883da22e5f36df92485f3ad4
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Fri Apr 18 22:47:15 2008 -0500
+
+ Fixes a few things inside the Matrix specs.
+
+ * Removes some of the "needs to be reviewed for completeness" messages.
+ * Changes some of the descriptions
+
+commit 3be265a93a75b6a0267b1770f8cad671c4244671
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 12:26:29 2008 -0400
+
+ Matrix.unit, one more alias for .identity.
+
+ Signed-off-by: Federico Builes <federico.builes@gmail.com>
+
+commit 57aa8ba9a1dbdf62e9cf644bbde4603b841ffc76
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 12:25:55 2008 -0400
+
+ Name spec correctly.
+
+ Signed-off-by: Federico Builes <federico.builes@gmail.com>
+
+commit 3390dc4c6725d996eeb0c2e4ec73949bc0be2290
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 12:23:54 2008 -0400
+
+ Specs for Matrix.scalar and .identity/I.
+
+ Signed-off-by: Federico Builes <federico.builes@gmail.com>
+
+commit 958ca1faa1dc60ce591b4b2f768f22ac7f6cb56f
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 12:22:09 2008 -0400
+
+ Move the "needs to be reviewed" indicator to the right place.
+
+ Signed-off-by: Federico Builes <federico.builes@gmail.com>
+
+commit bf3eab630654eaaca9256850d258343e3024989e
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 12:03:45 2008 -0400
+
+ Use size functions instead of constants.
+
+ Signed-off-by: Federico Builes <federico.builes@gmail.com>
+
+commit 3981c931e7f4fde730d51614d40e44b9209347f9
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 11:58:22 2008 -0400
+
+ Specs for Matrix#clone and #transpose (alias #t).
+
+ Signed-off-by: Federico Builes <federico.builes@gmail.com>
+
+commit 72e1ea8900a638c796de9e715c5dffcf4ac90546
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 01:27:06 2008 -0400
+
+ Basic specs for Matrix.zero.
+
+ Signed-off-by: Federico Builes <federico.builes@gmail.com>
+
+commit f5d294ad941c477060e9b5d2329790db7e1e5700
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 01:20:27 2008 -0400
+
+ Move before block to a clearer place.
+
+ Signed-off-by: Federico Builes <federico.builes@gmail.com>
+
+commit b6bc5b224ade56ab96f3585b6b1c25e6dd5e1ad5
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 01:19:27 2008 -0400
+
+ Write specs for Matrix.diagonal.
+
+ Signed-off-by: Federico Builes <federico.builes@gmail.com>
+
+commit b4d056baa33a2181ab64c065ad1eb4adebcfaddf
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 01:11:11 2008 -0400
+
+ Some initial specs for Matrix#[] and Matrix.[].
+
+ These are in the same file because of coding conventions, but they probably should not be since .[] is a constructor and has very little in common conceptually with #[], which is a subscript operator.
+
+ Signed-off-by: Federico Builes <federico.builes@gmail.com>
+
+commit 5476d836577c0fbdbda097762862cf153ffb5e07
+Author: Brian Ford <bford@engineyard.com>
+Date: Fri Apr 18 18:35:34 2008 -0700
+
+ Some method profiles of data provided by John Lam.
+
+ Run these as follows:
+
+ bin/mspec -w rails.yaml spec/ruby
+
+ We'll be adding our own trace script, but for now, these
+ are snapshots of methods used by Rails loading a simple
+ "hello world" controller. The rails.yaml file is core
+ methods. The core.yaml file is generated by NameMap from
+ mspec/bin/name_map.rb.
+
+commit 24c71675cc63c86832ef8bc55d2f0167dff53073
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 17:24:08 2008 -0400
+
+ First stab at BigDecimal.
+
+ * A skeleton of a somewhat naïve implementation of BigDecimal.
+ * Updated spec tags.
+
+commit 1f5f4b59400b8b11df83b274efc8ce98186220ef
+Merge: 9d21b0e... 968a0ec...
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 17:28:02 2008 -0400
+
+ Merge branch 'bigdecimal'
+
+commit 9d21b0e890a9394658689af2bdee7e449cd2200b
+Merge: c3f3507... 1a08506...
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 17:25:09 2008 -0400
+
+ Merge branch 'master' of git://git.rubini.us/code
+
+commit 968a0ecda8477b33ceab2e7d0c7e7d084a105bdb
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 17:24:08 2008 -0400
+
+ First stab at BigDecimal.
+
+ * A skeleton of a somewhat naïve implementation of BigDecimal.
+ * Updated spec tags.
+
+commit 1f410d918a59b9b49e87a407cc8fba4bbf342a79
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Fri Apr 18 22:34:53 2008 +0200
+
+ Fix a bunch of specs and minor issues in File::Stat
+
+ Specs for File::Stat#<=>, File::Stat#ino, File::Stat#inspect,
+ File::Stat#mode, File#Stat.initialize and some minor bugfixes
+ such as the fact that File::Stat needs to include Comparable
+ (like MRI).
+
+commit d6f2c6995941762878f4b777a39b0c23ea654605
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Thu Apr 17 22:30:36 2008 +0200
+
+ Remove specs for non-existent File::Stat#initialize_copy
+
+commit c3f350716a35cb869b3ea0289c0e404d07b8819f
+Merge: 810afff... b861102...
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 16:18:34 2008 -0400
+
+ Merge branch 'master' of git://git.rubini.us/code
+
+commit 72101783ec6e66a4f9ac3f9c90f7e8f5b67058ec
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Tue Apr 8 17:05:26 2008 -0700
+
+ Reworked masgn specs to evaluate L2R and assign L2R. excluded.
+
+commit 4e4bec628b21938617bdfa5a2ef17aedf02c112c
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Tue Apr 8 15:17:00 2008 -0700
+
+ trailing whitespace is killing me... evan\! fix your editor\!
+
+commit 810afffa2e549048947c07b30d77be255db42d73
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 12:26:29 2008 -0400
+
+ Matrix.unit, one more alias for .identity.
+
+commit 2c84f77535d677a42bee93759c77f79c2cdd4d93
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 12:25:55 2008 -0400
+
+ Name spec correctly.
+
+commit 762f5ee0f7ba4234847c695c92e3ed27dd05e134
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 12:23:54 2008 -0400
+
+ Specs for Matrix.scalar and .identity/I.
+
+commit b68295e0046a2eb1fb911ea891d6e0a29174ea30
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 12:22:09 2008 -0400
+
+ Move the "needs to be reviewed" indicator to the right place.
+
+commit 4b6e1097feafe2247e59d6004a36bb0987734138
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 12:03:45 2008 -0400
+
+ Use size functions instead of constants.
+
+commit 2086f0c1f1f899f2e41307a5434a5bb6446e20a2
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 11:58:22 2008 -0400
+
+ Specs for Matrix#clone and #transpose (alias #t).
+
+commit 2939c55b2e9f38b5115b98429de97bc4fff6f165
+Merge: a47f2b8... 42d3212...
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 02:09:07 2008 -0400
+
+ Merge branch 'master' of git://github.com/evanphx/rubinius
+
+commit a47f2b852ca309a68b687157a6cd973716328887
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 01:27:06 2008 -0400
+
+ Basic specs for Matrix.zero.
+
+commit aa3b2eeef70cb8967ef6c92ee24a226c2d1202c1
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 01:20:27 2008 -0400
+
+ Move before block to a clearer place.
+
+commit ca6ac1e59ddb268b388975a2fb5b11e6026e65c8
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 01:19:27 2008 -0400
+
+ Write specs for Matrix.diagonal.
+
+commit b24216d8b0ecfba6888f909415e2523eaed2aeb2
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Fri Apr 18 01:11:11 2008 -0400
+
+ Some initial specs for Matrix#[] and Matrix.[].
+
+ These are in the same file because of coding conventions, but they probably should not be since .[] is a constructor and has very little in common conceptually with #[], which is a subscript operator.
+
+commit 9313f29ed952f604e0d124ced38ee930b5780b27
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Thu Apr 17 22:35:43 2008 -0400
+
+ New spec tags for Complex#%.
+
+commit 08f316de96c94b7d4865d77873327deddeabb664
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Thu Apr 17 22:32:08 2008 -0400
+
+ More specs for Complex.
+
+ * Specs for <=>, conj/conjugate, to_s.
+
+commit 973c304cc16fa6b78dba31de11b151da2daae762
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Thu Apr 17 22:09:20 2008 -0400
+
+ More specs for Complex
+ * Complex#abs, abs2, angle, arg, and %. Not sure that % is correctly specified.
+
+commit e32b26694277065fe28f138dca837b8c0509c735
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Thu Apr 17 21:17:26 2008 -0400
+
+ More specs for Complex.
+
+ * Write specs for * and /.
+ * Rewrite + and - to use alternate constructor syntax.
+
+ Signed-off-by: Marnen Laibow-Koser <marnen@marnen.org>
+
+commit 0cbf88a6c61e477f4b9a7758a9fab1258efbf30f
+Author: Marnen Laibow-Koser <marnen@marnen.org>
+Date: Thu Apr 17 20:46:04 2008 -0400
+
+ Write some specs for Complex.
+
+ * Basic specs for Complex.new, Complex.new!, Complex#+, and Complex#-.
+
+ Signed-off-by: Marnen Laibow-Koser <marnen@marnen.org>
+
+commit 71909e78b8d77f7e48d306e30f51fbc21b5fbefb
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Apr 17 15:26:53 2008 -0700
+
+ Reorganize and fix Matrix specs.
+
+commit 5a9325457696dfba3c410c0adcbdec706ecda3bf
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Apr 17 12:49:45 2008 -0700
+
+ Added spec templates and incomplete tags for CGI.
+
+commit d62de6b4096a9b3bd3fda197b70d6e603596e865
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Apr 17 21:12:08 2008 +0200
+
+ More detailed speecs for BigDecimal#new
+
+commit ebd6fb8f879f94ff51b74cb4e76080fad7b66cb5
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Apr 17 19:55:31 2008 +0200
+
+ More detailed specs for BigDecimal's <, <=, >, >=, <=>.
+
+commit 8caef40cbe873dc2825bc0ba1e66f983b8219cca
+Author: MenTaLguY <mental@rydia.net>
+Date: Thu Apr 17 01:16:25 2008 -0400
+
+ add tag object argument to send_in_*
+
+commit 0596b1aca45a85de5f3d727632585da924fd3eb0
+Author: Matthias Reitinger <m.reitinger@mytum.de>
+Date: Sat Apr 12 10:11:13 2008 +0200
+
+ New specs for BigDecimal.new.
+
+ Signed-off-by: Marius Nuennerich <marius@nuenneri.ch>
+
+commit 5c176e50fe962de1095a75221b4d63e75acc505f
+Author: Benjamin Stiglitz <ben@tanjero.com>
+Date: Wed Apr 16 11:32:18 2008 -0700
+
+ Cleaned up Numeric#div spec
+
+ The spec names are no longer quite as atrocious; the spec output is now fairly
+ readable. The different Integer-Float quotient permutations are now correctly
+ specified as well.
+
+ Signed-off-by: Brian Ford <bford@engineyard.com>
+
+commit e1406b19c51bfca5f6936d143087043316c68c13
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Apr 15 20:07:48 2008 -0400
+
+ Specs for Kernel#p behaviour.
+
+ * Args vs. no args.
+ * Record separator is not taken into account.
+
+commit 30c717e1736b65a852df501f71e320599fc17786
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Apr 15 21:32:13 2008 +0200
+
+ Fix typo in File.grpowned? spec
+
+commit 1bc17a0b4c8f19b84ffdd0b17ec24243a1df6092
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Apr 15 21:31:02 2008 +0200
+
+ Fix File.grpowned? and it's spec
+
+commit f49cf4d0319b5772ede7bcddd763c691d5253b18
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Apr 15 20:44:18 2008 +0200
+
+ Update tags for implemented File.grpowned? specs
+
+commit ea19fb07cb7b789165aec5da0f571345b96f1f0f
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Apr 15 20:41:02 2008 +0200
+
+ Spec File::Stat#grpowned? and implement File.grpowned?
+
+commit c411b15b9f94fec21b02a9208cbae4b42452431d
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Apr 15 20:19:24 2008 +0200
+
+ Properly rename File::Stat#dev_major and File::Stat#dev_minor specs
+
+commit 26ba3ad30cd726b058cd76f23dc7a79555be724e
+Author: Charles Oliver Nutter <charles.nutter@sun.com>
+Date: Tue Apr 15 10:25:44 2008 -0700
+
+ Quarantine the cvar-related instance_eval spec for now; it's not clean.
+
+commit d72c609ce4567d7a7fdfd2ee4713ac07033c81db
+Author: Marius Nuennerich <marius@nuenneri.ch>
+Date: Mon Apr 14 20:13:38 2008 +0200
+
+ Use EnvSpecs where possible
+
+commit 8ccdf2d612f15515837095e2e4a570861024294c
+Author: Marius Nuennerich <marius@nuenneri.ch>
+Date: Mon Apr 14 17:42:36 2008 +0200
+
+ Use EnvSpecs module for platform dependent stuff
+
+commit 130e4bdb1d9fa9512dfe45d4ff4d718096683cdb
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Mon Apr 14 19:45:24 2008 +0200
+
+ Fix specs for a bunch of File::Stat methods and implement File::Stat#<=>
+
+ Created specs for atime, blksize, blocks, ctime, mtime and <=>
+
+commit e5aa89ff13128afb9b43ad77678792aeae4d48ea
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sat Apr 12 17:54:57 2008 +0200
+
+ Remove tag for fixed File#lstat
+
+commit 21cd4a10833ef3bdda1593423faccb334de16536
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sat Apr 12 17:52:33 2008 +0200
+
+ Remove unneccary spec placeholders for File#stat / File#lstat
+
+ The shared spec already tests this behavior. With the new added spec
+ for the difference between the two, File.stat / File.lstat is pretty
+ well covered for now.
+
+commit eacb4f8a4d0ba606458a5756ddd6f2ce723a3dfa
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sat Apr 12 17:45:55 2008 +0200
+
+ Specced different behavior between File.stat and File.lstat
+
+commit 4ae163810074effc068babf538f004e9ff117156
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Mon Apr 14 16:49:04 2008 +1000
+
+ Refactor Debugger interface into a CmdLineInterface class
+
+commit e61241498f6ca63b7d5e50e94a70456bc40e929b
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Mon Apr 14 14:17:03 2008 +1000
+
+ Breakpoint clean-up
+
+commit ae738f21979edf727437438b992629dd0b59a42e
+Author: MenTaLguY <mental@rydia.net>
+Date: Sun Apr 13 16:14:34 2008 -0400
+
+ elminate Mailbox#clear; difficult to implement with sane semanitics
+
+commit 76385484049e47f53b840ddf3c0dfe9e365ca8cf
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Sat Apr 12 16:39:19 2008 -0500
+
+ More specs for REXML::Element
+
+commit 00547bc562c359ddac13d04a5c955ee25171bcb4
+Author: Matthias Reitinger <m.reitinger@mytum.de>
+Date: Sat Apr 12 11:15:12 2008 +0200
+
+ Fixed incorrect check for object equality in BigDecimal#nonzero? spec
+
+ One should not use == to check if the method returns self, but equal?
+
+ Signed-off-by: Federico Builes <federico.builes@gmail.com>
+
+commit e8403792167c86f120ce7bdcd1e2c7ce1bc31fea
+Author: Matthias Reitinger <m.reitinger@mytum.de>
+Date: Sat Apr 12 11:31:25 2008 +0200
+
+ Eliminated use of to_s to check for NaN in BigDecimal specs
+
+ Changed "to_s.should == 'NaN'" to "nan?.should == true"
+
+ Signed-off-by: Federico Builes <federico.builes@gmail.com>
+
+commit 5883dd78ad92031c920bb9ee2b703702969a5854
+Author: Charles Oliver Nutter <charles.nutter@sun.com>
+Date: Sat Apr 12 09:42:11 2008 -0500
+
+ A few more instance_eval specs, for non-immediate numerics and cvars.
+
+commit e8fd8e696d5487fa698a9a8b1bab2fb54b420133
+Author: Charles Oliver Nutter <charles.nutter@sun.com>
+Date: Sat Apr 12 08:28:41 2008 -0500
+
+ Added instance_eval spec for defining methods under immediates.
+
+commit c23b365a95862cd438e6228929a3a4e935d60de9
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Apr 11 22:09:06 2008 +0200
+
+ New rubypsecs for BigDecimal#fix and #frac.
+
+commit 6b6b63ebedb61466b4f04f510bf859574efec7d9
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Apr 11 20:07:07 2008 +0200
+
+ New rubyspecs for Bigdecimal#floor and #ceil.
+
+commit 75e9118aea32baaeec82efedb5106c63bb0eef44
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Apr 11 17:50:36 2008 +0200
+
+ Corrected Bigdecimal specs since they were missing "should" statements. :)
+
+ Also, added some more cases.
+
+commit 18fafb2e1f653887fdd3cdef693448d9b2bea29e
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Apr 11 17:21:02 2008 +0200
+
+ New rubyspecs for BigDecimal's #power, #** and #exponent.
+
+commit 35e32daa38c7df385aac99f7b709a4038141faaa
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Apr 11 13:41:13 2008 +0200
+
+ New and updated rubyspecs for BigDecimal#precs.
+
+commit e0172d4eee7a775ab53562477997855ed66615a7
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Apr 11 12:31:22 2008 +0200
+
+ More rubyspecs for BigDecimal#split and some corrections for older ones.
+
+commit 37d312770700da5eb124fdce7a7b1687c2d9b839
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Fri Apr 11 13:55:00 2008 +1000
+
+ Get breakpoint handling working properly
+
+commit 498b95a720e98b70b56af9dfd2c1ba20c0bf89c3
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Wed Apr 9 17:23:56 2008 +1000
+
+ Make ISeq#decode return symbols rather than objects by default
+
+commit b8bda0546cdb9ac04ae629f13ccfce5f474e6f2c
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Mon Mar 17 14:33:45 2008 +1100
+
+ Ensure breakpoint original instruction is correct
+
+ When multiple breakpoints are set at the same location,
+ only the first breakpoint sees the original instruction.
+ This commit ensures the BreakpointTracker detects such
+ situations, and updates the breakpoint to set the correct
+ oringinal instruction to use.
+
+commit 2700924f23e0283a059583f9e92188b1c3c4f220
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Thu Mar 13 17:20:49 2008 +1100
+
+ Reorganize Breakpoint class hierarchy
+
+ Refactor Breakpoint class hierarchy in preparation for
+ adding PersistentBreakpoint and BreakpointRestorer classes.
+
+commit bfa69d930c38897df18b656d7b86f0b549bed57f
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Apr 11 03:01:37 2008 +0200
+
+ Some more test cases for BigDecimas#finite? and #nonzero?.
+
+commit 71a4b0a51ea4da0c41d7b096aa7b88deb8d0d049
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Apr 11 02:42:33 2008 +0200
+
+ A bit more rubyspecs for BigDecimal#sub and #to_s.
+
+commit 8ff9ae455c6c7f4b38f3b4dcbdc6c677759f13e2
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Apr 11 01:35:16 2008 +0200
+
+ New rubyspecs for BigDecimal#truncate.
+
+commit f0a5c13f218d1e2187dfff09bd27cbd6dde544ca
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Apr 10 15:09:55 2008 -0700
+
+ Converted VMActor specs to dir/files. Added incomplete tags.
+
+commit df74b0fd98597b51d4c1d51ae09706d51e1a5d3c
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Apr 10 15:01:08 2008 -0700
+
+ Converted Mailbox specs to dir/files. Added incomplete tags.
+
+commit 29d223d8bfcc36edc16db58d50f8186905df773a
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Apr 10 14:49:52 2008 -0700
+
+ Converted Actor specs to dir/files. Added incomplete tags.
+
+commit 08ab8db440cfdaa7e06b19a0d88750678d4fccbf
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Apr 10 22:09:39 2008 +0200
+
+ New rubyspecs for bigdecimal, and excludes.
+
+commit b76a9e964899348d667181d288c5d4ec0e422c9f
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Apr 10 19:33:03 2008 +0200
+
+ One rubyspec for the class definition: def nil:Foo; end
+
+commit c526f5744ece40e312340556991ee54e4504ebcd
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Apr 10 00:53:06 2008 -0700
+
+ Processed Rational, Complex, Matrix with mkspec.
+
+commit 3de6f530c42bdca8c9b1202e60d0d14850024d15
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Apr 10 00:27:40 2008 -0700
+
+ Processed IO with mkspec. Added incomplete tags.
+
+commit faaf8bdb8893f71234d7e2fab07aa11d6c556384
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Apr 10 00:17:35 2008 -0700
+
+ Clean up especially bad whitespace in File specs.
+
+commit fedda8f6865c6cdb07c7599606204f0700042574
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Apr 10 00:09:56 2008 -0700
+
+ Processed File specs with mkspec. Added incomplete tags.
+
+commit 09f6f1b5138b7ca1d276a8c68ee6bf1cba7691b7
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Apr 9 23:42:40 2008 -0700
+
+ Processed Kernel specs with mkspec. Added incomplete tags.
+
+commit e3ca2e3e077c0e026b96e1e68808b95d44233cf5
+Merge: cf0c855... 4d0d1f6...
+Author: Thomas Lachmann <thomas_lachmann@gmx.de>
+Date: Thu Apr 10 02:48:31 2008 +0200
+
+ Merge branch 'master' into bigdecimal_specs
+
+commit cf0c8552f31cfd856822c8aa43a5d9d265481ac0
+Author: Thomas Lachmann <thomas_lachmann@gmx.de>
+Date: Thu Apr 10 02:40:22 2008 +0200
+
+ Next bunch of specs for Bigdecimal.
+
+commit 4d0d1f6b98ac2dafa487ece31512443a07bbc928
+Author: Marius Nuennerich <marius@nuenneri.ch>
+Date: Wed Apr 9 23:42:10 2008 +0200
+
+ Fix ENV specs
+
+ * Try to avoid `env`
+
+commit dfcc69ea8bd78e9e463defdef3b4529a5af40bb5
+Merge: 75e6ccd... 6a50f0d...
+Author: Marius Nuennerich <marius@nuenneri.ch>
+Date: Wed Apr 9 22:26:26 2008 +0200
+
+ Merge branch 'master' of git://git.rubini.us/code
+
+commit 75e6ccd48bce9e0e939a0ff1d484f14a029969f9
+Author: Marius Nuennerich <marius@nuenneri.ch>
+Date: Wed Apr 9 22:26:11 2008 +0200
+
+ Fixes for ENV
+
+ * Add specs
+ * Add some missing methods to ENV
+
+commit 6a50f0d2f5146901fe96fe86802df155c9266a21
+Author: Thomas Lachmann <thomas_lachmann@gmx.de>
+Date: Wed Apr 9 21:11:05 2008 +0200
+
+ Fixed failures for BigDecimal#specs.
+
+commit 09bc62e39a8b92c25aeb6287f9fbf4e9cd2b9a6f
+Author: Thomas Lachmann <thomas_lachmann@gmx.de>
+Date: Wed Apr 9 19:55:45 2008 +0200
+
+ Bunch of specs for BigDecimal.
+
+commit c281add79d621f6327740109895c624dd25a2e1b
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Wed Apr 9 09:13:29 2008 -0500
+
+ Cleaning up UPSocket#send specs
+
+ * Got rid of the weird exception catching
+ * DRY things up a bit with before :each
+
+commit 8ebefe3c0a61b7aab8ac3d0ae9768c35b657cdb6
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Wed Apr 9 09:11:43 2008 -0500
+
+ Adding spec helpers to REXML specs
+
+commit e3064084efbbac1147d477435010d933ce101413
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Apr 8 22:20:43 2008 -0400
+
+ Amended spec wording for Singleton._load slightly. Updated exclude.
+
+commit 23e621625b95e0db82bd406a5eb8fa7324e41a6e
+Author: Chris Shea <chris@tie-rack.org>
+Date: Tue Apr 8 15:49:11 2008 -0600
+
+ Create spec for Marshal.load of Singleton instance
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit eec07baa07d591059c64f32c0ddef169cfcccaef
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Apr 8 20:20:02 2008 -0500
+
+ Thread#wakeup deadlock for MRI marked as ruby_bug
+
+commit dbb744d9692c2432d7aebecac17365125efe9087
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Apr 8 20:02:08 2008 -0500
+
+ spec for wakeup which causes MRI to deadlock when it shouldn't
+
+commit 208a7df6ec2d3c8f550a7ac24db849e593cdc9f3
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Apr 8 17:58:15 2008 -0500
+
+ specs for Thread::list
+
+commit f6f307e75e49cdf597b0b3755ab214c6fc1950dd
+Author: Thomas Lachmann <thomas_lachmann@gmx.de>
+Date: Wed Apr 9 00:54:34 2008 +0200
+
+ specs for BigDecimal.new and BigDecimal#zero? (plus tag files).
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 5b1f2043f70b0088f1c32be79eeaa8179c2210a6
+Author: Jeff Rose <jeff@rosejn.net>
+Date: Wed Apr 9 00:44:27 2008 +0200
+
+ Specs for Actor linking and registration, and Mailbox timeouts.
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 4eea149d3d503c121fb7c65115e374838fff8c8a
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Tue Apr 8 14:55:18 2008 -0700
+
+ Added extra Array subclass dup spec
+
+commit 783a884931b718b8fa65dd9768fbebd8a0d1ac0c
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Tue Apr 8 14:18:45 2008 -0700
+
+ minor cleanup
+
+commit 0e047cc97aa6a5acd7193bdde1139f6a89f108b8
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Tue Apr 8 14:18:27 2008 -0700
+
+ minor cleanup
+
+commit f4797827393e0d9d0e5df5aa5184ecebb066d766
+Author: Matthias Reitinger <m.reitinger@mytum.de>
+Date: Tue Apr 8 17:08:32 2008 +0200
+
+ Extended Symbol#inspect spec and reworked Symbol#inspect to fulfill them
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit b3c3a5f60177f9c52725b6cacf019412d2c747ea
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Apr 8 17:29:58 2008 -0400
+
+ Excludes for BigDecimal specs.
+
+commit b7cd3c38d146a7833ef1d426ea8acd4ee4cb09bf
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Apr 8 17:13:48 2008 -0400
+
+ Switched #requires around to have access to #pretty_inspect.
+
+commit aba428095e09ead8ed66895b175e5f3673c4310e
+Author: Thomas Lachmann <thomas_lachmann@gmx.de>
+Date: Tue Apr 8 17:45:13 2008 +0200
+
+ Spec for BigDecimal#to_f.
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 20a5789f9dc4e6d30dffb594476b354e4aeee201
+Author: Thomas Lachmann <thomas_lachmann@gmx.de>
+Date: Tue Apr 8 17:21:39 2008 +0200
+
+ Spec for BigDecimal#finite?
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 896609e7ae8ee12c72e4e3ce86897c1f8b98f3fb
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Tue Apr 8 02:03:22 2008 -0700
+
+ overlooked 2 specs
+
+commit 77774ed4300d5245c58dbcc686cd72dc48f08a1f
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Tue Apr 8 02:00:24 2008 -0700
+
+ Added a bunch of specs to String#to_f
+
+commit 1b91113c3e8fb46a0d355cae9000ee4c82f95ac3
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Mon Apr 7 21:46:17 2008 -0500
+
+ More specs for REXML
+
+commit 2460839e3fbe2967b9df70db3de33b2a102b9a44
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Apr 7 12:19:40 2008 -0700
+
+ Reworked how MSpec handles config files. Use 'set :sym, value' now.
+
+commit 67d3869e9b3fef6d47727206d02814da410e02fc
+Author: Jeff <rosejn@warp.(none)>
+Date: Mon Apr 7 15:04:09 2008 +0200
+
+ Adding specs for Mailbox and Actor, and renaming the VMActor describe to match the standard scheme.
+
+ Signed-off-by: Charles Comstock <dgtized@gmail.com>
+
+commit 7391c1fbc02966165de03724c42fc1d5243ac99f
+Author: Marius Nuennerich <marius@nuenneri.ch>
+Date: Sun Apr 6 22:29:22 2008 +0200
+
+ repair UDPSocket spec
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit 5a205207faad0a85271bfcb459390793702c4143
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Apr 5 04:54:21 2008 -0400
+
+ Partially revert "Add spec files for cgi.rb."
+
+ This partially reverts commit e2714f2fd2d8825ac8af761a5a4545e4d0731735.
+
+ Conflicts, left these files:
+
+ spec/ruby/1.8/library/cgi/escapeHTML_spec.rb
+ spec/ruby/1.8/library/cgi/escape_spec.rb
+ spec/ruby/1.8/library/cgi/rfc1123_date_spec.rb
+ spec/ruby/1.8/library/cgi/unescapeHTML_spec.rb
+ spec/ruby/1.8/library/cgi/unescape_spec.rb
+
+commit 22f3042377731cb6ff963b9e322b24014b286895
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Apr 5 03:18:15 2008 -0400
+
+ Added excludes for the CGI specs.
+
+commit 7b9f5a213c971636b663e992fcb8578888d27f52
+Author: makoto kuwata <kwa@kuwata-lab.com>
+Date: Sat Apr 5 13:03:59 2008 +0900
+
+ Add spec file for CGI::rfc1123_date().
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 31edbd64bba7f352930ac04d51b63e72553796a9
+Author: makoto kuwata <kwa@kuwata-lab.com>
+Date: Sat Apr 5 13:03:14 2008 +0900
+
+ Add spec files for CGI::escapeHTML() and CGI::unescapeHTML().
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit fc321869d73f58dcfbb55ba374646c1568528004
+Author: makoto kuwata <kwa@kuwata-lab.com>
+Date: Sat Apr 5 13:01:33 2008 +0900
+
+ Add spec files for CGI::escape() and CGI::unescape().
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit e2714f2fd2d8825ac8af761a5a4545e4d0731735
+Author: makoto kuwata <kwa@kuwata-lab.com>
+Date: Sat Apr 5 12:44:57 2008 +0900
+
+ Add spec files for cgi.rb.
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 2a1d0ad7e51ba52a918111d53be6a641c41a0445
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Fri Apr 4 22:21:47 2008 -0400
+
+ Improved the *rest argument count spec a bit.
+
+commit e8053e4bb108cf877ac8fdafc104eb34bad671f0
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Fri Apr 4 20:38:23 2008 -0400
+
+ Specs for unlimited argument count for *rest defns.
+
+commit 03e092e45015f8115f806e11460121c560e60b4b
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Fri Apr 4 17:54:25 2008 -0700
+
+ Converted symbol spec to be generative, allowing easier pattern detection
+
+commit bbda617127a8ac319a58fa190d43b3a0d960d309
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Fri Apr 4 14:07:50 2008 -0500
+
+ updated File#inspect tags
+
+commit 38eb679d6b6c5aef8bccb2139e681c926b3290c7
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Fri Apr 4 06:00:37 2008 -0400
+
+ Specs for ~/ expansion in #require, #load. It has broken at some point.
+
+commit 2d600c01205fbb7ccd98e7f7a88ebcbd0e1d1d43
+Author: Paul Thornthwaite <tokengeek@gmail.com>
+Date: Fri Apr 4 08:43:42 2008 +0100
+
+ Updated specs for Set library
+
+ * Added specs for Set#subset and Set#proper_subset
+ * Added specs covering empty sets and comparisons
+ * Corrected spec string to include ? on superset method names
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 3a547c2b82434c64b72967ebd917fc063ff1317d
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Apr 3 23:16:18 2008 -0700
+
+ Fixed GetoptLong specs to not depend on value of ARGV.
+
+commit 5dd9b0ecdddfd990d6387a0a7c70173ea0cededa
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Apr 2 23:27:04 2008 -0700
+
+ Add config file for and rework MSpec runners.
+
+commit 773a13ed9005628e48ed146180041caa035f4072
+Author: David Yip <yipdw@member.fsf.org>
+Date: Thu Apr 3 03:18:48 2008 -0400
+
+ Added spec: full contents of StringIO stream should be accessible after rewind.
+
+ Spec tested against Ruby 1.8.6p111 and Ruby 1.8.6p114 on OS X 10.4.11.
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit ba2ca41cb29ac08c94231a2383940464e6fd1c9d
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Thu Apr 3 08:45:49 2008 -0500
+
+ Updated tags for REXML specs
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 09c080bf33092b9d147d1b0a5de920fce8527fdc
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Thu Apr 3 08:45:26 2008 -0500
+
+ Fixes whitespace in REXML::Element specs
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 3a997bc18f589b91b4cd518448644171f3054abf
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Thu Apr 3 08:29:23 2008 -0500
+
+ More specs for REXML::Element
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit d250939060a4a91a6fee59bd4bfa4e86eb271373
+Author: Paul Thornthwaite <tokengeek@gmail.com>
+Date: Thu Apr 3 14:36:42 2008 +0100
+
+ Specs for Set#superset and Set#proper_superset added
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 01399738d5ad0136ef205b8501b12012c7e42230
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Apr 3 18:09:20 2008 -0400
+
+ Removed excludes for Object#kind_of?, #is_a?.
+
+commit 0e7d1c6e02e5617bb251366e0d60760edb29377e
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Apr 3 20:04:34 2008 +0200
+
+ Fixed copy-paste error in Object#is_a? specs.
+
+ Adjusted the Object#is_a? exclude.
+
+commit 4a9cb7cc0c734b4280c3a65906c85e1c1e2f4990
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Thu Apr 3 19:02:05 2008 +0200
+
+ Add specs for #kind_of? / #is_a? behaviour that are failing in Rubinius.
+
+commit e88fdb6cbd9fa829a81e6c7664e88f6956ddae64
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Apr 3 07:18:56 2008 -0400
+
+ Spec to check `A = 12; class A; end` raises TypeError. Works as is.
+
+commit 3c0db09626333405bdcb72e62ddb8fb2ea176ff5
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Apr 3 06:44:32 2008 -0400
+
+ Spec for const lookup: `A = 12; class A::B; end` should raise TypeError.
+
+ * Currently crashes due to a lookup problem.
+ * VVSiz discovered and reported.
+
+commit edda5994c293e4d26b4a741e90e0ab61513e8dec
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Wed Apr 2 16:39:09 2008 +1100
+
+ Do not strip leading spaces in debugger output
+
+commit eecc2bca5045921368378abfccafcf70339441f9
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Apr 2 21:34:52 2008 +0200
+
+ Enabled File#truncate testcase for JRuby.
+
+commit 4d555cf50dfe6a8e9cb2f24a6a636a9df3f03768
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Apr 2 20:03:02 2008 +0200
+
+ Added test case to File.open rubyspecs.
+
+ Courtesy of David Yip.
+
+commit 42f0b52cd9fbac4a39fc1e5c2a241462bee5bf3b
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Apr 2 01:11:38 2008 -0700
+
+ Use kind_of instruction since #kind_of? is not available at all times.
+
+commit 9ee52514eee820b9af7c9e6d2eaaca8d2bca363b
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Apr 1 17:16:47 2008 -0500
+
+ IO#reopen should return self
+
+commit f1481283091fcbe662fd01d409f5a2d2d7e3aa59
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Apr 1 18:57:06 2008 -0500
+
+ added primitive io_close_ng and tagged IO#close spec failures
+
+commit 3861e75e01af9319e2af879e2644fc8509947903
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Apr 1 16:07:27 2008 -0500
+
+ IO#close should return nil and refactored TCPServer.accept specs
+
+commit d6dfbd3b0bab57453e67991c3320744b08346979
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Apr 1 15:04:18 2008 -0500
+
+ DRb specs now attempt to check if server is up/down prior to each call to start_server
+
+ note that there is something wrong with the way stop_server works in rubinius as it appears that the TCPServer is still binding the port. Spec is tagged to deal with this but technically it's probably a bug in TCPServer
+
+commit 4119fe8baab45be6b1d1370b8a9537e710b1a60a
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Apr 1 12:40:47 2008 -0400
+
+ Sanity changes to #load specs to bring them up to date.
+
+ * Please change the specs if you change the implementation, sheesh.
+
+commit 3b58cb35abeba31f7ac72e3ab37b2630949406a7
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Apr 1 10:59:58 2008 -0400
+
+ Spec for forced recompiling through second parameter of Kernel#load.
+
+commit 5d7a73ae15a4c40e31486a60cbb66f3de1ac4697
+Author: David Whittington <djwhitt@gmail.com>
+Date: Wed Apr 2 02:57:35 2008 +0000
+
+ Add tags for failing private keyword specs
+
+commit 1b2f118be7ff9b6adfea736ecbbb8f3fd8dd0f49
+Author: David Whittington <djwhitt@gmail.com>
+Date: Wed Apr 2 02:53:43 2008 +0000
+
+ Added a couple evil private keyword tests
+
+commit f58c67e33a99f751c3520ab65c96e28a91c45900
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Apr 1 09:59:22 2008 -0400
+
+ Conditional compilation. Rubinius.compile_if($DEBUG) { p somevariable }.
+
+ * Hacky and probably fragile but it seems to work. Whenever the gvar
+ given as condition evaluates to false, the entire block is omitted
+ from the produced bytecode. If it evaluates to true, then the extra
+ block itself is stripped and only the block contents remain.
+ * Do NOT use indiscriminately until we have played around with it for
+ a bit to avoid problems.
+ * Manipulates the sexp, not the AST to avoid worrying about locals
+ and scopes and whatnot.
+ * Enabled by default; for example -d will work out of the box (you
+ do need to have the file recompiled obviously.)
+
+commit 4f78ee2b0bebb9170a483927af9c7520ca67f912
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Apr 1 09:58:53 2008 -0400
+
+ Specs to verify conditional compilation in the compiler.
+
+commit 8dfece35e3bc83e14e92bfee9ea0ebabb795da70
+Author: Brian Ford <bford@engineyard.com>
+Date: Tue Apr 1 01:07:14 2008 -0700
+
+ Fix up language symbol specs.
+
+commit 29cc22f2c1f7ce2ce15a7f339d1159cf93510daa
+Author: Brian Ford <bford@engineyard.com>
+Date: Tue Apr 1 00:40:34 2008 -0700
+
+ Constant lookup only searches class or module (#457).
+
+commit 538611f2aa06a1cf1c3958583bd6a8487deee994
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Mon Mar 31 18:03:41 2008 -0400
+
+ Spec for empty loop body.
+
+commit fd0d1079671d7664de3a6a836c5e5624d487a4e1
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Mon Mar 31 23:47:40 2008 +0200
+
+ Spec for constant lookup on non Module or Class objects
+
+ This exposes the bug also described in ticket \#457
+
+commit 3b7cf550c70db2dd53cb58ef3efd2651ee352134
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Mar 31 21:53:40 2008 +0200
+
+ Added a couple of Dir.glob/Dir[] rubyspecs.
+ (Courtesy of Roland Swingler)
+
+commit bbfa77a8517390bdc807f41bfe6d101791980d8f
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Mar 31 19:04:09 2008 +0200
+
+ Fixed DRb rubyspecs (proper spec name, removed invalid file, better cleanup).
+
+commit d8a4fb0b16dc4c722cf148ff83bcad05fbb4af1e
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Mar 31 14:29:54 2008 +0200
+
+ Make sure Marshall#load rubyspec closes the file.
+
+commit 4082a7663eaef50000be46d909c22fbb97a1a3e8
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Mar 31 13:57:35 2008 +0200
+
+ Reverted new Range#step rubyspecs, since they fail on MRI and JRuby.
+
+ Partial revert "Fixes for Range#step."
+ This (PARTIALLY) reverts commit a6b06a67207c40ffa9ccf191c051fdf2fa0f5359.
+
+ The specs are reverted since they fail on:
+ MRI 1.8.6 pl 36 (Ubuntu default)
+ MRI 1.8.6 pl 114 (Current compatibility target)
+ MRI 1.8.6 from 1_8 branch
+ MRI 1.9 from Ruby trunk
+ JRuby 1.1 from trunk
+
+ The specs expect that to_f is invoked, but MRI and JRuby don't behave
+ that way. Furthermore, Float is not a special case. There are other
+ cases, like Rational. Take a look into MRI code, there is no special
+ handling for Float.
+
+ Please, test your spec updates at least against the current
+ compatibility target (MRI 1.8.6 patchlevel 114) to avoid problems.
+
+commit 6d9680ecaaa2a9aadd35699c8064bf6481acc107
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Mar 31 13:23:20 2008 +0200
+
+ Added new rubyspecs for IndexError out of String#[]=
+
+commit c8a52bb7cf191bb35efc89c560bdeced4241f015
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Mon Mar 31 04:38:49 2008 -0400
+
+ Split Regexp#=~, #match specs; they behave differently on match.
+
+ * #=~ Returns index, #match returns MatchData.
+ * Grammar fixes.
+
+commit 6c2727e928991cdf9f809cb5941c3afedb5171ff
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Mon Mar 31 04:07:17 2008 -0400
+
+ Fix Regexp#match, #=~ spec to actually be shared. Exposes #454.
+
+commit e258a2bccafffba57ab86d1c1a104839bda424da
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Mon Mar 31 03:30:48 2008 -0400
+
+ Spec to verify IO behaviour with an altered BufferSize from Le Huy.
+
+ * Moved spec to spec/core/io/ and simply used the first one.
+ * This problem seems to have been largely corrected.
+
+commit 7a39be8bea055464838ff24c70e170a91f8df68c
+Author: Ben Burkert <ben@benburkert.com>
+Date: Sat Mar 29 19:39:11 2008 -0500
+
+ Added spec for Module#define_method
+
+ Methods defined by define_method with a proc should have the
+ same scope for local variables as the proc.
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 12c639d90ff3d14f8010ca7c782612bd7c1777ab
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Mar 29 23:58:13 2008 -0400
+
+ Tony Arcieri's specs for inter-VM Actors.
+
+ * VMActor implements the Actor interface to work in Rubinius' Multi-VM
+ context: VMActors can reside on any VM instance.
+
+commit a0d0884aa3c9e7a6fa949cbde1cdf2392bc4ff23
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Mar 29 15:59:42 2008 -0400
+
+ Module#attach_foreign allows using a symbol to give the function name.
+
+ * Specs for the same.
+
+commit a5f397f38d6c9eafcac163c2cf678d5c55a6b79b
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Fri Mar 28 23:40:04 2008 -0400
+
+ Specs for FFI in general and Module#attach_foreign in particular.
+
+ * Very basic specs to verify that FFI in fact works correctly.
+ * We need to define what the behaviour should be in the case of e.g. an
+ incorrect function signature. Currently it may or may not cause SEGVs
+ depending on the exact usage. Remainder specs are in but quarantined.
+
+commit 3dc5c635b56bc599a718a94f990976b67ab52b6c
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Mar 26 02:01:12 2008 -0400
+
+ Specs for Module#attach_foreign.
+
+ * The method is a replacement for #attach_function but allows
+ giving the library name as well to access external libs.
+ * This acts a higher-level interface to FFI.create_function. The
+ "real" FFI specs will be written for that method instead.
+
+commit 677412353409ba4e5d67f19a3d095c62d009c88f
+Author: Brian Ford <bford@engineyard.com>
+Date: Fri Mar 28 18:04:40 2008 -0700
+
+ Added CType#isctrl, #toprint. Rework String#inspect, #dump.
+
+commit 87ba991b9b488b808ebf729b9e41765df76cc602
+Author: Brian Ford <bford@engineyard.com>
+Date: Fri Mar 28 15:09:11 2008 -0700
+
+ Reworked String#each and #sum. Added String#modified? and specs.
+
+commit 204d8ce1a792a61882e549953b5b878139ac9cda
+Author: Hongli Lai <hongli@plan99.net>
+Date: Fri Mar 28 23:32:18 2008 +0100
+
+ Spec: Marshal raises EOFError on loading an empty file
+
+ Signed-off-by: Michael S. Klishin <michael@novemberain.com>
+
+commit f6e698f96ce9e2a8c8abe856322add02931df8b7
+Author: Michael S. Klishin <michael@novemberain.com>
+Date: Sat Mar 29 02:20:23 2008 +0200
+
+ Tag new spec for ensure as failing
+
+commit ef7e4436389a0f4346b3a3bc5c275b653f46d6bb
+Author: Hongli Lai <hongli@plan99.net>
+Date: Fri Mar 28 23:22:44 2008 +0100
+
+ Add spec for exception handling inside ensure block.
+
+ Signed-off-by: Michael S. Klishin <michael@novemberain.com>
+
+commit f54c91f6cb7498fe44b1b05a1372d9f6ed3ea1ee
+Author: Stuart Halloway <stu@thinkrelevance.com>
+Date: Fri Mar 28 10:11:05 2008 -0400
+
+ Fixes Pathname#absolute? and #relative?.
+
+ * specs now pass
+ * underlying cause was corner case in File#basename
+ * new passing spec for corner case
+
+ Signed-off-by: Charles Comstock <dgtized@gmail.com>
+
+commit 0d4606d53d8fc0bcb2370bd648546abffd402673
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Fri Mar 28 16:45:47 2008 -0500
+
+ fixed CSV::Reader.parse spec to use local fixtures
+
+commit 35a15c6c85ebb6eabaec16e03aa88399061844e9
+Author: Alister Lee <rubinius@dev.shortepic.com>
+Date: Sat Mar 8 18:11:24 2008 +1100
+
+ Beginning of specs for CVS::Reader.parse
+
+ Signed-off-by: Charles Comstock <dgtized@gmail.com>
+
+commit d4161a379eab621e338a8c82f088b834756082e9
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Fri Mar 28 16:39:50 2008 -0500
+
+ removed csv/reader/parse_spec to commit alister lee's spec
+
+commit 534806c10a95435873efcb0d215732d7da4f2fd6
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Fri Mar 28 16:38:03 2008 -0500
+
+ mkspec generated specs for csv.rb
+
+commit a6b06a67207c40ffa9ccf191c051fdf2fa0f5359
+Author: Stuart Halloway <stu@thinkrelevance.com>
+Date: Fri Mar 28 06:09:34 2008 -0400
+
+ Fixes for Range#step.
+
+ * previously failing specs pass
+ * new spec added to cover float/int difference
+
+ Signed-off-by: Michael S. Klishin <michael@novemberain.com>
+
+commit 6886ec5851783c5364ff5bc464ee94071fc8535e
+Author: Michael S. Klishin <michael@novemberain.com>
+Date: Fri Mar 28 00:06:56 2008 +0200
+
+ Update stdlib and specs for REXML from 1.8.6 patchlevel 114 (see details!)
+
+ * Update stdlib/rexml to use REXML from Ruby 1.8.6 p114.
+ * REXML in p114 is screwed up: call sites were not updated
+ after REXML::Formatters::Transient#initialize arity
+ change. Ruby 1.8.x branch in SVN though has
+ completely different REXML layout and organization
+ (rev. 15833) so there's no way to fix it until we know
+ where REXML changes are headed in 1.8.x branch.
+ * Update REXML spec and tags for it.
+
+commit 3145a74a85d72f6ef8a93384a74d96a589bfb5eb
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Mar 26 22:27:41 2008 -0700
+
+ Rework and cleanup of various String methods.
+
+ Also, ensure that when Strings are converted through FFI
+ and passed to C functions, the char array is explicitly
+ terminated with \0.
+
+commit 9ba3e515b49729e0cb80181af9e28e3ce4c70e97
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Mar 26 18:40:57 2008 -0700
+
+ Shuffle some String methods. Add specs for and rework String#substring.
+
+commit 990d47b84bc6301be2a8bcbaccbae65ef697c417
+Author: Brian Ford <bford@engineyard.com>
+Date: Tue Mar 25 16:22:45 2008 -0700
+
+ Added String#compare_substring. Reworked String#chop! and #chomp!.
+
+ Also, to ensure that ByteArray instances that are accessible in
+ Ruby are properly handled by C functions, changed string_equal_p
+ to use strncmp instead of strcmp.
+
+commit f47c446daa136e6f31f5c590dd535ba22e89a0b2
+Author: Brian Ford <bford@engineyard.com>
+Date: Tue Mar 25 11:36:16 2008 -0700
+
+ Fix errors in String#count_table spec descriptions.
+
+commit 9425d0de9a7883c14de6ae9ae5db05ab92141ab9
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Wed Mar 26 22:38:48 2008 +0100
+
+ Guarded two failing specs on OpenBSD that also fail on MRI
+
+ MRI on OpenBSD also suffers from the 0.0 / -0.0 issue (the
+ GCC version on that platform too). The child reaping spec
+ also fails on both MRI and Rubinius
+
+commit 288a6e2ca3675a1e60bfd6b8b328c2a4d513c12f
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Wed Mar 26 22:15:16 2008 +0100
+
+ Fix Socket specs for more strict BSD behavior
+
+commit 63513d23f16ca7919b8605e016a3a941b79c0834
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Tue Mar 25 17:20:53 2008 -0700
+
+ oops! extra exclude
+
+commit a36a4bf8cde95c99282e07f46438430588288736
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Tue Mar 25 17:20:19 2008 -0700
+
+ really minor changes
+
+commit e9b759812deaf97e7fe5846c116d53f69b63e244
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Tue Mar 25 17:19:41 2008 -0700
+
+ Added the sucky parser spec--not passed yet
+
+commit 90eb74998e132373e6b96e3c66bfa909854e3ef0
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Tue Mar 18 17:41:50 2008 -0700
+
+ Added spec for 'a [ 42 ]'
+
+commit 2d34643c75b53b832e89d2473d501ab1c8a5df02
+Author: David Whittington <djwhitt@gmail.com>
+Date: Wed Mar 26 08:01:20 2008 +0000
+
+ Tagged Generator specs as unstable due to memory consumption
+
+ Each spec consumes > 60MB of memory. After looking at the specs there is no way
+ they should be consuming that much memory.
+
+commit 52d81e0593dbca8abfecefe2e9c3d2ab504cfe0b
+Author: Brian Ford <bford@engineyard.com>
+Date: Tue Mar 25 10:43:17 2008 -0700
+
+ Added String#copy_from primitive. Reworked String justify methods.
+
+commit 1aabda50ea82974b96a7032a0ea13865b2332b5d
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Mar 24 21:57:02 2008 -0700
+
+ Added Tuple.template and reworked String#tr and friends.
+
+commit bc7d9ccb8b8ca77d8479f325ea314fc09bc34907
+Author: Brian Ford <bford@engineyard.com>
+Date: Fri Mar 21 00:51:10 2008 -0700
+
+ Rework methods that behave like String#count.
+
+commit 1e5ac9a6818c972882e080aeb723a105108e0c57
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Mar 19 21:25:07 2008 -0700
+
+ Rewrite of String#casecmp, approx 2x faster.
+
+commit c39f2cb708169d35c2fbeb969ee3323c704f0566
+Author: Matt Palmer <mpalmer@engineyard.com>
+Date: Tue Mar 25 21:09:37 2008 +1100
+
+ Some specs for the timeout library
+
+commit cb69bdadeb10cf6b4b2c71a095562f8d8371d76d
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Mon Mar 24 17:55:23 2008 -0500
+
+ Small fix for Socket.getaddrinfo spec
+
+ Signed-off-by: Michael S. Klishin <michael@novemberain.com>
+
+commit 5c3a61edef3c456b8296e65f8e06026347339a36
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Mon Mar 24 17:06:36 2008 -0500
+
+ Fix for the socket's issue
+
+ Signed-off-by: Michael S. Klishin <michael@novemberain.com>
+
+commit f3fd9ac4eebd0bc2a0a06bbe06921463d03177eb
+Author: Federico Builes <federico.builes@gmail.com>
+Date: Mon Mar 24 14:10:46 2008 -0500
+
+ Fixes specs for Socket and adds a gethostname spec
+
+ * Changes hardcoded "localhost"s to Socket#gethostname calls.
+ * Adds a simple spec for Socket#gethostname
+
+ Signed-off-by: Michael S. Klishin <michael@novemberain.com>
+
+commit 7131328bc02057b16071a933fe98f331b27e00bb
+Author: Michael S. Klishin <michael@novemberain.com>
+Date: Tue Mar 25 00:24:01 2008 +0200
+
+ Applied slightly modified patch by Federico Builes:
+
+ * Add REXML::Document and REXML::Attribute specs
+
+commit cb464295e5accb00e783f7f9e2a0b10c64ad6579
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sun Mar 23 12:06:07 2008 +0100
+
+ Added new Range#step rubyspecs.
+
+ Excludes for rbx also updated.
+
+commit 7d181716ac3b92d8a31a20ec30daee455d36fc58
+Author: Charles Oliver Nutter <charles.nutter@sun.com>
+Date: Sat Mar 22 14:51:30 2008 -0500
+
+ Added order-of-evaluation spec and tags for rubinius failures.
+
+commit 5caf94ce6deb5e28c9a3de02e60a9b86cbdaf7ec
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Fri Mar 21 12:37:02 2008 -0500
+
+ tagged new specs for pathname
+
+commit 62f88983ee3fa1b09d8f7df56e35cbfdac6d2a06
+Author: Martin Stannard <mstannard@gmail.com>
+Date: Fri Mar 21 12:10:23 2008 +1100
+
+ added some specs for pathname library
+
+ there are failures in absolute and relative specs
+
+ Signed-off-by: Charles Comstock <dgtized@gmail.com>
+
+commit 655f61650bb299f38c9fd978594baa483fc0d0cc
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Tue Mar 18 16:22:24 2008 -0700
+
+ Reduced parser todos from 113 to 89
+
+commit f97b2fc2ee3310e81871200125bbd7e33c2636bf
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Tue Mar 18 13:51:31 2008 -0700
+
+ Moved sexp_expectations.rb to fixtures subdir
+
+commit 0a185e5ac48954cf4addae0c8f09dcb5be259f8e
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Mon Mar 17 17:55:50 2008 -0700
+
+ Added f'd up note about the spec failing
+
+commit 978f043e1ed3a2b7cb7d4129e0002be485b0a78c
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Mar 18 21:17:32 2008 +0100
+
+ Fix Process.groups spec
+
+ Process.groups can return an array with the same gid multiple
+ times on certain platforms (at least on FreeBSD and OpenBSD).
+
+commit 8812658dde5e317dfebd0ea3c159ad0a1b98e8e8
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Mar 18 21:02:00 2008 +0100
+
+ Update spec tags for ERB
+
+commit 47216560d4a980cbaac2855e0c5ee302e0754bf8
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Mar 18 20:53:16 2008 +0100
+
+ Update spec tags for IO
+
+commit 7d34f4053023d99c3be4964bfebb3a1c74cd40c9
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Mar 18 20:32:39 2008 +0100
+
+ Update spec tags for File
+
+commit 8a66bc6f5e378f49febb80fba37723a7de0d2475
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Mar 17 15:46:51 2008 -0700
+
+ Added specs for File.[l]chown/#chown, code for File.lchown.
+
+commit 960872ae163a5615f513c58d727a7fd93664673e
+Author: Glenn Davy <glenn@thor.local>
+Date: Mon Mar 10 10:00:40 2008 +1100
+
+ Make File.fnmatch respect case when using square brackets
+
+commit 0e32f8e224543a3c152b0351540eaa36fdfcdb06
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Mar 17 11:04:27 2008 -0700
+
+ Added exclude for failing spec added in b635fcf0.
+
+commit 62687753b239984acba4f0e80899ca75a8a08cfe
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Mar 17 10:56:31 2008 -0700
+
+ Fixes and specs for Module class_variables methods.
+
+commit b635fcf041707fe55a26b7709aef8dc1b2509161
+Author: Charles Oliver Nutter <charles.nutter@sun.com>
+Date: Mon Mar 17 12:52:13 2008 -0500
+
+ Add a simple Module#private spec.
+
+commit 2aa98e1df50bba768b57018f6e90c56fe39206f4
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Mar 17 13:28:17 2008 +0100
+
+ Make sure no processes left hanging after IO#close specs.
+
+commit 8f332dde4460c03c378f1d1ecc1fbae54557d8ee
+Author: Matt Palmer <mpalmer@engineyard.com>
+Date: Mon Mar 17 16:44:24 2008 +1100
+
+ Raise an Errno exception if a write fails
+
+commit 55c830063115e4455eeda3f8de639a7f7e0624f5
+Author: Matt Palmer <mpalmer@engineyard.com>
+Date: Mon Mar 17 16:42:16 2008 +1100
+
+ Raise IOError if we attempt to write to a readonly file
+
+commit ad64c0ea7598b8a4c62ba2dd435f70c976186a50
+Author: David Whittington <djwhitt@gmail.com>
+Date: Sun Mar 16 04:24:54 2008 +0000
+
+ Modified file type specs to search for sockets in /var/run instead of /var
+
+ Doing a find on /var could take quite a while + might do nasty things like do
+ finds on backup files etc. Running a find on /var/run should be faster and
+ safer.
+
+commit ff5e9d3b9d7f3e484211b66fff96e665ed13614b
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Mar 12 17:44:55 2008 +0100
+
+ Revert "Added simple spec for range splatting".
+
+ This reverts commit 9b3988436a21f61c86168a7566d472c4dfa22162.
+
+ The spec uses '=' instead of '==', and it verifies something
+ that is not true for MRI (1.8, 1.9) or JRuby.
+
+commit 004662e54477269a98475f84724972b82885d9cb
+Author: Brian Ford <bford@engineyard.com>
+Date: Sat Mar 15 01:09:43 2008 -0700
+
+ Exclude failing UNIXServer.new spec.
+
+commit 13340924519f607d9c48da04c3f3ab41a1de3e86
+Author: Brian Ford <bford@engineyard.com>
+Date: Fri Mar 14 18:14:06 2008 -0700
+
+ Tagged unstable Process.kill specs that cause hangup on linux.
+
+commit c4a4dc19a26db058594c8056933cdab42d4f26fd
+Author: Matt Palmer <mpalmer@engineyard.com>
+Date: Fri Mar 14 21:13:31 2008 +1100
+
+ Fix up IO#write spec so it works cross-platform
+
+ It looks like the Linux implementation of IO#write and IO#read are a bit
+ different from the OS X version, because the spec worked on OS X.
+ Presumably this tiny change won't cause any conniptions.
+
+commit 33890d9a77d5a34c15263f84b9b415ffc084815a
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Fri Mar 14 14:42:11 2008 +0800
+
+ Remove fail tags from passing ruby/1.8/core specs
+
+commit 4bdd3df099fe627d158f4c6d35e5a7df0a891e86
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Fri Mar 14 12:58:40 2008 +1100
+
+ Fix bug where stepping by line would sometimes skip a line
+
+commit 260190092afbcfadd1a6e1d6db1674ecf021b686
+Author: Matt Palmer <mpalmer@hezmatt.org>
+Date: Sat Mar 8 19:26:41 2008 +1100
+
+ Put in an explicit IO.new test for single-argument
+
+ Assuming that your UDPSocket tests passing will prove that IO.new takes
+ one argument might have been, in retrospect, a little retarded.
+
+commit 58216e07f0728415762fe5fbe98e1e984dfea31b
+Author: Matt Palmer <mpalmer@hezmatt.org>
+Date: Sat Mar 8 18:45:17 2008 +1100
+
+ Mark changing failures in the CI test suite
+
+ Fix up so that the CI doesn't fail as a result of my previous changes to the
+ UDPSocket specs.
+
+commit 36f91c5da132f309fbf6d047fd74ebd8aa7cbf22
+Author: Matt Palmer <mpalmer@hezmatt.org>
+Date: Sat Mar 8 17:26:49 2008 +1100
+
+ Rearrange the UDPSocket test cases for better separation
+
+ * open_specs now only contains a spec that calls UDPSocket.open;
+ * send_specs now has separate tests for ad-hoc and connection-oriented
+ sends.
+
+commit b40c1cf434bd0879f672ec1dc471f1e1dfaccc1c
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Mar 13 17:07:50 2008 -0700
+
+ Add (failing) Symbol#to_yaml spec based on ticket 322
+
+commit c0bcb0151379fe9858d0fafd2ef56cf1b08daff3
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Mar 13 16:23:37 2008 -0700
+
+ Apply ticket 351 and resolve ticket 350 (RbYAML bugs)
+
+commit a8d6e8cddfd8bc2dccaa93b25adfb31b39b96dba
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Thu Mar 13 16:25:55 2008 -0700
+
+ Removed all should_not raise_error from shared/time_params.rb
+
+commit 01f09f4e5697c4a775ac321a71d3b777196d9001
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Thu Mar 13 15:47:37 2008 -0700
+
+ cleaned up spec with new raise_error block form
+
+commit e965fc735311915dd43c47cc4853e163376cc6be
+Author: Lachie Cox <lachie@smartbomb.com.au>
+Date: Sat Mar 8 17:14:37 2008 +1100
+
+ enhanced syntax error to give same message as MRI
+
+commit 868b38152ca99189fce85542a9068c0d01ee4a41
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Thu Mar 13 15:07:33 2008 -0700
+
+ Added exclude for last patch applied
+
+commit 9b3988436a21f61c86168a7566d472c4dfa22162
+Author: Patrick Hurley <phurley@gmail.com>
+Date: Mon Mar 3 14:04:14 2008 -0500
+
+ Added simple spec for range splatting
+
+commit 3c7a017e173945d3f9b18d566bb1c3d6d04e97e4
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Thu Mar 13 17:18:39 2008 -0500
+
+ fixed tags for new constant specs
+
+commit a966436b7be78bc063e32bc16496f5cabbb0a152
+Author: Matt Palmer <mpalmer@hezmatt.org>
+Date: Sat Mar 8 14:56:58 2008 +1100
+
+ Make sure modules included in Object are found
+
+ Add a spec to make sure that constants from modules included in Object are
+ found. Evan is committing the fix for this separately.
+
+ Signed-off-by: Charles Comstock <dgtized@gmail.com>
+
+commit 4e0ddd3e701f68b592cb69972f7d587b90392913
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Mar 13 15:05:54 2008 -0700
+
+ Correct a 'defined?' spec added by ticket 388
+
+commit 407095d8ffbf0563fa46e5d4ed6a08423eddb2ad
+Author: Martin Stannard <mstannard@gmail.com>
+Date: Sat Mar 8 15:47:59 2008 +1100
+
+ Added tests where defined? method should return string descriptions of objects
+
+commit f366309a8fff28552d7d27101d8b3d7b4352e235
+Author: Gianluigi Spagnuolo <glgspg@gmail.com>
+Date: Fri Feb 29 10:42:42 2008 +0100
+
+ Fixed Array set element problem
+
+commit 42c22bf542edc8c8379587507fd9e35ba25b190c
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Thu Mar 13 17:00:31 2008 -0500
+
+ updated tags for new read specs
+
+commit 45c43a7ab3310a41b0b3367f4762a1bb55b02405
+Author: Ben Askins <benj@supernova.local>
+Date: Sun Mar 9 11:41:49 2008 +1100
+
+ Fix typo in file/open_spec.rb
+
+ Signed-off-by: Charles Comstock <dgtized@gmail.com>
+
+commit a221ea56325fe082154a629094abb27d40919a39
+Author: Alister Lee <rubinius@dev.shortepic.com>
+Date: Sun Mar 9 15:27:34 2008 +1100
+
+ Specs to expose defect in eof treatment in IO.read
+
+ Signed-off-by: Charles Comstock <dgtized@gmail.com>
+
+commit 4967adb3d49252aae75b6b57159fb5879ac75db1
+Author: Myles Byrne <myles@ducknewmedia.com.au>
+Date: Sat Mar 8 12:14:20 2008 +1100
+
+ Check existence of ArgumentError
+
+commit 45e46234da288052e639bb5c9c122874fd4d4e1c
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Mar 13 10:28:54 2008 -0700
+
+ Fix File[Test].size? and specs for it.
+
+commit d467bf21c4037784a21ba964b24c28fc80b34736
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 17:36:56 2008 +0800
+
+ Fix IO::foreach when separator is nil
+
+commit 70615e1c15692b8a8149e1616c802db9eb5bad11
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 17:32:40 2008 +0800
+
+ Fix IO#flush to raise IOError on closed stream. Remove empty tag files.
+
+commit 9c9e7f422c98bf6add6c9a426ae25e3a6dbced85
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 17:29:31 2008 +0800
+
+ Fix IO#fcntl to raise IOError on closed stream
+
+commit 215d600002948efb949422c0163aa9bbe5790507
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 17:27:43 2008 +0800
+
+ Fix IO#dup to raise IOError on closed stream
+
+commit 879ee8124a2ad8ce83bcd9c51b2d6df0baecb40d
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 16:29:03 2008 +0800
+
+ Fix a bunch more IOError when closed stream
+
+commit 487d9561992eb03c3d12de5128772cd194b37b8b
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 16:26:33 2008 +0800
+
+ Implement IO#read_nonblock
+
+commit 15c58fa2c47d2dc61b3dac436ab3b56a727b7dc5
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 15:46:22 2008 +0800
+
+ Fix remaining IO.read specs
+
+ * Passing nil to length treats it as no length limit
+ * Passing nil to offset treats it as 0
+
+commit 9daee4f9c3b62db34b07d74171d1017fa823533c
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 15:32:13 2008 +0800
+
+ Fix IO#sync to raise IOError on closed stream
+
+commit 2ac848c09e055b3eacc8bb18f713d56715484063
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 15:24:36 2008 +0800
+
+ Fix IO#sync to raise IOError on closed stream
+
+commit 36aa8577603f1d8ca76344fc3e889bb7c991bfe9
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 14:51:13 2008 +0800
+
+ Fix IO#sysseek to raise IOError on closed stream
+
+commit 3307f5a4db121c2097b450278bc3cf19550f267b
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 14:48:30 2008 +0800
+
+ Fix IO#pos and #IO#tell to raise IOError, move their specs to shared
+
+commit 72890065371f3e1d1cde43618a3da04c900749aa
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 14:39:21 2008 +0800
+
+ Implement IO#to_io
+
+commit 4977bd1f22278e19ba69203c2545ad97c297ae23
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 14:33:03 2008 +0800
+
+ Remove IO#isatty tag file also, since they are sharing the same specs
+
+commit 5dd3115465852ddb03b7100b21739f9d38f0ee58
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 14:29:13 2008 +0800
+
+ Fix IO#tty? should raise IOError on closed stream
+
+commit 063f56b4c402180c2c989a15b75fe7a15d4c5c61
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 13:55:55 2008 +0800
+
+ Make IO#syswrite use the shared IO#write specs
+
+commit 22de413f6cccb3eb100fd29da90c2ded84ea19f3
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 12:08:50 2008 +0800
+
+ Update IO#write_nonblock's tag
+
+commit 25a5ac7e9123512e87e6460f1fa5ecbcfc7349b5
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 12:07:22 2008 +0800
+
+ Pull out 2 differences between IO#write and IO#write_nonblock specs
+
+commit a40dbd0f36f0237bc27c905c399aba1e62bbfa70
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 11:39:03 2008 +0800
+
+ Alias IO#write_nonblock IO#write and make IO#write specs shared
+
+commit 1c8eb4bc04405753dd607af1f5d231df01fd2536
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 11:06:09 2008 +0800
+
+ Make the mock return a string to prevent a coercion error
+
+commit a85b2105c826a7d39dc45c90cad37faf75baac86
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 10:39:00 2008 +0800
+
+ Fix IO#write should raise IOError on closed stream
+
+commit e8c8af1aa888dc3e5600cad64f03c09aebaf6d22
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Mar 13 10:34:08 2008 +0800
+
+ Fix IO#to_i should raise IOError on closed stream
+
+commit 49d48c381b7ed0f2576c2c5bff3ac8825a0dd49e
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Wed Mar 12 17:56:26 2008 -0700
+
+ Fix the insanity
+
+commit 646136d0f75b165a3a62266791556d3f4f03c835
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Wed Mar 12 17:04:30 2008 -0700
+
+ Finally got compiler specs passing
+
+commit 052bbcbe4f51b322ae44dc387320f9b4964d74cd
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed Mar 12 16:23:58 2008 -0700
+
+ Correctly set Syslog mask in Syslog::open and add crappy spec for it
+
+commit dbabc5bda94a2bd77b2cb777666d286155c75ee0
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed Mar 12 14:55:08 2008 -0700
+
+ Correct Syslog specs and modify syslog.rb to pass them
+
+commit 5b8bee08f2a19d6f25df98183a24745ed33ed519
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed Mar 12 14:54:38 2008 -0700
+
+ Modify Kernel#load specs so that they pass on 1.8.6-p111
+
+commit b96974693cee75772b09052f8ec7110a000c2429
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Wed Mar 12 14:00:42 2008 -0700
+
+ Fixed specs for wilson's compiler encloser changes
+
+commit 2a21597719bea1ea7db27a552ea6dfb6865963d7
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Wed Mar 12 14:00:06 2008 -0700
+
+ half work on pretty_inspect
+
+commit 6e398ca491b67a6c468798fd92a9764f70bc68a8
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Wed Mar 12 15:18:02 2008 +0800
+
+ Share String#to_a a specs with String#entries
+
+commit 2ff775cbcf2ade4315fbdbb37fa78ee84a1e645a
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Wed Mar 12 11:59:26 2008 +0800
+
+ Add String#to_a specs
+
+commit 4f1204bac224ad28375f06e5fb77156367895156
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue Mar 11 19:53:32 2008 -0700
+
+ Spec and implement Array#pack 'v' option
+
+commit 91d51783f44c3a9b1adfe03b7b9fa35476494ce1
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Wed Mar 12 12:45:56 2008 +1100
+
+ Debugger::Output#wrap should handle width of 0
+
+commit 51c316464ad44cadad7ecd997ce45e8392695f4c
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue Mar 11 15:26:52 2008 -0700
+
+ Implement support for :postexe nodes (END { some_code })
+
+commit 569dd9f10d5194c22335ce58a678d1f9c73f91d0
+Author: oleg dashevskii <be9@be9.ru>
+Date: Wed Mar 12 03:54:39 2008 +0600
+
+ Import matrix lib with specs (#389 and #400)
+
+ Original patches by Chris Lloyd and matta.
+
+commit 6beb50b7cc2dd3a0f57f3dee45767bb363082159
+Author: Brian Ford <bford@engineyard.com>
+Date: Tue Mar 11 13:20:07 2008 -0700
+
+ More Integer#times specs.
+
+commit 746d89d6d55c82f26be08f182301926efd62d362
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Mon Mar 10 15:39:26 2008 -0700
+
+ Correctly set the enclosing class for evaled code.
+
+commit 218cc7fbdd1b5d1c52248e65817752b8a50821ad
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Mar 10 04:46:00 2008 +0100
+
+ Added JRuby speciifc guard to singleton rubyspecs (JRUBY-2239).
+
+commit 25e3f23e78f2b17e02d2c0a058925f8a0ec0d790
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Mon Mar 10 11:25:38 2008 +0100
+
+ Be sure not to close the socket before the Errno.handle call
+
+ Also a small fix for a spec that fails on OpenBSD
+
+commit 9e7fdf3b0040971f7b8402b9cf5422efaedb2f4f
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Mon Mar 10 09:54:16 2008 +0100
+
+ Fix TCPSocket#new spec, BSD systems make a distinction between IPv6/IPv4 localhost
+
+commit e5512b2a7725a67471eba086b107b0f4b1f136b2
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Mon Mar 10 08:35:50 2008 +0100
+
+ Fix for failing unpack_sockaddr_in spec on Linux
+
+commit b9eab2266e5d1f073b6f876710dc9e848fe25b0c
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Mar 9 23:37:05 2008 +0100
+
+ Remove spec tag for now fixed Hash.allocate
+
+commit b6ba9a757b0531791424df38bce6587a53db6002
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Mar 9 23:12:16 2008 +0100
+
+ Remove tag for now correct Fixnum#[] spec
+
+commit 6785c2b44da90d95ef77e98cba42a953828b622f
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Mar 9 23:11:37 2008 +0100
+
+ Fix Fixnum#[] specs
+
+commit 0aa09ce9b7269d54cdef583a2eaf0cb57c32f773
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Mar 9 23:05:21 2008 +0100
+
+ Removed tags for working Socket specs
+
+commit 18b27b0ebdc3713962771ca75c1321cabee08d61
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Mar 9 22:48:45 2008 +0100
+
+ Untag now working IPAddr specs
+
+commit f4c0d08bec8fb2db7d130363b0609de7b7720d7e
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Mar 9 19:56:25 2008 +0100
+
+ Slow IPAddr specs are now fast
+
+commit ff71385a67b2853130e63f9942bcea6ac69d591f
+Author: Eero <ruerue@yawn.kittensoft.org>
+Date: Sun Mar 9 10:35:27 2008 -0400
+
+ Specs for #412. Array#sort and #sort block form calls #<=> on elements.
+
+ * Block form should not expect anything of the elements, all is
+ done through the return value of the block.
+
+commit e6edd1bb4bc52053bdb834d52e31fa185f2a2d62
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Mar 9 14:51:00 2008 +0100
+
+ Updated tags for IPAddr because of fixed bit operations
+
+commit 4f59fa9bd187822cd836aa046bb8fd40e4412c30
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Mar 9 14:49:02 2008 +0100
+
+ Fix Fixnum and Bignum shift operations to match MRI
+
+ Added behavior for the edge cases, but took a different
+ approach than the LH tickets. I don't think we should
+ change coercion functions for this.
+
+commit ad8c630662dcb611cd955db08a6f4d53d1dc0dfd
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Mar 9 13:43:38 2008 +0100
+
+ Fix Bignum#& and specs for Fixnum AND, OR and XOR
+
+commit 2529acd5e1cc8e61bd995e00834ee1f6941b1d9d
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Sun Mar 9 14:18:04 2008 +1100
+
+ Fix require_spec and load_spec.
+
+commit 57c7ded8e4d9567aa3c392e8a8262389387ebbfb
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Sun Mar 9 12:25:40 2008 +1100
+
+ Don't spec .rba require behavior in spec/ruby/1.8.
+
+commit ac630b23da01dcc3a1de1bfa06bac4d301a5031b
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sat Mar 8 17:23:34 2008 -0800
+
+ Better fix for calling to_proc on BlockPass nodes
+
+commit c17b32d44be8452cd867a8212a0fd8bb49c94821
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sat Mar 8 16:34:02 2008 -0800
+
+ Tag failing Method spec for CI
+
+commit c5d4a3b8f84b7558a5dfedb699a1a3ee4d61f118
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sat Mar 8 16:26:58 2008 -0800
+
+ Call Proc.__from_block__ on block_pass arguments
+
+commit a63f457821e67d138d9cf1c5ac8b0760cb25bfc2
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Sun Mar 9 10:42:51 2008 +1100
+
+ Remove support for zip rba files, libzip. rake clean required.
+
+commit 142222e41bddd2138d82f349f73dbc0fe2cf3fc2
+Author: Charles Nutter <headius@wlan100.headius.com>
+Date: Sat Mar 8 16:23:37 2008 -0600
+
+ Adding a spec for Method#to_proc proc used in define_method.
+
+commit b748efa9904baf0be26aa5b7297fc8ba76e46a74
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sat Mar 8 13:09:44 2008 -0800
+
+ Fix Module#method_defined? and friends for accessors
+
+commit 9b9d8216014c95eb7b4a925e93d0db8e9f5fd308
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sat Mar 8 10:46:18 2008 +0100
+
+ Adedd a couple of GzipReader#rewind specs.
+
+commit 4612812bde4a2fccbaa72ea54ef76c7d964d216b
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sat Mar 8 15:49:57 2008 +0100
+
+ Fix the Array#pack specs, network order is the same everywhere
+
+commit a720bba1619deb4358b453f58913d30a1a311b07
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sat Mar 8 15:27:47 2008 +0100
+
+ Fix Sprintf for positive non decimal notation
+
+ This fix combined with the pack/unpack implementation for
+ type n also fix some IPAddr specs.
+
+commit caef838aca82665d4c2f691e4873e339a9c7238d
+Author: Lachie Cox <lachie@smartbomb.com.au>
+Date: Sat Mar 8 12:47:19 2008 +1100
+
+ updated Array#pack specs to work on big endian machines
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit e3763469a224b4b3668bc1ddef2d982245787646
+Author: Lachie Cox <lachie@smartbomb.com.au>
+Date: Sat Mar 8 12:42:36 2008 +1100
+
+ Added implementation of pack schemes for "n" and added handling of multiple items for "i","s" and "l"
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit 41b26c49f5a16377af2c677eb702d665dd062a56
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Sat Mar 8 15:35:10 2008 +1100
+
+ Fix IO#pos EOF spec. Pair: Lincoln, Evan.
+
+commit 1e039fb5c9bcff987769c8644ec47c30aa250952
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Sat Mar 8 14:53:01 2008 +1100
+
+ Fix Zlib::GzipWriter#finish. Pair: Lincoln.
+
+commit 8551da47a01ef24eaf31fac55253fb05fe81cfcd
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Sat Mar 8 14:21:08 2008 +1100
+
+ Add Zlib::GzipReader #eof?, #pos, #read w/length
+
+commit a4dba8317311cc3a51231895b2eaea09daaa61be
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Sat Mar 8 10:41:33 2008 +1100
+
+ Ensure #pos clears internal eof flag
+
+commit 407e1a4191da6ecd59c1347198a60be2556e043b
+Author: Brian Ford <bford@engineyard.com>
+Date: Fri Mar 7 17:04:09 2008 -0800
+
+ Tweaks to LookupTable. Converted Errno::Mapping to use LT.
+
+commit eb937c8f1041884e412e3d074387ca9f14bb03ef
+Author: Brian Ford <bford@engineyard.com>
+Date: Fri Mar 7 13:48:45 2008 -0800
+
+ Fixed LookupTable#delete. Added LookupTable#entries, #dup.
+
+commit d7d9bfd01180cf2c4fc74d2709f71fc7dd59f2f6
+Author: Brian Ford <bford@engineyard.com>
+Date: Fri Mar 7 15:03:14 2008 -0800
+
+ Bandaid fix for failing #autoload specs.
+
+ These need to be properly scoped. However, changing
+ :A to ModuleSpec:A causes a sigbus.
+
+commit aea5cc446cd2c1b0cbd29e606b21b6d5959eb5ee
+Author: Caleb Tennis <ctennis@engineyard.com>
+Date: Fri Mar 7 16:18:19 2008 -0500
+
+ Add rb_gv_get and rb_gv_set, plus specs.
+
+ Add rb_set_safe_level, rb_secure, and rb_safe_level methods, and specs.
+
+commit cd0b8969487af84a4f40466714dab2d5a1efc224
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Thu Mar 6 17:11:20 2008 -0800
+
+ excluded
+
+commit e40f2bb09d8e3137de2856cb1e9c9438945603dc
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Thu Mar 6 17:11:00 2008 -0800
+
+ More specs to test out const scoping with eval
+
+commit 3926add9039d1af4a60b633ef8805d471f28e02f
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Thu Mar 6 17:01:21 2008 -0800
+
+ Further clarified StringIO#getc specs.
+ They weren't really testing what they were doing.
+ Fixed StringIO#getc. now properly pushes single chars and sets @pos so it can be mixed with puts/write as needed
+
+commit d2d3750c4960d4a6f2a5d2b16b8bae3d598fbe36
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Thu Mar 6 19:16:24 2008 -0500
+
+ Add rb_define_global_function to subtend, with tests
+
+commit 4ab5cc17b70b6569cf9311142d4b278dedfd0a64
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Mar 6 09:53:20 2008 -0800
+
+ Added LookupTable and specs.
+
+commit 1ca8a272137ed7020cb977bf51dd2b7164ccbd7e
+Author: Charles Nutter <headius@wlan100.headius.com>
+Date: Wed Mar 5 17:28:40 2008 -0600
+
+ TCPServer.new coerces non-integer port to string and uses getservbyname logic.
+
+commit f0c03880972c19d1a12367dc51ed77f69d9ce8ca
+Author: Charles Nutter <headius@wlan100.headius.com>
+Date: Wed Mar 5 16:44:33 2008 -0600
+
+ Add a couple specs for killing/raising in a thread blocked on accept.
+
+commit 9f80ef157851671727653f46225b99af5d1a259e
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Mar 4 21:26:33 2008 +0100
+
+ Proper spec for %u with negative bignums and comments on MRI behavior.
+
+commit 3f9c36081c9b62bcde40206e64afdc2ac088bee8
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Mar 4 19:09:56 2008 +0100
+
+ Update tags for fixed File#chmod specs
+
+commit 735e818c38f8cefe0cd90514dac5282845a67dd4
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Mar 4 15:13:23 2008 +0100
+
+ Improve testing of coercion in File#chmod specs
+
+commit 77a717f5962b2965ad9146e16cb36bedac891c80
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Mar 3 18:16:04 2008 +0100
+
+ Adjusted syslog specs to better handle impls that don't provide syslog.
+
+ For example, JRuby does not provide syslog (yet).
+
+commit 605bdc53e9dd4fb95dae6557d9ee6f9e2b8ceb80
+Author: David Whittington <djwhitt@gmail.com>
+Date: Mon Mar 3 08:44:33 2008 +0000
+
+ Modified Bignum threshold specs to take into account platform wordsize
+
+commit 0af27d11d7dd68cfe49985dc4588933cc41f4fc8
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sun Mar 2 16:40:15 2008 -0500
+
+ Tag headius's new to_proc spec as failing
+
+commit b1caeeac673451a960917bb699a20e74cf488432
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sun Mar 2 13:30:35 2008 +0100
+
+ Adjusted Kernel#catch test a bit, to make it more generic.
+
+commit 60f9544ade9d6e71fe3e423ab82cc87838478032
+Author: Charles Nutter <headius@wlan100.headius.com>
+Date: Sun Mar 2 04:36:53 2008 -0600
+
+ Add a spec for #363, & not coercing using to_proc.
+
+commit 70aa320f7f5bc75ed95362b0fb6d724e64224a88
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sat Mar 1 17:17:55 2008 -0500
+
+ Tweak new Marshal spec to pass on MatzRuby
+
+commit 35476e1bde23de26c01df409b750e91ef981fefc
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sat Mar 1 17:11:53 2008 -0500
+
+ Tag new failing Marshal spec
+
+commit d9f83819f1ed2505740ae0737199fecab29809bb
+Author: Jared Luxenberg <jared@jaredlux.com>
+Date: Sat Mar 1 16:20:18 2008 -0500
+
+ Added specs for marshalling subclasses of Hash with init parameters
+
+ Test that Marshal.dump gives correct output for such an object (passes)
+ Test that Marshal.load is able to deal such an object (fails)
+
+commit 6039a3bd457c5d3dc99f5935999da574d17f1e5d
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sat Mar 1 16:20:08 2008 -0500
+
+ Tweak Process.setrlimit spec for odd Linux platforms
+
+commit 25cfa6a96315ee203d06381ee3ddb76b60023360
+Author: Chuck Remes <cremes.devlist@mac.com>
+Date: Sat Mar 1 10:24:55 2008 -0600
+
+ Fixes a race condition on OSX when "find"-ing character devices
+
+ - on OSX the spec fixture would return /dev/fd/0 as a character
+ device when run from the command line. This always succeeded.
+ When run as a subprocess (like from cron or rubuildius'
+ IO#popen) then OSX uses /dev/fd/0 and /dev/fd/2 for capturing
+ stdin, stdout, stderr and others in that environment. While
+ the fixture would "find" /dev/fd/2 as a character device, by
+ the time the assertion tested it the underlying OS would change
+ it to another device type causing the assertion to fail. This
+ is just bad luck. We now grab the #last device found rather
+ than the first.
+
+commit b6e95321df023ac989c4e5bb926ec55493260bc9
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Feb 29 16:04:10 2008 +0100
+
+ New rubyspecs for IO#ungetc.
+
+commit 9bd2f0740c71d426cfa3c3636c2451762f640c14
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Feb 28 21:59:35 2008 -0800
+
+ Specs for Hash.allocate. Fix awaits replacing Hash with LookupTable in core.
+
+commit c1d979639bfc19072351211815ffd5c8da772dcd
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Feb 28 21:56:33 2008 -0800
+
+ Specs and fixes for Module.allocate.
+
+commit 904fd6136f00bab5fec62e8e702a0508dec44bac
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Feb 28 19:45:39 2008 -0800
+
+ Specs and fixes for Array.allocate.
+
+commit 776a24f0d14bbb5127c804cf0579960335c1a049
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Feb 28 19:35:55 2008 -0800
+
+ Specs for String.allocate and fixes to make them pass.
+
+commit fa35211f357ff1b9660a318c12b86ca156c5f26d
+Author: Ari Brown <ari@aribrown.com>
+Date: Thu Feb 28 20:27:55 2008 -0500
+
+ Moved stdlib/syslog.rb to lib/syslog.rb . it works!
+
+ * everything runs! yay!
+
+commit 8f103a6f9d7a168e37d1063e40bee960d64fc609
+Author: Ari Brown <ari@aribrown.com>
+Date: Thu Feb 28 19:42:11 2008 -0500
+
+ Added specs and the constant module for stdlib/syslog.rb
+
+ * added some specs for that which is testable
+ * fixed the constant module so the constants are defined
+ * fixed 'undefined method' problem in #write (private)
+
+commit 0c89dc90fdcb7933169e23462197d59f9627f510
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Thu Feb 28 14:31:05 2008 -0800
+
+ Added basic throw/catch specs. Fixed raised NameError to contain the name
+
+commit c8f4db4270984b60a087dd423c9e0da3e3760622
+Author: Phil Hagelberg <phil@hagelb.org>
+Date: Thu Feb 28 14:11:30 2008 -0800
+
+ tag failing proc spec
+
+commit a1591319696385191f3301516d2f8265cd8fedcb
+Merge: f167f8f... 3f1acce...
+Author: Phil Hagelberg <phil@hagelb.org>
+Date: Thu Feb 28 13:43:52 2008 -0800
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit f167f8f6f7f3a1b8804a5452643236a23c0ce4c4
+Author: Phil Hagelberg <phil@hagelb.org>
+Date: Thu Feb 28 13:43:40 2008 -0800
+
+ failing spec for returning from procs
+
+commit 3f1acce781c0dcf43698441036a085a0cef02d29
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Feb 28 16:14:55 2008 -0500
+
+ Basic support for UNIXSocket and UNIXServer
+ Fix some 'Errno' typos in socket.rb
+
+commit afbf38613364436630933753d99ee94c03b85074
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Feb 28 21:34:10 2008 +0100
+
+ Added specs for File.fnmatch with case-sensitive brackets.
+
+commit 28323bda3d1f3295371b6ea99ed8ba6ee15661bb
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Feb 28 20:47:51 2008 +0100
+
+ Added specs for File.fnmatch with '**/' patterns.
+
+commit 893ff4729d024198d5b423cc4426153f49cb5ebe
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Feb 28 11:30:49 2008 -0800
+
+ Fixed lookup of class variables defined in metaclasses.
+
+commit dee531b18d96199d608d8e2e8e27f54ef500a716
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 27 21:47:13 2008 -0800
+
+ Additional Symbol#inspect specs. Another try at making them pass.
+
+commit 3bfb705b709ab35593684a68b35fb0ee8e1e01d7
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 27 21:46:37 2008 -0800
+
+ Silence 'woot' echo on ubuntu from #system specs.
+
+commit 7fb76f2c4a9fb0c5695a38b90150ea6f50097237
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Wed Feb 27 18:05:48 2008 -0800
+
+ Fixed Symbol#inspect from over quoting
+
+commit 4ac32e4c9d0ff55aad50a00944f1a64931cfd1c6
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Wed Feb 27 17:18:09 2008 -0800
+
+ Added some pretty rude specs for Kernel#system and got them to pass.
+ Fixed a wierd problem with system/exec not cleaning up the fork process right
+
+commit 73be3b88af1ac96a6d4afabddd2871cfc4691eec
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 27 15:36:17 2008 -0800
+
+ Fix String to properly initialize backing store when subclassed.
+
+commit 5ab2f9e594b7e66a04028e60f3517488e345f508
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 27 15:04:09 2008 -0800
+
+ Scope classes used in String specs.
+
+commit e45d58100850443fedada905f654bae3f4144790
+Author: Charles Nutter <headius@wlan100.headius.com>
+Date: Wed Feb 27 17:04:58 2008 -0600
+
+ Add /devices to find commands; Solaris uses /devices instead of /dev.
+
+commit 1403477197873d613cfb93d644f78b4067d180d3
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Feb 27 20:21:48 2008 +0100
+
+ Adjusted Env spec, to be able to run it on Solaris.
+
+ grep is replaced by egrep, since older greps don't
+ take -e parameter (like on Solaris).
+
+commit b239a3b615d341f982a7a4a3a1b1200d95f79684
+Author: Adam Shelly <adam.shelly@gmail.com>
+Date: Wed Feb 27 04:09:24 2008 -0500
+
+ Amending specs for Array#pack('U')
+
+ * rbx is now passing most specs
+ * failing specs are due to String#unpack.
+
+ Signed-off-by: Brian Ford <bford@engineyard.com>
+
+commit 328c40e0f24601e739f404ab252652deca477513
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 27 02:46:21 2008 -0800
+
+ Fixed Array instantiation to work with subclasses.
+
+commit 96c4ea885fbd075765b9d234de2754df3c857b07
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Wed Feb 27 09:26:25 2008 +1100
+
+ Move Debugger::Output specs to match new location of class
+
+commit c59f16f34f47860b200c6de4a2c1144c566de3dd
+Author: Brian Ford <bford@engineyard.com>
+Date: Tue Feb 26 10:23:14 2008 -0800
+
+ Exclude new failing Array specs.
+
+commit 27248a45f079fd5a8cdb9ee71d008d135dcbe63d
+Author: Charles Nutter <headius@wlan100.headius.com>
+Date: Tue Feb 26 00:10:29 2008 -0600
+
+ Add additional Array tests from BFTS.
+
+commit a0e156f4c5bc12bf39950afeb58a6962b37efaa7
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Tue Feb 26 16:53:01 2008 +1100
+
+ Fix Debugger help output formatting to use wrapping
+
+commit fa5304d42c72a07b09cece99cb22c90f6b399a51
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Tue Feb 26 13:22:31 2008 +1100
+
+ Add wrapping to debugger column output
+
+commit 1a5d830b41eef37bb78168c959dd5b2f0757fde4
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Feb 25 18:58:11 2008 -0800
+
+ Conform Bignum#div, #divmod to weird MRI maths.
+
+commit eb5c6e367990bfdd193bcdf3055009f3e3e1aeaf
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Tue Feb 26 13:23:29 2008 +1100
+
+ Fix Debugger specs to pass on ci
+
+commit a2feff6782a052a9b71da90e9d4e1b2d991cc598
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Mon Feb 25 18:53:24 2008 -0500
+
+ Patch by Jos Backus (josb) - Closes ticket 364 (FreeBSD warnings)
+
+commit cee08883cc3de2e41a88b506f7d7f8d40697eaa2
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Mon Feb 25 13:20:21 2008 -0800
+
+ Fixed autotest churn by removing empty.txt and moving to /tmp
+
+commit f26bb0c4e3b8435a853a9f4843173748d98075fd
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Feb 25 11:25:10 2008 -0800
+
+ Add the rest of spec/* directories to CI process.
+
+commit 29f36833e79de6115c27d744adf158e1b3ba42f0
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Feb 25 00:29:45 2008 -0800
+
+ Excludes for spec/kernel, spec/debugger to run with CI.
+
+commit 12bbdf70af31d5168c2df0a9b53651f94b36899d
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Feb 25 00:22:13 2008 -0800
+
+ Excludes for subtend specs so they will run with CI.
+
+commit 0cbc2b1f20d8aee7ea74eb14e1f9cf242f8b47d5
+Author: Brian Ford <bford@engineyard.com>
+Date: Sun Feb 24 23:57:56 2008 -0800
+
+ Remove specs for removed Compression::ZLib.
+
+commit 1b4fbc76c2eb84e5cb45562f54ac105784f9e134
+Author: Brian Ford <bford@engineyard.com>
+Date: Sun Feb 24 23:49:57 2008 -0800
+
+ Conform Ar specs.
+
+commit f8e62002711c3cfd8024faca497775f7253a326a
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Mon Feb 25 05:24:54 2008 +0100
+
+ Add a second case for truncating IO buffers that specifies too-small size.
+
+commit 9f3e25289cc52cd3f3fb240de1ad82a16a8b135c
+Author: Nikolai Lugovoi <meadow.nnick@gmail.com>
+Date: Tue Feb 12 23:19:27 2008 +0200
+
+ Fixes for String#to_sub_replacement:
+
+ * removed String#replace_slashes
+ * using plain byte-by-byte scan instead of regexps to detect and handle backslash escapes
+ * better handle unknown escapes and cases like '\\\1'
+ * updated specs for String#sub
+
+ Signed-off-by: Brian Ford <bford@engineyard.com>
+
+commit d87df0b7634ae37f85fc8f2795e4c8c425614b11
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Mon Feb 25 02:57:27 2008 +0100
+
+ Add a spec for Enumerable#inject with a *arg; JRUBY-2162 exposed it.
+
+commit f04fcabf8c064dfcbf3b118bdc83289da169a30c
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Feb 24 21:24:51 2008 +0100
+
+ truncate behaves different on OpenBSD, changed specs according to MRI behavior
+
+commit b74a2f45b32a02469d61d4ace04912ec25f19543
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Feb 24 20:18:02 2008 +0100
+
+ Looks like Darwin does provide Process::RLIMIT_AS
+
+commit 7113973abff64eeb1304b15be46f07d301d84f3f
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Feb 24 18:25:55 2008 +0100
+
+ OpenBSD doesn't provide Process::RLIMIT_AS, so this spec should be excluded
+
+commit 49b72719bf5c732f4aa2ad0d70e5a224556fb471
+Author: oleg dashevskii <be9@be9.ru>
+Date: Sun Feb 24 11:04:07 2008 +0600
+
+ Spec for method taking lambda and block.
+
+ * should raise SyntaxError
+ * passes on MRI
+ * fails on rubinius
+
+ Signed-off-by: oleg dashevskii <be9@be9.ru>
+
+commit 60bbc8506d70571249972dbf124df520f0a4a476
+Author: Chuck Remes <cremes.devlist@mac.com>
+Date: Sat Feb 23 10:23:09 2008 -0600
+
+ Fix unpack_spec expectation for little-endian byte ordering
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit 17e45cee97057684e6c24608f97de48c28947384
+Author: Chuck Remes <cremes.devlist@mac.com>
+Date: Sat Feb 23 09:44:37 2008 -0600
+
+ Fix unpack to use native host byte order for formats /ILQS/
+
+ - unpack_spec had a bad expectation on little-endian platforms
+ - unpack_spec got some updated description strings to correctly identify
+ the host byte ordering expected in the spec
+ - kernel/core/string.rb now unpacks formats /ILQS/ in the platform's native byte
+ ordering
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit 1540cb7caa0f200ed6d318971fb7302cd089e27d
+Author: Chuck Remes <cremes.devlist@mac.com>
+Date: Sat Feb 23 08:10:00 2008 -0600
+
+ Add some missing endian guards to the unpack_spec
+
+ - in my haste, forgot one set of guards around some specs
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit 8488676fc0dac5db5d01dd92e061476226d58bd1
+Author: Chuck Remes <cremes.devlist@mac.com>
+Date: Sat Feb 23 00:19:33 2008 -0600
+
+ Fix several Array#pack and String#unpack bugs related to byte ordering (endiannes)
+
+ - added a small utility method endian? to the kernel module; determines host byte
+ ordering by taking a symbol (:big, :little) and comparing it to Rubinius::ENDIAN
+ - modified Array#pack to check for the native byte ordering for /ils/i formats
+ - modified String#unpack to use native byte ordering for /DdFfIiLlQqSs/ formats
+ - modified String#extract_number to do special processing for big-endian platforms
+ and for formats using native byte ordering on a big-endian platform
+ - added little_endian and big_endian guards around several String#unpack specs;
+ now passes running against MRI and rbx
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit f8146d29bfdf67349f3f9c0c7105ce595981255f
+Author: Gianluigi Spagnuolo <glgspg@gmail.com>
+Date: Sat Feb 23 12:44:25 2008 +0100
+
+ Added some test to Regexp.quote to manage tab and white space
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit 714efa8574687e1fd31f904a4f35cce8056719f5
+Author: Brian Ford <bford@engineyard.com>
+Date: Sat Feb 23 00:01:38 2008 -0800
+
+ Fixed Digest specs to pass with RSpec.
+
+commit a0fe2f7fa080729b77b32ffe21be5705a162ed71
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Fri Feb 22 22:26:52 2008 -0800
+
+ Remove ffi_decode_sockaddr, replace with existing ruby code.
+
+commit b2baf0911e4a88ba2f6c4cb8e3e31d2a3aa1c6bf
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Fri Feb 22 17:06:18 2008 -0800
+
+ Move Ar to kernel/core.
+
+commit 01baf002a8c7bd6e249b9477c1f78e6b99a67bf6
+Author: Philipp Bruschweiler <blei42@gmail.com>
+Date: Wed Jan 16 00:11:12 2008 +0100
+
+ added specs for SHA256/384/512
+
+ these specs were as well shamelessly copied from the md5 specs.
+ they work, but every sha* class has a folder for itsself, that's a
+ lot of duplicatd code. maybe someone with more experience in
+ writing specs should have a look at this.
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit 924224fcf655da90148ebd8234033a71e1b23090
+Author: Caleb Tennis <ctennis@engineyard.com>
+Date: Fri Feb 22 17:13:14 2008 -0500
+
+ Catch no block given in rb_yield, raise LocalJumpError
+
+ As well, define that as an exception for subtend
+
+ Update spec
+
+commit 3748843421832df5b842a677ddd2e55fbefb0b5f
+Author: Caleb Tennis <ctennis@engineyard.com>
+Date: Fri Feb 22 17:04:33 2008 -0500
+
+ Update rb_yield spec
+
+commit f60ca442b1466f29432995700457e8b34f4ff294
+Author: Caleb Tennis <ctennis@engineyard.com>
+Date: Fri Feb 22 17:00:36 2008 -0500
+
+ Fix rb_yield call
+
+commit a75afc4595fd20d7853ff65afe015de88b265b93
+Author: Caleb Tennis <ctennis@engineyard.com>
+Date: Fri Feb 22 16:48:17 2008 -0500
+
+ Add blocks to subtend methods, as they should be able to access them like any other method.
+
+ Also, update the spec
+
+commit d9911f8b00243f3c95759612dde35edf6edaa678
+Author: Caleb Tennis <ctennis@engineyard.com>
+Date: Thu Jan 31 13:54:24 2008 -0500
+
+ Add block specs and rb_block_given_p
+
+commit b6c806f0d8213c4751c69638174f60b80f9ba303
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri Feb 22 15:31:58 2008 -0500
+
+ Failing spec and exclude for left-to-right masgn evaluation order
+
+commit 8f9e3c9e5e7dfc535e8fe6b10b945587586651ec
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Fri Feb 22 13:59:44 2008 +0100
+
+ Fix Socket#getservbyname, not every platform defines http/udp
+
+commit f29ff3bcaf0bf83d2924d08ea5f6c0bbb5df9948
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Thu Feb 21 16:47:02 2008 -0800
+
+ Allow Ar to create archives
+
+commit e50ec6470dfc905198065a98b65b33a99da60e15
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Feb 21 20:20:41 2008 -0500
+
+ Some compiler specs for 'defined?' handling
+
+commit ba5a0d87182d83000205e1202f5c473568a50489
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Fri Feb 22 01:08:51 2008 +0100
+
+ Fixed #332 and cleaned up Time a bit. Thanks to gls
+
+commit edf1e0d530ebb39a1b46d0fa518b9ca85db544da
+Author: Evan Phoenix <ephoenix@engineyard.com>
+Date: Thu Feb 21 02:01:21 2008 -0800
+
+ Fix the last usage of block return (ie, internal long return).
+
+ * LongReturnException is now used whenever a block requests that
+ it's home context should return.
+
+commit 83ed7161701202d48490e7f38b568bc504f9690f
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 20 23:59:47 2008 -0800
+
+ Added little/big_endian guards to Array#pack and String#unpack specs.
+
+commit 65b4ed86002371f2b56759aadc61e61c1cbbdba4
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Feb 20 22:49:19 2008 -0800
+
+ Exclude Socket#unpack_sockaddr_in spec. See tag comment.
+
+commit 9fbda05c4dffb964a9f10e26d62240fbd52200a0
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Feb 20 22:48:31 2008 -0800
+
+ Exclude super slow IPAddr specs.
+
+commit 3d39fb35dcd3c28fa626aeb96057b927c6bfe7c9
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 20 18:54:50 2008 -0800
+
+ Redo expectation in Socket#getaddrinfo spec.
+
+commit 69576ede38d9bf09d1afd0120726ca756a0aa7cf
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 20 18:31:27 2008 -0800
+
+ Account for variable length array in Socket#getaddrinfo.
+
+commit f396bd718572d9402d0d7eeb8da02474914396a8
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 20 17:59:29 2008 -0800
+
+ Use File.delete in YAML specs instead of rm.
+
+commit 7698ec3855ce572f1e10962596804b82f3cd6534
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Thu Feb 14 10:07:48 2008 +1100
+
+ Hook-up new StepBreakpoint to new debugger step commands
+
+ * The commands step and stepi have now been added to the
+ debugger, and step into called methods.
+ * The commands next, nexti and out have been converted
+ to use the new StepBreakpoint. The legacy versions
+ remain, but have been renamed as ln, lni, and lo; these
+ will be removed once the new commands have proven stable.
+ * Replaced VM method cache command with VM send site command
+ to show details of SendSites in the current method.
+
+commit f192d65ec5eb31b4a807b9c3eb7360b84739d9f2
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Thu Jan 31 16:43:19 2008 +1100
+
+ Initial implementation of StepBreakpoint
+
+ StepBreakpoint class moves step logic out of the Debugger
+ and into breakpoint, where it more logically belongs.
+
+commit fd0ff43d2d384e221ff8de611843f3406d192657
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 20 17:04:46 2008 -0800
+
+ Fixed YAML spec to pass MRI. Added fails tag for rbx.
+
+commit d69834a5217ddc6667b495fbe7d4dd8ad413ba88
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed Feb 20 15:42:25 2008 -0500
+
+ Fix dead code in TCPSocket.new specs
+
+commit 4644222e63046783933ca9b2e4514e3ff21fbb57
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 20 12:31:14 2008 -0800
+
+ Add missing tag file for method_spec.
+
+commit 230d5d506f4203bcd3922880fae506fa480e6308
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 20 12:17:13 2008 -0800
+
+ Fix typo in socket specs.
+
+commit a5d49537832a9cc33b07cade265af0834f123533
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 20 12:09:11 2008 -0800
+
+ Move specs for calling methods to language/method_spec.rb.
+
+commit ead32a1f2820a4e2fcc906a8e7f3603490ba901c
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 20 11:53:45 2008 -0800
+
+ Use bignum_value where a Bignum is intended in the specs.
+
+commit 1021345337bca1f928879713cb84a76b9c7935a1
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 20 10:21:42 2008 -0800
+
+ Removed unused require 'stringio' from io/syswrite specs.
+
+commit cfd51af482321b4d672d69569de185f582a21831
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 20 09:00:36 2008 -0800
+
+ Symbols as Fixnums is long deprecated. We don't spec it.
+
+commit a8bd2a1aba97653625a9b568d1a7112b5fce45f6
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 20 00:15:54 2008 -0800
+
+ RbYAML is not in Ruby standard lib. Move specs for it to spec/library/rbyaml.
+
+commit 56b454af2ded18d0459bc974efa666ccf3b8de0f
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 20 00:10:24 2008 -0800
+
+ Restrict specs in spec/ruby/1.8 to current stable 1.8 version.
+
+commit 22e01d1914db92d159ee15d3cf73c9d6e9d0a24b
+Author: Brian Ford <bford@engineyard.com>
+Date: Tue Feb 19 23:20:27 2008 -0800
+
+ Fix Dir#pos=/#seek specs. We shouldn't spec undefined platform behavior.
+
+commit d522af83d0cfcdf39932afff7ba7d75d77dd0453
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Feb 20 11:51:44 2008 +0100
+
+ New IO.read specs.
+
+commit 77fdbe404e31f44e1c302eb99a7ff129523183ce
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Tue Feb 19 16:14:43 2008 -0800
+
+ Add library to read/write ar(5) files
+
+commit d7702f979732de90358dc35d795c6ac621f815bc
+Author: Matthijs Langenberg <mlangenberg@gmail.com>
+Date: Mon Feb 18 18:04:27 2008 +0100
+
+ writen some examples for Base64 module
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit 33b189478c05bd687ac8b062cd5307a3290d8931
+Author: Brian Ford <bford@engineyard.com>
+Date: Tue Feb 19 00:27:51 2008 -0800
+
+ Convert platform guard :size option to :wordsize.
+
+commit cbcdb8346a2c75ba65910b486cee718cd3aa5175
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Feb 18 23:07:41 2008 -0800
+
+ Exclude TCPSocket.new for now, hangs on ubuntu gutsy.
+
+commit ec990b6ebcd35cbf9dc192852f37e184c3e4079b
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Tue Feb 19 15:55:25 2008 +1100
+
+ Re-enable debug on context change
+
+ The cpu_yield_debugger_check was not being performed as
+ a result of changes to method dispatch related to the
+ implementation of SendSite.
+
+commit aa585b7e637e2fd873602ee6725256429f413582
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Feb 18 18:59:18 2008 -0800
+
+ Removed :version guarded specs that are not current stable.
+
+commit 431af5920a0a02dfca927961a2d6457ae5f050e2
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Feb 18 17:40:56 2008 -0800
+
+ Added new tags files for excludes.
+
+commit 10dd37903533cac9a6f77ead70f3aa9ee1dc9098
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Feb 18 16:38:58 2008 -0800
+
+ Removed deprecated $deferr from getoptlong.rb. Moved to /lib.
+
+ Small fixes to other library specs to get them running under CI.
+
+commit ee2dabf771a5e6d8d70c47fa49b1298d2d002c8c
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Feb 18 00:42:54 2008 -0800
+
+ Use the spec guards properly.
+
+commit 91d6c64be8827768ba2e39b80a4eb81b9affc122
+Author: Brian Ford <bford@engineyard.com>
+Date: Sun Feb 17 22:40:21 2008 -0800
+
+ Deprecate #setup, #teardown in specs; use #before, #after.
+
+commit 6ba49012504c08973e1fb2fd1b9fce75c351d148
+Author: Brian Ford <bford@engineyard.com>
+Date: Sun Feb 17 22:00:56 2008 -0800
+
+ The #fails_on guard has been removed. Use #ruby_bug or tagged excludes.
+
+commit e24231f5c62c0b73768c7503f50b53e8ffc345d1
+Author: Brian Ford <bford@engineyard.com>
+Date: Sun Feb 17 21:08:01 2008 -0800
+
+ Renamed *_excludes.txt to *_tags.txt for specs.
+
+commit a1c707b517e13115692173bc2048309e74c00915
+Author: Brian Ford <bford@engineyard.com>
+Date: Sun Feb 17 20:45:14 2008 -0800
+
+ Hand merge recent excludes changes to spec/tags directory.
+
+commit 838bee7e99bb1179c9a3a7782dcab9c2b904e72e
+Author: Brian Ford <bford@engineyard.com>
+Date: Sun Feb 17 00:17:51 2008 -0800
+
+ Moved excludes from spec/data to spec/tags. Added "fails" tags.
+
+commit 8ad91b03788d89ccd12fbcf19c06c9ef4f0cfee8
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Feb 4 19:19:00 2008 -0800
+
+ Misc fixes to get MSpec running specs.
+
+commit a683dd75786ab6c6a255c9bac399dc6be7aaa4b5
+Author: Tyler McMullen <tbmcmullen@gmail.com>
+Date: Sat Feb 16 23:39:38 2008 -0500
+
+ Add support for H and h to Array#pack.
+
+ * Updated array/pack_spec with specs for H and h, separately
+ * Updated Array#pack to handle both with a single block of code
+
+ Signed-off-by: Brian Ford <bford@engineyard.com>
+
+commit b1d3ba9d10f6a9ea87d8cb9be21d0d432e973117
+Author: oleg dashevskii <be9@be9.ru>
+Date: Mon Feb 18 01:18:24 2008 +0600
+
+ Update specs for calling methods.
+
+ Nasty binding stuff (first noted in #293) got specced and put into excludes.
+
+commit 02225daa5cef4fa3f48cac73d4bf0f9d02f3ebe0
+Author: oleg dashevskii <be9@be9.ru>
+Date: Sun Feb 17 23:20:08 2008 +0600
+
+ Cross-breed and update for, while & until language specs. Little fix for hash spec.
+
+ The compiler drops out on "for @@var in 1..3", so this is commented out.
+ Variable scope stuff arrived into excludes.
+
+commit f43383a150131278d30535196e8da4e60dff97b1
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sun Feb 17 13:10:55 2008 +0100
+
+ New specs for RangeExceptions out of Fixnum and Array methods.
+
+commit 7d1c744d9c1ae50376be406a28e383a04ca6b4fc
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sat Feb 16 13:49:11 2008 +0100
+
+ Corrected copy-paste error in recent fixnum specs.
+
+commit 08982321472008f7645212289d2624d19053ed7e
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Fri Feb 15 21:18:53 2008 -0800
+
+ Fix IO#read for large files and small parts of files.
+
+ Fix IO#read with buffer.
+
+ Fix IO#eof? when buffer reaches eof.
+
+commit 1d07588d61b3835a6165c5de1f731277812cff79
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Fri Feb 15 19:11:42 2008 -0800
+
+ Add missing spec for IO#eof? and fix.
+
+commit e0a6c8e179e48b423b6eb142b27460cd86d0223b
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Fri Feb 15 17:50:47 2008 -0800
+
+ (Last change was ok). Force check for data so #eof? works
+
+commit d7e67c257c213f9e25b3123ce85576feb71a0089
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Fri Feb 15 17:28:48 2008 -0800
+
+ Revert "Force a check for more data on the IO for IO#eof?"
+
+ This reverts commit 3d4427e802756678608bf9840ba6f26fc81cf7fe.
+
+commit 4c1182c184bb6c2c97c5fc8ce83f242fe5f5144b
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Fri Feb 15 17:26:08 2008 -0800
+
+ Force a check for more data on the IO for IO#eof?
+
+commit 94466db3347889850feb25dd7c83883df21bac92
+Author: Brian Ford <bford@engineyard.com>
+Date: Fri Feb 15 14:13:29 2008 -0800
+
+ Added Float examples to Bignum bitwise operator specs.
+
+commit 3a668451d3bcc46b162a69ce1f8ec5d6a98b2d22
+Author: Brian Ford <bford@engineyard.com>
+Date: Fri Feb 15 13:44:24 2008 -0800
+
+ Added bignum_value helper. Added specs for Fixnum bitwise operators.
+
+commit 217eb67a4c2f0bf1222628abfecfadbede5fb3b8
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Fri Feb 15 21:09:43 2008 +0100
+
+ Fix process specs for FreeBSD
+
+commit f25e0e130110ebbef0b5bc0c28c9b08db6c73a1f
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Fri Feb 15 13:18:40 2008 +0100
+
+ Removed now working exclude for Array#sort
+
+commit 56af7be26dcc9b7270de6d96e73e09a4f17cc710
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Feb 14 20:48:37 2008 -0500
+
+ Improved Array#sort, #sort! specs.
+
+commit 714ea4b5245172cc6d5c815ef7399d1a991dd83f
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Feb 13 10:30:22 2008 -0500
+
+ Improved Array#sort specs a bit.
+
+commit 8944e873848c610182405c2de466e41e6260573d
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Feb 13 02:34:37 2008 -0500
+
+ Tuple#swap specs.
+
+commit 24199f731dba40b72af6d121121dec9f085f890d
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Feb 14 20:03:16 2008 +0100
+
+ New rubyspecs for IO#reopen.
+
+commit 4f70320e5b7089c74b3899216763cd37d8854230
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Feb 14 17:27:32 2008 +0100
+
+ Removed JRuby-specific guards. Please don't use guards to hide bugs.
+
+ Guards to be used only when it is agreed that the JRuby behavior
+ is intentionally differs from MRI. For plain bugs, guards should
+ not be used. Instead, we maintain spec exclusions in JRuby repository.
+
+commit 0198a11b3bdf60983846a6c722dfa11d1b9f57bb
+Merge: ef3393e... 1f1e32e...
+Author: Mutwin Kraus <mutwin.kraus@blogage.de>
+Date: Thu Feb 14 15:57:57 2008 +0100
+
+ Merge branch 'mutle_file_specs_refactoring'
+
+commit 1f1e32e5e1fd12fb323e2a74a7f5caae96aa867b
+Author: Mutwin Kraus <mutwin.kraus@blogage.de>
+Date: Thu Feb 14 15:18:14 2008 +0100
+
+ Specs for File#chown #flock and #truncate now pass on JRuby
+
+commit 3a8e601d5205e050f83179376d2be3e922e80c20
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Thu Feb 14 17:25:02 2008 +1100
+
+ Fix context specs to wait for debug listener thread
+
+commit 608d7a99e75d293d6f9786cee940c0dd23156be3
+Author: Mutwin Kraus <mutwin.kraus@blogage.de>
+Date: Thu Feb 14 12:59:32 2008 +0100
+
+ Adding guards to only run File#chown and File.chown specs as root.
+
+commit b3a1069cf6c18b844b9eced32b7bcdb91ad7c558
+Author: Brian Ford <bford@engineyard.com>
+Date: Thu Feb 14 01:31:47 2008 -0800
+
+ Rework Bignum#==. Change Numeric#== to conform to MRI.
+
+commit 4eb58ebc45b2ee79f01d75fdb3e9104c73ad66e2
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Wed Feb 13 23:56:37 2008 -0800
+
+ Common implementation for Zlib::Inflate and Zlib::Deflate.
+
+commit 1804fdacce5c195a90befe502706d1f1e066e886
+Author: Brian Ford <bford@engineyard.com>
+Date: Wed Feb 13 19:31:20 2008 -0800
+
+ Port of JRuby's File.fnmatch to Ruby (yeah, like writing Java in Ruby).
+
+commit 1a78da8438535ee8ed231359bdb15ff3624c6b37
+Author: Mutwin Kraus <mutwin.kraus@blogage.de>
+Date: Thu Feb 14 01:27:52 2008 +0100
+
+ Adding File#truncate improvements from #325 and #326
+
+commit 5b62acbdcf0aab2e89be5ac3e12859ae36cd6950
+Author: Mutwin Kraus <mutwin.kraus@blogage.de>
+Date: Thu Feb 14 00:47:53 2008 +0100
+
+ Adding File#truncate with specs
+
+commit 1a2b3dde4f67abe0936e7ec6fb749e5bb8fda7d2
+Author: Mutwin Kraus <mutwin.kraus@blogage.de>
+Date: Thu Feb 14 00:36:32 2008 +0100
+
+ Adding File#chown with specs
+
+commit e132cd6f11285f0e106a5d2a292e23c8375fa1ee
+Author: Mutwin Kraus <mutwin.kraus@blogage.de>
+Date: Thu Feb 14 00:12:31 2008 +0100
+
+ Renamed File#flock spec to properly reflect an instance method
+
+commit 00cd22ccdf2b70fa53693000d4a5bb803c7d6df6
+Author: Mutwin Kraus <mutwin.kraus@blogage.de>
+Date: Thu Feb 14 00:07:05 2008 +0100
+
+ Adding File::flock with specs
+
+commit 3c9b3e4e4272889dd26ec9ddb25f7aaf88c6c380
+Author: Mutwin Kraus <mutwin.kraus@blogage.de>
+Date: Wed Feb 13 23:25:30 2008 +0100
+
+ Adding File::chown with specs
+
+ * The spec works fine on OS X, but was not tested anywhere else
+
+commit c894a6c46b4a3d0b9010020c394d3ba366bf145e
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Wed Feb 13 11:59:38 2008 +0100
+
+ Module#undef_method should accept string parameter, not only symbols by Nikolai Lugovoi (#321)
+
+commit c968d5c29cc3126c789cf5bb2005bd9637e85312
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Wed Feb 13 11:52:10 2008 +0100
+
+ Update excludes for File#truncate
+
+commit 408e69864546aea061e006073bb452b8db8c4610
+Author: Ragnar Dahlén <r.dahlen@gmail.com>
+Date: Wed Feb 13 11:15:31 2008 +0100
+
+ Implement File.truncate, passes specs.
+
+ * Adds truncate, ftruncate (not used yet) to posix.rb
+
+ Only tested on Mac OS X 10.5.1.
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit 5c75721d5a78e25a77e9f068bf4c95e729604959
+Author: oleg dashevskii <be9@be9.ru>
+Date: Wed Feb 13 11:26:34 2008 +0600
+
+ Remove tests that have been superseded by precedence_spec.
+
+commit 83a372674786a0be51a206cadcae644d72a1e8d2
+Author: oleg dashevskii <be9@be9.ru>
+Date: Wed Feb 13 11:05:34 2008 +0600
+
+ Made a real precedence_spec.
+
+ One test still commented out till the bug with flip2 is fixed.
+
+commit c3988a4a906594c050e058add8aa6996870dc115
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Wed Feb 13 02:10:49 2008 +0100
+
+ Remove excludes for File#stats specs
+
+commit 1624b463d0f70a27b6772d90626c94b6eed4e5c4
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue Feb 12 18:25:05 2008 -0500
+
+ Add specs for pass subclasses of Module to 'include'
+
+commit 64b0fb4131276feda0d0ab13301824b20f8d7f8e
+Author: oleg dashevskii <be9@be9.ru>
+Date: Wed Feb 13 00:49:37 2008 +0600
+
+ Make Dir.chdir spec work when /home is symlinked to /usr/home.
+
+commit 8cbf6312df160f30e284a4537039f808a42543fe
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue Feb 12 12:30:19 2008 -0500
+
+ Add failing Array#sort spec and matching exclude
+
+commit 9bef807b3b469b8790edbe96f1442394d528cb5a
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue Feb 12 12:09:11 2008 -0500
+
+ Move Time#<=> specs around until the descriptions make sense
+
+commit 60fbbc62cb04b2fddcd406f01f906482fbc84370
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Feb 12 05:33:49 2008 +0100
+
+ Mark JRuby as not deviating from MRI on unboundmethod specs.
+
+commit 4e6d8f7e3326f937a6916ed11984172670a71094
+Author: Yehuda Katz <ephoenix@engineyard.com>
+Date: Mon Feb 11 23:26:31 2008 -0800
+
+ Zlib.adler32
+
+commit 2f2d10e1aa57bae79f7fcda5e5a30b2a2ef3e37c
+Author: Yehuda Katz <ephoenix@engineyard.com>
+Date: Mon Feb 11 23:12:40 2008 -0800
+
+ Zlib.crc_table
+
+commit 49b9e4b624074d151e89f078c4080a0a7584abaa
+Author: Yehuda Katz <ephoenix@engineyard.com>
+Date: Mon Feb 11 22:56:52 2008 -0800
+
+ Zlib#crc32
+
+commit 7cb2ebfa008afc96135912ceefdbd81b1cd7e478
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Mon Feb 11 17:36:32 2008 -0800
+
+ Fix class variables for RDoc.
+
+commit 571d837bbeff221daacebc79c1ccab7de15c77f2
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Feb 11 08:54:36 2008 -0800
+
+ Exclude [r]dev_(major|minor) specs. We need some autoconf facilities.
+
+commit fb2bc81d50bf504e3997d009e3c13f841b859803
+Merge: 55a52f1... 9b58a59...
+Author: Jonas Pfenniger <zimbatm@oree.ch>
+Date: Mon Feb 11 16:19:02 2008 +0100
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit 55a52f18133fc9f92eef64838008a83dfaab3ffc
+Author: Jonas Pfenniger <zimbatm@oree.ch>
+Date: Mon Feb 11 16:18:37 2008 +0100
+
+ Removed Math.asinh excludes.
+
+ It wasn't working on OS X. Evan fixed the culprit FFI over the weekend.
+
+commit 9b58a59ca21c6622d246e629410230bfbe8cd4ce
+Author: makoto kuwata <kwa@kuwata-lab.com>
+Date: Mon Feb 11 21:24:05 2008 +0900
+
+ Modified to address differences of SyntaxError class between MRI and Rubinius in 'erb/filename_spec.rb'
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit d1c4280b70b82d6cd541251e3d7e1a3091fb304f
+Author: makoto kuwata <kwa@kuwata-lab.com>
+Date: Mon Feb 11 20:19:12 2008 +0900
+
+ Add 'erb/util/shared/url_encode.rb' which is missed file
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit b4b1114ac7dffabd672d462b5857a7e1957e8f07
+Author: makoto kuwata <kwa@kuwata-lab.com>
+Date: Mon Feb 11 19:10:58 2008 +0900
+
+ Add spec files for erb.rb
+
+ Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit dfdf90968e78f14e0755b5f3279ec878034dbdb5
+Author: Yehuda Katz <ephoenix@engineyard.com>
+Date: Mon Feb 11 00:09:23 2008 -0800
+
+ Added singleton specs (and reorganized stale one):
+ * Singleton#_dump
+ * Singleton._load
+ * Singleton#instance
+ * Singleton.instantiate?
+ * Singleton.new and Singleton.allocate
+ * Singleton#dup and Singleton#clone
+
+commit 54c4a4cab187be4328d6a810bae4bc4bd01ca1d8
+Author: Brian Ford <bford@engineyard.com>
+Date: Sun Feb 10 20:19:30 2008 -0800
+
+ Additional specs for File::Stat#rdev, #rdev_major, #rdev_minor.
+
+commit 6b2f05af4758c488b3e2e3b19ee9d2e872817932
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sun Feb 10 23:00:41 2008 -0500
+
+ Rubinius now passes all 'super' specs
+
+commit 5be84fce241c67bd8439bccbe54cec575b0ea93a
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sun Feb 10 22:53:11 2008 -0500
+
+ Failing spec for 'super' behavior
+
+commit 975d51e80d3df437eaa8ddd3c3384a5766255b12
+Author: Brian Ford <bford@engineyard.com>
+Date: Sat Feb 9 15:18:37 2008 -0800
+
+ Exclude Process constants spec until LFS is fixed on 32bit linux.
+
+commit 236def62bcfa3dca75a6eebf378a68235c4613ed
+Author: Yehuda Katz <ephoenix@engineyard.com>
+Date: Sat Feb 9 17:04:59 2008 -0500
+
+ Fixes exclude
+
+commit 26bedb481b45e77434b487c6395903c6110ef99e
+Author: Yehuda Katz <ephoenix@engineyard.com>
+Date: Sat Feb 9 16:01:58 2008 -0500
+
+ Moved bad variables spec out
+
+commit 87efbf9036e5c524e1b40481c89107538d574ba8
+Author: Brian Ford <bford@engineyard.com>
+Date: Sat Feb 9 11:26:30 2008 -0800
+
+ Revert all File::Stat stuff. We'll fix Dir first.
+
+commit 1f5bc0f98a23fc90b9bd00048af1551df8e534f7
+Author: Brian Ford <bford@engineyard.com>
+Date: Sat Feb 9 00:39:20 2008 -0800
+
+ Reduced File::Stat.stat primitive further. Details follow.
+
+ * Added ffi_major and ffi_minor to calculate the major, minor
+ parts of st_dev and st_rdev.
+ * Added (temporary) new primitive basic_stat to change the
+ return type from a tuple to a single MemoryPointer instance.
+ * Added simple specs for rdev, rdev_major, rdev_minor, nlink.
+
+commit e478731a2fc558c62cecbe327c5b35882d90b53d
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Feb 8 16:45:05 2008 +0100
+
+ One more rubyspec for File#open.
+
+commit 5f6ac709500cb64df110a44d31e0c0b89dd68aec
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Feb 8 16:39:47 2008 +0100
+
+ New rubyspecs for File#umask.
+
+commit fddaa684bd7e8c403ff96179ca71a5837f609b63
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Feb 8 16:30:20 2008 +0100
+
+ New File#chmod rubyspecs.
+
+commit ed20c3f9f36f343a37e2ac05ea91d84b54c87bc8
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri Feb 8 12:00:57 2008 -0500
+
+ Correctly guard Bignum specs for CI
+
+commit 36e9749984d6e4412c26d348afa8c501cf043ecf
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Feb 8 13:09:37 2008 +0100
+
+ Some more specs for File#new and File#open, and permissions.
+
+commit 29376695550c5608f466d63d49de76a6ee163e37
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Feb 8 12:10:27 2008 +0100
+
+ New specs for IO#new and IO#open, invoked with permissions parameter..
+
+commit 8cf27fcd86f88b75716b65dc1d94b721c01c3af9
+Author: oleg dashevskii <be9@be9.ru>
+Date: Fri Feb 1 21:03:08 2008 +0600
+
+ Heredocs and more stuff added to string_spec.
+
+commit 2cccd38a081c0303f8fa567058e4c26fa354abc5
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Fri Feb 8 12:51:53 2008 +0100
+
+ Add exclude for currently failing for_spec
+
+commit 031bb1b565a3446ab995ea55e6ae8890573ba6c0
+Author: oleg dashevskii <be9@be9.ru>
+Date: Fri Feb 1 20:23:56 2008 +0600
+
+ Added more tests to for expression spec.
+
+commit 73e40331c6b4c1c1b6e41ae312299f6815e089c2
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Feb 7 19:12:46 2008 -0500
+
+ Add a failing spec for Array#join and then fix it
+
+commit fa49548fe704252c352a1bc4833b5da20262061a
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Thu Feb 7 23:12:03 2008 +0100
+
+ Fix last two Failing Time specs for Time#+ and Time#-
+
+commit e4e51c6aa39e5a5a61b0df919ba02b88d4878f43
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Thu Feb 7 22:35:57 2008 +0100
+
+ Fix Time.at so it also works with floats
+
+commit f5505522fd0396c3864fce155681ac577bf2e7e6
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Thu Feb 7 17:10:48 2008 +0100
+
+ Fix Time#<=> for objects other than Time
+
+commit e8ab7b5eb30da84262a9395e20ac420e83674edf
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Wed Feb 6 13:59:18 2008 -0800
+
+ Only call Class#inherited once
+
+commit dc9ff28ae919292287f5562b8c105ff6310c5920
+Author: Phil Hagelberg <phil@hagelb.org>
+Date: Wed Feb 6 15:00:59 2008 -0800
+
+ Kernel#eval should be a module function
+
+ Added a spec as well
+
+commit 7dd83410a2159fd65f951689a8a1297baded4fa6
+Merge: 698ffa4... 339fed9...
+Author: Mutwin Kraus <mutwin.kraus@blogage.de>
+Date: Wed Feb 6 22:54:12 2008 +0100
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit 698ffa4e04fee58da5c3f2191372c4e4f2bc070d
+Author: Mutwin Kraus <mutwin.kraus@blogage.de>
+Date: Wed Feb 6 22:51:54 2008 +0100
+
+ Adding missing specs for ftools
+
+ * specs for chmod, compare, copy, install, makedirs, move and safe_unlink
+
+commit 339fed9821b75de056febc406b32fe52ff9354a9
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Wed Feb 6 22:00:46 2008 +0100
+
+ Forgot to remove spec excludes...
+
+commit 7ecca7222823a82252ed09b17eefafe6fec9f12e
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Wed Feb 6 21:46:19 2008 +0100
+
+ Fix last two failing Dir#glob specs
+
+commit fec39f27d287ca74becbecc120de8533e346b864
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Feb 6 18:13:13 2008 +0100
+
+ A few more corner cases fo IO#lineno specs.
+
+commit 3691d3f9a202abb22e11024e41b868d531a549be
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Feb 6 18:02:50 2008 +0100
+
+ New rubyspecs for IO#lineno.
+
+commit a5b0f9aa15c9372f74816e77073926780a9cc219
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Feb 6 15:58:26 2008 +0100
+
+ Improved IO#foreach specs.
+
+commit 91ea9f304c75592e7454411ef21391a0e34da5e5
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Feb 6 15:03:02 2008 +0100
+
+ New rubyspecs for IO#gets and IO#foreach.
+
+commit 9c494786fbf400bb295e1f19d142e2c903c21e54
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Feb 6 13:37:43 2008 +0100
+
+ New rubyspecs for IO#foreach.
+
+commit cdbbeba8f3351fe43f44d732348f380599ad5719
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Feb 6 10:40:13 2008 +0100
+
+ New rubyspecs for IO's sysread and read with buffer argument.
+
+commit e1cb4410b7e0a0ba2fd580784334bdfd05ba4b8a
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Feb 6 10:15:29 2008 +0100
+
+ Excluded rbx failure after spec rename.
+
+commit 1fde018b9378b55f6d51cb85bd65813b5cef5493
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Wed Feb 6 02:50:44 2008 -0600
+
+ Tidy up an apparent copy/paste mistake in IO#syswrite spec
+
+commit 1aa624e625dafaeebd70eac11819f02ecf570f8e
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Feb 6 00:19:39 2008 +0100
+
+ New IO specs for writing non-string data.
+
+commit f94a0cffd5fd0e186a9403d97800b55f8c44bdd1
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Tue Feb 5 15:13:47 2008 -0800
+
+ Regenerate zlib stubs.
+
+commit 52ce0e702170676ce02dcc288305097d58834cf8
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Tue Feb 5 13:34:47 2008 -0800
+
+ Add Zlib spec stubs
+
+commit b24ad594837b974a3ae3b207d63ce5cdc956a1a0
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Feb 5 21:45:09 2008 +0100
+
+ Added some boundary test cases for Float.
+
+commit 1a8c9966fa148fc3e912f8aecd42c8c00ca4f89c
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Feb 5 22:46:18 2008 +0100
+
+ Removed problematic Marshal spec because 2**40 is a Bignum on some archs and a Fixnum on others
+
+commit 6ad8a0a25f20fd137bcb7fcb83bc88440a2a2069
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Feb 5 17:03:56 2008 +0100
+
+ Eliminated file descriptors leakage out of IO tests.
+
+ These things make runs unpredictable, causing all kinds
+ of troubles (non-deterministic failures, fluctating
+ number of tests).
+
+commit 95ac3cb9900c52e4819b37166c71840d4bb4e3d9
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Feb 5 14:50:31 2008 +0100
+
+ New rubyspecs for File.open with block.
+
+ Also, IO.open specs improved to handle closing better.
+
+commit 66f636c346a63853ae37a06f1c3e8b5083370892
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Feb 5 13:22:34 2008 +0100
+
+ New tests for IO.open, and additional cases for IO's inspect and stat.
+
+commit dfb941da0f7503bce58dc88a85ccfd201615e13b
+Author: Brian Ford <bford@engineyard.com>
+Date: Tue Feb 5 01:57:20 2008 -0800
+
+ Ezra's patch for Regexp#inspect, #309.
+
+commit c5f9381ee74ed2d9c91cca1dd2ce9719b6f51bd8
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Feb 5 10:07:57 2008 +0100
+
+ Corrected IO test to not interfere with Kernel#puts tests.
+
+commit 751293c1ea14de1b1d2757bf5d60b082cc771e7a
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Mon Feb 4 17:43:06 2008 -0800
+
+ removed empty excludes
+
+commit 206399aee9ae7845d76c0726702c424b9fc44e80
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Mon Feb 4 17:39:45 2008 -0800
+
+ StringIO#seek now raises if passed bignum offset - should be platform specific, but this'll do for now
+
+commit b00f04ec0fcd8f8edf9943abb5999f1cca9e9e9e
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Mon Feb 4 17:06:34 2008 -0800
+
+ Knocked off the last of the method excludes.
+ Tightened up the spec for #to_s
+
+commit 3d4f87741135343a2e8ec6032fa3a69529cfbf69
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Mon Feb 4 16:51:37 2008 -0800
+
+ Fix Hash#key? to work with objects that have the same #hash.
+
+commit f814a15639f6e6ecd47ab99ad9e37e93fd6bc165
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Mon Feb 4 15:17:08 2008 -0800
+
+ Fix Marshal format version check
+
+commit 6d9e0afb5600416e5d66d5123abcfa5dd6c40903
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Mon Feb 4 14:24:48 2008 -0800
+
+ removed tmpfiles from dir/fixtures and moved them to tmp where they belong
+
+commit 4e18d1cf49573b1fa3f484686352734aa39457d0
+Author: Ruben Nine <ruben@leftbee.net>
+Date: Mon Feb 4 02:32:26 2008 +0100
+
+ Added support for tag:yaml.org,2002:sym to RbYAML library.
+
+ Signed-off-by: Jonas Pfenniger <zimbatm@oree.ch>
+
+commit 220ed05f204f4b8fe7f1f303fae9a18988f8879b
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Mon Feb 4 17:16:44 2008 -0500
+
+ Use an example number that is actually a Bignum everywhere in compiler Bignum spec
+
+commit db3f20c8ec905641de887bbd1ed581aa78f73471
+Merge: e33350e... 6e3dad3...
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Mon Feb 4 16:50:14 2008 -0500
+
+ Merge branch 'wilson64'
+
+commit 6e3dad3e5b2e982f96e991e9df2d46de5bf4ee1f
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Mon Feb 4 16:49:09 2008 -0500
+
+ Use the correct Fixnum#size spec on 64bit platforms
+
+commit e33350eddc3441b2ebe06336500e6445406285d1
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 22:12:14 2008 +0100
+
+ Moved one IO#inspect spec to File#inspect specs.
+
+ Since the behavior is File-specific.
+
+commit 1586e3a2c5d12f5438adddb8c84bc90c3defee82
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 20:57:09 2008 +0100
+
+ Add more IO specs. God, make it stop!
+
+commit e64f3b02423acb783ba8a62996847b0393e7f3ee
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 20:33:57 2008 +0100
+
+ And more IO specs.
+
+commit 3d584f0ee2cf988720bb5985c20b3bec6c2e143f
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 20:08:26 2008 +0100
+
+ Next batch of IO methods specs with closed streams.
+
+commit 6e22a99350195cfa7a40d6049d6d72a9ae7e1168
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 19:47:06 2008 +0100
+
+ And yet more specs for IO methods with closed streams.
+
+commit 12e8d881b90cbd60bc792693e799923fdb1041b0
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 19:20:39 2008 +0100
+
+ More rubysecs for IO methods invoked on closed streams.
+
+ Plus some refactoring to move repetitive code to the fixture.
+
+ NOTE: two specs marked as fails_on :rubinius due to fact
+ that they crash rubinius.
+
+commit 3ae3cafcb10725953c8e595641af277f36c88677
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 18:39:34 2008 +0100
+
+ New rubyspecs for IO#to_io.
+
+commit 4980bb83f53845e88cd0d1a3b0823fdbf0c0a001
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 18:09:36 2008 +0100
+
+ New rubyspecs for IO#ungetc.
+
+ Unfortunately, MRI doesn't follow some of its own
+ specified behaviors...
+
+commit f27fe4f3e4ccb298dcaa5014dac69d3148ee169e
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 17:18:08 2008 +0100
+
+ Added rubyspecs for IO's putc, puts, printf, print and closed streams.
+
+commit a1d7b67942aed8d1b185476dee6f2d99403ed227
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 17:07:00 2008 +0100
+
+ Added rubyspecs for IO#pid.
+
+commit cfa1ef21ce862a05ae352a4fe49a3ac4c04b9bed
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 16:36:21 2008 +0100
+
+ Added new rubyspecs for IO#sync and IO#sync=.
+
+commit b4f6c33c17e57fa44322124af088a97d475905e2
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 16:20:50 2008 +0100
+
+ New exclusions for rbx.
+
+commit 60309280c48b2bd1f1d8a5ea018f401e75b7dac1
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 16:17:27 2008 +0100
+
+ Added new tests for IO's pos, pos=, rewind, seek on closed streams.
+
+commit 40414ad1b39222494ff2a79a0091890a60b7adf1
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 14:26:13 2008 +0100
+
+ One more test for IO#eof?.
+
+commit 29db340f24c043b240fec6722c323fa1567ce855
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 14:19:46 2008 +0100
+
+ Added new rubyspecs for IO#getc and IO#getchar.
+
+commit 0e0a987782fc7834ba95a2e8e2c8ab6cd8dcea81
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Feb 4 13:54:59 2008 +0100
+
+ More rubyspecs for IO#eof?.
+
+commit f7d1139e4eace4a86f0c0512bf9269964442628d
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Feb 3 15:55:10 2008 +0100
+
+ Fix Date#strptime specs
+
+commit ac4600fcb42928aeba508371aea2f76510e70d5c
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Feb 3 13:26:52 2008 +0100
+
+ Fixed Time specs for non Rubinius platforms
+
+commit a5081ca646e99ec94fedfabf03b7eb0a8d37afc3
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sat Feb 2 23:53:52 2008 +0100
+
+ Fixed Time specs for 64 bit archs
+
+commit 26eef47571b921fe6b3228033119e5969c4100db
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Feb 2 13:30:16 2008 -0500
+
+ Updated IO excludes.
+
+commit 8edd73d9915f72ee70b661b23e8b42f8b985fa9c
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Sat Feb 2 10:45:23 2008 -0600
+
+ Repair IO#sysseek spec to not write to fixture file; uses a tmpfile now.
+
+commit bf6348c935c816a981672e9c26a40354cf0d722c
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Sat Feb 2 02:35:42 2008 -0600
+
+ Additional IO#sysseek spec for the warning after buffered writes
+
+commit 5241316a1c74e6580fb91940a9f061047e89cdbf
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Sat Feb 2 02:32:36 2008 -0600
+
+ Modify IO#seek specs for IO#sysseek, adding appropriate error tests
+
+commit 7f124cbf66b96fdcdaec73917e86eedfb4a9ddf8
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Fri Feb 1 18:05:15 2008 -0800
+
+ Refactored Marshal#dump specs and merged with fixtures/marshal_data.rb
+
+commit 3766b3ed41ffba71ecb1bef8079027bffe518e2a
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Fri Feb 1 16:54:32 2008 -0800
+
+ Refactored specs for Marshal#load
+
+commit e134d5bf8e247f4a231bfbfc1c3251b262f219e4
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Fri Feb 1 15:36:06 2008 -0800
+
+ Update excludes for recent failures
+
+commit 8ded8b443f55b47a1c30b59cfb0d96d8752d5fa9
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Fri Feb 1 15:11:37 2008 -0800
+
+ Process.group spec is missing a suplemental group on OS X
+
+commit 4b7de6ff839b220115dd29f34b5a9f46cb8f5bef
+Author: Evan Phoenix <ephoenix@engineyard.com>
+Date: Thu Jan 31 17:16:13 2008 -0800
+
+ Add proper primitive failures, fix empty symbol.
+
+commit dc55c88beee6a3a3a7fd352c1e374ecf84863459
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Feb 1 16:17:00 2008 +0100
+
+ Fixed 2 Date#strptime specs that otherwise would pass only in January.
+
+commit 86c372d0fb50aeb6235ed1595d18a876e09330db
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Feb 1 15:32:40 2008 +0100
+
+ Few specs for Time#<=> with non-Time arguments.
+
+commit 765ef93acd294922dc22a986213a5842ce3e67a7
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Feb 1 14:09:01 2008 +0100
+
+ Added more specs to Time#+ and Time#-.
+
+commit af76adac2182e46e34e68d29b3cd8614edd27d50
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Feb 1 13:38:20 2008 +0100
+
+ Added more test cases for Array#join on recursive arrays.
+
+commit bb15b72393b34d3d10bb644fb1d6ce47b6dc0826
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Feb 1 13:15:24 2008 +0100
+
+ Added more test cases for File::join with recursive arrays.
+
+commit 7041b2aef1e574dfe220a70da5210c683074f8ae
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Fri Feb 1 03:18:31 2008 -0800
+
+ Describe an unambiguous method.
+
+commit 84edf54799e0ccd09276a5cda3fccf544f971c48
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Fri Feb 1 03:07:48 2008 -0800
+
+ Use fixed Marshal data for all specs and fix many broken or useless specs.
+
+ Clean up spec naming and definition.
+
+ Use descriptive names for test classes.
+
+commit e0c3aa074c9525450a7a667ec2cc843ff3560e65
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Jan 31 23:29:57 2008 -0500
+
+ Hash.new patch from Phil Hagelberg + MethodTable workaround.
+
+ * The Hash.new patch splits a separate #setup method so that subclasses
+ can override #initialize without problems.
+ * Because it is part of the core code, MethodTable needs to explicitly
+ call #setup in its #initialize.
+
+commit a32f16d9288c5c0822cc6962ce3caed5e1bac5d0
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Jan 31 15:35:08 2008 -0500
+
+ Updated Module excludes.
+
+commit df731f327c4d47373ba6f2fe2f79d5d9acbf398e
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Jan 31 15:30:11 2008 -0500
+
+ More Module#module_function specs in #eval and #module_eval.
+
+ * #module_eval separates the two scopes but #eval does not.
+
+commit 6358e5893c52042c10c355173d1ad8441a00bcfa
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Jan 31 15:13:49 2008 -0500
+
+ Better Module#module_function specs.
+
+commit 1646bb6e99a6b4190641046ae730ea1be9c8be2a
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Jan 31 13:59:20 2008 -0500
+
+ Various whitespace removal in preparation to fix #module_function.
+
+commit 108601d85d2c41d05f9c00945664d9980e0e46c3
+Author: Evan Phoenix <ephoenix@engineyard.com>
+Date: Thu Jan 31 13:39:53 2008 -0800
+
+ Add meta_send_call instruction, speeds up calling blocks
+
+commit 50f9c50820b4305877af1c7fd7597c5dc94c623c
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Jan 31 12:20:11 2008 -0500
+
+ Added LC_ALL=C for all other platforms for Time specs too.
+
+ * If it breaks, report and we will figure out something else.
+
+commit 26059c1570c5ad2a64a796e2678ff2d9ace23e58
+Author: Pierre Yager <pierre@levosgien.net>
+Date: Tue Jan 29 22:48:22 2008 +0100
+
+ Fix for bin/ci spec failure on localised linux
+
+ * Force system date to be executed against "C" locale
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit e5ce9e7c29a34f685f7d3f8a9f855db28aece460
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Jan 31 11:32:56 2008 -0500
+
+ Removed trailing whitespace for Time and some Time specs.
+
+commit 3546f721ac86efa318b3802a2f498d41aa830c9f
+Author: Caleb Tennis <ctennis@engineyard.com>
+Date: Thu Jan 31 10:53:53 2008 -0500
+
+ Subtend: Add rb_define_private_method, rb_define_protected_method, rb_define_module_method, etc.
+
+commit 7553cb993a0c7e60c2212800b0ecc033ffc0b206
+Author: Caleb Tennis <ctennis@engineyard.com>
+Date: Thu Jan 31 10:39:08 2008 -0500
+
+ Add rb_class2name in subtend
+
+commit 4570b7c5d837025d765a6a2909d5536c466b9dcb
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Thu Jan 31 02:26:09 2008 -0800
+
+ quick addition of 2 exclusions
+
+commit 53c76326e76869a87ad0fc67adbd3aef9059ee35
+Author: Ryan Davis <rdavis@engineyard.com>
+Date: Thu Jan 31 02:15:15 2008 -0800
+
+ Parser spec updates
+
+commit 8fb2eb68858a1ee1dafb06b833f43d6da817756f
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Jan 30 19:29:25 2008 -0500
+
+ Slightly more permissive TCPServer.new spec for hostname string.
+
+commit 2a60dbbf011e806ae51c30ab2cb2b8e7b9b633a5
+Author: Mutwin Kraus <mutwin.kraus@blogage.de>
+Date: Wed Jan 23 18:41:12 2008 +0100
+
+ Fixing TCPSocket#new for localhost (with specs for both IPv4 and IPv6)
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit f485a6818a754c8110feafa9f6dced42a99187d0
+Author: Jacob Maine <jacob.maine@gmail.com>
+Date: Tue Jan 29 21:13:28 2008 -0500
+
+ Making Enumerable#inject only accept one paramter
+
+ Uses 'Undefined' idiom, which fixes spec, and cleans up code (thanks
+ for the pointer Eero)
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit ebfa5a0bf9f8e3efe61c0d34fe63a8cd74b7ddf8
+Author: Jacob Maine <jacob.maine@gmail.com>
+Date: Tue Jan 29 20:52:16 2008 -0500
+
+ spec to verify inject accepts one argument, at the most
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit b131b80df72a9ceaa9e920b7f78434f301135a6f
+Author: Jacob Maine <jacob.maine@gmail.com>
+Date: Wed Jan 30 00:33:03 2008 -0500
+
+ Adding Enumerable#count spec, including a few failing specs.
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 82b63bc0f5b79735a8021b6c5c69786dc76fa7f6
+Author: Alexandre Perrin <alexandre.perrin@epfl.ch>
+Date: Tue Jan 29 15:59:22 2008 +0100
+
+ udpdated language/string_spec.rb
+
+ * added spec for class/global variable with the \# simple interpolation
+ * added spec for ends of a \# simple interpolation
+ * added more delimiter character with the percent String construction
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit c58110bde52d64b30cf36ba3cb342357f3654812
+Author: Evan Phoenix <ephoenix@engineyard.com>
+Date: Wed Jan 30 17:19:26 2008 -0800
+
+ Fix break. It now uses LRE to properly return to callsite and appear like
+ it returned.
+
+commit 45109c222502de955d705f810333d8e7b331c953
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Thu Jan 31 00:42:12 2008 +0100
+
+ Added Date#strptime specs
+
+commit fe60e6a022d9e64bb568ccd47494f07a99382c58
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Jan 30 11:36:46 2008 -0500
+
+ Updated excludes for Marshal. Looks like Fixnum/Bignum issues.
+
+commit 209dde412310edc384be7d4a86bdfb0444f3b3bf
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Jan 30 01:45:27 2008 -0500
+
+ Updated IO/File excludes.
+
+commit 91031e51e49a1a3ddb9f74da31e2ed65c48e1ef5
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Jan 30 01:43:11 2008 -0500
+
+ IO.new and IO#close use stream API.
+
+ * IO.new uses fdopen() to open the given fd which also checks the mode
+ string for us. The returned FILE* is stored as a MemoryPointer in
+ @fptr.
+ * IO#close checks for presence of @fptr and if found, uses fflush() and
+ fclose() to release the handle instead of going the normal route.
+
+commit f4d64553a2a53c77235ef9acc3353ac455514057
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Jan 30 01:20:32 2008 -0500
+
+ Made probably broken File.open spec compliant_on :ruby.
+
+ * File.open should not take three args. File.new does.
+
+commit dc496f35502b4642137d3f0f74571c8245a6ae56
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Jan 30 01:19:29 2008 -0500
+
+ Slight IO.new spec tweaks.
+
+commit a9d9288315e88cffd59ec1b27e3c3209ceb1a3a9
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Jan 29 20:32:44 2008 -0500
+
+ Combined IO.new and IO.open specs for the shared parts.
+
+commit effa81cce1d42f7c1bc2e275cb75bd9069e934b8
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Jan 29 19:14:51 2008 -0500
+
+ Changed specs to use the two-argument IO.new.
+
+commit 1394b360fe70966e25809a349b400a69262060ca
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Jan 29 14:56:47 2008 -0500
+
+ Rewrote IO.new specs (still a bit sparse.)
+
+commit 94d50eb3e60971ffeff28bffa0beaff405c581bd
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Jan 30 21:47:35 2008 +0100
+
+ File#utime specs to use be_close rather than ==.
+
+ On some platforms, direct comparison just doesn't work,
+ producing non-deterministic test failures.
+
+commit 0f5574c28ff08c96326298b98b4ea50108168044
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Wed Jan 30 13:13:25 2008 +1100
+
+ Remove race in debug_context_change specs
+
+commit 00a62c3476dd0717f5c4caece453914e1392de9d
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Tue Jan 29 14:39:06 2008 -0800
+
+ Remove bogus Marshal specs for Fixnum/Bignum changeover
+
+commit db1b140db0fbecf70f8adda983e010ef2bbe94c4
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Jan 29 22:58:18 2008 +0100
+
+ Finished first version of Date specs. All public methods are specced
+
+commit 8ee52fd8dfd3ef6048c63b30d8aea71da944abb2
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Jan 29 17:21:57 2008 +0100
+
+ Follow rbx lead, and allow deviation in UnboundMethod#== for JRuby.
+
+commit f670bcb9e086ac9cc73b6ef6083966b296268f04
+Author: Caleb Tennis <ctennis@engineyard.com>
+Date: Tue Jan 29 09:46:50 2008 -0500
+
+ Fix typo
+
+commit 4e990269fd42aabd48cdc29b4288c78984d0e5cf
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Tue Jan 29 02:44:20 2008 -0800
+
+ Add File::join recursive Array spec.
+
+commit 4d947218e949e19515a9e89af99d4823048f3bb2
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Tue Jan 29 02:41:54 2008 -0800
+
+ Fix File::join spec name, duplication
+
+commit a38e10ddc19ebd59f8775a01f3e899c5348ba23f
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Tue Jan 29 02:40:37 2008 -0800
+
+ Make File::join remove extra / appropriately.
+
+commit 074251c03093ba40c0fc3558d512a77844ac45aa
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Tue Jan 29 00:53:23 2008 -0800
+
+ Make File::join specs more clear, remove whitespace
+
+commit f968bbe15a27d8ac6716d103119d41c4eef37696
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Mon Jan 28 18:07:24 2008 -0800
+
+ Use const_lookup in Marshal, fix #marshal_load.
+
+commit 7e00b857f56879564c1bf27f2e694f3c0783a4bb
+Author: Eric Hodel <ehodel@engineyard.com>
+Date: Mon Jan 28 17:23:26 2008 -0800
+
+ Support nested modules in Marshal
+
+commit 5c6e2af3d9ace07ca8387c5aecaa5c1d85e8d81f
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Mon Jan 28 20:22:40 2008 -0500
+
+ Added specs for rest of the filetypes to File::Stat#ftype specs.
+
+commit 221a077bef5e9007b548993eaf16c86137c6b0b3
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Mon Jan 28 20:08:12 2008 -0500
+
+ Added support to spec file type against sockets too.
+
+commit 9cb4791db10bc79f8c30a86f17e6c099dabeea80
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Mon Jan 28 19:52:07 2008 -0500
+
+ Moved File and File::Stat-related fixtures to fixtures.
+
+ * Module FileSpecs defines methods that yield filenames corresponding
+ to specific file types so they can be easily tested.
+
+commit c28c85602d3ab6770ed567a64b744baa15795511
+Author: Ben Hughes <ben@pixelmachine.org>
+Date: Sat Jan 19 16:10:36 2008 -0500
+
+ Added spec for File::Stat#dev, dev_major, and dev_minor
+
+ * Check that the result values are Integers for each operation
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 86ce52e32a35cb11564d0d5f306f4eea6d6b714d
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Tue Jan 22 15:21:38 2008 +1100
+
+ Added yield_debugger on context change
+
+ Added capability to set a flag in the VM that causes a
+ yield to the debugger to occur immediately following a
+ change in the active context. This provides a foundation
+ for step in logic for the debugger, which need only set
+ a flag on a task and have a breakpoint triggered at
+ whatever receiver is activated following a send.
+
+commit 3904ff2fbb209b8c2d476bb3f4a4ea4825a16f6e
+Author: Brian Ford <bford@engineyard.com>
+Date: Mon Jan 28 17:58:35 2008 -0800
+
+ Fixes for mSpec to coexist with autotest.
+
+commit 45f2d6de4b025acfa2429d88c729a3eb58a79528
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Mon Jan 28 17:08:45 2008 -0800
+
+ Added more brains to .autotest. Removed bad files that it pointed out
+
+commit 70eaa7feffcfd552c51b67a651cdf6063c9b549a
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Mon Jan 28 15:57:24 2008 -0800
+
+ Add File::Stat#dev.
+
+commit 020f4bec691ba658fab0f1ff24fa5df5a6f1921f
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Mon Jan 28 16:13:11 2008 -0800
+
+ Added enough process spec exclusions to drop the HUP issues
+
+commit 76b393566f2a89001952dbf1ec46dd52a5750448
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Sat Jan 26 10:58:42 2008 -0800
+
+ Fixed autotest support (needs latest version of zentest).
+ Minor clean up on bin/ci and kernel/core/module.rb.
+ Hacked mspec/matchers/base.rb to output with pretty print.
+ Fixed mspec's runner to output time BEFORE failures. Fixes unit_diff.
+ Updated Parser excludes.
+ Deleted a bunch of excludes.
+ Updated spec/parser/sexp_expectations.rb with latest ParseTreeTestCase.
+ Started adding a rewriter to make maintaining sexp_expectations easier.
+
+commit d147f6f0a87e30e240750d8c660bc89f8c84a472
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Mon Jan 28 18:20:38 2008 -0600
+
+ update CI excludes
+
+commit 159f17a228fa6a42cea79b9e3663e1f2b9dea9e4
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Mon Jan 28 18:07:56 2008 -0600
+
+ add Marshal specs
+
+commit 72e739590b6bbe571607df674e2f4106c64c8042
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Mon Jan 28 15:15:26 2008 -0800
+
+ Fix String#gsub when matching '^'.
+
+commit fc8c2c5584305b3e0b2a74ba8250a0b7072a372f
+Author: Ben Hughes <ben@pixelmachine.org>
+Date: Sat Jan 19 16:20:55 2008 -0500
+
+ Added specs for File::Stat#ftype. #264.
+
+ * Tests "file" and "directory"
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 2f9872b66d4dffc82e0a97e617fb9de18105f668
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 19:56:06 2008 -0500
+
+ Better living through mocks. String#+ spec cleanup & correction.
+
+ * String#+ in fact raises a TypeError when given ANY non-#to_str
+ object.
+ * Simplified spec code.
+
+commit f5a0f1e0e401db8f28727cdd8be99228c9c6aee3
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 19:37:08 2008 -0500
+
+ Removed trailing whitespace in String.
+
+commit 03c1c270236786b66930063669b95ac7cbf17f10
+Author: Matthew Draper <matthew@trebex.net>
+Date: Thu Jan 10 22:40:35 2008 +1030
+
+ String#+(65) throws a TypeError, unlike String#<<(65).
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit a17ede3e9c85c1bd2e06efa7381c1e5dbab47f80
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 19:10:33 2008 -0500
+
+ Minimal IO#open specs, IO#sysseek. Merged from Chen Yufei's patch.
+
+ * Merged patch by hand, most of it was already implemented separately
+ too.
+
+commit 30116d672d950687646c1668eac4d9f5b10f4df7
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 18:53:19 2008 -0500
+
+ IO#readline EOFError spec modified from Chen Yufei's patch.
+
+ * Patch was out-of-date, applied by hand.
+
+commit 1b81e68249741d53b38857440bba897987d00e43
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 17:44:30 2008 -0500
+
+ Separated and excluded NUL byte stripping for String#lstrip specs.
+
+ * Rubinius does strip leading NULs, MRI does not.
+
+commit 55f50888f22288b0fa45298d873dd265d7340aec
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 16:52:51 2008 -0500
+
+ Updated various excludes.
+
+commit b085f63d66519f93b59e3851b7e6796877e97107
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 16:47:24 2008 -0500
+
+ Documented Method, deleted unnecessary Method#module spec.
+
+commit f71f5c91e8ceab59d59614fe885dfeff096d7655
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 15:41:26 2008 -0500
+
+ More precise specs for Method and UnboundMethod #to_s / #inspect.
+
+ * Checks presence of own class, method name, name of the Module where
+ the method is defined and name of the Module where the method was
+ extracted from.
+
+commit 400b522d27515698e0a35b2507a4a8825ec9bf8f
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 15:22:46 2008 -0500
+
+ Rewrote Method#unbind specs, touch-up for Module#instance_method spec.
+
+commit 57bddb7b38dbb762b2469c51eb961e01f03c8518
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 14:50:38 2008 -0500
+
+ Updated UnboundMethod#== spec for Rubinius/MRI difference on Modules.
+
+ * Rubinius' UnboundMethod#== is true for methods from included Modules also.
+
+commit 8503c92f914d72e72eeeaede225f52242a1afad9
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 14:25:29 2008 -0500
+
+ Rewrote and added Module#instance_method specs.
+
+commit 8541f4cf83f8b776276e81ca41eb0f7b595e4fb7
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 13:43:36 2008 -0500
+
+ Improved/added UnboundMethod#bind specs.
+
+ * Removed unnecessarily specific error message check. Exception type
+ is plenty.
+ * Specified correct behaviour only in terms of Method since a Method
+ is returned and anything after that is not #bind's responsibility.
+ * Rubinius allows binding to any object that is kind_of? with respect
+ to the Module that the method is *defined* in. MRI requires that it
+ can only apply to objects of the same Module that the method was
+ extracted from.
+
+commit fc7073c85b5e201265e24a82c19bd6413681f6e1
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 13:02:24 2008 -0500
+
+ Removed trailing whitespace in UnboundMethod#bind specs.
+
+commit bde0cacff5f061accab7feb8a27b2417456f2f95
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 12:38:39 2008 -0500
+
+ Specced Rubinius to deviate in UnboundMethod#==.
+
+ * MRI requires that both UMs were extracted from the exact same
+ Module. Subclasses etc. are not OK even if the UMs both refer to
+ the original in the parent. This is somewhat nonsensical and
+ harder to implement so Rubinius allows it.
+
+commit e7ba146d3d0ef0aed1d297d157008661458723eb
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 12:16:07 2008 -0500
+
+ Much more comprehensive and precise UnboundMethod#== specs, documented.
+
+ * #== has some stupid behaviour but this is what we get.
+ * Explanation of criteria in the method doc.
+
+commit 43f2226c8882900a472f0a5347fa549936e8f000
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 09:37:51 2008 -0500
+
+ UnboundMethod String representation specs improved.
+
+ * Specs require that the returned String contains this object's class
+ and the [Module]#[method_name] it was extracted from.
+ * The spec specifies nothing else about the format or order etc.
+
+commit 67e3b5993d92776e0c9535549e8ffdb172225d52
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 09:11:19 2008 -0500
+
+ Trimmed whitespace for kernel/core/method.rb for patching.
+
+commit c61c5185589cf5a86b58b2e8c8b8d7a26cdc25ec
+Author: Scott Taylor <scott@railsnewbie.com>
+Date: Mon Jan 14 00:23:27 2008 -0500
+
+ fixing the specs for UnboundMethod#inspect
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 48bcca32329d48a20d5a6f2dd19598ea7b4167ce
+Author: Scott Taylor <scott@railsnewbie.com>
+Date: Mon Jan 14 00:16:24 2008 -0500
+
+ UnboundMethod#==, plus an extra spec
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 232015fed94b59adf627a7712da0d5d4d44c87d0
+Author: Scott Taylor <scott@railsnewbie.com>
+Date: Mon Jan 14 00:03:32 2008 -0500
+
+ UnboundMethod#bind
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit fcd0139307fd48f78f122457af1af43a543343ce
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 12:11:34 2008 -0500
+
+ Updated excludes for IO#write.
+
+commit eed253158fe0cc20b91f6c8dcc06f6a671092d84
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Sat Jan 26 14:36:33 2008 -0800
+
+ IO#write calls #to_s on it's argument
+
+commit bb5ff251bcc4baceac25a3a1fa64797b94551145
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 03:22:02 2008 -0500
+
+ Updated #attr_writer spec that was picking up a stray method.
+
+commit fa985a57f6cf802d6a83a6d02a31dd7fd33ebd36
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 01:20:27 2008 -0500
+
+ Module#const_get can now access top-level constants for Modules also.
+
+ * Modules explicitly check Object last, Classes already do it since
+ they all inherit from Object.
+ * Added some more specs too.
+
+commit fbc1cfb2d461891ee478802f44de3736959905a7
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 00:47:52 2008 -0500
+
+ Renamed the Module field 'parent' to 'encloser.' Some docs.
+
+ * When dealing with Modules and Classes, 'parent' is an ambiguous term.
+ In typical OO literature, 'parent' means the superclass which is not
+ the case here. Two separate sections of code already showedsome
+ uncertainty about the intent of this field.
+ * Added a few bits of documentation to Module.
+
+commit 92903e92564857350061d83f8eb3b54886986ac3
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 00:36:10 2008 -0500
+
+ Updated Module excludes.
+
+commit a705e687ce0d55e7ea184e1a3e67ba8d9d7c610c
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 27 00:24:20 2008 -0500
+
+ Improved Module#const_get specs for better coverage.
+
+commit bca6aef9b81166f9c5f4aeaafc673a54710d4d35
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Jan 26 19:57:33 2008 -0500
+
+ Corrected semantics of spec statements for Module#const_get.
+
+ * Specs were correct but the description was inaccurate.
+ * Prettified just a little.
+
+commit f699c18b68dee73086afb92d15b61745319a5321
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Jan 26 19:55:40 2008 -0500
+
+ Module#const_get specs for top-level constants by Le Huy.
+
+commit f3831a0693ea90271843bcc5910516e5a40ed3c1
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Jan 26 19:15:27 2008 -0500
+
+ Module whitespace cleanup before patching.
+
+commit e3cbe8136351f055bd99f10646d4f77515078430
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Sun Jan 27 00:51:55 2008 -0600
+
+ A few basic IO#write specs for file IO
+
+commit fd05adfedf70d795d8d91f650d5b76b05104dd7a
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Jan 26 18:54:35 2008 -0500
+
+ Specs for Enumerable#max_by (Rubinius extension.)
+
+commit 560b6460745c7821b9479b356c032a10daaa61ec
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Jan 26 18:53:52 2008 -0500
+
+ Specs for Enumerable#min_by (Rubinius extension.)
+
+commit 6ab0bc901bdc60bde1e251f72f0028dfb736a2dd
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Jan 26 14:25:41 2008 -0500
+
+ Updated excludes for Enumerable.
+
+commit 2e09eedb31e15c791e491e97bc2af1977a629c2b
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Jan 26 14:20:19 2008 -0500
+
+ Fixed Enumerable#max and #min nil problems using Undefined.
+
+commit d8e6ebf604fdcc228e9158336250dd29c6d35932
+Author: Jacob Maine <jacob.maine@gmail.com>
+Date: Sat Jan 26 13:22:33 2008 -0500
+
+ Failing spec for finding max when Enumerable contains nil
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 313ee6badb177c101e39e122c5b5d6ff4d73d93d
+Author: Jacob Maine <jacob.maine@gmail.com>
+Date: Sat Jan 26 13:01:06 2008 -0500
+
+ Failing spec for sorting a list that contains nils
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 467e8a60e0e25003894013e68f6d48e7bd6a22fc
+Author: Jacob Maine <jacob.maine@gmail.com>
+Date: Sat Jan 26 12:01:15 2008 -0500
+
+ adding failing spec for sorting enumerables that contain nils
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 8719a4ad46d7643c6e54aab3dffedb6831bde5f3
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Jan 26 13:42:11 2008 -0500
+
+ Whitespace cleanup before applying Enumerable patches.
+
+commit 015a0d023e8c649160800ddb8a269aa789266d51
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Jan 26 13:31:00 2008 -0500
+
+ Added/changed the Dir open specs after previous simplification.
+
+commit 6735df441af2489d47674b0cc500dab37dd4319e
+Author: Jonathan Younger <jonathan@daikini.com>
+Date: Thu Jan 24 17:09:49 2008 -0700
+
+ Simplify Dir#open spec dependencies.
+
+ The "takes a block which yields the Dir instance and closes it after"
+ expectation was failing because it depended on File.for_fd working
+ properly with closed file descriptors which it does not.
+
+ This revision removes the dependency on File.for_fd as well as
+ IO.sysopen (which is not yet implemented in jruby) such that
+ the spec now passes on ruby, rbx and jruby.
+
+ Signed-off-by: Eero Saynatkari <rubinius@projects.kittensoft.org>
+
+commit 27834ebec570c78011eaaf37998272d46ab9d118
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sat Jan 26 21:51:13 2008 +0100
+
+ Added Date#strftime specs and fixed some constants
+
+commit 767e58ec38af7c3bc78dd98541fb8235616e9691
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sat Jan 26 21:45:23 2008 +0100
+
+ Add spec for Rational#round, works because of added Numeric#round
+
+commit 2497d3b7b9d6112356204dc429c3c368e1a65573
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 26 12:31:45 2008 -0800
+
+ Templates for Rational specs.
+
+commit 5d63550a13cad4acbae3ae67e9ee9f672cbe5e61
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Sat Jan 26 00:47:35 2008 -0800
+
+ Revert back old date.rb, but use newer date/format.rb, with some fixes
+
+commit ecd3ee8a0a528f516283558585b86e729bd388ec
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Jan 25 21:47:38 2008 +0100
+
+ Updated not_compliant_on --> not_supported_on, where appropriate.
+
+commit 78ca098893d6231f74386eeadf0c30787f3dd2e6
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Fri Jan 25 12:18:43 2008 -0800
+
+ A couple of easy fixes, fix Time to handle 2 digit dates, pull in trunk date
+
+commit fe8433cda8ca49835e2581f35bbf0d31025e84c1
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Jan 25 18:42:31 2008 +0100
+
+ Better detection of AF_INET6 support in socket specs.
+
+commit 1834801229bf8b2c0abfea4c18448ed105691682
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Jan 25 18:16:56 2008 +0100
+
+ Added a guard for undefined AF_UNIX in Socket specs.
+
+commit 3af242cc180675272ee24d588f3328bc11342048
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Jan 25 14:32:04 2008 +0100
+
+ New specs for IO#seek, IO#pos=, StringIO#seek and non-fixnum args.
+
+ Rubinius fails all of them.
+
+commit 0ef7d55ebb5108bd5cf2f951236c8fade3999dfb
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Jan 25 13:42:08 2008 +0100
+
+ New specs for String#unpack with 'Q/q' patterns.
+
+commit 907081db80262a1403f659433934ef707c2ddee0
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Jan 25 12:35:07 2008 +0100
+
+ Adjusted socket specs, so they pass on MacOS (MRI/JRuby).
+
+commit 9cca76acbe066da357692a19d5af1c8f5e4601c9
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Jan 24 17:31:38 2008 -0800
+
+ Fix race in compiler version number, fix regex spec
+
+commit be18fcc2e0ee16f861f1e2bff0636c3288bce8d6
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Jan 24 15:10:00 2008 -0800
+
+ Be more flexible with set_priority (OSs are a bitch)
+
+commit 845336d81df42b5d1f93123ef148b78c2b220d25
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Jan 24 13:46:08 2008 -0800
+
+ Kernel flesh out, passes all but 1 spec now
+
+commit ab87e7641336dfb07f0ad99cc2881ec59a25053a
+Author: Jonas Pfenniger <zimbatm@oree.ch>
+Date: Thu Jan 24 12:36:50 2008 +0100
+
+ Changed __const_set__ to handle corner cases.
+
+ * Kernel#__const_set__ is now the catch-all. It triggers on things like :
+ M = 3
+ M::M = 3
+ * MAIN#__const_set__ is forwarded to Object
+ * Module#__const_set__: logic has been moved here, it is no more and alias
+ of Module#const_set because it needs to trigger a warning on re-assignment.
+
+commit 13dbdf62e802028cb61f9375196712f0b789ff37
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Jan 24 21:30:02 2008 +0100
+
+ Added some SystemCallError specs.
+
+ And exclusions for rubinius too.
+
+commit 75e2aac1d4b031fa36c8967549452436521b5eea
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Jan 23 19:41:30 2008 -0800
+
+ Rework Class.new and Module.new to initialize without VM help
+
+commit 2551e57644d091d44e5e2fa715a017a557a0b18c
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Jan 23 19:03:51 2008 -0800
+
+ Userland now uses __const_set__ for 'A = 3' syntax
+
+commit 400c5ceaf677aa2cd05a451c22144613ad7bdbe9
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Jan 23 14:32:26 2008 -0800
+
+ Introduce kernel/user land. Adds use of Module#__add_method__
+
+commit 9ee17f227ebe572b09d44b3b0d703b9f95717751
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Wed Jan 23 14:50:25 2008 -0800
+
+ Fix Hash#clone
+
+commit bf4875d337017736bd94781c1bf4cd7500fae5f5
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Wed Jan 23 22:55:02 2008 +0100
+
+ Implemented Enumerable#inject fix
+
+commit 0bd07f50ba75910ab579e3356dae93bc32b695bd
+Author: Jacob Maine <jacob.maine@gmail.com>
+Date: Wed Jan 23 15:46:06 2008 -0500
+
+ Enumerable#inject(nil) should yield nil as the first 'memo'
+
+ * Currently yields the first element of the enumerable instead
+
+commit 319f937284e60acc156c6b7f91e56d460e65ac94
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Jan 23 18:12:56 2008 +0100
+
+ Excluded the IO#new spec.
+
+commit 04da4120d939603d4a64aab71bbf94ca202e04b2
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Jan 23 18:11:45 2008 +0100
+
+ Added IO#new spec (block should be ignored, warning printed)
+
+commit 5617c3eb81a3f1d8f9a581695fe7897fadee500a
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Jan 23 17:50:48 2008 +0100
+
+ Excluded failures after IO specs additions.
+
+commit 107a072689bc9b97842f049f4fab2860ab79237d
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Jan 23 17:48:34 2008 +0100
+
+ Added specs for IO#open/popen, File#open with close inside block.
+
+commit bc3393a9041f8116d53bedfa6b604ec6dce3fd19
+Author: Jonas Pfenniger <zimbatm@oree.ch>
+Date: Wed Jan 23 14:25:13 2008 +0100
+
+ Revert "Added Module#name memoization spec". dbussink told me this behavior
+ is not wished.
+
+ This reverts commit ff411600202a59d00ffaca2c51330599c6b84966.
+
+commit 73e7d61d756cb7a06ea18b7f92c49bbebb06cc3f
+Merge: ff41160... 1dab607...
+Author: Jonas Pfenniger <zimbatm@oree.ch>
+Date: Wed Jan 23 13:57:28 2008 +0100
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit ff411600202a59d00ffaca2c51330599c6b84966
+Author: Jonas Pfenniger <zimbatm@oree.ch>
+Date: Wed Jan 23 13:55:36 2008 +0100
+
+ Added Module#name memoization spec
+
+commit 1dab607a79b79b370eda4776daf07a262451aea0
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Jan 23 13:28:29 2008 +0100
+
+ Added IO#close specs.
+
+commit c78091236495f4a16aa874de97cce3ec485c1f5b
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Jan 23 12:54:25 2008 +0100
+
+ Added IO#close_write and IO#close_read specs.
+
+ And all of them fail in rubinius. (not implemented)
+ And most of them fail in JRuby. (recent bugs)
+ They pass just fine on MRI 1.8.6 (p111 and latest)
+
+commit dc39943a4595855d64f23f9155a4e9cf658c39a3
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Wed Jan 23 10:37:45 2008 +0100
+
+ Small refactor of regexp_new
+
+commit 132ac4986a648dbf1354216145e5715a727a735b
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Jan 23 00:14:34 2008 -0500
+
+ Type.coerce_to no longer falls prey to identity fraudsters.
+
+ * Type.obj_kind_of? directly uses the internal kind_of instruction
+ so that overridden #kind_of? does not get in the way.
+ * Type.coerce_to uses Type.obj_kind_of? for its checks so that
+ Core can safely use it without worrying about breakage due to
+ overridden #kind_of?.
+ * Specs for both and a little documentation.
+
+commit 192882902154c9a68554337ccd1b8f3ee9aedd9e
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Jan 23 00:42:24 2008 -0800
+
+ Remove Symbol#to_i and Symbol#to_int. Farewell bastard children.
+
+commit f854667ff62528fe541c8cf67b9a1b291598d654
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Jan 23 00:35:29 2008 -0800
+
+ Removed Fixnum#to_sym and Fixnum#id2name, as well as fixed specs
+
+commit eb6cbc3604c81cc093edb1c182be1e456b05bef6
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Wed Jan 23 16:30:22 2008 +1100
+
+ Added specs for context iseq manipulation
+
+ Added specs to test MethodContext#reload_method and
+
+commit fd5fb764ee21b354b75b84f34906663874a24639
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Jan 22 22:28:02 2008 -0600
+
+ spec for DRb method call using a block
+
+commit a928762b48f7dc84bba0d43125063e9d8d54f183
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Tue Jan 22 19:15:10 2008 -0800
+
+ Better test of #instance_method.
+
+commit c055a5981bf4ecfd2efc0df74adb071056ff83b9
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Tue Jan 22 15:40:59 2008 -0800
+
+ removed remove_method_excludes.txt
+
+commit 08cb27454e7ae73e79bb432887dba917feaa1f92
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Tue Jan 22 15:36:36 2008 -0800
+
+ Clarified undef/remove specs a bit.
+ Fixed remove_method to raise NameError if you're not acting on local method.
+ Removed some fails_on calls to make specs pass... gonna remove them all soon.
+
+commit 62d93ac7916ff0d56a5b40ae1b9b501f10081638
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Tue Jan 22 15:35:05 2008 -0800
+
+ Fix up sysread and syswrite, disable testing for warnings on rubinius
+
+commit a482b17c4bfe9f40474839ba0cce2a37d8524c62
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Tue Jan 22 15:13:19 2008 -0800
+
+ Remove stale binding excludes
+
+commit f45030d33a9e1fe3c6bc111401a893e5649239f7
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Tue Jan 22 15:03:37 2008 -0800
+
+ Update Proc excludes
+
+commit 7f932fbdf5fa4e16df10d7731313d458ca21966c
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Tue Jan 22 14:55:50 2008 -0800
+
+ Add Proc#==
+
+commit 811cbe8ef876ef452051a9b07b3c95dbf57a7d9f
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Jan 22 23:41:55 2008 +0100
+
+ Removed debugging stdout from one spec.
+
+commit 2bf52de43bb90721d921f6d29504a8f098ed09b5
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Tue Jan 22 14:11:17 2008 -0800
+
+ Removed a lot of passing specs from the excludes
+
+commit b0e5a9ba6577c301f2737682d745128e268ebdab
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Tue Jan 22 14:02:38 2008 -0800
+
+ Fixed Symbol::all_symbols
+
+commit 68ae0b5acd647b9ebd73e53638b728cfaee6b6e0
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Jan 22 22:09:45 2008 +0100
+
+ Revert "Completed MRI's Module#name spec with corner case."
+
+ This reverts commit 970ede321d31ec75dd578866c683defe768fa356.
+
+ This spec seems like an implementation detail rather than
+ a specified behavior. It was agreed on IRC to revert it,
+ and that rbx won't support it.
+
+commit cc0e45cab2167e0fbc1d29308a5dcb4e7e077319
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Jan 22 22:06:28 2008 +0100
+
+ Revert "Module#name memoization work."
+
+ This reverts commit 7cd9fce4908aaeea9a35e273a3f15ed7ee7aa783.
+
+commit 996f9f4e5fc05f1b3aa618db3e1a4947730780b7
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Tue Jan 22 12:23:39 2008 -0800
+
+ Fix LongReturnException to be terminated in the correct place
+
+commit f453121dd2f3b4d9506a3f1c1e61d24e46bc9083
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Jan 22 20:53:45 2008 +0100
+
+ Corrected Module#instance_method failing spec. It was failing on all impls.
+
+commit c1d59239ddea95e73e2edd3a97ed6e1113a35d3c
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Jan 22 19:58:32 2008 +0100
+
+ Corrected Module#instance_method spec, it was failing on MRI/1.8/1.9/JRuby.
+
+commit ef5f4489caac2ad4bad94783a780aa40a054481c
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Jan 22 19:30:48 2008 +0100
+
+ Corrected String#to_f spec.
+
+commit 7cd9fce4908aaeea9a35e273a3f15ed7ee7aa783
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Sun Jan 20 22:00:34 2008 -0600
+
+ Module#name memoization work.
+
+ * Module#name is now memoized on access if @name is set
+ * Full module path is lazily calculcated on memoization
+ * Module#const_set(Ruby) and module_const_set(C) only set @name and @parent.
+ * The following methods unifily use module_const_set:
+ * cpu_const_set
+ * cpu_open_class
+ * cpu_open_module
+ * module_setup_name
+ * Module#calculate_name reworked, hack removed
+
+commit 7b4ef1344812faa76018ab41cc7fba97a3af8448
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Tue Jan 22 02:47:49 2008 -0600
+
+ implement more of Marshal.load
+
+ Float, obj._load, obj.marshal_load, IO.read, proc arg
+
+commit 41f07f0253a8fba205dbb0402e5d5e88c115d76c
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Tue Jan 22 16:32:01 2008 +1100
+
+ Fix Task#get_stack_value to not raise exception from primitive
+
+commit 14c811adaba3e8cfc5104d70e67c2e89c18cac4d
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 21 19:28:18 2008 -0800
+
+ Exclude Kernel#require is private spec when running with RSpec.
+
+commit 05a180e0051a0409c685d326a41e882545faaa53
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 21 19:26:41 2008 -0800
+
+ Explicitly run /bin/sh to get around limited /bin/pwd on linux.
+
+commit c09b3da391995a0e9006055ce19e838d3f180947
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 21 19:09:07 2008 -0800
+
+ Protect String#% specs from segfaulting on linux (ubuntu gutsy).
+
+commit d36b3f65f92b08ae078812788482387077d03380
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 21 17:55:03 2008 -0800
+
+ Removed use of `pwd -P` as at least ubuntu bin/pwd doesn't support it.
+
+commit 6d7a8292fb9a68a0dcfbd3f29f595e0ecf5902ae
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 21 17:54:11 2008 -0800
+
+ Unexclude Kernel#callcc specs as Evan's recent commits seems to fix it.
+
+commit eb04d409575772a85510770bd0db4f36490de6aa
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Mon Jan 21 13:28:06 2008 +0100
+
+ Fix Regexp error handling
+
+commit 93e50808eb7355c404a7f5295923083c8cf63549
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Sun Jan 20 22:00:34 2008 -0600
+
+ Quarantining IO#dup spec "sharing" example due to platform differences.
+
+commit ae9e2829becc495892c7ddce5eae67514f268120
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Mon Jan 21 10:26:56 2008 +0800
+
+ Update Module instance_method_specs excludes after revert put it back in
+
+commit df6c82f97987c233eab0534740054e2d0f0f2f2c
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Mon Jan 21 10:25:00 2008 +0800
+
+ Revert "Update CI excludes for Module"
+
+ This reverts commit 8aa00146f2eee9576094daa76c6f158b0deaf2e2.
+
+ * Fails when run with other specs
+
+commit 6f5245d4c20bf009bc120967f4a93d24faae66ba
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Mon Jan 21 10:23:15 2008 +0800
+
+ Revert "Update CI excludes for Symbol.all_symbols spec"
+
+ This reverts commit cb27e31b2a757ad108842bfa579eb9170d6cf244.
+
+ * Returns an F if run with other specs in ./bin/ci
+
+commit ec9677e593247ed8dfcbfc680151d04ac97936e3
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Mon Jan 21 10:11:35 2008 +0800
+
+ Fix Module instance_method_spec to match the inspect with a regexp
+
+commit 7e3474a16ec20094630e865594405ea7f1658c58
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Mon Jan 21 10:08:06 2008 +0800
+
+ Module#instance_method raises TypeError/ArgumentError on invalid arg
+
+ * Fixed spec to expect TypeError when passed nil
+ * Fixed spec to expect ArgumentError when passed non-symbol/string
+
+commit 8aa00146f2eee9576094daa76c6f158b0deaf2e2
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Mon Jan 21 09:41:37 2008 +0800
+
+ Update CI excludes for Module
+
+commit 9158b959d30babdceafc416650c1ba3234e5029a
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Mon Jan 21 09:10:06 2008 +0800
+
+ Add alias for Proc.to_s from Proc.inspect
+
+commit cb27e31b2a757ad108842bfa579eb9170d6cf244
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Mon Jan 21 09:09:40 2008 +0800
+
+ Update CI excludes for Symbol.all_symbols spec
+
+commit 7f16f313c907de0e22762d97fbba24e70c3259a3
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Mon Jan 21 08:46:36 2008 +0800
+
+ Raise TypeError/ArgumentError for invalid Thread key
+
+ * Raise TypeError is key is nil
+ * Raise ArgumentError is key is not Symbol or String
+ * Correct the description of Thread's element_set_spec to use #[]=
+
+commit 0b849f884beae9d11327e315da5c79fe789b8391
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sun Jan 20 23:05:33 2008 +0100
+
+ Added rubyspecs for Zlib.crc32.
+
+commit 67b52b6fb92b9e9a037e584474cff2dc97ce0163
+Merge: e6d8a61... 6f08d5e...
+Author: Jonas Pfenniger <zimbatm@oree.ch>
+Date: Sun Jan 20 22:30:52 2008 +0100
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit e6d8a61771b76198c0784677bb0a8fc97b1988bc
+Author: Jonas Pfenniger <zimbatm@oree.ch>
+Date: Sun Jan 20 22:30:29 2008 +0100
+
+ Fixed Struct#[] and Struct#[]= with negative indexes.
+
+ * Added corresponding specs
+ * Fixed kernel/core/struct.rb code
+
+ Example:
+
+ s = Struct.new(:x, :y)
+ x1 = s.new(:a, :b)
+ x1[-4] #=> should raise IndexError: offset -2 too small for struct
+
+commit 6f08d5e21473d0f2adff66a32acd46ddd8945fa0
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 20 12:47:11 2008 -0800
+
+ Added spec for Kernel.format.
+
+commit 6ab2691b455ac07643d98dc58f8a0f45487ab20d
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 20 11:52:56 2008 -0800
+
+ Added sane handling of non-reals for #format %e, %E.
+
+commit 1caab1ce237a52d78a402a5f0a7ce1d3ed9ac6b7
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Jan 20 18:55:27 2008 +0100
+
+ Add spec for singleton_methods and fix for Fixnum
+
+commit d7c46a0a1660f1d53e03a97571f3ec7b2431d0e4
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sun Jan 20 12:29:10 2008 +0100
+
+ Added failing spec for Regexp#new that could cause a segfailt. Needs error handling as stated in shotgut/lib/regexp.c:122
+
+commit d15c6605b7fb7db337d87ac1bd15f9a1371caa42
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 19 19:28:00 2008 -0800
+
+ Added language spec for return within a block.
+
+commit 3b516c028c4c9e064fbe839f0f9402a135eb90b0
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 19 17:27:13 2008 -0800
+
+ Added spec for class vars set from Kernel#instance_eval based on #267.
+
+commit 970ede321d31ec75dd578866c683defe768fa356
+Author: Jonas Pfenniger <zimbatm@oree.ch>
+Date: Sun Jan 20 00:44:04 2008 +0100
+
+ Completed MRI's Module#name spec with corner case.
+
+ It looks like Module#name is memoized in MRI
+
+commit caf440ac6a8037a2c223834c0ca4c5decd8e68ab
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sat Jan 19 22:48:31 2008 +0100
+
+ Revert "Wrapped one spec to prevent JRuby crash."
+
+ This reverts commit 9f266e3c785c7e3edbb6a30271f32debe6c14164.
+ JRuby issue is resolved.
+
+commit 9673e2c1c5a1142af52a0d82d8981bdd9e236c27
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 19 12:46:12 2008 -0800
+
+ Changed IO#syswrite to use should complain matcher.
+
+commit 8522186df7050782c4911f40aef381106e5e8c5b
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 19 12:29:12 2008 -0800
+
+ Added mSpec lambda { .. }.should complain matcher for warnings.
+
+commit 9f266e3c785c7e3edbb6a30271f32debe6c14164
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sat Jan 19 21:24:13 2008 +0100
+
+ Wrapped one spec to prevent JRuby crash.
+
+commit 54d1989997561271553ba72bd99f59ef2deb7c72
+Author: Jonas Pfenniger <zimbatm@oree.ch>
+Date: Sat Jan 19 19:13:06 2008 +0100
+
+ Fixed "X::X = 3". It would return a tuple instead of 3.
+
+ * changed shotgun's const_set instruction to push the variable on the stack.
+ * added corresponding specs under `language'
+ * found a new problem but added it to excludes
+
+commit d25ec129902789bc7d636ff5ccda8ff858ae38d3
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 19 10:21:19 2008 -0800
+
+ Added spec/README. Reformatted mspec/README. Removed old sprintf spec.
+
+commit 5eb06e3010707de1e273c23b3f0addf2ceaa824d
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 19 09:37:29 2008 -0800
+
+ Removed unused Sprintf, rename YSprintf to Sprintf.
+
+commit c144abc12230175a2a503c4426804ed19c8559e7
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sat Jan 19 16:21:08 2008 +0100
+
+ File::Stat time functions should return Time objects and added stat and lstat instance methods on File
+
+commit 177ef99db435a59e942566f7904167fc5e849d8d
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Sat Jan 19 22:24:16 2008 +0800
+
+ Fix ThreadGroup's add spec
+
+ * Fix is by initializing a new ThreadGroup on Thread setup
+
+commit 3fad84ec370eda1cab596adf5589e43240dfc381
+Author: Jonas Pfenniger <zimbatm@oree.ch>
+Date: Sat Jan 19 12:16:55 2008 +0100
+
+ Removed empty *excludes.txt for better searchability
+
+ `find -name "*excludes.txt" -size 0 -exec git rm {} \;`
+
+commit 9a2b1e6232f36c7a1508085b4606e25fbcf3cb4a
+Author: Jonathan Younger <jonathan@daikini.com>
+Date: Fri Jan 18 23:19:16 2008 -0700
+
+ Additional String#% platform specific formatting failure fixes
+
+commit aa32d6fcbed79b9e2afedc00f429ea78f5c540d3
+Author: Jonathan Younger <jonathan@daikini.com>
+Date: Fri Jan 18 22:12:23 2008 -0700
+
+ Fix String#% platform specific formatting failure
+
+ Darwin and FreeBSD return a different string format than other platforms,
+ so a different expectation is needed to match the appropriate format.
+
+commit c64dfd449dc89ec0016f14afd7f85522dbaa4148
+Author: Jonathan Younger <jonathan@daikini.com>
+Date: Fri Jan 18 21:05:44 2008 -0700
+
+ Moved String#% specs to ruby/1.8 and fixed to work with rbx and MRI
+
+commit e5e7f44983f1dbbc79726776b56a9cc7cb910e9f
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 18 19:53:50 2008 -0800
+
+ Updated IO excludes.
+
+commit b8b549dbc1aaf63e15717c3902d4485c97f845f7
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Jan 18 21:40:01 2008 -0600
+
+ Add regexp matching for output matcher and enable warning specs for syswrite.
+
+commit 004bd58b597034cbe734d9b7da318135a689190f
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Fri Jan 18 21:06:38 2008 -0600
+
+ implement some of Marshal.load
+
+commit 2c52db8022f060866d839992aaa6bff0f61963cf
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Sat Jan 19 11:21:55 2008 +0800
+
+ Fix UnboundMethod#bind
+
+commit a5680db20cf998f0db292e3b9aa69ed74fb19b10
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Fri Jan 18 17:49:44 2008 +0800
+
+ Implement UnboundMethod#==
+
+commit a1de7b0f405830f6bfe8000c051f4445135d8f63
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Jan 18 20:54:11 2008 -0600
+
+ Added some specs for sysread/syswrite on a file and p flushing to File.
+
+commit e7bc994d96398519ca205e87dec8e775bb0e67c6
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Sat Jan 19 01:31:46 2008 +0100
+
+ Fixed File#utime segfault
+
+commit 9887c6135e9353c3094dcf3c76d8e788b98f2bed
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Jan 18 22:00:10 2008 +0100
+
+ Added Time#strftime specs for '%U' and '%W' patterns.
+
+commit 0338fb5adb325e58d1ce61bccc9310fc7284e235
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Jan 18 20:35:23 2008 +0100
+
+ Added two testcases for String#% rubyspecs ('x', 'X').
+
+ There was a bug in JRuby's sprintf, which wasn't
+ detected by rubyspecs.
+
+commit 97db9fb72d6205227d61d92ed3153331b2328f97
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 18 10:50:00 2008 -0800
+
+ File#utime spec. Some cleanup of File#open specs.
+
+commit 10647cf8abfd0ea7a87d39978a22f68fdfa9fbd6
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Jan 18 17:08:04 2008 +0100
+
+ Wrapped 3 IO spec tests into fails_on :jruby.
+
+ Because these tests just break the spec run completely.
+
+commit cf6195eeabe382c4267e295ab786acedaed89050
+Author: Jonathan Younger <jonathan@daikini.com>
+Date: Thu Jan 17 21:55:48 2008 -0700
+
+ Fix specs that use `pwd` to use -P option so that symlinks are resolved
+
+commit 1b79705fb965ecd6fc897b6bf14c605d8325dabe
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Fri Jan 18 15:57:46 2008 +0100
+
+ Added IPAddr specs by manveru. Closes #262
+
+commit f05b96b33970e3f08da5c8992f7c6cb710649f42
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Fri Jan 18 02:23:15 2008 -0800
+
+ Fix spec for IPv6 environments
+
+commit 2007019ebad7974d7a54e6d599320675548313f0
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Fri Jan 18 17:39:09 2008 +0800
+
+ Fix UnboundMethod#inspect to use regexp
+
+ * Also aliased UnboundMethod#to_s to UnboundMethod#inspect
+
+commit c47b473b99b59074673adb7e8d50a250e34436e7
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Fri Jan 18 17:37:29 2008 +0800
+
+ Use a regexp to match the inspect output instead of deviating on rbx
+
+commit 8dc2a2b3115a49a15ed931301b1999560ee27db5
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Fri Jan 18 01:05:14 2008 -0800
+
+ Fix up specs and finalize LongReturnException
+
+commit 7c30ca7337b56a4194eb58952f74662e222b7707
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Jan 17 23:23:27 2008 -0800
+
+ Add support for return in a block obeying ensure properly
+
+commit c06fc665c6bf5898163f2854b93d62b8b314216e
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 17 21:20:42 2008 -0800
+
+ Changed Exception#backtrace to return an MRI compatible one.
+
+ * Exception#awesome_backtrace returns an Rubinius Backtrace
+ instance, as Exception#backtrace used to.
+ * Added templates for Backtrace specs.
+ * UnHACKed lib/test/unit to use the #backtrace as expected.
+
+commit a29f35c5a45776f10132c3ce0ef058b1e98a4f75
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Jan 17 20:18:04 2008 -0500
+
+ Guard failing Process.setpriority spec, add an exclude to CI
+
+commit 333d5c6920c01366c8b2887ecc7e33f775210c00
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 17 16:50:31 2008 -0800
+
+ Specs and fixes for Class.inherited.
+
+commit ac90d87a69c19c441b854660105d21ed771989f0
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Thu Jan 17 16:37:31 2008 -0800
+
+ One more step into the foray of bootstrap madness... removed useless 0 from lasgn nodes. needs full clean
+
+commit 0dbabefd081be4890d0d789a9c3ec122b9196cf8
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 17 12:00:55 2008 -0800
+
+ Fixes to enable RSpec 1.1.2 to run the spec/ruby specs.
+
+commit 9bd611ff5c5b411518c2f4ce5d3cd4b93f4bcebe
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Jan 17 08:25:24 2008 -0500
+
+ IO#puts, #isatty fixes from Dan Lucraft, slightly modified.
+
+ * The #puts spec exposed an issue with String#suffix? which Ifixed in
+ fa9a6c which means IO#puts did not need to be changed.
+ * #puts spec uses output_to_fd.
+
+commit 3f519a98bbc3a66d59884add5fcd98d5ca095149
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Jan 17 08:02:51 2008 -0500
+
+ String#suffix? specs and correct behaviour.
+
+ * The suffix can be the entire string, there is no need for it to
+ just be a substring always.
+
+commit ca6fa9cd760b06827f4f953ff28e8baed357f447
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Thu Jan 17 12:48:20 2008 +0100
+
+ Additional Date specs
+
+commit 8541022ffc918879142ecb3707e977050f774ece
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 17 00:47:04 2008 -0800
+
+ Added ?d, ?e, ?f to Kernel#test.
+
+commit e6f36980c2c94414e5c051b35d9ce403c492f1a2
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Thu Jan 17 02:18:25 2008 -0600
+
+ Fix my oops; missing 'do' for the fails_on
+
+commit 44483d8e414f107b3202cc69b8cdfbbe1222ee33
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Thu Jan 17 02:07:57 2008 -0600
+
+ Add a fails_on guard to IO#printf spec for JRuby; output dies otherwise
+
+commit 53a36c934ec44fad7e6d18424cb13b37496cf720
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 16 18:39:34 2008 -0800
+
+ Updates to compiler and core to protect Fixnum#/.
+
+ * Added compiler plugin SafeMathOperators.
+ * Added very simplistic way to pass flags to the compiler.
+ * Added -frbx-safe-math flag
+ * Changed core Fixnum, Float, Bignum, and Numeric methods
+ to use #divide rather than #/. Aliased #/ to #divide.
+ * Updated Rakefile to send flag when compiling core.
+
+commit feb260b904d87487428b558f7b7e9ac0170c160c
+Author: Ryan T Mulligan <ryan@ryantm.com>
+Date: Wed Jan 16 22:47:02 2008 -0600
+
+ very minimal SHA1 specs
+
+commit 511732d932d2fe934968c78b89cefa46e699b996
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Wed Jan 16 16:30:40 2008 -0800
+
+ Moved old spec excludes to new location and deleted all old
+
+commit cae6bba077190e158ceee7b8991daf16fd8c55d1
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Wed Jan 16 13:36:19 2008 +0100
+
+ Added more Date specs
+
+commit 75d49657f31091d37dfdba1fc5487164db861802
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Wed Jan 16 15:58:42 2008 -0600
+
+ more specs for TcpServer and TcpSocket
+
+commit b1d45cb0fdc573bfe5995456d846c11747d48b90
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Jan 16 13:31:33 2008 -0800
+
+ Implement ObjectSpace.define_finalizer, using WeakRef.
+
+commit 4e8a0d264dc7d5a4866a1a1b83238bebb47e4ab6
+Author: Jonas Pfenniger <zimbatm@oree.ch>
+Date: Wed Jan 16 21:51:52 2008 +0100
+
+ Removed platform-specific specs in spec/ruby/1.8/core/signal/list_spec.rb
+
+ There is not direct way to know if a signal exists or not, since it all
+ depends on <signal.h>. In practice, there is no real risk for rubinius
+ to miss a signal.
+
+commit 32537f8d3378154f7f52c278cd56a7d4159a3446
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 16 12:41:54 2008 -0800
+
+ Added IO#printf, fixed Kernel#printf to use IO's.
+
+commit e7bccb3f38f6ace3cb25a9f227ab5f6b1d2be346
+Author: Jonas Pfenniger <zimbatm@oree.ch>
+Date: Wed Jan 16 21:19:48 2008 +0100
+
+ Signal.list spec now passes. bin/ci removed list_excludes.txt
+
+commit b3a6461af30f2c144b4ee65e8539c51291e0156b
+Author: Jonas Pfenniger <zimbatm@oree.ch>
+Date: Wed Jan 16 20:03:15 2008 +0100
+
+ kernel/core/signal is no more platform dependent
+
+ * Now publishing platform.conf with rbx.platform.signal.* (only using the ones
+ defined in MRI's "signal.c")
+ * Signal::Names is now published with those values on @after_loaded@
+ * New method: Signal.list => Signal::Names
+ * Added EXIT=>0 and CLD=CHLD exceptions (see "signal.c" in MRI)
+ * Updated the corresponding specs for more details (on FIXME, please help !)
+
+commit bdbd712a5953f011f8d6f1142d50a452e1607f65
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Wed Jan 16 13:42:01 2008 -0600
+
+ updated Continuation excludes -- Kernel#callcc specs still bleedover
+
+commit 22d32d3461660ee7cd29760163b622fc94b6ea5b
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Wed Jan 16 01:55:21 2008 -0600
+
+ apply Marshal.dump patch by Justin Bradford. #252
+
+commit 513de8ab67ab9c017285a48108ccceb185ebaf24
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Wed Jan 16 16:44:34 2008 +1100
+
+ Bunch of Debugger fixes
+
+ * Debugger now has proper quit behaviour, which causes
+ the debugger to remove all breakpoints, clear the debug
+ channel, and resume the debuggee.
+ * Fix singleton(-ish) semantics of Debugger; essentially,
+ only a single Debugger instance can be instantiated at
+ one time.
+ * Added a bunch of specs for the above
+ * Changed Rubinius::VM.set_debug_channel to accessor
+ style Rubinius::VM.debug_channel.
+
+commit 2174009b215ce2f0445fc8df4711e7e6c64b0332
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Tue Jan 15 21:59:30 2008 -0600
+
+ add Marshal.load specs
+
+commit ec002dd0f0daddedaa5241c4f8f6d85fad0e9768
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Wed Jan 16 09:47:28 2008 +1100
+
+ Move VM under Rubinius namespace
+
+commit 983c54400542a03535accf2705ae227ae58970dc
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Tue Jan 15 15:24:10 2008 -0600
+
+ Added spec for File.new coercing filename using to_str.
+
+commit ff6a081de28711b0d8c1136e6e4272baf769043c
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Jan 15 14:29:43 2008 -0600
+
+ since DRbObject is within DRb it should be a subdirectory but mkspec generated the wrong path to helper
+
+commit a48cbbd3f5da3c971a215423b3e27b058de04196
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Jan 15 21:15:09 2008 +0100
+
+ Add more Date specs
+
+commit 9de289f1bbae86b12bc383e7e535de404f8aaa5f
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Tue Jan 15 14:58:31 2008 -0500
+
+ Add a servent class to StructGenerator for Socket.getservbyname
+
+ Also, add Socket.htons and Socket.ntohs for byte order encoding
+
+ And complete Socket.getservbyname along with specs.
+
+commit d9e37ff3c0f975a418fafbc7163ee1a9717dd92b
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Wed Jan 16 03:31:05 2008 +0800
+
+ Fix Proc#[] calling the wrong #call method
+
+ * Re-aliasing in Proc::Function because aiases don't follow subclass
+ methods
+
+commit 2273c919e80ab7186e3139941dc4d73a292bcd2d
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Jan 15 19:30:23 2008 +0100
+
+ Add add and minus specs for Date
+
+commit 1325e22c11c48c366d9f0387823de5941b59df66
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Jan 15 16:35:23 2008 +0100
+
+ First specs for Date object
+
+commit a3b76d162e58e75b4523151bb6911c840db8319f
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Tue Jan 15 12:58:14 2008 -0500
+
+ Implement Socket#pair (and Socket#socketpair) with corresponding spec.
+
+commit 836f1cf828ab62606a6b0e2f7313228b7482dcbe
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Jan 15 11:47:11 2008 -0600
+
+ generate spec files for DRbObject
+
+commit ef99f25be36f6ccd33b297bed14c1175847f1ecc
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Jan 15 11:31:08 2008 -0600
+
+ generated spec files for DRb with a basic spec for DRb.start_service
+
+commit 9637cf1e77efd1a3b53e6c4d82a7c7afe8509621
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Jan 15 18:48:09 2008 +0100
+
+ Eliminate stdout from IO#dup spec runs.
+
+ At least, under JRuby it was printing things like:
+ "No such file or directory".
+
+commit 30a2fce2a4fd7e840586ce8ae390ecb632c8bee0
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Tue Jan 15 23:00:32 2008 +0800
+
+ Implemented Dir#pos which fixes #pos, #pos=, #seek, and #rewind specs
+
+commit 013ab2e88ecd8d887c6a0009e7f8d2add4849143
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Tue Jan 15 22:52:26 2008 +0800
+
+ Revert "Updated CI excludes"
+
+ This reverts commit 15d1c7674496a99bf1d5ec42420864b22bf1569a.
+
+commit 15d1c7674496a99bf1d5ec42420864b22bf1569a
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Tue Jan 15 21:50:55 2008 +0800
+
+ Updated CI excludes
+
+commit 18470055d83a43c3371609aaac4471767adb3b1b
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Tue Jan 15 04:32:54 2008 -0800
+
+ Make TCPSocket.new work. Use socket library names for familiarity.
+
+ Make inheritance hierarchy of sockets better match MRI.
+
+ Add syscall names to Errno.handle checks.
+
+ Spec less of the socket library.
+
+commit bd34303986a068b40cce1366c85ea288fc24a3f5
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Mon Jan 14 23:55:38 2008 -0500
+
+ Regexp subclasses work now. Documented Regexp.new.
+
+commit 343acee55519fc97a35a9d50e8bdcfd679d432b7
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Mon Jan 14 23:49:47 2008 -0500
+
+ More Regexp.new specs.
+
+ * Subclass initialization verification.
+ * Multibyte options are case-insensitive.
+
+commit 758a468ffafdeea78016dbbce78f21e19f6735f6
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Mon Jan 14 18:22:32 2008 -0800
+
+ Fixed require modifying LOADED_FEATURES even if require raises an exception
+
+commit 5c8ff74b64f7ec6bd4c413b0e0e93334dff009ca
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Mon Jan 14 21:57:19 2008 -0500
+
+ Fixed Regexp#kcode specs.
+
+commit 34867cc1f1f3b7ac3145fb926491c0dc44629312
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Mon Jan 14 22:41:58 2008 -0800
+
+ Add Socket::getaddrinfo. Raise SocketError appropriately.
+
+commit e2009a38a8e1ef0dff6394b92a677f3120280f72
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Mon Jan 14 22:39:55 2008 -0800
+
+ Remove platform-specific code, remove spec of socket library behavior.
+
+commit 5afa1c34808c68c17bc02f5f76c42d64efdd7dd2
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Tue Jan 15 00:32:14 2008 -0600
+
+ Modified retry-in-rescue example to test nested blocks and be clearer.
+
+commit 17fd0cb781ec90d268668c5678e1135eb5f6e323
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 14 22:28:46 2008 -0800
+
+ Added Module#autload?.
+
+commit 96ca83312d1b5a1e38e25f94504f6f69a137b96d
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 14 21:50:37 2008 -0800
+
+ Updated CI excludes for language.
+
+commit ef4f49de672d40f43f53dadff1aa8fdbcafe1d45
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Mon Jan 14 21:37:23 2008 -0800
+
+ Fix specs for dregx change, fix regexp for specs
+
+commit 5cd2ef2a173394910249d93d8ef433d220f2d9a9
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Tue Jan 15 16:32:15 2008 +1100
+
+ Fix breakpoint specs
+
+ The breakpoint specs were interfering with one another,
+ due to the fact that each was modifying the bytecode for
+ a fixture class that is compiled only once.
+
+ Workaround this by saving off the bytecode and resetting
+ before each test.
+
+commit 29bf88b07f87182d94fcf7c550724efc07067239
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 14 20:00:27 2008 -0800
+
+ File spec/data/critical.txt is empty! Congrats to everyone!
+
+commit 8082760cc2215742464a9846295ec4a8a0c49244
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 14 19:37:33 2008 -0800
+
+ Removed Module methods from critical excludes.
+
+commit d075c115087c001d0d35562aeeea21efadc5e3b6
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 14 18:53:46 2008 -0800
+
+ Added not_compliant_on :rbx for class variable specs that use Fixnums.
+
+commit 23f1b523da2478f2ad962f0045dca3e7034f9b56
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 14 18:16:46 2008 -0800
+
+ Multiple fixes for #class_variable_get/set. Updated CI excludes.
+
+commit dbc5675058aa426dbfbbf7489d5393819edb16f8
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Mon Jan 14 16:42:27 2008 -0800
+
+ Fix attrasgn usage to pass specs
+
+commit 3e250999d6f1a7fdaf2bb5cd169a1024e2ab5ddc
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 14 16:07:08 2008 -0800
+
+ Removed leftover excludes for compiler[12].
+
+commit a2b8b5511e79b47fa7777e716ee16511fdec3fd4
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Mon Jan 14 16:47:21 2008 -0600
+
+ Remove the goofy Hash#delete spec and replace with two others.
+
+ The old version of this example depended on individual hash buckets having a
+ specific ordering, which overreaches a bit. The new version, while a little
+ cumbersome, should work correctly regardless of hash implementation or hash
+ and bucket ordering.
+
+ I also moved out a few lines that were unrelated to this example into a
+ separate one.
+
+commit 67d858885f1841e9c9aa295150da3c472949198d
+Author: Gregor Schmidt <ruby@schmidtwisser.de>
+Date: Mon Jan 14 14:57:53 2008 +0100
+
+ Passes Module#extended specs by added extended method to module and adding a call to it in Object#extend
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit 302ba965def902ccc5d3e97ed6bd5841f09d8f00
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 14 14:14:08 2008 -0800
+
+ Philipp Brüschweiler's patch for String#%, #242.
+
+commit abaf2efa9e467bb7b5ef3b53b8490f1e056a832e
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Mon Jan 14 16:59:23 2008 -0500
+
+ Another round of socket specs, and add a Rake StructGenerator to find sockaddr_un if it's available
+
+commit f5d0e435023a80bcc4c101a8d3ab9fc056a14c80
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Mon Jan 14 15:52:47 2008 -0500
+
+ More socket specs
+
+commit 7bbc927a9d8a6f9202025be62a3db861ced3216f
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Mon Jan 14 14:58:34 2008 -0500
+
+ More socket functions and specs.
+
+ Namely, this implements a Socket::SockAddr_In class that is a FFI::Struct around the
+ sockaddr_in C struct. This gives us a better ability to inspect what's going on in the
+ struct from the Ruby side of things.
+
+commit f351c6d3d8831705f0398abdae240abba9252a75
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Mon Jan 14 11:09:04 2008 -0500
+
+ More socket specs update
+
+commit cb8ce936394cafa00f77008083bccf9cded59f28
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Mon Jan 14 14:39:41 2008 -0500
+
+ Split process/constant expectations into Linux and BSD sections
+
+commit 0964d53edd80367611f63cd6eb4b294ec898cc8d
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Mon Jan 14 10:04:53 2008 -0500
+
+ Revert IO#dup spec to unmask errors, removed FileUtils dependency.
+
+ * Any errors occurring in specs should generally be raised normally
+ so that any potential problem or spec deficiency is exposed.
+
+commit 04f542e928c5fa0df460d8f11b4d87e008fa343f
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Mon Jan 14 13:36:49 2008 -0500
+
+ Update Process::Constants to fetch values from RUBY_CONFIG
+ Update process/constants_spec so that it passes on MRI as well
+
+commit 0ad02b57fd040196d11662bd1ab9b259dc2ce6d2
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Mon Jan 14 12:23:20 2008 -0600
+
+ squash Marshal.dump bugs
+
+ * fix order of evaluation problem
+ * put more objects in links and symlinks hashes
+
+commit ed98b9a14459b011f97fee5c781410c4d413ed9a
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Jan 14 17:36:00 2008 +0100
+
+ Updated Arry#pack specs to guard for always big-endian JRuby.
+
+commit 550f07dc7551573a975183209ba8904fdbd62607
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Jan 14 15:57:04 2008 +0100
+
+ More robust cleanup in IO#dup specs.
+
+ Without it, mspec against JRuby was reporting EIGHT
+ failures, while only 5 tests are actually exist.
+
+commit 1ea4f82183190c4c87da48c381f1db417c7403ac
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Mon Jan 14 09:26:36 2008 -0500
+
+ Updated some socket specs
+
+commit e20ab7ea377cd39209011b44204d2688b53611c5
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Mon Jan 14 02:23:26 2008 -0500
+
+ Fixed Regexp.new kcode setting, improved Regexp specs.
+
+ * Regexp kcode can be upper- or lowercase.
+ * More robust Regexp#options and #inspect specs.
+ * Updated Regexp excludes.
+
+commit cc71f359aa65101d2c00cfbb0c396b7cdc697ef2
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Mon Jan 14 01:25:21 2008 -0800
+
+ Improve bytecode performance a tiny bit, fix Kernel#`
+
+commit c561368c03c605de41746fac2ce5a6386fcf4f54
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Mon Jan 14 03:17:15 2008 -0600
+
+ Quarantine a suspicious TCPSocket.new speck failing on MRI on OS X.
+
+commit 71a9cf2afbbe6903b8d652b3ee201957e0b0c633
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 13 22:58:16 2008 -0500
+
+ Finished IO#print specs.
+
+commit ff75b95a690051736f49a9a113d21027f7f03e92
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 13 22:44:21 2008 -0500
+
+ IO#dup and specs.
+
+commit 02f1c03f4df3327ce1ddd20e2249a5e9830627a0
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 13 23:11:48 2008 -0800
+
+ Justin Bradford's patch for Float failure, #237.
+
+commit e43c148954ad609c438e5a4f14811c0349239374
+Author: Matthew Draper <matthew@trebex.net>
+Date: Fri Jan 11 21:35:57 2008 +1030
+
+ Kernel#Integer is very fussy about the strings it accepts.
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit adad84f7a26bf40809366f2f7b6acfc61dcfefc2
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Mon Jan 14 00:31:12 2008 -0600
+
+ Class.inherited gets invoked regardless of visibility
+
+commit c746fad52e9503d04c3cf65de979b0a5a9f9e495
+Author: Matthew Draper <matthew@trebex.net>
+Date: Mon Jan 14 10:25:58 2008 +1030
+
+ Many of Kernel's methods should be module_functions.
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit 0b8a4bfefaed3179f96721fdde35e32ed8ff7263
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 13 21:43:45 2008 -0800
+
+ Update CI excludes for Regexp. Remove empty CI exclude files.
+
+commit 89a87edbc61a877621c6f43266000aff32e92ae7
+Author: Warren Seen <warren@warrenseen.com>
+Date: Mon Jan 14 07:12:13 2008 +1100
+
+ Fixes visibility of methods passed to Module#module_function
+
+ * Make instance methods versions of functions passed to Module#module_function private
+ * Correctly identify visibility in error message raised in Module#set_visibility
+ * Added specs for module_function
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit 25d6fa558f88732d1aa28c68b0eb7c9910366243
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 13 20:42:52 2008 -0500
+
+ Updated Regexp excludes.
+
+commit 69e200276898f1c9208be527bdc64c318c56f86e
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 13 20:24:42 2008 -0500
+
+ Shared spec for Object#dup and Object#clone.
+
+commit 8a6fe609224c126bcf86987edd3f0690fc9e45ff
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Jan 13 18:19:56 2008 -0500
+
+ VM calls private hook methods now. Object#dup and #clone specs.
+
+ * Object#dup and #clone have rudimentary specs which also partially
+ confirm the private hook fix through #initialize_copy.
+
+commit 84773b6ba63ea6f715dcc4e99e0a8a2e2b739152
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Fri Jan 11 10:46:15 2008 -0500
+
+ Specs for Regexp.{new,compile}, updated excludes for same.
+
+commit 6c1603723bba7d58203aa9b03bbf92b4900e53d1
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 13 18:49:40 2008 -0800
+
+ Numerous fixes for File::Stat.
+
+ * Implemented readable(_real)?, writable(_real)?, executable(_real)?.
+ * Implemented a number of helper methods like rowned?, rgrpowned?,
+ superuser?, rsuperuser?. Made these private.
+ * Implemented owned?, grpowned?.
+
+commit d1b05e0bf98a3cdfda8a3d2398e78035a49c0c66
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Mon Jan 14 12:25:41 2008 +1100
+
+ Deprecate meta_send_stack* opcodes
+
+ The code path for these opcodes is almost identical to
+ send_stack, and no measurable performance improvement
+ comes from using them.
+
+commit 154fe5e1faad94f371c51a979240a6d7f5cd8909
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Sun Jan 13 20:07:12 2008 -0500
+
+ Implement BasicSocket#setsocketopt for String optvals, and add a spec for it.
+
+commit c21636d6b2502db344049e7dc62d42ff8c18b040
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Sun Jan 13 19:34:50 2008 -0500
+
+ Add specs for BasicSocket#getsockopt
+
+commit 1584f41148b8d8967df4c3ee6376b59919cb7db3
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Sun Jan 13 19:24:58 2008 -0500
+
+ Add Array.pack for i, s and l arguments.
+
+commit 7131e187e19bf0889f8ece802495865f7b3f1e5c
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Sat Jan 12 13:55:20 2008 -0800
+
+ Cleanup String#split, add edge case check
+
+commit 4ff46602c8a54a61697bb8d9eaa9ae89e56f7abe
+Merge: 1c95721... 908ccff...
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Sun Jan 13 14:49:48 2008 -0500
+
+ Merge branch 'socketspecs'
+
+commit 908ccff0a854038372dad0780e1de35727e2d657
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Sun Jan 13 14:49:17 2008 -0500
+
+ Some TCPSocket spec mods
+
+commit 1c95721bd873c4b30c187bfa7673cd7e3568a0fb
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 13 10:20:43 2008 -0800
+
+ Fixed File::Stat specs to output method name correctly with -f s.
+
+commit acb7505d41aa789157e50962253e686827a702d5
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Sun Jan 13 12:07:06 2008 -0600
+
+ update CI excludes
+
+commit c8db419ae06e9642b346e1bcae99367f3b72845f
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Sun Jan 13 11:55:50 2008 -0600
+
+ squash bug in Marshal.dump
+
+ symbols need a separate links hash
+
+commit eb953ae2c3fdeac4ae13b5461246b9f51b0f39cc
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Sun Jan 13 09:40:39 2008 -0500
+
+ Make the spec text more verbose
+
+commit db013bc06cef2dea4b77a215d4437e2172b391b6
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Sun Jan 13 09:04:08 2008 -0500
+
+ Commit an updated spec that shows the failure on rbx and passes MRI,
+ w.r.t. opening a module and aliasing a private module function from that
+ module.
+
+commit 759a9f8bd70ead9b5d2fc67b3872e3bf3bd34001
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 13 01:45:07 2008 -0800
+
+ Updated CI excludes for File::Stat.
+
+commit 107feb74eaf01c09d8c5bd14ac29e53900a5ed26
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 13 00:38:00 2008 -0800
+
+ Modified File, File::Stat, FileTest specs.
+
+ * Added templates for File::Stat specs.
+ * Added shared specs for some File::Stat methods.
+ * Altered toplevel File shared specs to take the name
+ of the constant to enable File::Stat to use a fixture
+ proxy but still have the correct name show with -f s.
+ * Split out specs for missing files because File.[l]stat
+ behaves differently than e.g. File.file?.
+
+commit e1a13f7ecfe7f2d18fd6ac20dd8c63cbd6d11855
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Sun Jan 13 02:26:09 2008 -0600
+
+ implement more of Marshal.dump
+
+ obj.marshal_dump, IO.write, depth limit, exceptions
+
+commit bc070232eab1bfa5d294897487339d259a406e74
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Sat Jan 12 15:16:21 2008 -0600
+
+ implement more of Marshal.dump
+
+ Float, obj._dump
+
+commit ad7a67ed5a3a1399773dda74c4688e9b00c8f9aa
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Sun Jan 13 01:46:36 2008 +0800
+
+ Update CI excludes for Process.initgroups
+
+ * It was affected earlier by the Enumerable lambda/Proc-arity issue
+
+commit 7b7a1e3e4712f35688823543b7a7c3c25405ef77
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Sun Jan 13 01:40:22 2008 +0800
+
+ Fix implicit block in Enumerable not passing the arg check in Proc#call
+
+ * Changed instances of lambda to Proc.new and arity once again
+ returned the correct value. Will investigate, but until then, this
+ passes.
+
+commit d9c21aaa18044bd54ed3b1f6ec5daacf9bd250fa
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Sun Jan 13 01:02:00 2008 +0800
+
+ WIP Fix block argument checking
+
+ * Passes all the proc/lambda call specs
+ * However, specs for methods that add implicit blocks like
+ Enumerable#all fail because their arity is somehow 0 - excluded for now
+
+commit 3d400bc8a91a793f49dcf5655dc28e6141d999d0
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Sat Jan 12 20:03:26 2008 +0800
+
+ Update CI excludes and add Module#class_variable_get to critical.txt
+
+commit 6bf7b8616837649ddd2c1435a54c86ed30910985
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Sat Jan 12 19:27:12 2008 +0800
+
+ Move custom classes for NoMethodError specs into fixtures
+
+commit 10cc61bb816ae67a7fad5b135f66d263d7ee07b1
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Sat Jan 12 19:20:27 2008 +0800
+
+ Swap the protected/private method calls around in the NoMethodError spec
+
+commit 6b2e66d3f9222b52cdae42b57206363ad47949e2
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sat Jan 12 11:51:28 2008 +0100
+
+ Corrected Module#alias_method spec.
+
+ Now it should pass on both MRI and JRuby.
+
+commit 67f74a936655b72c689d09c77d9fbe9d7194a0a0
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Sat Jan 12 17:50:22 2008 +0800
+
+ Fix proc/lambda/Proc.new arity
+
+commit 03440114d5e3f07111cdcae3657258cae4c803e7
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Fri Jan 11 13:24:26 2008 +0800
+
+ Fix Kernel.Integer parsing of invalid String
+
+commit 598598c10c66de38b52a8092cdd2fa99604eda6e
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Sat Jan 12 00:21:22 2008 -0800
+
+ Clean up expectations to use a common list.
+
+ Update excludes for other things using shared glob specs.
+
+commit 55aa5a1f10655618e45d0ec84502cc13c982227e
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Fri Jan 11 23:31:45 2008 -0800
+
+ Specs for File.fnmatch handling of Regexp specials.
+
+commit 7c0dc7edfcdf4948047ba051b0cbed7ba761f1dc
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Fri Jan 11 21:57:10 2008 -0800
+
+ Dir.glob support for {}.
+
+commit 9a097fe5634c1109919d1e120b1276827371c332
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Fri Jan 11 19:42:31 2008 -0800
+
+ Exclude {} specs for WIP Dir.glob
+
+commit ccdc6f5ae5fabbd0d2c32072811e2ecf7cca8987
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Fri Jan 11 21:40:04 2008 -0500
+
+ In MRI, you can alias private module methods. Not so here. This spec catches it.
+
+commit 8b402d1e32dc283124375374532024f6cfe7020d
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 11 17:35:21 2008 -0800
+
+ Added toplevel shared specs. Converted File, FileTest specs.
+
+ * spec/ruby/1.8/shared is the directory for sharing
+ specs across multiple classes.
+ * Added methods for FileTest
+
+commit c6aea2e10d7a4d0ee14175d5b79894e1e11699b1
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 11 15:59:34 2008 -0800
+
+ Converted File/FileTest#exist(s)? to toplevel shared specs.
+
+commit 06a5d8a3d5874303a71e4e9b939b44c204041edf
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Fri Jan 11 17:39:39 2008 -0500
+
+ Fix failing specs in udpsocket/open_spec.rb
+
+commit 530e40005d09140fdb55608890f0994f3a48d8be
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Fri Jan 11 13:11:58 2008 -0500
+
+ Observer specs
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit 0907a20d2bad2207be8e937c403c49634f3a23b6
+Author: Caleb Tennis <caleb@tarknology.com>
+Date: Fri Jan 11 12:24:44 2008 -0500
+
+ Add observer to lib and base-spec file
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit ec0ff1dfa1ee9de38d35537bec5071f6bb31cf7f
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Fri Jan 11 13:11:02 2008 -0600
+
+ implement more of Marshal.dump
+
+ Array, Hash, links
+
+commit 640e81394ad2385b535b08b535a4fca06a5f3eec
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 11 10:24:54 2008 -0800
+
+ Added CI exclude for failing MD5#== spec.
+
+commit 3c238cc9f4b32f63bc681bd64a507fc2ff49b017
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 11 10:00:50 2008 -0800
+
+ Converted Socket specs to use subdirs for subclasses.
+
+commit 2b98950eaa33b532fcef079b0997f9793228c608
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Fri Jan 11 09:23:23 2008 -0500
+
+ Specs and implementation for IO#print. Closes #222.
+
+ * IO#print without arguments spec excluded due to a lack of a lower
+ level output matcher. To be added shortly.
+
+commit d65c8c6899cf8e4a1fa56486cf417451e0c7fce6
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Fri Jan 11 19:19:56 2008 +0100
+
+ Fix String#* spec.
+
+commit 481e075bfeb9f8fb3bd4db645129a463307de09e
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Jan 11 17:41:07 2008 +0100
+
+ Improved digest/md5 specs a bit, some new test cases.
+
+commit 67f48236da3d114638310ab37bcc706719bf7fcd
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Fri Jan 11 11:59:06 2008 +0800
+
+ Updated Method's specs as Method#inspect and #to_s deviates on Rubinius
+
+commit 76846154773a87bc8d99c97e91250abda22f6378
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Jan 10 19:24:57 2008 -0800
+
+ A bunch of fixes found while working on Socket
+
+commit f69613740662d3ba4f85573c6c860a5987b29765
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Fri Jan 11 00:17:14 2008 +0100
+
+ Fixed Time object for throwing errors where appropriate
+
+commit 9396386f700646d0c55b9a7a75bc399dfe055d2c
+Merge: baae72c... 4d2e53e...
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Thu Jan 10 22:39:24 2008 +0100
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit baae72cc47c9c1f41c3478732b7bbfdfe514024a
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Thu Jan 10 22:39:11 2008 +0100
+
+ Fixed Time#xmlschema conversion
+
+commit 4d2e53e7376080e42b84dca486debcf4f153f32f
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Thu Jan 10 15:17:00 2008 -0600
+
+ implement more of Marshal.dump
+
+ negative Fixnum, Bignum, Regexp, Struct
+
+commit abdbcd70bba99149b7391effa48452971407b4d2
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 10 13:18:52 2008 -0800
+
+ Annotate Rubinius spec as non compliant.
+
+commit 2a2b3a016bfd70eb8cd14b6a043d59f119e0ad7c
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Jan 10 21:52:18 2008 +0100
+
+ Re-added divmod specs for Ruby/JRuby, with comments.
+
+commit 2f079e416e4389b091c8c9b5522d49c6f356c6c9
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 10 12:29:39 2008 -0800
+
+ Updated Bignum#divmod specs.
+
+commit 7a5c79415f2e6555bf2c69e416f6d3189f2e0c3e
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 10 10:46:05 2008 -0800
+
+ Simplify wording of VM.coerce_to_array specs.
+
+commit abc1237a0c96ecd77baee6ecbcf71a7bba338139
+Author: Ryan T Mulligan <ryan@ryantm.com>
+Date: Thu Jan 10 12:35:23 2008 -0600
+
+ md5 is now fully 1.8.6 MRI compatible
+
+ * MD5 digest specs
+ * Specs pass on MRI and RBX
+ * Platform::POSIX.memcpy hooked
+
+commit 9f991bd850c51cd624169b51768c2215d4b56edb
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Fri Jan 11 01:35:15 2008 +0800
+
+ Method#bind raises TypeError when binding a method from a non-descendant
+
+commit fc029ab13ded7eeb1ba838b99f00e2f14e232d65
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Fri Jan 11 00:12:44 2008 +0800
+
+ Implement that Precision.induced_from raises TypeError in certain cases
+
+ * For case when mixer class doesn't define it's own induced_from method
+ * Update CI exclude for precision
+
+commit 35d1a7bc3694bdcc327dd5ac89ca0f261e0bd705
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Jan 10 16:57:38 2008 +0100
+
+ Added one more Bignum#divmod testcase, known to fail on some implementations.
+
+ In fact, this test case fails on Rubinius and JRuby.
+
+commit 955676613f5e38cf029998e2712013e4575dd03e
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Thu Jan 10 16:12:37 2008 +0100
+
+ Changed spec failing on MRI
+
+commit f7b4f3fe02833081cc7f40c0feebbef0e5012f10
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Thu Jan 10 15:58:23 2008 +0100
+
+ Fixed Float#divmod
+
+commit aff6e1fc1a16eb9e7b7e207ebc2234154d891a92
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Jan 10 22:53:25 2008 +0800
+
+ Converted VM specs
+
+commit 969c0d8e0dbf43caa3999976cf259c623ff05ff1
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Jan 10 22:50:17 2008 +0800
+
+ Convert Options specs
+
+commit cc7c9dcb6697dea991342328a9b00fa01740e809
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Jan 10 22:28:42 2008 +0800
+
+ Replace example blocks using 'specify' with 'it'
+
+commit 7a5fa30a71072346abda17cdb79c2aa3b3922239
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Thu Jan 10 15:27:49 2008 +0100
+
+ Fixed Bignum#quo
+
+commit f2aafe4a352fd884d217b0361d2e7e617f58ebd5
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Jan 10 20:38:45 2008 +0800
+
+ Converted Generator specs and generated new CI exclude files for it
+
+commit edb7e341d9b3ab1c3bdc08bc57ec55d6bf8ace8b
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Thu Jan 10 19:33:53 2008 +0800
+
+ Remove generator_spec.rb because of spec conversion
+
+commit bb4de530c5980f0205875bdb5548e40a22ef6a62
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 10 01:45:47 2008 -0800
+
+ Updated CI excludes for library because of spec conversions.
+
+commit 61a66f69fe3a94d9ad5568ee2dd846cfc0b5211a
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 10 01:45:17 2008 -0800
+
+ Converted Socket specs.
+
+commit 167e05039eeeeb959f7aab1f3611268170037296
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 10 00:59:20 2008 -0800
+
+ Converted YAML specs and added template files for other methods.
+
+commit 027f568f79222cdee492f088edf8a2f14250635a
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 10 00:47:35 2008 -0800
+
+ Converted ostruct specs and added template files for other methods.
+
+commit e964c9342ade9341518bc46cf998703a2c16aa2b
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 10 00:39:49 2008 -0800
+
+ Converted ftools specs.
+
+commit 6263280187c81b0ee27893eae90f9d6a8a511b65
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 10 00:26:40 2008 -0800
+
+ Converted Etc specs.
+
+commit 7b94284063222eef42b9b7ad0d1c820adabe210d
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Jan 10 00:39:01 2008 -0800
+
+ Fix a few more Kernel bugs
+
+commit 1c58ee51f388da0490a7815c9a1787d21e151aab
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 10 00:08:15 2008 -0800
+
+ Fixed path for mock dirs in Dir specs.
+
+commit 320f7e7d3503d53216733f9b6eb75c387155ae5f
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 9 22:44:37 2008 -0800
+
+ Converted StringIO and Singleton specs.
+
+commit 5a94a7c3b73103c99a337a089f9cf2c7e601d2bc
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 9 21:50:14 2008 -0800
+
+ Converted stdlib Singleton specs.
+
+commit 12864a2057d1b6f5fa392f34d1fa3e8873a8c566
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Mon Dec 24 23:06:12 2007 +1100
+
+ Initial commit of Rubinius Debugger
+
+ * Created Debugger class for debugging Ruby code in Rubinius
+ * Added Kernel#debugger convenience method to set a breakpoint
+ and activate the debugger at the point at which the
+ debugger statement is encountered.
+ * Implemented the following debug commands:
+ - h: get a listing of commands
+ - b: list breakpoints
+ - b <Method>: set a breakpoint at the start of the method
+ - n: Step to the next line
+ - ni: step to the next VM instruction
+ - c: continue execution until the next breakpoint
+ - l: list source code around the current breakpoint
+ - d: decode VM bytecode around the current breakpoint
+ - v: display local variables and their values
+ - vs: display the contents of the VM stack
+ - Anything else is evaluated as a Ruby expression in the
+ context of the current breakpoint (so you can, e.g. change
+ the value of locals before resuming, etc)
+
+commit 01a189cc3e52e8bcc6f22bcc5713e765bba84160
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Jan 9 10:29:27 2008 -0500
+
+ Object#is_a?, #kind_of? and #instance_of? specs.
+
+ * Removed obsolete kernel specs for same.
+
+commit ab9645614bbbd0bca63c215819c12cc85a1507b1
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 9 21:35:39 2008 -0800
+
+ Converted specs for stdlib Time.
+
+commit 733b069f11c7136175036154a45b924cf89cc8ff
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Wed Jan 9 20:01:09 2008 -0600
+
+ update CI excludes
+
+commit 01e98dee4c24838ca518610443e43473ffdcf43c
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Wed Jan 9 16:42:07 2008 -0800
+
+ Fixed block args for |*a|
+
+commit dc9c1d05dd5e0e828a77acc09220f5894a9aa453
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 9 16:04:11 2008 -0800
+
+ Fixed Enumerator spec style. Updated YAML excludes.
+
+commit 0363685a97df83feb0d07f40a7a5c4d7a78e2a27
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Wed Jan 9 14:55:53 2008 -0800
+
+ Spec for String#sub bug.
+
+commit 23052eb5f993c959fdb2b327895df08e0a344edb
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Tue Jan 8 23:47:27 2008 -0800
+
+ Implement { } matching for Dir.glob
+
+commit c90b2531d183e4534268d4699634828f29e803cb
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Tue Jan 8 23:44:35 2008 -0800
+
+ Spec File::Stat#blksize.
+
+commit 62d2a1809936a304c0cf0b94fd28f5b83932f58f
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Tue Jan 8 22:47:37 2008 -0800
+
+ Implement Dir.glob '{a,b}'
+
+commit d9430ad1a3e582e830a994a83d6f99e017bfbe4d
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Tue Jan 8 20:56:37 2008 -0800
+
+ Fix module X::Y; end; X::Y.name
+
+commit 1baa9468e0d89777fdb6f23e78e8ab510a19d534
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Wed Jan 9 16:16:35 2008 -0600
+
+ incomplete Marshal.dump
+
+ an evil ivar_as_index is added to Object to hold
+ the names of modules that extend the object
+
+commit 85e98490fe45446e03801840d4628149f8977098
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Jan 9 12:26:19 2008 -0800
+
+ Move compiler2 => compiler, and Compiler2 => Compiler
+
+commit 5aa5cc66e2b0196728c80eb394ec3b2dfccd77ae
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Jan 9 10:30:42 2008 -0500
+
+ Centralised specs for Object#=~.
+
+commit e1fe9f57c942460338a18e38f66fbf6feb69b4bc
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Jan 9 13:23:59 2008 +0100
+
+ Added few edge cases to Numeric#eql? tests.
+
+commit b8dfd675fad5e82ebfd50c737beb9a9b919a9c8b
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Wed Jan 9 15:52:18 2008 +0800
+
+ Remove excludes for String#slice with the fixed send in place
+
+commit d7f69f17ac30f6b3161851e8df6a1e0a7694219d
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Tue Jan 8 22:42:43 2008 -0800
+
+ A couple more fixups
+
+commit 055d7545c7046102cd92b7054992b1b47f711c4a
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Tue Jan 8 22:31:50 2008 -0800
+
+ A boatload of fixes done while getting flexmock and rake running
+
+commit fd7c266e52c25d151214512cc801901813630d7a
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Wed Jan 9 11:49:02 2008 +0800
+
+ Removed last array exclude due to fix in 02e6e28
+
+commit 690626f43f7b4ce888de081033eaadfba543acff
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Jan 8 18:58:50 2008 -0800
+
+ Removed subtend specs from CI run. Increase File#mtime tolerance.
+
+commit dec4f25a47a9a962b77a97dea47985fe17421e5f
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Wed Jan 9 13:39:51 2008 +1100
+
+ Specs for stack usage
+
+commit d699f6605db86e6f6bc61d0f3a79fc1535816c70
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Wed Jan 9 10:38:06 2008 +0800
+
+ Add spec to illustrate Numeric#divmod bug in MRI and rubinius (excluded)
+
+commit 8a55f3047dc0fd502bb632dc9f5bdb9668b180fe
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Jan 8 18:32:18 2008 -0800
+
+ Subtend CI exclude to (hopefully) fix the build server runs.
+
+commit 33bde75b57a88baa850edccea382e1130ed586da
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Jan 8 17:19:30 2008 -0800
+
+ Added spec/compiler2 to CI. Added CI excludes for compiler2.
+
+commit 6964fc5644fddeef2238591674786f035d9db842
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Tue Jan 8 17:19:20 2008 -0800
+
+ Fixed up against evan's changes. ping
+
+commit 9423d1e8e9ed91fb9f0934b939899c753972cee1
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue Jan 8 20:11:54 2008 -0500
+
+ Fix warnings encountered when running compiler2 specs under MRI
+
+commit d71ad87c14a4378ad2f01c49d90304c29be548f3
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Tue Jan 8 17:02:02 2008 -0800
+
+ Fix a block_arg bytecode generation case
+
+commit 57199b5b468c0009512a479e13bbcf086d0d9526
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Tue Jan 8 16:16:07 2008 -0800
+
+ Added new combo bytecode/runtime tests for block args
+
+commit 8a88699af73d272a61332e11d022bd629aa0460d
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Tue Jan 8 16:15:24 2008 -0800
+
+ Improved inspect output for compiler spec objects. Added convencience methods for testing iter bytecode generation.
+
+commit 0dd9cd298cf735dc13cc2a2410ad6b5195790c11
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Jan 8 13:19:21 2008 -0800
+
+ Added subtend specs to CI. Updated subtend excludes.
+
+commit c07a5273844b32fe39090bb16d0e4ad59ecb0564
+Author: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Tue Jan 8 21:28:48 2008 +0100
+
+ Fixed given_spec? because of changed block_given? behavior
+
+commit 0f9a8dfee9dd1c7af1f8ba69497c8dd85539760a
+Author: Nitay <nitay@powerset.com>
+Date: Tue Jan 8 11:49:41 2008 -0800
+
+ Fix setpgid spec using pipes to avoid race condition
+
+commit 09feb8677c529d04969e63d1ff4e3746037611cf
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Jan 8 10:00:06 2008 -0800
+
+ CI excludes for ruby/1.8/library.
+
+commit cda3d86fa44f1d62fe503e54f42c5c5df361b8f9
+Author: Benjamin Andresen <bandresen@gmail.com>
+Date: Tue Jan 8 08:22:49 2008 +0100
+
+ Added explicit umask to File permission spec so it won't fail on
+ non-standard umasks.
+
+commit 6df303e29d7fd04f4a1a0af379f4947854dd4635
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Mon Jan 7 23:20:22 2008 -0500
+
+ Method#== and specs from Scott Taylor, slightly tweaked. Closes #137.
+
+commit 9b86b12be687bb29e25d0292786351d89a698adc
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 7 19:45:24 2008 -0800
+
+ Added CI exclude for Array#pack.
+
+commit 17a746b0aa2c89aa9e61b8965d125e962748c20d
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Mon Jan 7 21:07:25 2008 -0600
+
+ adds Marshal.dump and Float#to_s specs
+
+commit d5c19db2778e0cc3cbee5bf994b511448cb6bd78
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Mon Jan 7 15:31:11 2008 -0800
+
+ Fix IO#pos=
+
+commit 21f44f03f0aa44b2f172f89ad27797c943dc618b
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Jan 7 22:03:28 2008 +0100
+
+ Remove non-needed std output from Array#pack test.
+
+commit 9ec20509ad6533876bbbc984052e6b7e05d2ea55
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Jan 7 21:50:46 2008 +0100
+
+ Added Array#pack tests with empty array.
+
+commit 35170103bdba14d824780a41112f12034cb5c79e
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Jan 7 21:13:47 2008 +0100
+
+ Added Array#pack tests with 'w' pattern.
+
+commit 71b00e03ce2c6424fd262d737feb991835605da2
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Jan 7 20:46:58 2008 +0100
+
+ Added Array#pack('U') test with negative values.
+
+commit 7be0813127635ea54909179c9553c5052c4a3d90
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Mon Jan 7 18:00:03 2008 +0800
+
+ Add specs for Array.[]
+
+commit 0b762336e8c6040cbbe794cece64c56bfa46c296
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Sun Jan 6 23:35:35 2008 -0800
+
+ Fix breakages, comment out at_exit spec (need a better way to test)
+
+commit 8896e459f1bffb7ae2da2f2aa708419e6316cb4b
+Author: Matijs van Zuijlen <Matijs.van.Zuijlen@xs4all.nl>
+Date: Mon Jan 7 14:03:03 2008 +0900
+
+ Spec to demo failure of cases like "yield 1, *[1, 2]"
+
+commit 79da85bb1b1d63e617251b3a3ea6b0657c1e8ddb
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Mon Jan 7 13:04:22 2008 +1100
+
+ Modified CompiledMethod#decode to use #local_names
+
+ * CompiledMethod#decode now leverages new #local_names
+ method to return the names of locals accessed via
+ push_local/set_local etc.
+ * Removed excludes, as all decode specs now pass
+
+commit 89c1026cecbb9fcd09a62139e2d28b24b5658c25
+Author: David Whittington <djwhitt@gmail.com>
+Date: Mon Jan 7 01:37:10 2008 +0000
+
+ Added args to NoMethodError raised by Object.method_missing
+
+commit 0e4a02f0e2fede5d785b15a6b34c582c6ba586f1
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Sun Jan 6 12:43:14 2008 -0800
+
+ Removed redundant Bignum#to_s. Moved private radix_to_s to bottom. Cleaned up to_s spec a bit
+
+commit 48446c40a759d60b7465d82b40f2911d0f7e444b
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Sun Jan 6 13:45:22 2008 -0600
+
+ Add some additional Math.asinh specs from JRuby.
+
+commit 7c81ca307cd01d3752a08487bc3742c8452d61c4
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Mon Jan 7 00:41:10 2008 +0800
+
+ Add failing specs for Time.{local|mktime|utc|gm}
+
+commit 35816e118b327a150a2d26638f289633f5e51f16
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Sun Jan 6 17:56:23 2008 +0800
+
+ Add spec for Time.local to handle string arguments (excluded for now)
+
+commit f9f36f5bb99ddb62e15cb9a9ddd98414e3df93e2
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Sat Jan 5 23:44:33 2008 -0800
+
+ Allow Regexp to match nil.
+
+commit e650c39627b81498fc97c51725f2ac1277870e15
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Sat Jan 5 23:38:52 2008 -0800
+
+ Add some IO#read specs
+
+commit 20257ecce0d3161fae7ac78454f2b8672f2c1de3
+Merge: bc576b8... e549cc5...
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Sun Jan 6 14:45:13 2008 +0800
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit bc576b8e26fdb43d050df4fe3ad5ed974ec85057
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Sun Jan 6 14:44:44 2008 +0800
+
+ Fix handling of string-like second parameter to Time.local
+
+commit e549cc53a4905f21082a97cd6bcb279ace6d9eae
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Sat Jan 5 22:31:42 2008 -0800
+
+ Don't shift more bytes than available in the Buffer.
+
+commit 71285a2a9a8d0d3e71c678872ff2a146d5b2dc16
+Author: Chris Shoemaker <chris.shoemaker@cox.net>
+Date: Sat Jan 5 22:53:51 2008 -0500
+
+ Fixup the Process specs for setpgrp, getpgrp, setpgid, and getpgid.
+
+ They no longer may unwarranted assumptions about the relationship
+ between a progress group ids and process ids.
+
+commit 7b57b3ac6df612f81d60d3a31b030ba054b357a6
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 5 13:22:51 2008 -0800
+
+ Patch from Brandon Mitchell for #195, Float#to_s bug.
+
+commit 70ddfd43fd727122f56e8bdfcf3febd1ac1b5479
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Fri Jan 4 01:01:11 2008 +0800
+
+ Fix for Time#yday spec when Time.at might return yday+1 depending on tz
+
+ * Wrapped Time.at in a with_timezone("UTC") for consistent results
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit 7d4396e4e69fb7b046efdaaf87d1090a02c883a0
+Author: Kamal Fariz Mahyuddin <kamal.fariz@gmail.com>
+Date: Fri Jan 4 02:26:38 2008 +0800
+
+ Fixed Array#fill behavior when passed index and negative count
+
+ * Added additional spec when negative count is acceptable
+ * raise ArgumentError when negative count absolute value exceeds index
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit 8af2b55313eb55082df6a71cd3e6bd865f2901fc
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Sat Jan 5 13:33:26 2008 -0600
+
+ Save mtime during file creation to make mtime spec more reliable.
+
+commit 7043933af0475370462984c8d2df2b9301e58cfa
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 5 00:45:01 2008 -0800
+
+ Updated CI excludes after spec description changes.
+
+commit 3d7650100ba1756a4d67be8044e31498ea96d88e
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 4 21:13:28 2008 -0800
+
+ Multitudinous style cleanups in spec description strings.
+
+commit d54ed8791a74661adb87c938e92e037ece924c90
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Sat Jan 5 00:29:22 2008 -0800
+
+ A real, working eval and friends.
+
+ * Implements binding, eval, etc.
+ * Passes all eval and instance_eval specs currently
+
+commit 02ad19ab4132bf5d3ae35c2e11fa1a963d1f1805
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Fri Jan 4 00:25:39 2008 -0800
+
+ Fix a few more String specs, fix Integer()
+
+commit d67cfbcf4e7d35641de555ac1edd61b51780def8
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Fri Jan 4 19:16:43 2008 -0800
+
+ Make class variables work with inheritance.
+
+ Move class_variable* to Module.
+
+commit d79836e04d72796b723cdaab228871c87abe064a
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Fri Jan 4 16:26:21 2008 -0800
+
+ Replace Struct with a Struct that can be subclassed
+
+commit 8efb042a9c160af9e9c177ca14aed220dedcc26f
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Fri Jan 4 15:41:43 2008 -0800
+
+ Finished |*args| spec. Fixed MethodDescription and TestGenerator inspect methods
+
+commit 76cc487434f6cd9d60356560f1bbc3fba000397c
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Thu Jan 3 16:54:56 2008 -0800
+
+ Fuck you git. Adds a broken spec to compiler2/masgn for splatted goalpost arg
+
+commit dd2697b602a732e3e00c131f54f9cc557ae0cbe3
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Fri Jan 4 15:40:50 2008 -0800
+
+ Failing spec for case when with an empty body
+
+commit 22dcedebd484f655bba51399e38e83c5a14d4053
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Jan 4 18:31:48 2008 +0100
+
+ Added Time#local specs with string-like second arg.
+
+commit 43ff733a3097fff44ba8a12334f20a1bf77a965f
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Thu Jan 3 23:54:34 2008 +1100
+
+ CompiledMethod#decode now uses local variable names (if avail)
+
+ * CompiledMethod#decode now looks in the bonus tuple for the names
+ of stack and slot local variables
+ * Moved compiledmethod specs to spec/core/compiledmethod
+
+ Note: Compiler2 appears not to be setting the bonus tuple, so code
+ compiled under it cannot decode local names at present.
+
+commit 06006ec2a053ae49b243fa0aa98fc71c2ea7a524
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 4 01:24:49 2008 -0800
+
+ Updated CI excludes.
+
+commit dff2e75df3c371522b6a3ba4495d269bf793fe97
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 4 01:01:24 2008 -0800
+
+ Updated CI excludes for Bignum.
+
+commit 569fa3b9fc81410ce9fe6568427f0a0bc65b7036
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 3 19:26:22 2008 -0800
+
+ Updated CI excludes for String, Regexp.
+
+commit 7aedec383850eacad5db8248bfcea7615a3d1793
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Jan 3 17:48:32 2008 -0800
+
+ Fix up setrlimit/getrlimit on darwin
+
+commit d9aea8bba7276b53ca7c18b8625531be389d2cdc
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Jan 3 16:40:09 2008 -0800
+
+ Refactor $~ out as a global, into Regexp.last_match directly
+
+ * Uses MethodContext to store $~ now, so it's method local.
+
+commit c19dde305fd751c14a1b4dc798557e0b63c08c8d
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Jan 3 16:25:54 2008 -0800
+
+ Clean up compiler2 specs
+
+ * For is still broken, needs more love
+
+commit d02603a7e225d3b48ecf7899ea74768880aba7ec
+Author: Gregor Schmidt <ruby@schmidtwisser.de>
+Date: Wed Dec 12 16:56:11 2007 +0100
+
+ Add default implementation of Module#method_added
+
+commit 7ba5d1478106e4e0f5fcf21c66029df2f38d7e2f
+Author: Chris Shoemaker <chris.shoemaker@cox.net>
+Date: Sun Dec 30 17:55:21 2007 -0500
+
+ Unquarantine Process.kill specs.
+
+commit d68b380bdd2e0a0ec3bd968ffabd02f6e30a3aa1
+Author: Chris Shoemaker <chris.shoemaker@cox.net>
+Date: Tue Jan 1 22:14:59 2008 -0500
+
+ Improve kill, wait, detach, and setpriority specs for Process.
+
+ Restore any previously installed signal handler after the spec has run.
+
+ User IO.read(1) instead of IO.getc since rubinius has it implemented.
+
+ Fix a failing Process.detach spec uncovered by the raise_error fix.
+
+commit da7329d094b6ff437d37e6a1fcaf93883ac9172f
+Author: Chris Shoemaker <chris.shoemaker@cox.net>
+Date: Mon Dec 31 16:23:24 2007 -0500
+
+ Add specs for Process.setrlimit, Process.getrlimit, and Process.setsid.
+
+commit 42bef2feb46434b0ea67bc3f93d941d587c2d9c9
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 3 12:46:57 2008 -0800
+
+ Updated Process spec excludes.
+
+commit ca98172b8a923cce1691b0fcc5d2418417d82662
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 3 10:07:55 2008 -0800
+
+ Update CI excludes for IO from Evan's fixes.
+
+commit cde20d6c32156e4fc06859f1e84414f81f5af69e
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Jan 2 23:40:59 2008 -0500
+
+ Fixed #require specs.
+
+commit 06d99a2ac4be06b50848056b381c91531293a49e
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Jan 3 01:12:29 2008 -0800
+
+ Add read buffering to IO, passes 100% of IO specs
+
+commit 86170283715371b5a87c0518f89c2b882a49bc93
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Thu Jan 3 01:21:52 2008 -0600
+
+ Fix off-by-one on a few signal values. Doh!
+
+commit c7a64b10410308cec83077a66cda5859b326f296
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Thu Jan 3 01:13:27 2008 -0600
+
+ Modify Signal.list spec to not depend on hash ordering.
+
+commit f2980d9584c08d873cf1646c281d083946bcbc6c
+Author: Nitay <nitay@powerset.com>
+Date: Wed Jan 2 14:36:56 2008 -0800
+
+ Module#autoload:
+ * raises a NameError when an invalid constant name is given
+ * raises an ArgumentError when an empty filename is given
+ * does not autoload when the specified constant was already set
+ * registers the given filename to be loaded the first time that the Module
+ with the given name is accessed
+
+commit e68bd05defe5ab749110af507c86769c9a036b25
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Wed Jan 2 19:04:48 2008 -0600
+
+ Removing 'Range#initialize can't be called twice' spec.
+
+ Evan agreed that these specs aren't useful, and I don't believe they're
+ within the bounds of reasonable language specification since they're
+ going around visibility and testing behaviors no sane programmer would
+ ever be able to see.
+
+commit 1870720bac174feb627654f08c1749e1666c2acc
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Wed Jan 2 18:54:33 2008 -0600
+
+ Fix inspect spec to guarantee the target thread is actually sleeping.
+
+ A reminder for folks adding Thread specs: You *CAN NOT* know that a target
+ thread is sleeping unless you are polling for status == 'sleep'. No amount
+ of channel, lock, or state variable tricks will get around that. Please
+ use polling if you want to guarantee a target thread is asleep.
+
+commit df3057a541862bbd1c5c72b8626bb591bb5ae6fd
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Wed Jan 2 16:39:37 2008 -0800
+
+ Refactor Module#undef_method spec to #respond_to? and #instance_methods.
+
+ Now only method dispatch is tested for #undef_method. #respond_to? and
+
+commit d2ecd4119a152370210ccb6c2a816c9dccb9fe90
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Wed Jan 2 16:00:09 2008 -0800
+
+ Fix Rails indenting and whitespace
+
+commit f5b8afee4931bd09b0ce9fb88fc959c2ea0a1743
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Wed Jan 2 15:30:41 2008 -0800
+
+ Fix Module#undef_method and Module#instance_methods
+
+commit 82bf31562361a21f85a90d5628a40ff50280c555
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Wed Jan 2 14:10:35 2008 -0800
+
+ Rebuild excludes for #eof?
+
+commit b2aa0d56b04d7da5d333ba1449acda7c0b64c0c4
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Wed Jan 2 14:09:21 2008 -0800
+
+ Add IO#eof? spec.
+
+commit addeb47d834d1ce60f8146f747defacf1682e6c4
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Jan 2 22:31:30 2008 +0100
+
+ Removed JRuby spec excludes.
+
+ The JRuby excludes will reside in JRuby repository.
+
+commit 3239661ed5c38b37c966588341a043d6cdd9445b
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Jan 2 21:28:11 2008 +0100
+
+ Corrected String#modulo tests after clarifications from ruby-core.
+
+commit a0f3ba6632f8486e8f07a21a8e4720d8727ba4d2
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 2 11:28:42 2008 -0800
+
+ Fix require_spec_recursive on Ubuntu.
+
+commit 857c39564df2d8da480f549fff46ec3ab880066e
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 2 00:48:53 2008 -0800
+
+ A couple fixes. Updated CI excludes for last couple failures.
+
+commit 58c48ed05b493c71ee445062f27d47909e18b395
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Jan 1 23:49:14 2008 -0800
+
+ Updated CI excludes.
+
+commit 426f5a15eaac05ed1e900433837de0b9d0246c8d
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Jan 1 22:43:47 2008 -0800
+
+ Moved CI excludes files from .spec dirs to spec/data/*.
+
+commit a1d6211f3185f23cbc2c929f0352feca05fd079c
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Jan 1 22:03:43 2008 -0800
+
+ Moved ruby specs to spec/ruby/1.8/...
+
+commit af55eefd29c8acaf462efe03d2e0b3d95195cb21
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Jan 1 21:44:35 2008 -0800
+
+ Updates for bin/ci and bin/mspec.
+
+ * Removed -2 switch from both because compiler 2 is default.
+ * Added CI_EXCLUDES_DIR and -E switch to bin/ci to allow for
+ specifying the exclude directories. The default is '.spec'
+ in each directory containing spec files. Use a path starting
+ with a '/' to create the exclude directories relative to
+ that path, otherwise the exclude directories are created
+ relative to the directories containing the spec files.
+ * Moved spec/excludes.txt to spec/data/critical.txt
+
+commit 0e6645eb74f1f63b84f674dbcdfa991153a3ccd0
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Wed Jan 2 12:10:39 2008 +1100
+
+ Couple of Breakpoint changes
+
+ * Raise ArgumentError if no block given (spec for this
+ existed, but was masked by RaiseErrorMatcher bug)
+ * Added line property to Breakpoint
+
+commit 36a7acddfe74ab25895d13dd775741b042ba3b0c
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Tue Jan 1 23:10:13 2008 +1100
+
+ Reorganise breakpoint specs to new dir layout
+
+commit 8aa6712dd9e5e870194f77ff74dc8cf11c273805
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Fri Dec 21 16:15:19 2007 +1100
+
+ Refactored BreakpointTracker in preparation for debugger
+
+ * Moved code from debugger.rb to breakpoint.rb
+ * Refactored code extensively to support debugger
+ * Added breakpoint specs
+
+commit d16e905a67d64f67d7a24ce113f39b4b059c4139
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Dec 31 17:51:59 2007 -0800
+
+ Removed the rubinius dir from specs. It was redundant.
+
+commit 85ed07b6d739f013892a6cbcae5d0bb2c19f6e80
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Dec 29 15:45:31 2007 -0800
+
+ Split Ruby specs proper from Rubinius specs.
+
+commit b8e1466dc1b814bfb2022c1e4319d5ba63f5d762
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Dec 29 15:02:55 2007 -0800
+
+ Updated guards in specs.
+
+ * Changed guard names to new, more descriptive names.
+ * Removed all #extended_on guards for Rubinius-only specs.
+
+commit 5773ebe9e6f78abec9bfb03f144b5c7a86a27c7e
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Dec 29 00:50:56 2007 -0800
+
+ Changed Float constants specs to compare against precise values.
+
+commit 71874fcdc9eaf45a5adecf57d7609831a2a8e6c2
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Sat Dec 29 17:26:06 2007 -0600
+
+ Fix dump_spec to expect 1 or more write calls, rather than exactly 1.
+
+commit d4bfb39910aa4adf2c0c4e2dee214487bac34093
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Sat Dec 29 14:51:38 2007 -0600
+
+ Add a spec for procs being block-passed and some peculiarities therein.
+
+commit 1b0333479bf6da2c76c8d3c1e1640dc156086d9f
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Dec 28 08:24:30 2007 +0100
+
+ Improved ObjectSpace#each_object spec test.
+
+ Previously, the test was failing from time to time,
+ depending on Garbage Collector behavior.
+
+commit 3d7e628acc6699f9652383317bd416d8c75329d5
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Fri Dec 28 05:27:24 2007 +0100
+
+ Updated Time specs to use new :os guard.
+
+ The Time specs use this :os guard to properly detect
+ which external program with proper parameters to invoke.
+
+commit 7662638e9afa631f0581fc1c2b2b422b1b926f98
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Sat Dec 29 03:06:51 2007 -0600
+
+ adds Marshal.dump specs
+
+ for nil, true, false, String, Symbol, Fixnum, Bignum
+
+commit 93431a28d687372b95f1a1420a3bd1f24e660117
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Sat Dec 29 00:15:44 2007 -0600
+
+ Guard Continuation specs to not run on JRuby (JRuby does not, will not support continuations)
+
+commit ccf745b9eafe068de6f888de24387bc0a0e68859
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Sat Dec 29 00:08:04 2007 -0600
+
+ Fix for Fixnum size spec to guard "java" platform with 8 byte size
+
+commit 6f448f0dd72b5df2cc69e28db3d5593f897a9dbd
+Author: MenTaLguY <mental@rydia.net>
+Date: Sat Dec 29 00:58:58 2007 -0500
+
+ a more modest spec for Thread.pass
+
+commit b32c2d95d044a4979ab92b5881e32fc8b169d931
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 28 23:51:10 2007 -0600
+
+ Adjust Float MAX spec tolerance to work on both JRuby and MRI, since there's a few powers of precision difference.
+
+commit af7bb00beeb359fd6183def039b9a1fcd0ce7c48
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 28 23:39:15 2007 -0600
+
+ Expand Float divmod array equality comparisons to use be_close with a default tolerance.
+
+commit dbdf373751bce2c8a334315c8c5ed21458614c70
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 28 23:34:09 2007 -0600
+
+ Add a tolerance to the Float induced spec around the same scale as the value under test.
+
+commit a713d277e6a8148d4c53b66a3a8fa3aedbd6a108
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 28 23:31:25 2007 -0600
+
+ Modify Float multiply spec to be_close with a TOLERANCE multiplied by a similar scale as the value under test.
+
+commit b82d8af43356de31d16b1c36296d9e819ce70d46
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 28 23:13:38 2007 -0600
+
+ Fix Module class_variables spec to sort the variables before checking if they are all there.
+
+commit 1e60a25b57273dd6fd7e21b0a443da1f5c0be9e5
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 28 19:44:51 2007 -0600
+
+ Mark Process#fork specs as not_compliant_on jruby.
+
+commit 021a6ff317ed826a46ca2168f4ee9c7540a27214
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 28 19:19:14 2007 -0600
+
+ Remove fail and "failure" guard around the require/extension spec, since an unimplemented spec isn't necessarily a failure of any kind.
+
+commit 520c423860ef6553dae34eefd85188ab9b4773f6
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 28 19:12:52 2007 -0600
+
+ Modify previous compliance change to callcc spec to use not_compliant_on instead.
+
+commit ab85bfff2f9fea8e28f9518311aacccd30f380dd
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 28 17:56:42 2007 -0600
+
+ Remove compliance guards on identical spec's link/unlink, since they don't blow up now and JRuby supports them.
+
+commit e79c8af0ad6fb7ddf094b6ba4747932145f9b89b
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 28 15:19:24 2007 -0600
+
+ Removing "fail" and "failure" wrapper from unimplemented "loads extension files" spec; an empty or unimplemented spec is not a failing spec.
+
+commit 0f6b7387bcc8df946ec8d7504cc3935b6d0f9c58
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 28 15:06:07 2007 -0600
+
+ Add compliance to callcc specs, so JRuby doesn't run them (since it never will)
+
+commit ed43292ce58468e31b771eb4926a39dff8d70793
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 28 14:48:45 2007 -0600
+
+ Make umask spec work with different host process starting umasks, clean up literals to be easier to read through.
+
+commit 7e9f96741739e544c547f2898e8b5183dec87323
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 28 14:43:02 2007 -0600
+
+ Fix goofed-up paths in requires for rubinius-specific Integer spec
+
+commit d54fb1e7c3f586a6d8ac200d6de839ebe6cb4c46
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 28 14:40:50 2007 -0600
+
+ Move rubinius-specific spec from core/kernel/Integer_spec to rubinius/core/kernel/Integer_spec.
+
+commit eb561025707736ebe196eab3b4ff2bd1c98f45a4
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Dec 27 23:51:42 2007 -0800
+
+ Fixed language/block specs to guard ruby18 feature.
+
+commit 5659d057d756effe3acba1037d0ad6d638d930dd
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Dec 27 22:58:17 2007 -0800
+
+ Changed Bignum specs to use value suitable for all implementations.
+
+commit 2646b1a17f898f05233622c9251c3c36632e82a7
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Thu Dec 27 23:43:42 2007 -0600
+
+ implements m directive for String#unpack
+
+ moves a couple misplaced methods from Numeric to Integer
+
+commit 496d6761d7377081ff76b263a51bb39d0e30d80a
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Dec 26 18:07:45 2007 +0100
+
+ Marked one Rubinius-specific Kernel spec test as such.
+
+ Kernel#compile is not official part of Ruby.
+
+commit 50e35293bd3a117874203a75d214c3435170e5d3
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Dec 26 17:43:03 2007 +0100
+
+ Corrected String#pack with 'DEFG' pattern test.
+
+ Now, numeric comparison of values is used, with precision,
+ not literal string comparison.
+
+commit 0ef00fe14a04ef240fcca17d15271f92f2a44525
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Dec 26 16:00:20 2007 +0100
+
+ Added String#inspect test case with malformed UTF-8 string.
+
+commit 5bbde0cda03ea782090586a9afdb620663633456
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Thu Dec 27 13:30:34 2007 -0600
+
+ switched ThreadGroup specs to use Channels
+
+commit e3abd8b834b9f923d94ae381e81977feb4a4f6f8
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Thu Dec 27 13:22:20 2007 -0600
+
+ added Thread#stop? and fixed Thread#status + specs for Thread#status
+
+commit f8835353bc8be47760f70811616991463e4e681e
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Dec 26 09:18:24 2007 +0100
+
+ "Unexcluded" one Struct spec test for JRuby.
+
+commit 8a1b127cb33e43b916b0ccd820c6e16680cd4030
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Dec 26 09:12:23 2007 +0100
+
+ "Unexcluded" 6 Hash tests for JRuby.
+
+commit ae4ce805fb7611ea6de12b01b2500f501b54bd6a
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Wed Dec 26 09:01:35 2007 +0100
+
+ "Unexcluded" 27 Array tests for JRuby.
+
+commit c84540f96d7e265732a204ed72b3873545624444
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Dec 27 00:08:06 2007 -0800
+
+ Fixed Set#delete? spec to actually call delete?.
+
+commit e137c3279f511b49442ce2cea1b1832c1a0c6ab0
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Dec 27 00:00:56 2007 -0800
+
+ Added some specs for Set.
+
+commit 8054ed86a93a72ad4629d6f52455892d620138b0
+Author: Nitay <nitay@powerset.com>
+Date: Tue Dec 25 17:30:56 2007 -0800
+
+ require should prevent recursive includes infinite loop
+
+commit 23fb497a7ba2a853cbdc5e8a38b091df284a377e
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Dec 26 16:50:22 2007 -0800
+
+ Updated status output options for bin/ci and bin/mspec.
+
+ * Made dotted the default output format for bin/ci.
+ * Added -m MARKER option to ci and mspec.
+ * Added "Started" output as requested by autotest folks.
+
+commit 036b073753763afe86330d3f7fa0f61d755ac991
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Dec 26 10:41:01 2007 -0500
+
+ Moved class fixture back to spec/fixtures/.
+
+commit 91d46b86a86270bb3174909a2d5cbc343ea138c7
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Dec 25 19:20:18 2007 -0500
+
+ Added specs for $~, $&, $`, $', $+ and $1..N.
+
+commit c434614505511b8816548efcf4a4cf56d77220f4
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Dec 25 19:19:11 2007 -0500
+
+ Improved language-level class specs, moved fixture.
+
+ * Class fixture copied to spec/language/fixtures/.
+ * Disabled unnecessary class instance variable check and added
+ new ones.
+
+commit 0a49f3485fe7e26cc7d7d5bc3cb800ddf9fd6231
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Dec 25 19:13:22 2007 -0500
+
+ Changed strange_block_args_subspec.rb to block_args_subspec_strange.rb
+
+ * Name change to improve alphabetical sorting.
+
+commit 2ac50215dd32fd7ad2f2c20c7ae06ed73dc9f856
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Dec 25 13:32:06 2007 +0100
+
+ Added tests for Array#pack with "U" pattern.
+
+ Note: The tests are exclided for Rubinius.
+
+commit 4c0993fa90010322bb823a9799a8b3ccdd585e2e
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Wed Dec 26 04:07:02 2007 -0600
+
+ return excluded spec
+
+commit 0a69d9cd5a7d3a0be9411fa00c4eeebe5d270a0c
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Wed Dec 26 03:55:43 2007 -0600
+
+ implements @AM directives for String#unpack
+
+ squashes bug in a regexp where an alternation of things
+ between begin and end assertions wasn't wrapped in group delimiters
+
+commit ddda4d49f5535577c147d2154ecdae7cb4e32e24
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Dec 26 01:16:13 2007 -0800
+
+ Moved Kernel#load/#require fixtures to spec/fixtures.
+
+commit 0438e9e61c5958c5daf691b025e34bc79e7b2573
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Dec 26 01:06:12 2007 -0800
+
+ Reorganized specs to group all Rubinius specs under spec/rubinius.
+
+commit a4c3e286e44ee3df88395b9b5f44d5804154ed2b
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Dec 25 19:32:55 2007 -0800
+
+ Updated CI excludes.
+
+commit 8535481571712cf8c35f437c42ec53dcbfd44bc0
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Dec 25 15:52:53 2007 -0800
+
+ Enhanced guard for detecting :ruby, :ruby18, :ruby19 engines.
+
+ Changed dir specs to create the fixture directories with every
+ run to prevent pollution of the directories from causing spurious
+ errors.
+
+ Added spec/core/dir/fixtures/mock to .gitignore.
+
+commit a6b07ec37da7a59f34f45dfc84a66729b12f63b7
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Dec 24 16:25:16 2007 -0800
+
+ Removed Dir specs mock directories from version control.
+
+commit b0e4addbf7c6505c760e143e5fac0dab0109d8ac
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Dec 25 13:17:28 2007 -0500
+
+ Updated CI excludes for Dir.
+
+commit 80a9c6c2e2e5cd2acdcb6492c4a06fef258bb49e
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Dec 25 13:17:09 2007 -0500
+
+ Moved Rubinius-specific parts of #load/#require specs to extensions.
+
+commit 85f6b6e24518868f39ff39a5014a41a233237671
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Tue Dec 25 02:53:45 2007 -0600
+
+ implements U directive for String#unpack
+
+ uses only one of the exception messages every time
+
+commit b414c94db1fa1af8e6cd3382c34fc6de5ed3bd1e
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Mon Dec 24 16:42:22 2007 -0800
+
+ Merge identical specs
+
+commit e0f28c224a2348dbf7c005694971a86f8e6162e1
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Mon Dec 24 15:59:24 2007 -0800
+
+ Kernel.Integer() shouldn't pass a base to String#to_inum
+
+commit eb93da7c578599469fe209f7b1d30f0f77d148f5
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sun Dec 23 16:43:33 2007 +0100
+
+ Wrapped one String#crypt case into compliant block for JRuby.
+
+ "hello".crypt("\x00\x00") is not really defined,
+ and heavily platform dependent.
+
+commit 7594c89cf2f017cb1fffad16bac6fcc7c9629422
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Sun Dec 23 10:53:30 2007 +0100
+
+ Added JRuby wrapper for String#% test.
+
+ Allow "%e" % (0.0/0) in JRuby to return "NaN", and not "nan".
+ I think, returning "NaN" is a proper behavior, and
+ it seems that MRI 1.9 is also following it.
+
+commit eaf9e328e81f9c1d4e80737a96d0eea6b511fabb
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Mon Dec 24 06:55:30 2007 -0600
+
+ implements BbHhIiLlSs directives for String#unpack
+
+commit 701945421d6a656f8b0b183052c4535a895e2afd
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Dec 24 00:44:26 2007 -0800
+
+ Converted specs to use the new #platform guard syntax.
+
+commit 238fbbc2331a1926f3d3f447d8433b046e7d34ac
+Author: Tom Mornini <tmornini@engineyard.com>
+Date: Sun Dec 23 15:43:26 2007 -0800
+
+ Clean up language on now understood and fixed alias_method e2mmap spec.
+ Fixing the alias_method problem has now uncovered something in const_set,
+ so I've included a very vague test (require 'e2mmap') to document the
+ problem until it's better understood.
+
+commit 69149b261ac13cc1a2b7c80c7b103d397fd96b9b
+Author: Tom Mornini <tmornini@engineyard.com>
+Date: Sun Dec 23 14:13:22 2007 -0800
+
+ Add spec for alias usage that breaks e2mmap.rb
+
+commit 71d9a4144811b2c9c74edc55f348637c57b0cb84
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Sun Dec 23 05:25:37 2007 -0600
+
+ implements aDdEeFfGgXx directives for String#unpack
+
+commit bebafb1383a5126c959c33a1336f3a2e4b6993f6
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Dec 22 18:16:14 2007 -0500
+
+ Saner specs for stream-style Dir access. Passes 1.8.6-p111.
+
+ * Dir#read, #tell, #pos, #pos=, #seek and #rewind which are a part
+ of the stream interface to Dir no longer rely on platform-specific
+ position values, instead opting to just ensure they work as expected.
+
+commit a2e4c318a3406c9532404611f14d2790695c0a7a
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Dec 22 16:29:00 2007 -0500
+
+ Enabled Time#at spec to work with BSD `date`.
+
+commit 0e983f2e948ab997834dbc703e9eeb11d86a7022
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Dec 22 16:17:14 2007 -0500
+
+ String#to_i spec to check for correctly parsing 0x-1 and the like.
+
+ * This was fixed in 1.8.6-p111.
+
+commit 5e635a46f4733bcc2071b52ea076584614fe5655
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Dec 22 15:52:03 2007 -0500
+
+ Fixed various String spec issues and added a few. Passes 1.8.6-p111.
+
+ * String#% with o for octal numbers is still broken but that seems
+ to be due to MRI's sprintf.c.
+
+commit 780f22bde03e280f5af2509bef260585341f4e0b
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Dec 22 15:48:55 2007 -0500
+
+ Fixed incorrect use of #should raise_error.
+
+commit 0b239b4f66c20ad5690e429639c4bf11a809ab58
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Dec 22 12:22:20 2007 -0500
+
+ Hash specs fixed. Pass under 1.8.6-p111.
+
+ * Changed to use HashSpecs#frozen_hash and #empty_frozen_hash
+ for clarity and being less error-prone.
+ * Fixed various typos causing problems.
+
+commit 9a2450e5c51333474cf012c3a1364e95384af9e0
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Dec 22 12:20:48 2007 -0500
+
+ Kernel.caller specs revised. Pass 1.8.6-p111.
+
+ * Fuzzier matching of the data in the call stack.
+ * Fixed specs for omitting frames.
+
+commit 692f4e8a652e273096c0f77ffe571318c59d2b12
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Dec 22 10:59:19 2007 -0500
+
+ File.ftype specs pass on 1.8.6.
+
+ * Use `find` to locate specific file types instead of relying on
+ predefined paths being correct.
+ * Re-enabled character devices.
+ * FreeBSD does not implement block devices.
+
+commit f1251ebc602311ec305a4b1b35a765ee45b9c164
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Dec 22 10:11:07 2007 -0500
+
+ Bignum#div returns an Integer if evenly divided.
+
+commit b62e1b7a21df1d7736767530f216148b8a93e38a
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Dec 22 09:52:33 2007 -0500
+
+ Fixed Array spec failures under 1.8.6-p111.
+
+ * Array#fill raises if given a negative count.
+ * Array#initialize will always raise if frozen.
+
+commit e3d6a3df6c1dfc37731ff4de5de32dc996bb61bb
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Dec 22 09:37:46 2007 -0500
+
+ Silence warnings from removing *.rbc in #load and #require specs.
+
+commit a11171e853b3efb94b4cba03786ea851d81411c6
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Sat Dec 22 07:39:40 2007 -0600
+
+ implements CcQqVv directives for String#unpack
+
+commit eea90994f2a1b76ed11b29e05a16c9c299d59235
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Dec 22 00:23:29 2007 -0500
+
+ Added __FILE__ specs to #load.
+
+commit 0e04ca49ebdba35a7a293b6de82d9d67c6ff4ac5
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Fri Dec 21 22:59:50 2007 -0500
+
+ Correct __FILE__ information from #load and #require.
+
+commit 51c2543fe032b680a6c8f8cf8121196070c61c66
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Dec 21 23:51:37 2007 -0800
+
+ Replaced use of @path1 with equivalent nil in File#fnmatch.
+
+commit b9f979393456dc3c93250e3a50b54b489a25c5d1
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Dec 21 23:29:51 2007 -0800
+
+ Added -w to bin/mspec to emit warnings. Fixed warning in mSpec.
+
+commit 16ce249216f490b9f7921aa69932f9e8bd60ca0e
+Author: Jeremy Roach <jero_rub@yahoo.com>
+Date: Fri Dec 21 22:50:40 2007 -0800
+
+ Implements N, n, and Z directives for String#unpack.
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit e1d292e28fe409c087f314bb139371a1f248850d
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Thu Dec 20 15:04:12 2007 +0100
+
+ Fixed race condition in ThreadGroup#add specs.
+
+commit 469527ddf33484a4a77f3d73c611e9a393bd48ad
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Fri Dec 21 12:02:50 2007 +1100
+
+ Added CompiledMethod#decode specs
+
+ * Added UnboundMethod#compiled_method accessor
+ * Improved robustness of ISeq decode when dealing with junk at
+ the end of an iseq
+
+commit 08c2f5c29a2debed90ae1fff817c30e269913609
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Dec 20 23:45:53 2007 -0500
+
+ Re-enabled purging .rbc files in require_spec and fixed the masked problem.
+
+commit dd4f3c52e79d01e826918e49fa626d7358f87901
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Dec 20 22:53:48 2007 -0800
+
+ Clean up a couple of failures seen in ci. spec/core passes.
+
+commit a5667632ae8d112c0271e00cbba53a274075cd1a
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Dec 20 22:49:05 2007 -0800
+
+ Removed legacy, unused spec/reports/base.txt.
+
+commit 853e100b6f7fff24e4aaa40ed30c6add523f8df2
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Dec 20 17:49:36 2007 -0800
+
+ Fix a bunch of String specs (thanks random8r)
+
+ * Note: rubinius now has the same behavior as MRI for Nan,
+ Infinity and -Infinity when using String#to_f
+
+commit b220f4921fd799ac28c60132ca08cf16df6f713e
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Thu Dec 20 15:37:25 2007 -0600
+
+ fixed require specs to work correctly on any run including first
+
+commit 56ac483e3559e1d4913e4c36c9a8f007523fdab0
+Author: Hunter Kelly <retnuh@gmail.com>
+Date: Thu Dec 20 19:36:21 2007 +0000
+
+ Fix typo in spec/core/regexp/union_spec.rb
+
+commit 634300eed40ef0ded16ab7cac7865dd783486c2d
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Thu Dec 20 03:46:59 2007 -0600
+
+ Add 'sleep' checks to threadgroup spec to avoid the same race conditions seen in kernel/sleep_spec.
+
+commit 72b7123c9b3d1d266f4ce035b4e99dd0c2dbd88d
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Dec 19 22:46:17 2007 -0500
+
+ New compliant (moreso, anyway) #load and #require.
+
+ * Improved #load and #require.
+ * Specs for the above.
+ * File.to_sexp and String#to_sexp allow empty input. They are
+ processed as a file containing 'nil'.
+ * Archive#get_object_fuzzy allows no extension or .rb instead
+ of .rbc only.
+
+commit fe633062095096fe00599cbb89aa4370ab5ccb3e
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Dec 19 23:03:08 2007 -0800
+
+ Fix Kernel#puts
+
+commit 364ca08cbbb1848b549d99deb11e2449ad99334a
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Dec 19 23:10:17 2007 -0800
+
+ Updated CI excludes.
+
+commit 5f1c381560a8d4d594749d42b5b2feeec341d4e5
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Dec 19 22:24:10 2007 -0800
+
+ Fix Kernel#open
+
+commit 3b3ed6304deab01cb448665c5f4b17d813f04e65
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Dec 19 21:43:58 2007 -0800
+
+ Cleanup more method specs, all pass on rubinius now
+
+commit 76bbbf275f4e835444f684b2e688b292f20c1ffe
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Dec 19 18:08:09 2007 -0800
+
+ Implement protected methods
+
+ * Added a bit more verbose specs to methods_spec.rb, to show
+ specific cases.
+
+commit dce06b35481bb1951c587d36f63abaae069d0ae4
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Dec 18 22:28:30 2007 +0100
+
+ Wrapped one String#unpack test case into compliant block.
+
+ The test case is platform-specific, and not suitable for, say, JRuby.
+
+commit bb4945ea7b9253150f753508e92633b6e355194a
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Dec 18 21:30:24 2007 +0100
+
+ Added new String#unpack test to exclude file for CI.
+
+commit 1bd8beb8e0b335f1de309d6320312a1b64af1e4d
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Tue Dec 18 19:51:47 2007 +0100
+
+ Added more tests for String#unpack with Z/Z* patterns.
+
+ These patterns are known to be tricky, and their
+ handling was changed during Ruby's life.
+ See [ruby-talk:98364].
+
+commit d26edc2269a77667dbefcfb1ea6212d8ada9ef97
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Dec 19 01:25:27 2007 -0800
+
+ Fix a bunch of Task GC problems, better memory management.
+
+ * Use ALLOC* macros instead of malloc/calloc directly
+ * Also, simple fix for Time
+ * A Kernel#loop implementation
+
+commit 4143b92e6112241ff2facd64047491ce579bf0e9
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Dec 19 00:43:32 2007 -0800
+
+ Finished converting Object.new to mock() in specs.
+
+commit 9cae61f827d2eeca0a744e551551efd6bc85a2ae
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Dec 19 00:02:46 2007 -0800
+
+ Spec #it blocks must be inside #describe blocks for RSpec.
+
+commit 7df00ef6d2471d0b37829e0a4d1ef45edf782a44
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Dec 18 23:28:43 2007 -0800
+
+ More conversion of Object.new to mock() in specs.
+
+commit 12463512d0ad48fae3a1843d9d409649551dd13b
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Dec 17 22:53:38 2007 +0100
+
+ Corrected String#* test to pick large enough Bignum, even on x64.
+
+commit 79cbff2c9a0cb15e9e5767f94242fa4360a0c4a0
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Dec 17 16:43:16 2007 +0100
+
+ Removed race condition from Kernel.sleep spec.
+
+ This problem caused JRuby spec runs to hang.
+
+commit 3032e60e10dd1ae61ffb40b351f4f6731395602b
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Dec 17 15:43:42 2007 +0100
+
+ Enabled one Hash#rehash test case for JRuby
+
+commit 1808106191856f4f82b948abc5c7e708a747d059
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Dec 17 15:40:49 2007 +0100
+
+ Issue #153: Hash#rehash test enforces unspecified impl detail
+
+ Wrapped the test so that it won't run with JRuby.
+
+commit efbf30477ea289911d9cafbde89ecbe2c8c65089
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Dec 17 15:25:23 2007 +0100
+
+ Corrected :mri --> :ruby
+
+commit 60a3ede3c64b62fb26905ed1c236c0e241b64515
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Dec 17 15:23:42 2007 +0100
+
+ Issue #182: String#to_f spec corrections for NaN, Infinity
+
+ JRuby AND Rubinius treat "Infinity".to_f, "-Infinity".to_f ,
+ "NaN".to_f differently than MRI.
+
+ MRI returns 0.0 in all those cases, but JRuby and Rubinius probably
+ do something more meaningful, they return Infinity, -Infinity and Nan
+ respectively.
+
+ It was agreed that JRuby's and Rubinius' behavior is a feature rather
+ than a bug, and worth preserving and checking for.
+
+commit 521a82d8c325a33b3409423d61b589c7b8681870
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Dec 17 15:14:28 2007 +0100
+
+ Refactored commonly used generators into fixtures.
+
+ Thus reducing copy-paste.
+
+commit 86820a339c74e3ca8fc9515e5fdf31ad42780201
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Dec 17 14:12:43 2007 +0100
+
+ Initial version of Generator specs.
+
+commit 91353183ace65d8e751db14a829e8f24d043710c
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Dec 18 03:18:19 2007 -0600
+
+ updated excludes for Kernel#sleep and Thread
+
+commit 6de193c0819f74717eb2e9eff8480f0d801b0e41
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Dec 18 01:01:31 2007 -0800
+
+ Replaced Object.new with mock() where appropriate.
+
+commit 1217fa030ff26712e9718ebecfe351830c543d7e
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Dec 18 02:16:10 2007 -0600
+
+ fixed redo in loop by save/restore condmod around loop context in compiler1
+
+commit df757142c774becfc2cbc4b38e43e31056acbae2
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Dec 18 02:00:27 2007 -0600
+
+ spec for using redo,next, and break in one loop
+
+commit 7169fd31b7c22750241212c242bc8aacdafe632f
+Author: Vladimir Sizikov <vsizikov@gmail.com>
+Date: Mon Dec 17 13:08:09 2007 +0100
+
+ String#sub specs wrapped to correct JRuby test failures.
+
+ JRuby reports Ruby version to be 1.8.5, but in this
+ particular case it behaves like Ruby 1.8.6
+
+ Differences between Ruby 1.8.5 and Ruby 1.8.6:
+ different error raised.
+
+commit 04e228e131d06cd764d69375ddfdf44e4fec2b38
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Dec 18 01:27:00 2007 -0600
+
+ spec for Thread.pass and updated :mri to :ruby
+
+commit 11348e25ba30199e3beb05f8c38c18820fbefc3f
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Dec 18 01:11:38 2007 -0600
+
+ Some minor fixes in Thread specs
+
+commit 5cb3bcbf8f1d2a2237200ca0a9a9c6408d478ad6
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Dec 18 01:11:05 2007 -0600
+
+ spec and basic functionality for Thread.stop
+
+commit 3301fbb3ec43b5252c0aa6d45eb2f0e21581ff0a
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Dec 18 01:09:49 2007 -0600
+
+ Thread.sleep doesn't even exist in rubinius
+
+commit bd964f579f84a39097ecee1271664d672b6553a7
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Dec 17 21:40:16 2007 -0800
+
+ Replaced :mri with :ruby for spec guards.
+
+commit d21810882621356c35dcd101daca5ee5549f6607
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Dec 18 00:42:32 2007 -0600
+
+ Threads should report there status of sleep, aborting, and run
+
+commit 85a6476a236bd1e65d42ca03846c662a10842f37
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Dec 18 00:41:09 2007 -0600
+
+ Thread#status should return nil if Thread terminates with an exception
+
+commit ecb4455a75f4af2ae0059ca4960c2282b4ec632a
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Mon Dec 17 23:50:24 2007 -0600
+
+ specs for Thread#{key?,keys} and added key type checks for Thread#{[],[]=}
+
+commit c1a5d7e52b33ba6686441c61652bcc41ae0547f8
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Mon Dec 17 22:15:37 2007 -0600
+
+ specs for Thread#[]
+
+commit c2c7f0adc6ebbad925adb2471b6064b67528b420
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Mon Dec 17 21:47:52 2007 -0600
+
+ ensure LocalThread#current != Thread.current
+
+commit d05dac276f36326e143aa75bb43e4ab07bd8ddc9
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Mon Dec 17 21:28:44 2007 -0800
+
+ Fix a bunch more Kernel specs
+
+commit 4ddd0e144b4e4f64c51fc8d64952826d92a5e83f
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Mon Dec 17 19:45:26 2007 -0800
+
+ Fix Thread.abort_on_exception and Thread#inspect
+
+commit a12ad6fbc2589a7864a7c784386fb6ce7dae1db1
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Mon Dec 17 21:29:49 2007 -0600
+
+ Kernel#sleep and Thread#join specs now use locks to maintain automaticity instead of while th.status == 'run' loops
+ added a Channel fixture to Kernel to support the use of locks in Kernel#sleep specs
+
+commit 72e3fb453c266e514b817daa66bf6033f1d19e40
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Mon Dec 17 19:01:18 2007 -0800
+
+ Fix callcc specs, revert all locals back to using the locals tuple
+
+ * compiler1 now does what compiler2 is going to do, ie, only use the
+ locals tuple to store locals. Storing them on the stack has proved
+ to be a pain, and wont be used further.
+
+commit 6e35be2ddef8d055e064462c88a8b3f33eb4fe0f
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Mon Dec 17 18:37:16 2007 -0800
+
+ Faster Class#new, initialize can be private
+
+ * Class#new now uses a bunch of inline assembly to be able
+ to call a private initialize
+ * Clean up Module#name a little
+ * Made machine's rbt a little more robust
+
+commit 47a5bbf34ef8a60a18c1c8c6130d493a299ff852
+Author: Hunter Kelly <retnuh@gmail.com>
+Date: Tue Dec 18 01:10:33 2007 +0000
+
+ Make sure files opened with "w" are truncated.
+
+commit 0fdc8c9b7d05cc2e96908b280ac144de0d04f646
+Author: Hunter Kelly <retnuh@gmail.com>
+Date: Tue Dec 18 00:16:27 2007 +0000
+
+ Fix excludes for spec/core/stuct/{new,struct}_spec.rb
+
+commit 5b1252e6b2d8f8d70343b06f3520114de2040524
+Author: Hunter Kelly <retnuh@gmail.com>
+Date: Sat Dec 15 17:31:25 2007 +0000
+
+ Minor fix to struct_spec to include fixture.
+
+commit 4fd0356ab9e9bb5c2a805b1f863b3177458966fe
+Author: Hunter Kelly <retnuh@gmail.com>
+Date: Sat Dec 15 16:29:49 2007 +0000
+
+ Add case to spec/core/class/new_spec for names of nested classes.
+
+ * Updated spec
+ * Add some comments where this may be fixed
+
+commit 86736d564f34a2f97f7c7bedcab09c2472861b01
+Author: Hunter Kelly <retnuh@gmail.com>
+Date: Sat Dec 15 16:16:26 2007 +0000
+
+ Fixes for struct class names.
+
+ * Tighten up specs to show what class names should be.
+ * Fixes #inspect
+ * (Partially) solves the larger issue of an anon class getting a name when
+ assigned to a constant - works now when Module.const_set is called.
+
+commit ad0d5ff2396baf43c8b2e37a3132765a074b241d
+Author: Hunter Kelly <retnuh@gmail.com>
+Date: Fri Dec 14 02:33:23 2007 +0000
+
+ Fixes related to Struct.new
+
+ * Fix to method_table to correctly handle DelegatedMethods
+ * Changed spec/core/stuct/new_spec.rb to allow :rbx to call
+ to_sym on objects passed to Struct.new to get the symbol
+ value.
+
+commit 18f10dc700fe24f3bd230063bc7c1e8a82e8348f
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Dec 17 08:58:22 2007 -0800
+
+ Updated spec excludes to run with bin/ci under the new mspec.
+
+commit 678fb90c5c8aa96e10a9f95f520312f12f8fa3f2
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Dec 15 22:43:44 2007 -0800
+
+ Changed true/false/nil specs to not use def in describe block.
+
+commit 9e132474aafb6a0f0c968c2e085b09bfc07e1a0d
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Dec 15 18:49:31 2007 -0800
+
+ Fixes to run the specs under RSpec and mSpec.
+
+commit cd3ecf52645b94921db92393e6e4d295d12bba88
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Dec 15 00:59:11 2007 -0800
+
+ Misc fixes to mspec. bin/mspec -t r spec finally runs!
+
+commit f3b3f70bb47b04e7a67c1dbc3ae38711857b5184
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Dec 13 22:46:54 2007 -0800
+
+ Multitudinous miscellaneous fixups for mspec and mmock.
+
+commit 55ab5b2ee42e4fabcfd8c51d6fac304cdfec31c7
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Dec 10 01:01:58 2007 -0800
+
+ Various fixes to mspec to run Rubinius specs.
+
+commit 86c0f131608b4ad7cba93eabd172a48e5b60ca0f
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Dec 7 15:34:06 2007 -0800
+
+ Added runner guards to omit specs that will always fail under RSpec.
+
+commit 75706dbfabbe359b6410f0d3743f0ea682146ac1
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Dec 7 14:03:46 2007 -0800
+
+ Added #runner guards to mspec.
+
+commit 3da390988031bf0066a849934ee758475ebbfa04
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Dec 7 02:38:13 2007 -0800
+
+ More fixes to run the specs under RSpec.
+
+commit 2b0f4e408b733dcd9089a19d78cd8e4cce20b99c
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Dec 6 18:40:33 2007 -0800
+
+ Yet more spec cleanup to run under RSpec.
+
+commit 1e4171d4682f55776e01e42f564714548c1d9bd9
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Dec 6 02:16:42 2007 -0800
+
+ More changes to run specs under RSpec.
+
+commit db020d30374e419792f76077757784008953c0a6
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Dec 5 02:01:30 2007 -0800
+
+ Various changes to get the specs running under RSpec.
+
+commit 968c2daa5345a0cddb8d3d5bd2b6bf2eeb0c1d6f
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Dec 4 23:51:52 2007 -0800
+
+ Convert remaining mocks to RSpec syntax.
+
+commit e5dc3ac814d1cda923131257dfbc9a30bf501b62
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Dec 4 23:28:17 2007 -0800
+
+ Convert remaining 'should_raise' to 'should raise_error'.
+
+commit a7be230ac71ece2bb8dcece72d629bcd0ce6a5e0
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Dec 4 18:29:26 2007 -0800
+
+ Converted specs from mini_mock to RSpec mock syntax.
+
+commit 4136e2fef4a81eb6e9e14070ff5301638f9acf14
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Dec 4 02:25:31 2007 -0800
+
+ Integrated mini mock with mspec. Updated spec_helper for main specs.
+
+commit d71c0c7412af01d6295d8caab43a80d0221ea16d
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Dec 4 01:49:17 2007 -0800
+
+ Added #shared and #it_behaves_like to mspec.
+
+commit da61adc0a079c858385773b12d683e2f5e2cc0e8
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Dec 4 00:43:15 2007 -0800
+
+ Converted 'should_be_ancestor_of' to 'should be_ancestor_of'.
+
+commit 5ed0096aac58fef09fc766d808aea74356aacfa8
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Dec 3 17:57:56 2007 -0800
+
+ Replaced dev_null with CaptureOutput.
+
+commit 62282bd5cb5c555e6447dcf2d6d0da355913fe8b
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Dec 3 17:30:01 2007 -0800
+
+ Replaced 'should_include' with 'should include'.
+
+commit f990a7c58a7eee6dbbb3c50df7682942048b959f
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Dec 3 17:21:50 2007 -0800
+
+ Replaced 'should_be_close' to 'should be_close'.
+
+commit c53601c56bd222dfacf03f134132869eb71c5146
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Dec 3 17:10:41 2007 -0800
+
+ Finished converting should_raise to raise_error.
+
+commit 165dd99535b0829d2e2364fac24375068969c6ab
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Dec 2 23:22:54 2007 -0800
+
+ Convert should_raise to should raise_error for RSpec compatibility.
+
+commit c9ff50a4b4be25614cc0ac2ea5540cfe87a939d3
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Nov 28 23:30:44 2007 -0800
+
+ Moved mspec out of spec dir.
+
+commit e9a40a77b6fa7d08969ea195aabbb930b665fe02
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Nov 28 23:25:59 2007 -0800
+
+ MSpec base formatter and specs.
+
+commit 2fc3ac3f8efbaf0861cadfd59bcdf926d2196284
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Nov 28 20:32:42 2007 -0800
+
+ Port fix to ruby engine detection from mainline.
+
+commit 9a52e660536b4723bf24e2717fec757a1bdfa49f
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Nov 28 01:35:52 2007 -0800
+
+ Match RUBY_NAME against /^ruby/ to pick up e.g. ruby1.8.
+
+commit 85536b98862f6abec310bfad03be17652ee65944
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Nov 28 01:01:46 2007 -0800
+
+ Implemented mspec matchers.
+
+commit c953335397c6c8b9d7b27a3d240fde3b3518cb48
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Nov 27 01:35:07 2007 -0800
+
+ The rest of the mspec big picture.
+
+commit 2f598f193eb1b10065c8e1a8d5c2aaa89c689072
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Nov 27 00:39:59 2007 -0800
+
+ Added base operator matchers and specs.
+
+commit 0cc0b5a97661970d4cbb5e46406e7ee06421e637
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Nov 26 21:03:33 2007 -0800
+
+ Migrated mspec and ci runners to mspec dir.
+
+ Created stubs in bin/ci and bin/mspec that call the respective
+ scripts in spec/mspec/scripts.
+
+commit b98d65eaa90d966fc2f7b8f8387266e241c202de
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Nov 25 01:03:26 2007 -0800
+
+ Added specs and guards for mspec.
+
+commit 7bb316d1291c9d0a16904d4a3ee60094a713f215
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Nov 24 21:40:46 2007 -0800
+
+ Prevent MSpec's #should(_not) from overriding RSpec's.
+
+commit c446988257a2104d72abd4a362dc21ca6183aab0
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Nov 20 22:47:25 2007 -0800
+
+ Defines #should and #should_not for mspec.
+
+ Specs for #should and #should_not.
+ Adds example for using mspec "base" layer.
+
+commit 1aecf8e828dfd3d86f43d8c9c927e7c0ccb16b68
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Nov 12 00:40:04 2007 -0800
+
+ The mini rspec big picture.
+
+commit f2979b03f29e7ac810b81f9087ea53923de5a35c
+Author: Charles Lowe <aquasync@gmail.com>
+Date: Mon Dec 17 15:18:24 2007 +0100
+
+ Added missing error checks to Dir.chdir block form.
+
+commit 028fee4e6d48514cae53f87c143bb68501bf58e9
+Author: Chris Shoemaker <chris.shoemaker@cox.net>
+Date: Wed Dec 12 20:01:35 2007 -0500
+
+ Add further specification of size changes during Hash#delete.
+
+ This was actually failing a while ago but now passes after recent
+ changes.
+
+commit f757f4359c86f778ac8e5931b8915511fd03506d
+Author: Chris Shoemaker <chris.shoemaker@cox.net>
+Date: Wed Dec 12 19:59:28 2007 -0500
+
+ Tighten another spec in core/hash/equal_value_spec.rb
+
+ Hash#== compares keys with matching hash codes via eql?
+
+ This spec was using hash keys where key.eql?(key) was false.
+
+ That's pretty pathological, but there's probably some real
+ non-conformance with MRI here. MRI can test for object identity
+ without calling eql? so a key is still found even if it doesn't
+ eql? itself.
+
+ That's not really related to the behavior this spec is specifying,
+ though. So, this patch just uses a less pathological implementation
+ of eql?
+
+commit 3f73ddf6bec5c704ceb5ed43481971860293353d
+Author: Chris Shoemaker <chris.shoemaker@cox.net>
+Date: Wed Dec 12 19:49:49 2007 -0500
+
+ Tighten spec in core/hash/equal_value_spec.rb
+
+ "Hash does not compare keys with different hash codes via eql?" was
+ failing because it detected that eql? was called on a key. However,
+ eql? was not being used to compare keys with different hash codes
+ from the two hashes. Instead, eql? is used to compare a key to itself
+ during hash element reference, in order to distinguish between two keys
+ with equal hash codes that aren't eql?.
+
+ The tightened spec only fails if the keys are compared eql? to each other.
+
+commit e355e98a32f34619628a17f5052750da6881cda9
+Author: Chris Shoemaker <chris.shoemaker@cox.net>
+Date: Wed Dec 12 12:26:15 2007 -0500
+
+ Add specs for hash stability of various Numeric subclasses.
+
+commit 0d774c99254b2c5992a17ecb2a5a12dcd2cad05e
+Author: Chris Shoemaker <chris.shoemaker@cox.net>
+Date: Tue Dec 11 20:37:45 2007 -0500
+
+ Add a Hash#store spec for storing unequal keys w/ same hash.
+
+commit edfff4981285007ecac132f565243150a8a8bd7e
+Author: Curtis Schofield <123@noself.net>
+Date: Tue Nov 27 19:02:45 2007 -0800
+
+ Specs created for Process#gid and Process#uid
+
+ * both are using the unix system command 'id'
+
+commit 4e269d01238537cc45f4c347b12053616007d94d
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sun Dec 16 23:46:50 2007 +0100
+
+ Excluded evil Thread specs.
+
+ This makes bin/ci usable (pass) again on my system, where it was horribly
+ broken before.
+
+commit b32c46ba95f2ecdaf646a030b96ee9b3737929a0
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sun Dec 16 23:37:26 2007 +0100
+
+ Excluded failing Kernel#eval specs.
+
+commit 0c56f3a1f84dd94d1f9685af9e9d6e0efd0cfabf
+Author: Hunter Kelly <retnuh@gmail.com>
+Date: Sun Dec 16 20:24:54 2007 +0000
+
+ Tighten up specs for what should happen when array shrinks during iteration.
+
+commit bfa8c532605c9e3b3d7f853516de9aae596c611d
+Author: Hunter Kelly <retnuh@gmail.com>
+Date: Sun Dec 16 19:11:25 2007 +0000
+
+ Added specs for Array#each when the array is changed during iteration.
+
+ Ditto for Array#each_index.
+
+commit b3aa2af4a3467b4eeb8765010286c12bd5adfbf9
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sat Dec 15 22:53:35 2007 +0100
+
+ Sanitized Object#id spec.
+
+commit cce5b7004a774041d78c3b2e55af8063335a9512
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Fri Dec 14 19:02:57 2007 -0800
+
+ Fix sleep spec, implement Thread sleep status and death detection
+
+commit c2475838be23ae287075b7e9ea832013f1db77c4
+Merge: 30f20cf... d061b86...
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 14 10:56:53 2007 +0100
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit 30f20cfbd67487c426827406890fdb06fac8045c
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Fri Dec 14 10:56:17 2007 +0100
+
+ Fix race conditions in Kernel#sleep spec by ensuring target thread is actually sleeping before continuing.
+
+commit d061b864f636210e40982d961b0aa5afc24543d0
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Dec 13 23:04:27 2007 -0800
+
+ Fix require specs to not require checked in .rbc or .rba files
+
+commit 41831976d25a4d5a8e26673199276098cc45b4d3
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Dec 13 22:40:43 2007 -0800
+
+ Fixed Thread#run, added corruption detection to rbc files
+
+ * Thread#run was confusing the Thread scheduler, cause things the VM
+ to quit running.
+ * Added corruption detection to .rbc files in the form of a SHA1
+ hash placed in the .rbc, just after the header.
+
+commit 77f0f29060d5ba3f33dc45029525acb715eb61c2
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Fri Dec 14 15:10:59 2007 +1100
+
+ Compiler2 fix for attrasgn in masgn
+
+ An attrasgn node contained within an masgn does not include
+ the assigned value in the attrasgn sexp. This was leading
+ to the argument count to []= to be understated by 1.
+
+commit faaa1932fe05ee4d506b768f8d9d884af5345547
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Wed Dec 12 19:59:11 2007 -0600
+
+ fix non-determinism from Thread.sleep by removing blocking sleep
+ add check for duration of 0 or 0.0 to instant return and added more specs
+
+commit e98b2d1f9788c1813bef2d920779c95effbd3d9f
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Dec 11 17:27:43 2007 -0600
+
+ spec and fix to allow floating point timeouts to Thread#join
+
+commit 801cb5ef58a6debfd348a33f864737cbce7c3d77
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Dec 11 17:26:58 2007 -0600
+
+ added noncompliant spec showing that sleep(nil) is allowed in rubinius
+
+commit 84d280810c840d6699b5c9ad094964fe779235df
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Tue Dec 11 16:30:41 2007 -0600
+
+ fixed Thread#sleep to allows floats, and switched Thread::sleep, Kernel::sleep to use Thread#sleep on current thread
+
+commit 3d10a8a10741786ba76a4cc1083934f908d52ec2
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Mon Dec 10 17:13:34 2007 -0800
+
+ Allow Thread.new to take arguments
+
+commit 4f5258b938a7aacf31e73b5fe6312e3c927d9cf8
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Mon Dec 10 00:21:20 2007 -0800
+
+ Fix rb_define_alloc_func
+
+commit 7ab0f524de5a6b796ec1000402392cb138150eed
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Fri Dec 7 15:06:45 2007 -0800
+
+ Initial ThreadGroup implementation
+
+commit 53fff95e300b1b26ed16f12c13684eadf8235d7a
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Fri Dec 7 01:30:55 2007 -0800
+
+ Add rb_str_substr
+
+commit ec82de9f67e271718b874c0d777765da696bef88
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Sat Dec 8 15:26:17 2007 -0800
+
+ Add wrapped struct spec
+
+commit 65998d601aae601b3b43878f534362136a01ff17
+Author: Brian Takita & Nathan Sobo <brian.takita@gmail.com>
+Date: Fri Dec 7 17:18:24 2007 -0800
+
+ Added specs for Module#undef_method.
+
+commit 97f8c9c32b9400ae42d0dc80aa7e17b22864fce9
+Author: Brian Takita & Nathan Sobo <brian.takita@gmail.com>
+Date: Fri Dec 7 16:13:02 2007 -0800
+
+ Moved Object#to_a to Kernel#to_a. Added VM.coerce_to_array.
+
+ VM.coerce_to_array will be used for splatting any object.
+
+commit 865ce7d771a101bc8c2c9ae3a82cbc3f37450c4b
+Author: Brian Takita <brian.takita@gmail.com>
+Date: Fri Dec 7 12:47:08 2007 -0800
+
+ Merge branch 'array'; commit 'nathan/array' into array
+
+commit b4541a90f84c898e3cd9851ac4b207d559078a59
+Author: Nathan Sobo <nathansobo@gmail.com>
+Date: Thu Dec 6 23:33:10 2007 -0800
+
+ Updated language/array_spec.rb for more detail on splat operator.
+
+commit 577b2f1c395dc49165842c405fb47bbb7591158a
+Author: Nathan Sobo <nathansobo@gmail.com>
+Date: Thu Dec 6 18:30:16 2007 -0800
+
+ Fixed :many_if for compiler 1.
+
+ Before it was translating many_ifs to a flat array of if statements instead
+ of nesting them. Also, multiple boolean expressions in the case were not
+ expanded to a boolean disjunction.
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit 1d555fa07aaed8e59e728cb0013daa10b3b17b25
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Dec 6 21:24:44 2007 -0500
+
+ Add some JRuby-inspired eval specs
+
+commit 3131fb81eef380d163d028f5587475bbf170befb
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Dec 6 12:19:49 2007 -0800
+
+ Fix minor constant lookup issue and add timing to mspec
+
+commit 26897cd85c693cac10229d7467436717552088c0
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Dec 6 11:42:36 2007 -0800
+
+ Fix another constant lookup bug
+
+commit 06a3f07999aeb4f7379ea40205451d326d1ba596
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Dec 6 16:37:15 2007 +0100
+
+ Updated CI excludes for IO#each and IO#each_line.
+
+commit b495ab1019e9ee136e9d099faa51cba03c48e947
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Dec 6 16:01:31 2007 +0100
+
+ Extended argument checking in IO.read.
+
+ We're now checking that offset isn't negative either. This is done
+ before the length argument is checked, mirroring MRI's behaviour.
+ Also fixed a typo in the length check.
+
+commit 4fa2fbb6b6c27ced5d6cf902e63e3989c2d29b64
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Dec 6 14:56:41 2007 +0100
+
+ File.truncate raises Errno::ENOENT if the given file doesn't exist.
+
+commit cb7a0a7315e57f1adff0976bcd6b0c4a1a94d8c5
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Dec 6 14:15:55 2007 +0100
+
+ Added support for the length and offset arguments to IO.read.
+
+commit 9fe8f2bd73e28d28b7a9249e629ab7681321e4d5
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Dec 6 14:07:21 2007 +0100
+
+ IO.read only accepts file names and uses File to open and read them.
+
+commit 7ab1d9f3434e3f3b021de2f4087f2502e229c7a0
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Dec 6 14:00:19 2007 +0100
+
+ IO.new(nil) raises TypeError now.
+
+commit 227f6b4bf45b55eb659d41507d38fe5071ef7424
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Dec 6 13:24:31 2007 +0100
+
+ Fixed a typo in File.writable?.
+
+commit 645f30882c9dd39d13f49e45f2f32c43ebe25182
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Thu Dec 6 03:51:11 2007 -0800
+
+ Update Dir excludes
+
+commit e60ee517013d44c2ec6faf147f7dbd685fa520c2
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Thu Dec 6 00:40:17 2007 -0800
+
+ Fix flag checking in Dir.glob
+
+ Also clean ".", ".." skipping
+
+commit fa681ad7a3c1d0e1b4fb0702c2fc63cd80ec9377
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Wed Dec 5 18:08:41 2007 -0800
+
+ File.fnmatch? should accept escaped wildcards
+
+ Also fixes more Dir.glob specs
+
+commit e6b8ce23729606bf6fa748ea63c0e0a59b48a476
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Wed Dec 5 15:35:32 2007 -0800
+
+ Don't unescape leading period in File.fnmatch?
+
+commit 1cf054a08b0aeea7c348ff26c71ccaf22c02ce70
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Dec 6 02:36:54 2007 -0500
+
+ Rename Array#pretty_inspect to avoid conflict with pp
+ Hardcore bikeshed action on the way TestGenerators are inspected
+ Change describe.rb to call the renamed pretty_inspect
+
+commit 519d1226027623274766641a256e2a9753257266
+Author: Nitay <nitay@powerset.com>
+Date: Wed Dec 5 16:07:25 2007 -0800
+
+ Fix Constant = Class.new setting of name
+
+ Signed-off-by: Kevin Clark <kevin.clark@gmail.com>
+
+commit 568c57ca57d4a9183e492024e17aa1352902d1d2
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed Dec 5 20:31:43 2007 -0500
+
+ Clean up mspec output to prepare for unit_diff support
+ Use pretty_inspect to display compiler2 TestGenerator output
+
+commit e250521194380f4c942fd6d53664b746ca63e3e3
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Dec 5 17:27:27 2007 -0800
+
+ Fix constants spec to scope the fixtures
+
+commit 7010073617a4fa95ea5491284fef97a083d9d4f3
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Dec 5 14:42:27 2007 -0800
+
+ Vastly simplify and fix constant lookup
+
+ * New constant lookup specs to test behavior
+ * Added StaticScope object and field on CompiledMethod which stores
+ a StaticScope instance which indicates the lexical scope of the CM.
+
+commit 163e56646a817301201af843b45c973da058688c
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Wed Dec 5 23:00:16 2007 +0100
+
+ The spec for Dir#rewind doesn't pass on Rubinius.
+
+ It's not platform specific, but we don't have a working Dir#pos yet
+ and the Dir#rewind spec relies on it.
+
+commit 3afe61bd78aa9e850f081b83ca2c478ae297bda1
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Dec 5 13:51:05 2007 -0800
+
+ Changed mini_rspec to show failures unless being run by autotest.
+
+ Added dir_entry.rb to .gitignore.
+
+commit 0c661894b54615ee4915d61569e072ffdfa8826d
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Wed Dec 5 09:17:24 2007 -0500
+
+ Much-improved tiny option parser lib/options.rb.
+
+ * The Options API is much more user-friendly now,
+ size is still about 100 LoC
+ * Specs for the API.
+
+commit 092e0081c26eeda2ca6561eb19123b468965c84a
+Author: Ryan Davis <ryand-ruby@zenspider.com>
+Date: Wed Dec 5 01:40:31 2007 -0800
+
+ Added support for autotest.
+ Requires a new release of ZenTest to actually work.
+ I'm tired, I'll do that tomorrow
+
+commit 9e3e41d71d1bab8104ae17ff34aaa2311be3b0b1
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Tue Dec 4 22:59:49 2007 -0800
+
+ Commit miss for require specs
+
+commit d1a6f0805b739930e54406188e32ac1e0f30a74b
+Author: Eric Hodel <drbrain@segment7.net>
+Date: Tue Dec 4 22:24:49 2007 -0800
+
+ Add specs for Kernel#require, never add .rbc files to $LOADED_FEATURES
+
+commit a60e3bf901b62fbbbef59acb2c6c9f164be1fbbc
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Wed Dec 5 14:10:15 2007 +1100
+
+ Cleanup case spec, update excludes
+
+ * converted case specs to not use should
+ * separated out case specs with target expressions from those without
+ * updated excludes for two failing specs under compiler1
+
+commit f0de77911ff0b4532a47fb9803685e8d968d51ec
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Tue Dec 4 09:42:49 2007 +1100
+
+ Fixes for compiler2 when_spec failures
+
+ * Added compiler2 spec for when without an arg
+ * Added spec for when without arg with an else to
+ spec/language/case_spec.rb
+ * Implemented many_if sexp compilation
+
+commit c9c67738ecae341441098993923838a15b64d166
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Dec 4 20:42:27 2007 +0100
+
+ Test Etc.getgrnam() with "daemon" instead of "root".
+
+ The "root" group seems to be a Linux-ism.
+
+commit 1fd6d97e8eb20ce9908cc0abd09b7c5555ff5720
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Dec 4 19:31:24 2007 +0100
+
+ Post-move fix for the Options spec.
+
+commit ead52428d99549b6b53b8897d969e80072395ef6
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Dec 4 19:12:28 2007 +0100
+
+ Moved codearchive.rb, options.rb and readline.rb from kernel/core to lib.
+
+commit 0e4568bcc23011957cc250de2a93031648281b21
+Merge: 78fba04... fbc5ad5...
+Author: Charles Nutter <headius@wlan100.headius.com>
+Date: Tue Dec 4 00:50:46 2007 -0600
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit 78fba04c31e9d97c32862e9e104e3917dcff9137
+Author: Charles Nutter <headius@wlan100.headius.com>
+Date: Tue Dec 4 00:39:05 2007 -0600
+
+ Making socket spec more reliable by using nonblocking accept for TCPServer and adding a "ready" flag for UDPServer.
+
+commit 7e925ea53239207f5dd9ac5daddda8e0f1f3b687
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sun Dec 2 23:33:29 2007 +0100
+
+ Implemented Etc.
+
+commit 450778cf5f416f6b9531664d4fff2c159c93cbe7
+Author: Victor Hugo Borja <vic.borja@gmail.com>
+Date: Sun Dec 2 01:39:28 2007 -0600
+
+ Shared spec for class_eval.
+
+ - removed method-arguments from describe string
+ This was causing bin/completeness to report 0 examples for Module#class_eval/module_eval
+ - examples checking for TypeErrors test the exception is raised, but don't check
+ the exact message as it is not part of the interface.
+
+commit cd0d11c7eb23d881f1dd73701bd3edc12c5bd744
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Dec 1 15:28:41 2007 -0800
+
+ Updated CI excludes for Dir.[].
+
+commit e41e501bcf686937fbd3b8cfc86f325d7e06184d
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Dec 1 10:54:35 2007 -0800
+
+ CI spec excludes updates.
+
+ * Fixed rake pristine task to whitelist Kernel#require fixtures.
+ * Updated CI excludes for Dir.glob and Dir.[].
+
+commit 8f362a0350238366565a373f1feb9594efe03407
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sat Dec 1 18:50:55 2007 +0100
+
+ Make sure we delete the directories we're creating in the mock dir.
+
+commit dfc1b1cd32f47b48dd358ca50226d614425ef8b2
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sat Dec 1 18:08:27 2007 +0100
+
+ Dir.chdir now always resets the working directory when called with a block.
+
+commit 02f41a92bbafd1a555344e1082970e090cd1f9a5
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Fri Nov 30 23:41:29 2007 +0100
+
+ Call StringValue on require's and load's argument.
+
+commit 601fd404ba04f383ee286be015edb7e8c58574d5
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Fri Nov 30 14:27:44 2007 -0800
+
+ Refactor Kernel#require
+
+ * Refactor a bunch of Kernel#require into Compile#require_feature
+ * VM.load_library now detects if the extension is already there
+ and doesn't readd it
+ * Added specs for #require
+
+commit 9b903cb7c5c6a3bfbaa3a7a91dc7bad830af7294
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Fri Nov 30 10:56:02 2007 +1100
+
+ Compiler2 fix for anonymous masgn, e.g. * = 1,2
+
+commit 08bc0a2f14494a30d5956d5bdcca9eb37c921780
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Fri Nov 30 00:16:07 2007 +0100
+
+ Made check_argcount work with methods that don't take any arguments.
+
+commit 96108240fead7d764f3ec37d5eb20294f3a9dd97
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Nov 29 21:45:32 2007 +0100
+
+ Updated the CI excludes for Method#call.
+
+commit 61805ab7fac6ae9855baa05b42aebe66c3a2b3d3
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Nov 29 21:44:49 2007 +0100
+
+ Made Method#[] an alias for Method#call.
+
+commit 219d34dedf6ff0ed083cb5f1e8b6a5c437ad366c
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Nov 29 21:22:05 2007 +0100
+
+ Enabled the Kernel#method_missing specs.
+
+ They pass now that they specs aren't confused by the Dir spec helper
+ methods anymore.
+
+commit de5320efe8095e612e235bea7053084bb61d300d
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Nov 29 21:17:58 2007 +0100
+
+ Moved the Dir spec helper methods in their own module.
+
+commit 1ae47b5c091c209597bec7475935bbcff34b50b5
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Nov 28 23:45:09 2007 -0800
+
+ Applied patch from #151.
+
+commit adb5b139afa452869464fe53b710d7cb8b93131b
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Nov 28 20:29:38 2007 -0800
+
+ Better fix for guards to distinguish ruby, ruby1.x from ruby1.9.
+
+commit e53f72172e395c7766dcecadd2ffd6c7caf303e7
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Nov 28 20:15:27 2007 -0800
+
+ Patches (or modified patches) from #157-162.
+
+commit b07eeee79ea5a0c0160c34aec2d690f1b46f7380
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Wed Nov 28 14:43:41 2007 +0100
+
+ Fixed Bignum#modulo and Bignum#remainder.
+
+commit 85b05b5103aaeb5d946e0f691f77af2dafa6f30a
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Wed Nov 28 01:00:07 2007 +0100
+
+ Unified the File.unlink and File.delete specs.
+
+commit 2ec59a82f279a4ba6b5b781c90a7714aba767ed9
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Nov 27 23:30:43 2007 +0100
+
+ Be more specific wrt the expected exception.
+
+commit 54236949e9b974d4c4dcf95b63318c844c62aca4
+Author: Victor Hugo Borja <vic.borja@gmail.com>
+Date: Tue Nov 27 19:58:50 2007 -0600
+
+ Module#<=> is working, Updated CI excludes for Module specs.
+
+commit 55c7529f4c8b02eff7e0b594f33b28750877fca2
+Author: Victor Hugo Borja <vic.borja@gmail.com>
+Date: Tue Nov 27 14:53:02 2007 -0600
+
+ Specs for Module#private/public/protected
+
+commit 5b693fae3464abb6a5aa05d8236bd8f4610c89d4
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Nov 27 19:36:06 2007 +0100
+
+ We cannot use File.exists? to check whether a symlink exists.
+
+ Use File.symlink? instead.
+
+commit e7eb6a8e1e1310c08220db0ed7979ec4c721fccb
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Nov 27 19:34:48 2007 +0100
+
+ Moved the after(:each) block before the specs, so the block is actually run.
+
+commit 4c284abb32029029ab7002147ef544493c7070f6
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Nov 27 19:00:30 2007 +0100
+
+ Added a missing Errno.handle to File.readlink.
+
+commit f163ca7c5e4a03d698a881853d1e0fab8a5be1a4
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Nov 27 18:42:08 2007 +0100
+
+ readlink() only works with symbolic links.
+
+ This makes the spec pass on MRI.
+
+commit ca1cb21b5f694b3850a838f88d3ac5ded7de3e1f
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Nov 27 18:40:43 2007 +0100
+
+ Naming convention fixes.
+
+commit cbf351cb59152a5528f6c6105cee96c67f7f6fcd
+Merge: f70d531... 5452983...
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Mon Nov 26 19:48:21 2007 +1100
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit f70d5314fcc75ef2e32fbd484de58bd5f7ed6cbc
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Sat Nov 24 17:29:29 2007 +1100
+
+ Implemented File::symlink and spec.
+
+ Kudos to the Melbourne Railscamp :)
+
+commit c4a6a804185c18a182206afc1b8d5209d208077e
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Sat Nov 24 01:08:41 2007 +1100
+
+ Removed trailing whitespace.
+
+commit 9b9820e512f56b2c23c760887251d72c187aa297
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Sat Nov 24 00:50:11 2007 +1100
+
+ "Added File::readlink spec"
+
+commit 2dd272afe315dae0ad0b9bd49b6dfa9e98e50b1c
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Fri Nov 23 17:11:54 2007 +1100
+
+ Spec-ed implementation differences on masgn RHS eval order
+
+ Rubinius is (for now) deliberately non-compliant wrt eval
+ order of RHS expressions in an masgn.
+ * MRI, JRuby eval left-to-right
+ * Rubinius evals right-to-left
+
+commit 361a1adcee182cf069352effd0949064b621bddc
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Fri Nov 23 16:16:08 2007 +1100
+
+ Additional parallel assignment specs - use of to_ary
+
+ * Added spec for when to_ary should be called on the RHS of an masgn
+ * Added additional example of a complex masgn (from JRuby tests)
+
+commit 97cb3f5758f102cf8a07262c4c9bef4b22ca88b7
+Author: Victor Hugo Borja <vic.borja@gmail.com>
+Date: Thu Nov 22 22:04:51 2007 -0600
+
+ Added specs for metaclasses of true/false/nil on metaclass_spec as suggested by rue.
+
+commit d4f9eb7cd5fb17e3e8ce52db39e95a96362d3ad0
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Nov 22 13:05:10 2007 -0800
+
+ Fixed wording of Rubinius extension Bignum domain specs.
+
+commit 50e1f80ef54d25aaa69d52a3d422547593836ac6
+Author: Jeremy Durham <jeremydurham@gmail.com>
+Date: Thu Nov 22 17:18:54 2007 -0500
+
+ Added excludes for Kernel#open and Thread#abort_on_exception
+
+ * Excludes Kernel#open raise specs
+ * Excludes Thread#abort_on_exception specs
+
+commit 25607d4d884b4597bc69560e9390cd9dc1f4e44d
+Author: Victor Hugo Borja <vic.borja@gmail.com>
+Date: Thu Nov 22 13:47:50 2007 -0600
+
+ Specs for Module#alias_method
+
+commit c207618ad4113501aa5df4adb5d5aa3a60f5b9ff
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Nov 22 10:59:34 2007 -0800
+
+ Use RUBY_ENGINE first, then pull in rbconfig
+
+commit ed5a46e13b35d6ad48cce1d3eed96c2f78ace049
+Author: Jeremy Durham <jeremydurham@gmail.com>
+Date: Thu Nov 22 11:42:36 2007 -0500
+
+ Added basic specs for abort_on_exception
+
+ * Added specs for Thread#abort_on_exception ($DEBUG on and off)
+ * Added specs for Thread#abort_on_exception=
+
+commit 05ecef9162ba2c4a0da90c966a20a4f45c353d93
+Author: Jeremy Durham <jeremydurham@gmail.com>
+Date: Thu Nov 22 08:39:53 2007 -0500
+
+ Added specs for when parameters are missing or invalid parameters are given
+
+commit 25d2c940d561dcac2c06df747762a229dddfbed1
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Thu Nov 22 15:33:17 2007 +1100
+
+ Another parallel assignment spec - rhs should evaluate l->r
+
+commit 8e4f8de446b842c13ad45a8e0e2c3c1ebf30bddb
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Nov 21 17:42:17 2007 -0800
+
+ Stop-gap prevention for Kernel#callcc hanging CI specs.
+
+commit a9d7163e4d9e8d4fb79c9769691b232676a44bd8
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Wed Nov 21 15:50:39 2007 -0600
+
+ remove compliant(MRI) from callcc specs
+
+commit 812e922c8e7cda728d6b7f32933b75eb009eef11
+Merge: 2e221b9... f24bb1f...
+Author: Nathan Witmer <nwitmer@gmail.com>
+Date: Wed Nov 21 12:32:18 2007 -0700
+
+ Merge branch 'callcc_spec'
+
+commit f24bb1ffdf941df78098da262a62e881653b1a99
+Author: Nathan Witmer <nwitmer@gmail.com>
+Date: Wed Nov 21 12:31:28 2007 -0700
+
+ Added scope-related callcc specs, compliant(:ruby) only.
+
+commit 2e221b9f1d7ffa41431e5bd51fdd36434e7f838f
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Wed Nov 21 14:41:43 2007 +0100
+
+ Spec and fix some more String#slice bugs when given nil, also use Undefined.
+
+commit db338d9d8705fd668a5639d483ff47908aa014ca
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Tue Nov 6 16:53:13 2007 +0100
+
+ Fix String#rindex when given nil as offset.
+
+commit 6eab3b692a50c1a37cc39c21d743de1488402f64
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Nov 21 00:00:24 2007 -0800
+
+ Added MRI stdlib Fcntl to lib/ext with build script.
+
+ Added lib/fcntl to load extension. This may need a better solution.
+ Added INT2FIX to subtend.
+
+commit 5268c0b29c1fb07a911fe601e30b21ffe04f7e81
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Wed Nov 21 17:02:14 2007 +1100
+
+ Additional specs for parallel assignment
+
+ MRI allows parallel assignment to:
+ - assign via object.method=
+ - assign via []=
+ - use a lhs arg as an arg to another lhs assignment
+
+ All three scenarios currently fail in Rubinius, apparently due to
+ miscalculating the number of args to an assignment method under
+ parallel assignment.
+
+commit 462f68b95a70c24e41cad5a40969c4651c7de181
+Author: Jeremy Durham <jeremydurham@gmail.com>
+Date: Tue Nov 20 02:12:44 2007 -0500
+
+ Added specs for Kernel#open when block is given
+
+commit ab9e40600fd2522d4abce86f7b8bdc632f6e9018
+Author: Jeremy Durham <jeremydurham@gmail.com>
+Date: Mon Nov 19 23:42:55 2007 -0500
+
+ Added very basic specs for Kernel#open
+
+commit b47efc9f9872ecca68a06f6864f39617e06762b0
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Nov 19 00:24:15 2007 -0800
+
+ Updated CI excludes. Runs clean on Leopard.
+
+commit 3ff04e52bc9cb03439567ddb9b3b63b3034b30c3
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Mon Nov 19 00:03:58 2007 -0600
+
+ more specs for Kernel.callcc, ensures callcc return value semantics
+
+commit 06d5312c51b09faef87d2deb53f3c472eaa94100
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Sun Nov 18 17:49:44 2007 -0600
+
+ basic callcc behavioral specs
+
+commit 53433f0e9ddba2eac876f7a1fb0f9d292ee37286
+Author: Nathan Witmer <nwitmer@gmail.com>
+Date: Fri Nov 16 16:51:18 2007 -0700
+
+ Added Kernel#callcc spec and fix for LocalJumpError with no block given
+
+commit d324779b8b5c8dd84438c08ec4f2b2574282f93e
+Author: Victor Hugo Borja <vic.borja@gmail.com>
+Date: Fri Nov 16 15:17:21 2007 -0600
+
+ Added Module.nesting
+
+ Some specs are failing on rubinius because the parent
+ field is not being properly set.
+
+commit cff726c9dc3631b2e0ddc3e12bd3af532f7e1ef4
+Author: Victor Hugo Borja <vic.borja@gmail.com>
+Date: Fri Nov 16 14:49:38 2007 -0600
+
+ Added spec for calling Module.nesting on root level
+
+commit 05adb6070889d7021a1e53ab82b855c3554d4f5c
+Author: Victor Hugo Borja <vic.borja@gmail.com>
+Date: Fri Nov 16 14:45:23 2007 -0600
+
+ Fixed specs for Module#constants
+
+commit 1f1c857e1d8c37213a91daaad3fc3bfcbf2bef61
+Author: Victor Hugo Borja <vic.borja@gmail.com>
+Date: Fri Nov 16 14:43:55 2007 -0600
+
+ Fixed spec description for calling Module.nesting from methods.
+
+commit 242c947c6e4d007685e8aa0c44ac505c7dab4239
+Author: Victor Hugo Borja <vic.borja@gmail.com>
+Date: Fri Nov 16 14:38:32 2007 -0600
+
+ More specs for Module.nesting
+
+commit 3560fd7ef0d5a65a9cb055d87fa7103ed3bdb029
+Author: Victor Hugo Borja <vic.borja@gmail.com>
+Date: Fri Nov 16 10:24:45 2007 -0600
+
+ private keyword specs reflecting problem described on ticket #133
+
+commit fd31e1e592237832bd5e605f604d15385df0615a
+Author: Victor Hugo Borja <vic.borja@gmail.com>
+Date: Fri Nov 16 04:15:27 2007 -0600
+
+ specs for Kernel#block_given? by Francisco Laguna
+
+commit 87ebce4cf2430198578decdb4c7dc1003db37f8e
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Nov 15 22:07:39 2007 -0800
+
+ Ticket #121 by Jeremy Durham -- File modes
+
+commit 4a67e0ade233aaaa3a2ff17161b298872f8a5f83
+Author: Victor Hugo Borja <vic.borja@gmail.com>
+Date: Thu Nov 15 14:20:19 2007 -0600
+
+ Splitted enumerable_spec.rb into a file per method.
+
+ Added some specs by Francisco Laguna.
+
+commit 0b933650330f57e7db1bf8574d0b7eecf0635996
+Author: Bryan Helmkamp <bryan@brynary.com>
+Date: Tue Nov 13 11:34:20 2007 -0500
+
+ Added specs for File.mtime.
+
+commit 42a7de27c1a6082fee7b9baaf05b9394ffe90ddd
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Mon Nov 12 19:57:38 2007 +0100
+
+ Updated CI excludes for File#atime and File.new.
+
+commit 3d106d6a9b8ce0b34e7b6f9426da51b83fe5f676
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Mon Nov 12 19:54:43 2007 +0100
+
+ Added File#path.
+
+commit 247da25a0120d468fe9f189a6235962f9658b65e
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Mon Nov 12 19:23:09 2007 +0100
+
+ File now deals with numeric modes and accepts a permission argument, too.
+
+commit 087deaed0dcf4ae2c8dc713eeccfed9a0ebabe6f
+Merge: 4355e96... 1c9d213...
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Sun Nov 11 23:15:01 2007 -0800
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit 4355e96b05de4d4d086dfa86b8fe19bdcecfbe82
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Sun Nov 11 23:14:16 2007 -0800
+
+ -a
+
+commit 7e975d1aca38a3bfe07fda431aeaba376bce19c1
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Sun Nov 11 23:09:47 2007 -0800
+
+ Get rid of irrelevant specs
+
+commit 1c9d2133fc294964ce08e9a7020083c379f74ca0
+Author: David Waite <david@alkaline-solutions.com>
+Date: Sun Nov 11 23:46:08 2007 -0700
+
+ Remove temp directories within mkdir spec on exception.
+
+commit 2110fc75dc6a7ab521249f259bc6fdc78d565b11
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Sun Nov 11 22:36:08 2007 -0800
+
+ Update CI Excludes
+
+ * Expected failure of "raise an Exception if it has
+ the wrong number of argments" due to dispatcher bug
+
+commit 0f2a183a46ba085d9c99ed4767cd18c0482e6d45
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Sun Nov 11 22:14:07 2007 -0800
+
+ Implement File#atime
+
+commit 671b340f340ab6b8d9c13b27d52a782ce3268b2a
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Sun Nov 11 21:24:34 2007 -0800
+
+ Update CI excludes
+
+commit f7ba96f6b41de9a3696a03e9efe25c8b037a4f07
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Sat Nov 10 11:24:12 2007 -0800
+
+ Adds spec for File.open
+
+ * In resonse to Lighthouse Ticket #102
+ "File.open should throw Errno::EACCES opening non-permitted file"
+ * Passes MRI, doesn't yet pass RBX
+
+commit 719329b3f5179766e23a27e427cd0c0846c85ffa
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Mon Nov 12 10:38:42 2007 +1100
+
+ Added IO#to_i implementation and spec.
+
+commit cc100fc08be101ecdf0daba1966977fb8e39fa6e
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Mon Nov 12 10:30:25 2007 +1100
+
+ Added IO#fileno implementation and spec.
+
+commit d036f5c16a4836d638be83108be35df532d9221a
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sun Nov 11 23:17:12 2007 +0100
+
+ Made SystemStackError subclass of StandardError.
+
+commit bf1c3dc3e463aeaf4e0cee1cbd46b15e7693a395
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sun Nov 11 20:36:25 2007 +0100
+
+ Removed an old Method#arity spec exclude.
+
+commit 72c3495f3513e54c2488292bcdaca9208b6f0339
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Nov 11 10:44:53 2007 -0800
+
+ Removed transient dirs from Dir specs.
+
+commit 11f0ed51b4bfb3bea2b544a82b3158fd3daf2ad8
+Author: Victor Hugo Borja <vic.borja@gmail.com>
+Date: Fri Nov 2 03:02:28 2007 -0600
+
+ Specs for Module#remove_const
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit e8158f14f0f02e3b0cdcb4182e1277928324cc0c
+Author: Nathan Witmer <nwitmer@gmail.com>
+Date: Sun Nov 11 08:19:10 2007 -0700
+
+ Fixes for UDPSocket spec
+
+ * Renamed the description to match what was actually being tested
+ * Uncommented the code and wrapped it in an "it" block, to prevent
+ conflicts/hangs with bin/completeness runs.
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit 9b973a98d2ebddacd50f0fcb58903bb53bdff3f5
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sun Nov 11 13:30:22 2007 +0100
+
+ Ticket #98: Dir includes Enumerable now.
+
+commit 8d957f186cd4d2c5e6b236de4a0878d38b464848
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sun Nov 11 13:26:00 2007 +0100
+
+ Implemented Module#included_modules.
+
+commit 0021b24ba490fe01f96ef17957328feeedfc4c29
+Author: Nathan Witmer <nwitmer@gmail.com>
+Date: Thu Nov 8 21:42:41 2007 -0700
+
+ Commented out code in UDPSocket spec so bin/completeness doesn't hang
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit 56687aed201fb864587807cca893268a9f1e2050
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Nov 11 00:10:14 2007 -0800
+
+ Method fixture for yield specs.
+
+commit 421aa58f9135807487864adcdcac79f7b6da33c1
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Nov 11 00:08:35 2007 -0800
+
+ Specs for yield keyword based on patch in #114.
+
+commit 5ba0b2030c55474f9d8a096d309678ca24a4699b
+Author: Jeremy Durham <jeremydurham@gmail.com>
+Date: Sun Nov 11 01:35:08 2007 +0100
+
+ Ticket #105: Implemented Bignum#eql?.
+
+commit 47356fe39033f8571559a4fef933681fda871efd
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sun Nov 11 01:14:26 2007 +0100
+
+ Made specs for Kernel#respond_to? and Kernel#method pass.
+
+ KernelSpecs::Foo#baz is defined in another spec, so these two specs
+ may not rely on #baz being undefined. This is a horrible workaround
+ for the problem that multiple specs make use of the same module and modify
+ it freely.
+
+commit 68b4fc7c0192f537fe9727927ac35c440dbdc03a
+Author: Akshay Rawat <akshay.rawat@gmail.com>
+Date: Thu Nov 8 18:21:03 2007 +0530
+
+ Updated CI excludes.
+
+commit a1eee3814a5d054cd00e26b40c063d41880bf6c7
+Author: Chris Pettitt <cpettitt@gmail.com>
+Date: Sat Nov 10 14:10:47 2007 -0800
+
+ Refactor IO.gets spec to have less duplication.
+
+commit 56497d27bdb3a82d549f89b9fc9fcf0709f99b3e
+Author: Chris Pettitt <cpettitt@gmail.com>
+Date: Sat Nov 10 14:07:21 2007 -0800
+
+ New spec: IO.gets('') should advance the file position to the next non $/ character.
+
+commit 95158f5a4141d5d3e2893304e49bfeb62cc7b226
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Nov 10 13:35:03 2007 -0800
+
+ Added rescue to prevent meltdown until rbx begin/rescue/ensure is fixed.
+
+commit a39155cb029ca3c1e5e5d69e0e269c685c040f6e
+Author: Chen Yufei <cyfdecyf@gmail.com>
+Date: Sat Nov 10 12:24:38 2007 +0000
+
+ Fixed IO#gets when separator is empty.
+
+commit f9c31ce1d2a68c15def98aad6c6ff35eb56cd523
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Fri Nov 9 20:24:40 2007 -0800
+
+ Clean up Enumerable#include? specs
+
+commit f017fad69be5d4034a4c5437acf77ec4749b0d75
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Sun Nov 4 20:54:06 2007 -0500
+
+ Clean up Enumerable#(collect, entries, find, find_all) specs
+
+commit 81550f082396b4455c3681ae966be1371be0a5db
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Sun Nov 4 13:18:52 2007 -0500
+
+ Update excludes
+
+commit d5fd2ee893ea608c7e19cb674a4da7b9f49542e6
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Sun Nov 4 12:16:58 2007 -0500
+
+ Cleanup/rewrite Enumerable#find tests for sanity and clarity
+
+commit 39f21aa76f6ddc45be79e4e4e978b4c1c2beed71
+Merge: 17d2e4c... c1b9f74...
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Fri Nov 9 20:25:13 2007 -0800
+
+ Merge branch 'master' of http://git.rubini.us/code
+
+commit c1b9f74f88be963e72de763da9130f46869d89fb
+Author: Chris Pettitt <cpettitt@gmail.com>
+Date: Fri Nov 9 12:36:04 2007 -0800
+
+ Fix some failing specs for IO#each and IO#each_line.
+
+ Also refactor some common code into a helper method.
+
+commit 74af37b849507e504503359a08245effaad7634a
+Author: Chris Pettitt <cpettitt@gmail.com>
+Date: Fri Nov 9 10:58:33 2007 -0800
+
+ New specs for IO#each and IO#each_line
+
+ This change adds some new specs for IO#each and IO#each_line factored into
+ a shared .rb, because one is the alias of the other. Added failing specs to
+ excludes.
+
+commit d162a396b566846445328d6c42d3d5f10fcf7ee6
+Author: Matt Pelletier <matt@eastmedia.com>
+Date: Fri Nov 9 02:32:25 2007 -0500
+
+ Add and refactor patches from Andrea OK regarding #send
+
+commit 63f0ed010e65549597f6bddb0686ba04157ca478
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Nov 7 09:52:25 2007 -0800
+
+ Fixed Method#call spec failing from renamed fixture method name.
+
+commit 1ef46468d7808c52b07388130069cb2e7854bff8
+Author: Matt Pelletier <matt@eastmedia.com>
+Date: Wed Nov 7 04:48:39 2007 -0500
+
+ * Update CompiledMethod#arity to be accurate for cases of required and/or optional arguments, with or without blocks
+ * CompiledMethod#arity is still inaccurate when splat argument is present (the presence of splat overrides #required)
+ * Add specs for more thorough coverage of various argument use cases
+ * Includes known Rubinius-failing specs for splat-related arity
+
+commit b7726f26dae95936aa1c3fdf2c52dd18ef7413cf
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Nov 5 17:48:51 2007 -0800
+
+ Updated CI excludes for fixes to public|private_class_method.
+
+commit eb18f898e3ae8e5a1bf3b01291a12516c6a22301
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Nov 5 17:18:22 2007 -0800
+
+ Added Module#protected_method_defined?. Updated CI excludes.
+
+commit aa8904cdbd8b4851be4f05cec3000b04cfc9f6c1
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Nov 5 17:14:12 2007 -0800
+
+ Added Module#private_method_defined?.
+
+ Fixed specs for Module#public_method_defined? and
+ private_method_defined?. Updated CI excludes.
+
+commit 063b61759ee86f5def2422d16f1eb854c8b9eb76
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Nov 5 13:39:21 2007 -0800
+
+ Updated CI excludes for StringIO.
+
+commit 3c79f871379d2d4b5431138033f723efbf4a795d
+Author: Dr Nic <drnicwilliams@gmail.com>
+Date: Sun Nov 4 16:45:30 2007 -0500
+
+ Extended StringIO spec "flattens a nested array before writing it" to ensure deeper test scenario
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit 916d617a60cf83ac26c3090310236193f5842ff6
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Nov 5 13:11:54 2007 -0800
+
+ Updated CI excludes for String#scan.
+
+commit fee1d904197c369c561ae3b11aaf582f1b87d1b0
+Author: Matt Pelletier <matt@eastmedia.com>
+Date: Sat Nov 3 16:06:16 2007 -0400
+
+ Fix test of String#scan. Do not force matches into array using splat.
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit cc9182cfcde60a63bf566f73c6004b7e46347e77
+Author: Daniel Lucraft <dan@fluentradical.com>
+Date: Thu Nov 1 17:01:03 2007 +0000
+
+ Added File.rename
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit fc2b7aa65ab338d8ff543552659046c93659c3ce
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Nov 5 12:06:13 2007 -0800
+
+ Commit rework of Carl Drinkwater's patch from #72.
+
+commit 7f6564b96762d7b3deb9f021789182f5c664a766
+Author: Chris Pettitt <cpettitt@gmail.com>
+Date: Sun Nov 4 10:38:04 2007 -0800
+
+ Fixes for two IO#gets spec failures.
+
+ This patch fixes the following two IO#gets spec failures:
+
+ IO#gets assigns the returned line to $_
+ IO#gets returns the entire content if the separator is nil
+
+ Signed-off-by: Brian Ford <brixen@gmail.com>
+
+commit 705e8e05496167b1af3a1e3ff3446d325ca54e07
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Nov 5 02:10:56 2007 -0800
+
+ Added Module#public_method_defined?.
+
+ Updated CI specs for #public_method_defined?.
+ Small fix to find_method_in_hierarchy to symbolize arg.
+ Updated some spec wording and removed spec'ing exception string.
+
+commit 17d2e4c6ae0c40376fe121786a362c8bc8ce951c
+Merge: 2b77ee8... c07472c...
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Sun Nov 4 20:58:42 2007 -0500
+
+ Merge branch 'master' of http://git.rubini.us/code
+
+commit bd6c27f4724bdc461a7036e6373a0ad23060020a
+Author: Trotter Cashion <cashion@gmail.com>
+Date: Sat Nov 3 15:57:21 2007 -0400
+
+ Added operator precedence specs for '&&' and 'and'.
+
+commit 2b77ee8b74373a3251973d96c931422909605e29
+Merge: 30d7618... 76aa72e...
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Sun Nov 4 13:43:23 2007 -0500
+
+ Merge branch 'master' of http://git.rubini.us/code
+
+commit 2c90ce28cc73e08d9fb74b5c7e815807314ba269
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Nov 4 07:18:09 2007 -0800
+
+ Updated CI excludes from 85d63b676e.
+
+commit 85d63b676e463a2bec9a322bc8eeffd2daee433b
+Author: Chen Yufei <cyfdecyf@gmail.com>
+Date: Sat Nov 3 23:39:23 2007 +0000
+
+ Added specs for IO#gets
+
+commit be5b9595f2077080c0c1179ab9689352d8faea3a
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Nov 4 00:31:12 2007 -0700
+
+ Updated CI excludes.
+
+commit d46ad4b63d4a5f77609b0880b7f24e8e27404805
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Nov 4 00:11:52 2007 -0700
+
+ Updated CI excludes.
+
+commit 1f307223c673c6744f8b85fc3e707a3419b1a0e8
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Nov 4 00:06:48 2007 -0700
+
+ Guarded #freeze specs for MRI and JRuby.
+
+commit ca50fd7d979c36f8af306e0e1474aac5408dd66d
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Nov 3 23:22:31 2007 -0700
+
+ Guarded specs for #frozen? for MRI and JRuby.
+
+commit 16b36030a796b877809d5d6ea556266c4b4a6413
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Nov 3 20:53:26 2007 -0700
+
+ Removed NULL characters from language/precedence.
+
+ Enhanced rescue output when loading spec files.
+
+commit 10510ece16ebb5e0ba921e0be631a4740f3e4453
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Sat Nov 3 23:09:30 2007 -0400
+
+ Fix a method_missing cache error.
+
+commit b313c5632b039c03a448ae3b1046701c8b3243a2
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Nov 3 14:10:29 2007 -0700
+
+ Changed shared spec behavior to be compatible with RSpec.
+
+commit 7b825b89e96b3c8e38f9b8bcc8edf2bc6ec6ff22
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Nov 3 14:09:47 2007 -0700
+
+ Fixed language/class specs.
+
+commit 30d76181b0b3a9c5ac99c9d0e22a6a451346eff4
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Sat Nov 3 16:47:49 2007 -0400
+
+ Fix Dir#each/Dir#entries/Dir.foreach specs. They weren't updated for fixtures
+
+commit f44a8cceb9a186a7127276db2207dfc79957ee8d
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Nov 3 13:06:35 2007 -0700
+
+ Guard File.(un)link for jruby.
+
+commit 1ec2c3a99ca562c8944aac1f4a60f8e0af0aaf17
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sat Nov 3 18:20:48 2007 +0100
+
+ Properly resize the array in Array#<<.
+
+commit 8dec9918d8a6233ec2cde29d54687a5d950dc8df
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sat Nov 3 17:13:57 2007 +0100
+
+ Fixed Array#unshift for the case when @start > 0 && @start < values.size.
+
+ Also extended the Array#unshift specs to cover this case.
+
+commit 50b90918cd5a9a05e475690703c7867b443d191b
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Fri Nov 2 13:47:53 2007 -0500
+
+ added IO::foreach, fixed gets to use string separator, and fixed IO::readlines to use File.open
+
+commit 3efc01e110473d003ffb0a1376ec179f30e600de
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Fri Nov 2 13:30:44 2007 -0500
+
+ specs for IO::foreach and specs to test IO::readlines,IO.readlines with string separator
+
+commit fad18610b4416dfcfaf35db5029e880dff7e9820
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Mon Oct 29 14:10:11 2007 -0500
+
+ basic exec implementation and a single basic spec (not sure how test test exec)
+
+commit 730fc3ed9afc54612d14093148fb8583c9e39fe3
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Fri Nov 2 21:41:25 2007 +0100
+
+ Return mkdir()'s return value from Dir.mkdir.
+
+commit f5766696e701a069f908b3b5d5cfbccfee15ef1f
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Fri Nov 2 20:46:11 2007 +0100
+
+ Implemented Dir.foreach.
+
+commit c696f1edc50c58b87270811c0c9aa0e49b356fe7
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Fri Nov 2 20:38:36 2007 +0100
+
+ Implemented Dir.entries.
+
+commit e8e6188b252172690c1b584e528e3c71035897cd
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Fri Nov 2 18:16:13 2007 +0100
+
+ Raise an error if the opendir() call in Dir#initialize fails.
+
+commit 024309b560c6c69f6f331c614df1da221be7054c
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Fri Nov 2 16:15:19 2007 +0100
+
+ The Array#[]= spec seems to work now, so enable it.
+
+commit 76f118e62a0784326f5edf1c0fe46f6b6e682eee
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Fri Nov 2 14:28:49 2007 +0100
+
+ Made Math.ldexp only accept integers as the second argument.
+
+commit 22bd7369efd1f738835e9c0a6a4624a26dae02d1
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Fri Nov 2 13:30:50 2007 +0100
+
+ Implemented some missing File::Stat methods.
+
+commit d6dc42d9085fed5f8bf482d7f84dd9c5fbd4423c
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Nov 1 12:24:53 2007 -0700
+
+ Fixed specs failing MRI for File.stat and File.basename.
+
+commit 46f4de189e987f3071ede57f2bb1f7c892d67bd4
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Nov 1 17:30:47 2007 +0100
+
+ Fixed ticket #83: Array#push doesn't die anymore after calling Array#shift.
+
+commit 8debed24e957e48b10d60885d9a43083aab4d923
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Nov 1 11:14:22 2007 +0100
+
+ In the Numeric#coerce spec, coercing strings to numerics should work.
+
+ We can remove the TypeError checks from this spec, since those
+ are included in the specs for the Numeric operators.
+
+commit 28cf656fb25ce38453acb2efdcf2e9ac16bb4460
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Nov 1 01:33:31 2007 -0700
+
+ Removed Hash#fetch definition from fetch spec.
+
+ Fixed spelling of Hash#find_unambiguous.
+ Updated CI excludes.
+
+commit a6a69b469d94d0912ccbf123fdb9f53cbaf32830
+Author: Akshay Rawat <akshay.rawat@gmail.com>
+Date: Tue Oct 30 21:47:51 2007 +0530
+
+ In the Time#isdst spec, don't depend on the system's current time zone.
+
+commit 1be129f98e0d548a023cc32f5ab763361e2a9c6b
+Author: Daniel Lucraft <dan@fluentradical.com>
+Date: Wed Oct 31 21:05:04 2007 +0000
+
+ Fixed String#split to not return non-matching captures anymore.
+
+commit 5f7f798ef26fc8ee1e83c5e392c1fb2e60e31382
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Wed Oct 31 21:54:56 2007 +0100
+
+ In the Numeric#coerce spec, don't try to coerce strings to numerics.
+
+commit e26b7645af27d5bfc250c2c11f7e72349750f5c7
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Wed Oct 31 10:52:13 2007 +0100
+
+ Added a failing spec for Ticket #83.
+
+commit 9144f0c55eb5f40409bec514f08f89bdba61f800
+Author: Daniel Lucraft <dan@fluentradical.com>
+Date: Wed Oct 31 13:57:54 2007 +0100
+
+ Fixed math/constants_spec.rb.
+
+commit 4b521cacb667ca5245954bc03ebfec67c0ac235c
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Wed Oct 31 23:26:45 2007 +1100
+
+ Modified spec to reflect expected differences in masgn retval behavior
+ between rbx (true) and MRI (array of rhs vals).
+
+commit 1f6c50f5c77566e66cb0b842733b7f4f4b24e937
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Oct 30 18:13:47 2007 +0100
+
+ Use a fixed timezone for the Time#strftime spec.
+
+commit 60a25e997def085f3ae29773ce70ddc5b7c38d46
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Oct 30 00:47:38 2007 -0700
+
+ Fixed Kernel#raise to not output if $VERBOSE == nil.
+
+ Guard Marshal.dump specs to eliminate error output until
+ a proper Marshal is implemented.
+
+commit e446f2e329a6dfaacb45b5b86ba43ebd9ec606a3
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Oct 29 23:17:49 2007 -0700
+
+ Added IO::SEEK_SET, SEEK_CUR, SEEK_END with FFI.
+
+ Fixed IO#close to raise IOError if already closed.
+
+commit fcb1ac4d076c07065878c2e65bf7bb44ddef400c
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Oct 29 22:37:23 2007 -0700
+
+ Specs for IO#seek and IO::SEEK_SET, SEEK_CUR, SEEK_END.
+
+commit a3570f6702dabd303fcd10d4cfc0e753cff69bb5
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Mon Oct 29 22:33:34 2007 +0100
+
+ Make Module.new actually work.
+
+commit 9709fa96b91afe5140f76726f1f7d4b89f8a6d54
+Author: Brian Donovan <brian.donovan@gmail.com>
+Date: Mon Oct 29 11:30:32 2007 -0700
+
+ Ticket #75: Fixed Enumerable#sort_by.
+
+ We must not call the comparison proc when the object and pivot are
+ identical.
+
+commit 52e97da6bbcd28ec4349abcf25b089648d085652
+Author: Akshay Rawat <akshay.rawat@gmail.com>
+Date: Tue Oct 30 02:18:56 2007 +0530
+
+ Enabled Math specs that were fixed by Ticket #59.
+
+commit 0f98800d4ab1db526304f1d26597ca3880c811da
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Mon Oct 29 19:29:16 2007 +0100
+
+ Ticket #59: Kernel#send now calls private methods, too.
+
+commit 754e48c223c3464c7d048452585c07b8d0b3d8c7
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Oct 29 00:11:41 2007 -0700
+
+ Specs for IO#rewind.
+
+commit 0bfd6bcca8fb287899fadeae81dd7c00b05d07e9
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sun Oct 28 23:40:37 2007 +0100
+
+ Module#public_instance_methods now handles attribute accessor methods.
+
+commit 0a22b36b9bc50a34f1da1d0e994f1a6689195652
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sun Oct 28 23:26:17 2007 +0100
+
+ Added a failing spec for Enumerable#sort_by.
+
+commit 006534b173a186c25c228b653d5ac9d81b20f57e
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Oct 28 21:51:40 2007 +0100
+
+ Update Module spec excludes.
+
+commit 496df827eeeb600858fa8c7b26482aa3f653fee1
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Oct 28 21:50:47 2007 +0100
+
+ Make Module#ancestors specs pass.
+
+commit 18185cde2b47374c304e2528a084ea1f7b5178d2
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sun Oct 28 19:46:22 2007 +0100
+
+ Added a failing spec for building an array that includes a splatted array.
+
+commit f120a470a5e07a7e53b1e006173942d58956e86b
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Oct 28 19:33:10 2007 +0100
+
+ Extended Kernel#` specs.
+
+commit 7e184c5bbc7be16cc8f7be01713543f222edd267
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Oct 28 19:00:52 2007 +0100
+
+ Removed Exception message dependencies and extended Kernel.String specs.
+
+commit 2853dc58209b3b8d122cee66c7d83e967d0879de
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Sun Oct 28 23:01:54 2007 +1100
+
+ Fix for multi-arg operator assign through [], e.g. x[0,3] += 5
+
+commit 91b88710ddc1e8553e51c404cee4039f4d6abf24
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Oct 28 13:05:08 2007 +0100
+
+ Fixed File.ftype specs.
+
+commit 448fdc2def3a9ab249dadf9335568ca30b76f70e
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Oct 28 00:36:11 2007 -0700
+
+ Updated compiler specs.
+
+commit 0fb510c14ee8787b5965d5665e50da92a988faa6
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Oct 28 00:32:41 2007 -0700
+
+ Fixed Bignum#coerce specs.
+
+commit 5e52a259b91e81fe5497f44107dee6ffd613b3be
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Oct 28 00:22:40 2007 -0700
+
+ Fixed wording on Rubinius Bignum#coerce extension specs.
+
+commit 19e0259ca51691afe341b1217ab92862b307fe17
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Oct 28 00:15:18 2007 -0700
+
+ Revert "Remove invalid and failing Bignum#coerce spec."
+
+ This reverts commit 2371b920ca3f956213ab9e406a3b5d2afab4f18e.
+
+commit 4986ec283ee5aa9e392065c74d64952d36554b91
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Oct 27 23:50:05 2007 -0700
+
+ Updated CI excludes for Array#pack.
+
+commit 5472f30201d7cddd4465adb246fa32927fe03d91
+Author: Alan Hurdle <alan.hurdle@gmail.com>
+Date: Sat Oct 27 18:38:30 2007 +1000
+
+ Bunch of fixes to Array#pack to pass the current set of pack specs
+
+commit 20210a617a3f31c5dc0eda9fa371c49200c11f67
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Oct 28 02:30:02 2007 +0100
+
+ Updated spec excludes for Module specs.
+
+commit 712e3cc6a5d8a69834449e1039dfae3e07fcdcc2
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Oct 28 02:18:00 2007 +0100
+
+ Removed the dependency on some Exception messages.
+
+commit 2371b920ca3f956213ab9e406a3b5d2afab4f18e
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Oct 27 16:15:33 2007 +0200
+
+ Remove invalid and failing Bignum#coerce spec.
+
+commit e1e62e7749d47c838d6b7cd1e95863c0c90d3de0
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sat Oct 27 20:59:49 2007 +0200
+
+ Fixnum#div now always rounds towards negative infinity.
+
+commit 9a4ccbe8381db5b6280c9d1dfcf6fa21a4838c4e
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Oct 27 15:09:57 2007 +0200
+
+ Updated Spec excludes for Bignum and Fixnum specs.
+
+commit 889c939a668b9b1a4fd8f5a0cfd8bad85c3a5977
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Oct 27 14:24:27 2007 +0200
+
+ Fix Integer#[] when given a Bignum.
+
+commit 45d97332f4ec5a174884024c954ceeb6eb852f5f
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Oct 27 13:46:51 2007 +0200
+
+ Partial fix for #68: Fix Hash#fetch to correctly handle yielding with a default value passed.
+
+commit a785ea28f39c71a98007a7fafc23985dd21b596f
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sat Oct 27 13:39:42 2007 +0200
+
+ Updated CI excludes for recent Hash fixes.
+
+commit 6dd909fede466ed813ec7c5d207c5deeb69c9eb7
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Oct 27 11:33:22 2007 +0200
+
+ Fix for #67: Enumerable#sort should not depend on #size.
+
+commit 720489aa52bfabd492c307330204772b5eba6755
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Oct 26 00:21:37 2007 -0700
+
+ Updated CI excludes for spec/language.
+
+commit 41e8a07252b2df9c1e858922195a72f9a40c882c
+Author: Akshay Rawat <akshay.rawat@gmail.com>
+Date: Fri Oct 26 02:37:18 2007 +0530
+
+ private keyword should mark a Module method private
+
+commit a201e631cbaabcc5964cfa3eb28a9fa8be1bf347
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Oct 26 00:11:01 2007 -0700
+
+ Added spec/language into CI specs.
+
+ Updated CI excludes for spec/language.
+
+commit 833fe76de0c0b900ef5255b2abecd19943404c0c
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Oct 25 23:53:35 2007 -0700
+
+ Updated compiler specs and CI excludes.
+
+commit f31e5af358d2b5c3ff4afd3819b3a3e571427f8e
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Oct 25 23:28:13 2007 -0700
+
+ Migrated Adam Gardiner's compiler patch 236d213de8 to stable.
+
+ Updated CI excludes for language/variables specs.
+
+commit bd9e47b1b7624df5e2ae0f31a7fd53c787ecc7e4
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Oct 25 22:50:22 2007 -0700
+
+ Fixed language/variables spec to use fixture class.
+
+ Added CI exclude file for language/variables specs.
+
+commit 688f03ac452f698105812c28c29dcc7162b7037c
+Author: Adam Gardiner <adam.b.gardiner@gmail.com>
+Date: Thu Oct 25 22:43:20 2007 +1000
+
+ Added specs for operator assignment, i.e. +=, *=, ||= etc
+
+commit 982dfee01bedb55e8dbf62d279bc4a375e58ec50
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Oct 25 16:53:45 2007 +0200
+
+ Don't hardcode the result of Hash#to_a in the Hash#shift spec.
+
+commit 4bf7c8d2d387c002004da5df9f9c2a06fb65e61e
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Oct 25 01:42:32 2007 -0700
+
+ Commit tilman's language block spec additions with some modifications.
+
+commit ff8f6f5b5f5285b0fcf361d84523bf21320074b2
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Wed Oct 24 20:20:12 2007 +0200
+
+ Extended the Hash#[] spec with a test case for ticket #65.
+
+commit dc61b1e771c70e54f98859da3dd31a4ea61384e1
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Oct 24 07:11:07 2007 -0700
+
+ Specs after(:each) MUST come before it blocks.
+
+ Updated CI excludes for IO.read specs.
+
+commit 06539bad037e0ef7368ea5cbb5780fce7bbea443
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Wed Oct 24 21:26:49 2007 +1000
+
+ Initial IO::read specs.
+
+commit a2f26d7a7b1997510edff1792eaec6507ba38208
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Oct 23 19:30:46 2007 +0200
+
+ Implemented Numeric#remainder.
+
+commit 2bc5fcee6be4db3e0e0c46aa7c1b8ef5a5c57957
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Oct 23 19:10:54 2007 +0200
+
+ Fixed Bignum#modulo(0.0).
+
+commit 68965dc12ea369f6db64c208cb2ce123c1398bb8
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Oct 23 10:20:04 2007 +0200
+
+ Extended the Numeric#nonzero? spec a bit.
+
+commit 290ddde29d6c64e9c81f69780b7b0c967b2b4901
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Oct 23 10:15:57 2007 +0200
+
+ Added Numeric#nonzero? and killed bad Fixnum#nonzero? in bootstrap.
+
+ This fixes the Numeric#nonzero? specs.
+
+commit e9a1b257fc95c181e46c679d301a324134a725d4
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Oct 23 16:07:58 2007 +0200
+
+ Enabled the Numeric#step spec now that it works fine.
+
+commit f4016db94eee2ec93a2cc487181c9ec2fa0d59d0
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Oct 17 13:51:04 2007 -0700
+
+ A number of fixes found while debugging test/unit and optparse
+
+commit 7c7920c3e7727c3514b493ba299a52c5e5cde8f6
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Mon Oct 22 18:12:42 2007 -0500
+
+ Numeric#step is capable of floats and passes all tests
+
+commit ebc6ec5be0239bba544c55ff77fdc88903f4bb28
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Mon Oct 22 20:56:33 2007 +0200
+
+ Made the Symbol#inspect spec pass.
+
+commit f0db8c3d1bb5dc444fc72ed5ca222f4cf5df8b35
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Oct 21 14:00:30 2007 +0200
+
+ Fixed a bug in process_op_asgn1 and added a simple spec for it.
+
+commit 4fbce6e8a6ab8fbb6b69944677678611db68bcf2
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Oct 20 20:03:50 2007 +0200
+
+ Fixed a failure in String#sub specs
+
+commit ca0332f9edb9e01ae216dee90674fb6f9809951c
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sat Oct 20 22:33:42 2007 +0200
+
+ Fixed the Bignum#size spec for Rubinius' implementation.
+
+commit ae9c2ac3fe9dc59a027af7571d6d3083bcccb490
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Oct 20 14:14:49 2007 +0200
+
+ Don't rely on #respond_to? calls.
+
+commit 94938622aaf74e1f068c3b7ec8bfeccf763792ba
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Fri Oct 19 20:56:50 2007 +0200
+
+ Extended the private spec.
+
+commit bf000a15edcfdd30c43ae6563b5766617f245a60
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Oct 19 00:49:16 2007 -0700
+
+ Updated CI excludes.
+
+commit ad146fc7ae22bfc26a536a40cf8dc4c0338cf25c
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Oct 18 19:45:27 2007 +0200
+
+ Added a spec for the 'private' keyword.
+
+commit f15b5a8c818932d0ab5bea46f48a326e468b3511
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Thu Oct 18 19:44:54 2007 +0200
+
+ Added Object#should_not_include.
+
+commit 80fdbd626d8ff99dc7ba4cf23a05d44ad98bd0cb
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Thu Oct 18 09:31:51 2007 -0700
+
+ Updates Hash so:
+ * No longer freezes keys
+ * Specs reflect the lack of freezing
+ * shift spec doesn't fail purely because to_a is broken
+
+commit 197f36b6626b61203709704db869324a539764d5
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Wed Oct 17 20:11:50 2007 -0500
+
+ moved File::expand_path to platform and made several fixes + new specs
+
+commit 7e8506fd510ab4e6f07e6d36456fdfef6e5b080a
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Wed Oct 17 21:07:44 2007 +0200
+
+ Make sure that File.dirname doesn't modify its argument.
+
+commit 271bc31ba814e68fb414ebf29cf9648f57fe4cb6
+Author: David Altenburg <david@gensym.org>
+Date: Wed Oct 17 00:39:59 2007 -0500
+
+ Added to fork spec: check that fork returns a nil pid for the child process
+
+commit 5d45341c55400a51d8cae3128bba265e7d441fab
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Wed Oct 17 05:19:49 2007 -0500
+
+ moved File.basename to platform, added specs, and fixed specs for all but a disputable behavior
+
+commit ae7afd794881a4dedadf876f61369e5e88da695b
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Wed Oct 17 04:47:24 2007 -0500
+
+ added platform/file.rb and fixed File.dirname for all and updated specs
+
+commit 71bf9b1c9cd7fb81692186d21b133fde43e8a6b7
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Wed Oct 17 03:44:33 2007 -0500
+
+ spec to check if break exits all types of yields and loops correctly
+
+commit 3a546b40271d35bf7c60bb56a68ca49089ac9a34
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Wed Oct 17 03:01:40 2007 -0500
+
+ clarified include_spec
+
+commit 40d8ed96fa689daf31039e71f91ed5520a821aa2
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Wed Oct 17 02:38:46 2007 -0500
+
+ specs Module#include and Class#include that check to see if constants, public_methods and instance_methods are imported correctly
+
+commit ff462080a58aaa61759830e18f3a5757a883980d
+Merge: 5a2c858... 5cf41ab...
+Author: Jon Guymon <gnarg@kiryo.(none)>
+Date: Wed Oct 17 01:55:33 2007 -0400
+
+ Merge branch 'puts_specs'
+
+commit 5cf41abaff9dc04cdba5fe50492d4ebfdde2a274
+Author: Jon Guymon <gnarg@kiryo.(none)>
+Date: Wed Oct 17 01:55:01 2007 -0400
+
+ normalized specs for IO#puts StringIO#puts and Kernel#puts
+
+commit 5a2c858086c1b02a54864ff82c12d4bf3a559535
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Wed Oct 17 00:43:24 2007 -0500
+
+ fixed posix File.join and added edge cases to specs
+
+commit e7972b8617b8b0ef2a19a1f7ddedd4d93ab80f5c
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Oct 16 21:17:21 2007 -0700
+
+ Commit gnarg's loop specs (#49).
+
+commit 3fcdd60b4c9fc20081987bb13aab37b9419939a3
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Tue Oct 16 19:46:36 2007 +0200
+
+ Make this spec usable by loading pathname.
+
+commit 276b6cc5620a5a3629d56b04ade7c397a48c2488
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Oct 16 00:53:11 2007 -0700
+
+ Exclude metaclass instances from Module#ancestors list.
+
+commit e108e7f3f8a6ef7cf2acf4bb7e7a6609900a3ebc
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Oct 16 00:06:54 2007 -0700
+
+ Updated CI excludes.
+
+ Updated compiler specs to match recent changes.
+ Added compiled core/string.rb from changes in edeffe90517.
+
+commit 1a1410f394b3de23b63560f2a5c1312cc6451d2e
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Mon Oct 15 19:49:40 2007 -0500
+
+ spec for __FILE__ added
+
+commit e158c3130f033a1029ae26888b8e7e541f2b388a
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Mon Oct 15 19:46:58 2007 -0500
+
+ spec for __LINE__ added
+
+commit d09ad9e6b28c91f5d00db5d0b369c4932eabbe2b
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Mon Oct 15 18:45:48 2007 -0500
+
+ spec for throw/catch inside of ensure reverted and clarified
+
+commit 0e5336f1572fc1ad766cff61d8843410d28df9db
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Mon Oct 15 18:33:27 2007 -0500
+
+ spec super refactored into fixtures and expanded to test all methods on each class
+
+commit b998ec8e682c3a0f2160066bb84f40b68f748407
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Mon Oct 15 16:26:58 2007 -0700
+
+ Implement undef and Module#undef_method
+
+commit fed8486110930cabce64e0421638a867740e4d21
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Mon Oct 15 15:54:44 2007 -0700
+
+ A bunch more language spec cleanups.
+
+commit 66086cb333432a29d4c4ce4fec6a01c0ac88c5a5
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Fri Oct 12 19:33:59 2007 -0700
+
+ Bunch of compiler fixes to pass more language specs
+
+commit 680e0ca4cafb20fa053f0dd5cd72915da9fbc86f
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Mon Oct 15 17:36:09 2007 -0500
+
+ specs for super involving inheritence, modules and metaclasses
+
+commit 167febd232f5cf4696cf8e81a96d1c9d80744e36
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Oct 15 15:32:48 2007 -0700
+
+ Update CI excludes on OS X.
+
+commit 948e2573800859931e8c61e72069c21e9a50193b
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Oct 15 14:23:02 2007 -0700
+
+ Update compiler specs to match recent changes.
+
+commit a015bac050e1080548bd947c5f59b344175a809d
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Mon Oct 15 23:51:01 2007 +0200
+
+ Enabled the remaining Bignum#& spec.
+
+commit 0b37b2946772ea41fe7b14c762be7fdbaa4a6f8d
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Mon Oct 15 18:20:56 2007 +0200
+
+ Updated Spec excludes for Bignum.
+
+commit ae613272bcf0c260ad3da00ffd14d4a76422ac46
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Mon Oct 15 08:30:11 2007 +0200
+
+ Added the beginnings of a File.stat spec.
+
+commit 81147d2eadb0397c2bcc1b9dd620bb55b6e0e53d
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Oct 14 11:33:21 2007 -0700
+
+ Fixed Float#to_s for numbers of the form "\d+.0".
+
+commit 2f9ba53190ca19ca425126d1319f47b3bbce12f6
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Oct 14 18:57:04 2007 +0200
+
+ Updated Bignum excludes.
+
+commit 0c81822cf703da13f6a8783cc6cd4ad453d2ff74
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Oct 14 18:05:45 2007 +0200
+
+ Add some OpenStruct specs.
+
+commit add2a900029530cb35b6463525313f432b7f36f4
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Oct 14 17:29:38 2007 +0200
+
+ Extended some more Bignum specs.
+
+commit 45cf3a275d390d4ae1d995eca89e10ba82d2288f
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Oct 14 17:29:01 2007 +0200
+
+ Extended the Fixnum#to_s specs a bit.
+
+commit d8a42cdd57967ee07cccfa5f3f814d97353c48c9
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sat Oct 13 23:16:37 2007 +0200
+
+ Fixed the bug that broke 'case' blocks with a single 'when' statement.
+
+ Acked-by: Wilson Bilkovich <wilson@supremetyrant.com>
+
+commit 5624627fd61378fce65aebf2ffacc39c45ac5ee6
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sat Oct 13 23:48:32 2007 +0200
+
+ Ticket #37: Fixed Bignum#& segfaults when the argument isn't a bignum.
+
+commit 2081e5f53ba80cb9aa2ee272d4e543db1d4e732e
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Oct 13 23:43:08 2007 +0200
+
+ Modify and extend Bignum specs a bit.
+
+commit 7917f4f8a538a3251e3cb17d55e4cf2a523af8d5
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Oct 13 23:42:44 2007 +0200
+
+ Modify the fixnum specs a bit.
+
+ Remove dependencies on Exception messages.
+
+commit 171f25c25865da618c2e2a9a7b221abda613efa4
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Oct 13 02:02:12 2007 +0200
+
+ Extended Fixnum#<=> specs a bit.
+
+commit f708429a161c52dd713b4239527247c57fa158af
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sat Oct 13 23:30:37 2007 +0200
+
+ Guard bin/ci from running the new Bignum#& specs, which segfault (on OS X at least).
+
+commit 19f40e0ae1b60c037d0c38537a0924ad5726902a
+Author: Ben Curren <ben@esomnie.com>
+Date: Sat Oct 13 13:46:35 2007 -0700
+
+ Refactored const_name_to_sym to share logic with Class#attr.
+
+commit ee9daad614fa746a3fe2fc1b9123c65dbb0814c7
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Oct 13 11:52:08 2007 -0700
+
+ Identify which ruby platform and version before changing this spec!
+
+ Revert "Fix spec to expect correct result."
+
+ This reverts commit 8268469c563943cba6c1afce5d84defbc35f1789.
+
+commit 14a7781944491e5a1c3f5c664adcac4e1c383f2f
+Author: Tilman Sauerbeck <tilman@code-monkey.de>
+Date: Sat Oct 13 17:15:24 2007 +0200
+
+ Added a failing spec for 'case' with only one 'when' statement.
+
+commit 8268469c563943cba6c1afce5d84defbc35f1789
+Author: Tom Mornini <tmornini@engineyard.com>
+Date: Sat Oct 13 03:22:47 2007 -0700
+
+ Fix spec to expect correct result.
+
+commit 671f93c69e74976c3f5886c7fe8eb32402ccd338
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Fri Oct 12 19:06:07 2007 -0500
+
+ specs exiting threads using return, raise, and throw
+
+commit 119154a3ea5ecee20e77726b38fb58ee4b536d48
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Fri Oct 12 18:59:47 2007 -0500
+
+ spec to ensure throw exits from correct nesting and can return a value
+
+commit f36f68f075b34b5436257aba1ae41c14c04adcae
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Oct 13 01:54:42 2007 +0200
+
+ Method#[] specs should include the fixture classes.
+
+commit 22d32a24eb799307e42af55b04752c74ff500080
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Oct 13 01:40:01 2007 +0200
+
+ Extend Bignum#coerce, Bignum#&, Bignum#| and Bignum#^ specs.
+
+commit e1f682e27d2486d65297cf2121c354a99954a56e
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Oct 13 01:38:55 2007 +0200
+
+ Removed a dependency on an exception message in Fixnum#coerce specs.
+
+commit 9754ed5e74eeb6d62f0015f3615d077aa2e58a6f
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Fri Oct 12 21:41:56 2007 +0200
+
+ Fixed File.chmod and File#chmod specs on win32.
+
+commit 5ad3a4b7035bdade48586191c6e26cde1e74976c
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sat Oct 13 01:37:46 2007 +0200
+
+ Remove wrong spec from Bignum#divmod.
+ Update CI excludes.
+
+commit b068c8634b56cd9129f9fc7c309bfa81869209c8
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Fri Oct 12 18:21:52 2007 -0500
+
+ specs for behavior of throw/catch and how they interact with ensure
+
+commit 45d4a8be8f20b2b70d32d0fdd340feb8897a1ad7
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sat Oct 13 01:21:21 2007 +0200
+
+ Add (skeletal) Process::Status.
+ Set $? in Kernel#system and Kernel#`.
+
+commit ef1499962a16a7ce85bffe9e61863d2806caf6ec
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Fri Oct 12 23:52:16 2007 +0200
+
+ Regenerate core/dir.rbc and CI excludes for Dir.
+
+commit b999f31ded2a7eccb856d95653a2826a3a190204
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Fri Oct 12 23:02:39 2007 +0200
+
+ Fix typo in Dir.mkdir.
+
+commit de235630aa08df803be0420084b0a61ee35f5448
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Oct 12 13:56:13 2007 -0700
+
+ Added dev_null spec helper for capturing or silencing $stderr, etc.
+
+ Fixed failing specs on OS X MRI 1.8.5.
+
+commit 01e27ea5fa0e0f0d170cd88f128adfbb2a2703bd
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Fri Oct 12 22:01:43 2007 +0200
+
+ Add spec for backticks and their setting of $?.
+
+commit f7b18c19e47c15f3ab05f8fa548eff034206b0d8
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Oct 12 13:21:03 2007 -0700
+
+ Added guards to make specs pass on ubuntu feisty MRI 1.8.5.
+
+ Fixed rspec_helper should_include to take multiple args.
+
+commit b2d25d4a502dca79ea98f60d937be7dbd8f496d2
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Fri Oct 12 14:19:55 2007 -0500
+
+ spec for retry/redo to control order of an enumeration
+
+commit eec535a19dc2b20156349720dc3bb526c9fa4f1e
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Fri Oct 12 18:53:33 2007 +0200
+
+ Revert "Fix Kernel#`: set $? to the subprocesses exit status. Add a spec to check that."
+
+ This reverts commit 40da2d5c68196c3c9002c4ca75ead0fefc520bef.
+
+commit 40da2d5c68196c3c9002c4ca75ead0fefc520bef
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Fri Oct 12 18:43:44 2007 +0200
+
+ Fix Kernel#`: set $? to the subprocesses exit status. Add a spec to check that.
+
+commit 5da57253750e854bd9baf5378684222a895e7fd9
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Fri Oct 12 16:24:18 2007 +0200
+
+ Shield "strange block args" spec from being run by mspec.
+
+commit 2a8f7d7dd6b0f7f800320f84d16d5d089357e085
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Fri Oct 12 14:48:42 2007 +0200
+
+ Fix block specs for MRI.
+
+commit ade6c39f6199198e0015558698bc7d0333f7bcd0
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Fri Oct 12 12:07:49 2007 +0200
+
+ Fix Array#delete specs.
+
+commit 825af45d5effb6909bb0832f92621b96e51dc380
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Oct 12 01:15:23 2007 -0700
+
+ Updated CI excludes.
+
+commit e1bfb47d3560929512cbdf5c27f56c92435ce29f
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Oct 12 00:40:53 2007 -0700
+
+ Removed printing summary at exit in mini_rspec.
+
+ Set $VERBOSE=nil when running the specs.
+ Fixed specs failing MRI.
+
+commit 1f1a041d8bcfaeb8dd3cb17f7d31b21281e690a2
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Oct 11 18:26:14 2007 -0700
+
+ Moved shared specs to shared directories.
+
+ Rewrote Module#method_missing specs.
+
+commit f9177eb198003b495f485a13910808fe603030ad
+Author: Ben Curren <ben@esomnie.com>
+Date: Thu Oct 11 20:54:22 2007 -0700
+
+ Added a failing test for setting and getting constants on an instance of a module.
+
+commit d9cbad87fd4d578e4f637627fa03cab312882a36
+Author: Ben Curren <ben@esomnie.com>
+Date: Thu Oct 11 20:51:27 2007 -0700
+
+ Updated the excludes for module tests.
+
+commit 938f034bad41f4fe3391b941e536bce9e1be0af6
+Author: Ben Curren <ben@esomnie.com>
+Date: Thu Oct 11 20:49:57 2007 -0700
+
+ Updated const_set_spec to not create a new instance of Module for testing purposes.
+
+commit 550caaf78723b00f95c5f8f38215b15a0940698a
+Author: Ben Curren <ben@esomnie.com>
+Date: Thu Oct 11 18:21:01 2007 -0700
+
+ Updated the excludes for the tests that are now passing.
+
+commit 82c51fc652e215f0dc421099329c03a37af8e8f8
+Author: Ben Curren <ben@esomnie.com>
+Date: Thu Oct 11 17:56:48 2007 -0700
+
+ Updated const_set and refactored the valid_const_name? further.
+
+commit c6323c74d0ee7b554d2cfbff3bd8d85ea910e0c9
+Author: Ben Curren <ben@esomnie.com>
+Date: Thu Oct 11 12:01:27 2007 -0700
+
+ Added logic to remove Object and empty from a recursive string for const_get and const_defined?
+
+commit c108d2a623f5041f46b6efa32d0b331f4f91d669
+Author: Ben Curren <ben@esomnie.com>
+Date: Thu Oct 11 11:58:04 2007 -0700
+
+ Added back the recursive case for const_get and const_defined?
+
+commit b976f184d8ade3b5d32d3e7ec11027c21c2bce2a
+Author: Ben Curren <ben@esomnie.com>
+Date: Thu Oct 11 11:33:41 2007 -0700
+
+ Refactored const_defined to use const_get.
+
+commit 82c5c14b948cedbf3bed5f7996634b0238e4de55
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Oct 11 17:38:05 2007 -0700
+
+ Bunch of compiler fixes, almost have test/unit and optparse running.
+
+ * Adds support for /ao#{name}/o (aka dregx_once)
+ * Invalid redo's raise an exception at runtime instead of compile time
+ * defined?(a.foo) works
+ * Lots of work on getting the block arg semantics right, including a new
+ instruction, passed_blockarg which is used to detect at runtime how
+ many block args were passed in.
+ * bug in 'yield 1, 2' versus 'yield [1, 2]' fixed
+ * A little better error reporting on compile errors
+ * Fixed Class#<, added #>, #<=, and #>=
+ * Fixed Hash.new
+ * Fixed nested case problem
+
+commit 1369b104a3f966dd4d279362afdc6ccb72f06de3
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Thu Oct 11 22:47:16 2007 +0200
+
+ Fix String#eql? specs.
+
+commit b190009707c120edb257a9ad92697145092c5612
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Oct 11 10:57:10 2007 -0700
+
+ Shield parse errors in block args properly
+
+commit 0509ecbd6aeb973061866c5e04c590f975174b41
+Author: Ben Curren <ben@esomnie.com>
+Date: Thu Oct 11 10:21:08 2007 -0700
+
+ Updated tests to test FixNum being passed to const_defined?
+
+commit 80116298779dc5afd3294cd83d758d76d0dcdf50
+Author: Ben Curren <ben@esomnie.com>
+Date: Thu Oct 11 10:16:46 2007 -0700
+
+ Added error checking to const_defined?
+
+commit eed3ae097b1dae17e45cdb959b75d1fa7cf21c1b
+Author: Ben Curren <ben@esomnie.com>
+Date: Thu Oct 11 09:25:53 2007 -0700
+
+ Convert paramter to_str if it responds to the method.
+
+commit 348df85a082eee56c301bce594d6c522050e34dd
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Wed Oct 10 18:49:34 2007 -0500
+
+ specs for language/retry and updated redo to show differences between them
+
+commit e924e2bb206317e8f5375c979f5e4e1046fccca9
+Author: Tom Mornini <tmornini@engineyard.com>
+Date: Wed Oct 10 00:55:47 2007 -0700
+
+ Add Class#attr, refactor Class#attr_reader, Class#attr_writer and
+ Class#attr_accessor, pass all specs for Class#attr_*, fix a couple of issues
+ with said specs.
+
+commit 21b0bdc67c6a8cc4ad4b9d3942a2608fb45da31d
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Oct 9 15:10:04 2007 -0700
+
+ Commit #198, patch from Will for Module#(private|public)_class_method.
+
+commit 2cf5f0b4683d0a65181c1450d0714c4e165db1cd
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Tue Oct 9 21:00:13 2007 +0200
+
+ Moved shared specs.
+
+ Moved shared specs into the 'shared'-subdirectory so specs don't depend on each other anymore. Added some more shared specs.
+
+commit e558fab61ce9f7c5211d005aff2c5e8fc1b39931
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Tue Oct 9 19:53:46 2007 +0200
+
+ Fixed a failing Array#each spec. Closes #14.
+
+commit 0d77eefd718c826e02376edc8643364eb511773d
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Tue Oct 9 19:09:17 2007 +0200
+
+ Removed remaining dependencies on Exception messages in Fixnum specs.
+
+commit dd4063ba46eb313a57957d76dce3608dd8e5c161
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Tue Oct 9 18:58:01 2007 +0200
+
+ Fixed String#crypt spec.
+
+commit d5a2bb2b000fae7391e512c5bcab054ce967de3b
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Oct 9 09:35:51 2007 -0700
+
+ Updated CI excludes after applying Akshay's Precision specs patch.
+
+commit 10cd5764bbf51f1defa6815f8b07fcdce0de8875
+Author: Akshay Rawat <akshay.rawat@gmail.com>
+Date: Thu Oct 4 21:48:15 2007 +0530
+
+ Specs for the module Precision
+
+commit 2e711c30e4e9ce50d9c20ab14a3b99ea47be32e9
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Tue Oct 9 13:56:45 2007 +0200
+
+ Make IO#puts specs pass in MRI.
+
+commit 13dc28c47c3211f01663d002847badb50277f277
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Tue Oct 9 13:44:51 2007 +0200
+
+ Silence warnings when running Hash specs in MRI.
+
+commit 52f903938f4eacf4465f7a36cacb25aa662aa559
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Tue Oct 9 13:44:02 2007 +0200
+
+ Turn Hash#values_at into a shared spec.
+
+commit 1e02ced5a68f16b8a65809136d954c68c9fdc590
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Tue Oct 9 12:59:58 2007 +0200
+
+ Add a few more specs for Struct#new.
+ Regenerate CI excludes for Struct.
+
+commit 94ea8c1f25761384796e9499e0b4b3faeba9da66
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Tue Oct 9 12:48:03 2007 +0200
+
+ Silence warnings when running Struct specs in MRI.
+
+commit 5ea6b219a8465cfad86dae9ae12d6a8d85812532
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Tue Oct 9 12:42:58 2007 +0200
+
+ Apply patch from ticker #23 by Jon Guymon (gnarg).
+ Make Struct specs not depend on method argument evaluation order.
+
+commit 8cbf7b94300e6ebcc0ee3cbe0de8123ef3563e96
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Oct 8 22:08:29 2007 -0700
+
+ Fixed that including a module includes the whole chain.
+
+ Fixed that Module#include only allows modules.
+ Simplified some module fixtures.
+ Updated CI specs for module.
+
+commit 4e7e2768d50392831a4d26f236d4cff733418225
+Author: Ben Curren <ben@esomnie.com>
+Date: Mon Oct 8 11:54:51 2007 -0700
+
+ Added puts spec for io and updated IO implementation to match MRI's. Updated Kernal#puts to delegate to $stdout.puts.
+
+commit edc724086e84725995ed1720d4fa7a781fd9c3c6
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Oct 8 09:24:40 2007 -0700
+
+ Updated CI excludes for Module#define_method.
+
+commit a53ddb723a10d692223f05a49679e17f403fa128
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Mon Oct 8 01:05:04 2007 +0200
+
+ Fixed some Fixnum specs to not depend on error messages.
+
+commit d6bc4b47f3f395980c92f323cf029da1ccdba709
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Oct 7 23:42:12 2007 +0200
+
+ Added another failing spec for Module#define_method.
+
+commit 49435e31289f593a118377b3513ec9e7cdfea06b
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Oct 7 23:37:19 2007 +0200
+
+ Added failing specs for Module#define_method when given an UnboundMethod.
+
+commit 821c0114777fb2a77f1c85f216ee54e4c5340943
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sun Oct 7 22:29:47 2007 +0200
+
+ Apply patch from ticket #15 by Jon Guymon (gnarg).
+ Add Struct#eql?.
+ Rebuild core/struct.rbc.
+ Add more struct specs.
+ Update CI excludes for Struct.
+
+commit 1f14c3510d4563930d11155f36717c0fb851c678
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Oct 7 18:13:33 2007 +0200
+
+ Added some GetoptLong Specs.
+
+commit f9c8c00649212b924561300abbd0cb037c1d278d
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Oct 6 23:00:05 2007 -0700
+
+ Update CI excludes for File#executable.
+
+commit 6d4427d07fc474e2404cdd2b6f3b925d99d90e67
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Oct 6 22:50:45 2007 -0700
+
+ Updated spec/excludes.txt from 72 items to 17.
+
+ Added -V | --verbose flag to bin/mspec.
+ Updated CI excludes.
+
+commit d4f5e44a8e2f8e682b779f45d12d060e83eb9fc7
+Merge: a035e9d... 42abc5e...
+Author: Jason Yates <jaywhy@gmail.com>
+Date: Sat Oct 6 15:18:45 2007 -0400
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit a035e9d3d204cf7e5ddb2fec72ca471ff33c3b9b
+Author: Jason Yates <jaywhy@gmail.com>
+Date: Sat Oct 6 15:13:28 2007 -0400
+
+ fix a bug in the File.executable? spec
+
+commit 3c23c945aaf143aa8706b1cd2956908a71940e26
+Author: Jason Yates <jaywhy@gmail.com>
+Date: Sat Oct 6 15:09:55 2007 -0400
+
+ fix bug in File.executable? spec
+
+commit 94e59065bf921ae167a6b04edfef40de336978a1
+Author: Jason Yates <jaywhy@gmail.com>
+Date: Sat Oct 6 15:03:14 2007 -0400
+
+ Revert "Revert "Add a few more Proc#call specs. And CI excludes.""
+
+ This reverts commit 7658362c3882c6be2ef67f6b57d6c6796ff5de98.
+
+commit 42abc5ed6e1ced2fa86e9dc9379c6bed4da4537e
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Oct 6 11:40:49 2007 -0700
+
+ Updated CI excludes for Kernel specs.
+
+commit 7658362c3882c6be2ef67f6b57d6c6796ff5de98
+Author: Jason Yates <jaywhy@gmail.com>
+Date: Sat Oct 6 14:29:28 2007 -0400
+
+ Revert "Add a few more Proc#call specs. And CI excludes."
+
+ This reverts commit 567659dee34014d037d4797bf0c171597e0ac05d.
+
+commit 567659dee34014d037d4797bf0c171597e0ac05d
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sat Oct 6 16:23:54 2007 +0200
+
+ Add a few more Proc#call specs. And CI excludes.
+
+commit d8e737b09f8ed984e57b4fbbd5c016a7643aa67d
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sat Oct 6 15:56:33 2007 +0200
+
+ Fix Array#slice specs. Regenerate CI excludes.
+
+commit c79eeb620296a1802e6d194463063555638911bf
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sat Oct 6 14:34:05 2007 +0200
+
+ Fix Array specs that depended on respond_to? being called on coercion.
+ Regenerate CI excludes for core/array.
+
+commit 8a60522fd8237fdfa36ef5518c9642216c66f8d6
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sat Oct 6 14:08:22 2007 +0200
+
+ Guard Array specs for #freeze. Fix MRI Array specs for #freeze.
+ Regenerate CI excludes.
+
+commit eaaab65c54c3b81397441169761774ef95867297
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sat Oct 6 12:47:55 2007 +0200
+
+ Add specs for Proc#call. Regenerate CI excludes.
+ (Most of these seem to be from e6cf8978.)
+
+commit 19bcb0f6ec1b2247985823492f0c25f0aa5d94ab
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sat Oct 6 12:09:29 2007 +0200
+
+ Slightly amend the core/hash specs.
+ Regenerate CI excludes.
+
+commit 6d1afe325098a73757980bb208fb2c8c64bd016b
+Merge: ac9365e... 0d22ef5...
+Author: Jason Yates <jaywhy@gmail.com>
+Date: Sat Oct 6 01:13:59 2007 -0400
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit ac9365e384263b6f062353f2afab3e33d8f84f3e
+Author: Jason Yates <jaywhy@gmail.com>
+Date: Sat Oct 6 01:13:23 2007 -0400
+
+ added spec for OpenStruct
+
+commit e6cf8978a6dd441d5d4793c48438fab4150ca750
+Author: Jason Yates <jaywhy@gmail.com>
+Date: Sat Oct 6 00:14:01 2007 -0400
+
+ added several tests to Proc
+ added tests for Kernel#lambda and Kernel#proc
+
+commit 7b69ae066cab2252375d1ad19c6f17b365c47c32
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Oct 5 18:51:11 2007 -0700
+
+ Updated CI excludes for Array.
+
+commit f216e89033d10f3500798561282e48aa0e5b5537
+Author: Jason Yates <jaywhy@gmail.com>
+Date: Fri Oct 5 20:45:41 2007 -0400
+
+ really simple spec for the Singleton class.
+
+commit d682b176237a988afa7ebdb3460d64ea41fab919
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Oct 6 01:54:31 2007 +0200
+
+ Applied esomnies
+
+commit b3018362c0cad86a5a026eb39b5f6ea4a8af1192
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sat Oct 6 01:24:05 2007 +0200
+
+ Guard Hash specs for #freeze.
+
+commit a185f463adbab1d6f82126dde9abe88a29e83283
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sat Oct 6 01:15:23 2007 +0200
+
+ Fix workarounds in core/hash specs.
+
+commit d796eb3d8a5d9a070b105f1fe0e9f46be5bfaaee
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Fri Oct 5 17:09:48 2007 -0500
+
+ specs for UDPSocket client/server
+
+commit 46a9c1a0d0610865b659c289293b444f0b3d6ae9
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Oct 5 15:32:56 2007 -0700
+
+ Fixed bin/ci to not load spec/excludes.txt for every file.
+
+ Fixed bin/mkspec to not overwrite an existing spec file.
+ Updated Fixnum CI excludes.
+
+commit 1099f49c06de5621aff36216179f46c308e60a38
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Oct 5 14:40:24 2007 -0700
+
+ Added basic IO#readlines spec and implementation.
+
+commit edc438039ee503c7b9d1fb83b04bd9bc1664cda5
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sat Oct 6 00:09:41 2007 +0200
+
+ Regenerate core/hash.rbc, core/hash CI excludes.
+
+commit 2ad7d015a316620a488ed1cdeb45fe696b9d410a
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Fri Oct 5 23:56:59 2007 +0200
+
+ Extend core/hash specs to check for LocalJumpErrors.
+
+commit 42d961f0ab8a7e23a822b41ca82aaed5a48da2bf
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Fri Oct 5 23:21:20 2007 +0200
+
+ Refactor and fix Hash#inspect.
+
+commit b51402d8724478b85789d19857a2a48442470fcb
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Oct 5 11:29:08 2007 -0700
+
+ Fixed bin/mkspec to not create the spec file stub if the file exists.
+
+commit efe79de398db491ce97666a3f4f3b38265c1ab95
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Oct 5 10:55:52 2007 -0700
+
+ Updated CI specs. Guarded String specs for #freeze.
+
+commit 8647951df433d427be31bec060edf5b7efb46e46
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Fri Oct 5 17:04:25 2007 +0200
+
+ Replaced all occurrences of Object#coerce_to with Type.coerce_to and removed Object#coerce_to.
+
+commit 778e11d2df647cf91a712bb30df34152c71dbc3f
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Fri Oct 5 11:42:40 2007 +0200
+
+ Don't work around rbx bugs in Hash specs.
+
+commit 4b42923eb2cdebe43f9e9dd80fff98d9ded26e4b
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Fri Oct 5 11:29:33 2007 +0200
+
+ Fix Hash specs to pass in MRI again. (Doh!)
+
+commit 8a4f0b1c0d95b7a87ed99583797cf7d3710fb15a
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Oct 5 02:24:30 2007 -0700
+
+ Updated CI excludes for Hash.
+
+commit cecbf342546f37f4923728e8381e7fafcb039633
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Oct 5 02:22:41 2007 -0700
+
+ Changed bin/ci to run in a single process. Updated CI excludes.
+
+commit ba0f4ef5405665c84af4410d70be5ef911a93195
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Thu Oct 4 22:58:50 2007 -0700
+
+ Adds the intern spec from Ticket #8
+
+commit 0721f6ea40f51202ec9d2d421061be92e05a18b9
+Author: Jason Yates <jaywhy@gmail.com>
+Date: Thu Oct 4 22:39:00 2007 -0400
+
+ Commit #207 Xavier Shay Enhanced specs for Hash
+ fixed Binding#dup spec
+
+commit ce4a1866ef65e041fbed224c3f694ca534d0a0d1
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Thu Oct 4 18:24:21 2007 -0700
+
+ makes String#delete faster
+
+commit 7ec0eeadf554150159f0a04468b16de8f06c2e8a
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Fri Oct 5 00:32:25 2007 +0200
+
+ Whoops, two more frozen TypeErrors I forgot.
+
+commit 78a3de42bf0e6f1478b2aac903c25143fd56195a
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Thu Oct 4 23:17:42 2007 +0200
+
+ Regenerate CI excludes and core/hash.rbc after revert.
+
+commit beaa5d022b19cb70213d4fe14e10d7f1a5f90a3a
+Merge: c7ea881... 8cb4b0b...
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Thu Oct 4 16:12:41 2007 -0500
+
+ Merge branch 'spec_block_parameters'
+
+commit 8cb4b0b2c3c766618a523a0ef9a83106761ee2f8
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Thu Oct 4 16:11:15 2007 -0500
+
+ specs for setting variables in block parameters
+
+commit c7ea8812f74184e6ee33bb236766f32fface2f95
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Thu Oct 4 22:57:09 2007 +0200
+
+ Regenerate CI excludes for spec/core/hash. New hash.rbc.
+
+commit 939e8c533fb70586f8c7c6f3506d6be13f492d78
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Thu Oct 4 22:11:18 2007 +0200
+
+ Fix Hash specs: don't depend on coercion to call respond_to?
+
+commit 422e45f210ec9dc2438ae3b11544823bb6ffdd50
+Author: Jason Yates <jaywhy@gmail.com>
+Date: Thu Oct 4 16:21:27 2007 -0400
+
+ added specs for FileTest#exists? and FileTest#exist?
+
+commit e43466b52184a042bd38d33273acf7afa4580a96
+Author: Jason Yates <jaywhy@gmail.com>
+Date: Thu Oct 4 16:12:58 2007 -0400
+
+ added spec for File.exists?
+
+commit 9f69c8193d92752a2be7c21d23dfe90fb9765f11
+Author: Jason Yates <jaywhy@gmail.com>
+Date: Thu Oct 4 16:03:33 2007 -0400
+
+ fixed grammar error in Method#clone
+
+commit 9321aacf703eaec6d6bc26cce83ed7475cb27d46
+Author: Jason Yates <jaywhy@gmail.com>
+Date: Thu Oct 4 16:00:31 2007 -0400
+
+ added several specs for Module
+
+commit a282c1c4c137e1bdeae34f2f9cd58bc73f257809
+Author: Jason Yates <jaywhy@gmail.com>
+Date: Thu Oct 4 14:19:07 2007 -0400
+
+ added specs for Kernel#binding and Binding
+
+commit 3e92b4528dbf47b80a25232979554f7e4309460a
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Oct 4 10:45:10 2007 -0700
+
+ Fixed ffi_sprintf_[fd]. Updated String spec CI excludes.
+
+commit 9e9a292a8a4befeb8a928d476119b02ac0df976e
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Thu Oct 4 18:53:47 2007 +0200
+
+ Alias Object#object_id to Object#__id__
+ Regenerate CI excludes for spec/core/kernel
+
+commit 8790e93c6d231baf7da07f11d02b91a38d28375e
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Oct 3 18:23:42 2007 -0700
+
+ Superclass checking and loop {} fix.
+
+commit 718ae6f28223e94b8ca0f3af7ce321c81a597804
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Thu Oct 4 15:05:59 2007 +0200
+
+ Slightly extend Object#kind_of? specs.
+
+commit 7fa087a7058fe8872bb9743abd6dd472cd2119d7
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Thu Oct 4 08:43:55 2007 +0200
+
+ Fixed some String#slice specs.
+
+commit b9be176a9e64669f2a787c9bdebc1ba30e344d97
+Author: Paul Meserve <pmeserve@gmail.com>
+Date: Thu Oct 4 01:37:56 2007 -0400
+
+ adding String#each_char
+
+commit c1b17108a78a4dc5d3e224158f8f9d76232003e6
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Wed Oct 3 17:42:06 2007 -0500
+
+ basic specs for Kernel#sleep
+
+commit 1fe895518bdccb93991f85a92f53876ed3d4df13
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Wed Oct 3 14:16:14 2007 -0500
+
+ added spec for Kernel.local_variables
+
+commit 00417283b36dcb58da82c6fc2e9be7580de945b0
+Author: Charles Comstock <dgtized@gmail.com>
+Date: Wed Oct 3 14:15:25 2007 -0500
+
+ added specs for Kernel#global_variables
+
+commit 5b944520099f129462c3b03fa6ee7d1bb0636fc0
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Wed Oct 3 14:07:09 2007 +0200
+
+ Fix String#index specs.
+
+commit c9cdef77c7fa8dda92c91cee5a47624b9c9dc9e8
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Wed Oct 3 14:05:34 2007 +0200
+
+ Fix String#hex specs
+
+commit 4625a7afe509545f782cd4631632b4d7a58011aa
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Tue Oct 2 23:53:20 2007 -0700
+
+ String#% works with a few exceptions:
+
+ * %u doesn't work (it's aliased to %d for now, as in 1.9)
+ * There's a weird glitch in Float(10_1_0.5_5_5) that I can't track down
+
+commit 54ab6f559093e66f78cfa30db8aa6587061552d6
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Wed Oct 3 02:05:41 2007 +0200
+
+ Extend Kernel#kind_of? specs.
+ Regenerate CI excludes.
+
+commit 4583be7e7ed76e5843dcb396f8bae735f341de73
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Wed Oct 3 02:03:58 2007 +0200
+
+ Remove superfluous whitespace from Object#kind_of? specs.
+
+commit 5c237626469f4b0f4d227916752dd2e03510fcf9
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Wed Oct 3 01:50:33 2007 +0200
+
+ Remove superfluous comment from Object#kind_of? specs.
+
+commit 7a2c673d04b0d0506e89073ff231104e21c3304c
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Wed Oct 3 01:47:56 2007 +0200
+
+ Simplify Kernel#freeze specs.
+ Move Object#extend vs. frozen? spec to extend_spec.
+ Rebuild CI excludes for Object#extend specs.
+
+commit 3f6a27603b0b1f91ce32b9ff2a5fe3222fa7220b
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Oct 2 17:03:37 2007 -0700
+
+ Commit Charles Comstock's language return specs.
+
+commit ad4e7affcbaae4f0e967c97da485313168595a5e
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Wed Oct 3 01:36:27 2007 +0200
+
+ Fix Kernel#caller.
+ New core/kernel.rbc.
+ Regenerate CI excludes for Kernel#caller.
+
+commit 73b2ef1c889c940d22da6ad6bb8882eef66592fa
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Wed Oct 3 01:34:10 2007 +0200
+
+ Add spec for checking the default argument value for Kernel#caller.
+
+commit 180ecd6a7fa1d32e1932a322b1f8f82efd558e7f
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Wed Oct 3 01:32:29 2007 +0200
+
+ Add spec for checking that Kernel#caller returns nil.
+
+commit 9ad0c1428df70c9fd9e0081651e3b60cf5773267
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Wed Oct 3 01:30:46 2007 +0200
+
+ Add spec for checking the argument handling of Kernel#caller.
+
+commit 7a9483b823115b3122a4e42b21dfcb5b0f369a54
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Wed Oct 3 01:23:17 2007 +0200
+
+ Refactor the tedious part of the Kernel#caller specs.
+
+commit e204755859e4dc147217816ab3bf587db3d51dd6
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Wed Oct 3 01:22:26 2007 +0200
+
+ Cleanup description of Kernel#caller specs.
+
+commit dadbbb7930b62a4a6e47c8c32a4d9f26fcea38b4
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Tue Oct 2 23:52:49 2007 +0200
+
+ Regenerate CI excludes for spec/core/kernel. Again. Because I fixed the typos.
+
+commit 4a3587da37fd27effb30356fdd2e496f8c898be7
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Tue Oct 2 23:45:33 2007 +0200
+
+ Fix CI specs to pass in MRI 1.8.6.
+
+commit a46f5085f3c2a4849fe709044e447e5d6dacda4f
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Tue Oct 2 23:27:51 2007 +0200
+
+ Fix typos in spec/core/kernel specs.
+
+commit cb3d5867ec91a59a2a75136eb5210a10540b0ce0
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Tue Oct 2 23:27:23 2007 +0200
+
+ Regenerate spec/core/kernel excludes.
+
+commit 22e6fe0ef8dbf13aa01124447e09c9dc96f63fe3
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Tue Oct 2 23:26:51 2007 +0200
+
+ Fix Kernel#Array spec: don't depend on the exception message.
+
+commit 7b79130f38925cb48712be617cef5a80c71f0ba4
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Oct 2 14:29:42 2007 -0700
+
+ Commit #5 (LH) Charles Comstock Process.wait2 spec.
+
+commit 859c119a48909030f29a2085fbe0a80ed96d2408
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Tue Oct 2 20:57:31 2007 +0200
+
+ Fix Float#to_i and add a spec to catch the old misbehaviour.
+
+commit f2b5b2304588b4fb0efd9818c79a4b9774b2c850
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Tue Oct 2 19:07:49 2007 +0200
+
+ Fix some String#scan specs.
+
+commit 8e0ce11df329181efa440cbd55f29848e12188bf
+Author: Kevin Clark <kevin.clark@gmail.com>
+Date: Tue Oct 2 00:39:15 2007 -0700
+
+ Add error handling for Dir.mkdir/rmdir
+
+ Add aliases for Dir.delete/unlink
+
+commit 20e66dd965bfceb29e4939090a0fd543d05392a3
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Oct 2 00:21:49 2007 -0700
+
+ Commit #206 Jason Yates' UnboundMethod specs.
+
+commit 00d7d22b7d106c6aac5d9664cb444e14811171b0
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Mon Oct 1 17:57:29 2007 -0700
+
+ First pass at actually getting my modulo impl working
+
+commit c90766a09c7e1fe7a2261f8b09d9caa8eaf2214e
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Mon Oct 1 21:21:34 2007 +0200
+
+ Fix the String#crypt spec.
+
+commit a0f6f8e51a6f7a65230f8f2ea53587ccb09f9270
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Mon Oct 1 20:18:29 2007 +0200
+
+ Changed the String#to_f specs a bit.
+
+commit 21d43e565bb55a31b45dc9fabbefface156ec516
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Mon Oct 1 16:13:28 2007 -0400
+
+ Add failing Array#pack spec for use case taken from Mongrel
+
+commit b1d70b4a847fc1c8df3eb4a219c4318420121e82
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Oct 1 09:04:03 2007 -0700
+
+ Commit #205 Jason Yates' Method specs.
+
+commit de7e0f0183d072f101e0781635fc2fdb1af1b851
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Mon Oct 1 00:11:12 2007 +0200
+
+ Fix compiler specs to expect sret when appropriate.
+ Rewrite compiler specs for multiple assignments.
+ Regenerate CI excludes for compiler specs.
+
+commit 1d1e704306fca4453d600259e87175dcdc9de314
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sun Sep 30 23:27:12 2007 +0200
+
+ Add edge cases for File#extname specs.
+
+commit 108d757e6c24447b89fa785b2bf091b72d29933d
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sun Sep 30 22:34:15 2007 +0200
+
+ Regenerate CI excludes for Dir specs.
+
+commit 20c6c3cd9b5d59b9782b702ac6afeb828e895d5f
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sun Sep 30 21:49:52 2007 +0200
+
+ Make Dir specs pass in MRI.
+
+commit dcd172338bce5b70cb367db3c1e6f4653c05f9e8
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sun Sep 30 21:10:36 2007 +0200
+
+ Fix case comparison with Symbols.
+
+commit 2a45cd71d1eb90a7c11ff62d81371df9479b0d43
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sun Sep 30 13:28:32 2007 +0200
+
+ Add specs for String#tr! and String#tr_s!.
+ Cleanup specs for String#tr! and String#tr_s!.
+ Regenerate CI excludes for String#tr! and String#tr_s!.
+
+commit 6418bd672c8ea031d8ac2364c8a98bb631e53deb
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sun Sep 30 13:26:19 2007 +0200
+
+ Add specs for multiple asignments with splats vs. Array#dup.
+
+commit 10b04881d286acab9dc97147750743e03ee4509f
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sun Sep 30 13:25:14 2007 +0200
+
+ Fix Class.new to raise TypeError when the superclass is not a class.
+
+commit 0b9debba0672305f8551f5d5f35cdd3aaf16c1f1
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sun Sep 30 13:20:49 2007 +0200
+
+ Add specs for the names of classes generated with Class.new.
+
+commit e15cef6eb851318838351a4d8717b708bb09d31d
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sun Sep 30 13:15:43 2007 +0200
+
+ Remove debugging output from spec/core/dir/chdir_spec.rb
+
+commit f3251ba0e8ade79f157bc02dca550481488bf888
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Sun Sep 30 13:15:17 2007 +0200
+
+ Remove debugging output from spec/core/extensions/rubinius/options_spec.rb
+
+commit 5d4d2abaf02aef6caa7e532208da5e5f57bc6373
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sun Sep 30 20:54:14 2007 -0400
+
+ Working Socket implementation and specs. Still needs readpartial to support Mongrel.
+
+commit b514e53f589d509c287516d8dd985f96e66d9a1c
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sat Sep 29 15:44:06 2007 -0400
+
+ Working IPSocket#peername implementation
+
+commit 965ed2d88527ae8aa4ac962e8ca84180f61e6345
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Mon Sep 24 02:34:26 2007 -0400
+
+ Yet another interim socket commit
+
+commit ecd54e981a1098c4b3abf14587212e7d1d9049a6
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Sep 20 16:29:21 2007 -0400
+
+ Another interim Socket commit
+
+commit 351bbdf08f190e24328328df7b3f995b8dc27a9f
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed Sep 19 22:17:52 2007 -0400
+
+ Interim commit of Socket work so I can generate a patch
+
+commit 65a73cdfea95c5991f2044bee150e53643216ad3
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Wed Sep 26 13:15:45 2007 +0200
+
+ Added some Marshal#load and Marshal#dump specs.
+
+commit 89e1b91c606dfe18581c3ed3923340b952471d8e
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Wed Sep 26 13:15:19 2007 +0200
+
+ Extended Specs for Kernel.Float and Kernel.Array
+
+commit 5d46933362b8c54cb5d0370bbf61e063459de514
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Wed Sep 26 13:14:41 2007 +0200
+
+ Added specs for Symbol.all_symbols.
+
+commit b3324808584d7b4ee6af58d98eeb7c2162c31208
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Sep 27 10:57:46 2007 -0700
+
+ Commit #180, Jason Barton's specs for Module#undef_method, #remove_method.
+
+commit 2e1219fc03d9cb673074ee34b1f8af4bdffe9c0e
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Thu Sep 27 18:49:26 2007 +0200
+
+ Fix Kernel.Integer by making use of String#to_inum. Add some more Kernel.Integer specs.
+
+commit b0d4747cab49f4a17e9899392171087d7b67f687
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Thu Sep 27 18:23:42 2007 +0200
+
+ Fixed String#to_i. Added String#to_inum. Extended String#to_i specs.
+
+commit 2799c392b3f5383e9e74745ceb9cf7a52f82918b
+Author: Paul Meserve <pmeserve@gmail.com>
+Date: Thu Sep 27 01:20:00 2007 -0400
+
+ fix for Struct#new and a small struct spec changes
+
+ (also re-ran bin/ci on struct specs - most of the changes were from previous commits though)
+
+commit 30d9bf1f6ef9dcff067d427d6226bbce985f5e69
+Author: Paul Meserve <pmeserve@gmail.com>
+Date: Wed Sep 26 19:08:37 2007 -0400
+
+ raise proper error when passing non-block args to Enumerable#all?
+
+commit 1ef1e0ef65ef3c86558da3d313dee5cada6dd4c5
+Author: Paul Meserve <pmeserve@gmail.com>
+Date: Wed Sep 26 17:25:17 2007 -0400
+
+ adding alias for Float#quo to fix a couple number specs, and some modifications to Enumerable#min/#max, along with a couple new spec assertions. fixes failing specs and implementation should be a lot closer to MRI
+
+commit 547dd89791d92f061afcaef7184f054affae871d
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Sep 26 09:46:50 2007 -0700
+
+ Fixed placement of after(:each) block in numerous File specs.
+
+commit fa3dcbfdd623a7a7cdb15bc29b38ae47bb4056d5
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Tue Sep 25 20:25:36 2007 +0200
+
+ Added specs for UnboundMethod#arity
+
+commit 33783408b8ce1bdfcd205fd02bc3848119a632cc
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Tue Sep 25 18:00:10 2007 +0200
+
+ Added specs for Class.
+
+commit 0edea3c3a7dda5c453c527b2cef3ffba1eef1396
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Tue Sep 25 17:25:40 2007 +0200
+
+ Created some specs for Proc.
+
+commit 62c92f1c3aef6c2ff7ab8cbcd49eefb236d5caed
+Merge: b79d04d... 2d9c698...
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Sep 26 01:09:42 2007 -0700
+
+ Merge branch 'dir'
+
+commit 2d9c69848f4ca34685b95b07e17d1b5fe1ec2391
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Sep 26 01:09:28 2007 -0700
+
+ Updated CI spec excludes for Array, Dir, Fixnum, Enumerable, Hash.
+
+commit b79d04db673d9b7b5cc47f2918bccf1b0400bdbd
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Sep 26 00:56:01 2007 -0700
+
+ Commit crayz's patch from #195, #196, #197.
+
+commit ec960578671a327469d9545d6ced827736ceafa0
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Sep 25 19:40:54 2007 -0700
+
+ Fixed Dir specs failing MRI after conversion.
+
+commit 00b398352ed0f4cbcd326d56b7f8a4469056ee0d
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Sep 25 00:54:06 2007 -0700
+
+ Many cleanups of Dir specs.
+
+ Added Dir specs fixture directories and files.
+
+commit b75cfa7c0a9871dc34b8b315ca2311e65000b2f3
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Sep 23 00:41:36 2007 -0700
+
+ Converted Dir.glob specs.
+
+commit 3d8ea2a55e67cc6cfb85d2f4f7845a45984f6504
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Tue Sep 25 21:52:54 2007 +0200
+
+ Fix a few Struct specs. Failures are down to 15.
+
+commit a477ff678e9f5f39d3d2b94e559c77b34f0c56c5
+Author: Martin Kuehl <martin.kuehl@gmail.com>
+Date: Tue Sep 25 20:51:38 2007 +0200
+
+ Fix compiler warnings in Subtend spec extensions.
+
+commit db3e1be4e25b7e8cc463443d050afe9a5acaa7de
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Mon Sep 24 22:31:03 2007 +0200
+
+ Extended Fixnum specs.
+
+commit d136b779736af52d1eac08a40814ab4a47de93b3
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Sun Sep 23 17:47:30 2007 -0700
+
+ Array#each explosion spec
+
+commit 8a24a71ff13aac465f7f4a14587981c3c23dc800
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Sep 23 20:06:04 2007 +0200
+
+ Extended Comparable specs.
+
+commit ea9ba046ddfe91601d4453972a6d6f8fce96c392
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Sep 23 12:17:17 2007 +0200
+
+ Fix Numeric#divmod and Fixnum#divmod.
+
+commit 1c5ecb9d2e9066c66b9f0625d65cc4cefaee1f83
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Sep 23 12:00:16 2007 +0200
+
+ Fix Float#to_i for infinite, negative values. Fix Numeric#/ and Numeric#div. Add Numeric#do_coerce.
+
+commit 2d67d024e11e887eb07622963bfc36b0ec377746
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Sat Sep 22 20:08:38 2007 -0700
+
+ Added Onig 5 and got rindex working with it
+
+commit adc26eb525447010e28fc884eaa54b9d2228f4d6
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Sep 23 01:35:11 2007 +0200
+
+ Fix Kernel.Float specs
+
+commit 9a2ecf258fee3bda410776b3d3b77366590d64fe
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Sep 22 23:18:46 2007 +0200
+
+ Extended more Fixnum specs. Removed the spec/fixnum/induced_from_spec.rb.
+
+commit 53b0042824bb1b1c523d790cb3645aec6b789abe
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Sep 22 22:35:53 2007 +0200
+
+ Extended many Fixnum specs.
+
+commit 8661cdb78cd2c4afa0fb231aa9cc959e338e097e
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Sep 22 22:35:11 2007 +0200
+
+ Extended nil#to_s spec.
+
+commit 4008d8b39032ffa5667e95fb445ff816b1428330
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Sep 22 14:25:27 2007 +0200
+
+ Added some more specs and fixed some bugs in Range#initialize, Range#step and Range#each.
+
+commit 254e3d57dc6e859616ca7e0c44058d4b73211f68
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sat Sep 22 11:31:02 2007 +0200
+
+ Some updates to Range specs.
+
+commit b87e28bfc06e81fe5c4c3d6e285947f635f79f61
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Sep 21 20:12:18 2007 -0700
+
+ Commit wycats Regexp.regexp_match_region primitive.
+
+commit eaa56811836b4b5ed09a5e26d00f26eb004f2853
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Sep 21 18:44:25 2007 -0700
+
+ Added StringValue to some File methods.
+
+ Upated File CI spec excludes.
+
+commit 83f7b6020dbc881fbd6bd13da6ebb049d6080c2c
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Sep 21 17:54:53 2007 -0700
+
+ Upated spec excludes for Float, Fixnum, and Math.
+
+commit af0b0c8da8357fcae7437f6cdfd7797f03ffd73b
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Thu Sep 20 22:10:54 2007 -0700
+
+ Fixed a number of string-related issues:
+
+ String#inspect respects $KCODE
+ String#inspect returns tainted subclass
+ String#dump is no longer a copy of inspect, and does not respect $KCODE
+ String#match tries to call #to_str if it can before throwing an error
+
+commit 9409ace6d3e97946d10f9f7fcefa69ebcae43c47
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Thu Sep 20 16:55:11 2007 -0700
+
+ String#index works
+
+ Tweaked spec because [[x,y], [x,y]].each{|x,y| ... } wasn't working
+
+commit 6a72e4c4defef170f53a31c930c112022934dbc4
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Sep 20 15:22:28 2007 -0400
+
+ Correct StringIO#reopen specs and implementation. Submitted patch for 1.8.x stable.
+
+commit 2b10dd99c2de4f97b5faa45060eba929d02052c4
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Wed Sep 19 22:20:33 2007 +0200
+
+ Implemented File.identical?
+
+commit 2c3c1fc7bd2f6365b28262cae46872eb0925c2e7
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Wed Sep 19 21:48:13 2007 +0200
+
+ Implemented File.link.
+
+ Kudos to the Frankfurt Rails User Group! :)
+
+commit 0330b22006e39b5b173c78800f412831296de59a
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Thu Sep 20 01:32:43 2007 -0700
+
+ String#gsub works correctly
+
+ There was a weird segfault issue which I tracked down to a use of gsub inside of gsub.
+ I got things working by extracting that functionality into a mini-gsub for just that
+ use case, but we should fix it.
+
+commit 6c825ce63ca0eb7d6f882a767ee0e6a597219883
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed Sep 19 22:14:31 2007 -0400
+
+ Discover and fix an edge case in StringIO
+
+commit 6bd7adcfa115f11829a7efe8f526fabbb56d5c4c
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Sep 18 19:00:20 2007 -0700
+
+ File.fnmatch(?) now passes all existing specs.
+
+commit f626f4199b88ca09a2ba75127270c0bec2ec2c86
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Mon Sep 17 15:04:44 2007 -0700
+
+ Adds a new match_all primitive
+
+ * will be used as the base of regex-related String functions
+ * is called match_all instead of scan because it's more primitive than Ruby's scan
+
+commit 0b42d4d2610ec36a4ae5e21c37d5b587f2b9dcf8
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Mon Sep 17 02:06:50 2007 -0400
+
+ Avoid using $/ in IO#puts and StringIO#puts
+ Additional StringIO specs and fixes for failures
+
+commit 75969031a57bea50e4a6450bbc9ae9e5adf76fa7
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Sep 16 18:26:29 2007 -0700
+
+ Replaced File.fnmatch FFI version with custom version.
+
+ The custom version is needed to be as compliant as possible
+ with MRI on different platforms.
+
+ Lots of fixups to File.fnmatch specs.
+
+commit 18d098062cb1b996a571a946740eea7c52421e12
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Mon Sep 17 01:11:45 2007 -0400
+
+ Some failing sprintf specs and then the fixes for said failures
+
+commit 84f94ab41d72012e4ec3d0d236b183fd8a51fbe0
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Mon Sep 17 00:53:30 2007 -0400
+
+ Additional StringIO specs and fixes for the failures that arose
+
+commit 9b44df55c682f239f036d46efe45edc2190a7345
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sun Sep 16 12:55:27 2007 -0400
+
+ Add StringIO spec for $/ global handling
+
+commit dfcba62eb69f88d373359c75c3fa7fe827e24c69
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Sun Sep 16 00:43:41 2007 -0700
+
+ Fixes string/equal_spec to pass
+
+commit 5026350a166b94fc5fffff70dae510fa2abf2094
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Sun Sep 16 00:20:52 2007 -0700
+
+ String#slice works
+
+commit a4f3aa09d3aeb8d2b0a640ca9f659a5945692e04
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Sep 15 13:49:42 2007 -0700
+
+ Added ability to read/write to pointer to int or double in FFI.
+
+ * Fixed Math.frexp to use pointer to int to return exponent.
+ * Completed Math specs.
+
+commit 1a88ca4def8d7aa566a4254eebee3236a1359fc8
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri Sep 14 20:57:01 2007 -0400
+
+ mini_rspec.expectation_messages.yak_shave!
+
+commit 64f53c8b40b3a80b41a2c27b4ac7255b7aad5f4d
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri Sep 14 02:18:16 2007 -0400
+
+ Hopefully full StringIO coverage now
+
+commit 7cf9fe62bc9a5a00ae69ed3cac82e50012f3bb69
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri Sep 14 00:54:33 2007 -0400
+
+ Yet more StringIO specs
+
+commit 92da0550bd32db984fbb54f105b9701867d4faf9
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri Sep 14 00:38:55 2007 -0400
+
+ More StringIO specs
+
+commit 29826669197f44850d323910c7e60897e1ef7796
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri Sep 14 00:23:45 2007 -0400
+
+ Beginnings of StringIO specs
+
+commit 0c19e3557125dd366ddd119a34451715bfe5e7a1
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Wed Sep 12 18:11:49 2007 -0700
+
+ Fixed object allocation bug and Bignum spec
+
+commit e42a1b960f530a987527d8795a98b2de18fea824
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Sep 13 19:19:03 2007 -0400
+
+ Re-implement Module#define_method. Passes existing specs.
+
+commit a8b1a148e5fbfeb3c91558fd6caccc95006a5617
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Thu Sep 13 03:55:15 2007 -0500
+
+ Adding a non-compiling spec to case_spec and updating core.rba that didn't seem to get updated correctly.
+
+commit bbd682ba2e12ba5907fe2edf2f14f11fb110cac8
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Sep 13 04:04:33 2007 -0400
+
+ Fix anonymous 'rest' arguments
+ Suppress stray STDOUT traffic from 'defined?'
+
+commit 8f11498019eb49a4dd8bf52c4361432ebb1175d5
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Sep 12 22:21:30 2007 -0700
+
+ Updated CI process.
+
+ * Changed bin/ci to generate an exclude file per file put
+ in .spec directory.
+ * Generated CI spec excludes files.
+ * Updated .gitignore to not exclude .spec directory.
+ * Moved the critical excludes file to spec/excludes.txt
+
+commit b736263ff325efabb907f300c1c69a2e63bd5620
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Sep 12 22:21:30 2007 -0700
+
+ Updated CI process.
+
+ * Changed bin/ci to generate an exclude file per file put
+ in .spec directory.
+ * Generated CI spec excludes files.
+ * Updated .gitignore to not exclude .spec directory.
+ * Moved the critical excludes file to spec/excludes.txt
+
+commit 58ff9428a2a20e93e3682f834e32f754ed2c47d4
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Sep 12 18:39:48 2007 -0700
+
+ Fixed specs failing MRI.
+
+commit f54b1dffb9372e5cb1c71d93c67f2407fce0a1d0
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Sep 12 17:17:46 2007 -0700
+
+ Fixes to specs failing MRI for hash, float, fixnum, file, enumerable.
+
+commit e1d359eec451a69deb67ffdedd09b86d00774cc2
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Sep 12 14:34:50 2007 -0700
+
+ Fixes to Hash specs based on Ruby version differences.
+
+commit ee5eec5d32bb42fbf549068905ddebe215fbcf70
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Sep 12 08:53:47 2007 -0700
+
+ Fixed failing specs in kernel, module, numeric, string.
+
+commit 4f0af824f132428762f1e06409ca16c1022867bc
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Sep 12 06:24:16 2007 -0700
+
+ Added #platform and #version guards for specs.
+
+commit 355c602704cd402a1d7cbadc9b4d8fae0b34f1f4
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Sep 11 00:35:03 2007 -0700
+
+ Misc changes to specs to cleanup after the breakup.
+
+commit 85336c6a83736b01d63b645baf0e7e18bb5ce569
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Sep 10 23:21:24 2007 -0700
+
+ Converted exception, kernel, struct, object specs.
+
+commit e10bc8cbbaa26123724dad9f97f44d82e8cbf600
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Sep 10 21:48:15 2007 -0700
+
+ Converted string and numeric specs.
+
+commit 8ec64d24811a7951756c840c98a66a7c7d2ae7c8
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Sep 10 18:35:18 2007 -0700
+
+ Converted array and module specs.
+
+commit 2849f4a41b3fbda6c626d934bbf3d7476ea31848
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Sep 10 15:22:36 2007 -0700
+
+ Converted enumerable, file, hash, process, regexp, thread.
+
+ Fixed bin/mkspec to remove '=' from string for file name.
+
+commit ffa5328aa8ed7ea079c0cc8b4228ababa5919cf6
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Sep 10 13:16:14 2007 -0700
+
+ Converted time and bignum specs.
+
+commit 7798952047471d28a8e12a796092c4df7ae002f2
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Sep 10 01:21:38 2007 -0700
+
+ Converted range, math, matchdata, integer, float, fixnum, comparable, io.
+
+commit 423d85f4a7eb4b40d2eea83a462f5c38c4a6aee3
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Sep 9 23:43:14 2007 -0700
+
+ Added dir and files for ENV. Converted true, false, nil, symbol, process.
+
+ Added .spec to .gitignore.
+
+commit 8274bdcd0c747c21806065feb743e7794231f48f
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Sep 9 22:40:37 2007 -0700
+
+ Converted kernel specs.
+
+commit bc1917d630d7938b62a866c3825dfa08e5ec99e1
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Sep 9 21:57:03 2007 -0700
+
+ Initial create of spec/core subdirectories and files.
+
+ Updated bin/mkspec to exclude Exception subclasses and
+ OptionParser (which is in Object.constants because of
+ the script requiring it). Also normalize TrueClass etc.
+ to directory 'true'.
+
+commit b941eceb681c57d23d35f952b11b2a2d3a1ea4dd
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Sep 13 01:00:22 2007 -0400
+
+ Add a minimal spec for the 'undef' keyword
+
+commit 3af389594f3828763a85d8eef65f773b183e1b46
+Author: Charles Nutter <headius@charles-nutters-computer.local>
+Date: Wed Sep 12 19:33:36 2007 -0500
+
+ Adding a bunch of default argument specs to language/def_spec.
+
+commit 6b4936e834a2814602be54f01e08dcdc1f9433b5
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Sep 9 16:58:35 2007 +0200
+
+ Another fix for multiple mock expectations on the same method.
+
+commit f686ff256289263eb473249dd734cf2214c41cc2
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Sun Sep 9 16:13:53 2007 +0200
+
+ Mocks now support multiple expectations of the same method with different arguments.
+
+commit 860e0d08adc8cdee9ac4d9ff3bd0e30d5d3aaa49
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Sep 8 02:36:19 2007 -0700
+
+ Added to critical-excludes and ci-excludes. bin/ci -f s -C runs to completion.
+
+ rake build:core compiled string.rb, so checking in core.rba.
+
+commit c9c79c910a57e5628d1743f3b440c0066875500e
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Sep 7 23:24:44 2007 -0700
+
+ Added Math methods using FFI.
+
+ Added Kernel.coerce_to and rewrote Float(), Integer(),
+ Array(), and String() with it. Float() needs to be fixed
+ to raise on strings like rb_cstr_to_dbl does.
+
+ Fixed -C options for bin/ci and bin/mspec.
+
+commit b8d8b8c8475fde1ce3519e29788a34780dffae8c
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Fri Sep 7 19:40:02 2007 -0700
+
+ Fixes String#<<
+
+ * Added taint if other has taint
+ * Fixed 10 spec failures
+
+commit b0b85547ab9dd16ba88a75c64a91c3ae0d079b27
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Fri Sep 7 15:36:44 2007 -0700
+
+ Added bus error to critical-excludes
+
+commit ed13a10112d0a262a48c8e5db7d1eaaa3e076e55
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Fri Sep 7 15:19:51 2007 -0700
+
+ String specs work in 1.8.6 MRI
+
+commit 6fc507c96e990139c311900c73c7e31447879071
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Sep 7 12:03:09 2007 -0700
+
+ Changed VERSION and RUBY_VERSION to shadow MRI (currently 1.8.6).
+
+ Added RBX_VERSION (currently 0.8.0). Updated loader -v to display
+ RBX_VERSION and RUBY_VERSION and truncated BUILDREV.
+
+ Enabled before|after(:all) for mini_rspec.
+
+commit 8ce602f80b35f5859c58730968a9a7053a87bd59
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Fri Sep 7 11:16:16 2007 -0400
+
+ Array#uniq, #uniq! simplified. 72 failures.
+
+commit 38f271a1b7d49074d8db9285553756fb75ffe78b
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Fri Sep 7 11:23:36 2007 -0700
+
+ define_attr added to subtend
+
+commit 843706d585334c30943c8bbdd3ef6ca22297d42d
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Fri Sep 7 10:48:51 2007 -0700
+
+ Added rb_cstr2inum to subtend
+
+commit 97a22e2144b623a62780995333d65986c98c4ba2
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Fri Sep 7 10:42:30 2007 -0700
+
+ Added rb_const_defined to subtend
+
+ It also seems that my fixture for require didn't make it in; adding that as well
+
+commit 0ac9ec2b0f381bf2fb3a36cd0b6f30748771e818
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Fri Sep 7 10:21:28 2007 -0700
+
+ Added check_*_type to subtend
+
+ * check_array_type
+ * check_string_type
+ * check_convert_type
+
+commit b2bf1c44fa7c2663e6fc0b27127aa4f5e38e073f
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Fri Sep 7 09:45:03 2007 -0700
+
+ Added rb_attr_get to subtend
+
+commit 81605662ab8acc5a50536f1fc613e7d24e142df1
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Fri Sep 7 00:27:38 2007 -0700
+
+ int2inum added to subtend as well as INT2NUM
+
+commit e80084e6b245173c17403891c65d86db1e6b3022
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Fri Sep 7 00:02:06 2007 -0700
+
+ rb_str_split added to subtend
+
+commit 90f1fa95825caa8b21e147248d1a7d999579b937
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Thu Sep 6 23:51:56 2007 -0700
+
+ rb_require in subtend added
+
+commit 5bb87f516b2a26f77a864a15636331102d6d8499
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Thu Sep 6 22:58:29 2007 -0700
+
+ rb_to_id added
+
+commit e0532b3fb62089da7b7362ec2232997878a1221d
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Thu Sep 6 22:31:30 2007 -0700
+
+ Added specs
+
+commit 75f7a1d2b37067f55099dd117e8fcd905baa748d
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Thu Sep 6 21:59:12 2007 -0700
+
+ string subtend fixes
+
+ * rb_str_cmp added
+ * rb_str_cat fixed with working spec
+
+commit f058cea3e5291c349f5b2b1cfbdad4d734240f95
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Thu Sep 6 21:34:52 2007 -0700
+
+ added rb_define_const
+
+commit ec7ca7f45ef32794afb919851e4bfd5e8d7aa46d
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Thu Sep 6 21:03:44 2007 -0700
+
+ rb_include_module added
+
+commit 5b3471544508e973ba6afbd16daf47f5796f8b30
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Thu Sep 6 20:41:09 2007 -0700
+
+ float_new added and some functions missing in ruby.h added
+
+commit cd2af0bae996a4addfe23baa0558125a8a5523e7
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Sep 6 17:09:06 2007 -0700
+
+ Fixed subtend. Added diagram of the context chain.
+
+ NMCs (NativeMethodContext) now use the proper context stack.
+ The stack maintenance was all screwed up when calling in and out of
+ native methods, which was the source of a few problems.
+
+commit da5f9e6f942c11d906760e952debae4d05b3d872
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Sep 4 22:26:48 2007 -0700
+
+ Added load-order dependency generation to rake build:(core|platform) task.
+
+ Added 'depends on:' declarations to kernel/platform and kernel/core files.
+
+ Updated ci-excludes.txt to reflect recent spec checkins.
+
+commit 7b1ca6f305e33b34a99e8c9e049843a76cceeca7
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sun Sep 2 21:29:49 2007 -0400
+
+ Add more 'alias' specs. Show singleton methods in 'public_methods' output.
+
+commit 5c0b5fcb2a0c9f47a04e6a5d5027484224d0a942
+Merge: 4896039... 01c2126...
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sun Sep 2 18:58:53 2007 -0400
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit 48960394ab7f36ccd1b18609677b40721c30d7a2
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sun Sep 2 18:57:29 2007 -0400
+
+ Add some 'alias' specs that fail on rbx and pass on MRI
+
+commit 01c2126b705327d99aa183d51dc014169e8b4e07
+Merge: 04602c6... b6d92ec...
+Author: Florian Gross <flgr@ccan.de>
+Date: Sun Sep 2 19:45:25 2007 +0200
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+ Conflicts:
+
+ spec/core/string_spec.rb
+
+commit 04602c6756a9199b64e7d909c01dc995b25fa8a7
+Author: Florian Gross <flgr@ccan.de>
+Date: Sun Sep 2 19:32:47 2007 +0200
+
+ * New specs for String#tr_s(!) and upcase(!)
+ * Improved specs for String#capitalize!, downcase(!) and swapcase
+
+commit c94f83b20f7b11dc48c523c84de59b9ac6f76cce
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Fri Aug 31 23:15:07 2007 -0400
+
+ Array#reverse_each, #rindex fixed and cleaned. 80 failures.
+
+commit f531f812f87283b950c62648e3cf08a7400c2779
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Fri Aug 31 22:03:05 2007 -0400
+
+ Array#replace fixed, specs. 88 failures. * Disabled specs for #initialize_copy which is private.
+
+commit 1656b8a04a40bc5a43adec88ffd1480d9da6ba28
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Fri Aug 31 11:50:56 2007 -0400
+
+ Array#reject, #reject!, specs fixed and cleaned * Added spec to check #reject returning Array and corrected implementation.
+
+commit e1c499c6feafc19788addd098a6da052904cb09c
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Fri Aug 31 11:25:45 2007 -0400
+
+ Fixed logic in Array#rassoc, specs. 91 failures.
+
+commit da79b116d3fdc5fd4cd04f5ad1ad44b2c269ad77
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Fri Aug 31 10:38:39 2007 -0400
+
+ Array#push checks frozenness, specs. 92 failures.
+
+commit 290aa6fe561453821f59be3fa92695d0b0d77c04
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Fri Aug 31 10:26:25 2007 -0400
+
+ Uncommented most of Array#pack specs. * float -> int conversions still hang both C and c and are therefore disabled.
+
+commit 885f2522244c1792f45260194aba085028d5c919
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Fri Aug 31 09:59:30 2007 -0400
+
+ Fixed logic errors in Array#assoc, Array#include? * Both, contrary to docs, compare elem == obj, not the other way around.
+
+commit d825038a409f4d931e80736e2de49ff0752857a9
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Aug 31 00:12:06 2007 -0700
+
+ bin/ci supports options for separately running spec files
+
+ Use bin/ci -s to separately process each spec file. Use
+ bin/ci -m to run all the spec files in a single VM process.
+ -s is the default for --create, but -m is the default for
+ everything else.
+
+ Updated ci-excludes.txt and critical-excludes.txt.
+
+commit 7dfe5cb7936051685a2c79effb6295b9aa179810
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Aug 30 23:44:28 2007 -0700
+
+ Updated ci-excludes.txt to only exclude failing specs.
+
+commit fb09f0a7a6969adffd8d99bf869eb50c91eef097
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu Aug 30 20:31:20 2007 -0400
+
+ Fix object and array specs that failed under MRI
+
+commit 6fb73244537b61a20538c1f3d5a060a40a358be5
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Thu Aug 30 17:08:12 2007 -0700
+
+ Fixed two typos in the subtend string specs
+
+commit 00256f41d4e3ebfcdafdc25e27bfbf4bc7d3de3f
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Aug 30 11:19:09 2007 -0400
+
+ Array#last fixed. 48 failures.
+
+commit 887d41c64c6bdff693f6ecd8d3078f8453669648
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Aug 30 11:02:27 2007 -0400
+
+ Array#insert, specs corrected. 49 failures.
+ * Fixed Array#insert
+ * Re-complianced frozenness specs for Array#inspect.
+
+commit 1bc536e1128bc76b1c9efae593340f67bdcb5fb5
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Aug 30 10:42:05 2007 -0400
+
+ Array#indexes correct implementation. 51 failures.
+ * Array#indexes and #indices is now correct although
+ both methods are deprecated in favour of #values_at.
+
+commit 5de09c707b1ce43bf689e8ded9ea19784e77a49e
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Aug 30 10:09:27 2007 -0400
+
+ Array#include? implemented. 53 failures.
+ * Replaced use of Enumerable#include?
+ * Re-complianced to a simpler Array#include? spec to
+ avoid implementation-dependedness.
+
+commit 14ca6c2533764eea508b24b0ec89475a7aae5e94
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Aug 30 09:32:40 2007 -0400
+
+ Array#hash spec compliance change. 54 failures.
+ * Disabled an Array#hash spec for Rubinius because it
+ relies too much on implementation details.
+
+commit fc4f392fa7fba88b36bfdec61db3acaa1f1fadc2
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Thu Aug 30 00:42:03 2007 -0400
+
+ Array#flatten, #flatten!. Improved Array specs. 384 ex, 55 failures.
+ * Array#flatten, #flatten! implementation improved, they
+ also work recursively now.
+ * Re-enabled Array#flatten, #flatten! specs and the
+ recursive test for Array#inspect.
+
+commit efeaa622994e9868b9324247b0ff1fd5743792ac
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Wed Aug 29 23:29:36 2007 -0700
+
+ A series of rb_str functions in subtend, plus fixes to some of the tests earlier committed
+
+commit e4f5281148799ed716065c489d384a42d208290d
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Wed Aug 29 19:16:26 2007 -0700
+
+ rb_str_append() added
+
+commit a05c376478f7407da4e0aa2a6a7e3de98176a63b
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Wed Aug 29 13:25:17 2007 -0700
+
+ Updated subtend array functions
+
+ * rb_ary_reverse() added
+ * tests added for rb_ary_join() and rb_ary_reverse()
+
+commit 779fb97c35b78b9749cbb118fcb555096957e4c6
+Merge: 2793a99... e17987e...
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Wed Aug 29 12:40:33 2007 -0700
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit 2793a9917f8f5cc2f0fc14ba605cec499532e680
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Wed Aug 29 12:40:21 2007 -0700
+
+ Slightly improved rb_ary_pop() test
+
+commit c196c60b6cd32c85b18bdab31ee000cf097309b5
+Author: Me <rue@meow.kittensoft.org>
+Date: Tue Aug 28 23:49:46 2007 -0400
+
+ Array#fill fixed, cleaned up. 375 examples, 59 failures.
+
+commit 7736413f262357479c2f3354a73533fd89b3c9a6
+Author: Yehuda Katz <wycats@gmail.com>
+Date: Tue Aug 28 17:54:28 2007 -0700
+
+ added rb_ary_join() to subtend
+
+ Trying to get tests working but it's hard to see if I'm correct without the ability to run them.
+ I'll take care of making sure there are passing tests as soon as I can.
+
+commit 0effcaf3e948d80ae3ad17b33f0483313d85cdbe
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Tue Aug 28 00:24:41 2007 -0400
+
+ Array#fetch, specs, slightly cleaned array.rb. 62 failures.
+ * Array#fetch uses to_int.
+ * Specs for Array#fetch check for correct block var.
+ * Removed extra comments from array.rb.
+ * Removed old implementations from array.rb.
+
+commit 51737d35c24f853a23e14f7a227138d4d0f6b457
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Aug 27 21:46:30 2007 -0700
+
+ Added failing File specs to ci-excludes
+
+commit a195970e2b2d34fa4388e6a72e91ada13b4b0d32
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Tue Aug 28 09:20:33 2007 +1000
+
+ Ensure exists? is prefixed by File.
+
+commit 804b6f3358c1bb73492beaa0e978d4df8dbac138
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Tue Aug 28 00:18:41 2007 +1000
+
+ Minor refactoring work on file spec.
+
+ Removed duplicated constant tests.
+
+commit ef18eaaaa1a79b964667900b19f3f10e1b67032b
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Aug 27 14:25:06 2007 -0700
+
+ Updates to enable bin/ci to run to completion.
+
+ Changed mini_rspec to not use File.open with a block to work
+ around IO#read failing to catch EOFError.
+
+ Commented out object_spec.rb specs that need to be completely redone.
+
+ Updated spec/reports exclude files to enable bin/ci to work.
+
+commit 505617b26829d5f489c4488ed934a6dc720f64f0
+Author: Florian Gross <flgr@ccan.de>
+Date: Sun Aug 26 22:53:42 2007 +0200
+
+ A few new specs for String#sum, #to_i, #to_s, #to_str, #tr and #tr!
+
+commit a6a24a97dce2a4072a6ea17e48259b76f0c3681a
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Aug 26 02:52:55 2007 -0400
+
+ Array#delete, Array#delete_at, Array#delete_if. 63 failures.
+ * Fixed the three delete* methods, they still need clean-up
+ * The specs reflect difference in frozen handling for rbx and r18
+
+commit 063f8c25d45e0934bca236ecb8af36dcb517187f
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Aug 26 02:26:15 2007 -0400
+
+ Array#concat fixed. 69 failures.
+ * Array#concat checks frozenness and cleaned up
+ * Improved specs for #concat
+
+commit a9f3593593948cf72d94712765d05bfcc27f2e78
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sun Aug 26 01:53:05 2007 -0400
+
+ Array#dup, Array#compact, Array#compact!. 71 failing.
+ * Array#dup properly returns subclass
+ * Array#compact(!) improved to pass specs
+
+commit 7be3bc12ea2e5432e442cb44103b4b1c6d981163
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Aug 25 23:57:38 2007 -0400
+
+ Array#clear, Array#frozen?. 372 examples, 73 failures. * Array#frozen? checks for sorting freezes * Array#clear fails on frozen Arrays
+
+commit 5c958242fe25f8a18cd8d315f81fb3db80dc7a40
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Aug 25 17:19:23 2007 -0400
+
+ Array#==, Array#assoc improvements. 75 failures
+ * Cleaned up Array#==
+ * Array#assoc processes correctly
+
+commit f82f8a300ee394f9f1038cc84de1cf6b132d7ef5
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Aug 25 16:24:22 2007 -0400
+
+ Array#* improved. 371 specs, 81 failures
+ * Array#* processes to_int and to_ary correctly and forwards
+ to #join when needed.
+
+commit 91e16f06d5b5b16f1fa7ffc1d3673d7f1c681587
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Aug 25 15:54:12 2007 -0400
+
+ Array#join can process recursive Arrays.
+ * Rubinius cannot create recursive Arrays so this is somewhat moot.
+
+commit 2d7427bb638f1af6d7437beed4beafde5274dbdf
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Aug 25 13:46:45 2007 -0400
+
+ Array#|, better Array#&. 371 examples, 86 failures.
+ * Array#& explicitly uses #eql? semantics
+ * Cleaned up Array#|, uses to_ary
+
+commit 2b8707466f763662d52efaeab71b4789b132bb40
+Merge: c61b1e5... 76be87f...
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Aug 25 12:41:06 2007 -0400
+
+ Merge branch 'array'
+
+commit 76be87f74d352d79425e9c46d3df55678257fda9
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Aug 25 12:28:32 2007 -0400
+
+ Array#<< improvements. 371 examples, 89 failures.
+ * Specced and fixed resizing bug in Array#<<
+
+commit c61b1e54cc11c297b9e9a9eca70cb6a354ed21d9
+Merge: 3618a8b... c6cc98f...
+Author: Pedro Del Gallego Vida <pedro@la-vaca-roja.(none)>
+Date: Sat Aug 25 18:16:45 2007 +0200
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit 3618a8bc588588ef8fb0dcc4753bc42606b86c13
+Author: Pedro Del Gallego Vida <pedro@la-vaca-roja.(none)>
+Date: Sat Aug 25 18:16:05 2007 +0200
+
+ Update object specs
+ Update the object_spec.rb file
+ * add more specs
+ * refactor using it_behave_like
+
+commit 294e5aacda8a74a9d8f57d05bb433f2fadcd08f1
+Author: Eero Saynatkari <rubinius@projects.kittensoft.org>
+Date: Sat Aug 25 12:07:06 2007 -0400
+
+ Array#[], modified parts of array_spec. 369 examples, 90 failures.
+ * Array#[] passes its specs
+ * Disabled some parts of array_spec while fixing Array. These
+ will be re-enabled as soon as possible.
+
+commit 1369465aefcd1d50ddd268ba9af968c62137e2b2
+Author: Pedro Del Gallego Vida <pedro@la-vaca-roja.(none)>
+Date: Fri Aug 24 11:14:20 2007 +0200
+
+ Array#new correct implementation
+
+commit e321427a52878ef9d9c7c04aa7c3c4f1e3a6c940
+Merge: bff7c05... 69c0407...
+Author: Pedro Del Gallego Vida <pedro@la-vaca-roja.(none)>
+Date: Fri Aug 24 11:14:20 2007 +0200
+
+ Merge branch 'master' of git@git.rubini.us:code
+
+commit 07c7f93a64fc37f3cf94a0a2c272468d015a7fb3
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Aug 23 21:18:13 2007 -0700
+
+ Converted Regexp specs to new describe style.
+
+ Fixed String specs to be compatible with bin/completeness.
+ Fixed bin/completeness to use dotted reporter instead of CI
+ reporter since the latter no longer outputs summary info.
+
+commit 6776e1478fa7e78a0944a1ee59c55c3839f51ea4
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Fri Aug 24 13:14:47 2007 +1000
+
+ Added implementation of File.split and updated specs.
+
+commit 4053b9076b4b996f544095a75317453967723faa
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Wed Aug 22 21:58:13 2007 +1000
+
+ Added spec for mocking methods on a class
+
+commit 54cae1196db08f6a734c35079db8df62e491f300
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Thu Aug 23 17:38:40 2007 -0700
+
+ Added more IO stuff and platform methods.
+
+ I'm still a little unhappy with the input buffer situation.
+ (ie, there is none.)
+
+ Adds IO#sysread and IO#syswrite as well as a bunch of POSIX stuff.
+
+commit 12a755004c0a8a0319212965da61385738166f98
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Tue Aug 21 12:44:48 2007 -0700
+
+ Beginnings of ftools spec, fix backtrace generation.
+
+ Backtraces were failing to be properly built if the sender was a Block.
+
+commit bff7c05ce12c79ef111422ecf4525f1a65e7a5f0
+Author: Pedro Del Gallego Vida <pedro@la-vaca-roja.(none)>
+Date: Fri Aug 24 01:15:41 2007 +0200
+
+ More specs dor enumerable
+ * add inject, min, grep, find, detect, find_all, select
+
+commit ed9a8fefcc384bb6548a7f66bbafb97192ec8fd3
+Merge: 4ef0b9f... 8dd800e...
+Author: Arthur Schreiber <schreiber.arthur@gmail.com>
+Date: Thu Aug 23 11:01:25 2007 +0200
+
+ Merge branch 'specs'
+
+commit 8dd800e8189f616dc54390c0ebf96c331de41230
+Author: schreiber.arthur@gmail.com <arthur@arthur-desktop.(none)>
+Date: Thu Aug 23 10:45:56 2007 +0200
+
+ * Some more Module Specs.
+
+commit 0f414f56f9050d86011df75e7fd23428fe378996
+Author: schreiber.arthur@gmail.com <arthur@arthur-desktop.(none)>
+Date: Thu Aug 23 10:44:59 2007 +0200
+
+ * Added :count => :any option to mock expectations
+
+commit 4ef0b9feddfebfd1b6177fce6e3a1a4077f4f098
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Aug 22 23:10:22 2007 -0700
+
+ Updated exclusion list for CI specs.
+ Updated rake spec:ci task.
+ Changed ci spec run action to execute all specs in one process.
+ Added guard on file specs to prevent compilation exception.
+
+commit c3b61b239fa6a02327e5651513986d998d826eaf
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Aug 22 21:48:08 2007 -0700
+
+ Updated CI spec process to exclude specs failing on compilation.
+
+ Added critical failures to enable running especially spec/core.
+ Added failure guards to struct specs.
+
+commit f339a284c66357bc52749e5fe9c0d59bbbdc7ade
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Aug 20 22:31:56 2007 -0700
+
+ Fleshed out bin/ci constructs for running specs.
+
+commit 4f750d59adfff6c1751372c0d2853778dc7ae16d
+Author: Pedro Del Gallego Vida <pedro@la-vaca-roja.(none)>
+Date: Tue Aug 21 22:40:33 2007 +0200
+
+ * more enumerable specs
+ * refactor to it_behaves_like
+
+commit 3c79d5cf67b40b945602d5c5fa77589e0d7bae2c
+Author: Pedro Del Gallego Vida <pedro@la-vaca-roja.(none)>
+Date: Tue Aug 21 20:52:16 2007 +0200
+
+ update file_spec.rb
+
+commit b2a64089bffe5afb9148a665ecb6e70c3bc62b67
+Merge: 6865b97... 1b6a8a1...
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Aug 19 16:22:53 2007 -0700
+
+ merge 1b6a8a157
+
+commit bf54767922eb8d494c683ed8d57c6ffb5164fc29
+Merge: 6c6032e... 37d71c9...
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Aug 19 15:24:21 2007 -0700
+
+ merge from e83bcd022
+
+commit 05db33909c319231ac375812025ea2378710a299
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Sun Aug 19 21:26:15 2007 +1000
+
+ Add conditional when deleting a file in after(:each) block to prevent an exception if the file is missing.
+
+commit 138ab001175987cd38aff092a850e515745f9292
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Sun Aug 19 21:18:22 2007 +1000
+
+ Converted remaining context/specify spec's into describe/it, and followed class/method naming convention.
+
+commit 749b883d0260326573c581cc63eab67e1a4bc590
+Author: Marcus Crafter <crafterm@redartisan.com>
+Date: Sun Aug 19 20:23:11 2007 +1000
+
+ Added implementation of exists? blockdev? chardev? zero? size size? writable_real? executable_real? readable_real? unlink delete and chmod using ffi where needed. Specs for most of these methods existed already, added specs for those that weren't. Fixed a few typos.
+
+commit ddcb14f9f2311ec843a1f1f8d2b3fa868384ff0d
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Aug 18 23:19:32 2007 -0700
+
+ more misc changes to get ci specs working
+ added alias for File.exists? and File.exist?
+ added Dir.getcwd
+ added empty File.delete
+
+commit afb252fd6170ed051e97f1911e5f1200414ebf98
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Aug 18 22:20:10 2007 -0700
+
+ updated compiler specs.
+
+commit d0e6b658d9065b0fbc9180cd5d19139834f64f59
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Aug 18 21:04:18 2007 -0700
+
+ changes to support better CI specs
+ hat mini_rspec will take a filename as an exclude/include argument and read the actual excludes/includes from the file.
+ added that mspec will take -o FILE to use an alternate to STDOUT for the spec reporter output.
+ updated spec tasks. misc spec changes.
+ added naive implementation of IO#each.
+
+commit 541bcb521a8ee589c7d28c095ad7ee1489af42db
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Fri Aug 17 19:30:05 2007 -0700
+
+ Complete reorganization of bootstrap. Addition of kernel/platform. FFI fixed.
+
+ New restrictions for meta-programming in core bodies (not in methods).
+ kernel/platform is now where platform specific code, mainly related to FFI, lives.
+ A bunch of FFI bugs have been fixed and it should be working much better now.
+
+ FFI Note: you may now only specify :state as your first argument, and you must
+ leave it off when you call the method (rather than passing nil like before).
+
+commit af245dfbc80ff942de62408e70db7499a798fb0a
+Author: schreiber.arthur@gmail.com <arthur@arthur-desktop.(none)>
+Date: Tue Aug 14 01:30:09 2007 +0200
+
+ Forgot to add the autoloaded file for Module#autoload
+
+commit b946940f463028de067ef2e082c96fe431c94b0a
+Author: schreiber.arthur@gmail.com <arthur@arthur-desktop.(none)>
+Date: Tue Aug 14 01:09:10 2007 +0200
+
+ Updated Module Specs
+
+commit 6cd6aa53a5d20c78941442f7e367ef8c7aee17c2
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Aug 10 00:14:09 2007 +0000
+
+ converted array specs with a few edits.
+
+commit c075f7f70da2a029c69f3fff1f9caec419db64d5
+Author: Arthur <arthur@unknown>
+Date: Wed Aug 8 12:47:18 2007 +0000
+
+ fix a small typo
+
+commit c7262df9ee1c2544890b001574c8cb0f8ae26a75
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Aug 8 01:24:25 2007 +0000
+
+ converted exception specs. added #should_be_ancestor_of. use ExpectationNotMetError in mini_rspec like rspec does.
+
+commit f591e18978b73c508505db73f274f4bd69c372c5
+Author: Arthur <arthur@unknown>
+Date: Tue Aug 7 08:36:15 2007 +0000
+
+ * String#to_str specs should actually use String#to_str
+
+commit 80f69571c5378d6bbb2e7a118ada00db66226797
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Aug 7 06:47:53 2007 +0000
+
+ converted range specs.
+
+commit aca62d253a6b2df891ca4ec4b177ea95b621d636
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Aug 7 03:52:46 2007 +0000
+
+ fixed mini_rspec -e option, allows multiples. converted hash specs.
+
+commit 928c9a392102fa7b7945f332480a7477ec203467
+Author: Florian Gross <florgro@gmail.com>
+Date: Mon Aug 6 22:04:16 2007 +0000
+
+ New specs for String#swapcase(!), to_f, to_i, to_str, to_sym
+
+commit 6d0a6b0051a55af32743d9d98d6425489a622ebe
+Author: Pedro Del Gallego <pedro.delgallego@gmail.com>
+Date: Mon Aug 6 21:58:55 2007 +0000
+
+ * more specs for File.open. Specs for File.truncate
+
+commit 8b19b683a8593b4dd5024841d8023df827a44875
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Aug 5 23:39:36 2007 +0000
+
+ fixed completeness to not over match methods. converted comparable specs.
+
+commit c6f4d90df72b103884fa5470a433f5513d2c524d
+Author: Pedro Del Gallego <pedro.delgallego@gmail.com>
+Date: Sun Aug 5 22:52:17 2007 +0000
+
+ * more specs for File.open. Some of them are plataform dependent
+
+commit 38bfff9d014b90409e272ddf041dc63f53d48f5d
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Aug 5 22:45:31 2007 +0000
+
+ converted bignum specs. misc cleanup.
+
+commit 14890b68c447731417ce53ca2e4310175e39b440
+Author: Florian Gross <florgro@gmail.com>
+Date: Sun Aug 5 22:00:04 2007 +0000
+
+ Small spec fix
+
+commit b6c3cfca5cf1b2cb85dc216180ad21a6bf653a10
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Aug 5 17:20:32 2007 +0000
+
+ converted time specs. according to completeness, need to spec 9 more methods.
+
+commit 8829cf7e94ec0434f642fafa7dbf117a860045b9
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Aug 5 07:53:47 2007 +0000
+
+ more converted specs.
+
+commit 5bf174780e893b7ee9b82b6ca3964db7cad84e30
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Aug 5 07:08:08 2007 +0000
+
+ converted fixnum specs to describe per method.
+
+commit 88023701a88c1113e4874c193d26c6bf21fad383
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Aug 4 22:19:18 2007 +0000
+
+ misc noise cleanup in specs. use bin/completeness to find missing specs rather than warns.
+
+commit 3d960a021cb9ac2bdc2a204f94b4f024f3ef60a4
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Aug 4 22:06:12 2007 +0000
+
+ fixed completeness to pass correct spec example string for class methods. changed float specs to describe per method.
+
+commit 8a7abb5996e5bdf8b9d6c5884e0e0d8ae73d060e
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Aug 4 20:10:38 2007 +0000
+
+ beginning of a completeness reporter. use 'bin/completeness -t ruby' to report on the completeness of the specs against MRI. use 'bin/completeness' to report on the completeness of rbx relative to MRI. use -t target for other implementations. updated some specs to the 'describe Class#method' style.
+
+commit 463f13be4462e22bc3f4491a475658624c5832ab
+Author: Pedro Del Gallego <pedro.delgallego@gmail.com>
+Date: Sat Aug 4 17:09:25 2007 +0000
+
+ * fix some bugs in bignum_spec
+ * changed the File::Foo.shouid == bar assert to defined?(File::Foo).should == "constant". The specific value dependence on OS.
+
+commit 989d72394f1e175b058f55ccf3e60f09a2c76401
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Aug 4 04:15:01 2007 +0000
+
+ fixed mini_rspec specdox reporter to not output describe message until examples are executed. fixed specdox and dotted reporter to distinguish between errors and failures.
+
+commit f98fe7f211e5784a35e99643fb52c9350b20d7ae
+Author: Florian Gross <florgro@gmail.com>
+Date: Sat Aug 4 01:40:52 2007 +0000
+
+ More compatibility and a few small fixes
+
+commit c78ba9f96d7d4d229d6b1b1b11cf314fb5a0271d
+Author: Florian Gross <florgro@gmail.com>
+Date: Sat Aug 4 00:40:42 2007 +0000
+
+ Compatibility for USE_RSPEC=1, sanity, some clean-up
+
+commit 761d05b5cbd92339f9d02e65d005a65c5155618e
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri Aug 3 21:49:26 2007 +0000
+
+ * Compiler and Normalizer fixes for method definitions without bodies
+
+commit ad7abe4d61171f9650d08b277d45c7f680f37950
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Aug 3 17:07:19 2007 +0000
+
+ fixed mini_rspec shared behavior to be compatible with rspec.
+
+commit 1e1ccb902d11547e9f67db82c31a5898e6227d67
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Aug 3 16:39:07 2007 +0000
+
+ added an implementation of shared behavior for mini_rspec. altered Array#[] and Array#slice specs to use shared behavior.
+
+commit 7697b2ae3db6ed1d8697010a7e0f52f8e3587c8a
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Aug 2 20:41:43 2007 +0000
+
+ added SpecRunner class to mini_rspec to properly encapsulate behavior. added --example option to specify a regexp to match examples to execute.
+
+commit b80bb3d295d3648988b15a29553189f219d8ac0a
+Author: Pedro Del Gallego <pedro.delgallego@gmail.com>
+Date: Thu Aug 2 15:56:07 2007 +0000
+
+ * added specs for file_spec
+
+commit c277fd3de82678f055693422af19c3f45ffc2a88
+Author: Florian Gross <florgro@gmail.com>
+Date: Thu Aug 2 00:46:27 2007 +0000
+
+ mspec: Add -x option for excluding specs by RE
+
+commit aa53967c694ed7621aa1a8a8b542d067d9e58925
+Author: Florian Gross <florgro@gmail.com>
+Date: Thu Aug 2 00:05:53 2007 +0000
+
+ New specs for String#succ(!)
+
+commit eafa5b0fd43168b4ae649b145f9528f7deae3aa7
+Author: Florian Gross <florgro@gmail.com>
+Date: Wed Aug 1 22:59:38 2007 +0000
+
+ New specs for String#sub(!)
+
+commit 3406e64032251a2a9849da3f6c27d872dd339175
+Author: Florian Gross <florgro@gmail.com>
+Date: Wed Aug 1 22:17:18 2007 +0000
+
+ New specs for String#squeeze(!) and String#strip(!)
+
+commit fa4d66576528725085ef47cca27c5c85c55b3150
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Aug 1 21:08:26 2007 +0000
+
+ added Object#(public|private|protected)_methods and Module#(public|private|protected)_instance_methods. added Tuple#first, last.
+
+commit 4ca071ba4a48aa984308e0ba9448718a6e214d7a
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Jul 31 20:56:37 2007 +0000
+
+ Some Object#methods et al specs.
+
+commit 67be404ac0714ec01c1c92c77465915d90fd794b
+Author: Florian Gross <florgro@gmail.com>
+Date: Tue Jul 31 18:41:54 2007 +0000
+
+ Renamed variables_spec.rb to assignment_spec.rb
+
+commit c0187db3e51297dfffabebe9acb6d6321bd04578
+Author: Florian Gross <florgro@gmail.com>
+Date: Tue Jul 31 18:22:41 2007 +0000
+
+ New specs for String#split
+
+commit 06cb5ab7c39866c99bb8d9a5fbb678f2f8a19cf2
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jul 22 06:47:54 2007 +0000
+
+ Removed the .rbc files from externals dir and set svn:ignore. Added Tuple specs, fixed a couple small problems with Tuple. Modified mspec to pass -I, -r to the target; added -n RUBY_NAME to affect which specs are run. Added hashi dir as an experiment to implement a bootstrap that could be run on e.g. MRI or JRuby to allow the core libs to be run and tested against the specs.
+
+commit 567d4f710bc232fc9223972e22a7d92e4abe940d
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Tue Jul 17 17:49:53 2007 +0000
+
+ Stack allocated lvars, GC fixes, compiler changes, oh my!
+
+ This is a biggy (too big in fact). It started as a change to allow
+ arguments to be accessed directly from the stack, and turned into a
+ monster.
+
+ Arguments and some lvars can now be accessed directly from the stack,
+ making them cheaper to create and use. This turned out to expose
+ a large number of bugs in the VM related to stack access, as well
+ as some in the GC.
+
+ The big GC change here is that the mark/sweep GC is actually run now,
+ as opposed to before when it would just allocated more and more memory
+ (the source of memory issues I suspect).
+
+commit 564ac024e14a790f4a3d257ddf1d9fa0cb93ee3b
+Author: Florian Gross <florgro@gmail.com>
+Date: Tue Jul 17 00:20:52 2007 +0000
+
+ $~ specs all over the place;
+ Revised % format string specs (match MRI trunk);
+ Revised hex and oct specs (match MRI trunk);
+ Merged slice together with [];
+ New specs for scan and slice!
+
+commit 2389eb4b36d86732dbb621be1cad3edca0e36aa5
+Author: Pedro Del Gallego <pedro.delgallego@gmail.com>
+Date: Mon Jul 16 09:26:20 2007 +0000
+
+ * added specs for file_spec
+
+commit a6453b6184353633d14c271533c2e2af7a6c4b12
+Author: Florian Gross <florgro@gmail.com>
+Date: Sun Jul 15 17:53:58 2007 +0000
+
+ A few specs for char numbers outside of 0..255;
+ A few specs for modifying strings while iterating;
+ New specs for hex, index, initialize(_copy), ljust, lstrip(!), match, next(!), oct, replace, reverse(!), rindex, rjust, rstrip(!);
+ Small additions, fixes & refactoring
+
+commit 217dd5dae127c146559dd1512edac23a94565ae9
+Author: Florian Gross <florgro@gmail.com>
+Date: Sat Jul 14 23:52:52 2007 +0000
+
+ Taintedness specs all over the place;
+ String subclass specs for String#%;
+ Range subclass specs for access methods;
+ str[idx, count] = str specs (contributed by John Lam);
+ New cases for capitalize(!) / center / gsub(!);
+ Refactoring
+
+commit b8b0c3dd380335260c3870934ca51dce736ce15d
+Author: Florian Gross <florgro@gmail.com>
+Date: Sat Jul 14 23:39:38 2007 +0000
+
+ Added custom range subclass cases for access methods
+
+commit 558552ec549fd605bed2c8f5c384e8c944e780a2
+Author: Pedro Del Gallego <pedro.delgallego@gmail.com>
+Date: Fri Jul 13 14:40:15 2007 +0000
+
+ * added cases for file_spec
+
+commit 5218708c630bd8a631522a00aa6cba4e91cbec54
+Author: Pedro Del Gallego <pedro.delgallego@gmail.com>
+Date: Fri Jul 13 13:36:53 2007 +0000
+
+ * added cases for file_spec
+ * refactoring numeric_spec.rb
+
+commit 3e9dbc15a81950e55a15a7fcca0ab04a5fd5353f
+Author: Florian Gross <florgro@gmail.com>
+Date: Wed Jul 11 22:09:49 2007 +0000
+
+ Add specs for to_* calls having correct semantics with method_missing() and respond_to?();
+ Small clean up
+
+commit 23961f46af6f74d2d6b9019972e451a5ae12b728
+Author: Florian Gross <florgro@gmail.com>
+Date: Wed Jul 11 22:00:26 2007 +0000
+
+ Add specs for to_* calls having correct semantics with method_missing() and respond_to?();
+ Removed a few duplicate specs (probably resulting from a mismerge)
+
+commit 639c64ca0965ff79401989ca7dbde862815f13fb
+Author: Florian Gross <florgro@gmail.com>
+Date: Wed Jul 11 21:09:33 2007 +0000
+
+ Add specs for to_* calls having correct semantics with method_missing() and respond_to?();
+ Fixed String#%'s %E/e/f/G/g and %b/d/i/o/u/X/x specs to verify Kernel#Float / Kernel#Integer semantics instead of to_f / to_i ones
+
+commit e97879670bbc8425810a3c83f15a523066899a89
+Author: Arthur <arthur@unknown>
+Date: Tue Jul 10 20:04:48 2007 +0000
+
+ * fix a typo
+
+commit 1e8890613a215c61ef90629b8b6023ac4612c499
+Author: Arthur <arthur@unknown>
+Date: Tue Jul 10 20:03:44 2007 +0000
+
+ * Update Symbol Specs to the new format.
+ * make Symbol#to_int show a warning as in MRI.
+
+commit 1262f24460463628c7cc4e275b7c814048937b57
+Author: Florian Gross <florgro@gmail.com>
+Date: Tue Jul 10 18:28:06 2007 +0000
+
+ New specs for gsub() without block
+
+commit 0ba87f6edc183385551e4cf8c05212fadaf36427
+Author: Florian Gross <florgro@gmail.com>
+Date: Tue Jul 10 16:48:46 2007 +0000
+
+ New specs for capitalize, chomp, concat, crypt, eql?;
+ Added missing methods chop, chop!, count;
+ Small improvements and refactoring
+
+commit 226942caef6bd217a13dc235a89c5ccf4a18f98b
+Author: Florian Gross <florgro@gmail.com>
+Date: Mon Jul 9 21:29:19 2007 +0000
+
+ Strings specs for letters c through e
+
+commit e41c027537f1e4f8ea4b8b5b6fe90df9a21e3aff
+Author: Pedro Del Gallego <pedro.delgallego@gmail.com>
+Date: Fri Jul 6 17:13:39 2007 +0000
+
+ * added 62 cases for file_spec
+ * remove a bug from obejct#method_missing_spec that break the specs
+
+commit 06d2fd71b847e139a39ab3b7a132ab041a8d4c1e
+Author: Pedro Del Gallego <pedro.delgallego@gmail.com>
+Date: Wed Jul 4 16:05:21 2007 +0000
+
+ * add 84 cases to the numeric_spec.rb
+
+commit b8d334f575322c65932279346bba61caead61555
+Author: Pedro Del Gallego <pedro.delgallego@gmail.com>
+Date: Wed Jul 4 10:28:27 2007 +0000
+
+ * 14 cases for Object.method_missing
+ * Add File#atime, File.atime, File#ctime, File.ctime, File.delete, File.executable?, File.executable?
+
+commit f2276130c4bf1894ffb6efb451203dcbfe9322bb
+Author: Charles Nutter <charles.nutter@sun.com>
+Date: Wed Jul 4 08:48:57 2007 +0000
+
+ Added a spec for Process::times...it's not great, but it's something.
+
+commit f71bb57b3fc69c35d34abdb9959e27efb71bbdff
+Author: Charles Nutter <charles.nutter@sun.com>
+Date: Wed Jul 4 06:14:06 2007 +0000
+
+ Fixes for #150; handle Time - Time correctly, don't assume it's a number of seconds.
+
+commit 895f1abdc0bfcdb213f97067704b1bb87a7e6d17
+Author: Florian Gross <florgro@gmail.com>
+Date: Tue Jul 3 17:01:27 2007 +0000
+
+ New specs for casecmp
+
+commit 2aa7cb37925cd92c3b23d4a33a6d7bc7c2b66737
+Author: Florian Gross <florgro@gmail.com>
+Date: Tue Jul 3 14:07:56 2007 +0000
+
+ New #[] and #[]=, capitalize and casecmp specs;
+ Converted "should work" messages to "works" using a few regular expressions -- I'm still going through the file so bad replacements (if any) will be fixed
+
+commit 719ff3b8959d93d7da8165d6e5b44989afde92d7
+Author: Pedro Del Gallego <pedro.delgallego@gmail.com>
+Date: Tue Jul 3 00:33:59 2007 +0000
+
+ * new NoMethodErro_spec.rb
+
+commit 5363324044fdc1457cfbf1b738dd931d3255b191
+Author: Florian Gross <florgro@gmail.com>
+Date: Mon Jul 2 23:58:09 2007 +0000
+
+ Some more new specs
+
+commit fa1b3694e366bf087a8d1ac107257c38ce447251
+Author: Pedro Del Gallego <pedro.delgallego@gmail.com>
+Date: Mon Jul 2 20:29:20 2007 +0000
+
+ * Add to_s with a base spec
+ * Change foo.aMethod.to_s.should == "bar" with foo.aMethod.should_be_close(bar,TOLERANCE) because floats representation are plataform/implementation dependents, but not changed aBignum.to_s.should = ...
+
+commit 2a5c93afd4ddfef7c30de17c531f49849e9bb957
+Author: Pedro Del Gallego <pedro.delgallego@gmail.com>
+Date: Mon Jul 2 00:10:06 2007 +0000
+
+ * Add Float::Constant specs
+ * Change foo.aMethod.to_s.should == "bar" with foo.aMethod.should_be_close(bar,TOLERANCE) because floats representation are plataform/implementation dependents
+
+commit 7d3dcc24cb72d6548cf44d8519691f4cd7344801
+Author: Florian Gross <florgro@gmail.com>
+Date: Sun Jul 1 21:00:37 2007 +0000
+
+ Heavily extended and refactored String#% specs;
+ some cleanup
+
+commit 79ce6628df39d20d03efcd715ea42ba70ae9f03e
+Author: Florian Gross <florgro@gmail.com>
+Date: Sun Jul 1 20:59:38 2007 +0000
+
+ Add support for MRI as :mri for failure() and similar methods
+
+commit 7e43cd858c0380aaf17dd7bacd8a24cef96bb309
+Author: Florian Gross <florgro@gmail.com>
+Date: Sun Jul 1 17:11:08 2007 +0000
+
+ rindex terror specs
+
+commit e5b7cf88092cf59357124e3d8f35bc19f8ee589a
+Author: Florian Gross <florgro@gmail.com>
+Date: Sun Jul 1 01:14:09 2007 +0000
+
+ Added a few new format specs (Most of these should probably be moved to Kernel::format later)
+
+commit a533693824608a03ab6a66882b607fecab3a3a75
+Author: Florian Gross <florgro@gmail.com>
+Date: Sat Jun 30 00:29:29 2007 +0000
+
+ A few more specs, clean up and compatibility with MRI 1.9 head
+
+commit b9e8936562ec23db63879f9c127dadeadd8adf2e
+Author: Florian Gross <florgro@gmail.com>
+Date: Fri Jun 29 02:41:40 2007 +0000
+
+ New specs for [], default(), delete(), shift(), yield argument count semantics and modifying hashes while iterating over them;
+ Also removed some warnings and cleaned up the code a bit
+
+commit 72d1b106c1de4b00b9af184eb890e950854a9c77
+Author: Florian Gross <florgro@gmail.com>
+Date: Fri Jun 29 02:37:55 2007 +0000
+
+ Adding spec for join passing along separator argument for nested arrays
+
+commit 6e2848b7143cd0ae47a7b9ac632a567df7fd30fc
+Author: Florian Gross <florgro@gmail.com>
+Date: Fri Jun 29 02:35:09 2007 +0000
+
+ Adding message argument for should_raise()
+
+commit 31591886dde4bfd9b4e9de34c26960e45566b7ee
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jun 27 05:15:15 2007 +0000
+
+ Commiting (#147) math specs by pedro (modified for style, structure, and legibility).
+
+commit c7d623ee836363d0f3d443ba1c676ef0f86e34f7
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Jun 26 04:06:49 2007 +0000
+
+ enhanced spec:ci to take a target on the command line, invoke like: SPEC_TARGET=jruby rake spec:ci. removed deprecated #only and #except from spec_helper.
+
+commit c13a588cb7e37c20ce7e8a9430d854cc51be7b00
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jun 25 08:28:10 2007 +0000
+
+ misc cleanups to specs to eliminate interaction effects.
+
+commit 280296208bd699cb574c662f92b585519a739c6b
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jun 25 02:42:07 2007 +0000
+
+ removed extension dir and added README for subtend specs.
+
+commit 7ce8d4addc77ea9da0daf3ea3dc1fc7b00030b29
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jun 25 02:30:15 2007 +0000
+
+ reorganized subtend specs. stragglers from spec/language reorg.
+
+commit d499ebfd98d8fb9bd50c0f7a46b3587aa1f28c8a
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jun 24 19:44:12 2007 +0000
+
+ significant reorganization of spec/language, added files that correspond to the desired layout of this section. there remains to be done a large amount of cleanup for existing language specs, and especially spec description strings.
+
+commit bc0d0965bb5a6b3966884b63edd37218359aa46d
+Author: Tilman <tilman@unknown>
+Date: Sun Jun 24 14:52:15 2007 +0000
+
+ Extended specs for File.join.
+
+commit a4e189f31a8c256821564041c4dbce2a832ba78e
+Author: Tilman <tilman@unknown>
+Date: Sun Jun 24 13:00:33 2007 +0000
+
+ Fixed a typo.
+
+commit ecaf1abafeb69994b05463742ca4220797f62ad3
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Fri Jun 22 23:25:13 2007 +0000
+
+ * Array specs by Josh Susser (hasmanyjosh)
+
+commit 1c8987b6195d356126ebc3cc9c21e473be915240
+Author: Arthur <arthur@unknown>
+Date: Fri Jun 22 21:22:30 2007 +0000
+
+ * Heavily extended String specs
+
+commit 663e2cbe0c026aa7e792b6aab682301570ccd766
+Author: Tilman <tilman@unknown>
+Date: Fri Jun 22 08:59:42 2007 +0000
+
+ Added specs for Time#dup.
+
+commit f9dd8149bd7d794e8686053e8dca010ea71eacba
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jun 21 06:47:57 2007 +0000
+
+ added methods #compliant, #noncompliant, #extension, #failure. Please read the comments for them in spec_helper.rb. #only, #except are deprecated but have not yet been removed.
+
+commit 389b3cef5176b0244f78294a3c820cc84797e0df
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jun 21 05:22:36 2007 +0000
+
+ added -f i (immediate) reporter for mini_rspec.
+
+commit d113f855e32d09abaa74bb0ccafa4a65ffce66b1
+Author: Florian Gross <florgro@gmail.com>
+Date: Wed Jun 20 21:59:27 2007 +0000
+
+ A few more hash order consistency specs
+
+commit 8df2a605937c29b0ca4e89fae37b725e7244fbee
+Author: Florian Gross <florgro@gmail.com>
+Date: Wed Jun 20 21:24:01 2007 +0000
+
+ Small spec improvements all over the place
+
+commit 5c2472584637b6f5accaaf2450d4c23904b0bbd7
+Author: Florian Gross <florgro@gmail.com>
+Date: Tue Jun 19 23:06:33 2007 +0000
+
+ Small tweaks to let us run specs against Ruby 1.9
+
+commit 18b06659146f00f0ecf72846c445b03268305328
+Author: Florian Gross <florgro@gmail.com>
+Date: Tue Jun 19 15:56:02 2007 +0000
+
+ More specs, including frozen hash ones
+
+commit 3cc17a6c7d4c4e4d13b67da4e2bd8937160916f0
+Author: Florian Gross <florgro@gmail.com>
+Date: Tue Jun 19 15:02:21 2007 +0000
+
+ Specs for methods involving to_hash and more
+
+commit a33e72ba27dc7c80fb7c3947d4fe86521b8987e0
+Author: Florian Gross <florgro@gmail.com>
+Date: Tue Jun 19 13:35:14 2007 +0000
+
+ New specs for each, each_key, each_pair, each_value, fetch, has_value?, index, initialize_copy, inspect, invert, key?, keys, length, merge, merge!, rehash, to_a and value?
+
+commit f5ec55b0233fd6b7825b04afc6157caac0c529ce
+Author: Florian Gross <florgro@gmail.com>
+Date: Tue Jun 19 13:18:33 2007 +0000
+
+ Using except(:rbx) for "inspect should handle recursive arrays" instead of commenting it out
+
+commit 7fec6cb5534d22dbfa4dd245cf3b0c0776b3b465
+Author: Florian Gross <florgro@gmail.com>
+Date: Tue Jun 19 00:56:35 2007 +0000
+
+ New specs for Hash.new, #==, #[], #[]=, #clear, #default=, #delete and #empty?
+
+commit e3085af8e97177f8b7e4ff1c2aad2f306a4f474d
+Author: Florian Gross <florgro@gmail.com>
+Date: Tue Jun 19 00:53:43 2007 +0000
+
+ Specs for how Array#uniq should use eql?() and hash()
+
+commit 01799e95c71453e8dff9730dd283bf76989e75e5
+Author: Florian Gross <florgro@gmail.com>
+Date: Mon Jun 18 11:22:47 2007 +0000
+
+ Adding new specs from rue plus more. The diff is a bit chaotic, but everything should be OK.
+
+commit 890deed76153d05c6874b46ec29c474eb4e36e41
+Author: Florian Gross <florgro@gmail.com>
+Date: Mon Jun 18 11:12:04 2007 +0000
+
+ Moving only() to general spec helpers, adding expect()
+
+commit bf89af6c3b632b88e3cc74bead42f21561da58a7
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jun 18 07:19:15 2007 +0000
+
+ (Jason Toy) added some specs for File (with some modifications) (#130).
+
+commit a7a6d8e336f8d331c60e973fb8f9e0aac1fb61ac
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jun 18 05:20:29 2007 +0000
+
+ (Jason Toy) initial specs for YAML (#123).
+
+commit 4c2f70040050e35da28a8684296f913a3dd4a198
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jun 18 05:14:41 2007 +0000
+
+ (nitay) patch for Bignum#size (#120).
+
+commit b1e57c9c718acfc7f1e61ae1fb60f10b918f8e5c
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jun 18 01:40:29 2007 +0000
+
+ Range specs and code from Ryan Mulligan (#141).
+
+commit 9fa70f392bf83c55d67e682c36d9ebd247cff62c
+Author: Florian Gross <florgro@gmail.com>
+Date: Sun Jun 17 22:02:48 2007 +0000
+
+ New specs from Ryan Mulligan (#140)
+
+commit 64c970bddeb754115ed193d2f786c797ea90dab3
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jun 17 20:08:40 2007 +0000
+
+ reorganized specs to put implementation-specific extensions in a subdirectory within the logical division of the specs into core, library, language.
+
+commit 8d437f0f63d4d3f9eea6e4436a28f437e6e76053
+Author: Florian Gross <florgro@gmail.com>
+Date: Sun Jun 17 19:39:37 2007 +0000
+
+ Initial work on hash specs -- a few new cases and a bit of reorganization
+
+commit d8222049004ba0d6ec51db0c962b5200bb180aec
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jun 17 03:53:45 2007 +0000
+
+ (yipstar) module specs for undef_method, define_method, remove_method. all pass MRI.
+
+commit 0162cfe6a443ded5d6c8e01a866f5a8d1fbce901
+Author: Florian Gross <florgro@gmail.com>
+Date: Sat Jun 16 23:11:31 2007 +0000
+
+ Added specs for * / join / to_s with recursive arrays
+
+commit 0744e57d7860b9f6eefcc8e14962d7ee777d2ec4
+Author: Florian Gross <florgro@gmail.com>
+Date: Sat Jun 16 22:05:32 2007 +0000
+
+ A few more specs for array sub classes with to_ary [ruby-core:11472]
+
+commit 07e0df5111c8ceeda83e50ef434948ee17e92aae
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jun 16 19:09:19 2007 +0000
+
+ commented out specs that cause the VM to seriously misbehave allocating memory without bound or causing SIGBUS. currently 434 examples, 130 failures at r1357 on MBP.
+
+commit 2abc6e6dc5df662e8f0587636bd1cf3573e39f28
+Author: Tilman <tilman@unknown>
+Date: Sat Jun 16 16:41:07 2007 +0000
+
+ Don't use timezone names that aren't portable.
+ Instead, specify timezones by their standard name and the offset from GMT.
+ This makes the specs pass on FreeBSD w/ MRI.
+
+commit d4106115c2ca9a4678b7060b6ac0091d66312624
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jun 16 08:20:29 2007 +0000
+
+ a first, big step to making the entire set of specs more agnostic about the ruby implementation/engine by, paradoxically, qualifying certain specs to only run under certain ruby engines. removed incompatible and rubinius directories. folded in specs where appropriate and moved spec files (e.g. bytearray, tuple, compression) into the appropriate directories. the spec/parser and spec/compiler dividing line is not clearly defined given the range of types of implementations and perhaps should be merged.
+
+commit 3de0340e693e20b5e32c643f1f4dae7e1943e077
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jun 16 07:07:41 2007 +0000
+
+ migrated more tests from shotgun-tests to specs.
+
+commit 369813306643d98c277841c1e9b400f6b60d3316
+Author: Florian Gross <florgro@gmail.com>
+Date: Fri Jun 15 22:43:05 2007 +0000
+
+ Add spec for elements returning odd stuff on <=>
+
+commit d70ab64def5ebdcb0e1946618b06e810270eb2aa
+Author: Florian Gross <florgro@gmail.com>
+Date: Fri Jun 15 22:17:03 2007 +0000
+
+ New specs for frozen arrays
+
+commit fda7128521254d2db2668fec55ef7ce9337ecf1a
+Author: Florian Gross <florgro@gmail.com>
+Date: Fri Jun 15 21:55:30 2007 +0000
+
+ More specs, mostly for array subclasses
+
+commit 2d57166d2ef2ff63f333b7ee0c196f5c4e10b8cf
+Author: Florian Gross <florgro@gmail.com>
+Date: Fri Jun 15 16:56:25 2007 +0000
+
+ More than five bazillion new specs including a ton for the very evil []= method (letters i through s)
+
+commit b905a952af41a96f72499750c4635b1352b237ba
+Author: Florian Gross <florgro@gmail.com>
+Date: Thu Jun 14 23:06:33 2007 +0000
+
+ A few more specs.
+
+commit e17aa3690f7dcd2dab346bf7def0dd26b38072f1
+Author: Florian Gross <florgro@gmail.com>
+Date: Thu Jun 14 22:16:54 2007 +0000
+
+ A bunch of new specs. Includes highly exotic corner cases.
+
+commit 7845d8928d138353ad03bd496d1800c03e82b538
+Author: Florian Gross <florgro@gmail.com>
+Date: Thu Jun 14 20:52:23 2007 +0000
+
+ Fix cleanup to work with symbolic method names like ==
+
+commit c677ac036baa847cef3de6a34b2b56c9fd09213c
+Author: Florian Gross <florgro@gmail.com>
+Date: Thu Jun 14 18:04:43 2007 +0000
+
+ Fixed the test for DATA to match MRI (it's only supposed to be defined when the main file contains __END__);
+ Added test for TOPLEVEL_BINDING
+
+commit 27b2767cd21b5a69ee10a2a629a68de64fd8ae5a
+Author: Florian Gross <florgro@gmail.com>
+Date: Thu Jun 14 18:03:26 2007 +0000
+
+ Introducing RUBY_NAME so we can spawn a new Ruby for things that can't be tested otherwise
+
+commit 4c4a96f51b4c06dd6896f996ae0e87a68152a3bc
+Author: Tilman <tilman@unknown>
+Date: Thu Jun 14 15:15:56 2007 +0000
+
+ Don't call Time.now multiple times when comparing their values.
+
+commit cde774be8b188f5870b1ee387b5e5fffd9948163
+Author: Tilman <tilman@unknown>
+Date: Thu Jun 14 15:00:17 2007 +0000
+
+ Fixed a typo.
+
+commit 6e51eee65c310255183d81d97a98be313ca68afc
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jun 11 04:07:03 2007 +0000
+
+ put guards on imcompatible specs to prevent ruby, jruby from running them. work around for Dir ** globbing being broken.
+
+commit 03dfae6b896a6b67ac6066e94284d992833afac5
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jun 11 02:00:25 2007 +0000
+
+ Added rake task spec:ci. added svn:executable for bin/mspec. enhanced readability of system command in mspec. added globbing across directories to mspec command.
+
+commit 96425667fbff044333c13c5a88c3b8cad156504f
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jun 10 21:45:52 2007 +0000
+
+ modified mini_rspec to include proper reporters. converted mspec to use ruby to generate a command line to run specs. mspec usage should be similar to spec: mspec spec/core spec/language/class_spec.rb will execute any spec/core/*_spec.rb plus spec/language/class_spec.rb and output a single summary of exceptions, examples, failures.
+
+commit 1075f2cf34a81c00a1b06d2474c78300ae013161
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Sun Jun 10 06:59:40 2007 +0000
+
+ Cleaned up MatchData, added support for accessing named groups.
+
+commit c678d79f125d67328e267001e5fe353f5ef38a2c
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Sat Jun 9 04:20:51 2007 +0000
+
+ * =~ for regexps to mini_rspec by dean (Ticket #136)
+ * Kernel.caller spec relocation for reliability
+
+commit 00eec364dbf3cef03915a68a359ed06b7e501553
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jun 8 07:10:56 2007 +0000
+
+ reintegrated String specs. all specs in core follow the convention that there is one file named <class>_spec.
+
+commit 9f9817bbb9fea5cc02eb280f01eb50e45ea03118
+Author: Arthur <arthur@unknown>
+Date: Thu Jun 7 23:49:47 2007 +0000
+
+ * String#replace, String#chop! and String#chop behave now as in MRI.
+ * Specs
+
+commit 129e492fcacf937473bb1c602176b48a352f8572
+Author: Arthur <arthur@unknown>
+Date: Thu Jun 7 23:39:00 2007 +0000
+
+ * Fix String#<=> when the given object is not a String.
+ * Fix String#to_sym for invalid symbols.
+ * String#to_i raises an error when invalid radix is given.
+ * More specs.
+
+commit 8d7a9e21874d9e44c63d17dd8f6832b942805707
+Author: Arthur <arthur@unknown>
+Date: Thu Jun 7 23:31:25 2007 +0000
+
+ * Make String#capitalize!, String#downcase! and String#upcase! check for the 'frozenness' of self
+ * Specs
+
+commit 10087a0c92217d1fbadfede9fdb0099c7eb195e6
+Author: Arthur <arthur@unknown>
+Date: Thu Jun 7 23:25:08 2007 +0000
+
+ * Spec for String#==
+
+commit e51ca54f6f4e9e3dabd48895fa2cb746fb0d3c17
+Author: Arthur <arthur@unknown>
+Date: Thu Jun 7 23:23:23 2007 +0000
+
+ * Fix String#=~ to work as in MRI.
+ * Specs
+
+commit d2c7d6e6bb624c23e994888b6a235022486e0c69
+Author: Arthur <arthur@unknown>
+Date: Thu Jun 7 23:16:35 2007 +0000
+
+ * Alias String#size to String#length.
+ * String#<< now correctly checks and converts (where applicable) arguments.
+ * String#<< raises an error if used on frozen string.
+ * Added extended specs for String#<<.
+
+commit c389493556e3394cce846698aa4fc6a67a5f4b40
+Author: Tilman <tilman@unknown>
+Date: Thu Jun 7 18:14:02 2007 +0000
+
+ Time#zone_offset and #zone_utc? are also in stdlib already.
+
+commit 7dcd86dca123edaec7edec2853cba2af27ca6d52
+Author: Tilman <tilman@unknown>
+Date: Thu Jun 7 18:07:59 2007 +0000
+
+ Moved the specs for Time methods that should be in stdlib rather than in core to spec/lib/time_spec.rb
+
+commit 953dfdfdea395ead465a9e19339d94f8b8c7d684
+Author: Hapk <hapk@unknown>
+Date: Thu Jun 7 17:36:20 2007 +0000
+
+ Added more specs for Array#pack: covering %#bB.
+
+commit 7aa84bc102ad6943aed46cbb357f233ac4b6d3f9
+Author: Florian Gross <florgro@gmail.com>
+Date: Thu Jun 7 16:47:03 2007 +0000
+
+ Make next with arg match MRI; Fixed typos and a copy&paste error
+
+commit 40c354444d6d7a2fd3aeb940c3bdcf6fd2a28940
+Author: Florian Gross <florgro@gmail.com>
+Date: Thu Jun 7 16:43:17 2007 +0000
+
+ Fixed to run on MRI (uses Object.const_get now)
+
+commit 95dd0ca698d0b1f79a11f4a556c171bea33ba176
+Author: Florian Gross <florgro@gmail.com>
+Date: Thu Jun 7 12:52:40 2007 +0000
+
+ Make behaviour match MRI, fixed copy&paste errors, did some refactoring
+
+commit 191d4e80406a6e4ffd08594ddeada47fb3219bf0
+Author: Florian Gross <florgro@gmail.com>
+Date: Thu Jun 7 01:41:08 2007 +0000
+
+ Fix chaos introduced in rev 1283
+
+commit e0f5184493607f7d9c216146207dc298893a744c
+Author: Florian Gross <florgro@gmail.com>
+Date: Thu Jun 7 01:28:55 2007 +0000
+
+ Fix Dir.chdir test on OS X (/tmp is symlinked to /private/tmp)
+
+commit a32062d6099556a93afee61f0b0000a28675fbb6
+Author: Tilman <tilman@unknown>
+Date: Wed Jun 6 21:44:00 2007 +0000
+
+ Deleted old specs.
+
+commit de7b3ecaa61c4d3514c8a3534805e9c8d0d1bb2f
+Author: Tilman <tilman@unknown>
+Date: Wed Jun 6 21:09:48 2007 +0000
+
+ Made the Time#to_a spec pass with any time zone, too. MRI passes all the specs for me now.
+
+commit ac1e295da36f23184e583f5e62a4909959550dbd
+Author: Tilman <tilman@unknown>
+Date: Wed Jun 6 21:06:32 2007 +0000
+
+ Fixed specs for Time#localtime, #gmtime and friends.
+
+commit 064a5967f5550cf4a2e10885ab2c9f22afa59da2
+Author: Tilman <tilman@unknown>
+Date: Wed Jun 6 20:56:41 2007 +0000
+
+ Fixed a few more Time specs. Instead of the imaginary time zone "PDT"
+ I'm now using Asia/Kuwait for some specs, because those lucky people
+ don't have DST, which means we don't have to flip the spec expectations
+ every 6 months.
+
+commit 8f6b6a5a5f8e62631a58061b6cdeeaff5654aeb0
+Author: Florian Gross <florgro@gmail.com>
+Date: Wed Jun 6 20:41:32 2007 +0000
+
+ module_function specs matched to MRI (module_function makes the instance methods private)
+
+commit 8cc656b95cb71ea476e787ce635df889090f8050
+Author: Tilman <tilman@unknown>
+Date: Wed Jun 6 20:41:02 2007 +0000
+
+ Fixed the Time#year spec. Using CET, too, since specc'ing using 1969 just feels wrong.
+
+commit ed7be9eaa73b8b1d85964f5c8fdfc250e2e8f62f
+Author: Tilman <tilman@unknown>
+Date: Wed Jun 6 20:38:43 2007 +0000
+
+ Oops, fixed a stupid error in r1281.
+
+commit e81d716092e0c6a8b52775ca2b9e0d44c4e3ef7f
+Author: Florian Gross <florgro@gmail.com>
+Date: Wed Jun 6 20:35:42 2007 +0000
+
+ Divmod matched to MRI (Special casing darwin on MRI because it doesn't raise FloatDomainError exceptions in some cases)
+
+commit fc5f461c47b7212f05f8699110e313aeab46d46d
+Author: Tilman <tilman@unknown>
+Date: Wed Jun 6 20:35:34 2007 +0000
+
+ Fixed the Time#hour, #min and #day specs.
+
+commit 6847a1e49ff4ad4dfe7c6e9e4d7352f92d72b4cd
+Author: Tilman <tilman@unknown>
+Date: Wed Jun 6 20:26:26 2007 +0000
+
+ Don't call Time#seconds as MRI doesn't have it and it doesn't seem to be needed anyway.
+
+commit b9a6ccc89d46ffa910c9bdc0fece9d013440872d
+Author: Tilman <tilman@unknown>
+Date: Wed Jun 6 20:14:15 2007 +0000
+
+ Time#asctime needs to use %e to print the day of the month.
+
+commit 7311fdf31481ba7a4373d5b007efb62c1f84c389
+Author: Tilman <tilman@unknown>
+Date: Wed Jun 6 20:03:30 2007 +0000
+
+ Hardcode output for Time.gm(...).inspect. This makes utctime superfluous for now.
+
+commit 229e7eefe7944df92ab2f84e553992fc0c868dbd
+Author: Tilman <tilman@unknown>
+Date: Wed Jun 6 19:19:38 2007 +0000
+
+ Fixed two more Time specs.
+
+commit 7f490d752ed97bfb9b830d24f4e2c2f44107c141
+Author: Tilman <tilman@unknown>
+Date: Wed Jun 6 18:41:47 2007 +0000
+
+ Make sure that with_timezone resets .
+
+commit 9c8d25c4d072510215407209fa52a06b85e95d35
+Author: Tilman <tilman@unknown>
+Date: Wed Jun 6 15:57:32 2007 +0000
+
+ Made the wday spec work with any timezone.
+
+commit 4882f8a676d5234339296d6a4489f3a7134cf5fd
+Author: Tilman <tilman@unknown>
+Date: Tue Jun 5 20:08:22 2007 +0000
+
+ Fixed a typo.
+
+commit fb63faa2165cafdc7907f173344638609f884833
+Author: Tilman <tilman@unknown>
+Date: Tue Jun 5 20:03:06 2007 +0000
+
+ Make sure that a GMT Time object returns true from gmt?
+
+commit deae4911588b555264f529e765dc5baf7a2c6e69
+Author: Tilman <tilman@unknown>
+Date: Tue Jun 5 20:00:45 2007 +0000
+
+ Use with_timezone for the gmtoff specs.
+
+commit 625dd8e366f846cf15165e323a719199627ae422
+Author: Tilman <tilman@unknown>
+Date: Tue Jun 5 19:47:29 2007 +0000
+
+ Added a helper method to temporarily override the TZ env var and fixed one of the specs by using it.
+
+commit b359eb74d41749919c6177ec2af6b5b516308bd8
+Author: Tilman <tilman@unknown>
+Date: Tue Jun 5 19:33:01 2007 +0000
+
+ Provided 'date' calls for coreutils' date program.
+ Checking for coreutils using RUBY_PLATFORM isn't the right way,
+ but it will do for now.
+
+commit b19c3c8d886885adc08ac69469792b14e47ba265
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Mon Jun 4 23:42:20 2007 +0000
+
+ * Improved Array#sort with additional spec by wycats (ticket #135)
+
+commit 8513e72a5af7596c8782ee45dff39607489dbf12
+Author: Tilman <tilman@unknown>
+Date: Mon Jun 4 16:39:53 2007 +0000
+
+ Ticket #132: Implemented ENV in core and removed read-only ENV from shotgun.
+
+commit 03d75a78855213f6267fb8f80f9c71d0a031641f
+Author: Mojombo <mojombo@unknown>
+Date: Sun Jun 3 20:49:41 2007 +0000
+
+ Implemented most of Time, updated time specs accordingly
+
+commit 36975b1b79abbd38de68223cdc8ecbca9ba0feee
+Author: Bremac <bremac@unknown>
+Date: Sun Jun 3 16:20:59 2007 +0000
+
+ Add Functions::abort, Functions::printf, and Functions::sprintf, and minimal specs. Closes ticket 87.
+
+commit 9d25d95a4aa3fd22d3f3a340427d40410488a770
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Sun Jun 3 12:41:49 2007 +0000
+
+ * Lots of new assignment specs by crafterm (Marcus Crafter) (Ticket #133)
+ * MatchData#inspect and #to_s now indicate it is an MD object
+ * -dc notes which file/method is being compiled
+
+commit ec0fb5beed68155c9e1ff67185cc2c8e4e474c04
+Author: Arthur <arthur@unknown>
+Date: Fri Jun 1 22:23:39 2007 +0000
+
+ * Fix String#[] and add some edge cases to the specs
+
+commit 068b48538ec574558ab787d59b14ebd2925f1126
+Author: Tilman <tilman@unknown>
+Date: Fri Jun 1 18:12:10 2007 +0000
+
+ Renamed module 'B' to something more meaningful to avoid name clashes.
+
+commit eb3de8af03d070b03216daa4fc0c2216d9d3e2a2
+Author: Arthur <arthur@unknown>
+Date: Fri Jun 1 18:09:52 2007 +0000
+
+ * Extend String#slice! specs
+
+commit db4775403d57ea29165165b9cbf0110739d91e2a
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri Jun 1 02:26:30 2007 +0000
+
+ * Ticket #128 - Patch by Marcus Crafter to enhance assignment specs
+
+commit 6098aa16357ce4261feb51bdf083c02442b1f074
+Author: Tilman <tilman@unknown>
+Date: Thu May 31 19:05:45 2007 +0000
+
+ Implemented rb_obj_alloc() and friends in subtend.
+ The spec for rb_class_new_instance() is still failing because of strange
+ subtend behavior.
+
+commit ae2c3cb4502ba9475111eeef10b2b70780a1b9f4
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Thu May 31 13:54:24 2007 +0000
+
+ * Fixed shotgun compile failure (possibly r1204)
+ * Various casts to avoid warnings
+
+commit d317d336412d0c1778d4c015dfe96287c44e1bd9
+Author: Hapk <hapk@unknown>
+Date: Thu May 31 11:12:02 2007 +0000
+
+ Added specs for "break", "next" and "redo" keywords.
+
+commit a33f801f8f142d997a553e41cde9f5b10d75ee65
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Thu May 31 04:23:33 2007 +0000
+
+ * Kernel.caller spec by jtoy (Ticket #112)
+ (kernel_spec is badly broken still)
+
+commit ce15eb69e721820e75b0f7aeae6488701cbe9555
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Thu May 31 03:55:27 2007 +0000
+
+ * Fixed String#dump spec expectation (Ticket #105)
+ * Moved RUBY_ENGINE to rubinius-specific variables_spec (Ticket #109)
+
+commit 7d2c575164dfbbd436d7c6ff400c088a68b29fa8
+Author: Hapk <hapk@unknown>
+Date: Thu May 31 00:42:50 2007 +0000
+
+ Added "if" specs for variable scoping.
+
+commit ceb4430f9c713f409f2567a0aa324f19afb09b79
+Author: Hapk <hapk@unknown>
+Date: Thu May 31 00:35:49 2007 +0000
+
+ Added specs for while / until condition/block evaluation order.
+ Added specs for next and redo statements.
+
+commit 4adeecf9333236800396bd77f928fb902e785692
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Wed May 30 22:17:04 2007 +0000
+
+ * Fixes to RSpec cleanliness of specs, most patches by zimbatm.
+ (Tickets #97 #98 #100 #103, partially #99)
+
+commit bbb0714b6ae60adb0af7445ed56544ceec3bc890
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Wed May 30 21:41:03 2007 +0000
+
+ * Array specs are RSpec/MRI-clean and pass (Ticket #95)
+ * Fix to should_raise for MRI by zimbatm
+
+commit d627ab36d3839745e057d9f5de781269ba7b154a
+Author: Hapk <hapk@unknown>
+Date: Wed May 30 21:15:39 2007 +0000
+
+ Added specs for 'while' and 'until' statements.
+
+commit 8a2177c7f23181909c5dcb51dd6df1e5b930fdbe
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed May 30 20:32:13 2007 +0000
+
+ * Enhanced hatefulness of 'for' specs
+
+commit 9f64f2a1c584420fcabfc73a7c464aa3b1ba2e90
+Author: Hapk <hapk@unknown>
+Date: Wed May 30 19:46:57 2007 +0000
+
+ Added specs for postfix "if" form.
+
+commit 9dd8a846adf356d75ceac566612f35d1d30840bf
+Author: Hapk <hapk@unknown>
+Date: Wed May 30 19:34:50 2007 +0000
+
+ Moved loop specs from spec/language/expressions into spec/core/kernel_spec.rb.
+ Added more specs for Kernel#loop.
+
+commit ddf2e3169c3a8b587f9abfb0ddf196635aec5186
+Author: Hapk <hapk@unknown>
+Date: Wed May 30 18:57:38 2007 +0000
+
+ Removed obsolete "elsif" statement spec.
+
+commit 3b6dca92ea810aa7866a2c7aa0b8812e72b6630e
+Author: Hapk <hapk@unknown>
+Date: Wed May 30 18:56:00 2007 +0000
+
+ Updated specs for "if" statement to cover more cases.
+
+commit 678f609bfe826538ec16e75f7362bcb3f50c8d6d
+Author: Arthur <arthur@unknown>
+Date: Wed May 30 17:09:09 2007 +0000
+
+ * Extend Specs for expressions
+ * Break the Specs up
+
+commit df60cc21e4213ac8344b5ed91e802d8cbbfa47a0
+Author: Tilman <tilman@unknown>
+Date: Wed May 30 17:00:17 2007 +0000
+
+ Implemented rb_ary_store() in subtend. The last spec still fails, but we'll fix that later.
+
+commit 8e0fea820c1683913625dfe95c7d3210d4548814
+Author: Arthur <arthur@unknown>
+Date: Wed May 30 10:09:55 2007 +0000
+
+ * Extend Specs for Class Definitions (nested class definitions, class definitions that extend objects, Multiple Definitions of the same class).
+ * Lots of them are failing in rbx.
+
+commit ce16f2b568ea89cb5f13660d3175165b105e4233
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed May 30 01:18:39 2007 +0000
+
+ * OK, that should really be in 'rubinius', not 'incompatible'
+
+commit 888b777539baa116eedc14191ac85d57aec54349
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed May 30 01:15:37 2007 +0000
+
+ * 'Options' is a Rubinius-only class, and should therefore be in 'incompatible'. Closes ticket #102
+
+commit 46a58344fa2f03fb4154b78f34239a815b2d9944
+Author: Bremac <bremac@unknown>
+Date: Tue May 29 23:52:52 2007 +0000
+
+ Fix typos in specc'ing module_function, and make that spec play nicer with MRI.
+
+commit e0cedb691f76af4554bfc7522a7668ff861492f0
+Author: Bremac <bremac@unknown>
+Date: Tue May 29 20:49:42 2007 +0000
+
+ More complete raise implementation and specs: Handle instantiation.
+
+commit 9f3a3bfe9d2610dd7e9e752c86a1b8aba47f7fdf
+Author: Hapk <hapk@unknown>
+Date: Tue May 29 20:39:07 2007 +0000
+
+ Added 'case' spec for case with empty 'else' body.
+
+commit 8f888bd3d0a01afc945c45c0502a0b97f3227c48
+Author: Arthur <arthur@unknown>
+Date: Tue May 29 20:34:18 2007 +0000
+
+ Extend Class specs. Some specs failing in rbx.
+
+commit f52d9faadc1eac31e7b92c1edb1cf45ca1d42c89
+Author: Tilman <tilman@unknown>
+Date: Tue May 29 20:29:53 2007 +0000
+
+ Implemented rb_str_new2() in subtend.
+
+commit d4d5e3d1eb8e5ee44acff6697a29a37b9eca25b2
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue May 29 20:00:51 2007 +0000
+
+ * Patch by HaPK - Add specs for 'case', and enhance specs for 'for'
+
+commit f3e736731e852dacbf90e8e3e33d840384909354
+Author: Tilman <tilman@unknown>
+Date: Tue May 29 18:56:41 2007 +0000
+
+ Actually exercise rb_ary_unshift() in the spec.
+
+commit 69d756ae17fce1fb53be5e7a1b5b7169b69c4aa2
+Author: Tilman <tilman@unknown>
+Date: Tue May 29 18:55:32 2007 +0000
+
+ Implemented rb_ary_shift() in subtend.
+
+commit 9f84a5ecddae6c0daf1fd7e46815275c7d7429db
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Tue May 29 18:50:41 2007 +0000
+
+ * Fix to Array#to_a for subclasses
+
+commit 2bbc87fc1b5261b57927a02f75915829b398b478
+Author: Tilman <tilman@unknown>
+Date: Tue May 29 18:50:33 2007 +0000
+
+ Implemented rb_ary_unshift() in subtend.
+
+commit 4e69b95ad7991a57fd3f9b7cbf350cb5b13c6a5c
+Author: Tilman <tilman@unknown>
+Date: Tue May 29 18:46:53 2007 +0000
+
+ Implemented rb_ary_dup() in subtend.
+
+commit 122a9cfbe79e872146116c8e045a243fffd333e6
+Author: Tilman <tilman@unknown>
+Date: Tue May 29 18:31:48 2007 +0000
+
+ Marked SubtendArray method functions as static.
+
+commit 0e3319c07aa1d536343343fbbe1004c4cdce2df0
+Author: Tilman <tilman@unknown>
+Date: Tue May 29 18:28:05 2007 +0000
+
+ Implemented rb_ary_clear() in subtend.
+
+commit 621f0082fca85140791e2c40aabc8ad3fe3318a6
+Author: Tilman <tilman@unknown>
+Date: Tue May 29 17:58:31 2007 +0000
+
+ Implemented rb_ary_entry() in subtend.
+
+commit 9c7d05c0bb19e65f57fc6aab778785e2a727c4a4
+Author: Tilman <tilman@unknown>
+Date: Tue May 29 17:42:40 2007 +0000
+
+ Ticket #91: Made spec/subtend/rake_helper.rb more portable.
+
+commit 5dba201079bdf8da63364ea760342f3cef85df74
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue May 29 15:56:01 2007 +0000
+
+ * Add specs for Array#pack, patch by HaPK
+
+commit 2d71e18c6f08144d4fb402904a9226a8500343bd
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue May 29 15:28:45 2007 +0000
+
+ * Better fix to method argument scoping, to support: def foo(a, b=a.length)
+
+commit f9deebb38b80cdea3dff44d7461404e5f501f566
+Author: Defunkt <defunkt@unknown>
+Date: Tue May 29 09:59:00 2007 +0000
+
+ * Add spec for Struct subclasses. Closes ticket #110
+ * Add failing specs for Class.new. Closes tickets #89 and #94
+
+commit 3be02f950f32a288fac1cd5cff0ae014057c96fb
+Author: Defunkt <defunkt@unknown>
+Date: Tue May 29 09:12:00 2007 +0000
+
+ * Add should_include convenience method to make rspec more compatible with mspec. Closes ticket #106
+
+commit 68e716e1874e7dd4186c7eef2aea5e25157a44fd
+Author: Vagabond <vagabond@unknown>
+Date: Tue May 29 03:46:33 2007 +0000
+
+ * Add HaPk's fix to Numeric#== to handle failed coersion with corresponding specs
+ * Removed duplicate definition of Numeric#==
+
+commit a63e6fcb08e34c625957d8d23bbe602964863c5b
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue May 29 02:59:43 2007 +0000
+
+ * Add Kernel#eval. Probably lacks some crazy MRI semantics at the moment
+ * Method definitions should properly create a clean scope for locals
+ * Support wacky default arguments, such as blah = lambda {|z| z.foo(another_arg) }
+ * Optional label prefixes in assembly output, for easier debugging
+
+commit f1295ac58d2b601f539efe0e660dfed9d043d1d7
+Author: Vagabond <vagabond@unknown>
+Date: Tue May 29 01:39:46 2007 +0000
+
+ Change Time specs to use ENV['TZ'] instead of `date` in hopes of being more portable
+
+commit 404faeca93c007f3eb9b3df52c2bde7673565113
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Mon May 28 19:20:27 2007 +0000
+
+ * Fix some mistakes in method_spec that caused it not to pass under MRI
+
+commit d336078c02e1306acb4b2664a427b63e93b02788
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sun May 27 06:03:25 2007 +0000
+
+ * Fix local scoping to allow for method definitions on local variables
+ * Pass all the horrible method definition specs
+
+commit 7759a0f91f794d05a32d48dd2e67d05c0b1dace7
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sun May 27 05:03:53 2007 +0000
+
+ * Rename Thread.yield to Thread.pass
+ * Implementation of 'module_function' by bremac, with minor tweaks
+
+commit 55f30c5e59d16ebbf045be93a7d406fed9a4dcbd
+Author: Vagabond <vagabond@unknown>
+Date: Sun May 27 00:34:44 2007 +0000
+
+ Added defunkt's implementation of Module#const_set and const_get and associated specs (Ticket #72)
+
+commit 9cebe0c56fda41b83ab14d39275e327daf0bdcc9
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Sun May 27 00:31:47 2007 +0000
+
+ * Fixes to mini_mock by bremac (Tickets #85 and #86)
+
+commit f33756f22597bd280e453d5c7ad97685fa284579
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Sun May 27 00:24:51 2007 +0000
+
+ * Fix typos in splat_spec (Ticket #77) by tilman
+
+commit ea13a828e5fc19694fc24da25b2224a75462a88c
+Author: Vagabond <vagabond@unknown>
+Date: Sun May 27 00:11:58 2007 +0000
+
+ Add Chris Wanstrath's (defunkt) Struct patches. Tested working against MRI.
+
+commit 3ca0ddcc2c39fec74f10b75df2af5c1581b9eaa3
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Sat May 26 23:56:26 2007 +0000
+
+ * Array fixes. Only spec failure remaining is #pack
+
+commit 4d91aa707a47189398455eb1c40b341dc3766ccf
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Sat May 26 20:05:13 2007 +0000
+
+ * Array fixes to pass specs (including HaPK's code).
+ All remaining failing Array specs except #pack are
+ not Array bugs. Test this heavily.
+
+commit 6793b34a54ab8e24e8a66a8af026a34315ac9f5b
+Author: Vagabond <vagabond@unknown>
+Date: Sat May 26 07:34:12 2007 +0000
+
+ Add cdcarter's Enumerator implementation and specs translated from his test/unit tests
+
+commit fd10c39192825aeef68c8843c2813cf50b8137f2
+Author: Vagabond <vagabond@unknown>
+Date: Fri May 25 20:12:22 2007 +0000
+
+ Some fixes to rand with associated specs
+
+commit 60d37d28b715854f5186598c90101824665ce715
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri May 25 19:43:11 2007 +0000
+
+ * Properly normalize default method arguments, to support: def(x, y=puts('hi'))
+ * Fix 'for' loops so that they use 'create_block' in the proper way
+ * Move all 'for' processing out of compiler.rb
+
+commit 3c04a44e8ff9e84f48fbd2d3afabb886494b5a98
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri May 25 06:35:48 2007 +0000
+
+ * Patch by HaPK - Fixes String#dump / inspect / upto
+
+commit 9e2442110ec33ff9ca4875407b227f2cf79a606a
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri May 25 05:36:31 2007 +0000
+
+ * Use a random pivot point for better worst-case Array#sort performance
+ * Add 'rake pristine' task to kill all .rbc files
+ * More tricky specs for splats and multiple-assignments
+
+commit 7bea77d8d3e8f190dba4f34fead888551fd07730
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu May 24 23:50:06 2007 +0000
+
+ * HaPK's patch to String#<=>, along with its specs
+
+commit 0e6007e7eb9eee5e3ab1acdf55da00f4ab8c4be0
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu May 24 23:29:49 2007 +0000
+
+ * Add spec for masgn semantics
+ * Fix numerous multiple assignment bugs
+
+commit 6d68d22efd7d2dba75c77cf957edb28dca6df6ef
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu May 24 07:05:00 2007 +0000
+
+ * New Array#sort implementation, fixes several Array and Hash specs
+ * Add a warning comment to bytearray.rb about some incorrect <=> behavior
+ * Prevent unimplemented Array specs from crashing the spec run
+
+commit 0ca089c7354ec96103cb637f861751ca7df01136
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Thu May 24 01:15:40 2007 +0000
+
+ * Support all kinda crazy splat syntax
+ * Updated some compiler specs, though some TODOs remain
+ * Added a comment above unshift_tuple, since it really shifts
+
+commit bbe0b73b07a393f94724964941d2fdd717a2d72e
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed May 23 19:58:10 2007 +0000
+
+ * Add compiler support for: yield(*args)
+ * Update some compiler specs to match recent fixes
+
+commit 32a7082205d3d214ad43a477286270a96076b140
+Author: Kev <kev@unknown>
+Date: Wed May 23 17:32:06 2007 +0000
+
+ Make spec titles consistent (describing C api behavior)
+
+commit 699c66f8c8304522fbb3589356fe2bcd298277c8
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed May 23 06:56:25 2007 +0000
+
+ * Use yield instead of &prc.call when initializing a thread.
+ * Fixes VM crash / closes ticket #68
+ * TODO - Why the HELL does this fix it?
+
+commit f8b6e1ff9e19e786b08fee30988eb874eae748b5
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed May 23 03:18:26 2007 +0000
+
+ * Implement Thread.main
+ * Prevent Object#inspect from crashing the VM when the inspected object has itself as one of its instance variables
+
+commit f24f573608ee5569b29754a017769db0f866cf4c
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue May 22 22:35:14 2007 +0000
+
+ * Implement 'class_variables' method
+ * instance_variables and class_variables now accept an optional argument, causing them to return symbols instead of strings
+ * Support defined?(@@class_var)
+ * Support defined?(a_vcall)
+ * Fix false-positives in defined_spec.rb
+
+commit da540b51c47b2349b0ab8d4ca0bd11124138f9ce
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue May 22 20:52:30 2007 +0000
+
+ * Add compiler support for begin/rescue/else/end syntax
+ * Default rescue clause should be StandardError, not RuntimeError
+ * Add specs for 'else' and empty begin sections
+ * All Exception specs now pass
+
+commit 82abf73fd99ec45f7cb6d98d19b219a61af59a61
+Author: Vagabond <vagabond@unknown>
+Date: Tue May 22 18:39:59 2007 +0000
+
+ * Fix Object#instance_eval to bring it into line with the specs and MRI
+ * Fix Object#instance_variable_validate to not accept fixnums as instance variable names
+ * Add another Object#send spec that tests exception raising for missing singleton method names
+
+commit 3b624f3f49c0433289224baf656b3d7be78cecd8
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Tue May 22 08:15:48 2007 +0000
+
+ Fix the block arg scoping problem, also add a missing file from the compiler specs.
+
+commit 59af7028c060c8e3f9b9c107fb750a71dd37a1d6
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Tue May 22 07:14:54 2007 +0000
+
+ A bunch of yummy-ness. Local variables now conform to the 'standard' behavior, ie they're allocated at different depths inside blocks (this is the yarv/jruby behavior).
+
+ Cleaned up a couple of subtend things.
+
+commit f8ed63efac6fa661dd39db2c207b66c34d132546
+Author: Vagabond <vagabond@unknown>
+Date: Tue May 22 03:28:17 2007 +0000
+
+ Add specs for Object #method, #respond_to? and #__send__. These currently fail with singleton methods on rubinius.
+
+commit 0d6e6b7109014c97d8f8be136166b3279d5a1108
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue May 22 02:06:19 2007 +0000
+
+ * Handle 'call' nodes containing newlines, e.g. x = [5,6,7,8];p Hash[*x]
+ * This is probably the wrong implementation, but it does work
+
+commit 518f7d34112e536d726cecfb2473c7b3db9ec33e
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue May 22 00:52:01 2007 +0000
+
+ * Fix mini_mock's cleanup process
+ * Add the ParseTree sexp test cases as specs. Currently in serious need of auditing
+
+commit 17ad76c162ff0cfe9662c20d418f455581389b42
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Mon May 21 21:50:13 2007 +0000
+
+ Add a failing spec
+
+commit 1744773b7f57c766c75d188b04e55540d45e19d0
+Author: Vagabond <vagabond@unknown>
+Date: Mon May 21 20:22:04 2007 +0000
+
+ Do some env trickery to make Kernel#at_exit testable
+ Improve implementation and specs for Kernel#warn
+
+commit 45733aa44e8daee9e8c5e552ac9312f21163fe39
+Author: Vagabond <vagabond@unknown>
+Date: Mon May 21 20:16:18 2007 +0000
+
+ Convert time specs to compare against output of the date command
+ Change Time#inspect to use %z (GMT offset) instead of %Z (timezone)
+
+commit e58ef35a05d2a565befeaf3600bc00f21203a84c
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Mon May 21 20:12:34 2007 +0000
+
+ Add spec for breakage caused by 1089.
+
+commit a5d54efe9a45f3acc1cdb0183a8c13ce6ed5e327
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Mon May 21 04:57:41 2007 +0000
+
+ * Options implements a minimal lightweight option parser
+
+commit b28b77af82d99a7a3ec5a78f6ab8b4e138ac577b
+Author: Kev <kev@unknown>
+Date: Mon May 21 03:54:33 2007 +0000
+
+ Add missing hash spec
+
+commit 81496352bdc2b6b27e293b7542908c6be54b9b6b
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon May 21 02:08:57 2007 +0000
+
+ added specs from ticket #38 (David Anderson), but not patch because implementation was invalid (e.g. [].first(0) => [] not nil) and superceded by recent patches. fixed Array#[i,0] => [] exposed by the added specs.
+
+commit 4ed6afc81262a4197f1ddc646ada94277cd9abe6
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon May 21 01:07:36 2007 +0000
+
+ HaPK's patch to Array#[] with specs. Knocks 14 failures down to 6. Ticket #60.
+
+commit b267aee1c10b6092d954c72d5776f4eafc109e51
+Author: Kev <kev@unknown>
+Date: Sun May 20 21:09:49 2007 +0000
+
+ rb_raise, rb_const_get, exception definitions. Wooooo exceptions from C
+
+commit 37793ed650e6ce7352a7547cf4bc68f2ceb2f0b4
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sat May 19 08:42:23 2007 +0000
+
+ * Added nastier multiple-assignment-with-splat specs
+
+commit 39c9817fa1932f9fe708a8ba78f43cb39e7cb68b
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat May 19 08:30:06 2007 +0000
+
+ twifkak's Kernel.fail patch with slightly modified specs.
+
+commit 264a42e8c11d08afa895b415453d59e1e1efe2e1
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Sat May 19 08:01:47 2007 +0000
+
+ * Remove a misleading comment in exception_spec
+ * Pre-compile bin/*.rb after a make install
+
+commit 7608e585e02283677275aaf5e5283e397ed2d671
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri May 18 23:57:09 2007 +0000
+
+ Vagabond's Kernel.warn.
+
+commit 9a41c5a21bbc822ff9ff758eb2962ba80e2d454b
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri May 18 18:52:36 2007 +0000
+
+ * Re-enable tr and unpack String specs
+ * Change 'Nan' to 'nan' in Sprintf to match MRI
+
+commit fdc7032c6e4823727312cc7e5c33386cf9d91429
+Author: Mental <mental@unknown>
+Date: Fri May 18 06:01:20 2007 +0000
+
+ add spec for ensure result elision
+
+commit 37438dc826624c3fee3afc1d30a9f661bbb1ab8d
+Author: Mental <mental@unknown>
+Date: Fri May 18 05:45:28 2007 +0000
+
+ basic thread spec
+
+commit d89b7728d148ba8c1ddd74323aa8f9e3dae79691
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri May 18 02:37:55 2007 +0000
+
+ * Fix 'should_raise' in mspec and rspec helpers
+ * Added some new Module specs, and fixed existing failures
+
+commit f63e0cf797158a239f65714918debf7a6c1bb687
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Thu May 17 04:12:58 2007 +0000
+
+ * First draft of a mock lib for mini_rspec
+
+commit c7fd82a8b4b84088de45463dbc25ae7eea5aabe2
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed May 16 22:38:27 2007 +0000
+
+ * at_exit handlers should run in reverse order of registration
+
+commit 2fb5c6e46f1682d927be8a9e116a609c75ec8be5
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed May 16 22:14:21 2007 +0000
+
+ * Fix Kernel.Array(). All core/kernel specs pass now
+ * Add Kernel#at_exit specs
+ * Fix Kernel#`
+ * Move AtExit handler array from Ruby namespace to Rubinius
+ * It's spelled 'occurred', not 'occured'
+
+commit 3d1605a3ca731b05b5c03ebd8a6edcf386612930
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed May 16 06:04:16 2007 +0000
+
+ added incompatible specs for #instance_methods returning symbols.
+
+commit 8ba8409ae0ab94a33cd082f02a81d4d1eab35b59
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed May 16 04:27:22 2007 +0000
+
+ * Patch by shadowfiend - Enhance Module specs and implement Module#instance_methods
+ * Make sure instance_methods always returns symbols, not strings
+
+commit 1e9b0066d712d4507260be02cf2bf116b2519af2
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed May 16 03:24:35 2007 +0000
+
+ * MethodTable 'is a' Hash, and does not need its own fields in the bootstrap. Fixes 'Object.methods.keys'
+
+commit 8c57dd0e26cb5468c1b0150c5d9c5d80ae6f2de2
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon May 14 06:18:42 2007 +0000
+
+ fixed class specs to pass on MRI. put rbx-specific integer specs in spec/rubinius.
+
+commit 8b43acd25a14f540447a9f958f7671822f836817
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon May 14 05:59:06 2007 +0000
+
+ moved rbx-specific proc specs to a new home. made core proc_specs pass MRI.
+
+commit 4feb384d0a02b272bd1a3581dd4070ef475b25af
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon May 14 02:59:22 2007 +0000
+
+ added RUBY_ENGINE == 'rbx' to global constants and exposed Rubinius::<const> on Object like MRI. converted sprintf specs.
+
+commit 34ad791d5f60177de7992a24f07992bb0d6c8b09
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Mon May 14 02:25:06 2007 +0000
+
+ * Disabled Lightning's dissembler on amd64
+ * Split specrunner into bin/mspec and a wrapper
+
+commit 8796b1f00501813c62676266508a6f89a82ec48e
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun May 13 22:47:28 2007 +0000
+
+ minor reorganization, cleanup of spec dir.
+
+commit 9be73815e2037dcc5347c2ef9876e76316efc504
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun May 13 22:06:54 2007 +0000
+
+ specrunner outputs summary with 'examples'. converted language/literals, keywords, straggler method_spec.
+
+commit 647fe38ce5f132b7944cca8550233249d8b3c113
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun May 13 21:21:04 2007 +0000
+
+ converted language, parser, library specs.
+
+commit d9e8f1fd3bc70231c89a1bdc17a9af5a46fce819
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun May 13 08:28:21 2007 +0000
+
+ converted incompatible specs.
+
+commit 9a07bb52c526ce8883c53d437077d78510b0ac73
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun May 13 08:09:34 2007 +0000
+
+ added Object#coerce_string that should act like 1.8.x StringValue function. added String#crypt and a couple other String things.
+
+commit 2bae9b5e3baa33da21c1335e84c2eab062eac3a4
+Author: Kev <kev@unknown>
+Date: Sun May 13 06:21:06 2007 +0000
+
+ add rb_hash_delete
+
+commit 0ca1a5baa94b5984b0812365a408688420168d24
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Sun May 13 05:31:24 2007 +0000
+
+ Imported GNU Lightning. subtend's rb_define_method_ now generates stub's to pop the args and call the function. Next step, add type conversion to call functions that don't take handles.
+
+commit 141e795d5042cb4ea398c9b8eaa9cd7045f5625e
+Author: Kev <kev@unknown>
+Date: Sun May 13 05:15:33 2007 +0000
+
+ Add rb_hash_aset
+
+commit 0988a253d8e23b400a738ad74637e8b3655eae8c
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat May 12 19:40:35 2007 +0000
+
+ new .rba's with rue's changes. converted spec/shotgun specs. added specs for Tuple. added aliases size, length for Tuple#fields.
+
+commit abd44484b4b2a28a4c7f0bf7acdf12ff30123729
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Sat May 12 19:32:06 2007 +0000
+
+ * Fixed class variables, should work everywhere now
+ * Specs for cvar behaviour
+
+commit 8ec7dac58577cea314ff0fcd976219b23591bc4d
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat May 12 07:40:42 2007 +0000
+
+ reimplemented Object#instance_variable_get|set and #instance_variables. now works with immediate values, and classes with no __ivars__.
+
+commit 30c4dd441243277ec5b814ad9b4d4697e87641d0
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri May 11 07:56:14 2007 +0000
+
+ added primitives for instance_variable_get|set so that methods operate identically on objects that do not have an __ivars__ field (e.g. Array, String). this needs more work because an exception occurs when attempting to set|get instance vars on an immediate value.
+
+commit 998a0ab62542f36f9e36bdd497116349421951ce
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Wed May 9 23:07:35 2007 +0000
+
+ * Converted rest of spec/core/
+ * mini_rspec/specrunner improvements
+
+commit 0cac71dd1e4dbb728bd3401e73fda5b3fbe95e38
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed May 9 08:18:59 2007 +0000
+
+ updated expectations to be the actual compiler output. these specs should be carefully reviewed.
+
+commit 8d551887fd1fabc7700f9f0a432b728829dcef96
+Author: Kev <kev@unknown>
+Date: Wed May 9 07:42:15 2007 +0000
+
+ Pull out bundle that got caught in the commit
+
+commit 736916decc6d9bfd7096079a0118f41a168d735d
+Author: Kev <kev@unknown>
+Date: Wed May 9 07:41:42 2007 +0000
+
+ Add hash specs, and impl of rb_hash_new
+
+commit 2352f0a526be0f277f2e5d60f18acddc216045c1
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed May 9 07:06:45 2007 +0000
+
+ converted test/bytecode/test_compiler to specs.
+
+commit 1f1d30f9ca690214a61f299a4bb408c2d28ef004
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Wed May 9 06:08:41 2007 +0000
+
+ * Converted MatchData specs
+ * Default warnings for empty spec files
+ * Improved specrunner
+
+commit 04c03e648ca83de2c2aee37f9aef9079d0493bd7
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Tue May 8 06:24:48 2007 +0000
+
+ * Converted Integer and Kernel specs
+
+commit a202ef1dfb21cebf3ee33376775d86b9dc89269d
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue May 8 05:45:33 2007 +0000
+
+ added before, after methods to mini_rspec. started adding compiler specs as conversion from test/bytecode/test_compiler. removed all host/target junk. thanks. bye.
+
+commit 692da2d89089bc94c95915c90da756480a057dc1
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue May 8 03:36:51 2007 +0000
+
+ converted object specs. these really blow up rubinius.
+
+commit 714f5df86f583158d73eda366e2f2527156c3b8e
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Mon May 7 17:29:56 2007 +0000
+
+ The first compiler spec, testing the masgn assembly. Some are commented out because they don't yet work.
+
+commit 8ccfe13ca0eca4ceae6a201905a64666a75dd6ba
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Mon May 7 05:52:41 2007 +0000
+
+ * Converted and reviewed Hash specs
+ * specrunner reports specifications and failures
+
+commit aa32b8e94de5c1ccd49a9d6ddca5836d6303c460
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun May 6 06:50:52 2007 +0000
+
+ finished converting fixnum specs.
+
+commit 7c55264dc15ed2b8a1b341a5d605701c6626ad34
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Sat May 5 06:22:37 2007 +0000
+
+ * Converted Enumerable specs
+
+commit 1c660edd87fa91d8c244289b00eb9252d5654c3d
+Author: Evan Phoenix <evan@fallingsnow.net>
+Date: Fri May 4 23:45:08 2007 +0000
+
+ Fix array_append as well as the logic to call the extension function so the arguments are correct.
+
+commit 6b9c27b8f8d12be443d37635e17b23b7f0d76388
+Author: Kev <kev@unknown>
+Date: Fri May 4 07:54:10 2007 +0000
+
+ Complete rspec coverage of subtend string compat to date.
+
+commit 32db2e9a157cee24ae883b7b8fd563d98fc2dce5
+Author: Kev <kev@unknown>
+Date: Fri May 4 07:11:21 2007 +0000
+
+ Add loading of C extensions via require.
+ Stop grammar.c from generating every fricking time
+ Cleanup formatting on subtend
+ Add proper minispec tests for subtend
+ Remove old subtend test extension
+
+commit 851fbe6e587596fd074b4c99e42c43865118ae00
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri May 4 06:58:35 2007 +0000
+
+ converted (but not to the new new style) fixnum specs. fixed mini_rspec to rescue backtrace.show on MRI.
+
+commit 3e8deacb57ef80684281b1329778bc52681a8601
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri May 4 06:22:53 2007 +0000
+
+ converted module specs. added incompatible spec for const_defined?(Some::Class). made mini_rspec print backtrace on error.
+
+commit 3c1cc4ff4f6bf4fa28f65d9909a74f77f6524aa8
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri May 4 05:45:50 2007 +0000
+
+ converted math and exception. added two helper methods: should_be_close, should_include. I think spec_translator should handle converting these to 0.9.x syntax as soon as rspec runs.
+
+commit 28e3cdba63f2853b9e9a084f27ad764437830799
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu May 3 15:26:39 2007 +0000
+
+ converted float specs. added ignore for *.rbc on externals/rspec-0.9.1.
+
+commit 69ea5db15fb0562d8a4114d4e8ec54f2e19ad8fd
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu May 3 06:40:17 2007 +0000
+
+ converted range, nil, regexp specs. added ignore *.rbc on rspec dirs.
+
+commit 6cc364770406e4e04ef7baf2fdaab7425a7f5a6c
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Thu May 3 03:27:04 2007 +0000
+
+ * Converted Dir specs
+
+commit 5e39be7f97d5cd131b0cf564746d881245030f7d
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu May 3 03:04:24 2007 +0000
+
+ okay, rue insists that we use describe ... it now. (see spec_translator with rspec 0.9.1).
+
+commit 99f05b9d6572600ed0bf6a732048c1c4a2d2bb0b
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu May 3 03:00:06 2007 +0000
+
+ converted bignum specs. 100% pass on MRI.
+
+commit f9e4df4bdb721eb32c4ac7e5abd4a646daaf20d2
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu May 3 00:06:19 2007 +0000
+
+ fixed mini_rspec aliases for specify, etc. to work around exception: No method 'alias_method' on an instance of Object. (NoMethodError)
+
+commit 89d3ca0681816afd389907cbb52f7e0372dbecef
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Wed May 2 07:13:56 2007 +0000
+
+ * Converted spec/core/ binding, class and continuation
+
+commit 086f889a9bae2e40dd6a8b1ffa80113070f3ad46
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Wed May 2 04:22:12 2007 +0000
+
+ * bin/specrunner is a small bash kludge for running mini_rspec over
+ a directory (recursively) or a single file
+ * Rakefile allows diffing a current spec run against a base run to
+ easily see all changes among the thousands of specs as well as
+ storing a base run
+ * specrunner produces decent output for the minimal spec output
+ from mini_rspec
+
+commit ba89b2c015d2754b6470b324a013f018d8202cfe
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Tue May 1 22:57:22 2007 +0000
+
+ * Converted spec/core/array_spec.rb to mini_rspec
+ * Reviewed and fixed some specs for Array
+ * Spec-style output to mini_rspec (manual comment/uncomment to switch)
+ * should_raise for slightly more natural exception verification
+
+commit 0330bcc23fa1609db291cd382cb13fc168ec5bf3
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Tue May 1 22:44:40 2007 +0000
+
+ * Implement correct behavior for String#split when called with a zero-width Regexp
+
+commit c8e806e2dafd237fa8117ead21553a195900613e
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue May 1 02:29:35 2007 +0000
+
+ converted symbol_spec. 100% pass.
+
+commit 76e31065df70ebc5790fdb604f1b07d28ffaa81c
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue May 1 02:19:33 2007 +0000
+
+ commented out specs that cause rubinius to hang. String#delete and #tr (and methods that are implemented in terms of these).
+
+commit 2ae8aea13161a71c3fb4ca8e0486acd55c897579
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue May 1 01:17:14 2007 +0000
+
+ converted core/string_spec to regular syntax. added mspec_helper.
+
+commit ff84053991295b259ca8b1c17adff95f5d471961
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Apr 30 22:31:26 2007 +0000
+
+ converted false_spec. added svn:ignore *.rbc on all spec dirs.
+
+commit 3fc864ba235c56118e1db66dbf9537d6ff8c0c5f
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Apr 30 22:00:12 2007 +0000
+
+ Let the breakage begin. Converting all specs to use mini_rspec with 100% compatible syntax with rspec proper. usage: 'USE_RSPEC=1 spec spec/core/false_spec.rb' for any specs that use example {} method. spec spec/core/true_spec.rb OR ./shotgun/rubinius spec/core/true_spec.rb for converted specs.
+
+commit 4c6c7f406d0e5504a72c52b1ae5339a9dba36865
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Apr 29 17:28:11 2007 +0000
+
+ added setup method and print to STDERR and STDOUT to support a shell script runner.
+
+commit 958a0e9b1a066cf2d825b960b66788b05c928f36
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Apr 29 08:26:09 2007 +0000
+
+ mini rspec implementation. example {} method is dead.
+
+commit a323b3d424f226322cf20e65e87f8a4e962ed497
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Apr 29 03:23:17 2007 +0000
+
+ Added Array#first, Array#last that take numeric args to core. Added a bunch of failing specs for Array#[]. #first and #last are implemented using #[] so several of the specs for those fail, but the implementation of #first and #last was tested in MRI.
+
+commit 3897c943069582b1e5d1649a097bd77c0c895e0a
+Author: Hurdlea <hurdlea@unknown>
+Date: Thu Mar 29 13:51:13 2007 +0000
+
+ * Support for Floats in Sprintf
+ - Sprintf is still missing support for unsigned twos complement
+ * String#% now implemented
+ * Fixed a minor issue in the Rakefile
+
+commit 5ed87ff88793f8d44cfe34b443eb032d27dc2a4c
+Author: Hurdlea <hurdlea@unknown>
+Date: Thu Mar 15 05:08:34 2007 +0000
+
+ * Added Sprintf core module and classes for string % and Kernel
+ - Still needs some work with floats and requires a couple of
+ primitives to achieve this.
+ * Fixed a small issue with String#Index(Fixnum, offset)
+
+commit 982c09b15710429fc97d8d43d9f24a3a0badb6d5
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Mar 10 22:35:18 2007 +0000
+
+ Fixed array spec for #sort which improperly depended on the accidental order in which two elements of the array were being compared.
+
+commit 32fe004da7f35e9b7dcc96f8e57e1acb37164748
+Author: Hornbeck <hornbeck@unknown>
+Date: Sat Mar 10 06:38:23 2007 +0000
+
+ Two tests in the ObjectSpace spec. It was bare and needed love.
+
+commit e1530bb1999118bf88037dccc27d78f54bdbe5e4
+Author: Tlockney <tlockney@unknown>
+Date: Sat Mar 3 23:28:27 2007 +0000
+
+ updated all rspec exceptions. exception specs all pass in MRI. still a few rubinius exception spec issues
+
+commit 2c278533cbfe0efc7076d2c947323640be5f207a
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Mar 3 21:29:55 2007 +0000
+
+ Committing tlockney's additions to core/exception_spec.rb. These pass on MRI but illustrate areas to fix on rubinius.
+
+commit fd8993c0996e4524440a6572c45dad4ab112fb2f
+Author: Hornbeck <hornbeck@unknown>
+Date: Fri Feb 23 15:58:33 2007 +0000
+
+ committing Aki Reijonen's Hash patches for hash.rb and the hash_spec.rb. Also included is Thomas Lockney's exception_spec.rb patches.
+
+commit 08e6d924b8c0175242c1c40322ed3e45855a86c2
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Feb 18 07:48:46 2007 +0000
+
+ Altered Object#instance_variable_[get|set] rearranging flow control. Added specs for instance_variable_[get|set] for Array, IO, String.
+
+commit 2a2385413c03f21dfc038e110f46a7a3bd2fc9c7
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Feb 18 06:05:47 2007 +0000
+
+ Increased time out value when running rspec error report. Minor changes to text in class specs.
+
+commit bd0d7fcf72546a0a3a5a6a59b1a6f2aadd8e4262
+Author: Mae <mae@unknown>
+Date: Sun Feb 18 03:53:00 2007 +0000
+
+ Integer#bits for future refactoring of shift
+ * added Integer#bits which calculates minimum bit storage required for (signed int) form of the Integer
+ * spec'd it too
+
+commit 4e6b39d5e69c04d92ceac76ce5a5bd792fb65f39
+Author: Mae <mae@unknown>
+Date: Sun Feb 18 02:55:55 2007 +0000
+
+ Object#extend-a-gogo
+ *Fixed Small bug in rubinius_target where failures wouldn't be reported
+ *Implemented Object#extend and changed math.rb to use it accordingly
+
+commit 5472c10579cef38f9f28c904710246509633a040
+Author: Mae <mae@unknown>
+Date: Sun Feb 18 01:21:11 2007 +0000
+
+ A great Time patch from John Hornbeck <hornbeck@gmail.com>:
+
+ A more complete Time diff. This includes many of the instance methods for Time and a new primitive for usec. This diff also includes some failing specs as I went ahead and added the specs for the rest of the class methods. Also included is the constants for Time.
+
+ Keep the good work coming John!
+
+commit 83ab11e0ab6679b1c9eefc5095d3f20af9a61661
+Author: Mae <mae@unknown>
+Date: Sat Feb 17 23:26:33 2007 +0000
+
+ Patch from Aki Reijonen <aki.reijonen@gmail.com> without the Float.induced_from part
+
+ Summary of the changes:
+
+ ** Added methods **
+
+ Numeric#integer?
+ Numeric#div
+
+ Integer#to_int
+ Integer#round
+ Integer#truncate
+ Integer#next
+ Integer#succ
+ Integer#integer?
+
+ String#slice!
+
+ Object#to_a
+
+ Kernel#Array
+ Kernel#String
+
+ ** Fixed methods **
+ Float.induced_from
+ - Now return the passed object if it's an Float insted of calling #to_f
+
+ ** Removed methods **
+ Fixnum#div was broken, the end result should be converted to Integer,
+ not the number passed as a argument. (superceded by Numeric#div)
+
+ --
+ Aki Reijonen
+
+commit 243a4e9ba46149b8ba39c7238f8ff3d5f267689e
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Feb 17 06:17:44 2007 +0000
+
+ Ditched all the instance vars in array specs since we've got locals now.
+
+commit be5363e22e04b8baf26cb4abd8a8a67e7dd3cc0c
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Wed Feb 14 01:05:49 2007 +0000
+
+ Fixed * varargs to work in method definitions. Currently still
+ does not work as the single named parameter (foo(*a)). This
+ means that lib/bytecode/encoder.rb now compiles under Shotgun.
+
+commit 3bb810688e848c90d5c20929c630f36a32796d2d
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Feb 13 18:42:09 2007 +0000
+
+ Added Object#instance_variable_set and specs.
+
+commit 54392c99dc3db5b58c85799416cc528c60b12533
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Feb 13 18:25:24 2007 +0000
+
+ Added Object#instance_variable_get and specs. Uncommented Math module constants specs.
+
+commit 84267901502ca1e8e8b13afa0e3a16e0cdc8e493
+Author: Mae <mae@unknown>
+Date: Sun Feb 11 10:30:13 2007 +0000
+
+ * Primitive Specs
+ - Added spec for bignum_div (and fixed a problem where it would always fail)
+ - Fixed primitive_spec_helper (because it broke the old specs last time)
+ - Removed magic method chaining because it sucks
+ - DISCLAIMER: primitive specs atm just test functional things, _NOT_ stateful side-effects
+ - We can do this properly once rubinius can run rspec
+
+ * SIRB
+ - Made it so that => wouldn't get printed before the command prompt if you typed "exit"
+ - Made Kernel#p, Kernel#puts, Kernel#print return nil (like MRI)
+ - added #!shotgun/rubinius to top of sirb and symlinked it to bin/sirb.rb as well (for convenience)
+
+ * Removed unused local from __loader
+
+commit efce7d8a56748ab1831a34d21b8c92ff8b2eb977
+Author: Mae <mae@unknown>
+Date: Sun Feb 11 07:25:51 2007 +0000
+
+ Moved math to math_spec; added object_spec for primitives; made primitives_spec_helper maybe too smart? -- they chain methods on to the remote target
+
+commit 087a5e5a6e89e4a53a39e025ffe08d21e96b8f6e
+Author: Mae <mae@unknown>
+Date: Sun Feb 11 05:51:30 2007 +0000
+
+ * Made rubinius_target and example much more helpful
+ - backtraces are shown on failure now
+ - you can do this: example { 1 + nil }.should_raise(TypeError) and it works :) (with bt and all)
+ - injected some extra code in example snippets so try(exc) syntax still works
+ - Float, Nil, True, False specs all pass 100 %
+ - made rubinius_target make use of @src (used in bignum)
+
+ * Made Kernel.Float() and Kernel.Integer() behave appropriately like MRI _with_ the exception of Float() also checking for to_i method
+ - Integer(nil) => 0 and Float(nil) => 0.0
+ - lots of spec coverage
+
+ * Numeric#coerce was slightly tweaked to use new Float() and Integer() behavior
+ - Specifically complains about other being nil (so 1 == nil doesn't work)
+
+ * Made Float.induced_from() more anal retentive (only accepts core Fixnum, Bignum, Float types like mri)
+ - specs cover it
+
+ * Fixed infinite loop on Bignum#& and moved & out of Numeric into Integer (Float doesn't have &)
+
+commit eadf1ead754d3dbfaf703c205f6f5e8f4dc5c430
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Feb 11 03:00:25 2007 +0000
+
+ Put object flags values into a single include file. Added Object#taint, tainted?, freeze, frozen?. Neither of these states actually effect execution yet. Fixed up Object specs.
+
+commit 38e7f757e67b4ec985835e0e93ba4d32bbee5ca4
+Author: Mae <mae@unknown>
+Date: Sat Feb 10 23:57:37 2007 +0000
+
+ - Created specs for math_sqrt primitive (and created spec/primitives/math_spec.rb)
+ - Tweaked primitives_spec_helper to properly transport NaN's to testing environment
+ - Removed non-needed self parameter from math_sqrt c function
+
+commit a4267a136d7f0bf7f92421fcebd8011600a1d92e
+Author: Mae <mae@unknown>
+Date: Sat Feb 10 22:29:46 2007 +0000
+
+ Float.induced_from love
+ - made Float.induced_from work for any to_f item (controversial whether this should be done in Kernel.Float() or not)
+ - apparently this fixed some float failures
+ - made Float.induced_from safer because it now complains if to_f returns a non-float
+ - wrote specs for new induced_from behavior
+ - try (spec_helper) needs to be investigated, manual running of the premises of 'Float divmod should raise FloatDomainError if other is zero' show this to be a spec that _should_ pass
+
+commit 65a4e8abfc7f690456e4f44e7e4cc38911288516
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Feb 10 18:54:15 2007 +0000
+
+ Added spec files for the rest of the core classes documented in Pickaxe book. Add simple class hierarchy specs for exception classes.
+
+commit c7a2f68c36dd95f51af88e8fa62b24b71d68578a
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Feb 10 17:29:46 2007 +0000
+
+ Commit of scoopr's Math module beginnings. Thanks scoopr.
+
+commit b1e8d150460f2ae9ea2e5ef87d0df3b705c1d0d6
+Author: Mae <mae@unknown>
+Date: Fri Feb 9 08:54:25 2007 +0000
+
+ Bignum primitive specs
+ - Added spec and changed to metaprogramming style for primitives: bignum_add, bignum_compare, bignum_equal, bignum_and, bignum_divmod
+ - fixed bug that bignum_divmod spec found where bignum_divmod would always fail on divide by zero GUARD
+
+commit 0487a39ec9995af8eb5a8dff5ec64492261852e7
+Author: Mae <mae@unknown>
+Date: Fri Feb 9 07:48:56 2007 +0000
+
+ Primitive Spec Sexiness
+ - Added spec and changed to metaprogramming style for primitive fixnum_to_f
+ - Made usage of run_primitive(:add, 1, 5) more sexy i.e. 1.prim.add(5)
+ - converted fixnum spes to use sexiness
+
+commit 95fa48f29eaa7e3f10ccd63d385fb3f582f57eea
+Author: Hurdlea <hurdlea@unknown>
+Date: Fri Feb 9 01:36:14 2007 +0000
+
+ * Added String#delete, delete!, tr, tr!, tr_s, tr_s!
+ - String#count and squeeze to follow ...
+ * String#<< now accepts Fixnums
+
+commit 017bdc57602e2e5d55705de070c07edba46a347f
+Author: Mae <mae@unknown>
+Date: Thu Feb 8 09:23:55 2007 +0000
+
+ - removed noop from primitives (it does nothing)
+ - removed noop primitive spec
+ - changed CPU::Primitives.name_to_index to offset by +1 (to leave room for special 0 value)
+ - still having same closed parens issue with spec:primitives:
+ syntax error, unexpected $end, expecting ')' (SyntaxError)
+
+commit 18a3347bb32d8ac5269438376f0100ecce2c9e73
+Author: Mae <mae@unknown>
+Date: Thu Feb 8 08:20:14 2007 +0000
+
+ - made shotgun/lib/primitives.rb have less dependencies
+ - fixed bug where if a false was popped of the stack it wouldn't be recognized as an argument in primitives_spec_helper (nil will only do this now)
+ - fixed regression in primitives_spec_helper where the proper code wasn't showed when shotgun crashes
+ - noop_spec works again
+
+commit 16b08e446b69344da1edbc1f793e0161deac8e6c
+Author: Mae <mae@unknown>
+Date: Wed Feb 7 23:59:14 2007 +0000
+
+ More Primitive Goodness, Conform to unified rspec standards
+ - Added specs and changed to new metaprogramming style for the following primitives: fixnum_and, fixnum_or, fixnum_xor, fixnum_invert, fixnum_neg
+
+commit cb2ac85b45a41a63100cac673919ad8db1f93f43
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Wed Feb 7 23:16:35 2007 +0000
+
+ Basic specs for Symbol literals.
+
+commit 3ab7aced51f3a63c8f76706a2f159d0d5753dc64
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Feb 7 20:35:27 2007 +0000
+
+ Changed Fixnum primitives specs to be in a single file, spec/primitives/fixnum_spec.rb. Added back the alternative example of writing specs for others to evaluate or use. Updated the wiki specs page to lay down the law on spec files. Kindly follow it.
+
+commit cf16d691990f43f5bf8807bbef2ba1876892be57
+Author: Mae <mae@unknown>
+Date: Wed Feb 7 18:27:15 2007 +0000
+
+ Autotest, C warnings cleanup, and some primitive_spec usage cleanup
+ - Added Autotest Facilities for primitive bin/autotest/primitives
+ - need to gem install zentest to use this (and some diff gem i can't remember)
+ - Removed ugly require statement from cpu/primitives it was causing annoying ruby errors
+ - Added missing prototypes to cpu.h and regexp.h (primitives.gen was complaining)
+ - Localized bt and bt_size variable declarations to where they would be included by the preprocessor to make more warnings go away
+ - Added newlines to the end of numeric.c, numeric.h, float.c
+ - Used one of brixens suggestions (injection of primitive spec helper automatically)
+ - Removed extraneous primitive helper inclusion in each spec
+
+commit f4bbce9d761d27e1381b95a4ff6076e85577074d
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Feb 7 16:56:21 2007 +0000
+
+ This shows an example to 'fix' mae's rubyesquely-challenged (bluntly, ugly) primitive specs. Also, there should be one spec file for a group of related contexts. In this case, the group is the class Fixnum. So, mae, fixnum_spec.rb, NOT fixnum_xxx_spec.rb. Sorry. Cry tyrany, weep and gnash thy teeth, howl in protest, but please fix it. This is non-negotiable. Thank you and good work on the primitive specs. :)
+
+commit e5f6215824a40beb0ca678575596bd06afa8dd3a
+Author: Mae <mae@unknown>
+Date: Wed Feb 7 09:54:54 2007 +0000
+
+ - Added specs for primitives: add, sub, fixnum_mul, fixnum_size, fixnum_div, fixnum_modulo, fixnum_divmod, fixnum_to_s
+ - Updated primitives to new metaprogramming style: sub, fixnum_mul, fixnum_size, fixnum_div, fixnum_modulo, fixnum_divmod, fixnum_to_s
+ - Changed wording in a couple primitive spec files to be more explicit
+ - Made reporting by primitive_spec_helper more helpful when shotgun crashes from injected code
+
+commit 3032c6bd869a04c1517508850f94119975c36e54
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Feb 7 02:26:47 2007 +0000
+
+ Fixed up String#to_i a bit; added a bunch more specs for it.
+
+commit d8a24ffa8d9983a85b0f03784a89bfa667af1615
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Wed Feb 7 01:46:30 2007 +0000
+
+ Added very basic set of specs for assignment and multiple assignment semantics.
+
+commit edb7c82523b36b26e24437de42fd2638eef1653f
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Feb 6 19:02:46 2007 +0000
+
+ Added specs for and methods CType#isalnum, isdigit.
+
+commit e84ba1b12c51331d00bdd06684dcff96ea229322
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Feb 6 17:30:55 2007 +0000
+
+ Added spec/shotgun/bytearray_spec.rb. Added spec for ByteArray#[], []=. Modified various string methods to use BA#[], []= instead of get_byte, set_byte.
+
+commit be9589cc47cbf35edd94ca22407de4b1527a3fdb
+Author: Mae <mae@unknown>
+Date: Tue Feb 6 11:26:31 2007 +0000
+
+ Tweaks to primitive metaprogramming and addition of noop spec
+ - Added types 'qnil' 'qtrue' 'qfalse' to be used in primitive metaprogramming
+ - Added spec for noop primitive
+ - Added run_asm method to primitives_spec_helper for those tricky tests
+ - Converted noop primitive to new metaprogramming style
+
+commit e31f1af903dd8dd31427e34a718b30f5c63af8df
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Feb 6 10:33:00 2007 +0000
+
+ Some fixes to String#to_i and additions to specs. Added String#oct and hex.
+
+commit 2a157827bd72b6c7ce8a025928cdd9d7f2f8d00f
+Author: Mae <mae@unknown>
+Date: Tue Feb 6 10:18:16 2007 +0000
+
+ moved equal and compare to use new primitive technique
+ - also updated equal_spec to ask for ArgumentError instead
+ - uploaded new rba *glares at brixen*
+
+commit 771d0fede3086ce58d225ac4001ea0934f3bb0e3
+Author: Mae <mae@unknown>
+Date: Tue Feb 6 09:33:03 2007 +0000
+
+ ARITY macro raises ArgumentError directly from the primtive now and made specs pass
+ - Made ARITY macro raise an argument error exception instead of just ambiguously failing
+ - Made _ret return TRUE on arity failure (exception directly raised)
+ - Moved GUARD and POP macros to shotgun/lib/cpu_primitives.c where they belong (localized)
+ - Removed side-effect printf in cpu_raise_arg_error since stack trace is fine now
+ - Tweaked primitives_spec_helper should_raise to work for all exceptions
+ - Made specs for equal/compare pass again (expect ArgumentError instead of PrimitiveFailure)
+ - New compiler.rba (update these ppl!)
+
+commit e89190c8fdc4a71c7b8cd9c8b873a63b9d1888c5
+Author: Hurdlea <hurdlea@unknown>
+Date: Tue Feb 6 07:51:08 2007 +0000
+
+ * Finished String#[]= for string index
+ * found odd bug with spec where string[1,2]="foo" is not interpreted correctly
+ changed methods to use send(:[]=, ... and the tests pass
+
+commit c6e1bb68e930a537bd51d77afd37cdc8b5d62d31
+Author: Hurdlea <hurdlea@unknown>
+Date: Tue Feb 6 07:18:15 2007 +0000
+
+ * Added String#[]= slice functionality
+
+commit e405d4f5f32fd8192c435b3488f394b2635c7db7
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Feb 6 06:37:31 2007 +0000
+
+ Added String#chomp[(bang)].
+
+commit ff48a6c333f34c1b1882c260db7145facce3d71f
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Feb 6 06:00:56 2007 +0000
+
+ Added String#replace_if that calls replace if self != other else returns nil; Added upcase, downcase.
+
+commit f46d747eba82c215fa07b067a30f2a2e8868d284
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Feb 6 05:31:13 2007 +0000
+
+ Implemented String#reverse directly rather than with String#<<. Modified some string specs for [lr]strip but forgot to commit them earlier.
+
+commit d3b0e71e810a985f3b8f2e5f5c7d5c4619f151f9
+Author: Hurdlea <hurdlea@unknown>
+Date: Tue Feb 6 03:00:28 2007 +0000
+
+ * Added NilClass specs to detect NilClass coercion
+
+commit c570ca475cabeb3fcfcca26d4c57b8e57f6606b4
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Feb 6 02:01:19 2007 +0000
+
+ Added module CType mixin for Integer to provide isspace, isupper and friends. Added specs for CType in spec/shotgun. Implemented String#capitalize[(bang)]. Moved ByteArray into it's own file. Updated various string specs and commented out temporarily index spec.
+
+commit 4694d1511e880e43dfccb3e3f5309f0920395ba0
+Author: Hurdlea <hurdlea@unknown>
+Date: Tue Feb 6 00:22:17 2007 +0000
+
+ * Fixed operation of Regexp#=~
+ * Updated Regexp spec for =~
+ * String#== now works correcly for duck typed objects
+
+commit 8e42aa9c789fcc9bc475d460e7158f2adcc8ab64
+Author: Mae <mae@unknown>
+Date: Mon Feb 5 22:46:21 2007 +0000
+
+ Added arity checking for primitives
+ -for use in primitives: #define ARITY(length) GUARD( (length) == num_args )
+ -for instance if i have a primitive that takes one argument (self + arg) i put ARITY(1) at the top
+ -changed specs with regard to arity accordingly
+ -made block_given conform to the "self rule for primitives" by padding Qtrue where self would be
+ -fixed block_given? to pass the right arity (0) in the compiler
+ -specs for compare and equal pass now
+ -added primitives_spec_helper (forgot last time)
+
+ NOTE: Binary .rbc compatibility is broken now since there are arity checks done on block_given?
+ -the rba's i uploaded should be fine but if they arent...
+ do find -type f | grep .rbc | grep -v .svn | xargs rm
+ then rake build:rubinius
+
+commit 2d5c9bc3170bf959390627def10c0208088b48ee
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Feb 5 22:39:08 2007 +0000
+
+ Added Integer#isspace and spec in spec/incompatible. Added spec/incompatible/string_spec to describe behavior of stripping runs of whitespace and nulls from end of a string. Added or modified String#lstrip, lstrip(bang), strip, strip(bang), rstrip, rstrip(bang).
+
+commit d4b07b06ca85543423a308f12b82ae4671bdd0c2
+Author: Mae <mae@unknown>
+Date: Mon Feb 5 21:01:59 2007 +0000
+
+ -split out common primitive testing functionality to primitives_spec_helper.rb
+ -added spec for primitive "equal"
+ -again primitive specs are rake spec:primitives
+
+commit c1a7896f24e018df13af7f0d3d60db9f461130a5
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Feb 5 20:58:21 2007 +0000
+
+ Added that Module#include passes off to append_features. Added Module#include that takes multiple args later in the bootstrap sequence. Added specs for include and append_features.
+
+commit d407ecab13722599b75fecc20bdebd86c9f76fa6
+Author: Mae <mae@unknown>
+Date: Mon Feb 5 20:48:02 2007 +0000
+
+ Misc Changes to Tweak primitive specs
+ -Added spec to test arity restrictions of compare
+ -Fixed bug where should_raise for primitives was not catching the error condition
+ -Changed wording of some specs to be english rather than engrish :)
+
+commit f68ad63065002d4a3c9a0742770da4a112780aa7
+Author: Mae <mae@unknown>
+Date: Mon Feb 5 20:02:24 2007 +0000
+
+ - Remove printf from cpu_raise_primitive_failure so that the screen doesn't get littered
+ - Created PrimitiveSpecHelper and a "primitives" spec subdirectory
+ - Added rake task spec:primitives
+ - Added compare_spec as an example
+ - New rba's
+
+commit f997d3791099912001d09a427f24252182ba1d6c
+Author: Hurdlea <hurdlea@unknown>
+Date: Sat Feb 3 08:10:48 2007 +0000
+
+ * Added MatchData#values_at
+ * Fixed implementation of MatchData#select
+ * Updated specs for MatchData#values_at, select
+
+commit 4f8301aeb3a5a296a64b887b0f164ca02be2a71f
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Feb 2 17:00:10 2007 +0000
+
+ Added specs for and empty module methods private, protected, public as a first approximation to allow code that uses them to not choke. Added String#match.
+
+commit 15c3678ddcc365891fd92cc9cd33eb22308916e8
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Feb 2 10:43:58 2007 +0000
+
+ Committing Adam Ritter's patch to recognize 'for i in ...' expression, and associated spec. Uncommented line in float_spec.
+
+commit 370d7a955bf6e41c4ea7cf0f9217128ae7a72fd4
+Author: Hurdlea <hurdlea@unknown>
+Date: Fri Feb 2 07:06:22 2007 +0000
+
+ * Added MatchData#inspect, select, to_a, size, to_s
+ * MatchData#[] is now more compliant - behaves more like Array#[]
+ * Added Regexp#hash
+ * Added Regexp#hash spec
+
+commit b496d50c0ebf7d5c523efe2ef5383dd8043aa3f0
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Fri Feb 2 00:36:37 2007 +0000
+
+ Added specs for Dir and modified rubinius_target to allow
+ specs to change directories safely.
+
+commit 6ddf4051f3a6be7076e947bf3eccbc5dd9a7803f
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Feb 1 19:11:14 2007 +0000
+
+ Fixed Float#divmod, returning 0 guard on primitive, raising FloatDomainError rather than ZeroDivisionError. Fixed Float#% when other is zero. Spec try helper doesn't yet work with rubinius_target
+
+commit 679f3fbe54960a690f4e41e1403fdc8f50c0f346
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Feb 1 18:36:44 2007 +0000
+
+ Added more zero division behavior specs.
+
+commit b4d739a7cb68d6f82d657b69aee00923e0bfdbb4
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Feb 1 18:12:02 2007 +0000
+
+ oops, damn keystrokes. Previous commit msg should just include Fixnum. This change points out a problem that I'm not sure about: 1.quo(0) => Infinity in irb and run from a file, but in the spec I get zero division error. wth?
+
+commit 4e24fe43a7d6c55a53880a1c347e836f12937ed4
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Feb 1 18:03:13 2007 +0000
+
+ Added more specs around zero division behavior for Float and Fixnum.
+
+commit b8f412ee2bf3701acd211372d28ec596d6858ac8
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Feb 1 18:01:44 2007 +0000
+
+ Added more specs around zero division behavior for Float and Fixnum.
+
+commit 17a17e3008422bab9e91f8464d1ce2823c13ce78
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Feb 1 17:51:47 2007 +0000
+
+ Added try spec helper method for spec'ing things that raise exceptions. Added more Float specs that describe division by 0 behavior.
+
+commit 2c2bfc3663a34fbf4fd70a5787236ec8b9a87024
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Feb 1 17:22:39 2007 +0000
+
+ Added spec for Float#% when other is zero to show current implementation is broken.
+
+commit d1ddd71d5bd45df0c16651ecad2db3c1b75d90f8
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Feb 1 17:06:56 2007 +0000
+
+ Reverted mae's breakage to Float. seriously mae: you did NOT run the float specs and you did NOT write new specs for the behavior you were changing so you did NOT understand what you were doing. As a good CS student, you can negate the above to know what you SHOULD do. ;) Please, WRITE and RUN your specs.
+
+commit b2e08a170d1ab222d67d8767fa880a5e21c5bf74
+Author: Mae <mae@unknown>
+Date: Thu Feb 1 10:53:48 2007 +0000
+
+ know when to shoot your baby in the crib -- cleanup outdated unused code
+
+commit aac75dd658c96cf930852d86dbc79b66830bace5
+Author: Eero Saynatkari <eero@kittensoft.org>
+Date: Thu Feb 1 09:12:03 2007 +0000
+
+ Improved specs for Hash.[].
+
+commit 40f637f2685e969f097fbbb2ffa3f0173e6f9866
+Author: Mae <mae@unknown>
+Date: Thu Feb 1 02:44:14 2007 +0000
+
+ Fixed my pure ruby Numeric#floor and Numeric#ceil methods
+ - Please smash your c primitive brix BWAHAHA
+ - Also implemented eql? for float and now all 32 float specs pass
+ - Added some edge cases for ceil/float that were not previously covered in specs
+
+commit 5cc6f6b6068e945c6f5896370ee20567e57122e7
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 31 11:24:23 2007 +0000
+
+ Added Float#round. We now have 32 of 32 float specs passing. Please confirm on your platform.
+
+commit 9b902a80a008120a86ae18d4abff04d42efefc8f
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 31 06:22:10 2007 +0000
+
+ Folded in coerce specs.
+
+commit 09e61132d5b9e9b08d27f2f51db9580808bb370e
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 31 03:22:23 2007 +0000
+
+ Created spec/incompatible for specs that show where Rubinius is incompatible with other implementations. Added bignum_spec and fixnum_spec to incompatible dir. Under MRI, Bignum.coerce(Fixnum) => [Bignum, Bignum] whereas Fixnum.coerce(Bignum) => [Float, Float]. Since Bignum should be a seamless extension of Fixnum, this behavior in MRI seems less than consistent. Under Rubinius, mixed Fixnum and Bignum promote to Bignum uniformly, and this makes much more sense. There are other places where Rubinius implementation may deviate from MRI, so spec/incompatible is for describing those behaviors. Updated coerce specs.
+
+commit cb52bb9633d0e323d2f7d6c90879fb7decfea7d7
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Jan 30 10:44:46 2007 +0000
+
+ Added Numeric#coerce primitive. Reimplemented a number of primitves and methods on Fixnum, Float, Bignum to use Numeric#coerce rather than implementing knowledge of one another all over the place. Folded in mae's coerce specs. There is currently a (desirable IMHO) incompatibility in Numeric#coerce in that Bignum.coerce(Fixnum) == Fixnum.coerce(Bignum). There are a lot of other methods that need to be reimplemented using Numeric#coerce. Also, bignum_compare needs to be implemented (just returns 0 atm).
+
+commit e1aa382f2d596a73ef20dfde4184af7a721724e9
+Author: Mae <mae@unknown>
+Date: Tue Jan 30 00:26:11 2007 +0000
+
+ added specs for coercion of Fixnum, Bignum, and Float
+
+commit bd292d64a511eba51ea1569870bcf0fa365c903d
+Author: Cabo <cabo@unknown>
+Date: Sun Jan 28 21:05:10 2007 +0000
+
+ include yesterday's failed cases
+
+commit 7cac7f32e5c80e78aa75dfed7f4822e65d1ab4df
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 28 08:08:12 2007 +0000
+
+ committing rue's continuation specs. doomo arigatoo.
+
+commit f542b93031f8982daa13777d2eada81068e96ad5
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 28 07:03:46 2007 +0000
+
+ committing rue's class specs. thanks rue!
+
+commit 319b6f194d3c699a75de8da2ba3b53b8a4feffb1
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 28 04:49:01 2007 +0000
+
+ Commiting rue's binding specs.
+
+commit d8326b1321cc09c0aa64f441d2a81df6735603fd
+Author: Cabo <cabo@unknown>
+Date: Sat Jan 27 00:02:33 2007 +0000
+
+ remove superfluous p from "& should create an array with no
+ duplicates" (which now passes)
+
+commit 7d3baf10a79c1500e660fe5566ba8f3107d5a826
+Author: Hurdlea <hurdlea@unknown>
+Date: Thu Jan 25 05:43:50 2007 +0000
+
+ * Added MatchData specs
+
+commit 2d9966c9c30e541c18ac77ca646a1af41daf702e
+Author: Hurdlea <hurdlea@unknown>
+Date: Thu Jan 25 05:42:44 2007 +0000
+
+ * Added Match2 and correct Match3 in compiler.rb
+ * Added alias String#to_str
+ * Tweaked a few regexep specs for string return types
+ * Fixed MatchData#length so it uses Tuple#fields to get the no. items
+
+commit 0d9f9e21c2a268e0710c963c745f07d494e2ab1f
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 24 21:43:17 2007 +0000
+
+ Commiting zimbatm's update to exception_spec.
+
+commit 037d8b29872f1c4a81108a0713afd78cbdf9b484
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 20 04:03:20 2007 +0000
+
+ a few more tweaks to get string specs to execute with rcompile and shotgun.
+
+commit cfe7a6b4c87ac3ffccaeb7e70b9e6c386054e052
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 20 03:49:01 2007 +0000
+
+ added parser dir under spec. added parser/symbol_spec.rb to capture parsing a complex symbol like :' for one or two', which rcompile and shotgun choke on at the moment. removed this from core/string_spec because it crashes shotgun and makes it impossible to run all the specs.
+
+commit d39040ab1563f063192a3835723cfbae7bf147cb
+Author: Cabo <cabo@unknown>
+Date: Fri Jan 19 23:45:52 2007 +0000
+
+ lib/kernel.rbc is no longer a required (or wanted) command line argument
+
+commit 4774788e0ae9b24b3ff0b769aede0ba2de3f00b1
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 19 21:05:10 2007 +0000
+
+ added correct guard on Array#first to return nil when array is empty. added specs for #first and #last to describe this behavior. Thanks to cabo for finding this.
+
+commit baf1453678c9906c65b2f7c82bdb0e179e22d1b8
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 19 02:49:02 2007 +0000
+
+ added some minor changes to structure and wording of language/expression_spec. removed array and defined spec from language directory since they were added to language/literals directory. renamed several spec files to follow naming conventions.
+
+commit 7a24923ab9b79b226b6d8831e834ab509d5d2b76
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 19 01:27:10 2007 +0000
+
+ committing zimbatm's patch to language specs. super nice and thank you. please give zimbatm a commit bit!
+
+commit 9cd8c779a88f48604733afbe4357b7101a487669
+Author: Vic <vic@unknown>
+Date: Thu Jan 18 22:20:34 2007 +0000
+
+ Added Proc.given, the analog of MethodContext.current
+ Proc.given obtains the proc given to the current MethodContext.
+ Later will be able to get a Proc from a given Binding.
+
+ Original author: Victor Hugo Borja <vic@rubyforge.org>
+ Date: 2007-01-18 16:16:06+00:00
+
+commit 50c42413d33951397a46edb55ca910a2e8fb87e6
+Author: Vic <vic@unknown>
+Date: Thu Jan 18 21:34:29 2007 +0000
+
+ No output is available for specs if the returning value is a Numeric or Symbol
+ When the :example execution on shotgun evaluates to a Numeric or Symbol, no method
+ :stdout is added, because these object do not have singleton-classes on MRI.
+ If you really need both, stdout and a Numeric/Symbol, your evaluation may lead to
+ an array containing that Numeric/Symbol.
+
+ Also saved MRI from getting eval errors in cases like the following:
+
+ example do
+ class A; end
+ A.new
+ end
+
+ This leads to the following being evaled by MRI: [ #<A:0xb7a69c8c> , stdout]
+ which causes an error because of #<A:0xb7a69c8c> being invalid ruby syntax.
+ This patch fixes this situation by converting #<A:0xb7a69c8c> into "#<A:0xb7a69c8c>"
+
+ Original author: Victor Hugo Borja <vic@rubyforge.org>
+ Date: 2007-01-18 15:23:55+00:00
+
+commit ec5bea103b4b96ecde54668e47ab9e10ac8ec4ee
+Author: Hurdlea <hurdlea@unknown>
+Date: Thu Jan 18 21:04:22 2007 +0000
+
+ * Added bitwsie operators to Fixnum & | ^ << >> ~
+ * Split the fixnum specs into coerced and non-coerced tests
+ * Added primitives to support fixnum bitwise ops
+ * Fixed a bounds tests in Interger#chr
+
+commit 238d7e0611e9198c28a5e0ebe684bc7f1f03bf0f
+Author: Vic <vic@unknown>
+Date: Thu Jan 18 20:11:58 2007 +0000
+
+ [rAdded specs for the new STDOUT support] Empty log message
+
+ Original author: Victor Hugo Borja <vic@rubyforge.org>
+ Date: 2007-01-18 13:44:19+00:00
+
+commit 859b26f38749f160a706ed9dbb8f2a80886e94ef
+Author: Vic <vic@unknown>
+Date: Thu Jan 18 19:37:03 2007 +0000
+
+ Allow to specs to test what is written to STDOUT
+
+ also added String#unindent on spec_helper to help make output heredocs more readable.
+
+ You can access both the evaluation result and the stdout produced, ej:
+
+ context "Rubinius target" do
+
+ specify "should allow to get the resulting STDOUT" do
+ example do
+ puts "hola"
+ puts "space is significant in this heredoc"
+ puts "unindent removes the first blanks found on the first line"
+ puts "on each of these lines"
+ puts "adios"
+ end.stdout.should == <<-OUT.unindent
+ hola
+ space is significant in this heredoc
+ unindent removes the first blanks found on the first line
+ on each of these lines
+ adios
+ OUT
+ end
+
+ specify "should allow to get the lines written to STDOUT" do
+ example do
+ puts "hello"
+ end.stdout_lines.length == 1
+
+ example do
+ print "bye"
+ end.stdout_lines.first.should == "bye"
+ end
+
+ specify "should allow to access the evaluation result along with STDOUT" do
+ result = example do
+ puts "ok"
+ Object.new.class
+ end
+ result.should == Object
+ result.stdout_lines.should == ["ok\n"]
+ end
+
+ end
+
+commit e17069925d139c93acec00161a7111e6c78d54bb
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 18 08:44:33 2007 +0000
+
+ converted shotgun-test/test_sexp to spec/shotgun/sexp_spec. thanks to Victor Borja's recent additions to rubinius_target, it was a breeze.
+
+commit 46e9a259bc2212dee1fa7efa8ead468e63970731
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 17 08:02:35 2007 +0000
+
+ filled in the rest of the documented String instance methods except #pack.
+
+commit dfd08d6536ea497cf86d06ca503206d54b19479d
+Author: Cabo <cabo@unknown>
+Date: Tue Jan 16 10:24:18 2007 +0000
+
+ A bit more array fun (and lots of FIXMEs)
+
+commit b1e50e43d8d79a5dbd82345134ecd4bdffc6d182
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Jan 16 08:46:48 2007 +0000
+
+ and yet a few more string specs. these will asymptotically approach done.
+
+commit 4048d3dfa90a6de54ea2ed0aec2ec6adafb50b0c
+Author: Cabo <cabo@unknown>
+Date: Tue Jan 16 07:02:50 2007 +0000
+
+ I want to see what 'Shotgun has crashed' means, beautiful backtrace and all
+
+commit 0387baa914cb35c589c7872f7f98cf9f8ee10711
+Author: Cabo <cabo@unknown>
+Date: Tue Jan 16 02:19:45 2007 +0000
+
+ Fix Array#slice! bug workarounds
+
+commit c6b110b47667c5d6750492177492434f4c0446f8
+Author: Cabo <cabo@unknown>
+Date: Tue Jan 16 01:33:18 2007 +0000
+
+ add shift spec and fix String#strip so it works
+
+commit 19bcc086b7674f12e01f879a6ca83f3289feb770
+Author: Cabo <cabo@unknown>
+Date: Tue Jan 16 00:06:03 2007 +0000
+
+ Integer#chr should return a new string (spec)
+
+commit 5aa81499711ad5e57f5dfc03417f23705eb79b44
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 15 23:48:16 2007 +0000
+
+ a few more string specs.
+
+commit 8cd873e183c62b8929305ea54b9a437ca22ddb28
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 15 22:42:24 2007 +0000
+
+ committing Victor's define_method patch.
+
+commit cd04f4c570cd95fb869f025c4dac6e9342e2ba2a
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 15 22:02:23 2007 +0000
+
+ committing Victor Hugo Borja's instance_eval patch.
+
+commit a481142988d585bb8fa54e0186f5c9cf88ada8d9
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 15 20:13:03 2007 +0000
+
+ added a code method to mri_target and jruby_target to parallel the behavior of the code method for rubinius_target. now core/proc_spec.rb is passing with mri target.
+
+commit a4c621e8319349eda766f73ed9ca55f2a9323ac2
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 15 19:08:22 2007 +0000
+
+ checked in nicksieger's patch to spec_helper that enables specs to run on jruby, woohoo!
+
+commit 6b02aac6107b01258f85f9d15a77b498ad15e5b0
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 15 18:06:23 2007 +0000
+
+ checking in cabo's changes to target specs for jruby and rubinius. modified rubinius_target specs that compared paths to use should_match because a hash is used to generate part of the path.
+
+commit 6aa175d3367d76152476888ff1c52479530c56a2
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 15 17:57:25 2007 +0000
+
+ committing cabo's changes to remove heredocs from a number of specs. It is still possible to pass code as a source string to the example method. Use this if the block method is causing rubinius to choke on the ruby2ruby generated source. soon, soon, we'll have rspec running (I hope\!).
+
+commit 6679194f8e6afdbbb71f5213508bb81f12fdb2e7
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 14 17:28:06 2007 +0000
+
+ incorporated nicksieger's changes to mri_target removing needless requires. added jruby_target.rb and spec to parallel mri_target.rb.
+
+commit f573b9c16efccb92eec98d923831deafc7a3c809
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 14 08:10:24 2007 +0000
+
+ converted mri_target to use eval, yield to execute specs. converted some specs to new style. addressed issues with hash specs that implicitly relied on hash ordering, fixed numerous issues that result from loss of floating-point precision by using #inspect where necessary (more of these issues may arise in the specs on different platforms). added spec templates for documented String instance methods (many of which need to be filled in).
+
+commit 917cd03e5bee749d18d8d0c257381bca2362abbd
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 14 00:11:17 2007 +0000
+
+ checking in Alan Hurdles patch to allow running specific files, e.g. COMPILER=rcompile rake spec:core:array .
+
+commit 508eaacf9aaf67465a78ac53284ba6f06c3bcb3d
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 13 21:09:51 2007 +0000
+
+ added integer specs. these should be platform independent but other platforms may have some trouble with spec for 'chr' ;)
+
+commit 21463a87bac2121fa61c1c99927cdb039c724d89
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 13 20:37:37 2007 +0000
+
+ implemented the rest of the hash specs, this should cover the documented class, instance methods.
+
+commit 3656a95a1829b0be1a8b0d968e0a9e433ef9c847
+Author: Frederick <frederick@unknown>
+Date: Sat Jan 13 13:14:41 2007 +0000
+
+ Implements Fixnum#size
+
+commit ca38e49022f6bdf41b0e98409d3fec3528e59bfd
+Author: Frederick <frederick@unknown>
+Date: Sat Jan 13 12:54:30 2007 +0000
+
+ shotgun/string_spec.rb now follow new spec conventions
+
+commit 1b684385fe970f11a526e280d15c3f147a826886
+Author: Frederick <frederick@unknown>
+Date: Sat Jan 13 12:42:38 2007 +0000
+
+ language/expression_spec.rb and language/exception_spec.rb now use new spec style
+
+commit f62c2539a1eef27b356e4d809d76c4f9ddecd2a5
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 13 08:14:12 2007 +0000
+
+ new style specs for hash. numerous of these need to be implemented but there should be templates for all the documented class and instance methods.
+
+commit 28083a6e6dc89502d1c76e2a16f0003a589f01e0
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 13 08:12:37 2007 +0000
+
+ one more, bignum.
+
+commit 076aab00b795ff777c5ac11955130f12f69e1377
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 13 08:07:30 2007 +0000
+
+ new style specs for float, fixnum, symbol, string, object, file, module, regexp, range.
+
+commit 0161ab3527e91674eed4eeaad029eee654325155
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 13 07:39:26 2007 +0000
+
+ true, false, nil, enumerable specs are new style.
+
+commit bb11cce41a472606312eb0a62948c4a339f23dd9
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 13 07:27:05 2007 +0000
+
+ converted existing class and comparable specs to new style.
+
+commit 595b83a75044772136b83eaf84402ed73eb79da5
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Jan 13 07:08:14 2007 +0000
+
+ ladies and gentlemen, a huge round of applause and gratitude to headius for inspiration and help getting specs in a form that will easily run on MRI, JRuby, and Rubinius. Checking in the modified spec/core/array_spec.rb. The rest to follow. The mri_target is still using the sub-process method, but that should be superfluous now.
+
+commit ae75e76915432757be3c9a7126c2ee8c6656652c
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 12 08:13:29 2007 +0000
+
+ added a bunch more specs for array. two still need to be filled out. I think that covers all the documented class and instance methods.
+
+commit 8daf38e0f99eed3e42d654086a98e673d9855bef
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 12 01:14:35 2007 +0000
+
+ checking in more of cabo's changes to kernel/core/array.rb and array specs.
+
+commit 0a349583aba629748d5e85de0ede6f38730512f1
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 12 00:57:22 2007 +0000
+
+ checking in cabo's changes to array and array spec.
+
+commit 052512fea9b74e532ef6b68612c81061ad84e4f2
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 11 05:16:47 2007 +0000
+
+ finished Bignum specs for documented instance methods.
+
+commit 7f4786c85b2e0e95abc2728492ed1a7424d01dbe
+Author: Frederick <frederick@unknown>
+Date: Wed Jan 10 23:44:34 2007 +0000
+
+ Added File.mtime, File.atime, File.ctime
+ Avoid reusing old .rbc is .rb is newer
+ Remove useless CHECK_PTR
+
+commit 70458d6446f0858570571a64b5294c0bb4ac358f
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 10 17:40:36 2007 +0000
+
+ moved exception and expression specs to spec/language. added stub for time_spec in spec/library. updated a string spec that was failing. added specs for all (I think) float and fixnum instance methods. added specs for bignum, but about half need examples.
+
+commit ffe4a7a48dcc116f73b89b9a046d4430ed51975a
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Jan 9 18:07:31 2007 +0000
+
+ removed duplicate bk task from Rakefile. removed shotgun-tests/test-array.rb as all tests have corresponding specs in spec/core. added beginning of specs for bignum separated into spec/core for stuff that should be indendent of mri or rubinius, and spec/shotgun for implementation specific.
+
+commit 946d0b42293ea081666e71e13c4b77d5b5dba886
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 7 05:38:33 2007 +0000
+
+ checking in Alan Hurdles patches to regexp, string, and spec_string.
+
+commit 5f035040c4b3ce842fff4b39d1ca657c97deb7a4
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 7 03:03:31 2007 +0000
+
+ updated mri and rubinius target impl specs. added environment option for running rubinius target using obsolete.rcompile, e.g.: COMPILER=rcompile spec spec/core/symbol_spec -f s. If you don't use the COMPILER env var, rubinius target will use shotgun to compile.
+
+commit 6ea911ae5740508cdbd8feb5cddba5b8bf7fe1c3
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Jan 7 02:32:35 2007 +0000
+
+ changed some Hash specs to use instance vars rather than local vars because some versions of Ruby2Ruby output borked sexp for block local vars. E.g. use @h rather than h.
+
+commit 256fe9a8cada7ed512556e1701a5264670c6c28f
+Author: Mae <mae@unknown>
+Date: Sat Jan 6 07:55:32 2007 +0000
+
+ made regression spec for buggy behavior of [1,2,3][2..-1]
+
+commit 0ffe8a3e6cd92bc5cd872cc22919885ea80366a0
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 5 20:05:03 2007 +0000
+
+ added spec for String#reverse! to Laurent Julliard's spec for String#reverse and his implementation of both methods.
+
+commit 170737d2c77a4b2de862380cb87f7705560cca64
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri Jan 5 18:24:32 2007 +0000
+
+ * Much better implementation of Hash#key?
+ * Added working support for default Hash values and procs
+ * Added hash_get_undef for situations where nil and undefined hash values need to be differentiated
+
+commit 332378a8900f09009626cb7c4dbf0c8740a657c7
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 5 07:31:56 2007 +0000
+
+ added specs for aliases of Hash#key?
+
+commit 27c3b2aeaa3208a7e0218f051f623b93e2e635d8
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 5 07:28:29 2007 +0000
+
+ added spec for Hash#key?
+
+commit 2e88d941bc1b1ea506396a915e3c3c4e3dfd1601
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Jan 5 06:35:09 2007 +0000
+
+ updated and simplified float and fixnum specs.
+
+commit e4a5b6d8529d60e62875004bb60f33c6452ccf98
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Fri Jan 5 06:28:04 2007 +0000
+
+ * defined?() now handles: defined?(Kernel.puts) flavors of arguments.
+
+commit 27660379c09e561590cf1bc48a9459e29fc00e9c
+Author: Mae <mae@unknown>
+Date: Fri Jan 5 06:10:11 2007 +0000
+
+ Added spec for cvar declaration in class bodies
+
+commit a9b7b9f7db02ba78514eb869c2c52d4e5067f8d2
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 4 08:08:59 2007 +0000
+
+ added class def source code to specs.
+
+commit cdfa499ee238671c655800a81b51611704383500
+Author: Mae <mae@unknown>
+Date: Thu Jan 4 07:54:20 2007 +0000
+
+ fixed typo in spec still 7 failing specs *GLARES AT DEFILER*
+
+commit 03be9ca7da61363f8f0a02ee951bbafaf297c31b
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 4 07:43:58 2007 +0000
+
+ added specs for Module#const_defined?.
+
+commit c511d4c7a75001f2597b13f9fc2e910d2dd4d9a2
+Author: Mae <mae@unknown>
+Date: Thu Jan 4 06:54:07 2007 +0000
+
+ changed defined spec to be more dumb and just figure out whether its a true/false evaluation
+
+commit 4d9135f9694e4b692faf6a4c7b8dcd59f79f5069
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 4 06:38:57 2007 +0000
+
+ added library spec (beginning) for enumerator. trivial update for comparable specs. added specs for enumerable.
+
+commit 3de6e339526b1402f8995a5acc38fde707ec0695
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Jan 4 00:34:50 2007 +0000
+
+ added specs for comparable methods.
+
+commit ea25c6b17533c280e640dc97e3fec1207fb4be7b
+Author: Mae <mae@unknown>
+Date: Wed Jan 3 17:47:09 2007 +0000
+
+ almost done with defined? spec -- still need 'yield' and 'zsuper' test cases
+ from project dir: SPEC_TARGET=mri spec spec/language/defined_spec.rb
+ change SPEC_TARGET to rubinius to test on rubinius
+
+commit b795f6c2dc98952e7fa7231cded9156ade962b18
+Author: Mae <mae@unknown>
+Date: Wed Jan 3 17:25:31 2007 +0000
+
+ added incomplete specs for defined? behavior -- more work to be done
+
+commit 25e30e4668f1ef814bfb1182e032449263651590
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 3 17:17:03 2007 +0000
+
+ small fix to mri_target to generate reasonable cache soure name. added a couple specs.
+
+commit 3b5bc977ea2a4a3ad42ff83bcab2966459c262c0
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 3 08:51:18 2007 +0000
+
+ added object_spec for methods provided by Object, even mixed in ones. added a few specs for basic class, module, exceptions.
+
+commit c4f4dd722658b2a09ae273092744b76e65ce05b2
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Jan 3 02:36:58 2007 +0000
+
+ renamed flow_control_spec to expression_spec as these are all covered under heading expressions in pickaxe. run expression spec with mri target and then rubinius target to see an interesting rubinius failure. updated an incorrect string splice spec.
+
+commit ef8aa2f74896944134f5a8884ccc723ca9b472c1
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Jan 2 01:40:16 2007 +0000
+
+ separated specs that are shotgun specific methods (e.g. String#prefix?) into spec/shotgun/... fixed wrong specs so that all pass under mri/mir configuration.
+
+commit 8661488b40b9fccccf356889834c6a9162c8bebf
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 1 22:37:41 2007 +0000
+
+ very quick n' dirty implementation of example et al to run specs under mri like under rubinius.
+
+commit 0ebfa43e222ca4794243d36f10c2e429e930f527
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 1 20:47:13 2007 +0000
+
+ added methods for TrueClass and FalseClass, updated specs for each.
+
+commit e9bb50ced8bf997535f0bc9c6deeeffb86c40879
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 1 20:29:37 2007 +0000
+
+ added specs for true, false, nil.
+
+commit c30b0e38b88c686c11c6f7442e027d685d405505
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 1 18:59:59 2007 +0000
+
+ added spec templates for true, false, nil, class, module, enumerable, comparable, flow_control, exception. added specs to various others.
+
+commit b26777261970c213506e444b90412543f39b3c59
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 1 18:58:37 2007 +0000
+
+ added that rubinius target example method takes a default argument to pass strings of code to allow for creating classes, since classes can't be defined in a method body and for now example puts the block code into a method using ruby2ruby.
+
+commit 560c5a1331c76bce07289f1e5950b816fe7c9c24
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Jan 1 01:17:44 2007 +0000
+
+ added more string specs.
+
+commit 807864c76b701f6f976f3f2935599ba875fcc10e
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Dec 31 23:13:16 2006 +0000
+
+ added more core specs (or templates for specs) to cover existing tests in shotgun-tests.
+
+commit 8f83b600b12722d46b9791b2e2c3a399618474a0
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Dec 31 21:23:07 2006 +0000
+
+ removed shotgun/primitives_spec as spec/shotgun should be for shotgun-specific code. created spec/core for ruby core classes. spec/library is now for ruby stdlib classes. added more array specs.
+
+commit 58cc3ce5bd8b84a151b1a6e2334845f268ab894a
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Dec 31 08:58:50 2006 +0000
+
+ added specs for class methods of Regexp. added alias Regexp.compile for Regexp.new.
+
+commit 0a5a31bc6ef539eeda3de951ab633f5152d58153
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Dec 31 02:01:34 2006 +0000
+
+ added specs for symbol methods. added aliases to symbol for to_i, to_int, and id2name.
+
+commit cdfa28e492b8edfa55b950b06ce05ebb04b64643
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Dec 30 17:27:18 2006 +0000
+
+ changed example method for rubinius_target to raise exception if compile fails. added specs for range.
+
+commit 5316e652084b8624828d0a9306f580bfc93184dc
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Dec 29 20:37:46 2006 +0000
+
+ added spec files in spec/library for basic types (according to pickaxe book). added specs for all the methods in Regexp. there are many failing specs for a variety of reasons, but the goal is to get a good overview of where work needs to be done. more specs to follow.
+
+commit 06cd5ad6da819f2894996e42f4da70321767c9c7
+Author: Mae <mae@unknown>
+Date: Thu Dec 28 07:36:21 2006 +0000
+
+ Added Array expressions gleaned from spec/library/array_spec.rb as proof of concept for rapid compatibility testing
+ - A thought occurred to me that the scope for this type of testing might be limited severely to simple compatibility testing
+ - Its not very human understandable as a spec -- it just unravels incompatibilities given no hint as to why things are the way they are.
+ - It lets the ruby rval speak for itself
+ - Is this useful for rubinius?!
+
+commit 1c1fc9335aee4acbcd692c555b0ca194c5301013
+Author: Frederick <frederick@unknown>
+Date: Wed Dec 27 22:46:43 2006 +0000
+
+ Fix a bug in the allocation of a string. The underlying storage (byte array) did not have the correct size, leading to a write in a non allocated memory area.
+ The rationale is that, the storage are should be able to store the string plus a terminal \0. As we're allocating per block of 4 bytes (a word) we need to get the nearest multiple of 4.
+ This patch adds a spec to highlight the bug, and a fix to .. well, fix it ;)
+
+commit cdfdc272b71bbfca23b4c17e5572ebd2b966615e
+Author: Mae <mae@unknown>
+Date: Tue Dec 26 17:52:24 2006 +0000
+
+ Added my idea of a sanity check against MRI for compatibility purposes
+ - try it out!
+ - rake spec:compatibility
+ - example compatibility expressions go in spec/compatibility/expressions/*
+ - all the expression files are line-separated ruby expressions that return something basic and eval-able
+
+commit f9887648c7f239f8c862158b39f44b2410377204
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Dec 22 19:20:58 2006 +0000
+
+ added spec file for String methods.
+
+commit 8178e4478977c81940ac4bdcd8bea608be11708b
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Dec 22 17:14:46 2006 +0000
+
+ fixed that ruby2ruby was not generating correct ruby source when a local var was used in a block. changed local var to instance var and it works, converted primitives_spec to new block-style.
+
+commit 8a00080a1edb864af7573e8f9761f65fa1202d07
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Dec 22 06:58:28 2006 +0000
+
+ changed array_spec to not use local variables in blocks where possible because rubytoruby is not converting them to ruby source correctly. Array#uniq! fails at the moment. re-added that compile checks code-cache first so specs run faster.
+
+commit 4a199c559eebe73cd21d0997126b799f5d4e2be5
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Dec 22 06:00:21 2006 +0000
+
+ converted array_spec.rb to block-style specs, but they still depend on strings to be output. rewrote spec_helper based on nicksieger's example code. some specs are failing due to bugs converting to sexp and back to ruby source.
+
+commit 854bbc3617559a2ceac975d79a57ffa825a5cda6
+Author: Brian Ford <brixen@gmail.com>
+Date: Fri Dec 22 03:34:18 2006 +0000
+
+ added spec/targets for specs for 'target' part of host/target spec runner configuration. added mri_target and rubinius_target and specs.
+
+commit db81559c9c914413d2064b4202ec8ce43e503af2
+Author: Brian Ford <brixen@gmail.com>
+Date: Thu Dec 21 05:51:55 2006 +0000
+
+ added spec:targets task to run specs for target part of host/target spec configuration. added specs for mri_target and rubinius_target.
+
+commit a243a70bd17ec7e9839b69bb18e63d5d943b6095
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Dec 20 19:02:01 2006 +0000
+
+ updated primitives_spec to use example method.
+
+commit 9ce077283dd10c21377add499c5bcc4ea87cfe0f
+Author: Brian Ford <brixen@gmail.com>
+Date: Wed Dec 20 17:11:00 2006 +0000
+
+ Changed method from rubinius to example for specs. This is in anticipation of having independent 'host' (system running rspec) and 'target' (system executing spec). Created parallel arrayb_spec that illustrates this with a mri/mri configuration. Also added spec_bhelper that is a *very rough* beginning for having mri/rubinius configuration using block-style specs.
+
+commit a8ad71a0da9e1a866521074743ea1dfcceb596cb
+Author: Wilson Bilkovich <wilson@supremetyrant.com>
+Date: Wed Dec 20 15:39:36 2006 +0000
+
+ Applying 'array patch' from Jason Perkins (2006-12-20 8:30 EST)
+
+commit 82b3d880131e7080ebc6b4289b3954d89a988c13
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Dec 19 17:30:21 2006 +0000
+
+ added more specs to array_spec. most of these are failing, so there seems to be a lot of Array that needs implementing.
+
+commit c24f0e83b4d446afd541ffefbcb313f199b684ee
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Dec 19 08:24:00 2006 +0000
+
+ added specs for Array#* and <<, simplified other specs.
+
+commit 62b0737ce69768a8292bd3d9f13401ec8056f6eb
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Dec 19 07:46:39 2006 +0000
+
+ fixed messed up spec for &.
+
+commit 7635389b2abf5c492952a49eb8251d6fb34250c7
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Dec 19 07:40:01 2006 +0000
+
+ added spec for Array#&, which is currently unimplemented. updated spec_helper.
+
+commit 42b5e9fc47bcf6bcd403d25795c9bcf07bb5c007
+Author: Brian Ford <brixen@gmail.com>
+Date: Tue Dec 19 06:13:15 2006 +0000
+
+ added spec tasks :only to run only spec, :language for high level language conformance spcs, :library for ruby stdlib implementation specs, and :shotgun for specs related to shotgun. rake spec will run all specs and tests. removed spec/spec_suite.rb because all specs can be run from rake. minor updates to spec_helper.
+
+commit da4c42890f4b8163b8d49de64bdb76c16b0e5d1f
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Dec 18 17:41:44 2006 +0000
+
+ added shotgun dir under spec for things that relate to shotgun implementation of VM, like the prmitives_spec, while reserving spec/library for general ruby implementation of the std lib.
+
+commit 925cbf9f84f78a48189f7030c205942d266a6f66
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Dec 18 09:45:25 2006 +0000
+
+ added Fixnum#% primitive implementation. changed array_spec to use Fixnum#%. fixed test_primitive test for Fixnum#%.
+
+commit 06e50e48a92e3fa7d1fc4d6b681872d08e5aeba3
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Dec 18 09:10:37 2006 +0000
+
+ Added Fixnum#% primitive test, spec, cpu/primitive, stub.
+
+commit e0fbcf29f46dde89f65d13c1b6d7601a470cf223
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Dec 18 07:54:02 2006 +0000
+
+ all ports of test_array test to array_spec are now passing.
+
+commit c2330c2ff65cfa964d954340fb0eb2507972efd5
+Author: Brian Ford <brixen@gmail.com>
+Date: Mon Dec 18 07:39:55 2006 +0000
+
+ updated spec_helper to ensure code-cache dir exists. fixed several failing specs in array_spec by correcting expected value.
+
+commit 81cc03c6f7ce499da563543f00d273d3a9c3a184
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Dec 17 06:27:15 2006 +0000
+
+ Ported the rest of test_array.rb tests to specs. Several of these specs are not passing but ported them all to illustrate behavior of specs.
+
+commit b4cb073931f403119a5ed9b63a2c915612a9c46f
+Author: Brian Ford <brixen@gmail.com>
+Date: Sun Dec 17 04:42:55 2006 +0000
+
+ spec/spec_helper.rb rubinius method is a very naive port of shotgun-tests/helper.rb run_code method. spec/library/array_spec.rb is several ports of the tests in shotgun-tests/test_array.rb, which is testing /kernel/array.rb. In other words, you can now create specs that run under RSpec (which is running under MRI 1.8.x) that exercises the rubinius vm, shotgun, and the stdlib that is being written in ruby. Confused? Read the source, Luke. :)
+
+commit 1b37cd1ee800060fb215a52d2902c3f4b778a656
+Author: Brian Ford <brixen@gmail.com>
+Date: Sat Dec 16 07:55:41 2006 +0000
+
+ Added spec dir with spec_suite.rb and spec_helper.rb provided by nullstyle.
diff --git a/spec/ruby/CONTRIBUTING.md b/spec/ruby/CONTRIBUTING.md
new file mode 100644
index 0000000000..7c9363da37
--- /dev/null
+++ b/spec/ruby/CONTRIBUTING.md
@@ -0,0 +1,243 @@
+Contributions are much appreciated.
+Please open a pull request or add an issue to discuss what you intend to work on.
+If the pull requests passes the CI and conforms to the existing style of specs, it will be merged.
+
+### File organization
+
+Spec are grouped in 5 separate top-level groups:
+
+* `command_line`: for the ruby executable command-line flags (`-v`, `-e`, etc)
+* `language`: for the language keywords and syntax constructs (`if`, `def`, `A::B`, etc)
+* `core`: for the core methods (`Fixnum#+`, `String#upcase`, no need to require anything)
+* `library`: for the standard libraries methods (`CSV.new`, `YAML.parse`, need to require the stdlib)
+* `optional/capi`: for functions available to the Ruby C-extension API
+
+The exact file for methods is decided by the `#owner` of a method, for instance for `#group_by`:
+```ruby
+> [].method(:group_by)
+=> #<Method: Array(Enumerable)#group_by>
+> [].method(:group_by).owner
+=> Enumerable
+```
+Which should therefore be specified in `core/enumerable/group_by_spec.rb`.
+
+### MkSpec - a tool to generate the spec structure
+
+If you want to create new specs, you should use `mkspec`, part of [MSpec](http://github.com/ruby/mspec).
+
+ $ ../mspec/bin/mkspec -h
+
+#### Creating files for unspecified modules or classes
+
+For instance, to create specs for `forwardable`:
+
+ $ ../mspec/bin/mkspec -b library -rforwardable -c Forwardable
+
+Specify `core` or `library` as the `base`.
+
+#### Finding unspecified core methods
+
+This is very easy, just run the command below in your `spec` directory.
+`ruby` must be a recent version of MRI.
+
+ $ ruby --disable-gem ../mspec/bin/mkspec
+
+You might also want to search for:
+
+ it "needs to be reviewed for spec completeness"
+
+which indicates the file was generated but the method unspecified.
+
+### Matchers and expectations
+
+Here is a list of frequently-used matchers, which should be enough for most specs.
+There are a few extra specific matchers used in the couple specs that need it.
+
+```ruby
+(1 + 2).should == 3 # Calls #==
+(1 + 2).should_not == 5
+
+File.should equal(File) # Calls #equal? (tests identity)
+(1 + 2).should eql(3) # Calls #eql? (Hash equality)
+
+1.should < 2
+2.should <= 2
+3.should >= 3
+4.should > 3
+
+"Hello".should =~ /l{2}/ # Calls #=~ (Regexp match)
+
+[].should be_empty # Calls #empty?
+[1,2,3].should include(2) # Calls #include?
+
+(0.1 + 0.2).should be_close(0.3, TOLERANCE) # (0.2-0.1).abs < TOLERANCE
+(0.0/0.0).should be_nan # Calls Float#nan?
+(1.0/0.0).should be_positive_infinity
+(-1.0/0.0).should be_negative_infinity
+
+3.14.should be_an_instance_of(Float) # Calls #instance_of?
+3.14.should be_kind_of(Numeric) # Calls #is_a?
+Numeric.should be_ancestor_of(Float) # Float.ancestors.include?(Numeric)
+
+3.14.should respond_to(:to_i) # Calls #respond_to?
+Fixnum.should have_instance_method(:+)
+Array.should have_method(:new)
+# Also have_constant, have_private_instance_method, have_singleton_method, etc
+
+-> {
+ raise "oops"
+}.should raise_error(RuntimeError, /oops/)
+
+# To avoid! Instead, use an expectation testing what the code in the lambda does.
+# If an exception is raised, it will fail the example anyway.
+-> { ... }.should_not raise_error
+
+-> {
+ Fixnum
+}.should complain(/constant ::Fixnum is deprecated/) # Expect a warning
+```
+
+### Guards
+
+Different guards are available as defined by mspec.
+Here is a list of the most commonly-used guards:
+
+```ruby
+ruby_version_is ""..."2.4" do
+ # Specs for RUBY_VERSION < 2.4
+end
+
+ruby_version_is "2.4" do
+ # Specs for RUBY_VERSION >= 2.4
+end
+
+platform_is :windows do
+ # Specs only valid on Windows
+end
+
+platform_is_not :windows do
+ # Specs valid on platforms other than Windows
+end
+
+platform_is :linux, :darwin do # OR
+end
+
+platform_is_not :linux, :darwin do # Not Linux and not Darwin
+end
+
+platform_is wordsize: 64 do
+ # 64-bit platform
+end
+
+big_endian do
+ # Big-endian platform
+end
+
+# In case there is a bug in MRI but the expected behavior is obvious
+# 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
+ruby_bug '#13669', ''...'2.5' do
+ it "works like this" do
+ # Specify the expected behavior here, not the bug
+ end
+end
+
+
+# Combining guards
+guard -> { platform_is :windows and ruby_version_is ""..."2.3" } do
+ # Windows and RUBY_VERSION < 2.3
+end
+
+guard_not -> { platform_is :windows and ruby_version_is ""..."2.3" } do
+ # The opposite
+end
+
+# Custom guard
+max_uint = (1 << 32) - 1
+guard -> { max_uint <= fixnum_max } do
+end
+```
+
+Custom guards are better than a simple `if` as they allow [mspec commands](https://github.com/ruby/mspec/issues/30#issuecomment-312487779) to work properly.
+
+In general, the usage of guards should be minimized as possible.
+
+There are no guards to define implementation-specific behavior because
+the Ruby Spec Suite defines common behavior and not implementation details.
+Use the implementation test suite for these.
+
+If an implementation does not support some feature, simply tag the related specs as failing instead.
+
+### Shared Specs
+
+Often throughout Ruby, identical functionality is used by different methods and modules. In order
+to avoid duplication of specs, we have shared specs that are re-used in other specs. The use is a
+bit tricky however, so let's go over it.
+
+Commonly, if a shared spec is only reused within its own module, the shared spec will live within a
+shared directory inside that module's directory. For example, the `core/hash/shared/key.rb` spec is
+only used by `Hash` specs, and so it lives inside `core/hash/shared/`.
+
+When a shared spec is used across multiple modules or classes, it lives within the `shared/` directory.
+An example of this is the `shared/file/socket.rb` which is used by `core/file/socket_spec.rb`,
+`core/filetest/socket_spec.rb`, and `core/file/state/socket_spec.rb` and so it lives in the root `shared/`.
+
+Defining a shared spec involves adding a `shared: true` option to the top-level `describe` block. This
+will signal not to run the specs directly by the runner. Shared specs have access to two instance
+variables from the implementor spec: `@method` and `@object`, which the implementor spec will pass in.
+
+Here's an example of a snippet of a shared spec and two specs which integrates it:
+
+``` ruby
+# core/hash/shared/key.rb
+describe :hash_key_p, shared: true do
+ it "returns true if the key's matching value was false" do
+ { xyz: false }.send(@method, :xyz).should == true
+ end
+end
+
+# core/hash/key_spec.rb
+describe "Hash#key?" do
+ it_behaves_like :hash_key_p, :key?
+end
+
+# core/hash/include_spec.rb
+describe "Hash#include?" do
+ it_behaves_like :hash_key_p, :include?
+end
+```
+
+In the example, the first `describe` defines the shared spec `:hash_key_p`, which defines a spec that
+calls the `@method` method with an expectation. In the implementor spec, we use `it_behaves_like` to
+integrate the shared spec. `it_behaves_like` takes 3 parameters: the key of the shared spec, a method,
+and an object. These last two parameters are accessible via `@method` and `@object` in the shared spec.
+
+Sometimes, shared specs require more context from the implementor class than a simple object. We can address
+this by passing a lambda as the method, which will have the scope of the implementor. Here's an example of
+how this is used currently:
+
+``` ruby
+describe :kernel_sprintf, shared: true do
+ it "raises TypeError exception if cannot convert to Integer" do
+ -> { @method.call("%b", Object.new) }.should raise_error(TypeError)
+ end
+end
+
+describe "Kernel#sprintf" do
+ it_behaves_like :kernel_sprintf, -> (format, *args) {
+ sprintf(format, *args)
+ }
+end
+
+describe "Kernel.sprintf" do
+ it_behaves_like :kernel_sprintf, -> (format, *args) {
+ Kernel.sprintf(format, *args)
+ }
+end
+```
+
+In the above example, the method being passed is a lambda that triggers the specific conditions of the shared spec.
+
+### Style
+
+Do not leave any trailing space and follow the existing style.
diff --git a/spec/ruby/LICENSE b/spec/ruby/LICENSE
new file mode 100644
index 0000000000..d581dd1c9f
--- /dev/null
+++ b/spec/ruby/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2008 Engine Yard, Inc. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/spec/ruby/README.md b/spec/ruby/README.md
new file mode 100644
index 0000000000..eef4e9ca0b
--- /dev/null
+++ b/spec/ruby/README.md
@@ -0,0 +1,108 @@
+# The Ruby Spec Suite
+
+[![Build Status](https://travis-ci.org/ruby/spec.svg)](https://travis-ci.org/ruby/spec)
+[![Build Status](https://ci.appveyor.com/api/projects/status/1gs6f399320o44b1?svg=true)](https://ci.appveyor.com/project/eregon/spec-x948i)
+[![Gitter](https://badges.gitter.im/ruby/spec.svg)](https://gitter.im/ruby/spec)
+
+The Ruby Spec Suite is a test suite for the behavior of the Ruby programming language.
+
+It is not a standardized specification like the ISO one, and does not aim to become one.
+Instead, it is a practical tool to describe and test the behavior of Ruby with code.
+
+Every example code has a textual description, which presents several advantages:
+
+* It is easier to understand the intent of the author
+* It documents how recent versions of Ruby should behave
+* It helps Ruby implementations to agree on a common behavior
+
+The specs are written with syntax similar to RSpec 2.
+They are run with MSpec, the purpose-built framework for running the Ruby Spec Suite.
+For more information, see the [MSpec](http://github.com/ruby/mspec) project.
+
+The specs describe the [language syntax](language/), the [core library](core/), the [standard library](library/), the [C API for extensions](optional/capi) and the [command line flags](command_line/).
+The language specs are grouped by keyword while the core and standard library specs are grouped by class and method.
+
+ruby/spec is known to be tested in these implementations for every commit:
+* [MRI](http://rubyci.org/) on 30 platforms and 4 versions
+* [JRuby](https://github.com/jruby/jruby/tree/master/spec/ruby) for both 1.7 and 9.x
+* [TruffleRuby](https://github.com/oracle/truffleruby/tree/master/spec/ruby)
+* [Opal](https://github.com/opal/opal/tree/master/spec)
+
+The specs are synchronized both ways around once a month by @eregon between ruby/spec, MRI, JRuby and TruffleRuby.
+Each of these repositories has a full copy of the files to ease editing specs.
+
+ruby/spec describes the behavior of Ruby 2.3 and more recent Ruby versions.
+More precisely, every latest stable MRI release [passes](https://rubyci.org/) all specs of ruby/spec
+(2.3.x, 2.4.x, 2.5.x, etc).
+
+For older specs try these commits:
+* Ruby 2.0.0-p647 - [Suite](https://github.com/ruby/spec/commit/245862558761d5abc676843ef74f86c9bcc8ea8d) using [MSpec](https://github.com/ruby/mspec/commit/f90efa068791064f955de7a843e96e2d7d3041c2) (may encounter 2 failures)
+* Ruby 2.1.9 - [Suite](https://github.com/ruby/spec/commit/f029e65241374386077ac500add557ae65069b55) using [MSpec](https://github.com/ruby/mspec/commit/55568ea3918c6380e64db8c567d732fa5781efed)
+* Ruby 2.2.10 - [Suite](https://github.com/ruby/spec/commit/cbaa0e412270c944df0c2532fc500c920dba0e92) using [MSpec](https://github.com/ruby/mspec/commit/d84d7668449e96856c5f6bac8cb1526b6d357ce3)
+
+### Running the specs
+
+First, clone this repository:
+
+ $ git clone https://github.com/ruby/spec.git
+
+Then move to it:
+
+ $ cd spec
+
+Clone [MSpec](http://github.com/ruby/mspec):
+
+ $ git clone https://github.com/ruby/mspec.git ../mspec
+
+And run the spec suite:
+
+ $ ../mspec/bin/mspec
+
+This will execute all the specs using the executable named `ruby` on your current PATH.
+
+### Running Specs with a Specific Ruby Implementation
+
+Use the `-t` option to specify the Ruby implementation with which to run the specs.
+The argument may be a full path to the Ruby binary.
+
+ $ ../mspec/bin/mspec -t /path/to/some/bin/ruby
+
+### Running Selected Specs
+
+To run a single spec file, pass the filename to `mspec`:
+
+ $ ../mspec/bin/mspec core/kernel/kind_of_spec.rb
+
+You can also pass a directory, in which case all specs in that directories will be run:
+
+ $ ../mspec/bin/mspec core/kernel
+
+Finally, you can also run them per group as defined in `default.mspec`.
+The following command will run all language specs:
+
+ $ ../mspec/bin/mspec :language
+
+In similar fashion, the following commands run the respective specs:
+
+ $ ../mspec/bin/mspec :core
+ $ ../mspec/bin/mspec :library
+ $ ../mspec/bin/mspec :capi
+
+### Contributing and Writing Specs
+
+See [CONTRIBUTING.md](https://github.com/ruby/spec/blob/master/CONTRIBUTING.md) for documentation about contributing and writing specs (guards, matchers, etc).
+
+### Socket specs from rubysl-socket
+
+Most specs under `library/socket` were imported from [the rubysl-socket project](https://github.com/rubysl/rubysl-socket).
+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.
+
+### History and RubySpec
+
+This project was originally born from [Rubinius](https://github.com/rubinius/rubinius) tests being converted to the spec style.
+These specs were later extracted to their own project, RubySpec, with a specific vision and principles.
+At the end of 2014, Brian Shirai, the creator of RubySpec, decided to [end RubySpec](http://rubinius.com/2014/12/31/matz-s-ruby-developers-don-t-use-rubyspec/).
+A couple months later, the different repositories were merged and [the project was revived](http://eregon.github.io/rubyspec/2015/07/29/rubyspec-is-reborn.html).
+On 12 January 2016, the name was changed to "The Ruby Spec Suite" for clarity and to let the RubySpec ideology rest in peace.
diff --git a/spec/ruby/TODO b/spec/ruby/TODO
new file mode 100644
index 0000000000..f81f070d71
--- /dev/null
+++ b/spec/ruby/TODO
@@ -0,0 +1,8 @@
+* Decide a way to test methods that are only visible given a specific
+ command-line option. For example, Kernel#gsub with -n/-p on 1.9.
+* Look at automating discovery of guarded bugs which have been fixed.
+* Use mocks for all Math functions that coerce with #to_f; currently a fixture
+ is used.
+* investigate slow specs (run with -fp) and make them faster.
+* restore some caller specs from 642bf529
+* restore refinements specs and update. See 56c5528f and f20a62e8.
diff --git a/spec/ruby/appveyor.yml b/spec/ruby/appveyor.yml
new file mode 100644
index 0000000000..588172ee3d
--- /dev/null
+++ b/spec/ruby/appveyor.yml
@@ -0,0 +1,34 @@
+---
+version: "{build}"
+clone_depth: 100
+init:
+ # To avoid duplicated executables in PATH, see https://github.com/ruby/spec/pull/468
+ - set PATH=C:\Ruby%ruby_version%\bin;C:\Program Files\7-Zip;C:\Program Files\AppVeyor\BuildAgent;C:\Program Files\Git\cmd;C:\Windows\system32;C:\Program Files;C:\Windows
+ # Loads trunk build and updates MSYS2 / MinGW to most recent gcc compiler
+ - ps: |
+ if ($env:ruby_version -eq '_trunk') {
+ $trunk_uri = 'https://ci.appveyor.com/api/projects/MSP-Greg/ruby-loco/artifacts/ruby_trunk.7z'
+ (New-Object Net.WebClient).DownloadFile($trunk_uri, 'C:\ruby_trunk.7z')
+ 7z.exe x C:\ruby_trunk.7z -oC:\Ruby_trunk
+ }
+
+environment:
+ matrix:
+ - ruby_version: 24-x64
+ - ruby_version: 25-x64
+ - ruby_version: _trunk # So the folder name is ruby_trunk
+install:
+ - git clone https://github.com/ruby/mspec.git ../mspec
+build: off
+test_script:
+ - SET CHECK_LEAKS=true
+ - ../mspec/bin/mspec -rdevkit -ff
+on_finish:
+ - ruby -v
+matrix:
+ allow_failures:
+ - ruby_version: _trunk
+branches:
+ only:
+ - master
+ - /^try/
diff --git a/spec/ruby/command_line/dash_a_spec.rb b/spec/ruby/command_line/dash_a_spec.rb
new file mode 100644
index 0000000000..9ea135dc76
--- /dev/null
+++ b/spec/ruby/command_line/dash_a_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../spec_helper'
+
+describe "The -a command line option" do
+ before :each do
+ @names = fixture __FILE__, "full_names.txt"
+ end
+
+ it "runs the code in loop conditional on Kernel.gets()" do
+ ruby_exe("puts $F.last", options: "-n -a", escape: true,
+ args: " < #{@names}").should ==
+ "jones\nfield\ngrey\n"
+ end
+
+ it "sets $-a" do
+ ruby_exe("puts $-a", options: "-n -a", escape: true,
+ args: " < #{@names}").should ==
+ "true\ntrue\ntrue\n"
+ end
+end
diff --git a/spec/ruby/command_line/dash_c_spec.rb b/spec/ruby/command_line/dash_c_spec.rb
new file mode 100644
index 0000000000..6b3a5de685
--- /dev/null
+++ b/spec/ruby/command_line/dash_c_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../spec_helper'
+
+describe "The -c command line option" do
+ it "checks syntax in given file" do
+ ruby_exe(nil, args: "-c #{__FILE__}").chomp.should == "Syntax OK"
+ end
+
+ it "checks syntax in -e strings" do
+ ruby_exe(nil, args: "-c -e 'puts 1' -e 'hello world'").chomp.should == "Syntax OK"
+ end
+
+ #Also needs spec for reading from STDIN
+end
diff --git a/spec/ruby/command_line/dash_d_spec.rb b/spec/ruby/command_line/dash_d_spec.rb
new file mode 100644
index 0000000000..26891b4791
--- /dev/null
+++ b/spec/ruby/command_line/dash_d_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../spec_helper'
+
+describe "The -d command line option" do
+ before :each do
+ @script = fixture __FILE__, "debug.rb"
+ end
+
+ it "sets $DEBUG to true" do
+ ruby_exe(@script, options: "-d",
+ args: "0 2> #{File::NULL}").chomp.should == "$DEBUG true"
+ end
+
+ it "sets $VERBOSE to true" do
+ ruby_exe(@script, options: "-d",
+ args: "1 2> #{File::NULL}").chomp.should == "$VERBOSE true"
+ end
+
+ it "sets $-d to true" do
+ ruby_exe(@script, options: "-d",
+ args: "2 2> #{File::NULL}").chomp.should == "$-d true"
+ end
+end
diff --git a/spec/ruby/command_line/dash_e_spec.rb b/spec/ruby/command_line/dash_e_spec.rb
new file mode 100644
index 0000000000..70ecdd5dce
--- /dev/null
+++ b/spec/ruby/command_line/dash_e_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../spec_helper'
+
+describe "The -e command line option" do
+ it "evaluates the given string" do
+ ruby_exe("puts 'foo'").chomp.should == "foo"
+ end
+
+ it "joins multiple strings with newlines" do
+ ruby_exe(nil, args: %Q{-e "puts 'hello" -e "world'" 2>&1}).chomp.should == "hello\nworld"
+ end
+
+ it "uses 'main' as self" do
+ ruby_exe("puts self", escape: false).chomp.should == "main"
+ end
+
+ it "uses '-e' as file" do
+ ruby_exe("puts __FILE__", escape: false).chomp.should == "-e"
+ end
+
+ it "uses '-e' in $0" do
+ system(*ruby_exe, '-e', 'exit $0 == "-e"').should == true
+ end
+
+ #needs to test return => LocalJumpError
+
+ describe "with -n and a Fixnum range" do
+ before :each do
+ @script = "-W0 -ne 'print if %s' #{fixture(__FILE__, "conditional_range.txt")}"
+ end
+
+ it "mimics an awk conditional by comparing an inclusive-end range with $." do
+ ruby_exe(nil, args: (@script % "2..3")).should == "2\n3\n"
+ ruby_exe(nil, args: (@script % "2..2")).should == "2\n"
+ end
+
+ it "mimics a sed conditional by comparing an exclusive-end range with $." do
+ ruby_exe(nil, args: (@script % "2...3")).should == "2\n3\n"
+ ruby_exe(nil, args: (@script % "2...2")).should == "2\n3\n4\n5\n"
+ end
+ end
+end
diff --git a/spec/ruby/command_line/dash_encoding_spec.rb b/spec/ruby/command_line/dash_encoding_spec.rb
new file mode 100644
index 0000000000..36ce55af5f
--- /dev/null
+++ b/spec/ruby/command_line/dash_encoding_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../spec_helper'
+
+describe "The --encoding command line option" do
+ before :each do
+ @test_string = "print [Encoding.default_external.name, Encoding.default_internal&.name].inspect"
+ @enc2 = Encoding::ISO_8859_1
+ end
+
+ describe "sets Encoding.default_external and optionally Encoding.default_internal" do
+ it "if given a single encoding with an =" do
+ ruby_exe(@test_string, options: "--disable-gems --encoding=big5").should == [Encoding::Big5.name, nil].inspect
+ end
+
+ it "if given a single encoding as a separate argument" do
+ ruby_exe(@test_string, options: "--disable-gems --encoding big5").should == [Encoding::Big5.name, nil].inspect
+ end
+
+ it "if given two encodings with an =" do
+ ruby_exe(@test_string, options: "--disable-gems --encoding=big5:#{@enc2}").should == [Encoding::Big5.name, @enc2.name].inspect
+ end
+
+ it "if given two encodings as a separate argument" do
+ ruby_exe(@test_string, options: "--disable-gems --encoding big5:#{@enc2}").should == [Encoding::Big5.name, @enc2.name].inspect
+ end
+ end
+
+ it "does not accept a third encoding" do
+ ruby_exe(@test_string, options: "--disable-gems --encoding big5:#{@enc2}:utf-32le", args: "2>&1").should =~ /extra argument for --encoding: utf-32le/
+ end
+end
diff --git a/spec/ruby/command_line/dash_external_encoding_spec.rb b/spec/ruby/command_line/dash_external_encoding_spec.rb
new file mode 100644
index 0000000000..f052674dc8
--- /dev/null
+++ b/spec/ruby/command_line/dash_external_encoding_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../spec_helper'
+
+describe 'The --external-encoding command line option sets Encoding.default_external' do
+ before :each do
+ @test_string = "print Encoding.default_external.name"
+ end
+
+ it "if given an encoding with an =" do
+ ruby_exe(@test_string, options: '--external-encoding=big5').should == Encoding::Big5.name
+ end
+
+ it "if given an encoding as a separate argument" do
+ ruby_exe(@test_string, options: '--external-encoding big5').should == Encoding::Big5.name
+ end
+end
diff --git a/spec/ruby/command_line/dash_internal_encoding_spec.rb b/spec/ruby/command_line/dash_internal_encoding_spec.rb
new file mode 100644
index 0000000000..3049040bb4
--- /dev/null
+++ b/spec/ruby/command_line/dash_internal_encoding_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../spec_helper'
+
+describe 'The --internal-encoding command line option sets Encoding.default_internal' do
+ before :each do
+ @test_string = "print Encoding.default_internal.name"
+ end
+
+ it "if given an encoding with an =" do
+ ruby_exe(@test_string, options: '--internal-encoding=big5').should == Encoding::Big5.name
+ end
+
+ it "if given an encoding as a separate argument" do
+ ruby_exe(@test_string, options: '--internal-encoding big5').should == Encoding::Big5.name
+ end
+end
diff --git a/spec/ruby/command_line/dash_n_spec.rb b/spec/ruby/command_line/dash_n_spec.rb
new file mode 100644
index 0000000000..9d331d6065
--- /dev/null
+++ b/spec/ruby/command_line/dash_n_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../spec_helper'
+
+describe "The -n command line option" do
+ before :each do
+ @names = fixture __FILE__, "names.txt"
+ end
+
+ it "runs the code in loop conditional on Kernel.gets()" do
+ ruby_exe("puts $_", options: "-n", escape: true,
+ args: " < #{@names}").should ==
+ "alice\nbob\njames\n"
+ end
+
+ it "only evaluates BEGIN blocks once" do
+ ruby_exe("BEGIN { puts \"hi\" }; puts $_", options: "-n", escape: true,
+ args: " < #{@names}").should ==
+ "hi\nalice\nbob\njames\n"
+ end
+
+ it "only evaluates END blocks once" do
+ ruby_exe("puts $_; END {puts \"bye\"}", options: "-n", escape: true,
+ args: " < #{@names}").should ==
+ "alice\nbob\njames\nbye\n"
+ end
+
+ it "allows summing over a whole file" do
+ script = <<-script
+ BEGIN { $total = 0 }
+ $total += 1
+ END { puts $total }
+ script
+ ruby_exe(script, options: "-n", escape: true,
+ args: " < #{@names}").should ==
+ "3\n"
+ end
+end
diff --git a/spec/ruby/command_line/dash_p_spec.rb b/spec/ruby/command_line/dash_p_spec.rb
new file mode 100644
index 0000000000..39827c3868
--- /dev/null
+++ b/spec/ruby/command_line/dash_p_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../spec_helper'
+
+describe "The -p command line option" do
+ before :each do
+ @names = fixture __FILE__, "names.txt"
+ end
+
+ it "runs the code in loop conditional on Kernel.gets() and prints $_" do
+ ruby_exe("$_ = $_.upcase", options: "-p", escape: true,
+ args: " < #{@names}").should ==
+ "ALICE\nBOB\nJAMES\n"
+ end
+
+ it "sets $-p" do
+ ruby_exe("$_ = $-p", options: "-p", escape: true,
+ args: " < #{@names}").should ==
+ "truetruetrue"
+ end
+end
diff --git a/spec/ruby/command_line/dash_r_spec.rb b/spec/ruby/command_line/dash_r_spec.rb
new file mode 100644
index 0000000000..b29895bd26
--- /dev/null
+++ b/spec/ruby/command_line/dash_r_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../spec_helper'
+
+describe "The -r command line option" do
+ before :each do
+ @script = fixture __FILE__, "require.rb"
+ @test_file = fixture __FILE__, "test_file"
+ end
+
+ it "requires the specified file" do
+ result = ruby_exe(@script, options: "-r #{@test_file}")
+ result.should include(@test_file + ".rb")
+ end
+end
diff --git a/spec/ruby/command_line/dash_s_spec.rb b/spec/ruby/command_line/dash_s_spec.rb
new file mode 100644
index 0000000000..eaaeea7c96
--- /dev/null
+++ b/spec/ruby/command_line/dash_s_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../spec_helper'
+
+describe "The -s command line option" do
+ describe "when using -- to stop parsing" do
+ it "sets the value to true without an explicit value" do
+ ruby_exe(nil, options: "-s -e 'p $n'",
+ args: "-- -n").chomp.should == "true"
+ end
+
+ it "parses single letter args into globals" do
+ ruby_exe(nil, options: "-s -e 'puts $n'",
+ args: "-- -n=blah").chomp.should == "blah"
+ end
+
+ it "parses long args into globals" do
+ ruby_exe(nil, options: "-s -e 'puts $_name'",
+ args: "-- --name=blah").chomp.should == "blah"
+ end
+
+ it "converts extra dashes into underscores" do
+ ruby_exe(nil, options: "-s -e 'puts $___name__test__'",
+ args: "-- ----name--test--=blah").chomp.should == "blah"
+ end
+ end
+
+ describe "when running a script" do
+ before :all do
+ @script = fixture __FILE__, "dash_s_script.rb"
+ end
+
+ it "sets the value to true without an explicit value" do
+ ruby_exe(@script, options: "-s",
+ args: "-n 0").chomp.should == "true"
+ end
+
+ it "parses single letter args into globals" do
+ ruby_exe(@script, options: "-s",
+ args: "-n=blah 1").chomp.should == "blah"
+ end
+
+ it "parses long args into globals" do
+ ruby_exe(@script, options: "-s",
+ args: "--name=blah 2").chomp.should == "blah"
+ end
+
+ it "converts extra dashes into underscores" do
+ ruby_exe(@script, options: "-s",
+ args: "----name--test--=blah 3").chomp.should == "blah"
+ end
+
+ end
+end
diff --git a/spec/ruby/command_line/dash_upper_c_spec.rb b/spec/ruby/command_line/dash_upper_c_spec.rb
new file mode 100644
index 0000000000..761beaadab
--- /dev/null
+++ b/spec/ruby/command_line/dash_upper_c_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../spec_helper'
+
+describe 'The -C command line option' do
+ before :all do
+ @script = fixture(__FILE__, 'dash_upper_c_script.rb')
+ @tempdir = File.dirname(@script)
+ end
+
+ it 'changes the PWD when using a file' do
+ output = ruby_exe(@script, options: "-C #{@tempdir}")
+ output.should == @tempdir
+ end
+
+ it 'does not need a space after -C for the argument' do
+ output = ruby_exe(@script, options: "-C#{@tempdir}")
+ output.should == @tempdir
+ end
+
+ it 'changes the PWD when using -e' do
+ output = ruby_exe(nil, options: "-C #{@tempdir} -e 'print Dir.pwd'")
+ output.should == @tempdir
+ end
+end
diff --git a/spec/ruby/command_line/dash_upper_e_spec.rb b/spec/ruby/command_line/dash_upper_e_spec.rb
new file mode 100644
index 0000000000..b3c6ce262b
--- /dev/null
+++ b/spec/ruby/command_line/dash_upper_e_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../spec_helper'
+
+describe "ruby -E" do
+ it "sets the external encoding with '-E external'" do
+ result = ruby_exe("print Encoding.default_external", options: '-E euc-jp')
+ result.should == "EUC-JP"
+ end
+
+ platform_is_not :windows do
+ it "also sets the filesystem encoding with '-E external'" do
+ result = ruby_exe("print Encoding.find('filesystem')", options: '-E euc-jp')
+ result.should == "EUC-JP"
+ end
+ end
+
+ it "sets the external encoding with '-E external:'" do
+ result = ruby_exe("print Encoding.default_external", options: '-E Shift_JIS:')
+ result.should == "Shift_JIS"
+ end
+
+ it "sets the internal encoding with '-E :internal'" do
+ ruby_exe("print Encoding.default_internal", options: '-E :SHIFT_JIS').
+ should == 'Shift_JIS'
+ end
+
+ it "sets the external and internal encodings with '-E external:internal'" do
+ ruby_exe("puts Encoding.default_external, Encoding.default_internal", options: '-E euc-jp:SHIFT_JIS').
+ should == "EUC-JP\nShift_JIS\n"
+ end
+
+ it "raises a RuntimeError if used with -U" do
+ ruby_exe("p 1",
+ options: '-Eascii:ascii -U',
+ args: '2>&1').should =~ /RuntimeError/
+ end
+end
diff --git a/spec/ruby/command_line/dash_upper_f_spec.rb b/spec/ruby/command_line/dash_upper_f_spec.rb
new file mode 100644
index 0000000000..967acc2ece
--- /dev/null
+++ b/spec/ruby/command_line/dash_upper_f_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../spec_helper'
+
+describe "the -F command line option" do
+ before :each do
+ @passwd = fixture __FILE__, "passwd_file.txt"
+ end
+
+ it "specifies the field separator pattern for -a" do
+ ruby_exe("puts $F[0]", options: "-naF:", escape: true,
+ args: " < #{@passwd}").should ==
+ "nobody\nroot\ndaemon\n"
+ end
+end
diff --git a/spec/ruby/command_line/dash_upper_i_spec.rb b/spec/ruby/command_line/dash_upper_i_spec.rb
new file mode 100644
index 0000000000..4cafb724e3
--- /dev/null
+++ b/spec/ruby/command_line/dash_upper_i_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../spec_helper'
+
+describe "The -I command line option" do
+ before :each do
+ @script = fixture __FILE__, "loadpath.rb"
+ end
+
+ it "adds the path to the load path ($:)" do
+ ruby_exe(@script, options: "-I fixtures").should include("fixtures")
+ end
+
+ it "adds the path at the front of $LOAD_PATH" do
+ lines = ruby_exe(@script, options: "-I fixtures").lines
+ if PlatformGuard.implementation? :ruby
+ # In a MRI checkout, $PWD ends up as the first entry in $LOAD_PATH.
+ # So just assert that it's at the beginning.
+ idx = lines.index { |l| l.include?("fixtures") }
+ idx.should < 2
+ idx.should < lines.size-1
+ else
+ lines[0].should include("fixtures")
+ end
+ end
+
+ it "adds the path expanded from CWD to $LOAD_PATH" do
+ ruby_exe(@script, options: "-I fixtures").lines.should include "#{Dir.pwd}/fixtures\n"
+ end
+
+ it "expands a path from CWD even if it does not exist" do
+ ruby_exe(@script, options: "-I not_exist/not_exist").lines.should include "#{Dir.pwd}/not_exist/not_exist\n"
+ end
+end
+
+platform_is_not :windows do
+ describe "The -I command line option" do
+ before :each do
+ @script = fixture __FILE__, "loadpath.rb"
+ @fixtures = File.dirname(@script)
+ @symlink = tmp("loadpath_symlink")
+ File.symlink(@fixtures, @symlink)
+ end
+
+ after :each do
+ rm_r @symlink
+ end
+
+ it "does not expand symlinks" do
+ ruby_exe(@script, options: "-I #{@symlink}").lines.should include "#{@symlink}\n"
+ end
+ end
+end
diff --git a/spec/ruby/command_line/dash_upper_k_spec.rb b/spec/ruby/command_line/dash_upper_k_spec.rb
new file mode 100644
index 0000000000..ef1248b19c
--- /dev/null
+++ b/spec/ruby/command_line/dash_upper_k_spec.rb
@@ -0,0 +1,65 @@
+require_relative '../spec_helper'
+
+describe 'The -K command line option' do
+ before :each do
+ @test_string = "print [__ENCODING__&.name, Encoding.default_external&.name, Encoding.default_internal&.name].inspect"
+ end
+
+ describe 'sets __ENCODING__ and Encoding.default_external' do
+ it "to Encoding::ASCII_8BIT with -Ka" do
+ ruby_exe(@test_string, options: '-Ka').should ==
+ [Encoding::ASCII_8BIT.name, Encoding::ASCII_8BIT.name, nil].inspect
+ end
+
+ it "to Encoding::ASCII_8BIT with -KA" do
+ ruby_exe(@test_string, options: '-KA').should ==
+ [Encoding::ASCII_8BIT.name, Encoding::ASCII_8BIT.name, nil].inspect
+ end
+
+ it "to Encoding::ASCII_8BIT with -Kn" do
+ ruby_exe(@test_string, options: '-Kn').should ==
+ [Encoding::ASCII_8BIT.name, Encoding::ASCII_8BIT.name, nil].inspect
+ end
+
+ it "to Encoding::ASCII_8BIT with -KN" do
+ ruby_exe(@test_string, options: '-KN').should ==
+ [Encoding::ASCII_8BIT.name, Encoding::ASCII_8BIT.name, nil].inspect
+ end
+
+ it "to Encoding::EUC_JP with -Ke" do
+ ruby_exe(@test_string, options: '-Ke').should ==
+ [Encoding::EUC_JP.name, Encoding::EUC_JP.name, nil].inspect
+ end
+
+ it "to Encoding::EUC_JP with -KE" do
+ ruby_exe(@test_string, options: '-KE').should ==
+ [Encoding::EUC_JP.name, Encoding::EUC_JP.name, nil].inspect
+ end
+
+ it "to Encoding::UTF_8 with -Ku" do
+ ruby_exe(@test_string, options: '-Ku').should ==
+ [Encoding::UTF_8.name, Encoding::UTF_8.name, nil].inspect
+ end
+
+ it "to Encoding::UTF_8 with -KU" do
+ ruby_exe(@test_string, options: '-KU').should ==
+ [Encoding::UTF_8.name, Encoding::UTF_8.name, nil].inspect
+ end
+
+ it "to Encoding::Windows_31J with -Ks" do
+ ruby_exe(@test_string, options: '-Ks').should ==
+ [Encoding::Windows_31J.name, Encoding::Windows_31J.name, nil].inspect
+ end
+
+ it "to Encoding::Windows_31J with -KS" do
+ ruby_exe(@test_string, options: '-KS').should ==
+ [Encoding::Windows_31J.name, Encoding::Windows_31J.name, nil].inspect
+ end
+ end
+
+ it "ignores unknown codes" do
+ locale = Encoding.find('locale')
+ ruby_exe(@test_string, options: '-KZ').should ==
+ [Encoding::UTF_8.name, locale.name, nil].inspect
+ end
+end
diff --git a/spec/ruby/command_line/dash_upper_s_spec.rb b/spec/ruby/command_line/dash_upper_s_spec.rb
new file mode 100644
index 0000000000..3a28fa2ad2
--- /dev/null
+++ b/spec/ruby/command_line/dash_upper_s_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../spec_helper'
+
+describe 'The -S command line option' do
+ before :each do
+ @path = [ENV['PATH'], fixture(__FILE__, "bin")].join(':')
+ end
+
+ platform_is_not :windows do
+ # On VirtualBox shared directory (vboxsf) all files are world writable
+ # and MRI shows warning when including world writable path in ENV['PATH'].
+ # This is why we are using /success$/ matching in the following cases.
+
+ it "runs launcher found in PATH, but only code after the first /\#!.*ruby.*/-ish line in target file" do
+ result = ruby_exe(nil, options: '-S hybrid_launcher.sh', env: { 'PATH' => @path }, args: '2>&1')
+ result.should =~ /success$/
+ end
+
+ it "runs launcher found in PATH" do
+ result = ruby_exe(nil, options: '-S launcher.rb', env: { 'PATH' => @path }, args: '2>&1')
+ result.should =~ /success$/
+ end
+
+ it "runs launcher found in PATH and sets the exit status to 1 if it fails" do
+ result = ruby_exe(nil, options: '-S dash_s_fail', env: { 'PATH' => @path }, args: '2>&1')
+ result.should =~ /\bdie\b/
+ $?.exitstatus.should == 1
+ end
+ end
+end
diff --git a/spec/ruby/command_line/dash_upper_u_spec.rb b/spec/ruby/command_line/dash_upper_u_spec.rb
new file mode 100644
index 0000000000..2546b5b9f4
--- /dev/null
+++ b/spec/ruby/command_line/dash_upper_u_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+
+describe "ruby -U" do
+ it "sets Encoding.default_internal to UTF-8" do
+ ruby_exe('print Encoding.default_internal.name',
+ options: '-U').should == 'UTF-8'
+ end
+
+ it "does nothing different if specified multiple times" do
+ ruby_exe('print Encoding.default_internal.name',
+ options: '-U -U').should == 'UTF-8'
+ end
+
+ it "is overruled by Encoding.default_internal=" do
+ ruby_exe('Encoding.default_internal="ascii"; print Encoding.default_internal.name',
+ options: '-U').should == 'US-ASCII'
+ end
+
+ it "does not affect the default external encoding" do
+ ruby_exe('Encoding.default_external="ascii"; print Encoding.default_external.name',
+ options: '-U').should == 'US-ASCII'
+ end
+
+ it "does not affect the source encoding" do
+ ruby_exe("print __ENCODING__.name",
+ options: '-U -KE').should == 'EUC-JP'
+ ruby_exe("print __ENCODING__.name",
+ options: '-KE -U').should == 'EUC-JP'
+ end
+
+ # I assume IO redirection will break on Windows...
+ it "raises a RuntimeError if used with -Eext:int" do
+ ruby_exe("p 1",
+ options: '-U -Eascii:ascii',
+ args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError if used with -E:int" do
+ ruby_exe("p 1",
+ options: '-U -E:ascii',
+ args: '2>&1').should =~ /RuntimeError/
+ end
+end
diff --git a/spec/ruby/command_line/dash_upper_w_spec.rb b/spec/ruby/command_line/dash_upper_w_spec.rb
new file mode 100644
index 0000000000..31bb976ad2
--- /dev/null
+++ b/spec/ruby/command_line/dash_upper_w_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../spec_helper'
+require_relative 'shared/verbose'
+
+describe "The -W command line option" do
+ before :each do
+ @script = fixture __FILE__, "verbose.rb"
+ end
+
+ it "with 0 sets $VERBOSE to nil" do
+ ruby_exe(@script, options: "-W0").chomp.should == "nil"
+ end
+
+ it "with 1 sets $VERBOSE to false" do
+ ruby_exe(@script, options: "-W1").chomp.should == "false"
+ end
+end
+
+describe "The -W command line option with 2" do
+ it_behaves_like :command_line_verbose, "-W2"
+end
diff --git a/spec/ruby/command_line/dash_v_spec.rb b/spec/ruby/command_line/dash_v_spec.rb
new file mode 100644
index 0000000000..04d684fdad
--- /dev/null
+++ b/spec/ruby/command_line/dash_v_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../spec_helper'
+require_relative 'shared/verbose'
+
+describe "The -v command line option" do
+ it_behaves_like :command_line_verbose, "-v"
+
+ describe "when used alone" do
+ it "prints version and ends" do
+ ruby_exe(nil, args: '-v').should include(RUBY_DESCRIPTION)
+ end
+ end
+end
diff --git a/spec/ruby/command_line/dash_w_spec.rb b/spec/ruby/command_line/dash_w_spec.rb
new file mode 100644
index 0000000000..1d93e0347b
--- /dev/null
+++ b/spec/ruby/command_line/dash_w_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../spec_helper'
+require_relative 'shared/verbose'
+
+describe "The -w command line option" do
+ it_behaves_like :command_line_verbose, "-w"
+end
diff --git a/spec/ruby/command_line/dash_x_spec.rb b/spec/ruby/command_line/dash_x_spec.rb
new file mode 100644
index 0000000000..ad6be23063
--- /dev/null
+++ b/spec/ruby/command_line/dash_x_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../spec_helper'
+
+describe "The -x command line option" do
+ it "runs code after the first /\#!.*ruby.*/-ish line in target file" do
+ embedded_ruby = fixture __FILE__, "bin/embedded_ruby.txt"
+ result = ruby_exe(embedded_ruby, options: '-x')
+ result.should == "success\n"
+ end
+
+ it "fails when /\#!.*ruby.*/-ish line in target file is not found" do
+ bad_embedded_ruby = fixture __FILE__, "bin/bad_embedded_ruby.txt"
+ result = ruby_exe(bad_embedded_ruby, options: '-x', args: '2>&1')
+ result.should include "no Ruby script found in input"
+ end
+
+ it "behaves as -x was set when non-ruby shebang is encountered on first line" do
+ embedded_ruby = fixture __FILE__, "bin/hybrid_launcher.sh"
+ result = ruby_exe(embedded_ruby)
+ result.should == "success\n"
+ end
+
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/command_line/error_message_spec.rb b/spec/ruby/command_line/error_message_spec.rb
new file mode 100644
index 0000000000..5fee3ead44
--- /dev/null
+++ b/spec/ruby/command_line/error_message_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../spec_helper'
+
+describe "The error message caused by an exception" do
+ it "is not printed to stdout" do
+ out = ruby_exe("this_does_not_exist", args: "2> #{File::NULL}")
+ out.chomp.empty?.should == true
+
+ out = ruby_exe("end #syntax error", args: "2> #{File::NULL}")
+ out.chomp.empty?.should == true
+ end
+end
diff --git a/spec/ruby/command_line/fixtures/bad_syntax.rb b/spec/ruby/command_line/fixtures/bad_syntax.rb
new file mode 100644
index 0000000000..e7b8c7a357
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/bad_syntax.rb
@@ -0,0 +1 @@
+f {
diff --git a/spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt b/spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt
new file mode 100644
index 0000000000..a2b7ad085f
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt
@@ -0,0 +1,3 @@
+@@@This line is not value Ruby
+#!rub_y
+puts 'success'
diff --git a/spec/ruby/command_line/fixtures/bin/dash_s_fail b/spec/ruby/command_line/fixtures/bin/dash_s_fail
new file mode 100644
index 0000000000..70c1b8759c
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/bin/dash_s_fail
@@ -0,0 +1 @@
+raise 'die'
diff --git a/spec/ruby/command_line/fixtures/bin/embedded_ruby.txt b/spec/ruby/command_line/fixtures/bin/embedded_ruby.txt
new file mode 100644
index 0000000000..c556bf0b71
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/bin/embedded_ruby.txt
@@ -0,0 +1,3 @@
+@@@This line is not value Ruby
+#!ruby
+puts 'success' \ No newline at end of file
diff --git a/spec/ruby/command_line/fixtures/bin/hybrid_launcher.sh b/spec/ruby/command_line/fixtures/bin/hybrid_launcher.sh
new file mode 100644
index 0000000000..0eede2a99f
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/bin/hybrid_launcher.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+exec somehow this file
+#!ruby
+puts 'success'
diff --git a/spec/ruby/command_line/fixtures/bin/launcher.rb b/spec/ruby/command_line/fixtures/bin/launcher.rb
new file mode 100755
index 0000000000..92a0ee2b49
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/bin/launcher.rb
@@ -0,0 +1,2 @@
+#!ruby
+puts 'success'
diff --git a/spec/ruby/command_line/fixtures/conditional_range.txt b/spec/ruby/command_line/fixtures/conditional_range.txt
new file mode 100644
index 0000000000..8a1218a102
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/conditional_range.txt
@@ -0,0 +1,5 @@
+1
+2
+3
+4
+5
diff --git a/spec/ruby/command_line/fixtures/dash_s_script.rb b/spec/ruby/command_line/fixtures/dash_s_script.rb
new file mode 100644
index 0000000000..500eccbb84
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/dash_s_script.rb
@@ -0,0 +1,12 @@
+which = ARGV.shift.to_i
+
+case which
+when 0
+ p $n
+when 1
+ puts $n
+when 2
+ puts $_name
+when 3
+ puts $___name__test__
+end
diff --git a/spec/ruby/command_line/fixtures/dash_upper_c_script.rb b/spec/ruby/command_line/fixtures/dash_upper_c_script.rb
new file mode 100644
index 0000000000..abe244705f
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/dash_upper_c_script.rb
@@ -0,0 +1 @@
+print Dir.pwd
diff --git a/spec/ruby/command_line/fixtures/debug.rb b/spec/ruby/command_line/fixtures/debug.rb
new file mode 100644
index 0000000000..2d84c5faf6
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/debug.rb
@@ -0,0 +1,10 @@
+which = ARGV.first.to_i
+
+case which
+when 0
+ puts "$DEBUG #{$DEBUG}"
+when 1
+ puts "$VERBOSE #{$VERBOSE}"
+when 2
+ puts "$-d #{$-d}"
+end
diff --git a/spec/ruby/command_line/fixtures/debug_info.rb b/spec/ruby/command_line/fixtures/debug_info.rb
new file mode 100644
index 0000000000..ee607910c0
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/debug_info.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+a = 'string'
+b = a
+c = b
+d = c
+e = d
+begin
+ a << 'new part'
+rescue Exception => e
+ print e.message
+end
diff --git a/spec/ruby/command_line/fixtures/freeze_flag_across_files.rb b/spec/ruby/command_line/fixtures/freeze_flag_across_files.rb
new file mode 100644
index 0000000000..b258249f3a
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/freeze_flag_across_files.rb
@@ -0,0 +1,3 @@
+require_relative 'freeze_flag_required'
+
+p "abc".object_id == $second_literal_id
diff --git a/spec/ruby/command_line/fixtures/freeze_flag_across_files_diff_enc.rb b/spec/ruby/command_line/fixtures/freeze_flag_across_files_diff_enc.rb
new file mode 100644
index 0000000000..e9f045e9ea
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/freeze_flag_across_files_diff_enc.rb
@@ -0,0 +1,3 @@
+require_relative 'freeze_flag_required_diff_enc'
+
+p "abc".object_id != $second_literal_id
diff --git a/spec/ruby/command_line/fixtures/freeze_flag_one_literal.rb b/spec/ruby/command_line/fixtures/freeze_flag_one_literal.rb
new file mode 100644
index 0000000000..3718899d61
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/freeze_flag_one_literal.rb
@@ -0,0 +1,2 @@
+ids = Array.new(2) { "abc".object_id }
+p ids.first == ids.last
diff --git a/spec/ruby/command_line/fixtures/freeze_flag_required.rb b/spec/ruby/command_line/fixtures/freeze_flag_required.rb
new file mode 100644
index 0000000000..e09232a5f4
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/freeze_flag_required.rb
@@ -0,0 +1 @@
+$second_literal_id = "abc".object_id
diff --git a/spec/ruby/command_line/fixtures/freeze_flag_required_diff_enc.rb b/spec/ruby/command_line/fixtures/freeze_flag_required_diff_enc.rb
new file mode 100644
index 0000000000..fa348d59e7
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/freeze_flag_required_diff_enc.rb
Binary files differ
diff --git a/spec/ruby/command_line/fixtures/freeze_flag_two_literals.rb b/spec/ruby/command_line/fixtures/freeze_flag_two_literals.rb
new file mode 100644
index 0000000000..074092c9d9
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/freeze_flag_two_literals.rb
@@ -0,0 +1 @@
+p "abc".object_id == "abc".object_id
diff --git a/spec/ruby/command_line/fixtures/full_names.txt b/spec/ruby/command_line/fixtures/full_names.txt
new file mode 100644
index 0000000000..602a20b9dd
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/full_names.txt
@@ -0,0 +1,3 @@
+alice jones
+bob field
+james grey
diff --git a/spec/ruby/command_line/fixtures/loadpath.rb b/spec/ruby/command_line/fixtures/loadpath.rb
new file mode 100644
index 0000000000..d7fdf45d46
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/loadpath.rb
@@ -0,0 +1 @@
+puts $:
diff --git a/spec/ruby/command_line/fixtures/names.txt b/spec/ruby/command_line/fixtures/names.txt
new file mode 100644
index 0000000000..ae4bf4c8ad
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/names.txt
@@ -0,0 +1,3 @@
+alice
+bob
+james
diff --git a/spec/ruby/command_line/fixtures/passwd_file.txt b/spec/ruby/command_line/fixtures/passwd_file.txt
new file mode 100644
index 0000000000..08a4b23bbd
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/passwd_file.txt
@@ -0,0 +1,3 @@
+nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false
+root:*:0:0:System Administrator:/var/root:/bin/sh
+daemon:*:1:1:System Services:/var/root:/usr/bin/false
diff --git a/spec/ruby/command_line/fixtures/require.rb b/spec/ruby/command_line/fixtures/require.rb
new file mode 100644
index 0000000000..0be7049c66
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/require.rb
@@ -0,0 +1 @@
+puts $"
diff --git a/spec/ruby/command_line/fixtures/rubyopt.rb b/spec/ruby/command_line/fixtures/rubyopt.rb
new file mode 100644
index 0000000000..48d81e1bca
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/rubyopt.rb
@@ -0,0 +1 @@
+puts "rubyopt.rb required"
diff --git a/spec/ruby/command_line/fixtures/test_file.rb b/spec/ruby/command_line/fixtures/test_file.rb
new file mode 100644
index 0000000000..961e3c0b0c
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/test_file.rb
@@ -0,0 +1 @@
+"test file"
diff --git a/spec/ruby/command_line/fixtures/verbose.rb b/spec/ruby/command_line/fixtures/verbose.rb
new file mode 100644
index 0000000000..2aa99ed44d
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/verbose.rb
@@ -0,0 +1 @@
+puts $VERBOSE.inspect
diff --git a/spec/ruby/command_line/frozen_strings_spec.rb b/spec/ruby/command_line/frozen_strings_spec.rb
new file mode 100644
index 0000000000..b2631a4c76
--- /dev/null
+++ b/spec/ruby/command_line/frozen_strings_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../spec_helper'
+
+describe "The --enable-frozen-string-literal flag causes string literals to" do
+
+ it "produce the same object each time" do
+ ruby_exe(fixture(__FILE__, "freeze_flag_one_literal.rb"), options: "--enable-frozen-string-literal").chomp.should == "true"
+ end
+
+ it "produce the same object for literals with the same content" do
+ ruby_exe(fixture(__FILE__, "freeze_flag_two_literals.rb"), options: "--enable-frozen-string-literal").chomp.should == "true"
+ end
+
+ it "produce the same object for literals with the same content in different files" do
+ ruby_exe(fixture(__FILE__, "freeze_flag_across_files.rb"), options: "--enable-frozen-string-literal").chomp.should == "true"
+ end
+
+ it "produce different objects for literals with the same content in different files if they have different encodings" do
+ ruby_exe(fixture(__FILE__, "freeze_flag_across_files_diff_enc.rb"), options: "--enable-frozen-string-literal").chomp.should == "true"
+ end
+end
+
+describe "The --debug flag produces" do
+ it "debugging info on attempted frozen string modification" do
+ error_str = ruby_exe(fixture(__FILE__, 'debug_info.rb'), options: '--debug', args: "2>&1")
+ error_str.should include("can't modify frozen String, created at ")
+ error_str.should include("command_line/fixtures/debug_info.rb:2")
+ end
+end
diff --git a/spec/ruby/command_line/rubylib_spec.rb b/spec/ruby/command_line/rubylib_spec.rb
new file mode 100644
index 0000000000..20e9c2cf95
--- /dev/null
+++ b/spec/ruby/command_line/rubylib_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../spec_helper'
+
+describe "The RUBYLIB environment variable" do
+ before :each do
+ @rubylib, ENV["RUBYLIB"] = ENV["RUBYLIB"], nil
+ @pre = @rubylib.nil? ? '' : @rubylib + File::PATH_SEPARATOR
+ end
+
+ after :each do
+ ENV["RUBYLIB"] = @rubylib
+ end
+
+ it "adds a directory to $LOAD_PATH" do
+ dir = tmp("rubylib/incl")
+ ENV["RUBYLIB"] = @pre + dir
+ paths = ruby_exe("puts $LOAD_PATH").lines.map(&:chomp)
+ paths.should include(dir)
+ end
+
+ it "adds a File::PATH_SEPARATOR-separated list of directories to $LOAD_PATH" do
+ dir1, dir2 = tmp("rubylib/incl1"), tmp("rubylib/incl2")
+ ENV["RUBYLIB"] = @pre + "#{dir1}#{File::PATH_SEPARATOR}#{dir2}"
+ paths = ruby_exe("puts $LOAD_PATH").lines.map(&:chomp)
+ paths.should include(dir1)
+ paths.should include(dir2)
+ paths.index(dir1).should < paths.index(dir2)
+ end
+
+ it "adds the directory at the front of $LOAD_PATH" do
+ dir = tmp("rubylib/incl_front")
+ ENV["RUBYLIB"] = @pre + dir
+ paths = ruby_exe("puts $LOAD_PATH").lines.map(&:chomp)
+ if PlatformGuard.implementation? :ruby
+ # In a MRI checkout, $PWD and some extra -I entries end up as
+ # the first entries in $LOAD_PATH. So just assert that it's not last.
+ idx = paths.index(dir)
+ idx.should < paths.size-1
+ else
+ paths[0].should == dir
+ end
+ end
+
+ it "adds the directory after directories added by -I" do
+ dash_i_dir = tmp("dash_I_include")
+ rubylib_dir = tmp("rubylib_include")
+ ENV["RUBYLIB"] = @pre + rubylib_dir
+ paths = ruby_exe("puts $LOAD_PATH", options: "-I #{dash_i_dir}").lines.map(&:chomp)
+ paths.should include(dash_i_dir)
+ paths.should include(rubylib_dir)
+ paths.index(dash_i_dir).should < paths.index(rubylib_dir)
+ end
+
+ it "adds the directory after directories added by -I within RUBYOPT" do
+ rubyopt_dir = tmp("rubyopt_include")
+ rubylib_dir = tmp("rubylib_include")
+ ENV["RUBYLIB"] = @pre + rubylib_dir
+ paths = ruby_exe("puts $LOAD_PATH", env: { "RUBYOPT" => "-I#{rubyopt_dir}" }).lines.map(&:chomp)
+ paths.should include(rubyopt_dir)
+ paths.should include(rubylib_dir)
+ paths.index(rubyopt_dir).should < paths.index(rubylib_dir)
+ end
+
+ it "keeps spaces in the value" do
+ ENV["RUBYLIB"] = @pre + " rubylib/incl "
+ out = ruby_exe("puts $LOAD_PATH")
+ out.should include(" rubylib/incl ")
+ end
+end
diff --git a/spec/ruby/command_line/rubyopt_spec.rb b/spec/ruby/command_line/rubyopt_spec.rb
new file mode 100644
index 0000000000..2db42f77ef
--- /dev/null
+++ b/spec/ruby/command_line/rubyopt_spec.rb
@@ -0,0 +1,167 @@
+require_relative '../spec_helper'
+
+describe "Processing RUBYOPT" do
+ before :each do
+ @rubyopt, ENV["RUBYOPT"] = ENV["RUBYOPT"], nil
+ end
+
+ after :each do
+ ENV["RUBYOPT"] = @rubyopt
+ end
+
+ it "adds the -I path to $LOAD_PATH" do
+ ENV["RUBYOPT"] = "-Ioptrubyspecincl"
+ result = ruby_exe("puts $LOAD_PATH.grep(/byspecin/)", escape: true)
+ result.chomp[-15..-1].should == "optrubyspecincl"
+ end
+
+ it "sets $DEBUG to true for '-d'" do
+ ENV["RUBYOPT"] = '-d'
+ command = %[puts "value of $DEBUG is \#{$DEBUG}"]
+ result = ruby_exe(command, escape: true, args: "2>&1")
+ result.should =~ /value of \$DEBUG is true/
+ end
+
+ guard -> { not CROSS_COMPILING } do
+ it "prints the version number for '-v'" do
+ ENV["RUBYOPT"] = '-v'
+ ruby_exe("")[/\A.*/].should == RUBY_DESCRIPTION
+ end
+
+ it "ignores whitespace around the option" do
+ ENV["RUBYOPT"] = ' -v '
+ ruby_exe("")[/\A.*/].should == RUBY_DESCRIPTION
+ end
+ end
+
+ it "sets $VERBOSE to true for '-w'" do
+ ENV["RUBYOPT"] = '-w'
+ ruby_exe("p $VERBOSE", escape: true).chomp.should == "true"
+ end
+
+ it "sets $VERBOSE to true for '-W'" do
+ ENV["RUBYOPT"] = '-W'
+ ruby_exe("p $VERBOSE", escape: true).chomp.should == "true"
+ end
+
+ it "sets $VERBOSE to nil for '-W0'" do
+ ENV["RUBYOPT"] = '-W0'
+ ruby_exe("p $VERBOSE", escape: true).chomp.should == "nil"
+ end
+
+ it "sets $VERBOSE to false for '-W1'" do
+ ENV["RUBYOPT"] = '-W1'
+ ruby_exe("p $VERBOSE", escape: true).chomp.should == "false"
+ end
+
+ it "sets $VERBOSE to true for '-W2'" do
+ ENV["RUBYOPT"] = '-W2'
+ ruby_exe("p $VERBOSE", escape: true).chomp.should == "true"
+ end
+
+ it "requires the file for '-r'" do
+ f = fixture __FILE__, "rubyopt"
+ ENV["RUBYOPT"] = "-r#{f}"
+ ruby_exe("0", args: '2>&1').should =~ /^rubyopt.rb required/
+ end
+
+ it "raises a RuntimeError for '-a'" do
+ ENV["RUBYOPT"] = '-a'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '-p'" do
+ ENV["RUBYOPT"] = '-p'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '-n'" do
+ ENV["RUBYOPT"] = '-n'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '-y'" do
+ ENV["RUBYOPT"] = '-y'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '-c'" do
+ ENV["RUBYOPT"] = '-c'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '-s'" do
+ ENV["RUBYOPT"] = '-s'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '-h'" do
+ ENV["RUBYOPT"] = '-h'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '--help'" do
+ ENV["RUBYOPT"] = '--help'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '-l'" do
+ ENV["RUBYOPT"] = '-l'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '-S'" do
+ ENV["RUBYOPT"] = '-S irb'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '-e'" do
+ ENV["RUBYOPT"] = '-e0'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '-i'" do
+ ENV["RUBYOPT"] = '-i.bak'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '-x'" do
+ ENV["RUBYOPT"] = '-x'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '-C'" do
+ ENV["RUBYOPT"] = '-C'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '-X'" do
+ ENV["RUBYOPT"] = '-X.'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '-F'" do
+ ENV["RUBYOPT"] = '-F'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '-0'" do
+ ENV["RUBYOPT"] = '-0'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '--copyright'" do
+ ENV["RUBYOPT"] = '--copyright'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '--version'" do
+ ENV["RUBYOPT"] = '--version'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+
+ it "raises a RuntimeError for '--yydebug'" do
+ ENV["RUBYOPT"] = '--yydebug'
+ ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ end
+end
diff --git a/spec/ruby/command_line/shared/verbose.rb b/spec/ruby/command_line/shared/verbose.rb
new file mode 100644
index 0000000000..457fe3006a
--- /dev/null
+++ b/spec/ruby/command_line/shared/verbose.rb
@@ -0,0 +1,9 @@
+describe :command_line_verbose, shared: true do
+ before :each do
+ @script = fixture __FILE__, "verbose.rb"
+ end
+
+ it "sets $VERBOSE to true" do
+ ruby_exe(@script, options: @method).chomp.split.last.should == "true"
+ end
+end
diff --git a/spec/ruby/command_line/syntax_error_spec.rb b/spec/ruby/command_line/syntax_error_spec.rb
new file mode 100644
index 0000000000..f61cfe928d
--- /dev/null
+++ b/spec/ruby/command_line/syntax_error_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../spec_helper'
+
+describe "The interpreter" do
+ it "prints an error when given a file with invalid syntax" do
+ out = ruby_exe(fixture(__FILE__, "bad_syntax.rb"), args: "2>&1")
+ out.should include "syntax error"
+ end
+
+ it "prints an error when given code via -e with invalid syntax" do
+ out = ruby_exe(nil, args: "-e 'a{' 2>&1")
+ out.should include "syntax error"
+ end
+end
diff --git a/spec/ruby/core/argf/argf_spec.rb b/spec/ruby/core/argf/argf_spec.rb
new file mode 100644
index 0000000000..af67170b98
--- /dev/null
+++ b/spec/ruby/core/argf/argf_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "ARGF" do
+ it "is extended by the Enumerable module" do
+ ARGF.should be_kind_of(Enumerable)
+ end
+
+ it "is an instance of ARGF.class" do
+ ARGF.should be_an_instance_of(ARGF.class)
+ end
+end
diff --git a/spec/ruby/core/argf/argv_spec.rb b/spec/ruby/core/argf/argv_spec.rb
new file mode 100644
index 0000000000..eab03c450f
--- /dev/null
+++ b/spec/ruby/core/argf/argv_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+describe "ARGF.argv" do
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ it "returns ARGV for the initial ARGF" do
+ ARGF.argv.should equal ARGV
+ end
+
+ it "returns the remaining arguments to treat" do
+ argf [@file1, @file2] do
+ # @file1 is stored in current file
+ @argf.argv.should == [@file2]
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/binmode_spec.rb b/spec/ruby/core/argf/binmode_spec.rb
new file mode 100644
index 0000000000..061145a0c7
--- /dev/null
+++ b/spec/ruby/core/argf/binmode_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+
+describe "ARGF.binmode" do
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ @bin_file = fixture __FILE__, "bin_file.txt"
+ end
+
+ it "returns self" do
+ argf [@bin_file] do
+ @argf.binmode.should equal @argf
+ end
+ end
+
+ platform_is :windows do
+ it "puts reading into binmode" do
+ argf [@bin_file, @bin_file] do
+ @argf.gets.should == "test\n"
+ @argf.binmode
+ @argf.gets.should == "test\r\n"
+ end
+ end
+
+ it "puts alls subsequent stream reading through ARGF into binmode" do
+ argf [@bin_file, @bin_file] do
+ @argf.binmode
+ @argf.gets.should == "test\r\n"
+ @argf.gets.should == "test\r\n"
+ end
+ end
+ end
+
+ it "sets the file's encoding to ASCII-8BIT" do
+ argf [@bin_file, @file1] do
+ @argf.binmode
+ @argf.binmode?.should == true
+ @argf.gets.encoding.should == Encoding::ASCII_8BIT
+ @argf.skip
+ @argf.read.encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/bytes_spec.rb b/spec/ruby/core/argf/bytes_spec.rb
new file mode 100644
index 0000000000..71d07fabcb
--- /dev/null
+++ b/spec/ruby/core/argf/bytes_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/each_byte'
+
+describe "ARGF.bytes" do
+ it_behaves_like :argf_each_byte, :bytes
+end
diff --git a/spec/ruby/core/argf/chars_spec.rb b/spec/ruby/core/argf/chars_spec.rb
new file mode 100644
index 0000000000..ee79ea763b
--- /dev/null
+++ b/spec/ruby/core/argf/chars_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/each_char'
+
+describe "ARGF.chars" do
+ it_behaves_like :argf_each_char, :chars
+end
diff --git a/spec/ruby/core/argf/close_spec.rb b/spec/ruby/core/argf/close_spec.rb
new file mode 100644
index 0000000000..56faf30664
--- /dev/null
+++ b/spec/ruby/core/argf/close_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+
+describe "ARGF.close" do
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+ end
+
+ it "closes the current open stream" do
+ argf [@file1_name, @file2_name] do
+ io = @argf.to_io
+ @argf.close
+ io.closed?.should be_true
+ end
+ end
+
+ it "returns self" do
+ argf [@file1_name, @file2_name] do
+ @argf.close.should equal(@argf)
+ end
+ end
+
+ it "doesn't raise an IOError if called on a closed stream" do
+ argf [@file1_name] do
+ lambda { @argf.close }.should_not raise_error
+ lambda { @argf.close }.should_not raise_error
+ end
+ end
+end
+
+describe "ARGF.close" do
+ it "does not close STDIN" do
+ ruby_exe("ARGV.replace(['-']); ARGF.close; print ARGF.closed?").should == "false"
+ end
+end
diff --git a/spec/ruby/core/argf/closed_spec.rb b/spec/ruby/core/argf/closed_spec.rb
new file mode 100644
index 0000000000..e2dd6134e5
--- /dev/null
+++ b/spec/ruby/core/argf/closed_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+
+describe "ARGF.closed?" do
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+ end
+
+ it "returns true if the current stream has been closed" do
+ argf [@file1_name, @file2_name] do
+ stream = @argf.to_io
+ stream.close
+
+ @argf.closed?.should be_true
+ stream.reopen(@argf.filename, 'r')
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/codepoints_spec.rb b/spec/ruby/core/argf/codepoints_spec.rb
new file mode 100644
index 0000000000..7aa8a761fe
--- /dev/null
+++ b/spec/ruby/core/argf/codepoints_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/each_codepoint'
+
+describe "ARGF.codepoints" do
+ it_behaves_like :argf_each_codepoint, :codepoints
+end
diff --git a/spec/ruby/core/argf/each_byte_spec.rb b/spec/ruby/core/argf/each_byte_spec.rb
new file mode 100644
index 0000000000..c5cce9f250
--- /dev/null
+++ b/spec/ruby/core/argf/each_byte_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/each_byte'
+
+describe "ARGF.each_byte" do
+ it_behaves_like :argf_each_byte, :each_byte
+end
diff --git a/spec/ruby/core/argf/each_char_spec.rb b/spec/ruby/core/argf/each_char_spec.rb
new file mode 100644
index 0000000000..724e5e6e3e
--- /dev/null
+++ b/spec/ruby/core/argf/each_char_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/each_char'
+
+describe "ARGF.each_char" do
+ it_behaves_like :argf_each_char, :each_char
+end
diff --git a/spec/ruby/core/argf/each_codepoint_spec.rb b/spec/ruby/core/argf/each_codepoint_spec.rb
new file mode 100644
index 0000000000..0bf8bf9764
--- /dev/null
+++ b/spec/ruby/core/argf/each_codepoint_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/each_codepoint'
+
+describe "ARGF.each_codepoint" do
+ it_behaves_like :argf_each_codepoint, :each_codepoint
+end
diff --git a/spec/ruby/core/argf/each_line_spec.rb b/spec/ruby/core/argf/each_line_spec.rb
new file mode 100644
index 0000000000..52a7e5c411
--- /dev/null
+++ b/spec/ruby/core/argf/each_line_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/each_line'
+
+describe "ARGF.each_line" do
+ it_behaves_like :argf_each_line, :each_line
+end
diff --git a/spec/ruby/core/argf/each_spec.rb b/spec/ruby/core/argf/each_spec.rb
new file mode 100644
index 0000000000..5742ba43bd
--- /dev/null
+++ b/spec/ruby/core/argf/each_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/each_line'
+
+describe "ARGF.each" do
+ it_behaves_like :argf_each_line, :each
+end
diff --git a/spec/ruby/core/argf/eof_spec.rb b/spec/ruby/core/argf/eof_spec.rb
new file mode 100644
index 0000000000..518f6e566e
--- /dev/null
+++ b/spec/ruby/core/argf/eof_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'shared/eof'
+
+describe "ARGF.eof" do
+ it_behaves_like :argf_eof, :eof
+end
+
+describe "ARGF.eof?" do
+ it_behaves_like :argf_eof, :eof?
+end
diff --git a/spec/ruby/core/argf/file_spec.rb b/spec/ruby/core/argf/file_spec.rb
new file mode 100644
index 0000000000..df8552d457
--- /dev/null
+++ b/spec/ruby/core/argf/file_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+
+describe "ARGF.file" do
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ # NOTE: this test assumes that fixtures files have two lines each
+ it "returns the current file object on each file" do
+ argf [@file1, @file2] do
+ result = []
+ # returns first current file even when not yet open
+ result << @argf.file.path
+ result << @argf.file.path while @argf.gets
+ # returns last current file even when closed
+ result << @argf.file.path
+ result.should == [@file1, @file1, @file1, @file2, @file2, @file2]
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/filename_spec.rb b/spec/ruby/core/argf/filename_spec.rb
new file mode 100644
index 0000000000..7c0446269d
--- /dev/null
+++ b/spec/ruby/core/argf/filename_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/filename'
+
+describe "ARGF.filename" do
+ it_behaves_like :argf_filename, :filename
+end
diff --git a/spec/ruby/core/argf/fileno_spec.rb b/spec/ruby/core/argf/fileno_spec.rb
new file mode 100644
index 0000000000..29d50c3582
--- /dev/null
+++ b/spec/ruby/core/argf/fileno_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/fileno'
+
+describe "ARGF.fileno" do
+ it_behaves_like :argf_fileno, :fileno
+end
diff --git a/spec/ruby/core/argf/fixtures/bin_file.txt b/spec/ruby/core/argf/fixtures/bin_file.txt
new file mode 100644
index 0000000000..2545e9037e
--- /dev/null
+++ b/spec/ruby/core/argf/fixtures/bin_file.txt
@@ -0,0 +1,2 @@
+test
+test
diff --git a/spec/ruby/core/argf/fixtures/file1.txt b/spec/ruby/core/argf/fixtures/file1.txt
new file mode 100644
index 0000000000..1c89bfbd82
--- /dev/null
+++ b/spec/ruby/core/argf/fixtures/file1.txt
@@ -0,0 +1,2 @@
+file1.1
+file1.2
diff --git a/spec/ruby/core/argf/fixtures/file2.txt b/spec/ruby/core/argf/fixtures/file2.txt
new file mode 100644
index 0000000000..62e8dba00b
--- /dev/null
+++ b/spec/ruby/core/argf/fixtures/file2.txt
@@ -0,0 +1,2 @@
+line2.1
+line2.2
diff --git a/spec/ruby/core/argf/fixtures/filename.rb b/spec/ruby/core/argf/fixtures/filename.rb
new file mode 100644
index 0000000000..599c97dd57
--- /dev/null
+++ b/spec/ruby/core/argf/fixtures/filename.rb
@@ -0,0 +1,3 @@
+puts $FILENAME while ARGF.gets
+# returns last current file even when closed
+puts $FILENAME
diff --git a/spec/ruby/core/argf/fixtures/lineno.rb b/spec/ruby/core/argf/fixtures/lineno.rb
new file mode 100644
index 0000000000..079cc92e8e
--- /dev/null
+++ b/spec/ruby/core/argf/fixtures/lineno.rb
@@ -0,0 +1,5 @@
+puts $.
+ARGF.gets
+puts $.
+ARGF.gets
+puts $.
diff --git a/spec/ruby/core/argf/fixtures/rewind.rb b/spec/ruby/core/argf/fixtures/rewind.rb
new file mode 100644
index 0000000000..90a334cd89
--- /dev/null
+++ b/spec/ruby/core/argf/fixtures/rewind.rb
@@ -0,0 +1,5 @@
+puts ARGF.lineno
+ARGF.gets
+puts ARGF.lineno
+ARGF.rewind
+puts ARGF.lineno
diff --git a/spec/ruby/core/argf/fixtures/stdin.txt b/spec/ruby/core/argf/fixtures/stdin.txt
new file mode 100644
index 0000000000..063cdcb1d4
--- /dev/null
+++ b/spec/ruby/core/argf/fixtures/stdin.txt
@@ -0,0 +1,2 @@
+stdin.1
+stdin.2
diff --git a/spec/ruby/core/argf/getc_spec.rb b/spec/ruby/core/argf/getc_spec.rb
new file mode 100644
index 0000000000..dc5de9b7df
--- /dev/null
+++ b/spec/ruby/core/argf/getc_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require_relative 'shared/getc'
+
+describe "ARGF.getc" do
+ it_behaves_like :argf_getc, :getc
+end
+
+describe "ARGF.getc" do
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ it "returns nil when end of stream reached" do
+ argf [@file1, @file2] do
+ @argf.read
+ @argf.getc.should == nil
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/gets_spec.rb b/spec/ruby/core/argf/gets_spec.rb
new file mode 100644
index 0000000000..5863147ec8
--- /dev/null
+++ b/spec/ruby/core/argf/gets_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require_relative 'shared/gets'
+
+describe "ARGF.gets" do
+ it_behaves_like :argf_gets, :gets
+end
+
+describe "ARGF.gets" do
+ it_behaves_like :argf_gets_inplace_edit, :gets
+end
+
+describe "ARGF.gets" do
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+
+ @file1 = File.readlines @file1_name
+ @file2 = File.readlines @file2_name
+ end
+
+ it "returns nil when reaching end of files" do
+ argf [@file1_name, @file2_name] do
+ total = @file1.size + @file2.size
+ total.times { @argf.gets }
+ @argf.gets.should == nil
+ end
+ end
+
+ with_feature :encoding do
+ before :each do
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+
+ Encoding.default_external = Encoding::UTF_8
+ Encoding.default_internal = nil
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+ end
+
+ it "reads the contents of the file with default encoding" do
+ Encoding.default_external = Encoding::US_ASCII
+ argf [@file1_name, @file2_name] do
+ @argf.gets.encoding.should == Encoding::US_ASCII
+ end
+ end
+ end
+
+end
diff --git a/spec/ruby/core/argf/lineno_spec.rb b/spec/ruby/core/argf/lineno_spec.rb
new file mode 100644
index 0000000000..72a108c187
--- /dev/null
+++ b/spec/ruby/core/argf/lineno_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+
+describe "ARGF.lineno" do
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ # NOTE: this test assumes that fixtures files have two lines each
+ # TODO: break this into four specs
+ it "returns the current line number on each file" do
+ argf [@file1, @file2] do
+ @argf.lineno = 0
+ @argf.gets
+ @argf.lineno.should == 1
+ @argf.gets
+ @argf.lineno.should == 2
+ @argf.gets
+ @argf.lineno.should == 3
+ @argf.gets
+ @argf.lineno.should == 4
+ end
+ end
+
+ it "aliases to $." do
+ script = fixture __FILE__, "lineno.rb"
+ out = ruby_exe(script, args: [@file1, @file2])
+ out.should == "0\n1\n2\n"
+ end
+end
diff --git a/spec/ruby/core/argf/lines_spec.rb b/spec/ruby/core/argf/lines_spec.rb
new file mode 100644
index 0000000000..6ca6ff1256
--- /dev/null
+++ b/spec/ruby/core/argf/lines_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/each_line'
+
+describe "ARGF.lines" do
+ it_behaves_like :argf_each_line, :lines
+end
diff --git a/spec/ruby/core/argf/path_spec.rb b/spec/ruby/core/argf/path_spec.rb
new file mode 100644
index 0000000000..7120f7d0e3
--- /dev/null
+++ b/spec/ruby/core/argf/path_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/filename'
+
+describe "ARGF.path" do
+ it_behaves_like :argf_filename, :path
+end
diff --git a/spec/ruby/core/argf/pos_spec.rb b/spec/ruby/core/argf/pos_spec.rb
new file mode 100644
index 0000000000..fb3f25b945
--- /dev/null
+++ b/spec/ruby/core/argf/pos_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require_relative 'shared/pos'
+
+describe "ARGF.pos" do
+ it_behaves_like :argf_pos, :pos
+end
+
+describe "ARGF.pos=" do
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+
+ @file1 = File.readlines @file1_name
+ @file2 = File.readlines @file2_name
+ end
+
+ # NOTE: this test assumes that fixtures files have two lines each
+ it "sets the correct position in files" do
+ argf [@file1_name, @file2_name] do
+ @argf.pos = @file1.first.size
+ @argf.gets.should == @file1.last
+ @argf.pos = 0
+ @argf.gets.should == @file1.first
+
+ # finish reading file1
+ @argf.gets
+
+ @argf.gets
+ @argf.pos = 1
+ @argf.gets.should == @file2.first[1..-1]
+
+ @argf.pos = @file2.first.size + @file2.last.size - 1
+ @argf.gets.should == @file2.last[-1,1]
+ @argf.pos = 1000
+ @argf.read.should == ""
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/read_nonblock_spec.rb b/spec/ruby/core/argf/read_nonblock_spec.rb
new file mode 100644
index 0000000000..466264119f
--- /dev/null
+++ b/spec/ruby/core/argf/read_nonblock_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../../spec_helper'
+require_relative 'shared/read'
+
+platform_is_not :windows do
+ describe 'ARGF.read_nonblock' do
+ it_behaves_like :argf_read, :read_nonblock
+
+ before do
+ @file1_name = fixture(__FILE__, 'file1.txt')
+ @file2_name = fixture(__FILE__, 'file2.txt')
+
+ @file1 = File.read(@file1_name)
+ @file2 = File.read(@file2_name)
+
+ @chunk1 = File.read(@file1_name, 4)
+ @chunk2 = File.read(@file2_name, 4)
+ end
+
+ it 'reads up to the given amount of bytes' do
+ argf [@file1_name] do
+ @argf.read_nonblock(4).should == @chunk1
+ end
+ end
+
+ describe 'when using multiple files' do
+ it 'reads up to the given amount of bytes from the first file' do
+ argf [@file1_name, @file2_name] do
+ @argf.read_nonblock(4).should == @chunk1
+ end
+ end
+
+ it 'returns an empty String when reading after having read the first file in its entirety' do
+ argf [@file1_name, @file2_name] do
+ @argf.read_nonblock(File.size(@file1_name)).should == @file1
+ @argf.read_nonblock(4).should == ''
+ end
+ end
+ end
+
+ it 'reads up to the given bytes from STDIN' do
+ stdin = ruby_exe('print ARGF.read_nonblock(4)', :args => "< #{@file1_name}")
+
+ stdin.should == @chunk1
+ end
+
+ it 'reads up to the given bytes from a file when a file and STDIN are present' do
+ stdin = ruby_exe("print ARGF.read_nonblock(4)", :args => "#{@file1_name} - < #{@file2_name}")
+
+ stdin.should == @chunk1
+ end
+
+ context "with STDIN" do
+ before do
+ @r, @w = IO.pipe
+ @stdin = $stdin
+ $stdin = @r
+ end
+
+ after do
+ $stdin = @stdin
+ @w.close
+ @r.close unless @r.closed?
+ end
+
+ it 'raises IO::EAGAINWaitReadable when empty' do
+ argf ['-'] do
+ lambda {
+ @argf.read_nonblock(4)
+ }.should raise_error(IO::EAGAINWaitReadable)
+ end
+ end
+
+ it 'returns :wait_readable when the :exception is set to false' do
+ argf ['-'] do
+ @argf.read_nonblock(4, nil, exception: false).should == :wait_readable
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/read_spec.rb b/spec/ruby/core/argf/read_spec.rb
new file mode 100644
index 0000000000..b889605572
--- /dev/null
+++ b/spec/ruby/core/argf/read_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../../spec_helper'
+require_relative 'shared/read'
+
+describe "ARGF.read" do
+ it_behaves_like :argf_read, :read
+
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+ @stdin_name = fixture __FILE__, "stdin.txt"
+
+ @file1 = File.read @file1_name
+ @file2 = File.read @file2_name
+ @stdin = File.read @stdin_name
+ end
+
+ it "reads the contents of a file" do
+ argf [@file1_name] do
+ @argf.read().should == @file1
+ end
+ end
+
+ it "treats first nil argument as no length limit" do
+ argf [@file1_name] do
+ @argf.read(nil).should == @file1
+ end
+ end
+
+ it "reads the contents of two files" do
+ argf [@file1_name, @file2_name] do
+ @argf.read.should == @file1 + @file2
+ end
+ end
+
+ it "reads the contents of one file and some characters from the second" do
+ argf [@file1_name, @file2_name] do
+ len = @file1.size + (@file2.size / 2)
+ @argf.read(len).should == (@file1 + @file2)[0,len]
+ end
+ end
+
+ it "reads across two files consecutively" do
+ argf [@file1_name, @file2_name] do
+ @argf.read(@file1.size - 2).should == @file1[0..-3]
+ @argf.read(2+5).should == @file1[-2..-1] + @file2[0,5]
+ end
+ end
+
+ it "reads the contents of stdin" do
+ stdin = ruby_exe("print ARGF.read", args: "< #{@stdin_name}")
+ stdin.should == @stdin
+ end
+
+ it "reads the contents of one file and stdin" do
+ stdin = ruby_exe("print ARGF.read", args: "#{@file1_name} - < #{@stdin_name}")
+ stdin.should == @file1 + @stdin
+ end
+
+ it "reads the contents of the same file twice" do
+ argf [@file1_name, @file1_name] do
+ @argf.read.should == @file1 + @file1
+ end
+ end
+
+ with_feature :encoding do
+
+ before :each do
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+
+ Encoding.default_external = Encoding::UTF_8
+ Encoding.default_internal = nil
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+ end
+
+ it "reads the contents of the file with default encoding" do
+ Encoding.default_external = Encoding::US_ASCII
+ argf [@file1_name, @file2_name] do
+ @argf.read.encoding.should == Encoding::US_ASCII
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/readchar_spec.rb b/spec/ruby/core/argf/readchar_spec.rb
new file mode 100644
index 0000000000..bd3bfd28ae
--- /dev/null
+++ b/spec/ruby/core/argf/readchar_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require_relative 'shared/getc'
+
+describe "ARGF.getc" do
+ it_behaves_like :argf_getc, :readchar
+end
+
+describe "ARGF.readchar" do
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ it "raises EOFError when end of stream reached" do
+ argf [@file1, @file2] do
+ lambda { while @argf.readchar; end }.should raise_error(EOFError)
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/readline_spec.rb b/spec/ruby/core/argf/readline_spec.rb
new file mode 100644
index 0000000000..e196274068
--- /dev/null
+++ b/spec/ruby/core/argf/readline_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'shared/gets'
+
+describe "ARGF.readline" do
+ it_behaves_like :argf_gets, :readline
+end
+
+describe "ARGF.readline" do
+ it_behaves_like :argf_gets_inplace_edit, :readline
+end
+
+describe "ARGF.readline" do
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ it "raises an EOFError when reaching end of files" do
+ argf [@file1, @file2] do
+ lambda { while @argf.readline; end }.should raise_error(EOFError)
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/readlines_spec.rb b/spec/ruby/core/argf/readlines_spec.rb
new file mode 100644
index 0000000000..30be936dab
--- /dev/null
+++ b/spec/ruby/core/argf/readlines_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/readlines'
+
+describe "ARGF.readlines" do
+ it_behaves_like :argf_readlines, :readlines
+end
diff --git a/spec/ruby/core/argf/readpartial_spec.rb b/spec/ruby/core/argf/readpartial_spec.rb
new file mode 100644
index 0000000000..4e697683dc
--- /dev/null
+++ b/spec/ruby/core/argf/readpartial_spec.rb
@@ -0,0 +1,75 @@
+require_relative '../../spec_helper'
+require_relative 'shared/read'
+
+describe "ARGF.readpartial" do
+ it_behaves_like :argf_read, :readpartial
+
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+ @stdin_name = fixture __FILE__, "stdin.txt"
+
+ @file1 = File.read @file1_name
+ @file2 = File.read @file2_name
+ @stdin = File.read @stdin_name
+ end
+
+ it "raises an ArgumentError if called without a maximum read length" do
+ argf [@file1_name] do
+ lambda { @argf.readpartial }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "reads maximum number of bytes from one file at a time" do
+ argf [@file1_name, @file2_name] do
+ len = @file1.size + @file2.size
+ @argf.readpartial(len).should == @file1
+ end
+ end
+
+ it "clears output buffer even if EOFError is raised because @argf is at end" do
+ begin
+ output = "to be cleared"
+
+ argf [@file1_name] do
+ @argf.read
+ @argf.readpartial(1, output)
+ end
+ rescue EOFError
+ output.should == ""
+ end
+ end
+
+ it "reads maximum number of bytes from one file at a time" do
+ argf [@file1_name, @file2_name] do
+ len = @file1.size + @file2.size
+ @argf.readpartial(len).should == @file1
+ end
+ end
+
+ it "returns an empty string if EOFError is raised while reading any but the last file" do
+ argf [@file1_name, @file2_name] do
+ @argf.readpartial(@file1.size)
+ @argf.readpartial(1).should == ""
+ end
+ end
+
+ it "raises an EOFError if the exception was raised while reading the last file" do
+ argf [@file1_name, @file2_name] do
+ @argf.readpartial(@file1.size)
+ @argf.readpartial(1)
+ @argf.readpartial(@file2.size)
+ lambda { @argf.readpartial(1) }.should raise_error(EOFError)
+ lambda { @argf.readpartial(1) }.should raise_error(EOFError)
+ end
+ end
+
+ it "raises an EOFError if the exception was raised while reading STDIN" do
+ ruby_str = <<-STR
+ print ARGF.readpartial(#{@stdin.size})
+ ARGF.readpartial(1) rescue print $!.class
+ STR
+ stdin = ruby_exe(ruby_str, args: "< #{@stdin_name}", escape: true)
+ stdin.should == @stdin + "EOFError"
+ end
+end
diff --git a/spec/ruby/core/argf/rewind_spec.rb b/spec/ruby/core/argf/rewind_spec.rb
new file mode 100644
index 0000000000..fcb2e81a3e
--- /dev/null
+++ b/spec/ruby/core/argf/rewind_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+
+describe "ARGF.rewind" do
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+
+ @file1 = File.readlines @file1_name
+ @file2 = File.readlines @file2_name
+ end
+
+ # NOTE: this test assumes that fixtures files have two lines each
+ it "goes back to beginning of current file" do
+ argf [@file1_name, @file2_name] do
+ @argf.gets
+ @argf.rewind
+ @argf.gets.should == @file1.first
+
+ @argf.gets # finish reading file1
+
+ @argf.gets
+ @argf.rewind
+ @argf.gets.should == @file2.first
+ end
+ end
+
+ it "resets ARGF.lineno to 0" do
+ script = fixture __FILE__, "rewind.rb"
+ out = ruby_exe(script, args: [@file1_name, @file2_name])
+ out.should == "0\n1\n0\n"
+ end
+
+ it "raises an ArgumentError when end of stream reached" do
+ argf [@file1_name, @file2_name] do
+ @argf.read
+ lambda { @argf.rewind }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/seek_spec.rb b/spec/ruby/core/argf/seek_spec.rb
new file mode 100644
index 0000000000..c983c0cb5a
--- /dev/null
+++ b/spec/ruby/core/argf/seek_spec.rb
@@ -0,0 +1,63 @@
+require_relative '../../spec_helper'
+
+describe "ARGF.seek" do
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+
+ @file1 = File.readlines @file1_name
+ @file2 = File.readlines @file2_name
+ end
+
+ it "sets the absolute position relative to beginning of file" do
+ argf [@file1_name, @file2_name] do
+ @argf.seek 2
+ @argf.gets.should == @file1.first[2..-1]
+ @argf.seek @file1.first.size
+ @argf.gets.should == @file1.last
+ @argf.seek 0, IO::SEEK_END
+ @argf.gets.should == @file2.first
+ end
+ end
+
+ it "sets the position relative to current position in file" do
+ argf [@file1_name, @file2_name] do
+ @argf.seek(0, IO::SEEK_CUR)
+ @argf.gets.should == @file1.first
+ @argf.seek(-@file1.first.size+2, IO::SEEK_CUR)
+ @argf.gets.should == @file1.first[2..-1]
+ @argf.seek(1, IO::SEEK_CUR)
+ @argf.gets.should == @file1.last[1..-1]
+ @argf.seek(3, IO::SEEK_CUR)
+ @argf.gets.should == @file2.first
+ @argf.seek(@file1.last.size, IO::SEEK_CUR)
+ @argf.gets.should == nil
+ end
+ end
+
+ it "sets the absolute position relative to end of file" do
+ argf [@file1_name, @file2_name] do
+ @argf.seek(-@file1.first.size-@file1.last.size, IO::SEEK_END)
+ @argf.gets.should == @file1.first
+ @argf.seek(-6, IO::SEEK_END)
+ @argf.gets.should == @file1.last[-6..-1]
+ @argf.seek(-4, IO::SEEK_END)
+ @argf.gets.should == @file1.last[4..-1]
+ @argf.gets.should == @file2.first
+ @argf.seek(-6, IO::SEEK_END)
+ @argf.gets.should == @file2.last[-6..-1]
+ end
+ end
+end
+
+describe "ARGF.seek" do
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ end
+
+ it "takes at least one argument (offset)" do
+ argf [@file1_name] do
+ lambda { @argf.seek }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/set_encoding_spec.rb b/spec/ruby/core/argf/set_encoding_spec.rb
new file mode 100644
index 0000000000..a871e084b6
--- /dev/null
+++ b/spec/ruby/core/argf/set_encoding_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+
+describe "ARGF.set_encoding" do
+ before :each do
+ @file = fixture __FILE__, "file1.txt"
+ end
+
+ it "sets the external encoding when passed an encoding instance" do
+ argf [@file] do
+ @argf.set_encoding(Encoding::US_ASCII)
+ @argf.external_encoding.should == Encoding::US_ASCII
+ @argf.gets.encoding.should == Encoding::US_ASCII
+ end
+ end
+
+ it "sets the external encoding when passed an encoding name" do
+ argf [@file] do
+ @argf.set_encoding("us-ascii")
+ @argf.external_encoding.should == Encoding::US_ASCII
+ @argf.gets.encoding.should == Encoding::US_ASCII
+ end
+ end
+
+ it "sets the external, internal encoding when passed two encoding instances" do
+ argf [@file] do
+ @argf.set_encoding(Encoding::US_ASCII, Encoding::EUC_JP)
+ @argf.external_encoding.should == Encoding::US_ASCII
+ @argf.internal_encoding.should == Encoding::EUC_JP
+ @argf.gets.encoding.should == Encoding::EUC_JP
+ end
+ end
+
+ it "sets the external, internal encoding when passed 'ext:int' String" do
+ argf [@file] do
+ @argf.set_encoding("us-ascii:euc-jp")
+ @argf.external_encoding.should == Encoding::US_ASCII
+ @argf.internal_encoding.should == Encoding::EUC_JP
+ @argf.gets.encoding.should == Encoding::EUC_JP
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/shared/each_byte.rb b/spec/ruby/core/argf/shared/each_byte.rb
new file mode 100644
index 0000000000..6b1dc1dae2
--- /dev/null
+++ b/spec/ruby/core/argf/shared/each_byte.rb
@@ -0,0 +1,58 @@
+describe :argf_each_byte, shared: true do
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+
+ @bytes = []
+ File.read(@file1_name).each_byte { |b| @bytes << b }
+ File.read(@file2_name).each_byte { |b| @bytes << b }
+ end
+
+ it "yields each byte of all streams to the passed block" do
+ argf [@file1_name, @file2_name] do
+ bytes = []
+ @argf.send(@method) { |b| bytes << b }
+ bytes.should == @bytes
+ end
+ end
+
+ it "returns self when passed a block" do
+ argf [@file1_name, @file2_name] do
+ @argf.send(@method) {}.should equal(@argf)
+ end
+ end
+
+ it "returns an Enumerator when passed no block" do
+ argf [@file1_name, @file2_name] do
+ enum = @argf.send(@method)
+ enum.should be_an_instance_of(Enumerator)
+
+ bytes = []
+ enum.each { |b| bytes << b }
+ bytes.should == @bytes
+ end
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ argf [@file1_name, @file2_name] do
+ enum = @argf.send(@method)
+ enum.should be_an_instance_of(Enumerator)
+
+ bytes = []
+ enum.each { |b| bytes << b }
+ bytes.should == @bytes
+ end
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ argf [@file1_name, @file2_name] do
+ @argf.send(@method).size.should == nil
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/shared/each_char.rb b/spec/ruby/core/argf/shared/each_char.rb
new file mode 100644
index 0000000000..9e333ecc5b
--- /dev/null
+++ b/spec/ruby/core/argf/shared/each_char.rb
@@ -0,0 +1,58 @@
+describe :argf_each_char, shared: true do
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+
+ @chars = []
+ File.read(@file1_name).each_char { |c| @chars << c }
+ File.read(@file2_name).each_char { |c| @chars << c }
+ end
+
+ it "yields each char of all streams to the passed block" do
+ argf [@file1_name, @file2_name] do
+ chars = []
+ @argf.send(@method) { |c| chars << c }
+ chars.should == @chars
+ end
+ end
+
+ it "returns self when passed a block" do
+ argf [@file1_name, @file2_name] do
+ @argf.send(@method) {}.should equal(@argf)
+ end
+ end
+
+ it "returns an Enumerator when passed no block" do
+ argf [@file1_name, @file2_name] do
+ enum = @argf.send(@method)
+ enum.should be_an_instance_of(Enumerator)
+
+ chars = []
+ enum.each { |c| chars << c }
+ chars.should == @chars
+ end
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ argf [@file1_name, @file2_name] do
+ enum = @argf.send(@method)
+ enum.should be_an_instance_of(Enumerator)
+
+ chars = []
+ enum.each { |c| chars << c }
+ chars.should == @chars
+ end
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ argf [@file1_name, @file2_name] do
+ @argf.send(@method).size.should == nil
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/shared/each_codepoint.rb b/spec/ruby/core/argf/shared/each_codepoint.rb
new file mode 100644
index 0000000000..e2a2dfff46
--- /dev/null
+++ b/spec/ruby/core/argf/shared/each_codepoint.rb
@@ -0,0 +1,58 @@
+describe :argf_each_codepoint, shared: true do
+ before :each do
+ file1_name = fixture __FILE__, "file1.txt"
+ file2_name = fixture __FILE__, "file2.txt"
+ @filenames = [file1_name, file2_name]
+
+ @codepoints = File.read(file1_name).codepoints
+ @codepoints.concat File.read(file2_name).codepoints
+ end
+
+ it "is a public method" do
+ argf @filenames do
+ @argf.public_methods(false).should include(@method)
+ end
+ end
+
+ it "does not require arguments" do
+ argf @filenames do
+ @argf.method(@method).arity.should == 0
+ end
+ end
+
+ it "returns self when passed a block" do
+ argf @filenames do
+ @argf.send(@method) {}.should equal(@argf)
+ end
+ end
+
+ it "returns an Enumerator when passed no block" do
+ argf @filenames do
+ @argf.send(@method).should be_an_instance_of(Enumerator)
+ end
+ end
+
+ it "yields each codepoint of all streams" do
+ argf @filenames do
+ @argf.send(@method).to_a.should == @codepoints
+ end
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ argf @filenames do
+ @argf.send(@method).should be_an_instance_of(Enumerator)
+ end
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ argf @filenames do
+ @argf.send(@method).size.should == nil
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/shared/each_line.rb b/spec/ruby/core/argf/shared/each_line.rb
new file mode 100644
index 0000000000..c0ef77dc54
--- /dev/null
+++ b/spec/ruby/core/argf/shared/each_line.rb
@@ -0,0 +1,62 @@
+describe :argf_each_line, shared: true do
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+
+ @lines = File.readlines @file1_name
+ @lines += File.readlines @file2_name
+ end
+
+ it "is a public method" do
+ argf [@file1_name, @file2_name] do
+ @argf.public_methods(false).should include(@method)
+ end
+ end
+
+ it "requires multiple arguments" do
+ argf [@file1_name, @file2_name] do
+ @argf.method(@method).arity.should < 0
+ end
+ end
+
+ it "reads each line of files" do
+ argf [@file1_name, @file2_name] do
+ lines = []
+ @argf.send(@method) { |b| lines << b }
+ lines.should == @lines
+ end
+ end
+
+ it "returns self when passed a block" do
+ argf [@file1_name, @file2_name] do
+ @argf.send(@method) {}.should equal(@argf)
+ end
+ end
+
+ describe "with a separator" do
+ it "yields each separated section of all streams" do
+ argf [@file1_name, @file2_name] do
+ @argf.send(@method, '.').to_a.should ==
+ (File.readlines(@file1_name, '.') + File.readlines(@file2_name, '.'))
+ end
+ end
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ argf [@file1_name, @file2_name] do
+ @argf.send(@method).should be_an_instance_of(Enumerator)
+ end
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ argf [@file1_name, @file2_name] do
+ @argf.send(@method).size.should == nil
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/shared/eof.rb b/spec/ruby/core/argf/shared/eof.rb
new file mode 100644
index 0000000000..bba18ede50
--- /dev/null
+++ b/spec/ruby/core/argf/shared/eof.rb
@@ -0,0 +1,24 @@
+describe :argf_eof, shared: true do
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ # NOTE: this test assumes that fixtures files have two lines each
+ it "returns true when reaching the end of a file" do
+ argf [@file1, @file2] do
+ result = []
+ while @argf.gets
+ result << @argf.send(@method)
+ end
+ result.should == [false, true, false, true]
+ end
+ end
+
+ it "raises IOError when called on a closed stream" do
+ argf [@file1] do
+ @argf.read
+ lambda { @argf.send(@method) }.should raise_error(IOError)
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/shared/filename.rb b/spec/ruby/core/argf/shared/filename.rb
new file mode 100644
index 0000000000..f47c673dc0
--- /dev/null
+++ b/spec/ruby/core/argf/shared/filename.rb
@@ -0,0 +1,28 @@
+describe :argf_filename, shared: true do
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ # NOTE: this test assumes that fixtures files have two lines each
+ it "returns the current file name on each file" do
+ argf [@file1, @file2] do
+ result = []
+ # returns first current file even when not yet open
+ result << @argf.send(@method)
+ result << @argf.send(@method) while @argf.gets
+ # returns last current file even when closed
+ result << @argf.send(@method)
+
+ result.map! { |f| File.expand_path(f) }
+ result.should == [@file1, @file1, @file1, @file2, @file2, @file2]
+ end
+ end
+
+ # NOTE: this test assumes that fixtures files have two lines each
+ it "sets the $FILENAME global variable with the current file name on each file" do
+ script = fixture __FILE__, "filename.rb"
+ out = ruby_exe(script, args: [@file1, @file2])
+ out.should == "#{@file1}\n#{@file1}\n#{@file2}\n#{@file2}\n#{@file2}\n"
+ end
+end
diff --git a/spec/ruby/core/argf/shared/fileno.rb b/spec/ruby/core/argf/shared/fileno.rb
new file mode 100644
index 0000000000..891e250ad9
--- /dev/null
+++ b/spec/ruby/core/argf/shared/fileno.rb
@@ -0,0 +1,24 @@
+describe :argf_fileno, shared: true do
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ # NOTE: this test assumes that fixtures files have two lines each
+ it "returns the current file number on each file" do
+ argf [@file1, @file2] do
+ result = []
+ # returns first current file even when not yet open
+ result << @argf.send(@method) while @argf.gets
+ # returns last current file even when closed
+ result.map { |d| d.class }.should == [Fixnum, Fixnum, Fixnum, Fixnum]
+ end
+ end
+
+ it "raises an ArgumentError when called on a closed stream" do
+ argf [@file1] do
+ @argf.read
+ lambda { @argf.send(@method) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/shared/getc.rb b/spec/ruby/core/argf/shared/getc.rb
new file mode 100644
index 0000000000..8be39c60b6
--- /dev/null
+++ b/spec/ruby/core/argf/shared/getc.rb
@@ -0,0 +1,17 @@
+describe :argf_getc, shared: true do
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+
+ @chars = File.read @file1
+ @chars += File.read @file2
+ end
+
+ it "reads each char of files" do
+ argf [@file1, @file2] do
+ chars = ""
+ @chars.size.times { chars << @argf.send(@method) }
+ chars.should == @chars
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/shared/gets.rb b/spec/ruby/core/argf/shared/gets.rb
new file mode 100644
index 0000000000..160d24c27b
--- /dev/null
+++ b/spec/ruby/core/argf/shared/gets.rb
@@ -0,0 +1,99 @@
+describe :argf_gets, shared: true do
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+ @stdin_name = fixture __FILE__, "stdin.txt"
+
+ @file1 = File.readlines @file1_name
+ @file2 = File.readlines @file2_name
+ @stdin = File.read @stdin_name
+ end
+
+ it "reads one line of a file" do
+ argf [@file1_name] do
+ @argf.send(@method).should == @file1.first
+ end
+ end
+
+ it "reads all lines of a file" do
+ argf [@file1_name] do
+ lines = []
+ @file1.size.times { lines << @argf.send(@method) }
+ lines.should == @file1
+ end
+ end
+
+ it "reads all lines of stdin" do
+ total = @stdin.count $/
+ stdin = ruby_exe(
+ "#{total}.times { print ARGF.send(#{@method.inspect}) }",
+ args: "< #{@stdin_name}")
+ stdin.should == @stdin
+ end
+
+ it "reads all lines of two files" do
+ argf [@file1_name, @file2_name] do
+ total = @file1.size + @file2.size
+ lines = []
+ total.times { lines << @argf.send(@method) }
+ lines.should == @file1 + @file2
+ end
+ end
+
+ it "sets $_ global variable with each line read" do
+ argf [@file1_name, @file2_name] do
+ total = @file1.size + @file2.size
+ total.times do
+ line = @argf.send(@method)
+ $_.should == line
+ end
+ end
+ end
+end
+
+describe :argf_gets_inplace_edit, shared: true do
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+
+ @tmp1_name = tmp "file1.txt"
+ @tmp2_name = tmp "file2.txt"
+
+ @tmp1_name_bak = @tmp1_name + ".bak"
+ @tmp2_name_bak = @tmp2_name + ".bak"
+
+ cp @file1_name, @tmp1_name
+ cp @file2_name, @tmp2_name
+
+ method = "ARGF.send(#{@method.inspect})"
+ @code = "begin while line = #{method} do puts 'x' end rescue EOFError; end"
+ end
+
+ after :each do
+ rm_r @tmp1_name, @tmp2_name, @tmp1_name_bak, @tmp2_name_bak
+ end
+
+ # -i with no backup extension is not supported on Windows
+ platform_is_not :windows do
+ it "modifies the files when in place edit mode is on" do
+ ruby_exe(@code,
+ options: "-i",
+ args: "#{@tmp1_name} #{@tmp2_name}")
+
+ File.read(@tmp1_name).should == "x\nx\n"
+ File.read(@tmp2_name).should == "x\nx\n"
+ end
+ end
+
+ it "modifies and backups two files when in place edit mode is on" do
+ ruby_exe(@code,
+ options: "-i.bak",
+ args: "#{@tmp1_name} #{@tmp2_name}")
+
+ File.read(@tmp1_name).should == "x\nx\n"
+ File.read(@tmp2_name).should == "x\nx\n"
+
+ File.read(@tmp1_name_bak).should == "file1.1\nfile1.2\n"
+ File.read(@tmp2_name_bak).should == "line2.1\nline2.2\n"
+ end
+end
diff --git a/spec/ruby/core/argf/shared/pos.rb b/spec/ruby/core/argf/shared/pos.rb
new file mode 100644
index 0000000000..f7184f3d7c
--- /dev/null
+++ b/spec/ruby/core/argf/shared/pos.rb
@@ -0,0 +1,31 @@
+describe :argf_pos, shared: true do
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ it "gives the correct position for each read operation" do
+ argf [@file1, @file2] do
+ size1 = File.size(@file1)
+ size2 = File.size(@file2)
+
+ @argf.read(2)
+ @argf.send(@method).should == 2
+ @argf.read(size1-2)
+ @argf.send(@method).should == size1
+ @argf.read(6)
+ @argf.send(@method).should == 6
+ @argf.rewind
+ @argf.send(@method).should == 0
+ @argf.read(size2)
+ @argf.send(@method).should == size2
+ end
+ end
+
+ it "raises an ArgumentError when called on a closed stream" do
+ argf [@file1] do
+ @argf.read
+ lambda { @argf.send(@method) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/shared/read.rb b/spec/ruby/core/argf/shared/read.rb
new file mode 100644
index 0000000000..fe903983c0
--- /dev/null
+++ b/spec/ruby/core/argf/shared/read.rb
@@ -0,0 +1,58 @@
+describe :argf_read, shared: true do
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @stdin_name = fixture __FILE__, "stdin.txt"
+
+ @file1 = File.read @file1_name
+ @stdin = File.read @stdin_name
+ end
+
+ it "treats second nil argument as no output buffer" do
+ argf [@file1_name] do
+ @argf.send(@method, @file1.size, nil).should == @file1
+ end
+ end
+
+ it "treats second argument as an output buffer" do
+ argf [@file1_name] do
+ buffer = ""
+ @argf.send(@method, @file1.size, buffer)
+ buffer.should == @file1
+ end
+ end
+
+ it "clears output buffer before appending to it" do
+ argf [@file1_name] do
+ buffer = "to be cleared"
+ @argf.send(@method, @file1.size, buffer)
+ buffer.should == @file1
+ end
+ end
+
+ it "reads a number of bytes from the first file" do
+ argf [@file1_name] do
+ @argf.send(@method, 5).should == @file1[0, 5]
+ end
+ end
+
+ it "reads from a single file consecutively" do
+ argf [@file1_name] do
+ @argf.send(@method, 1).should == @file1[0, 1]
+ @argf.send(@method, 2).should == @file1[1, 2]
+ @argf.send(@method, 3).should == @file1[3, 3]
+ end
+ end
+
+ it "reads a number of bytes from stdin" do
+ stdin = ruby_exe("print ARGF.#{@method}(10)", :args => "< #{@stdin_name}")
+ stdin.should == @stdin[0, 10]
+ end
+
+ platform_is_not :windows do
+ it "reads the contents of a special device file" do
+ argf ['/dev/zero'] do
+ @argf.send(@method, 100).should == "\000" * 100
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/shared/readlines.rb b/spec/ruby/core/argf/shared/readlines.rb
new file mode 100644
index 0000000000..505fa94acb
--- /dev/null
+++ b/spec/ruby/core/argf/shared/readlines.rb
@@ -0,0 +1,22 @@
+describe :argf_readlines, shared: true do
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+
+ @lines = File.readlines(@file1)
+ @lines += File.readlines(@file2)
+ end
+
+ it "reads all lines of all files" do
+ argf [@file1, @file2] do
+ @argf.send(@method).should == @lines
+ end
+ end
+
+ it "returns an empty Array when end of stream reached" do
+ argf [@file1, @file2] do
+ @argf.read
+ @argf.send(@method).should == []
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/skip_spec.rb b/spec/ruby/core/argf/skip_spec.rb
new file mode 100644
index 0000000000..89b7146e3a
--- /dev/null
+++ b/spec/ruby/core/argf/skip_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+
+describe "ARGF.skip" do
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+
+ @file2 = File.readlines @file2_name
+ end
+
+ it "skips the current file" do
+ argf [@file1_name, @file2_name] do
+ @argf.read(1)
+ @argf.skip
+ @argf.gets.should == @file2.first
+ end
+ end
+
+ it "has no effect when called twice in a row" do
+ argf [@file1_name, @file2_name] do
+ @argf.read(1)
+ @argf.skip
+ @argf.skip
+ @argf.gets.should == @file2.first
+ end
+ end
+
+ it "has no effect at end of stream" do
+ argf [@file1_name, @file2_name] do
+ @argf.read
+ @argf.skip
+ @argf.gets.should == nil
+ end
+ end
+
+ # This bypasses argf helper because the helper will call argf.file
+ # which as a side-effect calls argf.file which will initialize
+ # internals of ARGF enough for this to work.
+ it "has no effect when nothing has been processed yet" do
+ lambda { ARGF.class.new(@file1_name).skip }.should_not raise_error
+ end
+end
diff --git a/spec/ruby/core/argf/tell_spec.rb b/spec/ruby/core/argf/tell_spec.rb
new file mode 100644
index 0000000000..16d9f29920
--- /dev/null
+++ b/spec/ruby/core/argf/tell_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/pos'
+
+describe "ARGF.tell" do
+ it_behaves_like :argf_pos, :tell
+end
diff --git a/spec/ruby/core/argf/to_a_spec.rb b/spec/ruby/core/argf/to_a_spec.rb
new file mode 100644
index 0000000000..b17a93db33
--- /dev/null
+++ b/spec/ruby/core/argf/to_a_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/readlines'
+
+describe "ARGF.to_a" do
+ it_behaves_like :argf_readlines, :to_a
+end
diff --git a/spec/ruby/core/argf/to_i_spec.rb b/spec/ruby/core/argf/to_i_spec.rb
new file mode 100644
index 0000000000..2183de6cd4
--- /dev/null
+++ b/spec/ruby/core/argf/to_i_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/fileno'
+
+describe "ARGF.to_i" do
+ it_behaves_like :argf_fileno, :to_i
+end
diff --git a/spec/ruby/core/argf/to_io_spec.rb b/spec/ruby/core/argf/to_io_spec.rb
new file mode 100644
index 0000000000..062383d291
--- /dev/null
+++ b/spec/ruby/core/argf/to_io_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+
+describe "ARGF.to_io" do
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ # NOTE: this test assumes that fixtures files have two lines each
+ it "returns the IO of the current file" do
+ argf [@file1, @file2] do
+ result = []
+ 4.times do
+ @argf.gets
+ result << @argf.to_io
+ end
+
+ result.each { |io| io.should be_kind_of(IO) }
+ result[0].should == result[1]
+ result[2].should == result[3]
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/to_s_spec.rb b/spec/ruby/core/argf/to_s_spec.rb
new file mode 100644
index 0000000000..3f505898f4
--- /dev/null
+++ b/spec/ruby/core/argf/to_s_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe "ARGF.to_s" do
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ it "returns 'ARGF'" do
+ argf [@file1, @file2] do
+ @argf.to_s.should == "ARGF"
+ end
+ end
+end
diff --git a/spec/ruby/core/array/allocate_spec.rb b/spec/ruby/core/array/allocate_spec.rb
new file mode 100644
index 0000000000..a1800e7e16
--- /dev/null
+++ b/spec/ruby/core/array/allocate_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+describe "Array.allocate" do
+ it "returns an instance of Array" do
+ ary = Array.allocate
+ ary.should be_an_instance_of(Array)
+ end
+
+ it "returns a fully-formed instance of Array" do
+ ary = Array.allocate
+ ary.size.should == 0
+ ary << 1
+ ary.should == [1]
+ end
+
+ it "does not accept any arguments" do
+ lambda { Array.allocate(1) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/array/any_spec.rb b/spec/ruby/core/array/any_spec.rb
new file mode 100644
index 0000000000..2fa5353e99
--- /dev/null
+++ b/spec/ruby/core/array/any_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+
+describe "Array#any?" do
+ describe 'with no block given (a default block of { |x| x } is implicit)' do
+ it "is false if the array is empty" do
+ empty_array = []
+ empty_array.any?.should == false
+ end
+
+ it "is false if the array is not empty, but all the members of the array are falsy" do
+ falsy_array = [false, nil, false]
+ falsy_array.any?.should == false
+ end
+
+ it "is true if the array has any truthy members" do
+ not_empty_array = ['anything', nil]
+ not_empty_array.any?.should == true
+ end
+ end
+
+ describe 'with a block given' do
+ it 'is false if the array is empty' do
+ empty_array = []
+ empty_array.any? {|v| 1 == 1 }.should == false
+ end
+
+ it 'is true if the block returns true for any member of the array' do
+ array_with_members = [false, false, true, false]
+ array_with_members.any? {|v| v == true }.should == true
+ end
+
+ it 'is false if the block returns false for all members of the array' do
+ array_with_members = [false, false, true, false]
+ array_with_members.any? {|v| v == 42 }.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/array/append_spec.rb b/spec/ruby/core/array/append_spec.rb
new file mode 100644
index 0000000000..08ea814d89
--- /dev/null
+++ b/spec/ruby/core/array/append_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/push'
+
+describe "Array#<<" do
+ it "pushes the object onto the end of the array" do
+ ([ 1, 2 ] << "c" << "d" << [ 3, 4 ]).should == [1, 2, "c", "d", [3, 4]]
+ end
+
+ it "returns self to allow chaining" do
+ a = []
+ b = a
+ (a << 1).should equal(b)
+ (a << 2 << 3).should equal(b)
+ end
+
+ it "correctly resizes the Array" do
+ a = []
+ a.size.should == 0
+ a << :foo
+ a.size.should == 1
+ a << :bar << :baz
+ a.size.should == 3
+
+ a = [1, 2, 3]
+ a.shift
+ a.shift
+ a.shift
+ a << :foo
+ a.should == [:foo]
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { ArraySpecs.frozen_array << 5 }.should raise_error(frozen_error_class)
+ end
+end
+
+ruby_version_is "2.5" do
+ describe "Array#append" do
+ it_behaves_like :array_push, :append
+ end
+end
diff --git a/spec/ruby/core/array/array_spec.rb b/spec/ruby/core/array/array_spec.rb
new file mode 100644
index 0000000000..855f17348f
--- /dev/null
+++ b/spec/ruby/core/array/array_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Array" do
+ it "includes Enumerable" do
+ Array.include?(Enumerable).should == true
+ end
+end
diff --git a/spec/ruby/core/array/assoc_spec.rb b/spec/ruby/core/array/assoc_spec.rb
new file mode 100644
index 0000000000..f8479d763c
--- /dev/null
+++ b/spec/ruby/core/array/assoc_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#assoc" do
+ it "returns the first array whose 1st item is == obj or nil" do
+ s1 = ["colors", "red", "blue", "green"]
+ s2 = [:letters, "a", "b", "c"]
+ s3 = [4]
+ s4 = ["colors", "cyan", "yellow", "magenda"]
+ s5 = [:letters, "a", "i", "u"]
+ s_nil = [nil, nil]
+ a = [s1, s2, s3, s4, s5, s_nil]
+ a.assoc(s1.first).should equal(s1)
+ a.assoc(s2.first).should equal(s2)
+ a.assoc(s3.first).should equal(s3)
+ a.assoc(s4.first).should equal(s1)
+ a.assoc(s5.first).should equal(s2)
+ a.assoc(s_nil.first).should equal(s_nil)
+ a.assoc(4).should equal(s3)
+ a.assoc("key not in array").should be_nil
+ end
+
+ it "calls == on first element of each array" do
+ key1 = 'it'
+ key2 = mock('key2')
+ items = [['not it', 1], [ArraySpecs::AssocKey.new, 2], ['na', 3]]
+
+ items.assoc(key1).should equal(items[1])
+ items.assoc(key2).should be_nil
+ end
+
+ it "ignores any non-Array elements" do
+ [1, 2, 3].assoc(2).should be_nil
+ s1 = [4]
+ s2 = [5, 4, 3]
+ a = ["foo", [], s1, s2, nil, []]
+ a.assoc(s1.first).should equal(s1)
+ a.assoc(s2.first).should equal(s2)
+ end
+end
diff --git a/spec/ruby/core/array/at_spec.rb b/spec/ruby/core/array/at_spec.rb
new file mode 100644
index 0000000000..d237c9508a
--- /dev/null
+++ b/spec/ruby/core/array/at_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#at" do
+ it "returns the (n+1)'th element for the passed index n" do
+ a = [1, 2, 3, 4, 5, 6]
+ a.at(0).should == 1
+ a.at(1).should == 2
+ a.at(5).should == 6
+ end
+
+ it "returns nil if the given index is greater than or equal to the array's length" do
+ a = [1, 2, 3, 4, 5, 6]
+ a.at(6).should == nil
+ a.at(7).should == nil
+ end
+
+ it "returns the (-n)'th element from the last, for the given negative index n" do
+ a = [1, 2, 3, 4, 5, 6]
+ a.at(-1).should == 6
+ a.at(-2).should == 5
+ a.at(-6).should == 1
+ end
+
+ it "returns nil if the given index is less than -len, where len is length of the array" do
+ a = [1, 2, 3, 4, 5, 6]
+ a.at(-7).should == nil
+ a.at(-8).should == nil
+ end
+
+ it "does not extend the array unless the given index is out of range" do
+ a = [1, 2, 3, 4, 5, 6]
+ a.length.should == 6
+ a.at(100)
+ a.length.should == 6
+ a.at(-100)
+ a.length.should == 6
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ a = ["a", "b", "c"]
+ a.at(0.5).should == "a"
+
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(2)
+ a.at(obj).should == "c"
+ end
+
+ it "raises a TypeError when the passed argument can't be coerced to Integer" do
+ lambda { [].at("cat") }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError when 2 or more arguments are passed" do
+ lambda { [:a, :b].at(0,1) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/array/bsearch_index_spec.rb b/spec/ruby/core/array/bsearch_index_spec.rb
new file mode 100644
index 0000000000..a075d06ed3
--- /dev/null
+++ b/spec/ruby/core/array/bsearch_index_spec.rb
@@ -0,0 +1,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
+ lambda { [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 Bignum#coerce" do
+ [1, 2].should include(@array.bsearch_index { |x| (2**100).coerce((1 - x / 4) * (2**100)).first })
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/array/bsearch_spec.rb b/spec/ruby/core/array/bsearch_spec.rb
new file mode 100644
index 0000000000..57b95fb934
--- /dev/null
+++ b/spec/ruby/core/array/bsearch_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../../spec_helper'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Array#bsearch" do
+ it "returns an Enumerator when not passed a block" do
+ [1].bsearch.should be_an_instance_of(Enumerator)
+ end
+
+ it_behaves_like :enumeratorized_with_unknown_size, :bsearch, [1,2,3]
+
+ it "raises a TypeError if the block returns an Object" do
+ lambda { [1].bsearch { Object.new } }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the block returns a String" do
+ lambda { [1].bsearch { "1" } }.should raise_error(TypeError)
+ end
+
+ context "with a block returning true or false" do
+ it "returns nil if the block returns false for every element" do
+ [0, 1, 2, 3].bsearch { |x| x > 3 }.should be_nil
+ end
+
+ it "returns nil if the block returns nil for every element" do
+ [0, 1, 2, 3].bsearch { |x| nil }.should be_nil
+ end
+
+ it "returns element at zero if the block returns true for every element" do
+ [0, 1, 2, 3].bsearch { |x| x < 4 }.should == 0
+
+ end
+
+ it "returns the element at the smallest index for which block returns true" do
+ [0, 1, 3, 4].bsearch { |x| x >= 2 }.should == 3
+ [0, 1, 3, 4].bsearch { |x| x >= 1 }.should == 1
+ end
+ end
+
+ context "with a block returning negative, zero, positive numbers" do
+ it "returns nil if the block returns less than zero for every element" do
+ [0, 1, 2, 3].bsearch { |x| x <=> 5 }.should be_nil
+ end
+
+ it "returns nil if the block returns greater than zero for every element" do
+ [0, 1, 2, 3].bsearch { |x| x <=> -1 }.should be_nil
+
+ end
+
+ it "returns nil if the block never returns zero" do
+ [0, 1, 3, 4].bsearch { |x| x <=> 2 }.should be_nil
+ end
+
+ it "accepts (+/-)Float::INFINITY from the block" do
+ [0, 1, 3, 4].bsearch { |x| Float::INFINITY }.should be_nil
+ [0, 1, 3, 4].bsearch { |x| -Float::INFINITY }.should be_nil
+ end
+
+ it "returns an element at an index for which block returns 0.0" do
+ result = [0, 1, 2, 3, 4].bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 }
+ result.should == 2
+ end
+
+ it "returns an element at an index for which block returns 0" do
+ result = [0, 1, 2, 3, 4].bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 }
+ [1, 2].should include(result)
+ end
+ end
+
+ context "with a block that calls break" do
+ it "returns nil if break is called without a value" do
+ ['a', 'b', 'c'].bsearch { |v| break }.should be_nil
+ end
+
+ it "returns nil if break is called with a nil value" do
+ ['a', 'b', 'c'].bsearch { |v| break nil }.should be_nil
+ end
+
+ it "returns object if break is called with an object" do
+ ['a', 'b', 'c'].bsearch { |v| break 1234 }.should == 1234
+ ['a', 'b', 'c'].bsearch { |v| break 'hi' }.should == 'hi'
+ ['a', 'b', 'c'].bsearch { |v| break [42] }.should == [42]
+ end
+ end
+end
diff --git a/spec/ruby/core/array/clear_spec.rb b/spec/ruby/core/array/clear_spec.rb
new file mode 100644
index 0000000000..8cba1f9e27
--- /dev/null
+++ b/spec/ruby/core/array/clear_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#clear" do
+ it "removes all elements" do
+ a = [1, 2, 3, 4]
+ a.clear.should equal(a)
+ a.should == []
+ end
+
+ it "returns self" do
+ a = [1]
+ a.should equal a.clear
+ end
+
+ it "leaves the Array empty" do
+ a = [1]
+ a.clear
+ a.empty?.should == true
+ a.size.should == 0
+ end
+
+ it "keeps tainted status" do
+ a = [1]
+ a.taint
+ a.tainted?.should be_true
+ a.clear
+ a.tainted?.should be_true
+ end
+
+ it "does not accept any arguments" do
+ lambda { [1].clear(true) }.should raise_error(ArgumentError)
+ end
+
+ it "keeps untrusted status" do
+ a = [1]
+ a.untrust
+ a.untrusted?.should be_true
+ a.clear
+ a.untrusted?.should be_true
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ a = [1]
+ a.freeze
+ lambda { a.clear }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/array/clone_spec.rb b/spec/ruby/core/array/clone_spec.rb
new file mode 100644
index 0000000000..803e746e02
--- /dev/null
+++ b/spec/ruby/core/array/clone_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/clone'
+
+describe "Array#clone" do
+ it_behaves_like :array_clone, :clone
+
+ it "copies frozen status from the original" do
+ a = [1, 2, 3, 4]
+ b = [1, 2, 3, 4]
+ a.freeze
+ aa = a.clone
+ bb = b.clone
+
+ aa.frozen?.should == true
+ bb.frozen?.should == false
+ end
+
+ it "copies singleton methods" do
+ a = [1, 2, 3, 4]
+ b = [1, 2, 3, 4]
+ def a.a_singleton_method; end
+ aa = a.clone
+ bb = b.clone
+
+ a.respond_to?(:a_singleton_method).should be_true
+ b.respond_to?(:a_singleton_method).should be_false
+ aa.respond_to?(:a_singleton_method).should be_true
+ bb.respond_to?(:a_singleton_method).should be_false
+ end
+end
diff --git a/spec/ruby/core/array/collect_spec.rb b/spec/ruby/core/array/collect_spec.rb
new file mode 100644
index 0000000000..0ad4c283b1
--- /dev/null
+++ b/spec/ruby/core/array/collect_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/collect'
+
+describe "Array#collect" do
+ it_behaves_like :array_collect, :collect
+end
+
+describe "Array#collect!" do
+ it_behaves_like :array_collect_b, :collect!
+end
diff --git a/spec/ruby/core/array/combination_spec.rb b/spec/ruby/core/array/combination_spec.rb
new file mode 100644
index 0000000000..94a8e19341
--- /dev/null
+++ b/spec/ruby/core/array/combination_spec.rb
@@ -0,0 +1,74 @@
+require_relative '../../spec_helper'
+
+describe "Array#combination" do
+ before :each do
+ @array = [1, 2, 3, 4]
+ end
+
+ it "returns an enumerator when no block is provided" do
+ @array.combination(2).should be_an_instance_of(Enumerator)
+ end
+
+ it "returns self when a block is given" do
+ @array.combination(2){}.should equal(@array)
+ end
+
+ it "yields nothing for out of bounds length and return self" do
+ @array.combination(5).to_a.should == []
+ @array.combination(-1).to_a.should == []
+ end
+
+ it "yields the expected combinations" do
+ @array.combination(3).to_a.sort.should == [[1,2,3],[1,2,4],[1,3,4],[2,3,4]]
+ end
+
+ it "yields nothing if the argument is out of bounds" do
+ @array.combination(-1).to_a.should == []
+ @array.combination(5).to_a.should == []
+ end
+
+ it "yields a copy of self if the argument is the size of the receiver" do
+ r = @array.combination(4).to_a
+ r.should == [@array]
+ r[0].should_not equal(@array)
+ end
+
+ it "yields [] when length is 0" do
+ @array.combination(0).to_a.should == [[]] # one combination of length 0
+ [].combination(0).to_a.should == [[]] # one combination of length 0
+ end
+
+ it "yields a partition consisting of only singletons" do
+ @array.combination(1).to_a.sort.should == [[1],[2],[3],[4]]
+ end
+
+ it "generates from a defensive copy, ignoring mutations" do
+ accum = []
+ @array.combination(2) do |x|
+ accum << x
+ @array[0] = 1
+ end
+ accum.should == [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
+ end
+
+ describe "when no block is given" do
+ describe "returned Enumerator" do
+ describe "size" do
+ it "returns 0 when the number of combinations is < 0" do
+ @array.combination(-1).size.should == 0
+ [].combination(-2).size.should == 0
+ end
+ it "returns the binomial coeficient between the array size the number of combinations" do
+ @array.combination(5).size.should == 0
+ @array.combination(4).size.should == 1
+ @array.combination(3).size.should == 4
+ @array.combination(2).size.should == 6
+ @array.combination(1).size.should == 4
+ @array.combination(0).size.should == 1
+ [].combination(0).size.should == 1
+ [].combination(1).size.should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/array/compact_spec.rb b/spec/ruby/core/array/compact_spec.rb
new file mode 100644
index 0000000000..4818217be1
--- /dev/null
+++ b/spec/ruby/core/array/compact_spec.rb
@@ -0,0 +1,77 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#compact" do
+ it "returns a copy of array with all nil elements removed" do
+ a = [1, 2, 4]
+ a.compact.should == [1, 2, 4]
+ a = [1, nil, 2, 4]
+ a.compact.should == [1, 2, 4]
+ a = [1, 2, 4, nil]
+ a.compact.should == [1, 2, 4]
+ a = [nil, 1, 2, 4]
+ a.compact.should == [1, 2, 4]
+ end
+
+ it "does not return self" do
+ a = [1, 2, 3]
+ a.compact.should_not equal(a)
+ end
+
+ it "does not return subclass instance for Array subclasses" do
+ ArraySpecs::MyArray[1, 2, 3, nil].compact.should be_an_instance_of(Array)
+ end
+
+ it "does not keep tainted status even if all elements are removed" do
+ a = [nil, nil]
+ a.taint
+ a.compact.tainted?.should be_false
+ end
+
+ it "does not keep untrusted status even if all elements are removed" do
+ a = [nil, nil]
+ a.untrust
+ a.compact.untrusted?.should be_false
+ end
+end
+
+describe "Array#compact!" do
+ it "removes all nil elements" do
+ a = ['a', nil, 'b', false, 'c']
+ a.compact!.should equal(a)
+ a.should == ["a", "b", false, "c"]
+ a = [nil, 'a', 'b', false, 'c']
+ a.compact!.should equal(a)
+ a.should == ["a", "b", false, "c"]
+ a = ['a', 'b', false, 'c', nil]
+ a.compact!.should equal(a)
+ a.should == ["a", "b", false, "c"]
+ end
+
+ it "returns self if some nil elements are removed" do
+ a = ['a', nil, 'b', false, 'c']
+ a.compact!.should equal a
+ end
+
+ it "returns nil if there are no nil elements to remove" do
+ [1, 2, false, 3].compact!.should == nil
+ end
+
+ it "keeps tainted status even if all elements are removed" do
+ a = [nil, nil]
+ a.taint
+ a.compact!
+ a.tainted?.should be_true
+ end
+
+ it "keeps untrusted status even if all elements are removed" do
+ a = [nil, nil]
+ a.untrust
+ a.compact!
+ a.untrusted?.should be_true
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { ArraySpecs.frozen_array.compact! }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/array/comparison_spec.rb b/spec/ruby/core/array/comparison_spec.rb
new file mode 100644
index 0000000000..5d1c3265f1
--- /dev/null
+++ b/spec/ruby/core/array/comparison_spec.rb
@@ -0,0 +1,97 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#<=>" do
+ it "calls <=> left to right and return first non-0 result" do
+ [-1, +1, nil, "foobar"].each do |result|
+ lhs = Array.new(3) { mock("#{result}") }
+ rhs = Array.new(3) { mock("#{result}") }
+
+ lhs[0].should_receive(:<=>).with(rhs[0]).and_return(0)
+ lhs[1].should_receive(:<=>).with(rhs[1]).and_return(result)
+ lhs[2].should_not_receive(:<=>)
+
+ (lhs <=> rhs).should == result
+ end
+ end
+
+ it "returns 0 if the arrays are equal" do
+ ([] <=> []).should == 0
+ ([1, 2, 3, 4, 5, 6] <=> [1, 2, 3, 4, 5.0, 6.0]).should == 0
+ end
+
+ it "returns -1 if the array is shorter than the other array" do
+ ([] <=> [1]).should == -1
+ ([1, 1] <=> [1, 1, 1]).should == -1
+ end
+
+ it "returns +1 if the array is longer than the other array" do
+ ([1] <=> []).should == +1
+ ([1, 1, 1] <=> [1, 1]).should == +1
+ end
+
+ it "returns -1 if the arrays have same length and a pair of corresponding elements returns -1 for <=>" do
+ eq_l = mock('an object equal to the other')
+ eq_r = mock('an object equal to the other')
+ eq_l.should_receive(:<=>).with(eq_r).any_number_of_times.and_return(0)
+
+ less = mock('less than the other')
+ greater = mock('greater then the other')
+ less.should_receive(:<=>).with(greater).any_number_of_times.and_return(-1)
+
+ rest = mock('an rest element of the arrays')
+ rest.should_receive(:<=>).with(rest).any_number_of_times.and_return(0)
+ lhs = [eq_l, eq_l, less, rest]
+ rhs = [eq_r, eq_r, greater, rest]
+
+ (lhs <=> rhs).should == -1
+ end
+
+ it "returns +1 if the arrays have same length and a pair of corresponding elements returns +1 for <=>" do
+ eq_l = mock('an object equal to the other')
+ eq_r = mock('an object equal to the other')
+ eq_l.should_receive(:<=>).with(eq_r).any_number_of_times.and_return(0)
+
+ greater = mock('greater then the other')
+ less = mock('less than the other')
+ greater.should_receive(:<=>).with(less).any_number_of_times.and_return(+1)
+
+ rest = mock('an rest element of the arrays')
+ rest.should_receive(:<=>).with(rest).any_number_of_times.and_return(0)
+ lhs = [eq_l, eq_l, greater, rest]
+ rhs = [eq_r, eq_r, less, rest]
+
+ (lhs <=> rhs).should == +1
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ (empty <=> empty).should == 0
+ (empty <=> []).should == 1
+ ([] <=> empty).should == -1
+
+ (ArraySpecs.recursive_array <=> []).should == 1
+ ([] <=> ArraySpecs.recursive_array).should == -1
+
+ (ArraySpecs.recursive_array <=> ArraySpecs.empty_recursive_array).should == nil
+
+ array = ArraySpecs.recursive_array
+ (array <=> array).should == 0
+ end
+
+ it "tries to convert the passed argument to an Array using #to_ary" do
+ obj = mock('to_ary')
+ obj.stub!(:to_ary).and_return([1, 2, 3])
+ ([4, 5] <=> obj).should == ([4, 5] <=> obj.to_ary)
+ end
+
+ it "does not call #to_ary on Array subclasses" do
+ obj = ArraySpecs::ToAryArray[5, 6, 7]
+ obj.should_not_receive(:to_ary)
+ ([5, 6, 7] <=> obj).should == 0
+ end
+
+ it "returns nil when the argument is not array-like" do
+ ([] <=> false).should be_nil
+ end
+end
diff --git a/spec/ruby/core/array/concat_spec.rb b/spec/ruby/core/array/concat_spec.rb
new file mode 100644
index 0000000000..985c5d884a
--- /dev/null
+++ b/spec/ruby/core/array/concat_spec.rb
@@ -0,0 +1,132 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#concat" do
+ it "returns the array itself" do
+ ary = [1,2,3]
+ ary.concat([4,5,6]).equal?(ary).should be_true
+ end
+
+ it "appends the elements in the other array" do
+ ary = [1, 2, 3]
+ ary.concat([9, 10, 11]).should equal(ary)
+ ary.should == [1, 2, 3, 9, 10, 11]
+ ary.concat([])
+ ary.should == [1, 2, 3, 9, 10, 11]
+ end
+
+ it "does not loop endlessly when argument is self" do
+ ary = ["x", "y"]
+ ary.concat(ary).should == ["x", "y", "x", "y"]
+ end
+
+ it "tries to convert the passed argument to an Array using #to_ary" do
+ obj = mock('to_ary')
+ obj.should_receive(:to_ary).and_return(["x", "y"])
+ [4, 5, 6].concat(obj).should == [4, 5, 6, "x", "y"]
+ end
+
+ it "does not call #to_ary on Array subclasses" do
+ obj = ArraySpecs::ToAryArray[5, 6, 7]
+ obj.should_not_receive(:to_ary)
+ [].concat(obj).should == [5, 6, 7]
+ end
+
+ it "raises a #{frozen_error_class} when Array is frozen and modification occurs" do
+ lambda { ArraySpecs.frozen_array.concat [1] }.should raise_error(frozen_error_class)
+ end
+
+ # see [ruby-core:23666]
+ it "raises a #{frozen_error_class} when Array is frozen and no modification occurs" do
+ lambda { ArraySpecs.frozen_array.concat([]) }.should raise_error(frozen_error_class)
+ end
+
+ it "keeps tainted status" do
+ ary = [1, 2]
+ ary.taint
+ ary.concat([3])
+ ary.tainted?.should be_true
+ ary.concat([])
+ ary.tainted?.should be_true
+ end
+
+ it "is not infected by the other" do
+ ary = [1,2]
+ other = [3]; other.taint
+ ary.tainted?.should be_false
+ ary.concat(other)
+ ary.tainted?.should be_false
+ end
+
+ it "keeps the tainted status of elements" do
+ ary = [ Object.new, Object.new, Object.new ]
+ ary.each {|x| x.taint }
+
+ ary.concat([ Object.new ])
+ ary[0].tainted?.should be_true
+ ary[1].tainted?.should be_true
+ ary[2].tainted?.should be_true
+ ary[3].tainted?.should be_false
+ end
+
+ it "keeps untrusted status" do
+ ary = [1, 2]
+ ary.untrust
+ ary.concat([3])
+ ary.untrusted?.should be_true
+ ary.concat([])
+ ary.untrusted?.should be_true
+ end
+
+ it "is not infected untrustedness by the other" do
+ ary = [1,2]
+ other = [3]; other.untrust
+ ary.untrusted?.should be_false
+ ary.concat(other)
+ ary.untrusted?.should be_false
+ end
+
+ it "keeps the untrusted status of elements" do
+ ary = [ Object.new, Object.new, Object.new ]
+ ary.each {|x| x.untrust }
+
+ ary.concat([ Object.new ])
+ ary[0].untrusted?.should be_true
+ ary[1].untrusted?.should be_true
+ ary[2].untrusted?.should be_true
+ ary[3].untrusted?.should be_false
+ end
+
+ it "appends elements to an Array with enough capacity that has been shifted" do
+ ary = [1, 2, 3, 4, 5]
+ 2.times { ary.shift }
+ 2.times { ary.pop }
+ ary.concat([5, 6]).should == [3, 5, 6]
+ end
+
+ it "appends elements to an Array without enough capacity that has been shifted" do
+ ary = [1, 2, 3, 4]
+ 3.times { ary.shift }
+ ary.concat([5, 6]).should == [4, 5, 6]
+ end
+
+ ruby_version_is "2.4" do
+ it "takes multiple arguments" do
+ ary = [1, 2]
+ ary.concat [3, 4]
+ ary.should == [1, 2, 3, 4]
+ end
+
+ it "concatenates the initial value when given arguments contain 2 self" do
+ ary = [1, 2]
+ ary.concat ary, ary
+ ary.should == [1, 2, 1, 2, 1, 2]
+ end
+
+ it "returns self when given no arguments" do
+ ary = [1, 2]
+ ary.concat.should equal(ary)
+ ary.should == [1, 2]
+ end
+ end
+end
diff --git a/spec/ruby/core/array/constructor_spec.rb b/spec/ruby/core/array/constructor_spec.rb
new file mode 100644
index 0000000000..6f36074c45
--- /dev/null
+++ b/spec/ruby/core/array/constructor_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array.[]" do
+ it "returns a new array populated with the given elements" do
+ obj = Object.new
+ Array.[](5, true, nil, 'a', "Ruby", obj).should == [5, true, nil, "a", "Ruby", obj]
+
+ a = ArraySpecs::MyArray.[](5, true, nil, 'a', "Ruby", obj)
+ a.should be_an_instance_of(ArraySpecs::MyArray)
+ a.inspect.should == [5, true, nil, "a", "Ruby", obj].inspect
+ end
+end
+
+describe "Array[]" do
+ it "is a synonym for .[]" do
+ obj = Object.new
+ Array[5, true, nil, 'a', "Ruby", obj].should == Array.[](5, true, nil, "a", "Ruby", obj)
+
+ a = ArraySpecs::MyArray[5, true, nil, 'a', "Ruby", obj]
+ a.should be_an_instance_of(ArraySpecs::MyArray)
+ a.inspect.should == [5, true, nil, "a", "Ruby", obj].inspect
+ end
+end
diff --git a/spec/ruby/core/array/count_spec.rb b/spec/ruby/core/array/count_spec.rb
new file mode 100644
index 0000000000..eaf275aeb7
--- /dev/null
+++ b/spec/ruby/core/array/count_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "Array#count" do
+ it "returns the number of elements" do
+ [:a, :b, :c].count.should == 3
+ end
+
+ it "returns the number of elements that equal the argument" do
+ [:a, :b, :b, :c].count(:b).should == 2
+ end
+
+ it "returns the number of element for which the block evaluates to true" do
+ [:a, :b, :c].count { |s| s != :b }.should == 2
+ end
+end
diff --git a/spec/ruby/core/array/cycle_spec.rb b/spec/ruby/core/array/cycle_spec.rb
new file mode 100644
index 0000000000..018005abb4
--- /dev/null
+++ b/spec/ruby/core/array/cycle_spec.rb
@@ -0,0 +1,101 @@
+require_relative '../../spec_helper'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Array#cycle" do
+ before :each do
+ ScratchPad.record []
+
+ @array = [1, 2, 3]
+ @prc = lambda { |x| ScratchPad << x }
+ end
+
+ it "does not yield and returns nil when the array is empty and passed value is an integer" do
+ [].cycle(6, &@prc).should be_nil
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not yield and returns nil when the array is empty and passed value is nil" do
+ [].cycle(nil, &@prc).should be_nil
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not yield and returns nil when passed 0" do
+ @array.cycle(0, &@prc).should be_nil
+ ScratchPad.recorded.should == []
+ end
+
+ it "iterates the array 'count' times yielding each item to the block" do
+ @array.cycle(2, &@prc)
+ ScratchPad.recorded.should == [1, 2, 3, 1, 2, 3]
+ end
+
+ it "iterates indefinitely when not passed a count" do
+ @array.cycle do |x|
+ ScratchPad << x
+ break if ScratchPad.recorded.size > 7
+ end
+ ScratchPad.recorded.should == [1, 2, 3, 1, 2, 3, 1, 2]
+ end
+
+ it "iterates indefinitely when passed nil" do
+ @array.cycle(nil) do |x|
+ ScratchPad << x
+ break if ScratchPad.recorded.size > 7
+ end
+ ScratchPad.recorded.should == [1, 2, 3, 1, 2, 3, 1, 2]
+ end
+
+ it "does not rescue StopIteration when not passed a count" do
+ lambda do
+ @array.cycle { raise StopIteration }
+ end.should raise_error(StopIteration)
+ end
+
+ it "does not rescue StopIteration when passed a count" do
+ lambda do
+ @array.cycle(3) { raise StopIteration }
+ end.should raise_error(StopIteration)
+ end
+
+ it "iterates the array Integer(count) times when passed a Float count" do
+ @array.cycle(2.7, &@prc)
+ ScratchPad.recorded.should == [1, 2, 3, 1, 2, 3]
+ end
+
+ it "calls #to_int to convert count to an Integer" do
+ count = mock("cycle count 2")
+ count.should_receive(:to_int).and_return(2)
+
+ @array.cycle(count, &@prc)
+ ScratchPad.recorded.should == [1, 2, 3, 1, 2, 3]
+ end
+
+ it "raises a TypeError if #to_int does not return an Integer" do
+ count = mock("cycle count 2")
+ count.should_receive(:to_int).and_return("2")
+
+ lambda { @array.cycle(count, &@prc) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed a String" do
+ lambda { @array.cycle("4") { } }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an Object" do
+ lambda { @array.cycle(mock("cycle count")) { } }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed true" do
+ lambda { @array.cycle(true) { } }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed false" do
+ lambda { @array.cycle(false) { } }.should raise_error(TypeError)
+ end
+
+ before :all do
+ @object = [1, 2, 3, 4]
+ @empty_object = []
+ end
+ it_should_behave_like :enumeratorized_with_cycle_size
+end
diff --git a/spec/ruby/core/array/delete_at_spec.rb b/spec/ruby/core/array/delete_at_spec.rb
new file mode 100644
index 0000000000..021554c76a
--- /dev/null
+++ b/spec/ruby/core/array/delete_at_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#delete_at" do
+ it "removes the element at the specified index" do
+ a = [1, 2, 3, 4]
+ a.delete_at(2)
+ a.should == [1, 2, 4]
+ a.delete_at(-1)
+ a.should == [1, 2]
+ end
+
+ it "returns the removed element at the specified index" do
+ a = [1, 2, 3, 4]
+ a.delete_at(2).should == 3
+ a.delete_at(-1).should == 4
+ end
+
+ it "returns nil and makes no modification if the index is out of range" do
+ a = [1, 2]
+ a.delete_at(3).should == nil
+ a.should == [1, 2]
+ a.delete_at(-3).should == nil
+ a.should == [1, 2]
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(-1)
+ [1, 2].delete_at(obj).should == 2
+ end
+
+ it "accepts negative indices" do
+ a = [1, 2]
+ a.delete_at(-2).should == 1
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { [1,2,3].freeze.delete_at(0) }.should raise_error(frozen_error_class)
+ end
+
+ it "keeps tainted status" do
+ ary = [1, 2]
+ ary.taint
+ ary.tainted?.should be_true
+ ary.delete_at(0)
+ ary.tainted?.should be_true
+ ary.delete_at(0) # now empty
+ ary.tainted?.should be_true
+ end
+
+ it "keeps untrusted status" do
+ ary = [1, 2]
+ ary.untrust
+ ary.untrusted?.should be_true
+ ary.delete_at(0)
+ ary.untrusted?.should be_true
+ ary.delete_at(0) # now empty
+ ary.untrusted?.should be_true
+ end
+end
diff --git a/spec/ruby/core/array/delete_if_spec.rb b/spec/ruby/core/array/delete_if_spec.rb
new file mode 100644
index 0000000000..12a7d1662d
--- /dev/null
+++ b/spec/ruby/core/array/delete_if_spec.rb
@@ -0,0 +1,66 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumeratorize'
+require_relative 'shared/delete_if'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Array#delete_if" do
+ before do
+ @a = [ "a", "b", "c" ]
+ end
+
+ it "removes each element for which block returns true" do
+ @a = [ "a", "b", "c" ]
+ @a.delete_if { |x| x >= "b" }
+ @a.should == ["a"]
+ end
+
+ it "returns self" do
+ @a.delete_if{ true }.equal?(@a).should be_true
+ end
+
+ it_behaves_like :enumeratorize, :delete_if
+
+ it "returns self when called on an Array emptied with #shift" do
+ array = [1]
+ array.shift
+ array.delete_if { |x| true }.should equal(array)
+ end
+
+ it "returns an Enumerator if no block given, and the enumerator can modify the original array" do
+ enum = @a.delete_if
+ enum.should be_an_instance_of(Enumerator)
+ @a.should_not be_empty
+ enum.each { true }
+ @a.should be_empty
+ end
+
+ it "returns an Enumerator if no block given, and the array is frozen" do
+ @a.freeze.delete_if.should be_an_instance_of(Enumerator)
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { ArraySpecs.frozen_array.delete_if {} }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} on an empty frozen array" do
+ lambda { ArraySpecs.empty_frozen_array.delete_if {} }.should raise_error(frozen_error_class)
+ end
+
+ it "keeps tainted status" do
+ @a.taint
+ @a.tainted?.should be_true
+ @a.delete_if{ true }
+ @a.tainted?.should be_true
+ end
+
+ it "keeps untrusted status" do
+ @a.untrust
+ @a.untrusted?.should be_true
+ @a.delete_if{ true }
+ @a.untrusted?.should be_true
+ end
+
+ it_behaves_like :enumeratorized_with_origin_size, :delete_if, [1,2,3]
+ it_behaves_like :delete_if, :delete_if
+end
diff --git a/spec/ruby/core/array/delete_spec.rb b/spec/ruby/core/array/delete_spec.rb
new file mode 100644
index 0000000000..62476f489b
--- /dev/null
+++ b/spec/ruby/core/array/delete_spec.rb
@@ -0,0 +1,66 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#delete" do
+ it "removes elements that are #== to object" do
+ x = mock('delete')
+ def x.==(other) 3 == other end
+
+ a = [1, 2, 3, x, 4, 3, 5, x]
+ a.delete mock('not contained')
+ a.should == [1, 2, 3, x, 4, 3, 5, x]
+
+ a.delete 3
+ a.should == [1, 2, 4, 5]
+ end
+
+ it "calculates equality correctly for reference values" do
+ a = ["foo", "bar", "foo", "quux", "foo"]
+ a.delete "foo"
+ a.should == ["bar","quux"]
+ end
+
+ it "returns object or nil if no elements match object" do
+ [1, 2, 4, 5].delete(1).should == 1
+ [1, 2, 4, 5].delete(3).should == nil
+ end
+
+ it "may be given a block that is executed if no element matches object" do
+ [1].delete(1) {:not_found}.should == 1
+ [].delete('a') {:not_found}.should == :not_found
+ end
+
+ it "returns nil if the array is empty due to a shift" do
+ a = [1]
+ a.shift
+ a.delete(nil).should == nil
+ end
+
+ it "returns nil on a frozen array if a modification does not take place" do
+ [1, 2, 3].freeze.delete(0).should == nil
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { [1, 2, 3].freeze.delete(1) }.should raise_error(frozen_error_class)
+ end
+
+ it "keeps tainted status" do
+ a = [1, 2]
+ a.taint
+ a.tainted?.should be_true
+ a.delete(2)
+ a.tainted?.should be_true
+ a.delete(1) # now empty
+ a.tainted?.should be_true
+ end
+
+ it "keeps untrusted status" do
+ a = [1, 2]
+ a.untrust
+ a.untrusted?.should be_true
+ a.delete(2)
+ a.untrusted?.should be_true
+ a.delete(1) # now empty
+ a.untrusted?.should be_true
+ end
+end
diff --git a/spec/ruby/core/array/dig_spec.rb b/spec/ruby/core/array/dig_spec.rb
new file mode 100644
index 0000000000..1ace4893ee
--- /dev/null
+++ b/spec/ruby/core/array/dig_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../spec_helper'
+
+describe "Array#dig" do
+
+ it "returns #at with one arg" do
+ ['a'].dig(0).should == 'a'
+ ['a'].dig(1).should be_nil
+ end
+
+ it "recurses array elements" do
+ a = [ [ 1, [2, '3'] ] ]
+ a.dig(0, 0).should == 1
+ a.dig(0, 1, 1).should == '3'
+ a.dig(0, -1, 0).should == 2
+ end
+
+ it "returns the nested value specified if the sequence includes a key" do
+ a = [42, { foo: :bar }]
+ a.dig(1, :foo).should == :bar
+ end
+
+ it "raises a TypeError for a non-numeric index" do
+ lambda {
+ ['a'].dig(:first)
+ }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if any intermediate step does not respond to #dig" do
+ a = [1, 2]
+ lambda {
+ a.dig(0, 1)
+ }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError if no arguments provided" do
+ lambda {
+ [10].dig()
+ }.should raise_error(ArgumentError)
+ end
+
+ it "returns nil if any intermediate step is nil" do
+ a = [[1, [2, 3]]]
+ a.dig(1, 2, 3).should == nil
+ end
+
+ it "calls #dig on the result of #at with the remaining arguments" do
+ h = [[nil, [nil, nil, 42]]]
+ h[0].should_receive(:dig).with(1, 2).and_return(42)
+ h.dig(0, 1, 2).should == 42
+ end
+
+end
diff --git a/spec/ruby/core/array/drop_spec.rb b/spec/ruby/core/array/drop_spec.rb
new file mode 100644
index 0000000000..1bd2e6cc25
--- /dev/null
+++ b/spec/ruby/core/array/drop_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+
+describe "Array#drop" do
+ it "removes the specified number of elements from the start of the array" do
+ [1, 2, 3, 4, 5].drop(2).should == [3, 4, 5]
+ end
+
+ it "raises an ArgumentError if the number of elements specified is negative" do
+ lambda { [1, 2].drop(-3) }.should raise_error(ArgumentError)
+ end
+
+ it "returns an empty Array if all elements are dropped" do
+ [1, 2].drop(2).should == []
+ end
+
+ it "returns an empty Array when called on an empty Array" do
+ [].drop(0).should == []
+ end
+
+ it "does not remove any elements when passed zero" do
+ [1, 2].drop(0).should == [1, 2]
+ end
+
+ it "returns an empty Array if more elements than exist are dropped" do
+ [1, 2].drop(3).should == []
+ end
+
+ it 'acts correctly after a shift' do
+ ary = [nil, 1, 2]
+ ary.shift
+ ary.drop(1).should == [2]
+ end
+end
diff --git a/spec/ruby/core/array/drop_while_spec.rb b/spec/ruby/core/array/drop_while_spec.rb
new file mode 100644
index 0000000000..cfb6b1e267
--- /dev/null
+++ b/spec/ruby/core/array/drop_while_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "Array#drop_while" do
+ it "removes elements from the start of the array while the block evaluates to true" do
+ [1, 2, 3, 4].drop_while { |n| n < 4 }.should == [4]
+ end
+
+ it "removes elements from the start of the array until the block returns nil" do
+ [1, 2, 3, nil, 5].drop_while { |n| n }.should == [nil, 5]
+ end
+
+ it "removes elements from the start of the array until the block returns false" do
+ [1, 2, 3, false, 5].drop_while { |n| n }.should == [false, 5]
+ end
+end
diff --git a/spec/ruby/core/array/dup_spec.rb b/spec/ruby/core/array/dup_spec.rb
new file mode 100644
index 0000000000..17f467d5fc
--- /dev/null
+++ b/spec/ruby/core/array/dup_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/clone'
+
+describe "Array#dup" do
+ it_behaves_like :array_clone, :dup # FIX: no, clone and dup are not alike
+
+ it "does not copy frozen status from the original" do
+ a = [1, 2, 3, 4]
+ b = [1, 2, 3, 4]
+ a.freeze
+ aa = a.dup
+ bb = b.dup
+
+ aa.frozen?.should be_false
+ bb.frozen?.should be_false
+ end
+
+ it "does not copy singleton methods" do
+ a = [1, 2, 3, 4]
+ b = [1, 2, 3, 4]
+ def a.a_singleton_method; end
+ aa = a.dup
+ bb = b.dup
+
+ a.respond_to?(:a_singleton_method).should be_true
+ b.respond_to?(:a_singleton_method).should be_false
+ aa.respond_to?(:a_singleton_method).should be_false
+ bb.respond_to?(:a_singleton_method).should be_false
+ end
+end
diff --git a/spec/ruby/core/array/each_index_spec.rb b/spec/ruby/core/array/each_index_spec.rb
new file mode 100644
index 0000000000..51af5842c4
--- /dev/null
+++ b/spec/ruby/core/array/each_index_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumeratorize'
+require_relative '../enumerable/shared/enumeratorized'
+
+# Modifying a collection while the contents are being iterated
+# gives undefined behavior. See
+# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633
+
+describe "Array#each_index" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "passes the index of each element to the block" do
+ a = ['a', 'b', 'c', 'd']
+ a.each_index { |i| ScratchPad << i }
+ ScratchPad.recorded.should == [0, 1, 2, 3]
+ end
+
+ it "returns self" do
+ a = [:a, :b, :c]
+ a.each_index { |i| }.should equal(a)
+ end
+
+ it "is not confused by removing elements from the front" do
+ a = [1, 2, 3]
+
+ a.shift
+ ScratchPad.record []
+ a.each_index { |i| ScratchPad << i }
+ ScratchPad.recorded.should == [0, 1]
+
+ a.shift
+ ScratchPad.record []
+ a.each_index { |i| ScratchPad << i }
+ ScratchPad.recorded.should == [0]
+ end
+
+ it_behaves_like :enumeratorize, :each_index
+ it_behaves_like :enumeratorized_with_origin_size, :each_index, [1,2,3]
+end
diff --git a/spec/ruby/core/array/each_spec.rb b/spec/ruby/core/array/each_spec.rb
new file mode 100644
index 0000000000..ad8a5ad3d5
--- /dev/null
+++ b/spec/ruby/core/array/each_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumeratorize'
+require_relative '../enumerable/shared/enumeratorized'
+
+# Modifying a collection while the contents are being iterated
+# gives undefined behavior. See
+# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633
+
+describe "Array#each" do
+ it "yields each element to the block" do
+ a = []
+ x = [1, 2, 3]
+ x.each { |item| a << item }.should equal(x)
+ a.should == [1, 2, 3]
+ end
+
+ it "yields each element to a block that takes multiple arguments" do
+ a = [[1, 2], :a, [3, 4]]
+ b = []
+
+ a.each { |x, y| b << x }
+ b.should == [1, :a, 3]
+
+ b = []
+ a.each { |x, y| b << y }
+ b.should == [2, nil, 4]
+ end
+
+ it_behaves_like :enumeratorize, :each
+ it_behaves_like :enumeratorized_with_origin_size, :each, [1,2,3]
+end
diff --git a/spec/ruby/core/array/element_reference_spec.rb b/spec/ruby/core/array/element_reference_spec.rb
new file mode 100644
index 0000000000..31e5578a09
--- /dev/null
+++ b/spec/ruby/core/array/element_reference_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/slice'
+
+describe "Array#[]" do
+ it_behaves_like :array_slice, :[]
+end
+
+describe "Array.[]" do
+ it "[] should return a new array populated with the given elements" do
+ array = Array[1, 'a', nil]
+ array[0].should == 1
+ array[1].should == 'a'
+ array[2].should == nil
+ end
+
+ it "when applied to a literal nested array, unpacks its elements into the containing array" do
+ Array[1, 2, *[3, 4, 5]].should == [1, 2, 3, 4, 5]
+ end
+
+ it "when applied to a nested referenced array, unpacks its elements into the containing array" do
+ splatted_array = Array[3, 4, 5]
+ Array[1, 2, *splatted_array].should == [1, 2, 3, 4, 5]
+ end
+
+ it "can unpack 2 or more nested referenced array" do
+ splatted_array = Array[3, 4, 5]
+ splatted_array2 = Array[6, 7, 8]
+ Array[1, 2, *splatted_array, *splatted_array2].should == [1, 2, 3, 4, 5, 6, 7, 8]
+ end
+
+ it "constructs a nested Hash for tailing key-value pairs" do
+ Array[1, 2, 3 => 4, 5 => 6].should == [1, 2, { 3 => 4, 5 => 6 }]
+ end
+
+ describe "with a subclass of Array" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "returns an instance of the subclass" do
+ ArraySpecs::MyArray[1, 2, 3].should be_an_instance_of(ArraySpecs::MyArray)
+ end
+
+ it "does not call #initialize on the subclass instance" do
+ ArraySpecs::MyArray[1, 2, 3].should == [1, 2, 3]
+ ScratchPad.recorded.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/array/element_set_spec.rb b/spec/ruby/core/array/element_set_spec.rb
new file mode 100644
index 0000000000..9992e4d32b
--- /dev/null
+++ b/spec/ruby/core/array/element_set_spec.rb
@@ -0,0 +1,436 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#[]=" do
+ it "sets the value of the element at index" do
+ a = [1, 2, 3, 4]
+ a[2] = 5
+ a[-1] = 6
+ a[5] = 3
+ a.should == [1, 2, 5, 6, nil, 3]
+
+ a = []
+ a[4] = "e"
+ a.should == [nil, nil, nil, nil, "e"]
+ a[3] = "d"
+ a.should == [nil, nil, nil, "d", "e"]
+ a[0] = "a"
+ a.should == ["a", nil, nil, "d", "e"]
+ a[-3] = "C"
+ a.should == ["a", nil, "C", "d", "e"]
+ a[-1] = "E"
+ a.should == ["a", nil, "C", "d", "E"]
+ a[-5] = "A"
+ a.should == ["A", nil, "C", "d", "E"]
+ a[5] = "f"
+ a.should == ["A", nil, "C", "d", "E", "f"]
+ a[1] = []
+ a.should == ["A", [], "C", "d", "E", "f"]
+ a[-1] = nil
+ a.should == ["A", [], "C", "d", "E", nil]
+ end
+
+ it "sets the section defined by [start,length] to other" do
+ a = [1, 2, 3, 4, 5, 6]
+ a[0, 1] = 2
+ a[3, 2] = ['a', 'b', 'c', 'd']
+ a.should == [2, 2, 3, "a", "b", "c", "d", 6]
+ end
+ it "replaces the section defined by [start,length] with the given values" do
+ a = [1, 2, 3, 4, 5, 6]
+ a[3, 2] = 'a', 'b', 'c', 'd'
+ a.should == [1, 2, 3, "a", "b", "c", "d", 6]
+ end
+
+ it "just sets the section defined by [start,length] to other even if other is nil" do
+ a = ['a', 'b', 'c', 'd', 'e']
+ a[1, 3] = nil
+ a.should == ["a", nil, "e"]
+ end
+
+ it "returns nil if the rhs is nil" do
+ a = [1, 2, 3]
+ (a[1, 3] = nil).should == nil
+ (a[1..3] = nil).should == nil
+ end
+
+ it "sets the section defined by range to other" do
+ a = [6, 5, 4, 3, 2, 1]
+ a[1...2] = 9
+ a[3..6] = [6, 6, 6]
+ a.should == [6, 9, 4, 6, 6, 6]
+ end
+
+ it "replaces the section defined by range with the given values" do
+ a = [6, 5, 4, 3, 2, 1]
+ a[3..6] = :a, :b, :c
+ a.should == [6, 5, 4, :a, :b, :c]
+ end
+
+ it "just sets the section defined by range to other even if other is nil" do
+ a = [1, 2, 3, 4, 5]
+ a[0..1] = nil
+ a.should == [nil, 3, 4, 5]
+ end
+
+ it 'expands and nil-pads the array if section assigned by range is outside array boundaries' do
+ a = ['a']
+ a[3..4] = ['b', 'c']
+ a.should == ['a', nil, nil, 'b', 'c']
+ end
+
+ it "calls to_int on its start and length arguments" do
+ obj = mock('to_int')
+ obj.stub!(:to_int).and_return(2)
+
+ a = [1, 2, 3, 4]
+ a[obj, 0] = [9]
+ a.should == [1, 2, 9, 3, 4]
+ a[obj, obj] = []
+ a.should == [1, 2, 4]
+ a[obj] = -1
+ a.should == [1, 2, -1]
+ end
+
+ it "checks frozen before attempting to coerce arguments" do
+ a = [1,2,3,4].freeze
+ lambda {a[:foo] = 1}.should raise_error(frozen_error_class)
+ lambda {a[:foo, :bar] = 1}.should raise_error(frozen_error_class)
+ end
+
+ it "sets elements in the range arguments when passed ranges" do
+ ary = [1, 2, 3]
+ rhs = [nil, [], ["x"], ["x", "y"]]
+ (0 .. ary.size + 2).each do |a|
+ (a .. ary.size + 3).each do |b|
+ rhs.each do |c|
+ ary1 = ary.dup
+ ary1[a .. b] = c
+ ary2 = ary.dup
+ ary2[a, 1 + b-a] = c
+ ary1.should == ary2
+
+ ary1 = ary.dup
+ ary1[a ... b] = c
+ ary2 = ary.dup
+ ary2[a, b-a] = c
+ ary1.should == ary2
+ end
+ end
+ end
+ end
+
+ it "inserts the given elements with [range] which the range is zero-width" do
+ ary = [1, 2, 3]
+ ary[1...1] = 0
+ ary.should == [1, 0, 2, 3]
+ ary[1...1] = [5]
+ ary.should == [1, 5, 0, 2, 3]
+ ary[1...1] = :a, :b, :c
+ ary.should == [1, :a, :b, :c, 5, 0, 2, 3]
+ end
+
+ it "inserts the given elements with [start, length] which length is zero" do
+ ary = [1, 2, 3]
+ ary[1, 0] = 0
+ ary.should == [1, 0, 2, 3]
+ ary[1, 0] = [5]
+ ary.should == [1, 5, 0, 2, 3]
+ ary[1, 0] = :a, :b, :c
+ ary.should == [1, :a, :b, :c, 5, 0, 2, 3]
+ end
+
+ # Now we only have to test cases where the start, length interface would
+ # have raise an exception because of negative size
+ it "inserts the given elements with [range] which the range has negative width" do
+ ary = [1, 2, 3]
+ ary[1..0] = 0
+ ary.should == [1, 0, 2, 3]
+ ary[1..0] = [4, 3]
+ ary.should == [1, 4, 3, 0, 2, 3]
+ ary[1..0] = :a, :b, :c
+ ary.should == [1, :a, :b, :c, 4, 3, 0, 2, 3]
+ end
+
+ it "just inserts nil if the section defined by range is zero-width and the rhs is nil" do
+ ary = [1, 2, 3]
+ ary[1...1] = nil
+ ary.should == [1, nil, 2, 3]
+ end
+
+ it "just inserts nil if the section defined by range has negative width and the rhs is nil" do
+ ary = [1, 2, 3]
+ ary[1..0] = nil
+ ary.should == [1, nil, 2, 3]
+ end
+
+ it "does nothing if the section defined by range is zero-width and the rhs is an empty array" do
+ ary = [1, 2, 3]
+ ary[1...1] = []
+ ary.should == [1, 2, 3]
+ end
+ it "does nothing if the section defined by range has negative width and the rhs is an empty array" do
+ ary = [1, 2, 3, 4, 5]
+ ary[1...0] = []
+ ary.should == [1, 2, 3, 4, 5]
+ ary[-2..2] = []
+ ary.should == [1, 2, 3, 4, 5]
+ end
+
+ it "tries to convert Range elements to Integers using #to_int with [m..n] and [m...n]" do
+ from = mock('from')
+ to = mock('to')
+
+ # So we can construct a range out of them...
+ def from.<=>(o) 0 end
+ def to.<=>(o) 0 end
+
+ def from.to_int() 1 end
+ def to.to_int() -2 end
+
+ a = [1, 2, 3, 4]
+
+ a[from .. to] = ["a", "b", "c"]
+ a.should == [1, "a", "b", "c", 4]
+
+ a[to .. from] = ["x"]
+ a.should == [1, "a", "b", "x", "c", 4]
+ lambda { a["a" .. "b"] = [] }.should raise_error(TypeError)
+ lambda { a[from .. "b"] = [] }.should raise_error(TypeError)
+ end
+
+ it "raises an IndexError when passed indexes out of bounds" do
+ a = [1, 2, 3, 4]
+ lambda { a[-5] = "" }.should raise_error(IndexError)
+ lambda { a[-5, -1] = "" }.should raise_error(IndexError)
+ lambda { a[-5, 0] = "" }.should raise_error(IndexError)
+ lambda { a[-5, 1] = "" }.should raise_error(IndexError)
+ lambda { a[-5, 2] = "" }.should raise_error(IndexError)
+ lambda { a[-5, 10] = "" }.should raise_error(IndexError)
+
+ lambda { a[-5..-5] = "" }.should raise_error(RangeError)
+ lambda { a[-5...-5] = "" }.should raise_error(RangeError)
+ lambda { a[-5..-4] = "" }.should raise_error(RangeError)
+ lambda { a[-5...-4] = "" }.should raise_error(RangeError)
+ lambda { a[-5..10] = "" }.should raise_error(RangeError)
+ lambda { a[-5...10] = "" }.should raise_error(RangeError)
+
+ # ok
+ a[0..-9] = [1]
+ a.should == [1, 1, 2, 3, 4]
+ end
+
+ it "calls to_ary on its rhs argument for multi-element sets" do
+ obj = mock('to_ary')
+ def obj.to_ary() [1, 2, 3] end
+ ary = [1, 2]
+ ary[0, 0] = obj
+ ary.should == [1, 2, 3, 1, 2]
+ ary[1, 10] = obj
+ ary.should == [1, 1, 2, 3]
+ end
+
+ it "does not call to_ary on rhs array subclasses for multi-element sets" do
+ ary = []
+ ary[0, 0] = ArraySpecs::ToAryArray[5, 6, 7]
+ ary.should == [5, 6, 7]
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { ArraySpecs.frozen_array[0, 0] = [] }.should raise_error(frozen_error_class)
+ end
+end
+
+describe "Array#[]= with [index]" do
+ it "returns value assigned if idx is inside array" do
+ a = [1, 2, 3, 4, 5]
+ (a[3] = 6).should == 6
+ end
+
+ it "returns value assigned if idx is right beyond right array boundary" do
+ a = [1, 2, 3, 4, 5]
+ (a[5] = 6).should == 6
+ end
+
+ it "returns value assigned if idx far beyond right array boundary" do
+ a = [1, 2, 3, 4, 5]
+ (a[10] = 6).should == 6
+ end
+
+ it "sets the value of the element at index" do
+ a = [1, 2, 3, 4]
+ a[2] = 5
+ a[-1] = 6
+ a[5] = 3
+ a.should == [1, 2, 5, 6, nil, 3]
+ end
+
+ it "sets the value of the element if it is right beyond the array boundary" do
+ a = [1, 2, 3, 4]
+ a[4] = 8
+ a.should == [1, 2, 3, 4, 8]
+ end
+
+end
+
+describe "Array#[]= with [index, count]" do
+ it "returns non-array value if non-array value assigned" do
+ a = [1, 2, 3, 4, 5]
+ (a[2, 3] = 10).should == 10
+ end
+
+ it "returns array if array assigned" do
+ a = [1, 2, 3, 4, 5]
+ (a[2, 3] = [4, 5]).should == [4, 5]
+ end
+
+ it "just sets the section defined by [start,length] to nil even if the rhs is nil" do
+ a = ['a', 'b', 'c', 'd', 'e']
+ a[1, 3] = nil
+ a.should == ["a", nil, "e"]
+ end
+
+ it "just sets the section defined by [start,length] to nil if negative index within bounds, cnt > 0 and the rhs is nil" do
+ a = ['a', 'b', 'c', 'd', 'e']
+ a[-3, 2] = nil
+ a.should == ["a", "b", nil, "e"]
+ end
+
+ it "replaces the section defined by [start,length] to other" do
+ a = [1, 2, 3, 4, 5, 6]
+ a[0, 1] = 2
+ a[3, 2] = ['a', 'b', 'c', 'd']
+ a.should == [2, 2, 3, "a", "b", "c", "d", 6]
+ end
+
+ it "replaces the section to other if idx < 0 and cnt > 0" do
+ a = [1, 2, 3, 4, 5, 6]
+ a[-3, 2] = ["x", "y", "z"]
+ a.should == [1, 2, 3, "x", "y", "z", 6]
+ end
+
+ it "replaces the section to other even if cnt spanning beyond the array boundary" do
+ a = [1, 2, 3, 4, 5]
+ a[-1, 3] = [7, 8]
+ a.should == [1, 2, 3, 4, 7, 8]
+ end
+
+ it "pads the Array with nils if the span is past the end" do
+ a = [1, 2, 3, 4, 5]
+ a[10, 1] = [1]
+ a.should == [1, 2, 3, 4, 5, nil, nil, nil, nil, nil, 1]
+
+ b = [1, 2, 3, 4, 5]
+ b[10, 0] = [1]
+ a.should == [1, 2, 3, 4, 5, nil, nil, nil, nil, nil, 1]
+ end
+
+ it "inserts other section in place defined by idx" do
+ a = [1, 2, 3, 4, 5]
+ a[3, 0] = [7, 8]
+ a.should == [1, 2, 3, 7, 8, 4, 5]
+
+ b = [1, 2, 3, 4, 5]
+ b[1, 0] = b
+ b.should == [1, 1, 2, 3, 4, 5, 2, 3, 4, 5]
+ end
+
+ it "raises an IndexError when passed start and negative length" do
+ a = [1, 2, 3, 4]
+ lambda { a[-2, -1] = "" }.should raise_error(IndexError)
+ lambda { a[0, -1] = "" }.should raise_error(IndexError)
+ lambda { a[2, -1] = "" }.should raise_error(IndexError)
+ lambda { a[4, -1] = "" }.should raise_error(IndexError)
+ lambda { a[10, -1] = "" }.should raise_error(IndexError)
+ lambda { [1, 2, 3, 4, 5][2, -1] = [7, 8] }.should raise_error(IndexError)
+ end
+end
+
+describe "Array#[]= with [m..n]" do
+ it "returns non-array value if non-array value assigned" do
+ a = [1, 2, 3, 4, 5]
+ (a[2..4] = 10).should == 10
+ (a.[]=(2..4, 10)).should == 10
+ end
+
+ it "returns array if array assigned" do
+ a = [1, 2, 3, 4, 5]
+ (a[2..4] = [7, 8]).should == [7, 8]
+ (a.[]=(2..4, [7, 8])).should == [7, 8]
+ end
+
+ it "just sets the section defined by range to nil even if the rhs is nil" do
+ a = [1, 2, 3, 4, 5]
+ a[0..1] = nil
+ a.should == [nil, 3, 4, 5]
+ end
+
+ it "just sets the section defined by range to nil if m and n < 0 and the rhs is nil" do
+ a = [1, 2, 3, 4, 5]
+ a[-3..-2] = nil
+ a.should == [1, 2, nil, 5]
+ end
+
+ it "replaces the section defined by range" do
+ a = [6, 5, 4, 3, 2, 1]
+ a[1...2] = 9
+ a[3..6] = [6, 6, 6]
+ a.should == [6, 9, 4, 6, 6, 6]
+ end
+
+ it "replaces the section if m and n < 0" do
+ a = [1, 2, 3, 4, 5]
+ a[-3..-2] = [7, 8, 9]
+ a.should == [1, 2, 7, 8, 9, 5]
+ end
+
+ it "replaces the section if m < 0 and n > 0" do
+ a = [1, 2, 3, 4, 5]
+ a[-4..3] = [8]
+ a.should == [1, 8, 5]
+ end
+
+ it "inserts the other section at m if m > n" do
+ a = [1, 2, 3, 4, 5]
+ a[3..1] = [8]
+ a.should == [1, 2, 3, 8, 4, 5]
+ end
+
+ describe "Range subclasses" do
+ before :each do
+ @range_incl = ArraySpecs::MyRange.new(1, 2)
+ @range_excl = ArraySpecs::MyRange.new(-3, -1, true)
+ end
+
+ it "accepts Range subclasses" do
+ a = [1, 2, 3, 4]
+
+ a[@range_incl] = ["a", "b"]
+ a.should == [1, "a", "b", 4]
+ a[@range_excl] = ["A", "B"]
+ a.should == [1, "A", "B", 4]
+ end
+
+ it "returns non-array value if non-array value assigned" do
+ a = [1, 2, 3, 4, 5]
+ (a[@range_incl] = 10).should == 10
+ (a.[]=(@range_incl, 10)).should == 10
+ end
+
+ it "returns array if array assigned" do
+ a = [1, 2, 3, 4, 5]
+ (a[@range_incl] = [7, 8]).should == [7, 8]
+ a.[]=(@range_incl, [7, 8]).should == [7, 8]
+ end
+ end
+end
+
+describe "Array#[] after a shift" do
+ it "works for insertion" do
+ a = [1,2]
+ a.shift
+ a.shift
+ a[0,0] = [3,4]
+ a.should == [3,4]
+ end
+end
diff --git a/spec/ruby/core/array/empty_spec.rb b/spec/ruby/core/array/empty_spec.rb
new file mode 100644
index 0000000000..b5f3e8ed48
--- /dev/null
+++ b/spec/ruby/core/array/empty_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#empty?" do
+ it "returns true if the array has no elements" do
+ [].empty?.should == true
+ [1].empty?.should == false
+ [1, 2].empty?.should == false
+ end
+end
diff --git a/spec/ruby/core/array/eql_spec.rb b/spec/ruby/core/array/eql_spec.rb
new file mode 100644
index 0000000000..8565b94c60
--- /dev/null
+++ b/spec/ruby/core/array/eql_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/eql'
+
+describe "Array#eql?" do
+ it_behaves_like :array_eql, :eql?
+
+ it "returns false if any corresponding elements are not #eql?" do
+ [1, 2, 3, 4].should_not eql([1, 2, 3, 4.0])
+ end
+
+ it "returns false if other is not a kind of Array" do
+ obj = mock("array eql?")
+ obj.should_not_receive(:to_ary)
+ obj.should_not_receive(:eql?)
+
+ [1, 2, 3].should_not eql(obj)
+ end
+end
diff --git a/spec/ruby/core/array/equal_value_spec.rb b/spec/ruby/core/array/equal_value_spec.rb
new file mode 100644
index 0000000000..39117c3d7c
--- /dev/null
+++ b/spec/ruby/core/array/equal_value_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/eql'
+
+describe "Array#==" do
+ it_behaves_like :array_eql, :==
+
+ it "compares with an equivalent Array-like object using #to_ary" do
+ obj = mock('array-like')
+ obj.should_receive(:respond_to?).at_least(1).with(:to_ary).and_return(true)
+ obj.should_receive(:==).with([1]).at_least(1).and_return(true)
+
+ ([1] == obj).should be_true
+ ([[1]] == [obj]).should be_true
+ ([[[1], 3], 2] == [[obj, 3], 2]).should be_true
+
+ # recursive arrays
+ arr1 = [[1]]
+ arr1 << arr1
+ arr2 = [obj]
+ arr2 << arr2
+ (arr1 == arr2).should be_true
+ (arr2 == arr1).should be_true
+ end
+
+ it "returns false if any corresponding elements are not #==" do
+ a = ["a", "b", "c"]
+ b = ["a", "b", "not equal value"]
+ a.should_not == b
+
+ c = mock("c")
+ c.should_receive(:==).and_return(false)
+ ["a", "b", c].should_not == a
+ end
+
+ it "returns true if corresponding elements are #==" do
+ [].should == []
+ ["a", "c", 7].should == ["a", "c", 7]
+
+ [1, 2, 3].should == [1.0, 2.0, 3.0]
+
+ obj = mock('5')
+ obj.should_receive(:==).and_return(true)
+ [obj].should == [5]
+ end
+
+ # As per bug #1720
+ it "returns false for [NaN] == [NaN]" do
+ [nan_value].should_not == [nan_value]
+ end
+end
diff --git a/spec/ruby/core/array/fetch_spec.rb b/spec/ruby/core/array/fetch_spec.rb
new file mode 100644
index 0000000000..ccca463935
--- /dev/null
+++ b/spec/ruby/core/array/fetch_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#fetch" do
+ it "returns the element at the passed index" do
+ [1, 2, 3].fetch(1).should == 2
+ [nil].fetch(0).should == nil
+ end
+
+ it "counts negative indices backwards from end" do
+ [1, 2, 3, 4].fetch(-1).should == 4
+ end
+
+ it "raises an IndexError if there is no element at index" do
+ lambda { [1, 2, 3].fetch(3) }.should raise_error(IndexError)
+ lambda { [1, 2, 3].fetch(-4) }.should raise_error(IndexError)
+ lambda { [].fetch(0) }.should raise_error(IndexError)
+ end
+
+ it "returns default if there is no element at index if passed a default value" do
+ [1, 2, 3].fetch(5, :not_found).should == :not_found
+ [1, 2, 3].fetch(5, nil).should == nil
+ [1, 2, 3].fetch(-4, :not_found).should == :not_found
+ [nil].fetch(0, :not_found).should == nil
+ end
+
+ it "returns the value of block if there is no element at index if passed a block" do
+ [1, 2, 3].fetch(9) { |i| i * i }.should == 81
+ [1, 2, 3].fetch(-9) { |i| i * i }.should == 81
+ end
+
+ it "passes the original index argument object to the block, not the converted Integer" do
+ o = mock('5')
+ def o.to_int(); 5; end
+
+ [1, 2, 3].fetch(o) { |i| i }.should equal(o)
+ end
+
+ it "gives precedence to the default block over the default argument" do
+ lambda {
+ @result = [1, 2, 3].fetch(9, :foo) { |i| i * i }
+ }.should complain(/block supersedes default value argument/)
+ @result.should == 81
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(2)
+ ["a", "b", "c"].fetch(obj).should == "c"
+ end
+
+ it "raises a TypeError when the passed argument can't be coerced to Integer" do
+ lambda { [].fetch("cat") }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/array/fill_spec.rb b/spec/ruby/core/array/fill_spec.rb
new file mode 100644
index 0000000000..f953613c26
--- /dev/null
+++ b/spec/ruby/core/array/fill_spec.rb
@@ -0,0 +1,317 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#fill" do
+ before :all do
+ @never_passed = lambda do |i|
+ raise ExpectationNotMetError, "the control path should not pass here"
+ end
+ end
+
+ it "returns self" do
+ ary = [1, 2, 3]
+ ary.fill(:a).should equal(ary)
+ end
+
+ it "is destructive" do
+ ary = [1, 2, 3]
+ ary.fill(:a)
+ ary.should == [:a, :a, :a]
+ end
+
+ it "does not replicate the filler" do
+ ary = [1, 2, 3, 4]
+ str = "x"
+ ary.fill(str).should == [str, str, str, str]
+ str << "y"
+ ary.should == [str, str, str, str]
+ ary[0].should equal(str)
+ ary[1].should equal(str)
+ ary[2].should equal(str)
+ ary[3].should equal(str)
+ end
+
+ it "replaces all elements in the array with the filler if not given a index nor a length" do
+ ary = ['a', 'b', 'c', 'duh']
+ ary.fill(8).should == [8, 8, 8, 8]
+
+ str = "x"
+ ary.fill(str).should == [str, str, str, str]
+ end
+
+ it "replaces all elements with the value of block (index given to block)" do
+ [nil, nil, nil, nil].fill { |i| i * 2 }.should == [0, 2, 4, 6]
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { ArraySpecs.frozen_array.fill('x') }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} on an empty frozen array" do
+ lambda { ArraySpecs.empty_frozen_array.fill('x') }.should raise_error(frozen_error_class)
+ end
+
+ it "raises an ArgumentError if 4 or more arguments are passed when no block given" do
+ lambda { [].fill('a') }.should_not raise_error(ArgumentError)
+
+ lambda { [].fill('a', 1) }.should_not raise_error(ArgumentError)
+
+ lambda { [].fill('a', 1, 2) }.should_not raise_error(ArgumentError)
+ lambda { [].fill('a', 1, 2, true) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if no argument passed and no block given" do
+ lambda { [].fill }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if 3 or more arguments are passed when a block given" do
+ lambda { [].fill() {|i|} }.should_not raise_error(ArgumentError)
+
+ lambda { [].fill(1) {|i|} }.should_not raise_error(ArgumentError)
+
+ lambda { [].fill(1, 2) {|i|} }.should_not raise_error(ArgumentError)
+ lambda { [].fill(1, 2, true) {|i|} }.should raise_error(ArgumentError)
+ end
+end
+
+describe "Array#fill with (filler, index, length)" do
+ it "replaces length elements beginning with the index with the filler if given an index and a length" do
+ ary = [1, 2, 3, 4, 5, 6]
+ ary.fill('x', 2, 3).should == [1, 2, 'x', 'x', 'x', 6]
+ end
+
+ it "replaces length elements beginning with the index with the value of block" do
+ [true, false, true, false, true, false, true].fill(1, 4) { |i| i + 3 }.should == [true, 4, 5, 6, 7, false, true]
+ end
+
+ it "replaces all elements after the index if given an index and no length" do
+ ary = [1, 2, 3]
+ ary.fill('x', 1).should == [1, 'x', 'x']
+ ary.fill(1){|i| i*2}.should == [1, 2, 4]
+ end
+
+ it "replaces all elements after the index if given an index and nil as a length" do
+ a = [1, 2, 3]
+ a.fill('x', 1, nil).should == [1, 'x', 'x']
+ a.fill(1, nil){|i| i*2}.should == [1, 2, 4]
+ a.fill('y', nil).should == ['y', 'y', 'y']
+ end
+
+ it "replaces the last (-n) elements if given an index n which is negative and no length" do
+ a = [1, 2, 3, 4, 5]
+ a.fill('x', -2).should == [1, 2, 3, 'x', 'x']
+ a.fill(-2){|i| i.to_s}.should == [1, 2, 3, '3', '4']
+ end
+
+ it "replaces the last (-n) elements if given an index n which is negative and nil as a length" do
+ a = [1, 2, 3, 4, 5]
+ a.fill('x', -2, nil).should == [1, 2, 3, 'x', 'x']
+ a.fill(-2, nil){|i| i.to_s}.should == [1, 2, 3, '3', '4']
+ end
+
+ it "makes no modifications if given an index greater than end and no length" do
+ [1, 2, 3, 4, 5].fill('a', 5).should == [1, 2, 3, 4, 5]
+ [1, 2, 3, 4, 5].fill(5, &@never_passed).should == [1, 2, 3, 4, 5]
+ end
+
+ it "makes no modifications if given an index greater than end and nil as a length" do
+ [1, 2, 3, 4, 5].fill('a', 5, nil).should == [1, 2, 3, 4, 5]
+ [1, 2, 3, 4, 5].fill(5, nil, &@never_passed).should == [1, 2, 3, 4, 5]
+ end
+
+ it "replaces length elements beginning with start index if given an index >= 0 and a length >= 0" do
+ [1, 2, 3, 4, 5].fill('a', 2, 0).should == [1, 2, 3, 4, 5]
+ [1, 2, 3, 4, 5].fill('a', 2, 2).should == [1, 2, "a", "a", 5]
+
+ [1, 2, 3, 4, 5].fill(2, 0, &@never_passed).should == [1, 2, 3, 4, 5]
+ [1, 2, 3, 4, 5].fill(2, 2){|i| i*2}.should == [1, 2, 4, 6, 5]
+ end
+
+ it "increases the Array size when necessary" do
+ a = [1, 2, 3]
+ a.size.should == 3
+ a.fill 'a', 0, 10
+ a.size.should == 10
+ end
+
+ it "pads between the last element and the index with nil if given an index which is greater than size of the array" do
+ [1, 2, 3, 4, 5].fill('a', 8, 5).should == [1, 2, 3, 4, 5, nil, nil, nil, 'a', 'a', 'a', 'a', 'a']
+ [1, 2, 3, 4, 5].fill(8, 5){|i| 'a'}.should == [1, 2, 3, 4, 5, nil, nil, nil, 'a', 'a', 'a', 'a', 'a']
+ end
+
+ it "replaces length elements beginning with the (-n)th if given an index n < 0 and a length > 0" do
+ [1, 2, 3, 4, 5].fill('a', -2, 2).should == [1, 2, 3, "a", "a"]
+ [1, 2, 3, 4, 5].fill('a', -2, 4).should == [1, 2, 3, "a", "a", "a", "a"]
+
+ [1, 2, 3, 4, 5].fill(-2, 2){|i| 'a'}.should == [1, 2, 3, "a", "a"]
+ [1, 2, 3, 4, 5].fill(-2, 4){|i| 'a'}.should == [1, 2, 3, "a", "a", "a", "a"]
+ end
+
+ it "starts at 0 if the negative index is before the start of the array" do
+ [1, 2, 3, 4, 5].fill('a', -25, 3).should == ['a', 'a', 'a', 4, 5]
+ [1, 2, 3, 4, 5].fill('a', -10, 10).should == %w|a a a a a a a a a a|
+
+ [1, 2, 3, 4, 5].fill(-25, 3){|i| 'a'}.should == ['a', 'a', 'a', 4, 5]
+ [1, 2, 3, 4, 5].fill(-10, 10){|i| 'a'}.should == %w|a a a a a a a a a a|
+ end
+
+ it "makes no modifications if the given length <= 0" do
+ [1, 2, 3, 4, 5].fill('a', 2, 0).should == [1, 2, 3, 4, 5]
+ [1, 2, 3, 4, 5].fill('a', -2, 0).should == [1, 2, 3, 4, 5]
+
+ [1, 2, 3, 4, 5].fill('a', 2, -2).should == [1, 2, 3, 4, 5]
+ [1, 2, 3, 4, 5].fill('a', -2, -2).should == [1, 2, 3, 4, 5]
+
+ [1, 2, 3, 4, 5].fill(2, 0, &@never_passed).should == [1, 2, 3, 4, 5]
+ [1, 2, 3, 4, 5].fill(-2, 0, &@never_passed).should == [1, 2, 3, 4, 5]
+
+ [1, 2, 3, 4, 5].fill(2, -2, &@never_passed).should == [1, 2, 3, 4, 5]
+ [1, 2, 3, 4, 5].fill(-2, -2, &@never_passed).should == [1, 2, 3, 4, 5]
+ end
+
+ # See: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/17481
+ it "does not raise an exception if the given length is negative and its absolute value does not exceed the index" do
+ lambda { [1, 2, 3, 4].fill('a', 3, -1)}.should_not raise_error(ArgumentError)
+ lambda { [1, 2, 3, 4].fill('a', 3, -2)}.should_not raise_error(ArgumentError)
+ lambda { [1, 2, 3, 4].fill('a', 3, -3)}.should_not raise_error(ArgumentError)
+
+ lambda { [1, 2, 3, 4].fill(3, -1, &@never_passed)}.should_not raise_error(ArgumentError)
+ lambda { [1, 2, 3, 4].fill(3, -2, &@never_passed)}.should_not raise_error(ArgumentError)
+ lambda { [1, 2, 3, 4].fill(3, -3, &@never_passed)}.should_not raise_error(ArgumentError)
+ end
+
+ it "does not raise an exception even if the given length is negative and its absolute value exceeds the index" do
+ lambda { [1, 2, 3, 4].fill('a', 3, -4)}.should_not raise_error(ArgumentError)
+ lambda { [1, 2, 3, 4].fill('a', 3, -5)}.should_not raise_error(ArgumentError)
+ lambda { [1, 2, 3, 4].fill('a', 3, -10000)}.should_not raise_error(ArgumentError)
+
+ lambda { [1, 2, 3, 4].fill(3, -4, &@never_passed)}.should_not raise_error(ArgumentError)
+ lambda { [1, 2, 3, 4].fill(3, -5, &@never_passed)}.should_not raise_error(ArgumentError)
+ lambda { [1, 2, 3, 4].fill(3, -10000, &@never_passed)}.should_not raise_error(ArgumentError)
+ end
+
+ it "tries to convert the second and third arguments to Integers using #to_int" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(2, 2)
+ filler = mock('filler')
+ filler.should_not_receive(:to_int)
+ [1, 2, 3, 4, 5].fill(filler, obj, obj).should == [1, 2, filler, filler, 5]
+ end
+
+ it "raises a TypeError if the index is not numeric" do
+ lambda { [].fill 'a', true }.should raise_error(TypeError)
+
+ obj = mock('nonnumeric')
+ lambda { [].fill('a', obj) }.should raise_error(TypeError)
+ end
+
+ not_supported_on :opal do
+ it "raises an ArgumentError or RangeError for too-large sizes" do
+ arr = [1, 2, 3]
+ lambda { arr.fill(10, 1, fixnum_max) }.should raise_error(ArgumentError)
+ lambda { arr.fill(10, 1, bignum_value) }.should raise_error(RangeError)
+ end
+ end
+end
+
+describe "Array#fill with (filler, range)" do
+ it "replaces elements in range with object" do
+ [1, 2, 3, 4, 5, 6].fill(8, 0..3).should == [8, 8, 8, 8, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill(8, 0...3).should == [8, 8, 8, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill('x', 4..6).should == [1, 2, 3, 4, 'x', 'x', 'x']
+ [1, 2, 3, 4, 5, 6].fill('x', 4...6).should == [1, 2, 3, 4, 'x', 'x']
+ [1, 2, 3, 4, 5, 6].fill('x', -2..-1).should == [1, 2, 3, 4, 'x', 'x']
+ [1, 2, 3, 4, 5, 6].fill('x', -2...-1).should == [1, 2, 3, 4, 'x', 6]
+ [1, 2, 3, 4, 5, 6].fill('x', -2...-2).should == [1, 2, 3, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill('x', -2..-2).should == [1, 2, 3, 4, 'x', 6]
+ [1, 2, 3, 4, 5, 6].fill('x', -2..0).should == [1, 2, 3, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill('x', 0...0).should == [1, 2, 3, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill('x', 1..1).should == [1, 'x', 3, 4, 5, 6]
+ end
+
+ it "replaces all elements in range with the value of block" do
+ [1, 1, 1, 1, 1, 1].fill(1..6) { |i| i + 1 }.should == [1, 2, 3, 4, 5, 6, 7]
+ end
+
+ it "increases the Array size when necessary" do
+ [1, 2, 3].fill('x', 1..6).should == [1, 'x', 'x', 'x', 'x', 'x', 'x']
+ [1, 2, 3].fill(1..6){|i| i+1}.should == [1, 2, 3, 4, 5, 6, 7]
+ end
+
+ it "raises a TypeError with range and length argument" do
+ lambda { [].fill('x', 0 .. 2, 5) }.should raise_error(TypeError)
+ end
+
+ it "replaces elements between the (-m)th to the last and the (n+1)th from the first if given an range m..n where m < 0 and n >= 0" do
+ [1, 2, 3, 4, 5, 6].fill('x', -4..4).should == [1, 2, 'x', 'x', 'x', 6]
+ [1, 2, 3, 4, 5, 6].fill('x', -4...4).should == [1, 2, 'x', 'x', 5, 6]
+
+ [1, 2, 3, 4, 5, 6].fill(-4..4){|i| (i+1).to_s}.should == [1, 2, '3', '4', '5', 6]
+ [1, 2, 3, 4, 5, 6].fill(-4...4){|i| (i+1).to_s}.should == [1, 2, '3', '4', 5, 6]
+ end
+
+ it "replaces elements between the (-m)th and (-n)th to the last if given an range m..n where m < 0 and n < 0" do
+ [1, 2, 3, 4, 5, 6].fill('x', -4..-2).should == [1, 2, 'x', 'x', 'x', 6]
+ [1, 2, 3, 4, 5, 6].fill('x', -4...-2).should == [1, 2, 'x', 'x', 5, 6]
+
+ [1, 2, 3, 4, 5, 6].fill(-4..-2){|i| (i+1).to_s}.should == [1, 2, '3', '4', '5', 6]
+ [1, 2, 3, 4, 5, 6].fill(-4...-2){|i| (i+1).to_s}.should == [1, 2, '3', '4', 5, 6]
+ end
+
+ it "replaces elements between the (m+1)th from the first and (-n)th to the last if given an range m..n where m >= 0 and n < 0" do
+ [1, 2, 3, 4, 5, 6].fill('x', 2..-2).should == [1, 2, 'x', 'x', 'x', 6]
+ [1, 2, 3, 4, 5, 6].fill('x', 2...-2).should == [1, 2, 'x', 'x', 5, 6]
+
+ [1, 2, 3, 4, 5, 6].fill(2..-2){|i| (i+1).to_s}.should == [1, 2, '3', '4', '5', 6]
+ [1, 2, 3, 4, 5, 6].fill(2...-2){|i| (i+1).to_s}.should == [1, 2, '3', '4', 5, 6]
+ end
+
+ it "makes no modifications if given an range which implies a section of zero width" do
+ [1, 2, 3, 4, 5, 6].fill('x', 2...2).should == [1, 2, 3, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill('x', -4...2).should == [1, 2, 3, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill('x', -4...-4).should == [1, 2, 3, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill('x', 2...-4).should == [1, 2, 3, 4, 5, 6]
+
+ [1, 2, 3, 4, 5, 6].fill(2...2, &@never_passed).should == [1, 2, 3, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill(-4...2, &@never_passed).should == [1, 2, 3, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill(-4...-4, &@never_passed).should == [1, 2, 3, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill(2...-4, &@never_passed).should == [1, 2, 3, 4, 5, 6]
+ end
+
+ it "makes no modifications if given an range which implies a section of negative width" do
+ [1, 2, 3, 4, 5, 6].fill('x', 2..1).should == [1, 2, 3, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill('x', -4..1).should == [1, 2, 3, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill('x', -2..-4).should == [1, 2, 3, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill('x', 2..-5).should == [1, 2, 3, 4, 5, 6]
+
+ [1, 2, 3, 4, 5, 6].fill(2..1, &@never_passed).should == [1, 2, 3, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill(-4..1, &@never_passed).should == [1, 2, 3, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill(-2..-4, &@never_passed).should == [1, 2, 3, 4, 5, 6]
+ [1, 2, 3, 4, 5, 6].fill(2..-5, &@never_passed).should == [1, 2, 3, 4, 5, 6]
+ end
+
+ it "raises an exception if some of the given range lies before the first of the array" do
+ lambda { [1, 2, 3].fill('x', -5..-3) }.should raise_error(RangeError)
+ lambda { [1, 2, 3].fill('x', -5...-3) }.should raise_error(RangeError)
+ lambda { [1, 2, 3].fill('x', -5..-4) }.should raise_error(RangeError)
+
+ lambda { [1, 2, 3].fill(-5..-3, &@never_passed) }.should raise_error(RangeError)
+ lambda { [1, 2, 3].fill(-5...-3, &@never_passed) }.should raise_error(RangeError)
+ lambda { [1, 2, 3].fill(-5..-4, &@never_passed) }.should raise_error(RangeError)
+ end
+
+ it "tries to convert the start and end of the passed range to Integers using #to_int" do
+ obj = mock('to_int')
+ def obj.<=>(rhs); rhs == self ? 0 : nil end
+ obj.should_receive(:to_int).twice.and_return(2)
+ filler = mock('filler')
+ filler.should_not_receive(:to_int)
+ [1, 2, 3, 4, 5].fill(filler, obj..obj).should == [1, 2, filler, 4, 5]
+ end
+
+ it "raises a TypeError if the start or end of the passed range is not numeric" do
+ obj = mock('nonnumeric')
+ def obj.<=>(rhs); rhs == self ? 0 : nil end
+ lambda { [].fill('a', obj..obj) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/array/filter_spec.rb b/spec/ruby/core/array/filter_spec.rb
new file mode 100644
index 0000000000..ee4f71ca28
--- /dev/null
+++ b/spec/ruby/core/array/filter_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require_relative 'shared/select'
+
+ruby_version_is "2.6" do
+ describe "Array#filter" do
+ it_behaves_like :array_select, :filter
+ end
+
+ describe "Array#filter!" do
+ it "returns nil if no changes were made in the array" do
+ [1, 2, 3].filter! { true }.should be_nil
+ end
+
+ it_behaves_like :keep_if, :filter!
+ end
+end
diff --git a/spec/ruby/core/array/find_index_spec.rb b/spec/ruby/core/array/find_index_spec.rb
new file mode 100644
index 0000000000..759472024a
--- /dev/null
+++ b/spec/ruby/core/array/find_index_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/index'
+
+describe "Array#find_index" do
+ it_behaves_like :array_index, :find_index
+end
diff --git a/spec/ruby/core/array/first_spec.rb b/spec/ruby/core/array/first_spec.rb
new file mode 100644
index 0000000000..5a0a25aeef
--- /dev/null
+++ b/spec/ruby/core/array/first_spec.rb
@@ -0,0 +1,93 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#first" do
+ it "returns the first element" do
+ %w{a b c}.first.should == 'a'
+ [nil].first.should == nil
+ end
+
+ it "returns nil if self is empty" do
+ [].first.should == nil
+ end
+
+ it "returns the first count elements if given a count" do
+ [true, false, true, nil, false].first(2).should == [true, false]
+ end
+
+ it "returns an empty array when passed count on an empty array" do
+ [].first(0).should == []
+ [].first(1).should == []
+ [].first(2).should == []
+ end
+
+ it "returns an empty array when passed count == 0" do
+ [1, 2, 3, 4, 5].first(0).should == []
+ end
+
+ it "returns an array containing the first element when passed count == 1" do
+ [1, 2, 3, 4, 5].first(1).should == [1]
+ end
+
+ it "raises an ArgumentError when count is negative" do
+ lambda { [1, 2].first(-1) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a RangeError when count is a Bignum" do
+ lambda { [].first(bignum_value) }.should raise_error(RangeError)
+ end
+
+ it "returns the entire array when count > length" do
+ [1, 2, 3, 4, 5, 9].first(10).should == [1, 2, 3, 4, 5, 9]
+ end
+
+ it "returns an array which is independent to the original when passed count" do
+ ary = [1, 2, 3, 4, 5]
+ ary.first(0).replace([1,2])
+ ary.should == [1, 2, 3, 4, 5]
+ ary.first(1).replace([1,2])
+ ary.should == [1, 2, 3, 4, 5]
+ ary.first(6).replace([1,2])
+ ary.should == [1, 2, 3, 4, 5]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.first.should equal(empty)
+
+ ary = ArraySpecs.head_recursive_array
+ ary.first.should equal(ary)
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(2)
+ [1, 2, 3, 4, 5].first(obj).should == [1, 2]
+ end
+
+ it "raises a TypeError if the passed argument is not numeric" do
+ lambda { [1,2].first(nil) }.should raise_error(TypeError)
+ lambda { [1,2].first("a") }.should raise_error(TypeError)
+
+ obj = mock("nonnumeric")
+ lambda { [1,2].first(obj) }.should raise_error(TypeError)
+ end
+
+ it "does not return subclass instance when passed count on Array subclasses" do
+ ArraySpecs::MyArray[].first(0).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[].first(2).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].first(0).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].first(1).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].first(2).should be_an_instance_of(Array)
+ end
+
+ it "is not destructive" do
+ a = [1, 2, 3]
+ a.first
+ a.should == [1, 2, 3]
+ a.first(2)
+ a.should == [1, 2, 3]
+ a.first(3)
+ a.should == [1, 2, 3]
+ end
+end
diff --git a/spec/ruby/core/array/fixtures/classes.rb b/spec/ruby/core/array/fixtures/classes.rb
new file mode 100644
index 0000000000..7ca9067328
--- /dev/null
+++ b/spec/ruby/core/array/fixtures/classes.rb
@@ -0,0 +1,522 @@
+class Object
+ # This helper is defined here rather than in MSpec because
+ # it is only used in #pack specs.
+ def pack_format(count=nil, repeat=nil)
+ format = instance_variable_get(:@method)
+ format += count.to_s unless format == 'P' || format == 'p'
+ format *= repeat if repeat
+ format.dup # because it may then become tainted
+ end
+end
+
+module ArraySpecs
+ SampleRange = 0..1000
+ SampleCount = 1000
+
+ def self.frozen_array
+ [1,2,3].freeze
+ end
+
+ def self.empty_frozen_array
+ [].freeze
+ end
+
+ def self.recursive_array
+ a = [1, 'two', 3.0]
+ 5.times { a << a }
+ a
+ end
+
+ def self.head_recursive_array
+ a = []
+ 5.times { a << a }
+ a << 1 << 'two' << 3.0
+ a
+ end
+
+ def self.empty_recursive_array
+ a = []
+ a << a
+ a
+ end
+
+ class MyArray < Array
+ # The #initialize method has a different signature than Array to help
+ # catch places in the specs that do not assert the #initialize is not
+ # called when Array methods make new instances.
+ def initialize(a, b)
+ self << a << b
+ ScratchPad.record :my_array_initialize
+ end
+ end
+
+ class Sexp < Array
+ def initialize(*args)
+ super(args)
+ end
+ end
+
+ # TODO: replace specs that use this with #should_not_receive(:to_ary)
+ # expectations on regular objects (e.g. Array instances).
+ class ToAryArray < Array
+ def to_ary() ["to_ary", "was", "called!"] end
+ end
+
+ class MyRange < Range; end
+
+ class AssocKey
+ def ==(other); other == 'it'; end
+ end
+
+ class D
+ def <=>(obj)
+ return 4 <=> obj unless obj.class == D
+ 0
+ end
+ end
+
+ class SubArray < Array
+ def initialize(*args)
+ ScratchPad.record args
+ end
+ end
+
+ class ArrayConvertable
+ attr_accessor :called
+ def initialize(*values, &block)
+ @values = values;
+ end
+
+ def to_a
+ self.called = :to_a
+ @values
+ end
+
+ def to_ary
+ self.called = :to_ary
+ @values
+ end
+ end
+
+ class SortSame
+ def <=>(other); 0; end
+ def ==(other); true; end
+ end
+
+ class UFOSceptic
+ def <=>(other); raise "N-uh, UFO:s do not exist!"; end
+ end
+
+ class MockForCompared
+ @@count = 0
+ @@compared = false
+ def initialize
+ @@compared = false
+ @order = (@@count += 1)
+ end
+ def <=>(rhs)
+ @@compared = true
+ return rhs.order <=> self.order
+ end
+ def self.compared?
+ @@compared
+ end
+
+ protected
+ attr_accessor :order
+ end
+
+ class ComparableWithFixnum
+ include Comparable
+ def initialize(num)
+ @num = num
+ end
+
+ def <=>(fixnum)
+ @num <=> fixnum
+ end
+ end
+
+ class Uncomparable
+ def <=>(obj)
+ nil
+ end
+ end
+
+ def self.universal_pack_object
+ obj = mock("string float int")
+ obj.stub!(:to_int).and_return(1)
+ obj.stub!(:to_str).and_return("1")
+ obj.stub!(:to_f).and_return(1.0)
+ obj
+ end
+
+ LargeArray = ["test_create_table_with_force_true_does_not_drop_nonexisting_table",
+ "test_add_table",
+ "assert_difference",
+ "assert_operator",
+ "instance_variables",
+ "class",
+ "instance_variable_get",
+ "__class__",
+ "expects",
+ "assert_no_difference",
+ "name",
+ "assert_blank",
+ "assert_not_same",
+ "is_a?",
+ "test_add_table_with_decimals",
+ "test_create_table_with_timestamps_should_create_datetime_columns",
+ "assert_present",
+ "assert_no_match",
+ "__instance_of__",
+ "assert_deprecated",
+ "assert",
+ "assert_throws",
+ "kind_of?",
+ "try",
+ "__instance_variable_get__",
+ "object_id",
+ "timeout",
+ "instance_variable_set",
+ "assert_nothing_thrown",
+ "__instance_variable_set__",
+ "copy_object",
+ "test_create_table_with_timestamps_should_create_datetime_columns_with_options",
+ "assert_not_deprecated",
+ "assert_in_delta",
+ "id",
+ "copy_metaclass",
+ "test_create_table_without_a_block",
+ "dup",
+ "assert_not_nil",
+ "send",
+ "__instance_variables__",
+ "to_sql",
+ "mock",
+ "assert_send",
+ "instance_variable_defined?",
+ "clone",
+ "require",
+ "test_migrator",
+ "__instance_variable_defined_eh__",
+ "frozen?",
+ "test_add_column_not_null_with_default",
+ "freeze",
+ "test_migrator_one_up",
+ "test_migrator_one_down",
+ "singleton_methods",
+ "method_exists?",
+ "create_fixtures",
+ "test_migrator_one_up_one_down",
+ "test_native_decimal_insert_manual_vs_automatic",
+ "instance_exec",
+ "__is_a__",
+ "test_migrator_double_up",
+ "stub",
+ "private_methods",
+ "stubs",
+ "test_migrator_double_down",
+ "fixture_path",
+ "private_singleton_methods",
+ "stub_everything",
+ "test_migrator_one_up_with_exception_and_rollback",
+ "sequence",
+ "protected_methods",
+ "enum_for",
+ "test_finds_migrations",
+ "run_before_mocha",
+ "states",
+ "protected_singleton_methods",
+ "to_json",
+ "instance_values",
+ "==",
+ "mocha_setup",
+ "public_methods",
+ "test_finds_pending_migrations",
+ "mocha_verify",
+ "assert_kind_of",
+ "===",
+ "=~",
+ "test_relative_migrations",
+ "mocha_teardown",
+ "gem",
+ "mocha",
+ "test_only_loads_pending_migrations",
+ "test_add_column_with_precision_and_scale",
+ "require_or_load",
+ "eql?",
+ "require_dependency",
+ "test_native_types",
+ "test_target_version_zero_should_run_only_once",
+ "extend",
+ "to_matcher",
+ "unloadable",
+ "require_association",
+ "hash",
+ "__id__",
+ "load_dependency",
+ "equals",
+ "test_migrator_db_has_no_schema_migrations_table",
+ "test_migrator_verbosity",
+ "kind_of",
+ "to_yaml",
+ "to_bool",
+ "test_migrator_verbosity_off",
+ "taint",
+ "test_migrator_going_down_due_to_version_target",
+ "tainted?",
+ "mocha_inspect",
+ "test_migrator_rollback",
+ "vim",
+ "untaint",
+ "taguri=",
+ "test_migrator_forward",
+ "test_schema_migrations_table_name",
+ "test_proper_table_name",
+ "all_of",
+ "test_add_drop_table_with_prefix_and_suffix",
+ "_setup_callbacks",
+ "setup",
+ "Not",
+ "test_create_table_with_binary_column",
+ "assert_not_equal",
+ "enable_warnings",
+ "acts_like?",
+ "Rational",
+ "_removed_setup_callbacks",
+ "Table",
+ "bind",
+ "any_of",
+ "__method__",
+ "test_migrator_with_duplicates",
+ "_teardown_callbacks",
+ "method",
+ "test_migrator_with_duplicate_names",
+ "_removed_teardown_callbacks",
+ "any_parameters",
+ "test_migrator_with_missing_version_numbers",
+ "test_add_remove_single_field_using_string_arguments",
+ "test_create_table_with_custom_sequence_name",
+ "test_add_remove_single_field_using_symbol_arguments",
+ "_one_time_conditions_valid_14?",
+ "_one_time_conditions_valid_16?",
+ "run_callbacks",
+ "anything",
+ "silence_warnings",
+ "instance_variable_names",
+ "_fixture_path",
+ "copy_instance_variables_from",
+ "fixture_path?",
+ "has_entry",
+ "__marshal__",
+ "_fixture_table_names",
+ "__kind_of__",
+ "fixture_table_names?",
+ "test_add_rename",
+ "assert_equal",
+ "_fixture_class_names",
+ "fixture_class_names?",
+ "has_entries",
+ "_use_transactional_fixtures",
+ "people",
+ "test_rename_column_using_symbol_arguments",
+ "use_transactional_fixtures?",
+ "instance_eval",
+ "blank?",
+ "with_warnings",
+ "__nil__",
+ "load",
+ "metaclass",
+ "_use_instantiated_fixtures",
+ "has_key",
+ "class_eval",
+ "present?",
+ "test_rename_column",
+ "teardown",
+ "use_instantiated_fixtures?",
+ "method_name",
+ "silence_stderr",
+ "presence",
+ "test_rename_column_preserves_default_value_not_null",
+ "silence_stream",
+ "_pre_loaded_fixtures",
+ "__metaclass__",
+ "__fixnum__",
+ "pre_loaded_fixtures?",
+ "has_value",
+ "suppress",
+ "to_yaml_properties",
+ "test_rename_nonexistent_column",
+ "test_add_index",
+ "includes",
+ "find_correlate_in",
+ "equality_predicate_sql",
+ "assert_nothing_raised",
+ "let",
+ "not_predicate_sql",
+ "test_rename_column_with_sql_reserved_word",
+ "singleton_class",
+ "test_rename_column_with_an_index",
+ "display",
+ "taguri",
+ "to_yaml_style",
+ "test_remove_column_with_index",
+ "size",
+ "current_adapter?",
+ "test_remove_column_with_multi_column_index",
+ "respond_to?",
+ "test_change_type_of_not_null_column",
+ "is_a",
+ "to_a",
+ "test_rename_table_for_sqlite_should_work_with_reserved_words",
+ "require_library_or_gem",
+ "setup_fixtures",
+ "equal?",
+ "teardown_fixtures",
+ "nil?",
+ "fixture_table_names",
+ "fixture_class_names",
+ "test_create_table_without_id",
+ "use_transactional_fixtures",
+ "test_add_column_with_primary_key_attribute",
+ "repair_validations",
+ "use_instantiated_fixtures",
+ "instance_of?",
+ "test_create_table_adds_id",
+ "test_rename_table",
+ "pre_loaded_fixtures",
+ "to_enum",
+ "test_create_table_with_not_null_column",
+ "instance_of",
+ "test_change_column_nullability",
+ "optionally",
+ "test_rename_table_with_an_index",
+ "run",
+ "test_change_column",
+ "default_test",
+ "assert_raise",
+ "test_create_table_with_defaults",
+ "assert_nil",
+ "flunk",
+ "regexp_matches",
+ "duplicable?",
+ "reset_mocha",
+ "stubba_method",
+ "filter_backtrace",
+ "test_create_table_with_limits",
+ "responds_with",
+ "stubba_object",
+ "test_change_column_with_nil_default",
+ "assert_block",
+ "__show__",
+ "assert_date_from_db",
+ "__respond_to_eh__",
+ "run_in_transaction?",
+ "inspect",
+ "assert_sql",
+ "test_change_column_with_new_default",
+ "yaml_equivalent",
+ "build_message",
+ "to_s",
+ "test_change_column_default",
+ "assert_queries",
+ "pending",
+ "as_json",
+ "assert_no_queries",
+ "test_change_column_quotes_column_names",
+ "assert_match",
+ "test_keeping_default_and_notnull_constaint_on_change",
+ "methods",
+ "connection_allow_concurrency_setup",
+ "connection_allow_concurrency_teardown",
+ "test_create_table_with_primary_key_prefix_as_table_name_with_underscore",
+ "__send__",
+ "make_connection",
+ "assert_raises",
+ "tap",
+ "with_kcode",
+ "assert_instance_of",
+ "test_create_table_with_primary_key_prefix_as_table_name",
+ "assert_respond_to",
+ "test_change_column_default_to_null",
+ "assert_same",
+ "__extend__"]
+
+ LargeTestArraySorted = ["test_add_column_not_null_with_default",
+ "test_add_column_with_precision_and_scale",
+ "test_add_column_with_primary_key_attribute",
+ "test_add_drop_table_with_prefix_and_suffix",
+ "test_add_index",
+ "test_add_remove_single_field_using_string_arguments",
+ "test_add_remove_single_field_using_symbol_arguments",
+ "test_add_rename",
+ "test_add_table",
+ "test_add_table_with_decimals",
+ "test_change_column",
+ "test_change_column_default",
+ "test_change_column_default_to_null",
+ "test_change_column_nullability",
+ "test_change_column_quotes_column_names",
+ "test_change_column_with_new_default",
+ "test_change_column_with_nil_default",
+ "test_change_type_of_not_null_column",
+ "test_create_table_adds_id",
+ "test_create_table_with_binary_column",
+ "test_create_table_with_custom_sequence_name",
+ "test_create_table_with_defaults",
+ "test_create_table_with_force_true_does_not_drop_nonexisting_table",
+ "test_create_table_with_limits",
+ "test_create_table_with_not_null_column",
+ "test_create_table_with_primary_key_prefix_as_table_name",
+ "test_create_table_with_primary_key_prefix_as_table_name_with_underscore",
+ "test_create_table_with_timestamps_should_create_datetime_columns",
+ "test_create_table_with_timestamps_should_create_datetime_columns_with_options",
+ "test_create_table_without_a_block",
+ "test_create_table_without_id",
+ "test_finds_migrations",
+ "test_finds_pending_migrations",
+ "test_keeping_default_and_notnull_constaint_on_change",
+ "test_migrator",
+ "test_migrator_db_has_no_schema_migrations_table",
+ "test_migrator_double_down",
+ "test_migrator_double_up",
+ "test_migrator_forward",
+ "test_migrator_going_down_due_to_version_target",
+ "test_migrator_one_down",
+ "test_migrator_one_up",
+ "test_migrator_one_up_one_down",
+ "test_migrator_one_up_with_exception_and_rollback",
+ "test_migrator_rollback",
+ "test_migrator_verbosity",
+ "test_migrator_verbosity_off",
+ "test_migrator_with_duplicate_names",
+ "test_migrator_with_duplicates",
+ "test_migrator_with_missing_version_numbers",
+ "test_native_decimal_insert_manual_vs_automatic",
+ "test_native_types",
+ "test_only_loads_pending_migrations",
+ "test_proper_table_name",
+ "test_relative_migrations",
+ "test_remove_column_with_index",
+ "test_remove_column_with_multi_column_index",
+ "test_rename_column",
+ "test_rename_column_preserves_default_value_not_null",
+ "test_rename_column_using_symbol_arguments",
+ "test_rename_column_with_an_index",
+ "test_rename_column_with_sql_reserved_word",
+ "test_rename_nonexistent_column",
+ "test_rename_table",
+ "test_rename_table_for_sqlite_should_work_with_reserved_words",
+ "test_rename_table_with_an_index",
+ "test_schema_migrations_table_name",
+ "test_target_version_zero_should_run_only_once"]
+
+ class PrivateToAry
+ private
+
+ def to_ary
+ [1, 2, 3]
+ end
+ end
+end
diff --git a/spec/ruby/core/array/fixtures/encoded_strings.rb b/spec/ruby/core/array/fixtures/encoded_strings.rb
new file mode 100644
index 0000000000..e31e247afe
--- /dev/null
+++ b/spec/ruby/core/array/fixtures/encoded_strings.rb
@@ -0,0 +1,69 @@
+# encoding: utf-8
+module ArraySpecs
+ def self.array_with_usascii_and_7bit_utf8_strings
+ [
+ 'foo'.force_encoding('US-ASCII'),
+ 'bar'
+ ]
+ end
+
+ def self.array_with_usascii_and_utf8_strings
+ [
+ 'foo'.force_encoding('US-ASCII'),
+ 'báz'
+ ]
+ end
+
+ def self.array_with_7bit_utf8_and_usascii_strings
+ [
+ 'bar',
+ 'foo'.force_encoding('US-ASCII')
+ ]
+ end
+
+ def self.array_with_utf8_and_usascii_strings
+ [
+ 'báz',
+ 'bar',
+ 'foo'.force_encoding('US-ASCII')
+ ]
+ end
+
+ def self.array_with_usascii_and_utf8_strings
+ [
+ 'foo'.force_encoding('US-ASCII'),
+ 'bar',
+ 'báz'
+ ]
+ end
+
+ def self.array_with_utf8_and_7bit_ascii8bit_strings
+ [
+ 'bar',
+ 'báz',
+ 'foo'.force_encoding('ASCII-8BIT')
+ ]
+ end
+
+ def self.array_with_utf8_and_ascii8bit_strings
+ [
+ 'bar',
+ 'báz',
+ [255].pack('C').force_encoding('ASCII-8BIT')
+ ]
+ end
+
+ def self.array_with_usascii_and_7bit_ascii8bit_strings
+ [
+ 'bar'.force_encoding('US-ASCII'),
+ 'foo'.force_encoding('ASCII-8BIT')
+ ]
+ end
+
+ def self.array_with_usascii_and_ascii8bit_strings
+ [
+ 'bar'.force_encoding('US-ASCII'),
+ [255].pack('C').force_encoding('ASCII-8BIT')
+ ]
+ end
+end
diff --git a/spec/ruby/core/array/flatten_spec.rb b/spec/ruby/core/array/flatten_spec.rb
new file mode 100644
index 0000000000..1b7361552a
--- /dev/null
+++ b/spec/ruby/core/array/flatten_spec.rb
@@ -0,0 +1,284 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#flatten" do
+ it "returns a one-dimensional flattening recursively" do
+ [[[1, [2, 3]],[2, 3, [4, [4, [5, 5]], [1, 2, 3]]], [4]], []].flatten.should == [1, 2, 3, 2, 3, 4, 4, 5, 5, 1, 2, 3, 4]
+ end
+
+ it "takes an optional argument that determines the level of recursion" do
+ [ 1, 2, [3, [4, 5] ] ].flatten(1).should == [1, 2, 3, [4, 5]]
+ end
+
+ it "returns dup when the level of recursion is 0" do
+ a = [ 1, 2, [3, [4, 5] ] ]
+ a.flatten(0).should == a
+ a.flatten(0).should_not equal(a)
+ end
+
+ it "ignores negative levels" do
+ [ 1, 2, [ 3, 4, [5, 6] ] ].flatten(-1).should == [1, 2, 3, 4, 5, 6]
+ [ 1, 2, [ 3, 4, [5, 6] ] ].flatten(-10).should == [1, 2, 3, 4, 5, 6]
+ end
+
+ it "tries to convert passed Objects to Integers using #to_int" do
+ obj = mock("Converted to Integer")
+ obj.should_receive(:to_int).and_return(1)
+
+ [ 1, 2, [3, [4, 5] ] ].flatten(obj).should == [1, 2, 3, [4, 5]]
+ end
+
+ it "raises a TypeError when the passed Object can't be converted to an Integer" do
+ obj = mock("Not converted")
+ lambda { [ 1, 2, [3, [4, 5] ] ].flatten(obj) }.should raise_error(TypeError)
+ end
+
+ it "does not call flatten on elements" do
+ obj = mock('[1,2]')
+ obj.should_not_receive(:flatten)
+ [obj, obj].flatten.should == [obj, obj]
+
+ obj = [5, 4]
+ obj.should_not_receive(:flatten)
+ [obj, obj].flatten.should == [5, 4, 5, 4]
+ end
+
+ it "raises an ArgumentError on recursive arrays" do
+ x = []
+ x << x
+ lambda { x.flatten }.should raise_error(ArgumentError)
+
+ x = []
+ y = []
+ x << y
+ y << x
+ lambda { x.flatten }.should raise_error(ArgumentError)
+ end
+
+ it "flattens any element which responds to #to_ary, using the return value of said method" do
+ x = mock("[3,4]")
+ x.should_receive(:to_ary).at_least(:once).and_return([3, 4])
+ [1, 2, x, 5].flatten.should == [1, 2, 3, 4, 5]
+
+ y = mock("MyArray[]")
+ y.should_receive(:to_ary).at_least(:once).and_return(ArraySpecs::MyArray[])
+ [y].flatten.should == []
+
+ z = mock("[2,x,y,5]")
+ z.should_receive(:to_ary).and_return([2, x, y, 5])
+ [1, z, 6].flatten.should == [1, 2, 3, 4, 5, 6]
+ end
+
+ it "does not call #to_ary on elements beyond the given level" do
+ obj = mock("1")
+ obj.should_not_receive(:to_ary)
+ [[obj]].flatten(1)
+ end
+
+ it "returns subclass instance for Array subclasses" do
+ ArraySpecs::MyArray[].flatten.should be_an_instance_of(ArraySpecs::MyArray)
+ ArraySpecs::MyArray[1, 2, 3].flatten.should be_an_instance_of(ArraySpecs::MyArray)
+ ArraySpecs::MyArray[1, [2], 3].flatten.should be_an_instance_of(ArraySpecs::MyArray)
+ ArraySpecs::MyArray[1, [2, 3], 4].flatten.should == ArraySpecs::MyArray[1, 2, 3, 4]
+ [ArraySpecs::MyArray[1, 2, 3]].flatten.should be_an_instance_of(Array)
+ end
+
+ it "is not destructive" do
+ ary = [1, [2, 3]]
+ ary.flatten
+ ary.should == [1, [2, 3]]
+ end
+
+ describe "with a non-Array object in the Array" do
+ before :each do
+ @obj = mock("Array#flatten")
+ ScratchPad.record []
+ end
+
+ it "does not call #to_ary if the method is not defined" do
+ [@obj].flatten.should == [@obj]
+ end
+
+ it "does not raise an exception if #to_ary returns nil" do
+ @obj.should_receive(:to_ary).and_return(nil)
+ [@obj].flatten.should == [@obj]
+ end
+
+ it "raises a TypeError if #to_ary does not return an Array" do
+ @obj.should_receive(:to_ary).and_return(1)
+ lambda { [@obj].flatten }.should raise_error(TypeError)
+ end
+
+ ruby_version_is ""..."2.5" do
+ it "calls respond_to_missing?(:to_ary, false) to try coercing" do
+ def @obj.respond_to_missing?(*args) ScratchPad << args; false end
+ [@obj].flatten.should == [@obj]
+ ScratchPad.recorded.should == [[:to_ary, false]]
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "calls respond_to_missing?(:to_ary, true) to try coercing" do
+ def @obj.respond_to_missing?(*args) ScratchPad << args; false end
+ [@obj].flatten.should == [@obj]
+ ScratchPad.recorded.should == [[:to_ary, true]]
+ end
+ end
+
+ it "does not call #to_ary if not defined when #respond_to_missing? returns false" do
+ def @obj.respond_to_missing?(name, priv) ScratchPad << name; false end
+
+ [@obj].flatten.should == [@obj]
+ ScratchPad.recorded.should == [:to_ary]
+ end
+
+ it "calls #to_ary if not defined when #respond_to_missing? returns true" do
+ def @obj.respond_to_missing?(name, priv) ScratchPad << name; true end
+
+ lambda { [@obj].flatten }.should raise_error(NoMethodError)
+ ScratchPad.recorded.should == [:to_ary]
+ end
+
+ it "calls #method_missing if defined" do
+ @obj.should_receive(:method_missing).with(:to_ary).and_return([1, 2, 3])
+ [@obj].flatten.should == [1, 2, 3]
+ end
+ end
+
+ it "returns a tainted array if self is tainted" do
+ [].taint.flatten.tainted?.should be_true
+ end
+
+ it "returns an untrusted array if self is untrusted" do
+ [].untrust.flatten.untrusted?.should be_true
+ end
+
+ it "performs respond_to? and method_missing-aware checks when coercing elements to array" do
+ bo = BasicObject.new
+ [bo].flatten.should == [bo]
+
+ def bo.method_missing(name, *)
+ [1,2]
+ end
+
+ [bo].flatten.should == [1,2]
+
+ def bo.respond_to?(name, *)
+ false
+ end
+
+ [bo].flatten.should == [bo]
+
+ def bo.respond_to?(name, *)
+ true
+ end
+
+ [bo].flatten.should == [1,2]
+ end
+end
+
+describe "Array#flatten!" do
+ it "modifies array to produce a one-dimensional flattening recursively" do
+ a = [[[1, [2, 3]],[2, 3, [4, [4, [5, 5]], [1, 2, 3]]], [4]], []]
+ a.flatten!
+ a.should == [1, 2, 3, 2, 3, 4, 4, 5, 5, 1, 2, 3, 4]
+ end
+
+ it "returns self if made some modifications" do
+ a = [[[1, [2, 3]],[2, 3, [4, [4, [5, 5]], [1, 2, 3]]], [4]], []]
+ a.flatten!.should equal(a)
+ end
+
+ it "returns nil if no modifications took place" do
+ a = [1, 2, 3]
+ a.flatten!.should == nil
+ a = [1, [2, 3]]
+ a.flatten!.should_not == nil
+ end
+
+ it "should not check modification by size" do
+ a = [1, 2, [3]]
+ a.flatten!.should_not == nil
+ a.should == [1, 2, 3]
+ end
+
+ it "takes an optional argument that determines the level of recursion" do
+ [ 1, 2, [3, [4, 5] ] ].flatten!(1).should == [1, 2, 3, [4, 5]]
+ end
+
+ # redmine #1440
+ it "returns nil when the level of recursion is 0" do
+ a = [ 1, 2, [3, [4, 5] ] ]
+ a.flatten!(0).should == nil
+ end
+
+ it "treats negative levels as no arguments" do
+ [ 1, 2, [ 3, 4, [5, 6] ] ].flatten!(-1).should == [1, 2, 3, 4, 5, 6]
+ [ 1, 2, [ 3, 4, [5, 6] ] ].flatten!(-10).should == [1, 2, 3, 4, 5, 6]
+ end
+
+ it "tries to convert passed Objects to Integers using #to_int" do
+ obj = mock("Converted to Integer")
+ obj.should_receive(:to_int).and_return(1)
+
+ [ 1, 2, [3, [4, 5] ] ].flatten!(obj).should == [1, 2, 3, [4, 5]]
+ end
+
+ it "raises a TypeError when the passed Object can't be converted to an Integer" do
+ obj = mock("Not converted")
+ lambda { [ 1, 2, [3, [4, 5] ] ].flatten!(obj) }.should raise_error(TypeError)
+ end
+
+ it "does not call flatten! on elements" do
+ obj = mock('[1,2]')
+ obj.should_not_receive(:flatten!)
+ [obj, obj].flatten!.should == nil
+
+ obj = [5, 4]
+ obj.should_not_receive(:flatten!)
+ [obj, obj].flatten!.should == [5, 4, 5, 4]
+ end
+
+ it "raises an ArgumentError on recursive arrays" do
+ x = []
+ x << x
+ lambda { x.flatten! }.should raise_error(ArgumentError)
+
+ x = []
+ y = []
+ x << y
+ y << x
+ lambda { x.flatten! }.should raise_error(ArgumentError)
+ end
+
+ it "flattens any elements which responds to #to_ary, using the return value of said method" do
+ x = mock("[3,4]")
+ x.should_receive(:to_ary).at_least(:once).and_return([3, 4])
+ [1, 2, x, 5].flatten!.should == [1, 2, 3, 4, 5]
+
+ y = mock("MyArray[]")
+ y.should_receive(:to_ary).at_least(:once).and_return(ArraySpecs::MyArray[])
+ [y].flatten!.should == []
+
+ z = mock("[2,x,y,5]")
+ z.should_receive(:to_ary).and_return([2, x, y, 5])
+ [1, z, 6].flatten!.should == [1, 2, 3, 4, 5, 6]
+
+ ary = [ArraySpecs::MyArray[1, 2, 3]]
+ ary.flatten!
+ ary.should be_an_instance_of(Array)
+ ary.should == [1, 2, 3]
+ end
+
+ it "raises a #{frozen_error_class} on frozen arrays when the array is modified" do
+ nested_ary = [1, 2, []]
+ nested_ary.freeze
+ lambda { nested_ary.flatten! }.should raise_error(frozen_error_class)
+ end
+
+ # see [ruby-core:23663]
+ it "raises a #{frozen_error_class} on frozen arrays when the array would not be modified" do
+ lambda { ArraySpecs.frozen_array.flatten! }.should raise_error(frozen_error_class)
+ lambda { ArraySpecs.empty_frozen_array.flatten! }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/array/frozen_spec.rb b/spec/ruby/core/array/frozen_spec.rb
new file mode 100644
index 0000000000..bb4b2b4067
--- /dev/null
+++ b/spec/ruby/core/array/frozen_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#frozen?" do
+ it "returns true if array is frozen" do
+ a = [1, 2, 3]
+ a.frozen?.should == false
+ a.freeze
+ a.frozen?.should == true
+ end
+
+ it "returns false for an array being sorted by #sort" do
+ a = [1, 2, 3]
+ a.sort { |x,y| a.frozen?.should == false; x <=> y }
+ end
+end
diff --git a/spec/ruby/core/array/hash_spec.rb b/spec/ruby/core/array/hash_spec.rb
new file mode 100644
index 0000000000..638acff12b
--- /dev/null
+++ b/spec/ruby/core/array/hash_spec.rb
@@ -0,0 +1,83 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#hash" do
+ it "returns the same fixnum for arrays with the same content" do
+ [].respond_to?(:hash).should == true
+
+ [[], [1, 2, 3]].each do |ary|
+ ary.hash.should == ary.dup.hash
+ ary.hash.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ lambda { empty.hash }.should_not raise_error
+
+ array = ArraySpecs.recursive_array
+ lambda { array.hash }.should_not raise_error
+ end
+
+ it "returns the same hash for equal recursive arrays" do
+ rec = []; rec << rec
+ rec.hash.should == [rec].hash
+ rec.hash.should == [[rec]].hash
+ # This is because rec.eql?([[rec]])
+ # Remember that if two objects are eql?
+ # then the need to have the same hash
+ # Check the Array#eql? specs!
+ end
+
+ it "returns the same hash for equal recursive arrays through hashes" do
+ h = {} ; rec = [h] ; h[:x] = rec
+ rec.hash.should == [h].hash
+ rec.hash.should == [{x: rec}].hash
+ # Like above, this is because rec.eql?([{x: rec}])
+ end
+
+ it "calls to_int on result of calling hash on each element" do
+ ary = Array.new(5) do
+ obj = mock('0')
+ obj.should_receive(:hash).and_return(obj)
+ obj.should_receive(:to_int).and_return(0)
+ obj
+ end
+
+ ary.hash
+
+
+ hash = mock('1')
+ hash.should_receive(:to_int).and_return(1.hash)
+
+ obj = mock('@hash')
+ obj.instance_variable_set(:@hash, hash)
+ def obj.hash() @hash end
+
+ [obj].hash.should == [1].hash
+ end
+
+ it "ignores array class differences" do
+ ArraySpecs::MyArray[].hash.should == [].hash
+ ArraySpecs::MyArray[1, 2].hash.should == [1, 2].hash
+ end
+
+ it "returns same hash code for arrays with the same content" do
+ a = [1, 2, 3, 4]
+ a.fill 'a', 0..3
+ b = %w|a a a a|
+ a.hash.should == b.hash
+ end
+
+ it "returns the same value if arrays are #eql?" do
+ a = [1, 2, 3, 4]
+ a.fill 'a', 0..3
+ b = %w|a a a a|
+ a.hash.should == b.hash
+ a.should eql(b)
+ end
+
+ it "produces different hashes for nested arrays with different values and empty terminator" do
+ [1, [1, []]].hash.should_not == [2, [2, []]].hash
+ end
+end
diff --git a/spec/ruby/core/array/include_spec.rb b/spec/ruby/core/array/include_spec.rb
new file mode 100644
index 0000000000..227173218f
--- /dev/null
+++ b/spec/ruby/core/array/include_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#include?" do
+ it "returns true if object is present, false otherwise" do
+ [1, 2, "a", "b"].include?("c").should == false
+ [1, 2, "a", "b"].include?("a").should == true
+ end
+
+ it "determines presence by using element == obj" do
+ o = mock('')
+
+ [1, 2, "a", "b"].include?(o).should == false
+
+ def o.==(other); other == 'a'; end
+
+ [1, 2, o, "b"].include?('a').should == true
+
+ [1, 2.0, 3].include?(2).should == true
+ end
+
+ it "calls == on elements from left to right until success" do
+ key = "x"
+ one = mock('one')
+ two = mock('two')
+ three = mock('three')
+ one.should_receive(:==).any_number_of_times.and_return(false)
+ two.should_receive(:==).any_number_of_times.and_return(true)
+ three.should_not_receive(:==)
+ ary = [one, two, three]
+ ary.include?(key).should == true
+ end
+end
diff --git a/spec/ruby/core/array/index_spec.rb b/spec/ruby/core/array/index_spec.rb
new file mode 100644
index 0000000000..3acb7d0ef3
--- /dev/null
+++ b/spec/ruby/core/array/index_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/index'
+
+describe "Array#index" do
+ it_behaves_like :array_index, :index
+end
diff --git a/spec/ruby/core/array/initialize_spec.rb b/spec/ruby/core/array/initialize_spec.rb
new file mode 100644
index 0000000000..350538a222
--- /dev/null
+++ b/spec/ruby/core/array/initialize_spec.rb
@@ -0,0 +1,156 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#initialize" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "is private" do
+ Array.should have_private_instance_method("initialize")
+ end
+
+ it "is called on subclasses" do
+ b = ArraySpecs::SubArray.new :size_or_array, :obj
+
+ b.should == []
+ ScratchPad.recorded.should == [:size_or_array, :obj]
+ end
+
+ it "preserves the object's identity even when changing its value" do
+ a = [1, 2, 3]
+ a.send(:initialize).should equal(a)
+ a.should_not == [1, 2, 3]
+ end
+
+ it "raises an ArgumentError if passed 3 or more arguments" do
+ lambda do
+ [1, 2].send :initialize, 1, 'x', true
+ end.should raise_error(ArgumentError)
+ lambda do
+ [1, 2].send(:initialize, 1, 'x', true) {}
+ end.should raise_error(ArgumentError)
+ end
+
+ it "raises a #{frozen_error_class} on frozen arrays" do
+ lambda do
+ ArraySpecs.frozen_array.send :initialize
+ end.should raise_error(frozen_error_class)
+ lambda do
+ ArraySpecs.frozen_array.send :initialize, ArraySpecs.frozen_array
+ end.should raise_error(frozen_error_class)
+ end
+
+ it "calls #to_ary to convert the value to an array, even if it's private" do
+ a = ArraySpecs::PrivateToAry.new
+ [].send(:initialize, a).should == [1, 2, 3]
+ end
+end
+
+describe "Array#initialize with no arguments" do
+ it "makes the array empty" do
+ [1, 2, 3].send(:initialize).should be_empty
+ end
+
+ it "does not use the given block" do
+ lambda{ [1, 2, 3].send(:initialize) { raise } }.should_not raise_error
+ end
+end
+
+describe "Array#initialize with (array)" do
+ it "replaces self with the other array" do
+ b = [4, 5, 6]
+ [1, 2, 3].send(:initialize, b).should == b
+ end
+
+ it "does not use the given block" do
+ lambda{ [1, 2, 3].send(:initialize) { raise } }.should_not raise_error
+ end
+
+ it "calls #to_ary to convert the value to an array" do
+ a = mock("array")
+ a.should_receive(:to_ary).and_return([1, 2])
+ a.should_not_receive(:to_int)
+ [].send(:initialize, a).should == [1, 2]
+ end
+
+ it "does not call #to_ary on instances of Array or subclasses of Array" do
+ a = [1, 2]
+ a.should_not_receive(:to_ary)
+ [].send(:initialize, a).should == a
+ end
+
+ it "raises a TypeError if an Array type argument and a default object" do
+ lambda { [].send(:initialize, [1, 2], 1) }.should raise_error(TypeError)
+ end
+end
+
+describe "Array#initialize with (size, object=nil)" do
+ it "sets the array to size and fills with the object" do
+ a = []
+ obj = [3]
+ a.send(:initialize, 2, obj).should == [obj, obj]
+ a[0].should equal(obj)
+ a[1].should equal(obj)
+
+ b = []
+ b.send(:initialize, 3, 14).should == [14, 14, 14]
+ b.should == [14, 14, 14]
+ end
+
+ it "sets the array to size and fills with nil when object is omitted" do
+ [].send(:initialize, 3).should == [nil, nil, nil]
+ end
+
+ it "raises an ArgumentError if size is negative" do
+ lambda { [].send(:initialize, -1, :a) }.should raise_error(ArgumentError)
+ lambda { [].send(:initialize, -1) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if size is too large" do
+ lambda { [].send(:initialize, fixnum_max+1) }.should raise_error(ArgumentError)
+ end
+
+ it "calls #to_int to convert the size argument to an Integer when object is given" do
+ obj = mock('1')
+ obj.should_receive(:to_int).and_return(1)
+ [].send(:initialize, obj, :a).should == [:a]
+ end
+
+ it "calls #to_int to convert the size argument to an Integer when object is not given" do
+ obj = mock('1')
+ obj.should_receive(:to_int).and_return(1)
+ [].send(:initialize, obj).should == [nil]
+ end
+
+ it "raises a TypeError if the size argument is not an Integer type" do
+ obj = mock('nonnumeric')
+ obj.stub!(:to_ary).and_return([1, 2])
+ lambda{ [].send(:initialize, obj, :a) }.should raise_error(TypeError)
+ end
+
+ it "yields the index of the element and sets the element to the value of the block" do
+ [].send(:initialize, 3) { |i| i.to_s }.should == ['0', '1', '2']
+ end
+
+ it "uses the block value instead of using the default value" do
+ lambda {
+ @result = [].send(:initialize, 3, :obj) { |i| i.to_s }
+ }.should complain(/block supersedes default value argument/)
+ @result.should == ['0', '1', '2']
+ end
+
+ it "returns the value passed to break" do
+ [].send(:initialize, 3) { break :a }.should == :a
+ end
+
+ it "sets the array to the values returned by the block before break is executed" do
+ a = [1, 2, 3]
+ a.send(:initialize, 3) do |i|
+ break if i == 2
+ i.to_s
+ end
+
+ a.should == ['0', '1']
+ end
+end
diff --git a/spec/ruby/core/array/insert_spec.rb b/spec/ruby/core/array/insert_spec.rb
new file mode 100644
index 0000000000..b65a14a6b7
--- /dev/null
+++ b/spec/ruby/core/array/insert_spec.rb
@@ -0,0 +1,78 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#insert" do
+ it "returns self" do
+ ary = []
+ ary.insert(0).should equal(ary)
+ ary.insert(0, :a).should equal(ary)
+ end
+
+ it "inserts objects before the element at index for non-negative index" do
+ ary = []
+ ary.insert(0, 3).should == [3]
+ ary.insert(0, 1, 2).should == [1, 2, 3]
+ ary.insert(0).should == [1, 2, 3]
+
+ # Let's just assume insert() always modifies the array from now on.
+ ary.insert(1, 'a').should == [1, 'a', 2, 3]
+ ary.insert(0, 'b').should == ['b', 1, 'a', 2, 3]
+ ary.insert(5, 'c').should == ['b', 1, 'a', 2, 3, 'c']
+ ary.insert(7, 'd').should == ['b', 1, 'a', 2, 3, 'c', nil, 'd']
+ ary.insert(10, 5, 4).should == ['b', 1, 'a', 2, 3, 'c', nil, 'd', nil, nil, 5, 4]
+ end
+
+ it "appends objects to the end of the array for index == -1" do
+ [1, 3, 3].insert(-1, 2, 'x', 0.5).should == [1, 3, 3, 2, 'x', 0.5]
+ end
+
+ it "inserts objects after the element at index with negative index" do
+ ary = []
+ ary.insert(-1, 3).should == [3]
+ ary.insert(-2, 2).should == [2, 3]
+ ary.insert(-3, 1).should == [1, 2, 3]
+ ary.insert(-2, -3).should == [1, 2, -3, 3]
+ ary.insert(-1, []).should == [1, 2, -3, 3, []]
+ ary.insert(-2, 'x', 'y').should == [1, 2, -3, 3, 'x', 'y', []]
+ ary = [1, 2, 3]
+ end
+
+ it "pads with nils if the index to be inserted to is past the end" do
+ [].insert(5, 5).should == [nil, nil, nil, nil, nil, 5]
+ end
+
+ it "can insert before the first element with a negative index" do
+ [1, 2, 3].insert(-4, -3).should == [-3, 1, 2, 3]
+ end
+
+ it "raises an IndexError if the negative index is out of bounds" do
+ lambda { [].insert(-2, 1) }.should raise_error(IndexError)
+ lambda { [1].insert(-3, 2) }.should raise_error(IndexError)
+ end
+
+ it "does nothing of no object is passed" do
+ [].insert(0).should == []
+ [].insert(-1).should == []
+ [].insert(10).should == []
+ [].insert(-2).should == []
+ end
+
+ it "tries to convert the passed position argument to an Integer using #to_int" do
+ obj = mock('2')
+ obj.should_receive(:to_int).and_return(2)
+ [].insert(obj, 'x').should == [nil, nil, 'x']
+ end
+
+ it "raises an ArgumentError if no argument passed" do
+ lambda { [].insert() }.should raise_error(ArgumentError)
+ end
+
+ it "raises a #{frozen_error_class} on frozen arrays when the array is modified" do
+ lambda { ArraySpecs.frozen_array.insert(0, 'x') }.should raise_error(frozen_error_class)
+ end
+
+ # see [ruby-core:23666]
+ it "raises a #{frozen_error_class} on frozen arrays when the array would not be modified" do
+ lambda { ArraySpecs.frozen_array.insert(0) }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/array/inspect_spec.rb b/spec/ruby/core/array/inspect_spec.rb
new file mode 100644
index 0000000000..0832224f5a
--- /dev/null
+++ b/spec/ruby/core/array/inspect_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/inspect'
+
+describe "Array#inspect" do
+ it_behaves_like :array_inspect, :inspect
+end
diff --git a/spec/ruby/core/array/intersection_spec.rb b/spec/ruby/core/array/intersection_spec.rb
new file mode 100644
index 0000000000..7bf2ec4dbe
--- /dev/null
+++ b/spec/ruby/core/array/intersection_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#&" do
+ it "creates an array with elements common to both arrays (intersection)" do
+ ([] & []).should == []
+ ([1, 2] & []).should == []
+ ([] & [1, 2]).should == []
+ ([ 1, 3, 5 ] & [ 1, 2, 3 ]).should == [1, 3]
+ end
+
+ it "creates an array with no duplicates" do
+ ([ 1, 1, 3, 5 ] & [ 1, 2, 3 ]).uniq!.should == nil
+ end
+
+ it "creates an array with elements in order they are first encountered" do
+ ([ 1, 2, 3, 2, 5 ] & [ 5, 2, 3, 4 ]).should == [2, 3, 5]
+ end
+
+ it "does not modify the original Array" do
+ a = [1, 1, 3, 5]
+ (a & [1, 2, 3]).should == [1, 3]
+ a.should == [1, 1, 3, 5]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ (empty & empty).should == empty
+
+ (ArraySpecs.recursive_array & []).should == []
+ ([] & ArraySpecs.recursive_array).should == []
+
+ (ArraySpecs.recursive_array & ArraySpecs.recursive_array).should == [1, 'two', 3.0, ArraySpecs.recursive_array]
+ end
+
+ it "tries to convert the passed argument to an Array using #to_ary" do
+ obj = mock('[1,2,3]')
+ obj.should_receive(:to_ary).and_return([1, 2, 3])
+ ([1, 2] & obj).should == ([1, 2])
+ end
+
+ it "determines equivalence between elements in the sense of eql?" do
+ not_supported_on :opal do
+ ([5.0, 4.0] & [5, 4]).should == []
+ end
+
+ str = "x"
+ ([str] & [str.dup]).should == [str]
+
+ obj1 = mock('1')
+ obj2 = mock('2')
+ obj1.stub!(:hash).and_return(0)
+ obj2.stub!(:hash).and_return(0)
+ obj1.should_receive(:eql?).at_least(1).and_return(true)
+ obj2.stub!(:eql?).and_return(true)
+
+ ([obj1] & [obj2]).should == [obj1]
+ ([obj1, obj1, obj2, obj2] & [obj2]).should == [obj1]
+
+ obj1 = mock('3')
+ obj2 = mock('4')
+ obj1.stub!(:hash).and_return(0)
+ obj2.stub!(:hash).and_return(0)
+ obj1.should_receive(:eql?).at_least(1).and_return(false)
+
+ ([obj1] & [obj2]).should == []
+ ([obj1, obj1, obj2, obj2] & [obj2]).should == [obj2]
+ end
+
+ it "does return subclass instances for Array subclasses" do
+ (ArraySpecs::MyArray[1, 2, 3] & []).should be_an_instance_of(Array)
+ (ArraySpecs::MyArray[1, 2, 3] & ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
+ ([] & ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
+ end
+
+ it "does not call to_ary on array subclasses" do
+ ([5, 6] & ArraySpecs::ToAryArray[1, 2, 5, 6]).should == [5, 6]
+ end
+
+ it "properly handles an identical item even when its #eql? isn't reflexive" do
+ x = mock('x')
+ x.stub!(:hash).and_return(42)
+ x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI.
+
+ ([x] & [x]).should == [x]
+ end
+end
diff --git a/spec/ruby/core/array/join_spec.rb b/spec/ruby/core/array/join_spec.rb
new file mode 100644
index 0000000000..16f0dcee7a
--- /dev/null
+++ b/spec/ruby/core/array/join_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/join'
+
+describe "Array#join" do
+ it_behaves_like :array_join_with_string_separator, :join
+ it_behaves_like :array_join_with_default_separator, :join
+
+ it "does not separate elements when the passed separator is nil" do
+ [1, 2, 3].join(nil).should == '123'
+ end
+
+ it "calls #to_str to convert the separator to a String" do
+ sep = mock("separator")
+ sep.should_receive(:to_str).and_return(", ")
+ [1, 2].join(sep).should == "1, 2"
+ end
+
+ it "does not call #to_str on the separator if the array is empty" do
+ sep = mock("separator")
+ sep.should_not_receive(:to_str)
+ [].join(sep).should == ""
+ end
+
+ it "raises a TypeError if the separator cannot be coerced to a String by calling #to_str" do
+ obj = mock("not a string")
+ lambda { [1, 2].join(obj) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed false as the separator" do
+ lambda { [1, 2].join(false) }.should raise_error(TypeError)
+ end
+end
+
+describe "Array#join with $," do
+ before :each do
+ @before_separator = $,
+ end
+
+ after :each do
+ $, = @before_separator
+ end
+
+ it "separates elements with default separator when the passed separator is nil" do
+ $, = "_"
+ [1, 2, 3].join(nil).should == '1_2_3'
+ end
+end
diff --git a/spec/ruby/core/array/keep_if_spec.rb b/spec/ruby/core/array/keep_if_spec.rb
new file mode 100644
index 0000000000..bf2bdeaf91
--- /dev/null
+++ b/spec/ruby/core/array/keep_if_spec.rb
@@ -0,0 +1,10 @@
+require_relative 'shared/keep_if'
+
+describe "Array#keep_if" do
+ it "returns the same array if no changes were made" do
+ array = [1, 2, 3]
+ array.keep_if { true }.should equal(array)
+ end
+
+ it_behaves_like :keep_if, :keep_if
+end
diff --git a/spec/ruby/core/array/last_spec.rb b/spec/ruby/core/array/last_spec.rb
new file mode 100644
index 0000000000..871f4a2352
--- /dev/null
+++ b/spec/ruby/core/array/last_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#last" do
+ it "returns the last element" do
+ [1, 1, 1, 1, 2].last.should == 2
+ end
+
+ it "returns nil if self is empty" do
+ [].last.should == nil
+ end
+
+ it "returns the last count elements if given a count" do
+ [1, 2, 3, 4, 5, 9].last(3).should == [4, 5, 9]
+ end
+
+ it "returns an empty array when passed a count on an empty array" do
+ [].last(0).should == []
+ [].last(1).should == []
+ end
+
+ it "returns an empty array when count == 0" do
+ [1, 2, 3, 4, 5].last(0).should == []
+ end
+
+ it "returns an array containing the last element when passed count == 1" do
+ [1, 2, 3, 4, 5].last(1).should == [5]
+ end
+
+ it "raises an ArgumentError when count is negative" do
+ lambda { [1, 2].last(-1) }.should raise_error(ArgumentError)
+ end
+
+ it "returns the entire array when count > length" do
+ [1, 2, 3, 4, 5, 9].last(10).should == [1, 2, 3, 4, 5, 9]
+ end
+
+ it "returns an array which is independent to the original when passed count" do
+ ary = [1, 2, 3, 4, 5]
+ ary.last(0).replace([1,2])
+ ary.should == [1, 2, 3, 4, 5]
+ ary.last(1).replace([1,2])
+ ary.should == [1, 2, 3, 4, 5]
+ ary.last(6).replace([1,2])
+ ary.should == [1, 2, 3, 4, 5]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.last.should equal(empty)
+
+ array = ArraySpecs.recursive_array
+ array.last.should equal(array)
+ end
+
+ it "tries to convert the passed argument to an Integer usinig #to_int" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(2)
+ [1, 2, 3, 4, 5].last(obj).should == [4, 5]
+ end
+
+ it "raises a TypeError if the passed argument is not numeric" do
+ lambda { [1,2].last(nil) }.should raise_error(TypeError)
+ lambda { [1,2].last("a") }.should raise_error(TypeError)
+
+ obj = mock("nonnumeric")
+ lambda { [1,2].last(obj) }.should raise_error(TypeError)
+ end
+
+ it "does not return subclass instance on Array subclasses" do
+ ArraySpecs::MyArray[].last(0).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[].last(2).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].last(0).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].last(1).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].last(2).should be_an_instance_of(Array)
+ end
+
+ it "is not destructive" do
+ a = [1, 2, 3]
+ a.last
+ a.should == [1, 2, 3]
+ a.last(2)
+ a.should == [1, 2, 3]
+ a.last(3)
+ a.should == [1, 2, 3]
+ end
+end
diff --git a/spec/ruby/core/array/length_spec.rb b/spec/ruby/core/array/length_spec.rb
new file mode 100644
index 0000000000..a90c001300
--- /dev/null
+++ b/spec/ruby/core/array/length_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/length'
+
+describe "Array#length" do
+ it_behaves_like :array_length, :length
+end
diff --git a/spec/ruby/core/array/map_spec.rb b/spec/ruby/core/array/map_spec.rb
new file mode 100644
index 0000000000..0c7f3afa8c
--- /dev/null
+++ b/spec/ruby/core/array/map_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/collect'
+
+describe "Array#map" do
+ it_behaves_like :array_collect, :map
+end
+
+describe "Array#map!" do
+ it_behaves_like :array_collect_b, :map!
+end
diff --git a/spec/ruby/core/array/max_spec.rb b/spec/ruby/core/array/max_spec.rb
new file mode 100644
index 0000000000..5d0423d1e4
--- /dev/null
+++ b/spec/ruby/core/array/max_spec.rb
@@ -0,0 +1,118 @@
+require_relative '../../spec_helper'
+
+describe "Array#max" do
+ ruby_version_is "2.4" do
+ it "is defined on Array" do
+ [1].method(:max).owner.should equal Array
+ end
+ end
+
+ it "returns nil with no values" do
+ [].max.should == nil
+ end
+
+ it "returns only element in one element array" do
+ [1].max.should == 1
+ end
+
+ it "returns largest value with multiple elements" do
+ [1,2].max.should == 2
+ [2,1].max.should == 2
+ end
+
+ describe "given a block with one argument" do
+ it "yields in turn the last length-1 values from the array" do
+ ary = []
+ result = [1,2,3,4,5].max {|x| ary << x; x}
+
+ ary.should == [2,3,4,5]
+ result.should == 5
+ end
+ end
+end
+
+# From Enumerable#max, copied for better readability
+describe "Array#max" do
+ before :each do
+ @a = [2, 4, 6, 8, 10]
+
+ @e_strs = ["333", "22", "666666", "1", "55555", "1010101010"]
+ @e_ints = [333, 22, 666666, 55555, 1010101010]
+ end
+
+ it "max should return the maximum element" do
+ [18, 42].max.should == 42
+ [2, 5, 3, 6, 1, 4].max.should == 6
+ end
+
+ it "returns the maximum element (basics cases)" do
+ [55].max.should == 55
+
+ [11,99].max.should == 99
+ [99,11].max.should == 99
+ [2, 33, 4, 11].max.should == 33
+
+ [1,2,3,4,5].max.should == 5
+ [5,4,3,2,1].max.should == 5
+ [1,4,3,5,2].max.should == 5
+ [5,5,5,5,5].max.should == 5
+
+ ["aa","tt"].max.should == "tt"
+ ["tt","aa"].max.should == "tt"
+ ["2","33","4","11"].max.should == "4"
+
+ @e_strs.max.should == "666666"
+ @e_ints.max.should == 1010101010
+ end
+
+ it "returns nil for an empty Enumerable" do
+ [].max.should == nil
+ end
+
+ it "raises a NoMethodError for elements without #<=>" do
+ lambda do
+ [BasicObject.new, BasicObject.new].max
+ end.should raise_error(NoMethodError)
+ end
+
+ it "raises an ArgumentError for incomparable elements" do
+ lambda do
+ [11,"22"].max
+ end.should raise_error(ArgumentError)
+ lambda do
+ [11,12,22,33].max{|a, b| nil}
+ end.should raise_error(ArgumentError)
+ end
+
+ it "returns the maximum element (with block)" do
+ # with a block
+ ["2","33","4","11"].max {|a,b| a <=> b }.should == "4"
+ [ 2 , 33 , 4 , 11 ].max {|a,b| a <=> b }.should == 33
+
+ ["2","33","4","11"].max {|a,b| b <=> a }.should == "11"
+ [ 2 , 33 , 4 , 11 ].max {|a,b| b <=> a }.should == 2
+
+ @e_strs.max {|a,b| a.length <=> b.length }.should == "1010101010"
+
+ @e_strs.max {|a,b| a <=> b }.should == "666666"
+ @e_strs.max {|a,b| a.to_i <=> b.to_i }.should == "1010101010"
+
+ @e_ints.max {|a,b| a <=> b }.should == 1010101010
+ @e_ints.max {|a,b| a.to_s <=> b.to_s }.should == 666666
+ end
+
+ it "returns the minimum for enumerables that contain nils" do
+ arr = [nil, nil, true]
+ arr.max { |a, b|
+ x = a.nil? ? 1 : a ? 0 : -1
+ y = b.nil? ? 1 : b ? 0 : -1
+ x <=> y
+ }.should == nil
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = [[1,2], [3,4,5], [6,7,8,9]]
+ multi.max.should == [6, 7, 8, 9]
+ end
+
+end
diff --git a/spec/ruby/core/array/min_spec.rb b/spec/ruby/core/array/min_spec.rb
new file mode 100644
index 0000000000..903fa69bb8
--- /dev/null
+++ b/spec/ruby/core/array/min_spec.rb
@@ -0,0 +1,123 @@
+require_relative '../../spec_helper'
+
+describe "Array#min" do
+ ruby_version_is "2.4" do
+ it "is defined on Array" do
+ [1].method(:max).owner.should equal Array
+ end
+ end
+
+ it "returns nil with no values" do
+ [].min.should == nil
+ end
+
+ it "returns only element in one element array" do
+ [1].min.should == 1
+ end
+
+ it "returns smallest value with multiple elements" do
+ [1,2].min.should == 1
+ [2,1].min.should == 1
+ end
+
+ describe "given a block with one argument" do
+ it "yields in turn the last length-1 values from the array" do
+ ary = []
+ result = [1,2,3,4,5].min {|x| ary << x; x}
+
+ ary.should == [2,3,4,5]
+ result.should == 1
+ end
+ end
+end
+
+# From Enumerable#min, copied for better readability
+describe "Array#min" do
+ before :each do
+ @a = [2, 4, 6, 8, 10]
+
+ @e_strs = ["333", "22", "666666", "1", "55555", "1010101010"]
+ @e_ints = [ 333, 22, 666666, 55555, 1010101010]
+ end
+
+ it "min should return the minimum element" do
+ [18, 42].min.should == 18
+ [2, 5, 3, 6, 1, 4].min.should == 1
+ end
+
+ it "returns the minimum (basic cases)" do
+ [55].min.should == 55
+
+ [11,99].min.should == 11
+ [99,11].min.should == 11
+ [2, 33, 4, 11].min.should == 2
+
+ [1,2,3,4,5].min.should == 1
+ [5,4,3,2,1].min.should == 1
+ [4,1,3,5,2].min.should == 1
+ [5,5,5,5,5].min.should == 5
+
+ ["aa","tt"].min.should == "aa"
+ ["tt","aa"].min.should == "aa"
+ ["2","33","4","11"].min.should == "11"
+
+ @e_strs.min.should == "1"
+ @e_ints.min.should == 22
+ end
+
+ it "returns nil for an empty Enumerable" do
+ [].min.should be_nil
+ end
+
+ it "raises a NoMethodError for elements without #<=>" do
+ lambda do
+ [BasicObject.new, BasicObject.new].min
+ end.should raise_error(NoMethodError)
+ end
+
+ it "raises an ArgumentError for incomparable elements" do
+ lambda do
+ [11,"22"].min
+ end.should raise_error(ArgumentError)
+ lambda do
+ [11,12,22,33].min{|a, b| nil}
+ end.should raise_error(ArgumentError)
+ end
+
+ it "returns the minimum when using a block rule" do
+ ["2","33","4","11"].min {|a,b| a <=> b }.should == "11"
+ [ 2 , 33 , 4 , 11 ].min {|a,b| a <=> b }.should == 2
+
+ ["2","33","4","11"].min {|a,b| b <=> a }.should == "4"
+ [ 2 , 33 , 4 , 11 ].min {|a,b| b <=> a }.should == 33
+
+ [ 1, 2, 3, 4 ].min {|a,b| 15 }.should == 1
+
+ [11,12,22,33].min{|a, b| 2 }.should == 11
+ @i = -2
+ [11,12,22,33].min{|a, b| @i += 1 }.should == 12
+
+ @e_strs.min {|a,b| a.length <=> b.length }.should == "1"
+
+ @e_strs.min {|a,b| a <=> b }.should == "1"
+ @e_strs.min {|a,b| a.to_i <=> b.to_i }.should == "1"
+
+ @e_ints.min {|a,b| a <=> b }.should == 22
+ @e_ints.min {|a,b| a.to_s <=> b.to_s }.should == 1010101010
+ end
+
+ it "returns the minimum for enumerables that contain nils" do
+ arr = [nil, nil, true]
+ arr.min { |a, b|
+ x = a.nil? ? -1 : a ? 0 : 1
+ y = b.nil? ? -1 : b ? 0 : 1
+ x <=> y
+ }.should == nil
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = [[1,2], [3,4,5], [6,7,8,9]]
+ multi.min.should == [1, 2]
+ end
+
+end
diff --git a/spec/ruby/core/array/minus_spec.rb b/spec/ruby/core/array/minus_spec.rb
new file mode 100644
index 0000000000..8cb6bb323c
--- /dev/null
+++ b/spec/ruby/core/array/minus_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#-" do
+ it "creates an array minus any items from other array" do
+ ([] - [ 1, 2, 4 ]).should == []
+ ([1, 2, 4] - []).should == [1, 2, 4]
+ ([ 1, 2, 3, 4, 5 ] - [ 1, 2, 4 ]).should == [3, 5]
+ end
+
+ it "removes multiple items on the lhs equal to one on the rhs" do
+ ([1, 1, 2, 2, 3, 3, 4, 5] - [1, 2, 4]).should == [3, 3, 5]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ (empty - empty).should == []
+
+ ([] - ArraySpecs.recursive_array).should == []
+
+ array = ArraySpecs.recursive_array
+ (array - array).should == []
+ end
+
+ it "tries to convert the passed arguments to Arrays using #to_ary" do
+ obj = mock('[2,3,3,4]')
+ obj.should_receive(:to_ary).and_return([2, 3, 3, 4])
+ ([1, 1, 2, 2, 3, 4] - obj).should == [1, 1]
+ end
+
+ it "raises a TypeError if the argument cannot be coerced to an Array by calling #to_ary" do
+ obj = mock('not an array')
+ lambda { [1, 2, 3] - obj }.should raise_error(TypeError)
+ end
+
+ it "does not return subclass instance for Array subclasses" do
+ (ArraySpecs::MyArray[1, 2, 3] - []).should be_an_instance_of(Array)
+ (ArraySpecs::MyArray[1, 2, 3] - ArraySpecs::MyArray[]).should be_an_instance_of(Array)
+ ([1, 2, 3] - ArraySpecs::MyArray[]).should be_an_instance_of(Array)
+ end
+
+ it "does not call to_ary on array subclasses" do
+ ([5, 6, 7] - ArraySpecs::ToAryArray[7]).should == [5, 6]
+ end
+
+ it "removes an item identified as equivalent via #hash and #eql?" do
+ obj1 = mock('1')
+ obj2 = mock('2')
+ obj1.stub!(:hash).and_return(0)
+ obj2.stub!(:hash).and_return(0)
+ obj1.should_receive(:eql?).at_least(1).and_return(true)
+
+ ([obj1] - [obj2]).should == []
+ ([obj1, obj1, obj2, obj2] - [obj2]).should == []
+ end
+
+ it "doesn't remove an item with the same hash but not #eql?" do
+ obj1 = mock('1')
+ obj2 = mock('2')
+ obj1.stub!(:hash).and_return(0)
+ obj2.stub!(:hash).and_return(0)
+ obj1.should_receive(:eql?).at_least(1).and_return(false)
+
+ ([obj1] - [obj2]).should == [obj1]
+ ([obj1, obj1, obj2, obj2] - [obj2]).should == [obj1, obj1]
+ end
+
+ it "removes an identical item even when its #eql? isn't reflexive" do
+ x = mock('x')
+ x.stub!(:hash).and_return(42)
+ x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI.
+
+ ([x] - [x]).should == []
+ end
+
+ it "is not destructive" do
+ a = [1, 2, 3]
+ a - []
+ a.should == [1, 2, 3]
+ a - [1]
+ a.should == [1, 2, 3]
+ a - [1,2,3]
+ a.should == [1, 2, 3]
+ a - [:a, :b, :c]
+ a.should == [1, 2, 3]
+ end
+end
diff --git a/spec/ruby/core/array/multiply_spec.rb b/spec/ruby/core/array/multiply_spec.rb
new file mode 100644
index 0000000000..f9ba20258a
--- /dev/null
+++ b/spec/ruby/core/array/multiply_spec.rb
@@ -0,0 +1,132 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/join'
+
+describe "Array#*" do
+ it "tries to convert the passed argument to a String using #to_str" do
+ obj = mock('separator')
+ obj.should_receive(:to_str).and_return('::')
+ ([1, 2, 3, 4] * obj).should == '1::2::3::4'
+ end
+
+ it "tires to convert the passed argument to an Integer using #to_int" do
+ obj = mock('count')
+ obj.should_receive(:to_int).and_return(2)
+ ([1, 2, 3, 4] * obj).should == [1, 2, 3, 4, 1, 2, 3, 4]
+ end
+
+ it "raises a TypeError if the argument can neither be converted to a string nor an integer" do
+ obj = mock('not a string or integer')
+ lambda{ [1,2] * obj }.should raise_error(TypeError)
+ end
+
+ it "converts the passed argument to a String rather than an Integer" do
+ obj = mock('2')
+ def obj.to_int() 2 end
+ def obj.to_str() "2" end
+ ([:a, :b, :c] * obj).should == "a2b2c"
+ end
+
+ it "raises a TypeError is the passed argument is nil" do
+ lambda{ [1,2] * nil }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError when passed 2 or more arguments" do
+ lambda{ [1,2].send(:*, 1, 2) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed no arguments" do
+ lambda{ [1,2].send(:*) }.should raise_error(ArgumentError)
+ end
+end
+
+describe "Array#* with an integer" do
+ it "concatenates n copies of the array when passed an integer" do
+ ([ 1, 2, 3 ] * 0).should == []
+ ([ 1, 2, 3 ] * 1).should == [1, 2, 3]
+ ([ 1, 2, 3 ] * 3).should == [1, 2, 3, 1, 2, 3, 1, 2, 3]
+ ([] * 10).should == []
+ end
+
+ it "does not return self even if the passed integer is 1" do
+ ary = [1, 2, 3]
+ (ary * 1).should_not equal(ary)
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ (empty * 0).should == []
+ (empty * 1).should == empty
+ (empty * 3).should == [empty, empty, empty]
+
+ array = ArraySpecs.recursive_array
+ (array * 0).should == []
+ (array * 1).should == array
+ end
+
+ it "raises an ArgumentError when passed a negative integer" do
+ lambda { [ 1, 2, 3 ] * -1 }.should raise_error(ArgumentError)
+ lambda { [] * -1 }.should raise_error(ArgumentError)
+ end
+
+ describe "with a subclass of Array" do
+ before :each do
+ ScratchPad.clear
+
+ @array = ArraySpecs::MyArray[1, 2, 3, 4, 5]
+ end
+
+ it "returns a subclass instance" do
+ (@array * 0).should be_an_instance_of(ArraySpecs::MyArray)
+ (@array * 1).should be_an_instance_of(ArraySpecs::MyArray)
+ (@array * 2).should be_an_instance_of(ArraySpecs::MyArray)
+ end
+
+ it "does not call #initialize on the subclass instance" do
+ (@array * 2).should == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
+ ScratchPad.recorded.should be_nil
+ end
+ end
+
+ it "copies the taint status of the original array even if the passed count is 0" do
+ ary = [1, 2, 3]
+ ary.taint
+ (ary * 0).tainted?.should == true
+ end
+
+ it "copies the taint status of the original array even if the array is empty" do
+ ary = []
+ ary.taint
+ (ary * 3).tainted?.should == true
+ end
+
+ it "copies the taint status of the original array if the passed count is not 0" do
+ ary = [1, 2, 3]
+ ary.taint
+ (ary * 1).tainted?.should == true
+ (ary * 2).tainted?.should == true
+ end
+
+ it "copies the untrusted status of the original array even if the passed count is 0" do
+ ary = [1, 2, 3]
+ ary.untrust
+ (ary * 0).untrusted?.should == true
+ end
+
+ it "copies the untrusted status of the original array even if the array is empty" do
+ ary = []
+ ary.untrust
+ (ary * 3).untrusted?.should == true
+ end
+
+ it "copies the untrusted status of the original array if the passed count is not 0" do
+ ary = [1, 2, 3]
+ ary.untrust
+ (ary * 1).untrusted?.should == true
+ (ary * 2).untrusted?.should == true
+ end
+end
+
+describe "Array#* with a string" do
+ it_behaves_like :array_join_with_string_separator, :*
+end
diff --git a/spec/ruby/core/array/new_spec.rb b/spec/ruby/core/array/new_spec.rb
new file mode 100644
index 0000000000..d5e4b5722f
--- /dev/null
+++ b/spec/ruby/core/array/new_spec.rb
@@ -0,0 +1,122 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array.new" do
+ it "returns an instance of Array" do
+ Array.new.should be_an_instance_of(Array)
+ end
+
+ it "returns an instance of a subclass" do
+ ArraySpecs::MyArray.new(1, 2).should be_an_instance_of(ArraySpecs::MyArray)
+ end
+
+ it "raises an ArgumentError if passed 3 or more arguments" do
+ lambda do
+ [1, 2].send :initialize, 1, 'x', true
+ end.should raise_error(ArgumentError)
+ lambda do
+ [1, 2].send(:initialize, 1, 'x', true) {}
+ end.should raise_error(ArgumentError)
+ end
+end
+
+describe "Array.new with no arguments" do
+ it "returns an empty array" do
+ Array.new.should be_empty
+ end
+
+ it "does not use the given block" do
+ lambda{ Array.new { raise } }.should_not raise_error
+ end
+end
+
+describe "Array.new with (array)" do
+ it "returns an array initialized to the other array" do
+ b = [4, 5, 6]
+ Array.new(b).should == b
+ end
+
+ it "does not use the given block" do
+ lambda{ Array.new([1, 2]) { raise } }.should_not raise_error
+ end
+
+ it "calls #to_ary to convert the value to an array" do
+ a = mock("array")
+ a.should_receive(:to_ary).and_return([1, 2])
+ a.should_not_receive(:to_int)
+ Array.new(a).should == [1, 2]
+ end
+
+ it "does not call #to_ary on instances of Array or subclasses of Array" do
+ a = [1, 2]
+ a.should_not_receive(:to_ary)
+ Array.new(a)
+ end
+
+ it "raises a TypeError if an Array type argument and a default object" do
+ lambda { Array.new([1, 2], 1) }.should raise_error(TypeError)
+ end
+end
+
+describe "Array.new with (size, object=nil)" do
+ it "returns an array of size filled with object" do
+ obj = [3]
+ a = Array.new(2, obj)
+ a.should == [obj, obj]
+ a[0].should equal(obj)
+ a[1].should equal(obj)
+
+ Array.new(3, 14).should == [14, 14, 14]
+ end
+
+ it "returns an array of size filled with nil when object is omitted" do
+ Array.new(3).should == [nil, nil, nil]
+ end
+
+ it "raises an ArgumentError if size is negative" do
+ lambda { Array.new(-1, :a) }.should raise_error(ArgumentError)
+ lambda { Array.new(-1) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if size is too large" do
+ lambda { Array.new(fixnum_max+1) }.should raise_error(ArgumentError)
+ end
+
+ it "calls #to_int to convert the size argument to an Integer when object is given" do
+ obj = mock('1')
+ obj.should_receive(:to_int).and_return(1)
+ Array.new(obj, :a).should == [:a]
+ end
+
+ it "calls #to_int to convert the size argument to an Integer when object is not given" do
+ obj = mock('1')
+ obj.should_receive(:to_int).and_return(1)
+ Array.new(obj).should == [nil]
+ end
+
+ it "raises a TypeError if the size argument is not an Integer type" do
+ obj = mock('nonnumeric')
+ obj.stub!(:to_ary).and_return([1, 2])
+ lambda{ Array.new(obj, :a) }.should raise_error(TypeError)
+ end
+
+ it "yields the index of the element and sets the element to the value of the block" do
+ Array.new(3) { |i| i.to_s }.should == ['0', '1', '2']
+ end
+
+ it "uses the block value instead of using the default value" do
+ lambda {
+ @result = Array.new(3, :obj) { |i| i.to_s }
+ }.should complain(/block supersedes default value argument/)
+ @result.should == ['0', '1', '2']
+ end
+
+ it "returns the value passed to break" do
+ a = Array.new(3) do |i|
+ break if i == 2
+ i.to_s
+ end
+
+ a.should == nil
+ end
+end
diff --git a/spec/ruby/core/array/pack/a_spec.rb b/spec/ruby/core/array/pack/a_spec.rb
new file mode 100644
index 0000000000..1cee3858ff
--- /dev/null
+++ b/spec/ruby/core/array/pack/a_spec.rb
@@ -0,0 +1,62 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/string'
+require_relative 'shared/taint'
+
+describe "Array#pack with format 'A'" do
+ it_behaves_like :array_pack_basic, 'A'
+ it_behaves_like :array_pack_basic_non_float, 'A'
+ it_behaves_like :array_pack_no_platform, 'A'
+ it_behaves_like :array_pack_string, 'A'
+ it_behaves_like :array_pack_taint, 'A'
+
+ it "adds all the bytes to the output when passed the '*' modifier" do
+ ["abc"].pack("A*").should == "abc"
+ end
+
+ it "padds the output with spaces when the count exceeds the size of the String" do
+ ["abc"].pack("A6").should == "abc "
+ end
+
+ it "adds a space when the value is nil" do
+ [nil].pack("A").should == " "
+ end
+
+ it "pads the output with spaces when the value is nil" do
+ [nil].pack("A3").should == " "
+ end
+
+ it "does not pad with spaces when passed the '*' modifier and the value is nil" do
+ [nil].pack("A*").should == ""
+ end
+end
+
+describe "Array#pack with format 'a'" do
+ it_behaves_like :array_pack_basic, 'a'
+ it_behaves_like :array_pack_basic_non_float, 'a'
+ it_behaves_like :array_pack_no_platform, 'a'
+ it_behaves_like :array_pack_string, 'a'
+ it_behaves_like :array_pack_taint, 'a'
+
+ it "adds all the bytes to the output when passed the '*' modifier" do
+ ["abc"].pack("a*").should == "abc"
+ end
+
+ it "padds the output with NULL bytes when the count exceeds the size of the String" do
+ ["abc"].pack("a6").should == "abc\x00\x00\x00"
+ end
+
+ it "adds a NULL byte when the value is nil" do
+ [nil].pack("a").should == "\x00"
+ end
+
+ it "pads the output with NULL bytes when the value is nil" do
+ [nil].pack("a3").should == "\x00\x00\x00"
+ end
+
+ it "does not pad with NULL bytes when passed the '*' modifier and the value is nil" do
+ [nil].pack("a*").should == ""
+ end
+end
diff --git a/spec/ruby/core/array/pack/at_spec.rb b/spec/ruby/core/array/pack/at_spec.rb
new file mode 100644
index 0000000000..00763c70f1
--- /dev/null
+++ b/spec/ruby/core/array/pack/at_spec.rb
@@ -0,0 +1,30 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+
+describe "Array#pack with format '@'" do
+ it_behaves_like :array_pack_basic, '@'
+ it_behaves_like :array_pack_basic_non_float, '@'
+ it_behaves_like :array_pack_no_platform, '@'
+
+ it "moves the insertion point to the index specified by the count modifier" do
+ [1, 2, 3, 4, 5].pack("C4@2C").should == "\x01\x02\x05"
+ end
+
+ it "does not consume any elements" do
+ [1, 2, 3].pack("C@3C").should == "\x01\x00\x00\x02"
+ end
+
+ it "extends the string with NULL bytes if the string size is less than the count" do
+ [1, 2, 3].pack("@3C*").should == "\x00\x00\x00\x01\x02\x03"
+ end
+
+ it "truncates the string if the string size is greater than the count" do
+ [1, 2, 3].pack("Cx5@2C").should == "\x01\x00\x02"
+ end
+
+ it "implicitly has a count of one when no count modifier is passed" do
+ [1, 2, 3].pack("C*@").should == "\x01"
+ end
+end
diff --git a/spec/ruby/core/array/pack/b_spec.rb b/spec/ruby/core/array/pack/b_spec.rb
new file mode 100644
index 0000000000..8a75825e3e
--- /dev/null
+++ b/spec/ruby/core/array/pack/b_spec.rb
@@ -0,0 +1,108 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/encodings'
+require_relative 'shared/taint'
+
+describe "Array#pack with format 'B'" do
+ it_behaves_like :array_pack_basic, 'B'
+ it_behaves_like :array_pack_basic_non_float, 'B'
+ it_behaves_like :array_pack_arguments, 'B'
+ it_behaves_like :array_pack_hex, 'B'
+ it_behaves_like :array_pack_taint, 'B'
+
+ it "calls #to_str to convert an Object to a String" do
+ obj = mock("pack H string")
+ obj.should_receive(:to_str).and_return("``abcdef")
+ [obj].pack("B*").should == "\x2a"
+ end
+
+ it "encodes one bit for each character starting with the most significant bit" do
+ [ [["0"], "\x00"],
+ [["1"], "\x80"]
+ ].should be_computed_by(:pack, "B")
+ end
+
+ it "implicitly has a count of one when not passed a count modifier" do
+ ["1"].pack("B").should == "\x80"
+ end
+
+ it "implicitly has count equal to the string length when passed the '*' modifier" do
+ [ [["00101010"], "\x2a"],
+ [["00000000"], "\x00"],
+ [["11111111"], "\xff"],
+ [["10000000"], "\x80"],
+ [["00000001"], "\x01"]
+ ].should be_computed_by(:pack, "B*")
+ end
+
+ it "encodes the least significant bit of a character other than 0 or 1" do
+ [ [["bbababab"], "\x2a"],
+ [["^&#&#^#^"], "\x2a"],
+ [["(()()()("], "\x2a"],
+ [["@@%@%@%@"], "\x2a"],
+ [["ppqrstuv"], "\x2a"],
+ [["rqtvtrqp"], "\x42"]
+ ].should be_computed_by(:pack, "B*")
+ end
+
+ it "returns an ASCII-8BIT string" do
+ ["1"].pack("B").encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "encodes the string as a sequence of bytes" do
+ ["ã‚ã‚ã‚ã‚ã‚ã‚ã‚ã‚"].pack("B*").should == "\xdbm\xb6"
+ end
+end
+
+describe "Array#pack with format 'b'" do
+ it_behaves_like :array_pack_basic, 'b'
+ it_behaves_like :array_pack_basic_non_float, 'b'
+ it_behaves_like :array_pack_arguments, 'b'
+ it_behaves_like :array_pack_hex, 'b'
+ it_behaves_like :array_pack_taint, 'b'
+
+ it "calls #to_str to convert an Object to a String" do
+ obj = mock("pack H string")
+ obj.should_receive(:to_str).and_return("`abcdef`")
+ [obj].pack("b*").should == "\x2a"
+ end
+
+ it "encodes one bit for each character starting with the least significant bit" do
+ [ [["0"], "\x00"],
+ [["1"], "\x01"]
+ ].should be_computed_by(:pack, "b")
+ end
+
+ it "implicitly has a count of one when not passed a count modifier" do
+ ["1"].pack("b").should == "\x01"
+ end
+
+ it "implicitly has count equal to the string length when passed the '*' modifier" do
+ [ [["0101010"], "\x2a"],
+ [["00000000"], "\x00"],
+ [["11111111"], "\xff"],
+ [["10000000"], "\x01"],
+ [["00000001"], "\x80"]
+ ].should be_computed_by(:pack, "b*")
+ end
+
+ it "encodes the least significant bit of a character other than 0 or 1" do
+ [ [["bababab"], "\x2a"],
+ [["&#&#^#^"], "\x2a"],
+ [["()()()("], "\x2a"],
+ [["@%@%@%@"], "\x2a"],
+ [["pqrstuv"], "\x2a"],
+ [["qrtrtvs"], "\x41"]
+ ].should be_computed_by(:pack, "b*")
+ end
+
+ it "returns an ASCII-8BIT string" do
+ ["1"].pack("b").encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "encodes the string as a sequence of bytes" do
+ ["ã‚ã‚ã‚ã‚ã‚ã‚ã‚ã‚"].pack("b*").should == "\xdb\xb6m"
+ end
+end
diff --git a/spec/ruby/core/array/pack/buffer_spec.rb b/spec/ruby/core/array/pack/buffer_spec.rb
new file mode 100644
index 0000000000..f2dc3e1930
--- /dev/null
+++ b/spec/ruby/core/array/pack/buffer_spec.rb
@@ -0,0 +1,52 @@
+# encoding: ascii-8bit
+
+require_relative '../../../spec_helper'
+
+ruby_version_is '2.4' do
+ describe "Array#pack with :buffer option" do
+ it "returns specified buffer" do
+ n = [ 65, 66, 67 ]
+ buffer = " "*3
+ result = n.pack("ccc", buffer: buffer) #=> "ABC"
+ result.should equal(buffer)
+ end
+
+ it "adds result at the end of buffer content" do
+ n = [ 65, 66, 67 ] # result without buffer is "ABC"
+
+ buffer = ""
+ n.pack("ccc", buffer: buffer).should == "ABC"
+
+ buffer = "123"
+ n.pack("ccc", buffer: buffer).should == "123ABC"
+
+ buffer = "12345"
+ n.pack("ccc", buffer: buffer).should == "12345ABC"
+ end
+
+ it "raises TypeError exception if buffer is not String" do
+ lambda { [65].pack("ccc", buffer: []) }.should raise_error(
+ TypeError, "buffer must be String, not Array")
+ end
+
+ context "offset (@) is specified" do
+ it 'keeps buffer content if it is longer than offset' do
+ n = [ 65, 66, 67 ]
+ buffer = "123456"
+ n.pack("@3ccc", buffer: buffer).should == "123ABC"
+ end
+
+ it "fills the gap with \\0 if buffer content is shorter than offset" do
+ n = [ 65, 66, 67 ]
+ buffer = "123"
+ n.pack("@6ccc", buffer: buffer).should == "123\0\0\0ABC"
+ end
+
+ it 'does not keep buffer content if it is longer than offset + result' do
+ n = [ 65, 66, 67 ]
+ buffer = "1234567890"
+ n.pack("@3ccc", buffer: buffer).should == "123ABC"
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/array/pack/c_spec.rb b/spec/ruby/core/array/pack/c_spec.rb
new file mode 100644
index 0000000000..225f0970fd
--- /dev/null
+++ b/spec/ruby/core/array/pack/c_spec.rb
@@ -0,0 +1,75 @@
+# -*- encoding: ascii-8bit -*-
+
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/numeric_basic'
+
+describe :array_pack_8bit, shared: true do
+ it "encodes the least significant eight bits of a positive number" do
+ [ [[49], "1"],
+ [[0b11111111], "\xFF"],
+ [[0b100000000], "\x00"],
+ [[0b100000001], "\x01"]
+ ].should be_computed_by(:pack, pack_format)
+ end
+
+ it "encodes the least significant eight bits of a negative number" do
+ [ [[-1], "\xFF"],
+ [[-0b10000000], "\x80"],
+ [[-0b11111111], "\x01"],
+ [[-0b100000000], "\x00"],
+ [[-0b100000001], "\xFF"]
+ ].should be_computed_by(:pack, pack_format)
+ end
+
+ it "encodes a Float truncated as an Integer" do
+ [ [[5.2], "\x05"],
+ [[5.8], "\x05"]
+ ].should be_computed_by(:pack, pack_format)
+ end
+
+ it "calls #to_int to convert the pack argument to an Integer" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(5)
+ [obj].pack(pack_format).should == "\x05"
+ end
+
+ it "encodes the number of array elements specified by the count modifier" do
+ [ [[1, 2, 3], pack_format(3), "\x01\x02\x03"],
+ [[1, 2, 3], pack_format(2) + pack_format(1), "\x01\x02\x03"]
+ ].should be_computed_by(:pack)
+ end
+
+ it "encodes all remaining elements when passed the '*' modifier" 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"
+ end
+
+ it "ignores spaces between directives" do
+ [1, 2, 3].pack(pack_format(' ', 2)).should == "\x01\x02"
+ end
+end
+
+describe "Array#pack with format 'C'" do
+ it_behaves_like :array_pack_basic, 'C'
+ it_behaves_like :array_pack_basic_non_float, 'C'
+ it_behaves_like :array_pack_8bit, 'C'
+ it_behaves_like :array_pack_arguments, 'C'
+ it_behaves_like :array_pack_numeric_basic, 'C'
+ it_behaves_like :array_pack_integer, 'C'
+ it_behaves_like :array_pack_no_platform, 'C'
+end
+
+describe "Array#pack with format 'c'" do
+ it_behaves_like :array_pack_basic, 'c'
+ it_behaves_like :array_pack_basic_non_float, 'c'
+ it_behaves_like :array_pack_8bit, 'c'
+ it_behaves_like :array_pack_arguments, 'c'
+ it_behaves_like :array_pack_numeric_basic, 'c'
+ it_behaves_like :array_pack_integer, 'c'
+ it_behaves_like :array_pack_no_platform, 'c'
+end
diff --git a/spec/ruby/core/array/pack/comment_spec.rb b/spec/ruby/core/array/pack/comment_spec.rb
new file mode 100644
index 0000000000..07daa2d6fb
--- /dev/null
+++ b/spec/ruby/core/array/pack/comment_spec.rb
@@ -0,0 +1,25 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Array#pack" do
+ it "ignores directives text from '#' to the first newline" do
+ [1, 2, 3].pack("c#this is a comment\nc").should == "\x01\x02"
+ end
+
+ it "ignores directives text from '#' to the end if no newline is present" do
+ [1, 2, 3].pack("c#this is a comment c").should == "\x01"
+ end
+
+ it "ignores comments at the start of the directives string" do
+ [1, 2, 3].pack("#this is a comment\nc").should == "\x01"
+ end
+
+ it "ignores the entire directive string if it is a comment" do
+ [1, 2, 3].pack("#this is a comment").should == ""
+ end
+
+ it "ignores multiple comments" do
+ [1, 2, 3].pack("c#comment\nc#comment\nc#c").should == "\x01\x02\x03"
+ end
+end
diff --git a/spec/ruby/core/array/pack/d_spec.rb b/spec/ruby/core/array/pack/d_spec.rb
new file mode 100644
index 0000000000..8bb3654633
--- /dev/null
+++ b/spec/ruby/core/array/pack/d_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/numeric_basic'
+require_relative 'shared/float'
+
+describe "Array#pack with format 'D'" do
+ it_behaves_like :array_pack_basic, 'D'
+ it_behaves_like :array_pack_basic_float, 'D'
+ it_behaves_like :array_pack_arguments, 'D'
+ it_behaves_like :array_pack_no_platform, 'D'
+ it_behaves_like :array_pack_numeric_basic, 'D'
+ it_behaves_like :array_pack_float, 'D'
+
+ little_endian do
+ it_behaves_like :array_pack_double_le, 'D'
+ end
+
+ big_endian do
+ it_behaves_like :array_pack_double_be, 'D'
+ end
+end
+
+describe "Array#pack with format 'd'" do
+ it_behaves_like :array_pack_basic, 'd'
+ it_behaves_like :array_pack_basic_float, 'd'
+ it_behaves_like :array_pack_arguments, 'd'
+ it_behaves_like :array_pack_no_platform, 'd'
+ it_behaves_like :array_pack_numeric_basic, 'd'
+ it_behaves_like :array_pack_float, 'd'
+
+ little_endian do
+ it_behaves_like :array_pack_double_le, 'd'
+ end
+
+ big_endian do
+ it_behaves_like :array_pack_double_be, 'd'
+ end
+end
diff --git a/spec/ruby/core/array/pack/e_spec.rb b/spec/ruby/core/array/pack/e_spec.rb
new file mode 100644
index 0000000000..ab61ef578f
--- /dev/null
+++ b/spec/ruby/core/array/pack/e_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/numeric_basic'
+require_relative 'shared/float'
+
+describe "Array#pack with format 'E'" do
+ it_behaves_like :array_pack_basic, 'E'
+ it_behaves_like :array_pack_basic_float, 'E'
+ it_behaves_like :array_pack_arguments, 'E'
+ it_behaves_like :array_pack_no_platform, 'E'
+ it_behaves_like :array_pack_numeric_basic, 'E'
+ it_behaves_like :array_pack_float, 'E'
+ it_behaves_like :array_pack_double_le, 'E'
+end
+
+describe "Array#pack with format 'e'" do
+ it_behaves_like :array_pack_basic, 'e'
+ it_behaves_like :array_pack_basic_float, 'e'
+ it_behaves_like :array_pack_arguments, 'e'
+ it_behaves_like :array_pack_no_platform, 'e'
+ it_behaves_like :array_pack_numeric_basic, 'e'
+ it_behaves_like :array_pack_float, 'e'
+ it_behaves_like :array_pack_float_le, 'e'
+end
diff --git a/spec/ruby/core/array/pack/empty_spec.rb b/spec/ruby/core/array/pack/empty_spec.rb
new file mode 100644
index 0000000000..d635d6a563
--- /dev/null
+++ b/spec/ruby/core/array/pack/empty_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+
+describe "Array#pack with empty format" do
+ it "returns an empty String" do
+ [1, 2, 3].pack("").should == ""
+ end
+
+ it "returns a String with US-ASCII encoding" do
+ [1, 2, 3].pack("").encoding.should == Encoding::US_ASCII
+ end
+end
diff --git a/spec/ruby/core/array/pack/f_spec.rb b/spec/ruby/core/array/pack/f_spec.rb
new file mode 100644
index 0000000000..d436e0787c
--- /dev/null
+++ b/spec/ruby/core/array/pack/f_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/numeric_basic'
+require_relative 'shared/float'
+
+describe "Array#pack with format 'F'" do
+ it_behaves_like :array_pack_basic, 'F'
+ it_behaves_like :array_pack_basic_float, 'F'
+ it_behaves_like :array_pack_arguments, 'F'
+ it_behaves_like :array_pack_no_platform, 'F'
+ it_behaves_like :array_pack_numeric_basic, 'F'
+ it_behaves_like :array_pack_float, 'F'
+
+ little_endian do
+ it_behaves_like :array_pack_float_le, 'F'
+ end
+
+ big_endian do
+ it_behaves_like :array_pack_float_be, 'F'
+ end
+end
+
+describe "Array#pack with format 'f'" do
+ it_behaves_like :array_pack_basic, 'f'
+ it_behaves_like :array_pack_basic_float, 'f'
+ it_behaves_like :array_pack_arguments, 'f'
+ it_behaves_like :array_pack_no_platform, 'f'
+ it_behaves_like :array_pack_numeric_basic, 'f'
+ it_behaves_like :array_pack_float, 'f'
+
+ little_endian do
+ it_behaves_like :array_pack_float_le, 'f'
+ end
+
+ big_endian do
+ it_behaves_like :array_pack_float_be, 'f'
+ end
+end
diff --git a/spec/ruby/core/array/pack/g_spec.rb b/spec/ruby/core/array/pack/g_spec.rb
new file mode 100644
index 0000000000..83b7f81acc
--- /dev/null
+++ b/spec/ruby/core/array/pack/g_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/numeric_basic'
+require_relative 'shared/float'
+
+describe "Array#pack with format 'G'" do
+ it_behaves_like :array_pack_basic, 'G'
+ it_behaves_like :array_pack_basic_float, 'G'
+ it_behaves_like :array_pack_arguments, 'G'
+ it_behaves_like :array_pack_no_platform, 'G'
+ it_behaves_like :array_pack_numeric_basic, 'G'
+ it_behaves_like :array_pack_float, 'G'
+ it_behaves_like :array_pack_double_be, 'G'
+end
+
+describe "Array#pack with format 'g'" do
+ it_behaves_like :array_pack_basic, 'g'
+ it_behaves_like :array_pack_basic_float, 'g'
+ it_behaves_like :array_pack_arguments, 'g'
+ it_behaves_like :array_pack_no_platform, 'g'
+ it_behaves_like :array_pack_numeric_basic, 'g'
+ it_behaves_like :array_pack_float, 'g'
+ it_behaves_like :array_pack_float_be, 'g'
+end
diff --git a/spec/ruby/core/array/pack/h_spec.rb b/spec/ruby/core/array/pack/h_spec.rb
new file mode 100644
index 0000000000..51bf551ba3
--- /dev/null
+++ b/spec/ruby/core/array/pack/h_spec.rb
@@ -0,0 +1,200 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/encodings'
+require_relative 'shared/taint'
+
+describe "Array#pack with format 'H'" do
+ it_behaves_like :array_pack_basic, 'H'
+ it_behaves_like :array_pack_basic_non_float, 'H'
+ it_behaves_like :array_pack_arguments, 'H'
+ it_behaves_like :array_pack_hex, 'H'
+ it_behaves_like :array_pack_taint, 'H'
+
+ it "calls #to_str to convert an Object to a String" do
+ obj = mock("pack H string")
+ obj.should_receive(:to_str).and_return("a")
+ [obj].pack("H").should == "\xa0"
+ end
+
+ it "encodes the first character as the most significant nibble when passed no count modifier" do
+ ["ab"].pack("H").should == "\xa0"
+ end
+
+ it "implicitly has count equal to the string length when passed the '*' modifier" do
+ ["deadbeef"].pack("H*").should == "\xde\xad\xbe\xef"
+ end
+
+ it "encodes count nibbles when passed a count modifier exceeding the string length" do
+ ["ab"].pack('H8').should == "\xab\x00\x00\x00"
+ end
+
+ it "encodes the first character as the most significant nibble of a hex value" do
+ [ [["0"], "\x00"],
+ [["1"], "\x10"],
+ [["2"], "\x20"],
+ [["3"], "\x30"],
+ [["4"], "\x40"],
+ [["5"], "\x50"],
+ [["6"], "\x60"],
+ [["7"], "\x70"],
+ [["8"], "\x80"],
+ [["9"], "\x90"],
+ [["a"], "\xa0"],
+ [["b"], "\xb0"],
+ [["c"], "\xc0"],
+ [["d"], "\xd0"],
+ [["e"], "\xe0"],
+ [["f"], "\xf0"],
+ [["A"], "\xa0"],
+ [["B"], "\xb0"],
+ [["C"], "\xc0"],
+ [["D"], "\xd0"],
+ [["E"], "\xe0"],
+ [["F"], "\xf0"]
+ ].should be_computed_by(:pack, "H")
+ end
+
+ it "encodes the second character as the least significant nibble of a hex value" do
+ [ [["00"], "\x00"],
+ [["01"], "\x01"],
+ [["02"], "\x02"],
+ [["03"], "\x03"],
+ [["04"], "\x04"],
+ [["05"], "\x05"],
+ [["06"], "\x06"],
+ [["07"], "\x07"],
+ [["08"], "\x08"],
+ [["09"], "\x09"],
+ [["0a"], "\x0a"],
+ [["0b"], "\x0b"],
+ [["0c"], "\x0c"],
+ [["0d"], "\x0d"],
+ [["0e"], "\x0e"],
+ [["0f"], "\x0f"],
+ [["0A"], "\x0a"],
+ [["0B"], "\x0b"],
+ [["0C"], "\x0c"],
+ [["0D"], "\x0d"],
+ [["0E"], "\x0e"],
+ [["0F"], "\x0f"]
+ ].should be_computed_by(:pack, "H2")
+ end
+
+ it "encodes the least significant nibble of a non alphanumeric character as the most significant nibble of the hex value" do
+ [ [["^"], "\xe0"],
+ [["*"], "\xa0"],
+ [["#"], "\x30"],
+ [["["], "\xb0"],
+ [["]"], "\xd0"],
+ [["@"], "\x00"],
+ [["!"], "\x10"],
+ [["H"], "\x10"],
+ [["O"], "\x80"],
+ [["T"], "\xd0"],
+ [["Z"], "\x30"],
+ ].should be_computed_by(:pack, "H")
+ end
+
+ it "returns an ASCII-8BIT string" do
+ ["41"].pack("H").encoding.should == Encoding::ASCII_8BIT
+ end
+end
+
+describe "Array#pack with format 'h'" do
+ it_behaves_like :array_pack_basic, 'h'
+ it_behaves_like :array_pack_basic_non_float, 'h'
+ it_behaves_like :array_pack_arguments, 'h'
+ it_behaves_like :array_pack_hex, 'h'
+ it_behaves_like :array_pack_taint, 'h'
+
+ it "calls #to_str to convert an Object to a String" do
+ obj = mock("pack H string")
+ obj.should_receive(:to_str).and_return("a")
+ [obj].pack("h").should == "\x0a"
+ end
+
+ it "encodes the first character as the least significant nibble when passed no count modifier" do
+ ["ab"].pack("h").should == "\x0a"
+ end
+
+ it "implicitly has count equal to the string length when passed the '*' modifier" do
+ ["deadbeef"].pack("h*").should == "\xed\xda\xeb\xfe"
+ end
+
+ it "encodes count nibbles when passed a count modifier exceeding the string length" do
+ ["ab"].pack('h8').should == "\xba\x00\x00\x00"
+ end
+
+ it "encodes the first character as the least significant nibble of a hex value" do
+ [ [["0"], "\x00"],
+ [["1"], "\x01"],
+ [["2"], "\x02"],
+ [["3"], "\x03"],
+ [["4"], "\x04"],
+ [["5"], "\x05"],
+ [["6"], "\x06"],
+ [["7"], "\x07"],
+ [["8"], "\x08"],
+ [["9"], "\x09"],
+ [["a"], "\x0a"],
+ [["b"], "\x0b"],
+ [["c"], "\x0c"],
+ [["d"], "\x0d"],
+ [["e"], "\x0e"],
+ [["f"], "\x0f"],
+ [["A"], "\x0a"],
+ [["B"], "\x0b"],
+ [["C"], "\x0c"],
+ [["D"], "\x0d"],
+ [["E"], "\x0e"],
+ [["F"], "\x0f"]
+ ].should be_computed_by(:pack, "h")
+ end
+
+ it "encodes the second character as the most significant nibble of a hex value" do
+ [ [["00"], "\x00"],
+ [["01"], "\x10"],
+ [["02"], "\x20"],
+ [["03"], "\x30"],
+ [["04"], "\x40"],
+ [["05"], "\x50"],
+ [["06"], "\x60"],
+ [["07"], "\x70"],
+ [["08"], "\x80"],
+ [["09"], "\x90"],
+ [["0a"], "\xa0"],
+ [["0b"], "\xb0"],
+ [["0c"], "\xc0"],
+ [["0d"], "\xd0"],
+ [["0e"], "\xe0"],
+ [["0f"], "\xf0"],
+ [["0A"], "\xa0"],
+ [["0B"], "\xb0"],
+ [["0C"], "\xc0"],
+ [["0D"], "\xd0"],
+ [["0E"], "\xe0"],
+ [["0F"], "\xf0"]
+ ].should be_computed_by(:pack, "h2")
+ end
+
+ it "encodes the least significant nibble of a non alphanumeric character as the least significant nibble of the hex value" do
+ [ [["^"], "\x0e"],
+ [["*"], "\x0a"],
+ [["#"], "\x03"],
+ [["["], "\x0b"],
+ [["]"], "\x0d"],
+ [["@"], "\x00"],
+ [["!"], "\x01"],
+ [["H"], "\x01"],
+ [["O"], "\x08"],
+ [["T"], "\x0d"],
+ [["Z"], "\x03"],
+ ].should be_computed_by(:pack, "h")
+ end
+
+ it "returns an ASCII-8BIT string" do
+ ["41"].pack("h").encoding.should == Encoding::ASCII_8BIT
+ end
+end
diff --git a/spec/ruby/core/array/pack/i_spec.rb b/spec/ruby/core/array/pack/i_spec.rb
new file mode 100644
index 0000000000..a237071227
--- /dev/null
+++ b/spec/ruby/core/array/pack/i_spec.rb
@@ -0,0 +1,133 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/numeric_basic'
+require_relative 'shared/integer'
+
+describe "Array#pack with format 'I'" do
+ it_behaves_like :array_pack_basic, 'I'
+ it_behaves_like :array_pack_basic_non_float, 'I'
+ it_behaves_like :array_pack_arguments, 'I'
+ it_behaves_like :array_pack_numeric_basic, 'I'
+ it_behaves_like :array_pack_integer, 'I'
+end
+
+describe "Array#pack with format 'i'" do
+ it_behaves_like :array_pack_basic, 'i'
+ it_behaves_like :array_pack_basic_non_float, 'i'
+ it_behaves_like :array_pack_arguments, 'i'
+ it_behaves_like :array_pack_numeric_basic, 'i'
+ it_behaves_like :array_pack_integer, 'i'
+end
+
+describe "Array#pack with format 'I'" do
+ describe "with modifier '<'" do
+ it_behaves_like :array_pack_32bit_le, 'I<'
+ end
+
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :array_pack_32bit_le, 'I<_'
+ it_behaves_like :array_pack_32bit_le, 'I_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :array_pack_32bit_le, 'I<!'
+ it_behaves_like :array_pack_32bit_le, 'I!<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :array_pack_32bit_be, 'I>'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :array_pack_32bit_be, 'I>_'
+ it_behaves_like :array_pack_32bit_be, 'I_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :array_pack_32bit_be, 'I>!'
+ it_behaves_like :array_pack_32bit_be, 'I!>'
+ end
+end
+
+describe "Array#pack with format 'i'" do
+ describe "with modifier '<'" do
+ it_behaves_like :array_pack_32bit_le, 'i<'
+ end
+
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :array_pack_32bit_le, 'i<_'
+ it_behaves_like :array_pack_32bit_le, 'i_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :array_pack_32bit_le, 'i<!'
+ it_behaves_like :array_pack_32bit_le, 'i!<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :array_pack_32bit_be, 'i>'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :array_pack_32bit_be, 'i>_'
+ it_behaves_like :array_pack_32bit_be, 'i_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :array_pack_32bit_be, 'i>!'
+ it_behaves_like :array_pack_32bit_be, 'i!>'
+ end
+end
+
+little_endian do
+ describe "Array#pack with format 'I'" do
+ it_behaves_like :array_pack_32bit_le, 'I'
+ end
+
+ describe "Array#pack with format 'I' with modifier '_'" do
+ it_behaves_like :array_pack_32bit_le_platform, 'I_'
+ end
+
+ describe "Array#pack with format 'I' with modifier '!'" do
+ it_behaves_like :array_pack_32bit_le_platform, 'I!'
+ end
+
+ describe "Array#pack with format 'i'" do
+ it_behaves_like :array_pack_32bit_le, 'i'
+ end
+
+ describe "Array#pack with format 'i' with modifier '_'" do
+ it_behaves_like :array_pack_32bit_le_platform, 'i_'
+ end
+
+ describe "Array#pack with format 'i' with modifier '!'" do
+ it_behaves_like :array_pack_32bit_le_platform, 'i!'
+ end
+end
+
+big_endian do
+ describe "Array#pack with format 'I'" do
+ it_behaves_like :array_pack_32bit_be, 'I'
+ end
+
+ describe "Array#pack with format 'I' with modifier '_'" do
+ it_behaves_like :array_pack_32bit_be_platform, 'I_'
+ end
+
+ describe "Array#pack with format 'I' with modifier '!'" do
+ it_behaves_like :array_pack_32bit_be_platform, 'I!'
+ end
+
+ describe "Array#pack with format 'i'" do
+ it_behaves_like :array_pack_32bit_be, 'i'
+ end
+
+ describe "Array#pack with format 'i' with modifier '_'" do
+ it_behaves_like :array_pack_32bit_be_platform, 'i_'
+ end
+
+ describe "Array#pack with format 'i' with modifier '!'" do
+ it_behaves_like :array_pack_32bit_be_platform, 'i!'
+ end
+end
diff --git a/spec/ruby/core/array/pack/j_spec.rb b/spec/ruby/core/array/pack/j_spec.rb
new file mode 100644
index 0000000000..7b62d5efdf
--- /dev/null
+++ b/spec/ruby/core/array/pack/j_spec.rb
@@ -0,0 +1,217 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/numeric_basic'
+require_relative 'shared/integer'
+
+platform_is pointer_size: 64 do
+ describe "Array#pack with format 'J'" do
+ it_behaves_like :array_pack_basic, 'J'
+ it_behaves_like :array_pack_basic_non_float, 'J'
+ it_behaves_like :array_pack_arguments, 'J'
+ it_behaves_like :array_pack_numeric_basic, 'J'
+ it_behaves_like :array_pack_integer, 'J'
+ end
+
+ describe "Array#pack with format 'j'" do
+ it_behaves_like :array_pack_basic, 'j'
+ it_behaves_like :array_pack_basic_non_float, 'j'
+ it_behaves_like :array_pack_arguments, 'j'
+ it_behaves_like :array_pack_numeric_basic, 'j'
+ it_behaves_like :array_pack_integer, 'j'
+ end
+
+ little_endian do
+ describe "Array#pack with format 'J'" do
+ describe "with modifier '_'" do
+ it_behaves_like :array_pack_64bit_le, 'J_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :array_pack_64bit_le, 'J!'
+ end
+ end
+
+ describe "Array#pack with format 'j'" do
+ describe "with modifier '_'" do
+ it_behaves_like :array_pack_64bit_le, 'j_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :array_pack_64bit_le, 'j!'
+ end
+ end
+ end
+
+ big_endian do
+ describe "Array#pack with format 'J'" do
+ describe "with modifier '_'" do
+ it_behaves_like :array_pack_64bit_be, 'J_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :array_pack_64bit_be, 'J!'
+ end
+ end
+
+ describe "Array#pack with format 'j'" do
+ describe "with modifier '_'" do
+ it_behaves_like :array_pack_64bit_be, 'j_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :array_pack_64bit_be, 'j!'
+ end
+ end
+ end
+
+ describe "Array#pack with format 'J'" do
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :array_pack_64bit_le, 'J<_'
+ it_behaves_like :array_pack_64bit_le, 'J_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :array_pack_64bit_le, 'J<!'
+ it_behaves_like :array_pack_64bit_le, 'J!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :array_pack_64bit_be, 'J>_'
+ it_behaves_like :array_pack_64bit_be, 'J_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :array_pack_64bit_be, 'J>!'
+ it_behaves_like :array_pack_64bit_be, 'J!>'
+ end
+ end
+
+ describe "Array#pack with format 'j'" do
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :array_pack_64bit_le, 'j<_'
+ it_behaves_like :array_pack_64bit_le, 'j_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :array_pack_64bit_le, 'j<!'
+ it_behaves_like :array_pack_64bit_le, 'j!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :array_pack_64bit_be, 'j>_'
+ it_behaves_like :array_pack_64bit_be, 'j_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :array_pack_64bit_be, 'j>!'
+ it_behaves_like :array_pack_64bit_be, 'j!>'
+ end
+ end
+end
+
+platform_is pointer_size: 32 do
+ describe "Array#pack with format 'J'" do
+ it_behaves_like :array_pack_basic, 'J'
+ it_behaves_like :array_pack_basic_non_float, 'J'
+ it_behaves_like :array_pack_arguments, 'J'
+ it_behaves_like :array_pack_numeric_basic, 'J'
+ it_behaves_like :array_pack_integer, 'J'
+ end
+
+ describe "Array#pack with format 'j'" do
+ it_behaves_like :array_pack_basic, 'j'
+ it_behaves_like :array_pack_basic_non_float, 'j'
+ it_behaves_like :array_pack_arguments, 'j'
+ it_behaves_like :array_pack_numeric_basic, 'j'
+ it_behaves_like :array_pack_integer, 'j'
+ end
+
+ big_endian do
+ describe "Array#pack with format 'J'" do
+ describe "with modifier '_'" do
+ it_behaves_like :array_pack_32bit_be, 'J_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :array_pack_32bit_be, 'J!'
+ end
+ end
+
+ describe "Array#pack with format 'j'" do
+ describe "with modifier '_'" do
+ it_behaves_like :array_pack_32bit_be, 'j_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :array_pack_32bit_be, 'j!'
+ end
+ end
+ end
+
+ little_endian do
+ describe "Array#pack with format 'J'" do
+ describe "with modifier '_'" do
+ it_behaves_like :array_pack_32bit_le, 'J_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :array_pack_32bit_le, 'J!'
+ end
+ end
+
+ describe "Array#pack with format 'j'" do
+ describe "with modifier '_'" do
+ it_behaves_like :array_pack_32bit_le, 'j_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :array_pack_32bit_le, 'j!'
+ end
+ end
+ end
+
+ describe "Array#pack with format 'J'" do
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :array_pack_32bit_le, 'J<_'
+ it_behaves_like :array_pack_32bit_le, 'J_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :array_pack_32bit_le, 'J<!'
+ it_behaves_like :array_pack_32bit_le, 'J!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :array_pack_32bit_be, 'J>_'
+ it_behaves_like :array_pack_32bit_be, 'J_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :array_pack_32bit_be, 'J>!'
+ it_behaves_like :array_pack_32bit_be, 'J!>'
+ end
+ end
+
+ describe "Array#pack with format 'j'" do
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :array_pack_32bit_le, 'j<_'
+ it_behaves_like :array_pack_32bit_le, 'j_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :array_pack_32bit_le, 'j<!'
+ it_behaves_like :array_pack_32bit_le, 'j!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :array_pack_32bit_be, 'j>_'
+ it_behaves_like :array_pack_32bit_be, 'j_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :array_pack_32bit_be, 'j>!'
+ it_behaves_like :array_pack_32bit_be, 'j!>'
+ end
+ end
+end
diff --git a/spec/ruby/core/array/pack/l_spec.rb b/spec/ruby/core/array/pack/l_spec.rb
new file mode 100644
index 0000000000..0a5552b984
--- /dev/null
+++ b/spec/ruby/core/array/pack/l_spec.rb
@@ -0,0 +1,221 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/numeric_basic'
+require_relative 'shared/integer'
+
+describe "Array#pack with format 'L'" do
+ it_behaves_like :array_pack_basic, 'L'
+ it_behaves_like :array_pack_basic_non_float, 'L'
+ it_behaves_like :array_pack_arguments, 'L'
+ it_behaves_like :array_pack_numeric_basic, 'L'
+ it_behaves_like :array_pack_integer, 'L'
+end
+
+describe "Array#pack with format 'l'" do
+ it_behaves_like :array_pack_basic, 'l'
+ it_behaves_like :array_pack_basic_non_float, 'l'
+ it_behaves_like :array_pack_arguments, 'l'
+ it_behaves_like :array_pack_numeric_basic, 'l'
+ it_behaves_like :array_pack_integer, 'l'
+end
+
+describe "Array#pack with format 'L'" do
+ describe "with modifier '<'" do
+ it_behaves_like :array_pack_32bit_le, 'L<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :array_pack_32bit_be, 'L>'
+ end
+
+ guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :array_pack_32bit_le, 'L<_'
+ it_behaves_like :array_pack_32bit_le, 'L_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :array_pack_32bit_le, 'L<!'
+ it_behaves_like :array_pack_32bit_le, 'L!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :array_pack_32bit_be, 'L>_'
+ it_behaves_like :array_pack_32bit_be, 'L_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :array_pack_32bit_be, 'L>!'
+ it_behaves_like :array_pack_32bit_be, 'L!>'
+ end
+ end
+
+ guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :array_pack_64bit_le, 'L<_'
+ it_behaves_like :array_pack_64bit_le, 'L_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :array_pack_64bit_le, 'L<!'
+ it_behaves_like :array_pack_64bit_le, 'L!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :array_pack_64bit_be, 'L>_'
+ it_behaves_like :array_pack_64bit_be, 'L_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :array_pack_64bit_be, 'L>!'
+ it_behaves_like :array_pack_64bit_be, 'L!>'
+ end
+ end
+end
+
+describe "Array#pack with format 'l'" do
+ describe "with modifier '<'" do
+ it_behaves_like :array_pack_32bit_le, 'l<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :array_pack_32bit_be, 'l>'
+ end
+
+ guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :array_pack_32bit_le, 'l<_'
+ it_behaves_like :array_pack_32bit_le, 'l_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :array_pack_32bit_le, 'l<!'
+ it_behaves_like :array_pack_32bit_le, 'l!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :array_pack_32bit_be, 'l>_'
+ it_behaves_like :array_pack_32bit_be, 'l_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :array_pack_32bit_be, 'l>!'
+ it_behaves_like :array_pack_32bit_be, 'l!>'
+ end
+ end
+
+ guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :array_pack_64bit_le, 'l<_'
+ it_behaves_like :array_pack_64bit_le, 'l_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :array_pack_64bit_le, 'l<!'
+ it_behaves_like :array_pack_64bit_le, 'l!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :array_pack_64bit_be, 'l>_'
+ it_behaves_like :array_pack_64bit_be, 'l_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :array_pack_64bit_be, 'l>!'
+ it_behaves_like :array_pack_64bit_be, 'l!>'
+ end
+ end
+end
+
+little_endian do
+ describe "Array#pack with format 'L'" do
+ it_behaves_like :array_pack_32bit_le, 'L'
+ end
+
+ describe "Array#pack with format 'l'" do
+ it_behaves_like :array_pack_32bit_le, 'l'
+ end
+
+ guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do
+ describe "Array#pack with format 'L' with modifier '_'" do
+ it_behaves_like :array_pack_32bit_le, 'L_'
+ end
+
+ describe "Array#pack with format 'L' with modifier '!'" do
+ it_behaves_like :array_pack_32bit_le, 'L!'
+ end
+
+ describe "Array#pack with format 'l' with modifier '_'" do
+ it_behaves_like :array_pack_32bit_le, 'l_'
+ end
+
+ describe "Array#pack with format 'l' with modifier '!'" do
+ it_behaves_like :array_pack_32bit_le, 'l!'
+ end
+ end
+
+ guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do
+ describe "Array#pack with format 'L' with modifier '_'" do
+ it_behaves_like :array_pack_64bit_le, 'L_'
+ end
+
+ describe "Array#pack with format 'L' with modifier '!'" do
+ it_behaves_like :array_pack_64bit_le, 'L!'
+ end
+
+ describe "Array#pack with format 'l' with modifier '_'" do
+ it_behaves_like :array_pack_64bit_le, 'l_'
+ end
+
+ describe "Array#pack with format 'l' with modifier '!'" do
+ it_behaves_like :array_pack_64bit_le, 'l!'
+ end
+ end
+end
+
+big_endian do
+ describe "Array#pack with format 'L'" do
+ it_behaves_like :array_pack_32bit_be, 'L'
+ end
+
+ describe "Array#pack with format 'l'" do
+ it_behaves_like :array_pack_32bit_be, 'l'
+ end
+
+ guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do
+ describe "Array#pack with format 'L' with modifier '_'" do
+ it_behaves_like :array_pack_32bit_be, 'L_'
+ end
+
+ describe "Array#pack with format 'L' with modifier '!'" do
+ it_behaves_like :array_pack_32bit_be, 'L!'
+ end
+
+ describe "Array#pack with format 'l' with modifier '_'" do
+ it_behaves_like :array_pack_32bit_be, 'l_'
+ end
+
+ describe "Array#pack with format 'l' with modifier '!'" do
+ it_behaves_like :array_pack_32bit_be, 'l!'
+ end
+ end
+
+ guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do
+ describe "Array#pack with format 'L' with modifier '_'" do
+ it_behaves_like :array_pack_64bit_be, 'L_'
+ end
+
+ describe "Array#pack with format 'L' with modifier '!'" do
+ it_behaves_like :array_pack_64bit_be, 'L!'
+ end
+
+ describe "Array#pack with format 'l' with modifier '_'" do
+ it_behaves_like :array_pack_64bit_be, 'l_'
+ end
+
+ describe "Array#pack with format 'l' with modifier '!'" do
+ it_behaves_like :array_pack_64bit_be, 'l!'
+ end
+ end
+end
diff --git a/spec/ruby/core/array/pack/m_spec.rb b/spec/ruby/core/array/pack/m_spec.rb
new file mode 100644
index 0000000000..24acf2cef2
--- /dev/null
+++ b/spec/ruby/core/array/pack/m_spec.rb
@@ -0,0 +1,309 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/taint'
+
+describe "Array#pack with format 'M'" do
+ it_behaves_like :array_pack_basic, 'M'
+ it_behaves_like :array_pack_basic_non_float, 'M'
+ it_behaves_like :array_pack_arguments, 'M'
+ it_behaves_like :array_pack_taint, 'M'
+
+ it "encodes an empty string as an empty string" do
+ [""].pack("M").should == ""
+ end
+
+ it "encodes nil as an empty string" do
+ [nil].pack("M").should == ""
+ end
+
+ it "appends a soft line break at the end of an encoded string" do
+ ["a"].pack("M").should == "a=\n"
+ end
+
+ it "does not append a soft break if the string ends with a newline" do
+ ["a\n"].pack("M").should == "a\n"
+ end
+
+ it "encodes one element for each directive" do
+ ["a", "b", "c"].pack("MM").should == "a=\nb=\n"
+ end
+
+ it "encodes byte values 33..60 directly" do
+ [ [["!\"\#$%&'()*+,-./"], "!\"\#$%&'()*+,-./=\n"],
+ [["0123456789"], "0123456789=\n"],
+ [[":;<"], ":;<=\n"]
+ ].should be_computed_by(:pack, "M")
+ end
+
+ it "encodes byte values 62..126 directly" do
+ [ [[">?@"], ">?@=\n"],
+ [["ABCDEFGHIJKLMNOPQRSTUVWXYZ"], "ABCDEFGHIJKLMNOPQRSTUVWXYZ=\n"],
+ [["[\\]^_`"], "[\\]^_`=\n"],
+ [["abcdefghijklmnopqrstuvwxyz"], "abcdefghijklmnopqrstuvwxyz=\n"],
+ [["{|}~"], "{|}~=\n"]
+ ].should be_computed_by(:pack, "M")
+ end
+
+ it "encodes an '=' character in hex format" do
+ ["="].pack("M").should == "=3D=\n"
+ end
+
+ it "encodes an embedded space directly" do
+ ["a b"].pack("M").should == "a b=\n"
+ end
+
+ it "encodes a space at the end of the string directly" do
+ ["a "].pack("M").should == "a =\n"
+ end
+
+ it "encodes an embedded tab directly" do
+ ["a\tb"].pack("M").should == "a\tb=\n"
+ end
+
+ it "encodes a tab at the end of the string directly" do
+ ["a\t"].pack("M").should == "a\t=\n"
+ end
+
+ it "encodes an embedded newline directly" do
+ ["a\nb"].pack("M").should == "a\nb=\n"
+ end
+
+ it "encodes 0..31 except tab and newline in hex format" do
+ [ [["\x00\x01\x02\x03\x04\x05\x06"], "=00=01=02=03=04=05=06=\n"],
+ [["\a\b\v\f\r"], "=07=08=0B=0C=0D=\n"],
+ [["\x0e\x0f\x10\x11\x12\x13\x14"], "=0E=0F=10=11=12=13=14=\n"],
+ [["\x15\x16\x17\x18\x19\x1a"], "=15=16=17=18=19=1A=\n"],
+ [["\e"], "=1B=\n"],
+ [["\x1c\x1d\x1e\x1f"], "=1C=1D=1E=1F=\n"]
+ ].should be_computed_by(:pack, "M")
+ end
+
+ it "encodes a tab followed by a newline with an encoded newline" do
+ ["\t\n"].pack("M").should == "\t=\n\n"
+ end
+
+ it "encodes 127..255 in hex format" do
+ [ [["\x7f\x80\x81\x82\x83\x84\x85\x86"], "=7F=80=81=82=83=84=85=86=\n"],
+ [["\x87\x88\x89\x8a\x8b\x8c\x8d\x8e"], "=87=88=89=8A=8B=8C=8D=8E=\n"],
+ [["\x8f\x90\x91\x92\x93\x94\x95\x96"], "=8F=90=91=92=93=94=95=96=\n"],
+ [["\x97\x98\x99\x9a\x9b\x9c\x9d\x9e"], "=97=98=99=9A=9B=9C=9D=9E=\n"],
+ [["\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6"], "=9F=A0=A1=A2=A3=A4=A5=A6=\n"],
+ [["\xa7\xa8\xa9\xaa\xab\xac\xad\xae"], "=A7=A8=A9=AA=AB=AC=AD=AE=\n"],
+ [["\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6"], "=AF=B0=B1=B2=B3=B4=B5=B6=\n"],
+ [["\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe"], "=B7=B8=B9=BA=BB=BC=BD=BE=\n"],
+ [["\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6"], "=BF=C0=C1=C2=C3=C4=C5=C6=\n"],
+ [["\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce"], "=C7=C8=C9=CA=CB=CC=CD=CE=\n"],
+ [["\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6"], "=CF=D0=D1=D2=D3=D4=D5=D6=\n"],
+ [["\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde"], "=D7=D8=D9=DA=DB=DC=DD=DE=\n"],
+ [["\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6"], "=DF=E0=E1=E2=E3=E4=E5=E6=\n"],
+ [["\xe7\xe8\xe9\xea\xeb\xec\xed\xee"], "=E7=E8=E9=EA=EB=EC=ED=EE=\n"],
+ [["\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6"], "=EF=F0=F1=F2=F3=F4=F5=F6=\n"],
+ [["\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe"], "=F7=F8=F9=FA=FB=FC=FD=FE=\n"],
+ [["\xff"], "=FF=\n"]
+ ].should be_computed_by(:pack, "M")
+ end
+
+ it "emits a soft line break when the output exceeds 72 characters when passed '*', 0, 1, or no count modifier" do
+ s1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ r1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=\na=\n"
+ s2 = "\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19"
+ r2 = "=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=\n=19=\n"
+ s3 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x15a"
+ r3 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=15=\na=\n"
+ s4 = "\x15aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x15a"
+ r4 = "=15aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=\na=15a=\n"
+
+ [ [[s1], "M", r1],
+ [[s1], "M0", r1],
+ [[s1], "M1", r1],
+ [[s2], "M", r2],
+ [[s2], "M0", r2],
+ [[s2], "M1", r2],
+ [[s3], "M", r3],
+ [[s3], "M0", r3],
+ [[s3], "M1", r3],
+ [[s4], "M", r4],
+ [[s4], "M0", r4],
+ [[s4], "M1", r4]
+ ].should be_computed_by(:pack)
+ end
+
+ it "emits a soft line break when the output exceeds count characters" do
+ [ [["abcdefghi"], "M2", "abc=\ndef=\nghi=\n"],
+ [["abcdefghi"], "M3", "abcd=\nefgh=\ni=\n"],
+ [["abcdefghi"], "M4", "abcde=\nfghi=\n"],
+ [["abcdefghi"], "M5", "abcdef=\nghi=\n"],
+ [["abcdefghi"], "M6", "abcdefg=\nhi=\n"],
+ [["\x19\x19\x19\x19"], "M2", "=19=\n=19=\n=19=\n=19=\n"],
+ [["\x19\x19\x19\x19"], "M3", "=19=19=\n=19=19=\n"],
+ [["\x19\x19\x19\x19"], "M4", "=19=19=\n=19=19=\n"],
+ [["\x19\x19\x19\x19"], "M5", "=19=19=\n=19=19=\n"],
+ [["\x19\x19\x19\x19"], "M6", "=19=19=19=\n=19=\n"],
+ [["\x19\x19\x19\x19"], "M7", "=19=19=19=\n=19=\n"]
+ ].should be_computed_by(:pack)
+ end
+
+ it "encodes a recursive array" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.pack('M').should be_an_instance_of(String)
+
+ array = ArraySpecs.recursive_array
+ array.pack('M').should == "1=\n"
+ end
+
+ it "calls #to_s to convert an object to a String" do
+ obj = mock("pack M string")
+ obj.should_receive(:to_s).and_return("packing")
+
+ [obj].pack("M").should == "packing=\n"
+ end
+
+ it "converts the object to a String representation if #to_s does not return a String" do
+ obj = mock("pack M non-string")
+ obj.should_receive(:to_s).and_return(2)
+
+ [obj].pack("M").should be_an_instance_of(String)
+ end
+
+ it "encodes a Symbol as a String" do
+ [:symbol].pack("M").should == "symbol=\n"
+ end
+
+ it "encodes an Integer as a String" do
+ [ [[1], "1=\n"],
+ [[bignum_value], "#{bignum_value}=\n"]
+ ].should be_computed_by(:pack, "M")
+ end
+
+ it "encodes a Float as a String" do
+ [1.0].pack("M").should == "1.0=\n"
+ end
+
+ it "converts Floats to the minimum unique representation" do
+ [1.0 / 3.0].pack("M").should == "0.3333333333333333=\n"
+ end
+
+ it "sets the output string to US-ASCII encoding" do
+ ["abcd"].pack("M").encoding.should == Encoding::US_ASCII
+ end
+end
+
+describe "Array#pack with format 'm'" do
+ it_behaves_like :array_pack_basic, 'm'
+ it_behaves_like :array_pack_basic_non_float, 'm'
+ it_behaves_like :array_pack_arguments, 'm'
+ it_behaves_like :array_pack_taint, 'm'
+
+ it "encodes an empty string as an empty string" do
+ [""].pack("m").should == ""
+ end
+
+ it "appends a newline to the end of the encoded string" do
+ ["a"].pack("m").should == "YQ==\n"
+ end
+
+ it "encodes one element per directive" do
+ ["abc", "DEF"].pack("mm").should == "YWJj\nREVG\n"
+ end
+
+ it "encodes 1, 2, or 3 characters in 4 output characters (Base64 encoding)" do
+ [ [["a"], "YQ==\n"],
+ [["ab"], "YWI=\n"],
+ [["abc"], "YWJj\n"],
+ [["abcd"], "YWJjZA==\n"],
+ [["abcde"], "YWJjZGU=\n"],
+ [["abcdef"], "YWJjZGVm\n"],
+ [["abcdefg"], "YWJjZGVmZw==\n"],
+ ].should be_computed_by(:pack, "m")
+ end
+
+ it "emits a newline after complete groups of count / 3 input characters when passed a count modifier" do
+ ["abcdefg"].pack("m3").should == "YWJj\nZGVm\nZw==\n"
+ end
+
+ it "implicitly has a count of 45 when passed '*', 1, 2 or no count modifier" do
+ s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ r = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\nYWFhYWE=\n"
+ [ [[s], "m", r],
+ [[s], "m*", r],
+ [[s], "m1", r],
+ [[s], "m2", r],
+ ].should be_computed_by(:pack)
+ end
+
+ it "encodes all ascii characters" do
+ [ [["\x00\x01\x02\x03\x04\x05\x06"], "AAECAwQFBg==\n"],
+ [["\a\b\t\n\v\f\r"], "BwgJCgsMDQ==\n"],
+ [["\x0E\x0F\x10\x11\x12\x13\x14\x15\x16"], "Dg8QERITFBUW\n"],
+ [["\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f"], "FxgZGhscHR4f\n"],
+ [["!\"\#$%&'()*+,-./"], "ISIjJCUmJygpKissLS4v\n"],
+ [["0123456789"], "MDEyMzQ1Njc4OQ==\n"],
+ [[":;<=>?@"], "Ojs8PT4/QA==\n"],
+ [["ABCDEFGHIJKLMNOPQRSTUVWXYZ"], "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=\n"],
+ [["[\\]^_`"], "W1xdXl9g\n"],
+ [["abcdefghijklmnopqrstuvwxyz"], "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=\n"],
+ [["{|}~"], "e3x9fg==\n"],
+ [["\x7f\xc2\x80\xc2\x81\xc2\x82\xc2\x83"], "f8KAwoHCgsKD\n"],
+ [["\xc2\x84\xc2\x85\xc2\x86\xc2\x87\xc2"], "woTChcKGwofC\n"],
+ [["\x88\xc2\x89\xc2\x8a\xc2\x8b\xc2\x8c"], "iMKJworCi8KM\n"],
+ [["\xc2\x8d\xc2\x8e\xc2\x8f\xc2\x90\xc2"], "wo3CjsKPwpDC\n"],
+ [["\x91\xc2\x92\xc2\x93\xc2\x94\xc2\x95"], "kcKSwpPClMKV\n"],
+ [["\xc2\x96\xc2\x97\xc2\x98\xc2\x99\xc2"], "wpbCl8KYwpnC\n"],
+ [["\x9a\xc2\x9b\xc2\x9c\xc2\x9d\xc2\x9e"], "msKbwpzCncKe\n"],
+ [["\xc2\x9f\xc2\xa0\xc2\xa1\xc2\xa2\xc2"], "wp/CoMKhwqLC\n"],
+ [["\xa3\xc2\xa4\xc2\xa5\xc2\xa6\xc2\xa7"], "o8KkwqXCpsKn\n"],
+ [["\xc2\xa8\xc2\xa9\xc2\xaa\xc2\xab\xc2"], "wqjCqcKqwqvC\n"],
+ [["\xac\xc2\xad\xc2\xae\xc2\xaf\xc2\xb0"], "rMKtwq7Cr8Kw\n"],
+ [["\xc2\xb1\xc2\xb2\xc2\xb3\xc2\xb4\xc2"], "wrHCssKzwrTC\n"],
+ [["\xb5\xc2\xb6\xc2\xb7\xc2\xb8\xc2\xb9"], "tcK2wrfCuMK5\n"],
+ [["\xc2\xba\xc2\xbb\xc2\xbc\xc2\xbd\xc2"], "wrrCu8K8wr3C\n"],
+ [["\xbe\xc2\xbf\xc3\x80\xc3\x81\xc3\x82"], "vsK/w4DDgcOC\n"],
+ [["\xc3\x83\xc3\x84\xc3\x85\xc3\x86\xc3"], "w4PDhMOFw4bD\n"],
+ [["\x87\xc3\x88\xc3\x89\xc3\x8a\xc3\x8b"], "h8OIw4nDisOL\n"],
+ [["\xc3\x8c\xc3\x8d\xc3\x8e\xc3\x8f\xc3"], "w4zDjcOOw4/D\n"],
+ [["\x90\xc3\x91\xc3\x92\xc3\x93\xc3\x94"], "kMORw5LDk8OU\n"],
+ [["\xc3\x95\xc3\x96\xc3\x97\xc3\x98\xc3"], "w5XDlsOXw5jD\n"],
+ [["\x99\xc3\x9a\xc3\x9b\xc3\x9c\xc3\x9d"], "mcOaw5vDnMOd\n"],
+ [["\xc3\x9e\xc3\x9f\xc3\xa0\xc3\xa1\xc3"], "w57Dn8Ogw6HD\n"],
+ [["\xa2\xc3\xa3\xc3\xa4\xc3\xa5\xc3\xa6"], "osOjw6TDpcOm\n"],
+ [["\xc3\xa7\xc3\xa8\xc3\xa9\xc3\xaa\xc3"], "w6fDqMOpw6rD\n"],
+ [["\xab\xc3\xac\xc3\xad\xc3\xae\xc3\xaf"], "q8Osw63DrsOv\n"],
+ [["\xc3\xb0\xc3\xb1\xc3\xb2\xc3\xb3\xc3"], "w7DDscOyw7PD\n"],
+ [["\xb4\xc3\xb5\xc3\xb6\xc3\xb7\xc3\xb8"], "tMO1w7bDt8O4\n"],
+ [["\xc3\xb9\xc3\xba\xc3\xbb\xc3\xbc\xc3"], "w7nDusO7w7zD\n"],
+ [["\xbd\xc3\xbe\xc3\xbf"], "vcO+w78=\n"]
+ ].should be_computed_by(:pack, "m")
+ end
+
+ it "calls #to_str to convert an object to a String" do
+ obj = mock("pack m string")
+ obj.should_receive(:to_str).and_return("abc")
+ [obj].pack("m").should == "YWJj\n"
+ end
+
+ it "raises a TypeError if #to_str does not return a String" do
+ obj = mock("pack m non-string")
+ lambda { [obj].pack("m") }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed nil" do
+ lambda { [nil].pack("m") }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an Integer" do
+ lambda { [0].pack("m") }.should raise_error(TypeError)
+ lambda { [bignum_value].pack("m") }.should raise_error(TypeError)
+ end
+
+ it "does not emit a newline if passed zero as the count modifier" do
+ s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ r = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWE="
+ [s].pack("m0").should == r
+ end
+
+ it "sets the output string to US-ASCII encoding" do
+ ["abcd"].pack("m").encoding.should == Encoding::US_ASCII
+ end
+end
diff --git a/spec/ruby/core/array/pack/n_spec.rb b/spec/ruby/core/array/pack/n_spec.rb
new file mode 100644
index 0000000000..ab9409fc1e
--- /dev/null
+++ b/spec/ruby/core/array/pack/n_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/numeric_basic'
+require_relative 'shared/integer'
+
+describe "Array#pack with format 'N'" do
+ it_behaves_like :array_pack_basic, 'N'
+ it_behaves_like :array_pack_basic_non_float, 'N'
+ it_behaves_like :array_pack_arguments, 'N'
+ it_behaves_like :array_pack_numeric_basic, 'N'
+ it_behaves_like :array_pack_integer, 'N'
+ it_behaves_like :array_pack_no_platform, 'N'
+ it_behaves_like :array_pack_32bit_be, 'N'
+end
+
+describe "Array#pack with format 'n'" do
+ it_behaves_like :array_pack_basic, 'n'
+ it_behaves_like :array_pack_basic_non_float, 'n'
+ it_behaves_like :array_pack_arguments, 'n'
+ it_behaves_like :array_pack_numeric_basic, 'n'
+ it_behaves_like :array_pack_integer, 'n'
+ it_behaves_like :array_pack_no_platform, 'n'
+ it_behaves_like :array_pack_16bit_be, 'n'
+end
diff --git a/spec/ruby/core/array/pack/p_spec.rb b/spec/ruby/core/array/pack/p_spec.rb
new file mode 100644
index 0000000000..857d403313
--- /dev/null
+++ b/spec/ruby/core/array/pack/p_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/taint'
+
+describe "Array#pack with format 'P'" do
+ it_behaves_like :array_pack_basic_non_float, 'P'
+ it_behaves_like :array_pack_taint, 'P'
+
+ it "produces as many bytes as there are in a pointer" do
+ ["hello"].pack("P").size.should == [0].pack("J").size
+ end
+
+ it "round-trips a string through pack and unpack" do
+ ["hello"].pack("P").unpack("P5").should == ["hello"]
+ end
+
+ it "taints the input string" do
+ input_string = "hello"
+ [input_string].pack("P")
+ input_string.tainted?.should be_true
+ end
+
+ it "does not taint the output string in normal cases" do
+ ["hello"].pack("P").tainted?.should be_false
+ end
+
+ it "with nil gives a null pointer" do
+ [nil].pack("P").unpack("J").should == [0]
+ end
+end
+
+describe "Array#pack with format 'p'" do
+ it_behaves_like :array_pack_basic_non_float, 'p'
+ it_behaves_like :array_pack_taint, 'p'
+
+ it "produces as many bytes as there are in a pointer" do
+ ["hello"].pack("p").size.should == [0].pack("J").size
+ end
+
+ it "round-trips a string through pack and unpack" do
+ ["hello"].pack("p").unpack("p").should == ["hello"]
+ end
+
+ it "taints the input string" do
+ input_string = "hello"
+ [input_string].pack("p")
+ input_string.tainted?.should be_true
+ end
+
+ it "does not taint the output string in normal cases" do
+ ["hello"].pack("p").tainted?.should be_false
+ end
+
+ it "with nil gives a null pointer" do
+ [nil].pack("p").unpack("J").should == [0]
+ end
+end
diff --git a/spec/ruby/core/array/pack/percent_spec.rb b/spec/ruby/core/array/pack/percent_spec.rb
new file mode 100644
index 0000000000..3c0e7eca0f
--- /dev/null
+++ b/spec/ruby/core/array/pack/percent_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+
+describe "Array#pack with format '%'" do
+ it "raises an Argument Error" do
+ lambda { [1].pack("%") }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/array/pack/q_spec.rb b/spec/ruby/core/array/pack/q_spec.rb
new file mode 100644
index 0000000000..bd6b2a4b71
--- /dev/null
+++ b/spec/ruby/core/array/pack/q_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/numeric_basic'
+require_relative 'shared/integer'
+
+describe "Array#pack with format 'Q'" do
+ it_behaves_like :array_pack_basic, 'Q'
+ it_behaves_like :array_pack_basic_non_float, 'Q'
+ it_behaves_like :array_pack_arguments, 'Q'
+ it_behaves_like :array_pack_numeric_basic, 'Q'
+ it_behaves_like :array_pack_integer, 'Q'
+end
+
+describe "Array#pack with format 'q'" do
+ it_behaves_like :array_pack_basic, 'q'
+ it_behaves_like :array_pack_basic_non_float, 'q'
+ it_behaves_like :array_pack_arguments, 'q'
+ it_behaves_like :array_pack_numeric_basic, 'q'
+ it_behaves_like :array_pack_integer, 'q'
+end
+
+describe "Array#pack with format 'Q'" do
+ describe "with modifier '<'" do
+ it_behaves_like :array_pack_64bit_le, 'Q<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :array_pack_64bit_be, 'Q>'
+ end
+end
+
+describe "Array#pack with format 'q'" do
+ describe "with modifier '<'" do
+ it_behaves_like :array_pack_64bit_le, 'q<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :array_pack_64bit_be, 'q>'
+ end
+end
+
+little_endian do
+ describe "Array#pack with format 'Q'" do
+ it_behaves_like :array_pack_64bit_le, 'Q'
+ end
+
+ describe "Array#pack with format 'q'" do
+ it_behaves_like :array_pack_64bit_le, 'q'
+ end
+end
+
+big_endian do
+ describe "Array#pack with format 'Q'" do
+ it_behaves_like :array_pack_64bit_be, 'Q'
+ end
+
+ describe "Array#pack with format 'q'" do
+ it_behaves_like :array_pack_64bit_be, 'q'
+ end
+end
diff --git a/spec/ruby/core/array/pack/s_spec.rb b/spec/ruby/core/array/pack/s_spec.rb
new file mode 100644
index 0000000000..4212d6a0b1
--- /dev/null
+++ b/spec/ruby/core/array/pack/s_spec.rb
@@ -0,0 +1,133 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/numeric_basic'
+require_relative 'shared/integer'
+
+describe "Array#pack with format 'S'" do
+ it_behaves_like :array_pack_basic, 'S'
+ it_behaves_like :array_pack_basic_non_float, 'S'
+ it_behaves_like :array_pack_arguments, 'S'
+ it_behaves_like :array_pack_numeric_basic, 'S'
+ it_behaves_like :array_pack_integer, 'S'
+end
+
+describe "Array#pack with format 's'" do
+ it_behaves_like :array_pack_basic, 's'
+ it_behaves_like :array_pack_basic_non_float, 's'
+ it_behaves_like :array_pack_arguments, 's'
+ it_behaves_like :array_pack_numeric_basic, 's'
+ it_behaves_like :array_pack_integer, 's'
+end
+
+describe "Array#pack with format 'S'" do
+ describe "with modifier '<'" do
+ it_behaves_like :array_pack_16bit_le, 'S<'
+ end
+
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :array_pack_16bit_le, 'S<_'
+ it_behaves_like :array_pack_16bit_le, 'S_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :array_pack_16bit_le, 'S<!'
+ it_behaves_like :array_pack_16bit_le, 'S!<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :array_pack_16bit_be, 'S>'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :array_pack_16bit_be, 'S>_'
+ it_behaves_like :array_pack_16bit_be, 'S_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :array_pack_16bit_be, 'S>!'
+ it_behaves_like :array_pack_16bit_be, 'S!>'
+ end
+end
+
+describe "Array#pack with format 's'" do
+ describe "with modifier '<'" do
+ it_behaves_like :array_pack_16bit_le, 's<'
+ end
+
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :array_pack_16bit_le, 's<_'
+ it_behaves_like :array_pack_16bit_le, 's_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :array_pack_16bit_le, 's<!'
+ it_behaves_like :array_pack_16bit_le, 's!<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :array_pack_16bit_be, 's>'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :array_pack_16bit_be, 's>_'
+ it_behaves_like :array_pack_16bit_be, 's_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :array_pack_16bit_be, 's>!'
+ it_behaves_like :array_pack_16bit_be, 's!>'
+ end
+end
+
+little_endian do
+ describe "Array#pack with format 'S'" do
+ it_behaves_like :array_pack_16bit_le, 'S'
+ end
+
+ describe "Array#pack with format 'S' with modifier '_'" do
+ it_behaves_like :array_pack_16bit_le, 'S_'
+ end
+
+ describe "Array#pack with format 'S' with modifier '!'" do
+ it_behaves_like :array_pack_16bit_le, 'S!'
+ end
+
+ describe "Array#pack with format 's'" do
+ it_behaves_like :array_pack_16bit_le, 's'
+ end
+
+ describe "Array#pack with format 's' with modifier '_'" do
+ it_behaves_like :array_pack_16bit_le, 's_'
+ end
+
+ describe "Array#pack with format 's' with modifier '!'" do
+ it_behaves_like :array_pack_16bit_le, 's!'
+ end
+end
+
+big_endian do
+ describe "Array#pack with format 'S'" do
+ it_behaves_like :array_pack_16bit_be, 'S'
+ end
+
+ describe "Array#pack with format 'S' with modifier '_'" do
+ it_behaves_like :array_pack_16bit_be, 'S_'
+ end
+
+ describe "Array#pack with format 'S' with modifier '!'" do
+ it_behaves_like :array_pack_16bit_be, 'S!'
+ end
+
+ describe "Array#pack with format 's'" do
+ it_behaves_like :array_pack_16bit_be, 's'
+ end
+
+ describe "Array#pack with format 's' with modifier '_'" do
+ it_behaves_like :array_pack_16bit_be, 's_'
+ end
+
+ describe "Array#pack with format 's' with modifier '!'" do
+ it_behaves_like :array_pack_16bit_be, 's!'
+ end
+end
diff --git a/spec/ruby/core/array/pack/shared/basic.rb b/spec/ruby/core/array/pack/shared/basic.rb
new file mode 100644
index 0000000000..39ab15308d
--- /dev/null
+++ b/spec/ruby/core/array/pack/shared/basic.rb
@@ -0,0 +1,65 @@
+describe :array_pack_arguments, shared: true do
+ it "raises an ArgumentError if there are fewer elements than the format requires" do
+ lambda { [].pack(pack_format(1)) }.should raise_error(ArgumentError)
+ end
+end
+
+describe :array_pack_basic, shared: true do
+ before :each do
+ @obj = ArraySpecs.universal_pack_object
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { [@obj].pack(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed an Integer" do
+ lambda { [@obj].pack(1) }.should raise_error(TypeError)
+ end
+end
+
+describe :array_pack_basic_non_float, shared: true do
+ before :each do
+ @obj = ArraySpecs.universal_pack_object
+ end
+
+ it "ignores whitespace in the format string" do
+ [@obj, @obj].pack("a \t\n\v\f\r"+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)
+ [@obj, @obj].pack(d).should be_an_instance_of(String)
+ end
+
+ it "taints the output string if the format string is tainted" do
+ [@obj, @obj].pack("x"+pack_format.taint).tainted?.should be_true
+ end
+end
+
+describe :array_pack_basic_float, shared: true do
+ it "ignores whitespace in the format string" do
+ [9.3, 4.7].pack(" \t\n\v\f\r"+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)
+ [1.2, 4.7].pack(d).should be_an_instance_of(String)
+ end
+
+ it "taints the output string if the format string is tainted" do
+ [3.2, 2.8].pack("x"+pack_format.taint).tainted?.should be_true
+ end
+end
+
+describe :array_pack_no_platform, shared: true do
+ it "raises ArgumentError when the format modifier is '_'" do
+ lambda{ [1].pack(pack_format("_")) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError when the format modifier is '!'" do
+ lambda{ [1].pack(pack_format("!")) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/array/pack/shared/encodings.rb b/spec/ruby/core/array/pack/shared/encodings.rb
new file mode 100644
index 0000000000..3724a5d859
--- /dev/null
+++ b/spec/ruby/core/array/pack/shared/encodings.rb
@@ -0,0 +1,16 @@
+describe :array_pack_hex, shared: true do
+ it "encodes no bytes when passed zero as the count modifier" do
+ ["abc"].pack(pack_format(0)).should == ""
+ end
+
+ it "raises a TypeError if the object does not respond to #to_str" do
+ obj = mock("pack hex non-string")
+ lambda { [obj].pack(pack_format) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if #to_str does not return a String" do
+ obj = mock("pack hex non-string")
+ obj.should_receive(:to_str).and_return(1)
+ lambda { [obj].pack(pack_format) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/array/pack/shared/float.rb b/spec/ruby/core/array/pack/shared/float.rb
new file mode 100644
index 0000000000..082de27acd
--- /dev/null
+++ b/spec/ruby/core/array/pack/shared/float.rb
@@ -0,0 +1,249 @@
+# -*- encoding: ascii-8bit -*-
+
+describe :array_pack_float_le, shared: true do
+ it "encodes a positive Float" do
+ [1.42].pack(pack_format).should == "\x8f\xc2\xb5?"
+ end
+
+ it "encodes a negative Float" do
+ [-34.2].pack(pack_format).should == "\xcd\xcc\x08\xc2"
+ end
+
+ it "converts an Integer to a Float" do
+ [8].pack(pack_format).should == "\x00\x00\x00A"
+ end
+
+ it "raises a TypeError if passed a String representation of a floating point number" do
+ lambda { ["13"].pack(pack_format) }.should raise_error(TypeError)
+ end
+
+ it "encodes the number of array elements specified by the count modifier" do
+ [2.9, 1.4, 8.2].pack(pack_format(nil, 2)).should == "\x9a\x999@33\xb3?"
+ end
+
+ it "encodes all remaining elements when passed the '*' modifier" 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"
+ end
+
+ it "ignores spaces between directives" do
+ [5.3, 9.2].pack(pack_format(" ", 2)).should == "\x9a\x99\xa9@33\x13A"
+ end
+
+ it "encodes positive Infinity" do
+ [infinity_value].pack(pack_format).should == "\x00\x00\x80\x7f"
+ end
+
+ it "encodes negative Infinity" do
+ [-infinity_value].pack(pack_format).should == "\x00\x00\x80\xff"
+ end
+
+ platform_is "86" do # x86 / x86_64
+ it "encodes NaN" do
+ [nan_value].pack(pack_format).should == "\x00\x00\xc0\xff"
+ end
+ end
+
+ platform_is "powerpc64" do
+ it "encodes NaN" do
+ [nan_value].pack(pack_format).should == "\x00\x00\xc0\x7f"
+ end
+ end
+
+ it "encodes a positive Float outside the range of a single precision float" do
+ [1e150].pack(pack_format).should == "\x00\x00\x80\x7f"
+ end
+
+ it "encodes a negative Float outside the range of a single precision float" do
+ [-1e150].pack(pack_format).should == "\x00\x00\x80\xff"
+ end
+end
+
+describe :array_pack_float_be, shared: true do
+ it "encodes a positive Float" do
+ [1.42].pack(pack_format).should == "?\xb5\xc2\x8f"
+ end
+
+ it "encodes a negative Float" do
+ [-34.2].pack(pack_format).should == "\xc2\x08\xcc\xcd"
+ end
+
+ it "converts an Integer to a Float" do
+ [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
+ lambda { ["13"].pack(pack_format) }.should raise_error(TypeError)
+ end
+
+ it "encodes the number of array elements specified by the count modifier" do
+ [2.9, 1.4, 8.2].pack(pack_format(nil, 2)).should == "@9\x99\x9a?\xb333"
+ end
+
+ it "encodes all remaining elements when passed the '*' modifier" 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"
+ end
+
+ it "ignores spaces between directives" do
+ [5.3, 9.2].pack(pack_format(" ", 2)).should == "@\xa9\x99\x9aA\x1333"
+ end
+
+ it "encodes positive Infinity" do
+ [infinity_value].pack(pack_format).should == "\x7f\x80\x00\x00"
+ end
+
+ it "encodes negative Infinity" do
+ [-infinity_value].pack(pack_format).should == "\xff\x80\x00\x00"
+ end
+
+ platform_is "86" do # x86 / x86_64
+ it "encodes NaN" do
+ [nan_value].pack(pack_format).should == "\xff\xc0\x00\x00"
+ end
+ end
+
+ platform_is "powerpc64" do
+ it "encodes NaN" do
+ [nan_value].pack(pack_format).should == "\x7f\xc0\x00\x00"
+ end
+ end
+
+ it "encodes a positive Float outside the range of a single precision float" do
+ [1e150].pack(pack_format).should == "\x7f\x80\x00\x00"
+ end
+
+ it "encodes a negative Float outside the range of a single precision float" do
+ [-1e150].pack(pack_format).should == "\xff\x80\x00\x00"
+ end
+end
+
+describe :array_pack_double_le, shared: true do
+ it "encodes a positive Float" do
+ [1.42].pack(pack_format).should == "\xb8\x1e\x85\xebQ\xb8\xf6?"
+ end
+
+ it "encodes a negative Float" do
+ [-34.2].pack(pack_format).should == "\x9a\x99\x99\x99\x99\x19A\xc0"
+ end
+
+ it "converts an Integer to a Float" do
+ [8].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\x20@"
+ end
+
+ it "raises a TypeError if passed a String representation of a floating point number" do
+ lambda { ["13"].pack(pack_format) }.should raise_error(TypeError)
+ end
+
+ it "encodes the number of array elements specified by the count modifier" do
+ [2.9, 1.4, 8.2].pack(pack_format(nil, 2)).should == "333333\x07@ffffff\xf6?"
+ end
+
+ it "encodes all remaining elements when passed the '*' modifier" 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@"
+ end
+
+ it "ignores spaces between directives" do
+ [5.3, 9.2].pack(pack_format(" ", 2)).should == "333333\x15@ffffff\x22@"
+ end
+
+ it "encodes positive Infinity" do
+ [infinity_value].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\xf0\x7f"
+ end
+
+ it "encodes negative Infinity" do
+ [-infinity_value].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\xf0\xff"
+ end
+
+ platform_is "86" do # x86 / x86_64
+ it "encodes NaN" do
+ [nan_value].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\xf8\xff"
+ end
+ end
+
+ platform_is "powerpc64" do
+ it "encodes NaN" do
+ [nan_value].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\xf8\x7f"
+ end
+ end
+
+ it "encodes a positive Float outside the range of a single precision float" do
+ [1e150].pack(pack_format).should == "\xaf\x96P\x2e5\x8d\x13_"
+ end
+
+ it "encodes a negative Float outside the range of a single precision float" do
+ [-1e150].pack(pack_format).should == "\xaf\x96P\x2e5\x8d\x13\xdf"
+ end
+end
+
+describe :array_pack_double_be, shared: true do
+ it "encodes a positive Float" do
+ [1.42].pack(pack_format).should == "?\xf6\xb8Q\xeb\x85\x1e\xb8"
+ end
+
+ it "encodes a negative Float" do
+ [-34.2].pack(pack_format).should == "\xc0A\x19\x99\x99\x99\x99\x9a"
+ end
+
+ it "converts an Integer to a Float" do
+ [8].pack(pack_format).should == "@\x20\x00\x00\x00\x00\x00\x00"
+ end
+
+ it "raises a TypeError if passed a String representation of a floating point number" do
+ lambda { ["13"].pack(pack_format) }.should raise_error(TypeError)
+ end
+
+ it "encodes the number of array elements specified by the count modifier" do
+ [2.9, 1.4, 8.2].pack(pack_format(nil, 2)).should == "@\x07333333?\xf6ffffff"
+ end
+
+ it "encodes all remaining elements when passed the '*' modifier" 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"
+ end
+
+ it "ignores spaces between directives" do
+ [5.3, 9.2].pack(pack_format(" ", 2)).should == "@\x15333333@\x22ffffff"
+ end
+
+ it "encodes positive Infinity" do
+ [infinity_value].pack(pack_format).should == "\x7f\xf0\x00\x00\x00\x00\x00\x00"
+ end
+
+ it "encodes negative Infinity" do
+ [-infinity_value].pack(pack_format).should == "\xff\xf0\x00\x00\x00\x00\x00\x00"
+ end
+
+ platform_is "86" do # x86 / x86_64
+ it "encodes NaN" do
+ [nan_value].pack(pack_format).should == "\xff\xf8\x00\x00\x00\x00\x00\x00"
+ end
+ end
+
+ platform_is "powerpc64" do
+ it "encodes NaN" do
+ [nan_value].pack(pack_format).should == "\x7f\xf8\x00\x00\x00\x00\x00\x00"
+ end
+ end
+
+ it "encodes a positive Float outside the range of a single precision float" do
+ [1e150].pack(pack_format).should == "_\x13\x8d5\x2eP\x96\xaf"
+ end
+
+ it "encodes a negative Float outside the range of a single precision float" do
+ [-1e150].pack(pack_format).should == "\xdf\x13\x8d5\x2eP\x96\xaf"
+ end
+end
diff --git a/spec/ruby/core/array/pack/shared/integer.rb b/spec/ruby/core/array/pack/shared/integer.rb
new file mode 100644
index 0000000000..0df03bbfd1
--- /dev/null
+++ b/spec/ruby/core/array/pack/shared/integer.rb
@@ -0,0 +1,381 @@
+# -*- encoding: ascii-8bit -*-
+
+describe :array_pack_16bit_le, shared: true do
+ it "encodes the least significant 16 bits of a positive number" do
+ [ [[0x0000_0021], "\x21\x00"],
+ [[0x0000_4321], "\x21\x43"],
+ [[0x0065_4321], "\x21\x43"],
+ [[0x7865_4321], "\x21\x43"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "encodes the least significant 16 bits of a negative number" do
+ [ [[-0x0000_0021], "\xdf\xff"],
+ [[-0x0000_4321], "\xdf\xbc"],
+ [[-0x0065_4321], "\xdf\xbc"],
+ [[-0x7865_4321], "\xdf\xbc"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "encodes a Float truncated as an Integer" do
+ [ [[2019902241.2], "\x21\x43"],
+ [[2019902241.8], "\x21\x43"],
+ [[-2019902241.2], "\xdf\xbc"],
+ [[-2019902241.8], "\xdf\xbc"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "calls #to_int to convert the pack argument to an Integer" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(0x1234_5678)
+ [obj].pack(pack_format()).should == "\x78\x56"
+ end
+
+ it "encodes the number of array elements specified by the count modifier" do
+ str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format(2))
+ str.should == "\x78\x65\xcd\xab"
+ end
+
+ it "encodes all remaining elements when passed the '*' modifier" do
+ str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format('*'))
+ 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"
+ end
+
+ it "ignores spaces between directives" do
+ str = [0x1243_6578, 0xdef0_abcd].pack(pack_format(' ', 2))
+ str.should == "\x78\x65\xcd\xab"
+ end
+end
+
+describe :array_pack_16bit_be, shared: true do
+ it "encodes the least significant 16 bits of a positive number" do
+ [ [[0x0000_0021], "\x00\x21"],
+ [[0x0000_4321], "\x43\x21"],
+ [[0x0065_4321], "\x43\x21"],
+ [[0x7865_4321], "\x43\x21"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "encodes the least significant 16 bits of a negative number" do
+ [ [[-0x0000_0021], "\xff\xdf"],
+ [[-0x0000_4321], "\xbc\xdf"],
+ [[-0x0065_4321], "\xbc\xdf"],
+ [[-0x7865_4321], "\xbc\xdf"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "encodes a Float truncated as an Integer" do
+ [ [[2019902241.2], "\x43\x21"],
+ [[2019902241.8], "\x43\x21"],
+ [[-2019902241.2], "\xbc\xdf"],
+ [[-2019902241.8], "\xbc\xdf"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "calls #to_int to convert the pack argument to an Integer" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(0x1234_5678)
+ [obj].pack(pack_format()).should == "\x56\x78"
+ end
+
+ it "encodes the number of array elements specified by the count modifier" do
+ str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format(2))
+ str.should == "\x65\x78\xab\xcd"
+ end
+
+ it "encodes all remaining elements when passed the '*' modifier" do
+ str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format('*'))
+ 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"
+ end
+
+ it "ignores spaces between directives" do
+ str = [0x1243_6578, 0xdef0_abcd].pack(pack_format(' ', 2))
+ str.should == "\x65\x78\xab\xcd"
+ end
+end
+
+describe :array_pack_32bit_le, shared: true do
+ it "encodes the least significant 32 bits of a positive number" do
+ [ [[0x0000_0021], "\x21\x00\x00\x00"],
+ [[0x0000_4321], "\x21\x43\x00\x00"],
+ [[0x0065_4321], "\x21\x43\x65\x00"],
+ [[0x7865_4321], "\x21\x43\x65\x78"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "encodes the least significant 32 bits of a negative number" do
+ [ [[-0x0000_0021], "\xdf\xff\xff\xff"],
+ [[-0x0000_4321], "\xdf\xbc\xff\xff"],
+ [[-0x0065_4321], "\xdf\xbc\x9a\xff"],
+ [[-0x7865_4321], "\xdf\xbc\x9a\x87"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "encodes a Float truncated as an Integer" do
+ [ [[2019902241.2], "\x21\x43\x65\x78"],
+ [[2019902241.8], "\x21\x43\x65\x78"],
+ [[-2019902241.2], "\xdf\xbc\x9a\x87"],
+ [[-2019902241.8], "\xdf\xbc\x9a\x87"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "calls #to_int to convert the pack argument to an Integer" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(0x1234_5678)
+ [obj].pack(pack_format()).should == "\x78\x56\x34\x12"
+ end
+
+ it "encodes the number of array elements specified by the count modifier" do
+ str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format(2))
+ str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde"
+ end
+
+ it "encodes all remaining elements when passed the '*' modifier" do
+ str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format('*'))
+ 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"
+ end
+
+ it "ignores spaces between directives" do
+ str = [0x1243_6578, 0xdef0_abcd].pack(pack_format(' ', 2))
+ str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde"
+ end
+end
+
+describe :array_pack_32bit_be, shared: true do
+ it "encodes the least significant 32 bits of a positive number" do
+ [ [[0x0000_0021], "\x00\x00\x00\x21"],
+ [[0x0000_4321], "\x00\x00\x43\x21"],
+ [[0x0065_4321], "\x00\x65\x43\x21"],
+ [[0x7865_4321], "\x78\x65\x43\x21"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "encodes the least significant 32 bits of a negative number" do
+ [ [[-0x0000_0021], "\xff\xff\xff\xdf"],
+ [[-0x0000_4321], "\xff\xff\xbc\xdf"],
+ [[-0x0065_4321], "\xff\x9a\xbc\xdf"],
+ [[-0x7865_4321], "\x87\x9a\xbc\xdf"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "encodes a Float truncated as an Integer" do
+ [ [[2019902241.2], "\x78\x65\x43\x21"],
+ [[2019902241.8], "\x78\x65\x43\x21"],
+ [[-2019902241.2], "\x87\x9a\xbc\xdf"],
+ [[-2019902241.8], "\x87\x9a\xbc\xdf"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "calls #to_int to convert the pack argument to an Integer" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(0x1234_5678)
+ [obj].pack(pack_format()).should == "\x12\x34\x56\x78"
+ end
+
+ it "encodes the number of array elements specified by the count modifier" do
+ str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format(2))
+ str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd"
+ end
+
+ it "encodes all remaining elements when passed the '*' modifier" do
+ str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format('*'))
+ 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"
+ end
+
+ it "ignores spaces between directives" do
+ str = [0x1243_6578, 0xdef0_abcd].pack(pack_format(' ', 2))
+ str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd"
+ end
+end
+
+describe :array_pack_32bit_le_platform, shared: true do
+ it "encodes the least significant 32 bits of a number" do
+ [ [[0x7865_4321], "\x21\x43\x65\x78"],
+ [[-0x7865_4321], "\xdf\xbc\x9a\x87"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "encodes the number of array elements specified by the count modifier" do
+ str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format(2))
+ str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde"
+ end
+
+ it "encodes all remaining elements when passed the '*' modifier" do
+ str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format('*'))
+ str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde\x21\x43\x65\x78"
+ end
+
+ platform_is wordsize: 64 do
+ it "encodes the least significant 32 bits of a number that is greater than 32 bits" do
+ [ [[0xff_7865_4321], "\x21\x43\x65\x78"],
+ [[-0xff_7865_4321], "\xdf\xbc\x9a\x87"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+ end
+end
+
+describe :array_pack_32bit_be_platform, shared: true do
+ it "encodes the least significant 32 bits of a number" do
+ [ [[0x7865_4321], "\x78\x65\x43\x21"],
+ [[-0x7865_4321], "\x87\x9a\xbc\xdf"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "encodes the number of array elements specified by the count modifier" do
+ str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format(2))
+ str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd"
+ end
+
+ it "encodes all remaining elements when passed the '*' modifier" do
+ str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format('*'))
+ str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd\x78\x65\x43\x21"
+ end
+
+ platform_is wordsize: 64 do
+ it "encodes the least significant 32 bits of a number that is greater than 32 bits" do
+ [ [[0xff_7865_4321], "\x78\x65\x43\x21"],
+ [[-0xff_7865_4321], "\x87\x9a\xbc\xdf"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+ end
+end
+
+describe :array_pack_64bit_le, shared: true do
+ it "encodes the least significant 64 bits of a positive number" do
+ [ [[0x0000_0000_0000_0021], "\x21\x00\x00\x00\x00\x00\x00\x00"],
+ [[0x0000_0000_0000_4321], "\x21\x43\x00\x00\x00\x00\x00\x00"],
+ [[0x0000_0000_0065_4321], "\x21\x43\x65\x00\x00\x00\x00\x00"],
+ [[0x0000_0000_7865_4321], "\x21\x43\x65\x78\x00\x00\x00\x00"],
+ [[0x0000_0090_7865_4321], "\x21\x43\x65\x78\x90\x00\x00\x00"],
+ [[0x0000_ba90_7865_4321], "\x21\x43\x65\x78\x90\xba\x00\x00"],
+ [[0x00dc_ba90_7865_4321], "\x21\x43\x65\x78\x90\xba\xdc\x00"],
+ [[0x7edc_ba90_7865_4321], "\x21\x43\x65\x78\x90\xba\xdc\x7e"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "encodes the least significant 64 bits of a negative number" do
+ [ [[-0x0000_0000_0000_0021], "\xdf\xff\xff\xff\xff\xff\xff\xff"],
+ [[-0x0000_0000_0000_4321], "\xdf\xbc\xff\xff\xff\xff\xff\xff"],
+ [[-0x0000_0000_0065_4321], "\xdf\xbc\x9a\xff\xff\xff\xff\xff"],
+ [[-0x0000_0000_7865_4321], "\xdf\xbc\x9a\x87\xff\xff\xff\xff"],
+ [[-0x0000_0090_7865_4321], "\xdf\xbc\x9a\x87\x6f\xff\xff\xff"],
+ [[-0x0000_ba90_7865_4321], "\xdf\xbc\x9a\x87\x6f\x45\xff\xff"],
+ [[-0x00dc_ba90_7865_4321], "\xdf\xbc\x9a\x87\x6f\x45\x23\xff"],
+ [[-0x7edc_ba90_7865_4321], "\xdf\xbc\x9a\x87\x6f\x45\x23\x81"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "encodes a Float truncated as an Integer" do
+ [ [[9.14138647331322368e+18], "\x00\x44\x65\x78\x90\xba\xdc\x7e"],
+ [[-9.14138647331322368e+18], "\x00\xbc\x9a\x87\x6f\x45\x23\x81"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "calls #to_int to convert the pack argument to an Integer" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(0x1234_5678_90ab_cdef)
+ [obj].pack(pack_format()).should == "\xef\xcd\xab\x90\x78\x56\x34\x12"
+ end
+
+ it "encodes the number of array elements specified by the count modifier" do
+ str = [0x1234_5678_90ab_cdef,
+ 0xdef0_abcd_3412_7856,
+ 0x7865_4321_dcba_def0].pack(pack_format(2))
+ str.should == "\xef\xcd\xab\x90\x78\x56\x34\x12\x56\x78\x12\x34\xcd\xab\xf0\xde"
+ end
+
+ it "encodes all remaining elements when passed the '*' modifier" do
+ str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format('*'))
+ 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"
+ end
+
+ it "ignores spaces between directives" do
+ str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format(' ', 2))
+ str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78"
+ end
+end
+
+describe :array_pack_64bit_be, shared: true do
+ it "encodes the least significant 64 bits of a positive number" do
+ [ [[0x0000_0000_0000_0021], "\x00\x00\x00\x00\x00\x00\x00\x21"],
+ [[0x0000_0000_0000_4321], "\x00\x00\x00\x00\x00\x00\x43\x21"],
+ [[0x0000_0000_0065_4321], "\x00\x00\x00\x00\x00\x65\x43\x21"],
+ [[0x0000_0000_7865_4321], "\x00\x00\x00\x00\x78\x65\x43\x21"],
+ [[0x0000_0090_7865_4321], "\x00\x00\x00\x90\x78\x65\x43\x21"],
+ [[0x0000_ba90_7865_4321], "\x00\x00\xba\x90\x78\x65\x43\x21"],
+ [[0x00dc_ba90_7865_4321], "\x00\xdc\xba\x90\x78\x65\x43\x21"],
+ [[0x7edc_ba90_7865_4321], "\x7e\xdc\xba\x90\x78\x65\x43\x21"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "encodes the least significant 64 bits of a negative number" do
+ [ [[-0x0000_0000_0000_0021], "\xff\xff\xff\xff\xff\xff\xff\xdf"],
+ [[-0x0000_0000_0000_4321], "\xff\xff\xff\xff\xff\xff\xbc\xdf"],
+ [[-0x0000_0000_0065_4321], "\xff\xff\xff\xff\xff\x9a\xbc\xdf"],
+ [[-0x0000_0000_7865_4321], "\xff\xff\xff\xff\x87\x9a\xbc\xdf"],
+ [[-0x0000_0090_7865_4321], "\xff\xff\xff\x6f\x87\x9a\xbc\xdf"],
+ [[-0x0000_ba90_7865_4321], "\xff\xff\x45\x6f\x87\x9a\xbc\xdf"],
+ [[-0x00dc_ba90_7865_4321], "\xff\x23\x45\x6f\x87\x9a\xbc\xdf"],
+ [[-0x7edc_ba90_7865_4321], "\x81\x23\x45\x6f\x87\x9a\xbc\xdf"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "encodes a Float truncated as an Integer" do
+ [ [[9.14138647331322368e+18], "\x7e\xdc\xba\x90\x78\x65\x44\x00"],
+ [[-9.14138647331322368e+18], "\x81\x23\x45\x6f\x87\x9a\xbc\x00"]
+ ].should be_computed_by(:pack, pack_format())
+ end
+
+ it "calls #to_int to convert the pack argument to an Integer" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(0x1234_5678_90ab_cdef)
+ [obj].pack(pack_format()).should == "\x12\x34\x56\x78\x90\xab\xcd\xef"
+ end
+
+ it "encodes the number of array elements specified by the count modifier" do
+ str = [0x1234_5678_90ab_cdef,
+ 0xdef0_abcd_3412_7856,
+ 0x7865_4321_dcba_def0].pack(pack_format(2))
+ str.should == "\x12\x34\x56\x78\x90\xab\xcd\xef\xde\xf0\xab\xcd\x34\x12\x78\x56"
+ end
+
+ it "encodes all remaining elements when passed the '*' modifier" do
+ str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format('*'))
+ 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"
+ end
+
+ it "ignores spaces between directives" do
+ str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format(' ', 2))
+ str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0"
+ end
+end
diff --git a/spec/ruby/core/array/pack/shared/numeric_basic.rb b/spec/ruby/core/array/pack/shared/numeric_basic.rb
new file mode 100644
index 0000000000..9224d6080e
--- /dev/null
+++ b/spec/ruby/core/array/pack/shared/numeric_basic.rb
@@ -0,0 +1,44 @@
+describe :array_pack_numeric_basic, shared: true do
+ it "returns an empty String if count is zero" do
+ [1].pack(pack_format(0)).should == ""
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { [nil].pack(pack_format) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed true" do
+ lambda { [true].pack(pack_format) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed false" do
+ lambda { [false].pack(pack_format) }.should raise_error(TypeError)
+ end
+
+ it "returns an ASCII-8BIT string" do
+ [0xFF].pack(pack_format).encoding.should == Encoding::ASCII_8BIT
+ [0xE3, 0x81, 0x82].pack(pack_format(3)).encoding.should == Encoding::ASCII_8BIT
+ end
+end
+
+describe :array_pack_integer, shared: true do
+ it "raises a TypeError when the object does not respond to #to_int" do
+ obj = mock('not an integer')
+ lambda { [obj].pack(pack_format) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a String" do
+ lambda { ["5"].pack(pack_format) }.should raise_error(TypeError)
+ end
+end
+
+describe :array_pack_float, shared: true do
+ it "raises a TypeError if a String does not represent a floating point number" do
+ lambda { ["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')
+ lambda { [obj].pack(pack_format) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/array/pack/shared/string.rb b/spec/ruby/core/array/pack/shared/string.rb
new file mode 100644
index 0000000000..256c1c08e8
--- /dev/null
+++ b/spec/ruby/core/array/pack/shared/string.rb
@@ -0,0 +1,48 @@
+# -*- encoding: binary -*-
+describe :array_pack_string, shared: true do
+ it "adds count bytes of a String to the output" do
+ ["abc"].pack(pack_format(2)).should == "ab"
+ end
+
+ it "implicitly has a count of one when no count is specified" do
+ ["abc"].pack(pack_format).should == "a"
+ end
+
+ it "does not add any bytes when the count is zero" do
+ ["abc"].pack(pack_format(0)).should == ""
+ end
+
+ it "is not affected by a previous count modifier" do
+ ["abcde", "defg"].pack(pack_format(3)+pack_format).should == "abcd"
+ end
+
+ it "raises an ArgumentError when the Array is empty" do
+ lambda { [].pack(pack_format) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the Array has too few elements" do
+ lambda { ["a"].pack(pack_format(nil, 2)) }.should raise_error(ArgumentError)
+ end
+
+ it "calls #to_str to convert the element to a String" do
+ obj = mock('pack string')
+ obj.should_receive(:to_str).and_return("abc")
+
+ [obj].pack(pack_format).should == "a"
+ end
+
+ it "raises a TypeError when the object does not respond to #to_str" do
+ obj = mock("not a string")
+ lambda { [obj].pack(pack_format) }.should raise_error(TypeError)
+ end
+
+ it "returns a string in encoding of common to the concatenated results" do
+ f = pack_format("*")
+ [ [["\u{3042 3044 3046 3048}", 0x2000B].pack(f+"U"), Encoding::ASCII_8BIT],
+ [["abcde\xd1", "\xFF\xFe\x81\x82"].pack(f+"u"), Encoding::ASCII_8BIT],
+ [["a".force_encoding("ascii"), "\xFF\xFe\x81\x82"].pack(f+"u"), Encoding::ASCII_8BIT],
+ # under discussion [ruby-dev:37294]
+ [["\u{3042 3044 3046 3048}", 1].pack(f+"N"), Encoding::ASCII_8BIT]
+ ].should be_computed_by(:encoding)
+ end
+end
diff --git a/spec/ruby/core/array/pack/shared/taint.rb b/spec/ruby/core/array/pack/shared/taint.rb
new file mode 100644
index 0000000000..88f349cb24
--- /dev/null
+++ b/spec/ruby/core/array/pack/shared/taint.rb
@@ -0,0 +1,33 @@
+describe :array_pack_taint, shared: true do
+ it "returns a tainted string when a pack argument is tainted" do
+ ["abcd".taint, 0x20].pack(pack_format("3C")).tainted?.should be_true
+ end
+
+ it "does not return a tainted string when the array is tainted" do
+ ["abcd", 0x20].taint.pack(pack_format("3C")).tainted?.should be_false
+ end
+
+ it "returns a tainted string when the format is tainted" do
+ ["abcd", 0x20].pack(pack_format("3C").taint).tainted?.should be_true
+ end
+
+ it "returns a tainted string when an empty format is tainted" do
+ ["abcd", 0x20].pack("".taint).tainted?.should be_true
+ end
+
+ it "returns a untrusted string when the format is untrusted" do
+ ["abcd", 0x20].pack(pack_format("3C").untrust).untrusted?.should be_true
+ end
+
+ it "returns a untrusted string when the empty format is untrusted" do
+ ["abcd", 0x20].pack("".untrust).untrusted?.should be_true
+ end
+
+ it "returns a untrusted string when a pack argument is untrusted" do
+ ["abcd".untrust, 0x20].pack(pack_format("3C")).untrusted?.should be_true
+ end
+
+ it "returns a trusted string when the array is untrusted" do
+ ["abcd", 0x20].untrust.pack(pack_format("3C")).untrusted?.should be_false
+ end
+end
diff --git a/spec/ruby/core/array/pack/shared/unicode.rb b/spec/ruby/core/array/pack/shared/unicode.rb
new file mode 100644
index 0000000000..e16110c491
--- /dev/null
+++ b/spec/ruby/core/array/pack/shared/unicode.rb
@@ -0,0 +1,94 @@
+# -*- encoding: utf-8 -*-
+
+describe :array_pack_unicode, shared: true do
+ it "encodes ASCII values as a Unicode codepoint" do
+ [ [[0], "\x00"],
+ [[1], "\x01"],
+ [[8], "\x08"],
+ [[15], "\x0f"],
+ [[24], "\x18"],
+ [[31], "\x1f"],
+ [[127], "\x7f"],
+ [[128], "\xc2\x80"],
+ [[129], "\xc2\x81"],
+ [[255], "\xc3\xbf"]
+ ].should be_computed_by(:pack, "U")
+ end
+
+ it "encodes UTF-8 BMP codepoints" do
+ [ [[0x80], "\xc2\x80"],
+ [[0x7ff], "\xdf\xbf"],
+ [[0x800], "\xe0\xa0\x80"],
+ [[0xffff], "\xef\xbf\xbf"]
+ ].should be_computed_by(:pack, "U")
+ end
+
+ it "constructs strings with valid encodings" do
+ str = [0x85].pack("U*")
+ str.should == "\xc2\x85"
+ str.valid_encoding?.should be_true
+ end
+
+ it "encodes values larger than UTF-8 max codepoints" do
+ [
+ [[0x00110000], [244, 144, 128, 128].pack('C*').force_encoding('utf-8')],
+ [[0x04000000], [252, 132, 128, 128, 128, 128].pack('C*').force_encoding('utf-8')],
+ [[0x7FFFFFFF], [253, 191, 191, 191, 191, 191].pack('C*').force_encoding('utf-8')]
+ ].should be_computed_by(:pack, "U")
+ end
+
+ it "encodes UTF-8 max codepoints" do
+ [ [[0x10000], "\xf0\x90\x80\x80"],
+ [[0xfffff], "\xf3\xbf\xbf\xbf"],
+ [[0x100000], "\xf4\x80\x80\x80"],
+ [[0x10ffff], "\xf4\x8f\xbf\xbf"]
+ ].should be_computed_by(:pack, "U")
+ end
+
+ it "encodes the number of array elements specified by the count modifier" do
+ [ [[0x41, 0x42, 0x43, 0x44], "U2", "\x41\x42"],
+ [[0x41, 0x42, 0x43, 0x44], "U2U", "\x41\x42\x43"]
+ ].should be_computed_by(:pack)
+ end
+
+ it "encodes all remaining elements when passed the '*' modifier" do
+ [0x41, 0x42, 0x43, 0x44].pack("U*").should == "\x41\x42\x43\x44"
+ end
+
+ it "calls #to_int to convert the pack argument to an Integer" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(5)
+ [obj].pack("U").should == "\x05"
+ end
+
+ it "raises a TypeError if #to_int does not return an Integer" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return("5")
+ lambda { [obj].pack("U") }.should raise_error(TypeError)
+ end
+
+ it "ignores NULL bytes between directives" do
+ [1, 2, 3].pack("U\x00U").should == "\x01\x02"
+ end
+
+ it "ignores spaces between directives" do
+ [1, 2, 3].pack("U U").should == "\x01\x02"
+ end
+
+ it "raises a RangeError if passed a negative number" do
+ lambda { [-1].pack("U") }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError if passed a number larger than an unsigned 32-bit integer" do
+ lambda { [2**32].pack("U") }.should raise_error(RangeError)
+ end
+
+ it "sets the output string to UTF-8 encoding" do
+ [ [[0x00].pack("U"), Encoding::UTF_8],
+ [[0x41].pack("U"), Encoding::UTF_8],
+ [[0x7F].pack("U"), Encoding::UTF_8],
+ [[0x80].pack("U"), Encoding::UTF_8],
+ [[0x10FFFF].pack("U"), Encoding::UTF_8]
+ ].should be_computed_by(:encoding)
+ end
+end
diff --git a/spec/ruby/core/array/pack/u_spec.rb b/spec/ruby/core/array/pack/u_spec.rb
new file mode 100644
index 0000000000..d708518c16
--- /dev/null
+++ b/spec/ruby/core/array/pack/u_spec.rb
@@ -0,0 +1,130 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/unicode'
+require_relative 'shared/taint'
+
+describe "Array#pack with format 'U'" do
+ it_behaves_like :array_pack_basic, 'U'
+ it_behaves_like :array_pack_basic_non_float, 'U'
+ it_behaves_like :array_pack_arguments, 'U'
+ it_behaves_like :array_pack_unicode, 'U'
+end
+
+describe "Array#pack with format 'u'" do
+ it_behaves_like :array_pack_basic, 'u'
+ it_behaves_like :array_pack_basic_non_float, 'u'
+ it_behaves_like :array_pack_arguments, 'u'
+ it_behaves_like :array_pack_taint, 'u'
+
+ it "encodes an empty string as an empty string" do
+ [""].pack("u").should == ""
+ end
+
+ it "appends a newline to the end of the encoded string" do
+ ["a"].pack("u").should == "!80``\n"
+ end
+
+ it "encodes one element per directive" do
+ ["abc", "DEF"].pack("uu").should == "#86)C\n#1$5&\n"
+ end
+
+ it "prepends the length of each segment of the input string as the first character (+32) in each line of the output" do
+ ["abcdefghijklm"].pack("u7").should == "&86)C9&5F\n&9VAI:FML\n!;0``\n"
+ end
+
+ it "encodes 1, 2, or 3 characters in 4 output characters (uuencoding)" do
+ [ [["a"], "!80``\n"],
+ [["ab"], "\"86(`\n"],
+ [["abc"], "#86)C\n"],
+ [["abcd"], "$86)C9```\n"],
+ [["abcde"], "%86)C9&4`\n"],
+ [["abcdef"], "&86)C9&5F\n"],
+ [["abcdefg"], "'86)C9&5F9P``\n"],
+ ].should be_computed_by(:pack, "u")
+ end
+
+ it "emits a newline after complete groups of count / 3 input characters when passed a count modifier" do
+ ["abcdefg"].pack("u3").should == "#86)C\n#9&5F\n!9P``\n"
+ end
+
+ it "implicitly has a count of 45 when passed '*', 0, 1, 2 or no count modifier" do
+ s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ r = "M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n%86%A86$`\n"
+ [ [[s], "u", r],
+ [[s], "u*", r],
+ [[s], "u0", r],
+ [[s], "u1", r],
+ [[s], "u2", r],
+ ].should be_computed_by(:pack)
+ end
+
+ it "encodes all ascii characters" do
+ [ [["\x00\x01\x02\x03\x04\x05\x06"], "'``$\"`P0%!@``\n"],
+ [["\a\b\t\n\v\f\r"], "'!P@)\"@L,#0``\n"],
+ [["\x0E\x0F\x10\x11\x12\x13\x14\x15\x16"], ")\#@\\0$1(3%!46\n"],
+ [["\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f"], ")%Q@9&AL<'1X?\n"],
+ [["!\"\#$%&'()*+,-./"], "/(2(C)\"4F)R@I*BLL+2XO\n"],
+ [["0123456789"], "*,\#$R,S0U-C<X.0``\n"],
+ [[":;<=>?@"], "'.CL\\/3X_0```\n"],
+ [["ABCDEFGHIJKLMNOPQRSTUVWXYZ"], ":04)#1$5&1TA)2DM,34Y/4%%24U155E=865H`\n"],
+ [["[\\]^_`"], "&6UQ=7E]@\n"],
+ [["abcdefghijklmnopqrstuvwxyz"], ":86)C9&5F9VAI:FML;6YO<'%R<W1U=G=X>7H`\n"],
+ [["{|}~"], "$>WQ]?@``\n"],
+ [["\x7f\xc2\x80\xc2\x81\xc2\x82\xc2\x83"], ")?\\*`PH'\"@L*#\n"],
+ [["\xc2\x84\xc2\x85\xc2\x86\xc2\x87\xc2"], ")PH3\"A<*&PH?\"\n"],
+ [["\x88\xc2\x89\xc2\x8a\xc2\x8b\xc2\x8c"], ")B,*)PHK\"B\\*,\n"],
+ [["\xc2\x8d\xc2\x8e\xc2\x8f\xc2\x90\xc2"], ")PHW\"CL*/PI#\"\n"],
+ [["\x91\xc2\x92\xc2\x93\xc2\x94\xc2\x95"], ")D<*2PI/\"E,*5\n"],
+ [["\xc2\x96\xc2\x97\xc2\x98\xc2\x99\xc2"], ")PI;\"E\\*8PIG\"\n"],
+ [["\x9a\xc2\x9b\xc2\x9c\xc2\x9d\xc2\x9e"], ")FL*;PIS\"G<*>\n"],
+ [["\xc2\x9f\xc2\xa0\xc2\xa1\xc2\xa2\xc2"], ")PI_\"H,*APJ+\"\n"],
+ [["\xa3\xc2\xa4\xc2\xa5\xc2\xa6\xc2\xa7"], ")H\\*DPJ7\"IL*G\n"],
+ [["\xc2\xa8\xc2\xa9\xc2\xaa\xc2\xab\xc2"], ")PJC\"J<*JPJO\"\n"],
+ [["\xac\xc2\xad\xc2\xae\xc2\xaf\xc2\xb0"], ")K,*MPJ[\"K\\*P\n"],
+ [["\xc2\xb1\xc2\xb2\xc2\xb3\xc2\xb4\xc2"], ")PK'\"LL*SPK3\"\n"],
+ [["\xb5\xc2\xb6\xc2\xb7\xc2\xb8\xc2\xb9"], ")M<*VPK?\"N,*Y\n"],
+ [["\xc2\xba\xc2\xbb\xc2\xbc\xc2\xbd\xc2"], ")PKK\"N\\*\\PKW\"\n"],
+ [["\xbe\xc2\xbf\xc3\x80\xc3\x81\xc3\x82"], ")OL*_PX#\#@<.\"\n"],
+ [["\xc3\x83\xc3\x84\xc3\x85\xc3\x86\xc3"], ")PX/#A,.%PX;#\n"],
+ [["\x87\xc3\x88\xc3\x89\xc3\x8a\xc3\x8b"], ")A\\.(PXG#BL.+\n"],
+ [["\xc3\x8c\xc3\x8d\xc3\x8e\xc3\x8f\xc3"], ")PXS#C<..PX_#\n"],
+ [["\x90\xc3\x91\xc3\x92\xc3\x93\xc3\x94"], ")D,.1PY+#D\\.4\n"],
+ [["\xc3\x95\xc3\x96\xc3\x97\xc3\x98\xc3"], ")PY7#EL.7PYC#\n"],
+ [["\x99\xc3\x9a\xc3\x9b\xc3\x9c\xc3\x9d"], ")F<.:PYO#G,.=\n"],
+ [["\xc3\x9e\xc3\x9f\xc3\xa0\xc3\xa1\xc3"], ")PY[#G\\.@PZ'#\n"],
+ [["\xa2\xc3\xa3\xc3\xa4\xc3\xa5\xc3\xa6"], ")HL.CPZ3#I<.F\n"],
+ [["\xc3\xa7\xc3\xa8\xc3\xa9\xc3\xaa\xc3"], ")PZ?#J,.IPZK#\n"],
+ [["\xab\xc3\xac\xc3\xad\xc3\xae\xc3\xaf"], ")J\\.LPZW#KL.O\n"],
+ [["\xc3\xb0\xc3\xb1\xc3\xb2\xc3\xb3\xc3"], ")P[##L<.RP[/#\n"],
+ [["\xb4\xc3\xb5\xc3\xb6\xc3\xb7\xc3\xb8"], ")M,.UP[;#M\\.X\n"],
+ [["\xc3\xb9\xc3\xba\xc3\xbb\xc3\xbc\xc3"], ")P[G#NL.[P[S#\n"],
+ [["\xbd\xc3\xbe\xc3\xbf"], "%O<.^P[\\`\n"]
+ ].should be_computed_by(:pack, "u")
+ end
+
+ it "calls #to_str to convert an object to a String" do
+ obj = mock("pack m string")
+ obj.should_receive(:to_str).and_return("abc")
+ [obj].pack("u").should == "#86)C\n"
+ end
+
+ it "raises a TypeError if #to_str does not return a String" do
+ obj = mock("pack m non-string")
+ lambda { [obj].pack("u") }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed nil" do
+ lambda { [nil].pack("u") }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an Integer" do
+ lambda { [0].pack("u") }.should raise_error(TypeError)
+ lambda { [bignum_value].pack("u") }.should raise_error(TypeError)
+ end
+
+ it "sets the output string to US-ASCII encoding" do
+ ["abcd"].pack("u").encoding.should == Encoding::US_ASCII
+ end
+end
diff --git a/spec/ruby/core/array/pack/v_spec.rb b/spec/ruby/core/array/pack/v_spec.rb
new file mode 100644
index 0000000000..d3932c84af
--- /dev/null
+++ b/spec/ruby/core/array/pack/v_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/numeric_basic'
+require_relative 'shared/integer'
+
+describe "Array#pack with format 'V'" do
+ it_behaves_like :array_pack_basic, 'V'
+ it_behaves_like :array_pack_basic_non_float, 'V'
+ it_behaves_like :array_pack_arguments, 'V'
+ it_behaves_like :array_pack_numeric_basic, 'V'
+ it_behaves_like :array_pack_integer, 'V'
+ it_behaves_like :array_pack_no_platform, 'V'
+ it_behaves_like :array_pack_32bit_le, 'V'
+end
+
+describe "Array#pack with format 'v'" do
+ it_behaves_like :array_pack_basic, 'v'
+ it_behaves_like :array_pack_basic_non_float, 'v'
+ it_behaves_like :array_pack_arguments, 'v'
+ it_behaves_like :array_pack_numeric_basic, 'v'
+ it_behaves_like :array_pack_integer, 'v'
+ it_behaves_like :array_pack_no_platform, 'v'
+ it_behaves_like :array_pack_16bit_le, 'v'
+end
diff --git a/spec/ruby/core/array/pack/w_spec.rb b/spec/ruby/core/array/pack/w_spec.rb
new file mode 100644
index 0000000000..76be1f925f
--- /dev/null
+++ b/spec/ruby/core/array/pack/w_spec.rb
@@ -0,0 +1,42 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/numeric_basic'
+
+describe "Array#pack with format 'w'" do
+ it_behaves_like :array_pack_basic, 'w'
+ it_behaves_like :array_pack_basic_non_float, 'w'
+ it_behaves_like :array_pack_arguments, 'w'
+ it_behaves_like :array_pack_numeric_basic, 'w'
+
+ it "encodes a BER-compressed integer" do
+ [ [[0], "\x00"],
+ [[1], "\x01"],
+ [[9999], "\xce\x0f"],
+ [[2**65], "\x84\x80\x80\x80\x80\x80\x80\x80\x80\x00"]
+ ].should be_computed_by(:pack, "w")
+ end
+
+ it "calls #to_int to convert the pack argument to an Integer" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(5)
+ [obj].pack("w").should == "\x05"
+ end
+
+ it "ignores NULL bytes between directives" do
+ [1, 2, 3].pack("w\x00w").should == "\x01\x02"
+ end
+
+ it "ignores spaces between directives" do
+ [1, 2, 3].pack("w w").should == "\x01\x02"
+ end
+
+ it "raises an ArgumentError when passed a negative value" do
+ lambda { [-1].pack("w") }.should raise_error(ArgumentError)
+ end
+
+ it "returns an ASCII-8BIT string" do
+ [1].pack('w').encoding.should == Encoding::ASCII_8BIT
+ end
+end
diff --git a/spec/ruby/core/array/pack/x_spec.rb b/spec/ruby/core/array/pack/x_spec.rb
new file mode 100644
index 0000000000..45fe34d08d
--- /dev/null
+++ b/spec/ruby/core/array/pack/x_spec.rb
@@ -0,0 +1,64 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+
+describe "Array#pack with format 'x'" do
+ it_behaves_like :array_pack_basic, 'x'
+ it_behaves_like :array_pack_basic_non_float, 'x'
+ it_behaves_like :array_pack_no_platform, 'x'
+
+ it "adds a NULL byte with an empty array" do
+ [].pack("x").should == "\x00"
+ end
+
+ it "adds a NULL byte without consuming an element" do
+ [1, 2].pack("CxC").should == "\x01\x00\x02"
+ end
+
+ it "is not affected by a previous count modifier" do
+ [].pack("x3x").should == "\x00\x00\x00\x00"
+ end
+
+ it "adds multiple NULL bytes when passed a count modifier" do
+ [].pack("x3").should == "\x00\x00\x00"
+ end
+
+ it "does not add a NULL byte if the count modifier is zero" do
+ [].pack("x0").should == ""
+ end
+
+ it "does not add a NULL byte when passed the '*' modifier" do
+ [].pack("x*").should == ""
+ end
+end
+
+describe "Array#pack with format 'X'" do
+ it_behaves_like :array_pack_basic, 'X'
+ it_behaves_like :array_pack_basic_non_float, 'X'
+ it_behaves_like :array_pack_no_platform, 'X'
+
+ it "reduces the output string by one byte at the point it is encountered" do
+ [1, 2, 3].pack("C2XC").should == "\x01\x03"
+ end
+
+ it "does not consume any elements" do
+ [1, 2, 3].pack("CXC").should == "\x02"
+ end
+
+ it "reduces the output string by multiple bytes when passed a count modifier" do
+ [1, 2, 3, 4, 5].pack("C2X2C").should == "\x03"
+ end
+
+ it "has no affect when passed the '*' modifier" do
+ [1, 2, 3].pack("C2X*C").should == "\x01\x02\x03"
+ end
+
+ it "raises an ArgumentError if the output string is empty" do
+ lambda { [1, 2, 3].pack("XC") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the count modifier is greater than the bytes in the string" do
+ lambda { [1, 2, 3].pack("C2X3") }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/array/pack/z_spec.rb b/spec/ruby/core/array/pack/z_spec.rb
new file mode 100644
index 0000000000..d0600f0c26
--- /dev/null
+++ b/spec/ruby/core/array/pack/z_spec.rb
@@ -0,0 +1,34 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/string'
+require_relative 'shared/taint'
+
+describe "Array#pack with format 'Z'" do
+ it_behaves_like :array_pack_basic, 'Z'
+ it_behaves_like :array_pack_basic_non_float, 'Z'
+ it_behaves_like :array_pack_no_platform, 'Z'
+ it_behaves_like :array_pack_string, 'Z'
+ it_behaves_like :array_pack_taint, 'Z'
+
+ it "adds all the bytes and appends a NULL byte when passed the '*' modifier" do
+ ["abc"].pack("Z*").should == "abc\x00"
+ end
+
+ it "padds the output with NULL bytes when the count exceeds the size of the String" do
+ ["abc"].pack("Z6").should == "abc\x00\x00\x00"
+ end
+
+ it "adds a NULL byte when the value is nil" do
+ [nil].pack("Z").should == "\x00"
+ end
+
+ it "pads the output with NULL bytes when the value is nil" do
+ [nil].pack("Z3").should == "\x00\x00\x00"
+ end
+
+ it "does not append a NULL byte when passed the '*' modifier and the value is nil" do
+ [nil].pack("Z*").should == "\x00"
+ end
+end
diff --git a/spec/ruby/core/array/partition_spec.rb b/spec/ruby/core/array/partition_spec.rb
new file mode 100644
index 0000000000..be36fffcab
--- /dev/null
+++ b/spec/ruby/core/array/partition_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#partition" do
+ it "returns two arrays" do
+ [].partition {}.should == [[], []]
+ end
+
+ it "returns in the left array values for which the block evaluates to true" do
+ ary = [0, 1, 2, 3, 4, 5]
+
+ ary.partition { |i| true }.should == [ary, []]
+ ary.partition { |i| 5 }.should == [ary, []]
+ ary.partition { |i| false }.should == [[], ary]
+ ary.partition { |i| nil }.should == [[], ary]
+ ary.partition { |i| i % 2 == 0 }.should == [[0, 2, 4], [1, 3, 5]]
+ ary.partition { |i| i / 3 == 0 }.should == [[0, 1, 2], [3, 4, 5]]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.partition { true }.should == [[empty], []]
+ empty.partition { false }.should == [[], [empty]]
+
+ array = ArraySpecs.recursive_array
+ array.partition { true }.should == [
+ [1, 'two', 3.0, array, array, array, array, array],
+ []
+ ]
+ condition = true
+ array.partition { condition = !condition }.should == [
+ ['two', array, array, array],
+ [1, 3.0, array, array]
+ ]
+ end
+
+ it "does not return subclass instances on Array subclasses" do
+ result = ArraySpecs::MyArray[1, 2, 3].partition { |x| x % 2 == 0 }
+ result.should be_an_instance_of(Array)
+ result[0].should be_an_instance_of(Array)
+ result[1].should be_an_instance_of(Array)
+ end
+end
diff --git a/spec/ruby/core/array/permutation_spec.rb b/spec/ruby/core/array/permutation_spec.rb
new file mode 100644
index 0000000000..f15bd76639
--- /dev/null
+++ b/spec/ruby/core/array/permutation_spec.rb
@@ -0,0 +1,138 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+
+describe "Array#permutation" do
+
+ before :each do
+ @numbers = (1..3).to_a
+ @yielded = []
+ end
+
+ it "returns an Enumerator of all permutations when called without a block or arguments" do
+ enum = @numbers.permutation
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.sort.should == [
+ [1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]
+ ].sort
+ end
+
+ it "returns an Enumerator of permutations of given length when called with an argument but no block" do
+ enum = @numbers.permutation(1)
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.sort.should == [[1],[2],[3]]
+ end
+
+ it "yields all permutations to the block then returns self when called with block but no arguments" do
+ array = @numbers.permutation {|n| @yielded << n}
+ array.should be_an_instance_of(Array)
+ array.sort.should == @numbers.sort
+ @yielded.sort.should == [
+ [1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]
+ ].sort
+ end
+
+ it "yields all permutations of given length to the block then returns self when called with block and argument" do
+ array = @numbers.permutation(2) {|n| @yielded << n}
+ array.should be_an_instance_of(Array)
+ array.sort.should == @numbers.sort
+ @yielded.sort.should == [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]].sort
+ end
+
+ it "returns the empty permutation ([[]]) when the given length is 0" do
+ @numbers.permutation(0).to_a.should == [[]]
+ @numbers.permutation(0) { |n| @yielded << n }
+ @yielded.should == [[]]
+ end
+
+ it "returns the empty permutation([]) when called on an empty Array" do
+ [].permutation.to_a.should == [[]]
+ [].permutation { |n| @yielded << n }
+ @yielded.should == [[]]
+ end
+
+ it "returns no permutations when the given length has no permutations" do
+ @numbers.permutation(9).entries.size.should == 0
+ @numbers.permutation(9) { |n| @yielded << n }
+ @yielded.should == []
+ end
+
+ it "handles duplicate elements correctly" do
+ @numbers << 1
+ @numbers.permutation(2).sort.should == [
+ [1,1],[1,1],[1,2],[1,2],[1,3],[1,3],
+ [2,1],[2,1],[2,3],
+ [3,1],[3,1],[3,2]
+ ].sort
+ end
+
+ it "handles nested Arrays correctly" do
+ # The ugliness is due to the order of permutations returned by
+ # permutation being undefined combined with #sort croaking on Arrays of
+ # Arrays.
+ @numbers << [4,5]
+ got = @numbers.permutation(2).to_a
+ expected = [
+ [1, 2], [1, 3], [1, [4, 5]],
+ [2, 1], [2, 3], [2, [4, 5]],
+ [3, 1], [3, 2], [3, [4, 5]],
+ [[4, 5], 1], [[4, 5], 2], [[4, 5], 3]
+ ]
+ expected.each {|e| got.include?(e).should be_true}
+ got.size.should == expected.size
+ end
+
+ it "truncates Float arguments" do
+ @numbers.permutation(3.7).to_a.sort.should ==
+ @numbers.permutation(3).to_a.sort
+ end
+
+ it "returns an Enumerator which works as expected even when the array was modified" do
+ @numbers = [1, 2]
+ enum = @numbers.permutation
+ @numbers << 3
+ enum.to_a.sort.should == [
+ [1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]
+ ].sort
+ end
+
+ it "generates from a defensive copy, ignoring mutations" do
+ accum = []
+ ary = [1,2,3]
+ ary.permutation(3) do |x|
+ accum << x
+ ary[0] = 5
+ end
+
+ accum.should == [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
+ end
+
+ describe "when no block is given" do
+ describe "returned Enumerator" do
+ describe "size" do
+ describe "with an array size greater than 0" do
+ it "returns the descending factorial of array size and given length" do
+ @numbers.permutation(4).size.should == 0
+ @numbers.permutation(3).size.should == 6
+ @numbers.permutation(2).size.should == 6
+ @numbers.permutation(1).size.should == 3
+ @numbers.permutation(0).size.should == 1
+ end
+ it "returns the descending factorial of array size with array size when there's no param" do
+ @numbers.permutation.size.should == 6
+ [1,2,3,4].permutation.size.should == 24
+ [1].permutation.size.should == 1
+ end
+ end
+ describe "with an empty array" do
+ it "returns 1 when the given length is 0" do
+ [].permutation(0).size.should == 1
+ end
+ it "returns 1 when there's param" do
+ [].permutation.size.should == 1
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/array/plus_spec.rb b/spec/ruby/core/array/plus_spec.rb
new file mode 100644
index 0000000000..7692163980
--- /dev/null
+++ b/spec/ruby/core/array/plus_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#+" do
+ it "concatenates two arrays" do
+ ([ 1, 2, 3 ] + [ 3, 4, 5 ]).should == [1, 2, 3, 3, 4, 5]
+ ([ 1, 2, 3 ] + []).should == [1, 2, 3]
+ ([] + [ 1, 2, 3 ]).should == [1, 2, 3]
+ ([] + []).should == []
+ end
+
+ it "can concatenate an array with itself" do
+ ary = [1, 2, 3]
+ (ary + ary).should == [1, 2, 3, 1, 2, 3]
+ end
+
+ it "tries to convert the passed argument to an Array using #to_ary" do
+ obj = mock('["x", "y"]')
+ obj.should_receive(:to_ary).and_return(["x", "y"])
+ ([1, 2, 3] + obj).should == [1, 2, 3, "x", "y"]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ (empty + empty).should == [empty, empty]
+
+ array = ArraySpecs.recursive_array
+ (empty + array).should == [empty, 1, 'two', 3.0, array, array, array, array, array]
+ (array + array).should == [
+ 1, 'two', 3.0, array, array, array, array, array,
+ 1, 'two', 3.0, array, array, array, array, array]
+ end
+
+ it "does return subclass instances with Array subclasses" do
+ (ArraySpecs::MyArray[1, 2, 3] + []).should be_an_instance_of(Array)
+ (ArraySpecs::MyArray[1, 2, 3] + ArraySpecs::MyArray[]).should be_an_instance_of(Array)
+ ([1, 2, 3] + ArraySpecs::MyArray[]).should be_an_instance_of(Array)
+ end
+
+ it "does not call to_ary on array subclasses" do
+ ([5, 6] + ArraySpecs::ToAryArray[1, 2]).should == [5, 6, 1, 2]
+ end
+
+ it "does not get infected even if an original array is tainted" do
+ ([1, 2] + [3, 4]).tainted?.should be_false
+ ([1, 2].taint + [3, 4]).tainted?.should be_false
+ ([1, 2] + [3, 4].taint).tainted?.should be_false
+ ([1, 2].taint + [3, 4].taint).tainted?.should be_false
+ end
+
+ it "does not infected even if an original array is untrusted" do
+ ([1, 2] + [3, 4]).untrusted?.should be_false
+ ([1, 2].untrust + [3, 4]).untrusted?.should be_false
+ ([1, 2] + [3, 4].untrust).untrusted?.should be_false
+ ([1, 2].untrust + [3, 4].untrust).untrusted?.should be_false
+ end
+end
diff --git a/spec/ruby/core/array/pop_spec.rb b/spec/ruby/core/array/pop_spec.rb
new file mode 100644
index 0000000000..335a0f2b60
--- /dev/null
+++ b/spec/ruby/core/array/pop_spec.rb
@@ -0,0 +1,168 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#pop" do
+ it "removes and returns the last element of the array" do
+ a = ["a", 1, nil, true]
+
+ a.pop.should == true
+ a.should == ["a", 1, nil]
+
+ a.pop.should == nil
+ a.should == ["a", 1]
+
+ a.pop.should == 1
+ a.should == ["a"]
+
+ a.pop.should == "a"
+ a.should == []
+ end
+
+ it "returns nil if there are no more elements" do
+ [].pop.should == nil
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.pop.should == []
+
+ array = ArraySpecs.recursive_array
+ array.pop.should == [1, 'two', 3.0, array, array, array, array]
+ end
+
+ it "keeps taint status" do
+ a = [1, 2].taint
+ a.pop
+ a.tainted?.should be_true
+ a.pop
+ a.tainted?.should be_true
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { ArraySpecs.frozen_array.pop }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} on an empty frozen array" do
+ lambda { ArraySpecs.empty_frozen_array.pop }.should raise_error(frozen_error_class)
+ end
+
+ it "keeps untrusted status" do
+ a = [1, 2].untrust
+ a.pop
+ a.untrusted?.should be_true
+ a.pop
+ a.untrusted?.should be_true
+ end
+
+ describe "passed a number n as an argument" do
+ it "removes and returns an array with the last n elements of the array" do
+ a = [1, 2, 3, 4, 5, 6]
+
+ a.pop(0).should == []
+ a.should == [1, 2, 3, 4, 5, 6]
+
+ a.pop(1).should == [6]
+ a.should == [1, 2, 3, 4, 5]
+
+ a.pop(2).should == [4, 5]
+ a.should == [1, 2, 3]
+
+ a.pop(3).should == [1, 2, 3]
+ a.should == []
+ end
+
+ it "returns an array with the last n elements even if shift was invoked" do
+ a = [1, 2, 3, 4]
+ a.shift
+ a.pop(3).should == [2, 3, 4]
+ end
+
+ it "returns a new empty array if there are no more elements" do
+ a = []
+ popped1 = a.pop(1)
+ popped1.should == []
+ a.should == []
+
+ popped2 = a.pop(2)
+ popped2.should == []
+ a.should == []
+
+ popped1.should_not equal(popped2)
+ end
+
+ it "returns whole elements if n exceeds size of the array" do
+ a = [1, 2, 3, 4, 5]
+ a.pop(6).should == [1, 2, 3, 4, 5]
+ a.should == []
+ end
+
+ it "does not return self even when it returns whole elements" do
+ a = [1, 2, 3, 4, 5]
+ a.pop(5).should_not equal(a)
+
+ a = [1, 2, 3, 4, 5]
+ a.pop(6).should_not equal(a)
+ end
+
+ it "raises an ArgumentError if n is negative" do
+ lambda{ [1, 2, 3].pop(-1) }.should raise_error(ArgumentError)
+ end
+
+ it "tries to convert n to an Integer using #to_int" do
+ a = [1, 2, 3, 4]
+ a.pop(2.3).should == [3, 4]
+
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(2)
+ a.should == [1, 2]
+ a.pop(obj).should == [1, 2]
+ a.should == []
+ end
+
+ it "raises a TypeError when the passed n cannot be coerced to Integer" do
+ lambda{ [1, 2].pop("cat") }.should raise_error(TypeError)
+ lambda{ [1, 2].pop(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError if more arguments are passed" do
+ lambda{ [1, 2].pop(1, 2) }.should raise_error(ArgumentError)
+ end
+
+ it "does not return subclass instances with Array subclass" do
+ ArraySpecs::MyArray[1, 2, 3].pop(2).should be_an_instance_of(Array)
+ end
+
+ it "returns an untainted array even if the array is tainted" do
+ ary = [1, 2].taint
+ ary.pop(2).tainted?.should be_false
+ ary.pop(0).tainted?.should be_false
+ end
+
+ it "keeps taint status" do
+ a = [1, 2].taint
+ a.pop(2)
+ a.tainted?.should be_true
+ a.pop(2)
+ a.tainted?.should be_true
+ end
+
+ it "returns a trusted array even if the array is untrusted" do
+ ary = [1, 2].untrust
+ ary.pop(2).untrusted?.should be_false
+ ary.pop(0).untrusted?.should be_false
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { ArraySpecs.frozen_array.pop(2) }.should raise_error(frozen_error_class)
+ lambda { ArraySpecs.frozen_array.pop(0) }.should raise_error(frozen_error_class)
+ end
+
+ it "keeps untrusted status" do
+ a = [1, 2].untrust
+ a.pop(2)
+ a.untrusted?.should be_true
+ a.pop(2)
+ a.untrusted?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/core/array/prepend_spec.rb b/spec/ruby/core/array/prepend_spec.rb
new file mode 100644
index 0000000000..22230ec300
--- /dev/null
+++ b/spec/ruby/core/array/prepend_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/unshift'
+
+ruby_version_is "2.5" do
+ describe "Array#prepend" do
+ it_behaves_like :array_unshift, :prepend
+ end
+end
diff --git a/spec/ruby/core/array/product_spec.rb b/spec/ruby/core/array/product_spec.rb
new file mode 100644
index 0000000000..9207a9b014
--- /dev/null
+++ b/spec/ruby/core/array/product_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#product" do
+ it "returns converted arguments using :to_ary" do
+ lambda{ [1].product(2..3) }.should raise_error(TypeError)
+ ar = ArraySpecs::ArrayConvertable.new(2,3)
+ [1].product(ar).should == [[1,2],[1,3]]
+ ar.called.should == :to_ary
+ end
+
+ it "returns the expected result" do
+ [1,2].product([3,4,5],[6,8]).should == [[1, 3, 6], [1, 3, 8], [1, 4, 6], [1, 4, 8], [1, 5, 6], [1, 5, 8],
+ [2, 3, 6], [2, 3, 8], [2, 4, 6], [2, 4, 8], [2, 5, 6], [2, 5, 8]]
+ end
+
+ it "has no required argument" do
+ [1,2].product.should == [[1],[2]]
+ end
+
+ it "returns an empty array when the argument is an empty array" do
+ [1, 2].product([]).should == []
+ end
+
+ it "does not attempt to produce an unreasonable number of products" do
+ a = (0..100).to_a
+ lambda do
+ a.product(a, a, a, a, a, a, a, a, a, a)
+ end.should raise_error(RangeError)
+ end
+
+ describe "when given a block" do
+ it "yields all combinations in turn" do
+ acc = []
+ [1,2].product([3,4,5],[6,8]){|array| acc << array}
+ acc.should == [[1, 3, 6], [1, 3, 8], [1, 4, 6], [1, 4, 8], [1, 5, 6], [1, 5, 8],
+ [2, 3, 6], [2, 3, 8], [2, 4, 6], [2, 4, 8], [2, 5, 6], [2, 5, 8]]
+
+ acc = []
+ [1,2].product([3,4,5],[],[6,8]){|array| acc << array}
+ acc.should be_empty
+ end
+
+ it "returns self" do
+ a = [1, 2, 3].freeze
+
+ a.product([1, 2]) { |p| p.first }.should == a
+ end
+
+ it "will ignore unreasonable numbers of products and yield anyway" do
+ a = (0..100).to_a
+ lambda do
+ a.product(a, a, a, a, a, a, a, a, a, a)
+ end.should raise_error(RangeError)
+ end
+ end
+
+ describe "when given an empty block" do
+ it "returns self" do
+ arr = [1,2]
+ arr.product([3,4,5],[6,8]){}.should equal(arr)
+ arr = []
+ arr.product([3,4,5],[6,8]){}.should equal(arr)
+ arr = [1,2]
+ arr.product([]){}.should equal(arr)
+ end
+ end
+end
diff --git a/spec/ruby/core/array/push_spec.rb b/spec/ruby/core/array/push_spec.rb
new file mode 100644
index 0000000000..607cbc7b4d
--- /dev/null
+++ b/spec/ruby/core/array/push_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/push'
+
+describe "Array#push" do
+ it_behaves_like :array_push, :push
+end
diff --git a/spec/ruby/core/array/rassoc_spec.rb b/spec/ruby/core/array/rassoc_spec.rb
new file mode 100644
index 0000000000..decdaae098
--- /dev/null
+++ b/spec/ruby/core/array/rassoc_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#rassoc" do
+ it "returns the first contained array whose second element is == object" do
+ ary = [[1, "a", 0.5], [2, "b"], [3, "b"], [4, "c"], [], [5], [6, "d"]]
+ ary.rassoc("a").should == [1, "a", 0.5]
+ ary.rassoc("b").should == [2, "b"]
+ ary.rassoc("d").should == [6, "d"]
+ ary.rassoc("z").should == nil
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.rassoc([]).should be_nil
+ [[empty, empty]].rassoc(empty).should == [empty, empty]
+
+ array = ArraySpecs.recursive_array
+ array.rassoc(array).should be_nil
+ [[empty, array]].rassoc(array).should == [empty, array]
+ end
+
+ it "calls elem == obj on the second element of each contained array" do
+ key = 'foobar'
+ o = mock('foobar')
+ def o.==(other); other == 'foobar'; end
+
+ [[1, :foobar], [2, o], [3, mock('foo')]].rassoc(key).should == [2, o]
+ end
+
+ it "does not check the last element in each contained but speficically the second" do
+ key = 'foobar'
+ o = mock('foobar')
+ def o.==(other); other == 'foobar'; end
+
+ [[1, :foobar, o], [2, o, 1], [3, mock('foo')]].rassoc(key).should == [2, o, 1]
+ end
+end
diff --git a/spec/ruby/core/array/reject_spec.rb b/spec/ruby/core/array/reject_spec.rb
new file mode 100644
index 0000000000..8bce7ad3bf
--- /dev/null
+++ b/spec/ruby/core/array/reject_spec.rb
@@ -0,0 +1,145 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumeratorize'
+require_relative 'shared/delete_if'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Array#reject" do
+ it "returns a new array without elements for which block is true" do
+ ary = [1, 2, 3, 4, 5]
+ ary.reject { true }.should == []
+ ary.reject { false }.should == ary
+ ary.reject { false }.should_not equal ary
+ ary.reject { nil }.should == ary
+ ary.reject { nil }.should_not equal ary
+ ary.reject { 5 }.should == []
+ ary.reject { |i| i < 3 }.should == [3, 4, 5]
+ ary.reject { |i| i % 2 == 0 }.should == [1, 3, 5]
+ end
+
+ it "returns self when called on an Array emptied with #shift" do
+ array = [1]
+ array.shift
+ array.reject { |x| true }.should == []
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.reject { false }.should == [empty]
+ empty.reject { true }.should == []
+
+ array = ArraySpecs.recursive_array
+ array.reject { false }.should == [1, 'two', 3.0, array, array, array, array, array]
+ array.reject { true }.should == []
+ end
+
+ it "does not return subclass instance on Array subclasses" do
+ ArraySpecs::MyArray[1, 2, 3].reject { |x| x % 2 == 0 }.should be_an_instance_of(Array)
+ end
+
+ it "does not retain instance variables" do
+ array = []
+ array.instance_variable_set("@variable", "value")
+ array.reject { false }.instance_variable_get("@variable").should == nil
+ end
+
+ it_behaves_like :enumeratorize, :reject
+ it_behaves_like :enumeratorized_with_origin_size, :reject, [1,2,3]
+end
+
+describe "Array#reject!" do
+ it "removes elements for which block is true" do
+ a = [3, 4, 5, 6, 7, 8, 9, 10, 11]
+ a.reject! { |i| i % 2 == 0 }.should equal(a)
+ a.should == [3, 5, 7, 9, 11]
+ a.reject! { |i| i > 8 }
+ a.should == [3, 5, 7]
+ a.reject! { |i| i < 4 }
+ a.should == [5, 7]
+ a.reject! { |i| i == 5 }
+ a.should == [7]
+ a.reject! { true }
+ a.should == []
+ a.reject! { true }
+ a.should == []
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty_dup = empty.dup
+ empty.reject! { false }.should == nil
+ empty.should == empty_dup
+
+ empty = ArraySpecs.empty_recursive_array
+ empty.reject! { true }.should == []
+ empty.should == []
+
+ array = ArraySpecs.recursive_array
+ array_dup = array.dup
+ array.reject! { false }.should == nil
+ array.should == array_dup
+
+ array = ArraySpecs.recursive_array
+ array.reject! { true }.should == []
+ array.should == []
+ end
+
+ it "returns nil when called on an Array emptied with #shift" do
+ array = [1]
+ array.shift
+ array.reject! { |x| true }.should == nil
+ end
+
+ it "returns nil if no changes are made" do
+ a = [1, 2, 3]
+
+ a.reject! { |i| i < 0 }.should == nil
+
+ a.reject! { true }
+ a.reject! { true }.should == nil
+ end
+
+ it "returns an Enumerator if no block given, and the array is frozen" do
+ ArraySpecs.frozen_array.reject!.should be_an_instance_of(Enumerator)
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { ArraySpecs.frozen_array.reject! {} }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} on an empty frozen array" do
+ lambda { ArraySpecs.empty_frozen_array.reject! {} }.should raise_error(frozen_error_class)
+ end
+
+ it "does not truncate the array is the block raises an exception" do
+ a = [1, 2, 3]
+ begin
+ a.reject! { raise StandardError, 'Oops' }
+ rescue
+ end
+
+ a.should == [1, 2, 3]
+ end
+
+ ruby_version_is "2.4" do
+ it "only removes elements for which the block returns true, keeping the element which raised an error." do
+ a = [1, 2, 3, 4]
+ begin
+ a.reject! do |x|
+ case x
+ when 2 then true
+ when 3 then raise StandardError, 'Oops'
+ else false
+ end
+ end
+ rescue StandardError
+ end
+
+ a.should == [1, 3, 4]
+ end
+ end
+
+ it_behaves_like :enumeratorize, :reject!
+ it_behaves_like :enumeratorized_with_origin_size, :reject!, [1,2,3]
+ it_behaves_like :delete_if, :reject!
+end
diff --git a/spec/ruby/core/array/repeated_combination_spec.rb b/spec/ruby/core/array/repeated_combination_spec.rb
new file mode 100644
index 0000000000..373b7bb6e5
--- /dev/null
+++ b/spec/ruby/core/array/repeated_combination_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../../spec_helper'
+
+describe "Array#repeated_combination" do
+ before :each do
+ @array = [10, 11, 12]
+ end
+
+ it "returns an enumerator when no block is provided" do
+ @array.repeated_combination(2).should be_an_instance_of(Enumerator)
+ end
+
+ it "returns self when a block is given" do
+ @array.repeated_combination(2){}.should equal(@array)
+ end
+
+ it "yields nothing for negative length and return self" do
+ @array.repeated_combination(-1){ fail }.should equal(@array)
+ @array.repeated_combination(-10){ fail }.should equal(@array)
+ end
+
+ it "yields the expected repeated_combinations" do
+ @array.repeated_combination(2).to_a.sort.should == [[10, 10], [10, 11], [10, 12], [11, 11], [11, 12], [12, 12]]
+ @array.repeated_combination(3).to_a.sort.should == [[10, 10, 10], [10, 10, 11], [10, 10, 12], [10, 11, 11], [10, 11, 12],
+ [10, 12, 12], [11, 11, 11], [11, 11, 12], [11, 12, 12], [12, 12, 12]]
+ end
+
+ it "yields [] when length is 0" do
+ @array.repeated_combination(0).to_a.should == [[]] # one repeated_combination of length 0
+ [].repeated_combination(0).to_a.should == [[]] # one repeated_combination of length 0
+ end
+
+ it "yields nothing when the array is empty and num is non zero" do
+ [].repeated_combination(5).to_a.should == [] # one repeated_combination of length 0
+ end
+
+ it "yields a partition consisting of only singletons" do
+ @array.repeated_combination(1).sort.to_a.should == [[10],[11],[12]]
+ end
+
+ it "accepts sizes larger than the original array" do
+ @array.repeated_combination(4).to_a.sort.should ==
+ [[10, 10, 10, 10], [10, 10, 10, 11], [10, 10, 10, 12],
+ [10, 10, 11, 11], [10, 10, 11, 12], [10, 10, 12, 12],
+ [10, 11, 11, 11], [10, 11, 11, 12], [10, 11, 12, 12],
+ [10, 12, 12, 12], [11, 11, 11, 11], [11, 11, 11, 12],
+ [11, 11, 12, 12], [11, 12, 12, 12], [12, 12, 12, 12]]
+ end
+
+ it "generates from a defensive copy, ignoring mutations" do
+ accum = []
+ @array.repeated_combination(2) do |x|
+ accum << x
+ @array[0] = 1
+ end
+ accum.sort.should == [[10, 10], [10, 11], [10, 12], [11, 11], [11, 12], [12, 12]]
+ end
+
+ describe "when no block is given" do
+ describe "returned Enumerator" do
+ describe "size" do
+ it "returns 0 when the combination_size is < 0" do
+ @array.repeated_combination(-1).size.should == 0
+ [].repeated_combination(-2).size.should == 0
+ end
+
+ it "returns 1 when the combination_size is 0" do
+ @array.repeated_combination(0).size.should == 1
+ [].repeated_combination(0).size.should == 1
+ end
+
+ it "returns the binomial coeficient between combination_size and array size + combination_size -1" do
+ @array.repeated_combination(5).size.should == 21
+ @array.repeated_combination(4).size.should == 15
+ @array.repeated_combination(3).size.should == 10
+ @array.repeated_combination(2).size.should == 6
+ @array.repeated_combination(1).size.should == 3
+ @array.repeated_combination(0).size.should == 1
+ [].repeated_combination(0).size.should == 1
+ [].repeated_combination(1).size.should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/array/repeated_permutation_spec.rb b/spec/ruby/core/array/repeated_permutation_spec.rb
new file mode 100644
index 0000000000..a165fda09e
--- /dev/null
+++ b/spec/ruby/core/array/repeated_permutation_spec.rb
@@ -0,0 +1,94 @@
+require_relative '../../spec_helper'
+
+
+describe "Array#repeated_permutation" do
+
+ before :each do
+ @numbers = [10, 11, 12]
+ @permutations = [[10, 10], [10, 11], [10, 12], [11, 10], [11, 11], [11, 12], [12, 10], [12, 11], [12, 12]]
+ end
+
+ it "returns an Enumerator of all repeated permutations of given length when called without a block" do
+ enum = @numbers.repeated_permutation(2)
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.sort.should == @permutations
+ end
+
+ it "yields all repeated_permutations to the block then returns self when called with block but no arguments" do
+ yielded = []
+ @numbers.repeated_permutation(2) {|n| yielded << n}.should equal(@numbers)
+ yielded.sort.should == @permutations
+ end
+
+ it "yields the empty repeated_permutation ([[]]) when the given length is 0" do
+ @numbers.repeated_permutation(0).to_a.should == [[]]
+ [].repeated_permutation(0).to_a.should == [[]]
+ end
+
+ it "does not yield when called on an empty Array with a nonzero argument" do
+ [].repeated_permutation(10).to_a.should == []
+ end
+
+ it "handles duplicate elements correctly" do
+ @numbers[-1] = 10
+ @numbers.repeated_permutation(2).sort.should ==
+ [[10, 10], [10, 10], [10, 10], [10, 10], [10, 11], [10, 11], [11, 10], [11, 10], [11, 11]]
+ end
+
+ it "truncates Float arguments" do
+ @numbers.repeated_permutation(3.7).to_a.sort.should ==
+ @numbers.repeated_permutation(3).to_a.sort
+ end
+
+ it "returns an Enumerator which works as expected even when the array was modified" do
+ @numbers.shift
+ enum = @numbers.repeated_permutation(2)
+ @numbers.unshift 10
+ enum.to_a.sort.should == @permutations
+ end
+
+ it "allows permutations larger than the number of elements" do
+ [1,2].repeated_permutation(3).sort.should ==
+ [[1, 1, 1], [1, 1, 2], [1, 2, 1],
+ [1, 2, 2], [2, 1, 1], [2, 1, 2],
+ [2, 2, 1], [2, 2, 2]]
+ end
+
+ it "generates from a defensive copy, ignoring mutations" do
+ accum = []
+ ary = [1,2]
+ ary.repeated_permutation(3) do |x|
+ accum << x
+ ary[0] = 5
+ end
+
+ accum.sort.should ==
+ [[1, 1, 1], [1, 1, 2], [1, 2, 1],
+ [1, 2, 2], [2, 1, 1], [2, 1, 2],
+ [2, 2, 1], [2, 2, 2]]
+ end
+
+ describe "when no block is given" do
+ describe "returned Enumerator" do
+ describe "size" do
+ it "returns 0 when combination_size is < 0" do
+ @numbers.repeated_permutation(-1).size.should == 0
+ [].repeated_permutation(-1).size.should == 0
+ end
+
+ it "returns array size ** combination_size" do
+ @numbers.repeated_permutation(4).size.should == 81
+ @numbers.repeated_permutation(3).size.should == 27
+ @numbers.repeated_permutation(2).size.should == 9
+ @numbers.repeated_permutation(1).size.should == 3
+ @numbers.repeated_permutation(0).size.should == 1
+ [].repeated_permutation(4).size.should == 0
+ [].repeated_permutation(3).size.should == 0
+ [].repeated_permutation(2).size.should == 0
+ [].repeated_permutation(1).size.should == 0
+ [].repeated_permutation(0).size.should == 1
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/array/replace_spec.rb b/spec/ruby/core/array/replace_spec.rb
new file mode 100644
index 0000000000..2f53338f5e
--- /dev/null
+++ b/spec/ruby/core/array/replace_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/replace'
+
+describe "Array#replace" do
+ it_behaves_like :array_replace, :replace
+end
diff --git a/spec/ruby/core/array/reverse_each_spec.rb b/spec/ruby/core/array/reverse_each_spec.rb
new file mode 100644
index 0000000000..28b8bfcb34
--- /dev/null
+++ b/spec/ruby/core/array/reverse_each_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumeratorize'
+require_relative '../enumerable/shared/enumeratorized'
+
+# Modifying a collection while the contents are being iterated
+# gives undefined behavior. See
+# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633
+
+describe "Array#reverse_each" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "traverses array in reverse order and pass each element to block" do
+ [1, 3, 4, 6].reverse_each { |i| ScratchPad << i }
+ ScratchPad.recorded.should == [6, 4, 3, 1]
+ end
+
+ it "returns self" do
+ a = [:a, :b, :c]
+ a.reverse_each { |x| }.should equal(a)
+ end
+
+ it "yields only the top level element of an empty recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.reverse_each { |i| ScratchPad << i }
+ ScratchPad.recorded.should == [empty]
+ end
+
+ it "yields only the top level element of a recursive array" do
+ array = ArraySpecs.recursive_array
+ array.reverse_each { |i| ScratchPad << i }
+ ScratchPad.recorded.should == [array, array, array, array, array, 3.0, 'two', 1]
+ end
+
+ it "returns the correct size when no block is given" do
+ [1, 2, 3].reverse_each.size.should == 3
+ end
+
+ it_behaves_like :enumeratorize, :reverse_each
+ it_behaves_like :enumeratorized_with_origin_size, :reverse_each, [1,2,3]
+end
diff --git a/spec/ruby/core/array/reverse_spec.rb b/spec/ruby/core/array/reverse_spec.rb
new file mode 100644
index 0000000000..d8ff26639d
--- /dev/null
+++ b/spec/ruby/core/array/reverse_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#reverse" do
+ it "returns a new array with the elements in reverse order" do
+ [].reverse.should == []
+ [1, 3, 5, 2].reverse.should == [2, 5, 3, 1]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.reverse.should == empty
+
+ array = ArraySpecs.recursive_array
+ array.reverse.should == [array, array, array, array, array, 3.0, 'two', 1]
+ end
+
+ it "does not return subclass instance on Array subclasses" do
+ ArraySpecs::MyArray[1, 2, 3].reverse.should be_an_instance_of(Array)
+ end
+end
+
+describe "Array#reverse!" do
+ it "reverses the elements in place" do
+ a = [6, 3, 4, 2, 1]
+ a.reverse!.should equal(a)
+ a.should == [1, 2, 4, 3, 6]
+ [].reverse!.should == []
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.reverse!.should == [empty]
+
+ array = ArraySpecs.recursive_array
+ array.reverse!.should == [array, array, array, array, array, 3.0, 'two', 1]
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { ArraySpecs.frozen_array.reverse! }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/array/rindex_spec.rb b/spec/ruby/core/array/rindex_spec.rb
new file mode 100644
index 0000000000..175c7bcfe2
--- /dev/null
+++ b/spec/ruby/core/array/rindex_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../enumerable/shared/enumeratorized'
+
+# Modifying a collection while the contents are being iterated
+# gives undefined behavior. See
+# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633
+
+describe "Array#rindex" do
+ it "returns the first index backwards from the end where element == to object" do
+ key = 3
+ uno = mock('one')
+ dos = mock('two')
+ tres = mock('three')
+ tres.should_receive(:==).any_number_of_times.and_return(false)
+ dos.should_receive(:==).any_number_of_times.and_return(true)
+ uno.should_not_receive(:==)
+ ary = [uno, dos, tres]
+
+ ary.rindex(key).should == 1
+ end
+
+ it "returns size-1 if last element == to object" do
+ [2, 1, 3, 2, 5].rindex(5).should == 4
+ end
+
+ it "returns 0 if only first element == to object" do
+ [2, 1, 3, 1, 5].rindex(2).should == 0
+ end
+
+ it "returns nil if no element == to object" do
+ [1, 1, 3, 2, 1, 3].rindex(4).should == nil
+ end
+
+ it "returns correct index even after delete_at" do
+ array = ["fish", "bird", "lion", "cat"]
+ array.delete_at(0)
+ array.rindex("lion").should == 1
+ end
+
+ it "properly handles empty recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.rindex(empty).should == 0
+ empty.rindex(1).should be_nil
+ end
+
+ it "properly handles recursive arrays" do
+ array = ArraySpecs.recursive_array
+ array.rindex(1).should == 0
+ array.rindex(array).should == 7
+ end
+
+ it "accepts a block instead of an argument" do
+ [4, 2, 1, 5, 1, 3].rindex { |x| x < 2 }.should == 4
+ end
+
+ it "ignores the block if there is an argument" do
+ -> {
+ [4, 2, 1, 5, 1, 3].rindex(5) { |x| x < 2 }.should == 3
+ }.should complain(/given block not used/)
+ end
+
+ it "rechecks the array size during iteration" do
+ ary = [4, 2, 1, 5, 1, 3]
+ seen = []
+ ary.rindex { |x| seen << x; ary.clear; false }
+
+ seen.should == [3]
+ end
+
+ describe "given no argument and no block" do
+ it "produces an Enumerator" do
+ enum = [4, 2, 1, 5, 1, 3].rindex
+ enum.should be_an_instance_of(Enumerator)
+ enum.each { |x| x < 2 }.should == 4
+ end
+ end
+
+ it_behaves_like :enumeratorized_with_unknown_size, :bsearch, [1,2,3]
+end
diff --git a/spec/ruby/core/array/rotate_spec.rb b/spec/ruby/core/array/rotate_spec.rb
new file mode 100644
index 0000000000..6450d3892b
--- /dev/null
+++ b/spec/ruby/core/array/rotate_spec.rb
@@ -0,0 +1,129 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#rotate" do
+ describe "when passed no argument" do
+ it "returns a copy of the array with the first element moved at the end" do
+ [1, 2, 3, 4, 5].rotate.should == [2, 3, 4, 5, 1]
+ end
+ end
+
+ describe "with an argument n" do
+ it "returns a copy of the array with the first (n % size) elements moved at the end" do
+ a = [1, 2, 3, 4, 5]
+ a.rotate( 2).should == [3, 4, 5, 1, 2]
+ a.rotate( -1).should == [5, 1, 2, 3, 4]
+ a.rotate(-21).should == [5, 1, 2, 3, 4]
+ a.rotate( 13).should == [4, 5, 1, 2, 3]
+ a.rotate( 0).should == a
+ end
+
+ it "coerces the argument using to_int" do
+ [1, 2, 3].rotate(2.6).should == [3, 1, 2]
+
+ obj = mock('integer_like')
+ obj.should_receive(:to_int).and_return(2)
+ [1, 2, 3].rotate(obj).should == [3, 1, 2]
+ end
+
+ it "raises a TypeError if not passed an integer-like argument" do
+ lambda {
+ [1, 2].rotate(nil)
+ }.should raise_error(TypeError)
+ lambda {
+ [1, 2].rotate("4")
+ }.should raise_error(TypeError)
+ end
+ end
+
+ it "returns a copy of the array when its length is one or zero" do
+ [1].rotate.should == [1]
+ [1].rotate(2).should == [1]
+ [1].rotate(-42).should == [1]
+ [ ].rotate.should == []
+ [ ].rotate(2).should == []
+ [ ].rotate(-42).should == []
+ end
+
+ it "does not mutate the receiver" do
+ lambda {
+ [].freeze.rotate
+ [2].freeze.rotate(2)
+ [1,2,3].freeze.rotate(-3)
+ }.should_not raise_error
+ end
+
+ it "does not return self" do
+ a = [1, 2, 3]
+ a.rotate.should_not equal(a)
+ a = []
+ a.rotate(0).should_not equal(a)
+ end
+
+ it "does not return subclass instance for Array subclasses" do
+ ArraySpecs::MyArray[1, 2, 3].rotate.should be_an_instance_of(Array)
+ end
+end
+
+describe "Array#rotate!" do
+ describe "when passed no argument" do
+ it "moves the first element to the end and returns self" do
+ a = [1, 2, 3, 4, 5]
+ a.rotate!.should equal(a)
+ a.should == [2, 3, 4, 5, 1]
+ end
+ end
+
+ describe "with an argument n" do
+ it "moves the first (n % size) elements at the end and returns self" do
+ a = [1, 2, 3, 4, 5]
+ a.rotate!(2).should equal(a)
+ a.should == [3, 4, 5, 1, 2]
+ a.rotate!(-12).should equal(a)
+ a.should == [1, 2, 3, 4, 5]
+ a.rotate!(13).should equal(a)
+ a.should == [4, 5, 1, 2, 3]
+ end
+
+ it "coerces the argument using to_int" do
+ [1, 2, 3].rotate!(2.6).should == [3, 1, 2]
+
+ obj = mock('integer_like')
+ obj.should_receive(:to_int).and_return(2)
+ [1, 2, 3].rotate!(obj).should == [3, 1, 2]
+ end
+
+ it "raises a TypeError if not passed an integer-like argument" do
+ lambda {
+ [1, 2].rotate!(nil)
+ }.should raise_error(TypeError)
+ lambda {
+ [1, 2].rotate!("4")
+ }.should raise_error(TypeError)
+ end
+ end
+
+ it "does nothing and returns self when the length is zero or one" do
+ a = [1]
+ a.rotate!.should equal(a)
+ a.should == [1]
+ a.rotate!(2).should equal(a)
+ a.should == [1]
+ a.rotate!(-21).should equal(a)
+ a.should == [1]
+
+ a = []
+ a.rotate!.should equal(a)
+ a.should == []
+ a.rotate!(2).should equal(a)
+ a.should == []
+ a.rotate!(-21).should equal(a)
+ a.should == []
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { [1, 2, 3].freeze.rotate!(0) }.should raise_error(frozen_error_class)
+ lambda { [1].freeze.rotate!(42) }.should raise_error(frozen_error_class)
+ lambda { [].freeze.rotate! }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/array/sample_spec.rb b/spec/ruby/core/array/sample_spec.rb
new file mode 100644
index 0000000000..3bd5d046cc
--- /dev/null
+++ b/spec/ruby/core/array/sample_spec.rb
@@ -0,0 +1,155 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#sample" do
+ it "samples evenly" do
+ ary = [0, 1, 2, 3]
+ 3.times do |i|
+ counts = [0, 0, 0, 0]
+ 4000.times do
+ counts[ary.sample(3)[i]] += 1
+ end
+ counts.each do |count|
+ (800..1200).should include(count)
+ end
+ end
+ end
+
+ it "returns nil for an empty Array" do
+ [].sample.should be_nil
+ end
+
+ it "returns a single value when not passed a count" do
+ [4].sample.should equal(4)
+ end
+
+ it "returns an empty Array when passed zero" do
+ [4].sample(0).should == []
+ end
+
+ it "returns an Array of elements when passed a count" do
+ [1, 2, 3, 4].sample(3).should be_an_instance_of(Array)
+ end
+
+ it "returns elements from the Array" do
+ array = [1, 2, 3, 4]
+ array.sample(3).all? { |x| array.should include(x) }
+ end
+
+ it "returns at most the number of elements in the Array" do
+ array = [1, 2, 3, 4]
+ result = array.sample(20)
+ result.size.should == 4
+ end
+
+ it "does not return the same value if the Array has unique values" do
+ array = [1, 2, 3, 4]
+ result = array.sample(20)
+ result.sort.should == array
+ end
+
+ it "may return the same value if the array is not unique" do
+ [4, 4].sample(2).should == [4,4]
+ end
+
+ it "calls #to_int to convert the count when passed an Object" do
+ [1, 2, 3, 4].sample(mock_int(2)).size.should == 2
+ end
+
+ it "raises ArgumentError when passed a negative count" do
+ lambda { [1, 2].sample(-1) }.should raise_error(ArgumentError)
+ end
+
+ it "does not return subclass instances with Array subclass" do
+ ArraySpecs::MyArray[1, 2, 3].sample(2).should be_an_instance_of(Array)
+ end
+
+ describe "with options" do
+ it "calls #to_hash to convert the passed Object" do
+ obj = mock("array_sample")
+ obj.should_receive(:to_hash).and_return({})
+ obj.should_not_receive(:to_int)
+
+ [1, 2].sample(obj).should be_an_instance_of(Fixnum)
+ end
+
+ it "calls #to_int on the first argument and #to_hash on the second when passed Objects" do
+ count = mock("array_sample_count")
+ count.should_receive(:to_int).and_return(2)
+ options = mock("array_sample_options")
+ options.should_receive(:to_hash).and_return({})
+
+ [1, 2].sample(count, options).size.should == 2
+ end
+
+ it "calls #rand on the Object passed by the :random key in the arguments Hash" do
+ obj = mock("array_sample_random")
+ obj.should_receive(:rand).and_return(0.5)
+
+ [1, 2].sample(random: obj).should be_an_instance_of(Fixnum)
+ end
+
+ it "raises a NoMethodError if an object passed for the RNG does not define #rand" do
+ obj = BasicObject.new
+
+ lambda { [1, 2].sample(random: obj) }.should raise_error(NoMethodError)
+ end
+
+ describe "when the object returned by #rand is a Fixnum" do
+ it "uses the fixnum as index" do
+ random = mock("array_sample_random_ret")
+ random.should_receive(:rand).and_return(0)
+
+ [1, 2].sample(random: random).should == 1
+
+ random = mock("array_sample_random_ret")
+ random.should_receive(:rand).and_return(1)
+
+ [1, 2].sample(random: random).should == 2
+ end
+
+ it "raises a RangeError if the value is less than zero" do
+ random = mock("array_sample_random")
+ random.should_receive(:rand).and_return(-1)
+
+ lambda { [1, 2].sample(random: random) }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError if the value is equal to the Array size" do
+ random = mock("array_sample_random")
+ random.should_receive(:rand).and_return(2)
+
+ lambda { [1, 2].sample(random: random) }.should raise_error(RangeError)
+ end
+ end
+ end
+
+ describe "when the object returned by #rand is not a Fixnum but responds to #to_int" do
+ it "calls #to_int on the Object" do
+ value = mock("array_sample_random_value")
+ value.should_receive(:to_int).and_return(1)
+ random = mock("array_sample_random")
+ random.should_receive(:rand).and_return(value)
+
+ [1, 2].sample(random: random).should == 2
+ end
+
+ it "raises a RangeError if the value is less than zero" do
+ value = mock("array_sample_random_value")
+ value.should_receive(:to_int).and_return(-1)
+ random = mock("array_sample_random")
+ random.should_receive(:rand).and_return(value)
+
+ lambda { [1, 2].sample(random: random) }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError if the value is equal to the Array size" do
+ value = mock("array_sample_random_value")
+ value.should_receive(:to_int).and_return(2)
+ random = mock("array_sample_random")
+ random.should_receive(:rand).and_return(value)
+
+ lambda { [1, 2].sample(random: random) }.should raise_error(RangeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/array/select_spec.rb b/spec/ruby/core/array/select_spec.rb
new file mode 100644
index 0000000000..298b591744
--- /dev/null
+++ b/spec/ruby/core/array/select_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require_relative 'shared/select'
+
+describe "Array#select" do
+ it_behaves_like :array_select, :select
+end
+
+describe "Array#select!" do
+ it "returns nil if no changes were made in the array" do
+ [1, 2, 3].select! { true }.should be_nil
+ end
+
+ it_behaves_like :keep_if, :select!
+end
diff --git a/spec/ruby/core/array/shared/clone.rb b/spec/ruby/core/array/shared/clone.rb
new file mode 100644
index 0000000000..95d0d0a3d5
--- /dev/null
+++ b/spec/ruby/core/array/shared/clone.rb
@@ -0,0 +1,42 @@
+describe :array_clone, shared: true do
+ it "returns an Array or a subclass instance" do
+ [].send(@method).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2].send(@method).should be_an_instance_of(ArraySpecs::MyArray)
+ end
+
+ it "produces a shallow copy where the references are directly copied" do
+ a = [mock('1'), mock('2')]
+ b = a.send @method
+ b.first.should equal a.first
+ b.last.should equal a.last
+ end
+
+ it "creates a new array containing all elements or the original" do
+ a = [1, 2, 3, 4]
+ b = a.send @method
+ b.should == a
+ b.__id__.should_not == a.__id__
+ end
+
+ it "copies taint status from the original" do
+ a = [1, 2, 3, 4]
+ b = [1, 2, 3, 4]
+ a.taint
+ aa = a.send @method
+ bb = b.send @method
+
+ aa.tainted?.should == true
+ bb.tainted?.should == false
+ end
+
+ it "copies untrusted status from the original" do
+ a = [1, 2, 3, 4]
+ b = [1, 2, 3, 4]
+ a.untrust
+ aa = a.send @method
+ bb = b.send @method
+
+ aa.untrusted?.should == true
+ bb.untrusted?.should == false
+ end
+end
diff --git a/spec/ruby/core/array/shared/collect.rb b/spec/ruby/core/array/shared/collect.rb
new file mode 100644
index 0000000000..8ad6e61855
--- /dev/null
+++ b/spec/ruby/core/array/shared/collect.rb
@@ -0,0 +1,136 @@
+require_relative '../../enumerable/shared/enumeratorized'
+
+describe :array_collect, shared: true do
+ it "returns a copy of array with each element replaced by the value returned by block" do
+ a = ['a', 'b', 'c', 'd']
+ b = a.send(@method) { |i| i + '!' }
+ b.should == ["a!", "b!", "c!", "d!"]
+ b.should_not equal a
+ end
+
+ it "does not return subclass instance" do
+ ArraySpecs::MyArray[1, 2, 3].send(@method) { |x| x + 1 }.should be_an_instance_of(Array)
+ end
+
+ it "does not change self" do
+ a = ['a', 'b', 'c', 'd']
+ a.send(@method) { |i| i + '!' }
+ a.should == ['a', 'b', 'c', 'd']
+ end
+
+ it "returns the evaluated value of block if it broke in the block" do
+ a = ['a', 'b', 'c', 'd']
+ b = a.send(@method) {|i|
+ if i == 'c'
+ break 0
+ else
+ i + '!'
+ end
+ }
+ b.should == 0
+ end
+
+ it "returns an Enumerator when no block given" do
+ a = [1, 2, 3]
+ a.send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ it "raises an ArgumentError when no block and with arguments" do
+ a = [1, 2, 3]
+ lambda {
+ a.send(@method, :foo)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "does not copy tainted status" do
+ a = [1, 2, 3]
+ a.taint
+ a.send(@method){|x| x}.tainted?.should be_false
+ end
+
+ it "does not copy untrusted status" do
+ a = [1, 2, 3]
+ a.untrust
+ a.send(@method){|x| x}.untrusted?.should be_false
+ end
+
+ before :all do
+ @object = [1, 2, 3, 4]
+ end
+ it_should_behave_like :enumeratorized_with_origin_size
+end
+
+describe :array_collect_b, shared: true do
+ it "replaces each element with the value returned by block" do
+ a = [7, 9, 3, 5]
+ a.send(@method) { |i| i - 1 }.should equal(a)
+ a.should == [6, 8, 2, 4]
+ end
+
+ it "returns self" do
+ a = [1, 2, 3, 4, 5]
+ b = a.send(@method) {|i| i+1 }
+ a.should equal b
+ end
+
+ it "returns the evaluated value of block but its contents is partially modified, if it broke in the block" do
+ a = ['a', 'b', 'c', 'd']
+ b = a.send(@method) {|i|
+ if i == 'c'
+ break 0
+ else
+ i + '!'
+ end
+ }
+ b.should == 0
+ a.should == ['a!', 'b!', 'c', 'd']
+ end
+
+ it "returns an Enumerator when no block given, and the enumerator can modify the original array" do
+ a = [1, 2, 3]
+ enum = a.send(@method)
+ enum.should be_an_instance_of(Enumerator)
+ enum.each{|i| "#{i}!" }
+ a.should == ["1!", "2!", "3!"]
+ end
+
+ it "keeps tainted status" do
+ a = [1, 2, 3]
+ a.taint
+ a.tainted?.should be_true
+ a.send(@method){|x| x}
+ a.tainted?.should be_true
+ end
+
+ it "keeps untrusted status" do
+ a = [1, 2, 3]
+ a.untrust
+ a.send(@method){|x| x}
+ a.untrusted?.should be_true
+ end
+
+ describe "when frozen" do
+ it "raises a #{frozen_error_class}" do
+ lambda { ArraySpecs.frozen_array.send(@method) {} }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} when empty" do
+ lambda { ArraySpecs.empty_frozen_array.send(@method) {} }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} when calling #each on the returned Enumerator" do
+ enumerator = ArraySpecs.frozen_array.send(@method)
+ lambda { enumerator.each {|x| x } }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} when calling #each on the returned Enumerator when empty" do
+ enumerator = ArraySpecs.empty_frozen_array.send(@method)
+ lambda { enumerator.each {|x| x } }.should raise_error(frozen_error_class)
+ end
+ end
+
+ before :all do
+ @object = [1, 2, 3, 4]
+ end
+ it_should_behave_like :enumeratorized_with_origin_size
+end
diff --git a/spec/ruby/core/array/shared/delete_if.rb b/spec/ruby/core/array/shared/delete_if.rb
new file mode 100644
index 0000000000..a3fdcf4fac
--- /dev/null
+++ b/spec/ruby/core/array/shared/delete_if.rb
@@ -0,0 +1,13 @@
+describe :delete_if, shared: true do
+ before :each do
+ @object = [1,2,3]
+ end
+
+ it "updates the receiver after all blocks" do
+ @object.send(@method) do |e|
+ @object.length.should == 3
+ true
+ end
+ @object.length.should == 0
+ end
+end
diff --git a/spec/ruby/core/array/shared/enumeratorize.rb b/spec/ruby/core/array/shared/enumeratorize.rb
new file mode 100644
index 0000000000..a19a5d3b9b
--- /dev/null
+++ b/spec/ruby/core/array/shared/enumeratorize.rb
@@ -0,0 +1,5 @@
+describe :enumeratorize, shared: true do
+ it "returns an Enumerator if no block given" do
+ [1,2].send(@method).should be_an_instance_of(Enumerator)
+ end
+end
diff --git a/spec/ruby/core/array/shared/eql.rb b/spec/ruby/core/array/shared/eql.rb
new file mode 100644
index 0000000000..b5d9128434
--- /dev/null
+++ b/spec/ruby/core/array/shared/eql.rb
@@ -0,0 +1,92 @@
+describe :array_eql, shared: true do
+ it "returns true if other is the same array" do
+ a = [1]
+ a.send(@method, a).should be_true
+ end
+
+ it "returns true if corresponding elements are #eql?" do
+ [].send(@method, []).should be_true
+ [1, 2, 3, 4].send(@method, [1, 2, 3, 4]).should be_true
+ end
+
+ it "returns false if other is shorter than self" do
+ [1, 2, 3, 4].send(@method, [1, 2, 3]).should be_false
+ end
+
+ it "returns false if other is longer than self" do
+ [1, 2, 3, 4].send(@method, [1, 2, 3, 4, 5]).should be_false
+ end
+
+ it "returns false immediately when sizes of the arrays differ" do
+ obj = mock('1')
+ obj.should_not_receive(@method)
+
+ [] .send(@method, [obj] ).should be_false
+ [obj] .send(@method, [] ).should be_false
+ end
+
+ it "handles well recursive arrays" do
+ a = ArraySpecs.empty_recursive_array
+ a .send(@method, [a] ).should be_true
+ a .send(@method, [[a]] ).should be_true
+ [a] .send(@method, a ).should be_true
+ [[a]] .send(@method, a ).should be_true
+ # These may be surprising, but no difference can be
+ # found between these arrays, so they are ==.
+ # There is no "path" that will lead to a difference
+ # (contrary to other examples below)
+
+ a2 = ArraySpecs.empty_recursive_array
+ a .send(@method, a2 ).should be_true
+ a .send(@method, [a2] ).should be_true
+ a .send(@method, [[a2]] ).should be_true
+ [a] .send(@method, a2 ).should be_true
+ [[a]] .send(@method, a2 ).should be_true
+
+ back = []
+ forth = [back]; back << forth;
+ back .send(@method, a ).should be_true
+
+ x = []; x << x << x
+ x .send(@method, a ).should be_false # since x.size != a.size
+ x .send(@method, [a, a] ).should be_false # since x[0].size != [a, a][0].size
+ x .send(@method, [x, a] ).should be_false # since x[1].size != [x, a][1].size
+ [x, a] .send(@method, [a, x] ).should be_false # etc...
+ x .send(@method, [x, x] ).should be_true
+ x .send(@method, [[x, x], [x, x]] ).should be_true
+
+ tree = [];
+ branch = []; branch << tree << tree; tree << branch
+ tree2 = [];
+ branch2 = []; branch2 << tree2 << tree2; tree2 << branch2
+ forest = [tree, branch, :bird, a]; forest << forest
+ forest2 = [tree2, branch2, :bird, a2]; forest2 << forest2
+
+ forest .send(@method, forest2 ).should be_true
+ forest .send(@method, [tree2, branch, :bird, a, forest2]).should be_true
+
+ diffforest = [branch2, tree2, :bird, a2]; diffforest << forest2
+ forest .send(@method, diffforest ).should be_false # since forest[0].size == 1 != 3 == diffforest[0]
+ forest .send(@method, [nil] ).should be_false
+ forest .send(@method, [forest] ).should be_false
+ end
+
+ it "does not call #to_ary on its argument" do
+ obj = mock('to_ary')
+ obj.should_not_receive(:to_ary)
+
+ [1, 2, 3].send(@method, obj).should be_false
+ end
+
+ it "does not call #to_ary on Array subclasses" do
+ ary = ArraySpecs::ToAryArray[5, 6, 7]
+ ary.should_not_receive(:to_ary)
+ [5, 6, 7].send(@method, ary).should be_true
+ end
+
+ it "ignores array class differences" do
+ ArraySpecs::MyArray[1, 2, 3].send(@method, [1, 2, 3]).should be_true
+ ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_true
+ [1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_true
+ end
+end
diff --git a/spec/ruby/core/array/shared/index.rb b/spec/ruby/core/array/shared/index.rb
new file mode 100644
index 0000000000..a9896554f2
--- /dev/null
+++ b/spec/ruby/core/array/shared/index.rb
@@ -0,0 +1,37 @@
+describe :array_index, shared: true do
+ it "returns the index of the first element == to object" do
+ x = mock('3')
+ def x.==(obj) 3 == obj; end
+
+ [2, x, 3, 1, 3, 1].send(@method, 3).should == 1
+ [2, 3.0, 3, x, 1, 3, 1].send(@method, x).should == 1
+ end
+
+ it "returns 0 if first element == to object" do
+ [2, 1, 3, 2, 5].send(@method, 2).should == 0
+ end
+
+ it "returns size-1 if only last element == to object" do
+ [2, 1, 3, 1, 5].send(@method, 5).should == 4
+ end
+
+ it "returns nil if no element == to object" do
+ [2, 1, 1, 1, 1].send(@method, 3).should == nil
+ end
+
+ it "accepts a block instead of an argument" do
+ [4, 2, 1, 5, 1, 3].send(@method) {|x| x < 2}.should == 2
+ end
+
+ it "ignores the block if there is an argument" do
+ -> {
+ [4, 2, 1, 5, 1, 3].send(@method, 5) {|x| x < 2}.should == 3
+ }.should complain(/given block not used/)
+ end
+
+ describe "given no argument and no block" do
+ it "produces an Enumerator" do
+ [].send(@method).should be_an_instance_of(Enumerator)
+ end
+ end
+end
diff --git a/spec/ruby/core/array/shared/inspect.rb b/spec/ruby/core/array/shared/inspect.rb
new file mode 100644
index 0000000000..1bcc9f9ca8
--- /dev/null
+++ b/spec/ruby/core/array/shared/inspect.rb
@@ -0,0 +1,131 @@
+require_relative '../fixtures/encoded_strings'
+
+describe :array_inspect, shared: true do
+ it "returns a string" do
+ [1, 2, 3].send(@method).should be_an_instance_of(String)
+ end
+
+ it "returns '[]' for an empty Array" do
+ [].send(@method).should == "[]"
+ end
+
+ it "calls inspect on its elements and joins the results with commas" do
+ items = Array.new(3) do |i|
+ obj = mock(i.to_s)
+ obj.should_receive(:inspect).and_return(i.to_s)
+ obj
+ end
+ items.send(@method).should == "[0, 1, 2]"
+ end
+
+ it "does not call #to_s on a String returned from #inspect" do
+ str = "abc"
+ str.should_not_receive(:to_s)
+
+ [str].send(@method).should == '["abc"]'
+ end
+
+ it "calls #to_s on the object returned from #inspect if the Object isn't a String" do
+ obj = mock("Array#inspect/to_s calls #to_s")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_receive(:to_s).and_return("abc")
+
+ [obj].send(@method).should == "[abc]"
+ end
+
+ it "does not call #to_str on the object returned from #inspect when it is not a String" do
+ obj = mock("Array#inspect/to_s does not call #to_str")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_not_receive(:to_str)
+
+ [obj].send(@method).should =~ /^\[#<MockObject:0x[0-9a-f]+>\]$/
+ end
+
+ it "does not call #to_str on the object returned from #to_s when it is not a String" do
+ obj = mock("Array#inspect/to_s does not call #to_str on #to_s result")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_receive(:to_s).and_return(obj)
+ obj.should_not_receive(:to_str)
+
+ [obj].send(@method).should =~ /^\[#<MockObject:0x[0-9a-f]+>\]$/
+ end
+
+ it "does not swallow exceptions raised by #to_s" do
+ obj = mock("Array#inspect/to_s does not swallow #to_s exceptions")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_receive(:to_s).and_raise(Exception)
+
+ lambda { [obj].send(@method) }.should raise_error(Exception)
+ end
+
+ it "represents a recursive element with '[...]'" do
+ ArraySpecs.recursive_array.send(@method).should == "[1, \"two\", 3.0, [...], [...], [...], [...], [...]]"
+ ArraySpecs.head_recursive_array.send(@method).should == "[[...], [...], [...], [...], [...], 1, \"two\", 3.0]"
+ ArraySpecs.empty_recursive_array.send(@method).should == "[[...]]"
+ end
+
+ it "taints the result if the Array is non-empty and tainted" do
+ [1, 2].taint.send(@method).tainted?.should be_true
+ end
+
+ it "does not taint the result if the Array is tainted but empty" do
+ [].taint.send(@method).tainted?.should be_false
+ end
+
+ it "taints the result if an element is tainted" do
+ ["str".taint].send(@method).tainted?.should be_true
+ end
+
+ it "untrusts the result if the Array is untrusted" do
+ [1, 2].untrust.send(@method).untrusted?.should be_true
+ end
+
+ it "does not untrust the result if the Array is untrusted but empty" do
+ [].untrust.send(@method).untrusted?.should be_false
+ end
+
+ it "untrusts the result if an element is untrusted" do
+ ["str".untrust].send(@method).untrusted?.should be_true
+ end
+
+ describe "with encoding" do
+ before :each do
+ @default_external_encoding = Encoding.default_external
+ end
+
+ after :each do
+ Encoding.default_external = @default_external_encoding
+ end
+
+ it "returns a US-ASCII string for an empty Array" do
+ [].send(@method).encoding.should == Encoding::US_ASCII
+ end
+
+ it "use the default external encoding if it is ascii compatible" do
+ Encoding.default_external = Encoding.find('UTF-8')
+
+ utf8 = "utf8".encode("UTF-8")
+ jp = "jp".encode("EUC-JP")
+ array = [jp, utf8]
+
+ array.send(@method).encoding.name.should == "UTF-8"
+ end
+
+ it "use US-ASCII encoding if the default external encoding is not ascii compatible" do
+ Encoding.default_external = Encoding.find('UTF-32')
+
+ utf8 = "utf8".encode("UTF-8")
+ jp = "jp".encode("EUC-JP")
+ array = [jp, utf8]
+
+ array.send(@method).encoding.name.should == "US-ASCII"
+ end
+
+ it "does not raise if inspected result is not default external encoding" do
+ utf_16be = mock("utf_16be")
+ utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode!(Encoding::UTF_16BE))
+
+ [utf_16be].send(@method).should == '["utf_16be \u3042"]'
+ end
+ end
+end
diff --git a/spec/ruby/core/array/shared/join.rb b/spec/ruby/core/array/shared/join.rb
new file mode 100644
index 0000000000..0fd2e0ff9b
--- /dev/null
+++ b/spec/ruby/core/array/shared/join.rb
@@ -0,0 +1,161 @@
+require_relative '../fixtures/classes'
+require_relative '../fixtures/encoded_strings'
+
+describe :array_join_with_default_separator, shared: true do
+ before :each do
+ @separator = $,
+ end
+
+ after :each do
+ $, = @separator
+ end
+
+ it "returns an empty string if the Array is empty" do
+ [].send(@method).should == ''
+ end
+
+ it "returns a US-ASCII string for an empty Array" do
+ [].send(@method).encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns a string formed by concatenating each String element separated by $," do
+ $, = " | "
+ ["1", "2", "3"].send(@method).should == "1 | 2 | 3"
+ end
+
+ it "attempts coercion via #to_str first" do
+ obj = mock('foo')
+ obj.should_receive(:to_str).any_number_of_times.and_return("foo")
+ [obj].send(@method).should == "foo"
+ end
+
+ it "attempts coercion via #to_ary second" do
+ obj = mock('foo')
+ obj.should_receive(:to_str).any_number_of_times.and_return(nil)
+ obj.should_receive(:to_ary).any_number_of_times.and_return(["foo"])
+ [obj].send(@method).should == "foo"
+ end
+
+ it "attempts coercion via #to_s third" do
+ obj = mock('foo')
+ obj.should_receive(:to_str).any_number_of_times.and_return(nil)
+ obj.should_receive(:to_ary).any_number_of_times.and_return(nil)
+ obj.should_receive(:to_s).any_number_of_times.and_return("foo")
+ [obj].send(@method).should == "foo"
+ end
+
+ it "raises a NoMethodError if an element does not respond to #to_str, #to_ary, or #to_s" do
+ obj = mock('o')
+ class << obj; undef :to_s; end
+ lambda { [1, obj].send(@method) }.should raise_error(NoMethodError)
+ end
+
+ it "raises an ArgumentError when the Array is recursive" do
+ lambda { ArraySpecs.recursive_array.send(@method) }.should raise_error(ArgumentError)
+ lambda { ArraySpecs.head_recursive_array.send(@method) }.should raise_error(ArgumentError)
+ lambda { ArraySpecs.empty_recursive_array.send(@method) }.should raise_error(ArgumentError)
+ end
+
+ it "taints the result if the Array is tainted and non-empty" do
+ [1, 2].taint.send(@method).tainted?.should be_true
+ end
+
+ it "does not taint the result if the Array is tainted but empty" do
+ [].taint.send(@method).tainted?.should be_false
+ end
+
+ it "taints the result if the result of coercing an element is tainted" do
+ s = mock("taint")
+ s.should_receive(:to_s).and_return("str".taint)
+ [s].send(@method).tainted?.should be_true
+ end
+
+ it "untrusts the result if the Array is untrusted and non-empty" do
+ [1, 2].untrust.send(@method).untrusted?.should be_true
+ end
+
+ it "does not untrust the result if the Array is untrusted but empty" do
+ [].untrust.send(@method).untrusted?.should be_false
+ end
+
+ it "untrusts the result if the result of coercing an element is untrusted" do
+ s = mock("untrust")
+ s.should_receive(:to_s).and_return("str".untrust)
+ [s].send(@method).untrusted?.should be_true
+ end
+
+ it "uses the first encoding when other strings are compatible" do
+ ary1 = ArraySpecs.array_with_7bit_utf8_and_usascii_strings
+ ary2 = ArraySpecs.array_with_usascii_and_7bit_utf8_strings
+ ary3 = ArraySpecs.array_with_utf8_and_7bit_ascii8bit_strings
+ ary4 = ArraySpecs.array_with_usascii_and_7bit_ascii8bit_strings
+
+ ary1.send(@method).encoding.should == Encoding::UTF_8
+ ary2.send(@method).encoding.should == Encoding::US_ASCII
+ ary3.send(@method).encoding.should == Encoding::UTF_8
+ ary4.send(@method).encoding.should == Encoding::US_ASCII
+ end
+
+ it "uses the widest common encoding when other strings are incompatible" do
+ ary1 = ArraySpecs.array_with_utf8_and_usascii_strings
+ ary2 = ArraySpecs.array_with_usascii_and_utf8_strings
+
+ ary1.send(@method).encoding.should == Encoding::UTF_8
+ ary2.send(@method).encoding.should == Encoding::UTF_8
+ end
+
+ it "fails for arrays with incompatibly-encoded strings" do
+ ary_utf8_bad_ascii8bit = ArraySpecs.array_with_utf8_and_ascii8bit_strings
+
+ lambda { ary_utf8_bad_ascii8bit.send(@method) }.should raise_error(EncodingError)
+ end
+end
+
+describe :array_join_with_string_separator, shared: true do
+ it "returns a string formed by concatenating each element.to_str separated by separator" do
+ obj = mock('foo')
+ obj.should_receive(:to_str).and_return("foo")
+ [1, 2, 3, 4, obj].send(@method, ' | ').should == '1 | 2 | 3 | 4 | foo'
+ end
+
+ it "uses the same separator with nested arrays" do
+ [1, [2, [3, 4], 5], 6].send(@method, ":").should == "1:2:3:4:5:6"
+ [1, [2, ArraySpecs::MyArray[3, 4], 5], 6].send(@method, ":").should == "1:2:3:4:5:6"
+ end
+
+ describe "with a tainted separator" do
+ before :each do
+ @sep = ":".taint
+ end
+
+ it "does not taint the result if the array is empty" do
+ [].send(@method, @sep).tainted?.should be_false
+ end
+
+ it "does not taint the result if the array has only one element" do
+ [1].send(@method, @sep).tainted?.should be_false
+ end
+
+ it "taints the result if the array has two or more elements" do
+ [1, 2].send(@method, @sep).tainted?.should be_true
+ end
+ end
+
+ describe "with an untrusted separator" do
+ before :each do
+ @sep = ":".untrust
+ end
+
+ it "does not untrust the result if the array is empty" do
+ [].send(@method, @sep).untrusted?.should be_false
+ end
+
+ it "does not untrust the result if the array has only one element" do
+ [1].send(@method, @sep).untrusted?.should be_false
+ end
+
+ it "untrusts the result if the array has two or more elements" do
+ [1, 2].send(@method, @sep).untrusted?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/core/array/shared/keep_if.rb b/spec/ruby/core/array/shared/keep_if.rb
new file mode 100644
index 0000000000..906ad9b9e2
--- /dev/null
+++ b/spec/ruby/core/array/shared/keep_if.rb
@@ -0,0 +1,60 @@
+require_relative '../../enumerable/shared/enumeratorized'
+
+describe :keep_if, shared: true do
+ it "deletes elements for which the block returns a false value" do
+ array = [1, 2, 3, 4, 5]
+ array.send(@method) {|item| item > 3 }.should equal(array)
+ array.should == [4, 5]
+ end
+
+ it "returns an enumerator if no block is given" do
+ [1, 2, 3].send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ it "updates the receiver after all blocks" do
+ a = [1, 2, 3]
+ a.send(@method) do |e|
+ a.length.should == 3
+ false
+ end
+ a.length.should == 0
+ end
+
+ before :all do
+ @object = [1,2,3]
+ end
+ it_should_behave_like :enumeratorized_with_origin_size
+
+ describe "on frozen objects" do
+ before :each do
+ @origin = [true, false]
+ @frozen = @origin.dup.freeze
+ end
+
+ it "returns an Enumerator if no block is given" do
+ @frozen.send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ describe "with truthy block" do
+ it "keeps elements after any exception" do
+ lambda { @frozen.send(@method) { true } }.should raise_error(Exception)
+ @frozen.should == @origin
+ end
+
+ it "raises a #{frozen_error_class}" do
+ lambda { @frozen.send(@method) { true } }.should raise_error(frozen_error_class)
+ end
+ end
+
+ describe "with falsy block" do
+ it "keeps elements after any exception" do
+ lambda { @frozen.send(@method) { false } }.should raise_error(Exception)
+ @frozen.should == @origin
+ end
+
+ it "raises a #{frozen_error_class}" do
+ lambda { @frozen.send(@method) { false } }.should raise_error(frozen_error_class)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/array/shared/length.rb b/spec/ruby/core/array/shared/length.rb
new file mode 100644
index 0000000000..f84966d0ba
--- /dev/null
+++ b/spec/ruby/core/array/shared/length.rb
@@ -0,0 +1,11 @@
+describe :array_length, shared: true do
+ it "returns the number of elements" do
+ [].send(@method).should == 0
+ [1, 2, 3].send(@method).should == 3
+ end
+
+ it "properly handles recursive arrays" do
+ ArraySpecs.empty_recursive_array.send(@method).should == 1
+ ArraySpecs.recursive_array.send(@method).should == 8
+ end
+end
diff --git a/spec/ruby/core/array/shared/push.rb b/spec/ruby/core/array/shared/push.rb
new file mode 100644
index 0000000000..effa632890
--- /dev/null
+++ b/spec/ruby/core/array/shared/push.rb
@@ -0,0 +1,33 @@
+describe :array_push, shared: true do
+ it "appends the arguments to the array" do
+ a = [ "a", "b", "c" ]
+ a.send(@method, "d", "e", "f").should equal(a)
+ a.send(@method).should == ["a", "b", "c", "d", "e", "f"]
+ a.send(@method, 5)
+ a.should == ["a", "b", "c", "d", "e", "f", 5]
+
+ a = [0, 1]
+ a.send(@method, 2)
+ a.should == [0, 1, 2]
+ end
+
+ it "isn't confused by previous shift" do
+ a = [ "a", "b", "c" ]
+ a.shift
+ a.send(@method, "foo")
+ a.should == ["b", "c", "foo"]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.send(@method, :last).should == [empty, :last]
+
+ array = ArraySpecs.recursive_array
+ array.send(@method, :last).should == [1, 'two', 3.0, array, array, array, array, array, :last]
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { ArraySpecs.frozen_array.send(@method, 1) }.should raise_error(frozen_error_class)
+ lambda { ArraySpecs.frozen_array.send(@method) }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/array/shared/replace.rb b/spec/ruby/core/array/shared/replace.rb
new file mode 100644
index 0000000000..b8dae8d33e
--- /dev/null
+++ b/spec/ruby/core/array/shared/replace.rb
@@ -0,0 +1,60 @@
+describe :array_replace, shared: true do
+ it "replaces the elements with elements from other array" do
+ a = [1, 2, 3, 4, 5]
+ b = ['a', 'b', 'c']
+ a.send(@method, b).should equal(a)
+ a.should == b
+ a.should_not equal(b)
+
+ a.send(@method, [4] * 10)
+ a.should == [4] * 10
+
+ a.send(@method, [])
+ a.should == []
+ end
+
+ it "properly handles recursive arrays" do
+ orig = [1, 2, 3]
+ empty = ArraySpecs.empty_recursive_array
+ orig.send(@method, empty)
+ orig.should == empty
+
+ array = ArraySpecs.recursive_array
+ orig.send(@method, array)
+ orig.should == array
+ end
+
+ it "returns self" do
+ ary = [1, 2, 3]
+ other = [:a, :b, :c]
+ ary.send(@method, other).should equal(ary)
+ end
+
+ it "does not make self dependent to the original array" do
+ ary = [1, 2, 3]
+ other = [:a, :b, :c]
+ ary.send(@method, other)
+ ary.should == [:a, :b, :c]
+ ary << :d
+ ary.should == [:a, :b, :c, :d]
+ other.should == [:a, :b, :c]
+ end
+
+ it "tries to convert the passed argument to an Array using #to_ary" do
+ obj = mock('to_ary')
+ obj.stub!(:to_ary).and_return([1, 2, 3])
+ [].send(@method, obj).should == [1, 2, 3]
+ end
+
+ it "does not call #to_ary on Array subclasses" do
+ obj = ArraySpecs::ToAryArray[5, 6, 7]
+ obj.should_not_receive(:to_ary)
+ [].send(@method, ArraySpecs::ToAryArray[5, 6, 7]).should == [5, 6, 7]
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda {
+ ArraySpecs.frozen_array.send(@method, ArraySpecs.frozen_array)
+ }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/array/shared/select.rb b/spec/ruby/core/array/shared/select.rb
new file mode 100644
index 0000000000..09101e8ab5
--- /dev/null
+++ b/spec/ruby/core/array/shared/select.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/enumeratorize'
+require_relative '../shared/keep_if'
+require_relative '../../enumerable/shared/enumeratorized'
+
+describe :array_select, shared: true do
+ it_should_behave_like :enumeratorize
+
+ before :each do
+ @object = [1,2,3]
+ end
+ it_should_behave_like :enumeratorized_with_origin_size
+
+ it "returns a new array of elements for which block is true" do
+ [1, 3, 4, 5, 6, 9].send(@method) { |i| i % ((i + 1) / 2) == 0}.should == [1, 4, 6]
+ end
+
+ it "does not return subclass instance on Array subclasses" do
+ ArraySpecs::MyArray[1, 2, 3].send(@method) { true }.should be_an_instance_of(Array)
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.send(@method) { true }.should == empty
+ empty.send(@method) { false }.should == []
+
+ array = ArraySpecs.recursive_array
+ array.send(@method) { true }.should == [1, 'two', 3.0, array, array, array, array, array]
+ array.send(@method) { false }.should == []
+ end
+end
diff --git a/spec/ruby/core/array/shared/slice.rb b/spec/ruby/core/array/shared/slice.rb
new file mode 100644
index 0000000000..b3f4ccb9a6
--- /dev/null
+++ b/spec/ruby/core/array/shared/slice.rb
@@ -0,0 +1,459 @@
+describe :array_slice, shared: true do
+ it "returns the element at index with [index]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 1).should == "b"
+
+ a = [1, 2, 3, 4]
+
+ a.send(@method, 0).should == 1
+ a.send(@method, 1).should == 2
+ a.send(@method, 2).should == 3
+ a.send(@method, 3).should == 4
+ a.send(@method, 4).should == nil
+ a.send(@method, 10).should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns the element at index from the end of the array with [-index]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, -2).should == "d"
+
+ a = [1, 2, 3, 4]
+
+ a.send(@method, -1).should == 4
+ a.send(@method, -2).should == 3
+ a.send(@method, -3).should == 2
+ a.send(@method, -4).should == 1
+ a.send(@method, -5).should == nil
+ a.send(@method, -10).should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns count elements starting from index with [index, count]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 2, 3).should == ["c", "d", "e"]
+
+ a = [1, 2, 3, 4]
+
+ a.send(@method, 0, 0).should == []
+ a.send(@method, 0, 1).should == [1]
+ a.send(@method, 0, 2).should == [1, 2]
+ a.send(@method, 0, 4).should == [1, 2, 3, 4]
+ a.send(@method, 0, 6).should == [1, 2, 3, 4]
+ a.send(@method, 0, -1).should == nil
+ a.send(@method, 0, -2).should == nil
+ a.send(@method, 0, -4).should == nil
+
+ a.send(@method, 2, 0).should == []
+ a.send(@method, 2, 1).should == [3]
+ a.send(@method, 2, 2).should == [3, 4]
+ a.send(@method, 2, 4).should == [3, 4]
+ a.send(@method, 2, -1).should == nil
+
+ a.send(@method, 4, 0).should == []
+ a.send(@method, 4, 2).should == []
+ a.send(@method, 4, -1).should == nil
+
+ a.send(@method, 5, 0).should == nil
+ a.send(@method, 5, 2).should == nil
+ a.send(@method, 5, -1).should == nil
+
+ a.send(@method, 6, 0).should == nil
+ a.send(@method, 6, 2).should == nil
+ a.send(@method, 6, -1).should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns count elements starting at index from the end of array with [-index, count]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, -2, 2).should == ["d", "e"]
+
+ a = [1, 2, 3, 4]
+
+ a.send(@method, -1, 0).should == []
+ a.send(@method, -1, 1).should == [4]
+ a.send(@method, -1, 2).should == [4]
+ a.send(@method, -1, -1).should == nil
+
+ a.send(@method, -2, 0).should == []
+ a.send(@method, -2, 1).should == [3]
+ a.send(@method, -2, 2).should == [3, 4]
+ a.send(@method, -2, 4).should == [3, 4]
+ a.send(@method, -2, -1).should == nil
+
+ a.send(@method, -4, 0).should == []
+ a.send(@method, -4, 1).should == [1]
+ a.send(@method, -4, 2).should == [1, 2]
+ a.send(@method, -4, 4).should == [1, 2, 3, 4]
+ a.send(@method, -4, 6).should == [1, 2, 3, 4]
+ a.send(@method, -4, -1).should == nil
+
+ a.send(@method, -5, 0).should == nil
+ a.send(@method, -5, 1).should == nil
+ a.send(@method, -5, 10).should == nil
+ a.send(@method, -5, -1).should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns the first count elements with [0, count]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 0, 3).should == ["a", "b", "c"]
+ end
+
+ it "returns the subarray which is independent to self with [index,count]" do
+ a = [1, 2, 3]
+ sub = a.send(@method, 1,2)
+ sub.replace([:a, :b])
+ a.should == [1, 2, 3]
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock('to_int')
+ obj.stub!(:to_int).and_return(2)
+
+ a = [1, 2, 3, 4]
+ a.send(@method, obj).should == 3
+ a.send(@method, obj, 1).should == [3]
+ a.send(@method, obj, obj).should == [3, 4]
+ a.send(@method, 0, obj).should == [1, 2]
+ end
+
+ it "returns the elements specified by Range indexes with [m..n]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 1..3).should == ["b", "c", "d"]
+ [ "a", "b", "c", "d", "e" ].send(@method, 4..-1).should == ['e']
+ [ "a", "b", "c", "d", "e" ].send(@method, 3..3).should == ['d']
+ [ "a", "b", "c", "d", "e" ].send(@method, 3..-2).should == ['d']
+ ['a'].send(@method, 0..-1).should == ['a']
+
+ a = [1, 2, 3, 4]
+
+ a.send(@method, 0..-10).should == []
+ a.send(@method, 0..0).should == [1]
+ a.send(@method, 0..1).should == [1, 2]
+ a.send(@method, 0..2).should == [1, 2, 3]
+ a.send(@method, 0..3).should == [1, 2, 3, 4]
+ a.send(@method, 0..4).should == [1, 2, 3, 4]
+ a.send(@method, 0..10).should == [1, 2, 3, 4]
+
+ a.send(@method, 2..-10).should == []
+ a.send(@method, 2..0).should == []
+ a.send(@method, 2..2).should == [3]
+ a.send(@method, 2..3).should == [3, 4]
+ a.send(@method, 2..4).should == [3, 4]
+
+ a.send(@method, 3..0).should == []
+ a.send(@method, 3..3).should == [4]
+ a.send(@method, 3..4).should == [4]
+
+ a.send(@method, 4..0).should == []
+ a.send(@method, 4..4).should == []
+ a.send(@method, 4..5).should == []
+
+ a.send(@method, 5..0).should == nil
+ a.send(@method, 5..5).should == nil
+ a.send(@method, 5..6).should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns elements specified by Range indexes except the element at index n with [m...n]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 1...3).should == ["b", "c"]
+
+ a = [1, 2, 3, 4]
+
+ a.send(@method, 0...-10).should == []
+ a.send(@method, 0...0).should == []
+ a.send(@method, 0...1).should == [1]
+ a.send(@method, 0...2).should == [1, 2]
+ a.send(@method, 0...3).should == [1, 2, 3]
+ a.send(@method, 0...4).should == [1, 2, 3, 4]
+ a.send(@method, 0...10).should == [1, 2, 3, 4]
+
+ a.send(@method, 2...-10).should == []
+ a.send(@method, 2...0).should == []
+ a.send(@method, 2...2).should == []
+ a.send(@method, 2...3).should == [3]
+ a.send(@method, 2...4).should == [3, 4]
+
+ a.send(@method, 3...0).should == []
+ a.send(@method, 3...3).should == []
+ a.send(@method, 3...4).should == [4]
+
+ a.send(@method, 4...0).should == []
+ a.send(@method, 4...4).should == []
+ a.send(@method, 4...5).should == []
+
+ a.send(@method, 5...0).should == nil
+ a.send(@method, 5...5).should == nil
+ a.send(@method, 5...6).should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns elements that exist if range start is in the array but range end is not with [m..n]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 4..7).should == ["e"]
+ end
+
+ it "accepts Range instances having a negative m and both signs for n with [m..n] and [m...n]" do
+ a = [1, 2, 3, 4]
+
+ a.send(@method, -1..-1).should == [4]
+ a.send(@method, -1...-1).should == []
+ a.send(@method, -1..3).should == [4]
+ a.send(@method, -1...3).should == []
+ a.send(@method, -1..4).should == [4]
+ a.send(@method, -1...4).should == [4]
+ a.send(@method, -1..10).should == [4]
+ a.send(@method, -1...10).should == [4]
+ a.send(@method, -1..0).should == []
+ a.send(@method, -1..-4).should == []
+ a.send(@method, -1...-4).should == []
+ a.send(@method, -1..-6).should == []
+ a.send(@method, -1...-6).should == []
+
+ a.send(@method, -2..-2).should == [3]
+ a.send(@method, -2...-2).should == []
+ a.send(@method, -2..-1).should == [3, 4]
+ a.send(@method, -2...-1).should == [3]
+ a.send(@method, -2..10).should == [3, 4]
+ a.send(@method, -2...10).should == [3, 4]
+
+ a.send(@method, -4..-4).should == [1]
+ a.send(@method, -4..-2).should == [1, 2, 3]
+ a.send(@method, -4...-2).should == [1, 2]
+ a.send(@method, -4..-1).should == [1, 2, 3, 4]
+ a.send(@method, -4...-1).should == [1, 2, 3]
+ a.send(@method, -4..3).should == [1, 2, 3, 4]
+ a.send(@method, -4...3).should == [1, 2, 3]
+ a.send(@method, -4..4).should == [1, 2, 3, 4]
+ a.send(@method, -4...4).should == [1, 2, 3, 4]
+ a.send(@method, -4...4).should == [1, 2, 3, 4]
+ a.send(@method, -4..0).should == [1]
+ a.send(@method, -4...0).should == []
+ a.send(@method, -4..1).should == [1, 2]
+ a.send(@method, -4...1).should == [1]
+
+ a.send(@method, -5..-5).should == nil
+ a.send(@method, -5...-5).should == nil
+ a.send(@method, -5..-4).should == nil
+ a.send(@method, -5..-1).should == nil
+ a.send(@method, -5..10).should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns the subarray which is independent to self with [m..n]" do
+ a = [1, 2, 3]
+ sub = a.send(@method, 1..2)
+ sub.replace([:a, :b])
+ a.should == [1, 2, 3]
+ end
+
+ it "tries to convert Range elements to Integers using #to_int with [m..n] and [m...n]" do
+ from = mock('from')
+ to = mock('to')
+
+ # So we can construct a range out of them...
+ def from.<=>(o) 0 end
+ def to.<=>(o) 0 end
+
+ def from.to_int() 1 end
+ def to.to_int() -2 end
+
+ a = [1, 2, 3, 4]
+
+ a.send(@method, from..to).should == [2, 3]
+ a.send(@method, from...to).should == [2]
+ a.send(@method, 1..0).should == []
+ a.send(@method, 1...0).should == []
+
+ lambda { a.send(@method, "a" .. "b") }.should raise_error(TypeError)
+ lambda { a.send(@method, "a" ... "b") }.should raise_error(TypeError)
+ lambda { a.send(@method, from .. "b") }.should raise_error(TypeError)
+ lambda { a.send(@method, from ... "b") }.should raise_error(TypeError)
+ end
+
+ it "returns the same elements as [m..n] and [m...n] with Range subclasses" do
+ a = [1, 2, 3, 4]
+ range_incl = ArraySpecs::MyRange.new(1, 2)
+ range_excl = ArraySpecs::MyRange.new(-3, -1, true)
+
+ a.send(@method, range_incl).should == [2, 3]
+ a.send(@method, range_excl).should == [2, 3]
+ end
+
+ it "returns nil for a requested index not in the array with [index]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 5).should == nil
+ end
+
+ it "returns [] if the index is valid but length is zero with [index, length]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 0, 0).should == []
+ [ "a", "b", "c", "d", "e" ].send(@method, 2, 0).should == []
+ end
+
+ it "returns nil if length is zero but index is invalid with [index, length]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 100, 0).should == nil
+ [ "a", "b", "c", "d", "e" ].send(@method, -50, 0).should == nil
+ end
+
+ # This is by design. It is in the official documentation.
+ it "returns [] if index == array.size with [index, length]" do
+ %w|a b c d e|.send(@method, 5, 2).should == []
+ end
+
+ it "returns nil if index > array.size with [index, length]" do
+ %w|a b c d e|.send(@method, 6, 2).should == nil
+ end
+
+ it "returns nil if length is negative with [index, length]" do
+ %w|a b c d e|.send(@method, 3, -1).should == nil
+ %w|a b c d e|.send(@method, 2, -2).should == nil
+ %w|a b c d e|.send(@method, 1, -100).should == nil
+ end
+
+ it "returns nil if no requested index is in the array with [m..n]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, 6..10).should == nil
+ end
+
+ it "returns nil if range start is not in the array with [m..n]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, -10..2).should == nil
+ [ "a", "b", "c", "d", "e" ].send(@method, 10..12).should == nil
+ end
+
+ it "returns an empty array when m == n with [m...n]" do
+ [1, 2, 3, 4, 5].send(@method, 1...1).should == []
+ end
+
+ it "returns an empty array with [0...0]" do
+ [1, 2, 3, 4, 5].send(@method, 0...0).should == []
+ end
+
+ it "returns a subarray where m, n negatives and m < n with [m..n]" do
+ [ "a", "b", "c", "d", "e" ].send(@method, -3..-2).should == ["c", "d"]
+ end
+
+ it "returns an array containing the first element with [0..0]" do
+ [1, 2, 3, 4, 5].send(@method, 0..0).should == [1]
+ end
+
+ it "returns the entire array with [0..-1]" do
+ [1, 2, 3, 4, 5].send(@method, 0..-1).should == [1, 2, 3, 4, 5]
+ end
+
+ it "returns all but the last element with [0...-1]" do
+ [1, 2, 3, 4, 5].send(@method, 0...-1).should == [1, 2, 3, 4]
+ end
+
+ it "returns [3] for [2..-1] out of [1, 2, 3]" do
+ [1,2,3].send(@method, 2..-1).should == [3]
+ end
+
+ it "returns an empty array when m > n and m, n are positive with [m..n]" do
+ [1, 2, 3, 4, 5].send(@method, 3..2).should == []
+ end
+
+ it "returns an empty array when m > n and m, n are negative with [m..n]" do
+ [1, 2, 3, 4, 5].send(@method, -2..-3).should == []
+ end
+
+ it "does not expand array when the indices are outside of the array bounds" do
+ a = [1, 2]
+ a.send(@method, 4).should == nil
+ a.should == [1, 2]
+ a.send(@method, 4, 0).should == nil
+ a.should == [1, 2]
+ a.send(@method, 6, 1).should == nil
+ a.should == [1, 2]
+ a.send(@method, 8...8).should == nil
+ a.should == [1, 2]
+ a.send(@method, 10..10).should == nil
+ a.should == [1, 2]
+ end
+
+ describe "with a subclass of Array" do
+ before :each do
+ ScratchPad.clear
+
+ @array = ArraySpecs::MyArray[1, 2, 3, 4, 5]
+ end
+
+ it "returns a subclass instance with [n, m]" do
+ @array.send(@method, 0, 2).should be_an_instance_of(ArraySpecs::MyArray)
+ end
+
+ it "returns a subclass instance with [-n, m]" do
+ @array.send(@method, -3, 2).should be_an_instance_of(ArraySpecs::MyArray)
+ end
+
+ it "returns a subclass instance with [n..m]" do
+ @array.send(@method, 1..3).should be_an_instance_of(ArraySpecs::MyArray)
+ end
+
+ it "returns a subclass instance with [n...m]" do
+ @array.send(@method, 1...3).should be_an_instance_of(ArraySpecs::MyArray)
+ end
+
+ it "returns a subclass instance with [-n..-m]" do
+ @array.send(@method, -3..-1).should be_an_instance_of(ArraySpecs::MyArray)
+ end
+
+ it "returns a subclass instance with [-n...-m]" do
+ @array.send(@method, -3...-1).should be_an_instance_of(ArraySpecs::MyArray)
+ end
+
+ it "returns an empty array when m == n with [m...n]" do
+ @array.send(@method, 1...1).should == []
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "returns an empty array with [0...0]" do
+ @array.send(@method, 0...0).should == []
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "returns an empty array when m > n and m, n are positive with [m..n]" do
+ @array.send(@method, 3..2).should == []
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "returns an empty array when m > n and m, n are negative with [m..n]" do
+ @array.send(@method, -2..-3).should == []
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "returns [] if index == array.size with [index, length]" do
+ @array.send(@method, 5, 2).should == []
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "returns [] if the index is valid but length is zero with [index, length]" do
+ @array.send(@method, 0, 0).should == []
+ @array.send(@method, 2, 0).should == []
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "does not call #initialize on the subclass instance" do
+ @array.send(@method, 0, 3).should == [1, 2, 3]
+ ScratchPad.recorded.should be_nil
+ end
+ end
+
+ it "raises a RangeError when the start index is out of range of Fixnum" do
+ array = [1, 2, 3, 4, 5, 6]
+ obj = mock('large value')
+ obj.should_receive(:to_int).and_return(0x8000_0000_0000_0000_0000)
+ lambda { array.send(@method, obj) }.should raise_error(RangeError)
+
+ obj = 8e19
+ lambda { array.send(@method, obj) }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError when the length is out of range of Fixnum" do
+ array = [1, 2, 3, 4, 5, 6]
+ obj = mock('large value')
+ obj.should_receive(:to_int).and_return(0x8000_0000_0000_0000_0000)
+ lambda { array.send(@method, 1, obj) }.should raise_error(RangeError)
+
+ obj = 8e19
+ lambda { array.send(@method, 1, obj) }.should raise_error(RangeError)
+ end
+end
diff --git a/spec/ruby/core/array/shared/unshift.rb b/spec/ruby/core/array/shared/unshift.rb
new file mode 100644
index 0000000000..d7464cdaca
--- /dev/null
+++ b/spec/ruby/core/array/shared/unshift.rb
@@ -0,0 +1,46 @@
+describe :array_unshift, shared: true do
+ it "prepends object to the original array" do
+ a = [1, 2, 3]
+ a.send(@method, "a").should equal(a)
+ a.should == ['a', 1, 2, 3]
+ a.send(@method).should equal(a)
+ a.should == ['a', 1, 2, 3]
+ a.send(@method, 5, 4, 3)
+ a.should == [5, 4, 3, 'a', 1, 2, 3]
+
+ # shift all but one element
+ a = [1, 2]
+ a.shift
+ a.send(@method, 3, 4)
+ a.should == [3, 4, 2]
+
+ # now shift all elements
+ a.shift
+ a.shift
+ a.shift
+ a.send(@method, 3, 4)
+ a.should == [3, 4]
+ end
+
+ it "quietly ignores unshifting nothing" do
+ [].send(@method).should == []
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.send(@method, :new).should == [:new, empty]
+
+ array = ArraySpecs.recursive_array
+ array.send(@method, :new)
+ array[0..5].should == [:new, 1, 'two', 3.0, array, array]
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array when the array is modified" do
+ lambda { ArraySpecs.frozen_array.send(@method, 1) }.should raise_error(frozen_error_class)
+ end
+
+ # see [ruby-core:23666]
+ it "raises a #{frozen_error_class} on a frozen array when the array would not be modified" do
+ lambda { ArraySpecs.frozen_array.send(@method) }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/array/shift_spec.rb b/spec/ruby/core/array/shift_spec.rb
new file mode 100644
index 0000000000..26bce8aeb3
--- /dev/null
+++ b/spec/ruby/core/array/shift_spec.rb
@@ -0,0 +1,134 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#shift" do
+ it "removes and returns the first element" do
+ a = [5, 1, 1, 5, 4]
+ a.shift.should == 5
+ a.should == [1, 1, 5, 4]
+ a.shift.should == 1
+ a.should == [1, 5, 4]
+ a.shift.should == 1
+ a.should == [5, 4]
+ a.shift.should == 5
+ a.should == [4]
+ a.shift.should == 4
+ a.should == []
+ end
+
+ it "returns nil when the array is empty" do
+ [].shift.should == nil
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.shift.should == []
+ empty.should == []
+
+ array = ArraySpecs.recursive_array
+ array.shift.should == 1
+ array[0..2].should == ['two', 3.0, array]
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { ArraySpecs.frozen_array.shift }.should raise_error(frozen_error_class)
+ end
+ it "raises a #{frozen_error_class} on an empty frozen array" do
+ lambda { ArraySpecs.empty_frozen_array.shift }.should raise_error(frozen_error_class)
+ end
+
+ describe "passed a number n as an argument" do
+ it "removes and returns an array with the first n element of the array" do
+ a = [1, 2, 3, 4, 5, 6]
+
+ a.shift(0).should == []
+ a.should == [1, 2, 3, 4, 5, 6]
+
+ a.shift(1).should == [1]
+ a.should == [2, 3, 4, 5, 6]
+
+ a.shift(2).should == [2, 3]
+ a.should == [4, 5, 6]
+
+ a.shift(3).should == [4, 5, 6]
+ a.should == []
+ end
+
+ it "does not corrupt the array when shift without arguments is followed by shift with an argument" do
+ a = [1, 2, 3, 4, 5]
+
+ a.shift.should == 1
+ a.shift(3).should == [2, 3, 4]
+ a.should == [5]
+ end
+
+ it "returns a new empty array if there are no more elements" do
+ a = []
+ popped1 = a.shift(1)
+ popped1.should == []
+ a.should == []
+
+ popped2 = a.shift(2)
+ popped2.should == []
+ a.should == []
+
+ popped1.should_not equal(popped2)
+ end
+
+ it "returns whole elements if n exceeds size of the array" do
+ a = [1, 2, 3, 4, 5]
+ a.shift(6).should == [1, 2, 3, 4, 5]
+ a.should == []
+ end
+
+ it "does not return self even when it returns whole elements" do
+ a = [1, 2, 3, 4, 5]
+ a.shift(5).should_not equal(a)
+
+ a = [1, 2, 3, 4, 5]
+ a.shift(6).should_not equal(a)
+ end
+
+ it "raises an ArgumentError if n is negative" do
+ lambda{ [1, 2, 3].shift(-1) }.should raise_error(ArgumentError)
+ end
+
+ it "tries to convert n to an Integer using #to_int" do
+ a = [1, 2, 3, 4]
+ a.shift(2.3).should == [1, 2]
+
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(2)
+ a.should == [3, 4]
+ a.shift(obj).should == [3, 4]
+ a.should == []
+ end
+
+ it "raises a TypeError when the passed n cannot be coerced to Integer" do
+ lambda{ [1, 2].shift("cat") }.should raise_error(TypeError)
+ lambda{ [1, 2].shift(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError if more arguments are passed" do
+ lambda{ [1, 2].shift(1, 2) }.should raise_error(ArgumentError)
+ end
+
+ it "does not return subclass instances with Array subclass" do
+ ArraySpecs::MyArray[1, 2, 3].shift(2).should be_an_instance_of(Array)
+ end
+
+ it "returns an untainted array even if the array is tainted" do
+ ary = [1, 2].taint
+ ary.shift(2).tainted?.should be_false
+ ary.shift(0).tainted?.should be_false
+ end
+
+ it "keeps taint status" do
+ a = [1, 2].taint
+ a.shift(2)
+ a.tainted?.should be_true
+ a.shift(2)
+ a.tainted?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/core/array/shuffle_spec.rb b/spec/ruby/core/array/shuffle_spec.rb
new file mode 100644
index 0000000000..4f793acf19
--- /dev/null
+++ b/spec/ruby/core/array/shuffle_spec.rb
@@ -0,0 +1,102 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#shuffle" do
+ it "returns the same values, in a usually different order" do
+ a = [1, 2, 3, 4]
+ different = false
+ 10.times do
+ s = a.shuffle
+ s.sort.should == a
+ different ||= (a != s)
+ end
+ different.should be_true # Will fail once in a blue moon (4!^10)
+ end
+
+ it "is not destructive" do
+ a = [1, 2, 3]
+ 10.times do
+ a.shuffle
+ a.should == [1, 2, 3]
+ end
+ end
+
+ it "does not return subclass instances with Array subclass" do
+ ArraySpecs::MyArray[1, 2, 3].shuffle.should be_an_instance_of(Array)
+ end
+
+ it "attempts coercion via #to_hash" do
+ obj = mock('hash')
+ obj.should_receive(:to_hash).once.and_return({})
+ [2, 3].shuffle(obj)
+ end
+
+ it "calls #rand on the Object passed by the :random key in the arguments Hash" do
+ obj = mock("array_shuffle_random")
+ obj.should_receive(:rand).at_least(1).times.and_return(0.5)
+
+ result = [1, 2].shuffle(random: obj)
+ result.size.should == 2
+ result.should include(1, 2)
+ end
+
+ it "raises a NoMethodError if an object passed for the RNG does not define #rand" do
+ obj = BasicObject.new
+
+ lambda { [1, 2].shuffle(random: obj) }.should raise_error(NoMethodError)
+ end
+
+ it "accepts a Float for the value returned by #rand" do
+ random = mock("array_shuffle_random")
+ random.should_receive(:rand).at_least(1).times.and_return(0.3)
+
+ [1, 2].shuffle(random: random).should be_an_instance_of(Array)
+ end
+
+ it "calls #to_int on the Object returned by #rand" do
+ value = mock("array_shuffle_random_value")
+ value.should_receive(:to_int).at_least(1).times.and_return(0)
+ random = mock("array_shuffle_random")
+ random.should_receive(:rand).at_least(1).times.and_return(value)
+
+ [1, 2].shuffle(random: random).should be_an_instance_of(Array)
+ end
+
+ it "raises a RangeError if the value is less than zero" do
+ value = mock("array_shuffle_random_value")
+ value.should_receive(:to_int).and_return(-1)
+ random = mock("array_shuffle_random")
+ random.should_receive(:rand).and_return(value)
+
+ lambda { [1, 2].shuffle(random: random) }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError if the value is equal to one" do
+ value = mock("array_shuffle_random_value")
+ value.should_receive(:to_int).at_least(1).times.and_return(1)
+ random = mock("array_shuffle_random")
+ random.should_receive(:rand).at_least(1).times.and_return(value)
+
+ lambda { [1, 2].shuffle(random: random) }.should raise_error(RangeError)
+ end
+end
+
+describe "Array#shuffle!" do
+ it "returns the same values, in a usually different order" do
+ a = [1, 2, 3, 4]
+ original = a
+ different = false
+ 10.times do
+ a = a.shuffle!
+ a.sort.should == [1, 2, 3, 4]
+ different ||= (a != [1, 2, 3, 4])
+ end
+ different.should be_true # Will fail once in a blue moon (4!^10)
+ a.should equal(original)
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { ArraySpecs.frozen_array.shuffle! }.should raise_error(frozen_error_class)
+ lambda { ArraySpecs.empty_frozen_array.shuffle! }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/array/size_spec.rb b/spec/ruby/core/array/size_spec.rb
new file mode 100644
index 0000000000..d68f956a83
--- /dev/null
+++ b/spec/ruby/core/array/size_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/length'
+
+describe "Array#size" do
+ it_behaves_like :array_length, :size
+end
diff --git a/spec/ruby/core/array/slice_spec.rb b/spec/ruby/core/array/slice_spec.rb
new file mode 100644
index 0000000000..cd16b3892c
--- /dev/null
+++ b/spec/ruby/core/array/slice_spec.rb
@@ -0,0 +1,160 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/slice'
+
+describe "Array#slice!" do
+ it "removes and return the element at index" do
+ a = [1, 2, 3, 4]
+ a.slice!(10).should == nil
+ a.should == [1, 2, 3, 4]
+ a.slice!(-10).should == nil
+ a.should == [1, 2, 3, 4]
+ a.slice!(2).should == 3
+ a.should == [1, 2, 4]
+ a.slice!(-1).should == 4
+ a.should == [1, 2]
+ a.slice!(1).should == 2
+ a.should == [1]
+ a.slice!(-1).should == 1
+ a.should == []
+ a.slice!(-1).should == nil
+ a.should == []
+ a.slice!(0).should == nil
+ a.should == []
+ end
+
+ it "removes and returns length elements beginning at start" do
+ a = [1, 2, 3, 4, 5, 6]
+ a.slice!(2, 3).should == [3, 4, 5]
+ a.should == [1, 2, 6]
+ a.slice!(1, 1).should == [2]
+ a.should == [1, 6]
+ a.slice!(1, 0).should == []
+ a.should == [1, 6]
+ a.slice!(2, 0).should == []
+ a.should == [1, 6]
+ a.slice!(0, 4).should == [1, 6]
+ a.should == []
+ a.slice!(0, 4).should == []
+ a.should == []
+
+ a = [1]
+ a.slice!(0, 1).should == [1]
+ a.should == []
+ a[-1].should == nil
+
+ a = [1, 2, 3]
+ a.slice!(0,1).should == [1]
+ a.should == [2, 3]
+ end
+
+ it "returns nil if length is negative" do
+ a = [1, 2, 3]
+ a.slice!(2, -1).should == nil
+ a.should == [1, 2, 3]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.slice(0).should == empty
+
+ array = ArraySpecs.recursive_array
+ array.slice(4).should == array
+ array.slice(0..3).should == [1, 'two', 3.0, array]
+ end
+
+ it "calls to_int on start and length arguments" do
+ obj = mock('2')
+ def obj.to_int() 2 end
+
+ a = [1, 2, 3, 4, 5]
+ a.slice!(obj).should == 3
+ a.should == [1, 2, 4, 5]
+ a.slice!(obj, obj).should == [4, 5]
+ a.should == [1, 2]
+ a.slice!(0, obj).should == [1, 2]
+ a.should == []
+ end
+
+ it "removes and return elements in range" do
+ a = [1, 2, 3, 4, 5, 6, 7, 8]
+ a.slice!(1..4).should == [2, 3, 4, 5]
+ a.should == [1, 6, 7, 8]
+ a.slice!(1...3).should == [6, 7]
+ a.should == [1, 8]
+ a.slice!(-1..-1).should == [8]
+ a.should == [1]
+ a.slice!(0...0).should == []
+ a.should == [1]
+ a.slice!(0..0).should == [1]
+ a.should == []
+
+ a = [1,2,3]
+ a.slice!(0..3).should == [1,2,3]
+ a.should == []
+ end
+
+ it "removes and returns elements in end-exclusive ranges" do
+ a = [1, 2, 3, 4, 5, 6, 7, 8]
+ a.slice!(4...a.length).should == [5, 6, 7, 8]
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "calls to_int on range arguments" do
+ from = mock('from')
+ to = mock('to')
+
+ # So we can construct a range out of them...
+ def from.<=>(o) 0 end
+ def to.<=>(o) 0 end
+
+ def from.to_int() 1 end
+ def to.to_int() -2 end
+
+ a = [1, 2, 3, 4, 5]
+
+ a.slice!(from .. to).should == [2, 3, 4]
+ a.should == [1, 5]
+
+ lambda { a.slice!("a" .. "b") }.should raise_error(TypeError)
+ lambda { a.slice!(from .. "b") }.should raise_error(TypeError)
+ end
+
+ it "returns last element for consecutive calls at zero index" do
+ a = [ 1, 2, 3 ]
+ a.slice!(0).should == 1
+ a.slice!(0).should == 2
+ a.slice!(0).should == 3
+ a.should == []
+ end
+
+ it "does not expand array with indices out of bounds" do
+ a = [1, 2]
+ a.slice!(4).should == nil
+ a.should == [1, 2]
+ a.slice!(4, 0).should == nil
+ a.should == [1, 2]
+ a.slice!(6, 1).should == nil
+ a.should == [1, 2]
+ a.slice!(8...8).should == nil
+ a.should == [1, 2]
+ a.slice!(10..10).should == nil
+ a.should == [1, 2]
+ end
+
+ it "does not expand array with negative indices out of bounds" do
+ a = [1, 2]
+ a.slice!(-3, 1).should == nil
+ a.should == [1, 2]
+ a.slice!(-3..2).should == nil
+ a.should == [1, 2]
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { ArraySpecs.frozen_array.slice!(0, 0) }.should raise_error(frozen_error_class)
+ end
+end
+
+describe "Array#slice" do
+ it_behaves_like :array_slice, :slice
+end
diff --git a/spec/ruby/core/array/sort_by_spec.rb b/spec/ruby/core/array/sort_by_spec.rb
new file mode 100644
index 0000000000..6428194dfb
--- /dev/null
+++ b/spec/ruby/core/array/sort_by_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Array#sort_by!" do
+ it "sorts array in place by passing each element to the given block" do
+ a = [-100, -2, 1, 200, 30000]
+ a.sort_by!{ |e| e.to_s.size }
+ a.should == [1, -2, 200, -100, 30000]
+ end
+
+ it "returns an Enumerator if not given a block" do
+ (1..10).to_a.sort_by!.should be_an_instance_of(Enumerator)
+ end
+
+ it "completes when supplied a block that always returns the same result" do
+ a = [2, 3, 5, 1, 4]
+ a.sort_by!{ 1 }
+ a.should be_an_instance_of(Array)
+ a.sort_by!{ 0 }
+ a.should be_an_instance_of(Array)
+ a.sort_by!{ -1 }
+ a.should be_an_instance_of(Array)
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { ArraySpecs.frozen_array.sort_by! {}}.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} on an empty frozen array" do
+ lambda { ArraySpecs.empty_frozen_array.sort_by! {}}.should raise_error(frozen_error_class)
+ end
+
+ it "returns the specified value when it would break in the given block" do
+ [1, 2, 3].sort_by!{ break :a }.should == :a
+ end
+
+ it "makes some modification even if finished sorting when it would break in the given block" do
+ partially_sorted = (1..5).map{|i|
+ ary = [5, 4, 3, 2, 1]
+ ary.sort_by!{|x,y| break if x==i; x<=>y}
+ ary
+ }
+ partially_sorted.any?{|ary| ary != [1, 2, 3, 4, 5]}.should be_true
+ end
+
+ it "changes nothing when called on a single element array" do
+ [1].sort_by!(&:to_s).should == [1]
+ end
+
+ it_behaves_like :enumeratorized_with_origin_size, :sort_by!, [1,2,3]
+end
diff --git a/spec/ruby/core/array/sort_spec.rb b/spec/ruby/core/array/sort_spec.rb
new file mode 100644
index 0000000000..c0d6628549
--- /dev/null
+++ b/spec/ruby/core/array/sort_spec.rb
@@ -0,0 +1,252 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#sort" do
+ it "returns a new array sorted based on comparing elements with <=>" do
+ a = [1, -2, 3, 9, 1, 5, -5, 1000, -5, 2, -10, 14, 6, 23, 0]
+ a.sort.should == [-10, -5, -5, -2, 0, 1, 1, 2, 3, 5, 6, 9, 14, 23, 1000]
+ end
+
+ it "does not affect the original Array" do
+ a = [3, 1, 2]
+ a.sort.should == [1, 2, 3]
+ a.should == [3, 1, 2]
+
+ a = [0, 15, 2, 3, 4, 6, 14, 5, 7, 12, 8, 9, 1, 10, 11, 13]
+ b = a.sort
+ a.should == [0, 15, 2, 3, 4, 6, 14, 5, 7, 12, 8, 9, 1, 10, 11, 13]
+ b.should == (0..15).to_a
+ end
+
+ it "sorts already-sorted Arrays" do
+ (0..15).to_a.sort.should == (0..15).to_a
+ end
+
+ it "sorts reverse-sorted Arrays" do
+ (0..15).to_a.reverse.sort.should == (0..15).to_a
+ end
+
+ it "sorts Arrays that consist entirely of equal elements" do
+ a = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
+ a.sort.should == a
+ b = Array.new(15).map { ArraySpecs::SortSame.new }
+ b.sort.should == b
+ end
+
+ it "sorts Arrays that consist mostly of equal elements" do
+ a = [1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1]
+ a.sort.should == [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
+ end
+
+ it "does not return self even if the array would be already sorted" do
+ a = [1, 2, 3]
+ sorted = a.sort
+ sorted.should == a
+ sorted.should_not equal(a)
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.sort.should == empty
+
+ array = [[]]; array << array
+ array.sort.should == [[], array]
+ end
+
+ it "uses #<=> of elements in order to sort" do
+ a = ArraySpecs::MockForCompared.new
+ b = ArraySpecs::MockForCompared.new
+ c = ArraySpecs::MockForCompared.new
+
+ ArraySpecs::MockForCompared.compared?.should == false
+ [a, b, c].sort.should == [c, b, a]
+ ArraySpecs::MockForCompared.compared?.should == true
+ end
+
+ it "does not deal with exceptions raised by unimplemented or incorrect #<=>" do
+ o = Object.new
+
+ lambda {
+ [o, 1].sort
+ }.should raise_error(ArgumentError)
+ end
+
+ it "may take a block which is used to determine the order of objects a and b described as -1, 0 or +1" do
+ a = [5, 1, 4, 3, 2]
+ a.sort.should == [1, 2, 3, 4, 5]
+ a.sort {|x, y| y <=> x}.should == [5, 4, 3, 2, 1]
+ end
+
+ it "raises an error when a given block returns nil" do
+ lambda { [1, 2].sort {} }.should raise_error(ArgumentError)
+ end
+
+ it "does not call #<=> on contained objects when invoked with a block" do
+ a = Array.new(25)
+ (0...25).each {|i| a[i] = ArraySpecs::UFOSceptic.new }
+
+ a.sort { -1 }.should be_an_instance_of(Array)
+ end
+
+ it "does not call #<=> on elements when invoked with a block even if Array is large (Rubinius #412)" do
+ a = Array.new(1500)
+ (0...1500).each {|i| a[i] = ArraySpecs::UFOSceptic.new }
+
+ a.sort { -1 }.should be_an_instance_of(Array)
+ end
+
+ it "completes when supplied a block that always returns the same result" do
+ a = [2, 3, 5, 1, 4]
+ a.sort { 1 }.should be_an_instance_of(Array)
+ a.sort { 0 }.should be_an_instance_of(Array)
+ a.sort { -1 }.should be_an_instance_of(Array)
+ end
+
+ it "does not freezes self during being sorted" do
+ a = [1, 2, 3]
+ a.sort { |x,y| a.frozen?.should == false; x <=> y }
+ end
+
+ it "returns the specified value when it would break in the given block" do
+ [1, 2, 3].sort{ break :a }.should == :a
+ end
+
+ it "uses the sign of Bignum block results as the sort result" do
+ a = [1, 2, 5, 10, 7, -4, 12]
+ begin
+ class Bignum;
+ alias old_spaceship <=>
+ def <=>(other)
+ raise
+ end
+ end
+ a.sort {|n, m| (n - m) * (2 ** 200)}.should == [-4, 1, 2, 5, 7, 10, 12]
+ ensure
+ class Bignum
+ alias <=> old_spaceship
+ end
+ end
+ end
+
+ it "compares values returned by block with 0" do
+ a = [1, 2, 5, 10, 7, -4, 12]
+ a.sort { |n, m| n - m }.should == [-4, 1, 2, 5, 7, 10, 12]
+ a.sort { |n, m|
+ ArraySpecs::ComparableWithFixnum.new(n-m)
+ }.should == [-4, 1, 2, 5, 7, 10, 12]
+ lambda {
+ a.sort { |n, m| (n - m).to_s }
+ }.should raise_error(ArgumentError)
+ end
+
+ it "sorts an array that has a value shifted off without a block" do
+ a = Array.new(20, 1)
+ a.shift
+ a[0] = 2
+ a.sort.last.should == 2
+ end
+
+ it "sorts an array that has a value shifted off with a block" do
+ a = Array.new(20, 1)
+ a.shift
+ a[0] = 2
+ a.sort {|x, y| x <=> y }.last.should == 2
+ end
+
+ it "raises an error if objects can't be compared" do
+ a=[ArraySpecs::Uncomparable.new, ArraySpecs::Uncomparable.new]
+ lambda {a.sort}.should raise_error(ArgumentError)
+ end
+
+ # From a strange Rubinius bug
+ it "handles a large array that has been pruned" do
+ pruned = ArraySpecs::LargeArray.dup.delete_if { |n| n !~ /^test./ }
+ pruned.sort.should == ArraySpecs::LargeTestArraySorted
+ end
+
+ it "does not return subclass instance on Array subclasses" do
+ ary = ArraySpecs::MyArray[1, 2, 3]
+ ary.sort.should be_an_instance_of(Array)
+ end
+end
+
+describe "Array#sort!" do
+ it "sorts array in place using <=>" do
+ a = [1, -2, 3, 9, 1, 5, -5, 1000, -5, 2, -10, 14, 6, 23, 0]
+ a.sort!
+ a.should == [-10, -5, -5, -2, 0, 1, 1, 2, 3, 5, 6, 9, 14, 23, 1000]
+ end
+
+ it "sorts array in place using block value if a block given" do
+ a = [0, 15, 2, 3, 4, 6, 14, 5, 7, 12, 8, 9, 1, 10, 11, 13]
+ a.sort! { |x, y| y <=> x }.should == (0..15).to_a.reverse
+ end
+
+ it "returns self if the order of elements changed" do
+ a = [6, 7, 2, 3, 7]
+ a.sort!.should equal(a)
+ a.should == [2, 3, 6, 7, 7]
+ end
+
+ it "returns self even if makes no modification" do
+ a = [1, 2, 3, 4, 5]
+ a.sort!.should equal(a)
+ a.should == [1, 2, 3, 4, 5]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.sort!.should == empty
+
+ array = [[]]; array << array
+ array.sort!.should == array
+ end
+
+ it "uses #<=> of elements in order to sort" do
+ a = ArraySpecs::MockForCompared.new
+ b = ArraySpecs::MockForCompared.new
+ c = ArraySpecs::MockForCompared.new
+
+ ArraySpecs::MockForCompared.compared?.should == false
+ [a, b, c].sort!.should == [c, b, a]
+ ArraySpecs::MockForCompared.compared?.should == true
+ end
+
+ it "does not call #<=> on contained objects when invoked with a block" do
+ a = Array.new(25)
+ (0...25).each {|i| a[i] = ArraySpecs::UFOSceptic.new }
+
+ a.sort! { -1 }.should be_an_instance_of(Array)
+ end
+
+ it "does not call #<=> on elements when invoked with a block even if Array is large (Rubinius #412)" do
+ a = Array.new(1500)
+ (0...1500).each {|i| a[i] = ArraySpecs::UFOSceptic.new }
+
+ a.sort! { -1 }.should be_an_instance_of(Array)
+ end
+
+ it "completes when supplied a block that always returns the same result" do
+ a = [2, 3, 5, 1, 4]
+ a.sort!{ 1 }.should be_an_instance_of(Array)
+ a.sort!{ 0 }.should be_an_instance_of(Array)
+ a.sort!{ -1 }.should be_an_instance_of(Array)
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array" do
+ lambda { ArraySpecs.frozen_array.sort! }.should raise_error(frozen_error_class)
+ end
+
+ it "returns the specified value when it would break in the given block" do
+ [1, 2, 3].sort{ break :a }.should == :a
+ end
+
+ it "makes some modification even if finished sorting when it would break in the given block" do
+ partially_sorted = (1..5).map{|i|
+ ary = [5, 4, 3, 2, 1]
+ ary.sort!{|x,y| break if x==i; x<=>y}
+ ary
+ }
+ partially_sorted.any?{|ary| ary != [1, 2, 3, 4, 5]}.should be_true
+ end
+end
diff --git a/spec/ruby/core/array/sum_spec.rb b/spec/ruby/core/array/sum_spec.rb
new file mode 100644
index 0000000000..71b8b9dc78
--- /dev/null
+++ b/spec/ruby/core/array/sum_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '2.4' do
+ describe "Array#sum" do
+ it "returns the sum of elements" do
+ [1, 2, 3].sum.should == 6
+ end
+
+ it "applies a block to each element before adding if it's given" do
+ [1, 2, 3].sum { |i| i * 10 }.should == 60
+ end
+
+ it "returns init value if array is empty" do
+ [].sum(-1).should == -1
+ end
+
+ it "returns 0 if array is empty and init is omitted" do
+ [].sum.should == 0
+ end
+
+ it "adds init value to the sum of elemens" do
+ [1, 2, 3].sum(10).should == 16
+ end
+
+ it "can be used for non-numeric objects by providing init value" do
+ ["a", "b", "c"].sum("").should == "abc"
+ end
+
+ it 'raises TypeError if any element are not numeric' do
+ lambda { ["a"].sum }.should raise_error(TypeError)
+ end
+
+ it 'raises TypeError if any element cannot be added to init value' do
+ lambda { [1].sum([]) }.should raise_error(TypeError)
+ end
+
+ it "calls + to sum the elements" do
+ a = mock("a")
+ b = mock("b")
+ a.should_receive(:+).with(b).and_return(42)
+ [b].sum(a).should == 42
+ end
+ end
+end
diff --git a/spec/ruby/core/array/take_spec.rb b/spec/ruby/core/array/take_spec.rb
new file mode 100644
index 0000000000..2c9e3f5cfe
--- /dev/null
+++ b/spec/ruby/core/array/take_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+
+describe "Array#take" do
+ it "returns the first specified number of elements" do
+ [1, 2, 3].take(2).should == [1, 2]
+ end
+
+ it "returns all elements when the argument is greater than the Array size" do
+ [1, 2].take(99).should == [1, 2]
+ end
+
+ it "returns all elements when the argument is less than the Array size" do
+ [1, 2].take(4).should == [1, 2]
+ end
+
+ it "returns an empty Array when passed zero" do
+ [1].take(0).should == []
+ end
+
+ it "returns an empty Array when called on an empty Array" do
+ [].take(3).should == []
+ end
+
+ it "raises an ArgumentError when the argument is negative" do
+ lambda{ [1].take(-3) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/array/take_while_spec.rb b/spec/ruby/core/array/take_while_spec.rb
new file mode 100644
index 0000000000..f159e6f251
--- /dev/null
+++ b/spec/ruby/core/array/take_while_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "Array#take_while" do
+ it "returns all elements until the block returns false" do
+ [1, 2, 3].take_while{ |element| element < 3 }.should == [1, 2]
+ end
+
+ it "returns all elements until the block returns nil" do
+ [1, 2, nil, 4].take_while{ |element| element }.should == [1, 2]
+ end
+
+ it "returns all elements until the block returns false" do
+ [1, 2, false, 4].take_while{ |element| element }.should == [1, 2]
+ end
+end
diff --git a/spec/ruby/core/array/to_a_spec.rb b/spec/ruby/core/array/to_a_spec.rb
new file mode 100644
index 0000000000..49d0a4782e
--- /dev/null
+++ b/spec/ruby/core/array/to_a_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#to_a" do
+ it "returns self" do
+ a = [1, 2, 3]
+ a.to_a.should == [1, 2, 3]
+ a.should equal(a.to_a)
+ end
+
+ it "does not return subclass instance on Array subclasses" do
+ e = ArraySpecs::MyArray.new(1, 2)
+ e.to_a.should be_an_instance_of(Array)
+ e.to_a.should == [1, 2]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.to_a.should == empty
+
+ array = ArraySpecs.recursive_array
+ array.to_a.should == array
+ end
+end
diff --git a/spec/ruby/core/array/to_ary_spec.rb b/spec/ruby/core/array/to_ary_spec.rb
new file mode 100644
index 0000000000..314699b709
--- /dev/null
+++ b/spec/ruby/core/array/to_ary_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#to_ary" do
+ it "returns self" do
+ a = [1, 2, 3]
+ a.should equal(a.to_ary)
+ a = ArraySpecs::MyArray[1, 2, 3]
+ a.should equal(a.to_ary)
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.to_ary.should == empty
+
+ array = ArraySpecs.recursive_array
+ array.to_ary.should == array
+ end
+
+end
diff --git a/spec/ruby/core/array/to_h_spec.rb b/spec/ruby/core/array/to_h_spec.rb
new file mode 100644
index 0000000000..e845d2c950
--- /dev/null
+++ b/spec/ruby/core/array/to_h_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#to_h" do
+ it "converts empty array to empty hash" do
+ [].to_h.should == {}
+ end
+
+ it "converts [key, value] pairs to a hash" do
+ hash = [[:a, 1], [:b, 2]].to_h
+ hash.should == { a: 1, b: 2 }
+ end
+
+ it "uses the last value of a duplicated key" do
+ hash = [[:a, 1], [:b, 2], [:a, 3]].to_h
+ hash.should == { a: 3, b: 2 }
+ end
+
+ it "calls #to_ary on contents" do
+ pair = mock('to_ary')
+ pair.should_receive(:to_ary).and_return([:b, 2])
+ hash = [[:a, 1], pair].to_h
+ hash.should == { a: 1, b: 2 }
+ end
+
+ it "raises TypeError if an element is not an array" do
+ lambda { [:x].to_h }.should raise_error(TypeError)
+ end
+
+ it "raises ArgumentError if an element is not a [key, value] pair" do
+ lambda { [[:x]].to_h }.should raise_error(ArgumentError)
+ end
+
+ it "does not accept arguments" do
+ lambda { [].to_h(:a, :b) }.should raise_error(ArgumentError)
+ end
+
+ ruby_version_is "2.6" do
+ it "converts [key, value] pairs returned by the block to a hash" do
+ i = 0
+ [:a, :b].to_h {|k| [k, i += 1]}.should == { a: 1, b: 2 }
+ end
+ end
+end
diff --git a/spec/ruby/core/array/to_s_spec.rb b/spec/ruby/core/array/to_s_spec.rb
new file mode 100644
index 0000000000..e8476702ec
--- /dev/null
+++ b/spec/ruby/core/array/to_s_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/join'
+require_relative 'shared/inspect'
+
+describe "Array#to_s" do
+ it_behaves_like :array_inspect, :to_s
+end
diff --git a/spec/ruby/core/array/transpose_spec.rb b/spec/ruby/core/array/transpose_spec.rb
new file mode 100644
index 0000000000..c7bd7e7338
--- /dev/null
+++ b/spec/ruby/core/array/transpose_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#transpose" do
+ it "assumes an array of arrays and returns the result of transposing rows and columns" do
+ [[1, 'a'], [2, 'b'], [3, 'c']].transpose.should == [[1, 2, 3], ["a", "b", "c"]]
+ [[1, 2, 3], ["a", "b", "c"]].transpose.should == [[1, 'a'], [2, 'b'], [3, 'c']]
+ [].transpose.should == []
+ [[]].transpose.should == []
+ [[], []].transpose.should == []
+ [[0]].transpose.should == [[0]]
+ [[0], [1]].transpose.should == [[0, 1]]
+ end
+
+ it "tries to convert the passed argument to an Array using #to_ary" do
+ obj = mock('[1,2]')
+ obj.should_receive(:to_ary).and_return([1, 2])
+ [obj, [:a, :b]].transpose.should == [[1, :a], [2, :b]]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.transpose.should == empty
+
+ a = []; a << a
+ b = []; b << b
+ [a, b].transpose.should == [[a, b]]
+
+ a = [1]; a << a
+ b = [2]; b << b
+ [a, b].transpose.should == [ [1, 2], [a, b] ]
+ end
+
+ it "raises a TypeError if the passed Argument does not respond to #to_ary" do
+ lambda { [Object.new, [:a, :b]].transpose }.should raise_error(TypeError)
+ end
+
+ it "does not call to_ary on array subclass elements" do
+ ary = [ArraySpecs::ToAryArray[1, 2], ArraySpecs::ToAryArray[4, 6]]
+ ary.transpose.should == [[1, 4], [2, 6]]
+ end
+
+ it "raises an IndexError if the arrays are not of the same length" do
+ lambda { [[1, 2], [:a]].transpose }.should raise_error(IndexError)
+ end
+
+ it "does not return subclass instance on Array subclasses" do
+ result = ArraySpecs::MyArray[ArraySpecs::MyArray[1, 2, 3], ArraySpecs::MyArray[4, 5, 6]].transpose
+ result.should be_an_instance_of(Array)
+ result[0].should be_an_instance_of(Array)
+ result[1].should be_an_instance_of(Array)
+ end
+end
diff --git a/spec/ruby/core/array/try_convert_spec.rb b/spec/ruby/core/array/try_convert_spec.rb
new file mode 100644
index 0000000000..5f653b6807
--- /dev/null
+++ b/spec/ruby/core/array/try_convert_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array.try_convert" do
+ it "returns the argument if it's an Array" do
+ x = Array.new
+ Array.try_convert(x).should equal(x)
+ end
+
+ it "returns the argument if it's a kind of Array" do
+ x = ArraySpecs::MyArray[]
+ Array.try_convert(x).should equal(x)
+ end
+
+ it "returns nil when the argument does not respond to #to_ary" do
+ Array.try_convert(Object.new).should be_nil
+ end
+
+ it "sends #to_ary to the argument and returns the result if it's nil" do
+ obj = mock("to_ary")
+ obj.should_receive(:to_ary).and_return(nil)
+ Array.try_convert(obj).should be_nil
+ end
+
+ it "sends #to_ary to the argument and returns the result if it's an Array" do
+ x = Array.new
+ obj = mock("to_ary")
+ obj.should_receive(:to_ary).and_return(x)
+ Array.try_convert(obj).should equal(x)
+ end
+
+ it "sends #to_ary to the argument and returns the result if it's a kind of Array" do
+ x = ArraySpecs::MyArray[]
+ obj = mock("to_ary")
+ obj.should_receive(:to_ary).and_return(x)
+ Array.try_convert(obj).should equal(x)
+ end
+
+ it "sends #to_ary to the argument and raises TypeError if it's not a kind of Array" do
+ obj = mock("to_ary")
+ obj.should_receive(:to_ary).and_return(Object.new)
+ lambda { Array.try_convert obj }.should raise_error(TypeError)
+ end
+
+ it "does not rescue exceptions raised by #to_ary" do
+ obj = mock("to_ary")
+ obj.should_receive(:to_ary).and_raise(RuntimeError)
+ lambda { Array.try_convert obj }.should raise_error(RuntimeError)
+ end
+end
diff --git a/spec/ruby/core/array/union_spec.rb b/spec/ruby/core/array/union_spec.rb
new file mode 100644
index 0000000000..0e177715e5
--- /dev/null
+++ b/spec/ruby/core/array/union_spec.rb
@@ -0,0 +1,82 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#|" do
+ it "returns an array of elements that appear in either array (union)" do
+ ([] | []).should == []
+ ([1, 2] | []).should == [1, 2]
+ ([] | [1, 2]).should == [1, 2]
+ ([ 1, 2, 3, 4 ] | [ 3, 4, 5 ]).should == [1, 2, 3, 4, 5]
+ end
+
+ it "creates an array with no duplicates" do
+ ([ 1, 2, 3, 1, 4, 5 ] | [ 1, 3, 4, 5, 3, 6 ]).should == [1, 2, 3, 4, 5, 6]
+ end
+
+ it "creates an array with elements in order they are first encountered" do
+ ([ 1, 2, 3, 1 ] | [ 1, 3, 4, 5 ]).should == [1, 2, 3, 4, 5]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ (empty | empty).should == empty
+
+ array = ArraySpecs.recursive_array
+ (array | []).should == [1, 'two', 3.0, array]
+ ([] | array).should == [1, 'two', 3.0, array]
+ (array | array).should == [1, 'two', 3.0, array]
+ (array | empty).should == [1, 'two', 3.0, array, empty]
+ end
+
+ it "tries to convert the passed argument to an Array using #to_ary" do
+ obj = mock('[1,2,3]')
+ obj.should_receive(:to_ary).and_return([1, 2, 3])
+ ([0] | obj).should == ([0] | [1, 2, 3])
+ end
+
+ # MRI follows hashing semantics here, so doesn't actually call eql?/hash for Fixnum/Symbol
+ it "acts as if using an intermediate hash to collect values" do
+ not_supported_on :opal do
+ ([5.0, 4.0] | [5, 4]).should == [5.0, 4.0, 5, 4]
+ end
+
+ str = "x"
+ ([str] | [str.dup]).should == [str]
+
+ obj1 = mock('1')
+ obj2 = mock('2')
+ obj1.stub!(:hash).and_return(0)
+ obj2.stub!(:hash).and_return(0)
+ obj2.should_receive(:eql?).at_least(1).and_return(true)
+
+ ([obj1] | [obj2]).should == [obj1]
+ ([obj1, obj1, obj2, obj2] | [obj2]).should == [obj1]
+
+ obj1 = mock('3')
+ obj2 = mock('4')
+ obj1.stub!(:hash).and_return(0)
+ obj2.stub!(:hash).and_return(0)
+ obj2.should_receive(:eql?).at_least(1).and_return(false)
+
+ ([obj1] | [obj2]).should == [obj1, obj2]
+ ([obj1, obj1, obj2, obj2] | [obj2]).should == [obj1, obj2]
+ end
+
+ it "does not return subclass instances for Array subclasses" do
+ (ArraySpecs::MyArray[1, 2, 3] | []).should be_an_instance_of(Array)
+ (ArraySpecs::MyArray[1, 2, 3] | ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
+ ([] | ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
+ end
+
+ it "does not call to_ary on array subclasses" do
+ ([1, 2] | ArraySpecs::ToAryArray[5, 6]).should == [1, 2, 5, 6]
+ end
+
+ it "properly handles an identical item even when its #eql? isn't reflexive" do
+ x = mock('x')
+ x.stub!(:hash).and_return(42)
+ x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI.
+
+ ([x] | [x]).should == [x]
+ end
+end
diff --git a/spec/ruby/core/array/uniq_spec.rb b/spec/ruby/core/array/uniq_spec.rb
new file mode 100644
index 0000000000..471717b8e2
--- /dev/null
+++ b/spec/ruby/core/array/uniq_spec.rb
@@ -0,0 +1,221 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#uniq" do
+ it "returns an array with no duplicates" do
+ ["a", "a", "b", "b", "c"].uniq.should == ["a", "b", "c"]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.uniq.should == [empty]
+
+ array = ArraySpecs.recursive_array
+ array.uniq.should == [1, 'two', 3.0, array]
+ end
+
+ it "uses eql? semantics" do
+ [1.0, 1].uniq.should == [1.0, 1]
+ end
+
+ it "compares elements first with hash" do
+ x = mock('0')
+ x.should_receive(:hash).at_least(1).and_return(0)
+ y = mock('0')
+ y.should_receive(:hash).at_least(1).and_return(0)
+
+ [x, y].uniq.should == [x, y]
+ end
+
+ it "does not compare elements with different hash codes via eql?" do
+ x = mock('0')
+ x.should_not_receive(:eql?)
+ y = mock('1')
+ y.should_not_receive(:eql?)
+
+ x.should_receive(:hash).at_least(1).and_return(0)
+ y.should_receive(:hash).at_least(1).and_return(1)
+
+ [x, y].uniq.should == [x, y]
+ end
+
+ it "compares elements with matching hash codes with #eql?" do
+ a = Array.new(2) do
+ obj = mock('0')
+ obj.should_receive(:hash).at_least(1).and_return(0)
+
+ def obj.eql?(o)
+ # It's undefined whether the impl does a[0].eql?(a[1]) or
+ # a[1].eql?(a[0]) so we taint both.
+ taint
+ o.taint
+ false
+ end
+
+ obj
+ end
+
+ a.uniq.should == a
+ a[0].tainted?.should == true
+ a[1].tainted?.should == true
+
+ a = Array.new(2) do
+ obj = mock('0')
+ obj.should_receive(:hash).at_least(1).and_return(0)
+
+ def obj.eql?(o)
+ # It's undefined whether the impl does a[0].eql?(a[1]) or
+ # a[1].eql?(a[0]) so we taint both.
+ taint
+ o.taint
+ true
+ end
+
+ obj
+ end
+
+ a.uniq.size.should == 1
+ a[0].tainted?.should == true
+ a[1].tainted?.should == true
+ end
+
+ it "compares elements based on the value returned from the block" do
+ a = [1, 2, 3, 4]
+ a.uniq { |x| x >= 2 ? 1 : 0 }.should == [1, 2]
+ end
+
+ it "yields items in order" do
+ a = [1, 2, 3]
+ yielded = []
+ a.uniq { |v| yielded << v }
+ yielded.should == a
+ end
+
+ it "handles nil and false like any other values" do
+ [nil, false, 42].uniq { :foo }.should == [nil]
+ [false, nil, 42].uniq { :bar }.should == [false]
+ end
+
+ it "returns subclass instance on Array subclasses" do
+ ArraySpecs::MyArray[1, 2, 3].uniq.should be_an_instance_of(ArraySpecs::MyArray)
+ end
+
+ it "properly handles an identical item even when its #eql? isn't reflexive" do
+ x = mock('x')
+ x.should_receive(:hash).at_least(1).and_return(42)
+ x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI.
+
+ [x, x].uniq.should == [x]
+ end
+
+ describe "given an array of BasicObject subclasses that define ==, eql?, and hash" do
+ # jruby/jruby#3227
+ it "filters equivalent elements using those definitions" do
+
+ basic = Class.new(BasicObject) do
+ attr_reader :x
+
+ def initialize(x)
+ @x = x
+ end
+
+ def ==(rhs)
+ @x == rhs.x
+ end
+ alias_method :eql?, :==
+
+ def hash
+ @x.hash
+ end
+ end
+
+ a = [basic.new(3), basic.new(2), basic.new(1), basic.new(4), basic.new(1), basic.new(2), basic.new(3)]
+ a.uniq.should == [basic.new(3), basic.new(2), basic.new(1), basic.new(4)]
+ end
+ end
+end
+
+describe "Array#uniq!" do
+ it "modifies the array in place" do
+ a = [ "a", "a", "b", "b", "c" ]
+ a.uniq!
+ a.should == ["a", "b", "c"]
+ end
+
+ it "returns self" do
+ a = [ "a", "a", "b", "b", "c" ]
+ a.should equal(a.uniq!)
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty_dup = empty.dup
+ empty.uniq!
+ empty.should == empty_dup
+
+ array = ArraySpecs.recursive_array
+ expected = array[0..3]
+ array.uniq!
+ array.should == expected
+ end
+
+ it "compares elements first with hash" do
+ x = mock('0')
+ x.should_receive(:hash).at_least(1).and_return(0)
+ y = mock('0')
+ y.should_receive(:hash).at_least(1).and_return(0)
+
+ a = [x, y]
+ a.uniq!
+ a.should == [x, y]
+ end
+
+ it "does not compare elements with different hash codes via eql?" do
+ x = mock('0')
+ x.should_not_receive(:eql?)
+ y = mock('1')
+ y.should_not_receive(:eql?)
+
+ x.should_receive(:hash).at_least(1).and_return(0)
+ y.should_receive(:hash).at_least(1).and_return(1)
+
+ a = [x, y]
+ a.uniq!
+ a.should == [x, y]
+ end
+
+ it "returns nil if no changes are made to the array" do
+ [ "a", "b", "c" ].uniq!.should == nil
+ end
+
+ it "raises a #{frozen_error_class} on a frozen array when the array is modified" do
+ dup_ary = [1, 1, 2]
+ dup_ary.freeze
+ lambda { dup_ary.uniq! }.should raise_error(frozen_error_class)
+ end
+
+ # see [ruby-core:23666]
+ it "raises a #{frozen_error_class} on a frozen array when the array would not be modified" do
+ lambda { ArraySpecs.frozen_array.uniq!}.should raise_error(frozen_error_class)
+ lambda { ArraySpecs.empty_frozen_array.uniq!}.should raise_error(frozen_error_class)
+ end
+
+ it "doesn't yield to the block on a frozen array" do
+ lambda { ArraySpecs.frozen_array.uniq!{ raise RangeError, "shouldn't yield"}}.should raise_error(frozen_error_class)
+ end
+
+ it "compares elements based on the value returned from the block" do
+ a = [1, 2, 3, 4]
+ a.uniq! { |x| x >= 2 ? 1 : 0 }.should == [1, 2]
+ end
+
+ it "properly handles an identical item even when its #eql? isn't reflexive" do
+ x = mock('x')
+ x.should_receive(:hash).at_least(1).and_return(42)
+ x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI.
+
+ a = [x, x]
+ a.uniq!
+ a.should == [x]
+ end
+end
diff --git a/spec/ruby/core/array/unshift_spec.rb b/spec/ruby/core/array/unshift_spec.rb
new file mode 100644
index 0000000000..b8b675e5f8
--- /dev/null
+++ b/spec/ruby/core/array/unshift_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/unshift'
+
+describe "Array#unshift" do
+ it_behaves_like :array_unshift, :unshift
+end
diff --git a/spec/ruby/core/array/values_at_spec.rb b/spec/ruby/core/array/values_at_spec.rb
new file mode 100644
index 0000000000..13860150bb
--- /dev/null
+++ b/spec/ruby/core/array/values_at_spec.rb
@@ -0,0 +1,63 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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 == []
+ [1, 2, 3, 4, 5].values_at(1, 0, 5, -1, -8, 10).should == [2, 1, nil, 5, nil, nil]
+ end
+
+ it "calls to_int on its indices" do
+ obj = mock('1')
+ def obj.to_int() 1 end
+ [1, 2].values_at(obj, obj, obj).should == [2, 2, 2]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.values_at(0, 1, 2).should == [empty, nil, nil]
+
+ array = ArraySpecs.recursive_array
+ array.values_at(0, 1, 2, 3).should == [1, 'two', 3.0, array]
+ end
+
+ describe "when passed ranges" do
+ it "returns an array of elements in the ranges" do
+ [1, 2, 3, 4, 5].values_at(0..2, 1...3, 2..-2).should == [1, 2, 3, 2, 3, 3, 4]
+ [1, 2, 3, 4, 5].values_at(6..4).should == []
+ end
+
+ it "calls to_int on arguments of ranges" do
+ from = mock('from')
+ to = mock('to')
+
+ # So we can construct a range out of them...
+ def from.<=>(o) 0 end
+ def to.<=>(o) 0 end
+
+ def from.to_int() 1 end
+ def to.to_int() -2 end
+
+ ary = [1, 2, 3, 4, 5]
+ ary.values_at(from .. to, from ... to, to .. from).should == [2, 3, 4, 2, 3]
+ end
+ end
+
+ describe "when passed a range" do
+ it "fills with nil if the index is out of the range" do
+ [0, 1].values_at(0..3).should == [0, 1, nil, nil]
+ [0, 1].values_at(2..4).should == [nil, nil, nil]
+ end
+
+ describe "on an empty array" do
+ it "fills with nils if the index is out of the range" do
+ [].values_at(0..2).should == [nil, nil, nil]
+ [].values_at(1..3).should == [nil, nil, nil]
+ end
+ end
+ end
+
+ it "does not return subclass instance on Array subclasses" do
+ ArraySpecs::MyArray[1, 2, 3].values_at(0, 1..2, 1).should be_an_instance_of(Array)
+ end
+end
diff --git a/spec/ruby/core/array/zip_spec.rb b/spec/ruby/core/array/zip_spec.rb
new file mode 100644
index 0000000000..f0de864ed4
--- /dev/null
+++ b/spec/ruby/core/array/zip_spec.rb
@@ -0,0 +1,65 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#zip" do
+ it "returns an array of arrays containing corresponding elements of each array" do
+ [1, 2, 3, 4].zip(["a", "b", "c", "d", "e"]).should ==
+ [[1, "a"], [2, "b"], [3, "c"], [4, "d"]]
+ end
+
+ it "fills in missing values with nil" do
+ [1, 2, 3, 4, 5].zip(["a", "b", "c", "d"]).should ==
+ [[1, "a"], [2, "b"], [3, "c"], [4, "d"], [5, nil]]
+ end
+
+ it "properly handles recursive arrays" do
+ a = []; a << a
+ b = [1]; b << b
+
+ a.zip(a).should == [ [a[0], a[0]] ]
+ a.zip(b).should == [ [a[0], b[0]] ]
+ b.zip(a).should == [ [b[0], a[0]], [b[1], a[1]] ]
+ b.zip(b).should == [ [b[0], b[0]], [b[1], b[1]] ]
+ end
+
+ it "calls #to_ary to convert the argument to an Array" do
+ obj = mock('[3,4]')
+ obj.should_receive(:to_ary).and_return([3, 4])
+ [1, 2].zip(obj).should == [[1, 3], [2, 4]]
+ end
+
+ it "uses #each to extract arguments' elements when #to_ary fails" do
+ obj = Class.new do
+ def each(&b)
+ [3,4].each(&b)
+ end
+ end.new
+
+ [1, 2].zip(obj).should == [[1, 3], [2, 4]]
+ end
+
+ it "stops at own size when given an infinite enumerator" do
+ [1, 2].zip(10.upto(Float::INFINITY)).should == [[1, 10], [2, 11]]
+ end
+
+ it "fills nil when the given enumereator is shorter than self" do
+ obj = Object.new
+ def obj.each
+ yield 10
+ end
+ [1, 2].zip(obj).should == [[1, 10], [2, nil]]
+ end
+
+ it "calls block if supplied" do
+ values = []
+ [1, 2, 3, 4].zip(["a", "b", "c", "d", "e"]) { |value|
+ values << value
+ }.should == nil
+
+ values.should == [[1, "a"], [2, "b"], [3, "c"], [4, "d"]]
+ end
+
+ 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
+end
diff --git a/spec/ruby/core/basicobject/__id__spec.rb b/spec/ruby/core/basicobject/__id__spec.rb
new file mode 100644
index 0000000000..6766db4e82
--- /dev/null
+++ b/spec/ruby/core/basicobject/__id__spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/kernel/object_id'
+
+describe "BasicObject#__id__" do
+ it_behaves_like :object_id, :__id__, BasicObject
+end
diff --git a/spec/ruby/core/basicobject/__send___spec.rb b/spec/ruby/core/basicobject/__send___spec.rb
new file mode 100644
index 0000000000..005b1d0d90
--- /dev/null
+++ b/spec/ruby/core/basicobject/__send___spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/basicobject/send'
+
+describe "BasicObject#__send__" do
+ it "is a public instance method" do
+ BasicObject.should have_public_instance_method(:__send__)
+ end
+
+ it_behaves_like :basicobject_send, :__send__
+end
diff --git a/spec/ruby/core/basicobject/basicobject_spec.rb b/spec/ruby/core/basicobject/basicobject_spec.rb
new file mode 100644
index 0000000000..860ad93e89
--- /dev/null
+++ b/spec/ruby/core/basicobject/basicobject_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "BasicObject" do
+ it "raises NoMethodError for nonexistent methods after #method_missing is removed" do
+ script = fixture __FILE__, "remove_method_missing.rb"
+ ruby_exe(script).chomp.should == "NoMethodError"
+ end
+
+ it "raises NameError when referencing built-in constants" do
+ lambda { class BasicObjectSpecs::BOSubclass; Kernel; end }.should raise_error(NameError)
+ end
+
+ it "does not define built-in constants (according to const_defined?)" do
+ BasicObject.const_defined?(:Kernel).should be_false
+ end
+
+ it "does not define built-in constants (according to defined?)" do
+ BasicObjectSpecs::BOSubclass.kernel_defined?.should be_nil
+ end
+
+ it "includes itself in its list of constants" do
+ BasicObject.constants.should include(:BasicObject)
+ end
+end
+
+describe "BasicObject metaclass" do
+ before :each do
+ @meta = class << BasicObject; self; end
+ end
+
+ it "is an instance of Class" do
+ @meta.should be_an_instance_of(Class)
+ end
+
+ it "has Class as superclass" do
+ @meta.superclass.should equal(Class)
+ end
+
+ it "contains methods for the BasicObject class" do
+ @meta.class_eval do
+ def rubyspec_test_method() :test end
+ end
+
+ BasicObject.rubyspec_test_method.should == :test
+ end
+end
+
+describe "BasicObject instance metaclass" do
+ before :each do
+ @object = BasicObject.new
+ @meta = class << @object; self; end
+ end
+
+ it "is an instance of Class" do
+ @meta.should be_an_instance_of(Class)
+ end
+
+ it "has BasicObject as superclass" do
+ @meta.superclass.should equal(BasicObject)
+ end
+
+ it "contains methods defined for the BasicObject instance" do
+ @meta.class_eval do
+ def test_method() :test end
+ end
+
+ @object.test_method.should == :test
+ end
+end
+
+describe "BasicObject subclass" do
+ it "contains Kernel methods when including Kernel" do
+ obj = BasicObjectSpecs::BOSubclass.new
+
+ obj.instance_variable_set(:@test, :value)
+ obj.instance_variable_get(:@test).should == :value
+
+ obj.respond_to?(:hash).should == true
+ end
+
+ describe "BasicObject references" do
+ it "can refer to BasicObject from within itself" do
+ lambda { BasicObject::BasicObject }.should_not raise_error
+ end
+ end
+end
diff --git a/spec/ruby/core/basicobject/equal_spec.rb b/spec/ruby/core/basicobject/equal_spec.rb
new file mode 100644
index 0000000000..3c1ad56d4a
--- /dev/null
+++ b/spec/ruby/core/basicobject/equal_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/kernel/equal'
+
+describe "BasicObject#equal?" do
+ it "is a public instance method" do
+ BasicObject.should have_public_instance_method(:equal?)
+ end
+
+ it_behaves_like :object_equal, :equal?
+
+ it "is unaffected by overriding __id__" do
+ o1 = mock("object")
+ o2 = mock("object")
+ def o1.__id__; 10; end
+ def o2.__id__; 10; end
+ o1.equal?(o2).should be_false
+ end
+
+ it "is unaffected by overriding object_id" do
+ o1 = mock("object")
+ o1.stub!(:object_id).and_return(10)
+ o2 = mock("object")
+ o2.stub!(:object_id).and_return(10)
+ o1.equal?(o2).should be_false
+ end
+
+ it "is unaffected by overriding ==" do
+ # different objects, overriding == to return true
+ o1 = mock("object")
+ o1.stub!(:==).and_return(true)
+ o2 = mock("object")
+ o1.equal?(o2).should be_false
+
+ # same objects, overriding == to return false
+ o3 = mock("object")
+ o3.stub!(:==).and_return(false)
+ o3.equal?(o3).should be_true
+ end
+
+ it "is unaffected by overriding eql?" do
+ # different objects, overriding eql? to return true
+ o1 = mock("object")
+ o1.stub!(:eql?).and_return(true)
+ o2 = mock("object")
+ o1.equal?(o2).should be_false
+
+ # same objects, overriding eql? to return false
+ o3 = mock("object")
+ o3.stub!(:eql?).and_return(false)
+ o3.equal?(o3).should be_true
+ end
+end
diff --git a/spec/ruby/core/basicobject/equal_value_spec.rb b/spec/ruby/core/basicobject/equal_value_spec.rb
new file mode 100644
index 0000000000..6c825513c1
--- /dev/null
+++ b/spec/ruby/core/basicobject/equal_value_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/kernel/equal'
+
+describe "BasicObject#==" do
+ it "is a public instance method" do
+ BasicObject.should have_public_instance_method(:==)
+ end
+
+ it_behaves_like :object_equal, :==
+end
diff --git a/spec/ruby/core/basicobject/fixtures/classes.rb b/spec/ruby/core/basicobject/fixtures/classes.rb
new file mode 100644
index 0000000000..d1785afe31
--- /dev/null
+++ b/spec/ruby/core/basicobject/fixtures/classes.rb
@@ -0,0 +1,33 @@
+module BasicObjectSpecs
+ class IVars
+ def initialize
+ @secret = 99
+ end
+ end
+
+ module InstExec
+ def self.included(base)
+ base.instance_exec { @@count = 2 }
+ end
+ end
+
+ module InstExecIncluded
+ include InstExec
+ end
+
+ module InstEvalCVar
+ instance_eval { @@count = 2 }
+ end
+
+ class InstEvalConst
+ INST_EVAL_CONST_X = 2
+ end
+
+ module InstEvalOuter
+ module Inner
+ obj = InstEvalConst.new
+ X_BY_STR = obj.instance_eval("INST_EVAL_CONST_X") rescue nil
+ X_BY_BLOCK = obj.instance_eval { INST_EVAL_CONST_X } rescue nil
+ end
+ end
+end
diff --git a/spec/ruby/core/basicobject/fixtures/common.rb b/spec/ruby/core/basicobject/fixtures/common.rb
new file mode 100644
index 0000000000..3447a3a5e7
--- /dev/null
+++ b/spec/ruby/core/basicobject/fixtures/common.rb
@@ -0,0 +1,9 @@
+module BasicObjectSpecs
+ class BOSubclass < BasicObject
+ def self.kernel_defined?
+ defined?(Kernel)
+ end
+
+ include ::Kernel
+ end
+end
diff --git a/spec/ruby/core/basicobject/fixtures/remove_method_missing.rb b/spec/ruby/core/basicobject/fixtures/remove_method_missing.rb
new file mode 100644
index 0000000000..095b982d3a
--- /dev/null
+++ b/spec/ruby/core/basicobject/fixtures/remove_method_missing.rb
@@ -0,0 +1,9 @@
+class BasicObject
+ remove_method :method_missing
+end
+
+begin
+ Object.new.test_method
+rescue NoMethodError => e
+ puts e.class.name
+end
diff --git a/spec/ruby/core/basicobject/fixtures/singleton_method.rb b/spec/ruby/core/basicobject/fixtures/singleton_method.rb
new file mode 100644
index 0000000000..0e00e035fa
--- /dev/null
+++ b/spec/ruby/core/basicobject/fixtures/singleton_method.rb
@@ -0,0 +1,10 @@
+module BasicObjectSpecs
+ class SingletonMethod
+ def self.singleton_method_added name
+ ScratchPad.record [:singleton_method_added, name]
+ end
+
+ def self.singleton_method_to_alias
+ end
+ end
+end
diff --git a/spec/ruby/core/basicobject/initialize_spec.rb b/spec/ruby/core/basicobject/initialize_spec.rb
new file mode 100644
index 0000000000..0266496856
--- /dev/null
+++ b/spec/ruby/core/basicobject/initialize_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+describe "BasicObject#initialize" do
+ it "is a private instance method" do
+ BasicObject.should have_private_instance_method(:initialize)
+ end
+
+ it "does not accept arguments" do
+ lambda {
+ BasicObject.new("This", "makes it easier", "to call super", "from other constructors")
+ }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/basicobject/instance_eval_spec.rb b/spec/ruby/core/basicobject/instance_eval_spec.rb
new file mode 100644
index 0000000000..24b4d6dc69
--- /dev/null
+++ b/spec/ruby/core/basicobject/instance_eval_spec.rb
@@ -0,0 +1,188 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "BasicObject#instance_eval" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "is a public instance method" do
+ BasicObject.should have_public_instance_method(:instance_eval)
+ end
+
+ it "sets self to the receiver in the context of the passed block" do
+ a = BasicObject.new
+ a.instance_eval { self }.equal?(a).should be_true
+ end
+
+ it "evaluates strings" do
+ a = BasicObject.new
+ a.instance_eval('self').equal?(a).should be_true
+ end
+
+ it "expects a block with no arguments" do
+ lambda { "hola".instance_eval }.should raise_error(ArgumentError)
+ end
+
+ it "takes no arguments with a block" do
+ lambda { "hola".instance_eval(4, 5) {|a,b| a + b } }.should raise_error(ArgumentError)
+ end
+
+ it "yields the object to the block" do
+ "hola".instance_eval {|o| ScratchPad.record o }
+ ScratchPad.recorded.should == "hola"
+ end
+
+ it "returns the result of the block" do
+ "hola".instance_eval { :result }.should == :result
+ end
+
+ it "only binds the eval to the receiver" do
+ f = Object.new
+ f.instance_eval do
+ def foo
+ 1
+ end
+ end
+ f.foo.should == 1
+ lambda { Object.new.foo }.should raise_error(NoMethodError)
+ end
+
+ it "preserves self in the original block when passed a block argument" do
+ prc = proc { self }
+
+ old_self = prc.call
+
+ new_self = Object.new
+ new_self.instance_eval(&prc).should == new_self
+
+ prc.call.should == old_self
+ end
+
+ # TODO: This should probably be replaced with a "should behave like" that uses
+ # the many scoping/binding specs from kernel/eval_spec, since most of those
+ # behaviors are the same for instance_eval. See also module_eval/class_eval.
+
+ it "binds self to the receiver" do
+ s = "hola"
+ (s == s.instance_eval { self }).should be_true
+ o = mock('o')
+ (o == o.instance_eval("self")).should be_true
+ end
+
+ it "executes in the context of the receiver" do
+ "Ruby-fu".instance_eval { size }.should == 7
+ "hola".instance_eval("size").should == 4
+ Object.class_eval { "hola".instance_eval("to_s") }.should == "hola"
+ Object.class_eval { "Ruby-fu".instance_eval{ to_s } }.should == "Ruby-fu"
+
+ end
+
+ it "has access to receiver's instance variables" do
+ BasicObjectSpecs::IVars.new.instance_eval { @secret }.should == 99
+ BasicObjectSpecs::IVars.new.instance_eval("@secret").should == 99
+ end
+
+ it "treats block-local variables as local to the block" do
+ prc = instance_eval <<-CODE
+ proc do |x, prc|
+ if x
+ n = 2
+ else
+ n = 1
+ prc.call(true, prc)
+ n
+ end
+ end
+ CODE
+
+ prc.call(false, prc).should == 1
+ end
+
+ it "sets class variables in the receiver" do
+ BasicObjectSpecs::InstEvalCVar.class_variables.should include(:@@count)
+ BasicObjectSpecs::InstEvalCVar.send(:class_variable_get, :@@count).should == 2
+ end
+
+ it "makes the receiver metaclass the scoped class when used with a string" do
+ obj = Object.new
+ obj.instance_eval %{
+ class B; end
+ B
+ }
+ obj.singleton_class.const_get(:B).should be_an_instance_of(Class)
+ end
+
+ it "gets constants in the receiver if a string given" do
+ BasicObjectSpecs::InstEvalOuter::Inner::X_BY_STR.should == 2
+ end
+
+ it "doesn't get constants in the receiver if a block given" do
+ BasicObjectSpecs::InstEvalOuter::Inner::X_BY_BLOCK.should be_nil
+ end
+
+ it "raises a TypeError when defining methods on an immediate" do
+ lambda do
+ 1.instance_eval { def foo; end }
+ end.should raise_error(TypeError)
+ lambda do
+ :foo.instance_eval { def foo; end }
+ end.should raise_error(TypeError)
+ end
+
+quarantine! do # Not clean, leaves cvars lying around to break other specs
+ it "scopes class var accesses in the caller when called on a Fixnum" do
+ # Fixnum can take instance vars
+ Fixnum.class_eval "@@__tmp_instance_eval_spec = 1"
+ (defined? @@__tmp_instance_eval_spec).should be_nil
+
+ @@__tmp_instance_eval_spec = 2
+ 1.instance_eval { @@__tmp_instance_eval_spec }.should == 2
+ Fixnum.__send__(:remove_class_variable, :@@__tmp_instance_eval_spec)
+ end
+end
+
+ it "raises a TypeError when defining methods on numerics" do
+ lambda do
+ (1.0).instance_eval { def foo; end }
+ end.should raise_error(TypeError)
+ lambda do
+ (1 << 64).instance_eval { def foo; end }
+ end.should raise_error(TypeError)
+ end
+
+ it "evaluates procs originating from methods" do
+ def meth(arg); arg; end
+
+ m = method(:meth)
+ obj = Object.new
+
+ obj.instance_eval(&m).should == obj
+ end
+
+ it "evaluates string with given filename and linenumber" do
+ err = begin
+ Object.new.instance_eval("raise", "a_file", 10)
+ rescue => e
+ e
+ end
+ err.backtrace.first.split(":")[0..1].should == ["a_file", "10"]
+ end
+
+ it "evaluates string with given filename and negative linenumber" do
+ err = begin
+ Object.new.instance_eval("\n\nraise\n", "b_file", -100)
+ rescue => e
+ e
+ end
+ err.backtrace.first.split(":")[0..1].should == ["b_file", "-98"]
+ end
+
+ it "has access to the caller's local variables" do
+ x = nil
+
+ instance_eval "x = :value"
+
+ x.should == :value
+ end
+end
diff --git a/spec/ruby/core/basicobject/instance_exec_spec.rb b/spec/ruby/core/basicobject/instance_exec_spec.rb
new file mode 100644
index 0000000000..33c6b5a1d3
--- /dev/null
+++ b/spec/ruby/core/basicobject/instance_exec_spec.rb
@@ -0,0 +1,107 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "BasicObject#instance_exec" do
+ it "is a public instance method" do
+ BasicObject.should have_public_instance_method(:instance_exec)
+ end
+
+ it "sets self to the receiver in the context of the passed block" do
+ a = BasicObject.new
+ a.instance_exec { self }.equal?(a).should be_true
+ end
+
+ it "passes arguments to the block" do
+ a = BasicObject.new
+ a.instance_exec(1) { |b| b }.should equal(1)
+ end
+
+ it "raises a LocalJumpError unless given a block" do
+ lambda { "hola".instance_exec }.should raise_error(LocalJumpError)
+ end
+
+ it "has an arity of -1" do
+ Object.new.method(:instance_exec).arity.should == -1
+ end
+
+ it "accepts arguments with a block" do
+ lambda { "hola".instance_exec(4, 5) { |a,b| a + b } }.should_not raise_error
+ end
+
+ it "doesn't pass self to the block as an argument" do
+ "hola".instance_exec { |o| o }.should be_nil
+ end
+
+ it "passes any arguments to the block" do
+ Object.new.instance_exec(1,2) {|one, two| one + two}.should == 3
+ end
+
+ it "only binds the exec to the receiver" do
+ f = Object.new
+ f.instance_exec do
+ def foo
+ 1
+ end
+ end
+ f.foo.should == 1
+ lambda { Object.new.foo }.should raise_error(NoMethodError)
+ end
+
+ # TODO: This should probably be replaced with a "should behave like" that uses
+ # the many scoping/binding specs from kernel/eval_spec, since most of those
+ # behaviors are the same for instance_exec. See also module_eval/class_eval.
+
+ it "binds self to the receiver" do
+ s = "hola"
+ (s == s.instance_exec { self }).should == true
+ end
+
+ it "binds the block's binding self to the receiver" do
+ s = "hola"
+ (s == s.instance_exec { eval "self", binding }).should == true
+ end
+
+ it "executes in the context of the receiver" do
+ "Ruby-fu".instance_exec { size }.should == 7
+ Object.class_eval { "Ruby-fu".instance_exec{ to_s } }.should == "Ruby-fu"
+ end
+
+ it "has access to receiver's instance variables" do
+ BasicObjectSpecs::IVars.new.instance_exec { @secret }.should == 99
+ end
+
+ it "sets class variables in the receiver" do
+ BasicObjectSpecs::InstExec.class_variables.should include(:@@count)
+ BasicObjectSpecs::InstExec.send(:class_variable_get, :@@count).should == 2
+ end
+
+ it "raises a TypeError when defining methods on an immediate" do
+ lambda do
+ 1.instance_exec { def foo; end }
+ end.should raise_error(TypeError)
+ lambda do
+ :foo.instance_exec { def foo; end }
+ end.should raise_error(TypeError)
+ end
+
+quarantine! do # Not clean, leaves cvars lying around to break other specs
+ it "scopes class var accesses in the caller when called on a Fixnum" do
+ # Fixnum can take instance vars
+ Fixnum.class_eval "@@__tmp_instance_exec_spec = 1"
+ (defined? @@__tmp_instance_exec_spec).should == nil
+
+ @@__tmp_instance_exec_spec = 2
+ 1.instance_exec { @@__tmp_instance_exec_spec }.should == 2
+ Fixnum.__send__(:remove_class_variable, :@@__tmp_instance_exec_spec)
+ end
+end
+
+ it "raises a TypeError when defining methods on numerics" do
+ lambda do
+ (1.0).instance_exec { def foo; end }
+ end.should raise_error(TypeError)
+ lambda do
+ (1 << 64).instance_exec { def foo; end }
+ end.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/basicobject/method_missing_spec.rb b/spec/ruby/core/basicobject/method_missing_spec.rb
new file mode 100644
index 0000000000..b048780ee8
--- /dev/null
+++ b/spec/ruby/core/basicobject/method_missing_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../shared/basicobject/method_missing'
+
+describe "BasicObject#method_missing" do
+ it "is a private method" do
+ BasicObject.should have_private_instance_method(:method_missing)
+ end
+end
+
+describe "BasicObject#method_missing" do
+ it_behaves_like :method_missing_class, nil, BasicObject
+end
+
+describe "BasicObject#method_missing" do
+ it_behaves_like :method_missing_instance, nil, BasicObject
+end
+
+describe "BasicObject#method_missing" do
+ it_behaves_like :method_missing_defined_module, nil, KernelSpecs::ModuleMM
+end
+
+describe "BasicObject#method_missing" do
+ it_behaves_like :method_missing_module, nil, KernelSpecs::ModuleNoMM
+end
+
+describe "BasicObject#method_missing" do
+ it_behaves_like :method_missing_defined_class, nil, KernelSpecs::ClassMM
+end
+
+describe "BasicObject#method_missing" do
+ it_behaves_like :method_missing_class, nil, KernelSpecs::ClassNoMM
+end
+
+describe "BasicObject#method_missing" do
+ it_behaves_like :method_missing_defined_instance, nil, KernelSpecs::ClassMM
+end
+
+describe "BasicObject#method_missing" do
+ it_behaves_like :method_missing_instance, nil, KernelSpecs::ClassNoMM
+end
diff --git a/spec/ruby/core/basicobject/not_equal_spec.rb b/spec/ruby/core/basicobject/not_equal_spec.rb
new file mode 100644
index 0000000000..9329128c43
--- /dev/null
+++ b/spec/ruby/core/basicobject/not_equal_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../spec_helper'
+
+describe "BasicObject#!=" do
+ it "is a public instance method" do
+ BasicObject.should have_public_instance_method(:'!=')
+ end
+
+ it "returns true if other is not identical to self" do
+ a = BasicObject.new
+ b = BasicObject.new
+ (a != b).should be_true
+ end
+
+ it "returns true if other is an Object" do
+ a = BasicObject.new
+ b = Object.new
+ (a != b).should be_true
+ end
+
+ it "returns false if other is identical to self" do
+ a = BasicObject.new
+ (a != a).should be_false
+ end
+
+ it "dispatches to #==" do
+ a = mock("not_equal")
+ b = BasicObject.new
+ a.should_receive(:==).and_return(true)
+
+ (a != b).should be_false
+ end
+
+ describe "when invoked using Kernel#send" do
+ it "returns true if other is not identical to self" do
+ a = Object.new
+ b = Object.new
+ a.send(:!=, b).should be_true
+ end
+
+ it "returns false if other is identical to self" do
+ a = Object.new
+ a.send(:!=, a).should be_false
+ end
+
+ it "dispatches to #==" do
+ a = mock("not_equal")
+ b = Object.new
+ a.should_receive(:==).and_return(true)
+
+ a.send(:!=, b).should be_false
+ end
+ end
+end
diff --git a/spec/ruby/core/basicobject/not_spec.rb b/spec/ruby/core/basicobject/not_spec.rb
new file mode 100644
index 0000000000..ca4cb6f5ff
--- /dev/null
+++ b/spec/ruby/core/basicobject/not_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "BasicObject#!" do
+ it "is a public instance method" do
+ BasicObject.should have_public_instance_method(:'!')
+ end
+
+ it "returns false" do
+ (!BasicObject.new).should be_false
+ end
+end
diff --git a/spec/ruby/core/basicobject/singleton_method_added_spec.rb b/spec/ruby/core/basicobject/singleton_method_added_spec.rb
new file mode 100644
index 0000000000..8d256e22db
--- /dev/null
+++ b/spec/ruby/core/basicobject/singleton_method_added_spec.rb
@@ -0,0 +1,86 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/singleton_method'
+
+describe "BasicObject#singleton_method_added" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "is a private method" do
+ BasicObject.should have_private_instance_method(:singleton_method_added)
+ end
+
+ it "is called when a singleton method is defined on an object" do
+ obj = BasicObject.new
+
+ def obj.singleton_method_added(name)
+ ScratchPad.record [:singleton_method_added, name]
+ end
+
+ def obj.new_singleton_method
+ end
+
+ ScratchPad.recorded.should == [:singleton_method_added, :new_singleton_method]
+ end
+
+ it "is not called for instance methods" do
+ ScratchPad.record []
+
+ Module.new do
+ def self.singleton_method_added(name)
+ ScratchPad << name
+ end
+
+ def new_instance_method
+ end
+ end
+
+ ScratchPad.recorded.should_not include(:new_instance_method)
+ end
+
+ it "is called when a singleton method is defined on a module" do
+ class BasicObjectSpecs::SingletonMethod
+ def self.new_method_on_self
+ end
+ end
+ ScratchPad.recorded.should == [:singleton_method_added, :new_method_on_self]
+ end
+
+ it "is called when a method is defined in the singleton class" do
+ class BasicObjectSpecs::SingletonMethod
+ class << self
+ def new_method_on_singleton
+ end
+ end
+ end
+ ScratchPad.recorded.should == [:singleton_method_added, :new_method_on_singleton]
+ end
+
+ it "is called when a method is defined with alias_method in the singleton class" do
+ class BasicObjectSpecs::SingletonMethod
+ class << self
+ alias_method :new_method_on_singleton_with_alias_method, :singleton_method_to_alias
+ end
+ end
+ ScratchPad.recorded.should == [:singleton_method_added, :new_method_on_singleton_with_alias_method]
+ end
+
+ it "is called when a method is defined with syntax alias in the singleton class" do
+ class BasicObjectSpecs::SingletonMethod
+ class << self
+ alias new_method_on_singleton_with_syntax_alias singleton_method_to_alias
+ end
+ end
+ ScratchPad.recorded.should == [:singleton_method_added, :new_method_on_singleton_with_syntax_alias]
+ end
+
+ it "is called when define_method is used in the singleton class" do
+ class BasicObjectSpecs::SingletonMethod
+ class << self
+ define_method :new_method_with_define_method do
+ end
+ end
+ end
+ ScratchPad.recorded.should == [:singleton_method_added, :new_method_with_define_method]
+ end
+end
diff --git a/spec/ruby/core/basicobject/singleton_method_removed_spec.rb b/spec/ruby/core/basicobject/singleton_method_removed_spec.rb
new file mode 100644
index 0000000000..46f9a6894c
--- /dev/null
+++ b/spec/ruby/core/basicobject/singleton_method_removed_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+
+describe "BasicObject#singleton_method_removed" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "is a private method" do
+ BasicObject.should have_private_instance_method(:singleton_method_removed)
+ end
+
+ it "is called when a method is removed on self" do
+ klass = Class.new
+ def klass.singleton_method_removed(name)
+ ScratchPad.record [:singleton_method_removed, name]
+ end
+ def klass.singleton_method_to_remove
+ end
+ class << klass
+ remove_method :singleton_method_to_remove
+ end
+ ScratchPad.recorded.should == [:singleton_method_removed, :singleton_method_to_remove]
+ end
+end
diff --git a/spec/ruby/core/basicobject/singleton_method_undefined_spec.rb b/spec/ruby/core/basicobject/singleton_method_undefined_spec.rb
new file mode 100644
index 0000000000..7d6c7207db
--- /dev/null
+++ b/spec/ruby/core/basicobject/singleton_method_undefined_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+
+describe "BasicObject#singleton_method_undefined" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "is a private method" do
+ BasicObject.should have_private_instance_method(:singleton_method_undefined)
+ end
+
+ it "is called when a method is removed on self" do
+ klass = Class.new
+ def klass.singleton_method_undefined(name)
+ ScratchPad.record [:singleton_method_undefined, name]
+ end
+ def klass.singleton_method_to_undefine
+ end
+ class << klass
+ undef_method :singleton_method_to_undefine
+ end
+ ScratchPad.recorded.should == [:singleton_method_undefined, :singleton_method_to_undefine]
+ end
+end
diff --git a/spec/ruby/core/binding/clone_spec.rb b/spec/ruby/core/binding/clone_spec.rb
new file mode 100644
index 0000000000..ebd40f5377
--- /dev/null
+++ b/spec/ruby/core/binding/clone_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/clone'
+
+describe "Binding#clone" do
+ it_behaves_like :binding_clone, :clone
+end
diff --git a/spec/ruby/core/binding/dup_spec.rb b/spec/ruby/core/binding/dup_spec.rb
new file mode 100644
index 0000000000..43968213c8
--- /dev/null
+++ b/spec/ruby/core/binding/dup_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/clone'
+
+describe "Binding#dup" do
+ it_behaves_like :binding_clone, :dup
+end
diff --git a/spec/ruby/core/binding/eval_spec.rb b/spec/ruby/core/binding/eval_spec.rb
new file mode 100644
index 0000000000..c0264192b8
--- /dev/null
+++ b/spec/ruby/core/binding/eval_spec.rb
@@ -0,0 +1,95 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Binding#eval" do
+ it "behaves like Kernel.eval(..., self)" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+
+ bind.eval("@secret += square(3)").should == 10
+ bind.eval("a").should be_true
+
+ bind.eval("class Inside; end")
+ bind.eval("Inside.name").should == "BindingSpecs::Demo::Inside"
+ end
+
+ it "does not leak variables to cloned bindings" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_empty_binding
+ bind2 = bind.dup
+
+ bind.eval("x = 72")
+ bind.local_variables.should == [:x]
+ bind2.local_variables.should == []
+ end
+
+ it "inherits __LINE__ from the enclosing scope" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ bind.eval("__LINE__").should == obj.get_line_of_binding
+ end
+
+ it "preserves __LINE__ across multiple calls to eval" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ bind.eval("__LINE__").should == obj.get_line_of_binding
+ bind.eval("__LINE__").should == obj.get_line_of_binding
+ end
+
+ it "increments __LINE__ on each line of a multiline eval" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ bind.eval("#foo\n__LINE__").should == obj.get_line_of_binding + 1
+ end
+
+ it "inherits __LINE__ from the enclosing scope even if the Binding is created with #send" do
+ obj = BindingSpecs::Demo.new(1)
+ bind, line = obj.get_binding_with_send_and_line
+ bind.eval("__LINE__").should == line
+ end
+
+ it "starts with a __LINE__ of 1 if a filename is passed" do
+ bind = BindingSpecs::Demo.new(1).get_binding
+ bind.eval("__LINE__", "(test)").should == 1
+ bind.eval("#foo\n__LINE__", "(test)").should == 2
+ end
+
+ it "starts with a __LINE__ from the third argument if passed" do
+ bind = BindingSpecs::Demo.new(1).get_binding
+ bind.eval("__LINE__", "(test)", 88).should == 88
+ bind.eval("#foo\n__LINE__", "(test)", 88).should == 89
+ end
+
+ it "inherits __FILE__ from the enclosing scope" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ bind.eval("__FILE__").should == obj.get_file_of_binding
+ end
+
+ it "uses the __FILE__ that is passed in" do
+ bind = BindingSpecs::Demo.new(1).get_binding
+ bind.eval("__FILE__", "(test)").should == "(test)"
+ end
+
+ describe "with a file given" do
+ it "does not store the filename permanently" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+
+ bind.eval("__FILE__", "test.rb").should == "test.rb"
+ bind.eval("__FILE__").should_not == "test.rb"
+ end
+ end
+
+ it "with __method__ returns the method where the Binding was created" do
+ obj = BindingSpecs::Demo.new(1)
+ bind, meth = obj.get_binding_and_method
+ bind.eval("__method__").should == meth
+ end
+
+ it "with __method__ returns the method where the Binding was created, ignoring #send" do
+ obj = BindingSpecs::Demo.new(1)
+ bind, meth = obj.get_binding_with_send_and_method
+ bind.eval("__method__").should == meth
+ end
+end
diff --git a/spec/ruby/core/binding/fixtures/classes.rb b/spec/ruby/core/binding/fixtures/classes.rb
new file mode 100644
index 0000000000..43e32cacf6
--- /dev/null
+++ b/spec/ruby/core/binding/fixtures/classes.rb
@@ -0,0 +1,52 @@
+module BindingSpecs
+ class Demo
+ def initialize(n)
+ @secret = n
+ end
+
+ def square(n)
+ n * n
+ end
+
+ def get_binding_and_line
+ a = true
+ [binding, __LINE__]
+ end
+
+ def get_binding
+ get_binding_and_line[0]
+ end
+
+ def get_line_of_binding
+ get_binding_and_line[1]
+ end
+
+ def get_file_of_binding
+ __FILE__
+ end
+
+ def get_binding_with_send_and_line
+ [send(:binding), __LINE__]
+ end
+
+ def get_binding_and_method
+ [binding, :get_binding_and_method]
+ end
+
+ def get_binding_with_send_and_method
+ [send(:binding), :get_binding_with_send_and_method]
+ end
+
+ def get_empty_binding
+ binding
+ end
+
+ def get_binding_in_block
+ a = true
+ 1.times do
+ b = false
+ return binding
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/binding/local_variable_defined_spec.rb b/spec/ruby/core/binding/local_variable_defined_spec.rb
new file mode 100644
index 0000000000..7b48257294
--- /dev/null
+++ b/spec/ruby/core/binding/local_variable_defined_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../../spec_helper'
+
+describe 'Binding#local_variable_defined?' do
+ it 'returns false when a variable is not defined' do
+ binding.local_variable_defined?(:foo).should == false
+ end
+
+ it 'returns true when a regular local variable is defined' do
+ foo = 10
+ binding.local_variable_defined?(:foo).should == true
+ end
+
+ it 'returns true when a local variable is defined using eval()' do
+ bind = binding
+ bind.eval('foo = 10')
+
+ bind.local_variable_defined?(:foo).should == true
+ end
+
+ it 'returns true when a local variable is defined using Binding#local_variable_set' do
+ bind = binding
+ bind.local_variable_set(:foo, 10)
+
+ bind.local_variable_defined?(:foo).should == true
+ end
+
+ it 'returns true when a local variable is defined in a parent scope' do
+ foo = 10
+ lambda {
+ binding.local_variable_defined?(:foo)
+ }.call.should == true
+ end
+
+ it 'allows usage of a String as the variable name' do
+ foo = 10
+ binding.local_variable_defined?('foo').should == true
+ end
+
+ it 'allows usage of an object responding to #to_str as the variable name' do
+ foo = 10
+ name = mock(:obj)
+ name.stub!(:to_str).and_return('foo')
+
+ binding.local_variable_defined?(name).should == true
+ end
+end
diff --git a/spec/ruby/core/binding/local_variable_get_spec.rb b/spec/ruby/core/binding/local_variable_get_spec.rb
new file mode 100644
index 0000000000..eeb3ae44ed
--- /dev/null
+++ b/spec/ruby/core/binding/local_variable_get_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Binding#local_variable_get" do
+ it "reads local variables captured in the binding" do
+ a = 42
+ bind = binding
+ bind.local_variable_get(:a).should == 42
+ end
+
+ it "raises a NameError for missing variables" do
+ bind = BindingSpecs::Demo.new(1).get_empty_binding
+
+ lambda {
+ bind.local_variable_get(:no_such_variable)
+ }.should raise_error(NameError)
+ end
+
+ it "reads variables added later to the binding" do
+ bind = BindingSpecs::Demo.new(1).get_empty_binding
+
+ lambda {
+ bind.local_variable_get(:a)
+ }.should raise_error(NameError)
+
+ bind.local_variable_set(:a, 42)
+
+ bind.local_variable_get(:a).should == 42
+ end
+
+ it 'gets a local variable defined in a parent scope' do
+ number = 10
+
+ lambda {
+ binding.local_variable_get(:number)
+ }.call.should == 10
+ end
+
+ it 'gets a local variable defined using eval()' do
+ bind = binding
+ bind.eval('number = 10')
+
+ bind.local_variable_get(:number).should == 10
+ end
+
+ it "raises a NameError on global access" do
+ bind = binding
+ lambda { bind.local_variable_get(:$0) }.should raise_error(NameError)
+ end
+
+ it "raises a NameError on special variable access" do
+ bind = binding
+ lambda { bind.local_variable_get(:$~) }.should raise_error(NameError)
+ lambda { bind.local_variable_get(:$_) }.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/core/binding/local_variable_set_spec.rb b/spec/ruby/core/binding/local_variable_set_spec.rb
new file mode 100644
index 0000000000..035e9a3c2e
--- /dev/null
+++ b/spec/ruby/core/binding/local_variable_set_spec.rb
@@ -0,0 +1,71 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Binding#local_variable_set" do
+ it "adds nonexistent variables to the binding's eval scope" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_empty_binding
+ bind.eval('local_variables').should == []
+ bind.local_variable_set :foo, 1
+ bind.eval('local_variables').should == [:foo]
+ bind.eval('foo').should == 1
+ end
+
+ it 'sets a new local variable' do
+ bind = binding
+
+ bind.local_variable_set(:number, 10)
+ bind.local_variable_get(:number).should == 10
+ end
+
+ it 'sets a local variable using a String as the variable name' do
+ bind = binding
+
+ bind.local_variable_set('number', 10)
+ bind.local_variable_get('number').should == 10
+ end
+
+ it 'sets a local variable using an object responding to #to_str as the variable name' do
+ bind = binding
+ name = mock(:obj)
+ name.stub!(:to_str).and_return('number')
+
+ bind.local_variable_set(name, 10)
+ bind.local_variable_get(name).should == 10
+ end
+
+ it 'scopes new local variables to the receiving Binding' do
+ bind = binding
+ bind.local_variable_set(:number, 10)
+
+ lambda { number }.should raise_error(NameError)
+ end
+
+ it 'overwrites an existing local variable defined before a Binding' do
+ number = 10
+ bind = binding
+
+ bind.local_variable_set(:number, 20)
+ number.should == 20
+ end
+
+ it 'overwrites a local variable defined using eval()' do
+ bind = binding
+ bind.eval('number = 10')
+
+ bind.local_variable_set(:number, 20)
+ bind.local_variable_get(:number).should == 20
+ end
+
+ it "raises a NameError on global access" do
+ bind = binding
+ lambda { bind.local_variable_set(:$0, "") }.should raise_error(NameError)
+ end
+
+ it "raises a NameError on special variable access" do
+ bind = binding
+ lambda { bind.local_variable_set(:$~, "") }.should raise_error(NameError)
+ lambda { bind.local_variable_set(:$_, "") }.should raise_error(NameError)
+ end
+
+end
diff --git a/spec/ruby/core/binding/local_variables_spec.rb b/spec/ruby/core/binding/local_variables_spec.rb
new file mode 100644
index 0000000000..92c817b9a8
--- /dev/null
+++ b/spec/ruby/core/binding/local_variables_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+
+describe "Binding#local_variables" do
+ it "returns an Array" do
+ binding.local_variables.should be_kind_of(Array)
+ end
+
+ it "includes local variables in the current scope" do
+ a = 1
+ b = nil
+ binding.local_variables.should == [:a, :b]
+ end
+
+ it "includes local variables defined after calling binding.local_variables" do
+ binding.local_variables.should == [:a, :b]
+ a = 1
+ b = 2
+ end
+
+ it "includes local variables of inherited scopes and eval'ed context" do
+ p = proc { |a| b = 1; eval("c = 2; binding.local_variables") }
+ p.call.should == [:c, :a, :b, :p]
+ end
+
+ it "includes shadowed local variables only once" do
+ a = 1
+ proc { |a| binding.local_variables }.call(2).should == [:a]
+ end
+
+ it "includes new variables defined in the binding" do
+ b = binding
+ b.local_variable_set :a, 42
+ b.local_variables.should == [:a, :b]
+ end
+end
diff --git a/spec/ruby/core/binding/receiver_spec.rb b/spec/ruby/core/binding/receiver_spec.rb
new file mode 100644
index 0000000000..4bf5e7a7bd
--- /dev/null
+++ b/spec/ruby/core/binding/receiver_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Binding#receiver" do
+ it "returns the object to which binding is bound" do
+ obj = BindingSpecs::Demo.new(1)
+ obj.get_binding.receiver.should == obj
+
+ binding.receiver.should == self
+ end
+end
diff --git a/spec/ruby/core/binding/shared/clone.rb b/spec/ruby/core/binding/shared/clone.rb
new file mode 100644
index 0000000000..0e934ac1b5
--- /dev/null
+++ b/spec/ruby/core/binding/shared/clone.rb
@@ -0,0 +1,34 @@
+describe :binding_clone, shared: true do
+ before :each do
+ @b1 = BindingSpecs::Demo.new(99).get_binding
+ @b2 = @b1.send(@method)
+ @b3 = BindingSpecs::Demo.new(99).get_binding_in_block
+ @b4 = @b3.send(@method)
+ end
+
+ it "returns a copy of the Binding object" do
+ [[@b1, @b2, "a"],
+ [@b3, @b4, "a", "b"]].each do |b1, b2, *vars|
+ b1.should_not == b2
+
+ eval("@secret", b1).should == eval("@secret", b2)
+ eval("square(2)", b1).should == eval("square(2)", b2)
+ eval("self.square(2)", b1).should == eval("self.square(2)", b2)
+ vars.each do |v|
+ eval("#{v}", b1).should == eval("#{v}", b2)
+ end
+ end
+ end
+
+ it "is a shallow copy of the Binding object" do
+ [[@b1, @b2, "a"],
+ [@b3, @b4, "a", "b"]].each do |b1, b2, *vars|
+ vars.each do |v|
+ eval("#{v} = false", b1)
+ eval("#{v}", b2).should == false
+ end
+ b1.local_variable_set(:x, 37)
+ b2.local_variable_defined?(:x).should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/builtin_constants/builtin_constants_spec.rb b/spec/ruby/core/builtin_constants/builtin_constants_spec.rb
new file mode 100644
index 0000000000..0840291994
--- /dev/null
+++ b/spec/ruby/core/builtin_constants/builtin_constants_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+
+describe "RUBY_VERSION" do
+ it "is a String" do
+ RUBY_VERSION.should be_kind_of(String)
+ end
+end
+
+describe "RUBY_PATCHLEVEL" do
+ it "is a Fixnum" do
+ RUBY_PATCHLEVEL.should be_kind_of(Fixnum)
+ end
+end
+
+describe "RUBY_COPYRIGHT" do
+ it "is a String" do
+ RUBY_COPYRIGHT.should be_kind_of(String)
+ end
+end
+
+describe "RUBY_DESCRIPTION" do
+ it "is a String" do
+ RUBY_DESCRIPTION.should be_kind_of(String)
+ end
+end
+
+describe "RUBY_ENGINE" do
+ it "is a String" do
+ RUBY_ENGINE.should be_kind_of(String)
+ end
+end
+
+describe "RUBY_PLATFORM" do
+ it "is a String" do
+ RUBY_PLATFORM.should be_kind_of(String)
+ end
+end
+
+describe "RUBY_RELEASE_DATE" do
+ it "is a String" do
+ RUBY_RELEASE_DATE.should be_kind_of(String)
+ end
+end
+
+describe "RUBY_REVISION" do
+ it "is a Fixnum" do
+ RUBY_REVISION.should be_kind_of(Fixnum)
+ end
+end
diff --git a/spec/ruby/core/class/allocate_spec.rb b/spec/ruby/core/class/allocate_spec.rb
new file mode 100644
index 0000000000..4cf5665392
--- /dev/null
+++ b/spec/ruby/core/class/allocate_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+
+describe "Class#allocate" do
+ it "returns an instance of self" do
+ klass = Class.new
+ klass.allocate.should be_an_instance_of(klass)
+ end
+
+ it "returns a fully-formed instance of Module" do
+ klass = Class.allocate
+ klass.constants.should_not == nil
+ klass.methods.should_not == nil
+ end
+
+ it "throws an exception when calling a method on a new instance" do
+ klass = Class.allocate
+ lambda do
+ klass.new
+ end.should raise_error(Exception)
+ end
+
+ it "does not call initialize on the new instance" do
+ klass = Class.new do
+ def initialize(*args)
+ @initialized = true
+ end
+
+ def initialized?
+ @initialized || false
+ end
+ end
+
+ klass.allocate.initialized?.should == false
+ end
+
+ it "raises TypeError for #superclass" do
+ lambda do
+ Class.allocate.superclass
+ end.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/class/dup_spec.rb b/spec/ruby/core/class/dup_spec.rb
new file mode 100644
index 0000000000..701fd72e19
--- /dev/null
+++ b/spec/ruby/core/class/dup_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# NOTE: This is actually implemented by Module#initialize_copy
+describe "Class#dup" do
+ it "duplicates both the class and the singleton class" do
+ klass = Class.new do
+ def hello
+ "hello"
+ end
+
+ def self.message
+ "text"
+ end
+ end
+
+ klass_dup = klass.dup
+
+ klass_dup.new.hello.should == "hello"
+ klass_dup.message.should == "text"
+ end
+
+ it "retains an included module in the ancestor chain for the singleton class" do
+ klass = Class.new
+ mod = Module.new do
+ def hello
+ "hello"
+ end
+ end
+
+ klass.extend(mod)
+ klass_dup = klass.dup
+ klass_dup.hello.should == "hello"
+ end
+
+ it "retains the correct ancestor chain for the singleton class" do
+ super_klass = Class.new do
+ def hello
+ "hello"
+ end
+
+ def self.message
+ "text"
+ end
+ end
+
+ klass = Class.new(super_klass)
+ klass_dup = klass.dup
+
+ klass_dup.new.hello.should == "hello"
+ klass_dup.message.should == "text"
+ end
+
+ it "sets the name from the class to nil if not assigned to a constant" do
+ copy = CoreClassSpecs::Record.dup
+ copy.name.should be_nil
+ end
+
+ it "stores the new name if assigned to a constant" do
+ CoreClassSpecs::RecordCopy = CoreClassSpecs::Record.dup
+ CoreClassSpecs::RecordCopy.name.should == "CoreClassSpecs::RecordCopy"
+ end
+
+end
diff --git a/spec/ruby/core/class/fixtures/classes.rb b/spec/ruby/core/class/fixtures/classes.rb
new file mode 100644
index 0000000000..f96db90795
--- /dev/null
+++ b/spec/ruby/core/class/fixtures/classes.rb
@@ -0,0 +1,47 @@
+module CoreClassSpecs
+ class Record
+ end
+
+ module M
+ def inherited(klass)
+ ScratchPad.record klass
+ super
+ end
+ end
+
+ class F; end
+ class << F
+ include M
+ end
+
+ class A
+ def self.inherited(klass)
+ ScratchPad.record klass
+ end
+ end
+
+ class H < A
+ def self.inherited(klass)
+ super
+ end
+ end
+
+ module Inherited
+ class A
+ SUBCLASSES = []
+ def self.inherited(subclass)
+ SUBCLASSES << [self, subclass]
+ end
+ end
+
+ class B < A; end
+ class B < A; end # reopen
+ class C < B; end
+
+ class D
+ def self.inherited(subclass)
+ ScratchPad << self
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/class/inherited_spec.rb b/spec/ruby/core/class/inherited_spec.rb
new file mode 100644
index 0000000000..fb7fb8e75a
--- /dev/null
+++ b/spec/ruby/core/class/inherited_spec.rb
@@ -0,0 +1,101 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Class.inherited" do
+
+ before :each do
+ ScratchPad.record nil
+ end
+
+ it "is invoked with the child Class when self is subclassed" do
+ begin
+ top = Class.new do
+ def self.inherited(cls)
+ $child_class = cls
+ end
+ end
+
+ child = Class.new(top)
+ $child_class.should == child
+
+ other_child = Class.new(top)
+ $child_class.should == other_child
+ ensure
+ $child_class = nil
+ end
+ end
+
+ it "is invoked only once per subclass" do
+ expected = [
+ [CoreClassSpecs::Inherited::A, CoreClassSpecs::Inherited::B],
+ [CoreClassSpecs::Inherited::B, CoreClassSpecs::Inherited::C],
+ ]
+
+ CoreClassSpecs::Inherited::A::SUBCLASSES.should == expected
+ end
+
+ it "is called when marked as a private class method" do
+ a = Class.new do
+ def self.inherited(klass)
+ ScratchPad.record klass
+ end
+ end
+ a.private_class_method :inherited
+ ScratchPad.recorded.should == nil
+ b = Class.new(a)
+ ScratchPad.recorded.should == b
+ end
+
+ it "is called when marked as a protected class method" do
+ a = Class.new
+ class << a
+ def inherited(klass)
+ ScratchPad.record klass
+ end
+ protected :inherited
+ end
+ ScratchPad.recorded.should == nil
+ b = Class.new(a)
+ ScratchPad.recorded.should == b
+ end
+
+ it "is called when marked as a public class method" do
+ a = Class.new do
+ def self.inherited(klass)
+ ScratchPad.record klass
+ end
+ end
+ a.public_class_method :inherited
+ ScratchPad.recorded.should == nil
+ b = Class.new(a)
+ ScratchPad.recorded.should == b
+ end
+
+ it "is called by super from a method provided by an included module" do
+ ScratchPad.recorded.should == nil
+ e = Class.new(CoreClassSpecs::F)
+ ScratchPad.recorded.should == e
+ end
+
+ it "is called by super even when marked as a private class method" do
+ ScratchPad.recorded.should == nil
+ CoreClassSpecs::H.private_class_method :inherited
+ i = Class.new(CoreClassSpecs::H)
+ ScratchPad.recorded.should == i
+ end
+
+ it "will be invoked by child class regardless of visibility" do
+ top = Class.new do
+ class << self
+ def inherited(cls); end
+ end
+ end
+
+ class << top; private :inherited; end
+ lambda { Class.new(top) }.should_not raise_error
+
+ class << top; protected :inherited; end
+ lambda { Class.new(top) }.should_not raise_error
+ end
+
+end
diff --git a/spec/ruby/core/class/initialize_spec.rb b/spec/ruby/core/class/initialize_spec.rb
new file mode 100644
index 0000000000..88a9fcfa7a
--- /dev/null
+++ b/spec/ruby/core/class/initialize_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+
+describe "Class#initialize" do
+ it "is private" do
+ Class.should have_private_method(:initialize)
+ end
+
+ it "raises a TypeError when called on already initialized classes" do
+ lambda{
+ Fixnum.send :initialize
+ }.should raise_error(TypeError)
+
+ lambda{
+ Object.send :initialize
+ }.should raise_error(TypeError)
+ end
+
+ # See [redmine:2601]
+ it "raises a TypeError when called on BasicObject" do
+ lambda{
+ BasicObject.send :initialize
+ }.should raise_error(TypeError)
+ end
+
+ describe "when given the Class" do
+ before :each do
+ @uninitialized = Class.allocate
+ end
+
+ it "raises a TypeError" do
+ lambda{@uninitialized.send(:initialize, Class)}.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/class/new_spec.rb b/spec/ruby/core/class/new_spec.rb
new file mode 100644
index 0000000000..7f7ec183ea
--- /dev/null
+++ b/spec/ruby/core/class/new_spec.rb
@@ -0,0 +1,154 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Class.new with a block given" do
+ it "yields the new class as self in the block" do
+ self_in_block = nil
+ klass = Class.new do
+ self_in_block = self
+ end
+ self_in_block.should equal klass
+ end
+
+ it "uses the given block as the class' body" do
+ klass = Class.new do
+ def self.message
+ "text"
+ end
+
+ def hello
+ "hello again"
+ end
+ end
+
+ klass.message.should == "text"
+ klass.new.hello.should == "hello again"
+ end
+
+ it "creates a subclass of the given superclass" do
+ sc = Class.new do
+ def self.body
+ @body
+ end
+ @body = self
+ def message; "text"; end
+ end
+ klass = Class.new(sc) do
+ def self.body
+ @body
+ end
+ @body = self
+ def message2; "hello"; end
+ end
+
+ klass.body.should == klass
+ sc.body.should == sc
+ klass.superclass.should == sc
+ klass.new.message.should == "text"
+ klass.new.message2.should == "hello"
+ end
+
+ it "runs the inherited hook after yielding the block" do
+ ScratchPad.record []
+ klass = Class.new(CoreClassSpecs::Inherited::D) do
+ ScratchPad << self
+ end
+
+ ScratchPad.recorded.should == [CoreClassSpecs::Inherited::D, klass]
+ end
+end
+
+describe "Class.new" do
+ it "creates a new anonymous class" do
+ klass = Class.new
+ klass.is_a?(Class).should == true
+
+ klass_instance = klass.new
+ klass_instance.is_a?(klass).should == true
+ end
+
+ it "raises a TypeError if passed a metaclass" do
+ obj = mock("Class.new metaclass")
+ meta = obj.singleton_class
+ lambda { Class.new meta }.should raise_error(TypeError)
+ end
+
+ it "creates a class without a name" do
+ Class.new.name.should be_nil
+ end
+
+ it "creates a class that can be given a name by assigning it to a constant" do
+ ::MyClass = Class.new
+ ::MyClass.name.should == "MyClass"
+ a = Class.new
+ MyClass::NestedClass = a
+ MyClass::NestedClass.name.should == "MyClass::NestedClass"
+ end
+
+ it "sets the new class' superclass to the given class" do
+ top = Class.new
+ Class.new(top).superclass.should == top
+ end
+
+ it "sets the new class' superclass to Object when no class given" do
+ Class.new.superclass.should == Object
+ end
+
+ it "raises a TypeError when given a non-Class" do
+ error_msg = /superclass must be a Class/
+ lambda { Class.new("") }.should raise_error(TypeError, error_msg)
+ lambda { Class.new(1) }.should raise_error(TypeError, error_msg)
+ lambda { Class.new(:symbol) }.should raise_error(TypeError, error_msg)
+ lambda { Class.new(mock('o')) }.should raise_error(TypeError, error_msg)
+ lambda { Class.new(Module.new) }.should raise_error(TypeError, error_msg)
+ end
+end
+
+describe "Class#new" do
+ it "returns a new instance of self" do
+ klass = Class.new
+ klass.new.is_a?(klass).should == true
+ end
+
+ it "invokes #initialize on the new instance with the given args" do
+ klass = Class.new do
+ def initialize(*args)
+ @initialized = true
+ @args = args
+ end
+
+ def args
+ @args
+ end
+
+ def initialized?
+ @initialized || false
+ end
+ end
+
+ klass.new.initialized?.should == true
+ klass.new(1, 2, 3).args.should == [1, 2, 3]
+ end
+
+ it "uses the internal allocator and does not call #allocate" do
+ klass = Class.new do
+ def self.allocate
+ raise "allocate should not be called"
+ end
+ end
+
+ instance = klass.new
+ instance.should be_kind_of klass
+ instance.class.should equal klass
+ end
+
+ it "passes the block to #initialize" do
+ klass = Class.new do
+ def initialize
+ yield
+ end
+ end
+
+ klass.new { break 42 }.should == 42
+ end
+end
diff --git a/spec/ruby/core/class/superclass_spec.rb b/spec/ruby/core/class/superclass_spec.rb
new file mode 100644
index 0000000000..deb5a45336
--- /dev/null
+++ b/spec/ruby/core/class/superclass_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Class#superclass" do
+ it "returns the superclass of self" do
+ BasicObject.superclass.should be_nil
+ Object.superclass.should == BasicObject
+ Class.superclass.should == Module
+ Class.new.superclass.should == Object
+ Class.new(String).superclass.should == String
+ Class.new(Fixnum).superclass.should == Fixnum
+ end
+
+ # redmine:567
+ describe "for a singleton class" do
+ it "of an object returns the class of the object" do
+ a = CoreClassSpecs::A.new
+ sc = class << a; self; end
+ sc.superclass.should == CoreClassSpecs::A
+ end
+
+ it "of a class returns the singleton class of its superclass" do # sorry, can't find a simpler way to express this...
+ sc = class << CoreClassSpecs::H; self; end
+ sc.superclass.should == class << CoreClassSpecs::A; self; end
+ end
+ end
+end
diff --git a/spec/ruby/core/class/to_s_spec.rb b/spec/ruby/core/class/to_s_spec.rb
new file mode 100644
index 0000000000..2055593a03
--- /dev/null
+++ b/spec/ruby/core/class/to_s_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Class#to_s" do
+ it 'regular class returns same name as Module#to_s' do
+ String.to_s.should == 'String'
+ end
+
+ describe 'singleton class' do
+ it 'for modules includes module name' do
+ CoreClassSpecs.singleton_class.to_s.should == '#<Class:CoreClassSpecs>'
+ end
+
+ it 'for classes includes class name' do
+ CoreClassSpecs::Record.singleton_class.to_s.should == '#<Class:CoreClassSpecs::Record>'
+ end
+
+ it 'for objects includes class name and object ID' do
+ obj = CoreClassSpecs::Record.new
+ obj.singleton_class.to_s.should =~ /#<Class:#<CoreClassSpecs::Record:0x[0-9a-f]+>>/
+ end
+ end
+end
diff --git a/spec/ruby/core/comparable/between_spec.rb b/spec/ruby/core/comparable/between_spec.rb
new file mode 100644
index 0000000000..fd79bb9b4c
--- /dev/null
+++ b/spec/ruby/core/comparable/between_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Comparable#between?" do
+ it "returns true if self is greater than or equal to the first and less than or equal to the second argument" do
+ a = ComparableSpecs::Weird.new(-1)
+ b = ComparableSpecs::Weird.new(0)
+ c = ComparableSpecs::Weird.new(1)
+ d = ComparableSpecs::Weird.new(2)
+
+ a.between?(a, a).should == true
+ a.between?(a, b).should == true
+ a.between?(a, c).should == true
+ a.between?(a, d).should == true
+ c.between?(c, d).should == true
+ d.between?(d, d).should == true
+ c.between?(a, d).should == true
+
+ a.between?(b, b).should == false
+ a.between?(b, c).should == false
+ a.between?(b, d).should == false
+ c.between?(a, a).should == false
+ c.between?(a, b).should == false
+ end
+end
diff --git a/spec/ruby/core/comparable/clamp_spec.rb b/spec/ruby/core/comparable/clamp_spec.rb
new file mode 100644
index 0000000000..eb6a0838b9
--- /dev/null
+++ b/spec/ruby/core/comparable/clamp_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is '2.4' do
+ describe 'Comparable#clamp' do
+ it 'raises an Argument error unless given 2 parameters' do
+ c = ComparableSpecs::Weird.new(0)
+ lambda { c.clamp(c) }.should raise_error(ArgumentError)
+ lambda { c.clamp(c, c, c) }.should raise_error(ArgumentError)
+ end
+
+ it 'raises an Argument error unless the 2 parameters are correctly ordered' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ two = ComparableSpecs::WithOnlyCompareDefined.new(2)
+ c = ComparableSpecs::Weird.new(3)
+
+ lambda { c.clamp(two, one) }.should raise_error(ArgumentError)
+ one.should_receive(:<=>).any_number_of_times.and_return(nil)
+ lambda { c.clamp(one, two) }.should raise_error(ArgumentError)
+ end
+
+ it 'returns self if within the given parameters' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ two = ComparableSpecs::WithOnlyCompareDefined.new(2)
+ three = ComparableSpecs::WithOnlyCompareDefined.new(3)
+ c = ComparableSpecs::Weird.new(2)
+
+ c.clamp(one, two).should equal(c)
+ c.clamp(two, two).should equal(c)
+ c.clamp(one, three).should equal(c)
+ c.clamp(two, three).should equal(c)
+ end
+
+ it 'returns the min parameter if smaller than it' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ two = ComparableSpecs::WithOnlyCompareDefined.new(2)
+ c = ComparableSpecs::Weird.new(0)
+
+ c.clamp(one, two).should equal(one)
+ end
+
+ it 'returns the max parameter if greater than it' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ two = ComparableSpecs::WithOnlyCompareDefined.new(2)
+ c = ComparableSpecs::Weird.new(3)
+
+ c.clamp(one, two).should equal(two)
+ end
+ end
+end
diff --git a/spec/ruby/core/comparable/equal_value_spec.rb b/spec/ruby/core/comparable/equal_value_spec.rb
new file mode 100644
index 0000000000..53c83aa2d3
--- /dev/null
+++ b/spec/ruby/core/comparable/equal_value_spec.rb
@@ -0,0 +1,114 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Comparable#==" do
+ a = b = nil
+ before :each do
+ a = ComparableSpecs::Weird.new(0)
+ b = ComparableSpecs::Weird.new(10)
+ end
+
+ it "returns true if other is the same as self" do
+ (a == a).should == true
+ (b == b).should == true
+ end
+
+ it "calls #<=> on self with other and returns true if #<=> returns 0" do
+ a.should_receive(:<=>).once.and_return(0)
+ (a == b).should == true
+ end
+
+ it "calls #<=> on self with other and returns true if #<=> returns 0.0" do
+ a.should_receive(:<=>).once.and_return(0.0)
+ (a == b).should == true
+ end
+
+ it "returns false if calling #<=> on self returns a positive Integer" do
+ a.should_receive(:<=>).once.and_return(1)
+ (a == b).should == false
+ end
+
+ it "returns false if calling #<=> on self returns a negative Integer" do
+ a.should_receive(:<=>).once.and_return(-1)
+ (a == b).should == false
+ end
+
+ context "when #<=> returns nil" do
+ before :each do
+ a.should_receive(:<=>).once.and_return(nil)
+ end
+
+ it "returns false" do
+ (a == b).should be_false
+ end
+ end
+
+ context "when #<=> returns nor nil neither an Integer" do
+ before :each do
+ a.should_receive(:<=>).once.and_return("abc")
+ end
+
+ it "raises an ArgumentError" do
+ lambda { (a == b) }.should raise_error(ArgumentError)
+ end
+ end
+
+ context "when #<=> raises an exception" do
+ context "if it is a StandardError" do
+ before :each do
+ a.should_receive(:<=>).once.and_raise(StandardError)
+ end
+
+ it "lets it go through" do
+ lambda { (a == b) }.should raise_error(StandardError)
+ end
+ end
+
+ context "if it is a subclass of StandardError" do
+ # TypeError < StandardError
+ before :each do
+ a.should_receive(:<=>).once.and_raise(TypeError)
+ end
+
+ it "lets it go through" do
+ lambda { (a == b) }.should raise_error(TypeError)
+ end
+ end
+
+ it "lets it go through if it is not a StandardError" do
+ a.should_receive(:<=>).once.and_raise(Exception)
+ lambda { (a == b) }.should raise_error(Exception)
+ end
+ end
+
+ context "when #<=> is not defined" do
+ before :each do
+ @a = ComparableSpecs::WithoutCompareDefined.new
+ @b = ComparableSpecs::WithoutCompareDefined.new
+ end
+
+ it "returns true for identical objects" do
+ @a.should == @a
+ end
+
+ it "returns false and does not recurse infinitely" do
+ @a.should_not == @b
+ end
+ end
+
+ context "when #<=> calls super" do
+ before :each do
+ @a = ComparableSpecs::CompareCallingSuper.new
+ @b = ComparableSpecs::CompareCallingSuper.new
+ end
+
+ it "returns true for identical objects" do
+ @a.should == @a
+ end
+
+ it "calls the defined #<=> only once for different objects" do
+ @a.should_not == @b
+ @a.calls.should == 1
+ end
+ end
+end
diff --git a/spec/ruby/core/comparable/fixtures/classes.rb b/spec/ruby/core/comparable/fixtures/classes.rb
new file mode 100644
index 0000000000..4239a47d2f
--- /dev/null
+++ b/spec/ruby/core/comparable/fixtures/classes.rb
@@ -0,0 +1,36 @@
+module ComparableSpecs
+ class WithOnlyCompareDefined
+ attr_reader :value
+
+ def initialize(value)
+ @value = value
+ end
+
+ def <=>(other)
+ self.value <=> other.value
+ end
+ end
+
+ class Weird < WithOnlyCompareDefined
+ include Comparable
+ end
+
+ class WithoutCompareDefined
+ include Comparable
+ end
+
+ class CompareCallingSuper
+ include Comparable
+
+ attr_reader :calls
+
+ def initialize
+ @calls = 0
+ end
+
+ def <=>(other)
+ @calls += 1
+ super(other)
+ end
+ end
+end
diff --git a/spec/ruby/core/comparable/gt_spec.rb b/spec/ruby/core/comparable/gt_spec.rb
new file mode 100644
index 0000000000..6dd661bbfa
--- /dev/null
+++ b/spec/ruby/core/comparable/gt_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Comparable#>" do
+ it "calls #<=> on self with other and returns true if #<=> returns any Integer greater than 0" do
+ a = ComparableSpecs::Weird.new(0)
+ b = ComparableSpecs::Weird.new(20)
+
+ a.should_receive(:<=>).any_number_of_times.and_return(1)
+ (a > b).should == true
+
+ a.should_receive(:<=>).any_number_of_times.and_return(0.1)
+ (a > b).should == true
+
+ a.should_receive(:<=>).any_number_of_times.and_return(10000000)
+ (a > b).should == true
+ end
+
+ it "returns false if calling #<=> on self returns 0 or any Integer less than 0" do
+ a = ComparableSpecs::Weird.new(0)
+ b = ComparableSpecs::Weird.new(10)
+
+ a.should_receive(:<=>).any_number_of_times.and_return(0)
+ (a > b).should == false
+
+ a.should_receive(:<=>).any_number_of_times.and_return(0.0)
+ (a > b).should == false
+
+ a.should_receive(:<=>).any_number_of_times.and_return(-1.0)
+ (a > b).should == false
+
+ a.should_receive(:<=>).any_number_of_times.and_return(-10000000)
+ (a > b).should == false
+ end
+
+ it "raises an ArgumentError if calling #<=> on self returns nil" do
+ a = ComparableSpecs::Weird.new(0)
+ b = ComparableSpecs::Weird.new(20)
+
+ a.should_receive(:<=>).any_number_of_times.and_return(nil)
+ lambda { (a > b) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/comparable/gte_spec.rb b/spec/ruby/core/comparable/gte_spec.rb
new file mode 100644
index 0000000000..cdaeb60843
--- /dev/null
+++ b/spec/ruby/core/comparable/gte_spec.rb
@@ -0,0 +1,47 @@
+require_relative 'fixtures/classes'
+require_relative '../../spec_helper'
+
+describe "Comparable#>=" do
+ it "calls #<=> on self with other and returns true if #<=> returns 0 or any Integer greater than 0" do
+ a = ComparableSpecs::Weird.new(0)
+ b = ComparableSpecs::Weird.new(20)
+
+ a.should_receive(:<=>).any_number_of_times.and_return(0)
+ (a >= b).should == true
+
+ a.should_receive(:<=>).any_number_of_times.and_return(0.0)
+ (a >= b).should == true
+
+ a.should_receive(:<=>).any_number_of_times.and_return(1)
+ (a >= b).should == true
+
+ a.should_receive(:<=>).any_number_of_times.and_return(0.1)
+ (a >= b).should == true
+
+ a.should_receive(:<=>).any_number_of_times.and_return(10000000)
+ (a >= b).should == true
+ end
+
+ it "returns false if calling #<=> on self returns any Integer less than 0" do
+ a = ComparableSpecs::Weird.new(0)
+ b = ComparableSpecs::Weird.new(10)
+
+
+ a.should_receive(:<=>).any_number_of_times.and_return(-0.1)
+ (a >= b).should == false
+
+ a.should_receive(:<=>).any_number_of_times.and_return(-1.0)
+ (a >= b).should == false
+
+ a.should_receive(:<=>).any_number_of_times.and_return(-10000000)
+ (a >= b).should == false
+ end
+
+ it "raises an ArgumentError if calling #<=> on self returns nil" do
+ a = ComparableSpecs::Weird.new(0)
+ b = ComparableSpecs::Weird.new(20)
+
+ a.should_receive(:<=>).any_number_of_times.and_return(nil)
+ lambda { (a >= b) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/comparable/lt_spec.rb b/spec/ruby/core/comparable/lt_spec.rb
new file mode 100644
index 0000000000..02f77dba50
--- /dev/null
+++ b/spec/ruby/core/comparable/lt_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Comparable#<" do
+ it "calls #<=> on self with other and returns true if #<=> returns any Integer less than 0" do
+ a = ComparableSpecs::Weird.new(0)
+ b = ComparableSpecs::Weird.new(20)
+
+ a.should_receive(:<=>).any_number_of_times.and_return(-1)
+ (a < b).should == true
+
+ a.should_receive(:<=>).any_number_of_times.and_return(-0.1)
+ (a < b).should == true
+
+ a.should_receive(:<=>).any_number_of_times.and_return(-10000000)
+ (a < b).should == true
+ end
+
+ it "returns false if calling #<=> on self returns 0 or any Integer greater than 0" do
+ a = ComparableSpecs::Weird.new(0)
+ b = ComparableSpecs::Weird.new(10)
+
+ a.should_receive(:<=>).any_number_of_times.and_return(0)
+ (a < b).should == false
+
+ a.should_receive(:<=>).any_number_of_times.and_return(0.0)
+ (a < b).should == false
+
+ a.should_receive(:<=>).any_number_of_times.and_return(1.0)
+ (a < b).should == false
+
+ a.should_receive(:<=>).any_number_of_times.and_return(10000000)
+ (a < b).should == false
+ end
+
+ it "raises an ArgumentError if calling #<=> on self returns nil" do
+ a = ComparableSpecs::Weird.new(0)
+ b = ComparableSpecs::Weird.new(20)
+
+ a.should_receive(:<=>).any_number_of_times.and_return(nil)
+ lambda { (a < b) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/comparable/lte_spec.rb b/spec/ruby/core/comparable/lte_spec.rb
new file mode 100644
index 0000000000..baf2c57229
--- /dev/null
+++ b/spec/ruby/core/comparable/lte_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Comparable#<=" do
+ it "calls #<=> on self with other and returns true if #<=> returns 0 or any Integer less than 0" do
+ a = ComparableSpecs::Weird.new(0)
+ b = ComparableSpecs::Weird.new(20)
+
+ a.should_receive(:<=>).any_number_of_times.and_return(0)
+ (a <= b).should == true
+
+ a.should_receive(:<=>).any_number_of_times.and_return(0.0)
+ (a <= b).should == true
+
+ a.should_receive(:<=>).any_number_of_times.and_return(-1)
+ (a <= b).should == true
+
+ a.should_receive(:<=>).any_number_of_times.and_return(-0.1)
+ (a <= b).should == true
+
+ a.should_receive(:<=>).any_number_of_times.and_return(-10000000)
+ (a <= b).should == true
+ end
+
+ it "returns false if calling #<=> on self returns any Integer greater than 0" do
+ a = ComparableSpecs::Weird.new(0)
+ b = ComparableSpecs::Weird.new(10)
+
+ a.should_receive(:<=>).any_number_of_times.and_return(0.1)
+ (a <= b).should == false
+
+ a.should_receive(:<=>).any_number_of_times.and_return(1.0)
+ (a <= b).should == false
+
+ a.should_receive(:<=>).any_number_of_times.and_return(10000000)
+ (a <= b).should == false
+ end
+
+ it "raises an ArgumentError if calling #<=> on self returns nil" do
+ a = ComparableSpecs::Weird.new(0)
+ b = ComparableSpecs::Weird.new(20)
+
+ a.should_receive(:<=>).any_number_of_times.and_return(nil)
+ lambda { (a <= b) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/complex/abs2_spec.rb b/spec/ruby/core/complex/abs2_spec.rb
new file mode 100644
index 0000000000..3e5c5fd225
--- /dev/null
+++ b/spec/ruby/core/complex/abs2_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Complex#abs2" do
+ it "returns the sum of the squares of the real and imaginary parts" do
+ Complex(1, -2).abs2.should == 1 + 4
+ Complex(-0.1, 0.2).abs2.should be_close(0.01 + 0.04, TOLERANCE)
+ Complex(0).abs2.should == 0
+ end
+end
diff --git a/spec/ruby/core/complex/abs_spec.rb b/spec/ruby/core/complex/abs_spec.rb
new file mode 100644
index 0000000000..43912c517f
--- /dev/null
+++ b/spec/ruby/core/complex/abs_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/abs'
+
+describe "Complex#abs" do
+ it_behaves_like :complex_abs, :abs
+end
diff --git a/spec/ruby/core/complex/angle_spec.rb b/spec/ruby/core/complex/angle_spec.rb
new file mode 100644
index 0000000000..4aa176956f
--- /dev/null
+++ b/spec/ruby/core/complex/angle_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arg'
+
+describe "Complex#angle" do
+ it_behaves_like :complex_arg, :angle
+end
diff --git a/spec/ruby/core/complex/arg_spec.rb b/spec/ruby/core/complex/arg_spec.rb
new file mode 100644
index 0000000000..009f19429f
--- /dev/null
+++ b/spec/ruby/core/complex/arg_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arg'
+
+describe "Complex#arg" do
+ it_behaves_like :complex_arg, :arg
+end
diff --git a/spec/ruby/core/complex/coerce_spec.rb b/spec/ruby/core/complex/coerce_spec.rb
new file mode 100644
index 0000000000..ce2fb36b73
--- /dev/null
+++ b/spec/ruby/core/complex/coerce_spec.rb
@@ -0,0 +1,70 @@
+require_relative '../../spec_helper'
+
+describe "Complex#coerce" do
+ before :each do
+ @one = Complex(1)
+ end
+
+ it "returns an array containing other and self as Complex when other is an Integer" do
+ result = @one.coerce(2)
+ result.should == [2, 1]
+ result.first.should be_kind_of(Complex)
+ result.last.should be_kind_of(Complex)
+ end
+
+ it "returns an array containing other and self as Complex when other is a Float" do
+ result = @one.coerce(20.5)
+ result.should == [20.5, 1]
+ result.first.should be_kind_of(Complex)
+ result.last.should be_kind_of(Complex)
+ end
+
+ it "returns an array containing other and self as Complex when other is a Bignum" do
+ result = @one.coerce(4294967296)
+ result.should == [4294967296, 1]
+ result.first.should be_kind_of(Complex)
+ result.last.should be_kind_of(Complex)
+ end
+
+ it "returns an array containing other and self as Complex when other is a Rational" do
+ result = @one.coerce(Rational(5,6))
+ result.should == [Rational(5,6), 1]
+ result.first.should be_kind_of(Complex)
+ result.last.should be_kind_of(Complex)
+ end
+
+ it "returns an array containing other and self when other is a Complex" do
+ other = Complex(2)
+ result = @one.coerce(other)
+ result.should == [other, @one]
+ result.first.should equal(other)
+ result.last.should equal(@one)
+ end
+
+ it "returns an array containing other as Complex and self when other is a Numeric which responds to #real? with true" do
+ other = mock_numeric('other')
+ other.should_receive(:real?).any_number_of_times.and_return(true)
+ result = @one.coerce(other)
+ result.should == [other, @one]
+ result.first.should eql(Complex(other))
+ result.last.should equal(@one)
+ end
+
+ it "raises TypeError when other is a Numeric which responds to #real? with false" do
+ other = mock_numeric('other')
+ other.should_receive(:real?).any_number_of_times.and_return(false)
+ lambda { @one.coerce(other) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when other is a String" do
+ lambda { @one.coerce("20") }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when other is nil" do
+ lambda { @one.coerce(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when other is false" do
+ lambda { @one.coerce(false) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/complex/conj_spec.rb b/spec/ruby/core/complex/conj_spec.rb
new file mode 100644
index 0000000000..5e3bc1acb8
--- /dev/null
+++ b/spec/ruby/core/complex/conj_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/conjugate'
+
+describe "Complex#conj" do
+ it_behaves_like :complex_conjugate, :conj
+end
diff --git a/spec/ruby/core/complex/conjugate_spec.rb b/spec/ruby/core/complex/conjugate_spec.rb
new file mode 100644
index 0000000000..f658bab4da
--- /dev/null
+++ b/spec/ruby/core/complex/conjugate_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/conjugate'
+
+describe "Complex#conjugate" do
+ it_behaves_like :complex_conjugate, :conjugate
+end
diff --git a/spec/ruby/core/complex/constants_spec.rb b/spec/ruby/core/complex/constants_spec.rb
new file mode 100644
index 0000000000..50303de16c
--- /dev/null
+++ b/spec/ruby/core/complex/constants_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Complex::I" do
+ it "is Complex(0, 1)" do
+ Complex::I.should eql(Complex(0, 1))
+ end
+end
diff --git a/spec/ruby/core/complex/denominator_spec.rb b/spec/ruby/core/complex/denominator_spec.rb
new file mode 100644
index 0000000000..c1a2003820
--- /dev/null
+++ b/spec/ruby/core/complex/denominator_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+describe "Complex#denominator" do
+ it "returns the least common multiple denominator of the real and imaginary parts" do
+ Complex(3, 4).denominator.should == 1
+ Complex(3, bignum_value).denominator.should == 1
+
+ Complex(3, Rational(3,4)).denominator.should == 4
+
+ Complex(Rational(4,8), Rational(3,4)).denominator.should == 4
+ Complex(Rational(3,8), Rational(3,4)).denominator.should == 8
+ end
+end
diff --git a/spec/ruby/core/complex/divide_spec.rb b/spec/ruby/core/complex/divide_spec.rb
new file mode 100644
index 0000000000..bebf862312
--- /dev/null
+++ b/spec/ruby/core/complex/divide_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/divide'
+
+describe "Complex#/" do
+ it_behaves_like :complex_divide, :/
+end
diff --git a/spec/ruby/core/complex/eql_spec.rb b/spec/ruby/core/complex/eql_spec.rb
new file mode 100644
index 0000000000..9194efc074
--- /dev/null
+++ b/spec/ruby/core/complex/eql_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+
+describe "Complex#eql?" do
+ it "returns false if other is not Complex" do
+ Complex(1).eql?(1).should be_false
+ end
+
+ it "returns true when the respective parts are of the same classes and self == other" do
+ Complex(1, 2).eql?(Complex(1, 2)).should be_true
+ end
+
+ it "returns false when the real parts are of different classes" do
+ Complex(1).eql?(Complex(1.0)).should be_false
+ end
+
+ it "returns false when the imaginary parts are of different classes" do
+ Complex(1, 2).eql?(Complex(1, 2.0)).should be_false
+ end
+
+ it "returns false when self == other is false" do
+ Complex(1, 2).eql?(Complex(2, 3)).should be_false
+ end
+
+ it "does NOT send #eql? to real or imaginary parts" do
+ real = mock_numeric('real')
+ imag = mock_numeric('imag')
+ real.should_not_receive(:eql?)
+ imag.should_not_receive(:eql?)
+ Complex(real, imag).eql?(Complex(real, imag)).should be_true
+ end
+end
diff --git a/spec/ruby/core/complex/equal_value_spec.rb b/spec/ruby/core/complex/equal_value_spec.rb
new file mode 100644
index 0000000000..b1e4f9cfcd
--- /dev/null
+++ b/spec/ruby/core/complex/equal_value_spec.rb
@@ -0,0 +1,93 @@
+require_relative '../../spec_helper'
+
+describe "Complex#==" do
+ describe "with Complex" do
+ it "returns true when self and other have numerical equality" do
+ Complex(1, 2).should == Complex(1, 2)
+ Complex(3, 9).should == Complex(3, 9)
+ Complex(-3, -9).should == Complex(-3, -9)
+
+ Complex(1, 2).should_not == Complex(3, 4)
+ Complex(3, 9).should_not == Complex(9, 3)
+
+ Complex(1.0, 2.0).should == Complex(1, 2)
+ Complex(3.0, 9.0).should_not == Complex(9.0, 3.0)
+
+ Complex(1.5, 2.5).should == Complex(1.5, 2.5)
+ Complex(1.5, 2.5).should == Complex(1.5, 2.5)
+ Complex(-1.5, 2.5).should == Complex(-1.5, 2.5)
+
+ Complex(1.5, 2.5).should_not == Complex(2.5, 1.5)
+ Complex(3.75, 2.5).should_not == Complex(1.5, 2.5)
+
+ Complex(bignum_value, 2.5).should == Complex(bignum_value, 2.5)
+ Complex(3.75, bignum_value).should_not == Complex(1.5, bignum_value)
+
+ Complex(nan_value).should_not == Complex(nan_value)
+ end
+ end
+
+ describe "with Numeric" do
+ it "returns true when self's imaginary part is 0 and the real part and other have numerical equality" do
+ Complex(3, 0).should == 3
+ Complex(-3, 0).should == -3
+
+ Complex(3.5, 0).should == 3.5
+ Complex(-3.5, 0).should == -3.5
+
+ Complex(bignum_value, 0).should == bignum_value
+ Complex(-bignum_value, 0).should == -bignum_value
+
+ Complex(3.0, 0).should == 3
+ Complex(-3.0, 0).should == -3
+
+ Complex(3, 0).should_not == 4
+ Complex(-3, 0).should_not == -4
+
+ Complex(3.5, 0).should_not == -4.5
+ Complex(-3.5, 0).should_not == 2.5
+
+ Complex(bignum_value, 0).should_not == bignum_value(10)
+ Complex(-bignum_value, 0).should_not == -bignum_value(20)
+ end
+ end
+
+ describe "with Object" do
+ # Fixnum#==, Float#== and Bignum#== only return booleans - Bug?
+ it "calls other#== with self" do
+ value = Complex(3, 0)
+
+ obj = mock("Object")
+ obj.should_receive(:==).with(value).and_return(:expected)
+
+ (value == obj).should_not be_false
+ end
+ end
+
+ describe "with a Numeric which responds to #real? with true" do
+ before do
+ @other = mock_numeric('other')
+ @other.should_receive(:real?).any_number_of_times.and_return(true)
+ end
+
+ it "returns real == other when the imaginary part is zero" do
+ real = mock_numeric('real')
+ real.should_receive(:==).with(@other).and_return(true)
+ (Complex(real, 0) == @other).should be_true
+ end
+
+ it "returns false when when the imaginary part is not zero" do
+ (Complex(3, 1) == @other).should be_false
+ end
+ end
+
+ describe "with a Numeric which responds to #real? with false" do
+ it "returns other == self" do
+ complex = Complex(3, 0)
+ other = mock_numeric('other')
+ other.should_receive(:real?).any_number_of_times.and_return(false)
+ other.should_receive(:==).with(complex).and_return(true)
+ (complex == other).should be_true
+ end
+ end
+end
diff --git a/spec/ruby/core/complex/exponent_spec.rb b/spec/ruby/core/complex/exponent_spec.rb
new file mode 100644
index 0000000000..bf07c90038
--- /dev/null
+++ b/spec/ruby/core/complex/exponent_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../spec_helper'
+
+describe "Complex#**" do
+ describe "with Fixnum 0" do
+ it "returns Complex(1)" do
+ (Complex(3, 4) ** 0).should eql(Complex(1))
+ end
+ end
+
+ describe "with Float 0.0" do
+ it "returns Complex(1.0, 0.0)" do
+ (Complex(3, 4) ** 0.0).should eql(Complex(1.0, 0.0))
+ end
+ end
+
+ describe "with Complex" do
+ it "returns self raised to the given power" do
+ (Complex(2, 1) ** Complex(2, 1)).should be_close(Complex(-0.504824688978319, 3.10414407699553), TOLERANCE)
+ (Complex(2, 1) ** Complex(3, 4)).should be_close(Complex(-0.179174656916581, -1.74071656397662), TOLERANCE)
+
+ (Complex(2, 1) ** Complex(-2, -1)).should be_close(Complex(-0.051041070450869, -0.313849223270419), TOLERANCE)
+ (Complex(-2, -1) ** Complex(2, 1)).should be_close(Complex(-11.6819929610857, 71.8320439736158), TOLERANCE)
+ end
+ end
+
+ describe "with Integer" do
+ it "returns self raised to the given power" do
+ (Complex(2, 1) ** 2).should == Complex(3, 4)
+ (Complex(3, 4) ** 2).should == Complex(-7, 24)
+ (Complex(3, 4) ** -2).should be_close(Complex(-0.0112, -0.0384), TOLERANCE)
+
+
+ (Complex(2, 1) ** 2.5).should be_close(Complex(2.99179707178602, 6.85206901006896), TOLERANCE)
+ (Complex(3, 4) ** 2.5).should be_close(Complex(-38.0, 41.0), TOLERANCE)
+ (Complex(3, 4) ** -2.5).should be_close(Complex(-0.01216, -0.01312), TOLERANCE)
+
+ (Complex(1) ** 1).should == Complex(1)
+
+ # NOTE: Takes way too long...
+ #(Complex(2, 1) ** bignum_value)
+ end
+ end
+
+ describe "with Rational" do
+ it "returns self raised to the given power" do
+ (Complex(2, 1) ** Rational(3, 4)).should be_close(Complex(1.71913265276568, 0.623124744394697), TOLERANCE)
+ (Complex(2, 1) ** Rational(4, 3)).should be_close(Complex(2.3828547125173, 1.69466313833091), TOLERANCE)
+ (Complex(2, 1) ** Rational(-4, 3)).should be_close(Complex(0.278700377879388, -0.198209003071003), TOLERANCE)
+ end
+ end
+
+ describe "with Object" do
+ it "tries to coerce self into other" do
+ value = Complex(3, 9)
+
+ obj = mock("Object")
+ obj.should_receive(:coerce).with(value).and_return([2, 5])
+ (value ** obj).should == 2 ** 5
+ end
+ end
+end
diff --git a/spec/ruby/core/complex/fdiv_spec.rb b/spec/ruby/core/complex/fdiv_spec.rb
new file mode 100644
index 0000000000..20d2f41354
--- /dev/null
+++ b/spec/ruby/core/complex/fdiv_spec.rb
@@ -0,0 +1,129 @@
+require_relative '../../spec_helper'
+
+describe "Complex#fdiv" do
+ it "accepts a numeric argument" do
+ lambda { Complex(20).fdiv(2) }.should_not raise_error(TypeError)
+ lambda { Complex(20).fdiv(2.0) }.should_not raise_error(TypeError)
+ lambda { Complex(20).fdiv(bignum_value) }.should_not raise_error(TypeError)
+ end
+
+ it "accepts a negative numeric argument" do
+ lambda { Complex(20).fdiv(-2) }.should_not raise_error(TypeError)
+ lambda { Complex(20).fdiv(-2.0) }.should_not raise_error(TypeError)
+ lambda { Complex(20).fdiv(-bignum_value) }.should_not raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed a non-numeric argument" do
+ lambda { Complex(20).fdiv([]) }.should raise_error(TypeError)
+ lambda { Complex(20).fdiv(:sym) }.should raise_error(TypeError)
+ lambda { Complex(20).fdiv('s') }.should raise_error(TypeError)
+ end
+
+ it "sets the real part to NaN if self's real part is NaN" do
+ Complex(nan_value).fdiv(2).real.nan?.should be_true
+ end
+
+ it "sets the imaginary part to NaN if self's imaginary part is NaN" do
+ Complex(2, nan_value).fdiv(2).imag.nan?.should be_true
+ end
+
+ it "sets the real and imaginary part to NaN if self's real and imaginary parts are NaN" do
+ Complex(nan_value, nan_value).fdiv(2).imag.nan?.should be_true
+ Complex(nan_value, nan_value).fdiv(2).real.nan?.should be_true
+ end
+
+ it "sets the real and imaginary part to NaN if self's real part and the argument are both NaN" do
+ Complex(nan_value, 2).fdiv(nan_value).imag.nan?.should be_true
+ Complex(nan_value, 2).fdiv(nan_value).real.nan?.should be_true
+ end
+
+ it "sets the real and imaginary part to NaN if self's real part, self's imaginary part, and the argument are NaN" do
+ Complex(nan_value, nan_value).fdiv(nan_value).imag.nan?.should be_true
+ Complex(nan_value, nan_value).fdiv(nan_value).real.nan?.should be_true
+ end
+
+ it "sets the real part to Infinity if self's real part is Infinity" do
+ Complex(infinity_value).fdiv(2).real.infinite?.should == 1
+ Complex(infinity_value,2).fdiv(2).real.infinite?.should == 1
+ end
+
+ it "sets the imaginary part to Infinity if self's imaginary part is Infinity" do
+ Complex(2, infinity_value).fdiv(2).imag.infinite?.should == 1
+ Complex(2, infinity_value).fdiv(2).imag.infinite?.should == 1
+ end
+
+ it "sets the imaginary and real part to Infinity if self's imaginary and real parts are Infinity" do
+ Complex(infinity_value, infinity_value).fdiv(2).real.infinite?.should == 1
+ Complex(infinity_value, infinity_value).fdiv(2).imag.infinite?.should == 1
+ end
+
+ it "sets the real part to NaN and the imaginary part to NaN if self's imaginary part, self's real part, and the argument are Infinity" do
+ Complex(infinity_value, infinity_value).fdiv(infinity_value).real.nan?.should be_true
+ Complex(infinity_value, infinity_value).fdiv(infinity_value).imag.nan?.should be_true
+ end
+end
+
+describe "Complex#fdiv with no imaginary part" do
+ before :each do
+ @numbers = [1, 5.43, 10, bignum_value, 99872.2918710].map{|n| [n,-n]}.flatten
+ end
+
+ it "returns a Complex number" do
+ @numbers.each do |real|
+ @numbers.each do |other|
+ Complex(real).fdiv(other).should be_an_instance_of(Complex)
+ end
+ end
+ end
+
+ it "sets the real part to self's real part fdiv'd with the argument" do
+ @numbers.each do |real|
+ @numbers.each do |other|
+ Complex(real).fdiv(other).real.should == real.fdiv(other)
+ end
+ end
+ end
+
+ it "sets the imaginary part to 0.0" do
+ @numbers.each do |real|
+ @numbers.each do |other|
+ Complex(real).fdiv(other).imaginary.should == 0.0
+ end
+ end
+ end
+end
+
+describe "Complex#fdiv with an imaginary part" do
+ before :each do
+ @numbers = [1, 5.43, 10, bignum_value, 99872.2918710].map{|n| [n,-n]}.flatten
+ end
+
+ it "returns a Complex number" do
+ @numbers.each do |real|
+ @numbers.each_with_index do |other,idx|
+ Complex(
+ real,@numbers[idx == 0 ? -1 : idx-1]
+ ).fdiv(other).should be_an_instance_of(Complex)
+ end
+ end
+ end
+
+ it "sets the real part to self's real part fdiv'd with the argument" do
+ @numbers.each do |real|
+ @numbers.each_with_index do |other,idx|
+ Complex(
+ real,@numbers[idx == 0 ? -1 : idx-1]
+ ).fdiv(other).real.should == real.fdiv(other)
+ end
+ end
+ end
+
+ it "sets the imaginary part to the imaginary part fdiv'd with the argument" do
+ @numbers.each do |real|
+ @numbers.each_with_index do |other,idx|
+ im = @numbers[idx == 0 ? -1 : idx-1]
+ Complex(real, im).fdiv(other).imag.should == im.fdiv(other)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/complex/finite_spec.rb b/spec/ruby/core/complex/finite_spec.rb
new file mode 100644
index 0000000000..de4ba78246
--- /dev/null
+++ b/spec/ruby/core/complex/finite_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.4" do
+ describe "Complex#finite?" do
+ it "returns true if magnitude is finite" do
+ (1+1i).finite?.should == true
+ end
+
+ it "returns false for positive infinity" do
+ value = Complex(Float::INFINITY, 42)
+ value.finite?.should == false
+ end
+
+ it "returns false for positive complex with infinite imaginary" do
+ value = Complex(1, Float::INFINITY)
+ value.finite?.should == false
+ end
+
+ it "returns false for negative infinity" do
+ value = -Complex(Float::INFINITY, 42)
+ value.finite?.should == false
+ end
+
+ it "returns false for negative complex with infinite imaginary" do
+ value = -Complex(1, Float::INFINITY)
+ value.finite?.should == false
+ end
+
+ ruby_bug "#14014", "2.4"..."2.5" do
+ it "returns false for NaN" do
+ value = Complex(Float::NAN, Float::NAN)
+ value.finite?.should == false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/complex/hash_spec.rb b/spec/ruby/core/complex/hash_spec.rb
new file mode 100644
index 0000000000..cad283309d
--- /dev/null
+++ b/spec/ruby/core/complex/hash_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+
+describe "Complex#hash" do
+ it "is static" do
+ Complex(1).hash.should == Complex(1).hash
+ Complex(1, 0).hash.should == Complex(1).hash
+ Complex(1, 1).hash.should == Complex(1, 1).hash
+ end
+
+ it "is different for different instances" do
+ Complex(1, 2).hash.should_not == Complex(1, 1).hash
+ Complex(2, 1).hash.should_not == Complex(1, 1).hash
+
+ Complex(1, 2).hash.should_not == Complex(2, 1).hash
+ end
+end
diff --git a/spec/ruby/core/complex/imag_spec.rb b/spec/ruby/core/complex/imag_spec.rb
new file mode 100644
index 0000000000..2bafd1ab54
--- /dev/null
+++ b/spec/ruby/core/complex/imag_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/image'
+
+describe "Complex#imag" do
+ it_behaves_like :complex_image, :imag
+end
diff --git a/spec/ruby/core/complex/imaginary_spec.rb b/spec/ruby/core/complex/imaginary_spec.rb
new file mode 100644
index 0000000000..a8a1bfea90
--- /dev/null
+++ b/spec/ruby/core/complex/imaginary_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/image'
+
+describe "Complex#imaginary" do
+ it_behaves_like :complex_image, :imaginary
+end
diff --git a/spec/ruby/core/complex/infinite_spec.rb b/spec/ruby/core/complex/infinite_spec.rb
new file mode 100644
index 0000000000..27aa038cd2
--- /dev/null
+++ b/spec/ruby/core/complex/infinite_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.4" do
+ describe "Complex#infinite?" do
+ it "returns nil if magnitude is finite" do
+ (1+1i).infinite?.should == nil
+ end
+
+ it "returns 1 for positive infinity" do
+ value = Complex(Float::INFINITY, 42).infinite?
+ value.should == 1
+ end
+
+ it "returns 1 for positive complex with infinite imaginary" do
+ value = Complex(1, Float::INFINITY).infinite?
+ value.should == 1
+ end
+
+ it "returns -1 for negative infinity" do
+ value = -Complex(Float::INFINITY, 42).infinite?
+ value.should == -1
+ end
+
+ it "returns -1 for negative complex with infinite imaginary" do
+ value = -Complex(1, Float::INFINITY).infinite?
+ value.should == -1
+ end
+
+ it "returns nil for NaN" do
+ value = Complex(0, Float::NAN).infinite?
+ value.should == nil
+ end
+ end
+end
diff --git a/spec/ruby/core/complex/inspect_spec.rb b/spec/ruby/core/complex/inspect_spec.rb
new file mode 100644
index 0000000000..71aabde5be
--- /dev/null
+++ b/spec/ruby/core/complex/inspect_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+
+describe "Complex#inspect" do
+ it "returns (${real}+${image}i) for positive imaginary parts" do
+ Complex(1).inspect.should == "(1+0i)"
+ Complex(7).inspect.should == "(7+0i)"
+ Complex(-1, 4).inspect.should == "(-1+4i)"
+ Complex(-7, 6.7).inspect.should == "(-7+6.7i)"
+ end
+
+ it "returns (${real}-${image}i) for negative imaginary parts" do
+ Complex(0, -1).inspect.should == "(0-1i)"
+ Complex(-1, -4).inspect.should == "(-1-4i)"
+ Complex(-7, -6.7).inspect.should == "(-7-6.7i)"
+ end
+end
diff --git a/spec/ruby/core/complex/integer_spec.rb b/spec/ruby/core/complex/integer_spec.rb
new file mode 100644
index 0000000000..0957accb70
--- /dev/null
+++ b/spec/ruby/core/complex/integer_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Complex#integer?" do
+ it "returns false for a Complex with no imaginary part" do
+ Complex(20).integer?.should be_false
+ end
+
+ it "returns false for a Complex with an imaginary part" do
+ Complex(20,3).integer?.should be_false
+ end
+end
diff --git a/spec/ruby/core/complex/magnitude_spec.rb b/spec/ruby/core/complex/magnitude_spec.rb
new file mode 100644
index 0000000000..86f3b29868
--- /dev/null
+++ b/spec/ruby/core/complex/magnitude_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/abs'
+
+describe "Complex#magnitude" do
+ it_behaves_like :complex_abs, :magnitude
+end
diff --git a/spec/ruby/core/complex/marshal_dump_spec.rb b/spec/ruby/core/complex/marshal_dump_spec.rb
new file mode 100644
index 0000000000..116899b0ad
--- /dev/null
+++ b/spec/ruby/core/complex/marshal_dump_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Complex#marshal_dump" do
+ it "is a private method" do
+ Complex.should have_private_instance_method(:marshal_dump, false)
+ end
+
+ it "dumps real and imaginary parts" do
+ Complex(1, 2).send(:marshal_dump).should == [1, 2]
+ end
+end
diff --git a/spec/ruby/core/complex/minus_spec.rb b/spec/ruby/core/complex/minus_spec.rb
new file mode 100644
index 0000000000..7c104ce784
--- /dev/null
+++ b/spec/ruby/core/complex/minus_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../../spec_helper'
+
+describe "Complex#-" do
+ describe "with Complex" do
+ it "subtracts both the real and imaginary components" do
+ (Complex(1, 2) - Complex(10, 20)).should == Complex(1 - 10, 2 - 20)
+ (Complex(1.5, 2.1) - Complex(100.2, -30.3)).should == Complex(1.5 - 100.2, 2.1 - (-30.3))
+ end
+ end
+
+ describe "with Integer" do
+ it "subtracts the real number from the real component of self" do
+ (Complex(1, 2) - 50).should == Complex(-49, 2)
+ (Complex(1, 2) - 50.5).should == Complex(-49.5, 2)
+ end
+ end
+
+ describe "with Object" do
+ it "tries to coerce self into other" do
+ value = Complex(3, 9)
+
+ obj = mock("Object")
+ obj.should_receive(:coerce).with(value).and_return([2, 5])
+ (value - obj).should == 2 - 5
+ end
+ end
+
+ describe "passed Numeric which responds to #real? with true" do
+ it "coerces the passed argument to the type of the real part and subtracts the resulting elements" do
+ n = mock_numeric('n')
+ n.should_receive(:real?).and_return(true)
+ n.should_receive(:coerce).with(1).and_return([1, 4])
+ (Complex(1, 2) - n).should == Complex(-3, 2)
+ end
+ end
+
+ describe "passed Numeric which responds to #real? with false" do
+ it "coerces the passed argument to Complex and subtracts the resulting elements" do
+ n = mock_numeric('n')
+ n.should_receive(:real?).and_return(false)
+ n.should_receive(:coerce).with(Complex(1, 2)).and_return([Complex(1, 2), Complex(3, 4)])
+ (Complex(1, 2) - n).should == Complex(-2, -2)
+ end
+ end
+end
diff --git a/spec/ruby/core/complex/multiply_spec.rb b/spec/ruby/core/complex/multiply_spec.rb
new file mode 100644
index 0000000000..35bf7c8455
--- /dev/null
+++ b/spec/ruby/core/complex/multiply_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+
+describe "Complex#*" do
+ describe "with Complex" do
+ it "multiplies according to the usual rule for complex numbers: (a + bi) * (c + di) = ac - bd + (ad + bc)i" do
+ (Complex(1, 2) * Complex(10, 20)).should == Complex((1 * 10) - (2 * 20), (1 * 20) + (2 * 10))
+ (Complex(1.5, 2.1) * Complex(100.2, -30.3)).should == Complex((1.5 * 100.2) - (2.1 * -30.3), (1.5 * -30.3) + (2.1 * 100.2))
+ end
+ end
+
+ describe "with Integer" do
+ it "multiplies both parts of self by the given Integer" do
+ (Complex(3, 2) * 50).should == Complex(150, 100)
+ (Complex(-3, 2) * 50.5).should == Complex(-151.5, 101)
+ end
+ end
+
+ describe "with Object" do
+ it "tries to coerce self into other" do
+ value = Complex(3, 9)
+
+ obj = mock("Object")
+ obj.should_receive(:coerce).with(value).and_return([2, 5])
+ (value * obj).should == 2 * 5
+ end
+ end
+
+ describe "with a Numeric which responds to #real? with true" do
+ it "multiples both parts of self by other" do
+ other = mock_numeric('other')
+ real = mock_numeric('real')
+ imag = mock_numeric('imag')
+ other.should_receive(:real?).and_return(true)
+ real.should_receive(:*).with(other).and_return(1)
+ imag.should_receive(:*).with(other).and_return(2)
+ (Complex(real, imag) * other).should == Complex(1, 2)
+ end
+
+ describe "with a Numeric which responds to #real? with false" do
+ it "coerces the passed argument to Complex and multiplies the resulting elements" do
+ complex = Complex(3, 0)
+ other = mock_numeric('other')
+ other.should_receive(:real?).any_number_of_times.and_return(false)
+ other.should_receive(:coerce).with(complex).and_return([5, 2])
+ (complex * other).should == 10
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/complex/negative_spec.rb b/spec/ruby/core/complex/negative_spec.rb
new file mode 100644
index 0000000000..1ea5392f39
--- /dev/null
+++ b/spec/ruby/core/complex/negative_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+describe "Complex#negative?" do
+ it "is undefined" do
+ c = Complex(1)
+
+ c.methods.should_not include(:negative?)
+
+ lambda {
+ c.negative?
+ }.should raise_error(NoMethodError)
+ end
+end
diff --git a/spec/ruby/core/complex/numerator_spec.rb b/spec/ruby/core/complex/numerator_spec.rb
new file mode 100644
index 0000000000..7ab66e6a61
--- /dev/null
+++ b/spec/ruby/core/complex/numerator_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+describe "Complex#numerator" do
+ it "returns self's numerator" do
+ Complex(2).numerator.should == Complex(2)
+ Complex(3, 4).numerator.should == Complex(3, 4)
+
+ Complex(Rational(3, 4), Rational(3, 4)).numerator.should == Complex(3, 3)
+ Complex(Rational(7, 4), Rational(8, 4)).numerator.should == Complex(7, 8)
+
+ Complex(Rational(7, 8), Rational(8, 4)).numerator.should == Complex(7, 16)
+ Complex(Rational(7, 4), Rational(8, 8)).numerator.should == Complex(7, 4)
+
+ # NOTE:
+ # Bug? - Fails with a MethodMissingError
+ # (undefined method `denominator' for 3.5:Float)
+ # Complex(3.5, 3.7).numerator
+ end
+end
diff --git a/spec/ruby/core/complex/phase_spec.rb b/spec/ruby/core/complex/phase_spec.rb
new file mode 100644
index 0000000000..89574bf533
--- /dev/null
+++ b/spec/ruby/core/complex/phase_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arg'
+
+describe "Complex#phase" do
+ it_behaves_like :complex_arg, :phase
+end
diff --git a/spec/ruby/core/complex/plus_spec.rb b/spec/ruby/core/complex/plus_spec.rb
new file mode 100644
index 0000000000..2056ca786c
--- /dev/null
+++ b/spec/ruby/core/complex/plus_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../../spec_helper'
+
+describe "Complex#+" do
+ describe "with Complex" do
+ it "adds both the real and imaginary components" do
+ (Complex(1, 2) + Complex(10, 20)).should == Complex(1 + 10, 2 + 20)
+ (Complex(1.5, 2.1) + Complex(100.2, -30.3)).should == Complex(1.5 + 100.2, 2.1 + (-30.3))
+ end
+ end
+
+ describe "with Integer" do
+ it "adds the real number to the real component of self" do
+ (Complex(1, 2) + 50).should == Complex(51, 2)
+ (Complex(1, 2) + 50.5).should == Complex(51.5, 2)
+ end
+ end
+
+ describe "with Object" do
+ it "tries to coerce self into other" do
+ value = Complex(3, 9)
+
+ obj = mock("Object")
+ obj.should_receive(:coerce).with(value).and_return([2, 5])
+ (value + obj).should == 2 + 5
+ end
+ end
+
+ describe "passed Numeric which responds to #real? with true" do
+ it "coerces the passed argument to the type of the real part and adds the resulting elements" do
+ n = mock_numeric('n')
+ n.should_receive(:real?).and_return(true)
+ n.should_receive(:coerce).with(1).and_return([1, 4])
+ (Complex(1, 2) + n).should == Complex(5, 2)
+ end
+ end
+
+ describe "passed Numeric which responds to #real? with false" do
+ it "coerces the passed argument to Complex and adds the resulting elements" do
+ n = mock_numeric('n')
+ n.should_receive(:real?).and_return(false)
+ n.should_receive(:coerce).with(Complex(1, 2)).and_return([Complex(1, 2), Complex(3, 4)])
+ (Complex(1, 2) + n).should == Complex(4, 6)
+ end
+ end
+end
diff --git a/spec/ruby/core/complex/polar_spec.rb b/spec/ruby/core/complex/polar_spec.rb
new file mode 100644
index 0000000000..f9d28b8388
--- /dev/null
+++ b/spec/ruby/core/complex/polar_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+
+describe "Complex.polar" do
+ it "returns a complex number in terms of radius and angle" do
+ Complex.polar(50, 60).should be_close(Complex(-47.6206490207578, -15.2405310551108), TOLERANCE)
+ Complex.polar(-10, -20).should be_close(Complex(-4.08082061813392, 9.12945250727628), TOLERANCE)
+ end
+
+ it "raises a TypeError when given non real arguments" do
+ lambda{ Complex.polar(nil) }.should raise_error(TypeError)
+ lambda{ Complex.polar(nil, nil) }.should raise_error(TypeError)
+ end
+end
+
+describe "Complex#polar" do
+ it "returns the absolute value and the argument" do
+ a = Complex(3, 4)
+ a.polar.size.should == 2
+ a.polar.first.should == 5.0
+ a.polar.last.should be_close(0.927295218001612, TOLERANCE)
+
+ b = Complex(-3.5, 4.7)
+ b.polar.size.should == 2
+ b.polar.first.should be_close(5.86003412959345, TOLERANCE)
+ b.polar.last.should be_close(2.21088447955664, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/complex/positive_spec.rb b/spec/ruby/core/complex/positive_spec.rb
new file mode 100644
index 0000000000..eea0af3755
--- /dev/null
+++ b/spec/ruby/core/complex/positive_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+describe "Complex#positive?" do
+ it "is undefined" do
+ c = Complex(1)
+
+ c.methods.should_not include(:positive?)
+
+ lambda {
+ c.positive?
+ }.should raise_error(NoMethodError)
+ end
+end
diff --git a/spec/ruby/core/complex/quo_spec.rb b/spec/ruby/core/complex/quo_spec.rb
new file mode 100644
index 0000000000..ee6fd65c79
--- /dev/null
+++ b/spec/ruby/core/complex/quo_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/divide'
+
+describe "Complex#quo" do
+ it_behaves_like :complex_divide, :quo
+end
diff --git a/spec/ruby/core/complex/rationalize_spec.rb b/spec/ruby/core/complex/rationalize_spec.rb
new file mode 100644
index 0000000000..165f3d2700
--- /dev/null
+++ b/spec/ruby/core/complex/rationalize_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+
+describe "Complex#rationalize" do
+ it "raises RangeError if self has non-zero imaginary part" do
+ lambda { Complex(1,5).rationalize }.should raise_error(RangeError)
+ end
+
+ it "raises RangeError if self has 0.0 imaginary part" do
+ lambda { Complex(1,0.0).rationalize }.should raise_error(RangeError)
+ end
+
+ it "returns a Rational if self has zero imaginary part" do
+ Complex(1,0).rationalize.should == Rational(1,1)
+ Complex(2<<63+5).rationalize.should == Rational(2<<63+5,1)
+ end
+
+ it "sends #rationalize to the real part" do
+ real = mock_numeric('real')
+ real.should_receive(:rationalize).with(0.1).and_return(:result)
+ Complex(real, 0).rationalize(0.1).should == :result
+ end
+
+ it "ignores a single argument" do
+ Complex(1,0).rationalize(0.1).should == Rational(1,1)
+ end
+
+ it "raises ArgumentError when passed more than one argument" do
+ lambda { Complex(1,0).rationalize(0.1, 0.1) }.should raise_error(ArgumentError)
+ lambda { Complex(1,0).rationalize(0.1, 0.1, 2) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/complex/real_spec.rb b/spec/ruby/core/complex/real_spec.rb
new file mode 100644
index 0000000000..2ea791c005
--- /dev/null
+++ b/spec/ruby/core/complex/real_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+
+describe "Complex#real" do
+ it "returns the real part of self" do
+ Complex(1, 0).real.should == 1
+ Complex(2, 1).real.should == 2
+ Complex(6.7, 8.9).real.should == 6.7
+ Complex(bignum_value, 3).real.should == bignum_value
+ end
+end
+
+describe "Complex#real?" do
+ it "returns false if there is an imaginary part" do
+ Complex(2,3).real?.should be_false
+ end
+
+ it "returns false if there is not an imaginary part" do
+ Complex(2).real?.should be_false
+ end
+
+ it "returns false if the real part is Infinity" do
+ Complex(infinity_value).real?.should be_false
+ end
+
+ it "returns false if the real part is NaN" do
+ Complex(nan_value).real?.should be_false
+ end
+end
diff --git a/spec/ruby/core/complex/rect_spec.rb b/spec/ruby/core/complex/rect_spec.rb
new file mode 100644
index 0000000000..9e95f3efc2
--- /dev/null
+++ b/spec/ruby/core/complex/rect_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'shared/rect'
+
+describe "Complex#rect" do
+ it_behaves_like :complex_rect, :rect
+end
+
+describe "Complex.rect" do
+ it_behaves_like :complex_rect_class, :rect
+end
diff --git a/spec/ruby/core/complex/rectangular_spec.rb b/spec/ruby/core/complex/rectangular_spec.rb
new file mode 100644
index 0000000000..d4b8ad9782
--- /dev/null
+++ b/spec/ruby/core/complex/rectangular_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'shared/rect'
+
+describe "Complex#rectangular" do
+ it_behaves_like :complex_rect, :rectangular
+end
+
+describe "Complex.rectangular" do
+ it_behaves_like :complex_rect_class, :rectangular
+end
diff --git a/spec/ruby/core/complex/shared/abs.rb b/spec/ruby/core/complex/shared/abs.rb
new file mode 100644
index 0000000000..2299479341
--- /dev/null
+++ b/spec/ruby/core/complex/shared/abs.rb
@@ -0,0 +1,10 @@
+describe :complex_abs, shared: true do
+ it "returns the modulus: |a + bi| = sqrt((a ^ 2) + (b ^ 2))" do
+ Complex(0, 0).send(@method).should == 0
+ Complex(3, 4).send(@method).should == 5 # well-known integer case
+ Complex(-3, 4).send(@method).should == 5
+ Complex(1, -1).send(@method).should be_close(Math.sqrt(2), TOLERANCE)
+ Complex(6.5, 0).send(@method).should be_close(6.5, TOLERANCE)
+ Complex(0, -7.2).send(@method).should be_close(7.2, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/complex/shared/arg.rb b/spec/ruby/core/complex/shared/arg.rb
new file mode 100644
index 0000000000..c81f197433
--- /dev/null
+++ b/spec/ruby/core/complex/shared/arg.rb
@@ -0,0 +1,9 @@
+describe :complex_arg, shared: true do
+ it "returns the argument -- i.e., the angle from (1, 0) in the complex plane" do
+ two_pi = 2 * Math::PI
+ (Complex(1, 0).send(@method) % two_pi).should be_close(0, TOLERANCE)
+ (Complex(0, 2).send(@method) % two_pi).should be_close(Math::PI * 0.5, TOLERANCE)
+ (Complex(-100, 0).send(@method) % two_pi).should be_close(Math::PI, TOLERANCE)
+ (Complex(0, -75.3).send(@method) % two_pi).should be_close(Math::PI * 1.5, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/complex/shared/conjugate.rb b/spec/ruby/core/complex/shared/conjugate.rb
new file mode 100644
index 0000000000..d1ae47bcb6
--- /dev/null
+++ b/spec/ruby/core/complex/shared/conjugate.rb
@@ -0,0 +1,8 @@
+describe :complex_conjugate, shared: true do
+ it "returns the complex conjugate: conj a + bi = a - bi" do
+ Complex(3, 5).send(@method).should == Complex(3, -5)
+ Complex(3, -5).send(@method).should == Complex(3, 5)
+ Complex(-3.0, 5.2).send(@method).should be_close(Complex(-3.0, -5.2), TOLERANCE)
+ Complex(3.0, -5.2).send(@method).should be_close(Complex(3.0, 5.2), TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/complex/shared/divide.rb b/spec/ruby/core/complex/shared/divide.rb
new file mode 100644
index 0000000000..e3701405b9
--- /dev/null
+++ b/spec/ruby/core/complex/shared/divide.rb
@@ -0,0 +1,82 @@
+describe :complex_divide, shared: true do
+ describe "with Complex" do
+ it "divides according to the usual rule for complex numbers" do
+ a = Complex((1 * 10) - (2 * 20), (1 * 20) + (2 * 10))
+ b = Complex(1, 2)
+ a.send(@method, b).should == Complex(10, 20)
+
+ c = Complex((1.5 * 100.2) - (2.1 * -30.3), (1.5 * -30.3) + (2.1 * 100.2))
+ d = Complex(1.5, 2.1)
+ # remember the floating-point arithmetic
+ c.send(@method, d).should be_close(Complex(100.2, -30.3), TOLERANCE)
+ end
+ end
+
+ describe "with Fixnum" do
+ it "divides both parts of the Complex number" do
+ Complex(20, 40).send(@method, 2).should == Complex(10, 20)
+ Complex(30, 30).send(@method, 10).should == Complex(3, 3)
+ end
+
+ it "raises a ZeroDivisionError when given zero" do
+ lambda { Complex(20, 40).send(@method, 0) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "produces Rational parts" do
+ Complex(5, 9).send(@method, 2).should eql(Complex(Rational(5,2), Rational(9,2)))
+ end
+ end
+
+ describe "with Bignum" do
+ it "divides both parts of the Complex number" do
+ Complex(20, 40).send(@method, 2).should == Complex(10, 20)
+ Complex(15, 16).send(@method, 2.0).should be_close(Complex(7.5, 8), TOLERANCE)
+ end
+ end
+
+ describe "with Float" do
+ it "divides both parts of the Complex number" do
+ Complex(3, 9).send(@method, 1.5).should == Complex(2, 6)
+ Complex(15, 16).send(@method, 2.0).should be_close(Complex(7.5, 8), TOLERANCE)
+ end
+
+ it "returns Complex(Infinity, Infinity) when given zero" do
+ Complex(20, 40).send(@method, 0.0).real.infinite?.should == 1
+ Complex(20, 40).send(@method, 0.0).imag.infinite?.should == 1
+ Complex(-20, 40).send(@method, 0.0).real.infinite?.should == -1
+ Complex(-20, 40).send(@method, 0.0).imag.infinite?.should == 1
+ end
+ end
+
+ describe "with Object" do
+ it "tries to coerce self into other" do
+ value = Complex(3, 9)
+
+ obj = mock("Object")
+ obj.should_receive(:coerce).with(value).and_return([4, 2])
+ value.send(@method, obj).should == 2
+ end
+ end
+
+ describe "with a Numeric which responds to #real? with true" do
+ it "returns Complex(real.quo(other), imag.quo(other))" do
+ other = mock_numeric('other')
+ real = mock_numeric('real')
+ imag = mock_numeric('imag')
+ other.should_receive(:real?).and_return(true)
+ real.should_receive(:quo).with(other).and_return(1)
+ imag.should_receive(:quo).with(other).and_return(2)
+ Complex(real, imag).send(@method, other).should == Complex(1, 2)
+ end
+ end
+
+ describe "with a Numeric which responds to #real? with false" do
+ it "coerces the passed argument to Complex and divides the resulting elements" do
+ complex = Complex(3, 0)
+ other = mock_numeric('other')
+ other.should_receive(:real?).any_number_of_times.and_return(false)
+ other.should_receive(:coerce).with(complex).and_return([5, 2])
+ complex.send(@method, other).should eql(Rational(5, 2))
+ end
+ end
+end
diff --git a/spec/ruby/core/complex/shared/image.rb b/spec/ruby/core/complex/shared/image.rb
new file mode 100644
index 0000000000..f839dbcaf9
--- /dev/null
+++ b/spec/ruby/core/complex/shared/image.rb
@@ -0,0 +1,8 @@
+describe :complex_image, shared: true do
+ it "returns the imaginary part of self" do
+ Complex(1, 0).send(@method).should == 0
+ Complex(2, 1).send(@method).should == 1
+ Complex(6.7, 8.9).send(@method).should == 8.9
+ Complex(1, bignum_value).send(@method).should == bignum_value
+ end
+end
diff --git a/spec/ruby/core/complex/shared/rect.rb b/spec/ruby/core/complex/shared/rect.rb
new file mode 100644
index 0000000000..971821ac33
--- /dev/null
+++ b/spec/ruby/core/complex/shared/rect.rb
@@ -0,0 +1,94 @@
+describe :complex_rect, shared: true do
+ before :each do
+ @numbers = [
+ Complex(1),
+ Complex(0, 20),
+ Complex(0, 0),
+ Complex(0.0),
+ Complex(9999999**99),
+ Complex(-20),
+ Complex.polar(76, 10)
+ ]
+ end
+
+ it "returns an Array" do
+ @numbers.each do |number|
+ number.send(@method).should be_an_instance_of(Array)
+ end
+ end
+
+ it "returns a two-element Array" do
+ @numbers.each do |number|
+ number.send(@method).size.should == 2
+ end
+ end
+
+ it "returns the real part of self as the first element" do
+ @numbers.each do |number|
+ number.send(@method).first.should == number.real
+ end
+ end
+
+ it "returns the imaginary part of self as the last element" do
+ @numbers.each do |number|
+ number.send(@method).last.should == number.imaginary
+ end
+ end
+
+ it "raises an ArgumentError if given any arguments" do
+ @numbers.each do |number|
+ lambda { number.send(@method, number) }.should raise_error(ArgumentError)
+ end
+ end
+end
+
+describe :complex_rect_class, shared: true do
+ describe "passed a Numeric n which responds to #real? with true" do
+ it "returns a Complex with real part n and imaginary part 0" do
+ n = mock_numeric('n')
+ n.should_receive(:real?).any_number_of_times.and_return(true)
+ result = Complex.send(@method, n)
+ result.real.should == n
+ result.imag.should == 0
+ end
+ end
+
+ describe "passed a Numeric which responds to #real? with false" do
+ it "raises TypeError" do
+ n = mock_numeric('n')
+ n.should_receive(:real?).any_number_of_times.and_return(false)
+ lambda { Complex.send(@method, n) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "passed Numerics n1 and n2 and at least one responds to #real? with false" do
+ [[false, false], [false, true], [true, false]].each do |r1, r2|
+ it "raises TypeError" do
+ n1 = mock_numeric('n1')
+ n2 = mock_numeric('n2')
+ n1.should_receive(:real?).any_number_of_times.and_return(r1)
+ n2.should_receive(:real?).any_number_of_times.and_return(r2)
+ lambda { Complex.send(@method, n1, n2) }.should raise_error(TypeError)
+ end
+ end
+ end
+
+ describe "passed Numerics n1 and n2 and both respond to #real? with true" do
+ it "returns a Complex with real part n1 and imaginary part n2" do
+ n1 = mock_numeric('n1')
+ n2 = mock_numeric('n2')
+ n1.should_receive(:real?).any_number_of_times.and_return(true)
+ n2.should_receive(:real?).any_number_of_times.and_return(true)
+ result = Complex.send(@method, n1, n2)
+ result.real.should == n1
+ result.imag.should == n2
+ end
+ end
+
+ describe "passed a non-Numeric" do
+ it "raises TypeError" do
+ lambda { Complex.send(@method, :sym) }.should raise_error(TypeError)
+ lambda { Complex.send(@method, 0, :sym) }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/complex/to_f_spec.rb b/spec/ruby/core/complex/to_f_spec.rb
new file mode 100644
index 0000000000..12c34dfbb1
--- /dev/null
+++ b/spec/ruby/core/complex/to_f_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+
+describe "Complex#to_f" do
+ describe "when the imaginary part is Fixnum 0" do
+ it "returns the result of sending #to_f to the real part" do
+ real = mock_numeric('real')
+ real.should_receive(:to_f).and_return(:f)
+ Complex(real, 0).to_f.should == :f
+ end
+ end
+
+ describe "when the imaginary part is Rational 0" do
+ it "returns the result of sending #to_f to the real part" do
+ real = mock_numeric('real')
+ real.should_receive(:to_f).and_return(:f)
+ Complex(real, Rational(0)).to_f.should == :f
+ end
+ end
+
+ describe "when the imaginary part responds to #== 0 with true" do
+ it "returns the result of sending #to_f to the real part" do
+ real = mock_numeric('real')
+ real.should_receive(:to_f).and_return(:f)
+ imag = mock_numeric('imag')
+ imag.should_receive(:==).with(0).any_number_of_times.and_return(true)
+ Complex(real, imag).to_f.should == :f
+ end
+ end
+
+ describe "when the imaginary part is non-zero" do
+ it "raises RangeError" do
+ lambda { Complex(0, 1).to_f }.should raise_error(RangeError)
+ end
+ end
+
+ describe "when the imaginary part is Float 0.0" do
+ it "raises RangeError" do
+ lambda { Complex(0, 0.0).to_f }.should raise_error(RangeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/complex/to_i_spec.rb b/spec/ruby/core/complex/to_i_spec.rb
new file mode 100644
index 0000000000..acdf719376
--- /dev/null
+++ b/spec/ruby/core/complex/to_i_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+
+describe "Complex#to_i" do
+ describe "when the imaginary part is Fixnum 0" do
+ it "returns the result of sending #to_i to the real part" do
+ real = mock_numeric('real')
+ real.should_receive(:to_i).and_return(:i)
+ Complex(real, 0).to_i.should == :i
+ end
+ end
+
+ describe "when the imaginary part is Rational 0" do
+ it "returns the result of sending #to_i to the real part" do
+ real = mock_numeric('real')
+ real.should_receive(:to_i).and_return(:i)
+ Complex(real, Rational(0)).to_i.should == :i
+ end
+ end
+
+ describe "when the imaginary part responds to #== 0 with true" do
+ it "returns the result of sending #to_i to the real part" do
+ real = mock_numeric('real')
+ real.should_receive(:to_i).and_return(:i)
+ imag = mock_numeric('imag')
+ imag.should_receive(:==).with(0).any_number_of_times.and_return(true)
+ Complex(real, imag).to_i.should == :i
+ end
+ end
+
+ describe "when the imaginary part is non-zero" do
+ it "raises RangeError" do
+ lambda { Complex(0, 1).to_i }.should raise_error(RangeError)
+ end
+ end
+
+ describe "when the imaginary part is Float 0.0" do
+ it "raises RangeError" do
+ lambda { Complex(0, 0.0).to_i }.should raise_error(RangeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/complex/to_r_spec.rb b/spec/ruby/core/complex/to_r_spec.rb
new file mode 100644
index 0000000000..46bac98ef4
--- /dev/null
+++ b/spec/ruby/core/complex/to_r_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+
+describe "Complex#to_r" do
+ describe "when the imaginary part is Fixnum 0" do
+ it "returns the result of sending #to_r to the real part" do
+ real = mock_numeric('real')
+ real.should_receive(:to_r).and_return(:r)
+ Complex(real, 0).to_r.should == :r
+ end
+ end
+
+ describe "when the imaginary part is Rational 0" do
+ it "returns the result of sending #to_r to the real part" do
+ real = mock_numeric('real')
+ real.should_receive(:to_r).and_return(:r)
+ Complex(real, Rational(0)).to_r.should == :r
+ end
+ end
+
+ describe "when the imaginary part responds to #== 0 with true" do
+ it "returns the result of sending #to_r to the real part" do
+ real = mock_numeric('real')
+ real.should_receive(:to_r).and_return(:r)
+ imag = mock_numeric('imag')
+ imag.should_receive(:==).with(0).any_number_of_times.and_return(true)
+ Complex(real, imag).to_r.should == :r
+ end
+ end
+
+ describe "when the imaginary part is non-zero" do
+ it "raises RangeError" do
+ lambda { Complex(0, 1).to_r }.should raise_error(RangeError)
+ end
+ end
+
+ describe "when the imaginary part is Float 0.0" do
+ it "raises RangeError" do
+ lambda { Complex(0, 0.0).to_r }.should raise_error(RangeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/complex/to_s_spec.rb b/spec/ruby/core/complex/to_s_spec.rb
new file mode 100644
index 0000000000..989a7ae0b7
--- /dev/null
+++ b/spec/ruby/core/complex/to_s_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+
+describe "Complex#to_s" do
+ describe "when self's real component is 0" do
+ it "returns both the real and imaginary component even when the real is 0" do
+ Complex(0, 5).to_s.should == "0+5i"
+ Complex(0, -3.2).to_s.should == "0-3.2i"
+ end
+ end
+
+ it "returns self as String" do
+ Complex(1, 5).to_s.should == "1+5i"
+ Complex(-2.5, 1.5).to_s.should == "-2.5+1.5i"
+
+ Complex(1, -5).to_s.should == "1-5i"
+ Complex(-2.5, -1.5).to_s.should == "-2.5-1.5i"
+
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ Complex(1, 0).to_s.should == "1+0i"
+ Complex(1, -0).to_s.should == "1+0i"
+ end
+ end
+
+ it "returns 1+0.0i for Complex(1, 0.0)" do
+ Complex(1, 0.0).to_s.should == "1+0.0i"
+ end
+
+ it "returns 1-0.0i for Complex(1, -0.0)" do
+ Complex(1, -0.0).to_s.should == "1-0.0i"
+ end
+
+ it "returns 1+Infinity*i for Complex(1, Infinity)" do
+ Complex(1, infinity_value).to_s.should == "1+Infinity*i"
+ end
+
+ it "returns 1-Infinity*i for Complex(1, -Infinity)" do
+ Complex(1, -infinity_value).to_s.should == "1-Infinity*i"
+ end
+
+ it "returns 1+NaN*i for Complex(1, NaN)" do
+ Complex(1, nan_value).to_s.should == "1+NaN*i"
+ end
+end
diff --git a/spec/ruby/core/complex/uminus_spec.rb b/spec/ruby/core/complex/uminus_spec.rb
new file mode 100644
index 0000000000..c0184e11de
--- /dev/null
+++ b/spec/ruby/core/complex/uminus_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Complex#-@" do
+ it "sends #-@ to the real and imaginary parts and returns a Complex with the resulting respective parts" do
+ real = mock_numeric('real')
+ imag = mock_numeric('imag')
+ real.should_receive(:-@).and_return(-1)
+ imag.should_receive(:-@).and_return(-2)
+ Complex(real, imag).send(:-@).should == Complex(-1, -2)
+ end
+end
diff --git a/spec/ruby/core/dir/chdir_spec.rb b/spec/ruby/core/dir/chdir_spec.rb
new file mode 100644
index 0000000000..a2383e6bf2
--- /dev/null
+++ b/spec/ruby/core/dir/chdir_spec.rb
@@ -0,0 +1,124 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Dir.chdir" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ before :each do
+ @original = Dir.pwd
+ end
+
+ after :each do
+ Dir.chdir(@original)
+ end
+
+ it "defaults to $HOME with no arguments" do
+ if ENV['HOME']
+ Dir.chdir
+ current_dir = Dir.pwd
+
+ Dir.chdir(ENV['HOME'])
+ home = Dir.pwd
+ current_dir.should == home
+ end
+ end
+
+ it "changes to the specified directory" do
+ Dir.chdir DirSpecs.mock_dir
+ Dir.pwd.should == DirSpecs.mock_dir
+ end
+
+ it "returns 0 when successfully changing directory" do
+ Dir.chdir(@original).should == 0
+ end
+
+ it "calls #to_str on the argument if it's not a String" do
+ obj = mock('path')
+ obj.should_receive(:to_str).and_return(Dir.pwd)
+ Dir.chdir(obj)
+ end
+
+ it "calls #to_str on the argument if it's not a String and a block is given" do
+ obj = mock('path')
+ obj.should_receive(:to_str).and_return(Dir.pwd)
+ Dir.chdir(obj) { }
+ end
+
+ it "calls #to_path on the argument if it's not a String" do
+ obj = mock('path')
+ obj.should_receive(:to_path).and_return(Dir.pwd)
+ Dir.chdir(obj)
+ end
+
+ it "prefers #to_path over #to_str" do
+ obj = Class.new do
+ def to_path; Dir.pwd; end
+ def to_str; DirSpecs.mock_dir; end
+ end
+ Dir.chdir(obj.new)
+ Dir.pwd.should == @original
+ end
+
+ it "returns the value of the block when a block is given" do
+ Dir.chdir(@original) { :block_value }.should == :block_value
+ end
+
+ it "defaults to the home directory when given a block but no argument" do
+ # Windows will return a path with forward slashes for ENV["HOME"] so we have
+ # to compare the route representations returned by Dir.chdir.
+ current_dir = ""
+ Dir.chdir { current_dir = Dir.pwd }
+
+ Dir.chdir(ENV['HOME'])
+ home = Dir.pwd
+ current_dir.should == home
+ end
+
+ it "changes to the specified directory for the duration of the block" do
+ ar = Dir.chdir(DirSpecs.mock_dir) { |dir| [dir, Dir.pwd] }
+ ar.should == [DirSpecs.mock_dir, DirSpecs.mock_dir]
+
+ Dir.pwd.should == @original
+ end
+
+ it "raises an Errno::ENOENT if the directory does not exist" do
+ lambda { Dir.chdir DirSpecs.nonexistent }.should raise_error(Errno::ENOENT)
+ lambda { Dir.chdir(DirSpecs.nonexistent) { } }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises an Errno::ENOENT if the original directory no longer exists" do
+ dir1 = tmp('/testdir1')
+ dir2 = tmp('/testdir2')
+ File.exist?(dir1).should == false
+ File.exist?(dir2).should == false
+ Dir.mkdir dir1
+ Dir.mkdir dir2
+ begin
+ lambda {
+ Dir.chdir dir1 do
+ Dir.chdir(dir2) { Dir.unlink dir1 }
+ end
+ }.should raise_error(Errno::ENOENT)
+ ensure
+ Dir.unlink dir1 if File.exist?(dir1)
+ Dir.unlink dir2 if File.exist?(dir2)
+ end
+ end
+
+ it "always returns to the original directory when given a block" do
+ begin
+ Dir.chdir(DirSpecs.mock_dir) do
+ raise StandardError, "something bad happened"
+ end
+ rescue StandardError
+ end
+
+ Dir.pwd.should == @original
+ end
+end
diff --git a/spec/ruby/core/dir/children_spec.rb b/spec/ruby/core/dir/children_spec.rb
new file mode 100644
index 0000000000..4bc64934b3
--- /dev/null
+++ b/spec/ruby/core/dir/children_spec.rb
@@ -0,0 +1,72 @@
+# encoding: utf-8
+
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+ruby_version_is "2.5" do
+ describe "Dir.children" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ before :each do
+ @internal = Encoding.default_internal
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ after :each do
+ Encoding.default_internal = @internal
+ end
+
+ it "returns an Array of filenames in an existing directory including dotfiles" do
+ a = Dir.children(DirSpecs.mock_dir).sort
+
+ a.should == DirSpecs.expected_paths - %w[. ..]
+
+ a = Dir.children("#{DirSpecs.mock_dir}/deeply/nested").sort
+ a.should == %w|.dotfile.ext directory|
+ end
+
+ it "calls #to_path on non-String arguments" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return(DirSpecs.mock_dir)
+ Dir.children(p)
+ end
+
+ it "accepts an options Hash" do
+ a = Dir.children("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").sort
+ a.should == %w|.dotfile.ext directory|
+ end
+
+ it "returns children encoded with the filesystem encoding by default" do
+ # This spec depends on the locale not being US-ASCII because if it is, the
+ # children that are not ascii_only? will be ASCII-8BIT encoded.
+ children = Dir.children(File.join(DirSpecs.mock_dir, 'special')).sort
+ encoding = Encoding.find("filesystem")
+ encoding = Encoding::ASCII_8BIT if encoding == Encoding::US_ASCII
+ platform_is_not :windows do
+ children.should include("ã“ã‚“ã«ã¡ã¯.txt".force_encoding(encoding))
+ end
+ children.first.encoding.should equal(Encoding.find("filesystem"))
+ end
+
+ it "returns children encoded with the specified encoding" do
+ dir = File.join(DirSpecs.mock_dir, 'special')
+ children = Dir.children(dir, encoding: "euc-jp").sort
+ children.first.encoding.should equal(Encoding::EUC_JP)
+ end
+
+ it "returns children transcoded to the default internal encoding" do
+ Encoding.default_internal = Encoding::EUC_KR
+ children = Dir.children(File.join(DirSpecs.mock_dir, 'special')).sort
+ children.first.encoding.should equal(Encoding::EUC_KR)
+ end
+
+ it "raises a SystemCallError if called with a nonexistent diretory" do
+ lambda { Dir.children DirSpecs.nonexistent }.should raise_error(SystemCallError)
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/chroot_spec.rb b/spec/ruby/core/dir/chroot_spec.rb
new file mode 100644
index 0000000000..bce4d6f286
--- /dev/null
+++ b/spec/ruby/core/dir/chroot_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/chroot'
+
+platform_is_not :windows do
+ as_superuser do
+ describe "Dir.chroot as root" do
+ it_behaves_like :dir_chroot_as_root, :chroot
+ end
+ end
+
+ platform_is_not :cygwin do
+ as_user do
+ describe "Dir.chroot as regular user" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it "raises an Errno::EPERM exception if the directory exists" do
+ lambda { Dir.chroot('.') }.should raise_error(Errno::EPERM)
+ end
+
+ it "raises a SystemCallError if the directory doesn't exist" do
+ lambda { Dir.chroot('xgwhwhsjai2222jg') }.should raise_error(SystemCallError)
+ end
+
+ it "calls #to_path on non-String argument" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return('.')
+ lambda { Dir.chroot(p) }.should raise_error(Errno::EPERM)
+ end
+ end
+ end
+ end
+
+ platform_is :cygwin do
+ as_user do
+ describe "Dir.chroot as regular user" do
+ it_behaves_like :dir_chroot_as_root, :chroot
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/close_spec.rb b/spec/ruby/core/dir/close_spec.rb
new file mode 100644
index 0000000000..64d87140ae
--- /dev/null
+++ b/spec/ruby/core/dir/close_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+describe "Dir#close" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it "does not raise an IOError even if the Dir instance is closed" do
+ dir = Dir.open DirSpecs.mock_dir
+ dir.close
+ lambda {
+ dir.close
+ }.should_not raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/dir/delete_spec.rb b/spec/ruby/core/dir/delete_spec.rb
new file mode 100644
index 0000000000..a0020788ca
--- /dev/null
+++ b/spec/ruby/core/dir/delete_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/delete'
+
+describe "Dir.delete" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it_behaves_like :dir_delete, :delete
+end
diff --git a/spec/ruby/core/dir/dir_spec.rb b/spec/ruby/core/dir/dir_spec.rb
new file mode 100644
index 0000000000..7d55ea26d4
--- /dev/null
+++ b/spec/ruby/core/dir/dir_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Dir" do
+ it "includes Enumerable" do
+ Dir.include?(Enumerable).should == true
+ end
+end
diff --git a/spec/ruby/core/dir/each_child_spec.rb b/spec/ruby/core/dir/each_child_spec.rb
new file mode 100644
index 0000000000..913b9cb96f
--- /dev/null
+++ b/spec/ruby/core/dir/each_child_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+ruby_version_is "2.5" do
+ describe "Dir.each_child" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it "yields all names in an existing directory to the provided block" do
+ a, b = [], []
+
+ Dir.each_child(DirSpecs.mock_dir) {|f| a << f}
+ Dir.each_child("#{DirSpecs.mock_dir}/deeply/nested") {|f| b << f}
+
+ a.sort.should == DirSpecs.expected_paths - %w[. ..]
+ b.sort.should == %w|.dotfile.ext directory|
+ end
+
+ it "returns nil when successful" do
+ Dir.each_child(DirSpecs.mock_dir) {|f| f}.should == nil
+ end
+
+ it "calls #to_path on non-String arguments" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return(DirSpecs.mock_dir)
+ Dir.each_child(p).to_a
+ end
+
+ it "raises a SystemCallError if passed a nonexistent directory" do
+ lambda { Dir.each_child(DirSpecs.nonexistent) {} }.should raise_error(SystemCallError)
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ Dir.each_child(DirSpecs.mock_dir).should be_an_instance_of(Enumerator)
+ Dir.each_child(DirSpecs.mock_dir).to_a.sort.should == DirSpecs.expected_paths - %w[. ..]
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ Dir.each_child(DirSpecs.mock_dir).size.should == nil
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/each_spec.rb b/spec/ruby/core/dir/each_spec.rb
new file mode 100644
index 0000000000..8c69a7212b
--- /dev/null
+++ b/spec/ruby/core/dir/each_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/closed'
+
+describe "Dir#each" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ before :each do
+ @dir = Dir.open DirSpecs.mock_dir
+ end
+
+ after :each do
+ @dir.close
+ end
+
+ it "yields each directory entry in succession" do
+ a = []
+ @dir.each {|dir| a << dir}
+
+ a.sort.should == DirSpecs.expected_paths
+ end
+
+ it "returns the directory which remains open" do
+ # an FS does not necessarily impose order
+ ls = Dir.entries(DirSpecs.mock_dir)
+ @dir.each {}.should == @dir
+ @dir.read.should == nil
+ @dir.rewind
+ ls.should include(@dir.read)
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ @dir.each.should be_an_instance_of(Enumerator)
+ @dir.each.to_a.sort.should == DirSpecs.expected_paths
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ @dir.each.size.should == nil
+ end
+ end
+ end
+ end
+end
+
+describe "Dir#each" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it_behaves_like :dir_closed, :each
+end
diff --git a/spec/ruby/core/dir/element_reference_spec.rb b/spec/ruby/core/dir/element_reference_spec.rb
new file mode 100644
index 0000000000..092114bed4
--- /dev/null
+++ b/spec/ruby/core/dir/element_reference_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/glob'
+
+describe "Dir.[]" do
+ it_behaves_like :dir_glob, :[]
+end
+
+describe "Dir.[]" do
+ it_behaves_like :dir_glob_recursive, :[]
+end
+
+describe "Dir.[]" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ @cwd = Dir.pwd
+ Dir.chdir DirSpecs.mock_dir
+ end
+
+ after :all do
+ Dir.chdir @cwd
+ DirSpecs.delete_mock_dirs
+ end
+
+ it "calls #to_path to convert multiple patterns" do
+ pat1 = mock('file_one.ext')
+ pat1.should_receive(:to_path).and_return('file_one.ext')
+ pat2 = mock('file_two.ext')
+ pat2.should_receive(:to_path).and_return('file_two.ext')
+
+ Dir[pat1, pat2].should == %w[file_one.ext file_two.ext]
+ end
+end
diff --git a/spec/ruby/core/dir/empty_spec.rb b/spec/ruby/core/dir/empty_spec.rb
new file mode 100644
index 0000000000..ddb955f816
--- /dev/null
+++ b/spec/ruby/core/dir/empty_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.4" do
+ describe "Dir.empty?" do
+ before :all do
+ @empty_dir = tmp("empty_dir")
+ mkdir_p @empty_dir
+ end
+
+ after :all do
+ rm_r @empty_dir
+ end
+
+ it "returns true for empty directories" do
+ result = Dir.empty? @empty_dir
+ result.should be_true
+ end
+
+ it "returns false for non-empty directories" do
+ result = Dir.empty? __dir__
+ result.should be_false
+ end
+
+ it "returns false for a non-directory" do
+ result = Dir.empty? __FILE__
+ result.should be_false
+ end
+
+ it "raises ENOENT for nonexistent directories" do
+ lambda { Dir.empty? tmp("nonexistent") }.should raise_error(Errno::ENOENT)
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/entries_spec.rb b/spec/ruby/core/dir/entries_spec.rb
new file mode 100644
index 0000000000..42f89a1623
--- /dev/null
+++ b/spec/ruby/core/dir/entries_spec.rb
@@ -0,0 +1,70 @@
+# encoding: utf-8
+
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Dir.entries" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ before :each do
+ @internal = Encoding.default_internal
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ after :each do
+ Encoding.default_internal = @internal
+ end
+
+ it "returns an Array of filenames in an existing directory including dotfiles" do
+ a = Dir.entries(DirSpecs.mock_dir).sort
+
+ a.should == DirSpecs.expected_paths
+
+ a = Dir.entries("#{DirSpecs.mock_dir}/deeply/nested").sort
+ a.should == %w|. .. .dotfile.ext directory|
+ end
+
+ it "calls #to_path on non-String arguments" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return(DirSpecs.mock_dir)
+ Dir.entries(p)
+ end
+
+ it "accepts an options Hash" do
+ a = Dir.entries("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").sort
+ a.should == %w|. .. .dotfile.ext directory|
+ end
+
+ it "returns entries encoded with the filesystem encoding by default" do
+ # This spec depends on the locale not being US-ASCII because if it is, the
+ # entries that are not ascii_only? will be ASCII-8BIT encoded.
+ entries = Dir.entries(File.join(DirSpecs.mock_dir, 'special')).sort
+ encoding = Encoding.find("filesystem")
+ encoding = Encoding::ASCII_8BIT if encoding == Encoding::US_ASCII
+ platform_is_not :windows do
+ entries.should include("ã“ã‚“ã«ã¡ã¯.txt".force_encoding(encoding))
+ end
+ entries.first.encoding.should equal(Encoding.find("filesystem"))
+ end
+
+ it "returns entries encoded with the specified encoding" do
+ dir = File.join(DirSpecs.mock_dir, 'special')
+ entries = Dir.entries(dir, encoding: "euc-jp").sort
+ entries.first.encoding.should equal(Encoding::EUC_JP)
+ end
+
+ it "returns entries transcoded to the default internal encoding" do
+ Encoding.default_internal = Encoding::EUC_KR
+ entries = Dir.entries(File.join(DirSpecs.mock_dir, 'special')).sort
+ entries.first.encoding.should equal(Encoding::EUC_KR)
+ end
+
+ it "raises a SystemCallError if called with a nonexistent diretory" do
+ lambda { Dir.entries DirSpecs.nonexistent }.should raise_error(SystemCallError)
+ end
+end
diff --git a/spec/ruby/core/dir/exist_spec.rb b/spec/ruby/core/dir/exist_spec.rb
new file mode 100644
index 0000000000..43987b0f32
--- /dev/null
+++ b/spec/ruby/core/dir/exist_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/exist'
+
+describe "Dir.exist?" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it_behaves_like :dir_exist, :exist?
+end
diff --git a/spec/ruby/core/dir/exists_spec.rb b/spec/ruby/core/dir/exists_spec.rb
new file mode 100644
index 0000000000..2c6f145db2
--- /dev/null
+++ b/spec/ruby/core/dir/exists_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/exist'
+
+describe "Dir.exists?" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it_behaves_like :dir_exist, :exists?
+end
diff --git a/spec/ruby/core/dir/fileno_spec.rb b/spec/ruby/core/dir/fileno_spec.rb
new file mode 100644
index 0000000000..5a893d2da9
--- /dev/null
+++ b/spec/ruby/core/dir/fileno_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+has_dir_fileno = begin
+ dir = Dir.new('.')
+ dir.fileno
+ true
+rescue NotImplementedError
+ false
+rescue Exception
+ true
+ensure
+ dir.close
+end
+
+describe "Dir#fileno" do
+ before :each do
+ @name = tmp("fileno")
+ mkdir_p @name
+ @dir = Dir.new(@name)
+ end
+
+ after :each do
+ @dir.close
+ rm_r @name
+ end
+
+ if has_dir_fileno
+ it "returns the file descriptor of the dir" do
+ @dir.fileno.should be_kind_of(Fixnum)
+ end
+ else
+ it "raises an error when not implemented on the platform" do
+ lambda { @dir.fileno }.should raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/fixtures/common.rb b/spec/ruby/core/dir/fixtures/common.rb
new file mode 100644
index 0000000000..f6708b04f7
--- /dev/null
+++ b/spec/ruby/core/dir/fixtures/common.rb
@@ -0,0 +1,169 @@
+# encoding: utf-8
+
+module DirSpecs
+ def self.mock_dir(dirs = ['dir_specs_mock'])
+ @mock_dir ||= tmp("")
+ File.join @mock_dir, dirs
+ end
+
+ def self.nonexistent
+ name = File.join mock_dir, "nonexistent00"
+ name = name.next while File.exist? name
+ name
+ end
+
+ # TODO: make these relative to the mock_dir
+ def self.clear_dirs
+ [ 'nonexisting',
+ 'default_perms',
+ 'reduced',
+ 'always_returns_0',
+ '???',
+ [0xe9].pack('U')
+ ].each do |dir|
+ begin
+ Dir.rmdir dir
+ rescue
+ end
+ end
+ end
+
+ # The names of the fixture directories and files used by
+ # various Dir specs.
+ def self.mock_dir_files
+ unless @mock_dir_files
+ @mock_dir_files = %w[
+ .dotfile
+ .dotsubdir/.dotfile
+ .dotsubdir/nondotfile
+
+ deeply/.dotfile
+ deeply/nested/.dotfile.ext
+ deeply/nested/directory/structure/.ext
+ deeply/nested/directory/structure/bar
+ deeply/nested/directory/structure/baz
+ deeply/nested/directory/structure/file_one
+ deeply/nested/directory/structure/file_one.ext
+ deeply/nested/directory/structure/foo
+ deeply/nondotfile
+
+ file_one.ext
+ file_two.ext
+
+ dir_filename_ordering
+ dir/filename_ordering
+
+ nondotfile
+
+ subdir_one/.dotfile
+ subdir_one/nondotfile
+ subdir_two/nondotfile
+ subdir_two/nondotfile.ext
+
+ brace/a
+ brace/a.js
+ brace/a.erb
+ brace/a.js.rjs
+ brace/a.html.erb
+
+ special/+
+
+ special/^
+ special/$
+
+ special/(
+ special/)
+ special/[
+ special/]
+ special/{
+ special/}
+
+ special/test{1}/file[1]
+ ]
+
+ platform_is_not :windows do
+ @mock_dir_files += %w[
+ special/*
+ special/?
+
+ special/|
+
+ special/ã“ã‚“ã«ã¡ã¯.txt
+ ]
+ end
+ end
+
+ @mock_dir_files
+ end
+
+ def self.create_mock_dirs
+ mock_dir_files.each do |name|
+ file = File.join mock_dir, name
+ mkdir_p File.dirname(file)
+ touch file
+ end
+ end
+
+ def self.delete_mock_dirs
+ begin
+ rm_r mock_dir
+ rescue Errno::ENOTEMPTY => e
+ puts Dir["#{mock_dir}/**/*"]
+ raise e
+ end
+ end
+
+ def self.mock_rmdir(*dirs)
+ mock_dir ['rmdir_dirs'].concat(dirs)
+ end
+
+ def self.rmdir_dirs(create = true)
+ dirs = %w[
+ empty
+ nonempty
+ nonempty/child
+ noperm
+ noperm/child
+ ]
+
+ base_dir = mock_dir ['rmdir_dirs']
+
+ dirs.reverse_each do |d|
+ dir = File.join base_dir, d
+ if File.exist? dir
+ File.chmod 0777, dir
+ rm_r dir
+ end
+ end
+ rm_r base_dir
+
+ if create
+ dirs.each do |d|
+ dir = File.join base_dir, d
+ unless File.exist? dir
+ mkdir_p dir
+ File.chmod 0777, dir
+ end
+ end
+ end
+ end
+
+ def self.expected_paths
+ %w[
+ .
+ ..
+ .dotfile
+ .dotsubdir
+ brace
+ deeply
+ dir
+ dir_filename_ordering
+ file_one.ext
+ file_two.ext
+ nondotfile
+ special
+ subdir_one
+ subdir_two
+ ]
+ end
+end
diff --git a/spec/ruby/core/dir/foreach_spec.rb b/spec/ruby/core/dir/foreach_spec.rb
new file mode 100644
index 0000000000..fed0aa8065
--- /dev/null
+++ b/spec/ruby/core/dir/foreach_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Dir.foreach" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it "yields all names in an existing directory to the provided block" do
+ a, b = [], []
+
+ Dir.foreach(DirSpecs.mock_dir) {|f| a << f}
+ Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested") {|f| b << f}
+
+ a.sort.should == DirSpecs.expected_paths
+ b.sort.should == %w|. .. .dotfile.ext directory|
+ end
+
+ it "returns nil when successful" do
+ Dir.foreach(DirSpecs.mock_dir) {|f| f}.should == nil
+ end
+
+ it "calls #to_path on non-String arguments" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return(DirSpecs.mock_dir)
+ Dir.foreach(p).to_a
+ end
+
+ it "raises a SystemCallError if passed a nonexistent directory" do
+ lambda { Dir.foreach(DirSpecs.nonexistent) {} }.should raise_error(SystemCallError)
+ end
+
+ it "returns an Enumerator if no block given" do
+ Dir.foreach(DirSpecs.mock_dir).should be_an_instance_of(Enumerator)
+ Dir.foreach(DirSpecs.mock_dir).to_a.sort.should == DirSpecs.expected_paths
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ Dir.foreach(DirSpecs.mock_dir).should be_an_instance_of(Enumerator)
+ Dir.foreach(DirSpecs.mock_dir).to_a.sort.should == DirSpecs.expected_paths
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ Dir.foreach(DirSpecs.mock_dir).size.should == nil
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/getwd_spec.rb b/spec/ruby/core/dir/getwd_spec.rb
new file mode 100644
index 0000000000..132634347c
--- /dev/null
+++ b/spec/ruby/core/dir/getwd_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/pwd'
+
+describe "Dir.getwd" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it_behaves_like :dir_pwd, :getwd
+end
diff --git a/spec/ruby/core/dir/glob_spec.rb b/spec/ruby/core/dir/glob_spec.rb
new file mode 100644
index 0000000000..4b437b0e24
--- /dev/null
+++ b/spec/ruby/core/dir/glob_spec.rb
@@ -0,0 +1,168 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/glob'
+
+describe "Dir.glob" do
+ it_behaves_like :dir_glob, :glob
+end
+
+describe "Dir.glob" do
+ it_behaves_like :dir_glob_recursive, :glob
+end
+
+describe "Dir.glob" do
+ before :each do
+ DirSpecs.create_mock_dirs
+
+ @cwd = Dir.pwd
+ Dir.chdir DirSpecs.mock_dir
+ end
+
+ after :each do
+ Dir.chdir @cwd
+
+ DirSpecs.delete_mock_dirs
+ end
+
+ it "can take an array of patterns" do
+ Dir.glob(["file_o*", "file_t*"]).should ==
+ %w!file_one.ext file_two.ext!
+ end
+
+ it "calls #to_path to convert multiple patterns" do
+ pat1 = mock('file_one.ext')
+ pat1.should_receive(:to_path).and_return('file_one.ext')
+ pat2 = mock('file_two.ext')
+ pat2.should_receive(:to_path).and_return('file_two.ext')
+
+ Dir.glob([pat1, pat2]).should == %w[file_one.ext file_two.ext]
+ end
+
+ it "matches both dot and non-dotfiles with '*' and option File::FNM_DOTMATCH" do
+ Dir.glob('*', File::FNM_DOTMATCH).sort.should == DirSpecs.expected_paths
+ end
+
+ it "matches files with any beginning with '*<non-special characters>' and option File::FNM_DOTMATCH" do
+ Dir.glob('*file', File::FNM_DOTMATCH).sort.should == %w|.dotfile nondotfile|.sort
+ end
+
+ it "matches any files in the current directory with '**' and option File::FNM_DOTMATCH" do
+ Dir.glob('**', File::FNM_DOTMATCH).sort.should == DirSpecs.expected_paths
+ end
+
+ it "recursively matches any subdirectories except './' or '../' with '**/' from the current directory and option File::FNM_DOTMATCH" do
+ expected = %w[
+ .dotsubdir/
+ brace/
+ deeply/
+ deeply/nested/
+ deeply/nested/directory/
+ deeply/nested/directory/structure/
+ dir/
+ special/
+ special/test{1}/
+ subdir_one/
+ subdir_two/
+ ]
+
+ Dir.glob('**/', File::FNM_DOTMATCH).sort.should == expected
+ end
+
+ # This is a separate case to check **/ coming after a constant
+ # directory as well.
+ it "recursively matches any subdirectories except './' or '../' with '**/' and option File::FNM_DOTMATCH" do
+ expected = %w[
+ ./
+ ./.dotsubdir/
+ ./brace/
+ ./deeply/
+ ./deeply/nested/
+ ./deeply/nested/directory/
+ ./deeply/nested/directory/structure/
+ ./dir/
+ ./special/
+ ./special/test{1}/
+ ./subdir_one/
+ ./subdir_two/
+ ]
+
+ Dir.glob('./**/', File::FNM_DOTMATCH).sort.should == expected
+ end
+
+ it "matches a list of paths by concatenating their individual results" do
+ expected = %w[
+ deeply/
+ deeply/nested/
+ deeply/nested/directory/
+ deeply/nested/directory/structure/
+ subdir_two/nondotfile
+ subdir_two/nondotfile.ext
+ ]
+
+ Dir.glob('{deeply/**/,subdir_two/*}').sort.should == expected
+ end
+
+ it "accepts a block and yields it with each elements" do
+ ary = []
+ ret = Dir.glob(["file_o*", "file_t*"]) { |t| ary << t }
+ ret.should be_nil
+ ary.should == %w!file_one.ext file_two.ext!
+ end
+
+ it "ignores non-dirs when traversing recursively" do
+ touch "spec"
+ Dir.glob("spec/**/*.rb").should == []
+ end
+
+ it "matches nothing when given an empty list of paths" do
+ Dir.glob('{}').should == []
+ end
+
+ it "handles infinite directory wildcards" do
+ Dir.glob('**/**/**').empty?.should == false
+ end
+
+ it "handles simple filename patterns" do
+ Dir.glob('.dotfile').should == ['.dotfile']
+ end
+
+ it "handles simple directory patterns" do
+ Dir.glob('.dotsubdir/').should == ['.dotsubdir/']
+ end
+
+ it "handles simple directory patterns applied to non-directories" do
+ Dir.glob('nondotfile/').should == []
+ end
+
+ platform_is_not(:windows) do
+ it "matches the literal character '\\' with option File::FNM_NOESCAPE" do
+ Dir.mkdir 'foo?bar'
+
+ begin
+ Dir.glob('foo?bar', File::FNM_NOESCAPE).should == %w|foo?bar|
+ Dir.glob('foo\?bar', File::FNM_NOESCAPE).should == []
+ ensure
+ Dir.rmdir 'foo?bar'
+ end
+
+ Dir.mkdir 'foo\?bar'
+
+ begin
+ Dir.glob('foo\?bar', File::FNM_NOESCAPE).should == %w|foo\\?bar|
+ ensure
+ Dir.rmdir 'foo\?bar'
+ end
+ end
+
+ it "returns nil for directories current user has no permission to read" do
+ Dir.mkdir('no_permission')
+ File.chmod(0, 'no_permission')
+
+ begin
+ Dir.glob('no_permission/*').should == []
+ ensure
+ Dir.rmdir('no_permission')
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/home_spec.rb b/spec/ruby/core/dir/home_spec.rb
new file mode 100644
index 0000000000..7e2256e1a1
--- /dev/null
+++ b/spec/ruby/core/dir/home_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Dir.home" do
+ it "returns the current user's home directory as a string if called without arguments" do
+ home_directory = ENV['HOME']
+ platform_is :windows do
+ unless home_directory
+ home_directory = ENV['HOMEDRIVE'] + ENV['HOMEPATH']
+ end
+ home_directory = home_directory.tr('\\', '/').chomp('/')
+ end
+
+ Dir.home.should == home_directory
+ end
+
+ platform_is :solaris do
+ it "returns the named user's home directory, from the user database, as a string if called with an argument" do
+ Dir.home(ENV['USER']).should == `getent passwd #{ENV['USER']}|cut -d: -f6`.chomp
+ end
+ end
+
+ platform_is_not :windows, :solaris do
+ it "returns the named user's home directory, from the user database, as a string if called with an argument" do
+ Dir.home(ENV['USER']).should == `echo ~#{ENV['USER']}`.chomp
+ end
+ end
+
+ it "raises an ArgumentError if the named user doesn't exist" do
+ lambda { Dir.home('geuw2n288dh2k') }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/dir/initialize_spec.rb b/spec/ruby/core/dir/initialize_spec.rb
new file mode 100644
index 0000000000..547b7dc18e
--- /dev/null
+++ b/spec/ruby/core/dir/initialize_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Dir#initialize" do
+ before :each do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :each do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it "calls #to_path on non-String arguments" do
+ p = mock('path')
+ p.stub!(:to_path).and_return(DirSpecs.mock_dir)
+ dir = Dir.new(p)
+ begin
+ dir.path.should == DirSpecs.mock_dir
+ ensure
+ dir.close
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/inspect_spec.rb b/spec/ruby/core/dir/inspect_spec.rb
new file mode 100644
index 0000000000..37338a97d4
--- /dev/null
+++ b/spec/ruby/core/dir/inspect_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Dir#inspect" do
+ before :each do
+ @dir = Dir.new(Dir.getwd)
+ end
+
+ after :each do
+ @dir.close
+ end
+
+ it "returns a String" do
+ @dir.inspect.should be_an_instance_of(String)
+ end
+
+ it "includes the class name" do
+ @dir.inspect.should =~ /Dir/
+ end
+
+ it "includes the directory name" do
+ @dir.inspect.should include(Dir.getwd)
+ end
+end
diff --git a/spec/ruby/core/dir/mkdir_spec.rb b/spec/ruby/core/dir/mkdir_spec.rb
new file mode 100644
index 0000000000..d0bf32c143
--- /dev/null
+++ b/spec/ruby/core/dir/mkdir_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Dir.mkdir" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it "creates the named directory with the given permissions" do
+ DirSpecs.clear_dirs
+
+ begin
+ File.exist?('nonexisting').should == false
+ Dir.mkdir 'nonexisting'
+ File.exist?('nonexisting').should == true
+ platform_is_not :windows do
+ Dir.mkdir 'default_perms'
+ a = File.stat('default_perms').mode
+ Dir.mkdir 'reduced', (a - 1)
+ File.stat('reduced').mode.should_not == a
+ end
+ platform_is :windows do
+ Dir.mkdir 'default_perms', 0666
+ a = File.stat('default_perms').mode
+ Dir.mkdir 'reduced', 0444
+ File.stat('reduced').mode.should_not == a
+ end
+
+ Dir.mkdir('always_returns_0').should == 0
+ platform_is_not(:windows) do
+ File.chmod(0777, "nonexisting","default_perms","reduced","always_returns_0")
+ end
+ platform_is_not(:windows) do
+ File.chmod(0644, "nonexisting","default_perms","reduced","always_returns_0")
+ end
+ ensure
+ DirSpecs.clear_dirs
+ end
+ end
+
+ it "calls #to_path on non-String arguments" do
+ DirSpecs.clear_dirs
+ p = mock('path')
+ p.should_receive(:to_path).and_return('nonexisting')
+ Dir.mkdir(p)
+ DirSpecs.clear_dirs
+ end
+
+ it "raises a SystemCallError if any of the directories in the path before the last does not exist" do
+ lambda { Dir.mkdir "#{DirSpecs.nonexistent}/subdir" }.should raise_error(SystemCallError)
+ end
+
+ it "raises Errno::EEXIST if the specified directory already exists" do
+ lambda { Dir.mkdir("#{DirSpecs.mock_dir}/dir") }.should raise_error(Errno::EEXIST)
+ end
+
+ it "raises Errno::EEXIST if the argument points to the existing file" do
+ lambda { Dir.mkdir("#{DirSpecs.mock_dir}/file_one.ext") }.should raise_error(Errno::EEXIST)
+ end
+end
+
+# The permissions flag are not supported on Windows as stated in documentation:
+# The permissions may be modified by the value of File.umask, and are ignored on NT.
+platform_is_not :windows do
+ as_user do
+ describe "Dir.mkdir" do
+ before :each do
+ @dir = tmp "noperms"
+ end
+
+ after :each do
+ File.chmod 0777, @dir
+ rm_r @dir
+ end
+
+ it "raises a SystemCallError when lacking adequate permissions in the parent dir" do
+ Dir.mkdir @dir, 0000
+
+ lambda { Dir.mkdir "#{@dir}/subdir" }.should raise_error(SystemCallError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/open_spec.rb b/spec/ruby/core/dir/open_spec.rb
new file mode 100644
index 0000000000..27f362320b
--- /dev/null
+++ b/spec/ruby/core/dir/open_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/open'
+
+describe "Dir.open" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it_behaves_like :dir_open, :open
+end
diff --git a/spec/ruby/core/dir/path_spec.rb b/spec/ruby/core/dir/path_spec.rb
new file mode 100644
index 0000000000..b1c24c406b
--- /dev/null
+++ b/spec/ruby/core/dir/path_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/path'
+
+describe "Dir#path" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it_behaves_like :dir_path, :path
+end
diff --git a/spec/ruby/core/dir/pos_spec.rb b/spec/ruby/core/dir/pos_spec.rb
new file mode 100644
index 0000000000..b382bff81f
--- /dev/null
+++ b/spec/ruby/core/dir/pos_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/closed'
+require_relative 'shared/pos'
+
+describe "Dir#pos" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it_behaves_like :dir_pos, :pos
+end
+
+describe "Dir#pos" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it_behaves_like :dir_closed, :pos
+end
+
+describe "Dir#pos=" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it_behaves_like :dir_pos_set, :pos=
+end
diff --git a/spec/ruby/core/dir/pwd_spec.rb b/spec/ruby/core/dir/pwd_spec.rb
new file mode 100644
index 0000000000..ad01286c90
--- /dev/null
+++ b/spec/ruby/core/dir/pwd_spec.rb
@@ -0,0 +1,39 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/pwd'
+
+describe "Dir.pwd" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it_behaves_like :dir_pwd, :pwd
+end
+
+describe "Dir.pwd" do
+ before :each do
+ @name = tmp("ã‚").force_encoding('binary')
+ @fs_encoding = Encoding.find('filesystem')
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ platform_is_not :windows do
+ it "correctly handles dirs with unicode characters in them" do
+ Dir.mkdir @name
+ Dir.chdir @name do
+ if @fs_encoding == Encoding::UTF_8
+ Dir.pwd.encoding.should == Encoding::UTF_8
+ end
+ Dir.pwd.force_encoding('binary').should == @name
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/read_spec.rb b/spec/ruby/core/dir/read_spec.rb
new file mode 100644
index 0000000000..59de2e81cf
--- /dev/null
+++ b/spec/ruby/core/dir/read_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/closed'
+
+describe "Dir#read" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it "returns the file name in the current seek position" do
+ # an FS does not necessarily impose order
+ ls = Dir.entries DirSpecs.mock_dir
+ dir = Dir.open DirSpecs.mock_dir
+ ls.should include(dir.read)
+ dir.close
+ end
+
+ it "returns nil when there are no more entries" do
+ dir = Dir.open DirSpecs.mock_dir
+ DirSpecs.expected_paths.size.times do
+ dir.read.should_not == nil
+ end
+ dir.read.should == nil
+ dir.close
+ end
+
+ it "returns each entry successively" do
+ dir = Dir.open DirSpecs.mock_dir
+ entries = []
+ while entry = dir.read
+ entries << entry
+ end
+ dir.close
+
+ entries.sort.should == DirSpecs.expected_paths
+ end
+
+ it_behaves_like :dir_closed, :read
+end
diff --git a/spec/ruby/core/dir/rewind_spec.rb b/spec/ruby/core/dir/rewind_spec.rb
new file mode 100644
index 0000000000..220d7f5372
--- /dev/null
+++ b/spec/ruby/core/dir/rewind_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/closed'
+
+describe "Dir#rewind" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ before :each do
+ @dir = Dir.open DirSpecs.mock_dir
+ end
+
+ after :each do
+ @dir.close
+ end
+
+ it "resets the next read to start from the first entry" do
+ a = @dir.read
+ b = @dir.read
+ a.should_not == b
+ @dir.rewind
+ c = @dir.read
+ c.should == a
+ end
+
+ it "returns the Dir instance" do
+ @dir.rewind.should == @dir
+ end
+
+ it_behaves_like :dir_closed, :rewind
+end
diff --git a/spec/ruby/core/dir/rmdir_spec.rb b/spec/ruby/core/dir/rmdir_spec.rb
new file mode 100644
index 0000000000..08cd1a5bc6
--- /dev/null
+++ b/spec/ruby/core/dir/rmdir_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/delete'
+
+describe "Dir.rmdir" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it_behaves_like :dir_delete, :rmdir
+end
diff --git a/spec/ruby/core/dir/seek_spec.rb b/spec/ruby/core/dir/seek_spec.rb
new file mode 100644
index 0000000000..ed409897cd
--- /dev/null
+++ b/spec/ruby/core/dir/seek_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/pos'
+
+describe "Dir#seek" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it "returns the Dir instance" do
+ @dir.seek(@dir.pos).should == @dir
+ end
+
+ it_behaves_like :dir_pos_set, :seek
+end
diff --git a/spec/ruby/core/dir/shared/chroot.rb b/spec/ruby/core/dir/shared/chroot.rb
new file mode 100644
index 0000000000..2ed033dfed
--- /dev/null
+++ b/spec/ruby/core/dir/shared/chroot.rb
@@ -0,0 +1,41 @@
+describe :dir_chroot_as_root, shared: true do
+ before :all do
+ DirSpecs.create_mock_dirs
+
+ @real_root = "../" * (File.dirname(__FILE__).count('/') - 1)
+ @ref_dir = File.join("/", Dir.new('/').entries.first)
+ end
+
+ after :all do
+ until File.exist?(@ref_dir)
+ Dir.send(@method, "../") or break
+ end
+
+ DirSpecs.delete_mock_dirs
+ end
+
+ it "can be used to change the process' root directory" do
+ lambda { Dir.send(@method, File.dirname(__FILE__)) }.should_not raise_error
+ File.exist?("/#{File.basename(__FILE__)}").should be_true
+ end
+
+ it "returns 0 if successful" do
+ Dir.send(@method, '/').should == 0
+ end
+
+ it "raises an Errno::ENOENT exception if the directory doesn't exist" do
+ lambda { Dir.send(@method, 'xgwhwhsjai2222jg') }.should raise_error(Errno::ENOENT)
+ end
+
+ it "can be escaped from with ../" do
+ Dir.send(@method, @real_root)
+ File.exist?(@ref_dir).should be_true
+ File.exist?("/#{File.basename(__FILE__)}").should be_false
+ end
+
+ it "calls #to_path on non-String argument" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return(@real_root)
+ Dir.send(@method, p)
+ end
+end
diff --git a/spec/ruby/core/dir/shared/closed.rb b/spec/ruby/core/dir/shared/closed.rb
new file mode 100644
index 0000000000..a1bce06a08
--- /dev/null
+++ b/spec/ruby/core/dir/shared/closed.rb
@@ -0,0 +1,9 @@
+describe :dir_closed, shared: true do
+ it "raises an IOError when called on a closed Dir instance" do
+ lambda {
+ dir = Dir.open DirSpecs.mock_dir
+ dir.close
+ dir.send(@method) {}
+ }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/dir/shared/delete.rb b/spec/ruby/core/dir/shared/delete.rb
new file mode 100644
index 0000000000..649708cd76
--- /dev/null
+++ b/spec/ruby/core/dir/shared/delete.rb
@@ -0,0 +1,63 @@
+describe :dir_delete, shared: true do
+ before :each do
+ DirSpecs.rmdir_dirs true
+ end
+
+ after :each do
+ DirSpecs.rmdir_dirs false
+ end
+
+ it "removes empty directories" do
+ Dir.send(@method, DirSpecs.mock_rmdir("empty")).should == 0
+ end
+
+ it "calls #to_path on non-String arguments" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return(DirSpecs.mock_rmdir("empty"))
+ Dir.send(@method, p)
+ end
+
+ platform_is_not :solaris do
+ it "raises an Errno::ENOTEMPTY when trying to remove a nonempty directory" do
+ lambda do
+ Dir.send @method, DirSpecs.mock_rmdir("nonempty")
+ end.should raise_error(Errno::ENOTEMPTY)
+ end
+ end
+
+ platform_is :solaris do
+ it "raises an Errno::EEXIST when trying to remove a nonempty directory" do
+ lambda do
+ Dir.send @method, DirSpecs.mock_rmdir("nonempty")
+ end.should raise_error(Errno::EEXIST)
+ end
+ end
+
+ it "raises an Errno::ENOENT when trying to remove a non-existing directory" do
+ lambda do
+ Dir.send @method, DirSpecs.nonexistent
+ end.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises an Errno::ENOTDIR when trying to remove a non-directory" do
+ file = DirSpecs.mock_rmdir("nonempty/regular")
+ touch(file)
+ lambda do
+ Dir.send @method, file
+ end.should raise_error(Errno::ENOTDIR)
+ end
+
+ # this won't work on Windows, since chmod(0000) does not remove all permissions
+ platform_is_not :windows do
+ as_user do
+ it "raises an Errno::EACCES if lacking adequate permissions to remove the directory" do
+ parent = DirSpecs.mock_rmdir("noperm")
+ child = DirSpecs.mock_rmdir("noperm", "child")
+ File.chmod(0000, parent)
+ lambda do
+ Dir.send @method, child
+ end.should raise_error(Errno::EACCES)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/shared/exist.rb b/spec/ruby/core/dir/shared/exist.rb
new file mode 100644
index 0000000000..fbd2c9862d
--- /dev/null
+++ b/spec/ruby/core/dir/shared/exist.rb
@@ -0,0 +1,56 @@
+describe :dir_exist, shared: true do
+ it "returns true if the given directory exists" do
+ Dir.send(@method, File.dirname(__FILE__)).should be_true
+ end
+
+ it "returns true for '.'" do
+ Dir.send(@method, '.').should be_true
+ end
+
+ it "returns true for '..'" do
+ Dir.send(@method, '..').should be_true
+ end
+
+ it "understands non-ASCII paths" do
+ subdir = File.join(tmp("\u{9876}\u{665}"))
+ Dir.send(@method, subdir).should be_false
+ Dir.mkdir(subdir)
+ Dir.send(@method, subdir).should be_true
+ Dir.rmdir(subdir)
+ end
+
+ it "understands relative paths" do
+ Dir.send(@method, File.dirname(__FILE__) + '/../').should be_true
+ end
+
+ it "returns false if the given directory doesn't exist" do
+ Dir.send(@method, 'y26dg27n2nwjs8a/').should be_false
+ end
+
+ it "doesn't require the name to have a trailing slash" do
+ dir = File.dirname(__FILE__)
+ dir.sub!(/\/$/,'')
+ Dir.send(@method, dir).should be_true
+ end
+
+ it "doesn't expand paths" do
+ Dir.send(@method, File.expand_path('~')).should be_true
+ Dir.send(@method, '~').should be_false
+ end
+
+ it "returns false if the argument exists but is a file" do
+ File.exist?(__FILE__).should be_true
+ Dir.send(@method, __FILE__).should be_false
+ end
+
+ it "doesn't set $! when file doesn't exist" do
+ Dir.send(@method, "/path/to/non/existent/dir")
+ $!.should be_nil
+ end
+
+ it "calls #to_path on non String arguments" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return(File.dirname(__FILE__))
+ Dir.send(@method, p)
+ end
+end
diff --git a/spec/ruby/core/dir/shared/glob.rb b/spec/ruby/core/dir/shared/glob.rb
new file mode 100644
index 0000000000..88ceb82c50
--- /dev/null
+++ b/spec/ruby/core/dir/shared/glob.rb
@@ -0,0 +1,411 @@
+# -*- encoding: utf-8 -*-
+describe :dir_glob, shared: true do
+ before :all do
+ DirSpecs.create_mock_dirs
+ @cwd = Dir.pwd
+ Dir.chdir DirSpecs.mock_dir
+ end
+
+ after :all do
+ Dir.chdir @cwd
+ DirSpecs.delete_mock_dirs
+ end
+
+ with_feature :encoding do
+ it "raises an Encoding::CompatibilityError if the argument encoding is not compatible with US-ASCII" do
+ pattern = "file*".force_encoding Encoding::UTF_16BE
+ lambda { Dir.send(@method, pattern) }.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+
+ it "calls #to_path to convert a pattern" do
+ obj = mock('file_one.ext')
+ obj.should_receive(:to_path).and_return('file_one.ext')
+
+ Dir.send(@method, obj).should == %w[file_one.ext]
+ end
+
+ ruby_version_is ""..."2.6" do
+ it "splits the string on \\0 if there is only one string given" do
+ Dir.send(@method, "file_o*\0file_t*").should ==
+ %w!file_one.ext file_two.ext!
+ end
+ end
+
+ ruby_version_is "2.6" do
+ it "splits the string on \\0 if there is only one string given and warns" do
+ -> {
+ Dir.send(@method, "file_o*\0file_t*").should ==
+ %w!file_one.ext file_two.ext!
+ }.should complain(/warning: use glob patterns list instead of nul-separated patterns/)
+ end
+ end
+
+ it "matches non-dotfiles with '*'" do
+ expected = %w[
+ brace
+ deeply
+ dir
+ dir_filename_ordering
+ file_one.ext
+ file_two.ext
+ nondotfile
+ special
+ subdir_one
+ subdir_two
+ ]
+
+ Dir.send(@method,'*').sort.should == expected
+ end
+
+ it "returns empty array when empty pattern provided" do
+ Dir.send(@method, '').should == []
+ end
+
+ it "matches regexp special +" do
+ Dir.send(@method, 'special/+').should == ['special/+']
+ end
+
+ platform_is_not :windows do
+ it "matches regexp special *" do
+ Dir.send(@method, 'special/\*').should == ['special/*']
+ end
+
+ it "matches regexp special ?" do
+ Dir.send(@method, 'special/\?').should == ['special/?']
+ end
+
+ it "matches regexp special |" do
+ Dir.send(@method, 'special/|').should == ['special/|']
+ end
+ end
+
+ it "matches regexp special ^" do
+ Dir.send(@method, 'special/^').should == ['special/^']
+ end
+
+ it "matches regexp special $" do
+ Dir.send(@method, 'special/$').should == ['special/$']
+ end
+
+ it "matches regexp special (" do
+ Dir.send(@method, 'special/(').should == ['special/(']
+ end
+
+ it "matches regexp special )" do
+ Dir.send(@method, 'special/)').should == ['special/)']
+ end
+
+ it "matches regexp special [" do
+ Dir.send(@method, 'special/\[').should == ['special/[']
+ end
+
+ it "matches regexp special ]" do
+ Dir.send(@method, 'special/]').should == ['special/]']
+ end
+
+ it "matches regexp special {" do
+ Dir.send(@method, 'special/\{').should == ['special/{']
+ end
+
+ it "matches regexp special }" do
+ Dir.send(@method, 'special/\}').should == ['special/}']
+ end
+
+ it "matches paths with glob patterns" do
+ Dir.send(@method, 'special/test\{1\}/*').should == ['special/test{1}/file[1]']
+ end
+
+ it "matches dotfiles with '.*'" do
+ Dir.send(@method, '.*').sort.should == %w|. .. .dotfile .dotsubdir|.sort
+ end
+
+ it "matches non-dotfiles with '*<non-special characters>'" do
+ Dir.send(@method, '*file').sort.should == %w|nondotfile|.sort
+ end
+
+ it "matches dotfiles with '.*<non-special characters>'" do
+ Dir.send(@method, '.*file').sort.should == %w|.dotfile|.sort
+ end
+
+ it "matches files with any ending with '<non-special characters>*'" do
+ Dir.send(@method, 'file*').sort.should == %w|file_one.ext file_two.ext|.sort
+ end
+
+ it "matches files with any middle with '<non-special characters>*<non-special characters>'" do
+ Dir.send(@method, 'sub*_one').sort.should == %w|subdir_one|.sort
+ end
+
+ it "handles directories with globs" do
+ Dir.send(@method, 'sub*/*').sort.should == %w!subdir_one/nondotfile subdir_two/nondotfile subdir_two/nondotfile.ext!
+ end
+
+ it "matches files with multiple '*' special characters" do
+ Dir.send(@method, '*fi*e*').sort.should == %w|dir_filename_ordering nondotfile file_one.ext file_two.ext|.sort
+ end
+
+ it "matches non-dotfiles in the current directory with '**'" do
+ expected = %w[
+ brace
+ deeply
+ dir
+ dir_filename_ordering
+ file_one.ext
+ file_two.ext
+ nondotfile
+ special
+ subdir_one
+ subdir_two
+ ]
+
+ Dir.send(@method, '**').sort.should == expected
+ end
+
+ it "matches dotfiles in the current directory with '.**'" do
+ Dir.send(@method, '.**').sort.should == %w|. .. .dotsubdir .dotfile|.sort
+ end
+
+ it "recursively matches any nondot subdirectories with '**/'" do
+ expected = %w[
+ brace/
+ deeply/
+ deeply/nested/
+ deeply/nested/directory/
+ deeply/nested/directory/structure/
+ dir/
+ special/
+ special/test{1}/
+ subdir_one/
+ subdir_two/
+ ]
+
+ Dir.send(@method, '**/').sort.should == expected
+ end
+
+ it "recursively matches any subdirectories including ./ and ../ with '.**/'" do
+ Dir.chdir("#{DirSpecs.mock_dir}/subdir_one") do
+ Dir.send(@method, '.**/').sort.should == %w|./ ../|.sort
+ end
+ end
+
+ it "matches a single character except leading '.' with '?'" do
+ Dir.send(@method, '?ubdir_one').sort.should == %w|subdir_one|.sort
+ end
+
+ it "accepts multiple '?' characters in a pattern" do
+ Dir.send(@method, 'subdir_???').sort.should == %w|subdir_one subdir_two|.sort
+ end
+
+ it "matches any characters in a set with '[<characters>]'" do
+ Dir.send(@method, '[stfu]ubdir_one').sort.should == %w|subdir_one|.sort
+ end
+
+ it "matches any characters in a range with '[<character>-<character>]'" do
+ Dir.send(@method, '[a-zA-Z]ubdir_one').sort.should == %w|subdir_one|.sort
+ end
+
+ it "matches any characters except those in a set with '[^<characters>]'" do
+ Dir.send(@method, '[^wtf]ubdir_one').sort.should == %w|subdir_one|.sort
+ end
+
+ it "matches any characters except those in a range with '[^<character>-<character]'" do
+ Dir.send(@method, '[^0-9]ubdir_one').sort.should == %w|subdir_one|.sort
+ end
+
+ it "matches any one of the strings in a set with '{<string>,<other>,...}'" do
+ Dir.send(@method, 'subdir_{one,two,three}').sort.should == %w|subdir_one subdir_two|.sort
+ end
+
+ it "matches a set '{<string>,<other>,...}' which also uses a glob" do
+ Dir.send(@method, 'sub*_{one,two,three}').sort.should == %w|subdir_one subdir_two|.sort
+ end
+
+ it "accepts string sets with empty strings with {<string>,,<other>}" do
+ a = Dir.send(@method, 'deeply/nested/directory/structure/file_one{.ext,}').sort
+ a.should == %w|deeply/nested/directory/structure/file_one.ext
+ deeply/nested/directory/structure/file_one|.sort
+ end
+
+ it "matches dot or non-dotfiles with '{,.}*'" do
+ Dir.send(@method, '{,.}*').sort.should == DirSpecs.expected_paths
+ end
+
+ it "respects the order of {} expressions, expanding left most first" do
+ files = Dir.send(@method, "brace/a{.js,.html}{.erb,.rjs}")
+ files.should == %w!brace/a.js.rjs brace/a.html.erb!
+ end
+
+ it "respects the optional nested {} expressions" do
+ files = Dir.send(@method, "brace/a{.{js,html},}{.{erb,rjs},}")
+ files.should == %w!brace/a.js.rjs brace/a.js brace/a.html.erb brace/a.erb brace/a!
+ end
+
+ it "matches special characters by escaping with a backslash with '\\<character>'" do
+ Dir.mkdir 'foo^bar'
+
+ begin
+ Dir.send(@method, 'foo?bar').should == %w|foo^bar|
+ Dir.send(@method, 'foo\?bar').should == []
+ Dir.send(@method, 'nond\otfile').should == %w|nondotfile|
+ ensure
+ Dir.rmdir 'foo^bar'
+ end
+ end
+
+ it "recursively matches directories with '**/<characters>'" do
+ Dir.send(@method, '**/*fil?{,.}*').uniq.sort.should ==
+ %w[deeply/nested/directory/structure/file_one
+ deeply/nested/directory/structure/file_one.ext
+ deeply/nondotfile
+
+ dir/filename_ordering
+ dir_filename_ordering
+
+ file_one.ext
+ file_two.ext
+
+ nondotfile
+
+ special/test{1}/file[1]
+
+ subdir_one/nondotfile
+ subdir_two/nondotfile
+ subdir_two/nondotfile.ext]
+ end
+
+ it "ignores matching through directories that doen't exist" do
+ Dir.send(@method, "deeply/notthere/blah*/whatever").should == []
+ end
+
+ it "ignores matching only directories under an nonexistant path" do
+ Dir.send(@method, "deeply/notthere/blah/").should == []
+ end
+
+ platform_is_not :windows do
+ it "matches UTF-8 paths" do
+ Dir.send(@method, "special/ã“ã‚“ã«ã¡ã¯{,.txt}").should == ["special/ã“ã‚“ã«ã¡ã¯.txt"]
+ end
+ end
+
+ ruby_version_is "2.5" do
+ context ":base option passed" do
+ before :each do
+ @mock_dir = File.expand_path tmp('dir_glob_mock')
+
+ %w[
+ a/b/x
+ a/b/c/y
+ a/b/c/d/z
+ ].each do |path|
+ file = File.join @mock_dir, path
+ mkdir_p File.dirname(file)
+ touch file
+ end
+ end
+
+ after :each do
+ rm_r @mock_dir
+ end
+
+ it "matches entries only from within the specified directory" do
+ path = File.join(@mock_dir, "a/b/c")
+ Dir.send(@method, "*", base: path).sort.should == %w( d y )
+ end
+
+ it "accepts both relative and absolute pathes" do
+ require 'pathname'
+
+ path_abs = File.join(@mock_dir, "a/b/c")
+ path_rel = Pathname.new(path_abs).relative_path_from(Pathname.new(Dir.pwd))
+
+ result_abs = Dir.send(@method, "*", base: path_abs).sort
+ result_rel = Dir.send(@method, "*", base: path_rel).sort
+
+ result_abs.should == %w( d y )
+ result_rel.should == %w( d y )
+ end
+
+ it "returns [] if specified path does not exist" do
+ path = File.join(@mock_dir, "fake-name")
+ File.exist?(path).should == false
+
+ Dir.send(@method, "*", base: path).should == []
+ end
+
+ it "returns [] if specified path is a file" do
+ path = File.join(@mock_dir, "a/b/x")
+ File.exist?(path).should == true
+
+ Dir.send(@method, "*", base: path).should == []
+ end
+
+ it "raises TypeError whene cannot convert value to string" do
+ -> {
+ Dir.send(@method, "*", base: [])
+ }.should raise_error(TypeError)
+ end
+
+ it "handles '' as current directory path" do
+ Dir.chdir @mock_dir do
+ Dir.send(@method, "*", base: "").should == %w( a )
+ end
+ end
+
+ it "handles nil as current directory path" do
+ Dir.chdir @mock_dir do
+ Dir.send(@method, "*", base: nil).should == %w( a )
+ end
+ end
+ end
+ end
+end
+
+describe :dir_glob_recursive, shared: true do
+ before :each do
+ @cwd = Dir.pwd
+ @mock_dir = File.expand_path tmp('dir_glob_mock')
+
+ %w[
+ a/x/b/y/e
+ a/x/b/y/b/z/e
+ ].each do |path|
+ file = File.join @mock_dir, path
+ mkdir_p File.dirname(file)
+ touch file
+ end
+
+ Dir.chdir @mock_dir
+ end
+
+ after :each do
+ Dir.chdir @cwd
+ rm_r @mock_dir
+ end
+
+ it "matches multiple recursives" do
+ expected = %w[
+ a/x/b/y/b/z/e
+ a/x/b/y/e
+ ]
+
+ Dir.send(@method, 'a/**/b/**/e').uniq.sort.should == expected
+ end
+
+ platform_is_not :windows do
+ it "ignores symlinks" do
+ file = File.join @mock_dir, 'b/z/e'
+ link = File.join @mock_dir, 'a/y'
+
+ mkdir_p File.dirname(file)
+ touch file
+ File.symlink(File.dirname(file), link)
+
+ expected = %w[
+ a/x/b/y/b/z/e
+ a/x/b/y/e
+ ]
+
+ Dir.send(@method, 'a/**/e').uniq.sort.should == expected
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/shared/open.rb b/spec/ruby/core/dir/shared/open.rb
new file mode 100644
index 0000000000..7f4fe5c2a6
--- /dev/null
+++ b/spec/ruby/core/dir/shared/open.rb
@@ -0,0 +1,63 @@
+describe :dir_open, shared: true do
+ it "returns a Dir instance representing the specified directory" do
+ dir = Dir.send(@method, DirSpecs.mock_dir)
+ dir.should be_kind_of(Dir)
+ dir.close
+ end
+
+ it "raises a SystemCallError if the directory does not exist" do
+ lambda do
+ Dir.send @method, DirSpecs.nonexistent
+ end.should raise_error(SystemCallError)
+ end
+
+ it "may take a block which is yielded to with the Dir instance" do
+ Dir.send(@method, DirSpecs.mock_dir) {|dir| dir.should be_kind_of(Dir)}
+ end
+
+ it "returns the value of the block if a block is given" do
+ Dir.send(@method, DirSpecs.mock_dir) {|dir| :value }.should == :value
+ end
+
+ it "closes the Dir instance when the block exits if given a block" do
+ closed_dir = Dir.send(@method, DirSpecs.mock_dir) { |dir| dir }
+ lambda { closed_dir.read }.should raise_error(IOError)
+ end
+
+ it "closes the Dir instance when the block exits the block even due to an exception" do
+ closed_dir = nil
+
+ lambda do
+ Dir.send(@method, DirSpecs.mock_dir) do |dir|
+ closed_dir = dir
+ raise "dir specs"
+ end
+ end.should raise_error(RuntimeError, "dir specs")
+
+ lambda { closed_dir.read }.should raise_error(IOError)
+ end
+
+ it "calls #to_path on non-String arguments" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return(DirSpecs.mock_dir)
+ Dir.send(@method, p) { true }
+ end
+
+ it "accepts an options Hash" do
+ dir = Dir.send(@method, DirSpecs.mock_dir, encoding: "utf-8") {|d| d }
+ dir.should be_kind_of(Dir)
+ end
+
+ it "calls #to_hash to convert the options object" do
+ options = mock("dir_open")
+ options.should_receive(:to_hash).and_return({ encoding: Encoding::UTF_8 })
+
+ dir = Dir.send(@method, DirSpecs.mock_dir, options) {|d| d }
+ dir.should be_kind_of(Dir)
+ end
+
+ it "ignores the :encoding option if it is nil" do
+ dir = Dir.send(@method, DirSpecs.mock_dir, encoding: nil) {|d| d }
+ dir.should be_kind_of(Dir)
+ end
+end
diff --git a/spec/ruby/core/dir/shared/path.rb b/spec/ruby/core/dir/shared/path.rb
new file mode 100644
index 0000000000..fe2d61ebf7
--- /dev/null
+++ b/spec/ruby/core/dir/shared/path.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/common'
+require_relative 'closed'
+
+describe :dir_path, shared: true do
+ it "returns the path that was supplied to .new or .open" do
+ dir = Dir.open DirSpecs.mock_dir
+ begin
+ dir.send(@method).should == DirSpecs.mock_dir
+ ensure
+ dir.close rescue nil
+ end
+ end
+
+ it "returns the path even when called on a closed Dir instance" do
+ dir = Dir.open DirSpecs.mock_dir
+ dir.close
+ dir.send(@method).should == DirSpecs.mock_dir
+ end
+
+ with_feature :encoding do
+ it "returns a String with the same encoding as the argument to .open" do
+ path = DirSpecs.mock_dir.force_encoding Encoding::IBM866
+ dir = Dir.open path
+ begin
+ dir.send(@method).encoding.should equal(Encoding::IBM866)
+ ensure
+ dir.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/shared/pos.rb b/spec/ruby/core/dir/shared/pos.rb
new file mode 100644
index 0000000000..2165932d99
--- /dev/null
+++ b/spec/ruby/core/dir/shared/pos.rb
@@ -0,0 +1,51 @@
+describe :dir_pos, shared: true do
+ before :each do
+ @dir = Dir.open DirSpecs.mock_dir
+ end
+
+ after :each do
+ @dir.close rescue nil
+ end
+
+ it "returns an Integer representing the current position in the directory" do
+ @dir.send(@method).should be_kind_of(Integer)
+ @dir.send(@method).should be_kind_of(Integer)
+ @dir.send(@method).should be_kind_of(Integer)
+ end
+
+ it "returns a different Integer if moved from previous position" do
+ a = @dir.send(@method)
+ @dir.read
+ b = @dir.send(@method)
+
+ a.should be_kind_of(Integer)
+ b.should be_kind_of(Integer)
+
+ a.should_not == b
+ end
+end
+
+describe :dir_pos_set, shared: true do
+ before :each do
+ @dir = Dir.open DirSpecs.mock_dir
+ end
+
+ after :each do
+ @dir.close
+ end
+
+ # NOTE: #seek/#pos= to a position not returned by #tell/#pos is undefined
+ # and should not be spec'd.
+
+ it "moves the read position to a previously obtained position" do
+ pos = @dir.pos
+ a = @dir.read
+ b = @dir.read
+ @dir.send @method, pos
+ c = @dir.read
+
+ a.should_not == b
+ b.should_not == c
+ c.should == a
+ end
+end
diff --git a/spec/ruby/core/dir/shared/pwd.rb b/spec/ruby/core/dir/shared/pwd.rb
new file mode 100644
index 0000000000..5f041a9d41
--- /dev/null
+++ b/spec/ruby/core/dir/shared/pwd.rb
@@ -0,0 +1,49 @@
+describe :dir_pwd, shared: true do
+ with_feature :encoding do
+ before :each do
+ @fs_encoding = Encoding.find('filesystem')
+ end
+ end
+
+ it "returns the current working directory" do
+ pwd = Dir.send(@method)
+
+ File.directory?(pwd).should == true
+
+ # On ubuntu gutsy, for example, /bin/pwd does not
+ # understand -P. With just `pwd -P`, /bin/pwd is run.
+
+ # The following uses inode rather than file names to account for
+ # case insensitive file systems like default OS/X file systems
+ platform_is_not :windows do
+ File.stat(pwd).ino.should == File.stat(`/bin/sh -c "pwd -P"`.chomp).ino
+ end
+ platform_is :windows do
+ File.stat(pwd).ino.should == File.stat(File.expand_path(`cd`.chomp)).ino
+ end
+ end
+
+ it "returns an absolute path" do
+ pwd = Dir.send(@method)
+ pwd.should == File.expand_path(pwd)
+ end
+
+ it "returns an absolute path even when chdir to a relative path" do
+ Dir.chdir(".") do
+ pwd = Dir.send(@method)
+ File.directory?(pwd).should == true
+ pwd.should == File.expand_path(pwd)
+ end
+ end
+
+ with_feature :encoding do
+ it "returns a String with the filesystem encoding" do
+ enc = Dir.send(@method).encoding
+ if @fs_encoding == Encoding::US_ASCII
+ [Encoding::US_ASCII, Encoding::ASCII_8BIT].should include(enc)
+ else
+ enc.should equal(@fs_encoding)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/tell_spec.rb b/spec/ruby/core/dir/tell_spec.rb
new file mode 100644
index 0000000000..af86dc1598
--- /dev/null
+++ b/spec/ruby/core/dir/tell_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/closed'
+require_relative 'shared/pos'
+
+describe "Dir#tell" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it_behaves_like :dir_pos, :tell
+
+ it_behaves_like :dir_closed, :tell
+end
diff --git a/spec/ruby/core/dir/to_path_spec.rb b/spec/ruby/core/dir/to_path_spec.rb
new file mode 100644
index 0000000000..77404a3dc8
--- /dev/null
+++ b/spec/ruby/core/dir/to_path_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/path'
+
+describe "Dir#to_path" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it_behaves_like :dir_path, :to_path
+end
diff --git a/spec/ruby/core/dir/unlink_spec.rb b/spec/ruby/core/dir/unlink_spec.rb
new file mode 100644
index 0000000000..79027e020c
--- /dev/null
+++ b/spec/ruby/core/dir/unlink_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/delete'
+
+describe "Dir.unlink" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ it_behaves_like :dir_delete, :unlink
+end
diff --git a/spec/ruby/core/encoding/_dump_spec.rb b/spec/ruby/core/encoding/_dump_spec.rb
new file mode 100644
index 0000000000..623fe88ec9
--- /dev/null
+++ b/spec/ruby/core/encoding/_dump_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Encoding#_dump" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/encoding/_load_spec.rb b/spec/ruby/core/encoding/_load_spec.rb
new file mode 100644
index 0000000000..608098d34b
--- /dev/null
+++ b/spec/ruby/core/encoding/_load_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Encoding._load" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/encoding/aliases_spec.rb b/spec/ruby/core/encoding/aliases_spec.rb
new file mode 100644
index 0000000000..88c7a1f2ef
--- /dev/null
+++ b/spec/ruby/core/encoding/aliases_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding.aliases" do
+ it "returns a Hash" do
+ Encoding.aliases.should be_an_instance_of(Hash)
+ end
+
+ it "has Strings as keys" do
+ Encoding.aliases.keys.each do |key|
+ key.should be_an_instance_of(String)
+ end
+ end
+
+ it "has Strings as values" do
+ Encoding.aliases.values.each do |value|
+ value.should be_an_instance_of(String)
+ end
+ end
+
+ it "has alias names as its keys" do
+ Encoding.aliases.key?('BINARY').should be_true
+ Encoding.aliases.key?('ASCII').should be_true
+ end
+
+ it "has the names of the aliased encoding as its values" do
+ Encoding.aliases['BINARY'].should == 'ASCII-8BIT'
+ Encoding.aliases['ASCII'].should == 'US-ASCII'
+ end
+
+ it "has an 'external' key with the external default encoding as its value" do
+ Encoding.aliases['external'].should == Encoding.default_external.name
+ end
+
+ it "has a 'locale' key and its value equals to the name of the encoding finded by the locale charmap" do
+ Encoding.aliases['locale'].should == Encoding.find(Encoding.locale_charmap).name
+ end
+
+ it "only contains valid aliased encodings" do
+ Encoding.aliases.each do |aliased, original|
+ Encoding.find(aliased).should == Encoding.find(original)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/ascii_compatible_spec.rb b/spec/ruby/core/encoding/ascii_compatible_spec.rb
new file mode 100644
index 0000000000..31ac75302e
--- /dev/null
+++ b/spec/ruby/core/encoding/ascii_compatible_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding#ascii_compatible?" do
+ it "returns true if self represents an ASCII-compatible encoding" do
+ Encoding::UTF_8.ascii_compatible?.should be_true
+ end
+
+ it "returns false if self does not represent an ASCII-compatible encoding" do
+ Encoding::UTF_16LE.ascii_compatible?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/compatible_spec.rb b/spec/ruby/core/encoding/compatible_spec.rb
new file mode 100644
index 0000000000..f35120325c
--- /dev/null
+++ b/spec/ruby/core/encoding/compatible_spec.rb
@@ -0,0 +1,381 @@
+# -*- encoding: ascii-8bit -*-
+
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ # TODO: add IO
+
+ describe "Encoding.compatible? String, String" do
+ describe "when the first's Encoding is valid US-ASCII" do
+ before :each do
+ @str = "abc".force_encoding Encoding::US_ASCII
+ end
+
+ it "returns US-ASCII when the second's is US-ASCII" do
+ Encoding.compatible?(@str, "def".encode("us-ascii")).should == Encoding::US_ASCII
+ end
+
+ it "returns US-ASCII if the second String is ASCII-8BIT and ASCII only" do
+ Encoding.compatible?(@str, "\x7f").should == Encoding::US_ASCII
+ end
+
+ it "returns ASCII-8BIT if the second String is ASCII-8BIT but not ASCII only" do
+ Encoding.compatible?(@str, "\xff").should == Encoding::ASCII_8BIT
+ end
+
+ it "returns US-ASCII if the second String is UTF-8 and ASCII only" do
+ Encoding.compatible?(@str, "\x7f".encode("utf-8")).should == Encoding::US_ASCII
+ end
+
+ it "returns UTF-8 if the second String is UTF-8 but not ASCII only" do
+ Encoding.compatible?(@str, "\u3042".encode("utf-8")).should == Encoding::UTF_8
+ end
+ end
+
+ describe "when the first's Encoding is ASCII compatible and ASCII only" do
+ it "returns the first's Encoding if the second is ASCII compatible and ASCII only" do
+ [ [Encoding, "abc".force_encoding("UTF-8"), "123".force_encoding("Shift_JIS"), Encoding::UTF_8],
+ [Encoding, "123".force_encoding("Shift_JIS"), "abc".force_encoding("UTF-8"), Encoding::Shift_JIS]
+ ].should be_computed_by(:compatible?)
+ end
+
+ it "returns the first's Encoding if the second is ASCII compatible and ASCII only" do
+ [ [Encoding, "abc".force_encoding("ASCII-8BIT"), "123".force_encoding("US-ASCII"), Encoding::ASCII_8BIT],
+ [Encoding, "123".force_encoding("US-ASCII"), "abc".force_encoding("ASCII-8BIT"), Encoding::US_ASCII]
+ ].should be_computed_by(:compatible?)
+ end
+
+ it "returns the second's Encoding if the second is ASCII compatible but not ASCII only" do
+ [ [Encoding, "abc".force_encoding("UTF-8"), "\xff".force_encoding("Shift_JIS"), Encoding::Shift_JIS],
+ [Encoding, "123".force_encoding("Shift_JIS"), "\xff".force_encoding("UTF-8"), Encoding::UTF_8],
+ [Encoding, "abc".force_encoding("ASCII-8BIT"), "\xff".force_encoding("US-ASCII"), Encoding::US_ASCII],
+ [Encoding, "123".force_encoding("US-ASCII"), "\xff".force_encoding("ASCII-8BIT"), Encoding::ASCII_8BIT],
+ ].should be_computed_by(:compatible?)
+ end
+
+ it "returns nil if the second's Encoding is not ASCII compatible" do
+ a = "abc".force_encoding("UTF-8")
+ b = "123".force_encoding("UTF-16LE")
+ Encoding.compatible?(a, b).should be_nil
+ end
+ end
+
+ describe "when the first's Encoding is ASCII compatible but not ASCII only" do
+ it "returns the first's Encoding if the second's is valid US-ASCII" do
+ Encoding.compatible?("\xff", "def".encode("us-ascii")).should == Encoding::ASCII_8BIT
+ end
+
+ it "returns the first's Encoding if the second's is UTF-8 and ASCII only" do
+ Encoding.compatible?("\xff", "\u{7f}".encode("utf-8")).should == Encoding::ASCII_8BIT
+ end
+
+ it "returns nil if the second encoding is ASCII compatible but neither String's encoding is ASCII only" do
+ Encoding.compatible?("\xff", "\u3042".encode("utf-8")).should be_nil
+ end
+ end
+
+ describe "when the first's Encoding is not ASCII compatible" do
+ before :each do
+ @str = "abc".force_encoding Encoding::UTF_7
+ end
+
+ it "returns nil when the second String is US-ASCII" do
+ Encoding.compatible?(@str, "def".encode("us-ascii")).should be_nil
+ end
+
+ it "returns nil when the second String is ASCII-8BIT and ASCII only" do
+ Encoding.compatible?(@str, "\x7f").should be_nil
+ end
+
+ it "returns nil when the second String is ASCII-8BIT but not ASCII only" do
+ Encoding.compatible?(@str, "\xff").should be_nil
+ end
+
+ it "returns the Encoding when the second's Encoding is not ASCII compatible but the same as the first's Encoding" do
+ encoding = Encoding.compatible?(@str, "def".force_encoding("utf-7"))
+ encoding.should == Encoding::UTF_7
+ end
+ end
+
+ describe "when the first's Encoding is invalid" do
+ before :each do
+ @str = "\xff".force_encoding Encoding::UTF_8
+ end
+
+ it "returns the first's Encoding when the second's Encoding is US-ASCII" do
+ Encoding.compatible?(@str, "def".encode("us-ascii")).should == Encoding::UTF_8
+ end
+
+ it "returns the first's Encoding when the second String is ASCII only" do
+ Encoding.compatible?(@str, "\x7f").should == Encoding::UTF_8
+ end
+
+ it "returns nil when the second's Encoding is ASCII-8BIT but not ASCII only" do
+ Encoding.compatible?(@str, "\xff").should be_nil
+ end
+
+ it "returns nil when the second's Encoding is invalid and ASCII only" do
+ Encoding.compatible?(@str, "\x7f".force_encoding("utf-16be")).should be_nil
+ end
+
+ it "returns nil when the second's Encoding is invalid and not ASCII only" do
+ Encoding.compatible?(@str, "\xff".force_encoding("utf-16be")).should be_nil
+ end
+
+ it "returns the Encoding when the second's Encoding is invalid but the same as the first" do
+ Encoding.compatible?(@str, @str).should == Encoding::UTF_8
+ end
+ end
+
+ describe "when the first String is empty and the second is not" do
+ describe "and the first's Encoding is ASCII compatible" do
+ before :each do
+ @str = "".force_encoding("utf-8")
+ end
+
+ it "returns the first's encoding when the second String is ASCII only" do
+ Encoding.compatible?(@str, "def".encode("us-ascii")).should == Encoding::UTF_8
+ end
+
+ it "returns the second's encoding when the second String is not ASCII only" do
+ Encoding.compatible?(@str, "def".encode("utf-32le")).should == Encoding::UTF_32LE
+ end
+ end
+
+ describe "when the first's Encoding is not ASCII compatible" do
+ before :each do
+ @str = "".force_encoding Encoding::UTF_7
+ end
+
+ it "returns the second string's encoding" do
+ Encoding.compatible?(@str, "def".encode("us-ascii")).should == Encoding::US_ASCII
+ end
+ end
+ end
+
+ describe "when the second String is empty" do
+ before :each do
+ @str = "abc".force_encoding("utf-7")
+ end
+
+ it "returns the first Encoding" do
+ Encoding.compatible?(@str, "").should == Encoding::UTF_7
+ end
+ end
+ end
+
+ describe "Encoding.compatible? String, Regexp" do
+ it "returns US-ASCII if both are US-ASCII" do
+ str = "abc".force_encoding("us-ascii")
+ Encoding.compatible?(str, /abc/).should == Encoding::US_ASCII
+ end
+
+ it "returns the String's Encoding if it is not US-ASCII but both are ASCII only" do
+ [ [Encoding, "abc", Encoding::ASCII_8BIT],
+ [Encoding, "abc".encode("utf-8"), Encoding::UTF_8],
+ [Encoding, "abc".encode("euc-jp"), Encoding::EUC_JP],
+ [Encoding, "abc".encode("shift_jis"), Encoding::Shift_JIS],
+ ].should be_computed_by(:compatible?, /abc/)
+ end
+
+ it "returns the String's Encoding if the String is not ASCII only" do
+ [ [Encoding, "\xff", Encoding::ASCII_8BIT],
+ [Encoding, "\u3042".encode("utf-8"), Encoding::UTF_8],
+ [Encoding, "\xa4\xa2".force_encoding("euc-jp"), Encoding::EUC_JP],
+ [Encoding, "\x82\xa0".force_encoding("shift_jis"), Encoding::Shift_JIS],
+ ].should be_computed_by(:compatible?, /abc/)
+ end
+ end
+
+ describe "Encoding.compatible? String, Symbol" do
+ it "returns US-ASCII if both are ASCII only" do
+ str = "abc".force_encoding("us-ascii")
+ Encoding.compatible?(str, :abc).should == Encoding::US_ASCII
+ end
+
+ it "returns the String's Encoding if it is not US-ASCII but both are ASCII only" do
+ [ [Encoding, "abc", Encoding::ASCII_8BIT],
+ [Encoding, "abc".encode("utf-8"), Encoding::UTF_8],
+ [Encoding, "abc".encode("euc-jp"), Encoding::EUC_JP],
+ [Encoding, "abc".encode("shift_jis"), Encoding::Shift_JIS],
+ ].should be_computed_by(:compatible?, :abc)
+ end
+
+ it "returns the String's Encoding if the String is not ASCII only" do
+ [ [Encoding, "\xff", Encoding::ASCII_8BIT],
+ [Encoding, "\u3042".encode("utf-8"), Encoding::UTF_8],
+ [Encoding, "\xa4\xa2".force_encoding("euc-jp"), Encoding::EUC_JP],
+ [Encoding, "\x82\xa0".force_encoding("shift_jis"), Encoding::Shift_JIS],
+ ].should be_computed_by(:compatible?, :abc)
+ end
+ end
+
+ describe "Encoding.compatible? String, Encoding" do
+ it "returns nil if the String's encoding is not ASCII compatible" do
+ Encoding.compatible?("abc".encode("utf-32le"), Encoding::US_ASCII).should be_nil
+ end
+
+ it "returns nil if the Encoding is not ASCII compatible" do
+ Encoding.compatible?("abc".encode("us-ascii"), Encoding::UTF_32LE).should be_nil
+ end
+
+ it "returns the String's encoding if the Encoding is US-ASCII" do
+ [ [Encoding, "\xff", Encoding::ASCII_8BIT],
+ [Encoding, "\u3042".encode("utf-8"), Encoding::UTF_8],
+ [Encoding, "\xa4\xa2".force_encoding("euc-jp"), Encoding::EUC_JP],
+ [Encoding, "\x82\xa0".force_encoding("shift_jis"), Encoding::Shift_JIS],
+ ].should be_computed_by(:compatible?, Encoding::US_ASCII)
+ end
+
+ it "returns the Encoding if the String's encoding is ASCII compatible and the String is ASCII only" do
+ str = "abc".encode("utf-8")
+
+ Encoding.compatible?(str, Encoding::ASCII_8BIT).should == Encoding::ASCII_8BIT
+ Encoding.compatible?(str, Encoding::UTF_8).should == Encoding::UTF_8
+ Encoding.compatible?(str, Encoding::EUC_JP).should == Encoding::EUC_JP
+ Encoding.compatible?(str, Encoding::Shift_JIS).should == Encoding::Shift_JIS
+ end
+
+ it "returns nil if the String's encoding is ASCII compatible but the string is not ASCII only" do
+ Encoding.compatible?("\u3042".encode("utf-8"), Encoding::ASCII_8BIT).should be_nil
+ end
+ end
+
+ describe "Encoding.compatible? Regexp, String" do
+ it "returns US-ASCII if both are US-ASCII" do
+ str = "abc".force_encoding("us-ascii")
+ Encoding.compatible?(/abc/, str).should == Encoding::US_ASCII
+ end
+
+ end
+
+ describe "Encoding.compatible? Regexp, Regexp" do
+ it "returns US-ASCII if both are US-ASCII" do
+ Encoding.compatible?(/abc/, /def/).should == Encoding::US_ASCII
+ end
+
+ it "returns the first's Encoding if it is not US-ASCII and not ASCII only" do
+ [ [Encoding, Regexp.new("\xff"), Encoding::ASCII_8BIT],
+ [Encoding, Regexp.new("\u3042".encode("utf-8")), Encoding::UTF_8],
+ [Encoding, Regexp.new("\xa4\xa2".force_encoding("euc-jp")), Encoding::EUC_JP],
+ [Encoding, Regexp.new("\x82\xa0".force_encoding("shift_jis")), Encoding::Shift_JIS],
+ ].should be_computed_by(:compatible?, /abc/)
+ end
+ end
+
+ describe "Encoding.compatible? Regexp, Symbol" do
+ it "returns US-ASCII if both are US-ASCII" do
+ Encoding.compatible?(/abc/, :def).should == Encoding::US_ASCII
+ end
+
+ it "returns the first's Encoding if it is not US-ASCII and not ASCII only" do
+ [ [Encoding, Regexp.new("\xff"), Encoding::ASCII_8BIT],
+ [Encoding, Regexp.new("\u3042".encode("utf-8")), Encoding::UTF_8],
+ [Encoding, Regexp.new("\xa4\xa2".force_encoding("euc-jp")), Encoding::EUC_JP],
+ [Encoding, Regexp.new("\x82\xa0".force_encoding("shift_jis")), Encoding::Shift_JIS],
+ ].should be_computed_by(:compatible?, /abc/)
+ end
+ end
+
+ describe "Encoding.compatible? Symbol, String" do
+ it "returns US-ASCII if both are ASCII only" do
+ str = "abc".force_encoding("us-ascii")
+ Encoding.compatible?(str, :abc).should == Encoding::US_ASCII
+ end
+ end
+
+ describe "Encoding.compatible? Symbol, Regexp" do
+ it "returns US-ASCII if both are US-ASCII" do
+ Encoding.compatible?(:abc, /def/).should == Encoding::US_ASCII
+ end
+
+ it "returns the Regexp's Encoding if it is not US-ASCII and not ASCII only" do
+ a = Regexp.new("\xff")
+ b = Regexp.new("\u3042".encode("utf-8"))
+ c = Regexp.new("\xa4\xa2".force_encoding("euc-jp"))
+ d = Regexp.new("\x82\xa0".force_encoding("shift_jis"))
+
+ [ [Encoding, :abc, a, Encoding::ASCII_8BIT],
+ [Encoding, :abc, b, Encoding::UTF_8],
+ [Encoding, :abc, c, Encoding::EUC_JP],
+ [Encoding, :abc, d, Encoding::Shift_JIS],
+ ].should be_computed_by(:compatible?)
+ end
+ end
+
+ describe "Encoding.compatible? Symbol, Symbol" do
+ it "returns US-ASCII if both are US-ASCII" do
+ Encoding.compatible?(:abc, :def).should == Encoding::US_ASCII
+ end
+
+ it "returns the first's Encoding if it is not ASCII only" do
+ [ [Encoding, "\xff".to_sym, Encoding::ASCII_8BIT],
+ [Encoding, "\u3042".encode("utf-8").to_sym, Encoding::UTF_8],
+ [Encoding, "\xa4\xa2".force_encoding("euc-jp").to_sym, Encoding::EUC_JP],
+ [Encoding, "\x82\xa0".force_encoding("shift_jis").to_sym, Encoding::Shift_JIS],
+ ].should be_computed_by(:compatible?, :abc)
+ end
+ end
+
+ describe "Encoding.compatible? Encoding, Encoding" do
+ it "returns nil if one of the encodings is a dummy encoding" do
+ [ [Encoding, Encoding::UTF_7, Encoding::US_ASCII, nil],
+ [Encoding, Encoding::US_ASCII, Encoding::UTF_7, nil],
+ [Encoding, Encoding::EUC_JP, Encoding::UTF_7, nil],
+ [Encoding, Encoding::UTF_7, Encoding::EUC_JP, nil],
+ [Encoding, Encoding::UTF_7, Encoding::ASCII_8BIT, nil],
+ [Encoding, Encoding::ASCII_8BIT, Encoding::UTF_7, nil],
+ ].should be_computed_by(:compatible?)
+ end
+
+ it "returns nil if one of the encodings is not US-ASCII" do
+ [ [Encoding, Encoding::UTF_8, Encoding::ASCII_8BIT, nil],
+ [Encoding, Encoding::ASCII_8BIT, Encoding::UTF_8, nil],
+ [Encoding, Encoding::ASCII_8BIT, Encoding::EUC_JP, nil],
+ [Encoding, Encoding::Shift_JIS, Encoding::EUC_JP, nil],
+ ].should be_computed_by(:compatible?)
+ end
+
+ it "returns the first if the second is US-ASCII" do
+ [ [Encoding, Encoding::UTF_8, Encoding::US_ASCII, Encoding::UTF_8],
+ [Encoding, Encoding::EUC_JP, Encoding::US_ASCII, Encoding::EUC_JP],
+ [Encoding, Encoding::Shift_JIS, Encoding::US_ASCII, Encoding::Shift_JIS],
+ [Encoding, Encoding::ASCII_8BIT, Encoding::US_ASCII, Encoding::ASCII_8BIT],
+ ].should be_computed_by(:compatible?)
+ end
+
+ it "returns the Encoding if both are the same" do
+ [ [Encoding, Encoding::UTF_8, Encoding::UTF_8, Encoding::UTF_8],
+ [Encoding, Encoding::US_ASCII, Encoding::US_ASCII, Encoding::US_ASCII],
+ [Encoding, Encoding::ASCII_8BIT, Encoding::ASCII_8BIT, Encoding::ASCII_8BIT],
+ [Encoding, Encoding::UTF_7, Encoding::UTF_7, Encoding::UTF_7],
+ ].should be_computed_by(:compatible?)
+ end
+ end
+
+ describe "Encoding.compatible? Object, Object" do
+ it "returns nil for Object, String" do
+ Encoding.compatible?(Object.new, "abc").should be_nil
+ end
+
+ it "returns nil for Object, Regexp" do
+ Encoding.compatible?(Object.new, /./).should be_nil
+ end
+
+ it "returns nil for Object, Symbol" do
+ Encoding.compatible?(Object.new, :sym).should be_nil
+ end
+
+ it "returns nil for String, Object" do
+ Encoding.compatible?("abc", Object.new).should be_nil
+ end
+
+ it "returns nil for Regexp, Object" do
+ Encoding.compatible?(/./, Object.new).should be_nil
+ end
+
+ it "returns nil for Symbol, Object" do
+ Encoding.compatible?(:sym, Object.new).should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/converter/asciicompat_encoding_spec.rb b/spec/ruby/core/encoding/converter/asciicompat_encoding_spec.rb
new file mode 100644
index 0000000000..f06a138ba6
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/asciicompat_encoding_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding::Converter.asciicompat_encoding" do
+ it "accepts an encoding name as a String argument" do
+ lambda { Encoding::Converter.asciicompat_encoding('UTF-8') }.
+ should_not raise_error
+ end
+
+ it "coerces non-String/Encoding objects with #to_str" do
+ str = mock('string')
+ str.should_receive(:to_str).at_least(1).times.and_return('string')
+ Encoding::Converter.asciicompat_encoding(str)
+ end
+
+ it "accepts an Encoding object as an argument" do
+ Encoding::Converter.
+ asciicompat_encoding(Encoding.find("ISO-2022-JP")).
+ should == Encoding::Converter.asciicompat_encoding("ISO-2022-JP")
+ end
+
+ it "returns a corresponding ASCII compatible encoding for ASCII-incompatible encodings" do
+ Encoding::Converter.asciicompat_encoding('UTF-16BE').should == Encoding::UTF_8
+ Encoding::Converter.asciicompat_encoding("ISO-2022-JP").should == Encoding.find("stateless-ISO-2022-JP")
+ end
+
+ it "returns nil when the given encoding is ASCII compatible" do
+ Encoding::Converter.asciicompat_encoding('ASCII').should be_nil
+ Encoding::Converter.asciicompat_encoding('UTF-8').should be_nil
+ end
+
+ it "handles encoding names who resolve to nil encodings" do
+ internal = Encoding.default_internal
+ Encoding.default_internal = nil
+ Encoding::Converter.asciicompat_encoding('internal').should be_nil
+ Encoding.default_internal = internal
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/converter/constants_spec.rb b/spec/ruby/core/encoding/converter/constants_spec.rb
new file mode 100644
index 0000000000..9a03f61baf
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/constants_spec.rb
@@ -0,0 +1,133 @@
+require_relative '../../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding::Converter::INVALID_MASK" do
+ it "exists" do
+ Encoding::Converter.should have_constant(:INVALID_MASK)
+ end
+
+ it "has a Fixnum value" do
+ Encoding::Converter::INVALID_MASK.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe "Encoding::Converter::INVALID_REPLACE" do
+ it "exists" do
+ Encoding::Converter.should have_constant(:INVALID_REPLACE)
+ end
+
+ it "has a Fixnum value" do
+ Encoding::Converter::INVALID_REPLACE.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe "Encoding::Converter::UNDEF_MASK" do
+ it "exists" do
+ Encoding::Converter.should have_constant(:UNDEF_MASK)
+ end
+
+ it "has a Fixnum value" do
+ Encoding::Converter::UNDEF_MASK.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe "Encoding::Converter::UNDEF_REPLACE" do
+ it "exists" do
+ Encoding::Converter.should have_constant(:UNDEF_REPLACE)
+ end
+
+ it "has a Fixnum value" do
+ Encoding::Converter::UNDEF_REPLACE.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe "Encoding::Converter::UNDEF_HEX_CHARREF" do
+ it "exists" do
+ Encoding::Converter.should have_constant(:UNDEF_HEX_CHARREF)
+ end
+
+ it "has a Fixnum value" do
+ Encoding::Converter::UNDEF_HEX_CHARREF.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe "Encoding::Converter::PARTIAL_INPUT" do
+ it "exists" do
+ Encoding::Converter.should have_constant(:PARTIAL_INPUT)
+ end
+
+ it "has a Fixnum value" do
+ Encoding::Converter::PARTIAL_INPUT.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe "Encoding::Converter::AFTER_OUTPUT" do
+ it "exists" do
+ Encoding::Converter.should have_constant(:AFTER_OUTPUT)
+ end
+
+ it "has a Fixnum value" do
+ Encoding::Converter::AFTER_OUTPUT.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe "Encoding::Converter::UNIVERSAL_NEWLINE_DECORATOR" do
+ it "exists" do
+ Encoding::Converter.should have_constant(:UNIVERSAL_NEWLINE_DECORATOR)
+ end
+
+ it "has a Fixnum value" do
+ Encoding::Converter::UNIVERSAL_NEWLINE_DECORATOR.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe "Encoding::Converter::CRLF_NEWLINE_DECORATOR" do
+ it "exists" do
+ Encoding::Converter.should have_constant(:CRLF_NEWLINE_DECORATOR)
+ end
+
+ it "has a Fixnum value" do
+ Encoding::Converter::CRLF_NEWLINE_DECORATOR.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe "Encoding::Converter::CR_NEWLINE_DECORATOR" do
+ it "exists" do
+ Encoding::Converter.should have_constant(:CR_NEWLINE_DECORATOR)
+ end
+
+ it "has a Fixnum value" do
+ Encoding::Converter::CR_NEWLINE_DECORATOR.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe "Encoding::Converter::XML_TEXT_DECORATOR" do
+ it "exists" do
+ Encoding::Converter.should have_constant(:XML_TEXT_DECORATOR)
+ end
+
+ it "has a Fixnum value" do
+ Encoding::Converter::XML_TEXT_DECORATOR.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe "Encoding::Converter::XML_ATTR_CONTENT_DECORATOR" do
+ it "exists" do
+ Encoding::Converter.should have_constant(:XML_ATTR_CONTENT_DECORATOR)
+ end
+
+ it "has a Fixnum value" do
+ Encoding::Converter::XML_ATTR_CONTENT_DECORATOR.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe "Encoding::Converter::XML_ATTR_QUOTE_DECORATOR" do
+ it "exists" do
+ Encoding::Converter.should have_constant(:XML_ATTR_QUOTE_DECORATOR)
+ end
+
+ it "has a Fixnum value" do
+ Encoding::Converter::XML_ATTR_QUOTE_DECORATOR.should be_an_instance_of(Fixnum)
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/converter/convert_spec.rb b/spec/ruby/core/encoding/converter/convert_spec.rb
new file mode 100644
index 0000000000..525e83a17f
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/convert_spec.rb
@@ -0,0 +1,47 @@
+# -*- encoding: binary -*-
+require_relative '../../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding::Converter#convert" do
+ it "returns a String" do
+ ec = Encoding::Converter.new('ascii', 'utf-8')
+ ec.convert('glark').should be_an_instance_of(String)
+ end
+
+ it "sets the encoding of the result to the target encoding" do
+ ec = Encoding::Converter.new('ascii', 'utf-8')
+ str = 'glark'.force_encoding('ascii')
+ ec.convert(str).encoding.should == Encoding::UTF_8
+ end
+
+ it "transcodes the given String to the target encoding" do
+ ec = Encoding::Converter.new("utf-8", "euc-jp")
+ ec.convert("\u3042".force_encoding('UTF-8')).should == \
+ "\xA4\xA2".force_encoding('EUC-JP')
+ end
+
+ it "allows Strings of different encodings to the source encoding" do
+ ec = Encoding::Converter.new('ascii', 'utf-8')
+ str = 'glark'.force_encoding('SJIS')
+ ec.convert(str).encoding.should == Encoding::UTF_8
+ end
+
+ it "reuses the given encoding pair if called multiple times" do
+ ec = Encoding::Converter.new('ascii', 'SJIS')
+ ec.convert('a'.force_encoding('ASCII')).should == 'a'.force_encoding('SJIS')
+ ec.convert('b'.force_encoding('ASCII')).should == 'b'.force_encoding('SJIS')
+ end
+
+ it "raises UndefinedConversionError if the String contains characters invalid for the target encoding" do
+ ec = Encoding::Converter.new('UTF-8', Encoding.find('macCyrillic'))
+ lambda { ec.convert("\u{6543}".force_encoding('UTF-8')) }.should \
+ raise_error(Encoding::UndefinedConversionError)
+ end
+
+ it "raises an ArgumentError if called on a finished stream" do
+ ec = Encoding::Converter.new('UTF-8', Encoding.find('macCyrillic'))
+ ec.finish
+ lambda { ec.convert("\u{65}") }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/converter/convpath_spec.rb b/spec/ruby/core/encoding/converter/convpath_spec.rb
new file mode 100644
index 0000000000..e41a6c4205
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/convpath_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding::Converter#convpath" do
+ it "returns an Array with a single element if there is a direct converter" do
+ cp = Encoding::Converter.new('ASCII', 'UTF-8').convpath
+ cp.should == [[Encoding::US_ASCII, Encoding::UTF_8]]
+ end
+
+ it "returns multiple encoding pairs when direct conversion is impossible" do
+ cp = Encoding::Converter.new('ascii','Big5').convpath
+ cp.should == [
+ [Encoding::US_ASCII, Encoding::UTF_8],
+ [Encoding::UTF_8, Encoding::Big5]
+ ]
+ end
+
+ it "indicates if crlf_newline conversion would occur" do
+ ec = Encoding::Converter.new("ISo-8859-1", "EUC-JP", {crlf_newline: true})
+ ec.convpath.last.should == "crlf_newline"
+
+ ec = Encoding::Converter.new("ASCII", "UTF-8", {crlf_newline: false})
+ ec.convpath.last.should_not == "crlf_newline"
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/converter/destination_encoding_spec.rb b/spec/ruby/core/encoding/converter/destination_encoding_spec.rb
new file mode 100644
index 0000000000..2d0f8e0697
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/destination_encoding_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding::Converter#destination_encoding" do
+ it "returns the destination encoding as an Encoding object" do
+ ec = Encoding::Converter.new('ASCII','Big5')
+ ec.destination_encoding.should == Encoding::BIG5
+
+ ec = Encoding::Converter.new('SJIS','EUC-JP')
+ ec.destination_encoding.should == Encoding::EUC_JP
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/converter/finish_spec.rb b/spec/ruby/core/encoding/converter/finish_spec.rb
new file mode 100644
index 0000000000..917f3d2912
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/finish_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding::Converter#finish" do
+ before :each do
+ @ec = Encoding::Converter.new("utf-8", "iso-2022-jp")
+ end
+
+ it "returns a String" do
+ @ec.convert('foo')
+ @ec.finish.should be_an_instance_of(String)
+ end
+
+ it "returns an empty String if there is nothing more to convert" do
+ @ec.convert("glark")
+ @ec.finish.should == ""
+ end
+
+ it "returns the last part of the converted String if it hasn't already" do
+ @ec.convert("\u{9999}").should == "\e$B9a".force_encoding('iso-2022-jp')
+ @ec.finish.should == "\e(B".force_encoding('iso-2022-jp')
+ end
+
+ it "returns a String in the destination encoding" do
+ @ec.convert("glark")
+ @ec.finish.encoding.should == Encoding::ISO2022_JP
+ end
+
+ it "returns an empty String if self was not given anything to convert" do
+ @ec.finish.should == ""
+ end
+
+ it "returns an empty String on subsequent invocations" do
+ @ec.finish.should == ""
+ @ec.finish.should == ""
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/converter/insert_output_spec.rb b/spec/ruby/core/encoding/converter/insert_output_spec.rb
new file mode 100644
index 0000000000..1346adde1e
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/insert_output_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Encoding::Converter#insert_output" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/encoding/converter/inspect_spec.rb b/spec/ruby/core/encoding/converter/inspect_spec.rb
new file mode 100644
index 0000000000..3170ee451f
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/inspect_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+
+describe "Encoding::Converter#inspect" do
+ it "includes the source and destination encodings in the return value" do
+ source = Encoding::UTF_8
+ destination = Encoding::UTF_16LE
+
+ output = "#<Encoding::Converter: #{source.name} to #{destination.name}>"
+
+ x = Encoding::Converter.new(source, destination)
+ x.inspect.should == output
+ end
+end
diff --git a/spec/ruby/core/encoding/converter/last_error_spec.rb b/spec/ruby/core/encoding/converter/last_error_spec.rb
new file mode 100644
index 0000000000..7275b31180
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/last_error_spec.rb
@@ -0,0 +1,93 @@
+# -*- encoding: binary -*-
+require_relative '../../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding::Converter#last_error" do
+ it "returns nil when the no conversion has been attempted" do
+ ec = Encoding::Converter.new('ascii','utf-8')
+ ec.last_error.should be_nil
+ end
+
+ it "returns nil when the last conversion did not produce an error" do
+ ec = Encoding::Converter.new('ascii','utf-8')
+ ec.convert('a'.force_encoding('ascii'))
+ ec.last_error.should be_nil
+ end
+
+ it "returns nil when #primitive_convert last returned :destination_buffer_full" do
+ ec = Encoding::Converter.new("utf-8", "iso-2022-jp")
+ ec.primitive_convert("\u{9999}", "", 0, 0, partial_input: false) \
+ .should == :destination_buffer_full
+ ec.last_error.should be_nil
+ end
+
+ it "returns nil when #primitive_convert last returned :finished" do
+ ec = Encoding::Converter.new("utf-8", "iso-8859-1")
+ ec.primitive_convert("glark".force_encoding('utf-8'),"").should == :finished
+ ec.last_error.should be_nil
+ end
+
+ it "returns nil if the last conversion succeeded but the penultimate failed" do
+ ec = Encoding::Converter.new("utf-8", "iso-8859-1")
+ ec.primitive_convert("\xf1abcd","").should == :invalid_byte_sequence
+ ec.primitive_convert("glark".force_encoding('utf-8'),"").should == :finished
+ ec.last_error.should be_nil
+ end
+
+ it "returns an Encoding::InvalidByteSequenceError when #primitive_convert last returned :invalid_byte_sequence" do
+ ec = Encoding::Converter.new("utf-8", "iso-8859-1")
+ ec.primitive_convert("\xf1abcd","").should == :invalid_byte_sequence
+ ec.last_error.should be_an_instance_of(Encoding::InvalidByteSequenceError)
+ end
+
+ it "returns an Encoding::UndefinedConversionError when #primitive_convert last returned :undefined_conversion" do
+ ec = Encoding::Converter.new("utf-8", "iso-8859-1")
+ ec.primitive_convert("\u{9876}","").should == :undefined_conversion
+ ec.last_error.should be_an_instance_of(Encoding::UndefinedConversionError)
+ end
+
+ it "returns an Encoding::InvalidByteSequenceError when #primitive_convert last returned :incomplete_input" do
+ ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
+ ec.primitive_convert("\xa4", "", nil, 10).should == :incomplete_input
+ ec.last_error.should be_an_instance_of(Encoding::InvalidByteSequenceError)
+ end
+
+ it "returns an Encoding::InvalidByteSequenceError when the last call to #convert produced one" do
+ ec = Encoding::Converter.new("utf-8", "iso-8859-1")
+ exception = nil
+ -> {
+ ec.convert("\xf1abcd")
+ }.should raise_error(Encoding::InvalidByteSequenceError) { |e|
+ exception = e
+ }
+ ec.last_error.should be_an_instance_of(Encoding::InvalidByteSequenceError)
+ ec.last_error.message.should == exception.message
+ end
+
+ it "returns an Encoding::UndefinedConversionError when the last call to #convert produced one" do
+ ec = Encoding::Converter.new("utf-8", "iso-8859-1")
+ exception = nil
+ -> {
+ ec.convert("\u{9899}")
+ }.should raise_error(Encoding::UndefinedConversionError) { |e|
+ exception = e
+ }
+ ec.last_error.should be_an_instance_of(Encoding::UndefinedConversionError)
+ ec.last_error.message.should == exception.message
+ ec.last_error.message.should include "from UTF-8 to ISO-8859-1"
+ end
+
+ it "returns the last error of #convert with a message showing the transcoding path" do
+ ec = Encoding::Converter.new("iso-8859-1", "Big5")
+ exception = nil
+ -> {
+ ec.convert("\xE9") # é in ISO-8859-1
+ }.should raise_error(Encoding::UndefinedConversionError) { |e|
+ exception = e
+ }
+ ec.last_error.should be_an_instance_of(Encoding::UndefinedConversionError)
+ ec.last_error.message.should == exception.message
+ ec.last_error.message.should include "from ISO-8859-1 to UTF-8 to Big5"
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/converter/new_spec.rb b/spec/ruby/core/encoding/converter/new_spec.rb
new file mode 100644
index 0000000000..08c47daefc
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/new_spec.rb
@@ -0,0 +1,121 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding::Converter.new" do
+ it "accepts a String for the source encoding" do
+ conv = Encoding::Converter.new("us-ascii", "utf-8")
+ conv.source_encoding.should == Encoding::US_ASCII
+ end
+
+ it "accepts a String for the destination encoding" do
+ conv = Encoding::Converter.new("us-ascii", "utf-8")
+ conv.destination_encoding.should == Encoding::UTF_8
+ end
+
+ it "accepts an Encoding object for the source encoding" do
+ conv = Encoding::Converter.new(Encoding::US_ASCII, "utf-8")
+ conv.source_encoding.should == Encoding::US_ASCII
+ end
+
+ it "accepts an Encoding object for the destination encoding" do
+ conv = Encoding::Converter.new("us-ascii", Encoding::UTF_8)
+ conv.destination_encoding.should == Encoding::UTF_8
+ end
+
+ it "raises an Encoding::ConverterNotFoundError if both encodings are the same" do
+ lambda do
+ Encoding::Converter.new "utf-8", "utf-8"
+ end.should raise_error(Encoding::ConverterNotFoundError)
+ end
+
+ it "calls #to_str to convert the source encoding argument to an encoding name" do
+ enc = mock("us-ascii")
+ enc.should_receive(:to_str).and_return("us-ascii")
+ conv = Encoding::Converter.new(enc, "utf-8")
+ conv.source_encoding.should == Encoding::US_ASCII
+ end
+
+ it "calls #to_str to convert the destination encoding argument to an encoding name" do
+ enc = mock("utf-8")
+ enc.should_receive(:to_str).and_return("utf-8")
+ conv = Encoding::Converter.new("us-ascii", enc)
+ conv.destination_encoding.should == Encoding::UTF_8
+ end
+
+ it "sets replacement from the options Hash" do
+ conv = Encoding::Converter.new("us-ascii", "utf-8", replace: "fubar")
+ conv.replacement.should == "fubar"
+ end
+
+ it "calls #to_hash to convert the options argument to a Hash if not a Fixnum" do
+ opts = mock("encoding converter options")
+ opts.should_receive(:to_hash).and_return({ replace: "fubar" })
+ conv = Encoding::Converter.new("us-ascii", "utf-8", opts)
+ conv.replacement.should == "fubar"
+ end
+
+ it "calls #to_str to convert the replacement object to a String" do
+ obj = mock("encoding converter replacement")
+ obj.should_receive(:to_str).and_return("fubar")
+ conv = Encoding::Converter.new("us-ascii", "utf-8", replace: obj)
+ conv.replacement.should == "fubar"
+ end
+
+ it "raises a TypeError if #to_str does not return a String" do
+ obj = mock("encoding converter replacement")
+ obj.should_receive(:to_str).and_return(1)
+
+ lambda do
+ Encoding::Converter.new("us-ascii", "utf-8", replace: obj)
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed true for the replacement object" do
+ lambda do
+ Encoding::Converter.new("us-ascii", "utf-8", replace: true)
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed false for the replacement object" do
+ lambda do
+ Encoding::Converter.new("us-ascii", "utf-8", replace: false)
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed a Fixnum for the replacement object" do
+ lambda do
+ Encoding::Converter.new("us-ascii", "utf-8", replace: 1)
+ end.should raise_error(TypeError)
+ end
+
+ it "accepts an empty String for the replacement object" do
+ conv = Encoding::Converter.new("us-ascii", "utf-8", replace: "")
+ conv.replacement.should == ""
+ end
+
+ describe "when passed nil for the replacement object" do
+ describe "when the destination encoding is not UTF-8" do
+ it "sets the replacement String to '?'" do
+ conv = Encoding::Converter.new("us-ascii", "ascii-8bit", replace: nil)
+ conv.replacement.should == "?"
+ end
+
+ it "sets the replacement String encoding to US-ASCII" do
+ conv = Encoding::Converter.new("us-ascii", "ascii-8bit", replace: nil)
+ conv.replacement.encoding.should == Encoding::US_ASCII
+ end
+
+ it "sets the replacement String to '\\uFFFD'" do
+ conv = Encoding::Converter.new("us-ascii", "utf-8", replace: nil)
+ conv.replacement.should == "\u{fffd}".force_encoding("utf-8")
+ end
+
+ it "sets the replacement String encoding to UTF-8" do
+ conv = Encoding::Converter.new("us-ascii", "utf-8", replace: nil)
+ conv.replacement.encoding.should == Encoding::UTF_8
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/converter/primitive_convert_spec.rb b/spec/ruby/core/encoding/converter/primitive_convert_spec.rb
new file mode 100644
index 0000000000..182e321e5c
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/primitive_convert_spec.rb
@@ -0,0 +1,213 @@
+# -*- encoding: binary -*-
+require_relative '../../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding::Converter#primitive_convert" do
+ before :each do
+ @ec = Encoding::Converter.new("utf-8", "iso-8859-1")
+ end
+
+ it "accepts a nil source buffer" do
+ lambda { @ec.primitive_convert(nil,"") }.should_not raise_error
+ end
+
+ it "accepts a String as the source buffer" do
+ lambda { @ec.primitive_convert("","") }.should_not raise_error
+ end
+
+ it "accepts nil for the destination byte offset" do
+ lambda { @ec.primitive_convert("","", nil) }.should_not raise_error
+ end
+
+ it "accepts an integer for the destination byte offset" do
+ lambda { @ec.primitive_convert("","a", 1) }.should_not raise_error
+ end
+
+ it "calls #to_int to convert the destination byte offset" do
+ offset = mock("encoding primitive_convert destination byte offset")
+ offset.should_receive(:to_int).and_return(2)
+ @ec.primitive_convert("abc", result = " ", offset).should == :finished
+ result.should == " abc"
+ end
+
+ it "raises an ArgumentError if the destination byte offset is greater than the bytesize of the destination buffer" do
+ lambda { @ec.primitive_convert("","am", 0) }.should_not raise_error
+ lambda { @ec.primitive_convert("","am", 1) }.should_not raise_error
+ lambda { @ec.primitive_convert("","am", 2) }.should_not raise_error
+ lambda { @ec.primitive_convert("","am", 3) }.should raise_error(ArgumentError)
+ end
+
+ it "uses the destination byte offset to determine where to write the result in the destination buffer" do
+ dest = "aa"
+ @ec.primitive_convert("b",dest, nil, 0)
+ dest.should == "aa"
+
+ @ec.primitive_convert("b",dest, nil, 1)
+ dest.should == "aab"
+
+ @ec.primitive_convert("b",dest, nil, 2)
+ dest.should == "aabbb"
+ end
+
+ it "accepts nil for the destination bytesize" do
+ lambda { @ec.primitive_convert("","", nil, nil) }.should_not raise_error
+ end
+
+ it "accepts an integer for the destination bytesize" do
+ lambda { @ec.primitive_convert("","", nil, 0) }.should_not raise_error
+ end
+
+ it "allows a destination bytesize value greater than the bytesize of the source buffer" do
+ lambda { @ec.primitive_convert("am","", nil, 3) }.should_not raise_error
+ end
+
+ it "allows a destination bytesize value less than the bytesize of the source buffer" do
+ lambda { @ec.primitive_convert("am","", nil, 1) }.should_not raise_error
+ end
+
+ it "calls #to_int to convert the destination byte size" do
+ size = mock("encoding primitive_convert destination byte size")
+ size.should_receive(:to_int).and_return(2)
+ @ec.primitive_convert("abc", result = " ", 0, size).should == :destination_buffer_full
+ result.should == "ab"
+ end
+
+ it "uses destination bytesize as the maximum bytesize of the destination buffer" do
+ dest = ""
+ @ec.primitive_convert("glark", dest, nil, 1)
+ dest.bytesize.should == 1
+ end
+
+ it "allows a destination buffer of unlimited size if destination bytesize is nil" do
+ source = "glark".force_encoding('utf-8')
+ dest = ""
+ @ec.primitive_convert("glark", dest, nil, nil)
+ dest.bytesize.should == source.bytesize
+ end
+
+ it "accepts an options hash" do
+ @ec.primitive_convert("","",nil,nil, {after_output: true}).should == :finished
+ end
+
+ it "sets the destination buffer's encoding to the destination encoding if the conversion succeeded" do
+ dest = "".force_encoding('utf-8')
+ dest.encoding.should == Encoding::UTF_8
+ @ec.primitive_convert("\u{98}",dest).should == :finished
+ dest.encoding.should == Encoding::ISO_8859_1
+ end
+
+ it "sets the destination buffer's encoding to the destination encoding if the conversion failed" do
+ dest = "".force_encoding('utf-8')
+ dest.encoding.should == Encoding::UTF_8
+ @ec.primitive_convert("\u{9878}",dest).should == :undefined_conversion
+ dest.encoding.should == Encoding::ISO_8859_1
+ end
+
+ it "removes the undefined part from the source buffer when returning :undefined_conversion" do
+ dest = "".force_encoding('utf-8')
+ s = "\u{9878}abcd"
+ @ec.primitive_convert(s, dest).should == :undefined_conversion
+
+ s.should == "abcd"
+ end
+
+ it "returns :incomplete_input when source buffer ends unexpectedly and :partial_input isn't specified" do
+ ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
+ ec.primitive_convert("\xa4", "", nil, nil, partial_input: false).should == :incomplete_input
+ end
+
+ it "clears the source buffer when returning :incomplete_input" do
+ ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
+ s = "\xa4"
+ ec.primitive_convert(s, "").should == :incomplete_input
+
+ s.should == ""
+ end
+
+ it "returns :source_buffer_empty when source buffer ends unexpectedly and :partial_input is true" do
+ ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
+ ec.primitive_convert("\xa4", "", nil, nil, partial_input: true).should == :source_buffer_empty
+ end
+
+ it "clears the source buffer when returning :source_buffer_empty" do
+ ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
+ s = "\xa4"
+ ec.primitive_convert(s, "", nil, nil, partial_input: true).should == :source_buffer_empty
+
+ s.should == ""
+ end
+
+ it "returns :undefined_conversion when a character in the source buffer is not representable in the output encoding" do
+ @ec.primitive_convert("\u{9876}","").should == :undefined_conversion
+ end
+
+ it "returns :invalid_byte_sequence when an invalid byte sequence was found in the source buffer" do
+ @ec.primitive_convert("\xf1abcd","").should == :invalid_byte_sequence
+ end
+
+ it "removes consumed and erroneous bytes from the source buffer when returning :invalid_byte_sequence" do
+ ec = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_8_MAC)
+ s = "\xC3\xA1\x80\x80\xC3\xA1".force_encoding("utf-8")
+ dest = "".force_encoding("utf-8")
+ ec.primitive_convert(s, dest)
+
+ s.should == "\x80\xC3\xA1".force_encoding("utf-8")
+ end
+
+ it "returns :finished when the conversion succeeded" do
+ @ec.primitive_convert("glark".force_encoding('utf-8'),"").should == :finished
+ end
+
+ it "clears the source buffer when returning :finished" do
+ s = "glark".force_encoding('utf-8')
+ @ec.primitive_convert(s, "").should == :finished
+
+ s.should == ""
+ end
+
+ it "returns :destination_buffer_full when the destination buffer is too small" do
+ ec = Encoding::Converter.new("utf-8", "iso-2022-jp")
+ source = "\u{9999}"
+ destination_bytesize = source.bytesize - 1
+ ec.primitive_convert(source, "", 0, destination_bytesize) \
+ .should == :destination_buffer_full
+ source.should == ""
+ end
+
+ it "clears the source buffer when returning :destination_buffer_full" do
+ ec = Encoding::Converter.new("utf-8", "iso-2022-jp")
+ s = "\u{9999}"
+ destination_bytesize = s.bytesize - 1
+ ec.primitive_convert(s, "", 0, destination_bytesize).should == :destination_buffer_full
+
+ s.should == ""
+ end
+
+ it "keeps removing invalid bytes from the source buffer" do
+ ec = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_8_MAC)
+ s = "\x80\x80\x80"
+ dest = "".force_encoding(Encoding::UTF_8_MAC)
+
+ ec.primitive_convert(s, dest)
+ s.should == "\x80\x80"
+ ec.primitive_convert(s, dest)
+ s.should == "\x80"
+ ec.primitive_convert(s, dest)
+ s.should == ""
+ end
+
+ it "reuses read-again bytes after the first error" do
+ s = "\xf1abcd"
+ dest = ""
+
+ @ec.primitive_convert(s, dest).should == :invalid_byte_sequence
+ s.should == "bcd"
+ @ec.primitive_errinfo[4].should == "a"
+
+ @ec.primitive_convert(s, dest).should == :finished
+ s.should == ""
+
+ dest.should == "abcd"
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb b/spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb
new file mode 100644
index 0000000000..835e5517e4
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb
@@ -0,0 +1,70 @@
+# -*- encoding: binary -*-
+require_relative '../../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding::Converter#primitive_errinfo" do
+ it "returns [:source_buffer_empty,nil,nil,nil,nil] when no conversion has been attempted" do
+ ec = Encoding::Converter.new('ascii','utf-8')
+ ec.primitive_errinfo.should == [:source_buffer_empty, nil, nil, nil, nil]
+ end
+
+ it "returns [:finished,nil,nil,nil,nil] when #primitive_convert last returned :finished" do
+ ec = Encoding::Converter.new('ascii','utf-8')
+ ec.primitive_convert("a","").should == :finished
+ ec.primitive_errinfo.should == [:finished, nil, nil, nil, nil]
+ end
+
+ it "returns [:source_buffer_empty,nil,nil,nil, nil] when #convert last succeeded" do
+ ec = Encoding::Converter.new('ascii','utf-8')
+ ec.convert("a".force_encoding('ascii')).should == "a".force_encoding('utf-8')
+ ec.primitive_errinfo.should == [:source_buffer_empty, nil, nil, nil, nil]
+ end
+
+ it "returns [:destination_buffer_full,nil,nil,nil,nil] when #primitive_convert last returned :destination_buffer_full" do
+ ec = Encoding::Converter.new("utf-8", "iso-2022-jp")
+ ec.primitive_convert("\u{9999}", "", 0, 0, partial_input: false).should == :destination_buffer_full
+ ec.primitive_errinfo.should == [:destination_buffer_full, nil, nil, nil, nil]
+ end
+
+ it "returns the status of the last primitive conversion, even if it was successful and the previous one wasn't" do
+ ec = Encoding::Converter.new("utf-8", "iso-8859-1")
+ ec.primitive_convert("\xf1abcd","").should == :invalid_byte_sequence
+ ec.primitive_convert("glark".force_encoding('utf-8'),"").should == :finished
+ ec.primitive_errinfo.should == [:finished, nil, nil, nil, nil]
+ end
+
+ it "returns the state, source encoding, target encoding, and the erroneous bytes when #primitive_convert last returned :undefined_conversion" do
+ ec = Encoding::Converter.new("utf-8", "iso-8859-1")
+ ec.primitive_convert("\u{9876}","").should == :undefined_conversion
+ ec.primitive_errinfo.should ==
+ [:undefined_conversion, "UTF-8", "ISO-8859-1", "\xE9\xA1\xB6", ""]
+ end
+
+ it "returns the state, source encoding, target encoding, and erroneous bytes when #primitive_convert last returned :incomplete_input" do
+ ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
+ ec.primitive_convert("\xa4", "", nil, 10).should == :incomplete_input
+ ec.primitive_errinfo.should == [:incomplete_input, "EUC-JP", "UTF-8", "\xA4", ""]
+ end
+
+ it "returns the state, source encoding, target encoding, erroneous bytes, and the read-again bytes when #primitive_convert last returned :invalid_byte_sequence" do
+ ec = Encoding::Converter.new("utf-8", "iso-8859-1")
+ ec.primitive_convert("\xf1abcd","").should == :invalid_byte_sequence
+ ec.primitive_errinfo.should ==
+ [:invalid_byte_sequence, "UTF-8", "ISO-8859-1", "\xF1", "a"]
+ end
+
+ it "returns the state, source encoding, target encoding, erroneous bytes, and the read-again bytes when #convert last raised InvalidByteSequenceError" do
+ ec = Encoding::Converter.new("utf-8", "iso-8859-1")
+ lambda { ec.convert("\xf1abcd") }.should raise_error(Encoding::InvalidByteSequenceError)
+ ec.primitive_errinfo.should ==
+ [:invalid_byte_sequence, "UTF-8", "ISO-8859-1", "\xF1", "a"]
+ end
+
+ it "returns the state, source encoding, target encoding, erroneous bytes, and the read-again bytes when #finish last raised InvalidByteSequenceError" do
+ ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
+ ec.convert("\xa4")
+ lambda { ec.finish }.should raise_error(Encoding::InvalidByteSequenceError)
+ ec.primitive_errinfo.should == [:incomplete_input, "EUC-JP", "UTF-8", "\xA4", ""]
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/converter/putback_spec.rb b/spec/ruby/core/encoding/converter/putback_spec.rb
new file mode 100644
index 0000000000..119ac7b20e
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/putback_spec.rb
@@ -0,0 +1,49 @@
+# -*- encoding: binary -*-
+require_relative '../../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding::Converter#putback" do
+ before :each do
+ @ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
+ @ret = @ec.primitive_convert(@src="abc\xa1def", @dst="", nil, 10)
+ end
+
+ it "returns a String" do
+ @ec.putback.should be_an_instance_of(String)
+ end
+
+ it "returns a String in the source encoding" do
+ @ec.putback.encoding.should == Encoding::EUC_JP
+ end
+
+ it "returns the bytes buffered due to an :invalid_byte_sequence error" do
+ @ret.should == :invalid_byte_sequence
+ @ec.putback.should == 'd'
+ @ec.primitive_errinfo.last.should == 'd'
+ end
+
+ it "allows conversion to be resumed after an :invalid_byte_sequence" do
+ @src = @ec.putback + @src
+ @ret = @ec.primitive_convert(@src, @dst, nil, 10)
+ @ret.should == :finished
+ @dst.should == "abcdef"
+ @src.should == ""
+ end
+
+ it "returns an empty String when there are no more bytes to put back" do
+ @ec.putback
+ @ec.putback.should == ""
+ end
+
+ it "accepts an integer argument corresponding to the number of bytes to be put back" do
+ ec = Encoding::Converter.new("utf-16le", "iso-8859-1")
+ src = "\x00\xd8\x61\x00"
+ dst = ""
+ ec.primitive_convert(src, dst).should == :invalid_byte_sequence
+ ec.primitive_errinfo.should == [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "a\x00"]
+ ec.putback(1).should == "\x00".force_encoding("utf-16le")
+ ec.putback.should == "a".force_encoding("utf-16le")
+ ec.putback.should == ""
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/converter/replacement_spec.rb b/spec/ruby/core/encoding/converter/replacement_spec.rb
new file mode 100644
index 0000000000..506bf5c4af
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/replacement_spec.rb
@@ -0,0 +1,74 @@
+require_relative '../../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding::Converter#replacement" do
+ it "returns '?' in US-ASCII when the destination encoding is not UTF-8" do
+ ec = Encoding::Converter.new("utf-8", "us-ascii")
+ ec.replacement.should == "?"
+ ec.replacement.encoding.should == Encoding::US_ASCII
+
+ ec = Encoding::Converter.new("utf-8", "sjis")
+ ec.replacement.should == "?"
+ ec.replacement.encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns \\uFFFD when the destination encoding is UTF-8" do
+ ec = Encoding::Converter.new("us-ascii", "utf-8")
+ ec.replacement.should == "\u{fffd}".force_encoding('utf-8')
+ ec.replacement.encoding.should == Encoding::UTF_8
+ end
+ end
+
+ describe "Encoding::Converter#replacement=" do
+ it "accepts a String argument" do
+ ec = Encoding::Converter.new("utf-8", "us-ascii")
+ ec.replacement = "!"
+ ec.replacement.should == "!"
+ end
+
+ it "accepts a String argument of arbitrary length" do
+ ec = Encoding::Converter.new("utf-8", "us-ascii")
+ ec.replacement = "?!?" * 9999
+ ec.replacement.should == "?!?" * 9999
+ end
+
+ it "raises a TypeError if assigned a non-String argument" do
+ ec = Encoding::Converter.new("utf-8", "us-ascii")
+ lambda { ec.replacement = nil }.should raise_error(TypeError)
+ end
+
+ it "sets #replacement" do
+ ec = Encoding::Converter.new("us-ascii", "utf-8")
+ ec.replacement.should == "\u{fffd}".force_encoding('utf-8')
+ ec.replacement = '?'.encode('utf-8')
+ ec.replacement.should == '?'.force_encoding('utf-8')
+ end
+
+ it "raises an UndefinedConversionError is the argument cannot be converted into the destination encoding" do
+ ec = Encoding::Converter.new("sjis", "ascii")
+ utf8_q = "\u{986}".force_encoding('utf-8')
+ ec.primitive_convert(utf8_q.dup, "").should == :undefined_conversion
+ lambda { ec.replacement = utf8_q }.should \
+ raise_error(Encoding::UndefinedConversionError)
+ end
+
+ it "does not change the replacement character if the argument cannot be converted into the destination encoding" do
+ ec = Encoding::Converter.new("sjis", "ascii")
+ utf8_q = "\u{986}".force_encoding('utf-8')
+ ec.primitive_convert(utf8_q.dup, "").should == :undefined_conversion
+ lambda { ec.replacement = utf8_q }.should \
+ raise_error(Encoding::UndefinedConversionError)
+ ec.replacement.should == "?".force_encoding('us-ascii')
+ end
+
+ it "uses the replacement character" do
+ ec = Encoding::Converter.new("utf-8", "us-ascii", :invalid => :replace, :undef => :replace)
+ ec.replacement = "!"
+ dest = ""
+ status = ec.primitive_convert "中文123", dest
+
+ status.should == :finished
+ dest.should == "!!123"
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/converter/search_convpath_spec.rb b/spec/ruby/core/encoding/converter/search_convpath_spec.rb
new file mode 100644
index 0000000000..09db480642
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/search_convpath_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding::Converter.search_convpath" do
+ it "returns an Array with a single element if there is a direct converter" do
+ cp = Encoding::Converter.search_convpath('ASCII', 'UTF-8')
+ cp.should == [[Encoding::US_ASCII, Encoding::UTF_8]]
+ end
+
+ it "returns multiple encoding pairs when direct conversion is impossible" do
+ cp = Encoding::Converter.search_convpath('ascii','Big5')
+ cp.should == [
+ [Encoding::US_ASCII, Encoding::UTF_8],
+ [Encoding::UTF_8, Encoding::Big5]
+ ]
+ end
+
+ it "indicates if crlf_newline conversion would occur" do
+ cp = Encoding::Converter.search_convpath(
+ "ISO-8859-1", "EUC-JP", {crlf_newline: true})
+ cp.last.should == "crlf_newline"
+
+ cp = Encoding::Converter.search_convpath(
+ "ASCII", "UTF-8", {crlf_newline: false})
+ cp.last.should_not == "crlf_newline"
+ end
+
+ it "raises an Encoding::ConverterNotFoundError if no conversion path exists" do
+ lambda do
+ Encoding::Converter.search_convpath(Encoding::ASCII_8BIT, Encoding::Emacs_Mule)
+ end.should raise_error(Encoding::ConverterNotFoundError)
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/converter/source_encoding_spec.rb b/spec/ruby/core/encoding/converter/source_encoding_spec.rb
new file mode 100644
index 0000000000..5abb009dda
--- /dev/null
+++ b/spec/ruby/core/encoding/converter/source_encoding_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding::Converter#source_encoding" do
+ it "returns the source encoding as an Encoding object" do
+ ec = Encoding::Converter.new('ASCII','Big5')
+ ec.source_encoding.should == Encoding::US_ASCII
+
+ ec = Encoding::Converter.new('Shift_JIS','EUC-JP')
+ ec.source_encoding.should == Encoding::SHIFT_JIS
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/default_external_spec.rb b/spec/ruby/core/encoding/default_external_spec.rb
new file mode 100644
index 0000000000..3de52043e2
--- /dev/null
+++ b/spec/ruby/core/encoding/default_external_spec.rb
@@ -0,0 +1,65 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding.default_external" do
+ before :each do
+ @original_encoding = Encoding.default_external
+ end
+
+ after :each do
+ Encoding.default_external = @original_encoding
+ end
+
+ it "returns an Encoding object" do
+ Encoding.default_external.should be_an_instance_of(Encoding)
+ end
+
+ it "returns the default external encoding" do
+ Encoding.default_external = Encoding::SHIFT_JIS
+ Encoding.default_external.should == Encoding::SHIFT_JIS
+ end
+ end
+
+ describe "Encoding.default_external=" do
+ before :each do
+ @original_encoding = Encoding.default_external
+ end
+
+ after :each do
+ Encoding.default_external = @original_encoding
+ end
+
+ it "sets the default external encoding" do
+ Encoding.default_external = Encoding::SHIFT_JIS
+ Encoding.default_external.should == Encoding::SHIFT_JIS
+ Encoding.find('external').should == Encoding::SHIFT_JIS
+ end
+
+ platform_is_not :windows do
+ it "also sets the filesystem encoding" do
+ Encoding.default_external = Encoding::SHIFT_JIS
+ Encoding.find('filesystem').should == Encoding::SHIFT_JIS
+ end
+ end
+
+ it "can accept a name of an encoding as a String" do
+ Encoding.default_external = 'Shift_JIS'
+ Encoding.default_external.should == Encoding::SHIFT_JIS
+ end
+
+ it "calls #to_s on arguments that are neither Strings nor Encodings" do
+ string = mock('string')
+ string.should_receive(:to_str).at_least(1).and_return('US-ASCII')
+ Encoding.default_external = string
+ Encoding.default_external.should == Encoding::ASCII
+ end
+
+ it "raises a TypeError unless the argument is an Encoding or convertible to a String" do
+ lambda { Encoding.default_external = [] }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError if the argument is nil" do
+ lambda { Encoding.default_external = nil }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/default_internal_spec.rb b/spec/ruby/core/encoding/default_internal_spec.rb
new file mode 100644
index 0000000000..5ff475454b
--- /dev/null
+++ b/spec/ruby/core/encoding/default_internal_spec.rb
@@ -0,0 +1,76 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding.default_internal" do
+ before :each do
+ @original_encoding = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_internal = @original_encoding
+ end
+
+ it "is nil by default" do
+ Encoding.default_internal.should be_nil
+ end
+
+ it "returns an Encoding object if a default internal encoding is set" do
+ Encoding.default_internal = Encoding::ASCII
+ Encoding.default_internal.should be_an_instance_of(Encoding)
+ end
+
+ it "returns nil if no default internal encoding is set" do
+ Encoding.default_internal = nil
+ Encoding.default_internal.should be_nil
+ end
+
+ it "returns the default internal encoding" do
+ Encoding.default_internal = Encoding::ASCII_8BIT
+ Encoding.default_internal.should == Encoding::ASCII_8BIT
+ end
+ end
+
+ describe "Encoding.default_internal=" do
+ before :each do
+ @original_encoding = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_internal = @original_encoding
+ end
+
+ it "sets the default internal encoding" do
+ Encoding.default_internal = Encoding::SHIFT_JIS
+ Encoding.default_internal.should == Encoding::SHIFT_JIS
+ end
+
+ it "can accept a name of an encoding as a String" do
+ Encoding.default_internal = 'Shift_JIS'
+ Encoding.default_internal.should == Encoding::SHIFT_JIS
+ end
+
+ it "calls #to_str to convert an object to a String" do
+ obj = mock('string')
+ obj.should_receive(:to_str).at_least(1).times.and_return('ascii')
+
+ Encoding.default_internal = obj
+ Encoding.default_internal.should == Encoding::ASCII
+ end
+
+ it "raises a TypeError if #to_str does not return a String" do
+ obj = mock('string')
+ obj.should_receive(:to_str).at_least(1).times.and_return(1)
+
+ lambda { Encoding.default_internal = obj }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed an object not providing #to_str" do
+ lambda { Encoding.default_internal = mock("encoding") }.should raise_error(TypeError)
+ end
+
+ it "accepts an argument of nil to unset the default internal encoding" do
+ Encoding.default_internal = nil
+ Encoding.default_internal.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/dummy_spec.rb b/spec/ruby/core/encoding/dummy_spec.rb
new file mode 100644
index 0000000000..ef634829d1
--- /dev/null
+++ b/spec/ruby/core/encoding/dummy_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding#dummy?" do
+ it "returns false for proper encodings" do
+ Encoding::UTF_8.dummy?.should be_false
+ Encoding::ASCII.dummy?.should be_false
+ end
+
+ it "returns true for dummy encodings" do
+ Encoding::ISO_2022_JP.dummy?.should be_true
+ Encoding::CP50221.dummy?.should be_true
+ Encoding::UTF_7.dummy?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/find_spec.rb b/spec/ruby/core/encoding/find_spec.rb
new file mode 100644
index 0000000000..3b00de27d4
--- /dev/null
+++ b/spec/ruby/core/encoding/find_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding.find" do
+ before :all do
+ @encodings = Encoding.aliases.to_a.flatten.uniq
+ end
+
+ it "returns the corresponding Encoding object if given a valid encoding name" do
+ @encodings.each do |enc|
+ Encoding.find(enc).should be_an_instance_of(Encoding)
+ end
+ end
+
+ it "returns the corresponding Encoding object if given a valid alias name" do
+ Encoding.aliases.keys.each do |enc_alias|
+ Encoding.find(enc_alias).should be_an_instance_of(Encoding)
+ end
+ end
+
+ it "raises a TypeError if passed a Symbol" do
+ lambda { Encoding.find(:"utf-8") }.should raise_error(TypeError)
+ end
+
+ it "returns the passed Encoding object" do
+ Encoding.find(Encoding::UTF_8).should == Encoding::UTF_8
+ end
+
+ it "accepts encoding names as Strings" do
+ Encoding.list.each do |enc|
+ Encoding.find(enc.name).should == enc
+ end
+ end
+
+ it "accepts any object as encoding name, if it responds to #to_str" do
+ obj = Class.new do
+ attr_writer :encoding_name
+ def to_str; @encoding_name; end
+ end.new
+
+ Encoding.list.each do |enc|
+ obj.encoding_name = enc.name
+ Encoding.find(obj).should == enc
+ end
+ end
+
+ it "is case insensitive" do
+ @encodings.each do |enc|
+ Encoding.find(enc.upcase).should == Encoding.find(enc)
+ end
+ end
+
+ it "raises an ArgumentError if the given encoding does not exist" do
+ lambda { Encoding.find('dh2dh278d') }.should raise_error(ArgumentError)
+ end
+
+ # Not sure how to do a better test, since locale depends on weird platform-specific stuff
+ it "supports the 'locale' encoding alias" do
+ enc = Encoding.find('locale')
+ enc.should_not == nil
+ end
+
+ it "returns default external encoding for the 'external' encoding alias" do
+ enc = Encoding.find('external')
+ enc.should == Encoding.default_external
+ end
+
+ it "returns default internal encoding for the 'internal' encoding alias" do
+ enc = Encoding.find('internal')
+ enc.should == Encoding.default_internal
+ end
+
+ platform_is_not :windows do
+ it "uses default external encoding for the 'filesystem' encoding alias" do
+ enc = Encoding.find('filesystem')
+ enc.should == Encoding.default_external
+ end
+ end
+
+ platform_is :windows do
+ it "needs to be reviewed for spec completeness"
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/fixtures/classes.rb b/spec/ruby/core/encoding/fixtures/classes.rb
new file mode 100644
index 0000000000..12e9a4f348
--- /dev/null
+++ b/spec/ruby/core/encoding/fixtures/classes.rb
@@ -0,0 +1,49 @@
+# -*- encoding: binary -*-
+module EncodingSpecs
+ class UndefinedConversionError
+ def self.exception
+ ec = Encoding::Converter.new('utf-8','ascii')
+ begin
+ ec.convert("\u{8765}")
+ rescue Encoding::UndefinedConversionError => e
+ e
+ end
+ end
+ end
+
+ class UndefinedConversionErrorIndirect
+ def self.exception
+ ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP")
+ begin
+ ec.convert("\xA0")
+ rescue Encoding::UndefinedConversionError => e
+ e
+ end
+ end
+ end
+
+ class InvalidByteSequenceError
+ def self.exception
+ ec = Encoding::Converter.new("utf-8", "iso-8859-1")
+ begin
+ ec.convert("\xf1abcd")
+ rescue Encoding::InvalidByteSequenceError => e
+ # Return the exception object and the primitive_errinfo Array
+ [e, ec.primitive_errinfo]
+ end
+ end
+ end
+
+ class InvalidByteSequenceErrorIndirect
+ def self.exception
+ ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
+ begin
+ ec.convert("abc\xA1\xFFdef")
+ rescue Encoding::InvalidByteSequenceError => e
+ # Return the exception object and the discarded bytes reported by
+ # #primitive_errinfo
+ [e, ec.primitive_errinfo]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/inspect_spec.rb b/spec/ruby/core/encoding/inspect_spec.rb
new file mode 100644
index 0000000000..9da9275eae
--- /dev/null
+++ b/spec/ruby/core/encoding/inspect_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding#inspect" do
+ it "returns a String" do
+ Encoding::UTF_8.inspect.should be_an_instance_of(String)
+ end
+
+ it "returns #<Encoding:name> for a non-dummy encoding named 'name'" do
+ Encoding.list.to_a.reject {|e| e.dummy? }.each do |enc|
+ enc.inspect.should =~ /#<Encoding:#{enc.name}>/
+ end
+ end
+
+ it "returns #<Encoding:name (dummy)> for a dummy encoding named 'name'" do
+ Encoding.list.to_a.select {|e| e.dummy? }.each do |enc|
+ enc.inspect.should =~ /#<Encoding:#{enc.name} \(dummy\)>/
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_name_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_name_spec.rb
new file mode 100644
index 0000000000..51c802f7e1
--- /dev/null
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_name_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../fixtures/classes'
+
+with_feature :encoding do
+ describe "Encoding::InvalidByteSequenceError#destination_encoding_name" do
+ before :each do
+ @exception, = EncodingSpecs::InvalidByteSequenceError.exception
+ @exception2, = EncodingSpecs::InvalidByteSequenceErrorIndirect.exception
+ end
+
+ it "returns a String" do
+ @exception.destination_encoding_name.should be_an_instance_of(String)
+ @exception2.destination_encoding_name.should be_an_instance_of(String)
+ end
+
+ it "is equal to the destination encoding name of the object that raised it" do
+ @exception.destination_encoding_name.should == "ISO-8859-1"
+ @exception2.destination_encoding_name.should == "UTF-8"
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_spec.rb
new file mode 100644
index 0000000000..d9e63a6779
--- /dev/null
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../fixtures/classes'
+
+with_feature :encoding do
+ describe "Encoding::InvalidByteSequenceError#destination_encoding" do
+ before :each do
+ @exception, = EncodingSpecs::InvalidByteSequenceError.exception
+ @exception2, = EncodingSpecs::InvalidByteSequenceErrorIndirect.exception
+ end
+
+ it "returns an Encoding object" do
+ @exception.destination_encoding.should be_an_instance_of(Encoding)
+ @exception2.destination_encoding.should be_an_instance_of(Encoding)
+ end
+
+ it "is equal to the destination encoding of the object that raised it" do
+ @exception.destination_encoding.should == Encoding::ISO_8859_1
+ @exception2.destination_encoding.should == Encoding::UTF_8
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/error_bytes_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/error_bytes_spec.rb
new file mode 100644
index 0000000000..79b381a370
--- /dev/null
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/error_bytes_spec.rb
@@ -0,0 +1,32 @@
+# -*- encoding: binary -*-
+require_relative '../fixtures/classes'
+
+with_feature :encoding do
+ describe "Encoding::InvalidByteSequenceError#error_bytes" do
+ before :each do
+ @exception, @errinfo = EncodingSpecs::InvalidByteSequenceError.exception
+ @exception2, @errinfo2 = EncodingSpecs::InvalidByteSequenceErrorIndirect.exception
+ end
+
+ it "returns a String" do
+ @exception.error_bytes.should be_an_instance_of(String)
+ @exception2.error_bytes.should be_an_instance_of(String)
+ end
+
+ it "returns the bytes that caused the exception" do
+ @exception.error_bytes.size.should == 1
+ @exception.error_bytes.should == "\xF1"
+ @exception.error_bytes.should == @errinfo[-2]
+
+ @exception2.error_bytes.size.should == 1
+ @exception2.error_bytes.should == "\xA1"
+ @exception2.error_bytes.should == @errinfo2[-2]
+ end
+
+ it "uses ASCII-8BIT as the encoding" do
+ @exception.error_bytes.encoding.should == Encoding::ASCII_8BIT
+
+ @exception2.error_bytes.encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb
new file mode 100644
index 0000000000..f89c0d8c03
--- /dev/null
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb
@@ -0,0 +1,31 @@
+# -*- encoding: binary -*-
+require_relative '../../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding::InvalidByteSequenceError#incomplete_input?" do
+
+ it "returns nil by default" do
+ Encoding::InvalidByteSequenceError.new.incomplete_input?.should be_nil
+ end
+
+ it "returns true if #primitive_convert returned :incomplete_input for the same data" do
+ ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
+ ec.primitive_convert("\xA1",'').should == :incomplete_input
+ begin
+ ec.convert("\xA1")
+ rescue Encoding::InvalidByteSequenceError => e
+ e.incomplete_input?.should be_true
+ end
+ end
+
+ it "returns false if #primitive_convert returned :invalid_byte_sequence for the same data" do
+ ec = Encoding::Converter.new("ascii", "utf-8")
+ ec.primitive_convert("\xfffffffff",'').should == :invalid_byte_sequence
+ begin
+ ec.convert("\xfffffffff")
+ rescue Encoding::InvalidByteSequenceError => e
+ e.incomplete_input?.should be_false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb
new file mode 100644
index 0000000000..4270a0647a
--- /dev/null
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb
@@ -0,0 +1,32 @@
+# -*- encoding: binary -*-
+require_relative '../fixtures/classes'
+
+with_feature :encoding do
+ describe "Encoding::InvalidByteSequenceError#readagain_bytes" do
+ before :each do
+ @exception, @errinfo = EncodingSpecs::InvalidByteSequenceError.exception
+ @exception2, @errinfo2 = EncodingSpecs::InvalidByteSequenceErrorIndirect.exception
+ end
+
+ it "returns a String" do
+ @exception.readagain_bytes.should be_an_instance_of(String)
+ @exception2.readagain_bytes.should be_an_instance_of(String)
+ end
+
+ it "returns the bytes to be read again" do
+ @exception.readagain_bytes.size.should == 1
+ @exception.readagain_bytes.should == "a".force_encoding('binary')
+ @exception.readagain_bytes.should == @errinfo[-1]
+
+ @exception2.readagain_bytes.size.should == 1
+ @exception2.readagain_bytes.should == "\xFF".force_encoding('binary')
+ @exception2.readagain_bytes.should == @errinfo2[-1]
+ end
+
+ it "uses ASCII-8BIT as the encoding" do
+ @exception.readagain_bytes.encoding.should == Encoding::ASCII_8BIT
+
+ @exception2.readagain_bytes.encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb
new file mode 100644
index 0000000000..bd31c03eee
--- /dev/null
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../fixtures/classes'
+
+with_feature :encoding do
+ describe "Encoding::UndefinedConversionError#source_encoding_name" do
+ before :each do
+ @exception, = EncodingSpecs::UndefinedConversionError.exception
+ @exception2, = EncodingSpecs::UndefinedConversionErrorIndirect.exception
+ end
+
+ it "returns a String" do
+ @exception.source_encoding_name.should be_an_instance_of(String)
+ end
+
+ it "is equal to the source encoding name of the object that raised it" do
+ @exception.source_encoding_name.should == "UTF-8"
+ end
+
+ # The source encoding specified in the Encoding::Converter constructor may
+ # differ from the source encoding returned here. What seems to happen is
+ # that when transcoding along a path with multiple pairs of encodings, the
+ # last one encountered when the error occurred is returned. So in this
+ # case, the conversion path is ISO-8859-1 -> UTF-8 -> EUC-JP. The
+ # conversion from ISO-8859-1 -> UTF-8 succeeded, but the conversion from
+ # UTF-8 to EUC-JP failed. IOW, it failed when the source encoding was
+ # UTF-8, so UTF-8 is regarded as the source encoding.
+ it "is equal to the source encoding at the stage of the conversion path where the error occurred" do
+ @exception2.source_encoding_name.should == 'UTF-8'
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb
new file mode 100644
index 0000000000..3f36d504d5
--- /dev/null
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../fixtures/classes'
+
+with_feature :encoding do
+ describe "Encoding::InvalidByteSequenceError#source_encoding" do
+ before :each do
+ @exception, = EncodingSpecs::InvalidByteSequenceError.exception
+ @exception2, = EncodingSpecs::InvalidByteSequenceErrorIndirect.exception
+ end
+
+ it "returns an Encoding object" do
+ @exception.source_encoding.should be_an_instance_of(Encoding)
+ @exception2.source_encoding.should be_an_instance_of(Encoding)
+ end
+
+ it "is equal to the source encoding of the object that raised it" do
+ @exception.source_encoding.should == Encoding::UTF_8
+ end
+
+ # The source encoding specified in the Encoding::Converter constructor may
+ # differ from the source encoding returned here. What seems to happen is
+ # that when transcoding along a path with multiple pairs of encodings, the
+ # last one encountered when the error occurred is returned. So in this
+ # case, the conversion path is EUC-JP -> UTF-8 -> ISO-8859-1. The
+ # conversions failed with the first pair of encodings (i.e. transcoding
+ # from EUC-JP to UTF-8, so UTF-8 is regarded as the source encoding; if
+ # the error had occurred when converting from UTF-8 to ISO-8859-1, UTF-8
+ # would have been the source encoding.
+
+ # FIXME: Derive example where the failure occurs at the UTF-8 ->
+ # ISO-8859-1 case so as to better illustrate the issue
+ it "is equal to the source encoding at the stage of the conversion path where the error occurred" do
+ @exception2.source_encoding.should == Encoding::EUC_JP
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/list_spec.rb b/spec/ruby/core/encoding/list_spec.rb
new file mode 100644
index 0000000000..b1e08c7a2e
--- /dev/null
+++ b/spec/ruby/core/encoding/list_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding.list" do
+ it "returns an Array" do
+ Encoding.list.should be_an_instance_of(Array)
+ end
+
+ it "returns an Array of Encoding objects" do
+ Encoding.list.each do |enc|
+ enc.should be_an_instance_of(Encoding)
+ end
+ end
+
+ it "returns each encoding only once" do
+ orig = Encoding.list.map {|e| e.name}
+ orig.should == orig.uniq
+ end
+
+ it "includes the default external encoding" do
+ Encoding.list.include?(Encoding.default_external).should be_true
+ end
+
+ it "does not include any alias names" do
+ Encoding.aliases.keys.each do |enc_alias|
+ Encoding.list.include?(enc_alias).should be_false
+ end
+ end
+
+ it "includes all aliased encodings" do
+ Encoding.aliases.values.each do |enc_alias|
+ Encoding.list.include?(Encoding.find(enc_alias)).should be_true
+ end
+ end
+
+ it "includes dummy encodings" do
+ Encoding.list.select {|e| e.dummy?}.should_not == []
+ end
+
+ # TODO: Find example that illustrates this
+ it "updates the list when #find is used to load a new encoding"
+ end
+end
diff --git a/spec/ruby/core/encoding/locale_charmap_spec.rb b/spec/ruby/core/encoding/locale_charmap_spec.rb
new file mode 100644
index 0000000000..54dad396fd
--- /dev/null
+++ b/spec/ruby/core/encoding/locale_charmap_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding.locale_charmap" do
+ it "returns a String" do
+ Encoding.locale_charmap.should be_an_instance_of(String)
+ end
+
+ # FIXME: Get this working on Windows
+ platform_is :linux do
+ it "returns a value based on the LC_ALL environment variable" do
+ old_lc_all = ENV['LC_ALL']
+ ENV['LC_ALL'] = 'C'
+ ruby_exe("print Encoding.locale_charmap").should == 'ANSI_X3.4-1968'
+ ENV['LC_ALL'] = old_lc_all
+ end
+ end
+
+ platform_is :freebsd, :openbsd, :darwin do
+ it "returns a value based on the LC_ALL environment variable" do
+ old_lc_all = ENV['LC_ALL']
+ ENV['LC_ALL'] = 'C'
+ ruby_exe("print Encoding.locale_charmap").should == 'US-ASCII'
+ ENV['LC_ALL'] = old_lc_all
+ end
+ end
+
+ platform_is :netbsd do
+ it "returns a value based on the LC_ALL environment variable" do
+ old_lc_all = ENV['LC_ALL']
+ ENV['LC_ALL'] = 'C'
+ ruby_exe("print Encoding.locale_charmap").should == '646'
+ ENV['LC_ALL'] = old_lc_all
+ end
+ end
+
+ platform_is :bsd, :darwin, :linux do
+ it "is unaffected by assigning to ENV['LC_ALL'] in the same process" do
+ old_charmap = Encoding.locale_charmap
+ old_lc_all = ENV['LC_ALL']
+ ENV['LC_ALL'] = 'C'
+ Encoding.locale_charmap.should == old_charmap
+ ENV['LC_ALL'] = old_lc_all
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/name_list_spec.rb b/spec/ruby/core/encoding/name_list_spec.rb
new file mode 100644
index 0000000000..6e02347bfd
--- /dev/null
+++ b/spec/ruby/core/encoding/name_list_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding.name_list" do
+ it "returns an Array" do
+ Encoding.name_list.should be_an_instance_of(Array)
+ end
+
+ it "returns encoding names as Strings" do
+ Encoding.name_list.each {|e| e.should be_an_instance_of(String) }
+ end
+
+ it "includes all aliases" do
+ Encoding.aliases.keys.each do |enc_alias|
+ Encoding.name_list.include?(enc_alias).should be_true
+ end
+ end
+
+ it "includes all non-dummy encodings" do
+ Encoding.list.each do |enc|
+ Encoding.name_list.include?(enc.name).should be_true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/name_spec.rb b/spec/ruby/core/encoding/name_spec.rb
new file mode 100644
index 0000000000..1632137f95
--- /dev/null
+++ b/spec/ruby/core/encoding/name_spec.rb
@@ -0,0 +1,7 @@
+require_relative 'shared/name'
+
+with_feature :encoding do
+ describe "Encoding#name" do
+ it_behaves_like :encoding_name, :name
+ end
+end
diff --git a/spec/ruby/core/encoding/names_spec.rb b/spec/ruby/core/encoding/names_spec.rb
new file mode 100644
index 0000000000..c0f84c9b2b
--- /dev/null
+++ b/spec/ruby/core/encoding/names_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding#names" do
+ it "returns an Array" do
+ Encoding.name_list.each do |name|
+ e = Encoding.find(name) or next
+ e.names.should be_an_instance_of(Array)
+ end
+ end
+
+ it "returns names as Strings" do
+ Encoding.name_list.each do |name|
+ e = Encoding.find(name) or next
+ e.names.each do |this_name|
+ this_name.should be_an_instance_of(String)
+ end
+ end
+ end
+
+ it "returns #name as the first value" do
+ Encoding.name_list.each do |name|
+ e = Encoding.find(name) or next
+ e.names.first.should == e.name
+ end
+ end
+
+ it "includes any aliases the encoding has" do
+ Encoding.name_list.each do |name|
+ e = Encoding.find(name) or next
+ aliases = Encoding.aliases.select{|a,n| n == name}.keys
+ names = e.names
+ aliases.each {|a| names.include?(a).should be_true}
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/replicate_spec.rb b/spec/ruby/core/encoding/replicate_spec.rb
new file mode 100644
index 0000000000..2e1d75f1d0
--- /dev/null
+++ b/spec/ruby/core/encoding/replicate_spec.rb
@@ -0,0 +1,48 @@
+# -*- encoding: binary -*-
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "Encoding#replicate" do
+ before :all do
+ @i = 0
+ 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
+ "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
+ "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
+ "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)
+ e.name.should == name
+ e.dummy?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/shared/name.rb b/spec/ruby/core/encoding/shared/name.rb
new file mode 100644
index 0000000000..cd37ea06db
--- /dev/null
+++ b/spec/ruby/core/encoding/shared/name.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+
+describe :encoding_name, shared: true do
+ it "returns a String" do
+ Encoding.list.each do |e|
+ e.send(@method).should be_an_instance_of(String)
+ end
+ end
+
+ it "uniquely identifies an encoding" do
+ Encoding.list.each do |e|
+ e.should == Encoding.find(e.send(@method))
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/to_s_spec.rb b/spec/ruby/core/encoding/to_s_spec.rb
new file mode 100644
index 0000000000..e554bc3fee
--- /dev/null
+++ b/spec/ruby/core/encoding/to_s_spec.rb
@@ -0,0 +1,7 @@
+require_relative 'shared/name'
+
+with_feature :encoding do
+ describe "Encoding#to_s" do
+ it_behaves_like :encoding_name, :to_s
+ end
+end
diff --git a/spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_name_spec.rb b/spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_name_spec.rb
new file mode 100644
index 0000000000..a40f295fcf
--- /dev/null
+++ b/spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_name_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../fixtures/classes'
+
+with_feature :encoding do
+ describe "Encoding::UndefinedConversionError#destination_encoding_name" do
+ before :each do
+ @exception = EncodingSpecs::UndefinedConversionError.exception
+ end
+
+ it "returns a String" do
+ @exception.destination_encoding_name.should be_an_instance_of(String)
+ end
+
+ it "is equal to the destination encoding name of the object that raised it" do
+ @exception.destination_encoding_name.should == "US-ASCII"
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_spec.rb b/spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_spec.rb
new file mode 100644
index 0000000000..579b0a37f8
--- /dev/null
+++ b/spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../fixtures/classes'
+
+with_feature :encoding do
+ describe "Encoding::UndefinedConversionError#destination_encoding" do
+ before :each do
+ @exception = EncodingSpecs::UndefinedConversionError.exception
+ end
+
+ it "returns an Encoding object" do
+ @exception.destination_encoding.should be_an_instance_of(Encoding)
+ end
+
+ it "is equal to the destination encoding of the object that raised it" do
+ @exception.destination_encoding.should == Encoding::US_ASCII
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/undefined_conversion_error/error_char_spec.rb b/spec/ruby/core/encoding/undefined_conversion_error/error_char_spec.rb
new file mode 100644
index 0000000000..740cd17e30
--- /dev/null
+++ b/spec/ruby/core/encoding/undefined_conversion_error/error_char_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../fixtures/classes'
+
+with_feature :encoding do
+ describe "Encoding::UndefinedConversionError#error_char" do
+ before :each do
+ @exception = EncodingSpecs::UndefinedConversionError.exception
+ @exception2 = EncodingSpecs::UndefinedConversionErrorIndirect.exception
+ end
+
+ it "returns a String" do
+ @exception.error_char.should be_an_instance_of(String)
+ @exception2.error_char.should be_an_instance_of(String)
+ end
+
+ it "returns the one-character String that caused the exception" do
+ @exception.error_char.size.should == 1
+ @exception.error_char.should == "\u{8765}"
+
+ @exception2.error_char.size.should == 1
+ @exception2.error_char.should == "\u{A0}"
+ end
+
+ it "uses the source encoding" do
+ @exception.error_char.encoding.should == @exception.source_encoding
+
+ @exception2.error_char.encoding.should == @exception2.source_encoding
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb b/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb
new file mode 100644
index 0000000000..79a59ff1e9
--- /dev/null
+++ b/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../fixtures/classes'
+
+with_feature :encoding do
+ describe "Encoding::UndefinedConversionError#source_encoding_name" do
+ before :each do
+ @exception = EncodingSpecs::UndefinedConversionError.exception
+ @exception2 = EncodingSpecs::UndefinedConversionErrorIndirect.exception
+ end
+
+ it "returns a String" do
+ @exception.source_encoding_name.should be_an_instance_of(String)
+ end
+
+ it "is equal to the source encoding name of the object that raised it" do
+ @exception.source_encoding_name.should == "UTF-8"
+ end
+
+ # The source encoding specified in the Encoding::Converter constructor may
+ # differ from the source encoding returned here. What seems to happen is
+ # that when transcoding along a path with multiple pairs of encodings, the
+ # last one encountered when the error occurred is returned. So in this
+ # case, the conversion path is ISO-8859-1 -> UTF-8 -> EUC-JP. The
+ # conversion from ISO-8859-1 -> UTF-8 succeeded, but the conversion from
+ # UTF-8 to EUC-JP failed. IOW, it failed when the source encoding was
+ # UTF-8, so UTF-8 is regarded as the source encoding.
+ it "is equal to the source encoding at the stage of the conversion path where the error occurred" do
+ @exception2.source_encoding_name.should == 'UTF-8'
+ end
+ end
+end
diff --git a/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_spec.rb b/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_spec.rb
new file mode 100644
index 0000000000..29be837e13
--- /dev/null
+++ b/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../fixtures/classes'
+
+with_feature :encoding do
+ describe "Encoding::UndefinedConversionError#source_encoding" do
+ before :each do
+ @exception = EncodingSpecs::UndefinedConversionError.exception
+ @exception2 = EncodingSpecs::UndefinedConversionErrorIndirect.exception
+ end
+
+ it "returns an Encoding object" do
+ @exception.source_encoding.should be_an_instance_of(Encoding)
+ @exception2.source_encoding.should be_an_instance_of(Encoding)
+ end
+
+ it "is equal to the source encoding of the object that raised it" do
+ @exception.source_encoding.should == Encoding::UTF_8
+ end
+
+ # The source encoding specified in the Encoding::Converter constructor may
+ # differ from the source encoding returned here. What seems to happen is
+ # that when transcoding along a path with multiple pairs of encodings, the
+ # last one encountered when the error occurred is returned. So in this
+ # case, the conversion path is ISO-8859-1 -> UTF-8 -> EUC-JP. The
+ # conversion from ISO-8859-1 -> UTF-8 succeeded, but the conversion from
+ # UTF-8 to EUC-JP failed. IOW, it failed when the source encoding was
+ # UTF-8, so UTF-8 is regarded as the source encoding.
+ it "is equal to the source encoding at the stage of the conversion path where the error occurred" do
+ @exception2.source_encoding.should == Encoding::UTF_8
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/all_spec.rb b/spec/ruby/core/enumerable/all_spec.rb
new file mode 100644
index 0000000000..b02ac8b21b
--- /dev/null
+++ b/spec/ruby/core/enumerable/all_spec.rb
@@ -0,0 +1,201 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#all?" do
+ before :each do
+ @enum = EnumerableSpecs::Numerous.new
+ @empty = EnumerableSpecs::Empty.new()
+ @enum1 = EnumerableSpecs::Numerous.new(0, 1, 2, -1)
+ @enum2 = EnumerableSpecs::Numerous.new(nil, false, true)
+ end
+
+ it "always returns true on empty enumeration" do
+ @empty.all?.should == true
+ @empty.all? { nil }.should == true
+
+ [].all?.should == true
+ [].all? { false }.should == true
+
+ {}.all?.should == true
+ {}.all? { nil }.should == true
+ end
+
+ it "raises an ArgumentError when more than 1 argument is provided" do
+ lambda { @enum.all?(1, 2, 3) }.should raise_error(ArgumentError)
+ lambda { [].all?(1, 2, 3) }.should raise_error(ArgumentError)
+ lambda { {}.all?(1, 2, 3) }.should raise_error(ArgumentError)
+ end
+
+ ruby_version_is ""..."2.5" do
+ it "raises an ArgumentError when any arguments provided" do
+ lambda { @enum.all?(Proc.new {}) }.should raise_error(ArgumentError)
+ lambda { @enum.all?(nil) }.should raise_error(ArgumentError)
+ lambda { @empty.all?(1) }.should raise_error(ArgumentError)
+ lambda { @enum1.all?(1) {} }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "does not hide exceptions out of #each" do
+ lambda {
+ EnumerableSpecs::ThrowingEach.new.all?
+ }.should raise_error(RuntimeError)
+
+ lambda {
+ EnumerableSpecs::ThrowingEach.new.all? { false }
+ }.should raise_error(RuntimeError)
+ end
+
+ describe "with no block" do
+ it "returns true if no elements are false or nil" do
+ @enum.all?.should == true
+ @enum1.all?.should == true
+ @enum2.all?.should == false
+
+ EnumerableSpecs::Numerous.new('a','b','c').all?.should == true
+ EnumerableSpecs::Numerous.new(0, "x", true).all?.should == true
+ end
+
+ it "returns false if there are false or nil elements" do
+ EnumerableSpecs::Numerous.new(false).all?.should == false
+ EnumerableSpecs::Numerous.new(false, false).all?.should == false
+
+ EnumerableSpecs::Numerous.new(nil).all?.should == false
+ EnumerableSpecs::Numerous.new(nil, nil).all?.should == false
+
+ EnumerableSpecs::Numerous.new(1, nil, 2).all?.should == false
+ EnumerableSpecs::Numerous.new(0, "x", false, true).all?.should == false
+ @enum2.all?.should == false
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMultiWithFalse.new
+ multi.all?.should be_true
+ end
+ end
+
+ describe "with block" do
+ it "returns true if the block never returns false or nil" do
+ @enum.all? { true }.should == true
+ @enum1.all?{ |o| o < 5 }.should == true
+ @enum1.all?{ |o| 5 }.should == true
+ end
+
+ it "returns false if the block ever returns false or nil" do
+ @enum.all? { false }.should == false
+ @enum.all? { nil }.should == false
+ @enum1.all?{ |o| o > 2 }.should == false
+
+ EnumerableSpecs::Numerous.new.all? { |i| i > 5 }.should == false
+ EnumerableSpecs::Numerous.new.all? { |i| i == 3 ? nil : true }.should == false
+ end
+
+ it "stops iterating once the return value is determined" do
+ yielded = []
+ EnumerableSpecs::Numerous.new(:one, :two, :three).all? do |e|
+ yielded << e
+ false
+ end.should == false
+ yielded.should == [:one]
+
+ yielded = []
+ EnumerableSpecs::Numerous.new(true, true, false, true).all? do |e|
+ yielded << e
+ e
+ end.should == false
+ yielded.should == [true, true, false]
+
+ yielded = []
+ EnumerableSpecs::Numerous.new(1, 2, 3, 4, 5).all? do |e|
+ yielded << e
+ e
+ end.should == true
+ yielded.should == [1, 2, 3, 4, 5]
+ end
+
+ it "does not hide exceptions out of the block" do
+ lambda {
+ @enum.all? { raise "from block" }
+ }.should raise_error(RuntimeError)
+ end
+
+ it "gathers initial args as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ yielded = []
+ multi.all? { |e| yielded << e }.should == true
+ yielded.should == [1, 3, 6]
+ end
+
+ it "yields multiple arguments when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ yielded = []
+ multi.all? { |*args| yielded << args }.should == true
+ yielded.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
+ end
+ end
+
+ ruby_version_is "2.5" do
+ describe 'when given a pattern argument' do
+ it "calls `===` on the pattern the return value " do
+ pattern = EnumerableSpecs::Pattern.new { |x| x >= 0 }
+ @enum1.all?(pattern).should == false
+ pattern.yielded.should == [[0], [1], [2], [-1]]
+ end
+
+ # may raise an exception in future versions
+ ruby_version_is ""..."2.6" do
+ it "ignores block" do
+ @enum2.all?(NilClass) { raise }.should == false
+ [1, 2, nil].all?(NilClass) { raise }.should == false
+ {a: 1}.all?(Array) { raise }.should == true
+ end
+ end
+
+ it "always returns true on empty enumeration" do
+ @empty.all?(Integer).should == true
+ [].all?(Integer).should == true
+ {}.all?(NilClass).should == true
+ end
+
+ it "does not hide exceptions out of #each" do
+ lambda {
+ EnumerableSpecs::ThrowingEach.new.all?(Integer)
+ }.should raise_error(RuntimeError)
+ end
+
+ it "returns true if the pattern never returns false or nil" do
+ pattern = EnumerableSpecs::Pattern.new { |x| 42 }
+ @enum.all?(pattern).should == true
+
+ [1, 42, 3].all?(pattern).should == true
+
+ pattern = EnumerableSpecs::Pattern.new { |x| Array === x }
+ {a: 1, b: 2}.all?(pattern).should == true
+ end
+
+ it "returns false if the pattern ever returns false or nil" do
+ pattern = EnumerableSpecs::Pattern.new { |x| x >= 0 }
+ @enum1.all?(pattern).should == false
+ pattern.yielded.should == [[0], [1], [2], [-1]]
+
+ [1, 2, 3, -1].all?(pattern).should == false
+
+ pattern = EnumerableSpecs::Pattern.new { |x| x[1] >= 0 }
+ {a: 1, b: -1}.all?(pattern).should == false
+ end
+
+ it "does not hide exceptions out of pattern#===" do
+ pattern = EnumerableSpecs::Pattern.new { raise "from pattern" }
+ lambda {
+ @enum.all?(pattern)
+ }.should raise_error(RuntimeError)
+ end
+
+ it "calls the pattern with gathered array when yielded with multiple arguments" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ pattern = EnumerableSpecs::Pattern.new { true }
+ multi.all?(pattern).should == true
+ pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/any_spec.rb b/spec/ruby/core/enumerable/any_spec.rb
new file mode 100644
index 0000000000..b3f10d6806
--- /dev/null
+++ b/spec/ruby/core/enumerable/any_spec.rb
@@ -0,0 +1,214 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#any?" do
+ before :each do
+ @enum = EnumerableSpecs::Numerous.new
+ @empty = EnumerableSpecs::Empty.new
+ @enum1 = EnumerableSpecs::Numerous.new(0, 1, 2, -1)
+ @enum2 = EnumerableSpecs::Numerous.new(nil, false, true)
+ end
+
+ it "always returns false on empty enumeration" do
+ @empty.any?.should == false
+ @empty.any? { nil }.should == false
+
+ [].any?.should == false
+ [].any? { false }.should == false
+
+ {}.any?.should == false
+ {}.any? { nil }.should == false
+ end
+
+ it "raises an ArgumentError when more than 1 argument is provided" do
+ lambda { @enum.any?(1, 2, 3) }.should raise_error(ArgumentError)
+ lambda { [].any?(1, 2, 3) }.should raise_error(ArgumentError)
+ lambda { {}.any?(1, 2, 3) }.should raise_error(ArgumentError)
+ end
+
+ ruby_version_is ""..."2.5" do
+ it "raises an ArgumentError when any arguments provided" do
+ lambda { @enum.any?(Proc.new {}) }.should raise_error(ArgumentError)
+ lambda { @enum.any?(nil) }.should raise_error(ArgumentError)
+ lambda { @empty.any?(1) }.should raise_error(ArgumentError)
+ lambda { @enum1.any?(1) {} }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "does not hide exceptions out of #each" do
+ lambda {
+ EnumerableSpecs::ThrowingEach.new.any?
+ }.should raise_error(RuntimeError)
+
+ lambda {
+ EnumerableSpecs::ThrowingEach.new.any? { false }
+ }.should raise_error(RuntimeError)
+ end
+
+ describe "with no block" do
+ it "returns true if any element is not false or nil" do
+ @enum.any?.should == true
+ @enum1.any?.should == true
+ @enum2.any?.should == true
+ EnumerableSpecs::Numerous.new(true).any?.should == true
+ EnumerableSpecs::Numerous.new('a','b','c').any?.should == true
+ EnumerableSpecs::Numerous.new('a','b','c', nil).any?.should == true
+ EnumerableSpecs::Numerous.new(1, nil, 2).any?.should == true
+ EnumerableSpecs::Numerous.new(1, false).any?.should == true
+ EnumerableSpecs::Numerous.new(false, nil, 1, false).any?.should == true
+ EnumerableSpecs::Numerous.new(false, 0, nil).any?.should == true
+ end
+
+ it "returns false if all elements are false or nil" do
+ EnumerableSpecs::Numerous.new(false).any?.should == false
+ EnumerableSpecs::Numerous.new(false, false).any?.should == false
+ EnumerableSpecs::Numerous.new(nil).any?.should == false
+ EnumerableSpecs::Numerous.new(nil, nil).any?.should == false
+ EnumerableSpecs::Numerous.new(nil, false, nil).any?.should == false
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMultiWithFalse.new
+ multi.any?.should be_true
+ end
+ end
+
+ describe "with block" do
+ it "returns true if the block ever returns other than false or nil" do
+ @enum.any? { true }.should == true
+ @enum.any? { 0 }.should == true
+ @enum.any? { 1 }.should == true
+
+ @enum1.any? { Object.new }.should == true
+ @enum1.any?{ |o| o < 1 }.should == true
+ @enum1.any?{ |o| 5 }.should == true
+
+ @enum2.any? { |i| i == nil }.should == true
+ end
+
+ it "returns false if the block never returns other than false or nil" do
+ @enum.any? { false }.should == false
+ @enum.any? { nil }.should == false
+
+ @enum1.any?{ |o| o < -10 }.should == false
+ @enum1.any?{ |o| nil }.should == false
+
+ @enum2.any? { |i| i == :stuff }.should == false
+ end
+
+ it "stops iterating once the return value is determined" do
+ yielded = []
+ EnumerableSpecs::Numerous.new(:one, :two, :three).any? do |e|
+ yielded << e
+ false
+ end.should == false
+ yielded.should == [:one, :two, :three]
+
+ yielded = []
+ EnumerableSpecs::Numerous.new(true, true, false, true).any? do |e|
+ yielded << e
+ e
+ end.should == true
+ yielded.should == [true]
+
+ yielded = []
+ EnumerableSpecs::Numerous.new(false, nil, false, true, false).any? do |e|
+ yielded << e
+ e
+ end.should == true
+ yielded.should == [false, nil, false, true]
+
+ yielded = []
+ EnumerableSpecs::Numerous.new(1, 2, 3, 4, 5).any? do |e|
+ yielded << e
+ e
+ end.should == true
+ yielded.should == [1]
+ end
+
+ it "does not hide exceptions out of the block" do
+ lambda {
+ @enum.any? { raise "from block" }
+ }.should raise_error(RuntimeError)
+ end
+
+ it "gathers initial args as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ yielded = []
+ multi.any? { |e| yielded << e; false }.should == false
+ yielded.should == [1, 3, 6]
+ end
+
+ it "yields multiple arguments when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ yielded = []
+ multi.any? { |*args| yielded << args; false }.should == false
+ yielded.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
+ end
+ end
+
+ ruby_version_is "2.5" do
+ describe 'when given a pattern argument' do
+ it "calls `===` on the pattern the return value " do
+ pattern = EnumerableSpecs::Pattern.new { |x| x == 2 }
+ @enum1.any?(pattern).should == true
+ pattern.yielded.should == [[0], [1], [2]]
+ end
+
+ # may raise an exception in future versions
+ ruby_version_is ""..."2.6" do
+ it "ignores block" do
+ @enum2.any?(NilClass) { raise }.should == true
+ [1, 2, nil].any?(NilClass) { raise }.should == true
+ {a: 1}.any?(Array) { raise }.should == true
+ end
+ end
+
+ it "always returns false on empty enumeration" do
+ @empty.any?(Integer).should == false
+ [].any?(Integer).should == false
+ {}.any?(NilClass).should == false
+ end
+
+ it "does not hide exceptions out of #each" do
+ lambda {
+ EnumerableSpecs::ThrowingEach.new.any?(Integer)
+ }.should raise_error(RuntimeError)
+ end
+
+ it "returns true if the pattern ever returns a truthy value" do
+ @enum2.any?(NilClass).should == true
+ pattern = EnumerableSpecs::Pattern.new { |x| 42 }
+ @enum.any?(pattern).should == true
+
+ [1, 42, 3].any?(pattern).should == true
+
+ pattern = EnumerableSpecs::Pattern.new { |x| x == [:b, 2] }
+ {a: 1, b: 2}.any?(pattern).should == true
+ end
+
+ it "returns false if the block never returns other than false or nil" do
+ pattern = EnumerableSpecs::Pattern.new { |x| nil }
+ @enum1.any?(pattern).should == false
+ pattern.yielded.should == [[0], [1], [2], [-1]]
+
+ [1, 2, 3].any?(pattern).should == false
+ {a: 1}.any?(pattern).should == false
+ end
+
+ it "does not hide exceptions out of pattern#===" do
+ pattern = EnumerableSpecs::Pattern.new { raise "from pattern" }
+ lambda {
+ @enum.any?(pattern)
+ }.should raise_error(RuntimeError)
+ end
+
+ it "calls the pattern with gathered array when yielded with multiple arguments" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ pattern = EnumerableSpecs::Pattern.new { false }
+ multi.any?(pattern).should == false
+ pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/chunk_spec.rb b/spec/ruby/core/enumerable/chunk_spec.rb
new file mode 100644
index 0000000000..34fa651b33
--- /dev/null
+++ b/spec/ruby/core/enumerable/chunk_spec.rb
@@ -0,0 +1,77 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#chunk" do
+ before do
+ ScratchPad.record []
+ end
+
+ ruby_version_is ""..."2.4" do
+ it "raises an ArgumentError if called without a block" do
+ lambda do
+ EnumerableSpecs::Numerous.new.chunk
+ end.should raise_error(ArgumentError)
+ end
+ end
+
+ ruby_version_is "2.4" do
+ it "returns an Enumerator if called without a block" do
+ chunk = EnumerableSpecs::Numerous.new(1, 2, 3, 1, 2).chunk
+ chunk.should be_an_instance_of(Enumerator)
+ result = chunk.with_index {|elt, i| elt - i }.to_a
+ result.should == [[1, [1, 2, 3]], [-2, [1, 2]]]
+ end
+ end
+
+ it "returns an Enumerator if given a block" do
+ EnumerableSpecs::Numerous.new.chunk {}.should be_an_instance_of(Enumerator)
+ end
+
+ it "yields the current element and the current chunk to the block" do
+ e = EnumerableSpecs::Numerous.new(1, 2, 3)
+ e.chunk { |x| ScratchPad << x }.to_a
+ ScratchPad.recorded.should == [1, 2, 3]
+ end
+
+ it "returns elements of the Enumerable in an Array of Arrays, [v, ary], where 'ary' contains the consecutive elements for which the block returned the value 'v'" do
+ e = EnumerableSpecs::Numerous.new(1, 2, 3, 2, 3, 2, 1)
+ result = e.chunk { |x| x < 3 && 1 || 0 }.to_a
+ result.should == [[1, [1, 2]], [0, [3]], [1, [2]], [0, [3]], [1, [2, 1]]]
+ end
+
+ it "returns elements for which the block returns :_alone in separate Arrays" do
+ e = EnumerableSpecs::Numerous.new(1, 2, 3, 2, 1)
+ result = e.chunk { |x| x < 2 && :_alone }.to_a
+ result.should == [[:_alone, [1]], [false, [2, 3, 2]], [:_alone, [1]]]
+ end
+
+ it "does not return elements for which the block returns :_separator" do
+ e = EnumerableSpecs::Numerous.new(1, 2, 3, 3, 2, 1)
+ result = e.chunk { |x| x == 2 ? :_separator : 1 }.to_a
+ result.should == [[1, [1]], [1, [3, 3]], [1, [1]]]
+ end
+
+ it "does not return elements for which the block returns nil" do
+ e = EnumerableSpecs::Numerous.new(1, 2, 3, 2, 1)
+ result = e.chunk { |x| x == 2 ? nil : 1 }.to_a
+ result.should == [[1, [1]], [1, [3]], [1, [1]]]
+ end
+
+ it "raises a RuntimeError if the block returns a Symbol starting with an underscore other than :_alone or :_separator" do
+ e = EnumerableSpecs::Numerous.new(1, 2, 3, 2, 1)
+ lambda { e.chunk { |x| :_arbitrary }.to_a }.should raise_error(RuntimeError)
+ end
+
+ it "does not accept arguments" do
+ e = EnumerableSpecs::Numerous.new(1, 2, 3)
+ lambda {
+ e.chunk(1) {}
+ }.should raise_error(ArgumentError)
+ end
+
+ it 'returned Enumerator size returns nil' do
+ e = EnumerableSpecs::NumerousWithSize.new(1, 2, 3, 2, 1)
+ enum = e.chunk { |x| true }
+ enum.size.should == nil
+ end
+end
diff --git a/spec/ruby/core/enumerable/chunk_while_spec.rb b/spec/ruby/core/enumerable/chunk_while_spec.rb
new file mode 100644
index 0000000000..88d6d2983f
--- /dev/null
+++ b/spec/ruby/core/enumerable/chunk_while_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#chunk_while" do
+ before :each do
+ ary = [10, 9, 7, 6, 4, 3, 2, 1]
+ @enum = EnumerableSpecs::Numerous.new(*ary)
+ @result = @enum.chunk_while { |i, j| i - 1 == j }
+ @enum_length = ary.length
+ end
+
+ context "when given a block" do
+ it "returns an enumerator" do
+ @result.should be_an_instance_of(Enumerator)
+ end
+
+ it "splits chunks between adjacent elements i and j where the block returns false" do
+ @result.to_a.should == [[10, 9], [7, 6], [4, 3, 2, 1]]
+ end
+
+ it "calls the block for length of the receiver enumerable minus one times" do
+ times_called = 0
+ @enum.chunk_while do |i, j|
+ times_called += 1
+ i - 1 == j
+ end.to_a
+ times_called.should == (@enum_length - 1)
+ end
+ end
+
+ context "when not given a block" do
+ it "raises an ArgumentError" do
+ lambda { @enum.chunk_while }.should raise_error(ArgumentError)
+ end
+ end
+
+ context "on a single-element array" do
+ it "ignores the block and returns an enumerator that yields [element]" do
+ [1].chunk_while {|x| x.even?}.to_a.should == [[1]]
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/collect_concat_spec.rb b/spec/ruby/core/enumerable/collect_concat_spec.rb
new file mode 100644
index 0000000000..6e34c9eb93
--- /dev/null
+++ b/spec/ruby/core/enumerable/collect_concat_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/collect_concat'
+
+describe "Enumerable#collect_concat" do
+ it_behaves_like :enumerable_collect_concat , :collect_concat
+end
diff --git a/spec/ruby/core/enumerable/collect_spec.rb b/spec/ruby/core/enumerable/collect_spec.rb
new file mode 100644
index 0000000000..1016b67798
--- /dev/null
+++ b/spec/ruby/core/enumerable/collect_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/collect'
+
+describe "Enumerable#collect" do
+ it_behaves_like :enumerable_collect , :collect
+end
diff --git a/spec/ruby/core/enumerable/count_spec.rb b/spec/ruby/core/enumerable/count_spec.rb
new file mode 100644
index 0000000000..50a1c8e1a4
--- /dev/null
+++ b/spec/ruby/core/enumerable/count_spec.rb
@@ -0,0 +1,59 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#count" do
+ before :each do
+ @elements = [1, 2, 4, 2]
+ @numerous = EnumerableSpecs::Numerous.new(*@elements)
+ end
+
+ describe "when no argument or a block" do
+ it "returns size" do
+ @numerous.count.should == 4
+ end
+
+ describe "with a custom size method" do
+ before :each do
+ class << @numerous
+ def size
+ :any_object
+ end
+ end
+ end
+
+ it "ignores the custom size method" do
+ @numerous.count.should == 4
+ end
+ end
+ end
+
+ it "counts nils if given nil as an argument" do
+ EnumerableSpecs::Numerous.new(nil, nil, nil, false).count(nil).should == 3
+ end
+
+ it "accepts an argument for comparison using ==" do
+ @numerous.count(2).should == 2
+ end
+
+ it "uses a block for comparison" do
+ @numerous.count{|x| x%2==0 }.should == 3
+ end
+
+ it "ignores the block when given an argument" do
+ -> {
+ @numerous.count(4){|x| x%2==0 }.should == 1
+ }.should complain(/given block not used/)
+ end
+
+ describe "when each yields multiple values" do
+ it "gathers initial args as elements" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.count {|e| e == 1 }.should == 1
+ end
+
+ it "accepts an argument for comparison using ==" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.count([1, 2]).should == 1
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/cycle_spec.rb b/spec/ruby/core/enumerable/cycle_spec.rb
new file mode 100644
index 0000000000..2db1ae56fe
--- /dev/null
+++ b/spec/ruby/core/enumerable/cycle_spec.rb
@@ -0,0 +1,104 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumeratorized'
+
+describe "Enumerable#cycle" do
+ describe "passed no argument or nil" do
+ it "loops indefinitely" do
+ [[],[nil]].each do |args|
+ bomb = 10
+ EnumerableSpecs::Numerous.new.cycle(*args) do
+ bomb -= 1
+ break 42 if bomb <= 0
+ end.should == 42
+ bomb.should == 0
+ end
+ end
+
+ it "returns nil if there are no elements" do
+ out = EnumerableSpecs::Empty.new.cycle { break :nope }
+ out.should be_nil
+ end
+
+ it "yields successive elements of the array repeatedly" do
+ b = []
+ EnumerableSpecs::Numerous.new(1,2,3).cycle do |elem|
+ b << elem
+ break if b.size == 7
+ end
+ b.should == [1,2,3,1,2,3,1]
+ end
+
+ it "calls each at most once" do
+ enum = EnumerableSpecs::EachCounter.new(1, 2)
+ enum.cycle.first(6).should == [1,2,1,2,1,2]
+ enum.times_called.should == 1
+ end
+
+ it "yields only when necessary" do
+ enum = EnumerableSpecs::EachCounter.new(10, 20, 30)
+ enum.cycle { |x| break if x == 20}
+ enum.times_yielded.should == 2
+ end
+ end
+
+ describe "passed a number n as an argument" do
+ it "returns nil and does nothing for non positive n" do
+ EnumerableSpecs::ThrowingEach.new.cycle(0) {}.should be_nil
+ EnumerableSpecs::NoEach.new.cycle(-22) {}.should be_nil
+ end
+
+ it "calls each at most once" do
+ enum = EnumerableSpecs::EachCounter.new(1, 2)
+ enum.cycle(3).to_a.should == [1,2,1,2,1,2]
+ enum.times_called.should == 1
+ end
+
+ it "yields only when necessary" do
+ enum = EnumerableSpecs::EachCounter.new(10, 20, 30)
+ enum.cycle(3) { |x| break if x == 20}
+ enum.times_yielded.should == 2
+ end
+
+ it "tries to convert n to an Integer using #to_int" do
+ enum = EnumerableSpecs::Numerous.new(3, 2, 1)
+ enum.cycle(2.3).to_a.should == [3, 2, 1, 3, 2, 1]
+
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(2)
+ enum.cycle(obj).to_a.should == [3, 2, 1, 3, 2, 1]
+ end
+
+ it "raises a TypeError when the passed n cannot be coerced to Integer" do
+ enum = EnumerableSpecs::Numerous.new
+ lambda{ enum.cycle("cat"){} }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError if more arguments are passed" do
+ enum = EnumerableSpecs::Numerous.new
+ lambda{ enum.cycle(1, 2) {} }.should raise_error(ArgumentError)
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.cycle(2).to_a.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9], [1, 2], [3, 4, 5], [6, 7, 8, 9]]
+ end
+ end
+
+ describe "Enumerable with size" do
+ before :all do
+ @object = EnumerableSpecs::NumerousWithSize.new(1, 2, 3, 4)
+ @empty_object = EnumerableSpecs::EmptyWithSize.new
+ end
+ it_should_behave_like :enumeratorized_with_cycle_size
+ end
+
+ describe "Enumerable with no size" do
+ before :all do
+ @object = EnumerableSpecs::Numerous.new(1, 2, 3, 4)
+ @method = :cycle
+ end
+ it_should_behave_like :enumeratorized_with_unknown_size
+ end
+
+end
diff --git a/spec/ruby/core/enumerable/detect_spec.rb b/spec/ruby/core/enumerable/detect_spec.rb
new file mode 100644
index 0000000000..e912134fed
--- /dev/null
+++ b/spec/ruby/core/enumerable/detect_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/find'
+
+describe "Enumerable#detect" do
+ it_behaves_like :enumerable_find , :detect
+end
diff --git a/spec/ruby/core/enumerable/drop_spec.rb b/spec/ruby/core/enumerable/drop_spec.rb
new file mode 100644
index 0000000000..1f54d4aac7
--- /dev/null
+++ b/spec/ruby/core/enumerable/drop_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#drop" do
+ before :each do
+ @enum = EnumerableSpecs::Numerous.new(3, 2, 1, :go)
+ end
+
+ it "requires exactly one argument" do
+ lambda{ @enum.drop{} }.should raise_error(ArgumentError)
+ lambda{ @enum.drop(1, 2){} }.should raise_error(ArgumentError)
+ end
+
+ describe "passed a number n as an argument" do
+ it "raises ArgumentError if n < 0" do
+ lambda{ @enum.drop(-1) }.should raise_error(ArgumentError)
+ end
+
+ it "tries to convert n to an Integer using #to_int" do
+ @enum.drop(2.3).should == [1, :go]
+
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(2)
+ @enum.drop(obj).should == [1, :go]
+ end
+
+ it "returns [] for empty enumerables" do
+ EnumerableSpecs::Empty.new.drop(0).should == []
+ EnumerableSpecs::Empty.new.drop(2).should == []
+ end
+
+ it "returns [] if dropping all" do
+ @enum.drop(5).should == []
+ EnumerableSpecs::Numerous.new(3, 2, 1, :go).drop(4).should == []
+ end
+
+ it "raises a TypeError when the passed n cannot be coerced to Integer" do
+ lambda{ @enum.drop("hat") }.should raise_error(TypeError)
+ lambda{ @enum.drop(nil) }.should raise_error(TypeError)
+ end
+
+ end
+end
diff --git a/spec/ruby/core/enumerable/drop_while_spec.rb b/spec/ruby/core/enumerable/drop_while_spec.rb
new file mode 100644
index 0000000000..636c3d284a
--- /dev/null
+++ b/spec/ruby/core/enumerable/drop_while_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumerable_enumeratorized'
+
+describe "Enumerable#drop_while" do
+ before :each do
+ @enum = EnumerableSpecs::Numerous.new(3, 2, 1, :go)
+ end
+
+ it "returns an Enumerator if no block given" do
+ @enum.drop_while.should be_an_instance_of(Enumerator)
+ end
+
+ it "returns no/all elements for {true/false} block" do
+ @enum.drop_while{true}.should == []
+ @enum.drop_while{false}.should == @enum.to_a
+ end
+
+ it "accepts returns other than true/false" do
+ @enum.drop_while{1}.should == []
+ @enum.drop_while{nil}.should == @enum.to_a
+ end
+
+ it "passes elements to the block until the first false" do
+ a = []
+ @enum.drop_while{|obj| (a << obj).size < 3}.should == [1, :go]
+ a.should == [3, 2, 1]
+ end
+
+ it "will only go through what's needed" do
+ enum = EnumerableSpecs::EachCounter.new(1,2,3,4)
+ enum.drop_while { |x|
+ break 42 if x == 3
+ true
+ }.should == 42
+ enum.times_yielded.should == 3
+ end
+
+ it "doesn't return self when it could" do
+ a = [1,2,3]
+ a.drop_while{false}.should_not equal(a)
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.drop_while {|e| e != [6, 7, 8, 9] }.should == [[6, 7, 8, 9]]
+ end
+
+ it_behaves_like :enumerable_enumeratorized_with_unknown_size, :drop_while
+end
diff --git a/spec/ruby/core/enumerable/each_cons_spec.rb b/spec/ruby/core/enumerable/each_cons_spec.rb
new file mode 100644
index 0000000000..40d0d53517
--- /dev/null
+++ b/spec/ruby/core/enumerable/each_cons_spec.rb
@@ -0,0 +1,99 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumeratorized'
+
+describe "Enumerable#each_cons" do
+ before :each do
+ @enum = EnumerableSpecs::Numerous.new(4,3,2,1)
+ @in_threes = [[4,3,2],[3,2,1]]
+ end
+
+ it "passes element groups to the block" do
+ acc = []
+ @enum.each_cons(3){|g| acc << g}.should be_nil
+ acc.should == @in_threes
+ end
+
+ it "raises an ArgumentError if there is not a single parameter > 0" do
+ lambda{ @enum.each_cons(0){} }.should raise_error(ArgumentError)
+ lambda{ @enum.each_cons(-2){} }.should raise_error(ArgumentError)
+ lambda{ @enum.each_cons{} }.should raise_error(ArgumentError)
+ lambda{ @enum.each_cons(2,2){} }.should raise_error(ArgumentError)
+ lambda{ @enum.each_cons(0) }.should raise_error(ArgumentError)
+ lambda{ @enum.each_cons(-2) }.should raise_error(ArgumentError)
+ lambda{ @enum.each_cons }.should raise_error(ArgumentError)
+ lambda{ @enum.each_cons(2,2) }.should raise_error(ArgumentError)
+ end
+
+ it "tries to convert n to an Integer using #to_int" do
+ acc = []
+ @enum.each_cons(3.3){|g| acc << g}.should == nil
+ acc.should == @in_threes
+
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(3)
+ @enum.each_cons(obj){|g| break g.length}.should == 3
+ end
+
+ it "works when n is >= full length" do
+ full = @enum.to_a
+ acc = []
+ @enum.each_cons(full.length){|g| acc << g}
+ acc.should == [full]
+ acc = []
+ @enum.each_cons(full.length+1){|g| acc << g}
+ acc.should == []
+ end
+
+ it "yields only as much as needed" do
+ cnt = EnumerableSpecs::EachCounter.new(1, 2, :stop, "I said stop!", :got_it)
+ cnt.each_cons(2) {|g| break 42 if g[-1] == :stop }.should == 42
+ cnt.times_yielded.should == 3
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.each_cons(2).to_a.should == [[[1, 2], [3, 4, 5]], [[3, 4, 5], [6, 7, 8, 9]]]
+ end
+
+ describe "when no block is given" do
+ it "returns an enumerator" do
+ e = @enum.each_cons(3)
+ e.should be_an_instance_of(Enumerator)
+ e.to_a.should == @in_threes
+ end
+
+ describe "Enumerable with size" do
+ describe "returned Enumerator" do
+ describe "size" do
+ it "returns enum size - each_cons argument + 1" do
+ enum = EnumerableSpecs::NumerousWithSize.new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ enum.each_cons(10).size.should == 1
+ enum.each_cons(9).size.should == 2
+ enum.each_cons(3).size.should == 8
+ enum.each_cons(2).size.should == 9
+ enum.each_cons(1).size.should == 10
+ end
+
+ it "returns 0 when the argument is larger than self" do
+ enum = EnumerableSpecs::NumerousWithSize.new(1, 2, 3)
+ enum.each_cons(20).size.should == 0
+ end
+
+ it "returns 0 when the enum is empty" do
+ enum = EnumerableSpecs::EmptyWithSize.new
+ enum.each_cons(10).size.should == 0
+ end
+ end
+ end
+ end
+
+ describe "Enumerable with no size" do
+ before :all do
+ @object = EnumerableSpecs::Numerous.new(1, 2, 3, 4)
+ @method = [:each_cons, 8]
+ end
+ it_should_behave_like :enumeratorized_with_unknown_size
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/each_entry_spec.rb b/spec/ruby/core/enumerable/each_entry_spec.rb
new file mode 100644
index 0000000000..41b61f6208
--- /dev/null
+++ b/spec/ruby/core/enumerable/each_entry_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumerable_enumeratorized'
+
+describe "Enumerable#each_entry" do
+ before :each do
+ ScratchPad.record []
+ @enum = EnumerableSpecs::YieldsMixed.new
+ @entries = [1, [2], [3,4], [5,6,7], [8,9], nil, []]
+ end
+
+ it "yields multiple arguments as an array" do
+ acc = []
+ @enum.each_entry {|g| acc << g}.should equal(@enum)
+ acc.should == @entries
+ end
+
+ it "returns an enumerator if no block" do
+ e = @enum.each_entry
+ e.should be_an_instance_of(Enumerator)
+ e.to_a.should == @entries
+ end
+
+ it "passes through the values yielded by #each_with_index" do
+ [:a, :b].each_with_index.each_entry { |x, i| ScratchPad << [x, i] }
+ ScratchPad.recorded.should == [[:a, 0], [:b, 1]]
+ end
+
+ it "raises an ArgumentError when extra arguments" do
+ lambda { @enum.each_entry("one").to_a }.should raise_error(ArgumentError)
+ lambda { @enum.each_entry("one"){}.to_a }.should raise_error(ArgumentError)
+ end
+
+ it "passes extra arguments to #each" do
+ enum = EnumerableSpecs::EachCounter.new(1, 2)
+ enum.each_entry(:foo, "bar").to_a.should == [1,2]
+ enum.arguments_passed.should == [:foo, "bar"]
+ end
+
+ it_behaves_like :enumerable_enumeratorized_with_origin_size, :each_entry
+end
diff --git a/spec/ruby/core/enumerable/each_slice_spec.rb b/spec/ruby/core/enumerable/each_slice_spec.rb
new file mode 100644
index 0000000000..9ffb95d29b
--- /dev/null
+++ b/spec/ruby/core/enumerable/each_slice_spec.rb
@@ -0,0 +1,101 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumeratorized'
+
+describe "Enumerable#each_slice" do
+ before :each do
+ @enum = EnumerableSpecs::Numerous.new(7,6,5,4,3,2,1)
+ @sliced = [[7,6,5],[4,3,2],[1]]
+ end
+
+ it "passes element groups to the block" do
+ acc = []
+ @enum.each_slice(3){|g| acc << g}.should be_nil
+ acc.should == @sliced
+ end
+
+ it "raises an ArgumentError if there is not a single parameter > 0" do
+ lambda{ @enum.each_slice(0){} }.should raise_error(ArgumentError)
+ lambda{ @enum.each_slice(-2){} }.should raise_error(ArgumentError)
+ lambda{ @enum.each_slice{} }.should raise_error(ArgumentError)
+ lambda{ @enum.each_slice(2,2){} }.should raise_error(ArgumentError)
+ lambda{ @enum.each_slice(0) }.should raise_error(ArgumentError)
+ lambda{ @enum.each_slice(-2) }.should raise_error(ArgumentError)
+ lambda{ @enum.each_slice }.should raise_error(ArgumentError)
+ lambda{ @enum.each_slice(2,2) }.should raise_error(ArgumentError)
+ end
+
+ it "tries to convert n to an Integer using #to_int" do
+ acc = []
+ @enum.each_slice(3.3){|g| acc << g}.should == nil
+ acc.should == @sliced
+
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(3)
+ @enum.each_slice(obj){|g| break g.length}.should == 3
+ end
+
+ it "works when n is >= full length" do
+ full = @enum.to_a
+ acc = []
+ @enum.each_slice(full.length){|g| acc << g}
+ acc.should == [full]
+ acc = []
+ @enum.each_slice(full.length+1){|g| acc << g}
+ acc.should == [full]
+ end
+
+ it "yields only as much as needed" do
+ cnt = EnumerableSpecs::EachCounter.new(1, 2, :stop, "I said stop!", :got_it)
+ cnt.each_slice(2) {|g| break 42 if g[0] == :stop }.should == 42
+ cnt.times_yielded.should == 4
+ end
+
+ it "returns an enumerator if no block" do
+ e = @enum.each_slice(3)
+ e.should be_an_instance_of(Enumerator)
+ e.to_a.should == @sliced
+ 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]]]
+ end
+
+ describe "when no block is given" do
+ it "returns an enumerator" do
+ e = @enum.each_slice(3)
+ e.should be_an_instance_of(Enumerator)
+ e.to_a.should == @sliced
+ end
+
+ describe "Enumerable with size" do
+ describe "returned Enumerator" do
+ describe "size" do
+ it "returns the ceil of Enumerable size divided by the argument value" do
+ enum = EnumerableSpecs::NumerousWithSize.new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ enum.each_slice(10).size.should == 1
+ enum.each_slice(9).size.should == 2
+ enum.each_slice(3).size.should == 4
+ enum.each_slice(2).size.should == 5
+ enum.each_slice(1).size.should == 10
+ end
+
+ it "returns 0 when the Enumerable is empty" do
+ enum = EnumerableSpecs::EmptyWithSize.new
+ enum.each_slice(10).size.should == 0
+ end
+ end
+ end
+ end
+
+ describe "Enumerable with no size" do
+ before :all do
+ @object = EnumerableSpecs::Numerous.new(1, 2, 3, 4)
+ @method = [:each_slice, 8]
+ end
+ it_should_behave_like :enumeratorized_with_unknown_size
+ end
+ end
+
+end
diff --git a/spec/ruby/core/enumerable/each_with_index_spec.rb b/spec/ruby/core/enumerable/each_with_index_spec.rb
new file mode 100644
index 0000000000..122e88eab7
--- /dev/null
+++ b/spec/ruby/core/enumerable/each_with_index_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumerable_enumeratorized'
+
+describe "Enumerable#each_with_index" do
+
+ before :each do
+ @b = EnumerableSpecs::Numerous.new(2, 5, 3, 6, 1, 4)
+ end
+
+ it "passes each element and its index to block" do
+ @a = []
+ @b.each_with_index { |o, i| @a << [o, i] }
+ @a.should == [[2, 0], [5, 1], [3, 2], [6, 3], [1, 4], [4, 5]]
+ end
+
+ it "provides each element to the block" do
+ acc = []
+ obj = EnumerableSpecs::EachDefiner.new()
+ res = obj.each_with_index {|a,i| acc << [a,i]}
+ acc.should == []
+ obj.should == res
+ end
+
+ it "provides each element to the block and its index" do
+ acc = []
+ res = @b.each_with_index {|a,i| acc << [a,i]}
+ [[2, 0], [5, 1], [3, 2], [6, 3], [1, 4], [4, 5]].should == acc
+ res.should eql(@b)
+ end
+
+ it "binds splat arguments properly" do
+ acc = []
+ res = @b.each_with_index { |*b| c,d = b; acc << c; acc << d }
+ [2, 0, 5, 1, 3, 2, 6, 3, 1, 4, 4, 5].should == acc
+ res.should eql(@b)
+ end
+
+ it "returns an enumerator if no block" do
+ e = @b.each_with_index
+ e.should be_an_instance_of(Enumerator)
+ e.to_a.should == [[2, 0], [5, 1], [3, 2], [6, 3], [1, 4], [4, 5]]
+ end
+
+ it "passes extra parameters to each" do
+ count = EnumerableSpecs::EachCounter.new(:apple)
+ e = count.each_with_index(:foo, :bar)
+ e.to_a.should == [[:apple, 0]]
+ count.arguments_passed.should == [:foo, :bar]
+ end
+
+ it_behaves_like :enumerable_enumeratorized_with_origin_size, :each_with_index
+end
diff --git a/spec/ruby/core/enumerable/each_with_object_spec.rb b/spec/ruby/core/enumerable/each_with_object_spec.rb
new file mode 100644
index 0000000000..35665e7019
--- /dev/null
+++ b/spec/ruby/core/enumerable/each_with_object_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumerable_enumeratorized'
+
+describe "Enumerable#each_with_object" do
+ before :each do
+ @values = [2, 5, 3, 6, 1, 4]
+ @enum = EnumerableSpecs::Numerous.new(*@values)
+ @initial = "memo"
+ end
+
+ it "passes each element and its argument to the block" do
+ acc = []
+ @enum.each_with_object(@initial) do |elem, obj|
+ obj.should equal(@initial)
+ obj = 42
+ acc << elem
+ end.should equal(@initial)
+ acc.should == @values
+ end
+
+ it "returns an enumerator if no block" do
+ acc = []
+ e = @enum.each_with_object(@initial)
+ e.each do |elem, obj|
+ obj.should equal(@initial)
+ obj = 42
+ acc << elem
+ end.should equal(@initial)
+ acc.should == @values
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ array = []
+ multi.each_with_object(array) { |elem, obj| obj << elem }
+ array.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
+ end
+
+ it_behaves_like :enumerable_enumeratorized_with_origin_size, [:each_with_object, []]
+end
diff --git a/spec/ruby/core/enumerable/entries_spec.rb b/spec/ruby/core/enumerable/entries_spec.rb
new file mode 100644
index 0000000000..83232cfa06
--- /dev/null
+++ b/spec/ruby/core/enumerable/entries_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/entries'
+
+describe "Enumerable#entries" do
+ it_behaves_like :enumerable_entries , :entries
+end
diff --git a/spec/ruby/core/enumerable/filter_spec.rb b/spec/ruby/core/enumerable/filter_spec.rb
new file mode 100644
index 0000000000..f2dc7a7b71
--- /dev/null
+++ b/spec/ruby/core/enumerable/filter_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/find_all'
+
+ruby_version_is "2.6" do
+ describe "Enumerable#filter" do
+ it_behaves_like(:enumerable_find_all , :filter)
+ end
+end
diff --git a/spec/ruby/core/enumerable/find_all_spec.rb b/spec/ruby/core/enumerable/find_all_spec.rb
new file mode 100644
index 0000000000..ce9058fe77
--- /dev/null
+++ b/spec/ruby/core/enumerable/find_all_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/find_all'
+
+describe "Enumerable#find_all" do
+ it_behaves_like :enumerable_find_all , :find_all
+end
diff --git a/spec/ruby/core/enumerable/find_index_spec.rb b/spec/ruby/core/enumerable/find_index_spec.rb
new file mode 100644
index 0000000000..542660fe04
--- /dev/null
+++ b/spec/ruby/core/enumerable/find_index_spec.rb
@@ -0,0 +1,89 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumerable_enumeratorized'
+
+describe "Enumerable#find_index" do
+ before :each do
+ @elements = [2, 4, 6, 8, 10]
+ @numerous = EnumerableSpecs::Numerous.new(*@elements)
+ @yieldsmixed = EnumerableSpecs::YieldsMixed2.new
+ end
+
+ it "passes each entry in enum to block while block when block is false" do
+ visited_elements = []
+ @numerous.find_index do |element|
+ visited_elements << element
+ false
+ end
+ visited_elements.should == @elements
+ end
+
+ it "returns nil when the block is false" do
+ @numerous.find_index {|e| false }.should == nil
+ end
+
+ it "returns the first index for which the block is not false" do
+ @elements.each_with_index do |element, index|
+ @numerous.find_index {|e| e > element - 1 }.should == index
+ end
+ end
+
+ it "returns the first index found" do
+ repeated = [10, 11, 11, 13, 11, 13, 10, 10, 13, 11]
+ numerous_repeat = EnumerableSpecs::Numerous.new(*repeated)
+ repeated.each do |element|
+ numerous_repeat.find_index(element).should == element - 10
+ end
+ end
+
+ it "returns nil when the element not found" do
+ @numerous.find_index(-1).should == nil
+ end
+
+ it "ignores the block if an argument is given" do
+ -> {
+ @numerous.find_index(-1) {|e| true }.should == nil
+ }.should complain(/given block not used/)
+ end
+
+ it "returns an Enumerator if no block given" do
+ @numerous.find_index.should be_an_instance_of(Enumerator)
+ end
+
+ it "uses #== for testing equality" do
+ [2].to_enum.find_index(2.0).should == 0
+ [2.0].to_enum.find_index(2).should == 0
+ end
+
+ describe "without block" do
+ it "gathers whole arrays as elements when each yields multiple" do
+ @yieldsmixed.find_index([0, 1, 2]).should == 3
+ end
+ end
+
+ describe "with block" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ after :each do
+ ScratchPad.clear
+ end
+
+ describe "given a single yield parameter" do
+ it "passes first element to the parameter" do
+ @yieldsmixed.find_index {|a| ScratchPad << a; false }
+ ScratchPad.recorded.should == EnumerableSpecs::YieldsMixed2.first_yields
+ end
+ end
+
+ describe "given a greedy yield parameter" do
+ it "passes a gathered array to the parameter" do
+ @yieldsmixed.find_index {|*args| ScratchPad << args; false }
+ ScratchPad.recorded.should == EnumerableSpecs::YieldsMixed2.greedy_yields
+ end
+ end
+ end
+
+ it_behaves_like :enumerable_enumeratorized_with_unknown_size, :find_index
+end
diff --git a/spec/ruby/core/enumerable/find_spec.rb b/spec/ruby/core/enumerable/find_spec.rb
new file mode 100644
index 0000000000..25aa3bf103
--- /dev/null
+++ b/spec/ruby/core/enumerable/find_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/find'
+
+describe "Enumerable#find" do
+ it_behaves_like :enumerable_find , :find
+end
diff --git a/spec/ruby/core/enumerable/first_spec.rb b/spec/ruby/core/enumerable/first_spec.rb
new file mode 100644
index 0000000000..19d02d7522
--- /dev/null
+++ b/spec/ruby/core/enumerable/first_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/take'
+
+describe "Enumerable#first" do
+ it "returns the first element" do
+ EnumerableSpecs::Numerous.new.first.should == 2
+ EnumerableSpecs::Empty.new.first.should == nil
+ end
+
+ it "returns nil if self is empty" do
+ EnumerableSpecs::Empty.new.first.should == nil
+ end
+
+ it 'returns a gathered array from yield parameters' do
+ EnumerableSpecs::YieldsMulti.new.to_enum.first.should == [1, 2]
+ EnumerableSpecs::YieldsMixed2.new.to_enum.first.should == nil
+ end
+
+ it "raises a RangeError when passed a Bignum" do
+ enum = EnumerableSpecs::Empty.new
+ lambda { enum.first(bignum_value) }.should raise_error(RangeError)
+ end
+
+ describe "when passed an argument" do
+ it_behaves_like :enumerable_take, :first
+ end
+end
diff --git a/spec/ruby/core/enumerable/fixtures/classes.rb b/spec/ruby/core/enumerable/fixtures/classes.rb
new file mode 100644
index 0000000000..e50bf34779
--- /dev/null
+++ b/spec/ruby/core/enumerable/fixtures/classes.rb
@@ -0,0 +1,345 @@
+module EnumerableSpecs
+
+ class Numerous
+ include Enumerable
+ def initialize(*list)
+ @list = list.empty? ? [2, 5, 3, 6, 1, 4] : list
+ end
+
+ def each
+ @list.each { |i| yield i }
+ end
+ end
+
+ class NumerousWithSize < Numerous
+ def size
+ @list.size
+ end
+ end
+
+ class EachCounter < Numerous
+ attr_reader :times_called, :times_yielded, :arguments_passed
+ def initialize(*list)
+ super(*list)
+ @times_yielded = @times_called = 0
+ end
+
+ def each(*arg)
+ @times_called += 1
+ @times_yielded = 0
+ @arguments_passed = arg
+ @list.each do |i|
+ @times_yielded +=1
+ yield i
+ end
+ end
+ end
+
+ class Empty
+ include Enumerable
+ def each
+ end
+ end
+
+ class EmptyWithSize
+ include Enumerable
+ def each
+ end
+ def size
+ 0
+ end
+ end
+
+ class ThrowingEach
+ include Enumerable
+ def each
+ raise "from each"
+ end
+ end
+
+ class NoEach
+ include Enumerable
+ end
+
+ # (Legacy form rubycon)
+ class EachDefiner
+
+ include Enumerable
+
+ attr_reader :arr
+
+ def initialize(*arr)
+ @arr = arr
+ end
+
+ def each
+ i = 0
+ loop do
+ break if i == @arr.size
+ yield @arr[i]
+ i += 1
+ end
+ end
+
+ end
+
+ class SortByDummy
+ def initialize(s)
+ @s = s
+ end
+
+ def s
+ @s
+ end
+ end
+
+ class ComparesByVowelCount
+
+ attr_accessor :value, :vowels
+
+ def self.wrap(*args)
+ args.map {|element| ComparesByVowelCount.new(element)}
+ end
+
+ def initialize(string)
+ self.value = string
+ self.vowels = string.gsub(/[^aeiou]/, '').size
+ end
+
+ def <=>(other)
+ self.vowels <=> other.vowels
+ end
+
+ end
+
+ class InvalidComparable
+ def <=>(other)
+ "Not Valid"
+ end
+ end
+
+ class ArrayConvertable
+ attr_accessor :called
+ def initialize(*values)
+ @values = values
+ end
+
+ def to_a
+ self.called = :to_a
+ @values
+ end
+
+ def to_ary
+ self.called = :to_ary
+ @values
+ end
+ end
+
+ class EnumConvertable
+ attr_accessor :called
+ attr_accessor :sym
+ def initialize(delegate)
+ @delegate = delegate
+ end
+
+ def to_enum(sym)
+ self.called = :to_enum
+ self.sym = sym
+ @delegate.to_enum(sym)
+ end
+
+ def respond_to_missing?(*args)
+ @delegate.respond_to?(*args)
+ end
+ end
+
+ class Equals
+ def initialize(obj)
+ @obj = obj
+ end
+ def ==(other)
+ @obj == other
+ end
+ end
+
+ class YieldsMulti
+ include Enumerable
+ def each
+ yield 1,2
+ yield 3,4,5
+ yield 6,7,8,9
+ end
+ end
+
+ class YieldsMultiWithFalse
+ include Enumerable
+ def each
+ yield false,2
+ yield false,4,5
+ yield false,7,8,9
+ end
+ end
+
+ class YieldsMultiWithSingleTrue
+ include Enumerable
+ def each
+ yield false,2
+ yield true,4,5
+ yield false,7,8,9
+ end
+ end
+
+ class YieldsMixed
+ include Enumerable
+ def each
+ yield 1
+ yield [2]
+ yield 3,4
+ yield 5,6,7
+ yield [8,9]
+ yield nil
+ yield []
+ end
+ end
+
+ class YieldsMixed2
+ include Enumerable
+
+ def self.first_yields
+ [nil, 0, 0, 0, 0, nil, :default_arg, [], [], [0], [0, 1], [0, 1, 2]]
+ end
+
+ def self.gathered_yields
+ [nil, 0, [0, 1], [0, 1, 2], [0, 1, 2], nil, :default_arg, [], [], [0], [0, 1], [0, 1, 2]]
+ end
+
+ def self.gathered_yields_with_args(arg, *args)
+ [nil, 0, [0, 1], [0, 1, 2], [0, 1, 2], nil, arg, args, [], [0], [0, 1], [0, 1, 2]]
+ end
+
+ def self.greedy_yields
+ [[], [0], [0, 1], [0, 1, 2], [0, 1, 2], [nil], [:default_arg], [[]], [[]], [[0]], [[0, 1]], [[0, 1, 2]]]
+ end
+
+ def each(arg=:default_arg, *args)
+ yield
+ yield 0
+ yield 0, 1
+ yield 0, 1, 2
+ yield(*[0, 1, 2])
+ yield nil
+ yield arg
+ yield args
+ yield []
+ yield [0]
+ yield [0, 1]
+ yield [0, 1, 2]
+ end
+ end
+
+ class ReverseComparable
+ include Comparable
+ def initialize(num)
+ @num = num
+ end
+
+ attr_accessor :num
+
+ # Reverse comparison
+ def <=>(other)
+ other.num <=> @num
+ end
+ end
+
+ class ComparableWithFixnum
+ include Comparable
+ def initialize(num)
+ @num = num
+ end
+
+ def <=>(fixnum)
+ @num <=> fixnum
+ end
+ end
+
+ class Uncomparable
+ def <=>(obj)
+ nil
+ end
+ end
+
+ class Undupable
+ attr_reader :initialize_called, :initialize_dup_called
+ def dup
+ raise "Can't, sorry"
+ end
+
+ def clone
+ raise "Can't, either, sorry"
+ end
+
+ def initialize
+ @initialize_dup = true
+ end
+
+ def initialize_dup(arg)
+ @initialize_dup_called = true
+ end
+ end
+
+ class Freezy
+ include Enumerable
+
+ def each
+ yield 1
+ yield 2
+ end
+
+ def to_a
+ super.freeze
+ end
+ end
+
+ class MapReturnsEnumerable
+ include Enumerable
+
+ class EnumerableMapping
+ include Enumerable
+
+ def initialize(items, block)
+ @items = items
+ @block = block
+ end
+
+ def each
+ @items.each do |i|
+ yield @block.call(i)
+ end
+ end
+ end
+
+ def each
+ yield 1
+ yield 2
+ yield 3
+ end
+
+ def map(&block)
+ EnumerableMapping.new(self, block)
+ end
+ end
+
+ class Pattern
+ attr_reader :yielded
+
+ def initialize(&block)
+ @block = block
+ @yielded = []
+ end
+
+ def ===(*args)
+ @yielded << args
+ @block.call(*args)
+ end
+ end
+end # EnumerableSpecs utility classes
diff --git a/spec/ruby/core/enumerable/flat_map_spec.rb b/spec/ruby/core/enumerable/flat_map_spec.rb
new file mode 100644
index 0000000000..a294b9ddad
--- /dev/null
+++ b/spec/ruby/core/enumerable/flat_map_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/collect_concat'
+
+describe "Enumerable#flat_map" do
+ it_behaves_like :enumerable_collect_concat , :flat_map
+end
diff --git a/spec/ruby/core/enumerable/grep_spec.rb b/spec/ruby/core/enumerable/grep_spec.rb
new file mode 100644
index 0000000000..a4dd26468d
--- /dev/null
+++ b/spec/ruby/core/enumerable/grep_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#grep" do
+ before :each do
+ @a = EnumerableSpecs::EachDefiner.new( 2, 4, 6, 8, 10)
+ end
+
+ it "grep without a block should return an array of all elements === pattern" do
+ class EnumerableSpecGrep; def ===(obj); obj == '2'; end; end
+
+ EnumerableSpecs::Numerous.new('2', 'a', 'nil', '3', false).grep(EnumerableSpecGrep.new).should == ['2']
+ end
+
+ it "grep with a block should return an array of elements === pattern passed through block" do
+ class EnumerableSpecGrep2; def ===(obj); /^ca/ =~ obj; end; end
+
+ EnumerableSpecs::Numerous.new("cat", "coat", "car", "cadr", "cost").grep(EnumerableSpecGrep2.new) { |i| i.upcase }.should == ["CAT", "CAR", "CADR"]
+ end
+
+ it "grep the enumerable (rubycon legacy)" do
+ EnumerableSpecs::EachDefiner.new().grep(1).should == []
+ @a.grep(3..7).should == [4,6]
+ @a.grep(3..7) {|a| a+1}.should == [5,7]
+ end
+
+ it "can use $~ in the block when used with a Regexp" do
+ ary = ["aba", "aba"]
+ ary.grep(/a(b)a/) { $1 }.should == ["b", "b"]
+ end
+
+ describe "with a block" do
+ before :each do
+ @numerous = EnumerableSpecs::Numerous.new(*(0..9).to_a)
+ def (@odd_matcher = BasicObject.new).===(obj)
+ obj.odd?
+ end
+ end
+
+ it "returns an Array of matched elements that mapped by the block" do
+ @numerous.grep(@odd_matcher) { |n| n * 2 }.should == [2, 6, 10, 14, 18]
+ end
+
+ it "calls the block with gathered array when yielded with multiple arguments" do
+ EnumerableSpecs::YieldsMixed2.new.grep(Object){ |e| e }.should == EnumerableSpecs::YieldsMixed2.gathered_yields
+ end
+
+ it "raises an ArgumentError when not given a pattern" do
+ -> { @numerous.grep { |e| e } }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/grep_v_spec.rb b/spec/ruby/core/enumerable/grep_v_spec.rb
new file mode 100644
index 0000000000..2268005dee
--- /dev/null
+++ b/spec/ruby/core/enumerable/grep_v_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#grep_v" do
+ before :each do
+ @numerous = EnumerableSpecs::Numerous.new(*(0..9).to_a)
+ def (@odd_matcher = BasicObject.new).===(obj)
+ obj.odd?
+ end
+ end
+
+ describe "without block" do
+ it "returns an Array of matched elements" do
+ @numerous.grep_v(@odd_matcher).should == [0, 2, 4, 6, 8]
+ end
+
+ it "compares pattern with gathered array when yielded with multiple arguments" do
+ (unmatcher = Object.new).stub!(:===).and_return(false)
+ EnumerableSpecs::YieldsMixed2.new.grep_v(unmatcher).should == EnumerableSpecs::YieldsMixed2.gathered_yields
+ end
+
+ it "raises an ArgumentError when not given a pattern" do
+ -> { @numerous.grep_v }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "with block" do
+ it "returns an Array of matched elements that mapped by the block" do
+ @numerous.grep_v(@odd_matcher) { |n| n * 2 }.should == [0, 4, 8, 12, 16]
+ end
+
+ it "calls the block with gathered array when yielded with multiple arguments" do
+ (unmatcher = Object.new).stub!(:===).and_return(false)
+ EnumerableSpecs::YieldsMixed2.new.grep_v(unmatcher){ |e| e }.should == EnumerableSpecs::YieldsMixed2.gathered_yields
+ end
+
+ it "raises an ArgumentError when not given a pattern" do
+ -> { @numerous.grep_v { |e| e } }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/group_by_spec.rb b/spec/ruby/core/enumerable/group_by_spec.rb
new file mode 100644
index 0000000000..580a90cf0c
--- /dev/null
+++ b/spec/ruby/core/enumerable/group_by_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumerable_enumeratorized'
+
+describe "Enumerable#group_by" do
+ it "returns a hash with values grouped according to the block" do
+ e = EnumerableSpecs::Numerous.new("foo", "bar", "baz")
+ h = e.group_by { |word| word[0..0].to_sym }
+ h.should == { f: ["foo"], b: ["bar", "baz"]}
+ end
+
+ it "returns an empty hash for empty enumerables" do
+ EnumerableSpecs::Empty.new.group_by { |x| x}.should == {}
+ end
+
+ it "returns a hash without default_proc" do
+ e = EnumerableSpecs::Numerous.new("foo", "bar", "baz")
+ h = e.group_by { |word| word[0..0].to_sym }
+ h[:some].should be_nil
+ h.default_proc.should be_nil
+ h.default.should be_nil
+ end
+
+ it "returns an Enumerator if called without a block" do
+ EnumerableSpecs::Numerous.new.group_by.should be_an_instance_of(Enumerator)
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ e = EnumerableSpecs::YieldsMulti.new
+ h = e.group_by { |i| i }
+ h.should == { [1, 2] => [[1, 2]],
+ [6, 7, 8, 9] => [[6, 7, 8, 9]],
+ [3, 4, 5] => [[3, 4, 5]] }
+ end
+
+ it "returns a tainted hash if self is tainted" do
+ EnumerableSpecs::Empty.new.taint.group_by {}.tainted?.should be_true
+ end
+
+ it "returns an untrusted hash if self is untrusted" do
+ EnumerableSpecs::Empty.new.untrust.group_by {}.untrusted?.should be_true
+ end
+
+ it_behaves_like :enumerable_enumeratorized_with_origin_size, :group_by
+end
diff --git a/spec/ruby/core/enumerable/include_spec.rb b/spec/ruby/core/enumerable/include_spec.rb
new file mode 100644
index 0000000000..dab1b04451
--- /dev/null
+++ b/spec/ruby/core/enumerable/include_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/include'
+
+describe "Enumerable#include?" do
+ it_behaves_like :enumerable_include, :include?
+end
diff --git a/spec/ruby/core/enumerable/inject_spec.rb b/spec/ruby/core/enumerable/inject_spec.rb
new file mode 100644
index 0000000000..e1fe216144
--- /dev/null
+++ b/spec/ruby/core/enumerable/inject_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/inject'
+
+describe "Enumerable#inject" do
+ it_behaves_like :enumerable_inject, :inject
+end
diff --git a/spec/ruby/core/enumerable/lazy_spec.rb b/spec/ruby/core/enumerable/lazy_spec.rb
new file mode 100644
index 0000000000..9a9ead81a0
--- /dev/null
+++ b/spec/ruby/core/enumerable/lazy_spec.rb
@@ -0,0 +1,10 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#lazy" do
+ it "returns an instance of Enumerator::Lazy" do
+ EnumerableSpecs::Numerous.new.lazy.should be_an_instance_of(Enumerator::Lazy)
+ end
+end
diff --git a/spec/ruby/core/enumerable/map_spec.rb b/spec/ruby/core/enumerable/map_spec.rb
new file mode 100644
index 0000000000..d65aec238c
--- /dev/null
+++ b/spec/ruby/core/enumerable/map_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/collect'
+
+describe "Enumerable#map" do
+ it_behaves_like :enumerable_collect , :map
+end
diff --git a/spec/ruby/core/enumerable/max_by_spec.rb b/spec/ruby/core/enumerable/max_by_spec.rb
new file mode 100644
index 0000000000..76590b602f
--- /dev/null
+++ b/spec/ruby/core/enumerable/max_by_spec.rb
@@ -0,0 +1,81 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumerable_enumeratorized'
+
+describe "Enumerable#max_by" do
+ it "returns an enumerator if no block" do
+ EnumerableSpecs::Numerous.new(42).max_by.should be_an_instance_of(Enumerator)
+ end
+
+ it "returns nil if #each yields no objects" do
+ EnumerableSpecs::Empty.new.max_by {|o| o.nonesuch }.should == nil
+ end
+
+ it "returns the object for whom the value returned by block is the largest" do
+ EnumerableSpecs::Numerous.new(*%w[1 2 3]).max_by {|obj| obj.to_i }.should == '3'
+ EnumerableSpecs::Numerous.new(*%w[three five]).max_by {|obj| obj.length }.should == 'three'
+ end
+
+ it "returns the object that appears first in #each in case of a tie" do
+ a, b, c = '1', '2', '2'
+ EnumerableSpecs::Numerous.new(a, b, c).max_by {|obj| obj.to_i }.should equal(b)
+ end
+
+ it "uses max.<=>(current) to determine order" do
+ a, b, c = (1..3).map{|n| EnumerableSpecs::ReverseComparable.new(n)}
+
+ # Just using self here to avoid additional complexity
+ EnumerableSpecs::Numerous.new(a, b, c).max_by {|obj| obj }.should == a
+ end
+
+ it "is able to return the maximum for enums that contain nils" do
+ enum = EnumerableSpecs::Numerous.new(nil, nil, true)
+ enum.max_by {|o| o.nil? ? 0 : 1 }.should == true
+ enum.max_by {|o| o.nil? ? 1 : 0 }.should == nil
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.max_by {|e| e.size}.should == [6, 7, 8, 9]
+ end
+
+ it_behaves_like :enumerable_enumeratorized_with_origin_size, :max_by
+
+ context "when called with an argument n" do
+ before :each do
+ @enum = EnumerableSpecs::Numerous.new(101, 55, 1, 20, 33, 500, 60)
+ end
+
+ context "without a block" do
+ it "returns an enumerator" do
+ @enum.max_by(2).should be_an_instance_of(Enumerator)
+ end
+ end
+
+ context "with a block" do
+ it "returns an array containing the maximum n elements based on the block's value" do
+ result = @enum.max_by(3) { |i| i.to_s }
+ result.should == [60, 55, 500]
+ end
+
+ context "on a enumerable of length x where x < n" do
+ it "returns an array containing the maximum n elements of length n" do
+ result = @enum.max_by(500) { |i| i.to_s }
+ result.length.should == 7
+ end
+ end
+
+ context "when n is negative" do
+ it "raises an ArgumentError" do
+ lambda { @enum.max_by(-1) { |i| i.to_s } }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ context "when n is nil" do
+ it "returns the maximum element" do
+ @enum.max_by(nil) { |i| i.to_s }.should == 60
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/max_spec.rb b/spec/ruby/core/enumerable/max_spec.rb
new file mode 100644
index 0000000000..b1d15126c2
--- /dev/null
+++ b/spec/ruby/core/enumerable/max_spec.rb
@@ -0,0 +1,119 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#max" do
+ before :each do
+ @e_strs = EnumerableSpecs::EachDefiner.new("333", "22", "666666", "1", "55555", "1010101010")
+ @e_ints = EnumerableSpecs::EachDefiner.new( 333, 22, 666666, 55555, 1010101010)
+ end
+
+ it "returns the maximum element" do
+ EnumerableSpecs::Numerous.new.max.should == 6
+ end
+
+ it "returns the maximum element (basics cases)" do
+ EnumerableSpecs::EachDefiner.new(55).max.should == 55
+
+ EnumerableSpecs::EachDefiner.new(11,99).max.should == 99
+ EnumerableSpecs::EachDefiner.new(99,11).max.should == 99
+ EnumerableSpecs::EachDefiner.new(2, 33, 4, 11).max.should == 33
+
+ EnumerableSpecs::EachDefiner.new(1,2,3,4,5).max.should == 5
+ EnumerableSpecs::EachDefiner.new(5,4,3,2,1).max.should == 5
+ EnumerableSpecs::EachDefiner.new(1,4,3,5,2).max.should == 5
+ EnumerableSpecs::EachDefiner.new(5,5,5,5,5).max.should == 5
+
+ EnumerableSpecs::EachDefiner.new("aa","tt").max.should == "tt"
+ EnumerableSpecs::EachDefiner.new("tt","aa").max.should == "tt"
+ EnumerableSpecs::EachDefiner.new("2","33","4","11").max.should == "4"
+
+ @e_strs.max.should == "666666"
+ @e_ints.max.should == 1010101010
+ end
+
+ it "returns nil for an empty Enumerable" do
+ EnumerableSpecs::EachDefiner.new.max.should == nil
+ end
+
+ it "raises a NoMethodError for elements without #<=>" do
+ lambda do
+ EnumerableSpecs::EachDefiner.new(BasicObject.new, BasicObject.new).max
+ end.should raise_error(NoMethodError)
+ end
+
+ it "raises an ArgumentError for incomparable elements" do
+ lambda do
+ EnumerableSpecs::EachDefiner.new(11,"22").max
+ end.should raise_error(ArgumentError)
+ lambda do
+ EnumerableSpecs::EachDefiner.new(11,12,22,33).max{|a, b| nil}
+ end.should raise_error(ArgumentError)
+ end
+
+ context "when passed a block" do
+ it "returns the maximum element" do
+ EnumerableSpecs::EachDefiner.new("2","33","4","11").max {|a,b| a <=> b }.should == "4"
+ EnumerableSpecs::EachDefiner.new( 2 , 33 , 4 , 11 ).max {|a,b| a <=> b }.should == 33
+
+ EnumerableSpecs::EachDefiner.new("2","33","4","11").max {|a,b| b <=> a }.should == "11"
+ EnumerableSpecs::EachDefiner.new( 2 , 33 , 4 , 11 ).max {|a,b| b <=> a }.should == 2
+
+ @e_strs.max {|a,b| a.length <=> b.length }.should == "1010101010"
+
+ @e_strs.max {|a,b| a <=> b }.should == "666666"
+ @e_strs.max {|a,b| a.to_i <=> b.to_i }.should == "1010101010"
+
+ @e_ints.max {|a,b| a <=> b }.should == 1010101010
+ @e_ints.max {|a,b| a.to_s <=> b.to_s }.should == 666666
+ end
+ end
+
+ it "returns the maximum for enumerables that contain nils" do
+ arr = EnumerableSpecs::Numerous.new(nil, nil, true)
+ arr.max { |a, b|
+ x = a.nil? ? 1 : a ? 0 : -1
+ y = b.nil? ? 1 : b ? 0 : -1
+ x <=> y
+ }.should == nil
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.max.should == [6, 7, 8, 9]
+ end
+
+ context "when called with an argument n" do
+ context "without a block" do
+ it "returns an array containing the maximum n elements" do
+ result = @e_ints.max(2)
+ result.should == [1010101010, 666666]
+ end
+ end
+
+ context "with a block" do
+ it "returns an array containing the maximum n elements" do
+ result = @e_ints.max(2) { |a, b| a * 2 <=> b * 2 }
+ result.should == [1010101010, 666666]
+ end
+ end
+
+ context "on a enumerable of length x where x < n" do
+ it "returns an array containing the maximum n elements of length x" do
+ result = @e_ints.max(500)
+ result.length.should == 5
+ end
+ end
+
+ context "that is negative" do
+ it "raises an ArgumentError" do
+ lambda { @e_ints.max(-1) }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ context "that is nil" do
+ it "returns the maximum element" do
+ @e_ints.max(nil).should == 1010101010
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/member_spec.rb b/spec/ruby/core/enumerable/member_spec.rb
new file mode 100644
index 0000000000..1fe3cebd28
--- /dev/null
+++ b/spec/ruby/core/enumerable/member_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/include'
+
+describe "Enumerable#member?" do
+ it_behaves_like :enumerable_include, :member?
+end
diff --git a/spec/ruby/core/enumerable/min_by_spec.rb b/spec/ruby/core/enumerable/min_by_spec.rb
new file mode 100644
index 0000000000..5f9b2aea00
--- /dev/null
+++ b/spec/ruby/core/enumerable/min_by_spec.rb
@@ -0,0 +1,81 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumerable_enumeratorized'
+
+describe "Enumerable#min_by" do
+ it "returns an enumerator if no block" do
+ EnumerableSpecs::Numerous.new(42).min_by.should be_an_instance_of(Enumerator)
+ end
+
+ it "returns nil if #each yields no objects" do
+ EnumerableSpecs::Empty.new.min_by {|o| o.nonesuch }.should == nil
+ end
+
+ it "returns the object for whom the value returned by block is the smallest" do
+ EnumerableSpecs::Numerous.new(*%w[3 2 1]).min_by {|obj| obj.to_i }.should == '1'
+ EnumerableSpecs::Numerous.new(*%w[five three]).min_by {|obj| obj.length }.should == 'five'
+ end
+
+ it "returns the object that appears first in #each in case of a tie" do
+ a, b, c = '2', '1', '1'
+ EnumerableSpecs::Numerous.new(a, b, c).min_by {|obj| obj.to_i }.should equal(b)
+ end
+
+ it "uses min.<=>(current) to determine order" do
+ a, b, c = (1..3).map{|n| EnumerableSpecs::ReverseComparable.new(n)}
+
+ # Just using self here to avoid additional complexity
+ EnumerableSpecs::Numerous.new(a, b, c).min_by {|obj| obj }.should == c
+ end
+
+ it "is able to return the minimum for enums that contain nils" do
+ enum = EnumerableSpecs::Numerous.new(nil, nil, true)
+ enum.min_by {|o| o.nil? ? 0 : 1 }.should == nil
+ enum.min_by {|o| o.nil? ? 1 : 0 }.should == true
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.min_by {|e| e.size}.should == [1, 2]
+ end
+
+ it_behaves_like :enumerable_enumeratorized_with_origin_size, :min_by
+
+ context "when called with an argument n" do
+ before :each do
+ @enum = EnumerableSpecs::Numerous.new(101, 55, 1, 20, 33, 500, 60)
+ end
+
+ context "without a block" do
+ it "returns an enumerator" do
+ @enum.min_by(2).should be_an_instance_of(Enumerator)
+ end
+ end
+
+ context "with a block" do
+ it "returns an array containing the minimum n elements based on the block's value" do
+ result = @enum.min_by(3) { |i| i.to_s }
+ result.should == [1, 101, 20]
+ end
+
+ context "on a enumerable of length x where x < n" do
+ it "returns an array containing the minimum n elements of length n" do
+ result = @enum.min_by(500) { |i| i.to_s }
+ result.length.should == 7
+ end
+ end
+
+ context "when n is negative" do
+ it "raises an ArgumentError" do
+ lambda { @enum.min_by(-1) { |i| i.to_s } }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ context "when n is nil" do
+ it "returns the minimum element" do
+ @enum.min_by(nil) { |i| i.to_s }.should == 1
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/min_spec.rb b/spec/ruby/core/enumerable/min_spec.rb
new file mode 100644
index 0000000000..a31633e8c4
--- /dev/null
+++ b/spec/ruby/core/enumerable/min_spec.rb
@@ -0,0 +1,123 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#min" do
+ before :each do
+ @e_strs = EnumerableSpecs::EachDefiner.new("333", "22", "666666", "1", "55555", "1010101010")
+ @e_ints = EnumerableSpecs::EachDefiner.new( 333, 22, 666666, 55555, 1010101010)
+ end
+
+ it "min should return the minimum element" do
+ EnumerableSpecs::Numerous.new.min.should == 1
+ end
+
+ it "returns the minimum (basic cases)" do
+ EnumerableSpecs::EachDefiner.new(55).min.should == 55
+
+ EnumerableSpecs::EachDefiner.new(11,99).min.should == 11
+ EnumerableSpecs::EachDefiner.new(99,11).min.should == 11
+ EnumerableSpecs::EachDefiner.new(2, 33, 4, 11).min.should == 2
+
+ EnumerableSpecs::EachDefiner.new(1,2,3,4,5).min.should == 1
+ EnumerableSpecs::EachDefiner.new(5,4,3,2,1).min.should == 1
+ EnumerableSpecs::EachDefiner.new(4,1,3,5,2).min.should == 1
+ EnumerableSpecs::EachDefiner.new(5,5,5,5,5).min.should == 5
+
+ EnumerableSpecs::EachDefiner.new("aa","tt").min.should == "aa"
+ EnumerableSpecs::EachDefiner.new("tt","aa").min.should == "aa"
+ EnumerableSpecs::EachDefiner.new("2","33","4","11").min.should == "11"
+
+ @e_strs.min.should == "1"
+ @e_ints.min.should == 22
+ end
+
+ it "returns nil for an empty Enumerable" do
+ EnumerableSpecs::EachDefiner.new.min.should be_nil
+ end
+
+ it "raises a NoMethodError for elements without #<=>" do
+ lambda do
+ EnumerableSpecs::EachDefiner.new(BasicObject.new, BasicObject.new).min
+ end.should raise_error(NoMethodError)
+ end
+
+ it "raises an ArgumentError for incomparable elements" do
+ lambda do
+ EnumerableSpecs::EachDefiner.new(11,"22").min
+ end.should raise_error(ArgumentError)
+ lambda do
+ EnumerableSpecs::EachDefiner.new(11,12,22,33).min{|a, b| nil}
+ end.should raise_error(ArgumentError)
+ end
+
+ it "returns the minimum when using a block rule" do
+ EnumerableSpecs::EachDefiner.new("2","33","4","11").min {|a,b| a <=> b }.should == "11"
+ EnumerableSpecs::EachDefiner.new( 2 , 33 , 4 , 11 ).min {|a,b| a <=> b }.should == 2
+
+ EnumerableSpecs::EachDefiner.new("2","33","4","11").min {|a,b| b <=> a }.should == "4"
+ EnumerableSpecs::EachDefiner.new( 2 , 33 , 4 , 11 ).min {|a,b| b <=> a }.should == 33
+
+ EnumerableSpecs::EachDefiner.new( 1, 2, 3, 4 ).min {|a,b| 15 }.should == 1
+
+ EnumerableSpecs::EachDefiner.new(11,12,22,33).min{|a, b| 2 }.should == 11
+ @i = -2
+ EnumerableSpecs::EachDefiner.new(11,12,22,33).min{|a, b| @i += 1 }.should == 12
+
+ @e_strs.min {|a,b| a.length <=> b.length }.should == "1"
+
+ @e_strs.min {|a,b| a <=> b }.should == "1"
+ @e_strs.min {|a,b| a.to_i <=> b.to_i }.should == "1"
+
+ @e_ints.min {|a,b| a <=> b }.should == 22
+ @e_ints.min {|a,b| a.to_s <=> b.to_s }.should == 1010101010
+ end
+
+ it "returns the minimum for enumerables that contain nils" do
+ arr = EnumerableSpecs::Numerous.new(nil, nil, true)
+ arr.min { |a, b|
+ x = a.nil? ? -1 : a ? 0 : 1
+ y = b.nil? ? -1 : b ? 0 : 1
+ x <=> y
+ }.should == nil
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.min.should == [1, 2]
+ end
+
+ context "when called with an argument n" do
+ context "without a block" do
+ it "returns an array containing the minimum n elements" do
+ result = @e_ints.min(2)
+ result.should == [22, 333]
+ end
+ end
+
+ context "with a block" do
+ it "returns an array containing the minimum n elements" do
+ result = @e_ints.min(2) { |a, b| a * 2 <=> b * 2 }
+ result.should == [22, 333]
+ end
+ end
+
+ context "on a enumerable of length x where x < n" do
+ it "returns an array containing the minimum n elements of length x" do
+ result = @e_ints.min(500)
+ result.length.should == 5
+ end
+ end
+
+ context "that is negative" do
+ it "raises an ArgumentError" do
+ lambda { @e_ints.min(-1) }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ context "that is nil" do
+ it "returns the minimum element" do
+ @e_ints.min(nil).should == 22
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/minmax_by_spec.rb b/spec/ruby/core/enumerable/minmax_by_spec.rb
new file mode 100644
index 0000000000..a6a9249270
--- /dev/null
+++ b/spec/ruby/core/enumerable/minmax_by_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumerable_enumeratorized'
+
+describe "Enumerable#minmax_by" do
+ it "returns an enumerator if no block" do
+ EnumerableSpecs::Numerous.new(42).minmax_by.should be_an_instance_of(Enumerator)
+ end
+
+ it "returns nil if #each yields no objects" do
+ EnumerableSpecs::Empty.new.minmax_by {|o| o.nonesuch }.should == [nil, nil]
+ end
+
+ it "returns the object for whom the value returned by block is the largest" do
+ EnumerableSpecs::Numerous.new(*%w[1 2 3]).minmax_by {|obj| obj.to_i }.should == ['1', '3']
+ EnumerableSpecs::Numerous.new(*%w[three five]).minmax_by {|obj| obj.length }.should == ['five', 'three']
+ end
+
+ it "returns the object that appears first in #each in case of a tie" do
+ a, b, c, d = '1', '1', '2', '2'
+ mm = EnumerableSpecs::Numerous.new(a, b, c, d).minmax_by {|obj| obj.to_i }
+ mm[0].should equal(a)
+ mm[1].should equal(c)
+ end
+
+ it "uses min/max.<=>(current) to determine order" do
+ a, b, c = (1..3).map{|n| EnumerableSpecs::ReverseComparable.new(n)}
+
+ # Just using self here to avoid additional complexity
+ EnumerableSpecs::Numerous.new(a, b, c).minmax_by {|obj| obj }.should == [c, a]
+ end
+
+ it "is able to return the maximum for enums that contain nils" do
+ enum = EnumerableSpecs::Numerous.new(nil, nil, true)
+ enum.minmax_by {|o| o.nil? ? 0 : 1 }.should == [nil, true]
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.minmax_by {|e| e.size}.should == [[1, 2], [6, 7, 8, 9]]
+ end
+
+ it_behaves_like :enumerable_enumeratorized_with_origin_size, :minmax_by
+end
diff --git a/spec/ruby/core/enumerable/minmax_spec.rb b/spec/ruby/core/enumerable/minmax_spec.rb
new file mode 100644
index 0000000000..f05291aec4
--- /dev/null
+++ b/spec/ruby/core/enumerable/minmax_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#minmax" do
+ before :each do
+ @enum = EnumerableSpecs::Numerous.new(6, 4, 5, 10, 8)
+
+ @strs = EnumerableSpecs::Numerous.new("333", "2", "60", "55555", "1010", "111")
+ end
+
+ it "min should return the minimum element" do
+ @enum.minmax.should == [4, 10]
+ @strs.minmax.should == ["1010", "60" ]
+ end
+
+ it "returns [nil, nil] for an empty Enumerable" do
+ EnumerableSpecs::Empty.new.minmax.should == [nil, nil]
+ end
+
+ it "raises an ArgumentError when elements are incomparable" do
+ lambda do
+ EnumerableSpecs::Numerous.new(11,"22").minmax
+ end.should raise_error(ArgumentError)
+ lambda do
+ EnumerableSpecs::Numerous.new(11,12,22,33).minmax{|a, b| nil}
+ end.should raise_error(ArgumentError)
+ end
+
+ it "raises a NoMethodError for elements without #<=>" do
+ lambda do
+ EnumerableSpecs::Numerous.new(BasicObject.new, BasicObject.new).minmax
+ end.should raise_error(NoMethodError)
+ end
+
+ it "returns the minimum when using a block rule" do
+ @enum.minmax {|a,b| b <=> a }.should == [10, 4]
+ @strs.minmax {|a,b| a.length <=> b.length }.should == ["2", "55555"]
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.minmax.should == [[1, 2], [6, 7, 8, 9]]
+ end
+end
diff --git a/spec/ruby/core/enumerable/none_spec.rb b/spec/ruby/core/enumerable/none_spec.rb
new file mode 100644
index 0000000000..bff252df5e
--- /dev/null
+++ b/spec/ruby/core/enumerable/none_spec.rb
@@ -0,0 +1,167 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#none?" do
+ before :each do
+ @empty = EnumerableSpecs::Empty.new
+ @enum = EnumerableSpecs::Numerous.new
+ @enum1 = EnumerableSpecs::Numerous.new(0, 1, 2, -1)
+ @enum2 = EnumerableSpecs::Numerous.new(nil, false, true)
+ end
+
+ it "always returns true on empty enumeration" do
+ @empty.none?.should == true
+ @empty.none? { true }.should == true
+ end
+
+ it "raises an ArgumentError when more than 1 argument is provided" do
+ lambda { @enum.none?(1, 2, 3) }.should raise_error(ArgumentError)
+ lambda { [].none?(1, 2, 3) }.should raise_error(ArgumentError)
+ lambda { {}.none?(1, 2, 3) }.should raise_error(ArgumentError)
+ end
+
+ ruby_version_is ""..."2.5" do
+ it "raises an ArgumentError when any arguments provided" do
+ lambda { @enum.none?(Proc.new {}) }.should raise_error(ArgumentError)
+ lambda { @enum.none?(nil) }.should raise_error(ArgumentError)
+ lambda { @empty.none?(1) }.should raise_error(ArgumentError)
+ lambda { @enum.none?(1) {} }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "does not hide exceptions out of #each" do
+ lambda {
+ EnumerableSpecs::ThrowingEach.new.none?
+ }.should raise_error(RuntimeError)
+
+ lambda {
+ EnumerableSpecs::ThrowingEach.new.none? { false }
+ }.should raise_error(RuntimeError)
+ end
+
+ describe "with no block" do
+ it "returns true if none of the elements in self are true" do
+ e = EnumerableSpecs::Numerous.new(false, nil, false)
+ e.none?.should be_true
+ end
+
+ it "returns false if at least one of the elements in self are true" do
+ e = EnumerableSpecs::Numerous.new(false, nil, true, false)
+ e.none?.should be_false
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMultiWithFalse.new
+ multi.none?.should be_false
+ end
+ end
+
+ describe "with a block" do
+ before :each do
+ @e = EnumerableSpecs::Numerous.new(1,1,2,3,4)
+ end
+
+ it "passes each element to the block in turn until it returns true" do
+ acc = []
+ @e.none? {|e| acc << e; false }
+ acc.should == [1,1,2,3,4]
+ end
+
+ it "stops passing elements to the block when it returns true" do
+ acc = []
+ @e.none? {|e| acc << e; e == 3 ? true : false }
+ acc.should == [1,1,2,3]
+ end
+
+ it "returns true if the block never returns true" do
+ @e.none? {|e| false }.should be_true
+ end
+
+ it "returns false if the block ever returns true" do
+ @e.none? {|e| e == 3 ? true : false }.should be_false
+ end
+
+ it "does not hide exceptions out of the block" do
+ lambda {
+ @enum.none? { raise "from block" }
+ }.should raise_error(RuntimeError)
+ end
+
+ it "gathers initial args as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ yielded = []
+ multi.none? { |e| yielded << e; false }
+ yielded.should == [1, 3, 6]
+ end
+
+ it "yields multiple arguments when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ yielded = []
+ multi.none? { |*args| yielded << args; false }
+ yielded.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
+ end
+ end
+
+ ruby_version_is "2.5" do
+ describe 'when given a pattern argument' do
+ it "calls `===` on the pattern the return value " do
+ pattern = EnumerableSpecs::Pattern.new { |x| x == 3 }
+ @enum1.none?(pattern).should == true
+ pattern.yielded.should == [[0], [1], [2], [-1]]
+ end
+
+ # may raise an exception in future versions
+ ruby_version_is ""..."2.6" do
+ it "ignores block" do
+ @enum2.none?(Integer) { raise }.should == true
+ [1, 2, nil].none?(TrueClass) { raise }.should == true
+ {a: 1}.none?(Hash) { raise }.should == true
+ end
+ end
+
+ it "always returns true on empty enumeration" do
+ @empty.none?(Integer).should == true
+ [].none?(Integer).should == true
+ {}.none?(NilClass).should == true
+ end
+
+ it "does not hide exceptions out of #each" do
+ lambda {
+ EnumerableSpecs::ThrowingEach.new.none?(Integer)
+ }.should raise_error(RuntimeError)
+ end
+
+ it "returns true if the pattern never returns a truthy value" do
+ @enum2.none?(Integer).should == true
+ pattern = EnumerableSpecs::Pattern.new { |x| nil }
+ @enum.none?(pattern).should == true
+
+ [1, 42, 3].none?(pattern).should == true
+ {a: 1, b: 2}.none?(pattern).should == true
+ end
+
+ it "returns false if the pattern ever returns other than false or nil" do
+ pattern = EnumerableSpecs::Pattern.new { |x| x < 0 }
+ @enum1.none?(pattern).should == false
+ pattern.yielded.should == [[0], [1], [2], [-1]]
+
+ [1, 2, 3, -1].none?(pattern).should == false
+ {a: 1}.none?(Array).should == false
+ end
+
+ it "does not hide exceptions out of pattern#===" do
+ pattern = EnumerableSpecs::Pattern.new { raise "from pattern" }
+ lambda {
+ @enum.none?(pattern)
+ }.should raise_error(RuntimeError)
+ end
+
+ it "calls the pattern with gathered array when yielded with multiple arguments" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ pattern = EnumerableSpecs::Pattern.new { false }
+ multi.none?(pattern).should == true
+ pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/one_spec.rb b/spec/ruby/core/enumerable/one_spec.rb
new file mode 100644
index 0000000000..2bd67adc26
--- /dev/null
+++ b/spec/ruby/core/enumerable/one_spec.rb
@@ -0,0 +1,169 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#one?" do
+ before :each do
+ @empty = EnumerableSpecs::Empty.new
+ @enum = EnumerableSpecs::Numerous.new
+ @enum1 = EnumerableSpecs::Numerous.new(0, 1, 2, -1)
+ @enum2 = EnumerableSpecs::Numerous.new(nil, false, true)
+ end
+
+ it "always returns false on empty enumeration" do
+ @empty.one?.should == false
+ @empty.one? { true }.should == false
+ end
+
+ it "raises an ArgumentError when more than 1 argument is provided" do
+ lambda { @enum.one?(1, 2, 3) }.should raise_error(ArgumentError)
+ lambda { [].one?(1, 2, 3) }.should raise_error(ArgumentError)
+ lambda { {}.one?(1, 2, 3) }.should raise_error(ArgumentError)
+ end
+
+ ruby_version_is ""..."2.5" do
+ it "raises an ArgumentError when any arguments provided" do
+ lambda { @enum.one?(Proc.new {}) }.should raise_error(ArgumentError)
+ lambda { @enum.one?(nil) }.should raise_error(ArgumentError)
+ lambda { @empty.one?(1) }.should raise_error(ArgumentError)
+ lambda { @enum.one?(1) {} }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "does not hide exceptions out of #each" do
+ lambda {
+ EnumerableSpecs::ThrowingEach.new.one?
+ }.should raise_error(RuntimeError)
+
+ lambda {
+ EnumerableSpecs::ThrowingEach.new.one? { false }
+ }.should raise_error(RuntimeError)
+ end
+
+ describe "with no block" do
+ it "returns true if only one element evaluates to true" do
+ [false, nil, true].one?.should be_true
+ end
+
+ it "returns false if two elements evaluate to true" do
+ [false, :value, nil, true].one?.should be_false
+ end
+
+ it "returns false if all elements evaluate to false" do
+ [false, nil, false].one?.should be_false
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMultiWithSingleTrue.new
+ multi.one?.should be_false
+ end
+ end
+
+ describe "with a block" do
+ it "returns true if block returns true once" do
+ [:a, :b, :c].one? { |s| s == :a }.should be_true
+ end
+
+ it "returns false if the block returns true more than once" do
+ [:a, :b, :c].one? { |s| s == :a || s == :b }.should be_false
+ end
+
+ it "returns false if the block only returns false" do
+ [:a, :b, :c].one? { |s| s == :d }.should be_false
+ end
+
+ it "does not hide exceptions out of the block" do
+ lambda {
+ @enum.one? { raise "from block" }
+ }.should raise_error(RuntimeError)
+ end
+
+ it "gathers initial args as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ yielded = []
+ multi.one? { |e| yielded << e; false }.should == false
+ yielded.should == [1, 3, 6]
+ end
+
+ it "yields multiple arguments when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ yielded = []
+ multi.one? { |*args| yielded << args; false }.should == false
+ yielded.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
+ end
+ end
+
+
+ ruby_version_is "2.5" do
+ describe 'when given a pattern argument' do
+ it "calls `===` on the pattern the return value " do
+ pattern = EnumerableSpecs::Pattern.new { |x| x == 1 }
+ @enum1.one?(pattern).should == true
+ pattern.yielded.should == [[0], [1], [2], [-1]]
+ end
+
+ # may raise an exception in future versions
+ ruby_version_is ""..."2.6" do
+ it "ignores block" do
+ @enum2.one?(NilClass) { raise }.should == true
+ [1, 2, nil].one?(NilClass) { raise }.should == true
+ {a: 1}.one?(Array) { raise }.should == true
+ end
+ end
+
+ it "always returns false on empty enumeration" do
+ @empty.one?(Integer).should == false
+ [].one?(Integer).should == false
+ {}.one?(NilClass).should == false
+ end
+
+ it "does not hide exceptions out of #each" do
+ lambda {
+ EnumerableSpecs::ThrowingEach.new.one?(Integer)
+ }.should raise_error(RuntimeError)
+ end
+
+ it "returns true if the pattern returns a truthy value only once" do
+ @enum2.one?(NilClass).should == true
+ pattern = EnumerableSpecs::Pattern.new { |x| x == 2 }
+ @enum1.one?(pattern).should == true
+
+ [1, 2, 42, 3].one?(pattern).should == true
+
+ pattern = EnumerableSpecs::Pattern.new { |x| x == [:b, 2] }
+ {a: 1, b: 2}.one?(pattern).should == true
+ end
+
+ it "returns false if the pattern returns a truthy value more than once" do
+ pattern = EnumerableSpecs::Pattern.new { |x| !x }
+ @enum2.one?(pattern).should == false
+ pattern.yielded.should == [[nil], [false]]
+
+ [1, 2, 3].one?(Integer).should == false
+ {a: 1, b: 2}.one?(Array).should == false
+ end
+
+ it "returns false if the pattern never returns a truthy value" do
+ pattern = EnumerableSpecs::Pattern.new { |x| nil }
+ @enum1.one?(pattern).should == false
+ pattern.yielded.should == [[0], [1], [2], [-1]]
+
+ [1, 2, 3].one?(pattern).should == false
+ {a: 1}.one?(pattern).should == false
+ end
+
+ it "does not hide exceptions out of pattern#===" do
+ pattern = EnumerableSpecs::Pattern.new { raise "from pattern" }
+ lambda {
+ @enum.one?(pattern)
+ }.should raise_error(RuntimeError)
+ end
+
+ it "calls the pattern with gathered array when yielded with multiple arguments" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ pattern = EnumerableSpecs::Pattern.new { false }
+ multi.one?(pattern).should == false
+ pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/partition_spec.rb b/spec/ruby/core/enumerable/partition_spec.rb
new file mode 100644
index 0000000000..d3d220b4b4
--- /dev/null
+++ b/spec/ruby/core/enumerable/partition_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumerable_enumeratorized'
+
+describe "Enumerable#partition" do
+ it "returns two arrays, the first containing elements for which the block is true, the second containing the rest" do
+ EnumerableSpecs::Numerous.new.partition { |i| i % 2 == 0 }.should == [[2, 6, 4], [5, 3, 1]]
+ end
+
+ it "returns an Enumerator if called without a block" do
+ EnumerableSpecs::Numerous.new.partition.should be_an_instance_of(Enumerator)
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.partition {|e| e == [3, 4, 5] }.should == [[[3, 4, 5]], [[1, 2], [6, 7, 8, 9]]]
+ end
+
+ it_behaves_like :enumerable_enumeratorized_with_origin_size, :partition
+end
diff --git a/spec/ruby/core/enumerable/reduce_spec.rb b/spec/ruby/core/enumerable/reduce_spec.rb
new file mode 100644
index 0000000000..bc8691c1b0
--- /dev/null
+++ b/spec/ruby/core/enumerable/reduce_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/inject'
+
+describe "Enumerable#reduce" do
+ it_behaves_like :enumerable_inject, :reduce
+end
diff --git a/spec/ruby/core/enumerable/reject_spec.rb b/spec/ruby/core/enumerable/reject_spec.rb
new file mode 100644
index 0000000000..0d86b49ea2
--- /dev/null
+++ b/spec/ruby/core/enumerable/reject_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumerable_enumeratorized'
+
+describe "Enumerable#reject" do
+ it "returns an array of the elements for which block is false" do
+ EnumerableSpecs::Numerous.new.reject { |i| i > 3 }.should == [2, 3, 1]
+ entries = (1..10).to_a
+ numerous = EnumerableSpecs::Numerous.new(*entries)
+ numerous.reject {|i| i % 2 == 0 }.should == [1,3,5,7,9]
+ numerous.reject {|i| true }.should == []
+ numerous.reject {|i| false }.should == entries
+ end
+
+ it "returns an Enumerator if called without a block" do
+ EnumerableSpecs::Numerous.new.reject.should be_an_instance_of(Enumerator)
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.reject {|e| e == [3, 4, 5] }.should == [[1, 2], [6, 7, 8, 9]]
+ end
+
+ it_behaves_like :enumerable_enumeratorized_with_origin_size, :reject
+end
diff --git a/spec/ruby/core/enumerable/reverse_each_spec.rb b/spec/ruby/core/enumerable/reverse_each_spec.rb
new file mode 100644
index 0000000000..2b1c233488
--- /dev/null
+++ b/spec/ruby/core/enumerable/reverse_each_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumerable_enumeratorized'
+
+describe "Enumerable#reverse_each" do
+ it "traverses enum in reverse order and pass each element to block" do
+ a=[]
+ EnumerableSpecs::Numerous.new.reverse_each { |i| a << i }
+ a.should == [4, 1, 6, 3, 5, 2]
+ end
+
+ it "returns an Enumerator if no block given" do
+ enum = EnumerableSpecs::Numerous.new.reverse_each
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.should == [4, 1, 6, 3, 5, 2]
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ yielded = []
+ multi.reverse_each {|e| yielded << e }
+ yielded.should == [[6, 7, 8, 9], [3, 4, 5], [1, 2]]
+ end
+
+ it_behaves_like :enumerable_enumeratorized_with_origin_size, :reverse_each
+end
diff --git a/spec/ruby/core/enumerable/select_spec.rb b/spec/ruby/core/enumerable/select_spec.rb
new file mode 100644
index 0000000000..11168eb42e
--- /dev/null
+++ b/spec/ruby/core/enumerable/select_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/find_all'
+
+describe "Enumerable#select" do
+ it_behaves_like :enumerable_find_all , :select
+end
diff --git a/spec/ruby/core/enumerable/shared/collect.rb b/spec/ruby/core/enumerable/shared/collect.rb
new file mode 100644
index 0000000000..16bf3f0d35
--- /dev/null
+++ b/spec/ruby/core/enumerable/shared/collect.rb
@@ -0,0 +1,32 @@
+require_relative 'enumerable_enumeratorized'
+
+describe :enumerable_collect, shared: true do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "returns a new array with the results of passing each element to block" do
+ entries = [0, 1, 3, 4, 5, 6]
+ numerous = EnumerableSpecs::Numerous.new(*entries)
+ numerous.send(@method) { |i| i % 2 }.should == [0, 1, 1, 0, 1, 0]
+ numerous.send(@method) { |i| i }.should == entries
+ end
+
+ it "passes through the values yielded by #each_with_index" do
+ [:a, :b].each_with_index.send(@method) { |x, i| ScratchPad << [x, i]; nil }
+ ScratchPad.recorded.should == [[:a, 0], [:b, 1]]
+ end
+
+ it "gathers initial args as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.send(@method) {|e| e}.should == [1,3,6]
+ end
+
+ it "returns an enumerator when no block given" do
+ enum = EnumerableSpecs::Numerous.new.send(@method)
+ enum.should be_an_instance_of(Enumerator)
+ enum.each { |i| -i }.should == [-2, -5, -3, -6, -1, -4]
+ end
+
+ it_should_behave_like :enumerable_enumeratorized_with_origin_size
+end
diff --git a/spec/ruby/core/enumerable/shared/collect_concat.rb b/spec/ruby/core/enumerable/shared/collect_concat.rb
new file mode 100644
index 0000000000..c081c1be71
--- /dev/null
+++ b/spec/ruby/core/enumerable/shared/collect_concat.rb
@@ -0,0 +1,54 @@
+require_relative 'enumerable_enumeratorized'
+
+describe :enumerable_collect_concat, shared: true do
+ it "yields elements to the block and flattens one level" do
+ numerous = EnumerableSpecs::Numerous.new(1, [2, 3], [4, [5, 6]], {foo: :bar})
+ numerous.send(@method) { |i| i }.should == [1, 2, 3, 4, [5, 6], {foo: :bar}]
+ end
+
+ it "appends non-Array elements that do not define #to_ary" do
+ obj = mock("to_ary undefined")
+
+ numerous = EnumerableSpecs::Numerous.new(1, obj, 2)
+ numerous.send(@method) { |i| i }.should == [1, obj, 2]
+ end
+
+ it "concatenates the result of calling #to_ary if it returns an Array" do
+ obj = mock("to_ary defined")
+ obj.should_receive(:to_ary).and_return([:a, :b])
+
+ numerous = EnumerableSpecs::Numerous.new(1, obj, 2)
+ numerous.send(@method) { |i| i }.should == [1, :a, :b, 2]
+ end
+
+ it "does not call #to_a" do
+ obj = mock("to_ary undefined")
+ obj.should_not_receive(:to_a)
+
+ numerous = EnumerableSpecs::Numerous.new(1, obj, 2)
+ numerous.send(@method) { |i| i }.should == [1, obj, 2]
+ end
+
+ it "appends an element that defines #to_ary that returns nil" do
+ obj = mock("to_ary defined")
+ obj.should_receive(:to_ary).and_return(nil)
+
+ numerous = EnumerableSpecs::Numerous.new(1, obj, 2)
+ numerous.send(@method) { |i| i }.should == [1, obj, 2]
+ end
+
+ it "raises a TypeError if an element defining #to_ary does not return an Array or nil" do
+ obj = mock("to_ary defined")
+ obj.should_receive(:to_ary).and_return("array")
+
+ lambda { [1, obj, 3].send(@method) { |i| i } }.should raise_error(TypeError)
+ end
+
+ it "returns an enumerator when no block given" do
+ enum = EnumerableSpecs::Numerous.new(1, 2).send(@method)
+ enum.should be_an_instance_of(Enumerator)
+ enum.each{ |i| [i] * i }.should == [1, 2, 2]
+ end
+
+ it_should_behave_like :enumerable_enumeratorized_with_origin_size
+end
diff --git a/spec/ruby/core/enumerable/shared/entries.rb b/spec/ruby/core/enumerable/shared/entries.rb
new file mode 100644
index 0000000000..f52844cb45
--- /dev/null
+++ b/spec/ruby/core/enumerable/shared/entries.rb
@@ -0,0 +1,24 @@
+describe :enumerable_entries, shared: true do
+ it "returns an array containing the elements" do
+ numerous = EnumerableSpecs::Numerous.new(1, nil, 'a', 2, false, true)
+ numerous.send(@method).should == [1, nil, "a", 2, false, true]
+ end
+
+ it "passes through the values yielded by #each_with_index" do
+ [:a, :b].each_with_index.send(@method).should == [[:a, 0], [:b, 1]]
+ end
+
+ it "passes arguments to each" do
+ count = EnumerableSpecs::EachCounter.new(1, 2, 3)
+ count.send(@method, :hello, "world").should == [1, 2, 3]
+ count.arguments_passed.should == [:hello, "world"]
+ end
+
+ it "returns a tainted array if self is tainted" do
+ EnumerableSpecs::Empty.new.taint.send(@method).tainted?.should be_true
+ end
+
+ it "returns an untrusted array if self is untrusted" do
+ EnumerableSpecs::Empty.new.untrust.send(@method).untrusted?.should be_true
+ end
+end
diff --git a/spec/ruby/core/enumerable/shared/enumerable_enumeratorized.rb b/spec/ruby/core/enumerable/shared/enumerable_enumeratorized.rb
new file mode 100644
index 0000000000..e2bbe18eda
--- /dev/null
+++ b/spec/ruby/core/enumerable/shared/enumerable_enumeratorized.rb
@@ -0,0 +1,33 @@
+require_relative 'enumeratorized'
+
+describe :enumerable_enumeratorized_with_unknown_size, shared: true do
+ describe "Enumerable with size" do
+ before :all do
+ @object = EnumerableSpecs::NumerousWithSize.new(1, 2, 3, 4)
+ end
+ it_should_behave_like :enumeratorized_with_unknown_size
+ end
+
+ describe "Enumerable with no size" do
+ before :all do
+ @object = EnumerableSpecs::Numerous.new(1, 2, 3, 4)
+ end
+ it_should_behave_like :enumeratorized_with_unknown_size
+ end
+end
+
+describe :enumerable_enumeratorized_with_origin_size, shared: true do
+ describe "Enumerable with size" do
+ before :all do
+ @object = EnumerableSpecs::NumerousWithSize.new(1, 2, 3, 4)
+ end
+ it_should_behave_like :enumeratorized_with_origin_size
+ end
+
+ describe "Enumerable with no size" do
+ before :all do
+ @object = EnumerableSpecs::Numerous.new(1, 2, 3, 4)
+ end
+ it_should_behave_like :enumeratorized_with_unknown_size
+ end
+end
diff --git a/spec/ruby/core/enumerable/shared/enumeratorized.rb b/spec/ruby/core/enumerable/shared/enumeratorized.rb
new file mode 100644
index 0000000000..05d27b5783
--- /dev/null
+++ b/spec/ruby/core/enumerable/shared/enumeratorized.rb
@@ -0,0 +1,42 @@
+describe :enumeratorized_with_unknown_size, shared: true do
+ describe "when no block is given" do
+ describe "returned Enumerator" do
+ it "size returns nil" do
+ @object.send(*@method).size.should == nil
+ end
+ end
+ end
+end
+
+describe :enumeratorized_with_origin_size, shared: true do
+ describe "when no block is given" do
+ describe "returned Enumerator" do
+ it "size returns the enumerable size" do
+ @object.send(*@method).size.should == @object.size
+ end
+ end
+ end
+end
+
+describe :enumeratorized_with_cycle_size, shared: true do
+ describe "when no block is given" do
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should be the result of multiplying the enumerable size by the argument passed" do
+ @object.cycle(2).size.should == @object.size * 2
+ @object.cycle(7).size.should == @object.size * 7
+ @object.cycle(0).size.should == 0
+ @empty_object.cycle(2).size.should == 0
+ end
+
+ it "should be zero when the argument passed is 0 or less" do
+ @object.cycle(-1).size.should == 0
+ end
+
+ it "should be Float::INFINITY when no argument is passed" do
+ @object.cycle.size.should == Float::INFINITY
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/shared/find.rb b/spec/ruby/core/enumerable/shared/find.rb
new file mode 100644
index 0000000000..5c097509cd
--- /dev/null
+++ b/spec/ruby/core/enumerable/shared/find.rb
@@ -0,0 +1,73 @@
+require_relative 'enumerable_enumeratorized'
+
+describe :enumerable_find, shared: true do
+ # #detect and #find are aliases, so we only need one function
+ before :each do
+ ScratchPad.record []
+ @elements = [2, 4, 6, 8, 10]
+ @numerous = EnumerableSpecs::Numerous.new(*@elements)
+ @empty = []
+ end
+
+ it "passes each entry in enum to block while block when block is false" do
+ visited_elements = []
+ @numerous.send(@method) do |element|
+ visited_elements << element
+ false
+ end
+ visited_elements.should == @elements
+ end
+
+ it "returns nil when the block is false and there is no ifnone proc given" do
+ @numerous.send(@method) {|e| false }.should == nil
+ end
+
+ it "returns the first element for which the block is not false" do
+ @elements.each do |element|
+ @numerous.send(@method) {|e| e > element - 1 }.should == element
+ end
+ end
+
+ it "returns the value of the ifnone proc if the block is false" do
+ fail_proc = lambda { "cheeseburgers" }
+ @numerous.send(@method, fail_proc) {|e| false }.should == "cheeseburgers"
+ end
+
+ it "doesn't call the ifnone proc if an element is found" do
+ fail_proc = lambda { raise "This shouldn't have been called" }
+ @numerous.send(@method, fail_proc) {|e| e == @elements.first }.should == 2
+ end
+
+ it "calls the ifnone proc only once when the block is false" do
+ times = 0
+ fail_proc = lambda { times += 1; raise if times > 1; "cheeseburgers" }
+ @numerous.send(@method, fail_proc) {|e| false }.should == "cheeseburgers"
+ end
+
+ it "calls the ifnone proc when there are no elements" do
+ fail_proc = lambda { "yay" }
+ @empty.send(@method, fail_proc) {|e| true}.should == "yay"
+ end
+
+ it "passes through the values yielded by #each_with_index" do
+ [:a, :b].each_with_index.send(@method) { |x, i| ScratchPad << [x, i]; nil }
+ ScratchPad.recorded.should == [[:a, 0], [:b, 1]]
+ end
+
+ it "returns an enumerator when no block given" do
+ @numerous.send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ it "passes the ifnone proc to the enumerator" do
+ times = 0
+ fail_proc = lambda { times += 1; raise if times > 1; "cheeseburgers" }
+ @numerous.send(@method, fail_proc).each {|e| false }.should == "cheeseburgers"
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.send(@method) {|e| e == [1, 2] }.should == [1, 2]
+ end
+
+ it_should_behave_like :enumerable_enumeratorized_with_unknown_size
+end
diff --git a/spec/ruby/core/enumerable/shared/find_all.rb b/spec/ruby/core/enumerable/shared/find_all.rb
new file mode 100644
index 0000000000..1bbe71f372
--- /dev/null
+++ b/spec/ruby/core/enumerable/shared/find_all.rb
@@ -0,0 +1,31 @@
+require_relative 'enumerable_enumeratorized'
+
+describe :enumerable_find_all, shared: true do
+ before :each do
+ ScratchPad.record []
+ @elements = (1..10).to_a
+ @numerous = EnumerableSpecs::Numerous.new(*@elements)
+ end
+
+ it "returns all elements for which the block is not false" do
+ @numerous.send(@method) {|i| i % 3 == 0 }.should == [3, 6, 9]
+ @numerous.send(@method) {|i| true }.should == @elements
+ @numerous.send(@method) {|i| false }.should == []
+ end
+
+ it "returns an enumerator when no block given" do
+ @numerous.send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ it "passes through the values yielded by #each_with_index" do
+ [:a, :b].each_with_index.send(@method) { |x, i| ScratchPad << [x, i] }
+ ScratchPad.recorded.should == [[:a, 0], [:b, 1]]
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.send(@method) {|e| e == [3, 4, 5] }.should == [[3, 4, 5]]
+ end
+
+ it_should_behave_like :enumerable_enumeratorized_with_origin_size
+end
diff --git a/spec/ruby/core/enumerable/shared/include.rb b/spec/ruby/core/enumerable/shared/include.rb
new file mode 100644
index 0000000000..569f350fd5
--- /dev/null
+++ b/spec/ruby/core/enumerable/shared/include.rb
@@ -0,0 +1,34 @@
+describe :enumerable_include, shared: true do
+ it "returns true if any element == argument for numbers" do
+ class EnumerableSpecIncludeP; def ==(obj) obj == 5; end; end
+
+ elements = (0..5).to_a
+ EnumerableSpecs::Numerous.new(*elements).send(@method,5).should == true
+ EnumerableSpecs::Numerous.new(*elements).send(@method,10).should == false
+ EnumerableSpecs::Numerous.new(*elements).send(@method,EnumerableSpecIncludeP.new).should == true
+ end
+
+ it "returns true if any element == argument for other objects" do
+ class EnumerableSpecIncludeP11; def ==(obj); obj == '11'; end; end
+
+ elements = ('0'..'5').to_a + [EnumerableSpecIncludeP11.new]
+ EnumerableSpecs::Numerous.new(*elements).send(@method,'5').should == true
+ EnumerableSpecs::Numerous.new(*elements).send(@method,'10').should == false
+ EnumerableSpecs::Numerous.new(*elements).send(@method,EnumerableSpecIncludeP11.new).should == true
+ EnumerableSpecs::Numerous.new(*elements).send(@method,'11').should == true
+ end
+
+
+ it "returns true if any member of enum equals obj when == compare different classes (legacy rubycon)" do
+ # equality is tested with ==
+ EnumerableSpecs::Numerous.new(2,4,6,8,10).send(@method, 2.0).should == true
+ EnumerableSpecs::Numerous.new(2,4,[6,8],10).send(@method, [6, 8]).should == true
+ EnumerableSpecs::Numerous.new(2,4,[6,8],10).send(@method, [6.0, 8.0]).should == true
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.send(@method, [1,2]).should be_true
+ end
+
+end
diff --git a/spec/ruby/core/enumerable/shared/inject.rb b/spec/ruby/core/enumerable/shared/inject.rb
new file mode 100644
index 0000000000..12e0665dda
--- /dev/null
+++ b/spec/ruby/core/enumerable/shared/inject.rb
@@ -0,0 +1,69 @@
+describe :enumerable_inject, shared: true do
+ it "with argument takes a block with an accumulator (with argument as initial value) and the current element. Value of block becomes new accumulator" do
+ a = []
+ EnumerableSpecs::Numerous.new.send(@method, 0) { |memo, i| a << [memo, i]; i }
+ a.should == [[0, 2], [2, 5], [5, 3], [3, 6], [6, 1], [1, 4]]
+ EnumerableSpecs::EachDefiner.new(true, true, true).send(@method, nil) {|result, i| i && result}.should == nil
+ end
+
+ it "produces an array of the accumulator and the argument when given a block with a *arg" do
+ a = []
+ [1,2].send(@method, 0) {|*args| a << args; args[0] + args[1]}
+ a.should == [[0, 1], [1, 2]]
+ end
+
+ it "can take two argument" do
+ EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, :-).should == 4
+ end
+
+ it "ignores the block if two arguments" do
+ EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, :-){ raise "we never get here"}.should == 4
+ end
+
+ it "can take a symbol argument" do
+ EnumerableSpecs::Numerous.new(10, 1, 2, 3).send(@method, :-).should == 4
+ end
+
+ it "without argument takes a block with an accumulator (with first element as initial value) and the current element. Value of block becomes new accumulator" do
+ a = []
+ EnumerableSpecs::Numerous.new.send(@method) { |memo, i| a << [memo, i]; i }
+ a.should == [[2, 5], [5, 3], [3, 6], [6, 1], [1, 4]]
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.send(@method, []) {|acc, e| acc << e }.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
+ end
+
+ it "with inject arguments(legacy rubycon)" do
+ # with inject argument
+ EnumerableSpecs::EachDefiner.new().send(@method, 1) {|acc,x| 999 }.should == 1
+ EnumerableSpecs::EachDefiner.new(2).send(@method, 1) {|acc,x| 999 }.should == 999
+ EnumerableSpecs::EachDefiner.new(2).send(@method, 1) {|acc,x| acc }.should == 1
+ EnumerableSpecs::EachDefiner.new(2).send(@method, 1) {|acc,x| x }.should == 2
+
+ EnumerableSpecs::EachDefiner.new(1,2,3,4).send(@method, 100) {|acc,x| acc + x }.should == 110
+ EnumerableSpecs::EachDefiner.new(1,2,3,4).send(@method, 100) {|acc,x| acc * x }.should == 2400
+
+ EnumerableSpecs::EachDefiner.new('a','b','c').send(@method, "z") {|result, i| i+result}.should == "cbaz"
+ end
+
+ it "without inject arguments(legacy rubycon)" do
+ # no inject argument
+ EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| 999 } .should == 2
+ EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| acc }.should == 2
+ EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| x }.should == 2
+
+ EnumerableSpecs::EachDefiner.new(1,2,3,4).send(@method) {|acc,x| acc + x }.should == 10
+ EnumerableSpecs::EachDefiner.new(1,2,3,4).send(@method) {|acc,x| acc * x }.should == 24
+
+ EnumerableSpecs::EachDefiner.new('a','b','c').send(@method) {|result, i| i+result}.should == "cba"
+ EnumerableSpecs::EachDefiner.new(3, 4, 5).send(@method) {|result, i| result*i}.should == 60
+ EnumerableSpecs::EachDefiner.new([1], 2, 'a','b').send(@method){|r,i| r<<i}.should == [1, 2, 'a', 'b']
+
+ end
+
+ it "returns nil when fails(legacy rubycon)" do
+ EnumerableSpecs::EachDefiner.new().send(@method) {|acc,x| 999 }.should == nil
+ end
+end
diff --git a/spec/ruby/core/enumerable/shared/take.rb b/spec/ruby/core/enumerable/shared/take.rb
new file mode 100644
index 0000000000..bf2536acda
--- /dev/null
+++ b/spec/ruby/core/enumerable/shared/take.rb
@@ -0,0 +1,63 @@
+describe :enumerable_take, shared: true do
+ before :each do
+ @values = [4,3,2,1,0,-1]
+ @enum = EnumerableSpecs::Numerous.new(*@values)
+ end
+
+ it "returns the first count elements if given a count" do
+ @enum.send(@method, 2).should == [4, 3]
+ @enum.send(@method, 4).should == [4, 3, 2, 1] # See redmine #1686 !
+ end
+
+ it "returns an empty array when passed count on an empty array" do
+ empty = EnumerableSpecs::Empty.new
+ empty.send(@method, 0).should == []
+ empty.send(@method, 1).should == []
+ empty.send(@method, 2).should == []
+ end
+
+ it "returns an empty array when passed count == 0" do
+ @enum.send(@method, 0).should == []
+ end
+
+ it "returns an array containing the first element when passed count == 1" do
+ @enum.send(@method, 1).should == [4]
+ end
+
+ it "raises an ArgumentError when count is negative" do
+ lambda { @enum.send(@method, -1) }.should raise_error(ArgumentError)
+ end
+
+ it "returns the entire array when count > length" do
+ @enum.send(@method, 100).should == @values
+ @enum.send(@method, 8).should == @values # See redmine #1686 !
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(3).at_most(:twice) # called twice, no apparent reason. See redmine #1554
+ @enum.send(@method, obj).should == [4, 3, 2]
+ end
+
+ it "raises a TypeError if the passed argument is not numeric" do
+ lambda { @enum.send(@method, nil) }.should raise_error(TypeError)
+ lambda { @enum.send(@method, "a") }.should raise_error(TypeError)
+
+ obj = mock("nonnumeric")
+ lambda { @enum.send(@method, obj) }.should raise_error(TypeError)
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.send(@method, 1).should == [[1, 2]]
+ end
+
+ it "consumes only what is needed" do
+ thrower = EnumerableSpecs::ThrowingEach.new
+ thrower.send(@method, 0).should == []
+ counter = EnumerableSpecs::EachCounter.new(1,2,3,4)
+ counter.send(@method, 2).should == [1,2]
+ counter.times_called.should == 1
+ counter.times_yielded.should == 2
+ end
+end
diff --git a/spec/ruby/core/enumerable/slice_after_spec.rb b/spec/ruby/core/enumerable/slice_after_spec.rb
new file mode 100644
index 0000000000..c1bb7f0d83
--- /dev/null
+++ b/spec/ruby/core/enumerable/slice_after_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#slice_after" do
+ before :each do
+ @enum = EnumerableSpecs::Numerous.new(7, 6, 5, 4, 3, 2, 1)
+ end
+
+ describe "when given an argument and no block" do
+ it "calls === on the argument to determine when to yield" do
+ arg = mock("filter")
+ arg.should_receive(:===).and_return(false, true, false, false, false, true, false)
+ e = @enum.slice_after(arg)
+ e.should be_an_instance_of(Enumerator)
+ e.to_a.should == [[7, 6], [5, 4, 3, 2], [1]]
+ end
+
+ it "doesn't yield an empty array if the filter matches the first entry or the last entry" do
+ arg = mock("filter")
+ arg.should_receive(:===).and_return(true).exactly(7)
+ e = @enum.slice_after(arg)
+ e.to_a.should == [[7], [6], [5], [4], [3], [2], [1]]
+ end
+
+ it "uses standard boolean as a test" do
+ arg = mock("filter")
+ arg.should_receive(:===).and_return(false, :foo, nil, false, false, 42, false)
+ e = @enum.slice_after(arg)
+ e.to_a.should == [[7, 6], [5, 4, 3, 2], [1]]
+ end
+ end
+
+ describe "when given a block" do
+ describe "and no argument" do
+ it "calls the block to determine when to yield" do
+ e = @enum.slice_after{ |i| i == 6 || i == 2 }
+ e.should be_an_instance_of(Enumerator)
+ e.to_a.should == [[7, 6], [5, 4, 3, 2], [1]]
+ end
+ end
+
+ describe "and an argument" do
+ it "raises an ArgumentError" do
+ lambda { @enum.slice_after(42) { |i| i == 6 } }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ it "raises an ArgumentError when given an incorrect number of arguments" do
+ lambda { @enum.slice_after("one", "two") }.should raise_error(ArgumentError)
+ lambda { @enum.slice_after }.should raise_error(ArgumentError)
+ end
+end
+
+describe "when an iterator method yields more than one value" do
+ it "processes all yielded values" do
+ enum = EnumerableSpecs::YieldsMulti.new
+ result = enum.slice_after { |i| i == [3, 4, 5] }.to_a
+ result.should == [[[1, 2], [3, 4, 5]], [[6, 7, 8, 9]]]
+ end
+end
diff --git a/spec/ruby/core/enumerable/slice_before_spec.rb b/spec/ruby/core/enumerable/slice_before_spec.rb
new file mode 100644
index 0000000000..ad730be245
--- /dev/null
+++ b/spec/ruby/core/enumerable/slice_before_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumerable_enumeratorized'
+
+describe "Enumerable#slice_before" do
+ before :each do
+ @enum = EnumerableSpecs::Numerous.new(7,6,5,4,3,2,1)
+ end
+
+ describe "when given an argument and no block" do
+ it "calls === on the argument to determine when to yield" do
+ arg = mock "filter"
+ arg.should_receive(:===).and_return(false, true, false, false, false, true, false)
+ e = @enum.slice_before(arg)
+ e.should be_an_instance_of(Enumerator)
+ e.to_a.should == [[7], [6, 5, 4, 3], [2, 1]]
+ end
+
+ it "doesn't yield an empty array if the filter matches the first entry or the last entry" do
+ arg = mock "filter"
+ arg.should_receive(:===).and_return(true).exactly(7)
+ e = @enum.slice_before(arg)
+ e.to_a.should == [[7], [6], [5], [4], [3], [2], [1]]
+ end
+
+ it "uses standard boolean as a test" do
+ arg = mock "filter"
+ arg.should_receive(:===).and_return(false, :foo, nil, false, false, 42, false)
+ e = @enum.slice_before(arg)
+ e.to_a.should == [[7], [6, 5, 4, 3], [2, 1]]
+ end
+ end
+
+ describe "when given a block" do
+ describe "and no argument" do
+ it "calls the block to determine when to yield" do
+ e = @enum.slice_before{|i| i == 6 || i == 2}
+ e.should be_an_instance_of(Enumerator)
+ e.to_a.should == [[7], [6, 5, 4, 3], [2, 1]]
+ end
+ end
+
+ it "does not accept arguments" do
+ lambda {
+ @enum.slice_before(1) {}
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "raises an ArgumentError when given an incorrect number of arguments" do
+ lambda { @enum.slice_before("one", "two") }.should raise_error(ArgumentError)
+ lambda { @enum.slice_before }.should raise_error(ArgumentError)
+ end
+
+ describe "when an iterator method yields more than one value" do
+ it "processes all yielded values" do
+ enum = EnumerableSpecs::YieldsMulti.new
+ result = enum.slice_before { |i| i == [3, 4, 5] }.to_a
+ result.should == [[[1, 2]], [[3, 4, 5], [6, 7, 8, 9]]]
+ end
+ end
+
+ it_behaves_like :enumerable_enumeratorized_with_unknown_size, [:slice_before, 3]
+end
diff --git a/spec/ruby/core/enumerable/slice_when_spec.rb b/spec/ruby/core/enumerable/slice_when_spec.rb
new file mode 100644
index 0000000000..884b1a471c
--- /dev/null
+++ b/spec/ruby/core/enumerable/slice_when_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#slice_when" do
+ before :each do
+ ary = [10, 9, 7, 6, 4, 3, 2, 1]
+ @enum = EnumerableSpecs::Numerous.new(*ary)
+ @result = @enum.slice_when { |i, j| i - 1 != j }
+ @enum_length = ary.length
+ end
+
+ context "when given a block" do
+ it "returns an enumerator" do
+ @result.should be_an_instance_of(Enumerator)
+ end
+
+ it "splits chunks between adjacent elements i and j where the block returns true" do
+ @result.to_a.should == [[10, 9], [7, 6], [4, 3, 2, 1]]
+ end
+
+ it "calls the block for length of the receiver enumerable minus one times" do
+ times_called = 0
+ @enum.slice_when do |i, j|
+ times_called += 1
+ i - 1 != j
+ end.to_a
+ times_called.should == (@enum_length - 1)
+ end
+
+ it "doesn't yield an empty array if the block matches the first or the last time" do
+ @enum.slice_when { true }.to_a.should == [[10], [9], [7], [6], [4], [3], [2], [1]]
+ end
+
+ it "doesn't yield an empty array on a small enumerable" do
+ EnumerableSpecs::Empty.new.slice_when { raise }.to_a.should == []
+ EnumerableSpecs::Numerous.new(42).slice_when { raise }.to_a.should == [[42]]
+ end
+ end
+
+ context "when not given a block" do
+ it "raises an ArgumentError" do
+ lambda { @enum.slice_when }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "when an iterator method yields more than one value" do
+ it "processes all yielded values" do
+ def foo
+ yield 1, 2
+ end
+ to_enum(:foo).slice_when { true }.to_a.should == [[[1, 2]]]
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/sort_by_spec.rb b/spec/ruby/core/enumerable/sort_by_spec.rb
new file mode 100644
index 0000000000..8fdd923fb4
--- /dev/null
+++ b/spec/ruby/core/enumerable/sort_by_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumerable_enumeratorized'
+
+describe "Enumerable#sort_by" do
+ it "returns an array of elements ordered by the result of block" do
+ a = EnumerableSpecs::Numerous.new("once", "upon", "a", "time")
+ a.sort_by { |i| i[0] }.should == ["a", "once", "time", "upon"]
+ end
+
+ it "sorts the object by the given attribute" do
+ a = EnumerableSpecs::SortByDummy.new("fooo")
+ b = EnumerableSpecs::SortByDummy.new("bar")
+
+ ar = [a, b].sort_by { |d| d.s }
+ ar.should == [b, a]
+ end
+
+ it "returns an Enumerator when a block is not supplied" do
+ a = EnumerableSpecs::Numerous.new("a","b")
+ a.sort_by.should be_an_instance_of(Enumerator)
+ a.to_a.should == ["a", "b"]
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.sort_by {|e| e.size}.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
+ end
+
+ it "returns an array of elements when a block is supplied and #map returns an enumerable" do
+ b = EnumerableSpecs::MapReturnsEnumerable.new
+ b.sort_by{ |x| -x }.should == [3, 2, 1]
+ end
+
+ it "calls #each to iterate over the elements to be sorted" do
+ b = EnumerableSpecs::Numerous.new( 1, 2, 3 )
+ b.should_receive(:each).once.and_yield(1).and_yield(2).and_yield(3)
+ b.should_not_receive :map
+ b.sort_by { |x| -x }.should == [3, 2, 1]
+ end
+
+ it_behaves_like :enumerable_enumeratorized_with_origin_size, :sort_by
+end
diff --git a/spec/ruby/core/enumerable/sort_spec.rb b/spec/ruby/core/enumerable/sort_spec.rb
new file mode 100644
index 0000000000..bf20e55f61
--- /dev/null
+++ b/spec/ruby/core/enumerable/sort_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#sort" do
+ it "sorts by the natural order as defined by <=>" do
+ EnumerableSpecs::Numerous.new.sort.should == [1, 2, 3, 4, 5, 6]
+ sorted = EnumerableSpecs::ComparesByVowelCount.wrap("a" * 1, "a" * 2, "a"*3, "a"*4, "a"*5)
+ EnumerableSpecs::Numerous.new(sorted[2],sorted[0],sorted[1],sorted[3],sorted[4]).sort.should == sorted
+ end
+
+ it "yields elements to the provided block" do
+ EnumerableSpecs::Numerous.new.sort { |a, b| b <=> a }.should == [6, 5, 4, 3, 2, 1]
+ EnumerableSpecs::Numerous.new(2,0,1,3,4).sort { |n, m| -(n <=> m) }.should == [4,3,2,1,0]
+ end
+
+ it "raises a NoMethodError if elements do not define <=>" do
+ lambda do
+ EnumerableSpecs::Numerous.new(BasicObject.new, BasicObject.new, BasicObject.new).sort
+ end.should raise_error(NoMethodError)
+ end
+
+ it "sorts enumerables that contain nils" do
+ arr = EnumerableSpecs::Numerous.new(nil, true, nil, false, nil, true, nil, false, nil)
+ arr.sort { |a, b|
+ x = a ? -1 : a.nil? ? 0 : 1
+ y = b ? -1 : b.nil? ? 0 : 1
+ x <=> y
+ }.should == [true, true, nil, nil, nil, nil, nil, false, false]
+ end
+
+ it "compare values returned by block with 0" do
+ EnumerableSpecs::Numerous.new.sort { |n, m| -(n+m) * (n <=> m) }.should == [6, 5, 4, 3, 2, 1]
+ EnumerableSpecs::Numerous.new.sort { |n, m|
+ EnumerableSpecs::ComparableWithFixnum.new(-(n+m) * (n <=> m))
+ }.should == [6, 5, 4, 3, 2, 1]
+ lambda {
+ EnumerableSpecs::Numerous.new.sort { |n, m| (n <=> m).to_s }
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error if objects can't be compared" do
+ a=EnumerableSpecs::Numerous.new(EnumerableSpecs::Uncomparable.new, EnumerableSpecs::Uncomparable.new)
+ lambda {a.sort}.should raise_error(ArgumentError)
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.sort {|a, b| a.first <=> b.first}.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
+ end
+
+ it "doesn't raise an error if #to_a returns a frozen Array" do
+ EnumerableSpecs::Freezy.new.sort.should == [1,2]
+ end
+end
diff --git a/spec/ruby/core/enumerable/sum_spec.rb b/spec/ruby/core/enumerable/sum_spec.rb
new file mode 100644
index 0000000000..77b66bc1ec
--- /dev/null
+++ b/spec/ruby/core/enumerable/sum_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is '2.4' do
+ describe 'Enumerable#sum' do
+ before :each do
+ @enum = Object.new.to_enum
+ class << @enum
+ def each
+ yield 0
+ yield(-1)
+ yield 2
+ yield 2/3r
+ end
+ end
+ end
+
+ it 'returns amount of the elements with taking an argument as the initial value' do
+ @enum.sum(10).should == 35/3r
+ end
+
+ it 'gives 0 as a default argument' do
+ @enum.sum.should == 5/3r
+ end
+
+ it 'takes a block to transform the elements' do
+ @enum.sum { |element| element * 2 }.should == 10/3r
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/take_spec.rb b/spec/ruby/core/enumerable/take_spec.rb
new file mode 100644
index 0000000000..c0af372752
--- /dev/null
+++ b/spec/ruby/core/enumerable/take_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/take'
+
+describe "Enumerable#take" do
+ it "requires an argument" do
+ lambda{ EnumerableSpecs::Numerous.new.take}.should raise_error(ArgumentError)
+ end
+
+ describe "when passed an argument" do
+ it_behaves_like :enumerable_take, :take
+ end
+end
diff --git a/spec/ruby/core/enumerable/take_while_spec.rb b/spec/ruby/core/enumerable/take_while_spec.rb
new file mode 100644
index 0000000000..26db39ac4b
--- /dev/null
+++ b/spec/ruby/core/enumerable/take_while_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumerable_enumeratorized'
+
+describe "Enumerable#take_while" do
+ before :each do
+ @enum = EnumerableSpecs::Numerous.new(3, 2, 1, :go)
+ end
+
+ it "returns an Enumerator if no block given" do
+ @enum.take_while.should be_an_instance_of(Enumerator)
+ end
+
+ it "returns no/all elements for {true/false} block" do
+ @enum.take_while{true}.should == @enum.to_a
+ @enum.take_while{false}.should == []
+ end
+
+ it "accepts returns other than true/false" do
+ @enum.take_while{1}.should == @enum.to_a
+ @enum.take_while{nil}.should == []
+ end
+
+ it "passes elements to the block until the first false" do
+ a = []
+ @enum.take_while{|obj| (a << obj).size < 3}.should == [3, 2]
+ a.should == [3, 2, 1]
+ end
+
+ it "will only go through what's needed" do
+ enum = EnumerableSpecs::EachCounter.new(4, 3, 2, 1, :stop)
+ enum.take_while { |x|
+ break 42 if x == 3
+ true
+ }.should == 42
+ enum.times_yielded.should == 2
+ end
+
+ it "doesn't return self when it could" do
+ a = [1,2,3]
+ a.take_while{true}.should_not equal(a)
+ end
+
+ it "calls the block with initial args when yielded with multiple arguments" do
+ yields = []
+ EnumerableSpecs::YieldsMixed.new.take_while{ |v| yields << v }
+ yields.should == [1, [2], 3, 5, [8, 9], nil, []]
+ end
+
+ it_behaves_like :enumerable_enumeratorized_with_unknown_size, :take_while
+end
diff --git a/spec/ruby/core/enumerable/to_a_spec.rb b/spec/ruby/core/enumerable/to_a_spec.rb
new file mode 100644
index 0000000000..0f3060dc48
--- /dev/null
+++ b/spec/ruby/core/enumerable/to_a_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/entries'
+
+describe "Enumerable#to_a" do
+ it_behaves_like :enumerable_entries , :to_a
+end
diff --git a/spec/ruby/core/enumerable/to_h_spec.rb b/spec/ruby/core/enumerable/to_h_spec.rb
new file mode 100644
index 0000000000..966325c736
--- /dev/null
+++ b/spec/ruby/core/enumerable/to_h_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#to_h" do
+ it "converts empty enumerable to empty hash" do
+ enum = EnumerableSpecs::EachDefiner.new
+ enum.to_h.should == {}
+ end
+
+ it "converts yielded [key, value] pairs to a hash" do
+ enum = EnumerableSpecs::EachDefiner.new([:a, 1], [:b, 2])
+ enum.to_h.should == { a: 1, b: 2 }
+ end
+
+ it "uses the last value of a duplicated key" do
+ enum = EnumerableSpecs::EachDefiner.new([:a, 1], [:b, 2], [:a, 3])
+ enum.to_h.should == { a: 3, b: 2 }
+ end
+
+ it "calls #to_ary on contents" do
+ pair = mock('to_ary')
+ pair.should_receive(:to_ary).and_return([:b, 2])
+ enum = EnumerableSpecs::EachDefiner.new([:a, 1], pair)
+ enum.to_h.should == { a: 1, b: 2 }
+ end
+
+ it "forwards arguments to #each" do
+ enum = Object.new
+ def enum.each(*args)
+ yield(*args)
+ yield([:b, 2])
+ end
+ enum.extend Enumerable
+ enum.to_h(:a, 1).should == { a: 1, b: 2 }
+ end
+
+ it "raises TypeError if an element is not an array" do
+ enum = EnumerableSpecs::EachDefiner.new(:x)
+ lambda { enum.to_h }.should raise_error(TypeError)
+ end
+
+ it "raises ArgumentError if an element is not a [key, value] pair" do
+ enum = EnumerableSpecs::EachDefiner.new([:x])
+ lambda { enum.to_h }.should raise_error(ArgumentError)
+ end
+
+ ruby_version_is "2.6" do
+ it "converts [key, value] pairs returned by the block to a hash" do
+ enum = EnumerableSpecs::EachDefiner.new(:a, :b)
+ i = 0
+ enum.to_h {|k| [k, i += 1]}.should == { a: 1, b: 2 }
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/uniq_spec.rb b/spec/ruby/core/enumerable/uniq_spec.rb
new file mode 100644
index 0000000000..eb1e70c208
--- /dev/null
+++ b/spec/ruby/core/enumerable/uniq_spec.rb
@@ -0,0 +1,94 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is '2.4' do
+ describe 'Enumerable#uniq' do
+ it 'returns an array that contains only unique elements' do
+ [0, 1, 2, 3].to_enum.uniq { |n| n.even? }.should == [0, 1]
+ end
+
+ it "uses eql? semantics" do
+ [1.0, 1].to_enum.uniq.should == [1.0, 1]
+ end
+
+ it "compares elements first with hash" do
+ x = mock('0')
+ x.should_receive(:hash).at_least(1).and_return(0)
+ y = mock('0')
+ y.should_receive(:hash).at_least(1).and_return(0)
+
+ [x, y].to_enum.uniq.should == [x, y]
+ end
+
+ it "does not compare elements with different hash codes via eql?" do
+ x = mock('0')
+ x.should_not_receive(:eql?)
+ y = mock('1')
+ y.should_not_receive(:eql?)
+
+ x.should_receive(:hash).at_least(1).and_return(0)
+ y.should_receive(:hash).at_least(1).and_return(1)
+
+ [x, y].to_enum.uniq.should == [x, y]
+ end
+
+ it "compares elements with matching hash codes with #eql?" do
+ a = Array.new(2) do
+ obj = mock('0')
+ obj.should_receive(:hash).at_least(1).and_return(0)
+
+ def obj.eql?(o)
+ # It's undefined whether the impl does a[0].eql?(a[1]) or
+ # a[1].eql?(a[0]) so we taint both.
+ taint
+ o.taint
+ false
+ end
+
+ obj
+ end
+
+ a.uniq.should == a
+ a[0].tainted?.should == true
+ a[1].tainted?.should == true
+
+ a = Array.new(2) do
+ obj = mock('0')
+ obj.should_receive(:hash).at_least(1).and_return(0)
+
+ def obj.eql?(o)
+ # It's undefined whether the impl does a[0].eql?(a[1]) or
+ # a[1].eql?(a[0]) so we taint both.
+ taint
+ o.taint
+ true
+ end
+
+ obj
+ end
+
+ a.to_enum.uniq.size.should == 1
+ a[0].tainted?.should == true
+ a[1].tainted?.should == true
+ end
+
+ context 'when yielded with multiple arguments' do
+ before :each do
+ @enum = Object.new.to_enum
+ class << @enum
+ def each
+ yield 0, 'foo'
+ yield 1, 'FOO'
+ yield 2, 'bar'
+ end
+ end
+ end
+
+ ruby_bug '#13669', ''...'2.5' do
+ it 'returns all yield arguments as an array' do
+ @enum.uniq { |_, label| label.downcase }.should == [[0, 'foo'], [2, 'bar']]
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/zip_spec.rb b/spec/ruby/core/enumerable/zip_spec.rb
new file mode 100644
index 0000000000..1212911697
--- /dev/null
+++ b/spec/ruby/core/enumerable/zip_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#zip" do
+
+ it "combines each element of the receiver with the element of the same index in arrays given as arguments" do
+ EnumerableSpecs::Numerous.new(1,2,3).zip([4,5,6],[7,8,9]).should == [[1,4,7],[2,5,8],[3,6,9]]
+ EnumerableSpecs::Numerous.new(1,2,3).zip.should == [[1],[2],[3]]
+ end
+
+ it "passes each element of the result array to a block and return nil if a block is given" do
+ expected = [[1,4,7],[2,5,8],[3,6,9]]
+ EnumerableSpecs::Numerous.new(1,2,3).zip([4,5,6],[7,8,9]) do |result_component|
+ result_component.should == expected.shift
+ end.should == nil
+ expected.size.should == 0
+ end
+
+ it "fills resulting array with nils if an argument array is too short" do
+ EnumerableSpecs::Numerous.new(1,2,3).zip([4,5,6], [7,8]).should == [[1,4,7],[2,5,8],[3,6,nil]]
+ end
+
+ it "converts arguments to arrays using #to_ary" do
+ convertable = EnumerableSpecs::ArrayConvertable.new(4,5,6)
+ EnumerableSpecs::Numerous.new(1,2,3).zip(convertable).should == [[1,4],[2,5],[3,6]]
+ convertable.called.should == :to_ary
+ end
+
+ it "converts arguments to enums using #to_enum" do
+ convertable = EnumerableSpecs::EnumConvertable.new(4..6)
+ EnumerableSpecs::Numerous.new(1,2,3).zip(convertable).should == [[1,4],[2,5],[3,6]]
+ convertable.called.should == :to_enum
+ convertable.sym.should == :each
+ end
+
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.zip(multi).should == [[[1, 2], [1, 2]], [[3, 4, 5], [3, 4, 5]], [[6, 7, 8, 9], [6, 7, 8, 9]]]
+ end
+
+end
diff --git a/spec/ruby/core/enumerator/each_spec.rb b/spec/ruby/core/enumerator/each_spec.rb
new file mode 100644
index 0000000000..e2a78a6a99
--- /dev/null
+++ b/spec/ruby/core/enumerator/each_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/enumerator/each'
+
+describe "Enumerator#each" do
+ it_behaves_like :enum_each, :each
+end
diff --git a/spec/ruby/core/enumerator/each_with_index_spec.rb b/spec/ruby/core/enumerator/each_with_index_spec.rb
new file mode 100644
index 0000000000..0a3c11e0b4
--- /dev/null
+++ b/spec/ruby/core/enumerator/each_with_index_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/enumerator/with_index'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Enumerator#each_with_index" do
+ it_behaves_like :enum_with_index, :each_with_index
+ it_behaves_like :enumeratorized_with_origin_size, :each_with_index, [1,2,3].select
+
+ it "returns a new Enumerator when no block is given" do
+ enum1 = [1,2,3].select
+ enum2 = enum1.each_with_index
+ enum2.should be_an_instance_of(Enumerator)
+ enum1.should_not === enum2
+ end
+
+ it "raises an ArgumentError if passed extra arguments" do
+ lambda do
+ [1].to_enum.each_with_index(:glark)
+ end.should raise_error(ArgumentError)
+ end
+
+ it "passes on the given block's return value" do
+ arr = [1,2,3]
+ arr.delete_if.with_index { |a,b| false }
+ arr.should == [1,2,3]
+ end
+
+ it "returns the iterator's return value" do
+ [1,2,3].select.with_index { |a,b| false }.should == []
+ end
+end
+
+describe "Enumerator#each_with_index" do
+ it "returns the correct value if chained with itself" do
+ [:a].each_with_index.each_with_index.to_a.should == [[[:a,0],0]]
+ [:a].each.with_index.with_index.to_a.should == [[[:a,0],0]]
+ end
+end
diff --git a/spec/ruby/core/enumerator/each_with_object_spec.rb b/spec/ruby/core/enumerator/each_with_object_spec.rb
new file mode 100644
index 0000000000..68524dc74a
--- /dev/null
+++ b/spec/ruby/core/enumerator/each_with_object_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/enumerator/with_object'
+
+describe "Enumerator#each_with_object" do
+ it_behaves_like :enum_with_object, :each_with_object
+end
diff --git a/spec/ruby/core/enumerator/enum_for_spec.rb b/spec/ruby/core/enumerator/enum_for_spec.rb
new file mode 100644
index 0000000000..fd33f463bf
--- /dev/null
+++ b/spec/ruby/core/enumerator/enum_for_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/enumerator/enum_for'
+
+describe "Enumerator#enum_for" do
+ it_behaves_like :enum_for, :enum_for
+end
diff --git a/spec/ruby/core/enumerator/enumerator_spec.rb b/spec/ruby/core/enumerator/enumerator_spec.rb
new file mode 100644
index 0000000000..7a263336cb
--- /dev/null
+++ b/spec/ruby/core/enumerator/enumerator_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Enumerator" do
+ it "includes Enumerable" do
+ Enumerator.include?(Enumerable).should == true
+ end
+end
diff --git a/spec/ruby/core/enumerator/feed_spec.rb b/spec/ruby/core/enumerator/feed_spec.rb
new file mode 100644
index 0000000000..90f0af8f14
--- /dev/null
+++ b/spec/ruby/core/enumerator/feed_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Enumerator#feed" do
+ before :each do
+ ScratchPad.record []
+ @enum = EnumeratorSpecs::Feed.new.to_enum(:each)
+ end
+
+ it "sets the future return value of yield if called before advancing the iterator" do
+ @enum.feed :a
+ @enum.next
+ @enum.next
+ @enum.next
+ ScratchPad.recorded.should == [:a, nil]
+ end
+
+ it "causes yield to return the value if called during iteration" do
+ @enum.next
+ @enum.feed :a
+ @enum.next
+ @enum.next
+ ScratchPad.recorded.should == [:a, nil]
+ end
+
+ it "can be called for each iteration" do
+ @enum.next
+ @enum.feed :a
+ @enum.next
+ @enum.feed :b
+ @enum.next
+ ScratchPad.recorded.should == [:a, :b]
+ end
+
+ it "returns nil" do
+ @enum.feed(:a).should be_nil
+ end
+
+ it "raises a TypeError if called more than once without advancing the enumerator" do
+ @enum.feed :a
+ @enum.next
+ lambda { @enum.feed :b }.should raise_error(TypeError)
+ end
+
+ it "sets the return value of Yielder#yield" do
+ enum = Enumerator.new { |y| ScratchPad << y.yield }
+ enum.next
+ enum.feed :a
+ lambda { enum.next }.should raise_error(StopIteration)
+ ScratchPad.recorded.should == [:a]
+ end
+end
diff --git a/spec/ruby/core/enumerator/first_spec.rb b/spec/ruby/core/enumerator/first_spec.rb
new file mode 100644
index 0000000000..458080bb31
--- /dev/null
+++ b/spec/ruby/core/enumerator/first_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Enumerator#first" do
+ it "returns arrays correctly when calling #first (2376)" do
+ Enumerator.new {|y| y << [42] }.first.should == [42]
+ end
+end
diff --git a/spec/ruby/core/enumerator/fixtures/common.rb b/spec/ruby/core/enumerator/fixtures/common.rb
new file mode 100644
index 0000000000..e332e3195b
--- /dev/null
+++ b/spec/ruby/core/enumerator/fixtures/common.rb
@@ -0,0 +1,9 @@
+module EnumeratorSpecs
+ class Feed
+ def each
+ ScratchPad << yield
+ ScratchPad << yield
+ ScratchPad << yield
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/generator/each_spec.rb b/spec/ruby/core/enumerator/generator/each_spec.rb
new file mode 100644
index 0000000000..cbdf863bec
--- /dev/null
+++ b/spec/ruby/core/enumerator/generator/each_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../../spec_helper'
+
+describe "Enumerator::Generator#each" do
+ before :each do
+ @generator = Enumerator::Generator.new do |y, *args|
+ y << 3 << 2 << 1
+ y << args unless args.empty?
+ :block_returned
+ end
+ end
+
+ it "is an enumerable" do
+ @generator.should be_kind_of(Enumerable)
+ end
+
+ it "supports enumeration with a block" do
+ r = []
+ @generator.each { |v| r << v }
+
+ r.should == [3, 2, 1]
+ end
+
+ it "raises a LocalJumpError if no block given" do
+ lambda { @generator.each }.should raise_error(LocalJumpError)
+ end
+
+ it "returns the block returned value" do
+ @generator.each {}.should equal(:block_returned)
+ end
+
+ it "requires multiple arguments" do
+ Enumerator::Generator.instance_method(:each).arity.should < 0
+ end
+
+ it "appends given arguments to receiver.each" do
+ yields = []
+ @generator.each(:each0, :each1) { |yielded| yields << yielded }
+ yields.should == [3, 2, 1, [:each0, :each1]]
+ end
+end
diff --git a/spec/ruby/core/enumerator/generator/initialize_spec.rb b/spec/ruby/core/enumerator/generator/initialize_spec.rb
new file mode 100644
index 0000000000..38da37a479
--- /dev/null
+++ b/spec/ruby/core/enumerator/generator/initialize_spec.rb
@@ -0,0 +1,26 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+
+describe "Enumerator::Generator#initialize" do
+ before :each do
+ @class = Enumerator::Generator
+ @uninitialized = @class.allocate
+ end
+
+ it "is a private method" do
+ @class.should have_private_instance_method(:initialize, false)
+ end
+
+ it "returns self when given a block" do
+ @uninitialized.send(:initialize) {}.should equal(@uninitialized)
+ end
+
+ describe "on frozen instance" do
+ it "raises a RuntimeError" do
+ lambda {
+ @uninitialized.freeze.send(:initialize) {}
+ }.should raise_error(RuntimeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/initialize_spec.rb b/spec/ruby/core/enumerator/initialize_spec.rb
new file mode 100644
index 0000000000..1305f150dc
--- /dev/null
+++ b/spec/ruby/core/enumerator/initialize_spec.rb
@@ -0,0 +1,61 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../spec_helper'
+
+describe "Enumerator#initialize" do
+ before :each do
+ @uninitialized = Enumerator.allocate
+ end
+
+ it "is a private method" do
+ Enumerator.should have_private_instance_method(:initialize, false)
+ end
+
+ it "returns self when given an object" do
+ @uninitialized.send(:initialize, Object.new).should equal(@uninitialized)
+ end
+
+ it "returns self when given a block" do
+ @uninitialized.send(:initialize) {}.should equal(@uninitialized)
+ end
+
+ # Maybe spec should be broken up?
+ it "accepts a block" do
+ @uninitialized.send(:initialize) do |yielder|
+ r = yielder.yield 3
+ yielder << r << 2 << 1
+ end
+ @uninitialized.should be_an_instance_of(Enumerator)
+ r = []
+ @uninitialized.each{|x| r << x; x * 2}
+ r.should == [3, 6, 2, 1]
+ end
+
+ it "sets size to nil if size is not given" do
+ @uninitialized.send(:initialize) {}.size.should be_nil
+ end
+
+ it "sets size to nil if the given size is nil" do
+ @uninitialized.send(:initialize, nil) {}.size.should be_nil
+ end
+
+ it "sets size to the given size if the given size is Float::INFINITY" do
+ @uninitialized.send(:initialize, Float::INFINITY) {}.size.should equal(Float::INFINITY)
+ end
+
+ it "sets size to the given size if the given size is a Fixnum" do
+ @uninitialized.send(:initialize, 100) {}.size.should == 100
+ end
+
+ it "sets size to the given size if the given size is a Proc" do
+ @uninitialized.send(:initialize, lambda { 200 }) {}.size.should == 200
+ end
+
+ describe "on frozen instance" do
+ it "raises a RuntimeError" do
+ lambda {
+ @uninitialized.freeze.send(:initialize) {}
+ }.should raise_error(RuntimeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/inject_spec.rb b/spec/ruby/core/enumerator/inject_spec.rb
new file mode 100644
index 0000000000..326c7ed581
--- /dev/null
+++ b/spec/ruby/core/enumerator/inject_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../shared/enumerator/each'
+
+describe "Enumerator#inject" do
+ it_behaves_like :enum_each, :each
+
+ it "works when chained against each_with_index" do
+ passed_values = []
+ [:a].each_with_index.inject(0) do |accumulator,value|
+ passed_values << value
+ accumulator + 1
+ end.should == 1
+ passed_values.should == [[:a,0]]
+ end
+
+end
diff --git a/spec/ruby/core/enumerator/inspect_spec.rb b/spec/ruby/core/enumerator/inspect_spec.rb
new file mode 100644
index 0000000000..3bcf07e754
--- /dev/null
+++ b/spec/ruby/core/enumerator/inspect_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "Enumerator#inspect" do
+ describe "shows a representation of the Enumerator" do
+ it "including receiver and method" do
+ (1..3).each.inspect.should == "#<Enumerator: 1..3:each>"
+ end
+
+ it "including receiver and method and arguments" do
+ (1..3).each_slice(2).inspect.should == "#<Enumerator: 1..3:each_slice(2)>"
+ end
+
+ it "including the nested Enumerator" do
+ (1..3).each.each_slice(2).inspect.should == "#<Enumerator: #<Enumerator: 1..3:each>:each_slice(2)>"
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/chunk_spec.rb b/spec/ruby/core/enumerator/lazy/chunk_spec.rb
new file mode 100644
index 0000000000..246dd72712
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/chunk_spec.rb
@@ -0,0 +1,71 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerator::Lazy#chunk" do
+
+ before :each do
+ @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy
+ @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy
+ ScratchPad.record []
+ end
+
+ after :each do
+ ScratchPad.clear
+ end
+
+ it "returns a new instance of Enumerator::Lazy" do
+ ret = @yieldsmixed.chunk {}
+ ret.should be_an_instance_of(Enumerator::Lazy)
+ ret.should_not equal(@yieldsmixed)
+ end
+
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.chunk { |v| v }.size.should == nil
+ end
+
+ ruby_version_is ""..."2.4" do
+ it "raises an ArgumentError if called without a block" do
+ lambda do
+ @yieldsmixed.chunk
+ end.should raise_error(ArgumentError)
+ end
+ end
+
+ ruby_version_is "2.4" do
+ it "returns an Enumerator if called without a block" do
+ chunk = @yieldsmixed.chunk
+ chunk.should be_an_instance_of(Enumerator::Lazy)
+
+ res = chunk.each { |v| true }.force
+ res.should == [[true, EnumeratorLazySpecs::YieldsMixed.gathered_yields]]
+ end
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ first_two = (0..Float::INFINITY).lazy.chunk { |n| n.even? }.first(2)
+ first_two.should == [[true, [0]], [false, [1]]]
+ end
+ end
+
+ it "calls the block with gathered values when yield with multiple arguments" do
+ yields = []
+ @yieldsmixed.chunk { |v| yields << v; true }.force
+ yields.should == EnumeratorLazySpecs::YieldsMixed.gathered_yields
+ end
+
+ describe "on a nested Lazy" do
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.take(20).chunk { |v| v }.size.should == nil
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ remains_lazy = (0..Float::INFINITY).lazy.chunk { |n| n }
+ remains_lazy.chunk { |n| n }.first(2).size.should == 2
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/collect_concat_spec.rb b/spec/ruby/core/enumerator/lazy/collect_concat_spec.rb
new file mode 100644
index 0000000000..8765bb2190
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/collect_concat_spec.rb
@@ -0,0 +1,8 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'shared/collect_concat'
+
+describe "Enumerator::Lazy#collect_concat" do
+ it_behaves_like :enumerator_lazy_collect_concat, :collect_concat
+end
diff --git a/spec/ruby/core/enumerator/lazy/collect_spec.rb b/spec/ruby/core/enumerator/lazy/collect_spec.rb
new file mode 100644
index 0000000000..14b79ce16d
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/collect_spec.rb
@@ -0,0 +1,8 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'shared/collect'
+
+describe "Enumerator::Lazy#collect" do
+ it_behaves_like :enumerator_lazy_collect, :collect
+end
diff --git a/spec/ruby/core/enumerator/lazy/drop_spec.rb b/spec/ruby/core/enumerator/lazy/drop_spec.rb
new file mode 100644
index 0000000000..8c15dea11d
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/drop_spec.rb
@@ -0,0 +1,52 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerator::Lazy#drop" do
+ before :each do
+ @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy
+ @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy
+ ScratchPad.record []
+ end
+
+ after :each do
+ ScratchPad.clear
+ end
+
+ it "returns a new instance of Enumerator::Lazy" do
+ ret = @yieldsmixed.drop(1)
+ ret.should be_an_instance_of(Enumerator::Lazy)
+ ret.should_not equal(@yieldsmixed)
+ end
+
+ it "sets difference of given count with old size to new size" do
+ Enumerator::Lazy.new(Object.new, 100) {}.drop(20).size.should == 80
+ Enumerator::Lazy.new(Object.new, 100) {}.drop(200).size.should == 0
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.drop(2).first(2).should == [2, 3]
+
+ @eventsmixed.drop(0).first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+
+ describe "on a nested Lazy" do
+ it "sets difference of given count with old size to new size" do
+ Enumerator::Lazy.new(Object.new, 100) {}.drop(20).drop(50).size.should == 30
+ Enumerator::Lazy.new(Object.new, 100) {}.drop(50).drop(20).size.should == 30
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.drop(2).drop(2).first(2).should == [4, 5]
+
+ @eventsmixed.drop(0).drop(0).first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/drop_while_spec.rb b/spec/ruby/core/enumerator/lazy/drop_while_spec.rb
new file mode 100644
index 0000000000..e29026be09
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/drop_while_spec.rb
@@ -0,0 +1,60 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerator::Lazy#drop_while" do
+ before :each do
+ @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy
+ @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy
+ ScratchPad.record []
+ end
+
+ after :each do
+ ScratchPad.clear
+ end
+
+ it "returns a new instance of Enumerator::Lazy" do
+ ret = @yieldsmixed.drop_while {}
+ ret.should be_an_instance_of(Enumerator::Lazy)
+ ret.should_not equal(@yieldsmixed)
+ end
+
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.drop_while { |v| v }.size.should == nil
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.drop_while { |n| n < 5 }.first(2).should == [5, 6]
+
+ @eventsmixed.drop_while { false }.first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+
+ it "calls the block with initial values when yield with multiple arguments" do
+ yields = []
+ @yieldsmixed.drop_while { |v| yields << v; true }.force
+ yields.should == EnumeratorLazySpecs::YieldsMixed.initial_yields
+ end
+
+ it "raises an ArgumentError when not given a block" do
+ lambda { @yieldsmixed.drop_while }.should raise_error(ArgumentError)
+ end
+
+ describe "on a nested Lazy" do
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.take(20).drop_while { |v| v }.size.should == nil
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.drop_while { |n| n < 5 }.drop_while { |n| n.odd? }.first(2).should == [6, 7]
+
+ @eventsmixed.drop_while { false }.drop_while { false }.first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/enum_for_spec.rb b/spec/ruby/core/enumerator/lazy/enum_for_spec.rb
new file mode 100644
index 0000000000..7e7783f6f1
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/enum_for_spec.rb
@@ -0,0 +1,8 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'shared/to_enum'
+
+describe "Enumerator::Lazy#enum_for" do
+ it_behaves_like :enumerator_lazy_to_enum, :enum_for
+end
diff --git a/spec/ruby/core/enumerator/lazy/find_all_spec.rb b/spec/ruby/core/enumerator/lazy/find_all_spec.rb
new file mode 100644
index 0000000000..8b05c53803
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/find_all_spec.rb
@@ -0,0 +1,8 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'shared/select'
+
+describe "Enumerator::Lazy#find_all" do
+ it_behaves_like :enumerator_lazy_select, :find_all
+end
diff --git a/spec/ruby/core/enumerator/lazy/fixtures/classes.rb b/spec/ruby/core/enumerator/lazy/fixtures/classes.rb
new file mode 100644
index 0000000000..e35592ba1c
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/fixtures/classes.rb
@@ -0,0 +1,54 @@
+# -*- encoding: us-ascii -*-
+
+module EnumeratorLazySpecs
+ class SpecificError < Exception; end
+
+ class YieldsMixed
+ def self.initial_yields
+ [nil, 0, 0, 0, 0, nil, :default_arg, [], [], [0], [0, 1], [0, 1, 2]]
+ end
+
+ def self.gathered_yields
+ [nil, 0, [0, 1], [0, 1, 2], [0, 1, 2], nil, :default_arg, [], [], [0], [0, 1], [0, 1, 2]]
+ end
+
+ def self.gathered_non_array_yields
+ [nil, 0, nil, :default_arg]
+ end
+
+ def self.gathered_yields_with_args(arg, *args)
+ [nil, 0, [0, 1], [0, 1, 2], [0, 1, 2], nil, arg, args, [], [0], [0, 1], [0, 1, 2]]
+ end
+
+ def each(arg=:default_arg, *args)
+ yield
+ yield 0
+ yield 0, 1
+ yield 0, 1, 2
+ yield(*[0, 1, 2])
+ yield nil
+ yield arg
+ yield args
+ yield []
+ yield [0]
+ yield [0, 1]
+ yield [0, 1, 2]
+ end
+ end
+
+ class EventsMixed
+ def each
+ ScratchPad << :before_yield
+
+ yield 0
+
+ ScratchPad << :after_yield
+
+ raise SpecificError
+
+ ScratchPad << :after_error
+
+ :should_not_reach_here
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/flat_map_spec.rb b/spec/ruby/core/enumerator/lazy/flat_map_spec.rb
new file mode 100644
index 0000000000..6a3391a503
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/flat_map_spec.rb
@@ -0,0 +1,8 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'shared/collect_concat'
+
+describe "Enumerator::Lazy#flat_map" do
+ it_behaves_like :enumerator_lazy_collect_concat, :flat_map
+end
diff --git a/spec/ruby/core/enumerator/lazy/force_spec.rb b/spec/ruby/core/enumerator/lazy/force_spec.rb
new file mode 100644
index 0000000000..15701d1fd7
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/force_spec.rb
@@ -0,0 +1,30 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerator::Lazy#force" do
+ before :each do
+ @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy
+ @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy
+ ScratchPad.record []
+ end
+
+ after :each do
+ ScratchPad.clear
+ end
+
+ it "passes given arguments to receiver.each" do
+ @yieldsmixed.force(:arg1, :arg2, :arg3).should ==
+ EnumeratorLazySpecs::YieldsMixed.gathered_yields_with_args(:arg1, :arg2, :arg3)
+ end
+
+ describe "on a nested Lazy" do
+ it "calls all block and returns an Array" do
+ (0..Float::INFINITY).lazy.map(&:succ).take(2).force.should == [1, 2]
+
+ @eventsmixed.take(1).map(&:succ).force.should == [1]
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/grep_spec.rb b/spec/ruby/core/enumerator/lazy/grep_spec.rb
new file mode 100644
index 0000000000..c46c760402
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/grep_spec.rb
@@ -0,0 +1,82 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerator::Lazy#grep" do
+ before :each do
+ @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy
+ @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy
+ ScratchPad.record []
+ end
+
+ after :each do
+ ScratchPad.clear
+ end
+
+ it "requires an argument" do
+ Enumerator::Lazy.instance_method(:grep).arity.should == 1
+ end
+
+ it "returns a new instance of Enumerator::Lazy" do
+ ret = @yieldsmixed.grep(Object) {}
+ ret.should be_an_instance_of(Enumerator::Lazy)
+ ret.should_not equal(@yieldsmixed)
+
+ ret = @yieldsmixed.grep(Object)
+ ret.should be_an_instance_of(Enumerator::Lazy)
+ ret.should_not equal(@yieldsmixed)
+ end
+
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.grep(Object) {}.size.should == nil
+ Enumerator::Lazy.new(Object.new, 100) {}.grep(Object).size.should == nil
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times when not given a block" do
+ (0..Float::INFINITY).lazy.grep(Integer).first(3).should == [0, 1, 2]
+
+ @eventsmixed.grep(BasicObject).first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+
+ it "stops after specified times when given a block" do
+ (0..Float::INFINITY).lazy.grep(Integer, &:succ).first(3).should == [1, 2, 3]
+
+ @eventsmixed.grep(BasicObject) {}.first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+
+ it "calls the block with a gathered array when yield with multiple arguments" do
+ yields = []
+ @yieldsmixed.grep(BasicObject) { |v| yields << v }.force
+ yields.should == EnumeratorLazySpecs::YieldsMixed.gathered_yields
+
+ @yieldsmixed.grep(BasicObject).force.should == yields
+ end
+
+ describe "on a nested Lazy" do
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.grep(Object) {}.size.should == nil
+ Enumerator::Lazy.new(Object.new, 100) {}.grep(Object).size.should == nil
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times when not given a block" do
+ (0..Float::INFINITY).lazy.grep(Integer).grep(Object).first(3).should == [0, 1, 2]
+
+ @eventsmixed.grep(BasicObject).grep(Object).first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+
+ it "stops after specified times when given a block" do
+ (0..Float::INFINITY).lazy.grep(Integer) { |n| n > 3 ? n : false }.grep(Integer) { |n| n.even? ? n : false }.first(3).should == [4, false, 6]
+
+ @eventsmixed.grep(BasicObject) {}.grep(Object) {}.first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/grep_v_spec.rb b/spec/ruby/core/enumerator/lazy/grep_v_spec.rb
new file mode 100644
index 0000000000..a0ec819505
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/grep_v_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerator::Lazy#grep_v" do
+ before(:each) do
+ @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy
+ @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy
+ ScratchPad.record []
+ end
+
+ after(:each) do
+ ScratchPad.clear
+ end
+
+ it "requires an argument" do
+ Enumerator::Lazy.instance_method(:grep_v).arity.should == 1
+ end
+
+ it "returns a new instance of Enumerator::Lazy" do
+ ret = @yieldsmixed.grep_v(Object) {}
+ ret.should be_an_instance_of(Enumerator::Lazy)
+ ret.should_not equal(@yieldsmixed)
+
+ ret = @yieldsmixed.grep_v(Object)
+ ret.should be_an_instance_of(Enumerator::Lazy)
+ ret.should_not equal(@yieldsmixed)
+ end
+
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.grep_v(Object) {}.size.should == nil
+ Enumerator::Lazy.new(Object.new, 100) {}.grep_v(Object).size.should == nil
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times when not given a block" do
+ (0..Float::INFINITY).lazy.grep_v(3..5).first(3).should == [0, 1, 2]
+
+ @eventsmixed.grep_v(Symbol).first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+
+ it "stops after specified times when given a block" do
+ (0..Float::INFINITY).lazy.grep_v(4..8, &:succ).first(3).should == [1, 2, 3]
+
+ @eventsmixed.grep_v(Symbol) {}.first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+
+ it "calls the block with a gathered array when yield with multiple arguments" do
+ yields = []
+ @yieldsmixed.grep_v(Array) { |v| yields << v }.force
+ yields.should == EnumeratorLazySpecs::YieldsMixed.gathered_non_array_yields
+
+ @yieldsmixed.grep_v(Array).force.should == yields
+ end
+
+ describe "on a nested Lazy" do
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.grep_v(Object).grep_v(Object) {}.size.should == nil
+ Enumerator::Lazy.new(Object.new, 100) {}.grep_v(Object).grep_v(Object).size.should == nil
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times when not given a block" do
+ (0..Float::INFINITY).lazy.grep_v(3..5).grep_v(6..10).first(3).should == [0, 1, 2]
+
+ @eventsmixed.grep_v(Symbol).grep_v(String).first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+
+ it "stops after specified times when given a block" do
+ (0..Float::INFINITY).lazy
+ .grep_v(1..2) { |n| n > 3 ? n : false }
+ .grep_v(false) { |n| n.even? ? n : false }
+ .first(3)
+ .should == [4, false, 6]
+
+ @eventsmixed.grep_v(Symbol) {}.grep_v(String) {}.first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/initialize_spec.rb b/spec/ruby/core/enumerator/lazy/initialize_spec.rb
new file mode 100644
index 0000000000..9fa6eff35a
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/initialize_spec.rb
@@ -0,0 +1,63 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+
+describe "Enumerator::Lazy#initialize" do
+ before :each do
+ @receiver = receiver = Object.new
+
+ def receiver.each
+ yield 0
+ yield 1
+ yield 2
+ end
+
+ @uninitialized = Enumerator::Lazy.allocate
+ end
+
+ it "is a private method" do
+ Enumerator::Lazy.should have_private_instance_method(:initialize, false)
+ end
+
+ it "returns self" do
+ @uninitialized.send(:initialize, @receiver) {}.should equal(@uninitialized)
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ @uninitialized.send(:initialize, @receiver) do |yielder, *values|
+ yielder.<<(*values)
+ end.first(2).should == [0, 1]
+ end
+ end
+
+ it "sets #size to nil if not given a size" do
+ @uninitialized.send(:initialize, @receiver) {}.size.should be_nil
+ end
+
+ it "sets #size to nil if given size is nil" do
+ @uninitialized.send(:initialize, @receiver, nil) {}.size.should be_nil
+ end
+
+ it "sets given size to own size if the given size is Float::INFINITY" do
+ @uninitialized.send(:initialize, @receiver, Float::INFINITY) {}.size.should equal(Float::INFINITY)
+ end
+
+ it "sets given size to own size if the given size is a Fixnum" do
+ @uninitialized.send(:initialize, @receiver, 100) {}.size.should == 100
+ end
+
+ it "sets given size to own size if the given size is a Proc" do
+ @uninitialized.send(:initialize, @receiver, lambda { 200 }) {}.size.should == 200
+ end
+
+ it "raises an ArgumentError when block is not given" do
+ lambda { @uninitialized.send :initialize, @receiver }.should raise_error(ArgumentError)
+ end
+
+ describe "on frozen instance" do
+ it "raises a RuntimeError" do
+ lambda { @uninitialized.freeze.send(:initialize, @receiver) {} }.should raise_error(RuntimeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/lazy_spec.rb b/spec/ruby/core/enumerator/lazy/lazy_spec.rb
new file mode 100644
index 0000000000..5f386e2184
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/lazy_spec.rb
@@ -0,0 +1,16 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+
+describe "Enumerator::Lazy" do
+ it "is a subclass of Enumerator" do
+ Enumerator::Lazy.superclass.should equal(Enumerator)
+ end
+end
+
+describe "Enumerator::Lazy#lazy" do
+ it "returns self" do
+ lazy = (1..3).to_enum.lazy
+ lazy.lazy.should equal(lazy)
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/map_spec.rb b/spec/ruby/core/enumerator/lazy/map_spec.rb
new file mode 100644
index 0000000000..5cb998f5f7
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/map_spec.rb
@@ -0,0 +1,12 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'shared/collect'
+
+describe "Enumerator::Lazy#map" do
+ it_behaves_like :enumerator_lazy_collect, :map
+
+ it "doesn't unwrap Arrays" do
+ Enumerator.new {|y| y.yield([1])}.lazy.to_a.should == [[1]]
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/reject_spec.rb b/spec/ruby/core/enumerator/lazy/reject_spec.rb
new file mode 100644
index 0000000000..2661907b39
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/reject_spec.rb
@@ -0,0 +1,60 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerator::Lazy#reject" do
+ before :each do
+ @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy
+ @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy
+ ScratchPad.record []
+ end
+
+ after :each do
+ ScratchPad.clear
+ end
+
+ it "returns a new instance of Enumerator::Lazy" do
+ ret = @yieldsmixed.reject {}
+ ret.should be_an_instance_of(Enumerator::Lazy)
+ ret.should_not equal(@yieldsmixed)
+ end
+
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.reject {}.size.should == nil
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.reject(&:even?).first(3).should == [1, 3, 5]
+
+ @eventsmixed.reject { false }.first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+
+ it "calls the block with a gathered array when yield with multiple arguments" do
+ yields = []
+ @yieldsmixed.reject { |v| yields << v }.force
+ yields.should == EnumeratorLazySpecs::YieldsMixed.gathered_yields
+ end
+
+ it "raises an ArgumentError when not given a block" do
+ lambda { @yieldsmixed.reject }.should raise_error(ArgumentError)
+ end
+
+ describe "on a nested Lazy" do
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.take(20).reject {}.size.should == nil
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.reject { |n| n < 4 }.reject(&:even?).first(3).should == [5, 7, 9]
+
+ @eventsmixed.reject { false }.reject { false }.first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/select_spec.rb b/spec/ruby/core/enumerator/lazy/select_spec.rb
new file mode 100644
index 0000000000..c4143c5251
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/select_spec.rb
@@ -0,0 +1,8 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'shared/select'
+
+describe "Enumerator::Lazy#select" do
+ it_behaves_like :enumerator_lazy_select, :select
+end
diff --git a/spec/ruby/core/enumerator/lazy/shared/collect.rb b/spec/ruby/core/enumerator/lazy/shared/collect.rb
new file mode 100644
index 0000000000..a686f30fd8
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/shared/collect.rb
@@ -0,0 +1,56 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :enumerator_lazy_collect, shared: true do
+ before :each do
+ @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy
+ @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy
+ ScratchPad.record []
+ end
+
+ after :each do
+ ScratchPad.clear
+ end
+
+ it "returns a new instance of Enumerator::Lazy" do
+ ret = @yieldsmixed.send(@method) {}
+ ret.should be_an_instance_of(Enumerator::Lazy)
+ ret.should_not equal(@yieldsmixed)
+ end
+
+ it "keeps size" do
+ Enumerator::Lazy.new(Object.new, 100) {}.send(@method) {}.size.should == 100
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.send(@method, &:succ).first(3).should == [1, 2, 3]
+
+ @eventsmixed.send(@method) {}.first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+
+ it "calls the block with initial values when yield with multiple arguments" do
+ yields = []
+ @yieldsmixed.send(@method) { |v| yields << v }.force
+ yields.should == EnumeratorLazySpecs::YieldsMixed.initial_yields
+ end
+
+ describe "on a nested Lazy" do
+ it "keeps size" do
+ Enumerator::Lazy.new(Object.new, 100) {}.send(@method) {}.send(@method) {}.size.should == 100
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.send(@method, &:succ).send(@method, &:succ).first(3).should == [2, 3, 4]
+
+ @eventsmixed.send(@method) {}.send(@method) {}.first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/shared/collect_concat.rb b/spec/ruby/core/enumerator/lazy/shared/collect_concat.rb
new file mode 100644
index 0000000000..26478f4ce0
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/shared/collect_concat.rb
@@ -0,0 +1,72 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :enumerator_lazy_collect_concat, shared: true do
+ before :each do
+ @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy
+ @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy
+ ScratchPad.record []
+ end
+
+ after :each do
+ ScratchPad.clear
+ end
+
+ it "returns a new instance of Enumerator::Lazy" do
+ ret = @yieldsmixed.send(@method) {}
+ ret.should be_an_instance_of(Enumerator::Lazy)
+ ret.should_not equal(@yieldsmixed)
+ end
+
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.send(@method) { true }.size.should == nil
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s }.first(6).should == %w[0 10 20 30 40 50]
+
+ @eventsmixed.send(@method) {}.first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+
+ it "flattens elements when the given block returned an array or responding to .each and .force" do
+ (0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s.chars }.first(6).should == %w[0 1 0 2 0 3]
+ (0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s.each_char }.first(6).all? { |o| o.instance_of? Enumerator }.should be_true
+ (0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s.each_char.lazy }.first(6).should == %w[0 1 0 2 0 3]
+ end
+ end
+
+ it "calls the block with initial values when yield with multiple arguments" do
+ yields = []
+ @yieldsmixed.send(@method) { |v| yields << v }.force
+ yields.should == EnumeratorLazySpecs::YieldsMixed.initial_yields
+ end
+
+ it "raises an ArgumentError when not given a block" do
+ lambda { @yieldsmixed.send(@method) }.should raise_error(ArgumentError)
+ end
+
+ describe "on a nested Lazy" do
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.take(50) {}.send(@method) {}.size.should == nil
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s }.first(6).should == %w[0 10 20 30 40 50]
+
+ @eventsmixed.send(@method) {}.send(@method) {}.first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+
+ it "flattens elements when the given block returned an array or responding to .each and .force" do
+ (0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s.chars }.first(6).should == %w[0 1 0 2 0 3]
+ (0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s.each_char }.first(6).all? { |o| o.instance_of? Enumerator }.should be_true
+ (0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s.each_char.lazy }.first(6).should == %w[0 1 0 2 0 3]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/shared/select.rb b/spec/ruby/core/enumerator/lazy/shared/select.rb
new file mode 100644
index 0000000000..9127cf72b4
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/shared/select.rb
@@ -0,0 +1,60 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :enumerator_lazy_select, shared: true do
+ before :each do
+ @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy
+ @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy
+ ScratchPad.record []
+ end
+
+ after :each do
+ ScratchPad.clear
+ end
+
+ it "returns a new instance of Enumerator::Lazy" do
+ ret = @yieldsmixed.send(@method) {}
+ ret.should be_an_instance_of(Enumerator::Lazy)
+ ret.should_not equal(@yieldsmixed)
+ end
+
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.send(@method) { true }.size.should == nil
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.send(@method, &:even?).first(3).should == [0, 2, 4]
+
+ @eventsmixed.send(@method) { true }.first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+
+ it "calls the block with a gathered array when yield with multiple arguments" do
+ yields = []
+ @yieldsmixed.send(@method) { |v| yields << v }.force
+ yields.should == EnumeratorLazySpecs::YieldsMixed.gathered_yields
+ end
+
+ it "raises an ArgumentError when not given a block" do
+ lambda { @yieldsmixed.send(@method) }.should raise_error(ArgumentError)
+ end
+
+ describe "on a nested Lazy" do
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.take(50) {}.send(@method) { true }.size.should == nil
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.send(@method) { |n| n > 5 }.send(@method, &:even?).first(3).should == [6, 8, 10]
+
+ @eventsmixed.send(@method) { true }.send(@method) { true }.first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/shared/to_enum.rb b/spec/ruby/core/enumerator/lazy/shared/to_enum.rb
new file mode 100644
index 0000000000..a62a8ef90e
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/shared/to_enum.rb
@@ -0,0 +1,50 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../../spec_helper'
+
+describe :enumerator_lazy_to_enum, shared: true do
+ before :each do
+ @infinite = (0..Float::INFINITY).lazy
+ end
+
+ it "requires multiple arguments" do
+ Enumerator::Lazy.instance_method(@method).arity.should < 0
+ end
+
+ it "returns a new instance of Enumerator::Lazy" do
+ ret = @infinite.send @method
+ ret.should be_an_instance_of(Enumerator::Lazy)
+ ret.should_not equal(@infinite)
+ end
+
+ it "sets #size to nil when not given a block" do
+ Enumerator::Lazy.new(Object.new, 100) {}.send(@method).size.should == nil
+ end
+
+ it "sets given block to size when given a block" do
+ Enumerator::Lazy.new(Object.new, 100) {}.send(@method) { 30 }.size.should == 30
+ end
+
+ it "generates a lazy enumerator from the given name" do
+ @infinite.send(@method, :with_index, 10).first(3).should == [[0, 10], [1, 11], [2, 12]]
+ end
+
+ it "passes given arguments to wrapped method" do
+ @infinite.send(@method, :each_slice, 2).map { |assoc| assoc.first * assoc.last }.first(4).should == [0, 6, 20, 42]
+ end
+
+ it "used by some parent's methods though returning Lazy" do
+ { each_with_index: [],
+ with_index: [],
+ cycle: [1],
+ each_with_object: [Object.new],
+ with_object: [Object.new],
+ each_slice: [2],
+ each_entry: [],
+ each_cons: [2]
+ }.each_pair do |method, args|
+ @infinite.method(method).owner.should_not equal(Enumerator::Lazy)
+ @infinite.send(method, *args).should be_an_instance_of(Enumerator::Lazy)
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/take_spec.rb b/spec/ruby/core/enumerator/lazy/take_spec.rb
new file mode 100644
index 0000000000..9fc17e969f
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/take_spec.rb
@@ -0,0 +1,66 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerator::Lazy#take" do
+ before :each do
+ @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy
+ @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy
+ ScratchPad.record []
+ end
+
+ after :each do
+ ScratchPad.clear
+ end
+
+ it "returns a new instance of Enumerator::Lazy" do
+ ret = @yieldsmixed.take(1)
+ ret.should be_an_instance_of(Enumerator::Lazy)
+ ret.should_not equal(@yieldsmixed)
+ end
+
+ it "sets given count to size if the given count is less than old size" do
+ Enumerator::Lazy.new(Object.new, 100) {}.take(20).size.should == 20
+ Enumerator::Lazy.new(Object.new, 100) {}.take(200).size.should == 100
+ end
+
+ it "sets given count to size if the old size is Infinity" do
+ loop.lazy.take(20).size.should == 20
+ end
+
+ describe "when the returned lazy enumerator is evaluated by .force" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.take(2).force.should == [0, 1]
+
+ @eventsmixed.take(1).force
+ ScratchPad.recorded.should == [:before_yield]
+ end
+
+ it "stops without iterations if the given argument is 0" do
+ @eventsmixed.take(0).force
+ ScratchPad.recorded.should == []
+ end
+ end
+
+ describe "on a nested Lazy" do
+ it "sets given count to size if the given count is less than old size" do
+ Enumerator::Lazy.new(Object.new, 100) {}.take(20).take(50).size.should == 20
+ Enumerator::Lazy.new(Object.new, 100) {}.take(50).take(20).size.should == 20
+ end
+
+ describe "when the returned lazy enumerator is evaluated by .force" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.map(&:succ).take(2).force.should == [1, 2]
+
+ @eventsmixed.take(10).take(1).force
+ ScratchPad.recorded.should == [:before_yield]
+ end
+
+ it "stops without iterations if the given argument is 0" do
+ @eventsmixed.take(10).take(0).force
+ ScratchPad.recorded.should == []
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/take_while_spec.rb b/spec/ruby/core/enumerator/lazy/take_while_spec.rb
new file mode 100644
index 0000000000..412ff76787
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/take_while_spec.rb
@@ -0,0 +1,60 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerator::Lazy#take_while" do
+ before :each do
+ @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy
+ @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy
+ ScratchPad.record []
+ end
+
+ after :each do
+ ScratchPad.clear
+ end
+
+ it "returns a new instance of Enumerator::Lazy" do
+ ret = @yieldsmixed.take_while {}
+ ret.should be_an_instance_of(Enumerator::Lazy)
+ ret.should_not equal(@yieldsmixed)
+ end
+
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.take_while { true }.size.should == nil
+ end
+
+ describe "when the returned lazy enumerator is evaluated by .force" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.take_while { |n| n < 3 }.force.should == [0, 1, 2]
+
+ @eventsmixed.take_while { false }.force
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+
+ it "calls the block with initial values when yield with multiple arguments" do
+ yields = []
+ @yieldsmixed.take_while { |v| yields << v; true }.force
+ yields.should == EnumeratorLazySpecs::YieldsMixed.initial_yields
+ end
+
+ it "raises an ArgumentError when not given a block" do
+ lambda { @yieldsmixed.take_while }.should raise_error(ArgumentError)
+ end
+
+ describe "on a nested Lazy" do
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.take(20).take_while { true }.size.should == nil
+ end
+
+ describe "when the returned lazy enumerator is evaluated by .force" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.take_while { |n| n < 3 }.take_while(&:even?).force.should == [0]
+
+ @eventsmixed.take_while { true }.take_while { false }.force
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/to_enum_spec.rb b/spec/ruby/core/enumerator/lazy/to_enum_spec.rb
new file mode 100644
index 0000000000..210e5294b7
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/to_enum_spec.rb
@@ -0,0 +1,8 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'shared/to_enum'
+
+describe "Enumerator::Lazy#to_enum" do
+ it_behaves_like :enumerator_lazy_to_enum, :to_enum
+end
diff --git a/spec/ruby/core/enumerator/lazy/uniq_spec.rb b/spec/ruby/core/enumerator/lazy/uniq_spec.rb
new file mode 100644
index 0000000000..fea2bec637
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/uniq_spec.rb
@@ -0,0 +1,76 @@
+require_relative '../../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is '2.4' do
+ describe 'Enumerator::Lazy#uniq' do
+ context 'without block' do
+ before :each do
+ @lazy = [0, 1, 0, 1].to_enum.lazy.uniq
+ end
+
+ it 'returns a lazy enumerator' do
+ @lazy.should be_an_instance_of(Enumerator::Lazy)
+ @lazy.force.should == [0, 1]
+ end
+
+ ruby_bug "#14495", "2.4"..."2.5.2" do
+ it 'return same value after rewind' do
+ @lazy.force.should == [0, 1]
+ @lazy.force.should == [0, 1]
+ end
+ end
+
+ it 'sets the size to nil' do
+ @lazy.size.should == nil
+ end
+ end
+
+ context 'when yielded with an argument' do
+ before :each do
+ @lazy = [0, 1, 2, 3].to_enum.lazy.uniq(&:even?)
+ end
+
+ it 'returns a lazy enumerator' do
+ @lazy.should be_an_instance_of(Enumerator::Lazy)
+ @lazy.force.should == [0, 1]
+ end
+
+ ruby_bug "#14495", "2.4"..."2.5.2" do
+ it 'return same value after rewind' do
+ @lazy.force.should == [0, 1]
+ @lazy.force.should == [0, 1]
+ end
+ end
+
+ it 'sets the size to nil' do
+ @lazy.size.should == nil
+ end
+ end
+
+ context 'when yielded with multiple arguments' do
+ before :each do
+ enum = Object.new.to_enum
+ class << enum
+ def each
+ yield 0, 'foo'
+ yield 1, 'FOO'
+ yield 2, 'bar'
+ end
+ end
+ @lazy = enum.lazy
+ end
+
+ ruby_bug "#14495", "2.4"..."2.5.2" do
+ it 'return same value after rewind' do
+ enum = @lazy.uniq { |_, label| label.downcase }
+ enum.force.should == [[0, 'foo'], [2, 'bar']]
+ enum.force.should == [[0, 'foo'], [2, 'bar']]
+ end
+ end
+
+ it 'returns all yield arguments as an array' do
+ @lazy.uniq { |_, label| label.downcase }.force.should == [[0, 'foo'], [2, 'bar']]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/zip_spec.rb b/spec/ruby/core/enumerator/lazy/zip_spec.rb
new file mode 100644
index 0000000000..f3dbb63158
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/zip_spec.rb
@@ -0,0 +1,74 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerator::Lazy#zip" do
+ before :each do
+ @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy
+ @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy
+ ScratchPad.record []
+ end
+
+ after :each do
+ ScratchPad.clear
+ end
+
+ it "returns a new instance of Enumerator::Lazy" do
+ ret = @yieldsmixed.zip []
+ ret.should be_an_instance_of(Enumerator::Lazy)
+ ret.should_not equal(@yieldsmixed)
+ end
+
+ it "keeps size" do
+ Enumerator::Lazy.new(Object.new, 100) {}.zip([], []).size.should == 100
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.zip([4, 5], [8]).first(2).should == [[0, 4, 8], [1, 5, nil]]
+
+ @eventsmixed.zip([0, 1]).first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+
+ it "calls the block with a gathered array when yield with multiple arguments" do
+ yields = @yieldsmixed.zip(EnumeratorLazySpecs::YieldsMixed.new.to_enum).force
+ yields.should == [EnumeratorLazySpecs::YieldsMixed.gathered_yields,
+ EnumeratorLazySpecs::YieldsMixed.gathered_yields].transpose
+ end
+
+ it "returns a Lazy when no arguments given" do
+ @yieldsmixed.zip.should be_an_instance_of(Enumerator::Lazy)
+ end
+
+ it "raises a TypeError if arguments contain non-list object" do
+ lambda { @yieldsmixed.zip [], Object.new, [] }.should raise_error(TypeError)
+ end
+
+ describe "on a nested Lazy" do
+ it "keeps size" do
+ Enumerator::Lazy.new(Object.new, 100) {}.map {}.zip([], []).size.should == 100
+ end
+
+ it "behaves as Enumerable#zip when given a block" do
+ lazy_yields = []
+ lazy_ret = @yieldsmixed.zip(EnumeratorLazySpecs::YieldsMixed.new.to_enum) { |lists| lazy_yields << lists }
+ enum_yields = []
+ enum_ret = EnumeratorLazySpecs::YieldsMixed.new.to_enum.zip(EnumeratorLazySpecs::YieldsMixed.new.to_enum) { |lists| enum_yields << lists }
+
+ lazy_yields.should == enum_yields
+ lazy_ret.should == enum_ret
+ end
+
+ describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
+ it "stops after specified times" do
+ (0..Float::INFINITY).lazy.map(&:succ).zip([4, 5], [8]).first(2).should == [[1, 4, 8], [2, 5, nil]]
+
+ @eventsmixed.zip([0, 1]).zip([0, 1]).first(1)
+ ScratchPad.recorded.should == [:before_yield]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/new_spec.rb b/spec/ruby/core/enumerator/new_spec.rb
new file mode 100644
index 0000000000..86d2d38022
--- /dev/null
+++ b/spec/ruby/core/enumerator/new_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/enumerator/new'
+
+describe "Enumerator.new" do
+ it_behaves_like :enum_new, :new
+end
diff --git a/spec/ruby/core/enumerator/next_spec.rb b/spec/ruby/core/enumerator/next_spec.rb
new file mode 100644
index 0000000000..1d3baf54d7
--- /dev/null
+++ b/spec/ruby/core/enumerator/next_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/enumerator/next'
+
+describe "Enumerator#next" do
+ it_behaves_like :enum_next,:next
+end
diff --git a/spec/ruby/core/enumerator/next_values_spec.rb b/spec/ruby/core/enumerator/next_values_spec.rb
new file mode 100644
index 0000000000..8b57332c28
--- /dev/null
+++ b/spec/ruby/core/enumerator/next_values_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../../spec_helper'
+
+describe "Enumerator#next_values" do
+ before :each do
+ o = Object.new
+ def o.each
+ yield :a
+ yield :b1, :b2
+ yield :c
+ yield :d1, :d2
+ yield :e1, :e2, :e3
+ yield nil
+ yield
+ end
+
+ @e = o.to_enum
+ end
+
+ it "returns the next element in self" do
+ @e.next_values.should == [:a]
+ end
+
+ it "advances the position of the current element" do
+ @e.next.should == :a
+ @e.next_values.should == [:b1, :b2]
+ @e.next.should == :c
+ end
+
+ it "advances the position of the enumerator each time when called multiple times" do
+ 2.times { @e.next_values }
+ @e.next_values.should == [:c]
+ @e.next.should == [:d1, :d2]
+ end
+
+ it "works in concert with #rewind" do
+ 2.times { @e.next }
+ @e.rewind
+ @e.next_values.should == [:a]
+ end
+
+ it "returns an array with only nil if yield is called with nil" do
+ 5.times { @e.next }
+ @e.next_values.should == [nil]
+ end
+
+ it "returns an empty array if yield is called without arguments" do
+ 6.times { @e.next }
+ @e.next_values.should == []
+ end
+
+ it "raises StopIteration if called on a finished enumerator" do
+ 7.times { @e.next }
+ lambda { @e.next_values }.should raise_error(StopIteration)
+ end
+end
diff --git a/spec/ruby/core/enumerator/peek_spec.rb b/spec/ruby/core/enumerator/peek_spec.rb
new file mode 100644
index 0000000000..0e4f60893d
--- /dev/null
+++ b/spec/ruby/core/enumerator/peek_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+
+describe "Enumerator#peek" do
+ before :each do
+ @e = (1..5).to_a.to_enum
+ end
+
+ it "returns the next element in self" do
+ @e.peek.should == 1
+ end
+
+ it "does not advance the position of the current element" do
+ @e.next.should == 1
+ @e.peek.should == 2
+ @e.next.should == 2
+ end
+
+ it "can be called repeatedly without advancing the position of the current element" do
+ @e.peek
+ @e.peek
+ @e.peek.should == 1
+ @e.next.should == 1
+ end
+
+ it "works in concert with #rewind" do
+ @e.next
+ @e.next
+ @e.rewind
+ @e.peek.should == 1
+ end
+
+ it "raises StopIteration if called on a finished enumerator" do
+ 5.times { @e.next }
+ lambda { @e.peek }.should raise_error(StopIteration)
+ end
+end
diff --git a/spec/ruby/core/enumerator/peek_values_spec.rb b/spec/ruby/core/enumerator/peek_values_spec.rb
new file mode 100644
index 0000000000..5c81b4e529
--- /dev/null
+++ b/spec/ruby/core/enumerator/peek_values_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../spec_helper'
+
+describe "Enumerator#peek_values" do
+ before :each do
+ o = Object.new
+ def o.each
+ yield :a
+ yield :b1, :b2
+ yield :c
+ yield :d1, :d2
+ yield :e1, :e2, :e3
+ yield nil
+ yield
+ end
+
+ @e = o.to_enum
+ end
+
+ it "returns the next element in self" do
+ @e.peek_values.should == [:a]
+ end
+
+ it "does not advance the position of the current element" do
+ @e.next.should == :a
+ @e.peek_values.should == [:b1, :b2]
+ @e.next.should == [:b1, :b2]
+ end
+
+ it "can be called repeatedly without advancing the position of the current element" do
+ @e.peek_values
+ @e.peek_values
+ @e.peek_values.should == [:a]
+ @e.next.should == :a
+ end
+
+ it "works in concert with #rewind" do
+ @e.next
+ @e.next
+ @e.rewind
+ @e.peek_values.should == [:a]
+ end
+
+ it "returns an array with only nil if yield is called with nil" do
+ 5.times { @e.next }
+ @e.peek_values.should == [nil]
+ end
+
+ it "returns an empty array if yield is called without arguments" do
+ 6.times { @e.next }
+ @e.peek_values.should == []
+ end
+
+ it "raises StopIteration if called on a finished enumerator" do
+ 7.times { @e.next }
+ lambda { @e.peek_values }.should raise_error(StopIteration)
+ end
+end
diff --git a/spec/ruby/core/enumerator/rewind_spec.rb b/spec/ruby/core/enumerator/rewind_spec.rb
new file mode 100644
index 0000000000..e846f63c49
--- /dev/null
+++ b/spec/ruby/core/enumerator/rewind_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/enumerator/rewind'
+require_relative 'fixtures/common'
+
+describe "Enumerator#rewind" do
+ it_behaves_like :enum_rewind, :rewind
+
+ it "calls the enclosed object's rewind method if one exists" do
+ obj = mock('rewinder')
+ enum = obj.to_enum
+ obj.should_receive(:each).at_most(1)
+ obj.should_receive(:rewind)
+ enum.rewind
+ end
+
+ it "does nothing if the object doesn't have a #rewind method" do
+ obj = mock('rewinder')
+ enum = obj.to_enum
+ obj.should_receive(:each).at_most(1)
+ lambda { enum.rewind.should == enum }.should_not raise_error
+ end
+end
+
+describe "Enumerator#rewind" do
+ before :each do
+ ScratchPad.record []
+ @enum = EnumeratorSpecs::Feed.new.to_enum(:each)
+ end
+
+ it "clears a pending #feed value" do
+ @enum.next
+ @enum.feed :a
+ @enum.rewind
+ @enum.next
+ @enum.next
+ ScratchPad.recorded.should == [nil]
+ end
+end
diff --git a/spec/ruby/core/enumerator/size_spec.rb b/spec/ruby/core/enumerator/size_spec.rb
new file mode 100644
index 0000000000..5729b9303d
--- /dev/null
+++ b/spec/ruby/core/enumerator/size_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+
+describe "Enumerator#size" do
+ it "returns same value if set size is an Integer" do
+ Enumerator.new(100) {}.size.should == 100
+ end
+
+ it "returns nil if set size is nil" do
+ Enumerator.new(nil) {}.size.should be_nil
+ end
+
+ it "returns returning value from size.call if set size is a Proc" do
+ base_size = 100
+ enum = Enumerator.new(lambda { base_size + 1 }) {}
+ base_size = 200
+ enum.size.should == 201
+ base_size = 300
+ enum.size.should == 301
+ end
+
+ it "returns the result from size.call if the size respond to call" do
+ obj = mock('call')
+ obj.should_receive(:call).and_return(42)
+ Enumerator.new(obj) {}.size.should == 42
+ end
+end
diff --git a/spec/ruby/core/enumerator/to_enum_spec.rb b/spec/ruby/core/enumerator/to_enum_spec.rb
new file mode 100644
index 0000000000..cadfcf6314
--- /dev/null
+++ b/spec/ruby/core/enumerator/to_enum_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/enumerator/enum_for'
+
+describe "Enumerator#to_enum" do
+ it_behaves_like :enum_for, :enum_for
+end
diff --git a/spec/ruby/core/enumerator/with_index_spec.rb b/spec/ruby/core/enumerator/with_index_spec.rb
new file mode 100644
index 0000000000..ca9f5a133e
--- /dev/null
+++ b/spec/ruby/core/enumerator/with_index_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/enumerator/with_index'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Enumerator#with_index" do
+ it_behaves_like :enum_with_index, :with_index
+ it_behaves_like :enumeratorized_with_origin_size, :with_index, [1,2,3].select
+
+ it "returns a new Enumerator when no block is given" do
+ enum1 = [1,2,3].select
+ enum2 = enum1.with_index
+ enum2.should be_an_instance_of(Enumerator)
+ enum1.should_not === enum2
+ end
+
+ it "accepts an optional argument when given a block" do
+ lambda do
+ @enum.with_index(1) { |f| f}
+ end.should_not raise_error(ArgumentError)
+ end
+
+ it "accepts an optional argument when not given a block" do
+ lambda do
+ @enum.with_index(1)
+ end.should_not raise_error(ArgumentError)
+ end
+
+ it "numbers indices from the given index when given an offset but no block" do
+ @enum.with_index(1).to_a.should == [[1,1], [2,2], [3,3], [4,4]]
+ end
+
+ it "numbers indices from the given index when given an offset and block" do
+ acc = []
+ @enum.with_index(1) {|e,i| acc << [e,i] }
+ acc.should == [[1,1], [2,2], [3,3], [4,4]]
+ end
+
+ it "raises a TypeError when the argument cannot be converted to numeric" do
+ lambda do
+ @enum.with_index('1') {|*i| i}
+ end.should raise_error(TypeError)
+ end
+
+ it "converts non-numeric arguments to Integer via #to_int" do
+ (o = mock('1')).should_receive(:to_int).and_return(1)
+ @enum.with_index(o).to_a.should == [[1,1], [2,2], [3,3], [4,4]]
+ end
+
+ it "coerces the given numeric argument to an Integer" do
+ @enum.with_index(1.678).to_a.should == [[1,1], [2,2], [3,3], [4,4]]
+
+ res = []
+ @enum.with_index(1.001) { |*x| res << x}
+ res.should == [[1,1], [2,2], [3,3], [4,4]]
+ end
+
+ it "treats nil argument as no argument" do
+ @enum.with_index(nil).to_a.should == [[1,0], [2,1], [3,2], [4,3]]
+
+ res = []
+ @enum.with_index(nil) { |*x| res << x}
+ res.should == [[1,0], [2,1], [3,2], [4,3]]
+ end
+
+ it "accepts negative argument" do
+ @enum.with_index(-1).to_a.should == [[1,-1], [2,0], [3,1], [4,2]]
+
+ res = []
+ @enum.with_index(-1) { |*x| res << x}
+ res.should == [[1,-1], [2,0], [3,1], [4,2]]
+ end
+end
diff --git a/spec/ruby/core/enumerator/with_object_spec.rb b/spec/ruby/core/enumerator/with_object_spec.rb
new file mode 100644
index 0000000000..e7ba83fd9f
--- /dev/null
+++ b/spec/ruby/core/enumerator/with_object_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/enumerator/with_object'
+
+describe "Enumerator#with_object" do
+ it_behaves_like :enum_with_object, :with_object
+end
diff --git a/spec/ruby/core/enumerator/yielder/append_spec.rb b/spec/ruby/core/enumerator/yielder/append_spec.rb
new file mode 100644
index 0000000000..dac66585a5
--- /dev/null
+++ b/spec/ruby/core/enumerator/yielder/append_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+
+describe "Enumerator::Yielder#<<" do
+ # TODO: There's some common behavior between yield and <<; move to a shared spec
+ it "yields the value to the block" do
+ ary = []
+ y = Enumerator::Yielder.new {|x| ary << x}
+ y << 1
+
+ ary.should == [1]
+ end
+
+ it "doesn't double-wrap Arrays" do
+ yields = []
+ y = Enumerator::Yielder.new {|args| yields << args }
+ y << [1]
+ yields.should == [[1]]
+ end
+
+ it "returns self" do
+ y = Enumerator::Yielder.new {|x| x + 1}
+ (y << 1).should equal(y)
+ end
+end
diff --git a/spec/ruby/core/enumerator/yielder/initialize_spec.rb b/spec/ruby/core/enumerator/yielder/initialize_spec.rb
new file mode 100644
index 0000000000..5a6eee2d0f
--- /dev/null
+++ b/spec/ruby/core/enumerator/yielder/initialize_spec.rb
@@ -0,0 +1,18 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+
+describe "Enumerator::Yielder#initialize" do
+ before :each do
+ @class = Enumerator::Yielder
+ @uninitialized = @class.allocate
+ end
+
+ it "is a private method" do
+ @class.should have_private_instance_method(:initialize, false)
+ end
+
+ it "returns self when given a block" do
+ @uninitialized.send(:initialize) {}.should equal(@uninitialized)
+ end
+end
diff --git a/spec/ruby/core/enumerator/yielder/yield_spec.rb b/spec/ruby/core/enumerator/yielder/yield_spec.rb
new file mode 100644
index 0000000000..58fc8e007a
--- /dev/null
+++ b/spec/ruby/core/enumerator/yielder/yield_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+
+describe "Enumerator::Yielder#yield" do
+ it "yields the value to the block" do
+ ary = []
+ y = Enumerator::Yielder.new {|x| ary << x}
+ y.yield 1
+
+ ary.should == [1]
+ end
+
+ it "yields with passed arguments" do
+ yields = []
+ y = Enumerator::Yielder.new {|*args| yields << args }
+ y.yield 1, 2
+ yields.should == [[1, 2]]
+ end
+
+ it "returns the result of the block for the given value" do
+ y = Enumerator::Yielder.new {|x| x + 1}
+ y.yield(1).should == 2
+ end
+end
diff --git a/spec/ruby/core/env/assoc_spec.rb b/spec/ruby/core/env/assoc_spec.rb
new file mode 100644
index 0000000000..853eca79a5
--- /dev/null
+++ b/spec/ruby/core/env/assoc_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+
+describe "ENV.assoc" do
+ after :each do
+ ENV.delete("foo")
+ end
+
+ it "returns an array of the key and value of the environment variable with the given key" do
+ ENV["foo"] = "bar"
+ ENV.assoc("foo").should == ["foo", "bar"]
+ end
+
+ it "returns nil if no environment variable with the given key exists" do
+ ENV.assoc("foo").should == nil
+ end
+
+ it "returns the key element coerced with #to_str" do
+ ENV["foo"] = "bar"
+ k = mock('key')
+ k.should_receive(:to_str).and_return("foo")
+ ENV.assoc(k).should == ["foo", "bar"]
+ end
+end
diff --git a/spec/ruby/core/env/clear_spec.rb b/spec/ruby/core/env/clear_spec.rb
new file mode 100644
index 0000000000..fd0984a220
--- /dev/null
+++ b/spec/ruby/core/env/clear_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+
+describe "ENV.clear" do
+ it "deletes all environment variables" do
+ orig = ENV.to_hash
+ begin
+ ENV.clear
+
+ # This used 'env' the helper before. That shells out to 'env' which
+ # itself sets up certain environment variables before it runs, because
+ # the shell sets them up before it runs any command.
+ #
+ # Thusly, you can ONLY test this by asking through ENV itself.
+ ENV.size.should == 0
+ ensure
+ ENV.replace orig
+ end
+ end
+
+end
diff --git a/spec/ruby/core/env/delete_if_spec.rb b/spec/ruby/core/env/delete_if_spec.rb
new file mode 100644
index 0000000000..d64443194e
--- /dev/null
+++ b/spec/ruby/core/env/delete_if_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "ENV.delete_if" do
+ it "deletes pairs if the block returns true" do
+ ENV["foo"] = "bar"
+ ENV.delete_if { |k, v| k == "foo" }
+ ENV["foo"].should == nil
+ end
+
+ it "returns ENV even if nothing deleted" do
+ ENV.delete_if { false }.should_not == nil
+ end
+
+ it "returns an Enumerator if no block given" do
+ ENV.delete_if.should be_an_instance_of(Enumerator)
+ end
+
+ it "deletes pairs through enumerator" do
+ ENV["foo"] = "bar"
+ enum = ENV.delete_if
+ enum.each { |k, v| k == "foo" }
+ ENV["foo"].should == nil
+ end
+
+ it_behaves_like :enumeratorized_with_origin_size, :delete_if, ENV
+end
diff --git a/spec/ruby/core/env/delete_spec.rb b/spec/ruby/core/env/delete_spec.rb
new file mode 100644
index 0000000000..1e677fb252
--- /dev/null
+++ b/spec/ruby/core/env/delete_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+
+describe "ENV.delete" do
+ after :each do
+ ENV.delete("foo")
+ end
+
+ it "removes the variable from the environment" do
+ ENV["foo"] = "bar"
+ ENV.delete("foo")
+ ENV["foo"].should == nil
+ end
+
+ it "returns the previous value" do
+ ENV["foo"] = "bar"
+ ENV.delete("foo").should == "bar"
+ end
+
+ it "yields the name to the given block if the named environment variable does not exist" do
+ ENV.delete("foo")
+ ENV.delete("foo") { |name| ScratchPad.record name }
+ ScratchPad.recorded.should == "foo"
+ end
+end
diff --git a/spec/ruby/core/env/each_key_spec.rb b/spec/ruby/core/env/each_key_spec.rb
new file mode 100644
index 0000000000..294bf39912
--- /dev/null
+++ b/spec/ruby/core/env/each_key_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "ENV.each_key" do
+
+ it "returns each key" do
+ e = []
+ orig = ENV.to_hash
+ begin
+ ENV.clear
+ ENV["1"] = "3"
+ ENV["2"] = "4"
+ ENV.each_key { |k| e << k }
+ e.should include("1")
+ e.should include("2")
+ ensure
+ ENV.replace orig
+ end
+ end
+
+ it "returns an Enumerator if called without a block" do
+ ENV.each_key.should be_an_instance_of(Enumerator)
+ end
+
+ it "returns keys in the locale encoding" do
+ ENV.each_key do |key|
+ key.encoding.should == Encoding.find('locale')
+ end
+ end
+
+ it_behaves_like :enumeratorized_with_origin_size, :each_key, ENV
+end
diff --git a/spec/ruby/core/env/each_pair_spec.rb b/spec/ruby/core/env/each_pair_spec.rb
new file mode 100644
index 0000000000..01082dd419
--- /dev/null
+++ b/spec/ruby/core/env/each_pair_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/each'
+
+describe "ENV.each_pair" do
+ it_behaves_like :env_each, :each_pair
+end
diff --git a/spec/ruby/core/env/each_spec.rb b/spec/ruby/core/env/each_spec.rb
new file mode 100644
index 0000000000..06d8be3124
--- /dev/null
+++ b/spec/ruby/core/env/each_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/each'
+
+describe "ENV.each" do
+ it_behaves_like :env_each, :each
+end
diff --git a/spec/ruby/core/env/each_value_spec.rb b/spec/ruby/core/env/each_value_spec.rb
new file mode 100644
index 0000000000..88f4bc49da
--- /dev/null
+++ b/spec/ruby/core/env/each_value_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "ENV.each_value" do
+
+ it "returns each value" do
+ e = []
+ orig = ENV.to_hash
+ begin
+ ENV.clear
+ ENV["1"] = "3"
+ ENV["2"] = "4"
+ ENV.each_value { |v| e << v }
+ e.should include("3")
+ e.should include("4")
+ ensure
+ ENV.replace orig
+ end
+ end
+
+ it "returns an Enumerator if called without a block" do
+ ENV.each_value.should be_an_instance_of(Enumerator)
+ end
+
+ it "uses the locale encoding" do
+ ENV.each_value do |value|
+ value.encoding.should == Encoding.find('locale')
+ end
+ end
+
+ it_behaves_like :enumeratorized_with_origin_size, :each_value, ENV
+end
diff --git a/spec/ruby/core/env/element_reference_spec.rb b/spec/ruby/core/env/element_reference_spec.rb
new file mode 100644
index 0000000000..f9b9fe5f49
--- /dev/null
+++ b/spec/ruby/core/env/element_reference_spec.rb
@@ -0,0 +1,66 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../spec_helper'
+
+describe "ENV.[]" do
+ before :each do
+ @variable = "returns_only_frozen_values"
+ end
+
+ after :each do
+ ENV.delete @variable
+ end
+
+ it "returns nil if the variable isn't found" do
+ ENV["this_var_is_never_set"].should == nil
+ end
+
+ it "returns only frozen values" do
+ ENV[@variable] = "a non-frozen string"
+ ENV[@variable].frozen?.should == true
+ end
+
+ platform_is :windows do
+ it "looks up values case-insensitively" do
+ ENV[@variable] = "bar"
+ ENV[@variable.upcase].should == "bar"
+ end
+ end
+end
+
+with_feature :encoding do
+ describe "ENV.[]" do
+ before :each do
+ @variable = "env_element_reference_encoding_specs"
+
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+
+ Encoding.default_external = Encoding::ASCII_8BIT
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+
+ ENV.delete @variable
+ end
+
+ it "uses the locale encoding if Encoding.default_internal is nil" do
+ Encoding.default_internal = nil
+
+ locale = Encoding.find('locale')
+ locale = Encoding::ASCII_8BIT if locale == Encoding::US_ASCII
+ ENV[@variable] = "\xC3\xB8"
+ ENV[@variable].encoding.should == locale
+ end
+
+ it "transcodes from the locale encoding to Encoding.default_internal if set" do
+ # We cannot reliably know the locale encoding, so we merely check that
+ # the result string has the expected encoding.
+ ENV[@variable] = ""
+ Encoding.default_internal = Encoding::IBM437
+
+ ENV[@variable].encoding.should equal(Encoding::IBM437)
+ end
+ end
+end
diff --git a/spec/ruby/core/env/element_set_spec.rb b/spec/ruby/core/env/element_set_spec.rb
new file mode 100644
index 0000000000..26dfee1ade
--- /dev/null
+++ b/spec/ruby/core/env/element_set_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/store'
+
+describe "ENV.[]=" do
+ it_behaves_like :env_store, :[]=
+end
diff --git a/spec/ruby/core/env/empty_spec.rb b/spec/ruby/core/env/empty_spec.rb
new file mode 100644
index 0000000000..7ef17d244f
--- /dev/null
+++ b/spec/ruby/core/env/empty_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+
+describe "ENV.empty?" do
+
+ it "returns true if the Environment is empty" do
+ if ENV.keys.size > 0
+ ENV.empty?.should == false
+ end
+ orig = ENV.to_hash
+ begin
+ ENV.clear
+ ENV.empty?.should == true
+ ensure
+ ENV.replace orig
+ end
+ end
+
+ it "returns false if not empty" do
+ if ENV.keys.size > 0
+ ENV.empty?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/env/fetch_spec.rb b/spec/ruby/core/env/fetch_spec.rb
new file mode 100644
index 0000000000..ea9995a3b0
--- /dev/null
+++ b/spec/ruby/core/env/fetch_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/hash/key_error'
+
+describe "ENV.fetch" do
+ it "returns a value" do
+ ENV["foo"] = "bar"
+ ENV.fetch("foo").should == "bar"
+ ENV.delete "foo"
+ end
+
+ it "raises a TypeError if the key is not a String" do
+ lambda { ENV.fetch :should_never_be_set }.should raise_error(TypeError)
+ end
+
+ context "when the key is not found" do
+ it_behaves_like :key_error, ->(obj, key) { obj.fetch(key) }, ENV
+ end
+
+ it "provides the given default parameter" do
+ ENV.fetch("should_never_be_set", "default").should == "default"
+ end
+
+ it "provides a default value from a block" do
+ ENV.fetch("should_never_be_set") { |k| "wanted #{k}" }.should == "wanted should_never_be_set"
+ end
+
+ it "warns on block and default parameter given" do
+ lambda do
+ ENV.fetch("should_never_be_set", "default") { 1 }.should == 1
+ end.should complain(/block supersedes default value argument/)
+ end
+
+ it "uses the locale encoding" do
+ ENV.fetch(ENV.keys.first).encoding.should == Encoding.find('locale')
+ end
+end
diff --git a/spec/ruby/core/env/has_key_spec.rb b/spec/ruby/core/env/has_key_spec.rb
new file mode 100644
index 0000000000..798668105d
--- /dev/null
+++ b/spec/ruby/core/env/has_key_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/include'
+
+describe "ENV.has_key?" do
+ it_behaves_like :env_include, :has_key?
+end
diff --git a/spec/ruby/core/env/has_value_spec.rb b/spec/ruby/core/env/has_value_spec.rb
new file mode 100644
index 0000000000..a2bf3eb877
--- /dev/null
+++ b/spec/ruby/core/env/has_value_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/value'
+
+describe "ENV.has_value?" do
+ it_behaves_like :env_value, :has_value?
+end
diff --git a/spec/ruby/core/env/include_spec.rb b/spec/ruby/core/env/include_spec.rb
new file mode 100644
index 0000000000..3975f095ac
--- /dev/null
+++ b/spec/ruby/core/env/include_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/include'
+
+describe "ENV.include?" do
+ it_behaves_like :env_include, :include?
+end
diff --git a/spec/ruby/core/env/index_spec.rb b/spec/ruby/core/env/index_spec.rb
new file mode 100644
index 0000000000..04986a0421
--- /dev/null
+++ b/spec/ruby/core/env/index_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/key'
+
+describe "ENV.index" do
+ it_behaves_like :env_key, :index
+end
diff --git a/spec/ruby/core/env/indexes_spec.rb b/spec/ruby/core/env/indexes_spec.rb
new file mode 100644
index 0000000000..e724feaa39
--- /dev/null
+++ b/spec/ruby/core/env/indexes_spec.rb
@@ -0,0 +1 @@
+require_relative '../../spec_helper'
diff --git a/spec/ruby/core/env/indices_spec.rb b/spec/ruby/core/env/indices_spec.rb
new file mode 100644
index 0000000000..e724feaa39
--- /dev/null
+++ b/spec/ruby/core/env/indices_spec.rb
@@ -0,0 +1 @@
+require_relative '../../spec_helper'
diff --git a/spec/ruby/core/env/inspect_spec.rb b/spec/ruby/core/env/inspect_spec.rb
new file mode 100644
index 0000000000..3c611c24a1
--- /dev/null
+++ b/spec/ruby/core/env/inspect_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "ENV.inspect" do
+
+ it "returns a String that looks like a Hash with real data" do
+ ENV["foo"] = "bar"
+ ENV.inspect.should =~ /\{.*"foo"=>"bar".*\}/
+ ENV.delete "foo"
+ end
+
+end
diff --git a/spec/ruby/core/env/invert_spec.rb b/spec/ruby/core/env/invert_spec.rb
new file mode 100644
index 0000000000..c095374d95
--- /dev/null
+++ b/spec/ruby/core/env/invert_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+
+describe "ENV.invert" do
+ before :each do
+ ENV["foo"] = "bar"
+ end
+
+ after :each do
+ ENV.delete "foo"
+ end
+
+ it "returns a hash with ENV.keys as the values and vice versa" do
+ ENV.invert["bar"].should == "foo"
+ ENV["foo"].should == "bar"
+ end
+end
diff --git a/spec/ruby/core/env/keep_if_spec.rb b/spec/ruby/core/env/keep_if_spec.rb
new file mode 100644
index 0000000000..cf8e27936e
--- /dev/null
+++ b/spec/ruby/core/env/keep_if_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "ENV.keep_if" do
+ before :each do
+ ENV["foo"] = "bar"
+ end
+
+ after :each do
+ ENV.delete "foo"
+ end
+
+ it "deletes pairs if the block returns false" do
+ ENV.keep_if { |k, v| k != "foo" }
+ ENV["foo"].should == nil
+ end
+
+ it "returns ENV even if nothing deleted" do
+ ENV.keep_if { true }.should_not == nil
+ end
+
+ it "returns an Enumerator if no block given" do
+ ENV.keep_if.should be_an_instance_of(Enumerator)
+ end
+
+ it "deletes pairs through enumerator" do
+ enum = ENV.keep_if
+ enum.each { |k, v| k != "foo" }
+ ENV["foo"].should == nil
+ end
+
+ it_behaves_like :enumeratorized_with_origin_size, :keep_if, ENV
+end
diff --git a/spec/ruby/core/env/key_spec.rb b/spec/ruby/core/env/key_spec.rb
new file mode 100644
index 0000000000..82cfbefa39
--- /dev/null
+++ b/spec/ruby/core/env/key_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/include'
+require_relative 'shared/key'
+
+describe "ENV.key?" do
+ it_behaves_like :env_include, :key?
+end
+
+describe "ENV.key" do
+ it_behaves_like :env_key, :key
+end
diff --git a/spec/ruby/core/env/keys_spec.rb b/spec/ruby/core/env/keys_spec.rb
new file mode 100644
index 0000000000..3699b2c225
--- /dev/null
+++ b/spec/ruby/core/env/keys_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe "ENV.keys" do
+
+ it "returns all the keys" do
+ ENV.keys.sort.should == ENV.to_hash.keys.sort
+ end
+
+ it "returns the keys in the locale encoding" do
+ ENV.keys.each do |key|
+ key.encoding.should == Encoding.find('locale')
+ end
+ end
+end
diff --git a/spec/ruby/core/env/length_spec.rb b/spec/ruby/core/env/length_spec.rb
new file mode 100644
index 0000000000..536af9edf5
--- /dev/null
+++ b/spec/ruby/core/env/length_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/length'
+
+describe "ENV.length" do
+ it_behaves_like :env_length, :length
+end
diff --git a/spec/ruby/core/env/member_spec.rb b/spec/ruby/core/env/member_spec.rb
new file mode 100644
index 0000000000..9119022ae5
--- /dev/null
+++ b/spec/ruby/core/env/member_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/include'
+
+describe "ENV.member?" do
+ it_behaves_like :env_include, :member?
+end
diff --git a/spec/ruby/core/env/rassoc_spec.rb b/spec/ruby/core/env/rassoc_spec.rb
new file mode 100644
index 0000000000..4b839c15c7
--- /dev/null
+++ b/spec/ruby/core/env/rassoc_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+
+describe "ENV.rassoc" do
+ after :each do
+ ENV.delete("foo")
+ end
+
+ it "returns an array of the key and value of the environment variable with the given value" do
+ ENV["foo"] = "bar"
+ ENV.rassoc("bar").should == ["foo", "bar"]
+ end
+
+ it "returns nil if no environment variable with the given value exists" do
+ ENV.rassoc("bar").should == nil
+ end
+
+ it "returns the value element coerced with #to_str" do
+ ENV["foo"] = "bar"
+ v = mock('value')
+ v.should_receive(:to_str).and_return("bar")
+ ENV.rassoc(v).should == ["foo", "bar"]
+ end
+end
diff --git a/spec/ruby/core/env/rehash_spec.rb b/spec/ruby/core/env/rehash_spec.rb
new file mode 100644
index 0000000000..e724feaa39
--- /dev/null
+++ b/spec/ruby/core/env/rehash_spec.rb
@@ -0,0 +1 @@
+require_relative '../../spec_helper'
diff --git a/spec/ruby/core/env/reject_spec.rb b/spec/ruby/core/env/reject_spec.rb
new file mode 100644
index 0000000000..409efa1385
--- /dev/null
+++ b/spec/ruby/core/env/reject_spec.rb
@@ -0,0 +1,77 @@
+require_relative '../../spec_helper'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "ENV.reject!" do
+ it "rejects entries based on key" do
+ ENV["foo"] = "bar"
+ ENV.reject! { |k, v| k == "foo" }
+ ENV["foo"].should == nil
+ end
+
+ it "rejects entries based on value" do
+ ENV["foo"] = "bar"
+ ENV.reject! { |k, v| v == "bar" }
+ ENV["foo"].should == nil
+ end
+
+ it "returns itself or nil" do
+ ENV.reject! { false }.should == nil
+ ENV["foo"] = "bar"
+ ENV.reject! { |k, v| k == "foo" }.should == ENV
+ ENV["foo"].should == nil
+ end
+
+ it "returns an Enumerator if called without a block" do
+ ENV.reject!.should be_an_instance_of(Enumerator)
+ end
+
+ it "doesn't raise if empty" do
+ orig = ENV.to_hash
+ begin
+ ENV.clear
+ lambda { ENV.reject! }.should_not raise_error(LocalJumpError)
+ ensure
+ ENV.replace orig
+ end
+ end
+
+ it_behaves_like :enumeratorized_with_origin_size, :reject!, ENV
+end
+
+describe "ENV.reject" do
+ it "rejects entries based on key" do
+ ENV["foo"] = "bar"
+ e = ENV.reject { |k, v| k == "foo" }
+ e["foo"].should == nil
+ ENV["foo"].should == "bar"
+ ENV["foo"] = nil
+ end
+
+ it "rejects entries based on value" do
+ ENV["foo"] = "bar"
+ e = ENV.reject { |k, v| v == "bar" }
+ e["foo"].should == nil
+ ENV["foo"].should == "bar"
+ ENV["foo"] = nil
+ end
+
+ it "returns a Hash" do
+ ENV.reject { false }.should be_kind_of(Hash)
+ end
+
+ it "returns an Enumerator if called without a block" do
+ ENV.reject.should be_an_instance_of(Enumerator)
+ end
+
+ it "doesn't raise if empty" do
+ orig = ENV.to_hash
+ begin
+ ENV.clear
+ lambda { ENV.reject }.should_not raise_error(LocalJumpError)
+ ensure
+ ENV.replace orig
+ end
+ end
+
+ it_behaves_like :enumeratorized_with_origin_size, :reject, ENV
+end
diff --git a/spec/ruby/core/env/replace_spec.rb b/spec/ruby/core/env/replace_spec.rb
new file mode 100644
index 0000000000..8837deea4a
--- /dev/null
+++ b/spec/ruby/core/env/replace_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "ENV.replace" do
+
+ it "replaces ENV with a Hash" do
+ ENV["foo"] = "bar"
+ e = ENV.reject { |k, v| k == "foo" }
+ e["baz"] = "bam"
+ ENV.replace e
+ ENV["foo"].should == nil
+ ENV["baz"].should == "bam"
+ ENV.delete "baz"
+ end
+
+end
diff --git a/spec/ruby/core/env/select_spec.rb b/spec/ruby/core/env/select_spec.rb
new file mode 100644
index 0000000000..9ccd27f3e7
--- /dev/null
+++ b/spec/ruby/core/env/select_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "ENV.select!" do
+ it "removes environment variables for which the block returns true" do
+ ENV["foo"] = "bar"
+ ENV.select! { |k, v| k != "foo" }
+ ENV["foo"].should == nil
+ end
+
+ it "returns self if any changes were made" do
+ ENV["foo"] = "bar"
+ ENV.select! { |k, v| k != "foo" }.should == ENV
+ end
+
+ it "returns nil if no changes were made" do
+ ENV.select! { true }.should == nil
+ end
+
+ it "returns an Enumerator if called without a block" do
+ ENV.select!.should be_an_instance_of(Enumerator)
+ end
+
+ it_behaves_like :enumeratorized_with_origin_size, :select!, ENV
+end
+
+describe "ENV.select" do
+ it "returns a Hash of names and values for which block return true" do
+ ENV["foo"] = "bar"
+ ENV.select { |k, v| k == "foo" }.should == {"foo" => "bar"}
+ ENV.delete "foo"
+ end
+
+ it "returns an Enumerator when no block is given" do
+ ENV.select.should be_an_instance_of(Enumerator)
+ end
+
+ it_behaves_like :enumeratorized_with_origin_size, :select, ENV
+end
diff --git a/spec/ruby/core/env/shared/each.rb b/spec/ruby/core/env/shared/each.rb
new file mode 100644
index 0000000000..d6cbc93f9a
--- /dev/null
+++ b/spec/ruby/core/env/shared/each.rb
@@ -0,0 +1,65 @@
+require_relative '../../enumerable/shared/enumeratorized'
+
+describe :env_each, shared: true do
+ it "returns each pair" do
+ orig = ENV.to_hash
+ e = []
+ begin
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "boo"
+ ENV.send(@method) { |k, v| e << [k, v] }
+ e.should include(["foo", "bar"])
+ e.should include(["baz", "boo"])
+ ensure
+ ENV.replace orig
+ end
+ end
+
+ it "returns an Enumerator if called without a block" do
+ ENV.send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ before :all do
+ @object = ENV
+ end
+ it_should_behave_like :enumeratorized_with_origin_size
+
+ with_feature :encoding do
+ describe "with encoding" do
+ before :each do
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+
+ Encoding.default_external = Encoding::ASCII_8BIT
+
+ @locale_encoding = Encoding.find "locale"
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+ end
+
+ it "uses the locale encoding when Encoding.default_internal is nil" do
+ Encoding.default_internal = nil
+
+ ENV.send(@method) do |key, value|
+ key.encoding.should equal(@locale_encoding)
+ value.encoding.should equal(@locale_encoding)
+ end
+ end
+
+ it "transcodes from the locale encoding to Encoding.default_internal if set" do
+ Encoding.default_internal = internal = Encoding::IBM437
+
+ ENV.send(@method) do |key, value|
+ key.encoding.should equal(internal)
+ if value.ascii_only?
+ value.encoding.should equal(internal)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/env/shared/include.rb b/spec/ruby/core/env/shared/include.rb
new file mode 100644
index 0000000000..8d8311dcf2
--- /dev/null
+++ b/spec/ruby/core/env/shared/include.rb
@@ -0,0 +1,11 @@
+describe :env_include, shared: true do
+ it "returns true if ENV has the key" do
+ ENV["foo"] = "bar"
+ ENV.send(@method, "foo").should == true
+ ENV.delete "foo"
+ end
+
+ it "returns false if ENV doesn't include the key" do
+ ENV.send(@method, "should_never_be_set").should == false
+ end
+end
diff --git a/spec/ruby/core/env/shared/key.rb b/spec/ruby/core/env/shared/key.rb
new file mode 100644
index 0000000000..c40a56093e
--- /dev/null
+++ b/spec/ruby/core/env/shared/key.rb
@@ -0,0 +1,13 @@
+describe :env_key, shared: true do
+ it "needs to be reviewed for completeness"
+
+ it "returns the index associated with the passed value" do
+ ENV["foo"] = "bar"
+ ENV.send(@method, "bar").should == "foo"
+ ENV.delete "foo"
+ end
+
+ it "returns nil if the passed value is not found" do
+ ENV.send(@method, "should_never_be_set").should be_nil
+ end
+end
diff --git a/spec/ruby/core/env/shared/length.rb b/spec/ruby/core/env/shared/length.rb
new file mode 100644
index 0000000000..6d788a3f4a
--- /dev/null
+++ b/spec/ruby/core/env/shared/length.rb
@@ -0,0 +1,13 @@
+describe :env_length, shared: true do
+ it "returns the number of ENV entries" do
+ orig = ENV.to_hash
+ begin
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "boo"
+ ENV.send(@method).should == 2
+ ensure
+ ENV.replace orig
+ end
+ end
+end
diff --git a/spec/ruby/core/env/shared/store.rb b/spec/ruby/core/env/shared/store.rb
new file mode 100644
index 0000000000..4949ca8c73
--- /dev/null
+++ b/spec/ruby/core/env/shared/store.rb
@@ -0,0 +1,56 @@
+describe :env_store, shared: true do
+ after :each do
+ ENV.delete("foo")
+ end
+
+ it "sets the environment variable to the given value" do
+ ENV.send(@method, "foo", "bar")
+ ENV["foo"].should == "bar"
+ end
+
+ it "returns the value" do
+ value = "bar"
+ ENV.send(@method, "foo", value).should equal(value)
+ end
+
+ it "deletes the environment variable when the value is nil" do
+ ENV["foo"] = "bar"
+ ENV.send(@method, "foo", nil)
+ ENV.key?("foo").should be_false
+ end
+
+ it "coerces the key argument with #to_str" do
+ k = mock("key")
+ k.should_receive(:to_str).and_return("foo")
+ ENV.send(@method, k, "bar")
+ ENV["foo"].should == "bar"
+ end
+
+ it "coerces the value argument with #to_str" do
+ v = mock("value")
+ v.should_receive(:to_str).and_return("bar")
+ ENV.send(@method, "foo", v)
+ ENV["foo"].should == "bar"
+ end
+
+ it "raises TypeError when the key is not coercible to String" do
+ lambda { ENV.send(@method, Object.new, "bar") }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError when the value is not coercible to String" do
+ lambda { ENV.send(@method, "foo", Object.new) }.should raise_error(TypeError)
+ end
+
+ it "raises Errno::EINVAL when the key contains the '=' character" do
+ lambda { ENV.send(@method, "foo=", "bar") }.should raise_error(Errno::EINVAL)
+ end
+
+ it "raises Errno::EINVAL when the key is an empty string" do
+ lambda { ENV.send(@method, "", "bar") }.should raise_error(Errno::EINVAL)
+ end
+
+ it "does nothing when the key is not a valid environment variable key and the value is nil" do
+ ENV.send(@method, "foo=", nil)
+ ENV.key?("foo=").should be_false
+ end
+end
diff --git a/spec/ruby/core/env/shared/to_hash.rb b/spec/ruby/core/env/shared/to_hash.rb
new file mode 100644
index 0000000000..254054c14d
--- /dev/null
+++ b/spec/ruby/core/env/shared/to_hash.rb
@@ -0,0 +1,22 @@
+describe :env_to_hash, shared: true do
+ it "returns the ENV as a hash" do
+ ENV["foo"] = "bar"
+ h = ENV.send(@method)
+ h.should be_an_instance_of(Hash)
+ h["foo"].should == "bar"
+ ENV.delete "foo"
+ end
+
+ it "uses the locale encoding for keys" do
+ ENV.send(@method).keys.all? {|k| k.encoding == Encoding.find('locale') }.should be_true
+ end
+
+ it "uses the locale encoding for values" do
+ ENV.send(@method).values.all? {|v| v.encoding == Encoding.find('locale') }.should be_true
+ end
+
+ it "duplicates the ENV when converting to a Hash" do
+ h = ENV.send(@method)
+ h.should_not equal ENV
+ end
+end
diff --git a/spec/ruby/core/env/shared/value.rb b/spec/ruby/core/env/shared/value.rb
new file mode 100644
index 0000000000..d9ee90f12d
--- /dev/null
+++ b/spec/ruby/core/env/shared/value.rb
@@ -0,0 +1,11 @@
+describe :env_value, shared: true do
+ it "returns true if ENV has the value" do
+ ENV["foo"] = "bar"
+ ENV.send(@method, "bar").should == true
+ ENV["foo"] = nil
+ end
+
+ it "returns false if ENV doesn't have the value" do
+ ENV.send(@method, "this_value_should_never_exist").should == false
+ end
+end
diff --git a/spec/ruby/core/env/shift_spec.rb b/spec/ruby/core/env/shift_spec.rb
new file mode 100644
index 0000000000..c5ecc3641e
--- /dev/null
+++ b/spec/ruby/core/env/shift_spec.rb
@@ -0,0 +1,59 @@
+require_relative '../../spec_helper'
+
+describe "ENV.shift" do
+ it "returns a pair and deletes it" do
+ ENV.empty?.should == false
+ orig = ENV.to_hash
+ begin
+ pair = ENV.shift
+ ENV.has_key?(pair.first).should == false
+ ensure
+ ENV.replace orig
+ end
+ ENV.has_key?(pair.first).should == true
+ end
+
+ it "returns nil if ENV.empty?" do
+ orig = ENV.to_hash
+ begin
+ ENV.clear
+ ENV.shift.should == nil
+ ensure
+ ENV.replace orig
+ end
+ end
+end
+
+with_feature :encoding do
+ describe "ENV.shift" do
+ before :each do
+ @orig = ENV.to_hash
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+
+ Encoding.default_external = Encoding::ASCII_8BIT
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+ ENV.replace @orig
+ end
+
+ it "uses the locale encoding if Encoding.default_internal is nil" do
+ Encoding.default_internal = nil
+
+ pair = ENV.shift
+ pair.first.encoding.should equal(Encoding.find("locale"))
+ pair.last.encoding.should equal(Encoding.find("locale"))
+ end
+
+ it "transcodes from the locale encoding to Encoding.default_internal if set" do
+ Encoding.default_internal = Encoding::IBM437
+
+ pair = ENV.shift
+ pair.first.encoding.should equal(Encoding::IBM437)
+ pair.last.encoding.should equal(Encoding::IBM437)
+ end
+ end
+end
diff --git a/spec/ruby/core/env/size_spec.rb b/spec/ruby/core/env/size_spec.rb
new file mode 100644
index 0000000000..f050e9e5a9
--- /dev/null
+++ b/spec/ruby/core/env/size_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/length'
+
+describe "ENV.size" do
+ it_behaves_like :env_length, :size
+end
diff --git a/spec/ruby/core/env/store_spec.rb b/spec/ruby/core/env/store_spec.rb
new file mode 100644
index 0000000000..b4700e0a96
--- /dev/null
+++ b/spec/ruby/core/env/store_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/store'
+
+describe "ENV.store" do
+ it_behaves_like :env_store, :store
+end
diff --git a/spec/ruby/core/env/to_a_spec.rb b/spec/ruby/core/env/to_a_spec.rb
new file mode 100644
index 0000000000..ed290a48a5
--- /dev/null
+++ b/spec/ruby/core/env/to_a_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+describe "ENV.to_a" do
+
+ it "returns the ENV as an array" do
+ ENV["foo"] = "bar"
+ a = ENV.to_a
+ a.is_a?(Array).should == true
+ a.find { |e| e.first == "foo" }.should == ["foo", "bar"]
+ ENV.delete "foo"
+ end
+
+ it "returns the entries in the locale encoding" do
+ ENV.to_a.each do |key, value|
+ key.encoding.should == Encoding.find('locale')
+ value.encoding.should == Encoding.find('locale')
+ end
+ end
+end
diff --git a/spec/ruby/core/env/to_h_spec.rb b/spec/ruby/core/env/to_h_spec.rb
new file mode 100644
index 0000000000..f6c796b4d6
--- /dev/null
+++ b/spec/ruby/core/env/to_h_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_hash'
+
+describe "ENV.to_hash" do
+ it_behaves_like :env_to_hash, :to_h
+
+ ruby_version_is "2.6" do
+ it "converts [key, value] pairs returned by the block to a hash" do
+ orig = ENV.to_hash
+ begin
+ ENV.replace "a" => "b", "c" => "d"
+ i = 0
+ ENV.to_h {|k, v| [k.to_sym, v.upcase]}.should == {a:"B", c:"D"}
+ ensure
+ ENV.replace orig
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/env/to_hash_spec.rb b/spec/ruby/core/env/to_hash_spec.rb
new file mode 100644
index 0000000000..4de0e077bb
--- /dev/null
+++ b/spec/ruby/core/env/to_hash_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_hash'
+
+describe "ENV.to_hash" do
+ it_behaves_like :env_to_hash, :to_hash
+end
diff --git a/spec/ruby/core/env/to_s_spec.rb b/spec/ruby/core/env/to_s_spec.rb
new file mode 100644
index 0000000000..0bd92cf217
--- /dev/null
+++ b/spec/ruby/core/env/to_s_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "ENV.to_s" do
+ it "returns \"ENV\"" do
+ ENV.to_s.should == "ENV"
+ end
+end
diff --git a/spec/ruby/core/env/update_spec.rb b/spec/ruby/core/env/update_spec.rb
new file mode 100644
index 0000000000..9f8775138a
--- /dev/null
+++ b/spec/ruby/core/env/update_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+
+describe "ENV.update" do
+
+ it "adds the parameter hash to ENV" do
+ ENV["foo"].should == nil
+ ENV.update "foo" => "bar"
+ ENV["foo"].should == "bar"
+ ENV.delete "foo"
+ end
+
+ it "yields key, the old value and the new value when replacing entries" do
+ ENV.update "foo" => "bar"
+ ENV["foo"].should == "bar"
+ ENV.update("foo" => "boo") do |key, old, new|
+ key.should == "foo"
+ old.should == "bar"
+ new.should == "boo"
+ "rab"
+ end
+ ENV["foo"].should == "rab"
+ ENV.delete "foo"
+ end
+
+end
diff --git a/spec/ruby/core/env/value_spec.rb b/spec/ruby/core/env/value_spec.rb
new file mode 100644
index 0000000000..906e86ab39
--- /dev/null
+++ b/spec/ruby/core/env/value_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/value'
+
+describe "ENV.value?" do
+ it_behaves_like :env_value, :value?
+end
diff --git a/spec/ruby/core/env/values_at_spec.rb b/spec/ruby/core/env/values_at_spec.rb
new file mode 100644
index 0000000000..906d8b01f2
--- /dev/null
+++ b/spec/ruby/core/env/values_at_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "ENV.values_at" do
+
+ it "returns an array of the values referenced by the parameters as keys" do
+ ENV["foo"] = "oof"
+ ENV["bar"] = "rab"
+ ENV.values_at.should == []
+ ENV.values_at("bar", "foo").should == ["rab", "oof"]
+ ENV.delete "foo"
+ ENV.delete "bar"
+ end
+
+ it "uses the locale encoding" do
+ ENV.values_at(ENV.keys.first).first.encoding.should == Encoding.find('locale')
+ end
+end
diff --git a/spec/ruby/core/env/values_spec.rb b/spec/ruby/core/env/values_spec.rb
new file mode 100644
index 0000000000..2e4e69c23b
--- /dev/null
+++ b/spec/ruby/core/env/values_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+
+describe "ENV.values" do
+
+ it "returns an array of the values" do
+ orig = ENV.to_hash
+ begin
+ ENV.replace "a" => "b", "c" => "d"
+ a = ENV.values
+ a.sort.should == ["b", "d"]
+ ensure
+ ENV.replace orig
+ end
+ end
+
+ it "uses the locale encoding" do
+ ENV.values.each do |value|
+ value.encoding.should == Encoding.find('locale')
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/args_spec.rb b/spec/ruby/core/exception/args_spec.rb
new file mode 100644
index 0000000000..005c2dd198
--- /dev/null
+++ b/spec/ruby/core/exception/args_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "NoMethodError#args" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/arguments_spec.rb b/spec/ruby/core/exception/arguments_spec.rb
new file mode 100644
index 0000000000..0b283e9a54
--- /dev/null
+++ b/spec/ruby/core/exception/arguments_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "ArgumentError" do
+ it "is a subclass of StandardError" do
+ StandardError.should be_ancestor_of(ArgumentError)
+ end
+
+ it "gives its own class name as message if it has no message" do
+ ArgumentError.new.message.should == "ArgumentError"
+ end
+end
diff --git a/spec/ruby/core/exception/backtrace_locations_spec.rb b/spec/ruby/core/exception/backtrace_locations_spec.rb
new file mode 100644
index 0000000000..86eb9d3413
--- /dev/null
+++ b/spec/ruby/core/exception/backtrace_locations_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Exception#backtrace_locations" do
+ before :each do
+ @backtrace = ExceptionSpecs::Backtrace.backtrace_locations
+ end
+
+ it "returns nil if no backtrace was set" do
+ Exception.new.backtrace_locations.should be_nil
+ end
+
+ it "returns an Array" do
+ @backtrace.should be_an_instance_of(Array)
+ end
+
+ it "sets each element to a Thread::Backtrace::Location" do
+ @backtrace.each {|l| l.should be_an_instance_of(Thread::Backtrace::Location)}
+ end
+
+ it "produces a backtrace for an exception captured using $!" do
+ exception = begin
+ raise
+ rescue RuntimeError
+ $!
+ end
+
+ exception.backtrace_locations.first.path.should =~ /backtrace_locations_spec/
+ end
+
+ it "returns an Array that can be updated" do
+ begin
+ raise
+ rescue RuntimeError => e
+ e.backtrace_locations.unshift "backtrace first"
+ e.backtrace_locations[0].should == "backtrace first"
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/backtrace_spec.rb b/spec/ruby/core/exception/backtrace_spec.rb
new file mode 100644
index 0000000000..70c75bda1e
--- /dev/null
+++ b/spec/ruby/core/exception/backtrace_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Exception#backtrace" do
+ before :each do
+ @backtrace = ExceptionSpecs::Backtrace.backtrace
+ end
+
+ it "returns nil if no backtrace was set" do
+ Exception.new.backtrace.should be_nil
+ end
+
+ it "returns an Array" do
+ @backtrace.should be_an_instance_of(Array)
+ end
+
+ it "sets each element to a String" do
+ @backtrace.each {|l| l.should be_an_instance_of(String)}
+ end
+
+ it "includes the filename of the location where self raised in the first element" do
+ @backtrace.first.should =~ /common\.rb/
+ end
+
+ it "includes the line number of the location where self raised in the first element" do
+ @backtrace.first.should =~ /:7:in /
+ end
+
+ it "includes the name of the method from where self raised in the first element" do
+ @backtrace.first.should =~ /in `backtrace'/
+ end
+
+ it "includes the filename of the location immediately prior to where self raised in the second element" do
+ @backtrace[1].should =~ /backtrace_spec\.rb/
+ end
+
+ it "includes the line number of the location immediately prior to where self raised in the second element" do
+ @backtrace[1].should =~ /:6(:in )?/
+ end
+
+ it "contains lines of the same format for each prior position in the stack" do
+ @backtrace[2..-1].each do |line|
+ # This regexp is deliberately imprecise to account for the need to abstract out
+ # the paths of the included mspec files and the desire to avoid specifying in any
+ # detail what the in `...' portion looks like.
+ line.should =~ /^[^ ]+\:\d+(:in `[^`]+')?$/
+ end
+ end
+
+ it "produces a backtrace for an exception captured using $!" do
+ exception = begin
+ raise
+ rescue RuntimeError
+ $!
+ end
+
+ exception.backtrace.first.should =~ /backtrace_spec/
+ end
+
+ it "returns an Array that can be updated" do
+ begin
+ raise
+ rescue RuntimeError => e
+ e.backtrace.unshift "backtrace first"
+ e.backtrace[0].should == "backtrace first"
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/case_compare_spec.rb b/spec/ruby/core/exception/case_compare_spec.rb
new file mode 100644
index 0000000000..a4c9eaa6bf
--- /dev/null
+++ b/spec/ruby/core/exception/case_compare_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "SystemCallError.===" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/cause_spec.rb b/spec/ruby/core/exception/cause_spec.rb
new file mode 100644
index 0000000000..736ff1a046
--- /dev/null
+++ b/spec/ruby/core/exception/cause_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+describe "Exception#cause" do
+ it "returns the active exception when an exception is raised" do
+ begin
+ raise Exception, "the cause"
+ rescue Exception
+ begin
+ raise RuntimeError, "the consequence"
+ rescue RuntimeError => e
+ e.should be_an_instance_of(RuntimeError)
+ e.message.should == "the consequence"
+
+ e.cause.should be_an_instance_of(Exception)
+ e.cause.message.should == "the cause"
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/destination_encoding_name_spec.rb b/spec/ruby/core/exception/destination_encoding_name_spec.rb
new file mode 100644
index 0000000000..b6ffff8c9c
--- /dev/null
+++ b/spec/ruby/core/exception/destination_encoding_name_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Encoding::UndefinedConversionError#destination_encoding_name" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "Encoding::InvalidByteSequenceError#destination_encoding_name" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/destination_encoding_spec.rb b/spec/ruby/core/exception/destination_encoding_spec.rb
new file mode 100644
index 0000000000..c3ad0342a1
--- /dev/null
+++ b/spec/ruby/core/exception/destination_encoding_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Encoding::UndefinedConversionError#destination_encoding" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "Encoding::InvalidByteSequenceError#destination_encoding" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/dup_spec.rb b/spec/ruby/core/exception/dup_spec.rb
new file mode 100644
index 0000000000..3ad1fc063b
--- /dev/null
+++ b/spec/ruby/core/exception/dup_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Exception#dup" do
+ before :each do
+ @obj = ExceptionSpecs::InitializeException.new("my exception")
+ end
+
+ it "calls #initialize_copy on the new instance" do
+ dup = @obj.dup
+ ScratchPad.recorded.should_not == @obj.object_id
+ ScratchPad.recorded.should == dup.object_id
+ end
+
+ it "copies instance variables" do
+ dup = @obj.dup
+ dup.ivar.should == 1
+ end
+
+ it "does not copy singleton methods" do
+ def @obj.special() :the_one end
+ dup = @obj.dup
+ lambda { dup.special }.should raise_error(NameError)
+ end
+
+ it "does not copy modules included in the singleton class" do
+ class << @obj
+ include ExceptionSpecs::ExceptionModule
+ end
+
+ dup = @obj.dup
+ lambda { dup.repr }.should raise_error(NameError)
+ end
+
+ it "does not copy constants defined in the singleton class" do
+ class << @obj
+ CLONE = :clone
+ end
+
+ dup = @obj.dup
+ lambda { class << dup; CLONE; end }.should raise_error(NameError)
+ end
+
+ it "does copy the message" do
+ @obj.dup.message.should == @obj.message
+ end
+
+ it "does copy the backtrace" do
+ begin
+ # Explicitly raise so a backtrace is associated with the exception.
+ # It's tempting to call `set_backtrace` instead, but that complicates
+ # the test because it might affect other state (e.g., instance variables)
+ # on some implementations.
+ raise ExceptionSpecs::InitializeException.new("my exception")
+ rescue => e
+ @obj = e
+ end
+
+ @obj.dup.backtrace.should == @obj.backtrace
+ end
+end
diff --git a/spec/ruby/core/exception/equal_value_spec.rb b/spec/ruby/core/exception/equal_value_spec.rb
new file mode 100644
index 0000000000..7f2065511a
--- /dev/null
+++ b/spec/ruby/core/exception/equal_value_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Exception#==" do
+ it "returns true if both exceptions are the same object" do
+ e = ArgumentError.new
+ e.should == e
+ end
+
+ it "returns true if one exception is the dup'd copy of the other" do
+ e = ArgumentError.new
+ e.should == e.dup
+ end
+
+ it "returns true if both exceptions have the same class, no message, and no backtrace" do
+ RuntimeError.new.should == RuntimeError.new
+ end
+
+ it "returns true if both exceptions have the same class, the same message, and no backtrace" do
+ TypeError.new("message").should == TypeError.new("message")
+ end
+
+ it "returns true if both exceptions have the same class, the same message, and the same backtrace" do
+ one = TypeError.new("message")
+ one.set_backtrace [File.dirname(__FILE__)]
+ two = TypeError.new("message")
+ two.set_backtrace [File.dirname(__FILE__)]
+ one.should == two
+ end
+
+ it "returns false if the two exceptions inherit from Exception but have different classes" do
+ one = RuntimeError.new("message")
+ one.set_backtrace [File.dirname(__FILE__)]
+ one.should be_kind_of(Exception)
+ two = TypeError.new("message")
+ two.set_backtrace [File.dirname(__FILE__)]
+ two.should be_kind_of(Exception)
+ one.should_not == two
+ end
+
+ it "returns true if the two objects subclass Exception and have the same message and backtrace" do
+ one = ExceptionSpecs::UnExceptional.new
+ two = ExceptionSpecs::UnExceptional.new
+ one.message.should == two.message
+ two.backtrace.should == two.backtrace
+ one.should == two
+ end
+
+ it "returns false if the argument is not an Exception" do
+ ArgumentError.new.should_not == String.new
+ end
+
+ it "returns false if the two exceptions differ only in their backtrace" do
+ one = RuntimeError.new("message")
+ one.set_backtrace [File.dirname(__FILE__)]
+ two = RuntimeError.new("message")
+ two.set_backtrace nil
+ one.should_not == two
+ end
+
+ it "returns false if the two exceptions differ only in their message" do
+ one = RuntimeError.new("message")
+ one.set_backtrace [File.dirname(__FILE__)]
+ two = RuntimeError.new("message2")
+ two.set_backtrace [File.dirname(__FILE__)]
+ one.should_not == two
+ end
+end
diff --git a/spec/ruby/core/exception/errno_spec.rb b/spec/ruby/core/exception/errno_spec.rb
new file mode 100644
index 0000000000..9e0bf0086a
--- /dev/null
+++ b/spec/ruby/core/exception/errno_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "SystemCallError#errno" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "Errno::EINVAL.new" do
+ it "can be called with no arguments" do
+ exc = Errno::EINVAL.new
+ exc.should be_an_instance_of(Errno::EINVAL)
+ exc.errno.should == Errno::EINVAL::Errno
+ exc.message.should == "Invalid argument"
+ end
+
+ it "accepts an optional custom message" do
+ exc = Errno::EINVAL.new('custom message')
+ exc.should be_an_instance_of(Errno::EINVAL)
+ exc.errno.should == Errno::EINVAL::Errno
+ exc.message.should == "Invalid argument - custom message"
+ end
+
+ it "accepts an optional custom message and location" do
+ exc = Errno::EINVAL.new('custom message', 'location')
+ exc.should be_an_instance_of(Errno::EINVAL)
+ exc.errno.should == Errno::EINVAL::Errno
+ exc.message.should == "Invalid argument @ location - custom message"
+ end
+end
+
+describe "Errno::EMFILE" do
+ it "can be subclassed" do
+ ExceptionSpecs::EMFILESub = Class.new(Errno::EMFILE)
+ exc = ExceptionSpecs::EMFILESub.new
+ exc.should be_an_instance_of(ExceptionSpecs::EMFILESub)
+ end
+end
+
+describe "Errno::EAGAIN" do
+ # From http://jira.codehaus.org/browse/JRUBY-4747
+ it "is the same class as Errno::EWOULDBLOCK if they represent the same errno value" do
+ if Errno::EAGAIN::Errno == Errno::EWOULDBLOCK::Errno
+ Errno::EAGAIN.should == Errno::EWOULDBLOCK
+ else
+ Errno::EAGAIN.should_not == Errno::EWOULDBLOCK
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/error_bytes_spec.rb b/spec/ruby/core/exception/error_bytes_spec.rb
new file mode 100644
index 0000000000..2a95bcfdf4
--- /dev/null
+++ b/spec/ruby/core/exception/error_bytes_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Encoding::InvalidByteSequenceError#error_bytes" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/error_char_spec.rb b/spec/ruby/core/exception/error_char_spec.rb
new file mode 100644
index 0000000000..c0256af03a
--- /dev/null
+++ b/spec/ruby/core/exception/error_char_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Encoding::UndefinedConversionError#error_char" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/exception_spec.rb b/spec/ruby/core/exception/exception_spec.rb
new file mode 100644
index 0000000000..750c0ae452
--- /dev/null
+++ b/spec/ruby/core/exception/exception_spec.rb
@@ -0,0 +1,83 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/new'
+
+describe "Exception.exception" do
+ it_behaves_like :exception_new, :exception
+end
+
+describe "Exception" do
+ it "is a Class" do
+ Exception.should be_kind_of(Class)
+ end
+
+ it "is a superclass of NoMemoryError" do
+ Exception.should be_ancestor_of(NoMemoryError)
+ end
+
+ it "is a superclass of ScriptError" do
+ Exception.should be_ancestor_of(ScriptError)
+ end
+
+ it "is a superclass of SignalException" do
+ Exception.should be_ancestor_of(SignalException)
+ end
+
+ it "is a superclass of Interrupt" do
+ SignalException.should be_ancestor_of(Interrupt)
+ end
+
+ it "is a superclass of StandardError" do
+ Exception.should be_ancestor_of(StandardError)
+ end
+
+ it "is a superclass of SystemExit" do
+ Exception.should be_ancestor_of(SystemExit)
+ end
+
+ it "is a superclass of SystemStackError" do
+ Exception.should be_ancestor_of(SystemStackError)
+ end
+
+ it "is a superclass of SecurityError" do
+ Exception.should be_ancestor_of(SecurityError)
+ end
+
+ it "is a superclass of EncodingError" do
+ Exception.should be_ancestor_of(EncodingError)
+ end
+end
+
+describe "Exception#exception" do
+ it "returns self when passed no argument" do
+ e = RuntimeError.new
+ e.should == e.exception
+ end
+
+ it "returns self when passed self as an argument" do
+ e = RuntimeError.new
+ e.should == e.exception(e)
+ end
+
+ it "returns an exception of the same class as self with the message given as argument" do
+ e = RuntimeError.new
+ e2 = e.exception("message")
+ e2.should be_an_instance_of(RuntimeError)
+ e2.message.should == "message"
+ end
+
+ class CustomArgumentError < StandardError
+ attr_reader :val
+ def initialize(val)
+ @val = val
+ end
+ end
+
+ it "returns an exception of the same class as self with the message given as argument, but without reinitializing" do
+ e = CustomArgumentError.new(:boom)
+ e2 = e.exception("message")
+ e2.should be_an_instance_of(CustomArgumentError)
+ e2.val.should == :boom
+ e2.message.should == "message"
+ end
+end
diff --git a/spec/ruby/core/exception/exit_value_spec.rb b/spec/ruby/core/exception/exit_value_spec.rb
new file mode 100644
index 0000000000..43de56af8b
--- /dev/null
+++ b/spec/ruby/core/exception/exit_value_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "LocalJumpError#exit_value" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/fixtures/common.rb b/spec/ruby/core/exception/fixtures/common.rb
new file mode 100644
index 0000000000..0ffb3ed855
--- /dev/null
+++ b/spec/ruby/core/exception/fixtures/common.rb
@@ -0,0 +1,95 @@
+module ExceptionSpecs
+ class Exceptional < Exception; end
+
+ class Backtrace
+ def self.backtrace
+ begin
+ raise # If you move this line, update backtrace_spec.rb
+ rescue RuntimeError => e
+ e.backtrace
+ end
+ end
+
+ def self.backtrace_locations
+ begin
+ raise
+ rescue RuntimeError => e
+ e.backtrace_locations
+ end
+ end
+ end
+
+ class UnExceptional < Exception
+ def backtrace
+ nil
+ end
+ def message
+ nil
+ end
+ end
+
+ class ConstructorException < Exception
+
+ def initialize
+ end
+
+ end
+
+ class OverrideToS < RuntimeError
+ def to_s
+ "this is from #to_s"
+ end
+ end
+
+ class EmptyToS < RuntimeError
+ def to_s
+ ""
+ end
+ end
+
+ class InitializeException < StandardError
+ attr_reader :ivar
+
+ def initialize(message = nil)
+ super
+ @ivar = 1
+ end
+
+ def initialize_copy(other)
+ super
+ ScratchPad.record object_id
+ end
+ end
+
+ module ExceptionModule
+ def repr
+ 1
+ end
+ end
+end
+
+module NoMethodErrorSpecs
+ class NoMethodErrorA; end
+
+ class NoMethodErrorB; end
+
+ class NoMethodErrorC;
+ protected
+ def a_protected_method;end
+ private
+ def a_private_method; end
+ end
+
+ class NoMethodErrorD; end
+
+ class InstanceException < Exception
+ end
+end
+
+class NameErrorSpecs
+ class ReceiverClass
+ def call_undefined_class_variable
+ @@doesnt_exist
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/full_message_spec.rb b/spec/ruby/core/exception/full_message_spec.rb
new file mode 100644
index 0000000000..f56282d67b
--- /dev/null
+++ b/spec/ruby/core/exception/full_message_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.5" do
+ describe "Exception#full_message" do
+ it "returns formatted string of exception using the same format that is used to print an uncaught exceptions to stderr" do
+ e = RuntimeError.new("Some runtime error")
+ e.set_backtrace(["a.rb:1", "b.rb:2"])
+
+ full_message = e.full_message
+ full_message.should include "RuntimeError"
+ full_message.should include "Some runtime error"
+ full_message.should include "a.rb:1"
+ full_message.should include "b.rb:2"
+ end
+
+ ruby_version_is "2.5.1" do
+ it "supports :highlight option and adds escape sequences to highlight some strings" do
+ e = RuntimeError.new("Some runtime error")
+
+ full_message = e.full_message(highlight: true, order: :bottom)
+ full_message.should include "\e[1mTraceback\e[m (most recent call last)"
+ full_message.should include "\e[1mSome runtime error (\e[1;4mRuntimeError\e[m\e[1m)"
+
+ full_message = e.full_message(highlight: false, order: :bottom)
+ full_message.should include "Traceback (most recent call last)"
+ full_message.should include "Some runtime error (RuntimeError)"
+ end
+
+ it "supports :order option and places the error message and the backtrace at the top or the bottom" do
+ e = RuntimeError.new("Some runtime error")
+ e.set_backtrace(["a.rb:1", "b.rb:2"])
+
+ e.full_message(order: :top, highlight: false).should =~ /a.rb:1.*b.rb:2/m
+ e.full_message(order: :bottom, highlight: false).should =~ /b.rb:2.*a.rb:1/m
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/incomplete_input_spec.rb b/spec/ruby/core/exception/incomplete_input_spec.rb
new file mode 100644
index 0000000000..b033b33f56
--- /dev/null
+++ b/spec/ruby/core/exception/incomplete_input_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Encoding::InvalidByteSequenceError#incomplete_input?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/initialize_spec.rb b/spec/ruby/core/exception/initialize_spec.rb
new file mode 100644
index 0000000000..e724feaa39
--- /dev/null
+++ b/spec/ruby/core/exception/initialize_spec.rb
@@ -0,0 +1 @@
+require_relative '../../spec_helper'
diff --git a/spec/ruby/core/exception/inspect_spec.rb b/spec/ruby/core/exception/inspect_spec.rb
new file mode 100644
index 0000000000..519ce574ae
--- /dev/null
+++ b/spec/ruby/core/exception/inspect_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Exception#inspect" do
+ it "returns '#<Exception: Exception>' when no message given" do
+ Exception.new.inspect.should == "#<Exception: Exception>"
+ end
+
+ it "includes #to_s when the result is non-empty" do
+ ExceptionSpecs::OverrideToS.new.inspect.should == "#<ExceptionSpecs::OverrideToS: this is from #to_s>"
+ end
+
+ it "returns the class name when #to_s returns an empty string" do
+ ExceptionSpecs::EmptyToS.new.inspect.should == "ExceptionSpecs::EmptyToS"
+ end
+
+ it "returns the derived class name with a subclassed Exception" do
+ ExceptionSpecs::UnExceptional.new.inspect.should == "#<ExceptionSpecs::UnExceptional: ExceptionSpecs::UnExceptional>"
+ end
+end
diff --git a/spec/ruby/core/exception/interrupt_spec.rb b/spec/ruby/core/exception/interrupt_spec.rb
new file mode 100644
index 0000000000..bc01b7b703
--- /dev/null
+++ b/spec/ruby/core/exception/interrupt_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+
+describe "Interrupt" do
+ it "is a subclass of SignalException" do
+ Interrupt.superclass.should == SignalException
+ end
+end
+
+describe "Interrupt.new" do
+ it "returns an instance of interrupt with no message given" do
+ e = Interrupt.new
+ e.signo.should == Signal.list["INT"]
+ e.signm.should == "Interrupt"
+ end
+
+ it "takes an optional message argument" do
+ e = Interrupt.new("message")
+ e.signo.should == Signal.list["INT"]
+ e.signm.should == "message"
+ end
+end
+
+describe "rescuing Interrupt" do
+ before do
+ @original_sigint_proc = Signal.trap(:INT, :SIG_DFL)
+ end
+
+ after do
+ Signal.trap(:INT, @original_sigint_proc)
+ end
+
+ it "raises an Interrupt when sent a signal SIGINT" do
+ begin
+ Process.kill :INT, Process.pid
+ sleep
+ rescue Interrupt => e
+ e.signo.should == Signal.list["INT"]
+ e.signm.should == ""
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/io_error_spec.rb b/spec/ruby/core/exception/io_error_spec.rb
new file mode 100644
index 0000000000..8dc10cc6ce
--- /dev/null
+++ b/spec/ruby/core/exception/io_error_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+
+describe "IOError" do
+ it "is a superclass of EOFError" do
+ IOError.should be_ancestor_of(EOFError)
+ end
+end
+
+describe "IO::EAGAINWaitReadable" do
+ it "combines Errno::EAGAIN and IO::WaitReadable" do
+ IO::EAGAINWaitReadable.superclass.should == Errno::EAGAIN
+ IO::EAGAINWaitReadable.ancestors.should include IO::WaitReadable
+ end
+
+ it "is the same as IO::EWOULDBLOCKWaitReadable if Errno::EAGAIN is the same as Errno::EWOULDBLOCK" do
+ if Errno::EAGAIN.equal? Errno::EWOULDBLOCK
+ IO::EAGAINWaitReadable.should equal IO::EWOULDBLOCKWaitReadable
+ else
+ IO::EAGAINWaitReadable.should_not equal IO::EWOULDBLOCKWaitReadable
+ end
+ end
+end
+
+describe "IO::EWOULDBLOCKWaitReadable" do
+ it "combines Errno::EWOULDBLOCK and IO::WaitReadable" do
+ IO::EWOULDBLOCKWaitReadable.superclass.should == Errno::EWOULDBLOCK
+ IO::EAGAINWaitReadable.ancestors.should include IO::WaitReadable
+ end
+end
+
+describe "IO::EAGAINWaitWritable" do
+ it "combines Errno::EAGAIN and IO::WaitWritable" do
+ IO::EAGAINWaitWritable.superclass.should == Errno::EAGAIN
+ IO::EAGAINWaitWritable.ancestors.should include IO::WaitWritable
+ end
+
+ it "is the same as IO::EWOULDBLOCKWaitWritable if Errno::EAGAIN is the same as Errno::EWOULDBLOCK" do
+ if Errno::EAGAIN.equal? Errno::EWOULDBLOCK
+ IO::EAGAINWaitWritable.should equal IO::EWOULDBLOCKWaitWritable
+ else
+ IO::EAGAINWaitWritable.should_not equal IO::EWOULDBLOCKWaitWritable
+ end
+ end
+end
+
+describe "IO::EWOULDBLOCKWaitWritable" do
+ it "combines Errno::EWOULDBLOCK and IO::WaitWritable" do
+ IO::EWOULDBLOCKWaitWritable.superclass.should == Errno::EWOULDBLOCK
+ IO::EAGAINWaitWritable.ancestors.should include IO::WaitWritable
+ end
+end
diff --git a/spec/ruby/core/exception/load_error_spec.rb b/spec/ruby/core/exception/load_error_spec.rb
new file mode 100644
index 0000000000..0056403e58
--- /dev/null
+++ b/spec/ruby/core/exception/load_error_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+
+describe "LoadError#path" do
+ before :each do
+ @le = LoadError.new
+ end
+
+ it "is nil when constructed directly" do
+ @le.path.should == nil
+ end
+end
+
+describe "LoadError raised by load or require" do
+ it "provides the failing path in its #path attribute" do
+ begin
+ require 'file_that_does_not_exist'
+ rescue LoadError => le
+ le.path.should == 'file_that_does_not_exist'
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/message_spec.rb b/spec/ruby/core/exception/message_spec.rb
new file mode 100644
index 0000000000..8d7476126e
--- /dev/null
+++ b/spec/ruby/core/exception/message_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Exception#message" do
+ it "returns the class name if there is no message" do
+ Exception.new.message.should == "Exception"
+ end
+
+ it "returns the message passed to #initialize" do
+ Exception.new("Ouch!").message.should == "Ouch!"
+ end
+
+ it "calls #to_s on self" do
+ exc = ExceptionSpecs::OverrideToS.new("you won't see this")
+ exc.message.should == "this is from #to_s"
+ end
+
+ context "when #backtrace is redefined" do
+ it "returns the Exception message" do
+ e = Exception.new
+ e.message.should == 'Exception'
+
+ def e.backtrace; []; end
+ e.message.should == 'Exception'
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/name_error_spec.rb b/spec/ruby/core/exception/name_error_spec.rb
new file mode 100644
index 0000000000..a5810f178f
--- /dev/null
+++ b/spec/ruby/core/exception/name_error_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+describe "NameError" do
+ it "is a superclass of NoMethodError" do
+ NameError.should be_ancestor_of(NoMethodError)
+ end
+end
+
+describe "NameError.new" do
+ it "should take optional name argument" do
+ NameError.new("msg","name").name.should == "name"
+ end
+end
diff --git a/spec/ruby/core/exception/name_spec.rb b/spec/ruby/core/exception/name_spec.rb
new file mode 100644
index 0000000000..d1def51f24
--- /dev/null
+++ b/spec/ruby/core/exception/name_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../../spec_helper'
+
+describe "NameError#name" do
+ it "returns a method name as a symbol" do
+ -> {
+ doesnt_exist
+ }.should raise_error(NameError) {|e| e.name.should == :doesnt_exist }
+ end
+
+ it "returns a constant name as a symbol" do
+ -> {
+ DoesntExist
+ }.should raise_error(NameError) {|e| e.name.should == :DoesntExist }
+ end
+
+ it "returns a constant name without namespace as a symbol" do
+ -> {
+ Object::DoesntExist
+ }.should raise_error(NameError) {|e| e.name.should == :DoesntExist }
+ end
+
+ it "returns a class variable name as a symbol" do
+ -> {
+ -> {
+ @@doesnt_exist
+ }.should complain(/class variable access from toplevel/)
+ }.should raise_error(NameError) { |e| e.name.should == :@@doesnt_exist }
+ end
+
+ it "returns the first argument passed to the method when a NameError is raised from #instance_variable_get" do
+ invalid_ivar_name = "invalid_ivar_name"
+
+ -> {
+ Object.new.instance_variable_get(invalid_ivar_name)
+ }.should raise_error(NameError) {|e| e.name.should equal(invalid_ivar_name) }
+ end
+
+ it "returns the first argument passed to the method when a NameError is raised from #class_variable_get" do
+ invalid_cvar_name = "invalid_cvar_name"
+
+ -> {
+ Object.class_variable_get(invalid_cvar_name)
+ }.should raise_error(NameError) {|e| e.name.should equal(invalid_cvar_name) }
+ end
+end
diff --git a/spec/ruby/core/exception/new_spec.rb b/spec/ruby/core/exception/new_spec.rb
new file mode 100644
index 0000000000..100dbb0a24
--- /dev/null
+++ b/spec/ruby/core/exception/new_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/new'
+
+describe "Exception.new" do
+ it_behaves_like :exception_new, :new
+end
diff --git a/spec/ruby/core/exception/no_method_error_spec.rb b/spec/ruby/core/exception/no_method_error_spec.rb
new file mode 100644
index 0000000000..7839c080a8
--- /dev/null
+++ b/spec/ruby/core/exception/no_method_error_spec.rb
@@ -0,0 +1,95 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "NoMethodError.new" do
+ it "allows passing method args" do
+ NoMethodError.new("msg","name","args").args.should == "args"
+ end
+
+ it "does not require a name" do
+ NoMethodError.new("msg").message.should == "msg"
+ end
+end
+
+describe "NoMethodError#args" do
+ it "returns an empty array if the caller method had no arguments" do
+ begin
+ NoMethodErrorSpecs::NoMethodErrorB.new.foo
+ rescue Exception => e
+ e.args.should == []
+ end
+ end
+
+ it "returns an array with the same elements as passed to the method" do
+ begin
+ a = NoMethodErrorSpecs::NoMethodErrorA.new
+ NoMethodErrorSpecs::NoMethodErrorB.new.foo(1,a)
+ rescue Exception => e
+ e.args.should == [1,a]
+ e.args[1].should equal a
+ end
+ end
+end
+
+describe "NoMethodError#message" do
+ it "for an undefined method match /undefined method/" do
+ begin
+ NoMethodErrorSpecs::NoMethodErrorD.new.foo
+ rescue Exception => e
+ e.should be_kind_of(NoMethodError)
+ end
+ end
+
+ it "for an protected method match /protected method/" do
+ begin
+ NoMethodErrorSpecs::NoMethodErrorC.new.a_protected_method
+ rescue Exception => e
+ e.should be_kind_of(NoMethodError)
+ end
+ end
+
+ it "for private method match /private method/" do
+ begin
+ NoMethodErrorSpecs::NoMethodErrorC.new.a_private_method
+ rescue Exception => e
+ e.should be_kind_of(NoMethodError)
+ e.message.match(/private method/).should_not == nil
+ end
+ end
+
+ it "calls receiver.inspect only when calling Exception#message" do
+ ScratchPad.record []
+ test_class = Class.new do
+ def inspect
+ ScratchPad << :inspect_called
+ "<inspect>"
+ end
+ end
+ instance = test_class.new
+ begin
+ instance.bar
+ rescue Exception => e
+ e.name.should == :bar
+ ScratchPad.recorded.should == []
+ e.message.should =~ /undefined method.+\bbar\b/
+ ScratchPad.recorded.should == [:inspect_called]
+ end
+ end
+
+ it "fallbacks to a simpler representation of the receiver when receiver.inspect raises an exception" do
+ test_class = Class.new do
+ def inspect
+ raise NoMethodErrorSpecs::InstanceException
+ end
+ end
+ instance = test_class.new
+ begin
+ instance.bar
+ rescue Exception => e
+ e.name.should == :bar
+ message = e.message
+ message.should =~ /undefined method.+\bbar\b/
+ message.should include test_class.inspect
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/range_error_spec.rb b/spec/ruby/core/exception/range_error_spec.rb
new file mode 100644
index 0000000000..7cfbd0f1ec
--- /dev/null
+++ b/spec/ruby/core/exception/range_error_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "RangeError" do
+ it "is a superclass of FloatDomainError" do
+ RangeError.should be_ancestor_of(FloatDomainError)
+ end
+end
diff --git a/spec/ruby/core/exception/readagain_bytes_spec.rb b/spec/ruby/core/exception/readagain_bytes_spec.rb
new file mode 100644
index 0000000000..f7e8d9d1d3
--- /dev/null
+++ b/spec/ruby/core/exception/readagain_bytes_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Encoding::InvalidByteSequenceError#readagain_bytes" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/reason_spec.rb b/spec/ruby/core/exception/reason_spec.rb
new file mode 100644
index 0000000000..6f18aaae13
--- /dev/null
+++ b/spec/ruby/core/exception/reason_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "LocalJumpError#reason" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/receiver_spec.rb b/spec/ruby/core/exception/receiver_spec.rb
new file mode 100644
index 0000000000..7c57d63c3e
--- /dev/null
+++ b/spec/ruby/core/exception/receiver_spec.rb
@@ -0,0 +1,60 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "NameError#receiver" do
+ class ::ReceiverClass
+ def call_undefined_class_variable; @@doesnt_exist end
+ end
+
+ it "returns the object that raised the exception" do
+ receiver = Object.new
+
+ -> {
+ receiver.doesnt_exist
+ }.should raise_error(NameError) {|e| e.receiver.should equal(receiver) }
+ end
+
+ it "returns the Object class when an undefined constant is called without namespace" do
+ -> {
+ DoesntExist
+ }.should raise_error(NameError) {|e| e.receiver.should equal(Object) }
+ end
+
+ it "returns a class when an undefined constant is called" do
+ -> {
+ NameErrorSpecs::ReceiverClass::DoesntExist
+ }.should raise_error(NameError) {|e| e.receiver.should equal(NameErrorSpecs::ReceiverClass) }
+ end
+
+ it "returns the Object class when an undefined class variable is called" do
+ -> {
+ -> {
+ @@doesnt_exist
+ }.should complain(/class variable access from toplevel/)
+ }.should raise_error(NameError) {|e| e.receiver.should equal(Object) }
+ end
+
+ it "returns a class when an undefined class variable is called in a subclass' namespace" do
+ -> {
+ NameErrorSpecs::ReceiverClass.new.call_undefined_class_variable
+ }.should raise_error(NameError) {|e| e.receiver.should equal(NameErrorSpecs::ReceiverClass) }
+ end
+
+ it "returns the receiver when raised from #instance_variable_get" do
+ receiver = Object.new
+
+ -> {
+ receiver.instance_variable_get("invalid_ivar_name")
+ }.should raise_error(NameError) {|e| e.receiver.should equal(receiver) }
+ end
+
+ it "returns the receiver when raised from #class_variable_get" do
+ -> {
+ Object.class_variable_get("invalid_cvar_name")
+ }.should raise_error(NameError) {|e| e.receiver.should equal(Object) }
+ end
+
+ it "raises an ArgumentError when the receiver is none" do
+ -> { NameError.new.receiver }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/exception/result_spec.rb b/spec/ruby/core/exception/result_spec.rb
new file mode 100644
index 0000000000..62247e9d85
--- /dev/null
+++ b/spec/ruby/core/exception/result_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+
+describe "StopIteration" do
+ it "is a subclass of IndexError" do
+ StopIteration.superclass.should equal(IndexError)
+ end
+end
+
+describe "StopIteration#result" do
+ before :each do
+ obj = Object.new
+ def obj.each
+ yield :yield_returned_1
+ yield :yield_returned_2
+ :method_returned
+ end
+ @enum = obj.to_enum
+ end
+
+ it "returns the method-returned-object from an Enumerator" do
+ @enum.next
+ @enum.next
+ lambda { @enum.next }.should(
+ raise_error(StopIteration) do |error|
+ error.result.should equal(:method_returned)
+ end
+ )
+ end
+end
diff --git a/spec/ruby/core/exception/script_error_spec.rb b/spec/ruby/core/exception/script_error_spec.rb
new file mode 100644
index 0000000000..e33a5d3a58
--- /dev/null
+++ b/spec/ruby/core/exception/script_error_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "ScriptError" do
+ it "is a superclass of LoadError" do
+ ScriptError.should be_ancestor_of(LoadError)
+ end
+
+ it "is a superclass of NotImplementedError" do
+ ScriptError.should be_ancestor_of(NotImplementedError)
+ end
+
+ it "is a superclass of SyntaxError" do
+ ScriptError.should be_ancestor_of(SyntaxError)
+ end
+end
diff --git a/spec/ruby/core/exception/set_backtrace_spec.rb b/spec/ruby/core/exception/set_backtrace_spec.rb
new file mode 100644
index 0000000000..4ebe574dcb
--- /dev/null
+++ b/spec/ruby/core/exception/set_backtrace_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Exception#set_backtrace" do
+ it "accepts an Array of Strings" do
+ err = RuntimeError.new
+ err.set_backtrace ["unhappy"]
+ err.backtrace.should == ["unhappy"]
+ end
+
+ it "allows the user to set the backtrace from a rescued exception" do
+ bt = ExceptionSpecs::Backtrace.backtrace
+ err = RuntimeError.new
+
+ err.set_backtrace bt
+ err.backtrace.should == bt
+ end
+
+ it "accepts an empty Array" do
+ err = RuntimeError.new
+ err.set_backtrace []
+ err.backtrace.should == []
+ end
+
+ it "accepts a String" do
+ err = RuntimeError.new
+ err.set_backtrace "unhappy"
+ err.backtrace.should == ["unhappy"]
+ end
+
+ it "accepts nil" do
+ err = RuntimeError.new
+ err.set_backtrace nil
+ err.backtrace.should be_nil
+ end
+
+ it "raises a TypeError when passed a Symbol" do
+ err = RuntimeError.new
+ lambda { err.set_backtrace :unhappy }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when the Array contains a Symbol" do
+ err = RuntimeError.new
+ lambda { err.set_backtrace ["String", :unhappy] }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when the array contains nil" do
+ err = Exception.new
+ lambda { err.set_backtrace ["String", nil] }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when the argument is a nested array" do
+ err = Exception.new
+ lambda { err.set_backtrace ["String", ["String"]] }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/exception/shared/new.rb b/spec/ruby/core/exception/shared/new.rb
new file mode 100644
index 0000000000..bcde8ee4b2
--- /dev/null
+++ b/spec/ruby/core/exception/shared/new.rb
@@ -0,0 +1,18 @@
+describe :exception_new, shared: true do
+ it "creates a new instance of Exception" do
+ Exception.should be_ancestor_of(Exception.send(@method).class)
+ end
+
+ it "sets the message of the Exception when passes a message" do
+ Exception.send(@method, "I'm broken.").message.should == "I'm broken."
+ end
+
+ it "returns 'Exception' for message when no message given" do
+ Exception.send(@method).message.should == "Exception"
+ end
+
+ it "returns the exception when it has a custom constructor" do
+ ExceptionSpecs::ConstructorException.send(@method).should be_kind_of(ExceptionSpecs::ConstructorException)
+ end
+
+end
diff --git a/spec/ruby/core/exception/signal_exception_spec.rb b/spec/ruby/core/exception/signal_exception_spec.rb
new file mode 100644
index 0000000000..c0632841d6
--- /dev/null
+++ b/spec/ruby/core/exception/signal_exception_spec.rb
@@ -0,0 +1,74 @@
+require_relative '../../spec_helper'
+
+describe "SignalException.new" do
+ it "takes a signal number as the first argument" do
+ exc = SignalException.new(Signal.list["INT"])
+ exc.signo.should == Signal.list["INT"]
+ exc.signm.should == "SIGINT"
+ exc.message.should == "SIGINT"
+ end
+
+ it "raises an exception with an invalid signal number" do
+ lambda { SignalException.new(100000) }.should raise_error(ArgumentError)
+ end
+
+ it "takes a signal name without SIG prefix as the first argument" do
+ exc = SignalException.new("INT")
+ exc.signo.should == Signal.list["INT"]
+ exc.signm.should == "SIGINT"
+ exc.message.should == "SIGINT"
+ end
+
+ it "takes a signal name with SIG prefix as the first argument" do
+ exc = SignalException.new("SIGINT")
+ exc.signo.should == Signal.list["INT"]
+ exc.signm.should == "SIGINT"
+ exc.message.should == "SIGINT"
+ end
+
+ it "raises an exception with an invalid signal name" do
+ lambda { SignalException.new("NONEXISTANT") }.should raise_error(ArgumentError)
+ end
+
+ it "takes a signal symbol without SIG prefix as the first argument" do
+ exc = SignalException.new(:INT)
+ exc.signo.should == Signal.list["INT"]
+ exc.signm.should == "SIGINT"
+ exc.message.should == "SIGINT"
+ end
+
+ it "takes a signal symbol with SIG prefix as the first argument" do
+ exc = SignalException.new(:SIGINT)
+ exc.signo.should == Signal.list["INT"]
+ exc.signm.should == "SIGINT"
+ exc.message.should == "SIGINT"
+ end
+
+ it "raises an exception with an invalid signal name" do
+ lambda { SignalException.new(:NONEXISTANT) }.should raise_error(ArgumentError)
+ end
+
+ it "takes an optional message argument with a signal number" do
+ exc = SignalException.new(Signal.list["INT"], "name")
+ exc.signo.should == Signal.list["INT"]
+ exc.signm.should == "name"
+ exc.message.should == "name"
+ end
+
+ it "raises an exception for an optional argument with a signal name" do
+ lambda { SignalException.new("INT","name") }.should raise_error(ArgumentError)
+ end
+end
+
+describe "rescuing SignalException" do
+ it "raises a SignalException when sent a signal" do
+ begin
+ Process.kill :TERM, Process.pid
+ sleep
+ rescue SignalException => e
+ e.signo.should == Signal.list["TERM"]
+ e.signm.should == "SIGTERM"
+ e.message.should == "SIGTERM"
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/signm_spec.rb b/spec/ruby/core/exception/signm_spec.rb
new file mode 100644
index 0000000000..8e3adcddae
--- /dev/null
+++ b/spec/ruby/core/exception/signm_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "SignalException#signm" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/signo_spec.rb b/spec/ruby/core/exception/signo_spec.rb
new file mode 100644
index 0000000000..2d04cd7805
--- /dev/null
+++ b/spec/ruby/core/exception/signo_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "SignalException#signo" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/source_encoding_name_spec.rb b/spec/ruby/core/exception/source_encoding_name_spec.rb
new file mode 100644
index 0000000000..bd8bc359b6
--- /dev/null
+++ b/spec/ruby/core/exception/source_encoding_name_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Encoding::UndefinedConversionError#source_encoding_name" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "Encoding::InvalidByteSequenceError#source_encoding_name" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/source_encoding_spec.rb b/spec/ruby/core/exception/source_encoding_spec.rb
new file mode 100644
index 0000000000..65ac98d791
--- /dev/null
+++ b/spec/ruby/core/exception/source_encoding_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Encoding::UndefinedConversionError#source_encoding" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "Encoding::InvalidByteSequenceError#source_encoding" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/standard_error_spec.rb b/spec/ruby/core/exception/standard_error_spec.rb
new file mode 100644
index 0000000000..1b469b5090
--- /dev/null
+++ b/spec/ruby/core/exception/standard_error_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../../spec_helper'
+
+describe "StandardError" do
+ it "is a superclass of ArgumentError" do
+ StandardError.should be_ancestor_of(ArgumentError)
+ end
+
+ it "is a superclass of IOError" do
+ StandardError.should be_ancestor_of(IOError)
+ end
+
+ it "is a superclass of IndexError" do
+ StandardError.should be_ancestor_of(IndexError)
+ end
+
+ it "is a superclass of LocalJumpError" do
+ StandardError.should be_ancestor_of(LocalJumpError)
+ end
+
+ it "is a superclass of NameError" do
+ StandardError.should be_ancestor_of(NameError)
+ end
+
+ it "is a superclass of RangeError" do
+ StandardError.should be_ancestor_of(RangeError)
+ end
+
+ it "is a superclass of RegexpError" do
+ StandardError.should be_ancestor_of(RegexpError)
+ end
+
+ it "is a superclass of RuntimeError" do
+ StandardError.should be_ancestor_of(RuntimeError)
+ end
+
+ it "is a superclass of SystemCallError" do
+ StandardError.should be_ancestor_of(SystemCallError.new("").class)
+ end
+ it "is a superclass of ThreadError" do
+ StandardError.should be_ancestor_of(ThreadError)
+ end
+
+ it "is a superclass of TypeError" do
+ StandardError.should be_ancestor_of(TypeError)
+ end
+
+ it "is a superclass of ZeroDivisionError" do
+ StandardError.should be_ancestor_of(ZeroDivisionError)
+ end
+
+ ruby_version_is '2.5' do
+ it "is a superclass of FrozenError" do
+ StandardError.should be_ancestor_of(FrozenError)
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/status_spec.rb b/spec/ruby/core/exception/status_spec.rb
new file mode 100644
index 0000000000..1609bff3a5
--- /dev/null
+++ b/spec/ruby/core/exception/status_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "SystemExit#status" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/success_spec.rb b/spec/ruby/core/exception/success_spec.rb
new file mode 100644
index 0000000000..82e3df92c6
--- /dev/null
+++ b/spec/ruby/core/exception/success_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "SystemExit#success?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/system_call_error_spec.rb b/spec/ruby/core/exception/system_call_error_spec.rb
new file mode 100644
index 0000000000..d9e303bad8
--- /dev/null
+++ b/spec/ruby/core/exception/system_call_error_spec.rb
@@ -0,0 +1,89 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "SystemCallError" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "can be subclassed" do
+ ExceptionSpecs::SCESub = Class.new(SystemCallError) do
+ def initialize
+ ScratchPad.record :initialize
+ end
+ end
+
+ exc = ExceptionSpecs::SCESub.new
+ ScratchPad.recorded.should equal(:initialize)
+ exc.should be_an_instance_of(ExceptionSpecs::SCESub)
+ end
+end
+
+describe "SystemCallError.new" do
+ it "requires at least one argument" do
+ lambda { SystemCallError.new }.should raise_error(ArgumentError)
+ end
+
+ it "accepts single Fixnum argument as errno" do
+ SystemCallError.new(-2**24).errno.should == -2**24
+ SystemCallError.new(42).errno.should == 42
+ SystemCallError.new(2**24).errno.should == 2**24
+ end
+
+ it "constructs the appropriate Errno class" do
+ # EINVAL should be more or less mortable across the platforms,
+ # so let's use it then.
+ SystemCallError.new(22).should be_kind_of(SystemCallError)
+ SystemCallError.new(22).should be_an_instance_of(Errno::EINVAL)
+ SystemCallError.new(2**28).should be_an_instance_of(SystemCallError)
+ end
+
+ it "accepts an optional custom message preceding the errno" do
+ exc = SystemCallError.new("custom message", 22)
+ exc.should be_an_instance_of(Errno::EINVAL)
+ exc.errno.should == 22
+ exc.message.should == "Invalid argument - custom message"
+ end
+
+ it "accepts an optional third argument specifying the location" do
+ exc = SystemCallError.new("custom message", 22, "location")
+ exc.should be_an_instance_of(Errno::EINVAL)
+ exc.errno.should == 22
+ exc.message.should == "Invalid argument @ location - custom message"
+ end
+
+ it "returns an arity of -1 for the initialize method" do
+ SystemCallError.instance_method(:initialize).arity.should == -1
+ end
+end
+
+describe "SystemCallError#errno" do
+ it "returns nil when no errno given" do
+ SystemCallError.new("message").errno.should == nil
+ end
+
+ it "returns the errno given as optional argument to new" do
+ SystemCallError.new("message", -2**20).errno.should == -2**20
+ SystemCallError.new("message", -1).errno.should == -1
+ SystemCallError.new("message", 0).errno.should == 0
+ SystemCallError.new("message", 1).errno.should == 1
+ SystemCallError.new("message", 42).errno.should == 42
+ SystemCallError.new("message", 2**20).errno.should == 2**20
+ end
+end
+
+describe "SystemCallError#message" do
+ it "returns the default message when no message is given" do
+ platform_is :aix do
+ SystemCallError.new(2**28).message.should =~ /Error .*occurred/i
+ end
+ platform_is_not :aix do
+ SystemCallError.new(2**28).message.should =~ /Unknown error/i
+ end
+ end
+
+ it "returns the message given as an argument to new" do
+ SystemCallError.new("message", 1).message.should =~ /message/
+ SystemCallError.new("XXX").message.should =~ /XXX/
+ end
+end
diff --git a/spec/ruby/core/exception/system_stack_error_spec.rb b/spec/ruby/core/exception/system_stack_error_spec.rb
new file mode 100644
index 0000000000..0343d2da59
--- /dev/null
+++ b/spec/ruby/core/exception/system_stack_error_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "SystemStackError" do
+ it "is a subclass of Exception" do
+ SystemStackError.superclass.should == Exception
+ end
+end
diff --git a/spec/ruby/core/exception/to_s_spec.rb b/spec/ruby/core/exception/to_s_spec.rb
new file mode 100644
index 0000000000..8570b18cfd
--- /dev/null
+++ b/spec/ruby/core/exception/to_s_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Exception#to_s" do
+ it "returns the self's name if no message is set" do
+ Exception.new.to_s.should == 'Exception'
+ ExceptionSpecs::Exceptional.new.to_s.should == 'ExceptionSpecs::Exceptional'
+ end
+
+ it "returns self's message if set" do
+ ExceptionSpecs::Exceptional.new('!!').to_s.should == '!!'
+ end
+
+ it "calls #to_s on the message" do
+ message = mock("message")
+ message.should_receive(:to_s).and_return("message")
+ ExceptionSpecs::Exceptional.new(message).to_s.should == "message"
+ end
+end
+
+describe "NameError#to_s" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/exception/uncaught_throw_error_spec.rb b/spec/ruby/core/exception/uncaught_throw_error_spec.rb
new file mode 100644
index 0000000000..57f391d755
--- /dev/null
+++ b/spec/ruby/core/exception/uncaught_throw_error_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+
+describe "UncaughtThrowError" do
+ it "is a subclass of ArgumentError" do
+ ArgumentError.should be_ancestor_of(UncaughtThrowError)
+ end
+end
+
+describe "UncaughtThrowError#tag" do
+ it "returns the object thrown" do
+ begin
+ throw :abc
+
+ rescue UncaughtThrowError => e
+ e.tag.should == :abc
+ end
+ end
+end
diff --git a/spec/ruby/core/false/and_spec.rb b/spec/ruby/core/false/and_spec.rb
new file mode 100644
index 0000000000..0b02ae62c5
--- /dev/null
+++ b/spec/ruby/core/false/and_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "FalseClass#&" do
+ it "returns false" do
+ (false & false).should == false
+ (false & true).should == false
+ (false & nil).should == false
+ (false & "").should == false
+ (false & mock('x')).should == false
+ end
+end
diff --git a/spec/ruby/core/false/dup_spec.rb b/spec/ruby/core/false/dup_spec.rb
new file mode 100644
index 0000000000..24360a9fc1
--- /dev/null
+++ b/spec/ruby/core/false/dup_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '2.4' do
+ describe "FalseClass#dup" do
+ it "returns self" do
+ false.dup.should equal(false)
+ end
+ end
+end
diff --git a/spec/ruby/core/false/falseclass_spec.rb b/spec/ruby/core/false/falseclass_spec.rb
new file mode 100644
index 0000000000..a944edd348
--- /dev/null
+++ b/spec/ruby/core/false/falseclass_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "FalseClass" do
+ it ".allocate raises a TypeError" do
+ lambda do
+ FalseClass.allocate
+ end.should raise_error(TypeError)
+ end
+
+ it ".new is undefined" do
+ lambda do
+ FalseClass.new
+ end.should raise_error(NoMethodError)
+ end
+end
diff --git a/spec/ruby/core/false/inspect_spec.rb b/spec/ruby/core/false/inspect_spec.rb
new file mode 100644
index 0000000000..4cbb55d434
--- /dev/null
+++ b/spec/ruby/core/false/inspect_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "FalseClass#inspect" do
+ it "returns the string 'false'" do
+ false.inspect.should == "false"
+ end
+end
diff --git a/spec/ruby/core/false/or_spec.rb b/spec/ruby/core/false/or_spec.rb
new file mode 100644
index 0000000000..f3ee1a3439
--- /dev/null
+++ b/spec/ruby/core/false/or_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "FalseClass#|" do
+ it "returns false if other is nil or false, otherwise true" do
+ (false | false).should == false
+ (false | true).should == true
+ (false | nil).should == false
+ (false | "").should == true
+ (false | mock('x')).should == true
+ end
+end
diff --git a/spec/ruby/core/false/to_s_spec.rb b/spec/ruby/core/false/to_s_spec.rb
new file mode 100644
index 0000000000..40853da88b
--- /dev/null
+++ b/spec/ruby/core/false/to_s_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "FalseClass#to_s" do
+ it "returns the string 'false'" do
+ false.to_s.should == "false"
+ end
+end
diff --git a/spec/ruby/core/false/xor_spec.rb b/spec/ruby/core/false/xor_spec.rb
new file mode 100644
index 0000000000..1b87b9f412
--- /dev/null
+++ b/spec/ruby/core/false/xor_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "FalseClass#^" do
+ it "returns false if other is nil or false, otherwise true" do
+ (false ^ false).should == false
+ (false ^ true).should == true
+ (false ^ nil).should == false
+ (false ^ "").should == true
+ (false ^ mock('x')).should == true
+ end
+end
diff --git a/spec/ruby/core/fiber/new_spec.rb b/spec/ruby/core/fiber/new_spec.rb
new file mode 100644
index 0000000000..734db0682e
--- /dev/null
+++ b/spec/ruby/core/fiber/new_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+
+with_feature :fiber do
+ describe "Fiber.new" do
+ it "creates a fiber from the given block" do
+ fiber = Fiber.new {}
+ fiber.resume
+ fiber.should be_an_instance_of(Fiber)
+ end
+
+ it "creates a fiber from a subclass" do
+ class MyFiber < Fiber
+ end
+ fiber = MyFiber.new {}
+ fiber.resume
+ fiber.should be_an_instance_of(MyFiber)
+ end
+
+ it "raises an ArgumentError if called without a block" do
+ lambda { Fiber.new }.should raise_error(ArgumentError)
+ end
+
+ it "does not invoke the block" do
+ invoked = false
+ fiber = Fiber.new { invoked = true }
+ invoked.should be_false
+ fiber.resume
+ end
+
+ it "closes over lexical environments" do
+ o = Object.new
+ def o.f
+ a = 1
+ f = Fiber.new { a = 2 }
+ f.resume
+ a
+ end
+ o.f.should == 2
+ end
+ end
+end
diff --git a/spec/ruby/core/fiber/resume_spec.rb b/spec/ruby/core/fiber/resume_spec.rb
new file mode 100644
index 0000000000..2f15a834d4
--- /dev/null
+++ b/spec/ruby/core/fiber/resume_spec.rb
@@ -0,0 +1,59 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/fiber/resume'
+
+with_feature :fiber do
+ describe "Fiber#resume" do
+ it_behaves_like :fiber_resume, :resume
+ end
+
+ describe "Fiber#resume" do
+ it "raises a FiberError if the Fiber tries to resume itself" do
+ fiber = Fiber.new { fiber.resume }
+ -> { fiber.resume }.should raise_error(FiberError, /double resume/)
+ end
+
+ it "returns control to the calling Fiber if called from one" do
+ fiber1 = Fiber.new { :fiber1 }
+ fiber2 = Fiber.new { fiber1.resume; :fiber2 }
+ fiber2.resume.should == :fiber2
+ end
+
+ with_feature :fork do
+ # Redmine #595
+ it "executes the ensure clause" do
+ rd, wr = IO.pipe
+
+ pid = Kernel::fork do
+ rd.close
+ f = Fiber.new do
+ begin
+ Fiber.yield
+ ensure
+ wr.write "executed"
+ end
+ end
+
+ # The apparent issue is that when Fiber.yield executes, control
+ # "leaves" the "ensure block" and so the ensure clause should run. But
+ # control really does NOT leave the ensure block when Fiber.yield
+ # executes. It merely pauses there. To require ensure to run when a
+ # Fiber is suspended then makes ensure-in-a-Fiber-context different
+ # than ensure-in-a-Thread-context and this would be very confusing.
+ f.resume
+
+ # When we execute the second #resume call, the ensure block DOES exit,
+ # the ensure clause runs. This is Ruby behavior as of 2.3.1.
+ f.resume
+
+ exit 0
+ end
+
+ wr.close
+ Process.waitpid pid
+
+ rd.read.should == "executed"
+ rd.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/fiber/yield_spec.rb b/spec/ruby/core/fiber/yield_spec.rb
new file mode 100644
index 0000000000..d002b29cf6
--- /dev/null
+++ b/spec/ruby/core/fiber/yield_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+
+with_feature :fiber do
+ describe "Fiber.yield" do
+ it "passes control to the Fiber's caller" do
+ step = 0
+ fiber = Fiber.new { step = 1; Fiber.yield; step = 2; Fiber.yield; step = 3 }
+ fiber.resume
+ step.should == 1
+ fiber.resume
+ step.should == 2
+ end
+
+ it "returns its arguments to the caller" do
+ fiber = Fiber.new { true; Fiber.yield :glark; true }
+ fiber.resume.should == :glark
+ fiber.resume
+ end
+
+ it "returns nil to the caller if given no arguments" do
+ fiber = Fiber.new { true; Fiber.yield; true }
+ fiber.resume.should be_nil
+ fiber.resume
+ end
+
+ it "returns to the Fiber the value of the #resume call that invoked it" do
+ fiber = Fiber.new { Fiber.yield.should == :caller }
+ fiber.resume
+ fiber.resume :caller
+ end
+
+ it "does not propagate or reraise a rescued exception" do
+ fiber = Fiber.new do
+ begin
+ raise "an error in a Fiber"
+ rescue
+ Fiber.yield :first
+ end
+
+ :second
+ end
+
+ fiber.resume.should == :first
+ fiber.resume.should == :second
+ end
+
+ it "raises a FiberError if called from the root Fiber" do
+ lambda{ Fiber.yield }.should raise_error(FiberError)
+ end
+ end
+end
diff --git a/spec/ruby/core/file/absolute_path_spec.rb b/spec/ruby/core/file/absolute_path_spec.rb
new file mode 100644
index 0000000000..d6f3f060c7
--- /dev/null
+++ b/spec/ruby/core/file/absolute_path_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+
+describe "File.absolute_path" do
+ before :each do
+ @abs = File.expand_path(__FILE__)
+ end
+
+ it "returns the argument if it's an absolute pathname" do
+ File.absolute_path(@abs).should == @abs
+ end
+
+ it "resolves paths relative to the current working directory" do
+ path = File.dirname(@abs)
+ Dir.chdir(path) do
+ File.absolute_path('hello.txt').should == File.join(Dir.pwd, 'hello.txt')
+ end
+ end
+
+ it "does not expand '~' to a home directory." do
+ File.absolute_path('~').should_not == File.expand_path('~')
+ end
+
+ it "does not expand '~user' to a home directory." do
+ path = File.dirname(@abs)
+ Dir.chdir(path) do
+ File.absolute_path('~user').should == File.join(Dir.pwd, '~user')
+ end
+ end
+
+ it "accepts a second argument of a directory from which to resolve the path" do
+ File.absolute_path(__FILE__, File.dirname(__FILE__)).should == @abs
+ end
+
+ it "calls #to_path on its argument" do
+ File.absolute_path(mock_to_path(@abs)).should == @abs
+ end
+end
diff --git a/spec/ruby/core/file/atime_spec.rb b/spec/ruby/core/file/atime_spec.rb
new file mode 100644
index 0000000000..d4106c0411
--- /dev/null
+++ b/spec/ruby/core/file/atime_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../../spec_helper'
+
+describe "File.atime" do
+ before :each do
+ @file = tmp('test.txt')
+ touch @file
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the last access time for the named file as a Time object" do
+ File.atime(@file)
+ File.atime(@file).should be_kind_of(Time)
+ end
+
+ platform_is :linux do
+ ## 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)
+ if supports_subseconds != 0
+ expected_time = Time.at(Time.now.to_i + 0.123456)
+ File.utime expected_time, 0, @file
+ File.atime(@file).usec.should == expected_time.usec
+ else
+ File.atime(__FILE__).usec.should == 0
+ end
+ end
+ end
+
+ it "raises an Errno::ENOENT exception if the file is not found" do
+ lambda { File.atime('a_fake_file') }.should raise_error(Errno::ENOENT)
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.atime(mock_to_path(@file))
+ end
+end
+
+describe "File#atime" do
+ before :each do
+ @name = File.expand_path(__FILE__)
+ @file = File.open(@name)
+ end
+
+ after :each do
+ @file.close rescue nil
+ end
+
+ it "returns the last access time to self" do
+ @file.atime
+ @file.atime.should be_kind_of(Time)
+ end
+end
diff --git a/spec/ruby/core/file/basename_spec.rb b/spec/ruby/core/file/basename_spec.rb
new file mode 100644
index 0000000000..671955302a
--- /dev/null
+++ b/spec/ruby/core/file/basename_spec.rb
@@ -0,0 +1,170 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+# TODO: Fix these
+describe "File.basename" do
+ it "returns the basename of a path (basic cases)" do
+ File.basename("/Some/path/to/test.txt").should == "test.txt"
+ File.basename(File.join("/tmp")).should == "tmp"
+ File.basename(File.join(*%w( g f d s a b))).should == "b"
+ File.basename("/tmp", ".*").should == "tmp"
+ File.basename("/tmp", ".c").should == "tmp"
+ File.basename("/tmp.c", ".c").should == "tmp"
+ File.basename("/tmp.c", ".*").should == "tmp"
+ File.basename("/tmp.c", ".?").should == "tmp.c"
+ File.basename("/tmp.cpp", ".*").should == "tmp"
+ File.basename("/tmp.cpp", ".???").should == "tmp.cpp"
+ File.basename("/tmp.o", ".c").should == "tmp.o"
+ File.basename(File.join("/tmp/")).should == "tmp"
+ File.basename("/").should == "/"
+ File.basename("//").should == "/"
+ File.basename("dir///base", ".*").should == "base"
+ File.basename("dir///base", ".c").should == "base"
+ File.basename("dir///base.c", ".c").should == "base"
+ File.basename("dir///base.c", ".*").should == "base"
+ File.basename("dir///base.o", ".c").should == "base.o"
+ File.basename("dir///base///").should == "base"
+ File.basename("dir//base/", ".*").should == "base"
+ File.basename("dir//base/", ".c").should == "base"
+ File.basename("dir//base.c/", ".c").should == "base"
+ File.basename("dir//base.c/", ".*").should == "base"
+ end
+
+ it "returns the last component of the filename" do
+ File.basename('a').should == 'a'
+ File.basename('/a').should == 'a'
+ File.basename('/a/b').should == 'b'
+ File.basename('/ab/ba/bag').should == 'bag'
+ File.basename('/ab/ba/bag.txt').should == 'bag.txt'
+ File.basename('/').should == '/'
+ File.basename('/foo/bar/baz.rb', '.rb').should == 'baz'
+ File.basename('baz.rb', 'z.rb').should == 'ba'
+ end
+
+ it "returns an string" do
+ File.basename("foo").should be_kind_of(String)
+ end
+
+ it "returns the basename for unix format" do
+ File.basename("/foo/bar").should == "bar"
+ File.basename("/foo/bar.txt").should == "bar.txt"
+ File.basename("bar.c").should == "bar.c"
+ File.basename("/bar").should == "bar"
+ File.basename("/bar/").should == "bar"
+
+ # Considered UNC paths on Windows
+ platform_is :windows do
+ File.basename("baz//foo").should =="foo"
+ File.basename("//foo/bar/baz").should == "baz"
+ end
+ end
+
+ it "returns the basename for edge cases" do
+ File.basename("").should == ""
+ File.basename(".").should == "."
+ File.basename("..").should == ".."
+ platform_is_not :windows do
+ File.basename("//foo/").should == "foo"
+ File.basename("//foo//").should == "foo"
+ end
+ File.basename("foo/").should == "foo"
+ end
+
+ it "ignores a trailing directory separator" do
+ File.basename("foo.rb/", '.rb').should == "foo"
+ File.basename("bar.rb///", '.*').should == "bar"
+ end
+
+ it "returns the basename for unix suffix" do
+ File.basename("bar.c", ".c").should == "bar"
+ File.basename("bar.txt", ".txt").should == "bar"
+ File.basename("/bar.txt", ".txt").should == "bar"
+ File.basename("/foo/bar.txt", ".txt").should == "bar"
+ File.basename("bar.txt", ".exe").should == "bar.txt"
+ File.basename("bar.txt.exe", ".exe").should == "bar.txt"
+ File.basename("bar.txt.exe", ".txt").should == "bar.txt.exe"
+ File.basename("bar.txt", ".*").should == "bar"
+ File.basename("bar.txt.exe", ".*").should == "bar.txt"
+ File.basename("bar.txt.exe", ".txt.exe").should == "bar"
+ end
+
+ platform_is_not :windows do
+ it "takes into consideration the platform path separator(s)" do
+ File.basename("C:\\foo\\bar").should == "C:\\foo\\bar"
+ File.basename("C:/foo/bar").should == "bar"
+ File.basename("/foo/bar\\baz").should == "bar\\baz"
+ end
+ end
+
+ platform_is :windows do
+ it "takes into consideration the platform path separator(s)" do
+ File.basename("C:\\foo\\bar").should == "bar"
+ File.basename("C:/foo/bar").should == "bar"
+ File.basename("/foo/bar\\baz").should == "baz"
+ end
+ end
+
+ it "raises a TypeError if the arguments are not String types" do
+ lambda { File.basename(nil) }.should raise_error(TypeError)
+ lambda { File.basename(1) }.should raise_error(TypeError)
+ lambda { File.basename("bar.txt", 1) }.should raise_error(TypeError)
+ lambda { File.basename(true) }.should raise_error(TypeError)
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.basename(mock_to_path("foo.txt"))
+ end
+
+ it "raises an ArgumentError if passed more than two arguments" do
+ lambda { File.basename('bar.txt', '.txt', '.txt') }.should raise_error(ArgumentError)
+ end
+
+ # specific to MS Windows
+ platform_is :windows do
+ it "returns the basename for windows" do
+ File.basename("C:\\foo\\bar\\baz.txt").should == "baz.txt"
+ File.basename("C:\\foo\\bar").should == "bar"
+ File.basename("C:\\foo\\bar\\").should == "bar"
+ File.basename("C:\\foo").should == "foo"
+ File.basename("C:\\").should == "\\"
+ end
+
+ it "returns basename windows unc" do
+ File.basename("\\\\foo\\bar\\baz.txt").should == "baz.txt"
+ File.basename("\\\\foo\\bar\\baz").should =="baz"
+ end
+
+ it "returns basename windows forward slash" do
+ File.basename("C:/").should == "/"
+ File.basename("C:/foo").should == "foo"
+ File.basename("C:/foo/bar").should == "bar"
+ File.basename("C:/foo/bar/").should == "bar"
+ File.basename("C:/foo/bar//").should == "bar"
+ end
+
+ it "returns basename with windows suffix" do
+ File.basename("c:\\bar.txt", ".txt").should == "bar"
+ File.basename("c:\\foo\\bar.txt", ".txt").should == "bar"
+ File.basename("c:\\bar.txt", ".exe").should == "bar.txt"
+ File.basename("c:\\bar.txt.exe", ".exe").should == "bar.txt"
+ File.basename("c:\\bar.txt.exe", ".txt").should == "bar.txt.exe"
+ File.basename("c:\\bar.txt", ".*").should == "bar"
+ File.basename("c:\\bar.txt.exe", ".*").should == "bar.txt"
+ end
+ end
+
+ with_feature :encoding do
+
+ it "returns the extension for a multibyte filename" do
+ File.basename('/path/ОфиÑ.m4a').should == "ОфиÑ.m4a"
+ end
+
+ it "returns the basename with the same encoding as the original" do
+ basename = File.basename('C:/Users/Scuby Pagrubý'.encode(Encoding::Windows_1250))
+ basename.should == 'Scuby Pagrubý'.encode(Encoding::Windows_1250)
+ basename.encoding.should == Encoding::Windows_1250
+ end
+
+ end
+
+end
diff --git a/spec/ruby/core/file/birthtime_spec.rb b/spec/ruby/core/file/birthtime_spec.rb
new file mode 100644
index 0000000000..d263fe5bf1
--- /dev/null
+++ b/spec/ruby/core/file/birthtime_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../../spec_helper'
+
+describe "File.birthtime" do
+ before :each do
+ @file = __FILE__
+ end
+
+ after :each do
+ @file = nil
+ end
+
+ platform_is :windows, :darwin, :freebsd, :netbsd do
+ it "returns the birth time for the named file as a Time object" do
+ File.birthtime(@file)
+ File.birthtime(@file).should be_kind_of(Time)
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.birthtime(mock_to_path(@file))
+ end
+
+ it "raises an Errno::ENOENT exception if the file is not found" do
+ lambda { File.birthtime('bogus') }.should raise_error(Errno::ENOENT)
+ end
+ end
+
+ platform_is :linux, :openbsd do
+ it "raises an NotImplementedError" do
+ lambda { File.birthtime(@file) }.should raise_error(NotImplementedError)
+ end
+ end
+end
+
+describe "File#birthtime" do
+ before :each do
+ @file = File.open(__FILE__)
+ end
+
+ after :each do
+ @file.close
+ @file = nil
+ end
+
+ platform_is :windows, :darwin, :freebsd, :netbsd do
+ it "returns the birth time for self" do
+ @file.birthtime
+ @file.birthtime.should be_kind_of(Time)
+ end
+ end
+
+ platform_is :linux, :openbsd do
+ it "raises an NotImplementedError" do
+ lambda { @file.birthtime }.should raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/ruby/core/file/blockdev_spec.rb b/spec/ruby/core/file/blockdev_spec.rb
new file mode 100644
index 0000000000..9ba9afc251
--- /dev/null
+++ b/spec/ruby/core/file/blockdev_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/blockdev'
+
+describe "File.blockdev?" do
+ it_behaves_like :file_blockdev, :blockdev?, File
+end
diff --git a/spec/ruby/core/file/chardev_spec.rb b/spec/ruby/core/file/chardev_spec.rb
new file mode 100644
index 0000000000..1fc932ee4e
--- /dev/null
+++ b/spec/ruby/core/file/chardev_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/chardev'
+
+describe "File.chardev?" do
+ it_behaves_like :file_chardev, :chardev?, File
+end
diff --git a/spec/ruby/core/file/chmod_spec.rb b/spec/ruby/core/file/chmod_spec.rb
new file mode 100644
index 0000000000..6c4e1099e3
--- /dev/null
+++ b/spec/ruby/core/file/chmod_spec.rb
@@ -0,0 +1,185 @@
+require_relative '../../spec_helper'
+
+describe "File#chmod" do
+ before :each do
+ @filename = tmp('i_exist.exe')
+ @file = File.open(@filename, 'w')
+ end
+
+ after :each do
+ @file.close
+ rm_r @filename
+ end
+
+ it "returns 0 if successful" do
+ @file.chmod(0755).should == 0
+ end
+
+ it "raises RangeError with too large values" do
+ -> { @file.chmod(2**64) }.should raise_error(RangeError)
+ -> { @file.chmod(-2**63 - 1) }.should raise_error(RangeError)
+ end
+
+ it "invokes to_int on non-integer argument" do
+ mode = File.stat(@filename).mode
+ (obj = mock('mode')).should_receive(:to_int).and_return(mode)
+ @file.chmod(obj)
+ File.stat(@filename).mode.should == mode
+ end
+
+ platform_is :windows do
+ it "with '0444' makes file readable and executable but not writable" do
+ @file.chmod(0444)
+ File.readable?(@filename).should == true
+ File.writable?(@filename).should == false
+ File.executable?(@filename).should == true
+ end
+
+ it "with '0644' makes file readable and writable and also executable" do
+ @file.chmod(0644)
+ File.readable?(@filename).should == true
+ File.writable?(@filename).should == true
+ File.executable?(@filename).should == true
+ end
+ end
+
+ platform_is_not :windows do
+ as_user do
+ it "with '0222' makes file writable but not readable or executable" do
+ @file.chmod(0222)
+ File.readable?(@filename).should == false
+ File.writable?(@filename).should == true
+ File.executable?(@filename).should == false
+ end
+
+ it "with '0444' makes file readable but not writable or executable" do
+ @file.chmod(0444)
+ File.readable?(@filename).should == true
+ File.writable?(@filename).should == false
+ File.executable?(@filename).should == false
+ end
+
+ it "with '0666' makes file readable and writable but not executable" do
+ @file.chmod(0666)
+ File.readable?(@filename).should == true
+ File.writable?(@filename).should == true
+ File.executable?(@filename).should == false
+ end
+
+ it "with '0111' makes file executable but not readable or writable" do
+ @file.chmod(0111)
+ File.readable?(@filename).should == false
+ File.writable?(@filename).should == false
+ File.executable?(@filename).should == true
+ end
+
+ it "modifies the permission bits of the files specified" do
+ @file.chmod(0755)
+ File.stat(@filename).mode.should == 33261
+ end
+ end
+ end
+end
+
+describe "File.chmod" do
+ before :each do
+ @file = tmp('i_exist.exe')
+ touch @file
+ @count = File.chmod(0755, @file)
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the number of files modified" do
+ @count.should == 1
+ end
+
+ it "raises RangeError with too large values" do
+ -> { File.chmod(2**64, @file) }.should raise_error(RangeError)
+ -> { File.chmod(-2**63 - 1, @file) }.should raise_error(RangeError)
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.chmod(0, mock_to_path(@file))
+ end
+
+ it "throws a TypeError if the given path is not coercable into a string" do
+ lambda { File.chmod(0, []) }.should raise_error(TypeError)
+ end
+
+ it "raises an error for a non existent path" do
+ lambda {
+ File.chmod(0644, "#{@file}.not.existing")
+ }.should raise_error(Errno::ENOENT)
+ end
+
+ it "invokes to_int on non-integer argument" do
+ mode = File.stat(@file).mode
+ (obj = mock('mode')).should_receive(:to_int).and_return(mode)
+ File.chmod(obj, @file)
+ File.stat(@file).mode.should == mode
+ end
+
+ it "invokes to_str on non-string file names" do
+ mode = File.stat(@file).mode
+ (obj = mock('path')).should_receive(:to_str).and_return(@file)
+ File.chmod(mode, obj)
+ File.stat(@file).mode.should == mode
+ end
+
+ platform_is :windows do
+ it "with '0444' makes file readable and executable but not writable" do
+ File.chmod(0444, @file)
+ File.readable?(@file).should == true
+ File.writable?(@file).should == false
+ File.executable?(@file).should == true
+ end
+
+ it "with '0644' makes file readable and writable and also executable" do
+ File.chmod(0644, @file)
+ File.readable?(@file).should == true
+ File.writable?(@file).should == true
+ File.executable?(@file).should == true
+ end
+ end
+
+ platform_is_not :windows do
+ as_user do
+ it "with '0222' makes file writable but not readable or executable" do
+ File.chmod(0222, @file)
+ File.readable?(@file).should == false
+ File.writable?(@file).should == true
+ File.executable?(@file).should == false
+ end
+
+ it "with '0444' makes file readable but not writable or executable" do
+ File.chmod(0444, @file)
+ File.readable?(@file).should == true
+ File.writable?(@file).should == false
+ File.executable?(@file).should == false
+ end
+ end
+
+ it "with '0666' makes file readable and writable but not executable" do
+ File.chmod(0666, @file)
+ File.readable?(@file).should == true
+ File.writable?(@file).should == true
+ File.executable?(@file).should == false
+ end
+
+ as_user do
+ it "with '0111' makes file executable but not readable or writable" do
+ File.chmod(0111, @file)
+ File.readable?(@file).should == false
+ File.writable?(@file).should == false
+ File.executable?(@file).should == true
+ end
+ end
+
+ it "modifies the permission bits of the files specified" do
+ File.stat(@file).mode.should == 33261
+ end
+ end
+end
diff --git a/spec/ruby/core/file/chown_spec.rb b/spec/ruby/core/file/chown_spec.rb
new file mode 100644
index 0000000000..8660534825
--- /dev/null
+++ b/spec/ruby/core/file/chown_spec.rb
@@ -0,0 +1,152 @@
+require_relative '../../spec_helper'
+
+describe "File.chown" do
+ before :each do
+ @fname = tmp('file_chown_test')
+ touch @fname
+ end
+
+ after :each do
+ rm_r @fname
+ end
+
+ as_superuser do
+ platform_is :windows do
+ it "does not modify the owner id of the file" do
+ File.chown 0, nil, @fname
+ File.stat(@fname).uid.should == 0
+ File.chown 501, nil, @fname
+ File.stat(@fname).uid.should == 0
+ end
+
+ it "does not modify the group id of the file" do
+ File.chown nil, 0, @fname
+ File.stat(@fname).gid.should == 0
+ File.chown nil, 501, @fname
+ File.stat(@fname).gid.should == 0
+ end
+ end
+
+ platform_is_not :windows do
+ it "changes the owner id of the file" do
+ File.chown 501, nil, @fname
+ File.stat(@fname).uid.should == 501
+ File.chown 0, nil, @fname
+ File.stat(@fname).uid.should == 0
+ end
+
+ it "changes the group id of the file" do
+ File.chown nil, 501, @fname
+ File.stat(@fname).gid.should == 501
+ File.chown nil, 0, @fname
+ File.stat(@fname).uid.should == 0
+ end
+
+ it "does not modify the owner id of the file if passed nil or -1" do
+ File.chown 501, nil, @fname
+ File.chown nil, nil, @fname
+ File.stat(@fname).uid.should == 501
+ File.chown nil, -1, @fname
+ File.stat(@fname).uid.should == 501
+ end
+
+ it "does not modify the group id of the file if passed nil or -1" do
+ File.chown nil, 501, @fname
+ File.chown nil, nil, @fname
+ File.stat(@fname).gid.should == 501
+ File.chown nil, -1, @fname
+ File.stat(@fname).gid.should == 501
+ end
+ end
+ end
+
+ it "returns the number of files processed" do
+ File.chown(nil, nil, @fname, @fname).should == 2
+ end
+
+ platform_is_not :windows do
+ it "raises an error for a non existent path" do
+ lambda {
+ File.chown(nil, nil, "#{@fname}_not_existing")
+ }.should raise_error(Errno::ENOENT)
+ end
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.chown(nil, nil, mock_to_path(@fname)).should == 1
+ end
+end
+
+describe "File#chown" do
+ before :each do
+ @fname = tmp('file_chown_test')
+ @file = File.open(@fname, 'w')
+ end
+
+ after :each do
+ @file.close unless @file.closed?
+ rm_r @fname
+ end
+
+ as_superuser do
+ platform_is :windows do
+ it "does not modify the owner id of the file" do
+ @file.chown 0, nil
+ @file.stat.uid.should == 0
+ @file.chown 501, nil
+ @file.stat.uid.should == 0
+ end
+
+ it "does not modify the group id of the file" do
+ @file.chown nil, 0
+ @file.stat.gid.should == 0
+ @file.chown nil, 501
+ @file.stat.gid.should == 0
+ end
+ end
+
+ platform_is_not :windows do
+ it "changes the owner id of the file" do
+ @file.chown 501, nil
+ @file.stat.uid.should == 501
+ @file.chown 0, nil
+ @file.stat.uid.should == 0
+ end
+
+ it "changes the group id of the file" do
+ @file.chown nil, 501
+ @file.stat.gid.should == 501
+ @file.chown nil, 0
+ @file.stat.uid.should == 0
+ end
+
+ it "does not modify the owner id of the file if passed nil or -1" do
+ @file.chown 501, nil
+ @file.chown nil, nil
+ @file.stat.uid.should == 501
+ @file.chown nil, -1
+ @file.stat.uid.should == 501
+ end
+
+ it "does not modify the group id of the file if passed nil or -1" do
+ @file.chown nil, 501
+ @file.chown nil, nil
+ @file.stat.gid.should == 501
+ @file.chown nil, -1
+ @file.stat.gid.should == 501
+ end
+ end
+ end
+
+ it "returns 0" do
+ @file.chown(nil, nil).should == 0
+ end
+end
+
+describe "File.chown" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "File#chown" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/constants/constants_spec.rb b/spec/ruby/core/file/constants/constants_spec.rb
new file mode 100644
index 0000000000..86946822c5
--- /dev/null
+++ b/spec/ruby/core/file/constants/constants_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../../spec_helper'
+
+["APPEND", "CREAT", "EXCL", "FNM_CASEFOLD",
+ "FNM_DOTMATCH", "FNM_EXTGLOB", "FNM_NOESCAPE", "FNM_PATHNAME",
+ "FNM_SYSCASE", "LOCK_EX", "LOCK_NB", "LOCK_SH",
+ "LOCK_UN", "NONBLOCK", "RDONLY",
+ "RDWR", "TRUNC", "WRONLY"].each do |const|
+ describe "File::Constants::#{const}" do
+ it "is defined" do
+ File::Constants.const_defined?(const).should be_true
+ end
+ end
+end
+
+platform_is :windows do
+ describe "File::Constants::BINARY" do
+ it "is defined" do
+ File::Constants.const_defined?(:BINARY).should be_true
+ end
+ end
+end
+
+platform_is_not :windows do
+ ["NOCTTY", "SYNC"].each do |const|
+ describe "File::Constants::#{const}" do
+ it "is defined" do
+ File::Constants.const_defined?(const).should be_true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/constants_spec.rb b/spec/ruby/core/file/constants_spec.rb
new file mode 100644
index 0000000000..5f058a7f40
--- /dev/null
+++ b/spec/ruby/core/file/constants_spec.rb
@@ -0,0 +1,141 @@
+require_relative '../../spec_helper'
+
+# TODO: migrate these to constants/constants_spec.rb
+
+describe "File::Constants" do
+ it "matches mode constants" do
+ File::FNM_NOESCAPE.should_not == nil
+ File::FNM_PATHNAME.should_not == nil
+ File::FNM_DOTMATCH.should_not == nil
+ File::FNM_CASEFOLD.should_not == nil
+ File::FNM_SYSCASE.should_not == nil
+
+ platform_is :windows do #|| VMS
+ File::FNM_SYSCASE.should == 8
+ end
+ end
+
+ # Only these constants are not inherited from the IO class
+ it "the separator constant" do
+ File::SEPARATOR.should_not == nil
+ File::Separator.should_not == nil
+ File::PATH_SEPARATOR.should_not == nil
+ File::SEPARATOR.should == "/"
+
+ platform_is :windows do #|| VMS
+ File::ALT_SEPARATOR.should_not == nil
+ File::PATH_SEPARATOR.should == ";"
+ end
+
+ platform_is_not :windows do
+ File::ALT_SEPARATOR.should == nil
+ File::PATH_SEPARATOR.should == ":"
+ end
+ end
+
+ it "the open mode constants" do
+ File::APPEND.should_not == nil
+ File::CREAT.should_not == nil
+ File::EXCL.should_not == nil
+ File::NONBLOCK.should_not == nil
+ File::RDONLY.should_not == nil
+ File::RDWR.should_not == nil
+ File::TRUNC.should_not == nil
+ File::WRONLY.should_not == nil
+
+ platform_is_not :windows do # Not sure about VMS here
+ File::NOCTTY.should_not == nil
+ end
+ end
+
+ it "lock mode constants" do
+ File::LOCK_EX.should_not == nil
+ File::LOCK_NB.should_not == nil
+ File::LOCK_SH.should_not == nil
+ File::LOCK_UN.should_not == nil
+ end
+end
+
+describe "File::Constants" do
+ # These mode and permission bits are platform dependent
+ it "File::RDONLY" do
+ defined?(File::RDONLY).should == "constant"
+ end
+
+ it "File::WRONLY" do
+ defined?(File::WRONLY).should == "constant"
+ end
+
+ it "File::CREAT" do
+ defined?(File::CREAT).should == "constant"
+ end
+
+ it "File::RDWR" do
+ defined?(File::RDWR).should == "constant"
+ end
+
+ it "File::APPEND" do
+ defined?(File::APPEND).should == "constant"
+ end
+
+ it "File::TRUNC" do
+ defined?(File::TRUNC).should == "constant"
+ end
+
+ platform_is_not :windows do # Not sure about VMS here
+ it "File::NOCTTY" do
+ defined?(File::NOCTTY).should == "constant"
+ end
+ end
+
+ it "File::NONBLOCK" do
+ defined?(File::NONBLOCK).should == "constant"
+ end
+
+ it "File::LOCK_EX" do
+ defined?(File::LOCK_EX).should == "constant"
+ end
+
+ it "File::LOCK_NB" do
+ defined?(File::LOCK_NB).should == "constant"
+ end
+
+ it "File::LOCK_SH" do
+ defined?(File::LOCK_SH).should == "constant"
+ end
+
+ it "File::LOCK_UN" do
+ defined?(File::LOCK_UN).should == "constant"
+ end
+
+ it "File::SEPARATOR" do
+ defined?(File::SEPARATOR).should == "constant"
+ end
+ it "File::Separator" do
+ defined?(File::Separator).should == "constant"
+ end
+
+ it "File::PATH_SEPARATOR" do
+ defined?(File::PATH_SEPARATOR).should == "constant"
+ end
+
+ it "File::SEPARATOR" do
+ defined?(File::SEPARATOR).should == "constant"
+ File::SEPARATOR.should == "/"
+ end
+
+ platform_is :windows do #|| VMS
+ it "File::ALT_SEPARATOR" do
+ defined?(File::ALT_SEPARATOR).should == "constant"
+ File::PATH_SEPARATOR.should == ";"
+ end
+ end
+
+ platform_is_not :windows do
+ it "File::PATH_SEPARATOR" do
+ defined?(File::PATH_SEPARATOR).should == "constant"
+ File::PATH_SEPARATOR.should == ":"
+ end
+ end
+
+end
diff --git a/spec/ruby/core/file/ctime_spec.rb b/spec/ruby/core/file/ctime_spec.rb
new file mode 100644
index 0000000000..50ffbe6a79
--- /dev/null
+++ b/spec/ruby/core/file/ctime_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+
+describe "File.ctime" do
+ before :each do
+ @file = __FILE__
+ end
+
+ after :each do
+ @file = nil
+ end
+
+ it "returns the change time for the named file (the time at which directory information about the file was changed, not the file itself)." do
+ File.ctime(@file)
+ File.ctime(@file).should be_kind_of(Time)
+ end
+
+ 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)
+ if supports_subseconds != 0
+ File.ctime(__FILE__).usec.should > 0
+ else
+ File.ctime(__FILE__).usec.should == 0
+ end
+ end
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.ctime(mock_to_path(@file))
+ end
+
+ it "raises an Errno::ENOENT exception if the file is not found" do
+ lambda { File.ctime('bogus') }.should raise_error(Errno::ENOENT)
+ end
+end
+
+describe "File#ctime" do
+ before :each do
+ @file = File.open(__FILE__)
+ end
+
+ after :each do
+ @file.close
+ @file = nil
+ end
+
+ it "returns the change time for the named file (the time at which directory information about the file was changed, not the file itself)." do
+ @file.ctime
+ @file.ctime.should be_kind_of(Time)
+ end
+end
diff --git a/spec/ruby/core/file/delete_spec.rb b/spec/ruby/core/file/delete_spec.rb
new file mode 100644
index 0000000000..4098499942
--- /dev/null
+++ b/spec/ruby/core/file/delete_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/unlink'
+
+describe "File.delete" do
+ it_behaves_like :file_unlink, :delete
+end
diff --git a/spec/ruby/core/file/directory_spec.rb b/spec/ruby/core/file/directory_spec.rb
new file mode 100644
index 0000000000..8014a7a03d
--- /dev/null
+++ b/spec/ruby/core/file/directory_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/directory'
+
+describe "File.directory?" do
+ it_behaves_like :file_directory, :directory?, File
+end
+
+describe "File.directory?" do
+ it_behaves_like :file_directory_io, :directory?, File
+end
diff --git a/spec/ruby/core/file/dirname_spec.rb b/spec/ruby/core/file/dirname_spec.rb
new file mode 100644
index 0000000000..0447a4e471
--- /dev/null
+++ b/spec/ruby/core/file/dirname_spec.rb
@@ -0,0 +1,108 @@
+require_relative '../../spec_helper'
+
+describe "File.dirname" do
+ it "returns all the components of filename except the last one" do
+ File.dirname('/home/jason').should == '/home'
+ File.dirname('/home/jason/poot.txt').should == '/home/jason'
+ File.dirname('poot.txt').should == '.'
+ File.dirname('/holy///schnikies//w00t.bin').should == '/holy///schnikies'
+ File.dirname('').should == '.'
+ File.dirname('/').should == '/'
+ File.dirname('/foo/foo').should == '/foo'
+ end
+
+ it "returns a String" do
+ File.dirname("foo").should be_kind_of(String)
+ end
+
+ it "does not modify its argument" do
+ x = "/usr/bin"
+ File.dirname(x)
+ x.should == "/usr/bin"
+ end
+
+ it "ignores a trailing /" do
+ File.dirname("/foo/bar/").should == "/foo"
+ end
+
+ it "returns the return all the components of filename except the last one (unix format)" do
+ File.dirname("foo").should =="."
+ File.dirname("/foo").should =="/"
+ File.dirname("/foo/bar").should =="/foo"
+ File.dirname("/foo/bar.txt").should =="/foo"
+ File.dirname("/foo/bar/baz").should =="/foo/bar"
+ end
+
+ it "returns all the components of filename except the last one (edge cases on all platforms)" do
+ File.dirname("").should == "."
+ File.dirname(".").should == "."
+ File.dirname("./").should == "."
+ File.dirname("./b/./").should == "./b"
+ File.dirname("..").should == "."
+ File.dirname("../").should == "."
+ File.dirname("/").should == "/"
+ File.dirname("/.").should == "/"
+ File.dirname("/foo/").should == "/"
+ File.dirname("/foo/.").should == "/foo"
+ File.dirname("/foo/./").should == "/foo"
+ File.dirname("/foo/../.").should == "/foo/.."
+ File.dirname("foo/../").should == "foo"
+ end
+
+ platform_is_not :windows do
+ it "returns all the components of filename except the last one (edge cases on non-windows)" do
+ File.dirname('/////').should == '/'
+ File.dirname("//foo//").should == "/"
+ File.dirname('foo\bar').should == '.'
+ File.dirname('/foo\bar').should == '/'
+ File.dirname('foo/bar\baz').should == 'foo'
+ end
+ end
+
+ platform_is :windows do
+ it "returns all the components of filename except the last one (edge cases on windows)" do
+ File.dirname("//foo").should == "//foo"
+ File.dirname("//foo//").should == "//foo"
+ File.dirname('/////').should == '//'
+ end
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.dirname(mock_to_path("/")).should == "/"
+ end
+
+ it "raises a TypeError if not passed a String type" do
+ lambda { File.dirname(nil) }.should raise_error(TypeError)
+ lambda { File.dirname(0) }.should raise_error(TypeError)
+ lambda { File.dirname(true) }.should raise_error(TypeError)
+ lambda { File.dirname(false) }.should raise_error(TypeError)
+ end
+
+ # Windows specific tests
+ platform_is :windows do
+ it "returns the return all the components of filename except the last one (Windows format)" do
+ File.dirname("C:\\foo\\bar\\baz.txt").should =="C:\\foo\\bar"
+ File.dirname("C:\\foo\\bar").should =="C:\\foo"
+ File.dirname("C:\\foo\\bar\\").should == "C:\\foo"
+ File.dirname("C:\\foo").should == "C:\\"
+ File.dirname("C:\\").should =="C:\\"
+ end
+
+ it "returns the return all the components of filename except the last one (windows unc)" do
+ File.dirname("\\\\foo\\bar\\baz.txt").should == "\\\\foo\\bar"
+ File.dirname("\\\\foo\\bar\\baz").should == "\\\\foo\\bar"
+ File.dirname("\\\\foo").should =="\\\\foo"
+ File.dirname("\\\\foo\\bar").should =="\\\\foo\\bar"
+ File.dirname("\\\\\\foo\\bar").should =="\\\\foo\\bar"
+ File.dirname("\\\\\\foo").should =="\\\\foo"
+ end
+
+ it "returns the return all the components of filename except the last one (forward_slash)" do
+ File.dirname("C:/").should == "C:/"
+ File.dirname("C:/foo").should == "C:/"
+ File.dirname("C:/foo/bar").should == "C:/foo"
+ File.dirname("C:/foo/bar/").should == "C:/foo"
+ File.dirname("C:/foo/bar//").should == "C:/foo"
+ end
+ end
+end
diff --git a/spec/ruby/core/file/empty_spec.rb b/spec/ruby/core/file/empty_spec.rb
new file mode 100644
index 0000000000..a904f140e6
--- /dev/null
+++ b/spec/ruby/core/file/empty_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/zero'
+
+describe "File.empty?" do
+ ruby_version_is "2.4" do
+ it_behaves_like :file_zero, :empty?, File
+ it_behaves_like :file_zero_missing, :empty?, File
+
+ platform_is :solaris do
+ it "returns false for /dev/null" do
+ File.empty?('/dev/null').should == true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/executable_real_spec.rb b/spec/ruby/core/file/executable_real_spec.rb
new file mode 100644
index 0000000000..0cb848b201
--- /dev/null
+++ b/spec/ruby/core/file/executable_real_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/executable_real'
+
+describe "File.executable_real?" do
+ it_behaves_like :file_executable_real, :executable_real?, File
+ it_behaves_like :file_executable_real_missing, :executable_real?, File
+end
diff --git a/spec/ruby/core/file/executable_spec.rb b/spec/ruby/core/file/executable_spec.rb
new file mode 100644
index 0000000000..1dbb3b233d
--- /dev/null
+++ b/spec/ruby/core/file/executable_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/executable'
+
+describe "File.executable?" do
+ it_behaves_like :file_executable, :executable?, File
+ it_behaves_like :file_executable_missing, :executable?, File
+end
diff --git a/spec/ruby/core/file/exist_spec.rb b/spec/ruby/core/file/exist_spec.rb
new file mode 100644
index 0000000000..ddb5febcba
--- /dev/null
+++ b/spec/ruby/core/file/exist_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/exist'
+
+describe "File.exist?" do
+ it_behaves_like :file_exist, :exist?, File
+end
diff --git a/spec/ruby/core/file/exists_spec.rb b/spec/ruby/core/file/exists_spec.rb
new file mode 100644
index 0000000000..31d0e4dd1e
--- /dev/null
+++ b/spec/ruby/core/file/exists_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/exist'
+
+describe "File.exists?" do
+ it_behaves_like :file_exist, :exists?, File
+end
diff --git a/spec/ruby/core/file/expand_path_spec.rb b/spec/ruby/core/file/expand_path_spec.rb
new file mode 100644
index 0000000000..cbc3d34bdb
--- /dev/null
+++ b/spec/ruby/core/file/expand_path_spec.rb
@@ -0,0 +1,261 @@
+# -*- encoding: utf-8 -*-
+
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "File.expand_path" do
+ before :each do
+ platform_is :windows do
+ @base = `cd`.chomp.tr '\\', '/'
+ @tmpdir = "c:/tmp"
+ @rootdir = "c:/"
+ end
+
+ platform_is_not :windows do
+ @base = Dir.pwd
+ @tmpdir = "/tmp"
+ @rootdir = "/"
+ end
+ end
+
+ with_feature :encoding do
+ before :each do
+ @external = Encoding.default_external
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ end
+ end
+
+ it "converts a pathname to an absolute pathname" do
+ File.expand_path('').should == @base
+ File.expand_path('a').should == File.join(@base, 'a')
+ File.expand_path('a', nil).should == File.join(@base, 'a')
+ end
+
+ it "converts a pathname to an absolute pathname, Ruby-Talk:18512" do
+ # See Ruby-Talk:18512
+ File.expand_path('.a').should == File.join(@base, '.a')
+ File.expand_path('..a').should == File.join(@base, '..a')
+ File.expand_path('a../b').should == File.join(@base, 'a../b')
+ end
+
+ platform_is_not :windows do
+ it "keeps trailing dots on absolute pathname" do
+ # See Ruby-Talk:18512
+ File.expand_path('a.').should == File.join(@base, 'a.')
+ File.expand_path('a..').should == File.join(@base, 'a..')
+ end
+ end
+
+ it "converts a pathname to an absolute pathname, using a complete path" do
+ File.expand_path("", "#{@tmpdir}").should == "#{@tmpdir}"
+ File.expand_path("a", "#{@tmpdir}").should =="#{@tmpdir}/a"
+ File.expand_path("../a", "#{@tmpdir}/xxx").should == "#{@tmpdir}/a"
+ File.expand_path(".", "#{@rootdir}").should == "#{@rootdir}"
+ end
+
+ # FIXME: do not use conditionals like this around #it blocks
+ unless not home = ENV['HOME']
+ platform_is_not :windows do
+ it "converts a pathname to an absolute pathname, using ~ (home) as base" do
+ File.expand_path('~').should == home
+ File.expand_path('~', '/tmp/gumby/ddd').should == home
+ File.expand_path('~/a', '/tmp/gumby/ddd').should == File.join(home, 'a')
+ end
+
+ it "does not return a frozen string" do
+ File.expand_path('~').frozen?.should == false
+ File.expand_path('~', '/tmp/gumby/ddd').frozen?.should == false
+ File.expand_path('~/a', '/tmp/gumby/ddd').frozen?.should == false
+ end
+ end
+ platform_is :windows do
+ it "converts a pathname to an absolute pathname, using ~ (home) as base" do
+ File.expand_path('~').should == home.tr("\\", '/')
+ File.expand_path('~', '/tmp/gumby/ddd').should == home.tr("\\", '/')
+ File.expand_path('~/a', '/tmp/gumby/ddd').should == File.join(home.tr("\\", '/'), 'a')
+ end
+
+ it "does not return a frozen string" do
+ File.expand_path('~').frozen?.should == false
+ File.expand_path('~', '/tmp/gumby/ddd').frozen?.should == false
+ File.expand_path('~/a', '/tmp/gumby/ddd').frozen?.should == false
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ before do
+ @var_home = ENV['HOME'].chomp('/')
+ @db_home = Dir.home
+ end
+
+ # FIXME: these are insane!
+ it "expand path with" do
+ File.expand_path("../../bin", "/tmp/x").should == "/bin"
+ File.expand_path("../../bin", "/tmp").should == "/bin"
+ File.expand_path("../../bin", "/").should == "/bin"
+ File.expand_path("../bin", "tmp/x").should == File.join(@base, 'tmp', 'bin')
+ File.expand_path("../bin", "x/../tmp").should == File.join(@base, 'bin')
+ end
+
+ it "expand_path for commoms unix path give a full path" do
+ File.expand_path('/tmp/').should =='/tmp'
+ File.expand_path('/tmp/../../../tmp').should == '/tmp'
+ File.expand_path('').should == Dir.pwd
+ File.expand_path('./////').should == Dir.pwd
+ File.expand_path('.').should == Dir.pwd
+ File.expand_path(Dir.pwd).should == Dir.pwd
+ File.expand_path('~/').should == @var_home
+ File.expand_path('~/..badfilename').should == "#{@var_home}/..badfilename"
+ File.expand_path('~/a','~/b').should == "#{@var_home}/a"
+ File.expand_path('..').should == File.dirname(Dir.pwd)
+ end
+
+ it "does not replace multiple '/' at the beginning of the path" do
+ File.expand_path('////some/path').should == "////some/path"
+ end
+
+ it "replaces multiple '/' with a single '/'" do
+ File.expand_path('/some////path').should == "/some/path"
+ end
+
+ it "raises an ArgumentError if the path is not valid" do
+ lambda { File.expand_path("~a_not_existing_user") }.should raise_error(ArgumentError)
+ end
+
+ it "expands ~ENV['USER'] to the user's home directory" do
+ File.expand_path("~#{ENV['USER']}").should == @db_home
+ end
+
+ it "expands ~ENV['USER']/a to a in the user's home directory" do
+ File.expand_path("~#{ENV['USER']}/a").should == "#{@db_home}/a"
+ end
+
+ it "does not expand ~ENV['USER'] when it's not at the start" do
+ File.expand_path("/~#{ENV['USER']}/a").should == "/~#{ENV['USER']}/a"
+ end
+
+ it "expands ../foo with ~/dir as base dir to /path/to/user/home/foo" do
+ File.expand_path('../foo', '~/dir').should == "#{@var_home}/foo"
+ end
+ end
+
+ it "accepts objects that have a #to_path method" do
+ File.expand_path(mock_to_path("a"), mock_to_path("#{@tmpdir}"))
+ end
+
+ it "raises a TypeError if not passed a String type" do
+ lambda { File.expand_path(1) }.should raise_error(TypeError)
+ lambda { File.expand_path(nil) }.should raise_error(TypeError)
+ lambda { File.expand_path(true) }.should raise_error(TypeError)
+ end
+
+ platform_is_not :windows do
+ it "expands /./dir to /dir" do
+ File.expand_path("/./dir").should == "/dir"
+ end
+ end
+
+ platform_is :windows do
+ it "expands C:/./dir to C:/dir" do
+ File.expand_path("C:/./dir").should == "C:/dir"
+ end
+ end
+
+ with_feature :encoding do
+ it "returns a String in the same encoding as the argument" do
+ Encoding.default_external = Encoding::SHIFT_JIS
+
+ path = "./a".force_encoding Encoding::CP1251
+ File.expand_path(path).encoding.should equal(Encoding::CP1251)
+
+ weird_path = [222, 173, 190, 175].pack('C*')
+ File.expand_path(weird_path).encoding.should equal(Encoding::ASCII_8BIT)
+ end
+
+ platform_is_not :windows do
+ it "expands a path when the default external encoding is ASCII-8BIT" do
+ Encoding.default_external = Encoding::ASCII_8BIT
+ path_8bit = [222, 173, 190, 175].pack('C*')
+ File.expand_path( path_8bit, @rootdir).should == "#{@rootdir}" + path_8bit
+ end
+ end
+
+ it "expands a path with multi-byte characters" do
+ File.expand_path("Ångström").should == "#{@base}/Ångström"
+ end
+
+ platform_is_not :windows do
+ it "raises an Encoding::CompatibilityError if the external encoding is not compatible" do
+ Encoding.default_external = Encoding::UTF_16BE
+ lambda { File.expand_path("./a") }.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+ end
+
+ it "does not modify the string argument" do
+ str = "./a/b/../c"
+ File.expand_path(str, @base).should == "#{@base}/a/c"
+ str.should == "./a/b/../c"
+ end
+
+ it "does not modify a HOME string argument" do
+ str = "~/a"
+ File.expand_path(str).should == "#{Dir.home}/a"
+ str.should == "~/a"
+ end
+
+ it "returns a String when passed a String subclass" do
+ str = FileSpecs::SubString.new "./a/b/../c"
+ path = File.expand_path(str, @base)
+ path.should == "#{@base}/a/c"
+ path.should be_an_instance_of(String)
+ end
+end
+
+platform_is_not :windows do
+ describe "File.expand_path when HOME is not set" do
+ before :each do
+ @home = ENV["HOME"]
+ end
+
+ after :each do
+ ENV["HOME"] = @home
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "raises an ArgumentError when passed '~' if HOME is nil" do
+ ENV.delete "HOME"
+ lambda { File.expand_path("~") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed '~/' if HOME is nil" do
+ ENV.delete "HOME"
+ lambda { File.expand_path("~/") }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "raises an ArgumentError when passed '~' if HOME == ''" do
+ ENV["HOME"] = ""
+ lambda { File.expand_path("~") }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "File.expand_path with a non-absolute HOME" do
+ before :each do
+ @home = ENV["HOME"]
+ end
+
+ after :each do
+ ENV["HOME"] = @home
+ end
+
+ it "raises an ArgumentError" do
+ ENV["HOME"] = "non-absolute"
+ lambda { File.expand_path("~") }.should raise_error(ArgumentError, 'non-absolute home')
+ end
+ end
+end
diff --git a/spec/ruby/core/file/extname_spec.rb b/spec/ruby/core/file/extname_spec.rb
new file mode 100644
index 0000000000..1513b30e90
--- /dev/null
+++ b/spec/ruby/core/file/extname_spec.rb
@@ -0,0 +1,54 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe "File.extname" do
+ it "returns the extension (the portion of file name in path after the period)." do
+ File.extname("foo.rb").should == ".rb"
+ File.extname("/foo/bar.rb").should == ".rb"
+ File.extname("/foo.rb/bar.c").should == ".c"
+ File.extname("bar").should == ""
+ File.extname(".bashrc").should == ""
+ File.extname("/foo.bar/baz").should == ""
+ File.extname(".app.conf").should == ".conf"
+ end
+
+ it "returns the extension (the portion of file name in path after the period).(edge cases)" do
+ File.extname("").should == ""
+ File.extname(".").should == ""
+ File.extname("/").should == ""
+ File.extname("/.").should == ""
+ File.extname("..").should == ""
+ File.extname("...").should == ""
+ File.extname("....").should == ""
+ File.extname(".foo.").should == ""
+ File.extname("foo.").should == ""
+ end
+
+ it "returns only the last extension of a file with several dots" do
+ File.extname("a.b.c.d.e").should == ".e"
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.extname(mock_to_path("a.b.c.d.e")).should == ".e"
+ end
+
+ it "raises a TypeError if not passed a String type" do
+ lambda { File.extname(nil) }.should raise_error(TypeError)
+ lambda { File.extname(0) }.should raise_error(TypeError)
+ lambda { File.extname(true) }.should raise_error(TypeError)
+ lambda { File.extname(false) }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError if not passed one argument" do
+ lambda { File.extname }.should raise_error(ArgumentError)
+ lambda { File.extname("foo.bar", "foo.baz") }.should raise_error(ArgumentError)
+ end
+
+ with_feature :encoding do
+
+ it "returns the extension for a multibyte filename" do
+ File.extname('ИмÑ.m4a').should == ".m4a"
+ end
+
+ end
+end
diff --git a/spec/ruby/core/file/file_spec.rb b/spec/ruby/core/file/file_spec.rb
new file mode 100644
index 0000000000..8a9dfd5fe2
--- /dev/null
+++ b/spec/ruby/core/file/file_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/file'
+
+describe "File" do
+ it "includes Enumerable" do
+ File.include?(Enumerable).should == true
+ end
+
+ it "includes File::Constants" do
+ File.include?(File::Constants).should == true
+ end
+end
+
+describe "File.file?" do
+ it_behaves_like :file_file, :file?, File
+end
diff --git a/spec/ruby/core/file/fixtures/common.rb b/spec/ruby/core/file/fixtures/common.rb
new file mode 100644
index 0000000000..50721388ad
--- /dev/null
+++ b/spec/ruby/core/file/fixtures/common.rb
@@ -0,0 +1,22 @@
+module FileSpecs
+ class SubString < String; end
+
+ def self.make_closer(obj, exc=nil)
+ ScratchPad << :file_opened
+
+ class << obj
+ attr_accessor :close_exception
+
+ alias_method :original_close, :close
+
+ def close
+ original_close
+ ScratchPad << :file_closed
+
+ raise @close_exception if @close_exception
+ end
+ end
+
+ obj.close_exception = exc
+ end
+end
diff --git a/spec/ruby/core/file/fixtures/do_not_remove b/spec/ruby/core/file/fixtures/do_not_remove
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/spec/ruby/core/file/fixtures/do_not_remove
@@ -0,0 +1 @@
+
diff --git a/spec/ruby/core/file/fixtures/file_types.rb b/spec/ruby/core/file/fixtures/file_types.rb
new file mode 100644
index 0000000000..a36817fb4e
--- /dev/null
+++ b/spec/ruby/core/file/fixtures/file_types.rb
@@ -0,0 +1,66 @@
+module FileSpecs
+ def self.configure_types
+ return if @configured
+
+ @file = tmp("test.txt")
+ @dir = Dir.pwd
+ @fifo = tmp("test_fifo")
+ @link = tmp("test_link")
+
+ platform_is_not :windows do
+ @block = `find /dev /devices -type b 2>/dev/null`.split("\n").first
+ @char = `{ tty || find /dev /devices -type c; } 2>/dev/null`.split("\n").last
+ end
+
+ @configured = true
+ end
+
+ def self.normal_file
+ touch(@file)
+ yield @file
+ ensure
+ rm_r @file
+ end
+
+ def self.directory
+ yield @dir
+ end
+
+ def self.fifo
+ File.mkfifo(@fifo)
+ yield @fifo
+ ensure
+ rm_r @fifo
+ end
+
+ def self.block_device
+ raise "Could not find a block device" unless @block
+ yield @block
+ end
+
+ def self.character_device
+ raise "Could not find a character device" unless @char
+ yield @char
+ end
+
+ def self.symlink
+ touch(@file)
+ File.symlink(@file, @link)
+ yield @link
+ ensure
+ rm_r @file, @link
+ end
+
+ def self.socket
+ require 'socket'
+ name = tmp("ftype_socket.socket")
+ rm_r name
+ socket = UNIXServer.new name
+ begin
+ yield name
+ ensure
+ socket.close
+ rm_r name
+ end
+ end
+end
diff --git a/spec/ruby/core/file/flock_spec.rb b/spec/ruby/core/file/flock_spec.rb
new file mode 100644
index 0000000000..6990915a65
--- /dev/null
+++ b/spec/ruby/core/file/flock_spec.rb
@@ -0,0 +1,106 @@
+require_relative '../../spec_helper'
+
+describe "File#flock" do
+ before :each do
+ ScratchPad.record []
+
+ @name = tmp("flock_test")
+ touch(@name)
+
+ @file = File.open @name, "w+"
+ end
+
+ after :each do
+ @file.flock File::LOCK_UN
+ @file.close
+
+ rm_r @name
+ end
+
+ it "exclusively locks a file" do
+ @file.flock(File::LOCK_EX).should == 0
+ @file.flock(File::LOCK_UN).should == 0
+ end
+
+ it "non-exclusively locks a file" do
+ @file.flock(File::LOCK_SH).should == 0
+ @file.flock(File::LOCK_UN).should == 0
+ end
+
+ it "returns false if trying to lock an exclusively locked file" do
+ @file.flock File::LOCK_EX
+
+ ruby_exe(<<-END_OF_CODE, escape: true).should == "false"
+ File.open('#{@name}', "w") do |f2|
+ print f2.flock(File::LOCK_EX | File::LOCK_NB).to_s
+ end
+ END_OF_CODE
+ end
+
+ it "blocks if trying to lock an exclusively locked file" do
+ @file.flock File::LOCK_EX
+
+ out = ruby_exe(<<-END_OF_CODE, escape: true)
+ running = false
+
+ t = Thread.new do
+ File.open('#{@name}', "w") do |f2|
+ puts "before"
+ running = true
+ f2.flock(File::LOCK_EX)
+ puts "after"
+ end
+ end
+
+ Thread.pass until running
+ Thread.pass while t.status and t.status != "sleep"
+ sleep 0.1
+
+ t.kill
+ t.join
+ END_OF_CODE
+
+ out.should == "before\n"
+ end
+
+ it "returns 0 if trying to lock a non-exclusively locked file" do
+ @file.flock File::LOCK_SH
+
+ File.open(@name, "r") do |f2|
+ f2.flock(File::LOCK_SH | File::LOCK_NB).should == 0
+ f2.flock(File::LOCK_UN).should == 0
+ end
+ end
+end
+
+platform_is :solaris do
+ describe "File#flock on Solaris" do
+ before :each do
+ @name = tmp("flock_test")
+ touch(@name)
+
+ @read_file = File.open @name, "r"
+ @write_file = File.open @name, "w"
+ end
+
+ after :each do
+ @read_file.flock File::LOCK_UN
+ @read_file.close
+ @write_file.flock File::LOCK_UN
+ @write_file.close
+ rm_r @name
+ end
+
+ it "fails with EBADF acquiring exclusive lock on read-only File" do
+ lambda do
+ @read_file.flock File::LOCK_EX
+ end.should raise_error(Errno::EBADF)
+ end
+
+ it "fails with EBADF acquiring shared lock on read-only File" do
+ lambda do
+ @write_file.flock File::LOCK_SH
+ end.should raise_error(Errno::EBADF)
+ end
+ end
+end
diff --git a/spec/ruby/core/file/fnmatch_spec.rb b/spec/ruby/core/file/fnmatch_spec.rb
new file mode 100644
index 0000000000..a1b7fa12b3
--- /dev/null
+++ b/spec/ruby/core/file/fnmatch_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'shared/fnmatch'
+
+describe "File.fnmatch" do
+ it_behaves_like :file_fnmatch, :fnmatch
+end
+
+describe "File.fnmatch?" do
+ it_behaves_like :file_fnmatch, :fnmatch?
+end
diff --git a/spec/ruby/core/file/ftype_spec.rb b/spec/ruby/core/file/ftype_spec.rb
new file mode 100644
index 0000000000..e92f04da2c
--- /dev/null
+++ b/spec/ruby/core/file/ftype_spec.rb
@@ -0,0 +1,73 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/file_types'
+
+describe "File.ftype" do
+ before :all do
+ FileSpecs.configure_types
+ end
+
+ it "raises ArgumentError if not given exactly one filename" do
+ lambda { File.ftype }.should raise_error(ArgumentError)
+ lambda { File.ftype('blah', 'bleh') }.should raise_error(ArgumentError)
+ end
+
+ it "raises Errno::ENOENT if the file is not valid" do
+ l = lambda { File.ftype("/#{$$}#{Time.now.to_f}") }
+ l.should raise_error(Errno::ENOENT)
+ end
+
+ it "returns a String" do
+ FileSpecs.normal_file do |file|
+ File.ftype(file).should be_kind_of(String)
+ end
+ end
+
+ it "returns 'file' when the file is a file" do
+ FileSpecs.normal_file do |file|
+ File.ftype(file).should == 'file'
+ end
+ end
+
+ it "returns 'directory' when the file is a dir" do
+ FileSpecs.directory do |dir|
+ File.ftype(dir).should == 'directory'
+ end
+ end
+
+ # Both FreeBSD and Windows does not have block devices
+ platform_is_not :freebsd, :windows do
+ with_block_device do
+ it "returns 'blockSpecial' when the file is a block" do
+ FileSpecs.block_device do |block|
+ File.ftype(block).should == 'blockSpecial'
+ end
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns 'characterSpecial' when the file is a char" do
+ FileSpecs.character_device do |char|
+ File.ftype(char).should == 'characterSpecial'
+ end
+ end
+
+ it "returns 'link' when the file is a link" do
+ FileSpecs.symlink do |link|
+ File.ftype(link).should == 'link'
+ end
+ end
+
+ it "returns fifo when the file is a fifo" do
+ FileSpecs.fifo do |fifo|
+ File.ftype(fifo).should == 'fifo'
+ end
+ end
+
+ it "returns 'socket' when the file is a socket" do
+ FileSpecs.socket do |socket|
+ File.ftype(socket).should == 'socket'
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/grpowned_spec.rb b/spec/ruby/core/file/grpowned_spec.rb
new file mode 100644
index 0000000000..8ddac5237c
--- /dev/null
+++ b/spec/ruby/core/file/grpowned_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/grpowned'
+
+describe "File.grpowned?" do
+ it_behaves_like :file_grpowned, :grpowned?, File
+
+ it "returns false if file the does not exist" do
+ File.grpowned?("i_am_a_bogus_file").should == false
+ end
+end
diff --git a/spec/ruby/core/file/identical_spec.rb b/spec/ruby/core/file/identical_spec.rb
new file mode 100644
index 0000000000..bbeaef24d2
--- /dev/null
+++ b/spec/ruby/core/file/identical_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/identical'
+
+describe "File.identical?" do
+ it_behaves_like :file_identical, :identical?, File
+end
diff --git a/spec/ruby/core/file/initialize_spec.rb b/spec/ruby/core/file/initialize_spec.rb
new file mode 100644
index 0000000000..00e2228031
--- /dev/null
+++ b/spec/ruby/core/file/initialize_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+
+describe "File#initialize" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "File#initialize" do
+ after :each do
+ @io.close if @io
+ end
+
+ it "accepts encoding options in mode parameter" do
+ @io = File.new(__FILE__, 'r:UTF-8:iso-8859-1')
+ @io.external_encoding.to_s.should == 'UTF-8'
+ @io.internal_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "accepts encoding options as a hash parameter" do
+ @io = File.new(__FILE__, 'r', encoding: 'UTF-8:iso-8859-1')
+ @io.external_encoding.to_s.should == 'UTF-8'
+ @io.internal_encoding.to_s.should == 'ISO-8859-1'
+ end
+end
diff --git a/spec/ruby/core/file/inspect_spec.rb b/spec/ruby/core/file/inspect_spec.rb
new file mode 100644
index 0000000000..148e789c62
--- /dev/null
+++ b/spec/ruby/core/file/inspect_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "File#inspect" do
+ before :each do
+ @name = tmp("file_inspect.txt")
+ @file = File.open @name, "w"
+ end
+
+ after :each do
+ @file.close unless @file.closed?
+ rm_r @name
+ end
+
+ it "returns a String" do
+ @file.inspect.should be_an_instance_of(String)
+ end
+end
diff --git a/spec/ruby/core/file/join_spec.rb b/spec/ruby/core/file/join_spec.rb
new file mode 100644
index 0000000000..333b566c3d
--- /dev/null
+++ b/spec/ruby/core/file/join_spec.rb
@@ -0,0 +1,139 @@
+require_relative '../../spec_helper'
+
+describe "File.join" do
+ # see [ruby-core:46804] for the 4 following rules
+ it "changes only boundaries separators" do
+ File.join("file/\\/usr/", "/bin").should == "file/\\/usr/bin"
+ File.join("file://usr", "bin").should == "file://usr/bin"
+ end
+
+ it "respects the given separator if only one part has a boundary separator" do
+ File.join("usr/", "bin").should == "usr/bin"
+ File.join("usr", "/bin").should == "usr/bin"
+ File.join("usr//", "bin").should == "usr//bin"
+ File.join("usr", "//bin").should == "usr//bin"
+ end
+
+ it "joins parts using File::SEPARATOR if there are no boundary separators" do
+ File.join("usr", "bin").should == "usr/bin"
+ end
+
+ it "prefers the separator of the right part if both parts have separators" do
+ File.join("usr/", "//bin").should == "usr//bin"
+ File.join("usr//", "/bin").should == "usr/bin"
+ end
+
+ platform_is :windows do
+ it "respects given separator if only one part has a boundary separator" do
+ File.join("C:\\", 'windows').should == "C:\\windows"
+ File.join("C:", "\\windows").should == "C:\\windows"
+ File.join("\\\\", "usr").should == "\\\\usr"
+ end
+
+ it "prefers the separator of the right part if both parts have separators" do
+ File.join("C:/", "\\windows").should == "C:\\windows"
+ File.join("C:\\", "/windows").should == "C:/windows"
+ end
+ end
+
+ platform_is_not :windows do
+ it "does not treat \\ as a separator on non-Windows" do
+ File.join("usr\\", 'bin').should == "usr\\/bin"
+ File.join("usr", "\\bin").should == "usr/\\bin"
+ File.join("usr/", "\\bin").should == "usr/\\bin"
+ File.join("usr\\", "/bin").should == "usr\\/bin"
+ end
+ end
+
+ it "returns an empty string when given no arguments" do
+ File.join.should == ""
+ end
+
+ it "returns a duplicate string when given a single argument" do
+ str = "usr"
+ File.join(str).should == str
+ File.join(str).should_not equal(str)
+ end
+
+ it "supports any number of arguments" do
+ File.join("a", "b", "c", "d").should == "a/b/c/d"
+ end
+
+ it "flattens nested arrays" do
+ File.join(["a", "b", "c"]).should == "a/b/c"
+ File.join(["a", ["b", ["c"]]]).should == "a/b/c"
+ end
+
+ it "inserts the separator in between empty strings and arrays" do
+ File.join("").should == ""
+ File.join("", "").should == "/"
+ File.join(["", ""]).should == "/"
+ File.join("a", "").should == "a/"
+ File.join("", "a").should == "/a"
+
+ File.join([]).should == ""
+ File.join([], []).should == "/"
+ File.join([[], []]).should == "/"
+ File.join("a", []).should == "a/"
+ File.join([], "a").should == "/a"
+ end
+
+ it "handles leading parts edge cases" do
+ File.join("/bin") .should == "/bin"
+ File.join("", "bin") .should == "/bin"
+ File.join("/", "bin") .should == "/bin"
+ File.join("/", "/bin").should == "/bin"
+ end
+
+ it "handles trailing parts edge cases" do
+ File.join("bin", "") .should == "bin/"
+ File.join("bin/") .should == "bin/"
+ File.join("bin/", "") .should == "bin/"
+ File.join("bin", "/") .should == "bin/"
+ File.join("bin/", "/").should == "bin/"
+ end
+
+ it "handles middle parts edge cases" do
+ File.join("usr", "", "bin") .should == "usr/bin"
+ File.join("usr/", "", "bin") .should == "usr/bin"
+ File.join("usr", "", "/bin").should == "usr/bin"
+ File.join("usr/", "", "/bin").should == "usr/bin"
+ end
+
+ # TODO: See MRI svn r23306. Add patchlevel when there is a release.
+ it "raises an ArgumentError if passed a recursive array" do
+ a = ["a"]
+ a << a
+ lambda { File.join a }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError exception when args are nil" do
+ lambda { File.join nil }.should raise_error(TypeError)
+ end
+
+ it "calls #to_str" do
+ lambda { File.join(mock('x')) }.should raise_error(TypeError)
+
+ bin = mock("bin")
+ bin.should_receive(:to_str).exactly(:twice).and_return("bin")
+ File.join(bin).should == "bin"
+ File.join("usr", bin).should == "usr/bin"
+ end
+
+ it "doesn't mutate the object when calling #to_str" do
+ usr = mock("usr")
+ str = "usr"
+ usr.should_receive(:to_str).and_return(str)
+ File.join(usr, "bin").should == "usr/bin"
+ str.should == "usr"
+ end
+
+ it "calls #to_path" do
+ lambda { File.join(mock('x')) }.should raise_error(TypeError)
+
+ bin = mock("bin")
+ bin.should_receive(:to_path).exactly(:twice).and_return("bin")
+ File.join(bin).should == "bin"
+ File.join("usr", bin).should == "usr/bin"
+ end
+end
diff --git a/spec/ruby/core/file/lchmod_spec.rb b/spec/ruby/core/file/lchmod_spec.rb
new file mode 100644
index 0000000000..7be09a05ef
--- /dev/null
+++ b/spec/ruby/core/file/lchmod_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+
+describe "File.lchmod" do
+ platform_is_not :linux, :windows, :openbsd, :solaris, :aix do
+ before :each do
+ @fname = tmp('file_chmod_test')
+ @lname = @fname + '.lnk'
+
+ touch(@fname) { |f| f.write "rubinius" }
+
+ rm_r @lname
+ File.symlink @fname, @lname
+ end
+
+ after :each do
+ rm_r @lname, @fname
+ end
+
+ it "changes the file mode of the link and not of the file" do
+ File.chmod(0222, @lname).should == 1
+ File.lchmod(0755, @lname).should == 1
+
+ File.lstat(@lname).executable?.should == true
+ File.lstat(@lname).readable?.should == true
+ File.lstat(@lname).writable?.should == true
+
+ File.stat(@lname).executable?.should == false
+ File.stat(@lname).readable?.should == false
+ File.stat(@lname).writable?.should == true
+ end
+ end
+
+ platform_is :linux, :openbsd, :aix do
+ it "returns false from #respond_to?" do
+ File.respond_to?(:lchmod).should be_false
+ end
+
+ it "raises a NotImplementedError when called" do
+ lambda { File.lchmod 0 }.should raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/ruby/core/file/lchown_spec.rb b/spec/ruby/core/file/lchown_spec.rb
new file mode 100644
index 0000000000..7889d68559
--- /dev/null
+++ b/spec/ruby/core/file/lchown_spec.rb
@@ -0,0 +1,63 @@
+require_relative '../../spec_helper'
+
+as_superuser do
+ describe "File.lchown" do
+ platform_is_not :windows do
+ before :each do
+ @fname = tmp('file_chown_test')
+ @lname = @fname + '.lnk'
+
+ touch(@fname) { |f| f.chown 501, 501 }
+
+ rm_r @lname
+ File.symlink @fname, @lname
+ end
+
+ after :each do
+ rm_r @lname, @fname
+ end
+
+ it "changes the owner id of the file" do
+ File.lchown 502, nil, @lname
+ File.stat(@fname).uid.should == 501
+ File.lstat(@lname).uid.should == 502
+ File.lchown 0, nil, @lname
+ File.stat(@fname).uid.should == 501
+ File.lstat(@lname).uid.should == 0
+ end
+
+ it "changes the group id of the file" do
+ File.lchown nil, 502, @lname
+ File.stat(@fname).gid.should == 501
+ File.lstat(@lname).gid.should == 502
+ File.lchown nil, 0, @lname
+ File.stat(@fname).uid.should == 501
+ File.lstat(@lname).uid.should == 0
+ end
+
+ it "does not modify the owner id of the file if passed nil or -1" do
+ File.lchown 502, nil, @lname
+ File.lchown nil, nil, @lname
+ File.lstat(@lname).uid.should == 502
+ File.lchown nil, -1, @lname
+ File.lstat(@lname).uid.should == 502
+ end
+
+ it "does not modify the group id of the file if passed nil or -1" do
+ File.lchown nil, 502, @lname
+ File.lchown nil, nil, @lname
+ File.lstat(@lname).gid.should == 502
+ File.lchown nil, -1, @lname
+ File.lstat(@lname).gid.should == 502
+ end
+
+ it "returns the number of files processed" do
+ File.lchown(nil, nil, @lname, @lname).should == 2
+ end
+ end
+ end
+end
+
+describe "File.lchown" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/link_spec.rb b/spec/ruby/core/file/link_spec.rb
new file mode 100644
index 0000000000..4e186c6076
--- /dev/null
+++ b/spec/ruby/core/file/link_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+
+describe "File.link" do
+ before :each do
+ @file = tmp("file_link.txt")
+ @link = tmp("file_link.lnk")
+
+ rm_r @link
+ touch @file
+ end
+
+ after :each do
+ rm_r @link, @file
+ end
+
+ platform_is_not :windows do
+ it "link a file with another" do
+ File.link(@file, @link).should == 0
+ File.exist?(@link).should == true
+ File.identical?(@file, @link).should == true
+ end
+
+ it "raises an Errno::EEXIST if the target already exists" do
+ File.link(@file, @link)
+ lambda { File.link(@file, @link) }.should raise_error(Errno::EEXIST)
+ end
+
+ it "raises an ArgumentError if not passed two arguments" do
+ lambda { File.link }.should raise_error(ArgumentError)
+ lambda { File.link(@file) }.should raise_error(ArgumentError)
+ lambda { File.link(@file, @link, @file) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if not passed String types" do
+ lambda { File.link(@file, nil) }.should raise_error(TypeError)
+ lambda { File.link(@file, 1) }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/file/lstat_spec.rb b/spec/ruby/core/file/lstat_spec.rb
new file mode 100644
index 0000000000..918ed02163
--- /dev/null
+++ b/spec/ruby/core/file/lstat_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+require_relative 'shared/stat'
+
+describe "File.lstat" do
+ it_behaves_like :file_stat, :lstat
+end
+
+describe "File.lstat" do
+
+ before :each do
+ @file = tmp('i_exist')
+ @link = tmp('i_am_a_symlink')
+ touch(@file) { |f| f.write 'rubinius' }
+ File.symlink(@file, @link)
+ end
+
+ after :each do
+ rm_r @link, @file
+ end
+
+ platform_is_not :windows do
+ it "returns a File::Stat object with symlink properties for a symlink" do
+ st = File.lstat(@link)
+
+ st.symlink?.should == true
+ st.file?.should == false
+ end
+ end
+end
+
+describe "File#lstat" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/mkfifo_spec.rb b/spec/ruby/core/file/mkfifo_spec.rb
new file mode 100644
index 0000000000..a6db87a12e
--- /dev/null
+++ b/spec/ruby/core/file/mkfifo_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+
+describe "File.mkfifo" do
+ platform_is_not :windows do
+ before do
+ @path = tmp('fifo')
+ end
+
+ after do
+ rm_r(@path)
+ end
+
+ context "when path passed responds to :to_path" do
+ it "creates a FIFO file at the path specified" do
+ File.mkfifo(@path)
+ File.ftype(@path).should == "fifo"
+ end
+ end
+
+ context "when path passed is not a String value" do
+ it "raises a TypeError" do
+ lambda { File.mkfifo(:"/tmp/fifo") }.should raise_error(TypeError)
+ end
+ end
+
+ context "when path does not exist" do
+ it "raises an Errno::ENOENT exception" do
+ lambda { File.mkfifo("/bogus/path") }.should raise_error(Errno::ENOENT)
+ end
+ end
+
+ it "creates a FIFO file at the passed path" do
+ File.mkfifo(@path.to_s)
+ File.ftype(@path).should == "fifo"
+ end
+
+ it "creates a FIFO file with passed mode & ~umask" do
+ File.mkfifo(@path, 0755)
+ File.stat(@path).mode.should == 010755 & ~File.umask
+ end
+
+ it "creates a FIFO file with a default mode of 0666 & ~umask" do
+ File.mkfifo(@path)
+ File.stat(@path).mode.should == 010666 & ~File.umask
+ end
+
+ it "returns 0 after creating the FIFO file" do
+ File.mkfifo(@path).should == 0
+ end
+ end
+end
diff --git a/spec/ruby/core/file/mtime_spec.rb b/spec/ruby/core/file/mtime_spec.rb
new file mode 100644
index 0000000000..4c26cb5dac
--- /dev/null
+++ b/spec/ruby/core/file/mtime_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+
+describe "File.mtime" do
+ before :each do
+ @filename = tmp('i_exist')
+ touch(@filename) { @mtime = Time.now }
+ end
+
+ after :each do
+ rm_r @filename
+ end
+
+ it "returns the modification Time of the file" do
+ File.mtime(@filename).should be_kind_of(Time)
+ File.mtime(@filename).should be_close(@mtime, 2.0)
+ end
+
+ platform_is :linux 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
+ end
+ end
+ end
+
+ it "raises an Errno::ENOENT exception if the file is not found" do
+ lambda { File.mtime('bogus') }.should raise_error(Errno::ENOENT)
+ end
+end
+
+describe "File#mtime" do
+ before :each do
+ @filename = tmp('i_exist')
+ @f = File.open(@filename, 'w')
+ end
+
+ after :each do
+ @f.close
+ rm_r @filename
+ end
+
+ it "returns the modification Time of the file" do
+ @f.mtime.should be_kind_of(Time)
+ end
+
+end
diff --git a/spec/ruby/core/file/new_spec.rb b/spec/ruby/core/file/new_spec.rb
new file mode 100644
index 0000000000..815ecd9d36
--- /dev/null
+++ b/spec/ruby/core/file/new_spec.rb
@@ -0,0 +1,162 @@
+require_relative '../../spec_helper'
+require_relative 'shared/open'
+
+describe "File.new" do
+ before :each do
+ @file = tmp('test.txt')
+ @fh = nil
+ @flags = File::CREAT | File::TRUNC | File::WRONLY
+ touch @file
+ end
+
+ after :each do
+ @fh.close if @fh
+ rm_r @file
+ end
+
+ it "returns a new File with mode string" do
+ @fh = File.new(@file, 'w')
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "returns a new File with mode num" do
+ @fh = File.new(@file, @flags)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "returns a new File with modus num and permissions" do
+ rm_r @file
+ File.umask(0011)
+ @fh = File.new(@file, @flags, 0755)
+ @fh.should be_kind_of(File)
+ platform_is_not :windows do
+ File.stat(@file).mode.to_s(8).should == "100744"
+ end
+ File.exist?(@file).should == true
+ end
+
+ it "creates the file and returns writable descriptor when called with 'w' mode and r-o permissions" do
+ # it should be possible to write to such a file via returned descriptior,
+ # even though the file permissions are r-r-r.
+
+ rm_r @file
+ begin
+ f = File.new(@file, "w", 0444)
+ lambda { f.puts("test") }.should_not raise_error(IOError)
+ ensure
+ f.close
+ end
+ File.exist?(@file).should == true
+ File.read(@file).should == "test\n"
+ end
+
+ platform_is_not :windows do
+ it "opens the existing file, does not change permissions even when they are specified" do
+ File.chmod(0644, @file) # r-w perms
+ orig_perms = File.stat(@file).mode & 0777
+ begin
+ f = File.new(@file, "w", 0444) # r-o perms, but they should be ignored
+ f.puts("test")
+ ensure
+ f.close
+ end
+ perms = File.stat(@file).mode & 0777
+ perms.should == orig_perms
+
+ # it should be still possible to read from the file
+ File.read(@file).should == "test\n"
+ end
+ end
+
+ it "returns a new File with modus fd" do
+ @fh = File.new(@file)
+ fh_copy = File.new(@fh.fileno)
+ fh_copy.autoclose = false
+ fh_copy.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "creates a new file when use File::EXCL mode" do
+ @fh = File.new(@file, File::EXCL)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "raises an Errorno::EEXIST if the file exists when create a new file with File::CREAT|File::EXCL" do
+ lambda { @fh = File.new(@file, File::CREAT|File::EXCL) }.should raise_error(Errno::EEXIST)
+ end
+
+ it "creates a new file when use File::WRONLY|File::APPEND mode" do
+ @fh = File.new(@file, File::WRONLY|File::APPEND)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "returns a new File when use File::APPEND mode" do
+ @fh = File.new(@file, File::APPEND)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "returns a new File when use File::RDONLY|File::APPEND mode" do
+ @fh = File.new(@file, File::RDONLY|File::APPEND)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "returns a new File when use File::RDONLY|File::WRONLY mode" do
+ @fh = File.new(@file, File::RDONLY|File::WRONLY)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+
+ it "creates a new file when use File::WRONLY|File::TRUNC mode" do
+ @fh = File.new(@file, File::WRONLY|File::TRUNC)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "coerces filename using to_str" do
+ name = mock("file")
+ name.should_receive(:to_str).and_return(@file)
+ @fh = File.new(name, "w")
+ File.exist?(@file).should == true
+ end
+
+ it "coerces filename using #to_path" do
+ name = mock("file")
+ name.should_receive(:to_path).and_return(@file)
+ @fh = File.new(name, "w")
+ File.exist?(@file).should == true
+ end
+
+ it "raises a TypeError if the first parameter can't be coerced to a string" do
+ lambda { File.new(true) }.should raise_error(TypeError)
+ lambda { File.new(false) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the first parameter is nil" do
+ lambda { File.new(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises an Errno::EBADF if the first parameter is an invalid file descriptor" do
+ lambda { File.new(-1) }.should raise_error(Errno::EBADF)
+ end
+
+ platform_is_not :windows do
+ it "can't alter mode or permissions when opening a file" do
+ @fh = File.new(@file)
+ lambda {
+ f = File.new(@fh.fileno, @flags)
+ f.autoclose = false
+ }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ platform_is_not :windows do
+ it_behaves_like :open_directory, :new
+ end
+end
diff --git a/spec/ruby/core/file/null_spec.rb b/spec/ruby/core/file/null_spec.rb
new file mode 100644
index 0000000000..355b72b799
--- /dev/null
+++ b/spec/ruby/core/file/null_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "File::NULL" do
+ platform_is :windows do
+ it "returns NUL as a string" do
+ File::NULL.should == 'NUL'
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns /dev/null as a string" do
+ File::NULL.should == '/dev/null'
+ end
+ end
+end
diff --git a/spec/ruby/core/file/open_spec.rb b/spec/ruby/core/file/open_spec.rb
new file mode 100644
index 0000000000..c6a0b68233
--- /dev/null
+++ b/spec/ruby/core/file/open_spec.rb
@@ -0,0 +1,680 @@
+# encoding: utf-8
+
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'shared/open'
+
+describe "File.open" do
+ before :all do
+ @file = tmp("file_open.txt")
+ @unicode_path = tmp("ã“ã‚“ã«ã¡ã¯.txt")
+ @nonexistent = tmp("fake.txt")
+ rm_r @file, @nonexistent
+ end
+
+ before :each do
+ ScratchPad.record []
+
+ @fh = @fd = nil
+ @flags = File::CREAT | File::TRUNC | File::WRONLY
+ touch @file
+ end
+
+ after :each do
+ @fh.close if @fh and not @fh.closed?
+ rm_r @file, @unicode_path, @nonexistent
+ end
+
+ describe "with a block" do
+ it "does not raise error when file is closed inside the block" do
+ @fh = File.open(@file) { |fh| fh.close; fh }
+ @fh.closed?.should == true
+ end
+
+ it "invokes close on an opened file when exiting the block" do
+ File.open(@file, 'r') { |f| FileSpecs.make_closer f }
+
+ ScratchPad.recorded.should == [:file_opened, :file_closed]
+ end
+
+ it "propagates non-StandardErrors produced by close" do
+ lambda {
+ File.open(@file, 'r') { |f| FileSpecs.make_closer f, Exception }
+ }.should raise_error(Exception)
+
+ ScratchPad.recorded.should == [:file_opened, :file_closed]
+ end
+
+ it "propagates StandardErrors produced by close" do
+ lambda {
+ File.open(@file, 'r') { |f| FileSpecs.make_closer f, StandardError }
+ }.should raise_error(StandardError)
+
+ ScratchPad.recorded.should == [:file_opened, :file_closed]
+ end
+
+ it "does not propagate IOError with 'closed stream' message produced by close" do
+ File.open(@file, 'r') { |f| FileSpecs.make_closer f, IOError.new('closed stream') }
+
+ ScratchPad.recorded.should == [:file_opened, :file_closed]
+ end
+ end
+
+ it "opens the file (basic case)" do
+ @fh = File.open(@file)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens the file with unicode characters" do
+ @fh = File.open(@unicode_path, "w")
+ @fh.should be_kind_of(File)
+ File.exist?(@unicode_path).should == true
+ end
+
+ it "opens a file when called with a block" do
+ File.open(@file) { |fh| }
+ File.exist?(@file).should == true
+ end
+
+ it "opens with mode string" do
+ @fh = File.open(@file, 'w')
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file with mode string and block" do
+ File.open(@file, 'w') { |fh| }
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file with mode num" do
+ @fh = File.open(@file, @flags)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file with mode num and block" do
+ File.open(@file, 'w') { |fh| }
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file with mode and permission as nil" do
+ @fh = File.open(@file, nil, nil)
+ @fh.should be_kind_of(File)
+ end
+
+ # For this test we delete the file first to reset the perms
+ it "opens the file when passed mode, num and permissions" do
+ rm_r @file
+ File.umask(0011)
+ @fh = File.open(@file, @flags, 0755)
+ @fh.should be_kind_of(File)
+ platform_is_not :windows do
+ @fh.lstat.mode.to_s(8).should == "100744"
+ end
+ File.exist?(@file).should == true
+ end
+
+ # For this test we delete the file first to reset the perms
+ it "opens the file when passed mode, num, permissions and block" do
+ rm_r @file
+ File.umask(0022)
+ File.open(@file, "w", 0755){ |fh| }
+ platform_is_not :windows do
+ File.stat(@file).mode.to_s(8).should == "100755"
+ end
+ File.exist?(@file).should == true
+ end
+
+ it "creates the file and returns writable descriptor when called with 'w' mode and r-o permissions" do
+ # it should be possible to write to such a file via returned descriptior,
+ # even though the file permissions are r-r-r.
+
+ File.open(@file, "w", 0444) { |f| f.write("test") }
+ File.read(@file).should == "test"
+ end
+
+ platform_is_not :windows do
+ it "opens the existing file, does not change permissions even when they are specified" do
+ File.chmod(0664, @file)
+ orig_perms = File.stat(@file).mode.to_s(8)
+ File.open(@file, "w", 0444) { |f| f.write("test") }
+
+ File.stat(@file).mode.to_s(8).should == orig_perms
+ File.read(@file).should == "test"
+ end
+ end
+
+ platform_is_not :windows do
+ as_user do
+ it "creates a new write-only file when invoked with 'w' and '0222'" do
+ rm_r @file
+ File.open(@file, 'w', 0222) {}
+ File.readable?(@file).should == false
+ File.writable?(@file).should == true
+ end
+ end
+ end
+
+ it "opens the file when call with fd" do
+ @fh = File.open(@file)
+ fh_copy = File.open(@fh.fileno)
+ fh_copy.autoclose = false
+ fh_copy.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file with a file descriptor d and a block" do
+ @fh = File.open(@file)
+ @fh.should be_kind_of(File)
+
+ lambda {
+ File.open(@fh.fileno) do |fh|
+ @fd = fh.fileno
+ @fh.close
+ end
+ }.should raise_error(Errno::EBADF)
+ lambda { File.open(@fd) }.should raise_error(Errno::EBADF)
+
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file that no exists when use File::WRONLY mode" do
+ lambda { File.open(@nonexistent, File::WRONLY) }.should raise_error(Errno::ENOENT)
+ end
+
+ it "opens a file that no exists when use File::RDONLY mode" do
+ lambda { File.open(@nonexistent, File::RDONLY) }.should raise_error(Errno::ENOENT)
+ end
+
+ it "opens a file that no exists when use 'r' mode" do
+ lambda { File.open(@nonexistent, 'r') }.should raise_error(Errno::ENOENT)
+ end
+
+ it "opens a file that no exists when use File::EXCL mode" do
+ lambda { File.open(@nonexistent, File::EXCL) }.should raise_error(Errno::ENOENT)
+ end
+
+ it "opens a file that no exists when use File::NONBLOCK mode" do
+ lambda { File.open(@nonexistent, File::NONBLOCK) }.should raise_error(Errno::ENOENT)
+ end
+
+ platform_is_not :openbsd, :windows do
+ it "opens a file that no exists when use File::TRUNC mode" do
+ lambda { File.open(@nonexistent, File::TRUNC) }.should raise_error(Errno::ENOENT)
+ end
+ end
+
+ platform_is :openbsd, :windows do
+ it "does not open a file that does no exists when using File::TRUNC mode" do
+ lambda { File.open(@nonexistent, File::TRUNC) }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ platform_is_not :windows do
+ it "opens a file that no exists when use File::NOCTTY mode" do
+ lambda { File.open(@nonexistent, File::NOCTTY) }.should raise_error(Errno::ENOENT)
+ end
+ end
+
+ it "opens a file that no exists when use File::CREAT mode" do
+ @fh = File.open(@nonexistent, File::CREAT) { |f| f }
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file that no exists when use 'a' mode" do
+ @fh = File.open(@nonexistent, 'a') { |f| f }
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file that no exists when use 'w' mode" do
+ @fh = File.open(@nonexistent, 'w') { |f| f }
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ # Check the grants associated to the differents open modes combinations.
+ it "raises an ArgumentError exception when call with an unknown mode" do
+ lambda { File.open(@file, "q") }.should raise_error(ArgumentError)
+ end
+
+ it "can read in a block when call open with RDONLY mode" do
+ File.open(@file, File::RDONLY) do |f|
+ f.gets.should == nil
+ end
+ end
+
+ it "can read in a block when call open with 'r' mode" do
+ File.open(@file, "r") do |f|
+ f.gets.should == nil
+ end
+ end
+
+ it "raises an IO exception when write in a block opened with RDONLY mode" do
+ File.open(@file, File::RDONLY) do |f|
+ lambda { f.puts "writing ..." }.should raise_error(IOError)
+ end
+ end
+
+ it "raises an IO exception when write in a block opened with 'r' mode" do
+ File.open(@file, "r") do |f|
+ lambda { f.puts "writing ..." }.should raise_error(IOError)
+ end
+ end
+
+ it "can't write in a block when call open with File::WRONLY||File::RDONLY mode" do
+ File.open(@file, File::WRONLY|File::RDONLY ) do |f|
+ f.puts("writing").should == nil
+ end
+ end
+
+ it "can't read in a block when call open with File::WRONLY||File::RDONLY mode" do
+ lambda {
+ File.open(@file, File::WRONLY|File::RDONLY ) do |f|
+ f.gets.should == nil
+ end
+ }.should raise_error(IOError)
+ end
+
+ it "can write in a block when call open with WRONLY mode" do
+ File.open(@file, File::WRONLY) do |f|
+ f.puts("writing").should == nil
+ end
+ end
+
+ it "can write in a block when call open with 'w' mode" do
+ File.open(@file, "w") do |f|
+ f.puts("writing").should == nil
+ end
+ end
+
+ it "raises an IOError when read in a block opened with WRONLY mode" do
+ File.open(@file, File::WRONLY) do |f|
+ lambda { f.gets }.should raise_error(IOError)
+ end
+ end
+
+ it "raises an IOError when read in a block opened with 'w' mode" do
+ File.open(@file, "w") do |f|
+ lambda { f.gets }.should raise_error(IOError)
+ end
+ end
+
+ it "raises an IOError when read in a block opened with 'a' mode" do
+ File.open(@file, "a") do |f|
+ lambda { f.gets }.should raise_error(IOError)
+ end
+ end
+
+ it "raises an IOError when read in a block opened with 'a' mode" do
+ File.open(@file, "a") do |f|
+ f.puts("writing").should == nil
+ lambda { f.gets }.should raise_error(IOError)
+ end
+ end
+
+ it "raises an IOError when read in a block opened with 'a' mode" do
+ File.open(@file, File::WRONLY|File::APPEND ) do |f|
+ lambda { f.gets }.should raise_error(IOError)
+ end
+ end
+
+ it "raises an IOError when read in a block opened with File::WRONLY|File::APPEND mode" do
+ File.open(@file, File::WRONLY|File::APPEND ) do |f|
+ f.puts("writing").should == nil
+ lambda { f.gets }.should raise_error(IOError)
+ end
+ end
+
+ it "raises an IOError when read in a block opened with File::RDONLY|File::APPEND mode" do
+ lambda {
+ File.open(@file, File::RDONLY|File::APPEND ) do |f|
+ f.puts("writing")
+ end
+ }.should raise_error(IOError)
+ end
+
+ it "can read and write in a block when call open with RDWR mode" do
+ File.open(@file, File::RDWR) do |f|
+ f.gets.should == nil
+ f.puts("writing").should == nil
+ f.rewind
+ f.gets.should == "writing\n"
+ end
+ end
+
+ it "can't read in a block when call open with File::EXCL mode" do
+ lambda {
+ File.open(@file, File::EXCL) do |f|
+ f.puts("writing").should == nil
+ end
+ }.should raise_error(IOError)
+ end
+
+ it "can read in a block when call open with File::EXCL mode" do
+ File.open(@file, File::EXCL) do |f|
+ f.gets.should == nil
+ end
+ end
+
+ it "can read and write in a block when call open with File::RDWR|File::EXCL mode" do
+ File.open(@file, File::RDWR|File::EXCL) do |f|
+ f.gets.should == nil
+ f.puts("writing").should == nil
+ f.rewind
+ f.gets.should == "writing\n"
+ end
+ end
+
+ it "raises an Errorno::EEXIST if the file exists when open with File::CREAT|File::EXCL" do
+ lambda {
+ File.open(@file, File::CREAT|File::EXCL) do |f|
+ f.puts("writing")
+ end
+ }.should raise_error(Errno::EEXIST)
+ end
+
+ it "creates a new file when use File::WRONLY|File::APPEND mode" do
+ @fh = File.open(@file, File::WRONLY|File::APPEND)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file when use File::WRONLY|File::APPEND mode" do
+ File.open(@file, File::WRONLY) do |f|
+ f.puts("hello file")
+ end
+ File.open(@file, File::RDWR|File::APPEND) do |f|
+ f.puts("bye file")
+ f.rewind
+ f.gets.should == "hello file\n"
+ f.gets.should == "bye file\n"
+ f.gets.should == nil
+ end
+ end
+
+ it "raises an IOError if the file exists when open with File::RDONLY|File::APPEND" do
+ lambda {
+ File.open(@file, File::RDONLY|File::APPEND) do |f|
+ f.puts("writing").should == nil
+ end
+ }.should raise_error(IOError)
+ end
+
+ platform_is_not :openbsd, :windows do
+ it "truncates the file when passed File::TRUNC mode" do
+ File.open(@file, File::RDWR) { |f| f.puts "hello file" }
+ @fh = File.open(@file, File::TRUNC)
+ @fh.gets.should == nil
+ end
+
+ it "can't read in a block when call open with File::TRUNC mode" do
+ File.open(@file, File::TRUNC) do |f|
+ f.gets.should == nil
+ end
+ end
+ end
+
+ it "opens a file when use File::WRONLY|File::TRUNC mode" do
+ fh1 = File.open(@file, "w")
+ begin
+ @fh = File.open(@file, File::WRONLY|File::TRUNC)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ ensure
+ fh1.close
+ end
+ end
+
+ platform_is_not :openbsd, :windows do
+ it "can't write in a block when call open with File::TRUNC mode" do
+ lambda {
+ File.open(@file, File::TRUNC) do |f|
+ f.puts("writing")
+ end
+ }.should raise_error(IOError)
+ end
+
+ it "raises an Errorno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do
+ lambda {
+ File.open(@file, File::RDONLY|File::TRUNC) do |f|
+ f.puts("writing").should == nil
+ end
+ }.should raise_error(IOError)
+ end
+ end
+
+ platform_is :openbsd, :windows do
+ it "can't write in a block when call open with File::TRUNC mode" do
+ lambda {
+ File.open(@file, File::TRUNC) do |f|
+ f.puts("writing")
+ end
+ }.should raise_error(Errno::EINVAL)
+ end
+
+ it "raises an Errorno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do
+ lambda {
+ File.open(@file, File::RDONLY|File::TRUNC) do |f|
+ f.puts("writing").should == nil
+ end
+ }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ platform_is_not :windows do
+ as_user do
+ it "raises an Errno::EACCES when opening non-permitted file" do
+ @fh = File.open(@file, "w")
+ @fh.chmod(000)
+ lambda { fh1 = File.open(@file); fh1.close }.should raise_error(Errno::EACCES)
+ end
+ end
+ end
+
+ as_user do
+ it "raises an Errno::EACCES when opening read-only file" do
+ @fh = File.open(@file, "w")
+ @fh.chmod(0444)
+ lambda { File.open(@file, "w") }.should raise_error(Errno::EACCES)
+ end
+ end
+
+ it "opens a file for binary read" do
+ @fh = File.open(@file, "rb")
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file for binary write" do
+ @fh = File.open(@file, "wb")
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file for read-write and truncate the file" do
+ File.open(@file, "w") { |f| f.puts "testing" }
+ File.size(@file).should > 0
+ File.open(@file, "w+") do |f|
+ f.pos.should == 0
+ f.eof?.should == true
+ end
+ File.size(@file).should == 0
+ end
+
+ it "opens a file for binary read-write starting at the beginning of the file" do
+ File.open(@file, "w") { |f| f.puts "testing" }
+ File.size(@file).should > 0
+ File.open(@file, "rb+") do |f|
+ f.pos.should == 0
+ f.eof?.should == false
+ end
+ end
+
+ it "opens a file for binary read-write and truncate the file" do
+ File.open(@file, "w") { |f| f.puts "testing" }
+ File.size(@file).should > 0
+ File.open(@file, "wb+") do |f|
+ f.pos.should == 0
+ f.eof?.should == true
+ end
+ File.size(@file).should == 0
+ end
+
+ platform_is :linux do
+ guard -> { defined?(File::TMPFILE) } do
+ it "creates an unnamed temporary file with File::TMPFILE" do
+ dir = tmp("tmpfilespec")
+ mkdir_p dir
+ begin
+ Dir["#{dir}/*"].should == []
+ File.open(dir, "r+", flags: File::TMPFILE) do |io|
+ io.write("ruby")
+ io.flush
+ io.rewind
+ io.read.should == "ruby"
+ Dir["#{dir}/*"].should == []
+ end
+ rescue Errno::EOPNOTSUPP, Errno::EINVAL, Errno::EISDIR
+ # EOPNOTSUPP: no support from the filesystem
+ # EINVAL: presumably bug in glibc
+ 1.should == 1
+ ensure
+ rm_r dir
+ end
+ end
+ end
+ end
+
+ it "raises a TypeError if passed a filename that is not a String or Integer type" do
+ lambda { File.open(true) }.should raise_error(TypeError)
+ lambda { File.open(false) }.should raise_error(TypeError)
+ lambda { File.open(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a SystemCallError if passed an invalid Integer type" do
+ lambda { File.open(-1) }.should raise_error(SystemCallError)
+ end
+
+ it "raises an ArgumentError if passed the wrong number of arguments" do
+ lambda { File.open(@file, File::CREAT, 0755, 'test') }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if passed an invalid string for mode" do
+ lambda { File.open(@file, 'fake') }.should raise_error(ArgumentError)
+ end
+
+ it "defaults external_encoding to ASCII-8BIT for binary modes" do
+ File.open(@file, 'rb') {|f| f.external_encoding.should == Encoding::ASCII_8BIT}
+ File.open(@file, 'wb+') {|f| f.external_encoding.should == Encoding::ASCII_8BIT}
+ end
+
+ it "uses the second argument as an options Hash" do
+ @fh = File.open(@file, mode: "r")
+ @fh.should be_an_instance_of(File)
+ end
+
+ it "calls #to_hash to convert the second argument to a Hash" do
+ options = mock("file open options")
+ options.should_receive(:to_hash).and_return({ mode: "r" })
+
+ @fh = File.open(@file, options)
+ end
+
+ it "accepts extra flags as a keyword argument and combine with a string mode" do
+ lambda {
+ File.open(@file, "w", flags: File::EXCL) { }
+ }.should raise_error(Errno::EEXIST)
+
+ lambda {
+ File.open(@file, mode: "w", flags: File::EXCL) { }
+ }.should raise_error(Errno::EEXIST)
+ end
+
+ it "accepts extra flags as a keyword argument and combine with an integer mode" do
+ lambda {
+ File.open(@file, File::WRONLY | File::CREAT, flags: File::EXCL) { }
+ }.should raise_error(Errno::EEXIST)
+ end
+
+ platform_is_not :windows do
+ describe "on a FIFO" do
+ before :each do
+ @fifo = tmp("File_open_fifo")
+ File.mkfifo(@fifo)
+ end
+
+ after :each do
+ rm_r @fifo
+ end
+
+ it "opens it as a normal file" do
+ file_w, file_r, read_bytes, written_length = nil
+
+ # open in threads, due to blocking open and writes
+ writer = Thread.new do
+ file_w = File.open(@fifo, 'w')
+ written_length = file_w.syswrite('hello')
+ end
+ reader = Thread.new do
+ file_r = File.open(@fifo, 'r')
+ read_bytes = file_r.sysread(5)
+ end
+
+ begin
+ writer.join
+ reader.join
+
+ written_length.should == 5
+ read_bytes.should == 'hello'
+ ensure
+ file_w.close if file_w
+ file_r.close if file_r
+ end
+ end
+ end
+ end
+
+end
+
+describe "File.open when passed a file descriptor" do
+ before do
+ @content = "File#open when passed a file descriptor"
+ @name = tmp("file_open_with_fd.txt")
+ @fd = new_fd @name, fmode("w:utf-8")
+ @file = nil
+ end
+
+ after do
+ @file.close if @file and not @file.closed?
+ rm_r @name
+ end
+
+ it "opens a file" do
+ @file = File.open(@fd, "w")
+ @file.should be_an_instance_of(File)
+ @file.fileno.should equal(@fd)
+ @file.write @content
+ @file.flush
+ File.read(@name).should == @content
+ end
+
+ it "opens a file when passed a block" do
+ @file = File.open(@fd, "w") do |f|
+ f.should be_an_instance_of(File)
+ f.fileno.should equal(@fd)
+ f.write @content
+ f
+ end
+ File.read(@name).should == @content
+ end
+end
+
+platform_is_not :windows do
+ describe "File.open" do
+ it_behaves_like :open_directory, :open
+ end
+end
diff --git a/spec/ruby/core/file/owned_spec.rb b/spec/ruby/core/file/owned_spec.rb
new file mode 100644
index 0000000000..06d6796da9
--- /dev/null
+++ b/spec/ruby/core/file/owned_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/owned'
+
+describe "File.owned?" do
+ it_behaves_like :file_owned, :owned?, File
+end
+
+describe "File.owned?" do
+ before :each do
+ @filename = tmp("i_exist")
+ touch(@filename)
+ end
+
+ after :each do
+ rm_r @filename
+ end
+
+ it "returns false if file does not exist" do
+ File.owned?("I_am_a_bogus_file").should == false
+ end
+
+ it "returns true if the file exist and is owned by the user" do
+ File.owned?(@filename).should == true
+ end
+
+ platform_is_not :windows do
+ as_user do
+ it "returns false when the file is not owned by the user" do
+ system_file = '/etc/passwd'
+ File.owned?(system_file).should == false
+ end
+ end
+ end
+
+end
diff --git a/spec/ruby/core/file/path_spec.rb b/spec/ruby/core/file/path_spec.rb
new file mode 100644
index 0000000000..dfa0c4ec02
--- /dev/null
+++ b/spec/ruby/core/file/path_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require_relative 'shared/path'
+
+describe "File#path" do
+ it_behaves_like :file_path, :path
+end
+
+describe "File.path" do
+ before :each do
+ @name = tmp("file_path")
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "returns the string argument without any change" do
+ File.path("abc").should == "abc"
+ File.path("./abc").should == "./abc"
+ File.path("../abc").should == "../abc"
+ File.path("/./a/../bc").should == "/./a/../bc"
+ end
+
+ it "returns path for File argument" do
+ File.open(@name, "w") do |f|
+ File.path(f).should == @name
+ end
+ end
+
+ it "returns path for Pathname argument" do
+ require "pathname"
+ File.path(Pathname.new(@name)).should == @name
+ end
+
+ it "calls #to_path for non-string argument and returns result" do
+ path = mock("path")
+ path.should_receive(:to_path).and_return("abc")
+ File.path(path).should == "abc"
+ end
+end
diff --git a/spec/ruby/core/file/pipe_spec.rb b/spec/ruby/core/file/pipe_spec.rb
new file mode 100644
index 0000000000..01d72dbe85
--- /dev/null
+++ b/spec/ruby/core/file/pipe_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/pipe'
+
+describe "File.pipe?" do
+ it_behaves_like :file_pipe, :pipe?, File
+end
+
+describe "File.pipe?" do
+ it "returns false if file does not exist" do
+ File.pipe?("I_am_a_bogus_file").should == false
+ end
+
+ it "returns false if the file is not a pipe" do
+ filename = tmp("i_exist")
+ touch(filename)
+
+ File.pipe?(filename).should == false
+
+ rm_r filename
+ end
+
+ platform_is_not :windows do
+ it "returns true if the file is a pipe" do
+ filename = tmp("i_am_a_pipe")
+ File.mkfifo(filename)
+
+ File.pipe?(filename).should == true
+
+ rm_r filename
+ end
+ end
+end
diff --git a/spec/ruby/core/file/printf_spec.rb b/spec/ruby/core/file/printf_spec.rb
new file mode 100644
index 0000000000..95ce3589c5
--- /dev/null
+++ b/spec/ruby/core/file/printf_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require_relative '../kernel/shared/sprintf'
+
+describe "File#printf" do
+ it_behaves_like :kernel_sprintf, -> (format, *args) {
+ begin
+ @filename = tmp("printf.txt")
+
+ File.open(@filename, "w", encoding: "utf-8") do |f|
+ f.printf(format, *args)
+ end
+
+ File.read(@filename, encoding: "utf-8")
+ ensure
+ rm_r @filename
+ end
+ }
+end
diff --git a/spec/ruby/core/file/read_spec.rb b/spec/ruby/core/file/read_spec.rb
new file mode 100644
index 0000000000..67a3325cbd
--- /dev/null
+++ b/spec/ruby/core/file/read_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/read'
+
+describe "File.read" do
+ it_behaves_like :file_read_directory, :read, File
+end
diff --git a/spec/ruby/core/file/readable_real_spec.rb b/spec/ruby/core/file/readable_real_spec.rb
new file mode 100644
index 0000000000..524466cd96
--- /dev/null
+++ b/spec/ruby/core/file/readable_real_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/readable_real'
+
+describe "File.readable_real?" do
+ it_behaves_like :file_readable_real, :readable_real?, File
+ it_behaves_like :file_readable_real_missing, :readable_real?, File
+end
diff --git a/spec/ruby/core/file/readable_spec.rb b/spec/ruby/core/file/readable_spec.rb
new file mode 100644
index 0000000000..ed75a23f39
--- /dev/null
+++ b/spec/ruby/core/file/readable_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/readable'
+
+describe "File.readable?" do
+ it_behaves_like :file_readable, :readable?, File
+ it_behaves_like :file_readable_missing, :readable?, File
+end
diff --git a/spec/ruby/core/file/readlink_spec.rb b/spec/ruby/core/file/readlink_spec.rb
new file mode 100644
index 0000000000..7b1fe74047
--- /dev/null
+++ b/spec/ruby/core/file/readlink_spec.rb
@@ -0,0 +1,86 @@
+require_relative '../../spec_helper'
+
+describe "File.readlink" do
+ # symlink/readlink are not supported on Windows
+ platform_is_not :windows do
+ describe "with absolute paths" do
+ before :each do
+ @file = tmp('file_readlink.txt')
+ @link = tmp('file_readlink.lnk')
+
+ File.symlink(@file, @link)
+ end
+
+ after :each do
+ rm_r @file, @link
+ end
+
+ it "returns the name of the file referenced by the given link" do
+ touch @file
+ File.readlink(@link).should == @file
+ end
+
+ it "returns the name of the file referenced by the given link when the file does not exist" do
+ File.readlink(@link).should == @file
+ end
+
+ it "raises an Errno::ENOENT if there is no such file" do
+ # TODO: missing_file
+ lambda { File.readlink("/this/surely/doesnt/exist") }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises an Errno::EINVAL if called with a normal file" do
+ touch @file
+ lambda { File.readlink(@file) }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ describe "with paths containing unicode characters" do
+ before :each do
+ @file = tmp('tàrget.txt')
+ @link = tmp('lïnk.lnk')
+ File.symlink(@file, @link)
+ end
+
+ after :each do
+ rm_r @file, @link
+ end
+
+ it "returns the name of the file referenced by the given link" do
+ touch @file
+ result = File.readlink(@link)
+ result.encoding.should equal Encoding.find('filesystem')
+ result.should == @file.dup.force_encoding(Encoding.find('filesystem'))
+ end
+ end
+
+ describe "when changing the working directory" do
+ before :each do
+ @cwd = Dir.pwd
+ @tmpdir = tmp("/readlink")
+ Dir.mkdir @tmpdir
+ Dir.chdir @tmpdir
+
+ @link = 'readlink_link'
+ @file = 'readlink_file'
+
+ File.symlink(@file, @link)
+ end
+
+ after :each do
+ rm_r @file, @link
+ Dir.chdir @cwd
+ Dir.rmdir @tmpdir
+ end
+
+ it "returns the name of the file referenced by the given link" do
+ touch @file
+ File.readlink(@link).should == @file
+ end
+
+ it "returns the name of the file referenced by the given link when the file does not exist" do
+ File.readlink(@link).should == @file
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/realdirpath_spec.rb b/spec/ruby/core/file/realdirpath_spec.rb
new file mode 100644
index 0000000000..9c90d7330f
--- /dev/null
+++ b/spec/ruby/core/file/realdirpath_spec.rb
@@ -0,0 +1,104 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ describe "File.realdirpath" do
+ before :each do
+ @real_dir = tmp('dir_realdirpath_real')
+ @fake_dir = tmp('dir_realdirpath_fake')
+ @link_dir = tmp('dir_realdirpath_link')
+
+ mkdir_p @real_dir
+ File.symlink(@real_dir, @link_dir)
+
+ @file = File.join(@real_dir, 'file')
+ @link = File.join(@link_dir, 'link')
+
+ touch @file
+ File.symlink(@file, @link)
+
+ @fake_file_in_real_dir = File.join(@real_dir, 'fake_file_in_real_dir')
+ @fake_file_in_fake_dir = File.join(@fake_dir, 'fake_file_in_fake_dir')
+ @fake_link_to_real_dir = File.join(@link_dir, 'fake_link_to_real_dir')
+ @fake_link_to_fake_dir = File.join(@link_dir, 'fake_link_to_fake_dir')
+
+ File.symlink(@fake_file_in_real_dir, @fake_link_to_real_dir)
+ File.symlink(@fake_file_in_fake_dir, @fake_link_to_fake_dir)
+
+ @dir_for_relative_link = File.join(@real_dir, 'dir1')
+ mkdir_p @dir_for_relative_link
+
+ @relative_path_to_file = File.join('..', 'file')
+ @relative_symlink = File.join(@dir_for_relative_link, 'link')
+ File.symlink(@relative_path_to_file, @relative_symlink)
+ end
+
+ after :each do
+ rm_r @file, @link, @fake_link_to_real_dir, @fake_link_to_fake_dir, @real_dir, @link_dir
+ end
+
+ it "returns '/' when passed '/'" do
+ File.realdirpath('/').should == '/'
+ end
+
+ it "returns the real (absolute) pathname not containing symlinks" do
+ File.realdirpath(@link).should == @file
+ end
+
+ it "uses base directory for interpreting relative pathname" do
+ File.realdirpath(File.basename(@link), @link_dir).should == @file
+ end
+
+ it "uses current directory for interpreting relative pathname" do
+ Dir.chdir @link_dir do
+ File.realdirpath(File.basename(@link)).should == @file
+ end
+ end
+
+ it "uses link directory for expanding relative links" do
+ File.realdirpath(@relative_symlink).should == @file
+ end
+
+ it "raises an Errno::ELOOP if the symlink points to itself" do
+ File.unlink @link
+ File.symlink(@link, @link)
+ lambda { File.realdirpath(@link) }.should raise_error(Errno::ELOOP)
+ end
+
+ it "returns the real (absolute) pathname if the file is absent" do
+ File.realdirpath(@fake_file_in_real_dir).should == @fake_file_in_real_dir
+ end
+
+ it "raises Errno::ENOENT if the directory is absent" do
+ lambda { File.realdirpath(@fake_file_in_fake_dir) }.should raise_error(Errno::ENOENT)
+ end
+
+ it "returns the real (absolute) pathname if the symlink points to an absent file" do
+ File.realdirpath(@fake_link_to_real_dir).should == @fake_file_in_real_dir
+ end
+
+ it "raises Errno::ENOENT if the symlink points to an absent directory" do
+ lambda { File.realdirpath(@fake_link_to_fake_dir) }.should raise_error(Errno::ENOENT)
+ end
+ end
+end
+
+platform_is :windows do
+ describe "File.realdirpath" do
+ before :each do
+ @file = tmp("realdirpath")
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the same path" do
+ touch @file
+ File.realdirpath(@file).should == @file
+ end
+
+ it "returns the same path even if the last component does not exist" do
+ File.realdirpath(@file).should == @file
+ end
+ end
+end
diff --git a/spec/ruby/core/file/realpath_spec.rb b/spec/ruby/core/file/realpath_spec.rb
new file mode 100644
index 0000000000..d7bb842759
--- /dev/null
+++ b/spec/ruby/core/file/realpath_spec.rb
@@ -0,0 +1,88 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ describe "File.realpath" do
+ before :each do
+ @real_dir = tmp('dir_realpath_real')
+ @link_dir = tmp('dir_realpath_link')
+
+ mkdir_p @real_dir
+ File.symlink(@real_dir, @link_dir)
+
+ @file = File.join(@real_dir, 'file')
+ @link = File.join(@link_dir, 'link')
+
+ touch @file
+ File.symlink(@file, @link)
+
+ @fake_file = File.join(@real_dir, 'fake_file')
+ @fake_link = File.join(@link_dir, 'fake_link')
+
+ File.symlink(@fake_file, @fake_link)
+
+ @dir_for_relative_link = File.join(@real_dir, 'dir1')
+ mkdir_p @dir_for_relative_link
+
+ @relative_path_to_file = File.join('..', 'file')
+ @relative_symlink = File.join(@dir_for_relative_link, 'link')
+ File.symlink(@relative_path_to_file, @relative_symlink)
+ end
+
+ after :each do
+ rm_r @file, @link, @fake_link, @real_dir, @link_dir
+ end
+
+ it "returns '/' when passed '/'" do
+ File.realpath('/').should == '/'
+ end
+
+ it "returns the real (absolute) pathname not containing symlinks" do
+ File.realpath(@link).should == @file
+ end
+
+ it "uses base directory for interpreting relative pathname" do
+ File.realpath(File.basename(@link), @link_dir).should == @file
+ end
+
+ it "uses current directory for interpreting relative pathname" do
+ Dir.chdir @link_dir do
+ File.realpath(File.basename(@link)).should == @file
+ end
+ end
+
+ it "uses link directory for expanding relative links" do
+ File.realpath(@relative_symlink).should == @file
+ end
+
+ it "raises an Errno::ELOOP if the symlink points to itself" do
+ File.unlink @link
+ File.symlink(@link, @link)
+ lambda { File.realpath(@link) }.should raise_error(Errno::ELOOP)
+ end
+
+ it "raises Errno::ENOENT if the file is absent" do
+ lambda { File.realpath(@fake_file) }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises Errno::ENOENT if the symlink points to an absent file" do
+ lambda { File.realpath(@fake_link) }.should raise_error(Errno::ENOENT)
+ end
+ end
+end
+
+platform_is :windows do
+ describe "File.realpath" do
+ before :each do
+ @file = tmp("realpath")
+ touch @file
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the same path" do
+ File.realpath(@file).should == @file
+ end
+ end
+end
diff --git a/spec/ruby/core/file/rename_spec.rb b/spec/ruby/core/file/rename_spec.rb
new file mode 100644
index 0000000000..5f47000d38
--- /dev/null
+++ b/spec/ruby/core/file/rename_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+
+describe "File.rename" do
+ before :each do
+ @old = tmp("file_rename.txt")
+ @new = tmp("file_rename.new")
+
+ rm_r @new
+ touch(@old) { |f| f.puts "hello" }
+ end
+
+ after :each do
+ rm_r @old, @new
+ end
+
+ it "renames a file" do
+ File.exist?(@old).should == true
+ File.exist?(@new).should == false
+ File.rename(@old, @new)
+ File.exist?(@old).should == false
+ File.exist?(@new).should == true
+ end
+
+ it "raises an Errno::ENOENT if the source does not exist" do
+ rm_r @old
+ lambda { File.rename(@old, @new) }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises an ArgumentError if not passed two arguments" do
+ lambda { File.rename }.should raise_error(ArgumentError)
+ lambda { File.rename(@file) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if not passed String types" do
+ lambda { File.rename(1, 2) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/file/reopen_spec.rb b/spec/ruby/core/file/reopen_spec.rb
new file mode 100644
index 0000000000..2d79129320
--- /dev/null
+++ b/spec/ruby/core/file/reopen_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+
+describe "File#reopen" do
+ before :each do
+ @name_a = tmp("file_reopen_a.txt")
+ @name_b = tmp("file_reopen_b.txt")
+ @content_a = "File#reopen a"
+ @content_b = "File#reopen b"
+
+ touch(@name_a) { |f| f.write @content_a }
+ touch(@name_b) { |f| f.write @content_b }
+
+ @file = nil
+ end
+
+ after :each do
+ @file.close if @file and not @file.closed?
+ rm_r @name_a, @name_b
+ end
+
+ it "resets the stream to a new file path" do
+ file = File.new @name_a, "r"
+ file.read.should == @content_a
+ @file = file.reopen(@name_b, "r")
+ @file.read.should == @content_b
+ end
+
+ it "calls #to_path to convern an Object" do
+ @file = File.new(@name_a).reopen(mock_to_path(@name_b), "r")
+ @file.read.should == @content_b
+ end
+end
diff --git a/spec/ruby/core/file/setgid_spec.rb b/spec/ruby/core/file/setgid_spec.rb
new file mode 100644
index 0000000000..f5df5390f5
--- /dev/null
+++ b/spec/ruby/core/file/setgid_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/setgid'
+
+describe "File.setgid?" do
+ it_behaves_like :file_setgid, :setgid?, File
+end
+
+describe "File.setgid?" do
+ before :each do
+ @name = tmp('test.txt')
+ touch @name
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "returns false if the file was just made" do
+ File.setgid?(@name).should == false
+ end
+
+ it "returns false if the file does not exist" do
+ rm_r @name # delete it prematurely, just for this part
+ File.setgid?(@name).should == false
+ end
+
+ as_superuser do
+ platform_is_not :windows do
+ it "returns true when the gid bit is set" do
+ system "chmod g+s #{@name}"
+
+ File.setgid?(@name).should == true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/setuid_spec.rb b/spec/ruby/core/file/setuid_spec.rb
new file mode 100644
index 0000000000..281ef01ab9
--- /dev/null
+++ b/spec/ruby/core/file/setuid_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/setuid'
+
+describe "File.setuid?" do
+ it_behaves_like :file_setuid, :setuid?, File
+end
+
+describe "File.setuid?" do
+ before :each do
+ @name = tmp('test.txt')
+ touch @name
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "returns false if the file was just made" do
+ File.setuid?(@name).should == false
+ end
+
+ it "returns false if the file does not exist" do
+ rm_r @name # delete it prematurely, just for this part
+ File.setuid?(@name).should == false
+ end
+
+ platform_is_not :windows do
+ it "returns true when the gid bit is set" do
+ platform_is :solaris do
+ # Solaris requires execute bit before setting suid
+ system "chmod u+x #{@name}"
+ end
+ system "chmod u+s #{@name}"
+
+ File.setuid?(@name).should == true
+ end
+ end
+end
diff --git a/spec/ruby/core/file/shared/fnmatch.rb b/spec/ruby/core/file/shared/fnmatch.rb
new file mode 100644
index 0000000000..9b423ae47e
--- /dev/null
+++ b/spec/ruby/core/file/shared/fnmatch.rb
@@ -0,0 +1,241 @@
+describe :file_fnmatch, shared: true do
+ it "matches entire strings" do
+ File.send(@method, 'cat', 'cat').should == true
+ end
+
+ it "does not match partial strings" do
+ File.send(@method, 'cat', 'category').should == false
+ end
+
+ it "does not support { } patterns by default" do
+ File.send(@method, 'c{at,ub}s', 'cats').should == false
+ File.send(@method, 'c{at,ub}s', 'c{at,ub}s').should == true
+ end
+
+ it "supports some { } patterns when File::FNM_EXTGLOB is passed" do
+ File.send(@method, "{a,b}", "a", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{a,b}", "b", File::FNM_EXTGLOB).should == true
+ File.send(@method, "c{at,ub}s", "cats", File::FNM_EXTGLOB).should == true
+ File.send(@method, "c{at,ub}s", "cubs", File::FNM_EXTGLOB).should == true
+ File.send(@method, "-c{at,ub}s-", "-cats-", File::FNM_EXTGLOB).should == true
+ File.send(@method, "-c{at,ub}s-", "-cubs-", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{a,b,c}{d,e,f}{g,h}", "adg", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{a,b,c}{d,e,f}{g,h}", "bdg", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{a,b,c}{d,e,f}{g,h}", "ceh", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{aa,bb,cc,dd}", "aa", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{aa,bb,cc,dd}", "bb", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{aa,bb,cc,dd}", "cc", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{aa,bb,cc,dd}", "dd", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{1,5{a,b{c,d}}}", "1", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{1,5{a,b{c,d}}}", "5a", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{1,5{a,b{c,d}}}", "5bc", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{1,5{a,b{c,d}}}", "5bd", File::FNM_EXTGLOB).should == true
+ File.send(@method, "\\\\{a\\,b,b\\}c}", "\\a,b", File::FNM_EXTGLOB).should == true
+ File.send(@method, "\\\\{a\\,b,b\\}c}", "\\b}c", File::FNM_EXTGLOB).should == true
+ end
+
+ it "doesn't support some { } patterns even when File::FNM_EXTGLOB is passed" do
+ File.send(@method, "a{0..3}b", "a0b", File::FNM_EXTGLOB).should == false
+ File.send(@method, "a{0..3}b", "a1b", File::FNM_EXTGLOB).should == false
+ File.send(@method, "a{0..3}b", "a2b", File::FNM_EXTGLOB).should == false
+ File.send(@method, "a{0..3}b", "a3b", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{0..12}", "0", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{0..12}", "6", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{0..12}", "12", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{3..-2}", "3", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{3..-2}", "0", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{3..-2}", "-2", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{a..g}", "a", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{a..g}", "d", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{a..g}", "g", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{g..a}", "a", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{g..a}", "d", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{g..a}", "g", File::FNM_EXTGLOB).should == false
+ File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: {", File::FNM_EXTGLOB).should == false
+ File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: ,", File::FNM_EXTGLOB).should == false
+ File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: }", File::FNM_EXTGLOB).should == false
+ File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: {", File::FNM_EXTGLOB).should == false
+ end
+
+ it "doesn't match an extra } when File::FNM_EXTGLOB is passed" do
+ File.send(@method, 'c{at,ub}}s', 'cats', File::FNM_EXTGLOB).should == false
+ end
+
+ it "matches when both FNM_EXTGLOB and FNM_PATHNAME are passed" do
+ File.send(@method, "?.md", "a.md", File::FNM_EXTGLOB | File::FNM_PATHNAME).should == true
+ end
+
+ it "matches a single character for each ? character" do
+ File.send(@method, 'c?t', 'cat').should == true
+ File.send(@method, 'c??t', 'cat').should == false
+ end
+
+ it "matches zero or more characters for each * character" do
+ File.send(@method, 'c*', 'cats').should == true
+ File.send(@method, 'c*t', 'c/a/b/t').should == true
+ end
+
+ it "matches ranges of characters using bracket expression (e.g. [a-z])" do
+ File.send(@method, 'ca[a-z]', 'cat').should == true
+ end
+
+ it "matches ranges of characters using bracket expression, taking case into account" do
+ File.send(@method, '[a-z]', 'D').should == false
+ File.send(@method, '[^a-z]', 'D').should == true
+ File.send(@method, '[A-Z]', 'd').should == false
+ File.send(@method, '[^A-Z]', 'd').should == true
+ File.send(@method, '[a-z]', 'D', File::FNM_CASEFOLD).should == true
+ end
+
+ it "does not match characters outside of the range of the bracket expresion" do
+ File.send(@method, 'ca[x-z]', 'cat').should == false
+ File.send(@method, '/ca[s][s-t]/rul[a-b]/[z]he/[x-Z]orld', '/cats/rule/the/World').should == false
+ end
+
+ it "matches ranges of characters using exclusive bracket expression (e.g. [^t] or [!t])" do
+ File.send(@method, 'ca[^t]', 'cat').should == false
+ File.send(@method, 'ca[!t]', 'cat').should == false
+ end
+
+ it "matches characters with a case sensitive comparison" do
+ File.send(@method, 'cat', 'CAT').should == false
+ end
+
+ it "matches characters with case insensitive comparison when flags includes FNM_CASEFOLD" do
+ File.send(@method, 'cat', 'CAT', File::FNM_CASEFOLD).should == true
+ end
+
+ platform_is_not :windows do
+ it "doesn't match case sensitive characters on platforms with case sensitive paths, when flags include FNM_SYSCASE" do
+ File.send(@method, 'cat', 'CAT', File::FNM_SYSCASE).should == false
+ end
+ end
+
+ platform_is :windows do
+ it "matches case sensitive characters on platforms with case insensitive paths, when flags include FNM_SYSCASE" do
+ File.send(@method, 'cat', 'CAT', File::FNM_SYSCASE).should == true
+ end
+ end
+
+ it "does not match '/' characters with ? or * when flags includes FNM_PATHNAME" do
+ File.send(@method, '?', '/', File::FNM_PATHNAME).should == false
+ File.send(@method, '*', '/', File::FNM_PATHNAME).should == false
+ end
+
+ it "does not match '/' characters inside bracket expressions when flags includes FNM_PATHNAME" do
+ File.send(@method, '[/]', '/', File::FNM_PATHNAME).should == false
+ end
+
+ it "matches literal ? or * in path when pattern includes \\? or \\*" do
+ File.send(@method, '\?', '?').should == true
+ File.send(@method, '\?', 'a').should == false
+
+ File.send(@method, '\*', '*').should == true
+ File.send(@method, '\*', 'a').should == false
+ end
+
+ it "matches literal character (e.g. 'a') in path when pattern includes escaped character (e.g. \\a)" do
+ File.send(@method, '\a', 'a').should == true
+ File.send(@method, 'this\b', 'thisb').should == true
+ end
+
+ it "matches '\\' characters in path when flags includes FNM_NOESACPE" do
+ File.send(@method, '\a', '\a', File::FNM_NOESCAPE).should == true
+ File.send(@method, '\a', 'a', File::FNM_NOESCAPE).should == false
+ File.send(@method, '\[foo\]\[bar\]', '[foo][bar]', File::FNM_NOESCAPE).should == false
+ end
+
+ it "escapes special characters inside bracket expression" do
+ File.send(@method, '[\?]', '?').should == true
+ File.send(@method, '[\*]', '*').should == true
+ 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
+ end
+
+ it "matches patterns with leading periods to dotfiles by default" do
+ File.send(@method, '.*', '.profile').should == true
+ File.send(@method, ".*file", "nondotfile").should == false
+ end
+
+ it "matches leading periods in filenames when flags includes FNM_DOTMATCH" do
+ File.send(@method, '*', '.profile', File::FNM_DOTMATCH).should == true
+ File.send(@method, '*', 'home/.profile', File::FNM_DOTMATCH).should == true
+ end
+
+ it "matches multiple directories with ** and *" do
+ files = '**/*.rb'
+ File.send(@method, files, 'main.rb').should == false
+ File.send(@method, files, './main.rb').should == false
+ File.send(@method, files, 'lib/song.rb').should == true
+ File.send(@method, '**.rb', 'main.rb').should == true
+ File.send(@method, '**.rb', './main.rb').should == false
+ File.send(@method, '**.rb', 'lib/song.rb').should == true
+ File.send(@method, '*', 'dave/.profile').should == true
+ end
+
+ it "matches multiple directories with ** when flags includes File::FNM_PATHNAME" do
+ files = '**/*.rb'
+ flags = File::FNM_PATHNAME
+
+ File.send(@method, files, 'main.rb', flags).should == true
+ File.send(@method, files, 'one/two/three/main.rb', flags).should == true
+ File.send(@method, files, './main.rb', flags).should == false
+
+ flags = File::FNM_PATHNAME | File::FNM_DOTMATCH
+
+ File.send(@method, files, './main.rb', flags).should == true
+ File.send(@method, files, 'one/two/.main.rb', flags).should == true
+
+ File.send(@method, "**/best/*", 'lib/my/best/song.rb').should == true
+ end
+
+ it "returns false if '/' in pattern do not match '/' in path when flags includes FNM_PATHNAME" do
+ pattern = '*/*'
+ File.send(@method, pattern, 'dave/.profile', File::FNM_PATHNAME).should be_false
+
+ pattern = '**/foo'
+ File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME).should be_false
+ end
+
+ it "returns true if '/' in pattern match '/' in path when flags includes FNM_PATHNAME" do
+ pattern = '*/*'
+ File.send(@method, pattern, 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true
+
+ pattern = '**/foo'
+ File.send(@method, pattern, 'a/b/c/foo', File::FNM_PATHNAME).should be_true
+ File.send(@method, pattern, '/a/b/c/foo', File::FNM_PATHNAME).should be_true
+ File.send(@method, pattern, 'c:/a/b/c/foo', File::FNM_PATHNAME).should be_true
+ File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.send(@method, '\*', mock_to_path('a')).should == false
+ end
+
+ it "raises a TypeError if the first and second arguments are not string-like" do
+ lambda { File.send(@method, nil, nil, 0, 0) }.should raise_error(ArgumentError)
+ lambda { File.send(@method, 1, 'some/thing') }.should raise_error(TypeError)
+ lambda { File.send(@method, 'some/thing', 1) }.should raise_error(TypeError)
+ lambda { File.send(@method, 1, 1) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the third argument is not an Integer" do
+ lambda { File.send(@method, "*/place", "path/to/file", "flags") }.should raise_error(TypeError)
+ lambda { File.send(@method, "*/place", "path/to/file", nil) }.should raise_error(TypeError)
+ end
+
+ it "does not raise a TypeError if the third argument can be coerced to an Integer" do
+ flags = mock("flags")
+ flags.should_receive(:to_int).and_return(10)
+ lambda { File.send(@method, "*/place", "path/to/file", flags) }.should_not raise_error
+ end
+
+ it "matches multibyte characters" do
+ File.fnmatch("*/ä/ø/ñ", "a/ä/ø/ñ").should == true
+ end
+end
diff --git a/spec/ruby/core/file/shared/open.rb b/spec/ruby/core/file/shared/open.rb
new file mode 100644
index 0000000000..677a82a351
--- /dev/null
+++ b/spec/ruby/core/file/shared/open.rb
@@ -0,0 +1,12 @@
+require_relative '../../dir/fixtures/common'
+
+describe :open_directory, shared: true do
+ it "opens directories" do
+ file = File.send(@method, tmp(""))
+ begin
+ file.should be_kind_of(File)
+ ensure
+ file.close
+ end
+ end
+end
diff --git a/spec/ruby/core/file/shared/path.rb b/spec/ruby/core/file/shared/path.rb
new file mode 100644
index 0000000000..503e485f1e
--- /dev/null
+++ b/spec/ruby/core/file/shared/path.rb
@@ -0,0 +1,80 @@
+describe :file_path, shared: true do
+ before :each do
+ @name = "file_to_path"
+ @path = tmp(@name)
+ touch @path
+ end
+
+ after :each do
+ @file.close if @file and !@file.closed?
+ rm_r @path
+ end
+
+ it "returns a String" do
+ @file = File.new @path
+ @file.send(@method).should be_an_instance_of(String)
+ end
+
+ it "calls to_str on argument and returns exact value" do
+ path = mock('path')
+ path.should_receive(:to_str).and_return(@path)
+ @file = File.new path
+ @file.send(@method).should == @path
+ end
+
+ it "does not normalise the path it returns" do
+ Dir.chdir(tmp("")) do
+ unorm = "./#{@name}"
+ @file = File.new unorm
+ @file.send(@method).should == unorm
+ end
+ end
+
+ it "does not canonicalize the path it returns" do
+ dir = File.basename tmp("")
+ path = "#{tmp("")}../#{dir}/#{@name}"
+ @file = File.new path
+ @file.send(@method).should == path
+ end
+
+ it "does not absolute-ise the path it returns" do
+ Dir.chdir(tmp("")) do
+ @file = File.new @name
+ @file.send(@method).should == @name
+ end
+ end
+
+ with_feature :encoding do
+ it "preserves the encoding of the path" do
+ path = @path.force_encoding("euc-jp")
+ @file = File.new path
+ @file.send(@method).encoding.should == Encoding.find("euc-jp")
+ end
+ end
+
+ ruby_version_is "2.5" do
+ platform_is :linux do
+ guard -> { defined?(File::TMPFILE) } do
+ before :each do
+ @dir = tmp("tmpfilespec")
+ mkdir_p @dir
+ end
+
+ after :each 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)
+ end
+ rescue Errno::EOPNOTSUPP, Errno::EINVAL, Errno::EISDIR
+ # EOPNOTSUPP: no support from the filesystem
+ 1.should == 1
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/shared/read.rb b/spec/ruby/core/file/shared/read.rb
new file mode 100644
index 0000000000..7fff7f29d6
--- /dev/null
+++ b/spec/ruby/core/file/shared/read.rb
@@ -0,0 +1,15 @@
+require_relative '../../dir/fixtures/common'
+
+describe :file_read_directory, shared: true do
+ platform_is :darwin, :linux, :openbsd, :windows do
+ it "raises an Errno::EISDIR when passed a path that is a directory" do
+ lambda { @object.send(@method, ".") }.should raise_error(Errno::EISDIR)
+ end
+ end
+
+ platform_is :freebsd, :netbsd do
+ it "does not raises any exception when passed a path that is a directory" do
+ lambda { @object.send(@method, ".") }.should_not raise_error
+ end
+ end
+end
diff --git a/spec/ruby/core/file/shared/stat.rb b/spec/ruby/core/file/shared/stat.rb
new file mode 100644
index 0000000000..aac710dd2f
--- /dev/null
+++ b/spec/ruby/core/file/shared/stat.rb
@@ -0,0 +1,32 @@
+describe :file_stat, shared: true do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file)
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns a File::Stat object if the given file exists" do
+ st = File.send(@method, @file)
+ st.should be_an_instance_of(File::Stat)
+ end
+
+ it "returns a File::Stat object when called on an instance of File" do
+ File.open(@file) do |f|
+ st = f.send(@method)
+ st.should be_an_instance_of(File::Stat)
+ end
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.send(@method, mock_to_path(@file))
+ end
+
+ it "raises an Errno::ENOENT if the file does not exist" do
+ lambda {
+ File.send(@method, "fake_file")
+ }.should raise_error(Errno::ENOENT)
+ end
+end
diff --git a/spec/ruby/core/file/shared/unlink.rb b/spec/ruby/core/file/shared/unlink.rb
new file mode 100644
index 0000000000..42b6a77c5d
--- /dev/null
+++ b/spec/ruby/core/file/shared/unlink.rb
@@ -0,0 +1,61 @@
+describe :file_unlink, shared: true do
+ before :each do
+ @file1 = tmp('test.txt')
+ @file2 = tmp('test2.txt')
+
+ touch @file1
+ touch @file2
+ end
+
+ after :each do
+ File.send(@method, @file1) if File.exist?(@file1)
+ File.send(@method, @file2) if File.exist?(@file2)
+
+ @file1 = nil
+ @file2 = nil
+ end
+
+ it "returns 0 when called without arguments" do
+ File.send(@method).should == 0
+ end
+
+ it "deletes a single file" do
+ File.send(@method, @file1).should == 1
+ File.exist?(@file1).should == false
+ end
+
+ it "deletes multiple files" do
+ File.send(@method, @file1, @file2).should == 2
+ File.exist?(@file1).should == false
+ File.exist?(@file2).should == false
+ end
+
+ it "raises a TypeError if not passed a String type" do
+ lambda { File.send(@method, 1) }.should raise_error(TypeError)
+ end
+
+ it "raises an Errno::ENOENT when the given file doesn't exist" do
+ lambda { File.send(@method, 'bogus') }.should raise_error(Errno::ENOENT)
+ end
+
+ it "coerces a given parameter into a string if possible" do
+ mock = mock("to_str")
+ mock.should_receive(:to_str).and_return(@file1)
+ File.send(@method, mock).should == 1
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.send(@method, mock_to_path(@file1)).should == 1
+ end
+
+ platform_is :windows do
+ it "allows deleting an open file with File::SHARE_DELETE" do
+ path = tmp("share_delete.txt")
+ File.open(path, mode: File::CREAT | File::WRONLY | File::BINARY | File::SHARE_DELETE) do |f|
+ File.exist?(path).should be_true
+ File.send(@method, path)
+ end
+ File.exist?(path).should be_false
+ end
+ end
+end
diff --git a/spec/ruby/core/file/size_spec.rb b/spec/ruby/core/file/size_spec.rb
new file mode 100644
index 0000000000..b0ab2c0651
--- /dev/null
+++ b/spec/ruby/core/file/size_spec.rb
@@ -0,0 +1,119 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/size'
+
+describe "File.size?" do
+ it_behaves_like :file_size, :size?, File
+end
+
+describe "File.size?" do
+ it_behaves_like :file_size_to_io, :size?, File
+end
+
+describe "File.size?" do
+ it_behaves_like :file_size_nil_when_missing, :size?, File
+end
+
+describe "File.size?" do
+ it_behaves_like :file_size_nil_when_empty, :size?, File
+end
+
+describe "File.size?" do
+ it_behaves_like :file_size_with_file_argument, :size?, File
+end
+
+describe "File.size" do
+ it_behaves_like :file_size, :size, File
+end
+
+describe "File.size" do
+ it_behaves_like :file_size_to_io, :size, File
+end
+
+describe "File.size" do
+ it_behaves_like :file_size_raise_when_missing, :size, File
+end
+
+describe "File.size" do
+ it_behaves_like :file_size_0_when_empty, :size, File
+end
+
+describe "File.size" do
+ it_behaves_like :file_size_with_file_argument, :size, File
+end
+
+describe "File#size" do
+
+ before :each do
+ @name = tmp('i_exist')
+ touch(@name) { |f| f.write 'rubinius' }
+ @file = File.new @name
+ @file_org = @file
+ end
+
+ after :each do
+ @file_org.close unless @file_org.closed?
+ rm_r @name
+ end
+
+ it "is an instance method" do
+ @file.respond_to?(:size).should be_true
+ end
+
+ it "returns the file's size as a Fixnum" do
+ @file.size.should be_an_instance_of(Fixnum)
+ end
+
+ it "returns the file's size in bytes" do
+ @file.size.should == 8
+ end
+
+ platform_is_not :windows do # impossible to remove opened file on Windows
+ it "returns the cached size of the file if subsequently deleted" do
+ rm_r @file.path
+ @file.size.should == 8
+ end
+ end
+
+ it "returns the file's current size even if modified" do
+ File.open(@file.path,'a') {|f| f.write '!'}
+ @file.size.should == 9
+ end
+
+ it "raises an IOError on a closed file" do
+ @file.close
+ lambda { @file.size }.should raise_error(IOError)
+ end
+
+ platform_is_not :windows do
+ it "follows symlinks if necessary" do
+ ln_file = tmp('i_exist_ln')
+ rm_r ln_file
+
+ begin
+ File.symlink(@file.path, ln_file).should == 0
+ file = File.new(ln_file)
+ file.size.should == 8
+ ensure
+ file.close if file && !file.closed?
+ rm_r ln_file
+ end
+ end
+ end
+end
+
+describe "File#size for an empty file" do
+ before :each do
+ @name = tmp('empty')
+ touch(@name)
+ @file = File.new @name
+ end
+
+ after :each do
+ @file.close unless @file.closed?
+ rm_r @name
+ end
+
+ it "returns 0" do
+ @file.size.should == 0
+ end
+end
diff --git a/spec/ruby/core/file/socket_spec.rb b/spec/ruby/core/file/socket_spec.rb
new file mode 100644
index 0000000000..5d12e21f55
--- /dev/null
+++ b/spec/ruby/core/file/socket_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/socket'
+require 'socket'
+
+describe "File.socket?" do
+ it_behaves_like :file_socket, :socket?, File
+end
+
+describe "File.socket?" do
+ it "returns false if file does not exist" do
+ File.socket?("I_am_a_bogus_file").should == false
+ end
+
+ it "returns false if the file is not a socket" do
+ filename = tmp("i_exist")
+ touch(filename)
+
+ File.socket?(filename).should == false
+
+ rm_r filename
+ end
+end
+
+platform_is_not :windows do
+ describe "File.socket?" do
+ before :each do
+ # We need a really short name here.
+ # On Linux the path length is limited to 107, see unix(7).
+ @name = tmp("s")
+ @server = UNIXServer.new @name
+ end
+
+ after :each do
+ @server.close
+ rm_r @name
+ end
+
+ it "returns true if the file is a socket" do
+ File.socket?(@name).should == true
+ end
+ end
+end
diff --git a/spec/ruby/core/file/split_spec.rb b/spec/ruby/core/file/split_spec.rb
new file mode 100644
index 0000000000..7a345e8891
--- /dev/null
+++ b/spec/ruby/core/file/split_spec.rb
@@ -0,0 +1,63 @@
+require_relative '../../spec_helper'
+
+describe "File.split" do
+ before :each do
+ @backslash_ext = "C:\\foo\\bar\\baz.rb"
+ @backslash = "C:\\foo\\bar\\baz"
+ end
+
+ it "splits the string at the last '/' when the last component does not have an extension" do
+ File.split("/foo/bar/baz").should == ["/foo/bar", "baz"]
+ File.split("C:/foo/bar/baz").should == ["C:/foo/bar", "baz"]
+ end
+
+ it "splits the string at the last '/' when the last component has an extension" do
+ File.split("/foo/bar/baz.rb").should == ["/foo/bar", "baz.rb"]
+ File.split("C:/foo/bar/baz.rb").should == ["C:/foo/bar", "baz.rb"]
+ end
+
+ it "splits an empty string into a '.' and an empty string" do
+ File.split("").should == [".", ""]
+ end
+
+ platform_is_not :windows do
+ it "collapses multiple '/' characters and strips trailing ones" do
+ File.split("//foo////").should == ["/", "foo"]
+ end
+ end
+
+ platform_is_not :windows do
+ it "does not split a string that contains '\\'" do
+ File.split(@backslash).should == [".", "C:\\foo\\bar\\baz"]
+ File.split(@backslash_ext).should == [".", "C:\\foo\\bar\\baz.rb"]
+ end
+ end
+
+ platform_is :windows do
+ it "splits the string at the last '\\' when the last component does not have an extension" do
+ File.split(@backslash).should == ["C:\\foo\\bar", "baz"]
+ end
+
+ it "splits the string at the last '\\' when the last component has an extension" do
+ File.split(@backslash_ext).should == ["C:\\foo\\bar", "baz.rb"]
+ end
+ end
+
+ it "raises an ArgumentError when not passed a single argument" do
+ lambda { File.split }.should raise_error(ArgumentError)
+ lambda { File.split('string', 'another string') }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if the argument is not a String type" do
+ lambda { File.split(1) }.should raise_error(TypeError)
+ end
+
+ it "coerces the argument with to_str if it is not a String type" do
+ class C; def to_str; "/rubinius/better/than/ruby"; end; end
+ File.split(C.new).should == ["/rubinius/better/than", "ruby"]
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.split(mock_to_path("")).should == [".", ""]
+ end
+end
diff --git a/spec/ruby/core/file/stat/atime_spec.rb b/spec/ruby/core/file/stat/atime_spec.rb
new file mode 100644
index 0000000000..9f1111ced1
--- /dev/null
+++ b/spec/ruby/core/file/stat/atime_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#atime" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the atime of a File::Stat object" do
+ st = File.stat(@file)
+ st.atime.should be_kind_of(Time)
+ st.atime.should <= Time.now
+ end
+end
diff --git a/spec/ruby/core/file/stat/birthtime_spec.rb b/spec/ruby/core/file/stat/birthtime_spec.rb
new file mode 100644
index 0000000000..40e501e87b
--- /dev/null
+++ b/spec/ruby/core/file/stat/birthtime_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#birthtime" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ platform_is :windows, :darwin, :freebsd, :netbsd do
+ it "returns the birthtime of a File::Stat object" do
+ st = File.stat(@file)
+ st.birthtime.should be_kind_of(Time)
+ st.birthtime.should <= Time.now
+ end
+ end
+
+ platform_is :linux, :openbsd do
+ it "raises an NotImplementedError" do
+ st = File.stat(@file)
+ lambda { st.birthtime }.should raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/blksize_spec.rb b/spec/ruby/core/file/stat/blksize_spec.rb
new file mode 100644
index 0000000000..4d85b05e4d
--- /dev/null
+++ b/spec/ruby/core/file/stat/blksize_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#blksize" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ platform_is_not :windows do
+ it "returns the blksize of a File::Stat object" do
+ st = File.stat(@file)
+ st.blksize.is_a?(Integer).should == true
+ st.blksize.should > 0
+ end
+ end
+
+ platform_is :windows do
+ it "returns nil" do
+ st = File.stat(@file)
+ st.blksize.should == nil
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/blockdev_spec.rb b/spec/ruby/core/file/stat/blockdev_spec.rb
new file mode 100644
index 0000000000..f986c18125
--- /dev/null
+++ b/spec/ruby/core/file/stat/blockdev_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/blockdev'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#blockdev?" do
+ it_behaves_like :file_blockdev, :blockdev?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/blocks_spec.rb b/spec/ruby/core/file/stat/blocks_spec.rb
new file mode 100644
index 0000000000..f3f903d0f7
--- /dev/null
+++ b/spec/ruby/core/file/stat/blocks_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#blocks" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ platform_is_not :windows do
+ it "returns a non-negative integer" do
+ st = File.stat(@file)
+ st.blocks.is_a?(Integer).should == true
+ st.blocks.should >= 0
+ end
+ end
+
+ platform_is :windows do
+ it "returns nil" do
+ st = File.stat(@file)
+ st.blocks.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/chardev_spec.rb b/spec/ruby/core/file/stat/chardev_spec.rb
new file mode 100644
index 0000000000..622fb2052d
--- /dev/null
+++ b/spec/ruby/core/file/stat/chardev_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/chardev'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#chardev?" do
+ it_behaves_like :file_chardev, :chardev?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/comparison_spec.rb b/spec/ruby/core/file/stat/comparison_spec.rb
new file mode 100644
index 0000000000..faa3b6bf62
--- /dev/null
+++ b/spec/ruby/core/file/stat/comparison_spec.rb
@@ -0,0 +1,66 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#<=>" do
+ before :each do
+ @name1 = tmp("i_exist")
+ @name2 = tmp("i_exist_too")
+ touch @name1
+ touch @name2
+ end
+
+ after :each do
+ rm_r @name1, @name2
+ end
+
+ it "is able to compare files by the same modification times" do
+ now = Time.now - 1 # 1 second ago to avoid NFS cache issue
+ File.utime(now, now, @name1)
+ File.utime(now, now, @name2)
+
+ File.open(@name1) { |file1|
+ File.open(@name2) { |file2|
+ (file1.stat <=> file2.stat).should == 0
+ }
+ }
+ end
+
+ it "is able to compare files by different modification times" do
+ now = Time.now
+ File.utime(now, now + 100, @name2)
+
+ File.open(@name1) { |file1|
+ File.open(@name2) { |file2|
+ (file1.stat <=> file2.stat).should == -1
+ }
+ }
+
+ File.utime(now, now - 100, @name2)
+
+ File.open(@name1) { |file1|
+ File.open(@name2) { |file2|
+ (file1.stat <=> file2.stat).should == 1
+ }
+ }
+ end
+
+ # TODO: Fix
+ it "includes Comparable and #== shows mtime equality between two File::Stat objects" do
+ File.open(@name1) { |file1|
+ File.open(@name2) { |file2|
+ (file1.stat == file1.stat).should == true
+ (file2.stat == file2.stat).should == true
+ }
+ }
+
+ now = Time.now
+ File.utime(now, now + 100, @name2)
+
+ File.open(@name1) { |file1|
+ File.open(@name2) { |file2|
+ (file1.stat == file2.stat).should == false
+ (file1.stat == file1.stat).should == true
+ (file2.stat == file2.stat).should == true
+ }
+ }
+ end
+end
diff --git a/spec/ruby/core/file/stat/ctime_spec.rb b/spec/ruby/core/file/stat/ctime_spec.rb
new file mode 100644
index 0000000000..fd50487a0a
--- /dev/null
+++ b/spec/ruby/core/file/stat/ctime_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#ctime" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the ctime of a File::Stat object" do
+ st = File.stat(@file)
+ st.ctime.should be_kind_of(Time)
+ st.ctime.should <= Time.now
+ end
+end
diff --git a/spec/ruby/core/file/stat/dev_major_spec.rb b/spec/ruby/core/file/stat/dev_major_spec.rb
new file mode 100644
index 0000000000..4966d609e2
--- /dev/null
+++ b/spec/ruby/core/file/stat/dev_major_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#dev_major" do
+ before :each do
+ @name = tmp("file.txt")
+ touch(@name)
+ end
+ after :each do
+ rm_r @name
+ end
+
+ platform_is_not :windows do
+ it "returns the major part of File::Stat#dev" do
+ File.stat(@name).dev_major.should be_kind_of(Integer)
+ end
+ end
+
+ platform_is :windows do
+ it "returns nil" do
+ File.stat(@name).dev_major.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/dev_minor_spec.rb b/spec/ruby/core/file/stat/dev_minor_spec.rb
new file mode 100644
index 0000000000..ea79c12b99
--- /dev/null
+++ b/spec/ruby/core/file/stat/dev_minor_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#dev_minor" do
+ before :each do
+ @name = tmp("file.txt")
+ touch(@name)
+ end
+ after :each do
+ rm_r @name
+ end
+
+ platform_is_not :windows do
+ it "returns the minor part of File::Stat#dev" do
+ File.stat(@name).dev_minor.should be_kind_of(Integer)
+ end
+ end
+
+ platform_is :windows do
+ it "returns nil" do
+ File.stat(@name).dev_minor.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/dev_spec.rb b/spec/ruby/core/file/stat/dev_spec.rb
new file mode 100644
index 0000000000..e953fcaa58
--- /dev/null
+++ b/spec/ruby/core/file/stat/dev_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#dev" do
+ before :each do
+ @name = tmp("file.txt")
+ touch(@name)
+ end
+ after :each do
+ rm_r @name
+ end
+
+ it "returns the number of the device on which the file exists" do
+ File.stat(@name).dev.should be_kind_of(Integer)
+ end
+end
diff --git a/spec/ruby/core/file/stat/directory_spec.rb b/spec/ruby/core/file/stat/directory_spec.rb
new file mode 100644
index 0000000000..c03610388b
--- /dev/null
+++ b/spec/ruby/core/file/stat/directory_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/directory'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#directory?" do
+ it_behaves_like :file_directory, :directory?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/executable_real_spec.rb b/spec/ruby/core/file/stat/executable_real_spec.rb
new file mode 100644
index 0000000000..23bffe89c5
--- /dev/null
+++ b/spec/ruby/core/file/stat/executable_real_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/executable_real'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#executable_real?" do
+ it_behaves_like :file_executable_real, :executable_real?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/executable_spec.rb b/spec/ruby/core/file/stat/executable_spec.rb
new file mode 100644
index 0000000000..422975d14b
--- /dev/null
+++ b/spec/ruby/core/file/stat/executable_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/executable'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#executable?" do
+ it_behaves_like :file_executable, :executable?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/file_spec.rb b/spec/ruby/core/file/stat/file_spec.rb
new file mode 100644
index 0000000000..d141536b4b
--- /dev/null
+++ b/spec/ruby/core/file/stat/file_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/file'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#file?" do
+ it_behaves_like :file_file, :file?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/fixtures/classes.rb b/spec/ruby/core/file/stat/fixtures/classes.rb
new file mode 100644
index 0000000000..4fe9a2a30f
--- /dev/null
+++ b/spec/ruby/core/file/stat/fixtures/classes.rb
@@ -0,0 +1,5 @@
+class FileStat
+ def self.method_missing(meth, file)
+ File.lstat(file).send(meth)
+ end
+end
diff --git a/spec/ruby/core/file/stat/ftype_spec.rb b/spec/ruby/core/file/stat/ftype_spec.rb
new file mode 100644
index 0000000000..a19e7c43d7
--- /dev/null
+++ b/spec/ruby/core/file/stat/ftype_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/file_types'
+
+describe "File::Stat#ftype" do
+ before :all do
+ FileSpecs.configure_types
+ end
+
+ it "returns a String" do
+ FileSpecs.normal_file do |file|
+ File.lstat(file).ftype.should be_kind_of(String)
+ end
+ end
+
+ it "returns 'file' when the file is a file" do
+ FileSpecs.normal_file do |file|
+ File.lstat(file).ftype.should == 'file'
+ end
+ end
+
+ it "returns 'directory' when the file is a dir" do
+ FileSpecs.directory do |dir|
+ File.lstat(dir).ftype.should == 'directory'
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns 'characterSpecial' when the file is a char" do
+ FileSpecs.character_device do |char|
+ File.lstat(char).ftype.should == 'characterSpecial'
+ end
+ end
+ end
+
+ platform_is_not :freebsd do # FreeBSD does not have block devices
+ with_block_device do
+ it "returns 'blockSpecial' when the file is a block" do
+ FileSpecs.block_device do |block|
+ File.lstat(block).ftype.should == 'blockSpecial'
+ end
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns 'link' when the file is a link" do
+ FileSpecs.symlink do |link|
+ File.lstat(link).ftype.should == 'link'
+ end
+ end
+
+ it "returns fifo when the file is a fifo" do
+ FileSpecs.fifo do |fifo|
+ File.lstat(fifo).ftype.should == 'fifo'
+ end
+ end
+
+ # This will silently not execute the block if no socket
+ # can be found. However, if you are running X, there is
+ # a good chance that if nothing else, at least the X
+ # Server socket exists.
+ it "returns 'socket' when the file is a socket" do
+ FileSpecs.socket do |socket|
+ File.lstat(socket).ftype.should == 'socket'
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/gid_spec.rb b/spec/ruby/core/file/stat/gid_spec.rb
new file mode 100644
index 0000000000..3bba65bc82
--- /dev/null
+++ b/spec/ruby/core/file/stat/gid_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#gid" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ File.chown(nil, Process.gid, @file)
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the group owner attribute of a File::Stat object" do
+ st = File.stat(@file)
+ st.gid.is_a?(Integer).should == true
+ st.gid.should == Process.gid
+ end
+end
diff --git a/spec/ruby/core/file/stat/grpowned_spec.rb b/spec/ruby/core/file/stat/grpowned_spec.rb
new file mode 100644
index 0000000000..e7278e229b
--- /dev/null
+++ b/spec/ruby/core/file/stat/grpowned_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/grpowned'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#grpowned?" do
+ it_behaves_like :file_grpowned, :grpowned?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/ino_spec.rb b/spec/ruby/core/file/stat/ino_spec.rb
new file mode 100644
index 0000000000..42370aecb7
--- /dev/null
+++ b/spec/ruby/core/file/stat/ino_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#ino" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ platform_is_not :windows do
+ it "returns the ino of a File::Stat object" do
+ st = File.stat(@file)
+ st.ino.should be_kind_of(Integer)
+ st.ino.should > 0
+ end
+ end
+
+ platform_is :windows do
+ it "returns BY_HANDLE_FILE_INFORMATION.nFileIndexHigh/Low of a File::Stat object" do
+ st = File.stat(@file)
+ st.ino.should be_kind_of(Integer)
+ st.ino.should > 0
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/inspect_spec.rb b/spec/ruby/core/file/stat/inspect_spec.rb
new file mode 100644
index 0000000000..b0a0658dc0
--- /dev/null
+++ b/spec/ruby/core/file/stat/inspect_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#inspect" do
+
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "produces a nicely formatted description of a File::Stat object" do
+ st = File.stat(@file)
+ expected = "#<File::Stat dev=0x#{st.dev.to_s(16)}, ino=#{st.ino}, mode=#{sprintf("%07o", st.mode)}, nlink=#{st.nlink}"
+ expected << ", uid=#{st.uid}, gid=#{st.gid}, rdev=0x#{st.rdev.to_s(16)}, size=#{st.size}, blksize=#{st.blksize.inspect}"
+ expected << ", blocks=#{st.blocks.inspect}, atime=#{st.atime}, mtime=#{st.mtime}, ctime=#{st.ctime}"
+ platform_is :netbsd, :freebsd, :darwin do
+ # Windows has File.birthtime but it's not here since already shown by ctime.
+ expected << ", birthtime=#{st.birthtime}"
+ end
+ expected << ">"
+ st.inspect.should == expected
+ end
+end
diff --git a/spec/ruby/core/file/stat/mode_spec.rb b/spec/ruby/core/file/stat/mode_spec.rb
new file mode 100644
index 0000000000..c85fb85a58
--- /dev/null
+++ b/spec/ruby/core/file/stat/mode_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#mode" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ File.chmod(0644, @file)
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the mode of a File::Stat object" do
+ st = File.stat(@file)
+ st.mode.is_a?(Integer).should == true
+ (st.mode & 0777).should == 0644
+ end
+end
diff --git a/spec/ruby/core/file/stat/mtime_spec.rb b/spec/ruby/core/file/stat/mtime_spec.rb
new file mode 100644
index 0000000000..08a2b83463
--- /dev/null
+++ b/spec/ruby/core/file/stat/mtime_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#mtime" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the mtime of a File::Stat object" do
+ st = File.stat(@file)
+ st.mtime.should be_kind_of(Time)
+ st.mtime.should <= Time.now
+ end
+end
diff --git a/spec/ruby/core/file/stat/new_spec.rb b/spec/ruby/core/file/stat/new_spec.rb
new file mode 100644
index 0000000000..f5f22e6711
--- /dev/null
+++ b/spec/ruby/core/file/stat/new_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#initialize" do
+
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ File.chmod(0755, @file)
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "raises an exception if the file doesn't exist" do
+ lambda {
+ File::Stat.new(tmp("i_am_a_dummy_file_that_doesnt_exist"))
+ }.should raise_error(Errno::ENOENT)
+ end
+
+ it "creates a File::Stat object for the given file" do
+ st = File::Stat.new(@file)
+ st.should be_kind_of(File::Stat)
+ st.ftype.should == 'file'
+ end
+
+ it "calls #to_path on non-String arguments" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return @file
+ File::Stat.new p
+ end
+end
diff --git a/spec/ruby/core/file/stat/nlink_spec.rb b/spec/ruby/core/file/stat/nlink_spec.rb
new file mode 100644
index 0000000000..2dd0bff124
--- /dev/null
+++ b/spec/ruby/core/file/stat/nlink_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#nlink" do
+ before :each do
+ @file = tmp("stat_nlink")
+ @link = @file + ".lnk"
+ touch @file
+ end
+
+ after :each do
+ rm_r @link, @file
+ end
+
+ platform_is_not :windows do
+ it "returns the number of links to a file" do
+ File::Stat.new(@file).nlink.should == 1
+ File.link(@file, @link)
+ File::Stat.new(@file).nlink.should == 2
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/owned_spec.rb b/spec/ruby/core/file/stat/owned_spec.rb
new file mode 100644
index 0000000000..6f0c250f88
--- /dev/null
+++ b/spec/ruby/core/file/stat/owned_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/owned'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#owned?" do
+ it_behaves_like :file_owned, :owned?, FileStat
+end
+
+describe "File::Stat#owned?" do
+ before :each do
+ @file = tmp("i_exist")
+ touch(@file)
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns true if the file is owned by the user" do
+ st = File.stat(@file)
+ st.owned?.should == true
+ end
+
+ platform_is_not :windows do
+ as_user do
+ it "returns false if the file is not owned by the user" do
+ system_file = '/etc/passwd'
+ st = File.stat(system_file)
+ st.owned?.should == false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/pipe_spec.rb b/spec/ruby/core/file/stat/pipe_spec.rb
new file mode 100644
index 0000000000..7abb6c742a
--- /dev/null
+++ b/spec/ruby/core/file/stat/pipe_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/pipe'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#pipe?" do
+ it_behaves_like :file_pipe, :pipe?, FileStat
+end
+
+describe "File::Stat#pipe?" do
+ it "returns false if the file is not a pipe" do
+ filename = tmp("i_exist")
+ touch(filename)
+
+ st = File.stat(filename)
+ st.pipe?.should == false
+
+ rm_r filename
+ end
+
+ platform_is_not :windows do
+ it "returns true if the file is a pipe" do
+ filename = tmp("i_am_a_pipe")
+ File.mkfifo(filename)
+
+ st = File.stat(filename)
+ st.pipe?.should == true
+
+ rm_r filename
+ end
+ end
+
+end
diff --git a/spec/ruby/core/file/stat/rdev_major_spec.rb b/spec/ruby/core/file/stat/rdev_major_spec.rb
new file mode 100644
index 0000000000..f8a8d1b107
--- /dev/null
+++ b/spec/ruby/core/file/stat/rdev_major_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#rdev_major" do
+ before :each do
+ platform_is :solaris do
+ @name = "/dev/zfs"
+ end
+ platform_is_not :solaris do
+ @name = tmp("file.txt")
+ touch(@name)
+ end
+ end
+
+ after :each do
+ platform_is_not :solaris do
+ rm_r @name
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns the major part of File::Stat#rdev" do
+ File.stat(@name).rdev_major.should be_kind_of(Integer)
+ end
+ end
+
+ platform_is :windows do
+ it "returns nil" do
+ File.stat(@name).rdev_major.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/rdev_minor_spec.rb b/spec/ruby/core/file/stat/rdev_minor_spec.rb
new file mode 100644
index 0000000000..dc30c1f56c
--- /dev/null
+++ b/spec/ruby/core/file/stat/rdev_minor_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#rdev_minor" do
+ before :each do
+ platform_is :solaris do
+ @name = "/dev/zfs"
+ end
+ platform_is_not :solaris do
+ @name = tmp("file.txt")
+ touch(@name)
+ end
+ end
+
+ after :each do
+ platform_is_not :solaris do
+ rm_r @name
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns the minor part of File::Stat#rdev" do
+ File.stat(@name).rdev_minor.should be_kind_of(Integer)
+ end
+ end
+
+ platform_is :windows do
+ it "returns nil" do
+ File.stat(@name).rdev_minor.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/rdev_spec.rb b/spec/ruby/core/file/stat/rdev_spec.rb
new file mode 100644
index 0000000000..9e1aee692d
--- /dev/null
+++ b/spec/ruby/core/file/stat/rdev_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#rdev" do
+ before :each do
+ @name = tmp("file.txt")
+ touch(@name)
+ end
+ after :each do
+ rm_r @name
+ end
+
+ it "returns the number of the device this file represents which the file exists" do
+ File.stat(@name).rdev.should be_kind_of(Integer)
+ end
+end
diff --git a/spec/ruby/core/file/stat/readable_real_spec.rb b/spec/ruby/core/file/stat/readable_real_spec.rb
new file mode 100644
index 0000000000..f138fd7b00
--- /dev/null
+++ b/spec/ruby/core/file/stat/readable_real_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/readable_real'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#readable_real?" do
+ it_behaves_like :file_readable_real, :readable_real?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/readable_spec.rb b/spec/ruby/core/file/stat/readable_spec.rb
new file mode 100644
index 0000000000..e99e48feed
--- /dev/null
+++ b/spec/ruby/core/file/stat/readable_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/readable'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#readable?" do
+ it_behaves_like :file_readable, :readable?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/setgid_spec.rb b/spec/ruby/core/file/stat/setgid_spec.rb
new file mode 100644
index 0000000000..9b972f5a6d
--- /dev/null
+++ b/spec/ruby/core/file/stat/setgid_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/setgid'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#setgid?" do
+ it_behaves_like :file_setgid, :setgid?, FileStat
+end
+
+describe "File::Stat#setgid?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/stat/setuid_spec.rb b/spec/ruby/core/file/stat/setuid_spec.rb
new file mode 100644
index 0000000000..892776a0ef
--- /dev/null
+++ b/spec/ruby/core/file/stat/setuid_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/setuid'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#setuid?" do
+ it_behaves_like :file_setuid, :setuid?, FileStat
+end
+
+describe "File::Stat#setuid?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/stat/size_spec.rb b/spec/ruby/core/file/stat/size_spec.rb
new file mode 100644
index 0000000000..4b4f57f8c8
--- /dev/null
+++ b/spec/ruby/core/file/stat/size_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/size'
+require_relative 'fixtures/classes'
+
+describe "File::Stat.size?" do
+ it_behaves_like :file_size, :size?, FileStat
+ it_behaves_like :file_size_nil_when_empty, :size?, FileStat
+end
+
+describe "File::Stat.size" do
+ it_behaves_like :file_size, :size, FileStat
+ it_behaves_like :file_size_0_when_empty, :size, FileStat
+end
+
+describe "File::Stat#size" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "File::Stat#size?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/stat/socket_spec.rb b/spec/ruby/core/file/stat/socket_spec.rb
new file mode 100644
index 0000000000..8dd43ec44a
--- /dev/null
+++ b/spec/ruby/core/file/stat/socket_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/socket'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#socket?" do
+ it_behaves_like :file_socket, :socket?, FileStat
+end
+
+describe "File::Stat#socket?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/stat/sticky_spec.rb b/spec/ruby/core/file/stat/sticky_spec.rb
new file mode 100644
index 0000000000..8634253a02
--- /dev/null
+++ b/spec/ruby/core/file/stat/sticky_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/sticky'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#sticky?" do
+ it_behaves_like :file_sticky, :sticky?, FileStat
+end
+
+describe "File::Stat#sticky?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/stat/symlink_spec.rb b/spec/ruby/core/file/stat/symlink_spec.rb
new file mode 100644
index 0000000000..0def832a4c
--- /dev/null
+++ b/spec/ruby/core/file/stat/symlink_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/symlink'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#symlink?" do
+ it_behaves_like :file_symlink, :symlink?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/uid_spec.rb b/spec/ruby/core/file/stat/uid_spec.rb
new file mode 100644
index 0000000000..b97147db21
--- /dev/null
+++ b/spec/ruby/core/file/stat/uid_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+
+describe "File::Stat#uid" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the owner attribute of a File::Stat object" do
+ st = File.stat(@file)
+ st.uid.is_a?(Integer).should == true
+ st.uid.should == Process.uid
+ end
+end
diff --git a/spec/ruby/core/file/stat/world_readable_spec.rb b/spec/ruby/core/file/stat/world_readable_spec.rb
new file mode 100644
index 0000000000..d94a02205e
--- /dev/null
+++ b/spec/ruby/core/file/stat/world_readable_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/world_readable'
+require_relative 'fixtures/classes'
+
+describe "File::Stat.world_readable?" do
+ it_behaves_like :file_world_readable, :world_readable?, FileStat
+end
+
+describe "File::Stat#world_readable?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/stat/world_writable_spec.rb b/spec/ruby/core/file/stat/world_writable_spec.rb
new file mode 100644
index 0000000000..8100008344
--- /dev/null
+++ b/spec/ruby/core/file/stat/world_writable_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/world_writable'
+require_relative 'fixtures/classes'
+
+describe "File::Stat.world_writable?" do
+ it_behaves_like :file_world_writable, :world_writable?, FileStat
+end
+
+describe "File::Stat#world_writable?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/stat/writable_real_spec.rb b/spec/ruby/core/file/stat/writable_real_spec.rb
new file mode 100644
index 0000000000..4c9e78eb70
--- /dev/null
+++ b/spec/ruby/core/file/stat/writable_real_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/writable_real'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#writable_real?" do
+ it_behaves_like :file_writable_real, :writable_real?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/writable_spec.rb b/spec/ruby/core/file/stat/writable_spec.rb
new file mode 100644
index 0000000000..551268751f
--- /dev/null
+++ b/spec/ruby/core/file/stat/writable_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/writable'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#writable?" do
+ it_behaves_like :file_writable, :writable?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/zero_spec.rb b/spec/ruby/core/file/stat/zero_spec.rb
new file mode 100644
index 0000000000..74facac66a
--- /dev/null
+++ b/spec/ruby/core/file/stat/zero_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../../../shared/file/zero'
+require_relative 'fixtures/classes'
+
+describe "File::Stat#zero?" do
+ it_behaves_like :file_zero, :zero?, FileStat
+end
diff --git a/spec/ruby/core/file/stat_spec.rb b/spec/ruby/core/file/stat_spec.rb
new file mode 100644
index 0000000000..31f9dc58af
--- /dev/null
+++ b/spec/ruby/core/file/stat_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../../spec_helper'
+require_relative 'shared/stat'
+
+describe "File.stat" do
+ it_behaves_like :file_stat, :stat
+end
+
+platform_is_not :windows do
+ describe "File.stat" do
+ before :each do
+ @file = tmp('i_exist')
+ @link = tmp('i_am_a_symlink')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @link, @file
+ end
+
+ it "returns information for a file that has been deleted but is still open" do
+ File.open(@file) do |f|
+ rm_r @file
+
+ st = f.stat
+
+ st.file?.should == true
+ st.zero?.should == false
+ st.size.should == 8
+ st.size?.should == 8
+ st.blksize.should >= 0
+ st.atime.should be_kind_of(Time)
+ st.ctime.should be_kind_of(Time)
+ st.mtime.should be_kind_of(Time)
+ end
+ end
+
+ it "returns a File::Stat object with file properties for a symlink" do
+ File.symlink(@file, @link)
+ st = File.stat(@link)
+
+ st.file?.should == true
+ st.symlink?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/file/sticky_spec.rb b/spec/ruby/core/file/sticky_spec.rb
new file mode 100644
index 0000000000..7805c39f79
--- /dev/null
+++ b/spec/ruby/core/file/sticky_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/sticky'
+
+describe "File.sticky?" do
+ it_behaves_like :file_sticky, :sticky?, File
+ it_behaves_like :file_sticky_missing, :sticky?, File
+end
+
+describe "File.sticky?" do
+ platform_is_not :windows do
+ it "returns false if file does not exist" do
+ File.sticky?("I_am_a_bogus_file").should == false
+ end
+
+ it "returns false if the file has not sticky bit set" do
+ filename = tmp("i_exist")
+ touch(filename)
+
+ File.sticky?(filename).should == false
+
+ rm_r filename
+ end
+ end
+
+ platform_is :linux, :darwin do
+ it "returns true if the file has sticky bit set" do
+ filename = tmp("i_exist")
+ touch(filename)
+ system "chmod +t #{filename}"
+
+ File.sticky?(filename).should == true
+
+ rm_r filename
+ end
+ end
+
+ platform_is :bsd do
+ # FreeBSD and NetBSD can't set stiky bit to a normal file
+ it "cannot set sticky bit to a normal file" do
+ filename = tmp("i_exist")
+ touch(filename)
+ stat = File.stat(filename)
+ mode = stat.mode
+ raise_error(Errno::EFTYPE){File.chmod(mode|01000, filename)}
+ File.sticky?(filename).should == false
+
+ rm_r filename
+ end
+ end
+end
diff --git a/spec/ruby/core/file/symlink_spec.rb b/spec/ruby/core/file/symlink_spec.rb
new file mode 100644
index 0000000000..a8486db04a
--- /dev/null
+++ b/spec/ruby/core/file/symlink_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/symlink'
+
+describe "File.symlink" do
+ before :each do
+ @file = tmp("file_symlink.txt")
+ @link = tmp("file_symlink.lnk")
+
+ rm_r @link
+ touch @file
+ end
+
+ after :each do
+ rm_r @link, @file
+ end
+
+ platform_is_not :windows do
+ it "creates a symlink between a source and target file" do
+ File.symlink(@file, @link).should == 0
+ File.identical?(@file, @link).should == true
+ end
+
+ it "creates a symbolic link" do
+ File.symlink(@file, @link)
+ File.symlink?(@link).should == true
+ end
+
+ it "accepts args that have #to_path methods" do
+ File.symlink(mock_to_path(@file), mock_to_path(@link))
+ File.symlink?(@link).should == true
+ end
+
+ it "raises an Errno::EEXIST if the target already exists" do
+ File.symlink(@file, @link)
+ lambda { File.symlink(@file, @link) }.should raise_error(Errno::EEXIST)
+ end
+
+ it "raises an ArgumentError if not called with two arguments" do
+ lambda { File.symlink }.should raise_error(ArgumentError)
+ lambda { File.symlink(@file) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if not called with String types" do
+ lambda { File.symlink(@file, nil) }.should raise_error(TypeError)
+ lambda { File.symlink(@file, 1) }.should raise_error(TypeError)
+ lambda { File.symlink(1, 1) }.should raise_error(TypeError)
+ end
+ end
+end
+
+describe "File.symlink?" do
+ it_behaves_like :file_symlink, :symlink?, File
+end
+
+describe "File.symlink?" do
+ it_behaves_like :file_symlink_nonexistent, :symlink?, File
+end
diff --git a/spec/ruby/core/file/to_path_spec.rb b/spec/ruby/core/file/to_path_spec.rb
new file mode 100644
index 0000000000..6d168a065c
--- /dev/null
+++ b/spec/ruby/core/file/to_path_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/path'
+
+describe "File#to_path" do
+ it_behaves_like :file_path, :to_path
+end
diff --git a/spec/ruby/core/file/truncate_spec.rb b/spec/ruby/core/file/truncate_spec.rb
new file mode 100644
index 0000000000..36fe26a2fe
--- /dev/null
+++ b/spec/ruby/core/file/truncate_spec.rb
@@ -0,0 +1,177 @@
+require_relative '../../spec_helper'
+
+describe "File.truncate" do
+ before :each do
+ @name = tmp("test.txt")
+ touch(@name) { |f| f.write("1234567890") }
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "truncates a file" do
+ File.size(@name).should == 10
+
+ File.truncate(@name, 5)
+ File.size(@name).should == 5
+
+ File.open(@name, "r") do |f|
+ f.read(99).should == "12345"
+ f.eof?.should == true
+ end
+ end
+
+ it "truncate a file size to 0" do
+ File.truncate(@name, 0).should == 0
+ IO.read(@name).should == ""
+ end
+
+ it "truncate a file size to 5" do
+ File.size(@name).should == 10
+ File.truncate(@name, 5)
+ File.size(@name).should == 5
+ IO.read(@name).should == "12345"
+ end
+
+ it "truncates to a larger file size than the original file" do
+ File.truncate(@name, 12)
+ File.size(@name).should == 12
+ IO.read(@name).should == "1234567890\000\000"
+ end
+
+ it "truncates to the same size as the original file" do
+ File.truncate(@name, File.size(@name))
+ File.size(@name).should == 10
+ IO.read(@name).should == "1234567890"
+ end
+
+ it "raises an Errno::ENOENT if the file does not exist" do
+ # TODO: missing_file
+ not_existing_file = tmp("file-does-not-exist-for-sure.txt")
+
+ # make sure it doesn't exist for real
+ rm_r not_existing_file
+
+ begin
+ lambda { File.truncate(not_existing_file, 5) }.should raise_error(Errno::ENOENT)
+ ensure
+ rm_r not_existing_file
+ end
+ end
+
+ it "raises an ArgumentError if not passed two arguments" do
+ lambda { File.truncate }.should raise_error(ArgumentError)
+ lambda { File.truncate(@name) }.should raise_error(ArgumentError)
+ end
+
+ platform_is_not :netbsd, :openbsd do
+ it "raises an Errno::EINVAL if the length argument is not valid" do
+ lambda { File.truncate(@name, -1) }.should raise_error(Errno::EINVAL) # May fail
+ end
+ end
+
+ it "raises a TypeError if not passed a String type for the first argument" do
+ lambda { File.truncate(1, 1) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if not passed an Integer type for the second argument" do
+ lambda { File.truncate(@name, nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.truncate(mock_to_path(@name), 0).should == 0
+ end
+end
+
+
+describe "File#truncate" do
+ before :each do
+ @name = tmp("test.txt")
+ @file = File.open @name, 'w'
+ @file.write "1234567890"
+ @file.flush
+ end
+
+ after :each do
+ @file.close unless @file.closed?
+ rm_r @name
+ end
+
+ it "does not move the file write pointer to the specified byte offset" do
+ @file.truncate(3)
+ @file.write "abc"
+ @file.close
+ File.read(@name).should == "123\x00\x00\x00\x00\x00\x00\x00abc"
+ end
+
+ it "does not move the file read pointer to the specified byte offset" do
+ File.open(@name, "r+") do |f|
+ f.read(1).should == "1"
+ f.truncate(0)
+ f.read(1).should == nil
+ end
+ end
+
+ it "truncates a file" do
+ File.size(@name).should == 10
+
+ @file.truncate(5)
+ File.size(@name).should == 5
+ File.open(@name, "r") do |f|
+ f.read(99).should == "12345"
+ f.eof?.should == true
+ end
+ end
+
+ it "truncates a file size to 0" do
+ @file.truncate(0).should == 0
+ IO.read(@name).should == ""
+ end
+
+ it "truncates a file size to 5" do
+ File.size(@name).should == 10
+ @file.truncate(5)
+ File.size(@name).should == 5
+ IO.read(@name).should == "12345"
+ end
+
+ it "truncates a file to a larger size than the original file" do
+ @file.truncate(12)
+ File.size(@name).should == 12
+ IO.read(@name).should == "1234567890\000\000"
+ end
+
+ it "truncates a file to the same size as the original file" do
+ @file.truncate(File.size(@name))
+ File.size(@name).should == 10
+ IO.read(@name).should == "1234567890"
+ end
+
+ it "raises an ArgumentError if not passed one argument" do
+ lambda { @file.truncate }.should raise_error(ArgumentError)
+ lambda { @file.truncate(1) }.should_not raise_error(ArgumentError)
+ end
+
+ platform_is_not :netbsd do
+ it "raises an Errno::EINVAL if the length argument is not valid" do
+ lambda { @file.truncate(-1) }.should raise_error(Errno::EINVAL) # May fail
+ end
+ end
+
+ it "raises an IOError if file is closed" do
+ @file.close
+ @file.closed?.should == true
+ lambda { @file.truncate(42) }.should raise_error(IOError)
+ end
+
+ it "raises an IOError if file is not opened for writing" do
+ File.open(@name, 'r') do |file|
+ lambda { file.truncate(42) }.should raise_error(IOError)
+ end
+ end
+
+ it "raises a TypeError if not passed an Integer type for the for the argument" do
+ lambda { @file.truncate(nil) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/file/umask_spec.rb b/spec/ruby/core/file/umask_spec.rb
new file mode 100644
index 0000000000..9a4beb8998
--- /dev/null
+++ b/spec/ruby/core/file/umask_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../spec_helper'
+
+describe "File.umask" do
+ before :each do
+ @orig_umask = File.umask
+ @file = tmp('test.txt')
+ touch @file
+ end
+
+ after :each do
+ rm_r @file
+ File.umask(@orig_umask)
+ end
+
+ it "returns a Fixnum" do
+ File.umask.should be_kind_of(Fixnum)
+ end
+
+ platform_is_not :windows do
+ it "returns the current umask value for the process" do
+ File.umask(022)
+ File.umask(006).should == 022
+ File.umask.should == 006
+ end
+
+ it "invokes to_int on non-integer argument" do
+ (obj = mock(022)).should_receive(:to_int).any_number_of_times.and_return(022)
+ File.umask(obj)
+ File.umask(obj).should == 022
+ end
+ end
+
+ platform_is :windows do
+ it "returns the current umask value for this process (basic)" do
+ File.umask.should == 0
+ File.umask(022).should == 0
+ File.umask(044).should == 0
+ end
+
+ # The value used here is the value of _S_IWRITE.
+ it "returns the current umask value for this process" do
+ File.umask(0000200)
+ File.umask.should == 0000200
+ File.umask(0006)
+ File.umask.should == 0
+ end
+ end
+
+ it "raises RangeError with too large values" do
+ -> { File.umask(2**64) }.should raise_error(RangeError)
+ -> { File.umask(-2**63 - 1) }.should raise_error(RangeError)
+ end
+
+ it "raises ArgumentError when more than one argument is provided" do
+ lambda { File.umask(022, 022) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/file/unlink_spec.rb b/spec/ruby/core/file/unlink_spec.rb
new file mode 100644
index 0000000000..28872d55ed
--- /dev/null
+++ b/spec/ruby/core/file/unlink_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/unlink'
+
+describe "File.unlink" do
+ it_behaves_like :file_unlink, :unlink
+end
diff --git a/spec/ruby/core/file/utime_spec.rb b/spec/ruby/core/file/utime_spec.rb
new file mode 100644
index 0000000000..542681ea93
--- /dev/null
+++ b/spec/ruby/core/file/utime_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../../spec_helper'
+
+describe "File.utime" do
+ before :each do
+ @atime = Time.now
+ @mtime = Time.now
+ @file1 = tmp("specs_file_utime1")
+ @file2 = tmp("specs_file_utime2")
+ touch @file1
+ touch @file2
+ end
+
+ after :each do
+ rm_r @file1, @file2
+ end
+
+ it "sets the access and modification time of each file" do
+ File.utime(@atime, @mtime, @file1, @file2)
+ File.atime(@file1).to_i.should be_close(@atime.to_i, 2)
+ File.mtime(@file1).to_i.should be_close(@mtime.to_i, 2)
+ File.atime(@file2).to_i.should be_close(@atime.to_i, 2)
+ File.mtime(@file2).to_i.should be_close(@mtime.to_i, 2)
+ end
+
+ it "uses the current times if two nil values are passed" do
+ File.utime(nil, nil, @file1, @file2)
+ File.atime(@file1).to_i.should be_close(Time.now.to_i, 2)
+ File.mtime(@file1).to_i.should be_close(Time.now.to_i, 2)
+ File.atime(@file2).to_i.should be_close(Time.now.to_i, 2)
+ File.mtime(@file2).to_i.should be_close(Time.now.to_i, 2)
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.utime(@atime, @mtime, mock_to_path(@file1), mock_to_path(@file2))
+ end
+
+ it "accepts numeric atime and mtime arguments" do
+ File.utime(@atime.to_i, @mtime.to_i, @file1, @file2)
+ File.atime(@file1).to_i.should be_close(@atime.to_i, 2)
+ File.mtime(@file1).to_i.should be_close(@mtime.to_i, 2)
+ File.atime(@file2).to_i.should be_close(@atime.to_i, 2)
+ File.mtime(@file2).to_i.should be_close(@mtime.to_i, 2)
+ end
+
+ platform_is :linux do
+ platform_is wordsize: 64 do
+ it "allows Time instances in the far future to set mtime and atime" do
+ time = Time.at(1<<44)
+ File.utime(time, time, @file1)
+ File.atime(@file1).year.should == 559444
+ File.mtime(@file1).year.should == 559444
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/world_readable_spec.rb b/spec/ruby/core/file/world_readable_spec.rb
new file mode 100644
index 0000000000..7e6c60b65c
--- /dev/null
+++ b/spec/ruby/core/file/world_readable_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/world_readable'
+
+describe "File.world_readable?" do
+ it_behaves_like :file_world_readable, :world_readable?, File
+
+ it "returns nil if the file does not exist" do
+ file = rand.to_s + $$.to_s
+ File.exist?(file).should be_false
+ File.world_readable?(file).should be_nil
+ end
+end
diff --git a/spec/ruby/core/file/world_writable_spec.rb b/spec/ruby/core/file/world_writable_spec.rb
new file mode 100644
index 0000000000..00333a98ab
--- /dev/null
+++ b/spec/ruby/core/file/world_writable_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/world_writable'
+
+describe "File.world_writable?" do
+ it_behaves_like :file_world_writable, :world_writable?, File
+
+ it "returns nil if the file does not exist" do
+ file = rand.to_s + $$.to_s
+ File.exist?(file).should be_false
+ File.world_writable?(file).should be_nil
+ end
+end
diff --git a/spec/ruby/core/file/writable_real_spec.rb b/spec/ruby/core/file/writable_real_spec.rb
new file mode 100644
index 0000000000..bea4c4c262
--- /dev/null
+++ b/spec/ruby/core/file/writable_real_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/writable_real'
+
+describe "File.writable_real?" do
+ it_behaves_like :file_writable_real, :writable_real?, File
+ it_behaves_like :file_writable_real_missing, :writable_real?, File
+end
diff --git a/spec/ruby/core/file/writable_spec.rb b/spec/ruby/core/file/writable_spec.rb
new file mode 100644
index 0000000000..519837b0d1
--- /dev/null
+++ b/spec/ruby/core/file/writable_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/writable'
+
+describe "File.writable?" do
+ it_behaves_like :file_writable, :writable?, File
+ it_behaves_like :file_writable_missing, :writable?, File
+end
diff --git a/spec/ruby/core/file/zero_spec.rb b/spec/ruby/core/file/zero_spec.rb
new file mode 100644
index 0000000000..63dd85ee46
--- /dev/null
+++ b/spec/ruby/core/file/zero_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/zero'
+
+describe "File.zero?" do
+ it_behaves_like :file_zero, :zero?, File
+ it_behaves_like :file_zero_missing, :zero?, File
+
+ platform_is :solaris do
+ it "returns false for /dev/null" do
+ File.zero?('/dev/null').should == true
+ end
+ end
+end
diff --git a/spec/ruby/core/filetest/blockdev_spec.rb b/spec/ruby/core/filetest/blockdev_spec.rb
new file mode 100644
index 0000000000..4f32991c4a
--- /dev/null
+++ b/spec/ruby/core/filetest/blockdev_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/blockdev'
+
+describe "FileTest.blockdev?" do
+ it_behaves_like :file_blockdev, :blockdev?, FileTest
+end
diff --git a/spec/ruby/core/filetest/chardev_spec.rb b/spec/ruby/core/filetest/chardev_spec.rb
new file mode 100644
index 0000000000..59c48bb2d5
--- /dev/null
+++ b/spec/ruby/core/filetest/chardev_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/chardev'
+
+describe "FileTest.chardev?" do
+ it_behaves_like :file_chardev, :chardev?, FileTest
+end
diff --git a/spec/ruby/core/filetest/directory_spec.rb b/spec/ruby/core/filetest/directory_spec.rb
new file mode 100644
index 0000000000..8f9d0e3901
--- /dev/null
+++ b/spec/ruby/core/filetest/directory_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/directory'
+
+describe "FileTest.directory?" do
+ it_behaves_like :file_directory, :directory?, FileTest
+end
+
+describe "FileTest.directory?" do
+ it_behaves_like :file_directory_io, :directory?, FileTest
+end
diff --git a/spec/ruby/core/filetest/executable_real_spec.rb b/spec/ruby/core/filetest/executable_real_spec.rb
new file mode 100644
index 0000000000..da65245785
--- /dev/null
+++ b/spec/ruby/core/filetest/executable_real_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/executable_real'
+
+describe "FileTest.executable_real?" do
+ it_behaves_like :file_executable_real, :executable_real?, FileTest
+ it_behaves_like :file_executable_real_missing, :executable_real?, FileTest
+end
diff --git a/spec/ruby/core/filetest/executable_spec.rb b/spec/ruby/core/filetest/executable_spec.rb
new file mode 100644
index 0000000000..03056669f6
--- /dev/null
+++ b/spec/ruby/core/filetest/executable_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/executable'
+
+describe "FileTest.executable?" do
+ it_behaves_like :file_executable, :executable?, FileTest
+ it_behaves_like :file_executable_missing, :executable?, FileTest
+end
diff --git a/spec/ruby/core/filetest/exist_spec.rb b/spec/ruby/core/filetest/exist_spec.rb
new file mode 100644
index 0000000000..4d14bea231
--- /dev/null
+++ b/spec/ruby/core/filetest/exist_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/exist'
+
+describe "FileTest.exist?" do
+ it_behaves_like :file_exist, :exist?, FileTest
+end
diff --git a/spec/ruby/core/filetest/exists_spec.rb b/spec/ruby/core/filetest/exists_spec.rb
new file mode 100644
index 0000000000..d090d7d740
--- /dev/null
+++ b/spec/ruby/core/filetest/exists_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/exist'
+
+describe "FileTest.exists?" do
+ it_behaves_like :file_exist, :exists?, FileTest
+end
diff --git a/spec/ruby/core/filetest/file_spec.rb b/spec/ruby/core/filetest/file_spec.rb
new file mode 100644
index 0000000000..0c0cb82f96
--- /dev/null
+++ b/spec/ruby/core/filetest/file_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/file'
+
+describe "File.file?" do
+ it_behaves_like :file_file, :file?, File
+end
+
+describe "FileTest.file?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/filetest/grpowned_spec.rb b/spec/ruby/core/filetest/grpowned_spec.rb
new file mode 100644
index 0000000000..d073cb9770
--- /dev/null
+++ b/spec/ruby/core/filetest/grpowned_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/grpowned'
+
+describe "FileTest.grpowned?" do
+ it_behaves_like :file_grpowned, :grpowned?, FileTest
+
+ it "returns false if the file doesn't exist" do
+ FileTest.grpowned?("xxx-tmp-doesnt_exist-blah").should be_false
+ end
+end
diff --git a/spec/ruby/core/filetest/identical_spec.rb b/spec/ruby/core/filetest/identical_spec.rb
new file mode 100644
index 0000000000..b00c5b75e8
--- /dev/null
+++ b/spec/ruby/core/filetest/identical_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/identical'
+
+describe "FileTest.identical?" do
+ it_behaves_like :file_identical, :identical?, FileTest
+end
diff --git a/spec/ruby/core/filetest/owned_spec.rb b/spec/ruby/core/filetest/owned_spec.rb
new file mode 100644
index 0000000000..5ffd8ced26
--- /dev/null
+++ b/spec/ruby/core/filetest/owned_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/owned'
+
+describe "FileTest.owned?" do
+ it_behaves_like :file_owned, :owned?, FileTest
+end
+
+describe "FileTest.owned?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/filetest/pipe_spec.rb b/spec/ruby/core/filetest/pipe_spec.rb
new file mode 100644
index 0000000000..912635b25f
--- /dev/null
+++ b/spec/ruby/core/filetest/pipe_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/pipe'
+
+describe "FileTest.pipe?" do
+ it_behaves_like :file_pipe, :pipe?, FileTest
+end
+
+describe "FileTest.pipe?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/filetest/readable_real_spec.rb b/spec/ruby/core/filetest/readable_real_spec.rb
new file mode 100644
index 0000000000..82c62fe8f0
--- /dev/null
+++ b/spec/ruby/core/filetest/readable_real_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/readable_real'
+
+describe "FileTest.readable_real?" do
+ it_behaves_like :file_readable_real, :readable_real?, FileTest
+ it_behaves_like :file_readable_real_missing, :readable_real?, FileTest
+end
diff --git a/spec/ruby/core/filetest/readable_spec.rb b/spec/ruby/core/filetest/readable_spec.rb
new file mode 100644
index 0000000000..039ca56ca3
--- /dev/null
+++ b/spec/ruby/core/filetest/readable_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/readable'
+
+describe "FileTest.readable?" do
+ it_behaves_like :file_readable, :readable?, FileTest
+ it_behaves_like :file_readable_missing, :readable?, FileTest
+end
diff --git a/spec/ruby/core/filetest/setgid_spec.rb b/spec/ruby/core/filetest/setgid_spec.rb
new file mode 100644
index 0000000000..e0feb3c8a9
--- /dev/null
+++ b/spec/ruby/core/filetest/setgid_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/setgid'
+
+describe "FileTest.setgid?" do
+ it_behaves_like :file_setgid, :setgid?, FileTest
+end
+
+describe "FileTest.setgid?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/filetest/setuid_spec.rb b/spec/ruby/core/filetest/setuid_spec.rb
new file mode 100644
index 0000000000..4371a215ba
--- /dev/null
+++ b/spec/ruby/core/filetest/setuid_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/setuid'
+
+describe "FileTest.setuid?" do
+ it_behaves_like :file_setuid, :setuid?, FileTest
+end
+
+describe "FileTest.setuid?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/filetest/size_spec.rb b/spec/ruby/core/filetest/size_spec.rb
new file mode 100644
index 0000000000..dc3ddb127f
--- /dev/null
+++ b/spec/ruby/core/filetest/size_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/size'
+
+describe "FileTest.size?" do
+ it_behaves_like :file_size, :size?, FileTest
+end
+
+describe "FileTest.size?" do
+ it_behaves_like :file_size_nil_when_missing, :size?, FileTest
+end
+
+describe "FileTest.size?" do
+ it_behaves_like :file_size_nil_when_empty, :size?, FileTest
+end
+
+describe "FileTest.size?" do
+ it_behaves_like :file_size_with_file_argument, :size?, FileTest
+end
+
+describe "FileTest.size" do
+ it_behaves_like :file_size, :size, FileTest
+end
+
+describe "FileTest.size" do
+ it_behaves_like :file_size_raise_when_missing, :size, FileTest
+end
+
+describe "FileTest.size" do
+ it_behaves_like :file_size_0_when_empty, :size, FileTest
+end
+
+describe "FileTest.size" do
+ it_behaves_like :file_size_with_file_argument, :size, FileTest
+end
diff --git a/spec/ruby/core/filetest/socket_spec.rb b/spec/ruby/core/filetest/socket_spec.rb
new file mode 100644
index 0000000000..1373c7e370
--- /dev/null
+++ b/spec/ruby/core/filetest/socket_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/socket'
+
+describe "FileTest.socket?" do
+ it_behaves_like :file_socket, :socket?, FileTest
+end
+
+describe "FileTest.socket?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/filetest/sticky_spec.rb b/spec/ruby/core/filetest/sticky_spec.rb
new file mode 100644
index 0000000000..8b776b6672
--- /dev/null
+++ b/spec/ruby/core/filetest/sticky_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/sticky'
+
+describe "FileTest.sticky?" do
+ it_behaves_like :file_sticky, :sticky?, FileTest
+ it_behaves_like :file_sticky_missing, :sticky?, FileTest
+end
diff --git a/spec/ruby/core/filetest/symlink_spec.rb b/spec/ruby/core/filetest/symlink_spec.rb
new file mode 100644
index 0000000000..41c924dc1a
--- /dev/null
+++ b/spec/ruby/core/filetest/symlink_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/symlink'
+
+describe "FileTest.symlink?" do
+ it_behaves_like :file_symlink, :symlink?, FileTest
+end
+
+describe "FileTest.symlink?" do
+ it_behaves_like :file_symlink_nonexistent, :symlink?, File
+end
diff --git a/spec/ruby/core/filetest/world_readable_spec.rb b/spec/ruby/core/filetest/world_readable_spec.rb
new file mode 100644
index 0000000000..72abdd9e03
--- /dev/null
+++ b/spec/ruby/core/filetest/world_readable_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "FileTest.world_readable?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/filetest/world_writable_spec.rb b/spec/ruby/core/filetest/world_writable_spec.rb
new file mode 100644
index 0000000000..533f698fd3
--- /dev/null
+++ b/spec/ruby/core/filetest/world_writable_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "FileTest.world_writable?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/filetest/writable_real_spec.rb b/spec/ruby/core/filetest/writable_real_spec.rb
new file mode 100644
index 0000000000..64abe4cd3f
--- /dev/null
+++ b/spec/ruby/core/filetest/writable_real_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/writable_real'
+
+describe "FileTest.writable_real?" do
+ it_behaves_like :file_writable_real, :writable_real?, FileTest
+ it_behaves_like :file_writable_real_missing, :writable_real?, FileTest
+end
diff --git a/spec/ruby/core/filetest/writable_spec.rb b/spec/ruby/core/filetest/writable_spec.rb
new file mode 100644
index 0000000000..e921a5887b
--- /dev/null
+++ b/spec/ruby/core/filetest/writable_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/writable'
+
+describe "FileTest.writable?" do
+ it_behaves_like :file_writable, :writable?, FileTest
+ it_behaves_like :file_writable_missing, :writable?, FileTest
+end
diff --git a/spec/ruby/core/filetest/zero_spec.rb b/spec/ruby/core/filetest/zero_spec.rb
new file mode 100644
index 0000000000..dd6a164ec9
--- /dev/null
+++ b/spec/ruby/core/filetest/zero_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/file/zero'
+
+describe "FileTest.zero?" do
+ it_behaves_like :file_zero, :zero?, FileTest
+ it_behaves_like :file_zero_missing, :zero?, FileTest
+
+ platform_is :solaris do
+ it "returns false for /dev/null" do
+ File.zero?('/dev/null').should == true
+ end
+ end
+end
diff --git a/spec/ruby/core/float/abs_spec.rb b/spec/ruby/core/float/abs_spec.rb
new file mode 100644
index 0000000000..a08601926d
--- /dev/null
+++ b/spec/ruby/core/float/abs_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/abs'
+
+describe "Float#abs" do
+ it_behaves_like :float_abs, :abs
+end
diff --git a/spec/ruby/core/float/angle_spec.rb b/spec/ruby/core/float/angle_spec.rb
new file mode 100644
index 0000000000..c07249aa97
--- /dev/null
+++ b/spec/ruby/core/float/angle_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arg'
+
+describe "Float#angle" do
+ it_behaves_like :float_arg, :angle
+end
diff --git a/spec/ruby/core/float/arg_spec.rb b/spec/ruby/core/float/arg_spec.rb
new file mode 100644
index 0000000000..d3a50668f8
--- /dev/null
+++ b/spec/ruby/core/float/arg_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arg'
+
+describe "Float#arg" do
+ it_behaves_like :float_arg, :arg
+end
diff --git a/spec/ruby/core/float/case_compare_spec.rb b/spec/ruby/core/float/case_compare_spec.rb
new file mode 100644
index 0000000000..b902fbea18
--- /dev/null
+++ b/spec/ruby/core/float/case_compare_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/equal'
+
+describe "Float#===" do
+ it_behaves_like :float_equal, :===
+end
diff --git a/spec/ruby/core/float/ceil_spec.rb b/spec/ruby/core/float/ceil_spec.rb
new file mode 100644
index 0000000000..8a4f72c70e
--- /dev/null
+++ b/spec/ruby/core/float/ceil_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+
+describe "Float#ceil" do
+ it "returns the smallest Integer greater than or equal to self" do
+ -1.2.ceil.should eql( -1)
+ -1.0.ceil.should eql( -1)
+ 0.0.ceil.should eql( 0 )
+ 1.3.ceil.should eql( 2 )
+ 3.0.ceil.should eql( 3 )
+ -9223372036854775808.1.ceil.should eql(-9223372036854775808)
+ +9223372036854775808.1.ceil.should eql(+9223372036854775808)
+ end
+
+ ruby_version_is "2.4" do
+ it "returns the smallest number greater than or equal to self with an optionally given precision" do
+ 2.1679.ceil(0).should eql(3)
+ 214.94.ceil(-1).should eql(220)
+ 7.0.ceil(1).should eql(7.0)
+ -1.234.ceil(2).should eql(-1.23)
+ 5.123812.ceil(4).should eql(5.1239)
+ end
+ end
+end
diff --git a/spec/ruby/core/float/coerce_spec.rb b/spec/ruby/core/float/coerce_spec.rb
new file mode 100644
index 0000000000..ea108f3303
--- /dev/null
+++ b/spec/ruby/core/float/coerce_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+
+describe "Float#coerce" do
+ it "returns [other, self] both as Floats" do
+ 1.2.coerce(1).should == [1.0, 1.2]
+ 5.28.coerce(1.0).should == [1.0, 5.28]
+ 1.0.coerce(1).should == [1.0, 1.0]
+ 1.0.coerce("2.5").should == [2.5, 1.0]
+ 1.0.coerce(3.14).should == [3.14, 1.0]
+
+ a, b = -0.0.coerce(bignum_value)
+ a.should be_close(9223372036854775808.0, TOLERANCE)
+ b.should be_close(-0.0, TOLERANCE)
+ a, b = 1.0.coerce(bignum_value)
+ a.should be_close(9223372036854775808.0, TOLERANCE)
+ b.should be_close(1.0, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/float/comparison_spec.rb b/spec/ruby/core/float/comparison_spec.rb
new file mode 100644
index 0000000000..a1ee423c24
--- /dev/null
+++ b/spec/ruby/core/float/comparison_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+
+describe "Float#<=>" do
+ it "returns -1, 0, 1 when self is less than, equal, or greater than other" do
+ (1.5 <=> 5).should == -1
+ (2.45 <=> 2.45).should == 0
+ ((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
+ end
+
+ it "returns nil when the given argument is not a Float" do
+ (1.0 <=> "1").should be_nil
+ 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 a Bignum" do
+ (infinity_value <=> Float::MAX.to_i*2).should == 1
+ end
+
+ it "returns -1 when self is negative and other is Infinty" 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
+end
diff --git a/spec/ruby/core/float/constants_spec.rb b/spec/ruby/core/float/constants_spec.rb
new file mode 100644
index 0000000000..497e0ae188
--- /dev/null
+++ b/spec/ruby/core/float/constants_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../../spec_helper'
+
+describe "Float constant" do
+ it "DIG is 15" do
+ Float::DIG.should == 15
+ end
+
+ it "EPSILON is 2.220446049250313e-16" do
+ Float::EPSILON.should == 2.0 ** -52
+ Float::EPSILON.should == 2.220446049250313e-16
+ end
+
+ it "MANT_DIG is 53" do
+ Float::MANT_DIG.should == 53
+ end
+
+ it "MAX_10_EXP is 308" do
+ Float::MAX_10_EXP.should == 308
+ end
+
+ it "MIN_10_EXP is -308" do
+ Float::MIN_10_EXP.should == -307
+ end
+
+ it "MAX_EXP is 1024" do
+ Float::MAX_EXP.should == 1024
+ end
+
+ it "MIN_EXP is -1021" do
+ Float::MIN_EXP.should == -1021
+ end
+
+ it "MAX is 1.7976931348623157e+308" do
+ # See https://en.wikipedia.org/wiki/Double-precision_floating-point_format#Double-precision_examples
+ Float::MAX.should == (1 + (1 - (2 ** -52))) * (2.0 ** 1023)
+ Float::MAX.should == 1.7976931348623157e+308
+ end
+
+ it "MIN is 2.2250738585072014e-308" do
+ Float::MIN.should == (2.0 ** -1022)
+ Float::MIN.should == 2.2250738585072014e-308
+ end
+
+ it "RADIX is 2" do
+ Float::RADIX.should == 2
+ end
+
+ it "INFINITY is the positive infinity" do
+ Float::INFINITY.infinite?.should == 1
+ end
+
+ it "NAN is 'not a number'" do
+ Float::NAN.nan?.should be_true
+ end
+end
diff --git a/spec/ruby/core/float/denominator_spec.rb b/spec/ruby/core/float/denominator_spec.rb
new file mode 100644
index 0000000000..6f4fcfcf23
--- /dev/null
+++ b/spec/ruby/core/float/denominator_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+
+describe "Float#denominator" do
+ before :each do
+ @numbers = [
+ 0.0,
+ 29871.22736282,
+ 7772222663.0,
+ 1.4592,
+ ].map {|n| [0-n, n]}.flatten
+ end
+
+ it "returns an Integer" do
+ @numbers.each do |number|
+ number.denominator.should be_kind_of(Integer)
+ end
+ end
+
+ it "converts self to a Rational and returns the denominator" do
+ @numbers.each do |number|
+ number.denominator.should == Rational(number).denominator
+ end
+ end
+
+ it "returns 1 for NaN and Infinity" do
+ nan_value.denominator.should == 1
+ infinity_value.denominator.should == 1
+ end
+end
diff --git a/spec/ruby/core/float/divide_spec.rb b/spec/ruby/core/float/divide_spec.rb
new file mode 100644
index 0000000000..f41b9f1f93
--- /dev/null
+++ b/spec/ruby/core/float/divide_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/coerce'
+require_relative 'shared/arithmetic_exception_in_coerce'
+
+describe "Float#/" do
+ it_behaves_like :float_arithmetic_exception_in_coerce, :/
+
+ it "returns self divided by other" do
+ (5.75 / -2).should be_close(-2.875,TOLERANCE)
+ (451.0 / 9.3).should be_close(48.494623655914,TOLERANCE)
+ (91.1 / -0xffffffff).should be_close(-2.12108716418061e-08, TOLERANCE)
+ end
+
+ it "properly coerces objects" do
+ (5.0 / FloatSpecs::CanCoerce.new(5)).should be_close(0, TOLERANCE)
+ end
+
+ it "returns +Infinity when dividing non-zero by zero of the same sign" do
+ (1.0 / 0.0).should be_positive_infinity
+ (-1.0 / -0.0).should be_positive_infinity
+ end
+
+ it "returns -Infinity when dividing non-zero by zero of opposite sign" do
+ (-1.0 / 0.0).should be_negative_infinity
+ (1.0 / -0.0).should be_negative_infinity
+ end
+
+ it "returns NaN when dividing zero by zero" do
+ (0.0 / 0.0).should be_nan
+ (-0.0 / 0.0).should be_nan
+ (0.0 / -0.0).should be_nan
+ (-0.0 / -0.0).should be_nan
+ end
+
+ it "raises a TypeError when given a non-Numeric" do
+ lambda { 13.0 / "10" }.should raise_error(TypeError)
+ lambda { 13.0 / :symbol }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/float/divmod_spec.rb b/spec/ruby/core/float/divmod_spec.rb
new file mode 100644
index 0000000000..5983efe1d9
--- /dev/null
+++ b/spec/ruby/core/float/divmod_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+
+describe "Float#divmod" do
+ it "returns an [quotient, modulus] from dividing self by other" do
+ values = 3.14.divmod(2)
+ values[0].should eql(1)
+ values[1].should be_close(1.14, TOLERANCE)
+ values = 2.8284.divmod(3.1415)
+ values[0].should eql(0)
+ values[1].should be_close(2.8284, TOLERANCE)
+ values = -1.0.divmod(bignum_value)
+ values[0].should eql(-1)
+ values[1].should be_close(9223372036854775808.000, TOLERANCE)
+ values = -1.0.divmod(1)
+ values[0].should eql(-1)
+ values[1].should eql(0.0)
+ end
+
+ # Behaviour established as correct in r23953
+ it "raises a FloatDomainError if self is NaN" do
+ lambda { nan_value.divmod(1) }.should raise_error(FloatDomainError)
+ end
+
+ # Behaviour established as correct in r23953
+ it "raises a FloatDomainError if other is NaN" do
+ lambda { 1.divmod(nan_value) }.should raise_error(FloatDomainError)
+ end
+
+ # Behaviour established as correct in r23953
+ it "raises a FloatDomainError if self is Infinity" do
+ lambda { infinity_value.divmod(1) }.should raise_error(FloatDomainError)
+ end
+
+ it "raises a ZeroDivisionError if other is zero" do
+ lambda { 1.0.divmod(0) }.should raise_error(ZeroDivisionError)
+ lambda { 1.0.divmod(0.0) }.should raise_error(ZeroDivisionError)
+ end
+
+ # redmine #5276"
+ it "returns the correct [quotient, modulus] even for large quotient" do
+ 0.59.divmod(7.761021455128987e-11).first.should eql(7602092113)
+ end
+end
diff --git a/spec/ruby/core/float/dup_spec.rb b/spec/ruby/core/float/dup_spec.rb
new file mode 100644
index 0000000000..8df7260652
--- /dev/null
+++ b/spec/ruby/core/float/dup_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '2.4' do
+ describe "Float#dup" do
+ it "returns self" do
+ float = 2.4
+ float.dup.should equal(float)
+ end
+ end
+end
diff --git a/spec/ruby/core/float/eql_spec.rb b/spec/ruby/core/float/eql_spec.rb
new file mode 100644
index 0000000000..6b5f91db33
--- /dev/null
+++ b/spec/ruby/core/float/eql_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+
+describe "Float#eql?" do
+ it "returns true if other is a Float equal to self" do
+ 0.0.eql?(0.0).should be_true
+ end
+
+ it "returns false if other is a Float not equal to self" do
+ 1.0.eql?(1.1).should be_false
+ end
+
+ it "returns false if other is not a Float" do
+ 1.0.eql?(1).should be_false
+ 1.0.eql?(:one).should be_false
+ end
+end
diff --git a/spec/ruby/core/float/equal_value_spec.rb b/spec/ruby/core/float/equal_value_spec.rb
new file mode 100644
index 0000000000..03eea5108e
--- /dev/null
+++ b/spec/ruby/core/float/equal_value_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/equal'
+
+describe "Float#==" do
+ it_behaves_like :float_equal, :==
+end
diff --git a/spec/ruby/core/float/exponent_spec.rb b/spec/ruby/core/float/exponent_spec.rb
new file mode 100644
index 0000000000..a4c03469a7
--- /dev/null
+++ b/spec/ruby/core/float/exponent_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "Float#**" do
+ it "returns self raise to the other power" do
+ (2.3 ** 3).should be_close(12.167,TOLERANCE)
+ (5.2 ** -1).should be_close(0.192307692307692,TOLERANCE)
+ (9.5 ** 0.5).should be_close(3.08220700148449, TOLERANCE)
+ (9.5 ** 0xffffffff).to_s.should == 'Infinity'
+ end
+
+ it "returns a complex number when negative and raised to a fractional power" do
+ ((-8.0) ** (1.0/3)) .should be_close(Complex(1, 1.73205), TOLERANCE)
+ ((-8.0) ** Rational(1,3)).should be_close(Complex(1, 1.73205), TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/float/fdiv_spec.rb b/spec/ruby/core/float/fdiv_spec.rb
new file mode 100644
index 0000000000..be25ee283b
--- /dev/null
+++ b/spec/ruby/core/float/fdiv_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/quo'
+
+describe "Float#fdiv" do
+ it_behaves_like :float_quo, :fdiv
+end
diff --git a/spec/ruby/core/float/finite_spec.rb b/spec/ruby/core/float/finite_spec.rb
new file mode 100644
index 0000000000..c5fb3df849
--- /dev/null
+++ b/spec/ruby/core/float/finite_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+describe "Float#finite?" do
+ it "returns true for finite values" do
+ 3.14159.finite?.should == true
+ end
+
+ it "returns false for positive infinity" do
+ infinity_value.finite?.should == false
+ end
+
+ it "returns false for negative infinity" do
+ (-infinity_value).finite?.should == false
+ end
+
+ it "returns false for NaN" do
+ nan_value.finite?.should == false
+ end
+end
diff --git a/spec/ruby/core/float/fixtures/classes.rb b/spec/ruby/core/float/fixtures/classes.rb
new file mode 100644
index 0000000000..2d80184e7d
--- /dev/null
+++ b/spec/ruby/core/float/fixtures/classes.rb
@@ -0,0 +1,4 @@
+module FloatSpecs
+ class CoerceError < StandardError
+ end
+end
diff --git a/spec/ruby/core/float/fixtures/coerce.rb b/spec/ruby/core/float/fixtures/coerce.rb
new file mode 100644
index 0000000000..2cf155be95
--- /dev/null
+++ b/spec/ruby/core/float/fixtures/coerce.rb
@@ -0,0 +1,15 @@
+module FloatSpecs
+ class CanCoerce
+ def initialize(a)
+ @a = a
+ end
+
+ def coerce(b)
+ [self.class.new(b), @a]
+ end
+
+ def /(b)
+ @a.to_i % b.to_i
+ end
+ end
+end
diff --git a/spec/ruby/core/float/float_spec.rb b/spec/ruby/core/float/float_spec.rb
new file mode 100644
index 0000000000..3ad4ce817a
--- /dev/null
+++ b/spec/ruby/core/float/float_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+describe "Float" do
+ it "includes Comparable" do
+ Float.include?(Comparable).should == true
+ end
+
+ it ".allocate raises a TypeError" do
+ lambda do
+ Float.allocate
+ end.should raise_error(TypeError)
+ end
+
+ it ".new is undefined" do
+ lambda do
+ Float.new
+ end.should raise_error(NoMethodError)
+ end
+end
diff --git a/spec/ruby/core/float/floor_spec.rb b/spec/ruby/core/float/floor_spec.rb
new file mode 100644
index 0000000000..f20eccae73
--- /dev/null
+++ b/spec/ruby/core/float/floor_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+
+describe "Float#floor" do
+ it "returns the largest Integer less than or equal to self" do
+ -1.2.floor.should eql( -2)
+ -1.0.floor.should eql( -1)
+ 0.0.floor.should eql( 0 )
+ 1.0.floor.should eql( 1 )
+ 5.9.floor.should eql( 5 )
+ -9223372036854775808.1.floor.should eql(-9223372036854775808)
+ +9223372036854775808.1.floor.should eql(+9223372036854775808)
+ end
+
+ ruby_version_is "2.4" do
+ it "returns the largest number less than or equal to self with an optionally given precision" do
+ 2.1679.floor(0).should eql(2)
+ 214.94.floor(-1).should eql(210)
+ 7.0.floor(1).should eql(7.0)
+ -1.234.floor(2).should eql(-1.24)
+ 5.123812.floor(4).should eql(5.1238)
+ end
+ end
+end
diff --git a/spec/ruby/core/float/gt_spec.rb b/spec/ruby/core/float/gt_spec.rb
new file mode 100644
index 0000000000..6ecab3592d
--- /dev/null
+++ b/spec/ruby/core/float/gt_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require_relative 'shared/comparison_exception_in_coerce'
+
+describe "Float#>" do
+ it_behaves_like :float_comparison_exception_in_coerce, :>
+
+ it "returns true if self is greater than other" do
+ (1.5 > 1).should == true
+ (2.5 > 3).should == false
+ (45.91 > bignum_value).should == false
+ end
+
+ it "raises an ArgumentError when given a non-Numeric" do
+ lambda { 5.0 > "4" }.should raise_error(ArgumentError)
+ lambda { 5.0 > mock('x') }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/float/gte_spec.rb b/spec/ruby/core/float/gte_spec.rb
new file mode 100644
index 0000000000..0886dffd97
--- /dev/null
+++ b/spec/ruby/core/float/gte_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require_relative 'shared/comparison_exception_in_coerce'
+
+describe "Float#>=" do
+ it_behaves_like :float_comparison_exception_in_coerce, :>=
+
+ it "returns true if self is greater than or equal to other" do
+ (5.2 >= 5.2).should == true
+ (9.71 >= 1).should == true
+ (5.55382 >= 0xfabdafbafcab).should == false
+ end
+
+ it "raises an ArgumentError when given a non-Numeric" do
+ lambda { 5.0 >= "4" }.should raise_error(ArgumentError)
+ lambda { 5.0 >= mock('x') }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/float/hash_spec.rb b/spec/ruby/core/float/hash_spec.rb
new file mode 100644
index 0000000000..5f77e3b4a1
--- /dev/null
+++ b/spec/ruby/core/float/hash_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Float#hash" do
+ it "is provided" do
+ 0.0.respond_to?(:hash).should == true
+ end
+
+ it "is stable" do
+ 1.0.hash.should == 1.0.hash
+ end
+end
diff --git a/spec/ruby/core/float/infinite_spec.rb b/spec/ruby/core/float/infinite_spec.rb
new file mode 100644
index 0000000000..901c2738aa
--- /dev/null
+++ b/spec/ruby/core/float/infinite_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+describe "Float#infinite?" do
+ it "returns nil for finite values" do
+ 1.0.infinite?.should == nil
+ end
+
+ it "returns 1 for positive infinity" do
+ infinity_value.infinite?.should == 1
+ end
+
+ it "returns -1 for negative infinity" do
+ (-infinity_value).infinite?.should == -1
+ end
+
+ it "returns nil for NaN" do
+ nan_value.infinite?.should == nil
+ end
+end
diff --git a/spec/ruby/core/float/lt_spec.rb b/spec/ruby/core/float/lt_spec.rb
new file mode 100644
index 0000000000..9723b59c5e
--- /dev/null
+++ b/spec/ruby/core/float/lt_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require_relative 'shared/comparison_exception_in_coerce'
+
+describe "Float#<" do
+ it_behaves_like :float_comparison_exception_in_coerce, :<
+
+ it "returns true if self is less than other" do
+ (71.3 < 91.8).should == true
+ (192.6 < -500).should == false
+ (-0.12 < 0x4fffffff).should == true
+ end
+
+ it "raises an ArgumentError when given a non-Numeric" do
+ lambda { 5.0 < "4" }.should raise_error(ArgumentError)
+ lambda { 5.0 < mock('x') }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/float/lte_spec.rb b/spec/ruby/core/float/lte_spec.rb
new file mode 100644
index 0000000000..0a0a06d753
--- /dev/null
+++ b/spec/ruby/core/float/lte_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require_relative 'shared/comparison_exception_in_coerce'
+
+describe "Float#<=" do
+ it_behaves_like :float_comparison_exception_in_coerce, :>=
+
+ it "returns true if self is less than or equal to other" do
+ (2.0 <= 3.14159).should == true
+ (-2.7183 <= -24).should == false
+ (0.0 <= 0.0).should == true
+ (9_235.9 <= bignum_value).should == true
+ end
+
+ it "raises an ArgumentError when given a non-Numeric" do
+ lambda { 5.0 <= "4" }.should raise_error(ArgumentError)
+ lambda { 5.0 <= mock('x') }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/float/magnitude_spec.rb b/spec/ruby/core/float/magnitude_spec.rb
new file mode 100644
index 0000000000..db577c15c5
--- /dev/null
+++ b/spec/ruby/core/float/magnitude_spec.rb
@@ -0,0 +1,5 @@
+require_relative 'shared/abs'
+
+describe "Float#magnitude" do
+ it_behaves_like :float_abs, :magnitude
+end
diff --git a/spec/ruby/core/float/minus_spec.rb b/spec/ruby/core/float/minus_spec.rb
new file mode 100644
index 0000000000..5626cbdac2
--- /dev/null
+++ b/spec/ruby/core/float/minus_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arithmetic_exception_in_coerce'
+
+describe "Float#-" do
+ it_behaves_like :float_arithmetic_exception_in_coerce, :-
+
+ it "returns self minus other" do
+ (9_237_212.5280 - 5_280).should be_close(9231932.528, TOLERANCE)
+ (2_560_496.1691 - bignum_value).should be_close(-9223372036852215808.000, TOLERANCE)
+ (5.5 - 5.5).should be_close(0.0,TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/float/modulo_spec.rb b/spec/ruby/core/float/modulo_spec.rb
new file mode 100644
index 0000000000..8ae80a0b05
--- /dev/null
+++ b/spec/ruby/core/float/modulo_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'shared/modulo'
+
+describe "Float#%" do
+ it_behaves_like :float_modulo, :%
+end
+
+describe "Float#modulo" do
+ it_behaves_like :float_modulo, :modulo
+end
diff --git a/spec/ruby/core/float/multiply_spec.rb b/spec/ruby/core/float/multiply_spec.rb
new file mode 100644
index 0000000000..69a5dcc95a
--- /dev/null
+++ b/spec/ruby/core/float/multiply_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arithmetic_exception_in_coerce'
+
+describe "Float#*" do
+ it_behaves_like :float_arithmetic_exception_in_coerce, :*
+
+ it "returns self multiplied by other" do
+ (4923.98221 * 2).should be_close(9847.96442, TOLERANCE)
+ (6712.5 * 0.25).should be_close(1678.125, TOLERANCE)
+ (256.4096 * bignum_value).should be_close(2364961134621118431232.000, TOLERANCE)
+ end
+
+ it "raises a TypeError when given a non-Numeric" do
+ lambda { 13.0 * "10" }.should raise_error(TypeError)
+ lambda { 13.0 * :symbol }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/float/nan_spec.rb b/spec/ruby/core/float/nan_spec.rb
new file mode 100644
index 0000000000..d09d25153c
--- /dev/null
+++ b/spec/ruby/core/float/nan_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Float#nan?" do
+ it "returns true if self is not a valid IEEE floating-point number" do
+ 0.0.nan?.should == false
+ -1.5.nan?.should == false
+ nan_value.nan?.should == true
+ end
+end
diff --git a/spec/ruby/core/float/next_float_spec.rb b/spec/ruby/core/float/next_float_spec.rb
new file mode 100644
index 0000000000..2f0eff605a
--- /dev/null
+++ b/spec/ruby/core/float/next_float_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+
+describe "Float#next_float" do
+ it "returns a float the smallest possible step greater than the receiver" do
+ barely_positive = 0.0.next_float
+ barely_positive.should == 0.0.next_float
+
+ barely_positive.should > 0.0
+ barely_positive.should < barely_positive.next_float
+
+ midpoint = barely_positive / 2
+ [0.0, barely_positive].should include midpoint
+ end
+
+ it "returns Float::INFINITY for Float::INFINITY" do
+ Float::INFINITY.next_float.should == Float::INFINITY
+ end
+
+ it "steps directly between MAX and INFINITY" do
+ (-Float::INFINITY).next_float.should == -Float::MAX
+ Float::MAX.next_float.should == Float::INFINITY
+ end
+
+ it "steps directly between 1.0 and 1.0 + EPSILON" do
+ 1.0.next_float.should == 1.0 + Float::EPSILON
+ end
+
+ it "steps directly between -1.0 and -1.0 + EPSILON/2" do
+ (-1.0).next_float.should == -1.0 + Float::EPSILON/2
+ end
+
+ it "reverses the effect of prev_float for all Floats except INFINITY and +0.0" do
+ num = -rand
+ num.prev_float.next_float.should == num
+ end
+
+ it "returns negative zero when stepping upward from just below zero" do
+ x = (-0.0).prev_float.next_float
+ (1/x).should == -Float::INFINITY
+ end
+
+ it "gives the same result for -0.0 as for +0.0" do
+ (-0.0).next_float.should == (0.0).next_float
+ end
+
+ it "returns NAN if NAN was the receiver" do
+ Float::NAN.next_float.nan?.should == true
+ end
+end
diff --git a/spec/ruby/core/float/numerator_spec.rb b/spec/ruby/core/float/numerator_spec.rb
new file mode 100644
index 0000000000..7832e8f056
--- /dev/null
+++ b/spec/ruby/core/float/numerator_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+
+describe "Float#numerator" do
+ before :all do
+ @numbers = [
+ 29871.2722891,
+ 999.1**99.928888,
+ -72628191273.22,
+ 29282.2827,
+ -2927.00091,
+ 12.0,
+ Float::MAX,
+ ]
+ end
+
+ it "converts self to a Rational object then returns its numerator" do
+ @numbers.each do |number|
+ number.infinite?.should be_nil
+ number.numerator.should == Rational(number).numerator
+ end
+ end
+
+ it "returns 0 for 0.0" do
+ 0.0.numerator.should == 0
+ end
+
+ it "returns NaN for NaN" do
+ nan_value.numerator.nan?.should be_true
+ end
+
+ it "returns Infinity for Infinity" do
+ infinity_value.numerator.infinite?.should == 1
+ end
+
+ it "returns -Infinity for -Infinity" do
+ (-infinity_value).numerator.infinite?.should == -1
+ end
+
+end
diff --git a/spec/ruby/core/float/phase_spec.rb b/spec/ruby/core/float/phase_spec.rb
new file mode 100644
index 0000000000..2aa84024b4
--- /dev/null
+++ b/spec/ruby/core/float/phase_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arg'
+
+describe "Float#phase" do
+ it_behaves_like :float_arg, :phase
+end
diff --git a/spec/ruby/core/float/plus_spec.rb b/spec/ruby/core/float/plus_spec.rb
new file mode 100644
index 0000000000..06b136a06b
--- /dev/null
+++ b/spec/ruby/core/float/plus_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arithmetic_exception_in_coerce'
+
+describe "Float#+" do
+ it_behaves_like :float_arithmetic_exception_in_coerce, :+
+
+ it "returns self plus other" do
+ (491.213 + 2).should be_close(493.213, TOLERANCE)
+ (9.99 + bignum_value).should be_close(9223372036854775808.000, TOLERANCE)
+ (1001.99 + 5.219).should be_close(1007.209, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/float/prev_float_spec.rb b/spec/ruby/core/float/prev_float_spec.rb
new file mode 100644
index 0000000000..40fcc25a6d
--- /dev/null
+++ b/spec/ruby/core/float/prev_float_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+
+describe "Float#prev_float" do
+ it "returns a float the smallest possible step smaller than the receiver" do
+ barely_negative = 0.0.prev_float
+ barely_negative.should == 0.0.prev_float
+
+ barely_negative.should < 0.0
+ barely_negative.should > barely_negative.prev_float
+
+ midpoint = barely_negative / 2
+ [0.0, barely_negative].should include midpoint
+ end
+
+ it "returns -Float::INFINITY for -Float::INFINITY" do
+ (-Float::INFINITY).prev_float.should == -Float::INFINITY
+ end
+
+ it "steps directly between MAX and INFINITY" do
+ Float::INFINITY.prev_float.should == Float::MAX
+ (-Float::MAX).prev_float.should == -Float::INFINITY
+ end
+
+ it "steps directly between 1.0 and 1.0 - EPSILON/2" do
+ 1.0.prev_float.should == 1.0 - Float::EPSILON/2
+ end
+
+ it "steps directly between -1.0 and -1.0 - EPSILON" do
+ (-1.0).prev_float.should == -1.0 - Float::EPSILON
+ end
+
+ it "reverses the effect of next_float for all Floats except -INFINITY and -0.0" do
+ num = rand
+ num.next_float.prev_float.should == num
+ end
+
+ it "returns positive zero when stepping downward from just above zero" do
+ x = 0.0.next_float.prev_float
+ (1/x).should == Float::INFINITY
+ end
+
+ it "gives the same result for -0.0 as for +0.0" do
+ (0.0).prev_float.should == (-0.0).prev_float
+ end
+
+ it "returns NAN if NAN was the receiver" do
+ Float::NAN.prev_float.nan?.should == true
+ end
+end
diff --git a/spec/ruby/core/float/quo_spec.rb b/spec/ruby/core/float/quo_spec.rb
new file mode 100644
index 0000000000..b5c64f9d87
--- /dev/null
+++ b/spec/ruby/core/float/quo_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/quo'
+
+describe "Float#quo" do
+ it_behaves_like :float_quo, :quo
+end
diff --git a/spec/ruby/core/float/rationalize_spec.rb b/spec/ruby/core/float/rationalize_spec.rb
new file mode 100644
index 0000000000..978425e084
--- /dev/null
+++ b/spec/ruby/core/float/rationalize_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+
+describe "Float#rationalize" do
+ it "returns self as a simplified Rational with no argument" do
+ (3382729202.92822).rationalize.should == Rational(4806858197361, 1421)
+ end
+
+ # FIXME: These specs need reviewing by somebody familiar with the
+ # algorithm used by #rationalize
+ it "simplifies self to the degree specified by a Rational argument" do
+ f = 0.3
+ f.rationalize(Rational(1,10)).should == Rational(1,3)
+ f.rationalize(Rational(-1,10)).should == Rational(1,3)
+
+ f = -f
+ f.rationalize(Rational(1,10)).should == Rational(-1,3)
+ f.rationalize(Rational(-1,10)).should == Rational(-1,3)
+
+ end
+
+ it "simplifies self to the degree specified by a Float argument" do
+ f = 0.3
+ f.rationalize(0.05).should == Rational(1,3)
+ f.rationalize(0.001).should == Rational(3, 10)
+
+ f = -f
+ f.rationalize(0.05).should == Rational(-1,3)
+ f.rationalize(0.001).should == Rational(-3,10)
+ end
+
+ it "raises a FloatDomainError for Infinity" do
+ lambda {infinity_value.rationalize}.should raise_error(FloatDomainError)
+ end
+
+ it "raises a FloatDomainError for NaN" do
+ lambda { nan_value.rationalize }.should raise_error(FloatDomainError)
+ end
+
+ it "raises ArgumentError when passed more than one argument" do
+ lambda { 0.3.rationalize(0.1, 0.1) }.should raise_error(ArgumentError)
+ lambda { 0.3.rationalize(0.1, 0.1, 2) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/float/round_spec.rb b/spec/ruby/core/float/round_spec.rb
new file mode 100644
index 0000000000..e04b376c36
--- /dev/null
+++ b/spec/ruby/core/float/round_spec.rb
@@ -0,0 +1,101 @@
+require_relative '../../spec_helper'
+
+describe "Float#round" do
+ it "returns the nearest Integer" do
+ 5.5.round.should == 6
+ 0.4.round.should == 0
+ 0.6.round.should == 1
+ -1.4.round.should == -1
+ -2.8.round.should == -3
+ 0.0.round.should == 0
+ end
+
+ platform_is_not :mingw32 do
+ it "returns the nearest Integer for Float near the limit" do
+ 0.49999999999999994.round.should == 0
+ -0.49999999999999994.round.should == 0
+ end
+ end
+
+ it "raises FloatDomainError for exceptional values" do
+ lambda { (+infinity_value).round }.should raise_error(FloatDomainError)
+ lambda { (-infinity_value).round }.should raise_error(FloatDomainError)
+ lambda { nan_value.round }.should raise_error(FloatDomainError)
+ end
+
+ it "rounds self to an optionally given precision" do
+ 5.5.round(0).should eql(6)
+ 5.7.round(1).should eql(5.7)
+ 1.2345678.round(2).should == 1.23
+ 123456.78.round(-2).should eql(123500) # rounded up
+ -123456.78.round(-2).should eql(-123500)
+ 12.345678.round(3.999).should == 12.346
+ end
+
+ it "returns zero when passed a negative argument with magitude greater the magitude of the whole number portion of the Float" do
+ 0.8346268.round(-1).should eql(0)
+ end
+
+ it "raises a TypeError when its argument can not be converted to an Integer" do
+ lambda { 1.0.round("4") }.should raise_error(TypeError)
+ lambda { 1.0.round(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises FloatDomainError for exceptional values when passed a non-positive precision" do
+ lambda { Float::INFINITY.round( 0) }.should raise_error(FloatDomainError)
+ lambda { Float::INFINITY.round(-2) }.should raise_error(FloatDomainError)
+ lambda { (-Float::INFINITY).round( 0) }.should raise_error(FloatDomainError)
+ lambda { (-Float::INFINITY).round(-2) }.should raise_error(FloatDomainError)
+ end
+
+ it "raises RangeError for NAN when passed a non-positive precision" do
+ lambda { Float::NAN.round(0) }.should raise_error(RangeError)
+ lambda { Float::NAN.round(-2) }.should raise_error(RangeError)
+ end
+
+ it "returns self for exceptional values when passed a non-negative precision" do
+ Float::INFINITY.round(2).should == Float::INFINITY
+ (-Float::INFINITY).round(2).should == -Float::INFINITY
+ Float::NAN.round(2).should be_nan
+ end
+
+ # redmine:5227
+ it "works for corner cases" do
+ 42.0.round(308).should eql(42.0)
+ 1.0e307.round(2).should eql(1.0e307)
+ end
+
+ # redmine:5271
+ it "returns rounded values for big argument" do
+ 0.42.round(2.0**30).should == 0.42
+ end
+
+ it "returns big values rounded to nearest" do
+ +2.5e20.round(-20).should eql( +3 * 10 ** 20 )
+ -2.5e20.round(-20).should eql( -3 * 10 ** 20 )
+ end
+
+ # redmine #5272
+ it "returns rounded values for big values" do
+ +2.4e20.round(-20).should eql( +2 * 10 ** 20 )
+ -2.4e20.round(-20).should eql( -2 * 10 ** 20 )
+ +2.5e200.round(-200).should eql( +3 * 10 ** 200 )
+ +2.4e200.round(-200).should eql( +2 * 10 ** 200 )
+ -2.5e200.round(-200).should eql( -3 * 10 ** 200 )
+ -2.4e200.round(-200).should eql( -2 * 10 ** 200 )
+ end
+
+ ruby_version_is "2.4" do
+ it "returns different rounded values depending on the half option" do
+ 2.5.round(half: :up).should eql(3)
+ 2.5.round(half: :down).should eql(2)
+ 2.5.round(half: :even).should eql(2)
+ 3.5.round(half: :up).should eql(4)
+ 3.5.round(half: :down).should eql(3)
+ 3.5.round(half: :even).should eql(4)
+ (-2.5).round(half: :up).should eql(-3)
+ (-2.5).round(half: :down).should eql(-2)
+ (-2.5).round(half: :even).should eql(-2)
+ end
+ end
+end
diff --git a/spec/ruby/core/float/shared/abs.rb b/spec/ruby/core/float/shared/abs.rb
new file mode 100644
index 0000000000..607983322d
--- /dev/null
+++ b/spec/ruby/core/float/shared/abs.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+
+describe :float_abs, shared: true do
+ it "returns the absolute value" do
+ -99.1.send(@method).should be_close(99.1, TOLERANCE)
+ 4.5.send(@method).should be_close(4.5, TOLERANCE)
+ 0.0.send(@method).should be_close(0.0, TOLERANCE)
+ end
+
+ it "returns 0.0 if -0.0" do
+ (-0.0).send(@method).should be_positive_zero
+ end
+
+ it "returns Infinity if -Infinity" do
+ (-infinity_value).send(@method).infinite?.should == 1
+ end
+
+ it "returns NaN if NaN" do
+ nan_value.send(@method).nan?.should be_true
+ end
+end
diff --git a/spec/ruby/core/float/shared/arg.rb b/spec/ruby/core/float/shared/arg.rb
new file mode 100644
index 0000000000..136cf19ec8
--- /dev/null
+++ b/spec/ruby/core/float/shared/arg.rb
@@ -0,0 +1,36 @@
+describe :float_arg, shared: true do
+ it "returns NaN if NaN" do
+ f = nan_value
+ f.send(@method).nan?.should be_true
+ end
+
+ it "returns self if NaN" do
+ f = nan_value
+ f.send(@method).should equal(f)
+ end
+
+ it "returns 0 if positive" do
+ 1.0.send(@method).should == 0
+ end
+
+ it "returns 0 if +0.0" do
+ 0.0.send(@method).should == 0
+ end
+
+ it "returns 0 if +Infinity" do
+ infinity_value.send(@method).should == 0
+ end
+
+ it "returns Pi if negative" do
+ (-1.0).send(@method).should == Math::PI
+ end
+
+ # This was established in r23960
+ it "returns Pi if -0.0" do
+ (-0.0).send(@method).should == Math::PI
+ end
+
+ it "returns Pi if -Infinity" do
+ (-infinity_value).send(@method).should == Math::PI
+ end
+end
diff --git a/spec/ruby/core/float/shared/arithmetic_exception_in_coerce.rb b/spec/ruby/core/float/shared/arithmetic_exception_in_coerce.rb
new file mode 100644
index 0000000000..19a02572d8
--- /dev/null
+++ b/spec/ruby/core/float/shared/arithmetic_exception_in_coerce.rb
@@ -0,0 +1,33 @@
+require_relative '../fixtures/classes'
+
+describe :float_arithmetic_exception_in_coerce, shared: true do
+ ruby_version_is ""..."2.5" do
+ it "rescues exception (StandardError and subclasses) raised in other#coerce and raises TypeError" do
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(FloatSpecs::CoerceError)
+
+ # e.g. 1.0 > b
+ -> { 1.0.send(@method, b) }.should raise_error(TypeError, /MockObject can't be coerced into Float/)
+ end
+
+ it "does not rescue Exception and StandardError siblings raised in other#coerce" do
+ [Exception, NoMemoryError].each do |exception|
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(exception)
+
+ # e.g. 1.0 > b
+ -> { 1.0.send(@method, b) }.should raise_error(exception)
+ end
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "does not rescue exception raised in other#coerce" do
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(FloatSpecs::CoerceError)
+
+ # e.g. 1.0 > b
+ -> { 1.0.send(@method, b) }.should raise_error(FloatSpecs::CoerceError)
+ end
+ end
+end
diff --git a/spec/ruby/core/float/shared/comparison_exception_in_coerce.rb b/spec/ruby/core/float/shared/comparison_exception_in_coerce.rb
new file mode 100644
index 0000000000..f8ded53644
--- /dev/null
+++ b/spec/ruby/core/float/shared/comparison_exception_in_coerce.rb
@@ -0,0 +1,35 @@
+require_relative '../fixtures/classes'
+
+describe :float_comparison_exception_in_coerce, shared: true do
+ ruby_version_is ""..."2.5" do
+ it "rescues exception (StandardError and subclasses) raised in other#coerce and raises ArgumentError" do
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(FloatSpecs::CoerceError)
+
+ # e.g. 1.0 > b
+ -> {
+ -> { 1.0.send(@method, b) }.should raise_error(ArgumentError, /comparison of Float with MockObject failed/)
+ }.should complain(/Numerical comparison operators will no more rescue exceptions of #coerce/)
+ end
+
+ it "does not rescue Exception and StandardError siblings raised in other#coerce" do
+ [Exception, NoMemoryError].each do |exception|
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(exception)
+
+ # e.g. 1.0 > b
+ -> { 1.0.send(@method, b) }.should raise_error(exception)
+ end
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "does not rescue exception raised in other#coerce" do
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(FloatSpecs::CoerceError)
+
+ # e.g. 1.0 > b
+ -> { 1.0.send(@method, b) }.should raise_error(FloatSpecs::CoerceError)
+ end
+ end
+end
diff --git a/spec/ruby/core/float/shared/equal.rb b/spec/ruby/core/float/shared/equal.rb
new file mode 100644
index 0000000000..668aa069b5
--- /dev/null
+++ b/spec/ruby/core/float/shared/equal.rb
@@ -0,0 +1,17 @@
+describe :float_equal, shared: true do
+ it "returns true if self has the same value as other" do
+ 1.0.send(@method, 1).should == true
+ 2.71828.send(@method, 1.428).should == false
+ -4.2.send(@method, 4.2).should == false
+ end
+
+ it "calls 'other == self' if coercion fails" do
+ x = mock('other')
+ def x.==(other)
+ 2.0 == other
+ end
+
+ 1.0.send(@method, x).should == false
+ 2.0.send(@method, x).should == true
+ end
+end
diff --git a/spec/ruby/core/float/shared/modulo.rb b/spec/ruby/core/float/shared/modulo.rb
new file mode 100644
index 0000000000..6c423a3a28
--- /dev/null
+++ b/spec/ruby/core/float/shared/modulo.rb
@@ -0,0 +1,48 @@
+describe :float_modulo, shared: true do
+ it "returns self modulo other" do
+ 6543.21.send(@method, 137).should be_close(104.21, TOLERANCE)
+ 5667.19.send(@method, bignum_value).should be_close(5667.19, TOLERANCE)
+ 6543.21.send(@method, 137.24).should be_close(92.9299999999996, TOLERANCE)
+
+ -1.0.send(@method, 1).should == 0
+ end
+
+ it "returns self when modulus is +Infinity" do
+ 4.2.send(@method, Float::INFINITY).should == 4.2
+ end
+
+ it "returns -Infinity when modulus is -Infinity" do
+ 4.2.send(@method, -Float::INFINITY).should == -Float::INFINITY
+ end
+
+ it "returns NaN when called on NaN or Infinities" do
+ Float::NAN.send(@method, 42).should be_nan
+ Float::INFINITY.send(@method, 42).should be_nan
+ (-Float::INFINITY).send(@method, 42).should be_nan
+ end
+
+ it "returns NaN when modulus is NaN" do
+ 4.2.send(@method, Float::NAN).should be_nan
+ end
+
+ it "returns -0.0 when called on -0.0 with a non zero modulus" do
+ r = (-0.0).send(@method, 42)
+ r.should == 0
+ (1/r).should < 0
+
+ r = (-0.0).send(@method, Float::INFINITY)
+ r.should == 0
+ (1/r).should < 0
+ end
+
+ it "tries to coerce the modulus" do
+ obj = mock("modulus")
+ obj.should_receive(:coerce).with(1.25).and_return([1.25, 0.5])
+ (1.25 % obj).should == 0.25
+ end
+
+ it "raises a ZeroDivisionError if other is zero" do
+ lambda { 1.0.send(@method, 0) }.should raise_error(ZeroDivisionError)
+ lambda { 1.0.send(@method, 0.0) }.should raise_error(ZeroDivisionError)
+ end
+end
diff --git a/spec/ruby/core/float/shared/quo.rb b/spec/ruby/core/float/shared/quo.rb
new file mode 100644
index 0000000000..afc921a2c1
--- /dev/null
+++ b/spec/ruby/core/float/shared/quo.rb
@@ -0,0 +1,59 @@
+describe :float_quo, shared: true do
+ it "performs floating-point division between self and a Fixnum" do
+ 8.9.send(@method, 7).should == 1.2714285714285716
+ end
+
+ it "performs floating-point division between self and a Bignum" do
+ 8.9.send(@method, 9999999999999**9).should == 8.900000000008011e-117
+ end
+
+ it "performs floating-point division between self and a Float" do
+ 2827.22.send(@method, 872.111111).should == 3.2418116961704433
+ end
+
+ it "returns NaN when the argument is NaN" do
+ -1819.999999.send(@method, nan_value).nan?.should be_true
+ 11109.1981271.send(@method, nan_value).nan?.should be_true
+ end
+
+ it "returns Infinity when the argument is 0.0" do
+ 2827.22.send(@method, 0.0).infinite?.should == 1
+ end
+
+ it "returns -Infinity when the argument is 0.0 and self is negative" do
+ -48229.282.send(@method, 0.0).infinite?.should == -1
+ end
+
+ it "returns Infinity when the argument is 0" do
+ 2827.22.send(@method, 0).infinite?.should == 1
+ end
+
+ it "returns -Infinity when the argument is 0 and self is negative" do
+ -48229.282.send(@method, 0).infinite?.should == -1
+ end
+
+ it "returns 0.0 when the argument is Infinity" do
+ 47292.2821.send(@method, infinity_value).should == 0.0
+ end
+
+ it "returns -0.0 when the argument is -Infinity" do
+ 1.9999918.send(@method, -infinity_value).should == -0.0
+ end
+
+ it "performs floating-point division between self and a Rational" do
+ 74620.09.send(@method, Rational(2,3)).should == 111930.135
+ end
+
+ it "performs floating-point division between self and a Complex" do
+ 74620.09.send(@method, Complex(8,2)).should == Complex(
+ 8778.834117647059, -2194.7085294117646)
+ end
+
+ it "raises a TypeError when argument isn't numeric" do
+ lambda { 27292.2.send(@method, mock('non-numeric')) }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError when passed multiple arguments" do
+ lambda { 272.221.send(@method, 6,0.2) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/float/shared/to_i.rb b/spec/ruby/core/float/shared/to_i.rb
new file mode 100644
index 0000000000..960295f095
--- /dev/null
+++ b/spec/ruby/core/float/shared/to_i.rb
@@ -0,0 +1,10 @@
+describe :float_to_i, shared: true do
+ it "returns self truncated to an Integer" do
+ 899.2.send(@method).should eql(899)
+ -1.122256e-45.send(@method).should eql(0)
+ 5_213_451.9201.send(@method).should eql(5213451)
+ 1.233450999123389e+12.send(@method).should eql(1233450999123)
+ -9223372036854775808.1.send(@method).should eql(-9223372036854775808)
+ 9223372036854775808.1.send(@method).should eql(9223372036854775808)
+ end
+end
diff --git a/spec/ruby/core/float/to_f_spec.rb b/spec/ruby/core/float/to_f_spec.rb
new file mode 100644
index 0000000000..6677556cd9
--- /dev/null
+++ b/spec/ruby/core/float/to_f_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Float#to_f" do
+ it "returns self" do
+ -500.3.to_f.should == -500.3
+ 267.51.to_f.should == 267.51
+ 1.1412.to_f.should == 1.1412
+ end
+end
diff --git a/spec/ruby/core/float/to_i_spec.rb b/spec/ruby/core/float/to_i_spec.rb
new file mode 100644
index 0000000000..91d84c5fa3
--- /dev/null
+++ b/spec/ruby/core/float/to_i_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_i'
+
+describe "Float#to_i" do
+ it_behaves_like :float_to_i, :to_i
+end
diff --git a/spec/ruby/core/float/to_int_spec.rb b/spec/ruby/core/float/to_int_spec.rb
new file mode 100644
index 0000000000..084a58b431
--- /dev/null
+++ b/spec/ruby/core/float/to_int_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_i'
+
+describe "Float#to_int" do
+ it_behaves_like :float_to_i, :to_int
+end
diff --git a/spec/ruby/core/float/to_r_spec.rb b/spec/ruby/core/float/to_r_spec.rb
new file mode 100644
index 0000000000..907ff08f27
--- /dev/null
+++ b/spec/ruby/core/float/to_r_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Float#to_r" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/float/to_s_spec.rb b/spec/ruby/core/float/to_s_spec.rb
new file mode 100644
index 0000000000..58a58549ed
--- /dev/null
+++ b/spec/ruby/core/float/to_s_spec.rb
@@ -0,0 +1,120 @@
+require_relative '../../spec_helper'
+
+describe "Float#to_s" do
+ it "returns 'NaN' for NaN" do
+ nan_value().to_s.should == 'NaN'
+ end
+
+ it "returns 'Infinity' for positive infinity" do
+ infinity_value().to_s.should == 'Infinity'
+ end
+
+ it "returns '-Infinity' for negative infinity" do
+ (-infinity_value()).to_s.should == '-Infinity'
+ end
+
+ it "returns '0.0' for 0.0" do
+ 0.0.to_s.should == "0.0"
+ end
+
+ platform_is_not :openbsd do
+ it "emits '-' for -0.0" do
+ -0.0.to_s.should == "-0.0"
+ end
+ end
+
+ it "emits a '-' for negative values" do
+ -3.14.to_s.should == "-3.14"
+ end
+
+ it "emits a trailing '.0' for a whole number" do
+ 50.0.to_s.should == "50.0"
+ end
+
+ it "emits a trailing '.0' for the mantissa in e format" do
+ 1.0e20.to_s.should == "1.0e+20"
+ end
+
+ it "uses non-e format for a positive value with fractional part having 5 significant figures" do
+ 0.0001.to_s.should == "0.0001"
+ end
+
+ it "uses non-e format for a negative value with fractional part having 5 significant figures" do
+ -0.0001.to_s.should == "-0.0001"
+ end
+
+ it "uses e format for a positive value with fractional part having 6 significant figures" do
+ 0.00001.to_s.should == "1.0e-05"
+ end
+
+ it "uses e format for a negative value with fractional part having 6 significant figures" do
+ -0.00001.to_s.should == "-1.0e-05"
+ end
+
+ it "uses non-e format for a positive value with whole part having 15 significant figures" do
+ 10000000000000.0.to_s.should == "10000000000000.0"
+ end
+
+ it "uses non-e format for a negative value with whole part having 15 significant figures" do
+ -10000000000000.0.to_s.should == "-10000000000000.0"
+ end
+
+ it "uses non-e format for a positive value with whole part having 16 significant figures" do
+ 100000000000000.0.to_s.should == "100000000000000.0"
+ end
+
+ it "uses non-e format for a negative value with whole part having 16 significant figures" do
+ -100000000000000.0.to_s.should == "-100000000000000.0"
+ end
+
+ it "uses e format for a positive value with whole part having 18 significant figures" do
+ 10000000000000000.0.to_s.should == "1.0e+16"
+ end
+
+ it "uses e format for a negative value with whole part having 18 significant figures" do
+ -10000000000000000.0.to_s.should == "-1.0e+16"
+ end
+
+ it "uses non-e format for a positive value with whole part having 17 significant figures" do
+ 1000000000000000.0.to_s.should == "1.0e+15"
+ end
+
+ it "uses non-e format for a negative value with whole part having 17 significant figures" do
+ -1000000000000000.0.to_s.should == "-1.0e+15"
+ end
+
+ # #3273
+ it "outputs the minimal, unique form necessary to recreate the value" do
+ value = 0.21611564636388508
+ string = "0.21611564636388508"
+
+ value.to_s.should == string
+ string.to_f.should == value
+ end
+
+ it "outputs the minimal, unique form to represent the value" do
+ 0.56.to_s.should == "0.56"
+ end
+end
+
+with_feature :encoding do
+ describe "Float#to_s" do
+ before :each do
+ @internal = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_internal = @internal
+ end
+
+ it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do
+ Encoding.default_internal = nil
+ 1.23.to_s.encoding.should equal(Encoding::US_ASCII)
+ end
+
+ it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do
+ Encoding.default_internal = Encoding::IBM437
+ 5.47.to_s.encoding.should equal(Encoding::US_ASCII)
+ end
+ end
+end
diff --git a/spec/ruby/core/float/truncate_spec.rb b/spec/ruby/core/float/truncate_spec.rb
new file mode 100644
index 0000000000..5c219da960
--- /dev/null
+++ b/spec/ruby/core/float/truncate_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_i'
+
+describe "Float#truncate" do
+ it_behaves_like :float_to_i, :truncate
+
+ ruby_version_is "2.4" do
+ it "returns self truncated to an optionally given precision" do
+ 2.1679.truncate(0).should eql(2)
+ 7.1.truncate(1).should eql(7.1)
+ 214.94.truncate(-1).should eql(210)
+ -1.234.truncate(2).should eql(-1.23)
+ 5.123812.truncate(4).should eql(5.1238)
+ end
+ end
+end
diff --git a/spec/ruby/core/float/uminus_spec.rb b/spec/ruby/core/float/uminus_spec.rb
new file mode 100644
index 0000000000..e676a26ada
--- /dev/null
+++ b/spec/ruby/core/float/uminus_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+
+describe "Float#-@" do
+ it "negates self" do
+ (2.221.send(:-@)).should be_close(-2.221, TOLERANCE)
+ -2.01.should be_close(-2.01,TOLERANCE)
+ -2_455_999_221.5512.should be_close(-2455999221.5512, TOLERANCE)
+ (--5.5).should be_close(5.5, TOLERANCE)
+ -8.551.send(:-@).should be_close(8.551, TOLERANCE)
+ end
+
+ it "negates self at Float boundaries" do
+ Float::MAX.send(:-@).should be_close(0.0 - Float::MAX, TOLERANCE)
+ Float::MIN.send(:-@).should be_close(0.0 - Float::MIN, TOLERANCE)
+ end
+
+ it "returns negative infinity for positive infinity" do
+ infinity_value.send(:-@).infinite?.should == -1
+ end
+
+ it "returns positive infinity for negative infinity" do
+ (-infinity_value).send(:-@).infinite?.should == 1
+ end
+
+ it "returns NaN for NaN" do
+ nan_value.send(:-@).nan?.should == true
+ end
+end
diff --git a/spec/ruby/core/float/uplus_spec.rb b/spec/ruby/core/float/uplus_spec.rb
new file mode 100644
index 0000000000..936123558c
--- /dev/null
+++ b/spec/ruby/core/float/uplus_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Float#+@" do
+ it "returns the same value with same sign (twos complement)" do
+ 34.56.send(:+@).should == 34.56
+ -34.56.send(:+@).should == -34.56
+ 0.0.send(:+@).should eql(0.0)
+ end
+end
diff --git a/spec/ruby/core/float/zero_spec.rb b/spec/ruby/core/float/zero_spec.rb
new file mode 100644
index 0000000000..e70edc422a
--- /dev/null
+++ b/spec/ruby/core/float/zero_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Float#zero?" do
+ it "returns true if self is 0.0" do
+ 0.0.zero?.should == true
+ 1.0.zero?.should == false
+ -1.0.zero?.should == false
+ end
+end
diff --git a/spec/ruby/core/gc/count_spec.rb b/spec/ruby/core/gc/count_spec.rb
new file mode 100644
index 0000000000..af2fbbe713
--- /dev/null
+++ b/spec/ruby/core/gc/count_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "GC.count" do
+ it "returns an integer" do
+ GC.count.should be_kind_of(Integer)
+ end
+
+ it "increases as collections are run" do
+ count_before = GC.count
+ i = 0
+ while GC.count <= count_before and i < 10
+ GC.start
+ i += 1
+ end
+ GC.count.should > count_before
+ end
+end
diff --git a/spec/ruby/core/gc/disable_spec.rb b/spec/ruby/core/gc/disable_spec.rb
new file mode 100644
index 0000000000..b0221d8520
--- /dev/null
+++ b/spec/ruby/core/gc/disable_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+
+describe "GC.disable" do
+ after :each do
+ GC.enable
+ end
+
+ it "returns true iff the garbage collection was previously disabled" do
+ GC.enable
+ GC.disable.should == false
+ GC.disable.should == true
+ GC.disable.should == true
+ GC.enable
+ GC.disable.should == false
+ GC.disable.should == true
+ end
+
+end
diff --git a/spec/ruby/core/gc/enable_spec.rb b/spec/ruby/core/gc/enable_spec.rb
new file mode 100644
index 0000000000..eb8d572f46
--- /dev/null
+++ b/spec/ruby/core/gc/enable_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+describe "GC.enable" do
+
+ it "returns true iff the garbage collection was already disabled" do
+ GC.enable
+ GC.enable.should == false
+ GC.disable
+ GC.enable.should == true
+ GC.enable.should == false
+ end
+
+end
diff --git a/spec/ruby/core/gc/garbage_collect_spec.rb b/spec/ruby/core/gc/garbage_collect_spec.rb
new file mode 100644
index 0000000000..f67f0486c8
--- /dev/null
+++ b/spec/ruby/core/gc/garbage_collect_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "GC#garbage_collect" do
+
+ before :each do
+ @obj = Object.new
+ @obj.extend(GC)
+ end
+
+ it "always returns nil" do
+ @obj.garbage_collect.should == nil
+ @obj.garbage_collect.should == nil
+ end
+
+end
diff --git a/spec/ruby/core/gc/profiler/clear_spec.rb b/spec/ruby/core/gc/profiler/clear_spec.rb
new file mode 100644
index 0000000000..95c3d4ed65
--- /dev/null
+++ b/spec/ruby/core/gc/profiler/clear_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "GC::Profiler.clear" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/gc/profiler/disable_spec.rb b/spec/ruby/core/gc/profiler/disable_spec.rb
new file mode 100644
index 0000000000..321a207deb
--- /dev/null
+++ b/spec/ruby/core/gc/profiler/disable_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../../spec_helper'
+
+describe "GC::Profiler.disable" do
+ before do
+ @status = GC::Profiler.enabled?
+ end
+
+ after do
+ @status ? GC::Profiler.enable : GC::Profiler.disable
+ end
+
+ it "disables the profiler" do
+ GC::Profiler.disable
+ GC::Profiler.enabled?.should == false
+ end
+end
diff --git a/spec/ruby/core/gc/profiler/enable_spec.rb b/spec/ruby/core/gc/profiler/enable_spec.rb
new file mode 100644
index 0000000000..1594511675
--- /dev/null
+++ b/spec/ruby/core/gc/profiler/enable_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+
+describe "GC::Profiler.enable" do
+
+ before do
+ @status = GC::Profiler.enabled?
+ end
+
+ after do
+ @status ? GC::Profiler.enable : GC::Profiler.disable
+ end
+
+ it "enables the profiler" do
+ GC::Profiler.enable
+ GC::Profiler.enabled?.should == true
+ end
+end
diff --git a/spec/ruby/core/gc/profiler/enabled_spec.rb b/spec/ruby/core/gc/profiler/enabled_spec.rb
new file mode 100644
index 0000000000..23677be555
--- /dev/null
+++ b/spec/ruby/core/gc/profiler/enabled_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+
+describe "GC::Profiler.enabled?" do
+ before do
+ @status = GC::Profiler.enabled?
+ end
+
+ after do
+ @status ? GC::Profiler.enable : GC::Profiler.disable
+ end
+
+ it "reports as enabled when enabled" do
+ GC::Profiler.enable
+ GC::Profiler.enabled?.should be_true
+ end
+
+ it "reports as disabled when disabled" do
+ GC::Profiler.disable
+ GC::Profiler.enabled?.should be_false
+ end
+end
diff --git a/spec/ruby/core/gc/profiler/report_spec.rb b/spec/ruby/core/gc/profiler/report_spec.rb
new file mode 100644
index 0000000000..732b1d0f51
--- /dev/null
+++ b/spec/ruby/core/gc/profiler/report_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "GC::Profiler.report" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/gc/profiler/result_spec.rb b/spec/ruby/core/gc/profiler/result_spec.rb
new file mode 100644
index 0000000000..2ab46a8371
--- /dev/null
+++ b/spec/ruby/core/gc/profiler/result_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+
+describe "GC::Profiler.result" do
+ it "returns a string" do
+ GC::Profiler.result.should be_kind_of(String)
+ end
+end
diff --git a/spec/ruby/core/gc/profiler/total_time_spec.rb b/spec/ruby/core/gc/profiler/total_time_spec.rb
new file mode 100644
index 0000000000..7709f168dd
--- /dev/null
+++ b/spec/ruby/core/gc/profiler/total_time_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+
+describe "GC::Profiler.total_time" do
+ it "returns an float" do
+ GC::Profiler.total_time.should be_kind_of(Float)
+ end
+end
diff --git a/spec/ruby/core/gc/start_spec.rb b/spec/ruby/core/gc/start_spec.rb
new file mode 100644
index 0000000000..fb6820db14
--- /dev/null
+++ b/spec/ruby/core/gc/start_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+
+describe "GC.start" do
+ it "always returns nil" do
+ GC.start.should == nil
+ GC.start.should == nil
+ end
+end
diff --git a/spec/ruby/core/gc/stress_spec.rb b/spec/ruby/core/gc/stress_spec.rb
new file mode 100644
index 0000000000..ca62c0cb4e
--- /dev/null
+++ b/spec/ruby/core/gc/stress_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+
+describe "GC.stress" do
+ after :each do
+ # make sure that we never leave these tests in stress enabled GC!
+ GC.stress = false
+ end
+
+ it "returns current status of GC stress mode" do
+ GC.stress.should be_false
+ GC.stress = true
+ GC.stress.should be_true
+ GC.stress = false
+ GC.stress.should be_false
+ end
+end
+
+describe "GC.stress=" do
+ after :each do
+ GC.stress = false
+ end
+
+ it "sets the stress mode" do
+ GC.stress = true
+ GC.stress.should be_true
+ end
+end
diff --git a/spec/ruby/core/hash/allocate_spec.rb b/spec/ruby/core/hash/allocate_spec.rb
new file mode 100644
index 0000000000..93420de866
--- /dev/null
+++ b/spec/ruby/core/hash/allocate_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "Hash.allocate" do
+ it "returns an instance of Hash" do
+ hsh = Hash.allocate
+ hsh.should be_an_instance_of(Hash)
+ end
+
+ it "returns a fully-formed instance of Hash" do
+ hsh = Hash.allocate
+ hsh.size.should == 0
+ hsh[:a] = 1
+ hsh.should == { a: 1 }
+ end
+end
diff --git a/spec/ruby/core/hash/any_spec.rb b/spec/ruby/core/hash/any_spec.rb
new file mode 100644
index 0000000000..bd33e8cd8f
--- /dev/null
+++ b/spec/ruby/core/hash/any_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+
+describe "Hash#any?" do
+ describe 'with no block given' do
+ it "checks if there are any members of a Hash" do
+ empty_hash = {}
+ empty_hash.any?.should == false
+
+ hash_with_members = { 'key' => 'value' }
+ hash_with_members.any?.should == true
+ end
+ end
+
+ describe 'with a block given' do
+ it 'is false if the hash is empty' do
+ empty_hash = {}
+ empty_hash.any? {|k,v| 1 == 1 }.should == false
+ end
+
+ it 'is true if the block returns true for any member of the hash' do
+ hash_with_members = { 'a' => false, 'b' => false, 'c' => true, 'd' => false }
+ hash_with_members.any? {|k,v| v == true}.should == true
+ end
+
+ it 'is false if the block returns false for all members of the hash' do
+ hash_with_members = { 'a' => false, 'b' => false, 'c' => true, 'd' => false }
+ hash_with_members.any? {|k,v| v == 42}.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/hash/assoc_spec.rb b/spec/ruby/core/hash/assoc_spec.rb
new file mode 100644
index 0000000000..64442918d1
--- /dev/null
+++ b/spec/ruby/core/hash/assoc_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+
+describe "Hash#assoc" do
+ before :each do
+ @h = {apple: :green, orange: :orange, grape: :green, banana: :yellow}
+ end
+
+ it "returns an Array if the argument is == to a key of the Hash" do
+ @h.assoc(:apple).should be_an_instance_of(Array)
+ end
+
+ it "returns a 2-element Array if the argument is == to a key of the Hash" do
+ @h.assoc(:grape).size.should == 2
+ end
+
+ it "sets the first element of the Array to the located key" do
+ @h.assoc(:banana).first.should == :banana
+ end
+
+ it "sets the last element of the Array to the value of the located key" do
+ @h.assoc(:banana).last.should == :yellow
+ end
+
+ it "only returns the first matching key-value pair for identity hashes" do
+ # Avoid literal String keys in Hash#[]= due to https://bugs.ruby-lang.org/issues/12855
+ h = {}.compare_by_identity
+ k1 = 'pear'
+ h[k1] = :red
+ k2 = 'pear'
+ h[k2] = :green
+ h.size.should == 2
+ h.keys.grep(/pear/).size.should == 2
+ h.assoc('pear').should == ['pear', :red]
+ end
+
+ it "uses #== to compare the argument to the keys" do
+ @h[1.0] = :value
+ 1.should == 1.0
+ @h.assoc(1).should == [1.0, :value]
+ end
+
+ it "returns nil if the argument is not a key of the Hash" do
+ @h.assoc(:green).should be_nil
+ end
+
+ it "returns nil if the argument is not a key of the Hash even when there is a default" do
+ Hash.new(42).merge!( foo: :bar ).assoc(42).should be_nil
+ Hash.new{|h, k| h[k] = 42}.merge!( foo: :bar ).assoc(42).should be_nil
+ end
+end
diff --git a/spec/ruby/core/hash/clear_spec.rb b/spec/ruby/core/hash/clear_spec.rb
new file mode 100644
index 0000000000..706fe57e1c
--- /dev/null
+++ b/spec/ruby/core/hash/clear_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#clear" do
+ it "removes all key, value pairs" do
+ h = { 1 => 2, 3 => 4 }
+ h.clear.should equal(h)
+ h.should == {}
+ end
+
+ it "does not remove default values" do
+ h = Hash.new(5)
+ h.clear
+ h.default.should == 5
+
+ h = { "a" => 100, "b" => 200 }
+ h.default = "Go fish"
+ h.clear
+ h["z"].should == "Go fish"
+ end
+
+ it "does not remove default procs" do
+ h = Hash.new { 5 }
+ h.clear
+ h.default_proc.should_not == nil
+ end
+
+ it "raises a #{frozen_error_class} if called on a frozen instance" do
+ lambda { HashSpecs.frozen_hash.clear }.should raise_error(frozen_error_class)
+ lambda { HashSpecs.empty_frozen_hash.clear }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/hash/clone_spec.rb b/spec/ruby/core/hash/clone_spec.rb
new file mode 100644
index 0000000000..6c96fc0c67
--- /dev/null
+++ b/spec/ruby/core/hash/clone_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+
+describe "Hash#clone" do
+ it "copies instance variable but not the objects they refer to" do
+ hash = { 'key' => 'value' }
+
+ clone = hash.clone
+
+ clone.should == hash
+ clone.should_not equal hash
+ end
+end
diff --git a/spec/ruby/core/hash/compact_spec.rb b/spec/ruby/core/hash/compact_spec.rb
new file mode 100644
index 0000000000..b75621b4d4
--- /dev/null
+++ b/spec/ruby/core/hash/compact_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is "2.4" do
+ describe "Hash#compact" do
+ before :each do
+ @hash = { truthy: true, false: false, nil: nil, nil => true }
+ @initial_pairs = @hash.dup
+ @compact = { truthy: true, false: false, nil => true }
+ end
+
+ it "returns new object that rejects pair has nil value" do
+ ret = @hash.compact
+ ret.should_not equal(@hash)
+ ret.should == @compact
+ end
+
+ it "keeps own pairs" do
+ @hash.compact
+ @hash.should == @initial_pairs
+ end
+ end
+
+ describe "Hash#compact!" do
+ before :each do
+ @hash = { truthy: true, false: false, nil: nil, nil => true }
+ @initial_pairs = @hash.dup
+ @compact = { truthy: true, false: false, nil => true }
+ end
+
+ it "returns self" do
+ @hash.compact!.should equal(@hash)
+ end
+
+ it "rejects own pair has nil value" do
+ @hash.compact!
+ @hash.should == @compact
+ end
+
+ context "when each pair does not have nil value" do
+ before :each do
+ @hash.compact!
+ end
+
+ it "returns nil" do
+ @hash.compact!.should be_nil
+ end
+ end
+
+ describe "on frozen instance" do
+ before :each do
+ @hash.freeze
+ end
+
+ it "keeps pairs and raises a #{frozen_error_class}" do
+ ->{ @hash.compact! }.should raise_error(frozen_error_class)
+ @hash.should == @initial_pairs
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/hash/compare_by_identity_spec.rb b/spec/ruby/core/hash/compare_by_identity_spec.rb
new file mode 100644
index 0000000000..e463c311be
--- /dev/null
+++ b/spec/ruby/core/hash/compare_by_identity_spec.rb
@@ -0,0 +1,140 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#compare_by_identity" do
+ before :each do
+ @h = {}
+ @idh = {}.compare_by_identity
+ end
+
+ it "causes future comparisons on the receiver to be made by identity" do
+ @h[[1]] = :a
+ @h[[1]].should == :a
+ @h.compare_by_identity
+ @h[[1].dup].should be_nil
+ end
+
+ it "rehashes internally so that old keys can be looked up" do
+ h = {}
+ (1..10).each { |k| h[k] = k }
+ o = Object.new
+ def o.hash; 123; end
+ h[o] = 1
+ h.compare_by_identity
+ h[o].should == 1
+ end
+
+ it "returns self" do
+ h = {}
+ h[:foo] = :bar
+ h.compare_by_identity.should equal h
+ end
+
+ it "has no effect on an already compare_by_identity hash" do
+ @idh[:foo] = :bar
+ @idh.compare_by_identity.should equal @idh
+ @idh.compare_by_identity?.should == true
+ @idh[:foo].should == :bar
+ end
+
+ it "uses the semantics of BasicObject#equal? to determine key identity" do
+ [1].should_not equal([1])
+ @idh[[1]] = :c
+ @idh[[1]] = :d
+ :bar.should equal(:bar)
+ @idh[:bar] = :e
+ @idh[:bar] = :f
+ @idh.values.should == [:c, :d, :f]
+ end
+
+ it "uses #equal? semantics, but doesn't actually call #equal? to determine identity" do
+ obj = mock('equal')
+ obj.should_not_receive(:equal?)
+ @idh[:foo] = :glark
+ @idh[obj] = :a
+ @idh[obj].should == :a
+ end
+
+ it "does not call #hash on keys" do
+ key = HashSpecs::ByIdentityKey.new
+ @idh[key] = 1
+ @idh[key].should == 1
+ end
+
+ it "regards #dup'd objects as having different identities" do
+ key = ['foo']
+ @idh[key.dup] = :str
+ @idh[key].should be_nil
+ end
+
+ it "regards #clone'd objects as having different identities" do
+ key = ['foo']
+ @idh[key.clone] = :str
+ @idh[key].should be_nil
+ end
+
+ it "regards references to the same object as having the same identity" do
+ o = Object.new
+ @h[o] = :o
+ @h[:a] = :a
+ @h[o].should == :o
+ end
+
+ it "raises a #{frozen_error_class} on frozen hashes" do
+ @h = @h.freeze
+ lambda { @h.compare_by_identity }.should raise_error(frozen_error_class)
+ end
+
+ # Behaviour confirmed in bug #1871
+ it "persists over #dups" do
+ @idh['foo'] = :bar
+ @idh['foo'] = :glark
+ @idh.dup.should == @idh
+ @idh.dup.size.should == @idh.size
+ end
+
+ it "persists over #clones" do
+ @idh['foo'] = :bar
+ @idh['foo'] = :glark
+ @idh.clone.should == @idh
+ @idh.clone.size.should == @idh.size
+ end
+
+ it "does not copy string keys" do
+ foo = 'foo'
+ @idh[foo] = true
+ @idh[foo] = true
+ @idh.size.should == 1
+ @idh.keys.first.should equal foo
+ end
+
+ ruby_bug "#12855", ""..."2.4.1" do
+ it "gives different identity for string literals" do
+ @idh['foo'] = 1
+ @idh['foo'] = 2
+ @idh.values.should == [1, 2]
+ @idh.size.should == 2
+ end
+ end
+end
+
+describe "Hash#compare_by_identity?" do
+ it "returns false by default" do
+ h = {}
+ h.compare_by_identity?.should be_false
+ end
+
+ it "returns true once #compare_by_identity has been invoked on self" do
+ h = {}
+ h.compare_by_identity
+ h.compare_by_identity?.should be_true
+ end
+
+ it "returns true when called multiple times on the same ident hash" do
+ h = {}
+ h.compare_by_identity
+ h.compare_by_identity?.should be_true
+ h.compare_by_identity?.should be_true
+ h.compare_by_identity?.should be_true
+ end
+end
diff --git a/spec/ruby/core/hash/constructor_spec.rb b/spec/ruby/core/hash/constructor_spec.rb
new file mode 100644
index 0000000000..55681c2a7c
--- /dev/null
+++ b/spec/ruby/core/hash/constructor_spec.rb
@@ -0,0 +1,110 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash.[]" do
+ describe "passed zero arguments" do
+ it "returns an empty hash" do
+ Hash[].should == {}
+ end
+ end
+
+ it "creates a Hash; values can be provided as the argument list" do
+ Hash[:a, 1, :b, 2].should == { a: 1, b: 2 }
+ Hash[].should == {}
+ Hash[:a, 1, :b, { c: 2 }].should == { a: 1, b: { c: 2 } }
+ end
+
+ it "creates a Hash; values can be provided as one single hash" do
+ Hash[a: 1, b: 2].should == { a: 1, b: 2 }
+ Hash[{1 => 2, 3 => 4}].should == {1 => 2, 3 => 4}
+ Hash[{}].should == {}
+ end
+
+ describe "passed an array" do
+ it "treats elements that are 2 element arrays as key and value" do
+ Hash[[[:a, :b], [:c, :d]]].should == { a: :b, c: :d }
+ end
+
+ it "treats elements that are 1 element arrays as keys with value nil" do
+ Hash[[[:a]]].should == { a: nil }
+ end
+ end
+
+ # #1000 #1385
+ it "creates a Hash; values can be provided as a list of value-pairs in an array" do
+ Hash[[[:a, 1], [:b, 2]]].should == { a: 1, b: 2 }
+ end
+
+ it "coerces a single argument which responds to #to_ary" do
+ ary = mock('to_ary')
+ ary.should_receive(:to_ary).and_return([[:a, :b]])
+
+ Hash[ary].should == { a: :b }
+ end
+
+ it "ignores elements that are not arrays" do
+ -> {
+ Hash[[:a]].should == {}
+ }.should complain(/ignoring wrong elements/)
+ -> {
+ Hash[[:nil]].should == {}
+ }.should complain(/ignoring wrong elements/)
+ end
+
+ it "raises an ArgumentError for arrays of more than 2 elements" do
+ lambda{ Hash[[[:a, :b, :c]]].should == {} }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed a list of value-invalid-pairs in an array" do
+ -> {
+ -> {
+ Hash[[[:a, 1], [:b], 42, [:d, 2], [:e, 2, 3], []]]
+ }.should complain(/ignoring wrong elements/)
+ }.should raise_error(ArgumentError)
+ end
+
+ describe "passed a single argument which responds to #to_hash" do
+ it "coerces it and returns a copy" do
+ h = { a: :b, c: :d }
+ to_hash = mock('to_hash')
+ to_hash.should_receive(:to_hash).and_return(h)
+
+ result = Hash[to_hash]
+ result.should == h
+ result.should_not equal(h)
+ end
+ end
+
+ it "raises an ArgumentError when passed an odd number of arguments" do
+ lambda { Hash[1, 2, 3] }.should raise_error(ArgumentError)
+ lambda { Hash[1, 2, { 3 => 4 }] }.should raise_error(ArgumentError)
+ end
+
+ it "calls to_hash" do
+ obj = mock('x')
+ def obj.to_hash() { 1 => 2, 3 => 4 } end
+ Hash[obj].should == { 1 => 2, 3 => 4 }
+ end
+
+ it "returns an instance of a subclass when passed an Array" do
+ HashSpecs::MyHash[1,2,3,4].should be_an_instance_of(HashSpecs::MyHash)
+ end
+
+ it "returns instances of subclasses" do
+ HashSpecs::MyHash[].should be_an_instance_of(HashSpecs::MyHash)
+ end
+
+ it "returns an instance of the class it's called on" do
+ Hash[HashSpecs::MyHash[1, 2]].class.should == Hash
+ HashSpecs::MyHash[Hash[1, 2]].should be_an_instance_of(HashSpecs::MyHash)
+ end
+
+ it "does not call #initialize on the subclass instance" do
+ HashSpecs::MyInitializerHash[Hash[1, 2]].should be_an_instance_of(HashSpecs::MyInitializerHash)
+ end
+
+ it "removes the default_proc" do
+ hash = Hash.new { |h, k| h[k] = [] }
+ Hash[hash].default_proc.should be_nil
+ end
+end
diff --git a/spec/ruby/core/hash/default_proc_spec.rb b/spec/ruby/core/hash/default_proc_spec.rb
new file mode 100644
index 0000000000..2254609607
--- /dev/null
+++ b/spec/ruby/core/hash/default_proc_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#default_proc" do
+ it "returns the block passed to Hash.new" do
+ h = Hash.new { 'Paris' }
+ p = h.default_proc
+ p.call(1).should == 'Paris'
+ end
+
+ it "returns nil if no block was passed to proc" do
+ {}.default_proc.should == nil
+ end
+end
+
+describe "Hash#default_proc=" do
+ it "replaces the block passed to Hash.new" do
+ h = Hash.new { 'Paris' }
+ h.default_proc = Proc.new { 'Montreal' }
+ p = h.default_proc
+ p.call(1).should == 'Montreal'
+ end
+
+ it "uses :to_proc on its argument" do
+ h = Hash.new { 'Paris' }
+ obj = mock('to_proc')
+ obj.should_receive(:to_proc).and_return(Proc.new { 'Montreal' })
+ (h.default_proc = obj).should equal(obj)
+ h[:cool_city].should == 'Montreal'
+ end
+
+ it "overrides the static default" do
+ h = Hash.new(42)
+ h.default_proc = Proc.new { 6 }
+ h.default.should be_nil
+ h.default_proc.call.should == 6
+ end
+
+ it "raises an error if passed stuff not convertible to procs" do
+ lambda{{}.default_proc = 42}.should raise_error(TypeError)
+ end
+
+ it "returns the passed Proc" do
+ new_proc = Proc.new {}
+ ({}.default_proc = new_proc).should equal(new_proc)
+ end
+
+ it "clears the default proc if passed nil" do
+ h = Hash.new { 'Paris' }
+ h.default_proc = nil
+ h.default_proc.should == nil
+ h[:city].should == nil
+ end
+
+ it "returns nil if passed nil" do
+ ({}.default_proc = nil).should be_nil
+ end
+
+ it "accepts a lambda with an arity of 2" do
+ h = {}
+ lambda do
+ h.default_proc = lambda {|a,b| }
+ end.should_not raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed a lambda with an arity other than 2" do
+ h = {}
+ lambda do
+ h.default_proc = lambda {|a| }
+ end.should raise_error(TypeError)
+ lambda do
+ h.default_proc = lambda {|a,b,c| }
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a #{frozen_error_class} if self is frozen" do
+ lambda { {}.freeze.default_proc = Proc.new {} }.should raise_error(frozen_error_class)
+ lambda { {}.freeze.default_proc = nil }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/hash/default_spec.rb b/spec/ruby/core/hash/default_spec.rb
new file mode 100644
index 0000000000..afc4f9780f
--- /dev/null
+++ b/spec/ruby/core/hash/default_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#default" do
+ it "returns the default value" do
+ h = Hash.new(5)
+ h.default.should == 5
+ h.default(4).should == 5
+ {}.default.should == nil
+ {}.default(4).should == nil
+ end
+
+ it "uses the default proc to compute a default value, passing given key" do
+ h = Hash.new { |*args| args }
+ h.default(nil).should == [h, nil]
+ h.default(5).should == [h, 5]
+ end
+
+ it "calls default proc with nil arg if passed a default proc but no arg" do
+ h = Hash.new { |*args| args }
+ h.default.should == nil
+ end
+end
+
+describe "Hash#default=" do
+ it "sets the default value" do
+ h = {}
+ h.default = 99
+ h.default.should == 99
+ end
+
+ it "unsets the default proc" do
+ [99, nil, lambda { 6 }].each do |default|
+ h = Hash.new { 5 }
+ h.default_proc.should_not == nil
+ h.default = default
+ h.default.should == default
+ h.default_proc.should == nil
+ end
+ end
+
+ it "raises a #{frozen_error_class} if called on a frozen instance" do
+ lambda { HashSpecs.frozen_hash.default = nil }.should raise_error(frozen_error_class)
+ lambda { HashSpecs.empty_frozen_hash.default = nil }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/hash/delete_if_spec.rb b/spec/ruby/core/hash/delete_if_spec.rb
new file mode 100644
index 0000000000..58fba1ff80
--- /dev/null
+++ b/spec/ruby/core/hash/delete_if_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/iteration'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Hash#delete_if" do
+ it "yields two arguments: key and value" do
+ all_args = []
+ { 1 => 2, 3 => 4 }.delete_if { |*args| all_args << args }
+ all_args.sort.should == [[1, 2], [3, 4]]
+ end
+
+ it "removes every entry for which block is true and returns self" do
+ h = { a: 1, b: 2, c: 3, d: 4 }
+ h.delete_if { |k,v| v % 2 == 1 }.should equal(h)
+ h.should == { b: 2, d: 4 }
+ end
+
+ it "removes all entries if the block is true" do
+ h = { a: 1, b: 2, c: 3 }
+ h.delete_if { |k,v| true }.should equal(h)
+ h.should == {}
+ end
+
+ it "processes entries with the same order as each()" do
+ h = { a: 1, b: 2, c: 3, d: 4 }
+
+ each_pairs = []
+ delete_pairs = []
+
+ h.each_pair { |k,v| each_pairs << [k, v] }
+ h.delete_if { |k,v| delete_pairs << [k,v] }
+
+ each_pairs.should == delete_pairs
+ end
+
+ it "raises a #{frozen_error_class} if called on a frozen instance" do
+ lambda { HashSpecs.frozen_hash.delete_if { false } }.should raise_error(frozen_error_class)
+ lambda { HashSpecs.empty_frozen_hash.delete_if { true } }.should raise_error(frozen_error_class)
+ end
+
+ it_behaves_like :hash_iteration_no_block, :delete_if
+ it_behaves_like :enumeratorized_with_origin_size, :delete_if, { 1 => 2, 3 => 4, 5 => 6 }
+end
diff --git a/spec/ruby/core/hash/delete_spec.rb b/spec/ruby/core/hash/delete_spec.rb
new file mode 100644
index 0000000000..6f0079dafa
--- /dev/null
+++ b/spec/ruby/core/hash/delete_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#delete" do
+ it "removes the entry and returns the deleted value" do
+ h = { a: 5, b: 2 }
+ h.delete(:b).should == 2
+ h.should == { a: 5 }
+ end
+
+ it "calls supplied block if the key is not found" do
+ { a: 1, b: 10, c: 100 }.delete(:d) { 5 }.should == 5
+ Hash.new(:default).delete(:d) { 5 }.should == 5
+ Hash.new { :default }.delete(:d) { 5 }.should == 5
+ end
+
+ it "returns nil if the key is not found when no block is given" do
+ { a: 1, b: 10, c: 100 }.delete(:d).should == nil
+ Hash.new(:default).delete(:d).should == nil
+ Hash.new { :default }.delete(:d).should == nil
+ end
+
+ # MRI explicitly implements this behavior
+ it "allows removing a key while iterating" do
+ h = { a: 1, b: 2 }
+ visited = []
+ h.each_pair { |k,v|
+ visited << k
+ h.delete(k)
+ }
+ visited.should == [:a, :b]
+ h.should == {}
+ end
+
+ it "accepts keys with private #hash method" do
+ key = HashSpecs::KeyWithPrivateHash.new
+ { key => 5 }.delete(key).should == 5
+ end
+
+ it "raises a #{frozen_error_class} if called on a frozen instance" do
+ lambda { HashSpecs.frozen_hash.delete("foo") }.should raise_error(frozen_error_class)
+ lambda { HashSpecs.empty_frozen_hash.delete("foo") }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/hash/dig_spec.rb b/spec/ruby/core/hash/dig_spec.rb
new file mode 100644
index 0000000000..dcba049ac4
--- /dev/null
+++ b/spec/ruby/core/hash/dig_spec.rb
@@ -0,0 +1,66 @@
+require_relative '../../spec_helper'
+
+describe "Hash#dig" do
+
+ it "returns #[] with one arg" do
+ h = { 0 => false, a: 1 }
+ h.dig(:a).should == 1
+ h.dig(0).should be_false
+ h.dig(1).should be_nil
+ end
+
+ it "returns the nested value specified by the sequence of keys" do
+ h = { foo: { bar: { baz: 1 } } }
+ h.dig(:foo, :bar, :baz).should == 1
+ h.dig(:foo, :bar, :nope).should be_nil
+ h.dig(:foo, :baz).should be_nil
+ h.dig(:bar, :baz, :foo).should be_nil
+ end
+
+ it "returns the nested value specified if the sequence includes an index" do
+ h = { foo: [1, 2, 3] }
+ h.dig(:foo, 2).should == 3
+ end
+
+ it "returns nil if any intermediate step is nil" do
+ h = { foo: { bar: { baz: 1 } } }
+ h.dig(:foo, :zot, :xyz).should == nil
+ end
+
+ it "raises an ArgumentError if no arguments provided" do
+ lambda { { the: 'borg' }.dig() }.should raise_error(ArgumentError)
+ end
+
+ it "handles type-mixed deep digging" do
+ h = {}
+ h[:foo] = [ { bar: [ 1 ] }, [ obj = Object.new, 'str' ] ]
+ def obj.dig(*args); [ 42 ] end
+
+ h.dig(:foo, 0, :bar).should == [ 1 ]
+ h.dig(:foo, 0, :bar, 0).should == 1
+ h.dig(:foo, 1, 1).should == 'str'
+ # MRI does not recurse values returned from `obj.dig`
+ h.dig(:foo, 1, 0, 0).should == [ 42 ]
+ h.dig(:foo, 1, 0, 0, 10).should == [ 42 ]
+ end
+
+ it "raises TypeError if an intermediate element does not respond to #dig" do
+ h = {}
+ h[:foo] = [ { bar: [ 1 ] }, [ nil, 'str' ] ]
+ lambda { h.dig(:foo, 0, :bar, 0, 0) }.should raise_error(TypeError)
+ lambda { h.dig(:foo, 1, 1, 0) }.should raise_error(TypeError)
+ end
+
+ it "calls #dig on the result of #[] with the remaining arguments" do
+ h = { foo: { bar: { baz: 42 } } }
+ h[:foo].should_receive(:dig).with(:bar, :baz).and_return(42)
+ h.dig(:foo, :bar, :baz).should == 42
+ end
+
+ it "respects Hash's default" do
+ default = {bar: 42}
+ h = Hash.new(default)
+ h.dig(:foo).should equal default
+ h.dig(:foo, :bar).should == 42
+ end
+end
diff --git a/spec/ruby/core/hash/each_key_spec.rb b/spec/ruby/core/hash/each_key_spec.rb
new file mode 100644
index 0000000000..c84dd696d5
--- /dev/null
+++ b/spec/ruby/core/hash/each_key_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/iteration'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Hash#each_key" do
+ it "calls block once for each key, passing key" do
+ r = {}
+ h = { 1 => -1, 2 => -2, 3 => -3, 4 => -4 }
+ h.each_key { |k| r[k] = k }.should equal(h)
+ r.should == { 1 => 1, 2 => 2, 3 => 3, 4 => 4 }
+ end
+
+ it "processes keys in the same order as keys()" do
+ keys = []
+ h = { 1 => -1, 2 => -2, 3 => -3, 4 => -4 }
+ h.each_key { |k| keys << k }
+ keys.should == h.keys
+ end
+
+ it_behaves_like :hash_iteration_no_block, :each_key
+ it_behaves_like :enumeratorized_with_origin_size, :each_key, { 1 => 2, 3 => 4, 5 => 6 }
+end
diff --git a/spec/ruby/core/hash/each_pair_spec.rb b/spec/ruby/core/hash/each_pair_spec.rb
new file mode 100644
index 0000000000..eb6656681d
--- /dev/null
+++ b/spec/ruby/core/hash/each_pair_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/iteration'
+require_relative 'shared/each'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Hash#each_pair" do
+ it_behaves_like :hash_each, :each_pair
+ it_behaves_like :hash_iteration_no_block, :each_pair
+ it_behaves_like :enumeratorized_with_origin_size, :each_pair, { 1 => 2, 3 => 4, 5 => 6 }
+end
diff --git a/spec/ruby/core/hash/each_spec.rb b/spec/ruby/core/hash/each_spec.rb
new file mode 100644
index 0000000000..f0de0bdee5
--- /dev/null
+++ b/spec/ruby/core/hash/each_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/iteration'
+require_relative 'shared/each'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Hash#each" do
+ it_behaves_like :hash_each, :each
+ it_behaves_like :hash_iteration_no_block, :each
+ it_behaves_like :enumeratorized_with_origin_size, :each, { 1 => 2, 3 => 4, 5 => 6 }
+end
diff --git a/spec/ruby/core/hash/each_value_spec.rb b/spec/ruby/core/hash/each_value_spec.rb
new file mode 100644
index 0000000000..19b076730d
--- /dev/null
+++ b/spec/ruby/core/hash/each_value_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/iteration'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Hash#each_value" do
+ it "calls block once for each key, passing value" do
+ r = []
+ h = { a: -5, b: -3, c: -2, d: -1, e: -1 }
+ h.each_value { |v| r << v }.should equal(h)
+ r.sort.should == [-5, -3, -2, -1, -1]
+ end
+
+ it "processes values in the same order as values()" do
+ values = []
+ h = { a: -5, b: -3, c: -2, d: -1, e: -1 }
+ h.each_value { |v| values << v }
+ values.should == h.values
+ end
+
+ it_behaves_like :hash_iteration_no_block, :each_value
+ it_behaves_like :enumeratorized_with_origin_size, :each_value, { 1 => 2, 3 => 4, 5 => 6 }
+end
diff --git a/spec/ruby/core/hash/element_reference_spec.rb b/spec/ruby/core/hash/element_reference_spec.rb
new file mode 100644
index 0000000000..2eb65d3789
--- /dev/null
+++ b/spec/ruby/core/hash/element_reference_spec.rb
@@ -0,0 +1,120 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#[]" do
+ it "returns the value for key" do
+ obj = mock('x')
+ h = { 1 => 2, 3 => 4, "foo" => "bar", obj => obj, [] => "baz" }
+ h[1].should == 2
+ h[3].should == 4
+ h["foo"].should == "bar"
+ h[obj].should == obj
+ h[[]].should == "baz"
+ end
+
+ it "returns nil as default default value" do
+ { 0 => 0 }[5].should == nil
+ end
+
+ it "returns the default (immediate) value for missing keys" do
+ h = Hash.new(5)
+ h[:a].should == 5
+ h[:a] = 0
+ h[:a].should == 0
+ h[:b].should == 5
+ end
+
+ it "calls subclass implementations of default" do
+ h = HashSpecs::DefaultHash.new
+ h[:nothing].should == 100
+ end
+
+ it "does not create copies of the immediate default value" do
+ str = "foo"
+ h = Hash.new(str)
+ a = h[:a]
+ b = h[:b]
+ a << "bar"
+
+ a.should equal(b)
+ a.should == "foobar"
+ b.should == "foobar"
+ end
+
+ it "returns the default (dynamic) value for missing keys" do
+ h = Hash.new { |hsh, k| k.kind_of?(Numeric) ? hsh[k] = k + 2 : hsh[k] = k }
+ h[1].should == 3
+ h['this'].should == 'this'
+ h.should == { 1 => 3, 'this' => 'this' }
+
+ i = 0
+ h = Hash.new { |hsh, key| i += 1 }
+ h[:foo].should == 1
+ h[:foo].should == 2
+ h[:bar].should == 3
+ end
+
+ it "does not return default values for keys with nil values" do
+ h = Hash.new(5)
+ h[:a] = nil
+ h[:a].should == nil
+
+ h = Hash.new { 5 }
+ h[:a] = nil
+ h[:a].should == nil
+ end
+
+ it "compares keys with eql? semantics" do
+ { 1.0 => "x" }[1].should == nil
+ { 1.0 => "x" }[1.0].should == "x"
+ { 1 => "x" }[1.0].should == nil
+ { 1 => "x" }[1].should == "x"
+ end
+
+ it "compares key via hash" do
+ x = mock('0')
+ x.should_receive(:hash).and_return(0)
+
+ h = {}
+ # 1.9 only calls #hash if the hash had at least one entry beforehand.
+ h[:foo] = :bar
+ h[x].should == nil
+ end
+
+ it "does not compare keys with different #hash values via #eql?" do
+ x = mock('x')
+ x.should_not_receive(:eql?)
+ x.stub!(:hash).and_return(0)
+
+ y = mock('y')
+ y.should_not_receive(:eql?)
+ y.stub!(:hash).and_return(1)
+
+ { y => 1 }[x].should == nil
+ end
+
+ it "compares keys with the same #hash value via #eql?" do
+ x = mock('x')
+ x.should_receive(:eql?).and_return(true)
+ x.stub!(:hash).and_return(42)
+
+ y = mock('y')
+ y.should_not_receive(:eql?)
+ y.stub!(:hash).and_return(42)
+
+ { y => 1 }[x].should == 1
+ end
+
+ it "finds a value via an identical key even when its #eql? isn't reflexive" do
+ x = mock('x')
+ x.should_receive(:hash).at_least(1).and_return(42)
+ x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI.
+
+ { x => :x }[x].should == :x
+ end
+
+ it "supports keys with private #hash method" do
+ key = HashSpecs::KeyWithPrivateHash.new
+ { key => 42 }[key].should == 42
+ end
+end
diff --git a/spec/ruby/core/hash/element_set_spec.rb b/spec/ruby/core/hash/element_set_spec.rb
new file mode 100644
index 0000000000..67c5a04d73
--- /dev/null
+++ b/spec/ruby/core/hash/element_set_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/store'
+
+describe "Hash#[]=" do
+ it_behaves_like :hash_store, :[]=
+end
diff --git a/spec/ruby/core/hash/empty_spec.rb b/spec/ruby/core/hash/empty_spec.rb
new file mode 100644
index 0000000000..e9be44bab0
--- /dev/null
+++ b/spec/ruby/core/hash/empty_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#empty?" do
+ it "returns true if the hash has no entries" do
+ {}.empty?.should == true
+ { 1 => 1 }.empty?.should == false
+ end
+
+ it "returns true if the hash has no entries and has a default value" do
+ Hash.new(5).empty?.should == true
+ Hash.new { 5 }.empty?.should == true
+ Hash.new { |hsh, k| hsh[k] = k }.empty?.should == true
+ end
+end
diff --git a/spec/ruby/core/hash/eql_spec.rb b/spec/ruby/core/hash/eql_spec.rb
new file mode 100644
index 0000000000..9013e12ffd
--- /dev/null
+++ b/spec/ruby/core/hash/eql_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/eql'
+
+describe "Hash#eql?" do
+ it_behaves_like :hash_eql, :eql?
+ it_behaves_like :hash_eql_additional, :eql?
+ it_behaves_like :hash_eql_additional_more, :eql?
+end
diff --git a/spec/ruby/core/hash/equal_value_spec.rb b/spec/ruby/core/hash/equal_value_spec.rb
new file mode 100644
index 0000000000..ae13a42679
--- /dev/null
+++ b/spec/ruby/core/hash/equal_value_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/eql'
+
+describe "Hash#==" do
+ it_behaves_like :hash_eql, :==
+ it_behaves_like :hash_eql_additional, :==
+ it_behaves_like :hash_eql_additional_more, :==
+
+ it "compares values with == semantics" do
+ l_val = mock("left")
+ r_val = mock("right")
+
+ l_val.should_receive(:==).with(r_val).and_return(true)
+
+ ({ 1 => l_val } == { 1 => r_val }).should be_true
+ end
+end
diff --git a/spec/ruby/core/hash/fetch_spec.rb b/spec/ruby/core/hash/fetch_spec.rb
new file mode 100644
index 0000000000..2fee5d0164
--- /dev/null
+++ b/spec/ruby/core/hash/fetch_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/hash/key_error'
+
+describe "Hash#fetch" do
+ context "when the key is not found" do
+ it_behaves_like :key_error, ->(obj, key) { obj.fetch(key) }, Hash.new(a: 5)
+ it_behaves_like :key_error, ->(obj, key) { obj.fetch(key) }, {}
+ it_behaves_like :key_error, ->(obj, key) { obj.fetch(key) }, Hash.new { 5 }
+ it_behaves_like :key_error, ->(obj, key) { obj.fetch(key) }, Hash.new(5)
+ end
+
+ it "returns the value for key" do
+ { a: 1, b: -1 }.fetch(:b).should == -1
+ end
+
+ it "returns default if key is not found when passed a default" do
+ {}.fetch(:a, nil).should == nil
+ {}.fetch(:a, 'not here!').should == "not here!"
+ { a: nil }.fetch(:a, 'not here!').should == nil
+ end
+
+ it "returns value of block if key is not found when passed a block" do
+ {}.fetch('a') { |k| k + '!' }.should == "a!"
+ end
+
+ it "gives precedence to the default block over the default argument when passed both" do
+ lambda {
+ @result = {}.fetch(9, :foo) { |i| i * i }
+ }.should complain(/block supersedes default value argument/)
+ @result.should == 81
+ end
+
+ it "raises an ArgumentError when not passed one or two arguments" do
+ lambda { {}.fetch() }.should raise_error(ArgumentError)
+ lambda { {}.fetch(1, 2, 3) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/hash/fetch_values_spec.rb b/spec/ruby/core/hash/fetch_values_spec.rb
new file mode 100644
index 0000000000..a4af153bf7
--- /dev/null
+++ b/spec/ruby/core/hash/fetch_values_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/hash/key_error'
+
+describe "Hash#fetch_values" do
+ before :each do
+ @hash = { a: 1, b: 2, c: 3 }
+ end
+
+ describe "with matched keys" do
+ it "returns the values for keys" do
+ @hash.fetch_values(:a).should == [1]
+ @hash.fetch_values(:a, :c).should == [1, 3]
+ end
+ end
+
+ describe "with unmatched keys" do
+ it_behaves_like :key_error, ->(obj, key) { obj.fetch_values(key) }, Hash.new(a: 5)
+
+ it "returns the default value from block" do
+ @hash.fetch_values(:z) { |key| "`#{key}' is not found" }.should == ["`z' is not found"]
+ @hash.fetch_values(:a, :z) { |key| "`#{key}' is not found" }.should == [1, "`z' is not found"]
+ end
+ end
+
+ describe "without keys" do
+ it "returns an empty Array" do
+ @hash.fetch_values.should == []
+ end
+ end
+end
diff --git a/spec/ruby/core/hash/filter_spec.rb b/spec/ruby/core/hash/filter_spec.rb
new file mode 100644
index 0000000000..4382c94e62
--- /dev/null
+++ b/spec/ruby/core/hash/filter_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'shared/select'
+
+ruby_version_is "2.6" do
+ describe "Hash#filter" do
+ it_behaves_like :hash_select, :filter
+ end
+
+ describe "Hash#filter!" do
+ it_behaves_like :hash_select!, :filter!
+ end
+end
diff --git a/spec/ruby/core/hash/fixtures/classes.rb b/spec/ruby/core/hash/fixtures/classes.rb
new file mode 100644
index 0000000000..ae907aaff6
--- /dev/null
+++ b/spec/ruby/core/hash/fixtures/classes.rb
@@ -0,0 +1,75 @@
+module HashSpecs
+ class MyHash < Hash; end
+
+ class MyInitializerHash < Hash
+
+ def initialize
+ raise "Constructor called"
+ end
+
+ end
+
+ class NewHash < Hash
+ def initialize(*args)
+ args.each_with_index do |val, index|
+ self[index] = val
+ end
+ end
+ end
+
+ class SubHashSettingInInitialize < Hash
+ def initialize(*args, &block)
+ self[:foo] = :bar
+ super(*args, &block)
+ end
+ end
+
+ class DefaultHash < Hash
+ def default(key)
+ 100
+ end
+ end
+
+ class ToHashHash < Hash
+ def to_hash
+ { "to_hash" => "was", "called!" => "duh." }
+ end
+ end
+
+ class KeyWithPrivateHash
+ private :hash
+ end
+
+ class ByIdentityKey
+ def hash
+ fail("#hash should not be called on compare_by_identity Hash")
+ end
+ end
+
+ class ByValueKey
+ attr_reader :n
+ def initialize(n)
+ @n = n
+ end
+
+ def hash
+ n
+ end
+
+ def eql? other
+ ByValueKey === other and @n == other.n
+ end
+ end
+
+ def self.empty_frozen_hash
+ @empty ||= {}
+ @empty.freeze
+ @empty
+ end
+
+ def self.frozen_hash
+ @hash ||= { 1 => 2, 3 => 4 }
+ @hash.freeze
+ @hash
+ end
+end
diff --git a/spec/ruby/core/hash/flatten_spec.rb b/spec/ruby/core/hash/flatten_spec.rb
new file mode 100644
index 0000000000..5a7ddd8baa
--- /dev/null
+++ b/spec/ruby/core/hash/flatten_spec.rb
@@ -0,0 +1,62 @@
+require_relative '../../spec_helper'
+
+describe "Hash#flatten" do
+
+ before :each do
+ @h = {plato: :greek,
+ witgenstein: [:austrian, :british],
+ russell: :welsh}
+ end
+
+ it "returns an Array" do
+ {}.flatten.should be_an_instance_of(Array)
+ end
+
+ it "returns an empty Array for an empty Hash" do
+ {}.flatten.should == []
+ end
+
+ it "sets each even index of the Array to a key of the Hash" do
+ a = @h.flatten
+ a[0].should == :plato
+ a[2].should == :witgenstein
+ a[4].should == :russell
+ end
+
+ it "sets each odd index of the Array to the value corresponding to the previous element" do
+ a = @h.flatten
+ a[1].should == :greek
+ a[3].should == [:austrian, :british]
+ a[5].should == :welsh
+ end
+
+ it "does not recursively flatten Array values when called without arguments" do
+ a = @h.flatten
+ a[3].should == [:austrian, :british]
+ end
+
+ it "does not recursively flatten Hash values when called without arguments" do
+ @h[:russell] = {born: :wales, influenced_by: :mill }
+ a = @h.flatten
+ a[5].should_not == {born: :wales, influenced_by: :mill }.flatten
+ end
+
+ it "recursively flattens Array values when called with an argument >= 2" do
+ a = @h.flatten(2)
+ a[3].should == :austrian
+ a[4].should == :british
+ end
+
+ it "recursively flattens Array values to the given depth" do
+ @h[:russell] = [[:born, :wales], [:influenced_by, :mill]]
+ a = @h.flatten(2)
+ a[6].should == [:born, :wales]
+ a[7].should == [:influenced_by, :mill]
+ end
+
+ it "raises a TypeError if given a non-Integer argument" do
+ lambda do
+ @h.flatten(Object.new)
+ end.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/hash/gt_spec.rb b/spec/ruby/core/hash/gt_spec.rb
new file mode 100644
index 0000000000..cd541d4d83
--- /dev/null
+++ b/spec/ruby/core/hash/gt_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'shared/comparison'
+require_relative 'shared/greater_than'
+
+describe "Hash#>" do
+ it_behaves_like :hash_comparison, :>
+ it_behaves_like :hash_greater_than, :>
+
+ it "returns false if both hashes are identical" do
+ h = { a: 1, b: 2 }
+ (h > h).should be_false
+ end
+end
+
+describe "Hash#>" do
+ before :each do
+ @hash = {a:1, b:2}
+ @bigger = {a:1, b:2, c:3}
+ @unrelated = {c:3, d:4}
+ @similar = {a:2, b:3}
+ end
+
+ it "returns false when receiver size is smaller than argument" do
+ (@hash > @bigger).should == false
+ (@unrelated > @bigger).should == false
+ end
+
+ it "returns false when receiver size is the same as argument" do
+ (@hash > @hash).should == false
+ (@hash > @unrelated).should == false
+ (@unrelated > @hash).should == false
+ end
+
+ it "returns true when argument is a subset of receiver" do
+ (@bigger > @hash).should == true
+ end
+
+ it "returns false when keys match but values don't" do
+ (@hash > @similar).should == false
+ (@similar > @hash).should == false
+ end
+end
diff --git a/spec/ruby/core/hash/gte_spec.rb b/spec/ruby/core/hash/gte_spec.rb
new file mode 100644
index 0000000000..99b89e7217
--- /dev/null
+++ b/spec/ruby/core/hash/gte_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'shared/comparison'
+require_relative 'shared/greater_than'
+
+describe "Hash#>=" do
+ it_behaves_like :hash_comparison, :>=
+ it_behaves_like :hash_greater_than, :>=
+
+ it "returns true if both hashes are identical" do
+ h = { a: 1, b: 2 }
+ (h >= h).should be_true
+ end
+end
+
+describe "Hash#>=" do
+ before :each do
+ @hash = {a:1, b:2}
+ @bigger = {a:1, b:2, c:3}
+ @unrelated = {c:3, d:4}
+ @similar = {a:2, b:3}
+ end
+
+ it "returns false when receiver size is smaller than argument" do
+ (@hash >= @bigger).should == false
+ (@unrelated >= @bigger).should == false
+ end
+
+ it "returns false when argument is not a subset or not equals to receiver" do
+ (@hash >= @unrelated).should == false
+ (@unrelated >= @hash).should == false
+ end
+
+ it "returns true when argument is a subset of receiver or equals to receiver" do
+ (@bigger >= @hash).should == true
+ (@hash >= @hash).should == true
+ end
+
+ it "returns false when keys match but values don't" do
+ (@hash >= @similar).should == false
+ (@similar >= @hash).should == false
+ end
+end
diff --git a/spec/ruby/core/hash/has_key_spec.rb b/spec/ruby/core/hash/has_key_spec.rb
new file mode 100644
index 0000000000..4af53579e5
--- /dev/null
+++ b/spec/ruby/core/hash/has_key_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/key'
+
+describe "Hash#has_key?" do
+ it_behaves_like :hash_key_p, :has_key?
+end
diff --git a/spec/ruby/core/hash/has_value_spec.rb b/spec/ruby/core/hash/has_value_spec.rb
new file mode 100644
index 0000000000..39f1627fd3
--- /dev/null
+++ b/spec/ruby/core/hash/has_value_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/value'
+
+describe "Hash#has_value?" do
+ it_behaves_like :hash_value_p, :has_value?
+end
diff --git a/spec/ruby/core/hash/hash_spec.rb b/spec/ruby/core/hash/hash_spec.rb
new file mode 100644
index 0000000000..7c26f02640
--- /dev/null
+++ b/spec/ruby/core/hash/hash_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+
+describe "Hash" do
+ it "includes Enumerable" do
+ Hash.include?(Enumerable).should == true
+ end
+end
+
+describe "Hash#hash" do
+ it "returns a value which doesn't depend on the hash order" do
+ { 0=>2, 11=>1 }.hash.should == { 11=>1, 0=>2 }.hash
+ end
+
+ it "generates a hash for recursive hash structures" do
+ h = {}
+ h[:a] = h
+ (h.hash == h[:a].hash).should == true
+ end
+
+ it "returns the same hash for recursive hashes" do
+ h = {} ; h[:x] = h
+ h.hash.should == {x: h}.hash
+ h.hash.should == {x: {x: h}}.hash
+ # This is because h.eql?(x: h)
+ # Remember that if two objects are eql?
+ # then the need to have the same hash.
+ # Check the Hash#eql? specs!
+ end
+
+ it "returns the same hash for recursive hashes through arrays" do
+ h = {} ; rec = [h] ; h[:x] = rec
+ h.hash.should == {x: rec}.hash
+ h.hash.should == {x: [h]}.hash
+ # Like above, because h.eql?(x: [h])
+ end
+end
diff --git a/spec/ruby/core/hash/include_spec.rb b/spec/ruby/core/hash/include_spec.rb
new file mode 100644
index 0000000000..f3959dc589
--- /dev/null
+++ b/spec/ruby/core/hash/include_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/key'
+
+describe "Hash#include?" do
+ it_behaves_like :hash_key_p, :include?
+end
diff --git a/spec/ruby/core/hash/index_spec.rb b/spec/ruby/core/hash/index_spec.rb
new file mode 100644
index 0000000000..2b52c69949
--- /dev/null
+++ b/spec/ruby/core/hash/index_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/index'
+
+describe "Hash#index" do
+ it_behaves_like :hash_index, :index
+end
diff --git a/spec/ruby/core/hash/initialize_spec.rb b/spec/ruby/core/hash/initialize_spec.rb
new file mode 100644
index 0000000000..344571631a
--- /dev/null
+++ b/spec/ruby/core/hash/initialize_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#initialize" do
+ it "is private" do
+ Hash.should have_private_instance_method("initialize")
+ end
+
+ it "can be used to reset default_proc" do
+ h = { "foo" => 1, "bar" => 2 }
+ h.default_proc.should == nil
+ h.send(:initialize) { |_, k| k * 2 }
+ h.default_proc.should_not == nil
+ h["a"].should == "aa"
+ end
+
+ it "can be used to reset the default value" do
+ h = {}
+ h.default = 42
+ h.default.should == 42
+ h.send(:initialize, 1)
+ h.default.should == 1
+ h.send(:initialize)
+ h.default.should == nil
+ end
+
+ it "receives the arguments passed to Hash#new" do
+ HashSpecs::NewHash.new(:one, :two)[0].should == :one
+ HashSpecs::NewHash.new(:one, :two)[1].should == :two
+ end
+
+ it "does not change the storage, only the default value or proc" do
+ h = HashSpecs::SubHashSettingInInitialize.new
+ h.to_a.should == [[:foo, :bar]]
+
+ h = HashSpecs::SubHashSettingInInitialize.new(:default)
+ h.to_a.should == [[:foo, :bar]]
+
+ h = HashSpecs::SubHashSettingInInitialize.new { :default_block }
+ h.to_a.should == [[:foo, :bar]]
+ end
+
+ it "returns self" do
+ h = Hash.new
+ h.send(:initialize).should equal(h)
+ end
+
+ it "raises a #{frozen_error_class} if called on a frozen instance" do
+ block = lambda { HashSpecs.frozen_hash.instance_eval { initialize() }}
+ block.should raise_error(frozen_error_class)
+
+ block = lambda { HashSpecs.frozen_hash.instance_eval { initialize(nil) } }
+ block.should raise_error(frozen_error_class)
+
+ block = lambda { HashSpecs.frozen_hash.instance_eval { initialize(5) } }
+ block.should raise_error(frozen_error_class)
+
+ block = lambda { HashSpecs.frozen_hash.instance_eval { initialize { 5 } } }
+ block.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/hash/inspect_spec.rb b/spec/ruby/core/hash/inspect_spec.rb
new file mode 100644
index 0000000000..f41ebb70a6
--- /dev/null
+++ b/spec/ruby/core/hash/inspect_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/to_s'
+
+describe "Hash#inspect" do
+ it_behaves_like :hash_to_s, :inspect
+end
diff --git a/spec/ruby/core/hash/invert_spec.rb b/spec/ruby/core/hash/invert_spec.rb
new file mode 100644
index 0000000000..73377a9e97
--- /dev/null
+++ b/spec/ruby/core/hash/invert_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#invert" do
+ it "returns a new hash where keys are values and vice versa" do
+ { 1 => 'a', 2 => 'b', 3 => 'c' }.invert.should ==
+ { 'a' => 1, 'b' => 2, 'c' => 3 }
+ end
+
+ it "handles collisions by overriding with the key coming later in keys()" do
+ h = { a: 1, b: 1 }
+ override_key = h.keys.last
+ h.invert[1].should == override_key
+ end
+
+ it "compares new keys with eql? semantics" do
+ h = { a: 1.0, b: 1 }
+ i = h.invert
+ i[1.0].should == :a
+ i[1].should == :b
+ end
+
+ it "does not return subclass instances for subclasses" do
+ HashSpecs::MyHash[1 => 2, 3 => 4].invert.class.should == Hash
+ HashSpecs::MyHash[].invert.class.should == Hash
+ end
+end
diff --git a/spec/ruby/core/hash/keep_if_spec.rb b/spec/ruby/core/hash/keep_if_spec.rb
new file mode 100644
index 0000000000..80f7fbbf64
--- /dev/null
+++ b/spec/ruby/core/hash/keep_if_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/iteration'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Hash#keep_if" do
+ it "yields two arguments: key and value" do
+ all_args = []
+ { 1 => 2, 3 => 4 }.keep_if { |*args| all_args << args }
+ all_args.should == [[1, 2], [3, 4]]
+ end
+
+ it "keeps every entry for which block is true and returns self" do
+ h = { a: 1, b: 2, c: 3, d: 4 }
+ h.keep_if { |k,v| v % 2 == 0 }.should equal(h)
+ h.should == { b: 2, d: 4 }
+ end
+
+ it "removes all entries if the block is false" do
+ h = { a: 1, b: 2, c: 3 }
+ h.keep_if { |k,v| false }.should equal(h)
+ h.should == {}
+ end
+
+ it "returns self even if unmodified" do
+ h = { 1 => 2, 3 => 4 }
+ h.keep_if { true }.should equal(h)
+ end
+
+ it "raises a #{frozen_error_class} if called on a frozen instance" do
+ lambda { HashSpecs.frozen_hash.keep_if { true } }.should raise_error(frozen_error_class)
+ lambda { HashSpecs.empty_frozen_hash.keep_if { false } }.should raise_error(frozen_error_class)
+ end
+
+ it_behaves_like :hash_iteration_no_block, :keep_if
+ it_behaves_like :enumeratorized_with_origin_size, :keep_if, { 1 => 2, 3 => 4, 5 => 6 }
+end
diff --git a/spec/ruby/core/hash/key_spec.rb b/spec/ruby/core/hash/key_spec.rb
new file mode 100644
index 0000000000..73eecbc98e
--- /dev/null
+++ b/spec/ruby/core/hash/key_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/key'
+require_relative 'shared/index'
+
+describe "Hash#key?" do
+ it_behaves_like :hash_key_p, :key?
+end
+
+describe "Hash#key" do
+ it_behaves_like :hash_index, :key
+end
diff --git a/spec/ruby/core/hash/keys_spec.rb b/spec/ruby/core/hash/keys_spec.rb
new file mode 100644
index 0000000000..9a067085e5
--- /dev/null
+++ b/spec/ruby/core/hash/keys_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#keys" do
+
+ it "returns an array with the keys in the order they were inserted" do
+ {}.keys.should == []
+ {}.keys.should be_kind_of(Array)
+ Hash.new(5).keys.should == []
+ Hash.new { 5 }.keys.should == []
+ { 1 => 2, 4 => 8, 2 => 4 }.keys.should == [1, 4, 2]
+ { 1 => 2, 2 => 4, 4 => 8 }.keys.should be_kind_of(Array)
+ { nil => nil }.keys.should == [nil]
+ end
+
+ it "uses the same order as #values" do
+ h = { 1 => "1", 2 => "2", 3 => "3", 4 => "4" }
+
+ h.size.times do |i|
+ h[h.keys[i]].should == h.values[i]
+ end
+ end
+end
diff --git a/spec/ruby/core/hash/length_spec.rb b/spec/ruby/core/hash/length_spec.rb
new file mode 100644
index 0000000000..d0af0945df
--- /dev/null
+++ b/spec/ruby/core/hash/length_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/length'
+
+describe "Hash#length" do
+ it_behaves_like :hash_length, :length
+end
diff --git a/spec/ruby/core/hash/lt_spec.rb b/spec/ruby/core/hash/lt_spec.rb
new file mode 100644
index 0000000000..2219615880
--- /dev/null
+++ b/spec/ruby/core/hash/lt_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'shared/comparison'
+require_relative 'shared/less_than'
+
+describe "Hash#<" do
+ it_behaves_like :hash_comparison, :<
+ it_behaves_like :hash_less_than, :<
+
+ it "returns false if both hashes are identical" do
+ h = { a: 1, b: 2 }
+ (h < h).should be_false
+ end
+end
+
+describe "Hash#<" do
+ before :each do
+ @hash = {a:1, b:2}
+ @bigger = {a:1, b:2, c:3}
+ @unrelated = {c:3, d:4}
+ @similar = {a:2, b:3}
+ end
+
+ it "returns false when receiver size is larger than argument" do
+ (@bigger < @hash).should == false
+ (@bigger < @unrelated).should == false
+ end
+
+ it "returns false when receiver size is the same as argument" do
+ (@hash < @hash).should == false
+ (@hash < @unrelated).should == false
+ (@unrelated < @hash).should == false
+ end
+
+ it "returns true when receiver is a subset of argument" do
+ (@hash < @bigger).should == true
+ end
+
+ it "returns false when keys match but values don't" do
+ (@hash < @similar).should == false
+ (@similar < @hash).should == false
+ end
+end
diff --git a/spec/ruby/core/hash/lte_spec.rb b/spec/ruby/core/hash/lte_spec.rb
new file mode 100644
index 0000000000..a166e5bca4
--- /dev/null
+++ b/spec/ruby/core/hash/lte_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'shared/comparison'
+require_relative 'shared/less_than'
+
+describe "Hash#<=" do
+ it_behaves_like :hash_comparison, :<=
+ it_behaves_like :hash_less_than, :<=
+
+ it "returns true if both hashes are identical" do
+ h = { a: 1, b: 2 }
+ (h <= h).should be_true
+ end
+end
+
+describe "Hash#<=" do
+ before :each do
+ @hash = {a:1, b:2}
+ @bigger = {a:1, b:2, c:3}
+ @unrelated = {c:3, d:4}
+ @similar = {a:2, b:3}
+ end
+
+ it "returns false when receiver size is larger than argument" do
+ (@bigger <= @hash).should == false
+ (@bigger <= @unrelated).should == false
+ end
+
+ it "returns false when receiver size is the same as argument" do
+ (@hash <= @unrelated).should == false
+ (@unrelated <= @hash).should == false
+ end
+
+ it "returns true when receiver is a subset of argument or equals to argument" do
+ (@hash <= @bigger).should == true
+ (@hash <= @hash).should == true
+ end
+
+ it "returns false when keys match but values don't" do
+ (@hash <= @similar).should == false
+ (@similar <= @hash).should == false
+ end
+end
diff --git a/spec/ruby/core/hash/member_spec.rb b/spec/ruby/core/hash/member_spec.rb
new file mode 100644
index 0000000000..37c0414559
--- /dev/null
+++ b/spec/ruby/core/hash/member_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/key'
+
+describe "Hash#member?" do
+ it_behaves_like :hash_key_p, :member?
+end
diff --git a/spec/ruby/core/hash/merge_spec.rb b/spec/ruby/core/hash/merge_spec.rb
new file mode 100644
index 0000000000..3af1ea6d4f
--- /dev/null
+++ b/spec/ruby/core/hash/merge_spec.rb
@@ -0,0 +1,77 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/iteration'
+require_relative 'shared/update'
+
+describe "Hash#merge" do
+ it "returns a new hash by combining self with the contents of other" do
+ h = { 1 => :a, 2 => :b, 3 => :c }.merge(a: 1, c: 2)
+ h.should == { c: 2, 1 => :a, 2 => :b, a: 1, 3 => :c }
+
+ hash = { a: 1, b: 2 }
+ {}.merge(hash).should == hash
+ hash.merge({}).should == hash
+
+ h = { 1 => :a, 2 => :b, 3 => :c }.merge(1 => :b)
+ h.should == { 1 => :b, 2 => :b, 3 => :c }
+
+ h = { 1 => :a, 2 => :b }.merge(1 => :b, 3 => :c)
+ h.should == { 1 => :b, 2 => :b, 3 => :c }
+ end
+
+ it "sets any duplicate key to the value of block if passed a block" do
+ h1 = { a: 2, b: 1, d: 5 }
+ h2 = { a: -2, b: 4, c: -3 }
+ r = h1.merge(h2) { |k,x,y| nil }
+ r.should == { a: nil, b: nil, c: -3, d: 5 }
+
+ r = h1.merge(h2) { |k,x,y| "#{k}:#{x+2*y}" }
+ r.should == { a: "a:-2", b: "b:9", c: -3, d: 5 }
+
+ lambda {
+ h1.merge(h2) { |k, x, y| raise(IndexError) }
+ }.should raise_error(IndexError)
+
+ r = h1.merge(h1) { |k,x,y| :x }
+ r.should == { a: :x, b: :x, d: :x }
+ end
+
+ it "tries to convert the passed argument to a hash using #to_hash" do
+ obj = mock('{1=>2}')
+ obj.should_receive(:to_hash).and_return({ 1 => 2 })
+ { 3 => 4 }.merge(obj).should == { 1 => 2, 3 => 4 }
+ end
+
+ it "does not call to_hash on hash subclasses" do
+ { 3 => 4 }.merge(HashSpecs::ToHashHash[1 => 2]).should == { 1 => 2, 3 => 4 }
+ end
+
+ it "returns subclass instance for subclasses" do
+ HashSpecs::MyHash[1 => 2, 3 => 4].merge({ 1 => 2 }).should be_an_instance_of(HashSpecs::MyHash)
+ HashSpecs::MyHash[].merge({ 1 => 2 }).should be_an_instance_of(HashSpecs::MyHash)
+
+ { 1 => 2, 3 => 4 }.merge(HashSpecs::MyHash[1 => 2]).class.should == Hash
+ {}.merge(HashSpecs::MyHash[1 => 2]).class.should == Hash
+ end
+
+ it "processes entries with same order as each()" do
+ h = { 1 => 2, 3 => 4, 5 => 6, "x" => nil, nil => 5, [] => [] }
+ merge_pairs = []
+ each_pairs = []
+ h.each_pair { |k, v| each_pairs << [k, v] }
+ h.merge(h) { |k, v1, v2| merge_pairs << [k, v1] }
+ merge_pairs.should == each_pairs
+ end
+
+end
+
+describe "Hash#merge!" do
+ it_behaves_like :hash_update, :merge!
+
+ it "does not raise an exception if changing the value of an existing key during iteration" do
+ hash = {1 => 2, 3 => 4, 5 => 6}
+ hash2 = {1 => :foo, 3 => :bar}
+ hash.each { hash.merge!(hash2) }
+ hash.should == {1 => :foo, 3 => :bar, 5 => 6}
+ end
+end
diff --git a/spec/ruby/core/hash/new_spec.rb b/spec/ruby/core/hash/new_spec.rb
new file mode 100644
index 0000000000..c21266777f
--- /dev/null
+++ b/spec/ruby/core/hash/new_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash.new" do
+ it "creates an empty Hash if passed no arguments" do
+ Hash.new.should == {}
+ Hash.new.size.should == 0
+ end
+
+ it "creates a new Hash with default object if passed a default argument" do
+ Hash.new(5).default.should == 5
+ Hash.new({}).default.should == {}
+ end
+
+ it "does not create a copy of the default argument" do
+ str = "foo"
+ Hash.new(str).default.should equal(str)
+ end
+
+ it "creates a Hash with a default_proc if passed a block" do
+ Hash.new.default_proc.should == nil
+
+ h = Hash.new { |x| "Answer to #{x}" }
+ h.default_proc.call(5).should == "Answer to 5"
+ h.default_proc.call("x").should == "Answer to x"
+ end
+
+ it "raises an ArgumentError if more than one argument is passed" do
+ lambda { Hash.new(5,6) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if passed both default argument and default block" do
+ lambda { Hash.new(5) { 0 } }.should raise_error(ArgumentError)
+ lambda { Hash.new(nil) { 0 } }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/hash/rassoc_spec.rb b/spec/ruby/core/hash/rassoc_spec.rb
new file mode 100644
index 0000000000..f3b2a6de20
--- /dev/null
+++ b/spec/ruby/core/hash/rassoc_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+
+describe "Hash#rassoc" do
+ before :each do
+ @h = {apple: :green, orange: :orange, grape: :green, banana: :yellow}
+ end
+
+ it "returns an Array if the argument is a value of the Hash" do
+ @h.rassoc(:green).should be_an_instance_of(Array)
+ end
+
+ it "returns a 2-element Array if the argument is a value of the Hash" do
+ @h.rassoc(:orange).size.should == 2
+ end
+
+ it "sets the first element of the Array to the key of the located value" do
+ @h.rassoc(:yellow).first.should == :banana
+ end
+
+ it "sets the last element of the Array to the located value" do
+ @h.rassoc(:yellow).last.should == :yellow
+ end
+
+ it "only returns the first matching key-value pair" do
+ @h.rassoc(:green).should == [:apple, :green]
+ end
+
+ it "uses #== to compare the argument to the values" do
+ @h[:key] = 1.0
+ 1.should == 1.0
+ @h.rassoc(1).should eql [:key, 1.0]
+ end
+
+ it "returns nil if the argument is not a value of the Hash" do
+ @h.rassoc(:banana).should be_nil
+ end
+
+ it "returns nil if the argument is not a value of the Hash even when there is a default" do
+ Hash.new(42).merge!( foo: :bar ).rassoc(42).should be_nil
+ Hash.new{|h, k| h[k] = 42}.merge!( foo: :bar ).rassoc(42).should be_nil
+ end
+end
diff --git a/spec/ruby/core/hash/rehash_spec.rb b/spec/ruby/core/hash/rehash_spec.rb
new file mode 100644
index 0000000000..32ba3fcfb9
--- /dev/null
+++ b/spec/ruby/core/hash/rehash_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#rehash" do
+ it "reorganizes the hash by recomputing all key hash codes" do
+ k1 = [1]
+ k2 = [2]
+ h = {}
+ h[k1] = 0
+ h[k2] = 1
+
+ k1 << 2
+ h.key?(k1).should == false
+ h.keys.include?(k1).should == true
+
+ h.rehash.should equal(h)
+ h.key?(k1).should == true
+ h[k1].should == 0
+
+ k1 = mock('k1')
+ k2 = mock('k2')
+ v1 = mock('v1')
+ v2 = mock('v2')
+
+ v1.should_not_receive(:hash)
+ v2.should_not_receive(:hash)
+
+ h = { k1 => v1, k2 => v2 }
+
+ k1.should_receive(:hash).twice.and_return(0)
+ k2.should_receive(:hash).twice.and_return(0)
+
+ h.rehash
+ h[k1].should == v1
+ h[k2].should == v2
+ end
+
+ it "removes duplicate keys" do
+ a = [1,2]
+ b = [1]
+
+ h = {}
+ h[a] = true
+ h[b] = true
+ b << 2
+ h.size.should == 2
+ h.keys.should == [a, b]
+ h.rehash
+ h.size.should == 1
+ h.keys.should == [a]
+ end
+
+ it "raises a #{frozen_error_class} if called on a frozen instance" do
+ lambda { HashSpecs.frozen_hash.rehash }.should raise_error(frozen_error_class)
+ lambda { HashSpecs.empty_frozen_hash.rehash }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/hash/reject_spec.rb b/spec/ruby/core/hash/reject_spec.rb
new file mode 100644
index 0000000000..a11e80a34a
--- /dev/null
+++ b/spec/ruby/core/hash/reject_spec.rb
@@ -0,0 +1,100 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/iteration'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Hash#reject" do
+ it "returns a new hash removing keys for which the block yields true" do
+ h = { 1=>false, 2=>true, 3=>false, 4=>true }
+ h.reject { |k,v| v }.keys.sort.should == [1,3]
+ end
+
+ it "is equivalent to hsh.dup.delete_if" do
+ h = { a: 'a', b: 'b', c: 'd' }
+ h.reject { |k,v| k == 'd' }.should == (h.dup.delete_if { |k, v| k == 'd' })
+
+ all_args_reject = []
+ all_args_delete_if = []
+ h = { 1 => 2, 3 => 4 }
+ h.reject { |*args| all_args_reject << args }
+ h.delete_if { |*args| all_args_delete_if << args }
+ all_args_reject.should == all_args_delete_if
+
+ h = { 1 => 2 }
+ # dup doesn't copy singleton methods
+ def h.to_a() end
+ h.reject { false }.to_a.should == [[1, 2]]
+ end
+
+ context "with extra state" do
+ it "returns Hash instance for subclasses" do
+ HashSpecs::MyHash[1 => 2, 3 => 4].reject { false }.should be_kind_of(Hash)
+ HashSpecs::MyHash[1 => 2, 3 => 4].reject { true }.should be_kind_of(Hash)
+ end
+
+ it "does not taint the resulting hash" do
+ h = { a: 1 }.taint
+ h.reject {false}.tainted?.should == false
+ end
+ end
+
+ it "processes entries with the same order as reject!" do
+ h = { a: 1, b: 2, c: 3, d: 4 }
+
+ reject_pairs = []
+ reject_bang_pairs = []
+ h.dup.reject { |*pair| reject_pairs << pair }
+ h.reject! { |*pair| reject_bang_pairs << pair }
+
+ reject_pairs.should == reject_bang_pairs
+ end
+
+ it_behaves_like :hash_iteration_no_block, :reject
+ it_behaves_like :enumeratorized_with_origin_size, :reject, { 1 => 2, 3 => 4, 5 => 6 }
+end
+
+describe "Hash#reject!" do
+ it "removes keys from self for which the block yields true" do
+ hsh = {}
+ (1 .. 10).each { |k| hsh[k] = (k % 2 == 0) }
+ hsh.reject! { |k,v| v }
+ hsh.keys.sort.should == [1,3,5,7,9]
+ end
+
+ it "removes all entries if the block is true" do
+ h = { a: 1, b: 2, c: 3 }
+ h.reject! { |k,v| true }.should equal(h)
+ h.should == {}
+ end
+
+ it "is equivalent to delete_if if changes are made" do
+ hsh = { a: 1 }
+ hsh.reject! { |k,v| v < 2 }.should == hsh.dup.delete_if { |k, v| v < 2 }
+ end
+
+ it "returns nil if no changes were made" do
+ { a: 1 }.reject! { |k,v| v > 1 }.should == nil
+ end
+
+ it "processes entries with the same order as delete_if" do
+ h = { a: 1, b: 2, c: 3, d: 4 }
+
+ reject_bang_pairs = []
+ delete_if_pairs = []
+ h.dup.reject! { |*pair| reject_bang_pairs << pair }
+ h.dup.delete_if { |*pair| delete_if_pairs << pair }
+
+ reject_bang_pairs.should == delete_if_pairs
+ end
+
+ it "raises a #{frozen_error_class} if called on a frozen instance that is modified" do
+ lambda { HashSpecs.empty_frozen_hash.reject! { true } }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} if called on a frozen instance that would not be modified" do
+ lambda { HashSpecs.frozen_hash.reject! { false } }.should raise_error(frozen_error_class)
+ end
+
+ it_behaves_like :hash_iteration_no_block, :reject!
+ it_behaves_like :enumeratorized_with_origin_size, :reject!, { 1 => 2, 3 => 4, 5 => 6 }
+end
diff --git a/spec/ruby/core/hash/replace_spec.rb b/spec/ruby/core/hash/replace_spec.rb
new file mode 100644
index 0000000000..92b2118fd3
--- /dev/null
+++ b/spec/ruby/core/hash/replace_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/replace'
+
+describe "Hash#replace" do
+ it_behaves_like :hash_replace, :replace
+end
diff --git a/spec/ruby/core/hash/select_spec.rb b/spec/ruby/core/hash/select_spec.rb
new file mode 100644
index 0000000000..38b0180b0e
--- /dev/null
+++ b/spec/ruby/core/hash/select_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'shared/select'
+
+describe "Hash#select" do
+ it_behaves_like :hash_select, :select
+end
+
+describe "Hash#select!" do
+ it_behaves_like :hash_select!, :select!
+end
diff --git a/spec/ruby/core/hash/shared/comparison.rb b/spec/ruby/core/hash/shared/comparison.rb
new file mode 100644
index 0000000000..bbb9bfd6ad
--- /dev/null
+++ b/spec/ruby/core/hash/shared/comparison.rb
@@ -0,0 +1,15 @@
+describe :hash_comparison, shared: true do
+ it "raises a TypeError if the right operand is not a hash" do
+ lambda { { a: 1 }.send(@method, 1) }.should raise_error(TypeError)
+ lambda { { a: 1 }.send(@method, nil) }.should raise_error(TypeError)
+ lambda { { a: 1 }.send(@method, []) }.should raise_error(TypeError)
+ end
+
+ it "returns false if both hashes have the same keys but different values" do
+ h1 = { a: 1 }
+ h2 = { a: 2 }
+
+ h1.send(@method, h2).should be_false
+ h2.send(@method, h1).should be_false
+ end
+end
diff --git a/spec/ruby/core/hash/shared/each.rb b/spec/ruby/core/hash/shared/each.rb
new file mode 100644
index 0000000000..bf4c569cfc
--- /dev/null
+++ b/spec/ruby/core/hash/shared/each.rb
@@ -0,0 +1,68 @@
+describe :hash_each, shared: true do
+ it "yields a [[key, value]] Array for each pair to a block expecting |*args|" do
+ all_args = []
+ { 1 => 2, 3 => 4 }.send(@method) { |*args| all_args << args }
+ all_args.sort.should == [[[1, 2]], [[3, 4]]]
+ end
+
+ it "yields the key and value of each pair to a block expecting |key, value|" do
+ r = {}
+ h = { a: 1, b: 2, c: 3, d: 5 }
+ h.send(@method) { |k,v| r[k.to_s] = v.to_s }.should equal(h)
+ r.should == { "a" => "1", "b" => "2", "c" => "3", "d" => "5" }
+ end
+
+ it "yields the key only to a block expecting |key,|" do
+ ary = []
+ h = { "a" => 1, "b" => 2, "c" => 3 }
+ h.send(@method) { |k,| ary << k }
+ ary.sort.should == ["a", "b", "c"]
+ end
+
+ it "uses the same order as keys() and values()" do
+ h = { a: 1, b: 2, c: 3, d: 5 }
+ keys = []
+ values = []
+
+ h.send(@method) do |k, v|
+ keys << k
+ values << v
+ end
+
+ keys.should == h.keys
+ values.should == h.values
+ end
+
+ # Confirming the argument-splatting works from child class for both k, v and [k, v]
+ it "properly expands (or not) child class's 'each'-yielded args" do
+ cls1 = Class.new(Hash) do
+ attr_accessor :k_v
+ def each
+ super do |k, v|
+ @k_v = [k, v]
+ yield k, v
+ end
+ end
+ end
+
+ cls2 = Class.new(Hash) do
+ attr_accessor :k_v
+ def each
+ super do |k, v|
+ @k_v = [k, v]
+ yield([k, v])
+ end
+ end
+ end
+
+ obj1 = cls1.new
+ obj1['a'] = 'b'
+ obj1.map {|k, v| [k, v]}.should == [['a', 'b']]
+ obj1.k_v.should == ['a', 'b']
+
+ obj2 = cls2.new
+ obj2['a'] = 'b'
+ obj2.map {|k, v| [k, v]}.should == [['a', 'b']]
+ obj2.k_v.should == ['a', 'b']
+ end
+end
diff --git a/spec/ruby/core/hash/shared/eql.rb b/spec/ruby/core/hash/shared/eql.rb
new file mode 100644
index 0000000000..1aed5f51fb
--- /dev/null
+++ b/spec/ruby/core/hash/shared/eql.rb
@@ -0,0 +1,216 @@
+describe :hash_eql, shared: true do
+ it "does not compare values when keys don't match" do
+ value = mock('x')
+ value.should_not_receive(:==)
+ value.should_not_receive(:eql?)
+ { 1 => value }.send(@method, { 2 => value }).should be_false
+ end
+
+ it "returns false when the numbers of keys differ without comparing any elements" do
+ obj = mock('x')
+ h = { obj => obj }
+
+ obj.should_not_receive(:==)
+ obj.should_not_receive(:eql?)
+
+ {}.send(@method, h).should be_false
+ h.send(@method, {}).should be_false
+ end
+
+ it "first compares keys via hash" do
+ x = mock('x')
+ x.should_receive(:hash).any_number_of_times.and_return(0)
+ y = mock('y')
+ y.should_receive(:hash).any_number_of_times.and_return(0)
+
+ { x => 1 }.send(@method, { y => 1 }).should be_false
+ end
+
+ it "does not compare keys with different hash codes via eql?" do
+ x = mock('x')
+ y = mock('y')
+ x.should_not_receive(:eql?)
+ y.should_not_receive(:eql?)
+
+ x.should_receive(:hash).any_number_of_times.and_return(0)
+ y.should_receive(:hash).any_number_of_times.and_return(1)
+
+ { x => 1 }.send(@method, { y => 1 }).should be_false
+ end
+
+ it "computes equality for recursive hashes" do
+ h = {}
+ h[:a] = h
+ h.send(@method, h[:a]).should be_true
+ (h == h[:a]).should be_true
+ end
+
+ it "doesn't call to_hash on objects" do
+ mock_hash = mock("fake hash")
+ def mock_hash.to_hash() {} end
+ {}.send(@method, mock_hash).should be_false
+ end
+
+ it "computes equality for complex recursive hashes" do
+ a, b = {}, {}
+ a.merge! self: a, other: b
+ b.merge! self: b, other: a
+ a.send(@method, b).should be_true # they both have the same structure!
+
+ c = {}
+ c.merge! other: c, self: c
+ c.send(@method, a).should be_true # subtle, but they both have the same structure!
+ a[:delta] = c[:delta] = a
+ c.send(@method, a).should be_false # not quite the same structure, as a[:other][:delta] = nil
+ c[:delta] = 42
+ c.send(@method, a).should be_false
+ a[:delta] = 42
+ c.send(@method, a).should be_false
+ b[:delta] = 42
+ c.send(@method, a).should be_true
+ end
+
+ it "computes equality for recursive hashes & arrays" do
+ x, y, z = [], [], []
+ a, b, c = {foo: x, bar: 42}, {foo: y, bar: 42}, {foo: z, bar: 42}
+ x << a
+ y << c
+ z << b
+ b.send(@method, c).should be_true # they clearly have the same structure!
+ y.send(@method, z).should be_true
+ a.send(@method, b).should be_true # subtle, but they both have the same structure!
+ x.send(@method, y).should be_true
+ y << x
+ y.send(@method, z).should be_false
+ z << x
+ y.send(@method, z).should be_true
+
+ a[:foo], a[:bar] = a[:bar], a[:foo]
+ a.send(@method, b).should be_false
+ b[:bar] = b[:foo]
+ b.send(@method, c).should be_false
+ end
+end
+
+describe :hash_eql_additional, shared: true do
+ it "compares values when keys match" do
+ x = mock('x')
+ y = mock('y')
+ def x.==(o) false end
+ def y.==(o) false end
+ def x.eql?(o) false end
+ def y.eql?(o) false end
+ { 1 => x }.send(@method, { 1 => y }).should be_false
+
+ x = mock('x')
+ y = mock('y')
+ def x.==(o) true end
+ def y.==(o) true end
+ def x.eql?(o) true end
+ def y.eql?(o) true end
+ { 1 => x }.send(@method, { 1 => y }).should be_true
+ end
+
+ it "compares keys with eql? semantics" do
+ { 1.0 => "x" }.send(@method, { 1.0 => "x" }).should be_true
+ { 1.0 => "x" }.send(@method, { 1.0 => "x" }).should be_true
+ { 1 => "x" }.send(@method, { 1.0 => "x" }).should be_false
+ { 1.0 => "x" }.send(@method, { 1 => "x" }).should be_false
+ end
+
+ it "returns true iff other Hash has the same number of keys and each key-value pair matches" do
+ a = { a: 5 }
+ b = {}
+ a.send(@method, b).should be_false
+
+ b[:a] = 5
+ a.send(@method, b).should be_true
+
+ not_supported_on :opal do
+ c = { "a" => 5 }
+ a.send(@method, c).should be_false
+ end
+
+ c = { "A" => 5 }
+ a.send(@method, c).should be_false
+
+ c = { a: 6 }
+ a.send(@method, c).should be_false
+ end
+
+ it "does not call to_hash on hash subclasses" do
+ { 5 => 6 }.send(@method, HashSpecs::ToHashHash[5 => 6]).should be_true
+ end
+
+ it "ignores hash class differences" do
+ h = { 1 => 2, 3 => 4 }
+ HashSpecs::MyHash[h].send(@method, h).should be_true
+ HashSpecs::MyHash[h].send(@method, HashSpecs::MyHash[h]).should be_true
+ h.send(@method, HashSpecs::MyHash[h]).should be_true
+ end
+
+ # Why isn't this true of eql? too ?
+ it "compares keys with matching hash codes via eql?" do
+ a = Array.new(2) do
+ obj = mock('0')
+ obj.should_receive(:hash).at_least(1).and_return(0)
+
+ # It's undefined whether the impl does a[0].eql?(a[1]) or
+ # a[1].eql?(a[0]) so we taint both.
+ def obj.eql?(o)
+ return true if self.equal?(o)
+ taint
+ o.taint
+ false
+ end
+
+ obj
+ end
+
+ { a[0] => 1 }.send(@method, { a[1] => 1 }).should be_false
+ a[0].tainted?.should be_true
+ a[1].tainted?.should be_true
+
+ a = Array.new(2) do
+ obj = mock('0')
+ obj.should_receive(:hash).at_least(1).and_return(0)
+
+ def obj.eql?(o)
+ # It's undefined whether the impl does a[0].send(@method, a[1]) or
+ # a[1].send(@method, a[0]) so we taint both.
+ taint
+ o.taint
+ true
+ end
+
+ obj
+ end
+
+ { a[0] => 1 }.send(@method, { a[1] => 1 }).should be_true
+ a[0].tainted?.should be_true
+ a[1].tainted?.should be_true
+ end
+
+ it "compares the values in self to values in other hash" do
+ l_val = mock("left")
+ r_val = mock("right")
+
+ l_val.should_receive(:eql?).with(r_val).and_return(true)
+
+ { 1 => l_val }.eql?({ 1 => r_val }).should be_true
+ end
+end
+
+describe :hash_eql_additional_more, shared: true do
+ it "returns true if other Hash has the same number of keys and each key-value pair matches, even though the default-value are not same" do
+ Hash.new(5).send(@method, Hash.new(1)).should be_true
+ Hash.new {|h, k| 1}.send(@method, Hash.new {}).should be_true
+ Hash.new {|h, k| 1}.send(@method, Hash.new(2)).should be_true
+
+ d = Hash.new {|h, k| 1}
+ e = Hash.new {}
+ d[1] = 2
+ e[1] = 2
+ d.send(@method, e).should be_true
+ end
+end
diff --git a/spec/ruby/core/hash/shared/equal.rb b/spec/ruby/core/hash/shared/equal.rb
new file mode 100644
index 0000000000..43606437fe
--- /dev/null
+++ b/spec/ruby/core/hash/shared/equal.rb
@@ -0,0 +1,90 @@
+describe :hash_equal, shared: true do
+ it "does not compare values when keys don't match" do
+ value = mock('x')
+ value.should_not_receive(:==)
+ value.should_not_receive(:eql?)
+ { 1 => value }.send(@method, { 2 => value }).should be_false
+ end
+
+ it "returns false when the numbers of keys differ without comparing any elements" do
+ obj = mock('x')
+ h = { obj => obj }
+
+ obj.should_not_receive(:==)
+ obj.should_not_receive(:eql?)
+
+ {}.send(@method, h).should be_false
+ h.send(@method, {}).should be_false
+ end
+
+ it "first compares keys via hash" do
+ x = mock('x')
+ x.should_receive(:hash).and_return(0)
+ y = mock('y')
+ y.should_receive(:hash).and_return(0)
+
+ { x => 1 }.send(@method, { y => 1 }).should be_false
+ end
+
+ it "does not compare keys with different hash codes via eql?" do
+ x = mock('x')
+ y = mock('y')
+ x.should_not_receive(:eql?)
+ y.should_not_receive(:eql?)
+
+ x.should_receive(:hash).and_return(0)
+ y.should_receive(:hash).and_return(1)
+
+ def x.hash() 0 end
+ def y.hash() 1 end
+
+ { x => 1 }.send(@method, { y => 1 }).should be_false
+ end
+
+ it "computes equality for recursive hashes" do
+ h = {}
+ h[:a] = h
+ h.send(@method, h[:a]).should be_true
+ (h == h[:a]).should be_true
+ end
+
+ it "computes equality for complex recursive hashes" do
+ a, b = {}, {}
+ a.merge! self: a, other: b
+ b.merge! self: b, other: a
+ a.send(@method, b).should be_true # they both have the same structure!
+
+ c = {}
+ c.merge! other: c, self: c
+ c.send(@method, a).should be_true # subtle, but they both have the same structure!
+ a[:delta] = c[:delta] = a
+ c.send(@method, a).should be_false # not quite the same structure, as a[:other][:delta] = nil
+ c[:delta] = 42
+ c.send(@method, a).should be_false
+ a[:delta] = 42
+ c.send(@method, a).should be_false
+ b[:delta] = 42
+ c.send(@method, a).should be_true
+ end
+
+ it "computes equality for recursive hashes & arrays" do
+ x, y, z = [], [], []
+ a, b, c = {foo: x, bar: 42}, {foo: y, bar: 42}, {foo: z, bar: 42}
+ x << a
+ y << c
+ z << b
+ b.send(@method, c).should be_true # they clearly have the same structure!
+ y.send(@method, z).should be_true
+ a.send(@method, b).should be_true # subtle, but they both have the same structure!
+ x.send(@method, y).should be_true
+ y << x
+ y.send(@method, z).should be_false
+ z << x
+ y.send(@method, z).should be_true
+
+ a[:foo], a[:bar] = a[:bar], a[:foo]
+ a.send(@method, b).should be_false
+ b[:bar] = b[:foo]
+ b.send(@method, c).should be_false
+ end
+end
diff --git a/spec/ruby/core/hash/shared/greater_than.rb b/spec/ruby/core/hash/shared/greater_than.rb
new file mode 100644
index 0000000000..1f8b9fcfb7
--- /dev/null
+++ b/spec/ruby/core/hash/shared/greater_than.rb
@@ -0,0 +1,23 @@
+describe :hash_greater_than, shared: true do
+ before do
+ @h1 = { a: 1, b: 2, c: 3 }
+ @h2 = { a: 1, b: 2 }
+ end
+
+ it "returns true if the other hash is a subset of self" do
+ @h1.send(@method, @h2).should be_true
+ end
+
+ it "returns false if the other hash is not a subset of self" do
+ @h2.send(@method, @h1).should be_false
+ end
+
+ it "converts the right operand to a hash before comparing" do
+ o = Object.new
+ def o.to_hash
+ { a: 1, b: 2 }
+ end
+
+ @h1.send(@method, o).should be_true
+ end
+end
diff --git a/spec/ruby/core/hash/shared/index.rb b/spec/ruby/core/hash/shared/index.rb
new file mode 100644
index 0000000000..4858ba85f5
--- /dev/null
+++ b/spec/ruby/core/hash/shared/index.rb
@@ -0,0 +1,27 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :hash_index, shared: true do
+ it "returns the corresponding key for value" do
+ { 2 => 'a', 1 => 'b' }.send(@method, 'b').should == 1
+ end
+
+ it "returns nil if the value is not found" do
+ { a: -1, b: 3.14, c: 2.718 }.send(@method, 1).should be_nil
+ end
+
+ it "doesn't return default value if the value is not found" do
+ Hash.new(5).send(@method, 5).should be_nil
+ end
+
+ it "compares values using ==" do
+ { 1 => 0 }.send(@method, 0.0).should == 1
+ { 1 => 0.0 }.send(@method, 0).should == 1
+
+ needle = mock('needle')
+ inhash = mock('inhash')
+ inhash.should_receive(:==).with(needle).and_return(true)
+
+ { 1 => inhash }.send(@method, needle).should == 1
+ end
+end
diff --git a/spec/ruby/core/hash/shared/iteration.rb b/spec/ruby/core/hash/shared/iteration.rb
new file mode 100644
index 0000000000..d27c2443f8
--- /dev/null
+++ b/spec/ruby/core/hash/shared/iteration.rb
@@ -0,0 +1,19 @@
+describe :hash_iteration_no_block, shared: true do
+ before :each do
+ @hsh = { 1 => 2, 3 => 4, 5 => 6 }
+ @empty = {}
+ end
+
+ it "returns an Enumerator if called on a non-empty hash without a block" do
+ @hsh.send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ it "returns an Enumerator if called on an empty hash without a block" do
+ @empty.send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ it "returns an Enumerator if called on a frozen instance" do
+ @hsh.freeze
+ @hsh.send(@method).should be_an_instance_of(Enumerator)
+ end
+end
diff --git a/spec/ruby/core/hash/shared/key.rb b/spec/ruby/core/hash/shared/key.rb
new file mode 100644
index 0000000000..17f9f81457
--- /dev/null
+++ b/spec/ruby/core/hash/shared/key.rb
@@ -0,0 +1,38 @@
+describe :hash_key_p, shared: true do
+ it "returns true if argument is a key" do
+ h = { a: 1, b: 2, c: 3, 4 => 0 }
+ h.send(@method, :a).should == true
+ h.send(@method, :b).should == true
+ h.send(@method, 2).should == false
+ h.send(@method, 4).should == true
+
+ not_supported_on :opal do
+ h.send(@method, 'b').should == false
+ h.send(@method, 4.0).should == false
+ end
+ end
+
+ it "returns true if the key's matching value was nil" do
+ { xyz: nil }.send(@method, :xyz).should == true
+ end
+
+ it "returns true if the key's matching value was false" do
+ { xyz: false }.send(@method, :xyz).should == true
+ end
+
+ it "returns true if the key is nil" do
+ { nil => 'b' }.send(@method, nil).should == true
+ { nil => nil }.send(@method, nil).should == true
+ end
+
+ it "compares keys with the same #hash value via #eql?" do
+ x = mock('x')
+ x.stub!(:hash).and_return(42)
+
+ y = mock('y')
+ y.stub!(:hash).and_return(42)
+ y.should_receive(:eql?).and_return(false)
+
+ { x => nil }.send(@method, y).should == false
+ end
+end
diff --git a/spec/ruby/core/hash/shared/length.rb b/spec/ruby/core/hash/shared/length.rb
new file mode 100644
index 0000000000..24f5563759
--- /dev/null
+++ b/spec/ruby/core/hash/shared/length.rb
@@ -0,0 +1,12 @@
+describe :hash_length, shared: true do
+ it "returns the number of entries" do
+ { a: 1, b: 'c' }.send(@method).should == 2
+ h = { a: 1, b: 2 }
+ h[:a] = 2
+ h.send(@method).should == 2
+ { a: 1, b: 1, c: 1 }.send(@method).should == 3
+ {}.send(@method).should == 0
+ Hash.new(5).send(@method).should == 0
+ Hash.new { 5 }.send(@method).should == 0
+ end
+end
diff --git a/spec/ruby/core/hash/shared/less_than.rb b/spec/ruby/core/hash/shared/less_than.rb
new file mode 100644
index 0000000000..cdc6f14546
--- /dev/null
+++ b/spec/ruby/core/hash/shared/less_than.rb
@@ -0,0 +1,23 @@
+describe :hash_less_than, shared: true do
+ before do
+ @h1 = { a: 1, b: 2 }
+ @h2 = { a: 1, b: 2, c: 3 }
+ end
+
+ it "returns true if self is a subset of the other hash" do
+ @h1.send(@method, @h2).should be_true
+ end
+
+ it "returns false if self is not a subset of the other hash" do
+ @h2.send(@method, @h1).should be_false
+ end
+
+ it "converts the right operand to a hash before comparing" do
+ o = Object.new
+ def o.to_hash
+ { a: 1, b: 2, c: 3 }
+ end
+
+ @h1.send(@method, o).should be_true
+ end
+end
diff --git a/spec/ruby/core/hash/shared/replace.rb b/spec/ruby/core/hash/shared/replace.rb
new file mode 100644
index 0000000000..eb51130781
--- /dev/null
+++ b/spec/ruby/core/hash/shared/replace.rb
@@ -0,0 +1,51 @@
+describe :hash_replace, shared: true do
+ it "replaces the contents of self with other" do
+ h = { a: 1, b: 2 }
+ h.send(@method, c: -1, d: -2).should equal(h)
+ h.should == { c: -1, d: -2 }
+ end
+
+ it "tries to convert the passed argument to a hash using #to_hash" do
+ obj = mock('{1=>2,3=>4}')
+ obj.should_receive(:to_hash).and_return({ 1 => 2, 3 => 4 })
+
+ h = {}
+ h.send(@method, obj)
+ h.should == { 1 => 2, 3 => 4 }
+ end
+
+ it "calls to_hash on hash subclasses" do
+ h = {}
+ h.send(@method, HashSpecs::ToHashHash[1 => 2])
+ h.should == { 1 => 2 }
+ end
+
+ it "does not transfer default values" do
+ hash_a = {}
+ hash_b = Hash.new(5)
+ hash_a.send(@method, hash_b)
+ hash_a.default.should == 5
+
+ hash_a = {}
+ hash_b = Hash.new { |h, k| k * 2 }
+ hash_a.send(@method, hash_b)
+ hash_a.default(5).should == 10
+
+ hash_a = Hash.new { |h, k| k * 5 }
+ hash_b = Hash.new(lambda { raise "Should not invoke lambda" })
+ hash_a.send(@method, hash_b)
+ hash_a.default.should == hash_b.default
+ end
+
+ it "raises a #{frozen_error_class} if called on a frozen instance that would not be modified" do
+ lambda do
+ HashSpecs.frozen_hash.send(@method, HashSpecs.frozen_hash)
+ end.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} if called on a frozen instance that is modified" do
+ lambda do
+ HashSpecs.frozen_hash.send(@method, HashSpecs.empty_frozen_hash)
+ end.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/hash/shared/select.rb b/spec/ruby/core/hash/shared/select.rb
new file mode 100644
index 0000000000..cc1a54da25
--- /dev/null
+++ b/spec/ruby/core/hash/shared/select.rb
@@ -0,0 +1,91 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/iteration'
+require_relative '../../enumerable/shared/enumeratorized'
+
+describe :hash_select, shared: true do
+ before :each do
+ @hsh = { 1 => 2, 3 => 4, 5 => 6 }
+ @empty = {}
+ end
+
+ it "yields two arguments: key and value" do
+ all_args = []
+ { 1 => 2, 3 => 4 }.send(@method) { |*args| all_args << args }
+ all_args.sort.should == [[1, 2], [3, 4]]
+ end
+
+ it "returns a Hash of entries for which block is true" do
+ a_pairs = { 'a' => 9, 'c' => 4, 'b' => 5, 'd' => 2 }.send(@method) { |k,v| v % 2 == 0 }
+ a_pairs.should be_an_instance_of(Hash)
+ a_pairs.sort.should == [['c', 4], ['d', 2]]
+ end
+
+ it "processes entries with the same order as reject" do
+ h = { a: 9, c: 4, b: 5, d: 2 }
+
+ select_pairs = []
+ reject_pairs = []
+ h.dup.send(@method) { |*pair| select_pairs << pair }
+ h.reject { |*pair| reject_pairs << pair }
+
+ select_pairs.should == reject_pairs
+ end
+
+ it "returns an Enumerator when called on a non-empty hash without a block" do
+ @hsh.send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ it "returns an Enumerator when called on an empty hash without a block" do
+ @empty.send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ it_should_behave_like :hash_iteration_no_block
+
+ before :each do
+ @object = { 1 => 2, 3 => 4, 5 => 6 }
+ end
+ it_should_behave_like :enumeratorized_with_origin_size
+end
+
+describe :hash_select!, shared: true do
+ before :each do
+ @hsh = { 1 => 2, 3 => 4, 5 => 6 }
+ @empty = {}
+ end
+
+ it "is equivalent to keep_if if changes are made" do
+ h = { a: 2 }
+ h.send(@method) { |k,v| v <= 1 }.should equal h
+
+ h = { 1 => 2, 3 => 4 }
+ all_args_select = []
+ h.dup.send(@method) { |*args| all_args_select << args }
+ all_args_select.should == [[1, 2], [3, 4]]
+ end
+
+ it "removes all entries if the block is false" do
+ h = { a: 1, b: 2, c: 3 }
+ h.send(@method) { |k,v| false }.should equal(h)
+ h.should == {}
+ end
+
+ it "returns nil if no changes were made" do
+ { a: 1 }.send(@method) { |k,v| v <= 1 }.should == nil
+ end
+
+ it "raises a #{frozen_error_class} if called on an empty frozen instance" do
+ lambda { HashSpecs.empty_frozen_hash.send(@method) { false } }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} if called on a frozen instance that would not be modified" do
+ lambda { HashSpecs.frozen_hash.send(@method) { true } }.should raise_error(frozen_error_class)
+ end
+
+ it_should_behave_like :hash_iteration_no_block
+
+ before :each do
+ @object = { 1 => 2, 3 => 4, 5 => 6 }
+ end
+ it_should_behave_like :enumeratorized_with_origin_size
+end
diff --git a/spec/ruby/core/hash/shared/store.rb b/spec/ruby/core/hash/shared/store.rb
new file mode 100644
index 0000000000..de61ed1ad1
--- /dev/null
+++ b/spec/ruby/core/hash/shared/store.rb
@@ -0,0 +1,98 @@
+require_relative '../fixtures/classes'
+
+describe :hash_store, shared: true do
+ it "associates the key with the value and return the value" do
+ h = { a: 1 }
+ h.send(@method, :b, 2).should == 2
+ h.should == { b:2, a:1 }
+ end
+
+ it "duplicates string keys using dup semantics" do
+ # dup doesn't copy singleton methods
+ key = "foo"
+ def key.reverse() "bar" end
+ h = {}
+ h.send(@method, key, 0)
+ h.keys[0].reverse.should == "oof"
+ end
+
+ it "stores unequal keys that hash to the same value" do
+ h = {}
+ k1 = ["x"]
+ k2 = ["y"]
+ # So they end up in the same bucket
+ k1.should_receive(:hash).and_return(0)
+ k2.should_receive(:hash).and_return(0)
+
+ h.send(@method, k1, 1)
+ h.send(@method, k2, 2)
+ h.size.should == 2
+ end
+
+ it "accepts keys with private #hash method" do
+ key = HashSpecs::KeyWithPrivateHash.new
+ h = {}
+ h.send(@method, key, "foo")
+ h[key].should == "foo"
+ end
+
+ it " accepts keys with a Bignum hash" do
+ o = mock(hash: 1 << 100)
+ h = {}
+ h[o] = 1
+ h[o].should == 1
+ end
+
+ it "duplicates and freezes string keys" do
+ key = "foo"
+ h = {}
+ h.send(@method, key, 0)
+ key << "bar"
+
+ h.should == { "foo" => 0 }
+ h.keys[0].frozen?.should == true
+ end
+
+ it "doesn't duplicate and freeze already frozen string keys" do
+ key = "foo".freeze
+ h = {}
+ h.send(@method, key, 0)
+ h.keys[0].should equal(key)
+ end
+
+ it "keeps the existing key in the hash if there is a matching one" do
+ h = { "a" => 1, "b" => 2, "c" => 3, "d" => 4 }
+ key1 = HashSpecs::ByValueKey.new(13)
+ key2 = HashSpecs::ByValueKey.new(13)
+ h[key1] = 41
+ key_in_hash = h.keys.last
+ key_in_hash.should equal(key1)
+ h[key2] = 42
+ last_key = h.keys.last
+ last_key.should equal(key_in_hash)
+ last_key.should_not equal(key2)
+ end
+
+ it "keeps the existing String key in the hash if there is a matching one" do
+ h = { "a" => 1, "b" => 2, "c" => 3, "d" => 4 }
+ key1 = "foo"
+ key2 = "foo"
+ key1.should_not equal(key2)
+ h[key1] = 41
+ frozen_key = h.keys.last
+ frozen_key.should_not equal(key1)
+ h[key2] = 42
+ h.keys.last.should equal(frozen_key)
+ h.keys.last.should_not equal(key2)
+ end
+
+ it "raises a #{frozen_error_class} if called on a frozen instance" do
+ lambda { HashSpecs.frozen_hash.send(@method, 1, 2) }.should raise_error(frozen_error_class)
+ end
+
+ it "does not raise an exception if changing the value of an existing key during iteration" do
+ hash = {1 => 2, 3 => 4, 5 => 6}
+ hash.each { hash.send(@method, 1, :foo) }
+ hash.should == {1 => :foo, 3 => 4, 5 => 6}
+ end
+end
diff --git a/spec/ruby/core/hash/shared/to_s.rb b/spec/ruby/core/hash/shared/to_s.rb
new file mode 100644
index 0000000000..88333e0f42
--- /dev/null
+++ b/spec/ruby/core/hash/shared/to_s.rb
@@ -0,0 +1,96 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :hash_to_s, shared: true do
+
+ it "returns a string representation with same order as each()" do
+ h = { a: [1, 2], b: -2, d: -6, nil => nil }
+
+ pairs = []
+ h.each do |key, value|
+ pairs << key.inspect + '=>' + value.inspect
+ end
+
+ str = '{' + pairs.join(', ') + '}'
+ h.send(@method).should == str
+ end
+
+ it "calls #inspect on keys and values" do
+ key = mock('key')
+ val = mock('val')
+ key.should_receive(:inspect).and_return('key')
+ val.should_receive(:inspect).and_return('val')
+
+ { key => val }.send(@method).should == '{key=>val}'
+ end
+
+ it "does not call #to_s on a String returned from #inspect" do
+ str = "abc"
+ str.should_not_receive(:to_s)
+
+ { a: str }.send(@method).should == '{:a=>"abc"}'
+ end
+
+ it "calls #to_s on the object returned from #inspect if the Object isn't a String" do
+ obj = mock("Hash#inspect/to_s calls #to_s")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_receive(:to_s).and_return("abc")
+
+ { a: obj }.send(@method).should == "{:a=>abc}"
+ end
+
+ it "does not call #to_str on the object returned from #inspect when it is not a String" do
+ obj = mock("Hash#inspect/to_s does not call #to_str")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_not_receive(:to_str)
+
+ { a: obj }.send(@method).should =~ /^\{:a=>#<MockObject:0x[0-9a-f]+>\}$/
+ end
+
+ it "does not call #to_str on the object returned from #to_s when it is not a String" do
+ obj = mock("Hash#inspect/to_s does not call #to_str on #to_s result")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_receive(:to_s).and_return(obj)
+ obj.should_not_receive(:to_str)
+
+ { a: obj }.send(@method).should =~ /^\{:a=>#<MockObject:0x[0-9a-f]+>\}$/
+ end
+
+ it "does not swallow exceptions raised by #to_s" do
+ obj = mock("Hash#inspect/to_s does not swallow #to_s exceptions")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_receive(:to_s).and_raise(Exception)
+
+ lambda { { a: obj }.send(@method) }.should raise_error(Exception)
+ end
+
+ it "handles hashes with recursive values" do
+ x = {}
+ x[0] = x
+ x.send(@method).should == '{0=>{...}}'
+
+ x = {}
+ y = {}
+ x[0] = y
+ y[1] = x
+ x.send(@method).should == "{0=>{1=>{...}}}"
+ y.send(@method).should == "{1=>{0=>{...}}}"
+ end
+
+ it "returns a tainted string if self is tainted and not empty" do
+ {}.taint.send(@method).tainted?.should be_false
+ { nil => nil }.taint.send(@method).tainted?.should be_true
+ end
+
+ it "returns an untrusted string if self is untrusted and not empty" do
+ {}.untrust.send(@method).untrusted?.should be_false
+ { nil => nil }.untrust.send(@method).untrusted?.should be_true
+ end
+
+ it "does not raise if inspected result is not default external encoding" do
+ utf_16be = mock("utf_16be")
+ utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode!(Encoding::UTF_16BE))
+
+ {a: utf_16be}.send(@method).should == '{:a=>"utf_16be \u3042"}'
+ end
+end
diff --git a/spec/ruby/core/hash/shared/update.rb b/spec/ruby/core/hash/shared/update.rb
new file mode 100644
index 0000000000..be03ac019e
--- /dev/null
+++ b/spec/ruby/core/hash/shared/update.rb
@@ -0,0 +1,59 @@
+describe :hash_update, shared: true do
+ it "adds the entries from other, overwriting duplicate keys. Returns self" do
+ h = { _1: 'a', _2: '3' }
+ h.send(@method, _1: '9', _9: 2).should equal(h)
+ h.should == { _1: "9", _2: "3", _9: 2 }
+ end
+
+ it "sets any duplicate key to the value of block if passed a block" do
+ h1 = { a: 2, b: -1 }
+ h2 = { a: -2, c: 1 }
+ h1.send(@method, h2) { |k,x,y| 3.14 }.should equal(h1)
+ h1.should == { c: 1, b: -1, a: 3.14 }
+
+ h1.send(@method, h1) { nil }
+ h1.should == { a: nil, b: nil, c: nil }
+ end
+
+ it "tries to convert the passed argument to a hash using #to_hash" do
+ obj = mock('{1=>2}')
+ obj.should_receive(:to_hash).and_return({ 1 => 2 })
+ { 3 => 4 }.send(@method, obj).should == { 1 => 2, 3 => 4 }
+ end
+
+ it "does not call to_hash on hash subclasses" do
+ { 3 => 4 }.send(@method, HashSpecs::ToHashHash[1 => 2]).should == { 1 => 2, 3 => 4 }
+ end
+
+ it "processes entries with same order as merge()" do
+ h = { 1 => 2, 3 => 4, 5 => 6, "x" => nil, nil => 5, [] => [] }
+ merge_bang_pairs = []
+ merge_pairs = []
+ h.merge(h) { |*arg| merge_pairs << arg }
+ h.send(@method, h) { |*arg| merge_bang_pairs << arg }
+ merge_bang_pairs.should == merge_pairs
+ end
+
+ it "raises a #{frozen_error_class} on a frozen instance that is modified" do
+ lambda do
+ HashSpecs.frozen_hash.send(@method, 1 => 2)
+ end.should raise_error(frozen_error_class)
+ end
+
+ it "checks frozen status before coercing an object with #to_hash" do
+ obj = mock("to_hash frozen")
+ # This is necessary because mock cleanup code cannot run on the frozen
+ # object.
+ def obj.to_hash() raise Exception, "should not receive #to_hash" end
+ obj.freeze
+
+ lambda { HashSpecs.frozen_hash.send(@method, obj) }.should raise_error(frozen_error_class)
+ end
+
+ # see redmine #1571
+ it "raises a #{frozen_error_class} on a frozen instance that would not be modified" do
+ lambda do
+ HashSpecs.frozen_hash.send(@method, HashSpecs.empty_frozen_hash)
+ end.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/hash/shared/value.rb b/spec/ruby/core/hash/shared/value.rb
new file mode 100644
index 0000000000..aac76c253e
--- /dev/null
+++ b/spec/ruby/core/hash/shared/value.rb
@@ -0,0 +1,14 @@
+describe :hash_value_p, shared: true do
+ it "returns true if the value exists in the hash" do
+ { a: :b }.send(@method, :a).should == false
+ { 1 => 2 }.send(@method, 2).should == true
+ h = Hash.new(5)
+ h.send(@method, 5).should == false
+ h = Hash.new { 5 }
+ h.send(@method, 5).should == false
+ end
+
+ it "uses == semantics for comparing values" do
+ { 5 => 2.0 }.send(@method, 2).should == true
+ end
+end
diff --git a/spec/ruby/core/hash/shared/values_at.rb b/spec/ruby/core/hash/shared/values_at.rb
new file mode 100644
index 0000000000..ef3b0e8ba0
--- /dev/null
+++ b/spec/ruby/core/hash/shared/values_at.rb
@@ -0,0 +1,9 @@
+describe :hash_values_at, shared: true do
+ it "returns an array of values for the given keys" do
+ h = { a: 9, b: 'a', c: -10, d: nil }
+ h.send(@method).should be_kind_of(Array)
+ h.send(@method).should == []
+ h.send(@method, :a, :d, :b).should be_kind_of(Array)
+ h.send(@method, :a, :d, :b).should == [9, nil, 'a']
+ end
+end
diff --git a/spec/ruby/core/hash/shift_spec.rb b/spec/ruby/core/hash/shift_spec.rb
new file mode 100644
index 0000000000..8cf3f4025a
--- /dev/null
+++ b/spec/ruby/core/hash/shift_spec.rb
@@ -0,0 +1,79 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#shift" do
+ it "removes a pair from hash and return it" do
+ h = { a: 1, b: 2, "c" => 3, nil => 4, [] => 5 }
+ h2 = h.dup
+
+ h.size.times do |i|
+ r = h.shift
+ r.should be_kind_of(Array)
+ h2[r.first].should == r.last
+ h.size.should == h2.size - i - 1
+ end
+
+ h.should == {}
+ end
+
+ # MRI explicitly implements this behavior
+ it "allows shifting entries while iterating" do
+ h = { a: 1, b: 2, c: 3 }
+ visited = []
+ shifted = []
+ h.each_pair { |k,v|
+ visited << k
+ shifted << h.shift
+ }
+ visited.should == [:a, :b, :c]
+ shifted.should == [[:a, 1], [:b, 2], [:c, 3]]
+ h.should == {}
+ end
+
+ it "calls #default with nil if the Hash is empty" do
+ h = {}
+ def h.default(key)
+ key.should == nil
+ :foo
+ end
+ h.shift.should == :foo
+ end
+
+ it "returns nil from an empty hash" do
+ {}.shift.should == nil
+ end
+
+ it "returns (computed) default for empty hashes" do
+ Hash.new(5).shift.should == 5
+ h = Hash.new { |*args| args }
+ h.shift.should == [h, nil]
+ end
+
+ it "preserves Hash invariants when removing the last item" do
+ h = { :a => 1, :b => 2 }
+ h.shift.should == [:a, 1]
+ h.shift.should == [:b, 2]
+ h[:c] = 3
+ h.should == {:c => 3}
+ end
+
+ it "raises a #{frozen_error_class} if called on a frozen instance" do
+ lambda { HashSpecs.frozen_hash.shift }.should raise_error(frozen_error_class)
+ lambda { HashSpecs.empty_frozen_hash.shift }.should raise_error(frozen_error_class)
+ end
+
+ it "works when the hash is at capacity" do
+ # We try a wide range of sizes in hopes that this will cover all implementationss base Hash size.
+ results = []
+ 1.upto(100) do |n|
+ h = {}
+ n.times do |i|
+ h[i] = i
+ end
+ h.shift
+ results << h.size
+ end
+
+ results.should == 0.upto(99).to_a
+ end
+end
diff --git a/spec/ruby/core/hash/size_spec.rb b/spec/ruby/core/hash/size_spec.rb
new file mode 100644
index 0000000000..1e8abd8d97
--- /dev/null
+++ b/spec/ruby/core/hash/size_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/length'
+
+describe "Hash#size" do
+ it_behaves_like :hash_length, :size
+end
diff --git a/spec/ruby/core/hash/slice_spec.rb b/spec/ruby/core/hash/slice_spec.rb
new file mode 100644
index 0000000000..f7717c9404
--- /dev/null
+++ b/spec/ruby/core/hash/slice_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.5" do
+ describe "Hash#slice" do
+ before :each do
+ @hash = { a: 1, b: 2, c: 3 }
+ end
+
+ it "returns a new empty hash without arguments" do
+ ret = @hash.slice
+ ret.should_not equal(@hash)
+ ret.should be_an_instance_of(Hash)
+ ret.should == {}
+ end
+
+ it "returns the requested subset" do
+ @hash.slice(:c, :a).should == { c: 3, a: 1 }
+ end
+
+ it "returns a hash ordered in the order of the requested keys" do
+ @hash.slice(:c, :a).keys.should == [:c, :a]
+ end
+
+ it "returns only the keys of the original hash" do
+ @hash.slice(:a, :chunky_bacon).should == { a: 1 }
+ end
+
+ it "returns a Hash instance, even on subclasses" do
+ klass = Class.new(Hash)
+ h = klass.new
+ h[:bar] = 12
+ h[:foo] = 42
+ r = h.slice(:foo)
+ r.should == {foo: 42}
+ r.class.should == Hash
+ end
+
+ it "uses the regular Hash#[] method, even on subclasses that override it" do
+ ScratchPad.record []
+ klass = Class.new(Hash) do
+ def [](value)
+ ScratchPad << :used_subclassed_operator
+ super
+ end
+ end
+
+ h = klass.new
+ h[:bar] = 12
+ h[:foo] = 42
+ h.slice(:foo)
+
+ ScratchPad.recorded.should == []
+ end
+ end
+end
diff --git a/spec/ruby/core/hash/sort_spec.rb b/spec/ruby/core/hash/sort_spec.rb
new file mode 100644
index 0000000000..26058c845e
--- /dev/null
+++ b/spec/ruby/core/hash/sort_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#sort" do
+ it "converts self to a nested array of [key, value] arrays and sort with Array#sort" do
+ { 'a' => 'b', '1' => '2', 'b' => 'a' }.sort.should ==
+ [["1", "2"], ["a", "b"], ["b", "a"]]
+ end
+
+ it "works when some of the keys are themselves arrays" do
+ { [1,2] => 5, [1,1] => 5 }.sort.should == [[[1,1],5], [[1,2],5]]
+ end
+
+ it "uses block to sort array if passed a block" do
+ { 1 => 2, 2 => 9, 3 => 4 }.sort { |a,b| b <=> a }.should == [[3, 4], [2, 9], [1, 2]]
+ end
+end
diff --git a/spec/ruby/core/hash/store_spec.rb b/spec/ruby/core/hash/store_spec.rb
new file mode 100644
index 0000000000..7e975380ec
--- /dev/null
+++ b/spec/ruby/core/hash/store_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/store'
+
+describe "Hash#store" do
+ it_behaves_like :hash_store, :store
+end
diff --git a/spec/ruby/core/hash/to_a_spec.rb b/spec/ruby/core/hash/to_a_spec.rb
new file mode 100644
index 0000000000..33ad7cdec9
--- /dev/null
+++ b/spec/ruby/core/hash/to_a_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#to_a" do
+ it "returns a list of [key, value] pairs with same order as each()" do
+ h = { a: 1, 1 => :a, 3 => :b, b: 5 }
+ pairs = []
+
+ h.each_pair do |key, value|
+ pairs << [key, value]
+ end
+
+ h.to_a.should be_kind_of(Array)
+ h.to_a.should == pairs
+ end
+
+ it "is called for Enumerable#entries" do
+ h = { a: 1, 1 => :a, 3 => :b, b: 5 }
+ pairs = []
+
+ h.each_pair do |key, value|
+ pairs << [key, value]
+ end
+
+ ent = h.entries
+ ent.should be_kind_of(Array)
+ ent.should == pairs
+ end
+
+ it "returns a tainted array if self is tainted" do
+ {}.taint.to_a.tainted?.should be_true
+ end
+
+ it "returns an untrusted array if self is untrusted" do
+ {}.untrust.to_a.untrusted?.should be_true
+ end
+end
diff --git a/spec/ruby/core/hash/to_h_spec.rb b/spec/ruby/core/hash/to_h_spec.rb
new file mode 100644
index 0000000000..40bd0d06d2
--- /dev/null
+++ b/spec/ruby/core/hash/to_h_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#to_h" do
+ it "returns self for Hash instances" do
+ h = {}
+ h.to_h.should equal(h)
+ end
+
+ ruby_version_is "2.6" do
+ it "converts [key, value] pairs returned by the block to a hash" do
+ {a: 1, b: 2}.to_h {|k, v| [k.to_s, v*v]}.should == { "a" => 1, "b" => 4 }
+ end
+ end
+
+ describe "when called on a subclass of Hash" do
+ before :each do
+ @h = HashSpecs::MyHash.new
+ @h[:foo] = :bar
+ end
+
+ it "returns a new Hash instance" do
+ @h.to_h.should be_an_instance_of(Hash)
+ @h.to_h.should == @h
+ @h[:foo].should == :bar
+ end
+
+ it "copies the default" do
+ @h.default = 42
+ @h.to_h.default.should == 42
+ @h[:hello].should == 42
+ end
+
+ it "copies the default_proc" do
+ @h.default_proc = prc = Proc.new{ |h, k| h[k] = 2 * k }
+ @h.to_h.default_proc.should == prc
+ @h[42].should == 84
+ end
+ end
+end
diff --git a/spec/ruby/core/hash/to_hash_spec.rb b/spec/ruby/core/hash/to_hash_spec.rb
new file mode 100644
index 0000000000..f479fa1fb2
--- /dev/null
+++ b/spec/ruby/core/hash/to_hash_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#to_hash" do
+ it "returns self for Hash instances" do
+ h = {}
+ h.to_hash.should equal(h)
+ end
+
+ it "returns self for instances of subclasses of Hash" do
+ h = HashSpecs::MyHash.new
+ h.to_hash.should equal(h)
+ end
+end
diff --git a/spec/ruby/core/hash/to_proc_spec.rb b/spec/ruby/core/hash/to_proc_spec.rb
new file mode 100644
index 0000000000..ca55604043
--- /dev/null
+++ b/spec/ruby/core/hash/to_proc_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#to_proc" do
+ before :each do
+ @key = Object.new
+ @value = Object.new
+ @hash = { @key => @value }
+ @default = Object.new
+ @unstored = Object.new
+ end
+
+ it "returns an instance of Proc" do
+ @hash.to_proc.should be_an_instance_of Proc
+ end
+
+ describe "the returned proc" do
+ before :each do
+ @proc = @hash.to_proc
+ end
+
+ it "is not a lambda" do
+ @proc.lambda?.should == false
+ end
+
+ it "raises ArgumentError if not passed exactly one argument" do
+ lambda {
+ @proc.call
+ }.should raise_error(ArgumentError)
+
+ lambda {
+ @proc.call 1, 2
+ }.should raise_error(ArgumentError)
+ end
+
+ context "with a stored key" do
+ it "returns the paired value" do
+ @proc.call(@key).should equal(@value)
+ end
+ end
+
+ context "passed as a block" do
+ it "retrieves the hash's values" do
+ [@key].map(&@proc)[0].should equal(@value)
+ end
+
+ context "to instance_exec" do
+ it "always retrieves the original hash's values" do
+ hash = {foo: 1, bar: 2}
+ proc = hash.to_proc
+
+ hash.instance_exec(:foo, &proc).should == 1
+
+ hash2 = {quux: 1}
+ hash2.instance_exec(:foo, &proc).should == 1
+ end
+ end
+ end
+
+ context "with no stored key" do
+ it "returns nil" do
+ @proc.call(@unstored).should be_nil
+ end
+
+ context "when the hash has a default value" do
+ before :each do
+ @hash.default = @default
+ end
+
+ it "returns the default value" do
+ @proc.call(@unstored).should equal(@default)
+ end
+ end
+
+ context "when the hash has a default proc" do
+ it "returns an evaluated value from the default proc" do
+ @hash.default_proc = -> hash, called_with { [hash.keys, called_with] }
+ @proc.call(@unstored).should == [[@key], @unstored]
+ end
+ end
+ end
+
+ it "raises an ArgumentError when calling #call on the Proc with no arguments" do
+ lambda { @hash.to_proc.call }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/hash/to_s_spec.rb b/spec/ruby/core/hash/to_s_spec.rb
new file mode 100644
index 0000000000..e52b09962e
--- /dev/null
+++ b/spec/ruby/core/hash/to_s_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/to_s'
+
+describe "Hash#to_s" do
+ it_behaves_like :hash_to_s, :to_s
+end
diff --git a/spec/ruby/core/hash/transform_keys_spec.rb b/spec/ruby/core/hash/transform_keys_spec.rb
new file mode 100644
index 0000000000..32ac89b765
--- /dev/null
+++ b/spec/ruby/core/hash/transform_keys_spec.rb
@@ -0,0 +1,132 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.5" do
+ describe "Hash#transform_keys" do
+ before :each do
+ @hash = { a: 1, b: 2, c: 3 }
+ end
+
+ it "returns new hash" do
+ ret = @hash.transform_keys(&:succ)
+ ret.should_not equal(@hash)
+ ret.should be_an_instance_of(Hash)
+ end
+
+ it "sets the result as transformed keys with the given block" do
+ @hash.transform_keys(&:succ).should == { b: 1, c: 2, d: 3 }
+ end
+
+ it "keeps last pair if new keys conflict" do
+ @hash.transform_keys { |_| :a }.should == { a: 3 }
+ end
+
+ it "makes both hashes to share values" do
+ value = [1, 2, 3]
+ new_hash = { a: value }.transform_keys(&:upcase)
+ new_hash[:A].should equal(value)
+ end
+
+ context "when no block is given" do
+ it "returns a sized Enumerator" do
+ enumerator = @hash.transform_keys
+ enumerator.should be_an_instance_of(Enumerator)
+ enumerator.size.should == @hash.size
+ enumerator.each(&:succ).should == { b: 1, c: 2, d: 3 }
+ end
+ end
+
+ it "returns a Hash instance, even on subclasses" do
+ klass = Class.new(Hash)
+ h = klass.new
+ h[:foo] = 42
+ r = h.transform_keys{|v| :"x#{v}"}
+ r.keys.should == [:xfoo]
+ r.class.should == Hash
+ end
+ end
+
+ describe "Hash#transform_keys!" do
+ before :each do
+ @hash = { a: 1, b: 2, c: 3, d: 4 }
+ @initial_pairs = @hash.dup
+ end
+
+ it "returns self" do
+ @hash.transform_keys!(&:succ).should equal(@hash)
+ end
+
+ it "updates self as transformed values with the given block" do
+ @hash.transform_keys!(&:to_s)
+ @hash.should == { 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4 }
+ end
+
+ # https://bugs.ruby-lang.org/issues/14380
+ ruby_version_is ""..."2.5.1" do
+ it "does not prevent conflicts between new keys and old ones" do
+ @hash.transform_keys!(&:succ)
+ @hash.should == { e: 1 }
+ end
+ end
+
+ ruby_version_is "2.5.1" do
+ it "prevents conflicts between new keys and old ones" do
+ @hash.transform_keys!(&:succ)
+ @hash.should == { b: 1, c: 2, d: 3, e: 4 }
+ end
+ end
+
+ ruby_version_is ""..."2.5.1" do
+ it "partially modifies the contents if we broke from the block" do
+ @hash.transform_keys! do |v|
+ break if v == :c
+ v.succ
+ end
+ @hash.should == { c: 1, d: 4 }
+ end
+ end
+
+ ruby_version_is "2.5.1" do
+ it "returns the processed keys if we broke from the block" do
+ @hash.transform_keys! do |v|
+ break if v == :c
+ v.succ
+ end
+ @hash.should == { b: 1, c: 2 }
+ end
+ end
+
+ it "keeps later pair if new keys conflict" do
+ @hash.transform_keys! { |_| :a }.should == { a: 4 }
+ end
+
+ context "when no block is given" do
+ it "returns a sized Enumerator" do
+ enumerator = @hash.transform_keys!
+ enumerator.should be_an_instance_of(Enumerator)
+ enumerator.size.should == @hash.size
+ enumerator.each(&:upcase).should == { A: 1, B: 2, C: 3, D: 4 }
+ end
+ end
+
+ describe "on frozen instance" do
+ before :each do
+ @hash.freeze
+ end
+
+ it "raises a #{frozen_error_class} on an empty hash" do
+ ->{ {}.freeze.transform_keys!(&:upcase) }.should raise_error(frozen_error_class)
+ end
+
+ it "keeps pairs and raises a #{frozen_error_class}" do
+ ->{ @hash.transform_keys!(&:upcase) }.should raise_error(frozen_error_class)
+ @hash.should == @initial_pairs
+ end
+
+ context "when no block is given" do
+ it "does not raise an exception" do
+ @hash.transform_keys!.should be_an_instance_of(Enumerator)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/hash/transform_values_spec.rb b/spec/ruby/core/hash/transform_values_spec.rb
new file mode 100644
index 0000000000..80e875097a
--- /dev/null
+++ b/spec/ruby/core/hash/transform_values_spec.rb
@@ -0,0 +1,99 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.4" do
+ describe "Hash#transform_values" do
+ before :each do
+ @hash = { a: 1, b: 2, c: 3 }
+ end
+
+ it "returns new hash" do
+ ret = @hash.transform_values(&:succ)
+ ret.should_not equal(@hash)
+ ret.should be_an_instance_of(Hash)
+ end
+
+ it "sets the result as transformed values with the given block" do
+ @hash.transform_values(&:succ).should == { a: 2, b: 3, c: 4 }
+ end
+
+ it "makes both hashes to share keys" do
+ key = [1, 2, 3]
+ new_hash = { key => 1 }.transform_values(&:succ)
+ new_hash[key].should == 2
+ new_hash.keys[0].should equal(key)
+ end
+
+ context "when no block is given" do
+ it "returns a sized Enumerator" do
+ enumerator = @hash.transform_values
+ enumerator.should be_an_instance_of(Enumerator)
+ enumerator.size.should == @hash.size
+ enumerator.each(&:succ).should == { a: 2, b: 3, c: 4 }
+ end
+ end
+
+ it "returns a Hash instance, even on subclasses" do
+ klass = Class.new(Hash)
+ h = klass.new
+ h[:foo] = 42
+ r = h.transform_values{|v| 2 * v}
+ r[:foo].should == 84
+ r.class.should == Hash
+ end
+ end
+
+ describe "Hash#transform_values!" do
+ before :each do
+ @hash = { a: 1, b: 2, c: 3 }
+ @initial_pairs = @hash.dup
+ end
+
+ it "returns self" do
+ @hash.transform_values!(&:succ).should equal(@hash)
+ end
+
+ it "updates self as transformed values with the given block" do
+ @hash.transform_values!(&:succ)
+ @hash.should == { a: 2, b: 3, c: 4 }
+ end
+
+ it "partially modifies the contents if we broke from the block" do
+ @hash.transform_values! do |v|
+ break if v == 3
+ 100 + v
+ end
+ @hash.should == { a: 101, b: 102, c: 3}
+ end
+
+ context "when no block is given" do
+ it "returns a sized Enumerator" do
+ enumerator = @hash.transform_values!
+ enumerator.should be_an_instance_of(Enumerator)
+ enumerator.size.should == @hash.size
+ enumerator.each(&:succ)
+ @hash.should == { a: 2, b: 3, c: 4 }
+ end
+ end
+
+ describe "on frozen instance" do
+ before :each do
+ @hash.freeze
+ end
+
+ it "raises a #{frozen_error_class} on an empty hash" do
+ ->{ {}.freeze.transform_values!(&:succ) }.should raise_error(frozen_error_class)
+ end
+
+ it "keeps pairs and raises a #{frozen_error_class}" do
+ ->{ @hash.transform_values!(&:succ) }.should raise_error(frozen_error_class)
+ @hash.should == @initial_pairs
+ end
+
+ context "when no block is given" do
+ it "does not raise an exception" do
+ @hash.transform_values!.should be_an_instance_of(Enumerator)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/hash/try_convert_spec.rb b/spec/ruby/core/hash/try_convert_spec.rb
new file mode 100644
index 0000000000..50bb59816c
--- /dev/null
+++ b/spec/ruby/core/hash/try_convert_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash.try_convert" do
+ it "returns the argument if it's a Hash" do
+ x = Hash.new
+ Hash.try_convert(x).should equal(x)
+ end
+
+ it "returns the argument if it's a kind of Hash" do
+ x = HashSpecs::MyHash.new
+ Hash.try_convert(x).should equal(x)
+ end
+
+ it "returns nil when the argument does not respond to #to_hash" do
+ Hash.try_convert(Object.new).should be_nil
+ end
+
+ it "sends #to_hash to the argument and returns the result if it's nil" do
+ obj = mock("to_hash")
+ obj.should_receive(:to_hash).and_return(nil)
+ Hash.try_convert(obj).should be_nil
+ end
+
+ it "sends #to_hash to the argument and returns the result if it's a Hash" do
+ x = Hash.new
+ obj = mock("to_hash")
+ obj.should_receive(:to_hash).and_return(x)
+ Hash.try_convert(obj).should equal(x)
+ end
+
+ it "sends #to_hash to the argument and returns the result if it's a kind of Hash" do
+ x = HashSpecs::MyHash.new
+ obj = mock("to_hash")
+ obj.should_receive(:to_hash).and_return(x)
+ Hash.try_convert(obj).should equal(x)
+ end
+
+ it "sends #to_hash to the argument and raises TypeError if it's not a kind of Hash" do
+ obj = mock("to_hash")
+ obj.should_receive(:to_hash).and_return(Object.new)
+ lambda { Hash.try_convert obj }.should raise_error(TypeError)
+ end
+
+ it "does not rescue exceptions raised by #to_hash" do
+ obj = mock("to_hash")
+ obj.should_receive(:to_hash).and_raise(RuntimeError)
+ lambda { Hash.try_convert obj }.should raise_error(RuntimeError)
+ end
+end
diff --git a/spec/ruby/core/hash/update_spec.rb b/spec/ruby/core/hash/update_spec.rb
new file mode 100644
index 0000000000..0975045ad1
--- /dev/null
+++ b/spec/ruby/core/hash/update_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/update'
+
+describe "Hash#update" do
+ it_behaves_like :hash_update, :update
+end
diff --git a/spec/ruby/core/hash/value_spec.rb b/spec/ruby/core/hash/value_spec.rb
new file mode 100644
index 0000000000..0ab16a5d1b
--- /dev/null
+++ b/spec/ruby/core/hash/value_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/value'
+
+describe "Hash#value?" do
+ it_behaves_like :hash_value_p, :value?
+end
diff --git a/spec/ruby/core/hash/values_at_spec.rb b/spec/ruby/core/hash/values_at_spec.rb
new file mode 100644
index 0000000000..b620a279ba
--- /dev/null
+++ b/spec/ruby/core/hash/values_at_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/values_at'
+
+describe "Hash#values_at" do
+ it_behaves_like :hash_values_at, :values_at
+end
diff --git a/spec/ruby/core/hash/values_spec.rb b/spec/ruby/core/hash/values_spec.rb
new file mode 100644
index 0000000000..9f2a481a48
--- /dev/null
+++ b/spec/ruby/core/hash/values_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash#values" do
+ it "returns an array of values" do
+ h = { 1 => :a, 'a' => :a, 'the' => 'lang' }
+ h.values.should be_kind_of(Array)
+ h.values.sort {|a, b| a.to_s <=> b.to_s}.should == [:a, :a, 'lang']
+ end
+end
diff --git a/spec/ruby/core/integer/abs_spec.rb b/spec/ruby/core/integer/abs_spec.rb
new file mode 100644
index 0000000000..c40356db12
--- /dev/null
+++ b/spec/ruby/core/integer/abs_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/abs'
+
+describe "Integer#abs" do
+ it_behaves_like :integer_abs, :abs
+end
diff --git a/spec/ruby/core/integer/allbits_spec.rb b/spec/ruby/core/integer/allbits_spec.rb
new file mode 100644
index 0000000000..d7bd58d638
--- /dev/null
+++ b/spec/ruby/core/integer/allbits_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '2.5' do
+ describe "Integer#allbits?" do
+ it "returns true iff all the bits of the argument are set in the receiver" do
+ 42.allbits?(42).should == true
+ 0b1010_1010.allbits?(0b1000_0010).should == true
+ 0b1010_1010.allbits?(0b1000_0001).should == false
+ 0b1000_0010.allbits?(0b1010_1010).should == false
+ (0b1010_1010 | bignum_value).allbits?(0b1000_0010 | bignum_value).should == true
+ (0b1010_1010 | bignum_value).allbits?(0b1000_0001 | bignum_value).should == false
+ (0b1000_0010 | bignum_value).allbits?(0b1010_1010 | bignum_value).should == false
+ end
+
+ it "handles negative values using two's complement notation" do
+ (~0b1).allbits?(42).should == true
+ (-42).allbits?(-42).should == true
+ (~0b1010_1010).allbits?(~0b1110_1011).should == true
+ (~0b1010_1010).allbits?(~0b1000_0010).should == false
+ (~(0b1010_1010 | bignum_value)).allbits?(~(0b1110_1011 | bignum_value)).should == true
+ (~(0b1010_1010 | bignum_value)).allbits?(~(0b1000_0010 | bignum_value)).should == false
+ end
+
+ it "coerces the rhs using to_int" do
+ obj = mock("the int 0b10")
+ obj.should_receive(:to_int).and_return(0b10)
+ 0b110.allbits?(obj).should == true
+ end
+
+ it "raises a TypeError when given a non-Integer" do
+ lambda {
+ (obj = mock('10')).should_receive(:coerce).any_number_of_times.and_return([42,10])
+ 13.allbits?(obj)
+ }.should raise_error(TypeError)
+ lambda { 13.allbits?("10") }.should raise_error(TypeError)
+ lambda { 13.allbits?(:symbol) }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/anybits_spec.rb b/spec/ruby/core/integer/anybits_spec.rb
new file mode 100644
index 0000000000..36803694aa
--- /dev/null
+++ b/spec/ruby/core/integer/anybits_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '2.5' do
+ describe "Integer#anybits?" do
+ it "returns true iff all the bits of the argument are set in the receiver" do
+ 42.anybits?(42).should == true
+ 0b1010_1010.anybits?(0b1000_0010).should == true
+ 0b1010_1010.anybits?(0b1000_0001).should == true
+ 0b1000_0010.anybits?(0b0010_1100).should == false
+ different_bignum = (2 * bignum_value) & (~bignum_value)
+ (0b1010_1010 | different_bignum).anybits?(0b1000_0010 | bignum_value).should == true
+ (0b1010_1010 | different_bignum).anybits?(0b0010_1100 | bignum_value).should == true
+ (0b1000_0010 | different_bignum).anybits?(0b0010_1100 | bignum_value).should == false
+ end
+
+ it "handles negative values using two's complement notation" do
+ (~42).anybits?(42).should == false
+ (-42).anybits?(-42).should == true
+ (~0b100).anybits?(~0b1).should == true
+ (~(0b100 | bignum_value)).anybits?(~(0b1 | bignum_value)).should == true
+ end
+
+ it "coerces the rhs using to_int" do
+ obj = mock("the int 0b10")
+ obj.should_receive(:to_int).and_return(0b10)
+ 0b110.anybits?(obj).should == true
+ end
+
+ it "raises a TypeError when given a non-Integer" do
+ lambda {
+ (obj = mock('10')).should_receive(:coerce).any_number_of_times.and_return([42,10])
+ 13.anybits?(obj)
+ }.should raise_error(TypeError)
+ lambda { 13.anybits?("10") }.should raise_error(TypeError)
+ lambda { 13.anybits?(:symbol) }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/bit_and_spec.rb b/spec/ruby/core/integer/bit_and_spec.rb
new file mode 100644
index 0000000000..8565f82397
--- /dev/null
+++ b/spec/ruby/core/integer/bit_and_spec.rb
@@ -0,0 +1,97 @@
+require_relative '../../spec_helper'
+
+describe "Integer#&" do
+ context "fixnum" do
+ it "returns self bitwise AND other" do
+ (256 & 16).should == 0
+ (2010 & 5).should == 0
+ (65535 & 1).should == 1
+ (0xffff & bignum_value + 0xffff_ffff).should == 65535
+ end
+
+ it "returns self bitwise AND other when one operand is negative" do
+ ((1 << 33) & -1).should == (1 << 33)
+ (-1 & (1 << 33)).should == (1 << 33)
+
+ ((-(1<<33)-1) & 5).should == 5
+ (5 & (-(1<<33)-1)).should == 5
+ end
+
+ it "returns self bitwise AND other when both operands are negative" do
+ (-5 & -1).should == -5
+ (-3 & -4).should == -4
+ (-12 & -13).should == -16
+ (-13 & -12).should == -16
+ end
+
+ it "returns self bitwise AND a bignum" do
+ (-1 & 2**64).should == 18446744073709551616
+ end
+
+ it "coerces the rhs and calls #coerce" do
+ obj = mock("fixnum bit and")
+ obj.should_receive(:coerce).with(6).and_return([3, 6])
+ (6 & obj).should == 2
+ end
+
+ it "raises a TypeError when passed a Float" do
+ lambda { (3 & 3.4) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError and does not call #to_int when defined on an object" do
+ obj = mock("fixnum bit and")
+ obj.should_not_receive(:to_int)
+
+ lambda { 3 & obj }.should raise_error(TypeError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value(5)
+ end
+
+ it "returns self bitwise AND other" do
+ @bignum = bignum_value(5)
+ (@bignum & 3).should == 1
+ (@bignum & 52).should == 4
+ (@bignum & bignum_value(9921)).should == 9223372036854775809
+
+ ((2*bignum_value) & 1).should == 0
+ ((2*bignum_value) & (2*bignum_value)).should == 18446744073709551616
+ end
+
+ it "returns self bitwise AND other when one operand is negative" do
+ ((2*bignum_value) & -1).should == (2*bignum_value)
+ ((4*bignum_value) & -1).should == (4*bignum_value)
+ (@bignum & -0xffffffffffffff5).should == 9223372036854775809
+ (@bignum & -@bignum).should == 1
+ (@bignum & -0x8000000000000000).should == 9223372036854775808
+ end
+
+ it "returns self bitwise AND other when both operands are negative" do
+ (-@bignum & -0x4000000000000005).should == -13835058055282163717
+ (-@bignum & -@bignum).should == -9223372036854775813
+ (-@bignum & -0x4000000000000000).should == -13835058055282163712
+ end
+
+ it "returns self bitwise AND other when both are negative and a multiple in bitsize of Fixnum::MIN" do
+ val = - ((1 << 93) - 1)
+ (val & val).should == val
+
+ val = - ((1 << 126) - 1)
+ (val & val).should == val
+ end
+
+ it "raises a TypeError when passed a Float" do
+ lambda { (@bignum & 3.4) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError and does not call #to_int when defined on an object" do
+ obj = mock("bignum bit and")
+ obj.should_not_receive(:to_int)
+
+ lambda { @bignum & obj }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/bit_length_spec.rb b/spec/ruby/core/integer/bit_length_spec.rb
new file mode 100644
index 0000000000..827007b7e0
--- /dev/null
+++ b/spec/ruby/core/integer/bit_length_spec.rb
@@ -0,0 +1,76 @@
+require_relative '../../spec_helper'
+
+describe "Integer#bit_length" do
+ context "fixnum" do
+ it "returns the position of the leftmost bit of a positive number" do
+ 0.bit_length.should == 0
+ 1.bit_length.should == 1
+ 2.bit_length.should == 2
+ 3.bit_length.should == 2
+ 4.bit_length.should == 3
+ n = fixnum_max.bit_length
+ fixnum_max[n].should == 0
+ fixnum_max[n - 1].should == 1
+
+ 0.bit_length.should == 0
+ 1.bit_length.should == 1
+ 0xff.bit_length.should == 8
+ 0x100.bit_length.should == 9
+ (2**12 - 1).bit_length.should == 12
+ (2**12).bit_length.should == 13
+ (2**12 + 1).bit_length.should == 13
+ end
+
+ it "returns the position of the leftmost 0 bit of a negative number" do
+ -1.bit_length.should == 0
+ -2.bit_length.should == 1
+ -3.bit_length.should == 2
+ -4.bit_length.should == 2
+ -5.bit_length.should == 3
+ n = fixnum_min.bit_length
+ fixnum_min[n].should == 1
+ fixnum_min[n - 1].should == 0
+
+ (-2**12 - 1).bit_length.should == 13
+ (-2**12).bit_length.should == 12
+ (-2**12 + 1).bit_length.should == 12
+ -0x101.bit_length.should == 9
+ -0x100.bit_length.should == 8
+ -0xff.bit_length.should == 8
+ -2.bit_length.should == 1
+ -1.bit_length.should == 0
+ end
+ end
+
+ context "bignum" do
+ it "returns the position of the leftmost bit of a positive number" do
+ (2**1000-1).bit_length.should == 1000
+ (2**1000).bit_length.should == 1001
+ (2**1000+1).bit_length.should == 1001
+
+ (2**10000-1).bit_length.should == 10000
+ (2**10000).bit_length.should == 10001
+ (2**10000+1).bit_length.should == 10001
+
+ (1 << 100).bit_length.should == 101
+ (1 << 100).succ.bit_length.should == 101
+ (1 << 100).pred.bit_length.should == 100
+ (1 << 10000).bit_length.should == 10001
+ end
+
+ it "returns the position of the leftmost 0 bit of a negative number" do
+ (-2**10000-1).bit_length.should == 10001
+ (-2**10000).bit_length.should == 10000
+ (-2**10000+1).bit_length.should == 10000
+
+ (-2**1000-1).bit_length.should == 1001
+ (-2**1000).bit_length.should == 1000
+ (-2**1000+1).bit_length.should == 1000
+
+ ((-1 << 100)-1).bit_length.should == 101
+ ((-1 << 100)-1).succ.bit_length.should == 100
+ ((-1 << 100)-1).pred.bit_length.should == 101
+ ((-1 << 10000)-1).bit_length.should == 10001
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/bit_or_spec.rb b/spec/ruby/core/integer/bit_or_spec.rb
new file mode 100644
index 0000000000..16ab430f8b
--- /dev/null
+++ b/spec/ruby/core/integer/bit_or_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../spec_helper'
+
+describe "Integer#|" do
+ context "fixnum" do
+ it "returns self bitwise OR other" do
+ (1 | 0).should == 1
+ (5 | 4).should == 5
+ (5 | 6).should == 7
+ (248 | 4096).should == 4344
+ (0xffff | bignum_value + 0xf0f0).should == 0x8000_0000_0000_ffff
+ end
+
+ it "returns self bitwise OR a bignum" do
+ (-1 | 2**64).should == -1
+ end
+
+ it "raises a TypeError when passed a Float" do
+ lambda { (3 | 3.4) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError and does not call #to_int when defined on an object" do
+ obj = mock("integer bit or")
+ obj.should_not_receive(:to_int)
+
+ lambda { 3 | obj }.should raise_error(TypeError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value(11)
+ end
+
+ it "returns self bitwise OR other" do
+ (@bignum | 2).should == 9223372036854775819
+ (@bignum | 9).should == 9223372036854775819
+ (@bignum | bignum_value).should == 9223372036854775819
+ end
+
+ it "returns self bitwise OR other when one operand is negative" do
+ (@bignum | -0x40000000000000000).should == -64563604257983430645
+ (@bignum | -@bignum).should == -1
+ (@bignum | -0x8000000000000000).should == -9223372036854775797
+ end
+
+ it "returns self bitwise OR other when both operands are negative" do
+ (-@bignum | -0x4000000000000005).should == -1
+ (-@bignum | -@bignum).should == -9223372036854775819
+ (-@bignum | -0x4000000000000000).should == -11
+ end
+
+ it "raises a TypeError when passed a Float" do
+ not_supported_on :opal do
+ lambda {
+ bignum_value | bignum_value(0xffff).to_f
+ }.should raise_error(TypeError)
+ end
+ lambda { @bignum | 9.9 }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError and does not call #to_int when defined on an object" do
+ obj = mock("bignum bit or")
+ obj.should_not_receive(:to_int)
+
+ lambda { @bignum | obj }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/bit_xor_spec.rb b/spec/ruby/core/integer/bit_xor_spec.rb
new file mode 100644
index 0000000000..65f88341e0
--- /dev/null
+++ b/spec/ruby/core/integer/bit_xor_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../../spec_helper'
+
+describe "Integer#^" do
+ context "fixnum" do
+ it "returns self bitwise EXCLUSIVE OR other" do
+ (3 ^ 5).should == 6
+ (-2 ^ -255).should == 255
+ (5 ^ bignum_value + 0xffff_ffff).should == 0x8000_0000_ffff_fffa
+ end
+
+ it "returns self bitwise EXCLUSIVE OR a bignum" do
+ (-1 ^ 2**64).should == -18446744073709551617
+ end
+
+ it "raises a TypeError when passed a Float" do
+ lambda { (3 ^ 3.4) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError and does not call #to_int when defined on an object" do
+ obj = mock("integer bit xor")
+ obj.should_not_receive(:to_int)
+
+ lambda { 3 ^ obj }.should raise_error(TypeError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value(18)
+ end
+
+ it "returns self bitwise EXCLUSIVE OR other" do
+ (@bignum ^ 2).should == 9223372036854775824
+ (@bignum ^ @bignum).should == 0
+ (@bignum ^ 14).should == 9223372036854775836
+ end
+
+ it "returns self bitwise EXCLUSIVE OR other when one operand is negative" do
+ (@bignum ^ -0x40000000000000000).should == -64563604257983430638
+ (@bignum ^ -@bignum).should == -4
+ (@bignum ^ -0x8000000000000000).should == -18446744073709551598
+ end
+
+ it "returns self bitwise EXCLUSIVE OR other when both operands are negative" do
+ (-@bignum ^ -0x40000000000000000).should == 64563604257983430638
+ (-@bignum ^ -@bignum).should == 0
+ (-@bignum ^ -0x4000000000000000).should == 13835058055282163694
+ end
+
+ it "returns self bitwise EXCLUSIVE OR other when all bits are 1 and other value is negative" do
+ (9903520314283042199192993791 ^ -1).should == -9903520314283042199192993792
+ (784637716923335095479473677900958302012794430558004314111 ^ -1).should ==
+ -784637716923335095479473677900958302012794430558004314112
+ end
+
+ it "raises a TypeError when passed a Float" do
+ not_supported_on :opal do
+ lambda {
+ bignum_value ^ bignum_value(0xffff).to_f
+ }.should raise_error(TypeError)
+ end
+ lambda { @bignum ^ 14.5 }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError and does not call #to_int when defined on an object" do
+ obj = mock("bignum bit xor")
+ obj.should_not_receive(:to_int)
+
+ lambda { @bignum ^ obj }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/case_compare_spec.rb b/spec/ruby/core/integer/case_compare_spec.rb
new file mode 100644
index 0000000000..e5dde2c64a
--- /dev/null
+++ b/spec/ruby/core/integer/case_compare_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/equal'
+
+describe "Integer#===" do
+ it_behaves_like :integer_equal, :===
+end
diff --git a/spec/ruby/core/integer/ceil_spec.rb b/spec/ruby/core/integer/ceil_spec.rb
new file mode 100644
index 0000000000..9e1311bc6d
--- /dev/null
+++ b/spec/ruby/core/integer/ceil_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_i'
+require_relative 'shared/integer_rounding'
+
+describe "Integer#ceil" do
+ it_behaves_like :integer_to_i, :ceil
+ it_behaves_like :integer_rounding_positive_precision, :ceil
+
+ ruby_version_is "2.4" do
+ context "precision argument specified as part of the ceil method is negative" do
+ it "returns the smallest integer greater than self with at least precision.abs trailing zeros" do
+ 18.ceil(-1).should eql(20)
+ 18.ceil(-2).should eql(100)
+ 18.ceil(-3).should eql(1000)
+ -1832.ceil(-1).should eql(-1830)
+ -1832.ceil(-2).should eql(-1800)
+ -1832.ceil(-3).should eql(-1000)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/chr_spec.rb b/spec/ruby/core/integer/chr_spec.rb
new file mode 100644
index 0000000000..8420d85cf6
--- /dev/null
+++ b/spec/ruby/core/integer/chr_spec.rb
@@ -0,0 +1,243 @@
+require_relative '../../spec_helper'
+
+describe "Integer#chr without argument" do
+ it "returns a String" do
+ 17.chr.should be_an_instance_of(String)
+ end
+
+ it "returns a new String for each call" do
+ 82.chr.should_not equal(82.chr)
+ end
+
+ it "raises a RangeError is self is less than 0" do
+ lambda { -1.chr }.should raise_error(RangeError)
+ lambda { -bignum_value.chr }.should raise_error(RangeError)
+ end
+
+ describe "when Encoding.default_internal is nil" do
+ describe "and self is between 0 and 127 (inclusive)" do
+ it "returns a US-ASCII String" do
+ (0..127).each do |c|
+ c.chr.encoding.should == Encoding::US_ASCII
+ end
+ end
+
+ it "returns a String encoding self interpreted as a US-ASCII codepoint" do
+ (0..127).each do |c|
+ c.chr.bytes.to_a.should == [c]
+ end
+ end
+ end
+
+ describe "and self is between 128 and 255 (inclusive)" do
+ it "returns an ASCII-8BIT String" do
+ (128..255).each do |c|
+ c.chr.encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+
+ it "returns a String containing self interpreted as a byte" do
+ (128..255).each do |c|
+ c.chr.bytes.to_a.should == [c]
+ end
+ end
+ end
+
+ it "raises a RangeError is self is greater than 255" do
+ lambda { 256.chr }.should raise_error(RangeError)
+ lambda { bignum_value.chr }.should raise_error(RangeError)
+ end
+ end
+
+ describe "when Encoding.default_internal is not nil" do
+ before do
+ @default_internal = Encoding.default_internal
+ end
+
+ after do
+ Encoding.default_internal = @default_internal
+ end
+
+ describe "and self is between 0 and 127 (inclusive)" do
+ it "returns a US-ASCII String" do
+ (0..127).each do |c|
+ Encoding.default_internal = Encoding::UTF_8
+ c.chr.encoding.should == Encoding::US_ASCII
+
+ Encoding.default_internal = Encoding::SHIFT_JIS
+ c.chr.encoding.should == Encoding::US_ASCII
+ end
+ end
+
+ it "returns a String encoding self interpreted as a US-ASCII codepoint" do
+ (0..127).each do |c|
+ Encoding.default_internal = Encoding::UTF_8
+ c.chr.bytes.to_a.should == [c]
+
+ Encoding.default_internal = Encoding::SHIFT_JIS
+ c.chr.bytes.to_a.should == [c]
+ end
+ end
+ end
+
+ describe "and self is between 128 and 255 (inclusive)" do
+ it "returns an ASCII-8BIT String" do
+ (128..255).each do |c|
+ Encoding.default_internal = Encoding::UTF_8
+ c.chr.encoding.should == Encoding::ASCII_8BIT
+
+ Encoding.default_internal = Encoding::SHIFT_JIS
+ c.chr.encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+
+ it "returns a String containing self interpreted as a byte" do
+ (128..255).each do |c|
+ Encoding.default_internal = Encoding::UTF_8
+ c.chr.bytes.to_a.should == [c]
+
+ Encoding.default_internal = Encoding::SHIFT_JIS
+ c.chr.bytes.to_a.should == [c]
+ end
+ end
+ end
+
+ describe "and self is greater than 255" do
+ it "returns a String with the default internal encoding" do
+ Encoding.default_internal = Encoding::UTF_8
+ 0x0100.chr.encoding.should == Encoding::UTF_8
+ 0x3000.chr.encoding.should == Encoding::UTF_8
+
+ Encoding.default_internal = Encoding::SHIFT_JIS
+ 0x8140.chr.encoding.should == Encoding::SHIFT_JIS
+ 0xFC4B.chr.encoding.should == Encoding::SHIFT_JIS
+ end
+
+ it "returns a String encoding self interpreted as a codepoint in the default internal encoding" do
+ Encoding.default_internal = Encoding::UTF_8
+ 0x0100.chr.bytes.to_a.should == [0xC4, 0x80]
+ 0x3000.chr.bytes.to_a.should == [0xE3, 0x80, 0x80]
+
+ Encoding.default_internal = Encoding::SHIFT_JIS
+ 0x8140.chr.bytes.to_a.should == [0x81, 0x40] # Smallest assigned CP932 codepoint greater than 255
+ 0xFC4B.chr.bytes.to_a.should == [0xFC, 0x4B] # Largest assigned CP932 codepoint
+ end
+
+ # #5864
+ it "raises RangeError if self is invalid as a codepoint in the default internal encoding" do
+ [ [0x0100, "US-ASCII"],
+ [0x0100, "ASCII-8BIT"],
+ [0x0100, "EUC-JP"],
+ [0xA1A0, "EUC-JP"],
+ [0x0100, "ISO-8859-9"],
+ [620, "TIS-620"]
+ ].each do |integer, encoding_name|
+ Encoding.default_internal = Encoding.find(encoding_name)
+ lambda { integer.chr }.should raise_error(RangeError)
+ end
+ end
+ end
+ end
+end
+
+describe "Integer#chr with an encoding argument" do
+ it "returns a String" do
+ 900.chr(Encoding::UTF_8).should be_an_instance_of(String)
+ end
+
+ it "returns a new String for each call" do
+ 8287.chr(Encoding::UTF_8).should_not equal(8287.chr(Encoding::UTF_8))
+ end
+
+ it "accepts a String as an argument" do
+ lambda { 0xA4A2.chr('euc-jp') }.should_not raise_error
+ end
+
+ it "converts a String to an Encoding as Encoding.find does" do
+ ['utf-8', 'UTF-8', 'Utf-8'].each do |encoding|
+ 7894.chr(encoding).encoding.should == Encoding::UTF_8
+ end
+ end
+
+ # http://redmine.ruby-lang.org/issues/4869
+ it "raises a RangeError is self is less than 0" do
+ lambda { -1.chr(Encoding::UTF_8) }.should raise_error(RangeError)
+ lambda { -bignum_value.chr(Encoding::EUC_JP) }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError if self is too large" do
+ lambda { 2206368128.chr(Encoding::UTF_8) }.should raise_error(RangeError)
+ end
+
+ it "returns a String with the specified encoding" do
+ 0x0000.chr(Encoding::US_ASCII).encoding.should == Encoding::US_ASCII
+ 0x007F.chr(Encoding::US_ASCII).encoding.should == Encoding::US_ASCII
+
+ 0x0000.chr(Encoding::ASCII_8BIT).encoding.should == Encoding::ASCII_8BIT
+ 0x007F.chr(Encoding::ASCII_8BIT).encoding.should == Encoding::ASCII_8BIT
+ 0x0080.chr(Encoding::ASCII_8BIT).encoding.should == Encoding::ASCII_8BIT
+ 0x00FF.chr(Encoding::ASCII_8BIT).encoding.should == Encoding::ASCII_8BIT
+
+ 0x0000.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8
+ 0x007F.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8
+ 0x0080.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8
+ 0x00FF.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8
+ 0x0100.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8
+ 0x3000.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8
+
+ 0x0000.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ 0x007F.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ 0x00A1.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ 0x00DF.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ 0x8140.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ 0xFC4B.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ end
+
+ it "returns a String encoding self interpreted as a codepoint in the specified encoding" do
+ 0x0000.chr(Encoding::US_ASCII).bytes.to_a.should == [0x00]
+ 0x007F.chr(Encoding::US_ASCII).bytes.to_a.should == [0x7F]
+
+ 0x0000.chr(Encoding::ASCII_8BIT).bytes.to_a.should == [0x00]
+ 0x007F.chr(Encoding::ASCII_8BIT).bytes.to_a.should == [0x7F]
+ 0x0080.chr(Encoding::ASCII_8BIT).bytes.to_a.should == [0x80]
+ 0x00FF.chr(Encoding::ASCII_8BIT).bytes.to_a.should == [0xFF]
+
+ 0x0000.chr(Encoding::UTF_8).bytes.to_a.should == [0x00]
+ 0x007F.chr(Encoding::UTF_8).bytes.to_a.should == [0x7F]
+ 0x0080.chr(Encoding::UTF_8).bytes.to_a.should == [0xC2, 0x80]
+ 0x00FF.chr(Encoding::UTF_8).bytes.to_a.should == [0xC3, 0xBF]
+ 0x0100.chr(Encoding::UTF_8).bytes.to_a.should == [0xC4, 0x80]
+ 0x3000.chr(Encoding::UTF_8).bytes.to_a.should == [0xE3, 0x80, 0x80]
+
+ 0x0000.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0x00]
+ 0x007F.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0x7F]
+ 0x00A1.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0xA1]
+ 0x00DF.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0xDF]
+ 0x8140.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0x81, 0x40] # Smallest assigned CP932 codepoint greater than 255
+ 0xFC4B.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0xFC, 0x4B] # Largest assigned CP932 codepoint
+ end
+
+ # #5864
+ it "raises RangeError if self is invalid as a codepoint in the specified encoding" do
+ [ [0x80, "US-ASCII"],
+ [0x0100, "ASCII-8BIT"],
+ [0x0100, "EUC-JP"],
+ [0xA1A0, "EUC-JP"],
+ [0xA1, "EUC-JP"],
+ [0x80, "SHIFT_JIS"],
+ [0xE0, "SHIFT_JIS"],
+ [0x0100, "ISO-8859-9"],
+ [620, "TIS-620"],
+ [0xD800, "UTF-8"],
+ [0xDBFF, "UTF-8"],
+ [0xDC00, "UTF-8"],
+ [0xDFFF, "UTF-8"],
+ [0xD800, "UTF-16"],
+ [0xDBFF, "UTF-16"],
+ [0xDC00, "UTF-16"],
+ [0xDFFF, "UTF-16"],
+ ].each do |integer, encoding_name|
+ lambda { integer.chr(encoding_name) }.should raise_error(RangeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/coerce_spec.rb b/spec/ruby/core/integer/coerce_spec.rb
new file mode 100644
index 0000000000..1bc30fe9ce
--- /dev/null
+++ b/spec/ruby/core/integer/coerce_spec.rb
@@ -0,0 +1,105 @@
+require_relative '../../spec_helper'
+
+describe "Integer#coerce" do
+ context "fixnum" do
+ describe "when given a Fixnum" do
+ it "returns an array containing two Fixnums" do
+ 1.coerce(2).should == [2, 1]
+ 1.coerce(2).map { |i| i.class }.should == [Fixnum, Fixnum]
+ end
+ end
+
+ describe "when given a String" do
+ it "raises an ArgumentError when trying to coerce with a non-number String" do
+ lambda { 1.coerce(":)") }.should raise_error(ArgumentError)
+ end
+
+ it "returns an array containing two Floats" do
+ 1.coerce("2").should == [2.0, 1.0]
+ 1.coerce("-2").should == [-2.0, 1.0]
+ end
+ end
+
+ it "raises a TypeError when trying to coerce with nil" do
+ lambda { 1.coerce(nil) }.should raise_error(TypeError)
+ end
+
+ it "tries to convert the given Object into a Float by using #to_f" do
+ (obj = mock('1.0')).should_receive(:to_f).and_return(1.0)
+ 2.coerce(obj).should == [1.0, 2.0]
+
+ (obj = mock('0')).should_receive(:to_f).and_return('0')
+ lambda { 2.coerce(obj).should == [1.0, 2.0] }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when given an Object that does not respond to #to_f" do
+ lambda { 1.coerce(mock('x')) }.should raise_error(TypeError)
+ lambda { 1.coerce(1..4) }.should raise_error(TypeError)
+ lambda { 1.coerce(:test) }.should raise_error(TypeError)
+ end
+ end
+
+ context "bignum" do
+ it "coerces other to a Bignum and returns [other, self] when passed a Fixnum" do
+ a = bignum_value
+ ary = a.coerce(2)
+
+ ary[0].should be_kind_of(Bignum)
+ ary[1].should be_kind_of(Bignum)
+ ary.should == [2, a]
+ end
+
+ it "returns [other, self] when passed a Bignum" do
+ a = bignum_value
+ b = bignum_value
+ ary = a.coerce(b)
+
+ ary[0].should be_kind_of(Bignum)
+ ary[1].should be_kind_of(Bignum)
+ ary.should == [b, a]
+ end
+
+ it "raises a TypeError when not passed a Fixnum or Bignum" do
+ a = bignum_value
+
+ lambda { a.coerce(nil) }.should raise_error(TypeError)
+ lambda { a.coerce(mock('str')) }.should raise_error(TypeError)
+ lambda { a.coerce(1..4) }.should raise_error(TypeError)
+ lambda { a.coerce(:test) }.should raise_error(TypeError)
+ end
+
+ ruby_version_is ""..."2.4" do
+ it "raises a TypeError when passed a String" do
+ a = bignum_value
+ lambda { a.coerce("123") }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a Float" do
+ a = bignum_value
+ lambda { a.coerce(12.3) }.should raise_error(TypeError)
+ end
+ end
+
+ ruby_version_is "2.4" do
+ it "coerces both values to Floats and returns [other, self] when passed a Float" do
+ a = bignum_value
+ a.coerce(1.2).should == [1.2, a.to_f]
+ end
+
+ it "coerces both values to Floats and returns [other, self] when passed a String" do
+ a = bignum_value
+ a.coerce("123").should == [123.0, a.to_f]
+ end
+
+ it "calls #to_f to coerce other to a Float" do
+ b = mock("bignum value")
+ b.should_receive(:to_f).and_return(1.2)
+
+ a = bignum_value
+ ary = a.coerce(b)
+
+ ary.should == [1.2, a.to_f]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/comparison_spec.rb b/spec/ruby/core/integer/comparison_spec.rb
new file mode 100644
index 0000000000..4cf1d7cdc7
--- /dev/null
+++ b/spec/ruby/core/integer/comparison_spec.rb
@@ -0,0 +1,189 @@
+require_relative '../../spec_helper'
+
+describe "Integer#<=>" do
+ context "fixnum" do
+ it "returns -1 when self is less than the given argument" do
+ (-3 <=> -1).should == -1
+ (-5 <=> 10).should == -1
+ (-5 <=> -4.5).should == -1
+ end
+
+ it "returns 0 when self is equal to the given argument" do
+ (0 <=> 0).should == 0
+ (954 <=> 954).should == 0
+ (954 <=> 954.0).should == 0
+ end
+
+ it "returns 1 when self is greater than the given argument" do
+ (496 <=> 5).should == 1
+ (200 <=> 100).should == 1
+ (51 <=> 50.5).should == 1
+ end
+
+ it "returns nil when the given argument is not an Integer" do
+ (3 <=> mock('x')).should == nil
+ (3 <=> 'test').should == nil
+ end
+ end
+
+ context "bignum" do
+ describe "with a Fixnum" do
+ it "returns -1 when other is larger" do
+ (-bignum_value <=> 2).should == -1
+ end
+
+ it "returns 1 when other is smaller" do
+ (bignum_value <=> 2).should == 1
+ end
+ end
+
+ describe "with a Bignum" do
+ describe "when other is negative" do
+ it "returns -1 when self is negative and other is larger" do
+ (-bignum_value(42) <=> -bignum_value).should == -1
+ end
+
+ it "returns 0 when other is equal" do
+ (-bignum_value <=> -bignum_value).should == 0
+ end
+
+ it "returns 1 when self is negative and other is smaller" do
+ (-bignum_value <=> -bignum_value(94)).should == 1
+ end
+
+ it "returns 1 when self is positive" do
+ (bignum_value <=> -bignum_value).should == 1
+ end
+ end
+
+ describe "when other is positive" do
+ it "returns -1 when self is negative" do
+ (-bignum_value <=> bignum_value).should == -1
+ end
+
+ it "returns -1 when self is positive and other is larger" do
+ (bignum_value <=> bignum_value(38)).should == -1
+ end
+
+ it "returns 0 when other is equal" do
+ (bignum_value <=> bignum_value).should == 0
+ end
+
+ it "returns 1 when other is smaller" do
+ (bignum_value(56) <=> bignum_value).should == 1
+ end
+ end
+ end
+
+ describe "with a Float" do
+ describe "when other is negative" do
+ it "returns -1 when self is negative and other is larger" do
+ (-bignum_value(0xffff) <=> -bignum_value.to_f).should == -1
+ end
+
+ it "returns 0 when other is equal" do
+ (-bignum_value <=> -bignum_value.to_f).should == 0
+ end
+
+ it "returns 1 when self is negative and other is smaller" do
+ (-bignum_value <=> -bignum_value(0xffef).to_f).should == 1
+ end
+
+ it "returns 1 when self is positive" do
+ (bignum_value <=> -bignum_value.to_f).should == 1
+ end
+ end
+
+ describe "when other is positive" do
+ it "returns -1 when self is negative" do
+ (-bignum_value <=> bignum_value.to_f).should == -1
+ end
+
+ it "returns -1 when self is positive and other is larger" do
+ (bignum_value <=> bignum_value(0xfffe).to_f).should == -1
+ end
+
+ it "returns 0 when other is equal" do
+ (bignum_value <=> bignum_value.to_f).should == 0
+ end
+
+ it "returns 1 when other is smaller" do
+ (bignum_value(0xfeff) <=> bignum_value.to_f).should == 1
+ end
+ end
+ end
+
+ describe "with an Object" do
+ before :each do
+ @big = bignum_value
+ @num = mock("value for Bignum#<=>")
+ end
+
+ it "calls #coerce on other" do
+ @num.should_receive(:coerce).with(@big).and_return([@big.to_f, 2.5])
+ @big <=> @num
+ end
+
+ ruby_version_is ""..."2.5" do
+ it "returns nil if #coerce raises an exception" do
+ @num.should_receive(:coerce).with(@big).and_raise(RuntimeError)
+ lambda {
+ @result = (@big <=> @num)
+ }.should complain(/Numerical comparison operators will no more rescue exceptions/)
+ @result.should be_nil
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "lets the exception go through if #coerce raises an exception" do
+ @num.should_receive(:coerce).with(@big).and_raise(RuntimeError.new("my error"))
+ lambda {
+ @big <=> @num
+ }.should raise_error(RuntimeError, "my error")
+ end
+ end
+
+ it "raises an exception if #coerce raises a non-StandardError exception" do
+ @num.should_receive(:coerce).with(@big).and_raise(Exception)
+ lambda { @big <=> @num }.should raise_error(Exception)
+ end
+
+ it "returns nil if #coerce does not return an Array" do
+ @num.should_receive(:coerce).with(@big).and_return(nil)
+ (@big <=> @num).should be_nil
+ end
+
+ it "returns -1 if the coerced value is larger" do
+ @num.should_receive(:coerce).with(@big).and_return([@big, bignum_value(10)])
+ (@big <=> @num).should == -1
+ end
+
+ it "returns 0 if the coerced value is equal" do
+ @num.should_receive(:coerce).with(@big).and_return([@big, bignum_value])
+ (@big <=> @num).should == 0
+ end
+
+ it "returns 1 if the coerced value is smaller" do
+ @num.should_receive(:coerce).with(@big).and_return([@big, 22])
+ (@big <=> @num).should == 1
+ end
+ end
+
+ # The tests below are taken from matz's revision 23730 for Ruby trunk
+ it "returns 1 when self is Infinity and other is a Bignum" do
+ (infinity_value <=> Float::MAX.to_i*2).should == 1
+ end
+
+ it "returns -1 when self is negative and other is Infinty" do
+ (-Float::MAX.to_i*2 <=> infinity_value).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
+ end
+end
diff --git a/spec/ruby/core/integer/complement_spec.rb b/spec/ruby/core/integer/complement_spec.rb
new file mode 100644
index 0000000000..eafa5c263f
--- /dev/null
+++ b/spec/ruby/core/integer/complement_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+
+describe "Integer#~" do
+ context "fixnum" do
+ it "returns self with each bit flipped" do
+ (~0).should == -1
+ (~1221).should == -1222
+ (~-2).should == 1
+ (~-599).should == 598
+ end
+ end
+
+ context "bignum" do
+ it "returns self with each bit flipped" do
+ (~bignum_value(48)).should == -9223372036854775857
+ (~(-bignum_value(21))).should == 9223372036854775828
+ (~bignum_value(1)).should == -9223372036854775810
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/denominator_spec.rb b/spec/ruby/core/integer/denominator_spec.rb
new file mode 100644
index 0000000000..c1477d0757
--- /dev/null
+++ b/spec/ruby/core/integer/denominator_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+
+describe "Integer#denominator" do
+ # The Numeric child classes override this method, so their behaviour is
+ # specified in the appropriate place
+ before :each do
+ @numbers = [
+ 20, # Integer
+ -2709, # Negative Integer
+ 99999999**99, # Bignum
+ -99999**621, # Negative BigNum
+ 0,
+ 1
+ ]
+ end
+
+ it "returns 1" do
+ @numbers.each {|number| number.denominator.should == 1}
+ end
+end
diff --git a/spec/ruby/core/integer/digits_spec.rb b/spec/ruby/core/integer/digits_spec.rb
new file mode 100644
index 0000000000..a60650246b
--- /dev/null
+++ b/spec/ruby/core/integer/digits_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.4" do
+ describe "Integer#digits" do
+ it "returns an array of place values in base-10 by default" do
+ 12345.digits.should == [5,4,3,2,1]
+ end
+
+ it "returns digits by place value of a given radix" do
+ 12345.digits(7).should == [4,6,6,0,5]
+ end
+
+ it "converts the radix with #to_int" do
+ 12345.digits(mock_int(2)).should == [1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1]
+ end
+
+ it "returns [0] when called on 0, regardless of base" do
+ 0.digits.should == [0]
+ 0.digits(7).should == [0]
+ end
+
+ it "raises ArgumentError when calling with a radix less than 2" do
+ lambda { 12345.digits(1) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError when calling with a negative radix" do
+ lambda { 12345.digits(-2) }.should raise_error(ArgumentError)
+ end
+
+ it "raises Math::DomainError when calling digits on a negative number" do
+ lambda { -12345.digits(7) }.should raise_error(Math::DomainError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/div_spec.rb b/spec/ruby/core/integer/div_spec.rb
new file mode 100644
index 0000000000..9329f5062e
--- /dev/null
+++ b/spec/ruby/core/integer/div_spec.rb
@@ -0,0 +1,138 @@
+require_relative '../../spec_helper'
+
+describe "Integer#div" do
+ context "fixnum" do
+ it "returns self divided by the given argument as an Integer" do
+ 2.div(2).should == 1
+ 1.div(2).should == 0
+ 5.div(2).should == 2
+ end
+
+ it "rounds towards -inf" do
+ 8192.div(10).should == 819
+ 8192.div(-10).should == -820
+ (-8192).div(10).should == -820
+ (-8192).div(-10).should == 819
+ end
+
+ it "means (x / y).floor" do
+ 5.div(2).should == (5 / 2).floor
+ 5.div(2.0).should == (5 / 2.0).floor
+ 5.div(-2).should == (5 / -2).floor
+
+ 5.div(100).should == (5 / 100).floor
+ 5.div(100.0).should == (5 / 100.0).floor
+ 5.div(-100).should == (5 / -100).floor
+ end
+
+ it "calls #coerce and #div if argument responds to #coerce" do
+ x = mock("x")
+ y = mock("y")
+ result = mock("result")
+
+ y.should_receive(:coerce).and_return([x, y])
+ x.should_receive(:div).with(y).and_return(result)
+
+ 10.div(y).should == result
+ end
+
+ it "coerces self and the given argument to Floats and returns self divided by other as Fixnum" do
+ 1.div(0.2).should == 5
+ 1.div(0.16).should == 6
+ 1.div(0.169).should == 5
+ -1.div(50.4).should == -1
+ 1.div(bignum_value).should == 0
+ 1.div(Rational(1, 5)).should == 5
+ end
+
+ it "raises a ZeroDivisionError when the given argument is 0 and a Float" do
+ lambda { 0.div(0.0) }.should raise_error(ZeroDivisionError)
+ lambda { 10.div(0.0) }.should raise_error(ZeroDivisionError)
+ lambda { -10.div(0.0) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "raises a ZeroDivisionError when the given argument is 0 and not a Float" do
+ lambda { 13.div(0) }.should raise_error(ZeroDivisionError)
+ lambda { 13.div(-0) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "raises a TypeError when given a non-numeric argument" do
+ lambda { 13.div(mock('10')) }.should raise_error(TypeError)
+ lambda { 5.div("2") }.should raise_error(TypeError)
+ lambda { 5.div(:"2") }.should raise_error(TypeError)
+ lambda { 5.div([]) }.should raise_error(TypeError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value(88)
+ end
+
+ it "returns self divided by other" do
+ @bignum.div(4).should == 2305843009213693974
+ @bignum.div(Rational(4, 1)).should == 2305843009213693974
+ @bignum.div(bignum_value(2)).should == 1
+
+ (-(10**50)).div(-(10**40 + 1)).should == 9999999999
+ (10**50).div(10**40 + 1).should == 9999999999
+
+ (-10**50).div(10**40 + 1).should == -10000000000
+ (10**50).div(-(10**40 + 1)).should == -10000000000
+ end
+
+ it "calls #coerce and #div if argument responds to #coerce" do
+ x = mock("x")
+ y = mock("y")
+ result = mock("result")
+
+ y.should_receive(:coerce).and_return([x, y])
+ x.should_receive(:div).with(y).and_return(result)
+
+ @bignum.div(y).should == result
+ end
+
+ it "means (x / y).floor" do
+ @bignum.div(2).should == (@bignum / 2).floor
+ @bignum.div(-2).should == (@bignum / -2).floor
+
+ @bignum.div(@bignum+1).should == (@bignum / (@bignum+1)).floor
+ @bignum.div(-(@bignum+1)).should == (@bignum / -(@bignum+1)).floor
+
+ @bignum.div(2.0).should == (@bignum / 2.0).floor
+ @bignum.div(100.0).should == (@bignum / 100.0).floor
+ end
+
+ it "looses precision if passed Float argument" do
+ @bignum.div(1).should_not == @bignum.div(1.0)
+ @bignum.div(4).should_not == @bignum.div(4.0)
+ @bignum.div(21).should_not == @bignum.div(21.0)
+ end
+
+ it "raises a TypeError when given a non-numeric" do
+ lambda { @bignum.div(mock("10")) }.should raise_error(TypeError)
+ lambda { @bignum.div("2") }.should raise_error(TypeError)
+ lambda { @bignum.div(:symbol) }.should raise_error(TypeError)
+ end
+
+ it "returns a result of integer division of self by a float argument" do
+ @bignum.div(4294967295.5).should eql(2147483648)
+ not_supported_on :opal do
+ @bignum.div(4294967295.0).should eql(2147483648)
+ @bignum.div(bignum_value(88).to_f).should eql(1)
+ @bignum.div(-bignum_value(88).to_f).should eql(-1)
+ end
+ end
+
+ # #5490
+ it "raises ZeroDivisionError if the argument is 0 and is a Float" do
+ lambda { @bignum.div(0.0) }.should raise_error(ZeroDivisionError)
+ lambda { @bignum.div(-0.0) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "raises ZeroDivisionError if the argument is 0 and is not a Float" do
+ lambda { @bignum.div(0) }.should raise_error(ZeroDivisionError)
+ lambda { @bignum.div(-0) }.should raise_error(ZeroDivisionError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/divide_spec.rb b/spec/ruby/core/integer/divide_spec.rb
new file mode 100644
index 0000000000..2d49307628
--- /dev/null
+++ b/spec/ruby/core/integer/divide_spec.rb
@@ -0,0 +1,95 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arithmetic_coerce'
+
+describe "Integer#/" do
+ ruby_version_is "2.4"..."2.5" do
+ it_behaves_like :integer_arithmetic_coerce_rescue, :/
+ end
+
+ ruby_version_is "2.5" do
+ it_behaves_like :integer_arithmetic_coerce_not_rescue, :/
+ end
+
+ context "fixnum" do
+ it "returns self divided by the given argument" do
+ (2 / 2).should == 1
+ (3 / 2).should == 1
+ end
+
+ it "supports dividing negative numbers" do
+ (-1 / 10).should == -1
+ end
+
+ it "returns result the same class as the argument" do
+ (3 / 2).should == 1
+ (3 / 2.0).should == 1.5
+ (3 / Rational(2, 1)).should == Rational(3, 2)
+ end
+
+ it "raises a ZeroDivisionError if the given argument is zero and not a Float" do
+ lambda { 1 / 0 }.should raise_error(ZeroDivisionError)
+ end
+
+ it "does NOT raise ZeroDivisionError if the given argument is zero and is a Float" do
+ (1 / 0.0).to_s.should == 'Infinity'
+ (-1 / 0.0).to_s.should == '-Infinity'
+ end
+
+ it "coerces fixnum and return self divided by other" do
+ (-1 / 50.4).should be_close(-0.0198412698412698, TOLERANCE)
+ (1 / bignum_value).should == 0
+ end
+
+ it "raises a TypeError when given a non-Integer" do
+ lambda { 13 / mock('10') }.should raise_error(TypeError)
+ lambda { 13 / "10" }.should raise_error(TypeError)
+ lambda { 13 / :symbol }.should raise_error(TypeError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value(88)
+ end
+
+ it "returns self divided by other" do
+ (@bignum / 4).should == 2305843009213693974
+
+ (@bignum / bignum_value(2)).should == 1
+
+ (-(10**50) / -(10**40 + 1)).should == 9999999999
+ ((10**50) / (10**40 + 1)).should == 9999999999
+
+ ((-10**50) / (10**40 + 1)).should == -10000000000
+ ((10**50) / -(10**40 + 1)).should == -10000000000
+ end
+
+ it "returns self divided by Float" do
+ not_supported_on :opal do
+ (bignum_value(88) / 4294967295.0).should be_close(2147483648.5, TOLERANCE)
+ end
+ (bignum_value(88) / 4294967295.5).should be_close(2147483648.25, TOLERANCE)
+ end
+
+ it "returns result the same class as the argument" do
+ (@bignum / 4).should == 2305843009213693974
+ (@bignum / 4.0).should be_close(2305843009213693974, TOLERANCE)
+ (@bignum / Rational(4, 1)).should == Rational(2305843009213693974, 1)
+ end
+
+ it "does NOT raise ZeroDivisionError if other is zero and is a Float" do
+ (bignum_value / 0.0).to_s.should == 'Infinity'
+ (bignum_value / -0.0).to_s.should == '-Infinity'
+ end
+
+ it "raises a ZeroDivisionError if other is zero and not a Float" do
+ lambda { @bignum / 0 }.should raise_error(ZeroDivisionError)
+ end
+
+ it "raises a TypeError when given a non-numeric" do
+ lambda { @bignum / mock('10') }.should raise_error(TypeError)
+ lambda { @bignum / "2" }.should raise_error(TypeError)
+ lambda { @bignum / :symbol }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/divmod_spec.rb b/spec/ruby/core/integer/divmod_spec.rb
new file mode 100644
index 0000000000..27fb34574c
--- /dev/null
+++ b/spec/ruby/core/integer/divmod_spec.rb
@@ -0,0 +1,117 @@
+require_relative '../../spec_helper'
+
+describe "Integer#divmod" do
+ context "fixnum" do
+ it "returns an Array containing quotient and modulus obtained from dividing self by the given argument" do
+ 13.divmod(4).should == [3, 1]
+ 4.divmod(13).should == [0, 4]
+
+ 13.divmod(4.0).should == [3, 1]
+ 4.divmod(13.0).should == [0, 4]
+
+ 1.divmod(2.0).should == [0, 1.0]
+ 200.divmod(bignum_value).should == [0, 200]
+ end
+
+ it "raises a ZeroDivisionError when the given argument is 0" do
+ lambda { 13.divmod(0) }.should raise_error(ZeroDivisionError)
+ lambda { 0.divmod(0) }.should raise_error(ZeroDivisionError)
+ lambda { -10.divmod(0) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "raises a ZeroDivisionError when the given argument is 0 and a Float" do
+ lambda { 0.divmod(0.0) }.should raise_error(ZeroDivisionError)
+ lambda { 10.divmod(0.0) }.should raise_error(ZeroDivisionError)
+ lambda { -10.divmod(0.0) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "raises a TypeError when given a non-Integer" do
+ lambda {
+ (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10)
+ 13.divmod(obj)
+ }.should raise_error(TypeError)
+ lambda { 13.divmod("10") }.should raise_error(TypeError)
+ lambda { 13.divmod(:symbol) }.should raise_error(TypeError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value(55)
+ end
+
+ # Based on MRI's test/test_integer.rb (test_divmod),
+ # MRI maintains the following property:
+ # if q, r = a.divmod(b) ==>
+ # assert(0 < b ? (0 <= r && r < b) : (b < r && r <= 0))
+ # So, r is always between 0 and b.
+ it "returns an Array containing quotient and modulus obtained from dividing self by the given argument" do
+ @bignum.divmod(4).should == [2305843009213693965, 3]
+ @bignum.divmod(13).should == [709490156681136604, 11]
+
+ @bignum.divmod(4.5).should == [2049638230412172288, 3.5]
+
+ not_supported_on :opal do
+ @bignum.divmod(4.0).should == [2305843009213693952, 0.0]
+ @bignum.divmod(13.0).should == [709490156681136640, 8.0]
+
+ @bignum.divmod(2.0).should == [4611686018427387904, 0.0]
+ end
+
+ @bignum.divmod(bignum_value).should == [1, 55]
+
+ (-(10**50)).divmod(-(10**40 + 1)).should == [9999999999, -9999999999999999999999999999990000000001]
+ (10**50).divmod(10**40 + 1).should == [9999999999, 9999999999999999999999999999990000000001]
+
+ (-10**50).divmod(10**40 + 1).should == [-10000000000, 10000000000]
+ (10**50).divmod(-(10**40 + 1)).should == [-10000000000, -10000000000]
+ end
+
+ describe "with q = floor(x/y), a = q*b + r," do
+ it "returns [q,r] when a < 0, b > 0 and |a| < b" do
+ a = -@bignum + 1
+ b = @bignum
+ a.divmod(b).should == [-1, 1]
+ end
+
+ it "returns [q,r] when a > 0, b < 0 and a > |b|" do
+ b = -@bignum + 1
+ a = @bignum
+ a.divmod(b).should == [-2, -@bignum + 2]
+ end
+
+ it "returns [q,r] when a > 0, b < 0 and a < |b|" do
+ a = @bignum - 1
+ b = -@bignum
+ a.divmod(b).should == [-1, -1]
+ end
+
+ it "returns [q,r] when a < 0, b < 0 and |a| < |b|" do
+ a = -@bignum + 1
+ b = -@bignum
+ a.divmod(b).should == [0, -@bignum + 1]
+ end
+ end
+
+ it "raises a ZeroDivisionError when the given argument is 0" do
+ lambda { @bignum.divmod(0) }.should raise_error(ZeroDivisionError)
+ lambda { (-@bignum).divmod(0) }.should raise_error(ZeroDivisionError)
+ end
+
+ # Behaviour established as correct in r23953
+ it "raises a FloatDomainError if other is NaN" do
+ lambda { @bignum.divmod(nan_value) }.should raise_error(FloatDomainError)
+ end
+
+ it "raises a ZeroDivisionError when the given argument is 0 and a Float" do
+ lambda { @bignum.divmod(0.0) }.should raise_error(ZeroDivisionError)
+ lambda { (-@bignum).divmod(0.0) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "raises a TypeError when the given argument is not an Integer" do
+ lambda { @bignum.divmod(mock('10')) }.should raise_error(TypeError)
+ lambda { @bignum.divmod("10") }.should raise_error(TypeError)
+ lambda { @bignum.divmod(:symbol) }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/downto_spec.rb b/spec/ruby/core/integer/downto_spec.rb
new file mode 100644
index 0000000000..92dee89fc9
--- /dev/null
+++ b/spec/ruby/core/integer/downto_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../../spec_helper'
+
+describe "Integer#downto [stop] when self and stop are Fixnums" do
+ it "does not yield when stop is greater than self" do
+ result = []
+ 5.downto(6) { |x| result << x }
+ result.should == []
+ end
+
+ it "yields once when stop equals self" do
+ result = []
+ 5.downto(5) { |x| result << x }
+ result.should == [5]
+ end
+
+ it "yields while decreasing self until it is less than stop" do
+ result = []
+ 5.downto(2) { |x| result << x }
+ result.should == [5, 4, 3, 2]
+ end
+
+ it "yields while decreasing self until it less than ceil for a Float endpoint" do
+ result = []
+ 9.downto(1.3) {|i| result << i}
+ 3.downto(-1.3) {|i| result << i}
+ result.should == [9, 8, 7, 6, 5, 4, 3, 2, 3, 2, 1, 0, -1]
+ end
+
+ it "raises an ArgumentError for invalid endpoints" do
+ lambda {1.downto("A") {|x| p x } }.should raise_error(ArgumentError)
+ lambda {1.downto(nil) {|x| p x } }.should raise_error(ArgumentError)
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ result = []
+
+ enum = 5.downto(2)
+ enum.each { |i| result << i }
+
+ result.should == [5, 4, 3, 2]
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "raises an ArgumentError for invalid endpoints" do
+ enum = 1.downto("A")
+ lambda { enum.size }.should raise_error(ArgumentError)
+ enum = 1.downto(nil)
+ lambda { enum.size }.should raise_error(ArgumentError)
+ end
+
+ it "returns self - stop + 1" do
+ 10.downto(5).size.should == 6
+ 10.downto(1).size.should == 10
+ 10.downto(0).size.should == 11
+ 0.downto(0).size.should == 1
+ -3.downto(-5).size.should == 3
+ end
+
+ it "returns 0 when stop > self" do
+ 4.downto(5).size.should == 0
+ -5.downto(0).size.should == 0
+ -5.downto(-3).size.should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/dup_spec.rb b/spec/ruby/core/integer/dup_spec.rb
new file mode 100644
index 0000000000..214367f0b4
--- /dev/null
+++ b/spec/ruby/core/integer/dup_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '2.4' do
+ describe "Integer#dup" do
+ it "returns self for small integers" do
+ integer = 1_000
+ integer.dup.should equal(integer)
+ end
+
+ it "returns self for large integers" do
+ integer = 4_611_686_018_427_387_905
+ integer.dup.should equal(integer)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/element_reference_spec.rb b/spec/ruby/core/integer/element_reference_spec.rb
new file mode 100644
index 0000000000..6c72a717d6
--- /dev/null
+++ b/spec/ruby/core/integer/element_reference_spec.rb
@@ -0,0 +1,111 @@
+require_relative '../../spec_helper'
+
+describe "Integer#[]" do
+ context "fixnum" do
+ it "behaves like (n >> b) & 1" do
+ 0b101[1].should == 0
+ 0b101[2].should == 1
+ end
+
+ it "returns 1 if the nth bit is set" do
+ 15[1].should == 1
+ end
+
+ it "returns 1 if the nth bit is set (in two's-complement representation)" do
+ (-1)[1].should == 1
+ end
+
+ it "returns 0 if the nth bit is not set" do
+ 8[2].should == 0
+ end
+
+ it "returns 0 if the nth bit is not set (in two's-complement representation)" do
+ (-2)[0].should == 0
+ end
+
+ it "returns 0 if the nth bit is greater than the most significant bit" do
+ 2[3].should == 0
+ end
+
+ it "returns 1 if self is negative and the nth bit is greater than the most significant bit" do
+ (-1)[3].should == 1
+ end
+
+ it "returns 0 when passed a negative argument" do
+ 3[-1].should == 0
+ (-1)[-1].should == 0
+ end
+
+ it "calls #to_int to convert the argument to an Integer and returns 1 if the nth bit is set" do
+ obj = mock('1')
+ obj.should_receive(:to_int).and_return(1)
+
+ 2[obj].should == 1
+ end
+
+ it "calls #to_int to convert the argument to an Integer and returns 0 if the nth bit is set" do
+ obj = mock('0')
+ obj.should_receive(:to_int).and_return(0)
+
+ 2[obj].should == 0
+ end
+
+ it "accepts a Float argument and returns 0 if the bit at the truncated value is not set" do
+ 13[1.3].should == 0
+ end
+
+ it "accepts a Float argument and returns 1 if the bit at the truncated value is set" do
+ 13[2.1].should == 1
+ end
+
+ it "raises a TypeError when passed a String" do
+ lambda { 3["3"] }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when #to_int does not return an Integer" do
+ obj = mock('asdf')
+ obj.should_receive(:to_int).and_return("asdf")
+ lambda { 3[obj] }.should raise_error(TypeError)
+ end
+
+ it "calls #to_int to coerce a String to a Bignum and returns 0" do
+ obj = mock('bignum value')
+ obj.should_receive(:to_int).and_return(bignum_value)
+
+ 3[obj].should == 0
+ end
+
+ it "returns 0 when passed a Float in the range of a Bignum" do
+ 3[bignum_value.to_f].should == 0
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value(4996)
+ end
+
+ it "returns the nth bit in the binary representation of self" do
+ @bignum[2].should == 1
+ @bignum[9.2].should == 1
+ @bignum[21].should == 0
+ @bignum[0xffffffff].should == 0
+ @bignum[-0xffffffff].should == 0
+ end
+
+ it "tries to convert the given argument to an Integer using #to_int" do
+ @bignum[1.3].should == @bignum[1]
+
+ (obj = mock('2')).should_receive(:to_int).at_least(1).and_return(2)
+ @bignum[obj].should == 1
+ end
+
+ it "raises a TypeError when the given argument can't be converted to Integer" do
+ obj = mock('asdf')
+ lambda { @bignum[obj] }.should raise_error(TypeError)
+
+ obj.should_receive(:to_int).and_return("asdf")
+ lambda { @bignum[obj] }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/equal_value_spec.rb b/spec/ruby/core/integer/equal_value_spec.rb
new file mode 100644
index 0000000000..67a73713af
--- /dev/null
+++ b/spec/ruby/core/integer/equal_value_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/equal'
+
+describe "Integer#==" do
+ it_behaves_like :integer_equal, :==
+end
diff --git a/spec/ruby/core/integer/even_spec.rb b/spec/ruby/core/integer/even_spec.rb
new file mode 100644
index 0000000000..a314cc6b19
--- /dev/null
+++ b/spec/ruby/core/integer/even_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+
+describe "Integer#even?" do
+ context "fixnum" do
+ it "returns true for a Fixnum when it is an even number" do
+ (-2).even?.should be_true
+ (-1).even?.should be_false
+
+ 0.even?.should be_true
+ 1.even?.should be_false
+ 2.even?.should be_true
+ end
+
+ it "returns true for a Bignum when it is an even number" do
+ bignum_value(0).even?.should be_true
+ bignum_value(1).even?.should be_false
+
+ (-bignum_value(0)).even?.should be_true
+ (-bignum_value(1)).even?.should be_false
+ end
+ end
+
+ context "bignum" do
+ it "returns true if self is even and positive" do
+ (10000**10).even?.should be_true
+ end
+
+ it "returns true if self is even and negative" do
+ (-10000**10).even?.should be_true
+ end
+
+ it "returns false if self is odd and positive" do
+ (9879**976).even?.should be_false
+ end
+
+ it "returns false if self is odd and negative" do
+ (-9879**976).even?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/exponent_spec.rb b/spec/ruby/core/integer/exponent_spec.rb
new file mode 100644
index 0000000000..c610661aff
--- /dev/null
+++ b/spec/ruby/core/integer/exponent_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/exponent'
+
+describe "Integer#**" do
+ it_behaves_like :integer_exponent, :**
+end
diff --git a/spec/ruby/core/integer/fdiv_spec.rb b/spec/ruby/core/integer/fdiv_spec.rb
new file mode 100644
index 0000000000..b27e6f1389
--- /dev/null
+++ b/spec/ruby/core/integer/fdiv_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+
+describe "Integer#fdiv" do
+ it "performs floating-point division between self and a fixnum" do
+ 8.fdiv(7).should be_close(1.14285714285714, TOLERANCE)
+ end
+
+ it "performs floating-point division between self and a bignum" do
+ 8.fdiv(bignum_value).should be_close(8.673617379884035e-19, TOLERANCE)
+ end
+
+ it "performs floating-point division between self and a Float" do
+ 8.fdiv(9.0).should be_close(0.888888888888889, TOLERANCE)
+ end
+
+ it "returns NaN when the argument is NaN" do
+ -1.fdiv(nan_value).nan?.should be_true
+ 1.fdiv(nan_value).nan?.should be_true
+ end
+
+ it "returns Infinity when the argument is 0" do
+ 1.fdiv(0).infinite?.should == 1
+ end
+
+ it "returns -Infinity when the argument is 0 and self is negative" do
+ -1.fdiv(0).infinite?.should == -1
+ end
+
+ it "returns Infinity when the argument is 0.0" do
+ 1.fdiv(0.0).infinite?.should == 1
+ end
+
+ it "returns -Infinity when the argument is 0.0 and self is negative" do
+ -1.fdiv(0.0).infinite?.should == -1
+ end
+
+ it "raises a TypeError when argument isn't numeric" do
+ lambda { 1.fdiv(mock('non-numeric')) }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError when passed multiple arguments" do
+ lambda { 1.fdiv(6,0.2) }.should raise_error(ArgumentError)
+ end
+
+ it "follows the coercion protocol" do
+ (obj = mock('10')).should_receive(:coerce).with(1).and_return([1, 10])
+ 1.fdiv(obj).should == 0.1
+ end
+end
diff --git a/spec/ruby/core/integer/fixtures/classes.rb b/spec/ruby/core/integer/fixtures/classes.rb
new file mode 100644
index 0000000000..6ebfbd1565
--- /dev/null
+++ b/spec/ruby/core/integer/fixtures/classes.rb
@@ -0,0 +1,4 @@
+module IntegerSpecs
+ class CoerceError < StandardError
+ end
+end
diff --git a/spec/ruby/core/integer/floor_spec.rb b/spec/ruby/core/integer/floor_spec.rb
new file mode 100644
index 0000000000..58439c98c1
--- /dev/null
+++ b/spec/ruby/core/integer/floor_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_i'
+require_relative 'shared/integer_rounding'
+
+describe "Integer#floor" do
+ it_behaves_like :integer_to_i, :floor
+ it_behaves_like :integer_rounding_positive_precision, :floor
+
+ ruby_version_is "2.4" do
+ context "precision argument specified as part of the floor method is negative" do
+ it "returns the largest integer less than self with at least precision.abs trailing zeros" do
+ 1832.floor(-1).should eql(1830)
+ 1832.floor(-2).should eql(1800)
+ 1832.floor(-3).should eql(1000)
+ -1832.floor(-1).should eql(-1840)
+ -1832.floor(-2).should eql(-1900)
+ -1832.floor(-3).should eql(-2000)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/gcd_spec.rb b/spec/ruby/core/integer/gcd_spec.rb
new file mode 100644
index 0000000000..238bf66cc0
--- /dev/null
+++ b/spec/ruby/core/integer/gcd_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../../spec_helper'
+
+describe "Integer#gcd" do
+ it "returns self if equal to the argument" do
+ 1.gcd(1).should == 1
+ 398.gcd(398).should == 398
+ end
+
+ it "returns an Integer" do
+ 36.gcd(6).should be_kind_of(Integer)
+ 4.gcd(20981).should be_kind_of(Integer)
+ end
+
+ it "returns the greatest common divisor of self and argument" do
+ 10.gcd(5).should == 5
+ 200.gcd(20).should == 20
+ end
+
+ it "returns a positive integer even if self is negative" do
+ -12.gcd(6).should == 6
+ -100.gcd(100).should == 100
+ end
+
+ it "returns a positive integer even if the argument is negative" do
+ 12.gcd(-6).should == 6
+ 100.gcd(-100).should == 100
+ end
+
+ it "returns a positive integer even if both self and argument are negative" do
+ -12.gcd(-6).should == 6
+ -100.gcd(-100).should == 100
+ end
+
+ it "accepts a Bignum argument" do
+ bignum = 9999**99
+ bignum.should be_kind_of(Bignum)
+ 99.gcd(bignum).should == 99
+ end
+
+ it "works if self is a Bignum" do
+ bignum = 9999**99
+ bignum.should be_kind_of(Bignum)
+ bignum.gcd(99).should == 99
+ end
+
+ it "doesn't cause an integer overflow" do
+ [2 ** (1.size * 8 - 2), 0x8000000000000000].each do |max|
+ [max - 1, max, max + 1].each do |num|
+ num.gcd(num).should == num
+ (-num).gcd(num).should == num
+ (-num).gcd(-num).should == num
+ num.gcd(-num).should == num
+ end
+ end
+ end
+
+ it "raises an ArgumentError if not given an argument" do
+ lambda { 12.gcd }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if given more than one argument" do
+ lambda { 12.gcd(30, 20) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError unless the argument is an Integer" do
+ lambda { 39.gcd(3.8) }.should raise_error(TypeError)
+ lambda { 45872.gcd([]) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/integer/gcdlcm_spec.rb b/spec/ruby/core/integer/gcdlcm_spec.rb
new file mode 100644
index 0000000000..362f8a51bd
--- /dev/null
+++ b/spec/ruby/core/integer/gcdlcm_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../spec_helper'
+
+describe "Integer#gcdlcm" do
+ it "returns [self, self] if self is equal to the argument" do
+ 1.gcdlcm(1).should == [1, 1]
+ 398.gcdlcm(398).should == [398, 398]
+ end
+
+ it "returns an Array" do
+ 36.gcdlcm(6).should be_kind_of(Array)
+ 4.gcdlcm(20981).should be_kind_of(Array)
+ end
+
+ it "returns a two-element Array" do
+ 36.gcdlcm(876).size.should == 2
+ 29.gcdlcm(17).size.should == 2
+ end
+
+ it "returns the greatest common divisor of self and argument as the first element" do
+ 10.gcdlcm(5)[0].should == 10.gcd(5)
+ 200.gcdlcm(20)[0].should == 200.gcd(20)
+ end
+
+ it "returns the least common multiple of self and argument as the last element" do
+ 10.gcdlcm(5)[1].should == 10.lcm(5)
+ 200.gcdlcm(20)[1].should == 200.lcm(20)
+ end
+
+ it "accepts a Bignum argument" do
+ bignum = 91999**99
+ bignum.should be_kind_of(Bignum)
+ 99.gcdlcm(bignum).should == [99.gcd(bignum), 99.lcm(bignum)]
+ end
+
+ it "works if self is a Bignum" do
+ bignum = 9999**89
+ bignum.should be_kind_of(Bignum)
+ bignum.gcdlcm(99).should == [bignum.gcd(99), bignum.lcm(99)]
+ end
+
+ it "raises an ArgumentError if not given an argument" do
+ lambda { 12.gcdlcm }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if given more than one argument" do
+ lambda { 12.gcdlcm(30, 20) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError unless the argument is an Integer" do
+ lambda { 39.gcdlcm(3.8) }.should raise_error(TypeError)
+ lambda { 45872.gcdlcm([]) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/integer/gt_spec.rb b/spec/ruby/core/integer/gt_spec.rb
new file mode 100644
index 0000000000..14c51c9acc
--- /dev/null
+++ b/spec/ruby/core/integer/gt_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+require_relative 'shared/comparison_coerce'
+
+describe "Integer#>" do
+ ruby_version_is "2.4"..."2.5" do
+ it_behaves_like :integer_comparison_coerce_rescue, :>
+ end
+
+ ruby_version_is "2.5" do
+ it_behaves_like :integer_comparison_coerce_not_rescue, :>
+ end
+
+ context "fixnum" do
+ it "returns true if self is greater than the given argument" do
+ (13 > 2).should == true
+ (-500 > -600).should == true
+
+ (1 > 5).should == false
+ (5 > 5).should == false
+
+ (900 > bignum_value).should == false
+ (5 > 4.999).should == true
+ end
+
+ it "raises an ArgumentError when given a non-Integer" do
+ lambda { 5 > "4" }.should raise_error(ArgumentError)
+ lambda { 5 > mock('x') }.should raise_error(ArgumentError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value(732)
+ end
+
+ it "returns true if self is greater than the given argument" do
+ (@bignum > (@bignum - 1)).should == true
+ (@bignum > 14.6).should == true
+ (@bignum > 10).should == true
+
+ (@bignum > (@bignum + 500)).should == false
+ end
+
+ it "raises an ArgumentError when given a non-Integer" do
+ lambda { @bignum > "4" }.should raise_error(ArgumentError)
+ lambda { @bignum > mock('str') }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/gte_spec.rb b/spec/ruby/core/integer/gte_spec.rb
new file mode 100644
index 0000000000..3d5c48faa5
--- /dev/null
+++ b/spec/ruby/core/integer/gte_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+require_relative 'shared/comparison_coerce'
+
+describe "Integer#>=" do
+ ruby_version_is "2.4"..."2.5" do
+ it_behaves_like :integer_comparison_coerce_rescue, :>=
+ end
+
+ ruby_version_is "2.5" do
+ it_behaves_like :integer_comparison_coerce_not_rescue, :>=
+ end
+
+ context "fixnum" do
+ it "returns true if self is greater than or equal to the given argument" do
+ (13 >= 2).should == true
+ (-500 >= -600).should == true
+
+ (1 >= 5).should == false
+ (2 >= 2).should == true
+ (5 >= 5).should == true
+
+ (900 >= bignum_value).should == false
+ (5 >= 4.999).should == true
+ end
+
+ it "raises an ArgumentError when given a non-Integer" do
+ lambda { 5 >= "4" }.should raise_error(ArgumentError)
+ lambda { 5 >= mock('x') }.should raise_error(ArgumentError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value(14)
+ end
+
+ it "returns true if self is greater than or equal to other" do
+ (@bignum >= @bignum).should == true
+ (@bignum >= (@bignum + 2)).should == false
+ (@bignum >= 5664.2).should == true
+ (@bignum >= 4).should == true
+ end
+
+ it "raises an ArgumentError when given a non-Integer" do
+ lambda { @bignum >= "4" }.should raise_error(ArgumentError)
+ lambda { @bignum >= mock('str') }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/integer_spec.rb b/spec/ruby/core/integer/integer_spec.rb
new file mode 100644
index 0000000000..6db2d50346
--- /dev/null
+++ b/spec/ruby/core/integer/integer_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+
+describe "Integer" do
+ it "includes Comparable" do
+ Integer.include?(Comparable).should == true
+ end
+
+ ruby_version_is "2.4" do
+ it "is the class of both small and large integers" do
+ 42.class.should equal(Integer)
+ bignum_value.class.should equal(Integer)
+ end
+ end
+end
+
+describe "Integer#integer?" do
+ it "returns true for Integers" do
+ 0.integer?.should == true
+ -1.integer?.should == true
+ bignum_value.integer?.should == true
+ end
+end
diff --git a/spec/ruby/core/integer/lcm_spec.rb b/spec/ruby/core/integer/lcm_spec.rb
new file mode 100644
index 0000000000..6d608bdf41
--- /dev/null
+++ b/spec/ruby/core/integer/lcm_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../../spec_helper'
+
+describe "Integer#lcm" do
+ it "returns self if equal to the argument" do
+ 1.lcm(1).should == 1
+ 398.lcm(398).should == 398
+ end
+
+ it "returns an Integer" do
+ 36.lcm(6).should be_kind_of(Integer)
+ 4.lcm(20981).should be_kind_of(Integer)
+ end
+
+ it "returns the least common multiple of self and argument" do
+ 200.lcm(2001).should == 400200
+ 99.lcm(90).should == 990
+ end
+
+ it "returns a positive integer even if self is negative" do
+ -12.lcm(6).should == 12
+ -100.lcm(100).should == 100
+ end
+
+ it "returns a positive integer even if the argument is negative" do
+ 12.lcm(-6).should == 12
+ 100.lcm(-100).should == 100
+ end
+
+ it "returns a positive integer even if both self and argument are negative" do
+ -12.lcm(-6).should == 12
+ -100.lcm(-100).should == 100
+ end
+
+ it "accepts a Bignum argument" do
+ bignum = 9999**99
+ bignum.should be_kind_of(Bignum)
+ 99.lcm(bignum).should == bignum
+ end
+
+ it "works if self is a Bignum" do
+ bignum = 9999**99
+ bignum.should be_kind_of(Bignum)
+ bignum.lcm(99).should == bignum
+ end
+
+ it "raises an ArgumentError if not given an argument" do
+ lambda { 12.lcm }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if given more than one argument" do
+ lambda { 12.lcm(30, 20) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError unless the argument is an Integer" do
+ lambda { 39.lcm(3.8) }.should raise_error(TypeError)
+ lambda { 45872.lcm([]) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/integer/left_shift_spec.rb b/spec/ruby/core/integer/left_shift_spec.rb
new file mode 100644
index 0000000000..a240c541c5
--- /dev/null
+++ b/spec/ruby/core/integer/left_shift_spec.rb
@@ -0,0 +1,165 @@
+require_relative '../../spec_helper'
+
+describe "Integer#<< (with n << m)" do
+ context "fixnum" do
+ it "returns n shifted left m bits when n > 0, m > 0" do
+ (1 << 1).should == 2
+ end
+
+ it "returns n shifted left m bits when n < 0, m > 0" do
+ (-1 << 1).should == -2
+ (-7 << 1).should == -14
+ (-42 << 2).should == -168
+ end
+
+ it "returns n shifted right m bits when n > 0, m < 0" do
+ (2 << -1).should == 1
+ end
+
+ it "returns n shifted right m bits when n < 0, m < 0" do
+ (-2 << -1).should == -1
+ end
+
+ it "returns 0 when n == 0" do
+ (0 << 1).should == 0
+ end
+
+ it "returns n when n > 0, m == 0" do
+ (1 << 0).should == 1
+ end
+
+ it "returns n when n < 0, m == 0" do
+ (-1 << 0).should == -1
+ end
+
+ it "returns 0 when n > 0, m < 0 and n < 2**-m" do
+ (3 << -2).should == 0
+ (7 << -3).should == 0
+ (127 << -7).should == 0
+
+ # To make sure the exponent is not truncated
+ (7 << -32).should == 0
+ (7 << -64).should == 0
+ end
+
+ it "returns -1 when n < 0, m < 0 and n > -(2**-m)" do
+ (-3 << -2).should == -1
+ (-7 << -3).should == -1
+ (-127 << -7).should == -1
+
+ # To make sure the exponent is not truncated
+ (-7 << -32).should == -1
+ (-7 << -64).should == -1
+ end
+
+ it "returns 0 when m < 0 and m is a Bignum" do
+ (3 << -bignum_value).should == 0
+ end
+
+ it "returns an Bignum == fixnum_max * 2 when fixnum_max << 1 and n > 0" do
+ result = fixnum_max << 1
+ result.should be_an_instance_of(Bignum)
+ result.should == fixnum_max * 2
+ end
+
+ it "returns an Bignum == fixnum_min * 2 when fixnum_min << 1 and n < 0" do
+ result = fixnum_min << 1
+ result.should be_an_instance_of(Bignum)
+ result.should == fixnum_min * 2
+ end
+
+ it "calls #to_int to convert the argument to an Integer" do
+ obj = mock("4")
+ obj.should_receive(:to_int).and_return(4)
+
+ (3 << obj).should == 48
+ end
+
+ it "raises a TypeError when #to_int does not return an Integer" do
+ obj = mock("a string")
+ obj.should_receive(:to_int).and_return("asdf")
+
+ lambda { 3 << obj }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { 3 << nil }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a String" do
+ lambda { 3 << "4" }.should raise_error(TypeError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value * 16
+ end
+
+ it "returns n shifted left m bits when n > 0, m > 0" do
+ (@bignum << 4).should == 2361183241434822606848
+ end
+
+ it "returns n shifted left m bits when n < 0, m > 0" do
+ (-@bignum << 9).should == -75557863725914323419136
+ end
+
+ it "returns n shifted right m bits when n > 0, m < 0" do
+ (@bignum << -1).should == 73786976294838206464
+ end
+
+ it "returns n shifted right m bits when n < 0, m < 0" do
+ (-@bignum << -2).should == -36893488147419103232
+ end
+
+ it "returns n when n > 0, m == 0" do
+ (@bignum << 0).should == @bignum
+ end
+
+ it "returns n when n < 0, m == 0" do
+ (-@bignum << 0).should == -@bignum
+ end
+
+ it "returns 0 when m < 0 and m == p where 2**p > n >= 2**(p-1)" do
+ (@bignum << -68).should == 0
+ end
+
+ it "returns 0 when m < 0 and m is a Bignum" do
+ (@bignum << -bignum_value).should == 0
+ end
+
+ it "returns a Fixnum == fixnum_max when (fixnum_max * 2) << -1 and n > 0" do
+ result = (fixnum_max * 2) << -1
+ result.should be_an_instance_of(Fixnum)
+ result.should == fixnum_max
+ end
+
+ it "returns a Fixnum == fixnum_min when (fixnum_min * 2) << -1 and n < 0" do
+ result = (fixnum_min * 2) << -1
+ result.should be_an_instance_of(Fixnum)
+ result.should == fixnum_min
+ end
+
+ it "calls #to_int to convert the argument to an Integer" do
+ obj = mock("4")
+ obj.should_receive(:to_int).and_return(4)
+
+ (@bignum << obj).should == 2361183241434822606848
+ end
+
+ it "raises a TypeError when #to_int does not return an Integer" do
+ obj = mock("a string")
+ obj.should_receive(:to_int).and_return("asdf")
+
+ lambda { @bignum << obj }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { @bignum << nil }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a String" do
+ lambda { @bignum << "4" }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/lt_spec.rb b/spec/ruby/core/integer/lt_spec.rb
new file mode 100644
index 0000000000..a33a297d8b
--- /dev/null
+++ b/spec/ruby/core/integer/lt_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require_relative 'shared/comparison_coerce'
+
+describe "Integer#<" do
+ ruby_version_is "2.4"..."2.5" do
+ it_behaves_like :integer_comparison_coerce_rescue, :<
+ end
+
+ ruby_version_is "2.5" do
+ it_behaves_like :integer_comparison_coerce_not_rescue, :<
+ end
+
+ context "fixnum" do
+ it "returns true if self is less than the given argument" do
+ (2 < 13).should == true
+ (-600 < -500).should == true
+
+ (5 < 1).should == false
+ (5 < 5).should == false
+
+ (900 < bignum_value).should == true
+ (5 < 4.999).should == false
+ end
+
+ it "raises an ArgumentError when given a non-Integer" do
+ lambda { 5 < "4" }.should raise_error(ArgumentError)
+ lambda { 5 < mock('x') }.should raise_error(ArgumentError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value(32)
+ end
+
+ it "returns true if self is less than the given argument" do
+ (@bignum < @bignum + 1).should == true
+ (-@bignum < -(@bignum - 1)).should == true
+
+ (@bignum < 1).should == false
+ (@bignum < 5).should == false
+
+ (@bignum < 4.999).should == false
+ end
+
+ it "raises an ArgumentError when given a non-Integer" do
+ lambda { @bignum < "4" }.should raise_error(ArgumentError)
+ lambda { @bignum < mock('str') }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/lte_spec.rb b/spec/ruby/core/integer/lte_spec.rb
new file mode 100644
index 0000000000..1d3ecea2ac
--- /dev/null
+++ b/spec/ruby/core/integer/lte_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+require_relative 'shared/comparison_coerce'
+
+describe "Integer#<=" do
+ ruby_version_is "2.4"..."2.5" do
+ it_behaves_like :integer_comparison_coerce_rescue, :<=
+ end
+
+ ruby_version_is "2.5" do
+ it_behaves_like :integer_comparison_coerce_not_rescue, :<=
+ end
+
+ context "fixnum" do
+ it "returns true if self is less than or equal to other" do
+ (2 <= 13).should == true
+ (-600 <= -500).should == true
+
+ (5 <= 1).should == false
+ (5 <= 5).should == true
+ (-2 <= -2).should == true
+
+ (900 <= bignum_value).should == true
+ (5 <= 4.999).should == false
+ end
+
+ it "raises an ArgumentError when given a non-Integer" do
+ lambda { 5 <= "4" }.should raise_error(ArgumentError)
+ lambda { 5 <= mock('x') }.should raise_error(ArgumentError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value(39)
+ end
+
+ it "returns true if self is less than or equal to other" do
+ (@bignum <= @bignum).should == true
+ (-@bignum <= -(@bignum - 1)).should == true
+
+ (@bignum <= 4.999).should == false
+ end
+
+ it "returns false if compares with near float" do
+ (@bignum <= (@bignum + 0.0)).should == false
+ (@bignum <= (@bignum + 0.5)).should == false
+ end
+
+ it "raises an ArgumentError when given a non-Integer" do
+ lambda { @bignum <= "4" }.should raise_error(ArgumentError)
+ lambda { @bignum <= mock('str') }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/magnitude_spec.rb b/spec/ruby/core/integer/magnitude_spec.rb
new file mode 100644
index 0000000000..48cf1a8534
--- /dev/null
+++ b/spec/ruby/core/integer/magnitude_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/abs'
+
+describe "Integer#magnitude" do
+ it_behaves_like :integer_abs, :magnitude
+end
diff --git a/spec/ruby/core/integer/minus_spec.rb b/spec/ruby/core/integer/minus_spec.rb
new file mode 100644
index 0000000000..84db427172
--- /dev/null
+++ b/spec/ruby/core/integer/minus_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arithmetic_coerce'
+
+describe "Integer#-" do
+ ruby_version_is "2.4"..."2.5" do
+ it_behaves_like :integer_arithmetic_coerce_rescue, :-
+ end
+
+ ruby_version_is "2.5" do
+ it_behaves_like :integer_arithmetic_coerce_not_rescue, :-
+ end
+
+ context "fixnum" do
+ it "returns self minus the given Integer" do
+ (5 - 10).should == -5
+ (9237212 - 5_280).should == 9231932
+
+ (781 - 0.5).should == 780.5
+ (2_560_496 - bignum_value).should == -9223372036852215312
+ end
+
+ it "raises a TypeError when given a non-Integer" do
+ lambda {
+ (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10)
+ 13 - obj
+ }.should raise_error(TypeError)
+ lambda { 13 - "10" }.should raise_error(TypeError)
+ lambda { 13 - :symbol }.should raise_error(TypeError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value(314)
+ end
+
+ it "returns self minus the given Integer" do
+ (@bignum - 9).should == 9223372036854776113
+ (@bignum - 12.57).should be_close(9223372036854776109.43, TOLERANCE)
+ (@bignum - bignum_value(42)).should == 272
+ end
+
+ it "raises a TypeError when given a non-Integer" do
+ lambda { @bignum - mock('10') }.should raise_error(TypeError)
+ lambda { @bignum - "10" }.should raise_error(TypeError)
+ lambda { @bignum - :symbol }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/modulo_spec.rb b/spec/ruby/core/integer/modulo_spec.rb
new file mode 100644
index 0000000000..e263338e38
--- /dev/null
+++ b/spec/ruby/core/integer/modulo_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'shared/modulo'
+
+describe "Integer#%" do
+ it_behaves_like :integer_modulo, :%
+end
+
+describe "Integer#modulo" do
+ it_behaves_like :integer_modulo, :modulo
+end
diff --git a/spec/ruby/core/integer/multiply_spec.rb b/spec/ruby/core/integer/multiply_spec.rb
new file mode 100644
index 0000000000..1683b0c96e
--- /dev/null
+++ b/spec/ruby/core/integer/multiply_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arithmetic_coerce'
+
+describe "Integer#*" do
+ ruby_version_is "2.4"..."2.5" do
+ it_behaves_like :integer_arithmetic_coerce_rescue, :*
+ end
+
+ ruby_version_is "2.5" do
+ it_behaves_like :integer_arithmetic_coerce_not_rescue, :*
+ end
+
+ context "fixnum" do
+ it "returns self multiplied by the given Integer" do
+ (4923 * 2).should == 9846
+ (1342177 * 800).should == 1073741600
+ (65536 * 65536).should == 4294967296
+
+ (256 * bignum_value).should == 2361183241434822606848
+ (6712 * 0.25).should == 1678.0
+ end
+
+ it "raises a TypeError when given a non-Integer" do
+ lambda {
+ (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10)
+ 13 * obj
+ }.should raise_error(TypeError)
+ lambda { 13 * "10" }.should raise_error(TypeError)
+ lambda { 13 * :symbol }.should raise_error(TypeError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value(772)
+ end
+
+ it "returns self multiplied by the given Integer" do
+ (@bignum * (1/bignum_value(0xffff).to_f)).should be_close(1.0, TOLERANCE)
+ (@bignum * (1/bignum_value(0xffff).to_f)).should be_close(1.0, TOLERANCE)
+ (@bignum * 10).should == 92233720368547765800
+ (@bignum * (@bignum - 40)).should == 85070591730234629737795195287525433200
+ end
+
+ it "raises a TypeError when given a non-Integer" do
+ lambda { @bignum * mock('10') }.should raise_error(TypeError)
+ lambda { @bignum * "10" }.should raise_error(TypeError)
+ lambda { @bignum * :symbol }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/next_spec.rb b/spec/ruby/core/integer/next_spec.rb
new file mode 100644
index 0000000000..63c4e67893
--- /dev/null
+++ b/spec/ruby/core/integer/next_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/next'
+
+describe "Integer#next" do
+ it_behaves_like :integer_next, :next
+end
diff --git a/spec/ruby/core/integer/nobits_spec.rb b/spec/ruby/core/integer/nobits_spec.rb
new file mode 100644
index 0000000000..da689de5df
--- /dev/null
+++ b/spec/ruby/core/integer/nobits_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '2.5' do
+ describe "Integer#nobits?" do
+ it "returns true iff all no bits of the argument are set in the receiver" do
+ 42.nobits?(42).should == false
+ 0b1010_1010.nobits?(0b1000_0010).should == false
+ 0b1010_1010.nobits?(0b1000_0001).should == false
+ 0b0100_0101.nobits?(0b1010_1010).should == true
+ different_bignum = (2 * bignum_value) & (~bignum_value)
+ (0b1010_1010 | different_bignum).nobits?(0b1000_0010 | bignum_value).should == false
+ (0b1010_1010 | different_bignum).nobits?(0b1000_0001 | bignum_value).should == false
+ (0b0100_0101 | different_bignum).nobits?(0b1010_1010 | bignum_value).should == true
+ end
+
+ it "handles negative values using two's complement notation" do
+ (~0b1101).nobits?(0b1101).should == true
+ (-42).nobits?(-42).should == false
+ (~0b1101).nobits?(~0b10).should == false
+ (~(0b1101 | bignum_value)).nobits?(~(0b10 | bignum_value)).should == false
+ end
+
+ it "coerces the rhs using to_int" do
+ obj = mock("the int 0b10")
+ obj.should_receive(:to_int).and_return(0b10)
+ 0b110.nobits?(obj).should == false
+ end
+
+ it "raises a TypeError when given a non-Integer" do
+ lambda {
+ (obj = mock('10')).should_receive(:coerce).any_number_of_times.and_return([42,10])
+ 13.nobits?(obj)
+ }.should raise_error(TypeError)
+ lambda { 13.nobits?("10") }.should raise_error(TypeError)
+ lambda { 13.nobits?(:symbol) }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/numerator_spec.rb b/spec/ruby/core/integer/numerator_spec.rb
new file mode 100644
index 0000000000..f4149d770e
--- /dev/null
+++ b/spec/ruby/core/integer/numerator_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+
+describe "Integer#numerator" do
+ before :all do
+ @numbers = [
+ 0,
+ 29871,
+ 99999999999999**99,
+ 72628191273,
+ ].map{|n| [-n, n]}.flatten
+ end
+
+ it "returns self" do
+ @numbers.each do |number|
+ number.numerator.should == number
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/odd_spec.rb b/spec/ruby/core/integer/odd_spec.rb
new file mode 100644
index 0000000000..dd779fa44b
--- /dev/null
+++ b/spec/ruby/core/integer/odd_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+
+describe "Integer#odd?" do
+ context "fixnum" do
+ it "returns true when self is an odd number" do
+ (-2).odd?.should be_false
+ (-1).odd?.should be_true
+
+ 0.odd?.should be_false
+ 1.odd?.should be_true
+ 2.odd?.should be_false
+
+ bignum_value(0).odd?.should be_false
+ bignum_value(1).odd?.should be_true
+
+ (-bignum_value(0)).odd?.should be_false
+ (-bignum_value(1)).odd?.should be_true
+ end
+ end
+
+ context "bignum" do
+ it "returns true if self is odd and positive" do
+ (987279**19).odd?.should be_true
+ end
+
+ it "returns true if self is odd and negative" do
+ (-9873389**97).odd?.should be_true
+ end
+
+ it "returns false if self is even and positive" do
+ (10000000**10).odd?.should be_false
+ end
+
+ it "returns false if self is even and negative" do
+ (-1000000**100).odd?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/ord_spec.rb b/spec/ruby/core/integer/ord_spec.rb
new file mode 100644
index 0000000000..bcb57bea98
--- /dev/null
+++ b/spec/ruby/core/integer/ord_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "Integer#ord" do
+ it "returns self" do
+ 20.ord.should eql(20)
+ 40.ord.should eql(40)
+
+ 0.ord.should eql(0)
+ (-10).ord.should eql(-10)
+
+ ?a.ord.should eql(97)
+ ?Z.ord.should eql(90)
+
+ bignum_value.ord.should eql(bignum_value)
+ (-bignum_value).ord.should eql(-bignum_value)
+ end
+end
diff --git a/spec/ruby/core/integer/plus_spec.rb b/spec/ruby/core/integer/plus_spec.rb
new file mode 100644
index 0000000000..2880840bc1
--- /dev/null
+++ b/spec/ruby/core/integer/plus_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arithmetic_coerce'
+
+describe "Integer#+" do
+ ruby_version_is "2.4"..."2.5" do
+ it_behaves_like :integer_arithmetic_coerce_rescue, :+
+ end
+
+ ruby_version_is "2.5" do
+ it_behaves_like :integer_arithmetic_coerce_not_rescue, :+
+ end
+
+ context "fixnum" do
+ it "returns self plus the given Integer" do
+ (491 + 2).should == 493
+ (90210 + 10).should == 90220
+
+ (9 + bignum_value).should == 9223372036854775817
+ (1001 + 5.219).should == 1006.219
+ end
+
+ it "raises a TypeError when given a non-Integer" do
+ lambda {
+ (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10)
+ 13 + obj
+ }.should raise_error(TypeError)
+ lambda { 13 + "10" }.should raise_error(TypeError)
+ lambda { 13 + :symbol }.should raise_error(TypeError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value(76)
+ end
+
+ it "returns self plus the given Integer" do
+ (@bignum + 4).should == 9223372036854775888
+ (@bignum + 4.2).should be_close(9223372036854775888.2, TOLERANCE)
+ (@bignum + bignum_value(3)).should == 18446744073709551695
+ end
+
+ it "raises a TypeError when given a non-Integer" do
+ lambda { @bignum + mock('10') }.should raise_error(TypeError)
+ lambda { @bignum + "10" }.should raise_error(TypeError)
+ lambda { @bignum + :symbol}.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/pow_spec.rb b/spec/ruby/core/integer/pow_spec.rb
new file mode 100644
index 0000000000..fb0ba996bc
--- /dev/null
+++ b/spec/ruby/core/integer/pow_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/exponent'
+
+ruby_version_is "2.5" do
+ describe "Integer#pow" do
+ context "one argument is passed" do
+ it_behaves_like :integer_exponent, :pow
+ end
+
+ context "two arguments are passed" do
+ it "returns modulo of self raised to the given power" do
+ 2.pow(5, 12).should == 8
+ 2.pow(6, 13).should == 12
+ 2.pow(7, 14).should == 2
+ 2.pow(8, 15).should == 1
+ end
+
+ ruby_bug '#13669', '2.5'...'2.5.1' do
+ it "works well with bignums" do
+ 2.pow(61, 5843009213693951).should eql 3697379018277258
+ 2.pow(62, 5843009213693952).should eql 1551748822859776
+ 2.pow(63, 5843009213693953).should eql 3103497645717974
+ 2.pow(64, 5843009213693954).should eql 363986077738838
+ end
+ end
+
+ it "handles sign like #divmod does" do
+ 2.pow(5, 12).should == 8
+ 2.pow(5, -12).should == -4
+ -2.pow(5, 12).should == 4
+ -2.pow(5, -12).should == -8
+ end
+
+ it "ensures all arguments are integers" do
+ -> { 2.pow(5, 12.0) }.should raise_error(TypeError, /2nd argument not allowed unless all arguments are integers/)
+ -> { 2.pow(5, Rational(12, 1)) }.should raise_error(TypeError, /2nd argument not allowed unless all arguments are integers/)
+ end
+
+ it "raises TypeError for non-numeric value" do
+ -> { 2.pow(5, "12") }.should raise_error(TypeError)
+ -> { 2.pow(5, []) }.should raise_error(TypeError)
+ -> { 2.pow(5, nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a ZeroDivisionError when the given argument is 0" do
+ -> { 2.pow(5, 0) }.should raise_error(ZeroDivisionError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/pred_spec.rb b/spec/ruby/core/integer/pred_spec.rb
new file mode 100644
index 0000000000..86750ebfd1
--- /dev/null
+++ b/spec/ruby/core/integer/pred_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Integer#pred" do
+ it "returns the Integer equal to self - 1" do
+ 0.pred.should eql(-1)
+ -1.pred.should eql(-2)
+ bignum_value.pred.should eql(bignum_value(-1))
+ fixnum_min.pred.should eql(fixnum_min - 1)
+ 20.pred.should eql(19)
+ end
+end
diff --git a/spec/ruby/core/integer/rationalize_spec.rb b/spec/ruby/core/integer/rationalize_spec.rb
new file mode 100644
index 0000000000..590ab40cb2
--- /dev/null
+++ b/spec/ruby/core/integer/rationalize_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+
+describe "Integer#rationalize" do
+ before :all do
+ @numbers = [
+ 0,
+ 29871,
+ 99999999999999**99,
+ -72628191273,
+ ]
+ end
+
+ it "returns a Rational object" do
+ @numbers.each do |number|
+ number.rationalize.should be_an_instance_of(Rational)
+ end
+ end
+
+ it "uses self as the numerator" do
+ @numbers.each do |number|
+ number.rationalize.numerator.should == number
+ end
+ end
+
+ it "uses 1 as the denominator" do
+ @numbers.each do |number|
+ number.rationalize.denominator.should == 1
+ end
+ end
+
+ it "ignores a single argument" do
+ 1.rationalize(0.1).should == Rational(1,1)
+ end
+
+ it "raises ArgumentError when passed more than one argument" do
+ lambda { 1.rationalize(0.1, 0.1) }.should raise_error(ArgumentError)
+ lambda { 1.rationalize(0.1, 0.1, 2) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/integer/remainder_spec.rb b/spec/ruby/core/integer/remainder_spec.rb
new file mode 100644
index 0000000000..473c59a2f0
--- /dev/null
+++ b/spec/ruby/core/integer/remainder_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+
+describe "Integer#remainder" do
+ context "fixnum" do
+ it "returns the remainder of dividing self by other" do
+ 5.remainder(3).should == 2
+ 5.remainder(3.0).should == 2.0
+ 5.remainder(Rational(3, 1)).should == Rational(2, 1)
+ end
+
+ it "means x-y*(x/y).truncate" do
+ 5.remainder(3).should == 2
+ 5.remainder(3.3).should be_close(1.7, TOLERANCE)
+ 5.remainder(3.7).should be_close(1.3, TOLERANCE)
+ end
+
+ it "keeps sign of self" do
+ 5.remainder( 3).should == 2
+ 5.remainder(-3).should == 2
+ -5.remainder( 3).should == -2
+ -5.remainder(-3).should == -2
+ end
+
+ it "raises TypeError if passed non-numeric argument" do
+ -> { 5.remainder("3") }.should raise_error(TypeError)
+ -> { 5.remainder(:"3") }.should raise_error(TypeError)
+ -> { 5.remainder([]) }.should raise_error(TypeError)
+ -> { 5.remainder(nil) }.should raise_error(TypeError)
+ end
+ end
+
+ context "bignum" do
+ it "returns the remainder of dividing self by other" do
+ a = bignum_value(79)
+ a.remainder(2).should == 1
+ a.remainder(97.345).should be_close(46.5674996147722, TOLERANCE)
+ a.remainder(bignum_value).should == 79
+ end
+
+ it "raises a ZeroDivisionError if other is zero and not a Float" do
+ lambda { bignum_value(66).remainder(0) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "does raises ZeroDivisionError if other is zero and a Float" do
+ a = bignum_value(7)
+ b = bignum_value(32)
+ lambda { a.remainder(0.0) }.should raise_error(ZeroDivisionError)
+ lambda { b.remainder(-0.0) }.should raise_error(ZeroDivisionError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/right_shift_spec.rb b/spec/ruby/core/integer/right_shift_spec.rb
new file mode 100644
index 0000000000..641d4da662
--- /dev/null
+++ b/spec/ruby/core/integer/right_shift_spec.rb
@@ -0,0 +1,191 @@
+require_relative '../../spec_helper'
+
+describe "Integer#>> (with n >> m)" do
+ context "fixnum" do
+ it "returns n shifted right m bits when n > 0, m > 0" do
+ (2 >> 1).should == 1
+ end
+
+ it "returns n shifted right m bits when n < 0, m > 0" do
+ (-2 >> 1).should == -1
+ (-7 >> 1).should == -4
+ (-42 >> 2).should == -11
+ end
+
+ it "returns n shifted left m bits when n > 0, m < 0" do
+ (1 >> -1).should == 2
+ end
+
+ it "returns n shifted left m bits when n < 0, m < 0" do
+ (-1 >> -1).should == -2
+ end
+
+ it "returns 0 when n == 0" do
+ (0 >> 1).should == 0
+ end
+
+ it "returns n when n > 0, m == 0" do
+ (1 >> 0).should == 1
+ end
+
+ it "returns n when n < 0, m == 0" do
+ (-1 >> 0).should == -1
+ end
+
+ it "returns 0 when n > 0, m > 0 and n < 2**m" do
+ (3 >> 2).should == 0
+ (7 >> 3).should == 0
+ (127 >> 7).should == 0
+
+ # To make sure the exponent is not truncated
+ (7 >> 32).should == 0
+ (7 >> 64).should == 0
+ end
+
+ it "returns -1 when n < 0, m > 0 and n > -(2**m)" do
+ (-3 >> 2).should == -1
+ (-7 >> 3).should == -1
+ (-127 >> 7).should == -1
+
+ # To make sure the exponent is not truncated
+ (-7 >> 32).should == -1
+ (-7 >> 64).should == -1
+ end
+
+ it "returns 0 when m is a bignum" do
+ (3 >> bignum_value).should == 0
+ end
+
+ it "returns an Bignum == fixnum_max * 2 when fixnum_max >> -1 and n > 0" do
+ result = fixnum_max >> -1
+ result.should be_an_instance_of(Bignum)
+ result.should == fixnum_max * 2
+ end
+
+ it "returns an Bignum == fixnum_min * 2 when fixnum_min >> -1 and n < 0" do
+ result = fixnum_min >> -1
+ result.should be_an_instance_of(Bignum)
+ result.should == fixnum_min * 2
+ end
+
+ it "calls #to_int to convert the argument to an Integer" do
+ obj = mock("2")
+ obj.should_receive(:to_int).and_return(2)
+
+ (8 >> obj).should == 2
+ end
+
+ it "raises a TypeError when #to_int does not return an Integer" do
+ obj = mock("a string")
+ obj.should_receive(:to_int).and_return("asdf")
+
+ lambda { 3 >> obj }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { 3 >> nil }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a String" do
+ lambda { 3 >> "4" }.should raise_error(TypeError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value * 16
+ end
+
+ it "returns n shifted right m bits when n > 0, m > 0" do
+ (@bignum >> 1).should == 73786976294838206464
+ end
+
+ it "returns n shifted right m bits when n < 0, m > 0" do
+ (-@bignum >> 2).should == -36893488147419103232
+ end
+
+ it "respects twos complement signed shifting" do
+ # This explicit left hand value is important because it is the
+ # exact bit pattern that matters, so it's important it's right
+ # here to show the significance.
+ #
+
+ (-42949672980000000000000 >> 14).should == -2621440001220703125
+ (-42949672980000000000001 >> 14).should == -2621440001220703126
+ # Note the off by one -------------------- ^^^^^^^^^^^^^^^^^^^^
+ # This is because even though we discard the lowest bit, in twos
+ # complement it would influence the bits to the left of it.
+
+ (-42949672980000000000000 >> 15).should == -1310720000610351563
+ (-42949672980000000000001 >> 15).should == -1310720000610351563
+
+ (-0xfffffffffffffffff >> 32).should == -68719476736
+ end
+
+ it "respects twos complement signed shifting for very large values" do
+ giant = 42949672980000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ neg = -giant
+
+ (giant >> 84).should == 2220446050284288846538547929770901490087453566957265138626098632812
+ (neg >> 84).should == -2220446050284288846538547929770901490087453566957265138626098632813
+ end
+
+ it "returns n shifted left m bits when n > 0, m < 0" do
+ (@bignum >> -2).should == 590295810358705651712
+ end
+
+ it "returns n shifted left m bits when n < 0, m < 0" do
+ (-@bignum >> -3).should == -1180591620717411303424
+ end
+
+ it "returns n when n > 0, m == 0" do
+ (@bignum >> 0).should == @bignum
+ end
+
+ it "returns n when n < 0, m == 0" do
+ (-@bignum >> 0).should == -@bignum
+ end
+
+ it "returns 0 when m > 0 and m == p where 2**p > n >= 2**(p-1)" do
+ (@bignum >> 68).should == 0
+ end
+
+ it "returns 0 when m is a Bignum" do
+ (@bignum >> bignum_value).should == 0
+ end
+
+ it "returns a Fixnum == fixnum_max when (fixnum_max * 2) >> 1 and n > 0" do
+ result = (fixnum_max * 2) >> 1
+ result.should be_an_instance_of(Fixnum)
+ result.should == fixnum_max
+ end
+
+ it "returns a Fixnum == fixnum_min when (fixnum_min * 2) >> 1 and n < 0" do
+ result = (fixnum_min * 2) >> 1
+ result.should be_an_instance_of(Fixnum)
+ result.should == fixnum_min
+ end
+
+ it "calls #to_int to convert the argument to an Integer" do
+ obj = mock("2")
+ obj.should_receive(:to_int).and_return(2)
+
+ (@bignum >> obj).should == 36893488147419103232
+ end
+
+ it "raises a TypeError when #to_int does not return an Integer" do
+ obj = mock("a string")
+ obj.should_receive(:to_int).and_return("asdf")
+
+ lambda { @bignum >> obj }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { @bignum >> nil }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a String" do
+ lambda { @bignum >> "4" }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/round_spec.rb b/spec/ruby/core/integer/round_spec.rb
new file mode 100644
index 0000000000..aa6345fda5
--- /dev/null
+++ b/spec/ruby/core/integer/round_spec.rb
@@ -0,0 +1,95 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_i'
+require_relative 'shared/integer_rounding'
+
+describe "Integer#round" do
+ it_behaves_like :integer_to_i, :round
+ it_behaves_like :integer_rounding_positive_precision, :round
+
+ ruby_version_is ""..."2.5" do # Not just since 2.4
+ it "rounds itself as a float if passed a positive precision" do
+ [2, -4, 10**70, -10**100].each do |v|
+ v.round(42).should eql(v.to_f)
+ end
+ end
+ end
+
+ # redmine:5228
+ it "returns itself rounded if passed a negative value" do
+ +249.round(-2).should eql(+200)
+ -249.round(-2).should eql(-200)
+ (+25 * 10**70 - 1).round(-71).should eql(+20 * 10**70)
+ (-25 * 10**70 + 1).round(-71).should eql(-20 * 10**70)
+ end
+
+ it "returns itself rounded to nearest if passed a negative value" do
+ +250.round(-2).should eql(+300)
+ -250.round(-2).should eql(-300)
+ (+25 * 10**70).round(-71).should eql(+30 * 10**70)
+ (-25 * 10**70).round(-71).should eql(-30 * 10**70)
+ end
+
+ platform_is_not wordsize: 32 do
+ it "raises a RangeError when passed a big negative value" do
+ lambda { 42.round(fixnum_min) }.should raise_error(RangeError)
+ end
+ end
+
+ it "raises a RangeError when passed Float::INFINITY" do
+ lambda { 42.round(Float::INFINITY) }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError when passed a beyond signed int" do
+ lambda { 42.round(1<<31) }.should raise_error(RangeError)
+ end
+
+ it "raises a TypeError when passed a String" do
+ lambda { 42.round("4") }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when its argument cannot be converted to an Integer" do
+ lambda { 42.round(nil) }.should raise_error(TypeError)
+ end
+
+ it "calls #to_int on the argument to convert it to an Integer" do
+ obj = mock("Object")
+ obj.should_receive(:to_int).and_return(0)
+ 42.round(obj)
+ end
+
+ it "raises a TypeError when #to_int does not return an Integer" do
+ obj = mock("Object")
+ obj.stub!(:to_int).and_return([])
+ lambda { 42.round(obj) }.should raise_error(TypeError)
+ end
+
+ ruby_version_is "2.4" do
+ it "returns different rounded values depending on the half option" do
+ 25.round(-1, half: :up).should eql(30)
+ 25.round(-1, half: :down).should eql(20)
+ 25.round(-1, half: :even).should eql(20)
+ 35.round(-1, half: :up).should eql(40)
+ 35.round(-1, half: :down).should eql(30)
+ 35.round(-1, half: :even).should eql(40)
+ (-25).round(-1, half: :up).should eql(-30)
+ (-25).round(-1, half: :down).should eql(-20)
+ (-25).round(-1, half: :even).should eql(-20)
+ end
+ end
+
+ ruby_version_is "2.4"..."2.5" do
+ it "returns itself as a float if passed a positive precision and the half option" do
+ 35.round(1, half: :up).should eql(35.0)
+ 35.round(1, half: :down).should eql(35.0)
+ 35.round(1, half: :even).should eql(35.0)
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "returns itself if passed a positive precision and the half option" do
+ 35.round(1, half: :up).should eql(35)
+ 35.round(1, half: :down).should eql(35)
+ 35.round(1, half: :even).should eql(35)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/shared/abs.rb b/spec/ruby/core/integer/shared/abs.rb
new file mode 100644
index 0000000000..946aa21864
--- /dev/null
+++ b/spec/ruby/core/integer/shared/abs.rb
@@ -0,0 +1,18 @@
+describe :integer_abs, shared: true do
+ context "fixnum" do
+ it "returns self's absolute fixnum value" do
+ { 0 => [0, -0, +0], 2 => [2, -2, +2], 100 => [100, -100, +100] }.each do |key, values|
+ values.each do |value|
+ value.send(@method).should == key
+ end
+ end
+ end
+ end
+
+ context "bignum" do
+ it "returns the absolute bignum value" do
+ bignum_value(39).send(@method).should == 9223372036854775847
+ (-bignum_value(18)).send(@method).should == 9223372036854775826
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/shared/arithmetic_coerce.rb b/spec/ruby/core/integer/shared/arithmetic_coerce.rb
new file mode 100644
index 0000000000..4c0cbcb999
--- /dev/null
+++ b/spec/ruby/core/integer/shared/arithmetic_coerce.rb
@@ -0,0 +1,31 @@
+require_relative '../fixtures/classes'
+
+describe :integer_arithmetic_coerce_rescue, shared: true do
+ it "rescues exception (StandardError and subclasses) raised in other#coerce and raises TypeError" do
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(IntegerSpecs::CoerceError)
+
+ # e.g. 1 + b
+ -> { 1.send(@method, b) }.should raise_error(TypeError, /MockObject can't be coerced into Integer/)
+ end
+
+ it "does not rescue Exception and StandardError siblings raised in other#coerce" do
+ [Exception, NoMemoryError].each do |exception|
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(exception)
+
+ # e.g. 1 + b
+ -> { 1.send(@method, b) }.should raise_error(exception)
+ end
+ end
+end
+
+describe :integer_arithmetic_coerce_not_rescue, shared: true do
+ it "does not rescue exception raised in other#coerce" do
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(IntegerSpecs::CoerceError)
+
+ # e.g. 1 + b
+ -> { 1.send(@method, b) }.should raise_error(IntegerSpecs::CoerceError)
+ end
+end
diff --git a/spec/ruby/core/integer/shared/comparison_coerce.rb b/spec/ruby/core/integer/shared/comparison_coerce.rb
new file mode 100644
index 0000000000..50437f77f5
--- /dev/null
+++ b/spec/ruby/core/integer/shared/comparison_coerce.rb
@@ -0,0 +1,33 @@
+require_relative '../fixtures/classes'
+
+describe :integer_comparison_coerce_rescue, shared: true do
+ it "rescues exception (StandardError and subclasses) raised in other#coerce and raises ArgumentError" do
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(IntegerSpecs::CoerceError)
+
+ # e.g. 1 > b
+ -> {
+ -> { 1.send(@method, b) }.should raise_error(ArgumentError, /comparison of Integer with MockObject failed/)
+ }.should complain(/Numerical comparison operators will no more rescue exceptions of #coerce/)
+ end
+
+ it "does not rescue Exception and StandardError siblings raised in other#coerce" do
+ [Exception, NoMemoryError].each do |exception|
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(exception)
+
+ # e.g. 1 > b
+ -> { 1.send(@method, b) }.should raise_error(exception)
+ end
+ end
+end
+
+describe :integer_comparison_coerce_not_rescue, shared: true do
+ it "does not rescue exception raised in other#coerce" do
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(IntegerSpecs::CoerceError)
+
+ # e.g. 1 > b
+ -> { 1.send(@method, b) }.should raise_error(IntegerSpecs::CoerceError)
+ end
+end
diff --git a/spec/ruby/core/integer/shared/equal.rb b/spec/ruby/core/integer/shared/equal.rb
new file mode 100644
index 0000000000..03416b60f5
--- /dev/null
+++ b/spec/ruby/core/integer/shared/equal.rb
@@ -0,0 +1,58 @@
+describe :integer_equal, shared: true do
+ context "fixnum" do
+ it "returns true if self has the same value as other" do
+ 1.send(@method, 1).should == true
+ 9.send(@method, 5).should == false
+
+ # Actually, these call Float#==, Bignum#== etc.
+ 9.send(@method, 9.0).should == true
+ 9.send(@method, 9.01).should == false
+
+ 10.send(@method, bignum_value).should == false
+ end
+
+ it "calls 'other == self' if the given argument is not a Integer" do
+ 1.send(@method, '*').should == false
+
+ obj = mock('one other')
+ obj.should_receive(:==).any_number_of_times.and_return(false)
+ 1.send(@method, obj).should == false
+
+ obj = mock('another')
+ obj.should_receive(:==).any_number_of_times.and_return(true)
+ 2.send(@method, obj).should == true
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value
+ end
+
+ it "returns true if self has the same value as the given argument" do
+ @bignum.send(@method, @bignum).should == true
+ @bignum.send(@method, @bignum.to_f).should == true
+
+ @bignum.send(@method, @bignum + 1).should == false
+ (@bignum + 1).send(@method, @bignum).should == false
+
+ @bignum.send(@method, 9).should == false
+ @bignum.send(@method, 9.01).should == false
+
+ @bignum.send(@method, bignum_value(10)).should == false
+ end
+
+ it "calls 'other == self' if the given argument is not an Integer" do
+ obj = mock('not integer')
+ obj.should_receive(:==).and_return(true)
+ @bignum.send(@method, obj).should == true
+ end
+
+ it "returns the result of 'other == self' as a boolean" do
+ obj = mock('not integer')
+ obj.should_receive(:==).exactly(2).times.and_return("woot", nil)
+ @bignum.send(@method, obj).should == true
+ @bignum.send(@method, obj).should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/shared/exponent.rb b/spec/ruby/core/integer/shared/exponent.rb
new file mode 100644
index 0000000000..810e2a7101
--- /dev/null
+++ b/spec/ruby/core/integer/shared/exponent.rb
@@ -0,0 +1,118 @@
+describe :integer_exponent, shared: true do
+ context "fixnum" do
+ it "returns self raised to the given power" do
+ 2.send(@method, 0).should eql 1
+ 2.send(@method, 1).should eql 2
+ 2.send(@method, 2).should eql 4
+
+ 9.send(@method, 0.5).should eql 3.0
+ 9.send(@method, Rational(1, 2)).should eql 3.0
+ 5.send(@method, -1).to_f.to_s.should == '0.2'
+
+ 2.send(@method, 40).should eql 1099511627776
+ end
+
+ it "overflows the answer to a bignum transparently" do
+ 2.send(@method, 29).should eql 536870912
+ 2.send(@method, 30).should eql 1073741824
+ 2.send(@method, 31).should eql 2147483648
+ 2.send(@method, 32).should eql 4294967296
+
+ 2.send(@method, 61).should eql 2305843009213693952
+ 2.send(@method, 62).should eql 4611686018427387904
+ 2.send(@method, 63).should eql 9223372036854775808
+ 2.send(@method, 64).should eql 18446744073709551616
+ 8.send(@method, 23).should eql 590295810358705651712
+ end
+
+ it "raises negative numbers to the given power" do
+ (-2).send(@method, 29).should eql(-536870912)
+ (-2).send(@method, 30).should eql(1073741824)
+ (-2).send(@method, 31).should eql(-2147483648)
+ (-2).send(@method, 32).should eql(4294967296)
+
+ (-2).send(@method, 61).should eql(-2305843009213693952)
+ (-2).send(@method, 62).should eql(4611686018427387904)
+ (-2).send(@method, 63).should eql(-9223372036854775808)
+ (-2).send(@method, 64).should eql(18446744073709551616)
+ end
+
+ it "can raise 1 to a bignum safely" do
+ 1.send(@method, 4611686018427387904).should eql 1
+ end
+
+ it "can raise -1 to a bignum safely" do
+ (-1).send(@method, 4611686018427387904).should eql(1)
+ (-1).send(@method, 4611686018427387905).should eql(-1)
+ end
+
+ it "returns Float::INFINITY when the number is too big" do
+ 2.send(@method, 427387904).should == Float::INFINITY
+ end
+
+ it "raises a ZeroDivisionError for 0 ** -1" do
+ -> { 0.send(@method, -1) }.should raise_error(ZeroDivisionError)
+ -> { 0.send(@method, Rational(-1, 1)) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "returns Float::INFINITY for 0 ** -1.0" do
+ 0.send(@method, -1.0).should == Float::INFINITY
+ end
+
+ it "raises a TypeError when given a non-numeric power" do
+ -> { 13.send(@method, "10") }.should raise_error(TypeError)
+ -> { 13.send(@method, :symbol) }.should raise_error(TypeError)
+ -> { 13.send(@method, nil) }.should raise_error(TypeError)
+ end
+
+ it "coerces power and calls #**" do
+ num_2 = mock("2")
+ num_13 = mock("13")
+ num_2.should_receive(:coerce).with(13).and_return([num_13, num_2])
+ num_13.should_receive(:**).with(num_2).and_return(169)
+
+ 13.send(@method, num_2).should == 169
+ end
+
+ it "returns Float when power is Float" do
+ 2.send(@method, 2.0).should == 4.0
+ end
+
+ it "returns Rational when power is Rational" do
+ 2.send(@method, Rational(2, 1)).should == Rational(4, 1)
+ end
+
+ it "returns a complex number when negative and raised to a fractional power" do
+ (-8).send(@method, 1.0/3) .should be_close(Complex(1, 1.73205), TOLERANCE)
+ (-8).send(@method, Rational(1, 3)).should be_close(Complex(1, 1.73205), TOLERANCE)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value(47)
+ end
+
+ it "returns self raised to other power" do
+ (@bignum.send(@method, 4)).should == 7237005577332262361485077344629993318496048279512298547155833600056910050625
+ (@bignum.send(@method, 1.2)).should be_close(57262152889751597425762.57804, TOLERANCE)
+ end
+
+ it "raises a TypeError when given a non-Integer" do
+ lambda { @bignum.send(@method, mock('10')) }.should raise_error(TypeError)
+ lambda { @bignum.send(@method, "10") }.should raise_error(TypeError)
+ lambda { @bignum.send(@method, :symbol) }.should raise_error(TypeError)
+ end
+
+ it "switch to a Float when the values is too big" do
+ flt = @bignum.send(@method, @bignum)
+ flt.should be_kind_of(Float)
+ flt.infinite?.should == 1
+ end
+
+ it "returns a complex number when negative and raised to a fractional power" do
+ ((-@bignum).send(@method, (1.0/3))) .should be_close(Complex(1048576,1816186.907597341), TOLERANCE)
+ ((-@bignum).send(@method, Rational(1,3))).should be_close(Complex(1048576,1816186.907597341), TOLERANCE)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/shared/integer_rounding.rb b/spec/ruby/core/integer/shared/integer_rounding.rb
new file mode 100644
index 0000000000..ecbda1bb4a
--- /dev/null
+++ b/spec/ruby/core/integer/shared/integer_rounding.rb
@@ -0,0 +1,31 @@
+describe :integer_rounding_positive_precision, shared: true do
+ it "returns self if not passed a precision" do
+ [2, -4, 10**70, -10**100].each do |v|
+ v.send(@method).should eql(v)
+ end
+ end
+
+ ruby_version_is "2.4" do
+ it "returns self if passed a precision of zero" do
+ [2, -4, 10**70, -10**100].each do |v|
+ v.send(@method, 0).should eql(v)
+ end
+ end
+ end
+
+ ruby_version_is "2.4"..."2.5" do
+ it "returns itself as a float if passed a positive precision" do
+ [2, -4, 10**70, -10**100].each do |v|
+ v.send(@method, 42).should eql(v.to_f)
+ end
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "returns itself if passed a positive precision" do
+ [2, -4, 10**70, -10**100].each do |v|
+ v.send(@method, 42).should eql(v)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/shared/modulo.rb b/spec/ruby/core/integer/shared/modulo.rb
new file mode 100644
index 0000000000..d545a9af55
--- /dev/null
+++ b/spec/ruby/core/integer/shared/modulo.rb
@@ -0,0 +1,74 @@
+describe :integer_modulo, shared: true do
+ context "fixnum" do
+ it "returns the modulus obtained from dividing self by the given argument" do
+ 13.send(@method, 4).should == 1
+ 4.send(@method, 13).should == 4
+
+ 13.send(@method, 4.0).should == 1
+ 4.send(@method, 13.0).should == 4
+
+ (-200).send(@method, 256).should == 56
+ (-1000).send(@method, 512).should == 24
+
+ (-200).send(@method, -256).should == -200
+ (-1000).send(@method, -512).should == -488
+
+ (200).send(@method, -256).should == -56
+ (1000).send(@method, -512).should == -24
+
+ 1.send(@method, 2.0).should == 1.0
+ 200.send(@method, bignum_value).should == 200
+ end
+
+ it "raises a ZeroDivisionError when the given argument is 0" do
+ lambda { 13.send(@method, 0) }.should raise_error(ZeroDivisionError)
+ lambda { 0.send(@method, 0) }.should raise_error(ZeroDivisionError)
+ lambda { -10.send(@method, 0) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "raises a ZeroDivisionError when the given argument is 0 and a Float" do
+ lambda { 0.send(@method, 0.0) }.should raise_error(ZeroDivisionError)
+ lambda { 10.send(@method, 0.0) }.should raise_error(ZeroDivisionError)
+ lambda { -10.send(@method, 0.0) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "raises a TypeError when given a non-Integer" do
+ lambda {
+ (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10)
+ 13.send(@method, obj)
+ }.should raise_error(TypeError)
+ lambda { 13.send(@method, "10") }.should raise_error(TypeError)
+ lambda { 13.send(@method, :symbol) }.should raise_error(TypeError)
+ end
+ end
+
+ context "bignum" do
+ before :each do
+ @bignum = bignum_value
+ end
+
+ it "returns the modulus obtained from dividing self by the given argument" do
+ @bignum.send(@method, 5).should == 3
+ @bignum.send(@method, -5).should == -2
+ @bignum.send(@method, -100).should == -92
+ @bignum.send(@method, 2.22).should be_close(0.780180180180252, TOLERANCE)
+ @bignum.send(@method, bignum_value(10)).should == 9223372036854775808
+ end
+
+ it "raises a ZeroDivisionError when the given argument is 0" do
+ lambda { @bignum.send(@method, 0) }.should raise_error(ZeroDivisionError)
+ lambda { (-@bignum).send(@method, 0) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "raises a ZeroDivisionError when the given argument is 0 and a Float" do
+ lambda { @bignum.send(@method, 0.0) }.should raise_error(ZeroDivisionError)
+ lambda { -@bignum.send(@method, 0.0) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "raises a TypeError when given a non-Integer" do
+ lambda { @bignum.send(@method, mock('10')) }.should raise_error(TypeError)
+ lambda { @bignum.send(@method, "10") }.should raise_error(TypeError)
+ lambda { @bignum.send(@method, :symbol) }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/shared/next.rb b/spec/ruby/core/integer/shared/next.rb
new file mode 100644
index 0000000000..85b83d6965
--- /dev/null
+++ b/spec/ruby/core/integer/shared/next.rb
@@ -0,0 +1,25 @@
+describe :integer_next, shared: true do
+ it "returns the next larger positive Fixnum" do
+ 2.send(@method).should == 3
+ end
+
+ it "returns the next larger negative Fixnum" do
+ (-2).send(@method).should == -1
+ end
+
+ it "returns the next larger positive Bignum" do
+ bignum_value.send(@method).should == bignum_value(1)
+ end
+
+ it "returns the next larger negative Bignum" do
+ (-bignum_value(1)).send(@method).should == -bignum_value
+ end
+
+ it "overflows a Fixnum to a Bignum" do
+ fixnum_max.send(@method).should == fixnum_max + 1
+ end
+
+ it "underflows a Bignum to a Fixnum" do
+ (fixnum_min - 1).send(@method).should == fixnum_min
+ end
+end
diff --git a/spec/ruby/core/integer/shared/to_i.rb b/spec/ruby/core/integer/shared/to_i.rb
new file mode 100644
index 0000000000..7b974cd3a7
--- /dev/null
+++ b/spec/ruby/core/integer/shared/to_i.rb
@@ -0,0 +1,8 @@
+describe :integer_to_i, shared: true do
+ it "returns self" do
+ 10.send(@method).should eql(10)
+ (-15).send(@method).should eql(-15)
+ bignum_value.send(@method).should eql(bignum_value)
+ (-bignum_value).send(@method).should eql(-bignum_value)
+ end
+end
diff --git a/spec/ruby/core/integer/size_spec.rb b/spec/ruby/core/integer/size_spec.rb
new file mode 100644
index 0000000000..a134e82384
--- /dev/null
+++ b/spec/ruby/core/integer/size_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+
+describe "Integer#size" do
+ platform_is wordsize: 32 do
+ it "returns the number of bytes in the machine representation of self" do
+ -1.size.should == 4
+ 0.size.should == 4
+ 4091.size.should == 4
+ end
+ end
+
+ platform_is wordsize: 64 do
+ it "returns the number of bytes in the machine representation of self" do
+ -1.size.should == 8
+ 0.size.should == 8
+ 4091.size.should == 8
+ end
+ end
+
+ context "bignum" do
+ it "returns the number of bytes required to hold the unsigned bignum data" do
+ # that is, n such that 256 * n <= val.abs < 256 * (n+1)
+ (256**7).size.should == 8
+ (256**8).size.should == 9
+ (256**9).size.should == 10
+ (256**10).size.should == 11
+ (256**10-1).size.should == 10
+ (256**11).size.should == 12
+ (256**12).size.should == 13
+ (256**20-1).size.should == 20
+ (256**40-1).size.should == 40
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/sqrt_spec.rb b/spec/ruby/core/integer/sqrt_spec.rb
new file mode 100644
index 0000000000..e40bd12b19
--- /dev/null
+++ b/spec/ruby/core/integer/sqrt_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.5" do
+ describe "Integer.sqrt" do
+ it "returns an integer" do
+ Integer.sqrt(10).should be_kind_of(Integer)
+ end
+
+ it "returns the integer square root of the argument" do
+ Integer.sqrt(0).should == 0
+ Integer.sqrt(1).should == 1
+ Integer.sqrt(24).should == 4
+ Integer.sqrt(25).should == 5
+ Integer.sqrt(10**400).should == 10**200
+ end
+
+ it "raises a Math::DomainError if the argument is negative" do
+ lambda { Integer.sqrt(-4) }.should raise_error(Math::DomainError)
+ end
+
+ it "accepts any argument that can be coerced to Integer" do
+ Integer.sqrt(10.0).should == 3
+ end
+
+ it "converts the argument with #to_int" do
+ Integer.sqrt(mock_int(10)).should == 3
+ end
+
+ it "raises a TypeError if the argument cannot be coerced to Integer" do
+ lambda { Integer.sqrt("test") }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/succ_spec.rb b/spec/ruby/core/integer/succ_spec.rb
new file mode 100644
index 0000000000..9ae9a14fe7
--- /dev/null
+++ b/spec/ruby/core/integer/succ_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/next'
+
+describe "Integer#succ" do
+ it_behaves_like :integer_next, :succ
+end
diff --git a/spec/ruby/core/integer/times_spec.rb b/spec/ruby/core/integer/times_spec.rb
new file mode 100644
index 0000000000..356340592b
--- /dev/null
+++ b/spec/ruby/core/integer/times_spec.rb
@@ -0,0 +1,79 @@
+require_relative '../../spec_helper'
+
+describe "Integer#times" do
+ it "returns self" do
+ 5.times {}.should == 5
+ 9.times {}.should == 9
+ 9.times { |n| n - 2 }.should == 9
+ end
+
+ it "yields each value from 0 to self - 1" do
+ a = []
+ 9.times { |i| a << i }
+ -2.times { |i| a << i }
+ a.should == [0, 1, 2, 3, 4, 5, 6, 7, 8]
+ end
+
+ it "skips the current iteration when encountering 'next'" do
+ a = []
+ 3.times do |i|
+ next if i == 1
+ a << i
+ end
+ a.should == [0, 2]
+ end
+
+ it "skips all iterations when encountering 'break'" do
+ a = []
+ 5.times do |i|
+ break if i == 3
+ a << i
+ end
+ a.should == [0, 1, 2]
+ end
+
+ it "skips all iterations when encountering break with an argument and returns that argument" do
+ 9.times { break 2 }.should == 2
+ end
+
+ it "executes a nested while loop containing a break expression" do
+ a = [false]
+ b = 1.times do |i|
+ while true
+ a.shift or break
+ end
+ end
+ a.should == []
+ b.should == 1
+ end
+
+ it "executes a nested #times" do
+ a = 0
+ b = 3.times do |i|
+ 2.times { a += 1 }
+ end
+ a.should == 6
+ b.should == 3
+ end
+
+ it "returns an Enumerator" do
+ result = []
+
+ enum = 3.times
+ enum.each { |i| result << i }
+
+ result.should == [0, 1, 2]
+ end
+
+ describe "when no block is given" do
+ describe "returned Enumerator" do
+ describe "size" do
+ it "returns self" do
+ 5.times.size.should == 5
+ 10.times.size.should == 10
+ 0.times.size.should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/to_f_spec.rb b/spec/ruby/core/integer/to_f_spec.rb
new file mode 100644
index 0000000000..06092eaa92
--- /dev/null
+++ b/spec/ruby/core/integer/to_f_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+
+describe "Integer#to_f" do
+ context "fixnum" do
+ it "returns self converted to a Float" do
+ 0.to_f.should == 0.0
+ -500.to_f.should == -500.0
+ 9_641_278.to_f.should == 9641278.0
+ end
+ end
+
+ context "bignum" do
+ it "returns self converted to a Float" do
+ bignum_value(0x4000_0aa0_0bb0_0000).to_f.should eql(13_835_069_737_789_292_544.00)
+ bignum_value(0x8000_0000_0000_0ccc).to_f.should eql(18_446_744_073_709_555_712.00)
+ (-bignum_value(99)).to_f.should eql(-9_223_372_036_854_775_808.00)
+ end
+
+ it "converts number close to Float::MAX without exceeding MAX or producing NaN" do
+ (10**308).to_f.should == 10.0 ** 308
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/to_i_spec.rb b/spec/ruby/core/integer/to_i_spec.rb
new file mode 100644
index 0000000000..1a8e477b3c
--- /dev/null
+++ b/spec/ruby/core/integer/to_i_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_i'
+
+describe "Integer#to_i" do
+ it_behaves_like :integer_to_i, :to_i
+end
diff --git a/spec/ruby/core/integer/to_int_spec.rb b/spec/ruby/core/integer/to_int_spec.rb
new file mode 100644
index 0000000000..4b7eccad3a
--- /dev/null
+++ b/spec/ruby/core/integer/to_int_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_i'
+
+describe "Integer#to_int" do
+ it_behaves_like :integer_to_i, :to_int
+end
diff --git a/spec/ruby/core/integer/to_r_spec.rb b/spec/ruby/core/integer/to_r_spec.rb
new file mode 100644
index 0000000000..dbb12166f1
--- /dev/null
+++ b/spec/ruby/core/integer/to_r_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+
+describe "Integer#to_r" do
+ it "returns a Rational object" do
+ 309.to_r.should be_an_instance_of(Rational)
+ end
+
+ it "constructs a rational number with self as the numerator" do
+ 34.to_r.numerator.should == 34
+ end
+
+ it "constructs a rational number with 1 as the denominator" do
+ 298.to_r.denominator.should == 1
+ end
+
+ it "works even if self is a Bignum" do
+ bignum = 99999**999
+ bignum.should be_an_instance_of(Bignum)
+ bignum.to_r.should == Rational(bignum, 1)
+ end
+
+ it "raises an ArgumentError if given any arguments" do
+ lambda { 287.to_r(2) }.should raise_error(ArgumentError)
+ lambda { 9102826.to_r(309, [], 71) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/integer/to_s_spec.rb b/spec/ruby/core/integer/to_s_spec.rb
new file mode 100644
index 0000000000..c980be535a
--- /dev/null
+++ b/spec/ruby/core/integer/to_s_spec.rb
@@ -0,0 +1,99 @@
+require_relative '../../spec_helper'
+
+describe "Integer#to_s" do
+ context "fixnum" do
+ context "when given a base" do
+ it "returns self converted to a String in the given base" do
+ 12345.to_s(2).should == "11000000111001"
+ 12345.to_s(8).should == "30071"
+ 12345.to_s(10).should == "12345"
+ 12345.to_s(16).should == "3039"
+ 95.to_s(16).should == "5f"
+ 12345.to_s(36).should == "9ix"
+ end
+
+ it "raises an ArgumentError if the base is less than 2 or higher than 36" do
+ lambda { 123.to_s(-1) }.should raise_error(ArgumentError)
+ lambda { 123.to_s(0) }.should raise_error(ArgumentError)
+ lambda { 123.to_s(1) }.should raise_error(ArgumentError)
+ lambda { 123.to_s(37) }.should raise_error(ArgumentError)
+ end
+ end
+
+ context "when no base given" do
+ it "returns self converted to a String using base 10" do
+ 255.to_s.should == '255'
+ 3.to_s.should == '3'
+ 0.to_s.should == '0'
+ -9002.to_s.should == '-9002'
+ end
+ end
+
+ with_feature :encoding do
+ before :each do
+ @internal = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_internal = @internal
+ end
+
+ it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do
+ Encoding.default_internal = nil
+ 1.to_s.encoding.should equal(Encoding::US_ASCII)
+ end
+
+ it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do
+ Encoding.default_internal = Encoding::IBM437
+ 1.to_s.encoding.should equal(Encoding::US_ASCII)
+ end
+ end
+ end
+
+ context "bignum" do
+ describe "when given a base" do
+ it "returns self converted to a String using the given base" do
+ a = 2**64
+ a.to_s(2).should == "10000000000000000000000000000000000000000000000000000000000000000"
+ a.to_s(8).should == "2000000000000000000000"
+ a.to_s(16).should == "10000000000000000"
+ a.to_s(32).should == "g000000000000"
+ end
+
+ it "raises an ArgumentError if the base is less than 2 or higher than 36" do
+ lambda { 123.to_s(-1) }.should raise_error(ArgumentError)
+ lambda { 123.to_s(0) }.should raise_error(ArgumentError)
+ lambda { 123.to_s(1) }.should raise_error(ArgumentError)
+ lambda { 123.to_s(37) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "when given no base" do
+ it "returns self converted to a String using base 10" do
+ bignum_value(9).to_s.should == "9223372036854775817"
+ bignum_value.to_s.should == "9223372036854775808"
+ (-bignum_value(675)).to_s.should == "-9223372036854776483"
+ end
+ end
+
+ with_feature :encoding do
+ before :each do
+ @internal = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_internal = @internal
+ end
+
+ it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do
+ Encoding.default_internal = nil
+ bignum_value.to_s.encoding.should equal(Encoding::US_ASCII)
+ end
+
+ it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do
+ Encoding.default_internal = Encoding::IBM437
+ bignum_value.to_s.encoding.should equal(Encoding::US_ASCII)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/truncate_spec.rb b/spec/ruby/core/integer/truncate_spec.rb
new file mode 100644
index 0000000000..761a3dbd31
--- /dev/null
+++ b/spec/ruby/core/integer/truncate_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_i'
+require_relative 'shared/integer_rounding'
+
+describe "Integer#truncate" do
+ it_behaves_like :integer_to_i, :truncate
+ it_behaves_like :integer_rounding_positive_precision, :truncate
+
+ ruby_version_is "2.4" do
+ context "precision argument specified as part of the truncate method is negative" do
+ it "returns an integer with at least precision.abs trailing zeros" do
+ 1832.truncate(-1).should eql(1830)
+ 1832.truncate(-2).should eql(1800)
+ 1832.truncate(-3).should eql(1000)
+ -1832.truncate(-1).should eql(-1830)
+ -1832.truncate(-2).should eql(-1800)
+ -1832.truncate(-3).should eql(-1000)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/uminus_spec.rb b/spec/ruby/core/integer/uminus_spec.rb
new file mode 100644
index 0000000000..56c5f7a085
--- /dev/null
+++ b/spec/ruby/core/integer/uminus_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+
+describe "Integer#-@" do
+ context "fixnum" do
+ it "returns self as a negative value" do
+ 2.send(:-@).should == -2
+ -2.should == -2
+ -268435455.should == -268435455
+ (--5).should == 5
+ -8.send(:-@).should == 8
+ end
+
+ it "negates self at Fixnum/Bignum boundaries" do
+ fixnum_max.send(:-@).should == (0 - fixnum_max)
+ fixnum_min.send(:-@).should == (0 - fixnum_min)
+ end
+ end
+
+ context "bignum" do
+ it "returns self as a negative value" do
+ bignum_value.send(:-@).should == -9223372036854775808
+ (-bignum_value).send(:-@).should == 9223372036854775808
+
+ bignum_value(921).send(:-@).should == -9223372036854776729
+ (-bignum_value(921).send(:-@)).should == 9223372036854776729
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/upto_spec.rb b/spec/ruby/core/integer/upto_spec.rb
new file mode 100644
index 0000000000..7cab834ad4
--- /dev/null
+++ b/spec/ruby/core/integer/upto_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../../spec_helper'
+
+describe "Integer#upto [stop] when self and stop are Fixnums" do
+ it "does not yield when stop is less than self" do
+ result = []
+ 5.upto(4) { |x| result << x }
+ result.should == []
+ end
+
+ it "yields once when stop equals self" do
+ result = []
+ 5.upto(5) { |x| result << x }
+ result.should == [5]
+ end
+
+ it "yields while increasing self until it is less than stop" do
+ result = []
+ 2.upto(5) { |x| result << x }
+ result.should == [2, 3, 4, 5]
+ end
+
+ it "yields while increasing self until it is greater than floor of a Float endpoint" do
+ result = []
+ 9.upto(13.3) {|i| result << i}
+ -5.upto(-1.3) {|i| result << i}
+ result.should == [9,10,11,12,13,-5,-4,-3,-2]
+ end
+
+ it "raises an ArgumentError for non-numeric endpoints" do
+ lambda { 1.upto("A") {|x| p x} }.should raise_error(ArgumentError)
+ lambda { 1.upto(nil) {|x| p x} }.should raise_error(ArgumentError)
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ result = []
+
+ enum = 2.upto(5)
+ enum.each { |i| result << i }
+
+ result.should == [2, 3, 4, 5]
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "raises an ArgumentError for non-numeric endpoints" do
+ enum = 1.upto("A")
+ lambda { enum.size }.should raise_error(ArgumentError)
+ enum = 1.upto(nil)
+ lambda { enum.size }.should raise_error(ArgumentError)
+ end
+
+ it "returns stop - self + 1" do
+ 5.upto(10).size.should == 6
+ 1.upto(10).size.should == 10
+ 0.upto(10).size.should == 11
+ 0.upto(0).size.should == 1
+ -5.upto(-3).size.should == 3
+ end
+
+ it "returns 0 when stop < self" do
+ 5.upto(4).size.should == 0
+ 0.upto(-5).size.should == 0
+ -3.upto(-5).size.should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/io/advise_spec.rb b/spec/ruby/core/io/advise_spec.rb
new file mode 100644
index 0000000000..81d5a49849
--- /dev/null
+++ b/spec/ruby/core/io/advise_spec.rb
@@ -0,0 +1,97 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#advise" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "raises a TypeError if advise is not a Symbol" do
+ lambda {
+ @io.advise("normal")
+ }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if offset cannot be coerced to an Integer" do
+ lambda {
+ @io.advise(:normal, "wat")
+ }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if len cannot be coerced to an Integer" do
+ lambda {
+ @io.advise(:normal, 0, "wat")
+ }.should raise_error(TypeError)
+ end
+
+ it "raises a RangeError if offset is too big" do
+ lambda {
+ @io.advise(:normal, 10 ** 32)
+ }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError if len is too big" do
+ lambda {
+ @io.advise(:normal, 0, 10 ** 32)
+ }.should raise_error(RangeError)
+ end
+
+ it "raises a NotImplementedError if advise is not recognized" do
+ lambda{
+ @io.advise(:foo)
+ }.should raise_error(NotImplementedError)
+ end
+
+ it "supports the normal advice type" do
+ @io.advise(:normal).should be_nil
+ end
+
+ it "supports the sequential advice type" do
+ @io.advise(:sequential).should be_nil
+ end
+
+ it "supports the random advice type" do
+ @io.advise(:random).should be_nil
+ end
+
+ it "supports the dontneed advice type" do
+ @io.advise(:dontneed).should be_nil
+ end
+
+ it "supports the noreuse advice type" do
+ @io.advise(:noreuse).should be_nil
+ end
+
+ platform_is_not :linux do
+ it "supports the willneed advice type" do
+ @io.advise(:willneed).should be_nil
+ end
+ end
+
+ platform_is :linux do
+ it "supports the willneed advice type" do
+ require 'etc'
+ uname = if Etc.respond_to?(:uname)
+ Etc.uname[:release]
+ else
+ `uname -r`.chomp
+ end
+ if (uname.split('.').map(&:to_i) <=> [3,6]) < 0
+ # [ruby-core:65355] tmpfs is not supported
+ 1.should == 1
+ else
+ @io.advise(:willneed).should be_nil
+ end
+ end
+ end
+
+ it "raises an IOError if the stream is closed" do
+ @io.close
+ lambda { @io.advise(:normal) }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/binmode_spec.rb b/spec/ruby/core/io/binmode_spec.rb
new file mode 100644
index 0000000000..cc10e297dd
--- /dev/null
+++ b/spec/ruby/core/io/binmode_spec.rb
@@ -0,0 +1,60 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#binmode" do
+ before :each do
+ @name = tmp("io_binmode.txt")
+ end
+
+ after :each do
+ @io.close if @io and !@io.closed?
+ rm_r @name
+ end
+
+ it "returns self" do
+ @io = new_io(@name)
+ @io.binmode.should equal(@io)
+ end
+
+ it "raises an IOError on closed stream" do
+ lambda { IOSpecs.closed_io.binmode }.should raise_error(IOError)
+ end
+
+ it "sets external encoding to binary" do
+ @io = new_io(@name, "w:utf-8")
+ @io.binmode
+ @io.external_encoding.should == Encoding::BINARY
+ end
+
+ it "sets internal encoding to nil" do
+ @io = new_io(@name, "w:utf-8:ISO-8859-1")
+ @io.binmode
+ @io.internal_encoding.should == nil
+ end
+end
+
+describe "IO#binmode?" do
+ before :each do
+ @filename = tmp("IO_binmode_file")
+ @file = File.open(@filename, "w")
+ @duped = nil
+ end
+
+ after :each do
+ @duped.close if @duped
+ @file.close
+ rm_r @filename
+ end
+
+ it "is true after a call to IO#binmode" do
+ @file.binmode?.should be_false
+ @file.binmode
+ @file.binmode?.should be_true
+ end
+
+ it "propagates to dup'ed IO objects" do
+ @file.binmode
+ @duped = @file.dup
+ @duped.binmode?.should == @file.binmode?
+ end
+end
diff --git a/spec/ruby/core/io/binread_spec.rb b/spec/ruby/core/io/binread_spec.rb
new file mode 100644
index 0000000000..5e936ac6ba
--- /dev/null
+++ b/spec/ruby/core/io/binread_spec.rb
@@ -0,0 +1,47 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO.binread" do
+ before :each do
+ @internal = Encoding.default_internal
+
+ @fname = tmp('io_read.txt')
+ @contents = "1234567890"
+ touch(@fname) { |f| f.write @contents }
+ end
+
+ after :each do
+ rm_r @fname
+ Encoding.default_internal = @internal
+ end
+
+ it "reads the contents of a file" do
+ IO.binread(@fname).should == @contents
+ end
+
+ it "reads the contents of a file up to a certain size when specified" do
+ IO.binread(@fname, 5).should == @contents.slice(0..4)
+ end
+
+ it "reads the contents of a file from an offset of a specific size when specified" do
+ IO.binread(@fname, 5, 3).should == @contents.slice(3, 5)
+ end
+
+ it "returns a String in ASCII-8BIT encoding" do
+ IO.binread(@fname).encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "returns a String in ASCII-8BIT encoding regardless of Encoding.default_internal" do
+ Encoding.default_internal = Encoding::EUC_JP
+ IO.binread(@fname).encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "raises an ArgumentError when not passed a valid length" do
+ lambda { IO.binread @fname, -1 }.should raise_error(ArgumentError)
+ end
+
+ it "raises an Errno::EINVAL when not passed a valid offset" do
+ lambda { IO.binread @fname, 0, -1 }.should raise_error(Errno::EINVAL)
+ end
+end
diff --git a/spec/ruby/core/io/binwrite_spec.rb b/spec/ruby/core/io/binwrite_spec.rb
new file mode 100644
index 0000000000..e28ea8ebb1
--- /dev/null
+++ b/spec/ruby/core/io/binwrite_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'shared/binwrite'
+
+describe "IO.binwrite" do
+ it_behaves_like :io_binwrite, :binwrite
+
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/io/bytes_spec.rb b/spec/ruby/core/io/bytes_spec.rb
new file mode 100644
index 0000000000..2d2bd950f1
--- /dev/null
+++ b/spec/ruby/core/io/bytes_spec.rb
@@ -0,0 +1,43 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#bytes" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "returns an enumerator of the next bytes from the stream" do
+ enum = @io.bytes
+ enum.should be_an_instance_of(Enumerator)
+ @io.readline.should == "Voici la ligne une.\n"
+ enum.first(5).should == [81, 117, 105, 32, 195]
+ end
+
+ it "yields each byte" do
+ count = 0
+ ScratchPad.record []
+ @io.each_byte do |byte|
+ ScratchPad << byte
+ break if 4 < count += 1
+ end
+
+ ScratchPad.recorded.should == [86, 111, 105, 99, 105]
+ end
+
+ it "raises an IOError on closed stream" do
+ enum = IOSpecs.closed_io.bytes
+ lambda { enum.first }.should raise_error(IOError)
+ end
+
+ it "raises an IOError on an enumerator for a stream that has been closed" do
+ enum = @io.bytes
+ enum.first.should == 86
+ @io.close
+ lambda { enum.first }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/chars_spec.rb b/spec/ruby/core/io/chars_spec.rb
new file mode 100644
index 0000000000..cd5dbbce4f
--- /dev/null
+++ b/spec/ruby/core/io/chars_spec.rb
@@ -0,0 +1,12 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/chars'
+
+describe "IO#chars" do
+ it_behaves_like :io_chars, :chars
+end
+
+describe "IO#chars" do
+ it_behaves_like :io_chars_empty, :chars
+end
diff --git a/spec/ruby/core/io/close_on_exec_spec.rb b/spec/ruby/core/io/close_on_exec_spec.rb
new file mode 100644
index 0000000000..d6ba3c3cef
--- /dev/null
+++ b/spec/ruby/core/io/close_on_exec_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../../spec_helper'
+
+describe "IO#close_on_exec=" do
+ before :each do
+ @name = tmp('io_close_on_exec.txt')
+ @io = new_io @name
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @name
+ end
+
+ guard -> { platform_is_not :windows } do
+ it "sets the close-on-exec flag if true" do
+ @io.close_on_exec = true
+ @io.close_on_exec?.should == true
+ end
+
+ it "sets the close-on-exec flag if non-false" do
+ @io.close_on_exec = :true
+ @io.close_on_exec?.should == true
+ end
+
+ it "unsets the close-on-exec flag if false" do
+ @io.close_on_exec = true
+ @io.close_on_exec = false
+ @io.close_on_exec?.should == false
+ end
+
+ it "unsets the close-on-exec flag if nil" do
+ @io.close_on_exec = true
+ @io.close_on_exec = nil
+ @io.close_on_exec?.should == false
+ end
+
+ it "ensures the IO's file descriptor is closed in exec'ed processes" do
+ require 'fcntl'
+ @io.close_on_exec = true
+ (@io.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC).should == Fcntl::FD_CLOEXEC
+ end
+
+ it "raises IOError if called on a closed IO" do
+ @io.close
+ lambda { @io.close_on_exec = true }.should raise_error(IOError)
+ end
+
+ it "returns nil" do
+ @io.send(:close_on_exec=, true).should be_nil
+ end
+ end
+end
+
+describe "IO#close_on_exec?" do
+ before :each do
+ @name = tmp('io_is_close_on_exec.txt')
+ @io = new_io @name
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @name
+ end
+
+ guard -> { platform_is_not :windows } do
+ it "returns true by default" do
+ @io.close_on_exec?.should == true
+ end
+
+ it "returns true if set" do
+ @io.close_on_exec = true
+ @io.close_on_exec?.should == true
+ end
+
+ it "raises IOError if called on a closed IO" do
+ @io.close
+ lambda { @io.close_on_exec? }.should raise_error(IOError)
+ end
+ end
+end
diff --git a/spec/ruby/core/io/close_read_spec.rb b/spec/ruby/core/io/close_read_spec.rb
new file mode 100644
index 0000000000..9783cb252a
--- /dev/null
+++ b/spec/ruby/core/io/close_read_spec.rb
@@ -0,0 +1,60 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#close_read" do
+
+ before :each do
+ @io = IO.popen 'cat', "r+"
+ @path = tmp('io.close.txt')
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @path
+ end
+
+ it "closes the read end of a duplex I/O stream" do
+ @io.close_read
+
+ lambda { @io.read }.should raise_error(IOError)
+ end
+
+ it "does nothing on subsequent invocations" do
+ @io.close_read
+
+ @io.close_read.should be_nil
+ end
+
+ it "allows subsequent invocation of close" do
+ @io.close_read
+
+ lambda { @io.close }.should_not raise_error
+ end
+
+ it "raises an IOError if the stream is writable and not duplexed" do
+ io = File.open @path, 'w'
+
+ begin
+ lambda { io.close_read }.should raise_error(IOError)
+ ensure
+ io.close unless io.closed?
+ end
+ end
+
+ it "closes the stream if it is neither writable nor duplexed" do
+ io_close_path = @path
+ touch io_close_path
+
+ io = File.open io_close_path
+
+ io.close_read
+
+ io.closed?.should == true
+ end
+
+ it "does nothing on closed stream" do
+ @io.close
+
+ @io.close_read.should be_nil
+ end
+end
diff --git a/spec/ruby/core/io/close_spec.rb b/spec/ruby/core/io/close_spec.rb
new file mode 100644
index 0000000000..b7aa2276d1
--- /dev/null
+++ b/spec/ruby/core/io/close_spec.rb
@@ -0,0 +1,97 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#close" do
+ before :each do
+ @name = tmp('io_close.txt')
+ @io = new_io @name
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @name
+ end
+
+ it "closes the stream" do
+ @io.close
+ @io.closed?.should == true
+ end
+
+ it "returns nil" do
+ @io.close.should == nil
+ end
+
+ it "raises an IOError reading from a closed IO" do
+ @io.close
+ lambda { @io.read }.should raise_error(IOError)
+ end
+
+ it "raises an IOError writing to a closed IO" do
+ @io.close
+ lambda { @io.write "data" }.should raise_error(IOError)
+ end
+
+ it 'does not close the stream if autoclose is false' do
+ other_io = IO.new(@io.fileno)
+ other_io.autoclose = false
+ other_io.close
+ lambda { @io.write "data" }.should_not raise_error(IOError)
+ end
+
+ it "does nothing if already closed" do
+ @io.close
+
+ @io.close.should be_nil
+ end
+
+ ruby_version_is '2.5' do
+ it 'raises an IOError with a clear message' do
+ read_io, write_io = IO.pipe
+ going_to_read = false
+ thread = Thread.new do
+ lambda do
+ going_to_read = true
+ read_io.read
+ end.should raise_error(IOError, 'stream closed in another thread')
+ end
+
+ Thread.pass until going_to_read && thread.stop?
+ read_io.close
+ thread.join
+ write_io.close
+ end
+ end
+end
+
+describe "IO#close on an IO.popen stream" do
+
+ it "clears #pid" do
+ io = IO.popen ruby_cmd('r = loop{puts "y"; 0} rescue 1; exit r'), 'r'
+
+ io.pid.should_not == 0
+
+ io.close
+
+ lambda { io.pid }.should raise_error(IOError)
+ end
+
+ it "sets $?" do
+ io = IO.popen ruby_cmd('exit 0'), 'r'
+ io.close
+
+ $?.exitstatus.should == 0
+
+ io = IO.popen ruby_cmd('exit 1'), 'r'
+ io.close
+
+ $?.exitstatus.should == 1
+ end
+
+ it "waits for the child to exit" do
+ io = IO.popen ruby_cmd('r = loop{puts "y"; 0} rescue 1; exit r'), 'r'
+ io.close
+
+ $?.exitstatus.should_not == 0
+ end
+
+end
diff --git a/spec/ruby/core/io/close_write_spec.rb b/spec/ruby/core/io/close_write_spec.rb
new file mode 100644
index 0000000000..8643659025
--- /dev/null
+++ b/spec/ruby/core/io/close_write_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#close_write" do
+ before :each do
+ @io = IO.popen 'cat', 'r+'
+ @path = tmp('io.close.txt')
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @path
+ end
+
+ it "closes the write end of a duplex I/O stream" do
+ @io.close_write
+
+ lambda { @io.write "attempt to write" }.should raise_error(IOError)
+ end
+
+ it "does nothing on subsequent invocations" do
+ @io.close_write
+
+ @io.close_write.should be_nil
+ end
+
+ it "allows subsequent invocation of close" do
+ @io.close_write
+
+ lambda { @io.close }.should_not raise_error
+ end
+
+ it "raises an IOError if the stream is readable and not duplexed" do
+ io = File.open @path, 'w+'
+
+ begin
+ lambda { io.close_write }.should raise_error(IOError)
+ ensure
+ io.close unless io.closed?
+ end
+ end
+
+ it "closes the stream if it is neither readable nor duplexed" do
+ io = File.open @path, 'w'
+
+ io.close_write
+
+ io.closed?.should == true
+ end
+
+ it "flushes and closes the write stream" do
+ @io.puts '12345'
+
+ @io.close_write
+
+ @io.read.should == "12345\n"
+ end
+
+ it "does nothing on closed stream" do
+ @io.close
+
+ @io.close_write.should be_nil
+ end
+end
diff --git a/spec/ruby/core/io/closed_spec.rb b/spec/ruby/core/io/closed_spec.rb
new file mode 100644
index 0000000000..7316546a0d
--- /dev/null
+++ b/spec/ruby/core/io/closed_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#closed?" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close
+ end
+
+ it "returns true on closed stream" do
+ IOSpecs.closed_io.closed?.should be_true
+ end
+
+ it "returns false on open stream" do
+ @io.closed?.should be_false
+ end
+end
diff --git a/spec/ruby/core/io/codepoints_spec.rb b/spec/ruby/core/io/codepoints_spec.rb
new file mode 100644
index 0000000000..915d99c027
--- /dev/null
+++ b/spec/ruby/core/io/codepoints_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/codepoints'
+
+# See redmine #1667
+describe "IO#codepoints" do
+ it_behaves_like :io_codepoints, :codepoints
+end
+
+describe "IO#codepoints" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "calls the given block" do
+ r = []
+ @io.codepoints { |c| r << c }
+ r[24].should == 232
+ r.last.should == 10
+ end
+end
diff --git a/spec/ruby/core/io/constants_spec.rb b/spec/ruby/core/io/constants_spec.rb
new file mode 100644
index 0000000000..f9dccd08b9
--- /dev/null
+++ b/spec/ruby/core/io/constants_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+describe "IO::SEEK_SET" do
+ it "is defined" do
+ IO.const_defined?(:SEEK_SET).should == true
+ end
+end
+
+describe "IO::SEEK_CUR" do
+ it "is defined" do
+ IO.const_defined?(:SEEK_CUR).should == true
+ end
+end
+
+describe "IO::SEEK_END" do
+ it "is defined" do
+ IO.const_defined?(:SEEK_END).should == true
+ end
+end
diff --git a/spec/ruby/core/io/copy_stream_spec.rb b/spec/ruby/core/io/copy_stream_spec.rb
new file mode 100644
index 0000000000..622be0818b
--- /dev/null
+++ b/spec/ruby/core/io/copy_stream_spec.rb
@@ -0,0 +1,282 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe :io_copy_stream_to_file, shared: true do
+ it "copies the entire IO contents to the file" do
+ IO.copy_stream(@object.from, @to_name)
+ File.read(@to_name).should == @content
+ IO.copy_stream(@from_bigfile, @to_name)
+ File.read(@to_name).should == @content_bigfile
+ end
+
+ it "returns the number of bytes copied" do
+ IO.copy_stream(@object.from, @to_name).should == @size
+ IO.copy_stream(@from_bigfile, @to_name).should == @size_bigfile
+ end
+
+ it "copies only length bytes when specified" do
+ IO.copy_stream(@object.from, @to_name, 8).should == 8
+ File.read(@to_name).should == "Line one"
+ end
+
+ it "calls #to_path to convert on object to a file name" do
+ obj = mock("io_copy_stream_to")
+ obj.should_receive(:to_path).and_return(@to_name)
+
+ IO.copy_stream(@object.from, obj)
+ File.read(@to_name).should == @content
+ end
+
+ it "raises a TypeError if #to_path does not return a String" do
+ obj = mock("io_copy_stream_to")
+ obj.should_receive(:to_path).and_return(1)
+
+ lambda { IO.copy_stream(@object.from, obj) }.should raise_error(TypeError)
+ end
+end
+
+describe :io_copy_stream_to_file_with_offset, shared: true do
+ platform_is_not :windows do
+ it "copies only length bytes from the offset" do
+ IO.copy_stream(@object.from, @to_name, 8, 4).should == 8
+ File.read(@to_name).should == " one\n\nLi"
+ end
+ end
+end
+
+describe :io_copy_stream_to_io, shared: true do
+ it "copies the entire IO contents to the IO" do
+ IO.copy_stream(@object.from, @to_io)
+ File.read(@to_name).should == @content
+ IO.copy_stream(@from_bigfile, @to_io)
+ File.read(@to_name).should == (@content + @content_bigfile)
+ end
+
+ it "returns the number of bytes copied" do
+ IO.copy_stream(@object.from, @to_io).should == @size
+ IO.copy_stream(@from_bigfile, @to_io).should == @size_bigfile
+ end
+
+ it "starts writing at the destination IO's current position" do
+ @to_io.write("prelude ")
+ IO.copy_stream(@object.from, @to_io)
+ File.read(@to_name).should == ("prelude " + @content)
+ end
+
+ it "leaves the destination IO position at the last write" do
+ IO.copy_stream(@object.from, @to_io)
+ @to_io.pos.should == @size
+ end
+
+ it "raises an IOError if the destination IO is not open for writing" do
+ @to_io.close
+ @to_io = new_io @to_name, "r"
+ lambda { IO.copy_stream @object.from, @to_io }.should raise_error(IOError)
+ end
+
+ it "does not close the destination IO" do
+ IO.copy_stream(@object.from, @to_io)
+ @to_io.closed?.should be_false
+ end
+
+ it "copies only length bytes when specified" do
+ IO.copy_stream(@object.from, @to_io, 8).should == 8
+ File.read(@to_name).should == "Line one"
+ end
+end
+
+describe :io_copy_stream_to_io_with_offset, shared: true do
+ platform_is_not :windows do
+ it "copies only length bytes from the offset" do
+ IO.copy_stream(@object.from, @to_io, 8, 4).should == 8
+ File.read(@to_name).should == " one\n\nLi"
+ end
+ end
+end
+
+describe "IO.copy_stream" do
+ before :each do
+ @from_name = fixture __FILE__, "copy_stream.txt"
+ @to_name = tmp("io_copy_stream_io_name")
+
+ @content = IO.read(@from_name)
+ @size = @content.size
+
+ @from_bigfile = tmp("io_copy_stream_bigfile")
+ @content_bigfile = "A" * 17_000
+ touch(@from_bigfile){|f| f.print @content_bigfile }
+ @size_bigfile = @content_bigfile.size
+ end
+
+ after :each do
+ rm_r @to_name, @from_bigfile
+ end
+
+ describe "from an IO" do
+ before :each do
+ @from_io = new_io @from_name, "rb"
+ IOSpecs::CopyStream.from = @from_io
+ end
+
+ after :each do
+ @from_io.close
+ end
+
+ it "raises an IOError if the source IO is not open for reading" do
+ @from_io.close
+ @from_io = new_io @from_bigfile, "a"
+ lambda { IO.copy_stream @from_io, @to_name }.should raise_error(IOError)
+ end
+
+ it "does not close the source IO" do
+ IO.copy_stream(@from_io, @to_name)
+ @from_io.closed?.should be_false
+ end
+
+ platform_is_not :windows do
+ it "does not change the IO offset when an offset is specified" do
+ @from_io.pos = 10
+ IO.copy_stream(@from_io, @to_name, 8, 4)
+ @from_io.pos.should == 10
+ end
+ end
+
+ it "does change the IO offset when an offset is not specified" do
+ @from_io.pos = 10
+ IO.copy_stream(@from_io, @to_name)
+ @from_io.pos.should == 42
+ end
+
+ describe "to a file name" do
+ it_behaves_like :io_copy_stream_to_file, nil, IOSpecs::CopyStream
+ it_behaves_like :io_copy_stream_to_file_with_offset, nil, IOSpecs::CopyStream
+ end
+
+ describe "to an IO" do
+ before :each do
+ @to_io = new_io @to_name, "wb"
+ end
+
+ after :each do
+ @to_io.close
+ end
+
+ it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream
+ it_behaves_like :io_copy_stream_to_io_with_offset, nil, IOSpecs::CopyStream
+ end
+ end
+
+ describe "from a file name" do
+ before :each do
+ IOSpecs::CopyStream.from = @from_name
+ end
+
+ it "calls #to_path to convert on object to a file name" do
+ obj = mock("io_copy_stream_from")
+ obj.should_receive(:to_path).and_return(@from_name)
+
+ IO.copy_stream(obj, @to_name)
+ File.read(@to_name).should == @content
+ end
+
+ it "raises a TypeError if #to_path does not return a String" do
+ obj = mock("io_copy_stream_from")
+ obj.should_receive(:to_path).and_return(1)
+
+ lambda { IO.copy_stream(obj, @to_name) }.should raise_error(TypeError)
+ end
+
+ describe "to a file name" do
+ it_behaves_like :io_copy_stream_to_file, nil, IOSpecs::CopyStream
+ it_behaves_like :io_copy_stream_to_file_with_offset, nil, IOSpecs::CopyStream
+ end
+
+ describe "to an IO" do
+ before :each do
+ @to_io = new_io @to_name, "wb"
+ end
+
+ after :each do
+ @to_io.close
+ end
+
+ it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream
+ it_behaves_like :io_copy_stream_to_io_with_offset, nil, IOSpecs::CopyStream
+ end
+ end
+
+ describe "from a pipe IO" do
+ before :each do
+ @from_io = IOSpecs.pipe_fixture(@content)
+ IOSpecs::CopyStream.from = @from_io
+ end
+
+ after :each do
+ @from_io.close
+ end
+
+ it "does not close the source IO" do
+ IO.copy_stream(@from_io, @to_name)
+ @from_io.closed?.should be_false
+ end
+
+ platform_is_not :windows do
+ it "raises an error when an offset is specified" do
+ lambda { IO.copy_stream(@from_io, @to_name, 8, 4) }.should raise_error(Errno::ESPIPE)
+ end
+ end
+
+ describe "to a file name" do
+ it_behaves_like :io_copy_stream_to_file, nil, IOSpecs::CopyStream
+ end
+
+ describe "to an IO" do
+ before :each do
+ @to_io = new_io @to_name, "wb"
+ end
+
+ after :each do
+ @to_io.close
+ end
+
+ it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream
+ end
+ end
+
+ describe "with non-IO Objects" do
+ before do
+ @io = new_io @from_name, "rb"
+ end
+
+ after do
+ @io.close unless @io.closed?
+ end
+
+ it "calls #readpartial on the source Object if defined" do
+ from = IOSpecs::CopyStreamReadPartial.new @io
+
+ IO.copy_stream(from, @to_name)
+ File.read(@to_name).should == @content
+ end
+
+ it "calls #read on the source Object" do
+ from = IOSpecs::CopyStreamRead.new @io
+
+ IO.copy_stream(from, @to_name)
+ File.read(@to_name).should == @content
+ end
+
+ it "calls #write on the destination Object" do
+ to = mock("io_copy_stream_to_object")
+ to.should_receive(:write).with(@content).and_return(@content.size)
+
+ IO.copy_stream(@from_name, to)
+ end
+
+ it "does not call #pos on the source if no offset is given" do
+ @io.should_not_receive(:pos)
+ IO.copy_stream(@io, @to_name)
+ end
+
+ end
+end
diff --git a/spec/ruby/core/io/dup_spec.rb b/spec/ruby/core/io/dup_spec.rb
new file mode 100644
index 0000000000..421ae27f24
--- /dev/null
+++ b/spec/ruby/core/io/dup_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#dup" do
+ before :each do
+ @file = tmp("rubinius_spec_io_dup_#{$$}_#{Time.now.to_f}")
+ @f = File.open @file, 'w+'
+ @i = @f.dup
+
+ @f.sync = true
+ @i.sync = true
+ end
+
+ after :each do
+ @i.close if @i && !@i.closed?
+ @f.close if @f && !@f.closed?
+ rm_r @file
+ end
+
+ it "returns a new IO instance" do
+ @i.class.should == @f.class
+ end
+
+ it "sets a new descriptor on the returned object" do
+ @i.fileno.should_not == @f.fileno
+ end
+
+quarantine! do # This does not appear to be consistent across platforms
+ it "shares the original stream between the two IOs" do
+ start = @f.pos
+ @i.pos.should == start
+
+ s = "Hello, wo.. wait, where am I?\n"
+ s2 = "<evil voice> Muhahahaa!"
+
+ @f.write s
+ @i.pos.should == @f.pos
+
+ @i.rewind
+ @i.gets.should == s
+
+ @i.rewind
+ @i.write s2
+
+ @f.rewind
+ @f.gets.should == "#{s2}\n"
+ end
+end
+
+ it "allows closing the new IO without affecting the original" do
+ @i.close
+ lambda { @f.gets }.should_not raise_error(Exception)
+
+ @i.closed?.should == true
+ @f.closed?.should == false
+ end
+
+ it "allows closing the original IO without affecting the new one" do
+ @f.close
+ lambda { @i.gets }.should_not raise_error(Exception)
+
+ @i.closed?.should == false
+ @f.closed?.should == true
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.dup }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/each_byte_spec.rb b/spec/ruby/core/io/each_byte_spec.rb
new file mode 100644
index 0000000000..9cdb1ac0c9
--- /dev/null
+++ b/spec/ruby/core/io/each_byte_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#each_byte" do
+ before :each do
+ ScratchPad.record []
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.each_byte {} }.should raise_error(IOError)
+ end
+
+ it "yields each byte" do
+ count = 0
+ @io.each_byte do |byte|
+ ScratchPad << byte
+ break if 4 < count += 1
+ end
+
+ ScratchPad.recorded.should == [86, 111, 105, 99, 105]
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ enum = @io.each_byte
+ enum.should be_an_instance_of(Enumerator)
+ enum.first(5).should == [86, 111, 105, 99, 105]
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ @io.each_byte.size.should == nil
+ end
+ end
+ end
+ end
+end
+
+describe "IO#each_byte" do
+ before :each do
+ @io = IOSpecs.io_fixture "empty.txt"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "returns self on an empty stream" do
+ @io.each_byte { |b| }.should equal(@io)
+ end
+end
diff --git a/spec/ruby/core/io/each_char_spec.rb b/spec/ruby/core/io/each_char_spec.rb
new file mode 100644
index 0000000000..5d460a1e7c
--- /dev/null
+++ b/spec/ruby/core/io/each_char_spec.rb
@@ -0,0 +1,12 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/chars'
+
+describe "IO#each_char" do
+ it_behaves_like :io_chars, :each_char
+end
+
+describe "IO#each_char" do
+ it_behaves_like :io_chars_empty, :each_char
+end
diff --git a/spec/ruby/core/io/each_codepoint_spec.rb b/spec/ruby/core/io/each_codepoint_spec.rb
new file mode 100644
index 0000000000..19824c38e4
--- /dev/null
+++ b/spec/ruby/core/io/each_codepoint_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/codepoints'
+
+# See redmine #1667
+describe "IO#each_codepoint" do
+ it_behaves_like :io_codepoints, :codepoints
+end
+
+describe "IO#each_codepoint" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ it "calls the given block" do
+ r = []
+ @io.each_codepoint { |c| r << c }
+ r[24].should == 232
+ r.last.should == 10
+ end
+
+ it "returns self" do
+ @io.each_codepoint { |l| l }.should equal(@io)
+ end
+end
+
+describe "IO#each_codepoint" do
+ before :each do
+ @io = IOSpecs.io_fixture("incomplete.txt")
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ it "raises an exception at incomplete character before EOF when conversion takes place" do
+ lambda { @io.each_codepoint {} }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/io/each_line_spec.rb b/spec/ruby/core/io/each_line_spec.rb
new file mode 100644
index 0000000000..58d26b325d
--- /dev/null
+++ b/spec/ruby/core/io/each_line_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/each'
+
+describe "IO#each_line" do
+ it_behaves_like :io_each, :each_line
+end
+
+describe "IO#each_line" do
+ it_behaves_like :io_each_default_separator, :each_line
+end
diff --git a/spec/ruby/core/io/each_spec.rb b/spec/ruby/core/io/each_spec.rb
new file mode 100644
index 0000000000..91ecbd19c8
--- /dev/null
+++ b/spec/ruby/core/io/each_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/each'
+
+describe "IO#each" do
+ it_behaves_like :io_each, :each
+end
+
+describe "IO#each" do
+ it_behaves_like :io_each_default_separator, :each
+end
diff --git a/spec/ruby/core/io/eof_spec.rb b/spec/ruby/core/io/eof_spec.rb
new file mode 100644
index 0000000000..3ab389af09
--- /dev/null
+++ b/spec/ruby/core/io/eof_spec.rb
@@ -0,0 +1,107 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#eof?" do
+ before :each do
+ @name = tmp("empty.txt")
+ touch @name
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "returns true on an empty stream that has just been opened" do
+ File.open(@name) { |empty| empty.eof?.should == true }
+ end
+
+ it "raises IOError on stream not opened for reading" do
+ lambda do
+ File.open(@name, "w") { |f| f.eof? }
+ end.should raise_error(IOError)
+ end
+end
+
+describe "IO#eof?" do
+ before :each do
+ @name = fixture __FILE__, "lines.txt"
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close if @io && !@io.closed?
+ end
+
+ it "returns false when not at end of file" do
+ @io.read 1
+ @io.eof?.should == false
+ end
+
+ it "returns true after reading with read with no parameters" do
+ @io.read()
+ @io.eof?.should == true
+ end
+
+ it "returns true after reading with read" do
+ @io.read(File.size(@name))
+ @io.eof?.should == true
+ end
+
+ it "returns true after reading with sysread" do
+ @io.sysread(File.size(@name))
+ @io.eof?.should == true
+ end
+
+ it "returns true after reading with readlines" do
+ @io.readlines
+ @io.eof?.should == true
+ end
+
+ it "returns false on just opened non-empty stream" do
+ @io.eof?.should == false
+ end
+
+ it "does not consume the data from the stream" do
+ @io.eof?.should == false
+ @io.getc.should == 'V'
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.eof? }.should raise_error(IOError)
+ end
+
+ it "raises IOError on stream closed for reading by close_read" do
+ @io.close_read
+ lambda { @io.eof? }.should raise_error(IOError)
+ end
+
+ it "returns true on one-byte stream after single-byte read" do
+ File.open(File.dirname(__FILE__) + '/fixtures/one_byte.txt') { |one_byte|
+ one_byte.read(1)
+ one_byte.eof?.should == true
+ }
+ end
+end
+
+describe "IO#eof?" do
+ after :each do
+ @r.close if @r && !@r.closed?
+ @w.close if @w && !@w.closed?
+ end
+
+ it "returns true on receiving side of Pipe when writing side is closed" do
+ @r, @w = IO.pipe
+ @w.close
+ @r.eof?.should == true
+ end
+
+ it "returns false on receiving side of Pipe when writing side wrote some data" do
+ @r, @w = IO.pipe
+ @w.puts "hello"
+ @r.eof?.should == false
+ @w.close
+ @r.eof?.should == false
+ @r.read
+ @r.eof?.should == true
+ end
+end
diff --git a/spec/ruby/core/io/external_encoding_spec.rb b/spec/ruby/core/io/external_encoding_spec.rb
new file mode 100644
index 0000000000..e625484670
--- /dev/null
+++ b/spec/ruby/core/io/external_encoding_spec.rb
@@ -0,0 +1,218 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe :io_external_encoding_write, shared: true do
+ describe "when Encoding.default_internal is nil" do
+ before :each do
+ Encoding.default_internal = nil
+ end
+
+ it "returns nil" do
+ @io = new_io @name, @object
+ Encoding.default_external = Encoding::IBM437
+ @io.external_encoding.should be_nil
+ end
+
+ it "returns the external encoding specified when the instance was created" do
+ @io = new_io @name, "#{@object}:ibm866"
+ Encoding.default_external = Encoding::IBM437
+ @io.external_encoding.should equal(Encoding::IBM866)
+ end
+
+ it "returns the encoding set by #set_encoding" do
+ @io = new_io @name, "#{@object}:ibm866"
+ @io.set_encoding Encoding::EUC_JP, nil
+ @io.external_encoding.should equal(Encoding::EUC_JP)
+ end
+ end
+
+ describe "when Encoding.default_external != Encoding.default_internal" do
+ before :each do
+ Encoding.default_external = Encoding::IBM437
+ Encoding.default_internal = Encoding::IBM866
+ end
+
+ it "returns the value of Encoding.default_external when the instance was created" do
+ @io = new_io @name, @object
+ Encoding.default_external = Encoding::UTF_8
+ @io.external_encoding.should equal(Encoding::IBM437)
+ end
+
+ it "returns the external encoding specified when the instance was created" do
+ @io = new_io @name, "#{@object}:ibm866"
+ Encoding.default_external = Encoding::IBM437
+ @io.external_encoding.should equal(Encoding::IBM866)
+ end
+
+ it "returns the encoding set by #set_encoding" do
+ @io = new_io @name, "#{@object}:ibm866"
+ @io.set_encoding Encoding::EUC_JP, nil
+ @io.external_encoding.should equal(Encoding::EUC_JP)
+ end
+ end
+
+ describe "when Encoding.default_external == Encoding.default_internal" do
+ before :each do
+ Encoding.default_external = Encoding::IBM866
+ Encoding.default_internal = Encoding::IBM866
+ end
+
+ it "returns the value of Encoding.default_external when the instance was created" do
+ @io = new_io @name, @object
+ Encoding.default_external = Encoding::UTF_8
+ @io.external_encoding.should equal(Encoding::IBM866)
+ end
+
+ it "returns the external encoding specified when the instance was created" do
+ @io = new_io @name, "#{@object}:ibm866"
+ Encoding.default_external = Encoding::IBM437
+ @io.external_encoding.should equal(Encoding::IBM866)
+ end
+
+ it "returns the encoding set by #set_encoding" do
+ @io = new_io @name, "#{@object}:ibm866"
+ @io.set_encoding Encoding::EUC_JP, nil
+ @io.external_encoding.should equal(Encoding::EUC_JP)
+ end
+ end
+ end
+
+ describe "IO#external_encoding" do
+ before :each do
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+
+ @name = tmp("io_external_encoding")
+ touch(@name)
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+
+ @io.close if @io
+ rm_r @name
+ end
+
+ describe "with 'r' mode" do
+ describe "when Encoding.default_internal is nil" do
+ before :each do
+ Encoding.default_internal = nil
+ Encoding.default_external = Encoding::IBM866
+ end
+
+ it "returns Encoding.default_external if the external encoding is not set" do
+ @io = new_io @name, "r"
+ @io.external_encoding.should equal(Encoding::IBM866)
+ end
+
+ it "returns Encoding.default_external when that encoding is changed after the instance is created" do
+ @io = new_io @name, "r"
+ Encoding.default_external = Encoding::IBM437
+ @io.external_encoding.should equal(Encoding::IBM437)
+ end
+
+ it "returns the external encoding specified when the instance was created" do
+ @io = new_io @name, "r:utf-8"
+ Encoding.default_external = Encoding::IBM437
+ @io.external_encoding.should equal(Encoding::UTF_8)
+ end
+
+ it "returns the encoding set by #set_encoding" do
+ @io = new_io @name, "r:utf-8"
+ @io.set_encoding Encoding::EUC_JP, nil
+ @io.external_encoding.should equal(Encoding::EUC_JP)
+ end
+ end
+
+ describe "when Encoding.default_external == Encoding.default_internal" do
+ before :each do
+ Encoding.default_external = Encoding::IBM866
+ Encoding.default_internal = Encoding::IBM866
+ end
+
+ it "returns the value of Encoding.default_external when the instance was created" do
+ @io = new_io @name, "r"
+ Encoding.default_external = Encoding::IBM437
+ @io.external_encoding.should equal(Encoding::IBM866)
+ end
+
+ it "returns the external encoding specified when the instance was created" do
+ @io = new_io @name, "r:utf-8"
+ Encoding.default_external = Encoding::IBM437
+ @io.external_encoding.should equal(Encoding::UTF_8)
+ end
+
+ it "returns the encoding set by #set_encoding" do
+ @io = new_io @name, "r:utf-8"
+ @io.set_encoding Encoding::EUC_JP, nil
+ @io.external_encoding.should equal(Encoding::EUC_JP)
+ end
+ end
+
+ describe "when Encoding.default_external != Encoding.default_internal" do
+ before :each do
+ Encoding.default_external = Encoding::IBM437
+ Encoding.default_internal = Encoding::IBM866
+ end
+
+
+ it "returns the external encoding specified when the instance was created" do
+ @io = new_io @name, "r:utf-8"
+ Encoding.default_external = Encoding::IBM437
+ @io.external_encoding.should equal(Encoding::UTF_8)
+ end
+
+ it "returns the encoding set by #set_encoding" do
+ @io = new_io @name, "r:utf-8"
+ @io.set_encoding Encoding::EUC_JP, nil
+ @io.external_encoding.should equal(Encoding::EUC_JP)
+ end
+ end
+ end
+
+ describe "with 'rb' mode" do
+ it "returns Encoding::ASCII_8BIT" do
+ @io = new_io @name, "rb"
+ @io.external_encoding.should equal(Encoding::ASCII_8BIT)
+ end
+
+ it "returns the external encoding specified by the mode argument" do
+ @io = new_io @name, "rb:ibm437"
+ @io.external_encoding.should equal(Encoding::IBM437)
+ end
+ end
+
+ describe "with 'r+' mode" do
+ it_behaves_like :io_external_encoding_write, nil, "r+"
+ end
+
+ describe "with 'w' mode" do
+ it_behaves_like :io_external_encoding_write, nil, "w"
+ end
+
+ describe "with 'wb' mode" do
+ it "returns Encoding::ASCII_8BIT" do
+ @io = new_io @name, "wb"
+ @io.external_encoding.should equal(Encoding::ASCII_8BIT)
+ end
+
+ it "returns the external encoding specified by the mode argument" do
+ @io = new_io @name, "wb:ibm437"
+ @io.external_encoding.should equal(Encoding::IBM437)
+ end
+ end
+
+ describe "with 'w+' mode" do
+ it_behaves_like :io_external_encoding_write, nil, "w+"
+ end
+
+ describe "with 'a' mode" do
+ it_behaves_like :io_external_encoding_write, nil, "a"
+ end
+
+ describe "with 'a+' mode" do
+ it_behaves_like :io_external_encoding_write, nil, "a+"
+ end
+ end
+end
diff --git a/spec/ruby/core/io/fcntl_spec.rb b/spec/ruby/core/io/fcntl_spec.rb
new file mode 100644
index 0000000000..049f92c0a2
--- /dev/null
+++ b/spec/ruby/core/io/fcntl_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#fcntl" do
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.fcntl(5, 5) }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/fdatasync_spec.rb b/spec/ruby/core/io/fdatasync_spec.rb
new file mode 100644
index 0000000000..6242258ea0
--- /dev/null
+++ b/spec/ruby/core/io/fdatasync_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "IO#fdatasync" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/io/fileno_spec.rb b/spec/ruby/core/io/fileno_spec.rb
new file mode 100644
index 0000000000..d7aff99e72
--- /dev/null
+++ b/spec/ruby/core/io/fileno_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#fileno" do
+ it "returns the numeric file descriptor of the given IO object" do
+ $stdout.fileno.should == 1
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.fileno }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/fixtures/bom_UTF-16BE.txt b/spec/ruby/core/io/fixtures/bom_UTF-16BE.txt
new file mode 100644
index 0000000000..c7c42e9de4
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/bom_UTF-16BE.txt
Binary files differ
diff --git a/spec/ruby/core/io/fixtures/bom_UTF-16LE.txt b/spec/ruby/core/io/fixtures/bom_UTF-16LE.txt
new file mode 100644
index 0000000000..53642b6984
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/bom_UTF-16LE.txt
Binary files differ
diff --git a/spec/ruby/core/io/fixtures/bom_UTF-32BE.txt b/spec/ruby/core/io/fixtures/bom_UTF-32BE.txt
new file mode 100644
index 0000000000..c5efe6c278
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/bom_UTF-32BE.txt
Binary files differ
diff --git a/spec/ruby/core/io/fixtures/bom_UTF-32LE.txt b/spec/ruby/core/io/fixtures/bom_UTF-32LE.txt
new file mode 100644
index 0000000000..1168384393
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/bom_UTF-32LE.txt
Binary files differ
diff --git a/spec/ruby/core/io/fixtures/bom_UTF-8.txt b/spec/ruby/core/io/fixtures/bom_UTF-8.txt
new file mode 100644
index 0000000000..ca971bef61
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/bom_UTF-8.txt
@@ -0,0 +1 @@
+UTF-8
diff --git a/spec/ruby/core/io/fixtures/classes.rb b/spec/ruby/core/io/fixtures/classes.rb
new file mode 100644
index 0000000000..a771e3d929
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/classes.rb
@@ -0,0 +1,189 @@
+# -*- encoding: utf-8 -*-
+
+module IOSpecs
+ class SubIO < IO
+ end
+
+ def self.collector
+ Proc.new { |x| ScratchPad << x }
+ end
+
+ def self.lines
+ [ "Voici la ligne une.\n",
+ "Qui \303\250 la linea due.\n",
+ "\n",
+ "\n",
+ "Aqu\303\255 est\303\241 la l\303\255nea tres.\n",
+ "Hier ist Zeile vier.\n",
+ "\n",
+ "Est\303\241 aqui a linha cinco.\n",
+ "Here is line six.\n" ]
+ end
+
+ def self.lines_without_newline_characters
+ [ "Voici la ligne une.",
+ "Qui \303\250 la linea due.",
+ "",
+ "",
+ "Aqu\303\255 est\303\241 la l\303\255nea tres.",
+ "Hier ist Zeile vier.",
+ "",
+ "Est\303\241 aqui a linha cinco.",
+ "Here is line six." ]
+ end
+
+ def self.lines_limit
+ [ "Voici la l",
+ "igne une.\n",
+ "Qui è la ",
+ "linea due.",
+ "\n",
+ "\n",
+ "\n",
+ "Aquí está",
+ " la línea",
+ " tres.\n",
+ "Hier ist Z",
+ "eile vier.",
+ "\n",
+ "\n",
+ "Está aqui",
+ " a linha c",
+ "inco.\n",
+ "Here is li",
+ "ne six.\n" ]
+ end
+
+ def self.lines_space_separator_limit
+ [ "Voici ",
+ "la ",
+ "ligne ",
+ "une.\nQui ",
+ "è ",
+ "la ",
+ "linea ",
+ "due.\n\n\nAqu",
+ "í ",
+ "está ",
+ "la ",
+ "línea ",
+ "tres.\nHier",
+ " ",
+ "ist ",
+ "Zeile ",
+ "vier.\n\nEst",
+ "á ",
+ "aqui ",
+ "a ",
+ "linha ",
+ "cinco.\nHer",
+ "e ",
+ "is ",
+ "line ",
+ "six.\n" ]
+ end
+
+ def self.lines_r_separator
+ [ "Voici la ligne une.\nQui \303\250 la linea due.\n\n\nAqu\303\255 est\303\241 la l\303\255nea tr",
+ "es.\nHier",
+ " ist Zeile vier",
+ ".\n\nEst\303\241 aqui a linha cinco.\nHer",
+ "e is line six.\n" ]
+ end
+
+ def self.lines_empty_separator
+ [ "Voici la ligne une.\nQui \303\250 la linea due.\n\n",
+ "Aqu\303\255 est\303\241 la l\303\255nea tres.\nHier ist Zeile vier.\n\n",
+ "Est\303\241 aqui a linha cinco.\nHere is line six.\n" ]
+ end
+
+ def self.lines_space_separator
+ [ "Voici ", "la ", "ligne ", "une.\nQui ",
+ "\303\250 ", "la ", "linea ", "due.\n\n\nAqu\303\255 ",
+ "est\303\241 ", "la ", "l\303\255nea ", "tres.\nHier ",
+ "ist ", "Zeile ", "vier.\n\nEst\303\241 ", "aqui ", "a ",
+ "linha ", "cinco.\nHere ", "is ", "line ", "six.\n" ]
+ end
+
+ def self.lines_arbitrary_separator
+ [ "Voici la ligne une.\nQui \303\250",
+ " la linea due.\n\n\nAqu\303\255 est\303\241 la l\303\255nea tres.\nHier ist Zeile vier.\n\nEst\303\241 aqui a linha cinco.\nHere is line six.\n" ]
+ end
+
+ def self.paragraphs
+ [ "Voici la ligne une.\nQui \303\250 la linea due.\n\n",
+ "Aqu\303\255 est\303\241 la l\303\255nea tres.\nHier ist Zeile vier.\n\n",
+ "Est\303\241 aqui a linha cinco.\nHere is line six.\n" ]
+ end
+
+ # Creates an IO instance for an existing fixture file. The
+ # file should obviously not be deleted.
+ def self.io_fixture(name, options_or_mode="r:utf-8")
+ path = fixture __FILE__, name
+ name = path if File.exist? path
+ new_io name, options_or_mode
+ end
+
+ # Returns a closed instance of IO that was opened to reference
+ # a fixture file (i.e. the IO instance was perfectly valid at
+ # one point but is now closed).
+ def self.closed_io
+ io = io_fixture "lines.txt"
+ io.close
+ io
+ end
+
+ # Creates a pipe-based IO fixture containing the specified
+ # contents
+ def self.pipe_fixture(content)
+ source, sink = IO.pipe
+ sink.write content
+ sink.close
+ source
+ end
+
+ # Defines +method+ on +obj+ using the provided +block+. This
+ # special helper is needed for e.g. IO.open specs to avoid
+ # mock methods preventing IO#close from running.
+ def self.io_mock(obj, method, &block)
+ obj.singleton_class.send(:define_method, method, &block)
+ end
+
+ module CopyStream
+ def self.from=(from)
+ @from = from
+ end
+
+ def self.from
+ @from
+ end
+ end
+
+ class CopyStreamRead
+ def initialize(io)
+ @io = io
+ end
+
+ def read(size, buf=nil)
+ @io.read size, buf
+ end
+
+ def send(*args)
+ raise "send called"
+ end
+ end
+
+ class CopyStreamReadPartial
+ def initialize(io)
+ @io = io
+ end
+
+ def readpartial(size, buf=nil)
+ @io.readpartial size, buf
+ end
+
+ def send(*args)
+ raise "send called"
+ end
+ end
+end
diff --git a/spec/ruby/core/io/fixtures/copy_stream.txt b/spec/ruby/core/io/fixtures/copy_stream.txt
new file mode 100644
index 0000000000..a2d827b351
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/copy_stream.txt
@@ -0,0 +1,6 @@
+Line one
+
+Line three
+Line four
+
+Line last
diff --git a/spec/ruby/core/io/fixtures/empty.txt b/spec/ruby/core/io/fixtures/empty.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/empty.txt
diff --git a/spec/ruby/core/io/fixtures/incomplete.txt b/spec/ruby/core/io/fixtures/incomplete.txt
new file mode 100644
index 0000000000..23d432f642
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/incomplete.txt
@@ -0,0 +1 @@
+ðŸ \ No newline at end of file
diff --git a/spec/ruby/core/io/fixtures/lines.txt b/spec/ruby/core/io/fixtures/lines.txt
new file mode 100644
index 0000000000..0959997e7b
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/lines.txt
@@ -0,0 +1,9 @@
+Voici la ligne une.
+Qui è la linea due.
+
+
+Aquí está la línea tres.
+Hier ist Zeile vier.
+
+Está aqui a linha cinco.
+Here is line six.
diff --git a/spec/ruby/core/io/fixtures/no_bom_UTF-8.txt b/spec/ruby/core/io/fixtures/no_bom_UTF-8.txt
new file mode 100644
index 0000000000..7edc66b06a
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/no_bom_UTF-8.txt
@@ -0,0 +1 @@
+UTF-8
diff --git a/spec/ruby/core/io/fixtures/numbered_lines.txt b/spec/ruby/core/io/fixtures/numbered_lines.txt
new file mode 100644
index 0000000000..70e49a3d98
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/numbered_lines.txt
@@ -0,0 +1,5 @@
+Line 1: One
+Line 2: Two
+Line 3: Three
+Line 4: Four
+Line 5: Five
diff --git a/spec/ruby/core/io/fixtures/one_byte.txt b/spec/ruby/core/io/fixtures/one_byte.txt
new file mode 100644
index 0000000000..56a6051ca2
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/one_byte.txt
@@ -0,0 +1 @@
+1 \ No newline at end of file
diff --git a/spec/ruby/core/io/fixtures/read_binary.txt b/spec/ruby/core/io/fixtures/read_binary.txt
new file mode 100644
index 0000000000..fa036dca4b
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/read_binary.txt
@@ -0,0 +1 @@
+abcâdef
diff --git a/spec/ruby/core/io/fixtures/read_euc_jp.txt b/spec/ruby/core/io/fixtures/read_euc_jp.txt
new file mode 100644
index 0000000000..0e17cd717a
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/read_euc_jp.txt
@@ -0,0 +1 @@
+¤¢¤ê¤¬¤È¤¦
diff --git a/spec/ruby/core/io/fixtures/read_text.txt b/spec/ruby/core/io/fixtures/read_text.txt
new file mode 100644
index 0000000000..5a7a80f6e4
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/read_text.txt
@@ -0,0 +1 @@
+abcâdef
diff --git a/spec/ruby/core/io/fixtures/reopen_stdout.rb b/spec/ruby/core/io/fixtures/reopen_stdout.rb
new file mode 100644
index 0000000000..506ba072bd
--- /dev/null
+++ b/spec/ruby/core/io/fixtures/reopen_stdout.rb
@@ -0,0 +1,3 @@
+STDOUT.reopen ARGV[0]
+system "echo from system"
+exec "echo from exec"
diff --git a/spec/ruby/core/io/flush_spec.rb b/spec/ruby/core/io/flush_spec.rb
new file mode 100644
index 0000000000..c81ff74a69
--- /dev/null
+++ b/spec/ruby/core/io/flush_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#flush" do
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.flush }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/for_fd_spec.rb b/spec/ruby/core/io/for_fd_spec.rb
new file mode 100644
index 0000000000..2d86361b73
--- /dev/null
+++ b/spec/ruby/core/io/for_fd_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'shared/new'
+
+describe "IO.for_fd" do
+ it_behaves_like :io_new, :for_fd
+end
+
+describe "IO.for_fd" do
+ it_behaves_like :io_new_errors, :for_fd
+end
diff --git a/spec/ruby/core/io/foreach_spec.rb b/spec/ruby/core/io/foreach_spec.rb
new file mode 100644
index 0000000000..c5c1178787
--- /dev/null
+++ b/spec/ruby/core/io/foreach_spec.rb
@@ -0,0 +1,81 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/readlines'
+
+describe "IO.foreach" do
+ before :each do
+ @name = fixture __FILE__, "lines.txt"
+ @count = 0
+ ScratchPad.record []
+ end
+
+ it "updates $. with each yield" do
+ IO.foreach(@name) { $..should == @count += 1 }
+ end
+
+ describe "when the filename starts with |" do
+ it "gets data from the standard out of the subprocess" do
+ cmd = "|sh -c 'echo hello;echo line2'"
+ platform_is :windows do
+ cmd = "|cmd.exe /C echo hello&echo line2"
+ end
+ IO.foreach(cmd) { |l| ScratchPad << l }
+ ScratchPad.recorded.should == ["hello\n", "line2\n"]
+ end
+
+ with_feature :fork do
+ it "gets data from a fork when passed -" do
+ parent_pid = $$
+
+ IO.foreach("|-") { |l| ScratchPad << l }
+
+ if $$ == parent_pid
+ ScratchPad.recorded.should == ["hello\n", "from a fork\n"]
+ else # child
+ puts "hello"
+ puts "from a fork"
+ exit!
+ end
+ end
+ end
+ end
+end
+
+describe "IO.foreach" do
+ before :each do
+ @external = Encoding.default_external
+ Encoding.default_external = Encoding::UTF_8
+
+ @name = fixture __FILE__, "lines.txt"
+ ScratchPad.record []
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ end
+
+ it "sets $_ to nil" do
+ $_ = "test"
+ IO.foreach(@name) { }
+ $_.should be_nil
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ IO.foreach(@name).should be_an_instance_of(Enumerator)
+ IO.foreach(@name).to_a.should == IOSpecs.lines
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ IO.foreach(@name).size.should == nil
+ end
+ end
+ end
+ end
+
+ it_behaves_like :io_readlines, :foreach, IOSpecs.collector
+ it_behaves_like :io_readlines_options_19, :foreach, IOSpecs.collector
+end
diff --git a/spec/ruby/core/io/fsync_spec.rb b/spec/ruby/core/io/fsync_spec.rb
new file mode 100644
index 0000000000..0261939631
--- /dev/null
+++ b/spec/ruby/core/io/fsync_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#fsync" do
+ before :each do
+ @name = tmp("io_fsync.txt")
+ ScratchPad.clear
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "raises an IOError on closed stream" do
+ lambda { IOSpecs.closed_io.fsync }.should raise_error(IOError)
+ end
+
+ it "writes the buffered data to permanent storage" do
+ File.open(@name, "w") do |f|
+ f.write "one hit wonder"
+ f.fsync.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/core/io/getbyte_spec.rb b/spec/ruby/core/io/getbyte_spec.rb
new file mode 100644
index 0000000000..6b665029d6
--- /dev/null
+++ b/spec/ruby/core/io/getbyte_spec.rb
@@ -0,0 +1,42 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#getbyte" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ it "returns the next byte from the stream" do
+ @io.readline.should == "Voici la ligne une.\n"
+ letters = @io.getbyte, @io.getbyte, @io.getbyte, @io.getbyte, @io.getbyte
+ letters.should == [81, 117, 105, 32, 195]
+ end
+
+ it "returns nil when invoked at the end of the stream" do
+ @io.read
+ @io.getbyte.should == nil
+ end
+
+ it "raises an IOError on closed stream" do
+ lambda { IOSpecs.closed_io.getbyte }.should raise_error(IOError)
+ end
+end
+
+describe "IO#getbyte" do
+ before :each do
+ @io = IOSpecs.io_fixture "empty.txt"
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ it "returns nil on empty stream" do
+ @io.getbyte.should == nil
+ end
+end
diff --git a/spec/ruby/core/io/getc_spec.rb b/spec/ruby/core/io/getc_spec.rb
new file mode 100644
index 0000000000..7c1c18cd90
--- /dev/null
+++ b/spec/ruby/core/io/getc_spec.rb
@@ -0,0 +1,42 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#getc" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ it "returns the next character from the stream" do
+ @io.readline.should == "Voici la ligne une.\n"
+ letters = @io.getc, @io.getc, @io.getc, @io.getc, @io.getc
+ letters.should == ["Q", "u", "i", " ", "è"]
+ end
+
+ it "returns nil when invoked at the end of the stream" do
+ @io.read
+ @io.getc.should be_nil
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.getc }.should raise_error(IOError)
+ end
+end
+
+describe "IO#getc" do
+ before :each do
+ @io = IOSpecs.io_fixture "empty.txt"
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ it "returns nil on empty stream" do
+ @io.getc.should be_nil
+ end
+end
diff --git a/spec/ruby/core/io/gets_spec.rb b/spec/ruby/core/io/gets_spec.rb
new file mode 100644
index 0000000000..b22b226beb
--- /dev/null
+++ b/spec/ruby/core/io/gets_spec.rb
@@ -0,0 +1,321 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/gets_ascii'
+
+describe "IO#gets" do
+ it_behaves_like :io_gets_ascii, :gets
+end
+
+describe "IO#gets" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ @count = 0
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ it "assigns the returned line to $_" do
+ IOSpecs.lines.each do |line|
+ @io.gets
+ $_.should == line
+ end
+ end
+
+ it "returns nil if called at the end of the stream" do
+ IOSpecs.lines.length.times { @io.gets }
+ @io.gets.should == nil
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.gets }.should raise_error(IOError)
+ end
+
+ describe "with no separator" do
+ it "returns the next line of string that is separated by $/" do
+ IOSpecs.lines.each { |line| line.should == @io.gets }
+ end
+
+ it "returns tainted strings" do
+ while line = @io.gets
+ line.tainted?.should == true
+ end
+ end
+
+ it "updates lineno with each invocation" do
+ while @io.gets
+ @io.lineno.should == @count += 1
+ end
+ end
+
+ it "updates $. with each invocation" do
+ while @io.gets
+ $..should == @count += 1
+ end
+ end
+ end
+
+ describe "with nil separator" do
+ it "returns the entire contents" do
+ @io.gets(nil).should == IOSpecs.lines.join("")
+ end
+
+ it "returns tainted strings" do
+ while line = @io.gets(nil)
+ line.tainted?.should == true
+ end
+ end
+
+ it "updates lineno with each invocation" do
+ while @io.gets(nil)
+ @io.lineno.should == @count += 1
+ end
+ end
+
+ it "updates $. with each invocation" do
+ while @io.gets(nil)
+ $..should == @count += 1
+ end
+ end
+ end
+
+ describe "with an empty String separator" do
+ # Two successive newlines in the input separate paragraphs.
+ # When there are more than two successive newlines, only two are kept.
+ it "returns the next paragraph" do
+ @io.gets("").should == IOSpecs.lines[0,3].join("")
+ @io.gets("").should == IOSpecs.lines[4,3].join("")
+ @io.gets("").should == IOSpecs.lines[7,2].join("")
+ end
+
+ it "reads until the beginning of the next paragraph" do
+ # There are three newlines between the first and second paragraph
+ @io.gets("")
+ @io.gets.should == IOSpecs.lines[4]
+ end
+
+ it "returns tainted strings" do
+ while line = @io.gets("")
+ line.tainted?.should == true
+ end
+ end
+
+ it "updates lineno with each invocation" do
+ while @io.gets("")
+ @io.lineno.should == @count += 1
+ end
+ end
+
+ it "updates $. with each invocation" do
+ while @io.gets("")
+ $..should == @count += 1
+ end
+ end
+ end
+
+ describe "with an arbitrary String separator" do
+ it "reads up to and including the separator" do
+ @io.gets("la linea").should == "Voici la ligne une.\nQui \303\250 la linea"
+ end
+
+ it "returns tainted strings" do
+ while line = @io.gets("la")
+ line.tainted?.should == true
+ end
+ end
+
+ it "updates lineno with each invocation" do
+ while (@io.gets("la"))
+ @io.lineno.should == @count += 1
+ end
+ end
+
+ it "updates $. with each invocation" do
+ while @io.gets("la")
+ $..should == @count += 1
+ end
+ end
+ end
+
+ ruby_version_is "2.4" do
+ describe "when passed chomp" do
+ it "returns the first line without a trailing newline character" do
+ @io.gets(chomp: true).should == IOSpecs.lines_without_newline_characters[0]
+ end
+ end
+ end
+end
+
+describe "IO#gets" do
+ before :each do
+ @name = tmp("io_gets")
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "raises an IOError if the stream is opened for append only" do
+ lambda { File.open(@name, fmode("a:utf-8")) { |f| f.gets } }.should raise_error(IOError)
+ end
+
+ it "raises an IOError if the stream is opened for writing only" do
+ lambda { File.open(@name, fmode("w:utf-8")) { |f| f.gets } }.should raise_error(IOError)
+ end
+end
+
+describe "IO#gets" do
+ before :each do
+ @name = tmp("io_gets")
+ touch(@name) { |f| f.write "one\n\ntwo\n\nthree\nfour\n" }
+ @io = new_io @name, fmode("r:utf-8")
+ end
+
+ after :each do
+ @io.close if @io
+ rm_r @name
+ end
+
+ it "calls #to_int to convert a single object argument to an Integer limit" do
+ obj = mock("io gets limit")
+ obj.should_receive(:to_int).and_return(6)
+
+ @io.gets(obj).should == "one\n"
+ end
+
+ it "calls #to_int to convert the second object argument to an Integer limit" do
+ obj = mock("io gets limit")
+ obj.should_receive(:to_int).and_return(2)
+
+ @io.gets(nil, obj).should == "on"
+ end
+
+ it "calls #to_str to convert the first argument to a String when passed a limit" do
+ obj = mock("io gets separator")
+ obj.should_receive(:to_str).and_return($/)
+
+ @io.gets(obj, 5).should == "one\n"
+ end
+
+ it "reads to the default separator when passed a single argument greater than the number of bytes to the separator" do
+ @io.gets(6).should == "one\n"
+ end
+
+ it "reads limit bytes when passed a single argument less than the number of bytes to the default separator" do
+ @io.gets(3).should == "one"
+ end
+
+ it "reads limit bytes when passed nil and a limit" do
+ @io.gets(nil, 6).should == "one\n\nt"
+ end
+
+ it "reads all bytes when the limit is higher than the available bytes" do
+ @io.gets(nil, 100).should == "one\n\ntwo\n\nthree\nfour\n"
+ end
+
+ it "reads until the next paragraph when passed '' and a limit greater than the next paragraph" do
+ @io.gets("", 6).should == "one\n\n"
+ end
+
+ it "reads limit bytes when passed '' and a limit less than the next paragraph" do
+ @io.gets("", 3).should == "one"
+ end
+
+ it "reads all bytes when pass a separator and reading more than all bytes" do
+ @io.gets("\t", 100).should == "one\n\ntwo\n\nthree\nfour\n"
+ end
+end
+
+describe "IO#gets" do
+ before :each do
+ @name = tmp("io_gets")
+ # create data "æœæ—¥" + "\xE3\x81" * 100 to avoid utf-8 conflicts
+ data = "æœæ—¥" + ([227,129].pack('C*') * 100).force_encoding('utf-8')
+ touch(@name) { |f| f.write data }
+ @io = new_io @name, fmode("r:utf-8")
+ end
+
+ after :each do
+ @io.close if @io
+ rm_r @name
+ end
+
+ it "reads limit bytes and extra bytes when limit is reached not at character boundary" do
+ [@io.gets(1), @io.gets(1)].should == ["æœ", "æ—¥"]
+ end
+
+ it "read limit bytes and extra bytes with maximum of 16" do
+ # create str "æœæ—¥\xE3" + "\x81\xE3" * 8 to avoid utf-8 conflicts
+ str = "æœæ—¥" + ([227] + [129,227] * 8).pack('C*').force_encoding('utf-8')
+ @io.gets(7).should == str
+ end
+end
+
+describe "IO#gets" do
+ before :each do
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+
+ Encoding.default_external = Encoding::UTF_8
+ Encoding.default_internal = nil
+
+ @name = tmp("io_gets")
+ touch(@name) { |f| f.write "line" }
+ end
+
+ after :each do
+ @io.close if @io
+ rm_r @name
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+ end
+
+ it "uses the default external encoding" do
+ @io = new_io @name, 'r'
+ @io.gets.encoding.should == Encoding::UTF_8
+ end
+
+ it "uses the IO object's external encoding, when set" do
+ @io = new_io @name, 'r'
+ @io.set_encoding Encoding::US_ASCII
+ @io.gets.encoding.should == Encoding::US_ASCII
+ end
+
+ it "transcodes into the default internal encoding" do
+ Encoding.default_internal = Encoding::US_ASCII
+ @io = new_io @name, 'r'
+ @io.gets.encoding.should == Encoding::US_ASCII
+ end
+
+ it "transcodes into the IO object's internal encoding, when set" do
+ Encoding.default_internal = Encoding::US_ASCII
+ @io = new_io @name, 'r'
+ @io.set_encoding Encoding::UTF_8, Encoding::UTF_16
+ @io.gets.encoding.should == Encoding::UTF_16
+ end
+
+ it "overwrites the default external encoding with the IO object's own external encoding" do
+ Encoding.default_external = Encoding::ASCII_8BIT
+ Encoding.default_internal = Encoding::UTF_8
+ @io = new_io @name, 'r'
+ @io.set_encoding Encoding::IBM866
+ @io.gets.encoding.should == Encoding::UTF_8
+ end
+
+ it "ignores the internal encoding if the default external encoding is ASCII-8BIT" do
+ Encoding.default_external = Encoding::ASCII_8BIT
+ Encoding.default_internal = Encoding::UTF_8
+ @io = new_io @name, 'r'
+ @io.gets.encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "transcodes to internal encoding if the IO object's external encoding is ASCII-8BIT" do
+ Encoding.default_external = Encoding::ASCII_8BIT
+ Encoding.default_internal = Encoding::UTF_8
+ @io = new_io @name, 'r'
+ @io.set_encoding Encoding::ASCII_8BIT, Encoding::UTF_8
+ @io.gets.encoding.should == Encoding::UTF_8
+ end
+end
diff --git a/spec/ruby/core/io/initialize_spec.rb b/spec/ruby/core/io/initialize_spec.rb
new file mode 100644
index 0000000000..5bf194f15c
--- /dev/null
+++ b/spec/ruby/core/io/initialize_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#initialize" do
+ before :each do
+ @name = tmp("io_initialize.txt")
+ @io = new_io @name
+ @fd = @io.fileno
+ end
+
+ after :each do
+ @io.close if @io
+ rm_r @name
+ end
+
+ # File descriptor numbers are not predictable in multi-threaded code;
+ # MJIT will be opening/closing files the background
+ without_feature :mjit do
+ it "reassociates the IO instance with the new descriptor when passed a Fixnum" do
+ fd = new_fd @name, "r:utf-8"
+ @io.send :initialize, fd, 'r'
+ @io.fileno.should == fd
+ # initialize has closed the old descriptor
+ lambda { IO.for_fd(@fd).close }.should raise_error(Errno::EBADF)
+ end
+
+ it "calls #to_int to coerce the object passed as an fd" do
+ obj = mock('fileno')
+ fd = new_fd @name, "r:utf-8"
+ obj.should_receive(:to_int).and_return(fd)
+ @io.send :initialize, obj, 'r'
+ @io.fileno.should == fd
+ # initialize has closed the old descriptor
+ lambda { IO.for_fd(@fd).close }.should raise_error(Errno::EBADF)
+ end
+ end
+
+ it "raises a TypeError when passed an IO" do
+ lambda { @io.send :initialize, STDOUT, 'w' }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { @io.send :initialize, nil, 'w' }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a String" do
+ lambda { @io.send :initialize, "4", 'w' }.should raise_error(TypeError)
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { @io.send :initialize, IOSpecs.closed_io.fileno }.should raise_error(IOError)
+ end
+
+ it "raises an Errno::EBADF when given an invalid file descriptor" do
+ lambda { @io.send :initialize, -1, 'w' }.should raise_error(Errno::EBADF)
+ end
+end
diff --git a/spec/ruby/core/io/inspect_spec.rb b/spec/ruby/core/io/inspect_spec.rb
new file mode 100644
index 0000000000..c653c307c4
--- /dev/null
+++ b/spec/ruby/core/io/inspect_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+
+describe "IO#inspect" do
+ after :each do
+ @r.close if @r && !@r.closed?
+ @w.close if @w && !@w.closed?
+ end
+
+ it "contains the file descriptor number" do
+ @r, @w = IO.pipe
+ @r.inspect.should include("fd #{@r.fileno}")
+ end
+
+ it "contains \"(closed)\" if the stream is closed" do
+ @r, @w = IO.pipe
+ @r.close
+ @r.inspect.should include("(closed)")
+ end
+
+ it "reports IO as its Method object's owner" do
+ IO.instance_method(:inspect).owner.should == IO
+ end
+end
diff --git a/spec/ruby/core/io/internal_encoding_spec.rb b/spec/ruby/core/io/internal_encoding_spec.rb
new file mode 100644
index 0000000000..a4f13fa100
--- /dev/null
+++ b/spec/ruby/core/io/internal_encoding_spec.rb
@@ -0,0 +1,140 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe :io_internal_encoding, shared: true do
+ describe "when Encoding.default_internal is not set" do
+ before :each do
+ Encoding.default_internal = nil
+ end
+
+ it "returns nil if the internal encoding is not set" do
+ @io = new_io @name, @object
+ @io.internal_encoding.should be_nil
+ end
+
+ it "returns nil if Encoding.default_internal is changed after the instance is created" do
+ @io = new_io @name, @object
+ Encoding.default_internal = Encoding::IBM437
+ @io.internal_encoding.should be_nil
+ end
+
+ it "returns the value set when the instance was created" do
+ @io = new_io @name, "#{@object}:utf-8:euc-jp"
+ Encoding.default_internal = Encoding::IBM437
+ @io.internal_encoding.should equal(Encoding::EUC_JP)
+ end
+
+ it "returns the value set by #set_encoding" do
+ @io = new_io @name, @object
+ @io.set_encoding(Encoding::US_ASCII, Encoding::IBM437)
+ @io.internal_encoding.should equal(Encoding::IBM437)
+ end
+ end
+
+ describe "when Encoding.default_internal == Encoding.default_external" do
+ before :each do
+ Encoding.default_external = Encoding::IBM866
+ Encoding.default_internal = Encoding::IBM866
+ end
+
+ it "returns nil" do
+ @io = new_io @name, @object
+ @io.internal_encoding.should be_nil
+ end
+
+ it "returns nil regardless of Encoding.default_internal changes" do
+ @io = new_io @name, @object
+ Encoding.default_internal = Encoding::IBM437
+ @io.internal_encoding.should be_nil
+ end
+ end
+
+ describe "when Encoding.default_internal != Encoding.default_external" do
+ before :each do
+ Encoding.default_external = Encoding::IBM437
+ Encoding.default_internal = Encoding::IBM866
+ end
+
+ it "returns the value of Encoding.default_internal when the instance was created if the internal encoding is not set" do
+ @io = new_io @name, @object
+ @io.internal_encoding.should equal(Encoding::IBM866)
+ end
+
+ it "does not change when Encoding.default_internal is changed" do
+ @io = new_io @name, @object
+ Encoding.default_internal = Encoding::IBM437
+ @io.internal_encoding.should equal(Encoding::IBM866)
+ end
+
+ it "returns the internal encoding set when the instance was created" do
+ @io = new_io @name, "#{@object}:utf-8:euc-jp"
+ @io.internal_encoding.should equal(Encoding::EUC_JP)
+ end
+
+ it "does not change when set and Encoding.default_internal is changed" do
+ @io = new_io @name, "#{@object}:utf-8:euc-jp"
+ Encoding.default_internal = Encoding::IBM437
+ @io.internal_encoding.should equal(Encoding::EUC_JP)
+ end
+
+ it "returns the value set by #set_encoding" do
+ @io = new_io @name, @object
+ @io.set_encoding(Encoding::US_ASCII, Encoding::IBM437)
+ @io.internal_encoding.should equal(Encoding::IBM437)
+ end
+
+ it "returns nil when Encoding.default_external is ASCII-8BIT and the internal encoding is not set" do
+ Encoding.default_external = Encoding::ASCII_8BIT
+ @io = new_io @name, @object
+ @io.internal_encoding.should be_nil
+ end
+
+ it "returns nil when the external encoding is ASCII-8BIT and the internal encoding is not set" do
+ @io = new_io @name, "#{@object}:ascii-8bit"
+ @io.internal_encoding.should be_nil
+ end
+ end
+ end
+
+ describe "IO#internal_encoding" do
+ before :each do
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+
+ @name = tmp("io_internal_encoding")
+ touch(@name)
+ end
+
+ after :each do
+ @io.close if @io
+ rm_r @name
+
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+ end
+
+ describe "with 'r' mode" do
+ it_behaves_like :io_internal_encoding, nil, "r"
+ end
+
+ describe "with 'r+' mode" do
+ it_behaves_like :io_internal_encoding, nil, "r+"
+ end
+
+ describe "with 'w' mode" do
+ it_behaves_like :io_internal_encoding, nil, "w"
+ end
+
+ describe "with 'w+' mode" do
+ it_behaves_like :io_internal_encoding, nil, "w+"
+ end
+
+ describe "with 'a' mode" do
+ it_behaves_like :io_internal_encoding, nil, "a"
+ end
+
+ describe "with 'a+' mode" do
+ it_behaves_like :io_internal_encoding, nil, "a+"
+ end
+ end
+end
diff --git a/spec/ruby/core/io/io_spec.rb b/spec/ruby/core/io/io_spec.rb
new file mode 100644
index 0000000000..0feb1a8774
--- /dev/null
+++ b/spec/ruby/core/io/io_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "IO" do
+ it "includes File::Constants" do
+ IO.include?(File::Constants).should == true
+ end
+
+ it "includes Enumerable" do
+ IO.include?(Enumerable).should == true
+ end
+end
diff --git a/spec/ruby/core/io/ioctl_spec.rb b/spec/ruby/core/io/ioctl_spec.rb
new file mode 100644
index 0000000000..726f0c9c51
--- /dev/null
+++ b/spec/ruby/core/io/ioctl_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#ioctl" do
+ platform_is_not :windows do
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.ioctl(5, 5) }.should raise_error(IOError)
+ end
+ end
+
+ platform_is :linux do
+ platform_is "86" do # x86 / x86_64
+ it "resizes an empty String to match the output size" do
+ File.open(__FILE__, 'r') do |f|
+ buffer = ''
+ # FIONREAD in /usr/include/asm-generic/ioctls.h
+ f.ioctl 0x541B, buffer
+ buffer.unpack('I').first.should be_kind_of(Integer)
+ end
+ end
+ end
+
+ it "raises an Errno error when ioctl fails" do
+ File.open(__FILE__, 'r') do |f|
+ lambda {
+ # TIOCGWINSZ in /usr/include/asm-generic/ioctls.h
+ f.ioctl 0x5413, nil
+ }.should raise_error(Errno::ENOTTY)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/io/isatty_spec.rb b/spec/ruby/core/io/isatty_spec.rb
new file mode 100644
index 0000000000..3b6c69b190
--- /dev/null
+++ b/spec/ruby/core/io/isatty_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/tty'
+
+describe "IO#isatty" do
+ it_behaves_like :io_tty, :isatty
+end
diff --git a/spec/ruby/core/io/lineno_spec.rb b/spec/ruby/core/io/lineno_spec.rb
new file mode 100644
index 0000000000..322e60d643
--- /dev/null
+++ b/spec/ruby/core/io/lineno_spec.rb
@@ -0,0 +1,95 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#lineno" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ it "raises an IOError on a closed stream" do
+ lambda { IOSpecs.closed_io.lineno }.should raise_error(IOError)
+ end
+
+ it "returns the current line number" do
+ @io.lineno.should == 0
+
+ count = 0
+ while @io.gets
+ @io.lineno.should == count += 1
+ end
+
+ @io.rewind
+ @io.lineno.should == 0
+ end
+end
+
+describe "IO#lineno=" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ it "raises an IOError on a closed stream" do
+ lambda { IOSpecs.closed_io.lineno = 5 }.should raise_error(IOError)
+ end
+
+ it "calls #to_int on a non-numeric argument" do
+ obj = mock('123')
+ obj.should_receive(:to_int).and_return(123)
+
+ @io.lineno = obj
+ @io.lineno.should == 123
+ end
+
+ it "truncates a Float argument" do
+ @io.lineno = 1.5
+ @io.lineno.should == 1
+
+ @io.lineno = 92233.72036854775808
+ @io.lineno.should == 92233
+ end
+
+ it "raises TypeError on nil argument" do
+ lambda { @io.lineno = nil }.should raise_error(TypeError)
+ end
+
+ it "sets the current line number to the given value" do
+ @io.lineno = count = 500
+
+ while @io.gets
+ @io.lineno.should == count += 1
+ end
+
+ @io.rewind
+ @io.lineno.should == 0
+ end
+
+ it "does not change $." do
+ original_line = $.
+ numbers = [-2**30, -2**16, -2**8, -100, -10, -1, 0, 1, 10, 2**8, 2**16, 2**30]
+ numbers.each do |num|
+ @io.lineno = num
+ @io.lineno.should == num
+ $..should == original_line
+ end
+ end
+
+ it "does not change $. until next read" do
+ $. = 0
+ $..should == 0
+
+ @io.lineno = count = 500
+ $..should == 0
+
+ while @io.gets
+ $..should == count += 1
+ end
+ end
+end
diff --git a/spec/ruby/core/io/lines_spec.rb b/spec/ruby/core/io/lines_spec.rb
new file mode 100644
index 0000000000..a8b8023a2a
--- /dev/null
+++ b/spec/ruby/core/io/lines_spec.rb
@@ -0,0 +1,42 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#lines" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ it "returns an Enumerator" do
+ @io.lines.should be_an_instance_of(Enumerator)
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ @io.lines.should be_an_instance_of(Enumerator)
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ @io.lines.size.should == nil
+ end
+ end
+ end
+ end
+
+ it "returns a line when accessed" do
+ enum = @io.lines
+ enum.first.should == IOSpecs.lines[0]
+ end
+
+ it "yields each line to the passed block" do
+ ScratchPad.record []
+ @io.lines { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.lines
+ end
+end
diff --git a/spec/ruby/core/io/new_spec.rb b/spec/ruby/core/io/new_spec.rb
new file mode 100644
index 0000000000..3597098caf
--- /dev/null
+++ b/spec/ruby/core/io/new_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'shared/new'
+
+describe "IO.new" do
+ it_behaves_like :io_new, :new
+end
+
+describe "IO.new" do
+ it_behaves_like :io_new_errors, :new
+end
diff --git a/spec/ruby/core/io/open_spec.rb b/spec/ruby/core/io/open_spec.rb
new file mode 100644
index 0000000000..f26753cde7
--- /dev/null
+++ b/spec/ruby/core/io/open_spec.rb
@@ -0,0 +1,86 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/new'
+
+describe "IO.open" do
+ it_behaves_like :io_new, :open
+end
+
+describe "IO.open" do
+ it_behaves_like :io_new_errors, :open
+end
+
+# These specs use a special mock helper to avoid mock
+# methods from preventing IO#close from running and
+# which would prevent the file referenced by @fd from
+# being deleted on Windows.
+
+describe "IO.open" do
+ before :each do
+ @name = tmp("io_open.txt")
+ @fd = new_fd @name
+ ScratchPad.clear
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "calls #close after yielding to the block" do
+ IO.open(@fd, "w") do |io|
+ IOSpecs.io_mock(io, :close) do
+ super()
+ ScratchPad.record :called
+ end
+ io.closed?.should be_false
+ end
+ ScratchPad.recorded.should == :called
+ end
+
+ it "propagates an exception raised by #close that is not a StandardError" do
+ lambda do
+ IO.open(@fd, "w") do |io|
+ IOSpecs.io_mock(io, :close) do
+ super()
+ ScratchPad.record :called
+ raise Exception
+ end
+ end
+ end.should raise_error(Exception)
+ ScratchPad.recorded.should == :called
+ end
+
+ it "propagates an exception raised by #close that is a StandardError" do
+ lambda do
+ IO.open(@fd, "w") do |io|
+ IOSpecs.io_mock(io, :close) do
+ super()
+ ScratchPad.record :called
+ raise StandardError
+ end
+ end
+ end.should raise_error(StandardError)
+ ScratchPad.recorded.should == :called
+ end
+
+ it "does not propagate a IOError with 'closed stream' message raised by #close" do
+ IO.open(@fd, "w") do |io|
+ IOSpecs.io_mock(io, :close) do
+ super()
+ ScratchPad.record :called
+ raise IOError, 'closed stream'
+ end
+ end
+ ScratchPad.recorded.should == :called
+ end
+
+ it "does not set last error when a IOError with 'closed stream' raised by #close" do
+ IO.open(@fd, "w") do |io|
+ IOSpecs.io_mock(io, :close) do
+ super()
+ raise IOError, 'closed stream'
+ end
+ end
+ $!.should == nil
+ end
+end
diff --git a/spec/ruby/core/io/output_spec.rb b/spec/ruby/core/io/output_spec.rb
new file mode 100644
index 0000000000..d3ec71c563
--- /dev/null
+++ b/spec/ruby/core/io/output_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#<<" do
+ it "writes an object to the IO stream" do
+ lambda {
+ $stderr << "Oh noes, an error!"
+ }.should output_to_fd("Oh noes, an error!", $stderr)
+ end
+
+ it "calls #to_s on the object to print it" do
+ lambda {
+ $stderr << 1337
+ }.should output_to_fd("1337", $stderr)
+ end
+
+ it "raises an error if the stream is closed" do
+ io = IOSpecs.closed_io
+ lambda { io << "test" }.should raise_error(IOError)
+ end
+
+ it "returns self" do
+ lambda {
+ ($stderr << "to_stderr").should == $stderr
+ }.should output(nil, "to_stderr")
+ end
+end
diff --git a/spec/ruby/core/io/pid_spec.rb b/spec/ruby/core/io/pid_spec.rb
new file mode 100644
index 0000000000..97b8a8529d
--- /dev/null
+++ b/spec/ruby/core/io/pid_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#pid" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ it "returns nil for IO not associated with a process" do
+ @io.pid.should == nil
+ end
+end
+
+describe "IO#pid" do
+ before :each do
+ @io = IO.popen ruby_cmd('STDIN.read'), "r+"
+ end
+
+ after :each do
+ @io.close if @io && !@io.closed?
+ end
+
+ it "returns the ID of a process associated with stream" do
+ @io.pid.should_not be_nil
+ end
+
+ it "raises an IOError on closed stream" do
+ @io.close
+ lambda { @io.pid }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/pipe_spec.rb b/spec/ruby/core/io/pipe_spec.rb
new file mode 100644
index 0000000000..005f60fab6
--- /dev/null
+++ b/spec/ruby/core/io/pipe_spec.rb
@@ -0,0 +1,214 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO.pipe" do
+ after :each do
+ @r.close if @r && !@r.closed?
+ @w.close if @w && !@w.closed?
+ end
+
+ it "creates a two-ended pipe" do
+ @r, @w = IO.pipe
+ @w.puts "test_create_pipe\\n"
+ @w.close
+ @r.read(16).should == "test_create_pipe"
+ end
+
+ it "returns two IO objects" do
+ @r, @w = IO.pipe
+ @r.should be_kind_of(IO)
+ @w.should be_kind_of(IO)
+ end
+
+ it "returns instances of a subclass when called on a subclass" do
+ @r, @w = IOSpecs::SubIO.pipe
+ @r.should be_an_instance_of(IOSpecs::SubIO)
+ @w.should be_an_instance_of(IOSpecs::SubIO)
+ end
+end
+
+describe "IO.pipe" do
+ describe "passed a block" do
+ it "yields two IO objects" do
+ IO.pipe do |r, w|
+ r.should be_kind_of(IO)
+ w.should be_kind_of(IO)
+ end
+ end
+
+ it "returns the result of the block" do
+ IO.pipe { |r, w| :result }.should == :result
+ end
+
+ it "closes both IO objects" do
+ r, w = IO.pipe do |_r, _w|
+ [_r, _w]
+ end
+ r.closed?.should == true
+ w.closed?.should == true
+ end
+
+ it "closes both IO objects when the block raises" do
+ r = w = nil
+ lambda do
+ IO.pipe do |_r, _w|
+ r = _r
+ w = _w
+ raise RuntimeError
+ end
+ end.should raise_error(RuntimeError)
+ r.closed?.should == true
+ w.closed?.should == true
+ end
+
+ it "allows IO objects to be closed within the block" do
+ r, w = IO.pipe do |_r, _w|
+ _r.close
+ _w.close
+ [_r, _w]
+ end
+ r.closed?.should == true
+ w.closed?.should == true
+ end
+ end
+end
+
+describe "IO.pipe" do
+ before :each do
+ @default_external = Encoding.default_external
+ @default_internal = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_external = @default_external
+ Encoding.default_internal = @default_internal
+ end
+
+ it "sets the external encoding of the read end to the default when passed no arguments" do
+ Encoding.default_external = Encoding::ISO_8859_1
+
+ IO.pipe do |r, w|
+ r.external_encoding.should == Encoding::ISO_8859_1
+ r.internal_encoding.should be_nil
+ end
+ end
+
+ it "sets the internal encoding of the read end to the default when passed no arguments" do
+ Encoding.default_external = Encoding::ISO_8859_1
+ Encoding.default_internal = Encoding::UTF_8
+
+ IO.pipe do |r, w|
+ r.external_encoding.should == Encoding::ISO_8859_1
+ r.internal_encoding.should == Encoding::UTF_8
+ end
+ end
+
+ it "sets the internal encoding to nil if the same as the external" do
+ Encoding.default_external = Encoding::UTF_8
+ Encoding.default_internal = Encoding::UTF_8
+
+ IO.pipe do |r, w|
+ r.external_encoding.should == Encoding::UTF_8
+ r.internal_encoding.should be_nil
+ end
+ end
+
+ it "sets the external encoding of the read end when passed an Encoding argument" do
+ IO.pipe(Encoding::UTF_8) do |r, w|
+ r.external_encoding.should == Encoding::UTF_8
+ r.internal_encoding.should be_nil
+ end
+ end
+
+ it "sets the external and internal encodings of the read end when passed two Encoding arguments" do
+ IO.pipe(Encoding::UTF_8, Encoding::UTF_16BE) do |r, w|
+ r.external_encoding.should == Encoding::UTF_8
+ r.internal_encoding.should == Encoding::UTF_16BE
+ end
+ end
+
+ it "sets the external encoding of the read end when passed the name of an Encoding" do
+ IO.pipe("UTF-8") do |r, w|
+ r.external_encoding.should == Encoding::UTF_8
+ r.internal_encoding.should be_nil
+ end
+ end
+
+ it "accepts 'bom|' prefix for external encoding" do
+ IO.pipe("BOM|UTF-8") do |r, w|
+ r.external_encoding.should == Encoding::UTF_8
+ r.internal_encoding.should be_nil
+ end
+ end
+
+ it "sets the external and internal encodings specified as a String and separated with a colon" do
+ IO.pipe("UTF-8:ISO-8859-1") do |r, w|
+ r.external_encoding.should == Encoding::UTF_8
+ r.internal_encoding.should == Encoding::ISO_8859_1
+ end
+ end
+
+ it "accepts 'bom|' prefix for external encoding when specifying 'external:internal'" do
+ IO.pipe("BOM|UTF-8:ISO-8859-1") do |r, w|
+ r.external_encoding.should == Encoding::UTF_8
+ r.internal_encoding.should == Encoding::ISO_8859_1
+ end
+ end
+
+ it "sets the external and internal encoding when passed two String arguments" do
+ IO.pipe("UTF-8", "UTF-16BE") do |r, w|
+ r.external_encoding.should == Encoding::UTF_8
+ r.internal_encoding.should == Encoding::UTF_16BE
+ end
+ end
+
+ it "accepts an options Hash with one String encoding argument" do
+ IO.pipe("BOM|UTF-8:ISO-8859-1", invalid: :replace) do |r, w|
+ r.external_encoding.should == Encoding::UTF_8
+ r.internal_encoding.should == Encoding::ISO_8859_1
+ end
+ end
+
+ it "accepts an options Hash with two String encoding arguments" do
+ IO.pipe("UTF-8", "ISO-8859-1", invalid: :replace) do |r, w|
+ r.external_encoding.should == Encoding::UTF_8
+ r.internal_encoding.should == Encoding::ISO_8859_1
+ end
+ end
+
+ it "calls #to_hash to convert an options argument" do
+ options = mock("io pipe encoding options")
+ options.should_receive(:to_hash).and_return({ invalid: :replace })
+ IO.pipe("UTF-8", "ISO-8859-1", options) { |r, w| }
+ end
+
+ it "calls #to_str to convert the first argument to a String" do
+ obj = mock("io_pipe_encoding")
+ obj.should_receive(:to_str).and_return("UTF-8:UTF-16BE")
+ IO.pipe(obj) do |r, w|
+ r.external_encoding.should == Encoding::UTF_8
+ r.internal_encoding.should == Encoding::UTF_16BE
+ end
+ end
+
+ it "calls #to_str to convert the second argument to a String" do
+ obj = mock("io_pipe_encoding")
+ obj.should_receive(:to_str).at_least(1).times.and_return("UTF-16BE")
+ IO.pipe(Encoding::UTF_8, obj) do |r, w|
+ r.external_encoding.should == Encoding::UTF_8
+ r.internal_encoding.should == Encoding::UTF_16BE
+ end
+ end
+
+ it "sets no external encoding for the write end" do
+ IO.pipe(Encoding::UTF_8) do |r, w|
+ w.external_encoding.should be_nil
+ end
+ end
+
+ it "sets no internal encoding for the write end" do
+ IO.pipe(Encoding::UTF_8) do |r, w|
+ w.external_encoding.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/io/popen_spec.rb b/spec/ruby/core/io/popen_spec.rb
new file mode 100644
index 0000000000..d15ac48fe4
--- /dev/null
+++ b/spec/ruby/core/io/popen_spec.rb
@@ -0,0 +1,286 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO.popen" do
+ before :each do
+ @io = nil
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ it "returns an open IO" do
+ @io = IO.popen(ruby_cmd('exit'), "r")
+ @io.closed?.should be_false
+ end
+
+ it "reads a read-only pipe" do
+ @io = IO.popen(ruby_cmd('puts "foo"'), "r")
+ @io.read.should == "foo\n"
+ end
+
+ it "raises IOError when writing a read-only pipe" do
+ @io = IO.popen(ruby_cmd('puts "foo"'), "r")
+ lambda { @io.write('bar') }.should raise_error(IOError)
+ @io.read.should == "foo\n"
+ end
+end
+
+describe "IO.popen" do
+ before :each do
+ @fname = tmp("IO_popen_spec")
+ @io = nil
+ end
+
+ after :each do
+ @io.close if @io and !@io.closed?
+ rm_r @fname
+ end
+
+ it "sees an infinitely looping subprocess exit when read pipe is closed" do
+ io = IO.popen ruby_cmd('r = loop{puts "y"; 0} rescue 1; exit r'), 'r'
+ io.close
+
+ $?.exitstatus.should_not == 0
+ end
+
+ it "writes to a write-only pipe" do
+ @io = IO.popen(ruby_cmd('IO.copy_stream(STDIN,STDOUT)', args: "> #{@fname}"), "w")
+ @io.write("bar")
+ @io.close
+
+ File.read(@fname).should == "bar"
+ end
+
+ it "raises IOError when reading a write-only pipe" do
+ @io = IO.popen(ruby_cmd('IO.copy_stream(STDIN,STDOUT)'), "w")
+ lambda { @io.read }.should raise_error(IOError)
+ end
+
+ it "reads and writes a read/write pipe" do
+ @io = IO.popen(ruby_cmd('IO.copy_stream(STDIN,STDOUT)'), "r+")
+ @io.write("bar")
+ @io.read(3).should == "bar"
+ end
+
+ it "waits for the child to finish" do
+ @io = IO.popen(ruby_cmd('IO.copy_stream(STDIN,STDOUT)', args: "> #{@fname}"), "w")
+ @io.write("bar")
+ @io.close
+
+ $?.exitstatus.should == 0
+
+ File.read(@fname).should == "bar"
+ end
+
+ it "does not throw an exception if child exited and has been waited for" do
+ @io = IO.popen([*ruby_exe, '-e', 'sleep'])
+ pid = @io.pid
+ Process.kill "KILL", pid
+ @io.close
+ platform_is_not :windows do
+ $?.signaled?.should == true
+ end
+ platform_is :windows do
+ $?.exited?.should == true
+ end
+ end
+
+ it "returns an instance of a subclass when called on a subclass" do
+ @io = IOSpecs::SubIO.popen(ruby_cmd('exit'), "r")
+ @io.should be_an_instance_of(IOSpecs::SubIO)
+ end
+
+ it "coerces mode argument with #to_str" do
+ mode = mock("mode")
+ mode.should_receive(:to_str).and_return("r")
+ @io = IO.popen(ruby_cmd('exit 0'), mode)
+ end
+end
+
+describe "IO.popen" do
+ before :each do
+ @io = nil
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ describe "with a block" do
+ it "yields an open IO to the block" do
+ IO.popen(ruby_cmd('exit'), "r") do |io|
+ io.closed?.should be_false
+ end
+ end
+
+ it "yields an instance of a subclass when called on a subclass" do
+ IOSpecs::SubIO.popen(ruby_cmd('exit'), "r") do |io|
+ io.should be_an_instance_of(IOSpecs::SubIO)
+ end
+ end
+
+ it "closes the IO after yielding" do
+ io = IO.popen(ruby_cmd('exit'), "r") { |_io| _io }
+ io.closed?.should be_true
+ end
+
+ it "allows the IO to be closed inside the block" do
+ io = IO.popen(ruby_cmd('exit'), 'r') { |_io| _io.close; _io }
+ io.closed?.should be_true
+ end
+
+ it "returns the value of the block" do
+ IO.popen(ruby_cmd('exit'), "r") { :hello }.should == :hello
+ end
+ end
+
+ with_feature :fork do
+ it "starts returns a forked process if the command is -" do
+ io = IO.popen("-")
+
+ if io # parent
+ begin
+ io.gets.should == "hello from child\n"
+ ensure
+ io.close
+ end
+ else # child
+ puts "hello from child"
+ exit!
+ end
+ end
+ end
+
+ with_feature :encoding do
+ it "has the given external encoding" do
+ @io = IO.popen(ruby_cmd('exit'), external_encoding: Encoding::EUC_JP)
+ @io.external_encoding.should == Encoding::EUC_JP
+ end
+
+ it "has the given internal encoding" do
+ @io = IO.popen(ruby_cmd('exit'), internal_encoding: Encoding::EUC_JP)
+ @io.internal_encoding.should == Encoding::EUC_JP
+ end
+
+ it "sets the internal encoding to nil if it's the same as the external encoding" do
+ @io = IO.popen(ruby_cmd('exit'), external_encoding: Encoding::EUC_JP,
+ internal_encoding: Encoding::EUC_JP)
+ @io.internal_encoding.should be_nil
+ end
+ end
+
+ context "with a leading ENV Hash" do
+ it "accepts a single String command" do
+ IO.popen({"FOO" => "bar"}, ruby_cmd('puts ENV["FOO"]')) do |io|
+ io.read.should == "bar\n"
+ end
+ end
+
+ it "accepts a single String command, and an IO mode" do
+ IO.popen({"FOO" => "bar"}, ruby_cmd('puts ENV["FOO"]'), "r") do |io|
+ io.read.should == "bar\n"
+ end
+ end
+
+ it "accepts a single String command with a trailing Hash of Process.exec options" do
+ IO.popen({"FOO" => "bar"}, ruby_cmd('STDERR.puts ENV["FOO"]'),
+ err: [:child, :out]) do |io|
+ io.read.should == "bar\n"
+ end
+ end
+
+ it "accepts a single String command with a trailing Hash of Process.exec options, and an IO mode" do
+ IO.popen({"FOO" => "bar"}, ruby_cmd('STDERR.puts ENV["FOO"]'), "r",
+ err: [:child, :out]) do |io|
+ io.read.should == "bar\n"
+ end
+ end
+
+ it "accepts an Array of command and arguments" do
+ exe, *args = ruby_exe
+ IO.popen({"FOO" => "bar"}, [[exe, "specfu"], *args, "-e", "puts ENV['FOO']"]) do |io|
+ io.read.should == "bar\n"
+ end
+ end
+
+ it "accepts an Array of command and arguments, and an IO mode" do
+ exe, *args = ruby_exe
+ IO.popen({"FOO" => "bar"}, [[exe, "specfu"], *args, "-e", "puts ENV['FOO']"], "r") do |io|
+ io.read.should == "bar\n"
+ end
+ end
+
+ it "accepts an Array command with a separate trailing Hash of Process.exec options" do
+ IO.popen({"FOO" => "bar"}, [*ruby_exe, "-e", "STDERR.puts ENV['FOO']"],
+ err: [:child, :out]) do |io|
+ io.read.should == "bar\n"
+ end
+ end
+
+ it "accepts an Array command with a separate trailing Hash of Process.exec options, and an IO mode" do
+ IO.popen({"FOO" => "bar"}, [*ruby_exe, "-e", "STDERR.puts ENV['FOO']"],
+ "r", err: [:child, :out]) do |io|
+ io.read.should == "bar\n"
+ end
+ end
+ end
+
+ context "with a leading Array argument" do
+ it "uses the Array as command plus args for the child process" do
+ IO.popen([*ruby_exe, "-e", "puts 'hello'"]) do |io|
+ io.read.should == "hello\n"
+ end
+ end
+
+ it "accepts a leading ENV Hash" do
+ IO.popen([{"FOO" => "bar"}, *ruby_exe, "-e", "puts ENV['FOO']"]) do |io|
+ io.read.should == "bar\n"
+ end
+ end
+
+ it "accepts a trailing Hash of Process.exec options" do
+ IO.popen([*ruby_exe, "does_not_exist", {err: [:child, :out]}]) do |io|
+ io.read.should =~ /LoadError/
+ end
+ end
+
+ it "accepts an IO mode argument following the Array" do
+ IO.popen([*ruby_exe, "does_not_exist", {err: [:child, :out]}], "r") do |io|
+ io.read.should =~ /LoadError/
+ end
+ end
+
+ it "accepts [env, command, arg1, arg2, ..., exec options]" do
+ IO.popen([{"FOO" => "bar"}, *ruby_exe, "-e", "STDERR.puts ENV[:FOO.to_s]",
+ err: [:child, :out]]) do |io|
+ io.read.should == "bar\n"
+ end
+ end
+
+ it "accepts '[env, command, arg1, arg2, ..., exec options], mode'" do
+ IO.popen([{"FOO" => "bar"}, *ruby_exe, "-e", "STDERR.puts ENV[:FOO.to_s]",
+ err: [:child, :out]], "r") do |io|
+ io.read.should == "bar\n"
+ end
+ end
+
+ it "accepts '[env, command, arg1, arg2, ..., exec options], mode, IO options'" do
+ IO.popen([{"FOO" => "bar"}, *ruby_exe, "-e", "STDERR.puts ENV[:FOO.to_s]",
+ err: [:child, :out]], "r",
+ internal_encoding: Encoding::EUC_JP) do |io|
+ io.read.should == "bar\n"
+ io.internal_encoding.should == Encoding::EUC_JP
+ end
+ end
+
+ it "accepts '[env, command, arg1, arg2, ...], mode, IO + exec options'" do
+ IO.popen([{"FOO" => "bar"}, *ruby_exe, "-e", "STDERR.puts ENV[:FOO.to_s]"], "r",
+ err: [:child, :out], internal_encoding: Encoding::EUC_JP) do |io|
+ io.read.should == "bar\n"
+ io.internal_encoding.should == Encoding::EUC_JP
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/io/pos_spec.rb b/spec/ruby/core/io/pos_spec.rb
new file mode 100644
index 0000000000..e6cda2643d
--- /dev/null
+++ b/spec/ruby/core/io/pos_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/pos'
+
+describe "IO#pos" do
+ it_behaves_like :io_pos, :pos
+end
+
+describe "IO#pos=" do
+ it_behaves_like :io_set_pos, :pos=
+end
diff --git a/spec/ruby/core/io/pread_spec.rb b/spec/ruby/core/io/pread_spec.rb
new file mode 100644
index 0000000000..b5b516fa53
--- /dev/null
+++ b/spec/ruby/core/io/pread_spec.rb
@@ -0,0 +1,52 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+ruby_version_is "2.5" do
+ platform_is_not :windows do
+ describe "IO#pread" do
+ before :each do
+ @fname = tmp("io_pread.txt")
+ @contents = "1234567890"
+ touch(@fname) { |f| f.write @contents }
+ @file = File.open(@fname, "r+")
+ end
+
+ after :each do
+ @file.close
+ rm_r @fname
+ end
+
+ it "accepts a length, and an offset" do
+ @file.pread(4, 0).should == "1234"
+ @file.pread(3, 4).should == "567"
+ end
+
+ it "accepts a length, an offset, and an output buffer" do
+ buffer = "foo"
+ @file.pread(3, 4, buffer)
+ buffer.should == "567"
+ end
+
+ it "does not advance the file pointer" do
+ @file.pread(4, 0).should == "1234"
+ @file.read.should == "1234567890"
+ end
+
+ it "raises EOFError if end-of-file is reached" do
+ lambda { @file.pread(1, 10) }.should raise_error(EOFError)
+ end
+
+ it "raises IOError when file is not open in read mode" do
+ File.open(@fname, "w") do |file|
+ lambda { file.pread(1, 1) }.should raise_error(IOError)
+ end
+ end
+
+ it "raises IOError when file is closed" do
+ file = File.open(@fname, "r+")
+ file.close
+ lambda { file.pread(1, 1) }.should raise_error(IOError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/io/print_spec.rb b/spec/ruby/core/io/print_spec.rb
new file mode 100644
index 0000000000..0dd48344ce
--- /dev/null
+++ b/spec/ruby/core/io/print_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe IO, "#print" do
+ before :each do
+ @old_separator = $\
+ $\ = '->'
+ @name = tmp("io_print")
+ end
+
+ after :each do
+ $\ = @old_separator
+ rm_r @name
+ 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")
+ $_ = o
+
+ touch(@name) { |f| f.print }
+ IO.read(@name).should == "mockmockmock#{$\}"
+
+ # Set $_ to something known
+ string = File.open(__FILE__) {|f| f.gets }
+
+ touch(@name) { |f| f.print }
+ IO.read(@name).should == "#{string}#{$\}"
+ end
+
+ it "calls obj.to_s and not obj.to_str then writes the record separator" do
+ o = mock('o')
+ o.should_not_receive(:to_str)
+ o.should_receive(:to_s).and_return("hello")
+
+ touch(@name) { |f| f.print(o) }
+
+ IO.read(@name).should == "hello#{$\}"
+ end
+
+ it "writes each obj.to_s to the stream 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}#{$\}"
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.print("stuff") }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/printf_spec.rb b/spec/ruby/core/io/printf_spec.rb
new file mode 100644
index 0000000000..be4e5c339e
--- /dev/null
+++ b/spec/ruby/core/io/printf_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#printf" do
+ before :each do
+ @name = tmp("io_printf.txt")
+ @io = new_io @name
+ @io.sync = true
+ end
+
+ after :each do
+ @io.close if @io
+ rm_r @name
+ end
+
+ it "calls #to_str to convert the format object to a String" do
+ obj = mock("printf format")
+ obj.should_receive(:to_str).and_return("%s")
+
+ @io.printf obj, "printf"
+ File.read(@name).should == "printf"
+ end
+
+ it "writes the #sprintf formatted string" do
+ @io.printf "%d %s", 5, "cookies"
+ File.read(@name).should == "5 cookies"
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.printf("stuff") }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/putc_spec.rb b/spec/ruby/core/io/putc_spec.rb
new file mode 100644
index 0000000000..73473ad821
--- /dev/null
+++ b/spec/ruby/core/io/putc_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/io/putc'
+
+describe "IO#putc" do
+ before :each do
+ @name = tmp("io_putc.txt")
+ @io_object = @io = new_io(@name)
+ end
+
+ it_behaves_like :io_putc, :putc
+end
diff --git a/spec/ruby/core/io/puts_spec.rb b/spec/ruby/core/io/puts_spec.rb
new file mode 100644
index 0000000000..e99cffb00f
--- /dev/null
+++ b/spec/ruby/core/io/puts_spec.rb
@@ -0,0 +1,141 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#puts" do
+ before :each do
+ @before_separator = $/
+ @name = tmp("io_puts.txt")
+ @io = new_io @name
+ ScratchPad.record ""
+ def @io.write(str)
+ ScratchPad << str
+ end
+ end
+
+ after :each do
+ ScratchPad.clear
+ @io.close if @io
+ rm_r @name
+ $/ = @before_separator
+ end
+
+ it "writes just a newline when given no args" do
+ @io.puts.should == nil
+ ScratchPad.recorded.should == "\n"
+ end
+
+ it "writes just a newline when given just a newline" do
+ lambda { $stdout.puts "\n" }.should output_to_fd("\n", STDOUT)
+ end
+
+ it "writes empty string with a newline when given nil as an arg" do
+ @io.puts(nil).should == nil
+ ScratchPad.recorded.should == "\n"
+ end
+
+ it "writes empty string with a newline when when given nil as multiple args" do
+ @io.puts(nil, nil).should == nil
+ ScratchPad.recorded.should == "\n\n"
+ end
+
+ it "calls :to_ary before writing non-string objects, regardless of it being implemented in the receiver" do
+ object = mock('hola')
+ object.should_receive(:method_missing).with(:to_ary)
+ object.should_receive(:to_s).and_return("#<Object:0x...>")
+
+ @io.puts(object).should == nil
+ ScratchPad.recorded.should == "#<Object:0x...>\n"
+ end
+
+ it "calls :to_ary before writing non-string objects" do
+ object = mock('hola')
+ object.should_receive(:to_ary).and_return(["hola"])
+
+ @io.puts(object).should == nil
+ ScratchPad.recorded.should == "hola\n"
+ end
+
+ it "calls :to_s before writing non-string objects that don't respond to :to_ary" do
+ object = mock('hola')
+ object.should_receive(:to_s).and_return("hola")
+
+ @io.puts(object).should == nil
+ ScratchPad.recorded.should == "hola\n"
+ end
+
+ it "returns general object info if :to_s does not return a string" do
+ object = mock('hola')
+ object.should_receive(:to_s).and_return(false)
+
+ @io.puts(object).should == nil
+ ScratchPad.recorded.should == object.inspect.split(" ")[0] + ">\n"
+ end
+
+ it "writes each arg if given several" do
+ @io.puts(1, "two", 3).should == nil
+ ScratchPad.recorded.should == "1\ntwo\n3\n"
+ end
+
+ it "flattens a nested array before writing it" do
+ @io.puts([1, 2, [3]]).should == nil
+ ScratchPad.recorded.should == "1\n2\n3\n"
+ end
+
+ it "writes nothing for an empty array" do
+ x = []
+ @io.should_not_receive(:write)
+ @io.puts(x).should == nil
+ end
+
+ it "writes [...] for a recursive array arg" do
+ x = []
+ x << 2 << x
+ @io.puts(x).should == nil
+ ScratchPad.recorded.should == "2\n[...]\n"
+ end
+
+ it "writes a newline after objects that do not end in newlines" do
+ @io.puts(5).should == nil
+ ScratchPad.recorded.should == "5\n"
+ end
+
+ it "does not write a newline after objects that end in newlines" do
+ @io.puts("5\n").should == nil
+ ScratchPad.recorded.should == "5\n"
+ end
+
+ it "ignores the $/ separator global" do
+ $/ = ":"
+ @io.puts(5).should == nil
+ ScratchPad.recorded.should == "5\n"
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.puts("stuff") }.should raise_error(IOError)
+ end
+
+ with_feature :encoding do
+ it "writes crlf when IO is opened with newline: :crlf" do
+ File.open(@name, 'wt', newline: :crlf) do |file|
+ file.puts
+ end
+ File.binread(@name).should == "\r\n"
+ end
+
+ it "writes cr when IO is opened with newline: :cr" do
+ File.open(@name, 'wt', newline: :cr) do |file|
+ file.puts
+ end
+ File.binread(@name).should == "\r"
+ end
+
+ platform_is_not :windows do # https://bugs.ruby-lang.org/issues/12436
+ it "writes lf when IO is opened with newline: :lf" do
+ File.open(@name, 'wt', newline: :lf) do |file|
+ file.puts
+ end
+ File.binread(@name).should == "\n"
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/io/pwrite_spec.rb b/spec/ruby/core/io/pwrite_spec.rb
new file mode 100644
index 0000000000..fd3d1b98e9
--- /dev/null
+++ b/spec/ruby/core/io/pwrite_spec.rb
@@ -0,0 +1,45 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+ruby_version_is "2.5" do
+ platform_is_not :windows do
+ describe "IO#pwrite" do
+ before :each do
+ @fname = tmp("io_pwrite.txt")
+ @file = File.open(@fname, "w+")
+ end
+
+ after :each do
+ @file.close
+ rm_r @fname
+ end
+
+ it "returns the number of bytes written" do
+ @file.pwrite("foo", 0).should == 3
+ end
+
+ it "accepts a string and an offset" do
+ @file.pwrite("foo", 2)
+ @file.pread(3, 2).should == "foo"
+ end
+
+ it "does not advance the pointer in the file" do
+ @file.pwrite("bar", 3)
+ @file.write("foo")
+ @file.pread(6, 0).should == "foobar"
+ end
+
+ it "raises IOError when file is not open in write mode" do
+ File.open(@fname, "r") do |file|
+ lambda { file.pwrite("foo", 1) }.should raise_error(IOError)
+ end
+ end
+
+ it "raises IOError when file is closed" do
+ file = File.open(@fname, "w+")
+ file.close
+ lambda { file.pwrite("foo", 1) }.should raise_error(IOError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/io/read_nonblock_spec.rb b/spec/ruby/core/io/read_nonblock_spec.rb
new file mode 100644
index 0000000000..3c02f662f6
--- /dev/null
+++ b/spec/ruby/core/io/read_nonblock_spec.rb
@@ -0,0 +1,92 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#read_nonblock" do
+ before :each do
+ @read, @write = IO.pipe
+ end
+
+ after :each do
+ @read.close if @read && !@read.closed?
+ @write.close if @write && !@write.closed?
+ end
+
+ it "raises an exception extending IO::WaitReadable when there is no data" do
+ lambda { @read.read_nonblock(5) }.should raise_error(IO::WaitReadable) { |e|
+ platform_is_not :windows do
+ e.should be_kind_of(Errno::EAGAIN)
+ end
+ platform_is :windows do
+ e.should be_kind_of(Errno::EWOULDBLOCK)
+ end
+ }
+ end
+
+ context "when exception option is set to false" do
+ context "when there is no data" do
+ it "returns :wait_readable" do
+ @read.read_nonblock(5, exception: false).should == :wait_readable
+ end
+ end
+
+ context "when the end is reached" do
+ it "returns nil" do
+ @write << "hello"
+ @write.close
+
+ @read.read_nonblock(5)
+
+ @read.read_nonblock(5, exception: false).should be_nil
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ it 'sets the IO in nonblock mode' do
+ require 'io/nonblock'
+ @write.write "abc"
+ @read.read_nonblock(1).should == "a"
+ @read.nonblock?.should == true
+ end
+ end
+
+ it "returns at most the number of bytes requested" do
+ @write << "hello"
+ @read.read_nonblock(4).should == "hell"
+ end
+
+ it "returns less data if that is all that is available" do
+ @write << "hello"
+ @read.read_nonblock(10).should == "hello"
+ end
+
+ it "allows for reading 0 bytes before any write" do
+ @read.read_nonblock(0).should == ""
+ end
+
+ it "allows for reading 0 bytes after a write" do
+ @write.write "1"
+ @read.read_nonblock(0).should == ""
+ @read.read_nonblock(1).should == "1"
+ end
+
+ it "reads into the passed buffer" do
+ buffer = ""
+ @write.write("1")
+ @read.read_nonblock(1, buffer)
+ buffer.should == "1"
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.read_nonblock(5) }.should raise_error(IOError)
+ end
+
+ it "raises EOFError when the end is reached" do
+ @write << "hello"
+ @write.close
+
+ @read.read_nonblock(5)
+
+ lambda { @read.read_nonblock(5) }.should raise_error(EOFError)
+ end
+end
diff --git a/spec/ruby/core/io/read_spec.rb b/spec/ruby/core/io/read_spec.rb
new file mode 100644
index 0000000000..3bb581f430
--- /dev/null
+++ b/spec/ruby/core/io/read_spec.rb
@@ -0,0 +1,620 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO.read" do
+ before :each do
+ @fname = tmp("io_read.txt")
+ @contents = "1234567890"
+ touch(@fname) { |f| f.write @contents }
+ end
+
+ after :each do
+ rm_r @fname
+ end
+
+ it "reads the contents of a file" do
+ IO.read(@fname).should == @contents
+ end
+
+ it "calls #to_path on non-String arguments" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return(@fname)
+ IO.read(p)
+ end
+
+ it "accepts an empty options Hash" do
+ IO.read(@fname, {}).should == @contents
+ end
+
+ it "accepts a length, and empty options Hash" do
+ IO.read(@fname, 3, {}).should == @contents[0, 3]
+ end
+
+ it "accepts a length, offset, and empty options Hash" do
+ IO.read(@fname, 3, 0, {}).should == @contents[0, 3]
+ end
+
+ it "raises an IOError if the options Hash specifies write mode" do
+ lambda { IO.read(@fname, 3, 0, {mode: "w"}) }.should raise_error(IOError)
+ end
+
+ it "raises an IOError if the options Hash specifies append only mode" do
+ lambda { IO.read(@fname, {mode: "a"}) }.should raise_error(IOError)
+ end
+
+ it "reads the file if the options Hash includes read mode" do
+ IO.read(@fname, {mode: "r"}).should == @contents
+ end
+
+ it "reads the file if the options Hash includes read/write mode" do
+ IO.read(@fname, {mode: "r+"}).should == @contents
+ end
+
+ it "reads the file if the options Hash includes read/write append mode" do
+ IO.read(@fname, {mode: "a+"}).should == @contents
+ end
+
+ it "treats second nil argument as no length limit" do
+ IO.read(@fname, nil).should == @contents
+ IO.read(@fname, nil, 5).should == IO.read(@fname, @contents.length, 5)
+ end
+
+ it "treats third nil argument as 0" do
+ IO.read(@fname, nil, nil).should == @contents
+ IO.read(@fname, 5, nil).should == IO.read(@fname, 5, 0)
+ end
+
+ it "reads the contents of a file up to a certain size when specified" do
+ IO.read(@fname, 5).should == @contents.slice(0..4)
+ end
+
+ it "reads the contents of a file from an offset of a specific size when specified" do
+ IO.read(@fname, 5, 3).should == @contents.slice(3, 5)
+ end
+
+ it "returns nil at end-of-file when length is passed" do
+ IO.read(@fname, 1, 10).should == nil
+ end
+
+ it "raises an Errno::ENOENT when the requested file does not exist" do
+ rm_r @fname
+ lambda { IO.read @fname }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises a TypeError when not passed a String type" do
+ lambda { IO.read nil }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError when not passed a valid length" do
+ lambda { IO.read @fname, -1 }.should raise_error(ArgumentError)
+ end
+
+ it "raises an Errno::EINVAL when not passed a valid offset" do
+ lambda { IO.read @fname, 0, -1 }.should raise_error(Errno::EINVAL)
+ lambda { IO.read @fname, -1, -1 }.should raise_error(Errno::EINVAL)
+ end
+
+ with_feature :encoding do
+ it "uses the external encoding specified via the :external_encoding option" do
+ str = IO.read(@fname, external_encoding: Encoding::ISO_8859_1)
+ str.encoding.should == Encoding::ISO_8859_1
+ end
+
+ it "uses the external encoding specified via the :encoding option" do
+ str = IO.read(@fname, encoding: Encoding::ISO_8859_1)
+ str.encoding.should == Encoding::ISO_8859_1
+ end
+ end
+end
+
+describe "IO.read from a pipe" do
+ it "runs the rest as a subprocess and returns the standard output" do
+ cmd = "|sh -c 'echo hello'"
+ platform_is :windows do
+ cmd = "|cmd.exe /C echo hello"
+ end
+ IO.read(cmd).should == "hello\n"
+ end
+
+ with_feature :fork do
+ it "opens a pipe to a fork if the rest is -" do
+ str = IO.read("|-")
+ if str # parent
+ str.should == "hello from child\n"
+ else #child
+ puts "hello from child"
+ exit!
+ end
+ end
+ end
+
+ it "reads only the specified number of bytes requested" do
+ cmd = "|sh -c 'echo hello'"
+ platform_is :windows do
+ cmd = "|cmd.exe /C echo hello"
+ end
+ IO.read(cmd, 1).should == "h"
+ end
+
+ platform_is_not :windows do
+ it "raises Errno::ESPIPE if passed an offset" do
+ lambda {
+ IO.read("|sh -c 'echo hello'", 1, 1)
+ }.should raise_error(Errno::ESPIPE)
+ end
+ end
+
+quarantine! do # The process tried to write to a nonexistent pipe.
+ platform_is :windows do
+ # TODO: It should raise Errno::ESPIPE on Windows as well
+ # once https://bugs.ruby-lang.org/issues/12230 is fixed.
+ it "raises Errno::EINVAL if passed an offset" do
+ lambda {
+ IO.read("|cmd.exe /C echo hello", 1, 1)
+ }.should raise_error(Errno::EINVAL)
+ end
+ end
+end
+end
+
+describe "IO.read on an empty file" do
+ before :each do
+ @fname = tmp("io_read_empty.txt")
+ touch(@fname)
+ end
+
+ after :each do
+ rm_r @fname
+ end
+
+ it "returns nil when length is passed" do
+ IO.read(@fname, 1).should == nil
+ end
+
+ it "returns an empty string when no length is passed" do
+ IO.read(@fname).should == ""
+ end
+end
+
+describe "IO#read" do
+
+ before :each do
+ @fname = tmp("io_read.txt")
+ @contents = "1234567890"
+ touch(@fname) { |f| f.write @contents }
+
+ @io = open @fname, "r+"
+ end
+
+ after :each do
+ @io.close
+ rm_r @fname
+ end
+
+ it "can be read from consecutively" do
+ @io.read(1).should == '1'
+ @io.read(2).should == '23'
+ @io.read(3).should == '456'
+ @io.read(4).should == '7890'
+ end
+
+ it "clears the output buffer if there is nothing to read" do
+ @io.pos = 10
+
+ buf = 'non-empty string'
+
+ @io.read(10, buf).should == nil
+
+ buf.should == ''
+ end
+
+ it "consumes zero bytes when reading zero bytes" do
+ @io.read(0).should == ''
+ @io.pos.should == 0
+
+ @io.getc.chr.should == '1'
+ end
+
+ it "is at end-of-file when everything has been read" do
+ @io.read
+ @io.eof?.should == true
+ end
+
+ it "reads the contents of a file" do
+ @io.read.should == @contents
+ end
+
+ it "places the specified number of bytes in the buffer" do
+ buf = ""
+ @io.read 5, buf
+
+ buf.should == "12345"
+ end
+
+ it "expands the buffer when too small" do
+ buf = "ABCDE"
+ @io.read nil, buf
+
+ buf.should == @contents
+ end
+
+ it "overwrites the buffer" do
+ buf = "ABCDEFGHIJ"
+ @io.read nil, buf
+
+ buf.should == @contents
+ end
+
+ it "truncates the buffer when too big" do
+ buf = "ABCDEFGHIJKLMNO"
+ @io.read nil, buf
+ buf.should == @contents
+
+ @io.rewind
+
+ buf = "ABCDEFGHIJKLMNO"
+ @io.read 5, buf
+ buf.should == @contents[0..4]
+ end
+
+ it "returns the given buffer" do
+ buf = ""
+
+ @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")
+ obj.should_receive(:to_str).any_number_of_times.and_return(buf)
+
+ @io.read(15, obj).should_not equal obj
+ buf.should == @contents
+ end
+
+ it "returns an empty string at end-of-file" do
+ @io.read
+ @io.read.should == ''
+ end
+
+ it "reads the contents of a file when more bytes are specified" do
+ @io.read(@contents.length + 1).should == @contents
+ end
+
+ it "returns an empty string at end-of-file" do
+ @io.read
+ @io.read.should == ''
+ end
+
+ it "returns an empty string when the current pos is bigger than the content size" do
+ @io.pos = 1000
+ @io.read.should == ''
+ end
+
+ it "returns nil at end-of-file with a length" do
+ @io.read
+ @io.read(1).should == nil
+ end
+
+ it "with length argument returns nil when the current pos is bigger than the content size" do
+ @io.pos = 1000
+ @io.read(1).should == nil
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.read }.should raise_error(IOError)
+ end
+
+
+ platform_is_not :windows do
+ it "raises IOError when stream is closed by another thread" do
+ r, w = IO.pipe
+ t = Thread.new do
+ begin
+ r.read(1)
+ rescue => e
+ e
+ end
+ end
+
+ Thread.pass until t.stop?
+ r.close
+ t.join
+ t.value.should be_kind_of(IOError)
+ w.close
+ end
+ end
+end
+
+platform_is :windows do
+ describe "IO#read on Windows" do
+ before :each do
+ @fname = tmp("io_read.txt")
+ touch(@fname, "wb") { |f| f.write "a\r\nb\r\nc" }
+ end
+
+ after :each do
+ @io.close if @io
+ rm_r @fname
+ end
+
+ it "normalizes line endings in text mode" do
+ @io = new_io(@fname, "r")
+ @io.read.should == "a\nb\nc"
+ end
+
+ it "does not normalize line endings in binary mode" do
+ @io = new_io(@fname, "rb")
+ @io.read.should == "a\r\nb\r\nc"
+ end
+ end
+end
+
+describe "IO#read" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ it "ignores unicode encoding" do
+ @io.readline.should == "Voici la ligne une.\n"
+ # read "Qui è"
+ @io.read(5).should == "Qui " + [195].pack('C*')
+ end
+end
+
+describe "IO#read in binary mode" do
+ before :each do
+ @internal = Encoding.default_internal
+ @name = fixture __FILE__, "read_binary.txt"
+ end
+
+ after :each do
+ Encoding.default_internal = @internal
+ end
+
+ it "does not transcode file contents when Encoding.default_internal is set" do
+ Encoding.default_internal = "utf-8"
+
+ result = File.open(@name, "rb") { |f| f.read }.chomp
+
+ result.encoding.should == Encoding::ASCII_8BIT
+ xE2 = [226].pack('C*')
+ result.should == ("abc" + xE2 + "def").force_encoding(Encoding::ASCII_8BIT)
+ 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::ASCII_8BIT
+ xE2 = [226].pack('C*')
+ result.should == ("abc" + xE2 + "def").force_encoding(Encoding::ASCII_8BIT)
+ end
+end
+
+describe "IO#read in text mode" do
+ before :each do
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+ @name = fixture __FILE__, "read_text.txt"
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+ end
+
+ it "reads data according to the internal encoding" do
+ Encoding.default_internal = "utf-8"
+ Encoding.default_external = "utf-8"
+
+ result = File.open(@name, "rt") { |f| f.read }.chomp
+
+ result.encoding.should == Encoding::UTF_8
+ result.should == "abcâdef"
+ end
+end
+
+describe "IO.read with BOM" do
+ it "reads a file without a bom" do
+ name = fixture __FILE__, "no_bom_UTF-8.txt"
+ result = File.read(name, mode: "rb:BOM|utf-8")
+ result.force_encoding("ascii-8bit").should == "UTF-8\n"
+ end
+
+ it "reads a file with a utf-8 bom" do
+ name = fixture __FILE__, "bom_UTF-8.txt"
+ result = File.read(name, mode: "rb:BOM|utf-16le")
+ result.force_encoding("ascii-8bit").should == "UTF-8\n"
+ end
+
+ it "reads a file with a utf-16le bom" do
+ name = fixture __FILE__, "bom_UTF-16LE.txt"
+ result = File.read(name, mode: "rb:BOM|utf-8")
+ result.force_encoding("ascii-8bit").should == "U\x00T\x00F\x00-\x001\x006\x00L\x00E\x00\n\x00"
+ end
+
+ it "reads a file with a utf-16be bom" do
+ name = fixture __FILE__, "bom_UTF-16BE.txt"
+ result = File.read(name, mode: "rb:BOM|utf-8")
+ result.force_encoding("ascii-8bit").should == "\x00U\x00T\x00F\x00-\x001\x006\x00B\x00E\x00\n"
+ end
+
+ it "reads a file with a utf-32le bom" do
+ name = fixture __FILE__, "bom_UTF-32LE.txt"
+ result = File.read(name, mode: "rb:BOM|utf-8")
+ result.force_encoding("ascii-8bit").should == "U\x00\x00\x00T\x00\x00\x00F\x00\x00\x00-\x00\x00\x003\x00\x00\x002\x00\x00\x00L\x00\x00\x00E\x00\x00\x00\n\x00\x00\x00"
+ end
+
+ it "reads a file with a utf-32be bom" do
+ name = fixture __FILE__, "bom_UTF-32BE.txt"
+ result = File.read(name, mode: "rb:BOM|utf-8")
+ result.force_encoding("ascii-8bit").should == "\x00\x00\x00U\x00\x00\x00T\x00\x00\x00F\x00\x00\x00-\x00\x00\x003\x00\x00\x002\x00\x00\x00B\x00\x00\x00E\x00\x00\x00\n"
+ end
+end
+
+with_feature :encoding do
+ describe :io_read_internal_encoding, shared: true do
+ it "returns a transcoded String" do
+ @io.read.should == "ã‚りãŒã¨ã†\n"
+ end
+
+ it "sets the String encoding to the internal encoding" do
+ @io.read.encoding.should equal(Encoding::UTF_8)
+ end
+
+ describe "when passed nil for limit" do
+ it "sets the buffer to a transcoded String" do
+ result = @io.read(nil, buf = "")
+ buf.should equal(result)
+ buf.should == "ã‚りãŒã¨ã†\n"
+ end
+
+ it "sets the buffer's encoding to the internal encoding" do
+ buf = "".force_encoding Encoding::ISO_8859_1
+ @io.read(nil, buf)
+ buf.encoding.should equal(Encoding::UTF_8)
+ end
+ end
+ end
+
+ describe :io_read_size_internal_encoding, shared: true do
+ it "reads bytes when passed a size" do
+ @io.read(2).should == [164, 162].pack('C*').force_encoding(Encoding::ASCII_8BIT)
+ end
+
+ it "returns a String in ASCII-8BIT when passed a size" do
+ @io.read(4).encoding.should equal(Encoding::ASCII_8BIT)
+ end
+
+ it "does not change the buffer's encoding when passed a limit" do
+ buf = "".force_encoding Encoding::ISO_8859_1
+ @io.read(4, buf)
+ buf.should == [164, 162, 164, 234].pack('C*').force_encoding(Encoding::ISO_8859_1)
+ buf.encoding.should equal(Encoding::ISO_8859_1)
+ end
+
+ it "trucates the buffer but does not change the buffer's encoding when no data remains" do
+ buf = "abc".force_encoding Encoding::ISO_8859_1
+ @io.read
+
+ @io.read(1, buf).should be_nil
+ buf.size.should == 0
+ buf.encoding.should equal(Encoding::ISO_8859_1)
+ end
+ end
+
+ describe "IO#read" do
+ describe "when IO#external_encoding and IO#internal_encoding are nil" do
+ before :each do
+ @name = tmp("io_read.txt")
+ touch(@name) { |f| f.write "\x00\x01\x02" }
+ @io = new_io @name, "r+"
+ end
+
+ after :each do
+ @io.close if @io
+ rm_r @name
+ end
+
+ it "sets the String encoding to Encoding.default_external" do
+ @io.read.encoding.should equal(Encoding.default_external)
+ end
+ end
+
+ describe "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.read.should == ("ã‚りãŒã¨ã†\n").encode(Encoding::EUC_JP)
+ end
+
+ it "sets the String encoding to the external encoding" do
+ @io.read.encoding.should equal(Encoding::EUC_JP)
+ end
+
+ it_behaves_like :io_read_size_internal_encoding, nil
+ 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_read_internal_encoding, nil
+ it_behaves_like :io_read_size_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_read_internal_encoding, nil
+ it_behaves_like :io_read_size_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_read_internal_encoding, nil
+ it_behaves_like :io_read_size_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_read_internal_encoding, nil
+ it_behaves_like :io_read_size_internal_encoding, nil
+ end
+ end
+ end
+end
+
+describe "IO#read with large data" do
+ before :each do
+ # TODO: what is the significance of this mystery math?
+ @data_size = 8096 * 2 + 1024
+ @data = "*" * @data_size
+
+ @fname = tmp("io_read.txt")
+ touch(@fname) { |f| f.write @data }
+ end
+
+ after :each do
+ rm_r @fname
+ end
+
+ it "reads all the data at once" do
+ File.open(@fname, 'r') { |io| ScratchPad.record io.read }
+
+ ScratchPad.recorded.size.should == @data_size
+ ScratchPad.recorded.should == @data
+ end
+
+ it "reads only the requested number of bytes" do
+ read_size = @data_size / 2
+ File.open(@fname, 'r') { |io| ScratchPad.record io.read(read_size) }
+
+ ScratchPad.recorded.size.should == read_size
+ ScratchPad.recorded.should == @data[0, read_size]
+ end
+end
diff --git a/spec/ruby/core/io/readbyte_spec.rb b/spec/ruby/core/io/readbyte_spec.rb
new file mode 100644
index 0000000000..f7653bf3bb
--- /dev/null
+++ b/spec/ruby/core/io/readbyte_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+
+describe "IO#readbyte" do
+ before :each do
+ @io = File.open(__FILE__, 'r')
+ end
+
+ after :each do
+ @io.close
+ end
+
+ it "reads one byte from the stream" do
+ byte = @io.readbyte
+ byte.should == ?r.getbyte(0)
+ @io.pos.should == 1
+ end
+
+ it "raises EOFError on EOF" do
+ @io.seek(999999)
+ lambda do
+ @io.readbyte
+ end.should raise_error EOFError
+ end
+
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/io/readchar_spec.rb b/spec/ruby/core/io/readchar_spec.rb
new file mode 100644
index 0000000000..74f78b5a6e
--- /dev/null
+++ b/spec/ruby/core/io/readchar_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#readchar" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "returns the next string from the stream" do
+ @io.readchar.should == 'V'
+ @io.readchar.should == 'o'
+ @io.readchar.should == 'i'
+ # read the rest of line
+ @io.readline.should == "ci la ligne une.\n"
+ @io.readchar.should == 'Q'
+ end
+
+ it "raises an EOFError when invoked at the end of the stream" do
+ @io.read
+ lambda { @io.readchar }.should raise_error(EOFError)
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.readchar }.should raise_error(IOError)
+ end
+end
+
+describe "IO#readchar" do
+ before :each do
+ @io = IOSpecs.io_fixture "empty.txt"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "raises EOFError on empty stream" do
+ lambda { @io.readchar }.should raise_error(EOFError)
+ end
+end
diff --git a/spec/ruby/core/io/readline_spec.rb b/spec/ruby/core/io/readline_spec.rb
new file mode 100644
index 0000000000..f82ba36a1d
--- /dev/null
+++ b/spec/ruby/core/io/readline_spec.rb
@@ -0,0 +1,53 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#readline" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "returns the next line on the stream" do
+ @io.readline.should == "Voici la ligne une.\n"
+ @io.readline.should == "Qui è la linea due.\n"
+ end
+
+ it "goes back to first position after a rewind" do
+ @io.readline.should == "Voici la ligne une.\n"
+ @io.rewind
+ @io.readline.should == "Voici la ligne une.\n"
+ end
+
+ it "returns characters after the position set by #seek" do
+ @io.seek(1)
+ @io.readline.should == "oici la ligne une.\n"
+ end
+
+ it "raises EOFError on end of stream" do
+ IOSpecs.lines.length.times { @io.readline }
+ lambda { @io.readline }.should raise_error(EOFError)
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.readline }.should raise_error(IOError)
+ end
+
+ it "assigns the returned line to $_" do
+ IOSpecs.lines.each do |line|
+ @io.readline
+ $_.should == line
+ end
+ end
+
+ ruby_version_is "2.4" do
+ describe "when passed chomp" do
+ it "returns the first line without a trailing newline character" do
+ @io.readline(chomp: true).should == IOSpecs.lines_without_newline_characters[0]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/io/readlines_spec.rb b/spec/ruby/core/io/readlines_spec.rb
new file mode 100644
index 0000000000..533c9c3132
--- /dev/null
+++ b/spec/ruby/core/io/readlines_spec.rb
@@ -0,0 +1,210 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/readlines'
+
+describe "IO#readlines" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ @orig_exteenc = Encoding.default_external
+ Encoding.default_external = Encoding::UTF_8
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ Encoding.default_external = @orig_exteenc
+ end
+
+ it "raises an IOError if the stream is closed" do
+ @io.close
+ lambda { @io.readlines }.should raise_error(IOError)
+ end
+
+ describe "when passed no arguments" do
+ before :each do
+ @sep, $/ = $/, " "
+ end
+
+ after :each do
+ $/ = @sep
+ end
+
+ it "returns an Array containing lines based on $/" do
+ @io.readlines.should == IOSpecs.lines_space_separator
+ end
+ end
+
+ describe "when passed no arguments" do
+ it "updates self's position" do
+ @io.readlines
+ @io.pos.should eql(137)
+ end
+
+ it "updates self's lineno based on the number of lines read" do
+ @io.readlines
+ @io.lineno.should eql(9)
+ end
+
+ it "does not change $_" do
+ $_ = "test"
+ @io.readlines
+ $_.should == "test"
+ end
+
+ it "returns an empty Array when self is at the end" do
+ @io.readlines.should == IOSpecs.lines
+ @io.readlines.should == []
+ end
+ end
+
+ describe "when passed nil" do
+ it "returns the remaining content as one line starting at the current position" do
+ @io.readlines(nil).should == [IOSpecs.lines.join]
+ end
+ end
+
+ describe "when passed an empty String" do
+ it "returns an Array containing all paragraphs" do
+ @io.readlines("").should == IOSpecs.paragraphs
+ end
+ end
+
+ describe "when passed a separator" do
+ it "returns an Array containing lines based on the separator" do
+ @io.readlines("r").should == IOSpecs.lines_r_separator
+ end
+
+ it "returns an empty Array when self is at the end" do
+ @io.readlines
+ @io.readlines("r").should == []
+ end
+
+ it "updates self's lineno based on the number of lines read" do
+ @io.readlines("r")
+ @io.lineno.should eql(5)
+ end
+
+ it "updates self's position based on the number of characters read" do
+ @io.readlines("r")
+ @io.pos.should eql(137)
+ end
+
+ it "does not change $_" do
+ $_ = "test"
+ @io.readlines("r")
+ $_.should == "test"
+ end
+
+ it "tries to convert the passed separator to a String using #to_str" do
+ obj = mock('to_str')
+ obj.stub!(:to_str).and_return("r")
+ @io.readlines(obj).should == IOSpecs.lines_r_separator
+ end
+ end
+
+ describe "when passed a string that starts with a |" do
+ it "gets data from the standard out of the subprocess" do
+ cmd = "|sh -c 'echo hello;echo line2'"
+ platform_is :windows do
+ cmd = "|cmd.exe /C echo hello&echo line2"
+ end
+ lines = IO.readlines(cmd)
+ lines.should == ["hello\n", "line2\n"]
+ end
+
+ with_feature :fork do
+ it "gets data from a fork when passed -" do
+ lines = IO.readlines("|-")
+
+ if lines # parent
+ lines.should == ["hello\n", "from a fork\n"]
+ else
+ puts "hello"
+ puts "from a fork"
+ exit!
+ end
+ end
+ end
+ end
+end
+
+describe "IO#readlines" do
+ before :each do
+ @name = tmp("io_readlines")
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "raises an IOError if the stream is opened for append only" do
+ lambda do
+ File.open(@name, fmode("a:utf-8")) { |f| f.readlines }
+ end.should raise_error(IOError)
+ end
+
+ it "raises an IOError if the stream is opened for write only" do
+ lambda do
+ File.open(@name, fmode("w:utf-8")) { |f| f.readlines }
+ end.should raise_error(IOError)
+ end
+end
+
+describe "IO.readlines" do
+ before :each do
+ @external = Encoding.default_external
+ Encoding.default_external = Encoding::UTF_8
+
+ @name = fixture __FILE__, "lines.txt"
+ ScratchPad.record []
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ end
+
+ it "does not change $_" do
+ $_ = "test"
+ IO.readlines(@name)
+ $_.should == "test"
+ end
+
+ it_behaves_like :io_readlines, :readlines
+ it_behaves_like :io_readlines_options_19, :readlines
+end
+
+describe "IO.readlines" do
+ before :each do
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+ @name = fixture __FILE__, "lines.txt"
+ @dollar_slash = $/
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+ $/ = @dollar_slash
+ end
+
+ it "encodes lines using the default external encoding" do
+ Encoding.default_external = Encoding::UTF_8
+ lines = IO.readlines(@name)
+ lines.all? { |s| s.encoding == Encoding::UTF_8 }.should be_true
+ end
+
+ it "encodes lines using the default internal encoding, when set" do
+ Encoding.default_external = Encoding::UTF_8
+ Encoding.default_internal = Encoding::UTF_16
+ $/ = $/.encode Encoding::UTF_16
+ lines = IO.readlines(@name)
+ lines.all? { |s| s.encoding == Encoding::UTF_16 }.should be_true
+ end
+
+ it "ignores the default internal encoding if the external encoding is ASCII-8BIT" do
+ Encoding.default_external = Encoding::ASCII_8BIT
+ Encoding.default_internal = Encoding::UTF_8
+ lines = IO.readlines(@name)
+ lines.all? { |s| s.encoding == Encoding::ASCII_8BIT }.should be_true
+ end
+end
diff --git a/spec/ruby/core/io/readpartial_spec.rb b/spec/ruby/core/io/readpartial_spec.rb
new file mode 100644
index 0000000000..53160ca337
--- /dev/null
+++ b/spec/ruby/core/io/readpartial_spec.rb
@@ -0,0 +1,96 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#readpartial" do
+ before :each do
+ @rd, @wr = IO.pipe
+ @rd.binmode
+ @wr.binmode
+ end
+
+ after :each do
+ @rd.close unless @rd.closed?
+ @wr.close unless @wr.closed?
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.readpartial(10) }.should raise_error(IOError)
+
+ @rd.close
+ lambda { @rd.readpartial(10) }.should raise_error(IOError)
+ end
+
+ it "reads at most the specified number of bytes" do
+ @wr.write("foobar")
+
+ # buffered read
+ @rd.read(1).should == 'f'
+ # return only specified number, not the whole buffer
+ @rd.readpartial(1).should == "o"
+ end
+
+ it "reads after ungetc with data in the buffer" do
+ @wr.write("foobar")
+ c = @rd.getc
+ @rd.ungetc(c)
+ @rd.readpartial(3).should == "foo"
+ @rd.readpartial(3).should == "bar"
+ end
+
+ it "reads after ungetc with multibyte characters in the buffer" do
+ @wr.write("∂φ/∂x = gaîté")
+ c = @rd.getc
+ @rd.ungetc(c)
+ @rd.readpartial(3).should == "\xE2\x88\x82"
+ @rd.readpartial(3).should == "\xCF\x86/"
+ end
+
+ it "reads after ungetc without data in the buffer" do
+ @wr.write("f")
+ c = @rd.getc
+ @rd.ungetc(c)
+ @rd.readpartial(2).should == "f"
+
+ # now, also check that the ungot char is cleared and
+ # not returned again
+ @wr.write("b")
+ @rd.readpartial(2).should == "b"
+ end
+
+ it "discards the existing buffer content upon successful read" do
+ buffer = "existing"
+ @wr.write("hello world")
+ @wr.close
+ @rd.readpartial(11, buffer)
+ buffer.should == "hello world"
+ end
+
+ it "raises EOFError on EOF" do
+ @wr.write("abc")
+ @wr.close
+ @rd.readpartial(10).should == 'abc'
+ lambda { @rd.readpartial(10) }.should raise_error(EOFError)
+ end
+
+ it "discards the existing buffer content upon error" do
+ buffer = 'hello'
+ @wr.close
+ lambda { @rd.readpartial(1, buffer) }.should raise_error(EOFError)
+ buffer.should be_empty
+ end
+
+ it "raises IOError if the stream is closed" do
+ @wr.close
+ lambda { @rd.readpartial(1) }.should raise_error(IOError)
+ end
+
+ it "raises ArgumentError if the negative argument is provided" do
+ lambda { @rd.readpartial(-1) }.should raise_error(ArgumentError)
+ end
+
+ it "immediately returns an empty string if the length argument is 0" do
+ @rd.readpartial(0).should == ""
+ end
+
+end
diff --git a/spec/ruby/core/io/reopen_spec.rb b/spec/ruby/core/io/reopen_spec.rb
new file mode 100644
index 0000000000..e769991554
--- /dev/null
+++ b/spec/ruby/core/io/reopen_spec.rb
@@ -0,0 +1,306 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+require 'fcntl'
+
+describe "IO#reopen" do
+ before :each do
+ @name = tmp("io_reopen.txt")
+ @other_name = tmp("io_reopen_other.txt")
+
+ @io = new_io @name
+ @other_io = File.open @other_name, "w"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ @other_io.close unless @other_io.closed?
+ rm_r @name, @other_name
+ end
+
+ it "calls #to_io to convert an object" do
+ obj = mock("io")
+ obj.should_receive(:to_io).and_return(@other_io)
+ @io.reopen obj
+ end
+
+ it "changes the class of the instance to the class of the object returned by #to_io" do
+ obj = mock("io")
+ obj.should_receive(:to_io).and_return(@other_io)
+ @io.reopen(obj).should be_an_instance_of(File)
+ end
+
+ it "raises an IOError if the object returned by #to_io is closed" do
+ obj = mock("io")
+ obj.should_receive(:to_io).and_return(IOSpecs.closed_io)
+ lambda { @io.reopen obj }.should raise_error(IOError)
+ end
+
+ it "raises a TypeError if #to_io does not return an IO instance" do
+ obj = mock("io")
+ obj.should_receive(:to_io).and_return("something else")
+ lambda { @io.reopen obj }.should raise_error(TypeError)
+ end
+
+ it "raises an IOError when called on a closed stream with an object" do
+ @io.close
+ obj = mock("io")
+ obj.should_not_receive(:to_io)
+ lambda { @io.reopen(STDOUT) }.should raise_error(IOError)
+ end
+
+ it "raises an IOError if the IO argument is closed" do
+ lambda { @io.reopen(IOSpecs.closed_io) }.should raise_error(IOError)
+ end
+
+ it "raises an IOError when called on a closed stream with an IO" do
+ @io.close
+ lambda { @io.reopen(STDOUT) }.should raise_error(IOError)
+ end
+end
+
+describe "IO#reopen with a String" do
+ before :each do
+ @name = fixture __FILE__, "numbered_lines.txt"
+ @other_name = tmp("io_reopen.txt")
+ touch @other_name
+ @io = IOSpecs.io_fixture "lines.txt"
+
+ @tmp_file = tmp("reopen")
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @other_name, @tmp_file
+ end
+
+ it "does not raise an exception when called on a closed stream with a path" do
+ @io.close
+ @io.reopen @name, "r"
+ @io.closed?.should be_false
+ @io.gets.should == "Line 1: One\n"
+ end
+
+ it "returns self" do
+ @io.reopen(@name).should equal(@io)
+ end
+
+ it "positions a newly created instance at the beginning of the new stream" do
+ @io.reopen(@name)
+ @io.gets.should == "Line 1: One\n"
+ end
+
+ it "positions an instance that has been read from at the beginning of the new stream" do
+ @io.gets
+ @io.reopen(@name)
+ @io.gets.should == "Line 1: One\n"
+ end
+
+ platform_is_not :windows do
+ it "passes all mode flags through" do
+ @io.reopen(@tmp_file, "ab")
+ (@io.fcntl(Fcntl::F_GETFL) & File::APPEND).should == File::APPEND
+ end
+ end
+
+ platform_is_not :windows do
+ # TODO Should this work on Windows?
+ it "affects exec/system/fork performed after it" do
+ ruby_exe fixture(__FILE__, "reopen_stdout.rb"), args: @tmp_file
+ File.read(@tmp_file).should == "from system\nfrom exec\n"
+ end
+ end
+
+ it "calls #to_path on non-String arguments" do
+ obj = mock('path')
+ obj.should_receive(:to_path).and_return(@other_name)
+ @io.reopen(obj)
+ end
+end
+
+describe "IO#reopen with a String" do
+ before :each do
+ @name = tmp("io_reopen.txt")
+ @other_name = tmp("io_reopen_other.txt")
+ @other_io = nil
+
+ rm_r @other_name
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ @other_io.close if @other_io and not @other_io.closed?
+ rm_r @name, @other_name
+ end
+
+ it "opens a path after writing to the original file descriptor" do
+ @io = new_io @name, "w"
+
+ @io.print "original data"
+ @io.reopen @other_name
+ @io.print "new data"
+ @io.flush
+
+ File.read(@name).should == "original data"
+ File.read(@other_name).should == "new data"
+ end
+
+ # File descriptor numbers are not predictable in multi-threaded code;
+ # MJIT will be opening/closing files the background
+ without_feature :mjit do
+ it "closes the file descriptor obtained by opening the new file" do
+ @io = new_io @name, "w"
+
+ @other_io = File.open @other_name, "w"
+ max = @other_io.fileno
+ @other_io.close
+
+ @io.reopen @other_name
+
+ @other_io = File.open @other_name, "w"
+ @other_io.fileno.should == max
+ end
+ end
+
+ it "creates the file if it doesn't exist if the IO is opened in write mode" do
+ @io = new_io @name, "w"
+
+ @io.reopen(@other_name)
+ File.exist?(@other_name).should be_true
+ end
+
+ it "creates the file if it doesn't exist if the IO is opened in write mode" do
+ @io = new_io @name, "a"
+
+ @io.reopen(@other_name)
+ File.exist?(@other_name).should be_true
+ end
+end
+
+describe "IO#reopen with a String" do
+ before :each do
+ @name = tmp("io_reopen.txt")
+ @other_name = tmp("io_reopen_other.txt")
+
+ touch @name
+ rm_r @other_name
+ end
+
+ after :each do
+ @io.close
+ rm_r @name, @other_name
+ end
+
+ it "raises an Errno::ENOENT if the file does not exist and the IO is not opened in write mode" do
+ @io = new_io @name, "r"
+ lambda { @io.reopen(@other_name) }.should raise_error(Errno::ENOENT)
+ end
+end
+
+describe "IO#reopen with an IO at EOF" do
+ before :each do
+ @name = tmp("io_reopen.txt")
+ touch(@name) { |f| f.puts "a line" }
+ @other_name = tmp("io_reopen_other.txt")
+ touch(@other_name) do |f|
+ f.puts "Line 1"
+ f.puts "Line 2"
+ end
+
+ @io = new_io @name, "r"
+ @other_io = new_io @other_name, "r"
+ @io.read
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ @other_io.close unless @other_io.closed?
+ rm_r @name, @other_name
+ end
+
+ it "resets the EOF status to false" do
+ @io.eof?.should be_true
+ @io.reopen @other_io
+ @io.eof?.should be_false
+ end
+end
+
+describe "IO#reopen with an IO" do
+ before :each do
+ @name = tmp("io_reopen.txt")
+ @other_name = tmp("io_reopen_other.txt")
+ touch(@other_name) do |f|
+ f.puts "Line 1"
+ f.puts "Line 2"
+ end
+
+ @io = new_io @name
+ @other_io = new_io @other_name, "r"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ @other_io.close unless @other_io.closed?
+ rm_r @name, @other_name
+ end
+
+ it "does not call #to_io" do
+ # Why do we not use #should_not_receive(:to_io) here? Because
+ # MRI actually changes the class of @io in the call to #reopen
+ # but does not preserve the existing singleton class of @io.
+ def @io.to_io; flunk; end
+ @io.reopen(@other_io).should be_an_instance_of(IO)
+ end
+
+ it "does not change the object_id" do
+ obj_id = @io.object_id
+ @io.reopen @other_io
+ @io.object_id.should == obj_id
+ end
+
+ it "reads from the beginning if the other IO has not been read from" do
+ @io.reopen @other_io
+ @io.gets.should == "Line 1\n"
+ end
+
+ it "reads from the current position of the other IO's stream" do
+ @other_io.gets.should == "Line 1\n"
+ @io.reopen @other_io
+ @io.gets.should == "Line 2\n"
+ end
+end
+
+describe "IO#reopen with an IO" do
+ before :each do
+ @name = tmp("io_reopen.txt")
+ @other_name = tmp("io_reopen_other.txt")
+
+ @io = new_io @name
+ @other_io = File.open @other_name, "w"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ @other_io.close unless @other_io.closed?
+ rm_r @name, @other_name
+ end
+
+ it "associates the IO instance with the other IO's stream" do
+ File.read(@other_name).should == ""
+ @io.reopen @other_io
+ @io.print "io data"
+ @io.flush
+ File.read(@name).should == ""
+ File.read(@other_name).should == "io data"
+ end
+
+ it "may change the class of the instance" do
+ @io.reopen @other_io
+ @io.should be_an_instance_of(File)
+ end
+
+ it "sets path equals to the other IO's path if other IO is File" do
+ @io.reopen @other_io
+ @io.path.should == @other_io.path
+ end
+end
diff --git a/spec/ruby/core/io/rewind_spec.rb b/spec/ruby/core/io/rewind_spec.rb
new file mode 100644
index 0000000000..d1ec7a69c7
--- /dev/null
+++ b/spec/ruby/core/io/rewind_spec.rb
@@ -0,0 +1,38 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#rewind" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "positions the instance to the beginning of input" do
+ @io.readline.should == "Voici la ligne une.\n"
+ @io.readline.should == "Qui è la linea due.\n"
+ @io.rewind
+ @io.readline.should == "Voici la ligne une.\n"
+ end
+
+ it "positions the instance to the beginning of input and clears EOF" do
+ value = @io.read
+ @io.rewind
+ @io.eof?.should == false
+ value.should == @io.read
+ end
+
+ it "sets lineno to 0" do
+ @io.readline.should == "Voici la ligne une.\n"
+ @io.lineno.should == 1
+ @io.rewind
+ @io.lineno.should == 0
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.rewind }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/seek_spec.rb b/spec/ruby/core/io/seek_spec.rb
new file mode 100644
index 0000000000..9978d7c0e7
--- /dev/null
+++ b/spec/ruby/core/io/seek_spec.rb
@@ -0,0 +1,79 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/pos'
+
+describe "IO#seek" do
+ it_behaves_like :io_set_pos, :seek
+end
+
+describe "IO#seek" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "moves the read position relative to the current position with SEEK_CUR" do
+ lambda { @io.seek(-1) }.should raise_error(Errno::EINVAL)
+ @io.seek(10, IO::SEEK_CUR)
+ @io.readline.should == "igne une.\n"
+ @io.seek(-5, IO::SEEK_CUR)
+ @io.readline.should == "une.\n"
+ end
+
+ it "moves the read position relative to the start with SEEK_SET" do
+ @io.seek(1)
+ @io.pos.should == 1
+ @io.rewind
+ @io.seek(43, IO::SEEK_SET)
+ @io.readline.should == "Aquí está la línea tres.\n"
+ @io.seek(5, IO::SEEK_SET)
+ @io.readline.should == " la ligne une.\n"
+ end
+
+ it "moves the read position relative to the end with SEEK_END" do
+ @io.seek(0, IO::SEEK_END)
+ @io.tell.should == 137
+ @io.seek(-25, IO::SEEK_END)
+ @io.readline.should == "cinco.\n"
+ end
+
+ it "moves the read position and clears EOF with SEEK_SET" do
+ value = @io.read
+ @io.seek(0, IO::SEEK_SET)
+ @io.eof?.should == false
+ value.should == @io.read
+ end
+
+ it "moves the read position and clears EOF with SEEK_CUR" do
+ value = @io.read
+ @io.seek(-1, IO::SEEK_CUR)
+ @io.eof?.should == false
+ value[-1].should == @io.read[0]
+ end
+
+ it "moves the read position and clears EOF with SEEK_END" do
+ value = @io.read
+ @io.seek(-1, IO::SEEK_END)
+ @io.eof?.should == false
+ value[-1].should == @io.read[0]
+ end
+
+ platform_is :darwin do
+ it "supports seek offsets greater than 2^32" do
+ begin
+ zero = File.open('/dev/zero')
+ offset = 2**33
+ zero.seek(offset, File::SEEK_SET)
+ pos = zero.pos
+
+ pos.should == offset
+ ensure
+ zero.close rescue nil
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/io/select_spec.rb b/spec/ruby/core/io/select_spec.rb
new file mode 100644
index 0000000000..e69669efd2
--- /dev/null
+++ b/spec/ruby/core/io/select_spec.rb
@@ -0,0 +1,120 @@
+require_relative '../../spec_helper'
+
+describe "IO.select" do
+ before :each do
+ @rd, @wr = IO.pipe
+ end
+
+ after :each do
+ @rd.close unless @rd.closed?
+ @wr.close unless @wr.closed?
+ end
+
+ it "blocks for duration of timeout and returns nil if there are no objects ready for I/O" do
+ IO.select([@rd], nil, nil, 0.001).should == nil
+ end
+
+ it "returns immediately all objects that are ready for I/O when timeout is 0" do
+ @wr.syswrite("be ready")
+ IO.pipe do |_, wr|
+ result = IO.select [@rd], [wr], nil, 0
+ result.should == [[@rd], [wr], []]
+ end
+ end
+
+ it "returns nil after timeout if there are no objects ready for I/O" do
+ result = IO.select [@rd], nil, nil, 0
+ result.should == nil
+ end
+
+ it "returns supplied objects when they are ready for I/O" do
+ main = Thread.current
+ t = Thread.new {
+ Thread.pass until main.status == "sleep"
+ @wr.write "be ready"
+ }
+ result = IO.select [@rd], nil, nil, nil
+ result.should == [[@rd], [], []]
+ t.join
+ end
+
+ it "leaves out IO objects for which there is no I/O ready" do
+ @wr.write "be ready"
+ platform_is :aix do
+ # In AIX, when a pipe is readable, select(2) returns the write side
+ # of the pipe as "readable", even though you cannot actually read
+ # anything from the write side.
+ result = IO.select [@wr, @rd], nil, nil, nil
+ result.should == [[@wr, @rd], [], []]
+ end
+ platform_is_not :aix do
+ # Order matters here. We want to see that @wr doesn't expand the size
+ # of the returned array, so it must be 1st.
+ result = IO.select [@wr, @rd], nil, nil, nil
+ result.should == [[@rd], [], []]
+ end
+ end
+
+ it "returns supplied objects correctly even when monitoring the same object in different arrays" do
+ filename = tmp("IO_select_pipe_file") + $$.to_s
+ io = File.open(filename, 'w+')
+ result = IO.select [io], [io], nil, 0
+ result.should == [[io], [io], []]
+ io.close
+ rm_r filename
+ end
+
+ it "invokes to_io on supplied objects that are not IO and returns the supplied objects" do
+ # make some data available
+ @wr.write("foobar")
+
+ obj = mock("read_io")
+ obj.should_receive(:to_io).at_least(1).and_return(@rd)
+ IO.select([obj]).should == [[obj], [], []]
+
+ IO.pipe do |_, wr|
+ obj = mock("write_io")
+ obj.should_receive(:to_io).at_least(1).and_return(wr)
+ IO.select(nil, [obj]).should == [[], [obj], []]
+ end
+ end
+
+ it "raises TypeError if supplied objects are not IO" do
+ lambda { IO.select([Object.new]) }.should raise_error(TypeError)
+ lambda { IO.select(nil, [Object.new]) }.should raise_error(TypeError)
+
+ obj = mock("io")
+ obj.should_receive(:to_io).any_number_of_times.and_return(nil)
+
+ lambda { IO.select([obj]) }.should raise_error(TypeError)
+ lambda { IO.select(nil, [obj]) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the specified timeout value is not Numeric" do
+ lambda { IO.select([@rd], nil, nil, Object.new) }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError if the first three arguments are not Arrays" do
+ lambda { IO.select(Object.new)}.should raise_error(TypeError)
+ lambda { IO.select(nil, Object.new)}.should raise_error(TypeError)
+ lambda { IO.select(nil, nil, Object.new)}.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError when passed a negative timeout" do
+ lambda { IO.select(nil, nil, nil, -5)}.should raise_error(ArgumentError)
+ end
+end
+
+describe "IO.select when passed nil for timeout" do
+ it "sleeps forever and sets the thread status to 'sleep'" do
+ t = Thread.new do
+ IO.select(nil, nil, nil, nil)
+ end
+
+ Thread.pass while t.status && t.status != "sleep"
+ t.join unless t.status
+ t.status.should == "sleep"
+ t.kill
+ t.join
+ end
+end
diff --git a/spec/ruby/core/io/set_encoding_spec.rb b/spec/ruby/core/io/set_encoding_spec.rb
new file mode 100644
index 0000000000..b749331ee0
--- /dev/null
+++ b/spec/ruby/core/io/set_encoding_spec.rb
@@ -0,0 +1,193 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe :io_set_encoding_write, shared: true do
+ it "sets the encodings to nil" do
+ @io = new_io @name, "#{@object}:ibm437:ibm866"
+ @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
+
+ Encoding.default_external = Encoding::IBM437
+ Encoding.default_internal = Encoding::IBM866
+
+ @io.external_encoding.should be_nil
+ @io.internal_encoding.should be_nil
+ end
+
+ it "sets the encodings to the current Encoding defaults" do
+ @io = new_io @name, @object
+
+ Encoding.default_external = Encoding::IBM437
+ Encoding.default_internal = Encoding::IBM866
+
+ @io.set_encoding nil, nil
+
+ @io.external_encoding.should == Encoding::IBM437
+ @io.internal_encoding.should == Encoding::IBM866
+ end
+ end
+
+ describe "IO#set_encoding when passed nil, nil" do
+ before :each do
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+
+ Encoding.default_external = Encoding::UTF_8
+ Encoding.default_internal = nil
+
+ @name = tmp('io_set_encoding.txt')
+ touch(@name)
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+
+ @io.close if @io and not @io.closed?
+ rm_r @name
+ end
+
+ describe "with 'r' mode" do
+ it "sets the encodings to the current Encoding defaults" do
+ @io = new_io @name, "r"
+
+ Encoding.default_external = Encoding::IBM437
+ Encoding.default_internal = Encoding::IBM866
+
+ @io.set_encoding nil, nil
+ @io.external_encoding.should equal(Encoding::IBM437)
+ @io.internal_encoding.should equal(Encoding::IBM866)
+ end
+
+ it "prevents the #internal_encoding from changing when Encoding.default_internal is changed" do
+ @io = new_io @name, "r"
+ @io.set_encoding nil, nil
+
+ Encoding.default_internal = Encoding::IBM437
+
+ @io.internal_encoding.should be_nil
+ end
+
+ it "allows the #external_encoding to change when Encoding.default_external is changed" do
+ @io = new_io @name, "r"
+ @io.set_encoding nil, nil
+
+ Encoding.default_external = Encoding::IBM437
+
+ @io.external_encoding.should equal(Encoding::IBM437)
+ end
+ end
+
+ describe "with 'rb' mode" do
+ it "returns Encoding.default_external" do
+ @io = new_io @name, "rb"
+ @io.external_encoding.should equal(Encoding::ASCII_8BIT)
+
+ @io.set_encoding nil, nil
+ @io.external_encoding.should equal(Encoding.default_external)
+ end
+ end
+
+ describe "with 'r+' mode" do
+ it_behaves_like :io_set_encoding_write, nil, "r+"
+ end
+
+ describe "with 'w' mode" do
+ it_behaves_like :io_set_encoding_write, nil, "w"
+ end
+
+ describe "with 'w+' mode" do
+ it_behaves_like :io_set_encoding_write, nil, "w+"
+ end
+
+ describe "with 'a' mode" do
+ it_behaves_like :io_set_encoding_write, nil, "a"
+ end
+
+ describe "with 'a+' mode" do
+ it_behaves_like :io_set_encoding_write, nil, "a+"
+ end
+ end
+
+ describe "IO#set_encoding" do
+ before :each do
+ @name = tmp('io_set_encoding.txt')
+ touch(@name)
+ @io = new_io @name
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @name
+ end
+
+ it "returns self" do
+ @io.set_encoding(Encoding::UTF_8).should equal(@io)
+ end
+
+ it "sets the external encoding when passed an Encoding argument" do
+ @io.set_encoding(Encoding::UTF_8)
+ @io.external_encoding.should == Encoding::UTF_8
+ @io.internal_encoding.should be_nil
+ end
+
+ it "sets the external and internal encoding when passed two Encoding arguments" do
+ @io.set_encoding(Encoding::UTF_8, Encoding::UTF_16BE)
+ @io.external_encoding.should == Encoding::UTF_8
+ @io.internal_encoding.should == Encoding::UTF_16BE
+ end
+
+ it "sets the external encoding when passed the name of an Encoding" do
+ @io.set_encoding("utf-8")
+ @io.external_encoding.should == Encoding::UTF_8
+ @io.internal_encoding.should be_nil
+ end
+
+ it "ignores the internal encoding if the same as external when passed Encoding objects" do
+ @io.set_encoding(Encoding::UTF_8, Encoding::UTF_8)
+ @io.external_encoding.should == Encoding::UTF_8
+ @io.internal_encoding.should be_nil
+ end
+
+ it "ignores the internal encoding if the same as external when passed encoding names separanted by ':'" do
+ @io.set_encoding("utf-8:utf-8")
+ @io.external_encoding.should == Encoding::UTF_8
+ @io.internal_encoding.should be_nil
+ end
+
+ it "sets the external and internal encoding when passed the names of Encodings separated by ':'" do
+ @io.set_encoding("utf-8:utf-16be")
+ @io.external_encoding.should == Encoding::UTF_8
+ @io.internal_encoding.should == Encoding::UTF_16BE
+ end
+
+ it "sets the external and internal encoding when passed two String arguments" do
+ @io.set_encoding("utf-8", "utf-16be")
+ @io.external_encoding.should == Encoding::UTF_8
+ @io.internal_encoding.should == Encoding::UTF_16BE
+ end
+
+ it "calls #to_str to convert an abject to a String" do
+ obj = mock("io_set_encoding")
+ obj.should_receive(:to_str).and_return("utf-8:utf-16be")
+ @io.set_encoding(obj)
+ @io.external_encoding.should == Encoding::UTF_8
+ @io.internal_encoding.should == Encoding::UTF_16BE
+ end
+
+ it "calls #to_str to convert the second argument to a String" do
+ obj = mock("io_set_encoding")
+ obj.should_receive(:to_str).at_least(1).times.and_return("utf-16be")
+ @io.set_encoding(Encoding::UTF_8, obj)
+ @io.external_encoding.should == Encoding::UTF_8
+ @io.internal_encoding.should == Encoding::UTF_16BE
+ end
+ end
+end
diff --git a/spec/ruby/core/io/shared/binwrite.rb b/spec/ruby/core/io/shared/binwrite.rb
new file mode 100644
index 0000000000..1a88442a3b
--- /dev/null
+++ b/spec/ruby/core/io/shared/binwrite.rb
@@ -0,0 +1,78 @@
+require_relative '../fixtures/classes'
+
+describe :io_binwrite, shared: true do
+ before :each do
+ @filename = tmp("IO_binwrite_file") + $$.to_s
+ File.open(@filename, "w") do |file|
+ file << "012345678901234567890123456789"
+ end
+ end
+
+ after :each do
+ rm_r @filename
+ end
+
+ it "coerces the argument to a string using to_s" do
+ (obj = mock('test')).should_receive(:to_s).and_return('a string')
+ IO.send(@method, @filename, obj)
+ end
+
+ it "returns the number of bytes written" do
+ IO.send(@method, @filename, "abcde").should == 5
+ end
+
+ it "creates a file if missing" do
+ fn = @filename + "xxx"
+ begin
+ File.exist?(fn).should be_false
+ IO.send(@method, fn, "test")
+ File.exist?(fn).should be_true
+ ensure
+ rm_r fn
+ end
+ end
+
+ it "creates file if missing even if offset given" do
+ fn = @filename + "xxx"
+ begin
+ File.exist?(fn).should be_false
+ IO.send(@method, fn, "test", 0)
+ File.exist?(fn).should be_true
+ ensure
+ rm_r fn
+ end
+ end
+
+ it "truncates the file and writes the given string" do
+ IO.send(@method, @filename, "hello, world!")
+ File.read(@filename).should == "hello, world!"
+ end
+
+ it "doesn't truncate the file and writes the given string if an offset is given" do
+ IO.send(@method, @filename, "hello, world!", 0)
+ File.read(@filename).should == "hello, world!34567890123456789"
+ IO.send(@method, @filename, "hello, world!", 20)
+ File.read(@filename).should == "hello, world!3456789hello, world!"
+ end
+
+ it "doesn't truncate and writes at the given offset after passing empty opts" do
+ IO.send(@method, @filename, "hello world!", 1, {})
+ File.read(@filename).should == "0hello world!34567890123456789"
+ end
+
+ it "accepts a :mode option" do
+ IO.send(@method, @filename, "hello, world!", mode: 'a')
+ File.read(@filename).should == "012345678901234567890123456789hello, world!"
+ IO.send(@method, @filename, "foo", 2, mode: 'w')
+ File.read(@filename).should == "\0\0foo"
+ end
+
+ it "raises an error if readonly mode is specified" do
+ lambda { IO.send(@method, @filename, "abcde", mode: "r") }.should raise_error(IOError)
+ end
+
+ it "truncates if empty :opts provided and offset skipped" do
+ IO.send(@method, @filename, "hello, world!", {})
+ File.read(@filename).should == "hello, world!"
+ end
+end
diff --git a/spec/ruby/core/io/shared/chars.rb b/spec/ruby/core/io/shared/chars.rb
new file mode 100644
index 0000000000..7f2edd2b6d
--- /dev/null
+++ b/spec/ruby/core/io/shared/chars.rb
@@ -0,0 +1,73 @@
+# -*- encoding: utf-8 -*-
+describe :io_chars, shared: true do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ ScratchPad.record []
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "yields each character" do
+ @io.readline.should == "Voici la ligne une.\n"
+
+ count = 0
+ @io.send(@method) do |c|
+ ScratchPad << c
+ break if 4 < count += 1
+ end
+
+ ScratchPad.recorded.should == ["Q", "u", "i", " ", "è"]
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ enum = @io.send(@method)
+ enum.should be_an_instance_of(Enumerator)
+ enum.first(5).should == ["V", "o", "i", "c", "i"]
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ @io.send(@method).size.should == nil
+ end
+ end
+ end
+ end
+
+ it "returns itself" do
+ @io.send(@method) { |c| }.should equal(@io)
+ end
+
+ it "returns an enumerator for a closed stream" do
+ IOSpecs.closed_io.send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ it "raises an IOError when an enumerator created on a closed stream is accessed" do
+ lambda { IOSpecs.closed_io.send(@method).first }.should raise_error(IOError)
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.send(@method) {} }.should raise_error(IOError)
+ end
+end
+
+describe :io_chars_empty, shared: true do
+ before :each do
+ @name = tmp("io_each_char")
+ @io = new_io @name, "w+:utf-8"
+ ScratchPad.record []
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @name
+ end
+
+ it "does not yield any characters on an empty stream" do
+ @io.send(@method) { |c| ScratchPad << c }
+ ScratchPad.recorded.should == []
+ end
+end
diff --git a/spec/ruby/core/io/shared/codepoints.rb b/spec/ruby/core/io/shared/codepoints.rb
new file mode 100644
index 0000000000..a5062e7f79
--- /dev/null
+++ b/spec/ruby/core/io/shared/codepoints.rb
@@ -0,0 +1,54 @@
+# -*- encoding: utf-8 -*-
+require_relative '../fixtures/classes'
+
+describe :io_codepoints, shared: true do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ @enum = @io.send(@method)
+ end
+
+ after :each do
+ @io.close
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ @enum.should be_an_instance_of(Enumerator)
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ @enum.size.should == nil
+ end
+ end
+ end
+ end
+
+ it "yields each codepoint" do
+ @enum.first(25).should == [
+ 86, 111, 105, 99, 105, 32, 108, 97, 32, 108, 105, 103, 110,
+ 101, 32, 117, 110, 101, 46, 10, 81, 117, 105, 32, 232
+ ]
+ end
+
+ it "yields each codepoint starting from the current position" do
+ @io.pos = 130
+ @enum.to_a.should == [101, 32, 115, 105, 120, 46, 10]
+ end
+
+ it "raises an error if reading invalid sequence" do
+ @io.pos = 60 # inside of a multibyte sequence
+ lambda { @enum.first }.should raise_error(ArgumentError)
+ end
+
+ it "does not change $_" do
+ $_ = "test"
+ @enum.to_a
+ $_.should == "test"
+ end
+
+ it "raises an IOError when self is not readable" do
+ lambda { IOSpecs.closed_io.send(@method).to_a }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/shared/each.rb b/spec/ruby/core/io/shared/each.rb
new file mode 100644
index 0000000000..ac01a49df1
--- /dev/null
+++ b/spec/ruby/core/io/shared/each.rb
@@ -0,0 +1,185 @@
+# -*- encoding: utf-8 -*-
+require_relative '../fixtures/classes'
+
+describe :io_each, shared: true do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ ScratchPad.record []
+ end
+
+ after :each do
+ @io.close if @io
+ end
+
+ describe "with no separator" do
+ it "yields each line to the passed block" do
+ @io.send(@method) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.lines
+ end
+
+ it "yields each line starting from the current position" do
+ @io.pos = 41
+ @io.send(@method) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.lines[2..-1]
+ end
+
+ it "returns self" do
+ @io.send(@method) { |l| l }.should equal(@io)
+ end
+
+ it "does not change $_" do
+ $_ = "test"
+ @io.send(@method) { |s| s }
+ $_.should == "test"
+ end
+
+ it "returns self" do
+ @io.send(@method) { |l| l }.should equal(@io)
+ end
+
+ it "raises an IOError when self is not readable" do
+ lambda { IOSpecs.closed_io.send(@method) {} }.should raise_error(IOError)
+ end
+
+ it "makes line count accessible via lineno" do
+ @io.send(@method) { ScratchPad << @io.lineno }
+ ScratchPad.recorded.should == [ 1,2,3,4,5,6,7,8,9 ]
+ end
+
+ it "makes line count accessible via $." do
+ @io.send(@method) { ScratchPad << $. }
+ ScratchPad.recorded.should == [ 1,2,3,4,5,6,7,8,9 ]
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ enum = @io.send(@method)
+ enum.should be_an_instance_of(Enumerator)
+
+ enum.each { |l| ScratchPad << l }
+ ScratchPad.recorded.should == IOSpecs.lines
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ @io.send(@method).size.should == nil
+ end
+ end
+ end
+ end
+ end
+
+ describe "with limit" do
+ describe "when limit is 0" do
+ it "raises an ArgumentError" do
+ # must pass block so Enumerator is evaluated and raises
+ lambda { @io.send(@method, 0){} }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe "when passed a String containing one space as a separator" do
+ it "uses the passed argument as the line separator" do
+ @io.send(@method, " ") { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.lines_space_separator
+ end
+
+ it "does not change $_" do
+ $_ = "test"
+ @io.send(@method, " ") { |s| }
+ $_.should == "test"
+ end
+
+ it "tries to convert the passed separator to a String using #to_str" do
+ obj = mock("to_str")
+ obj.stub!(:to_str).and_return(" ")
+
+ @io.send(@method, obj) { |l| ScratchPad << l }
+ ScratchPad.recorded.should == IOSpecs.lines_space_separator
+ end
+ end
+
+ describe "when passed nil as a separator" do
+ it "yields self's content starting from the current position when the passed separator is nil" do
+ @io.pos = 100
+ @io.send(@method, nil) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six.\n"]
+ end
+ end
+
+ describe "when passed an empty String as a separator" do
+ it "yields each paragraph" do
+ @io.send(@method, "") { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.paragraphs
+ end
+ end
+
+ describe "with both separator and limit" do
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ enum = @io.send(@method, nil, 1024)
+ enum.should be_an_instance_of(Enumerator)
+
+ enum.each { |l| ScratchPad << l }
+ ScratchPad.recorded.should == [IOSpecs.lines.join]
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ @io.send(@method, nil, 1024).size.should == nil
+ end
+ end
+ end
+ end
+
+ describe "when a block is given" do
+ it "accepts an empty block" do
+ @io.send(@method, nil, 1024) {}.should equal(@io)
+ end
+
+ describe "when passed nil as a separator" do
+ it "yields self's content starting from the current position when the passed separator is nil" do
+ @io.pos = 100
+ @io.send(@method, nil, 1024) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six.\n"]
+ end
+ end
+
+ describe "when passed an empty String as a separator" do
+ it "yields each paragraph" do
+ @io.send(@method, "", 1024) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.paragraphs
+ end
+ end
+ end
+ end
+
+ ruby_version_is "2.4" do
+ describe "when passed chomp" do
+ it "yields each line without trailing newline characters to the passed block" do
+ @io.send(@method, chomp: true) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.lines_without_newline_characters
+ end
+ end
+ end
+end
+
+describe :io_each_default_separator, shared: true do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ ScratchPad.record []
+ @sep, $/ = $/, " "
+ end
+
+ after :each do
+ @io.close if @io
+ $/ = @sep
+ end
+
+ it "uses $/ as the default line separator" do
+ @io.send(@method) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.lines_space_separator
+ end
+end
diff --git a/spec/ruby/core/io/shared/gets_ascii.rb b/spec/ruby/core/io/shared/gets_ascii.rb
new file mode 100644
index 0000000000..2a8fe3c9a5
--- /dev/null
+++ b/spec/ruby/core/io/shared/gets_ascii.rb
@@ -0,0 +1,19 @@
+# -*- encoding: binary -*-
+describe :io_gets_ascii, shared: true do
+ describe "with ASCII separator" do
+ before :each do
+ @name = tmp("gets_specs.txt")
+ touch(@name, "wb") { |f| f.print "this is a test\xFFtesty\ntestier" }
+
+ File.open(@name, "rb") { |f| @data = f.send(@method, "\xFF") }
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "returns the separator's character representation" do
+ @data.should == "this is a test\xFF"
+ end
+ end
+end
diff --git a/spec/ruby/core/io/shared/new.rb b/spec/ruby/core/io/shared/new.rb
new file mode 100644
index 0000000000..ea22f40090
--- /dev/null
+++ b/spec/ruby/core/io/shared/new.rb
@@ -0,0 +1,378 @@
+require_relative '../fixtures/classes'
+
+# 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
+ before :each do
+ @name = tmp("io_new.txt")
+ @fd = new_fd @name
+ @io = nil
+ end
+
+ after :each do
+ if @io
+ @io.close
+ elsif @fd
+ IO.new(@fd, "w").close
+ end
+ rm_r @name
+ end
+
+ it "creates an IO instance from a Fixnum argument" do
+ @io = IO.send(@method, @fd, "w")
+ @io.should be_an_instance_of(IO)
+ end
+
+ it "creates an IO instance when STDOUT is closed" do
+ verbose, $VERBOSE = $VERBOSE, nil
+ stdout = STDOUT
+ stdout_file = tmp("stdout.txt")
+
+ begin
+ @io = IO.send(@method, @fd, "w")
+ @io.should be_an_instance_of(IO)
+ ensure
+ STDOUT = stdout
+ $VERBOSE = verbose
+ rm_r stdout_file
+ end
+ end
+
+ it "creates an IO instance when STDERR is closed" do
+ verbose, $VERBOSE = $VERBOSE, nil
+ stderr = STDERR
+ stderr_file = tmp("stderr.txt")
+ STDERR = new_io stderr_file
+ STDERR.close
+
+ begin
+ @io = IO.send(@method, @fd, "w")
+ @io.should be_an_instance_of(IO)
+ ensure
+ STDERR = stderr
+ $VERBOSE = verbose
+ rm_r stderr_file
+ end
+ end
+
+ it "calls #to_int on an object to convert to a Fixnum" do
+ obj = mock("file descriptor")
+ obj.should_receive(:to_int).and_return(@fd)
+ @io = IO.send(@method, obj, "w")
+ @io.should be_an_instance_of(IO)
+ end
+
+ it "accepts a :mode option" do
+ @io = IO.send(@method, @fd, mode: "w")
+ @io.write("foo").should == 3
+ end
+
+ it "accepts a mode argument set to nil with a valid :mode option" do
+ @io = IO.send(@method, @fd, nil, mode: "w")
+ @io.write("foo").should == 3
+ end
+
+ it "accepts a mode argument with a :mode option set to nil" do
+ @io = IO.send(@method, @fd, "w", mode: nil)
+ @io.write("foo").should == 3
+ end
+
+ it "uses the external encoding specified in the mode argument" do
+ @io = IO.send(@method, @fd, 'w:utf-8')
+ @io.external_encoding.to_s.should == 'UTF-8'
+ end
+
+ it "uses the external and the internal encoding specified in the mode argument" do
+ @io = IO.send(@method, @fd, 'w:utf-8:ISO-8859-1')
+ @io.external_encoding.to_s.should == 'UTF-8'
+ @io.internal_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "uses the external encoding specified via the :external_encoding option" do
+ @io = IO.send(@method, @fd, 'w', {external_encoding: 'utf-8'})
+ @io.external_encoding.to_s.should == 'UTF-8'
+ end
+
+ it "uses the internal encoding specified via the :internal_encoding option" do
+ @io = IO.send(@method, @fd, 'w', {internal_encoding: 'ibm866'})
+ @io.internal_encoding.to_s.should == 'IBM866'
+ end
+
+ it "uses the colon-separated encodings specified via the :encoding option" do
+ @io = IO.send(@method, @fd, 'w', {encoding: 'utf-8:ISO-8859-1'})
+ @io.external_encoding.to_s.should == 'UTF-8'
+ @io.internal_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "uses the :encoding option as the external encoding when only one is given" do
+ @io = IO.send(@method, @fd, 'w', {encoding: 'ISO-8859-1'})
+ @io.external_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "uses the :encoding options as the external encoding when it's an Encoding object" do
+ @io = IO.send(@method, @fd, 'w', {encoding: Encoding::ISO_8859_1})
+ @io.external_encoding.should == Encoding::ISO_8859_1
+ end
+
+ it "ignores the :encoding option when the :external_encoding option is present" do
+ lambda {
+ @io = IO.send(@method, @fd, 'w', {external_encoding: 'utf-8', encoding: 'iso-8859-1:iso-8859-1'})
+ }.should complain(/Ignoring encoding parameter/)
+ @io.external_encoding.to_s.should == 'UTF-8'
+ end
+
+ it "ignores the :encoding option when the :internal_encoding option is present" do
+ lambda {
+ @io = IO.send(@method, @fd, 'w', {internal_encoding: 'ibm866', encoding: 'iso-8859-1:iso-8859-1'})
+ }.should complain(/Ignoring encoding parameter/)
+ @io.internal_encoding.to_s.should == 'IBM866'
+ end
+
+ it "uses the encoding specified via the :mode option hash" do
+ @io = IO.send(@method, @fd, {mode: 'w:utf-8:ISO-8859-1'})
+ @io.external_encoding.to_s.should == 'UTF-8'
+ @io.internal_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "ignores the :internal_encoding option when the same as the external encoding" do
+ @io = IO.send(@method, @fd, 'w', {external_encoding: 'utf-8', internal_encoding: 'utf-8'})
+ @io.external_encoding.to_s.should == 'UTF-8'
+ @io.internal_encoding.to_s.should == ''
+ end
+
+ it "sets internal encoding to nil when passed '-'" do
+ @io = IO.send(@method, @fd, 'w', {external_encoding: 'utf-8', internal_encoding: '-'})
+ @io.external_encoding.to_s.should == 'UTF-8'
+ @io.internal_encoding.to_s.should == ''
+ end
+
+ it "sets binmode from mode string" do
+ @io = IO.send(@method, @fd, 'wb')
+ @io.binmode?.should == true
+ end
+
+ it "does not set binmode without being asked" do
+ @io = IO.send(@method, @fd, 'w')
+ @io.binmode?.should == false
+ end
+
+ it "sets binmode from :binmode option" do
+ @io = IO.send(@method, @fd, 'w', {binmode: true})
+ @io.binmode?.should == true
+ end
+
+ it "does not set binmode from false :binmode" do
+ @io = IO.send(@method, @fd, 'w', {binmode: false})
+ @io.binmode?.should == false
+ end
+
+ it "sets external encoding to binary with binmode in mode string" do
+ @io = IO.send(@method, @fd, 'wb')
+ @io.external_encoding.to_s.should == 'ASCII-8BIT'
+ end
+
+ # #5917
+ it "sets external encoding to binary with :binmode option" do
+ @io = IO.send(@method, @fd, 'w', {binmode: true})
+ @io.external_encoding.to_s.should == 'ASCII-8BIT'
+ end
+
+ it "does not use binary encoding when mode encoding is specified" do
+ @io = IO.send(@method, @fd, 'wb:iso-8859-1')
+ @io.external_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "does not use binary encoding when :encoding option is specified" do
+ @io = IO.send(@method, @fd, 'wb', encoding: "iso-8859-1")
+ @io.external_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "does not use binary encoding when :external_encoding option is specified" do
+ @io = IO.send(@method, @fd, 'wb', external_encoding: "iso-8859-1")
+ @io.external_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "does not use binary encoding when :internal_encoding option is specified" do
+ @io = IO.send(@method, @fd, 'wb', internal_encoding: "ibm866")
+ @io.internal_encoding.to_s.should == 'IBM866'
+ end
+
+ it "accepts nil options" do
+ @io = IO.send(@method, @fd, 'w', nil)
+ @io.write("foo").should == 3
+ end
+
+ it "coerces mode with #to_str" do
+ mode = mock("mode")
+ mode.should_receive(:to_str).and_return('w')
+ @io = IO.send(@method, @fd, mode)
+ end
+
+ it "coerces mode with #to_int" do
+ mode = mock("mode")
+ mode.should_receive(:to_int).and_return(File::WRONLY)
+ @io = IO.send(@method, @fd, mode)
+ end
+
+ it "coerces mode with #to_str when passed in options" do
+ mode = mock("mode")
+ mode.should_receive(:to_str).and_return('w')
+ @io = IO.send(@method, @fd, mode: mode)
+ end
+
+ it "coerces mode with #to_int when passed in options" do
+ mode = mock("mode")
+ mode.should_receive(:to_int).and_return(File::WRONLY)
+ @io = IO.send(@method, @fd, mode: mode)
+ end
+
+ it "coerces :encoding option with #to_str" do
+ encoding = mock("encoding")
+ encoding.should_receive(:to_str).and_return('utf-8')
+ @io = IO.send(@method, @fd, 'w', encoding: encoding)
+ end
+
+ it "coerces :external_encoding option with #to_str" do
+ encoding = mock("encoding")
+ encoding.should_receive(:to_str).and_return('utf-8')
+ @io = IO.send(@method, @fd, 'w', external_encoding: encoding)
+ end
+
+ it "coerces :internal_encoding option with #to_str" do
+ encoding = mock("encoding")
+ encoding.should_receive(:to_str).at_least(:once).and_return('utf-8')
+ @io = IO.send(@method, @fd, 'w', internal_encoding: encoding)
+ end
+
+ it "coerces options as third argument with #to_hash" do
+ options = mock("options")
+ options.should_receive(:to_hash).and_return({})
+ @io = IO.send(@method, @fd, 'w', options)
+ end
+
+ it "coerces options as second argument with #to_hash" do
+ options = mock("options")
+ options.should_receive(:to_hash).and_return({})
+ @io = IO.send(@method, @fd, options)
+ end
+
+ it "accepts an :autoclose option" do
+ @io = IO.send(@method, @fd, 'w', autoclose: false)
+ @io.autoclose?.should == false
+ @io.autoclose = true
+ end
+
+ it "accepts any truthy option :autoclose" do
+ @io = IO.send(@method, @fd, 'w', autoclose: 42)
+ @io.autoclose?.should == true
+ end
+end
+
+# This group of specs may ONLY contain specs that do not actually create
+# an IO instance from the file descriptor returned by #new_fd helper.
+describe :io_new_errors, shared: true do
+ before :each do
+ @name = tmp("io_new.txt")
+ @fd = new_fd @name
+ end
+
+ after :each do
+ IO.new(@fd, "w").close if @fd
+ rm_r @name
+ end
+
+ it "raises an Errno::EBADF if the file descriptor is not valid" do
+ lambda { IO.send(@method, -1, "w") }.should raise_error(Errno::EBADF)
+ end
+
+ it "raises an IOError if passed a closed stream" do
+ lambda { IO.send(@method, IOSpecs.closed_io.fileno, 'w') }.should raise_error(IOError)
+ end
+
+ platform_is_not :windows do
+ it "raises an Errno::EINVAL if the new mode is not compatible with the descriptor's current mode" do
+ lambda { IO.send(@method, @fd, "r") }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ it "raises ArgumentError if passed an empty mode string" do
+ lambda { IO.send(@method, @fd, "") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error if passed modes two ways" do
+ lambda {
+ IO.send(@method, @fd, "w", mode: "w")
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error if passed encodings two ways" do
+ lambda {
+ @io = IO.send(@method, @fd, 'w:ISO-8859-1', {encoding: 'ISO-8859-1'})
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, 'w:ISO-8859-1', {external_encoding: 'ISO-8859-1'})
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, '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
+ lambda {
+ @io = IO.send(@method, @fd, "wb", binmode: true)
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, "wt", textmode: true)
+ }.should raise_error(ArgumentError)
+
+ lambda {
+ @io = IO.send(@method, @fd, "wb", textmode: false)
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, "wt", binmode: false)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error if passed conflicting binary/text mode two ways" do
+ lambda {
+ @io = IO.send(@method, @fd, "wb", binmode: false)
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, "wt", textmode: false)
+ }.should raise_error(ArgumentError)
+
+ lambda {
+ @io = IO.send(@method, @fd, "wb", textmode: true)
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, "wt", binmode: true)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error when trying to set both binmode and textmode" do
+ lambda {
+ @io = IO.send(@method, @fd, "w", textmode: true, binmode: true)
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, File::Constants::WRONLY, textmode: true, binmode: true)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if not passed a hash or nil for options" do
+ lambda {
+ @io = IO.send(@method, @fd, 'w', false)
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, false, false)
+ }.should raise_error(ArgumentError)
+ lambda {
+ @io = IO.send(@method, @fd, nil, false)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises TypeError if passed a hash for mode and nil for options" do
+ lambda {
+ @io = IO.send(@method, @fd, {mode: 'w'}, nil)
+ }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/io/shared/pos.rb b/spec/ruby/core/io/shared/pos.rb
new file mode 100644
index 0000000000..fef7ab2bf7
--- /dev/null
+++ b/spec/ruby/core/io/shared/pos.rb
@@ -0,0 +1,72 @@
+describe :io_pos, shared: true do
+ before :each do
+ @fname = tmp('test.txt')
+ File.open(@fname, 'w') { |f| f.write "123" }
+ end
+
+ after :each do
+ rm_r @fname
+ end
+
+ it "gets the offset" do
+ File.open @fname do |f|
+ f.send(@method).should == 0
+ f.read 1
+ f.send(@method).should == 1
+ f.read 2
+ f.send(@method).should == 3
+ end
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.send(@method) }.should raise_error(IOError)
+ end
+
+ it "resets #eof?" do
+ open @fname do |io|
+ io.read 1
+ io.read 1
+ io.send(@method)
+ io.eof?.should == false
+ end
+ end
+end
+
+describe :io_set_pos, shared: true do
+ before :each do
+ @fname = tmp('test.txt')
+ File.open(@fname, 'w') { |f| f.write "123" }
+ end
+
+ after :each do
+ rm_r @fname
+ end
+
+ it "sets the offset" do
+ File.open @fname do |f|
+ val1 = f.read 1
+ f.send @method, 0
+ f.read(1).should == val1
+ end
+ end
+
+ it "converts arguments to Integers" do
+ File.open @fname do |io|
+ o = mock("o")
+ o.should_receive(:to_int).and_return(1)
+
+ io.send @method, o
+ io.pos.should == 1
+ end
+ end
+
+ it "does not accept Bignums that don't fit in a C long" do
+ File.open @fname do |io|
+ lambda { io.send @method, 2**128 }.should raise_error(RangeError)
+ end
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.send @method, 0 }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/shared/readlines.rb b/spec/ruby/core/io/shared/readlines.rb
new file mode 100644
index 0000000000..f545d8876a
--- /dev/null
+++ b/spec/ruby/core/io/shared/readlines.rb
@@ -0,0 +1,211 @@
+describe :io_readlines, shared: true do
+ it "raises TypeError if the first parameter is nil" do
+ lambda { IO.send(@method, nil, &@object) }.should raise_error(TypeError)
+ end
+
+ it "raises an Errno::ENOENT if the file does not exist" do
+ name = tmp("nonexistent.txt")
+ lambda { IO.send(@method, name, &@object) }.should raise_error(Errno::ENOENT)
+ end
+
+ it "yields a single string with entire content when the separator is nil" do
+ result = IO.send(@method, @name, nil, &@object)
+ (result ? result : ScratchPad.recorded).should == [IO.read(@name)]
+ end
+
+ it "yields a sequence of paragraphs when the separator is an empty string" do
+ result = IO.send(@method, @name, "", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_empty_separator
+ end
+
+ ruby_version_is "2.4" do
+ it "yields a sequence of lines without trailing newline characters when chomp is passed" do
+ result = IO.send(@method, @name, chomp: true, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_without_newline_characters
+ end
+ end
+end
+
+describe :io_readlines_options_19, shared: true do
+ before :each do
+ @filename = tmp("io readlines options")
+ end
+
+ after :each do
+ rm_r @filename
+ end
+
+ describe "when passed name" do
+ it "calls #to_path to convert the name" do
+ name = mock("io name to_path")
+ name.should_receive(:to_path).and_return(@name)
+ IO.send(@method, name, &@object)
+ end
+
+ it "defaults to $/ as the separator" do
+ result = IO.send(@method, @name, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines
+ end
+ end
+
+ describe "when passed name, object" do
+ it "calls #to_str to convert the object to a separator" do
+ sep = mock("io readlines separator")
+ sep.should_receive(:to_str).at_least(1).and_return(" ")
+ result = IO.send(@method, @name, sep, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator
+ end
+
+ describe "when the object is a Fixnum" do
+ before :each do
+ @sep = $/
+ end
+
+ after :each do
+ $/ = @sep
+ end
+
+ it "defaults to $/ as the separator" do
+ $/ = " "
+ result = IO.send(@method, @name, 10, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "uses the object as a limit if it is a Fixnum" do
+ result = IO.send(@method, @name, 10, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_limit
+ end
+ end
+
+ describe "when the object is a String" do
+ it "uses the value as the separator" do
+ result = IO.send(@method, @name, " ", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator
+ end
+
+ it "accepts non-ASCII data as separator" do
+ result = IO.send(@method, @name, "\303\250".force_encoding("utf-8"), &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_arbitrary_separator
+ end
+ end
+
+ describe "when the object is a Hash" do
+ it "uses the value as the options hash" do
+ result = IO.send(@method, @name, mode: "r", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines
+ end
+ end
+ end
+
+ describe "when passed name, object, object" do
+ describe "when the first object is a Fixnum" do
+ it "uses the second object as an options Hash" do
+ lambda do
+ IO.send(@method, @filename, 10, mode: "w", &@object)
+ end.should raise_error(IOError)
+ end
+
+ it "calls #to_hash to convert the second object to a Hash" do
+ options = mock("io readlines options Hash")
+ options.should_receive(:to_hash).and_return({ mode: "w" })
+ lambda do
+ IO.send(@method, @filename, 10, options, &@object)
+ end.should raise_error(IOError)
+ end
+ end
+
+ describe "when the first object is a String" do
+ it "uses the second object as a limit if it is a Fixnum" do
+ result = IO.send(@method, @name, " ", 10, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "calls #to_int to convert the second object" do
+ limit = mock("io readlines limit")
+ limit.should_receive(:to_int).at_least(1).and_return(10)
+ result = IO.send(@method, @name, " ", limit, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "uses the second object as an options Hash" do
+ lambda do
+ IO.send(@method, @filename, " ", mode: "w", &@object)
+ end.should raise_error(IOError)
+ end
+
+ it "calls #to_hash to convert the second object to a Hash" do
+ options = mock("io readlines options Hash")
+ options.should_receive(:to_hash).and_return({ mode: "w" })
+ lambda do
+ IO.send(@method, @filename, " ", options, &@object)
+ end.should raise_error(IOError)
+ end
+ end
+
+ describe "when the first object is not a String or Fixnum" do
+ it "calls #to_str to convert the object to a String" do
+ sep = mock("io readlines separator")
+ sep.should_receive(:to_str).at_least(1).and_return(" ")
+ result = IO.send(@method, @name, sep, 10, mode: "r", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "uses the second object as a limit if it is a Fixnum" do
+ result = IO.send(@method, @name, " ", 10, mode: "r", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "calls #to_int to convert the second object" do
+ limit = mock("io readlines limit")
+ limit.should_receive(:to_int).at_least(1).and_return(10)
+ result = IO.send(@method, @name, " ", limit, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "uses the second object as an options Hash" do
+ lambda do
+ IO.send(@method, @filename, " ", mode: "w", &@object)
+ end.should raise_error(IOError)
+ end
+
+ it "calls #to_hash to convert the second object to a Hash" do
+ options = mock("io readlines options Hash")
+ options.should_receive(:to_hash).and_return({ mode: "w" })
+ lambda do
+ IO.send(@method, @filename, " ", options, &@object)
+ end.should raise_error(IOError)
+ end
+ end
+ end
+
+ describe "when passed name, separator, limit, options" do
+ it "calls #to_path to convert the name object" do
+ name = mock("io name to_path")
+ name.should_receive(:to_path).and_return(@name)
+ result = IO.send(@method, name, " ", 10, mode: "r", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "calls #to_str to convert the separator object" do
+ sep = mock("io readlines separator")
+ sep.should_receive(:to_str).at_least(1).and_return(" ")
+ result = IO.send(@method, @name, sep, 10, mode: "r", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "calls #to_int to convert the limit argument" do
+ limit = mock("io readlines limit")
+ limit.should_receive(:to_int).at_least(1).and_return(10)
+ result = IO.send(@method, @name, " ", limit, mode: "r", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
+ end
+
+ it "calls #to_hash to convert the options object" do
+ options = mock("io readlines options Hash")
+ options.should_receive(:to_hash).and_return({ mode: "w" })
+ lambda do
+ IO.send(@method, @filename, " ", 10, options, &@object)
+ end.should raise_error(IOError)
+ end
+ end
+end
diff --git a/spec/ruby/core/io/shared/tty.rb b/spec/ruby/core/io/shared/tty.rb
new file mode 100644
index 0000000000..947b887f81
--- /dev/null
+++ b/spec/ruby/core/io/shared/tty.rb
@@ -0,0 +1,25 @@
+require_relative '../fixtures/classes'
+
+describe :io_tty, shared: true do
+ platform_is_not :windows do
+ it "returns true if this stream is a terminal device (TTY)" do
+ begin
+ # check to enabled tty
+ File.open('/dev/tty') {}
+ rescue Errno::ENXIO
+ # workaround for not configured environment like OS X
+ 1.should == 1
+ else
+ File.open('/dev/tty') { |f| f.send(@method) }.should == true
+ end
+ end
+ end
+
+ it "returns false if this stream is not a terminal device (TTY)" do
+ File.open(__FILE__) { |f| f.send(@method) }.should == false
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.send @method }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/shared/write.rb b/spec/ruby/core/io/shared/write.rb
new file mode 100644
index 0000000000..140eeb04ab
--- /dev/null
+++ b/spec/ruby/core/io/shared/write.rb
@@ -0,0 +1,93 @@
+# encoding: utf-8
+require_relative '../fixtures/classes'
+
+describe :io_write, shared: true do
+ before :each do
+ @filename = tmp("IO_syswrite_file") + $$.to_s
+ File.open(@filename, "w") do |file|
+ file.send(@method, "012345678901234567890123456789")
+ end
+ @file = File.open(@filename, "r+")
+ @readonly_file = File.open(@filename)
+ end
+
+ after :each do
+ @readonly_file.close if @readonly_file
+ @file.close if @file
+ rm_r @filename
+ end
+
+ it "coerces the argument to a string using to_s" do
+ (obj = mock('test')).should_receive(:to_s).and_return('a string')
+ @file.send(@method, obj)
+ end
+
+ it "checks if the file is writable if writing more than zero bytes" do
+ lambda { @readonly_file.send(@method, "abcde") }.should raise_error(IOError)
+ end
+
+ it "returns the number of bytes written" do
+ written = @file.send(@method, "abcde")
+ written.should == 5
+ end
+
+ it "invokes to_s on non-String argument" do
+ data = "abcdefgh9876"
+ (obj = mock(data)).should_receive(:to_s).and_return(data)
+ @file.send(@method, obj)
+ @file.seek(0)
+ @file.read(data.size).should == data
+ end
+
+ it "writes all of the string's bytes without buffering if mode is sync" do
+ @file.sync = true
+ written = @file.send(@method, "abcde")
+ written.should == 5
+ File.open(@filename) do |file|
+ file.read(10).should == "abcde56789"
+ end
+ end
+
+ it "does not warn if called after IO#read" do
+ @file.read(5)
+ lambda { @file.send(@method, "fghij") }.should_not complain
+ end
+
+ it "writes to the current position after IO#read" do
+ @file.read(5)
+ @file.send(@method, "abcd")
+ @file.rewind
+ @file.read.should == "01234abcd901234567890123456789"
+ end
+
+ it "advances the file position by the count of given bytes" do
+ @file.send(@method, "abcde")
+ @file.read(10).should == "5678901234"
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.send(@method, "hello") }.should raise_error(IOError)
+ end
+
+ describe "on a pipe" do
+ before :each do
+ @r, @w = IO.pipe
+ end
+
+ after :each do
+ @r.close
+ @w.close
+ end
+
+ it "writes the given String to the pipe" do
+ @w.send(@method, "foo")
+ @w.close
+ @r.read.should == "foo"
+ end
+
+ it "raises Errno::EPIPE if the read end is closed and does not die from SIGPIPE" do
+ @r.close
+ -> { @w.send(@method, "foo") }.should raise_error(Errno::EPIPE, /Broken pipe/)
+ end
+ end
+end
diff --git a/spec/ruby/core/io/stat_spec.rb b/spec/ruby/core/io/stat_spec.rb
new file mode 100644
index 0000000000..d46d4105ca
--- /dev/null
+++ b/spec/ruby/core/io/stat_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#stat" do
+ before :each do
+ @io = IO.popen 'cat', "r+"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.stat }.should raise_error(IOError)
+ end
+
+ it "returns a File::Stat object for the stream" do
+ STDOUT.stat.should be_an_instance_of(File::Stat)
+ end
+
+ it "can stat pipes" do
+ @io.stat.should be_an_instance_of(File::Stat)
+ end
+end
diff --git a/spec/ruby/core/io/sync_spec.rb b/spec/ruby/core/io/sync_spec.rb
new file mode 100644
index 0000000000..c8c1c5a57e
--- /dev/null
+++ b/spec/ruby/core/io/sync_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#sync=" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "sets the sync mode to true or false" do
+ @io.sync = true
+ @io.sync.should == true
+ @io.sync = false
+ @io.sync.should == false
+ end
+
+ it "accepts non-boolean arguments" do
+ @io.sync = 10
+ @io.sync.should == true
+ @io.sync = nil
+ @io.sync.should == false
+ @io.sync = Object.new
+ @io.sync.should == true
+ end
+
+ it "raises an IOError on closed stream" do
+ lambda { IOSpecs.closed_io.sync = true }.should raise_error(IOError)
+ end
+end
+
+describe "IO#sync" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "returns the current sync mode" do
+ @io.sync.should == false
+ end
+
+ it "raises an IOError on closed stream" do
+ lambda { IOSpecs.closed_io.sync }.should raise_error(IOError)
+ end
+end
+
+describe "IO#sync" do
+ it "is false by default for STDIN" do
+ STDIN.sync.should == false
+ end
+
+ it "is false by default for STDOUT" do
+ STDOUT.sync.should == false
+ end
+
+ it "is true by default for STDERR" do
+ STDERR.sync.should == true
+ end
+end
diff --git a/spec/ruby/core/io/sysopen_spec.rb b/spec/ruby/core/io/sysopen_spec.rb
new file mode 100644
index 0000000000..91f70c3ca9
--- /dev/null
+++ b/spec/ruby/core/io/sysopen_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+
+describe "IO.sysopen" do
+ before :each do
+ @filename = tmp("rubinius-spec-io-sysopen-#{$$}.txt")
+ @fd = nil
+ end
+
+ after :each do
+ IO.for_fd(@fd).close if @fd
+ rm_r @filename
+ end
+
+ it "returns the file descriptor for a given path" do
+ @fd = IO.sysopen(@filename, "w")
+ @fd.should be_kind_of(Fixnum)
+ @fd.should_not equal(0)
+ end
+
+ # opening a directory is not supported on Windows
+ platform_is_not :windows do
+ it "works on directories" do
+ @fd = IO.sysopen(tmp("")) # /tmp
+ @fd.should be_kind_of(Fixnum)
+ @fd.should_not equal(0)
+ end
+ end
+
+ it "calls #to_path to convert an object to a path" do
+ path = mock('sysopen to_path')
+ path.should_receive(:to_path).and_return(@filename)
+ @fd = IO.sysopen(path, 'w')
+ end
+
+ it "accepts a mode as second argument" do
+ lambda { @fd = IO.sysopen(@filename, "w") }.should_not raise_error
+ @fd.should_not equal(0)
+ end
+
+ it "accepts permissions as third argument" do
+ @fd = IO.sysopen(@filename, "w", 777)
+ @fd.should_not equal(0)
+ end
+
+ it "accepts mode & permission that are nil" do
+ touch @filename # create the file
+ @fd = IO.sysopen(@filename, nil, nil)
+ @fd.should_not equal(0)
+ end
+end
diff --git a/spec/ruby/core/io/sysread_spec.rb b/spec/ruby/core/io/sysread_spec.rb
new file mode 100644
index 0000000000..4062620367
--- /dev/null
+++ b/spec/ruby/core/io/sysread_spec.rb
@@ -0,0 +1,98 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#sysread on a file" do
+ before :each do
+ @file_name = tmp("IO_sysread_file") + $$.to_s
+ File.open(@file_name, "w") do |f|
+ # write some stuff
+ f.write("012345678901234567890123456789")
+ end
+ @file = File.open(@file_name, "r+")
+ end
+
+ after :each do
+ @file.close
+ rm_r @file_name
+ end
+
+ it "reads the specified number of bytes from the file" do
+ @file.sysread(15).should == "012345678901234"
+ end
+
+ it "reads the specified number of bytes from the file to the buffer" do
+ buf = "" # empty buffer
+ @file.sysread(15, buf).should == buf
+ buf.should == "012345678901234"
+
+ @file.rewind
+
+ buf = "ABCDE" # small buffer
+ @file.sysread(15, buf).should == buf
+ buf.should == "012345678901234"
+
+ @file.rewind
+
+ buf = "ABCDE" * 5 # large buffer
+ @file.sysread(15, buf).should == buf
+ buf.should == "012345678901234"
+ end
+
+ it "coerces the second argument to string and uses it as a buffer" do
+ buf = "ABCDE"
+ (obj = mock("buff")).should_receive(:to_str).any_number_of_times.and_return(buf)
+ @file.sysread(15, obj).should == buf
+ buf.should == "012345678901234"
+ end
+
+ it "advances the position of the file by the specified number of bytes" do
+ @file.sysread(15)
+ @file.sysread(5).should == "56789"
+ end
+
+ it "reads normally even when called immediately after a buffered IO#read" do
+ @file.read(15)
+ @file.sysread(5).should == "56789"
+ end
+
+ it "does not raise error if called after IO#read followed by IO#write" do
+ @file.read(5)
+ @file.write("abcde")
+ lambda { @file.sysread(5) }.should_not raise_error(IOError)
+ end
+
+ it "does not raise error if called after IO#read followed by IO#syswrite" do
+ @file.read(5)
+ @file.syswrite("abcde")
+ lambda { @file.sysread(5) }.should_not raise_error(IOError)
+ end
+
+ it "reads updated content after the flushed buffered IO#write" do
+ @file.write("abcde")
+ @file.flush
+ @file.sysread(5).should == "56789"
+ File.open(@file_name) do |f|
+ f.sysread(10).should == "abcde56789"
+ end
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.sysread(5) }.should raise_error(IOError)
+ end
+end
+
+describe "IO#sysread" do
+ before do
+ @read, @write = IO.pipe
+ end
+
+ after do
+ @read.close
+ @write.close
+ end
+
+ it "returns a smaller string if less than size bytes are available" do
+ @write.syswrite "ab"
+ @read.sysread(3).should == "ab"
+ end
+end
diff --git a/spec/ruby/core/io/sysseek_spec.rb b/spec/ruby/core/io/sysseek_spec.rb
new file mode 100644
index 0000000000..84e0a1a4ac
--- /dev/null
+++ b/spec/ruby/core/io/sysseek_spec.rb
@@ -0,0 +1,44 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/pos'
+
+describe "IO#sysseek" do
+ it_behaves_like :io_set_pos, :seek
+end
+
+describe "IO#sysseek" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "moves the read position relative to the current position with SEEK_CUR" do
+ @io.sysseek(10, IO::SEEK_CUR)
+ @io.readline.should == "igne une.\n"
+ end
+
+ it "raises an error when called after buffered reads" do
+ @io.readline
+ lambda { @io.sysseek(-5, IO::SEEK_CUR) }.should raise_error(IOError)
+ end
+
+ it "moves the read position relative to the start with SEEK_SET" do
+ @io.sysseek(43, IO::SEEK_SET)
+ @io.readline.should == "Aquí está la línea tres.\n"
+ end
+
+ it "moves the read position relative to the end with SEEK_END" do
+ @io.sysseek(1, IO::SEEK_END)
+
+ # this is the safest way of checking the EOF when
+ # sys-* methods are invoked
+ lambda { @io.sysread(1) }.should raise_error(EOFError)
+
+ @io.sysseek(-25, IO::SEEK_END)
+ @io.sysread(7).should == "cinco.\n"
+ end
+end
diff --git a/spec/ruby/core/io/syswrite_spec.rb b/spec/ruby/core/io/syswrite_spec.rb
new file mode 100644
index 0000000000..a4dc8328aa
--- /dev/null
+++ b/spec/ruby/core/io/syswrite_spec.rb
@@ -0,0 +1,71 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/write'
+
+describe "IO#syswrite on a file" do
+ before :each do
+ @filename = tmp("IO_syswrite_file") + $$.to_s
+ File.open(@filename, "w") do |file|
+ file.syswrite("012345678901234567890123456789")
+ end
+ @file = File.open(@filename, "r+")
+ @readonly_file = File.open(@filename)
+ end
+
+ after :each do
+ @file.close
+ @readonly_file.close
+ rm_r @filename
+ end
+
+ it "writes all of the string's bytes but does not buffer them" do
+ written = @file.syswrite("abcde")
+ written.should == 5
+ File.open(@filename) do |file|
+ file.sysread(10).should == "abcde56789"
+ file.seek(0)
+ @file.fsync
+ file.sysread(10).should == "abcde56789"
+ end
+ end
+
+ it "warns if called immediately after a buffered IO#write" do
+ @file.write("abcde")
+ lambda { @file.syswrite("fghij") }.should complain(/syswrite/)
+ end
+
+ it "does not warn if called after IO#write with intervening IO#sysread" do
+ @file.syswrite("abcde")
+ @file.sysread(5)
+ lambda { @file.syswrite("fghij") }.should_not complain
+ end
+
+ it "writes to the actual file position when called after buffered IO#read" do
+ @file.read(5)
+ @file.syswrite("abcde")
+ File.open(@filename) do |file|
+ file.sysread(10).should == "01234abcde"
+ end
+ end
+end
+
+describe "IO#syswrite on a pipe" do
+ it "returns the written bytes if the fd is in nonblock mode and write would block" do
+ require 'io/nonblock'
+ r, w = IO.pipe
+ begin
+ w.nonblock = true
+ larger_than_pipe_capacity = 2 * 1024 * 1024
+ written = w.syswrite("a"*larger_than_pipe_capacity)
+ written.should > 0
+ written.should < larger_than_pipe_capacity
+ ensure
+ w.close
+ r.close
+ end
+ end
+end
+
+describe "IO#syswrite" do
+ it_behaves_like :io_write, :syswrite
+end
diff --git a/spec/ruby/core/io/tell_spec.rb b/spec/ruby/core/io/tell_spec.rb
new file mode 100644
index 0000000000..0d6c6b02d3
--- /dev/null
+++ b/spec/ruby/core/io/tell_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/pos'
+
+describe "IO#tell" do
+ it_behaves_like :io_pos, :tell
+end
diff --git a/spec/ruby/core/io/to_i_spec.rb b/spec/ruby/core/io/to_i_spec.rb
new file mode 100644
index 0000000000..7cd9e170d2
--- /dev/null
+++ b/spec/ruby/core/io/to_i_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#to_i" do
+ it "returns the numeric file descriptor of the given IO object" do
+ $stdout.to_i.should == 1
+ end
+
+ it "raises IOError on closed stream" do
+ lambda { IOSpecs.closed_io.to_i }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/to_io_spec.rb b/spec/ruby/core/io/to_io_spec.rb
new file mode 100644
index 0000000000..55a0977740
--- /dev/null
+++ b/spec/ruby/core/io/to_io_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#to_io" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ end
+
+ it "returns self for open stream" do
+ @io.to_io.should equal(@io)
+ end
+
+ it "returns self for closed stream" do
+ io = IOSpecs.closed_io
+ io.to_io.should equal(io)
+ end
+end
diff --git a/spec/ruby/core/io/try_convert_spec.rb b/spec/ruby/core/io/try_convert_spec.rb
new file mode 100644
index 0000000000..cff56ba618
--- /dev/null
+++ b/spec/ruby/core/io/try_convert_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO.try_convert" do
+ before :each do
+ @name = tmp("io_try_convert.txt")
+ @io = new_io @name
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @name
+ end
+
+ it "returns the passed IO object" do
+ IO.try_convert(@io).should equal(@io)
+ end
+
+ it "does not call #to_io on an IO instance" do
+ @io.should_not_receive(:to_io)
+ IO.try_convert(@io)
+ end
+
+ it "calls #to_io to coerce an object" do
+ obj = mock("io")
+ obj.should_receive(:to_io).and_return(@io)
+ IO.try_convert(obj).should equal(@io)
+ end
+
+ it "returns nil when the passed object does not respond to #to_io" do
+ IO.try_convert(mock("io")).should be_nil
+ end
+
+ it "return nil when BasicObject is passed" do
+ IO.try_convert(BasicObject.new).should be_nil
+ end
+
+ it "raises a TypeError if the object does not return an IO from #to_io" do
+ obj = mock("io")
+ obj.should_receive(:to_io).and_return("io")
+ lambda { IO.try_convert(obj) }.should raise_error(TypeError)
+ end
+
+ it "propagates an exception raised by #to_io" do
+ obj = mock("io")
+ obj.should_receive(:to_io).and_raise(TypeError.new)
+ lambda{ IO.try_convert(obj) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/io/tty_spec.rb b/spec/ruby/core/io/tty_spec.rb
new file mode 100644
index 0000000000..3b76c6d2b8
--- /dev/null
+++ b/spec/ruby/core/io/tty_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/tty'
+
+describe "IO#tty?" do
+ it_behaves_like :io_tty, :tty?
+end
diff --git a/spec/ruby/core/io/ungetbyte_spec.rb b/spec/ruby/core/io/ungetbyte_spec.rb
new file mode 100644
index 0000000000..f5f9a11be1
--- /dev/null
+++ b/spec/ruby/core/io/ungetbyte_spec.rb
@@ -0,0 +1,73 @@
+require_relative '../../spec_helper'
+
+describe "IO#ungetbyte" do
+ before :each do
+ @name = tmp("io_ungetbyte")
+ touch(@name) { |f| f.write "a" }
+ @io = new_io @name, "r"
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @name
+ end
+
+ it "does nothing when passed nil" do
+ @io.ungetbyte(nil).should be_nil
+ @io.getbyte.should == 97
+ end
+
+ it "puts back each byte in a String argument" do
+ @io.ungetbyte("cat").should be_nil
+ @io.getbyte.should == 99
+ @io.getbyte.should == 97
+ @io.getbyte.should == 116
+ @io.getbyte.should == 97
+ end
+
+ it "calls #to_str to convert the argument" do
+ str = mock("io ungetbyte")
+ str.should_receive(:to_str).and_return("dog")
+
+ @io.ungetbyte(str).should be_nil
+ @io.getbyte.should == 100
+ @io.getbyte.should == 111
+ @io.getbyte.should == 103
+ @io.getbyte.should == 97
+ end
+
+ ruby_version_is ''...'2.6' do
+ it "puts back one byte for a Fixnum argument..." do
+ @io.ungetbyte(4095).should be_nil
+ @io.getbyte.should == 255
+ end
+
+ it "... but not for Bignum argument (eh?)" do
+ lambda {
+ @io.ungetbyte(0x4f7574206f6620636861722072616e6765)
+ }.should raise_error(TypeError)
+ end
+ end
+
+ ruby_version_is '2.6'...'2.6.1' do
+ it "is an RangeError if the integer is not in 8bit" do
+ for i in [4095, 0x4f7574206f6620636861722072616e6765] do
+ lambda { @io.ungetbyte(i) }.should raise_error(RangeError)
+ end
+ end
+ end
+
+ ruby_version_is '2.6.1' do
+ it "never raises RangeError" do
+ for i in [4095, 0x4f7574206f6620636861722072616e67ff] do
+ @io.ungetbyte(i).should be_nil
+ @io.getbyte.should == 255
+ end
+ end
+ end
+
+ it "raises an IOError if the IO is closed" do
+ @io.close
+ lambda { @io.ungetbyte(42) }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/ungetc_spec.rb b/spec/ruby/core/io/ungetc_spec.rb
new file mode 100644
index 0000000000..7d090d0e5d
--- /dev/null
+++ b/spec/ruby/core/io/ungetc_spec.rb
@@ -0,0 +1,135 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#ungetc" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+
+ @empty = tmp('empty.txt')
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @empty
+ end
+
+ it "pushes back one character onto stream" do
+ @io.getc.should == ?V
+ @io.ungetc(86)
+ @io.getc.should == ?V
+
+ @io.ungetc(10)
+ @io.getc.should == ?\n
+
+ @io.getc.should == ?o
+ @io.getc.should == ?i
+ # read the rest of line
+ @io.readline.should == "ci la ligne une.\n"
+ @io.getc.should == ?Q
+ @io.ungetc(99)
+ @io.getc.should == ?c
+ end
+
+ it "interprets the codepoint in the external encoding" do
+ @io.set_encoding(Encoding::UTF_8)
+ @io.ungetc(233)
+ c = @io.getc
+ c.encoding.should == Encoding::UTF_8
+ c.should == "é"
+ c.bytes.should == [195, 169]
+
+ @io.set_encoding(Encoding::IBM437)
+ @io.ungetc(130)
+ c = @io.getc
+ c.encoding.should == Encoding::IBM437
+ c.bytes.should == [130]
+ c.encode(Encoding::UTF_8).should == "é"
+ end
+
+ it "pushes back one character when invoked at the end of the stream" do
+ # read entire content
+ @io.read
+ @io.ungetc(100)
+ @io.getc.should == ?d
+ end
+
+ it "pushes back one character when invoked at the start of the stream" do
+ @io.read(0)
+ @io.ungetc(100)
+ @io.getc.should == ?d
+ end
+
+ it "pushes back one character when invoked on empty stream" do
+ touch(@empty)
+
+ File.open(@empty) { |empty|
+ empty.getc().should == nil
+ empty.ungetc(10)
+ empty.getc.should == ?\n
+ }
+ end
+
+ it "affects EOF state" do
+ touch(@empty)
+
+ File.open(@empty) { |empty|
+ empty.eof?.should == true
+ empty.getc.should == nil
+ empty.ungetc(100)
+ empty.eof?.should == false
+ }
+ end
+
+ it "adjusts the stream position" do
+ @io.pos.should == 0
+
+ # read one char
+ c = @io.getc
+ @io.pos.should == 1
+ @io.ungetc(c)
+ @io.pos.should == 0
+
+ # read all
+ @io.read
+ pos = @io.pos
+ @io.ungetc(98)
+ @io.pos.should == pos - 1
+ end
+
+ it "makes subsequent unbuffered operations to raise IOError" do
+ @io.getc
+ @io.ungetc(100)
+ lambda { @io.sysread(1) }.should raise_error(IOError)
+ end
+
+ it "does not affect the stream and returns nil when passed nil" do
+ @io.getc.should == ?V
+ @io.ungetc(nil)
+ @io.getc.should == ?o
+ end
+
+ it "puts one or more characters back in the stream" do
+ @io.gets
+ @io.ungetc("Aquí ").should be_nil
+ @io.gets.chomp.should == "Aquí Qui è la linea due."
+ end
+
+ it "calls #to_str to convert the argument if it is not an Integer" do
+ chars = mock("io ungetc")
+ chars.should_receive(:to_str).and_return("Aquí ")
+
+ @io.ungetc(chars).should be_nil
+ @io.gets.chomp.should == "Aquí Voici la ligne une."
+ end
+
+ it "returns nil when invoked on stream that was not yet read" do
+ @io.ungetc(100).should be_nil
+ end
+
+ it "raises IOError on closed stream" do
+ @io.getc
+ @io.close
+ lambda { @io.ungetc(100) }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/write_nonblock_spec.rb b/spec/ruby/core/io/write_nonblock_spec.rb
new file mode 100644
index 0000000000..285d1af376
--- /dev/null
+++ b/spec/ruby/core/io/write_nonblock_spec.rb
@@ -0,0 +1,83 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/write'
+
+# See https://bugs.ruby-lang.org/issues/5954#note-5
+platform_is_not :windows do
+ describe "IO#write_nonblock on a file" do
+ before :each do
+ @filename = tmp("IO_syswrite_file") + $$.to_s
+ File.open(@filename, "w") do |file|
+ file.write_nonblock("012345678901234567890123456789")
+ end
+ @file = File.open(@filename, "r+")
+ @readonly_file = File.open(@filename)
+ end
+
+ after :each do
+ @file.close if @file
+ @readonly_file.close if @readonly_file
+ rm_r @filename
+ end
+
+ it "writes all of the string's bytes but does not buffer them" do
+ written = @file.write_nonblock("abcde")
+ written.should == 5
+ File.open(@filename) do |file|
+ file.sysread(10).should == "abcde56789"
+ file.seek(0)
+ @file.fsync
+ file.sysread(10).should == "abcde56789"
+ end
+ end
+
+ it "checks if the file is writable if writing zero bytes" do
+ lambda {
+ @readonly_file.write_nonblock("")
+ }.should raise_error(IOError)
+ end
+ end
+
+ describe "IO#write_nonblock" do
+ it_behaves_like :io_write, :write_nonblock
+ end
+end
+
+describe 'IO#write_nonblock' do
+ before do
+ @read, @write = IO.pipe
+ end
+
+ after do
+ @read.close
+ @write.close
+ end
+
+ it "raises an exception extending IO::WaitWritable when the write would block" do
+ lambda {
+ loop { @write.write_nonblock('a' * 10_000) }
+ }.should raise_error(IO::WaitWritable) { |e|
+ platform_is_not :windows do
+ e.should be_kind_of(Errno::EAGAIN)
+ end
+ platform_is :windows do
+ e.should be_kind_of(Errno::EWOULDBLOCK)
+ end
+ }
+ end
+
+ context "when exception option is set to false" do
+ it "returns :wait_writable when the operation would block" do
+ loop { break if @write.write_nonblock("a" * 10_000, exception: false) == :wait_writable }
+ 1.should == 1
+ end
+ end
+
+ platform_is_not :windows do
+ it 'sets the IO in nonblock mode' do
+ require 'io/nonblock'
+ @write.write_nonblock('a')
+ @write.nonblock?.should == true
+ end
+ end
+end
diff --git a/spec/ruby/core/io/write_spec.rb b/spec/ruby/core/io/write_spec.rb
new file mode 100644
index 0000000000..50e3df864d
--- /dev/null
+++ b/spec/ruby/core/io/write_spec.rb
@@ -0,0 +1,168 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/write'
+require_relative 'shared/binwrite'
+
+describe "IO#write on a file" do
+ before :each do
+ @filename = tmp("IO_syswrite_file") + $$.to_s
+ File.open(@filename, "w") do |file|
+ file.write("012345678901234567890123456789")
+ end
+ @file = File.open(@filename, "r+")
+ @readonly_file = File.open(@filename)
+ end
+
+ after :each do
+ @file.close
+ @readonly_file.close
+ rm_r @filename
+ end
+
+ it "does not check if the file is writable if writing zero bytes" do
+ lambda { @readonly_file.write("") }.should_not raise_error
+ end
+
+ it "returns a length of 0 when writing a blank string" do
+ @file.write('').should == 0
+ end
+
+ with_feature :encoding do
+ before :each do
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+
+ Encoding.default_external = Encoding::UTF_8
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+ end
+
+ it "returns the number of bytes written" do
+ @file.write("hellø").should == 6
+ 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
+ end
+ File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000"
+ end
+
+ it "uses an :open_args option" do
+ IO.write(@filename, 'hi', open_args: ["w", nil, {encoding: Encoding::UTF_32LE}]).should == 8
+ end
+
+ it "raises a invalid byte sequence error if invalid bytes are being written" do
+ # pack "\xFEhi" to avoid utf-8 conflict
+ xFEhi = ([254].pack('C*') + 'hi').force_encoding('utf-8')
+ File.open(@filename, "w", encoding: Encoding::US_ASCII) do |file|
+ lambda { file.write(xFEhi) }.should raise_error(Encoding::InvalidByteSequenceError)
+ end
+ end
+
+ it "writes binary data if no encoding is given" do
+ File.open(@filename, "w") do |file|
+ file.write('Hëllö'.encode('ISO-8859-1'))
+ end
+ ë = ([235].pack('U')).encode('ISO-8859-1')
+ ö = ([246].pack('U')).encode('ISO-8859-1')
+ res = "H#{ë}ll#{ö}"
+ File.binread(@filename).should == res.force_encoding(Encoding::ASCII_8BIT)
+ end
+ end
+end
+
+describe "IO.write" do
+ it_behaves_like :io_binwrite, :write
+
+ it "uses an :open_args option" do
+ IO.write(@filename, 'hi', open_args: ["w", nil, {encoding: Encoding::UTF_32LE}]).should == 8
+ end
+
+ it "disregards other options if :open_args is given" do
+ IO.write(@filename, 'hi', 2, mode: "r", encoding: Encoding::UTF_32LE, open_args: ["w"]).should == 2
+ File.read(@filename).should == "\0\0hi"
+ end
+
+ it "uses the given encoding and returns the number of bytes written" do
+ IO.write(@filename, 'hi', mode: "w", encoding: Encoding::UTF_32LE).should == 8
+ end
+
+ it "writes binary data if no encoding is given" do
+ IO.write(@filename, 'Hëllö'.encode('ISO-8859-1'))
+ xEB = [235].pack('C*')
+ xF6 = [246].pack('C*')
+ File.binread(@filename).should == ("H" + xEB + "ll" + xF6).force_encoding(Encoding::ASCII_8BIT)
+ end
+
+ platform_is_not :windows do
+ describe "on a FIFO" do
+ before :each do
+ @fifo = tmp("File_open_fifo")
+ File.mkfifo(@fifo)
+ end
+
+ after :each do
+ rm_r @fifo
+ end
+
+ it "writes correctly" do
+ thr = Thread.new do
+ IO.read(@fifo)
+ end
+ begin
+ string = "hi"
+ IO.write(@fifo, string).should == string.length
+ ensure
+ thr.join
+ end
+ end
+ end
+ end
+end
+
+describe "IO#write" do
+ it_behaves_like :io_write, :write
+
+ ruby_version_is "2.5" do
+ it "accepts multiple arguments" do
+ IO.pipe do |r, w|
+ w.write("foo", "bar")
+ w.close
+
+ r.read.should == "foobar"
+ end
+ end
+ end
+end
+
+platform_is :windows do
+ describe "IO#write on Windows" do
+ before :each do
+ @fname = tmp("io_write.txt")
+ end
+
+ after :each do
+ rm_r @fname
+ @io.close if @io and !@io.closed?
+ end
+
+ it "normalizes line endings in text mode" do
+ @io = new_io(@fname, "wt")
+ @io.write "a\nb\nc"
+ @io.close
+ File.binread(@fname).should == "a\r\nb\r\nc"
+ end
+
+ it "does not normalize line endings in binary mode" do
+ @io = new_io(@fname, "wb")
+ @io.write "a\r\nb\r\nc"
+ @io.close
+ File.binread(@fname).should == "a\r\nb\r\nc"
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/Array_spec.rb b/spec/ruby/core/kernel/Array_spec.rb
new file mode 100644
index 0000000000..5aa54cbcb6
--- /dev/null
+++ b/spec/ruby/core/kernel/Array_spec.rb
@@ -0,0 +1,97 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel" do
+ it "has private instance method Array()" do
+ Kernel.should have_private_instance_method(:Array)
+ end
+end
+
+describe :kernel_Array, shared: true do
+ before :each do
+ @array = [1, 2, 3]
+ end
+
+ it "does not call #to_ary on an Array" do
+ @array.should_not_receive(:to_ary)
+ @object.send(@method, @array).should == @array
+ end
+
+ it "calls #to_ary to convert the argument to an Array" do
+ obj = mock("Array([1,2,3])")
+ obj.should_receive(:to_ary).and_return(@array)
+ obj.should_not_receive(:to_a)
+
+ @object.send(@method, obj).should == @array
+ end
+
+ it "does not call #to_a on an Array" do
+ @array.should_not_receive(:to_a)
+ @object.send(@method, @array).should == @array
+ end
+
+ it "calls #to_a if the argument does not respond to #to_ary" do
+ obj = mock("Array([1,2,3])")
+ obj.should_receive(:to_a).and_return(@array)
+
+ @object.send(@method, obj).should == @array
+ end
+
+ it "calls #to_a if #to_ary returns nil" do
+ obj = mock("Array([1,2,3])")
+ obj.should_receive(:to_ary).and_return(nil)
+ obj.should_receive(:to_a).and_return(@array)
+
+ @object.send(@method, obj).should == @array
+ end
+
+ it "returns an Array containing the argument if #to_a returns nil" do
+ obj = mock("Array([1,2,3])")
+ obj.should_receive(:to_a).and_return(nil)
+
+ @object.send(@method, obj).should == [obj]
+ end
+
+ it "calls #to_ary first, even if it's private" do
+ obj = KernelSpecs::PrivateToAry.new
+
+ @object.send(@method, obj).should == [1, 2]
+ end
+
+ it "calls #to_a if #to_ary is not defined, even if it's private" do
+ obj = KernelSpecs::PrivateToA.new
+
+ @object.send(@method, obj).should == [3, 4]
+ end
+
+ it "returns an Array containing the argument if it responds to neither #to_ary nor #to_a" do
+ obj = mock("Array(x)")
+ @object.send(@method, obj).should == [obj]
+ end
+
+ it "returns an empty Array when passed nil" do
+ @object.send(@method, nil).should == []
+ end
+
+ it "raises a TypeError if #to_ary does not return an Array" do
+ obj = mock("Array() string")
+ obj.should_receive(:to_ary).and_return("string")
+
+ lambda { @object.send(@method, obj) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if #to_a does not return an Array" do
+ obj = mock("Array() string")
+ obj.should_receive(:to_a).and_return("string")
+
+ lambda { @object.send(@method, obj) }.should raise_error(TypeError)
+ end
+end
+
+describe "Kernel.Array" do
+ it_behaves_like :kernel_Array, :Array_method, KernelSpecs
+end
+
+describe "Kernel#Array" do
+ it_behaves_like :kernel_Array, :Array_function, KernelSpecs
+end
diff --git a/spec/ruby/core/kernel/Complex_spec.rb b/spec/ruby/core/kernel/Complex_spec.rb
new file mode 100644
index 0000000000..44e4f44ada
--- /dev/null
+++ b/spec/ruby/core/kernel/Complex_spec.rb
@@ -0,0 +1,141 @@
+require_relative '../../spec_helper'
+
+describe "Kernel.Complex()" do
+ describe "when passed [Complex, Complex]" do
+ it "returns a new Complex number based on the two given numbers" do
+ Complex(Complex(3, 4), Complex(5, 6)).should == Complex(3 - 6, 4 + 5)
+ Complex(Complex(1.5, 2), Complex(-5, 6.3)).should == Complex(1.5 - 6.3, 2 - 5)
+ end
+ end
+
+ describe "when passed [Complex]" do
+ it "returns the passed Complex number" do
+ Complex(Complex(1, 2)).should == Complex(1, 2)
+ Complex(Complex(-3.4, bignum_value)).should == Complex(-3.4, bignum_value)
+ end
+ end
+
+ describe "when passed [Integer, Integer]" do
+ it "returns a new Complex number" do
+ Complex(1, 2).should be_an_instance_of(Complex)
+ Complex(1, 2).real.should == 1
+ Complex(1, 2).imag.should == 2
+
+ Complex(-3, -5).should be_an_instance_of(Complex)
+ Complex(-3, -5).real.should == -3
+ Complex(-3, -5).imag.should == -5
+
+ Complex(3.5, -4.5).should be_an_instance_of(Complex)
+ Complex(3.5, -4.5).real.should == 3.5
+ Complex(3.5, -4.5).imag.should == -4.5
+
+ Complex(bignum_value, 30).should be_an_instance_of(Complex)
+ Complex(bignum_value, 30).real.should == bignum_value
+ Complex(bignum_value, 30).imag.should == 30
+ end
+ end
+
+ describe "when passed [Integer/Float]" do
+ it "returns a new Complex number with 0 as the imaginary component" do
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ Complex(1).should be_an_instance_of(Complex)
+ Complex(1).imag.should == 0
+ Complex(1).real.should == 1
+
+ Complex(-3).should be_an_instance_of(Complex)
+ Complex(-3).imag.should == 0
+ Complex(-3).real.should == -3
+
+ Complex(-4.5).should be_an_instance_of(Complex)
+ Complex(-4.5).imag.should == 0
+ Complex(-4.5).real.should == -4.5
+
+ Complex(bignum_value).should be_an_instance_of(Complex)
+ Complex(bignum_value).imag.should == 0
+ Complex(bignum_value).real.should == bignum_value
+ end
+ end
+ end
+
+ describe "when passed a String" do
+ it "needs to be reviewed for spec completeness"
+ end
+
+ describe "when passed an Object which responds to #to_c" do
+ it "returns the passed argument" do
+ obj = Object.new; def obj.to_c; 1i end
+ Complex(obj).should == Complex(0, 1)
+ end
+ end
+
+ describe "when passed a Numeric which responds to #real? with false" do
+ it "returns the passed argument" do
+ n = mock_numeric("unreal")
+ n.should_receive(:real?).any_number_of_times.and_return(false)
+ Complex(n).should equal(n)
+ end
+ end
+
+ describe "when passed a Numeric which responds to #real? with true" do
+ it "returns a Complex with the passed argument as the real component and 0 as the imaginary component" do
+ n = mock_numeric("real")
+ n.should_receive(:real?).any_number_of_times.and_return(true)
+ result = Complex(n)
+ result.real.should equal(n)
+ result.imag.should equal(0)
+ end
+ end
+
+ describe "when passed Numerics n1 and n2 and at least one responds to #real? with false" do
+ [[false, false], [false, true], [true, false]].each do |r1, r2|
+ it "returns n1 + n2 * Complex(0, 1)" do
+ n1 = mock_numeric("n1")
+ n2 = mock_numeric("n2")
+ n3 = mock_numeric("n3")
+ n4 = mock_numeric("n4")
+ n1.should_receive(:real?).any_number_of_times.and_return(r1)
+ n2.should_receive(:real?).any_number_of_times.and_return(r2)
+ n2.should_receive(:*).with(Complex(0, 1)).and_return(n3)
+ n1.should_receive(:+).with(n3).and_return(n4)
+ Complex(n1, n2).should equal(n4)
+ end
+ end
+ end
+
+ describe "when passed two Numerics and both respond to #real? with true" do
+ it "returns a Complex with the passed arguments as real and imaginary components respectively" do
+ n1 = mock_numeric("n1")
+ n2 = mock_numeric("n2")
+ n1.should_receive(:real?).any_number_of_times.and_return(true)
+ n2.should_receive(:real?).any_number_of_times.and_return(true)
+ result = Complex(n1, n2)
+ result.real.should equal(n1)
+ result.imag.should equal(n2)
+ end
+ end
+
+ describe "when passed a single non-Numeric" do
+ it "coerces the passed argument using #to_c" do
+ n = mock("n")
+ c = Complex(0, 0)
+ n.should_receive(:to_c).and_return(c)
+ Complex(n).should equal(c)
+ end
+ end
+
+ describe "when passed a non-Numeric second argument" do
+ it "raises TypeError" do
+ lambda { Complex(:sym, :sym) }.should raise_error(TypeError)
+ lambda { Complex(0, :sym) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "when passed nil" do
+ it "raises TypeError" do
+ lambda { Complex(nil) }.should raise_error(TypeError, "can't convert nil into Complex")
+ lambda { Complex(0, nil) }.should raise_error(TypeError, "can't convert nil into Complex")
+ lambda { Complex(nil, 0) }.should raise_error(TypeError, "can't convert nil into Complex")
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/Float_spec.rb b/spec/ruby/core/kernel/Float_spec.rb
new file mode 100644
index 0000000000..47d7d0816f
--- /dev/null
+++ b/spec/ruby/core/kernel/Float_spec.rb
@@ -0,0 +1,316 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe :kernel_float, shared: true do
+ it "returns the identical Float for numeric Floats" do
+ float = 1.12
+ float2 = @object.send(:Float, float)
+ float2.should == float
+ float2.should equal float
+ end
+
+ it "returns a Float for Fixnums" do
+ @object.send(:Float, 1).should == 1.0
+ end
+
+ it "returns a Float for Complex with only a real part" do
+ @object.send(:Float, Complex(1)).should == 1.0
+ end
+
+ it "returns a Float for Bignums" do
+ @object.send(:Float, 1000000000000).should == 1000000000000.0
+ end
+
+ it "raises an ArgumentError for nil" do
+ lambda { @object.send(:Float, nil) }.should raise_error(TypeError)
+ end
+
+ it "returns the identical NaN for NaN" do
+ nan = nan_value
+ nan.nan?.should be_true
+ nan2 = @object.send(:Float, nan)
+ nan2.nan?.should be_true
+ nan2.should equal(nan)
+ end
+
+ it "returns the same Infinity for Infinity" do
+ infinity = infinity_value
+ infinity2 = @object.send(:Float, infinity)
+ infinity2.should == infinity_value
+ infinity.should equal(infinity2)
+ end
+
+ it "converts Strings to floats without calling #to_f" do
+ string = "10"
+ string.should_not_receive(:to_f)
+ @object.send(:Float, string).should == 10.0
+ end
+
+ it "converts Strings with decimal points into Floats" do
+ @object.send(:Float, "10.0").should == 10.0
+ end
+
+ it "raises an ArgumentError for a String of word characters" do
+ lambda { @object.send(:Float, "float") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are two decimal points in the String" do
+ lambda { @object.send(:Float, "10.0.0") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String of numbers followed by word characters" do
+ lambda { @object.send(:Float, "10D") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String of word characters followed by numbers" do
+ lambda { @object.send(:Float, "D10") }.should raise_error(ArgumentError)
+ end
+
+ it "is strict about the string form even across newlines" do
+ lambda { @object.send(:Float, "not a number\n10") }.should raise_error(ArgumentError)
+ lambda { @object.send(:Float, "10\nnot a number") }.should raise_error(ArgumentError)
+ end
+
+ it "converts String subclasses to floats without calling #to_f" do
+ my_string = Class.new(String) do
+ def to_f() 1.2 end
+ end
+
+ @object.send(:Float, my_string.new("10")).should == 10.0
+ end
+
+ it "returns a positive Float if the string is prefixed with +" do
+ @object.send(:Float, "+10").should == 10.0
+ @object.send(:Float, " +10").should == 10.0
+ end
+
+ it "returns a negative Float if the string is prefixed with +" do
+ @object.send(:Float, "-10").should == -10.0
+ @object.send(:Float, " -10").should == -10.0
+ end
+
+ it "raises an ArgumentError if a + or - is embedded in a String" do
+ lambda { @object.send(:Float, "1+1") }.should raise_error(ArgumentError)
+ lambda { @object.send(:Float, "1-1") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if a String has a trailing + or -" do
+ lambda { @object.send(:Float, "11+") }.should raise_error(ArgumentError)
+ lambda { @object.send(:Float, "11-") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String with a leading _" do
+ lambda { @object.send(:Float, "_1") }.should raise_error(ArgumentError)
+ end
+
+ it "returns a value for a String with an embedded _" do
+ @object.send(:Float, "1_000").should == 1000.0
+ end
+
+ it "raises an ArgumentError for a String with a trailing _" do
+ lambda { @object.send(:Float, "10_") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String of \\0" do
+ lambda { @object.send(:Float, "\0") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String with a leading \\0" do
+ lambda { @object.send(:Float, "\01") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String with an embedded \\0" do
+ lambda { @object.send(:Float, "1\01") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String with a trailing \\0" do
+ lambda { @object.send(:Float, "1\0") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String that is just an empty space" do
+ lambda { @object.send(:Float, " ") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String that with an embedded space" do
+ lambda { @object.send(:Float, "1 2") }.should raise_error(ArgumentError)
+ end
+
+ it "returns a value for a String with a leading space" do
+ @object.send(:Float, " 1").should == 1.0
+ end
+
+ it "returns a value for a String with a trailing space" do
+ @object.send(:Float, "1 ").should == 1.0
+ end
+
+ it "returns a value for a String with any leading whitespace" do
+ @object.send(:Float, "\t\n1").should == 1.0
+ end
+
+ it "returns a value for a String with any trailing whitespace" do
+ @object.send(:Float, "1\t\n").should == 1.0
+ end
+
+ %w(e E).each do |e|
+ it "raises an ArgumentError if #{e} is the trailing character" do
+ lambda { @object.send(:Float, "2#{e}") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if #{e} is the leading character" do
+ lambda { @object.send(:Float, "#{e}2") }.should raise_error(ArgumentError)
+ end
+
+ it "returns Infinity for '2#{e}1000'" do
+ @object.send(:Float, "2#{e}1000").should == Float::INFINITY
+ end
+
+ it "returns 0 for '2#{e}-1000'" do
+ @object.send(:Float, "2#{e}-1000").should == 0
+ end
+
+ it "allows embedded _ in a number on either side of the #{e}" do
+ @object.send(:Float, "2_0#{e}100").should == 20e100
+ @object.send(:Float, "20#{e}1_00").should == 20e100
+ @object.send(:Float, "2_0#{e}1_00").should == 20e100
+ end
+
+ it "raises an exception if a space is embedded on either side of the '#{e}'" do
+ lambda { @object.send(:Float, "2 0#{e}100") }.should raise_error(ArgumentError)
+ lambda { @object.send(:Float, "20#{e}1 00") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an exception if there's a leading _ on either side of the '#{e}'" do
+ lambda { @object.send(:Float, "_20#{e}100") }.should raise_error(ArgumentError)
+ lambda { @object.send(:Float, "20#{e}_100") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an exception if there's a trailing _ on either side of the '#{e}'" do
+ lambda { @object.send(:Float, "20_#{e}100") }.should raise_error(ArgumentError)
+ lambda { @object.send(:Float, "20#{e}100_") }.should raise_error(ArgumentError)
+ end
+
+ it "allows decimal points on the left side of the '#{e}'" do
+ @object.send(:Float, "2.0#{e}2").should == 2e2
+ end
+
+ it "raises an ArgumentError if there's a decimal point on the right side of the '#{e}'" do
+ lambda { @object.send(:Float, "20#{e}2.0") }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "for hexadecimal literals with binary exponent" do
+ %w(p P).each do |p|
+ it "interprets the fractional part (on the left side of '#{p}') in hexadecimal" do
+ @object.send(:Float, "0x10#{p}0").should == 16.0
+ end
+
+ it "interprets the exponent (on the right of '#{p}') in decimal" do
+ @object.send(:Float, "0x1#{p}10").should == 1024.0
+ end
+
+ it "raises an ArgumentError if #{p} is the trailing character" do
+ lambda { @object.send(:Float, "0x1#{p}") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if #{p} is the leading character" do
+ lambda { @object.send(:Float, "0x#{p}1") }.should raise_error(ArgumentError)
+ end
+
+ it "returns Infinity for '0x1#{p}10000'" do
+ @object.send(:Float, "0x1#{p}10000").should == Float::INFINITY
+ end
+
+ it "returns 0 for '0x1#{p}-10000'" do
+ @object.send(:Float, "0x1#{p}-10000").should == 0
+ end
+
+ it "allows embedded _ in a number on either side of the #{p}" do
+ @object.send(:Float, "0x1_0#{p}10").should == 16384.0
+ @object.send(:Float, "0x10#{p}1_0").should == 16384.0
+ @object.send(:Float, "0x1_0#{p}1_0").should == 16384.0
+ end
+
+ it "raises an exception if a space is embedded on either side of the '#{p}'" do
+ lambda { @object.send(:Float, "0x1 0#{p}10") }.should raise_error(ArgumentError)
+ lambda { @object.send(:Float, "0x10#{p}1 0") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an exception if there's a leading _ on either side of the '#{p}'" do
+ lambda { @object.send(:Float, "0x_10#{p}10") }.should raise_error(ArgumentError)
+ lambda { @object.send(:Float, "0x10#{p}_10") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an exception if there's a trailing _ on either side of the '#{p}'" do
+ lambda { @object.send(:Float, "0x10_#{p}10") }.should raise_error(ArgumentError)
+ lambda { @object.send(:Float, "0x10#{p}10_") }.should raise_error(ArgumentError)
+ end
+
+ it "allows hexadecimal points on the left side of the '#{p}'" do
+ @object.send(:Float, "0x1.8#{p}0").should == 1.5
+ end
+
+ it "raises an ArgumentError if there's a decimal point on the right side of the '#{p}'" do
+ lambda { @object.send(:Float, "0x1#{p}1.0") }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ it "returns a Float that can be a parameter to #Float again" do
+ float = @object.send(:Float, "10")
+ @object.send(:Float, float).should == 10.0
+ end
+
+ it "otherwise, converts the given argument to a Float by calling #to_f" do
+ (obj = mock('1.2')).should_receive(:to_f).once.and_return(1.2)
+ obj.should_not_receive(:to_i)
+ @object.send(:Float, obj).should == 1.2
+ end
+
+ it "returns the identical NaN if to_f is called and it returns NaN" do
+ nan = nan_value
+ (nan_to_f = mock('NaN')).should_receive(:to_f).once.and_return(nan)
+ nan2 = @object.send(:Float, nan_to_f)
+ nan2.nan?.should be_true
+ nan2.should equal(nan)
+ end
+
+ it "returns the identical Infinity if to_f is called and it returns Infinity" do
+ infinity = infinity_value
+ (infinity_to_f = mock('Infinity')).should_receive(:to_f).once.and_return(infinity)
+ infinity2 = @object.send(:Float, infinity_to_f)
+ infinity2.should equal(infinity)
+ end
+
+ it "raises a TypeError if #to_f is not provided" do
+ lambda { @object.send(:Float, mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if #to_f returns a String" do
+ (obj = mock('ha!')).should_receive(:to_f).once.and_return('ha!')
+ lambda { @object.send(:Float, obj) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if #to_f returns an Integer" do
+ (obj = mock('123')).should_receive(:to_f).once.and_return(123)
+ lambda { @object.send(:Float, obj) }.should raise_error(TypeError)
+ end
+
+ it "raises a RangeError when passed a Complex argument" do
+ c = Complex(2, 3)
+ lambda { @object.send(:Float, c) }.should raise_error(RangeError)
+ end
+end
+
+describe "Kernel.Float" do
+ it_behaves_like :kernel_float, :Float, Kernel
+end
+
+describe "Kernel#Float" do
+ it_behaves_like :kernel_float, :Float, Object.new
+end
+
+describe "Kernel#Float" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:Float)
+ end
+end
diff --git a/spec/ruby/core/kernel/Hash_spec.rb b/spec/ruby/core/kernel/Hash_spec.rb
new file mode 100644
index 0000000000..89e97fb11e
--- /dev/null
+++ b/spec/ruby/core/kernel/Hash_spec.rb
@@ -0,0 +1,63 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#hash" do
+ it "is provided" do
+ 1.respond_to?(:hash).should == true
+ end
+
+ it "is stable" do
+ 1.hash.should == 1.hash
+ end
+end
+
+describe "Kernel" do
+ it "has private instance method Hash()" do
+ Kernel.should have_private_instance_method(:Hash)
+ end
+end
+
+describe :kernel_Hash, shared: true do
+ before :each do
+ @hash = { a: 1}
+ end
+
+ it "converts nil to a Hash" do
+ @object.send(@method, nil).should == {}
+ end
+
+ it "converts an empty array to a Hash" do
+ @object.send(@method, []).should == {}
+ end
+
+ it "does not call #to_hash on an Hash" do
+ @hash.should_not_receive(:to_hash)
+ @object.send(@method, @hash).should == @hash
+ end
+
+ it "calls #to_hash to convert the argument to an Hash" do
+ obj = mock("Hash(a: 1)")
+ obj.should_receive(:to_hash).and_return(@hash)
+
+ @object.send(@method, obj).should == @hash
+ end
+
+ it "raises a TypeError if it doesn't respond to #to_hash" do
+ lambda { @object.send(@method, mock("")) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if #to_hash does not return an Hash" do
+ obj = mock("Hash() string")
+ obj.should_receive(:to_hash).and_return("string")
+
+ lambda { @object.send(@method, obj) }.should raise_error(TypeError)
+ end
+end
+
+describe "Kernel.Hash" do
+ it_behaves_like :kernel_Hash, :Hash_method, KernelSpecs
+end
+
+describe "Kernel#Hash" do
+ it_behaves_like :kernel_Hash, :Hash_function, KernelSpecs
+end
diff --git a/spec/ruby/core/kernel/Integer_spec.rb b/spec/ruby/core/kernel/Integer_spec.rb
new file mode 100644
index 0000000000..b79c827d31
--- /dev/null
+++ b/spec/ruby/core/kernel/Integer_spec.rb
@@ -0,0 +1,715 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe :kernel_integer, shared: true do
+ it "returns a Bignum for a Bignum" do
+ Integer(2e100).should == 2e100
+ end
+
+ it "returns a Fixnum for a Fixnum" do
+ Integer(100).should == 100
+ end
+
+ ruby_version_is ""..."2.6" do
+ it "uncritically return the value of to_int even if it is not an Integer" do
+ obj = mock("object")
+ obj.should_receive(:to_int).and_return("1")
+ obj.should_not_receive(:to_i)
+ Integer(obj).should == "1"
+ end
+ end
+
+ ruby_version_is "2.6" do
+ it "raises a TypeError when to_int returns not-an-Integer object and to_i returns nil" do
+ obj = mock("object")
+ obj.should_receive(:to_int).and_return("1")
+ obj.should_receive(:to_i).and_return(nil)
+ lambda { Integer(obj) }.should raise_error(TypeError)
+ end
+
+ it "return a result of to_i when to_int does not return an Integer" do
+ obj = mock("object")
+ obj.should_receive(:to_int).and_return("1")
+ obj.should_receive(:to_i).and_return(42)
+ Integer(obj).should == 42
+ end
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { Integer(nil) }.should raise_error(TypeError)
+ end
+
+ it "returns a Fixnum or Bignum object" do
+ Integer(2).should be_an_instance_of(Fixnum)
+ Integer(9**99).should be_an_instance_of(Bignum)
+ end
+
+ it "truncates Floats" do
+ Integer(3.14).should == 3
+ Integer(90.8).should == 90
+ end
+
+ it "calls to_i on Rationals" do
+ Integer(Rational(8,3)).should == 2
+ Integer(3.quo(2)).should == 1
+ end
+
+ it "returns the value of to_int if the result is a Fixnum" do
+ obj = mock("object")
+ obj.should_receive(:to_int).and_return(1)
+ obj.should_not_receive(:to_i)
+ Integer(obj).should == 1
+ end
+
+ it "returns the value of to_int if the result is a Bignum" do
+ obj = mock("object")
+ obj.should_receive(:to_int).and_return(2 * 10**100)
+ obj.should_not_receive(:to_i)
+ Integer(obj).should == 2 * 10**100
+ end
+
+ it "calls to_i on an object whose to_int returns nil" do
+ obj = mock("object")
+ obj.should_receive(:to_int).and_return(nil)
+ obj.should_receive(:to_i).and_return(1)
+ Integer(obj).should == 1
+ end
+
+ it "raises a TypeError if to_i returns a value that is not an Integer" do
+ obj = mock("object")
+ obj.should_receive(:to_i).and_return("1")
+ lambda { Integer(obj) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if no to_int or to_i methods exist" do
+ obj = mock("object")
+ lambda { Integer(obj) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if to_int returns nil and no to_i exists" do
+ obj = mock("object")
+ obj.should_receive(:to_i).and_return(nil)
+ lambda { Integer(obj) }.should raise_error(TypeError)
+ end
+
+ it "raises a FloatDomainError when passed NaN" do
+ lambda { Integer(nan_value) }.should raise_error(FloatDomainError)
+ end
+
+ it "raises a FloatDomainError when passed Infinity" do
+ lambda { Integer(infinity_value) }.should raise_error(FloatDomainError)
+ end
+end
+
+describe "Integer() given a String", shared: true do
+ it "raises an ArgumentError if the String is a null byte" do
+ lambda { Integer("\0") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the String starts with a null byte" do
+ lambda { Integer("\01") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the String ends with a null byte" do
+ lambda { Integer("1\0") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the String contains a null byte" do
+ lambda { Integer("1\01") }.should raise_error(ArgumentError)
+ end
+
+ it "ignores leading whitespace" do
+ Integer(" 1").should == 1
+ Integer(" 1").should == 1
+ Integer("\t\n1").should == 1
+ end
+
+ it "ignores trailing whitespace" do
+ Integer("1 ").should == 1
+ Integer("1 ").should == 1
+ Integer("1\t\n").should == 1
+ end
+
+ it "raises an ArgumentError if there are leading _s" do
+ lambda { Integer("_1") }.should raise_error(ArgumentError)
+ lambda { Integer("___1") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are trailing _s" do
+ lambda { Integer("1_") }.should raise_error(ArgumentError)
+ lambda { Integer("1___") }.should raise_error(ArgumentError)
+ end
+
+ it "ignores an embedded _" do
+ Integer("1_1").should == 11
+ end
+
+ it "raises an ArgumentError if there are multiple embedded _s" do
+ lambda { Integer("1__1") }.should raise_error(ArgumentError)
+ lambda { Integer("1___1") }.should raise_error(ArgumentError)
+ end
+
+ it "ignores a single leading +" do
+ Integer("+1").should == 1
+ end
+
+ it "raises an ArgumentError if there is a space between the + and number" do
+ lambda { Integer("+ 1") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are multiple leading +s" do
+ lambda { Integer("++1") }.should raise_error(ArgumentError)
+ lambda { Integer("+++1") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are trailing +s" do
+ lambda { Integer("1+") }.should raise_error(ArgumentError)
+ lambda { Integer("1+++") }.should raise_error(ArgumentError)
+ end
+
+ it "makes the number negative if there's a leading -" do
+ Integer("-1").should == -1
+ end
+
+ it "raises an ArgumentError if there are multiple leading -s" do
+ lambda { Integer("--1") }.should raise_error(ArgumentError)
+ lambda { Integer("---1") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are trailing -s" do
+ lambda { Integer("1-") }.should raise_error(ArgumentError)
+ lambda { Integer("1---") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there is a period" do
+ lambda { Integer("0.0") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for an empty String" do
+ lambda { Integer("") }.should raise_error(ArgumentError)
+ end
+
+ it "parses the value as 0 if the string consists of a single zero character" do
+ Integer("0").should == 0
+ end
+
+ %w(x X).each do |x|
+ it "parses the value as a hex number if there's a leading 0#{x}" do
+ Integer("0#{x}1").should == 0x1
+ Integer("0#{x}dd").should == 0xdd
+ end
+
+ it "is a positive hex number if there's a leading +0#{x}" do
+ Integer("+0#{x}1").should == 0x1
+ Integer("+0#{x}dd").should == 0xdd
+ end
+
+ it "is a negative hex number if there's a leading -0#{x}" do
+ Integer("-0#{x}1").should == -0x1
+ Integer("-0#{x}dd").should == -0xdd
+ end
+
+ it "raises an ArgumentError if the number cannot be parsed as hex" do
+ lambda { Integer("0#{x}g") }.should raise_error(ArgumentError)
+ end
+ end
+
+ %w(b B).each do |b|
+ it "parses the value as a binary number if there's a leading 0#{b}" do
+ Integer("0#{b}1").should == 0b1
+ Integer("0#{b}10").should == 0b10
+ end
+
+ it "is a positive binary number if there's a leading +0#{b}" do
+ Integer("+0#{b}1").should == 0b1
+ Integer("+0#{b}10").should == 0b10
+ end
+
+ it "is a negative binary number if there's a leading -0#{b}" do
+ Integer("-0#{b}1").should == -0b1
+ Integer("-0#{b}10").should == -0b10
+ end
+
+ it "raises an ArgumentError if the number cannot be parsed as binary" do
+ lambda { Integer("0#{b}2") }.should raise_error(ArgumentError)
+ end
+ end
+
+ ["o", "O", ""].each do |o|
+ it "parses the value as an octal number if there's a leading 0#{o}" do
+ Integer("0#{o}1").should == 0O1
+ Integer("0#{o}10").should == 0O10
+ end
+
+ it "is a positive octal number if there's a leading +0#{o}" do
+ Integer("+0#{o}1").should == 0O1
+ Integer("+0#{o}10").should == 0O10
+ end
+
+ it "is a negative octal number if there's a leading -0#{o}" do
+ Integer("-0#{o}1").should == -0O1
+ Integer("-0#{o}10").should == -0O10
+ end
+
+ it "raises an ArgumentError if the number cannot be parsed as octal" do
+ lambda { Integer("0#{o}9") }.should raise_error(ArgumentError)
+ end
+ end
+
+ %w(D d).each do |d|
+ it "parses the value as a decimal number if there's a leading 0#{d}" do
+ Integer("0#{d}1").should == 1
+ Integer("0#{d}10").should == 10
+ end
+
+ it "is a positive decimal number if there's a leading +0#{d}" do
+ Integer("+0#{d}1").should == 1
+ Integer("+0#{d}10").should == 10
+ end
+
+ it "is a negative decimal number if there's a leading -0#{d}" do
+ Integer("-0#{d}1").should == -1
+ Integer("-0#{d}10").should == -10
+ end
+
+ it "raises an ArgumentError if the number cannot be parsed as decimal" do
+ lambda { Integer("0#{d}a") }.should raise_error(ArgumentError)
+ end
+ end
+end
+
+describe "Integer() given a String and base", shared: true do
+ it "raises an ArgumentError if the String is a null byte" do
+ lambda { Integer("\0", 2) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the String starts with a null byte" do
+ lambda { Integer("\01", 3) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the String ends with a null byte" do
+ lambda { Integer("1\0", 4) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the String contains a null byte" do
+ lambda { Integer("1\01", 5) }.should raise_error(ArgumentError)
+ end
+
+ it "ignores leading whitespace" do
+ Integer(" 16", 16).should == 22
+ Integer(" 16", 16).should == 22
+ Integer("\t\n16", 16).should == 22
+ end
+
+ it "ignores trailing whitespace" do
+ Integer("16 ", 16).should == 22
+ Integer("16 ", 16).should == 22
+ Integer("16\t\n", 16).should == 22
+ end
+
+ it "raises an ArgumentError if there are leading _s" do
+ lambda { Integer("_1", 7) }.should raise_error(ArgumentError)
+ lambda { Integer("___1", 7) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are trailing _s" do
+ lambda { Integer("1_", 12) }.should raise_error(ArgumentError)
+ lambda { Integer("1___", 12) }.should raise_error(ArgumentError)
+ end
+
+ it "ignores an embedded _" do
+ Integer("1_1", 4).should == 5
+ end
+
+ it "raises an ArgumentError if there are multiple embedded _s" do
+ lambda { Integer("1__1", 4) }.should raise_error(ArgumentError)
+ lambda { Integer("1___1", 4) }.should raise_error(ArgumentError)
+ end
+
+ it "ignores a single leading +" do
+ Integer("+10", 3).should == 3
+ end
+
+ it "raises an ArgumentError if there is a space between the + and number" do
+ lambda { Integer("+ 1", 3) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are multiple leading +s" do
+ lambda { Integer("++1", 3) }.should raise_error(ArgumentError)
+ lambda { Integer("+++1", 3) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are trailing +s" do
+ lambda { Integer("1+", 3) }.should raise_error(ArgumentError)
+ lambda { Integer("1+++", 12) }.should raise_error(ArgumentError)
+ end
+
+ it "makes the number negative if there's a leading -" do
+ Integer("-19", 20).should == -29
+ end
+
+ it "raises an ArgumentError if there are multiple leading -s" do
+ lambda { Integer("--1", 9) }.should raise_error(ArgumentError)
+ lambda { Integer("---1", 9) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are trailing -s" do
+ lambda { Integer("1-", 12) }.should raise_error(ArgumentError)
+ lambda { Integer("1---", 12) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there is a period" do
+ lambda { Integer("0.0", 3) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for an empty String" do
+ lambda { Integer("", 12) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a base of 1" do
+ lambda { Integer("1", 1) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a base of 37" do
+ lambda { Integer("1", 37) }.should raise_error(ArgumentError)
+ end
+
+ it "accepts wholly lowercase alphabetic strings for bases > 10" do
+ Integer('ab',12).should == 131
+ Integer('af',20).should == 215
+ Integer('ghj',30).should == 14929
+ end
+
+ it "accepts wholly uppercase alphabetic strings for bases > 10" do
+ Integer('AB',12).should == 131
+ Integer('AF',20).should == 215
+ Integer('GHJ',30).should == 14929
+ end
+
+ it "accepts mixed-case alphabetic strings for bases > 10" do
+ Integer('Ab',12).should == 131
+ Integer('aF',20).should == 215
+ Integer('GhJ',30).should == 14929
+ end
+
+ it "accepts alphanumeric strings for bases > 10" do
+ Integer('a3e',19).should == 3681
+ Integer('12q',31).should == 1049
+ Integer('c00o',29).should == 292692
+ end
+
+ it "raises an ArgumentError for letters invalid in the given base" do
+ lambda { Integer('z',19) }.should raise_error(ArgumentError)
+ lambda { Integer('c00o',2) }.should raise_error(ArgumentError)
+ end
+
+ %w(x X).each do |x|
+ it "parses the value as a hex number if there's a leading 0#{x} and a base of 16" do
+ Integer("0#{x}10", 16).should == 16
+ Integer("0#{x}dd", 16).should == 221
+ end
+
+ it "is a positive hex number if there's a leading +0#{x} and base of 16" do
+ Integer("+0#{x}1", 16).should == 0x1
+ Integer("+0#{x}dd", 16).should == 0xdd
+ end
+
+ it "is a negative hex number if there's a leading -0#{x} and a base of 16" do
+ Integer("-0#{x}1", 16).should == -0x1
+ Integer("-0#{x}dd", 16).should == -0xdd
+ end
+
+ 2.upto(15) do |base|
+ it "raises an ArgumentError if the number begins with 0#{x} and the base is #{base}" do
+ lambda { Integer("0#{x}1", base) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "raises an ArgumentError if the number cannot be parsed as hex and the base is 16" do
+ lambda { Integer("0#{x}g", 16) }.should raise_error(ArgumentError)
+ end
+ end
+
+ %w(b B).each do |b|
+ it "parses the value as a binary number if there's a leading 0#{b} and the base is 2" do
+ Integer("0#{b}1", 2).should == 0b1
+ Integer("0#{b}10", 2).should == 0b10
+ end
+
+ it "is a positive binary number if there's a leading +0#{b} and a base of 2" do
+ Integer("+0#{b}1", 2).should == 0b1
+ Integer("+0#{b}10", 2).should == 0b10
+ end
+
+ it "is a negative binary number if there's a leading -0#{b} and a base of 2" do
+ Integer("-0#{b}1", 2).should == -0b1
+ Integer("-0#{b}10", 2).should == -0b10
+ end
+
+ it "raises an ArgumentError if the number cannot be parsed as binary and the base is 2" do
+ lambda { Integer("0#{b}2", 2) }.should raise_error(ArgumentError)
+ end
+ end
+
+ ["o", "O"].each do |o|
+ it "parses the value as an octal number if there's a leading 0#{o} and a base of 8" do
+ Integer("0#{o}1", 8).should == 0O1
+ Integer("0#{o}10", 8).should == 0O10
+ end
+
+ it "is a positive octal number if there's a leading +0#{o} and a base of 8" do
+ Integer("+0#{o}1", 8).should == 0O1
+ Integer("+0#{o}10", 8).should == 0O10
+ end
+
+ it "is a negative octal number if there's a leading -0#{o} and a base of 8" do
+ Integer("-0#{o}1", 8).should == -0O1
+ Integer("-0#{o}10", 8).should == -0O10
+ end
+
+ it "raises an ArgumentError if the number cannot be parsed as octal and the base is 8" do
+ lambda { Integer("0#{o}9", 8) }.should raise_error(ArgumentError)
+ end
+
+ 2.upto(7) do |base|
+ it "raises an ArgumentError if the number begins with 0#{o} and the base is #{base}" do
+ lambda { Integer("0#{o}1", base) }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ %w(D d).each do |d|
+ it "parses the value as a decimal number if there's a leading 0#{d} and a base of 10" do
+ Integer("0#{d}1", 10).should == 1
+ Integer("0#{d}10",10).should == 10
+ end
+
+ it "is a positive decimal number if there's a leading +0#{d} and a base of 10" do
+ Integer("+0#{d}1", 10).should == 1
+ Integer("+0#{d}10", 10).should == 10
+ end
+
+ it "is a negative decimal number if there's a leading -0#{d} and a base of 10" do
+ Integer("-0#{d}1", 10).should == -1
+ Integer("-0#{d}10", 10).should == -10
+ end
+
+ it "raises an ArgumentError if the number cannot be parsed as decimal and the base is 10" do
+ lambda { Integer("0#{d}a", 10) }.should raise_error(ArgumentError)
+ end
+
+ 2.upto(9) do |base|
+ it "raises an ArgumentError if the number begins with 0#{d} and the base is #{base}" do
+ lambda { Integer("0#{d}1", base) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "raises an ArgumentError if a base is given for a non-String value" do
+ lambda { Integer(98, 15) }.should raise_error(ArgumentError)
+ end
+ end
+end
+
+describe :kernel_Integer, shared: true do
+ it "raises an ArgumentError when the String contains digits out of range of radix 2" do
+ str = "23456789abcdefghijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 2) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 3" do
+ str = "3456789abcdefghijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 3) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 4" do
+ str = "456789abcdefghijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 4) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 5" do
+ str = "56789abcdefghijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 5) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 6" do
+ str = "6789abcdefghijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 6) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 7" do
+ str = "789abcdefghijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 7) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 8" do
+ str = "89abcdefghijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 8) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 9" do
+ str = "9abcdefghijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 9) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 10" do
+ str = "abcdefghijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 10) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 11" do
+ str = "bcdefghijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 11) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 12" do
+ str = "cdefghijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 12) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 13" do
+ str = "defghijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 13) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 14" do
+ str = "efghijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 14) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 15" do
+ str = "fghijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 15) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 16" do
+ str = "ghijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 16) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 17" do
+ str = "hijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 17) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 18" do
+ str = "ijklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 18) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 19" do
+ str = "jklmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 19) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 20" do
+ str = "klmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 20) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 21" do
+ str = "lmnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 21) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 22" do
+ str = "mnopqrstuvwxyz"
+ lambda { @object.send(@method, str, 22) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 23" do
+ str = "nopqrstuvwxyz"
+ lambda { @object.send(@method, str, 23) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 24" do
+ str = "opqrstuvwxyz"
+ lambda { @object.send(@method, str, 24) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 25" do
+ str = "pqrstuvwxyz"
+ lambda { @object.send(@method, str, 25) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 26" do
+ str = "qrstuvwxyz"
+ lambda { @object.send(@method, str, 26) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 27" do
+ str = "rstuvwxyz"
+ lambda { @object.send(@method, str, 27) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 28" do
+ str = "stuvwxyz"
+ lambda { @object.send(@method, str, 28) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 29" do
+ str = "tuvwxyz"
+ lambda { @object.send(@method, str, 29) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 30" do
+ str = "uvwxyz"
+ lambda { @object.send(@method, str, 30) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 31" do
+ str = "vwxyz"
+ lambda { @object.send(@method, str, 31) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 32" do
+ str = "wxyz"
+ lambda { @object.send(@method, str, 32) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 33" do
+ str = "xyz"
+ lambda { @object.send(@method, str, 33) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 34" do
+ str = "yz"
+ lambda { @object.send(@method, str, 34) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 35" do
+ str = "z"
+ lambda { @object.send(@method, str, 35) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the String contains digits out of range of radix 36" do
+ lambda { @object.send(@method, "{", 36) }.should raise_error(ArgumentError)
+ end
+end
+
+describe "Kernel.Integer" do
+ it_behaves_like :kernel_Integer, :Integer_method, KernelSpecs
+
+ # TODO: fix these specs
+ it_behaves_like :kernel_integer, :Integer, Kernel
+ it_behaves_like "Integer() given a String", :Integer
+
+ it_behaves_like "Integer() given a String and base", :Integer
+
+ it "is a public method" do
+ Kernel.Integer(10).should == 10
+ end
+end
+
+describe "Kernel#Integer" do
+ it_behaves_like :kernel_Integer, :Integer_function, KernelSpecs
+
+ # TODO: fix these specs
+ it_behaves_like :kernel_integer, :Integer, Object.new
+ it_behaves_like "Integer() given a String", :Integer
+
+ it_behaves_like "Integer() given a String and base", :Integer
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:Integer)
+ end
+end
diff --git a/spec/ruby/core/kernel/Rational_spec.rb b/spec/ruby/core/kernel/Rational_spec.rb
new file mode 100644
index 0000000000..2d1051db7f
--- /dev/null
+++ b/spec/ruby/core/kernel/Rational_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/rational/Rational'
+
+describe "Kernel.Rational" do
+ it_behaves_like :kernel_Rational, :Rational
+end
diff --git a/spec/ruby/core/kernel/String_spec.rb b/spec/ruby/core/kernel/String_spec.rb
new file mode 100644
index 0000000000..81489875c8
--- /dev/null
+++ b/spec/ruby/core/kernel/String_spec.rb
@@ -0,0 +1,106 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe :kernel_String, shared: true do
+ it "converts nil to a String" do
+ @object.send(@method, nil).should == ""
+ end
+
+ it "converts a Float to a String" do
+ @object.send(@method, 1.12).should == "1.12"
+ end
+
+ it "converts a boolean to a String" do
+ @object.send(@method, false).should == "false"
+ @object.send(@method, true).should == "true"
+ end
+
+ it "converts a constant to a String" do
+ @object.send(@method, Object).should == "Object"
+ end
+
+ it "calls #to_s to convert an arbitrary object to a String" do
+ obj = mock('test')
+ obj.should_receive(:to_s).and_return("test")
+
+ @object.send(@method, obj).should == "test"
+ end
+
+ it "raises a TypeError if #to_s does not exist" do
+ obj = mock('to_s')
+ class << obj
+ undef_method :to_s
+ end
+
+ lambda { @object.send(@method, obj) }.should raise_error(TypeError)
+ end
+
+ # #5158
+ it "raises a TypeError if respond_to? returns false for #to_s" do
+ obj = mock("to_s")
+ class << obj
+ def respond_to?(meth, include_private=false)
+ meth == :to_s ? false : super
+ end
+ end
+
+ lambda { @object.send(@method, obj) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if #to_s is not defined, even though #respond_to?(:to_s) returns true" do
+ # cannot use a mock because of how RSpec affects #method_missing
+ obj = Object.new
+ class << obj
+ undef_method :to_s
+ def respond_to?(meth, include_private=false)
+ meth == :to_s ? true : super
+ end
+ end
+
+ lambda { @object.send(@method, obj) }.should raise_error(TypeError)
+ end
+
+ it "calls #to_s if #respond_to?(:to_s) returns true" do
+ obj = mock('to_s')
+ class << obj
+ undef_method :to_s
+ def method_missing(meth, *args)
+ meth == :to_s ? "test" : super
+ end
+ end
+
+ @object.send(@method, obj).should == "test"
+ end
+
+ it "raises a TypeError if #to_s does not return a String" do
+ (obj = mock('123')).should_receive(:to_s).and_return(123)
+ lambda { @object.send(@method, obj) }.should raise_error(TypeError)
+ end
+
+ it "returns the same object if it is already a String" do
+ string = "Hello"
+ string.should_not_receive(:to_s)
+ string2 = @object.send(@method, string)
+ string.should equal(string2)
+ end
+
+ it "returns the same object if it is an instance of a String subclass" do
+ subklass = Class.new(String)
+ string = subklass.new("Hello")
+ string.should_not_receive(:to_s)
+ string2 = @object.send(@method, string)
+ string.should equal(string2)
+ end
+end
+
+describe "Kernel.String" do
+ it_behaves_like :kernel_String, :String, Kernel
+end
+
+describe "Kernel#String" do
+ it_behaves_like :kernel_String, :String, Object.new
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:String)
+ end
+end
diff --git a/spec/ruby/core/kernel/__callee___spec.rb b/spec/ruby/core/kernel/__callee___spec.rb
new file mode 100644
index 0000000000..3059ea8b57
--- /dev/null
+++ b/spec/ruby/core/kernel/__callee___spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/__callee__'
+
+describe "Kernel.__callee__" do
+ it "returns the current method, even when aliased" do
+ KernelSpecs::CalleeTest.new.f.should == :f
+ end
+
+ it "returns the aliased name when aliased method" do
+ KernelSpecs::CalleeTest.new.g.should == :g
+ end
+
+ it "returns the caller from blocks too" do
+ KernelSpecs::CalleeTest.new.in_block.should == [:in_block, :in_block]
+ end
+
+ it "returns the caller from define_method too" do
+ KernelSpecs::CalleeTest.new.dm.should == :dm
+ end
+
+ it "returns the caller from block inside define_method too" do
+ KernelSpecs::CalleeTest.new.dm_block.should == [:dm_block, :dm_block]
+ end
+
+ it "returns method name even from send" do
+ KernelSpecs::CalleeTest.new.from_send.should == :from_send
+ end
+
+ it "returns method name even from eval" do
+ KernelSpecs::CalleeTest.new.from_eval.should == :from_eval
+ end
+
+ it "returns nil from inside a class body" do
+ KernelSpecs::CalleeTest.new.from_class_body.should == nil
+ end
+
+ it "returns nil when not called from a method" do
+ __callee__.should == nil
+ end
+
+ it "returns the caller from a define_method called from the same class" do
+ c = Class.new do
+ define_method(:f) { 1.times{ break __callee__ } }
+ def g; f end
+ end
+ c.new.g.should == :f
+ end
+end
diff --git a/spec/ruby/core/kernel/__dir___spec.rb b/spec/ruby/core/kernel/__dir___spec.rb
new file mode 100644
index 0000000000..3c34277277
--- /dev/null
+++ b/spec/ruby/core/kernel/__dir___spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+
+describe "Kernel#__dir__" do
+ it "returns the real name of the directory containing the currently-executing file" do
+ __dir__.should == File.realpath(File.dirname(__FILE__))
+ end
+
+ context "when used in eval with a given filename" do
+ it "returns File.dirname(filename)" do
+ eval("__dir__", nil, "foo.rb").should == "."
+ eval("__dir__", nil, "foo/bar.rb").should == "foo"
+ end
+ end
+
+ context "when used in eval with top level binding" do
+ it "returns the real name of the directory containing the currently-executing file" do
+ eval("__dir__", binding).should == File.realpath(File.dirname(__FILE__))
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/__method___spec.rb b/spec/ruby/core/kernel/__method___spec.rb
new file mode 100644
index 0000000000..578d25640d
--- /dev/null
+++ b/spec/ruby/core/kernel/__method___spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/__method__'
+
+describe "Kernel.__method__" do
+ it "returns the current method, even when aliased" do
+ KernelSpecs::MethodTest.new.f.should == :f
+ end
+
+ it "returns the original name when aliased method" do
+ KernelSpecs::MethodTest.new.g.should == :f
+ end
+
+ it "returns the caller from blocks too" do
+ KernelSpecs::MethodTest.new.in_block.should == [:in_block, :in_block]
+ end
+
+ it "returns the caller from define_method too" do
+ KernelSpecs::MethodTest.new.dm.should == :dm
+ end
+
+ it "returns the caller from block inside define_method too" do
+ KernelSpecs::MethodTest.new.dm_block.should == [:dm_block, :dm_block]
+ end
+
+ it "returns method name even from send" do
+ KernelSpecs::MethodTest.new.from_send.should == :from_send
+ end
+
+ it "returns method name even from eval" do
+ KernelSpecs::MethodTest.new.from_eval.should == :from_eval
+ end
+
+ it "returns nil from inside a class body" do
+ KernelSpecs::MethodTest.new.from_class_body.should == nil
+ end
+
+ it "returns nil when not called from a method" do
+ __method__.should == nil
+ end
+end
diff --git a/spec/ruby/core/kernel/abort_spec.rb b/spec/ruby/core/kernel/abort_spec.rb
new file mode 100644
index 0000000000..f8152718c5
--- /dev/null
+++ b/spec/ruby/core/kernel/abort_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/process/abort'
+
+describe "Kernel#abort" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:abort)
+ end
+
+ it_behaves_like :process_abort, :abort, KernelSpecs::Method.new
+end
+
+describe "Kernel.abort" do
+ it_behaves_like :process_abort, :abort, Kernel
+end
diff --git a/spec/ruby/core/kernel/at_exit_spec.rb b/spec/ruby/core/kernel/at_exit_spec.rb
new file mode 100644
index 0000000000..21149f965b
--- /dev/null
+++ b/spec/ruby/core/kernel/at_exit_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel.at_exit" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:at_exit)
+ end
+
+ it "runs after all other code" do
+ ruby_exe("at_exit {print 5}; print 6").should == "65"
+ end
+
+ it "runs in reverse order of registration" do
+ code = "at_exit {print 4};at_exit {print 5}; print 6; at_exit {print 7}"
+ ruby_exe(code).should == "6754"
+ end
+
+ it "allows calling exit inside at_exit handler" do
+ code = "at_exit {print 3}; at_exit {print 4; exit; print 5}; at_exit {print 6}"
+ ruby_exe(code).should == "643"
+ end
+
+ it "gives access to the last raised exception" do
+ code = <<-EOC
+ at_exit do
+ puts "The exception matches: \#{$! == $exception}"
+ end
+
+ begin
+ raise "foo"
+ rescue => $exception
+ raise
+ end
+ EOC
+
+ result = ruby_exe(code, args: "2>&1", escape: true)
+ result.should =~ /The exception matches: true/
+ end
+
+end
+
+describe "Kernel#at_exit" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/autoload_spec.rb b/spec/ruby/core/kernel/autoload_spec.rb
new file mode 100644
index 0000000000..5fa8fa92b3
--- /dev/null
+++ b/spec/ruby/core/kernel/autoload_spec.rb
@@ -0,0 +1,160 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# These specs only illustrate the basic autoload cases
+# and where toplevel autoload behaves differently from
+# Module#autoload. See those specs for more examples.
+
+autoload :KSAutoloadA, "autoload_a.rb"
+autoload :KSAutoloadB, fixture(__FILE__, "autoload_b.rb")
+autoload :KSAutoloadCallsRequire, "main_autoload_not_exist.rb"
+
+def check_autoload(const)
+ autoload? const
+end
+
+describe "Kernel#autoload" do
+ before :each do
+ @loaded_features = $".dup
+ end
+
+ after :each do
+ $".replace @loaded_features
+ end
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:autoload)
+ end
+
+ it "registers a file to load the first time the named constant is accessed" do
+ Object.autoload?(:KSAutoloadA).should == "autoload_a.rb"
+ end
+
+ it "registers a file to load the first time the named constant is accessed" do
+ check_autoload(:KSAutoloadA).should == "autoload_a.rb"
+ end
+
+ it "sets the autoload constant in Object's constant table" do
+ Object.should have_constant(:KSAutoloadA)
+ end
+
+ it "loads the file when the constant is accessed" do
+ KSAutoloadB.loaded.should == :ksautoload_b
+ end
+
+ it "calls main.require(path) to load the file" do
+ main = TOPLEVEL_BINDING.eval("self")
+ main.should_receive(:require).with("main_autoload_not_exist.rb")
+ # The constant won't be defined since require is mocked to do nothing
+ -> { KSAutoloadCallsRequire }.should raise_error(NameError)
+ end
+
+ it "can autoload in instance_eval" do
+ Object.new.instance_eval do
+ autoload :KSAutoloadD, fixture(__FILE__, "autoload_d.rb")
+ KSAutoloadD.loaded.should == :ksautoload_d
+ end
+ end
+
+ describe "when Object is frozen" do
+ it "raises a FrozenError before defining the constant" do
+ ruby_exe(fixture(__FILE__, "autoload_frozen.rb")).should == "#{frozen_error_class} - nil"
+ end
+ end
+
+ describe "when called from included module's method" do
+ before :all do
+ @path = fixture(__FILE__, "autoload_from_included_module.rb")
+ KernelSpecs::AutoloadMethodIncluder.new.setup_autoload(@path)
+ end
+
+ it "setups the autoload on the included module" do
+ KernelSpecs::AutoloadMethod.autoload?(:AutoloadFromIncludedModule).should == @path
+ end
+
+ it "the autoload is reacheable from the class too" do
+ KernelSpecs::AutoloadMethodIncluder.autoload?(:AutoloadFromIncludedModule).should == @path
+ end
+
+ it "the autoload relative to the included module works" do
+ KernelSpecs::AutoloadMethod::AutoloadFromIncludedModule.loaded.should == :autoload_from_included_module
+ end
+ end
+end
+
+describe "Kernel#autoload?" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:autoload?)
+ end
+
+ it "returns the name of the file that will be autoloaded" do
+ check_autoload(:KSAutoloadA).should == "autoload_a.rb"
+ end
+
+ it "returns nil if no file has been registered for a constant" do
+ check_autoload(:Manualload).should be_nil
+ end
+end
+
+Kernel.autoload :KSAutoloadBB, "no_autoload.rb"
+
+describe "Kernel.autoload" do
+ before :all do
+ @non_existent = fixture __FILE__, "no_autoload.rb"
+ end
+
+ before :each do
+ @loaded_features = $".dup
+
+ ScratchPad.clear
+ end
+
+ after :each do
+ $".replace @loaded_features
+ end
+
+ it "registers a file to load the first time the toplevel constant is accessed" do
+ Kernel.autoload :KSAutoloadAA, @non_existent
+ Kernel.autoload?(:KSAutoloadAA).should == @non_existent
+ end
+
+ it "sets the autoload constant in Object's constant table" do
+ Object.should have_constant(:KSAutoloadBB)
+ end
+
+ it "calls #to_path on non-String filenames" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return @non_existent
+ Kernel.autoload :KSAutoloadAA, p
+ end
+
+ describe "when called from included module's method" do
+ before :all do
+ @path = fixture(__FILE__, "autoload_from_included_module2.rb")
+ KernelSpecs::AutoloadMethodIncluder2.new.setup_autoload(@path)
+ end
+
+ it "setups the autoload on the included module" do
+ KernelSpecs::AutoloadMethod2.autoload?(:AutoloadFromIncludedModule2).should == @path
+ end
+
+ it "the autoload is reacheable from the class too" do
+ KernelSpecs::AutoloadMethodIncluder2.autoload?(:AutoloadFromIncludedModule2).should == @path
+ end
+
+ it "the autoload relative to the included module works" do
+ KernelSpecs::AutoloadMethod2::AutoloadFromIncludedModule2.loaded.should == :autoload_from_included_module2
+ end
+ end
+end
+
+describe "Kernel.autoload?" do
+ it "returns the name of the file that will be autoloaded" do
+ Kernel.autoload :KSAutoload, "autoload.rb"
+ Kernel.autoload?(:KSAutoload).should == "autoload.rb"
+ end
+
+ it "returns nil if no file has been registered for a constant" do
+ Kernel.autoload?(:Manualload).should be_nil
+ end
+end
diff --git a/spec/ruby/core/kernel/backtick_spec.rb b/spec/ruby/core/kernel/backtick_spec.rb
new file mode 100644
index 0000000000..dcba218f61
--- /dev/null
+++ b/spec/ruby/core/kernel/backtick_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#`" do
+ before :each do
+ @original_external = Encoding.default_external
+ end
+
+ after :each do
+ Encoding.default_external = @original_external
+ end
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:`)
+ end
+
+ it "returns the standard output of the executed sub-process" do
+ ip = 'world'
+ `echo disc #{ip}`.should == "disc world\n"
+ end
+
+ it "lets the standard error stream pass through to the inherited stderr" do
+ cmd = ruby_cmd('STDERR.print "error stream"')
+ lambda {
+ `#{cmd}`.should == ""
+ }.should output_to_fd("error stream", STDERR)
+ end
+
+ it "produces a String in the default external encoding" do
+ Encoding.default_external = Encoding::SHIFT_JIS
+ `echo disc`.encoding.should equal(Encoding::SHIFT_JIS)
+ end
+
+ it "raises an Errno::ENOENT if the command is not executable" do
+ lambda { `nonexistent_command` }.should raise_error(Errno::ENOENT)
+ end
+
+ platform_is_not :windows do
+ it "sets $? to the exit status of the executed sub-process" do
+ ip = 'world'
+ `echo disc #{ip}`
+ $?.should be_kind_of(Process::Status)
+ $?.stopped?.should == false
+ $?.exited?.should == true
+ $?.exitstatus.should == 0
+ $?.success?.should == true
+ `echo disc #{ip}; exit 99`
+ $?.should be_kind_of(Process::Status)
+ $?.stopped?.should == false
+ $?.exited?.should == true
+ $?.exitstatus.should == 99
+ $?.success?.should == false
+ end
+ end
+
+ platform_is :windows do
+ it "sets $? to the exit status of the executed sub-process" do
+ ip = 'world'
+ `echo disc #{ip}`
+ $?.should be_kind_of(Process::Status)
+ $?.stopped?.should == false
+ $?.exited?.should == true
+ $?.exitstatus.should == 0
+ $?.success?.should == true
+ `echo disc #{ip}& exit 99`
+ $?.should be_kind_of(Process::Status)
+ $?.stopped?.should == false
+ $?.exited?.should == true
+ $?.exitstatus.should == 99
+ $?.success?.should == false
+ end
+ end
+end
+
+describe "Kernel.`" do
+ it "tries to convert the given argument to String using #to_str" do
+ (obj = mock('echo test')).should_receive(:to_str).and_return("echo test")
+ Kernel.`(obj).should == "test\n" #` fix vim syntax highlighting
+ end
+end
diff --git a/spec/ruby/core/kernel/binding_spec.rb b/spec/ruby/core/kernel/binding_spec.rb
new file mode 100644
index 0000000000..e9fca6e3ab
--- /dev/null
+++ b/spec/ruby/core/kernel/binding_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel.binding" do
+ it "returns a binding for the caller" do
+ Kernel.binding.eval("self").should == self
+ end
+end
+
+describe "Kernel#binding" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:binding)
+ end
+
+ before :each do
+ @b1 = KernelSpecs::Binding.new(99).get_binding
+ ScratchPad.clear
+ end
+
+ it "returns a Binding object" do
+ @b1.kind_of?(Binding).should == true
+ end
+
+ it "encapsulates the execution context properly" do
+ eval("@secret", @b1).should == 100
+ eval("a", @b1).should == true
+ eval("b", @b1).should == true
+ eval("@@super_secret", @b1).should == "password"
+
+ eval("square(2)", @b1).should == 4
+ eval("self.square(2)", @b1).should == 4
+
+ eval("a = false", @b1)
+ eval("a", @b1).should == false
+ end
+
+ it "raises a NameError on undefined variable" do
+ lambda { eval("a_fake_variable", @b1) }.should raise_error(NameError)
+ end
+
+ it "uses the closure's self as self in the binding" do
+ m = mock(:whatever)
+ eval('self', m.send(:binding)).should == self
+ end
+
+ it "uses the class as self in a Class.new block" do
+ m = mock(:whatever)
+ cls = Class.new { ScratchPad.record eval('self', m.send(:binding)) }
+ ScratchPad.recorded.should == cls
+ end
+end
diff --git a/spec/ruby/core/kernel/block_given_spec.rb b/spec/ruby/core/kernel/block_given_spec.rb
new file mode 100644
index 0000000000..b00bfabfc3
--- /dev/null
+++ b/spec/ruby/core/kernel/block_given_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe :kernel_block_given, shared: true do
+ it "returns true if and only if a block is supplied" do
+ @object.accept_block {}.should == true
+ @object.accept_block_as_argument {}.should == true
+
+ @object.accept_block.should == false
+ @object.accept_block_as_argument.should == false
+ end
+
+ # Clarify: Based on http://www.ruby-forum.com/topic/137822 it appears
+ # that Matz wanted this to be true in 1.9.
+ it "returns false when a method defined by define_method is called with a block" do
+ @object.defined_block {}.should == false
+ end
+end
+
+describe "Kernel#block_given?" do
+ it_behaves_like :kernel_block_given, :block_given?, KernelSpecs::BlockGiven
+
+ it "returns false outside of a method" do
+ block_given?.should == false
+ end
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:block_given?)
+ end
+end
+
+describe "Kernel.block_given?" do
+ it_behaves_like :kernel_block_given, :block_given?, KernelSpecs::KernelBlockGiven
+end
+
+describe "self.send(:block_given?)" do
+ it_behaves_like :kernel_block_given, :block_given?, KernelSpecs::SelfBlockGiven
+end
diff --git a/spec/ruby/core/kernel/caller_locations_spec.rb b/spec/ruby/core/kernel/caller_locations_spec.rb
new file mode 100644
index 0000000000..03c05883d1
--- /dev/null
+++ b/spec/ruby/core/kernel/caller_locations_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/caller_locations'
+
+describe 'Kernel#caller_locations' do
+ it 'is a private method' do
+ Kernel.should have_private_instance_method(:caller_locations)
+ end
+
+ it 'returns an Array of caller locations' do
+ KernelSpecs::CallerLocationsTest.locations.empty?.should == false
+ end
+
+ it 'returns an Array of caller locations using a custom offset' do
+ locations = KernelSpecs::CallerLocationsTest.locations(2)
+
+ locations[0].absolute_path.end_with?('mspec.rb').should == true
+ end
+
+ it 'returns an Array of caller locations using a custom limit' do
+ locations = KernelSpecs::CallerLocationsTest.locations(1, 1)
+
+ locations.length.should == 1
+ end
+
+ it 'returns the locations as Thread::Backtrace::Location instances' do
+ locations = KernelSpecs::CallerLocationsTest.locations
+
+ locations.each do |location|
+ location.kind_of?(Thread::Backtrace::Location).should == true
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/caller_spec.rb b/spec/ruby/core/kernel/caller_spec.rb
new file mode 100644
index 0000000000..8469319c94
--- /dev/null
+++ b/spec/ruby/core/kernel/caller_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/caller'
+
+describe 'Kernel#caller' do
+ it 'is a private method' do
+ Kernel.should have_private_instance_method(:caller)
+ end
+
+ it 'returns an Array of caller locations' do
+ KernelSpecs::CallerTest.locations.empty?.should == false
+ end
+
+ it 'returns an Array of caller locations using a custom offset' do
+ locations = KernelSpecs::CallerTest.locations(2)
+
+ locations[0].should =~ %r{runner/mspec.rb}
+ end
+
+ it 'returns an Array of caller locations using a custom limit' do
+ locations = KernelSpecs::CallerTest.locations(1, 1)
+
+ locations.length.should == 1
+ end
+
+ it 'returns an Array of caller locations using a range' do
+ locations = KernelSpecs::CallerTest.locations(1..1)
+
+ locations.length.should == 1
+ end
+
+ it 'returns the locations as String instances' do
+ locations = KernelSpecs::CallerTest.locations
+ line = __LINE__ - 1
+
+ locations[0].should include("#{__FILE__}:#{line}:in")
+ end
+
+ it "returns an Array with the block given to #at_exit at the base of the stack" do
+ path = fixture(__FILE__, "caller_at_exit.rb")
+ lines = ruby_exe(path).lines
+ lines.should == [
+ "#{path}:6:in `foo'\n",
+ "#{path}:2:in `block in <main>'\n"
+ ]
+ end
+end
diff --git a/spec/ruby/core/kernel/case_compare_spec.rb b/spec/ruby/core/kernel/case_compare_spec.rb
new file mode 100644
index 0000000000..b8d30960e8
--- /dev/null
+++ b/spec/ruby/core/kernel/case_compare_spec.rb
@@ -0,0 +1,135 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+
+module Specs
+ module Kernel
+
+ class HasNone
+ end
+
+ class HasOpEqual
+ def ==(other)
+ other.kind_of? HasOpEqual
+ end
+ end
+
+ class HasEqual
+ def equal?(other)
+ false
+ end
+ end
+
+ class HasOppoOpEqual
+ def ==(other)
+ false
+ end
+
+ def equal?(other)
+ false
+ end
+ end
+ end
+end
+
+
+describe "Kernel#=== for a class with default #== and #equal?" do
+ before :each do
+ @o1 = Specs::Kernel::HasNone.new
+ @o2 = @o1.dup
+ end
+
+ it "returns true if other object has same object id" do
+ @o1.object_id.should == @o1.object_id
+ (@o1 === @o1).should == true
+ end
+
+ it "returns false if other object does not have same object id" do
+ @o1.object_id.should_not == @o2.object_id
+ (@o1 === @o2).should == false
+ end
+end
+
+describe "Kernel#=== for a class with #== overridden to consider other object's class" do
+ before :each do
+ @o = Object.new
+ @o1 = Specs::Kernel::HasOpEqual.new
+ @o2 = @o1.dup
+ end
+
+ it "returns true if #== returns true even if #equal? is false" do
+ @o1.should_not equal(@o2)
+ (@o1 == @o2).should == true
+ (@o1 === @o2).should == true
+ end
+
+ it "returns true if #equal? returns true" do
+ @o1.should equal(@o1)
+ (@o1 === @o1).should == true
+ end
+
+ it "returns false if neither #== nor #equal? returns true" do
+ @o1.should_not equal(@o)
+ (@o1 == @o).should == false
+ (@o1 === @o).should == false
+ end
+end
+
+describe "Kernel#=== for a class with #equal? overridden to always be false" do
+ before :each do
+ @o = Object.new
+ @o1 = Specs::Kernel::HasEqual.new
+ @o2 = @o1.dup
+ end
+
+ it "returns true if #== returns true even if #equal? is false" do
+ @o1.should_not equal(@o1)
+ (@o1 == @o1).should == true
+ (@o1 === @o1).should == true
+ end
+
+ it "returns false if neither #== nor #equal? returns true" do
+ @o1.should_not equal(@o)
+ (@o1 == @o).should == false
+ (@o1 === @o).should == false
+ end
+end
+
+describe "Kernel#=== for a class with #== and #equal? overridden to always be false" do
+ before :each do
+ @o = Object.new
+ @o1 = Specs::Kernel::HasOppoOpEqual.new
+ @o2 = @o1.dup
+ end
+
+ it "returns true if the object id is the same even if both #== and #equal? return false" do
+ @o1.object_id.should == @o1.object_id
+
+ @o1.should_not equal(@o1)
+ (@o1 == @o1).should == false
+
+ (@o1 === @o1).should == true
+ end
+
+ it "returns false if the object id is not the same and both #== and #equal? return false" do
+ @o1.object_id.should_not == @o2.object_id
+
+ @o1.should_not equal(@o2)
+ (@o1 == @o2).should == false
+
+ (@o1 === @o2).should == false
+ end
+end
+
+describe "Kernel#=== does not call #object_id nor #equal?" do
+ before :each do
+ @o1 = Object.new
+ @o1.should_not_receive(:object_id)
+ @o1.should_not_receive(:equal?)
+ end
+
+ it "but still returns true for #== or #=== on the same object" do
+ (@o1 == @o1).should == true
+ (@o1 === @o1).should == true
+ end
+end
diff --git a/spec/ruby/core/kernel/catch_spec.rb b/spec/ruby/core/kernel/catch_spec.rb
new file mode 100644
index 0000000000..092fb4968a
--- /dev/null
+++ b/spec/ruby/core/kernel/catch_spec.rb
@@ -0,0 +1,127 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel.catch" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "executes its block and catches a thrown value matching its argument" do
+ catch :thrown_key do
+ ScratchPad.record :catch_block
+ throw :thrown_key
+ ScratchPad.record :throw_failed
+ end
+ ScratchPad.recorded.should == :catch_block
+ end
+
+ it "returns the second value passed to throw" do
+ catch(:thrown_key) { throw :thrown_key, :catch_value }.should == :catch_value
+ end
+
+ it "returns the last expression evaluated if throw was not called" do
+ catch(:thrown_key) { 1; :catch_block }.should == :catch_block
+ end
+
+ it "passes the given symbol to its block" do
+ catch :thrown_key do |tag|
+ ScratchPad.record tag
+ end
+ ScratchPad.recorded.should == :thrown_key
+ end
+
+ it "raises an ArgumentError if a Symbol is thrown for a String catch value" do
+ lambda { catch("exit") { throw :exit } }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if a String with different identity is thrown" do
+ lambda { catch("exit") { throw "exit" } }.should raise_error(ArgumentError)
+ end
+
+ it "catches a Symbol when thrown a matching Symbol" do
+ catch :thrown_key do
+ ScratchPad.record :catch_block
+ throw :thrown_key
+ end
+ ScratchPad.recorded.should == :catch_block
+ end
+
+ it "catches a String when thrown a String with the same identity" do
+ key = "thrown_key"
+ catch key do
+ ScratchPad.record :catch_block
+ throw key
+ end
+ ScratchPad.recorded.should == :catch_block
+ end
+
+ it "accepts an object as an argument" do
+ catch(Object.new) { :catch_block }.should == :catch_block
+ end
+
+ it "yields an object when called without arguments" do
+ catch { |tag| tag }.should be_an_instance_of(Object)
+ end
+
+ it "can be used even in a method different from where throw is called" do
+ class CatchSpecs
+ def self.throwing_method
+ throw :blah, :thrown_value
+ end
+ def self.catching_method
+ catch :blah do
+ throwing_method
+ end
+ end
+ end
+ CatchSpecs.catching_method.should == :thrown_value
+ end
+
+ describe "when nested" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "catches across invocation boundaries" do
+ catch :one do
+ ScratchPad << 1
+ catch :two do
+ ScratchPad << 2
+ catch :three do
+ ScratchPad << 3
+ throw :one
+ ScratchPad << 4
+ end
+ ScratchPad << 5
+ end
+ ScratchPad << 6
+ end
+
+ ScratchPad.recorded.should == [1, 2, 3]
+ end
+
+ it "catches in the nested invocation with the same key object" do
+ catch :thrown_key do
+ ScratchPad << 1
+ catch :thrown_key do
+ ScratchPad << 2
+ throw :thrown_key
+ ScratchPad << 3
+ end
+ ScratchPad << 4
+ end
+
+ ScratchPad.recorded.should == [1, 2, 4]
+ end
+ end
+
+ it "raises LocalJumpError if no block is given" do
+ lambda { catch :blah }.should raise_error(LocalJumpError)
+ end
+end
+
+describe "Kernel#catch" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:catch)
+ end
+end
diff --git a/spec/ruby/core/kernel/chomp_spec.rb b/spec/ruby/core/kernel/chomp_spec.rb
new file mode 100644
index 0000000000..e6dcc07d42
--- /dev/null
+++ b/spec/ruby/core/kernel/chomp_spec.rb
@@ -0,0 +1,67 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe :kernel_chomp, shared: true do
+ it "removes the final newline of $_" do
+ KernelSpecs.chomp("abc\n", @method).should == "abc"
+ end
+
+ it "removes the final carriage return of $_" do
+ KernelSpecs.chomp("abc\r", @method).should == "abc"
+ end
+
+ it "removes the final carriage return, newline of $_" do
+ KernelSpecs.chomp("abc\r\n", @method).should == "abc"
+ end
+
+ it "removes only the final newline of $_" do
+ KernelSpecs.chomp("abc\n\n", @method).should == "abc\n"
+ end
+
+ it "removes the value of $/ from the end of $_" do
+ KernelSpecs.chomp("abcde", @method, "cde").should == "ab"
+ end
+end
+
+describe :kernel_chomp_private, shared: true do
+ it "is a private method" do
+ KernelSpecs.has_private_method(@method).should be_true
+ end
+end
+
+describe "Kernel.chomp" do
+ it_behaves_like :kernel_chomp, "Kernel.chomp"
+end
+
+describe "Kernel#chomp" do
+ it_behaves_like :kernel_chomp, "chomp"
+
+ it_behaves_like :kernel_chomp_private, :chomp
+end
+
+with_feature :encoding do
+ describe :kernel_chomp_encoded, shared: true do
+ before :each do
+ @external = Encoding.default_external
+ Encoding.default_external = Encoding::UTF_8
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ end
+
+ it "removes the final carriage return, newline from a multi-byte $_" do
+ script = fixture __FILE__, "#{@method}.rb"
+ KernelSpecs.run_with_dash_n(script).should == "ã‚れ"
+ end
+ end
+
+ describe "Kernel.chomp" do
+ it_behaves_like :kernel_chomp_encoded, "chomp"
+ end
+
+ describe "Kernel#chomp" do
+ it_behaves_like :kernel_chomp_encoded, "chomp_f"
+ end
+end
diff --git a/spec/ruby/core/kernel/chop_spec.rb b/spec/ruby/core/kernel/chop_spec.rb
new file mode 100644
index 0000000000..e9338d8990
--- /dev/null
+++ b/spec/ruby/core/kernel/chop_spec.rb
@@ -0,0 +1,55 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe :kernel_chop, shared: true do
+ it "removes the final character of $_" do
+ KernelSpecs.chop("abc", @method).should == "ab"
+ end
+
+ it "removes the final carriage return, newline of $_" do
+ KernelSpecs.chop("abc\r\n", @method).should == "abc"
+ end
+end
+
+describe :kernel_chop_private, shared: true do
+ it "is a private method" do
+ KernelSpecs.has_private_method(@method).should be_true
+ end
+end
+
+describe "Kernel.chop" do
+ it_behaves_like :kernel_chop, "Kernel.chop"
+end
+
+describe "Kernel#chop" do
+ it_behaves_like :kernel_chop_private, :chop
+
+ it_behaves_like :kernel_chop, "chop"
+end
+
+with_feature :encoding do
+ describe :kernel_chop_encoded, shared: true do
+ before :each do
+ @external = Encoding.default_external
+ Encoding.default_external = Encoding::UTF_8
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ end
+
+ it "removes the final multi-byte character from $_" do
+ script = fixture __FILE__, "#{@method}.rb"
+ KernelSpecs.run_with_dash_n(script).should == "ã‚"
+ end
+ end
+
+ describe "Kernel.chop" do
+ it_behaves_like :kernel_chop_encoded, "chop"
+ end
+
+ describe "Kernel#chop" do
+ it_behaves_like :kernel_chop_encoded, "chop_f"
+ end
+end
diff --git a/spec/ruby/core/kernel/class_spec.rb b/spec/ruby/core/kernel/class_spec.rb
new file mode 100644
index 0000000000..4d3b4e549b
--- /dev/null
+++ b/spec/ruby/core/kernel/class_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#class" do
+ it "returns the class of the object" do
+ Object.new.class.should equal(Object)
+
+ 1.class.should equal(Fixnum)
+ 3.14.class.should equal(Float)
+ :hello.class.should equal(Symbol)
+ "hello".class.should equal(String)
+ [1, 2].class.should equal(Array)
+ { 1 => 2 }.class.should equal(Hash)
+ end
+
+ it "returns Class for a class" do
+ BasicObject.class.should equal(Class)
+ String.class.should equal(Class)
+ end
+
+ it "returns the first non-singleton class" do
+ a = "hello"
+ def a.my_singleton_method; end
+ a.class.should equal(String)
+ end
+end
diff --git a/spec/ruby/core/kernel/clone_spec.rb b/spec/ruby/core/kernel/clone_spec.rb
new file mode 100644
index 0000000000..ed426c2927
--- /dev/null
+++ b/spec/ruby/core/kernel/clone_spec.rb
@@ -0,0 +1,118 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/dup_clone'
+
+describe "Kernel#clone" do
+ it_behaves_like :kernel_dup_clone, :clone
+
+ before :each do
+ ScratchPad.clear
+ @obj = KernelSpecs::Duplicate.new 1, :a
+ end
+
+ it "calls #initialize_copy on the new instance" do
+ clone = @obj.clone
+ ScratchPad.recorded.should_not == @obj.object_id
+ ScratchPad.recorded.should == clone.object_id
+ end
+
+ it "uses the internal allocator and does not call #allocate" do
+ klass = Class.new
+ instance = klass.new
+
+ def klass.allocate
+ raise "allocate should not be called"
+ end
+
+ clone = instance.clone
+ clone.class.should equal klass
+ end
+
+ it "copies frozen state from the original" do
+ o2 = @obj.clone
+ @obj.freeze
+ o3 = @obj.clone
+
+ o2.frozen?.should == false
+ o3.frozen?.should == true
+ end
+
+ ruby_version_is '2.4' do
+ it 'takes an option to copy freeze state or not' do
+ @obj.clone(freeze: true).frozen?.should == false
+ @obj.clone(freeze: false).frozen?.should == false
+ @obj.freeze
+ @obj.clone(freeze: true).frozen?.should == true
+ @obj.clone(freeze: false).frozen?.should == false
+ end
+ end
+
+ it "copies instance variables" do
+ clone = @obj.clone
+ clone.one.should == 1
+ clone.two.should == :a
+ end
+
+ it "copies singleton methods" do
+ def @obj.special() :the_one end
+ clone = @obj.clone
+ clone.special.should == :the_one
+ end
+
+ it "copies modules included in the singleton class" do
+ class << @obj
+ include KernelSpecs::DuplicateM
+ end
+
+ clone = @obj.clone
+ clone.repr.should == "KernelSpecs::Duplicate"
+ end
+
+ it "copies constants defined in the singleton class" do
+ class << @obj
+ CLONE = :clone
+ end
+
+ clone = @obj.clone
+ class << clone
+ CLONE.should == :clone
+ end
+ end
+
+ it "replaces a singleton object's metaclass with a new copy with the same superclass" do
+ cls = Class.new do
+ def bar
+ ['a']
+ end
+ end
+
+ object = cls.new
+ object.define_singleton_method(:bar) do
+ ['b', *super()]
+ end
+ object.bar.should == ['b', 'a']
+
+ cloned = object.clone
+
+ cloned.singleton_methods.should == [:bar]
+
+ # bar should replace previous one
+ cloned.define_singleton_method(:bar) do
+ ['c', *super()]
+ end
+ cloned.bar.should == ['c', 'a']
+
+ # bar should be removed and call through to superclass
+ cloned.singleton_class.class_eval do
+ remove_method :bar
+ end
+
+ cloned.bar.should == ['a']
+ end
+
+ it 'copies frozen? and tainted?' do
+ o = ''.taint.freeze.clone
+ o.frozen?.should be_true
+ o.tainted?.should be_true
+ end
+end
diff --git a/spec/ruby/core/kernel/comparison_spec.rb b/spec/ruby/core/kernel/comparison_spec.rb
new file mode 100644
index 0000000000..affdc5c00d
--- /dev/null
+++ b/spec/ruby/core/kernel/comparison_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+
+describe "Kernel#<=>" do
+ it "returns 0 if self" do
+ obj = Object.new
+ obj.<=>(obj).should == 0
+ end
+
+ it "returns 0 if self is == to the argument" do
+ obj = mock('has ==')
+ obj.should_receive(:==).and_return(true)
+ obj.<=>(Object.new).should == 0
+ end
+
+ it "returns nil if self is eql? but not == to the argument" do
+ obj = mock('has eql?')
+ obj.should_not_receive(:eql?)
+ obj.<=>(Object.new).should be_nil
+ end
+
+ it "returns nil if self.==(arg) returns nil" do
+ obj = mock('wrong ==')
+ obj.should_receive(:==).and_return(nil)
+ obj.<=>(Object.new).should be_nil
+ end
+
+ it "returns nil if self is not == to the argument" do
+ obj = Object.new
+ obj.<=>(3.14).should be_nil
+ end
+end
diff --git a/spec/ruby/core/kernel/define_singleton_method_spec.rb b/spec/ruby/core/kernel/define_singleton_method_spec.rb
new file mode 100644
index 0000000000..c2f2da7c79
--- /dev/null
+++ b/spec/ruby/core/kernel/define_singleton_method_spec.rb
@@ -0,0 +1,99 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#define_singleton_method" do
+ describe "when given an UnboundMethod" do
+ class DefineSingletonMethodSpecClass
+ MY_CONST = 42
+ define_singleton_method(:another_test_method, self.method(:constants))
+ end
+
+ it "correctly calls the new method" do
+ klass = DefineSingletonMethodSpecClass
+ klass.another_test_method.should == klass.constants
+ end
+
+ it "adds the new method to the methods list" do
+ DefineSingletonMethodSpecClass.should have_method(:another_test_method)
+ end
+
+ it "defines any Child class method from any Parent's class methods" do
+ um = KernelSpecs::Parent.method(:parent_class_method).unbind
+ KernelSpecs::Child.send :define_singleton_method, :child_class_method, um
+ KernelSpecs::Child.child_class_method.should == :foo
+ lambda{KernelSpecs::Parent.child_class_method}.should raise_error(NoMethodError)
+ end
+
+ it "will raise when attempting to define an object's singleton method from another object's singleton method" do
+ other = KernelSpecs::Parent.new
+ p = KernelSpecs::Parent.new
+ class << p
+ def singleton_method
+ :single
+ end
+ end
+ um = p.method(:singleton_method).unbind
+ lambda{ other.send :define_singleton_method, :other_singleton_method, um }.should raise_error(TypeError)
+ end
+
+ end
+
+ it "defines a new method with the given name and the given block as body in self" do
+ class DefineSingletonMethodSpecClass
+ define_singleton_method(:block_test1) { self }
+ define_singleton_method(:block_test2, &lambda { self })
+ end
+
+ o = DefineSingletonMethodSpecClass
+ o.block_test1.should == o
+ o.block_test2.should == o
+ end
+
+ it "raises a TypeError when the given method is no Method/Proc" do
+ lambda {
+ Class.new { define_singleton_method(:test, "self") }
+ }.should raise_error(TypeError)
+
+ lambda {
+ Class.new { define_singleton_method(:test, 1234) }
+ }.should raise_error(TypeError)
+ end
+
+ it "defines a new singleton method for objects" do
+ obj = Object.new
+ obj.define_singleton_method(:test) { "world!" }
+ obj.test.should == "world!"
+ lambda {
+ Object.new.test
+ }.should raise_error(NoMethodError)
+ end
+
+ it "maintains the Proc's scope" do
+ class DefineMethodByProcClass
+ in_scope = true
+ method_proc = proc { in_scope }
+
+ define_singleton_method(:proc_test, &method_proc)
+ end
+
+ DefineMethodByProcClass.proc_test.should == true
+ end
+
+ it "raises an ArgumentError when no block is given" do
+ obj = Object.new
+ lambda {
+ obj.define_singleton_method(:test)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "does not use the caller block when no block is given" do
+ o = Object.new
+ def o.define(name)
+ define_singleton_method(name)
+ end
+
+ lambda {
+ o.define(:foo) { raise "not used" }
+ }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/kernel/display_spec.rb b/spec/ruby/core/kernel/display_spec.rb
new file mode 100644
index 0000000000..9d429a9fac
--- /dev/null
+++ b/spec/ruby/core/kernel/display_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#display" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/dup_spec.rb b/spec/ruby/core/kernel/dup_spec.rb
new file mode 100644
index 0000000000..ce81f7018a
--- /dev/null
+++ b/spec/ruby/core/kernel/dup_spec.rb
@@ -0,0 +1,67 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/dup_clone'
+
+describe "Kernel#dup" do
+ it_behaves_like :kernel_dup_clone, :dup
+
+ before :each do
+ ScratchPad.clear
+ @obj = KernelSpecs::Duplicate.new 1, :a
+ end
+
+ it "calls #initialize_copy on the new instance" do
+ dup = @obj.dup
+ ScratchPad.recorded.should_not == @obj.object_id
+ ScratchPad.recorded.should == dup.object_id
+ end
+
+ it "uses the internal allocator and does not call #allocate" do
+ klass = Class.new
+ instance = klass.new
+
+ def klass.allocate
+ raise "allocate should not be called"
+ end
+
+ dup = instance.dup
+ dup.class.should equal klass
+ end
+
+ it "does not copy frozen state from the original" do
+ @obj.freeze
+ dup = @obj.dup
+
+ dup.frozen?.should == false
+ end
+
+ it "copies instance variables" do
+ dup = @obj.dup
+ dup.one.should == 1
+ dup.two.should == :a
+ end
+
+ it "does not copy singleton methods" do
+ def @obj.special() :the_one end
+ dup = @obj.dup
+ lambda { dup.special }.should raise_error(NameError)
+ end
+
+ it "does not copy modules included in the singleton class" do
+ class << @obj
+ include KernelSpecs::DuplicateM
+ end
+
+ dup = @obj.dup
+ lambda { dup.repr }.should raise_error(NameError)
+ end
+
+ it "does not copy constants defined in the singleton class" do
+ class << @obj
+ CLONE = :clone
+ end
+
+ dup = @obj.dup
+ lambda { class << dup; CLONE; end }.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/core/kernel/enum_for_spec.rb b/spec/ruby/core/kernel/enum_for_spec.rb
new file mode 100644
index 0000000000..0092e20468
--- /dev/null
+++ b/spec/ruby/core/kernel/enum_for_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Kernel#enum_for" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/eql_spec.rb b/spec/ruby/core/kernel/eql_spec.rb
new file mode 100644
index 0000000000..e62a601a79
--- /dev/null
+++ b/spec/ruby/core/kernel/eql_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/kernel/equal'
+
+describe "Kernel#eql?" do
+ it "is a public instance method" do
+ Kernel.should have_public_instance_method(:eql?)
+ end
+
+ it_behaves_like :object_equal, :eql?
+end
diff --git a/spec/ruby/core/kernel/equal_value_spec.rb b/spec/ruby/core/kernel/equal_value_spec.rb
new file mode 100644
index 0000000000..2be151263d
--- /dev/null
+++ b/spec/ruby/core/kernel/equal_value_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#==" do
+ it "returns true only if obj and other are the same object" do
+ o1 = mock('o1')
+ o2 = mock('o2')
+ (o1 == o1).should == true
+ (o2 == o2).should == true
+ (o1 == o2).should == false
+ (nil == nil).should == true
+ (o1 == nil).should == false
+ (nil == o2).should == false
+ end
+end
diff --git a/spec/ruby/core/kernel/eval_spec.rb b/spec/ruby/core/kernel/eval_spec.rb
new file mode 100644
index 0000000000..46158628a3
--- /dev/null
+++ b/spec/ruby/core/kernel/eval_spec.rb
@@ -0,0 +1,345 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+EvalSpecs::A.new.c
+
+describe "Kernel#eval" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:eval)
+ end
+
+ it "is a module function" do
+ Kernel.respond_to?(:eval).should == true
+ end
+
+ it "evaluates the code within" do
+ eval("2 + 3").should == 5
+ end
+
+ it "coerces an object to string" do
+ eval(EvalSpecs::CoercedObject.new).should == 5
+ end
+
+ it "evaluates within the scope of the eval" do
+ EvalSpecs::A::B.name.should == "EvalSpecs::A::B"
+ end
+
+ it "evaluates such that consts are scoped to the class of the eval" do
+ EvalSpecs::A::C.name.should == "EvalSpecs::A::C"
+ end
+
+ it "finds a local in an enclosing scope" do
+ a = 1
+ eval("a").should == 1
+ end
+
+ it "updates a local in an enclosing scope" do
+ a = 1
+ eval("a = 2")
+ a.should == 2
+ end
+
+ it "updates a local in a surrounding block scope" do
+ EvalSpecs.new.f do
+ a = 1
+ eval("a = 2")
+ a.should == 2
+ end
+ end
+
+ it "updates a local in a scope above a surrounding block scope" do
+ a = 1
+ EvalSpecs.new.f do
+ eval("a = 2")
+ a.should == 2
+ end
+ a.should == 2
+ end
+
+ it "updates a local in a scope above when modified in a nested block scope" do
+ a = 1
+ es = EvalSpecs.new
+ eval("es.f { es.f { a = 2 } }")
+ a.should == 2
+ end
+
+ it "finds locals in a nested eval" do
+ eval('test = 10; eval("test")').should == 10
+ end
+
+ it "does not share locals across eval scopes" do
+ code = fixture __FILE__, "eval_locals.rb"
+ ruby_exe(code).chomp.should == "NameError"
+ end
+
+ it "doesn't accept a Proc object as a binding" do
+ x = 1
+ bind = proc {}
+
+ lambda { eval("x", bind) }.should raise_error(TypeError)
+ end
+
+ it "does not make Proc locals visible to evaluated code" do
+ bind = proc { inner = 4 }
+ lambda { eval("inner", bind.binding) }.should raise_error(NameError)
+ end
+
+ # REWRITE ME: This obscures the real behavior of where locals are stored
+ # in eval bindings.
+ it "allows a binding to be captured inside an eval" do
+ outer_binding = binding
+ level1 = eval("binding", outer_binding)
+ level2 = eval("binding", level1)
+
+ eval("x = 2", outer_binding)
+ eval("y = 3", level1)
+
+ eval("w=1", outer_binding)
+ eval("w", outer_binding).should == 1
+ eval("w=1", level1).should == 1
+ eval("w", level1).should == 1
+ eval("w=1", level2).should == 1
+ eval("w", level2).should == 1
+
+ eval("x", outer_binding).should == 2
+ eval("x=2", level1)
+ eval("x", level1).should == 2
+ eval("x=2", level2)
+ eval("x", level2).should == 2
+
+ eval("y=3", outer_binding)
+ eval("y", outer_binding).should == 3
+ eval("y=3", level1)
+ eval("y", level1).should == 3
+ eval("y=3", level2)
+ eval("y", level2).should == 3
+ end
+
+ it "uses the same scope for local variables when given the same binding" do
+ outer_binding = binding
+
+ eval("if false; a = 1; end", outer_binding)
+ eval("a", outer_binding).should be_nil
+ end
+
+ it "allows creating a new class in a binding" do
+ bind = proc {}
+ eval("class EvalBindingProcA; end; EvalBindingProcA.name", bind.binding).should =~ /EvalBindingProcA$/
+ end
+
+ it "allows creating a new class in a binding created by #eval" do
+ bind = eval "binding"
+ eval("class EvalBindingA; end; EvalBindingA.name", bind).should =~ /EvalBindingA$/
+ end
+
+ it "includes file and line information in syntax error" do
+ expected = 'speccing.rb'
+ lambda {
+ eval('if true',TOPLEVEL_BINDING, expected)
+ }.should raise_error(SyntaxError) { |e|
+ e.message.should =~ /#{expected}:1:.+/
+ }
+ end
+
+ it "evaluates string with given filename and negative linenumber" do
+ expected_file = 'speccing.rb'
+ lambda {
+ eval('if true',TOPLEVEL_BINDING, expected_file, -100)
+ }.should raise_error(SyntaxError) { |e|
+ e.message.should =~ /#{expected_file}:-100:.+/
+ }
+ end
+
+ it "sets constants at the toplevel from inside a block" do
+ # The class Object bit is needed to workaround some mspec oddness
+ class Object
+ [1].each { eval "Const = 1"}
+ Const.should == 1
+ remove_const :Const
+ end
+ end
+
+ it "uses the filename of the binding if none is provided" do
+ eval("__FILE__").should == "(eval)"
+ eval("__FILE__", binding).should == __FILE__
+ eval("__FILE__", binding, "success").should == "success"
+ eval("eval '__FILE__', binding").should == "(eval)"
+ eval("eval '__FILE__', binding", binding).should == __FILE__
+ eval("eval '__FILE__', binding", binding, 'success').should == 'success'
+ end
+
+ # Found via Rubinius bug github:#149
+ it "does not alter the value of __FILE__ in the binding" do
+ first_time = EvalSpecs.call_eval
+ second_time = EvalSpecs.call_eval
+
+ # This bug is seen by calling the method twice and comparing the values
+ # of __FILE__ each time. If the bug is present, calling eval will set the
+ # value of __FILE__ to the eval's "filename" argument.
+
+ second_time.should_not == "(eval)"
+ first_time.should == second_time
+ end
+
+ it "can be aliased" do
+ alias aliased_eval eval
+ x = 2
+ aliased_eval('x += 40')
+ x.should == 42
+ end
+
+ # See http://jira.codehaus.org/browse/JRUBY-5163
+ it "uses the receiver as self inside the eval" do
+ eval("self").should equal(self)
+ Kernel.eval("self").should equal(Kernel)
+ end
+
+ it "does not pass the block to the method being eval'ed" do
+ lambda {
+ eval('KernelSpecs::EvalTest.call_yield') { "content" }
+ }.should raise_error(LocalJumpError)
+ end
+
+ it "returns from the scope calling #eval when evaluating 'return'" do
+ lambda { eval("return :eval") }.call.should == :eval
+ end
+
+ it "unwinds through a Proc-style closure and returns from a lambda-style closure in the closure chain" do
+ code = fixture __FILE__, "eval_return_with_lambda.rb"
+ ruby_exe(code).chomp.should == "a,b,c,eval,f"
+ end
+
+ it "raises a LocalJumpError if there is no lambda-style closure in the chain" do
+ code = fixture __FILE__, "eval_return_without_lambda.rb"
+ ruby_exe(code).chomp.should == "a,b,c,e,LocalJumpError,f"
+ end
+
+ # See language/magic_comment_spec.rb for more magic comments specs
+ describe "with a magic encoding comment" do
+ it "uses the magic comment encoding for the encoding of literal strings" do
+ code = "# encoding: UTF-8\n'é'.encoding".b
+ code.encoding.should == Encoding::BINARY
+ eval(code).should == Encoding::UTF_8
+ end
+
+ it "uses the magic comment encoding for parsing constants" do
+ code = <<CODE.b
+# encoding: UTF-8
+class EvalSpecs
+ VÏ€ = 3.14
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should include(:"VÏ€")
+ EvalSpecs::VÏ€.should == 3.14
+ end
+
+ it "allows an emacs-style magic comment encoding" do
+ code = <<CODE.b
+# -*- encoding: UTF-8 -*-
+class EvalSpecs
+VÏ€emacs = 3.14
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should include(:"VÏ€emacs")
+ EvalSpecs::VÏ€emacs.should == 3.14
+ end
+
+ it "allows spaces before the magic encoding comment" do
+ code = <<CODE.b
+\t \t # encoding: UTF-8
+class EvalSpecs
+ VÏ€spaces = 3.14
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should include(:"VÏ€spaces")
+ EvalSpecs::VÏ€spaces.should == 3.14
+ end
+
+ it "allows a shebang line before the magic encoding comment" do
+ code = <<CODE.b
+#!/usr/bin/env ruby
+# encoding: UTF-8
+class EvalSpecs
+ VÏ€shebang = 3.14
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should include(:"VÏ€shebang")
+ EvalSpecs::VÏ€shebang.should == 3.14
+ end
+
+ it "allows a shebang line and some spaces before the magic encoding comment" do
+ code = <<CODE.b
+#!/usr/bin/env ruby
+ # encoding: UTF-8
+class EvalSpecs
+ VÏ€shebang_spaces = 3.14
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should include(:"VÏ€shebang_spaces")
+ EvalSpecs::VÏ€shebang_spaces.should == 3.14
+ end
+
+ it "allows a magic encoding comment and a subsequent frozen_string_literal magic comment" do
+ # Make sure frozen_string_literal is not default true
+ eval("'foo'".b).frozen?.should be_false
+
+ code = <<CODE.b
+# encoding: UTF-8
+# frozen_string_literal: true
+class EvalSpecs
+ VÏ€string = "frozen"
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should include(:"VÏ€string")
+ EvalSpecs::VÏ€string.should == "frozen"
+ EvalSpecs::VÏ€string.encoding.should == Encoding::UTF_8
+ EvalSpecs::VÏ€string.frozen?.should be_true
+ end
+
+ it "allows a magic encoding comment and a frozen_string_literal magic comment on the same line in emacs style" do
+ code = <<CODE.b
+# -*- encoding: UTF-8; frozen_string_literal: true -*-
+class EvalSpecs
+VÏ€same_line = "frozen"
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should include(:"VÏ€same_line")
+ EvalSpecs::VÏ€same_line.should == "frozen"
+ EvalSpecs::VÏ€same_line.encoding.should == Encoding::UTF_8
+ EvalSpecs::VÏ€same_line.frozen?.should be_true
+ end
+
+ it "ignores the magic encoding comment if it is after a frozen_string_literal magic comment" do
+ code = <<CODE.b
+# frozen_string_literal: true
+# encoding: UTF-8
+class EvalSpecs
+ VÏ€frozen_first = "frozen"
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should_not include(:"VÏ€frozen_first")
+ binary_constant = "VÏ€frozen_first".b.to_sym
+ EvalSpecs.constants(false).should include(binary_constant)
+ value = EvalSpecs.const_get(binary_constant)
+ value.should == "frozen"
+ value.encoding.should == Encoding::BINARY
+ value.frozen?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/exec_spec.rb b/spec/ruby/core/kernel/exec_spec.rb
new file mode 100644
index 0000000000..1b4a7ae6f4
--- /dev/null
+++ b/spec/ruby/core/kernel/exec_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#exec" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:exec)
+ end
+
+ it "runs the specified command, replacing current process" do
+ ruby_exe('exec "echo hello"; puts "fail"', escape: true).should == "hello\n"
+ end
+end
+
+describe "Kernel.exec" do
+ it "runs the specified command, replacing current process" do
+ ruby_exe('Kernel.exec "echo hello"; puts "fail"', escape: true).should == "hello\n"
+ end
+end
diff --git a/spec/ruby/core/kernel/exit_spec.rb b/spec/ruby/core/kernel/exit_spec.rb
new file mode 100644
index 0000000000..f168cb375e
--- /dev/null
+++ b/spec/ruby/core/kernel/exit_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/process/exit'
+
+describe "Kernel#exit" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:exit)
+ end
+
+ it_behaves_like :process_exit, :exit, KernelSpecs::Method.new
+end
+
+describe "Kernel#exit!" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:exit!)
+ end
+
+ it_behaves_like :process_exit!, :exit!, "self"
+end
+
+describe "Kernel.exit" do
+ it_behaves_like :process_exit, :exit, Kernel
+end
+
+describe "Kernel.exit!" do
+ it_behaves_like :process_exit!, :exit!, Kernel
+end
diff --git a/spec/ruby/core/kernel/extend_spec.rb b/spec/ruby/core/kernel/extend_spec.rb
new file mode 100644
index 0000000000..2dbc224177
--- /dev/null
+++ b/spec/ruby/core/kernel/extend_spec.rb
@@ -0,0 +1,79 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+module KernelSpecs::M
+ def self.extend_object(o)
+ ScratchPad << "extend_object"
+ super
+ end
+
+ def self.extended(o)
+ ScratchPad << "extended"
+ super
+ end
+
+ def self.append_features(o)
+ ScratchPad << "append_features"
+ super
+ end
+end
+
+describe "Kernel#extend" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "requires multiple arguments" do
+ Object.new.method(:extend).arity.should < 0
+ end
+
+ it "calls extend_object on argument" do
+ o = mock('o')
+ o.extend KernelSpecs::M
+ ScratchPad.recorded.include?("extend_object").should == true
+ end
+
+ it "does not calls append_features on arguments metaclass" do
+ o = mock('o')
+ o.extend KernelSpecs::M
+ ScratchPad.recorded.include?("append_features").should == false
+ end
+
+ it "calls extended on argument" do
+ o = mock('o')
+ o.extend KernelSpecs::M
+ ScratchPad.recorded.include?("extended").should == true
+ end
+
+ it "makes the class a kind_of? the argument" do
+ class C
+ extend KernelSpecs::M
+ end
+ (C.kind_of? KernelSpecs::M).should == true
+ end
+
+ it "raises an ArgumentError when no arguments given" do
+ lambda { Object.new.extend }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError when the argument is not a Module" do
+ o = mock('o')
+ klass = Class.new
+ lambda { o.extend(klass) }.should raise_error(TypeError)
+ end
+
+ describe "on frozen instance" do
+ before :each do
+ @frozen = Object.new.freeze
+ @module = KernelSpecs::M
+ end
+
+ it "raises an ArgumentError when no arguments given" do
+ lambda { @frozen.extend }.should raise_error(ArgumentError)
+ end
+
+ it "raises a #{frozen_error_class}" do
+ lambda { @frozen.extend @module }.should raise_error(frozen_error_class)
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/fail_spec.rb b/spec/ruby/core/kernel/fail_spec.rb
new file mode 100644
index 0000000000..01dffa4a69
--- /dev/null
+++ b/spec/ruby/core/kernel/fail_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel.fail" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:fail)
+ end
+
+ it "raises a RuntimeError" do
+ lambda { fail }.should raise_error(RuntimeError)
+ end
+
+ it "accepts an Object with an exception method returning an Exception" do
+ class Boring
+ def self.exception(msg)
+ StandardError.new msg
+ end
+ end
+ lambda { fail Boring, "..." }.should raise_error(StandardError)
+ end
+
+ it "instantiates the specified exception class" do
+ class LittleBunnyFooFoo < RuntimeError; end
+ lambda { fail LittleBunnyFooFoo }.should raise_error(LittleBunnyFooFoo)
+ end
+
+ it "uses the specified message" do
+ lambda {
+ begin
+ fail "the duck is not irish."
+ rescue => e
+ e.message.should == "the duck is not irish."
+ raise
+ else
+ raise Exception
+ end
+ }.should raise_error(RuntimeError)
+ end
+end
+
+describe "Kernel#fail" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/fixtures/__callee__.rb b/spec/ruby/core/kernel/fixtures/__callee__.rb
new file mode 100644
index 0000000000..7138dbc5aa
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/__callee__.rb
@@ -0,0 +1,34 @@
+module KernelSpecs
+ class CalleeTest
+ def f
+ __callee__
+ end
+
+ alias_method :g, :f
+
+ def in_block
+ (1..2).map { __callee__ }
+ end
+
+ define_method(:dm) do
+ __callee__
+ end
+
+ define_method(:dm_block) do
+ (1..2).map { __callee__ }
+ end
+
+ def from_send
+ send "__callee__"
+ end
+
+ def from_eval
+ eval "__callee__"
+ end
+
+ @@method = __callee__
+ def from_class_body
+ @@method
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/fixtures/__method__.rb b/spec/ruby/core/kernel/fixtures/__method__.rb
new file mode 100644
index 0000000000..9300366b37
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/__method__.rb
@@ -0,0 +1,34 @@
+module KernelSpecs
+ class MethodTest
+ def f
+ __method__
+ end
+
+ alias_method :g, :f
+
+ def in_block
+ (1..2).map { __method__ }
+ end
+
+ define_method(:dm) do
+ __method__
+ end
+
+ define_method(:dm_block) do
+ (1..2).map { __method__ }
+ end
+
+ def from_send
+ send "__method__"
+ end
+
+ def from_eval
+ eval "__method__"
+ end
+
+ @@method = __method__
+ def from_class_body
+ @@method
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/fixtures/autoload_b.rb b/spec/ruby/core/kernel/fixtures/autoload_b.rb
new file mode 100644
index 0000000000..e8be221ec7
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/autoload_b.rb
@@ -0,0 +1,5 @@
+module KSAutoloadB
+ def self.loaded
+ :ksautoload_b
+ end
+end
diff --git a/spec/ruby/core/kernel/fixtures/autoload_d.rb b/spec/ruby/core/kernel/fixtures/autoload_d.rb
new file mode 100644
index 0000000000..552cb5e82c
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/autoload_d.rb
@@ -0,0 +1,5 @@
+module KSAutoloadD
+ def self.loaded
+ :ksautoload_d
+ end
+end
diff --git a/spec/ruby/core/kernel/fixtures/autoload_from_included_module.rb b/spec/ruby/core/kernel/fixtures/autoload_from_included_module.rb
new file mode 100644
index 0000000000..f5bedc6992
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/autoload_from_included_module.rb
@@ -0,0 +1,9 @@
+module KernelSpecs
+ module AutoloadMethod
+ module AutoloadFromIncludedModule
+ def self.loaded
+ :autoload_from_included_module
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/fixtures/autoload_from_included_module2.rb b/spec/ruby/core/kernel/fixtures/autoload_from_included_module2.rb
new file mode 100644
index 0000000000..f4f1cfbf7c
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/autoload_from_included_module2.rb
@@ -0,0 +1,9 @@
+module KernelSpecs
+ module AutoloadMethod2
+ module AutoloadFromIncludedModule2
+ def self.loaded
+ :autoload_from_included_module2
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/fixtures/autoload_frozen.rb b/spec/ruby/core/kernel/fixtures/autoload_frozen.rb
new file mode 100644
index 0000000000..e9dc42912b
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/autoload_frozen.rb
@@ -0,0 +1,7 @@
+Object.freeze
+
+begin
+ autoload :ANY_CONSTANT, "no_autoload.rb"
+rescue Exception => e
+ print e.class, " - ", defined?(ANY_CONSTANT).inspect
+end
diff --git a/spec/ruby/core/kernel/fixtures/caller.rb b/spec/ruby/core/kernel/fixtures/caller.rb
new file mode 100644
index 0000000000..ae3e13e9c9
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/caller.rb
@@ -0,0 +1,7 @@
+module KernelSpecs
+ class CallerTest
+ def self.locations(*args)
+ caller(*args)
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/fixtures/caller_at_exit.rb b/spec/ruby/core/kernel/fixtures/caller_at_exit.rb
new file mode 100644
index 0000000000..ca9d808597
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/caller_at_exit.rb
@@ -0,0 +1,7 @@
+at_exit {
+ foo
+}
+
+def foo
+ puts caller(0)
+end
diff --git a/spec/ruby/core/kernel/fixtures/caller_locations.rb b/spec/ruby/core/kernel/fixtures/caller_locations.rb
new file mode 100644
index 0000000000..cc4e04d38f
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/caller_locations.rb
@@ -0,0 +1,7 @@
+module KernelSpecs
+ class CallerLocationsTest
+ def self.locations(*args)
+ caller_locations(*args)
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/fixtures/chomp.rb b/spec/ruby/core/kernel/fixtures/chomp.rb
new file mode 100644
index 0000000000..f08dbadce5
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/chomp.rb
@@ -0,0 +1,4 @@
+# -*- encoding: utf-8 -*-
+
+$_ = "ã‚れ\r\n"
+print Kernel.chomp
diff --git a/spec/ruby/core/kernel/fixtures/chomp_f.rb b/spec/ruby/core/kernel/fixtures/chomp_f.rb
new file mode 100644
index 0000000000..3c22cb21e7
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/chomp_f.rb
@@ -0,0 +1,4 @@
+# -*- encoding: utf-8 -*-
+
+$_ = "ã‚れ\r\n"
+print chomp
diff --git a/spec/ruby/core/kernel/fixtures/chop.rb b/spec/ruby/core/kernel/fixtures/chop.rb
new file mode 100644
index 0000000000..dfd0626723
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/chop.rb
@@ -0,0 +1,4 @@
+# -*- encoding: utf-8 -*-
+
+$_ = "ã‚れ"
+print Kernel.chop
diff --git a/spec/ruby/core/kernel/fixtures/chop_f.rb b/spec/ruby/core/kernel/fixtures/chop_f.rb
new file mode 100644
index 0000000000..4ec89eb9ec
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/chop_f.rb
@@ -0,0 +1,4 @@
+# -*- encoding: utf-8 -*-
+
+$_ = "ã‚れ"
+print chop
diff --git a/spec/ruby/core/kernel/fixtures/classes.rb b/spec/ruby/core/kernel/fixtures/classes.rb
new file mode 100644
index 0000000000..1f45bbb083
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/classes.rb
@@ -0,0 +1,465 @@
+module KernelSpecs
+ def self.Array_function(arg)
+ Array(arg)
+ end
+
+ def self.Array_method(arg)
+ Kernel.Array(arg)
+ end
+
+ def self.Hash_function(arg)
+ Hash(arg)
+ end
+
+ def self.Hash_method(arg)
+ Kernel.Hash(arg)
+ end
+
+ def self.Integer_function(arg)
+ Integer(arg)
+ end
+
+ def self.Integer_method(arg)
+ Kernel.Integer(arg)
+ end
+
+ def self.putc_function(arg)
+ putc arg
+ end
+
+ def self.putc_method(arg)
+ Kernel.putc arg
+ end
+
+ def self.has_private_method(name)
+ IO.popen([*ruby_exe, "-n", "-e", "print Kernel.private_method_defined?(#{name.inspect})"], "r+") do |io|
+ io.puts
+ io.close_write
+ io.read
+ end == "true"
+ end
+
+ def self.chop(str, method)
+ IO.popen([*ruby_exe, "-n", "-e", "$_ = #{str.inspect}; #{method}; print $_"], "r+") do |io|
+ io.puts
+ io.close_write
+ io.read
+ end
+ end
+
+ def self.chomp(str, method, sep="\n")
+ code = "$_ = #{str.inspect}; $/ = #{sep.inspect}; #{method}; print $_"
+ IO.popen([*ruby_exe, "-n", "-e", code], "r+") do |io|
+ io.puts
+ io.close_write
+ io.read
+ end
+ end
+
+ def self.run_with_dash_n(file)
+ IO.popen([*ruby_exe, "-n", file], "r+") do |io|
+ io.puts
+ io.close_write
+ io.read
+ end
+ end
+
+ # kind_of?, is_a?, instance_of?
+ module SomeOtherModule; end
+ module AncestorModule; end
+ module MyModule; end
+ module MyPrependedModule; end
+ module MyExtensionModule; end
+
+ class AncestorClass < String
+ include AncestorModule
+ end
+
+ class InstanceClass < AncestorClass
+ include MyModule
+ end
+
+ class KindaClass < AncestorClass
+ include MyModule
+ prepend MyPrependedModule
+
+ def initialize
+ self.extend MyExtensionModule
+ end
+ end
+
+ class Method
+ public :abort, :exit, :exit!, :fork, :system
+ end
+
+ class Methods
+
+ module MetaclassMethods
+ def peekaboo
+ end
+
+ protected
+
+ def nopeeking
+ end
+
+ private
+
+ def shoo
+ end
+ end
+
+ def self.ichi; end
+ def ni; end
+ class << self
+ def san; end
+ end
+
+ private
+
+ def self.shi; end
+ def juu_shi; end
+
+ class << self
+ def roku; end
+
+ private
+
+ def shichi; end
+ end
+
+ protected
+
+ def self.hachi; end
+ def ku; end
+
+ class << self
+ def juu; end
+
+ protected
+
+ def juu_ichi; end
+ end
+
+ public
+
+ def self.juu_ni; end
+ def juu_san; end
+ end
+
+ class PrivateSup
+ def public_in_sub
+ end
+
+ private :public_in_sub
+ end
+
+ class PublicSub < PrivateSup
+ def public_in_sub
+ end
+ end
+
+ class A
+ # There is Kernel#public_method, so we don't want this one to clash
+ def pub_method; :public_method; end
+
+ def undefed_method; :undefed_method; end
+ undef_method :undefed_method
+
+ protected
+ def protected_method; :protected_method; end
+
+ private
+ def private_method; :private_method; end
+
+ public
+ define_method(:defined_method) { :defined }
+ end
+
+ class B < A
+ alias aliased_pub_method pub_method
+ end
+
+ class VisibilityChange
+ class << self
+ private :new
+ end
+ end
+
+ class Binding
+ @@super_secret = "password"
+
+ def initialize(n)
+ @secret = n
+ end
+
+ def square(n)
+ n * n
+ end
+
+ def get_binding
+ a = true
+ @bind = binding
+
+ # Add/Change stuff
+ b = true
+ @secret += 1
+
+ @bind
+ end
+ end
+
+
+ module BlockGiven
+ def self.accept_block
+ block_given?
+ end
+
+ def self.accept_block_as_argument(&block)
+ block_given?
+ end
+
+ class << self
+ define_method(:defined_block) do
+ block_given?
+ end
+ end
+ end
+
+ module SelfBlockGiven
+ def self.accept_block
+ self.send(:block_given?)
+ end
+
+ def self.accept_block_as_argument(&block)
+ self.send(:block_given?)
+ end
+
+ class << self
+ define_method(:defined_block) do
+ self.send(:block_given?)
+ end
+ end
+ end
+
+ module KernelBlockGiven
+ def self.accept_block
+ Kernel.block_given?
+ end
+
+ def self.accept_block_as_argument(&block)
+ Kernel.block_given?
+ end
+
+ class << self
+ define_method(:defined_block) do
+ Kernel.block_given?
+ end
+ end
+ end
+
+ class EvalTest
+ def self.eval_yield_with_binding
+ eval("yield", binding)
+ end
+ def self.call_yield
+ yield
+ end
+ end
+
+ module DuplicateM
+ def repr
+ self.class.name.to_s
+ end
+ end
+
+ class Duplicate
+ attr_accessor :one, :two
+
+ def initialize(one, two)
+ @one = one
+ @two = two
+ end
+
+ def initialize_copy(other)
+ ScratchPad.record object_id
+ end
+ end
+
+ class Clone
+ def initialize_clone(other)
+ ScratchPad.record other.object_id
+ end
+ end
+
+ class Dup
+ def initialize_dup(other)
+ ScratchPad.record other.object_id
+ end
+ end
+
+ module ParentMixin
+ def parent_mixin_method; end
+ end
+
+ class Parent
+ include ParentMixin
+ def parent_method; end
+ def another_parent_method; end
+ def self.parent_class_method; :foo; end
+ end
+
+ class Child < Parent
+ undef_method :parent_method
+ end
+
+ class Grandchild < Child
+ undef_method :parent_mixin_method
+ end
+
+ # for testing lambda
+ class Lambda
+ def outer
+ inner
+ end
+
+ def mp(&b); b; end
+
+ def inner
+ b = mp { return :good }
+
+ pr = lambda { |x| x.call }
+
+ pr.call(b)
+
+ # We shouldn't be here, b should have unwinded through
+ return :bad
+ end
+ end
+
+ class RespondViaMissing
+ def respond_to_missing?(method, priv=false)
+ case method
+ when :handled_publicly
+ true
+ when :handled_privately
+ priv
+ when :not_handled
+ false
+ else
+ raise "Typo in method name"
+ end
+ end
+
+ def method_missing(method, *args)
+ "Done #{method}(#{args})"
+ end
+ end
+
+ class InstanceVariable
+ def initialize
+ @greeting = "hello"
+ end
+ end
+
+ class PrivateToAry
+ private
+
+ def to_ary
+ [1, 2]
+ end
+
+ def to_a
+ [3, 4]
+ end
+ end
+
+ class PrivateToA
+ private
+
+ def to_a
+ [3, 4]
+ end
+ end
+
+ module AutoloadMethod
+ def setup_autoload(file)
+ autoload :AutoloadFromIncludedModule, file
+ end
+ end
+
+ class AutoloadMethodIncluder
+ include AutoloadMethod
+ end
+
+ module AutoloadMethod2
+ def setup_autoload(file)
+ Kernel.autoload :AutoloadFromIncludedModule2, file
+ end
+ end
+
+ class AutoloadMethodIncluder2
+ include AutoloadMethod2
+ end
+
+ class WarnInNestedCall
+ def f4(s = "", n)
+ f3(s, n)
+ end
+
+ def f3(s, n)
+ f2(s, n)
+ end
+
+ def f2(s, n)
+ f1(s, n)
+ end
+
+ def f1(s, n)
+ warn(s, uplevel: n)
+ end
+
+ def warn_call_lineno; method(:f1).source_location[1] + 1; end
+ def f1_call_lineno; method(:f2).source_location[1] + 1; end
+ def f2_call_lineno; method(:f3).source_location[1] + 1; end
+ def f3_call_lineno; method(:f4).source_location[1] + 1; end
+ end
+end
+
+class EvalSpecs
+ class A
+ eval "class B; end"
+ def c
+ eval "class C; end"
+ end
+ end
+
+ class CoercedObject
+ def to_str
+ '2 + 3'
+ end
+
+ def hash
+ nil
+ end
+ end
+
+ def f
+ yield
+ end
+
+ def self.call_eval
+ f = __FILE__
+ eval "true", binding, "(eval)", 1
+ return f
+ end
+end
+
+# for Kernel#sleep to have Channel in it's specs
+# TODO: switch directly to queue for both Kernel#sleep and Thread specs?
+unless defined? Channel
+ require 'thread'
+ class Channel < Queue
+ alias receive shift
+ end
+end
diff --git a/spec/ruby/core/kernel/fixtures/eval_locals.rb b/spec/ruby/core/kernel/fixtures/eval_locals.rb
new file mode 100644
index 0000000000..ca8b381806
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/eval_locals.rb
@@ -0,0 +1,6 @@
+begin
+ eval("a = 2")
+ eval("p a")
+rescue Object => e
+ puts e.class
+end
diff --git a/spec/ruby/core/kernel/fixtures/eval_return_with_lambda.rb b/spec/ruby/core/kernel/fixtures/eval_return_with_lambda.rb
new file mode 100644
index 0000000000..a48b5685f3
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/eval_return_with_lambda.rb
@@ -0,0 +1,12 @@
+print "a,"
+x = lambda do
+ print "b,"
+ Proc.new do
+ print "c,"
+ eval("return :eval")
+ print "d,"
+ end.call
+ print "e,"
+end.call
+print x, ","
+print "f"
diff --git a/spec/ruby/core/kernel/fixtures/eval_return_without_lambda.rb b/spec/ruby/core/kernel/fixtures/eval_return_without_lambda.rb
new file mode 100644
index 0000000000..fc8b7f1d4a
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/eval_return_without_lambda.rb
@@ -0,0 +1,14 @@
+print "a,"
+begin
+ print "b,"
+ x = Proc.new do
+ print "c,"
+ eval("return :eval")
+ print "d,"
+ end.call
+ print x, ","
+rescue LocalJumpError => e
+ print "e,"
+ print e.class, ","
+end
+print "f"
diff --git a/spec/ruby/core/kernel/fixtures/singleton_methods.rb b/spec/ruby/core/kernel/fixtures/singleton_methods.rb
new file mode 100644
index 0000000000..32ea0adf89
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/singleton_methods.rb
@@ -0,0 +1,13 @@
+module SingletonMethodsSpecs
+ module Prepended
+ def mspec_test_kernel_singleton_methods
+ end
+ public :mspec_test_kernel_singleton_methods
+ end
+
+ ::Module.prepend Prepended
+
+ module SelfExtending
+ extend self
+ end
+end
diff --git a/spec/ruby/core/kernel/fixtures/test.rb b/spec/ruby/core/kernel/fixtures/test.rb
new file mode 100644
index 0000000000..949948606f
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/test.rb
@@ -0,0 +1,362 @@
+def foo1
+end
+
+def foo2
+end
+
+def foo3
+end
+
+def foo4
+end
+
+def foo5
+end
+
+def foo6
+end
+
+def foo7
+end
+
+def foo8
+end
+
+def foo9
+end
+
+def foo10
+end
+
+def foo11
+end
+
+def foo12
+end
+
+def foo13
+end
+
+def foo14
+end
+
+def foo15
+end
+
+def foo16
+end
+
+def foo17
+end
+
+def foo18
+end
+
+def foo19
+end
+
+def foo20
+end
+
+def foo21
+end
+
+def foo22
+end
+
+def foo23
+end
+
+def foo24
+end
+
+def foo25
+end
+
+def foo26
+end
+
+def foo27
+end
+
+def foo28
+end
+
+def foo29
+end
+
+def foo30
+end
+
+def foo31
+end
+
+def foo32
+end
+
+def foo33
+end
+
+def foo34
+end
+
+def foo35
+end
+
+def foo36
+end
+
+def foo37
+end
+
+def foo38
+end
+
+def foo39
+end
+
+def foo40
+end
+
+def foo41
+end
+
+def foo42
+end
+
+def foo43
+end
+
+def foo44
+end
+
+def foo45
+end
+
+def foo46
+end
+
+def foo47
+end
+
+def foo48
+end
+
+def foo49
+end
+
+def foo50
+end
+
+def foo51
+end
+
+def foo52
+end
+
+def foo53
+end
+
+def foo54
+end
+
+def foo55
+end
+
+def foo56
+end
+
+def foo57
+end
+
+def foo58
+end
+
+def foo59
+end
+
+def foo60
+end
+
+def foo61
+end
+
+def foo62
+end
+
+def foo63
+end
+
+def foo64
+end
+
+def foo65
+end
+
+def foo66
+end
+
+def foo67
+end
+
+def foo68
+end
+
+def foo69
+end
+
+def foo70
+end
+
+def foo71
+end
+
+def foo72
+end
+
+def foo73
+end
+
+def foo74
+end
+
+def foo75
+end
+
+def foo76
+end
+
+def foo77
+end
+
+def foo78
+end
+
+def foo79
+end
+
+def foo80
+end
+
+def foo81
+end
+
+def foo82
+end
+
+def foo83
+end
+
+def foo84
+end
+
+def foo85
+end
+
+def foo86
+end
+
+def foo87
+end
+
+def foo88
+end
+
+def foo89
+end
+
+def foo90
+end
+
+def foo91
+end
+
+def foo92
+end
+
+def foo93
+end
+
+def foo94
+end
+
+def foo95
+end
+
+def foo96
+end
+
+def foo97
+end
+
+def foo98
+end
+
+def foo99
+end
+
+def foo100
+end
+
+def foo101
+end
+
+def foo102
+end
+
+def foo103
+end
+
+def foo104
+end
+
+def foo105
+end
+
+def foo106
+end
+
+def foo107
+end
+
+def foo108
+end
+
+def foo109
+end
+
+def foo110
+end
+
+def foo111
+end
+
+def foo112
+end
+
+def foo113
+end
+
+def foo114
+end
+
+def foo115
+end
+
+def foo116
+end
+
+def foo117
+end
+
+def foo118
+end
+
+def foo119
+end
+
+def foo120
+end
+
+def foo121
+end
diff --git a/spec/ruby/core/kernel/fork_spec.rb b/spec/ruby/core/kernel/fork_spec.rb
new file mode 100644
index 0000000000..b37f9980e0
--- /dev/null
+++ b/spec/ruby/core/kernel/fork_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/process/fork'
+
+describe "Kernel#fork" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:fork)
+ end
+
+ it_behaves_like :process_fork, :fork, KernelSpecs::Method.new
+end
+
+describe "Kernel.fork" do
+ it_behaves_like :process_fork, :fork, Kernel
+end
diff --git a/spec/ruby/core/kernel/format_spec.rb b/spec/ruby/core/kernel/format_spec.rb
new file mode 100644
index 0000000000..72fd40b952
--- /dev/null
+++ b/spec/ruby/core/kernel/format_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#format" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:format)
+ end
+end
+
+describe "Kernel.format" do
+ it "is accessible as a module function" do
+ Kernel.format("%s", "hello").should == "hello"
+ end
+end
diff --git a/spec/ruby/core/kernel/freeze_spec.rb b/spec/ruby/core/kernel/freeze_spec.rb
new file mode 100644
index 0000000000..018784a4f5
--- /dev/null
+++ b/spec/ruby/core/kernel/freeze_spec.rb
@@ -0,0 +1,85 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#freeze" do
+ it "prevents self from being further modified" do
+ o = mock('o')
+ o.frozen?.should be_false
+ o.freeze
+ o.frozen?.should be_true
+ end
+
+ it "returns self" do
+ o = Object.new
+ o.freeze.should equal(o)
+ end
+
+ describe "on integers" do
+ it "has no effect since they are already frozen" do
+ 1.frozen?.should be_true
+ 1.freeze
+
+ bignum = bignum_value
+ bignum.frozen?.should be_true
+ bignum.freeze
+ end
+ end
+
+ describe "on a Float" do
+ it "has no effect since it is already frozen" do
+ 1.2.frozen?.should be_true
+ 1.2.freeze
+ end
+ end
+
+ describe "on a Symbol" do
+ it "has no effect since it is already frozen" do
+ :sym.frozen?.should be_true
+ :sym.freeze
+ end
+ end
+
+ describe "on true, false and nil" do
+ it "has no effect since they are already frozen" do
+ nil.frozen?.should be_true
+ true.frozen?.should be_true
+ false.frozen?.should be_true
+
+ nil.freeze
+ true.freeze
+ false.freeze
+ end
+ end
+
+ ruby_version_is "2.5" do
+ describe "on a Complex" do
+ it "has no effect since it is already frozen" do
+ c = Complex(1.3, 3.1)
+ c.frozen?.should be_true
+ c.freeze
+ end
+ end
+
+ describe "on a Rational" do
+ it "has no effect since it is already frozen" do
+ r = Rational(1, 3)
+ r.frozen?.should be_true
+ r.freeze
+ end
+ end
+ end
+
+ it "causes mutative calls to raise RuntimeError" do
+ o = Class.new do
+ def mutate; @foo = 1; end
+ end.new
+ o.freeze
+ lambda {o.mutate}.should raise_error(RuntimeError)
+ end
+
+ it "causes instance_variable_set to raise RuntimeError" do
+ o = Object.new
+ o.freeze
+ lambda {o.instance_variable_set(:@foo, 1)}.should raise_error(RuntimeError)
+ end
+end
diff --git a/spec/ruby/core/kernel/frozen_spec.rb b/spec/ruby/core/kernel/frozen_spec.rb
new file mode 100644
index 0000000000..5887ed75ba
--- /dev/null
+++ b/spec/ruby/core/kernel/frozen_spec.rb
@@ -0,0 +1,78 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#frozen?" do
+ it "returns true if self is frozen" do
+ o = mock('o')
+ p = mock('p')
+ p.freeze
+ o.frozen?.should == false
+ p.frozen?.should == true
+ end
+
+ describe "on true, false and nil" do
+ it "returns true" do
+ true.frozen?.should be_true
+ false.frozen?.should be_true
+ nil.frozen?.should be_true
+ end
+ end
+
+ describe "on integers" do
+ before :each do
+ @fixnum = 1
+ @bignum = bignum_value
+ end
+
+ it "returns true" do
+ @fixnum.frozen?.should be_true
+ @bignum.frozen?.should be_true
+ end
+ end
+
+ describe "on a Float" do
+ before :each do
+ @float = 0.1
+ end
+
+ it "returns true" do
+ @float.frozen?.should be_true
+ end
+ end
+
+ describe "on a Symbol" do
+ before :each do
+ @symbol = :symbol
+ end
+
+ it "returns true" do
+ @symbol.frozen?.should be_true
+ end
+ end
+
+ ruby_version_is "2.5" do
+ describe "on a Complex" do
+ it "returns true" do
+ c = Complex(1.3, 3.1)
+ c.frozen?.should be_true
+ end
+
+ it "literal returns true" do
+ c = eval "1.3i"
+ c.frozen?.should be_true
+ end
+ end
+
+ describe "on a Rational" do
+ it "returns true" do
+ r = Rational(1, 3)
+ r.frozen?.should be_true
+ end
+
+ it "literal returns true" do
+ r = eval "1/3r"
+ r.frozen?.should be_true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/gets_spec.rb b/spec/ruby/core/kernel/gets_spec.rb
new file mode 100644
index 0000000000..104613dbfa
--- /dev/null
+++ b/spec/ruby/core/kernel/gets_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#gets" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:gets)
+ end
+
+ it "calls ARGF.gets" do
+ ARGF.should_receive(:gets).and_return("spec")
+ gets.should == "spec"
+ end
+end
+
+describe "Kernel.gets" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/global_variables_spec.rb b/spec/ruby/core/kernel/global_variables_spec.rb
new file mode 100644
index 0000000000..8bce8e25b7
--- /dev/null
+++ b/spec/ruby/core/kernel/global_variables_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel.global_variables" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:global_variables)
+ end
+
+ before :all do
+ @i = 0
+ end
+
+ it "finds subset starting with std" do
+ global_variables.grep(/std/).should include(:$stderr, :$stdin, :$stdout)
+ a = global_variables.size
+ gvar_name = "$foolish_global_var#{@i += 1}"
+ global_variables.include?(gvar_name.to_sym).should == false
+ eval("#{gvar_name} = 1")
+ global_variables.size.should == a+1
+ global_variables.should include(gvar_name.to_sym)
+ end
+end
+
+describe "Kernel#global_variables" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/gsub_spec.rb b/spec/ruby/core/kernel/gsub_spec.rb
new file mode 100644
index 0000000000..2bcb29f60e
--- /dev/null
+++ b/spec/ruby/core/kernel/gsub_spec.rb
@@ -0,0 +1,96 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# FIXME: These methods exist only when the -n or -p option is passed to
+# ruby, but we currently don't have a way of specifying that.
+ruby_version_is ""..."1.9" do
+ describe "Kernel#gsub" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:gsub)
+ end
+
+ it "raises a TypeError if $_ is not a String" do
+ lambda {
+ $_ = 123
+ gsub(/./, "!")
+ }.should raise_error(TypeError)
+ end
+
+ it "when matches sets $_ to a new string, leaving the former value unaltered" do
+ orig_value = $_ = "hello"
+ gsub("ello", "ola")
+ $_.should_not equal(orig_value)
+ $_.should == "hola"
+ orig_value.should == "hello"
+ end
+
+ it "returns a string with the same contents as $_ after the operation" do
+ $_ = "bye"
+ gsub("non-match", "?").should == "bye"
+
+ orig_value = $_ = "bye"
+ gsub(/$/, "!").should == "bye!"
+ orig_value.should == "bye"
+ end
+
+ it "accepts Regexps as patterns" do
+ $_ = "food"
+ gsub(/.$/, "l")
+ $_.should == "fool"
+ end
+
+ it "accepts Strings as patterns, treated literally" do
+ $_ = "hello, world."
+ gsub(".", "!")
+ $_.should == "hello, world!"
+ end
+
+ it "accepts objects which respond to #to_str as patterns and treats them as strings" do
+ $_ = "hello, world."
+ stringlike = mock(".")
+ stringlike.should_receive(:to_str).and_return(".")
+ gsub(stringlike, "!")
+ $_.should == "hello, world!"
+ end
+ end
+
+ describe "Kernel#gsub with a pattern and replacement" do
+ it "accepts strings for replacement" do
+ $_ = "hello"
+ gsub(/./, ".")
+ $_.should == "....."
+ end
+
+ it "accepts objects which respond to #to_str for replacement" do
+ o = mock("o")
+ o.should_receive(:to_str).and_return("o")
+ $_ = "ping"
+ gsub("i", o)
+ $_.should == "pong"
+ end
+
+ it "replaces \\1 sequences with the regexp's corresponding capture" do
+ $_ = "hello!"
+ gsub(/(.)(.)/, '\2\1')
+ $_.should == "ehll!o"
+ end
+ end
+
+ describe "Kernel#gsub with pattern and block" do
+ it "acts similarly to using $_.gsub" do
+ $_ = "olleh dlrow"
+ gsub(/(\w+)/){ $1.reverse }
+ $_.should == "hello world"
+ end
+ end
+
+ describe "Kernel#gsub!" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:gsub!)
+ end
+ end
+
+ describe "Kernel.gsub!" do
+ it "needs to be reviewed for spec completeness"
+ end
+end
diff --git a/spec/ruby/core/kernel/inspect_spec.rb b/spec/ruby/core/kernel/inspect_spec.rb
new file mode 100644
index 0000000000..b5ba1a3903
--- /dev/null
+++ b/spec/ruby/core/kernel/inspect_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#inspect" do
+ it "returns a String" do
+ Object.new.inspect.should be_an_instance_of(String)
+ end
+
+ it "returns a tainted string if self is tainted" do
+ Object.new.taint.inspect.tainted?.should be_true
+ end
+
+ it "returns an untrusted string if self is untrusted" do
+ Object.new.untrust.inspect.untrusted?.should be_true
+ end
+
+ it "does not call #to_s if it is defined" do
+ # We must use a bare Object here
+ obj = Object.new
+ inspected = obj.inspect
+
+ obj.stub!(:to_s).and_return("to_s'd")
+
+ obj.inspect.should == inspected
+ end
+
+ it "returns a String with the object class and object_id encoded" do
+ obj = Object.new
+ obj.inspect.should =~ /^#<Object:0x[0-9a-f]+>$/
+ end
+end
diff --git a/spec/ruby/core/kernel/instance_of_spec.rb b/spec/ruby/core/kernel/instance_of_spec.rb
new file mode 100644
index 0000000000..78cddb443f
--- /dev/null
+++ b/spec/ruby/core/kernel/instance_of_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe Kernel, "#instance_of?" do
+ before :each do
+ @o = KernelSpecs::InstanceClass.new
+ end
+
+ it "returns true if given class is object's class" do
+ @o.instance_of?(KernelSpecs::InstanceClass).should == true
+ [].instance_of?(Array).should == true
+ ''.instance_of?(String).should == true
+ end
+
+ it "returns false if given class is object's ancestor class" do
+ @o.instance_of?(KernelSpecs::AncestorClass).should == false
+ end
+
+ it "returns false if given class is not object's class nor object's ancestor class" do
+ @o.instance_of?(Array).should == false
+ end
+
+ it "returns false if given a Module that is included in object's class" do
+ @o.instance_of?(KernelSpecs::MyModule).should == false
+ end
+
+ it "returns false if given a Module that is included one of object's ancestors only" do
+ @o.instance_of?(KernelSpecs::AncestorModule).should == false
+ end
+
+ it "returns false if given a Module that is not included in object's class" do
+ @o.instance_of?(KernelSpecs::SomeOtherModule).should == false
+ end
+
+ it "raises a TypeError if given an object that is not a Class nor a Module" do
+ lambda { @o.instance_of?(Object.new) }.should raise_error(TypeError)
+ lambda { @o.instance_of?('KernelSpecs::InstanceClass') }.should raise_error(TypeError)
+ lambda { @o.instance_of?(1) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/kernel/instance_variable_defined_spec.rb b/spec/ruby/core/kernel/instance_variable_defined_spec.rb
new file mode 100644
index 0000000000..99f5e90b4d
--- /dev/null
+++ b/spec/ruby/core/kernel/instance_variable_defined_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#instance_variable_defined?" do
+ before do
+ @instance = KernelSpecs::InstanceVariable.new
+ end
+
+ describe "when passed a String" do
+ it "returns false if the instance variable is not defined" do
+ @instance.instance_variable_defined?("@goodbye").should be_false
+ end
+
+ it "returns true if the instance variable is defined" do
+ @instance.instance_variable_defined?("@greeting").should be_true
+ end
+ end
+
+ describe "when passed a Symbol" do
+ it "returns false if the instance variable is not defined" do
+ @instance.instance_variable_defined?(:@goodbye).should be_false
+ end
+
+ it "returns true if the instance variable is defined" do
+ @instance.instance_variable_defined?(:@greeting).should be_true
+ end
+ end
+
+ it "raises a TypeError if passed an Object not defining #to_str" do
+ lambda do
+ obj = mock("kernel instance_variable_defined?")
+ @instance.instance_variable_defined? obj
+ end.should raise_error(TypeError)
+ end
+
+ it "returns false if the instance variable is not defined for different types" do
+ [nil, false, true, 1, 2.0, :test, "test"].each do |obj|
+ obj.instance_variable_defined?("@goodbye").should be_false
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/instance_variable_get_spec.rb b/spec/ruby/core/kernel/instance_variable_get_spec.rb
new file mode 100644
index 0000000000..918abd8f5c
--- /dev/null
+++ b/spec/ruby/core/kernel/instance_variable_get_spec.rb
@@ -0,0 +1,105 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#instance_variable_get" do
+ before :each do
+ @obj = Object.new
+ @obj.instance_variable_set("@test", :test)
+ end
+
+ it "tries to convert the passed argument to a String using #to_str" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return("@test")
+ @obj.instance_variable_get(obj)
+ end
+
+ it "returns the value of the passed instance variable that is referred to by the conversion result" do
+ obj = mock("to_str")
+ obj.stub!(:to_str).and_return("@test")
+ @obj.instance_variable_get(obj).should == :test
+ end
+
+ it "returns nil when the referred instance variable does not exist" do
+ @obj.instance_variable_get(:@does_not_exist).should be_nil
+ end
+
+ it "raises a TypeError when the passed argument does not respond to #to_str" do
+ lambda { @obj.instance_variable_get(Object.new) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when the passed argument can't be converted to a String" do
+ obj = mock("to_str")
+ obj.stub!(:to_str).and_return(123)
+ lambda { @obj.instance_variable_get(obj) }.should raise_error(TypeError)
+ end
+
+ it "raises a NameError when the conversion result does not start with an '@'" do
+ obj = mock("to_str")
+ obj.stub!(:to_str).and_return("test")
+ lambda { @obj.instance_variable_get(obj) }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when passed just '@'" do
+ obj = mock("to_str")
+ obj.stub!(:to_str).and_return('@')
+ lambda { @obj.instance_variable_get(obj) }.should raise_error(NameError)
+ end
+end
+
+describe "Kernel#instance_variable_get when passed Symbol" do
+ before :each do
+ @obj = Object.new
+ @obj.instance_variable_set("@test", :test)
+ end
+
+ it "returns the value of the instance variable that is referred to by the passed Symbol" do
+ @obj.instance_variable_get(:@test).should == :test
+ end
+
+ it "raises a NameError when passed :@ as an instance variable name" do
+ lambda { @obj.instance_variable_get(:"@") }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when the passed Symbol does not start with an '@'" do
+ lambda { @obj.instance_variable_get(:test) }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when the passed Symbol is an invalid instance variable name" do
+ lambda { @obj.instance_variable_get(:"@0") }.should raise_error(NameError)
+ end
+end
+
+describe "Kernel#instance_variable_get when passed String" do
+ before :each do
+ @obj = Object.new
+ @obj.instance_variable_set("@test", :test)
+ end
+
+ it "returns the value of the instance variable that is referred to by the passed String" do
+ @obj.instance_variable_get("@test").should == :test
+ end
+
+ it "raises a NameError when the passed String does not start with an '@'" do
+ lambda { @obj.instance_variable_get("test") }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when the passed String is an invalid instance variable name" do
+ lambda { @obj.instance_variable_get("@0") }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when passed '@' as an instance variable name" do
+ lambda { @obj.instance_variable_get("@") }.should raise_error(NameError)
+ end
+end
+
+describe "Kernel#instance_variable_get when passed Fixnum" do
+ before :each do
+ @obj = Object.new
+ @obj.instance_variable_set("@test", :test)
+ end
+
+ it "raises a TypeError" do
+ lambda { @obj.instance_variable_get(10) }.should raise_error(TypeError)
+ lambda { @obj.instance_variable_get(-10) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/kernel/instance_variable_set_spec.rb b/spec/ruby/core/kernel/instance_variable_set_spec.rb
new file mode 100644
index 0000000000..ce74b6fc29
--- /dev/null
+++ b/spec/ruby/core/kernel/instance_variable_set_spec.rb
@@ -0,0 +1,93 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#instance_variable_set" do
+ it "sets the value of the specified instance variable" do
+ class Dog
+ def initialize(p1, p2)
+ @a, @b = p1, p2
+ end
+ end
+ Dog.new('cat', 99).instance_variable_set(:@a, 'dog').should == "dog"
+ end
+
+ it "sets the value of the instance variable when no instance variables exist yet" do
+ class NoVariables; end
+ NoVariables.new.instance_variable_set(:@a, "new").should == "new"
+ end
+
+ it "raises a NameError exception if the argument is not of form '@x'" do
+ class NoDog; end
+ lambda { NoDog.new.instance_variable_set(:c, "cat") }.should raise_error(NameError)
+ end
+
+ it "raises a NameError exception if the argument is an invalid instance variable name" do
+ class DigitDog; end
+ lambda { DigitDog.new.instance_variable_set(:"@0", "cat") }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when the argument is '@'" do
+ class DogAt; end
+ lambda { DogAt.new.instance_variable_set(:"@", "cat") }.should raise_error(NameError)
+ end
+
+ it "raises a TypeError if the instance variable name is a Fixnum" do
+ lambda { "".instance_variable_set(1, 2) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the instance variable name is an object that does not respond to to_str" do
+ class KernelSpecs::A; end
+ lambda { "".instance_variable_set(KernelSpecs::A.new, 3) }.should raise_error(TypeError)
+ end
+
+ it "raises a NameError if the passed object, when coerced with to_str, does not start with @" do
+ class KernelSpecs::B
+ def to_str
+ ":c"
+ end
+ end
+ lambda { "".instance_variable_set(KernelSpecs::B.new, 4) }.should raise_error(NameError)
+ end
+
+ it "raises a NameError if pass an object that cannot be a symbol" do
+ lambda { "".instance_variable_set(:c, 1) }.should raise_error(NameError)
+ end
+
+ it "accepts as instance variable name any instance of a class that responds to to_str" do
+ class KernelSpecs::C
+ def initialize
+ @a = 1
+ end
+ def to_str
+ "@a"
+ end
+ end
+ KernelSpecs::C.new.instance_variable_set(KernelSpecs::C.new, 2).should == 2
+ end
+
+ describe "on frozen objects" do
+ before :each do
+ klass = Class.new do
+ attr_reader :ivar
+ def initialize
+ @ivar = :origin
+ end
+ end
+
+ @frozen = klass.new.freeze
+ end
+
+ it "keeps stored object after any exceptions" do
+ lambda { @frozen.instance_variable_set(:@ivar, :replacement) }.should raise_error(Exception)
+ @frozen.ivar.should equal(:origin)
+ end
+
+ it "raises a #{frozen_error_class} when passed replacement is identical to stored object" do
+ lambda { @frozen.instance_variable_set(:@ivar, :origin) }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} when passed replacement is different from stored object" do
+ lambda { @frozen.instance_variable_set(:@ivar, :replacement) }.should raise_error(frozen_error_class)
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/instance_variables_spec.rb b/spec/ruby/core/kernel/instance_variables_spec.rb
new file mode 100644
index 0000000000..bf17b88c2f
--- /dev/null
+++ b/spec/ruby/core/kernel/instance_variables_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#instance_variables" do
+ describe "immediate values" do
+ it "returns an empty array if no instance variables are defined" do
+ 0.instance_variables.should == []
+ end
+
+ it "returns the correct array if an instance variable is added" do
+ a = 0
+ lambda{ a.instance_variable_set("@test", 1) }.should raise_error(RuntimeError)
+ end
+ end
+
+ describe "regular objects" do
+ it "returns an empty array if no instance variables are defined" do
+ Object.new.instance_variables.should == []
+ end
+
+ it "returns the correct array if an instance variable is added" do
+ a = Object.new
+ a.instance_variable_set("@test", 1)
+ a.instance_variables.should == [:@test]
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/is_a_spec.rb b/spec/ruby/core/kernel/is_a_spec.rb
new file mode 100644
index 0000000000..dc69766f83
--- /dev/null
+++ b/spec/ruby/core/kernel/is_a_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/kind_of'
+
+describe "Kernel#is_a?" do
+ it_behaves_like :kernel_kind_of , :is_a?
+end
diff --git a/spec/ruby/core/kernel/iterator_spec.rb b/spec/ruby/core/kernel/iterator_spec.rb
new file mode 100644
index 0000000000..7fbdade9dc
--- /dev/null
+++ b/spec/ruby/core/kernel/iterator_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#iterator?" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:iterator?)
+ end
+end
+
+describe "Kernel.iterator?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/itself_spec.rb b/spec/ruby/core/kernel/itself_spec.rb
new file mode 100644
index 0000000000..c906d7c3e8
--- /dev/null
+++ b/spec/ruby/core/kernel/itself_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#itself" do
+ it "returns the receiver itself" do
+ foo = Object.new
+ foo.itself.should equal foo
+ end
+end
diff --git a/spec/ruby/core/kernel/kind_of_spec.rb b/spec/ruby/core/kernel/kind_of_spec.rb
new file mode 100644
index 0000000000..734035620c
--- /dev/null
+++ b/spec/ruby/core/kernel/kind_of_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/kind_of'
+
+describe "Kernel#kind_of?" do
+ it_behaves_like :kernel_kind_of , :kind_of?
+end
diff --git a/spec/ruby/core/kernel/lambda_spec.rb b/spec/ruby/core/kernel/lambda_spec.rb
new file mode 100644
index 0000000000..4796d65352
--- /dev/null
+++ b/spec/ruby/core/kernel/lambda_spec.rb
@@ -0,0 +1,100 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/lambda'
+
+# The functionality of lambdas is specified in core/proc
+
+describe "Kernel.lambda" do
+ it_behaves_like :kernel_lambda, :lambda
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:lambda)
+ end
+
+ it "creates a lambda-style Proc if given a literal block" do
+ l = lambda { 42 }
+ l.lambda?.should be_true
+ end
+
+ it "creates a lambda-style Proc if given a literal block via #send" do
+ l = send(:lambda) { 42 }
+ l.lambda?.should be_true
+ end
+
+ it "creates a lambda-style Proc if given a literal block via #__send__" do
+ l = __send__(:lambda) { 42 }
+ l.lambda?.should be_true
+ end
+
+ it "creates a lambda-style Proc if given a literal block via Kernel.public_send" do
+ l = Kernel.public_send(:lambda) { 42 }
+ l.lambda?.should be_true
+ end
+
+ it "returned the passed Proc if given an existing Proc" do
+ some_proc = proc {}
+ l = lambda(&some_proc)
+ l.should equal(some_proc)
+ l.lambda?.should be_false
+ end
+
+ it "checks the arity of the call when no args are specified" do
+ l = lambda { :called }
+ l.call.should == :called
+
+ lambda { l.call(1) }.should raise_error(ArgumentError)
+ lambda { l.call(1, 2) }.should raise_error(ArgumentError)
+ end
+
+ it "checks the arity when 1 arg is specified" do
+ l = lambda { |a| :called }
+ l.call(1).should == :called
+
+ lambda { l.call }.should raise_error(ArgumentError)
+ lambda { l.call(1, 2) }.should raise_error(ArgumentError)
+ end
+
+ it "does not check the arity when passing a Proc with &" do
+ l = lambda { || :called }
+ p = proc { || :called }
+
+ lambda { l.call(1) }.should raise_error(ArgumentError)
+ p.call(1).should == :called
+ end
+
+ it "accepts 0 arguments when used with ||" do
+ lambda {
+ lambda { || }.call(1)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "strictly checks the arity when 0 or 2..inf args are specified" do
+ l = lambda { |a,b| }
+
+ lambda {
+ l.call
+ }.should raise_error(ArgumentError)
+
+ lambda {
+ l.call(1)
+ }.should raise_error(ArgumentError)
+
+ lambda {
+ l.call(1,2)
+ }.should_not raise_error(ArgumentError)
+ end
+
+ it "returns from the lambda itself, not the creation site of the lambda" do
+ @reached_end_of_method = nil
+ def test
+ send(:lambda) { return }.call
+ @reached_end_of_method = true
+ end
+ test
+ @reached_end_of_method.should be_true
+ end
+
+ it "allows long returns to flow through it" do
+ KernelSpecs::Lambda.new.outer.should == :good
+ end
+end
diff --git a/spec/ruby/core/kernel/load_spec.rb b/spec/ruby/core/kernel/load_spec.rb
new file mode 100644
index 0000000000..a165cc4acd
--- /dev/null
+++ b/spec/ruby/core/kernel/load_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/code_loading'
+require_relative 'shared/load'
+require_relative 'shared/require'
+
+describe "Kernel#load" do
+ before :each do
+ CodeLoadingSpecs.spec_setup
+ end
+
+ after :each do
+ CodeLoadingSpecs.spec_cleanup
+ end
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:load)
+ end
+
+ it_behaves_like :kernel_require_basic, :load, CodeLoadingSpecs::Method.new
+end
+
+describe "Kernel#load" do
+ it_behaves_like :kernel_load, :load, CodeLoadingSpecs::Method.new
+end
+
+describe "Kernel.load" do
+ before :each do
+ CodeLoadingSpecs.spec_setup
+ end
+
+ after :each do
+ CodeLoadingSpecs.spec_cleanup
+ end
+
+ it_behaves_like :kernel_require_basic, :load, Kernel
+end
+
+describe "Kernel.load" do
+ it_behaves_like :kernel_load, :load, Kernel
+end
diff --git a/spec/ruby/core/kernel/local_variables_spec.rb b/spec/ruby/core/kernel/local_variables_spec.rb
new file mode 100644
index 0000000000..d0f09943bd
--- /dev/null
+++ b/spec/ruby/core/kernel/local_variables_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#local_variables" do
+ after :each do
+ ScratchPad.clear
+ end
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:local_variables)
+ end
+
+ it "contains locals as they are added" do
+ a = 1
+ b = 2
+ local_variables.should include(:a, :b)
+ local_variables.length.should == 2
+ end
+
+ it "is accessible from bindings" do
+ def local_var_foo
+ a = 1
+ b = 2
+ binding
+ end
+ foo_binding = local_var_foo()
+ res = eval("local_variables",foo_binding)
+ res.should include(:a, :b)
+ res.length.should == 2
+ end
+
+ it "is accessible in eval" do
+ eval "a=1; b=2; ScratchPad.record local_variables"
+ ScratchPad.recorded.should include(:a, :b)
+ ScratchPad.recorded.length.should == 2
+ end
+end
diff --git a/spec/ruby/core/kernel/loop_spec.rb b/spec/ruby/core/kernel/loop_spec.rb
new file mode 100644
index 0000000000..3386326a13
--- /dev/null
+++ b/spec/ruby/core/kernel/loop_spec.rb
@@ -0,0 +1,79 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel.loop" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:loop)
+ end
+
+ it "calls block until it is terminated by a break" do
+ i = 0
+ loop do
+ i += 1
+ break if i == 10
+ end
+
+ i.should == 10
+ end
+
+ it "returns value passed to break" do
+ loop do
+ break 123
+ end.should == 123
+ end
+
+ it "returns nil if no value passed to break" do
+ loop do
+ break
+ end.should == nil
+ end
+
+ it "returns an enumerator if no block given" do
+ enum = loop
+ enum.instance_of?(Enumerator).should be_true
+ cnt = 0
+ enum.each do |*args|
+ raise "Args should be empty #{args.inspect}" unless args.empty?
+ cnt += 1
+ break cnt if cnt >= 42
+ end.should == 42
+ end
+
+ it "rescues StopIteration" do
+ loop do
+ raise StopIteration
+ end
+ 42.should == 42
+ end
+
+ it "rescues StopIteration's subclasses" do
+ finish = Class.new StopIteration
+ loop do
+ raise finish
+ end
+ 42.should == 42
+ end
+
+ it "does not rescue other errors" do
+ lambda{ loop do raise StandardError end }.should raise_error( StandardError )
+ end
+
+ it "returns StopIteration#result, the result value of a finished iterator" do
+ e = Enumerator.new { |y|
+ y << 1
+ y << 2
+ :stopped
+ }
+ loop { e.next }.should == :stopped
+ end
+
+ describe "when no block is given" do
+ describe "returned Enumerator" do
+ describe "size" do
+ it "returns Float::INFINITY" do
+ loop.size.should == Float::INFINITY
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/match_spec.rb b/spec/ruby/core/kernel/match_spec.rb
new file mode 100644
index 0000000000..e4af24791b
--- /dev/null
+++ b/spec/ruby/core/kernel/match_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe "Kernel#=~" do
+ it "returns nil matching any object" do
+ o = Object.new
+
+ (o =~ /Object/).should be_nil
+ (o =~ 'Object').should be_nil
+ (o =~ Object).should be_nil
+ (o =~ Object.new).should be_nil
+ (o =~ nil).should be_nil
+ (o =~ true).should be_nil
+ end
+end
diff --git a/spec/ruby/core/kernel/method_spec.rb b/spec/ruby/core/kernel/method_spec.rb
new file mode 100644
index 0000000000..25c6691e10
--- /dev/null
+++ b/spec/ruby/core/kernel/method_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require_relative 'shared/method'
+require_relative 'fixtures/classes'
+
+describe "Kernel#method" do
+ it_behaves_like :kernel_method, :method
+
+ before :each do
+ @obj = KernelSpecs::A.new
+ end
+
+ it "can be called on a private method" do
+ @obj.send(:private_method).should == :private_method
+ @obj.method(:private_method).should be_an_instance_of(Method)
+ end
+
+ it "can be called on a protected method" do
+ @obj.send(:protected_method).should == :protected_method
+ @obj.method(:protected_method).should be_an_instance_of(Method)
+ end
+
+ it "will see an alias of the original method as == when in a derived class" do
+ obj = KernelSpecs::B.new
+ obj.method(:aliased_pub_method).should == obj.method(:pub_method)
+ end
+
+ it "can call methods created with define_method" do
+ m = @obj.method(:defined_method)
+ m.call.should == :defined
+ end
+
+ it "can be called even if we only repond_to_missing? method, true" do
+ m = KernelSpecs::RespondViaMissing.new.method(:handled_privately)
+ m.should be_an_instance_of(Method)
+ m.call(1, 2, 3).should == "Done handled_privately([1, 2, 3])"
+ end
+end
diff --git a/spec/ruby/core/kernel/methods_spec.rb b/spec/ruby/core/kernel/methods_spec.rb
new file mode 100644
index 0000000000..fb7a7e8be9
--- /dev/null
+++ b/spec/ruby/core/kernel/methods_spec.rb
@@ -0,0 +1,101 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../fixtures/reflection'
+
+# TODO: rewrite
+describe "Kernel#methods" do
+ it "returns singleton methods defined by obj.meth" do
+ KernelSpecs::Methods.methods(false).should include(:ichi)
+ end
+
+ it "returns singleton methods defined in 'class << self'" do
+ KernelSpecs::Methods.methods(false).should include(:san)
+ end
+
+ it "returns private singleton methods defined by obj.meth" do
+ KernelSpecs::Methods.methods(false).should include(:shi)
+ end
+
+ it "returns singleton methods defined in 'class << self' when it follows 'private'" do
+ KernelSpecs::Methods.methods(false).should include(:roku)
+ end
+
+ it "does not return private singleton methods defined in 'class << self'" do
+ KernelSpecs::Methods.methods(false).should_not include(:shichi)
+ end
+
+ it "returns the publicly accessible methods of the object" do
+ meths = KernelSpecs::Methods.methods(false)
+ meths.should include(:hachi, :ichi, :juu, :juu_ichi,
+ :juu_ni, :roku, :san, :shi)
+
+ KernelSpecs::Methods.new.methods(false).should == []
+ end
+
+ it "returns the publicly accessible methods in the object, its ancestors and mixed-in modules" do
+ meths = KernelSpecs::Methods.methods(false) & KernelSpecs::Methods.methods
+ meths.should include(:hachi, :ichi, :juu, :juu_ichi,
+ :juu_ni, :roku, :san, :shi)
+
+ KernelSpecs::Methods.new.methods.should include(:ku, :ni, :juu_san)
+ end
+
+ it "returns methods added to the metaclass through extend" do
+ meth = KernelSpecs::Methods.new
+ meth.methods.should_not include(:peekaboo)
+ meth.extend(KernelSpecs::Methods::MetaclassMethods)
+ meth.methods.should include(:peekaboo)
+ end
+
+ it "does not return undefined singleton methods defined by obj.meth" do
+ o = KernelSpecs::Child.new
+ def o.single; end
+ o.methods.should include(:single)
+
+ class << o; self; end.send :undef_method, :single
+ o.methods.should_not include(:single)
+ end
+
+ it "does not return superclass methods undefined in the object's class" do
+ KernelSpecs::Child.new.methods.should_not include(:parent_method)
+ end
+
+ it "does not return superclass methods undefined in a superclass" do
+ KernelSpecs::Grandchild.new.methods.should_not include(:parent_method)
+ end
+
+ it "does not return included module methods undefined in the object's class" do
+ KernelSpecs::Grandchild.new.methods.should_not include(:parent_mixin_method)
+ end
+end
+
+describe :kernel_methods_supers, shared: true do
+ before :all do
+ @ms = [:pro, :pub]
+ end
+
+ it "returns a unique list for an object extended by a module" do
+ m = ReflectSpecs.oed.methods(*@object)
+ m.select { |x| @ms.include? x }.sort.should == @ms
+ end
+
+ it "returns a unique list for a class including a module" do
+ m = ReflectSpecs::D.new.methods(*@object)
+ m.select { |x| @ms.include? x }.sort.should == @ms
+ end
+
+ it "returns a unique list for a subclass of a class that includes a module" do
+ m = ReflectSpecs::E.new.methods(*@object)
+ m.select { |x| @ms.include? x }.sort.should == @ms
+ end
+end
+
+describe "Kernel#methods" do
+ describe "when not passed an argument" do
+ it_behaves_like :kernel_methods_supers, nil, []
+ end
+
+ describe "when passed true" do
+ it_behaves_like :kernel_methods_supers, nil, true
+ end
+end
diff --git a/spec/ruby/core/kernel/nil_spec.rb b/spec/ruby/core/kernel/nil_spec.rb
new file mode 100644
index 0000000000..b63705f7bc
--- /dev/null
+++ b/spec/ruby/core/kernel/nil_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#nil?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/not_match_spec.rb b/spec/ruby/core/kernel/not_match_spec.rb
new file mode 100644
index 0000000000..906f18df2c
--- /dev/null
+++ b/spec/ruby/core/kernel/not_match_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#!~" do
+ class KernelSpecs::NotMatch
+ def !~(obj)
+ :foo
+ end
+ end
+
+ it 'calls =~ internally and negates the result' do
+ obj = Object.new
+ obj.should_receive(:=~).and_return(true)
+ (obj !~ :foo).should == false
+ end
+
+ it 'can be overridden in subclasses' do
+ obj = KernelSpecs::NotMatch.new
+ (obj !~ :bar).should == :foo
+ end
+end
diff --git a/spec/ruby/core/kernel/object_id_spec.rb b/spec/ruby/core/kernel/object_id_spec.rb
new file mode 100644
index 0000000000..ef9e80c831
--- /dev/null
+++ b/spec/ruby/core/kernel/object_id_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/kernel/object_id'
+
+describe "Kernel#object_id" do
+ it_behaves_like :object_id, :object_id, Object
+end
diff --git a/spec/ruby/core/kernel/open_spec.rb b/spec/ruby/core/kernel/open_spec.rb
new file mode 100644
index 0000000000..5d3da871f0
--- /dev/null
+++ b/spec/ruby/core/kernel/open_spec.rb
@@ -0,0 +1,142 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#open" do
+
+ before :each do
+ @name = tmp("kernel_open.txt")
+ @content = "This is a test"
+ touch(@name) { |f| f.write @content }
+ @file = nil
+ end
+
+ after :each do
+ @file.close if @file
+ rm_r @name
+ end
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:open)
+ end
+
+ it "opens a file when given a valid filename" do
+ @file = open(@name)
+ @file.should be_kind_of(File)
+ end
+
+ it "opens a file when called with a block" do
+ open(@name, "r") { |f| f.gets }.should == @content
+ end
+
+ platform_is_not :windows do
+ it "opens an io when path starts with a pipe" do
+ @io = open("|date")
+ begin
+ @io.should be_kind_of(IO)
+ @io.read
+ ensure
+ @io.close
+ end
+ end
+
+ it "opens an io when called with a block" do
+ @output = open("|date") { |f| f.read }
+ @output.should_not == ''
+ end
+
+ it "opens an io for writing" do
+ bytes = open("|cat", "w") { |io| io.write(".") }
+ bytes.should == 1
+ end
+ end
+
+ platform_is :windows do
+ it "opens an io when path starts with a pipe" do
+ @io = open("|date /t")
+ begin
+ @io.should be_kind_of(IO)
+ @io.read
+ ensure
+ @io.close
+ end
+ end
+
+ it "opens an io when called with a block" do
+ @output = open("|date /t") { |f| f.read }
+ @output.should_not == ''
+ end
+ end
+
+ it "raises an ArgumentError if not passed one argument" do
+ lambda { open }.should raise_error(ArgumentError)
+ end
+
+ describe "when given an object that responds to to_open" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "calls #to_path to covert the argument to a String before calling #to_str" do
+ obj = mock("open to_path")
+ obj.should_receive(:to_path).at_least(1).times.and_return(@name)
+ obj.should_not_receive(:to_str)
+
+ open(obj, "r") { |f| f.gets }.should == @content
+ end
+
+ it "calls #to_str to convert the argument to a String" do
+ obj = mock("open to_str")
+ obj.should_receive(:to_str).at_least(1).times.and_return(@name)
+
+ open(obj, "r") { |f| f.gets }.should == @content
+ end
+
+ it "calls #to_open on argument" do
+ obj = mock('fileish')
+ @file = File.open(@name)
+ obj.should_receive(:to_open).and_return(@file)
+ @file = open(obj)
+ @file.should be_kind_of(File)
+ end
+
+ it "returns the value from #to_open" do
+ obj = mock('to_open')
+ obj.should_receive(:to_open).and_return(:value)
+
+ open(obj).should == :value
+ end
+
+ it "passes its arguments onto #to_open" do
+ obj = mock('to_open')
+ obj.should_receive(:to_open).with(1,2,3)
+
+ open(obj, 1, 2, 3)
+ end
+
+ it "passes the return value from #to_open to a block" do
+ obj = mock('to_open')
+ obj.should_receive(:to_open).and_return(:value)
+
+ open(obj) do |mock|
+ ScratchPad.record(mock)
+ end
+
+ ScratchPad.recorded.should == :value
+ end
+ end
+
+ it "raises a TypeError if passed a non-String that does not respond to #to_open" do
+ obj = mock('non-fileish')
+ lambda { open(obj) }.should raise_error(TypeError)
+ lambda { open(nil) }.should raise_error(TypeError)
+ lambda { open(7) }.should raise_error(TypeError)
+ end
+
+ it "accepts nil for mode and permission" do
+ open(@name, nil, nil) { |f| f.gets }.should == @content
+ end
+end
+
+describe "Kernel.open" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/p_spec.rb b/spec/ruby/core/kernel/p_spec.rb
new file mode 100644
index 0000000000..c95055cf35
--- /dev/null
+++ b/spec/ruby/core/kernel/p_spec.rb
@@ -0,0 +1,79 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#p" do
+ before :all do
+ @rs_f, @rs_b, @rs_c = $/, $\, $,
+ end
+
+ after :each do
+ $/, $\, $, = @rs_f, @rs_b, @rs_c
+ end
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:p)
+ end
+
+ # TODO: fix
+ it "flushes output if receiver is a File" do
+ filename = tmp("Kernel_p_flush") + $$.to_s
+ begin
+ File.open(filename, "w") do |f|
+ begin
+ old_stdout = $stdout
+ $stdout = f
+ p("abcde")
+ ensure
+ $stdout = old_stdout
+ end
+
+ File.open(filename) do |f2|
+ f2.read(7).should == "\"abcde\""
+ end
+ end
+ ensure
+ rm_r filename
+ end
+ end
+
+ it "prints obj.inspect followed by system record separator for each argument given" do
+ o = mock("Inspector Gadget")
+ o.should_receive(:inspect).any_number_of_times.and_return "Next time, Gadget, NEXT TIME!"
+
+ lambda { p(o) }.should output("Next time, Gadget, NEXT TIME!\n")
+ lambda { p(*[o]) }.should output("Next time, Gadget, NEXT TIME!\n")
+ lambda { p(*[o, o]) }.should output("Next time, Gadget, NEXT TIME!\nNext time, Gadget, NEXT TIME!\n")
+ lambda { p([o])}.should output("[#{o.inspect}]\n")
+ end
+
+ it "is not affected by setting $\\, $/ or $," do
+ o = mock("Inspector Gadget")
+ o.should_receive(:inspect).any_number_of_times.and_return "Next time, Gadget, NEXT TIME!"
+
+ $, = " *helicopter sound*\n"
+ lambda { p(o) }.should output_to_fd("Next time, Gadget, NEXT TIME!\n")
+
+ $\ = " *helicopter sound*\n"
+ lambda { p(o) }.should output_to_fd("Next time, Gadget, NEXT TIME!\n")
+
+ $/ = " *helicopter sound*\n"
+ lambda { p(o) }.should output_to_fd("Next time, Gadget, NEXT TIME!\n")
+ end
+
+ it "prints nothing if no argument is given" do
+ lambda { p }.should output("")
+ end
+
+ it "prints nothing if called splatting an empty Array" do
+ lambda { 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
+end
+
+describe "Kernel.p" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/print_spec.rb b/spec/ruby/core/kernel/print_spec.rb
new file mode 100644
index 0000000000..c8c4453d1e
--- /dev/null
+++ b/spec/ruby/core/kernel/print_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#print" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:print)
+ end
+end
+
+describe "Kernel.print" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/printf_spec.rb b/spec/ruby/core/kernel/printf_spec.rb
new file mode 100644
index 0000000000..ca88082694
--- /dev/null
+++ b/spec/ruby/core/kernel/printf_spec.rb
@@ -0,0 +1,60 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/sprintf'
+require "stringio"
+
+describe "Kernel#printf" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:printf)
+ end
+end
+
+describe "Kernel.printf" do
+
+ before :each do
+ @stdout = $stdout
+ @name = tmp("kernel_puts.txt")
+ $stdout = new_io @name
+ end
+
+ after :each do
+ $stdout.close
+ $stdout = @stdout
+ rm_r @name
+ end
+
+ it "writes to stdout when a string is the first argument" do
+ $stdout.should_receive(:write).with("string")
+ Kernel.printf("%s", "string")
+ end
+
+ it "calls write on the first argument when it is not a string" do
+ object = mock('io')
+ object.should_receive(:write).with("string")
+ Kernel.printf(object, "%s", "string")
+ end
+
+ describe "formatting" do
+ context "io is specified" do
+ it_behaves_like :kernel_sprintf, -> (format, *args) {
+ io = StringIO.new
+ printf(io, format, *args)
+ io.string
+ }
+ end
+
+ context "io is not specified" do
+ it_behaves_like :kernel_sprintf, -> (format, *args) {
+ stdout = $stdout
+
+ begin
+ $stdout = io = StringIO.new
+ printf(format, *args)
+ io.string
+ ensure
+ $stdout = stdout
+ end
+ }
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/private_methods_spec.rb b/spec/ruby/core/kernel/private_methods_spec.rb
new file mode 100644
index 0000000000..041634d1e5
--- /dev/null
+++ b/spec/ruby/core/kernel/private_methods_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../fixtures/reflection'
+
+# TODO: rewrite
+describe "Kernel#private_methods" do
+ it "returns a list of the names of privately accessible methods in the object" do
+ m = KernelSpecs::Methods.private_methods(false)
+ m.should include(:shichi)
+ m = KernelSpecs::Methods.new.private_methods(false)
+ m.should include(:juu_shi)
+ end
+
+ it "returns a list of the names of privately accessible methods in the object and its ancestors and mixed-in modules" do
+ m = (KernelSpecs::Methods.private_methods(false) & KernelSpecs::Methods.private_methods)
+
+ m.should include(:shichi)
+ m = KernelSpecs::Methods.new.private_methods
+ m.should include(:juu_shi)
+ end
+
+ it "returns private methods mixed in to the metaclass" do
+ m = KernelSpecs::Methods.new
+ m.extend(KernelSpecs::Methods::MetaclassMethods)
+ m.private_methods.should include(:shoo)
+ end
+end
+
+describe :kernel_private_methods_supers, shared: true do
+ it "returns a unique list for an object extended by a module" do
+ m = ReflectSpecs.oed.private_methods(*@object)
+ m.select { |x| x == :pri }.sort.should == [:pri]
+ end
+
+ it "returns a unique list for a class including a module" do
+ m = ReflectSpecs::D.new.private_methods(*@object)
+ m.select { |x| x == :pri }.sort.should == [:pri]
+ end
+
+ it "returns a unique list for a subclass of a class that includes a module" do
+ m = ReflectSpecs::E.new.private_methods(*@object)
+ m.select { |x| x == :pri }.sort.should == [:pri]
+ end
+end
+
+describe :kernel_private_methods_with_falsy, shared: true do
+ it "returns a list of private methods in without its ancestors" do
+ ReflectSpecs::F.private_methods(@object).select{|m|/_pri\z/ =~ m}.sort.should == [:ds_pri, :fs_pri]
+ ReflectSpecs::F.new.private_methods(@object).should == [:f_pri]
+ end
+end
+
+describe "Kernel#private_methods" do
+ describe "when not passed an argument" do
+ it_behaves_like :kernel_private_methods_supers, nil, []
+ end
+
+ describe "when passed true" do
+ it_behaves_like :kernel_private_methods_supers, nil, true
+ end
+
+ describe "when passed false" do
+ it_behaves_like :kernel_private_methods_with_falsy, nil, false
+ end
+
+ describe "when passed nil" do
+ it_behaves_like :kernel_private_methods_with_falsy, nil, nil
+ end
+end
diff --git a/spec/ruby/core/kernel/proc_spec.rb b/spec/ruby/core/kernel/proc_spec.rb
new file mode 100644
index 0000000000..d9c09117e7
--- /dev/null
+++ b/spec/ruby/core/kernel/proc_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/lambda'
+
+# The functionality of Proc objects is specified in core/proc
+
+describe "Kernel.proc" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:proc)
+ end
+
+ it "creates a proc-style Proc if given a literal block" do
+ l = proc { 42 }
+ l.lambda?.should be_false
+ end
+
+ it "returned the passed Proc if given an existing Proc" do
+ some_lambda = lambda {}
+ some_lambda.lambda?.should be_true
+ l = proc(&some_lambda)
+ l.should equal(some_lambda)
+ l.lambda?.should be_true
+ end
+
+ it_behaves_like :kernel_lambda, :proc
+
+ it "returns from the creation site of the proc, not just the proc itself" do
+ @reached_end_of_method = nil
+ def test
+ proc { return }.call
+ @reached_end_of_method = true
+ end
+ test
+ @reached_end_of_method.should be_nil
+ end
+end
+
+describe "Kernel#proc" do
+ it "uses the implicit block from an enclosing method" do
+ def some_method
+ proc
+ end
+
+ prc = some_method { "hello" }
+
+ prc.call.should == "hello"
+ end
+
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/protected_methods_spec.rb b/spec/ruby/core/kernel/protected_methods_spec.rb
new file mode 100644
index 0000000000..d3334e886b
--- /dev/null
+++ b/spec/ruby/core/kernel/protected_methods_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../fixtures/reflection'
+
+# TODO: rewrite
+
+# The reason why having include() is to show the specification explicitly.
+# You should use have_protected_method() with the exception of this spec.
+describe "Kernel#protected_methods" do
+ it "returns a list of the names of protected methods accessible in the object" do
+ KernelSpecs::Methods.protected_methods(false).sort.should include(:juu_ichi)
+ KernelSpecs::Methods.new.protected_methods(false).should include(:ku)
+ end
+
+ it "returns a list of the names of protected methods accessible in the object and from its ancestors and mixed-in modules" do
+ l1 = KernelSpecs::Methods.protected_methods(false)
+ l2 = KernelSpecs::Methods.protected_methods
+ (l1 & l2).should include(:juu_ichi)
+ KernelSpecs::Methods.new.protected_methods.should include(:ku)
+ end
+
+ it "returns methods mixed in to the metaclass" do
+ m = KernelSpecs::Methods.new
+ m.extend(KernelSpecs::Methods::MetaclassMethods)
+ m.protected_methods.should include(:nopeeking)
+ end
+end
+
+describe :kernel_protected_methods_supers, shared: true do
+ it "returns a unique list for an object extended by a module" do
+ m = ReflectSpecs.oed.protected_methods(*@object)
+ m.select { |x| x == :pro }.sort.should == [:pro]
+ end
+
+ it "returns a unique list for a class including a module" do
+ m = ReflectSpecs::D.new.protected_methods(*@object)
+ m.select { |x| x == :pro }.sort.should == [:pro]
+ end
+
+ it "returns a unique list for a subclass of a class that includes a module" do
+ m = ReflectSpecs::E.new.protected_methods(*@object)
+ m.select { |x| x == :pro }.sort.should == [:pro]
+ end
+end
+
+describe :kernel_protected_methods_with_falsy, shared: true do
+ it "returns a list of protected methods in without its ancestors" do
+ ReflectSpecs::F.protected_methods(@object).select{|m|/_pro\z/ =~ m}.sort.should == [:ds_pro, :fs_pro]
+ ReflectSpecs::F.new.protected_methods(@object).should == [:f_pro]
+ end
+end
+
+describe "Kernel#protected_methods" do
+ describe "when not passed an argument" do
+ it_behaves_like :kernel_protected_methods_supers, nil, []
+ end
+
+ describe "when passed true" do
+ it_behaves_like :kernel_protected_methods_supers, nil, true
+ end
+
+ describe "when passed false" do
+ it_behaves_like :kernel_protected_methods_with_falsy, nil, false
+ end
+
+ describe "when passed nil" do
+ it_behaves_like :kernel_protected_methods_with_falsy, nil, nil
+ end
+end
diff --git a/spec/ruby/core/kernel/public_method_spec.rb b/spec/ruby/core/kernel/public_method_spec.rb
new file mode 100644
index 0000000000..72d0c62720
--- /dev/null
+++ b/spec/ruby/core/kernel/public_method_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/method'
+
+describe "Kernel#public_method" do
+ it_behaves_like :kernel_method, :public_method
+
+ before :each do
+ @obj = KernelSpecs::A.new
+ end
+
+ it "raises a NameError when called on a private method" do
+ @obj.send(:private_method).should == :private_method
+ lambda do
+ @obj.public_method(:private_method)
+ end.should raise_error(NameError)
+ end
+
+ it "raises a NameError when called on a protected method" do
+ @obj.send(:protected_method).should == :protected_method
+ lambda {
+ @obj.public_method(:protected_method)
+ }.should raise_error(NameError)
+ end
+
+ it "raises a NameError if we only repond_to_missing? method, true" do
+ obj = KernelSpecs::RespondViaMissing.new
+ lambda do
+ obj.public_method(:handled_privately)
+ end.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/core/kernel/public_methods_spec.rb b/spec/ruby/core/kernel/public_methods_spec.rb
new file mode 100644
index 0000000000..a5512784fb
--- /dev/null
+++ b/spec/ruby/core/kernel/public_methods_spec.rb
@@ -0,0 +1,76 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../fixtures/reflection'
+
+# TODO: rewrite
+describe "Kernel#public_methods" do
+ it "returns a list of the names of publicly accessible methods in the object" do
+ KernelSpecs::Methods.public_methods(false).sort.should include(:hachi,
+ :ichi, :juu, :juu_ni, :roku, :san, :shi)
+ KernelSpecs::Methods.new.public_methods(false).sort.should include(:juu_san, :ni)
+ end
+
+ it "returns a list of names without protected accessible methods in the object" do
+ KernelSpecs::Methods.public_methods(false).sort.should_not include(:juu_ichi)
+ KernelSpecs::Methods.new.public_methods(false).sort.should_not include(:ku)
+ end
+
+ it "returns a list of the names of publicly accessible methods in the object and its ancestors and mixed-in modules" do
+ (KernelSpecs::Methods.public_methods(false) & KernelSpecs::Methods.public_methods).sort.should include(
+ :hachi, :ichi, :juu, :juu_ni, :roku, :san, :shi)
+ m = KernelSpecs::Methods.new.public_methods
+ m.should include(:ni, :juu_san)
+ end
+
+ it "returns methods mixed in to the metaclass" do
+ m = KernelSpecs::Methods.new
+ m.extend(KernelSpecs::Methods::MetaclassMethods)
+ m.public_methods.should include(:peekaboo)
+ end
+
+ it "returns public methods for immediates" do
+ 10.public_methods.should include(:divmod)
+ end
+end
+
+describe :kernel_public_methods_supers, shared: true do
+ it "returns a unique list for an object extended by a module" do
+ m = ReflectSpecs.oed.public_methods(*@object)
+ m.select { |x| x == :pub }.sort.should == [:pub]
+ end
+
+ it "returns a unique list for a class including a module" do
+ m = ReflectSpecs::D.new.public_methods(*@object)
+ m.select { |x| x == :pub }.sort.should == [:pub]
+ end
+
+ it "returns a unique list for a subclass of a class that includes a module" do
+ m = ReflectSpecs::E.new.public_methods(*@object)
+ m.select { |x| x == :pub }.sort.should == [:pub]
+ end
+end
+
+describe :kernel_public_methods_with_falsy, shared: true do
+ it "returns a list of public methods in without its ancestors" do
+ ReflectSpecs::F.public_methods(@object).select{|m|/_pub\z/ =~ m}.sort.should == [:ds_pub, :fs_pub]
+ ReflectSpecs::F.new.public_methods(@object).should == [:f_pub]
+ end
+end
+
+describe "Kernel#public_methods" do
+ describe "when not passed an argument" do
+ it_behaves_like :kernel_public_methods_supers, nil, []
+ end
+
+ describe "when passed true" do
+ it_behaves_like :kernel_public_methods_supers, nil, true
+ end
+
+ describe "when passed false" do
+ it_behaves_like :kernel_public_methods_with_falsy, nil, false
+ end
+
+ describe "when passed nil" do
+ it_behaves_like :kernel_public_methods_with_falsy, nil, nil
+ end
+end
diff --git a/spec/ruby/core/kernel/public_send_spec.rb b/spec/ruby/core/kernel/public_send_spec.rb
new file mode 100644
index 0000000000..984ab41802
--- /dev/null
+++ b/spec/ruby/core/kernel/public_send_spec.rb
@@ -0,0 +1,108 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/basicobject/send'
+
+describe "Kernel#public_send" do
+ it "invokes the named public method" do
+ class KernelSpecs::Foo
+ def bar
+ 'done'
+ end
+ end
+ KernelSpecs::Foo.new.public_send(:bar).should == 'done'
+ end
+
+ it "invokes the named alias of a public method" do
+ class KernelSpecs::Foo
+ def bar
+ 'done'
+ end
+ alias :aka :bar
+ end
+ KernelSpecs::Foo.new.public_send(:aka).should == 'done'
+ end
+
+ it "raises a NoMethodError if the method is protected" do
+ class KernelSpecs::Foo
+ protected
+ def bar
+ 'done'
+ end
+ end
+ lambda { KernelSpecs::Foo.new.public_send(:bar)}.should raise_error(NoMethodError)
+ end
+
+ it "raises a NoMethodError if the named method is private" do
+ class KernelSpecs::Foo
+ private
+ def bar
+ 'done2'
+ end
+ end
+ lambda {
+ KernelSpecs::Foo.new.public_send(:bar)
+ }.should raise_error(NoMethodError)
+ end
+
+ context 'called from own public method' do
+ before do
+ class << @receiver = Object.new
+ def call_protected_method
+ public_send :protected_method
+ end
+
+ def call_private_method
+ public_send :private_method
+ end
+
+ protected
+
+ def protected_method
+ raise 'Should not called'
+ end
+
+ private
+
+ def private_method
+ raise 'Should not called'
+ end
+ end
+ end
+
+ it "raises a NoMethodError if the method is protected" do
+ lambda { @receiver.call_protected_method }.should raise_error(NoMethodError)
+ end
+
+ it "raises a NoMethodError if the method is private" do
+ lambda { @receiver.call_private_method }.should raise_error(NoMethodError)
+ end
+ end
+
+ it "raises a NoMethodError if the named method is an alias of a private method" do
+ class KernelSpecs::Foo
+ private
+ def bar
+ 'done2'
+ end
+ alias :aka :bar
+ end
+ lambda {
+ KernelSpecs::Foo.new.public_send(:aka)
+ }.should raise_error(NoMethodError)
+ end
+
+ it "raises a NoMethodError if the named method is an alias of a protected method" do
+ class KernelSpecs::Foo
+ protected
+ def bar
+ 'done2'
+ end
+ alias :aka :bar
+ end
+ lambda {
+ KernelSpecs::Foo.new.public_send(:aka)
+ }.should raise_error(NoMethodError)
+ end
+
+ it_behaves_like :basicobject_send, :public_send
+end
diff --git a/spec/ruby/core/kernel/putc_spec.rb b/spec/ruby/core/kernel/putc_spec.rb
new file mode 100644
index 0000000000..74bd3765db
--- /dev/null
+++ b/spec/ruby/core/kernel/putc_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/io/putc'
+
+describe "Kernel#putc" do
+ it "is a private instance method" do
+ Kernel.should have_private_instance_method(:putc)
+ end
+end
+
+describe "Kernel.putc" do
+ before :each do
+ @name = tmp("kernel_putc.txt")
+ @io = new_io @name
+ @io_object = @object
+ @stdout, $stdout = $stdout, @io
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it_behaves_like :io_putc, :putc_method, KernelSpecs
+end
+
+describe "Kernel#putc" do
+ before :each do
+ @name = tmp("kernel_putc.txt")
+ @io = new_io @name
+ @io_object = @object
+ @stdout, $stdout = $stdout, @io
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it_behaves_like :io_putc, :putc_function, KernelSpecs
+end
diff --git a/spec/ruby/core/kernel/puts_spec.rb b/spec/ruby/core/kernel/puts_spec.rb
new file mode 100644
index 0000000000..6eb38e8fcf
--- /dev/null
+++ b/spec/ruby/core/kernel/puts_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#puts" do
+ before :each do
+ @stdout = $stdout
+ @name = tmp("kernel_puts.txt")
+ $stdout = new_io @name
+ end
+
+ after :each do
+ $stdout.close
+ $stdout = @stdout
+ rm_r @name
+ end
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:puts)
+ end
+
+ it "delegates to $stdout.puts" do
+ $stdout.should_receive(:puts).with(:arg)
+ puts :arg
+ end
+end
+
+describe "Kernel.puts" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/raise_spec.rb b/spec/ruby/core/kernel/raise_spec.rb
new file mode 100644
index 0000000000..bf26560246
--- /dev/null
+++ b/spec/ruby/core/kernel/raise_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/kernel/raise'
+
+describe "Kernel#raise" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:raise)
+ end
+end
+
+describe "Kernel#raise" do
+ it_behaves_like :kernel_raise, :raise, Kernel
+end
+
+describe "Kernel.raise" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/rand_spec.rb b/spec/ruby/core/kernel/rand_spec.rb
new file mode 100644
index 0000000000..bdf5842f1a
--- /dev/null
+++ b/spec/ruby/core/kernel/rand_spec.rb
@@ -0,0 +1,139 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel.rand" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:rand)
+ end
+
+ it "returns a float if no argument is passed" do
+ rand.should be_kind_of(Float)
+ end
+
+ it "returns an integer for an integer argument" do
+ rand(77).should be_kind_of(Integer)
+ end
+
+ it "returns an integer for a float argument greater than 1" do
+ rand(1.3).should be_kind_of(Integer)
+ end
+
+ it "returns a float for an argument between -1 and 1" do
+ rand(-0.999).should be_kind_of(Float)
+ rand(-0.01).should be_kind_of(Float)
+ rand(0).should be_kind_of(Float)
+ rand(0.01).should be_kind_of(Float)
+ rand(0.999).should be_kind_of(Float)
+ end
+
+ it "ignores the sign of the argument" do
+ [0, 1, 2, 3].should include(rand(-4))
+ end
+
+ it "never returns a value greater or equal to 1.0 with no arguments" do
+ 1000.times do
+ (0...1.0).should include(rand)
+ end
+ end
+
+ it "never returns a value greater or equal to any passed in max argument" do
+ 1000.times do
+ (0...100).to_a.should include(rand(100))
+ end
+ end
+
+ it "calls to_int on its argument" do
+ l = mock('limit')
+ l.should_receive(:to_int).and_return 7
+
+ rand l
+ end
+
+ context "given an exclusive range" do
+ it "returns an Integer between the two Integers" do
+ 1000.times do
+ x = rand(4...6)
+ x.should be_kind_of(Integer)
+ (4...6).should include(x)
+ end
+ end
+
+ it "returns a Float between the given Integer and Float" do
+ 1000.times do
+ x = rand(4...6.5)
+ x.should be_kind_of(Float)
+ (4...6.5).should include(x)
+ end
+ end
+
+ it "returns a Float between the given Float and Integer" do
+ 1000.times do
+ x = rand(3.5...6)
+ x.should be_kind_of(Float)
+ (3.5...6).should include(x)
+ end
+ end
+
+ it "returns a Float between the two given Floats" do
+ 1000.times do
+ x = rand(3.5...6.5)
+ x.should be_kind_of(Float)
+ (3.5...6.5).should include(x)
+ end
+ end
+ end
+
+ context "given an inclusive range" do
+ it "returns an Integer between the two Integers" do
+ 1000.times do
+ x = rand(4..6)
+ x.should be_kind_of(Integer)
+ (4..6).should include(x)
+ end
+ end
+
+ it "returns a Float between the given Integer and Float" do
+ 1000.times do
+ x = rand(4..6.5)
+ x.should be_kind_of(Float)
+ (4..6.5).should include(x)
+ end
+ end
+
+ it "returns a Float between the given Float and Integer" do
+ 1000.times do
+ x = rand(3.5..6)
+ x.should be_kind_of(Float)
+ (3.5..6).should include(x)
+ end
+ end
+
+ it "returns a Float between the two given Floats" do
+ 1000.times do
+ x = rand(3.5..6.5)
+ x.should be_kind_of(Float)
+ (3.5..6.5).should include(x)
+ end
+ end
+ end
+
+ it "returns a numeric for an range argument where max is < 1" do
+ rand(0.25..0.75).should be_kind_of(Numeric)
+ end
+
+ it "returns nil when range is backwards" do
+ rand(1..0).should be_nil
+ end
+
+ it "returns the range start/end when Float range is 0" do
+ rand(1.0..1.0).should eql(1.0)
+ end
+
+ it "returns the range start/end when Integer range is 0" do
+ rand(42..42).should eql(42)
+ end
+end
+
+describe "Kernel#rand" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/readline_spec.rb b/spec/ruby/core/kernel/readline_spec.rb
new file mode 100644
index 0000000000..dce7b03dc8
--- /dev/null
+++ b/spec/ruby/core/kernel/readline_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#readline" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:readline)
+ end
+end
+
+describe "Kernel.readline" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/readlines_spec.rb b/spec/ruby/core/kernel/readlines_spec.rb
new file mode 100644
index 0000000000..2b6d65fff2
--- /dev/null
+++ b/spec/ruby/core/kernel/readlines_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#readlines" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:readlines)
+ end
+end
+
+describe "Kernel.readlines" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/remove_instance_variable_spec.rb b/spec/ruby/core/kernel/remove_instance_variable_spec.rb
new file mode 100644
index 0000000000..5022a70c1d
--- /dev/null
+++ b/spec/ruby/core/kernel/remove_instance_variable_spec.rb
@@ -0,0 +1,59 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe :kernel_remove_instance_variable, shared: true do
+ it "returns the instance variable's value" do
+ value = @instance.send :remove_instance_variable, @object
+ value.should == "hello"
+ end
+
+ it "removes the instance variable" do
+ @instance.send :remove_instance_variable, @object
+ @instance.instance_variable_defined?(@object).should be_false
+ end
+end
+
+describe "Kernel#remove_instance_variable" do
+ before do
+ @instance = KernelSpecs::InstanceVariable.new
+ end
+
+ it "is a public method" do
+ Kernel.should have_public_instance_method(:remove_instance_variable, false)
+ end
+
+ it "raises a NameError if the instance variable is not defined" do
+ lambda do
+ @instance.send :remove_instance_variable, :@unknown
+ end.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the argument is not a valid instance variable name" do
+ lambda do
+ @instance.send :remove_instance_variable, :"@0"
+ end.should raise_error(NameError)
+ end
+
+ it "raises a TypeError if passed an Object not defining #to_str" do
+ lambda do
+ obj = mock("kernel remove_instance_variable")
+ @instance.send :remove_instance_variable, obj
+ end.should raise_error(TypeError)
+ end
+
+ describe "when passed a String" do
+ it_behaves_like :kernel_remove_instance_variable, nil, "@greeting"
+ end
+
+ describe "when passed a Symbol" do
+ it_behaves_like :kernel_remove_instance_variable, nil, :@greeting
+ end
+
+ describe "when passed an Object" do
+ it "calls #to_str to convert the argument" do
+ name = mock("kernel remove_instance_variable")
+ name.should_receive(:to_str).and_return("@greeting")
+ @instance.send :remove_instance_variable, name
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/require_relative_spec.rb b/spec/ruby/core/kernel/require_relative_spec.rb
new file mode 100644
index 0000000000..a16e7164e6
--- /dev/null
+++ b/spec/ruby/core/kernel/require_relative_spec.rb
@@ -0,0 +1,431 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/code_loading'
+
+describe "Kernel#require_relative with a relative path" do
+ it "needs to be reviewed for spec completeness"
+
+ before :each do
+ CodeLoadingSpecs.spec_setup
+ @dir = "../../fixtures/code"
+ @abs_dir = File.realpath(@dir, File.dirname(__FILE__))
+ @path = "#{@dir}/load_fixture.rb"
+ @abs_path = File.realpath(@path, File.dirname(__FILE__))
+ end
+
+ after :each do
+ CodeLoadingSpecs.spec_cleanup
+ end
+
+ platform_is_not :windows do
+ describe "when file is a symlink" do
+ before :each do
+ @link = tmp("symlink.rb", false)
+ @real_path = "#{@abs_dir}/symlink/symlink1.rb"
+ File.symlink(@real_path, @link)
+ end
+
+ after :each do
+ rm_r @link
+ end
+
+ it "loads a path relative to current file" do
+ require_relative(@link).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+ end
+ end
+
+ it "loads a path relative to the current file" do
+ require_relative(@path).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ describe "in an #instance_eval with a" do
+
+ it "synthetic file base name loads a file base name relative to the working directory" do
+ Dir.chdir @abs_dir do
+ Object.new.instance_eval("require_relative(#{File.basename(@path).inspect})", "foo.rb").should be_true
+ end
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "synthetic file path loads a relative path relative to the working directory plus the directory of the synthetic path" do
+ Dir.chdir @abs_dir do
+ Object.new.instance_eval("require_relative(File.join('..', #{File.basename(@path).inspect}))", "bar/foo.rb").should be_true
+ end
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ platform_is_not :windows do
+ it "synthetic relative file path with a Windows path separator specified loads a relative path relative to the working directory" do
+ Dir.chdir @abs_dir do
+ Object.new.instance_eval("require_relative(#{File.basename(@path).inspect})", "bar\\foo.rb").should be_true
+ end
+ ScratchPad.recorded.should == [:loaded]
+ end
+ end
+
+ it "absolute file path loads a path relative to the absolute path" do
+ Object.new.instance_eval("require_relative(#{@path.inspect})", __FILE__).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "absolute file path loads a path relative to the root directory" do
+ root = @abs_path
+ until File.dirname(root) == root
+ root = File.dirname(root)
+ end
+ root_relative = @abs_path[root.size..-1]
+ Object.new.instance_eval("require_relative(#{root_relative.inspect})", "/").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ end
+
+ it "loads a file defining many methods" do
+ require_relative("#{@dir}/methods_fixture.rb").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "raises a LoadError if the file does not exist" do
+ lambda { require_relative("#{@dir}/nonexistent.rb") }.should raise_error(LoadError)
+ ScratchPad.recorded.should == []
+ end
+
+ it "raises a LoadError if basepath does not exist" do
+ lambda { eval("require_relative('#{@dir}/nonexistent.rb')") }.should raise_error(LoadError)
+ end
+
+ it "stores the missing path in a LoadError object" do
+ path = "#{@dir}/nonexistent.rb"
+
+ lambda {
+ require_relative(path)
+ }.should(raise_error(LoadError) { |e|
+ e.path.should == File.expand_path(path, @abs_dir)
+ })
+ end
+
+ it "calls #to_str on non-String objects" do
+ name = mock("load_fixture.rb mock")
+ name.should_receive(:to_str).and_return(@path)
+ require_relative(name).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "raises a TypeError if argument does not respond to #to_str" do
+ lambda { require_relative(nil) }.should raise_error(TypeError)
+ lambda { require_relative(42) }.should raise_error(TypeError)
+ lambda {
+ require_relative([@path,@path])
+ }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an object that has #to_s but not #to_str" do
+ name = mock("load_fixture.rb mock")
+ name.stub!(:to_s).and_return(@path)
+ lambda { require_relative(name) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if #to_str does not return a String" do
+ name = mock("#to_str returns nil")
+ name.should_receive(:to_str).at_least(1).times.and_return(nil)
+ lambda { require_relative(name) }.should raise_error(TypeError)
+ end
+
+ it "calls #to_path on non-String objects" do
+ name = mock("load_fixture.rb mock")
+ name.should_receive(:to_path).and_return(@path)
+ require_relative(name).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "calls #to_str on non-String objects returned by #to_path" do
+ name = mock("load_fixture.rb mock")
+ to_path = mock("load_fixture_rb #to_path mock")
+ name.should_receive(:to_path).and_return(to_path)
+ to_path.should_receive(:to_str).and_return(@path)
+ require_relative(name).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ describe "(file extensions)" do
+ it "loads a .rb extensioned file when passed a non-extensioned path" do
+ require_relative("#{@dir}/load_fixture").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "loads a .rb extensioned file when a C-extension file of the same name is loaded" do
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.bundle"
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.dylib"
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.so"
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.dll"
+ require_relative(@path).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "does not load a C-extension file if a .rb extensioned file is already loaded" do
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.rb"
+ require_relative("#{@dir}/load_fixture").should be_false
+ ScratchPad.recorded.should == []
+ end
+
+ it "loads a .rb extensioned file when passed a non-.rb extensioned path" do
+ require_relative("#{@dir}/load_fixture.ext").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb"
+ end
+
+ it "loads a .rb extensioned file when a complex-extensioned C-extension file of the same name is loaded" do
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.bundle"
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dylib"
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.so"
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dll"
+ require_relative("#{@dir}/load_fixture.ext").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb"
+ end
+
+ it "does not load a C-extension file if a complex-extensioned .rb file is already loaded" do
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.rb"
+ require_relative("#{@dir}/load_fixture.ext").should be_false
+ ScratchPad.recorded.should == []
+ end
+ end
+
+ describe "($LOADED_FEATURES)" do
+ it "stores an absolute path" do
+ require_relative(@path).should be_true
+ $LOADED_FEATURES.should include(@abs_path)
+ end
+
+ platform_is_not :windows do
+ describe "with symlinks" do
+ before :each do
+ @symlink_to_code_dir = tmp("codesymlink")
+ File.symlink(CODE_LOADING_DIR, @symlink_to_code_dir)
+ @symlink_basename = File.basename(@symlink_to_code_dir)
+ @requiring_file = tmp("requiring")
+ end
+
+ after :each do
+ rm_r @symlink_to_code_dir, @requiring_file
+ end
+
+ it "does not canonicalize the path and stores a path with symlinks" do
+ symlink_path = "#{@symlink_basename}/load_fixture.rb"
+ absolute_path = "#{tmp("")}#{symlink_path}"
+ canonical_path = "#{CODE_LOADING_DIR}/load_fixture.rb"
+ touch(@requiring_file) { |f|
+ f.puts "require_relative #{symlink_path.inspect}"
+ }
+ load(@requiring_file)
+ ScratchPad.recorded.should == [:loaded]
+
+ features = $LOADED_FEATURES.select { |path| path.end_with?('load_fixture.rb') }
+ features.should include(absolute_path)
+ features.should_not include(canonical_path)
+ end
+
+ it "stores the same path that __FILE__ returns in the required file" do
+ symlink_path = "#{@symlink_basename}/load_fixture_and__FILE__.rb"
+ touch(@requiring_file) { |f|
+ f.puts "require_relative #{symlink_path.inspect}"
+ }
+ load(@requiring_file)
+ loaded_feature = $LOADED_FEATURES.last
+ ScratchPad.recorded.should == [loaded_feature]
+ end
+ end
+ end
+
+ it "does not store the path if the load fails" do
+ saved_loaded_features = $LOADED_FEATURES.dup
+ lambda { require_relative("#{@dir}/raise_fixture.rb") }.should raise_error(RuntimeError)
+ $LOADED_FEATURES.should == saved_loaded_features
+ end
+
+ it "does not load an absolute path that is already stored" do
+ $LOADED_FEATURES << @abs_path
+ require_relative(@path).should be_false
+ ScratchPad.recorded.should == []
+ end
+
+ it "adds the suffix of the resolved filename" do
+ require_relative("#{@dir}/load_fixture").should be_true
+ $LOADED_FEATURES.should include("#{@abs_dir}/load_fixture.rb")
+ end
+
+ it "loads a path for a file already loaded with a relative path" do
+ $LOAD_PATH << File.expand_path(@dir)
+ $LOADED_FEATURES << "load_fixture.rb" << "load_fixture"
+ require_relative(@path).should be_true
+ $LOADED_FEATURES.should include(@abs_path)
+ ScratchPad.recorded.should == [:loaded]
+ end
+ end
+end
+
+describe "Kernel#require_relative with an absolute path" do
+ it "needs to be reviewed for spec completeness"
+
+ before :each do
+ CodeLoadingSpecs.spec_setup
+ @dir = File.expand_path "../../fixtures/code", File.dirname(__FILE__)
+ @abs_dir = @dir
+ @path = File.join @dir, "load_fixture.rb"
+ @abs_path = @path
+ end
+
+ after :each do
+ CodeLoadingSpecs.spec_cleanup
+ end
+
+ it "loads a path relative to the current file" do
+ require_relative(@path).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "loads a file defining many methods" do
+ require_relative("#{@dir}/methods_fixture.rb").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "raises a LoadError if the file does not exist" do
+ lambda { require_relative("#{@dir}/nonexistent.rb") }.should raise_error(LoadError)
+ ScratchPad.recorded.should == []
+ end
+
+ it "raises a LoadError if basepath does not exist" do
+ lambda { eval("require_relative('#{@dir}/nonexistent.rb')") }.should raise_error(LoadError)
+ end
+
+ it "stores the missing path in a LoadError object" do
+ path = "#{@dir}/nonexistent.rb"
+
+ lambda {
+ require_relative(path)
+ }.should(raise_error(LoadError) { |e|
+ e.path.should == File.expand_path(path, @abs_dir)
+ })
+ end
+
+ it "calls #to_str on non-String objects" do
+ name = mock("load_fixture.rb mock")
+ name.should_receive(:to_str).and_return(@path)
+ require_relative(name).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "raises a TypeError if argument does not respond to #to_str" do
+ lambda { require_relative(nil) }.should raise_error(TypeError)
+ lambda { require_relative(42) }.should raise_error(TypeError)
+ lambda {
+ require_relative([@path,@path])
+ }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an object that has #to_s but not #to_str" do
+ name = mock("load_fixture.rb mock")
+ name.stub!(:to_s).and_return(@path)
+ lambda { require_relative(name) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if #to_str does not return a String" do
+ name = mock("#to_str returns nil")
+ name.should_receive(:to_str).at_least(1).times.and_return(nil)
+ lambda { require_relative(name) }.should raise_error(TypeError)
+ end
+
+ it "calls #to_path on non-String objects" do
+ name = mock("load_fixture.rb mock")
+ name.should_receive(:to_path).and_return(@path)
+ require_relative(name).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "calls #to_str on non-String objects returned by #to_path" do
+ name = mock("load_fixture.rb mock")
+ to_path = mock("load_fixture_rb #to_path mock")
+ name.should_receive(:to_path).and_return(to_path)
+ to_path.should_receive(:to_str).and_return(@path)
+ require_relative(name).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ describe "(file extensions)" do
+ it "loads a .rb extensioned file when passed a non-extensioned path" do
+ require_relative("#{@dir}/load_fixture").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "loads a .rb extensioned file when a C-extension file of the same name is loaded" do
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.bundle"
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.dylib"
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.so"
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.dll"
+ require_relative(@path).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "does not load a C-extension file if a .rb extensioned file is already loaded" do
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.rb"
+ require_relative("#{@dir}/load_fixture").should be_false
+ ScratchPad.recorded.should == []
+ end
+
+ it "loads a .rb extensioned file when passed a non-.rb extensioned path" do
+ require_relative("#{@dir}/load_fixture.ext").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb"
+ end
+
+ it "loads a .rb extensioned file when a complex-extensioned C-extension file of the same name is loaded" do
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.bundle"
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dylib"
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.so"
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dll"
+ require_relative("#{@dir}/load_fixture.ext").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb"
+ end
+
+ it "does not load a C-extension file if a complex-extensioned .rb file is already loaded" do
+ $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.rb"
+ require_relative("#{@dir}/load_fixture.ext").should be_false
+ ScratchPad.recorded.should == []
+ end
+ end
+
+ describe "($LOAD_FEATURES)" do
+ it "stores an absolute path" do
+ require_relative(@path).should be_true
+ $LOADED_FEATURES.should include(@abs_path)
+ end
+
+ it "does not store the path if the load fails" do
+ saved_loaded_features = $LOADED_FEATURES.dup
+ lambda { require_relative("#{@dir}/raise_fixture.rb") }.should raise_error(RuntimeError)
+ $LOADED_FEATURES.should == saved_loaded_features
+ end
+
+ it "does not load an absolute path that is already stored" do
+ $LOADED_FEATURES << @abs_path
+ require_relative(@path).should be_false
+ ScratchPad.recorded.should == []
+ end
+
+ it "adds the suffix of the resolved filename" do
+ require_relative("#{@dir}/load_fixture").should be_true
+ $LOADED_FEATURES.should include("#{@abs_dir}/load_fixture.rb")
+ end
+
+ it "loads a path for a file already loaded with a relative path" do
+ $LOAD_PATH << File.expand_path(@dir)
+ $LOADED_FEATURES << "load_fixture.rb" << "load_fixture"
+ require_relative(@path).should be_true
+ $LOADED_FEATURES.should include(@abs_path)
+ ScratchPad.recorded.should == [:loaded]
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/require_spec.rb b/spec/ruby/core/kernel/require_spec.rb
new file mode 100644
index 0000000000..dc3da4b7e6
--- /dev/null
+++ b/spec/ruby/core/kernel/require_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/code_loading'
+require_relative 'shared/require'
+
+describe "Kernel#require" do
+ before :each do
+ CodeLoadingSpecs.spec_setup
+ end
+
+ after :each do
+ CodeLoadingSpecs.spec_cleanup
+ end
+
+ # if this fails, update your rubygems
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:require)
+ end
+
+ it_behaves_like :kernel_require_basic, :require, CodeLoadingSpecs::Method.new
+ it_behaves_like :kernel_require, :require, CodeLoadingSpecs::Method.new
+end
+
+describe "Kernel.require" do
+ before :each do
+ CodeLoadingSpecs.spec_setup
+ end
+
+ after :each do
+ CodeLoadingSpecs.spec_cleanup
+ end
+
+ it_behaves_like :kernel_require_basic, :require, Kernel
+ it_behaves_like :kernel_require, :require, Kernel
+end
diff --git a/spec/ruby/core/kernel/respond_to_missing_spec.rb b/spec/ruby/core/kernel/respond_to_missing_spec.rb
new file mode 100644
index 0000000000..cc82031e26
--- /dev/null
+++ b/spec/ruby/core/kernel/respond_to_missing_spec.rb
@@ -0,0 +1,100 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#respond_to_missing?" do
+ before :each do
+ @a = KernelSpecs::A.new
+ end
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:respond_to_missing?, false)
+ end
+
+ it "is only an instance method" do
+ Kernel.method(:respond_to_missing?).owner.should == Kernel
+ end
+
+ it "is not called when #respond_to? would return true" do
+ obj = mock('object')
+ obj.stub!(:glark)
+ obj.should_not_receive(:respond_to_missing?)
+ obj.respond_to?(:glark).should be_true
+ end
+
+ it "is called with a 2nd argument of false when #respond_to? is" do
+ obj = mock('object')
+ obj.should_receive(:respond_to_missing?).with(:undefined_method, false)
+ obj.respond_to?(:undefined_method, false)
+ end
+
+ it "is called a 2nd argument of false when #respond_to? is called with only 1 argument" do
+ obj = mock('object')
+ obj.should_receive(:respond_to_missing?).with(:undefined_method, false)
+ obj.respond_to?(:undefined_method)
+ end
+
+ it "is called with true as the second argument when #respond_to? is" do
+ obj = mock('object')
+ obj.should_receive(:respond_to_missing?).with(:undefined_method, true)
+ obj.respond_to?(:undefined_method, true)
+ end
+
+ it "is called when #respond_to? would return false" do
+ obj = mock('object')
+ obj.should_receive(:respond_to_missing?).with(:undefined_method, false)
+ obj.respond_to?(:undefined_method)
+ end
+
+ it "causes #respond_to? to return true if called and not returning false" do
+ obj = mock('object')
+ obj.should_receive(:respond_to_missing?).with(:undefined_method, false).and_return(:glark)
+ obj.respond_to?(:undefined_method).should be_true
+ end
+
+ it "causes #respond_to? to return false if called and returning false" do
+ obj = mock('object')
+ obj.should_receive(:respond_to_missing?).with(:undefined_method, false).and_return(false)
+ obj.respond_to?(:undefined_method).should be_false
+ end
+
+ it "causes #respond_to? to return false if called and returning nil" do
+ obj = mock('object')
+ obj.should_receive(:respond_to_missing?).with(:undefined_method, false).and_return(nil)
+ obj.respond_to?(:undefined_method).should be_false
+ end
+
+ it "isn't called when obj responds to the given public method" do
+ @a.should_not_receive(:respond_to_missing?)
+ @a.respond_to?(:pub_method).should be_true
+ end
+
+ it "isn't called when obj responds to the given public method, include_private = true" do
+ @a.should_not_receive(:respond_to_missing?)
+ @a.respond_to?(:pub_method, true).should be_true
+ end
+
+ it "is called when obj responds to the given protected method, include_private = false" do
+ @a.should_receive(:respond_to_missing?)
+ @a.respond_to?(:protected_method, false).should be_false
+ end
+
+ it "isn't called when obj responds to the given protected method, include_private = true" do
+ @a.should_not_receive(:respond_to_missing?)
+ @a.respond_to?(:protected_method, true).should be_true
+ end
+
+ it "is called when obj responds to the given private method, include_private = false" do
+ @a.should_receive(:respond_to_missing?).with(:private_method, false)
+ @a.respond_to?(:private_method)
+ end
+
+ it "isn't called when obj responds to the given private method, include_private = true" do
+ @a.should_not_receive(:respond_to_missing?)
+ @a.respond_to?(:private_method, true).should be_true
+ end
+
+ it "is called for missing class methods" do
+ @a.class.should_receive(:respond_to_missing?).with(:oOoOoO, false)
+ @a.class.respond_to?(:oOoOoO)
+ end
+end
diff --git a/spec/ruby/core/kernel/respond_to_spec.rb b/spec/ruby/core/kernel/respond_to_spec.rb
new file mode 100644
index 0000000000..80487421d7
--- /dev/null
+++ b/spec/ruby/core/kernel/respond_to_spec.rb
@@ -0,0 +1,73 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#respond_to?" do
+ before :each do
+ @a = KernelSpecs::A.new
+ end
+
+ it "is a public method" do
+ Kernel.should have_public_instance_method(:respond_to?, false)
+ end
+
+ it "is only an instance method" do
+ Kernel.method(:respond_to?).owner.should == Kernel
+ end
+
+ it "returns false if the given method was undefined" do
+ @a.respond_to?(:undefed_method).should == false
+ @a.respond_to?("undefed_method").should == false
+ end
+
+ it "returns true if obj responds to the given public method" do
+ @a.respond_to?(:pub_method).should == true
+ @a.respond_to?("pub_method").should == true
+ end
+
+ it "throws a type error if argument can't be coerced into a Symbol" do
+ lambda { @a.respond_to?(Object.new) }.should raise_error(TypeError)
+ end
+
+ it "returns false if obj responds to the given protected method" do
+ @a.respond_to?(:protected_method).should == false
+ @a.respond_to?("protected_method").should == false
+ end
+
+ it "returns false if obj responds to the given private method" do
+ @a.respond_to?(:private_method).should == false
+ @a.respond_to?("private_method").should == false
+ end
+
+ it "returns true if obj responds to the given protected method (include_private = true)" do
+ @a.respond_to?(:protected_method, true).should == true
+ @a.respond_to?("protected_method", true).should == true
+ end
+
+ it "returns false if obj responds to the given protected method (include_private = false)" do
+ @a.respond_to?(:protected_method, false).should == false
+ @a.respond_to?("protected_method", false).should == false
+ end
+
+ it "returns false even if obj responds to the given private method (include_private = false)" do
+ @a.respond_to?(:private_method, false).should == false
+ @a.respond_to?("private_method", false).should == false
+ end
+
+ it "returns true if obj responds to the given private method (include_private = true)" do
+ @a.respond_to?(:private_method, true).should == true
+ @a.respond_to?("private_method", true).should == true
+ end
+
+ it "does not change method visibility when finding private method" do
+ KernelSpecs::VisibilityChange.respond_to?(:new, false).should == false
+ KernelSpecs::VisibilityChange.respond_to?(:new, true).should == true
+ lambda { KernelSpecs::VisibilityChange.new }.should raise_error(NoMethodError)
+ end
+
+ it "indicates if an object responds to a particular message" do
+ class KernelSpecs::Foo; def bar; 'done'; end; end
+ KernelSpecs::Foo.new.respond_to?(:bar).should == true
+ KernelSpecs::Foo.new.respond_to?(:invalid_and_silly_method_name).should == false
+ end
+
+end
diff --git a/spec/ruby/core/kernel/select_spec.rb b/spec/ruby/core/kernel/select_spec.rb
new file mode 100644
index 0000000000..2661bbfac7
--- /dev/null
+++ b/spec/ruby/core/kernel/select_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#select" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:select)
+ end
+end
+
+describe "Kernel.select" do
+ it "needs to be reviewed for spec completeness"
+
+ it 'does not block when timeout is 0' do
+ IO.pipe do |read, write|
+ IO.select([read], [], [], 0).should == nil
+ write.write 'data'
+ IO.select([read], [], [], 0).should == [[read], [], []]
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/send_spec.rb b/spec/ruby/core/kernel/send_spec.rb
new file mode 100644
index 0000000000..9a4d261964
--- /dev/null
+++ b/spec/ruby/core/kernel/send_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/basicobject/send'
+
+describe "Kernel#send" do
+ it "invokes the named public method" do
+ class KernelSpecs::Foo
+ def bar
+ 'done'
+ end
+ end
+ KernelSpecs::Foo.new.send(:bar).should == 'done'
+ end
+
+ it "invokes the named alias of a public method" do
+ class KernelSpecs::Foo
+ def bar
+ 'done'
+ end
+ alias :aka :bar
+ end
+ KernelSpecs::Foo.new.send(:aka).should == 'done'
+ end
+
+ it "invokes the named protected method" do
+ class KernelSpecs::Foo
+ protected
+ def bar
+ 'done'
+ end
+ end
+ KernelSpecs::Foo.new.send(:bar).should == 'done'
+ end
+
+ it "invokes the named private method" do
+ class KernelSpecs::Foo
+ private
+ def bar
+ 'done2'
+ end
+ end
+ KernelSpecs::Foo.new.send(:bar).should == 'done2'
+ end
+
+ it "invokes the named alias of a private method" do
+ class KernelSpecs::Foo
+ private
+ def bar
+ 'done2'
+ end
+ alias :aka :bar
+ end
+ KernelSpecs::Foo.new.send(:aka).should == 'done2'
+ end
+
+ it "invokes the named alias of a protected method" do
+ class KernelSpecs::Foo
+ protected
+ def bar
+ 'done2'
+ end
+ alias :aka :bar
+ end
+ KernelSpecs::Foo.new.send(:aka).should == 'done2'
+ end
+
+ it_behaves_like :basicobject_send, :send
+end
diff --git a/spec/ruby/core/kernel/set_trace_func_spec.rb b/spec/ruby/core/kernel/set_trace_func_spec.rb
new file mode 100644
index 0000000000..1f43e7a009
--- /dev/null
+++ b/spec/ruby/core/kernel/set_trace_func_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#set_trace_func" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:set_trace_func)
+ end
+end
+
+describe "Kernel.set_trace_func" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/shared/dup_clone.rb b/spec/ruby/core/kernel/shared/dup_clone.rb
new file mode 100644
index 0000000000..116989958b
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/dup_clone.rb
@@ -0,0 +1,149 @@
+class ObjectSpecDup
+ def initialize()
+ @obj = :original
+ end
+
+ attr_accessor :obj
+end
+
+class ObjectSpecDupInitCopy
+ def initialize()
+ @obj = :original
+ end
+
+ attr_accessor :obj, :original
+
+ def initialize_copy(original)
+ @obj = :init_copy
+ @original = original
+ end
+
+ private :initialize_copy
+end
+
+describe :kernel_dup_clone, shared: true do
+ it "returns a new object duplicated from the original" do
+ o = ObjectSpecDup.new
+ o2 = ObjectSpecDup.new
+
+ o.obj = 10
+
+ o3 = o.send(@method)
+
+ o3.obj.should == 10
+ o2.obj.should == :original
+ end
+
+ it "produces a shallow copy, contained objects are not recursively dupped" do
+ o = ObjectSpecDup.new
+ array = [1, 2]
+ o.obj = array
+
+ o2 = o.send(@method)
+ o2.obj.should equal(o.obj)
+ end
+
+ it "calls #initialize_copy on the NEW object if available, passing in original object" do
+ o = ObjectSpecDupInitCopy.new
+ o2 = o.send(@method)
+
+ o.obj.should == :original
+ o2.obj.should == :init_copy
+ o2.original.should equal(o)
+ end
+
+ it "preserves tainted state from the original" do
+ o = ObjectSpecDupInitCopy.new
+ o2 = o.send(@method)
+ o.taint
+ o3 = o.send(@method)
+
+ o2.tainted?.should == false
+ o3.tainted?.should == true
+ end
+
+ it "does not preserve the object_id" do
+ o1 = ObjectSpecDupInitCopy.new
+ old_object_id = o1.object_id
+ o2 = o1.send(@method)
+ o2.object_id.should_not == old_object_id
+ end
+
+ it "preserves untrusted state from the original" do
+ o = ObjectSpecDupInitCopy.new
+ o2 = o.send(@method)
+ o.untrust
+ o3 = o.send(@method)
+
+ o2.untrusted?.should == false
+ o3.untrusted?.should == true
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "raises a TypeError for NilClass" do
+ lambda { nil.send(@method) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError for TrueClass" do
+ lambda { true.send(@method) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError for FalseClass" do
+ lambda { false.send(@method) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError for Fixnum" do
+ lambda { 1.send(@method) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError for Symbol" do
+ lambda { :my_symbol.send(@method) }.should raise_error(TypeError)
+ end
+ end
+
+ ruby_version_is '2.4' do
+ it "returns nil for NilClass" do
+ nil.send(@method).should == nil
+ end
+
+ it "returns true for TrueClass" do
+ true.send(@method).should == true
+ end
+
+ it "returns false for FalseClass" do
+ false.send(@method).should == false
+ end
+
+ it "returns the same Integer for Integer" do
+ 1.send(@method).should == 1
+ end
+
+ it "returns the same Symbol for Symbol" do
+ :my_symbol.send(@method).should == :my_symbol
+ end
+ end
+
+ ruby_version_is ''...'2.5' do
+ it "raises a TypeError for Complex" do
+ c = Complex(1.3, 3.1)
+ lambda { c.send(@method) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError for Rational" do
+ r = Rational(1, 3)
+ lambda { r.send(@method) }.should raise_error(TypeError)
+ end
+ end
+
+ ruby_version_is '2.5' do
+ it "returns self for Complex" do
+ c = Complex(1.3, 3.1)
+ c.send(@method).should equal c
+ end
+
+ it "returns self for Rational" do
+ r = Rational(1, 3)
+ r.send(@method).should equal r
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/shared/kind_of.rb b/spec/ruby/core/kernel/shared/kind_of.rb
new file mode 100644
index 0000000000..0614c02214
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/kind_of.rb
@@ -0,0 +1,55 @@
+require_relative '../fixtures/classes'
+
+describe :kernel_kind_of, shared: true do
+ before :each do
+ @o = KernelSpecs::KindaClass.new
+ end
+
+ it "returns true if given class is the object's class" do
+ @o.send(@method, KernelSpecs::KindaClass).should == true
+ end
+
+ it "returns true if given class is an ancestor of the object's class" do
+ @o.send(@method, KernelSpecs::AncestorClass).should == true
+ @o.send(@method, String).should == true
+ @o.send(@method, Object).should == true
+ end
+
+ it "returns false if the given class is not object's class nor an ancestor" do
+ @o.send(@method, Array).should == false
+ end
+
+ it "returns true if given a Module that is included in object's class" do
+ @o.send(@method, KernelSpecs::MyModule).should == true
+ end
+
+ it "returns true if given a Module that is included one of object's ancestors only" do
+ @o.send(@method, KernelSpecs::AncestorModule).should == true
+ end
+
+ it "returns true if given a Module that object has been extended with" do
+ @o.send(@method, KernelSpecs::MyExtensionModule).should == true
+ end
+
+ it "returns true if given a Module that object has been prepended with" do
+ @o.send(@method, KernelSpecs::MyPrependedModule).should == true
+ end
+
+ it "returns false if given a Module not included nor prepended in object's class nor ancestors" do
+ @o.send(@method, KernelSpecs::SomeOtherModule).should == false
+ end
+
+ it "raises a TypeError if given an object that is not a Class nor a Module" do
+ lambda { @o.send(@method, 1) }.should raise_error(TypeError)
+ lambda { @o.send(@method, 'KindaClass') }.should raise_error(TypeError)
+ lambda { @o.send(@method, :KindaClass) }.should raise_error(TypeError)
+ lambda { @o.send(@method, Object.new) }.should raise_error(TypeError)
+ end
+
+ it "does not take into account `class` method overriding" do
+ def @o.class; Integer; end
+
+ @o.send(@method, Integer).should == false
+ @o.send(@method, KernelSpecs::KindaClass).should == true
+ end
+end
diff --git a/spec/ruby/core/kernel/shared/lambda.rb b/spec/ruby/core/kernel/shared/lambda.rb
new file mode 100644
index 0000000000..bebb111c43
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/lambda.rb
@@ -0,0 +1,9 @@
+describe :kernel_lambda, shared: true do
+ it "returns a Proc object" do
+ send(@method) { true }.kind_of?(Proc).should == true
+ end
+
+ it "raises an ArgumentError when no block is given" do
+ lambda { send(@method) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/kernel/shared/load.rb b/spec/ruby/core/kernel/shared/load.rb
new file mode 100644
index 0000000000..0ce7d58d2c
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/load.rb
@@ -0,0 +1,139 @@
+describe :kernel_load, shared: true do
+ before :each do
+ CodeLoadingSpecs.spec_setup
+ @path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR
+ end
+
+ after :each do
+ CodeLoadingSpecs.spec_cleanup
+ end
+
+ it "loads a non-extensioned file as a Ruby source file" do
+ path = File.expand_path "load_fixture", CODE_LOADING_DIR
+ @object.load(path).should be_true
+ ScratchPad.recorded.should == [:no_ext]
+ end
+
+ it "loads a non .rb extensioned file as a Ruby source file" do
+ path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR
+ @object.load(path).should be_true
+ ScratchPad.recorded.should == [:no_rb_ext]
+ end
+
+ it "loads from the current working directory" do
+ Dir.chdir CODE_LOADING_DIR do
+ @object.load("load_fixture.rb").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+ end
+
+ it "loads a file that recursively requires itself" do
+ path = File.expand_path "recursive_require_fixture.rb", CODE_LOADING_DIR
+ -> {
+ $VERBOSE = true
+ @object.load(path).should be_true
+ }.should complain(/circular require considered harmful/)
+ ScratchPad.recorded.should == [:loaded, :loaded]
+ end
+
+ it "loads a file that recursively loads itself" do
+ path = File.expand_path "recursive_load_fixture.rb", CODE_LOADING_DIR
+ @object.load(path).should be_true
+ ScratchPad.recorded.should == [:loaded, :loaded]
+ end
+
+ it "loads a file each time the method is called" do
+ @object.load(@path).should be_true
+ @object.load(@path).should be_true
+ ScratchPad.recorded.should == [:loaded, :loaded]
+ end
+
+ it "loads a file even when the name appears in $LOADED_FEATURES" do
+ $LOADED_FEATURES << @path
+ @object.load(@path).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "loads a file that has been loaded by #require" do
+ @object.require(@path).should be_true
+ @object.load(@path).should be_true
+ ScratchPad.recorded.should == [:loaded, :loaded]
+ end
+
+ it "loads file even after $LOAD_PATH change" do
+ $LOAD_PATH << CODE_LOADING_DIR
+ @object.load("load_fixture.rb").should be_true
+ $LOAD_PATH.unshift CODE_LOADING_DIR + "/gem"
+ @object.load("load_fixture.rb").should be_true
+ ScratchPad.recorded.should == [:loaded, :loaded_gem]
+ end
+
+ it "does not cause #require with the same path to fail" do
+ @object.load(@path).should be_true
+ @object.require(@path).should be_true
+ ScratchPad.recorded.should == [:loaded, :loaded]
+ end
+
+ it "does not add the loaded path to $LOADED_FEATURES" do
+ saved_loaded_features = $LOADED_FEATURES.dup
+ @object.load(@path).should be_true
+ $LOADED_FEATURES.should == saved_loaded_features
+ end
+
+ it "raises a LoadError if passed a non-extensioned path that does not exist but a .rb extensioned path does exist" do
+ path = File.expand_path "load_ext_fixture", CODE_LOADING_DIR
+ lambda { @object.load(path) }.should raise_error(LoadError)
+ end
+
+ describe "when passed true for 'wrap'" do
+ it "loads from an existing path" do
+ path = File.expand_path "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
+ @object.load(path, true)
+
+ Object.const_defined?(:LoadSpecWrap).should be_false
+ end
+
+ it "allows referencing outside namespaces" do
+ path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ @object.load(path, true)
+
+ ScratchPad.recorded.first.should be_an_instance_of(Class)
+ end
+
+ describe "with top-level methods" do
+ before :each do
+ path = File.expand_path "load_wrap_method_fixture.rb", CODE_LOADING_DIR
+ @object.load(path, true)
+ end
+
+ it "allows calling top-level methods" do
+ ScratchPad.recorded.last.should == :load_wrap_loaded
+ end
+
+ it "does not pollute the receiver" do
+ lambda { @object.send(:top_level_method) }.should raise_error(NameError)
+ end
+ end
+ end
+
+ describe "(shell expansion)" do
+ before :each do
+ @env_home = ENV["HOME"]
+ ENV["HOME"] = CODE_LOADING_DIR
+ end
+
+ after :each do
+ ENV["HOME"] = @env_home
+ end
+
+ it "expands a tilde to the HOME environment variable as the path to load" do
+ @object.require("~/load_fixture.rb").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/shared/method.rb b/spec/ruby/core/kernel/shared/method.rb
new file mode 100644
index 0000000000..006ebbffb8
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/method.rb
@@ -0,0 +1,50 @@
+require_relative '../../../spec_helper'
+
+describe :kernel_method, shared: true do
+ it "returns a method object for a valid method" do
+ class KernelSpecs::Foo; def bar; 'done'; end; end
+ m = KernelSpecs::Foo.new.send(@method, :bar)
+ m.should be_an_instance_of Method
+ m.call.should == 'done'
+ end
+
+ it "returns a method object for a valid singleton method" do
+ class KernelSpecs::Foo; def self.bar; 'class done'; end; end
+ m = KernelSpecs::Foo.send(@method, :bar)
+ m.should be_an_instance_of Method
+ m.call.should == 'class done'
+ end
+
+ it "returns a method object if we repond_to_missing? method" do
+ m = KernelSpecs::RespondViaMissing.new.send(@method, :handled_publicly)
+ m.should be_an_instance_of Method
+ m.call(42).should == "Done handled_publicly([42])"
+ end
+
+ it "raises a NameError for an invalid method name" do
+ class KernelSpecs::Foo; def bar; 'done'; end; end
+ lambda {
+ KernelSpecs::Foo.new.send(@method, :invalid_and_silly_method_name)
+ }.should raise_error(NameError)
+ end
+
+ it "raises a NameError for an invalid singleton method name" do
+ class KernelSpecs::Foo; def self.bar; 'done'; end; end
+ lambda { KernelSpecs::Foo.send(@method, :baz) }.should raise_error(NameError)
+ end
+
+ it "changes the method called for super on a target aliased method" do
+ c1 = Class.new do
+ def a; 'a'; end
+ def b; 'b'; end
+ end
+ c2 = Class.new(c1) do
+ def a; super; end
+ alias b a
+ end
+
+ c2.new.a.should == 'a'
+ c2.new.b.should == 'a'
+ c2.new.send(@method, :b).call.should == 'a'
+ end
+end
diff --git a/spec/ruby/core/kernel/shared/require.rb b/spec/ruby/core/kernel/shared/require.rb
new file mode 100644
index 0000000000..a81a68088a
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/require.rb
@@ -0,0 +1,765 @@
+describe :kernel_require_basic, shared: true do
+ describe "(path resolution)" do
+ it "loads an absolute path" do
+ path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR
+ @object.send(@method, path).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "loads a non-canonical absolute path" do
+ path = File.join CODE_LOADING_DIR, "..", "code", "load_fixture.rb"
+ @object.send(@method, path).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "loads a file defining many methods" do
+ path = File.expand_path "methods_fixture.rb", CODE_LOADING_DIR
+ @object.send(@method, path).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "raises a LoadError if the file does not exist" do
+ path = File.expand_path "nonexistent.rb", CODE_LOADING_DIR
+ File.exist?(path).should be_false
+ lambda { @object.send(@method, path) }.should raise_error(LoadError)
+ ScratchPad.recorded.should == []
+ end
+
+ # Can't make a file unreadable on these platforms
+ platform_is_not :windows, :cygwin do
+ as_user do
+ describe "with an unreadable file" do
+ before :each do
+ @path = tmp("unreadable_file.rb")
+ touch @path
+ File.chmod 0000, @path
+ end
+
+ after :each do
+ File.chmod 0666, @path
+ rm_r @path
+ end
+
+ it "raises a LoadError" do
+ File.exist?(@path).should be_true
+ lambda { @object.send(@method, @path) }.should raise_error(LoadError)
+ end
+ end
+ end
+ end
+
+ it "calls #to_str on non-String objects" do
+ path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR
+ name = mock("load_fixture.rb mock")
+ name.should_receive(:to_str).and_return(path)
+ @object.send(@method, name).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "raises a TypeError if passed nil" do
+ lambda { @object.send(@method, nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed a Fixnum" do
+ lambda { @object.send(@method, 42) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an Array" do
+ lambda { @object.send(@method, []) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an object that does not provide #to_str" do
+ lambda { @object.send(@method, mock("not a filename")) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an object that has #to_s but not #to_str" do
+ name = mock("load_fixture.rb mock")
+ name.stub!(:to_s).and_return("load_fixture.rb")
+ $LOAD_PATH << "."
+ Dir.chdir CODE_LOADING_DIR do
+ lambda { @object.send(@method, name) }.should raise_error(TypeError)
+ end
+ end
+
+ it "raises a TypeError if #to_str does not return a String" do
+ name = mock("#to_str returns nil")
+ name.should_receive(:to_str).at_least(1).times.and_return(nil)
+ lambda { @object.send(@method, name) }.should raise_error(TypeError)
+ end
+
+ it "calls #to_path on non-String objects" do
+ name = mock("load_fixture.rb mock")
+ name.stub!(:to_path).and_return("load_fixture.rb")
+ $LOAD_PATH << "."
+ Dir.chdir CODE_LOADING_DIR do
+ @object.send(@method, name).should be_true
+ end
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "calls #to_path on a String" do
+ path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR
+ str = mock("load_fixture.rb mock")
+ str.should_receive(:to_path).and_return(path)
+ @object.send(@method, str).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "calls #to_str on non-String objects returned by #to_path" do
+ path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR
+ name = mock("load_fixture.rb mock")
+ to_path = mock("load_fixture_rb #to_path mock")
+ name.should_receive(:to_path).and_return(to_path)
+ to_path.should_receive(:to_str).and_return(path)
+ @object.send(@method, name).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ # "http://redmine.ruby-lang.org/issues/show/2578"
+ it "loads a ./ relative path from the current working directory with empty $LOAD_PATH" do
+ Dir.chdir CODE_LOADING_DIR do
+ @object.send(@method, "./load_fixture.rb").should be_true
+ end
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "loads a ../ relative path from the current working directory with empty $LOAD_PATH" do
+ Dir.chdir CODE_LOADING_DIR do
+ @object.send(@method, "../code/load_fixture.rb").should be_true
+ end
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "loads a ./ relative path from the current working directory with non-empty $LOAD_PATH" do
+ $LOAD_PATH << "an_irrelevant_dir"
+ Dir.chdir CODE_LOADING_DIR do
+ @object.send(@method, "./load_fixture.rb").should be_true
+ end
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "loads a ../ relative path from the current working directory with non-empty $LOAD_PATH" do
+ $LOAD_PATH << "an_irrelevant_dir"
+ Dir.chdir CODE_LOADING_DIR do
+ @object.send(@method, "../code/load_fixture.rb").should be_true
+ end
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "loads a non-canonical path from the current working directory with non-empty $LOAD_PATH" do
+ $LOAD_PATH << "an_irrelevant_dir"
+ Dir.chdir CODE_LOADING_DIR do
+ @object.send(@method, "../code/../code/load_fixture.rb").should be_true
+ end
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "resolves a filename against $LOAD_PATH entries" do
+ $LOAD_PATH << CODE_LOADING_DIR
+ @object.send(@method, "load_fixture.rb").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "does not require file twice after $LOAD_PATH change" do
+ $LOAD_PATH << CODE_LOADING_DIR
+ @object.require("load_fixture.rb").should be_true
+ $LOAD_PATH.unshift CODE_LOADING_DIR + "/gem"
+ @object.require("load_fixture.rb").should be_false
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "does not resolve a ./ relative path against $LOAD_PATH entries" do
+ $LOAD_PATH << CODE_LOADING_DIR
+ lambda do
+ @object.send(@method, "./load_fixture.rb")
+ end.should raise_error(LoadError)
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not resolve a ../ relative path against $LOAD_PATH entries" do
+ $LOAD_PATH << CODE_LOADING_DIR
+ lambda do
+ @object.send(@method, "../code/load_fixture.rb")
+ end.should raise_error(LoadError)
+ ScratchPad.recorded.should == []
+ end
+
+ it "resolves a non-canonical path against $LOAD_PATH entries" do
+ $LOAD_PATH << File.dirname(CODE_LOADING_DIR)
+ @object.send(@method, "code/../code/load_fixture.rb").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "loads a path with duplicate path separators" do
+ $LOAD_PATH << "."
+ sep = File::Separator + File::Separator
+ path = ["..", "code", "load_fixture.rb"].join(sep)
+ Dir.chdir CODE_LOADING_DIR do
+ @object.send(@method, path).should be_true
+ end
+ ScratchPad.recorded.should == [:loaded]
+ end
+ end
+end
+
+describe :kernel_require, shared: true do
+ describe "(path resolution)" do
+ # For reference see [ruby-core:24155] in which matz confirms this feature is
+ # intentional for security reasons.
+ it "does not load a bare filename unless the current working directory is in $LOAD_PATH" do
+ Dir.chdir CODE_LOADING_DIR do
+ lambda { @object.require("load_fixture.rb") }.should raise_error(LoadError)
+ ScratchPad.recorded.should == []
+ end
+ end
+
+ it "does not load a relative path unless the current working directory is in $LOAD_PATH" do
+ Dir.chdir File.dirname(CODE_LOADING_DIR) do
+ lambda do
+ @object.require("code/load_fixture.rb")
+ end.should raise_error(LoadError)
+ ScratchPad.recorded.should == []
+ end
+ end
+
+ it "loads a file that recursively requires itself" do
+ path = File.expand_path "recursive_require_fixture.rb", CODE_LOADING_DIR
+ -> {
+ $VERBOSE = true
+ @object.require(path).should be_true
+ }.should complain(/circular require considered harmful/)
+ ScratchPad.recorded.should == [:loaded]
+ end
+ end
+
+ describe "(non-extensioned path)" do
+ before :each do
+ a = File.expand_path "a", CODE_LOADING_DIR
+ b = File.expand_path "b", CODE_LOADING_DIR
+ $LOAD_PATH.replace [a, b]
+ end
+
+ it "loads a .rb extensioned file when a C-extension file exists on an earlier load path" do
+ @object.require("load_fixture").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+ end
+
+ describe "(file extensions)" do
+ it "loads a .rb extensioned file when passed a non-extensioned path" do
+ path = File.expand_path "load_fixture", CODE_LOADING_DIR
+ File.exist?(path).should be_true
+ @object.require(path).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "loads a .rb extensioned file when a C-extension file of the same name is loaded" do
+ $LOADED_FEATURES << File.expand_path("load_fixture.bundle", CODE_LOADING_DIR)
+ $LOADED_FEATURES << File.expand_path("load_fixture.dylib", CODE_LOADING_DIR)
+ $LOADED_FEATURES << File.expand_path("load_fixture.so", CODE_LOADING_DIR)
+ $LOADED_FEATURES << File.expand_path("load_fixture.dll", CODE_LOADING_DIR)
+ path = File.expand_path "load_fixture", CODE_LOADING_DIR
+ @object.require(path).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "does not load a C-extension file if a .rb extensioned file is already loaded" do
+ $LOADED_FEATURES << File.expand_path("load_fixture.rb", CODE_LOADING_DIR)
+ path = File.expand_path "load_fixture", CODE_LOADING_DIR
+ @object.require(path).should be_false
+ ScratchPad.recorded.should == []
+ end
+
+ it "loads a .rb extensioned file when passed a non-.rb extensioned path" do
+ path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR
+ File.exist?(path).should be_true
+ @object.require(path).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "loads a .rb extensioned file when a complex-extensioned C-extension file of the same name is loaded" do
+ $LOADED_FEATURES << File.expand_path("load_fixture.ext.bundle", CODE_LOADING_DIR)
+ $LOADED_FEATURES << File.expand_path("load_fixture.ext.dylib", CODE_LOADING_DIR)
+ $LOADED_FEATURES << File.expand_path("load_fixture.ext.so", CODE_LOADING_DIR)
+ $LOADED_FEATURES << File.expand_path("load_fixture.ext.dll", CODE_LOADING_DIR)
+ path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR
+ @object.require(path).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "does not load a C-extension file if a complex-extensioned .rb file is already loaded" do
+ $LOADED_FEATURES << File.expand_path("load_fixture.ext.rb", CODE_LOADING_DIR)
+ path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR
+ @object.require(path).should be_false
+ ScratchPad.recorded.should == []
+ end
+ end
+
+ describe "($LOADED_FEATURES)" do
+ before :each do
+ @path = File.expand_path("load_fixture.rb", CODE_LOADING_DIR)
+ end
+
+ it "stores an absolute path" do
+ @object.require(@path).should be_true
+ $LOADED_FEATURES.should include(@path)
+ end
+
+ platform_is_not :windows do
+ describe "with symlinks" do
+ before :each do
+ @symlink_to_code_dir = tmp("codesymlink")
+ File.symlink(CODE_LOADING_DIR, @symlink_to_code_dir)
+
+ $LOAD_PATH.delete(CODE_LOADING_DIR)
+ $LOAD_PATH.unshift(@symlink_to_code_dir)
+ end
+
+ after :each do
+ rm_r @symlink_to_code_dir
+ end
+
+ it "does not canonicalize the path and stores a path with symlinks" do
+ symlink_path = "#{@symlink_to_code_dir}/load_fixture.rb"
+ canonical_path = "#{CODE_LOADING_DIR}/load_fixture.rb"
+ @object.require(symlink_path).should be_true
+ ScratchPad.recorded.should == [:loaded]
+
+ features = $LOADED_FEATURES.select { |path| path.end_with?('load_fixture.rb') }
+ features.should include(symlink_path)
+ features.should_not include(canonical_path)
+ end
+
+ it "stores the same path that __FILE__ returns in the required file" do
+ symlink_path = "#{@symlink_to_code_dir}/load_fixture_and__FILE__.rb"
+ @object.require(symlink_path).should be_true
+ loaded_feature = $LOADED_FEATURES.last
+ ScratchPad.recorded.should == [loaded_feature]
+ end
+ end
+
+ describe "with symlinks in the required feature and $LOAD_PATH" do
+ before :each do
+ @dir = tmp("realdir")
+ mkdir_p @dir
+ @file = "#{@dir}/realfile.rb"
+ touch(@file) { |f| f.puts 'ScratchPad << __FILE__' }
+
+ @symlink_to_dir = tmp("symdir").freeze
+ File.symlink(@dir, @symlink_to_dir)
+ @symlink_to_file = "#{@dir}/symfile.rb"
+ File.symlink("realfile.rb", @symlink_to_file)
+ end
+
+ after :each do
+ rm_r @dir, @symlink_to_dir
+ end
+
+ ruby_version_is ""..."2.4.4" do
+ it "canonicalizes neither the entry in $LOAD_PATH nor the filename passed to #require" do
+ $LOAD_PATH.unshift(@symlink_to_dir)
+ @object.require("symfile").should be_true
+ loaded_feature = "#{@symlink_to_dir}/symfile.rb"
+ ScratchPad.recorded.should == [loaded_feature]
+ $".last.should == loaded_feature
+ $LOAD_PATH[0].should == @symlink_to_dir
+ end
+ end
+
+ ruby_version_is "2.4.4" do
+ it "canonicalizes the entry in $LOAD_PATH but not the filename passed to #require" do
+ $LOAD_PATH.unshift(@symlink_to_dir)
+ @object.require("symfile").should be_true
+ loaded_feature = "#{@dir}/symfile.rb"
+ ScratchPad.recorded.should == [loaded_feature]
+ $".last.should == loaded_feature
+ $LOAD_PATH[0].should == @symlink_to_dir
+ end
+ end
+ end
+ end
+
+ it "does not store the path if the load fails" do
+ $LOAD_PATH << CODE_LOADING_DIR
+ saved_loaded_features = $LOADED_FEATURES.dup
+ lambda { @object.require("raise_fixture.rb") }.should raise_error(RuntimeError)
+ $LOADED_FEATURES.should == saved_loaded_features
+ end
+
+ it "does not load an absolute path that is already stored" do
+ $LOADED_FEATURES << @path
+ @object.require(@path).should be_false
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not load a ./ relative path that is already stored" do
+ $LOADED_FEATURES << "./load_fixture.rb"
+ Dir.chdir CODE_LOADING_DIR do
+ @object.require("./load_fixture.rb").should be_false
+ end
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not load a ../ relative path that is already stored" do
+ $LOADED_FEATURES << "../load_fixture.rb"
+ Dir.chdir CODE_LOADING_DIR do
+ @object.require("../load_fixture.rb").should be_false
+ end
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not load a non-canonical path that is already stored" do
+ $LOADED_FEATURES << "code/../code/load_fixture.rb"
+ $LOAD_PATH << File.dirname(CODE_LOADING_DIR)
+ @object.require("code/../code/load_fixture.rb").should be_false
+ ScratchPad.recorded.should == []
+ end
+
+ it "respects being replaced with a new array" do
+ prev = $LOADED_FEATURES.dup
+
+ @object.require(@path).should be_true
+ $LOADED_FEATURES.should include(@path)
+
+ $LOADED_FEATURES.replace(prev)
+
+ $LOADED_FEATURES.should_not include(@path)
+ @object.require(@path).should be_true
+ $LOADED_FEATURES.should include(@path)
+ end
+
+ it "does not load twice the same file with and without extension" do
+ $LOAD_PATH << CODE_LOADING_DIR
+ @object.require("load_fixture.rb").should be_true
+ @object.require("load_fixture").should be_false
+ end
+
+ describe "when a non-extensioned file is in $LOADED_FEATURES" do
+ before :each do
+ $LOADED_FEATURES << "load_fixture"
+ end
+
+ it "loads a .rb extensioned file when a non extensioned file is in $LOADED_FEATURES" do
+ $LOAD_PATH << CODE_LOADING_DIR
+ @object.require("load_fixture").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "loads a .rb extensioned file from a subdirectory" do
+ $LOAD_PATH << File.dirname(CODE_LOADING_DIR)
+ @object.require("code/load_fixture").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "returns false if the file is not found" do
+ Dir.chdir File.dirname(CODE_LOADING_DIR) do
+ @object.require("load_fixture").should be_false
+ ScratchPad.recorded.should == []
+ end
+ end
+
+ it "returns false when passed a path and the file is not found" do
+ $LOADED_FEATURES << "code/load_fixture"
+ Dir.chdir CODE_LOADING_DIR do
+ @object.require("code/load_fixture").should be_false
+ ScratchPad.recorded.should == []
+ end
+ end
+ end
+
+ it "stores ../ relative paths as absolute paths" do
+ Dir.chdir CODE_LOADING_DIR do
+ @object.require("../code/load_fixture.rb").should be_true
+ end
+ $LOADED_FEATURES.should include(@path)
+ end
+
+ it "stores ./ relative paths as absolute paths" do
+ Dir.chdir CODE_LOADING_DIR do
+ @object.require("./load_fixture.rb").should be_true
+ end
+ $LOADED_FEATURES.should include(@path)
+ end
+
+ it "collapses duplicate path separators" do
+ $LOAD_PATH << "."
+ sep = File::Separator + File::Separator
+ path = ["..", "code", "load_fixture.rb"].join(sep)
+ Dir.chdir CODE_LOADING_DIR do
+ @object.require(path).should be_true
+ end
+ $LOADED_FEATURES.should include(@path)
+ end
+
+ it "expands absolute paths containing .." do
+ path = File.join CODE_LOADING_DIR, "..", "code", "load_fixture.rb"
+ @object.require(path).should be_true
+ $LOADED_FEATURES.should include(@path)
+ end
+
+ it "adds the suffix of the resolved filename" do
+ $LOAD_PATH << CODE_LOADING_DIR
+ @object.require("load_fixture").should be_true
+ $LOADED_FEATURES.should include(@path)
+ end
+
+ it "does not load a non-canonical path for a file already loaded" do
+ $LOADED_FEATURES << @path
+ $LOAD_PATH << File.dirname(CODE_LOADING_DIR)
+ @object.require("code/../code/load_fixture.rb").should be_false
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not load a ./ relative path for a file already loaded" do
+ $LOADED_FEATURES << @path
+ $LOAD_PATH << "an_irrelevant_dir"
+ Dir.chdir CODE_LOADING_DIR do
+ @object.require("./load_fixture.rb").should be_false
+ end
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not load a ../ relative path for a file already loaded" do
+ $LOADED_FEATURES << @path
+ $LOAD_PATH << "an_irrelevant_dir"
+ Dir.chdir CODE_LOADING_DIR do
+ @object.require("../code/load_fixture.rb").should be_false
+ end
+ ScratchPad.recorded.should == []
+ end
+
+ ruby_version_is ""..."2.5" do
+ it "complex, enumerator, rational, thread and unicode_normalize are already required" do
+ provided = %w[complex enumerator rational thread unicode_normalize]
+ features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems')
+ provided.each { |feature|
+ features.should =~ /\b#{feature}\.(rb|so|jar)$/
+ }
+
+ code = provided.map { |f| "puts require #{f.inspect}\n" }.join
+ required = ruby_exe(code, options: '--disable-gems')
+ required.should == "false\n" * provided.size
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "complex, enumerator, rational and thread are already required" do
+ provided = %w[complex enumerator rational thread]
+ features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems')
+ provided.each { |feature|
+ features.should =~ /\b#{feature}\.(rb|so|jar)$/
+ }
+
+ code = provided.map { |f| "puts require #{f.inspect}\n" }.join
+ required = ruby_exe(code, options: '--disable-gems')
+ required.should == "false\n" * provided.size
+ end
+ end
+ end
+
+ describe "(shell expansion)" do
+ before :each do
+ @path = File.expand_path("load_fixture.rb", CODE_LOADING_DIR)
+ @env_home = ENV["HOME"]
+ ENV["HOME"] = CODE_LOADING_DIR
+ end
+
+ after :each do
+ ENV["HOME"] = @env_home
+ end
+
+ # "#3171"
+ it "performs tilde expansion on a .rb file before storing paths in $LOADED_FEATURES" do
+ @object.require("~/load_fixture.rb").should be_true
+ $LOADED_FEATURES.should include(@path)
+ end
+
+ it "performs tilde expansion on a non-extensioned file before storing paths in $LOADED_FEATURES" do
+ @object.require("~/load_fixture").should be_true
+ $LOADED_FEATURES.should include(@path)
+ end
+ end
+
+ describe "(concurrently)" do
+ before :each do
+ ScratchPad.record []
+ @path = File.expand_path "concurrent.rb", CODE_LOADING_DIR
+ @path2 = File.expand_path "concurrent2.rb", CODE_LOADING_DIR
+ @path3 = File.expand_path "concurrent3.rb", CODE_LOADING_DIR
+ end
+
+ after :each do
+ ScratchPad.clear
+ $LOADED_FEATURES.delete @path
+ $LOADED_FEATURES.delete @path2
+ $LOADED_FEATURES.delete @path3
+ end
+
+ # Quick note about these specs:
+ #
+ # The behavior we're spec'ing requires that t2 enter #require, see t1 is
+ # loading @path, grab a lock, and wait on it.
+ #
+ # We do make sure that t2 starts the require once t1 is in the middle
+ # of concurrent.rb, but we then need to get t2 to get far enough into #require
+ # to see t1's lock and try to lock it.
+ it "blocks a second thread from returning while the 1st is still requiring" do
+ fin = false
+
+ t1_res = nil
+ t2_res = nil
+
+ t2 = nil
+ t1 = Thread.new do
+ Thread.pass until t2
+ Thread.current[:wait_for] = t2
+ t1_res = @object.require(@path)
+ Thread.pass until fin
+ ScratchPad.recorded << :t1_post
+ end
+
+ t2 = Thread.new do
+ Thread.pass until t1[:in_concurrent_rb]
+ $VERBOSE, @verbose = nil, $VERBOSE
+ begin
+ t2_res = @object.require(@path)
+ ScratchPad.recorded << :t2_post
+ ensure
+ $VERBOSE = @verbose
+ fin = true
+ end
+ end
+
+ t1.join
+ t2.join
+
+ t1_res.should be_true
+ t2_res.should be_false
+
+ ScratchPad.recorded.should == [:con_pre, :con_post, :t2_post, :t1_post]
+ end
+
+ it "blocks based on the path" do
+ t1_res = nil
+ t2_res = nil
+
+ t2 = nil
+ t1 = Thread.new do
+ Thread.pass until t2
+ Thread.current[:concurrent_require_thread] = t2
+ t1_res = @object.require(@path2)
+ end
+
+ t2 = Thread.new do
+ Thread.pass until t1[:in_concurrent_rb2]
+ t2_res = @object.require(@path3)
+ end
+
+ t1.join
+ t2.join
+
+ t1_res.should be_true
+ t2_res.should be_true
+
+ ScratchPad.recorded.should == [:con2_pre, :con3, :con2_post]
+ end
+
+ it "allows a 2nd require if the 1st raised an exception" do
+ fin = false
+
+ t2_res = nil
+
+ t2 = nil
+ t1 = Thread.new do
+ Thread.pass until t2
+ Thread.current[:wait_for] = t2
+ Thread.current[:con_raise] = true
+
+ lambda {
+ @object.require(@path)
+ }.should raise_error(RuntimeError)
+
+ Thread.pass until fin
+ ScratchPad.recorded << :t1_post
+ end
+
+ t2 = Thread.new do
+ Thread.pass until t1[:in_concurrent_rb]
+ $VERBOSE, @verbose = nil, $VERBOSE
+ begin
+ t2_res = @object.require(@path)
+ ScratchPad.recorded << :t2_post
+ ensure
+ $VERBOSE = @verbose
+ fin = true
+ end
+ end
+
+ t1.join
+ t2.join
+
+ t2_res.should be_true
+
+ ScratchPad.recorded.should == [:con_pre, :con_pre, :con_post, :t2_post, :t1_post]
+ end
+
+ # "redmine #5754"
+ it "blocks a 3rd require if the 1st raises an exception and the 2nd is still running" do
+ fin = false
+
+ t1_res = nil
+ t2_res = nil
+
+ raised = false
+
+ t2 = nil
+ t1 = Thread.new do
+ Thread.current[:con_raise] = true
+
+ lambda {
+ @object.require(@path)
+ }.should raise_error(RuntimeError)
+
+ raised = true
+
+ # This hits the bug. Because MRI removes its internal lock from a table
+ # when the exception is raised, this #require doesn't see that t2 is in
+ # the middle of requiring the file, so this #require runs when it should not.
+ Thread.pass until t2 && t2[:in_concurrent_rb]
+ t1_res = @object.require(@path)
+
+ Thread.pass until fin
+ ScratchPad.recorded << :t1_post
+ end
+
+ t2 = Thread.new do
+ Thread.pass until raised
+ Thread.current[:wait_for] = t1
+ begin
+ t2_res = @object.require(@path)
+ ScratchPad.recorded << :t2_post
+ ensure
+ fin = true
+ end
+ end
+
+ t1.join
+ t2.join
+
+ t1_res.should be_false
+ t2_res.should be_true
+
+ ScratchPad.recorded.should == [:con_pre, :con_pre, :con_post, :t2_post, :t1_post]
+ end
+ end
+
+ it "stores the missing path in a LoadError object" do
+ path = "abcd1234"
+
+ lambda {
+ @object.send(@method, path)
+ }.should raise_error(LoadError) { |e|
+ e.path.should == path
+ }
+ end
+end
diff --git a/spec/ruby/core/kernel/shared/sprintf.rb b/spec/ruby/core/kernel/shared/sprintf.rb
new file mode 100644
index 0000000000..925c79e82b
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/sprintf.rb
@@ -0,0 +1,877 @@
+require_relative '../../../shared/hash/key_error'
+
+describe :kernel_sprintf, shared: true do
+ def format(*args)
+ @method.call(*args)
+ end
+
+ describe "integer formats" do
+ it "converts argument into Integer with to_int" do
+ obj = Object.new
+ def obj.to_i; 10; end
+ def obj.to_int; 10; end
+
+ obj.should_receive(:to_int).and_return(10)
+ format("%b", obj).should == "1010"
+ end
+
+ it "converts argument into Integer with to_i if to_int isn't available" do
+ obj = Object.new
+ def obj.to_i; 10; end
+
+ obj.should_receive(:to_i).and_return(10)
+ format("%b", obj).should == "1010"
+ end
+
+ it "converts String argument with Kernel#Integer" do
+ format("%d", "0b1010").should == "10"
+ format("%d", "112").should == "112"
+ format("%d", "0127").should == "87"
+ format("%d", "0xc4").should == "196"
+ end
+
+ it "raises TypeError exception if cannot convert to Integer" do
+ -> () {
+ format("%b", Object.new)
+ }.should raise_error(TypeError)
+ end
+
+ ["b", "B"].each do |f|
+ describe f do
+ it "converts argument as a binary number" do
+ format("%#{f}", 10).should == "1010"
+ end
+
+ it "displays negative number as a two's complement prefixed with '..1'" do
+ format("%#{f}", -10).should == "..1" + "0110"
+ end
+
+ it "collapse negative number representation if it equals 1" do
+ format("%#{f}", -1).should_not == "..11"
+ format("%#{f}", -1).should == "..1"
+ end
+ end
+ end
+
+ ["d", "i", "u"].each do |f|
+ describe f do
+ it "converts argument as a decimal number" do
+ format("%#{f}", 112).should == "112"
+ format("%#{f}", -112).should == "-112"
+ end
+
+ it "works well with large numbers" do
+ format("%#{f}", 1234567890987654321).should == "1234567890987654321"
+ end
+ end
+ end
+
+ describe "o" do
+ it "converts argument as an octal number" do
+ format("%o", 87).should == "127"
+ end
+
+ it "displays negative number as a two's complement prefixed with '..7'" do
+ format("%o", -87).should == "..7" + "651"
+ end
+
+ it "collapse negative number representation if it equals 7" do
+ format("%o", -1).should_not == "..77"
+ format("%o", -1).should == "..7"
+ end
+ end
+
+ describe "x" do
+ it "converts argument as a hexadecimal number" do
+ format("%x", 196).should == "c4"
+ end
+
+ it "displays negative number as a two's complement prefixed with '..f'" do
+ format("%x", -196).should == "..f" + "3c"
+ end
+
+ it "collapse negative number representation if it equals f" do
+ format("%x", -1).should_not == "..ff"
+ format("%x", -1).should == "..f"
+ end
+ end
+
+ describe "X" do
+ it "converts argument as a hexadecimal number with uppercase letters" do
+ format("%X", 196).should == "C4"
+ end
+
+ it "displays negative number as a two's complement prefixed with '..f'" do
+ format("%X", -196).should == "..F" + "3C"
+ end
+
+ it "collapse negative number representation if it equals F" do
+ format("%X", -1).should_not == "..FF"
+ format("%X", -1).should == "..F"
+ end
+ end
+ end
+
+ describe "float formats" do
+ it "converts argument into Float" do
+ obj = mock("float")
+ obj.should_receive(:to_f).and_return(9.6)
+ format("%f", obj).should == "9.600000"
+ end
+
+ it "raises TypeError exception if cannot convert to Float" do
+ -> () {
+ format("%f", Object.new)
+ }.should raise_error(TypeError)
+ end
+
+ {"e" => "e", "E" => "E"}.each_pair do |f, exp|
+ describe f do
+ it "converts argument into exponential notation [-]d.dddddde[+-]dd" do
+ format("%#{f}", 109.52).should == "1.095200#{exp}+02"
+ format("%#{f}", -109.52).should == "-1.095200#{exp}+02"
+ format("%#{f}", 0.10952).should == "1.095200#{exp}-01"
+ format("%#{f}", -0.10952).should == "-1.095200#{exp}-01"
+ end
+
+ it "cuts excessive digits and keeps only 6 ones" do
+ format("%#{f}", 1.123456789).should == "1.123457#{exp}+00"
+ end
+
+ it "rounds the last significant digit to the closest one" do
+ format("%#{f}", 1.555555555).should == "1.555556#{exp}+00"
+ format("%#{f}", -1.555555555).should == "-1.555556#{exp}+00"
+ format("%#{f}", 1.444444444).should == "1.444444#{exp}+00"
+ end
+
+ it "displays Float::INFINITY as Inf" do
+ format("%#{f}", Float::INFINITY).should == "Inf"
+ format("%#{f}", -Float::INFINITY).should == "-Inf"
+ end
+
+ it "displays Float::NAN as NaN" do
+ format("%#{f}", Float::NAN).should == "NaN"
+ format("%#{f}", -Float::NAN).should == "NaN"
+ end
+ end
+ end
+
+ describe "f" do
+ it "converts floating point argument as [-]ddd.dddddd" do
+ format("%f", 10.952).should == "10.952000"
+ format("%f", -10.952).should == "-10.952000"
+ end
+
+ it "cuts excessive digits and keeps only 6 ones" do
+ format("%f", 1.123456789).should == "1.123457"
+ end
+
+ it "rounds the last significant digit to the closest one" do
+ format("%f", 1.555555555).should == "1.555556"
+ format("%f", -1.555555555).should == "-1.555556"
+ format("%f", 1.444444444).should == "1.444444"
+ end
+
+ it "displays Float::INFINITY as Inf" do
+ format("%f", Float::INFINITY).should == "Inf"
+ format("%f", -Float::INFINITY).should == "-Inf"
+ end
+
+ it "displays Float::NAN as NaN" do
+ format("%f", Float::NAN).should == "NaN"
+ format("%f", -Float::NAN).should == "NaN"
+ end
+ end
+
+ {"g" => "e", "G" => "E"}.each_pair do |f, exp|
+ describe f do
+ context "the exponent is less than -4" do
+ it "converts a floating point number using exponential form" do
+ format("%#{f}", 0.0000123456).should == "1.23456#{exp}-05"
+ format("%#{f}", -0.0000123456).should == "-1.23456#{exp}-05"
+
+ format("%#{f}", 0.000000000123456).should == "1.23456#{exp}-10"
+ format("%#{f}", -0.000000000123456).should == "-1.23456#{exp}-10"
+ end
+ end
+
+ context "the exponent is greater than or equal to the precision (6 by default)" do
+ it "converts a floating point number using exponential form" do
+ format("%#{f}", 1234567).should == "1.23457#{exp}+06"
+ format("%#{f}", 1234567890123).should == "1.23457#{exp}+12"
+ format("%#{f}", -1234567).should == "-1.23457#{exp}+06"
+ end
+ end
+
+ context "otherwise" do
+ it "converts a floating point number in dd.dddd form" do
+ format("%#{f}", 0.0001).should == "0.0001"
+ format("%#{f}", -0.0001).should == "-0.0001"
+ format("%#{f}", 123456).should == "123456"
+ format("%#{f}", -123456).should == "-123456"
+ end
+
+ it "cuts excessive digits in fractional part and keeps only 4 ones" do
+ format("%#{f}", 12.12341111).should == "12.1234"
+ format("%#{f}", -12.12341111).should == "-12.1234"
+ end
+
+ it "rounds the last significant digit to the closest one in fractional part" do
+ format("%#{f}", 1.555555555).should == "1.55556"
+ format("%#{f}", -1.555555555).should == "-1.55556"
+ format("%#{f}", 1.444444444).should == "1.44444"
+ end
+
+ it "cuts fraction part to have only 6 digits at all" do
+ format("%#{f}", 1.1234567).should == "1.12346"
+ format("%#{f}", 12.1234567).should == "12.1235"
+ format("%#{f}", 123.1234567).should == "123.123"
+ format("%#{f}", 1234.1234567).should == "1234.12"
+ format("%#{f}", 12345.1234567).should == "12345.1"
+ format("%#{f}", 123456.1234567).should == "123456"
+ end
+ end
+
+ it "displays Float::INFINITY as Inf" do
+ format("%#{f}", Float::INFINITY).should == "Inf"
+ format("%#{f}", -Float::INFINITY).should == "-Inf"
+ end
+
+ it "displays Float::NAN as NaN" do
+ format("%#{f}", Float::NAN).should == "NaN"
+ format("%#{f}", -Float::NAN).should == "NaN"
+ end
+ end
+ end
+
+ describe "a" do
+ it "converts floating point argument as [-]0xh.hhhhp[+-]dd" do
+ format("%a", 196).should == "0x1.88p+7"
+ format("%a", -196).should == "-0x1.88p+7"
+ format("%a", 196.1).should == "0x1.8833333333333p+7"
+ format("%a", 0.01).should == "0x1.47ae147ae147bp-7"
+ format("%a", -0.01).should == "-0x1.47ae147ae147bp-7"
+ end
+
+ it "displays Float::INFINITY as Inf" do
+ format("%a", Float::INFINITY).should == "Inf"
+ format("%a", -Float::INFINITY).should == "-Inf"
+ end
+
+ it "displays Float::NAN as NaN" do
+ format("%a", Float::NAN).should == "NaN"
+ format("%a", -Float::NAN).should == "NaN"
+ end
+ end
+
+ describe "A" do
+ it "converts floating point argument as [-]0xh.hhhhp[+-]dd and use uppercase X and P" do
+ format("%A", 196).should == "0X1.88P+7"
+ format("%A", -196).should == "-0X1.88P+7"
+ format("%A", 196.1).should == "0X1.8833333333333P+7"
+ format("%A", 0.01).should == "0X1.47AE147AE147BP-7"
+ format("%A", -0.01).should == "-0X1.47AE147AE147BP-7"
+ end
+
+ it "displays Float::INFINITY as Inf" do
+ format("%A", Float::INFINITY).should == "Inf"
+ format("%A", -Float::INFINITY).should == "-Inf"
+ end
+
+ it "displays Float::NAN as NaN" do
+ format("%A", Float::NAN).should == "NaN"
+ format("%A", -Float::NAN).should == "NaN"
+ end
+ end
+ end
+
+ describe "other formats" do
+ describe "c" do
+ it "displays character if argument is a numeric code of character" do
+ format("%c", 97).should == "a"
+ end
+
+ it "displays character if argument is a single character string" do
+ format("%c", "a").should == "a"
+ end
+
+ it "raises ArgumentError if argument is a string of several characters" do
+ -> () {
+ format("%c", "abc")
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if argument is an empty string" do
+ -> () {
+ format("%c", "")
+ }.should raise_error(ArgumentError)
+ end
+
+ it "supports Unicode characters" do
+ format("%c", 1286).should == "Ô†"
+ format("%c", "Ø´").should == "Ø´"
+ end
+ end
+
+ describe "p" do
+ it "displays argument.inspect value" do
+ obj = mock("object")
+ obj.should_receive(:inspect).and_return("<inspect-result>")
+ format("%p", obj).should == "<inspect-result>"
+ end
+ end
+
+ describe "s" do
+ it "substitute argument passes as a string" do
+ format("%s", "abc").should == "abc"
+ end
+
+ it "converts argument to string with to_s" do
+ obj = mock("string")
+ obj.should_receive(:to_s).and_return("abc")
+ format("%s", obj).should == "abc"
+ end
+
+ it "does not try to convert with to_str" do
+ obj = BasicObject.new
+ def obj.to_str
+ "abc"
+ end
+
+ -> () {
+ format("%s", obj)
+ }.should raise_error(NoMethodError)
+ end
+ end
+
+ describe "%" do
+ ruby_version_is ""..."2.5" do
+ it "alone displays the percent sign" do
+ format("%").should == "%"
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "alone raises an ArgumentError" do
+ -> {
+ format("%")
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "is escaped by %" do
+ format("%%").should == "%"
+ format("%%d", 10).should == "%d"
+ end
+ end
+ end
+
+ describe "flags" do
+ describe "space" do
+ context "applies to numeric formats bBdiouxXeEfgGaA" do
+ it "leaves a space at the start of non-negative numbers" do
+ format("% b", 10).should == " 1010"
+ format("% B", 10).should == " 1010"
+ format("% d", 112).should == " 112"
+ format("% i", 112).should == " 112"
+ format("% o", 87).should == " 127"
+ format("% u", 112).should == " 112"
+ format("% x", 196).should == " c4"
+ format("% X", 196).should == " C4"
+
+ format("% e", 109.52).should == " 1.095200e+02"
+ format("% E", 109.52).should == " 1.095200E+02"
+ format("% f", 10.952).should == " 10.952000"
+ format("% g", 12.1234).should == " 12.1234"
+ format("% G", 12.1234).should == " 12.1234"
+ format("% a", 196).should == " 0x1.88p+7"
+ format("% A", 196).should == " 0X1.88P+7"
+ end
+
+ it "does not leave a space at the start of negative numbers" do
+ format("% b", -10).should == "-1010"
+ format("% B", -10).should == "-1010"
+ format("% d", -112).should == "-112"
+ format("% i", -112).should == "-112"
+ format("% o", -87).should == "-127"
+ format("% u", -112).should == "-112"
+ format("% x", -196).should == "-c4"
+ format("% X", -196).should == "-C4"
+
+ format("% e", -109.52).should == "-1.095200e+02"
+ format("% E", -109.52).should == "-1.095200E+02"
+ format("% f", -10.952).should == "-10.952000"
+ format("% g", -12.1234).should == "-12.1234"
+ format("% G", -12.1234).should == "-12.1234"
+ format("% a", -196).should == "-0x1.88p+7"
+ format("% A", -196).should == "-0X1.88P+7"
+ end
+
+ it "prevents converting negative argument to two's complement form" do
+ format("% b", -10).should == "-1010"
+ format("% B", -10).should == "-1010"
+ format("% o", -87).should == "-127"
+ format("% x", -196).should == "-c4"
+ format("% X", -196).should == "-C4"
+ end
+
+ it "treats several white spaces as one" do
+ format("% b", 10).should == " 1010"
+ format("% B", 10).should == " 1010"
+ format("% d", 112).should == " 112"
+ format("% i", 112).should == " 112"
+ format("% o", 87).should == " 127"
+ format("% u", 112).should == " 112"
+ format("% x", 196).should == " c4"
+ format("% X", 196).should == " C4"
+
+ format("% e", 109.52).should == " 1.095200e+02"
+ format("% E", 109.52).should == " 1.095200E+02"
+ format("% f", 10.952).should == " 10.952000"
+ format("% g", 12.1234).should == " 12.1234"
+ format("% G", 12.1234).should == " 12.1234"
+ format("% a", 196).should == " 0x1.88p+7"
+ format("% A", 196).should == " 0X1.88P+7"
+ end
+ end
+ end
+
+ describe "(digit)$" do
+ it "specifies the absolute argument number for this field" do
+ format("%2$b", 0, 10).should == "1010"
+ format("%2$B", 0, 10).should == "1010"
+ format("%2$d", 0, 112).should == "112"
+ format("%2$i", 0, 112).should == "112"
+ format("%2$o", 0, 87).should == "127"
+ format("%2$u", 0, 112).should == "112"
+ format("%2$x", 0, 196).should == "c4"
+ format("%2$X", 0, 196).should == "C4"
+
+ format("%2$e", 0, 109.52).should == "1.095200e+02"
+ format("%2$E", 0, 109.52).should == "1.095200E+02"
+ format("%2$f", 0, 10.952).should == "10.952000"
+ format("%2$g", 0, 12.1234).should == "12.1234"
+ format("%2$G", 0, 12.1234).should == "12.1234"
+ format("%2$a", 0, 196).should == "0x1.88p+7"
+ format("%2$A", 0, 196).should == "0X1.88P+7"
+
+ format("%2$c", 1, 97).should == "a"
+ format("%2$p", "a", []).should == "[]"
+ format("%2$s", "-", "abc").should == "abc"
+ end
+
+ it "raises exception if argument number is bigger than actual arguments list" do
+ -> () {
+ format("%4$d", 1, 2, 3)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "ignores '-' sign" do
+ format("%2$d", 1, 2, 3).should == "2"
+ format("%-2$d", 1, 2, 3).should == "2"
+ end
+
+ it "raises ArgumentError exception when absolute and relative argument numbers are mixed" do
+ -> () {
+ format("%1$d %d", 1, 2)
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "#" do
+ context "applies to format o" do
+ it "increases the precision until the first digit will be `0' if it is not formatted as complements" do
+ format("%#o", 87).should == "0127"
+ end
+
+ it "does nothing for negative argument" do
+ format("%#o", -87).should == "..7651"
+ end
+ end
+
+ context "applies to formats bBxX" do
+ it "prefixes the result with 0x, 0X, 0b and 0B respectively for non-zero argument" do
+ format("%#b", 10).should == "0b1010"
+ format("%#b", -10).should == "0b..10110"
+ format("%#B", 10).should == "0B1010"
+ format("%#B", -10).should == "0B..10110"
+
+ format("%#x", 196).should == "0xc4"
+ format("%#x", -196).should == "0x..f3c"
+ format("%#X", 196).should == "0XC4"
+ format("%#X", -196).should == "0X..F3C"
+ end
+
+ it "does nothing for zero argument" do
+ format("%#b", 0).should == "0"
+ format("%#B", 0).should == "0"
+
+ format("%#o", 0).should == "0"
+
+ format("%#x", 0).should == "0"
+ format("%#X", 0).should == "0"
+ end
+ end
+
+ context "applies to formats aAeEfgG" do
+ it "forces a decimal point to be added, even if no digits follow" do
+ format("%#.0a", 16.25).should == "0x1.p+4"
+ format("%#.0A", 16.25).should == "0X1.P+4"
+
+ format("%#.0e", 100).should == "1.e+02"
+ format("%#.0E", 100).should == "1.E+02"
+
+ format("%#.0f", 123.4).should == "123."
+
+ format("%#g", 123456).should == "123456."
+ format("%#G", 123456).should == "123456."
+ end
+
+ it "changes format from dd.dddd to exponential form for gG" do
+ format("%#.0g", 123.4).should_not == "123."
+ format("%#.0g", 123.4).should == "1.e+02"
+ end
+ end
+
+ context "applies to gG" do
+ it "does not remove trailing zeros" do
+ format("%#g", 123.4).should == "123.400"
+ format("%#g", 123.4).should == "123.400"
+ end
+ end
+ end
+
+ describe "+" do
+ context "applies to numeric formats bBdiouxXaAeEfgG" do
+ it "adds a leading plus sign to non-negative numbers" do
+ format("%+b", 10).should == "+1010"
+ format("%+B", 10).should == "+1010"
+ format("%+d", 112).should == "+112"
+ format("%+i", 112).should == "+112"
+ format("%+o", 87).should == "+127"
+ format("%+u", 112).should == "+112"
+ format("%+x", 196).should == "+c4"
+ format("%+X", 196).should == "+C4"
+
+ format("%+e", 109.52).should == "+1.095200e+02"
+ format("%+E", 109.52).should == "+1.095200E+02"
+ format("%+f", 10.952).should == "+10.952000"
+ format("%+g", 12.1234).should == "+12.1234"
+ format("%+G", 12.1234).should == "+12.1234"
+ format("%+a", 196).should == "+0x1.88p+7"
+ format("%+A", 196).should == "+0X1.88P+7"
+ end
+
+ it "does not use two's complement form for negative numbers for formats bBoxX" do
+ format("%+b", -10).should == "-1010"
+ format("%+B", -10).should == "-1010"
+ format("%+o", -87).should == "-127"
+ format("%+x", -196).should == "-c4"
+ format("%+X", -196).should == "-C4"
+ end
+ end
+ end
+
+ describe "-" do
+ it "left-justifies the result of conversion if width is specified" do
+ format("%-10b", 10).should == "1010 "
+ format("%-10B", 10).should == "1010 "
+ format("%-10d", 112).should == "112 "
+ format("%-10i", 112).should == "112 "
+ format("%-10o", 87).should == "127 "
+ format("%-10u", 112).should == "112 "
+ format("%-10x", 196).should == "c4 "
+ format("%-10X", 196).should == "C4 "
+
+ format("%-20e", 109.52).should == "1.095200e+02 "
+ format("%-20E", 109.52).should == "1.095200E+02 "
+ format("%-20f", 10.952).should == "10.952000 "
+ format("%-20g", 12.1234).should == "12.1234 "
+ format("%-20G", 12.1234).should == "12.1234 "
+ format("%-20a", 196).should == "0x1.88p+7 "
+ format("%-20A", 196).should == "0X1.88P+7 "
+
+ format("%-10c", 97).should == "a "
+ format("%-10p", []).should == "[] "
+ format("%-10s", "abc").should == "abc "
+ end
+ end
+
+ describe "0 (zero)" do
+ context "applies to numeric formats bBdiouxXaAeEfgG and width is specified" do
+ it "pads with zeros, not spaces" do
+ format("%010b", 10).should == "0000001010"
+ format("%010B", 10).should == "0000001010"
+ format("%010d", 112).should == "0000000112"
+ format("%010i", 112).should == "0000000112"
+ format("%010o", 87).should == "0000000127"
+ format("%010u", 112).should == "0000000112"
+ format("%010x", 196).should == "00000000c4"
+ format("%010X", 196).should == "00000000C4"
+
+ format("%020e", 109.52).should == "000000001.095200e+02"
+ format("%020E", 109.52).should == "000000001.095200E+02"
+ format("%020f", 10.952).should == "0000000000010.952000"
+ format("%020g", 12.1234).should == "000000000000012.1234"
+ format("%020G", 12.1234).should == "000000000000012.1234"
+ format("%020a", 196).should == "0x000000000001.88p+7"
+ format("%020A", 196).should == "0X000000000001.88P+7"
+ end
+
+ it "uses radix-1 when displays negative argument as a two's complement" do
+ format("%010b", -10).should == "..11110110"
+ format("%010B", -10).should == "..11110110"
+ format("%010o", -87).should == "..77777651"
+ format("%010x", -196).should == "..ffffff3c"
+ format("%010X", -196).should == "..FFFFFF3C"
+ end
+ end
+ end
+
+ describe "*" do
+ it "uses the previous argument as the field width" do
+ format("%*b", 10, 10).should == " 1010"
+ format("%*B", 10, 10).should == " 1010"
+ format("%*d", 10, 112).should == " 112"
+ format("%*i", 10, 112).should == " 112"
+ format("%*o", 10, 87).should == " 127"
+ format("%*u", 10, 112).should == " 112"
+ format("%*x", 10, 196).should == " c4"
+ format("%*X", 10, 196).should == " C4"
+
+ format("%*e", 20, 109.52).should == " 1.095200e+02"
+ format("%*E", 20, 109.52).should == " 1.095200E+02"
+ format("%*f", 20, 10.952).should == " 10.952000"
+ format("%*g", 20, 12.1234).should == " 12.1234"
+ format("%*G", 20, 12.1234).should == " 12.1234"
+ format("%*a", 20, 196).should == " 0x1.88p+7"
+ format("%*A", 20, 196).should == " 0X1.88P+7"
+
+ format("%*c", 10, 97).should == " a"
+ format("%*p", 10, []).should == " []"
+ format("%*s", 10, "abc").should == " abc"
+ end
+
+ it "left-justifies the result if width is negative" do
+ format("%*b", -10, 10).should == "1010 "
+ format("%*B", -10, 10).should == "1010 "
+ format("%*d", -10, 112).should == "112 "
+ format("%*i", -10, 112).should == "112 "
+ format("%*o", -10, 87).should == "127 "
+ format("%*u", -10, 112).should == "112 "
+ format("%*x", -10, 196).should == "c4 "
+ format("%*X", -10, 196).should == "C4 "
+
+ format("%*e", -20, 109.52).should == "1.095200e+02 "
+ format("%*E", -20, 109.52).should == "1.095200E+02 "
+ format("%*f", -20, 10.952).should == "10.952000 "
+ format("%*g", -20, 12.1234).should == "12.1234 "
+ format("%*G", -20, 12.1234).should == "12.1234 "
+ format("%*a", -20, 196).should == "0x1.88p+7 "
+ format("%*A", -20, 196).should == "0X1.88P+7 "
+
+ format("%*c", -10, 97).should == "a "
+ format("%*p", -10, []).should == "[] "
+ format("%*s", -10, "abc").should == "abc "
+ end
+
+ it "uses the specified argument as the width if * is followed by a number and $" do
+ format("%1$*2$b", 10, 10).should == " 1010"
+ format("%1$*2$B", 10, 10).should == " 1010"
+ format("%1$*2$d", 112, 10).should == " 112"
+ format("%1$*2$i", 112, 10).should == " 112"
+ format("%1$*2$o", 87, 10).should == " 127"
+ format("%1$*2$u", 112, 10).should == " 112"
+ format("%1$*2$x", 196, 10).should == " c4"
+ format("%1$*2$X", 196, 10).should == " C4"
+
+ format("%1$*2$e", 109.52, 20).should == " 1.095200e+02"
+ format("%1$*2$E", 109.52, 20).should == " 1.095200E+02"
+ format("%1$*2$f", 10.952, 20).should == " 10.952000"
+ format("%1$*2$g", 12.1234, 20).should == " 12.1234"
+ format("%1$*2$G", 12.1234, 20).should == " 12.1234"
+ format("%1$*2$a", 196, 20).should == " 0x1.88p+7"
+ format("%1$*2$A", 196, 20).should == " 0X1.88P+7"
+
+ format("%1$*2$c", 97, 10).should == " a"
+ format("%1$*2$p", [], 10).should == " []"
+ format("%1$*2$s", "abc", 10).should == " abc"
+ end
+
+ it "left-justifies the result if specified with $ argument is negative" do
+ format("%1$*2$b", 10, -10).should == "1010 "
+ format("%1$*2$B", 10, -10).should == "1010 "
+ format("%1$*2$d", 112, -10).should == "112 "
+ format("%1$*2$i", 112, -10).should == "112 "
+ format("%1$*2$o", 87, -10).should == "127 "
+ format("%1$*2$u", 112, -10).should == "112 "
+ format("%1$*2$x", 196, -10).should == "c4 "
+ format("%1$*2$X", 196, -10).should == "C4 "
+
+ format("%1$*2$e", 109.52, -20).should == "1.095200e+02 "
+ format("%1$*2$E", 109.52, -20).should == "1.095200E+02 "
+ format("%1$*2$f", 10.952, -20).should == "10.952000 "
+ format("%1$*2$g", 12.1234, -20).should == "12.1234 "
+ format("%1$*2$G", 12.1234, -20).should == "12.1234 "
+ format("%1$*2$a", 196, -20).should == "0x1.88p+7 "
+ format("%1$*2$A", 196, -20).should == "0X1.88P+7 "
+
+ format("%1$*2$c", 97, -10).should == "a "
+ format("%1$*2$p", [], -10).should == "[] "
+ format("%1$*2$s", "abc", -10).should == "abc "
+ end
+
+ it "raises ArgumentError when is mixed with width" do
+ -> () {
+ format("%*10d", 10, 112)
+ }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe "width" do
+ it "specifies the minimum number of characters that will be written to the result" do
+ format("%10b", 10).should == " 1010"
+ format("%10B", 10).should == " 1010"
+ format("%10d", 112).should == " 112"
+ format("%10i", 112).should == " 112"
+ format("%10o", 87).should == " 127"
+ format("%10u", 112).should == " 112"
+ format("%10x", 196).should == " c4"
+ format("%10X", 196).should == " C4"
+
+ format("%20e", 109.52).should == " 1.095200e+02"
+ format("%20E", 109.52).should == " 1.095200E+02"
+ format("%20f", 10.952).should == " 10.952000"
+ format("%20g", 12.1234).should == " 12.1234"
+ format("%20G", 12.1234).should == " 12.1234"
+ format("%20a", 196).should == " 0x1.88p+7"
+ format("%20A", 196).should == " 0X1.88P+7"
+
+ format("%10c", 97).should == " a"
+ format("%10p", []).should == " []"
+ format("%10s", "abc").should == " abc"
+ end
+
+ it "is ignored if argument's actual length is greater" do
+ format("%5d", 1234567890).should == "1234567890"
+ end
+ end
+
+ describe "precision" do
+ context "integer types" do
+ it "controls the number of decimal places displayed" do
+ format("%.6b", 10).should == "001010"
+ format("%.6B", 10).should == "001010"
+ format("%.5d", 112).should == "00112"
+ format("%.5i", 112).should == "00112"
+ format("%.5o", 87).should == "00127"
+ format("%.5u", 112).should == "00112"
+
+ format("%.5x", 196).should == "000c4"
+ format("%.5X", 196).should == "000C4"
+ end
+ end
+
+ context "float types" do
+ it "controls the number of decimal places displayed in fraction part" do
+ format("%.10e", 109.52).should == "1.0952000000e+02"
+ format("%.10E", 109.52).should == "1.0952000000E+02"
+ format("%.10f", 10.952).should == "10.9520000000"
+ format("%.10a", 196).should == "0x1.8800000000p+7"
+ format("%.10A", 196).should == "0X1.8800000000P+7"
+ end
+
+ it "does not affect G format" do
+ format("%.10g", 12.1234).should == "12.1234"
+ format("%.10g", 123456789).should == "123456789"
+ end
+ end
+
+ context "string formats" do
+ it "determines the maximum number of characters to be copied from the string" do
+ format("%.1p", [1]).should == "["
+ format("%.2p", [1]).should == "[1"
+ format("%.10p", [1]).should == "[1]"
+ format("%.0p", [1]).should == ""
+
+ format("%.1s", "abc").should == "a"
+ format("%.2s", "abc").should == "ab"
+ format("%.10s", "abc").should == "abc"
+ format("%.0s", "abc").should == ""
+ end
+ end
+ end
+
+ describe "reference by name" do
+ describe "%<name>s style" do
+ it "uses value passed in a hash argument" do
+ format("%<foo>d", foo: 123).should == "123"
+ end
+
+ it "supports flags, width, precision and type" do
+ format("%+20.10<foo>f", foo: 10.952).should == " +10.9520000000"
+ end
+
+ it "allows to place name in any position" do
+ format("%+15.5<foo>f", foo: 10.952).should == " +10.95200"
+ format("%+15<foo>.5f", foo: 10.952).should == " +10.95200"
+ format("%+<foo>15.5f", foo: 10.952).should == " +10.95200"
+ format("%<foo>+15.5f", foo: 10.952).should == " +10.95200"
+ end
+
+ it "cannot be mixed with unnamed style" do
+ -> () {
+ format("%d %<foo>d", 1, foo: "123")
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "%{name} style" do
+ it "uses value passed in a hash argument" do
+ format("%{foo}", foo: 123).should == "123"
+ end
+
+ it "does not support type style" do
+ format("%{foo}d", foo: 123).should == "123d"
+ end
+
+ it "supports flags, width and precision" do
+ format("%-20.5{foo}", foo: "123456789").should == "12345 "
+ end
+
+ it "cannot be mixed with unnamed style" do
+ -> () {
+ format("%d %{foo}", 1, foo: "123")
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises KeyError when there is no matching key" do
+ -> () {
+ format("%{foo}", {})
+ }.should raise_error(KeyError)
+ end
+
+ it "converts value to String with to_s" do
+ obj = Object.new
+ def obj.to_s; end
+ def obj.to_str; end
+
+ obj.should_receive(:to_s).and_return("42")
+ obj.should_not_receive(:to_str)
+
+ format("%{foo}", foo: obj).should == "42"
+ end
+ end
+ end
+
+ describe "faulty key" do
+ before :all do
+ @base_method = @method
+ end
+
+ it_behaves_like :key_error, -> (obj, key) {
+ @base_method.call("%<#{key}>s", obj)
+ }, { foooo: 1 }
+ end
+end
diff --git a/spec/ruby/core/kernel/shared/sprintf_encoding.rb b/spec/ruby/core/kernel/shared/sprintf_encoding.rb
new file mode 100644
index 0000000000..a92f3c10cd
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/sprintf_encoding.rb
@@ -0,0 +1,28 @@
+describe :kernel_sprintf_encoding, shared: true do
+ def format(*args)
+ @method.call(*args)
+ end
+
+ it "returns a String in the same encoding as the format String if compatible" do
+ string = "%s".force_encoding(Encoding::KOI8_U)
+ result = format(string, "dogs")
+ result.encoding.should equal(Encoding::KOI8_U)
+ end
+
+ it "returns a String in the argument's encoding if format encoding is more restrictive" do
+ string = "foo %s".force_encoding(Encoding::US_ASCII)
+ argument = "b\303\274r".force_encoding(Encoding::UTF_8)
+
+ result = format(string, argument)
+ 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
+ string = "Ä %s".encode('windows-1252')
+ argument = "Ђ".encode('windows-1251')
+
+ -> () {
+ format(string, argument)
+ }.should raise_error(Encoding::CompatibilityError)
+ end
+end
diff --git a/spec/ruby/core/kernel/shared/then.rb b/spec/ruby/core/kernel/shared/then.rb
new file mode 100644
index 0000000000..b52075371f
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/then.rb
@@ -0,0 +1,20 @@
+describe :kernel_then, shared: true do
+ it "yields self" do
+ object = Object.new
+ object.send(@method) { |o| o.should equal object }
+ end
+
+ it "returns the block return value" do
+ object = Object.new
+ object.send(@method) { 42 }.should equal 42
+ end
+
+ it "returns a sized Enumerator when no block given" do
+ object = Object.new
+ enum = object.send(@method)
+ enum.should be_an_instance_of Enumerator
+ enum.size.should equal 1
+ enum.peek.should equal object
+ enum.first.should equal object
+ end
+end
diff --git a/spec/ruby/core/kernel/singleton_class_spec.rb b/spec/ruby/core/kernel/singleton_class_spec.rb
new file mode 100644
index 0000000000..b5e0703905
--- /dev/null
+++ b/spec/ruby/core/kernel/singleton_class_spec.rb
@@ -0,0 +1,27 @@
+describe "Kernel#singleton_class" do
+ it "returns class extended from an object" do
+ x = Object.new
+ xs = class << x; self; end
+ xs.should == x.singleton_class
+ end
+
+ it "returns NilClass for nil" do
+ nil.singleton_class.should == NilClass
+ end
+
+ it "returns TrueClass for true" do
+ true.singleton_class.should == TrueClass
+ end
+
+ it "returns FalseClass for false" do
+ false.singleton_class.should == FalseClass
+ end
+
+ it "raises TypeError for Fixnum" do
+ lambda { 123.singleton_class }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError for Symbol" do
+ lambda { :foo.singleton_class }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/kernel/singleton_method_spec.rb b/spec/ruby/core/kernel/singleton_method_spec.rb
new file mode 100644
index 0000000000..0bdf125ad8
--- /dev/null
+++ b/spec/ruby/core/kernel/singleton_method_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+
+describe "Kernel#singleton_method" do
+ it "find a method defined on the singleton class" do
+ obj = Object.new
+ def obj.foo; end
+ obj.singleton_method(:foo).should be_an_instance_of(Method)
+ end
+
+ it "returns a Method which can be called" do
+ obj = Object.new
+ def obj.foo; 42; end
+ obj.singleton_method(:foo).call.should == 42
+ end
+
+ it "only looks at singleton methods and not at methods in the class" do
+ klass = Class.new do
+ def foo
+ 42
+ end
+ end
+ obj = klass.new
+ obj.foo.should == 42
+ -> {
+ obj.singleton_method(:foo)
+ }.should raise_error(NameError) { |e|
+ # a NameError and not a NoMethodError
+ e.class.should == NameError
+ }
+ end
+
+ it "raises a NameError if there is no such method" do
+ obj = Object.new
+ -> {
+ obj.singleton_method(:not_existing)
+ }.should raise_error(NameError) { |e|
+ # a NameError and not a NoMethodError
+ e.class.should == NameError
+ }
+ end
+end
diff --git a/spec/ruby/core/kernel/singleton_methods_spec.rb b/spec/ruby/core/kernel/singleton_methods_spec.rb
new file mode 100644
index 0000000000..eb4cede110
--- /dev/null
+++ b/spec/ruby/core/kernel/singleton_methods_spec.rb
@@ -0,0 +1,192 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/reflection'
+require_relative 'fixtures/classes'
+
+describe :kernel_singleton_methods, shared: true do
+ it "returns an empty Array for an object with no singleton methods" do
+ ReflectSpecs.o.singleton_methods(*@object).should == []
+ end
+
+ it "returns the names of module methods for a module" do
+ ReflectSpecs::M.singleton_methods(*@object).should include(:ms_pro, :ms_pub)
+ end
+
+ it "does not return private module methods for a module" do
+ ReflectSpecs::M.singleton_methods(*@object).should_not include(:ms_pri)
+ end
+
+ it "returns the names of class methods for a class" do
+ ReflectSpecs::A.singleton_methods(*@object).should include(:as_pro, :as_pub)
+ end
+
+ it "does not return private class methods for a class" do
+ ReflectSpecs::A.singleton_methods(*@object).should_not include(:as_pri)
+ end
+
+ it "returns the names of singleton methods for an object" do
+ ReflectSpecs.os.singleton_methods(*@object).should include(:os_pro, :os_pub)
+ end
+end
+
+describe :kernel_singleton_methods_modules, shared: true do
+ it "does not return any included methods for a module including a module" do
+ ReflectSpecs::N.singleton_methods(*@object).should include(:ns_pro, :ns_pub)
+ end
+
+ it "does not return any included methods for a class including a module" do
+ ReflectSpecs::D.singleton_methods(*@object).should include(:ds_pro, :ds_pub)
+ end
+
+ it "for a module does not return methods in a module prepended to Module itself" do
+ require_relative 'fixtures/singleton_methods'
+ mod = SingletonMethodsSpecs::SelfExtending
+ mod.method(:mspec_test_kernel_singleton_methods).owner.should == SingletonMethodsSpecs::Prepended
+
+ ancestors = mod.singleton_class.ancestors
+ ancestors[0...2].should == [ mod.singleton_class, mod ]
+ ancestors.should include(SingletonMethodsSpecs::Prepended)
+
+ # Do not search prepended modules of `Module`, as that's a non-singleton class
+ mod.singleton_methods.should == []
+ end
+end
+
+describe :kernel_singleton_methods_supers, shared: true do
+ it "returns the names of singleton methods for an object extented with a module" do
+ ReflectSpecs.oe.singleton_methods(*@object).should include(:m_pro, :m_pub)
+ end
+
+ it "returns a unique list for an object extended with a module" do
+ m = ReflectSpecs.oed.singleton_methods(*@object)
+ r = m.select { |x| x == :pub or x == :pro }.sort
+ r.should == [:pro, :pub]
+ end
+
+ it "returns the names of singleton methods for an object extented with two modules" do
+ ReflectSpecs.oee.singleton_methods(*@object).should include(:m_pro, :m_pub, :n_pro, :n_pub)
+ end
+
+ it "returns the names of singleton methods for an object extented with a module including a module" do
+ ReflectSpecs.oei.singleton_methods(*@object).should include(:n_pro, :n_pub, :m_pro, :m_pub)
+ end
+
+ it "returns the names of inherited singleton methods for a subclass" do
+ ReflectSpecs::B.singleton_methods(*@object).should include(:as_pro, :as_pub, :bs_pro, :bs_pub)
+ end
+
+ it "returns a unique list for a subclass" do
+ m = ReflectSpecs::B.singleton_methods(*@object)
+ r = m.select { |x| x == :pub or x == :pro }.sort
+ r.should == [:pro, :pub]
+ end
+
+ it "returns the names of inherited singleton methods for a subclass including a module" do
+ ReflectSpecs::C.singleton_methods(*@object).should include(:as_pro, :as_pub, :cs_pro, :cs_pub)
+ end
+
+ it "returns a unique list for a subclass including a module" do
+ m = ReflectSpecs::C.singleton_methods(*@object)
+ r = m.select { |x| x == :pub or x == :pro }.sort
+ r.should == [:pro, :pub]
+ end
+
+ it "returns the names of inherited singleton methods for a subclass of a class including a module" do
+ ReflectSpecs::E.singleton_methods(*@object).should include(:ds_pro, :ds_pub, :es_pro, :es_pub)
+ end
+
+ it "returns the names of inherited singleton methods for a subclass of a class that includes a module, where the subclass also includes a module" do
+ ReflectSpecs::F.singleton_methods(*@object).should include(:ds_pro, :ds_pub, :fs_pro, :fs_pub)
+ end
+
+ it "returns the names of inherited singleton methods for a class extended with a module" do
+ ReflectSpecs::P.singleton_methods(*@object).should include(:m_pro, :m_pub)
+ end
+end
+
+describe :kernel_singleton_methods_private_supers, shared: true do
+ it "does not return private singleton methods for an object extended with a module" do
+ ReflectSpecs.oe.singleton_methods(*@object).should_not include(:m_pri)
+ end
+
+ it "does not return private singleton methods for an object extended with two modules" do
+ ReflectSpecs.oee.singleton_methods(*@object).should_not include(:m_pri)
+ end
+
+ it "does not return private singleton methods for an object extented with a module including a module" do
+ ReflectSpecs.oei.singleton_methods(*@object).should_not include(:n_pri, :m_pri)
+ end
+
+ it "does not return private singleton methods for a class extended with a module" do
+ ReflectSpecs::P.singleton_methods(*@object).should_not include(:m_pri)
+ end
+
+ it "does not return private inherited singleton methods for a module including a module" do
+ ReflectSpecs::N.singleton_methods(*@object).should_not include(:ns_pri)
+ end
+
+ it "does not return private inherited singleton methods for a class including a module" do
+ ReflectSpecs::D.singleton_methods(*@object).should_not include(:ds_pri)
+ end
+
+ it "does not return private inherited singleton methods for a subclass" do
+ ReflectSpecs::B.singleton_methods(*@object).should_not include(:as_pri, :bs_pri)
+ end
+
+ it "does not return private inherited singleton methods for a subclass including a module" do
+ ReflectSpecs::C.singleton_methods(*@object).should_not include(:as_pri, :cs_pri)
+ end
+
+ it "does not return private inherited singleton methods for a subclass of a class including a module" do
+ ReflectSpecs::E.singleton_methods(*@object).should_not include(:ds_pri, :es_pri)
+ end
+
+ it "does not return private inherited singleton methods for a subclass of a class that includes a module, where the subclass also includes a module" do
+ ReflectSpecs::F.singleton_methods(*@object).should_not include(:ds_pri, :fs_pri)
+ end
+end
+
+describe "Kernel#singleton_methods" do
+ describe "when not passed an argument" do
+ it_behaves_like :kernel_singleton_methods, nil, []
+ it_behaves_like :kernel_singleton_methods_supers, nil, []
+ it_behaves_like :kernel_singleton_methods_modules, nil, []
+ it_behaves_like :kernel_singleton_methods_private_supers, nil, []
+ end
+
+ describe "when passed true" do
+ it_behaves_like :kernel_singleton_methods, nil, true
+ it_behaves_like :kernel_singleton_methods_supers, nil, true
+ it_behaves_like :kernel_singleton_methods_modules, nil, true
+ it_behaves_like :kernel_singleton_methods_private_supers, nil, true
+ end
+
+ describe "when passed false" do
+ it_behaves_like :kernel_singleton_methods, nil, false
+ it_behaves_like :kernel_singleton_methods_modules, nil, false
+ it_behaves_like :kernel_singleton_methods_private_supers, nil, false
+
+ it "returns an empty Array for an object extented with a module" do
+ ReflectSpecs.oe.singleton_methods(false).should == []
+ end
+
+ it "returns an empty Array for an object extented with two modules" do
+ ReflectSpecs.oee.singleton_methods(false).should == []
+ end
+
+ it "returns an empty Array for an object extended with a module including a module" do
+ ReflectSpecs.oei.singleton_methods(false).should == []
+ end
+
+ it "returns the names of singleton methods of the subclass" do
+ ReflectSpecs::B.singleton_methods(false).should include(:bs_pro, :bs_pub)
+ end
+
+ it "does not return names of inherited singleton methods for a subclass" do
+ ReflectSpecs::B.singleton_methods(false).should_not include(:as_pro, :as_pub)
+ end
+
+ it "does not return the names of inherited singleton methods for a class extended with a module" do
+ ReflectSpecs::P.singleton_methods(false).should_not include(:m_pro, :m_pub)
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/sleep_spec.rb b/spec/ruby/core/kernel/sleep_spec.rb
new file mode 100644
index 0000000000..489f4f8410
--- /dev/null
+++ b/spec/ruby/core/kernel/sleep_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#sleep" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:sleep)
+ end
+
+ it "accepts a Float" do
+ sleep(0.1).should be_close(0, 2)
+ end
+
+ it "accepts a Fixnum" do
+ sleep(0).should be_close(0, 2)
+ end
+
+ it "accepts a Rational" do
+ sleep(Rational(1, 9)).should be_close(0, 2)
+ end
+
+ it "raises an ArgumentError when passed a negative duration" do
+ lambda { sleep(-0.1) }.should raise_error(ArgumentError)
+ lambda { sleep(-1) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { sleep(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a String" do
+ lambda { sleep('2') }.should raise_error(TypeError)
+ end
+
+ it "pauses execution indefinitely if not given a duration" do
+ running = false
+ t = Thread.new do
+ running = true
+ sleep
+ 5
+ end
+
+ Thread.pass until running
+ Thread.pass while t.status and t.status != "sleep"
+
+ t.wakeup
+ t.value.should == 5
+ end
+end
+
+describe "Kernel.sleep" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/spawn_spec.rb b/spec/ruby/core/kernel/spawn_spec.rb
new file mode 100644
index 0000000000..da3cf39021
--- /dev/null
+++ b/spec/ruby/core/kernel/spawn_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# These specs only run a basic usage of #spawn.
+# Process.spawn has more complete specs and they are not
+# run here as it is redundant and takes too long for little gain.
+describe "Kernel#spawn" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:spawn)
+ end
+
+ it "executes the given command" do
+ lambda {
+ Process.wait spawn("echo spawn")
+ }.should output_to_fd("spawn\n")
+ end
+end
+
+describe "Kernel.spawn" do
+ it "executes the given command" do
+ lambda {
+ Process.wait Kernel.spawn("echo spawn")
+ }.should output_to_fd("spawn\n")
+ end
+end
diff --git a/spec/ruby/core/kernel/sprintf_spec.rb b/spec/ruby/core/kernel/sprintf_spec.rb
new file mode 100644
index 0000000000..4aa8c2351f
--- /dev/null
+++ b/spec/ruby/core/kernel/sprintf_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/sprintf'
+require_relative 'shared/sprintf_encoding'
+
+describe "Kernel#sprintf" do
+ it_behaves_like :kernel_sprintf, -> (format, *args) {
+ sprintf(format, *args)
+ }
+
+ it_behaves_like :kernel_sprintf_encoding, -> (format, *args) {
+ sprintf(format, *args)
+ }
+end
+
+describe "Kernel.sprintf" do
+ it_behaves_like :kernel_sprintf, -> (format, *args) {
+ Kernel.sprintf(format, *args)
+ }
+
+ it_behaves_like :kernel_sprintf_encoding, -> (format, *args) {
+ Kernel.sprintf(format, *args)
+ }
+end
diff --git a/spec/ruby/core/kernel/srand_spec.rb b/spec/ruby/core/kernel/srand_spec.rb
new file mode 100644
index 0000000000..053fb521ef
--- /dev/null
+++ b/spec/ruby/core/kernel/srand_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel.srand" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:srand)
+ end
+
+ it "returns the previous seed value" do
+ srand(10)
+ srand(20).should == 10
+ end
+
+ it "seeds the RNG correctly and repeatably" do
+ srand(10)
+ x = rand
+ srand(10)
+ rand.should == x
+ end
+
+ it "defaults number to a random value" do
+ lambda { srand }.should_not raise_error
+ srand.should_not == 0
+ end
+
+ it "accepts and uses a seed of 0" do
+ srand(0)
+ srand.should == 0
+ end
+
+ it "accepts a negative seed" do
+ srand(-17)
+ srand.should == -17
+ end
+
+ it "accepts a Bignum as a seed" do
+ srand(0x12345678901234567890)
+ srand.should == 0x12345678901234567890
+ end
+
+ it "calls #to_int on seed" do
+ srand(3.8)
+ srand.should == 3
+
+ s = mock('seed')
+ s.should_receive(:to_int).and_return 0
+ srand(s)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { srand(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a String" do
+ lambda { srand("7") }.should raise_error(TypeError)
+ end
+end
+
+describe "Kernel#srand" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/sub_spec.rb b/spec/ruby/core/kernel/sub_spec.rb
new file mode 100644
index 0000000000..9130bd159c
--- /dev/null
+++ b/spec/ruby/core/kernel/sub_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# FIXME: These methods exist only when the -n or -p option is passed to
+# ruby, but we currently don't have a way of specifying that.
+ruby_version_is ""..."1.9" do
+ describe "Kernel#sub" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:sub)
+ end
+ end
+
+ describe "Kernel#sub!" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:sub!)
+ end
+ end
+
+ describe "Kernel.sub" do
+ it "needs to be reviewed for spec completeness"
+ end
+
+ describe "Kernel.sub!" do
+ it "needs to be reviewed for spec completeness"
+ end
+end
diff --git a/spec/ruby/core/kernel/syscall_spec.rb b/spec/ruby/core/kernel/syscall_spec.rb
new file mode 100644
index 0000000000..32d07b3ae2
--- /dev/null
+++ b/spec/ruby/core/kernel/syscall_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#syscall" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:syscall)
+ end
+end
+
+describe "Kernel.syscall" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/system_spec.rb b/spec/ruby/core/kernel/system_spec.rb
new file mode 100644
index 0000000000..6a4ba6283d
--- /dev/null
+++ b/spec/ruby/core/kernel/system_spec.rb
@@ -0,0 +1,117 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe :kernel_system, shared: true do
+ it "executes the specified command in a subprocess" do
+ lambda { @object.system("echo a") }.should output_to_fd("a\n")
+
+ $?.should be_an_instance_of Process::Status
+ $?.success?.should == true
+ end
+
+ it "returns true when the command exits with a zero exit status" do
+ @object.system(ruby_cmd('exit 0')).should == true
+
+ $?.should be_an_instance_of Process::Status
+ $?.success?.should == true
+ $?.exitstatus.should == 0
+ end
+
+ it "returns false when the command exits with a non-zero exit status" do
+ @object.system(ruby_cmd('exit 1')).should == false
+
+ $?.should be_an_instance_of Process::Status
+ $?.success?.should == false
+ $?.exitstatus.should == 1
+ end
+
+ ruby_version_is "2.6" do
+ it "raises RuntimeError when `exception: true` is given and the command exits with a non-zero exit status" do
+ lambda { @object.system(ruby_cmd('exit 1'), exception: true) }.should raise_error(RuntimeError)
+ end
+
+ it "raises Errno::ENOENT when `exception: true` is given and the specified command does not exist" do
+ lambda { @object.system('feature_14386', exception: true) }.should raise_error(Errno::ENOENT)
+ end
+ end
+
+ it "returns nil when command execution fails" do
+ @object.system("sad").should be_nil
+
+ $?.should be_an_instance_of Process::Status
+ $?.pid.should be_kind_of(Integer)
+ $?.exitstatus.should == 127
+ end
+
+ it "does not write to stderr when command execution fails" do
+ lambda { @object.system("sad") }.should output_to_fd("", STDERR)
+ end
+
+ platform_is_not :windows do
+ before :each do
+ @shell = ENV['SHELL']
+ end
+
+ after :each do
+ ENV['SHELL'] = @shell
+ end
+
+ it "executes with `sh` if the command contains shell characters" do
+ lambda { @object.system("echo $0") }.should output_to_fd("sh\n")
+ end
+
+ it "ignores SHELL env var and always uses `sh`" do
+ ENV['SHELL'] = "/bin/fakeshell"
+ lambda { @object.system("echo $0") }.should output_to_fd("sh\n")
+ end
+ end
+
+ before :each do
+ ENV['TEST_SH_EXPANSION'] = 'foo'
+ @shell_var = '$TEST_SH_EXPANSION'
+ platform_is :windows do
+ @shell_var = '%TEST_SH_EXPANSION%'
+ end
+ end
+
+ after :each do
+ ENV.delete('TEST_SH_EXPANSION')
+ end
+
+ it "expands shell variables when given a single string argument" do
+ lambda { @object.system("echo #{@shell_var}") }.should output_to_fd("foo\n")
+ end
+
+ platform_is_not :windows do
+ it "does not expand shell variables when given multiples arguments" do
+ lambda { @object.system("echo", @shell_var) }.should output_to_fd("#{@shell_var}\n")
+ end
+ end
+
+ platform_is :windows do
+ it "does expand shell variables when given multiples arguments" do
+ # See https://bugs.ruby-lang.org/issues/12231
+ lambda { @object.system("echo", @shell_var) }.should output_to_fd("foo\n")
+ end
+ end
+
+ platform_is :windows do
+ it "runs commands starting with any number of @ using shell" do
+ `#{ruby_cmd("p system 'does_not_exist'")} 2>NUL`.chomp.should == "nil"
+ @object.system('@does_not_exist 2>NUL').should == false
+ @object.system("@@@#{ruby_cmd('exit 0')}").should == true
+ end
+ end
+end
+
+describe "Kernel#system" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:system)
+ end
+
+ it_behaves_like :kernel_system, :system, KernelSpecs::Method.new
+end
+
+describe "Kernel.system" do
+ it_behaves_like :kernel_system, :system, Kernel
+end
diff --git a/spec/ruby/core/kernel/taint_spec.rb b/spec/ruby/core/kernel/taint_spec.rb
new file mode 100644
index 0000000000..ff1b20f9ca
--- /dev/null
+++ b/spec/ruby/core/kernel/taint_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#taint" do
+ it "returns self" do
+ o = Object.new
+ o.taint.should equal(o)
+ end
+
+ it "sets the tainted bit" do
+ o = Object.new
+ o.taint
+ o.tainted?.should == true
+ end
+
+ it "raises #{frozen_error_class} on an untainted, frozen object" do
+ o = Object.new.freeze
+ lambda { o.taint }.should raise_error(frozen_error_class)
+ end
+
+ it "does not raise an error on a tainted, frozen object" do
+ o = Object.new.taint.freeze
+ o.taint.should equal(o)
+ end
+
+ it "has no effect on immediate values" do
+ [nil, true, false].each do |v|
+ v.taint
+ v.tainted?.should == false
+ end
+ end
+
+ it "no raises a RuntimeError on symbols" do
+ v = :sym
+ lambda { v.taint }.should_not raise_error(RuntimeError)
+ v.tainted?.should == false
+ end
+
+ it "no raises error on fixnum values" do
+ [1].each do |v|
+ lambda { v.taint }.should_not raise_error(RuntimeError)
+ v.tainted?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/tainted_spec.rb b/spec/ruby/core/kernel/tainted_spec.rb
new file mode 100644
index 0000000000..c024756110
--- /dev/null
+++ b/spec/ruby/core/kernel/tainted_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#tainted?" do
+ it "returns true if Object is tainted" do
+ o = mock('o')
+ p = mock('p')
+ p.taint
+ o.tainted?.should == false
+ p.tainted?.should == true
+ end
+end
diff --git a/spec/ruby/core/kernel/tap_spec.rb b/spec/ruby/core/kernel/tap_spec.rb
new file mode 100644
index 0000000000..a7d548847c
--- /dev/null
+++ b/spec/ruby/core/kernel/tap_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#tap" do
+ it "always yields self and returns self" do
+ a = KernelSpecs::A.new
+ a.tap{|o| o.should equal(a); 42}.should equal(a)
+ end
+
+ it "raises a LocalJumpError when no block given" do
+ lambda { 3.tap }.should raise_error(LocalJumpError)
+ end
+end
diff --git a/spec/ruby/core/kernel/test_spec.rb b/spec/ruby/core/kernel/test_spec.rb
new file mode 100644
index 0000000000..abb365aed2
--- /dev/null
+++ b/spec/ruby/core/kernel/test_spec.rb
@@ -0,0 +1,109 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#test" do
+ before :all do
+ @file = File.dirname(__FILE__) + '/fixtures/classes.rb'
+ @dir = File.dirname(__FILE__) + '/fixtures'
+ end
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:test)
+ end
+
+ it "returns true when passed ?f if the argument is a regular file" do
+ Kernel.test(?f, @file).should == true
+ end
+
+ it "returns true when passed ?e if the argument is a file" do
+ Kernel.test(?e, @file).should == true
+ end
+
+ it "returns true when passed ?d if the argument is a directory" do
+ Kernel.test(?d, @dir).should == true
+ end
+
+ platform_is_not :windows do
+ it "returns true when passed ?l if the argument is a symlink" do
+ link = tmp("file_symlink.lnk")
+ File.symlink(@file, link)
+ begin
+ Kernel.test(?l, link).should be_true
+ ensure
+ rm_r link
+ end
+ end
+ end
+
+ it "returns true when passed ?r if the argument is readable by the effective uid" do
+ Kernel.test(?r, @file).should be_true
+ end
+
+ it "returns true when passed ?R if the argument is readable by the real uid" do
+ Kernel.test(?R, @file).should be_true
+ end
+
+ context "writable test" do
+ before do
+ @tmp_file = tmp("file.kernel.test")
+ touch(@tmp_file)
+ end
+
+ after do
+ rm_r @tmp_file
+ end
+
+ it "returns true when passed ?w if the argument is readable by the effective uid" do
+ Kernel.test(?w, @tmp_file).should be_true
+ end
+
+ it "returns true when passed ?W if the argument is readable by the real uid" do
+ Kernel.test(?W, @tmp_file).should be_true
+ end
+ end
+
+ context "time commands" do
+ before :each do
+ @tmp_file = File.new(tmp("file.kernel.test"), "w")
+ end
+
+ after :each do
+ @tmp_file.close
+ rm_r @tmp_file
+ end
+
+ it "returns the last access time for the provided file when passed ?A" do
+ Kernel.test(?A, @tmp_file).should == @tmp_file.atime
+ end
+
+ it "returns the time at which the file was created when passed ?C" do
+ Kernel.test(?C, @tmp_file).should == @tmp_file.ctime
+ end
+
+ it "returns the time at which the file was modified when passed ?M" do
+ Kernel.test(?M, @tmp_file).should == @tmp_file.mtime
+ end
+ end
+
+ it "calls #to_path on second argument when passed ?f and a filename" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return @file
+ Kernel.test(?f, p)
+ end
+
+ it "calls #to_path on second argument when passed ?e and a filename" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return @file
+ Kernel.test(?e, p)
+ end
+
+ it "calls #to_path on second argument when passed ?d and a directory" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return @dir
+ Kernel.test(?d, p)
+ end
+end
+
+describe "Kernel.test" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/then_spec.rb b/spec/ruby/core/kernel/then_spec.rb
new file mode 100644
index 0000000000..fa896b52b1
--- /dev/null
+++ b/spec/ruby/core/kernel/then_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'shared/then'
+
+ruby_version_is "2.6" do
+ describe "Kernel#then" do
+ it_behaves_like :kernel_then, :then
+ end
+end
diff --git a/spec/ruby/core/kernel/throw_spec.rb b/spec/ruby/core/kernel/throw_spec.rb
new file mode 100644
index 0000000000..8f8486accc
--- /dev/null
+++ b/spec/ruby/core/kernel/throw_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel.throw" do
+ it "transfers control to the end of the active catch block waiting for symbol" do
+ catch(:blah) do
+ :value
+ throw :blah
+ fail("throw didn't transfer the control")
+ end.should be_nil
+ end
+
+ it "transfers control to the innermost catch block waiting for the same sympol" do
+ one = two = three = 0
+ catch :duplicate do
+ catch :duplicate do
+ catch :duplicate do
+ one = 1
+ throw :duplicate
+ end
+ two = 2
+ throw :duplicate
+ end
+ three = 3
+ throw :duplicate
+ end
+ [one, two, three].should == [1, 2, 3]
+ end
+
+ it "sets the return value of the catch block to nil by default" do
+ res = catch :blah do
+ throw :blah
+ end
+ res.should == nil
+ end
+
+ it "sets the return value of the catch block to a value specified as second parameter" do
+ res = catch :blah do
+ throw :blah, :return_value
+ end
+ res.should == :return_value
+ end
+
+ it "raises an ArgumentError if there is no catch block for the symbol" do
+ lambda { throw :blah }.should raise_error(ArgumentError)
+ end
+
+ it "raises an UncaughtThrowError if there is no catch block for the symbol" do
+ lambda { throw :blah }.should raise_error(UncaughtThrowError)
+ end
+
+ it "raises ArgumentError if 3 or more arguments provided" do
+ lambda {
+ catch :blah do
+ throw :blah, :return_value, 2
+ end
+ }.should raise_error(ArgumentError)
+
+ lambda {
+ catch :blah do
+ throw :blah, :return_value, 2, 3, 4, 5
+ end
+ }.should raise_error(ArgumentError)
+ end
+
+ it "can throw an object" do
+ lambda {
+ obj = Object.new
+ catch obj do
+ throw obj
+ end
+ }.should_not raise_error(NameError)
+ end
+end
+
+describe "Kernel#throw" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:throw)
+ end
+end
diff --git a/spec/ruby/core/kernel/to_enum_spec.rb b/spec/ruby/core/kernel/to_enum_spec.rb
new file mode 100644
index 0000000000..9d9945450f
--- /dev/null
+++ b/spec/ruby/core/kernel/to_enum_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Kernel#to_enum" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/to_s_spec.rb b/spec/ruby/core/kernel/to_s_spec.rb
new file mode 100644
index 0000000000..4b59520ce7
--- /dev/null
+++ b/spec/ruby/core/kernel/to_s_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#to_s" do
+ it "returns a String containing the name of self's class" do
+ Object.new.to_s.should =~ /Object/
+ end
+
+ it "returns a tainted result if self is tainted" do
+ Object.new.taint.to_s.tainted?.should be_true
+ end
+
+ it "returns an untrusted result if self is untrusted" do
+ Object.new.untrust.to_s.untrusted?.should be_true
+ end
+end
diff --git a/spec/ruby/core/kernel/trace_var_spec.rb b/spec/ruby/core/kernel/trace_var_spec.rb
new file mode 100644
index 0000000000..019902a230
--- /dev/null
+++ b/spec/ruby/core/kernel/trace_var_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#trace_var" do
+ before :each do
+ $Kernel_trace_var_global = nil
+ end
+
+ after :each do
+ untrace_var :$Kernel_trace_var_global
+
+ $Kernel_trace_var_global = nil
+ $Kernel_trace_var_extra = nil
+ end
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:trace_var)
+ end
+
+ it "hooks assignments to a global variable" do
+ captured = nil
+
+ trace_var :$Kernel_trace_var_global do |value|
+ captured = value
+ end
+
+ $Kernel_trace_var_global = 'foo'
+ captured.should == 'foo'
+ end
+
+ it "accepts a proc argument instead of a block" do
+ captured = nil
+
+ trace_var :$Kernel_trace_var_global, proc {|value| captured = value}
+
+ $Kernel_trace_var_global = 'foo'
+ captured.should == 'foo'
+ end
+
+ # String arguments should be evaluated in the context of the caller.
+ it "accepts a String argument instead of a Proc or block" do
+ trace_var :$Kernel_trace_var_global, '$Kernel_trace_var_extra = true'
+
+ $Kernel_trace_var_global = 'foo'
+
+ $Kernel_trace_var_extra.should == true
+ end
+
+ it "raises ArgumentError if no block or proc is provided" do
+ lambda do
+ trace_var :$Kernel_trace_var_global
+ end.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/kernel/trap_spec.rb b/spec/ruby/core/kernel/trap_spec.rb
new file mode 100644
index 0000000000..465aacb0fb
--- /dev/null
+++ b/spec/ruby/core/kernel/trap_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#trap" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:trap)
+ end
+end
+
+describe "Kernel.trap" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/trust_spec.rb b/spec/ruby/core/kernel/trust_spec.rb
new file mode 100644
index 0000000000..c258c4fce7
--- /dev/null
+++ b/spec/ruby/core/kernel/trust_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#trust" do
+ it "returns self" do
+ o = Object.new
+ o.trust.should equal(o)
+ end
+
+ it "clears the untrusted bit" do
+ o = Object.new.untrust
+ o.trust
+ o.untrusted?.should == false
+ end
+
+ it "raises #{frozen_error_class} on an untrusted, frozen object" do
+ o = Object.new.untrust.freeze
+ lambda { o.trust }.should raise_error(frozen_error_class)
+ end
+
+ it "does not raise an error on a trusted, frozen object" do
+ o = Object.new.freeze
+ o.trust.should equal(o)
+ end
+end
diff --git a/spec/ruby/core/kernel/untaint_spec.rb b/spec/ruby/core/kernel/untaint_spec.rb
new file mode 100644
index 0000000000..73f7942124
--- /dev/null
+++ b/spec/ruby/core/kernel/untaint_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#untaint" do
+ it "returns self" do
+ o = Object.new
+ o.untaint.should equal(o)
+ end
+
+ it "clears the tainted bit" do
+ o = Object.new.taint
+ o.untaint
+ o.tainted?.should == false
+ end
+
+ it "raises #{frozen_error_class} on a tainted, frozen object" do
+ o = Object.new.taint.freeze
+ lambda { o.untaint }.should raise_error(frozen_error_class)
+ end
+
+ it "does not raise an error on an untainted, frozen object" do
+ o = Object.new.freeze
+ o.untaint.should equal(o)
+ end
+end
diff --git a/spec/ruby/core/kernel/untrace_var_spec.rb b/spec/ruby/core/kernel/untrace_var_spec.rb
new file mode 100644
index 0000000000..1925a3a836
--- /dev/null
+++ b/spec/ruby/core/kernel/untrace_var_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#untrace_var" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:untrace_var)
+ end
+end
+
+describe "Kernel.untrace_var" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/untrust_spec.rb b/spec/ruby/core/kernel/untrust_spec.rb
new file mode 100644
index 0000000000..f70e4356ff
--- /dev/null
+++ b/spec/ruby/core/kernel/untrust_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#untrust" do
+ it "returns self" do
+ o = Object.new
+ o.untrust.should equal(o)
+ end
+
+ it "sets the untrusted bit" do
+ o = Object.new
+ o.untrust
+ o.untrusted?.should == true
+ end
+
+ it "raises #{frozen_error_class} on a trusted, frozen object" do
+ o = Object.new.freeze
+ lambda { o.untrust }.should raise_error(frozen_error_class)
+ end
+
+ it "does not raise an error on an untrusted, frozen object" do
+ o = Object.new.untrust.freeze
+ o.untrust.should equal(o)
+ end
+end
diff --git a/spec/ruby/core/kernel/untrusted_spec.rb b/spec/ruby/core/kernel/untrusted_spec.rb
new file mode 100644
index 0000000000..55638a5dd3
--- /dev/null
+++ b/spec/ruby/core/kernel/untrusted_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#untrusted?" do
+ it "returns the untrusted status of an object" do
+ o = mock('o')
+ o.untrusted?.should == false
+ o.untrust
+ o.untrusted?.should == true
+ end
+
+ it "has no effect on immediate values" do
+ a = nil
+ b = true
+ c = false
+ a.untrust
+ b.untrust
+ c.untrust
+ a.untrusted?.should == false
+ b.untrusted?.should == false
+ c.untrusted?.should == false
+ end
+
+ it "has effect on immediate values" do
+ d = 1
+ lambda { d.untrust }.should_not raise_error(RuntimeError)
+ end
+end
diff --git a/spec/ruby/core/kernel/warn_spec.rb b/spec/ruby/core/kernel/warn_spec.rb
new file mode 100644
index 0000000000..7a0c431ff0
--- /dev/null
+++ b/spec/ruby/core/kernel/warn_spec.rb
@@ -0,0 +1,135 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#warn" do
+ before :each do
+ @before_verbose = $VERBOSE
+ @before_separator = $/
+ end
+
+ after :each do
+ $VERBOSE = @before_verbose
+ $/ = @before_separator
+ end
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:warn)
+ end
+
+ it "requires multiple arguments" do
+ Kernel.method(:warn).arity.should < 0
+ end
+
+ it "does not append line-end if last character is line-end" do
+ lambda {
+ $VERBOSE = true
+ warn("this is some simple text with line-end\n")
+ }.should output(nil, "this is some simple text with line-end\n")
+ end
+
+ it "calls #write on $stderr if $VERBOSE is true" do
+ lambda {
+ $VERBOSE = true
+ warn("this is some simple text")
+ }.should output(nil, "this is some simple text\n")
+ end
+
+ it "calls #write on $stderr if $VERBOSE is false" do
+ lambda {
+ $VERBOSE = false
+ warn("this is some simple text")
+ }.should output(nil, "this is some simple text\n")
+ end
+
+ it "does not call #write on $stderr if $VERBOSE is nil" do
+ lambda {
+ $VERBOSE = nil
+ warn("this is some simple text")
+ }.should output(nil, "")
+ end
+
+ it "writes each argument on a line when passed multiple arguments" do
+ lambda {
+ $VERBOSE = true
+ warn("line 1", "line 2")
+ }.should output(nil, "line 1\nline 2\n")
+ end
+
+ it "writes each array element on a line when passes an array" do
+ lambda {
+ $VERBOSE = true
+ warn(["line 1", "line 2"])
+ }.should output(nil, "line 1\nline 2\n")
+ end
+
+ it "does not write strings when passed no arguments" do
+ lambda {
+ $VERBOSE = true
+ warn
+ }.should output("", "")
+ end
+
+ it "writes the default record separator and NOT $/ to $stderr after the warning message" do
+ lambda {
+ $VERBOSE = true
+ $/ = 'rs'
+ warn("")
+ }.should output(nil, /\n/)
+ end
+
+ ruby_version_is "2.5" do
+ describe ":uplevel keyword argument" do
+ before :each do
+ $VERBOSE = true
+ end
+
+ it "prepends a message with specified line from the backtrace" do
+ w = KernelSpecs::WarnInNestedCall.new
+
+ -> { w.f4("foo", 0) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.warn_call_lineno}: warning: foo|)
+ -> { w.f4("foo", 1) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f1_call_lineno}: warning: foo|)
+ -> { w.f4("foo", 2) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f2_call_lineno}: warning: foo|)
+ -> { w.f4("foo", 3) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f3_call_lineno}: warning: foo|)
+ end
+
+ ruby_bug "#14846", "2.5"..."2.6" do
+ it "does not prepend caller information if line number is too big" do
+ w = KernelSpecs::WarnInNestedCall.new
+ -> { w.f4("foo", 100) }.should output(nil, "warning: foo\n")
+ end
+ end
+
+ it "prepends even if a message is empty or nil" do
+ w = KernelSpecs::WarnInNestedCall.new
+
+ -> { w.f4("", 0) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.warn_call_lineno}: warning: \n$|)
+ -> { w.f4(nil, 0) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.warn_call_lineno}: warning: \n$|)
+ end
+
+ it "converts value to Integer" do
+ w = KernelSpecs::WarnInNestedCall.new
+
+ -> { w.f4(0.1) }.should output(nil, %r|classes.rb:#{w.warn_call_lineno}:|)
+ -> { w.f4(Rational(1, 2)) }.should output(nil, %r|classes.rb:#{w.warn_call_lineno}:|)
+ end
+
+ it "raises ArgumentError if passed negative value" do
+ -> { warn "", uplevel: -2 }.should raise_error(ArgumentError)
+ -> { warn "", uplevel: -100 }.should raise_error(ArgumentError)
+ end
+
+ ruby_bug "#14846", "2.5"..."2.6" do
+ it "raises ArgumentError if passed -1" do
+ -> { warn "", uplevel: -1 }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "raises TypeError if passed not Integer" do
+ -> { warn "", uplevel: "" }.should raise_error(TypeError)
+ -> { warn "", uplevel: [] }.should raise_error(TypeError)
+ -> { warn "", uplevel: {} }.should raise_error(TypeError)
+ -> { warn "", uplevel: Object.new }.should raise_error(TypeError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/yield_self_spec.rb b/spec/ruby/core/kernel/yield_self_spec.rb
new file mode 100644
index 0000000000..affedae144
--- /dev/null
+++ b/spec/ruby/core/kernel/yield_self_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'shared/then'
+
+ruby_version_is "2.5" do
+ describe "Kernel#yield_self" do
+ it_behaves_like :kernel_then, :yield_self
+ end
+end
diff --git a/spec/ruby/core/main/define_method_spec.rb b/spec/ruby/core/main/define_method_spec.rb
new file mode 100644
index 0000000000..d85c5e8119
--- /dev/null
+++ b/spec/ruby/core/main/define_method_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+script_binding = binding
+
+describe "main#define_method" do
+ before :each do
+ @code = 'define_method(:boom) { :bam }'
+ end
+
+ after :each do
+ Object.send :remove_method, :boom
+ end
+
+ it 'creates a public method in TOPLEVEL_BINDING' do
+ eval @code, TOPLEVEL_BINDING
+ Object.should have_method :boom
+ end
+
+ it 'creates a public method in script binding' do
+ eval @code, script_binding
+ Object.should have_method :boom
+ end
+
+ it 'returns the method name as symbol' do
+ eval(@code, TOPLEVEL_BINDING).should equal :boom
+ end
+end
diff --git a/spec/ruby/core/main/fixtures/classes.rb b/spec/ruby/core/main/fixtures/classes.rb
new file mode 100644
index 0000000000..6aba948ce0
--- /dev/null
+++ b/spec/ruby/core/main/fixtures/classes.rb
@@ -0,0 +1,18 @@
+module MainSpecs
+ module Module
+ end
+
+ module WrapIncludeModule
+ end
+
+ DATA = {}
+end
+
+
+def main_public_method
+end
+public :main_public_method
+
+def main_private_method
+end
+private :main_private_method
diff --git a/spec/ruby/core/main/fixtures/string_refinement.rb b/spec/ruby/core/main/fixtures/string_refinement.rb
new file mode 100644
index 0000000000..2dc6de52ca
--- /dev/null
+++ b/spec/ruby/core/main/fixtures/string_refinement.rb
@@ -0,0 +1,7 @@
+module StringRefinement
+ refine(String) do
+ def foo
+ 'foo'
+ end
+ end
+end
diff --git a/spec/ruby/core/main/fixtures/string_refinement_user.rb b/spec/ruby/core/main/fixtures/string_refinement_user.rb
new file mode 100644
index 0000000000..48620c325f
--- /dev/null
+++ b/spec/ruby/core/main/fixtures/string_refinement_user.rb
@@ -0,0 +1,11 @@
+using StringRefinement
+
+module MainSpecs
+ DATA[:in_module] = 'hello'.foo
+
+ def self.call_foo(x)
+ x.foo
+ end
+end
+
+MainSpecs::DATA[:toplevel] = 'hello'.foo
diff --git a/spec/ruby/core/main/fixtures/wrapped_include.rb b/spec/ruby/core/main/fixtures/wrapped_include.rb
new file mode 100644
index 0000000000..307c98b419
--- /dev/null
+++ b/spec/ruby/core/main/fixtures/wrapped_include.rb
@@ -0,0 +1 @@
+include MainSpecs::WrapIncludeModule
diff --git a/spec/ruby/core/main/include_spec.rb b/spec/ruby/core/main/include_spec.rb
new file mode 100644
index 0000000000..9f5a5f54ea
--- /dev/null
+++ b/spec/ruby/core/main/include_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "main#include" do
+ it "includes the given Module in Object" do
+ eval "include MainSpecs::Module", TOPLEVEL_BINDING
+ Object.ancestors.should include(MainSpecs::Module)
+ end
+
+ context "in a file loaded with wrapping" do
+ it "includes the given Module in the load wrapper" do
+ load(File.expand_path("../fixtures/wrapped_include.rb", __FILE__), true)
+ Object.ancestors.should_not include(MainSpecs::WrapIncludeModule)
+ end
+ end
+end
diff --git a/spec/ruby/core/main/private_spec.rb b/spec/ruby/core/main/private_spec.rb
new file mode 100644
index 0000000000..eef826ad81
--- /dev/null
+++ b/spec/ruby/core/main/private_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "main#private" do
+ after :each do
+ Object.send(:public, :main_public_method)
+ end
+
+ it "sets the visibility of the given method to private" do
+ eval "private :main_public_method", TOPLEVEL_BINDING
+ Object.should have_private_method(:main_public_method)
+ end
+
+ it "returns Object" do
+ eval("private :main_public_method", TOPLEVEL_BINDING).should equal(Object)
+ end
+
+ it "raises a NameError when given an undefined name" do
+ lambda do
+ eval "private :main_undefined_method", TOPLEVEL_BINDING
+ end.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/core/main/public_spec.rb b/spec/ruby/core/main/public_spec.rb
new file mode 100644
index 0000000000..259a8f9418
--- /dev/null
+++ b/spec/ruby/core/main/public_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "main#public" do
+ after :each do
+ Object.send(:private, :main_private_method)
+ end
+
+ it "sets the visibility of the given method to public" do
+ eval "public :main_private_method", TOPLEVEL_BINDING
+ Object.should_not have_private_method(:main_private_method)
+ end
+
+ it "returns Object" do
+ eval("public :main_private_method", TOPLEVEL_BINDING).should equal(Object)
+ end
+
+ it "raises a NameError when given an undefined name" do
+ lambda do
+ eval "public :main_undefined_method", TOPLEVEL_BINDING
+ end.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/core/main/to_s_spec.rb b/spec/ruby/core/main/to_s_spec.rb
new file mode 100644
index 0000000000..642cfa4433
--- /dev/null
+++ b/spec/ruby/core/main/to_s_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "main#to_s" do
+ it "returns 'main'" do
+ eval('to_s', TOPLEVEL_BINDING).should == "main"
+ end
+end
diff --git a/spec/ruby/core/main/using_spec.rb b/spec/ruby/core/main/using_spec.rb
new file mode 100644
index 0000000000..1fb812f5cc
--- /dev/null
+++ b/spec/ruby/core/main/using_spec.rb
@@ -0,0 +1,132 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "main.using" do
+ it "requires one Module argument" do
+ lambda do
+ eval('using', TOPLEVEL_BINDING)
+ end.should raise_error(ArgumentError)
+
+ lambda do
+ eval('using "foo"', TOPLEVEL_BINDING)
+ end.should raise_error(TypeError)
+ end
+
+ it "uses refinements from the given module only in the target file" do
+ require_relative 'fixtures/string_refinement'
+ load File.expand_path('../fixtures/string_refinement_user.rb', __FILE__)
+ MainSpecs::DATA[:in_module].should == 'foo'
+ MainSpecs::DATA[:toplevel].should == 'foo'
+ lambda do
+ 'hello'.foo
+ end.should raise_error(NoMethodError)
+ end
+
+ it "uses refinements from the given module for method calls in the target file" do
+ require_relative 'fixtures/string_refinement'
+ load File.expand_path('../fixtures/string_refinement_user.rb', __FILE__)
+ lambda do
+ 'hello'.foo
+ end.should raise_error(NoMethodError)
+ MainSpecs.call_foo('hello').should == 'foo'
+ end
+
+ it "uses refinements from the given module in the eval string" do
+ cls = MainSpecs::DATA[:cls] = Class.new {def foo; 'foo'; end}
+ MainSpecs::DATA[:mod] = Module.new do
+ refine(cls) do
+ def foo; 'bar'; end
+ end
+ end
+ eval(<<-EOS, TOPLEVEL_BINDING).should == 'bar'
+ using MainSpecs::DATA[:mod]
+ MainSpecs::DATA[:cls].new.foo
+ EOS
+ end
+
+ it "does not affect methods defined before it is called" do
+ cls = Class.new {def foo; 'foo'; end}
+ MainSpecs::DATA[:mod] = Module.new do
+ refine(cls) do
+ def foo; 'bar'; end
+ end
+ end
+ x = MainSpecs::DATA[:x] = Object.new
+ eval <<-EOS, TOPLEVEL_BINDING
+ x = MainSpecs::DATA[:x]
+ def x.before_using(obj)
+ obj.foo
+ end
+ using MainSpecs::DATA[:mod]
+ def x.after_using(obj)
+ obj.foo
+ end
+ EOS
+
+ obj = cls.new
+ x.before_using(obj).should == 'foo'
+ x.after_using(obj).should == 'bar'
+ end
+
+ it "propagates refinements added to existing modules after it is called" do
+ cls = Class.new {def foo; 'foo'; end}
+ mod = MainSpecs::DATA[:mod] = Module.new do
+ refine(cls) do
+ def foo; 'quux'; end
+ end
+ end
+ x = MainSpecs::DATA[:x] = Object.new
+ eval <<-EOS, TOPLEVEL_BINDING
+ using MainSpecs::DATA[:mod]
+ x = MainSpecs::DATA[:x]
+ def x.call_foo(obj)
+ obj.foo
+ end
+ def x.call_bar(obj)
+ obj.bar
+ end
+ EOS
+
+ obj = cls.new
+ x.call_foo(obj).should == 'quux'
+
+ mod.module_eval do
+ refine(cls) do
+ def bar; 'quux'; end
+ end
+ end
+
+ x.call_bar(obj).should == 'quux'
+ end
+
+ it "does not propagate refinements of new modules added after it is called" do
+ cls = Class.new {def foo; 'foo'; end}
+ cls2 = Class.new {def bar; 'bar'; end}
+ mod = MainSpecs::DATA[:mod] = Module.new do
+ refine(cls) do
+ def foo; 'quux'; end
+ end
+ end
+ x = MainSpecs::DATA[:x] = Object.new
+ eval <<-EOS, TOPLEVEL_BINDING
+ using MainSpecs::DATA[:mod]
+ x = MainSpecs::DATA[:x]
+ def x.call_foo(obj)
+ obj.foo
+ end
+ def x.call_bar(obj)
+ obj.bar
+ end
+ EOS
+
+ x.call_foo(cls.new).should == 'quux'
+
+ mod.module_eval do
+ refine(cls2) do
+ def bar; 'quux'; end
+ end
+ end
+
+ x.call_bar(cls2.new).should == 'bar'
+ end
+end
diff --git a/spec/ruby/core/marshal/dump_spec.rb b/spec/ruby/core/marshal/dump_spec.rb
new file mode 100644
index 0000000000..644a88b4a3
--- /dev/null
+++ b/spec/ruby/core/marshal/dump_spec.rb
@@ -0,0 +1,597 @@
+# -*- encoding: binary -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/marshal_data'
+
+describe "Marshal.dump" do
+ it "dumps nil" do
+ Marshal.dump(nil).should == "\004\b0"
+ end
+
+ it "dumps true" do
+ Marshal.dump(true).should == "\004\bT"
+ end
+
+ it "dumps false" do
+ Marshal.dump(false).should == "\004\bF"
+ end
+
+ describe "with a Fixnum" do
+ it "dumps a Fixnum" do
+ [ [Marshal, 0, "\004\bi\000"],
+ [Marshal, 5, "\004\bi\n"],
+ [Marshal, 8, "\004\bi\r"],
+ [Marshal, 122, "\004\bi\177"],
+ [Marshal, 123, "\004\bi\001{"],
+ [Marshal, 1234, "\004\bi\002\322\004"],
+ [Marshal, -8, "\004\bi\363"],
+ [Marshal, -123, "\004\bi\200"],
+ [Marshal, -124, "\004\bi\377\204"],
+ [Marshal, -1234, "\004\bi\376.\373"],
+ [Marshal, -4516727, "\004\bi\375\211\024\273"],
+ [Marshal, 2**8, "\004\bi\002\000\001"],
+ [Marshal, 2**16, "\004\bi\003\000\000\001"],
+ [Marshal, 2**24, "\004\bi\004\000\000\000\001"],
+ [Marshal, -2**8, "\004\bi\377\000"],
+ [Marshal, -2**16, "\004\bi\376\000\000"],
+ [Marshal, -2**24, "\004\bi\375\000\000\000"],
+ ].should be_computed_by(:dump)
+ end
+
+ platform_is wordsize: 64 do
+ it "dumps a positive Fixnum > 31 bits as a Bignum" do
+ Marshal.dump(2**31 + 1).should == "\x04\bl+\a\x01\x00\x00\x80"
+ end
+
+ it "dumps a negative Fixnum > 31 bits as a Bignum" do
+ Marshal.dump(-2**31 - 1).should == "\x04\bl-\a\x01\x00\x00\x80"
+ end
+ end
+ end
+
+ describe "with a Symbol" do
+ it "dumps a Symbol" do
+ Marshal.dump(:symbol).should == "\004\b:\vsymbol"
+ end
+
+ it "dumps a big Symbol" do
+ Marshal.dump(('big' * 100).to_sym).should == "\004\b:\002,\001#{'big' * 100}"
+ end
+
+ it "dumps an encoded Symbol" do
+ s = "\u2192"
+ [ [Marshal, s.encode("utf-8").to_sym,
+ "\x04\bI:\b\xE2\x86\x92\x06:\x06ET"],
+ [Marshal, s.encode("utf-16").to_sym,
+ "\x04\bI:\t\xFE\xFF!\x92\x06:\rencoding\"\vUTF-16"],
+ [Marshal, s.encode("euc-jp").to_sym,
+ "\x04\bI:\a\xA2\xAA\x06:\rencoding\"\vEUC-JP"],
+ [Marshal, s.encode("sjis").to_sym,
+ "\x04\bI:\a\x81\xA8\x06:\rencoding\"\x10Windows-31J"]
+ ].should be_computed_by(:dump)
+ end
+
+ it "dumps a binary encoded Symbol" do
+ s = "\u2192".force_encoding("binary").to_sym
+ Marshal.dump(s).should == "\x04\b:\b\xE2\x86\x92"
+ end
+
+ end
+
+ it "dumps an extended_object" do
+ Marshal.dump(Object.new.extend(Meths)).should == "\x04\be:\nMethso:\vObject\x00"
+ end
+
+ it "dumps an object that has had an ivar added and removed as though the ivar never was set" do
+ obj = Object.new
+ initial = Marshal.dump(obj)
+ obj.instance_variable_set(:@ivar, 1)
+ Marshal.dump(obj).should == "\004\bo:\vObject\006:\n@ivari\006"
+ obj.send :remove_instance_variable, :@ivar
+ Marshal.dump(obj).should == initial
+ end
+
+ describe "with an object responding to #marshal_dump" do
+ it "dumps the object returned by #marshal_dump" do
+ Marshal.dump(UserMarshal.new).should == "\x04\bU:\x10UserMarshal:\tdata"
+ end
+
+ it "does not use Class#name" do
+ UserMarshal.should_not_receive(:name)
+ Marshal.dump(UserMarshal.new)
+ end
+ end
+
+ describe "with an object responding to #_dump" do
+ it "dumps the object returned by #marshal_dump" do
+ Marshal.dump(UserDefined.new).should == "\004\bu:\020UserDefined\022\004\b[\a:\nstuff;\000"
+ end
+
+ it "raises a TypeError if _dump returns a non-string" do
+ m = mock("marshaled")
+ m.should_receive(:_dump).and_return(0)
+ lambda { Marshal.dump(m) }.should raise_error(TypeError)
+ end
+
+ it "favors marshal_dump over _dump" do
+ m = mock("marshaled")
+ m.should_receive(:marshal_dump).and_return(0)
+ m.should_not_receive(:_dump)
+ Marshal.dump(m)
+ end
+ end
+
+ describe "with a Class" do
+ it "dumps a builtin Class" do
+ Marshal.dump(String).should == "\004\bc\vString"
+ end
+
+ it "dumps a user Class" do
+ Marshal.dump(UserDefined).should == "\x04\bc\x10UserDefined"
+ end
+
+ it "dumps a nested Class" do
+ Marshal.dump(UserDefined::Nested).should == "\004\bc\030UserDefined::Nested"
+ end
+
+ it "raises TypeError with an anonymous Class" do
+ lambda { Marshal.dump(Class.new) }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError with a singleton Class" do
+ lambda { Marshal.dump(class << self; self end) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "with a Module" do
+ it "dumps a builtin Module" do
+ Marshal.dump(Marshal).should == "\004\bm\fMarshal"
+ end
+
+ it "raises TypeError with an anonymous Module" do
+ lambda { Marshal.dump(Module.new) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "with a Float" do
+ it "dumps a Float" do
+ [ [Marshal, 0.0, "\004\bf\0060"],
+ [Marshal, -0.0, "\004\bf\a-0"],
+ [Marshal, 1.0, "\004\bf\0061"],
+ [Marshal, 123.4567, "\004\bf\r123.4567"],
+ [Marshal, -0.841, "\x04\bf\v-0.841"],
+ [Marshal, -9876.345, "\x04\bf\x0E-9876.345"],
+ [Marshal, infinity_value, "\004\bf\binf"],
+ [Marshal, -infinity_value, "\004\bf\t-inf"],
+ [Marshal, nan_value, "\004\bf\bnan"],
+ ].should be_computed_by(:dump)
+ end
+ end
+
+ describe "with a Bignum" do
+ it "dumps a Bignum" do
+ [ [Marshal, -4611686018427387903, "\004\bl-\t\377\377\377\377\377\377\377?"],
+ [Marshal, -2361183241434822606847, "\004\bl-\n\377\377\377\377\377\377\377\377\177\000"],
+ ].should be_computed_by(:dump)
+ end
+
+ it "dumps a Bignum" do
+ [ [Marshal, 2**64, "\004\bl+\n\000\000\000\000\000\000\000\000\001\000"],
+ [Marshal, 2**90, "\004\bl+\v#{"\000" * 11}\004"],
+ [Marshal, -2**63, "\004\bl-\t\000\000\000\000\000\000\000\200"],
+ [Marshal, -2**64, "\004\bl-\n\000\000\000\000\000\000\000\000\001\000"],
+ ].should be_computed_by(:dump)
+ end
+ end
+
+ describe "with a String" do
+ it "dumps a blank String" do
+ Marshal.dump("".force_encoding("binary")).should == "\004\b\"\000"
+ end
+
+ it "dumps a short String" do
+ Marshal.dump("short".force_encoding("binary")).should == "\004\b\"\012short"
+ end
+
+ it "dumps a long String" do
+ Marshal.dump(("big" * 100).force_encoding("binary")).should == "\004\b\"\002,\001#{"big" * 100}"
+ end
+
+ it "dumps a String extended with a Module" do
+ Marshal.dump("".extend(Meths).force_encoding("binary")).should == "\004\be:\nMeths\"\000"
+ end
+
+ it "dumps a String subclass" do
+ Marshal.dump(UserString.new.force_encoding("binary")).should == "\004\bC:\017UserString\"\000"
+ end
+
+ it "dumps a String subclass extended with a Module" do
+ Marshal.dump(UserString.new.extend(Meths).force_encoding("binary")).should == "\004\be:\nMethsC:\017UserString\"\000"
+ end
+
+ it "dumps a String with instance variables" do
+ str = ""
+ str.instance_variable_set("@foo", "bar")
+ Marshal.dump(str.force_encoding("binary")).should == "\x04\bI\"\x00\x06:\t@foo\"\bbar"
+ end
+
+ with_feature :encoding do
+ it "dumps a US-ASCII String" do
+ str = "abc".force_encoding("us-ascii")
+ Marshal.dump(str).should == "\x04\bI\"\babc\x06:\x06EF"
+ end
+
+ it "dumps a UTF-8 String" do
+ str = "\x6d\xc3\xb6\x68\x72\x65".force_encoding("utf-8")
+ Marshal.dump(str).should == "\x04\bI\"\vm\xC3\xB6hre\x06:\x06ET"
+ end
+
+ it "dumps a String in another encoding" do
+ str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".force_encoding("utf-16le")
+ result = "\x04\bI\"\x0Fm\x00\xF6\x00h\x00r\x00e\x00\x06:\rencoding\"\rUTF-16LE"
+ Marshal.dump(str).should == result
+ end
+
+ it "dumps multiple strings using symlinks for the :E (encoding) symbol" do
+ Marshal.dump(["".encode("us-ascii"), "".encode("utf-8")]).should == "\x04\b[\aI\"\x00\x06:\x06EFI\"\x00\x06;\x00T"
+ end
+ end
+ end
+
+ describe "with a Regexp" do
+ it "dumps a Regexp" do
+ Marshal.dump(/\A.\Z/).should == "\x04\bI/\n\\A.\\Z\x00\x06:\x06EF"
+ end
+
+ it "dumps a Regexp with flags" do
+ Marshal.dump(//im).should == "\x04\bI/\x00\x05\x06:\x06EF"
+ end
+
+ it "dumps a Regexp with instance variables" do
+ o = //
+ o.instance_variable_set(:@ivar, :ivar)
+ Marshal.dump(o).should == "\x04\bI/\x00\x00\a:\x06EF:\n@ivar:\tivar"
+ end
+
+ it "dumps an extended Regexp" do
+ Marshal.dump(//.extend(Meths)).should == "\x04\bIe:\nMeths/\x00\x00\x06:\x06EF"
+ end
+
+ it "dumps a Regexp subclass" do
+ Marshal.dump(UserRegexp.new("")).should == "\x04\bIC:\x0FUserRegexp/\x00\x00\x06:\x06EF"
+ end
+
+ it "dumps a binary Regexp" do
+ o = Regexp.new("".force_encoding("binary"), Regexp::FIXEDENCODING)
+ Marshal.dump(o).should == "\x04\b/\x00\x10"
+ end
+
+ it "dumps a UTF-8 Regexp" do
+ o = Regexp.new("".force_encoding("utf-8"), Regexp::FIXEDENCODING)
+ Marshal.dump(o).should == "\x04\bI/\x00\x10\x06:\x06ET"
+ end
+
+ it "dumps a Regexp in another encoding" do
+ o = Regexp.new("".force_encoding("utf-16le"), Regexp::FIXEDENCODING)
+ Marshal.dump(o).should == "\x04\bI/\x00\x10\x06:\rencoding\"\rUTF-16LE"
+ end
+ end
+
+ describe "with an Array" do
+ it "dumps an empty Array" do
+ Marshal.dump([]).should == "\004\b[\000"
+ end
+
+ it "dumps a non-empty Array" do
+ Marshal.dump([:a, 1, 2]).should == "\004\b[\b:\006ai\006i\a"
+ end
+
+ it "dumps an Array subclass" do
+ Marshal.dump(UserArray.new).should == "\004\bC:\016UserArray[\000"
+ end
+
+ it "dumps a recursive Array" do
+ a = []
+ a << a
+ Marshal.dump(a).should == "\x04\b[\x06@\x00"
+ end
+
+ it "dumps an Array with instance variables" do
+ a = []
+ a.instance_variable_set(:@ivar, 1)
+ Marshal.dump(a).should == "\004\bI[\000\006:\n@ivari\006"
+ end
+
+ it "dumps an extended Array" do
+ Marshal.dump([].extend(Meths)).should == "\004\be:\nMeths[\000"
+ end
+ end
+
+ describe "with a Hash" do
+ it "dumps a Hash" do
+ Marshal.dump({}).should == "\004\b{\000"
+ end
+
+ it "dumps a Hash subclass" do
+ Marshal.dump(UserHash.new).should == "\004\bC:\rUserHash{\000"
+ end
+
+ it "dumps a Hash with a default value" do
+ Marshal.dump(Hash.new(1)).should == "\004\b}\000i\006"
+ end
+
+ it "raises a TypeError with hash having default proc" do
+ lambda { Marshal.dump(Hash.new {}) }.should raise_error(TypeError)
+ end
+
+ it "dumps a Hash with instance variables" do
+ a = {}
+ a.instance_variable_set(:@ivar, 1)
+ Marshal.dump(a).should == "\004\bI{\000\006:\n@ivari\006"
+ end
+
+ it "dumps an extended Hash" do
+ Marshal.dump({}.extend(Meths)).should == "\004\be:\nMeths{\000"
+ end
+
+ it "dumps an Hash subclass with a parameter to initialize" do
+ Marshal.dump(UserHashInitParams.new(1)).should == "\004\bIC:\027UserHashInitParams{\000\006:\a@ai\006"
+ end
+ end
+
+ describe "with a Struct" do
+ it "dumps a Struct" do
+ Marshal.dump(Struct::Pyramid.new).should == "\004\bS:\024Struct::Pyramid\000"
+ end
+
+ it "dumps a Struct" do
+ Marshal.dump(Struct::Useful.new(1, 2)).should == "\004\bS:\023Struct::Useful\a:\006ai\006:\006bi\a"
+ end
+
+ it "dumps a Struct with instance variables" do
+ st = Struct.new("Thick").new
+ st.instance_variable_set(:@ivar, 1)
+ Marshal.dump(st).should == "\004\bIS:\022Struct::Thick\000\006:\n@ivari\006"
+ Struct.send(:remove_const, :Thick)
+ end
+
+ it "dumps an extended Struct" do
+ st = Struct.new("Extended", :a, :b).new
+ Marshal.dump(st.extend(Meths)).should == "\004\be:\nMethsS:\025Struct::Extended\a:\006a0:\006b0"
+ Struct.send(:remove_const, :Extended)
+ end
+ end
+
+ describe "with an Object" do
+ it "dumps an Object" do
+ Marshal.dump(Object.new).should == "\004\bo:\x0BObject\x00"
+ end
+
+ it "dumps an extended Object" do
+ Marshal.dump(Object.new.extend(Meths)).should == "\004\be:\x0AMethso:\x0BObject\x00"
+ end
+
+ it "dumps an Object with an instance variable" do
+ obj = Object.new
+ obj.instance_variable_set(:@ivar, 1)
+ Marshal.dump(obj).should == "\004\bo:\vObject\006:\n@ivari\006"
+ end
+
+ it "dumps an Object that has had an instance variable added and removed as though it was never set" do
+ obj = Object.new
+ obj.instance_variable_set(:@ivar, 1)
+ obj.send(:remove_instance_variable, :@ivar)
+ Marshal.dump(obj).should == "\004\bo:\x0BObject\x00"
+ end
+
+ it "dumps an Object if it has a singleton class but no singleton methods" do
+ obj = Object.new
+ obj.singleton_class
+ Marshal.dump(obj).should == "\004\bo:\x0BObject\x00"
+ end
+
+ it "raises if an Object has a singleton class and singleton methods" do
+ obj = Object.new
+ def obj.foo; end
+ lambda {
+ Marshal.dump(obj)
+ }.should raise_error(TypeError, "singleton can't be dumped")
+ end
+
+ it "dumps a BasicObject subclass if it defines respond_to?" do
+ obj = MarshalSpec::BasicObjectSubWithRespondToFalse.new
+ Marshal.dump(obj).should == "\x04\bo:2MarshalSpec::BasicObjectSubWithRespondToFalse\x00"
+ end
+ end
+
+ describe "with a Range" do
+ it "dumps a Range inclusive of end (with indeterminant order)" do
+ dump = Marshal.dump(1..2)
+ load = Marshal.load(dump)
+ load.should == (1..2)
+ end
+
+ it "dumps a Range exclusive of end (with indeterminant order)" do
+ dump = Marshal.dump(1...2)
+ load = Marshal.load(dump)
+ load.should == (1...2)
+ end
+
+ it "dumps a Range with extra instance variables" do
+ range = (1...3)
+ range.instance_variable_set :@foo, 42
+ dump = Marshal.dump(range)
+ load = Marshal.load(dump)
+ load.should == range
+ load.instance_variable_get(:@foo).should == 42
+ end
+ end
+
+ describe "with a Time" do
+ before :each do
+ @internal = Encoding.default_internal
+ Encoding.default_internal = Encoding::UTF_8
+
+ @utc = Time.utc(2012, 1, 1)
+ @utc_dump = @utc.send(:_dump)
+
+ with_timezone 'AST', 3 do
+ @t = Time.local(2012, 1, 1)
+ @fract = Time.local(2012, 1, 1, 1, 59, 56.2)
+ @t_dump = @t.send(:_dump)
+ @fract_dump = @fract.send(:_dump)
+ end
+ end
+
+ after :each do
+ Encoding.default_internal = @internal
+ end
+
+ it "dumps the zone and the offset" do
+ with_timezone 'AST', 3 do
+ dump = Marshal.dump(@t)
+ base = "\x04\bIu:\tTime\r#{@t_dump}\a"
+ offset = ":\voffseti\x020*"
+ zone = ":\tzoneI\"\bAST\x06:\x06EF" # Last is 'F' (US-ASCII)
+ [ "#{base}#{offset}#{zone}", "#{base}#{zone}#{offset}" ].should include(dump)
+ end
+
+ it "dumps the zone, but not the offset if zone is UTC" do
+ dump = Marshal.dump(@utc)
+ zone = ":\tzoneI\"\bUTC\x06:\x06EF" # Last is 'F' (US-ASCII)
+ dump.should == "\x04\bIu:\tTime\r#{@utc_dump}\x06#{zone}"
+ end
+ end
+
+ end
+
+ describe "with an Exception" do
+ it "dumps an empty Exception" do
+ Marshal.dump(Exception.new).should == "\x04\bo:\x0EException\a:\tmesg0:\abt0"
+ end
+
+ it "dumps the message for the exception" do
+ Marshal.dump(Exception.new("foo")).should == "\x04\bo:\x0EException\a:\tmesg\"\bfoo:\abt0"
+ end
+
+ it "contains the filename in the backtrace" do
+ obj = Exception.new("foo")
+ obj.set_backtrace(["foo/bar.rb:10"])
+ Marshal.dump(obj).should == "\x04\bo:\x0EException\a:\tmesg\"\bfoo:\abt[\x06\"\x12foo/bar.rb:10"
+ end
+ end
+
+ it "dumps subsequent appearances of a symbol as a link" do
+ Marshal.dump([:a, :a]).should == "\004\b[\a:\006a;\000"
+ end
+
+ it "dumps subsequent appearances of an object as a link" do
+ o = Object.new
+ Marshal.dump([o, o]).should == "\004\b[\ao:\vObject\000@\006"
+ end
+
+ MarshalSpec::DATA_19.each do |description, (object, marshal, attributes)|
+ it "#{description} returns a binary string" do
+ Marshal.dump(object).encoding.should == Encoding::BINARY
+ end
+ end
+
+ it "raises an ArgumentError when the recursion limit is exceeded" do
+ h = {'one' => {'two' => {'three' => 0}}}
+ lambda { Marshal.dump(h, 3) }.should raise_error(ArgumentError)
+ lambda { Marshal.dump([h], 4) }.should raise_error(ArgumentError)
+ lambda { Marshal.dump([], 0) }.should raise_error(ArgumentError)
+ lambda { Marshal.dump([[[]]], 1) }.should raise_error(ArgumentError)
+ end
+
+ it "ignores the recursion limit if the limit is negative" do
+ Marshal.dump([], -1).should == "\004\b[\000"
+ Marshal.dump([[]], -1).should == "\004\b[\006[\000"
+ Marshal.dump([[[]]], -1).should == "\004\b[\006[\006[\000"
+ end
+
+ describe "when passed an IO" do
+
+ it "writes the serialized data to the IO-Object" do
+ (obj = mock('test')).should_receive(:write).at_least(1)
+ Marshal.dump("test", obj)
+ end
+
+ it "returns the IO-Object" do
+ (obj = mock('test')).should_receive(:write).at_least(1)
+ Marshal.dump("test", obj).should == obj
+ end
+
+ it "raises an Error when the IO-Object does not respond to #write" do
+ obj = mock('test')
+ lambda { Marshal.dump("test", obj) }.should raise_error(TypeError)
+ end
+
+ with_feature :encoding do
+
+ it "calls binmode when it's defined" do
+ obj = mock('test')
+ obj.should_receive(:write).at_least(1)
+ obj.should_receive(:binmode).at_least(1)
+ Marshal.dump("test", obj)
+ end
+
+ end
+
+ end
+
+ describe "when passed a StringIO" do
+
+ it "should raise an error" do
+ require "stringio"
+
+ lambda { Marshal.dump(StringIO.new) }.should raise_error(TypeError)
+ end
+
+ end
+
+ it "raises a TypeError if marshalling a Method instance" do
+ lambda { Marshal.dump(Marshal.method(:dump)) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if marshalling a Proc" do
+ lambda { Marshal.dump(proc {}) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if dumping a IO/File instance" do
+ lambda { Marshal.dump(STDIN) }.should raise_error(TypeError)
+ lambda { File.open(__FILE__) { |f| Marshal.dump(f) } }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if dumping a MatchData instance" do
+ lambda { Marshal.dump(/(.)/.match("foo")) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if dumping a Mutex instance" do
+ m = Mutex.new
+ lambda { Marshal.dump(m) }.should raise_error(TypeError)
+ end
+
+ it "returns an untainted string if object is untainted" do
+ Marshal.dump(Object.new).tainted?.should be_false
+ end
+
+ it "returns a tainted string if object is tainted" do
+ Marshal.dump(Object.new.taint).tainted?.should be_true
+ end
+
+ it "returns a tainted string if nested object is tainted" do
+ Marshal.dump([[Object.new.taint]]).tainted?.should be_true
+ end
+
+ it "returns a trusted string if object is trusted" do
+ Marshal.dump(Object.new).untrusted?.should be_false
+ end
+
+ it "returns an untrusted string if object is untrusted" do
+ Marshal.dump(Object.new.untrust).untrusted?.should be_true
+ end
+
+ it "returns an untrusted string if nested object is untrusted" do
+ Marshal.dump([[Object.new.untrust]]).untrusted?.should be_true
+ end
+end
diff --git a/spec/ruby/core/marshal/fixtures/marshal_data.rb b/spec/ruby/core/marshal/fixtures/marshal_data.rb
new file mode 100644
index 0000000000..2931278290
--- /dev/null
+++ b/spec/ruby/core/marshal/fixtures/marshal_data.rb
@@ -0,0 +1,420 @@
+# -*- encoding: binary -*-
+class UserDefined
+ class Nested
+ def ==(other)
+ other.kind_of? self.class
+ end
+ end
+
+ attr_reader :a, :b
+
+ def initialize
+ @a = 'stuff'
+ @b = @a
+ end
+
+ def _dump(depth)
+ Marshal.dump [:stuff, :stuff]
+ end
+
+ def self._load(data)
+ a, b = Marshal.load data
+
+ obj = allocate
+ obj.instance_variable_set :@a, a
+ obj.instance_variable_set :@b, b
+
+ obj
+ end
+
+ def ==(other)
+ self.class === other and
+ @a == other.a and
+ @b == other.b
+ end
+end
+
+class UserDefinedWithIvar
+ attr_reader :a, :b, :c
+
+ def initialize
+ @a = 'stuff'
+ @a.instance_variable_set :@foo, :UserDefinedWithIvar
+ @b = 'more'
+ @c = @b
+ end
+
+ def _dump(depth)
+ Marshal.dump [:stuff, :more, :more]
+ end
+
+ def self._load(data)
+ a, b, c = Marshal.load data
+
+ obj = allocate
+ obj.instance_variable_set :@a, a
+ obj.instance_variable_set :@b, b
+ obj.instance_variable_set :@c, c
+
+ obj
+ end
+
+ def ==(other)
+ self.class === other and
+ @a == other.a and
+ @b == other.b and
+ @c == other.c and
+ @a.instance_variable_get(:@foo) == other.a.instance_variable_get(:@foo)
+ end
+end
+
+class UserDefinedImmediate
+ def _dump(depth)
+ ''
+ end
+
+ def self._load(data)
+ nil
+ end
+end
+
+class UserPreviouslyDefinedWithInitializedIvar
+ attr_accessor :field1, :field2
+end
+
+class UserMarshal
+ attr_reader :data
+
+ def initialize
+ @data = 'stuff'
+ end
+ def marshal_dump() :data end
+ def marshal_load(data) @data = data end
+ def ==(other) self.class === other and @data == other.data end
+end
+
+class UserMarshalWithClassName < UserMarshal
+ def self.name
+ "Never::A::Real::Class"
+ end
+end
+
+class UserMarshalWithIvar
+ attr_reader :data
+
+ def initialize
+ @data = 'my data'
+ end
+
+ def marshal_dump
+ [:data]
+ end
+
+ def marshal_load(o)
+ @data = o.first
+ end
+
+ def ==(other)
+ self.class === other and
+ @data = other.data
+ end
+end
+
+class UserArray < Array
+end
+
+class UserHash < Hash
+end
+
+class UserHashInitParams < Hash
+ def initialize(a)
+ @a = a
+ end
+end
+
+class UserObject
+end
+
+class UserRegexp < Regexp
+end
+
+class UserString < String
+end
+
+class UserCustomConstructorString < String
+ def initialize(arg1, arg2)
+ end
+end
+
+module Meths
+ def meths_method() end
+end
+
+module MethsMore
+ def meths_more_method() end
+end
+
+Struct.new "Pyramid"
+Struct.new "Useful", :a, :b
+
+module MarshalSpec
+ class StructWithUserInitialize < Struct.new(:a)
+ THREADLOCAL_KEY = :marshal_load_struct_args
+ def initialize(*args)
+ # using thread-local to avoid ivar marshaling
+ Thread.current[THREADLOCAL_KEY] = args
+ super(*args)
+ end
+ end
+
+ class BasicObjectSubWithRespondToFalse < BasicObject
+ def respond_to?(method_name, include_all=false)
+ false
+ end
+ end
+
+ def self.random_data
+ randomizer = Random.new(42)
+ 1000.times{randomizer.rand} # Make sure we exhaust his first state of 624 random words
+ dump_data = File.binread(fixture(__FILE__, 'random.dump'))
+ [randomizer, dump_data]
+ rescue => e
+ ["Error when building Random marshal data #{e}", ""]
+ end
+
+ SwappedClass = nil
+ def self.set_swapped_class(cls)
+ remove_const(:SwappedClass)
+ const_set(:SwappedClass, cls)
+ end
+
+ def self.reset_swapped_class
+ set_swapped_class(nil)
+ end
+
+ DATA = {
+ "nil" => [nil, "\004\b0"],
+ "1..2" => [(1..2),
+ "\004\bo:\nRange\b:\nbegini\006:\texclF:\bendi\a",
+ { begin: 1, end: 2, :exclude_end? => false }],
+ "1...2" => [(1...2),
+ "\004\bo:\nRange\b:\nbegini\006:\texclT:\bendi\a",
+ { begin: 1, end: 2, :exclude_end? => true }],
+ "'a'..'b'" => [('a'..'b'),
+ "\004\bo:\nRange\b:\nbegin\"\006a:\texclF:\bend\"\006b",
+ { begin: 'a', end: 'b', :exclude_end? => false }],
+ "Struct" => [Struct::Useful.new(1, 2),
+ "\004\bS:\023Struct::Useful\a:\006ai\006:\006bi\a"],
+ "Symbol" => [:symbol,
+ "\004\b:\vsymbol"],
+ "true" => [true,
+ "\004\bT"],
+ "false" => [false,
+ "\004\bF"],
+ "String empty" => ['',
+ "\004\b\"\000"],
+ "String small" => ['small',
+ "\004\b\"\012small"],
+ "String big" => ['big' * 100,
+ "\004\b\"\002,\001#{'big' * 100}"],
+ "String extended" => [''.extend(Meths), # TODO: check for module on load
+ "\004\be:\nMeths\"\000"],
+ "String subclass" => [UserString.new,
+ "\004\bC:\017UserString\"\000"],
+ "String subclass extended" => [UserString.new.extend(Meths),
+ "\004\be:\nMethsC:\017UserString\"\000"],
+ "Symbol small" => [:big,
+ "\004\b:\010big"],
+ "Symbol big" => [('big' * 100).to_sym,
+ "\004\b:\002,\001#{'big' * 100}"],
+ "Bignum -2**64" => [-2**64,
+ "\004\bl-\n\000\000\000\000\000\000\000\000\001\000"],
+ "Bignum -2**63" => [-2**63,
+ "\004\bl-\t\000\000\000\000\000\000\000\200"],
+ "Fixnum -2**24" => [-2**24,
+ "\004\bi\375\000\000\000"],
+ "Fixnum -4516727" => [-4516727,
+ "\004\bi\375\211\024\273"],
+ "Fixnum -2**16" => [-2**16,
+ "\004\bi\376\000\000"],
+ "Fixnum -2**8" => [-2**8,
+ "\004\bi\377\000"],
+ "Fixnum -123" => [-123,
+ "\004\bi\200"],
+ "Fixnum -124" => [-124, "\004\bi\377\204"],
+ "Fixnum 0" => [0,
+ "\004\bi\000"],
+ "Fixnum 5" => [5,
+ "\004\bi\n"],
+ "Fixnum 122" => [122, "\004\bi\177"],
+ "Fixnum 123" => [123, "\004\bi\001{"],
+ "Fixnum 2**8" => [2**8,
+ "\004\bi\002\000\001"],
+ "Fixnum 2**16" => [2**16,
+ "\004\bi\003\000\000\001"],
+ "Fixnum 2**24" => [2**24,
+ "\004\bi\004\000\000\000\001"],
+ "Bignum 2**64" => [2**64,
+ "\004\bl+\n\000\000\000\000\000\000\000\000\001\000"],
+ "Bignum 2**90" => [2**90,
+ "\004\bl+\v#{"\000" * 11}\004"],
+ "Class String" => [String,
+ "\004\bc\vString"],
+ "Module Marshal" => [Marshal,
+ "\004\bm\fMarshal"],
+ "Module nested" => [UserDefined::Nested.new,
+ "\004\bo:\030UserDefined::Nested\000"],
+ "_dump object" => [UserDefinedWithIvar.new,
+ "\004\bu:\030UserDefinedWithIvar5\004\b[\bI\"\nstuff\006:\t@foo:\030UserDefinedWithIvar\"\tmore@\a"],
+ "_dump object extended" => [UserDefined.new.extend(Meths),
+ "\004\bu:\020UserDefined\022\004\b[\a\"\nstuff@\006"],
+ "marshal_dump object" => [UserMarshalWithIvar.new,
+ "\004\bU:\030UserMarshalWithIvar[\006\"\fmy data"],
+ "Regexp" => [/\A.\Z/,
+ "\004\b/\n\\A.\\Z\000"],
+ "Regexp subclass /i" => [UserRegexp.new('', Regexp::IGNORECASE),
+ "\004\bC:\017UserRegexp/\000\001"],
+ "Float 0.0" => [0.0,
+ "\004\bf\0060"],
+ "Float -0.0" => [-0.0,
+ "\004\bf\a-0"],
+ "Float Infinity" => [(1.0 / 0.0),
+ "\004\bf\binf"],
+ "Float -Infinity" => [(-1.0 / 0.0),
+ "\004\bf\t-inf"],
+ "Float 1.0" => [1.0,
+ "\004\bf\0061"],
+ "Float 8323434.342" => [8323434.342,
+ "\004\bf\0328323434.3420000002\000S\370"],
+ "Float 1.0799999999999912" => [1.0799999999999912,
+ "\004\bf\0321.0799999999999912\000\341 "],
+ "Hash" => [Hash.new,
+ "\004\b{\000"],
+ "Hash subclass" => [UserHash.new,
+ "\004\bC:\rUserHash{\000"],
+ "Array" => [Array.new,
+ "\004\b[\000"],
+ "Array subclass" => [UserArray.new,
+ "\004\bC:\016UserArray[\000"],
+ "Struct Pyramid" => [Struct::Pyramid.new,
+ "\004\bS:\024Struct::Pyramid\000"],
+ }
+ DATA_19 = {
+ "nil" => [nil, "\004\b0"],
+ "1..2" => [(1..2),
+ "\004\bo:\nRange\b:\nbegini\006:\texclF:\bendi\a",
+ { begin: 1, end: 2, :exclude_end? => false }],
+ "1...2" => [(1...2),
+ "\004\bo:\nRange\b:\nbegini\006:\texclT:\bendi\a",
+ { begin: 1, end: 2, :exclude_end? => true }],
+ "'a'..'b'" => [('a'..'b'),
+ "\004\bo:\nRange\b:\nbegin\"\006a:\texclF:\bend\"\006b",
+ { begin: 'a', end: 'b', :exclude_end? => false }],
+ "Struct" => [Struct::Useful.new(1, 2),
+ "\004\bS:\023Struct::Useful\a:\006ai\006:\006bi\a"],
+ "Symbol" => [:symbol,
+ "\004\b:\vsymbol"],
+ "true" => [true,
+ "\004\bT"],
+ "false" => [false,
+ "\004\bF"],
+ "String empty" => ['',
+ "\x04\bI\"\x00\x06:\x06EF"],
+ "String small" => ['small',
+ "\x04\bI\"\nsmall\x06:\x06EF"],
+ "String big" => ['big' * 100,
+ "\x04\bI\"\x02,\x01bigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbig\x06:\x06EF"],
+ "String extended" => [''.extend(Meths), # TODO: check for module on load
+ "\x04\bIe:\nMeths\"\x00\x06:\x06EF"],
+ "String subclass" => [UserString.new,
+ "\004\bC:\017UserString\"\000"],
+ "String subclass extended" => [UserString.new.extend(Meths),
+ "\004\be:\nMethsC:\017UserString\"\000"],
+ "Symbol small" => [:big,
+ "\004\b:\010big"],
+ "Symbol big" => [('big' * 100).to_sym,
+ "\004\b:\002,\001#{'big' * 100}"],
+ "Bignum -2**64" => [-2**64,
+ "\004\bl-\n\000\000\000\000\000\000\000\000\001\000"],
+ "Bignum -2**63" => [-2**63,
+ "\004\bl-\t\000\000\000\000\000\000\000\200"],
+ "Fixnum -2**24" => [-2**24,
+ "\004\bi\375\000\000\000"],
+ "Fixnum -2**16" => [-2**16,
+ "\004\bi\376\000\000"],
+ "Fixnum -2**8" => [-2**8,
+ "\004\bi\377\000"],
+ "Fixnum -123" => [-123,
+ "\004\bi\200"],
+ "Fixnum 0" => [0,
+ "\004\bi\000"],
+ "Fixnum 5" => [5,
+ "\004\bi\n"],
+ "Fixnum 2**8" => [2**8,
+ "\004\bi\002\000\001"],
+ "Fixnum 2**16" => [2**16,
+ "\004\bi\003\000\000\001"],
+ "Fixnum 2**24" => [2**24,
+ "\004\bi\004\000\000\000\001"],
+ "Bignum 2**64" => [2**64,
+ "\004\bl+\n\000\000\000\000\000\000\000\000\001\000"],
+ "Bignum 2**90" => [2**90,
+ "\004\bl+\v#{"\000" * 11}\004"],
+ "Class String" => [String,
+ "\004\bc\vString"],
+ "Module Marshal" => [Marshal,
+ "\004\bm\fMarshal"],
+ "Module nested" => [UserDefined::Nested.new,
+ "\004\bo:\030UserDefined::Nested\000"],
+ "_dump object" => [UserDefinedWithIvar.new,
+ "\x04\bu:\x18UserDefinedWithIvar>\x04\b[\bI\"\nstuff\a:\x06EF:\t@foo:\x18UserDefinedWithIvarI\"\tmore\x06;\x00F@\a"],
+ "_dump object extended" => [UserDefined.new.extend(Meths),
+ "\x04\bu:\x10UserDefined\x18\x04\b[\aI\"\nstuff\x06:\x06EF@\x06"],
+ "marshal_dump object" => [UserMarshalWithIvar.new,
+ "\x04\bU:\x18UserMarshalWithIvar[\x06I\"\fmy data\x06:\x06EF"],
+ "Regexp" => [/\A.\Z/,
+ "\x04\bI/\n\\A.\\Z\x00\x06:\x06EF"],
+ "Regexp subclass /i" => [UserRegexp.new('', Regexp::IGNORECASE),
+ "\x04\bIC:\x0FUserRegexp/\x00\x01\x06:\x06EF"],
+ "Float 0.0" => [0.0,
+ "\004\bf\0060"],
+ "Float -0.0" => [-0.0,
+ "\004\bf\a-0"],
+ "Float Infinity" => [(1.0 / 0.0),
+ "\004\bf\binf"],
+ "Float -Infinity" => [(-1.0 / 0.0),
+ "\004\bf\t-inf"],
+ "Float 1.0" => [1.0,
+ "\004\bf\0061"],
+ "Hash" => [Hash.new,
+ "\004\b{\000"],
+ "Hash subclass" => [UserHash.new,
+ "\004\bC:\rUserHash{\000"],
+ "Array" => [Array.new,
+ "\004\b[\000"],
+ "Array subclass" => [UserArray.new,
+ "\004\bC:\016UserArray[\000"],
+ "Struct Pyramid" => [Struct::Pyramid.new,
+ "\004\bS:\024Struct::Pyramid\000"],
+ "Random" => random_data,
+ }
+end
+
+class ArraySub < Array
+ def initialize(*args)
+ super(args)
+ end
+end
+
+class ArraySubPush < Array
+ def << value
+ raise 'broken'
+ end
+ alias_method :push, :<<
+end
+
+class SameName
+end
+
+module NamespaceTest
+end
diff --git a/spec/ruby/core/marshal/fixtures/random.dump b/spec/ruby/core/marshal/fixtures/random.dump
new file mode 100644
index 0000000000..0af56120aa
--- /dev/null
+++ b/spec/ruby/core/marshal/fixtures/random.dump
Binary files differ
diff --git a/spec/ruby/core/marshal/float_spec.rb b/spec/ruby/core/marshal/float_spec.rb
new file mode 100644
index 0000000000..5793bbd564
--- /dev/null
+++ b/spec/ruby/core/marshal/float_spec.rb
@@ -0,0 +1,77 @@
+require_relative '../../spec_helper'
+
+describe "Marshal.dump with Float" do
+ it "represents NaN" do
+ Marshal.dump(nan_value).should == "\004\bf\bnan"
+ end
+
+ it "represents +Infinity" do
+ Marshal.dump(infinity_value).should == "\004\bf\binf"
+ end
+
+ it "represents -Infinity" do
+ Marshal.dump(-infinity_value).should == "\004\bf\t-inf"
+ end
+
+ it "represents zero" do
+ Marshal.dump(0.0).should == "\004\bf\0060"
+ end
+
+ it "represents a Float less than 1" do
+ Marshal.dump(0.666666667).should == "\x04\bf\x100.666666667"
+ end
+
+ it "represents a Float much less than 1" do
+ Marshal.dump(0.000000001234697).should == "\x04\bf\x101.234697e-9"
+ end
+
+ it "represents a Float greater than 1" do
+ Marshal.dump(42.666666667).should == "\x04\bf\x1142.666666667"
+ end
+
+ it "represents a Float much greater than 1" do
+ Marshal.dump(98743561239011.0).should == "\x04\bf\x1398743561239011"
+ end
+
+ it "represents a Float much greater than 1 with a very small fractional part" do
+ Marshal.dump(799346183459.0000000002999312541).should == "\x04\bf\x11799346183459"
+ end
+end
+
+describe "Marshal.load with Float" do
+ it "loads NaN" do
+ Marshal.load("\004\bf\bnan").should be_nan
+ end
+
+ it "loads +Infinity" do
+ Marshal.load("\004\bf\binf").should == infinity_value
+ end
+
+ it "loads -Infinity" do
+ Marshal.load("\004\bf\t-inf").should == -infinity_value
+ end
+
+ it "loads zero" do
+ Marshal.load("\004\bf\0060").should == 0.0
+ end
+
+ it "loads a Float less than 1" do
+ Marshal.load("\x04\bf\x100.666666667").should == 0.666666667
+ end
+
+ it "loads a Float much less than 1" do
+ Marshal.load("\x04\bf\x101.234697e-9").should == 0.000000001234697
+ end
+
+ it "loads a Float greater than 1" do
+ Marshal.load("\x04\bf\x1142.666666667").should == 42.666666667
+ end
+
+ it "loads a Float much greater than 1" do
+ Marshal.load("\x04\bf\x1398743561239011").should == 98743561239011.0
+ end
+
+ it "loads a Float much greater than 1 with a very small fractional part" do
+ Marshal.load("\x04\bf\x16793468359.0002999").should == 793468359.0002999
+ end
+end
diff --git a/spec/ruby/core/marshal/load_spec.rb b/spec/ruby/core/marshal/load_spec.rb
new file mode 100644
index 0000000000..a5bdfbf520
--- /dev/null
+++ b/spec/ruby/core/marshal/load_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/load'
+
+describe "Marshal.load" do
+ it_behaves_like :marshal_load, :load
+end
diff --git a/spec/ruby/core/marshal/major_version_spec.rb b/spec/ruby/core/marshal/major_version_spec.rb
new file mode 100644
index 0000000000..931f1f6c27
--- /dev/null
+++ b/spec/ruby/core/marshal/major_version_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Marshal::MAJOR_VERSION" do
+ it "is 4" do
+ Marshal::MAJOR_VERSION.should == 4
+ end
+end
diff --git a/spec/ruby/core/marshal/minor_version_spec.rb b/spec/ruby/core/marshal/minor_version_spec.rb
new file mode 100644
index 0000000000..f19210c4e1
--- /dev/null
+++ b/spec/ruby/core/marshal/minor_version_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Marshal::MINOR_VERSION" do
+ it "is 8" do
+ Marshal::MINOR_VERSION.should == 8
+ end
+end
diff --git a/spec/ruby/core/marshal/restore_spec.rb b/spec/ruby/core/marshal/restore_spec.rb
new file mode 100644
index 0000000000..7e75d7dea6
--- /dev/null
+++ b/spec/ruby/core/marshal/restore_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/load'
+
+describe "Marshal.restore" do
+ it_behaves_like :marshal_load, :restore
+end
diff --git a/spec/ruby/core/marshal/shared/load.rb b/spec/ruby/core/marshal/shared/load.rb
new file mode 100644
index 0000000000..8b68384738
--- /dev/null
+++ b/spec/ruby/core/marshal/shared/load.rb
@@ -0,0 +1,830 @@
+# -*- encoding: binary -*-
+require_relative '../fixtures/marshal_data'
+require 'stringio'
+
+describe :marshal_load, shared: true do
+ before :all do
+ @num_self_class = 1
+ end
+
+ it "raises an ArgumentError when the dumped data is truncated" do
+ obj = {first: 1, second: 2, third: 3}
+ lambda { Marshal.send(@method, Marshal.dump(obj)[0, 5]) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the dumped class is missing" do
+ Object.send(:const_set, :KaBoom, Class.new)
+ kaboom = Marshal.dump(KaBoom.new)
+ Object.send(:remove_const, :KaBoom)
+
+ lambda { Marshal.send(@method, kaboom) }.should raise_error(ArgumentError)
+ end
+
+ describe "when called with a proc" do
+ it "returns the value of the proc" do
+ Marshal.send(@method, Marshal.dump([1,2]), proc { [3,4] }).should == [3,4]
+ end
+
+ it "calls the proc for recursively visited data" do
+ a = [1]
+ a << a
+ ret = []
+ Marshal.send(@method, Marshal.dump(a), proc { |arg| ret << arg; arg })
+ ret.first.should == 1
+ ret[1].should == [1,a]
+ ret[2].should == a
+ ret.size.should == 3
+ end
+
+ it "loads an Array with proc" do
+ arr = []
+ s = 'hi'
+ s.instance_variable_set(:@foo, 5)
+ st = Struct.new("Brittle", :a).new
+ st.instance_variable_set(:@clue, 'none')
+ st.a = 0.0
+ h = Hash.new('def')
+ h['nine'] = 9
+ a = [:a, :b, :c]
+ a.instance_variable_set(:@two, 2)
+ obj = [s, 10, s, s, st, a]
+ obj.instance_variable_set(:@zoo, 'ant')
+ proc = Proc.new { |o| arr << o; o}
+
+ Marshal.send(@method, "\x04\bI[\vI\"\ahi\a:\x06EF:\t@fooi\ni\x0F@\x06@\x06IS:\x14Struct::Brittle\x06:\x06af\x060\x06:\n@clueI\"\tnone\x06;\x00FI[\b;\b:\x06b:\x06c\x06:\t@twoi\a\x06:\t@zooI\"\bant\x06;\x00F", proc)
+
+ arr.should == ["hi", false, 5, 10, "hi", "hi", 0.0, st, "none", false,
+ :b, :c, a, 2, ["hi", 10, "hi", "hi", st, [:a, :b, :c]], "ant", false]
+
+ Struct.send(:remove_const, :Brittle)
+ end
+ end
+
+ describe "when called with nil for the proc argument" do
+ it "behaves as if no proc argument was passed" do
+ a = [1]
+ a << a
+ b = Marshal.send(@method, Marshal.dump(a), nil)
+ b.should == a
+ end
+ end
+
+ describe "when called on objects with custom _dump methods" do
+ it "does not set instance variables of an object with user-defined _dump/_load" do
+ # this string represents: <#UserPreviouslyDefinedWithInitializedIvar @field2=7 @field1=6>
+ dump_str = "\004\bu:-UserPreviouslyDefinedWithInitializedIvar\a:\f@field2i\f:\f@field1i\v"
+
+ UserPreviouslyDefinedWithInitializedIvar.should_receive(:_load).and_return(UserPreviouslyDefinedWithInitializedIvar.new)
+ marshaled_obj = Marshal.send(@method, dump_str)
+
+ marshaled_obj.should be_an_instance_of(UserPreviouslyDefinedWithInitializedIvar)
+ marshaled_obj.field1.should be_nil
+ marshaled_obj.field2.should be_nil
+ end
+
+ describe "that return an immediate value" do
+ it "loads an array containing an instance of the object, followed by multiple instances of another object" do
+ str = "string"
+
+ # this string represents: [<#UserDefinedImmediate A>, <#String "string">, <#String "string">]
+ marshaled_obj = Marshal.send(@method, "\004\b[\bu:\031UserDefinedImmediate\000\"\vstring@\a")
+
+ marshaled_obj.should == [nil, str, str]
+ end
+
+ it "loads any structure with multiple references to the same object, followed by multiple instances of another object" do
+ str = "string"
+
+ # this string represents: {a: <#UserDefinedImmediate A>, b: <#UserDefinedImmediate A>, c: <#String "string">, d: <#String "string">}
+ hash_dump = "\x04\b{\t:\x06aIu:\x19UserDefinedImmediate\x00\x06:\x06ET:\x06b@\x06:\x06cI\"\vstring\x06;\aT:\x06d@\a"
+
+ marshaled_obj = Marshal.send(@method, hash_dump)
+ marshaled_obj.should == {a: nil, b: nil, c: str, d: str}
+
+ # this string represents: [<#UserDefinedImmediate A>, <#UserDefinedImmediate A>, <#String "string">, <#String "string">]
+ array_dump = "\x04\b[\tIu:\x19UserDefinedImmediate\x00\x06:\x06ET@\x06I\"\vstring\x06;\x06T@\a"
+
+ marshaled_obj = Marshal.send(@method, array_dump)
+ marshaled_obj.should == [nil, nil, str, str]
+ end
+
+ it "loads an array containing references to multiple instances of the object, followed by multiple instances of another object" do
+ str = "string"
+
+ # this string represents: [<#UserDefinedImmediate A>, <#UserDefinedImmediate B>, <#String "string">, <#String "string">]
+ array_dump = "\x04\b[\tIu:\x19UserDefinedImmediate\x00\x06:\x06ETIu;\x00\x00\x06;\x06TI\"\vstring\x06;\x06T@\b"
+
+ marshaled_obj = Marshal.send(@method, array_dump)
+ marshaled_obj.should == [nil, nil, str, str]
+ end
+ end
+ end
+
+ it "loads an array containing objects having _dump method, and with proc" do
+ arr = []
+ myproc = Proc.new { |o| arr << o; o }
+ o1 = UserDefined.new;
+ o2 = UserDefinedWithIvar.new
+ obj = [o1, o2, o1, o2]
+
+ Marshal.send(@method, "\x04\b[\tu:\x10UserDefined\x18\x04\b[\aI\"\nstuff\x06:\x06EF@\x06u:\x18UserDefinedWithIvar>\x04\b[\bI\"\nstuff\a:\x06EF:\t@foo:\x18UserDefinedWithIvarI\"\tmore\x06;\x00F@\a@\x06@\a", myproc)
+
+ arr.should == [o1, o2, o1, o2, obj]
+ end
+
+ it "loads an array containing objects having marshal_dump method, and with proc" do
+ arr = []
+ proc = Proc.new { |o| arr << o; o }
+ o1 = UserMarshal.new
+ o2 = UserMarshalWithIvar.new
+ obj = [o1, o2, o1, o2]
+
+ Marshal.send(@method, "\004\b[\tU:\020UserMarshal\"\nstuffU:\030UserMarshalWithIvar[\006\"\fmy data@\006@\b", proc)
+
+ arr.should == ['stuff', o1, 'my data', ['my data'], o2, o1, o2, obj]
+ end
+
+ it "assigns classes to nested subclasses of Array correctly" do
+ arr = ArraySub.new(ArraySub.new)
+ arr_dump = Marshal.dump(arr)
+ Marshal.send(@method, arr_dump).class.should == ArraySub
+ end
+
+ it "loads subclasses of Array with overridden << and push correctly" do
+ arr = ArraySubPush.new
+ arr[0] = '1'
+ arr_dump = Marshal.dump(arr)
+ Marshal.send(@method, arr_dump).should == arr
+ end
+
+ it "raises a TypeError with bad Marshal version" do
+ marshal_data = '\xff\xff'
+ marshal_data[0] = (Marshal::MAJOR_VERSION).chr
+ marshal_data[1] = (Marshal::MINOR_VERSION + 1).chr
+
+ lambda { Marshal.send(@method, marshal_data) }.should raise_error(TypeError)
+
+ marshal_data = '\xff\xff'
+ marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr
+ marshal_data[1] = (Marshal::MINOR_VERSION).chr
+
+ lambda { Marshal.send(@method, marshal_data) }.should raise_error(TypeError)
+ end
+
+ it "raises EOFError on loading an empty file" do
+ temp_file = tmp("marshal.rubyspec.tmp.#{Process.pid}")
+ file = File.new(temp_file, "w+")
+ begin
+ lambda { Marshal.send(@method, file) }.should raise_error(EOFError)
+ ensure
+ file.close
+ rm_r temp_file
+ end
+ end
+
+ it "returns an untainted object if source is untainted" do
+ x = Object.new
+ y = Marshal.send(@method, Marshal.dump(x))
+ y.tainted?.should be_false
+ end
+
+ describe "when source is tainted" do
+ it "returns a tainted object" do
+ x = Object.new
+ x.taint
+ s = Marshal.dump(x)
+ y = Marshal.send(@method, s)
+ y.tainted?.should be_true
+
+ # note that round-trip via Marshal does not preserve
+ # the taintedness at each level of the nested structure
+ y = Marshal.send(@method, Marshal.dump([[x]]))
+ y.tainted?.should be_true
+ y.first.tainted?.should be_true
+ y.first.first.tainted?.should be_true
+ end
+
+ it "does not taint Symbols" do
+ x = [:x]
+ y = Marshal.send(@method, Marshal.dump(x).taint)
+ y.tainted?.should be_true
+ y.first.tainted?.should be_false
+ end
+
+ it "does not taint Fixnums" do
+ x = [1]
+ y = Marshal.send(@method, Marshal.dump(x).taint)
+ y.tainted?.should be_true
+ y.first.tainted?.should be_false
+ end
+
+ it "does not taint Bignums" do
+ x = [bignum_value]
+ y = Marshal.send(@method, Marshal.dump(x).taint)
+ y.tainted?.should be_true
+ y.first.tainted?.should be_false
+ end
+
+ it "does not taint Floats" do
+ x = [1.2]
+ y = Marshal.send(@method, Marshal.dump(x).taint)
+ y.tainted?.should be_true
+ y.first.tainted?.should be_false
+ end
+ end
+
+ it "preserves taintedness of nested structure" do
+ x = Object.new
+ a = [[x]]
+ x.taint
+ y = Marshal.send(@method, Marshal.dump(a))
+ y.tainted?.should be_true
+ y.first.tainted?.should be_true
+ y.first.first.tainted?.should be_true
+ end
+
+ it "returns a trusted object if source is trusted" do
+ x = Object.new
+ y = Marshal.send(@method, Marshal.dump(x))
+ y.untrusted?.should be_false
+ end
+
+ it "returns an untrusted object if source is untrusted" do
+ x = Object.new
+ x.untrust
+ y = Marshal.send(@method, Marshal.dump(x))
+ y.untrusted?.should be_true
+
+ # note that round-trip via Marshal does not preserve
+ # the untrustedness at each level of the nested structure
+ y = Marshal.send(@method, Marshal.dump([[x]]))
+ y.untrusted?.should be_true
+ y.first.untrusted?.should be_true
+ y.first.first.untrusted?.should be_true
+ end
+
+ # Note: Ruby 1.9 should be compatible with older marshal format
+ MarshalSpec::DATA.each do |description, (object, marshal, attributes)|
+ it "loads a #{description}" do
+ Marshal.send(@method, marshal).should == object
+ end
+ end
+
+ MarshalSpec::DATA_19.each do |description, (object, marshal, attributes)|
+ it "loads a #{description}" do
+ Marshal.send(@method, marshal).should == object
+ end
+ end
+
+ describe "for an Array" do
+ it "loads an array containing the same objects" do
+ s = 'oh'
+ b = 'hi'
+ r = //
+ d = [b, :no, s, :go]
+ c = String
+ f = 1.0
+
+ o1 = UserMarshalWithIvar.new; o2 = UserMarshal.new
+
+ obj = [:so, 'hello', 100, :so, :so, d, :so, o2, :so, :no, o2,
+ :go, c, nil, Struct::Pyramid.new, f, :go, :no, s, b, r,
+ :so, 'huh', o1, true, b, b, 99, r, b, s, :so, f, c, :no, o1, d]
+
+ Marshal.send(@method, "\004\b[*:\aso\"\nhelloii;\000;\000[\t\"\ahi:\ano\"\aoh:\ago;\000U:\020UserMarshal\"\nstuff;\000;\006@\n;\ac\vString0S:\024Struct::Pyramid\000f\0061;\a;\006@\t@\b/\000\000;\000\"\bhuhU:\030UserMarshalWithIvar[\006\"\fmy dataT@\b@\bih@\017@\b@\t;\000@\016@\f;\006@\021@\a").should ==
+ obj
+ end
+
+ it "loads an array having ivar" do
+ s = 'well'
+ s.instance_variable_set(:@foo, 10)
+ obj = ['5', s, 'hi'].extend(Meths, MethsMore)
+ obj.instance_variable_set(:@mix, s)
+ new_obj = Marshal.send(@method, "\004\bI[\b\"\0065I\"\twell\006:\t@fooi\017\"\ahi\006:\t@mix@\a")
+ new_obj.should == obj
+ new_obj.instance_variable_get(:@mix).should equal new_obj[1]
+ new_obj[1].instance_variable_get(:@foo).should == 10
+ end
+
+ it "loads an extended Array object containing a user-marshaled object" do
+ obj = [UserMarshal.new, UserMarshal.new].extend(Meths)
+ new_obj = Marshal.send(@method, "\x04\be:\nMeths[\ao:\x10UserMarshal\x06:\n@dataI\"\nstuff\x06:\x06ETo;\x06\x06;\aI\"\nstuff\x06;\bT")
+
+ new_obj.should == obj
+ obj_ancestors = class << obj; ancestors[1..-1]; end
+ new_obj_ancestors = class << new_obj; ancestors[1..-1]; end
+ obj_ancestors.should == new_obj_ancestors
+ end
+ end
+
+ describe "for a Hash" do
+ it "loads an extended_user_hash with a parameter to initialize" do
+ obj = UserHashInitParams.new(:abc).extend(Meths)
+
+ new_obj = Marshal.send(@method, "\004\bIe:\nMethsC:\027UserHashInitParams{\000\006:\a@a:\babc")
+
+ new_obj.should == obj
+ new_obj_metaclass_ancestors = class << new_obj; ancestors; end
+ new_obj_metaclass_ancestors[@num_self_class].should == Meths
+ new_obj_metaclass_ancestors[@num_self_class+1].should == UserHashInitParams
+ end
+
+ it "loads an extended hash object containing a user-marshaled object" do
+ obj = {a: UserMarshal.new}.extend(Meths)
+
+ new_obj = Marshal.send(@method, "\004\be:\nMeths{\006:\006aU:\020UserMarshal\"\nstuff")
+
+ new_obj.should == obj
+ new_obj_metaclass_ancestors = class << new_obj; ancestors; end
+ new_obj_metaclass_ancestors[@num_self_class].should == Meths
+ new_obj_metaclass_ancestors[@num_self_class+1].should == Hash
+ end
+
+ it "preserves hash ivars when hash contains a string having ivar" do
+ s = 'string'
+ s.instance_variable_set :@string_ivar, 'string ivar'
+ h = { key: s }
+ h.instance_variable_set :@hash_ivar, 'hash ivar'
+
+ unmarshalled = Marshal.send(@method, Marshal.dump(h))
+ unmarshalled.instance_variable_get(:@hash_ivar).should == 'hash ivar'
+ unmarshalled[:key].instance_variable_get(:@string_ivar).should == 'string ivar'
+ end
+ end
+
+ describe "for a String" do
+ it "loads a string having ivar with ref to self" do
+ obj = 'hi'
+ obj.instance_variable_set(:@self, obj)
+ Marshal.send(@method, "\004\bI\"\ahi\006:\n@self@\000").should == obj
+ end
+
+ it "loads a string through StringIO stream" do
+ obj = "This is a string which should be unmarshalled through StringIO stream!"
+ Marshal.send(@method, StringIO.new(Marshal.dump(obj))).should == obj
+ end
+
+ it "loads a string with an ivar" do
+ str = Marshal.send(@method, "\x04\bI\"\x00\x06:\t@fooI\"\bbar\x06:\x06EF")
+ str.instance_variable_get("@foo").should == "bar"
+ end
+
+ it "loads a String subclass with custom constructor" do
+ str = Marshal.send(@method, "\x04\bC: UserCustomConstructorString\"\x00")
+ str.should be_an_instance_of(UserCustomConstructorString)
+ end
+
+ with_feature :encoding do
+ it "loads a US-ASCII String" do
+ str = "abc".force_encoding("us-ascii")
+ data = "\x04\bI\"\babc\x06:\x06EF"
+ result = Marshal.send(@method, data)
+ result.should == str
+ result.encoding.should equal(Encoding::US_ASCII)
+ end
+
+ it "loads a UTF-8 String" do
+ str = "\x6d\xc3\xb6\x68\x72\x65".force_encoding("utf-8")
+ data = "\x04\bI\"\vm\xC3\xB6hre\x06:\x06ET"
+ result = Marshal.send(@method, data)
+ result.should == str
+ result.encoding.should equal(Encoding::UTF_8)
+ end
+
+ it "loads a String in another encoding" do
+ str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".force_encoding("utf-16le")
+ data = "\x04\bI\"\x0Fm\x00\xF6\x00h\x00r\x00e\x00\x06:\rencoding\"\rUTF-16LE"
+ result = Marshal.send(@method, data)
+ result.should == str
+ result.encoding.should equal(Encoding::UTF_16LE)
+ end
+
+ it "loads a String as ASCII-8BIT if no encoding is specified at the end" do
+ str = "\xC3\xB8".force_encoding("ASCII-8BIT")
+ data = "\x04\b\"\a\xC3\xB8".force_encoding("UTF-8")
+ result = Marshal.send(@method, data)
+ result.encoding.should == Encoding::ASCII_8BIT
+ result.should == str
+ end
+ end
+ end
+
+ describe "for a Struct" do
+ it "loads a extended_struct having fields with same objects" do
+ s = 'hi'
+ obj = Struct.new("Ure2", :a, :b).new.extend(Meths)
+ obj.a = [:a, s]
+ obj.b = [:Meths, s]
+
+ Marshal.send(@method,
+ "\004\be:\nMethsS:\021Struct::Ure2\a:\006a[\a;\a\"\ahi:\006b[\a;\000@\a"
+ ).should == obj
+ Struct.send(:remove_const, :Ure2)
+ end
+
+ it "loads a struct having ivar" do
+ obj = Struct.new("Thick").new
+ obj.instance_variable_set(:@foo, 5)
+ Marshal.send(@method, "\004\bIS:\022Struct::Thick\000\006:\t@fooi\n").should == obj
+ Struct.send(:remove_const, :Thick)
+ end
+
+ it "loads a struct having fields" do
+ obj = Struct.new("Ure1", :a, :b).new
+ Marshal.send(@method, "\004\bS:\021Struct::Ure1\a:\006a0:\006b0").should == obj
+ Struct.send(:remove_const, :Ure1)
+ end
+
+ it "does not call initialize on the unmarshaled struct" do
+ threadlocal_key = MarshalSpec::StructWithUserInitialize::THREADLOCAL_KEY
+
+ s = MarshalSpec::StructWithUserInitialize.new('foo')
+ Thread.current[threadlocal_key].should == ['foo']
+ s.a.should == 'foo'
+
+ Thread.current[threadlocal_key] = nil
+
+ dumped = Marshal.dump(s)
+ loaded = Marshal.send(@method, dumped)
+
+ Thread.current[threadlocal_key].should == nil
+ loaded.a.should == 'foo'
+ end
+ end
+
+ describe "for an Exception" do
+ it "loads a marshalled exception with no message" do
+ obj = Exception.new
+ loaded = Marshal.send(@method, "\004\bo:\016Exception\a:\abt0:\tmesg0")
+ loaded.message.should == obj.message
+ loaded.backtrace.should == obj.backtrace
+ loaded = Marshal.send(@method, "\x04\bo:\x0EException\a:\tmesg0:\abt0")
+ loaded.message.should == obj.message
+ loaded.backtrace.should == obj.backtrace
+ end
+
+ it "loads a marshalled exception with a message" do
+ obj = Exception.new("foo")
+ loaded = Marshal.send(@method, "\004\bo:\016Exception\a:\abt0:\tmesg\"\bfoo")
+ loaded.message.should == obj.message
+ loaded.backtrace.should == obj.backtrace
+ loaded = Marshal.send(@method, "\x04\bo:\x0EException\a:\tmesgI\"\bfoo\x06:\x06EF:\abt0")
+ loaded.message.should == obj.message
+ loaded.backtrace.should == obj.backtrace
+ end
+
+ it "loads a marshalled exception with a backtrace" do
+ obj = Exception.new("foo")
+ obj.set_backtrace(["foo/bar.rb:10"])
+ loaded = Marshal.send(@method, "\004\bo:\016Exception\a:\abt[\006\"\022foo/bar.rb:10:\tmesg\"\bfoo")
+ loaded.message.should == obj.message
+ loaded.backtrace.should == obj.backtrace
+ loaded = Marshal.send(@method, "\x04\bo:\x0EException\a:\tmesgI\"\bfoo\x06:\x06EF:\abt[\x06I\"\x12foo/bar.rb:10\x06;\aF")
+ loaded.message.should == obj.message
+ loaded.backtrace.should == obj.backtrace
+ end
+ end
+
+ describe "for a user Class" do
+ it "loads a user-marshaled extended object" do
+ obj = UserMarshal.new.extend(Meths)
+
+ new_obj = Marshal.send(@method, "\004\bU:\020UserMarshal\"\nstuff")
+
+ new_obj.should == obj
+ new_obj_metaclass_ancestors = class << new_obj; ancestors; end
+ new_obj_metaclass_ancestors[@num_self_class].should == UserMarshal
+ end
+
+ it "loads a user_object" do
+ UserObject.new
+ Marshal.send(@method, "\004\bo:\017UserObject\000").should be_kind_of(UserObject)
+ end
+
+ it "loads an object" do
+ Marshal.send(@method, "\004\bo:\vObject\000").should be_kind_of(Object)
+ end
+
+ it "raises ArgumentError if the object from an 'o' stream is not dumpable as 'o' type user class" do
+ lambda do
+ Marshal.send(@method, "\x04\bo:\tFile\001\001:\001\005@path\"\x10/etc/passwd")
+ end.should raise_error(ArgumentError)
+ end
+
+ it "loads an extended Object" do
+ obj = Object.new.extend(Meths)
+
+ new_obj = Marshal.send(@method, "\004\be:\nMethso:\vObject\000")
+
+ new_obj.class.should == obj.class
+ new_obj_metaclass_ancestors = class << new_obj; ancestors; end
+ new_obj_metaclass_ancestors[@num_self_class, 2].should == [Meths, Object]
+ end
+
+ describe "that extends a core type other than Object or BasicObject" do
+ after :each do
+ MarshalSpec.reset_swapped_class
+ end
+
+ it "raises ArgumentError if the resulting class does not extend the same type" do
+ MarshalSpec.set_swapped_class(Class.new(Hash))
+ data = Marshal.dump(MarshalSpec::SwappedClass.new)
+
+ MarshalSpec.set_swapped_class(Class.new(Array))
+ lambda { Marshal.send(@method, data) }.should raise_error(ArgumentError)
+
+ MarshalSpec.set_swapped_class(Class.new)
+ lambda { Marshal.send(@method, data) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "loads an object having ivar" do
+ s = 'hi'
+ arr = [:so, :so, s, s]
+ obj = Object.new
+ obj.instance_variable_set :@str, arr
+
+ new_obj = Marshal.send(@method, "\004\bo:\vObject\006:\t@str[\t:\aso;\a\"\ahi@\a")
+ new_str = new_obj.instance_variable_get :@str
+
+ new_str.should == arr
+ end
+ end
+
+ describe "for a Regexp" do
+ it "loads an extended Regexp" do
+ obj = /[a-z]/.extend(Meths, MethsMore)
+ new_obj = Marshal.send(@method, "\004\be:\nMethse:\016MethsMore/\n[a-z]\000")
+
+ new_obj.should == obj
+ new_obj_metaclass_ancestors = class << new_obj; ancestors; end
+ new_obj_metaclass_ancestors[@num_self_class, 3].should ==
+ [Meths, MethsMore, Regexp]
+ end
+
+ it "loads a extended_user_regexp having ivar" do
+ obj = UserRegexp.new('').extend(Meths)
+ obj.instance_variable_set(:@noise, 'much')
+
+ new_obj = Marshal.send(@method, "\004\bIe:\nMethsC:\017UserRegexp/\000\000\006:\v@noise\"\tmuch")
+
+ new_obj.should == obj
+ new_obj.instance_variable_get(:@noise).should == 'much'
+ new_obj_metaclass_ancestors = class << new_obj; ancestors; end
+ new_obj_metaclass_ancestors[@num_self_class, 3].should ==
+ [Meths, UserRegexp, Regexp]
+ end
+ end
+
+ describe "for a Float" do
+ it "loads a Float NaN" do
+ obj = 0.0 / 0.0
+ Marshal.send(@method, "\004\bf\bnan").to_s.should == obj.to_s
+ end
+
+ it "loads a Float 1.3" do
+ Marshal.send(@method, "\004\bf\v1.3\000\314\315").should == 1.3
+ end
+
+ it "loads a Float -5.1867345e-22" do
+ obj = -5.1867345e-22
+ Marshal.send(@method, "\004\bf\037-5.1867345000000008e-22\000\203_").should be_close(obj, 1e-30)
+ end
+
+ it "loads a Float 1.1867345e+22" do
+ obj = 1.1867345e+22
+ Marshal.send(@method, "\004\bf\0361.1867344999999999e+22\000\344@").should == obj
+ end
+ end
+
+ describe "for a Integer" do
+ it "loads 0" do
+ Marshal.send(@method, "\004\bi\000").should == 0
+ Marshal.send(@method, "\004\bi\005").should == 0
+ end
+
+ it "loads an Integer 8" do
+ Marshal.send(@method, "\004\bi\r" ).should == 8
+ end
+
+ it "loads and Integer -8" do
+ Marshal.send(@method, "\004\bi\363" ).should == -8
+ end
+
+ it "loads an Integer 1234" do
+ Marshal.send(@method, "\004\bi\002\322\004").should == 1234
+ end
+
+ it "loads an Integer -1234" do
+ Marshal.send(@method, "\004\bi\376.\373").should == -1234
+ end
+
+ it "loads an Integer 4611686018427387903" do
+ Marshal.send(@method, "\004\bl+\t\377\377\377\377\377\377\377?").should == 4611686018427387903
+ end
+
+ it "loads an Integer -4611686018427387903" do
+ Marshal.send(@method, "\004\bl-\t\377\377\377\377\377\377\377?").should == -4611686018427387903
+ end
+
+ it "loads an Integer 2361183241434822606847" do
+ Marshal.send(@method, "\004\bl+\n\377\377\377\377\377\377\377\377\177\000").should == 2361183241434822606847
+ end
+
+ it "loads an Integer -2361183241434822606847" do
+ Marshal.send(@method, "\004\bl-\n\377\377\377\377\377\377\377\377\177\000").should == -2361183241434822606847
+ end
+
+ it "raises ArgumentError if the input is too short" do
+ ["\004\bi",
+ "\004\bi\001",
+ "\004\bi\002",
+ "\004\bi\002\0",
+ "\004\bi\003",
+ "\004\bi\003\0",
+ "\004\bi\003\0\0",
+ "\004\bi\004",
+ "\004\bi\004\0",
+ "\004\bi\004\0\0",
+ "\004\bi\004\0\0\0"].each do |invalid|
+ lambda { Marshal.send(@method, invalid) }.should raise_error(ArgumentError)
+ end
+ end
+
+ if 0.size == 8 # for platforms like x86_64
+ it "roundtrips 4611686018427387903 from dump/load correctly" do
+ Marshal.send(@method, Marshal.dump(4611686018427387903)).should == 4611686018427387903
+ end
+ end
+ end
+
+ describe "for a Rational" do
+ it "loads" do
+ Marshal.send(@method, Marshal.dump(Rational(1, 3))).should == Rational(1, 3)
+ end
+ end
+
+ describe "for a Complex" do
+ it "loads" do
+ Marshal.send(@method, Marshal.dump(Complex(4, 3))).should == Complex(4, 3)
+ end
+ end
+
+ describe "for a Bignum" do
+ platform_is wordsize: 64 do
+ context "that is Bignum on 32-bit platforms but Fixnum on 64-bit" do
+ it "dumps a Fixnum" do
+ val = Marshal.send(@method, "\004\bl+\ab:wU")
+ val.should == 1433877090
+ val.class.should == Fixnum
+ end
+
+ it "dumps an array containing multiple references to the Bignum as an array of Fixnum" do
+ arr = Marshal.send(@method, "\004\b[\al+\a\223BwU@\006")
+ arr.should == [1433879187, 1433879187]
+ arr.each { |v| v.class.should == Fixnum }
+ end
+ end
+ end
+ end
+
+ describe "for a Time" do
+ it "loads" do
+ Marshal.send(@method, Marshal.dump(Time.at(1))).should == Time.at(1)
+ end
+
+ it "loads serialized instance variables" do
+ t = Time.new
+ t.instance_variable_set(:@foo, 'bar')
+
+ Marshal.send(@method, Marshal.dump(t)).instance_variable_get(:@foo).should == 'bar'
+ end
+
+ it "loads Time objects stored as links" do
+ t = Time.new
+
+ t1, t2 = Marshal.send(@method, Marshal.dump([t, t]))
+ t1.should equal t2
+ end
+
+ it "loads the zone" do
+ with_timezone 'AST', 3 do
+ t = Time.local(2012, 1, 1)
+ Marshal.send(@method, Marshal.dump(t)).zone.should == t.zone
+ end
+ end
+
+ it "loads nanoseconds" do
+ t = Time.now
+ Marshal.send(@method, Marshal.dump(t)).nsec.should == t.nsec
+ end
+ end
+
+ describe "for nil" do
+ it "loads" do
+ Marshal.send(@method, "\x04\b0").should be_nil
+ end
+ end
+
+ describe "for true" do
+ it "loads" do
+ Marshal.send(@method, "\x04\bT").should be_true
+ end
+ end
+
+ describe "for false" do
+ it "loads" do
+ Marshal.send(@method, "\x04\bF").should be_false
+ end
+ end
+
+ describe "for a Class" do
+ it "loads" do
+ Marshal.send(@method, "\x04\bc\vString").should == String
+ end
+
+ it "raises ArgumentError if given the name of a non-Module" do
+ lambda { Marshal.send(@method, "\x04\bc\vKernel") }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if given a nonexistent class" do
+ lambda { Marshal.send(@method, "\x04\bc\vStrung") }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "for a Module" do
+ it "loads a module" do
+ Marshal.send(@method, "\x04\bm\vKernel").should == Kernel
+ end
+
+ it "raises ArgumentError if given the name of a non-Class" do
+ lambda { Marshal.send(@method, "\x04\bm\vString") }.should raise_error(ArgumentError)
+ end
+
+ it "loads an old module" do
+ Marshal.send(@method, "\x04\bM\vKernel").should == Kernel
+ end
+ end
+
+ describe "for a wrapped C pointer" do
+ it "loads" do
+ class DumpableDir < Dir
+ def _dump_data
+ path
+ end
+ def _load_data path
+ initialize(path)
+ end
+ end
+
+ data = "\x04\bd:\x10DumpableDirI\"\x06.\x06:\x06ET"
+
+ dir = Marshal.send(@method, data)
+ begin
+ dir.path.should == '.'
+ ensure
+ dir.close
+ end
+ end
+
+ it "raises TypeError when the local class is missing _load_data" do
+ class UnloadableDumpableDir < Dir
+ def _dump_data
+ path
+ end
+ # no _load_data
+ end
+
+ data = "\x04\bd:\x1AUnloadableDumpableDirI\"\x06.\x06:\x06ET"
+
+ lambda { Marshal.send(@method, data) }.should raise_error(TypeError)
+ end
+
+ it "raises ArgumentError when the local class is a regular object" do
+ data = "\004\bd:\020UserDefined\0"
+
+ lambda { Marshal.send(@method, data) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "when a class does not exist in the namespace" do
+ before :each do
+ NamespaceTest.send(:const_set, :SameName, Class.new)
+ @data = Marshal.dump(NamespaceTest::SameName.new)
+ NamespaceTest.send(:remove_const, :SameName)
+ end
+
+ it "raises an ArgumentError" do
+ message = "undefined class/module NamespaceTest::SameName"
+ lambda { Marshal.send(@method, @data) }.should raise_error(ArgumentError, message)
+ end
+ end
+
+ it "raises an ArgumentError with full constant name when the dumped constant is missing" do
+ NamespaceTest.send(:const_set, :KaBoom, Class.new)
+ @data = Marshal.dump(NamespaceTest::KaBoom.new)
+ NamespaceTest.send(:remove_const, :KaBoom)
+
+ lambda { Marshal.send(@method, @data) }.should raise_error(ArgumentError, /NamespaceTest::KaBoom/)
+ end
+end
diff --git a/spec/ruby/core/matchdata/begin_spec.rb b/spec/ruby/core/matchdata/begin_spec.rb
new file mode 100644
index 0000000000..b791018633
--- /dev/null
+++ b/spec/ruby/core/matchdata/begin_spec.rb
@@ -0,0 +1,30 @@
+# -*- encoding: utf-8 -*-
+
+require_relative '../../spec_helper'
+
+describe "MatchData#begin" do
+ it "returns the offset of the start of the nth element" do
+ match_data = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ match_data.begin(0).should == 1
+ match_data.begin(2).should == 2
+ end
+
+ it "returns nil when the nth match isn't found" do
+ match_data = /something is( not)? (right)/.match("something is right")
+ match_data.begin(1).should be_nil
+ end
+
+ it "returns the offset for multi byte strings" do
+ match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.")
+ match_data.begin(0).should == 1
+ match_data.begin(2).should == 2
+ end
+
+ not_supported_on :opal do
+ it "returns the offset for multi byte strings with unicode regexp" do
+ match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.")
+ match_data.begin(0).should == 1
+ match_data.begin(2).should == 2
+ end
+ end
+end
diff --git a/spec/ruby/core/matchdata/captures_spec.rb b/spec/ruby/core/matchdata/captures_spec.rb
new file mode 100644
index 0000000000..8c0d2978b7
--- /dev/null
+++ b/spec/ruby/core/matchdata/captures_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "MatchData#captures" do
+ it "returns an array of the match captures" do
+ /(.)(.)(\d+)(\d)/.match("THX1138.").captures.should == ["H","X","113","8"]
+ end
+end
diff --git a/spec/ruby/core/matchdata/element_reference_spec.rb b/spec/ruby/core/matchdata/element_reference_spec.rb
new file mode 100644
index 0000000000..1f02ef235b
--- /dev/null
+++ b/spec/ruby/core/matchdata/element_reference_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../../spec_helper'
+
+describe "MatchData#[]" do
+ it "acts as normal array indexing [index]" do
+ md = /(.)(.)(\d+)(\d)/.match("THX1138.")
+
+ md[0].should == 'HX1138'
+ md[1].should == 'H'
+ md[2].should == 'X'
+ md[-3].should == 'X'
+ md[10000].should == nil
+ md[-10000].should == nil
+ end
+
+ it "supports accessors [start, length]" do
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[1, 2].should == %w|H X|
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[-3, 2].should == %w|X 113|
+ end
+
+ it "supports ranges [start..end]" do
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[1..3].should == %w|H X 113|
+ end
+end
+
+describe "MatchData#[Symbol]" do
+ it "returns the corresponding named match when given a Symbol" do
+ md = 'haystack'.match(/(?<t>t(?<a>ack))/)
+ md[:a].should == 'ack'
+ md[:t].should == 'tack'
+ end
+
+ it "returns the corresponding named match when given a String" do
+ md = 'haystack'.match(/(?<t>t(?<a>ack))/)
+ md['a'].should == 'ack'
+ md['t'].should == 'tack'
+ end
+
+ it "returns the matching version of multiple corresponding named match" do
+ regexp = /(?:
+ A(?<word>\w+)
+ |
+ B(?<word>\w+)
+ )/x
+ md_a = regexp.match("Afoo")
+ md_b = regexp.match("Bfoo")
+
+ md_a[:word].should == "foo"
+ md_b[:word].should == "foo"
+
+ md_a['word'].should == "foo"
+ md_b['word'].should == "foo"
+ end
+
+ it "returns the last match when multiple named matches exist with the same name" do
+ md = /(?<word>hay)(?<word>stack)/.match('haystack')
+ md[:word].should == "stack"
+ md['word'].should == "stack"
+ end
+
+ it "returns nil on non-matching named matches" do
+ regexp = /(?<foo>foo )?(?<bar>bar)/
+ full_match = regexp.match("foo bar")
+ partial_match = regexp.match("bar")
+
+ full_match[:foo].should == "foo "
+ partial_match[:foo].should == nil
+
+ full_match['foo'].should == "foo "
+ partial_match['foo'].should == nil
+ end
+
+ it "raises an IndexError if there is no named match corresponding to the Symbol" do
+ md = 'haystack'.match(/(?<t>t(?<a>ack))/)
+ lambda { md[:baz] }.should raise_error(IndexError, /baz/)
+ end
+
+ it "raises an IndexError if there is no named match corresponding to the String" do
+ md = 'haystack'.match(/(?<t>t(?<a>ack))/)
+ lambda { md['baz'] }.should raise_error(IndexError, /baz/)
+ end
+
+ it "returns matches in the String's encoding" do
+ rex = /(?<t>t(?<a>ack))/u
+ md = 'haystack'.force_encoding('euc-jp').match(rex)
+ md[:t].encoding.should == Encoding::EUC_JP
+ end
+end
diff --git a/spec/ruby/core/matchdata/end_spec.rb b/spec/ruby/core/matchdata/end_spec.rb
new file mode 100644
index 0000000000..f6f3e1a281
--- /dev/null
+++ b/spec/ruby/core/matchdata/end_spec.rb
@@ -0,0 +1,30 @@
+# -*- encoding: utf-8 -*-
+
+require_relative '../../spec_helper'
+
+describe "MatchData#end" do
+ it "returns the offset of the end of the nth element" do
+ match_data = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ match_data.end(0).should == 7
+ match_data.end(2).should == 3
+ end
+
+ it "returns nil when the nth match isn't found" do
+ match_data = /something is( not)? (right)/.match("something is right")
+ match_data.end(1).should be_nil
+ end
+
+ it "returns the offset for multi byte strings" do
+ match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.")
+ match_data.end(0).should == 7
+ match_data.end(2).should == 3
+ end
+
+ not_supported_on :opal do
+ it "returns the offset for multi byte strings with unicode regexp" do
+ match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.")
+ match_data.end(0).should == 7
+ match_data.end(2).should == 3
+ end
+ end
+end
diff --git a/spec/ruby/core/matchdata/eql_spec.rb b/spec/ruby/core/matchdata/eql_spec.rb
new file mode 100644
index 0000000000..1d9666ebe1
--- /dev/null
+++ b/spec/ruby/core/matchdata/eql_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/eql'
+
+describe "MatchData#eql?" do
+ it_behaves_like :matchdata_eql, :eql?
+end
diff --git a/spec/ruby/core/matchdata/equal_value_spec.rb b/spec/ruby/core/matchdata/equal_value_spec.rb
new file mode 100644
index 0000000000..a58f1277e4
--- /dev/null
+++ b/spec/ruby/core/matchdata/equal_value_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/eql'
+
+describe "MatchData#==" do
+ it_behaves_like :matchdata_eql, :==
+end
diff --git a/spec/ruby/core/matchdata/hash_spec.rb b/spec/ruby/core/matchdata/hash_spec.rb
new file mode 100644
index 0000000000..cef18fdd20
--- /dev/null
+++ b/spec/ruby/core/matchdata/hash_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "MatchData#hash" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/matchdata/inspect_spec.rb b/spec/ruby/core/matchdata/inspect_spec.rb
new file mode 100644
index 0000000000..5315257677
--- /dev/null
+++ b/spec/ruby/core/matchdata/inspect_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+
+describe "MatchData#inspect" do
+ before :each do
+ @match_data = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ end
+
+ it "returns a String" do
+ @match_data.inspect.should be_kind_of(String)
+ end
+
+ it "returns a human readable representation that contains entire matched string and the captures" do
+ # yeah, hardcoding the inspect output is not ideal, but in this case
+ # it makes perfect sense. See JRUBY-4558 for example.
+ @match_data.inspect.should == '#<MatchData "HX1138" 1:"H" 2:"X" 3:"113" 4:"8">'
+ end
+
+ it "returns a human readable representation of named captures" do
+ match_data = "abc def ghi".match(/(?<first>\w+)\s+(?<last>\w+)\s+(\w+)/)
+
+ match_data.inspect.should == '#<MatchData "abc def ghi" first:"abc" last:"def">'
+ end
+end
diff --git a/spec/ruby/core/matchdata/length_spec.rb b/spec/ruby/core/matchdata/length_spec.rb
new file mode 100644
index 0000000000..39df36df4b
--- /dev/null
+++ b/spec/ruby/core/matchdata/length_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/length'
+
+describe "MatchData#length" do
+ it_behaves_like :matchdata_length, :length
+end
diff --git a/spec/ruby/core/matchdata/named_captures_spec.rb b/spec/ruby/core/matchdata/named_captures_spec.rb
new file mode 100644
index 0000000000..588b607a44
--- /dev/null
+++ b/spec/ruby/core/matchdata/named_captures_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '2.4' do
+ describe 'MatchData#named_captures' do
+ it 'returns a Hash that has captured name and the matched string pairs' do
+ /(?<a>.)(?<b>.)?/.match('0').named_captures.should == { 'a' => '0', 'b' => nil }
+ end
+
+ it 'prefers later captures' do
+ /\A(?<a>.)(?<b>.)(?<b>.)(?<a>.)\z/.match('0123').named_captures.should == { 'a' => '3', 'b' => '2' }
+ end
+ end
+end
diff --git a/spec/ruby/core/matchdata/names_spec.rb b/spec/ruby/core/matchdata/names_spec.rb
new file mode 100644
index 0000000000..25ca06ced9
--- /dev/null
+++ b/spec/ruby/core/matchdata/names_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+
+describe "MatchData#names" do
+ it "returns an Array" do
+ md = 'haystack'.match(/(?<yellow>hay)/)
+ md.names.should be_an_instance_of(Array)
+ end
+
+ it "sets each element to a String" do
+ 'haystack'.match(/(?<yellow>hay)/).names.all? do |e|
+ e.should be_an_instance_of(String)
+ end
+ end
+
+ it "returns the names of the named capture groups" do
+ md = 'haystack'.match(/(?<yellow>hay).(?<pin>tack)/)
+ md.names.should == ['yellow', 'pin']
+ end
+
+ it "returns [] if there were no named captures" do
+ 'haystack'.match(/(hay).(tack)/).names.should == []
+ end
+
+ it "returns each name only once" do
+ md = 'haystack'.match(/(?<hay>hay)(?<dot>.)(?<hay>tack)/)
+ md.names.should == ['hay', 'dot']
+ end
+
+ it "equals Regexp#names" do
+ r = /(?<hay>hay)(?<dot>.)(?<hay>tack)/
+ 'haystack'.match(r).names.should == r.names
+ end
+end
diff --git a/spec/ruby/core/matchdata/offset_spec.rb b/spec/ruby/core/matchdata/offset_spec.rb
new file mode 100644
index 0000000000..1ccb54b7a7
--- /dev/null
+++ b/spec/ruby/core/matchdata/offset_spec.rb
@@ -0,0 +1,30 @@
+# -*- encoding: utf-8 -*-
+
+require_relative '../../spec_helper'
+
+describe "MatchData#offset" do
+ it "returns a two element array with the begin and end of the nth match" do
+ match_data = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ match_data.offset(0).should == [1, 7]
+ match_data.offset(4).should == [6, 7]
+ end
+
+ it "returns [nil, nil] when the nth match isn't found" do
+ match_data = /something is( not)? (right)/.match("something is right")
+ match_data.offset(1).should == [nil, nil]
+ end
+
+ it "returns the offset for multi byte strings" do
+ match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.")
+ match_data.offset(0).should == [1, 7]
+ match_data.offset(4).should == [6, 7]
+ end
+
+ not_supported_on :opal do
+ it "returns the offset for multi byte strings with unicode regexp" do
+ match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.")
+ match_data.offset(0).should == [1, 7]
+ match_data.offset(4).should == [6, 7]
+ end
+ end
+end
diff --git a/spec/ruby/core/matchdata/post_match_spec.rb b/spec/ruby/core/matchdata/post_match_spec.rb
new file mode 100644
index 0000000000..43e25561af
--- /dev/null
+++ b/spec/ruby/core/matchdata/post_match_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+
+describe "MatchData#post_match" do
+ it "returns the string after the match equiv. special var $'" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").post_match.should == ': The Movie'
+ $'.should == ': The Movie'
+ end
+
+ it "keeps taint status from the source string" do
+ str = "THX1138: The Movie"
+ str.taint
+ res = /(.)(.)(\d+)(\d)/.match(str).post_match
+ res.tainted?.should be_true
+ $'.tainted?.should be_true
+ end
+
+ it "keeps untrusted status from the source string" do
+ str = "THX1138: The Movie"
+ str.untrust
+ res = /(.)(.)(\d+)(\d)/.match(str).post_match
+ res.untrusted?.should be_true
+ $'.untrusted?.should be_true
+ end
+
+ with_feature :encoding do
+ it "sets the encoding to the encoding of the source String" do
+ str = "abc".force_encoding Encoding::EUC_JP
+ str.match(/b/).post_match.encoding.should equal(Encoding::EUC_JP)
+ end
+
+ it "sets an empty result to the encoding of the source String" do
+ str = "abc".force_encoding Encoding::ISO_8859_1
+ str.match(/c/).post_match.encoding.should equal(Encoding::ISO_8859_1)
+ end
+ end
+end
diff --git a/spec/ruby/core/matchdata/pre_match_spec.rb b/spec/ruby/core/matchdata/pre_match_spec.rb
new file mode 100644
index 0000000000..f71920354c
--- /dev/null
+++ b/spec/ruby/core/matchdata/pre_match_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+
+describe "MatchData#pre_match" do
+ it "returns the string before the match, equiv. special var $`" do
+ /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").pre_match.should == 'T'
+ $`.should == 'T'
+ end
+
+ it "keeps taint status from the source string" do
+ str = "THX1138: The Movie"
+ str.taint
+ res = /(.)(.)(\d+)(\d)/.match(str).pre_match
+ res.tainted?.should be_true
+ $`.tainted?.should be_true
+ end
+
+ it "keeps untrusted status from the source string" do
+ str = "THX1138: The Movie"
+ str.untrust
+ res = /(.)(.)(\d+)(\d)/.match(str).pre_match
+ res.untrusted?.should be_true
+ $`.untrusted?.should be_true
+ end
+
+ with_feature :encoding do
+ it "sets the encoding to the encoding of the source String" do
+ str = "abc".force_encoding Encoding::EUC_JP
+ str.match(/b/).pre_match.encoding.should equal(Encoding::EUC_JP)
+ end
+
+ it "sets an empty result to the encoding of the source String" do
+ str = "abc".force_encoding Encoding::ISO_8859_1
+ str.match(/a/).pre_match.encoding.should equal(Encoding::ISO_8859_1)
+ end
+ end
+end
diff --git a/spec/ruby/core/matchdata/regexp_spec.rb b/spec/ruby/core/matchdata/regexp_spec.rb
new file mode 100644
index 0000000000..05a8e80567
--- /dev/null
+++ b/spec/ruby/core/matchdata/regexp_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+describe "MatchData#regexp" do
+ it "returns a Regexp object" do
+ m = 'haystack'.match(/hay/)
+ m.regexp.should be_an_instance_of(Regexp)
+ end
+
+ it "returns the pattern used in the match" do
+ m = 'haystack'.match(/hay/)
+ m.regexp.should == /hay/
+ end
+end
diff --git a/spec/ruby/core/matchdata/shared/eql.rb b/spec/ruby/core/matchdata/shared/eql.rb
new file mode 100644
index 0000000000..e021baa178
--- /dev/null
+++ b/spec/ruby/core/matchdata/shared/eql.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+
+describe :matchdata_eql, shared: true do
+ it "returns true if both operands have equal target strings, patterns, and match positions" do
+ a = 'haystack'.match(/hay/)
+ b = 'haystack'.match(/hay/)
+ a.send(@method, b).should be_true
+ end
+
+ it "returns false if the operands have different target strings" do
+ a = 'hay'.match(/hay/)
+ b = 'haystack'.match(/hay/)
+ a.send(@method, b).should be_false
+ end
+
+ it "returns false if the operands have different patterns" do
+ a = 'haystack'.match(/h.y/)
+ b = 'haystack'.match(/hay/)
+ a.send(@method, b).should be_false
+ end
+
+ it "returns false if the argument is not a MatchData object" do
+ a = 'haystack'.match(/hay/)
+ a.send(@method, Object.new).should be_false
+ end
+end
diff --git a/spec/ruby/core/matchdata/shared/length.rb b/spec/ruby/core/matchdata/shared/length.rb
new file mode 100644
index 0000000000..6312a7ed4c
--- /dev/null
+++ b/spec/ruby/core/matchdata/shared/length.rb
@@ -0,0 +1,5 @@
+describe :matchdata_length, shared: true do
+ it "length should return the number of elements in the match array" do
+ /(.)(.)(\d+)(\d)/.match("THX1138.").send(@method).should == 5
+ end
+end
diff --git a/spec/ruby/core/matchdata/size_spec.rb b/spec/ruby/core/matchdata/size_spec.rb
new file mode 100644
index 0000000000..b4965db3b8
--- /dev/null
+++ b/spec/ruby/core/matchdata/size_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/length'
+
+describe "MatchData#size" do
+ it_behaves_like :matchdata_length, :size
+end
diff --git a/spec/ruby/core/matchdata/string_spec.rb b/spec/ruby/core/matchdata/string_spec.rb
new file mode 100644
index 0000000000..ff1b4eab5b
--- /dev/null
+++ b/spec/ruby/core/matchdata/string_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe "MatchData#string" do
+ it "returns a copy of the match string" do
+ str = /(.)(.)(\d+)(\d)/.match("THX1138.").string
+ str.should == "THX1138."
+ end
+
+ it "returns a frozen copy of the match string" do
+ str = /(.)(.)(\d+)(\d)/.match("THX1138.").string
+ str.should == "THX1138."
+ str.frozen?.should == true
+ end
+end
diff --git a/spec/ruby/core/matchdata/to_a_spec.rb b/spec/ruby/core/matchdata/to_a_spec.rb
new file mode 100644
index 0000000000..6231d096fb
--- /dev/null
+++ b/spec/ruby/core/matchdata/to_a_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "MatchData#to_a" do
+ it "returns an array of matches" do
+ /(.)(.)(\d+)(\d)/.match("THX1138.").to_a.should == ["HX1138", "H", "X", "113", "8"]
+ end
+end
diff --git a/spec/ruby/core/matchdata/to_s_spec.rb b/spec/ruby/core/matchdata/to_s_spec.rb
new file mode 100644
index 0000000000..9e213bb342
--- /dev/null
+++ b/spec/ruby/core/matchdata/to_s_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "MatchData#to_s" do
+ it "returns the entire matched string" do
+ /(.)(.)(\d+)(\d)/.match("THX1138.").to_s.should == "HX1138"
+ end
+end
diff --git a/spec/ruby/core/matchdata/values_at_spec.rb b/spec/ruby/core/matchdata/values_at_spec.rb
new file mode 100644
index 0000000000..af844904f6
--- /dev/null
+++ b/spec/ruby/core/matchdata/values_at_spec.rb
@@ -0,0 +1,23 @@
+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"]
+ 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"]
+ end
+ end
+
+ ruby_version_is '2.4' do
+ it 'slices captures with the given names' do
+ /(?<a>.)(?<b>.)(?<c>.)/.match('012').values_at(:c, :a).should == ['2', '0']
+ end
+
+ it 'takes names and indices' do
+ /\A(?<a>.)(?<b>.)\z/.match('01').values_at(0, 1, 2, :a, :b).should == ['01', '0', '1', '0', '1']
+ end
+ end
+end
diff --git a/spec/ruby/core/math/acos_spec.rb b/spec/ruby/core/math/acos_spec.rb
new file mode 100644
index 0000000000..0f104b8efc
--- /dev/null
+++ b/spec/ruby/core/math/acos_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# arccosine : (-1.0, 1.0) --> (0, PI)
+describe "Math.acos" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "returns a float" do
+ Math.acos(1).should be_kind_of(Float )
+ end
+
+ it "returns the arccosine of the argument" do
+ Math.acos(1).should be_close(0.0, TOLERANCE)
+ Math.acos(0).should be_close(1.5707963267949, TOLERANCE)
+ Math.acos(-1).should be_close(Math::PI,TOLERANCE)
+ Math.acos(0.25).should be_close(1.31811607165282, TOLERANCE)
+ Math.acos(0.50).should be_close(1.0471975511966 , TOLERANCE)
+ Math.acos(0.75).should be_close(0.722734247813416, TOLERANCE)
+ end
+
+ it "raises an Math::DomainError if the argument is greater than 1.0" do
+ lambda { Math.acos(1.0001) }.should raise_error(Math::DomainError)
+ end
+
+ it "raises an Math::DomainError if the argument is less than -1.0" do
+ lambda { Math.acos(-1.0001) }.should raise_error(Math::DomainError)
+ end
+
+ it "raises a TypeError if the string argument cannot be coerced with Float()" do
+ lambda { Math.acos("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.acos(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.acos(MathSpecs::UserClass.new) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.acos(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.acos(MathSpecs::Float.new(0.5)).should be_close(Math.acos(0.5), TOLERANCE)
+ end
+end
+
+describe "Math#acos" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:acos, 0).should be_close(1.5707963267949, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/acosh_spec.rb b/spec/ruby/core/math/acosh_spec.rb
new file mode 100644
index 0000000000..26c18a530f
--- /dev/null
+++ b/spec/ruby/core/math/acosh_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Math.acosh" do
+ it "returns a float" do
+ Math.acosh(1.0).should be_kind_of(Float)
+ end
+
+ it "returns the principle value of the inverse hyperbolic cosine of the argument" do
+ Math.acosh(14.2).should be_close(3.345146999647, TOLERANCE)
+ Math.acosh(1.0).should be_close(0.0, TOLERANCE)
+ end
+
+ it "raises Math::DomainError if the passed argument is less than -1.0 or greater than 1.0" do
+ lambda { Math.acosh(1.0 - TOLERANCE) }.should raise_error(Math::DomainError)
+ lambda { Math.acosh(0) }.should raise_error(Math::DomainError)
+ lambda { Math.acosh(-1.0) }.should raise_error(Math::DomainError)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.acosh("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.acosh(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.acosh(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.acosh(MathSpecs::Float.new).should == 0.0
+ end
+end
+
+describe "Math#acosh" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:acosh, 1.0).should be_close(0.0, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/asin_spec.rb b/spec/ruby/core/math/asin_spec.rb
new file mode 100644
index 0000000000..8591ea80ea
--- /dev/null
+++ b/spec/ruby/core/math/asin_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# arcsine : (-1.0, 1.0) --> (-PI/2, PI/2)
+describe "Math.asin" do
+ it "returns a float" do
+ Math.asin(1).should be_kind_of(Float)
+ end
+
+ it "returns the arcsine of the argument" do
+ Math.asin(1).should be_close(Math::PI/2, TOLERANCE)
+ Math.asin(0).should be_close(0.0, TOLERANCE)
+ Math.asin(-1).should be_close(-Math::PI/2, TOLERANCE)
+ Math.asin(0.25).should be_close(0.252680255142079, TOLERANCE)
+ Math.asin(0.50).should be_close(0.523598775598299, TOLERANCE)
+ Math.asin(0.75).should be_close(0.8480620789814816,TOLERANCE)
+ end
+
+ it "raises an Math::DomainError if the argument is greater than 1.0" do
+ lambda { Math.asin(1.0001) }.should raise_error( Math::DomainError)
+ end
+
+ it "raises an Math::DomainError if the argument is less than -1.0" do
+ lambda { Math.asin(-1.0001) }.should raise_error( Math::DomainError)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.asin("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.asin(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.asin(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.asin(MathSpecs::Float.new).should be_close(1.5707963267949, TOLERANCE)
+ end
+end
+
+describe "Math#asin" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:asin, 0.5).should be_close(0.523598775598299, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/asinh_spec.rb b/spec/ruby/core/math/asinh_spec.rb
new file mode 100644
index 0000000000..66a2beaa36
--- /dev/null
+++ b/spec/ruby/core/math/asinh_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Math.asinh" do
+ it "returns a float" do
+ Math.asinh(1.5).should be_kind_of(Float)
+ end
+
+ it "returns the inverse hyperbolic sin of the argument" do
+ Math.asinh(1.5).should be_close(1.19476321728711, TOLERANCE)
+ Math.asinh(-2.97).should be_close(-1.8089166921397, TOLERANCE)
+ Math.asinh(0.0).should == 0.0
+ Math.asinh(-0.0).should == -0.0
+ Math.asinh(1.05367e-08).should be_close(1.05367e-08, TOLERANCE)
+ Math.asinh(-1.05367e-08).should be_close(-1.05367e-08, TOLERANCE)
+ # Default tolerance does not scale right for these...
+ #Math.asinh(94906265.62).should be_close(19.0615, TOLERANCE)
+ #Math.asinh(-94906265.62).should be_close(-19.0615, TOLERANCE)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.asinh("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.asinh(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.asinh(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.asinh(MathSpecs::Float.new).should be_close(0.881373587019543, TOLERANCE)
+ end
+end
+
+describe "Math#asinh" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:asinh, 19.275).should be_close(3.65262832292466, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/atan2_spec.rb b/spec/ruby/core/math/atan2_spec.rb
new file mode 100644
index 0000000000..da1b3476cc
--- /dev/null
+++ b/spec/ruby/core/math/atan2_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Math.atan2" do
+ it "returns a float" do
+ Math.atan2(1.2, 0.5).should be_kind_of(Float)
+ end
+
+ it "returns the arc tangent of y, x" do
+ Math.atan2(4.2, 0.3).should be_close(1.49948886200961, TOLERANCE)
+ Math.atan2(0.0, 1.0).should be_close(0.0, TOLERANCE)
+ Math.atan2(-9.1, 3.2).should be_close(-1.23265379809025, TOLERANCE)
+ Math.atan2(7.22, -3.3).should be_close(1.99950888779256, TOLERANCE)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.atan2(1.0, "test") }.should raise_error(TypeError)
+ lambda { Math.atan2("test", 0.0) }.should raise_error(TypeError)
+ lambda { Math.atan2("test", "this") }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.atan2(nil, 1.0) }.should raise_error(TypeError)
+ lambda { Math.atan2(-1.0, nil) }.should raise_error(TypeError)
+ lambda { Math.atan2(nil, nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.atan2(MathSpecs::Float.new, MathSpecs::Float.new).should be_close(0.785398163397448, TOLERANCE)
+ end
+
+ it "returns positive zero when passed 0.0, 0.0" do
+ Math.atan2(0.0, 0.0).should be_positive_zero
+ end
+
+ it "returns negative zero when passed -0.0, 0.0" do
+ Math.atan2(-0.0, 0.0).should be_negative_zero
+ end
+
+ it "returns Pi when passed 0.0, -0.0" do
+ Math.atan2(0.0, -0.0).should == Math::PI
+ end
+
+ it "returns -Pi when passed -0.0, -0.0" do
+ Math.atan2(-0.0, -0.0).should == -Math::PI
+ end
+
+end
+
+describe "Math#atan2" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:atan2, 1.1, 2.2).should be_close(0.463647609000806, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/atan_spec.rb b/spec/ruby/core/math/atan_spec.rb
new file mode 100644
index 0000000000..9a9791266b
--- /dev/null
+++ b/spec/ruby/core/math/atan_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# arctangent : (-Inf, Inf) --> (-PI/2, PI/2)
+describe "Math.atan" do
+ it "returns a float" do
+ Math.atan(1).should be_kind_of(Float)
+ end
+
+ it "returns the arctangent of the argument" do
+ Math.atan(1).should be_close(Math::PI/4, TOLERANCE)
+ Math.atan(0).should be_close(0.0, TOLERANCE)
+ Math.atan(-1).should be_close(-Math::PI/4, TOLERANCE)
+ Math.atan(0.25).should be_close(0.244978663126864, TOLERANCE)
+ Math.atan(0.50).should be_close(0.463647609000806, TOLERANCE)
+ Math.atan(0.75).should be_close(0.643501108793284, TOLERANCE)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.atan("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.atan(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.atan(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.atan(MathSpecs::Float.new).should be_close(0.785398163397448, TOLERANCE)
+ end
+end
+
+describe "Math#atan" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:atan, 3.1415).should be_close(1.2626187313511, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/atanh_spec.rb b/spec/ruby/core/math/atanh_spec.rb
new file mode 100644
index 0000000000..21fb209941
--- /dev/null
+++ b/spec/ruby/core/math/atanh_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/math/common'
+require_relative '../../shared/math/atanh'
+
+describe "Math.atanh" do
+ it_behaves_like :math_atanh_base, :atanh, Math
+ it_behaves_like :math_atanh_no_complex, :atanh, Math
+end
+
+describe "Math#atanh" do
+ it_behaves_like :math_atanh_private, :atanh
+ it_behaves_like :math_atanh_base, :atanh, IncludesMath.new
+ it_behaves_like :math_atanh_no_complex, :atanh, IncludesMath.new
+end
diff --git a/spec/ruby/core/math/cbrt_spec.rb b/spec/ruby/core/math/cbrt_spec.rb
new file mode 100644
index 0000000000..8c96243658
--- /dev/null
+++ b/spec/ruby/core/math/cbrt_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Math.cbrt" do
+ it "returns a float" do
+ Math.cbrt(1).should be_an_instance_of(Float)
+ end
+
+ it "returns the cubic root of the argument" do
+ Math.cbrt(1).should == 1.0
+ Math.cbrt(8.0).should == 2.0
+ Math.cbrt(-8.0).should == -2.0
+ Math.cbrt(3).should be_close(1.44224957030741, TOLERANCE)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.cbrt("foobar") }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.cbrt(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.cbrt(MathSpecs::Float.new).should be_close(1.0, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/constants_spec.rb b/spec/ruby/core/math/constants_spec.rb
new file mode 100644
index 0000000000..b500b21a79
--- /dev/null
+++ b/spec/ruby/core/math/constants_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Math::PI" do
+ it "approximates the value of pi" do
+ Math::PI.should be_close(3.14159_26535_89793_23846, TOLERANCE)
+ end
+
+ it "is accessible to a class that includes Math" do
+ IncludesMath::PI.should == Math::PI
+ end
+end
+
+describe "Math::E" do
+ it "approximates the value of Napier's constant" do
+ Math::E.should be_close(2.71828_18284_59045_23536, TOLERANCE)
+ end
+
+ it "is accessible to a class that includes Math" do
+ IncludesMath::E.should == Math::E
+ end
+end
diff --git a/spec/ruby/core/math/cos_spec.rb b/spec/ruby/core/math/cos_spec.rb
new file mode 100644
index 0000000000..cd27b99794
--- /dev/null
+++ b/spec/ruby/core/math/cos_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# cosine : (-Inf, Inf) --> (-1.0, 1.0)
+describe "Math.cos" do
+ it "returns a float" do
+ Math.cos(Math::PI).should be_kind_of(Float)
+ end
+
+ it "returns the cosine of the argument expressed in radians" do
+ Math.cos(Math::PI).should be_close(-1.0, TOLERANCE)
+ Math.cos(0).should be_close(1.0, TOLERANCE)
+ Math.cos(Math::PI/2).should be_close(0.0, TOLERANCE)
+ Math.cos(3*Math::PI/2).should be_close(0.0, TOLERANCE)
+ Math.cos(2*Math::PI).should be_close(1.0, TOLERANCE)
+ end
+
+
+ it "raises a TypeError unless the argument is Numeric and has #to_f" do
+ lambda { Math.cos("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.cos(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.cos(nil) }.should raise_error(TypeError)
+ end
+
+ it "coerces its argument with #to_f" do
+ f = mock_numeric('8.2')
+ f.should_receive(:to_f).and_return(8.2)
+ Math.cos(f).should == Math.cos(8.2)
+ end
+end
+
+describe "Math#cos" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:cos, 3.1415).should be_close(-0.999999995707656, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/cosh_spec.rb b/spec/ruby/core/math/cosh_spec.rb
new file mode 100644
index 0000000000..14d4c2fe0f
--- /dev/null
+++ b/spec/ruby/core/math/cosh_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Math.cosh" do
+ it "returns a float" do
+ Math.cosh(1.0).should be_kind_of(Float)
+ end
+
+ it "returns the hyperbolic cosine of the argument" do
+ Math.cosh(0.0).should == 1.0
+ Math.cosh(-0.0).should == 1.0
+ Math.cosh(1.5).should be_close(2.35240961524325, TOLERANCE)
+ Math.cosh(-2.99).should be_close(9.96798496414416, TOLERANCE)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.cosh("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.cosh(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.cosh(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.cosh(MathSpecs::Float.new).should be_close(1.54308063481524, TOLERANCE)
+ end
+end
+
+describe "Math#cosh" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:cos, 3.1415).should be_close(-0.999999995707656, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/erf_spec.rb b/spec/ruby/core/math/erf_spec.rb
new file mode 100644
index 0000000000..96757e412d
--- /dev/null
+++ b/spec/ruby/core/math/erf_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# erf method is the "error function" encountered in integrating the normal
+# distribution (which is a normalized form of the Gaussian function).
+describe "Math.erf" do
+ it "returns a float" do
+ Math.erf(1).should be_kind_of(Float)
+ end
+
+ it "returns the error function of the argument" do
+ Math.erf(0).should be_close(0.0, TOLERANCE)
+ Math.erf(1).should be_close(0.842700792949715, TOLERANCE)
+ Math.erf(-1).should be_close(-0.842700792949715, TOLERANCE)
+ Math.erf(0.5).should be_close(0.520499877813047, TOLERANCE)
+ Math.erf(-0.5).should be_close(-0.520499877813047, TOLERANCE)
+ Math.erf(10000).should be_close(1.0, TOLERANCE)
+ Math.erf(-10000).should be_close(-1.0, TOLERANCE)
+ Math.erf(0.00000000000001).should be_close(0.0, TOLERANCE)
+ Math.erf(-0.00000000000001).should be_close(0.0, TOLERANCE)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.erf("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.erf(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.erf(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.erf(MathSpecs::Float.new).should be_close(0.842700792949715, TOLERANCE)
+ end
+end
+
+describe "Math#erf" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:erf, 3.1415).should be_close(0.999991118444483, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/erfc_spec.rb b/spec/ruby/core/math/erfc_spec.rb
new file mode 100644
index 0000000000..eca23eaf7c
--- /dev/null
+++ b/spec/ruby/core/math/erfc_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# erfc is the complementary error function
+describe "Math.erfc" do
+ it "returns a float" do
+ Math.erf(1).should be_kind_of(Float)
+ end
+
+ it "returns the complementary error function of the argument" do
+ Math.erfc(0).should be_close(1.0, TOLERANCE)
+ Math.erfc(1).should be_close(0.157299207050285, TOLERANCE)
+ Math.erfc(-1).should be_close(1.84270079294971, TOLERANCE)
+ Math.erfc(0.5).should be_close(0.479500122186953, TOLERANCE)
+ Math.erfc(-0.5).should be_close(1.52049987781305, TOLERANCE)
+ Math.erfc(10000).should be_close(0.0, TOLERANCE)
+ Math.erfc(-10000).should be_close(2.0, TOLERANCE)
+ Math.erfc(0.00000000000001).should be_close(0.999999999999989, TOLERANCE)
+ Math.erfc(-0.00000000000001).should be_close(1.00000000000001, TOLERANCE)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.erfc("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.erfc(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.erfc(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.erfc(MathSpecs::Float.new).should be_close(0.157299207050285, TOLERANCE)
+ end
+end
+
+describe "Math#erfc" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:erf, 3.1415).should be_close(0.999991118444483, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/exp_spec.rb b/spec/ruby/core/math/exp_spec.rb
new file mode 100644
index 0000000000..d97550dc4e
--- /dev/null
+++ b/spec/ruby/core/math/exp_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Math.exp" do
+ it "returns a float" do
+ Math.exp(1.0).should be_kind_of(Float)
+ end
+
+ it "returns the base-e exponential of the argument" do
+ Math.exp(0.0).should == 1.0
+ Math.exp(-0.0).should == 1.0
+ Math.exp(-1.8).should be_close(0.165298888221587, TOLERANCE)
+ Math.exp(1.25).should be_close(3.49034295746184, TOLERANCE)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.exp("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.exp(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.exp(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.exp(MathSpecs::Float.new).should be_close(Math::E, TOLERANCE)
+ end
+end
+
+describe "Math#exp" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:exp, 23.1415).should be_close(11226018484.0012, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/fixtures/classes.rb b/spec/ruby/core/math/fixtures/classes.rb
new file mode 100644
index 0000000000..6f2241e739
--- /dev/null
+++ b/spec/ruby/core/math/fixtures/classes.rb
@@ -0,0 +1,28 @@
+class IncludesMath
+ include Math
+end
+
+module MathSpecs
+ class Float < Numeric
+ def initialize(value=1.0)
+ @value = value
+ end
+
+ def to_f
+ @value
+ end
+ end
+
+ class Integer
+ def to_int
+ 2
+ end
+ end
+
+ class UserClass
+ end
+
+ class StringSubClass < String
+ end
+
+end
diff --git a/spec/ruby/core/math/frexp_spec.rb b/spec/ruby/core/math/frexp_spec.rb
new file mode 100644
index 0000000000..31b5b4079f
--- /dev/null
+++ b/spec/ruby/core/math/frexp_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Math.frexp" do
+ it "returns the normalized fraction and exponent" do
+ frac, exp = Math.frexp(102.83)
+ frac.should be_close(0.803359375, TOLERANCE)
+ exp.should == 7
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.frexp("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ frac, _exp = Math.frexp(nan_value)
+ frac.nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.frexp(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ frac, exp = Math.frexp(MathSpecs::Float.new)
+ frac.should be_close(0.5, TOLERANCE)
+ exp.should == 1
+ end
+end
+
+describe "Math#frexp" do
+ it "is accessible as a private instance method" do
+ frac, exp = IncludesMath.new.send(:frexp, 2.1415)
+ frac.should be_close(0.535375, TOLERANCE)
+ exp.should == 2
+ end
+end
diff --git a/spec/ruby/core/math/gamma_spec.rb b/spec/ruby/core/math/gamma_spec.rb
new file mode 100644
index 0000000000..ce7f82421f
--- /dev/null
+++ b/spec/ruby/core/math/gamma_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../../spec_helper'
+
+describe "Math.gamma" do
+ it "returns +infinity given 0" do
+ Math.gamma(0).should == Float::INFINITY
+ end
+
+ platform_is_not :windows do
+ # https://bugs.ruby-lang.org/issues/12249
+ it "returns -infinity given -0.0" do
+ Math.gamma(-0.0).should == -Float::INFINITY
+ end
+ end
+
+ it "returns Math.sqrt(Math::PI) given 0.5" do
+ Math.gamma(0.5).should be_close(Math.sqrt(Math::PI), TOLERANCE)
+ end
+
+ # stop at n == 23 because 23! cannot be exactly represented by IEEE 754 double
+ it "returns exactly (n-1)! given n for n between 2 and 23" do
+ fact = 1
+ 2.upto(23) do |n|
+ fact *= (n - 1)
+ Math.gamma(n).should == fact
+ end
+ end
+
+ it "returns approximately (n-1)! given n for n between 24 and 30" do
+ fact2 = 1124000727777607680000 # 22!
+ 24.upto(30) do |n|
+ fact2 *= n - 1
+ # compare only the first 12 places, tolerate the rest
+ Math.gamma(n).should be_close(fact2, fact2.to_s[12..-1].to_i)
+ end
+ end
+
+ it "returns good numerical approximation for gamma(3.2)" do
+ Math.gamma(3.2).should be_close(2.423965, TOLERANCE)
+ end
+
+ it "returns good numerical approximation for gamma(-2.15)" do
+ Math.gamma(-2.15).should be_close(-2.999619, TOLERANCE)
+ end
+
+ it "returns good numerical approximation for gamma(0.00001)" do
+ Math.gamma(0.00001).should be_close(99999.422794, TOLERANCE)
+ end
+
+ it "returns good numerical approximation for gamma(-0.00001)" do
+ Math.gamma(-0.00001).should be_close(-100000.577225, TOLERANCE)
+ end
+
+ it "raises Math::DomainError given -1" do
+ lambda { Math.gamma(-1) }.should raise_error(Math::DomainError)
+ end
+
+ # See https://bugs.ruby-lang.org/issues/10642
+ it "returns +infinity given +infinity" do
+ Math.gamma(infinity_value).infinite?.should == 1
+ end
+
+ it "raises Math::DomainError given negative infinity" do
+ lambda { Math.gamma(-Float::INFINITY) }.should raise_error(Math::DomainError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.gamma(nan_value).nan?.should be_true
+ end
+end
diff --git a/spec/ruby/core/math/hypot_spec.rb b/spec/ruby/core/math/hypot_spec.rb
new file mode 100644
index 0000000000..20badb2840
--- /dev/null
+++ b/spec/ruby/core/math/hypot_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Math.hypot" do
+ it "returns a float" do
+ Math.hypot(3, 4).should be_kind_of(Float)
+ end
+
+ it "returns the length of the hypotenuse of a right triangle with legs given by the arguments" do
+ Math.hypot(0, 0).should be_close(0.0, TOLERANCE)
+ Math.hypot(2, 10).should be_close( 10.1980390271856, TOLERANCE)
+ Math.hypot(5000, 5000).should be_close(7071.06781186548, TOLERANCE)
+ Math.hypot(0.0001, 0.0002).should be_close(0.000223606797749979, TOLERANCE)
+ Math.hypot(-2, -10).should be_close(10.1980390271856, TOLERANCE)
+ Math.hypot(2, 10).should be_close(10.1980390271856, TOLERANCE)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.hypot("test", "this") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.hypot(nan_value, 0).nan?.should be_true
+ Math.hypot(0, nan_value).nan?.should be_true
+ Math.hypot(nan_value, nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.hypot(nil, nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.hypot(MathSpecs::Float.new, MathSpecs::Float.new).should be_close(1.4142135623731, TOLERANCE)
+ end
+end
+
+describe "Math#hypot" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:hypot, 2, 3.1415).should be_close(3.72411361937307, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/ldexp_spec.rb b/spec/ruby/core/math/ldexp_spec.rb
new file mode 100644
index 0000000000..8602b33d43
--- /dev/null
+++ b/spec/ruby/core/math/ldexp_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Math.ldexp" do
+ it "returns a float" do
+ Math.ldexp(1.0, 2).should be_kind_of(Float)
+ end
+
+ it "returns the argument multiplied by 2**n" do
+ Math.ldexp(0.0, 0.0).should == 0.0
+ Math.ldexp(0.0, 1.0).should == 0.0
+ Math.ldexp(-1.25, 2).should be_close(-5.0, TOLERANCE)
+ Math.ldexp(2.1, -3).should be_close(0.2625, TOLERANCE)
+ Math.ldexp(5.7, 4).should be_close(91.2, TOLERANCE)
+ end
+
+ it "raises a TypeError if the first argument cannot be coerced with Float()" do
+ lambda { Math.ldexp("test", 2) }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.ldexp(nan_value, 0).nan?.should be_true
+ end
+
+ it "raises RangeError if NaN is given as the second arg" do
+ lambda { Math.ldexp(0, nan_value) }.should raise_error(RangeError)
+ end
+
+ it "raises a TypeError if the second argument cannot be coerced with Integer()" do
+ lambda { Math.ldexp(3.2, "this") }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the first argument is nil" do
+ lambda { Math.ldexp(nil, 2) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the second argument is nil" do
+ lambda { Math.ldexp(3.1, nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any first argument that can be coerced with Float()" do
+ Math.ldexp(MathSpecs::Float.new, 2).should be_close(4.0, TOLERANCE)
+ end
+
+ it "accepts any second argument that can be coerced with Integer()" do
+ Math.ldexp(3.23, MathSpecs::Integer.new).should be_close(12.92, TOLERANCE)
+ end
+end
+
+describe "Math#ldexp" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:ldexp, 3.1415, 2).should be_close(12.566, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/lgamma_spec.rb b/spec/ruby/core/math/lgamma_spec.rb
new file mode 100644
index 0000000000..7104f2aa21
--- /dev/null
+++ b/spec/ruby/core/math/lgamma_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../../spec_helper'
+
+describe "Math.lgamma" do
+ it "returns [Infinity, 1] when passed 0" do
+ Math.lgamma(0).should == [infinity_value, 1]
+ end
+
+ platform_is_not :windows do
+ it "returns [Infinity, 1] when passed -1" do
+ Math.lgamma(-1).should == [infinity_value, 1]
+ end
+ end
+
+ ruby_version_is "2.4" do
+ it "returns [Infinity, -1] when passed -0.0" do
+ Math.lgamma(-0.0).should == [infinity_value, -1]
+ end
+ end
+
+ it "returns [log(sqrt(PI)), 1] when passed 0.5" do
+ lg1 = Math.lgamma(0.5)
+ lg1[0].should be_close(Math.log(Math.sqrt(Math::PI)), TOLERANCE)
+ lg1[1].should == 1
+ end
+
+ it "returns [log(2/3*PI, 1] when passed 6.0" do
+ lg2 = Math.lgamma(6.0)
+ lg2[0].should be_close(Math.log(120.0), TOLERANCE)
+ lg2[1].should == 1
+ end
+
+ it "returns an approximate value when passed -0.5" do
+ lg1 = Math.lgamma(-0.5)
+ lg1[0].should be_close(1.2655121, TOLERANCE)
+ lg1[1].should == -1
+ end
+
+ it "returns an approximate value when passed -1.5" do
+ lg2 = Math.lgamma(-1.5)
+ lg2[0].should be_close(0.8600470, TOLERANCE)
+ lg2[1].should == 1
+ end
+
+ it "raises Math::DomainError when passed -Infinity" do
+ lambda { Math.lgamma(-infinity_value) }.should raise_error(Math::DomainError)
+ end
+
+ it "returns [Infinity, 1] when passed Infinity" do
+ Math.lgamma(infinity_value).should == [infinity_value, 1]
+ end
+
+ it "returns [NaN, 1] when passed NaN" do
+ Math.lgamma(nan_value)[0].nan?.should be_true
+ Math.lgamma(nan_value)[1].should == 1
+ end
+end
diff --git a/spec/ruby/core/math/log10_spec.rb b/spec/ruby/core/math/log10_spec.rb
new file mode 100644
index 0000000000..2c594999b7
--- /dev/null
+++ b/spec/ruby/core/math/log10_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# The common logarithm, having base 10
+describe "Math.log10" do
+ it "returns a float" do
+ Math.log10(1).should be_kind_of(Float)
+ end
+
+ it "returns the base-10 logarithm of the argument" do
+ Math.log10(0.0001).should be_close(-4.0, TOLERANCE)
+ Math.log10(0.000000000001e-15).should be_close(-27.0, TOLERANCE)
+ Math.log10(1).should be_close(0.0, TOLERANCE)
+ Math.log10(10).should be_close(1.0, TOLERANCE)
+ Math.log10(10e15).should be_close(16.0, TOLERANCE)
+ end
+
+ it "raises an Math::DomainError if the argument is less than 0" do
+ lambda { Math.log10(-1e-15) }.should raise_error(Math::DomainError)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.log10("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.log10(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.log10(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.log10(MathSpecs::Float.new).should be_close(0.0, TOLERANCE)
+ end
+end
+
+describe "Math#log10" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:log10, 4.15).should be_close(0.618048096712093, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/log2_spec.rb b/spec/ruby/core/math/log2_spec.rb
new file mode 100644
index 0000000000..8a7c582f92
--- /dev/null
+++ b/spec/ruby/core/math/log2_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Math.log2" do
+ it "returns a float" do
+ Math.log2(5.79).should be_close(2.53356334821451, TOLERANCE)
+ end
+
+ it "returns the natural logarithm of the argument" do
+ Math.log2(1.1).should be_close(0.137503523749935, TOLERANCE)
+ Math.log2(3.14).should be_close(1.6507645591169, TOLERANCE)
+ Math.log2((2**101+45677544234809571)).should be_close(101.00000000000003, TOLERANCE)
+
+ Math.log2((2**10001+45677544234809571)).should == 10001.0
+ Math.log2((2**301+45677544234809571)).should == 301.0
+ end
+
+ it "raises an Errno::EDOM if the argument is less than 0" do
+ lambda { Math.log2(-1e-15) }.should raise_error( Math::DomainError)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.log2("test") }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed a numerical argument as a string" do
+ lambda { Math.log2("1.0") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.log2(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.log2(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.log2(MathSpecs::Float.new).should be_close(0.0, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/log_spec.rb b/spec/ruby/core/math/log_spec.rb
new file mode 100644
index 0000000000..c09e270c06
--- /dev/null
+++ b/spec/ruby/core/math/log_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# The natural logarithm, having base Math::E
+describe "Math.log" do
+ it "returns a float" do
+ Math.log(1).should be_kind_of(Float)
+ end
+
+ it "returns the natural logarithm of the argument" do
+ Math.log(0.0001).should be_close(-9.21034037197618, TOLERANCE)
+ Math.log(0.000000000001e-15).should be_close(-62.1697975108392, TOLERANCE)
+ Math.log(1).should be_close(0.0, TOLERANCE)
+ Math.log(10).should be_close( 2.30258509299405, TOLERANCE)
+ Math.log(10e15).should be_close(36.8413614879047, TOLERANCE)
+ end
+
+ it "raises an Math::DomainError if the argument is less than 0" do
+ lambda { Math.log(-1e-15) }.should raise_error(Math::DomainError)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.log("test") }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError for numerical values passed as string" do
+ lambda { Math.log("10") }.should raise_error(TypeError)
+ end
+
+ it "accepts a second argument for the base" do
+ Math.log(9, 3).should be_close(2, TOLERANCE)
+ Math.log(8, 1.4142135623730951).should be_close(6, TOLERANCE)
+ end
+
+ it "raises a TypeError when the numerical base cannot be coerced to a float" do
+ lambda { Math.log(10, "2") }.should raise_error(TypeError)
+ lambda { Math.log(10, nil) }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.log(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.log(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.log(MathSpecs::Float.new).should be_close(0.0, TOLERANCE)
+ end
+end
+
+describe "Math#log" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:log, 5.21).should be_close(1.65057985576528, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/sin_spec.rb b/spec/ruby/core/math/sin_spec.rb
new file mode 100644
index 0000000000..96bef5fed1
--- /dev/null
+++ b/spec/ruby/core/math/sin_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# sine : (-Inf, Inf) --> (-1.0, 1.0)
+describe "Math.sin" do
+ it "returns a float" do
+ Math.sin(Math::PI).should be_kind_of(Float)
+ end
+
+ it "returns the sine of the argument expressed in radians" do
+ Math.sin(Math::PI).should be_close(0.0, TOLERANCE)
+ Math.sin(0).should be_close(0.0, TOLERANCE)
+ Math.sin(Math::PI/2).should be_close(1.0, TOLERANCE)
+ Math.sin(3*Math::PI/2).should be_close(-1.0, TOLERANCE)
+ Math.sin(2*Math::PI).should be_close(0.0, TOLERANCE)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.sin("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.sin(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.sin(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.sin(MathSpecs::Float.new).should be_close(0.841470984807897, TOLERANCE)
+ end
+end
+
+describe "Math#sin" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:sin, 1.21).should be_close(0.935616001553386, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/sinh_spec.rb b/spec/ruby/core/math/sinh_spec.rb
new file mode 100644
index 0000000000..e0fa0c99e9
--- /dev/null
+++ b/spec/ruby/core/math/sinh_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Math.sinh" do
+ it "returns a float" do
+ Math.sinh(1.2).should be_kind_of(Float)
+ end
+
+ it "returns the hyperbolic sin of the argument" do
+ Math.sinh(0.0).should == 0.0
+ Math.sinh(-0.0).should == 0.0
+ Math.sinh(1.5).should be_close(2.12927945509482, TOLERANCE)
+ Math.sinh(-2.8).should be_close(-8.19191835423591, TOLERANCE)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.sinh("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.sinh(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.sinh(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.sinh(MathSpecs::Float.new).should be_close(1.1752011936438, TOLERANCE)
+ end
+end
+
+describe "Math#sinh" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:sinh, 1.99).should be_close(3.58941916843202, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/sqrt_spec.rb b/spec/ruby/core/math/sqrt_spec.rb
new file mode 100644
index 0000000000..50fd132b2a
--- /dev/null
+++ b/spec/ruby/core/math/sqrt_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Math.sqrt" do
+ it "returns a float" do
+ Math.sqrt(1).should be_kind_of(Float)
+ end
+
+ it "returns the square root of the argument" do
+ Math.sqrt(1).should == 1.0
+ Math.sqrt(4.0).should == 2.0
+ Math.sqrt(15241578780673814.441547445).should be_close(123456789.123457, TOLERANCE)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.sqrt("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.sqrt(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.sqrt(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.sqrt(MathSpecs::Float.new).should be_close(1.0, TOLERANCE)
+ end
+end
+
+describe "Math#sqrt" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:sqrt, 2.23).should be_close(1.49331845230681, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/tan_spec.rb b/spec/ruby/core/math/tan_spec.rb
new file mode 100644
index 0000000000..bf5c80f77c
--- /dev/null
+++ b/spec/ruby/core/math/tan_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Math.tan" do
+ it "returns a float" do
+ Math.tan(1.35).should be_kind_of(Float)
+ end
+
+ it "returns the tangent of the argument" do
+ Math.tan(0.0).should == 0.0
+ Math.tan(-0.0).should == -0.0
+ Math.tan(4.22).should be_close(1.86406937682395, TOLERANCE)
+ Math.tan(-9.65).should be_close(-0.229109052606441, TOLERANCE)
+ end
+
+ it "returns NaN if called with +-Infinitty" do
+ Math.tan(infinity_value).nan?.should == true
+ Math.tan(-infinity_value).nan?.should == true
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.tan("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.tan(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.tan(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.tan(MathSpecs::Float.new).should be_close(1.5574077246549, TOLERANCE)
+ end
+end
+
+describe "Math#tan" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:tan, 1.0).should be_close(1.5574077246549, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/math/tanh_spec.rb b/spec/ruby/core/math/tanh_spec.rb
new file mode 100644
index 0000000000..699d882ee8
--- /dev/null
+++ b/spec/ruby/core/math/tanh_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Math.tanh" do
+ it "returns a float" do
+ Math.tanh(0.5).should be_kind_of(Float)
+ end
+
+ it "returns the hyperbolic tangent of the argument" do
+ Math.tanh(0.0).should == 0.0
+ Math.tanh(-0.0).should == -0.0
+ Math.tanh(infinity_value).should == 1.0
+ Math.tanh(-infinity_value).should == -1.0
+ Math.tanh(2.5).should be_close(0.98661429815143, TOLERANCE)
+ Math.tanh(-4.892).should be_close(-0.999887314427707, TOLERANCE)
+ end
+
+ it "raises a TypeError if the argument cannot be coerced with Float()" do
+ lambda { Math.tanh("test") }.should raise_error(TypeError)
+ end
+
+ it "returns NaN given NaN" do
+ Math.tanh(nan_value).nan?.should be_true
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { Math.tanh(nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts any argument that can be coerced with Float()" do
+ Math.tanh(MathSpecs::Float.new).should be_close(0.761594155955765, TOLERANCE)
+ end
+end
+
+describe "Math#tanh" do
+ it "is accessible as a private instance method" do
+ IncludesMath.new.send(:tanh, 5.21).should be_close(0.99994034202065, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/core/method/arity_spec.rb b/spec/ruby/core/method/arity_spec.rb
new file mode 100644
index 0000000000..4bb821735a
--- /dev/null
+++ b/spec/ruby/core/method/arity_spec.rb
@@ -0,0 +1,222 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#arity" do
+ SpecEvaluate.desc = "for method definition"
+
+ context "returns zero" do
+ evaluate <<-ruby do
+ def m() end
+ ruby
+
+ method(:m).arity.should == 0
+ end
+
+ evaluate <<-ruby do
+ def n(&b) end
+ ruby
+
+ method(:n).arity.should == 0
+ end
+ end
+
+ context "returns positive values" do
+ evaluate <<-ruby do
+ def m(a) end
+ def n(a, b) end
+ def o(a, b, c) end
+ def p(a, b, c, d) end
+ ruby
+
+ method(:m).arity.should == 1
+ method(:n).arity.should == 2
+ method(:o).arity.should == 3
+ method(:p).arity.should == 4
+ end
+
+ evaluate <<-ruby do
+ def m(a:) end
+ def n(a:, b:) end
+ def o(a: 1, b:, c:, d: 2) end
+ ruby
+
+ method(:m).arity.should == 1
+ method(:n).arity.should == 1
+ method(:o).arity.should == 1
+ end
+
+ evaluate <<-ruby do
+ def m(a, b:) end
+ def n(a, b:, &l) end
+ ruby
+
+ method(:m).arity.should == 2
+ method(:n).arity.should == 2
+ end
+
+ evaluate <<-ruby do
+ def m(a, b, c:, d: 1) end
+ def n(a, b, c:, d: 1, **k, &l) end
+ ruby
+
+ method(:m).arity.should == 3
+ method(:n).arity.should == 3
+ end
+ end
+
+ context "returns negative values" do
+ evaluate <<-ruby do
+ def m(a=1) end
+ def n(a=1, b=2) end
+ ruby
+
+ method(:m).arity.should == -1
+ method(:n).arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(a, b=1) end
+ def n(a, b, c=1, d=2) end
+ ruby
+
+ method(:m).arity.should == -2
+ method(:n).arity.should == -3
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, *b) end
+ def n(a=1, b=2, *c) end
+ ruby
+
+ method(:m).arity.should == -1
+ method(:n).arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(*) end
+ def n(*a) end
+ ruby
+
+ method(:m).arity.should == -1
+ method(:n).arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(a, *) end
+ def n(a, *b) end
+ def o(a, b, *c) end
+ def p(a, b, c, *d) end
+ ruby
+
+ method(:m).arity.should == -2
+ method(:n).arity.should == -2
+ method(:o).arity.should == -3
+ method(:p).arity.should == -4
+ end
+
+ evaluate <<-ruby do
+ def m(*a, b) end
+ def n(*a, b, c) end
+ def o(*a, b, c, d) end
+ ruby
+
+ method(:m).arity.should == -2
+ method(:n).arity.should == -3
+ method(:o).arity.should == -4
+ end
+
+ evaluate <<-ruby do
+ def m(a, *b, c) end
+ def n(a, b, *c, d, e) end
+ ruby
+
+ method(:m).arity.should == -3
+ method(:n).arity.should == -5
+ end
+
+ evaluate <<-ruby do
+ def m(a, b=1, c=2, *d, e, f) end
+ def n(a, b, c=1, *d, e, f, g) end
+ ruby
+
+ method(:m).arity.should == -4
+ method(:n).arity.should == -6
+ end
+
+ evaluate <<-ruby do
+ def m(a: 1) end
+ def n(a: 1, b: 2) end
+ ruby
+
+ method(:m).arity.should == -1
+ method(:n).arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, b: 2) end
+ def n(*a, b: 1) end
+ def o(a=1, b: 2) end
+ def p(a=1, *b, c: 2, &l) end
+ ruby
+
+ method(:m).arity.should == -1
+ method(:n).arity.should == -1
+ method(:o).arity.should == -1
+ method(:p).arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(**k, &l) end
+ def n(*a, **k) end
+ def o(a: 1, b: 2, **k) end
+ ruby
+
+ method(:m).arity.should == -1
+ method(:n).arity.should == -1
+ method(:o).arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, *b, c:, d: 2, **k, &l) end
+ ruby
+
+ method(:m).arity.should == -2
+ end
+
+ evaluate <<-ruby do
+ def m(a, b=1, *c, d, e:, f: 2, **k, &l) end
+ def n(a, b=1, *c, d:, e:, f: 2, **k, &l) end
+ def o(a=0, b=1, *c, d, e:, f: 2, **k, &l) end
+ def p(a=0, b=1, *c, d:, e:, f: 2, **k, &l) end
+ ruby
+
+ method(:m).arity.should == -4
+ method(:n).arity.should == -3
+ method(:o).arity.should == -3
+ method(:p).arity.should == -2
+ end
+ end
+
+ context "for a Method generated by respond_to_missing?" do
+ it "returns -1" do
+ obj = mock("method arity respond_to_missing")
+ obj.should_receive(:respond_to_missing?).and_return(true)
+
+ obj.method(:m).arity.should == -1
+ end
+ end
+
+ context "for a Method generated by attr_reader" do
+ it "return 0" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:reader).arity.should == 0
+ end
+ end
+
+ context "for a Method generated by attr_writer" do
+ it "returns 1" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:writer=).arity.should == 1
+ end
+ end
+end
diff --git a/spec/ruby/core/method/call_spec.rb b/spec/ruby/core/method/call_spec.rb
new file mode 100644
index 0000000000..6d997325fa
--- /dev/null
+++ b/spec/ruby/core/method/call_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/call'
+
+describe "Method#call" do
+ it_behaves_like :method_call, :call
+end
diff --git a/spec/ruby/core/method/case_compare_spec.rb b/spec/ruby/core/method/case_compare_spec.rb
new file mode 100644
index 0000000000..17785b5c1d
--- /dev/null
+++ b/spec/ruby/core/method/case_compare_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/call'
+
+ruby_version_is "2.5" do
+ describe "Method#===" do
+ it_behaves_like :method_call, :===
+ end
+end
diff --git a/spec/ruby/core/method/clone_spec.rb b/spec/ruby/core/method/clone_spec.rb
new file mode 100644
index 0000000000..3fe4000fb7
--- /dev/null
+++ b/spec/ruby/core/method/clone_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#clone" do
+ it "returns a copy of the method" do
+ m1 = MethodSpecs::Methods.new.method(:foo)
+ m2 = m1.clone
+
+ m1.should == m2
+ m1.should_not equal(m2)
+
+ m1.call.should == m2.call
+ end
+end
diff --git a/spec/ruby/core/method/curry_spec.rb b/spec/ruby/core/method/curry_spec.rb
new file mode 100644
index 0000000000..36449f7512
--- /dev/null
+++ b/spec/ruby/core/method/curry_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#curry" do
+ it "returns a curried proc" do
+ x = Object.new
+ def x.foo(a,b,c); [a,b,c]; end
+
+ c = x.method(:foo).curry
+ c.should be_kind_of(Proc)
+ c.call(1).call(2, 3).should == [1,2,3]
+ end
+
+ describe "with optional arity argument" do
+ before(:each) do
+ @obj = MethodSpecs::Methods.new
+ end
+
+ it "returns a curried proc when given correct arity" do
+ @obj.method(:one_req).curry(1).should be_kind_of(Proc)
+ @obj.method(:zero_with_splat).curry(100).should be_kind_of(Proc)
+ @obj.method(:two_req_with_splat).curry(2).should be_kind_of(Proc)
+ end
+
+ it "raises ArgumentError when the method requires less arguments than the given arity" do
+ lambda { @obj.method(:zero).curry(1) }.should raise_error(ArgumentError)
+ lambda { @obj.method(:one_req_one_opt).curry(3) }.should raise_error(ArgumentError)
+ lambda { @obj.method(:two_req_one_opt_with_block).curry(4) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError when the method requires more arguments than the given arity" do
+ lambda { @obj.method(:two_req_with_splat).curry(1) }.should raise_error(ArgumentError)
+ lambda { @obj.method(:one_req).curry(0) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/method/element_reference_spec.rb b/spec/ruby/core/method/element_reference_spec.rb
new file mode 100644
index 0000000000..aa6c54d1cb
--- /dev/null
+++ b/spec/ruby/core/method/element_reference_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/call'
+
+describe "Method#[]" do
+ it_behaves_like :method_call, :[]
+end
diff --git a/spec/ruby/core/method/eql_spec.rb b/spec/ruby/core/method/eql_spec.rb
new file mode 100644
index 0000000000..b97c9e4db0
--- /dev/null
+++ b/spec/ruby/core/method/eql_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/eql'
+
+describe "Method#eql?" do
+ it_behaves_like :method_equal, :eql?
+end
diff --git a/spec/ruby/core/method/equal_value_spec.rb b/spec/ruby/core/method/equal_value_spec.rb
new file mode 100644
index 0000000000..0431d0c5f6
--- /dev/null
+++ b/spec/ruby/core/method/equal_value_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/eql'
+
+describe "Method#==" do
+ it_behaves_like :method_equal, :==
+end
diff --git a/spec/ruby/core/method/fixtures/classes.rb b/spec/ruby/core/method/fixtures/classes.rb
new file mode 100644
index 0000000000..142cbd1bec
--- /dev/null
+++ b/spec/ruby/core/method/fixtures/classes.rb
@@ -0,0 +1,184 @@
+module MethodSpecs
+
+
+ class SourceLocation
+ def self.location # This needs to be on this line
+ :location # for the spec to pass
+ end
+
+ def self.redefined
+ :first
+ end
+
+ def self.redefined
+ :last
+ end
+
+ def original
+ end
+
+ alias :aka :original
+ end
+
+ class Methods
+ def foo
+ true
+ end
+
+ alias bar foo
+
+ def same_as_foo
+ true
+ end
+
+ def respond_to_missing? method, bool
+ [:handled_via_method_missing, :also_handled].include? method
+ end
+
+ def method_missing(method, *arguments)
+ if [:handled_via_method_missing, :also_handled].include? method
+ arguments
+ else
+ super
+ end
+ end
+
+ attr_accessor :attr
+
+ def zero; end
+ def one_req(a); end
+ def two_req(a, b); end
+
+ def zero_with_block(&blk); end
+ def one_req_with_block(a, &blk); end
+ def two_req_with_block(a, b, &blk); end
+
+ def one_opt(a=nil); end
+ def one_req_one_opt(a, b=nil); end
+ def one_req_two_opt(a, b=nil, c=nil); end
+ def two_req_one_opt(a, b, c=nil); end
+
+ def one_opt_with_block(a=nil, &blk); end
+ def one_req_one_opt_with_block(a, b=nil, &blk); end
+ def one_req_two_opt_with_block(a, b=nil, c=nil, &blk); end
+ def two_req_one_opt_with_block(a, b, c=nil, &blk); end
+
+ def zero_with_splat(*a); end
+ def one_req_with_splat(a, *b); end
+ def two_req_with_splat(a, b, *c); end
+ def one_req_one_opt_with_splat(a, b=nil, *c); end
+ def two_req_one_opt_with_splat(a, b, c=nil, *d); end
+ def one_req_two_opt_with_splat(a, b=nil, c=nil, *d); end
+
+ def zero_with_splat_and_block(*a, &blk); end
+ def one_req_with_splat_and_block(a, *b, &blk); end
+ def two_req_with_splat_and_block(a, b, *c, &blk); end
+ def one_req_one_opt_with_splat_and_block(a, b=nil, *c, &blk); end
+ 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
+
+ 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|})
+ define_method(:two_req_defined_method, Proc.new {|x, y|})
+ define_method(:no_args_defined_method) {}
+ define_method(:two_grouped_defined_method) {|(_x1,_x2)|}
+
+ attr_reader :reader
+ attr_writer :writer
+ end
+
+ module MyMod
+ def bar; :bar; end
+ end
+
+ class MySuper
+ include MyMod
+ end
+
+ class MySub < MySuper; end
+
+ class A
+ def baz(a, b)
+ self.class
+ end
+ def overridden; end
+ end
+
+ class B < A
+ def overridden; end
+ end
+
+ module BetweenBAndC
+ def overridden; end
+ end
+
+ class C < B
+ include BetweenBAndC
+ def overridden; end
+ end
+
+ module OverrideAgain
+ def overridden; end
+ end
+
+ class D
+ def bar() 'done' end
+ end
+
+ class Eql
+
+ def same_body
+ 1 + 1
+ end
+
+ alias :same_body_alias :same_body
+
+ def same_body_with_args(arg)
+ 1 + 1
+ end
+
+ def different_body
+ 1 + 2
+ end
+
+ def same_body_two
+ 1 + 1
+ end
+
+ private
+ def same_body_private
+ 1 + 1
+ end
+ end
+
+ class Eql2
+
+ def same_body
+ 1 + 1
+ end
+
+ end
+
+ class ToProc
+ def method_called(a, b)
+ ScratchPad << [a, b]
+ end
+
+ def to_proc
+ method(:method_called).to_proc
+ end
+ end
+
+ class ToProcBeta
+ def method_called(a)
+ ScratchPad << a
+ a
+ end
+
+ def to_proc
+ method(:method_called).to_proc
+ end
+ end
+
+end
diff --git a/spec/ruby/core/method/hash_spec.rb b/spec/ruby/core/method/hash_spec.rb
new file mode 100644
index 0000000000..b6807ca1e2
--- /dev/null
+++ b/spec/ruby/core/method/hash_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#hash" do
+ it "needs to be reviewed for spec completeness"
+
+ it "returns the same value for user methods that are eql?" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:foo).hash.should == obj.method(:bar).hash
+ end
+
+ # See also redmine #6048
+ it "returns the same value for builtin methods that are eql?" do
+ obj = [42]
+ obj.method(:to_s).hash.should == obj.method(:inspect).hash
+ end
+end
diff --git a/spec/ruby/core/method/inspect_spec.rb b/spec/ruby/core/method/inspect_spec.rb
new file mode 100644
index 0000000000..e0fe1afdd0
--- /dev/null
+++ b/spec/ruby/core/method/inspect_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_s'
+
+describe "Method#inspect" do
+ it_behaves_like :method_to_s, :inspect
+end
diff --git a/spec/ruby/core/method/name_spec.rb b/spec/ruby/core/method/name_spec.rb
new file mode 100644
index 0000000000..de390c6f52
--- /dev/null
+++ b/spec/ruby/core/method/name_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#name" do
+ it "returns the name of the method" do
+ "abc".method(:upcase).name.should == :upcase
+ end
+
+ it "returns the name even when aliased" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:foo).name.should == :foo
+ obj.method(:bar).name.should == :bar
+ obj.method(:bar).unbind.bind(obj).name.should == :bar
+ end
+
+ describe "for a Method generated by respond_to_missing?" do
+ it "returns the name passed to respond_to_missing?" do
+ @m = MethodSpecs::Methods.new
+ @m.method(:handled_via_method_missing).name.should == :handled_via_method_missing
+ end
+ end
+end
diff --git a/spec/ruby/core/method/owner_spec.rb b/spec/ruby/core/method/owner_spec.rb
new file mode 100644
index 0000000000..ca5dff7295
--- /dev/null
+++ b/spec/ruby/core/method/owner_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#owner" do
+ it "returns the owner of the method" do
+ "abc".method(:upcase).owner.should == String
+ end
+
+ it "returns the same owner when aliased in the same classes" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:foo).owner.should == MethodSpecs::Methods
+ obj.method(:bar).owner.should == MethodSpecs::Methods
+ end
+
+ it "returns the class/module it was defined in" do
+ MethodSpecs::C.new.method(:baz).owner.should == MethodSpecs::A
+ MethodSpecs::MySuper.new.method(:bar).owner.should == MethodSpecs::MyMod
+ end
+
+ describe "for a Method generated by respond_to_missing?" do
+ it "returns the owner of the method" do
+ @m = MethodSpecs::Methods.new
+ @m.method(:handled_via_method_missing).owner.should == MethodSpecs::Methods
+ end
+ end
+end
diff --git a/spec/ruby/core/method/parameters_spec.rb b/spec/ruby/core/method/parameters_spec.rb
new file mode 100644
index 0000000000..d750f4c221
--- /dev/null
+++ b/spec/ruby/core/method/parameters_spec.rb
@@ -0,0 +1,244 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#parameters" do
+ class MethodSpecs::Methods
+ def one_key(a: 1); end
+ def one_keyrest(**a); end
+
+ def one_keyreq(a:); end
+
+ def one_splat_one_req(*a,b); end
+ def one_splat_two_req(*a,b,c); end
+ def one_splat_one_req_with_block(*a,b,&blk); end
+
+ def one_opt_with_stabby(a=->(b){true}); end
+
+ def one_unnamed_splat(*); end
+
+ def one_splat_one_block(*args, &block)
+ local_is_not_parameter = {}
+ end
+
+ define_method(:one_optional_defined_method) {|x = 1|}
+ end
+
+ it "returns an empty Array when the method expects no arguments" do
+ MethodSpecs::Methods.instance_method(:zero).parameters.should == []
+ end
+
+ it "returns [[:req,:name]] for a method expecting one required argument called 'name'" do
+ MethodSpecs::Methods.instance_method(:one_req).parameters.should == [[:req,:a]]
+ end
+
+ it "returns [[:req,:a],[:req,:b]] for a method expecting two required arguments called 'a' and 'b''" do
+ m = MethodSpecs::Methods.instance_method(:two_req)
+ m.parameters.should == [[:req,:a], [:req,:b]]
+ end
+
+ it "returns [[:block,:blk]] for a method expecting one block argument called 'a'" do
+ m = MethodSpecs::Methods.instance_method(:zero_with_block)
+ m.parameters.should == [[:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:block,:b] for a method expecting a required argument ('a') and a block argument ('b')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_with_block)
+ m.parameters.should == [[:req,:a], [:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:req,:b],[:block,:c] for a method expecting two required arguments ('a','b') and a block argument ('c')" do
+ m = MethodSpecs::Methods.instance_method(:two_req_with_block)
+ m.parameters.should == [[:req,:a], [:req,:b], [:block,:blk]]
+ end
+
+ it "returns [[:opt,:a]] for a method expecting one optional argument ('a')" do
+ m = MethodSpecs::Methods.instance_method(:one_opt)
+ m.parameters.should == [[:opt,:a]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b]] for a method expecting one required argument ('a') and one optional argument ('b')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_one_opt)
+ m.parameters.should == [[:req,:a],[:opt,:b]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b]] for a method expecting one required argument ('a') and one optional argument ('b')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_one_opt)
+ m.parameters.should == [[:req,:a],[:opt,:b]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b],[:opt,:c]] for a method expecting one required argument ('a') and two optional arguments ('b','c')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_two_opt)
+ m.parameters.should == [[:req,:a],[:opt,:b],[:opt,:c]]
+ end
+
+ it "returns [[:req,:a],[:req,:b],[:opt,:c]] for a method expecting two required arguments ('a','b') and one optional arguments ('c')" do
+ m = MethodSpecs::Methods.instance_method(:two_req_one_opt)
+ m.parameters.should == [[:req,:a],[:req,:b],[:opt,:c]]
+ end
+
+ it "returns [[:opt,:a],[:block,:b]] for a method expecting one required argument ('a') and one block argument ('b')" do
+ m = MethodSpecs::Methods.instance_method(:one_opt_with_block)
+ m.parameters.should == [[:opt,:a],[:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b],[:block,:c]] for a method expecting one required argument ('a'), one optional argument ('b'), and a block ('c')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_one_opt_with_block)
+ m.parameters.should == [[:req,:a],[:opt,:b],[:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b],[:opt,:c],[:block,:d]] for a method expecting one required argument ('a'), two optional arguments ('b','c'), and a block ('d')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_two_opt_with_block)
+ m.parameters.should == [[:req,:a],[:opt,:b],[:opt,:c],[:block,:blk]]
+ end
+
+ it "returns [[:rest,:a]] for a method expecting a single splat argument ('a')" do
+ m = MethodSpecs::Methods.instance_method(:zero_with_splat)
+ m.parameters.should == [[:rest,:a]]
+ end
+
+ it "returns [[:req,:a],[:rest,:b]] for a method expecting a splat argument ('a') and a required argument ('b')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_with_splat)
+ m.parameters.should == [[:req,:a],[:rest,:b]]
+ end
+
+ it "returns [[:req,:a],[:req,:b],[:rest,:c]] for a method expecting two required arguments ('a','b') and a splat argument ('c')" do
+ m = MethodSpecs::Methods.instance_method(:two_req_with_splat)
+ m.parameters.should == [[:req,:a],[:req,:b],[:rest,:c]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b],[:rest,:c]] for a method expecting a required argument ('a','b'), an optional argument ('b'), and a splat argument ('c')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_one_opt_with_splat)
+ m.parameters.should == [[:req,:a],[:opt,:b],[:rest,:c]]
+ end
+
+ it "returns [[:req,:a],[:req,:b],[:opt,:b],[:rest,:d]] for a method expecting two required arguments ('a','b'), an optional argument ('c'), and a splat argument ('d')" do
+ m = MethodSpecs::Methods.instance_method(:two_req_one_opt_with_splat)
+ m.parameters.should == [[:req,:a],[:req,:b],[:opt,:c],[:rest,:d]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b],[:opt,:c],[:rest,:d]] for a method expecting a required argument ('a'), two optional arguments ('b','c'), and a splat argument ('d')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_two_opt_with_splat)
+ m.parameters.should == [[:req,:a],[:opt,:b],[:opt,:c],[:rest,:d]]
+ end
+
+ it "returns [[:rest,:a],[:block,:b]] for a method expecting a splat argument ('a') and a block argument ('b')" do
+ m = MethodSpecs::Methods.instance_method(:zero_with_splat_and_block)
+ m.parameters.should == [[:rest,:a],[:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:rest,:b],[:block,:c]] for a method expecting a required argument ('a'), a splat argument ('b'), and a block ('c')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_with_splat_and_block)
+ m.parameters.should == [[:req,:a],[:rest,:b],[:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:req,:b],[:rest,:c],[:block,:d]] for a method expecting two required arguments ('a','b'), a splat argument ('c'), and a block ('d')" do
+ m = MethodSpecs::Methods.instance_method(:two_req_with_splat_and_block)
+ m.parameters.should == [[:req,:a],[:req,:b],[:rest,:c],[:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:opt,:b],[:rest,:c],[:block,:d]] for a method expecting a required argument ('a'), a splat argument ('c'), and a block ('d')" do
+ m = MethodSpecs::Methods.instance_method(:one_req_one_opt_with_splat_and_block)
+ m.parameters.should == [[:req,:a],[:opt,:b],[:rest,:c],[:block,:blk]]
+ end
+
+ it "returns [[:req,:a],[:req,:b],[:opt,:c],[:block,:d]] for a method expecting two required arguments ('a','b'), an optional argument ('c'), a splat argument ('d'), and a block ('e')" do
+ m = MethodSpecs::Methods.instance_method(:two_req_one_opt_with_splat_and_block)
+ m.parameters.should == [[:req,:a],[:req,:b],[:opt,:c],[:rest,:d],[:block,:blk]]
+ end
+
+ it "returns [[:rest,:a],[:req,:b]] for a method expecting a splat argument ('a') and a required argument ('b')" do
+ m = MethodSpecs::Methods.instance_method(:one_splat_one_req)
+ m.parameters.should == [[:rest,:a],[:req,:b]]
+ end
+
+ it "returns [[:rest,:a],[:req,:b],[:req,:c]] for a method expecting a splat argument ('a') and two required arguments ('b','c')" do
+ m = MethodSpecs::Methods.instance_method(:one_splat_two_req)
+ m.parameters.should == [[:rest,:a],[:req,:b],[:req,:c]]
+ end
+
+ it "returns [[:rest,:a],[:req,:b],[:block,:c]] for a method expecting a splat argument ('a'), a required argument ('b'), and a block ('c')" do
+ m = MethodSpecs::Methods.instance_method(:one_splat_one_req_with_block)
+ m.parameters.should == [[:rest,:a],[:req,:b],[:block,:blk]]
+ end
+
+ it "returns [[:key,:a]] for a method with a single optional keyword argument" do
+ m = MethodSpecs::Methods.instance_method(:one_key)
+ m.parameters.should == [[:key,:a]]
+ end
+
+ it "returns [[:keyrest,:a]] for a method with a keyword rest argument" do
+ m = MethodSpecs::Methods.instance_method(:one_keyrest)
+ m.parameters.should == [[:keyrest,:a]]
+ end
+
+ it "returns [[:keyreq,:a]] for a method with a single required keyword argument" do
+ m = MethodSpecs::Methods.instance_method(:one_keyreq)
+ m.parameters.should == [[:keyreq,:a]]
+ end
+
+ it "works with ->(){} as the value of an optional argument" do
+ m = MethodSpecs::Methods.instance_method(:one_opt_with_stabby)
+ m.parameters.should == [[:opt,:a]]
+ end
+
+ # define_method variants
+ it "returns [] for a define_method method with explicit no-args || specification" do
+ m = MethodSpecs::Methods.instance_method(:zero_defined_method)
+ m.parameters.should == []
+ end
+
+ it "returns [[:rest, :x]] for a define_method method with rest arg 'x' only" do
+ m = MethodSpecs::Methods.instance_method(:zero_with_splat_defined_method)
+ m.parameters.should == [[:rest, :x]]
+ end
+
+ it "returns [[:req, :x]] for a define_method method expecting one required argument 'x'" do
+ m = MethodSpecs::Methods.instance_method(:one_req_defined_method)
+ m.parameters.should == [[:req, :x]]
+ end
+
+ it "returns [[:req, :x], [:req, :y]] for a define_method method expecting two required arguments 'x' and 'y'" do
+ m = MethodSpecs::Methods.instance_method(:two_req_defined_method)
+ m.parameters.should == [[:req, :x], [:req, :y]]
+ end
+
+ it "returns [] for a define_method method with no args specification" do
+ m = MethodSpecs::Methods.instance_method(:no_args_defined_method)
+ m.parameters.should == []
+ end
+
+ it "returns [[:req]] for a define_method method with a grouping as its only argument" do
+ m = MethodSpecs::Methods.instance_method(:two_grouped_defined_method)
+ m.parameters.should == [[:req]]
+ end
+
+ it "returns [[:opt, :x]] for a define_method method with an optional argument 'x'" do
+ m = MethodSpecs::Methods.instance_method(:one_optional_defined_method)
+ m.parameters.should == [[:opt, :x]]
+ end
+
+ it "returns [[:rest]] for a Method generated by respond_to_missing?" do
+ m = MethodSpecs::Methods.new
+ m.method(:handled_via_method_missing).parameters.should == [[:rest]]
+ end
+
+ it "adds nameless rest arg for \"star\" argument" do
+ m = MethodSpecs::Methods.new
+ m.method(:one_unnamed_splat).parameters.should == [[:rest]]
+ end
+
+ it "returns the args and block for a splat and block argument" do
+ m = MethodSpecs::Methods.new
+ m.method(:one_splat_one_block).parameters.should == [[:rest, :args], [:block, :block]]
+ end
+
+ it "returns [] for a Method generated by attr_reader" do
+ m = MethodSpecs::Methods.new
+ m.method(:reader).parameters.should == []
+ end
+
+ it "return [[:req]] for a Method generated by attr_writer" do
+ m = MethodSpecs::Methods.new
+ m.method(:writer=).parameters.should == [[:req]]
+ end
+end
diff --git a/spec/ruby/core/method/receiver_spec.rb b/spec/ruby/core/method/receiver_spec.rb
new file mode 100644
index 0000000000..2b2e11dd2e
--- /dev/null
+++ b/spec/ruby/core/method/receiver_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#receiver" do
+ it "returns the receiver of the method" do
+ s = "abc"
+ s.method(:upcase).receiver.should equal(s)
+ end
+
+ it "returns the right receiver even when aliased" do
+ obj = MethodSpecs::Methods.new
+ obj.method(:foo).receiver.should equal(obj)
+ obj.method(:bar).receiver.should equal(obj)
+ end
+
+ describe "for a Method generated by respond_to_missing?" do
+ it "returns the receiver of the method" do
+ m = MethodSpecs::Methods.new
+ m.method(:handled_via_method_missing).receiver.should equal(m)
+ end
+ end
+end
diff --git a/spec/ruby/core/method/shared/call.rb b/spec/ruby/core/method/shared/call.rb
new file mode 100644
index 0000000000..f178b9da7d
--- /dev/null
+++ b/spec/ruby/core/method/shared/call.rb
@@ -0,0 +1,51 @@
+describe :method_call, shared: true do
+ it "invokes the method with the specified arguments, returning the method's return value" do
+ m = 12.method("+")
+ m.send(@method, 3).should == 15
+ m.send(@method, 20).should == 32
+
+ m = MethodSpecs::Methods.new.method(:attr=)
+ m.send(@method, 42).should == 42
+ end
+
+ it "raises an ArgumentError when given incorrect number of arguments" do
+ lambda {
+ MethodSpecs::Methods.new.method(:two_req).send(@method, 1, 2, 3)
+ }.should raise_error(ArgumentError)
+ lambda {
+ MethodSpecs::Methods.new.method(:two_req).send(@method, 1)
+ }.should raise_error(ArgumentError)
+ end
+
+ describe "for a Method generated by respond_to_missing?" do
+ it "invokes method_missing with the specified arguments and returns the result" do
+ @m = MethodSpecs::Methods.new
+ meth = @m.method(:handled_via_method_missing)
+ meth.send(@method, :argument).should == [:argument]
+ end
+
+ it "invokes method_missing with the method name and the specified arguments" do
+ @m = MethodSpecs::Methods.new
+ meth = @m.method(:handled_via_method_missing)
+
+ @m.should_receive(:method_missing).with(:handled_via_method_missing, :argument)
+ meth.send(@method, :argument)
+ end
+
+ it "invokes method_missing dynamically" do
+ @m = MethodSpecs::Methods.new
+ meth = @m.method(:handled_via_method_missing)
+
+ def @m.method_missing(*); :changed; end
+ meth.send(@method, :argument).should == :changed
+ end
+
+ it "does not call the original method name even if it now exists" do
+ @m = MethodSpecs::Methods.new
+ meth = @m.method(:handled_via_method_missing)
+
+ def @m.handled_via_method_missing(*); :not_called; end
+ meth.send(@method, :argument).should == [:argument]
+ end
+ end
+end
diff --git a/spec/ruby/core/method/shared/eql.rb b/spec/ruby/core/method/shared/eql.rb
new file mode 100644
index 0000000000..5c720cbac1
--- /dev/null
+++ b/spec/ruby/core/method/shared/eql.rb
@@ -0,0 +1,94 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :method_equal, shared: true do
+ before :each do
+ @m = MethodSpecs::Methods.new
+ @m_foo = @m.method(:foo)
+ @m2 = MethodSpecs::Methods.new
+ @a = MethodSpecs::A.new
+ end
+
+ it "returns true if methods are the same" do
+ m2 = @m.method(:foo)
+
+ @m_foo.send(@method, @m_foo).should be_true
+ @m_foo.send(@method, m2).should be_true
+ end
+
+ it "returns true on aliased methods" do
+ m_bar = @m.method(:bar)
+
+ m_bar.send(@method, @m_foo).should be_true
+ end
+
+ it "returns true if the two core methods are aliases" do
+ s = "hello"
+ a = s.method(:size)
+ b = s.method(:length)
+ a.send(@method, b).should be_true
+ end
+
+ it "returns false on a method which is neither aliased nor the same method" do
+ m2 = @m.method(:zero)
+
+ @m_foo.send(@method, m2).should be_false
+ end
+
+ it "returns false for a method which is not bound to the same object" do
+ m2_foo = @m2.method(:foo)
+ a_baz = @a.method(:baz)
+
+ @m_foo.send(@method, m2_foo).should be_false
+ @m_foo.send(@method, a_baz).should be_false
+ end
+
+ it "returns false if the two methods are bound to the same object but were defined independently" do
+ m2 = @m.method(:same_as_foo)
+ @m_foo.send(@method, m2).should be_false
+ end
+
+ it "returns true if a method was defined using the other one" do
+ MethodSpecs::Methods.send :define_method, :defined_foo, MethodSpecs::Methods.instance_method(:foo)
+ m2 = @m.method(:defined_foo)
+ @m_foo.send(@method, m2).should be_true
+ end
+
+ it "returns false if comparing a method defined via define_method and def" do
+ defn = @m.method(:zero)
+ defined = @m.method(:zero_defined_method)
+
+ defn.send(@method, defined).should be_false
+ defined.send(@method, defn).should be_false
+ end
+
+ describe 'missing methods' do
+ it "returns true for the same method missing" do
+ miss1 = @m.method(:handled_via_method_missing)
+ miss1bis = @m.method(:handled_via_method_missing)
+ miss2 = @m.method(:also_handled)
+
+ miss1.send(@method, miss1bis).should be_true
+ miss1.send(@method, miss2).should be_false
+ end
+
+ it 'calls respond_to_missing? with true to include private methods' do
+ @m.should_receive(:respond_to_missing?).with(:some_missing_method, true).and_return(true)
+ @m.method(:some_missing_method)
+ end
+ end
+
+ it "returns false if the two methods are bound to different objects, have the same names, and identical bodies" do
+ a = MethodSpecs::Eql.instance_method(:same_body)
+ b = MethodSpecs::Eql2.instance_method(:same_body)
+ a.send(@method, b).should be_false
+ end
+
+ it "returns false if the argument is not a Method object" do
+ String.instance_method(:size).send(@method, 7).should be_false
+ end
+
+ it "returns false if the argument is an unbound version of self" do
+ method(:load).send(@method, method(:load).unbind).should be_false
+ end
+end
diff --git a/spec/ruby/core/method/shared/to_s.rb b/spec/ruby/core/method/shared/to_s.rb
new file mode 100644
index 0000000000..7666322936
--- /dev/null
+++ b/spec/ruby/core/method/shared/to_s.rb
@@ -0,0 +1,52 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :method_to_s, shared: true do
+ before :each do
+ @m = MethodSpecs::MySub.new.method :bar
+ @string = @m.send(@method).sub(/0x\w+/, '0xXXXXXX')
+ end
+
+ it "returns a String" do
+ @m.send(@method).should be_kind_of(String)
+ end
+
+ it "returns a String for methods defined with attr_accessor" do
+ m = MethodSpecs::Methods.new.method :attr
+ m.send(@method).should be_kind_of(String)
+ end
+
+ it "returns a String containing 'Method'" do
+ @string.should =~ /\bMethod\b/
+ end
+
+ it "returns a String containing the method name" do
+ @string.should =~ /\#bar/
+ end
+
+ it "returns a String containing the Module the method is defined in" do
+ @string.should =~ /MethodSpecs::MyMod/
+ end
+
+ it "returns a String containing the Module the method is referenced from" do
+ @string.should =~ /MethodSpecs::MySub/
+ end
+
+ ruby_version_is '2.8' do
+ it "returns a String containing the Module containing the method if object has a singleton class but method is not defined in the singleton class" do
+ obj = MethodSpecs::MySub.new
+ obj.singleton_class
+ @m = obj.method(:bar)
+ @string = @m.send(@method).sub(/0x\w+/, '0xXXXXXX')
+ @string.should =~ /\A#<Method: MethodSpecs::MySub\(MethodSpecs::MyMod\)#bar\(\) /
+ end
+ end
+
+ it "returns a String containing the singleton class if method is defined in the singleton class" do
+ obj = MethodSpecs::MySub.new
+ def obj.bar; end
+ @m = obj.method(:bar)
+ @string = @m.send(@method).sub(/0x\w+/, '0xXXXXXX')
+ @string.should =~ /\A#<Method: #<MethodSpecs::MySub:0xXXXXXX>\.bar/
+ end
+end
diff --git a/spec/ruby/core/method/source_location_spec.rb b/spec/ruby/core/method/source_location_spec.rb
new file mode 100644
index 0000000000..dd81b02c77
--- /dev/null
+++ b/spec/ruby/core/method/source_location_spec.rb
@@ -0,0 +1,95 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#source_location" do
+ before :each do
+ @method = MethodSpecs::SourceLocation.method(:location)
+ end
+
+ it "returns an Array" do
+ @method.source_location.should be_an_instance_of(Array)
+ end
+
+ it "sets the first value to the path of the file in which the method was defined" do
+ file = @method.source_location.first
+ file.should be_an_instance_of(String)
+ file.should == File.realpath('../fixtures/classes.rb', __FILE__)
+ end
+
+ it "sets the last value to a Fixnum representing the line on which the method was defined" do
+ line = @method.source_location.last
+ line.should be_an_instance_of(Fixnum)
+ line.should == 5
+ end
+
+ it "returns the last place the method was defined" do
+ MethodSpecs::SourceLocation.method(:redefined).source_location.last.should == 13
+ end
+
+ it "returns the location of the original method even if it was aliased" do
+ MethodSpecs::SourceLocation.new.method(:aka).source_location.last.should == 17
+ end
+
+ it "works for methods defined with a block" do
+ line = nil
+ klass = Class.new do
+ line = __LINE__ + 1
+ define_method(:f) { }
+ end
+
+ method = klass.new.method(:f)
+ method.source_location[0].should =~ /#{__FILE__}/
+ method.source_location[1].should == line
+ end
+
+ it "works for methods defined with a Method" do
+ line = nil
+ klass = Class.new do
+ line = __LINE__ + 1
+ def f
+ end
+ define_method :g, new.method(:f)
+ end
+
+ method = klass.new.method(:g)
+ method.source_location[0].should =~ /#{__FILE__}/
+ method.source_location[1].should == line
+ end
+
+ it "works for methods defined with an UnboundMethod" do
+ line = nil
+ klass = Class.new do
+ line = __LINE__ + 1
+ def f
+ end
+ define_method :g, instance_method(:f)
+ end
+
+ method = klass.new.method(:g)
+ method.source_location[0].should =~ /#{__FILE__}/
+ method.source_location[1].should == line
+ end
+
+ it "works for methods whose visibility has been overridden in a subclass" do
+ line = nil
+ superclass = Class.new do
+ line = __LINE__ + 1
+ def f
+ end
+ end
+ subclass = Class.new(superclass) do
+ private :f
+ end
+
+ method = subclass.new.method(:f)
+ method.source_location[0].should =~ /#{__FILE__}/
+ method.source_location[1].should == line
+ end
+
+ describe "for a Method generated by respond_to_missing?" do
+ it "returns nil" do
+ m = MethodSpecs::Methods.new
+ m.method(:handled_via_method_missing).source_location.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/method/super_method_spec.rb b/spec/ruby/core/method/super_method_spec.rb
new file mode 100644
index 0000000000..e5d8b87a06
--- /dev/null
+++ b/spec/ruby/core/method/super_method_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#super_method" do
+ it "returns the method that would be called by super in the method" do
+ obj = MethodSpecs::C.new
+ obj.extend MethodSpecs::OverrideAgain
+ meth = obj.method(:overridden)
+
+ s_meth = meth.super_method
+ s_meth.owner.should == MethodSpecs::C
+ s_meth.receiver.should == obj
+ s_meth.name.should == :overridden
+
+ ss_meth = meth.super_method.super_method
+ ss_meth.owner.should == MethodSpecs::BetweenBAndC
+ ss_meth.receiver.should == obj
+ ss_meth.name.should == :overridden
+
+ sss_meth = meth.super_method.super_method.super_method
+ sss_meth.owner.should == MethodSpecs::B
+ sss_meth.receiver.should == obj
+ sss_meth.name.should == :overridden
+ end
+
+ it "returns nil when there's no super method in the parent" do
+ method = Object.new.method(:method)
+ method.super_method.should == nil
+ end
+
+ it "returns nil when the parent's method is removed" do
+ klass = Class.new do
+ def overridden; end
+ end
+ sub = Class.new(klass) do
+ def overridden; end
+ end
+ object = sub.new
+ method = object.method(:overridden)
+
+ klass.class_eval { undef :overridden }
+
+ method.super_method.should == nil
+ end
+end
diff --git a/spec/ruby/core/method/to_proc_spec.rb b/spec/ruby/core/method/to_proc_spec.rb
new file mode 100644
index 0000000000..92bd64aaf1
--- /dev/null
+++ b/spec/ruby/core/method/to_proc_spec.rb
@@ -0,0 +1,93 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#to_proc" do
+ before :each do
+ ScratchPad.record []
+
+ @m = MethodSpecs::Methods.new
+ @meth = @m.method(:foo)
+ end
+
+ it "returns a Proc object corresponding to the method" do
+ @meth.to_proc.kind_of?(Proc).should == true
+ end
+
+ it "returns a Proc which does not depends on the value of self" do
+ 3.instance_exec(4, &5.method(:+)).should == 9
+ end
+
+
+ it "returns a Proc object with the correct arity" do
+ # This may seem redundant but this bug has cropped up in jruby, mri and yarv.
+ # http://jira.codehaus.org/browse/JRUBY-124
+ [ :zero, :one_req, :two_req,
+ :zero_with_block, :one_req_with_block, :two_req_with_block,
+ :one_opt, :one_req_one_opt, :one_req_two_opt, :two_req_one_opt,
+ :one_opt_with_block, :one_req_one_opt_with_block, :one_req_two_opt_with_block, :two_req_one_opt_with_block,
+ :zero_with_splat, :one_req_with_splat, :two_req_with_splat,
+ :one_req_one_opt_with_splat, :one_req_two_opt_with_splat, :two_req_one_opt_with_splat,
+ :zero_with_splat_and_block, :one_req_with_splat_and_block, :two_req_with_splat_and_block,
+ :one_req_one_opt_with_splat_and_block, :one_req_two_opt_with_splat_and_block, :two_req_one_opt_with_splat_and_block
+ ].each do |m|
+ @m.method(m).to_proc.arity.should == @m.method(m).arity
+ end
+ end
+
+ it "returns a proc that can be used by define_method" do
+ x = 'test'
+ to_s = class << x
+ define_method :foo, method(:to_s).to_proc
+ to_s
+ end
+
+ x.foo.should == to_s
+ end
+
+ it "returns a proc that can be yielded to" do
+ x = Object.new
+ def x.foo(*a); a; end
+ def x.bar; yield; end
+ def x.baz(*a); yield(*a); end
+
+ m = x.method :foo
+ x.bar(&m).should == []
+ x.baz(1,2,3,&m).should == [1,2,3]
+ end
+
+ it "returns a proc whose binding has the same receiver as the method" do
+ @meth.receiver.should == @meth.to_proc.binding.receiver
+ end
+
+ # #5926
+ it "returns a proc that can receive a block" do
+ x = Object.new
+ def x.foo; yield 'bar'; end
+
+ m = x.method :foo
+ result = nil
+ m.to_proc.call {|val| result = val}
+ result.should == 'bar'
+ end
+
+ it "can be called directly and not unwrap arguments like a block" do
+ obj = MethodSpecs::ToProcBeta.new
+ obj.to_proc.call([1]).should == [1]
+ end
+
+ it "should correct handle arguments (unwrap)" do
+ obj = MethodSpecs::ToProcBeta.new
+
+ array = [[1]]
+ array.each(&obj)
+ ScratchPad.recorded.should == [[1]]
+ end
+
+ it "executes method with whole array (one argument)" do
+ obj = MethodSpecs::ToProcBeta.new
+
+ array = [[1, 2]]
+ array.each(&obj)
+ ScratchPad.recorded.should == [[1, 2]]
+ end
+end
diff --git a/spec/ruby/core/method/to_s_spec.rb b/spec/ruby/core/method/to_s_spec.rb
new file mode 100644
index 0000000000..9f19011302
--- /dev/null
+++ b/spec/ruby/core/method/to_s_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_s'
+
+describe "Method#to_s" do
+ it_behaves_like :method_to_s, :to_s
+end
diff --git a/spec/ruby/core/method/unbind_spec.rb b/spec/ruby/core/method/unbind_spec.rb
new file mode 100644
index 0000000000..3d36c6d675
--- /dev/null
+++ b/spec/ruby/core/method/unbind_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Method#unbind" do
+ before :each do
+ @normal = MethodSpecs::Methods.new
+ @normal_m = @normal.method :foo
+ @normal_um = @normal_m.unbind
+ @pop_um = MethodSpecs::MySub.new.method(:bar).unbind
+ @string = @pop_um.inspect.sub(/0x\w+/, '0xXXXXXX')
+ end
+
+ it "returns an UnboundMethod" do
+ @normal_um.should be_kind_of(UnboundMethod)
+ end
+
+ it "returns a String containing 'UnboundMethod'" do
+ @string.should =~ /\bUnboundMethod\b/
+ end
+
+ it "returns a String containing the method name" do
+ @string.should =~ /\#bar/
+ end
+
+ it "returns a String containing the Module the method is defined in" do
+ @string.should =~ /MethodSpecs::MyMod/
+ end
+
+ it "returns a String containing the Module the method is referenced from" do
+ @string.should =~ /MethodSpecs::MySub/
+ end
+
+ specify "rebinding UnboundMethod to Method's obj produces exactly equivalent Methods" do
+ @normal_um.bind(@normal).should == @normal_m
+ @normal_m.should == @normal_um.bind(@normal)
+ end
+end
diff --git a/spec/ruby/core/module/alias_method_spec.rb b/spec/ruby/core/module/alias_method_spec.rb
new file mode 100644
index 0000000000..8a2ddafda1
--- /dev/null
+++ b/spec/ruby/core/module/alias_method_spec.rb
@@ -0,0 +1,157 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#alias_method" do
+ before :each do
+ @class = Class.new(ModuleSpecs::Aliasing)
+ @object = @class.new
+ end
+
+ it "makes a copy of the method" do
+ @class.make_alias :uno, :public_one
+ @class.make_alias :double, :public_two
+ @object.uno.should == @object.public_one
+ @object.double(12).should == @object.public_two(12)
+ end
+
+ it "creates methods that are == to eachother" do
+ @class.make_alias :uno, :public_one
+ @object.method(:uno).should == @object.method(:public_one)
+ end
+
+ it "preserves the arguments information of the original methods" do
+ @class.make_alias :uno, :public_one
+ @class.make_alias :double, :public_two
+ @class.instance_method(:uno).parameters.should == @class.instance_method(:public_one).parameters
+ @class.instance_method(:double).parameters.should == @class.instance_method(:public_two).parameters
+ end
+
+ it "retains method visibility" do
+ @class.make_alias :private_ichi, :private_one
+ lambda { @object.private_one }.should raise_error(NameError)
+ lambda { @object.private_ichi }.should raise_error(NameError)
+ @class.make_alias :public_ichi, :public_one
+ @object.public_ichi.should == @object.public_one
+ @class.make_alias :protected_ichi, :protected_one
+ lambda { @object.protected_ichi }.should raise_error(NameError)
+ end
+
+ it "handles aliasing a stub that changes visibility" do
+ @class.__send__ :public, :private_one
+ @class.make_alias :was_private_one, :private_one
+ @object.was_private_one.should == 1
+ end
+
+ it "fails if origin method not found" do
+ lambda { @class.make_alias :ni, :san }.should raise_error(NameError) { |e|
+ # a NameError and not a NoMethodError
+ e.class.should == NameError
+ }
+ end
+
+ it "raises #{frozen_error_class} if frozen" do
+ @class.freeze
+ lambda { @class.make_alias :uno, :public_one }.should raise_error(frozen_error_class)
+ end
+
+ it "converts the names using #to_str" do
+ @class.make_alias "un", "public_one"
+ @class.make_alias :deux, "public_one"
+ @class.make_alias "trois", :public_one
+ @class.make_alias :quatre, :public_one
+ name = mock('cinq')
+ name.should_receive(:to_str).any_number_of_times.and_return("cinq")
+ @class.make_alias name, "public_one"
+ @class.make_alias "cinq", name
+ end
+
+ it "raises a TypeError when the given name can't be converted using to_str" do
+ lambda { @class.make_alias mock('x'), :public_one }.should raise_error(TypeError)
+ end
+
+ ruby_version_is ''...'2.5' do
+ it "is a private method" do
+ lambda { @class.alias_method :ichi, :public_one }.should raise_error(NoMethodError)
+ end
+ end
+ ruby_version_is '2.5' do
+ it "is a public method" do
+ Module.should have_public_instance_method(:alias_method, false)
+ end
+ end
+
+ it "returns self" do
+ @class.send(:alias_method, :checking_return_value, :public_one).should equal(@class)
+ end
+
+ it "works in module" do
+ ModuleSpecs::Allonym.new.publish.should == :report
+ end
+
+ it "works on private module methods in a module that has been reopened" do
+ ModuleSpecs::ReopeningModule.foo.should == true
+ lambda { ModuleSpecs::ReopeningModule.foo2 }.should_not raise_error(NoMethodError)
+ end
+
+ it "accesses a method defined on Object from Kernel" do
+ Kernel.should_not have_public_instance_method(:module_specs_public_method_on_object)
+
+ Kernel.should have_public_instance_method(:module_specs_alias_on_kernel)
+ Object.should have_public_instance_method(:module_specs_alias_on_kernel)
+ end
+
+ it "can call a method with super aliased twice" do
+ ModuleSpecs::AliasingSuper::Target.new.super_call(1).should == 1
+ end
+
+ it "preserves original super call after alias redefine" do
+ ModuleSpecs::AliasingSuper::RedefineAfterAlias.new.alias_super_call(1).should == 1
+ end
+
+ describe "aliasing special methods" do
+ before :all do
+ @class = ModuleSpecs::Aliasing
+ @subclass = ModuleSpecs::AliasingSubclass
+ end
+
+ it "keeps initialize private when aliasing" do
+ @class.make_alias(:initialize, :public_one)
+ @class.private_instance_methods.include?(:initialize).should be_true
+
+ @subclass.make_alias(:initialize, :public_one)
+ @subclass.private_instance_methods.include?(:initialize).should be_true
+ end
+
+ it "keeps initialize_copy private when aliasing" do
+ @class.make_alias(:initialize_copy, :public_one)
+ @class.private_instance_methods.include?(:initialize_copy).should be_true
+
+ @subclass.make_alias(:initialize_copy, :public_one)
+ @subclass.private_instance_methods.include?(:initialize_copy).should be_true
+ end
+
+ it "keeps initialize_clone private when aliasing" do
+ @class.make_alias(:initialize_clone, :public_one)
+ @class.private_instance_methods.include?(:initialize_clone).should be_true
+
+ @subclass.make_alias(:initialize_clone, :public_one)
+ @subclass.private_instance_methods.include?(:initialize_clone).should be_true
+ end
+
+ it "keeps initialize_dup private when aliasing" do
+ @class.make_alias(:initialize_dup, :public_one)
+ @class.private_instance_methods.include?(:initialize_dup).should be_true
+
+ @subclass.make_alias(:initialize_dup, :public_one)
+ @subclass.private_instance_methods.include?(:initialize_dup).should be_true
+ end
+
+ it "keeps respond_to_missing? private when aliasing" do
+ @class.make_alias(:respond_to_missing?, :public_one)
+ @class.private_instance_methods.include?(:respond_to_missing?).should be_true
+
+ @subclass.make_alias(:respond_to_missing?, :public_one)
+ @subclass.private_instance_methods.include?(:respond_to_missing?).should be_true
+ end
+ end
+end
diff --git a/spec/ruby/core/module/allocate_spec.rb b/spec/ruby/core/module/allocate_spec.rb
new file mode 100644
index 0000000000..3b2c4119c3
--- /dev/null
+++ b/spec/ruby/core/module/allocate_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe "Module.allocate" do
+ it "returns an instance of Module" do
+ mod = Module.allocate
+ mod.should be_an_instance_of(Module)
+ end
+
+ it "returns a fully-formed instance of Module" do
+ mod = Module.allocate
+ mod.constants.should_not == nil
+ mod.methods.should_not == nil
+ end
+end
diff --git a/spec/ruby/core/module/ancestors_spec.rb b/spec/ruby/core/module/ancestors_spec.rb
new file mode 100644
index 0000000000..5e4c196206
--- /dev/null
+++ b/spec/ruby/core/module/ancestors_spec.rb
@@ -0,0 +1,70 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#ancestors" do
+ it "returns a list of modules included in self (including self)" do
+ BasicObject.ancestors.should == [BasicObject]
+ ModuleSpecs.ancestors.should == [ModuleSpecs]
+ ModuleSpecs::Basic.ancestors.should == [ModuleSpecs::Basic]
+ ModuleSpecs::Super.ancestors.should == [ModuleSpecs::Super, ModuleSpecs::Basic]
+ ModuleSpecs.without_test_modules(ModuleSpecs::Parent.ancestors).should ==
+ [ModuleSpecs::Parent, Object, Kernel, BasicObject]
+ ModuleSpecs.without_test_modules(ModuleSpecs::Child.ancestors).should ==
+ [ModuleSpecs::Child, ModuleSpecs::Super, ModuleSpecs::Basic, ModuleSpecs::Parent, Object, Kernel, BasicObject]
+ end
+
+ it "returns only modules and classes" do
+ class << ModuleSpecs::Child; self; end.ancestors.should include(ModuleSpecs::Internal, Class, Module, Object, Kernel)
+ end
+
+ it "has 1 entry per module or class" do
+ ModuleSpecs::Parent.ancestors.should == ModuleSpecs::Parent.ancestors.uniq
+ end
+
+ describe "when called on a singleton class" do
+ it "includes the singleton classes of ancestors" do
+ parent = Class.new
+ child = Class.new(parent)
+ schild = child.singleton_class
+
+ schild.ancestors.should include(schild,
+ parent.singleton_class,
+ Object.singleton_class,
+ BasicObject.singleton_class,
+ Class,
+ Module,
+ Object,
+ Kernel,
+ BasicObject)
+
+ end
+
+ describe 'for a standalone module' do
+ it 'does not include Class' do
+ s_mod = ModuleSpecs.singleton_class
+ s_mod.ancestors.should_not include(Class)
+ end
+
+ it 'does not include other singleton classes' do
+ s_standalone_mod = ModuleSpecs.singleton_class
+ s_module = Module.singleton_class
+ s_object = Object.singleton_class
+ s_basic_object = BasicObject.singleton_class
+
+ s_standalone_mod.ancestors.should_not include(s_module, s_object, s_basic_object)
+ end
+
+ it 'includes its own singleton class' do
+ s_mod = ModuleSpecs.singleton_class
+
+ s_mod.ancestors.should include(s_mod)
+ end
+
+ it 'includes standard chain' do
+ s_mod = ModuleSpecs.singleton_class
+
+ s_mod.ancestors.should include(Module, Object, Kernel, BasicObject)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/module/append_features_spec.rb b/spec/ruby/core/module/append_features_spec.rb
new file mode 100644
index 0000000000..c5dd64aa6f
--- /dev/null
+++ b/spec/ruby/core/module/append_features_spec.rb
@@ -0,0 +1,73 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#append_features" do
+ it "is a private method" do
+ Module.should have_private_instance_method(:append_features)
+ end
+
+ describe "on Class" do
+ it "is undefined" do
+ Class.should_not have_private_instance_method(:append_features, true)
+ end
+
+ it "raises a TypeError if calling after rebinded to Class" do
+ lambda {
+ Module.instance_method(:append_features).bind(Class.new).call Module.new
+ }.should raise_error(TypeError)
+ end
+ end
+
+ it "gets called when self is included in another module/class" do
+ begin
+ m = Module.new do
+ def self.append_features(mod)
+ $appended_to = mod
+ end
+ end
+
+ c = Class.new do
+ include m
+ end
+
+ $appended_to.should == c
+ ensure
+ $appended_to = nil
+ end
+ end
+
+ it "raises an ArgumentError on a cyclic include" do
+ lambda {
+ ModuleSpecs::CyclicAppendA.send(:append_features, ModuleSpecs::CyclicAppendA)
+ }.should raise_error(ArgumentError)
+
+ lambda {
+ ModuleSpecs::CyclicAppendB.send(:append_features, ModuleSpecs::CyclicAppendA)
+ }.should raise_error(ArgumentError)
+
+ end
+
+ it "copies own tainted status to the given module" do
+ other = Module.new
+ Module.new.taint.send :append_features, other
+ other.tainted?.should be_true
+ end
+
+ it "copies own untrusted status to the given module" do
+ other = Module.new
+ Module.new.untrust.send :append_features, other
+ other.untrusted?.should be_true
+ end
+
+ describe "when other is frozen" do
+ before :each do
+ @receiver = Module.new
+ @other = Module.new.freeze
+ end
+
+ it "raises a #{frozen_error_class} before appending self" do
+ lambda { @receiver.send(:append_features, @other) }.should raise_error(frozen_error_class)
+ @other.ancestors.should_not include(@receiver)
+ end
+ end
+end
diff --git a/spec/ruby/core/module/attr_accessor_spec.rb b/spec/ruby/core/module/attr_accessor_spec.rb
new file mode 100644
index 0000000000..b5e3d72524
--- /dev/null
+++ b/spec/ruby/core/module/attr_accessor_spec.rb
@@ -0,0 +1,97 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#attr_accessor" do
+ it "creates a getter and setter for each given attribute name" do
+ c = Class.new do
+ attr_accessor :a, "b"
+ end
+
+ o = c.new
+
+ ['a','b'].each do |x|
+ o.respond_to?(x).should == true
+ o.respond_to?("#{x}=").should == true
+ end
+
+ o.a = "a"
+ o.a.should == "a"
+
+ o.b = "b"
+ o.b.should == "b"
+ o.a = o.b = nil
+
+ o.send(:a=,"a")
+ o.send(:a).should == "a"
+
+ o.send(:b=, "b")
+ o.send(:b).should == "b"
+ end
+
+ it "not allows creating an attr_accessor on an immediate class" do
+ class TrueClass
+ attr_accessor :spec_attr_accessor
+ end
+
+ lambda { true.spec_attr_accessor = "a" }.should raise_error(RuntimeError)
+ end
+
+ it "converts non string/symbol/fixnum names to strings using to_str" do
+ (o = mock('test')).should_receive(:to_str).any_number_of_times.and_return("test")
+ c = Class.new do
+ attr_accessor o
+ end
+
+ c.new.respond_to?("test").should == true
+ c.new.respond_to?("test=").should == true
+ end
+
+ it "raises a TypeError when the given names can't be converted to strings using to_str" do
+ o = mock('o')
+ lambda { Class.new { attr_accessor o } }.should raise_error(TypeError)
+ (o = mock('123')).should_receive(:to_str).and_return(123)
+ lambda { Class.new { attr_accessor o } }.should raise_error(TypeError)
+ end
+
+ it "applies current visibility to methods created" do
+ c = Class.new do
+ protected
+ attr_accessor :foo
+ end
+
+ lambda { c.new.foo }.should raise_error(NoMethodError)
+ lambda { c.new.foo=1 }.should raise_error(NoMethodError)
+ end
+
+ ruby_version_is ''...'2.5' do
+ it "is a private method" do
+ Module.should have_private_instance_method(:attr_accessor, false)
+ end
+ end
+ ruby_version_is '2.5' do
+ it "is a public method" do
+ Module.should have_public_instance_method(:attr_accessor, false)
+ end
+ end
+
+ describe "on immediates" do
+ before :each do
+ class Fixnum
+ attr_accessor :foobar
+ end
+ end
+
+ after :each do
+ if Fixnum.method_defined?(:foobar)
+ Fixnum.send(:remove_method, :foobar)
+ end
+ if Fixnum.method_defined?(:foobar=)
+ Fixnum.send(:remove_method, :foobar=)
+ end
+ end
+
+ it "can read through the accessor" do
+ 1.foobar.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/module/attr_reader_spec.rb b/spec/ruby/core/module/attr_reader_spec.rb
new file mode 100644
index 0000000000..f537b5f1c6
--- /dev/null
+++ b/spec/ruby/core/module/attr_reader_spec.rb
@@ -0,0 +1,71 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#attr_reader" do
+ it "creates a getter for each given attribute name" do
+ c = Class.new do
+ attr_reader :a, "b"
+
+ def initialize
+ @a = "test"
+ @b = "test2"
+ end
+ end
+
+ o = c.new
+ %w{a b}.each do |x|
+ o.respond_to?(x).should == true
+ o.respond_to?("#{x}=").should == false
+ end
+
+ o.a.should == "test"
+ o.b.should == "test2"
+ o.send(:a).should == "test"
+ o.send(:b).should == "test2"
+ end
+
+ it "not allows for adding an attr_reader to an immediate" do
+ class TrueClass
+ attr_reader :spec_attr_reader
+ end
+
+ lambda { true.instance_variable_set("@spec_attr_reader", "a") }.should raise_error(RuntimeError)
+ end
+
+ it "converts non string/symbol/fixnum names to strings using to_str" do
+ (o = mock('test')).should_receive(:to_str).any_number_of_times.and_return("test")
+ c = Class.new do
+ attr_reader o
+ end
+
+ c.new.respond_to?("test").should == true
+ c.new.respond_to?("test=").should == false
+ end
+
+ it "raises a TypeError when the given names can't be converted to strings using to_str" do
+ o = mock('o')
+ lambda { Class.new { attr_reader o } }.should raise_error(TypeError)
+ (o = mock('123')).should_receive(:to_str).and_return(123)
+ lambda { Class.new { attr_reader o } }.should raise_error(TypeError)
+ end
+
+ it "applies current visibility to methods created" do
+ c = Class.new do
+ protected
+ attr_reader :foo
+ end
+
+ lambda { c.new.foo }.should raise_error(NoMethodError)
+ end
+
+ ruby_version_is ''...'2.5' do
+ it "is a private method" do
+ Module.should have_private_instance_method(:attr_reader, false)
+ end
+ end
+ ruby_version_is '2.5' do
+ it "is a public method" do
+ Module.should have_public_instance_method(:attr_reader, false)
+ end
+ end
+end
diff --git a/spec/ruby/core/module/attr_spec.rb b/spec/ruby/core/module/attr_spec.rb
new file mode 100644
index 0000000000..87bf8ea57f
--- /dev/null
+++ b/spec/ruby/core/module/attr_spec.rb
@@ -0,0 +1,156 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#attr" do
+ before :each do
+ $VERBOSE, @verbose = false, $VERBOSE
+ end
+
+ after :each do
+ $VERBOSE = @verbose
+ end
+
+ it "creates a getter for the given attribute name" do
+ c = Class.new do
+ attr :attr
+ attr "attr3"
+
+ def initialize
+ @attr, @attr2, @attr3 = "test", "test2", "test3"
+ end
+ end
+
+ o = c.new
+
+ %w{attr attr3}.each do |a|
+ o.respond_to?(a).should == true
+ o.respond_to?("#{a}=").should == false
+ end
+
+ o.attr.should == "test"
+ o.attr3.should == "test3"
+ o.send(:attr).should == "test"
+ o.send(:attr3).should == "test3"
+ end
+
+ it "creates a setter for the given attribute name if writable is true" do
+ c = Class.new do
+ attr :attr, true
+ attr "attr3", true
+
+ def initialize
+ @attr, @attr2, @attr3 = "test", "test2", "test3"
+ end
+ end
+
+ o = c.new
+
+ %w{attr attr3}.each do |a|
+ o.respond_to?(a).should == true
+ o.respond_to?("#{a}=").should == true
+ end
+
+ o.attr = "test updated"
+ o.attr3 = "test3 updated"
+ end
+
+ it "creates a getter and setter for the given attribute name if called with and without writeable is true" do
+ c = Class.new do
+ attr :attr, true
+ attr :attr
+
+ attr "attr3", true
+ attr "attr3"
+
+ def initialize
+ @attr, @attr2, @attr3 = "test", "test2", "test3"
+ end
+ end
+
+ o = c.new
+
+ %w{attr attr3}.each do |a|
+ o.respond_to?(a).should == true
+ o.respond_to?("#{a}=").should == true
+ end
+
+ o.attr.should == "test"
+ o.attr = "test updated"
+ o.attr.should == "test updated"
+
+ o.attr3.should == "test3"
+ o.attr3 = "test3 updated"
+ o.attr3.should == "test3 updated"
+ end
+
+ it "applies current visibility to methods created" do
+ c = Class.new do
+ protected
+ attr :foo, true
+ end
+
+ lambda { c.new.foo }.should raise_error(NoMethodError)
+ lambda { c.new.foo=1 }.should raise_error(NoMethodError)
+ end
+
+ it "creates a getter but no setter for all given attribute names" do
+ c = Class.new do
+ attr :attr, "attr2", "attr3"
+
+ def initialize
+ @attr, @attr2, @attr3 = "test", "test2", "test3"
+ end
+ end
+
+ o = c.new
+
+ %w{attr attr2 attr3}.each do |a|
+ o.respond_to?(a).should == true
+ o.respond_to?("#{a}=").should == false
+ end
+
+ o.attr.should == "test"
+ o.attr2.should == "test2"
+ o.attr3.should == "test3"
+ end
+
+ it "applies current visibility to methods created" do
+ c = Class.new do
+ protected
+ attr :foo, :bar
+ end
+
+ lambda { c.new.foo }.should raise_error(NoMethodError)
+ lambda { c.new.bar }.should raise_error(NoMethodError)
+ end
+
+ it "converts non string/symbol/fixnum names to strings using to_str" do
+ (o = mock('test')).should_receive(:to_str).any_number_of_times.and_return("test")
+ Class.new { attr o }.new.respond_to?("test").should == true
+ end
+
+ it "raises a TypeError when the given names can't be converted to strings using to_str" do
+ o = mock('o')
+ lambda { Class.new { attr o } }.should raise_error(TypeError)
+ (o = mock('123')).should_receive(:to_str).and_return(123)
+ lambda { Class.new { attr o } }.should raise_error(TypeError)
+ end
+
+ it "with a boolean argument emits a warning when $VERBOSE is true" do
+ lambda {
+ $VERBOSE = true
+ Class.new { attr :foo, true }
+ }.should complain(/boolean argument is obsoleted/)
+ end
+
+ ruby_version_is ''...'2.5' do
+ it "is a private method" do
+ Module.should have_private_instance_method(:attr, false)
+ end
+ end
+ ruby_version_is '2.5' do
+ it "is a public method" do
+ Module.should have_public_instance_method(:attr, false)
+ end
+ end
+end
diff --git a/spec/ruby/core/module/attr_writer_spec.rb b/spec/ruby/core/module/attr_writer_spec.rb
new file mode 100644
index 0000000000..ea77f81f28
--- /dev/null
+++ b/spec/ruby/core/module/attr_writer_spec.rb
@@ -0,0 +1,71 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#attr_writer" do
+ it "creates a setter for each given attribute name" do
+ c = Class.new do
+ attr_writer :test1, "test2"
+ end
+ o = c.new
+
+ o.respond_to?("test1").should == false
+ o.respond_to?("test2").should == false
+
+ o.respond_to?("test1=").should == true
+ o.test1 = "test_1"
+ o.instance_variable_get(:@test1).should == "test_1"
+
+ o.respond_to?("test2=").should == true
+ o.test2 = "test_2"
+ o.instance_variable_get(:@test2).should == "test_2"
+ o.send(:test1=,"test_1 updated")
+ o.instance_variable_get(:@test1).should == "test_1 updated"
+ o.send(:test2=,"test_2 updated")
+ o.instance_variable_get(:@test2).should == "test_2 updated"
+ end
+
+ it "not allows for adding an attr_writer to an immediate" do
+ class TrueClass
+ attr_writer :spec_attr_writer
+ end
+
+ lambda { true.spec_attr_writer = "a" }.should raise_error(RuntimeError)
+ end
+
+ it "converts non string/symbol/fixnum names to strings using to_str" do
+ (o = mock('test')).should_receive(:to_str).any_number_of_times.and_return("test")
+ c = Class.new do
+ attr_writer o
+ end
+
+ c.new.respond_to?("test").should == false
+ c.new.respond_to?("test=").should == true
+ end
+
+ it "raises a TypeError when the given names can't be converted to strings using to_str" do
+ o = mock('test1')
+ lambda { Class.new { attr_writer o } }.should raise_error(TypeError)
+ (o = mock('123')).should_receive(:to_str).and_return(123)
+ lambda { Class.new { attr_writer o } }.should raise_error(TypeError)
+ end
+
+ it "applies current visibility to methods created" do
+ c = Class.new do
+ protected
+ attr_writer :foo
+ end
+
+ lambda { c.new.foo=1 }.should raise_error(NoMethodError)
+ end
+
+ ruby_version_is ''...'2.5' do
+ it "is a private method" do
+ Module.should have_private_instance_method(:attr_writer, false)
+ end
+ end
+ ruby_version_is '2.5' do
+ it "is a public method" do
+ Module.should have_public_instance_method(:attr_writer, false)
+ end
+ end
+end
diff --git a/spec/ruby/core/module/autoload_spec.rb b/spec/ruby/core/module/autoload_spec.rb
new file mode 100644
index 0000000000..ba8132100b
--- /dev/null
+++ b/spec/ruby/core/module/autoload_spec.rb
@@ -0,0 +1,830 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'thread'
+
+describe "Module#autoload?" do
+ it "returns the name of the file that will be autoloaded" do
+ ModuleSpecs::Autoload.autoload :Autoload, "autoload.rb"
+ ModuleSpecs::Autoload.autoload?(:Autoload).should == "autoload.rb"
+ end
+
+ it "returns nil if no file has been registered for a constant" do
+ ModuleSpecs::Autoload.autoload?(:Manualload).should be_nil
+ end
+end
+
+describe "Module#autoload" do
+ before :all do
+ @non_existent = fixture __FILE__, "no_autoload.rb"
+ end
+
+ before :each do
+ @loaded_features = $".dup
+ @frozen_module = Module.new.freeze
+
+ ScratchPad.clear
+ end
+
+ after :each do
+ $".replace @loaded_features
+ end
+
+ it "registers a file to load the first time the named constant is accessed" do
+ ModuleSpecs::Autoload.autoload :A, @non_existent
+ ModuleSpecs::Autoload.autoload?(:A).should == @non_existent
+ end
+
+ it "sets the autoload constant in the constants table" do
+ ModuleSpecs::Autoload.autoload :B, @non_existent
+ ModuleSpecs::Autoload.should have_constant(:B)
+ end
+
+ it "loads the registered constant when it is accessed" do
+ ModuleSpecs::Autoload.should_not have_constant(:X)
+ ModuleSpecs::Autoload.autoload :X, fixture(__FILE__, "autoload_x.rb")
+ ModuleSpecs::Autoload::X.should == :x
+ ModuleSpecs::Autoload.send(:remove_const, :X)
+ end
+
+ it "loads the registered constant into a dynamically created class" do
+ cls = Class.new { autoload :C, fixture(__FILE__, "autoload_c.rb") }
+ ModuleSpecs::Autoload::DynClass = cls
+
+ ScratchPad.recorded.should be_nil
+ ModuleSpecs::Autoload::DynClass::C.new.loaded.should == :dynclass_c
+ ScratchPad.recorded.should == :loaded
+ end
+
+ it "loads the registered constant into a dynamically created module" do
+ mod = Module.new { autoload :D, fixture(__FILE__, "autoload_d.rb") }
+ ModuleSpecs::Autoload::DynModule = mod
+
+ ScratchPad.recorded.should be_nil
+ ModuleSpecs::Autoload::DynModule::D.new.loaded.should == :dynmodule_d
+ ScratchPad.recorded.should == :loaded
+ end
+
+ it "loads the registered constant when it is opened as a class" do
+ ModuleSpecs::Autoload.autoload :E, fixture(__FILE__, "autoload_e.rb")
+ class ModuleSpecs::Autoload::E
+ end
+ ModuleSpecs::Autoload::E.new.loaded.should == :autoload_e
+ end
+
+ it "loads the registered constant when it is opened as a module" do
+ ModuleSpecs::Autoload.autoload :F, fixture(__FILE__, "autoload_f.rb")
+ module ModuleSpecs::Autoload::F
+ end
+ ModuleSpecs::Autoload::F.loaded.should == :autoload_f
+ end
+
+ it "loads the registered constant when it is inherited from" do
+ ModuleSpecs::Autoload.autoload :G, fixture(__FILE__, "autoload_g.rb")
+ class ModuleSpecs::Autoload::Gsub < ModuleSpecs::Autoload::G
+ end
+ ModuleSpecs::Autoload::Gsub.new.loaded.should == :autoload_g
+ end
+
+ it "loads the registered constant when it is included" do
+ ModuleSpecs::Autoload.autoload :H, fixture(__FILE__, "autoload_h.rb")
+ class ModuleSpecs::Autoload::HClass
+ include ModuleSpecs::Autoload::H
+ end
+ ModuleSpecs::Autoload::HClass.new.loaded.should == :autoload_h
+ end
+
+ it "does not load the file when the constant is already set" do
+ ModuleSpecs::Autoload.autoload :I, fixture(__FILE__, "autoload_i.rb")
+ ModuleSpecs::Autoload.const_set :I, 3
+ ModuleSpecs::Autoload::I.should == 3
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "loads a file with .rb extension when passed the name without the extension" do
+ ModuleSpecs::Autoload.autoload :J, fixture(__FILE__, "autoload_j")
+ ModuleSpecs::Autoload::J.should == :autoload_j
+ end
+
+ it "calls main.require(path) to load the file" do
+ ModuleSpecs::Autoload.autoload :ModuleAutoloadCallsRequire, "module_autoload_not_exist.rb"
+ main = TOPLEVEL_BINDING.eval("self")
+ main.should_receive(:require).with("module_autoload_not_exist.rb")
+ # The constant won't be defined since require is mocked to do nothing
+ -> { ModuleSpecs::Autoload::ModuleAutoloadCallsRequire }.should raise_error(NameError)
+ end
+
+ it "does not load the file if the file is manually required" do
+ filename = fixture(__FILE__, "autoload_k.rb")
+ ModuleSpecs::Autoload.autoload :KHash, filename
+
+ require filename
+ ScratchPad.recorded.should == :loaded
+ ScratchPad.clear
+
+ ModuleSpecs::Autoload::KHash.should be_kind_of(Class)
+ ModuleSpecs::Autoload::KHash::K.should == :autoload_k
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "ignores the autoload request if the file is already loaded" do
+ filename = fixture(__FILE__, "autoload_s.rb")
+
+ require filename
+
+ ScratchPad.recorded.should == :loaded
+ ScratchPad.clear
+
+ ModuleSpecs::Autoload.autoload :S, filename
+ ModuleSpecs::Autoload.autoload?(:S).should be_nil
+ ModuleSpecs::Autoload.send(:remove_const, :S)
+ end
+
+ it "retains the autoload even if the request to require fails" do
+ filename = fixture(__FILE__, "a_path_that_should_not_exist.rb")
+
+ ModuleSpecs::Autoload.autoload :NotThere, filename
+ ModuleSpecs::Autoload.autoload?(:NotThere).should == filename
+
+ lambda {
+ require filename
+ }.should raise_error(LoadError)
+
+ ModuleSpecs::Autoload.autoload?(:NotThere).should == filename
+ end
+
+ it "allows multiple autoload constants for a single file" do
+ filename = fixture(__FILE__, "autoload_lm.rb")
+ ModuleSpecs::Autoload.autoload :L, filename
+ ModuleSpecs::Autoload.autoload :M, filename
+ ModuleSpecs::Autoload::L.should == :autoload_l
+ ModuleSpecs::Autoload::M.should == :autoload_m
+ end
+
+ it "runs for an exception condition class and doesn't trample the exception" do
+ filename = fixture(__FILE__, "autoload_ex1.rb")
+ ModuleSpecs::Autoload.autoload :EX1, filename
+ ModuleSpecs::Autoload.use_ex1.should == :good
+ end
+
+ describe "interacting with defined?" do
+ it "does not load the file when referring to the constant in defined?" do
+ module ModuleSpecs::Autoload::Dog
+ autoload :R, fixture(__FILE__, "autoload_exception.rb")
+ end
+
+ defined?(ModuleSpecs::Autoload::Dog::R).should == "constant"
+ ScratchPad.recorded.should be_nil
+
+ ModuleSpecs::Autoload::Dog.should have_constant(:R)
+ end
+
+ it "loads an autoloaded parent when referencing a nested constant" do
+ module ModuleSpecs::Autoload
+ autoload :GoodParent, fixture(__FILE__, "autoload_nested.rb")
+ end
+
+ defined?(ModuleSpecs::Autoload::GoodParent::Nested).should == 'constant'
+ ScratchPad.recorded.should == :loaded
+
+ ModuleSpecs::Autoload.send(:remove_const, :GoodParent)
+ end
+
+ it "returns nil when it fails to load an autoloaded parent when referencing a nested constant" do
+ module ModuleSpecs::Autoload
+ autoload :BadParent, fixture(__FILE__, "autoload_exception.rb")
+ end
+
+ defined?(ModuleSpecs::Autoload::BadParent::Nested).should be_nil
+ ScratchPad.recorded.should == :exception
+ end
+ end
+
+ describe "the autoload is removed when the same file is required directly without autoload" do
+ before :each do
+ module ModuleSpecs::Autoload
+ autoload :RequiredDirectly, fixture(__FILE__, "autoload_required_directly.rb")
+ end
+ @path = fixture(__FILE__, "autoload_required_directly.rb")
+ @check = -> {
+ [
+ defined?(ModuleSpecs::Autoload::RequiredDirectly),
+ ModuleSpecs::Autoload.autoload?(:RequiredDirectly)
+ ]
+ }
+ ScratchPad.record @check
+ end
+
+ after :each do
+ ModuleSpecs::Autoload.send(:remove_const, :RequiredDirectly)
+ end
+
+ it "with a full path" do
+ @check.call.should == ["constant", @path]
+ require @path
+ ScratchPad.recorded.should == [nil, nil]
+ @check.call.should == ["constant", nil]
+ end
+
+ it "with a relative path" do
+ @check.call.should == ["constant", @path]
+ $:.push File.dirname(@path)
+ begin
+ require "autoload_required_directly.rb"
+ ensure
+ $:.pop
+ end
+ ScratchPad.recorded.should == [nil, nil]
+ @check.call.should == ["constant", nil]
+ end
+
+ it "in a nested require" do
+ nested = fixture(__FILE__, "autoload_required_directly_nested.rb")
+ nested_require = -> {
+ result = nil
+ ScratchPad.record -> {
+ result = [@check.call, Thread.new { @check.call }.value]
+ }
+ require nested
+ result
+ }
+ ScratchPad.record nested_require
+
+ @check.call.should == ["constant", @path]
+ require @path
+ cur, other = ScratchPad.recorded
+ cur.should == [nil, nil]
+ other.should == [nil, nil]
+ @check.call.should == ["constant", nil]
+ end
+ end
+
+ describe "during the autoload before the constant is assigned" do
+ before :each do
+ @path = fixture(__FILE__, "autoload_during_autoload.rb")
+ ModuleSpecs::Autoload.autoload :DuringAutoload, @path
+ raise unless ModuleSpecs::Autoload.autoload?(:DuringAutoload) == @path
+ end
+
+ after :each do
+ ModuleSpecs::Autoload.send(:remove_const, :DuringAutoload)
+ end
+
+ def check_before_during_thread_after(&check)
+ before = check.call
+ to_autoload_thread, from_autoload_thread = Queue.new, Queue.new
+ ScratchPad.record -> {
+ from_autoload_thread.push check.call
+ to_autoload_thread.pop
+ }
+ t = Thread.new {
+ in_loading_thread = from_autoload_thread.pop
+ in_other_thread = check.call
+ to_autoload_thread.push :done
+ [in_loading_thread, in_other_thread]
+ }
+ in_loading_thread, in_other_thread = nil
+ begin
+ ModuleSpecs::Autoload::DuringAutoload
+ ensure
+ in_loading_thread, in_other_thread = t.value
+ end
+ after = check.call
+ [before, in_loading_thread, in_other_thread, after]
+ end
+
+ it "returns nil in autoload thread and 'constant' otherwise for defined?" do
+ results = check_before_during_thread_after {
+ defined?(ModuleSpecs::Autoload::DuringAutoload)
+ }
+ results.should == ['constant', nil, 'constant', 'constant']
+ end
+
+ it "keeps the constant in Module#constants" do
+ results = check_before_during_thread_after {
+ ModuleSpecs::Autoload.constants(false).include?(:DuringAutoload)
+ }
+ results.should == [true, true, true, true]
+ end
+
+ it "returns false in autoload thread and true otherwise for Module#const_defined?" do
+ results = check_before_during_thread_after {
+ ModuleSpecs::Autoload.const_defined?(:DuringAutoload, false)
+ }
+ results.should == [true, false, true, true]
+ end
+
+ it "returns nil in autoload thread and returns the path in other threads for Module#autoload?" do
+ results = check_before_during_thread_after {
+ ModuleSpecs::Autoload.autoload?(:DuringAutoload)
+ }
+ results.should == [@path, nil, @path, nil]
+ end
+ end
+
+ it "does not remove the constant from Module#constants if load fails and keeps it as an autoload" do
+ ModuleSpecs::Autoload.autoload :Fail, @non_existent
+
+ ModuleSpecs::Autoload.const_defined?(:Fail).should == true
+ ModuleSpecs::Autoload.should have_constant(:Fail)
+ ModuleSpecs::Autoload.autoload?(:Fail).should == @non_existent
+
+ lambda { ModuleSpecs::Autoload::Fail }.should raise_error(LoadError)
+
+ ModuleSpecs::Autoload.should have_constant(:Fail)
+ ModuleSpecs::Autoload.const_defined?(:Fail).should == true
+ ModuleSpecs::Autoload.autoload?(:Fail).should == @non_existent
+
+ lambda { ModuleSpecs::Autoload::Fail }.should raise_error(LoadError)
+ end
+
+ it "does not remove the constant from Module#constants if load raises a RuntimeError and keeps it as an autoload" do
+ path = fixture(__FILE__, "autoload_raise.rb")
+ ScratchPad.record []
+ ModuleSpecs::Autoload.autoload :Raise, path
+
+ ModuleSpecs::Autoload.const_defined?(:Raise).should == true
+ ModuleSpecs::Autoload.should have_constant(:Raise)
+ ModuleSpecs::Autoload.autoload?(:Raise).should == path
+
+ lambda { ModuleSpecs::Autoload::Raise }.should raise_error(RuntimeError)
+ ScratchPad.recorded.should == [:raise]
+
+ ModuleSpecs::Autoload.should have_constant(:Raise)
+ ModuleSpecs::Autoload.const_defined?(:Raise).should == true
+ ModuleSpecs::Autoload.autoload?(:Raise).should == path
+
+ lambda { ModuleSpecs::Autoload::Raise }.should raise_error(RuntimeError)
+ ScratchPad.recorded.should == [:raise, :raise]
+ end
+
+ it "does not remove the constant from Module#constants if the loaded file does not define it, but leaves it as 'undefined'" do
+ path = fixture(__FILE__, "autoload_o.rb")
+ ScratchPad.record []
+ ModuleSpecs::Autoload.autoload :O, path
+
+ ModuleSpecs::Autoload.const_defined?(:O).should == true
+ ModuleSpecs::Autoload.should have_constant(:O)
+ ModuleSpecs::Autoload.autoload?(:O).should == path
+
+ lambda { ModuleSpecs::Autoload::O }.should raise_error(NameError)
+
+ ModuleSpecs::Autoload.should have_constant(:O)
+ ModuleSpecs::Autoload.const_defined?(:O).should == false
+ ModuleSpecs::Autoload.autoload?(:O).should == nil
+ -> { ModuleSpecs::Autoload.const_get(:O) }.should raise_error(NameError)
+ end
+
+ it "does not try to load the file again if the loaded file did not define the constant" do
+ path = fixture(__FILE__, "autoload_o.rb")
+ ScratchPad.record []
+ ModuleSpecs::Autoload.autoload :NotDefinedByFile, path
+
+ -> { ModuleSpecs::Autoload::NotDefinedByFile }.should raise_error(NameError)
+ ScratchPad.recorded.should == [:loaded]
+ -> { ModuleSpecs::Autoload::NotDefinedByFile }.should raise_error(NameError)
+ ScratchPad.recorded.should == [:loaded]
+
+ Thread.new {
+ -> { ModuleSpecs::Autoload::NotDefinedByFile }.should raise_error(NameError)
+ }.join
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "returns 'constant' on referring the constant with defined?()" do
+ module ModuleSpecs::Autoload::Q
+ autoload :R, fixture(__FILE__, "autoload.rb")
+ defined?(R).should == 'constant'
+ end
+ ModuleSpecs::Autoload::Q.should have_constant(:R)
+ end
+
+ it "does not load the file when removing an autoload constant" do
+ module ModuleSpecs::Autoload::Q
+ autoload :R, fixture(__FILE__, "autoload.rb")
+ remove_const :R
+ end
+ ModuleSpecs::Autoload::Q.should_not have_constant(:R)
+ end
+
+ it "does not load the file when accessing the constants table of the module" do
+ ModuleSpecs::Autoload.autoload :P, @non_existent
+ ModuleSpecs::Autoload.const_defined?(:P).should be_true
+ ModuleSpecs::Autoload.const_defined?("P").should be_true
+ end
+
+ it "loads the file when opening a module that is the autoloaded constant" do
+ module ModuleSpecs::Autoload::U
+ autoload :V, fixture(__FILE__, "autoload_v.rb")
+
+ class V
+ X = get_value
+ end
+ end
+
+ ModuleSpecs::Autoload::U::V::X.should == :autoload_uvx
+ end
+
+ it "loads the file that defines subclass XX::YY < YY and YY is a top level constant" do
+ module ModuleSpecs::Autoload::XX
+ autoload :YY, fixture(__FILE__, "autoload_subclass.rb")
+ end
+
+ ModuleSpecs::Autoload::XX::YY.superclass.should == YY
+ end
+
+ describe "after autoloading searches for the constant like the original lookup" do
+ it "in lexical scopes if both declared and defined in parent" do
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ DeclaredAndDefinedInParent = :declared_and_defined_in_parent
+ }
+ autoload :DeclaredAndDefinedInParent, fixture(__FILE__, "autoload_callback.rb")
+ class LexicalScope
+ DeclaredAndDefinedInParent.should == :declared_and_defined_in_parent
+
+ # The constant is really in Autoload, not Autoload::LexicalScope
+ self.should_not have_constant(:DeclaredAndDefinedInParent)
+ -> { const_get(:DeclaredAndDefinedInParent) }.should raise_error(NameError)
+ end
+ DeclaredAndDefinedInParent.should == :declared_and_defined_in_parent
+ end
+ end
+
+ it "in lexical scopes if declared in parent and defined in current" do
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ class LexicalScope
+ DeclaredInParentDefinedInCurrent = :declared_in_parent_defined_in_current
+ end
+ }
+ autoload :DeclaredInParentDefinedInCurrent, fixture(__FILE__, "autoload_callback.rb")
+
+ class LexicalScope
+ DeclaredInParentDefinedInCurrent.should == :declared_in_parent_defined_in_current
+ LexicalScope::DeclaredInParentDefinedInCurrent.should == :declared_in_parent_defined_in_current
+ end
+
+ # Basically, the parent autoload constant remains in a "undefined" state
+ self.autoload?(:DeclaredInParentDefinedInCurrent).should == nil
+ const_defined?(:DeclaredInParentDefinedInCurrent).should == false
+ self.should have_constant(:DeclaredInParentDefinedInCurrent)
+ -> { DeclaredInParentDefinedInCurrent }.should raise_error(NameError)
+
+ ModuleSpecs::Autoload::LexicalScope.send(:remove_const, :DeclaredInParentDefinedInCurrent)
+ end
+ end
+
+ it "and fails when finding the undefined autoload constant in the the current scope when declared in current and defined in parent" do
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ DeclaredInCurrentDefinedInParent = :declared_in_current_defined_in_parent
+ }
+
+ class LexicalScope
+ autoload :DeclaredInCurrentDefinedInParent, fixture(__FILE__, "autoload_callback.rb")
+ -> { DeclaredInCurrentDefinedInParent }.should raise_error(NameError)
+ # Basically, the autoload constant remains in a "undefined" state
+ self.autoload?(:DeclaredInCurrentDefinedInParent).should == nil
+ const_defined?(:DeclaredInCurrentDefinedInParent).should == false
+ self.should have_constant(:DeclaredInCurrentDefinedInParent)
+ -> { const_get(:DeclaredInCurrentDefinedInParent) }.should raise_error(NameError)
+ end
+
+ DeclaredInCurrentDefinedInParent.should == :declared_in_current_defined_in_parent
+ end
+ end
+
+ it "in the included modules" do
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ module DefinedInIncludedModule
+ Incl = :defined_in_included_module
+ end
+ include DefinedInIncludedModule
+ }
+ autoload :Incl, fixture(__FILE__, "autoload_callback.rb")
+ Incl.should == :defined_in_included_module
+ end
+ end
+
+ it "in the included modules of the superclass" do
+ module ModuleSpecs::Autoload
+ class LookupAfterAutoloadSuper
+ end
+ class LookupAfterAutoloadChild < LookupAfterAutoloadSuper
+ end
+
+ ScratchPad.record -> {
+ module DefinedInSuperclassIncludedModule
+ InclS = :defined_in_superclass_included_module
+ end
+ LookupAfterAutoloadSuper.include DefinedInSuperclassIncludedModule
+ }
+
+ class LookupAfterAutoloadChild
+ autoload :InclS, fixture(__FILE__, "autoload_callback.rb")
+ InclS.should == :defined_in_superclass_included_module
+ end
+ end
+ end
+
+ it "in the prepended modules" do
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ module DefinedInPrependedModule
+ Prep = :defined_in_prepended_module
+ end
+ include DefinedInPrependedModule
+ }
+ autoload :Prep, fixture(__FILE__, "autoload_callback.rb")
+ Prep.should == :defined_in_prepended_module
+ end
+ end
+
+ it "in a meta class scope" do
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ class MetaScope
+ end
+ }
+ autoload :MetaScope, fixture(__FILE__, "autoload_callback.rb")
+ class << self
+ def r
+ MetaScope.new
+ end
+ end
+ end
+ ModuleSpecs::Autoload.r.should be_kind_of(ModuleSpecs::Autoload::MetaScope)
+ end
+ end
+
+ # [ruby-core:19127] [ruby-core:29941]
+ it "does NOT raise a NameError when the autoload file did not define the constant and a module is opened with the same name" do
+ module ModuleSpecs::Autoload
+ class W
+ autoload :Y, fixture(__FILE__, "autoload_w.rb")
+
+ class Y
+ end
+ end
+ end
+
+ ModuleSpecs::Autoload::W::Y.should be_kind_of(Class)
+ ScratchPad.recorded.should == :loaded
+ ModuleSpecs::Autoload::W.send(:remove_const, :Y)
+ end
+
+ it "does not call #require a second time and does not warn if already loading the same feature with #require" do
+ main = TOPLEVEL_BINDING.eval("self")
+ main.should_not_receive(:require)
+
+ module ModuleSpecs::Autoload
+ autoload :AutoloadDuringRequire, fixture(__FILE__, "autoload_during_require.rb")
+ end
+
+ -> {
+ $VERBOSE = true
+ Kernel.require fixture(__FILE__, "autoload_during_require.rb")
+ }.should_not complain
+ ModuleSpecs::Autoload::AutoloadDuringRequire.should be_kind_of(Class)
+ end
+
+ it "calls #to_path on non-string filenames" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return @non_existent
+ ModuleSpecs.autoload :A, p
+ end
+
+ it "raises an ArgumentError when an empty filename is given" do
+ lambda { ModuleSpecs.autoload :A, "" }.should raise_error(ArgumentError)
+ end
+
+ it "raises a NameError when the constant name starts with a lower case letter" do
+ lambda { ModuleSpecs.autoload "a", @non_existent }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when the constant name starts with a number" do
+ lambda { ModuleSpecs.autoload "1two", @non_existent }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when the constant name has a space in it" do
+ lambda { ModuleSpecs.autoload "a name", @non_existent }.should raise_error(NameError)
+ end
+
+ it "shares the autoload request across dup'ed copies of modules" do
+ require fixture(__FILE__, "autoload_s.rb")
+ filename = fixture(__FILE__, "autoload_t.rb")
+ mod1 = Module.new { autoload :T, filename }
+ lambda {
+ ModuleSpecs::Autoload::S = mod1
+ }.should complain(/already initialized constant/)
+ mod2 = mod1.dup
+
+ mod1.autoload?(:T).should == filename
+ mod2.autoload?(:T).should == filename
+
+ mod1::T.should == :autoload_t
+ lambda { mod2::T }.should raise_error(NameError)
+ end
+
+ it "raises a TypeError if opening a class with a different superclass than the class defined in the autoload file" do
+ ModuleSpecs::Autoload.autoload :Z, fixture(__FILE__, "autoload_z.rb")
+ class ModuleSpecs::Autoload::ZZ
+ end
+
+ lambda do
+ class ModuleSpecs::Autoload::Z < ModuleSpecs::Autoload::ZZ
+ end
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if not passed a String or object respodning to #to_path for the filename" do
+ name = mock("autoload_name.rb")
+
+ lambda { ModuleSpecs::Autoload.autoload :Str, name }.should raise_error(TypeError)
+ end
+
+ it "calls #to_path on non-String filename arguments" do
+ name = mock("autoload_name.rb")
+ name.should_receive(:to_path).and_return("autoload_name.rb")
+
+ lambda { ModuleSpecs::Autoload.autoload :Str, name }.should_not raise_error
+ end
+
+ describe "on a frozen module" do
+ it "raises a #{frozen_error_class} before setting the name" do
+ lambda { @frozen_module.autoload :Foo, @non_existent }.should raise_error(frozen_error_class)
+ @frozen_module.should_not have_constant(:Foo)
+ end
+ end
+
+ describe "when changing $LOAD_PATH" do
+ before do
+ $LOAD_PATH.unshift(File.expand_path('../fixtures/path1', __FILE__))
+ end
+
+ after do
+ $LOAD_PATH.shift
+ $LOAD_PATH.shift
+ end
+
+ it "does not reload a file due to a different load path" do
+ ModuleSpecs::Autoload.autoload :LoadPath, "load_path"
+ ModuleSpecs::Autoload::LoadPath.loaded.should == :autoload_load_path
+ end
+ end
+
+ describe "(concurrently)" do
+ it "blocks a second thread while a first is doing the autoload" do
+ ModuleSpecs::Autoload.autoload :Concur, fixture(__FILE__, "autoload_concur.rb")
+
+ start = false
+
+ ScratchPad.record []
+
+ t1_val = nil
+ t2_val = nil
+
+ fin = false
+
+ t1 = Thread.new do
+ Thread.pass until start
+ t1_val = ModuleSpecs::Autoload::Concur
+ ScratchPad.recorded << :t1_post
+ fin = true
+ end
+
+ t2_exc = nil
+
+ t2 = Thread.new do
+ Thread.pass until t1 and t1[:in_autoload_rb]
+ begin
+ t2_val = ModuleSpecs::Autoload::Concur
+ rescue Exception => e
+ t2_exc = e
+ else
+ Thread.pass until fin
+ ScratchPad.recorded << :t2_post
+ end
+ end
+
+ start = true
+
+ t1.join
+ t2.join
+
+ ScratchPad.recorded.should == [:con_pre, :con_post, :t1_post, :t2_post]
+
+ t1_val.should == 1
+ t2_val.should == t1_val
+
+ t2_exc.should be_nil
+
+ ModuleSpecs::Autoload.send(:remove_const, :Concur)
+ end
+
+ # https://bugs.ruby-lang.org/issues/10892
+ it "blocks others threads while doing an autoload" do
+ file_path = fixture(__FILE__, "repeated_concurrent_autoload.rb")
+ autoload_path = file_path.sub(/\.rb\Z/, '')
+ mod_count = 30
+ thread_count = 16
+
+ mod_names = []
+ mod_count.times do |i|
+ mod_name = :"Mod#{i}"
+ Object.autoload mod_name, autoload_path
+ mod_names << mod_name
+ end
+
+ barrier = ModuleSpecs::CyclicBarrier.new thread_count
+ ScratchPad.record ModuleSpecs::ThreadSafeCounter.new
+
+ threads = (1..thread_count).map do
+ Thread.new do
+ mod_names.each do |mod_name|
+ break false unless barrier.enabled?
+
+ was_last_one_in = barrier.await # wait for all threads to finish the iteration
+ # clean up so we can autoload the same file again
+ $LOADED_FEATURES.delete(file_path) if was_last_one_in && $LOADED_FEATURES.include?(file_path)
+ barrier.await # get ready for race
+
+ begin
+ Object.const_get(mod_name).foo
+ rescue NoMethodError
+ barrier.disable!
+ break false
+ end
+ end
+ end
+ end
+
+ # check that no thread got a NoMethodError because of partially loaded module
+ threads.all? {|t| t.value}.should be_true
+
+ # check that the autoloaded file was evaled exactly once
+ ScratchPad.recorded.get.should == mod_count
+
+ mod_names.each do |mod_name|
+ Object.send(:remove_const, mod_name)
+ end
+ end
+
+ it "raises a NameError in each thread if the constant is not set" do
+ file = fixture(__FILE__, "autoload_never_set.rb")
+ start = false
+
+ threads = Array.new(10) do
+ Thread.new do
+ Thread.pass until start
+ begin
+ ModuleSpecs::Autoload.autoload :NeverSetConstant, file
+ Thread.pass
+ ModuleSpecs::Autoload::NeverSetConstant
+ rescue NameError => e
+ e
+ ensure
+ Thread.pass
+ end
+ end
+ end
+
+ start = true
+ threads.each { |t|
+ t.value.should be_an_instance_of(NameError)
+ }
+ end
+
+ it "raises a LoadError in each thread if the file does not exist" do
+ file = fixture(__FILE__, "autoload_does_not_exist.rb")
+ start = false
+
+ threads = Array.new(10) do
+ Thread.new do
+ Thread.pass until start
+ begin
+ ModuleSpecs::Autoload.autoload :FileDoesNotExist, file
+ Thread.pass
+ ModuleSpecs::Autoload::FileDoesNotExist
+ rescue LoadError => e
+ e
+ ensure
+ Thread.pass
+ end
+ end
+ end
+
+ start = true
+ threads.each { |t|
+ t.value.should be_an_instance_of(LoadError)
+ }
+ end
+ end
+
+ it "loads the registered constant even if the constant was already loaded by another thread" do
+ Thread.new {
+ ModuleSpecs::Autoload::FromThread::D.foo
+ }.value.should == :foo
+ end
+end
diff --git a/spec/ruby/core/module/case_compare_spec.rb b/spec/ruby/core/module/case_compare_spec.rb
new file mode 100644
index 0000000000..49ac359f6f
--- /dev/null
+++ b/spec/ruby/core/module/case_compare_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#===" do
+ it "returns true when the given Object is an instance of self or of self's descendants" do
+ (ModuleSpecs::Child === ModuleSpecs::Child.new).should == true
+ (ModuleSpecs::Parent === ModuleSpecs::Parent.new).should == true
+
+ (ModuleSpecs::Parent === ModuleSpecs::Child.new).should == true
+ (Object === ModuleSpecs::Child.new).should == true
+
+ (ModuleSpecs::Child === String.new).should == false
+ (ModuleSpecs::Child === mock('x')).should == false
+ end
+
+ it "returns true when the given Object's class includes self or when the given Object is extended by self" do
+ (ModuleSpecs::Basic === ModuleSpecs::Child.new).should == true
+ (ModuleSpecs::Super === ModuleSpecs::Child.new).should == true
+ (ModuleSpecs::Basic === mock('x').extend(ModuleSpecs::Super)).should == true
+ (ModuleSpecs::Super === mock('y').extend(ModuleSpecs::Super)).should == true
+
+ (ModuleSpecs::Basic === ModuleSpecs::Parent.new).should == false
+ (ModuleSpecs::Super === ModuleSpecs::Parent.new).should == false
+ (ModuleSpecs::Basic === mock('z')).should == false
+ (ModuleSpecs::Super === mock('a')).should == false
+ end
+
+ it "does not let a module singleton class interfere when its on the RHS" do
+ (Class === ModuleSpecs::CaseCompareOnSingleton).should == false
+ end
+end
diff --git a/spec/ruby/core/module/class_eval_spec.rb b/spec/ruby/core/module/class_eval_spec.rb
new file mode 100644
index 0000000000..c6665d5aff
--- /dev/null
+++ b/spec/ruby/core/module/class_eval_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/class_eval'
+
+describe "Module#class_eval" do
+ it_behaves_like :module_class_eval, :class_eval
+end
diff --git a/spec/ruby/core/module/class_exec_spec.rb b/spec/ruby/core/module/class_exec_spec.rb
new file mode 100644
index 0000000000..4acd0169ad
--- /dev/null
+++ b/spec/ruby/core/module/class_exec_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/class_exec'
+
+describe "Module#class_exec" do
+ it_behaves_like :module_class_exec, :class_exec
+end
diff --git a/spec/ruby/core/module/class_variable_defined_spec.rb b/spec/ruby/core/module/class_variable_defined_spec.rb
new file mode 100644
index 0000000000..25fd73a136
--- /dev/null
+++ b/spec/ruby/core/module/class_variable_defined_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#class_variable_defined?" do
+ it "returns true if a class variable with the given name is defined in self" do
+ c = Class.new { class_variable_set :@@class_var, "test" }
+ c.class_variable_defined?(:@@class_var).should == true
+ c.class_variable_defined?("@@class_var").should == true
+ c.class_variable_defined?(:@@no_class_var).should == false
+ c.class_variable_defined?("@@no_class_var").should == false
+ ModuleSpecs::CVars.class_variable_defined?("@@cls").should == true
+ end
+
+ it "returns true if a class variable with the given name is defined in the metaclass" do
+ ModuleSpecs::CVars.class_variable_defined?("@@meta").should == true
+ end
+
+ it "returns true if the class variable is defined in a metaclass" do
+ obj = mock("metaclass class variable")
+ meta = obj.singleton_class
+ meta.send :class_variable_set, :@@var, 1
+ meta.send(:class_variable_defined?, :@@var).should be_true
+ end
+
+ it "returns false if the class variable is not defined in a metaclass" do
+ obj = mock("metaclass class variable")
+ meta = obj.singleton_class
+ meta.class_variable_defined?(:@@var).should be_false
+ end
+
+ it "returns true if a class variables with the given name is defined in an included module" do
+ c = Class.new { include ModuleSpecs::MVars }
+ c.class_variable_defined?("@@mvar").should == true
+ end
+
+ it "returns false if a class variables with the given name is defined in an extended module" do
+ c = Class.new
+ c.extend ModuleSpecs::MVars
+ c.class_variable_defined?("@@mvar").should == false
+ end
+
+ it "raises a NameError when the given name is not allowed" do
+ c = Class.new
+
+ lambda {
+ c.class_variable_defined?(:invalid_name)
+ }.should raise_error(NameError)
+
+ lambda {
+ c.class_variable_defined?("@invalid_name")
+ }.should raise_error(NameError)
+ end
+
+ it "converts a non string/symbol/fixnum name to string using to_str" do
+ c = Class.new { class_variable_set :@@class_var, "test" }
+ (o = mock('@@class_var')).should_receive(:to_str).and_return("@@class_var")
+ c.class_variable_defined?(o).should == true
+ end
+
+ it "raises a TypeError when the given names can't be converted to strings using to_str" do
+ c = Class.new { class_variable_set :@@class_var, "test" }
+ o = mock('123')
+ lambda {
+ c.class_variable_defined?(o)
+ }.should raise_error(TypeError)
+
+ o.should_receive(:to_str).and_return(123)
+ lambda {
+ c.class_variable_defined?(o)
+ }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/module/class_variable_get_spec.rb b/spec/ruby/core/module/class_variable_get_spec.rb
new file mode 100644
index 0000000000..7619c135ab
--- /dev/null
+++ b/spec/ruby/core/module/class_variable_get_spec.rb
@@ -0,0 +1,76 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#class_variable_get" do
+ it "returns the value of the class variable with the given name" do
+ c = Class.new { class_variable_set :@@class_var, "test" }
+ c.send(:class_variable_get, :@@class_var).should == "test"
+ c.send(:class_variable_get, "@@class_var").should == "test"
+ end
+
+ it "returns the value of a class variable with the given name defined in an included module" do
+ c = Class.new { include ModuleSpecs::MVars }
+ c.send(:class_variable_get, "@@mvar").should == :mvar
+ end
+
+ it "raises a NameError for a class variable named '@@'" do
+ c = Class.new
+ lambda { c.send(:class_variable_get, "@@") }.should raise_error(NameError)
+ lambda { c.send(:class_variable_get, :"@@") }.should raise_error(NameError)
+ end
+
+ it "raises a NameError for a class variables with the given name defined in an extended module" do
+ c = Class.new
+ c.extend ModuleSpecs::MVars
+ lambda {
+ c.send(:class_variable_get, "@@mvar")
+ }.should raise_error(NameError)
+ end
+
+ it "returns class variables defined in the class body and accessed in the metaclass" do
+ ModuleSpecs::CVars.cls.should == :class
+ end
+
+ it "returns class variables defined in the metaclass and accessed by class methods" do
+ ModuleSpecs::CVars.meta.should == :metainfo
+ end
+
+ it "returns class variables defined in the metaclass and accessed by instance methods" do
+ ModuleSpecs::CVars.new.meta.should == :metainfo
+ end
+
+ it "returns a class variable defined in a metaclass" do
+ obj = mock("metaclass class variable")
+ meta = obj.singleton_class
+ meta.send :class_variable_set, :@@var, :cvar_value
+ meta.send(:class_variable_get, :@@var).should == :cvar_value
+ end
+
+ it "raises a NameError when an uninitialized class variable is accessed" do
+ c = Class.new
+ [:@@no_class_var, "@@no_class_var"].each do |cvar|
+ lambda { c.send(:class_variable_get, cvar) }.should raise_error(NameError)
+ end
+ end
+
+ it "raises a NameError when the given name is not allowed" do
+ c = Class.new
+
+ lambda { c.send(:class_variable_get, :invalid_name) }.should raise_error(NameError)
+ lambda { c.send(:class_variable_get, "@invalid_name") }.should raise_error(NameError)
+ end
+
+ it "converts a non string/symbol/fixnum name to string using to_str" do
+ c = Class.new { class_variable_set :@@class_var, "test" }
+ (o = mock('@@class_var')).should_receive(:to_str).and_return("@@class_var")
+ c.send(:class_variable_get, o).should == "test"
+ end
+
+ it "raises a TypeError when the given names can't be converted to strings using to_str" do
+ c = Class.new { class_variable_set :@@class_var, "test" }
+ o = mock('123')
+ lambda { c.send(:class_variable_get, o) }.should raise_error(TypeError)
+ o.should_receive(:to_str).and_return(123)
+ lambda { c.send(:class_variable_get, o) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/module/class_variable_set_spec.rb b/spec/ruby/core/module/class_variable_set_spec.rb
new file mode 100644
index 0000000000..dcb5da87b6
--- /dev/null
+++ b/spec/ruby/core/module/class_variable_set_spec.rb
@@ -0,0 +1,62 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#class_variable_set" do
+ it "sets the class variable with the given name to the given value" do
+ c = Class.new
+
+ c.send(:class_variable_set, :@@test, "test")
+ c.send(:class_variable_set, "@@test3", "test3")
+
+ c.send(:class_variable_get, :@@test).should == "test"
+ c.send(:class_variable_get, :@@test3).should == "test3"
+ end
+
+ it "sets a class variable on a metaclass" do
+ obj = mock("metaclass class variable")
+ meta = obj.singleton_class
+ meta.send(:class_variable_set, :@@var, :cvar_value).should == :cvar_value
+ meta.send(:class_variable_get, :@@var).should == :cvar_value
+ end
+
+ it "sets the value of a class variable with the given name defined in an included module" do
+ c = Class.new { include ModuleSpecs::MVars.dup }
+ c.send(:class_variable_set, "@@mvar", :new_mvar).should == :new_mvar
+ c.send(:class_variable_get, "@@mvar").should == :new_mvar
+ end
+
+ it "raises a #{frozen_error_class} when self is frozen" do
+ lambda {
+ Class.new.freeze.send(:class_variable_set, :@@test, "test")
+ }.should raise_error(frozen_error_class)
+ lambda {
+ Module.new.freeze.send(:class_variable_set, :@@test, "test")
+ }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a NameError when the given name is not allowed" do
+ c = Class.new
+
+ lambda {
+ c.send(:class_variable_set, :invalid_name, "test")
+ }.should raise_error(NameError)
+ lambda {
+ c.send(:class_variable_set, "@invalid_name", "test")
+ }.should raise_error(NameError)
+ end
+
+ it "converts a non string/symbol/fixnum name to string using to_str" do
+ (o = mock('@@class_var')).should_receive(:to_str).and_return("@@class_var")
+ c = Class.new
+ c.send(:class_variable_set, o, "test")
+ c.send(:class_variable_get, :@@class_var).should == "test"
+ end
+
+ it "raises a TypeError when the given names can't be converted to strings using to_str" do
+ c = Class.new { class_variable_set :@@class_var, "test" }
+ o = mock('123')
+ lambda { c.send(:class_variable_set, o, "test") }.should raise_error(TypeError)
+ o.should_receive(:to_str).and_return(123)
+ lambda { c.send(:class_variable_set, o, "test") }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/module/class_variables_spec.rb b/spec/ruby/core/module/class_variables_spec.rb
new file mode 100644
index 0000000000..fd7aa93aa8
--- /dev/null
+++ b/spec/ruby/core/module/class_variables_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#class_variables" do
+ it "returns an Array with the names of class variables of self" do
+ ModuleSpecs::ClassVars::A.class_variables.should include(:@@a_cvar)
+ ModuleSpecs::ClassVars::M.class_variables.should include(:@@m_cvar)
+ end
+
+ it "returns an Array of Symbols of class variable names defined in a metaclass" do
+ obj = mock("metaclass class variable")
+ meta = obj.singleton_class
+ meta.send :class_variable_set, :@@var, :cvar_value
+ meta.class_variables.should == [:@@var]
+ end
+
+ it "returns an Array with names of class variables defined in metaclasses" do
+ ModuleSpecs::CVars.class_variables.should include(:@@cls, :@@meta)
+ end
+
+ it "does not return class variables defined in extended modules" do
+ c = Class.new
+ c.extend ModuleSpecs::MVars
+ c.class_variables.should_not include(:@@mvar)
+ end
+end
diff --git a/spec/ruby/core/module/comparison_spec.rb b/spec/ruby/core/module/comparison_spec.rb
new file mode 100644
index 0000000000..069bbd7a97
--- /dev/null
+++ b/spec/ruby/core/module/comparison_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#<=>" do
+ it "returns -1 if self is a subclass of or includes the given module" do
+ (ModuleSpecs::Child <=> ModuleSpecs::Parent).should == -1
+ (ModuleSpecs::Child <=> ModuleSpecs::Basic).should == -1
+ (ModuleSpecs::Child <=> ModuleSpecs::Super).should == -1
+ (ModuleSpecs::Super <=> ModuleSpecs::Basic).should == -1
+ end
+
+ it "returns 0 if self is the same as the given module" do
+ (ModuleSpecs::Child <=> ModuleSpecs::Child).should == 0
+ (ModuleSpecs::Parent <=> ModuleSpecs::Parent).should == 0
+ (ModuleSpecs::Basic <=> ModuleSpecs::Basic).should == 0
+ (ModuleSpecs::Super <=> ModuleSpecs::Super).should == 0
+ end
+
+ it "returns +1 if self is a superclas of or included by the given module" do
+ (ModuleSpecs::Parent <=> ModuleSpecs::Child).should == +1
+ (ModuleSpecs::Basic <=> ModuleSpecs::Child).should == +1
+ (ModuleSpecs::Super <=> ModuleSpecs::Child).should == +1
+ (ModuleSpecs::Basic <=> ModuleSpecs::Super).should == +1
+ end
+
+ it "returns nil if self and the given module are not related" do
+ (ModuleSpecs::Parent <=> ModuleSpecs::Basic).should == nil
+ (ModuleSpecs::Parent <=> ModuleSpecs::Super).should == nil
+ (ModuleSpecs::Basic <=> ModuleSpecs::Parent).should == nil
+ (ModuleSpecs::Super <=> ModuleSpecs::Parent).should == nil
+ end
+
+ it "returns nil if the argument is not a class/module" do
+ (ModuleSpecs::Parent <=> mock('x')).should == nil
+ end
+end
diff --git a/spec/ruby/core/module/const_defined_spec.rb b/spec/ruby/core/module/const_defined_spec.rb
new file mode 100644
index 0000000000..cb1674c7e7
--- /dev/null
+++ b/spec/ruby/core/module/const_defined_spec.rb
@@ -0,0 +1,144 @@
+# encoding: utf-8
+
+require_relative '../../spec_helper'
+require_relative '../../fixtures/constants'
+require_relative 'fixtures/constant_unicode'
+
+describe "Module#const_defined?" do
+ it "returns true if the given Symbol names a constant defined in the receiver" do
+ ConstantSpecs.const_defined?(:CS_CONST2).should == true
+ ConstantSpecs.const_defined?(:ModuleA).should == true
+ ConstantSpecs.const_defined?(:ClassA).should == true
+ ConstantSpecs::ContainerA.const_defined?(:ChildA).should == true
+ end
+
+ it "returns true if the constant is defined in the receiver's superclass" do
+ # CS_CONST4 is defined in the superclass of ChildA
+ 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
+ # 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 Object and the receiver is a module" do
+ # CS_CONST1 is defined in Object
+ ConstantSpecs::ModuleA.const_defined?(:CS_CONST1).should be_true
+ end
+
+ it "returns true if the constant is defined in Object and the receiver is a class that has Object among its ancestors" do
+ # CS_CONST1 is defined in Object
+ ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST1).should be_true
+ end
+
+ it "returns false if the constant is defined in the receiver's superclass and the inherit flag is false" do
+ ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, false).should be_false
+ end
+
+ it "returns true if the constant is defined in the receiver's superclass and the inherit flag is true" do
+ ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, true).should be_true
+ end
+
+ it "returns true if the given String names a constant defined in the receiver" do
+ ConstantSpecs.const_defined?("CS_CONST2").should == true
+ ConstantSpecs.const_defined?("ModuleA").should == true
+ ConstantSpecs.const_defined?("ClassA").should == true
+ ConstantSpecs::ContainerA.const_defined?("ChildA").should == true
+ end
+
+ it "returns true when passed a constant name with unicode characters" do
+ ConstantUnicodeSpecs.const_defined?("CS_CONSTλ").should be_true
+ end
+
+ it "returns true when passed a constant name with EUC-JP characters" do
+ str = "CS_CONSTλ".encode("euc-jp")
+ ConstantSpecs.const_set str, 1
+ ConstantSpecs.const_defined?(str).should be_true
+ end
+
+ it "returns false if the constant is not defined in the receiver, its superclass, or any included modules" do
+ # The following constant isn't defined at all.
+ ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4726).should be_false
+ # DETACHED_CONSTANT is defined in ConstantSpecs::Detached, which isn't
+ # included by or inherited from ParentA
+ ConstantSpecs::ParentA.const_defined?(:DETACHED_CONSTANT).should be_false
+ end
+
+ it "does not call #const_missing if the constant is not defined in the receiver" do
+ ConstantSpecs::ClassA.should_not_receive(:const_missing)
+ ConstantSpecs::ClassA.const_defined?(:CS_CONSTX).should == false
+ end
+
+ it "calls #to_str to convert the given name to a String" do
+ name = mock("ClassA")
+ name.should_receive(:to_str).and_return("ClassA")
+ ConstantSpecs.const_defined?(name).should == true
+ end
+
+ it "special cases Object and checks it's included Modules" do
+ Object.const_defined?(:CS_CONST10).should be_true
+ end
+
+ it "returns true for toplevel constant when the name begins with '::'" do
+ ConstantSpecs.const_defined?("::Array").should be_true
+ end
+
+ it "returns true when passed a scoped constant name" do
+ ConstantSpecs.const_defined?("ClassC::CS_CONST1").should be_true
+ end
+
+ it "returns true when passed a scoped constant name for a constant in the inheritance hierarchy and the inherited flag is default" do
+ ConstantSpecs::ClassD.const_defined?("ClassE::CS_CONST2").should be_true
+ end
+
+ it "returns true when passed a scoped constant name for a constant in the inheritance hierarchy and the inherited flag is true" do
+ ConstantSpecs::ClassD.const_defined?("ClassE::CS_CONST2", true).should be_true
+ end
+
+ it "returns false when passed a scoped constant name for a constant in the inheritance hierarchy and the inherited flag is false" do
+ ConstantSpecs::ClassD.const_defined?("ClassE::CS_CONST2", false).should be_false
+ end
+
+ it "returns false when the name begins with '::' and the toplevel constant does not exist" do
+ ConstantSpecs.const_defined?("::Name").should be_false
+ end
+
+ it "raises a NameError if the name does not start with a capital letter" do
+ lambda { ConstantSpecs.const_defined? "name" }.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the name starts with '_'" do
+ lambda { ConstantSpecs.const_defined? "__CONSTX__" }.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the name starts with '@'" do
+ lambda { ConstantSpecs.const_defined? "@Name" }.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the name starts with '!'" do
+ lambda { ConstantSpecs.const_defined? "!Name" }.should raise_error(NameError)
+ end
+
+ it "returns true or false for the nested name" do
+ ConstantSpecs.const_defined?("NotExist::Name").should == false
+ ConstantSpecs.const_defined?("::Name").should == false
+ ConstantSpecs.const_defined?("::Object").should == true
+ ConstantSpecs.const_defined?("ClassA::CS_CONST10").should == true
+ ConstantSpecs.const_defined?("ClassA::CS_CONST10_").should == false
+ end
+
+ it "raises a NameError if the name contains non-alphabetic characters except '_'" do
+ ConstantSpecs.const_defined?("CS_CONSTX").should == false
+ lambda { ConstantSpecs.const_defined? "Name=" }.should raise_error(NameError)
+ lambda { ConstantSpecs.const_defined? "Name?" }.should raise_error(NameError)
+ end
+
+ it "raises a TypeError if conversion to a String by calling #to_str fails" do
+ name = mock('123')
+ lambda { ConstantSpecs.const_defined? name }.should raise_error(TypeError)
+
+ name.should_receive(:to_str).and_return(123)
+ lambda { ConstantSpecs.const_defined? name }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/module/const_get_spec.rb b/spec/ruby/core/module/const_get_spec.rb
new file mode 100644
index 0000000000..461b303d6d
--- /dev/null
+++ b/spec/ruby/core/module/const_get_spec.rb
@@ -0,0 +1,229 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/constants'
+require_relative 'fixtures/constants_autoload'
+
+describe "Module#const_get" do
+ it "accepts a String or Symbol name" do
+ Object.const_get(:CS_CONST1).should == :const1
+ Object.const_get("CS_CONST1").should == :const1
+ end
+
+ it "raises a NameError if no constant is defined in the search path" do
+ lambda { ConstantSpecs.const_get :CS_CONSTX }.should raise_error(NameError)
+ end
+
+ it "raises a NameError with the not found constant symbol" do
+ error_inspection = lambda { |e| e.name.should == :CS_CONSTX }
+ lambda { ConstantSpecs.const_get :CS_CONSTX }.should raise_error(NameError, &error_inspection)
+ end
+
+ it "raises a NameError if the name does not start with a capital letter" do
+ lambda { ConstantSpecs.const_get "name" }.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the name starts with a non-alphabetic character" do
+ lambda { ConstantSpecs.const_get "__CONSTX__" }.should raise_error(NameError)
+ lambda { ConstantSpecs.const_get "@CS_CONST1" }.should raise_error(NameError)
+ lambda { ConstantSpecs.const_get "!CS_CONST1" }.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the name contains non-alphabetic characters except '_'" do
+ Object.const_get("CS_CONST1").should == :const1
+ lambda { ConstantSpecs.const_get "CS_CONST1=" }.should raise_error(NameError)
+ lambda { ConstantSpecs.const_get "CS_CONST1?" }.should raise_error(NameError)
+ end
+
+ it "calls #to_str to convert the given name to a String" do
+ name = mock("ClassA")
+ name.should_receive(:to_str).and_return("ClassA")
+ ConstantSpecs.const_get(name).should == ConstantSpecs::ClassA
+ end
+
+ it "raises a TypeError if conversion to a String by calling #to_str fails" do
+ name = mock('123')
+ lambda { ConstantSpecs.const_get(name) }.should raise_error(TypeError)
+
+ name.should_receive(:to_str).and_return(123)
+ lambda { ConstantSpecs.const_get(name) }.should raise_error(TypeError)
+ end
+
+ it "calls #const_missing on the receiver if unable to locate the constant" do
+ ConstantSpecs::ContainerA.should_receive(:const_missing).with(:CS_CONSTX)
+ ConstantSpecs::ContainerA.const_get(:CS_CONSTX)
+ end
+
+ it "does not search the singleton class of a Class or Module" do
+ lambda do
+ ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST14)
+ end.should raise_error(NameError)
+ lambda { ConstantSpecs.const_get(:CS_CONST14) }.should raise_error(NameError)
+ end
+
+ it "does not search the containing scope" do
+ ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST20).should == :const20_2
+ lambda do
+ ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST5)
+ end.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the constant is defined in the receiver's supperclass and the inherit flag is false" do
+ lambda do
+ ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST4, false)
+ end.should raise_error(NameError)
+ end
+
+ it "searches into the receiver superclasses if the inherit flag is true" do
+ ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST4, true).should == :const4
+ end
+
+ it "raises a NameError when the receiver is a Module, the constant is defined at toplevel and the inherit flag is false" do
+ lambda do
+ ConstantSpecs::ModuleA.const_get(:CS_CONST1, false)
+ end.should raise_error(NameError)
+ end
+
+ it "raises a NameError when the receiver is a Class, the constant is defined at toplevel and the inherit flag is false" do
+ lambda do
+ ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST1, false)
+ end.should raise_error(NameError)
+ end
+
+ it "accepts a toplevel scope qualifier" do
+ ConstantSpecs.const_get("::CS_CONST1").should == :const1
+ end
+
+ it "accepts a scoped constant name" do
+ ConstantSpecs.const_get("ClassA::CS_CONST10").should == :const10_10
+ end
+
+ it "raises a NameError if the name includes two successive scope separators" do
+ lambda { ConstantSpecs.const_get("ClassA::::CS_CONST10") }.should raise_error(NameError)
+ end
+
+ it "raises a NameError if only '::' is passed" do
+ lambda { ConstantSpecs.const_get("::") }.should raise_error(NameError)
+ end
+
+ it "raises a NameError if a Symbol has a toplevel scope qualifier" do
+ lambda { ConstantSpecs.const_get(:'::CS_CONST1') }.should raise_error(NameError)
+ end
+
+ it "raises a NameError if a Symbol is a scoped constant name" do
+ lambda { ConstantSpecs.const_get(:'ClassA::CS_CONST10') }.should raise_error(NameError)
+ end
+
+ it "does read private constants" do
+ ConstantSpecs.const_get(:CS_PRIVATE).should == :cs_private
+ end
+
+ it 'does autoload a constant' do
+ Object.const_get('CSAutoloadA').name.should == 'CSAutoloadA'
+ end
+
+ it 'does autoload a constant with a toplevel scope qualifier' do
+ Object.const_get('::CSAutoloadB').name.should == 'CSAutoloadB'
+ end
+
+ it 'does autoload a module and resolve a constant within' do
+ Object.const_get('CSAutoloadC::CONST').should == 7
+ end
+
+ it 'does autoload a non-toplevel module' do
+ Object.const_get('CSAutoloadD::InnerModule').name.should == 'CSAutoloadD::InnerModule'
+ end
+
+ describe "with statically assigned constants" do
+ it "searches the immediate class or module first" do
+ ConstantSpecs::ClassA.const_get(:CS_CONST10).should == :const10_10
+ ConstantSpecs::ModuleA.const_get(:CS_CONST10).should == :const10_1
+ ConstantSpecs::ParentA.const_get(:CS_CONST10).should == :const10_5
+ ConstantSpecs::ContainerA.const_get(:CS_CONST10).should == :const10_2
+ ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST10).should == :const10_3
+ end
+
+ it "searches a module included in the immediate class before the superclass" do
+ ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST15).should == :const15_1
+ end
+
+ it "searches the superclass before a module included in the superclass" do
+ ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST11).should == :const11_1
+ end
+
+ it "searches a module included in the superclass" do
+ ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST12).should == :const12_1
+ end
+
+ it "searches the superclass chain" do
+ ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST13).should == :const13
+ end
+
+ it "returns a toplevel constant when the receiver is a Class" do
+ ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST1).should == :const1
+ end
+
+ it "returns a toplevel constant when the receiver is a Module" do
+ ConstantSpecs.const_get(:CS_CONST1).should == :const1
+ ConstantSpecs::ModuleA.const_get(:CS_CONST1).should == :const1
+ end
+ end
+
+ describe "with dynamically assigned constants" do
+ it "searches the immediate class or module first" do
+ ConstantSpecs::ClassA::CS_CONST301 = :const301_1
+ ConstantSpecs::ClassA.const_get(:CS_CONST301).should == :const301_1
+
+ ConstantSpecs::ModuleA::CS_CONST301 = :const301_2
+ ConstantSpecs::ModuleA.const_get(:CS_CONST301).should == :const301_2
+
+ ConstantSpecs::ParentA::CS_CONST301 = :const301_3
+ ConstantSpecs::ParentA.const_get(:CS_CONST301).should == :const301_3
+
+ ConstantSpecs::ContainerA::ChildA::CS_CONST301 = :const301_5
+ ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST301).should == :const301_5
+ end
+
+ it "searches a module included in the immediate class before the superclass" do
+ ConstantSpecs::ParentB::CS_CONST302 = :const302_1
+ ConstantSpecs::ModuleF::CS_CONST302 = :const302_2
+ ConstantSpecs::ContainerB::ChildB.const_get(:CS_CONST302).should == :const302_2
+ end
+
+ it "searches the superclass before a module included in the superclass" do
+ ConstantSpecs::ModuleE::CS_CONST303 = :const303_1
+ ConstantSpecs::ParentB::CS_CONST303 = :const303_2
+ ConstantSpecs::ContainerB::ChildB.const_get(:CS_CONST303).should == :const303_2
+ end
+
+ it "searches a module included in the superclass" do
+ ConstantSpecs::ModuleA::CS_CONST304 = :const304_1
+ ConstantSpecs::ModuleE::CS_CONST304 = :const304_2
+ ConstantSpecs::ContainerB::ChildB.const_get(:CS_CONST304).should == :const304_2
+ end
+
+ it "searches the superclass chain" do
+ ConstantSpecs::ModuleA::CS_CONST305 = :const305
+ ConstantSpecs::ContainerB::ChildB.const_get(:CS_CONST305).should == :const305
+ end
+
+ it "returns a toplevel constant when the receiver is a Class" do
+ Object::CS_CONST306 = :const306
+ ConstantSpecs::ContainerB::ChildB.const_get(:CS_CONST306).should == :const306
+ end
+
+ it "returns a toplevel constant when the receiver is a Module" do
+ Object::CS_CONST308 = :const308
+ ConstantSpecs.const_get(:CS_CONST308).should == :const308
+ ConstantSpecs::ModuleA.const_get(:CS_CONST308).should == :const308
+ end
+
+ it "returns the updated value of a constant" do
+ ConstantSpecs::ClassB::CS_CONST309 = :const309_1
+ ConstantSpecs::ClassB.const_get(:CS_CONST309).should == :const309_1
+
+ lambda {
+ ConstantSpecs::ClassB::CS_CONST309 = :const309_2
+ }.should complain(/already initialized constant/)
+ ConstantSpecs::ClassB.const_get(:CS_CONST309).should == :const309_2
+ end
+ end
+end
diff --git a/spec/ruby/core/module/const_missing_spec.rb b/spec/ruby/core/module/const_missing_spec.rb
new file mode 100644
index 0000000000..930fe63f8b
--- /dev/null
+++ b/spec/ruby/core/module/const_missing_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/constants'
+
+describe "Module#const_missing" do
+ it "is called when an undefined constant is referenced via literal form" do
+ ConstantSpecs::ClassA::CS_CONSTX.should == :CS_CONSTX
+ end
+
+ it "is called when an undefined constant is referenced via #const_get" do
+ ConstantSpecs::ClassA.const_get(:CS_CONSTX).should == :CS_CONSTX
+ end
+
+ it "raises NameError and includes the name of the value that wasn't found" do
+ lambda {
+ ConstantSpecs.const_missing("HelloMissing")
+ }.should raise_error(NameError, /ConstantSpecs::HelloMissing/)
+ end
+
+ it "raises NameError and does not include toplevel Object" do
+ begin
+ Object.const_missing("HelloMissing")
+ rescue NameError => e
+ e.message.should_not =~ / Object::/
+ end
+ end
+
+ it "is called regardless of visibility" do
+ klass = Class.new do
+ def self.const_missing(name)
+ "Found:#{name}"
+ end
+ private_class_method :const_missing
+ end
+ klass::Hello.should == 'Found:Hello'
+ end
+end
diff --git a/spec/ruby/core/module/const_set_spec.rb b/spec/ruby/core/module/const_set_spec.rb
new file mode 100644
index 0000000000..f3c69cd3e0
--- /dev/null
+++ b/spec/ruby/core/module/const_set_spec.rb
@@ -0,0 +1,86 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/constants'
+
+describe "Module#const_set" do
+ it "sets the constant specified by a String or Symbol to the given value" do
+ ConstantSpecs.const_set :CS_CONST401, :const401
+ ConstantSpecs::CS_CONST401.should == :const401
+
+ ConstantSpecs.const_set "CS_CONST402", :const402
+ ConstantSpecs.const_get(:CS_CONST402).should == :const402
+ end
+
+ it "returns the value set" do
+ ConstantSpecs.const_set(:CS_CONST403, :const403).should == :const403
+ end
+
+ it "sets the name of an anonymous module" do
+ m = Module.new
+ ConstantSpecs.const_set(:CS_CONST1000, m)
+ m.name.should == "ConstantSpecs::CS_CONST1000"
+ end
+
+ it "does not set the name of a module scoped by an anonymous module" do
+ a, b = Module.new, Module.new
+ a.const_set :B, b
+ b.name.should be_nil
+ end
+
+ it "sets the name of contained modules when assigning a toplevel anonymous module" do
+ a, b, c, d = Module.new, Module.new, Module.new, Module.new
+ a::B = b
+ a::B::C = c
+ a::B::C::E = c
+ a::D = d
+
+ Object.const_set :ModuleSpecs_CS3, a
+ a.name.should == "ModuleSpecs_CS3"
+ b.name.should == "ModuleSpecs_CS3::B"
+ c.name.should == "ModuleSpecs_CS3::B::C"
+ d.name.should == "ModuleSpecs_CS3::D"
+ end
+
+ it "raises a NameError if the name does not start with a capital letter" do
+ lambda { ConstantSpecs.const_set "name", 1 }.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the name starts with a non-alphabetic character" do
+ lambda { ConstantSpecs.const_set "__CONSTX__", 1 }.should raise_error(NameError)
+ lambda { ConstantSpecs.const_set "@Name", 1 }.should raise_error(NameError)
+ lambda { ConstantSpecs.const_set "!Name", 1 }.should raise_error(NameError)
+ lambda { ConstantSpecs.const_set "::Name", 1 }.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the name contains non-alphabetic characters except '_'" do
+ ConstantSpecs.const_set("CS_CONST404", :const404).should == :const404
+ lambda { ConstantSpecs.const_set "Name=", 1 }.should raise_error(NameError)
+ lambda { ConstantSpecs.const_set "Name?", 1 }.should raise_error(NameError)
+ end
+
+ it "calls #to_str to convert the given name to a String" do
+ name = mock("CS_CONST405")
+ name.should_receive(:to_str).and_return("CS_CONST405")
+ ConstantSpecs.const_set(name, :const405).should == :const405
+ ConstantSpecs::CS_CONST405.should == :const405
+ end
+
+ it "raises a TypeError if conversion to a String by calling #to_str fails" do
+ name = mock('123')
+ lambda { ConstantSpecs.const_set name, 1 }.should raise_error(TypeError)
+
+ name.should_receive(:to_str).and_return(123)
+ lambda { ConstantSpecs.const_set name, 1 }.should raise_error(TypeError)
+ end
+
+ describe "on a frozen module" do
+ before :each do
+ @frozen = Module.new.freeze
+ @name = :Foo
+ end
+
+ it "raises a #{frozen_error_class} before setting the name" do
+ lambda { @frozen.const_set @name, nil }.should raise_error(frozen_error_class)
+ @frozen.should_not have_constant(@name)
+ end
+ end
+end
diff --git a/spec/ruby/core/module/constants_spec.rb b/spec/ruby/core/module/constants_spec.rb
new file mode 100644
index 0000000000..4538e828dd
--- /dev/null
+++ b/spec/ruby/core/module/constants_spec.rb
@@ -0,0 +1,91 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/constants'
+require_relative 'fixtures/classes'
+
+describe "Module.constants" do
+ it "returns an array of the names of all toplevel constants" do
+ count = Module.constants.size
+ module ConstantSpecsAdded
+ end
+ Module.constants.size.should == count + 1
+ Object.send(:remove_const, :ConstantSpecsAdded)
+ end
+
+ it "returns an array of Symbol names" do
+ # This in NOT an exhaustive list
+ Module.constants.should include(:Array, :Bignum, :Class, :Comparable, :Dir,
+ :Enumerable, :ENV, :Exception, :FalseClass,
+ :File, :Fixnum, :Float, :Hash, :Integer, :IO,
+ :Kernel, :Math, :Method, :Module, :NilClass,
+ :Numeric, :Object, :Range, :Regexp, :String,
+ :Symbol, :Thread, :Time, :TrueClass)
+ end
+
+ it "returns Module's constants when given a parameter" do
+ direct = Module.constants(false)
+ indirect = Module.constants(true)
+ module ConstantSpecsIncludedModule
+ MODULE_CONSTANTS_SPECS_INDIRECT = :foo
+ end
+
+ class Module
+ MODULE_CONSTANTS_SPECS_DIRECT = :bar
+ include ConstantSpecsIncludedModule
+ end
+ (Module.constants(false) - direct).should == [:MODULE_CONSTANTS_SPECS_DIRECT]
+ (Module.constants(true) - indirect).sort.should == [:MODULE_CONSTANTS_SPECS_DIRECT, :MODULE_CONSTANTS_SPECS_INDIRECT]
+
+ Module.send(:remove_const, :MODULE_CONSTANTS_SPECS_DIRECT)
+ ConstantSpecsIncludedModule.send(:remove_const, :MODULE_CONSTANTS_SPECS_INDIRECT)
+ end
+end
+
+describe "Module#constants" do
+ it "returns an array of Symbol names of all constants defined in the module and all included modules" do
+ ConstantSpecs::ContainerA.constants.sort.should == [
+ :CS_CONST10, :CS_CONST23, :CS_CONST24, :CS_CONST5, :ChildA
+ ]
+ end
+
+ it "returns all constants including inherited when passed true" do
+ ConstantSpecs::ContainerA.constants(true).sort.should == [
+ :CS_CONST10, :CS_CONST23, :CS_CONST24, :CS_CONST5, :ChildA
+ ]
+ end
+
+ it "returns all constants including inherited when passed some object" do
+ ConstantSpecs::ContainerA.constants(Object.new).sort.should == [
+ :CS_CONST10, :CS_CONST23, :CS_CONST24, :CS_CONST5, :ChildA
+ ]
+ end
+
+ it "doesn't returns inherited constants when passed false" do
+ ConstantSpecs::ContainerA.constants(false).sort.should == [
+ :CS_CONST10, :CS_CONST23, :CS_CONST5, :ChildA
+ ]
+ end
+
+ it "doesn't returns inherited constants when passed nil" do
+ ConstantSpecs::ContainerA.constants(nil).sort.should == [
+ :CS_CONST10, :CS_CONST23, :CS_CONST5, :ChildA
+ ]
+ end
+
+ it "returns only public constants" do
+ ModuleSpecs::PrivConstModule.constants.should == [:PUBLIC_CONSTANT]
+ end
+end
+
+describe "Module#constants" do
+ before :each do
+ ConstantSpecs::ModuleM::CS_CONST251 = :const251
+ end
+
+ after :each do
+ ConstantSpecs::ModuleM.send(:remove_const, :CS_CONST251)
+ end
+
+ it "includes names of constants defined after a module is included" do
+ ConstantSpecs::ContainerA.constants.should include(:CS_CONST251)
+ end
+end
diff --git a/spec/ruby/core/module/define_method_spec.rb b/spec/ruby/core/module/define_method_spec.rb
new file mode 100644
index 0000000000..3f35c051a1
--- /dev/null
+++ b/spec/ruby/core/module/define_method_spec.rb
@@ -0,0 +1,656 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+class DefineMethodSpecClass
+end
+
+describe "passed { |a, b = 1| } creates a method that" do
+ before :each do
+ @klass = Class.new do
+ define_method(:m) { |a, b = 1| return a, b }
+ end
+ end
+
+ it "raises an ArgumentError when passed zero arguments" do
+ lambda { @klass.new.m }.should raise_error(ArgumentError)
+ end
+
+ it "has a default value for b when passed one argument" do
+ @klass.new.m(1).should == [1, 1]
+ end
+
+ it "overrides the default argument when passed two arguments" do
+ @klass.new.m(1, 2).should == [1, 2]
+ end
+
+ it "raises an ArgumentError when passed three arguments" do
+ lambda { @klass.new.m(1, 2, 3) }.should raise_error(ArgumentError)
+ end
+end
+
+describe "Module#define_method when given an UnboundMethod" do
+ it "passes the given arguments to the new method" do
+ klass = Class.new do
+ def test_method(arg1, arg2)
+ [arg1, arg2]
+ end
+ define_method(:another_test_method, instance_method(:test_method))
+ end
+
+ klass.new.another_test_method(1, 2).should == [1, 2]
+ end
+
+ it "adds the new method to the methods list" do
+ klass = Class.new do
+ def test_method(arg1, arg2)
+ [arg1, arg2]
+ end
+ define_method(:another_test_method, instance_method(:test_method))
+ end
+ klass.new.should have_method(:another_test_method)
+ end
+
+ describe "defining a method on a singleton class" do
+ before do
+ klass = Class.new
+ class << klass
+ def test_method
+ :foo
+ end
+ end
+ child = Class.new(klass)
+ sc = class << child; self; end
+ sc.send :define_method, :another_test_method, klass.method(:test_method).unbind
+
+ @class = child
+ end
+
+ it "doesn't raise TypeError when calling the method" do
+ @class.another_test_method.should == :foo
+ end
+ end
+
+ it "sets the new method's visibility to the current frame's visibility" do
+ foo = Class.new do
+ def ziggy
+ 'piggy'
+ end
+ private :ziggy
+
+ # make sure frame visibility is public
+ public
+
+ define_method :piggy, instance_method(:ziggy)
+ end
+
+ lambda { foo.new.ziggy }.should raise_error(NoMethodError)
+ foo.new.piggy.should == 'piggy'
+ end
+end
+
+describe "Module#define_method when name is not a special private name" do
+ describe "given an UnboundMethod" do
+ describe "and called from the target module" do
+ it "sets the visibility of the method to the current visibility" do
+ klass = Class.new do
+ define_method(:bar, ModuleSpecs::EmptyFooMethod)
+ private
+ define_method(:baz, ModuleSpecs::EmptyFooMethod)
+ end
+
+ klass.should have_public_instance_method(:bar)
+ klass.should have_private_instance_method(:baz)
+ end
+ end
+
+ describe "and called from another module" do
+ it "sets the visibility of the method to public" do
+ klass = Class.new
+ Class.new do
+ klass.send(:define_method, :bar, ModuleSpecs::EmptyFooMethod)
+ private
+ klass.send(:define_method, :baz, ModuleSpecs::EmptyFooMethod)
+ end
+
+ klass.should have_public_instance_method(:bar)
+ klass.should have_public_instance_method(:baz)
+ end
+ end
+ end
+
+ describe "passed a block" do
+ describe "and called from the target module" do
+ it "sets the visibility of the method to the current visibility" do
+ klass = Class.new do
+ define_method(:bar) {}
+ private
+ define_method(:baz) {}
+ end
+
+ klass.should have_public_instance_method(:bar)
+ klass.should have_private_instance_method(:baz)
+ end
+ end
+
+ describe "and called from another module" do
+ it "sets the visibility of the method to public" do
+ klass = Class.new
+ Class.new do
+ klass.send(:define_method, :bar) {}
+ private
+ klass.send(:define_method, :baz) {}
+ end
+
+ klass.should have_public_instance_method(:bar)
+ klass.should have_public_instance_method(:baz)
+ end
+ end
+ end
+end
+
+describe "Module#define_method when name is :initialize" do
+ describe "passed a block" do
+ it "sets visibility to private when method name is :initialize" do
+ klass = Class.new do
+ define_method(:initialize) { }
+ end
+ klass.should have_private_instance_method(:initialize)
+ end
+ end
+
+ describe "given an UnboundMethod" do
+ it "sets the visibility to private when method is named :initialize" do
+ klass = Class.new do
+ def test_method
+ end
+ define_method(:initialize, instance_method(:test_method))
+ end
+ klass.should have_private_instance_method(:initialize)
+ end
+ end
+end
+
+describe "Module#define_method" do
+ it "defines the given method as an instance method with the given name in self" do
+ class DefineMethodSpecClass
+ def test1
+ "test"
+ end
+ define_method(:another_test, instance_method(:test1))
+ end
+
+ o = DefineMethodSpecClass.new
+ o.test1.should == o.another_test
+ end
+
+ it "calls #method_added after the method is added to the Module" do
+ DefineMethodSpecClass.should_receive(:method_added).with(:test_ma)
+
+ class DefineMethodSpecClass
+ define_method(:test_ma) { true }
+ end
+ end
+
+ it "defines a new method with the given name and the given block as body in self" do
+ class DefineMethodSpecClass
+ define_method(:block_test1) { self }
+ define_method(:block_test2, &lambda { self })
+ end
+
+ o = DefineMethodSpecClass.new
+ o.block_test1.should == o
+ o.block_test2.should == o
+ end
+
+ it "raises a TypeError when the given method is no Method/Proc" do
+ lambda {
+ Class.new { define_method(:test, "self") }
+ }.should raise_error(TypeError)
+
+ lambda {
+ Class.new { define_method(:test, 1234) }
+ }.should raise_error(TypeError)
+
+ lambda {
+ Class.new { define_method(:test, nil) }
+ }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError when no block is given" do
+ lambda {
+ Class.new { define_method(:test) }
+ }.should raise_error(ArgumentError)
+ end
+
+ it "does not use the caller block when no block is given" do
+ o = Object.new
+ def o.define(name)
+ self.class.class_eval do
+ define_method(name)
+ end
+ end
+
+ lambda {
+ o.define(:foo) { raise "not used" }
+ }.should raise_error(ArgumentError)
+ end
+
+ it "does not change the arity check style of the original proc" do
+ class DefineMethodSpecClass
+ prc = Proc.new { || true }
+ define_method("proc_style_test", &prc)
+ end
+
+ obj = DefineMethodSpecClass.new
+ lambda { obj.proc_style_test :arg }.should raise_error(ArgumentError)
+ end
+
+ it "raises a #{frozen_error_class} if frozen" do
+ lambda {
+ Class.new { freeze; define_method(:foo) {} }
+ }.should raise_error(frozen_error_class)
+ end
+
+ it "accepts a Method (still bound)" do
+ class DefineMethodSpecClass
+ attr_accessor :data
+ def inspect_data
+ "data is #{@data}"
+ end
+ end
+ o = DefineMethodSpecClass.new
+ o.data = :foo
+ m = o.method(:inspect_data)
+ m.should be_an_instance_of(Method)
+ klass = Class.new(DefineMethodSpecClass)
+ klass.send(:define_method,:other_inspect, m)
+ c = klass.new
+ c.data = :bar
+ c.other_inspect.should == "data is bar"
+ lambda{o.other_inspect}.should raise_error(NoMethodError)
+ end
+
+ it "raises a TypeError when a Method from a singleton class is defined on another class" do
+ c = Class.new do
+ class << self
+ def foo
+ end
+ end
+ end
+ m = c.method(:foo)
+
+ lambda {
+ Class.new { define_method :bar, m }
+ }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when a Method from one class is defined on an unrelated class" do
+ c = Class.new do
+ def foo
+ end
+ end
+ m = c.new.method(:foo)
+
+ lambda {
+ Class.new { define_method :bar, m }
+ }.should raise_error(TypeError)
+ end
+
+ it "accepts an UnboundMethod from an attr_accessor method" do
+ class DefineMethodSpecClass
+ attr_accessor :accessor_method
+ end
+
+ m = DefineMethodSpecClass.instance_method(:accessor_method)
+ o = DefineMethodSpecClass.new
+
+ DefineMethodSpecClass.send(:undef_method, :accessor_method)
+ lambda { o.accessor_method }.should raise_error(NoMethodError)
+
+ DefineMethodSpecClass.send(:define_method, :accessor_method, m)
+
+ o.accessor_method = :abc
+ o.accessor_method.should == :abc
+ end
+
+ it "accepts a proc from a method" do
+ class ProcFromMethod
+ attr_accessor :data
+ def cool_method
+ "data is #{@data}"
+ end
+ end
+
+ object1 = ProcFromMethod.new
+ object1.data = :foo
+
+ method_proc = object1.method(:cool_method).to_proc
+ klass = Class.new(ProcFromMethod)
+ klass.send(:define_method, :other_cool_method, &method_proc)
+
+ object2 = klass.new
+ object2.data = :bar
+ object2.other_cool_method.should == "data is foo"
+ end
+
+ it "maintains the Proc's scope" do
+ class DefineMethodByProcClass
+ in_scope = true
+ method_proc = proc { in_scope }
+
+ define_method(:proc_test, &method_proc)
+ end
+
+ o = DefineMethodByProcClass.new
+ o.proc_test.should be_true
+ end
+
+ it "accepts a String method name" do
+ klass = Class.new do
+ define_method("string_test") do
+ "string_test result"
+ end
+ end
+
+ klass.new.string_test.should == "string_test result"
+ end
+
+ ruby_version_is ''...'2.5' do
+ it "is a private method" do
+ Module.should have_private_instance_method(:define_method)
+ end
+ end
+ ruby_version_is '2.5' do
+ it "is a public method" do
+ Module.should have_public_instance_method(:define_method)
+ end
+ end
+
+ it "returns its symbol" do
+ class DefineMethodSpecClass
+ method = define_method("return_test") { true }
+ method.should == :return_test
+ end
+ end
+
+ it "allows an UnboundMethod from a module to be defined on a class" do
+ klass = Class.new {
+ define_method :bar, ModuleSpecs::UnboundMethodTest.instance_method(:foo)
+ }
+ klass.new.should respond_to(:bar)
+ end
+
+ it "allows an UnboundMethod from a parent class to be defined on a child class" do
+ parent = Class.new { define_method(:foo) { :bar } }
+ child = Class.new(parent) {
+ define_method :baz, parent.instance_method(:foo)
+ }
+ child.new.should respond_to(:baz)
+ end
+
+ it "allows an UnboundMethod from a module to be defined on another unrelated module" do
+ mod = Module.new {
+ define_method :bar, ModuleSpecs::UnboundMethodTest.instance_method(:foo)
+ }
+ klass = Class.new { include mod }
+ klass.new.should respond_to(:bar)
+ end
+
+ it "raises a TypeError when an UnboundMethod from a child class is defined on a parent class" do
+ lambda {
+ ParentClass = Class.new { define_method(:foo) { :bar } }
+ ChildClass = Class.new(ParentClass) { define_method(:foo) { :baz } }
+ ParentClass.send :define_method, :foo, ChildClass.instance_method(:foo)
+ }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when an UnboundMethod from one class is defined on an unrelated class" do
+ lambda {
+ DestinationClass = Class.new {
+ define_method :bar, ModuleSpecs::InstanceMeth.instance_method(:foo)
+ }
+ }.should raise_error(TypeError)
+ end
+end
+
+describe "Module#define_method" do
+ describe "passed { } creates a method that" do
+ before :each do
+ @klass = Class.new do
+ define_method(:m) { :called }
+ end
+ end
+
+ it "returns the value computed by the block when passed zero arguments" do
+ @klass.new.m().should == :called
+ end
+
+ it "raises an ArgumentError when passed one argument" do
+ lambda { @klass.new.m 1 }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed two arguments" do
+ lambda { @klass.new.m 1, 2 }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "passed { || } creates a method that" do
+ before :each do
+ @klass = Class.new do
+ define_method(:m) { || :called }
+ end
+ end
+
+ it "returns the value computed by the block when passed zero arguments" do
+ @klass.new.m().should == :called
+ end
+
+ it "raises an ArgumentError when passed one argument" do
+ lambda { @klass.new.m 1 }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed two arguments" do
+ lambda { @klass.new.m 1, 2 }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "passed { |a| } creates a method that" do
+ before :each do
+ @klass = Class.new do
+ define_method(:m) { |a| a }
+ end
+ end
+
+ it "raises an ArgumentError when passed zero arguments" do
+ lambda { @klass.new.m }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed zero arguments and a block" do
+ lambda { @klass.new.m { :computed } }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed two arguments" do
+ lambda { @klass.new.m 1, 2 }.should raise_error(ArgumentError)
+ end
+
+ it "receives the value passed as the argument when passed one argument" do
+ @klass.new.m(1).should == 1
+ end
+
+ end
+
+ describe "passed { |*a| } creates a method that" do
+ before :each do
+ @klass = Class.new do
+ define_method(:m) { |*a| a }
+ end
+ end
+
+ it "receives an empty array as the argument when passed zero arguments" do
+ @klass.new.m().should == []
+ end
+
+ it "receives the value in an array when passed one argument" do
+ @klass.new.m(1).should == [1]
+ end
+
+ it "receives the values in an array when passed two arguments" do
+ @klass.new.m(1, 2).should == [1, 2]
+ end
+ end
+
+ describe "passed { |a, *b| } creates a method that" do
+ before :each do
+ @klass = Class.new do
+ define_method(:m) { |a, *b| return a, b }
+ end
+ end
+
+ it "raises an ArgumentError when passed zero arguments" do
+ lambda { @klass.new.m }.should raise_error(ArgumentError)
+ end
+
+ it "returns the value computed by the block when passed one argument" do
+ @klass.new.m(1).should == [1, []]
+ end
+
+ it "returns the value computed by the block when passed two arguments" do
+ @klass.new.m(1, 2).should == [1, [2]]
+ end
+
+ it "returns the value computed by the block when passed three arguments" do
+ @klass.new.m(1, 2, 3).should == [1, [2, 3]]
+ end
+ end
+
+ describe "passed { |a, b| } creates a method that" do
+ before :each do
+ @klass = Class.new do
+ define_method(:m) { |a, b| return a, b }
+ end
+ end
+
+ it "returns the value computed by the block when passed two arguments" do
+ @klass.new.m(1, 2).should == [1, 2]
+ end
+
+ it "raises an ArgumentError when passed zero arguments" do
+ lambda { @klass.new.m }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed one argument" do
+ lambda { @klass.new.m 1 }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed one argument and a block" do
+ lambda { @klass.new.m(1) { } }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed three arguments" do
+ lambda { @klass.new.m 1, 2, 3 }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "passed { |a, b, *c| } creates a method that" do
+ before :each do
+ @klass = Class.new do
+ define_method(:m) { |a, b, *c| return a, b, c }
+ end
+ end
+
+ it "raises an ArgumentError when passed zero arguments" do
+ lambda { @klass.new.m }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed one argument" do
+ lambda { @klass.new.m 1 }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed one argument and a block" do
+ lambda { @klass.new.m(1) { } }.should raise_error(ArgumentError)
+ end
+
+ it "receives an empty array as the third argument when passed two arguments" do
+ @klass.new.m(1, 2).should == [1, 2, []]
+ end
+
+ it "receives the third argument in an array when passed three arguments" do
+ @klass.new.m(1, 2, 3).should == [1, 2, [3]]
+ end
+ end
+end
+
+describe "Method#define_method when passed a Method object" do
+ before :each do
+ @klass = Class.new do
+ def m(a, b, *c)
+ :m
+ end
+ end
+
+ @obj = @klass.new
+ m = @obj.method :m
+
+ @klass.class_exec do
+ define_method :n, m
+ end
+ end
+
+ it "defines a method with the same #arity as the original" do
+ @obj.method(:n).arity.should == @obj.method(:m).arity
+ end
+
+ it "defines a method with the same #parameters as the original" do
+ @obj.method(:n).parameters.should == @obj.method(:m).parameters
+ end
+end
+
+describe "Method#define_method when passed an UnboundMethod object" do
+ before :each do
+ @klass = Class.new do
+ def m(a, b, *c)
+ :m
+ end
+ end
+
+ @obj = @klass.new
+ m = @klass.instance_method :m
+
+ @klass.class_exec do
+ define_method :n, m
+ end
+ end
+
+ it "defines a method with the same #arity as the original" do
+ @obj.method(:n).arity.should == @obj.method(:m).arity
+ end
+
+ it "defines a method with the same #parameters as the original" do
+ @obj.method(:n).parameters.should == @obj.method(:m).parameters
+ end
+end
+
+describe "Method#define_method when passed a Proc object" do
+ describe "and a method is defined inside" do
+ it "defines the nested method in the default definee where the Proc was created" do
+ prc = nil
+ t = Class.new do
+ prc = -> {
+ def nested_method_in_proc_for_define_method
+ 42
+ end
+ }
+ end
+
+ c = Class.new do
+ define_method(:test, prc)
+ end
+
+ o = c.new
+ o.test
+ o.should_not have_method :nested_method_in_proc_for_define_method
+
+ t.new.nested_method_in_proc_for_define_method.should == 42
+ end
+ end
+end
diff --git a/spec/ruby/core/module/define_singleton_method_spec.rb b/spec/ruby/core/module/define_singleton_method_spec.rb
new file mode 100644
index 0000000000..38a4fc51b7
--- /dev/null
+++ b/spec/ruby/core/module/define_singleton_method_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "Module#define_singleton_method" do
+ it "defines the given method as an class method with the given name in self" do
+ klass = Module.new do
+ define_singleton_method :a do
+ 42
+ end
+ define_singleton_method(:b, lambda {|x| 2*x })
+ end
+
+ klass.a.should == 42
+ klass.b(10).should == 20
+ end
+
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/module/deprecate_constant_spec.rb b/spec/ruby/core/module/deprecate_constant_spec.rb
new file mode 100644
index 0000000000..0954a6d8a5
--- /dev/null
+++ b/spec/ruby/core/module/deprecate_constant_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+
+describe "Module#deprecate_constant" do
+ before :each do
+ @module = Module.new
+ @value = :value
+ @module::PUBLIC1 = @value
+ @module::PUBLIC2 = @value
+ @module::PRIVATE = @value
+ @module.private_constant :PRIVATE
+ @module.deprecate_constant :PRIVATE
+ @pattern = /deprecated/
+ end
+
+ describe "when accessing the deprecated module" do
+ it "passes the accessing" do
+ @module.deprecate_constant :PUBLIC1
+
+ value = nil
+ lambda {
+ value = @module::PUBLIC1
+ }.should complain(@pattern)
+ value.should equal(@value)
+
+ lambda { @module::PRIVATE }.should raise_error(NameError)
+ end
+
+ it "warns with a message" do
+ @module.deprecate_constant :PUBLIC1
+
+ lambda { @module::PUBLIC1 }.should complain(@pattern)
+ lambda { @module.const_get :PRIVATE }.should complain(@pattern)
+ end
+ end
+
+ it "accepts multiple symbols and strings as constant names" do
+ @module.deprecate_constant "PUBLIC1", :PUBLIC2
+
+ lambda { @module::PUBLIC1 }.should complain(@pattern)
+ lambda { @module::PUBLIC2 }.should complain(@pattern)
+ end
+
+ it "returns self" do
+ @module.deprecate_constant(:PUBLIC1).should equal(@module)
+ end
+
+ it "raises a NameError when given an undefined name" do
+ lambda { @module.deprecate_constant :UNDEFINED }.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/core/module/eql_spec.rb b/spec/ruby/core/module/eql_spec.rb
new file mode 100644
index 0000000000..76bb271d8d
--- /dev/null
+++ b/spec/ruby/core/module/eql_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/equal_value'
+
+describe "Module#eql?" do
+ it_behaves_like :module_equal, :eql?
+end
diff --git a/spec/ruby/core/module/equal_spec.rb b/spec/ruby/core/module/equal_spec.rb
new file mode 100644
index 0000000000..01ab06152d
--- /dev/null
+++ b/spec/ruby/core/module/equal_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/equal_value'
+
+describe "Module#equal?" do
+ it_behaves_like :module_equal, :equal?
+end
diff --git a/spec/ruby/core/module/equal_value_spec.rb b/spec/ruby/core/module/equal_value_spec.rb
new file mode 100644
index 0000000000..a7191cd755
--- /dev/null
+++ b/spec/ruby/core/module/equal_value_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/equal_value'
+
+describe "Module#==" do
+ it_behaves_like :module_equal, :==
+end
diff --git a/spec/ruby/core/module/extend_object_spec.rb b/spec/ruby/core/module/extend_object_spec.rb
new file mode 100644
index 0000000000..df16f03ea0
--- /dev/null
+++ b/spec/ruby/core/module/extend_object_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#extend_object" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "is a private method" do
+ Module.should have_private_instance_method(:extend_object)
+ end
+
+ describe "on Class" do
+ it "is undefined" do
+ Class.should_not have_private_instance_method(:extend_object, true)
+ end
+
+ it "raises a TypeError if calling after rebinded to Class" do
+ lambda {
+ Module.instance_method(:extend_object).bind(Class.new).call Object.new
+ }.should raise_error(TypeError)
+ end
+ end
+
+ it "is called when #extend is called on an object" do
+ ModuleSpecs::ExtendObject.should_receive(:extend_object)
+ obj = mock("extended object")
+ obj.extend ModuleSpecs::ExtendObject
+ end
+
+ it "extends the given object with its constants and methods by default" do
+ obj = mock("extended direct")
+ ModuleSpecs::ExtendObject.send :extend_object, obj
+
+ obj.test_method.should == "hello test"
+ obj.singleton_class.const_get(:C).should == :test
+ end
+
+ it "is called even when private" do
+ obj = mock("extended private")
+ obj.extend ModuleSpecs::ExtendObjectPrivate
+ ScratchPad.recorded.should == :extended
+ end
+
+ it "does not copy own tainted status to the given object" do
+ other = Object.new
+ Module.new.taint.send :extend_object, other
+ other.tainted?.should be_false
+ end
+
+ it "does not copy own untrusted status to the given object" do
+ other = Object.new
+ Module.new.untrust.send :extend_object, other
+ other.untrusted?.should be_false
+ end
+
+ describe "when given a frozen object" do
+ before :each do
+ @receiver = Module.new
+ @object = Object.new.freeze
+ end
+
+ it "raises a RuntimeError before extending the object" do
+ lambda { @receiver.send(:extend_object, @object) }.should raise_error(RuntimeError)
+ @object.should_not be_kind_of(@receiver)
+ end
+ end
+end
diff --git a/spec/ruby/core/module/extended_spec.rb b/spec/ruby/core/module/extended_spec.rb
new file mode 100644
index 0000000000..c6300ffa0b
--- /dev/null
+++ b/spec/ruby/core/module/extended_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#extended" do
+ it "is called when an object gets extended with self" do
+ begin
+ m = Module.new do
+ def self.extended(o)
+ $extended_object = o
+ end
+ end
+
+ (o = mock('x')).extend(m)
+
+ $extended_object.should == o
+ ensure
+ $extended_object = nil
+ end
+ end
+
+ it "is called after Module#extend_object" do
+ begin
+ m = Module.new do
+ def self.extend_object(o)
+ $extended_object = nil
+ end
+
+ def self.extended(o)
+ $extended_object = o
+ end
+ end
+
+ (o = mock('x')).extend(m)
+
+ $extended_object.should == o
+ ensure
+ $extended_object = nil
+ end
+ end
+
+ it "is private in its default implementation" do
+ Module.new.private_methods.should include(:extended)
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload.rb b/spec/ruby/core/module/fixtures/autoload.rb
new file mode 100644
index 0000000000..5a77a2d9d4
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload.rb
@@ -0,0 +1 @@
+$m.const_set(:AAA, "test") unless $m.nil?
diff --git a/spec/ruby/core/module/fixtures/autoload_abc.rb b/spec/ruby/core/module/fixtures/autoload_abc.rb
new file mode 100644
index 0000000000..ffaec91cfe
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_abc.rb
@@ -0,0 +1,11 @@
+module ModuleSpecs::Autoload::FromThread
+ module A
+ class B
+ class C
+ def self.foo
+ :foo
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_c.rb b/spec/ruby/core/module/fixtures/autoload_c.rb
new file mode 100644
index 0000000000..ff2bcc548c
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_c.rb
@@ -0,0 +1,11 @@
+module ModuleSpecs::Autoload
+ class DynClass
+ class C
+ def loaded
+ :dynclass_c
+ end
+ end
+ end
+end
+
+ScratchPad.record :loaded
diff --git a/spec/ruby/core/module/fixtures/autoload_callback.rb b/spec/ruby/core/module/fixtures/autoload_callback.rb
new file mode 100644
index 0000000000..51d53eb580
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_callback.rb
@@ -0,0 +1,2 @@
+block = ScratchPad.recorded
+block.call
diff --git a/spec/ruby/core/module/fixtures/autoload_concur.rb b/spec/ruby/core/module/fixtures/autoload_concur.rb
new file mode 100644
index 0000000000..0585e36880
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_concur.rb
@@ -0,0 +1,9 @@
+ScratchPad.recorded << :con_pre
+Thread.current[:in_autoload_rb] = true
+sleep 0.1
+
+module ModuleSpecs::Autoload
+ Concur = 1
+end
+
+ScratchPad.recorded << :con_post
diff --git a/spec/ruby/core/module/fixtures/autoload_d.rb b/spec/ruby/core/module/fixtures/autoload_d.rb
new file mode 100644
index 0000000000..6f5eee741c
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_d.rb
@@ -0,0 +1,11 @@
+module ModuleSpecs::Autoload
+ module DynModule
+ class D
+ def loaded
+ :dynmodule_d
+ end
+ end
+ end
+end
+
+ScratchPad.record :loaded
diff --git a/spec/ruby/core/module/fixtures/autoload_during_autoload.rb b/spec/ruby/core/module/fixtures/autoload_during_autoload.rb
new file mode 100644
index 0000000000..5202bd8b23
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_during_autoload.rb
@@ -0,0 +1,7 @@
+block = ScratchPad.recorded
+ScratchPad.record(block.call)
+
+module ModuleSpecs::Autoload
+ class DuringAutoload
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_during_require.rb b/spec/ruby/core/module/fixtures/autoload_during_require.rb
new file mode 100644
index 0000000000..6fd81592e3
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_during_require.rb
@@ -0,0 +1,4 @@
+module ModuleSpecs::Autoload
+ class AutoloadDuringRequire
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_e.rb b/spec/ruby/core/module/fixtures/autoload_e.rb
new file mode 100644
index 0000000000..fb78c6cbd4
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_e.rb
@@ -0,0 +1,7 @@
+module ModuleSpecs::Autoload
+ class E
+ def loaded
+ :autoload_e
+ end
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_empty.rb b/spec/ruby/core/module/fixtures/autoload_empty.rb
new file mode 100644
index 0000000000..d7116f3049
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_empty.rb
@@ -0,0 +1 @@
+# This file is left empty on purpose
diff --git a/spec/ruby/core/module/fixtures/autoload_ex1.rb b/spec/ruby/core/module/fixtures/autoload_ex1.rb
new file mode 100644
index 0000000000..a90092389c
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_ex1.rb
@@ -0,0 +1,16 @@
+
+class ModuleSpecs::Autoload::EX1 < Exception
+ def self.trample1
+ 1.times { return }
+ end
+
+ def self.trample2
+ begin
+ raise "hello"
+ rescue
+ end
+ end
+
+ trample1
+ trample2
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_exception.rb b/spec/ruby/core/module/fixtures/autoload_exception.rb
new file mode 100644
index 0000000000..09acf9f537
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_exception.rb
@@ -0,0 +1,3 @@
+ScratchPad.record(:exception)
+
+raise 'intentional error to test failure conditions during autoloading'
diff --git a/spec/ruby/core/module/fixtures/autoload_f.rb b/spec/ruby/core/module/fixtures/autoload_f.rb
new file mode 100644
index 0000000000..54c2b05b7b
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_f.rb
@@ -0,0 +1,7 @@
+module ModuleSpecs::Autoload
+ module F
+ def self.loaded
+ :autoload_f
+ end
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_g.rb b/spec/ruby/core/module/fixtures/autoload_g.rb
new file mode 100644
index 0000000000..a1c4e429d9
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_g.rb
@@ -0,0 +1,7 @@
+module ModuleSpecs::Autoload
+ class G
+ def loaded
+ :autoload_g
+ end
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_h.rb b/spec/ruby/core/module/fixtures/autoload_h.rb
new file mode 100644
index 0000000000..53988c5382
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_h.rb
@@ -0,0 +1,7 @@
+module ModuleSpecs::Autoload
+ module H
+ def loaded
+ :autoload_h
+ end
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_i.rb b/spec/ruby/core/module/fixtures/autoload_i.rb
new file mode 100644
index 0000000000..f7f720516e
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_i.rb
@@ -0,0 +1,5 @@
+module ModuleSpecs::Autoload
+ I = :autoloaded
+end
+
+ScratchPad.record :loaded
diff --git a/spec/ruby/core/module/fixtures/autoload_j.rb b/spec/ruby/core/module/fixtures/autoload_j.rb
new file mode 100644
index 0000000000..da6d35d43d
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_j.rb
@@ -0,0 +1,3 @@
+module ModuleSpecs::Autoload
+ J = :autoload_j
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_k.rb b/spec/ruby/core/module/fixtures/autoload_k.rb
new file mode 100644
index 0000000000..431602bf80
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_k.rb
@@ -0,0 +1,7 @@
+module ModuleSpecs::Autoload
+ class KHash < Hash
+ K = :autoload_k
+ end
+end
+
+ScratchPad.record :loaded
diff --git a/spec/ruby/core/module/fixtures/autoload_lm.rb b/spec/ruby/core/module/fixtures/autoload_lm.rb
new file mode 100644
index 0000000000..d93d783cd0
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_lm.rb
@@ -0,0 +1,4 @@
+module ModuleSpecs::Autoload
+ L = :autoload_l
+ M = :autoload_m
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_nested.rb b/spec/ruby/core/module/fixtures/autoload_nested.rb
new file mode 100644
index 0000000000..073cec0dce
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_nested.rb
@@ -0,0 +1,8 @@
+module ModuleSpecs::Autoload
+ module GoodParent
+ class Nested
+ end
+ end
+end
+
+ScratchPad.record(:loaded)
diff --git a/spec/ruby/core/module/fixtures/autoload_never_set.rb b/spec/ruby/core/module/fixtures/autoload_never_set.rb
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_never_set.rb
@@ -0,0 +1 @@
+
diff --git a/spec/ruby/core/module/fixtures/autoload_o.rb b/spec/ruby/core/module/fixtures/autoload_o.rb
new file mode 100644
index 0000000000..7d88f969b2
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_o.rb
@@ -0,0 +1,2 @@
+# does not define ModuleSpecs::Autoload::O
+ScratchPad << :loaded
diff --git a/spec/ruby/core/module/fixtures/autoload_r.rb b/spec/ruby/core/module/fixtures/autoload_r.rb
new file mode 100644
index 0000000000..34209d20c3
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_r.rb
@@ -0,0 +1,4 @@
+module ModuleSpecs::Autoload
+ class R
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_raise.rb b/spec/ruby/core/module/fixtures/autoload_raise.rb
new file mode 100644
index 0000000000..f6051e3ba2
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_raise.rb
@@ -0,0 +1,2 @@
+ScratchPad << :raise
+raise "exception during autoload"
diff --git a/spec/ruby/core/module/fixtures/autoload_required_directly.rb b/spec/ruby/core/module/fixtures/autoload_required_directly.rb
new file mode 100644
index 0000000000..bed60a71ec
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_required_directly.rb
@@ -0,0 +1,7 @@
+block = ScratchPad.recorded
+ScratchPad.record(block.call)
+
+module ModuleSpecs::Autoload
+ class RequiredDirectly
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_required_directly_nested.rb b/spec/ruby/core/module/fixtures/autoload_required_directly_nested.rb
new file mode 100644
index 0000000000..a9f11c2188
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_required_directly_nested.rb
@@ -0,0 +1 @@
+ScratchPad.recorded.call
diff --git a/spec/ruby/core/module/fixtures/autoload_s.rb b/spec/ruby/core/module/fixtures/autoload_s.rb
new file mode 100644
index 0000000000..f5d412ff18
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_s.rb
@@ -0,0 +1,5 @@
+module ModuleSpecs::Autoload
+ S = :autoload_s
+end
+
+ScratchPad.record :loaded
diff --git a/spec/ruby/core/module/fixtures/autoload_subclass.rb b/spec/ruby/core/module/fixtures/autoload_subclass.rb
new file mode 100644
index 0000000000..569972118c
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_subclass.rb
@@ -0,0 +1,11 @@
+class YY
+end
+
+module ModuleSpecs
+ module Autoload
+ module XX
+ class YY < YY
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_t.rb b/spec/ruby/core/module/fixtures/autoload_t.rb
new file mode 100644
index 0000000000..4962c97f4c
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_t.rb
@@ -0,0 +1,3 @@
+module ModuleSpecs::Autoload::S
+ T = :autoload_t
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_v.rb b/spec/ruby/core/module/fixtures/autoload_v.rb
new file mode 100644
index 0000000000..2aa8c44169
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_v.rb
@@ -0,0 +1,7 @@
+module ModuleSpecs::Autoload::U
+ class V
+ def self.get_value
+ :autoload_uvx
+ end
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_w.rb b/spec/ruby/core/module/fixtures/autoload_w.rb
new file mode 100644
index 0000000000..997273812e
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_w.rb
@@ -0,0 +1,2 @@
+# Fails to define ModuleSpecs::Autoload::W::Y
+ScratchPad.record :loaded
diff --git a/spec/ruby/core/module/fixtures/autoload_w2.rb b/spec/ruby/core/module/fixtures/autoload_w2.rb
new file mode 100644
index 0000000000..a8dbebf322
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_w2.rb
@@ -0,0 +1 @@
+ScratchPad.record :loaded
diff --git a/spec/ruby/core/module/fixtures/autoload_x.rb b/spec/ruby/core/module/fixtures/autoload_x.rb
new file mode 100644
index 0000000000..a6c5842609
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_x.rb
@@ -0,0 +1,3 @@
+module ModuleSpecs::Autoload
+ X = :x
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_z.rb b/spec/ruby/core/module/fixtures/autoload_z.rb
new file mode 100644
index 0000000000..cce1c13f37
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_z.rb
@@ -0,0 +1,5 @@
+class ModuleSpecs::Autoload::YY
+end
+
+class ModuleSpecs::Autoload::Z < ModuleSpecs::Autoload::YY
+end
diff --git a/spec/ruby/core/module/fixtures/classes.rb b/spec/ruby/core/module/fixtures/classes.rb
new file mode 100644
index 0000000000..f93c39683e
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/classes.rb
@@ -0,0 +1,605 @@
+module ModuleSpecs
+ def self.without_test_modules(modules)
+ ignore = %w[MSpecRSpecAdapter PP::ObjectMixin ModuleSpecs::IncludedInObject MainSpecs::Module ConstantSpecs::ModuleA]
+ modules.reject { |k| ignore.include?(k.name) }
+ end
+
+ CONST = :plain_constant
+
+ module PrivConstModule
+ PRIVATE_CONSTANT = 1
+ private_constant :PRIVATE_CONSTANT
+ PUBLIC_CONSTANT = 2
+ end
+
+ class Subclass < Module
+ end
+
+ class SubclassSpec
+ end
+
+ class RemoveClassVariable
+ end
+
+ module LookupModInMod
+ INCS = :ethereal
+ end
+
+ module LookupMod
+ include LookupModInMod
+
+ MODS = :rockers
+ end
+
+ class Lookup
+ include LookupMod
+ LOOKIE = :lookie
+ end
+
+ class LookupChild < Lookup
+ end
+
+ class Parent
+ # For private_class_method spec
+ def self.private_method; end
+ private_class_method :private_method
+
+ def undefed_method() end
+ undef_method :undefed_method
+
+ def parent_method; end
+ def another_parent_method; end
+
+ # For public_class_method spec
+ private
+ def self.public_method; end
+ public_class_method :public_method
+
+ public
+ def public_parent() end
+
+ protected
+ def protected_parent() end
+
+ private
+ def private_parent() end
+ end
+
+ module Basic
+ def public_module() end
+
+ protected
+ def protected_module() end
+
+ private
+ def private_module() end
+ end
+
+ module Super
+ include Basic
+
+ def public_super_module() end
+
+ protected
+ def protected_super_module() end
+
+ private
+ def private_super_module() end
+
+ def super_included_method; end
+
+ class SuperChild
+ end
+ end
+
+ module Internal
+ end
+
+ class Child < Parent
+ include Super
+
+ class << self
+ include Internal
+ end
+ attr_accessor :accessor_method
+
+ def public_child() end
+
+ undef_method :parent_method
+ undef_method :another_parent_method
+
+ protected
+ def protected_child() end
+
+ private
+ def private_child() end
+ end
+
+ class Grandchild < Child
+ undef_method :super_included_method
+ end
+
+ class Child2 < Parent
+ attr_reader :foo
+ end
+
+ # Be careful touching the Counts* classes as there used for testing
+ # private_instance_methods, public_instance_methods, etc. So adding, removing
+ # a method will break those tests.
+ module CountsMixin
+ def public_3; end
+ public :public_3
+
+ def private_3; end
+ private :private_3
+
+ def protected_3; end
+ protected :protected_3
+ end
+
+ class CountsParent
+ include CountsMixin
+
+ def public_2; end
+
+ private
+ def private_2; end
+
+ protected
+ def protected_2; end
+ end
+
+ class CountsChild < CountsParent
+ def public_1; end
+
+ private
+ def private_1; end
+
+ protected
+ def protected_1; end
+ end
+
+ module AddConstant
+ end
+
+ module A
+ CONSTANT_A = :a
+ OVERRIDE = :a
+ def ma(); :a; end
+ def self.cma(); :a; end
+ end
+
+ module B
+ CONSTANT_B = :b
+ OVERRIDE = :b
+ include A
+ def mb(); :b; end
+ def self.cmb(); :b; end
+ end
+
+ class C
+ OVERRIDE = :c
+ include B
+ end
+
+ module Z
+ MODULE_SPEC_TOPLEVEL_CONSTANT = 1
+ end
+
+ module Alias
+ def report() :report end
+ alias publish report
+ end
+
+ class Allonym
+ include ModuleSpecs::Alias
+ end
+
+ class Aliasing
+ def self.make_alias(*a)
+ alias_method(*a)
+ end
+
+ def public_one; 1; end
+
+ def public_two(n); n * 2; end
+
+ private
+ def private_one; 1; end
+
+ protected
+ def protected_one; 1; end
+ end
+
+ class AliasingSubclass < Aliasing
+ end
+
+ module AliasingSuper
+
+ module Parent
+ def super_call(arg)
+ arg
+ end
+ end
+
+ module Child
+ include Parent
+ def super_call(arg)
+ super(arg)
+ end
+ end
+
+ class Target
+ include Child
+ alias_method :alias_super_call, :super_call
+ alias_method :super_call, :alias_super_call
+ end
+
+ class RedefineAfterAlias
+ include Parent
+
+ def super_call(arg)
+ super(arg)
+ end
+
+ alias_method :alias_super_call, :super_call
+
+ def super_call(arg)
+ :wrong
+ end
+ end
+ end
+
+
+ module ReopeningModule
+ def foo; true; end
+ module_function :foo
+ private :foo
+ end
+
+ # Yes, we want to re-open the module
+ module ReopeningModule
+ alias :foo2 :foo
+ module_function :foo2
+ end
+
+ module Nesting
+ @tests = {}
+ def self.[](name); @tests[name]; end
+ def self.[]=(name, val); @tests[name] = val; end
+ def self.meta; class << self; self; end; end
+
+ Nesting[:basic] = Module.nesting
+
+ module ::ModuleSpecs
+ Nesting[:open_first_level] = Module.nesting
+ end
+
+ class << self
+ Nesting[:open_meta] = Module.nesting
+ end
+
+ def self.called_from_module_method
+ Module.nesting
+ end
+
+ class NestedClass
+ Nesting[:nest_class] = Module.nesting
+
+ def self.called_from_class_method
+ Module.nesting
+ end
+
+ def called_from_inst_method
+ Module.nesting
+ end
+ end
+
+ end
+
+ Nesting[:first_level] = Module.nesting
+
+ module InstanceMethMod
+ def bar(); :bar; end
+ end
+
+ class InstanceMeth
+ include InstanceMethMod
+ def foo(); :foo; end
+ end
+
+ class InstanceMethChild < InstanceMeth
+ end
+
+ module ClassVars
+ class A
+ @@a_cvar = :a_cvar
+ end
+
+ module M
+ @@m_cvar = :m_cvar
+ end
+
+ class B < A
+ include M
+
+ @@b_cvar = :b_cvar
+ end
+ end
+
+ class CVars
+ @@cls = :class
+
+ # Singleton class lexical scopes are ignored for class variables
+ class << self
+ def cls
+ # This looks in the parent lexical scope, class CVars
+ @@cls
+ end
+ # This actually adds it to the parent lexical scope, class CVars
+ @@meta = :metainfo
+ end
+
+ def self.meta
+ @@meta
+ end
+
+ def meta
+ @@meta
+ end
+ end
+
+ module MVars
+ @@mvar = :mvar
+ end
+
+ class SubModule < Module
+ attr_reader :special
+ def initialize
+ @special = 10
+ end
+ end
+
+ module MA; end
+ module MB
+ include MA
+ end
+ module MC; end
+
+ class MultipleIncludes
+ include MB
+ end
+
+ # empty modules
+ module M1; end
+ module M2; end
+
+ module Autoload
+ def self.use_ex1
+ begin
+ begin
+ raise "test exception"
+ rescue ModuleSpecs::Autoload::EX1
+ end
+ rescue RuntimeError
+ return :good
+ end
+ end
+
+ module FromThread
+ module A
+ autoload :B, fixture(__FILE__, "autoload_empty.rb")
+
+ class B
+ autoload :C, fixture(__FILE__, "autoload_abc.rb")
+
+ def self.foo
+ C.foo
+ end
+ end
+ end
+
+ class D < A::B; end
+ end
+ end
+
+ # This class isn't inherited from or included in anywhere.
+ # It exists to test the constant scoping rules.
+ class Detached
+ DETACHED_CONSTANT = :d
+ end
+
+ class ParentPrivateMethodRedef
+ private
+ def private_method_redefined
+ :before_redefinition
+ end
+ end
+
+ class ChildPrivateMethodMadePublic < ParentPrivateMethodRedef
+ public :private_method_redefined
+ end
+
+ class ParentPrivateMethodRedef
+ def private_method_redefined
+ :after_redefinition
+ end
+ end
+
+ module CyclicAppendA
+ end
+
+ module CyclicAppendB
+ include CyclicAppendA
+ end
+
+ module CyclicPrepend
+ end
+
+ module ExtendObject
+ C = :test
+ def test_method
+ "hello test"
+ end
+ end
+
+ module ExtendObjectPrivate
+ class << self
+ def extend_object(obj)
+ ScratchPad.record :extended
+ end
+ private :extend_object
+ end
+ end
+
+ class CyclicBarrier
+ def initialize(count = 1)
+ @count = count
+ @state = 0
+ @mutex = Mutex.new
+ @cond = ConditionVariable.new
+ end
+
+ def await
+ @mutex.synchronize do
+ @state += 1
+ if @state >= @count
+ @state = 0
+ @cond.broadcast
+ true
+ else
+ @cond.wait @mutex
+ false
+ end
+ end
+ end
+
+ def enabled?
+ @mutex.synchronize { @count != -1 }
+ end
+
+ def disable!
+ @mutex.synchronize do
+ @count = -1
+ @cond.broadcast
+ end
+ end
+ end
+
+ class ThreadSafeCounter
+ def initialize(value = 0)
+ @value = 0
+ @mutex = Mutex.new
+ end
+
+ def get
+ @mutex.synchronize { @value }
+ end
+
+ def increment_and_get
+ @mutex.synchronize do
+ prev_value = @value
+ @value += 1
+ prev_value
+ end
+ end
+ end
+
+ module ShadowingOuter
+ module M
+ SHADOW = 123
+ end
+
+ module N
+ SHADOW = 456
+ end
+ end
+
+ module UnboundMethodTest
+ def foo
+ 'bar'
+ end
+ end
+
+ module ClassEvalTest
+ def self.get_constant_from_scope
+ module_eval("Lookup")
+ end
+
+ def self.get_constant_from_scope_with_send(method)
+ send(method, "Lookup")
+ end
+ end
+
+ class RecordIncludedModules
+ def self.inherited(base)
+ ScratchPad.record base
+ end
+ end
+
+ module SingletonOnModuleCase
+ module Foo
+ class << Foo
+ def included(base)
+ base.included_called
+ super
+ end
+ end
+ end
+
+ class Bar
+ @included_called = false
+
+ class << self
+ def included_called
+ @included_called = true
+ end
+
+ def included_called?
+ @included_called
+ end
+ end
+ end
+ end
+
+ module CaseCompareOnSingleton
+ def self.===(*)
+ raise 'method contents are irrelevant to test'
+ end
+ end
+
+ m = Module.new do
+ def foo
+ end
+ private :foo
+ end
+ EmptyFooMethod = m.instance_method(:foo)
+end
+
+class Object
+ def module_specs_public_method_on_object; end
+
+ def module_specs_private_method_on_object; end
+ private :module_specs_private_method_on_object
+
+ def module_specs_protected_method_on_object; end
+ protected :module_specs_private_method_on_object
+
+ def module_specs_private_method_on_object_for_kernel_public; end
+ private :module_specs_private_method_on_object_for_kernel_public
+
+ def module_specs_public_method_on_object_for_kernel_protected; end
+ def module_specs_public_method_on_object_for_kernel_private; end
+end
+
+module Kernel
+ def module_specs_public_method_on_kernel; end
+
+ alias_method :module_specs_alias_on_kernel, :module_specs_public_method_on_object
+
+ public :module_specs_private_method_on_object_for_kernel_public
+ protected :module_specs_public_method_on_object_for_kernel_protected
+ private :module_specs_public_method_on_object_for_kernel_private
+end
+
+ModuleSpecs::Nesting[:root_level] = Module.nesting
diff --git a/spec/ruby/core/module/fixtures/constant_unicode.rb b/spec/ruby/core/module/fixtures/constant_unicode.rb
new file mode 100644
index 0000000000..415911576d
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constant_unicode.rb
@@ -0,0 +1,5 @@
+# encoding: utf-8
+
+module ConstantUnicodeSpecs
+ CS_CONSTλ = :const_unicode
+end
diff --git a/spec/ruby/core/module/fixtures/constants_autoload.rb b/spec/ruby/core/module/fixtures/constants_autoload.rb
new file mode 100644
index 0000000000..8e9aa8de0c
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constants_autoload.rb
@@ -0,0 +1,6 @@
+autoload :CSAutoloadA, fixture(__FILE__, 'constants_autoload_a.rb')
+autoload :CSAutoloadB, fixture(__FILE__, 'constants_autoload_b.rb')
+autoload :CSAutoloadC, fixture(__FILE__, 'constants_autoload_c.rb')
+module CSAutoloadD
+ autoload :InnerModule, fixture(__FILE__, 'constants_autoload_d.rb')
+end
diff --git a/spec/ruby/core/module/fixtures/constants_autoload_a.rb b/spec/ruby/core/module/fixtures/constants_autoload_a.rb
new file mode 100644
index 0000000000..48d3b63681
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constants_autoload_a.rb
@@ -0,0 +1,2 @@
+module CSAutoloadA
+end
diff --git a/spec/ruby/core/module/fixtures/constants_autoload_b.rb b/spec/ruby/core/module/fixtures/constants_autoload_b.rb
new file mode 100644
index 0000000000..29cd742d03
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constants_autoload_b.rb
@@ -0,0 +1,2 @@
+module CSAutoloadB
+end
diff --git a/spec/ruby/core/module/fixtures/constants_autoload_c.rb b/spec/ruby/core/module/fixtures/constants_autoload_c.rb
new file mode 100644
index 0000000000..9d6a6bf4d7
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constants_autoload_c.rb
@@ -0,0 +1,3 @@
+module CSAutoloadC
+ CONST = 7
+end
diff --git a/spec/ruby/core/module/fixtures/constants_autoload_d.rb b/spec/ruby/core/module/fixtures/constants_autoload_d.rb
new file mode 100644
index 0000000000..52d550bab0
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constants_autoload_d.rb
@@ -0,0 +1,4 @@
+module CSAutoloadD
+ module InnerModule
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/module.rb b/spec/ruby/core/module/fixtures/module.rb
new file mode 100644
index 0000000000..9050a272ec
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/module.rb
@@ -0,0 +1,4 @@
+module ModuleSpecs
+ module Anonymous
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/name.rb b/spec/ruby/core/module/fixtures/name.rb
new file mode 100644
index 0000000000..fb9e66c309
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/name.rb
@@ -0,0 +1,10 @@
+# -*- encoding: utf-8 -*-
+module ModuleSpecs
+ class NameEncoding
+ class Cß
+ end
+ def name
+ Cß.name
+ end
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/path1/load_path.rb b/spec/ruby/core/module/fixtures/path1/load_path.rb
new file mode 100644
index 0000000000..d4c45463dc
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/path1/load_path.rb
@@ -0,0 +1,9 @@
+$LOAD_PATH.unshift(File.expand_path('../../path2', __FILE__))
+
+module ModuleSpecs::Autoload
+ module LoadPath
+ def self.loaded
+ :autoload_load_path
+ end
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/path2/load_path.rb b/spec/ruby/core/module/fixtures/path2/load_path.rb
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/path2/load_path.rb
@@ -0,0 +1 @@
+
diff --git a/spec/ruby/core/module/fixtures/refine.rb b/spec/ruby/core/module/fixtures/refine.rb
new file mode 100644
index 0000000000..46975361dd
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/refine.rb
@@ -0,0 +1,13 @@
+module ModuleSpecs
+ class ClassWithFoo
+ def foo; "foo" end
+ end
+
+ module PrependedModule
+ def foo; "foo from prepended module"; end
+ end
+
+ module IncludedModule
+ def foo; "foo from included module"; end
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/repeated_concurrent_autoload.rb b/spec/ruby/core/module/fixtures/repeated_concurrent_autoload.rb
new file mode 100644
index 0000000000..32b770e6cf
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/repeated_concurrent_autoload.rb
@@ -0,0 +1,8 @@
+prev_value = ScratchPad.recorded.increment_and_get
+eval <<-RUBY_EVAL
+ module Mod#{prev_value}
+ sleep(0.05)
+ def self.foo
+ end
+ end
+RUBY_EVAL
diff --git a/spec/ruby/core/module/freeze_spec.rb b/spec/ruby/core/module/freeze_spec.rb
new file mode 100644
index 0000000000..fd76141431
--- /dev/null
+++ b/spec/ruby/core/module/freeze_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#freeze" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/module/gt_spec.rb b/spec/ruby/core/module/gt_spec.rb
new file mode 100644
index 0000000000..adab89a78b
--- /dev/null
+++ b/spec/ruby/core/module/gt_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#>" do
+ it "returns false if self is a subclass of or includes the given module" do
+ (ModuleSpecs::Child > ModuleSpecs::Parent).should be_false
+ (ModuleSpecs::Child > ModuleSpecs::Basic).should be_false
+ (ModuleSpecs::Child > ModuleSpecs::Super).should be_false
+ (ModuleSpecs::Super > ModuleSpecs::Basic).should be_false
+ end
+
+ it "returns true if self is a superclass of or included by the given module" do
+ (ModuleSpecs::Parent > ModuleSpecs::Child).should == true
+ (ModuleSpecs::Basic > ModuleSpecs::Child).should == true
+ (ModuleSpecs::Super > ModuleSpecs::Child).should == true
+ (ModuleSpecs::Basic > ModuleSpecs::Super).should == true
+ end
+
+ it "returns false if self is the same as the given module" do
+ (ModuleSpecs::Child > ModuleSpecs::Child).should == false
+ (ModuleSpecs::Parent > ModuleSpecs::Parent).should == false
+ (ModuleSpecs::Basic > ModuleSpecs::Basic).should == false
+ (ModuleSpecs::Super > ModuleSpecs::Super).should == false
+ end
+
+ it "returns nil if self is not related to the given module" do
+ (ModuleSpecs::Parent > ModuleSpecs::Basic).should == nil
+ (ModuleSpecs::Parent > ModuleSpecs::Super).should == nil
+ (ModuleSpecs::Basic > ModuleSpecs::Parent).should == nil
+ (ModuleSpecs::Super > ModuleSpecs::Parent).should == nil
+ end
+
+ it "raises a TypeError if the argument is not a class/module" do
+ lambda { ModuleSpecs::Parent > mock('x') }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/module/gte_spec.rb b/spec/ruby/core/module/gte_spec.rb
new file mode 100644
index 0000000000..945721fe6b
--- /dev/null
+++ b/spec/ruby/core/module/gte_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#>=" do
+ it "returns true if self is a superclass of, the same as or included by given module" do
+ (ModuleSpecs::Parent >= ModuleSpecs::Child).should == true
+ (ModuleSpecs::Basic >= ModuleSpecs::Child).should == true
+ (ModuleSpecs::Super >= ModuleSpecs::Child).should == true
+ (ModuleSpecs::Basic >= ModuleSpecs::Super).should == true
+ (ModuleSpecs::Child >= ModuleSpecs::Child).should == true
+ (ModuleSpecs::Parent >= ModuleSpecs::Parent).should == true
+ (ModuleSpecs::Basic >= ModuleSpecs::Basic).should == true
+ (ModuleSpecs::Super >= ModuleSpecs::Super).should == true
+ end
+
+ it "returns nil if self is not related to the given module" do
+ (ModuleSpecs::Parent >= ModuleSpecs::Basic).should == nil
+ (ModuleSpecs::Parent >= ModuleSpecs::Super).should == nil
+ (ModuleSpecs::Basic >= ModuleSpecs::Parent).should == nil
+ (ModuleSpecs::Super >= ModuleSpecs::Parent).should == nil
+ end
+
+ it "returns false if self is a subclass of or includes the given module" do
+ (ModuleSpecs::Child >= ModuleSpecs::Parent).should == false
+ (ModuleSpecs::Child >= ModuleSpecs::Basic).should == false
+ (ModuleSpecs::Child >= ModuleSpecs::Super).should == false
+ (ModuleSpecs::Super >= ModuleSpecs::Basic).should == false
+ end
+
+ it "raises a TypeError if the argument is not a class/module" do
+ lambda { ModuleSpecs::Parent >= mock('x') }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/module/include_spec.rb b/spec/ruby/core/module/include_spec.rb
new file mode 100644
index 0000000000..2a31afa59a
--- /dev/null
+++ b/spec/ruby/core/module/include_spec.rb
@@ -0,0 +1,270 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#include" do
+ it "is a public method" do
+ Module.should have_public_instance_method(:include, false)
+ end
+
+ it "calls #append_features(self) in reversed order on each module" do
+ $appended_modules = []
+
+ m = Module.new do
+ def self.append_features(mod)
+ $appended_modules << [ self, mod ]
+ end
+ end
+
+ m2 = Module.new do
+ def self.append_features(mod)
+ $appended_modules << [ self, mod ]
+ end
+ end
+
+ m3 = Module.new do
+ def self.append_features(mod)
+ $appended_modules << [ self, mod ]
+ end
+ end
+
+ c = Class.new { include(m, m2, m3) }
+
+ $appended_modules.should == [ [ m3, c], [ m2, c ], [ m, c ] ]
+ end
+
+ it "adds all ancestor modules when a previously included module is included again" do
+ ModuleSpecs::MultipleIncludes.ancestors.should include(ModuleSpecs::MA, ModuleSpecs::MB)
+ ModuleSpecs::MB.include(ModuleSpecs::MC)
+ ModuleSpecs::MultipleIncludes.include(ModuleSpecs::MB)
+ ModuleSpecs::MultipleIncludes.ancestors.should include(ModuleSpecs::MA, ModuleSpecs::MB, ModuleSpecs::MC)
+ end
+
+ it "raises a TypeError when the argument is not a Module" do
+ lambda { ModuleSpecs::Basic.include(Class.new) }.should raise_error(TypeError)
+ end
+
+ it "does not raise a TypeError when the argument is an instance of a subclass of Module" do
+ lambda { ModuleSpecs::SubclassSpec.include(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError)
+ end
+
+ it "imports constants to modules and classes" do
+ ModuleSpecs::A.constants.should include(:CONSTANT_A)
+ ModuleSpecs::B.constants.should include(:CONSTANT_A, :CONSTANT_B)
+ ModuleSpecs::C.constants.should include(:CONSTANT_A, :CONSTANT_B)
+ end
+
+ it "shadows constants from ancestors" do
+ klass = Class.new
+ klass.include ModuleSpecs::ShadowingOuter::M
+ klass::SHADOW.should == 123
+ klass.include ModuleSpecs::ShadowingOuter::N
+ klass::SHADOW.should == 456
+ end
+
+ it "does not override existing constants in modules and classes" do
+ ModuleSpecs::A::OVERRIDE.should == :a
+ ModuleSpecs::B::OVERRIDE.should == :b
+ ModuleSpecs::C::OVERRIDE.should == :c
+ end
+
+ it "imports instance methods to modules and classes" do
+ ModuleSpecs::A.instance_methods.should include(:ma)
+ ModuleSpecs::B.instance_methods.should include(:ma,:mb)
+ ModuleSpecs::C.instance_methods.should include(:ma,:mb)
+ end
+
+ it "does not import methods to modules and classes" do
+ ModuleSpecs::A.methods.include?(:cma).should == true
+ ModuleSpecs::B.methods.include?(:cma).should == false
+ ModuleSpecs::B.methods.include?(:cmb).should == true
+ ModuleSpecs::C.methods.include?(:cma).should == false
+ ModuleSpecs::C.methods.include?(:cmb).should == false
+ end
+
+ it "attaches the module as the caller's immediate ancestor" do
+ module IncludeSpecsTop
+ def value; 5; end
+ end
+
+ module IncludeSpecsMiddle
+ include IncludeSpecsTop
+ def value; 6; end
+ end
+
+ class IncludeSpecsClass
+ include IncludeSpecsMiddle
+ end
+
+ IncludeSpecsClass.new.value.should == 6
+ end
+
+ it "doesn't include module if it is included in a super class" do
+ module ModuleSpecs::M1
+ module M; end
+ class A; include M; end
+ class B < A; include M; end
+
+ all = [A,B,M]
+
+ (B.ancestors & all).should == [B, A, M]
+ end
+ end
+
+ it "recursively includes new mixins" do
+ module ModuleSpecs::M1
+ module U; end
+ module V; end
+ module W; end
+ module X; end
+ module Y; end
+ class A; include X; end;
+ class B < A; include U, V, W; end;
+
+ # update V
+ module V; include X, U, Y; end
+
+ # This code used to use Array#& and then compare 2 arrays, but
+ # the ordering from Array#& is undefined, as it uses Hash internally.
+ #
+ # Loop is more verbose, but more explicit in what we're testing.
+
+ anc = B.ancestors
+ [B, U, V, W, A, X].each do |i|
+ anc.include?(i).should be_true
+ end
+
+ class B; include V; end
+
+ # the only new module is Y, it is added after U since it follows U in V mixin list:
+ anc = B.ancestors
+ [B, U, Y, V, W, A, X].each do |i|
+ anc.include?(i).should be_true
+ end
+ end
+ end
+
+ it "preserves ancestor order" do
+ module ModuleSpecs::M2
+ module M1; end
+ module M2; end
+ module M3; include M2; end
+
+ module M2; include M1; end
+ module M3; include M2; end
+
+ M3.ancestors.should == [M3, M2, M1]
+
+ end
+ end
+
+ it "detects cyclic includes" do
+ lambda {
+ module ModuleSpecs::M
+ include ModuleSpecs::M
+ end
+ }.should raise_error(ArgumentError)
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "accepts no-arguments" do
+ lambda {
+ Module.new do
+ include
+ end
+ }.should_not raise_error
+ end
+ end
+
+ ruby_version_is '2.4' do
+ it "doesn't accept no-arguments" do
+ lambda {
+ Module.new do
+ include
+ end
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "returns the class it's included into" do
+ m = Module.new
+ r = nil
+ c = Class.new { r = include m }
+ r.should == c
+ end
+
+ it "ignores modules it has already included via module mutual inclusion" do
+ module ModuleSpecs::AlreadyInc
+ module M0
+ end
+
+ module M
+ include M0
+ end
+
+ class K
+ include M
+ include M
+ end
+
+ K.ancestors[0].should == K
+ K.ancestors[1].should == M
+ K.ancestors[2].should == M0
+ end
+ end
+
+ it "clears any caches" do
+ module ModuleSpecs::M3
+ module M1
+ def foo
+ :m1
+ end
+ end
+
+ module M2
+ def foo
+ :m2
+ end
+ end
+
+ class C
+ include M1
+
+ def get
+ foo
+ end
+ end
+
+ c = C.new
+ c.get.should == :m1
+
+ class C
+ include M2
+ end
+
+ c.get.should == :m2
+
+ remove_const :C
+ end
+ end
+end
+
+describe "Module#include?" do
+ it "returns true if the given module is included by self or one of it's ancestors" do
+ ModuleSpecs::Super.include?(ModuleSpecs::Basic).should == true
+ ModuleSpecs::Child.include?(ModuleSpecs::Basic).should == true
+ ModuleSpecs::Child.include?(ModuleSpecs::Super).should == true
+ ModuleSpecs::Child.include?(Kernel).should == true
+
+ ModuleSpecs::Parent.include?(ModuleSpecs::Basic).should == false
+ ModuleSpecs::Basic.include?(ModuleSpecs::Super).should == false
+ end
+
+ it "returns false if given module is equal to self" do
+ ModuleSpecs.include?(ModuleSpecs).should == false
+ end
+
+ it "raises a TypeError when no module was given" do
+ lambda { ModuleSpecs::Child.include?("Test") }.should raise_error(TypeError)
+ lambda { ModuleSpecs::Child.include?(ModuleSpecs::Parent) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/module/included_modules_spec.rb b/spec/ruby/core/module/included_modules_spec.rb
new file mode 100644
index 0000000000..40e20953f4
--- /dev/null
+++ b/spec/ruby/core/module/included_modules_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+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)
+ end
+end
diff --git a/spec/ruby/core/module/included_spec.rb b/spec/ruby/core/module/included_spec.rb
new file mode 100644
index 0000000000..f8dbad1d31
--- /dev/null
+++ b/spec/ruby/core/module/included_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#included" do
+ it "is invoked when self is included in another module or class" do
+ begin
+ m = Module.new do
+ def self.included(o)
+ $included_by = o
+ end
+ end
+
+ c = Class.new { include m }
+
+ $included_by.should == c
+ ensure
+ $included_by = nil
+ end
+ end
+
+ it "allows extending self with the object into which it is being included" do
+ m = Module.new do
+ def self.included(o)
+ o.extend(self)
+ end
+
+ def test
+ :passed
+ end
+ end
+
+ c = Class.new{ include(m) }
+ c.test.should == :passed
+ end
+
+ it "is private in its default implementation" do
+ Module.should have_private_instance_method(:included)
+ end
+
+ it "works with super using a singleton class" do
+ ModuleSpecs::SingletonOnModuleCase::Bar.include ModuleSpecs::SingletonOnModuleCase::Foo
+ ModuleSpecs::SingletonOnModuleCase::Bar.included_called?.should == true
+ end
+end
diff --git a/spec/ruby/core/module/initialize_copy_spec.rb b/spec/ruby/core/module/initialize_copy_spec.rb
new file mode 100644
index 0000000000..7ae48f85a9
--- /dev/null
+++ b/spec/ruby/core/module/initialize_copy_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+
+describe "Module#initialize_copy" do
+ it "should retain singleton methods when duped" do
+ mod = Module.new
+ def mod.hello
+ end
+ mod.dup.methods(false).should == [:hello]
+ end
+
+ # jruby/jruby#5245, https://bugs.ruby-lang.org/issues/3461
+ it "should produce a duped module with inspectable class methods" do
+ mod = Module.new
+ def mod.hello
+ end
+ mod.dup.method(:hello).inspect.should =~ /Module.*hello/
+ end
+end
diff --git a/spec/ruby/core/module/initialize_spec.rb b/spec/ruby/core/module/initialize_spec.rb
new file mode 100644
index 0000000000..99e41e4619
--- /dev/null
+++ b/spec/ruby/core/module/initialize_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#initialize" do
+ it "accepts a block" do
+ m = Module.new do
+ const_set :A, "A"
+ end
+ m.const_get("A").should == "A"
+ end
+
+ it "is called on subclasses" do
+ m = ModuleSpecs::SubModule.new
+ m.special.should == 10
+ m.methods.should_not == nil
+ m.constants.should_not == nil
+ end
+end
diff --git a/spec/ruby/core/module/instance_method_spec.rb b/spec/ruby/core/module/instance_method_spec.rb
new file mode 100644
index 0000000000..9328df9179
--- /dev/null
+++ b/spec/ruby/core/module/instance_method_spec.rb
@@ -0,0 +1,85 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#instance_method" do
+ before :all do
+ @parent_um = ModuleSpecs::InstanceMeth.instance_method(:foo)
+ @child_um = ModuleSpecs::InstanceMethChild.instance_method(:foo)
+ @mod_um = ModuleSpecs::InstanceMethChild.instance_method(:bar)
+ end
+
+ it "is a public method" do
+ Module.should have_public_instance_method(:instance_method, false)
+ end
+
+ it "requires an argument" do
+ Module.new.method(:instance_method).arity.should == 1
+ end
+
+ it "returns an UnboundMethod corresponding to the given name" do
+ @parent_um.should be_kind_of(UnboundMethod)
+ @parent_um.bind(ModuleSpecs::InstanceMeth.new).call.should == :foo
+ end
+
+ it "returns an UnboundMethod corresponding to the given name from a superclass" do
+ @child_um.should be_kind_of(UnboundMethod)
+ @child_um.bind(ModuleSpecs::InstanceMethChild.new).call.should == :foo
+ end
+
+ it "returns an UnboundMethod corresponding to the given name from an included Module" do
+ @mod_um.should be_kind_of(UnboundMethod)
+ @mod_um.bind(ModuleSpecs::InstanceMethChild.new).call.should == :bar
+ end
+
+ it "returns an UnboundMethod when given a protected method name" do
+ ModuleSpecs::Basic.instance_method(:protected_module).should be_an_instance_of(UnboundMethod)
+ end
+
+ it "returns an UnboundMethod when given a private method name" do
+ ModuleSpecs::Basic.instance_method(:private_module).should be_an_instance_of(UnboundMethod)
+ end
+
+ it "gives UnboundMethod method name, Module defined in and Module extracted from" do
+ @parent_um.inspect.should =~ /\bfoo\b/
+ @parent_um.inspect.should =~ /\bModuleSpecs::InstanceMeth\b/
+ @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/
+ end
+
+ it "raises a TypeError if not passed a symbol" do
+ lambda { Object.instance_method([]) }.should raise_error(TypeError)
+ lambda { Object.instance_method(0) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the given name is not a string/symbol" do
+ lambda { Object.instance_method(nil) }.should raise_error(TypeError)
+ lambda { Object.instance_method(mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "raises a NameError if the method has been undefined" do
+ child = Class.new(ModuleSpecs::InstanceMeth)
+ child.send :undef_method, :foo
+ um = ModuleSpecs::InstanceMeth.instance_method(:foo)
+ um.should == @parent_um
+ lambda do
+ child.instance_method(:foo)
+ end.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the method does not exist" do
+ lambda { Object.instance_method(:missing) }.should raise_error(NameError)
+ end
+
+ it "sets the NameError#name attribute to the name of the missing method" do
+ begin
+ Object.instance_method(:missing)
+ rescue NameError => e
+ e.name.should == :missing
+ end
+ end
+end
diff --git a/spec/ruby/core/module/instance_methods_spec.rb b/spec/ruby/core/module/instance_methods_spec.rb
new file mode 100644
index 0000000000..d2d38cdda2
--- /dev/null
+++ b/spec/ruby/core/module/instance_methods_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#instance_methods" do
+ it "does not return methods undefined in a superclass" do
+ methods = ModuleSpecs::Parent.instance_methods(false)
+ methods.should_not include(:undefed_method)
+ end
+
+ it "only includes module methods on an included module" do
+ methods = ModuleSpecs::Basic.instance_methods(false)
+ methods.should include(:public_module)
+ # Child is an including class
+ methods = ModuleSpecs::Child.instance_methods(false)
+ methods.should include(:public_child)
+ methods.should_not include(:public_module)
+ end
+
+ it "does not return methods undefined in a subclass" do
+ methods = ModuleSpecs::Grandchild.instance_methods
+ methods.should_not include(:parent_method, :another_parent_method)
+ end
+
+ it "does not return methods undefined in the current class" do
+ class ModuleSpecs::Child
+ def undefed_child
+ end
+ end
+ ModuleSpecs::Child.send(:undef_method, :undefed_child)
+ methods = ModuleSpecs::Child.instance_methods
+ methods.should_not include(:undefed_method, :undefed_child)
+ end
+
+ it "does not return methods from an included module that are undefined in the class" do
+ ModuleSpecs::Grandchild.instance_methods.should_not include(:super_included_method)
+ end
+
+ it "returns the public and protected methods of self if include_super is false" do
+ methods = ModuleSpecs::Parent.instance_methods(false)
+ methods.should include(:protected_parent, :public_parent)
+
+ methods = ModuleSpecs::Child.instance_methods(false)
+ methods.should include(:protected_child, :public_child)
+ end
+
+ it "returns the public and protected methods of self and it's ancestors" do
+ methods = ModuleSpecs::Basic.instance_methods
+ methods.should include(:protected_module, :public_module)
+
+ methods = ModuleSpecs::Super.instance_methods
+ methods.should include(:protected_module, :protected_super_module,
+ :public_module, :public_super_module)
+ end
+
+ it "makes a private Object instance method public in Kernel" do
+ methods = Kernel.instance_methods
+ methods.should include(:module_specs_private_method_on_object_for_kernel_public)
+ methods = Object.instance_methods
+ methods.should_not include(:module_specs_private_method_on_object_for_kernel_public)
+ end
+end
diff --git a/spec/ruby/core/module/lt_spec.rb b/spec/ruby/core/module/lt_spec.rb
new file mode 100644
index 0000000000..1fd528a54e
--- /dev/null
+++ b/spec/ruby/core/module/lt_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#<" do
+ it "returns true if self is a subclass of or includes the given module" do
+ (ModuleSpecs::Child < ModuleSpecs::Parent).should == true
+ (ModuleSpecs::Child < ModuleSpecs::Basic).should == true
+ (ModuleSpecs::Child < ModuleSpecs::Super).should == true
+ (ModuleSpecs::Super < ModuleSpecs::Basic).should == true
+ end
+
+ it "returns false if self is a superclass of or included by the given module" do
+ (ModuleSpecs::Parent < ModuleSpecs::Child).should be_false
+ (ModuleSpecs::Basic < ModuleSpecs::Child).should be_false
+ (ModuleSpecs::Super < ModuleSpecs::Child).should be_false
+ (ModuleSpecs::Basic < ModuleSpecs::Super).should be_false
+ end
+
+ it "returns false if self is the same as the given module" do
+ (ModuleSpecs::Child < ModuleSpecs::Child).should == false
+ (ModuleSpecs::Parent < ModuleSpecs::Parent).should == false
+ (ModuleSpecs::Basic < ModuleSpecs::Basic).should == false
+ (ModuleSpecs::Super < ModuleSpecs::Super).should == false
+ end
+
+ it "returns nil if self is not related to the given module" do
+ (ModuleSpecs::Parent < ModuleSpecs::Basic).should == nil
+ (ModuleSpecs::Parent < ModuleSpecs::Super).should == nil
+ (ModuleSpecs::Basic < ModuleSpecs::Parent).should == nil
+ (ModuleSpecs::Super < ModuleSpecs::Parent).should == nil
+ end
+
+ it "raises a TypeError if the argument is not a class/module" do
+ lambda { ModuleSpecs::Parent < mock('x') }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/module/lte_spec.rb b/spec/ruby/core/module/lte_spec.rb
new file mode 100644
index 0000000000..b8252cfe55
--- /dev/null
+++ b/spec/ruby/core/module/lte_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#<=" do
+ it "returns true if self is a subclass of, the same as or includes the given module" do
+ (ModuleSpecs::Child <= ModuleSpecs::Parent).should == true
+ (ModuleSpecs::Child <= ModuleSpecs::Basic).should == true
+ (ModuleSpecs::Child <= ModuleSpecs::Super).should == true
+ (ModuleSpecs::Super <= ModuleSpecs::Basic).should == true
+ (ModuleSpecs::Child <= ModuleSpecs::Child).should == true
+ (ModuleSpecs::Parent <= ModuleSpecs::Parent).should == true
+ (ModuleSpecs::Basic <= ModuleSpecs::Basic).should == true
+ (ModuleSpecs::Super <= ModuleSpecs::Super).should == true
+ end
+
+ it "returns nil if self is not related to the given module" do
+ (ModuleSpecs::Parent <= ModuleSpecs::Basic).should == nil
+ (ModuleSpecs::Parent <= ModuleSpecs::Super).should == nil
+ (ModuleSpecs::Basic <= ModuleSpecs::Parent).should == nil
+ (ModuleSpecs::Super <= ModuleSpecs::Parent).should == nil
+ end
+
+ it "returns false if self is a superclass of or is included by the given module" do
+ (ModuleSpecs::Parent <= ModuleSpecs::Child).should == false
+ (ModuleSpecs::Basic <= ModuleSpecs::Child).should == false
+ (ModuleSpecs::Super <= ModuleSpecs::Child).should == false
+ (ModuleSpecs::Basic <= ModuleSpecs::Super).should == false
+ end
+
+ it "raises a TypeError if the argument is not a class/module" do
+ lambda { ModuleSpecs::Parent <= mock('x') }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/module/method_added_spec.rb b/spec/ruby/core/module/method_added_spec.rb
new file mode 100644
index 0000000000..e1b1eda430
--- /dev/null
+++ b/spec/ruby/core/module/method_added_spec.rb
@@ -0,0 +1,62 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#method_added" do
+ it "is a private instance method" do
+ Module.should have_private_instance_method(:method_added)
+ end
+
+ it "returns nil in the default implementation" do
+ Module.new do
+ method_added(:test).should == nil
+ end
+ end
+
+ it "is called when a new instance method is defined in self" do
+ ScratchPad.record []
+
+ Module.new do
+ def self.method_added(name)
+ ScratchPad << name
+ end
+
+ def test() end
+ def test2() end
+ def test() end
+ alias_method :aliased_test, :test
+ alias aliased_test2 test
+ end
+
+ ScratchPad.recorded.should == [:test, :test2, :test, :aliased_test, :aliased_test2]
+ end
+
+ it "is not called when a singleton method is added" do
+ # obj.singleton_method_added is called instead
+ ScratchPad.record []
+
+ klass = Class.new
+ def klass.method_added(name)
+ ScratchPad << name
+ end
+
+ obj = klass.new
+ def obj.new_singleton_method
+ end
+
+ ScratchPad.recorded.should == []
+ end
+
+ it "is not called when a method is undefined in self" do
+ m = Module.new do
+ def method_to_undef
+ end
+
+ def self.method_added(name)
+ fail("method_added called by undef_method")
+ end
+
+ undef_method :method_to_undef
+ end
+ m.should_not have_method(:method_to_undef)
+ end
+end
diff --git a/spec/ruby/core/module/method_defined_spec.rb b/spec/ruby/core/module/method_defined_spec.rb
new file mode 100644
index 0000000000..62e0db8ee6
--- /dev/null
+++ b/spec/ruby/core/module/method_defined_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#method_defined?" do
+ it "returns true if a public or private method with the given name is defined in self, self's ancestors or one of self's included modules" do
+ # Defined in Child
+ ModuleSpecs::Child.method_defined?(:public_child).should == true
+ ModuleSpecs::Child.method_defined?("private_child").should == false
+ ModuleSpecs::Child.method_defined?(:accessor_method).should == true
+
+ # Defined in Parent
+ ModuleSpecs::Child.method_defined?("public_parent").should == true
+ ModuleSpecs::Child.method_defined?(:private_parent).should == false
+
+ # Defined in Module
+ ModuleSpecs::Child.method_defined?(:public_module).should == true
+ ModuleSpecs::Child.method_defined?(:protected_module).should == true
+ ModuleSpecs::Child.method_defined?(:private_module).should == false
+
+ # Defined in SuperModule
+ ModuleSpecs::Child.method_defined?(:public_super_module).should == true
+ ModuleSpecs::Child.method_defined?(:protected_super_module).should == true
+ ModuleSpecs::Child.method_defined?(:private_super_module).should == false
+ end
+
+ # unlike alias_method, module_function, public, and friends,
+ it "does not search Object or Kernel when called on a module" do
+ m = Module.new
+
+ m.method_defined?(:module_specs_public_method_on_kernel).should be_false
+ end
+
+ it "raises a TypeError when the given object is not a string/symbol/fixnum" do
+ c = Class.new
+ o = mock('123')
+
+ lambda { c.method_defined?(o) }.should raise_error(TypeError)
+
+ o.should_receive(:to_str).and_return(123)
+ lambda { c.method_defined?(o) }.should raise_error(TypeError)
+ end
+
+ it "converts the given name to a string using to_str" do
+ c = Class.new { def test(); end }
+ (o = mock('test')).should_receive(:to_str).and_return("test")
+
+ c.method_defined?(o).should == true
+ end
+end
diff --git a/spec/ruby/core/module/method_removed_spec.rb b/spec/ruby/core/module/method_removed_spec.rb
new file mode 100644
index 0000000000..9b39eb77a6
--- /dev/null
+++ b/spec/ruby/core/module/method_removed_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#method_removed" do
+ it "is a private instance method" do
+ Module.should have_private_instance_method(:method_removed)
+ end
+
+ it "returns nil in the default implementation" do
+ Module.new do
+ method_removed(:test).should == nil
+ end
+ end
+
+ it "is called when a method is removed from self" do
+ begin
+ Module.new do
+ def self.method_removed(name)
+ $method_removed = name
+ end
+
+ def test
+ "test"
+ end
+ remove_method :test
+ end
+
+ $method_removed.should == :test
+ ensure
+ $method_removed = nil
+ end
+ end
+end
diff --git a/spec/ruby/core/module/method_undefined_spec.rb b/spec/ruby/core/module/method_undefined_spec.rb
new file mode 100644
index 0000000000..a94388fe0a
--- /dev/null
+++ b/spec/ruby/core/module/method_undefined_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#method_undefined" do
+ it "is a private instance method" do
+ Module.should have_private_instance_method(:method_undefined)
+ end
+
+ it "returns nil in the default implementation" do
+ Module.new do
+ method_undefined(:test).should == nil
+ end
+ end
+
+ it "is called when a method is undefined from self" do
+ begin
+ Module.new do
+ def self.method_undefined(name)
+ $method_undefined = name
+ end
+
+ def test
+ "test"
+ end
+ undef_method :test
+ end
+
+ $method_undefined.should == :test
+ ensure
+ $method_undefined = nil
+ end
+ end
+end
diff --git a/spec/ruby/core/module/module_eval_spec.rb b/spec/ruby/core/module/module_eval_spec.rb
new file mode 100644
index 0000000000..e9e9fda28d
--- /dev/null
+++ b/spec/ruby/core/module/module_eval_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/class_eval'
+
+describe "Module#module_eval" do
+ it_behaves_like :module_class_eval, :module_eval
+end
diff --git a/spec/ruby/core/module/module_exec_spec.rb b/spec/ruby/core/module/module_exec_spec.rb
new file mode 100644
index 0000000000..47cdf7ef52
--- /dev/null
+++ b/spec/ruby/core/module/module_exec_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/class_exec'
+
+describe "Module#module_exec" do
+ it_behaves_like :module_class_exec, :module_exec
+end
diff --git a/spec/ruby/core/module/module_function_spec.rb b/spec/ruby/core/module/module_function_spec.rb
new file mode 100644
index 0000000000..41d52849fd
--- /dev/null
+++ b/spec/ruby/core/module/module_function_spec.rb
@@ -0,0 +1,269 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#module_function" do
+ it "is a private method" do
+ Module.should have_private_instance_method(:module_function)
+ end
+
+ describe "on Class" do
+ it "is undefined" do
+ Class.should_not have_private_instance_method(:module_function, true)
+ end
+
+ it "raises a TypeError if calling after rebinded to Class" do
+ lambda {
+ Module.instance_method(:module_function).bind(Class.new).call
+ }.should raise_error(TypeError)
+
+ lambda {
+ Module.instance_method(:module_function).bind(Class.new).call :foo
+ }.should raise_error(TypeError)
+ end
+ end
+end
+
+describe "Module#module_function with specific method names" do
+ it "creates duplicates of the given instance methods on the Module object" do
+ m = Module.new do
+ def test() end
+ def test2() end
+ def test3() end
+
+ module_function :test, :test2
+ end
+
+ m.respond_to?(:test).should == true
+ m.respond_to?(:test2).should == true
+ m.respond_to?(:test3).should == false
+ end
+
+ it "returns the current module" do
+ x = nil
+ m = Module.new do
+ def test() end
+ x = module_function :test
+ end
+
+ x.should == m
+ end
+
+ it "creates an independent copy of the method, not a redirect" do
+ module Mixin
+ def test
+ "hello"
+ end
+ module_function :test
+ end
+
+ class BaseClass
+ include Mixin
+ def call_test
+ test
+ end
+ end
+
+ Mixin.test.should == "hello"
+ c = BaseClass.new
+ c.call_test.should == "hello"
+
+ module Mixin
+ def test
+ "goodbye"
+ end
+ end
+
+ Mixin.test.should == "hello"
+ c.call_test.should == "goodbye"
+ end
+
+ it "makes the instance methods private" do
+ m = Module.new do
+ def test() "hello" end
+ module_function :test
+ end
+
+ (o = mock('x')).extend(m)
+ o.respond_to?(:test).should == false
+ m.should have_private_instance_method(:test)
+ o.send(:test).should == "hello"
+ lambda { o.test }.should raise_error(NoMethodError)
+ end
+
+ it "makes the new Module methods public" do
+ m = Module.new do
+ def test() "hello" end
+ module_function :test
+ end
+
+ m.public_methods.map {|me| me.to_s }.include?('test').should == true
+ end
+
+ it "tries to convert the given names to strings using to_str" do
+ (o = mock('test')).should_receive(:to_str).any_number_of_times.and_return("test")
+ (o2 = mock('test2')).should_receive(:to_str).any_number_of_times.and_return("test2")
+
+ m = Module.new do
+ def test() end
+ def test2() end
+ module_function o, o2
+ end
+
+ m.respond_to?(:test).should == true
+ m.respond_to?(:test2).should == true
+ end
+
+ it "raises a TypeError when the given names can't be converted to string using to_str" do
+ o = mock('123')
+
+ lambda { Module.new { module_function(o) } }.should raise_error(TypeError)
+
+ o.should_receive(:to_str).and_return(123)
+ lambda { Module.new { module_function(o) } }.should raise_error(TypeError)
+ end
+
+ it "can make accessible private methods" do # JRUBY-4214
+ m = Module.new do
+ module_function :require
+ end
+ m.respond_to?(:require).should be_true
+ end
+
+ it "creates Module methods that super up the singleton class of the module" do
+ super_m = Module.new do
+ def foo
+ "super_m"
+ end
+ end
+
+ m = Module.new do
+ extend super_m
+ module_function
+ def foo
+ ["m", super]
+ end
+ end
+
+ m.foo.should == ["m", "super_m"]
+ end
+end
+
+describe "Module#module_function as a toggle (no arguments) in a Module body" do
+ it "makes any subsequently defined methods module functions with the normal semantics" do
+ m = Module.new {
+ module_function
+ def test1() end
+ def test2() end
+ }
+
+ m.respond_to?(:test1).should == true
+ m.respond_to?(:test2).should == true
+ end
+
+ it "returns the current module" do
+ x = nil
+ m = Module.new {
+ x = module_function
+ }
+
+ x.should == m
+ end
+
+ it "stops creating module functions if the body encounters another toggle " \
+ "like public/protected/private without arguments" do
+ m = Module.new {
+ module_function
+ def test1() end
+ def test2() end
+ public
+ def test3() end
+ }
+
+ m.respond_to?(:test1).should == true
+ m.respond_to?(:test2).should == true
+ m.respond_to?(:test3).should == false
+ end
+
+ it "does not stop creating module functions if the body encounters " \
+ "public/protected/private WITH arguments" do
+ m = Module.new {
+ def foo() end
+ module_function
+ def test1() end
+ def test2() end
+ public :foo
+ def test3() end
+ }
+
+ m.respond_to?(:test1).should == true
+ m.respond_to?(:test2).should == true
+ m.respond_to?(:test3).should == true
+ end
+
+ it "does not affect module_evaled method definitions also if outside the eval itself" do
+ m = Module.new {
+ module_function
+ module_eval { def test1() end }
+ module_eval " def test2() end "
+ }
+
+ m.respond_to?(:test1).should == false
+ m.respond_to?(:test2).should == false
+ end
+
+ it "has no effect if inside a module_eval if the definitions are outside of it" do
+ m = Module.new {
+ module_eval { module_function }
+ def test1() end
+ def test2() end
+ }
+
+ m.respond_to?(:test1).should == false
+ m.respond_to?(:test2).should == false
+ end
+
+ it "functions normally if both toggle and definitions inside a module_eval" do
+ m = Module.new {
+ module_eval {
+ module_function
+ def test1() end
+ def test2() end
+ }
+ }
+
+ m.respond_to?(:test1).should == true
+ m.respond_to?(:test2).should == true
+ end
+
+ it "affects evaled method definitions also even when outside the eval itself" do
+ m = Module.new {
+ module_function
+ eval "def test1() end"
+ }
+
+ m.respond_to?(:test1).should == true
+ end
+
+ it "doesn't affect definitions when inside an eval even if the definitions are outside of it" do
+ m = Module.new {
+ eval "module_function"
+ def test1() end
+ }
+
+ m.respond_to?(:test1).should == false
+ end
+
+ it "functions normally if both toggle and definitions inside a eval" do
+ m = Module.new {
+ eval <<-CODE
+ module_function
+
+ def test1() end
+ def test2() end
+ CODE
+ }
+
+ m.respond_to?(:test1).should == true
+ m.respond_to?(:test2).should == true
+ end
+end
diff --git a/spec/ruby/core/module/name_spec.rb b/spec/ruby/core/module/name_spec.rb
new file mode 100644
index 0000000000..5b59b6adaa
--- /dev/null
+++ b/spec/ruby/core/module/name_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/module'
+
+describe "Module#name" do
+ it "is nil for an anonymous module" do
+ Module.new.name.should be_nil
+ end
+
+ it "is nil when assigned to a constant in an anonymous module" do
+ m = Module.new
+ m::N = Module.new
+ m::N.name.should be_nil
+ end
+
+ it "is not nil for a nested module created with the module keyword" do
+ m = Module.new
+ module m::N; end
+ m::N.name.should =~ /#<Module:0x[0-9a-f]+>::N/
+ end
+
+ it "is set when opened with the module keyword" do
+ ModuleSpecs.name.should == "ModuleSpecs"
+ end
+
+ it "is set when a nested module is opened with the module keyword" do
+ ModuleSpecs::Anonymous.name.should == "ModuleSpecs::Anonymous"
+ end
+
+ it "is set when assigning to a constant" do
+ m = Module.new
+ ModuleSpecs::Anonymous::A = m
+ m.name.should == "ModuleSpecs::Anonymous::A"
+ end
+
+ it "is not modified when assigning to a new constant after it has been accessed" do
+ m = Module.new
+ ModuleSpecs::Anonymous::B = m
+ m.name.should == "ModuleSpecs::Anonymous::B"
+ ModuleSpecs::Anonymous::C = m
+ m.name.should == "ModuleSpecs::Anonymous::B"
+ end
+
+ # http://bugs.ruby-lang.org/issues/6067
+ it "is set with a conditional assignment to a nested constant" do
+ eval("ModuleSpecs::Anonymous::F ||= Module.new")
+ ModuleSpecs::Anonymous::F.name.should == "ModuleSpecs::Anonymous::F"
+ end
+
+ it "is set with a conditional assignment to a constant" do
+ module ModuleSpecs::Anonymous
+ D ||= Module.new
+ end
+ ModuleSpecs::Anonymous::D.name.should == "ModuleSpecs::Anonymous::D"
+ end
+
+ # http://redmine.ruby-lang.org/issues/show/1833
+ it "preserves the encoding in which the class was defined" do
+ require fixture(__FILE__, "name")
+ ModuleSpecs::NameEncoding.new.name.encoding.should == Encoding::UTF_8
+ end
+
+ it "is set when the anonymous outer module name is set" do
+ m = Module.new
+ m::N = Module.new
+ ModuleSpecs::Anonymous::E = m
+ m::N.name.should == "ModuleSpecs::Anonymous::E::N"
+ end
+end
diff --git a/spec/ruby/core/module/nesting_spec.rb b/spec/ruby/core/module/nesting_spec.rb
new file mode 100644
index 0000000000..d0611b3efe
--- /dev/null
+++ b/spec/ruby/core/module/nesting_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module::Nesting" do
+
+ it "returns the list of Modules nested at the point of call" do
+ ModuleSpecs::Nesting[:root_level].should == []
+ ModuleSpecs::Nesting[:first_level].should == [ModuleSpecs]
+ ModuleSpecs::Nesting[:basic].should == [ModuleSpecs::Nesting, ModuleSpecs]
+ ModuleSpecs::Nesting[:open_first_level].should ==
+ [ModuleSpecs, ModuleSpecs::Nesting, ModuleSpecs]
+ ModuleSpecs::Nesting[:open_meta].should ==
+ [ModuleSpecs::Nesting.meta, ModuleSpecs::Nesting, ModuleSpecs]
+ ModuleSpecs::Nesting[:nest_class].should ==
+ [ModuleSpecs::Nesting::NestedClass, ModuleSpecs::Nesting, ModuleSpecs]
+ end
+
+ it "returns the nesting for module/class declaring the called method" do
+ ModuleSpecs::Nesting.called_from_module_method.should ==
+ [ModuleSpecs::Nesting, ModuleSpecs]
+ ModuleSpecs::Nesting::NestedClass.called_from_class_method.should ==
+ [ModuleSpecs::Nesting::NestedClass, ModuleSpecs::Nesting, ModuleSpecs]
+ ModuleSpecs::Nesting::NestedClass.new.called_from_inst_method.should ==
+ [ModuleSpecs::Nesting::NestedClass, ModuleSpecs::Nesting, ModuleSpecs]
+ end
+
+end
+
+describe "Module.nesting" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/module/new_spec.rb b/spec/ruby/core/module/new_spec.rb
new file mode 100644
index 0000000000..da7f3b8720
--- /dev/null
+++ b/spec/ruby/core/module/new_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module.new" do
+ it "creates a new anonymous Module" do
+ Module.new.is_a?(Module).should == true
+ end
+
+ it "creates a new Module and passes it to the provided block" do
+ test_mod = nil
+ m = Module.new do |mod|
+ mod.should_not == nil
+ self.should == mod
+ test_mod = mod
+ mod.is_a?(Module).should == true
+ Object.new # trying to return something
+ end
+ test_mod.should == m
+ end
+
+ it "evaluates a passed block in the context of the module" do
+ fred = Module.new do
+ def hello() "hello" end
+ def bye() "bye" end
+ end
+
+ (o = mock('x')).extend(fred)
+ o.hello.should == "hello"
+ o.bye.should == "bye"
+ end
+end
diff --git a/spec/ruby/core/module/prepend_features_spec.rb b/spec/ruby/core/module/prepend_features_spec.rb
new file mode 100644
index 0000000000..2779073e9c
--- /dev/null
+++ b/spec/ruby/core/module/prepend_features_spec.rb
@@ -0,0 +1,76 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#prepend_features" do
+ it "is a private method" do
+ Module.should have_private_instance_method(:prepend_features, true)
+ end
+
+ it "gets called when self is included in another module/class" do
+ ScratchPad.record []
+
+ m = Module.new do
+ def self.prepend_features(mod)
+ ScratchPad << mod
+ end
+ end
+
+ c = Class.new do
+ prepend m
+ end
+
+ ScratchPad.recorded.should == [c]
+ end
+
+ it "raises an ArgumentError on a cyclic prepend" do
+ lambda {
+ ModuleSpecs::CyclicPrepend.send(:prepend_features, ModuleSpecs::CyclicPrepend)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "copies own tainted status to the given module" do
+ other = Module.new
+ Module.new.taint.send :prepend_features, other
+ other.tainted?.should be_true
+ end
+
+ it "copies own untrusted status to the given module" do
+ other = Module.new
+ Module.new.untrust.send :prepend_features, other
+ other.untrusted?.should be_true
+ end
+
+ it "clears caches of the given module" do
+ parent = Class.new do
+ def bar; :bar; end
+ end
+
+ child = Class.new(parent) do
+ def foo; :foo; end
+ def bar; super; end
+ end
+
+ mod = Module.new do
+ def foo; :fooo; end
+ end
+
+ child.new.foo
+ child.new.bar
+
+ child.prepend(mod)
+
+ child.new.bar.should == :bar
+ end
+
+ describe "on Class" do
+ it "is undefined" do
+ Class.should_not have_private_instance_method(:prepend_features, true)
+ end
+
+ it "raises a TypeError if calling after rebinded to Class" do
+ lambda {
+ Module.instance_method(:prepend_features).bind(Class.new).call Module.new
+ }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/module/prepend_spec.rb b/spec/ruby/core/module/prepend_spec.rb
new file mode 100644
index 0000000000..ca80eb360f
--- /dev/null
+++ b/spec/ruby/core/module/prepend_spec.rb
@@ -0,0 +1,373 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#prepend" do
+ it "is a public method" do
+ Module.should have_public_instance_method(:prepend, false)
+ end
+
+ it "does not affect the superclass" do
+ Class.new { prepend Module.new }.superclass.should == Object
+ end
+
+ it "calls #prepend_features(self) in reversed order on each module" do
+ ScratchPad.record []
+
+ m = Module.new do
+ def self.prepend_features(mod)
+ ScratchPad << [ self, mod ]
+ end
+ end
+
+ m2 = Module.new do
+ def self.prepend_features(mod)
+ ScratchPad << [ self, mod ]
+ end
+ end
+
+ m3 = Module.new do
+ def self.prepend_features(mod)
+ ScratchPad << [ self, mod ]
+ end
+ end
+
+ c = Class.new { prepend(m, m2, m3) }
+
+ ScratchPad.recorded.should == [ [ m3, c], [ m2, c ], [ m, c ] ]
+ end
+
+ it "raises a TypeError when the argument is not a Module" do
+ lambda { ModuleSpecs::Basic.prepend(Class.new) }.should raise_error(TypeError)
+ end
+
+ it "does not raise a TypeError when the argument is an instance of a subclass of Module" do
+ lambda { ModuleSpecs::SubclassSpec.prepend(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError)
+ end
+
+ it "imports constants" do
+ m1 = Module.new
+ m1::MY_CONSTANT = 1
+ m2 = Module.new { prepend(m1) }
+ m2.constants.should include(:MY_CONSTANT)
+ end
+
+ it "imports instance methods" do
+ Module.new { prepend ModuleSpecs::A }.instance_methods.should include(:ma)
+ end
+
+ it "does not import methods to modules and classes" do
+ Module.new { prepend ModuleSpecs::A }.methods.should_not include(:ma)
+ end
+
+ it "allows wrapping methods" do
+ m = Module.new { def calc(x) super + 3 end }
+ c = Class.new { def calc(x) x*2 end }
+ c.prepend(m)
+ c.new.calc(1).should == 5
+ end
+
+ it "also prepends included modules" do
+ a = Module.new { def calc(x) x end }
+ b = Module.new { include a }
+ c = Class.new { prepend b }
+ c.new.calc(1).should == 1
+ end
+
+ it "prepends multiple modules in the right order" do
+ m1 = Module.new { def chain; super << :m1; end }
+ m2 = Module.new { def chain; super << :m2; end; prepend(m1) }
+ c = Class.new { def chain; [:c]; end; prepend(m2) }
+ c.new.chain.should == [:c, :m2, :m1]
+ end
+
+ it "includes prepended modules in ancestors" do
+ m = Module.new
+ Class.new { prepend(m) }.ancestors.should include(m)
+ end
+
+ it "reports the prepended module as the method owner" do
+ m = Module.new { def meth; end }
+ c = Class.new { def meth; end; prepend(m) }
+ c.new.method(:meth).owner.should == m
+ end
+
+ it "reports the prepended module as the unbound method owner" do
+ m = Module.new { def meth; end }
+ c = Class.new { def meth; end; prepend(m) }
+ c.instance_method(:meth).owner.should == m
+ c.public_instance_method(:meth).owner.should == m
+ end
+
+ it "causes the prepended module's method to be aliased by alias_method" do
+ m = Module.new { def meth; :m end }
+ c = Class.new { def meth; :c end; prepend(m); alias_method :alias, :meth }
+ c.new.alias.should == :m
+ end
+
+ it "reports the class for the owner of an aliased method on the class" do
+ m = Module.new
+ c = Class.new { prepend(m); def meth; :c end; alias_method :alias, :meth }
+ c.instance_method(:alias).owner.should == c
+ end
+
+ it "reports the class for the owner of a method aliased from the prepended module" do
+ m = Module.new { def meth; :m end }
+ c = Class.new { prepend(m); alias_method :alias, :meth }
+ c.instance_method(:alias).owner.should == c
+ end
+
+ it "sees an instance of a prepended class as kind of the prepended module" do
+ m = Module.new
+ c = Class.new { prepend(m) }
+ c.new.should be_kind_of(m)
+ end
+
+ it "keeps the module in the chain when dupping the class" do
+ m = Module.new
+ c = Class.new { prepend(m) }
+ c.dup.new.should be_kind_of(m)
+ end
+
+ it "keeps the module in the chain when dupping an intermediate module" do
+ m1 = Module.new { def calc(x) x end }
+ m2 = Module.new { prepend(m1) }
+ c1 = Class.new { prepend(m2) }
+ m2dup = m2.dup
+ m2dup.ancestors.should == [m2dup,m1,m2]
+ c2 = Class.new { prepend(m2dup) }
+ c1.ancestors[0,3].should == [m1,m2,c1]
+ c1.new.should be_kind_of(m1)
+ c2.ancestors[0,4].should == [m2dup,m1,m2,c2]
+ c2.new.should be_kind_of(m1)
+ end
+
+ it "depends on prepend_features to add the module" do
+ m = Module.new { def self.prepend_features(mod) end }
+ Class.new { prepend(m) }.ancestors.should_not include(m)
+ end
+
+ it "adds the module in the subclass chains" do
+ parent = Class.new { def chain; [:parent]; end }
+ child = Class.new(parent) { def chain; super << :child; end }
+ mod = Module.new { def chain; super << :mod; end }
+ parent.prepend(mod)
+ parent.ancestors[0,2].should == [mod, parent]
+ child.ancestors[0,3].should == [child, mod, parent]
+
+ parent.new.chain.should == [:parent, :mod]
+ child.new.chain.should == [:parent, :mod, :child]
+ end
+
+ it "inserts a later prepended module into the chain" do
+ m1 = Module.new { def chain; super << :m1; end }
+ m2 = Module.new { def chain; super << :m2; end }
+ c1 = Class.new { def chain; [:c1]; end; prepend m1 }
+ c2 = Class.new(c1) { def chain; super << :c2; end }
+ c2.new.chain.should == [:c1, :m1, :c2]
+ c1.prepend(m2)
+ c2.new.chain.should == [:c1, :m1, :m2, :c2]
+ end
+
+ it "works with subclasses" do
+ m = Module.new do
+ def chain
+ super << :module
+ end
+ end
+
+ c = Class.new do
+ prepend m
+ def chain
+ [:class]
+ end
+ end
+
+ s = Class.new(c) do
+ def chain
+ super << :subclass
+ end
+ end
+
+ s.new.chain.should == [:class, :module, :subclass]
+ end
+
+ it "throws a NoMethodError when there is no more superclass" do
+ m = Module.new do
+ def chain
+ super << :module
+ end
+ end
+
+ c = Class.new do
+ prepend m
+ def chain
+ super << :class
+ end
+ end
+ lambda { c.new.chain }.should raise_error(NoMethodError)
+ end
+
+ it "calls prepended after prepend_features" do
+ ScratchPad.record []
+
+ m = Module.new do
+ def self.prepend_features(klass)
+ ScratchPad << [:prepend_features, klass]
+ end
+ def self.prepended(klass)
+ ScratchPad << [:prepended, klass]
+ end
+ end
+
+ c = Class.new { prepend(m) }
+ ScratchPad.recorded.should == [[:prepend_features, c], [:prepended, c]]
+ end
+
+ it "detects cyclic prepends" do
+ lambda {
+ module ModuleSpecs::P
+ prepend ModuleSpecs::P
+ end
+ }.should raise_error(ArgumentError)
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "accepts no-arguments" do
+ lambda {
+ Module.new do
+ prepend
+ end
+ }.should_not raise_error
+ end
+ end
+
+ ruby_version_is '2.4' do
+ it "doesn't accept no-arguments" do
+ lambda {
+ Module.new do
+ prepend
+ end
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "returns the class it's included into" do
+ m = Module.new
+ r = nil
+ c = Class.new { r = prepend m }
+ r.should == c
+ end
+
+ it "clears any caches" do
+ module ModuleSpecs::M3
+ module PM1
+ def foo
+ :m1
+ end
+ end
+
+ module PM2
+ def foo
+ :m2
+ end
+ end
+
+ klass = Class.new do
+ prepend PM1
+
+ def get
+ foo
+ end
+ end
+
+ o = klass.new
+ o.get.should == :m1
+
+ klass.class_eval do
+ prepend PM2
+ end
+
+ o.get.should == :m2
+ end
+ end
+
+ it "supports super when the module is prepended into a singleton class" do
+ ScratchPad.record []
+
+ mod = Module.new do
+ def self.inherited(base)
+ super
+ end
+ end
+
+ module_with_singleton_class_prepend = Module.new do
+ singleton_class.prepend(mod)
+ end
+
+ klass = Class.new(ModuleSpecs::RecordIncludedModules) do
+ include module_with_singleton_class_prepend
+ end
+
+ ScratchPad.recorded.should == klass
+ end
+
+ it "supports super when the module is prepended into a singleton class with a class super" do
+ ScratchPad.record []
+
+ base_class = Class.new(ModuleSpecs::RecordIncludedModules) do
+ def self.inherited(base)
+ super
+ end
+ end
+
+ prepended_module = Module.new
+ base_class.singleton_class.prepend(prepended_module)
+
+ child_class = Class.new(base_class)
+ ScratchPad.recorded.should == child_class
+ end
+
+ it "does not interfere with a define_method super in the original class" do
+ base_class = Class.new do
+ def foo(ary)
+ ary << 1
+ end
+ end
+
+ child_class = Class.new(base_class) do
+ define_method :foo do |ary|
+ ary << 2
+ super(ary)
+ end
+ end
+
+ prep_mod = Module.new do
+ def foo(ary)
+ ary << 3
+ super(ary)
+ end
+ end
+
+ child_class.prepend(prep_mod)
+
+ ary = []
+ child_class.new.foo(ary)
+ ary.should == [3, 2, 1]
+ end
+
+ describe "called on a module" do
+ describe "included into a class"
+ it "does not obscure the module's methods from reflective access" do
+ mod = Module.new do
+ def foo; end
+ end
+ cls = Class.new do
+ include mod
+ end
+ pre = Module.new
+ mod.prepend pre
+
+ cls.instance_methods.should include(:foo)
+ end
+ end
+end
diff --git a/spec/ruby/core/module/prepended_spec.rb b/spec/ruby/core/module/prepended_spec.rb
new file mode 100644
index 0000000000..bd95d8fd05
--- /dev/null
+++ b/spec/ruby/core/module/prepended_spec.rb
@@ -0,0 +1,25 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../spec_helper'
+
+describe "Module#prepended" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "is a private method" do
+ Module.should have_private_instance_method(:prepended, true)
+ end
+
+ it "is invoked when self is prepended to another module or class" do
+ m = Module.new do
+ def self.prepended(o)
+ ScratchPad.record o
+ end
+ end
+
+ c = Class.new { prepend m }
+
+ ScratchPad.recorded.should == c
+ end
+end
diff --git a/spec/ruby/core/module/private_class_method_spec.rb b/spec/ruby/core/module/private_class_method_spec.rb
new file mode 100644
index 0000000000..73275b0e6b
--- /dev/null
+++ b/spec/ruby/core/module/private_class_method_spec.rb
@@ -0,0 +1,81 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#private_class_method" do
+ before :each do
+ # This is not in classes.rb because after marking a class method private it
+ # will stay private.
+ class << ModuleSpecs::Parent
+ public
+ def private_method_1; end
+ def private_method_2; end
+ end
+ end
+
+ after :each do
+ class << ModuleSpecs::Parent
+ remove_method :private_method_1
+ remove_method :private_method_2
+ end
+ end
+
+ it "makes an existing class method private" do
+ ModuleSpecs::Parent.private_method_1.should == nil
+ ModuleSpecs::Parent.private_class_method :private_method_1
+ lambda { ModuleSpecs::Parent.private_method_1 }.should raise_error(NoMethodError)
+
+ # Technically above we're testing the Singleton classes, class method(right?).
+ # Try a "real" class method set private.
+ lambda { ModuleSpecs::Parent.private_method }.should raise_error(NoMethodError)
+ end
+
+ it "makes an existing class method private up the inheritance tree" do
+ ModuleSpecs::Child.public_class_method :private_method_1
+ ModuleSpecs::Child.private_method_1.should == nil
+ ModuleSpecs::Child.private_class_method :private_method_1
+
+ lambda { ModuleSpecs::Child.private_method_1 }.should raise_error(NoMethodError)
+ lambda { ModuleSpecs::Child.private_method }.should raise_error(NoMethodError)
+ end
+
+ it "accepts more than one method at a time" do
+ ModuleSpecs::Parent.private_method_1.should == nil
+ ModuleSpecs::Parent.private_method_2.should == nil
+
+ ModuleSpecs::Child.private_class_method :private_method_1, :private_method_2
+
+ lambda { ModuleSpecs::Child.private_method_1 }.should raise_error(NoMethodError)
+ lambda { ModuleSpecs::Child.private_method_2 }.should raise_error(NoMethodError)
+ end
+
+ it "raises a NameError if class method doesn't exist" do
+ lambda do
+ ModuleSpecs.private_class_method :no_method_here
+ end.should raise_error(NameError)
+ end
+
+ it "makes a class method private" do
+ c = Class.new do
+ def self.foo() "foo" end
+ private_class_method :foo
+ end
+ lambda { c.foo }.should raise_error(NoMethodError)
+ end
+
+ it "raises a NameError when the given name is not a method" do
+ lambda do
+ Class.new do
+ private_class_method :foo
+ end
+ end.should raise_error(NameError)
+ end
+
+ it "raises a NameError when the given name is an instance method" do
+ lambda do
+ Class.new do
+ def foo() "foo" end
+ private_class_method :foo
+ end
+ end.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/core/module/private_constant_spec.rb b/spec/ruby/core/module/private_constant_spec.rb
new file mode 100644
index 0000000000..45aaec6c26
--- /dev/null
+++ b/spec/ruby/core/module/private_constant_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+
+describe "Module#private_constant" do
+ it "can only be passed constant names defined in the target (self) module" do
+ cls1 = Class.new
+ cls1.const_set :Foo, true
+ cls2 = Class.new(cls1)
+
+ lambda do
+ cls2.send :private_constant, :Foo
+ end.should raise_error(NameError)
+ end
+
+ it "accepts strings as constant names" do
+ cls = Class.new
+ cls.const_set :Foo, true
+ cls.send :private_constant, "Foo"
+
+ lambda { cls::Foo }.should raise_error(NameError)
+ end
+
+ it "accepts multiple names" do
+ mod = Module.new
+ mod.const_set :Foo, true
+ mod.const_set :Bar, true
+
+ mod.send :private_constant, :Foo, :Bar
+
+ lambda {mod::Foo}.should raise_error(NameError)
+ lambda {mod::Bar}.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/core/module/private_instance_methods_spec.rb b/spec/ruby/core/module/private_instance_methods_spec.rb
new file mode 100644
index 0000000000..cce0f001bf
--- /dev/null
+++ b/spec/ruby/core/module/private_instance_methods_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../fixtures/reflection'
+
+# TODO: rewrite
+describe "Module#private_instance_methods" do
+ it "returns a list of private methods in module and its ancestors" do
+ ModuleSpecs::CountsMixin.should have_private_instance_method(:private_3)
+
+ ModuleSpecs::CountsParent.should have_private_instance_method(:private_2)
+ ModuleSpecs::CountsParent.should have_private_instance_method(:private_3)
+
+ ModuleSpecs::CountsChild.should have_private_instance_method(:private_1)
+ ModuleSpecs::CountsChild.should have_private_instance_method(:private_2)
+ ModuleSpecs::CountsChild.should have_private_instance_method(:private_3)
+ end
+
+ it "when passed false as a parameter, should return only methods defined in that module" do
+ ModuleSpecs::CountsMixin.should have_private_instance_method(:private_3, false)
+ ModuleSpecs::CountsParent.should have_private_instance_method(:private_2, false)
+ ModuleSpecs::CountsChild.should have_private_instance_method(:private_1, false)
+ end
+
+ it "default list should be the same as passing true as an argument" do
+ ModuleSpecs::CountsMixin.private_instance_methods(true).should ==
+ ModuleSpecs::CountsMixin.private_instance_methods
+ ModuleSpecs::CountsParent.private_instance_methods(true).should ==
+ ModuleSpecs::CountsParent.private_instance_methods
+ ModuleSpecs::CountsChild.private_instance_methods(true).should ==
+ ModuleSpecs::CountsChild.private_instance_methods
+ end
+end
+
+describe :module_private_instance_methods_supers, shared: true do
+ it "returns a unique list for a class including a module" do
+ m = ReflectSpecs::D.private_instance_methods(*@object)
+ m.select { |x| x == :pri }.sort.should == [:pri]
+ end
+
+ it "returns a unique list for a subclass" do
+ m = ReflectSpecs::E.private_instance_methods(*@object)
+ m.select { |x| x == :pri }.sort.should == [:pri]
+ end
+end
+
+describe "Module#private_instance_methods" do
+ describe "when not passed an argument" do
+ it_behaves_like :module_private_instance_methods_supers, nil, []
+ end
+
+ describe "when passed true" do
+ it_behaves_like :module_private_instance_methods_supers, nil, true
+ end
+end
diff --git a/spec/ruby/core/module/private_method_defined_spec.rb b/spec/ruby/core/module/private_method_defined_spec.rb
new file mode 100644
index 0000000000..e00a8c8a9b
--- /dev/null
+++ b/spec/ruby/core/module/private_method_defined_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#private_method_defined?" do
+ it "returns true if the named private method is defined by module or its ancestors" do
+ ModuleSpecs::CountsMixin.private_method_defined?("private_3").should == true
+
+ ModuleSpecs::CountsParent.private_method_defined?("private_3").should == true
+ ModuleSpecs::CountsParent.private_method_defined?("private_2").should == true
+
+ ModuleSpecs::CountsChild.private_method_defined?("private_3").should == true
+ ModuleSpecs::CountsChild.private_method_defined?("private_2").should == true
+ ModuleSpecs::CountsChild.private_method_defined?("private_1").should == true
+ end
+
+ it "returns false if method is not a private method" do
+ ModuleSpecs::CountsChild.private_method_defined?("public_3").should == false
+ ModuleSpecs::CountsChild.private_method_defined?("public_2").should == false
+ ModuleSpecs::CountsChild.private_method_defined?("public_1").should == false
+
+ ModuleSpecs::CountsChild.private_method_defined?("protected_3").should == false
+ ModuleSpecs::CountsChild.private_method_defined?("protected_2").should == false
+ ModuleSpecs::CountsChild.private_method_defined?("protected_1").should == false
+ end
+
+ it "returns false if the named method is not defined by the module or its ancestors" do
+ ModuleSpecs::CountsMixin.private_method_defined?(:private_10).should == false
+ end
+
+ it "accepts symbols for the method name" do
+ ModuleSpecs::CountsMixin.private_method_defined?(:private_3).should == true
+ end
+
+ it "raises a TypeError if passed a Fixnum" do
+ lambda do
+ ModuleSpecs::CountsMixin.private_method_defined?(1)
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed nil" do
+ lambda do
+ ModuleSpecs::CountsMixin.private_method_defined?(nil)
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed false" do
+ lambda do
+ ModuleSpecs::CountsMixin.private_method_defined?(false)
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an object that does not defined #to_str" do
+ lambda do
+ ModuleSpecs::CountsMixin.private_method_defined?(mock('x'))
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an object that defines #to_sym" do
+ sym = mock('symbol')
+ def sym.to_sym() :private_3 end
+
+ lambda do
+ ModuleSpecs::CountsMixin.private_method_defined?(sym)
+ end.should raise_error(TypeError)
+ end
+
+ it "calls #to_str to convert an Object" do
+ str = mock('string')
+ def str.to_str() 'private_3' end
+ ModuleSpecs::CountsMixin.private_method_defined?(str).should == true
+ end
+end
diff --git a/spec/ruby/core/module/private_spec.rb b/spec/ruby/core/module/private_spec.rb
new file mode 100644
index 0000000000..d476c6f54e
--- /dev/null
+++ b/spec/ruby/core/module/private_spec.rb
@@ -0,0 +1,95 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_visibility'
+
+describe "Module#private" do
+ it_behaves_like :set_visibility, :private
+
+ it "makes the target method uncallable from other types" do
+ obj = Object.new
+ class << obj
+ def foo; true; end
+ end
+
+ obj.foo.should == true
+
+ class << obj
+ private :foo
+ end
+
+ lambda { obj.foo }.should raise_error(NoMethodError)
+ end
+
+ it "makes a public Object instance method private in a new module" do
+ m = Module.new do
+ private :module_specs_public_method_on_object
+ end
+
+ m.should have_private_instance_method(:module_specs_public_method_on_object)
+
+ # Ensure we did not change Object's method
+ Object.should_not have_private_instance_method(:module_specs_public_method_on_object)
+ end
+
+ it "makes a public Object instance method private in Kernel" do
+ Kernel.should have_private_instance_method(
+ :module_specs_public_method_on_object_for_kernel_private)
+ Object.should_not have_private_instance_method(
+ :module_specs_public_method_on_object_for_kernel_private)
+ end
+
+ it "returns self" do
+ (class << Object.new; self; end).class_eval do
+ def foo; end
+ private(:foo).should equal(self)
+ private.should equal(self)
+ end
+ end
+
+ it "raises a NameError when given an undefined name" do
+ lambda do
+ Module.new.send(:private, :undefined)
+ end.should raise_error(NameError)
+ end
+
+ ruby_bug "#14604", ""..."2.5.1" do
+ it "only makes the method private in the class it is called on" do
+ base = Class.new do
+ def wrapped
+ 1
+ end
+ end
+
+ klass = Class.new(base) do
+ def wrapped
+ super + 1
+ end
+ private :wrapped
+ end
+
+ base.new.wrapped.should == 1
+ lambda do
+ klass.new.wrapped
+ end.should raise_error(NameError)
+ end
+
+ it "continues to allow a prepended module method to call super" do
+ wrapper = Module.new do
+ def wrapped
+ super + 1
+ end
+ end
+
+ klass = Class.new do
+ prepend wrapper
+
+ def wrapped
+ 1
+ end
+ private :wrapped
+ end
+
+ klass.new.wrapped.should == 2
+ end
+ end
+end
diff --git a/spec/ruby/core/module/protected_instance_methods_spec.rb b/spec/ruby/core/module/protected_instance_methods_spec.rb
new file mode 100644
index 0000000000..78ce7e788f
--- /dev/null
+++ b/spec/ruby/core/module/protected_instance_methods_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../fixtures/reflection'
+
+# TODO: rewrite
+describe "Module#protected_instance_methods" do
+ it "returns a list of protected methods in module and its ancestors" do
+ methods = ModuleSpecs::CountsMixin.protected_instance_methods
+ methods.should include(:protected_3)
+
+ methods = ModuleSpecs::CountsParent.protected_instance_methods
+ methods.should include(:protected_3)
+ methods.should include(:protected_2)
+
+ methods = ModuleSpecs::CountsChild.protected_instance_methods
+ methods.should include(:protected_3)
+ methods.should include(:protected_2)
+ methods.should include(:protected_1)
+ end
+
+ it "when passed false as a parameter, should return only methods defined in that module" do
+ ModuleSpecs::CountsMixin.protected_instance_methods(false).should == [:protected_3]
+ ModuleSpecs::CountsParent.protected_instance_methods(false).should == [:protected_2]
+ ModuleSpecs::CountsChild.protected_instance_methods(false).should == [:protected_1]
+ end
+
+ it "default list should be the same as passing true as an argument" do
+ ModuleSpecs::CountsMixin.protected_instance_methods(true).should ==
+ ModuleSpecs::CountsMixin.protected_instance_methods
+ ModuleSpecs::CountsParent.protected_instance_methods(true).should ==
+ ModuleSpecs::CountsParent.protected_instance_methods
+ ModuleSpecs::CountsChild.protected_instance_methods(true).should ==
+ ModuleSpecs::CountsChild.protected_instance_methods
+ end
+end
+
+describe :module_protected_instance_methods_supers, shared: true do
+ it "returns a unique list for a class including a module" do
+ m = ReflectSpecs::D.protected_instance_methods(*@object)
+ m.select { |x| x == :pro }.sort.should == [:pro]
+ end
+
+ it "returns a unique list for a subclass" do
+ m = ReflectSpecs::E.protected_instance_methods(*@object)
+ m.select { |x| x == :pro }.sort.should == [:pro]
+ end
+end
+
+describe "Module#protected_instance_methods" do
+ describe "when not passed an argument" do
+ it_behaves_like :module_protected_instance_methods_supers, nil, []
+ end
+
+ describe "when passed true" do
+ it_behaves_like :module_protected_instance_methods_supers, nil, true
+ end
+end
diff --git a/spec/ruby/core/module/protected_method_defined_spec.rb b/spec/ruby/core/module/protected_method_defined_spec.rb
new file mode 100644
index 0000000000..8492a497d3
--- /dev/null
+++ b/spec/ruby/core/module/protected_method_defined_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#protected_method_defined?" do
+ it "returns true if the named protected method is defined by module or its ancestors" do
+ ModuleSpecs::CountsMixin.protected_method_defined?("protected_3").should == true
+
+ ModuleSpecs::CountsParent.protected_method_defined?("protected_3").should == true
+ ModuleSpecs::CountsParent.protected_method_defined?("protected_2").should == true
+
+ ModuleSpecs::CountsChild.protected_method_defined?("protected_3").should == true
+ ModuleSpecs::CountsChild.protected_method_defined?("protected_2").should == true
+ ModuleSpecs::CountsChild.protected_method_defined?("protected_1").should == true
+ end
+
+ it "returns false if method is not a protected method" do
+ ModuleSpecs::CountsChild.protected_method_defined?("public_3").should == false
+ ModuleSpecs::CountsChild.protected_method_defined?("public_2").should == false
+ ModuleSpecs::CountsChild.protected_method_defined?("public_1").should == false
+
+ ModuleSpecs::CountsChild.protected_method_defined?("private_3").should == false
+ ModuleSpecs::CountsChild.protected_method_defined?("private_2").should == false
+ ModuleSpecs::CountsChild.protected_method_defined?("private_1").should == false
+ end
+
+ it "returns false if the named method is not defined by the module or its ancestors" do
+ ModuleSpecs::CountsMixin.protected_method_defined?(:protected_10).should == false
+ end
+
+ it "accepts symbols for the method name" do
+ ModuleSpecs::CountsMixin.protected_method_defined?(:protected_3).should == true
+ end
+
+ it "raises a TypeError if passed a Fixnum" do
+ lambda do
+ ModuleSpecs::CountsMixin.protected_method_defined?(1)
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed nil" do
+ lambda do
+ ModuleSpecs::CountsMixin.protected_method_defined?(nil)
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed false" do
+ lambda do
+ ModuleSpecs::CountsMixin.protected_method_defined?(false)
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an object that does not defined #to_str" do
+ lambda do
+ ModuleSpecs::CountsMixin.protected_method_defined?(mock('x'))
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an object that defines #to_sym" do
+ sym = mock('symbol')
+ def sym.to_sym() :protected_3 end
+
+ lambda do
+ ModuleSpecs::CountsMixin.protected_method_defined?(sym)
+ end.should raise_error(TypeError)
+ end
+
+ it "calls #to_str to convert an Object" do
+ str = mock('protected_3')
+ str.should_receive(:to_str).and_return("protected_3")
+ ModuleSpecs::CountsMixin.protected_method_defined?(str).should == true
+ end
+end
diff --git a/spec/ruby/core/module/protected_spec.rb b/spec/ruby/core/module/protected_spec.rb
new file mode 100644
index 0000000000..985e4621c5
--- /dev/null
+++ b/spec/ruby/core/module/protected_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_visibility'
+
+describe "Module#protected" do
+ before :each do
+ class << ModuleSpecs::Parent
+ def protected_method_1; 5; end
+ end
+ end
+
+ it_behaves_like :set_visibility, :protected
+
+ it "makes an existing class method protected" do
+ ModuleSpecs::Parent.protected_method_1.should == 5
+
+ class << ModuleSpecs::Parent
+ protected :protected_method_1
+ end
+
+ lambda { ModuleSpecs::Parent.protected_method_1 }.should raise_error(NoMethodError)
+ end
+
+ it "makes a public Object instance method protected in a new module" do
+ m = Module.new do
+ protected :module_specs_public_method_on_object
+ end
+
+ m.should have_protected_instance_method(:module_specs_public_method_on_object)
+
+ # Ensure we did not change Object's method
+ Object.should_not have_protected_instance_method(:module_specs_public_method_on_object)
+ end
+
+ it "makes a public Object instance method protected in Kernel" do
+ Kernel.should have_protected_instance_method(
+ :module_specs_public_method_on_object_for_kernel_protected)
+ Object.should_not have_protected_instance_method(
+ :module_specs_public_method_on_object_for_kernel_protected)
+ end
+
+ it "returns self" do
+ (class << Object.new; self; end).class_eval do
+ def foo; end
+ protected(:foo).should equal(self)
+ protected.should equal(self)
+ end
+ end
+
+ it "raises a NameError when given an undefined name" do
+ lambda do
+ Module.new.send(:protected, :undefined)
+ end.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/core/module/public_class_method_spec.rb b/spec/ruby/core/module/public_class_method_spec.rb
new file mode 100644
index 0000000000..7745ed1f86
--- /dev/null
+++ b/spec/ruby/core/module/public_class_method_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#public_class_method" do
+ before :each do
+ class << ModuleSpecs::Parent
+ private
+ def public_method_1; end
+ def public_method_2; end
+ end
+ end
+
+ after :each do
+ class << ModuleSpecs::Parent
+ remove_method :public_method_1
+ remove_method :public_method_2
+ end
+ end
+
+ it "makes an existing class method public" do
+ lambda { ModuleSpecs::Parent.public_method_1 }.should raise_error(NoMethodError)
+ ModuleSpecs::Parent.public_class_method :public_method_1
+ ModuleSpecs::Parent.public_method_1.should == nil
+
+ # Technically above we're testing the Singleton classes, class method(right?).
+ # Try a "real" class method set public.
+ ModuleSpecs::Parent.public_method.should == nil
+ end
+
+ it "makes an existing class method public up the inheritance tree" do
+ ModuleSpecs::Child.private_class_method :public_method_1
+ lambda { ModuleSpecs::Child.public_method_1 }.should raise_error(NoMethodError)
+ ModuleSpecs::Child.public_class_method :public_method_1
+
+ ModuleSpecs::Child.public_method_1.should == nil
+ ModuleSpecs::Child.public_method.should == nil
+ end
+
+ it "accepts more than one method at a time" do
+ lambda { ModuleSpecs::Parent.public_method_1 }.should raise_error(NameError)
+ lambda { ModuleSpecs::Parent.public_method_2 }.should raise_error(NameError)
+
+ ModuleSpecs::Child.public_class_method :public_method_1, :public_method_2
+
+ ModuleSpecs::Child.public_method_1.should == nil
+ ModuleSpecs::Child.public_method_2.should == nil
+ end
+
+ it "raises a NameError if class method doesn't exist" do
+ lambda do
+ ModuleSpecs.public_class_method :no_method_here
+ end.should raise_error(NameError)
+ end
+
+ it "makes a class method public" do
+ c = Class.new do
+ def self.foo() "foo" end
+ public_class_method :foo
+ end
+
+ c.foo.should == "foo"
+ end
+
+ it "raises a NameError when the given name is not a method" do
+ lambda do
+ Class.new do
+ public_class_method :foo
+ end
+ end.should raise_error(NameError)
+ end
+
+ it "raises a NameError when the given name is an instance method" do
+ lambda do
+ Class.new do
+ def foo() "foo" end
+ public_class_method :foo
+ end
+ end.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/core/module/public_constant_spec.rb b/spec/ruby/core/module/public_constant_spec.rb
new file mode 100644
index 0000000000..b2e6a08a4e
--- /dev/null
+++ b/spec/ruby/core/module/public_constant_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+
+describe "Module#public_constant" do
+ it "can only be passed constant names defined in the target (self) module" do
+ cls1 = Class.new
+ cls1.const_set :Foo, true
+ cls2 = Class.new(cls1)
+
+ lambda do
+ cls2.send :public_constant, :Foo
+ end.should raise_error(NameError)
+ end
+
+ it "accepts strings as constant names" do
+ cls = Class.new
+ cls.const_set :Foo, true
+
+ cls.send :private_constant, :Foo
+ cls.send :public_constant, "Foo"
+
+ cls::Foo.should == true
+ end
+
+ # [ruby-list:48558]
+ it "accepts multiple names" do
+ mod = Module.new
+ mod.const_set :Foo, true
+ mod.const_set :Bar, true
+
+ mod.send :private_constant, :Foo
+ mod.send :private_constant, :Bar
+
+ mod.send :public_constant, :Foo, :Bar
+
+ mod::Foo.should == true
+ mod::Bar.should == true
+ end
+end
diff --git a/spec/ruby/core/module/public_instance_method_spec.rb b/spec/ruby/core/module/public_instance_method_spec.rb
new file mode 100644
index 0000000000..aee6c03cf2
--- /dev/null
+++ b/spec/ruby/core/module/public_instance_method_spec.rb
@@ -0,0 +1,65 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#public_instance_method" do
+ it "is a public method" do
+ Module.should have_public_instance_method(:public_instance_method, false)
+ end
+
+ it "requires an argument" do
+ Module.new.method(:public_instance_method).arity.should == 1
+ end
+
+ describe "when given a public method name" do
+ it "returns an UnboundMethod corresponding to the defined Module" do
+ ret = ModuleSpecs::Super.public_instance_method(:public_module)
+ ret.should be_an_instance_of(UnboundMethod)
+ ret.owner.should equal(ModuleSpecs::Basic)
+
+ ret = ModuleSpecs::Super.public_instance_method(:public_super_module)
+ ret.should be_an_instance_of(UnboundMethod)
+ ret.owner.should equal(ModuleSpecs::Super)
+ end
+
+ it "accepts if the name is a Symbol or String" do
+ ret = ModuleSpecs::Basic.public_instance_method(:public_module)
+ ModuleSpecs::Basic.public_instance_method("public_module").should == ret
+ end
+ end
+
+ it "raises a TypeError when given a name is not Symbol or String" do
+ lambda { Module.new.public_instance_method(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a NameError when given a protected method name" do
+ lambda do
+ ModuleSpecs::Basic.public_instance_method(:protected_module)
+ end.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the method is private" do
+ lambda do
+ ModuleSpecs::Basic.public_instance_method(:private_module)
+ end.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the method has been undefined" do
+ lambda do
+ ModuleSpecs::Parent.public_instance_method(:undefed_method)
+ end.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the method does not exist" do
+ lambda do
+ Module.new.public_instance_method(:missing)
+ end.should raise_error(NameError)
+ end
+
+ it "sets the NameError#name attribute to the name of the missing method" do
+ begin
+ Module.new.public_instance_method(:missing)
+ rescue NameError => e
+ e.name.should == :missing
+ end
+ end
+end
diff --git a/spec/ruby/core/module/public_instance_methods_spec.rb b/spec/ruby/core/module/public_instance_methods_spec.rb
new file mode 100644
index 0000000000..ae7d9b5ffb
--- /dev/null
+++ b/spec/ruby/core/module/public_instance_methods_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../fixtures/reflection'
+
+# TODO: rewrite
+
+describe "Module#public_instance_methods" do
+ it "returns a list of public methods in module and its ancestors" do
+ methods = ModuleSpecs::CountsMixin.public_instance_methods
+ methods.should include(:public_3)
+
+ methods = ModuleSpecs::CountsParent.public_instance_methods
+ methods.should include(:public_3)
+ methods.should include(:public_2)
+
+ methods = ModuleSpecs::CountsChild.public_instance_methods
+ methods.should include(:public_3)
+ methods.should include(:public_2)
+ methods.should include(:public_1)
+
+ methods = ModuleSpecs::Child2.public_instance_methods
+ methods.should include(:foo)
+ end
+
+ it "when passed false as a parameter, should return only methods defined in that module" do
+ ModuleSpecs::CountsMixin.public_instance_methods(false).should == [:public_3]
+ ModuleSpecs::CountsParent.public_instance_methods(false).should == [:public_2]
+ ModuleSpecs::CountsChild.public_instance_methods(false).should == [:public_1]
+ end
+
+ it "default list should be the same as passing true as an argument" do
+ ModuleSpecs::CountsMixin.public_instance_methods(true).should ==
+ ModuleSpecs::CountsMixin.public_instance_methods
+ ModuleSpecs::CountsParent.public_instance_methods(true).should ==
+ ModuleSpecs::CountsParent.public_instance_methods
+ ModuleSpecs::CountsChild.public_instance_methods(true).should ==
+ ModuleSpecs::CountsChild.public_instance_methods
+ end
+end
+
+describe :module_public_instance_methods_supers, shared: true do
+ it "returns a unique list for a class including a module" do
+ m = ReflectSpecs::D.public_instance_methods(*@object)
+ m.select { |x| x == :pub }.sort.should == [:pub]
+ end
+
+ it "returns a unique list for a subclass" do
+ m = ReflectSpecs::E.public_instance_methods(*@object)
+ m.select { |x| x == :pub }.sort.should == [:pub]
+ end
+end
+
+describe "Module#public_instance_methods" do
+ describe "when not passed an argument" do
+ it_behaves_like :module_public_instance_methods_supers, nil, []
+ end
+
+ describe "when passed true" do
+ it_behaves_like :module_public_instance_methods_supers, nil, true
+ end
+end
diff --git a/spec/ruby/core/module/public_method_defined_spec.rb b/spec/ruby/core/module/public_method_defined_spec.rb
new file mode 100644
index 0000000000..0d7d3e031c
--- /dev/null
+++ b/spec/ruby/core/module/public_method_defined_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#public_method_defined?" do
+ it "returns true if the named public method is defined by module or its ancestors" do
+ ModuleSpecs::CountsMixin.public_method_defined?("public_3").should == true
+
+ ModuleSpecs::CountsParent.public_method_defined?("public_3").should == true
+ ModuleSpecs::CountsParent.public_method_defined?("public_2").should == true
+
+ ModuleSpecs::CountsChild.public_method_defined?("public_3").should == true
+ ModuleSpecs::CountsChild.public_method_defined?("public_2").should == true
+ ModuleSpecs::CountsChild.public_method_defined?("public_1").should == true
+ end
+
+ it "returns false if method is not a public method" do
+ ModuleSpecs::CountsChild.public_method_defined?("private_3").should == false
+ ModuleSpecs::CountsChild.public_method_defined?("private_2").should == false
+ ModuleSpecs::CountsChild.public_method_defined?("private_1").should == false
+
+ ModuleSpecs::CountsChild.public_method_defined?("protected_3").should == false
+ ModuleSpecs::CountsChild.public_method_defined?("protected_2").should == false
+ ModuleSpecs::CountsChild.public_method_defined?("protected_1").should == false
+ end
+
+ it "returns false if the named method is not defined by the module or its ancestors" do
+ ModuleSpecs::CountsMixin.public_method_defined?(:public_10).should == false
+ end
+
+ it "accepts symbols for the method name" do
+ ModuleSpecs::CountsMixin.public_method_defined?(:public_3).should == true
+ end
+
+ it "raises a TypeError if passed a Fixnum" do
+ lambda do
+ ModuleSpecs::CountsMixin.public_method_defined?(1)
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed nil" do
+ lambda do
+ ModuleSpecs::CountsMixin.public_method_defined?(nil)
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed false" do
+ lambda do
+ ModuleSpecs::CountsMixin.public_method_defined?(false)
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an object that does not defined #to_str" do
+ lambda do
+ ModuleSpecs::CountsMixin.public_method_defined?(mock('x'))
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an object that defines #to_sym" do
+ sym = mock('symbol')
+ def sym.to_sym() :public_3 end
+
+ lambda do
+ ModuleSpecs::CountsMixin.public_method_defined?(sym)
+ end.should raise_error(TypeError)
+ end
+
+ it "calls #to_str to convert an Object" do
+ str = mock('public_3')
+ def str.to_str() 'public_3' end
+ ModuleSpecs::CountsMixin.public_method_defined?(str).should == true
+ end
+end
diff --git a/spec/ruby/core/module/public_spec.rb b/spec/ruby/core/module/public_spec.rb
new file mode 100644
index 0000000000..a948a55e5e
--- /dev/null
+++ b/spec/ruby/core/module/public_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_visibility'
+
+describe "Module#public" do
+ it_behaves_like :set_visibility, :public
+
+ it "on a superclass method calls the redefined method" do
+ ModuleSpecs::ChildPrivateMethodMadePublic.new.private_method_redefined.should == :after_redefinition
+ end
+
+ it "makes a private Object instance method public in a new module" do
+ m = Module.new do
+ public :module_specs_private_method_on_object
+ end
+
+ m.should have_public_instance_method(:module_specs_private_method_on_object)
+
+ # Ensure we did not change Object's method
+ Object.should_not have_public_instance_method(:module_specs_private_method_on_object)
+ end
+
+ it "makes a private Object instance method public in Kernel" do
+ Kernel.should have_public_instance_method(
+ :module_specs_private_method_on_object_for_kernel_public)
+ Object.should_not have_public_instance_method(
+ :module_specs_private_method_on_object_for_kernel_public)
+ end
+
+ it "returns self" do
+ (class << Object.new; self; end).class_eval do
+ def foo; end
+ private :foo
+ public(:foo).should equal(self)
+ public.should equal(self)
+ end
+ end
+
+ it "raises a NameError when given an undefined name" do
+ lambda do
+ Module.new.send(:public, :undefined)
+ end.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/core/module/refine_spec.rb b/spec/ruby/core/module/refine_spec.rb
new file mode 100644
index 0000000000..97ea0a706d
--- /dev/null
+++ b/spec/ruby/core/module/refine_spec.rb
@@ -0,0 +1,735 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/refine'
+
+describe "Module#refine" do
+ it "runs its block in an anonymous module" do
+ inner_self = nil
+ mod = Module.new do
+ refine String do
+ inner_self = self
+ end
+ end
+
+ mod.should_not == inner_self
+ inner_self.should be_kind_of(Module)
+ inner_self.name.should == nil
+ end
+
+ it "uses the same anonymous module for future refines of the same class" do
+ selves = []
+ mod = Module.new do
+ refine String do
+ selves << self
+ end
+ end
+
+ mod.module_eval do
+ refine String do
+ selves << self
+ end
+ end
+
+ selves[0].should == selves[1]
+ end
+
+ it "adds methods defined in its block to the anonymous module's public instance methods" do
+ inner_self = nil
+ mod = Module.new do
+ refine String do
+ def blah
+ "blah"
+ end
+ inner_self = self
+ end
+ end
+
+ inner_self.public_instance_methods.should include(:blah)
+ end
+
+ it "returns created anonymous module" do
+ inner_self = nil
+ result = nil
+ mod = Module.new do
+ result = refine String do
+ inner_self = self
+ end
+ end
+
+ result.should == inner_self
+ end
+
+ it "raises ArgumentError if not passed an argument" do
+ lambda do
+ Module.new do
+ refine {}
+ end
+ end.should raise_error(ArgumentError)
+ end
+
+ it "raises TypeError if not passed a class" do
+ lambda do
+ Module.new do
+ refine("foo") {}
+ end
+ end.should raise_error(TypeError)
+ end
+
+ ruby_version_is "" ... "2.4" do
+ it "raises TypeError if passed a module" do
+ lambda do
+ Module.new do
+ refine(Enumerable) {}
+ end
+ end.should raise_error(TypeError)
+ end
+ end
+
+ quarantine! do # https://bugs.ruby-lang.org/issues/14070
+ ruby_version_is "2.4" do
+ it "accepts a module as argument" do
+ inner_self = nil
+ Module.new do
+ refine(Enumerable) do
+ def blah
+ end
+ inner_self = self
+ end
+ end
+
+ inner_self.public_instance_methods.should include(:blah)
+ end
+ end
+ end
+
+ it "raises ArgumentError if not given a block" do
+ lambda do
+ Module.new do
+ refine String
+ end
+ end.should raise_error(ArgumentError)
+ end
+
+ it "applies refinements to calls in the refine block" do
+ result = nil
+ Module.new do
+ refine(String) do
+ def foo; "foo"; end
+ result = "hello".foo
+ end
+ end
+ result.should == "foo"
+ end
+
+ it "doesn't apply refinements outside the refine block" do
+ Module.new do
+ refine(String) {def foo; "foo"; end}
+ -> () {
+ "hello".foo
+ }.should raise_error(NoMethodError)
+ end
+ end
+
+ it "does not apply refinements to external scopes not using the module" do
+ Module.new do
+ refine(String) {def foo; 'foo'; end}
+ end
+
+ lambda {"hello".foo}.should raise_error(NoMethodError)
+ end
+
+ # When defining multiple refinements in the same module,
+ # inside a refine block all refinements from the same
+ # module are active when a refined method is called
+ it "makes available all refinements from the same module" do
+ refinement = Module.new do
+ refine Integer do
+ def to_json_format
+ to_s
+ end
+ end
+
+ refine Array do
+ def to_json_format
+ "[" + map { |i| i.to_json_format }.join(", ") + "]"
+ end
+ end
+
+ refine Hash do
+ def to_json_format
+ "{" + map { |k, v| k.to_s.dump + ": " + v.to_json_format }.join(", ") + "}"
+ end
+ end
+ end
+
+ result = nil
+
+ Module.new do
+ using refinement
+
+ result = [{1 => 2}, {3 => 4}].to_json_format
+ end
+
+ result.should == '[{"1": 2}, {"3": 4}]'
+ end
+
+ it "does not make available methods from another refinement module" do
+ refinery_integer = Module.new do
+ refine Integer do
+ def to_json_format
+ to_s
+ end
+ end
+ end
+
+ refinery_array = Module.new do
+ refine Array do
+ def to_json_format
+ "[" + map { |i| i.to_json_format }.join(",") + "]"
+ end
+ end
+ end
+
+ result = nil
+
+ -> () {
+ Module.new do
+ using refinery_integer
+ using refinery_array
+
+ [1, 2].to_json_format
+ end
+ }.should raise_error(NoMethodError)
+ end
+
+ # method lookup:
+ # * The prepended modules from the refinement for C
+ # * The refinement for C
+ # * The included modules from the refinement for C
+ # * The prepended modules of C
+ # * C
+ # * The included modules of C
+ describe "method lookup" do
+ it "looks in the object singleton class first" do
+ refinement = Module.new do
+ refine ModuleSpecs::ClassWithFoo do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+
+ obj = ModuleSpecs::ClassWithFoo.new
+ class << obj
+ def foo; "foo from singleton class"; end
+ end
+ result = obj.foo
+ end
+
+ result.should == "foo from singleton class"
+ end
+
+ it "looks in prepended modules from the refinement first" do
+ refinement = Module.new do
+ refine ModuleSpecs::ClassWithFoo do
+ include ModuleSpecs::IncludedModule
+ prepend ModuleSpecs::PrependedModule
+
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = ModuleSpecs::ClassWithFoo.new.foo
+ end
+
+ result.should == "foo from prepended module"
+ end
+
+ it "looks in refinement then" do
+ refinement = Module.new do
+ refine(ModuleSpecs::ClassWithFoo) do
+ include ModuleSpecs::IncludedModule
+
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = ModuleSpecs::ClassWithFoo.new.foo
+ end
+
+ result.should == "foo from refinement"
+ end
+
+ it "looks in included modules from the refinement then" do
+ refinement = Module.new do
+ refine ModuleSpecs::ClassWithFoo do
+ include ModuleSpecs::IncludedModule
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = ModuleSpecs::ClassWithFoo.new.foo
+ end
+
+ result.should == "foo from included module"
+ end
+
+ it "looks in the class then" do
+ refinement = Module.new do
+ refine(ModuleSpecs::ClassWithFoo) { }
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = ModuleSpecs::ClassWithFoo.new.foo
+ end
+
+ result.should == "foo"
+ end
+ end
+
+
+ # methods in a subclass have priority over refinements in a superclass
+ it "does not override methods in subclasses" do
+ subclass = Class.new(ModuleSpecs::ClassWithFoo) do
+ def foo; "foo from subclass"; end
+ end
+
+ refinement = Module.new do
+ refine ModuleSpecs::ClassWithFoo do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = subclass.new.foo
+ end
+
+ result.should == "foo from subclass"
+ end
+
+ context "for methods accessed indirectly" do
+ ruby_version_is "" ... "2.4" do
+ it "is not honored by Kernel#send" do
+ refinement = Module.new do
+ refine ModuleSpecs::ClassWithFoo do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = ModuleSpecs::ClassWithFoo.new.send :foo
+ end
+
+ result.should == "foo"
+ end
+
+ it "is not honored by BasicObject#__send__" do
+ refinement = Module.new do
+ refine ModuleSpecs::ClassWithFoo do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = ModuleSpecs::ClassWithFoo.new.__send__ :foo
+ end
+
+ result.should == "foo"
+ end
+
+ it "is not honored by Symbol#to_proc" do
+ refinement = Module.new do
+ refine Integer do
+ def to_s
+ "(#{super})"
+ end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = [1, 2, 3].map(&:to_s)
+ end
+
+ result.should == ["1", "2", "3"]
+ end
+ end
+
+ ruby_version_is "2.4" do
+ it "is honored by Kernel#send" do
+ refinement = Module.new do
+ refine ModuleSpecs::ClassWithFoo do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = ModuleSpecs::ClassWithFoo.new.send :foo
+ end
+
+ result.should == "foo from refinement"
+ end
+
+ it "is honored by BasicObject#__send__" do
+ refinement = Module.new do
+ refine ModuleSpecs::ClassWithFoo do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = ModuleSpecs::ClassWithFoo.new.__send__ :foo
+ end
+
+ result.should == "foo from refinement"
+ end
+
+ it "is honored by Symbol#to_proc" do
+ refinement = Module.new do
+ refine Integer do
+ def to_s
+ "(#{super})"
+ end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = [1, 2, 3].map(&:to_s)
+ end
+
+ result.should == ["(1)", "(2)", "(3)"]
+ end
+ end
+
+ ruby_version_is "2.6" do
+ it "is honored by Kernel#public_send" do
+ refinement = Module.new do
+ refine ModuleSpecs::ClassWithFoo do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = ModuleSpecs::ClassWithFoo.new.public_send :foo
+ end
+
+ result.should == "foo from refinement"
+ end
+ end
+
+ ruby_version_is "" ... "2.5" do
+ it "is not honored by string interpolation" do
+ refinement = Module.new do
+ refine Integer do
+ def to_s
+ "foo"
+ end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = "#{1}"
+ end
+
+ result.should == "1"
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "is honored by string interpolation" do
+ refinement = Module.new do
+ refine Integer do
+ def to_s
+ "foo"
+ end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = "#{1}"
+ end
+
+ result.should == "foo"
+ end
+ end
+
+ it "is honored by Kernel#binding" do
+ refinement = Module.new do
+ refine String do
+ def to_s
+ "hello from refinement"
+ end
+ end
+ end
+
+ klass = Class.new do
+ using refinement
+
+ def foo
+ "foo".to_s
+ end
+
+ def get_binding
+ binding
+ end
+ end
+
+ result = Kernel.eval("self.foo()", klass.new.get_binding)
+ result.should == "hello from refinement"
+ end
+
+ it "is not honored by Kernel#method" do
+ klass = Class.new
+ refinement = Module.new do
+ refine klass do
+ def foo; end
+ end
+ end
+
+ -> {
+ Module.new do
+ using refinement
+ klass.new.method(:foo)
+ end
+ }.should raise_error(NameError, /undefined method `foo'/)
+ end
+
+ ruby_version_is "" ... "2.6" do
+ it "is not honored by Kernel#respond_to?" do
+ klass = Class.new
+ refinement = Module.new do
+ refine klass do
+ def foo; end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = klass.new.respond_to?(:foo)
+ end
+
+ result.should == false
+ end
+ end
+
+ ruby_version_is "2.6" do
+ it "is honored by Kernel#respond_to?" do
+ klass = Class.new
+ refinement = Module.new do
+ refine klass do
+ def foo; end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = klass.new.respond_to?(:foo)
+ end
+
+ result.should == true
+ end
+ end
+ end
+
+ context "when super is called in a refinement" do
+ it "looks in the included to refinery module" do
+ refinement = Module.new do
+ refine ModuleSpecs::ClassWithFoo do
+ include ModuleSpecs::IncludedModule
+
+ def foo
+ super
+ end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = ModuleSpecs::ClassWithFoo.new.foo
+ end
+
+ result.should == "foo from included module"
+ end
+
+ it "looks in the refined class" do
+ refinement = Module.new do
+ refine ModuleSpecs::ClassWithFoo do
+ def foo
+ super
+ end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = ModuleSpecs::ClassWithFoo.new.foo
+ end
+
+ result.should == "foo"
+ end
+
+ # super in a method of a refinement invokes the method in the refined
+ # class even if there is another refinement which has been activated
+ # in the same context.
+ it "looks in the refined class even if there is another active refinement" do
+ refinement = Module.new do
+ refine ModuleSpecs::ClassWithFoo do
+ def foo
+ "foo from refinement"
+ end
+ end
+ end
+
+ refinement_with_super = Module.new do
+ refine ModuleSpecs::ClassWithFoo do
+ def foo
+ super
+ end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ using refinement_with_super
+ result = ModuleSpecs::ClassWithFoo.new.foo
+ end
+
+ result.should == "foo"
+ end
+ end
+
+ it 'and alias aliases a method within a refinement module, but not outside it' do
+ Module.new do
+ using Module.new {
+ refine Array do
+ alias :orig_count :count
+ end
+ }
+ [1,2].orig_count.should == 2
+ end
+ lambda { [1,2].orig_count }.should raise_error(NoMethodError)
+ end
+
+ it 'and alias_method aliases a method within a refinement module, but not outside it' do
+ Module.new do
+ using Module.new {
+ refine Array do
+ alias_method :orig_count, :count
+ end
+ }
+ [1,2].orig_count.should == 2
+ end
+ lambda { [1,2].orig_count }.should raise_error(NoMethodError)
+ end
+
+ # Refinements are inherited by module inclusion.
+ # That is, using activates all refinements in the ancestors of the specified module.
+ # Refinements in a descendant have priority over refinements in an ancestor.
+ context "module inclusion" do
+ it "activates all refinements from all ancestors" do
+ refinement_included = Module.new do
+ refine Integer do
+ def to_json_format
+ to_s
+ end
+ end
+ end
+
+ refinement = Module.new do
+ include refinement_included
+
+ refine Array do
+ def to_json_format
+ "[" + map { |i| i.to_s }.join(", ") + "]"
+ end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = [5.to_json_format, [1, 2, 3].to_json_format]
+ end
+
+ result.should == ["5", "[1, 2, 3]"]
+ end
+
+ it "overrides methods of ancestors by methods in descendants" do
+ refinement_included = Module.new do
+ refine Integer do
+ def to_json_format
+ to_s
+ end
+ end
+ end
+
+ refinement = Module.new do
+ include refinement_included
+
+ refine Integer do
+ def to_json_format
+ "hello from refinement"
+ end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = 5.to_json_format
+ end
+
+ result.should == "hello from refinement"
+ end
+ end
+
+ it 'does not list methods defined only in refinement' do
+ refine_object = Module.new do
+ refine Object do
+ def refinement_only_method
+ end
+ end
+ end
+ spec = self
+ klass = Class.new { instance_methods.should_not spec.send(:include, :refinement_only_method) }
+ instance = klass.new
+ instance.methods.should_not include :refinement_only_method
+ instance.respond_to?(:refinement_only_method).should == false
+ -> { instance.method :refinement_only_method }.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/core/module/remove_class_variable_spec.rb b/spec/ruby/core/module/remove_class_variable_spec.rb
new file mode 100644
index 0000000000..e431243ab4
--- /dev/null
+++ b/spec/ruby/core/module/remove_class_variable_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#remove_class_variable" do
+ it "removes class variable" do
+ m = ModuleSpecs::MVars.dup
+ m.send(:remove_class_variable, :@@mvar)
+ m.class_variable_defined?(:@@mvar).should == false
+ end
+
+ it "returns the value of removing class variable" do
+ m = ModuleSpecs::MVars.dup
+ m.send(:remove_class_variable, :@@mvar).should == :mvar
+ end
+
+ it "removes a class variable defined in a metaclass" do
+ obj = mock("metaclass class variable")
+ meta = obj.singleton_class
+ meta.send :class_variable_set, :@@var, 1
+ meta.send(:remove_class_variable, :@@var).should == 1
+ meta.class_variable_defined?(:@@var).should be_false
+ end
+
+ it "raises a NameError when removing class variable declared in included module" do
+ c = ModuleSpecs::RemoveClassVariable.new { include ModuleSpecs::MVars.dup }
+ lambda { c.send(:remove_class_variable, :@@mvar) }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when passed a symbol with one leading @" do
+ lambda { ModuleSpecs::MVars.send(:remove_class_variable, :@mvar) }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when passed a symbol with no leading @" do
+ lambda { ModuleSpecs::MVars.send(:remove_class_variable, :mvar) }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when an uninitialized class variable is given" do
+ lambda { ModuleSpecs::MVars.send(:remove_class_variable, :@@nonexisting_class_variable) }.should raise_error(NameError)
+ end
+
+ it "is public" do
+ Module.should_not have_private_instance_method(:remove_class_variable)
+ end
+end
diff --git a/spec/ruby/core/module/remove_const_spec.rb b/spec/ruby/core/module/remove_const_spec.rb
new file mode 100644
index 0000000000..073e2c1501
--- /dev/null
+++ b/spec/ruby/core/module/remove_const_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/constants'
+
+describe "Module#remove_const" do
+ it "removes the constant specified by a String or Symbol from the receiver's constant table" do
+ ConstantSpecs::ModuleM::CS_CONST252 = :const252
+ ConstantSpecs::ModuleM::CS_CONST252.should == :const252
+
+ ConstantSpecs::ModuleM.send :remove_const, :CS_CONST252
+ lambda { ConstantSpecs::ModuleM::CS_CONST252 }.should raise_error(NameError)
+
+ ConstantSpecs::ModuleM::CS_CONST253 = :const253
+ ConstantSpecs::ModuleM::CS_CONST253.should == :const253
+
+ ConstantSpecs::ModuleM.send :remove_const, "CS_CONST253"
+ lambda { ConstantSpecs::ModuleM::CS_CONST253 }.should raise_error(NameError)
+ end
+
+ it "returns the value of the removed constant" do
+ ConstantSpecs::ModuleM::CS_CONST254 = :const254
+ ConstantSpecs::ModuleM.send(:remove_const, :CS_CONST254).should == :const254
+ end
+
+ it "raises a NameError and does not call #const_missing if the constant is not defined" do
+ ConstantSpecs.should_not_receive(:const_missing)
+ lambda { ConstantSpecs.send(:remove_const, :Nonexistent) }.should raise_error(NameError)
+ end
+
+ it "raises a NameError and does not call #const_missing if the constant is not defined directly in the module" do
+ begin
+ ConstantSpecs::ModuleM::CS_CONST255 = :const255
+ ConstantSpecs::ContainerA::CS_CONST255.should == :const255
+ ConstantSpecs::ContainerA.should_not_receive(:const_missing)
+
+ lambda do
+ ConstantSpecs::ContainerA.send :remove_const, :CS_CONST255
+ end.should raise_error(NameError)
+ ensure
+ ConstantSpecs::ModuleM.send :remove_const, "CS_CONST255"
+ end
+ end
+
+ it "raises a NameError if the name does not start with a capital letter" do
+ lambda { ConstantSpecs.send :remove_const, "name" }.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the name starts with a non-alphabetic character" do
+ lambda { ConstantSpecs.send :remove_const, "__CONSTX__" }.should raise_error(NameError)
+ lambda { ConstantSpecs.send :remove_const, "@Name" }.should raise_error(NameError)
+ lambda { ConstantSpecs.send :remove_const, "!Name" }.should raise_error(NameError)
+ lambda { ConstantSpecs.send :remove_const, "::Name" }.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the name contains non-alphabetic characters except '_'" do
+ ConstantSpecs::ModuleM::CS_CONST256 = :const256
+ ConstantSpecs::ModuleM.send :remove_const, "CS_CONST256"
+ lambda { ConstantSpecs.send :remove_const, "Name=" }.should raise_error(NameError)
+ lambda { ConstantSpecs.send :remove_const, "Name?" }.should raise_error(NameError)
+ end
+
+ it "calls #to_str to convert the given name to a String" do
+ ConstantSpecs::CS_CONST257 = :const257
+ name = mock("CS_CONST257")
+ name.should_receive(:to_str).and_return("CS_CONST257")
+ ConstantSpecs.send(:remove_const, name).should == :const257
+ end
+
+ it "raises a TypeError if conversion to a String by calling #to_str fails" do
+ name = mock('123')
+ lambda { ConstantSpecs.send :remove_const, name }.should raise_error(TypeError)
+
+ name.should_receive(:to_str).and_return(123)
+ lambda { ConstantSpecs.send :remove_const, name }.should raise_error(TypeError)
+ end
+
+ it "is a private method" do
+ Module.private_methods.should include(:remove_const)
+ end
+
+ it "returns nil when removing autoloaded constant" do
+ ConstantSpecs.autoload :AutoloadedConstant, 'a_file'
+ ConstantSpecs.send(:remove_const, :AutoloadedConstant).should be_nil
+ end
+end
diff --git a/spec/ruby/core/module/remove_method_spec.rb b/spec/ruby/core/module/remove_method_spec.rb
new file mode 100644
index 0000000000..f83928f715
--- /dev/null
+++ b/spec/ruby/core/module/remove_method_spec.rb
@@ -0,0 +1,116 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+module ModuleSpecs
+ class Parent
+ def method_to_remove; 1; end
+ end
+
+ class First
+ def method_to_remove; 1; end
+ end
+
+ class Second < First
+ def method_to_remove; 2; end
+ end
+end
+
+describe "Module#remove_method" do
+ before :each do
+ @module = Module.new { def method_to_remove; end }
+ end
+
+ ruby_version_is ''...'2.5' do
+ it "is a private method" do
+ Module.should have_private_instance_method(:remove_method, false)
+ end
+ end
+ ruby_version_is '2.5' do
+ it "is a public method" do
+ Module.should have_public_instance_method(:remove_method, false)
+ end
+ end
+
+ it "removes the method from a class" do
+ klass = Class.new do
+ def method_to_remove; 1; end
+ end
+ x = klass.new
+ klass.send(:remove_method, :method_to_remove)
+ x.respond_to?(:method_to_remove).should == false
+ end
+
+ it "removes method from subclass, but not parent" do
+ child = Class.new(ModuleSpecs::Parent) do
+ def method_to_remove; 2; end
+ remove_method :method_to_remove
+ end
+ x = child.new
+ x.respond_to?(:method_to_remove).should == true
+ x.method_to_remove.should == 1
+ end
+
+ it "removes multiple methods with 1 call" do
+ klass = Class.new do
+ def method_to_remove_1; 1; end
+ def method_to_remove_2; 2; end
+ remove_method :method_to_remove_1, :method_to_remove_2
+ end
+ x = klass.new
+ x.respond_to?(:method_to_remove_1).should == false
+ x.respond_to?(:method_to_remove_2).should == false
+ end
+
+ it "accepts multiple arguments" do
+ Module.instance_method(:remove_method).arity.should < 0
+ end
+
+ it "does not remove any instance methods when argument not given" do
+ before = @module.instance_methods(true) + @module.private_instance_methods(true)
+ @module.send :remove_method
+ after = @module.instance_methods(true) + @module.private_instance_methods(true)
+ before.sort.should == after.sort
+ end
+
+ it "returns self" do
+ @module.send(:remove_method, :method_to_remove).should equal(@module)
+ end
+
+ it "raises a NameError when attempting to remove method further up the inheritance tree" do
+ lambda {
+ class Third < ModuleSpecs::Second
+ remove_method :method_to_remove
+ end
+ }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when attempting to remove a missing method" do
+ lambda {
+ class Third < ModuleSpecs::Second
+ remove_method :blah
+ end
+ }.should raise_error(NameError)
+ end
+
+ describe "on frozen instance" do
+ before :each do
+ @frozen = @module.dup.freeze
+ end
+
+ it "raises a #{frozen_error_class} when passed a name" do
+ lambda { @frozen.send :remove_method, :method_to_remove }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} when passed a missing name" do
+ lambda { @frozen.send :remove_method, :not_exist }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a TypeError when passed a not name" do
+ lambda { @frozen.send :remove_method, Object.new }.should raise_error(TypeError)
+ end
+
+ it "does not raise exceptions when no arguments given" do
+ @frozen.send(:remove_method).should equal(@frozen)
+ end
+ end
+end
diff --git a/spec/ruby/core/module/shared/class_eval.rb b/spec/ruby/core/module/shared/class_eval.rb
new file mode 100644
index 0000000000..6bb9668fee
--- /dev/null
+++ b/spec/ruby/core/module/shared/class_eval.rb
@@ -0,0 +1,115 @@
+describe :module_class_eval, shared: true do
+ # TODO: This should probably be replaced with a "should behave like" that uses
+ # the many scoping/binding specs from kernel/eval_spec, since most of those
+ # behaviors are the same for instance_eval. See also module_eval/class_eval.
+
+ it "evaluates a given string in the context of self" do
+ ModuleSpecs.send(@method, "self").should == ModuleSpecs
+ ModuleSpecs.send(@method, "1 + 1").should == 2
+ end
+
+ it "does not add defined methods to other classes" do
+ FalseClass.send(@method) do
+ def foo
+ 'foo'
+ end
+ end
+ lambda {42.foo}.should raise_error(NoMethodError)
+ end
+
+ it "resolves constants in the caller scope" do
+ ModuleSpecs::ClassEvalTest.get_constant_from_scope.should == ModuleSpecs::Lookup
+ end
+
+ it "resolves constants in the caller scope ignoring send" do
+ ModuleSpecs::ClassEvalTest.get_constant_from_scope_with_send(@method).should == ModuleSpecs::Lookup
+ end
+
+ it "resolves constants in the receiver's scope" do
+ ModuleSpecs.send(@method, "Lookup").should == ModuleSpecs::Lookup
+ ModuleSpecs.send(@method, "Lookup::LOOKIE").should == ModuleSpecs::Lookup::LOOKIE
+ end
+
+ it "defines constants in the receiver's scope" do
+ ModuleSpecs.send(@method, "module NewEvaluatedModule;end")
+ ModuleSpecs.const_defined?(:NewEvaluatedModule, false).should == true
+ end
+
+ it "evaluates a given block in the context of self" do
+ ModuleSpecs.send(@method) { self }.should == ModuleSpecs
+ ModuleSpecs.send(@method) { 1 + 1 }.should == 2
+ end
+
+ it "passes the module as the first argument of the block" do
+ given = nil
+ ModuleSpecs.send(@method) do |block_parameter|
+ given = block_parameter
+ end
+ given.should equal ModuleSpecs
+ end
+
+ it "uses the optional filename and lineno parameters for error messages" do
+ ModuleSpecs.send(@method, "[__FILE__, __LINE__]", "test", 102).should == ["test", 102]
+ end
+
+ 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)
+ 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)
+ lambda { ModuleSpecs.send(@method, "1+1", file) }.should raise_error(TypeError)
+ 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
+ end
+
+ it "raises a TypeError when the given eval-string can't be converted to string using to_str" do
+ o = mock('x')
+ lambda { ModuleSpecs.send(@method, o) }.should raise_error(TypeError)
+
+ (o = mock('123')).should_receive(:to_str).and_return(123)
+ lambda { ModuleSpecs.send(@method, o) }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError when no arguments and no block are given" do
+ lambda { ModuleSpecs.send(@method) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when more than 3 arguments are given" do
+ lambda {
+ ModuleSpecs.send(@method, "1 + 1", "some file", 0, "bogus")
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when a block and normal arguments are given" do
+ lambda {
+ ModuleSpecs.send(@method, "1 + 1") { 1 + 1 }
+ }.should raise_error(ArgumentError)
+ end
+
+ # This case was found because Rubinius was caching the compiled
+ # version of the string and not duping the methods within the
+ # eval, causing the method addition to change the static scope
+ # of the shared CompiledCode.
+ it "adds methods respecting the lexical constant scope" do
+ code = "def self.attribute; C; end"
+
+ a = Class.new do
+ self::C = "A"
+ end
+
+ b = Class.new do
+ self::C = "B"
+ end
+
+ a.send @method, code
+ b.send @method, code
+
+ a.attribute.should == "A"
+ b.attribute.should == "B"
+ end
+end
diff --git a/spec/ruby/core/module/shared/class_exec.rb b/spec/ruby/core/module/shared/class_exec.rb
new file mode 100644
index 0000000000..c5c18b0a34
--- /dev/null
+++ b/spec/ruby/core/module/shared/class_exec.rb
@@ -0,0 +1,29 @@
+describe :module_class_exec, shared: true do
+ it "does not add defined methods to other classes" do
+ FalseClass.send(@method) do
+ def foo
+ 'foo'
+ end
+ end
+ lambda {42.foo}.should raise_error(NoMethodError)
+ end
+
+ it "defines method in the receiver's scope" do
+ ModuleSpecs::Subclass.send(@method) { def foo; end }
+ ModuleSpecs::Subclass.new.respond_to?(:foo).should == true
+ end
+
+ it "evaluates a given block in the context of self" do
+ ModuleSpecs::Subclass.send(@method) { self }.should == ModuleSpecs::Subclass
+ ModuleSpecs::Subclass.new.send(@method) { 1 + 1 }.should == 2
+ end
+
+ it "raises a LocalJumpError when no block is given" do
+ lambda { ModuleSpecs::Subclass.send(@method) }.should raise_error(LocalJumpError)
+ end
+
+ it "passes arguments to the block" do
+ a = ModuleSpecs::Subclass
+ a.send(@method, 1) { |b| b }.should equal(1)
+ end
+end
diff --git a/spec/ruby/core/module/shared/equal_value.rb b/spec/ruby/core/module/shared/equal_value.rb
new file mode 100644
index 0000000000..f1227d873c
--- /dev/null
+++ b/spec/ruby/core/module/shared/equal_value.rb
@@ -0,0 +1,14 @@
+describe :module_equal, shared: true do
+ it "returns true if self and the given module are the same" do
+ ModuleSpecs.send(@method, ModuleSpecs).should == true
+ ModuleSpecs::Child.send(@method, ModuleSpecs::Child).should == true
+ ModuleSpecs::Parent.send(@method, ModuleSpecs::Parent).should == true
+ ModuleSpecs::Basic.send(@method, ModuleSpecs::Basic).should == true
+ ModuleSpecs::Super.send(@method, ModuleSpecs::Super).should == true
+
+ ModuleSpecs::Child.send(@method, ModuleSpecs).should == false
+ ModuleSpecs::Child.send(@method, ModuleSpecs::Parent).should == false
+ ModuleSpecs::Child.send(@method, ModuleSpecs::Basic).should == false
+ ModuleSpecs::Child.send(@method, ModuleSpecs::Super).should == false
+ end
+end
diff --git a/spec/ruby/core/module/shared/set_visibility.rb b/spec/ruby/core/module/shared/set_visibility.rb
new file mode 100644
index 0000000000..c39d59e05d
--- /dev/null
+++ b/spec/ruby/core/module/shared/set_visibility.rb
@@ -0,0 +1,135 @@
+# -*- encoding: us-ascii -*-
+
+describe :set_visibility, shared: true do
+ it "is a private method" do
+ Module.should have_private_instance_method(@method, false)
+ end
+
+ describe "without arguments" do
+ it "sets visibility to following method definitions" do
+ visibility = @method
+ mod = Module.new {
+ send visibility
+
+ def test1() end
+ def test2() end
+ }
+
+ mod.should send(:"have_#{@method}_instance_method", :test1, false)
+ mod.should send(:"have_#{@method}_instance_method", :test2, false)
+ end
+
+ it "stops setting visibility if the body encounters other visibility setters without arguments" do
+ visibility = @method
+ new_visibility = nil
+ mod = Module.new {
+ send visibility
+ new_visibility = [:protected, :private].find {|vis| vis != visibility }
+ send new_visibility
+ def test1() end
+ }
+
+ mod.should send(:"have_#{new_visibility}_instance_method", :test1, false)
+ end
+
+ it "continues setting visibility if the body encounters other visibility setters with arguments" do
+ visibility = @method
+ mod = Module.new {
+ send visibility
+ def test1() end
+ send([:protected, :private].find {|vis| vis != visibility }, :test1)
+ def test2() end
+ }
+
+ mod.should send(:"have_#{@method}_instance_method", :test2, false)
+ end
+
+ it "does not affect module_evaled method definitions when itself is outside the eval" do
+ visibility = @method
+ mod = Module.new {
+ send visibility
+
+ module_eval { def test1() end }
+ module_eval " def test2() end "
+ }
+
+ mod.should have_public_instance_method(:test1, false)
+ mod.should have_public_instance_method(:test2, false)
+ end
+
+ it "does not affect outside method definitions when itself is inside a module_eval" do
+ visibility = @method
+ mod = Module.new {
+ module_eval { send visibility }
+
+ def test1() end
+ }
+
+ mod.should have_public_instance_method(:test1, false)
+ end
+
+ it "affects normally if itself and method definitions are inside a module_eval" do
+ visibility = @method
+ mod = Module.new {
+ module_eval {
+ send visibility
+
+ def test1() end
+ }
+ }
+
+ mod.should send(:"have_#{@method}_instance_method", :test1, false)
+ end
+
+ it "does not affect method definitions when itself is inside an eval and method definitions are outside" do
+ visibility = @method
+ initialized_visibility = [:public, :protected, :private].find {|sym| sym != visibility }
+ mod = Module.new {
+ send initialized_visibility
+ eval visibility.to_s
+
+ def test1() end
+ }
+
+ mod.should send(:"have_#{initialized_visibility}_instance_method", :test1, false)
+ end
+
+ it "affects evaled method definitions when itself is outside the eval" do
+ visibility = @method
+ mod = Module.new {
+ send visibility
+
+ eval "def test1() end"
+ }
+
+ mod.should send(:"have_#{@method}_instance_method", :test1, false)
+ end
+
+ it "affects normally if itself and following method definitions are inside a eval" do
+ visibility = @method
+ mod = Module.new {
+ eval <<-CODE
+ #{visibility}
+
+ def test1() end
+ CODE
+ }
+
+ mod.should send(:"have_#{@method}_instance_method", :test1, false)
+ end
+
+ describe "within a closure" do
+ it "sets the visibility outside the closure" do
+ visibility = @method
+ mod = Module.new {
+ 1.times {
+ send visibility
+ }
+ def test1() end
+ }
+
+ mod.should send(:"have_#{@method}_instance_method", :test1, false)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/module/singleton_class_spec.rb b/spec/ruby/core/module/singleton_class_spec.rb
new file mode 100644
index 0000000000..b9f78eeb21
--- /dev/null
+++ b/spec/ruby/core/module/singleton_class_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+
+describe "Module#singleton_class?" do
+ it "returns true for singleton classes" do
+ xs = self.singleton_class
+ xs.singleton_class?.should == true
+ end
+
+ it "returns false for other classes" do
+ c = Class.new
+ c.singleton_class?.should == false
+ end
+
+ describe "with singleton values" do
+ it "returns false for nil's singleton class" do
+ NilClass.singleton_class?.should == false
+ end
+
+ it "returns false for true's singleton class" do
+ TrueClass.singleton_class?.should == false
+ end
+
+ it "returns false for false's singleton class" do
+ FalseClass.singleton_class?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/module/to_s_spec.rb b/spec/ruby/core/module/to_s_spec.rb
new file mode 100644
index 0000000000..bebdcf6e1b
--- /dev/null
+++ b/spec/ruby/core/module/to_s_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Module#to_s" do
+ it "returns the full constant path leading to the module" do
+ ModuleSpecs::LookupMod.to_s.should == "ModuleSpecs::LookupMod"
+ end
+
+ it "works with an anonymous module" do
+ m = Module.new
+ m.to_s.should =~ /#<Module:0x[0-9a-f]+>/
+ end
+
+ it "works with an anonymous class" do
+ c = Class.new
+ c.to_s.should =~ /#<Class:0x[0-9a-f]+>/
+ end
+end
diff --git a/spec/ruby/core/module/undef_method_spec.rb b/spec/ruby/core/module/undef_method_spec.rb
new file mode 100644
index 0000000000..539de330e9
--- /dev/null
+++ b/spec/ruby/core/module/undef_method_spec.rb
@@ -0,0 +1,159 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+module ModuleSpecs
+ class Parent
+ def method_to_undef() 1 end
+ def another_method_to_undef() 1 end
+ end
+
+ class Ancestor
+ def method_to_undef() 1 end
+ def another_method_to_undef() 1 end
+ end
+end
+
+describe "Module#undef_method" do
+ before :each do
+ @module = Module.new { def method_to_undef; end }
+ end
+
+ ruby_version_is ''...'2.5' do
+ it "is a private method" do
+ Module.should have_private_instance_method(:undef_method, false)
+ end
+ end
+ ruby_version_is '2.5' do
+ it "is a public method" do
+ Module.should have_public_instance_method(:undef_method, false)
+ end
+ end
+
+ it "requires multiple arguments" do
+ Module.instance_method(:undef_method).arity.should < 0
+ end
+
+ it "allows multiple methods to be removed at once" do
+ klass = Class.new do
+ def method_to_undef() 1 end
+ def another_method_to_undef() 1 end
+ end
+ x = klass.new
+ klass.send(:undef_method, :method_to_undef, :another_method_to_undef)
+
+ lambda { x.method_to_undef }.should raise_error(NoMethodError)
+ lambda { x.another_method_to_undef }.should raise_error(NoMethodError)
+ end
+
+ it "does not undef any instance methods when argument not given" do
+ before = @module.instance_methods(true) + @module.private_instance_methods(true)
+ @module.send :undef_method
+ after = @module.instance_methods(true) + @module.private_instance_methods(true)
+ before.sort.should == after.sort
+ end
+
+ it "returns self" do
+ @module.send(:undef_method, :method_to_undef).should equal(@module)
+ end
+
+ it "raises a NameError when passed a missing name" do
+ lambda { @module.send :undef_method, :not_exist }.should raise_error(NameError) { |e|
+ # a NameError and not a NoMethodError
+ e.class.should == NameError
+ }
+ end
+
+ describe "on frozen instance" do
+ before :each do
+ @frozen = @module.dup.freeze
+ end
+
+ it "raises a #{frozen_error_class} when passed a name" do
+ lambda { @frozen.send :undef_method, :method_to_undef }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} when passed a missing name" do
+ lambda { @frozen.send :undef_method, :not_exist }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a TypeError when passed a not name" do
+ lambda { @frozen.send :undef_method, Object.new }.should raise_error(TypeError)
+ end
+
+ it "does not raise exceptions when no arguments given" do
+ @frozen.send(:undef_method).should equal(@frozen)
+ end
+ end
+end
+
+describe "Module#undef_method with symbol" do
+ it "removes a method defined in a class" do
+ klass = Class.new do
+ def method_to_undef() 1 end
+ def another_method_to_undef() 1 end
+ end
+ x = klass.new
+
+ x.method_to_undef.should == 1
+
+ klass.send :undef_method, :method_to_undef
+
+ lambda { x.method_to_undef }.should raise_error(NoMethodError)
+ end
+
+ it "removes a method defined in a super class" do
+ child_class = Class.new(ModuleSpecs::Parent)
+ child = child_class.new
+ child.method_to_undef.should == 1
+
+ child_class.send :undef_method, :method_to_undef
+
+ lambda { child.method_to_undef }.should raise_error(NoMethodError)
+ end
+
+ it "does not remove a method defined in a super class when removed from a subclass" do
+ descendant = Class.new(ModuleSpecs::Ancestor)
+ ancestor = ModuleSpecs::Ancestor.new
+ ancestor.method_to_undef.should == 1
+
+ descendant.send :undef_method, :method_to_undef
+
+ ancestor.method_to_undef.should == 1
+ end
+end
+
+describe "Module#undef_method with string" do
+ it "removes a method defined in a class" do
+ klass = Class.new do
+ def method_to_undef() 1 end
+ def another_method_to_undef() 1 end
+ end
+ x = klass.new
+
+ x.another_method_to_undef.should == 1
+
+ klass.send :undef_method, 'another_method_to_undef'
+
+ lambda { x.another_method_to_undef }.should raise_error(NoMethodError)
+ end
+
+ it "removes a method defined in a super class" do
+ child_class = Class.new(ModuleSpecs::Parent)
+ child = child_class.new
+ child.another_method_to_undef.should == 1
+
+ child_class.send :undef_method, 'another_method_to_undef'
+
+ lambda { child.another_method_to_undef }.should raise_error(NoMethodError)
+ end
+
+ it "does not remove a method defined in a super class when removed from a subclass" do
+ descendant = Class.new(ModuleSpecs::Ancestor)
+ ancestor = ModuleSpecs::Ancestor.new
+ ancestor.another_method_to_undef.should == 1
+
+ descendant.send :undef_method, 'another_method_to_undef'
+
+ ancestor.another_method_to_undef.should == 1
+ end
+end
diff --git a/spec/ruby/core/module/using_spec.rb b/spec/ruby/core/module/using_spec.rb
new file mode 100644
index 0000000000..9b6b38f622
--- /dev/null
+++ b/spec/ruby/core/module/using_spec.rb
@@ -0,0 +1,287 @@
+require_relative '../../spec_helper'
+
+describe "Module#using" do
+ it "imports class refinements from module into the current class/module" do
+ refinement = Module.new do
+ refine Integer do
+ def foo; "foo"; end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = 1.foo
+ end
+
+ result.should == "foo"
+ end
+
+ it "accepts module as argument" do
+ refinement = Module.new do
+ refine Integer do
+ def foo; "foo"; end
+ end
+ end
+
+ -> () {
+ Module.new do
+ using refinement
+ end
+ }.should_not raise_error
+ end
+
+ it "accepts module without refinements" do
+ mod = Module.new
+
+ -> () {
+ Module.new do
+ using mod
+ end
+ }.should_not raise_error
+ end
+
+ it "does not accept class" do
+ klass = Class.new
+
+ -> () {
+ Module.new do
+ using klass
+ end
+ }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError if passed something other than module" do
+ -> () {
+ Module.new do
+ using "foo"
+ end
+ }.should raise_error(TypeError)
+ end
+
+ it "returns self" do
+ refinement = Module.new
+
+ result = nil
+ mod = Module.new do
+ result = using refinement
+ end
+
+ result.should equal(mod)
+ end
+
+ it "works in classes too" do
+ refinement = Module.new do
+ refine Integer do
+ def foo; "foo"; end
+ end
+ end
+
+ result = nil
+ Class.new do
+ using refinement
+ result = 1.foo
+ end
+
+ result.should == "foo"
+ end
+
+ it "raises error in method scope" do
+ mod = Module.new do
+ def self.foo
+ using Module.new {}
+ end
+ end
+
+ -> () {
+ mod.foo
+ }.should raise_error(RuntimeError, /Module#using is not permitted in methods/)
+ end
+
+ it "activates refinement even for existed objects" do
+ result = nil
+
+ Module.new do
+ klass = Class.new do
+ def foo; "foo"; end
+ end
+
+ refinement = Module.new do
+ refine klass do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ obj = klass.new
+ using refinement
+ result = obj.foo
+ end
+
+ result.should == "foo from refinement"
+ end
+
+ it "activates updates when refinement reopens later" do
+ result = nil
+
+ Module.new do
+ klass = Class.new do
+ def foo; "foo"; end
+ end
+
+ refinement = Module.new do
+ refine klass do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ using refinement
+
+ refinement.class_eval do
+ refine klass do
+ def foo; "foo from reopened refinement"; end
+ end
+ end
+
+ obj = klass.new
+ result = obj.foo
+ end
+
+ result.should == "foo from reopened refinement"
+ end
+
+ describe "scope of refinement" do
+ it "is active until the end of current class/module" do
+ ScratchPad.record []
+
+ Module.new do
+ Class.new do
+ using Module.new {
+ refine String do
+ def to_s; "hello from refinement"; end
+ end
+ }
+ ScratchPad << "1".to_s
+ end
+
+ ScratchPad << "1".to_s
+ end
+
+ ScratchPad.recorded.should == ["hello from refinement", "1"]
+ end
+
+ # Refinements are lexical in scope.
+ # Refinements are only active within a scope after the call to using.
+ # Any code before the using statement will not have the refinement activated.
+ it "is not active before the `using` call" do
+ ScratchPad.record []
+
+ Module.new do
+ Class.new do
+ ScratchPad << "1".to_s
+ using Module.new {
+ refine String do
+ def to_s; "hello from refinement"; end
+ end
+ }
+ ScratchPad << "1".to_s
+ end
+ end
+
+ ScratchPad.recorded.should == ["1", "hello from refinement"]
+ end
+
+ # If you call a method that is defined outside the current scope
+ # the refinement will be deactivated
+ it "is not active for code defined outside the current scope" do
+ result = nil
+
+ Module.new do
+ klass = Class.new do
+ def foo; "foo"; end
+ end
+
+ refinement = Module.new do
+ refine klass do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ def self.call_foo(c)
+ c.foo
+ end
+
+ using refinement
+
+ result = call_foo(klass.new)
+ end
+
+ result.should == "foo"
+ end
+
+ # If a method is defined in a scope where a refinement is active
+ # the refinement will be active when the method is called.
+ it "is active for method defined in a scope wherever it's called" do
+ klass = Class.new do
+ def foo; "foo"; end
+ end
+
+ mod = Module.new do
+ refinement = Module.new do
+ refine klass do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ using refinement
+
+ def self.call_foo(c)
+ c.foo
+ end
+ end
+
+ c = klass.new
+ mod.call_foo(c).should == "foo from refinement"
+ end
+
+ it "is not active if `using` call is not evaluated" do
+ result = nil
+
+ Module.new do
+ if false
+ using Module.new {
+ refine String do
+ def to_s; "hello from refinement"; end
+ end
+ }
+ end
+ result = "1".to_s
+ end
+
+ result.should == "1"
+ end
+
+ # The refinements in module are not activated automatically
+ # if the class is reopened later
+ it "is not active when class/module reopens" do
+ refinement = Module.new do
+ refine String do
+ def to_s
+ "hello from refinement"
+ end
+ end
+ end
+
+ result = []
+ klass = Class.new do
+ using refinement
+ result << "1".to_s
+ end
+
+ klass.class_eval do
+ result << "1".to_s
+ end
+
+ result.should == ["hello from refinement", "1"]
+ end
+ end
+end
diff --git a/spec/ruby/core/mutex/lock_spec.rb b/spec/ruby/core/mutex/lock_spec.rb
new file mode 100644
index 0000000000..a1a2972d8c
--- /dev/null
+++ b/spec/ruby/core/mutex/lock_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+
+describe "Mutex#lock" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "returns self" do
+ m = Mutex.new
+ m.lock.should == m
+ m.unlock
+ end
+
+ it "blocks the caller if already locked" do
+ m = Mutex.new
+ m.lock
+ lambda { m.lock }.should block_caller
+ end
+
+ it "does not block the caller if not locked" do
+ m = Mutex.new
+ lambda { m.lock }.should_not block_caller
+ end
+
+ # Unable to find a specific ticket but behavior change may be
+ # related to this ML thread.
+ it "raises a ThreadError when used recursively" do
+ m = Mutex.new
+ m.lock
+ -> {
+ m.lock
+ }.should raise_error(ThreadError)
+ end
+end
diff --git a/spec/ruby/core/mutex/locked_spec.rb b/spec/ruby/core/mutex/locked_spec.rb
new file mode 100644
index 0000000000..1bf3ed6394
--- /dev/null
+++ b/spec/ruby/core/mutex/locked_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+
+describe "Mutex#locked?" do
+ it "returns true if locked" do
+ m = Mutex.new
+ m.lock
+ m.locked?.should be_true
+ end
+
+ it "returns false if unlocked" do
+ m = Mutex.new
+ m.locked?.should be_false
+ end
+
+ it "returns the status of the lock" do
+ m1 = Mutex.new
+ m2 = Mutex.new
+
+ m2.lock # hold th with only m1 locked
+ m1_locked = false
+
+ th = Thread.new do
+ m1.lock
+ m1_locked = true
+ m2.lock
+ end
+
+ Thread.pass until m1_locked
+
+ m1.locked?.should be_true
+ m2.unlock # release th
+ th.join
+ # A Thread releases its locks upon termination
+ m1.locked?.should be_false
+ end
+end
diff --git a/spec/ruby/core/mutex/owned_spec.rb b/spec/ruby/core/mutex/owned_spec.rb
new file mode 100644
index 0000000000..e66062534e
--- /dev/null
+++ b/spec/ruby/core/mutex/owned_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+
+describe "Mutex#owned?" do
+ describe "when unlocked" do
+ it "returns false" do
+ m = Mutex.new
+ m.owned?.should be_false
+ end
+ end
+
+ describe "when locked by the current thread" do
+ it "returns true" do
+ m = Mutex.new
+ m.lock
+ m.owned?.should be_true
+ end
+ end
+
+ describe "when locked by another thread" do
+ before :each do
+ @checked = false
+ end
+
+ after :each do
+ @checked = true
+ @th.join
+ end
+
+ it "returns false" do
+ m = Mutex.new
+ locked = false
+
+ @th = Thread.new do
+ m.lock
+ locked = true
+ Thread.pass until @checked
+ end
+
+ Thread.pass until locked
+ m.owned?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/core/mutex/sleep_spec.rb b/spec/ruby/core/mutex/sleep_spec.rb
new file mode 100644
index 0000000000..72ed397eb5
--- /dev/null
+++ b/spec/ruby/core/mutex/sleep_spec.rb
@@ -0,0 +1,95 @@
+require_relative '../../spec_helper'
+
+describe "Mutex#sleep" do
+ describe "when not locked by the current thread" do
+ it "raises a ThreadError" do
+ m = Mutex.new
+ lambda { m.sleep }.should raise_error(ThreadError)
+ end
+
+ it "raises an ArgumentError if passed a negative duration" do
+ m = Mutex.new
+ lambda { m.sleep(-0.1) }.should raise_error(ArgumentError)
+ lambda { m.sleep(-1) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "raises an ArgumentError if passed a negative duration" do
+ m = Mutex.new
+ m.lock
+ lambda { m.sleep(-0.1) }.should raise_error(ArgumentError)
+ lambda { m.sleep(-1) }.should raise_error(ArgumentError)
+ end
+
+ it "pauses execution for approximately the duration requested" do
+ m = Mutex.new
+ m.lock
+ duration = 0.1
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ m.sleep duration
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ (now - start).should > 0
+ (now - start).should < 2.0
+ end
+
+ it "unlocks the mutex while sleeping" do
+ m = Mutex.new
+ locked = false
+ th = Thread.new { m.lock; locked = true; m.sleep }
+ Thread.pass until locked
+ Thread.pass while th.status and th.status != "sleep"
+ m.locked?.should be_false
+ th.run
+ th.join
+ end
+
+ it "relocks the mutex when woken" do
+ m = Mutex.new
+ m.lock
+ m.sleep(0.01)
+ m.locked?.should be_true
+ end
+
+ it "relocks the mutex when woken by an exception being raised" do
+ m = Mutex.new
+ locked = false
+ th = Thread.new do
+ m.lock
+ locked = true
+ begin
+ m.sleep
+ rescue Exception
+ m.locked?
+ end
+ end
+ Thread.pass until locked
+ Thread.pass while th.status and th.status != "sleep"
+ th.raise(Exception)
+ th.value.should be_true
+ end
+
+ it "returns the rounded number of seconds asleep" do
+ m = Mutex.new
+ m.lock
+ m.sleep(0.01).should be_kind_of(Integer)
+ end
+
+ it "wakes up when requesting sleep times near or equal to zero" do
+ times = []
+ val = 1
+
+ # power of two divisor so we eventually get near zero
+ loop do
+ val = val / 16.0
+ times << val
+ break if val == 0.0
+ end
+
+ m = Mutex.new
+ m.lock
+ times.each do |time|
+ # just testing that sleep completes
+ m.sleep(time).should_not == nil
+ end
+ end
+end
diff --git a/spec/ruby/core/mutex/synchronize_spec.rb b/spec/ruby/core/mutex/synchronize_spec.rb
new file mode 100644
index 0000000000..e3dad508b8
--- /dev/null
+++ b/spec/ruby/core/mutex/synchronize_spec.rb
@@ -0,0 +1,66 @@
+require_relative '../../spec_helper'
+
+describe "Mutex#synchronize" do
+ it "wraps the lock/unlock pair in an ensure" do
+ m1 = Mutex.new
+ m2 = Mutex.new
+ m2.lock
+ synchronized = false
+
+ th = Thread.new do
+ lambda do
+ m1.synchronize do
+ synchronized = true
+ m2.lock
+ raise Exception
+ end
+ end.should raise_error(Exception)
+ end
+
+ Thread.pass until synchronized
+
+ m1.locked?.should be_true
+ m2.unlock
+ th.join
+ m1.locked?.should be_false
+ end
+
+ it "blocks the caller if already locked" do
+ m = Mutex.new
+ m.lock
+ lambda { m.synchronize { } }.should block_caller
+ end
+
+ it "does not block the caller if not locked" do
+ m = Mutex.new
+ lambda { m.synchronize { } }.should_not block_caller
+ end
+
+ it "blocks the caller if another thread is also in the synchronize block" do
+ m = Mutex.new
+ q1 = Queue.new
+ q2 = Queue.new
+
+ t = Thread.new {
+ m.synchronize {
+ q1.push :ready
+ q2.pop
+ }
+ }
+
+ q1.pop.should == :ready
+
+ lambda { m.synchronize { } }.should block_caller
+
+ q2.push :done
+ t.join
+ end
+
+ it "is not recursive" do
+ m = Mutex.new
+
+ m.synchronize do
+ lambda { m.synchronize { } }.should raise_error(ThreadError)
+ end
+ end
+end
diff --git a/spec/ruby/core/mutex/try_lock_spec.rb b/spec/ruby/core/mutex/try_lock_spec.rb
new file mode 100644
index 0000000000..8d521f4c6b
--- /dev/null
+++ b/spec/ruby/core/mutex/try_lock_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+
+describe "Mutex#try_lock" do
+ describe "when unlocked" do
+ it "returns true" do
+ m = Mutex.new
+ m.try_lock.should be_true
+ end
+
+ it "locks the mutex" do
+ m = Mutex.new
+ m.try_lock
+ m.locked?.should be_true
+ end
+ end
+
+ describe "when locked by the current thread" do
+ it "returns false" do
+ m = Mutex.new
+ m.lock
+ m.try_lock.should be_false
+ end
+ end
+
+ describe "when locked by another thread" do
+ it "returns false" do
+ m = Mutex.new
+ m.lock
+ Thread.new { m.try_lock }.value.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/core/mutex/unlock_spec.rb b/spec/ruby/core/mutex/unlock_spec.rb
new file mode 100644
index 0000000000..12353f9ba3
--- /dev/null
+++ b/spec/ruby/core/mutex/unlock_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+
+describe "Mutex#unlock" do
+ it "raises ThreadError unless Mutex is locked" do
+ mutex = Mutex.new
+ lambda { mutex.unlock }.should raise_error(ThreadError)
+ end
+
+ it "raises ThreadError unless thread owns Mutex" do
+ mutex = Mutex.new
+ wait = Mutex.new
+ wait.lock
+ th = Thread.new do
+ mutex.lock
+ wait.lock
+ end
+
+ # avoid race on mutex.lock
+ Thread.pass until mutex.locked?
+ Thread.pass while th.status and th.status != "sleep"
+
+ lambda { mutex.unlock }.should raise_error(ThreadError)
+
+ wait.unlock
+ th.join
+ end
+
+ it "raises ThreadError if previously locking thread is gone" do
+ mutex = Mutex.new
+ th = Thread.new do
+ mutex.lock
+ end
+
+ th.join
+
+ lambda { mutex.unlock }.should raise_error(ThreadError)
+ end
+end
diff --git a/spec/ruby/core/nil/and_spec.rb b/spec/ruby/core/nil/and_spec.rb
new file mode 100644
index 0000000000..cd25aff1de
--- /dev/null
+++ b/spec/ruby/core/nil/and_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "NilClass#&" do
+ it "returns false" do
+ (nil & nil).should == false
+ (nil & true).should == false
+ (nil & false).should == false
+ (nil & "").should == false
+ (nil & mock('x')).should == false
+ end
+end
diff --git a/spec/ruby/core/nil/dup_spec.rb b/spec/ruby/core/nil/dup_spec.rb
new file mode 100644
index 0000000000..21b2c92220
--- /dev/null
+++ b/spec/ruby/core/nil/dup_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '2.4' do
+ describe "NilClass#dup" do
+ it "returns self" do
+ nil.dup.should equal(nil)
+ end
+ end
+end
diff --git a/spec/ruby/core/nil/inspect_spec.rb b/spec/ruby/core/nil/inspect_spec.rb
new file mode 100644
index 0000000000..1babc3d062
--- /dev/null
+++ b/spec/ruby/core/nil/inspect_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "NilClass#inspect" do
+ it "returns the string 'nil'" do
+ nil.inspect.should == "nil"
+ end
+end
diff --git a/spec/ruby/core/nil/nil_spec.rb b/spec/ruby/core/nil/nil_spec.rb
new file mode 100644
index 0000000000..47db0c5cf0
--- /dev/null
+++ b/spec/ruby/core/nil/nil_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "NilClass#nil?" do
+ it "returns true" do
+ nil.nil?.should == true
+ end
+end
diff --git a/spec/ruby/core/nil/nilclass_spec.rb b/spec/ruby/core/nil/nilclass_spec.rb
new file mode 100644
index 0000000000..72d3d37b2e
--- /dev/null
+++ b/spec/ruby/core/nil/nilclass_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "NilClass" do
+ it ".allocate raises a TypeError" do
+ lambda do
+ NilClass.allocate
+ end.should raise_error(TypeError)
+ end
+
+ it ".new is undefined" do
+ lambda do
+ NilClass.new
+ end.should raise_error(NoMethodError)
+ end
+end
diff --git a/spec/ruby/core/nil/or_spec.rb b/spec/ruby/core/nil/or_spec.rb
new file mode 100644
index 0000000000..473a833d71
--- /dev/null
+++ b/spec/ruby/core/nil/or_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "NilClass#|" do
+ it "returns false if other is nil or false, otherwise true" do
+ (nil | nil).should == false
+ (nil | true).should == true
+ (nil | false).should == false
+ (nil | "").should == true
+ (nil | mock('x')).should == true
+ end
+end
diff --git a/spec/ruby/core/nil/rationalize_spec.rb b/spec/ruby/core/nil/rationalize_spec.rb
new file mode 100644
index 0000000000..f65d8eebee
--- /dev/null
+++ b/spec/ruby/core/nil/rationalize_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+
+describe "NilClass#rationalize" do
+ it "returns 0/1" do
+ nil.rationalize.should == Rational(0, 1)
+ end
+
+ it "ignores a single argument" do
+ nil.rationalize(0.1).should == Rational(0, 1)
+ end
+
+ it "raises ArgumentError when passed more than one argument" do
+ lambda { nil.rationalize(0.1, 0.1) }.should raise_error(ArgumentError)
+ lambda { nil.rationalize(0.1, 0.1, 2) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/nil/to_a_spec.rb b/spec/ruby/core/nil/to_a_spec.rb
new file mode 100644
index 0000000000..b8b339e330
--- /dev/null
+++ b/spec/ruby/core/nil/to_a_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "NilClass#to_a" do
+ it "returns an empty array" do
+ nil.to_a.should == []
+ end
+end
diff --git a/spec/ruby/core/nil/to_c_spec.rb b/spec/ruby/core/nil/to_c_spec.rb
new file mode 100644
index 0000000000..e0052be5bd
--- /dev/null
+++ b/spec/ruby/core/nil/to_c_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "NilClass#to_c" do
+ it "returns Complex(0, 0)" do
+ nil.to_c.should eql(Complex(0, 0))
+ end
+end
diff --git a/spec/ruby/core/nil/to_f_spec.rb b/spec/ruby/core/nil/to_f_spec.rb
new file mode 100644
index 0000000000..a5f24ba3bf
--- /dev/null
+++ b/spec/ruby/core/nil/to_f_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "NilClass#to_f" do
+ it "returns 0.0" do
+ nil.to_f.should == 0.0
+ end
+
+ it "does not cause NilClass to be coerced to Float" do
+ (0.0 == nil).should == false
+ end
+end
diff --git a/spec/ruby/core/nil/to_h_spec.rb b/spec/ruby/core/nil/to_h_spec.rb
new file mode 100644
index 0000000000..5c8d5dc25a
--- /dev/null
+++ b/spec/ruby/core/nil/to_h_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+
+describe "NilClass#to_h" do
+ it "returns an empty hash" do
+ nil.to_h.should == {}
+ nil.to_h.default.should == nil
+ end
+end
diff --git a/spec/ruby/core/nil/to_i_spec.rb b/spec/ruby/core/nil/to_i_spec.rb
new file mode 100644
index 0000000000..099792ef68
--- /dev/null
+++ b/spec/ruby/core/nil/to_i_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "NilClass#to_i" do
+ it "returns 0" do
+ nil.to_i.should == 0
+ end
+
+ it "does not cause NilClass to be coerced to Fixnum" do
+ (0 == nil).should == false
+ end
+end
diff --git a/spec/ruby/core/nil/to_r_spec.rb b/spec/ruby/core/nil/to_r_spec.rb
new file mode 100644
index 0000000000..8be43baf00
--- /dev/null
+++ b/spec/ruby/core/nil/to_r_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "NilClass#to_r" do
+ it "returns 0/1" do
+ nil.to_r.should == Rational(0, 1)
+ end
+end
diff --git a/spec/ruby/core/nil/to_s_spec.rb b/spec/ruby/core/nil/to_s_spec.rb
new file mode 100644
index 0000000000..c7a18e2527
--- /dev/null
+++ b/spec/ruby/core/nil/to_s_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "NilClass#to_s" do
+ it "returns the string ''" do
+ nil.to_s.should == ""
+ end
+end
diff --git a/spec/ruby/core/nil/xor_spec.rb b/spec/ruby/core/nil/xor_spec.rb
new file mode 100644
index 0000000000..b45da9d443
--- /dev/null
+++ b/spec/ruby/core/nil/xor_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "NilClass#^" do
+ it "returns false if other is nil or false, otherwise true" do
+ (nil ^ nil).should == false
+ (nil ^ true).should == true
+ (nil ^ false).should == false
+ (nil ^ "").should == true
+ (nil ^ mock('x')).should == true
+ end
+end
diff --git a/spec/ruby/core/numeric/abs2_spec.rb b/spec/ruby/core/numeric/abs2_spec.rb
new file mode 100644
index 0000000000..0e60cd0ae7
--- /dev/null
+++ b/spec/ruby/core/numeric/abs2_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+
+describe "Numeric#abs2" do
+ before :each do
+ @numbers = [
+ 0,
+ 0.0,
+ 1,
+ 20,
+ bignum_value,
+ 278202.292871,
+ 72829,
+ 3.333333333333,
+ 0.1,
+ infinity_value
+ ].map { |n| [-n, n] }.flatten
+ end
+
+ it "returns the square of the absolute value of self" do
+ @numbers.each do |number|
+ number.abs2.should eql(number.abs ** 2)
+ end
+ end
+
+ it "calls #* on self" do
+ number = mock_numeric('numeric')
+ number.should_receive(:*).and_return(:result)
+ number.abs2.should == :result
+ end
+
+ it "returns NaN when self is NaN" do
+ nan_value.abs2.nan?.should be_true
+ end
+end
diff --git a/spec/ruby/core/numeric/abs_spec.rb b/spec/ruby/core/numeric/abs_spec.rb
new file mode 100644
index 0000000000..8bec50e337
--- /dev/null
+++ b/spec/ruby/core/numeric/abs_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/abs'
+
+describe "Numeric#abs" do
+ it_behaves_like :numeric_abs, :abs
+end
diff --git a/spec/ruby/core/numeric/angle_spec.rb b/spec/ruby/core/numeric/angle_spec.rb
new file mode 100644
index 0000000000..bb38165777
--- /dev/null
+++ b/spec/ruby/core/numeric/angle_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arg'
+
+describe "Numeric#angle" do
+ it_behaves_like :numeric_arg, :angle
+end
diff --git a/spec/ruby/core/numeric/arg_spec.rb b/spec/ruby/core/numeric/arg_spec.rb
new file mode 100644
index 0000000000..ba3b57c687
--- /dev/null
+++ b/spec/ruby/core/numeric/arg_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arg'
+
+describe "Numeric#arg" do
+ it_behaves_like :numeric_arg, :arg
+end
diff --git a/spec/ruby/core/numeric/ceil_spec.rb b/spec/ruby/core/numeric/ceil_spec.rb
new file mode 100644
index 0000000000..00c856e79b
--- /dev/null
+++ b/spec/ruby/core/numeric/ceil_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#ceil" do
+ it "converts self to a Float (using #to_f) and returns the #ceil'ed result" do
+ o = mock_numeric("ceil")
+ o.should_receive(:to_f).and_return(1 + TOLERANCE)
+ o.ceil.should == 2
+
+ o2 = mock_numeric("ceil")
+ v = -1 - TOLERANCE
+ o2.should_receive(:to_f).and_return(v)
+ o2.ceil.should == -1
+ end
+end
diff --git a/spec/ruby/core/numeric/coerce_spec.rb b/spec/ruby/core/numeric/coerce_spec.rb
new file mode 100644
index 0000000000..c0136b181e
--- /dev/null
+++ b/spec/ruby/core/numeric/coerce_spec.rb
@@ -0,0 +1,59 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#coerce" do
+ before :each do
+ @obj = NumericSpecs::Subclass.new
+ @obj.should_receive(:to_f).any_number_of_times.and_return(10.5)
+ end
+
+ it "returns [other, self] if self and other are instances of the same class" do
+ a = NumericSpecs::Subclass.new
+ b = NumericSpecs::Subclass.new
+
+ a.coerce(b).should == [b, a]
+ end
+
+ # I (emp) think that this behavior is actually a bug in MRI. It's here as documentation
+ # of the behavior until we find out if it's a bug.
+ quarantine! do
+ it "considers the presence of a metaclass when checking the class of the objects" do
+ a = NumericSpecs::Subclass.new
+ b = NumericSpecs::Subclass.new
+
+ # inject a metaclass on a
+ class << a; true; end
+
+ # watch it explode
+ lambda { a.coerce(b) }.should raise_error(TypeError)
+ end
+ end
+
+ it "returns [other.to_f, self.to_f] if self and other are instances of different classes" do
+ @obj.coerce(2.5).should == [2.5, 10.5]
+ @obj.coerce(3).should == [3.0, 10.5]
+ @obj.coerce("4.4").should == [4.4, 10.5]
+ @obj.coerce(bignum_value).should == [bignum_value.to_f, 10.5]
+ end
+
+ it "raise TypeError if they are instances of different classes and other does not respond to #to_f" do
+ other = mock("numeric")
+ lambda { @obj.coerce(other) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { @obj.coerce(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a boolean" do
+ lambda { @obj.coerce(false) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a Symbol" do
+ lambda { @obj.coerce(:symbol) }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError when passed a non-numeric String" do
+ lambda { @obj.coerce("test") }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/numeric/comparison_spec.rb b/spec/ruby/core/numeric/comparison_spec.rb
new file mode 100644
index 0000000000..4b4d52501a
--- /dev/null
+++ b/spec/ruby/core/numeric/comparison_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#<=>" do
+ before :each do
+ @obj = NumericSpecs::Subclass.new
+ end
+
+ it "returns 0 if self equals other" do
+ (@obj <=> @obj).should == 0
+ end
+
+ it "returns nil if self does not equal other" do
+ (@obj <=> NumericSpecs::Subclass.new).should == nil
+ (@obj <=> 10).should == nil
+ (@obj <=> -3.5).should == nil
+ (@obj <=> bignum_value).should == nil
+ end
+
+ describe "with subclasses of Numeric" do
+ before :each do
+ @a = NumericSpecs::Comparison.new
+ @b = NumericSpecs::Comparison.new
+
+ ScratchPad.clear
+ end
+
+ it "is called when instances are compared with #<" do
+ (@a < @b).should be_false
+ ScratchPad.recorded.should == :numeric_comparison
+ end
+
+ it "is called when instances are compared with #<=" do
+ (@a <= @b).should be_false
+ ScratchPad.recorded.should == :numeric_comparison
+ end
+
+ it "is called when instances are compared with #>" do
+ (@a > @b).should be_true
+ ScratchPad.recorded.should == :numeric_comparison
+ end
+
+ it "is called when instances are compared with #>=" do
+ (@a >= @b).should be_true
+ ScratchPad.recorded.should == :numeric_comparison
+ end
+ end
+end
diff --git a/spec/ruby/core/numeric/conj_spec.rb b/spec/ruby/core/numeric/conj_spec.rb
new file mode 100644
index 0000000000..7d4777ca60
--- /dev/null
+++ b/spec/ruby/core/numeric/conj_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/conj'
+
+describe "Numeric#conj" do
+ it_behaves_like :numeric_conj, :conj
+end
diff --git a/spec/ruby/core/numeric/conjugate_spec.rb b/spec/ruby/core/numeric/conjugate_spec.rb
new file mode 100644
index 0000000000..99854766e7
--- /dev/null
+++ b/spec/ruby/core/numeric/conjugate_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/conj'
+
+describe "Numeric#conjugate" do
+ it_behaves_like :numeric_conj, :conjugate
+end
diff --git a/spec/ruby/core/numeric/denominator_spec.rb b/spec/ruby/core/numeric/denominator_spec.rb
new file mode 100644
index 0000000000..34729446a2
--- /dev/null
+++ b/spec/ruby/core/numeric/denominator_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+
+describe "Numeric#denominator" do
+ # The Numeric child classes override this method, so their behaviour is
+ # specified in the appropriate place
+ before :each do
+ @numbers = [
+ 20, # Integer
+ 99999999**99, # Bignum
+ ]
+ end
+
+ it "returns 1" do
+ @numbers.each {|number| number.denominator.should == 1}
+ end
+
+ it "works with Numeric subclasses" do
+ rational = mock_numeric('rational')
+ rational.should_receive(:denominator).and_return(:denominator)
+ numeric = mock_numeric('numeric')
+ numeric.should_receive(:to_r).and_return(rational)
+ numeric.denominator.should == :denominator
+ end
+end
diff --git a/spec/ruby/core/numeric/div_spec.rb b/spec/ruby/core/numeric/div_spec.rb
new file mode 100644
index 0000000000..0017311487
--- /dev/null
+++ b/spec/ruby/core/numeric/div_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#div" do
+ before :each do
+ @obj = NumericSpecs::Subclass.new
+ end
+
+ it "calls self#/ with other, then returns the #floor'ed result" do
+ result = mock("Numeric#div result")
+ result.should_receive(:floor).and_return(12)
+ @obj.should_receive(:/).with(10).and_return(result)
+
+ @obj.div(10).should == 12
+ end
+
+ it "raises ZeroDivisionError for 0" do
+ lambda { @obj.div(0) }.should raise_error(ZeroDivisionError)
+ lambda { @obj.div(0.0) }.should raise_error(ZeroDivisionError)
+ lambda { @obj.div(Complex(0,0)) }.should raise_error(ZeroDivisionError)
+ end
+end
diff --git a/spec/ruby/core/numeric/divmod_spec.rb b/spec/ruby/core/numeric/divmod_spec.rb
new file mode 100644
index 0000000000..8d5259bbcd
--- /dev/null
+++ b/spec/ruby/core/numeric/divmod_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#divmod" do
+ before :each do
+ @obj = NumericSpecs::Subclass.new
+ end
+
+ it "returns [quotient, modulus], with quotient being obtained as in Numeric#div then #floor and modulus being obtained by calling self#- with quotient * other" do
+ @obj.should_receive(:/).twice.with(10).and_return(13 - TOLERANCE, 13 - TOLERANCE)
+ @obj.should_receive(:-).with(120).and_return(3)
+
+ @obj.divmod(10).should == [12, 3]
+ end
+end
diff --git a/spec/ruby/core/numeric/eql_spec.rb b/spec/ruby/core/numeric/eql_spec.rb
new file mode 100644
index 0000000000..b33e00e51f
--- /dev/null
+++ b/spec/ruby/core/numeric/eql_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#eql?" do
+ before :each do
+ @obj = NumericSpecs::Subclass.new
+ end
+
+ it "returns false if self's and other's types don't match" do
+ @obj.should_not eql(1)
+ @obj.should_not eql(-1.5)
+ @obj.should_not eql(bignum_value)
+ @obj.should_not eql(:sym)
+ end
+
+ it "returns the result of calling self#== with other when self's and other's types match" do
+ other = NumericSpecs::Subclass.new
+ @obj.should_receive(:==).with(other).and_return("result", nil)
+ @obj.should eql(other)
+ @obj.should_not eql(other)
+ end
+end
diff --git a/spec/ruby/core/numeric/fdiv_spec.rb b/spec/ruby/core/numeric/fdiv_spec.rb
new file mode 100644
index 0000000000..907e5d343c
--- /dev/null
+++ b/spec/ruby/core/numeric/fdiv_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require_relative 'shared/quo'
+
+describe "Numeric#fdiv" do
+ it "coerces self with #to_f" do
+ numeric = mock_numeric('numeric')
+ numeric.should_receive(:to_f).and_return(3.0)
+ numeric.fdiv(0.5).should == 6.0
+ end
+
+ it "coerces other with #to_f" do
+ numeric = mock_numeric('numeric')
+ numeric.should_receive(:to_f).and_return(3.0)
+ 6.fdiv(numeric).should == 2.0
+ end
+
+ it "performs floating-point division" do
+ 3.fdiv(2).should == 1.5
+ end
+
+ it "returns a Float" do
+ bignum_value.fdiv(Float::MAX).should be_an_instance_of(Float)
+ end
+
+ it "returns Infinity if other is 0" do
+ 8121.92821.fdiv(0).infinite?.should == 1
+ end
+
+ it "returns NaN if other is NaN" do
+ 3334.fdiv(nan_value).nan?.should be_true
+ end
+end
diff --git a/spec/ruby/core/numeric/finite_spec.rb b/spec/ruby/core/numeric/finite_spec.rb
new file mode 100644
index 0000000000..a4df23602b
--- /dev/null
+++ b/spec/ruby/core/numeric/finite_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.4" do
+ describe "Numeric#finite?" do
+ it "returns true by default" do
+ o = mock_numeric("finite")
+ o.finite?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/core/numeric/fixtures/classes.rb b/spec/ruby/core/numeric/fixtures/classes.rb
new file mode 100644
index 0000000000..1505584889
--- /dev/null
+++ b/spec/ruby/core/numeric/fixtures/classes.rb
@@ -0,0 +1,17 @@
+module NumericSpecs
+ class Comparison < Numeric
+ # This method is used because we cannot define
+ # singleton methods on subclasses of Numeric,
+ # which is needed for a.should_receive to work.
+ def <=>(other)
+ ScratchPad.record :numeric_comparison
+ 1
+ end
+ end
+
+ class Subclass < Numeric
+ # Allow methods to be mocked
+ def singleton_method_added(val)
+ end
+ end
+end
diff --git a/spec/ruby/core/numeric/floor_spec.rb b/spec/ruby/core/numeric/floor_spec.rb
new file mode 100644
index 0000000000..80a4868e4d
--- /dev/null
+++ b/spec/ruby/core/numeric/floor_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#floor" do
+ before :each do
+ @obj = NumericSpecs::Subclass.new
+ end
+
+ it "converts self to a Float (using #to_f) and returns the #floor'ed result" do
+ @obj.should_receive(:to_f).and_return(2 - TOLERANCE, TOLERANCE - 2)
+ @obj.floor.should == 1
+ @obj.floor.should == -2
+ end
+end
diff --git a/spec/ruby/core/numeric/i_spec.rb b/spec/ruby/core/numeric/i_spec.rb
new file mode 100644
index 0000000000..621ecc09ec
--- /dev/null
+++ b/spec/ruby/core/numeric/i_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "Numeric#i" do
+ it "returns a Complex object" do
+ 34.i.should be_an_instance_of(Complex)
+ end
+
+ it "sets the real part to 0" do
+ 7342.i.real.should == 0
+ end
+
+ it "sets the imaginary part to self" do
+ 62.81.i.imag.should == 62.81
+ end
+end
diff --git a/spec/ruby/core/numeric/imag_spec.rb b/spec/ruby/core/numeric/imag_spec.rb
new file mode 100644
index 0000000000..b9e343cee9
--- /dev/null
+++ b/spec/ruby/core/numeric/imag_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/imag'
+
+describe "Numeric#imag" do
+ it_behaves_like :numeric_imag, :imag
+end
diff --git a/spec/ruby/core/numeric/imaginary_spec.rb b/spec/ruby/core/numeric/imaginary_spec.rb
new file mode 100644
index 0000000000..ec708cb505
--- /dev/null
+++ b/spec/ruby/core/numeric/imaginary_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/imag'
+
+describe "Numeric#imaginary" do
+ it_behaves_like :numeric_imag, :imaginary
+end
diff --git a/spec/ruby/core/numeric/infinite_spec.rb b/spec/ruby/core/numeric/infinite_spec.rb
new file mode 100644
index 0000000000..7ed2f6d125
--- /dev/null
+++ b/spec/ruby/core/numeric/infinite_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.4" do
+ describe "Numeric#infinite?" do
+ it "returns nil by default" do
+ o = mock_numeric("infinite")
+ o.infinite?.should == nil
+ end
+ end
+end
diff --git a/spec/ruby/core/numeric/integer_spec.rb b/spec/ruby/core/numeric/integer_spec.rb
new file mode 100644
index 0000000000..5d4bf00360
--- /dev/null
+++ b/spec/ruby/core/numeric/integer_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#integer?" do
+ it "returns false" do
+ NumericSpecs::Subclass.new.integer?.should == false
+ end
+end
diff --git a/spec/ruby/core/numeric/magnitude_spec.rb b/spec/ruby/core/numeric/magnitude_spec.rb
new file mode 100644
index 0000000000..7a3290b036
--- /dev/null
+++ b/spec/ruby/core/numeric/magnitude_spec.rb
@@ -0,0 +1,5 @@
+require_relative 'shared/abs'
+
+describe "Numeric#magnitude" do
+ it_behaves_like :numeric_abs, :magnitude
+end
diff --git a/spec/ruby/core/numeric/modulo_spec.rb b/spec/ruby/core/numeric/modulo_spec.rb
new file mode 100644
index 0000000000..e3dc7e56f3
--- /dev/null
+++ b/spec/ruby/core/numeric/modulo_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe :numeric_modulo_19, shared: true do
+ it "returns self - other * self.div(other)" do
+ s = mock_numeric('self')
+ o = mock_numeric('other')
+ n3 = mock_numeric('n3')
+ n4 = mock_numeric('n4')
+ n5 = mock_numeric('n5')
+ s.should_receive(:div).with(o).and_return(n3)
+ o.should_receive(:*).with(n3).and_return(n4)
+ s.should_receive(:-).with(n4).and_return(n5)
+ s.send(@method, o).should == n5
+ end
+end
+
+describe "Numeric#modulo" do
+ it_behaves_like :numeric_modulo_19, :modulo
+end
+
+describe "Numeric#%" do
+ it_behaves_like :numeric_modulo_19, :%
+end
diff --git a/spec/ruby/core/numeric/negative_spec.rb b/spec/ruby/core/numeric/negative_spec.rb
new file mode 100644
index 0000000000..da464a9094
--- /dev/null
+++ b/spec/ruby/core/numeric/negative_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#negative?" do
+ describe "on positive numbers" do
+ it "returns false" do
+ 1.negative?.should be_false
+ 0.1.negative?.should be_false
+ end
+ end
+
+ describe "on zero" do
+ it "returns false" do
+ 0.negative?.should be_false
+ 0.0.negative?.should be_false
+ end
+ end
+
+ describe "on negative numbers" do
+ it "returns true" do
+ -1.negative?.should be_true
+ -0.1.negative?.should be_true
+ end
+ end
+end
+
+describe "Numeric#negative?" do
+ before(:each) do
+ @obj = NumericSpecs::Subclass.new
+ end
+
+ it "returns true if self is less than 0" do
+ @obj.should_receive(:<).with(0).and_return(true)
+ @obj.negative?.should == true
+ end
+
+ it "returns false if self is greater than 0" do
+ @obj.should_receive(:<).with(0).and_return(false)
+ @obj.negative?.should == false
+ end
+end
diff --git a/spec/ruby/core/numeric/nonzero_spec.rb b/spec/ruby/core/numeric/nonzero_spec.rb
new file mode 100644
index 0000000000..464ed4f4f8
--- /dev/null
+++ b/spec/ruby/core/numeric/nonzero_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#nonzero?" do
+ before :each do
+ @obj = NumericSpecs::Subclass.new
+ end
+
+ it "returns self if self#zero? is false" do
+ @obj.should_receive(:zero?).and_return(false)
+ @obj.nonzero?.should == @obj
+ end
+
+ it "returns nil if self#zero? is true" do
+ @obj.should_receive(:zero?).and_return(true)
+ @obj.nonzero?.should == nil
+ end
+end
diff --git a/spec/ruby/core/numeric/numerator_spec.rb b/spec/ruby/core/numeric/numerator_spec.rb
new file mode 100644
index 0000000000..668df8b797
--- /dev/null
+++ b/spec/ruby/core/numeric/numerator_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+
+describe "Numeric#numerator" do
+ before :all do
+ @numbers = [
+ 0,
+ 29871,
+ 99999999999999**99,
+ -72628191273,
+ 29282.2827,
+ -2927.00091,
+ 0.0,
+ 12.0,
+ Float::MAX,
+ ]
+ end
+
+ # This isn't entirely true, as NaN.numerator works, whereas
+ # Rational(NaN) raises an exception, but we test this in Float#numerator
+ it "converts self to a Rational object then returns its numerator" do
+ @numbers.each do |number|
+ number.numerator.should == Rational(number).numerator
+ end
+ end
+
+ it "works with Numeric subclasses" do
+ rational = mock_numeric('rational')
+ rational.should_receive(:numerator).and_return(:numerator)
+ numeric = mock_numeric('numeric')
+ numeric.should_receive(:to_r).and_return(rational)
+ numeric.numerator.should == :numerator
+ end
+end
diff --git a/spec/ruby/core/numeric/numeric_spec.rb b/spec/ruby/core/numeric/numeric_spec.rb
new file mode 100644
index 0000000000..2bcf2a1175
--- /dev/null
+++ b/spec/ruby/core/numeric/numeric_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Numeric" do
+ it "includes Comparable" do
+ Numeric.include?(Comparable).should == true
+ end
+end
diff --git a/spec/ruby/core/numeric/phase_spec.rb b/spec/ruby/core/numeric/phase_spec.rb
new file mode 100644
index 0000000000..bc1995303f
--- /dev/null
+++ b/spec/ruby/core/numeric/phase_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/arg'
+
+describe "Numeric#phase" do
+ it_behaves_like :numeric_arg, :phase
+end
diff --git a/spec/ruby/core/numeric/polar_spec.rb b/spec/ruby/core/numeric/polar_spec.rb
new file mode 100644
index 0000000000..b594e408b2
--- /dev/null
+++ b/spec/ruby/core/numeric/polar_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+
+describe "Numeric#polar" do
+ before :each do
+ @pos_numbers = [
+ 1,
+ 3898172610**9,
+ 987.18273,
+ Float::MAX,
+ Rational(13,7),
+ infinity_value,
+ ]
+ @neg_numbers = @pos_numbers.map {|n| -n}
+ @numbers = @pos_numbers + @neg_numbers
+ @numbers.push(0, 0.0)
+ end
+
+ it "returns a two-element Array" do
+ @numbers.each do |number|
+ number.polar.should be_an_instance_of(Array)
+ number.polar.size.should == 2
+ end
+ end
+
+ it "sets the first value to the absolute value of self" do
+ @numbers.each do |number|
+ number.polar.first.should == number.abs
+ end
+ end
+
+ it "sets the last value to 0 if self is positive" do
+ (@numbers - @neg_numbers).each do |number|
+ number.should >= 0
+ number.polar.last.should == 0
+ end
+ end
+
+ it "sets the last value to Pi if self is negative" do
+ @neg_numbers.each do |number|
+ number.should < 0
+ number.polar.last.should == Math::PI
+ end
+ end
+
+ it "returns [NaN, NaN] if self is NaN" do
+ nan_value.polar.size.should == 2
+ nan_value.polar.first.nan?.should be_true
+ nan_value.polar.last.nan?.should be_true
+ end
+end
diff --git a/spec/ruby/core/numeric/positive_spec.rb b/spec/ruby/core/numeric/positive_spec.rb
new file mode 100644
index 0000000000..8f98fbfa26
--- /dev/null
+++ b/spec/ruby/core/numeric/positive_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#positive?" do
+ describe "on positive numbers" do
+ it "returns true" do
+ 1.positive?.should be_true
+ 0.1.positive?.should be_true
+ end
+ end
+
+ describe "on zero" do
+ it "returns false" do
+ 0.positive?.should be_false
+ 0.0.positive?.should be_false
+ end
+ end
+
+ describe "on negative numbers" do
+ it "returns false" do
+ -1.positive?.should be_false
+ -0.1.positive?.should be_false
+ end
+ end
+end
+
+describe "Numeric#positive?" do
+ before(:each) do
+ @obj = NumericSpecs::Subclass.new
+ end
+
+ it "returns true if self is greater than 0" do
+ @obj.should_receive(:>).with(0).and_return(true)
+ @obj.positive?.should == true
+ end
+
+ it "returns false if self is less than 0" do
+ @obj.should_receive(:>).with(0).and_return(false)
+ @obj.positive?.should == false
+ end
+end
diff --git a/spec/ruby/core/numeric/quo_spec.rb b/spec/ruby/core/numeric/quo_spec.rb
new file mode 100644
index 0000000000..bf05cf678d
--- /dev/null
+++ b/spec/ruby/core/numeric/quo_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/quo'
+
+describe "Numeric#quo" do
+ it "returns the result of self divided by the given Integer as a Rational" do
+ 5.quo(2).should eql(Rational(5,2))
+ end
+
+ it "returns the result of self divided by the given Float as a Float" do
+ 2.quo(2.5).should eql(0.8)
+ end
+
+ it "returns the result of self divided by the given Bignum as a Float" do
+ 45.quo(bignum_value).should be_close(1.04773789668636e-08, TOLERANCE)
+ end
+
+ it "raises a ZeroDivisionError when the given Integer is 0" do
+ lambda { 0.quo(0) }.should raise_error(ZeroDivisionError)
+ lambda { 10.quo(0) }.should raise_error(ZeroDivisionError)
+ lambda { -10.quo(0) }.should raise_error(ZeroDivisionError)
+ lambda { bignum_value.quo(0) }.should raise_error(ZeroDivisionError)
+ lambda { -bignum_value.quo(0) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "calls #to_r to convert the object to a Rational" do
+ obj = NumericSpecs::Subclass.new
+ obj.should_receive(:to_r).and_return(Rational(1))
+
+ obj.quo(19).should == Rational(1, 19)
+ end
+
+ it "raises a TypeError of #to_r does not return a Rational" do
+ obj = NumericSpecs::Subclass.new
+ obj.should_receive(:to_r).and_return(1)
+
+ lambda { obj.quo(19) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when given a non-Integer" do
+ lambda {
+ (obj = mock('x')).should_not_receive(:to_int)
+ 13.quo(obj)
+ }.should raise_error(TypeError)
+ lambda { 13.quo("10") }.should raise_error(TypeError)
+ lambda { 13.quo(:symbol) }.should raise_error(TypeError)
+ end
+
+ it "returns the result of calling self#/ with other" do
+ obj = NumericSpecs::Subclass.new
+ obj.should_receive(:to_r).and_return(19.quo(20))
+
+ obj.quo(19).should == 1.quo(20)
+ end
+end
diff --git a/spec/ruby/core/numeric/real_spec.rb b/spec/ruby/core/numeric/real_spec.rb
new file mode 100644
index 0000000000..8c318dce0f
--- /dev/null
+++ b/spec/ruby/core/numeric/real_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#real" do
+ before :each do
+ @numbers = [
+ 20, # Integer
+ 398.72, # Float
+ Rational(3, 4), # Rational
+ bignum_value, # Bignum
+ infinity_value,
+ nan_value
+ ].map{ |n| [n, -n] }.flatten
+ end
+
+ it "returns self" do
+ @numbers.each do |number|
+ if number.to_f.nan?
+ number.real.nan?.should be_true
+ else
+ number.real.should == number
+ end
+ end
+ end
+
+ it "raises an ArgumentError if given any arguments" do
+ @numbers.each do |number|
+ lambda { number.real(number) }.should raise_error(ArgumentError)
+ end
+ end
+end
+
+describe "Numeric#real?" do
+ it "returns true" do
+ NumericSpecs::Subclass.new.real?.should == true
+ end
+end
diff --git a/spec/ruby/core/numeric/rect_spec.rb b/spec/ruby/core/numeric/rect_spec.rb
new file mode 100644
index 0000000000..79a144c5a4
--- /dev/null
+++ b/spec/ruby/core/numeric/rect_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/rect'
+
+describe "Numeric#rect" do
+ it_behaves_like :numeric_rect, :rect
+end
diff --git a/spec/ruby/core/numeric/rectangular_spec.rb b/spec/ruby/core/numeric/rectangular_spec.rb
new file mode 100644
index 0000000000..2c68985a16
--- /dev/null
+++ b/spec/ruby/core/numeric/rectangular_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/rect'
+
+describe "Numeric#rectangular" do
+ it_behaves_like :numeric_rect, :rectangular
+end
diff --git a/spec/ruby/core/numeric/remainder_spec.rb b/spec/ruby/core/numeric/remainder_spec.rb
new file mode 100644
index 0000000000..1e2f5f3a96
--- /dev/null
+++ b/spec/ruby/core/numeric/remainder_spec.rb
@@ -0,0 +1,67 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#remainder" do
+ before :each do
+ @obj = NumericSpecs::Subclass.new
+ @result = mock("Numeric#% result")
+ @other = mock("Passed Object")
+ end
+
+ it "returns the result of calling self#% with other if self is 0" do
+ @obj.should_receive(:%).with(@other).and_return(@result)
+ @result.should_receive(:==).with(0).and_return(true)
+
+ @obj.remainder(@other).should equal(@result)
+ end
+
+ it "returns the result of calling self#% with other if self and other are greater than 0" do
+ @obj.should_receive(:%).with(@other).and_return(@result)
+ @result.should_receive(:==).with(0).and_return(false)
+
+ @obj.should_receive(:<).with(0).and_return(false)
+
+ @obj.should_receive(:>).with(0).and_return(true)
+ @other.should_receive(:<).with(0).and_return(false)
+
+ @obj.remainder(@other).should equal(@result)
+ end
+
+ it "returns the result of calling self#% with other if self and other are less than 0" do
+ @obj.should_receive(:%).with(@other).and_return(@result)
+ @result.should_receive(:==).with(0).and_return(false)
+
+ @obj.should_receive(:<).with(0).and_return(true)
+ @other.should_receive(:>).with(0).and_return(false)
+
+ @obj.should_receive(:>).with(0).and_return(false)
+
+ @obj.remainder(@other).should equal(@result)
+ end
+
+ it "returns the result of calling self#% with other - other if self is greater than 0 and other is less than 0" do
+ @obj.should_receive(:%).with(@other).and_return(@result)
+ @result.should_receive(:==).with(0).and_return(false)
+
+ @obj.should_receive(:<).with(0).and_return(false)
+
+ @obj.should_receive(:>).with(0).and_return(true)
+ @other.should_receive(:<).with(0).and_return(true)
+
+ @result.should_receive(:-).with(@other).and_return(:result)
+
+ @obj.remainder(@other).should == :result
+ end
+
+ it "returns the result of calling self#% with other - other if self is less than 0 and other is greater than 0" do
+ @obj.should_receive(:%).with(@other).and_return(@result)
+ @result.should_receive(:==).with(0).and_return(false)
+
+ @obj.should_receive(:<).with(0).and_return(true)
+ @other.should_receive(:>).with(0).and_return(true)
+
+ @result.should_receive(:-).with(@other).and_return(:result)
+
+ @obj.remainder(@other).should == :result
+ end
+end
diff --git a/spec/ruby/core/numeric/round_spec.rb b/spec/ruby/core/numeric/round_spec.rb
new file mode 100644
index 0000000000..47c5837693
--- /dev/null
+++ b/spec/ruby/core/numeric/round_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#round" do
+ before :each do
+ @obj = NumericSpecs::Subclass.new
+ end
+
+ it "converts self to a Float (using #to_f) and returns the #round'ed result" do
+ @obj.should_receive(:to_f).and_return(2 - TOLERANCE, TOLERANCE - 2)
+ @obj.round.should == 2
+ @obj.round.should == -2
+ end
+end
diff --git a/spec/ruby/core/numeric/shared/abs.rb b/spec/ruby/core/numeric/shared/abs.rb
new file mode 100644
index 0000000000..c3dadccfd6
--- /dev/null
+++ b/spec/ruby/core/numeric/shared/abs.rb
@@ -0,0 +1,19 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :numeric_abs, shared: true do
+ before :each do
+ @obj = NumericSpecs::Subclass.new
+ end
+
+ it "returns self when self is greater than 0" do
+ @obj.should_receive(:<).with(0).and_return(false)
+ @obj.send(@method).should == @obj
+ end
+
+ it "returns self\#@- when self is less than 0" do
+ @obj.should_receive(:<).with(0).and_return(true)
+ @obj.should_receive(:-@).and_return(:absolute_value)
+ @obj.send(@method).should == :absolute_value
+ end
+end
diff --git a/spec/ruby/core/numeric/shared/arg.rb b/spec/ruby/core/numeric/shared/arg.rb
new file mode 100644
index 0000000000..c8e7ad8333
--- /dev/null
+++ b/spec/ruby/core/numeric/shared/arg.rb
@@ -0,0 +1,38 @@
+require_relative '../../../spec_helper'
+
+describe :numeric_arg, shared: true do
+ before :each do
+ @numbers = [
+ 20,
+ Rational(3, 4),
+ bignum_value,
+ infinity_value
+ ]
+ end
+
+ it "returns 0 if positive" do
+ @numbers.each do |number|
+ number.send(@method).should == 0
+ end
+ end
+
+ it "returns Pi if negative" do
+ @numbers.each do |number|
+ (0-number).send(@method).should == Math::PI
+ end
+ end
+
+ describe "with a Numeric subclass" do
+ it "returns 0 if self#<(0) returns false" do
+ numeric = mock_numeric('positive')
+ numeric.should_receive(:<).with(0).and_return(false)
+ numeric.send(@method).should == 0
+ end
+
+ it "returns Pi if self#<(0) returns true" do
+ numeric = mock_numeric('positive')
+ numeric.should_receive(:<).with(0).and_return(true)
+ numeric.send(@method).should == Math::PI
+ end
+ end
+end
diff --git a/spec/ruby/core/numeric/shared/conj.rb b/spec/ruby/core/numeric/shared/conj.rb
new file mode 100644
index 0000000000..6d5197ecab
--- /dev/null
+++ b/spec/ruby/core/numeric/shared/conj.rb
@@ -0,0 +1,20 @@
+require_relative '../../../spec_helper'
+
+describe :numeric_conj, shared: true do
+ before :each do
+ @numbers = [
+ 20, # Integer
+ 398.72, # Float
+ Rational(3, 4), # Rational
+ bignum_value,
+ infinity_value,
+ nan_value
+ ]
+ end
+
+ it "returns self" do
+ @numbers.each do |number|
+ number.send(@method).should equal(number)
+ end
+ end
+end
diff --git a/spec/ruby/core/numeric/shared/imag.rb b/spec/ruby/core/numeric/shared/imag.rb
new file mode 100644
index 0000000000..11daf0af55
--- /dev/null
+++ b/spec/ruby/core/numeric/shared/imag.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+
+describe :numeric_imag, shared: true do
+ before :each do
+ @numbers = [
+ 20, # Integer
+ 398.72, # Float
+ Rational(3, 4), # Rational
+ bignum_value, # Bignum
+ infinity_value,
+ nan_value
+ ].map{|n| [n,-n]}.flatten
+ end
+
+ it "returns 0" do
+ @numbers.each do |number|
+ number.send(@method).should == 0
+ end
+ end
+
+ it "raises an ArgumentError if given any arguments" do
+ @numbers.each do |number|
+ lambda { number.send(@method, number) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/numeric/shared/quo.rb b/spec/ruby/core/numeric/shared/quo.rb
new file mode 100644
index 0000000000..2392636fe7
--- /dev/null
+++ b/spec/ruby/core/numeric/shared/quo.rb
@@ -0,0 +1,7 @@
+describe :numeric_quo_18, shared: true do
+ it "returns the result of calling self#/ with other" do
+ obj = mock_numeric('numeric')
+ obj.should_receive(:/).with(19).and_return(:result)
+ obj.send(@method, 19).should == :result
+ end
+end
diff --git a/spec/ruby/core/numeric/shared/rect.rb b/spec/ruby/core/numeric/shared/rect.rb
new file mode 100644
index 0000000000..047d6556e1
--- /dev/null
+++ b/spec/ruby/core/numeric/shared/rect.rb
@@ -0,0 +1,48 @@
+require_relative '../../../spec_helper'
+
+describe :numeric_rect, shared: true do
+ before :each do
+ @numbers = [
+ 20, # Integer
+ 398.72, # Float
+ Rational(3, 4), # Rational
+ 99999999**99, # Bignum
+ infinity_value,
+ nan_value
+ ]
+ end
+
+ it "returns an Array" do
+ @numbers.each do |number|
+ number.send(@method).should be_an_instance_of(Array)
+ end
+ end
+
+ it "returns a two-element Array" do
+ @numbers.each do |number|
+ number.send(@method).size.should == 2
+ end
+ end
+
+ it "returns self as the first element" do
+ @numbers.each do |number|
+ if Float === number and number.nan?
+ number.send(@method).first.nan?.should be_true
+ else
+ number.send(@method).first.should == number
+ end
+ end
+ end
+
+ it "returns 0 as the last element" do
+ @numbers.each do |number|
+ number.send(@method).last.should == 0
+ end
+ end
+
+ it "raises an ArgumentError if given any arguments" do
+ @numbers.each do |number|
+ lambda { number.send(@method, number) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/numeric/shared/step.rb b/spec/ruby/core/numeric/shared/step.rb
new file mode 100644
index 0000000000..0fb2336bf5
--- /dev/null
+++ b/spec/ruby/core/numeric/shared/step.rb
@@ -0,0 +1,430 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+# Describes Numeric#step shared specs between different argument styles.
+# To be able to do it, the @step_args var must contain a Proc that transforms
+# the step call arguments passed as positional arguments to the style of
+# arguments pretended to test.
+describe :numeric_step, :shared => true do
+ before :each do
+ ScratchPad.record []
+ @prc = lambda { |x| ScratchPad << x }
+ end
+
+ it "defaults to step = 1" do
+ 1.send(@method, *@step_args.call(5), &@prc)
+ ScratchPad.recorded.should eql [1, 2, 3, 4, 5]
+ end
+
+ describe "when self, stop and step are Fixnums" do
+ it "yields only Fixnums" do
+ 1.send(@method, *@step_args.call(5, 1)) { |x| x.should be_an_instance_of(Fixnum) }
+ end
+
+ describe "with a positive step" do
+ it "yields while increasing self by step until stop is reached" do
+ 1.send(@method, *@step_args.call(5, 1), &@prc)
+ ScratchPad.recorded.should eql [1, 2, 3, 4, 5]
+ end
+
+ it "yields once when self equals stop" do
+ 1.send(@method, *@step_args.call(1, 1), &@prc)
+ ScratchPad.recorded.should eql [1]
+ end
+
+ it "does not yield when self is greater than stop" do
+ 2.send(@method, *@step_args.call(1, 1), &@prc)
+ ScratchPad.recorded.should eql []
+ end
+ end
+
+ describe "with a negative step" do
+ it "yields while decreasing self by step until stop is reached" do
+ 5.send(@method, *@step_args.call(1, -1), &@prc)
+ ScratchPad.recorded.should eql [5, 4, 3, 2, 1]
+ end
+
+ it "yields once when self equals stop" do
+ 5.send(@method, *@step_args.call(5, -1), &@prc)
+ ScratchPad.recorded.should eql [5]
+ end
+
+ it "does not yield when self is less than stop" do
+ 1.send(@method, *@step_args.call(5, -1), &@prc)
+ ScratchPad.recorded.should == []
+ end
+ end
+ end
+
+ describe "when at least one of self, stop or step is a Float" do
+ it "yields Floats even if only self is a Float" do
+ 1.5.send(@method, *@step_args.call(5, 1)) { |x| x.should be_an_instance_of(Float) }
+ end
+
+ it "yields Floats even if only stop is a Float" do
+ 1.send(@method, *@step_args.call(5.0, 1)) { |x| x.should be_an_instance_of(Float) }
+ end
+
+ it "yields Floats even if only step is a Float" do
+ 1.send(@method, *@step_args.call(5, 1.0)) { |x| x.should be_an_instance_of(Float) }
+ end
+
+ describe "with a positive step" do
+ it "yields while increasing self by step while < stop" do
+ 1.5.send(@method, *@step_args.call(5, 1), &@prc)
+ ScratchPad.recorded.should eql [1.5, 2.5, 3.5, 4.5]
+ end
+
+ it "yields once when self equals stop" do
+ 1.5.send(@method, *@step_args.call(1.5, 1), &@prc)
+ ScratchPad.recorded.should eql [1.5]
+ end
+
+ it "does not yield when self is greater than stop" do
+ 2.5.send(@method, *@step_args.call(1.5, 1), &@prc)
+ ScratchPad.recorded.should == []
+ end
+
+ it "is careful about not yielding a value greater than limit" do
+ # As 9*1.3+1.0 == 12.700000000000001 > 12.7, we test:
+ 1.0.send(@method, *@step_args.call(12.7, 1.3), &@prc)
+ ScratchPad.recorded.should eql [1.0, 2.3, 3.6, 4.9, 6.2, 7.5, 8.8, 10.1, 11.4, 12.7]
+ end
+ end
+
+ describe "with a negative step" do
+ it "yields while decreasing self by step while self > stop" do
+ 5.send(@method, *@step_args.call(1.5, -1), &@prc)
+ ScratchPad.recorded.should eql [5.0, 4.0, 3.0, 2.0]
+ end
+
+ it "yields once when self equals stop" do
+ 1.5.send(@method, *@step_args.call(1.5, -1), &@prc)
+ ScratchPad.recorded.should eql [1.5]
+ end
+
+ it "does not yield when self is less than stop" do
+ 1.send(@method, *@step_args.call(5, -1.5), &@prc)
+ ScratchPad.recorded.should == []
+ end
+
+ it "is careful about not yielding a value smaller than limit" do
+ # As -9*1.3-1.0 == -12.700000000000001 < -12.7, we test:
+ -1.0.send(@method, *@step_args.call(-12.7, -1.3), &@prc)
+ ScratchPad.recorded.should eql [-1.0, -2.3, -3.6, -4.9, -6.2, -7.5, -8.8, -10.1, -11.4, -12.7]
+ end
+ end
+
+ describe "with a positive Infinity step" do
+ it "yields once if self < stop" do
+ 42.send(@method, *@step_args.call(100, infinity_value), &@prc)
+ ScratchPad.recorded.should eql [42.0]
+ end
+
+ it "yields once when stop is Infinity" do
+ 42.send(@method, *@step_args.call(infinity_value, infinity_value), &@prc)
+ ScratchPad.recorded.should eql [42.0]
+ end
+
+ it "yields once when self equals stop" do
+ 42.send(@method, *@step_args.call(42, infinity_value), &@prc)
+ ScratchPad.recorded.should eql [42.0]
+ end
+
+ it "yields once when self and stop are Infinity" do
+ (infinity_value).send(@method, *@step_args.call(infinity_value, infinity_value), &@prc)
+ ScratchPad.recorded.should == [infinity_value]
+ end
+
+ it "does not yield when self > stop" do
+ 100.send(@method, *@step_args.call(42, infinity_value), &@prc)
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not yield when stop is -Infinity" do
+ 42.send(@method, *@step_args.call(-infinity_value, infinity_value), &@prc)
+ ScratchPad.recorded.should == []
+ end
+ end
+
+ describe "with a negative Infinity step" do
+ it "yields once if self > stop" do
+ 42.send(@method, *@step_args.call(6, -infinity_value), &@prc)
+ ScratchPad.recorded.should eql [42.0]
+ end
+
+ it "yields once if stop is -Infinity" do
+ 42.send(@method, *@step_args.call(-infinity_value, -infinity_value), &@prc)
+ ScratchPad.recorded.should eql [42.0]
+ end
+
+ it "yields once when self equals stop" do
+ 42.send(@method, *@step_args.call(42, -infinity_value), &@prc)
+ ScratchPad.recorded.should eql [42.0]
+ end
+
+ it "yields once when self and stop are Infinity" do
+ (infinity_value).send(@method, *@step_args.call(infinity_value, -infinity_value), &@prc)
+ ScratchPad.recorded.should == [infinity_value]
+ end
+
+ it "does not yield when self > stop" do
+ 42.send(@method, *@step_args.call(100, -infinity_value), &@prc)
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not yield when stop is Infinity" do
+ 42.send(@method, *@step_args.call(infinity_value, -infinity_value), &@prc)
+ ScratchPad.recorded.should == []
+ end
+ end
+
+ describe "with a Infinity stop and a positive step" do
+ it "does not yield when self is infinity" do
+ (infinity_value).send(@method, *@step_args.call(infinity_value, 1), &@prc)
+ ScratchPad.recorded.should == []
+ end
+ end
+
+ describe "with a Infinity stop and a negative step" do
+ it "does not yield when self is negative infinity" do
+ (-infinity_value).send(@method, *@step_args.call(infinity_value, -1), &@prc)
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not yield when self is positive infinity" do
+ infinity_value.send(@method, *@step_args.call(infinity_value, -1), &@prc)
+ ScratchPad.recorded.should == []
+ end
+ end
+
+ describe "with a negative Infinity stop and a positive step" do
+ it "does not yield when self is negative infinity" do
+ (-infinity_value).send(@method, *@step_args.call(-infinity_value, 1), &@prc)
+ ScratchPad.recorded.should == []
+ end
+ end
+
+ describe "with a negative Infinity stop and a negative step" do
+ it "does not yield when self is negative infinity" do
+ (-infinity_value).send(@method, *@step_args.call(-infinity_value, -1), &@prc)
+ ScratchPad.recorded.should == []
+ end
+ end
+
+ end
+
+ describe "when step is a String" do
+ error = nil
+ ruby_version_is ""..."2.4" do
+ error = ArgumentError
+ end
+ ruby_version_is "2.4"..."2.5" do
+ error = TypeError
+ end
+ ruby_version_is "2.5" do
+ error = ArgumentError
+ end
+
+ describe "with self and stop as Fixnums" do
+ it "raises an #{error} when step is a numeric representation" do
+ lambda { 1.send(@method, *@step_args.call(5, "1")) {} }.should raise_error(error)
+ lambda { 1.send(@method, *@step_args.call(5, "0.1")) {} }.should raise_error(error)
+ lambda { 1.send(@method, *@step_args.call(5, "1/3")) {} }.should raise_error(error)
+ end
+ it "raises an #{error} with step as an alphanumeric string" do
+ lambda { 1.send(@method, *@step_args.call(5, "foo")) {} }.should raise_error(error)
+ end
+ end
+
+ describe "with self and stop as Floats" do
+ it "raises an #{error} when step is a numeric representation" do
+ lambda { 1.1.send(@method, *@step_args.call(5.1, "1")) {} }.should raise_error(error)
+ lambda { 1.1.send(@method, *@step_args.call(5.1, "0.1")) {} }.should raise_error(error)
+ lambda { 1.1.send(@method, *@step_args.call(5.1, "1/3")) {} }.should raise_error(error)
+ end
+ it "raises an #{error} with step as an alphanumeric string" do
+ lambda { 1.1.send(@method, *@step_args.call(5.1, "foo")) {} }.should raise_error(error)
+ end
+ end
+ end
+
+ it "does not rescue ArgumentError exceptions" do
+ lambda { 1.send(@method, *@step_args.call(2)) { raise ArgumentError, "" }}.should raise_error(ArgumentError)
+ end
+
+ it "does not rescue TypeError exceptions" do
+ lambda { 1.send(@method, *@step_args.call(2)) { raise TypeError, "" } }.should raise_error(TypeError)
+ end
+
+ describe "when no block is given" do
+ step_enum_class = Enumerator
+ ruby_version_is "2.6" do
+ step_enum_class = Enumerator::ArithmeticSequence
+ end
+
+ it "returns an #{step_enum_class} when step is 0" do
+ 1.send(@method, *@step_args.call(2, 0)).should be_an_instance_of(step_enum_class)
+ end
+
+ it "returns an #{step_enum_class} when not passed a block and self > stop" do
+ 1.send(@method, *@step_args.call(0, 2)).should be_an_instance_of(step_enum_class)
+ end
+
+ it "returns an #{step_enum_class} when not passed a block and self < stop" do
+ 1.send(@method, *@step_args.call(2, 3)).should be_an_instance_of(step_enum_class)
+ end
+
+ it "returns an #{step_enum_class} that uses the given step" do
+ 0.send(@method, *@step_args.call(5, 2)).to_a.should eql [0, 2, 4]
+ end
+
+ describe "when step is a String" do
+ describe "with self and stop as Fixnums" do
+ it "returns an Enumerator" do
+ 1.send(@method, *@step_args.call(5, "foo")).should be_an_instance_of(Enumerator)
+ end
+ end
+
+ describe "with self and stop as Floats" do
+ it "returns an Enumerator" do
+ 1.1.send(@method, *@step_args.call(5.1, "foo")).should be_an_instance_of(Enumerator)
+ end
+ end
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ describe "when step is a String" do
+ error = nil
+ ruby_version_is ""..."2.4" do
+ error = ArgumentError
+ end
+ ruby_version_is "2.4"..."2.5" do
+ error = TypeError
+ end
+ ruby_version_is "2.5" do
+ error = ArgumentError
+ end
+
+ describe "with self and stop as Fixnums" do
+ it "raises an #{error} when step is a numeric representation" do
+ lambda { 1.send(@method, *@step_args.call(5, "1")).size }.should raise_error(error)
+ lambda { 1.send(@method, *@step_args.call(5, "0.1")).size }.should raise_error(error)
+ lambda { 1.send(@method, *@step_args.call(5, "1/3")).size }.should raise_error(error)
+ end
+ it "raises an #{error} with step as an alphanumeric string" do
+ lambda { 1.send(@method, *@step_args.call(5, "foo")).size }.should raise_error(error)
+ end
+ end
+
+ describe "with self and stop as Floats" do
+ it "raises an #{error} when step is a numeric representation" do
+ lambda { 1.1.send(@method, *@step_args.call(5.1, "1")).size }.should raise_error(error)
+ lambda { 1.1.send(@method, *@step_args.call(5.1, "0.1")).size }.should raise_error(error)
+ lambda { 1.1.send(@method, *@step_args.call(5.1, "1/3")).size }.should raise_error(error)
+ end
+ it "raises an #{error} with step as an alphanumeric string" do
+ lambda { 1.1.send(@method, *@step_args.call(5.1, "foo")).size }.should raise_error(error)
+ end
+ end
+ end
+
+ describe "when self, stop and step are Fixnums and step is positive" do
+ it "returns the difference between self and stop divided by the number of steps" do
+ 5.send(@method, *@step_args.call(10, 11)).size.should == 1
+ 5.send(@method, *@step_args.call(10, 6)).size.should == 1
+ 5.send(@method, *@step_args.call(10, 5)).size.should == 2
+ 5.send(@method, *@step_args.call(10, 4)).size.should == 2
+ 5.send(@method, *@step_args.call(10, 2)).size.should == 3
+ 5.send(@method, *@step_args.call(10, 1)).size.should == 6
+ 5.send(@method, *@step_args.call(10)).size.should == 6
+ 10.send(@method, *@step_args.call(10, 1)).size.should == 1
+ end
+
+ it "returns 0 if value > limit" do
+ 11.send(@method, *@step_args.call(10, 1)).size.should == 0
+ end
+ end
+
+ describe "when self, stop and step are Fixnums and step is negative" do
+ it "returns the difference between self and stop divided by the number of steps" do
+ 10.send(@method, *@step_args.call(5, -11)).size.should == 1
+ 10.send(@method, *@step_args.call(5, -6)).size.should == 1
+ 10.send(@method, *@step_args.call(5, -5)).size.should == 2
+ 10.send(@method, *@step_args.call(5, -4)).size.should == 2
+ 10.send(@method, *@step_args.call(5, -2)).size.should == 3
+ 10.send(@method, *@step_args.call(5, -1)).size.should == 6
+ 10.send(@method, *@step_args.call(10, -1)).size.should == 1
+ end
+
+ it "returns 0 if value < limit" do
+ 10.send(@method, *@step_args.call(11, -1)).size.should == 0
+ end
+ end
+
+ describe "when self, stop or step is a Float" do
+ describe "and step is positive" do
+ it "returns the difference between self and stop divided by the number of steps" do
+ 5.send(@method, *@step_args.call(10, 11.0)).size.should == 1
+ 5.send(@method, *@step_args.call(10, 6.0)).size.should == 1
+ 5.send(@method, *@step_args.call(10, 5.0)).size.should == 2
+ 5.send(@method, *@step_args.call(10, 4.0)).size.should == 2
+ 5.send(@method, *@step_args.call(10, 2.0)).size.should == 3
+ 5.send(@method, *@step_args.call(10, 0.5)).size.should == 11
+ 5.send(@method, *@step_args.call(10, 1.0)).size.should == 6
+ 5.send(@method, *@step_args.call(10.5)).size.should == 6
+ 10.send(@method, *@step_args.call(10, 1.0)).size.should == 1
+ end
+
+ it "returns 0 if value > limit" do
+ 10.send(@method, *@step_args.call(5.5)).size.should == 0
+ 11.send(@method, *@step_args.call(10, 1.0)).size.should == 0
+ 11.send(@method, *@step_args.call(10, 1.5)).size.should == 0
+ 10.send(@method, *@step_args.call(5, infinity_value)).size.should == 0
+ end
+
+ it "returns 1 if step is infinity_value" do
+ 5.send(@method, *@step_args.call(10, infinity_value)).size.should == 1
+ end
+ end
+
+ describe "and step is negative" do
+ it "returns the difference between self and stop divided by the number of steps" do
+ 10.send(@method, *@step_args.call(5, -11.0)).size.should == 1
+ 10.send(@method, *@step_args.call(5, -6.0)).size.should == 1
+ 10.send(@method, *@step_args.call(5, -5.0)).size.should == 2
+ 10.send(@method, *@step_args.call(5, -4.0)).size.should == 2
+ 10.send(@method, *@step_args.call(5, -2.0)).size.should == 3
+ 10.send(@method, *@step_args.call(5, -0.5)).size.should == 11
+ 10.send(@method, *@step_args.call(5, -1.0)).size.should == 6
+ 10.send(@method, *@step_args.call(10, -1.0)).size.should == 1
+ end
+
+ it "returns 0 if value < limit" do
+ 10.send(@method, *@step_args.call(11, -1.0)).size.should == 0
+ 10.send(@method, *@step_args.call(11, -1.5)).size.should == 0
+ 5.send(@method, *@step_args.call(10, -infinity_value)).size.should == 0
+ end
+
+ it "returns 1 if step is infinity_value" do
+ 10.send(@method, *@step_args.call(5, -infinity_value)).size.should == 1
+ end
+ end
+ end
+
+ describe "when stop is not passed" do
+ it "returns infinity_value" do
+ 1.send(@method, *@step_args.call()).size.should == infinity_value
+ end
+ end
+
+ describe "when stop is nil" do
+ it "returns infinity_value" do
+ 1.send(@method, *@step_args.call(nil, 5)).size.should == infinity_value
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/numeric/singleton_method_added_spec.rb b/spec/ruby/core/numeric/singleton_method_added_spec.rb
new file mode 100644
index 0000000000..c39e9d7b32
--- /dev/null
+++ b/spec/ruby/core/numeric/singleton_method_added_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#singleton_method_added" do
+ before :all do
+ class ::NumericSpecs::Subclass
+ # We want restore default Numeric behaviour for this particular test
+ remove_method :singleton_method_added
+ end
+ end
+
+ after :all do
+ class ::NumericSpecs::Subclass
+ # Allow mocking methods again
+ def singleton_method_added(val)
+ end
+ end
+ end
+
+ it "raises a TypeError when trying to define a singleton method on a Numeric" do
+ lambda do
+ a = NumericSpecs::Subclass.new
+ def a.test; end
+ end.should raise_error(TypeError)
+
+ lambda do
+ a = 1
+ def a.test; end
+ end.should raise_error(TypeError)
+
+ lambda do
+ a = 1.5
+ def a.test; end
+ end.should raise_error(TypeError)
+
+ lambda do
+ a = bignum_value
+ def a.test; end
+ end.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/numeric/step_spec.rb b/spec/ruby/core/numeric/step_spec.rb
new file mode 100644
index 0000000000..1eb91aa1e5
--- /dev/null
+++ b/spec/ruby/core/numeric/step_spec.rb
@@ -0,0 +1,182 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/step'
+
+describe "Numeric#step" do
+
+ describe 'with positional args' do
+ it "raises an ArgumentError when step is 0" do
+ lambda { 1.step(5, 0) {} }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when step is 0.0" do
+ lambda { 1.step(2, 0.0) {} }.should raise_error(ArgumentError)
+ end
+
+ before :all do
+ # This lambda definition limits to return the arguments it receives.
+ # It's needed to test numeric_step behaviour with positional arguments.
+ @step_args = ->(*args) { args }
+ end
+
+ it_behaves_like :numeric_step, :step
+
+ describe "when no block is given" do
+ step_enum_class = Enumerator
+ ruby_version_is "2.6" do
+ step_enum_class = Enumerator::ArithmeticSequence
+ end
+
+ it "returns an #{step_enum_class} when step is 0" do
+ 1.step(5, 0).should be_an_instance_of(step_enum_class)
+ end
+
+ it "returns an #{step_enum_class} when step is 0.0" do
+ 1.step(2, 0.0).should be_an_instance_of(step_enum_class)
+ end
+
+ describe "returned #{step_enum_class}" do
+ describe "size" do
+ ruby_version_is ""..."2.6" do
+ it "raises an ArgumentError when step is 0" do
+ enum = 1.step(5, 0)
+ lambda { enum.size }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when step is 0.0" do
+ enum = 1.step(2, 0.0)
+ lambda { enum.size }.should raise_error(ArgumentError)
+ end
+ end
+
+ ruby_version_is "2.6" do
+ it "is infinity when step is 0" do
+ enum = 1.step(5, 0)
+ enum.size.should == Float::INFINITY
+ end
+
+ it "is infinity when step is 0.0" do
+ enum = 1.step(2, 0.0)
+ enum.size.should == Float::INFINITY
+ end
+ end
+ end
+ end
+ end
+
+ end
+
+ describe 'with keyword arguments' do
+ it "doesn't raise an error when step is 0" do
+ lambda { 1.step(to: 5, by: 0) { break } }.should_not raise_error
+ end
+
+ it "doesn't raise an error when step is 0.0" do
+ lambda { 1.step(to: 2, by: 0.0) { break } }.should_not raise_error
+ end
+
+ it "should loop over self when step is 0 or 0.0" do
+ 1.step(to: 2, by: 0.0).take(5).should eql [1.0, 1.0, 1.0, 1.0, 1.0]
+ 1.step(to: 2, by: 0).take(5).should eql [1, 1, 1, 1, 1]
+ 1.1.step(to: 2, by: 0).take(5).should eql [1.1, 1.1, 1.1, 1.1, 1.1]
+ end
+
+ describe "when no block is given" do
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return infinity_value when limit is nil" do
+ 1.step(by: 42).size.should == infinity_value
+ end
+
+ it "should return infinity_value when step is 0" do
+ 1.step(to: 5, by: 0).size.should == infinity_value
+ end
+
+ it "should return infinity_value when step is 0.0" do
+ 1.step(to: 2, by: 0.0).size.should == infinity_value
+ end
+
+ it "should return infinity_value when ascending towards a limit of Float::INFINITY" do
+ 1.step(to: Float::INFINITY, by: 42).size.should == infinity_value
+ end
+
+ it "should return infinity_value when decending towards a limit of -Float::INFINITY" do
+ 1.step(to: -Float::INFINITY, by: -42).size.should == infinity_value
+ end
+
+ it "should return 1 when the both limit and step are Float::INFINITY" do
+ 1.step(to: Float::INFINITY, by: Float::INFINITY).size.should == 1
+ end
+
+ it "should return 1 when the both limit and step are -Float::INFINITY" do
+ 1.step(to: -Float::INFINITY, by: -Float::INFINITY).size.should == 1
+ end
+ end
+ end
+ end
+
+ before :all do
+ # This lambda transforms a positional step method args into
+ # keyword arguments.
+ # It's needed to test numeric_step behaviour with keyword arguments.
+ @step_args = ->(*args) do
+ kw_args = {to: args[0]}
+ kw_args[:by] = args[1] if args.size == 2
+ [kw_args]
+ end
+ end
+ it_behaves_like :numeric_step, :step
+ end
+
+ describe 'with mixed arguments' do
+ it "doesn't raise an error when step is 0" do
+ lambda { 1.step(5, by: 0) { break } }.should_not raise_error
+ end
+
+ it "doesn't raise an error when step is 0.0" do
+ lambda { 1.step(2, by: 0.0) { break } }.should_not raise_error
+ end
+
+ it "raises a ArgumentError when limit and to are defined" do
+ lambda { 1.step(5, 1, to: 5) { break } }.should raise_error(ArgumentError)
+ end
+
+ it "raises a ArgumentError when step and by are defined" do
+ lambda { 1.step(5, 1, by: 5) { break } }.should raise_error(ArgumentError)
+ end
+
+ it "should loop over self when step is 0 or 0.0" do
+ 1.step(2, by: 0.0).take(5).should eql [1.0, 1.0, 1.0, 1.0, 1.0]
+ 1.step(2, by: 0).take(5).should eql [1, 1, 1, 1, 1]
+ 1.1.step(2, by: 0).take(5).should eql [1.1, 1.1, 1.1, 1.1, 1.1]
+ end
+
+ describe "when no block is given" do
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return infinity_value when step is 0" do
+ 1.step(5, by: 0).size.should == infinity_value
+ end
+
+ it "should return infinity_value when step is 0.0" do
+ 1.step(2, by: 0.0).size.should == infinity_value
+ end
+ end
+ end
+ end
+ before :all do
+ # This lambda definition transforms a positional step method args into
+ # a mix of positional and keyword arguments.
+ # It's needed to test numeric_step behaviour with positional mixed with
+ # keyword arguments.
+ @step_args = ->(*args) do
+ if args.size == 2
+ [args[0], {by: args[1]}]
+ else
+ args
+ end
+ end
+ end
+ it_behaves_like :numeric_step, :step
+ end
+end
diff --git a/spec/ruby/core/numeric/to_c_spec.rb b/spec/ruby/core/numeric/to_c_spec.rb
new file mode 100644
index 0000000000..75245a612e
--- /dev/null
+++ b/spec/ruby/core/numeric/to_c_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../../spec_helper'
+
+describe "Numeric#to_c" do
+ before :all do
+ @numbers = [
+ 0,
+ 29871,
+ 99999999999999**99,
+ -72628191273,
+ Rational(2,3),
+ Rational(1.898),
+ Rational(-238),
+ 29282.2827,
+ -2927.00091,
+ 0.0,
+ 12.0,
+ Float::MAX,
+ infinity_value,
+ nan_value
+ ]
+ end
+
+ it "returns a Complex object" do
+ @numbers.each do |number|
+ number.to_c.should be_an_instance_of(Complex)
+ end
+ end
+
+ it "uses self as the real component" do
+ @numbers.each do |number|
+ real = number.to_c.real
+ if Float === number and number.nan?
+ real.nan?.should be_true
+ else
+ real.should == number
+ end
+ end
+ end
+
+ it "uses 0 as the imaginary component" do
+ @numbers.each do |number|
+ number.to_c.imag.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/core/numeric/to_int_spec.rb b/spec/ruby/core/numeric/to_int_spec.rb
new file mode 100644
index 0000000000..3cc39a6d40
--- /dev/null
+++ b/spec/ruby/core/numeric/to_int_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#to_int" do
+ it "returns self#to_i" do
+ obj = NumericSpecs::Subclass.new
+ obj.should_receive(:to_i).and_return(:result)
+ obj.to_int.should == :result
+ end
+end
diff --git a/spec/ruby/core/numeric/truncate_spec.rb b/spec/ruby/core/numeric/truncate_spec.rb
new file mode 100644
index 0000000000..f1592334c5
--- /dev/null
+++ b/spec/ruby/core/numeric/truncate_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#truncate" do
+ before :each do
+ @obj = NumericSpecs::Subclass.new
+ end
+
+ it "converts self to a Float (using #to_f) and returns the #truncate'd result" do
+ @obj.should_receive(:to_f).and_return(2.5555, -2.3333)
+ @obj.truncate.should == 2
+ @obj.truncate.should == -2
+ end
+end
diff --git a/spec/ruby/core/numeric/uminus_spec.rb b/spec/ruby/core/numeric/uminus_spec.rb
new file mode 100644
index 0000000000..39065fa392
--- /dev/null
+++ b/spec/ruby/core/numeric/uminus_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+
+describe "Numeric#-@" do
+ it "returns the same value with opposite sign (integers)" do
+ 0.send(:-@).should == 0
+ 100.send(:-@).should == -100
+ -100.send(:-@).should == 100
+ end
+
+ it "returns the same value with opposite sign (floats)" do
+ 34.56.send(:-@).should == -34.56
+ -34.56.send(:-@).should == 34.56
+ end
+
+ it "returns the same value with opposite sign (two complement)" do
+ 2147483648.send(:-@).should == -2147483648
+ -2147483648.send(:-@).should == 2147483648
+ 9223372036854775808.send(:-@).should == -9223372036854775808
+ -9223372036854775808.send(:-@).should == 9223372036854775808
+ end
+
+ describe "with a Numeric subclass" do
+ it "calls #coerce(0) on self, then subtracts the second element of the result from the first" do
+ ten = mock_numeric('10')
+ zero = mock_numeric('0')
+ ten.should_receive(:coerce).with(0).and_return([zero, ten])
+ zero.should_receive(:-).with(ten).and_return(-10)
+ ten.send(:-@).should == -10
+ end
+ end
+end
diff --git a/spec/ruby/core/numeric/uplus_spec.rb b/spec/ruby/core/numeric/uplus_spec.rb
new file mode 100644
index 0000000000..88cf5e037b
--- /dev/null
+++ b/spec/ruby/core/numeric/uplus_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#+@" do
+ it "returns self" do
+ obj = NumericSpecs::Subclass.new
+ obj.send(:+@).should == obj
+ end
+end
diff --git a/spec/ruby/core/numeric/zero_spec.rb b/spec/ruby/core/numeric/zero_spec.rb
new file mode 100644
index 0000000000..9de71d1dc9
--- /dev/null
+++ b/spec/ruby/core/numeric/zero_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Numeric#zero?" do
+ before :each do
+ @obj = NumericSpecs::Subclass.new
+ end
+
+ it "returns true if self is 0" do
+ @obj.should_receive(:==).with(0).and_return(true)
+ @obj.zero?.should == true
+ end
+
+ it "returns false if self is not 0" do
+ @obj.should_receive(:==).with(0).and_return(false)
+ @obj.zero?.should == false
+ end
+end
diff --git a/spec/ruby/core/objectspace/_id2ref_spec.rb b/spec/ruby/core/objectspace/_id2ref_spec.rb
new file mode 100644
index 0000000000..1d8ad02deb
--- /dev/null
+++ b/spec/ruby/core/objectspace/_id2ref_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+
+describe "ObjectSpace._id2ref" do
+ it "converts an object id to a reference to the object" do
+ s = "I am a string"
+ r = ObjectSpace._id2ref(s.object_id)
+ r.should == s
+ end
+
+ it "retrieves a Fixnum by object_id" do
+ f = 1
+ r = ObjectSpace._id2ref(f.object_id)
+ r.should == f
+ end
+
+ it "retrieves a Symbol by object_id" do
+ s = :sym
+ r = ObjectSpace._id2ref(s.object_id)
+ r.should == s
+ end
+
+ it 'raises RangeError when an object could not be found' do
+ proc { ObjectSpace._id2ref(1 << 60) }.should raise_error(RangeError)
+ end
+end
diff --git a/spec/ruby/core/objectspace/add_finalizer_spec.rb b/spec/ruby/core/objectspace/add_finalizer_spec.rb
new file mode 100644
index 0000000000..3540ac0413
--- /dev/null
+++ b/spec/ruby/core/objectspace/add_finalizer_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "ObjectSpace.add_finalizer" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/objectspace/call_finalizer_spec.rb b/spec/ruby/core/objectspace/call_finalizer_spec.rb
new file mode 100644
index 0000000000..6dce92ddd6
--- /dev/null
+++ b/spec/ruby/core/objectspace/call_finalizer_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "ObjectSpace.call_finalizer" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/objectspace/count_objects_spec.rb b/spec/ruby/core/objectspace/count_objects_spec.rb
new file mode 100644
index 0000000000..e9831a3a42
--- /dev/null
+++ b/spec/ruby/core/objectspace/count_objects_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "ObjectSpace.count_objects" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/objectspace/define_finalizer_spec.rb b/spec/ruby/core/objectspace/define_finalizer_spec.rb
new file mode 100644
index 0000000000..e2869ee706
--- /dev/null
+++ b/spec/ruby/core/objectspace/define_finalizer_spec.rb
@@ -0,0 +1,101 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# NOTE: A call to define_finalizer does not guarantee that the
+# passed proc or callable will be called at any particular time.
+# It is highly questionable whether these aspects of ObjectSpace
+# should be spec'd at all.
+describe "ObjectSpace.define_finalizer" do
+ it "raises an ArgumentError if the action does not respond to call" do
+ lambda {
+ ObjectSpace.define_finalizer("", mock("ObjectSpace.define_finalizer no #call"))
+ }.should raise_error(ArgumentError)
+ end
+
+ it "accepts an object and a proc" do
+ handler = lambda { |obj| obj }
+ ObjectSpace.define_finalizer("garbage", handler).should == [0, handler]
+ end
+
+ it "accepts an object and a callable" do
+ handler = mock("callable")
+ def handler.call(obj) end
+ ObjectSpace.define_finalizer("garbage", handler).should == [0, handler]
+ end
+
+ it "raises ArgumentError trying to define a finalizer on a non-reference" do
+ lambda {
+ ObjectSpace.define_finalizer(:blah) { 1 }
+ }.should raise_error(ArgumentError)
+ end
+
+ # see [ruby-core:24095]
+ with_feature :fork do
+ it "calls finalizer on process termination" do
+ rd, wr = IO.pipe
+ pid = Process.fork do
+ rd.close
+ handler = ObjectSpaceFixtures.scoped(wr)
+ obj = "Test"
+ ObjectSpace.define_finalizer(obj, handler)
+ exit 0
+ end
+
+ wr.close
+ begin
+ rd.read.should == "finalized"
+ ensure
+ rd.close
+ Process.wait pid
+ end
+ end
+
+ it "calls finalizer at exit even if it is self-referencing" do
+ rd, wr = IO.pipe
+ pid = Process.fork do
+ rd.close
+ obj = "Test"
+ handler = Proc.new { wr.write "finalized"; wr.close }
+ ObjectSpace.define_finalizer(obj, handler)
+ exit 0
+ end
+
+ wr.close
+ begin
+ rd.read.should == "finalized"
+ ensure
+ rd.close
+ Process.wait pid
+ end
+ end
+
+ # These specs are defined under the fork specs because there is no
+ # deterministic way to force finalizers to be run, except process exit, so
+ # we rely on that.
+ it "allows multiple finalizers with different 'callables' to be defined" do
+ rd1, wr1 = IO.pipe
+ rd2, wr2 = IO.pipe
+
+ pid = Kernel::fork do
+ rd1.close
+ rd2.close
+ obj = mock("ObjectSpace.define_finalizer multiple")
+
+ ObjectSpace.define_finalizer(obj, Proc.new { wr1.write "finalized1"; wr1.close })
+ ObjectSpace.define_finalizer(obj, Proc.new { wr2.write "finalized2"; wr2.close })
+
+ exit 0
+ end
+
+ wr1.close
+ wr2.close
+
+ rd1.read.should == "finalized1"
+ rd2.read.should == "finalized2"
+
+ rd1.close
+ rd2.close
+ Process.wait pid
+ end
+ end
+end
diff --git a/spec/ruby/core/objectspace/each_object_spec.rb b/spec/ruby/core/objectspace/each_object_spec.rb
new file mode 100644
index 0000000000..c827867fdc
--- /dev/null
+++ b/spec/ruby/core/objectspace/each_object_spec.rb
@@ -0,0 +1,214 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "ObjectSpace.each_object" do
+ it "calls the block once for each living, non-immediate object in the Ruby process" do
+ klass = Class.new
+ new_obj = klass.new
+
+ yields = 0
+ count = ObjectSpace.each_object(klass) do |obj|
+ obj.should == new_obj
+ yields += 1
+ end
+ count.should == 1
+ yields.should == 1
+
+ # this is needed to prevent the new_obj from being GC'd too early
+ new_obj.should_not == nil
+ end
+
+ it "calls the block once for each class, module in the Ruby process" do
+ klass = Class.new
+ mod = Module.new
+
+ [klass, mod].each do |k|
+ yields = 0
+ got_it = false
+ count = ObjectSpace.each_object(k.class) do |obj|
+ got_it = true if obj == k
+ yields += 1
+ end
+ got_it.should == true
+ count.should == yields
+ end
+ end
+
+ it "returns an enumerator if not given a block" do
+ klass = Class.new
+ new_obj = klass.new
+
+ counter = ObjectSpace.each_object(klass)
+ counter.should be_an_instance_of(Enumerator)
+ counter.each{}.should == 1
+ # this is needed to prevent the new_obj from being GC'd too early
+ new_obj.should_not == nil
+ end
+
+ it "finds an object stored in a global variable" do
+ $object_space_global_variable = ObjectSpaceFixtures::ObjectToBeFound.new(:global)
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:global)
+ end
+
+ it "finds an object stored in a top-level constant" do
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:top_level_constant)
+ end
+
+ it "finds an object stored in a second-level constant" do
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:second_level_constant)
+ end
+
+ it "finds an object stored in a local variable" do
+ local = ObjectSpaceFixtures::ObjectToBeFound.new(:local)
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:local)
+ end
+
+ it "finds an object stored in a local variable captured in a block explicitly" do
+ proc = Proc.new {
+ local_in_block = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_block_explicit)
+ Proc.new { local_in_block }
+ }.call
+
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_block_explicit)
+ end
+
+ it "finds an object stored in a local variable captured in a block implicitly" do
+ proc = Proc.new {
+ local_in_block = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_block_implicit)
+ Proc.new { }
+ }.call
+
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_block_implicit)
+ end
+
+ it "finds an object stored in a local variable captured in by a method defined with a block" do
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:captured_by_define_method)
+ end
+
+ it "finds an object stored in a local variable captured in a Proc#binding" do
+ binding = Proc.new {
+ local_in_proc_binding = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_proc_binding)
+ Proc.new { }.binding
+ }.call
+
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_proc_binding)
+ end
+
+ it "finds an object stored in a local variable captured in a Kernel#binding" do
+ b = Proc.new {
+ local_in_kernel_binding = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_kernel_binding)
+ binding
+ }.call
+
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_kernel_binding)
+ end
+
+ it "finds an object stored in a local variable set in a binding manually" do
+ b = binding
+ b.eval("local = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_manual_binding)")
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_manual_binding)
+ end
+
+ it "finds an object stored in an array" do
+ array = [ObjectSpaceFixtures::ObjectToBeFound.new(:array)]
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:array)
+ end
+
+ it "finds an object stored in a hash key" do
+ hash = {ObjectSpaceFixtures::ObjectToBeFound.new(:hash_key) => :value}
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:hash_key)
+ end
+
+ it "finds an object stored in a hash value" do
+ hash = {a: ObjectSpaceFixtures::ObjectToBeFound.new(:hash_value)}
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:hash_value)
+ end
+
+ it "finds an object stored in an instance variable" do
+ local = ObjectSpaceFixtures::ObjectWithInstanceVariable.new
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:instance_variable)
+ end
+
+ it "finds an object stored in a thread local" do
+ thread = Thread.new {}
+ thread.thread_variable_set(:object_space_thread_local, ObjectSpaceFixtures::ObjectToBeFound.new(:thread_local))
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:thread_local)
+ thread.join
+ end
+
+ it "finds an object stored in a fiber local" do
+ Thread.current[:object_space_fiber_local] = ObjectSpaceFixtures::ObjectToBeFound.new(:fiber_local)
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:fiber_local)
+ end
+
+ it "finds an object captured in an at_exit handler" do
+ Proc.new {
+ local = ObjectSpaceFixtures::ObjectToBeFound.new(:at_exit)
+
+ at_exit do
+ local
+ end
+ }.call
+
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:at_exit)
+ end
+
+ it "finds an object captured in finalizer" do
+ alive = Object.new
+
+ Proc.new {
+ local = ObjectSpaceFixtures::ObjectToBeFound.new(:finalizer)
+
+ ObjectSpace.define_finalizer(alive, Proc.new {
+ local
+ })
+ }.call
+
+ ObjectSpaceFixtures.to_be_found_symbols.should include(:finalizer)
+
+ alive.should_not be_nil
+ end
+
+ describe "on singleton classes" do
+ before :each do
+ @klass = Class.new
+ instance = @klass.new
+ @sclass = instance.singleton_class
+ @meta = @klass.singleton_class
+ end
+
+ it "does not walk hidden metaclasses" do
+ klass = Class.new.singleton_class
+ ancestors = ObjectSpace.each_object(Class).select { |c| klass.is_a? c }
+ hidden = ancestors.find { |h| h.inspect.include? klass.inspect }
+ hidden.should == nil
+ end
+
+ it "walks singleton classes" do
+ @sclass.should be_kind_of(@meta)
+ ObjectSpace.each_object(@meta).to_a.should include(@sclass)
+ end
+ end
+
+ it "walks a class and its normal descendants when passed the class's singleton class" do
+ a = Class.new
+ b = Class.new(a)
+ c = Class.new(a)
+ d = Class.new(b)
+
+ c_instance = c.new
+ c_sclass = c_instance.singleton_class
+
+ expected = [ a, b, c, d ]
+
+ # singleton classes should be walked only on >= 2.3
+ expected << c_sclass
+ c_sclass.should be_kind_of(a.singleton_class)
+
+ b.extend Enumerable # included modules should not be walked
+
+ classes = ObjectSpace.each_object(a.singleton_class).to_a
+
+ classes.sort_by(&:object_id).should == expected.sort_by(&:object_id)
+ end
+end
diff --git a/spec/ruby/core/objectspace/finalizers_spec.rb b/spec/ruby/core/objectspace/finalizers_spec.rb
new file mode 100644
index 0000000000..e7f20fc8a0
--- /dev/null
+++ b/spec/ruby/core/objectspace/finalizers_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "ObjectSpace.finalizers" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/objectspace/fixtures/classes.rb b/spec/ruby/core/objectspace/fixtures/classes.rb
new file mode 100644
index 0000000000..9a4b0aa865
--- /dev/null
+++ b/spec/ruby/core/objectspace/fixtures/classes.rb
@@ -0,0 +1,64 @@
+module ObjectSpaceFixtures
+ def self.garbage
+ blah
+ end
+
+ def self.blah
+ o = "hello"
+ @garbage_objid = o.object_id
+ return o
+ end
+
+ @last_objid = nil
+
+ def self.last_objid
+ @last_objid
+ end
+
+ def self.garbage_objid
+ @garbage_objid
+ end
+
+ def self.make_finalizer
+ proc { |obj_id| @last_objid = obj_id }
+ end
+
+ def self.define_finalizer
+ handler = lambda { |obj| ScratchPad.record :finalized }
+ ObjectSpace.define_finalizer "#{rand 5}", handler
+ end
+
+ def self.scoped(wr)
+ return Proc.new { wr.write "finalized"; wr.close }
+ end
+
+ class ObjectToBeFound
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+ end
+
+ class ObjectWithInstanceVariable
+ def initialize
+ @instance_variable = ObjectToBeFound.new(:instance_variable)
+ end
+ end
+
+ def self.to_be_found_symbols
+ ObjectSpace.each_object(ObjectToBeFound).map do |o|
+ o.name
+ end
+ end
+
+ o = ObjectToBeFound.new(:captured_by_define_method)
+ define_method :capturing_method do
+ o
+ end
+
+ SECOND_LEVEL_CONSTANT = ObjectToBeFound.new(:second_level_constant)
+
+end
+
+OBJECT_SPACE_TOP_LEVEL_CONSTANT = ObjectSpaceFixtures::ObjectToBeFound.new(:top_level_constant)
diff --git a/spec/ruby/core/objectspace/garbage_collect_spec.rb b/spec/ruby/core/objectspace/garbage_collect_spec.rb
new file mode 100644
index 0000000000..90000038c2
--- /dev/null
+++ b/spec/ruby/core/objectspace/garbage_collect_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+
+describe "ObjectSpace.garbage_collect" do
+
+ it "can be invoked without any exceptions" do
+ lambda { ObjectSpace.garbage_collect }.should_not raise_error
+ end
+
+ it "doesn't accept any arguments" do
+ lambda { ObjectSpace.garbage_collect(1) }.should raise_error(ArgumentError)
+ end
+
+ it "ignores the supplied block" do
+ lambda { ObjectSpace.garbage_collect {} }.should_not raise_error
+ end
+
+ it "always returns nil" do
+ ObjectSpace.garbage_collect.should == nil
+ ObjectSpace.garbage_collect.should == nil
+ end
+
+end
diff --git a/spec/ruby/core/objectspace/remove_finalizer_spec.rb b/spec/ruby/core/objectspace/remove_finalizer_spec.rb
new file mode 100644
index 0000000000..0b2b8cf16b
--- /dev/null
+++ b/spec/ruby/core/objectspace/remove_finalizer_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "ObjectSpace.remove_finalizer" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/objectspace/undefine_finalizer_spec.rb b/spec/ruby/core/objectspace/undefine_finalizer_spec.rb
new file mode 100644
index 0000000000..11d43121f8
--- /dev/null
+++ b/spec/ruby/core/objectspace/undefine_finalizer_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "ObjectSpace.undefine_finalizer" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/proc/allocate_spec.rb b/spec/ruby/core/proc/allocate_spec.rb
new file mode 100644
index 0000000000..a512c73b1a
--- /dev/null
+++ b/spec/ruby/core/proc/allocate_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Proc.allocate" do
+ it "raises a TypeError" do
+ lambda {
+ Proc.allocate
+ }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/proc/arity_spec.rb b/spec/ruby/core/proc/arity_spec.rb
new file mode 100644
index 0000000000..f7cb5ad0f8
--- /dev/null
+++ b/spec/ruby/core/proc/arity_spec.rb
@@ -0,0 +1,640 @@
+require_relative '../../spec_helper'
+
+describe "Proc#arity" do
+ SpecEvaluate.desc = "for definition"
+
+ context "for instances created with -> () { }" do
+ context "returns zero" do
+ evaluate <<-ruby do
+ @a = -> () {}
+ ruby
+
+ @a.arity.should == 0
+ end
+
+ evaluate <<-ruby do
+ @a = -> (&b) {}
+ ruby
+
+ @a.arity.should == 0
+ end
+ end
+
+ context "returns positive values" do
+ evaluate <<-ruby do
+ @a = -> (a) { }
+ @b = -> (a, b) { }
+ @c = -> (a, b, c) { }
+ @d = -> (a, b, c, d) { }
+ ruby
+
+ @a.arity.should == 1
+ @b.arity.should == 2
+ @c.arity.should == 3
+ @d.arity.should == 4
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a:) { }
+ @b = -> (a:, b:) { }
+ @c = -> (a: 1, b:, c:, d: 2) { }
+ ruby
+
+ @a.arity.should == 1
+ @b.arity.should == 1
+ @c.arity.should == 1
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a, b:) { }
+ @b = -> (a, b:, &l) { }
+ ruby
+
+ @a.arity.should == 2
+ @b.arity.should == 2
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a, b, c:, d: 1) { }
+ @b = -> (a, b, c:, d: 1, **k, &l) { }
+ ruby
+
+ @a.arity.should == 3
+ @b.arity.should == 3
+ end
+
+ evaluate <<-ruby do
+ @a = -> ((a, (*b, c))) { }
+ @b = -> (a, (*b, c), d, (*e), (*)) { }
+ ruby
+
+ @a.arity.should == 1
+ @b.arity.should == 5
+ end
+ end
+
+ context "returns negative values" do
+ evaluate <<-ruby do
+ @a = -> (a=1) { }
+ @b = -> (a=1, b=2) { }
+ ruby
+
+ @a.arity.should == -1
+ @b.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a, b=1) { }
+ @b = -> (a, b, c=1, d=2) { }
+ ruby
+
+ @a.arity.should == -2
+ @b.arity.should == -3
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a=1, *b) { }
+ @b = -> (a=1, b=2, *c) { }
+ ruby
+
+ @a.arity.should == -1
+ @b.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = -> (*) { }
+ @b = -> (*a) { }
+ ruby
+
+ @a.arity.should == -1
+ @b.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a, *) { }
+ @b = -> (a, *b) { }
+ @c = -> (a, b, *c) { }
+ @d = -> (a, b, c, *d) { }
+ ruby
+
+ @a.arity.should == -2
+ @b.arity.should == -2
+ @c.arity.should == -3
+ @d.arity.should == -4
+ end
+
+ evaluate <<-ruby do
+ @a = -> (*a, b) { }
+ @b = -> (*a, b, c) { }
+ @c = -> (*a, b, c, d) { }
+ ruby
+
+ @a.arity.should == -2
+ @b.arity.should == -3
+ @c.arity.should == -4
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a, *b, c) { }
+ @b = -> (a, b, *c, d, e) { }
+ ruby
+
+ @a.arity.should == -3
+ @b.arity.should == -5
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a, b=1, c=2, *d, e, f) { }
+ @b = -> (a, b, c=1, *d, e, f, g) { }
+ ruby
+
+ @a.arity.should == -4
+ @b.arity.should == -6
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a: 1) { }
+ @b = -> (a: 1, b: 2) { }
+ ruby
+
+ @a.arity.should == -1
+ @b.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a=1, b: 2) { }
+ @b = -> (*a, b: 1) { }
+ @c = -> (a=1, b: 2) { }
+ @d = -> (a=1, *b, c: 2, &l) { }
+ ruby
+
+ @a.arity.should == -1
+ @b.arity.should == -1
+ @c.arity.should == -1
+ @d.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = -> (**k, &l) { }
+ @b= -> (*a, **k) { }
+ @c = ->(a: 1, b: 2, **k) { }
+ ruby
+
+ @a.arity.should == -1
+ @b.arity.should == -1
+ @c.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a=1, *b, c:, d: 2, **k, &l) { }
+ ruby
+
+ @a.arity.should == -2
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a, b=1, *c, d, e:, f: 2, **k, &l) { }
+ @b = -> (a, b=1, *c, d:, e:, f: 2, **k, &l) { }
+ @c = -> (a=0, b=1, *c, d, e:, f: 2, **k, &l) { }
+ @d = -> (a=0, b=1, *c, d:, e:, f: 2, **k, &l) { }
+ ruby
+
+ @a.arity.should == -4
+ @b.arity.should == -3
+ @c.arity.should == -3
+ @d.arity.should == -2
+ end
+ end
+ end
+
+ context "for instances created with lambda { || }" do
+ context "returns zero" do
+ evaluate <<-ruby do
+ @a = lambda { }
+ @b = lambda { || }
+ ruby
+
+ @a.arity.should == 0
+ @b.arity.should == 0
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |&b| }
+ ruby
+
+ @a.arity.should == 0
+ end
+ end
+
+ context "returns positive values" do
+ evaluate <<-ruby do
+ @a = lambda { |a| }
+ @b = lambda { |a, b| }
+ @c = lambda { |a, b, c| }
+ @d = lambda { |a, b, c, d| }
+ ruby
+
+ @a.arity.should == 1
+ @b.arity.should == 2
+ @c.arity.should == 3
+ @d.arity.should == 4
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a:| }
+ @b = lambda { |a:, b:| }
+ @c = lambda { |a: 1, b:, c:, d: 2| }
+ ruby
+
+ @a.arity.should == 1
+ @b.arity.should == 1
+ @c.arity.should == 1
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a, b:| }
+ @b = lambda { |a, b:, &l| }
+ ruby
+
+ @a.arity.should == 2
+ @b.arity.should == 2
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a, b, c:, d: 1| }
+ @b = lambda { |a, b, c:, d: 1, **k, &l| }
+ ruby
+
+ @a.arity.should == 3
+ @b.arity.should == 3
+ end
+ end
+
+ context "returns negative values" do
+ evaluate <<-ruby do
+ @a = lambda { |a=1| }
+ @b = lambda { |a=1, b=2| }
+ ruby
+
+ @a.arity.should == -1
+ @b.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a, b=1| }
+ @b = lambda { |a, b, c=1, d=2| }
+ ruby
+
+ @a.arity.should == -2
+ @b.arity.should == -3
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a=1, *b| }
+ @b = lambda { |a=1, b=2, *c| }
+ ruby
+
+ @a.arity.should == -1
+ @b.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |*| }
+ @b = lambda { |*a| }
+ ruby
+
+ @a.arity.should == -1
+ @b.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a, *| }
+ @b = lambda { |a, *b| }
+ @c = lambda { |a, b, *c| }
+ @d = lambda { |a, b, c, *d| }
+ ruby
+
+ @a.arity.should == -2
+ @b.arity.should == -2
+ @c.arity.should == -3
+ @d.arity.should == -4
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |*a, b| }
+ @b = lambda { |*a, b, c| }
+ @c = lambda { |*a, b, c, d| }
+ ruby
+
+ @a.arity.should == -2
+ @b.arity.should == -3
+ @c.arity.should == -4
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a, *b, c| }
+ @b = lambda { |a, b, *c, d, e| }
+ ruby
+
+ @a.arity.should == -3
+ @b.arity.should == -5
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a, b=1, c=2, *d, e, f| }
+ @b = lambda { |a, b, c=1, *d, e, f, g| }
+ ruby
+
+ @a.arity.should == -4
+ @b.arity.should == -6
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a: 1| }
+ @b = lambda { |a: 1, b: 2| }
+ ruby
+
+ @a.arity.should == -1
+ @b.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a=1, b: 2| }
+ @b = lambda { |*a, b: 1| }
+ @c = lambda { |a=1, b: 2| }
+ @d = lambda { |a=1, *b, c: 2, &l| }
+ ruby
+
+ @a.arity.should == -1
+ @b.arity.should == -1
+ @c.arity.should == -1
+ @d.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |**k, &l| }
+ @b = lambda { |*a, **k| }
+ @c = lambda { |a: 1, b: 2, **k| }
+ ruby
+
+ @a.arity.should == -1
+ @b.arity.should == -1
+ @c.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a=1, *b, c:, d: 2, **k, &l| }
+ ruby
+
+ @a.arity.should == -2
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |(a, (*b, c)), d=1| }
+ @b = lambda { |a, (*b, c), d, (*e), (*), **k| }
+ @c = lambda { |a, (b, c), *, d:, e: 2, **| }
+ ruby
+
+ @a.arity.should == -2
+ @b.arity.should == -6
+ @c.arity.should == -4
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a, b=1, *c, d, e:, f: 2, **k, &l| }
+ @b = lambda { |a, b=1, *c, d:, e:, f: 2, **k, &l| }
+ @c = lambda { |a=0, b=1, *c, d, e:, f: 2, **k, &l| }
+ @d = lambda { |a=0, b=1, *c, d:, e:, f: 2, **k, &l| }
+ ruby
+
+ @a.arity.should == -4
+ @b.arity.should == -3
+ @c.arity.should == -3
+ @d.arity.should == -2
+ end
+ end
+ end
+
+ context "for instances created with proc { || }" do
+ context "returns zero" do
+ evaluate <<-ruby do
+ @a = proc { }
+ @b = proc { || }
+ ruby
+
+ @a.arity.should == 0
+ @b.arity.should == 0
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |&b| }
+ ruby
+
+ @a.arity.should == 0
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |a=1| }
+ @b = proc { |a=1, b=2| }
+ ruby
+
+ @a.arity.should == 0
+ @b.arity.should == 0
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |a: 1| }
+ @b = proc { |a: 1, b: 2| }
+ ruby
+
+ @a.arity.should == 0
+ @b.arity.should == 0
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |**k, &l| }
+ @b = proc { |a: 1, b: 2, **k| }
+ ruby
+
+ @a.arity.should == 0
+ @b.arity.should == 0
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |a=1, b: 2| }
+ @b = proc { |a=1, b: 2| }
+ ruby
+
+ @a.arity.should == 0
+ @b.arity.should == 0
+ end
+ end
+
+ context "returns positive values" do
+ evaluate <<-ruby do
+ @a = proc { |a| }
+ @b = proc { |a, b| }
+ @c = proc { |a, b, c| }
+ @d = proc { |a, b, c, d| }
+ ruby
+
+ @a.arity.should == 1
+ @b.arity.should == 2
+ @c.arity.should == 3
+ @d.arity.should == 4
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |a, b=1| }
+ @b = proc { |a, b, c=1, d=2| }
+ ruby
+
+ @a.arity.should == 1
+ @b.arity.should == 2
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a:| }
+ @b = lambda { |a:, b:| }
+ @c = lambda { |a: 1, b:, c:, d: 2| }
+ ruby
+
+ @a.arity.should == 1
+ @b.arity.should == 1
+ @c.arity.should == 1
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |a, b:| }
+ @b = proc { |a, b:, &l| }
+ ruby
+
+ @a.arity.should == 2
+ @b.arity.should == 2
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |a, b, c:, d: 1| }
+ @b = proc { |a, b, c:, d: 1, **k, &l| }
+ ruby
+
+ @a.arity.should == 3
+ @b.arity.should == 3
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |(a, (*b, c)), d=1| }
+ @b = proc { |a, (*b, c), d, (*e), (*), **k| }
+ ruby
+
+ @a.arity.should == 1
+ @b.arity.should == 5
+ end
+ end
+
+ context "returns negative values" do
+ evaluate <<-ruby do
+ @a = proc { |a=1, *b| }
+ @b = proc { |a=1, b=2, *c| }
+ ruby
+
+ @a.arity.should == -1
+ @b.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |*| }
+ @b = proc { |*a| }
+ ruby
+
+ @a.arity.should == -1
+ @b.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |a, *| }
+ @b = proc { |a, *b| }
+ @c = proc { |a, b, *c| }
+ @d = proc { |a, b, c, *d| }
+ ruby
+
+ @a.arity.should == -2
+ @b.arity.should == -2
+ @c.arity.should == -3
+ @d.arity.should == -4
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |*a, b| }
+ @b = proc { |*a, b, c| }
+ @c = proc { |*a, b, c, d| }
+ ruby
+
+ @a.arity.should == -2
+ @b.arity.should == -3
+ @c.arity.should == -4
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |a, *b, c| }
+ @b = proc { |a, b, *c, d, e| }
+ ruby
+
+ @a.arity.should == -3
+ @b.arity.should == -5
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |a, b=1, c=2, *d, e, f| }
+ @b = proc { |a, b, c=1, *d, e, f, g| }
+ ruby
+
+ @a.arity.should == -4
+ @b.arity.should == -6
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |*a, b: 1| }
+ @b = proc { |a=1, *b, c: 2, &l| }
+ ruby
+
+ @a.arity.should == -1
+ @b.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |*a, **k| }
+ ruby
+
+ @a.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |a=1, *b, c:, d: 2, **k, &l| }
+ ruby
+
+ @a.arity.should == -2
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |a, (b, c), *, d:, e: 2, **| }
+ ruby
+
+ @a.arity.should == -4
+ end
+
+ evaluate <<-ruby do
+ @a = proc { |a, b=1, *c, d, e:, f: 2, **k, &l| }
+ @b = proc { |a, b=1, *c, d:, e:, f: 2, **k, &l| }
+ @c = proc { |a=0, b=1, *c, d, e:, f: 2, **k, &l| }
+ @d = proc { |a=0, b=1, *c, d:, e:, f: 2, **k, &l| }
+ ruby
+
+ @a.arity.should == -4
+ @b.arity.should == -3
+ @c.arity.should == -3
+ @d.arity.should == -2
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/proc/binding_spec.rb b/spec/ruby/core/proc/binding_spec.rb
new file mode 100644
index 0000000000..5e8a461a55
--- /dev/null
+++ b/spec/ruby/core/proc/binding_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+
+describe "Proc#binding" do
+ it "returns a Binding instance" do
+ [Proc.new{}, lambda {}, proc {}].each { |p|
+ p.binding.should be_kind_of(Binding)
+ }
+ end
+
+ it "returns the binding associated with self" do
+ obj = mock('binding')
+ def obj.test_binding(some, params)
+ lambda {}
+ end
+
+ lambdas_binding = obj.test_binding(1, 2).binding
+
+ eval("some", lambdas_binding).should == 1
+ eval("params", lambdas_binding).should == 2
+ end
+end
diff --git a/spec/ruby/core/proc/block_pass_spec.rb b/spec/ruby/core/proc/block_pass_spec.rb
new file mode 100644
index 0000000000..282f00b5a8
--- /dev/null
+++ b/spec/ruby/core/proc/block_pass_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+
+describe "Proc as a block pass argument" do
+ def revivify(&b)
+ b
+ end
+
+ it "remains the same object if re-vivified by the target method" do
+ p = Proc.new {}
+ p2 = revivify(&p)
+ p.should equal p2
+ p.should == p2
+ end
+
+ it "remains the same object if reconstructed with Proc.new" do
+ p = Proc.new {}
+ p2 = Proc.new(&p)
+ p.should equal p2
+ p.should == p2
+ end
+end
+
+describe "Proc as an implicit block pass argument" do
+ def revivify
+ Proc.new
+ end
+
+ it "remains the same object if re-vivified by the target method" do
+ p = Proc.new {}
+ p2 = revivify(&p)
+ p.should equal p2
+ p.should == p2
+ end
+
+ it "remains the same object if reconstructed with Proc.new" do
+ p = Proc.new {}
+ p2 = Proc.new(&p)
+ p.should equal p2
+ p.should == p2
+ end
+end
diff --git a/spec/ruby/core/proc/call_spec.rb b/spec/ruby/core/proc/call_spec.rb
new file mode 100644
index 0000000000..6ec2fc8682
--- /dev/null
+++ b/spec/ruby/core/proc/call_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require_relative 'shared/call'
+require_relative 'shared/call_arguments'
+
+describe "Proc#call" do
+ it_behaves_like :proc_call, :call
+ it_behaves_like :proc_call_block_args, :call
+end
+
+describe "Proc#call on a Proc created with Proc.new" do
+ it_behaves_like :proc_call_on_proc_new, :call
+end
+
+describe "Proc#call on a Proc created with Kernel#lambda or Kernel#proc" do
+ it_behaves_like :proc_call_on_proc_or_lambda, :call
+end
diff --git a/spec/ruby/core/proc/case_compare_spec.rb b/spec/ruby/core/proc/case_compare_spec.rb
new file mode 100644
index 0000000000..f11513cdb9
--- /dev/null
+++ b/spec/ruby/core/proc/case_compare_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require_relative 'shared/call'
+require_relative 'shared/call_arguments'
+
+describe "Proc#===" do
+ it_behaves_like :proc_call, :===
+ it_behaves_like :proc_call_block_args, :===
+end
+
+describe "Proc#=== on a Proc created with Proc.new" do
+ it_behaves_like :proc_call_on_proc_new, :===
+end
+
+describe "Proc#=== on a Proc created with Kernel#lambda or Kernel#proc" do
+ it_behaves_like :proc_call_on_proc_or_lambda, :===
+end
diff --git a/spec/ruby/core/proc/clone_spec.rb b/spec/ruby/core/proc/clone_spec.rb
new file mode 100644
index 0000000000..a1a1292654
--- /dev/null
+++ b/spec/ruby/core/proc/clone_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/dup'
+
+describe "Proc#clone" do
+ it_behaves_like :proc_dup, :clone
+end
diff --git a/spec/ruby/core/proc/curry_spec.rb b/spec/ruby/core/proc/curry_spec.rb
new file mode 100644
index 0000000000..7d1da441f1
--- /dev/null
+++ b/spec/ruby/core/proc/curry_spec.rb
@@ -0,0 +1,180 @@
+require_relative '../../spec_helper'
+
+describe "Proc#curry" do
+ before :each do
+ @proc_add = Proc.new {|x,y,z| (x||0) + (y||0) + (z||0) }
+ @lambda_add = lambda {|x,y,z| (x||0) + (y||0) + (z||0) }
+ end
+
+ it "returns a Proc when called on a proc" do
+ p = proc { true }
+ p.curry.should be_an_instance_of(Proc)
+ end
+
+ it "returns a Proc when called on a lambda" do
+ p = lambda { true }
+ p.curry.should be_an_instance_of(Proc)
+ end
+
+ it "calls the curried proc with the arguments if sufficient arguments have been given" do
+ @proc_add.curry[1][2][3].should == 6
+ @lambda_add.curry[1][2][3].should == 6
+ end
+
+ it "returns a Proc that consumes the remainder of the arguments unless sufficient arguments have been given" do
+ proc2 = @proc_add.curry[1][2]
+ proc2.should be_an_instance_of(Proc)
+ proc2.call(3).should == 6
+
+ lambda2 = @lambda_add.curry[1][2]
+ lambda2.should be_an_instance_of(Proc)
+ lambda2.call(3).should == 6
+
+ @proc_add.curry.call(1,2,3).should == 6
+ @lambda_add.curry.call(1,2,3).should == 6
+ end
+
+ it "can be called multiple times on the same Proc" do
+ @proc_add.curry
+ lambda { @proc_add.curry }.should_not raise_error
+
+ @lambda_add.curry
+ lambda { @lambda_add.curry }.should_not raise_error
+ end
+
+ it "can be passed superfluous arguments if created from a proc" do
+ @proc_add.curry[1,2,3,4].should == 6
+
+ @proc_add.curry[1,2].curry[3,4,5,6].should == 6
+ end
+
+ it "raises an ArgumentError if passed superfluous arguments when created from a lambda" do
+ lambda { @lambda_add.curry[1,2,3,4] }.should raise_error(ArgumentError)
+ lambda { @lambda_add.curry[1,2].curry[3,4,5,6] }.should raise_error(ArgumentError)
+ end
+
+ it "returns Procs with arities of -1" do
+ @proc_add.curry.arity.should == -1
+ @lambda_add.curry.arity.should == -1
+ l = lambda { |*a| }
+ l.curry.arity.should == -1
+ end
+
+ it "produces Procs that raise ArgumentError for #binding" do
+ lambda do
+ @proc_add.curry.binding
+ end.should raise_error(ArgumentError)
+ end
+
+ it "produces Procs that return [[:rest]] for #parameters" do
+ @proc_add.curry.parameters.should == [[:rest]]
+ end
+
+ it "produces Procs that return nil for #source_location" do
+ @proc_add.curry.source_location.should == nil
+ end
+
+ it "produces Procs that can be passed as the block for instance_exec" do
+ curried = @proc_add.curry.call(1, 2)
+
+ instance_exec(3, &curried).should == 6
+ end
+
+ it "combines arguments and calculates incoming arity accurately for successively currying" do
+ l = lambda{|a,b,c| a+b+c }
+ l1 = l.curry.call(1)
+ # the l1 currying seems unnecessary, but it triggered the original issue
+ l2 = l1.curry.call(2)
+
+ l2.curry.call(3).should == 6
+ l1.curry.call(2,3).should == 6
+ end
+end
+
+describe "Proc#curry with arity argument" do
+ before :each do
+ @proc_add = proc {|x,y,z| (x||0) + (y||0) + (z||0) }
+ @lambda_add = lambda {|x,y,z| (x||0) + (y||0) + (z||0) }
+ end
+
+ it "accepts an optional Integer argument for the arity" do
+ lambda { @proc_add.curry(3) }.should_not raise_error
+ lambda { @lambda_add.curry(3) }.should_not raise_error
+ end
+
+ it "returns a Proc when called on a proc" do
+ @proc_add.curry(3).should be_an_instance_of(Proc)
+ end
+
+ it "returns a Proc when called on a lambda" do
+ @lambda_add.curry(3).should be_an_instance_of(Proc)
+ end
+
+ # [ruby-core:24127]
+ it "retains the lambda-ness of the Proc on which its called" do
+ @lambda_add.curry(3).lambda?.should be_true
+ @proc_add.curry(3).lambda?.should be_false
+ end
+
+ it "raises an ArgumentError if called on a lambda that requires more than _arity_ arguments" do
+ lambda { @lambda_add.curry(2) }.should raise_error(ArgumentError)
+ lambda { lambda{|x, y, z, *more|}.curry(2) }.should raise_error(ArgumentError)
+ end
+
+ it 'returns a Proc if called on a lambda that requires fewer than _arity_ arguments but may take more' do
+ lambda{|a, b, c, d=nil, e=nil|}.curry(4).should be_an_instance_of(Proc)
+ lambda{|a, b, c, d=nil, *e|}.curry(4).should be_an_instance_of(Proc)
+ lambda{|a, b, c, *d|}.curry(4).should be_an_instance_of(Proc)
+ end
+
+ it "raises an ArgumentError if called on a lambda that requires fewer than _arity_ arguments" do
+ lambda { @lambda_add.curry(4) }.should raise_error(ArgumentError)
+ lambda { lambda { true }.curry(1) }.should raise_error(ArgumentError)
+ lambda { lambda {|a, b=nil|}.curry(5) }.should raise_error(ArgumentError)
+ lambda { lambda {|a, &b|}.curry(2) }.should raise_error(ArgumentError)
+ lambda { lambda {|a, b=nil, &c|}.curry(3) }.should raise_error(ArgumentError)
+ end
+
+ it "calls the curried proc with the arguments if _arity_ arguments have been given" do
+ @proc_add.curry(3)[1][2][3].should == 6
+ @lambda_add.curry(3)[1][2][3].should == 6
+ end
+
+ it "returns a Proc that consumes the remainder of the arguments when fewer than _arity_ arguments are given" do
+ proc2 = @proc_add.curry(3)[1][2]
+ proc2.should be_an_instance_of(Proc)
+ proc2.call(3).should == 6
+
+ lambda2 = @lambda_add.curry(3)[1][2]
+ lambda2.should be_an_instance_of(Proc)
+ lambda2.call(3).should == 6
+ end
+
+ it "can be specified multiple times on the same Proc" do
+ @proc_add.curry(2)
+ lambda { @proc_add.curry(1) }.should_not raise_error
+
+ @lambda_add.curry(3)
+ lambda { @lambda_add.curry(3) }.should_not raise_error
+ end
+
+ it "can be passed more than _arity_ arguments if created from a proc" do
+ lambda { @proc_add.curry(3)[1,2,3,4].should == 6 }.should_not
+ raise_error(ArgumentError)
+ lambda { @proc_add.curry(1)[1,2].curry(3)[3,4,5,6].should == 6 }.should_not
+ raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if passed more than _arity_ arguments when created from a lambda" do
+ lambda { @lambda_add.curry(3)[1,2,3,4] }.should raise_error(ArgumentError)
+ lambda { @lambda_add.curry(1)[1,2].curry(3)[3,4,5,6] }.should raise_error(ArgumentError)
+ end
+
+ it "returns Procs with arities of -1 regardless of the value of _arity_" do
+ @proc_add.curry(1).arity.should == -1
+ @proc_add.curry(2).arity.should == -1
+ @lambda_add.curry(3).arity.should == -1
+ l = lambda { |*a| }
+ l.curry(3).arity.should == -1
+ end
+end
diff --git a/spec/ruby/core/proc/dup_spec.rb b/spec/ruby/core/proc/dup_spec.rb
new file mode 100644
index 0000000000..6da2f3080c
--- /dev/null
+++ b/spec/ruby/core/proc/dup_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/dup'
+
+describe "Proc#dup" do
+ it_behaves_like :proc_dup, :dup
+end
diff --git a/spec/ruby/core/proc/element_reference_spec.rb b/spec/ruby/core/proc/element_reference_spec.rb
new file mode 100644
index 0000000000..f60ae1b086
--- /dev/null
+++ b/spec/ruby/core/proc/element_reference_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+require_relative 'shared/call'
+require_relative 'shared/call_arguments'
+require_relative 'fixtures/proc_aref'
+require_relative 'fixtures/proc_aref_frozen'
+
+describe "Proc#[]" do
+ it_behaves_like :proc_call, :[]
+ it_behaves_like :proc_call_block_args, :[]
+end
+
+describe "Proc#call on a Proc created with Proc.new" do
+ it_behaves_like :proc_call_on_proc_new, :call
+end
+
+describe "Proc#call on a Proc created with Kernel#lambda or Kernel#proc" do
+ it_behaves_like :proc_call_on_proc_or_lambda, :call
+end
+
+ruby_bug "#15118", ""..."2.6" do
+ describe "Proc#[] with frozen_string_literals" do
+ it "doesn't duplicate frozen strings" do
+ ProcArefSpecs.aref.frozen?.should be_false
+ ProcArefSpecs.aref_freeze.frozen?.should be_true
+ ProcArefFrozenSpecs.aref.frozen?.should be_true
+ ProcArefFrozenSpecs.aref_freeze.frozen?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/core/proc/eql_spec.rb b/spec/ruby/core/proc/eql_spec.rb
new file mode 100644
index 0000000000..a3c5a5658d
--- /dev/null
+++ b/spec/ruby/core/proc/eql_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/equal'
+
+describe "Proc#eql?" do
+ it_behaves_like :proc_equal_undefined, :eql?
+end
diff --git a/spec/ruby/core/proc/equal_value_spec.rb b/spec/ruby/core/proc/equal_value_spec.rb
new file mode 100644
index 0000000000..1b6ac792cf
--- /dev/null
+++ b/spec/ruby/core/proc/equal_value_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/equal'
+
+describe "Proc#==" do
+ it_behaves_like :proc_equal_undefined, :==
+end
diff --git a/spec/ruby/core/proc/fixtures/common.rb b/spec/ruby/core/proc/fixtures/common.rb
new file mode 100644
index 0000000000..6e27a2dee7
--- /dev/null
+++ b/spec/ruby/core/proc/fixtures/common.rb
@@ -0,0 +1,51 @@
+module ProcSpecs
+ class ToAryAsNil
+ def to_ary
+ nil
+ end
+ end
+ def self.new_proc_in_method
+ Proc.new
+ end
+
+ def self.new_proc_from_amp(&block)
+ block
+ end
+
+ def self.proc_for_1
+ proc { 1 }
+ end
+
+ class ProcSubclass < Proc
+ end
+
+ def self.new_proc_subclass_in_method
+ ProcSubclass.new
+ end
+
+ class MyProc < Proc
+ end
+
+ class MyProc2 < Proc
+ def initialize(a, b)
+ @first = a
+ @second = b
+ end
+
+ attr_reader :first, :second
+ end
+
+ class Arity
+ def arity_check(&block)
+ pn = Proc.new(&block).arity
+ pr = proc(&block).arity
+ lm = lambda(&block).arity
+
+ if pn == pr and pr == lm
+ return pn
+ else
+ return :arity_check_failed
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/proc/fixtures/proc_aref.rb b/spec/ruby/core/proc/fixtures/proc_aref.rb
new file mode 100644
index 0000000000..a305667797
--- /dev/null
+++ b/spec/ruby/core/proc/fixtures/proc_aref.rb
@@ -0,0 +1,9 @@
+module ProcArefSpecs
+ def self.aref
+ proc {|a| a }["sometext"]
+ end
+
+ def self.aref_freeze
+ proc {|a| a }["sometext".freeze]
+ end
+end
diff --git a/spec/ruby/core/proc/fixtures/proc_aref_frozen.rb b/spec/ruby/core/proc/fixtures/proc_aref_frozen.rb
new file mode 100644
index 0000000000..50a330ba4f
--- /dev/null
+++ b/spec/ruby/core/proc/fixtures/proc_aref_frozen.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+module ProcArefFrozenSpecs
+ def self.aref
+ proc {|a| a }["sometext"]
+ end
+
+ def self.aref_freeze
+ proc {|a| a }["sometext".freeze]
+ end
+end
diff --git a/spec/ruby/core/proc/fixtures/source_location.rb b/spec/ruby/core/proc/fixtures/source_location.rb
new file mode 100644
index 0000000000..468262e02a
--- /dev/null
+++ b/spec/ruby/core/proc/fixtures/source_location.rb
@@ -0,0 +1,55 @@
+module ProcSpecs
+ class SourceLocation
+ def self.my_proc
+ proc { true }
+ end
+
+ def self.my_lambda
+ lambda { true }
+ end
+
+ def self.my_proc_new
+ Proc.new { true }
+ end
+
+ def self.my_method
+ method(__method__).to_proc
+ end
+
+ def self.my_multiline_proc
+ proc do
+ 'a'.upcase
+ 1 + 22
+ end
+ end
+
+ def self.my_multiline_lambda
+ lambda do
+ 'a'.upcase
+ 1 + 22
+ end
+ end
+
+ def self.my_multiline_proc_new
+ Proc.new do
+ 'a'.upcase
+ 1 + 22
+ end
+ end
+
+ def self.my_detached_proc
+ body = proc { true }
+ proc(&body)
+ end
+
+ def self.my_detached_lambda
+ body = lambda { true }
+ lambda(&body)
+ end
+
+ def self.my_detached_proc_new
+ body = Proc.new { true }
+ Proc.new(&body)
+ end
+ end
+end
diff --git a/spec/ruby/core/proc/hash_spec.rb b/spec/ruby/core/proc/hash_spec.rb
new file mode 100644
index 0000000000..5993ad5098
--- /dev/null
+++ b/spec/ruby/core/proc/hash_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "Proc#hash" do
+ it "is provided" do
+ proc {}.respond_to?(:hash).should be_true
+ lambda {}.respond_to?(:hash).should be_true
+ end
+
+ it "returns an Integer" do
+ proc { 1 + 489 }.hash.should be_kind_of(Fixnum)
+ end
+
+ it "is stable" do
+ body = proc { :foo }
+ proc(&body).hash.should == proc(&body).hash
+ end
+end
diff --git a/spec/ruby/core/proc/inspect_spec.rb b/spec/ruby/core/proc/inspect_spec.rb
new file mode 100644
index 0000000000..f53d34116f
--- /dev/null
+++ b/spec/ruby/core/proc/inspect_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_s'
+
+describe "Proc#inspect" do
+ it_behaves_like :proc_to_s, :inspect
+end
diff --git a/spec/ruby/core/proc/lambda_spec.rb b/spec/ruby/core/proc/lambda_spec.rb
new file mode 100644
index 0000000000..edf2151f27
--- /dev/null
+++ b/spec/ruby/core/proc/lambda_spec.rb
@@ -0,0 +1,60 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Proc#lambda?" do
+ it "returns true if the Proc was created from a block with the lambda keyword" do
+ lambda {}.lambda?.should be_true
+ end
+
+ it "returns false if the Proc was created from a block with the proc keyword" do
+ proc {}.lambda?.should be_false
+ end
+
+ it "returns false if the Proc was created from a block with Proc.new" do
+ Proc.new {}.lambda?.should be_false
+ end
+
+ it "is preserved when passing a Proc with & to the lambda keyword" do
+ lambda(&lambda{}).lambda?.should be_true
+ lambda(&proc{}).lambda?.should be_false
+ end
+
+ it "is preserved when passing a Proc with & to the proc keyword" do
+ proc(&lambda{}).lambda?.should be_true
+ proc(&proc{}).lambda?.should be_false
+ end
+
+ it "is preserved when passing a Proc with & to Proc.new" do
+ Proc.new(&lambda{}).lambda?.should be_true
+ Proc.new(&proc{}).lambda?.should be_false
+ end
+
+ it "returns false if the Proc was created from a block with &" do
+ ProcSpecs.new_proc_from_amp{}.lambda?.should be_false
+ end
+
+ it "is preserved when the Proc was passed using &" do
+ ProcSpecs.new_proc_from_amp(&lambda{}).lambda?.should be_true
+ ProcSpecs.new_proc_from_amp(&proc{}).lambda?.should be_false
+ ProcSpecs.new_proc_from_amp(&Proc.new{}).lambda?.should be_false
+ end
+
+ it "returns true for a Method converted to a Proc" do
+ m = :foo.method(:to_s)
+ m.to_proc.lambda?.should be_true
+ ProcSpecs.new_proc_from_amp(&m).lambda?.should be_true
+ end
+
+ # [ruby-core:24127]
+ it "is preserved when a Proc is curried" do
+ lambda{}.curry.lambda?.should be_true
+ proc{}.curry.lambda?.should be_false
+ Proc.new{}.curry.lambda?.should be_false
+ end
+
+ it "is preserved when a curried Proc is called without enough arguments" do
+ lambda{|x,y|}.curry.call(42).lambda?.should be_true
+ proc{|x,y|}.curry.call(42).lambda?.should be_false
+ Proc.new{|x,y|}.curry.call(42).lambda?.should be_false
+ end
+end
diff --git a/spec/ruby/core/proc/new_spec.rb b/spec/ruby/core/proc/new_spec.rb
new file mode 100644
index 0000000000..abdd187276
--- /dev/null
+++ b/spec/ruby/core/proc/new_spec.rb
@@ -0,0 +1,190 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Proc.new with an associated block" do
+ it "returns a proc that represents the block" do
+ Proc.new { }.call.should == nil
+ Proc.new { "hello" }.call.should == "hello"
+ end
+
+ describe "called on a subclass of Proc" do
+ before :each do
+ @subclass = Class.new(Proc) do
+ attr_reader :ok
+ def initialize
+ @ok = true
+ super
+ end
+ end
+ end
+
+ it "returns an instance of the subclass" do
+ proc = @subclass.new {"hello"}
+
+ proc.class.should == @subclass
+ proc.call.should == "hello"
+ proc.ok.should == true
+ end
+
+ # JRUBY-5026
+ describe "using a reified block parameter" do
+ it "returns an instance of the subclass" do
+ cls = Class.new do
+ def self.subclass=(subclass)
+ @subclass = subclass
+ end
+ def self.foo(&block)
+ @subclass.new(&block)
+ end
+ end
+ cls.subclass = @subclass
+ proc = cls.foo {"hello"}
+
+ proc.class.should == @subclass
+ proc.call.should == "hello"
+ proc.ok.should == true
+ end
+ end
+ end
+
+ # JRUBY-5261; Proc sets up the block during .new, not in #initialize
+ describe "called on a subclass of Proc that does not 'super' in 'initialize'" do
+ before :each do
+ @subclass = Class.new(Proc) do
+ attr_reader :ok
+ def initialize
+ @ok = true
+ end
+ end
+ end
+
+ it "still constructs a functional proc" do
+ proc = @subclass.new {'ok'}
+ proc.call.should == 'ok'
+ proc.ok.should == true
+ end
+ end
+
+ it "raises a LocalJumpError when context of the block no longer exists" do
+ def some_method
+ Proc.new { return }
+ end
+ res = some_method()
+
+ lambda { res.call }.should raise_error(LocalJumpError)
+ end
+
+ it "returns from within enclosing method when 'return' is used in the block" do
+ # we essentially verify that the created instance behaves like proc,
+ # not like lambda.
+ def some_method
+ Proc.new { return :proc_return_value }.call
+ :method_return_value
+ end
+ some_method.should == :proc_return_value
+ end
+
+ it "returns a subclass of Proc" do
+ obj = ProcSpecs::MyProc.new { }
+ obj.should be_kind_of(ProcSpecs::MyProc)
+ end
+
+ it "calls initialize on the Proc object" do
+ obj = ProcSpecs::MyProc2.new(:a, 2) { }
+ obj.first.should == :a
+ obj.second.should == 2
+ end
+
+ it "returns a new Proc instance from the block passed to the containing method" do
+ prc = ProcSpecs.new_proc_in_method { "hello" }
+ prc.should be_an_instance_of(Proc)
+ prc.call.should == "hello"
+ end
+
+ it "returns a new Proc instance from the block passed to the containing method" do
+ prc = ProcSpecs.new_proc_subclass_in_method { "hello" }
+ prc.should be_an_instance_of(ProcSpecs::ProcSubclass)
+ prc.call.should == "hello"
+ end
+end
+
+describe "Proc.new with a block argument" do
+ it "returns the passed proc created from a block" do
+ passed_prc = Proc.new { "hello".size }
+ prc = Proc.new(&passed_prc)
+
+ prc.should equal(passed_prc)
+ prc.call.should == 5
+ end
+
+ it "returns the passed proc created from a method" do
+ method = "hello".method(:size)
+ passed_prc = Proc.new(&method)
+ prc = Proc.new(&passed_prc)
+
+ prc.should equal(passed_prc)
+ prc.call.should == 5
+ end
+
+ it "returns the passed proc created from a symbol" do
+ passed_prc = Proc.new(&:size)
+ prc = Proc.new(&passed_prc)
+
+ prc.should equal(passed_prc)
+ prc.call("hello").should == 5
+ end
+end
+
+describe "Proc.new with a block argument called indirectly from a subclass" do
+ it "returns the passed proc created from a block" do
+ passed_prc = ProcSpecs::MyProc.new { "hello".size }
+ passed_prc.class.should == ProcSpecs::MyProc
+ prc = ProcSpecs::MyProc.new(&passed_prc)
+
+ prc.should equal(passed_prc)
+ prc.call.should == 5
+ end
+
+ it "returns the passed proc created from a method" do
+ method = "hello".method(:size)
+ passed_prc = ProcSpecs::MyProc.new(&method)
+ passed_prc.class.should == ProcSpecs::MyProc
+ prc = ProcSpecs::MyProc.new(&passed_prc)
+
+ prc.should equal(passed_prc)
+ prc.call.should == 5
+ end
+
+ it "returns the passed proc created from a symbol" do
+ passed_prc = ProcSpecs::MyProc.new(&:size)
+ passed_prc.class.should == ProcSpecs::MyProc
+ prc = ProcSpecs::MyProc.new(&passed_prc)
+
+ prc.should equal(passed_prc)
+ prc.call("hello").should == 5
+ end
+end
+
+describe "Proc.new without a block" do
+ it "raises an ArgumentError" do
+ lambda { Proc.new }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if invoked from within a method with no block" do
+ lambda { ProcSpecs.new_proc_in_method }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if invoked on a subclass from within a method with no block" do
+ lambda { ProcSpecs.new_proc_subclass_in_method }.should raise_error(ArgumentError)
+ end
+
+ it "uses the implicit block from an enclosing method" do
+ def some_method
+ Proc.new
+ end
+
+ prc = some_method { "hello" }
+
+ prc.call.should == "hello"
+ end
+end
diff --git a/spec/ruby/core/proc/parameters_spec.rb b/spec/ruby/core/proc/parameters_spec.rb
new file mode 100644
index 0000000000..4a8958497d
--- /dev/null
+++ b/spec/ruby/core/proc/parameters_spec.rb
@@ -0,0 +1,95 @@
+require_relative '../../spec_helper'
+
+describe "Proc#parameters" do
+ it "returns an empty Array for a proc expecting no parameters" do
+ proc {}.parameters.should == []
+ end
+
+ it "returns an Array of Arrays for a proc expecting parameters" do
+ p = proc {|x| }
+ p.parameters.should be_an_instance_of(Array)
+ p.parameters.first.should be_an_instance_of(Array)
+ end
+
+ it "sets the first element of each sub-Array to :opt for optional arguments" do
+ proc {|x| }.parameters.first.first.should == :opt
+ proc {|y,*x| }.parameters.first.first.should == :opt
+ end
+
+ it "regards named parameters in procs as optional" do
+ proc {|x| }.parameters.first.first.should == :opt
+ end
+
+ it "regards optional keyword parameters in procs as optional" do
+ proc {|x: :y| }.parameters.first.first.should == :key
+ end
+
+ it "regards parameters with default values as optional" do
+ lambda {|x=1| }.parameters.first.first.should == :opt
+ proc {|x=1| }.parameters.first.first.should == :opt
+ end
+
+ it "sets the first element of each sub-Array to :req for required arguments" do
+ lambda {|x,y=[]| }.parameters.first.first.should == :req
+ lambda {|y,*x| }.parameters.first.first.should == :req
+ end
+
+ it "regards named parameters in lambdas as required" do
+ lambda {|x| }.parameters.first.first.should == :req
+ end
+
+ it "regards keyword parameters in lambdas as required" do
+ eval("lambda {|x:| }").parameters.first.first.should == :keyreq
+ end
+
+ it "sets the first element of each sub-Array to :rest for parameters prefixed with asterisks" do
+ lambda {|*x| }.parameters.first.first.should == :rest
+ lambda {|x,*y| }.parameters.last.first.should == :rest
+ proc {|*x| }.parameters.first.first.should == :rest
+ proc {|x,*y| }.parameters.last.first.should == :rest
+ end
+
+ it "sets the first element of each sub-Array to :keyrest for parameters prefixed with double asterisks" do
+ lambda {|**x| }.parameters.first.first.should == :keyrest
+ lambda {|x,**y| }.parameters.last.first.should == :keyrest
+ proc {|**x| }.parameters.first.first.should == :keyrest
+ proc {|x,**y| }.parameters.last.first.should == :keyrest
+ end
+
+ it "sets the first element of each sub-Array to :block for parameters prefixed with ampersands" do
+ lambda {|&x| }.parameters.first.first.should == :block
+ lambda {|x,&y| }.parameters.last.first.should == :block
+ proc {|&x| }.parameters.first.first.should == :block
+ proc {|x,&y| }.parameters.last.first.should == :block
+ end
+
+ it "sets the second element of each sub-Array to the name of the argument" do
+ lambda {|x| }.parameters.first.last.should == :x
+ lambda {|x=Math::PI| }.parameters.first.last.should == :x
+ lambda {|an_argument, glark, &foo| }.parameters[1].last.should == :glark
+ lambda {|*rest| }.parameters.first.last.should == :rest
+ lambda {|&block| }.parameters.first.last.should == :block
+ proc {|x| }.parameters.first.last.should == :x
+ proc {|x=Math::PI| }.parameters.first.last.should == :x
+ proc {|an_argument, glark, &foo| }.parameters[1].last.should == :glark
+ proc {|*rest| }.parameters.first.last.should == :rest
+ proc {|&block| }.parameters.first.last.should == :block
+ end
+
+ it "ignores unnamed rest args" do
+ lambda {|x,|}.parameters.should == [[:req, :x]]
+ end
+
+ it "adds nameless rest arg for \"star\" argument" do
+ lambda {|x,*|}.parameters.should == [[:req, :x], [:rest]]
+ end
+
+ it "does not add locals as block options with a block and splat" do
+ lambda do |*args, &blk|
+ local_is_not_parameter = {}
+ end.parameters.should == [[:rest, :args], [:block, :blk]]
+ proc do |*args, &blk|
+ local_is_not_parameter = {}
+ end.parameters.should == [[:rest, :args], [:block, :blk]]
+ end
+end
diff --git a/spec/ruby/core/proc/shared/call.rb b/spec/ruby/core/proc/shared/call.rb
new file mode 100644
index 0000000000..996d0e055d
--- /dev/null
+++ b/spec/ruby/core/proc/shared/call.rb
@@ -0,0 +1,96 @@
+require_relative '../fixtures/common'
+
+describe :proc_call, shared: true do
+ it "invokes self" do
+ Proc.new { "test!" }.send(@method).should == "test!"
+ lambda { "test!" }.send(@method).should == "test!"
+ proc { "test!" }.send(@method).should == "test!"
+ end
+
+ it "sets self's parameters to the given values" do
+ Proc.new { |a, b| a + b }.send(@method, 1, 2).should == 3
+ Proc.new { |*args| args }.send(@method, 1, 2, 3, 4).should == [1, 2, 3, 4]
+ Proc.new { |_, *args| args }.send(@method, 1, 2, 3).should == [2, 3]
+
+ lambda { |a, b| a + b }.send(@method, 1, 2).should == 3
+ lambda { |*args| args }.send(@method, 1, 2, 3, 4).should == [1, 2, 3, 4]
+ lambda { |_, *args| args }.send(@method, 1, 2, 3).should == [2, 3]
+
+ proc { |a, b| a + b }.send(@method, 1, 2).should == 3
+ proc { |*args| args }.send(@method, 1, 2, 3, 4).should == [1, 2, 3, 4]
+ proc { |_, *args| args }.send(@method, 1, 2, 3).should == [2, 3]
+ end
+end
+
+
+describe :proc_call_on_proc_new, shared: true do
+ it "replaces missing arguments with nil" do
+ Proc.new { |a, b| [a, b] }.send(@method).should == [nil, nil]
+ Proc.new { |a, b| [a, b] }.send(@method, 1).should == [1, nil]
+ end
+
+ it "silently ignores extra arguments" do
+ Proc.new { |a, b| a + b }.send(@method, 1, 2, 5).should == 3
+ end
+
+ it "auto-explodes a single Array argument" do
+ p = Proc.new { |a, b| [a, b] }
+ p.send(@method, 1, 2).should == [1, 2]
+ p.send(@method, [1, 2]).should == [1, 2]
+ p.send(@method, [1, 2, 3]).should == [1, 2]
+ p.send(@method, [1, 2, 3], 4).should == [[1, 2, 3], 4]
+ end
+end
+
+describe :proc_call_on_proc_or_lambda, shared: true do
+ it "ignores excess arguments when self is a proc" do
+ a = proc {|x| x}.send(@method, 1, 2)
+ a.should == 1
+
+ a = proc {|x| x}.send(@method, 1, 2, 3)
+ a.should == 1
+ end
+
+ it "will call #to_ary on argument and return self if return is nil" do
+ argument = ProcSpecs::ToAryAsNil.new
+ result = proc { |x, _| x }.send(@method, argument)
+ result.should == argument
+ end
+
+ it "substitutes nil for missing arguments when self is a proc" do
+ proc {|x,y| [x,y]}.send(@method).should == [nil,nil]
+
+ a = proc {|x,y| [x, y]}.send(@method, 1)
+ a.should == [1,nil]
+ end
+
+ it "raises an ArgumentError on excess arguments when self is a lambda" do
+ lambda {
+ lambda {|x| x}.send(@method, 1, 2)
+ }.should raise_error(ArgumentError)
+
+ lambda {
+ lambda {|x| x}.send(@method, 1, 2, 3)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError on missing arguments when self is a lambda" do
+ lambda {
+ lambda {|x| x}.send(@method)
+ }.should raise_error(ArgumentError)
+
+ lambda {
+ lambda {|x,y| [x,y]}.send(@method, 1)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "treats a single Array argument as a single argument when self is a lambda" do
+ lambda { |a| a }.send(@method, [1, 2]).should == [1, 2]
+ lambda { |a, b| [a, b] }.send(@method, [1, 2], 3).should == [[1,2], 3]
+ end
+
+ it "treats a single Array argument as a single argument when self is a proc" do
+ proc { |a| a }.send(@method, [1, 2]).should == [1, 2]
+ proc { |a, b| [a, b] }.send(@method, [1, 2], 3).should == [[1,2], 3]
+ end
+end
diff --git a/spec/ruby/core/proc/shared/call_arguments.rb b/spec/ruby/core/proc/shared/call_arguments.rb
new file mode 100644
index 0000000000..2e510b194e
--- /dev/null
+++ b/spec/ruby/core/proc/shared/call_arguments.rb
@@ -0,0 +1,7 @@
+describe :proc_call_block_args, shared: true do
+ it "can receive block arguments" do
+ Proc.new {|&b| b.send(@method)}.send(@method) {1 + 1}.should == 2
+ lambda {|&b| b.send(@method)}.send(@method) {1 + 1}.should == 2
+ proc {|&b| b.send(@method)}.send(@method) {1 + 1}.should == 2
+ end
+end
diff --git a/spec/ruby/core/proc/shared/dup.rb b/spec/ruby/core/proc/shared/dup.rb
new file mode 100644
index 0000000000..fb6fff299d
--- /dev/null
+++ b/spec/ruby/core/proc/shared/dup.rb
@@ -0,0 +1,10 @@
+describe :proc_dup, shared: true do
+ it "returns a copy of self" do
+ a = lambda { "hello" }
+ b = a.send(@method)
+
+ a.should_not equal(b)
+
+ a.call.should == b.call
+ end
+end
diff --git a/spec/ruby/core/proc/shared/equal.rb b/spec/ruby/core/proc/shared/equal.rb
new file mode 100644
index 0000000000..a5d067cea3
--- /dev/null
+++ b/spec/ruby/core/proc/shared/equal.rb
@@ -0,0 +1,100 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/common'
+
+describe :proc_equal, shared: true do
+ it "is a public method" do
+ Proc.should have_public_instance_method(@method, false)
+ end
+
+ it "returns true if self and other are the same object" do
+ p = proc { :foo }
+ p.send(@method, p).should be_true
+
+ p = Proc.new { :foo }
+ p.send(@method, p).should be_true
+
+ p = lambda { :foo }
+ p.send(@method, p).should be_true
+ end
+
+ it "returns true if other is a dup of the original" do
+ p = proc { :foo }
+ p.send(@method, p.dup).should be_true
+
+ p = Proc.new { :foo }
+ p.send(@method, p.dup).should be_true
+
+ p = lambda { :foo }
+ p.send(@method, p.dup).should be_true
+ end
+
+ # identical here means the same method invocation.
+ it "returns false when bodies are the same but capture env is not identical" do
+ a = ProcSpecs.proc_for_1
+ b = ProcSpecs.proc_for_1
+
+ a.send(@method, b).should be_false
+ end
+
+ it "returns true if both procs have the same body and environment" do
+ p = proc { :foo }
+ p2 = proc { :foo }
+ p.send(@method, p2).should be_true
+ end
+
+ it "returns true if both lambdas with the same body and environment" do
+ x = lambda { :foo }
+ x2 = lambda { :foo }
+ x.send(@method, x2).should be_true
+ end
+
+ it "returns true if both different kinds of procs with the same body and env" do
+ p = lambda { :foo }
+ p2 = proc { :foo }
+ p.send(@method, p2).should be_true
+
+ x = proc { :bar }
+ x2 = lambda { :bar }
+ x.send(@method, x2).should be_true
+ end
+
+ it "returns false if other is not a Proc" do
+ p = proc { :foo }
+ p.send(@method, []).should be_false
+
+ p = Proc.new { :foo }
+ p.send(@method, Object.new).should be_false
+
+ p = lambda { :foo }
+ p.send(@method, :foo).should be_false
+ end
+
+ it "returns false if self and other are both procs but have different bodies" do
+ p = proc { :bar }
+ p2 = proc { :foo }
+ p.send(@method, p2).should be_false
+ end
+
+ it "returns false if self and other are both lambdas but have different bodies" do
+ p = lambda { :foo }
+ p2 = lambda { :bar }
+ p.send(@method, p2).should be_false
+ end
+end
+
+describe :proc_equal_undefined, shared: true do
+ it "is not defined" do
+ Proc.should_not have_instance_method(@method, false)
+ end
+
+ it "returns false if other is a dup of the original" do
+ p = proc { :foo }
+ p.send(@method, p.dup).should be_false
+
+ p = Proc.new { :foo }
+ p.send(@method, p.dup).should be_false
+
+ p = lambda { :foo }
+ p.send(@method, p.dup).should be_false
+ end
+end
diff --git a/spec/ruby/core/proc/shared/to_s.rb b/spec/ruby/core/proc/shared/to_s.rb
new file mode 100644
index 0000000000..530eaff3a0
--- /dev/null
+++ b/spec/ruby/core/proc/shared/to_s.rb
@@ -0,0 +1,45 @@
+describe :proc_to_s, shared: true do
+ describe "for a proc created with Proc.new" do
+ it "returns a description optionally including file and line number" do
+ Proc.new { "hello" }.send(@method).should =~ /^#<Proc:([^ ]*?)(@([^ ]*)\/to_s\.rb:4)?>$/
+ end
+
+ it "has an ASCII-8BIT encoding" do
+ Proc.new { "hello" }.send(@method).encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+
+ describe "for a proc created with lambda" do
+ it "returns a description including '(lambda)' and optionally including file and line number" do
+ lambda { "hello" }.send(@method).should =~ /^#<Proc:([^ ]*?)(@([^ ]*)\/to_s\.rb:10)? \(lambda\)>$/
+ end
+
+ it "has an ASCII-8BIT encoding" do
+ lambda { "hello" }.send(@method).encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+
+ describe "for a proc created with proc" do
+ it "returns a description optionally including file and line number" do
+ proc { "hello" }.send(@method).should =~ /^#<Proc:([^ ]*?)(@([^ ]*)\/to_s\.rb:16)?>$/
+ end
+
+ it "has an ASCII-8BIT encoding" do
+ proc { "hello" }.send(@method).encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+
+ describe "for a proc created with UnboundMethod#to_proc" do
+ it "returns a description including '(lambda)' and optionally including file and line number" do
+ def hello; end
+
+ method("hello").to_proc.send(@method).should =~ /^#<Proc:([^ ]*?)(@([^ ]*)\/to_s\.rb:22)? \(lambda\)>$/
+ end
+
+ it "has an ASCII-8BIT encoding" do
+ def hello; end
+
+ method("hello").to_proc.send(@method).encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+end
diff --git a/spec/ruby/core/proc/source_location_spec.rb b/spec/ruby/core/proc/source_location_spec.rb
new file mode 100644
index 0000000000..89409c357b
--- /dev/null
+++ b/spec/ruby/core/proc/source_location_spec.rb
@@ -0,0 +1,86 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/source_location'
+
+describe "Proc#source_location" do
+ before :each do
+ @proc = ProcSpecs::SourceLocation.my_proc
+ @lambda = ProcSpecs::SourceLocation.my_lambda
+ @proc_new = ProcSpecs::SourceLocation.my_proc_new
+ @method = ProcSpecs::SourceLocation.my_method
+ end
+
+ it "returns an Array" do
+ @proc.source_location.should be_an_instance_of(Array)
+ @proc_new.source_location.should be_an_instance_of(Array)
+ @lambda.source_location.should be_an_instance_of(Array)
+ @method.source_location.should be_an_instance_of(Array)
+ end
+
+ it "sets the first value to the path of the file in which the proc was defined" do
+ file = @proc.source_location.first
+ file.should be_an_instance_of(String)
+ file.should == File.realpath('../fixtures/source_location.rb', __FILE__)
+
+ file = @proc_new.source_location.first
+ file.should be_an_instance_of(String)
+ file.should == File.realpath('../fixtures/source_location.rb', __FILE__)
+
+ file = @lambda.source_location.first
+ file.should be_an_instance_of(String)
+ file.should == File.realpath('../fixtures/source_location.rb', __FILE__)
+
+ file = @method.source_location.first
+ file.should be_an_instance_of(String)
+ file.should == File.realpath('../fixtures/source_location.rb', __FILE__)
+ end
+
+ it "sets the last value to a Fixnum representing the line on which the proc was defined" do
+ line = @proc.source_location.last
+ line.should be_an_instance_of(Fixnum)
+ line.should == 4
+
+ line = @proc_new.source_location.last
+ line.should be_an_instance_of(Fixnum)
+ line.should == 12
+
+ line = @lambda.source_location.last
+ line.should be_an_instance_of(Fixnum)
+ line.should == 8
+
+ line = @method.source_location.last
+ line.should be_an_instance_of(Fixnum)
+ line.should == 15
+ end
+
+ it "works even if the proc was created on the same line" do
+ proc { true }.source_location.should == [__FILE__, __LINE__]
+ Proc.new { true }.source_location.should == [__FILE__, __LINE__]
+ lambda { true }.source_location.should == [__FILE__, __LINE__]
+ end
+
+ it "returns the first line of a multi-line proc (i.e. the line containing 'proc do')" do
+ ProcSpecs::SourceLocation.my_multiline_proc.source_location.last.should == 20
+ ProcSpecs::SourceLocation.my_multiline_proc_new.source_location.last.should == 34
+ ProcSpecs::SourceLocation.my_multiline_lambda.source_location.last.should == 27
+ end
+
+ it "returns the location of the proc's body; not necessarily the proc itself" do
+ ProcSpecs::SourceLocation.my_detached_proc.source_location.last.should == 41
+ ProcSpecs::SourceLocation.my_detached_proc_new.source_location.last.should == 51
+ ProcSpecs::SourceLocation.my_detached_lambda.source_location.last.should == 46
+ end
+
+ it "returns the same value for a proc-ified method as the method reports" do
+ method = ProcSpecs::SourceLocation.method(:my_proc)
+ proc = method.to_proc
+
+ method.source_location.should == proc.source_location
+ end
+
+ it "returns nil for a core method that has been proc-ified" do
+ method = [].method(:<<)
+ proc = method.to_proc
+
+ proc.source_location.should == nil
+ end
+end
diff --git a/spec/ruby/core/proc/to_proc_spec.rb b/spec/ruby/core/proc/to_proc_spec.rb
new file mode 100644
index 0000000000..24e66760e4
--- /dev/null
+++ b/spec/ruby/core/proc/to_proc_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Proc#to_proc" do
+ it "returns self" do
+ [Proc.new {}, lambda {}, proc {}].each { |p|
+ p.to_proc.should equal(p)
+ }
+ end
+end
diff --git a/spec/ruby/core/proc/to_s_spec.rb b/spec/ruby/core/proc/to_s_spec.rb
new file mode 100644
index 0000000000..5e9c46b6b8
--- /dev/null
+++ b/spec/ruby/core/proc/to_s_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_s'
+
+describe "Proc#to_s" do
+ it_behaves_like :proc_to_s, :to_s
+end
diff --git a/spec/ruby/core/proc/yield_spec.rb b/spec/ruby/core/proc/yield_spec.rb
new file mode 100644
index 0000000000..365d5b04bd
--- /dev/null
+++ b/spec/ruby/core/proc/yield_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require_relative 'shared/call'
+require_relative 'shared/call_arguments'
+
+describe "Proc#yield" do
+ it_behaves_like :proc_call, :yield
+ it_behaves_like :proc_call_block_args, :yield
+end
+
+describe "Proc#yield on a Proc created with Proc.new" do
+ it_behaves_like :proc_call_on_proc_new, :yield
+end
+
+describe "Proc#yield on a Proc created with Kernel#lambda or Kernel#proc" do
+ it_behaves_like :proc_call_on_proc_or_lambda, :yield
+end
diff --git a/spec/ruby/core/process/abort_spec.rb b/spec/ruby/core/process/abort_spec.rb
new file mode 100644
index 0000000000..1b6ad1da43
--- /dev/null
+++ b/spec/ruby/core/process/abort_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/process/abort'
+
+describe "Process.abort" do
+ it_behaves_like :process_abort, :abort, Process
+end
diff --git a/spec/ruby/core/process/clock_gettime_spec.rb b/spec/ruby/core/process/clock_gettime_spec.rb
new file mode 100644
index 0000000000..165f0db730
--- /dev/null
+++ b/spec/ruby/core/process/clock_gettime_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+
+describe "Process.clock_gettime" do
+ describe 'time units' do
+ it 'handles a fixed set of time units' do
+ [:nanosecond, :microsecond, :millisecond, :second].each do |unit|
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, unit).should be_kind_of(Integer)
+ end
+
+ [:float_microsecond, :float_millisecond, :float_second].each do |unit|
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, unit).should be_an_instance_of(Float)
+ end
+ end
+
+ it 'raises an ArgumentError for an invalid time unit' do
+ lambda { Process.clock_gettime(Process::CLOCK_MONOTONIC, :bad) }.should raise_error(ArgumentError)
+ end
+
+ it 'defaults to :float_second' do
+ t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second)
+
+ t1.should be_an_instance_of(Float)
+ t2.should be_close(t1, 2.0) # 2.0 is chosen arbitrarily to allow for time skew without admitting failure cases, which would be off by an order of magnitude.
+ end
+
+ it 'uses the default time unit (:float_second) when passed nil' do
+ t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC, nil)
+ t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second)
+
+ t1.should be_an_instance_of(Float)
+ t2.should be_close(t1, 2.0) # 2.0 is chosen arbitrarily to allow for time skew without admitting failure cases, which would be off by an order of magnitude.
+ end
+ end
+end
diff --git a/spec/ruby/core/process/constants_spec.rb b/spec/ruby/core/process/constants_spec.rb
new file mode 100644
index 0000000000..2fa93ad8bf
--- /dev/null
+++ b/spec/ruby/core/process/constants_spec.rb
@@ -0,0 +1,63 @@
+
+describe "Process::Constants" do
+ platform_is :darwin, :netbsd, :freebsd do
+ it "has the correct constant values on BSD-like systems" do
+ Process::WNOHANG.should == 1
+ Process::WUNTRACED.should == 2
+ Process::PRIO_PROCESS.should == 0
+ Process::PRIO_PGRP.should == 1
+ Process::PRIO_USER.should == 2
+ Process::RLIM_INFINITY.should == 9223372036854775807
+ Process::RLIMIT_CPU.should == 0
+ Process::RLIMIT_FSIZE.should == 1
+ Process::RLIMIT_DATA.should == 2
+ Process::RLIMIT_STACK.should == 3
+ Process::RLIMIT_CORE.should == 4
+ Process::RLIMIT_RSS.should == 5
+ Process::RLIMIT_MEMLOCK.should == 6
+ Process::RLIMIT_NPROC.should == 7
+ Process::RLIMIT_NOFILE.should == 8
+ end
+ end
+
+ platform_is :darwin do
+ it "has the correct constant values on Darwin" do
+ Process::RLIM_SAVED_MAX.should == 9223372036854775807
+ Process::RLIM_SAVED_CUR.should == 9223372036854775807
+ Process::RLIMIT_AS.should == 5
+ end
+ end
+
+ platform_is :linux do
+ it "has the correct constant values on Linux" do
+ Process::WNOHANG.should == 1
+ Process::WUNTRACED.should == 2
+ Process::PRIO_PROCESS.should == 0
+ Process::PRIO_PGRP.should == 1
+ Process::PRIO_USER.should == 2
+ Process::RLIMIT_CPU.should == 0
+ Process::RLIMIT_FSIZE.should == 1
+ Process::RLIMIT_DATA.should == 2
+ Process::RLIMIT_STACK.should == 3
+ Process::RLIMIT_CORE.should == 4
+ Process::RLIMIT_RSS.should == 5
+ Process::RLIMIT_NPROC.should == 6
+ Process::RLIMIT_NOFILE.should == 7
+ Process::RLIMIT_MEMLOCK.should == 8
+ Process::RLIMIT_AS.should == 9
+
+ # These values appear to change according to the platform.
+ values = [4294967295, 9223372036854775807, 18446744073709551615]
+ values.include?(Process::RLIM_INFINITY).should be_true
+ values.include?(Process::RLIM_SAVED_MAX).should be_true
+ values.include?(Process::RLIM_SAVED_CUR).should be_true
+ end
+ end
+
+ platform_is :netbsd, :freebsd do
+ it "Process::RLIMIT_SBSIZE" do
+ Process::RLIMIT_SBSIZE.should == 9 # FIXME: what's it equal?
+ Process::RLIMIT_AS.should == 10
+ end
+ end
+end
diff --git a/spec/ruby/core/process/daemon_spec.rb b/spec/ruby/core/process/daemon_spec.rb
new file mode 100644
index 0000000000..9567382d3d
--- /dev/null
+++ b/spec/ruby/core/process/daemon_spec.rb
@@ -0,0 +1,123 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+platform_is_not :windows do
+ 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 == ""
+ end
+
+ it "redirects stderr to /dev/null" do
+ @daemon.invoke("keep_stdio_open_false_stderr", @object).should == ""
+ end
+
+ it "redirects stdin to /dev/null" do
+ @daemon.invoke("keep_stdio_open_false_stdin", @object).should == ""
+ end
+
+ it "does not close open files" do
+ @daemon.invoke("keep_stdio_open_files", @object).should == "false"
+ end
+ end
+
+ describe :process_daemon_keep_stdio_open_true, shared: true do
+ it "does not redirect stdout to /dev/null" do
+ @daemon.invoke("keep_stdio_open_true_stdout", @object).should == "writing to stdout"
+ end
+
+ it "does not redirect stderr to /dev/null" do
+ @daemon.invoke("keep_stdio_open_true_stderr", @object).should == "writing to stderr"
+ end
+
+ it "does not redirect stdin to /dev/null" do
+ @daemon.invoke("keep_stdio_open_true_stdin", @object).should == "reading from stdin"
+ end
+
+ it "does not close open files" do
+ @daemon.invoke("keep_stdio_open_files", @object).should == "false"
+ end
+ end
+
+ describe "Process.daemon" do
+ before :each do
+ @invoke_dir = Dir.pwd
+ @daemon = ProcessSpecs::Daemonizer.new
+ end
+
+ after :each do
+ rm_r @daemon.input, @daemon.data if @daemon
+ end
+
+ it "returns 0" do
+ @daemon.invoke("return_value").should == "0"
+ end
+
+ it "has a different PID after daemonizing" do
+ parent, daemon = @daemon.invoke("pid").split(":")
+ parent.should_not == daemon
+ end
+
+ it "has a different process group after daemonizing" do
+ parent, daemon = @daemon.invoke("process_group").split(":")
+ parent.should_not == daemon
+ end
+
+ it "does not run existing at_exit handlers when daemonizing" do
+ @daemon.invoke("daemonizing_at_exit").should == "not running at_exit"
+ end
+
+ it "runs at_exit handlers when the daemon exits" do
+ @daemon.invoke("daemon_at_exit").should == "running at_exit"
+ end
+
+ it "changes directory to the root directory if the first argument is not given" do
+ @daemon.invoke("stay_in_dir").should == "/"
+ end
+
+ it "changes directory to the root directory if the first argument is false" do
+ @daemon.invoke("stay_in_dir", [false]).should == "/"
+ end
+
+ it "changes directory to the root directory if the first argument is nil" do
+ @daemon.invoke("stay_in_dir", [nil]).should == "/"
+ end
+
+ it "does not change to the root directory if the first argument is true" do
+ @daemon.invoke("stay_in_dir", [true]).should == @invoke_dir
+ end
+
+ it "does not change to the root directory if the first argument is non-false" do
+ @daemon.invoke("stay_in_dir", [:yes]).should == @invoke_dir
+ end
+
+ describe "when the second argument is not given" do
+ it_behaves_like :process_daemon_keep_stdio_open_false, nil, [false]
+ end
+
+ describe "when the second argument is false" do
+ it_behaves_like :process_daemon_keep_stdio_open_false, nil, [false, false]
+ end
+
+ describe "when the second argument is nil" do
+ it_behaves_like :process_daemon_keep_stdio_open_false, nil, [false, nil]
+ end
+
+ describe "when the second argument is true" do
+ it_behaves_like :process_daemon_keep_stdio_open_true, nil, [false, true]
+ end
+
+ describe "when the second argument is non-false" do
+ it_behaves_like :process_daemon_keep_stdio_open_true, nil, [false, :yes]
+ end
+ end
+end
+
+platform_is :windows do
+ describe "Process.daemon" do
+ it "raises a NotImplementedError" do
+ lambda {
+ Process.daemon
+ }.should raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/ruby/core/process/detach_spec.rb b/spec/ruby/core/process/detach_spec.rb
new file mode 100644
index 0000000000..74169a2fed
--- /dev/null
+++ b/spec/ruby/core/process/detach_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../../spec_helper'
+
+describe "Process.detach" do
+ platform_is_not :windows do
+ it "returns a thread" do
+ pid = Process.fork { Process.exit! }
+ thr = Process.detach(pid)
+ thr.should be_kind_of(Thread)
+ thr.join
+ end
+
+ it "produces the exit Process::Status as the thread value" do
+ pid = Process.fork { Process.exit! }
+ thr = Process.detach(pid)
+ thr.join
+
+ status = thr.value
+ status.should be_kind_of(Process::Status)
+ status.pid.should == pid
+ end
+
+ platform_is_not :openbsd do
+ it "reaps the child process's status automatically" do
+ pid = Process.fork { Process.exit! }
+ Process.detach(pid).join
+ lambda { Process.waitpid(pid) }.should raise_error(Errno::ECHILD)
+ end
+ end
+
+ it "sets the :pid thread-local to the PID" do
+ pid = Process.fork { Process.exit! }
+ thr = Process.detach(pid)
+ thr.join
+
+ thr[:pid].should == pid
+ end
+
+ it "provides a #pid method on the returned thread which returns the PID" do
+ pid = Process.fork { Process.exit! }
+ thr = Process.detach(pid)
+ thr.join
+
+ thr.pid.should == pid
+ end
+ end
+end
diff --git a/spec/ruby/core/process/egid_spec.rb b/spec/ruby/core/process/egid_spec.rb
new file mode 100644
index 0000000000..24dda43804
--- /dev/null
+++ b/spec/ruby/core/process/egid_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+describe "Process.egid" do
+ it "returns the effective group ID for this process" do
+ Process.egid.should be_kind_of(Integer)
+ end
+
+ it "also goes by Process::GID.eid" do
+ Process::GID.eid.should == Process.egid
+ end
+
+ it "also goes by Process::Sys.getegid" do
+ Process::Sys.getegid.should == Process.egid
+ end
+end
+
+describe "Process.egid=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/euid_spec.rb b/spec/ruby/core/process/euid_spec.rb
new file mode 100644
index 0000000000..bc3f52592e
--- /dev/null
+++ b/spec/ruby/core/process/euid_spec.rb
@@ -0,0 +1,59 @@
+require_relative '../../spec_helper'
+
+describe "Process.euid" do
+ it "returns the effective user ID for this process" do
+ Process.euid.should be_kind_of(Fixnum)
+ end
+
+ it "also goes by Process::UID.eid" do
+ Process::UID.eid.should == Process.euid
+ end
+
+ it "also goes by Process::Sys.geteuid" do
+ Process::Sys.geteuid.should == Process.euid
+ end
+end
+
+describe "Process.euid=" do
+
+ platform_is_not :windows do
+ it "raises TypeError if not passed an Integer" do
+ lambda { Process.euid = Object.new }.should raise_error(TypeError)
+ end
+
+ as_user do
+ it "raises Errno::ERPERM if run by a non superuser trying to set the superuser id" do
+ lambda { (Process.euid = 0)}.should raise_error(Errno::EPERM)
+ end
+
+ it "raises Errno::ERPERM if run by a non superuser trying to set the superuser id from username" do
+ lambda { Process.euid = "root" }.should raise_error(Errno::EPERM)
+ end
+ end
+
+ as_superuser do
+ describe "if run by a superuser" do
+ with_feature :fork do
+ it "sets the effective user id for the current process if run by a superuser" do
+ read, write = IO.pipe
+ pid = Process.fork do
+ begin
+ read.close
+ Process.euid = 1
+ write << Process.euid
+ write.close
+ rescue Exception => e
+ write << e << e.backtrace
+ end
+ Process.exit!
+ end
+ write.close
+ euid = read.gets
+ euid.should == "1"
+ Process.wait pid
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/process/exec_spec.rb b/spec/ruby/core/process/exec_spec.rb
new file mode 100644
index 0000000000..098898d42e
--- /dev/null
+++ b/spec/ruby/core/process/exec_spec.rb
@@ -0,0 +1,218 @@
+require_relative '../../spec_helper'
+
+describe "Process.exec" do
+ it "raises Errno::ENOENT for an empty string" do
+ lambda { Process.exec "" }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises Errno::ENOENT for a command which does not exist" do
+ lambda { Process.exec "bogus-noent-script.sh" }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises an ArgumentError if the command includes a null byte" do
+ lambda { Process.exec "\000" }.should raise_error(ArgumentError)
+ end
+
+ unless File.executable?(__FILE__) # Some FS (e.g. vboxfs) locate all files executable
+ platform_is_not :windows do
+ it "raises Errno::EACCES when the file does not have execute permissions" do
+ lambda { Process.exec __FILE__ }.should raise_error(Errno::EACCES)
+ end
+ end
+
+ platform_is :windows do
+ it "raises Errno::EACCES or Errno::ENOEXEC when the file is not an executable file" do
+ lambda { Process.exec __FILE__ }.should raise_error(SystemCallError) { |e|
+ [Errno::EACCES, Errno::ENOEXEC].should include(e.class)
+ }
+ end
+ end
+ end
+
+ platform_is_not :openbsd do
+ it "raises Errno::EACCES when passed a directory" do
+ lambda { Process.exec File.dirname(__FILE__) }.should raise_error(Errno::EACCES)
+ end
+ end
+
+ platform_is :openbsd do
+ it "raises Errno::EISDIR when passed a directory" do
+ lambda { Process.exec File.dirname(__FILE__) }.should raise_error(Errno::EISDIR)
+ end
+ end
+
+ it "runs the specified command, replacing current process" do
+ ruby_exe('Process.exec "echo hello"; puts "fail"', escape: true).should == "hello\n"
+ end
+
+ it "sets the current directory when given the :chdir option" do
+ tmpdir = tmp("")[0..-2]
+ platform_is_not :windows do
+ ruby_exe("Process.exec(\"pwd\", chdir: #{tmpdir.inspect})", escape: true).should == "#{tmpdir}\n"
+ end
+ platform_is :windows do
+ ruby_exe("Process.exec(\"cd\", chdir: #{tmpdir.inspect})", escape: true).tr('\\', '/').should == "#{tmpdir}\n"
+ end
+ end
+
+ it "flushes STDOUT upon exit when it's not set to sync" do
+ ruby_exe("STDOUT.sync = false; STDOUT.write 'hello'").should == "hello"
+ end
+
+ it "flushes STDERR upon exit when it's not set to sync" do
+ ruby_exe("STDERR.sync = false; STDERR.write 'hello'", args: "2>&1").should == "hello"
+ end
+
+ describe "with a single argument" do
+ before :each do
+ @dir = tmp("exec_with_dir", false)
+ Dir.mkdir @dir
+
+ @name = "some_file"
+ @path = tmp("exec_with_dir/#{@name}", false)
+ touch @path
+ end
+
+ after :each do
+ rm_r @path
+ rm_r @dir
+ end
+
+ platform_is_not :windows do
+ it "subjects the specified command to shell expansion" do
+ result = Dir.chdir(@dir) do
+ ruby_exe('Process.exec "echo *"', escape: true)
+ end
+ result.chomp.should == @name
+ end
+
+ it "creates an argument array with shell parsing semantics for whitespace" do
+ ruby_exe('Process.exec "echo a b c d"', escape: true).should == "a b c d\n"
+ end
+ end
+
+ platform_is :windows do
+ # There is no shell expansion on Windows
+ it "does not subject the specified command to shell expansion on Windows" do
+ result = Dir.chdir(@dir) do
+ ruby_exe('Process.exec "echo *"', escape: true)
+ end
+ result.should == "*\n"
+ end
+
+ it "does not create an argument array with shell parsing semantics for whitespace on Windows" do
+ ruby_exe('Process.exec "echo a b c d"', escape: true).should == "a b c d\n"
+ end
+ end
+
+ end
+
+ describe "with multiple arguments" do
+ it "does not subject the arguments to shell expansion" do
+ cmd = '"echo", "*"'
+ platform_is :windows do
+ cmd = '"cmd.exe", "/C", "echo", "*"'
+ end
+ ruby_exe("Process.exec #{cmd}", escape: true).should == "*\n"
+ end
+ end
+
+ describe "(environment variables)" do
+ before :each do
+ ENV["FOO"] = "FOO"
+ end
+
+ after :each do
+ ENV["FOO"] = nil
+ end
+
+ var = '$FOO'
+ platform_is :windows do
+ var = '%FOO%'
+ end
+
+ it "sets environment variables in the child environment" do
+ ruby_exe('Process.exec({"FOO" => "BAR"}, "echo ' + var + '")', escape: true).should == "BAR\n"
+ end
+
+ it "unsets environment variables whose value is nil" do
+ platform_is_not :windows do
+ ruby_exe('Process.exec({"FOO" => nil}, "echo ' + var + '")', escape: true).should == "\n"
+ end
+ platform_is :windows do
+ # On Windows, echo-ing a non-existent env var is treated as echo-ing any other string of text
+ ruby_exe('Process.exec({"FOO" => nil}, "echo ' + var + '")', escape: true).should == var + "\n"
+ end
+ end
+
+ it "coerces environment argument using to_hash" do
+ ruby_exe('o = Object.new; def o.to_hash; {"FOO" => "BAR"}; end; Process.exec(o, "echo ' + var + '")', escape: true).should == "BAR\n"
+ end
+
+ it "unsets other environment variables when given a true :unsetenv_others option" do
+ platform_is_not :windows do
+ ruby_exe('Process.exec("echo ' + var + '", unsetenv_others: true)', escape: true).should == "\n"
+ end
+ platform_is :windows do
+ ruby_exe('Process.exec("' + ENV['COMSPEC'].gsub('\\', '\\\\\\') + ' /C echo ' + var + '", unsetenv_others: true)', escape: true).should == var + "\n"
+ end
+ end
+ end
+
+ describe "with a command array" do
+ it "uses the first element as the command name and the second as the argv[0] value" do
+ platform_is_not :windows do
+ ruby_exe('Process.exec(["/bin/sh", "argv_zero"], "-c", "echo $0")', escape: true).should == "argv_zero\n"
+ end
+ platform_is :windows do
+ ruby_exe('Process.exec(["cmd.exe", "/C"], "/C", "echo", "argv_zero")', escape: true).should == "argv_zero\n"
+ end
+ end
+
+ it "coerces the argument using to_ary" do
+ platform_is_not :windows do
+ ruby_exe('o = Object.new; def o.to_ary; ["/bin/sh", "argv_zero"]; end; Process.exec(o, "-c", "echo $0")', escape: true).should == "argv_zero\n"
+ end
+ platform_is :windows do
+ ruby_exe('o = Object.new; def o.to_ary; ["cmd.exe", "/C"]; end; Process.exec(o, "/C", "echo", "argv_zero")', escape: true).should == "argv_zero\n"
+ end
+ end
+
+ it "raises an ArgumentError if the Array does not have exactly two elements" do
+ lambda { Process.exec([]) }.should raise_error(ArgumentError)
+ lambda { Process.exec([:a]) }.should raise_error(ArgumentError)
+ lambda { Process.exec([:a, :b, :c]) }.should raise_error(ArgumentError)
+ end
+ end
+
+ platform_is_not :windows do
+ describe "with an options Hash" do
+ describe "with Integer option keys" do
+ before :each do
+ @name = tmp("exec_fd_map.txt")
+ @child_fd_file = tmp("child_fd_file.txt")
+ end
+
+ after :each do
+ rm_r @name, @child_fd_file
+ end
+
+ it "maps the key to a file descriptor in the child that inherits the file descriptor from the parent specified by the value" do
+ map_fd_fixture = fixture __FILE__, "map_fd.rb"
+ cmd = <<-EOC
+ f = File.open("#{@name}", "w+")
+ child_fd = f.fileno + 1
+ File.open("#{@child_fd_file}", "w") { |io| io.print child_fd }
+ Process.exec "#{ruby_cmd(map_fd_fixture)} \#{child_fd}", { child_fd => f }
+ EOC
+
+ ruby_exe(cmd, escape: true)
+ child_fd = IO.read(@child_fd_file).to_i
+ child_fd.to_i.should > STDERR.fileno
+
+ File.read(@name).should == "writing to fd: #{child_fd}"
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/process/exit_spec.rb b/spec/ruby/core/process/exit_spec.rb
new file mode 100644
index 0000000000..70d79d789d
--- /dev/null
+++ b/spec/ruby/core/process/exit_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/process/exit'
+
+describe "Process.exit" do
+ it_behaves_like :process_exit, :exit, Process
+end
+
+describe "Process.exit!" do
+ it_behaves_like :process_exit!, :exit!, Process
+end
diff --git a/spec/ruby/core/process/fixtures/common.rb b/spec/ruby/core/process/fixtures/common.rb
new file mode 100644
index 0000000000..bdbf1e654b
--- /dev/null
+++ b/spec/ruby/core/process/fixtures/common.rb
@@ -0,0 +1,84 @@
+module ProcessSpecs
+ def self.use_system_ruby(context)
+ if defined?(MSpecScript::SYSTEM_RUBY)
+ context.send(:before, :all) do
+ @ruby = ::RUBY_EXE
+ Object.const_set(:RUBY_EXE, MSpecScript::SYSTEM_RUBY)
+ end
+
+ context.send(:after, :all) do
+ Object.const_set(:RUBY_EXE, @ruby)
+ end
+ end
+ end
+
+ class Daemonizer
+ attr_reader :input, :data
+
+ def initialize
+ # Fast feedback for implementations without Process.daemon
+ raise NotImplementedError, "Process.daemon is not implemented" unless Process.respond_to? :daemon
+
+ @script = fixture __FILE__, "daemon.rb"
+ @input = tmp("process_daemon_input_file")
+ @data = tmp("process_daemon_data_file")
+ @args = []
+ end
+
+ def wait_for_daemon
+ sleep 0.001 until File.exist?(@data) and File.size?(@data)
+ end
+
+ def invoke(behavior, arguments=[])
+ args = Marshal.dump(arguments).unpack("H*")
+ args << @input << @data << behavior
+
+ ruby_exe @script, args: args
+
+ wait_for_daemon
+
+ return unless File.exist? @data
+
+ File.open(@data, "rb") { |f| return f.read.chomp }
+ end
+ end
+
+ class Signalizer
+ attr_reader :pid_file, :pid
+
+ def initialize(scenario=nil)
+ platform_is :windows do
+ fail "not supported on windows"
+ end
+ @script = fixture __FILE__, "kill.rb"
+ @pid = nil
+ @pid_file = tmp("process_kill_signal_file")
+ rm_r @pid_file
+
+ @thread = Thread.new do
+ Thread.current.abort_on_exception = true
+ args = [@pid_file]
+ args << scenario if scenario
+ @result = ruby_exe @script, args: args
+ end
+ Thread.pass while @thread.status and !File.exist?(@pid_file)
+ while @thread.status && (@pid.nil? || @pid == 0)
+ @pid = IO.read(@pid_file).chomp.to_i
+ end
+ end
+
+ def wait_on_result
+ @thread.join
+ end
+
+ def cleanup
+ wait_on_result
+ rm_r pid_file
+ end
+
+ def result
+ wait_on_result
+ @result.chomp if @result
+ end
+ end
+end
diff --git a/spec/ruby/core/process/fixtures/daemon.rb b/spec/ruby/core/process/fixtures/daemon.rb
new file mode 100644
index 0000000000..772df2d09e
--- /dev/null
+++ b/spec/ruby/core/process/fixtures/daemon.rb
@@ -0,0 +1,111 @@
+module ProcessSpecs
+ class Daemon
+ def initialize(argv)
+ args, @input, @data, @behavior = argv
+ @args = Marshal.load [args].pack("H*")
+ @no_at_exit = false
+ end
+
+ def run
+ send @behavior
+
+ # Exit without running any at_exit handlers
+ exit!(0) if @no_at_exit
+ end
+
+ def write(data)
+ File.open(@data, "wb") { |f| f.puts data }
+ end
+
+ def daemonizing_at_exit
+ at_exit do
+ write "running at_exit"
+ end
+
+ @no_at_exit = true
+ Process.daemon
+ write "not running at_exit"
+ end
+
+ def return_value
+ write Process.daemon.to_s
+ end
+
+ def pid
+ parent = Process.pid
+ Process.daemon
+ daemon = Process.pid
+ write "#{parent}:#{daemon}"
+ end
+
+ def process_group
+ parent = Process.getpgrp
+ Process.daemon
+ daemon = Process.getpgrp
+ write "#{parent}:#{daemon}"
+ end
+
+ def daemon_at_exit
+ at_exit do
+ write "running at_exit"
+ end
+
+ Process.daemon
+ end
+
+ def stay_in_dir
+ Process.daemon(*@args)
+ write Dir.pwd
+ end
+
+ def keep_stdio_open_false_stdout
+ Process.daemon(*@args)
+ $stdout.write "writing to stdout"
+ write ""
+ end
+
+ def keep_stdio_open_false_stderr
+ Process.daemon(*@args)
+ $stderr.write "writing to stderr"
+ write ""
+ end
+
+ def keep_stdio_open_false_stdin
+ Process.daemon(*@args)
+
+ # Reading from /dev/null will return right away. If STDIN were not
+ # /dev/null, reading would block and the spec would hang. This is not a
+ # perfect way to spec the behavior but it works.
+ write $stdin.read
+ end
+
+ def keep_stdio_open_true_stdout
+ $stdout.reopen @data
+ Process.daemon(*@args)
+ $stdout.write "writing to stdout"
+ end
+
+ def keep_stdio_open_true_stderr
+ $stderr.reopen @data
+ Process.daemon(*@args)
+ $stderr.write "writing to stderr"
+ end
+
+ def keep_stdio_open_true_stdin
+ File.open(@input, "w") { |f| f.puts "reading from stdin" }
+
+ $stdin.reopen @input, "r"
+ Process.daemon(*@args)
+ write $stdin.read
+ end
+
+ def keep_stdio_open_files
+ file = File.open @input, "w"
+
+ Process.daemon(*@args)
+ write file.closed?
+ end
+ end
+end
+
+ProcessSpecs::Daemon.new(ARGV).run
diff --git a/spec/ruby/core/process/fixtures/kill.rb b/spec/ruby/core/process/fixtures/kill.rb
new file mode 100644
index 0000000000..0b88f8ee1f
--- /dev/null
+++ b/spec/ruby/core/process/fixtures/kill.rb
@@ -0,0 +1,45 @@
+require 'thread'
+
+pid_file = ARGV.shift
+scenario = ARGV.shift
+
+# We must do this first otherwise there will be a race with the process that
+# creates this process and the TERM signal below could go to that process
+# instead, which will likely abort the specs process.
+Process.setsid if scenario
+
+mutex = Mutex.new
+
+Signal.trap(:TERM) do
+ if mutex.try_lock
+ STDOUT.puts "signaled"
+ STDOUT.flush
+ $signaled = true
+ end
+end
+
+File.open(pid_file, "wb") { |f| f.puts Process.pid }
+
+if scenario
+ # We are sending a signal to the process group
+ process = "Process.getpgrp"
+
+ case scenario
+ when "self"
+ signal = %["SIGTERM"]
+ process = "0"
+ when "group_numeric"
+ signal = %[-Signal.list["TERM"]]
+ when "group_short_string"
+ signal = %["-TERM"]
+ when "group_full_string"
+ signal = %["-SIGTERM"]
+ else
+ raise "unknown scenario: #{scenario.inspect}"
+ end
+
+ code = "Process.kill(#{signal}, #{process})"
+ system(ENV["RUBY_EXE"], *ENV["RUBY_FLAGS"].split(' '), "-e", code)
+end
+
+sleep 0.001 until mutex.locked? and $signaled
diff --git a/spec/ruby/core/process/fixtures/map_fd.rb b/spec/ruby/core/process/fixtures/map_fd.rb
new file mode 100644
index 0000000000..3ed887486b
--- /dev/null
+++ b/spec/ruby/core/process/fixtures/map_fd.rb
@@ -0,0 +1,9 @@
+fd = ARGV.shift.to_i
+
+f = File.for_fd(fd)
+f.autoclose = false
+begin
+ f.write "writing to fd: #{fd}"
+ensure
+ f.close
+end
diff --git a/spec/ruby/core/process/fixtures/setpriority.rb b/spec/ruby/core/process/fixtures/setpriority.rb
new file mode 100644
index 0000000000..cf22d85b12
--- /dev/null
+++ b/spec/ruby/core/process/fixtures/setpriority.rb
@@ -0,0 +1,12 @@
+case ARGV[0]
+when "process"
+ which = Process::PRIO_PROCESS
+when "group"
+ Process.setpgrp
+ which = Process::PRIO_PGRP
+end
+
+priority = Process.getpriority(which, 0)
+p priority
+p Process.setpriority(which, 0, priority + 1)
+p Process.getpriority(which, 0)
diff --git a/spec/ruby/core/process/fork_spec.rb b/spec/ruby/core/process/fork_spec.rb
new file mode 100644
index 0000000000..a4f765247d
--- /dev/null
+++ b/spec/ruby/core/process/fork_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/process/fork'
+
+describe "Process.fork" do
+ it_behaves_like :process_fork, :fork, Process
+end
diff --git a/spec/ruby/core/process/getpgid_spec.rb b/spec/ruby/core/process/getpgid_spec.rb
new file mode 100644
index 0000000000..c1dd007b16
--- /dev/null
+++ b/spec/ruby/core/process/getpgid_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "Process.getpgid" do
+ platform_is_not :windows do
+ it "coerces the argument to an Integer" do
+ Process.getpgid(mock_int(Process.pid)).should == Process.getpgrp
+ end
+
+ it "returns the process group ID for the given process id" do
+ Process.getpgid(Process.pid).should == Process.getpgrp
+ end
+
+ it "returns the process group ID for the calling process id when passed 0" do
+ Process.getpgid(0).should == Process.getpgrp
+ end
+ end
+end
diff --git a/spec/ruby/core/process/getpgrp_spec.rb b/spec/ruby/core/process/getpgrp_spec.rb
new file mode 100644
index 0000000000..e1d1c5f92d
--- /dev/null
+++ b/spec/ruby/core/process/getpgrp_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+# see setpgrp_spec.rb
+
+describe "Process.getpgrp" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/getpriority_spec.rb b/spec/ruby/core/process/getpriority_spec.rb
new file mode 100644
index 0000000000..4b66e18679
--- /dev/null
+++ b/spec/ruby/core/process/getpriority_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+
+describe "Process.getpriority" do
+ platform_is_not :windows do
+
+ it "coerces arguments to Integers" do
+ ret = Process.getpriority mock_int(Process::PRIO_PROCESS), mock_int(0)
+ ret.should be_kind_of(Fixnum)
+ end
+
+ it "gets the scheduling priority for a specified process" do
+ Process.getpriority(Process::PRIO_PROCESS, 0).should be_kind_of(Fixnum)
+ end
+
+ it "gets the scheduling priority for a specified process group" do
+ Process.getpriority(Process::PRIO_PGRP, 0).should be_kind_of(Fixnum)
+ end
+
+ it "gets the scheduling priority for a specified user" do
+ Process.getpriority(Process::PRIO_USER, 0).should be_kind_of(Fixnum)
+ end
+ end
+end
diff --git a/spec/ruby/core/process/getrlimit_spec.rb b/spec/ruby/core/process/getrlimit_spec.rb
new file mode 100644
index 0000000000..2ab825532b
--- /dev/null
+++ b/spec/ruby/core/process/getrlimit_spec.rb
@@ -0,0 +1,91 @@
+require_relative '../../spec_helper'
+
+platform_is :aix do
+ # In AIX, if getrlimit(2) is called multiple times with RLIMIT_DATA,
+ # the first call and the subequent calls return slightly different
+ # values of rlim_cur, even if the process does nothing between
+ # the calls. This behavior causes some of the tests in this spec
+ # to fail, so call Process.getrlimit(:DATA) once and discard the result.
+ # Subsequent calls to Process.getrlimit(:DATA) should return
+ # a consistent value of rlim_cur.
+ Process.getrlimit(:DATA)
+end
+
+platform_is_not :windows do
+ describe "Process.getrlimit" do
+ it "returns a two-element Array of Integers" do
+ result = Process.getrlimit Process::RLIMIT_CORE
+ result.size.should == 2
+ result.first.should be_kind_of(Integer)
+ result.last.should be_kind_of(Integer)
+ end
+
+ context "when passed an Object" do
+ before do
+ @resource = Process::RLIMIT_CORE
+ end
+
+ it "calls #to_int to convert to an Integer" do
+ obj = mock("process getrlimit integer")
+ obj.should_receive(:to_int).and_return(@resource)
+
+ Process.getrlimit(obj).should == Process.getrlimit(@resource)
+ end
+
+ it "raises a TypeError if #to_int does not return an Integer" do
+ obj = mock("process getrlimit integer")
+ obj.should_receive(:to_int).and_return(nil)
+
+ lambda { Process.getrlimit(obj) }.should raise_error(TypeError)
+ end
+ end
+
+ context "when passed a Symbol" do
+ Process.constants.grep(/\ARLIMIT_/) do |fullname|
+ short = $'
+ it "coerces :#{short} into #{fullname}" do
+ Process.getrlimit(short.to_sym).should == Process.getrlimit(Process.const_get(fullname))
+ end
+ end
+
+ it "raises ArgumentError when passed an unknown resource" do
+ lambda { Process.getrlimit(:FOO) }.should raise_error(ArgumentError)
+ end
+ end
+
+ context "when passed a String" do
+ Process.constants.grep(/\ARLIMIT_/) do |fullname|
+ short = $'
+ it "coerces '#{short}' into #{fullname}" do
+ Process.getrlimit(short).should == Process.getrlimit(Process.const_get(fullname))
+ end
+ end
+
+ it "raises ArgumentError when passed an unknown resource" do
+ lambda { Process.getrlimit("FOO") }.should raise_error(ArgumentError)
+ end
+ end
+
+ context "when passed on Object" do
+ before do
+ @resource = Process::RLIMIT_CORE
+ end
+
+ it "calls #to_str to convert to a String" do
+ obj = mock("process getrlimit string")
+ obj.should_receive(:to_str).and_return("CORE")
+ obj.should_not_receive(:to_int)
+
+ Process.getrlimit(obj).should == Process.getrlimit(@resource)
+ end
+
+ it "calls #to_int if #to_str does not return a String" do
+ obj = mock("process getrlimit string")
+ obj.should_receive(:to_str).and_return(nil)
+ obj.should_receive(:to_int).and_return(@resource)
+
+ Process.getrlimit(obj).should == Process.getrlimit(@resource)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/process/gid/change_privilege_spec.rb b/spec/ruby/core/process/gid/change_privilege_spec.rb
new file mode 100644
index 0000000000..4ada277514
--- /dev/null
+++ b/spec/ruby/core/process/gid/change_privilege_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::GID.change_privilege" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/gid/eid_spec.rb b/spec/ruby/core/process/gid/eid_spec.rb
new file mode 100644
index 0000000000..3f2186bb6a
--- /dev/null
+++ b/spec/ruby/core/process/gid/eid_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+
+describe "Process::GID.eid" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "Process::GID.eid=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/gid/grant_privilege_spec.rb b/spec/ruby/core/process/gid/grant_privilege_spec.rb
new file mode 100644
index 0000000000..10da7f8a26
--- /dev/null
+++ b/spec/ruby/core/process/gid/grant_privilege_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::GID.grant_privilege" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/gid/re_exchange_spec.rb b/spec/ruby/core/process/gid/re_exchange_spec.rb
new file mode 100644
index 0000000000..9642c81c5b
--- /dev/null
+++ b/spec/ruby/core/process/gid/re_exchange_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::GID.re_exchange" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/gid/re_exchangeable_spec.rb b/spec/ruby/core/process/gid/re_exchangeable_spec.rb
new file mode 100644
index 0000000000..1c38f903d5
--- /dev/null
+++ b/spec/ruby/core/process/gid/re_exchangeable_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::GID.re_exchangeable?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/gid/rid_spec.rb b/spec/ruby/core/process/gid/rid_spec.rb
new file mode 100644
index 0000000000..ad66c94e72
--- /dev/null
+++ b/spec/ruby/core/process/gid/rid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::GID.rid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/gid/sid_available_spec.rb b/spec/ruby/core/process/gid/sid_available_spec.rb
new file mode 100644
index 0000000000..8d86b3e9d2
--- /dev/null
+++ b/spec/ruby/core/process/gid/sid_available_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::GID.sid_available?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/gid/switch_spec.rb b/spec/ruby/core/process/gid/switch_spec.rb
new file mode 100644
index 0000000000..b162f1e782
--- /dev/null
+++ b/spec/ruby/core/process/gid/switch_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::GID.switch" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/gid_spec.rb b/spec/ruby/core/process/gid_spec.rb
new file mode 100644
index 0000000000..07221da420
--- /dev/null
+++ b/spec/ruby/core/process/gid_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+
+describe "Process.gid" do
+ platform_is_not :windows do
+ it "returns the correct gid for the user executing this process" do
+ current_gid_according_to_unix = `id -gr`.to_i
+ Process.gid.should == current_gid_according_to_unix
+ end
+ end
+
+ it "also goes by Process::GID.rid" do
+ Process::GID.rid.should == Process.gid
+ end
+
+ it "also goes by Process::Sys.getgid" do
+ Process::Sys.getgid.should == Process.gid
+ end
+end
+
+describe "Process.gid=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/groups_spec.rb b/spec/ruby/core/process/groups_spec.rb
new file mode 100644
index 0000000000..325deb5977
--- /dev/null
+++ b/spec/ruby/core/process/groups_spec.rb
@@ -0,0 +1,63 @@
+require_relative '../../spec_helper'
+
+describe "Process.groups" do
+ platform_is_not :windows do
+ it "gets an Array of the gids of groups in the supplemental group access list" do
+ groups = `id -G`.scan(/\d+/).map { |i| i.to_i }
+ gid = Process.gid
+
+ expected = (groups.sort - [gid]).uniq.sort
+ actual = (Process.groups - [gid]).uniq.sort
+ actual.should == expected
+ end
+ end
+end
+
+describe "Process.groups=" do
+ platform_is_not :windows do
+ as_superuser do
+ it "sets the list of gids of groups in the supplemental group access list" do
+ groups = Process.groups
+ Process.groups = []
+ Process.groups.should == []
+ Process.groups = groups
+ Process.groups.sort.should == groups.sort
+ end
+ end
+
+ as_user do
+ platform_is :aix do
+ it "sets the list of gids of groups in the supplemental group access list" do
+ # setgroups() is not part of the POSIX standard,
+ # so its behavior varies from OS to OS. AIX allows a non-root
+ # process to set the supplementary group IDs, as long as
+ # they are presently in its supplementary group IDs.
+ # The order of the following tests matters.
+ # After this process executes "Process.groups = []"
+ # it should no longer be able to set any supplementary
+ # group IDs, even if it originally belonged to them.
+ # It should only be able to set its primary group ID.
+ groups = Process.groups
+ Process.groups = groups
+ Process.groups.sort.should == groups.sort
+ Process.groups = []
+ Process.groups.should == []
+ Process.groups = [ Process.gid ]
+ Process.groups.should == [ Process.gid ]
+ supplementary = groups - [ Process.gid ]
+ if supplementary.length > 0
+ lambda { Process.groups = supplementary }.should raise_error(Errno::EPERM)
+ end
+ end
+ end
+
+ platform_is_not :aix do
+ it "raises Errno::EPERM" do
+ lambda {
+ Process.groups = [0]
+ }.should raise_error(Errno::EPERM)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/process/initgroups_spec.rb b/spec/ruby/core/process/initgroups_spec.rb
new file mode 100644
index 0000000000..e711fc0798
--- /dev/null
+++ b/spec/ruby/core/process/initgroups_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+
+describe "Process.initgroups" do
+ platform_is_not :windows do
+ as_user do
+ it "initializes the supplemental group access list" do
+ name = `id -un`.strip
+ groups = Process.groups
+ gid = groups.max.to_i + 1
+ augmented_groups = `id -G`.scan(/\d+/).map {|i| i.to_i} << gid
+ if Process.uid == 0
+ Process.groups = []
+ Process.initgroups(name, gid).sort.should == augmented_groups.sort
+ Process.groups.sort.should == augmented_groups.sort
+ Process.groups = groups
+ else
+ lambda { Process.initgroups(name, gid) }.should raise_error(Errno::EPERM)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/process/kill_spec.rb b/spec/ruby/core/process/kill_spec.rb
new file mode 100644
index 0000000000..f7665ba087
--- /dev/null
+++ b/spec/ruby/core/process/kill_spec.rb
@@ -0,0 +1,128 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Process.kill" do
+ ProcessSpecs.use_system_ruby(self)
+
+ before :each do
+ @pid = Process.pid
+ end
+
+ it "raises an ArgumentError for unknown signals" do
+ lambda { Process.kill("FOO", @pid) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if passed a lowercase signal name" do
+ lambda { Process.kill("term", @pid) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if signal is not a Fixnum or String" do
+ signal = mock("process kill signal")
+ signal.should_not_receive(:to_int)
+
+ lambda { Process.kill(signal, @pid) }.should raise_error(ArgumentError)
+ end
+
+ it "raises Errno::ESRCH if the process does not exist" do
+ pid = Process.spawn(*ruby_exe, "-e", "sleep 10")
+ Process.kill("SIGKILL", pid)
+ Process.wait(pid)
+ lambda {
+ Process.kill("SIGKILL", pid)
+ }.should raise_error(Errno::ESRCH)
+ end
+end
+
+platform_is_not :windows do
+ describe "Process.kill" do
+ ProcessSpecs.use_system_ruby(self)
+
+ before :each do
+ @sp = ProcessSpecs::Signalizer.new
+ end
+
+ after :each do
+ @sp.cleanup
+ end
+
+ it "accepts a Symbol as a signal name" do
+ Process.kill(:SIGTERM, @sp.pid)
+ @sp.result.should == "signaled"
+ end
+
+ it "accepts a String as signal name" do
+ Process.kill("SIGTERM", @sp.pid)
+ @sp.result.should == "signaled"
+ end
+
+ it "accepts a signal name without the 'SIG' prefix" do
+ Process.kill("TERM", @sp.pid)
+ @sp.result.should == "signaled"
+ end
+
+ it "accepts a signal name with the 'SIG' prefix" do
+ Process.kill("SIGTERM", @sp.pid)
+ @sp.result.should == "signaled"
+ end
+
+ it "acceps an Integer as a signal value" do
+ Process.kill(15, @sp.pid)
+ @sp.result.should == "signaled"
+ end
+
+ it "calls #to_int to coerce the pid to an Integer" do
+ Process.kill("SIGTERM", mock_int(@sp.pid))
+ @sp.result.should == "signaled"
+ end
+ end
+
+ describe "Process.kill" do
+ ProcessSpecs.use_system_ruby(self)
+
+ before :each do
+ @sp1 = ProcessSpecs::Signalizer.new
+ @sp2 = ProcessSpecs::Signalizer.new
+ end
+
+ after :each do
+ @sp1.cleanup
+ @sp2.cleanup
+ end
+
+ it "signals multiple processes" do
+ Process.kill("SIGTERM", @sp1.pid, @sp2.pid)
+ @sp1.result.should == "signaled"
+ @sp2.result.should == "signaled"
+ end
+
+ it "returns the number of processes signaled" do
+ Process.kill("SIGTERM", @sp1.pid, @sp2.pid).should == 2
+ end
+ end
+
+ describe "Process.kill" do
+ after :each do
+ @sp.cleanup if @sp
+ end
+
+ it "signals the process group if the PID is zero" do
+ @sp = ProcessSpecs::Signalizer.new "self"
+ @sp.result.should == "signaled"
+ end
+
+ it "signals the process group if the signal number is negative" do
+ @sp = ProcessSpecs::Signalizer.new "group_numeric"
+ @sp.result.should == "signaled"
+ end
+
+ it "signals the process group if the short signal name starts with a minus sign" do
+ @sp = ProcessSpecs::Signalizer.new "group_short_string"
+ @sp.result.should == "signaled"
+ end
+
+ it "signals the process group if the full signal name starts with a minus sign" do
+ @sp = ProcessSpecs::Signalizer.new "group_full_string"
+ @sp.result.should == "signaled"
+ end
+ end
+end
diff --git a/spec/ruby/core/process/last_status_spec.rb b/spec/ruby/core/process/last_status_spec.rb
new file mode 100644
index 0000000000..3898dd6b95
--- /dev/null
+++ b/spec/ruby/core/process/last_status_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '2.5' do
+ describe 'Process#last_status' do
+ it 'returns the status of the last executed child process in the current thread' do
+ pid = Process.wait Process.spawn("exit 0")
+ Process.last_status.pid.should == pid
+ end
+
+ it 'returns nil if no child process has been ever executed in the current thread' do
+ Thread.new do
+ Process.last_status.should == nil
+ end.join
+ end
+
+ it 'raises an ArgumentError if any arguments are provided' do
+ -> { Process.last_status(1) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/process/maxgroups_spec.rb b/spec/ruby/core/process/maxgroups_spec.rb
new file mode 100644
index 0000000000..362f788ab2
--- /dev/null
+++ b/spec/ruby/core/process/maxgroups_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ describe "Process.maxgroups" do
+ it "returns the maximum number of gids allowed in the supplemental group access list" do
+ Process.maxgroups.should be_kind_of(Fixnum)
+ end
+
+ it "sets the maximum number of gids allowed in the supplemental group access list" do
+ n = Process.maxgroups
+ begin
+ Process.maxgroups = n - 1
+ Process.maxgroups.should == n - 1
+ ensure
+ Process.maxgroups = n
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/process/pid_spec.rb b/spec/ruby/core/process/pid_spec.rb
new file mode 100644
index 0000000000..c5947ab50d
--- /dev/null
+++ b/spec/ruby/core/process/pid_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Process.pid" do
+ it "returns the process id of this process" do
+ pid = Process.pid
+ pid.should be_kind_of(Fixnum)
+ Process.pid.should == pid
+ end
+end
diff --git a/spec/ruby/core/process/ppid_spec.rb b/spec/ruby/core/process/ppid_spec.rb
new file mode 100644
index 0000000000..e0bdfef30b
--- /dev/null
+++ b/spec/ruby/core/process/ppid_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+
+describe "Process.ppid" do
+ with_feature :fork do
+ it "returns the process id of the parent of this process" do
+
+ read, write = IO.pipe
+
+ child_pid = Process.fork {
+ read.close
+ write << "#{Process.ppid}\n"
+ write.close
+ exit!
+ }
+
+ write.close
+ pid = read.gets
+ read.close
+ Process.wait(child_pid)
+ pid.to_i.should == Process.pid
+ end
+ end
+end
diff --git a/spec/ruby/core/process/set_proctitle_spec.rb b/spec/ruby/core/process/set_proctitle_spec.rb
new file mode 100644
index 0000000000..d022f2021c
--- /dev/null
+++ b/spec/ruby/core/process/set_proctitle_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+
+# Note that there's no way to get the current process title defined as a spec
+# somewhere. Process.setproctitle explicitly does not change `$0` so the only
+# way to get the process title is to shell out.
+describe 'Process.setproctitle' do
+ platform_is :linux, :darwin do
+ before :each do
+ @old_title = $0
+ end
+
+ after :each do
+ Process.setproctitle(@old_title)
+ end
+
+ it 'should set the process title' do
+ title = 'rubyspec-proctitle-test'
+
+ Process.setproctitle(title).should == title
+ `ps -ocommand= -p#{$$}`.should include(title)
+ end
+ end
+end
diff --git a/spec/ruby/core/process/setpgid_spec.rb b/spec/ruby/core/process/setpgid_spec.rb
new file mode 100644
index 0000000000..992fbc3f4d
--- /dev/null
+++ b/spec/ruby/core/process/setpgid_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+
+describe "Process.setpgid" do
+ with_feature :fork do
+ it "sets the process group id of the specified process" do
+ rd, wr = IO.pipe
+
+ pid = Process.fork do
+ wr.close
+ rd.read
+ rd.close
+ Process.exit!
+ end
+
+ rd.close
+
+ begin
+ Process.getpgid(pid).should == Process.getpgrp
+ Process.setpgid(mock_int(pid), mock_int(pid)).should == 0
+ Process.getpgid(pid).should == pid
+ ensure
+ wr.write ' '
+ wr.close
+ Process.wait pid
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/process/setpgrp_spec.rb b/spec/ruby/core/process/setpgrp_spec.rb
new file mode 100644
index 0000000000..800668008d
--- /dev/null
+++ b/spec/ruby/core/process/setpgrp_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+
+# TODO: put these in the right files.
+describe "Process.setpgrp and Process.getpgrp" do
+ platform_is_not :windows do
+ it "sets and gets the process group ID of the calling process" do
+ # there are two synchronization points here:
+ # One for the child to let the parent know that it has finished
+ # setting its process group;
+ # and another for the parent to let the child know that it's ok to die.
+ read1, write1 = IO.pipe
+ read2, write2 = IO.pipe
+ pid = Process.fork do
+ read1.close
+ write2.close
+ Process.setpgrp
+ write1 << Process.getpgrp
+ write1.close
+ read2.read(1)
+ read2.close
+ Process.exit!
+ end
+ write1.close
+ read2.close
+ pgid = read1.read # wait for child to change process groups
+ read1.close
+
+ begin
+ Process.getpgid(pid).should == pgid.to_i
+ ensure
+ write2 << "!"
+ write2.close
+ Process.wait pid
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/process/setpriority_spec.rb b/spec/ruby/core/process/setpriority_spec.rb
new file mode 100644
index 0000000000..d3be02eb6d
--- /dev/null
+++ b/spec/ruby/core/process/setpriority_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+
+describe "Process.setpriority" do
+ platform_is_not :windows do
+ it "sets the scheduling priority for a specified process" do
+ priority = Process.getpriority(Process::PRIO_PROCESS, 0)
+
+ out = ruby_exe(fixture(__FILE__, "setpriority.rb"), args: "process")
+ out = out.lines.map { |l| Integer(l) }
+ pr = out[0]
+ out.should == [pr, 0, pr+1]
+
+ Process.getpriority(Process::PRIO_PROCESS, 0).should == priority
+ end
+
+ # Darwin and FreeBSD don't seem to handle these at all, getting all out of
+ # whack with either permission errors or just the wrong value
+ platform_is_not :darwin, :freebsd do
+ it "sets the scheduling priority for a specified process group" do
+ priority = Process.getpriority(Process::PRIO_PGRP, 0)
+
+ out = ruby_exe(fixture(__FILE__, "setpriority.rb"), args: "group")
+ out = out.lines.map { |l| Integer(l) }
+ pr = out[0]
+ out.should == [pr, 0, pr+1]
+
+ Process.getpriority(Process::PRIO_PGRP, 0).should == priority
+ end
+ end
+
+ as_superuser do
+ it "sets the scheduling priority for a specified user" do
+ p = Process.getpriority(Process::PRIO_USER, 0)
+ Process.setpriority(Process::PRIO_USER, 0, p + 1).should == 0
+ Process.getpriority(Process::PRIO_USER, 0).should == (p + 1)
+ Process.setpriority(Process::PRIO_USER, 0, p).should == 0
+ end
+ end
+ end
+
+end
diff --git a/spec/ruby/core/process/setrlimit_spec.rb b/spec/ruby/core/process/setrlimit_spec.rb
new file mode 100644
index 0000000000..f4ff607b7b
--- /dev/null
+++ b/spec/ruby/core/process/setrlimit_spec.rb
@@ -0,0 +1,232 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ describe "Process.setrlimit" do
+ context "when passed an Object" do
+ before do
+ @resource = Process::RLIMIT_CORE
+ @limit, @max = Process.getrlimit @resource
+ end
+
+ it "calls #to_int to convert resource to an Integer" do
+ Process.setrlimit(mock_int(@resource), @limit, @max).should be_nil
+ end
+
+ it "raises a TypeError if #to_int for resource does not return an Integer" do
+ obj = mock("process getrlimit integer")
+ obj.should_receive(:to_int).and_return(nil)
+
+ lambda { Process.setrlimit(obj, @limit, @max) }.should raise_error(TypeError)
+ end
+
+ it "calls #to_int to convert the soft limit to an Integer" do
+ Process.setrlimit(@resource, mock_int(@limit), @max).should be_nil
+ end
+
+ it "raises a TypeError if #to_int for resource does not return an Integer" do
+ obj = mock("process getrlimit integer")
+ obj.should_receive(:to_int).and_return(nil)
+
+ lambda { Process.setrlimit(@resource, obj, @max) }.should raise_error(TypeError)
+ end
+
+ it "calls #to_int to convert the hard limit to an Integer" do
+ Process.setrlimit(@resource, @limit, mock_int(@max)).should be_nil
+ end
+
+ it "raises a TypeError if #to_int for resource does not return an Integer" do
+ obj = mock("process getrlimit integer")
+ obj.should_receive(:to_int).and_return(nil)
+
+ lambda { Process.setrlimit(@resource, @limit, obj) }.should raise_error(TypeError)
+ end
+ end
+
+ context "when passed a Symbol" do
+ platform_is_not :openbsd do
+ it "coerces :AS into RLIMIT_AS" do
+ Process.setrlimit(:AS, *Process.getrlimit(Process::RLIMIT_AS)).should be_nil
+ end
+ end
+
+ it "coerces :CORE into RLIMIT_CORE" do
+ Process.setrlimit(:CORE, *Process.getrlimit(Process::RLIMIT_CORE)).should be_nil
+ end
+
+ it "coerces :CPU into RLIMIT_CPU" do
+ Process.setrlimit(:CPU, *Process.getrlimit(Process::RLIMIT_CPU)).should be_nil
+ end
+
+ it "coerces :DATA into RLIMIT_DATA" do
+ Process.setrlimit(:DATA, *Process.getrlimit(Process::RLIMIT_DATA)).should be_nil
+ end
+
+ it "coerces :FSIZE into RLIMIT_FSIZE" do
+ Process.setrlimit(:FSIZE, *Process.getrlimit(Process::RLIMIT_FSIZE)).should be_nil
+ end
+
+ it "coerces :NOFILE into RLIMIT_NOFILE" do
+ Process.setrlimit(:NOFILE, *Process.getrlimit(Process::RLIMIT_NOFILE)).should be_nil
+ end
+
+ it "coerces :STACK into RLIMIT_STACK" do
+ Process.setrlimit(:STACK, *Process.getrlimit(Process::RLIMIT_STACK)).should be_nil
+ end
+
+ platform_is_not :solaris, :aix do
+ it "coerces :MEMLOCK into RLIMIT_MEMLOCK" do
+ Process.setrlimit(:MEMLOCK, *Process.getrlimit(Process::RLIMIT_MEMLOCK)).should be_nil
+ end
+ end
+
+ platform_is_not :solaris do
+ it "coerces :NPROC into RLIMIT_NPROC" do
+ Process.setrlimit(:NPROC, *Process.getrlimit(Process::RLIMIT_NPROC)).should be_nil
+ end
+
+ it "coerces :RSS into RLIMIT_RSS" do
+ Process.setrlimit(:RSS, *Process.getrlimit(Process::RLIMIT_RSS)).should be_nil
+ end
+ end
+
+ platform_is :netbsd, :freebsd do
+ it "coerces :SBSIZE into RLIMIT_SBSIZE" do
+ Process.setrlimit(:SBSIZE, *Process.getrlimit(Process::RLIMIT_SBSIZE)).should be_nil
+ end
+ end
+
+ platform_is :linux do
+ it "coerces :RTPRIO into RLIMIT_RTPRIO" do
+ Process.setrlimit(:RTPRIO, *Process.getrlimit(Process::RLIMIT_RTPRIO)).should be_nil
+ end
+
+ guard -> { defined?(Process::RLIMIT_RTTIME) } do
+ it "coerces :RTTIME into RLIMIT_RTTIME" do
+ Process.setrlimit(:RTTIME, *Process.getrlimit(Process::RLIMIT_RTTIME)).should be_nil
+ end
+ end
+
+ it "coerces :SIGPENDING into RLIMIT_SIGPENDING" do
+ Process.setrlimit(:SIGPENDING, *Process.getrlimit(Process::RLIMIT_SIGPENDING)).should be_nil
+ end
+
+ it "coerces :MSGQUEUE into RLIMIT_MSGQUEUE" do
+ Process.setrlimit(:MSGQUEUE, *Process.getrlimit(Process::RLIMIT_MSGQUEUE)).should be_nil
+ end
+
+ it "coerces :NICE into RLIMIT_NICE" do
+ Process.setrlimit(:NICE, *Process.getrlimit(Process::RLIMIT_NICE)).should be_nil
+ end
+ end
+
+ it "raises ArgumentError when passed an unknown resource" do
+ lambda { Process.setrlimit(:FOO, 1, 1) }.should raise_error(ArgumentError)
+ end
+ end
+
+ context "when passed a String" do
+ platform_is_not :openbsd do
+ it "coerces 'AS' into RLIMIT_AS" do
+ Process.setrlimit("AS", *Process.getrlimit(Process::RLIMIT_AS)).should be_nil
+ end
+ end
+
+ it "coerces 'CORE' into RLIMIT_CORE" do
+ Process.setrlimit("CORE", *Process.getrlimit(Process::RLIMIT_CORE)).should be_nil
+ end
+
+ it "coerces 'CPU' into RLIMIT_CPU" do
+ Process.setrlimit("CPU", *Process.getrlimit(Process::RLIMIT_CPU)).should be_nil
+ end
+
+ it "coerces 'DATA' into RLIMIT_DATA" do
+ Process.setrlimit("DATA", *Process.getrlimit(Process::RLIMIT_DATA)).should be_nil
+ end
+
+ it "coerces 'FSIZE' into RLIMIT_FSIZE" do
+ Process.setrlimit("FSIZE", *Process.getrlimit(Process::RLIMIT_FSIZE)).should be_nil
+ end
+
+ it "coerces 'NOFILE' into RLIMIT_NOFILE" do
+ Process.setrlimit("NOFILE", *Process.getrlimit(Process::RLIMIT_NOFILE)).should be_nil
+ end
+
+ it "coerces 'STACK' into RLIMIT_STACK" do
+ Process.setrlimit("STACK", *Process.getrlimit(Process::RLIMIT_STACK)).should be_nil
+ end
+
+ platform_is_not :solaris, :aix do
+ it "coerces 'MEMLOCK' into RLIMIT_MEMLOCK" do
+ Process.setrlimit("MEMLOCK", *Process.getrlimit(Process::RLIMIT_MEMLOCK)).should be_nil
+ end
+ end
+
+ platform_is_not :solaris do
+ it "coerces 'NPROC' into RLIMIT_NPROC" do
+ Process.setrlimit("NPROC", *Process.getrlimit(Process::RLIMIT_NPROC)).should be_nil
+ end
+
+ it "coerces 'RSS' into RLIMIT_RSS" do
+ Process.setrlimit("RSS", *Process.getrlimit(Process::RLIMIT_RSS)).should be_nil
+ end
+ end
+
+ platform_is :netbsd, :freebsd do
+ it "coerces 'SBSIZE' into RLIMIT_SBSIZE" do
+ Process.setrlimit("SBSIZE", *Process.getrlimit(Process::RLIMIT_SBSIZE)).should be_nil
+ end
+ end
+
+ platform_is :linux do
+ it "coerces 'RTPRIO' into RLIMIT_RTPRIO" do
+ Process.setrlimit("RTPRIO", *Process.getrlimit(Process::RLIMIT_RTPRIO)).should be_nil
+ end
+
+ guard -> { defined?(Process::RLIMIT_RTTIME) } do
+ it "coerces 'RTTIME' into RLIMIT_RTTIME" do
+ Process.setrlimit("RTTIME", *Process.getrlimit(Process::RLIMIT_RTTIME)).should be_nil
+ end
+ end
+
+ it "coerces 'SIGPENDING' into RLIMIT_SIGPENDING" do
+ Process.setrlimit("SIGPENDING", *Process.getrlimit(Process::RLIMIT_SIGPENDING)).should be_nil
+ end
+
+ it "coerces 'MSGQUEUE' into RLIMIT_MSGQUEUE" do
+ Process.setrlimit("MSGQUEUE", *Process.getrlimit(Process::RLIMIT_MSGQUEUE)).should be_nil
+ end
+
+ it "coerces 'NICE' into RLIMIT_NICE" do
+ Process.setrlimit("NICE", *Process.getrlimit(Process::RLIMIT_NICE)).should be_nil
+ end
+ end
+
+ it "raises ArgumentError when passed an unknown resource" do
+ lambda { Process.setrlimit("FOO", 1, 1) }.should raise_error(ArgumentError)
+ end
+ end
+
+ context "when passed on Object" do
+ before do
+ @resource = Process::RLIMIT_CORE
+ @limit, @max = Process.getrlimit @resource
+ end
+
+ it "calls #to_str to convert to a String" do
+ obj = mock("process getrlimit string")
+ obj.should_receive(:to_str).and_return("CORE")
+ obj.should_not_receive(:to_int)
+
+ Process.setrlimit(obj, @limit, @max).should be_nil
+ end
+
+ it "calls #to_int if #to_str does not return a String" do
+ obj = mock("process getrlimit string")
+ obj.should_receive(:to_str).and_return(nil)
+ obj.should_receive(:to_int).and_return(@resource)
+
+ Process.setrlimit(obj, @limit, @max).should be_nil
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/process/setsid_spec.rb b/spec/ruby/core/process/setsid_spec.rb
new file mode 100644
index 0000000000..d00508a1f7
--- /dev/null
+++ b/spec/ruby/core/process/setsid_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+
+describe "Process.setsid" do
+ with_feature :fork do
+ it "establishes this process as a new session and process group leader" do
+ read, write = IO.pipe
+ read2, write2 = IO.pipe
+ pid = Process.fork {
+ begin
+ read.close
+ write2.close
+ pgid = Process.setsid
+ write << pgid
+ write.close
+ read2.gets
+ rescue Exception => e
+ write << e << e.backtrace
+ end
+ Process.exit!
+ }
+ write.close
+ read2.close
+ pgid_child = Integer(read.gets)
+ read.close
+ platform_is_not :aix, :openbsd do
+ # AIX does not allow Process.getsid(pid)
+ # if pid is in a different session.
+ pgid = Process.getsid(pid)
+ pgid_child.should == pgid
+ end
+ write2.close
+ Process.wait pid
+
+ pgid_child.should_not == Process.getsid
+ end
+ end
+end
diff --git a/spec/ruby/core/process/spawn_spec.rb b/spec/ruby/core/process/spawn_spec.rb
new file mode 100644
index 0000000000..1bd1dfac83
--- /dev/null
+++ b/spec/ruby/core/process/spawn_spec.rb
@@ -0,0 +1,636 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+newline = "\n"
+platform_is :windows do
+ newline = "\r\n"
+end
+
+describe :process_spawn_does_not_close_std_streams, shared: true do
+ platform_is_not :windows do
+ it "does not close STDIN" do
+ code = "STDOUT.puts STDIN.read(0).inspect"
+ cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})"
+ ruby_exe(cmd, args: "> #{@output}")
+ File.binread(@output).should == %[""#{newline}]
+ end
+
+ it "does not close STDOUT" do
+ code = "STDOUT.puts 'hello'"
+ cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})"
+ ruby_exe(cmd, args: "> #{@output}")
+ File.binread(@output).should == "hello#{newline}"
+ end
+
+ it "does not close STDERR" do
+ code = "STDERR.puts 'hello'"
+ cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})"
+ ruby_exe(cmd, args: "2> #{@output}")
+ File.binread(@output).should =~ /hello#{newline}/
+ end
+ end
+end
+
+describe "Process.spawn" do
+ ProcessSpecs.use_system_ruby(self)
+
+ before :each do
+ @name = tmp("process_spawn.txt")
+ @var = "$FOO"
+ platform_is :windows do
+ @var = "%FOO%"
+ end
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "executes the given command" do
+ lambda { Process.wait Process.spawn("echo spawn") }.should output_to_fd("spawn\n")
+ end
+
+ it "returns the process ID of the new process as a Fixnum" do
+ pid = Process.spawn(*ruby_exe, "-e", "exit")
+ Process.wait pid
+ pid.should be_an_instance_of(Fixnum)
+ end
+
+ it "returns immediately" do
+ start = Time.now
+ pid = Process.spawn(*ruby_exe, "-e", "sleep 10")
+ (Time.now - start).should < 5
+ Process.kill :KILL, pid
+ Process.wait pid
+ end
+
+ # argv processing
+
+ describe "with a single argument" do
+ platform_is_not :windows do
+ it "subjects the specified command to shell expansion" do
+ lambda { Process.wait Process.spawn("echo *") }.should_not output_to_fd("*\n")
+ end
+
+ it "creates an argument array with shell parsing semantics for whitespace" do
+ lambda { Process.wait Process.spawn("echo a b c d") }.should output_to_fd("a b c d\n")
+ end
+ end
+
+ platform_is :windows do
+ # There is no shell expansion on Windows
+ it "does not subject the specified command to shell expansion on Windows" do
+ lambda { Process.wait Process.spawn("echo *") }.should output_to_fd("*\n")
+ end
+
+ it "does not create an argument array with shell parsing semantics for whitespace on Windows" do
+ lambda { Process.wait Process.spawn("echo a b c d") }.should output_to_fd("a b c d\n")
+ end
+ end
+
+ it "calls #to_str to convert the argument to a String" do
+ o = mock("to_str")
+ o.should_receive(:to_str).and_return("echo foo")
+ lambda { Process.wait Process.spawn(o) }.should output_to_fd("foo\n")
+ end
+
+ it "raises an ArgumentError if the command includes a null byte" do
+ lambda { Process.spawn "\000" }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if the argument does not respond to #to_str" do
+ lambda { Process.spawn :echo }.should raise_error(TypeError)
+ end
+ end
+
+ describe "with multiple arguments" do
+ it "does not subject the arguments to shell expansion" do
+ lambda { Process.wait Process.spawn("echo", "*") }.should output_to_fd("*\n")
+ end
+
+ it "preserves whitespace in passed arguments" do
+ out = "a b c d\n"
+ platform_is :windows do
+ # The echo command on Windows takes quotes literally
+ out = "\"a b c d\"\n"
+ end
+ lambda { Process.wait Process.spawn("echo", "a b c d") }.should output_to_fd(out)
+ end
+
+ it "calls #to_str to convert the arguments to Strings" do
+ o = mock("to_str")
+ o.should_receive(:to_str).and_return("foo")
+ lambda { Process.wait Process.spawn("echo", o) }.should output_to_fd("foo\n")
+ end
+
+ it "raises an ArgumentError if an argument includes a null byte" do
+ lambda { Process.spawn "echo", "\000" }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if an argument does not respond to #to_str" do
+ lambda { Process.spawn "echo", :foo }.should raise_error(TypeError)
+ end
+ end
+
+ describe "with a command array" do
+ it "uses the first element as the command name and the second as the argv[0] value" do
+ platform_is_not :windows do
+ lambda { Process.wait Process.spawn(["/bin/sh", "argv_zero"], "-c", "echo $0") }.should output_to_fd("argv_zero\n")
+ end
+ platform_is :windows do
+ lambda { Process.wait Process.spawn(["cmd.exe", "/C"], "/C", "echo", "argv_zero") }.should output_to_fd("argv_zero\n")
+ end
+ end
+
+ it "does not subject the arguments to shell expansion" do
+ lambda { Process.wait Process.spawn(["echo", "echo"], "*") }.should output_to_fd("*\n")
+ end
+
+ it "preserves whitespace in passed arguments" do
+ out = "a b c d\n"
+ platform_is :windows do
+ # The echo command on Windows takes quotes literally
+ out = "\"a b c d\"\n"
+ end
+ lambda { Process.wait Process.spawn(["echo", "echo"], "a b c d") }.should output_to_fd(out)
+ end
+
+ it "calls #to_ary to convert the argument to an Array" do
+ o = mock("to_ary")
+ platform_is_not :windows do
+ o.should_receive(:to_ary).and_return(["/bin/sh", "argv_zero"])
+ lambda { Process.wait Process.spawn(o, "-c", "echo $0") }.should output_to_fd("argv_zero\n")
+ end
+ platform_is :windows do
+ o.should_receive(:to_ary).and_return(["cmd.exe", "/C"])
+ lambda { Process.wait Process.spawn(o, "/C", "echo", "argv_zero") }.should output_to_fd("argv_zero\n")
+ end
+ end
+
+ it "calls #to_str to convert the first element to a String" do
+ o = mock("to_str")
+ o.should_receive(:to_str).and_return("echo")
+ lambda { Process.wait Process.spawn([o, "echo"], "foo") }.should output_to_fd("foo\n")
+ end
+
+ it "calls #to_str to convert the second element to a String" do
+ o = mock("to_str")
+ o.should_receive(:to_str).and_return("echo")
+ lambda { Process.wait Process.spawn(["echo", o], "foo") }.should output_to_fd("foo\n")
+ end
+
+ it "raises an ArgumentError if the Array does not have exactly two elements" do
+ lambda { Process.spawn([]) }.should raise_error(ArgumentError)
+ lambda { Process.spawn([:a]) }.should raise_error(ArgumentError)
+ lambda { Process.spawn([:a, :b, :c]) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the Strings in the Array include a null byte" do
+ lambda { Process.spawn ["\000", "echo"] }.should raise_error(ArgumentError)
+ lambda { Process.spawn ["echo", "\000"] }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if an element in the Array does not respond to #to_str" do
+ lambda { Process.spawn ["echo", :echo] }.should raise_error(TypeError)
+ lambda { Process.spawn [:echo, "echo"] }.should raise_error(TypeError)
+ end
+ end
+
+ # env handling
+
+ after :each do
+ ENV.delete("FOO")
+ end
+
+ it "sets environment variables in the child environment" do
+ Process.wait Process.spawn({"FOO" => "BAR"}, "echo #{@var}>#{@name}")
+ File.read(@name).should == "BAR\n"
+ end
+
+ it "unsets environment variables whose value is nil" do
+ ENV["FOO"] = "BAR"
+ Process.wait Process.spawn({"FOO" => nil}, "echo #{@var}>#{@name}")
+ expected = "\n"
+ platform_is :windows do
+ # Windows does not expand the variable if it is unset
+ expected = "#{@var}\n"
+ end
+ File.read(@name).should == expected
+ end
+
+ it "calls #to_hash to convert the environment" do
+ o = mock("to_hash")
+ o.should_receive(:to_hash).and_return({"FOO" => "BAR"})
+ Process.wait Process.spawn(o, "echo #{@var}>#{@name}")
+ File.read(@name).should == "BAR\n"
+ end
+
+ it "calls #to_str to convert the environment keys" do
+ o = mock("to_str")
+ o.should_receive(:to_str).and_return("FOO")
+ Process.wait Process.spawn({o => "BAR"}, "echo #{@var}>#{@name}")
+ File.read(@name).should == "BAR\n"
+ end
+
+ it "calls #to_str to convert the environment values" do
+ o = mock("to_str")
+ o.should_receive(:to_str).and_return("BAR")
+ Process.wait Process.spawn({"FOO" => o}, "echo #{@var}>#{@name}")
+ File.read(@name).should == "BAR\n"
+ end
+
+ it "raises an ArgumentError if an environment key includes an equals sign" do
+ lambda do
+ Process.spawn({"FOO=" => "BAR"}, "echo #{@var}>#{@name}")
+ end.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if an environment key includes a null byte" do
+ lambda do
+ Process.spawn({"\000" => "BAR"}, "echo #{@var}>#{@name}")
+ end.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if an environment value includes a null byte" do
+ lambda do
+ Process.spawn({"FOO" => "\000"}, "echo #{@var}>#{@name}")
+ end.should raise_error(ArgumentError)
+ end
+
+ # :unsetenv_others
+
+ before :each do
+ @minimal_env = {
+ "PATH" => ENV["PATH"],
+ "HOME" => ENV["HOME"]
+ }
+ @common_env_spawn_args = [@minimal_env, "echo #{@var}>#{@name}"]
+ end
+
+ platform_is_not :windows do
+ it "unsets other environment variables when given a true :unsetenv_others option" do
+ ENV["FOO"] = "BAR"
+ Process.wait Process.spawn(*@common_env_spawn_args, unsetenv_others: true)
+ $?.success?.should be_true
+ File.read(@name).should == "\n"
+ end
+ end
+
+ it "does not unset other environment variables when given a false :unsetenv_others option" do
+ ENV["FOO"] = "BAR"
+ Process.wait Process.spawn(*@common_env_spawn_args, unsetenv_others: false)
+ $?.success?.should be_true
+ File.read(@name).should == "BAR\n"
+ end
+
+ platform_is_not :windows do
+ it "does not unset environment variables included in the environment hash" do
+ env = @minimal_env.merge({"FOO" => "BAR"})
+ Process.wait Process.spawn(env, "echo #{@var}>#{@name}", unsetenv_others: true)
+ $?.success?.should be_true
+ File.read(@name).should == "BAR\n"
+ end
+ end
+
+ # :pgroup
+
+ platform_is_not :windows do
+ it "joins the current process group by default" do
+ lambda do
+ Process.wait Process.spawn(ruby_cmd("print Process.getpgid(Process.pid)"))
+ end.should output_to_fd(Process.getpgid(Process.pid).to_s)
+ end
+
+ it "joins the current process if pgroup: false" do
+ lambda do
+ Process.wait Process.spawn(ruby_cmd("print Process.getpgid(Process.pid)"), pgroup: false)
+ end.should output_to_fd(Process.getpgid(Process.pid).to_s)
+ end
+
+ it "joins the current process if pgroup: nil" do
+ lambda do
+ Process.wait Process.spawn(ruby_cmd("print Process.getpgid(Process.pid)"), pgroup: nil)
+ end.should output_to_fd(Process.getpgid(Process.pid).to_s)
+ end
+
+ it "joins a new process group if pgroup: true" do
+ process = lambda do
+ Process.wait Process.spawn(ruby_cmd("print Process.getpgid(Process.pid)"), pgroup: true)
+ end
+
+ process.should_not output_to_fd(Process.getpgid(Process.pid).to_s)
+ process.should output_to_fd(/\d+/)
+ end
+
+ it "joins a new process group if pgroup: 0" do
+ process = lambda do
+ Process.wait Process.spawn(ruby_cmd("print Process.getpgid(Process.pid)"), pgroup: 0)
+ end
+
+ process.should_not output_to_fd(Process.getpgid(Process.pid).to_s)
+ process.should output_to_fd(/\d+/)
+ end
+
+ it "joins the specified process group if pgroup: pgid" do
+ pgid = Process.getpgid(Process.pid)
+ lambda do
+ Process.wait Process.spawn(ruby_cmd("print Process.getpgid(Process.pid)"), pgroup: pgid)
+ end.should output_to_fd(pgid.to_s)
+ end
+
+ it "raises an ArgumentError if given a negative :pgroup option" do
+ lambda { Process.spawn("echo", pgroup: -1) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if given a symbol as :pgroup option" do
+ lambda { Process.spawn("echo", pgroup: :true) }.should raise_error(TypeError)
+ end
+ end
+
+ platform_is :windows do
+ it "raises an ArgumentError if given :pgroup option" do
+ lambda { Process.spawn("echo", pgroup: false) }.should raise_error(ArgumentError)
+ end
+ end
+
+ # :rlimit_core
+ # :rlimit_cpu
+ # :rlimit_data
+
+ # :chdir
+
+ it "uses the current working directory as its working directory" do
+ lambda do
+ Process.wait Process.spawn(ruby_cmd("print Dir.pwd"))
+ end.should output_to_fd(Dir.pwd)
+ end
+
+ describe "when passed :chdir" do
+ before do
+ @dir = tmp("spawn_chdir", false)
+ Dir.mkdir @dir
+ end
+
+ after do
+ rm_r @dir
+ end
+
+ it "changes to the directory passed for :chdir" do
+ lambda do
+ Process.wait Process.spawn(ruby_cmd("print Dir.pwd"), chdir: @dir)
+ end.should output_to_fd(@dir)
+ end
+
+ it "calls #to_path to convert the :chdir value" do
+ dir = mock("spawn_to_path")
+ dir.should_receive(:to_path).and_return(@dir)
+
+ lambda do
+ Process.wait Process.spawn(ruby_cmd("print Dir.pwd"), chdir: dir)
+ end.should output_to_fd(@dir)
+ end
+ end
+
+ # :umask
+
+ it "uses the current umask by default" do
+ lambda do
+ Process.wait Process.spawn(ruby_cmd("print File.umask"))
+ end.should output_to_fd(File.umask.to_s)
+ end
+
+ platform_is_not :windows do
+ it "sets the umask if given the :umask option" do
+ lambda do
+ Process.wait Process.spawn(ruby_cmd("print File.umask"), umask: 146)
+ end.should output_to_fd("146")
+ end
+ end
+
+ # redirection
+
+ it "redirects STDOUT to the given file descriptior if out: Fixnum" do
+ File.open(@name, 'w') do |file|
+ lambda do
+ Process.wait Process.spawn("echo glark", out: file.fileno)
+ end.should output_to_fd("glark\n", file)
+ end
+ end
+
+ it "redirects STDOUT to the given file if out: IO" do
+ File.open(@name, 'w') do |file|
+ lambda do
+ Process.wait Process.spawn("echo glark", out: file)
+ end.should output_to_fd("glark\n", file)
+ end
+ end
+
+ it "redirects STDOUT to the given file if out: String" do
+ Process.wait Process.spawn("echo glark", out: @name)
+ File.read(@name).should == "glark\n"
+ end
+
+ it "redirects STDOUT to the given file if out: [String name, String mode]" do
+ Process.wait Process.spawn("echo glark", out: [@name, 'w'])
+ File.read(@name).should == "glark\n"
+ end
+
+ it "redirects STDERR to the given file descriptior if err: Fixnum" do
+ File.open(@name, 'w') do |file|
+ lambda do
+ Process.wait Process.spawn("echo glark>&2", err: file.fileno)
+ end.should output_to_fd("glark\n", file)
+ end
+ end
+
+ it "redirects STDERR to the given file descriptor if err: IO" do
+ File.open(@name, 'w') do |file|
+ lambda do
+ Process.wait Process.spawn("echo glark>&2", err: file)
+ end.should output_to_fd("glark\n", file)
+ end
+ end
+
+ it "redirects STDERR to the given file if err: String" do
+ Process.wait Process.spawn("echo glark>&2", err: @name)
+ File.read(@name).should == "glark\n"
+ end
+
+ it "redirects STDERR to child STDOUT if :err => [:child, :out]" do
+ File.open(@name, 'w') do |file|
+ lambda do
+ Process.wait Process.spawn("echo glark>&2", :out => file, :err => [:child, :out])
+ end.should output_to_fd("glark\n", file)
+ end
+ end
+
+ it "redirects both STDERR and STDOUT to the given file descriptior" do
+ File.open(@name, 'w') do |file|
+ lambda do
+ Process.wait Process.spawn(ruby_cmd("print(:glark); STDOUT.flush; STDERR.print(:bang)"),
+ [:out, :err] => file.fileno)
+ end.should output_to_fd("glarkbang", file)
+ end
+ end
+
+ it "redirects both STDERR and STDOUT to the given IO" do
+ File.open(@name, 'w') do |file|
+ lambda do
+ Process.wait Process.spawn(ruby_cmd("print(:glark); STDOUT.flush; STDERR.print(:bang)"),
+ [:out, :err] => file)
+ end.should output_to_fd("glarkbang", file)
+ end
+ end
+
+ it "redirects both STDERR and STDOUT at the time to the given name" do
+ touch @name
+ Process.wait Process.spawn(ruby_cmd("print(:glark); STDOUT.flush; STDERR.print(:bang)"), [:out, :err] => @name)
+ File.read(@name).should == "glarkbang"
+ end
+
+ context "when passed close_others: true" do
+ before :each do
+ @output = tmp("spawn_close_others_true")
+ @options = { close_others: true }
+ end
+
+ after :each do
+ rm_r @output
+ end
+
+ it "closes file descriptors >= 3 in the child process" do
+ IO.pipe do |r, w|
+ begin
+ pid = Process.spawn(ruby_cmd("while File.exist? '#{@name}'; sleep 0.1; end"), @options)
+ w.close
+ lambda { r.read_nonblock(1) }.should raise_error(EOFError)
+ ensure
+ rm_r @name
+ Process.wait(pid) if pid
+ end
+ end
+ end
+
+ it_should_behave_like :process_spawn_does_not_close_std_streams
+ end
+
+ context "when passed close_others: false" do
+ before :each do
+ @output = tmp("spawn_close_others_false")
+ @options = { close_others: false }
+ end
+
+ after :each do
+ rm_r @output
+ end
+
+ it "closes file descriptors >= 3 in the child process because they are set close_on_exec by default" do
+ IO.pipe do |r, w|
+ begin
+ pid = Process.spawn(ruby_cmd("while File.exist? '#{@name}'; sleep 0.1; end"), @options)
+ w.close
+ lambda { r.read_nonblock(1) }.should raise_error(EOFError)
+ ensure
+ rm_r @name
+ Process.wait(pid) if pid
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ it "does not close file descriptors >= 3 in the child process if fds are set close_on_exec=false" do
+ IO.pipe do |r, w|
+ r.close_on_exec = false
+ w.close_on_exec = false
+ begin
+ pid = Process.spawn(ruby_cmd("while File.exist? '#{@name}'; sleep 0.1; end"), @options)
+ w.close
+ lambda { r.read_nonblock(1) }.should raise_error(Errno::EAGAIN)
+ ensure
+ rm_r @name
+ Process.wait(pid) if pid
+ end
+ end
+ end
+ end
+
+ it_should_behave_like :process_spawn_does_not_close_std_streams
+ end
+
+ # error handling
+
+ it "raises an ArgumentError if passed no command arguments" do
+ lambda { Process.spawn }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if passed env or options but no command arguments" do
+ lambda { Process.spawn({}) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if passed env and options but no command arguments" do
+ lambda { Process.spawn({}, {}) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an Errno::ENOENT for an empty string" do
+ lambda { Process.spawn "" }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises an Errno::ENOENT if the command does not exist" do
+ lambda { Process.spawn "nonesuch" }.should raise_error(Errno::ENOENT)
+ end
+
+ unless File.executable?(__FILE__) # Some FS (e.g. vboxfs) locate all files executable
+ platform_is_not :windows do
+ it "raises an Errno::EACCES when the file does not have execute permissions" do
+ lambda { Process.spawn __FILE__ }.should raise_error(Errno::EACCES)
+ end
+ end
+
+ platform_is :windows do
+ it "raises Errno::EACCES or Errno::ENOEXEC when the file is not an executable file" do
+ lambda { Process.spawn __FILE__ }.should raise_error(SystemCallError) { |e|
+ [Errno::EACCES, Errno::ENOEXEC].should include(e.class)
+ }
+ end
+ end
+ end
+
+ it "raises an Errno::EACCES or Errno::EISDIR when passed a directory" do
+ lambda { Process.spawn File.dirname(__FILE__) }.should raise_error(SystemCallError) { |e|
+ [Errno::EACCES, Errno::EISDIR].should include(e.class)
+ }
+ end
+
+ it "raises an ArgumentError when passed a string key in options" do
+ lambda { Process.spawn("echo", "chdir" => Dir.pwd) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed an unknown option key" do
+ lambda { Process.spawn("echo", nonesuch: :foo) }.should raise_error(ArgumentError)
+ end
+
+ platform_is_not :windows do
+ describe "with Integer option keys" do
+ before :each do
+ @name = tmp("spawn_fd_map.txt")
+ @io = new_io @name, "w+"
+ @io.sync = true
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @name
+ end
+
+ it "maps the key to a file descriptor in the child that inherits the file descriptor from the parent specified by the value" do
+ child_fd = @io.fileno + 1
+ args = ruby_cmd(fixture(__FILE__, "map_fd.rb"), args: [child_fd.to_s])
+ pid = Process.spawn(*args, { child_fd => @io })
+ Process.waitpid pid
+ @io.rewind
+
+ @io.read.should == "writing to fd: #{child_fd}"
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/process/status/bit_and_spec.rb b/spec/ruby/core/process/status/bit_and_spec.rb
new file mode 100644
index 0000000000..97f768fdc1
--- /dev/null
+++ b/spec/ruby/core/process/status/bit_and_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Status#&" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/status/coredump_spec.rb b/spec/ruby/core/process/status/coredump_spec.rb
new file mode 100644
index 0000000000..fbbaf926f7
--- /dev/null
+++ b/spec/ruby/core/process/status/coredump_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Status#coredump?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/status/equal_value_spec.rb b/spec/ruby/core/process/status/equal_value_spec.rb
new file mode 100644
index 0000000000..9e9a2d0a2b
--- /dev/null
+++ b/spec/ruby/core/process/status/equal_value_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Status#==" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/status/exited_spec.rb b/spec/ruby/core/process/status/exited_spec.rb
new file mode 100644
index 0000000000..0ae3f9e7ae
--- /dev/null
+++ b/spec/ruby/core/process/status/exited_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Status#exited?" do
+
+ describe "for a child that exited normally" do
+
+ before :each do
+ ruby_exe("exit(0)")
+ end
+
+ it "returns true" do
+ $?.exited?.should be_true
+ end
+ end
+
+
+ describe "for a terminated child" do
+
+ before :each do
+ ruby_exe("Process.kill(:KILL, $$); exit(42)")
+ end
+
+ platform_is_not :windows do
+ it "returns false" do
+ $?.exited?.should be_false
+ end
+ end
+
+ platform_is :windows do
+ it "always returns true" do
+ $?.exited?.should be_true
+ end
+ end
+
+ end
+
+end
diff --git a/spec/ruby/core/process/status/exitstatus_spec.rb b/spec/ruby/core/process/status/exitstatus_spec.rb
new file mode 100644
index 0000000000..cd46b2081f
--- /dev/null
+++ b/spec/ruby/core/process/status/exitstatus_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Status#exitstatus" do
+ before :each do
+ ruby_exe("exit(42)")
+ end
+
+ it "returns the process exit code" do
+ $?.exitstatus.should == 42
+ end
+
+ describe "for a child that raised SignalException" do
+ before :each do
+ ruby_exe("raise SignalException, 'SIGTERM'")
+ end
+
+ platform_is_not :windows do
+ # The exitstatus is not set in these cases. See the termsig_spec
+ # for info on where the signal number (SIGTERM) is available.
+ it "returns nil" do
+ $?.exitstatus.should == nil
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/process/status/inspect_spec.rb b/spec/ruby/core/process/status/inspect_spec.rb
new file mode 100644
index 0000000000..03f0479f2c
--- /dev/null
+++ b/spec/ruby/core/process/status/inspect_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Status#inspect" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/status/pid_spec.rb b/spec/ruby/core/process/status/pid_spec.rb
new file mode 100644
index 0000000000..9965fc3bdf
--- /dev/null
+++ b/spec/ruby/core/process/status/pid_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+
+platform_is_not :windows do
+ describe "Process::Status#pid" do
+
+ before :each do
+ @pid = ruby_exe("print $$").to_i
+ end
+
+ it "returns the pid of the process" do
+ $?.pid.should == @pid
+ end
+
+ end
+end
diff --git a/spec/ruby/core/process/status/right_shift_spec.rb b/spec/ruby/core/process/status/right_shift_spec.rb
new file mode 100644
index 0000000000..e9dda437e8
--- /dev/null
+++ b/spec/ruby/core/process/status/right_shift_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Status#>>" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/status/signaled_spec.rb b/spec/ruby/core/process/status/signaled_spec.rb
new file mode 100644
index 0000000000..f23c95025f
--- /dev/null
+++ b/spec/ruby/core/process/status/signaled_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Status#signaled?" do
+
+ describe "for a cleanly exited child" do
+
+ before :each do
+ ruby_exe("exit(0)")
+ end
+
+ it "returns false" do
+ $?.signaled?.should be_false
+ end
+ end
+
+ describe "for a terminated child" do
+
+ before :each do
+ ruby_exe("Process.kill(:KILL, $$); exit(42)")
+ end
+
+ platform_is_not :windows do
+ it "returns true" do
+ $?.signaled?.should be_true
+ end
+ end
+
+ platform_is :windows do
+ it "always returns false" do
+ $?.signaled?.should be_false
+ end
+ end
+
+ end
+end
diff --git a/spec/ruby/core/process/status/stopped_spec.rb b/spec/ruby/core/process/status/stopped_spec.rb
new file mode 100644
index 0000000000..bebd441d6f
--- /dev/null
+++ b/spec/ruby/core/process/status/stopped_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Status#stopped?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/status/stopsig_spec.rb b/spec/ruby/core/process/status/stopsig_spec.rb
new file mode 100644
index 0000000000..b2a7c5d9e2
--- /dev/null
+++ b/spec/ruby/core/process/status/stopsig_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Status#stopsig" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/status/success_spec.rb b/spec/ruby/core/process/status/success_spec.rb
new file mode 100644
index 0000000000..28a1721800
--- /dev/null
+++ b/spec/ruby/core/process/status/success_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Status#success?" do
+
+ describe "for a child that exited normally" do
+
+ before :each do
+ ruby_exe("exit(0)")
+ end
+
+ it "returns true" do
+ $?.success?.should be_true
+ end
+ end
+
+ describe "for a child that exited with a non zero status" do
+
+ before :each do
+ ruby_exe("exit(42)")
+ end
+
+ it "returns false" do
+ $?.success?.should be_false
+ end
+ end
+
+ describe "for a child that was terminated" do
+
+ before :each do
+ ruby_exe("Process.kill(:KILL, $$); exit(42)")
+ end
+
+ platform_is_not :windows do
+
+ it "returns nil" do
+ $?.success?.should be_nil
+ end
+
+ end
+
+ platform_is :windows do
+
+ it "always returns true" do
+ $?.success?.should be_true
+ end
+
+ end
+
+ end
+
+end
diff --git a/spec/ruby/core/process/status/termsig_spec.rb b/spec/ruby/core/process/status/termsig_spec.rb
new file mode 100644
index 0000000000..1482d27146
--- /dev/null
+++ b/spec/ruby/core/process/status/termsig_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Status#termsig" do
+
+ describe "for a child that exited normally" do
+
+ before :each do
+ ruby_exe("exit(0)")
+ end
+
+ it "returns true" do
+ $?.termsig.should be_nil
+ end
+ end
+
+ describe "for a child that raised SignalException" do
+ before :each do
+ ruby_exe("raise SignalException, 'SIGTERM'")
+ end
+
+ platform_is_not :windows do
+ it "returns the signal" do
+ $?.termsig.should == Signal.list["TERM"]
+ end
+ end
+ end
+
+ describe "for a child that was sent a signal" do
+
+ before :each do
+ ruby_exe("Process.kill(:KILL, $$); exit(42)")
+ end
+
+ platform_is_not :windows do
+
+ it "returns the signal" do
+ $?.termsig.should == Signal.list["KILL"]
+ end
+
+ end
+
+ platform_is :windows do
+
+ it "always returns nil" do
+ $?.termsig.should be_nil
+ end
+
+ end
+
+ end
+end
diff --git a/spec/ruby/core/process/status/to_i_spec.rb b/spec/ruby/core/process/status/to_i_spec.rb
new file mode 100644
index 0000000000..37b7bdb1e4
--- /dev/null
+++ b/spec/ruby/core/process/status/to_i_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Status#to_i" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/status/to_int_spec.rb b/spec/ruby/core/process/status/to_int_spec.rb
new file mode 100644
index 0000000000..fb596c1bfb
--- /dev/null
+++ b/spec/ruby/core/process/status/to_int_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Status#to_int" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/status/to_s_spec.rb b/spec/ruby/core/process/status/to_s_spec.rb
new file mode 100644
index 0000000000..5c0d4cd30c
--- /dev/null
+++ b/spec/ruby/core/process/status/to_s_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Status#to_s" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/sys/getegid_spec.rb b/spec/ruby/core/process/sys/getegid_spec.rb
new file mode 100644
index 0000000000..5b8588f147
--- /dev/null
+++ b/spec/ruby/core/process/sys/getegid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Sys.getegid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/sys/geteuid_spec.rb b/spec/ruby/core/process/sys/geteuid_spec.rb
new file mode 100644
index 0000000000..0ce7fc5459
--- /dev/null
+++ b/spec/ruby/core/process/sys/geteuid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Sys.geteuid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/sys/getgid_spec.rb b/spec/ruby/core/process/sys/getgid_spec.rb
new file mode 100644
index 0000000000..05c0fd4c0b
--- /dev/null
+++ b/spec/ruby/core/process/sys/getgid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Sys.getgid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/sys/getuid_spec.rb b/spec/ruby/core/process/sys/getuid_spec.rb
new file mode 100644
index 0000000000..56bcef01cc
--- /dev/null
+++ b/spec/ruby/core/process/sys/getuid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Sys.getuid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/sys/issetugid_spec.rb b/spec/ruby/core/process/sys/issetugid_spec.rb
new file mode 100644
index 0000000000..4fc7dc3bc6
--- /dev/null
+++ b/spec/ruby/core/process/sys/issetugid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Sys.issetugid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/sys/setegid_spec.rb b/spec/ruby/core/process/sys/setegid_spec.rb
new file mode 100644
index 0000000000..8f20330b94
--- /dev/null
+++ b/spec/ruby/core/process/sys/setegid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Sys.setegid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/sys/seteuid_spec.rb b/spec/ruby/core/process/sys/seteuid_spec.rb
new file mode 100644
index 0000000000..57d3a4800d
--- /dev/null
+++ b/spec/ruby/core/process/sys/seteuid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Sys.seteuid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/sys/setgid_spec.rb b/spec/ruby/core/process/sys/setgid_spec.rb
new file mode 100644
index 0000000000..cc712e0102
--- /dev/null
+++ b/spec/ruby/core/process/sys/setgid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Sys.setgid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/sys/setregid_spec.rb b/spec/ruby/core/process/sys/setregid_spec.rb
new file mode 100644
index 0000000000..57d491a707
--- /dev/null
+++ b/spec/ruby/core/process/sys/setregid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Sys.setregid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/sys/setresgid_spec.rb b/spec/ruby/core/process/sys/setresgid_spec.rb
new file mode 100644
index 0000000000..9be06612c1
--- /dev/null
+++ b/spec/ruby/core/process/sys/setresgid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Sys.setresgid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/sys/setresuid_spec.rb b/spec/ruby/core/process/sys/setresuid_spec.rb
new file mode 100644
index 0000000000..1092b349cb
--- /dev/null
+++ b/spec/ruby/core/process/sys/setresuid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Sys.setresuid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/sys/setreuid_spec.rb b/spec/ruby/core/process/sys/setreuid_spec.rb
new file mode 100644
index 0000000000..f3451c63c8
--- /dev/null
+++ b/spec/ruby/core/process/sys/setreuid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Sys.setreuid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/sys/setrgid_spec.rb b/spec/ruby/core/process/sys/setrgid_spec.rb
new file mode 100644
index 0000000000..27eea2ed86
--- /dev/null
+++ b/spec/ruby/core/process/sys/setrgid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Sys.setrgid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/sys/setruid_spec.rb b/spec/ruby/core/process/sys/setruid_spec.rb
new file mode 100644
index 0000000000..9dbd84bf68
--- /dev/null
+++ b/spec/ruby/core/process/sys/setruid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Sys.setruid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/sys/setuid_spec.rb b/spec/ruby/core/process/sys/setuid_spec.rb
new file mode 100644
index 0000000000..e06c3588a5
--- /dev/null
+++ b/spec/ruby/core/process/sys/setuid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Sys.setuid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/times_spec.rb b/spec/ruby/core/process/times_spec.rb
new file mode 100644
index 0000000000..07b4fa5c9f
--- /dev/null
+++ b/spec/ruby/core/process/times_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+
+describe "Process.times" do
+ it "returns a Process::Tms" do
+ Process.times.should be_kind_of(Process::Tms)
+ end
+
+ it "returns current cpu times" do
+ t = Process.times
+
+ # Do busy work for a wall-clock interval.
+ start = Time.now
+ 1 until (Time.now - start) > 0.5
+
+ # Ensure times is larger. NOTE that there is no
+ # guarantee of an upper bound since anything may be
+ # happening at the OS level, so we ONLY check that at
+ # least an interval has elapsed. Also, we are assuming
+ # there is a correlation between wall clock time and
+ # process time. In practice, there is an observed
+ # discrepancy often 10% or greater. In other words,
+ # this is a very fuzzy test.
+ t2 = Process.times
+ diff = (t2.utime + t2.stime) - (t.utime + t.stime)
+ diff.should > 0
+ end
+end
diff --git a/spec/ruby/core/process/tms/cstime_spec.rb b/spec/ruby/core/process/tms/cstime_spec.rb
new file mode 100644
index 0000000000..207d4391c0
--- /dev/null
+++ b/spec/ruby/core/process/tms/cstime_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Tms#cstime" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "Process::Tms#cstime=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/tms/cutime_spec.rb b/spec/ruby/core/process/tms/cutime_spec.rb
new file mode 100644
index 0000000000..390280f005
--- /dev/null
+++ b/spec/ruby/core/process/tms/cutime_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Tms#cutime" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "Process::Tms#cutime=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/tms/element_reference_spec.rb b/spec/ruby/core/process/tms/element_reference_spec.rb
new file mode 100644
index 0000000000..84a34089ae
--- /dev/null
+++ b/spec/ruby/core/process/tms/element_reference_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Tms.[]" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/tms/members_spec.rb b/spec/ruby/core/process/tms/members_spec.rb
new file mode 100644
index 0000000000..005a8baec1
--- /dev/null
+++ b/spec/ruby/core/process/tms/members_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Tms.members" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/tms/new_spec.rb b/spec/ruby/core/process/tms/new_spec.rb
new file mode 100644
index 0000000000..9dd1f5a8f2
--- /dev/null
+++ b/spec/ruby/core/process/tms/new_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Tms.new" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/tms/stime_spec.rb b/spec/ruby/core/process/tms/stime_spec.rb
new file mode 100644
index 0000000000..4104b625e2
--- /dev/null
+++ b/spec/ruby/core/process/tms/stime_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Tms#stime" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "Process::Tms#stime=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/tms/utime_spec.rb b/spec/ruby/core/process/tms/utime_spec.rb
new file mode 100644
index 0000000000..28371590e9
--- /dev/null
+++ b/spec/ruby/core/process/tms/utime_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+
+describe "Process::Tms#utime" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "Process::Tms#utime=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/uid/change_privilege_spec.rb b/spec/ruby/core/process/uid/change_privilege_spec.rb
new file mode 100644
index 0000000000..e4b552dd94
--- /dev/null
+++ b/spec/ruby/core/process/uid/change_privilege_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::UID.change_privilege" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/uid/eid_spec.rb b/spec/ruby/core/process/uid/eid_spec.rb
new file mode 100644
index 0000000000..f0bb9ce762
--- /dev/null
+++ b/spec/ruby/core/process/uid/eid_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+
+describe "Process::UID.eid" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "Process::UID.eid=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/uid/grant_privilege_spec.rb b/spec/ruby/core/process/uid/grant_privilege_spec.rb
new file mode 100644
index 0000000000..2b8a5c9102
--- /dev/null
+++ b/spec/ruby/core/process/uid/grant_privilege_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::UID.grant_privilege" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/uid/re_exchange_spec.rb b/spec/ruby/core/process/uid/re_exchange_spec.rb
new file mode 100644
index 0000000000..c0f10f33c4
--- /dev/null
+++ b/spec/ruby/core/process/uid/re_exchange_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::UID.re_exchange" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/uid/re_exchangeable_spec.rb b/spec/ruby/core/process/uid/re_exchangeable_spec.rb
new file mode 100644
index 0000000000..8200d7ecb7
--- /dev/null
+++ b/spec/ruby/core/process/uid/re_exchangeable_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::UID.re_exchangeable?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/uid/rid_spec.rb b/spec/ruby/core/process/uid/rid_spec.rb
new file mode 100644
index 0000000000..e865cbfef6
--- /dev/null
+++ b/spec/ruby/core/process/uid/rid_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::UID.rid" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/uid/sid_available_spec.rb b/spec/ruby/core/process/uid/sid_available_spec.rb
new file mode 100644
index 0000000000..be7912eb68
--- /dev/null
+++ b/spec/ruby/core/process/uid/sid_available_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::UID.sid_available?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/uid/switch_spec.rb b/spec/ruby/core/process/uid/switch_spec.rb
new file mode 100644
index 0000000000..4191b97db4
--- /dev/null
+++ b/spec/ruby/core/process/uid/switch_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../../spec_helper'
+
+describe "Process::UID.switch" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/uid_spec.rb b/spec/ruby/core/process/uid_spec.rb
new file mode 100644
index 0000000000..4a66beaa2a
--- /dev/null
+++ b/spec/ruby/core/process/uid_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../../spec_helper'
+
+describe "Process.uid" do
+ platform_is_not :windows do
+ it "returns the correct uid for the user executing this process" do
+ current_uid_according_to_unix = `id -ur`.to_i
+ Process.uid.should == current_uid_according_to_unix
+ end
+ end
+
+ it "also goes by Process::UID.rid" do
+ Process::UID.rid.should == Process.uid
+ end
+
+ it "also goes by Process::Sys.getuid" do
+ Process::Sys.getuid.should == Process.uid
+ end
+end
+
+describe "Process.uid=" do
+
+ platform_is_not :windows do
+ it "raises TypeError if not passed an Integer" do
+ lambda { Process.uid = Object.new }.should raise_error(TypeError)
+ end
+
+ as_user do
+ it "raises Errno::ERPERM if run by a non privileged user trying to set the superuser id" do
+ lambda { (Process.uid = 0)}.should raise_error(Errno::EPERM)
+ end
+
+ it "raises Errno::ERPERM if run by a non privileged user trying to set the superuser id from username" do
+ lambda { Process.uid = "root" }.should raise_error(Errno::EPERM)
+ end
+ end
+
+ as_superuser do
+ describe "if run by a superuser" do
+ with_feature :fork do
+ it "sets the real user id for the current process" do
+ read, write = IO.pipe
+ pid = Process.fork do
+ begin
+ read.close
+ Process.uid = 1
+ write << Process.uid
+ write.close
+ rescue Exception => e
+ write << e << e.backtrace
+ end
+ Process.exit!
+ end
+ write.close
+ uid = read.gets
+ uid.should == "1"
+ Process.wait pid
+ end
+
+ it "sets the real user id if preceded by Process.euid=id" do
+ read, write = IO.pipe
+ pid = Process.fork do
+ begin
+ read.close
+ Process.euid = 1
+ Process.uid = 1
+ write << Process.uid
+ write.close
+ rescue Exception => e
+ write << e << e.backtrace
+ end
+ Process.exit!
+ end
+ write.close
+ uid = read.gets
+ uid.should == "1"
+ Process.wait pid
+ end
+ end
+ end
+ end
+ end
+
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/wait2_spec.rb b/spec/ruby/core/process/wait2_spec.rb
new file mode 100644
index 0000000000..d0163f80af
--- /dev/null
+++ b/spec/ruby/core/process/wait2_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+
+describe "Process.wait2" do
+ before :all do
+ # HACK: this kludge is temporarily necessary because some
+ # misbehaving spec somewhere else does not clear processes
+ # Note: background processes are unavoidable with MJIT,
+ # but we shouldn't reap them from Ruby-space
+ begin
+ Process.wait(-1, Process::WNOHANG)
+ without_feature :mjit do
+ $stderr.puts "Leaked process before wait2 specs! Waiting for it"
+ end
+ leaked = Process.waitall
+ $stderr.puts "leaked before wait2 specs: #{leaked}" unless leaked.empty?
+ with_feature :mjit do
+ # Ruby-space should not see PIDs used by mjit
+ leaked.should be_empty
+ end
+ rescue Errno::ECHILD # No child processes
+ rescue NotImplementedError
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns the pid and status of child process" do
+ pidf = Process.fork { Process.exit! 99 }
+ results = Process.wait2
+ results.size.should == 2
+ pidw, status = results
+ pidf.should == pidw
+ status.exitstatus.should == 99
+ end
+ end
+
+ it "raises a StandardError if no child processes exist" do
+ lambda { Process.wait2 }.should raise_error(Errno::ECHILD)
+ lambda { Process.wait2 }.should raise_error(StandardError)
+ end
+end
diff --git a/spec/ruby/core/process/wait_spec.rb b/spec/ruby/core/process/wait_spec.rb
new file mode 100644
index 0000000000..5130bb4391
--- /dev/null
+++ b/spec/ruby/core/process/wait_spec.rb
@@ -0,0 +1,94 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Process.wait" do
+ ProcessSpecs.use_system_ruby(self)
+
+ before :all do
+ begin
+ leaked = Process.waitall
+ puts "leaked before wait specs: #{leaked}" unless leaked.empty?
+ with_feature :mjit do
+ # Ruby-space should not see PIDs used by mjit
+ leaked.should be_empty
+ end
+ rescue NotImplementedError
+ end
+ end
+
+ it "raises an Errno::ECHILD if there are no child processes" do
+ lambda { Process.wait }.should raise_error(Errno::ECHILD)
+ end
+
+ platform_is_not :windows do
+ it "returns its childs pid" do
+ pid = Process.spawn(ruby_cmd('exit'))
+ Process.wait.should == pid
+ end
+
+ it "sets $? to a Process::Status" do
+ pid = Process.spawn(ruby_cmd('exit'))
+ Process.wait
+ $?.should be_kind_of(Process::Status)
+ $?.pid.should == pid
+ end
+
+ it "waits for any child process if no pid is given" do
+ pid = Process.spawn(ruby_cmd('exit'))
+ Process.wait.should == pid
+ lambda { Process.kill(0, pid) }.should raise_error(Errno::ESRCH)
+ end
+
+ it "waits for a specific child if a pid is given" do
+ pid1 = Process.spawn(ruby_cmd('exit'))
+ pid2 = Process.spawn(ruby_cmd('exit'))
+ Process.wait(pid2).should == pid2
+ Process.wait(pid1).should == pid1
+ lambda { Process.kill(0, pid1) }.should raise_error(Errno::ESRCH)
+ lambda { Process.kill(0, pid2) }.should raise_error(Errno::ESRCH)
+ end
+
+ it "coerces the pid to an Integer" do
+ pid1 = Process.spawn(ruby_cmd('exit'))
+ Process.wait(mock_int(pid1)).should == pid1
+ lambda { Process.kill(0, pid1) }.should raise_error(Errno::ESRCH)
+ end
+
+ # This spec is probably system-dependent.
+ it "waits for a child whose process group ID is that of the calling process" do
+ pid1 = Process.spawn(ruby_cmd('exit'), pgroup: true)
+ pid2 = Process.spawn(ruby_cmd('exit'))
+
+ Process.wait(0).should == pid2
+ Process.wait.should == pid1
+ end
+
+ # This spec is probably system-dependent.
+ it "doesn't block if no child is available when WNOHANG is used" do
+ read, write = IO.pipe
+ pid = Process.fork do
+ read.close
+ Signal.trap("TERM") { Process.exit! }
+ write << 1
+ write.close
+ sleep
+ end
+
+ Process.wait(pid, Process::WNOHANG).should be_nil
+
+ # wait for the child to setup its TERM handler
+ write.close
+ read.read(1)
+ read.close
+
+ Process.kill("TERM", pid)
+ Process.wait.should == pid
+ end
+
+ it "always accepts flags=0" do
+ pid = Process.spawn(ruby_cmd('exit'))
+ Process.wait(-1, 0).should == pid
+ lambda { Process.kill(0, pid) }.should raise_error(Errno::ESRCH)
+ end
+ end
+end
diff --git a/spec/ruby/core/process/waitall_spec.rb b/spec/ruby/core/process/waitall_spec.rb
new file mode 100644
index 0000000000..bdc1ea7490
--- /dev/null
+++ b/spec/ruby/core/process/waitall_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+
+describe "Process.waitall" do
+ before :all do
+ begin
+ Process.waitall
+ rescue NotImplementedError
+ end
+ end
+
+ it "returns an empty array when there are no children" do
+ Process.waitall.should == []
+ end
+
+ it "takes no arguments" do
+ lambda { Process.waitall(0) }.should raise_error(ArgumentError)
+ end
+
+ platform_is_not :windows do
+ it "waits for all children" do
+ pids = []
+ pids << Process.fork { Process.exit! 2 }
+ pids << Process.fork { Process.exit! 1 }
+ pids << Process.fork { Process.exit! 0 }
+ Process.waitall
+ pids.each { |pid|
+ lambda { Process.kill(0, pid) }.should raise_error(Errno::ESRCH)
+ }
+ end
+
+ it "returns an array of pid/status pairs" do
+ pids = []
+ pids << Process.fork { Process.exit! 2 }
+ pids << Process.fork { Process.exit! 1 }
+ pids << Process.fork { Process.exit! 0 }
+ a = Process.waitall
+ a.should be_kind_of(Array)
+ a.size.should == 3
+ pids.each { |pid|
+ pid_status = a.assoc(pid)
+ pid_status.should be_kind_of(Array)
+ pid_status.size.should == 2
+ pid_status.first.should == pid
+ pid_status.last.should be_kind_of(Process::Status)
+ }
+ end
+ end
+end
diff --git a/spec/ruby/core/process/waitpid2_spec.rb b/spec/ruby/core/process/waitpid2_spec.rb
new file mode 100644
index 0000000000..45513af667
--- /dev/null
+++ b/spec/ruby/core/process/waitpid2_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Process.waitpid2" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/process/waitpid_spec.rb b/spec/ruby/core/process/waitpid_spec.rb
new file mode 100644
index 0000000000..fdd6be1287
--- /dev/null
+++ b/spec/ruby/core/process/waitpid_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "Process.waitpid" do
+ it "needs to be reviewed for spec completeness"
+
+ it "returns nil when the process has not yet completed and WNOHANG is specified" do
+ pid = spawn("sleep 5")
+ begin
+ Process.waitpid(pid, Process::WNOHANG).should == nil
+ Process.kill("KILL", pid)
+ ensure
+ Process.wait(pid)
+ end
+ end
+end
diff --git a/spec/ruby/core/queue/append_spec.rb b/spec/ruby/core/queue/append_spec.rb
new file mode 100644
index 0000000000..34165a7506
--- /dev/null
+++ b/spec/ruby/core/queue/append_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/enque'
+
+describe "Queue#<<" do
+ it_behaves_like :queue_enq, :<<, -> { Queue.new }
+end
diff --git a/spec/ruby/core/queue/clear_spec.rb b/spec/ruby/core/queue/clear_spec.rb
new file mode 100644
index 0000000000..3245e4cb83
--- /dev/null
+++ b/spec/ruby/core/queue/clear_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/clear'
+
+describe "Queue#clear" do
+ it_behaves_like :queue_clear, :clear, -> { Queue.new }
+end
diff --git a/spec/ruby/core/queue/close_spec.rb b/spec/ruby/core/queue/close_spec.rb
new file mode 100644
index 0000000000..c0d774cd74
--- /dev/null
+++ b/spec/ruby/core/queue/close_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/close'
+
+describe "Queue#close" do
+ it_behaves_like :queue_close, :close, -> { Queue.new }
+end
diff --git a/spec/ruby/core/queue/closed_spec.rb b/spec/ruby/core/queue/closed_spec.rb
new file mode 100644
index 0000000000..10d552996d
--- /dev/null
+++ b/spec/ruby/core/queue/closed_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/closed'
+
+describe "Queue#closed?" do
+ it_behaves_like :queue_closed?, :closed?, -> { Queue.new }
+end
diff --git a/spec/ruby/core/queue/deq_spec.rb b/spec/ruby/core/queue/deq_spec.rb
new file mode 100644
index 0000000000..9510978eac
--- /dev/null
+++ b/spec/ruby/core/queue/deq_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/deque'
+
+describe "Queue#deq" do
+ it_behaves_like :queue_deq, :deq, -> { Queue.new }
+end
diff --git a/spec/ruby/core/queue/empty_spec.rb b/spec/ruby/core/queue/empty_spec.rb
new file mode 100644
index 0000000000..55ca777466
--- /dev/null
+++ b/spec/ruby/core/queue/empty_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/empty'
+
+describe "Queue#empty?" do
+ it_behaves_like :queue_empty?, :empty?, -> { Queue.new }
+end
diff --git a/spec/ruby/core/queue/enq_spec.rb b/spec/ruby/core/queue/enq_spec.rb
new file mode 100644
index 0000000000..c69c496fbc
--- /dev/null
+++ b/spec/ruby/core/queue/enq_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/enque'
+
+describe "Queue#enq" do
+ it_behaves_like :queue_enq, :enq, -> { Queue.new }
+end
diff --git a/spec/ruby/core/queue/length_spec.rb b/spec/ruby/core/queue/length_spec.rb
new file mode 100644
index 0000000000..25399b2b76
--- /dev/null
+++ b/spec/ruby/core/queue/length_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/length'
+
+describe "Queue#length" do
+ it_behaves_like :queue_length, :length, -> { Queue.new }
+end
diff --git a/spec/ruby/core/queue/num_waiting_spec.rb b/spec/ruby/core/queue/num_waiting_spec.rb
new file mode 100644
index 0000000000..edc0c37a82
--- /dev/null
+++ b/spec/ruby/core/queue/num_waiting_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/num_waiting'
+
+describe "Queue#num_waiting" do
+ it_behaves_like :queue_num_waiting, :num_waiting, -> { Queue.new }
+end
diff --git a/spec/ruby/core/queue/pop_spec.rb b/spec/ruby/core/queue/pop_spec.rb
new file mode 100644
index 0000000000..1ce9231685
--- /dev/null
+++ b/spec/ruby/core/queue/pop_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/deque'
+
+describe "Queue#pop" do
+ it_behaves_like :queue_deq, :pop, -> { Queue.new }
+end
diff --git a/spec/ruby/core/queue/push_spec.rb b/spec/ruby/core/queue/push_spec.rb
new file mode 100644
index 0000000000..e936f9d282
--- /dev/null
+++ b/spec/ruby/core/queue/push_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/enque'
+
+describe "Queue#push" do
+ it_behaves_like :queue_enq, :push, -> { Queue.new }
+end
diff --git a/spec/ruby/core/queue/shift_spec.rb b/spec/ruby/core/queue/shift_spec.rb
new file mode 100644
index 0000000000..f84058e1df
--- /dev/null
+++ b/spec/ruby/core/queue/shift_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/deque'
+
+describe "Queue#shift" do
+ it_behaves_like :queue_deq, :shift, -> { Queue.new }
+end
diff --git a/spec/ruby/core/queue/size_spec.rb b/spec/ruby/core/queue/size_spec.rb
new file mode 100644
index 0000000000..f528dfe797
--- /dev/null
+++ b/spec/ruby/core/queue/size_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/length'
+
+describe "Queue#size" do
+ it_behaves_like :queue_length, :size, -> { Queue.new }
+end
diff --git a/spec/ruby/core/random/bytes_spec.rb b/spec/ruby/core/random/bytes_spec.rb
new file mode 100644
index 0000000000..2caf18fbd0
--- /dev/null
+++ b/spec/ruby/core/random/bytes_spec.rb
@@ -0,0 +1,32 @@
+# -*- encoding: binary -*-
+require_relative '../../spec_helper'
+require_relative 'shared/bytes'
+
+describe "Random#bytes" do
+ it_behaves_like :random_bytes, :bytes, Random.new
+
+ it "returns the same output for a given seed" do
+ Random.new(33).bytes(2).should == Random.new(33).bytes(2)
+ end
+
+ # Should double check this is official spec
+ it "returns the same numeric output for a given seed across all implementations and platforms" do
+ rnd = Random.new(33)
+ rnd.bytes(2).should == "\x14\\"
+ rnd.bytes(1000) # skip some
+ rnd.bytes(2).should == "\xA1p"
+ end
+
+ it "returns the same numeric output for a given huge seed across all implementations and platforms" do
+ rnd = Random.new(bignum_value ** 4)
+ rnd.bytes(2).should == "_\x91"
+ rnd.bytes(1000) # skip some
+ rnd.bytes(2).should == "\x17\x12"
+ end
+end
+
+ruby_version_is "2.6" do
+ describe "Random.bytes" do
+ it_behaves_like :random_bytes, :bytes, Random
+ end
+end
diff --git a/spec/ruby/core/random/default_spec.rb b/spec/ruby/core/random/default_spec.rb
new file mode 100644
index 0000000000..1d8b1ce5ee
--- /dev/null
+++ b/spec/ruby/core/random/default_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Random::DEFAULT" do
+ it "returns a Random instance" do
+ Random::DEFAULT.should be_an_instance_of(Random)
+ end
+end
diff --git a/spec/ruby/core/random/equal_value_spec.rb b/spec/ruby/core/random/equal_value_spec.rb
new file mode 100644
index 0000000000..5f470d6a4c
--- /dev/null
+++ b/spec/ruby/core/random/equal_value_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+
+describe "Random#==" do
+ it "returns true if the two objects have the same state" do
+ a = Random.new(42)
+ b = Random.new(42)
+ a.send(:state).should == b.send(:state)
+ a.should == b
+ end
+
+ it "returns false if the two objects have different state" do
+ a = Random.new
+ b = Random.new
+ a.send(:state).should_not == b.send(:state)
+ a.should_not == b
+ end
+
+ it "returns true if the two objects have the same seed" do
+ a = Random.new(42)
+ b = Random.new(42.5)
+ a.seed.should == b.seed
+ a.should == b
+ end
+
+ it "returns false if the two objects have a different seed" do
+ a = Random.new(42)
+ b = Random.new(41)
+ a.seed.should_not == b.seed
+ a.should_not == b
+ end
+
+ it "returns false if the other object is not a Random" do
+ a = Random.new(42)
+ a.should_not == 42
+ a.should_not == [a]
+ end
+end
diff --git a/spec/ruby/core/random/new_seed_spec.rb b/spec/ruby/core/random/new_seed_spec.rb
new file mode 100644
index 0000000000..4b34e871a2
--- /dev/null
+++ b/spec/ruby/core/random/new_seed_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+
+describe "Random.new_seed" do
+ it "returns a Bignum" do
+ Random.new_seed.should be_an_instance_of(Bignum)
+ end
+
+ it "returns an arbitrary seed value each time" do
+ bigs = 200.times.map { Random.new_seed }
+ bigs.uniq.size.should == 200
+ end
+
+ it "is not affected by Kernel#srand" do
+ begin
+ srand 25
+ a = Random.new_seed
+ srand 25
+ b = Random.new_seed
+ a.should_not == b
+ ensure
+ srand Random.new_seed
+ end
+ end
+end
diff --git a/spec/ruby/core/random/new_spec.rb b/spec/ruby/core/random/new_spec.rb
new file mode 100644
index 0000000000..8160f44d79
--- /dev/null
+++ b/spec/ruby/core/random/new_spec.rb
@@ -0,0 +1,37 @@
+describe "Random.new" do
+ it "returns a new instance of Random" do
+ Random.new.should be_an_instance_of(Random)
+ end
+
+ it "uses a random seed value if none is supplied" do
+ Random.new.seed.should be_an_instance_of(Bignum)
+ end
+
+ it "returns Random instances initialized with different seeds" do
+ first = Random.new
+ second = Random.new
+ (0..20).map { first.rand } .should_not == (0..20).map { second.rand }
+ end
+
+ it "accepts an Integer seed value as an argument" do
+ Random.new(2).seed.should == 2
+ end
+
+ it "accepts (and truncates) a Float seed value as an argument" do
+ Random.new(3.4).seed.should == 3
+ end
+
+ it "accepts (and converts to Integer) a Rational seed value as an argument" do
+ Random.new(Rational(20,2)).seed.should == 10
+ end
+
+ it "accepts (and converts to Integer) a Complex (without imaginary part) seed value as an argument" do
+ Random.new(Complex(20)).seed.should == 20
+ end
+
+ it "raises a RangeError if passed a Complex (with imaginary part) seed value as an argument" do
+ lambda do
+ Random.new(Complex(20,2))
+ end.should raise_error(RangeError)
+ end
+end
diff --git a/spec/ruby/core/random/rand_spec.rb b/spec/ruby/core/random/rand_spec.rb
new file mode 100644
index 0000000000..395e89dc75
--- /dev/null
+++ b/spec/ruby/core/random/rand_spec.rb
@@ -0,0 +1,216 @@
+require_relative '../../spec_helper'
+
+describe "Random.rand" do
+ it "returns a Float if no max argument is passed" do
+ Random.rand.should be_kind_of(Float)
+ end
+
+ it "returns a Float >= 0 if no max argument is passed" do
+ floats = 200.times.map { Random.rand }
+ floats.min.should >= 0
+ end
+
+ it "returns a Float < 1 if no max argument is passed" do
+ floats = 200.times.map { Random.rand }
+ floats.max.should < 1
+ end
+
+ it "returns the same sequence for a given seed if no max argument is passed" do
+ Random.srand 33
+ floats_a = 20.times.map { Random.rand }
+ Random.srand 33
+ floats_b = 20.times.map { Random.rand }
+ floats_a.should == floats_b
+ end
+
+ it "returns an Integer if an Integer argument is passed" do
+ Random.rand(20).should be_kind_of(Integer)
+ end
+
+ it "returns an Integer >= 0 if an Integer argument is passed" do
+ ints = 200.times.map { Random.rand(34) }
+ ints.min.should >= 0
+ end
+
+ it "returns an Integer < the max argument if an Integer argument is passed" do
+ ints = 200.times.map { Random.rand(55) }
+ ints.max.should < 55
+ end
+
+ it "returns the same sequence for a given seed if an Integer argument is passed" do
+ Random.srand 33
+ floats_a = 20.times.map { Random.rand(90) }
+ Random.srand 33
+ floats_b = 20.times.map { Random.rand(90) }
+ floats_a.should == floats_b
+ end
+
+ it "coerces arguments to Integers with #to_int" do
+ obj = mock_numeric('int')
+ obj.should_receive(:to_int).and_return(99)
+ Random.rand(obj).should be_kind_of(Integer)
+ end
+end
+
+describe "Random#rand with Fixnum" do
+ it "returns an Integer" do
+ Random.new.rand(20).should be_an_instance_of(Fixnum)
+ end
+
+ it "returns a Fixnum greater than or equal to 0" do
+ prng = Random.new
+ ints = 20.times.map { prng.rand(5) }
+ ints.min.should >= 0
+ end
+
+ it "returns a Fixnum less than the argument" do
+ prng = Random.new
+ ints = 20.times.map { prng.rand(5) }
+ ints.max.should <= 4
+ end
+
+ it "returns the same sequence for a given seed" do
+ prng = Random.new 33
+ a = 20.times.map { prng.rand(90) }
+ prng = Random.new 33
+ b = 20.times.map { prng.rand(90) }
+ a.should == b
+ end
+
+ it "eventually returns all possible values" do
+ prng = Random.new 33
+ 100.times.map{ prng.rand(10) }.uniq.sort.should == (0...10).to_a
+ end
+
+ it "raises an ArgumentError when the argument is 0" do
+ lambda do
+ Random.new.rand(0)
+ end.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the argument is negative" do
+ lambda do
+ Random.new.rand(-12)
+ end.should raise_error(ArgumentError)
+ end
+end
+
+describe "Random#rand with Bignum" do
+ it "typically returns a Bignum" do
+ rnd = Random.new(1)
+ 10.times.map{ rnd.rand(bignum_value*2) }.max.should be_an_instance_of(Bignum)
+ end
+
+ it "returns a Bignum greater than or equal to 0" do
+ prng = Random.new
+ bigs = 20.times.map { prng.rand(bignum_value) }
+ bigs.min.should >= 0
+ end
+
+ it "returns a Bignum less than the argument" do
+ prng = Random.new
+ bigs = 20.times.map { prng.rand(bignum_value) }
+ bigs.max.should < bignum_value
+ end
+
+ it "returns the same sequence for a given seed" do
+ prng = Random.new 33
+ a = 20.times.map { prng.rand(bignum_value) }
+ prng = Random.new 33
+ b = 20.times.map { prng.rand(bignum_value) }
+ a.should == b
+ end
+
+ it "raises an ArgumentError when the argument is negative" do
+ lambda do
+ Random.new.rand(-bignum_value)
+ end.should raise_error(ArgumentError)
+ end
+end
+
+describe "Random#rand with Float" do
+ it "returns a Float" do
+ Random.new.rand(20.43).should be_an_instance_of(Float)
+ end
+
+ it "returns a Float greater than or equal to 0.0" do
+ prng = Random.new
+ floats = 20.times.map { prng.rand(5.2) }
+ floats.min.should >= 0.0
+ end
+
+ it "returns a Float less than the argument" do
+ prng = Random.new
+ floats = 20.times.map { prng.rand(4.30) }
+ floats.max.should < 4.30
+ end
+
+ it "returns the same sequence for a given seed" do
+ prng = Random.new 33
+ a = 20.times.map { prng.rand(89.2928) }
+ prng = Random.new 33
+ b = 20.times.map { prng.rand(89.2928) }
+ a.should == b
+ end
+
+ it "raises an ArgumentError when the argument is negative" do
+ lambda do
+ Random.new.rand(-1.234567)
+ end.should raise_error(ArgumentError)
+ end
+end
+
+describe "Random#rand with Range" do
+ it "returns an element from the Range" do
+ Random.new.rand(20..43).should be_an_instance_of(Fixnum)
+ end
+
+ it "returns an object that is a member of the Range" do
+ prng = Random.new
+ r = 20..30
+ 20.times { r.member?(prng.rand(r)).should be_true }
+ end
+
+ it "works with inclusive ranges" do
+ prng = Random.new 33
+ r = 3..5
+ 40.times.map { prng.rand(r) }.uniq.sort.should == [3,4,5]
+ end
+
+ it "works with exclusive ranges" do
+ prng = Random.new 33
+ r = 3...5
+ 20.times.map { prng.rand(r) }.uniq.sort.should == [3,4]
+ end
+
+ it "returns the same sequence for a given seed" do
+ prng = Random.new 33
+ a = 20.times.map { prng.rand(76890.028..800000.00) }
+ prng = Random.new 33
+ b = 20.times.map { prng.rand(76890.028..800000.00) }
+ a.should == b
+ end
+
+ it "eventually returns all possible values" do
+ prng = Random.new 33
+ 100.times.map{ prng.rand(10..20) }.uniq.sort.should == (10..20).to_a
+ 100.times.map{ prng.rand(10...20) }.uniq.sort.should == (10...20).to_a
+ end
+
+ it "considers Integers as Floats if one end point is a float" do
+ Random.new(42).rand(0.0..1).should be_kind_of(Float)
+ Random.new(42).rand(0..1.0).should be_kind_of(Float)
+ end
+
+ it "raises an ArgumentError when the startpoint lacks #+ and #- methods" do
+ lambda do
+ Random.new.rand(Object.new..67)
+ end.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the endpoint lacks #+ and #- methods" do
+ lambda do
+ Random.new.rand(68..Object.new)
+ end.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/random/raw_seed_spec.rb b/spec/ruby/core/random/raw_seed_spec.rb
new file mode 100644
index 0000000000..c1a1eb1f42
--- /dev/null
+++ b/spec/ruby/core/random/raw_seed_spec.rb
@@ -0,0 +1,9 @@
+# -*- encoding: binary -*-
+require_relative '../../spec_helper'
+require_relative 'shared/urandom'
+
+ruby_version_is "2.5" do
+ describe "Random.urandom" do
+ it_behaves_like :random_urandom, :urandom
+ end
+end
diff --git a/spec/ruby/core/random/seed_spec.rb b/spec/ruby/core/random/seed_spec.rb
new file mode 100644
index 0000000000..bf4524fdd9
--- /dev/null
+++ b/spec/ruby/core/random/seed_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+
+describe "Random#seed" do
+ it "returns an Integer" do
+ Random.new.seed.should be_kind_of(Integer)
+ end
+
+ it "returns an arbitrary seed if the constructor was called without arguments" do
+ Random.new.seed.should_not == Random.new.seed
+ end
+
+ it "returns the same generated seed when repeatedly called on the same object" do
+ prng = Random.new
+ prng.seed.should == prng.seed
+ end
+
+ it "returns the seed given in the constructor" do
+ prng = Random.new(36788)
+ prng.seed.should == prng.seed
+ prng.seed.should == 36788
+ end
+
+ it "returns the given seed coerced with #to_int" do
+ obj = mock_numeric('int')
+ obj.should_receive(:to_int).and_return(34)
+ prng = Random.new(obj)
+ prng.seed.should == 34
+ end
+end
diff --git a/spec/ruby/core/random/shared/bytes.rb b/spec/ruby/core/random/shared/bytes.rb
new file mode 100644
index 0000000000..3485d0f221
--- /dev/null
+++ b/spec/ruby/core/random/shared/bytes.rb
@@ -0,0 +1,17 @@
+describe :random_bytes, shared: true do
+ it "returns a String" do
+ @object.bytes(1).should be_an_instance_of(String)
+ end
+
+ it "returns a String of the length given as argument" do
+ @object.bytes(15).length.should == 15
+ end
+
+ it "returns an ASCII-8BIT String" do
+ @object.bytes(15).encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "returns a random binary String" do
+ @object.bytes(12).should_not == @object.bytes(12)
+ end
+end
diff --git a/spec/ruby/core/random/shared/urandom.rb b/spec/ruby/core/random/shared/urandom.rb
new file mode 100644
index 0000000000..f50d30c9de
--- /dev/null
+++ b/spec/ruby/core/random/shared/urandom.rb
@@ -0,0 +1,23 @@
+describe :random_urandom, shared: true do
+ it "returns a String" do
+ Random.send(@method, 1).should be_an_instance_of(String)
+ end
+
+ it "returns a String of the length given as argument" do
+ Random.send(@method, 15).length.should == 15
+ end
+
+ it "raises an ArgumentError on a negative size" do
+ lambda {
+ Random.send(@method, -1)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "returns an ASCII-8BIT String" do
+ Random.send(@method, 15).encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "returns a random binary String" do
+ Random.send(@method, 12).should_not == Random.send(@method, 12)
+ end
+end
diff --git a/spec/ruby/core/random/srand_spec.rb b/spec/ruby/core/random/srand_spec.rb
new file mode 100644
index 0000000000..1ef8e32cfb
--- /dev/null
+++ b/spec/ruby/core/random/srand_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+
+describe "Random.srand" do
+ it "returns an arbitrary seed if .srand wasn't called previously with an argument and no argument is supplied this time" do
+ Random.srand # Reset to random seed in case .srand was called previously
+ Random.srand.should_not == Random.srand
+ end
+
+ it "returns the previous argument to .srand if one was given and no argument is supplied" do
+ Random.srand 34
+ Random.srand.should == 34
+ end
+
+ it "returns an arbitrary seed if .srand wasn't called previously with an argument and 0 is supplied this time" do
+ Random.srand # Reset to random seed in case .srand was called previously
+ Random.srand(0).should_not == Random.srand(0)
+ end
+
+ it "returns the previous argument to .srand if one was given and 0 is supplied" do
+ Random.srand 34
+ Random.srand(0).should == 34
+ end
+
+ it "seeds Random.rand such that its return value is deterministic" do
+ Random.srand 176542
+ a = 20.times.map { Random.rand }
+ Random.srand 176542
+ b = 20.times.map { Random.rand }
+ a.should == b
+ end
+
+ it "seeds Kernel.rand such that its return value is deterministic" do
+ Random.srand 176542
+ a = 20.times.map { Kernel.rand }
+ Random.srand 176542
+ b = 20.times.map { Kernel.rand }
+ a.should == b
+ end
+end
diff --git a/spec/ruby/core/random/urandom_spec.rb b/spec/ruby/core/random/urandom_spec.rb
new file mode 100644
index 0000000000..e27f83cdcd
--- /dev/null
+++ b/spec/ruby/core/random/urandom_spec.rb
@@ -0,0 +1,9 @@
+# -*- encoding: binary -*-
+require_relative '../../spec_helper'
+require_relative 'shared/urandom'
+
+ruby_version_is ""..."2.5" do
+ describe "Random.raw_seed" do
+ it_behaves_like :random_urandom, :raw_seed
+ end
+end
diff --git a/spec/ruby/core/range/begin_spec.rb b/spec/ruby/core/range/begin_spec.rb
new file mode 100644
index 0000000000..ab82b45b7e
--- /dev/null
+++ b/spec/ruby/core/range/begin_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/begin'
+
+describe "Range#begin" do
+ it_behaves_like :range_begin, :begin
+end
diff --git a/spec/ruby/core/range/bsearch_spec.rb b/spec/ruby/core/range/bsearch_spec.rb
new file mode 100644
index 0000000000..399bd49623
--- /dev/null
+++ b/spec/ruby/core/range/bsearch_spec.rb
@@ -0,0 +1,137 @@
+require_relative '../../spec_helper'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Range#bsearch" do
+ it "returns an Enumerator when not passed a block" do
+ (0..1).bsearch.should be_an_instance_of(Enumerator)
+ end
+
+ it_behaves_like :enumeratorized_with_unknown_size, :bsearch, (1..3)
+
+ it "raises a TypeError if the block returns an Object" do
+ lambda { (0..1).bsearch { Object.new } }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the block returns a String" do
+ lambda { (0..1).bsearch { "1" } }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the Range has Object values" do
+ value = mock("range bsearch")
+ r = Range.new value, value
+
+ lambda { r.bsearch { true } }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the Range has String values" do
+ lambda { ("a".."e").bsearch { true } }.should raise_error(TypeError)
+ end
+
+ context "with Integer values" do
+ context "with a block returning true or false" do
+ it "returns nil if the block returns false for every element" do
+ (0...3).bsearch { |x| x > 3 }.should be_nil
+ end
+
+ it "returns nil if the block returns nil for every element" do
+ (0..3).bsearch { |x| nil }.should be_nil
+ end
+
+ it "returns minimum element if the block returns true for every element" do
+ (-2..4).bsearch { |x| x < 4 }.should == -2
+ end
+
+ it "returns the smallest element for which block returns true" do
+ (0..4).bsearch { |x| x >= 2 }.should == 2
+ (-1..4).bsearch { |x| x >= 1 }.should == 1
+ end
+
+ it "returns the last element if the block returns true for the last element" do
+ (0..4).bsearch { |x| x >= 4 }.should == 4
+ (0...4).bsearch { |x| x >= 3 }.should == 3
+ end
+ end
+
+ context "with a block returning negative, zero, positive numbers" do
+ it "returns nil if the block returns less than zero for every element" do
+ (0..3).bsearch { |x| x <=> 5 }.should be_nil
+ end
+
+ it "returns nil if the block returns greater than zero for every element" do
+ (0..3).bsearch { |x| x <=> -1 }.should be_nil
+
+ end
+
+ it "returns nil if the block never returns zero" do
+ (0..3).bsearch { |x| x < 2 ? 1 : -1 }.should be_nil
+ end
+
+ it "accepts (+/-)Float::INFINITY from the block" do
+ (0..4).bsearch { |x| Float::INFINITY }.should be_nil
+ (0..4).bsearch { |x| -Float::INFINITY }.should be_nil
+ end
+
+ it "returns an element at an index for which block returns 0.0" do
+ result = (0..4).bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 }
+ result.should == 2
+ end
+
+ it "returns an element at an index for which block returns 0" do
+ result = (0..4).bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 }
+ [1, 2].should include(result)
+ end
+ end
+ end
+
+ context "with Float values" do
+ context "with a block returning true or false" do
+ it "returns nil if the block returns false for every element" do
+ (0.1...2.3).bsearch { |x| x > 3 }.should be_nil
+ end
+
+ it "returns nil if the block returns nil for every element" do
+ (-0.0..2.3).bsearch { |x| nil }.should be_nil
+ end
+
+ it "returns minimum element if the block returns true for every element" do
+ (-0.2..4.8).bsearch { |x| x < 4 }.should == -0.2
+ end
+
+ it "returns the smallest element for which block returns true" do
+ (0..4.2).bsearch { |x| x >= 2 }.should == 2
+ (-1.2..4.3).bsearch { |x| x >= 1 }.should == 1
+ end
+ end
+
+ context "with a block returning negative, zero, positive numbers" do
+ it "returns nil if the block returns less than zero for every element" do
+ (-2.0..3.2).bsearch { |x| x <=> 5 }.should be_nil
+ end
+
+ it "returns nil if the block returns greater than zero for every element" do
+ (0.3..3.0).bsearch { |x| x <=> -1 }.should be_nil
+
+ end
+
+ it "returns nil if the block never returns zero" do
+ (0.2..2.3).bsearch { |x| x < 2 ? 1 : -1 }.should be_nil
+ end
+
+ it "accepts (+/-)Float::INFINITY from the block" do
+ (0.1..4.5).bsearch { |x| Float::INFINITY }.should be_nil
+ (-5.0..4.0).bsearch { |x| -Float::INFINITY }.should be_nil
+ end
+
+ it "returns an element at an index for which block returns 0.0" do
+ result = (0.0..4.0).bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 }
+ result.should == 2
+ end
+
+ it "returns an element at an index for which block returns 0" do
+ result = (0.1..4.9).bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 }
+ result.should >= 1
+ result.should <= 2
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/range/case_compare_spec.rb b/spec/ruby/core/range/case_compare_spec.rb
new file mode 100644
index 0000000000..9a33c5b73b
--- /dev/null
+++ b/spec/ruby/core/range/case_compare_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require_relative 'shared/cover_and_include'
+require_relative 'shared/cover'
+
+describe "Range#===" do
+ ruby_version_is ""..."2.6" do
+ it "returns the result of calling #include? on self" do
+ range = 0...10
+ range.should_receive(:include?).with(2).and_return(:true)
+ (range === 2).should == :true
+ end
+ end
+
+ ruby_version_is "2.6" do
+ it "returns the result of calling #cover? on self" do
+ range = RangeSpecs::Custom.new(0)..RangeSpecs::Custom.new(10)
+ (range === RangeSpecs::Custom.new(2)).should == true
+ end
+ end
+end
diff --git a/spec/ruby/core/range/cover_spec.rb b/spec/ruby/core/range/cover_spec.rb
new file mode 100644
index 0000000000..47b75742b2
--- /dev/null
+++ b/spec/ruby/core/range/cover_spec.rb
@@ -0,0 +1,9 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../spec_helper'
+require_relative 'shared/cover_and_include'
+require_relative 'shared/cover'
+
+describe "Range#cover?" do
+ it_behaves_like :range_cover_and_include, :cover?
+ it_behaves_like :range_cover, :cover?
+end
diff --git a/spec/ruby/core/range/dup_spec.rb b/spec/ruby/core/range/dup_spec.rb
new file mode 100644
index 0000000000..d1c029c6b7
--- /dev/null
+++ b/spec/ruby/core/range/dup_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "Range#dup" do
+ it "duplicates the range" do
+ copy = (1..3).dup
+ copy.begin.should == 1
+ copy.end.should == 3
+ copy.exclude_end?.should == false
+
+ copy = ("a"..."z").dup
+ copy.begin.should == "a"
+ copy.end.should == "z"
+ copy.exclude_end?.should == true
+ end
+end
diff --git a/spec/ruby/core/range/each_spec.rb b/spec/ruby/core/range/each_spec.rb
new file mode 100644
index 0000000000..c5253dafd9
--- /dev/null
+++ b/spec/ruby/core/range/each_spec.rb
@@ -0,0 +1,66 @@
+require_relative '../../spec_helper'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Range#each" do
+ it "passes each element to the given block by using #succ" do
+ a = []
+ (-5..5).each { |i| a << i }
+ a.should == [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]
+
+ a = []
+ ('A'..'D').each { |i| a << i }
+ a.should == ['A','B','C','D']
+
+ a = []
+ ('A'...'D').each { |i| a << i }
+ a.should == ['A','B','C']
+
+ a = []
+ (0xfffd...0xffff).each { |i| a << i }
+ a.should == [0xfffd, 0xfffe]
+
+ y = mock('y')
+ x = mock('x')
+ x.should_receive(:<=>).with(y).any_number_of_times.and_return(-1)
+ x.should_receive(:<=>).with(x).any_number_of_times.and_return(0)
+ x.should_receive(:succ).any_number_of_times.and_return(y)
+ y.should_receive(:<=>).with(x).any_number_of_times.and_return(1)
+ y.should_receive(:<=>).with(y).any_number_of_times.and_return(0)
+
+ a = []
+ (x..y).each { |i| a << i }
+ a.should == [x, y]
+ end
+
+ it "raises a TypeError if the first element does not respond to #succ" do
+ lambda { (0.5..2.4).each { |i| i } }.should raise_error(TypeError)
+
+ b = mock('x')
+ (a = mock('1')).should_receive(:<=>).with(b).and_return(1)
+
+ lambda { (a..b).each { |i| i } }.should raise_error(TypeError)
+ end
+
+ it "returns self" do
+ range = 1..10
+ range.each{}.should equal(range)
+ end
+
+ it "returns an enumerator when no block given" do
+ enum = (1..3).each
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.should == [1, 2, 3]
+ end
+
+ it "raises a TypeError if the first element is a Time object" do
+ t = Time.now
+ lambda { (t..t+1).each { |i| i } }.should raise_error(TypeError)
+ end
+
+ it "passes each Symbol element by using #succ" do
+ (:aa..:ac).each.to_a.should == [:aa, :ab, :ac]
+ (:aa...:ac).each.to_a.should == [:aa, :ab]
+ end
+
+ it_behaves_like :enumeratorized_with_origin_size, :each, (1..3)
+end
diff --git a/spec/ruby/core/range/end_spec.rb b/spec/ruby/core/range/end_spec.rb
new file mode 100644
index 0000000000..9e5e6f7d43
--- /dev/null
+++ b/spec/ruby/core/range/end_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/end'
+
+describe "Range#end" do
+ it_behaves_like :range_end, :end
+end
diff --git a/spec/ruby/core/range/eql_spec.rb b/spec/ruby/core/range/eql_spec.rb
new file mode 100644
index 0000000000..fa6c71840e
--- /dev/null
+++ b/spec/ruby/core/range/eql_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'shared/equal_value'
+
+describe "Range#eql?" do
+ it_behaves_like :range_eql, :eql?
+
+ it "returns false if the endpoints are not eql?" do
+ (0..1).should_not eql(0..1.0)
+ end
+end
diff --git a/spec/ruby/core/range/equal_value_spec.rb b/spec/ruby/core/range/equal_value_spec.rb
new file mode 100644
index 0000000000..889557fc2a
--- /dev/null
+++ b/spec/ruby/core/range/equal_value_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'shared/equal_value'
+
+describe "Range#==" do
+ it_behaves_like :range_eql, :==
+
+ it "returns true if the endpoints are ==" do
+ (0..1).should == (0..1.0)
+ end
+end
diff --git a/spec/ruby/core/range/exclude_end_spec.rb b/spec/ruby/core/range/exclude_end_spec.rb
new file mode 100644
index 0000000000..a209603d18
--- /dev/null
+++ b/spec/ruby/core/range/exclude_end_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+describe "Range#exclude_end?" do
+ it "returns false if the range does not exclude the end value" do
+ (-2..2).exclude_end?.should == false
+ ('A'..'B').exclude_end?.should == false
+ (0.5..2.4).exclude_end?.should == false
+ (0xfffd..0xffff).exclude_end?.should == false
+ Range.new(0, 1).exclude_end?.should == false
+ end
+
+ it "returns true if the range excludes the end value" do
+ (0...5).exclude_end?.should == true
+ ('A'...'B').exclude_end?.should == true
+ (0.5...2.4).exclude_end?.should == true
+ (0xfffd...0xffff).exclude_end?.should == true
+ Range.new(0, 1, true).exclude_end?.should == true
+ end
+end
diff --git a/spec/ruby/core/range/first_spec.rb b/spec/ruby/core/range/first_spec.rb
new file mode 100644
index 0000000000..b7ae72ec6c
--- /dev/null
+++ b/spec/ruby/core/range/first_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+require_relative 'shared/begin'
+
+describe "Range#first" do
+ it_behaves_like :range_begin, :first
+
+ it "returns the specified number of elements from the beginning" do
+ (0..2).first(2).should == [0, 1]
+ end
+
+ it "returns an empty array for an empty Range" do
+ (0...0).first(2).should == []
+ end
+
+ it "returns an empty array when passed zero" do
+ (0..2).first(0).should == []
+ end
+
+ it "returns all elements in the range when count exceeds the number of elements" do
+ (0..2).first(4).should == [0, 1, 2]
+ end
+
+ it "raises an ArgumentError when count is negative" do
+ lambda { (0..2).first(-1) }.should raise_error(ArgumentError)
+ end
+
+ it "calls #to_int to convert the argument" do
+ obj = mock_int(2)
+ (3..7).first(obj).should == [3, 4]
+ end
+
+ it "raises a TypeError if #to_int does not return an Integer" do
+ obj = mock("to_int")
+ obj.should_receive(:to_int).and_return("1")
+ lambda { (2..3).first(obj) }.should raise_error(TypeError)
+ end
+
+ it "truncates the value when passed a Float" do
+ (2..9).first(2.8).should == [2, 3]
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { (2..3).first(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a String" do
+ lambda { (2..3).first("1") }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/range/fixtures/classes.rb b/spec/ruby/core/range/fixtures/classes.rb
new file mode 100644
index 0000000000..b62704ca39
--- /dev/null
+++ b/spec/ruby/core/range/fixtures/classes.rb
@@ -0,0 +1,68 @@
+module RangeSpecs
+ class TenfoldSucc
+ include Comparable
+
+ attr_reader :n
+
+ def initialize(n)
+ @n = n
+ end
+
+ def <=>(other)
+ @n <=> other.n
+ end
+
+ def succ
+ self.class.new(@n * 10)
+ end
+ end
+
+ # Custom Range classes Xs and Ys
+ class Custom
+ include Comparable
+ attr_reader :length
+
+ def initialize(n)
+ @length = n
+ end
+
+ def eql?(other)
+ inspect.eql? other.inspect
+ end
+ alias :== :eql?
+
+ def inspect
+ 'custom'
+ end
+
+ def <=>(other)
+ @length <=> other.length
+ end
+ end
+
+ class Xs < Custom # represent a string of 'x's
+ def succ
+ Xs.new(@length + 1)
+ end
+
+ def inspect
+ 'x' * @length
+ end
+ end
+
+ class Ys < Custom # represent a string of 'y's
+ def succ
+ Ys.new(@length + 1)
+ end
+
+ def inspect
+ 'y' * @length
+ end
+ end
+
+ class MyRange < Range
+ end
+
+ class ComparisonError < RuntimeError
+ end
+end
diff --git a/spec/ruby/core/range/hash_spec.rb b/spec/ruby/core/range/hash_spec.rb
new file mode 100644
index 0000000000..90aeee7890
--- /dev/null
+++ b/spec/ruby/core/range/hash_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+
+describe "Range#hash" do
+ it "is provided" do
+ (0..1).respond_to?(:hash).should == true
+ ('A'..'Z').respond_to?(:hash).should == true
+ (0xfffd..0xffff).respond_to?(:hash).should == true
+ (0.5..2.4).respond_to?(:hash).should == true
+ end
+
+ it "generates the same hash values for Ranges with the same start, end and exclude_end? values" do
+ (0..1).hash.should == (0..1).hash
+ (0...10).hash.should == (0...10).hash
+ (0..10).hash.should_not == (0...10).hash
+ end
+
+ it "generates a Fixnum for the hash value" do
+ (0..0).hash.should be_an_instance_of(Fixnum)
+ (0..1).hash.should be_an_instance_of(Fixnum)
+ (0...10).hash.should be_an_instance_of(Fixnum)
+ (0..10).hash.should be_an_instance_of(Fixnum)
+ end
+
+end
diff --git a/spec/ruby/core/range/include_spec.rb b/spec/ruby/core/range/include_spec.rb
new file mode 100644
index 0000000000..e1c5954917
--- /dev/null
+++ b/spec/ruby/core/range/include_spec.rb
@@ -0,0 +1,10 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../spec_helper'
+require_relative 'shared/cover_and_include'
+require_relative 'shared/include'
+require_relative 'shared/cover'
+
+describe "Range#include?" do
+ it_behaves_like :range_cover_and_include, :include?
+ it_behaves_like :range_include, :include?
+end
diff --git a/spec/ruby/core/range/initialize_spec.rb b/spec/ruby/core/range/initialize_spec.rb
new file mode 100644
index 0000000000..53f6485954
--- /dev/null
+++ b/spec/ruby/core/range/initialize_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+
+describe "Range#initialize" do
+ before do
+ @range = Range.allocate
+ end
+
+ it "is private" do
+ Range.should have_private_instance_method("initialize")
+ end
+
+ it "initializes correctly the Range object when given 2 arguments" do
+ lambda { @range.send(:initialize, 0, 1) }.should_not raise_error
+ end
+
+ it "initializes correctly the Range object when given 3 arguments" do
+ lambda { @range.send(:initialize, 0, 1, true) }.should_not raise_error
+ end
+
+ it "raises an ArgumentError if passed without or with only one argument" do
+ lambda { @range.send(:initialize) }.should raise_error(ArgumentError)
+ lambda { @range.send(:initialize, 1) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if passed with four or more arguments" do
+ lambda { @range.send(:initialize, 1, 3, 5, 7) }.should raise_error(ArgumentError)
+ lambda { @range.send(:initialize, 1, 3, 5, 7, 9) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a NameError if called on an already initialized Range" do
+ lambda { (0..1).send(:initialize, 1, 3) }.should raise_error(NameError)
+ lambda { (0..1).send(:initialize, 1, 3, true) }.should raise_error(NameError)
+ end
+
+ it "raises an ArgumentError if arguments don't respond to <=>" do
+ o1 = Object.new
+ o2 = Object.new
+
+ lambda { @range.send(:initialize, o1, o2) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/range/inspect_spec.rb b/spec/ruby/core/range/inspect_spec.rb
new file mode 100644
index 0000000000..3c130812d0
--- /dev/null
+++ b/spec/ruby/core/range/inspect_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+
+describe "Range#inspect" do
+ it "provides a printable form, using #inspect to convert the start and end objects" do
+ ('A'..'Z').inspect.should == '"A".."Z"'
+ ('A'...'Z').inspect.should == '"A"..."Z"'
+
+ (0..21).inspect.should == "0..21"
+ (-8..0).inspect.should == "-8..0"
+ (-411..959).inspect.should == "-411..959"
+ (0xfff..0xfffff).inspect.should == "4095..1048575"
+ (0.5..2.4).inspect.should == "0.5..2.4"
+ end
+
+ it "returns a tainted string if either end is tainted" do
+ (("a".taint)..."c").inspect.tainted?.should be_true
+ ("a"...("c".taint)).inspect.tainted?.should be_true
+ ("a"..."c").taint.inspect.tainted?.should be_true
+ end
+
+ it "returns a untrusted string if either end is untrusted" do
+ (("a".untrust)..."c").inspect.untrusted?.should be_true
+ ("a"...("c".untrust)).inspect.untrusted?.should be_true
+ ("a"..."c").untrust.inspect.untrusted?.should be_true
+ end
+end
diff --git a/spec/ruby/core/range/last_spec.rb b/spec/ruby/core/range/last_spec.rb
new file mode 100644
index 0000000000..8983f80cfb
--- /dev/null
+++ b/spec/ruby/core/range/last_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+require_relative 'shared/end'
+
+describe "Range#last" do
+ it_behaves_like :range_end, :last
+
+ it "returns the specified number of elements from the end" do
+ (1..5).last(3).should == [3, 4, 5]
+ end
+
+ it "returns an empty array for an empty Range" do
+ (0...0).last(2).should == []
+ end
+
+ it "returns an empty array when passed zero" do
+ (0..2).last(0).should == []
+ end
+
+ it "returns all elements in the range when count exceeds the number of elements" do
+ (2..4).last(5).should == [2, 3, 4]
+ end
+
+ it "raises an ArgumentError when count is negative" do
+ lambda { (0..2).last(-1) }.should raise_error(ArgumentError)
+ end
+
+ it "calls #to_int to convert the argument" do
+ obj = mock_int(2)
+ (3..7).last(obj).should == [6, 7]
+ end
+
+ it "raises a TypeError if #to_int does not return an Integer" do
+ obj = mock("to_int")
+ obj.should_receive(:to_int).and_return("1")
+ lambda { (2..3).last(obj) }.should raise_error(TypeError)
+ end
+
+ it "truncates the value when passed a Float" do
+ (2..9).last(2.8).should == [8, 9]
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { (2..3).last(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a String" do
+ lambda { (2..3).last("1") }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/range/max_spec.rb b/spec/ruby/core/range/max_spec.rb
new file mode 100644
index 0000000000..e6915cc450
--- /dev/null
+++ b/spec/ruby/core/range/max_spec.rb
@@ -0,0 +1,82 @@
+require_relative '../../spec_helper'
+
+describe "Range#max" do
+ it "returns the maximum value in the range when called with no arguments" do
+ (1..10).max.should == 10
+ (1...10).max.should == 9
+ (0...2**64).max.should == 18446744073709551615
+ ('f'..'l').max.should == 'l'
+ ('a'...'f').max.should == 'e'
+ end
+
+ it "returns the maximum value in the Float range when called with no arguments" do
+ (303.20..908.1111).max.should == 908.1111
+ end
+
+ it "raises TypeError when called on an exclusive range and a non Integer value" do
+ lambda { (303.20...908.1111).max }.should raise_error(TypeError)
+ end
+
+ it "returns nil when the endpoint is less than the start point" do
+ (100..10).max.should be_nil
+ ('z'..'l').max.should be_nil
+ end
+
+ it "returns nil when the endpoint equals the start point and the range is exclusive" do
+ (5...5).max.should be_nil
+ end
+
+ it "returns the endpoint when the endpoint equals the start point and the range is inclusive" do
+ (5..5).max.should equal(5)
+ end
+
+ it "returns nil when the endpoint is less than the start point in a Float range" do
+ (3003.20..908.1111).max.should be_nil
+ end
+
+ it "returns end point when the range is Time..Time(included end point)" do
+ time_start = Time.now
+ time_end = Time.now + 1.0
+ (time_start..time_end).max.should equal(time_end)
+ end
+
+ it "raises TypeError when called on a Time...Time(excluded end point)" do
+ time_start = Time.now
+ time_end = Time.now + 1.0
+ lambda { (time_start...time_end).max }.should raise_error(TypeError)
+ end
+end
+
+describe "Range#max given a block" do
+ it "passes each pair of values in the range to the block" do
+ acc = []
+ (1..10).max {|a,b| acc << [a,b]; a }
+ acc.flatten!
+ (1..10).each do |value|
+ acc.include?(value).should be_true
+ end
+ end
+
+ it "passes each pair of elements to the block in reversed order" do
+ acc = []
+ (1..5).max {|a,b| acc << [a,b]; a }
+ acc.should == [[2,1],[3,2], [4,3], [5, 4]]
+ end
+
+ it "calls #> and #< on the return value of the block" do
+ obj = mock('obj')
+ obj.should_receive(:>).exactly(2).times
+ obj.should_receive(:<).exactly(2).times
+ (1..3).max {|a,b| obj }
+ end
+
+ it "returns the element the block determines to be the maximum" do
+ (1..3).max {|a,b| -3 }.should == 1
+ end
+
+ it "returns nil when the endpoint is less than the start point" do
+ (100..10).max {|x,y| x <=> y}.should be_nil
+ ('z'..'l').max {|x,y| x <=> y}.should be_nil
+ (5...5).max {|x,y| x <=> y}.should be_nil
+ end
+end
diff --git a/spec/ruby/core/range/member_spec.rb b/spec/ruby/core/range/member_spec.rb
new file mode 100644
index 0000000000..ca9deabd35
--- /dev/null
+++ b/spec/ruby/core/range/member_spec.rb
@@ -0,0 +1,10 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../spec_helper'
+require_relative 'shared/cover_and_include'
+require_relative 'shared/include'
+require_relative 'shared/cover'
+
+describe "Range#member?" do
+ it_behaves_like :range_cover_and_include, :member?
+ it_behaves_like :range_include, :member?
+end
diff --git a/spec/ruby/core/range/min_spec.rb b/spec/ruby/core/range/min_spec.rb
new file mode 100644
index 0000000000..424bd1dc87
--- /dev/null
+++ b/spec/ruby/core/range/min_spec.rb
@@ -0,0 +1,75 @@
+require_relative '../../spec_helper'
+
+describe "Range#min" do
+ it "returns the minimum value in the range when called with no arguments" do
+ (1..10).min.should == 1
+ ('f'..'l').min.should == 'f'
+ end
+
+ it "returns the minimum value in the Float range when called with no arguments" do
+ (303.20..908.1111).min.should == 303.20
+ end
+
+ it "returns nil when the start point is greater than the endpoint" do
+ (100..10).min.should be_nil
+ ('z'..'l').min.should be_nil
+ end
+
+ it "returns nil when the endpoint equals the start point and the range is exclusive" do
+ (7...7).min.should be_nil
+ end
+
+ it "returns the start point when the endpoint equals the start point and the range is inclusive" do
+ (7..7).min.should equal(7)
+ end
+
+ it "returns nil when the start point is greater than the endpoint in a Float range" do
+ (3003.20..908.1111).min.should be_nil
+ end
+
+ it "returns start point when the range is Time..Time(included end point)" do
+ time_start = Time.now
+ time_end = Time.now + 1.0
+ (time_start..time_end).min.should equal(time_start)
+ end
+
+ it "returns start point when the range is Time...Time(excluded end point)" do
+ time_start = Time.now
+ time_end = Time.now + 1.0
+ (time_start...time_end).min.should equal(time_start)
+ end
+end
+
+describe "Range#min given a block" do
+ it "passes each pair of values in the range to the block" do
+ acc = []
+ (1..10).min {|a,b| acc << [a,b]; a }
+ acc.flatten!
+ (1..10).each do |value|
+ acc.include?(value).should be_true
+ end
+ end
+
+ it "passes each pair of elements to the block where the first argument is the current element, and the last is the first element" do
+ acc = []
+ (1..5).min {|a,b| acc << [a,b]; a }
+ acc.should == [[2, 1], [3, 1], [4, 1], [5, 1]]
+ end
+
+ it "calls #> and #< on the return value of the block" do
+ obj = mock('obj')
+ obj.should_receive(:>).exactly(2).times
+ obj.should_receive(:<).exactly(2).times
+ (1..3).min {|a,b| obj }
+ end
+
+ it "returns the element the block determines to be the minimum" do
+ (1..3).min {|a,b| -3 }.should == 3
+ end
+
+ it "returns nil when the start point is greater than the endpoint" do
+ (100..10).min {|x,y| x <=> y}.should be_nil
+ ('z'..'l').min {|x,y| x <=> y}.should be_nil
+ (7...7).min {|x,y| x <=> y}.should be_nil
+ end
+end
diff --git a/spec/ruby/core/range/new_spec.rb b/spec/ruby/core/range/new_spec.rb
new file mode 100644
index 0000000000..26ea12867d
--- /dev/null
+++ b/spec/ruby/core/range/new_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Range.new" do
+ it "constructs a range using the given start and end" do
+ range = Range.new('a', 'c')
+ range.should == ('a'..'c')
+
+ range.first.should == 'a'
+ range.last.should == 'c'
+ end
+
+ it "includes the end object when the third parameter is omitted or false" do
+ Range.new('a', 'c').to_a.should == ['a', 'b', 'c']
+ Range.new(1, 3).to_a.should == [1, 2, 3]
+
+ Range.new('a', 'c', false).to_a.should == ['a', 'b', 'c']
+ Range.new(1, 3, false).to_a.should == [1, 2, 3]
+
+ Range.new('a', 'c', true).to_a.should == ['a', 'b']
+ Range.new(1, 3, 1).to_a.should == [1, 2]
+
+ Range.new(1, 3, mock('[1,2]')).to_a.should == [1, 2]
+ Range.new(1, 3, :test).to_a.should == [1, 2]
+ end
+
+ it "raises an ArgumentError when the given start and end can't be compared by using #<=>" do
+ lambda { Range.new(1, mock('x')) }.should raise_error(ArgumentError)
+ lambda { Range.new(mock('x'), mock('y')) }.should raise_error(ArgumentError)
+
+ b = mock('x')
+ (a = mock('nil')).should_receive(:<=>).with(b).and_return(nil)
+ lambda { Range.new(a, b) }.should raise_error(ArgumentError)
+ end
+
+ ruby_version_is "2.5" do
+ it "does not rescue exception raised in #<=> when compares the given start and end" do
+ b = mock('a')
+ a = mock('b')
+ a.should_receive(:<=>).with(b).and_raise(RangeSpecs::ComparisonError)
+
+ -> { Range.new(a, b) }.should raise_error(RangeSpecs::ComparisonError)
+ end
+ end
+end
diff --git a/spec/ruby/core/range/range_spec.rb b/spec/ruby/core/range/range_spec.rb
new file mode 100644
index 0000000000..8e9433f8c1
--- /dev/null
+++ b/spec/ruby/core/range/range_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Range" do
+ it "includes Enumerable" do
+ Range.include?(Enumerable).should == true
+ end
+end
diff --git a/spec/ruby/core/range/shared/begin.rb b/spec/ruby/core/range/shared/begin.rb
new file mode 100644
index 0000000000..f660e3faf9
--- /dev/null
+++ b/spec/ruby/core/range/shared/begin.rb
@@ -0,0 +1,10 @@
+describe :range_begin, shared: true do
+ it "returns the first element of self" do
+ (-1..1).send(@method).should == -1
+ (0..1).send(@method).should == 0
+ (0xffff...0xfffff).send(@method).should == 65535
+ ('Q'..'T').send(@method).should == 'Q'
+ ('Q'...'T').send(@method).should == 'Q'
+ (0.5..2.4).send(@method).should == 0.5
+ end
+end
diff --git a/spec/ruby/core/range/shared/cover.rb b/spec/ruby/core/range/shared/cover.rb
new file mode 100644
index 0000000000..b2de86531d
--- /dev/null
+++ b/spec/ruby/core/range/shared/cover.rb
@@ -0,0 +1,93 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :range_cover, shared: true do
+ it "uses the range element's <=> to make the comparison" do
+ a = mock('a')
+ a.should_receive(:<=>).twice.and_return(-1,-1)
+ (a..'z').send(@method, 'b').should be_true
+ end
+
+ it "uses a continuous inclusion test" do
+ ('a'..'f').send(@method, 'aa').should be_true
+ ('a'..'f').send(@method, 'babe').should be_true
+ ('a'..'f').send(@method, 'baby').should be_true
+ ('a'..'f').send(@method, 'ga').should be_false
+ (-10..-2).send(@method, -2.5).should be_true
+ end
+
+ describe "on string elements" do
+ it "returns true if other is matched by element.succ" do
+ ('a'..'c').send(@method, 'b').should be_true
+ ('a'...'c').send(@method, 'b').should be_true
+ end
+
+ it "returns true if other is not matched by element.succ" do
+ ('a'..'c').send(@method, 'bc').should be_true
+ ('a'...'c').send(@method, 'bc').should be_true
+ end
+ end
+
+ describe "with weird succ" do
+ describe "when included end value" do
+ before :each do
+ @range = RangeSpecs::TenfoldSucc.new(1)..RangeSpecs::TenfoldSucc.new(99)
+ end
+
+ it "returns false if other is less than first element" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should be_false
+ end
+
+ it "returns true if other is equal as first element" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should be_true
+ end
+
+ it "returns true if other is matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should be_true
+ end
+
+ it "returns true if other is not matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should be_true
+ end
+
+ it "returns true if other is equal as last element but not matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should be_true
+ end
+
+ it "returns false if other is greater than last element but matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should be_false
+ end
+ end
+
+ describe "when excluded end value" do
+ before :each do
+ @range = RangeSpecs::TenfoldSucc.new(1)...RangeSpecs::TenfoldSucc.new(99)
+ end
+
+ it "returns false if other is less than first element" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should be_false
+ end
+
+ it "returns true if other is equal as first element" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should be_true
+ end
+
+ it "returns true if other is matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should be_true
+ end
+
+ it "returns true if other is not matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should be_true
+ end
+
+ it "returns false if other is equal as last element but not matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should be_false
+ end
+
+ it "returns false if other is greater than last element but matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should be_false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/range/shared/cover_and_include.rb b/spec/ruby/core/range/shared/cover_and_include.rb
new file mode 100644
index 0000000000..d3c0700f1a
--- /dev/null
+++ b/spec/ruby/core/range/shared/cover_and_include.rb
@@ -0,0 +1,66 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+
+describe :range_cover_and_include, shared: true do
+ it "returns true if other is an element of self" do
+ (0..5).send(@method, 2).should == true
+ (-5..5).send(@method, 0).should == true
+ (-1...1).send(@method, 10.5).should == false
+ (-10..-2).send(@method, -2.5).should == true
+ ('C'..'X').send(@method, 'M').should == true
+ ('C'..'X').send(@method, 'A').should == false
+ ('B'...'W').send(@method, 'W').should == false
+ ('B'...'W').send(@method, 'Q').should == true
+ (0xffff..0xfffff).send(@method, 0xffffd).should == true
+ (0xffff..0xfffff).send(@method, 0xfffd).should == false
+ (0.5..2.4).send(@method, 2).should == true
+ (0.5..2.4).send(@method, 2.5).should == false
+ (0.5..2.4).send(@method, 2.4).should == true
+ (0.5...2.4).send(@method, 2.4).should == false
+ end
+
+ it "compares values using <=>" do
+ rng = (1..5)
+ m = mock("int")
+ m.should_receive(:coerce).and_return([1, 2])
+ m.should_receive(:<=>).and_return(1)
+
+ rng.send(@method, m).should be_false
+ end
+
+ it "raises an ArgumentError without exactly one argument" do
+ lambda{ (1..2).send(@method) }.should raise_error(ArgumentError)
+ lambda{ (1..2).send(@method, 1, 2) }.should raise_error(ArgumentError)
+ end
+
+ it "returns true if argument is equal to the first value of the range" do
+ (0..5).send(@method, 0).should be_true
+ ('f'..'s').send(@method, 'f').should be_true
+ end
+
+ it "returns true if argument is equal to the last value of the range" do
+ (0..5).send(@method, 5).should be_true
+ (0...5).send(@method, 4).should be_true
+ ('f'..'s').send(@method, 's').should be_true
+ end
+
+ it "returns true if argument is less than the last value of the range and greater than the first value" do
+ (20..30).send(@method, 28).should be_true
+ ('e'..'h').send(@method, 'g').should be_true
+ ("\u{999}".."\u{9999}").send @method, "\u{9995}"
+ end
+
+ it "returns true if argument is sole element in the range" do
+ (30..30).send(@method, 30).should be_true
+ end
+
+ it "returns false if range is empty" do
+ (30...30).send(@method, 30).should be_false
+ (30...30).send(@method, nil).should be_false
+ end
+
+ it "returns false if the range does not contain the argument" do
+ ('A'..'C').send(@method, 20.9).should be_false
+ ('A'...'C').send(@method, 'C').should be_false
+ end
+end
diff --git a/spec/ruby/core/range/shared/end.rb b/spec/ruby/core/range/shared/end.rb
new file mode 100644
index 0000000000..b26394fe31
--- /dev/null
+++ b/spec/ruby/core/range/shared/end.rb
@@ -0,0 +1,10 @@
+describe :range_end, shared: true do
+ it "end returns the last element of self" do
+ (-1..1).send(@method).should == 1
+ (0..1).send(@method).should == 1
+ ("A".."Q").send(@method).should == "Q"
+ ("A"..."Q").send(@method).should == "Q"
+ (0xffff...0xfffff).send(@method).should == 1048575
+ (0.5..2.4).send(@method).should == 2.4
+ end
+end
diff --git a/spec/ruby/core/range/shared/equal_value.rb b/spec/ruby/core/range/shared/equal_value.rb
new file mode 100644
index 0000000000..9d8bb13351
--- /dev/null
+++ b/spec/ruby/core/range/shared/equal_value.rb
@@ -0,0 +1,45 @@
+require_relative '../fixtures/classes'
+
+describe :range_eql, shared: true do
+ it "returns true if other has same begin, end, and exclude_end? values" do
+ (0..2).send(@method, 0..2).should == true
+ ('G'..'M').send(@method,'G'..'M').should == true
+ (0.5..2.4).send(@method, 0.5..2.4).should == true
+ (5..10).send(@method, Range.new(5,10)).should == true
+ ('D'..'V').send(@method, Range.new('D','V')).should == true
+ (0.5..2.4).send(@method, Range.new(0.5, 2.4)).should == true
+ (0xffff..0xfffff).send(@method, 0xffff..0xfffff).should == true
+ (0xffff..0xfffff).send(@method, Range.new(0xffff,0xfffff)).should == true
+
+ a = RangeSpecs::Xs.new(3)..RangeSpecs::Xs.new(5)
+ b = Range.new(RangeSpecs::Xs.new(3), RangeSpecs::Xs.new(5))
+ a.send(@method, b).should == true
+ end
+
+ it "returns false if one of the attributes differs" do
+ ('Q'..'X').send(@method, 'A'..'C').should == false
+ ('Q'...'X').send(@method, 'Q'..'W').should == false
+ ('Q'..'X').send(@method, 'Q'...'X').should == false
+ (0.5..2.4).send(@method, 0.5...2.4).should == false
+ (1482..1911).send(@method, 1482...1911).should == false
+ (0xffff..0xfffff).send(@method, 0xffff...0xfffff).should == false
+
+ a = RangeSpecs::Xs.new(3)..RangeSpecs::Xs.new(5)
+ b = Range.new(RangeSpecs::Ys.new(3), RangeSpecs::Ys.new(5))
+ a.send(@method, b).should == false
+ end
+
+ it "returns false if other is not a Range" do
+ (1..10).send(@method, 1).should == false
+ (1..10).send(@method, 'a').should == false
+ (1..10).send(@method, mock('x')).should == false
+ end
+
+ it "returns true for subclasses of Range" do
+ Range.new(1, 2).send(@method, RangeSpecs::MyRange.new(1, 2)).should == true
+
+ a = Range.new(RangeSpecs::Xs.new(3), RangeSpecs::Xs.new(5))
+ b = RangeSpecs::MyRange.new(RangeSpecs::Xs.new(3), RangeSpecs::Xs.new(5))
+ a.send(@method, b).should == true
+ end
+end
diff --git a/spec/ruby/core/range/shared/include.rb b/spec/ruby/core/range/shared/include.rb
new file mode 100644
index 0000000000..b0fc5c2915
--- /dev/null
+++ b/spec/ruby/core/range/shared/include.rb
@@ -0,0 +1,91 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :range_include, shared: true do
+ describe "on string elements" do
+ it "returns true if other is matched by element.succ" do
+ ('a'..'c').send(@method, 'b').should be_true
+ ('a'...'c').send(@method, 'b').should be_true
+ end
+
+ it "returns false if other is not matched by element.succ" do
+ ('a'..'c').send(@method, 'bc').should be_false
+ ('a'...'c').send(@method, 'bc').should be_false
+ end
+ end
+
+ describe "with weird succ" do
+ describe "when included end value" do
+ before :each do
+ @range = RangeSpecs::TenfoldSucc.new(1)..RangeSpecs::TenfoldSucc.new(99)
+ end
+
+ it "returns false if other is less than first element" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should be_false
+ end
+
+ it "returns true if other is equal as first element" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should be_true
+ end
+
+ it "returns true if other is matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should be_true
+ end
+
+ it "returns false if other is not matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should be_false
+ end
+
+ it "returns false if other is equal as last element but not matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should be_false
+ end
+
+ it "returns false if other is greater than last element but matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should be_false
+ end
+ end
+
+ describe "when excluded end value" do
+ before :each do
+ @range = RangeSpecs::TenfoldSucc.new(1)...RangeSpecs::TenfoldSucc.new(99)
+ end
+
+ it "returns false if other is less than first element" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should be_false
+ end
+
+ it "returns true if other is equal as first element" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should be_true
+ end
+
+ it "returns true if other is matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should be_true
+ end
+
+ it "returns false if other is not matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should be_false
+ end
+
+ it "returns false if other is equal as last element but not matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should be_false
+ end
+
+ it "returns false if other is greater than last element but matched by element.succ" do
+ @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should be_false
+ end
+ end
+ end
+
+ describe "with Time endpoints" do
+ it "uses cover? logic" do
+ now = Time.now
+ range = (now..(now + 60))
+
+ range.include?(now).should == true
+ range.include?(now - 1).should == false
+ range.include?(now + 60).should == true
+ range.include?(now + 61).should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/range/size_spec.rb b/spec/ruby/core/range/size_spec.rb
new file mode 100644
index 0000000000..09759940dd
--- /dev/null
+++ b/spec/ruby/core/range/size_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+
+describe "Range#size" do
+ it "returns the number of elements in the range" do
+ (1..16).size.should == 16
+ (1...16).size.should == 15
+
+ (1.0..16.0).size.should == 16
+ (1.0...16.0).size.should == 15
+ (1.0..15.9).size.should == 15
+ (1.1..16.0).size.should == 15
+ (1.1..15.9).size.should == 15
+ end
+
+ it "returns 0 if last is less than first" do
+ (16..0).size.should == 0
+ (16.0..0.0).size.should == 0
+ (Float::INFINITY..0).size.should == 0
+ end
+
+ it 'returns Float::INFINITY for increasing, infinite ranges' do
+ (0..Float::INFINITY).size.should == Float::INFINITY
+ (-Float::INFINITY..0).size.should == Float::INFINITY
+ (-Float::INFINITY..Float::INFINITY).size.should == Float::INFINITY
+ end
+
+ it "returns nil if first and last are not Numeric" do
+ (:a..:z).size.should be_nil
+ ('a'..'z').size.should be_nil
+ end
+end
diff --git a/spec/ruby/core/range/step_spec.rb b/spec/ruby/core/range/step_spec.rb
new file mode 100644
index 0000000000..cdf66e4565
--- /dev/null
+++ b/spec/ruby/core/range/step_spec.rb
@@ -0,0 +1,368 @@
+require_relative '../../spec_helper'
+
+describe "Range#step" do
+ step_enum_class = Enumerator
+ ruby_version_is "2.6" do
+ step_enum_class = Enumerator::ArithmeticSequence
+ end
+
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "returns an #{step_enum_class} when no block is given" do
+ enum = (1..10).step(4)
+ enum.should be_an_instance_of(step_enum_class)
+ enum.to_a.should eql([1, 5, 9])
+ end
+
+ it "returns self" do
+ r = 1..2
+ r.step { }.should equal(r)
+ end
+
+ it "raises TypeError if step" do
+ obj = mock("mock")
+ lambda { (1..10).step(obj) { } }.should raise_error(TypeError)
+ end
+
+ it "calls #to_int to coerce step to an Integer" do
+ obj = mock("Range#step")
+ obj.should_receive(:to_int).and_return(1)
+
+ (1..2).step(obj) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([1, 2])
+ end
+
+ it "raises a TypeError if step does not respond to #to_int" do
+ obj = mock("Range#step non-integer")
+
+ lambda { (1..2).step(obj) { } }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if #to_int does not return an Integer" do
+ obj = mock("Range#step non-integer")
+ obj.should_receive(:to_int).and_return("1")
+
+ lambda { (1..2).step(obj) { } }.should raise_error(TypeError)
+ end
+
+ it "coerces the argument to integer by invoking to_int" do
+ (obj = mock("2")).should_receive(:to_int).and_return(2)
+ res = []
+ (1..10).step(obj) {|x| res << x}
+ res.should == [1, 3, 5, 7, 9]
+ end
+
+ it "raises a TypeError if the first element does not respond to #succ" do
+ obj = mock("Range#step non-comparable")
+ obj.should_receive(:<=>).with(obj).and_return(1)
+
+ lambda { (obj..obj).step { |x| x } }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError if step is 0" do
+ lambda { (-1..1).step(0) { |x| x } }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if step is 0.0" do
+ lambda { (-1..1).step(0.0) { |x| x } }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if step is negative" do
+ lambda { (-1..1).step(-2) { |x| x } }.should raise_error(ArgumentError)
+ end
+
+ describe "with inclusive end" do
+ describe "and Integer values" do
+ it "yields Integer values incremented by 1 and less than or equal to end when not passed a step" do
+ (-2..2).step { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-2, -1, 0, 1, 2])
+ end
+
+ it "yields Integer values incremented by an Integer step" do
+ (-5..5).step(2) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-5, -3, -1, 1, 3, 5])
+ end
+
+ it "yields Float values incremented by a Float step" do
+ (-2..2).step(1.5) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-2.0, -0.5, 1.0])
+ end
+ end
+
+ describe "and Float values" do
+ it "yields Float values incremented by 1 and less than or equal to end when not passed a step" do
+ (-2.0..2.0).step { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0, 2.0])
+ end
+
+ it "yields Float values incremented by an Integer step" do
+ (-5.0..5.0).step(2) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0, 5.0])
+ end
+
+ it "yields Float values incremented by a Float step" do
+ (-1.0..1.0).step(0.5) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5, 1.0])
+ end
+
+ it "returns Float values of 'step * n + begin <= end'" do
+ (1.0..6.4).step(1.8) { |x| ScratchPad << x }
+ (1.0..12.7).step(1.3) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([1.0, 2.8, 4.6, 6.4, 1.0, 2.3, 3.6,
+ 4.9, 6.2, 7.5, 8.8, 10.1, 11.4, 12.7])
+ end
+ end
+
+ describe "and Integer, Float values" do
+ it "yields Float values incremented by 1 and less than or equal to end when not passed a step" do
+ (-2..2.0).step { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0, 2.0])
+ end
+
+ it "yields Float values incremented by an Integer step" do
+ (-5..5.0).step(2) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0, 5.0])
+ end
+
+ it "yields Float values incremented by a Float step" do
+ (-1..1.0).step(0.5) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5, 1.0])
+ end
+ end
+
+ describe "and Float, Integer values" do
+ it "yields Float values incremented by 1 and less than or equal to end when not passed a step" do
+ (-2.0..2).step { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0, 2.0])
+ end
+
+ it "yields Float values incremented by an Integer step" do
+ (-5.0..5).step(2) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0, 5.0])
+ end
+
+ it "yields Float values incremented by a Float step" do
+ (-1.0..1).step(0.5) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5, 1.0])
+ end
+ end
+
+ describe "and String values" do
+ it "yields String values incremented by #succ and less than or equal to end when not passed a step" do
+ ("A".."E").step { |x| ScratchPad << x }
+ ScratchPad.recorded.should == ["A", "B", "C", "D", "E"]
+ end
+
+ it "yields String values incremented by #succ called Integer step times" do
+ ("A".."G").step(2) { |x| ScratchPad << x }
+ ScratchPad.recorded.should == ["A", "C", "E", "G"]
+ end
+
+ it "raises a TypeError when passed a Float step" do
+ lambda { ("A".."G").step(2.0) { } }.should raise_error(TypeError)
+ end
+
+ it "calls #succ on begin and each element returned by #succ" do
+ obj = mock("Range#step String start")
+ obj.should_receive(:<=>).exactly(3).times.and_return(-1, -1, -1, 0)
+ obj.should_receive(:succ).exactly(2).times.and_return(obj)
+
+ (obj..obj).step { |x| ScratchPad << x }
+ ScratchPad.recorded.should == [obj, obj, obj]
+ end
+ end
+ end
+
+ describe "with exclusive end" do
+ describe "and Integer values" do
+ it "yields Integer values incremented by 1 and less than end when not passed a step" do
+ (-2...2).step { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-2, -1, 0, 1])
+ end
+
+ it "yields Integer values incremented by an Integer step" do
+ (-5...5).step(2) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-5, -3, -1, 1, 3])
+ end
+
+ it "yields Float values incremented by a Float step" do
+ (-2...2).step(1.5) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-2.0, -0.5, 1.0])
+ end
+ end
+
+ describe "and Float values" do
+ it "yields Float values incremented by 1 and less than end when not passed a step" do
+ (-2.0...2.0).step { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0])
+ end
+
+ it "yields Float values incremented by an Integer step" do
+ (-5.0...5.0).step(2) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0])
+ end
+
+ it "yields Float values incremented by a Float step" do
+ (-1.0...1.0).step(0.5) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5])
+ end
+
+ it "returns Float values of 'step * n + begin < end'" do
+ (1.0...6.4).step(1.8) { |x| ScratchPad << x }
+ (1.0...55.6).step(18.2) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([1.0, 2.8, 4.6, 1.0, 19.2, 37.4])
+ end
+ end
+
+ describe "and Integer, Float values" do
+ it "yields Float values incremented by 1 and less than end when not passed a step" do
+ (-2...2.0).step { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0])
+ end
+
+ it "yields Float values incremented by an Integer step" do
+ (-5...5.0).step(2) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0])
+ end
+
+ it "yields an Float and then Float values incremented by a Float step" do
+ (-1...1.0).step(0.5) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5])
+ end
+ end
+
+ describe "and Float, Integer values" do
+ it "yields Float values incremented by 1 and less than end when not passed a step" do
+ (-2.0...2).step { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0])
+ end
+
+ it "yields Float values incremented by an Integer step" do
+ (-5.0...5).step(2) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0])
+ end
+
+ it "yields Float values incremented by a Float step" do
+ (-1.0...1).step(0.5) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5])
+ end
+ end
+
+ describe "and String values" do
+ it "yields String values incremented by #succ and less than or equal to end when not passed a step" do
+ ("A"..."E").step { |x| ScratchPad << x }
+ ScratchPad.recorded.should == ["A", "B", "C", "D"]
+ end
+
+ it "yields String values incremented by #succ called Integer step times" do
+ ("A"..."G").step(2) { |x| ScratchPad << x }
+ ScratchPad.recorded.should == ["A", "C", "E"]
+ end
+
+ it "raises a TypeError when passed a Float step" do
+ lambda { ("A"..."G").step(2.0) { } }.should raise_error(TypeError)
+ end
+ end
+ end
+
+ describe "when no block is given" do
+ describe "returned #{step_enum_class}" do
+ describe "size" do
+ it "raises a TypeError if step does not respond to #to_int" do
+ obj = mock("Range#step non-integer")
+ enum = (1..2).step(obj)
+ lambda { enum.size }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if #to_int does not return an Integer" do
+ obj = mock("Range#step non-integer")
+ obj.should_receive(:to_int).and_return("1")
+ enum = (1..2).step(obj)
+
+ lambda { enum.size }.should raise_error(TypeError)
+ end
+
+ ruby_version_is ""..."2.6" do
+ it "raises an ArgumentError if step is 0" do
+ enum = (-1..1).step(0)
+ lambda { enum.size }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if step is 0.0" do
+ enum = (-1..1).step(0.0)
+ lambda { enum.size }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if step is negative" do
+ enum = (-1..1).step(-2)
+ lambda { enum.size }.should raise_error(ArgumentError)
+ end
+ end
+
+ ruby_version_is "2.6" do
+ it "returns Float::INFINITY for zero step" do
+ (-1..1).step(0).size.should == Float::INFINITY
+ (-1..1).step(0.0).size.should == Float::INFINITY
+ end
+ end
+
+ it "returns the ceil of range size divided by the number of steps" do
+ (1..10).step(4).size.should == 3
+ (1..10).step(3).size.should == 4
+ (1..10).step(2).size.should == 5
+ (1..10).step(1).size.should == 10
+ (-5..5).step(2).size.should == 6
+ (1...10).step(4).size.should == 3
+ (1...10).step(3).size.should == 3
+ (1...10).step(2).size.should == 5
+ (1...10).step(1).size.should == 9
+ (-5...5).step(2).size.should == 5
+ end
+
+ ruby_version_is "2.6" do
+ it "returns the ceil of range size divided by the number of steps even if step is negative" do
+ (-1..1).step(-1).size.should == 0
+ (1..-1).step(-1).size.should == 3
+ end
+ end
+
+ it "returns the correct number of steps when one of the arguments is a float" do
+ (-1..1.0).step(0.5).size.should == 5
+ (-1.0...1.0).step(0.5).size.should == 4
+ end
+
+ it "returns the range size when there's no step_size" do
+ (-2..2).step.size.should == 5
+ (-2.0..2.0).step.size.should == 5
+ (-2..2.0).step.size.should == 5
+ (-2.0..2).step.size.should == 5
+ (1.0..6.4).step(1.8).size.should == 4
+ (1.0..12.7).step(1.3).size.should == 10
+ (-2...2).step.size.should == 4
+ (-2.0...2.0).step.size.should == 4
+ (-2...2.0).step.size.should == 4
+ (-2.0...2).step.size.should == 4
+ (1.0...6.4).step(1.8).size.should == 3
+ (1.0...55.6).step(18.2).size.should == 3
+ end
+
+ it "returns nil with begin and end are String" do
+ ("A".."E").step(2).size.should == nil
+ ("A"..."E").step(2).size.should == nil
+ ("A".."E").step.size.should == nil
+ ("A"..."E").step.size.should == nil
+ end
+
+ it "return nil and not raises a TypeError if the first element does not respond to #succ" do
+ obj = mock("Range#step non-comparable")
+ obj.should_receive(:<=>).with(obj).and_return(1)
+ enum = (obj..obj).step
+ lambda { enum.size }.should_not raise_error
+ enum.size.should == nil
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/range/to_a_spec.rb b/spec/ruby/core/range/to_a_spec.rb
new file mode 100644
index 0000000000..ad2fbf5223
--- /dev/null
+++ b/spec/ruby/core/range/to_a_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+
+describe "Range#to_a" do
+ it "converts self to an array" do
+ (-5..5).to_a.should == [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]
+ ('A'..'D').to_a.should == ['A','B','C','D']
+ ('A'...'D').to_a.should == ['A','B','C']
+ (0xfffd...0xffff).to_a.should == [0xfffd,0xfffe]
+ lambda { (0.5..2.4).to_a }.should raise_error(TypeError)
+ end
+
+ it "returns empty array for descending-ordered" do
+ (5..-5).to_a.should == []
+ ('D'..'A').to_a.should == []
+ ('D'...'A').to_a.should == []
+ (0xffff...0xfffd).to_a.should == []
+ end
+
+ it "works with Ranges of Symbols" do
+ (:A..:z).to_a.size.should == 58
+ end
+end
diff --git a/spec/ruby/core/range/to_s_spec.rb b/spec/ruby/core/range/to_s_spec.rb
new file mode 100644
index 0000000000..4c37e81fe0
--- /dev/null
+++ b/spec/ruby/core/range/to_s_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+
+describe "Range#to_s" do
+ it "provides a printable form of self" do
+ (0..21).to_s.should == "0..21"
+ (-8..0).to_s.should == "-8..0"
+ (-411..959).to_s.should == "-411..959"
+ ('A'..'Z').to_s.should == 'A..Z'
+ ('A'...'Z').to_s.should == 'A...Z'
+ (0xfff..0xfffff).to_s.should == "4095..1048575"
+ (0.5..2.4).to_s.should == "0.5..2.4"
+ end
+
+ it "returns a tainted string if either end is tainted" do
+ (("a".taint)..."c").to_s.tainted?.should be_true
+ ("a"...("c".taint)).to_s.tainted?.should be_true
+ ("a"..."c").taint.to_s.tainted?.should be_true
+ end
+
+ it "returns a untrusted string if either end is untrusted" do
+ (("a".untrust)..."c").to_s.untrusted?.should be_true
+ ("a"...("c".untrust)).to_s.untrusted?.should be_true
+ ("a"..."c").untrust.to_s.untrusted?.should be_true
+ end
+end
diff --git a/spec/ruby/core/rational/abs_spec.rb b/spec/ruby/core/rational/abs_spec.rb
new file mode 100644
index 0000000000..aed7713058
--- /dev/null
+++ b/spec/ruby/core/rational/abs_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/abs'
+
+describe "Rational#abs" do
+ it_behaves_like :rational_abs, :abs
+end
diff --git a/spec/ruby/core/rational/ceil_spec.rb b/spec/ruby/core/rational/ceil_spec.rb
new file mode 100644
index 0000000000..5b0ca4a9d6
--- /dev/null
+++ b/spec/ruby/core/rational/ceil_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/ceil'
+
+describe "Rational#ceil" do
+ it_behaves_like :rational_ceil, :ceil
+end
diff --git a/spec/ruby/core/rational/coerce_spec.rb b/spec/ruby/core/rational/coerce_spec.rb
new file mode 100644
index 0000000000..3f78f0bcd6
--- /dev/null
+++ b/spec/ruby/core/rational/coerce_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/coerce'
+
+describe "Rational#coerce" do
+ it_behaves_like :rational_coerce, :coerce
+end
diff --git a/spec/ruby/core/rational/comparison_spec.rb b/spec/ruby/core/rational/comparison_spec.rb
new file mode 100644
index 0000000000..b2784f3e7d
--- /dev/null
+++ b/spec/ruby/core/rational/comparison_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../shared/rational/comparison'
+
+describe "Rational#<=> when passed a Rational object" do
+ it_behaves_like :rational_cmp_rat, :<=>
+end
+
+describe "Rational#<=> when passed a Integer object" do
+ it_behaves_like :rational_cmp_int, :<=>
+end
+
+describe "Rational#<=> when passed a Float object" do
+ it_behaves_like :rational_cmp_float, :<=>
+end
+
+describe "Rational#<=> when passed an Object that responds to #coerce" do
+ it_behaves_like :rational_cmp_coerce, :<=>
+ it_behaves_like :rational_cmp_coerce_exception, :<=>
+end
+
+describe "Rational#<=> when passed a non-Numeric Object that doesn't respond to #coerce" do
+ it_behaves_like :rational_cmp_other, :<=>
+end
diff --git a/spec/ruby/core/rational/denominator_spec.rb b/spec/ruby/core/rational/denominator_spec.rb
new file mode 100644
index 0000000000..6214b40587
--- /dev/null
+++ b/spec/ruby/core/rational/denominator_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/denominator'
+
+describe "Rational#denominator" do
+ it_behaves_like :rational_denominator, :denominator
+end
diff --git a/spec/ruby/core/rational/div_spec.rb b/spec/ruby/core/rational/div_spec.rb
new file mode 100644
index 0000000000..1cd8606b90
--- /dev/null
+++ b/spec/ruby/core/rational/div_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../shared/rational/div'
+
+describe "Rational#div" do
+ it_behaves_like :rational_div, :div
+end
+
+describe "Rational#div passed a Rational" do
+ it_behaves_like :rational_div_rat, :div
+end
+
+describe "Rational#div passed an Integer" do
+ it_behaves_like :rational_div_int, :div
+end
+
+describe "Rational#div passed a Float" do
+ it_behaves_like :rational_div_float, :div
+end
diff --git a/spec/ruby/core/rational/divide_spec.rb b/spec/ruby/core/rational/divide_spec.rb
new file mode 100644
index 0000000000..d8e3a44dc2
--- /dev/null
+++ b/spec/ruby/core/rational/divide_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../shared/rational/divide'
+require_relative '../../shared/rational/arithmetic_exception_in_coerce'
+
+describe "Rational#/" do
+ it_behaves_like :rational_divide, :/
+ it_behaves_like :rational_arithmetic_exception_in_coerce, :/
+end
+
+describe "Rational#/ when passed an Integer" do
+ it_behaves_like :rational_divide_int, :/
+end
+
+describe "Rational#/ when passed a Rational" do
+ it_behaves_like :rational_divide_rat, :/
+end
+
+describe "Rational#/ when passed a Float" do
+ it_behaves_like :rational_divide_float, :/
+end
diff --git a/spec/ruby/core/rational/divmod_spec.rb b/spec/ruby/core/rational/divmod_spec.rb
new file mode 100644
index 0000000000..6be1f8bd73
--- /dev/null
+++ b/spec/ruby/core/rational/divmod_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../shared/rational/divmod'
+
+describe "Rational#divmod when passed a Rational" do
+ it_behaves_like :rational_divmod_rat, :divmod
+end
+
+describe "Rational#divmod when passed an Integer" do
+ it_behaves_like :rational_divmod_int, :divmod
+end
+
+describe "Rational#divmod when passed a Float" do
+ it_behaves_like :rational_divmod_float, :divmod
+end
diff --git a/spec/ruby/core/rational/equal_value_spec.rb b/spec/ruby/core/rational/equal_value_spec.rb
new file mode 100644
index 0000000000..8e7acb1354
--- /dev/null
+++ b/spec/ruby/core/rational/equal_value_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../shared/rational/equal_value'
+
+describe "Rational#==" do
+ it_behaves_like :rational_equal_value, :==
+end
+
+describe "Rational#== when passed a Rational" do
+ it_behaves_like :rational_equal_value_rat, :==
+end
+
+describe "Rational#== when passed a Float" do
+ it_behaves_like :rational_equal_value_float, :==
+end
+
+describe "Rational#== when passed an Integer" do
+ it_behaves_like :rational_equal_value_int, :==
+end
diff --git a/spec/ruby/core/rational/exponent_spec.rb b/spec/ruby/core/rational/exponent_spec.rb
new file mode 100644
index 0000000000..622cf22782
--- /dev/null
+++ b/spec/ruby/core/rational/exponent_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/exponent'
+
+describe "Rational#**" do
+ it_behaves_like :rational_exponent, :**
+end
diff --git a/spec/ruby/core/rational/fdiv_spec.rb b/spec/ruby/core/rational/fdiv_spec.rb
new file mode 100644
index 0000000000..bfb321abaa
--- /dev/null
+++ b/spec/ruby/core/rational/fdiv_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/fdiv'
+
+describe "Rational#fdiv" do
+ it_behaves_like :rational_fdiv, :fdiv
+end
diff --git a/spec/ruby/core/rational/floor_spec.rb b/spec/ruby/core/rational/floor_spec.rb
new file mode 100644
index 0000000000..752a2d8815
--- /dev/null
+++ b/spec/ruby/core/rational/floor_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/floor'
+
+describe "Rational#floor" do
+ it_behaves_like :rational_floor, :floor
+end
diff --git a/spec/ruby/core/rational/hash_spec.rb b/spec/ruby/core/rational/hash_spec.rb
new file mode 100644
index 0000000000..84cd31518a
--- /dev/null
+++ b/spec/ruby/core/rational/hash_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/hash'
+
+describe "Rational#hash" do
+ it_behaves_like :rational_hash, :hash
+end
diff --git a/spec/ruby/core/rational/inspect_spec.rb b/spec/ruby/core/rational/inspect_spec.rb
new file mode 100644
index 0000000000..ef337ef0ce
--- /dev/null
+++ b/spec/ruby/core/rational/inspect_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/inspect'
+
+describe "Rational#inspect" do
+ it_behaves_like :rational_inspect, :inspect
+end
diff --git a/spec/ruby/core/rational/integer_spec.rb b/spec/ruby/core/rational/integer_spec.rb
new file mode 100644
index 0000000000..0f9a3bdead
--- /dev/null
+++ b/spec/ruby/core/rational/integer_spec.rb
@@ -0,0 +1,12 @@
+describe "Rational#integer?" do
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ it "returns false for a rational with a numerator and no denominator" do
+ Rational(20).integer?.should be_false
+ end
+ end
+
+ it "returns false for a rational with a numerator and a denominator" do
+ Rational(20,3).integer?.should be_false
+ end
+end
diff --git a/spec/ruby/core/rational/magnitude_spec.rb b/spec/ruby/core/rational/magnitude_spec.rb
new file mode 100644
index 0000000000..878fc8f879
--- /dev/null
+++ b/spec/ruby/core/rational/magnitude_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/abs'
+
+describe "Rational#abs" do
+ it_behaves_like :rational_abs, :magnitude
+end
diff --git a/spec/ruby/core/rational/marshal_dump_spec.rb b/spec/ruby/core/rational/marshal_dump_spec.rb
new file mode 100644
index 0000000000..17a6107cd5
--- /dev/null
+++ b/spec/ruby/core/rational/marshal_dump_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Rational#marshal_dump" do
+ it "is a private method" do
+ Rational.should have_private_instance_method(:marshal_dump, false)
+ end
+
+ it "dumps numerator and denominator" do
+ Rational(1, 2).send(:marshal_dump).should == [1, 2]
+ end
+end
diff --git a/spec/ruby/core/rational/minus_spec.rb b/spec/ruby/core/rational/minus_spec.rb
new file mode 100644
index 0000000000..9e0f81556b
--- /dev/null
+++ b/spec/ruby/core/rational/minus_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../shared/rational/minus'
+require_relative '../../shared/rational/arithmetic_exception_in_coerce'
+
+describe "Rational#-" do
+ it_behaves_like :rational_minus, :-
+ it_behaves_like :rational_arithmetic_exception_in_coerce, :-
+end
diff --git a/spec/ruby/core/rational/modulo_spec.rb b/spec/ruby/core/rational/modulo_spec.rb
new file mode 100644
index 0000000000..c43f7788e3
--- /dev/null
+++ b/spec/ruby/core/rational/modulo_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/modulo'
+
+describe "Rational#%" do
+ it_behaves_like :rational_modulo, :%
+end
diff --git a/spec/ruby/core/rational/multiply_spec.rb b/spec/ruby/core/rational/multiply_spec.rb
new file mode 100644
index 0000000000..ea644074e9
--- /dev/null
+++ b/spec/ruby/core/rational/multiply_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../shared/rational/multiply'
+require_relative '../../shared/rational/arithmetic_exception_in_coerce'
+
+describe "Rational#*" do
+ it_behaves_like :rational_multiply, :*
+ it_behaves_like :rational_arithmetic_exception_in_coerce, :*
+end
+
+describe "Rational#* passed a Rational" do
+ it_behaves_like :rational_multiply_rat, :*
+end
+
+describe "Rational#* passed a Float" do
+ it_behaves_like :rational_multiply_float, :*
+end
+
+describe "Rational#* passed an Integer" do
+ it_behaves_like :rational_multiply_int, :*
+end
diff --git a/spec/ruby/core/rational/numerator_spec.rb b/spec/ruby/core/rational/numerator_spec.rb
new file mode 100644
index 0000000000..85b2ed9e86
--- /dev/null
+++ b/spec/ruby/core/rational/numerator_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/numerator'
+
+describe "Rational#numerator" do
+ it_behaves_like :rational_numerator, :numerator
+end
diff --git a/spec/ruby/core/rational/plus_spec.rb b/spec/ruby/core/rational/plus_spec.rb
new file mode 100644
index 0000000000..e7ef3a8f92
--- /dev/null
+++ b/spec/ruby/core/rational/plus_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../shared/rational/plus'
+require_relative '../../shared/rational/arithmetic_exception_in_coerce'
+
+describe "Rational#+" do
+ it_behaves_like :rational_plus, :+
+ it_behaves_like :rational_arithmetic_exception_in_coerce, :+
+end
+
+describe "Rational#+ with a Rational" do
+ it_behaves_like :rational_plus_rat, :+
+end
+describe "Rational#+ with a Float" do
+ it_behaves_like :rational_plus_float, :+
+end
+
+describe "Rational#+ with an Integer" do
+ it_behaves_like :rational_plus_int, :+
+end
diff --git a/spec/ruby/core/rational/quo_spec.rb b/spec/ruby/core/rational/quo_spec.rb
new file mode 100644
index 0000000000..119aca1955
--- /dev/null
+++ b/spec/ruby/core/rational/quo_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/divide'
+
+describe "Rational#quo" do
+ it_behaves_like :rational_divide, :quo
+end
diff --git a/spec/ruby/core/rational/rational_spec.rb b/spec/ruby/core/rational/rational_spec.rb
new file mode 100644
index 0000000000..704e49354e
--- /dev/null
+++ b/spec/ruby/core/rational/rational_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Rational" do
+ it "includes Comparable" do
+ Rational.include?(Comparable).should == true
+ end
+end
diff --git a/spec/ruby/core/rational/rationalize_spec.rb b/spec/ruby/core/rational/rationalize_spec.rb
new file mode 100644
index 0000000000..61c7411104
--- /dev/null
+++ b/spec/ruby/core/rational/rationalize_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+
+describe "Rational#rationalize" do
+ it "returns self with no argument" do
+ Rational(12,3).rationalize.should == Rational(12,3)
+ Rational(-45,7).rationalize.should == Rational(-45,7)
+ end
+
+ # FIXME: These specs need reviewing by somebody familiar with the
+ # algorithm used by #rationalize
+ it "simplifies self to the degree specified by a Rational argument" do
+ r = Rational(5404319552844595,18014398509481984)
+ r.rationalize(Rational(1,10)).should == Rational(1,3)
+ r.rationalize(Rational(-1,10)).should == Rational(1,3)
+
+ r = Rational(-5404319552844595,18014398509481984)
+ r.rationalize(Rational(1,10)).should == Rational(-1,3)
+ r.rationalize(Rational(-1,10)).should == Rational(-1,3)
+
+ end
+
+ it "simplifies self to the degree specified by a Float argument" do
+ r = Rational(5404319552844595,18014398509481984)
+ r.rationalize(0.05).should == Rational(1,3)
+ r.rationalize(0.001).should == Rational(3, 10)
+
+ r = Rational(-5404319552844595,18014398509481984)
+ r.rationalize(0.05).should == Rational(-1,3)
+ r.rationalize(0.001).should == Rational(-3,10)
+ end
+
+ it "raises ArgumentError when passed more than one argument" do
+ lambda { Rational(1,1).rationalize(0.1, 0.1) }.should raise_error(ArgumentError)
+ lambda { Rational(1,1).rationalize(0.1, 0.1, 2) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/rational/remainder_spec.rb b/spec/ruby/core/rational/remainder_spec.rb
new file mode 100644
index 0000000000..0f9442f6f5
--- /dev/null
+++ b/spec/ruby/core/rational/remainder_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/remainder'
+
+describe "Rational#remainder" do
+ it_behaves_like :rational_remainder, :remainder
+end
diff --git a/spec/ruby/core/rational/round_spec.rb b/spec/ruby/core/rational/round_spec.rb
new file mode 100644
index 0000000000..deb0caf1b9
--- /dev/null
+++ b/spec/ruby/core/rational/round_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/round'
+
+describe "Rational#round" do
+ it_behaves_like :rational_round, :round
+end
diff --git a/spec/ruby/core/rational/to_f_spec.rb b/spec/ruby/core/rational/to_f_spec.rb
new file mode 100644
index 0000000000..15bf1e88dc
--- /dev/null
+++ b/spec/ruby/core/rational/to_f_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/to_f'
+
+describe "Rational#to_f" do
+ it_behaves_like :rational_to_f, :to_f
+end
diff --git a/spec/ruby/core/rational/to_i_spec.rb b/spec/ruby/core/rational/to_i_spec.rb
new file mode 100644
index 0000000000..3deb3664e1
--- /dev/null
+++ b/spec/ruby/core/rational/to_i_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/to_i'
+
+describe "Rational#to_i" do
+ it_behaves_like :rational_to_i, :to_i
+end
diff --git a/spec/ruby/core/rational/to_r_spec.rb b/spec/ruby/core/rational/to_r_spec.rb
new file mode 100644
index 0000000000..bd1c7c9307
--- /dev/null
+++ b/spec/ruby/core/rational/to_r_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../shared/rational/to_r'
+
+describe "Rational#to_r" do
+ it_behaves_like :rational_to_r, :to_r
+
+ it "raises TypeError trying to convert BasicObject" do
+ obj = BasicObject.new
+ lambda { Rational(obj) }.should raise_error(TypeError)
+ end
+
+ it "works when a BasicObject has to_r" do
+ obj = BasicObject.new; def obj.to_r; 1 / 2.to_r end
+ Rational(obj).should == Rational('1/2')
+ end
+
+ it "fails when a BasicObject's to_r does not return a Rational" do
+ obj = BasicObject.new; def obj.to_r; 1 end
+ lambda { Rational(obj) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/rational/to_s_spec.rb b/spec/ruby/core/rational/to_s_spec.rb
new file mode 100644
index 0000000000..c5c419787c
--- /dev/null
+++ b/spec/ruby/core/rational/to_s_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/to_s'
+
+describe "Rational#to_s" do
+ it_behaves_like :rational_to_s, :to_s
+end
diff --git a/spec/ruby/core/rational/truncate_spec.rb b/spec/ruby/core/rational/truncate_spec.rb
new file mode 100644
index 0000000000..4e72339752
--- /dev/null
+++ b/spec/ruby/core/rational/truncate_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../shared/rational/truncate'
+
+describe "Rational#truncate" do
+ it_behaves_like :rational_truncate, :truncate
+end
diff --git a/spec/ruby/core/rational/zero_spec.rb b/spec/ruby/core/rational/zero_spec.rb
new file mode 100644
index 0000000000..e6dd751922
--- /dev/null
+++ b/spec/ruby/core/rational/zero_spec.rb
@@ -0,0 +1,13 @@
+describe "Rational#zero?" do
+ it "returns true if the numerator is 0" do
+ Rational(0,26).zero?.should be_true
+ end
+
+ it "returns true if the numerator is 0.0" do
+ Rational(0.0,26).zero?.should be_true
+ end
+
+ it "returns false if the numerator isn't 0" do
+ Rational(26).zero?.should be_false
+ end
+end
diff --git a/spec/ruby/core/regexp/case_compare_spec.rb b/spec/ruby/core/regexp/case_compare_spec.rb
new file mode 100644
index 0000000000..2a41cfc25e
--- /dev/null
+++ b/spec/ruby/core/regexp/case_compare_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+
+describe "Regexp#===" do
+ it "is true if there is a match" do
+ (/abc/ === "aabcc").should be_true
+ end
+
+ it "is false if there is no match" do
+ (/abc/ === "xyz").should be_false
+ end
+
+ it "returns true if it matches a Symbol" do
+ (/a/ === :a).should be_true
+ end
+
+ it "returns false if it does not match a Symbol" do
+ (/a/ === :b).should be_false
+ end
+
+ # mirroring https://github.com/ruby/ruby/blob/trunk/test/ruby/test_regexp.rb
+ it "returns false if the other value cannot be coerced to a string" do
+ (/abc/ === nil).should be_false
+ (/abc/ === /abc/).should be_false
+ end
+end
diff --git a/spec/ruby/core/regexp/casefold_spec.rb b/spec/ruby/core/regexp/casefold_spec.rb
new file mode 100644
index 0000000000..d84a2d63c9
--- /dev/null
+++ b/spec/ruby/core/regexp/casefold_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+
+describe "Regexp#casefold?" do
+ it "returns the value of the case-insensitive flag" do
+ /abc/i.casefold?.should == true
+ /xyz/.casefold?.should == false
+ end
+end
diff --git a/spec/ruby/core/regexp/compile_spec.rb b/spec/ruby/core/regexp/compile_spec.rb
new file mode 100644
index 0000000000..4088c17c3c
--- /dev/null
+++ b/spec/ruby/core/regexp/compile_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require_relative 'shared/new_ascii'
+require_relative 'shared/new_ascii_8bit'
+
+describe "Regexp.compile" do
+ it_behaves_like :regexp_new_ascii, :compile
+ it_behaves_like :regexp_new_ascii_8bit, :compile
+end
+
+describe "Regexp.compile given a String" do
+ it_behaves_like :regexp_new_string_ascii, :compile
+ it_behaves_like :regexp_new_string_ascii_8bit, :compile
+end
+
+describe "Regexp.compile given a Regexp" do
+ it_behaves_like :regexp_new_regexp_ascii, :compile
+ it_behaves_like :regexp_new_regexp_ascii_8bit, :compile
+end
diff --git a/spec/ruby/core/regexp/encoding_spec.rb b/spec/ruby/core/regexp/encoding_spec.rb
new file mode 100644
index 0000000000..1d5bd8890c
--- /dev/null
+++ b/spec/ruby/core/regexp/encoding_spec.rb
@@ -0,0 +1,58 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe "Regexp#encoding" do
+ it "returns an Encoding object" do
+ /glar/.encoding.should be_an_instance_of(Encoding)
+ end
+
+ it "defaults to US-ASCII if the Regexp contains only US-ASCII character" do
+ /ASCII/.encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns US_ASCII if the 'n' modifier is supplied and only US-ASCII characters are present" do
+ /ASCII/n.encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns ASCII-8BIT if the 'n' modifier is supplied and non-US-ASCII characters are present" do
+ /\xc2\xa1/n.encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "defaults to UTF-8 if \\u escapes appear" do
+ /\u{9879}/.encoding.should == Encoding::UTF_8
+ end
+
+ it "defaults to UTF-8 if a literal UTF-8 character appears" do
+ /Â¥/.encoding.should == Encoding::UTF_8
+ end
+
+ it "returns UTF-8 if the 'u' modifier is supplied" do
+ /ASCII/u.encoding.should == Encoding::UTF_8
+ end
+
+ it "returns Windows-31J if the 's' modifier is supplied" do
+ /ASCII/s.encoding.should == Encoding::Windows_31J
+ end
+
+ it "returns EUC_JP if the 'e' modifier is supplied" do
+ /ASCII/e.encoding.should == Encoding::EUC_JP
+ end
+
+ it "upgrades the encoding to that of an embedded String" do
+ str = "文字化ã‘".encode('euc-jp')
+ /#{str}/.encoding.should == Encoding::EUC_JP
+ end
+
+ it "ignores the encoding and uses US-ASCII if the string has only ASCII characters" do
+ str = "abc".encode('euc-jp')
+ str.encoding.should == Encoding::EUC_JP
+ /#{str}/.encoding.should == Encoding::US_ASCII
+ end
+
+ it "ignores the default_internal encoding" do
+ old_internal = Encoding.default_internal
+ Encoding.default_internal = Encoding::EUC_JP
+ /foo/.encoding.should_not == Encoding::EUC_JP
+ Encoding.default_internal = old_internal
+ end
+end
diff --git a/spec/ruby/core/regexp/eql_spec.rb b/spec/ruby/core/regexp/eql_spec.rb
new file mode 100644
index 0000000000..bd5ae43eb2
--- /dev/null
+++ b/spec/ruby/core/regexp/eql_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/equal_value'
+
+describe "Regexp#eql?" do
+ it_behaves_like :regexp_eql, :eql?
+end
diff --git a/spec/ruby/core/regexp/equal_value_spec.rb b/spec/ruby/core/regexp/equal_value_spec.rb
new file mode 100644
index 0000000000..5455a30598
--- /dev/null
+++ b/spec/ruby/core/regexp/equal_value_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/equal_value'
+
+describe "Regexp#==" do
+ it_behaves_like :regexp_eql, :==
+end
diff --git a/spec/ruby/core/regexp/escape_spec.rb b/spec/ruby/core/regexp/escape_spec.rb
new file mode 100644
index 0000000000..6b06ab1cbc
--- /dev/null
+++ b/spec/ruby/core/regexp/escape_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/quote'
+
+describe "Regexp.escape" do
+ it_behaves_like :regexp_quote, :escape
+end
diff --git a/spec/ruby/core/regexp/fixed_encoding_spec.rb b/spec/ruby/core/regexp/fixed_encoding_spec.rb
new file mode 100644
index 0000000000..29d0a22c53
--- /dev/null
+++ b/spec/ruby/core/regexp/fixed_encoding_spec.rb
@@ -0,0 +1,36 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe "Regexp#fixed_encoding?" do
+ it "returns false by default" do
+ /needle/.fixed_encoding?.should be_false
+ end
+
+ it "returns false if the 'n' modifier was supplied to the Regexp" do
+ /needle/n.fixed_encoding?.should be_false
+ end
+
+ it "returns true if the 'u' modifier was supplied to the Regexp" do
+ /needle/u.fixed_encoding?.should be_true
+ end
+
+ it "returns true if the 's' modifier was supplied to the Regexp" do
+ /needle/s.fixed_encoding?.should be_true
+ end
+
+ it "returns true if the 'e' modifier was supplied to the Regexp" do
+ /needle/e.fixed_encoding?.should be_true
+ end
+
+ it "returns true if the Regexp contains a \\u escape" do
+ /needle \u{8768}/.fixed_encoding?.should be_true
+ end
+
+ it "returns true if the Regexp contains a UTF-8 literal" do
+ /文字化ã‘/.fixed_encoding?.should be_true
+ end
+
+ it "returns true if the Regexp was created with the Regexp::FIXEDENCODING option" do
+ Regexp.new("", Regexp::FIXEDENCODING).fixed_encoding?.should be_true
+ end
+end
diff --git a/spec/ruby/core/regexp/hash_spec.rb b/spec/ruby/core/regexp/hash_spec.rb
new file mode 100644
index 0000000000..2d42e288e6
--- /dev/null
+++ b/spec/ruby/core/regexp/hash_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+
+describe "Regexp#hash" do
+ it "is provided" do
+ Regexp.new('').respond_to?(:hash).should == true
+ end
+
+ it "is based on the text and options of Regexp" do
+ (/cat/.hash == /dog/.hash).should == false
+ (/dog/m.hash == /dog/m.hash).should == true
+ not_supported_on :opal do
+ (/cat/ix.hash == /cat/ixn.hash).should == true
+ (/cat/.hash == /cat/ix.hash).should == false
+ end
+ end
+
+ it "returns the same value for two Regexps differing only in the /n option" do
+ (//.hash == //n.hash).should == true
+ end
+end
diff --git a/spec/ruby/core/regexp/initialize_spec.rb b/spec/ruby/core/regexp/initialize_spec.rb
new file mode 100644
index 0000000000..026348b1dd
--- /dev/null
+++ b/spec/ruby/core/regexp/initialize_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "Regexp#initialize" do
+ it "is a private method" do
+ Regexp.should have_private_method(:initialize)
+ end
+
+ it "raises a SecurityError on a Regexp literal" do
+ lambda { //.send(:initialize, "") }.should raise_error(SecurityError)
+ end
+
+ it "raises a TypeError on an initialized non-literal Regexp" do
+ lambda { Regexp.new("").send(:initialize, "") }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/regexp/inspect_spec.rb b/spec/ruby/core/regexp/inspect_spec.rb
new file mode 100644
index 0000000000..f4e39234f5
--- /dev/null
+++ b/spec/ruby/core/regexp/inspect_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+
+describe "Regexp#inspect" do
+ it "returns a formatted string that would eval to the same regexp" do
+ not_supported_on :opal do
+ /ab+c/ix.inspect.should == "/ab+c/ix"
+ /a(.)+s/n.inspect.should =~ %r|/a(.)+s/n?| # Default 'n' may not appear
+ end
+ # 1.9 doesn't round-trip the encoding flags, such as 'u'. This is
+ # seemingly by design.
+ /a(.)+s/m.inspect.should == "/a(.)+s/m" # But a specified one does
+ end
+
+ it "returns options in the order 'mixn'" do
+ //nixm.inspect.should == "//mixn"
+ end
+
+ it "does not include the 'o' option" do
+ //o.inspect.should == "//"
+ end
+
+ it "does not include a character set code" do
+ //u.inspect.should == "//"
+ //s.inspect.should == "//"
+ //e.inspect.should == "//"
+ end
+
+ it "correctly escapes forward slashes /" do
+ Regexp.new("/foo/bar").inspect.should == "/\\/foo\\/bar/"
+ Regexp.new("/foo/bar[/]").inspect.should == "/\\/foo\\/bar[\\/]/"
+ end
+
+ it "doesn't over escape forward slashes" do
+ /\/foo\/bar/.inspect.should == '/\/foo\/bar/'
+ end
+
+ it "escapes 2 slashes in a row properly" do
+ Regexp.new("//").inspect.should == '/\/\//'
+ end
+
+ it "does not over escape" do
+ Regexp.new('\\\/').inspect.should == "/\\\\\\//"
+ end
+end
diff --git a/spec/ruby/core/regexp/last_match_spec.rb b/spec/ruby/core/regexp/last_match_spec.rb
new file mode 100644
index 0000000000..ed496b7941
--- /dev/null
+++ b/spec/ruby/core/regexp/last_match_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe "Regexp.last_match" do
+ it "returns MatchData instance when not passed arguments" do
+ /c(.)t/ =~ 'cat'
+
+ Regexp.last_match.should be_kind_of(MatchData)
+ end
+
+ it "returns the nth field in this MatchData when passed a Fixnum" do
+ /c(.)t/ =~ 'cat'
+ Regexp.last_match(1).should == 'a'
+ end
+end
diff --git a/spec/ruby/core/regexp/match_spec.rb b/spec/ruby/core/regexp/match_spec.rb
new file mode 100644
index 0000000000..e3247a088d
--- /dev/null
+++ b/spec/ruby/core/regexp/match_spec.rb
@@ -0,0 +1,148 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe :regexp_match, shared: true do
+ it "returns nil if there is no match" do
+ /xyz/.send(@method,"abxyc").should be_nil
+ end
+
+ it "returns nil if the object is nil" do
+ /\w+/.send(@method, nil).should be_nil
+ end
+end
+
+describe "Regexp#=~" do
+ it_behaves_like :regexp_match, :=~
+
+ it "returns the index of the first character of the matching region" do
+ (/(.)(.)(.)/ =~ "abc").should == 0
+ end
+
+ it "returns the index too, when argument is a Symbol" do
+ (/(.)(.)(.)/ =~ :abc).should == 0
+ end
+end
+
+describe "Regexp#match" do
+ it_behaves_like :regexp_match, :match
+
+ it "returns a MatchData object" do
+ /(.)(.)(.)/.match("abc").should be_kind_of(MatchData)
+ end
+
+ it "returns a MatchData object, when argument is a Symbol" do
+ /(.)(.)(.)/.match(:abc).should be_kind_of(MatchData)
+ end
+
+ it "raises a TypeError on an uninitialized Regexp" do
+ lambda { Regexp.allocate.match('foo') }.should raise_error(TypeError)
+ end
+
+ describe "with [string, position]" do
+ describe "when given a positive position" do
+ it "matches the input at a given position" do
+ /(.).(.)/.match("01234", 1).captures.should == ["1", "3"]
+ end
+
+ with_feature :encoding do
+ it "uses the start as a character offset" do
+ /(.).(.)/.match("零一二三四", 1).captures.should == ["一", "三"]
+ end
+
+ it "raises an ArgumentError for an invalid encoding" do
+ x96 = ([150].pack('C')).force_encoding('utf-8')
+ lambda { /(.).(.)/.match("Hello, #{x96} world!", 1) }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe "when given a negative position" do
+ it "matches the input at a given position" do
+ /(.).(.)/.match("01234", -4).captures.should == ["1", "3"]
+ end
+
+ with_feature :encoding do
+ it "uses the start as a character offset" do
+ /(.).(.)/.match("零一二三四", -4).captures.should == ["一", "三"]
+ end
+
+ it "raises an ArgumentError for an invalid encoding" do
+ x96 = ([150].pack('C')).force_encoding('utf-8')
+ lambda { /(.).(.)/.match("Hello, #{x96} world!", -1) }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe "when passed a block" do
+ it "yields the MatchData" do
+ /./.match("abc") {|m| ScratchPad.record m }
+ ScratchPad.recorded.should be_kind_of(MatchData)
+ end
+
+ it "returns the block result" do
+ /./.match("abc") { :result }.should == :result
+ end
+
+ it "does not yield if there is no match" do
+ ScratchPad.record []
+ /a/.match("b") {|m| ScratchPad << m }
+ ScratchPad.recorded.should == []
+ end
+ end
+ end
+
+ it "resets $~ if passed nil" do
+ # set $~
+ /./.match("a")
+ $~.should be_kind_of(MatchData)
+
+ /1/.match(nil)
+ $~.should be_nil
+ end
+
+ it "raises TypeError when the given argument cannot be coarce to String" do
+ f = 1
+ lambda { /foo/.match(f)[0] }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError when the given argument is an Exception" do
+ f = Exception.new("foo")
+ lambda { /foo/.match(f)[0] }.should raise_error(TypeError)
+ end
+end
+
+ruby_version_is "2.4" do
+ describe "Regexp#match?" do
+ before :each do
+ # Resetting Regexp.last_match
+ /DONTMATCH/.match ''
+ end
+
+ context "when matches the given value" do
+ it "returns true but does not set Regexp.last_match" do
+ /string/i.match?('string').should be_true
+ Regexp.last_match.should be_nil
+ end
+ end
+
+ it "returns false when does not match the given value" do
+ /STRING/.match?('string').should be_false
+ end
+
+ it "takes matching position as the 2nd argument" do
+ /str/i.match?('string', 0).should be_true
+ /str/i.match?('string', 1).should be_false
+ end
+
+ it "returns false when given nil" do
+ /./.match?(nil).should be_false
+ end
+ end
+end
+
+describe "Regexp#~" do
+ it "matches against the contents of $_" do
+ $_ = "input data"
+ (~ /at/).should == 7
+ end
+end
diff --git a/spec/ruby/core/regexp/named_captures_spec.rb b/spec/ruby/core/regexp/named_captures_spec.rb
new file mode 100644
index 0000000000..1a68d7877b
--- /dev/null
+++ b/spec/ruby/core/regexp/named_captures_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+
+describe "Regexp#named_captures" do
+ it "returns a Hash" do
+ /foo/.named_captures.should be_an_instance_of(Hash)
+ end
+
+ it "returns an empty Hash when there are no capture groups" do
+ /foo/.named_captures.should == {}
+ end
+
+ it "sets the keys of the Hash to the names of the capture groups" do
+ rex = /this (?<is>is) [aA] (?<pat>pate?rn)/
+ rex.named_captures.keys.should == ['is','pat']
+ end
+
+ it "sets the values of the Hash to Arrays" do
+ rex = /this (?<is>is) [aA] (?<pat>pate?rn)/
+ rex.named_captures.values.each do |value|
+ value.should be_an_instance_of(Array)
+ end
+ end
+
+ it "sets each element of the Array to the corresponding group's index" do
+ rex = /this (?<is>is) [aA] (?<pat>pate?rn)/
+ rex.named_captures['is'].should == [1]
+ rex.named_captures['pat'].should == [2]
+ end
+
+ it "works with duplicate capture group names" do
+ rex = /this (?<is>is) [aA] (?<pat>pate?(?<is>rn))/
+ rex.named_captures['is'].should == [1,3]
+ rex.named_captures['pat'].should == [2]
+ end
+end
diff --git a/spec/ruby/core/regexp/names_spec.rb b/spec/ruby/core/regexp/names_spec.rb
new file mode 100644
index 0000000000..099768fd26
--- /dev/null
+++ b/spec/ruby/core/regexp/names_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+
+describe "Regexp#names" do
+ it "returns an Array" do
+ /foo/.names.should be_an_instance_of(Array)
+ end
+
+ it "returns an empty Array if there are no named captures" do
+ /needle/.names.should == []
+ end
+
+ it "returns each named capture as a String" do
+ /n(?<cap>ee)d(?<ture>le)/.names.each do |name|
+ name.should be_an_instance_of(String)
+ end
+ end
+
+ it "returns all of the named captures" do
+ /n(?<cap>ee)d(?<ture>le)/.names.should == ['cap', 'ture']
+ end
+
+ it "works with nested named captures" do
+ /n(?<cap>eed(?<ture>le))/.names.should == ['cap', 'ture']
+ end
+
+ it "returns each capture name only once" do
+ /n(?<cap>ee)d(?<cap>le)/.names.should == ['cap']
+ end
+end
diff --git a/spec/ruby/core/regexp/new_spec.rb b/spec/ruby/core/regexp/new_spec.rb
new file mode 100644
index 0000000000..dbac7a5a33
--- /dev/null
+++ b/spec/ruby/core/regexp/new_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require_relative 'shared/new_ascii'
+require_relative 'shared/new_ascii_8bit'
+
+describe "Regexp.new" do
+ it_behaves_like :regexp_new_ascii, :new
+ it_behaves_like :regexp_new_ascii_8bit, :new
+end
+
+describe "Regexp.new given a String" do
+ it_behaves_like :regexp_new_string_ascii, :new
+ it_behaves_like :regexp_new_string_ascii_8bit, :new
+end
+
+describe "Regexp.new given a Regexp" do
+ it_behaves_like :regexp_new_regexp_ascii, :new
+ it_behaves_like :regexp_new_regexp_ascii_8bit, :new
+end
+
+describe "Regexp.new given a Fixnum" do
+ it "raises a TypeError" do
+ lambda { Regexp.new(1) }.should raise_error(TypeError)
+ end
+end
+
+describe "Regexp.new given a Float" do
+ it "raises a TypeError" do
+ lambda { Regexp.new(1.0) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/regexp/options_spec.rb b/spec/ruby/core/regexp/options_spec.rb
new file mode 100644
index 0000000000..5fbde89988
--- /dev/null
+++ b/spec/ruby/core/regexp/options_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+
+describe "Regexp#options" do
+ it "returns a Fixnum bitvector of regexp options for the Regexp object" do
+ /cat/.options.should be_kind_of(Fixnum)
+ not_supported_on :opal do
+ /cat/ix.options.should be_kind_of(Fixnum)
+ end
+ end
+
+ it "allows checking for presence of a certain option with bitwise &" do
+ (/cat/.options & Regexp::IGNORECASE).should == 0
+ (/cat/i.options & Regexp::IGNORECASE).should_not == 0
+ (/cat/.options & Regexp::MULTILINE).should == 0
+ (/cat/m.options & Regexp::MULTILINE).should_not == 0
+ not_supported_on :opal do
+ (/cat/.options & Regexp::EXTENDED).should == 0
+ (/cat/x.options & Regexp::EXTENDED).should_not == 0
+ (/cat/mx.options & Regexp::MULTILINE).should_not == 0
+ (/cat/mx.options & Regexp::EXTENDED).should_not == 0
+ (/cat/xi.options & Regexp::IGNORECASE).should_not == 0
+ (/cat/xi.options & Regexp::EXTENDED).should_not == 0
+ end
+ end
+
+ it "returns 0 for a Regexp literal without options" do
+ //.options.should == 0
+ /abc/.options.should == 0
+ end
+
+ it "raises a TypeError on an uninitialized Regexp" do
+ lambda { Regexp.allocate.options }.should raise_error(TypeError)
+ end
+
+ it "includes Regexp::FIXEDENCODING for a Regexp literal with the 'u' option" do
+ (//u.options & Regexp::FIXEDENCODING).should_not == 0
+ end
+
+ it "includes Regexp::FIXEDENCODING for a Regexp literal with the 'e' option" do
+ (//e.options & Regexp::FIXEDENCODING).should_not == 0
+ end
+
+ it "includes Regexp::FIXEDENCODING for a Regexp literal with the 's' option" do
+ (//s.options & Regexp::FIXEDENCODING).should_not == 0
+ end
+
+ it "does not include Regexp::FIXEDENCODING for a Regexp literal with the 'n' option" do
+ (//n.options & Regexp::FIXEDENCODING).should == 0
+ end
+
+ it "includes Regexp::NOENCODING for a Regexp literal with the 'n' option" do
+ (//n.options & Regexp::NOENCODING).should_not == 0
+ end
+end
diff --git a/spec/ruby/core/regexp/quote_spec.rb b/spec/ruby/core/regexp/quote_spec.rb
new file mode 100644
index 0000000000..370ab13e30
--- /dev/null
+++ b/spec/ruby/core/regexp/quote_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/quote'
+
+describe "Regexp.quote" do
+ it_behaves_like :regexp_quote, :quote
+end
diff --git a/spec/ruby/core/regexp/shared/equal_value.rb b/spec/ruby/core/regexp/shared/equal_value.rb
new file mode 100644
index 0000000000..803988de9e
--- /dev/null
+++ b/spec/ruby/core/regexp/shared/equal_value.rb
@@ -0,0 +1,31 @@
+describe :regexp_eql, shared: true do
+ it "is true if self and other have the same pattern" do
+ /abc/.send(@method, /abc/).should == true
+ /abc/.send(@method, /abd/).should == false
+ end
+
+ not_supported_on :opal do
+ it "is true if self and other have the same character set code" do
+ /abc/.send(@method, /abc/x).should == false
+ /abc/x.send(@method, /abc/x).should == true
+ /abc/u.send(@method, /abc/n).should == false
+ /abc/u.send(@method, /abc/u).should == true
+ /abc/n.send(@method, /abc/n).should == true
+ end
+ end
+
+ it "is true if other has the same #casefold? values" do
+ /abc/.send(@method, /abc/i).should == false
+ /abc/i.send(@method, /abc/i).should == true
+ end
+
+ not_supported_on :opal do
+ it "is true if self does not specify /n option and other does" do
+ //.send(@method, //n).should == true
+ end
+
+ it "is true if self specifies /n option and other does not" do
+ //n.send(@method, //).should == true
+ end
+ end
+end
diff --git a/spec/ruby/core/regexp/shared/new_ascii.rb b/spec/ruby/core/regexp/shared/new_ascii.rb
new file mode 100644
index 0000000000..98c458312e
--- /dev/null
+++ b/spec/ruby/core/regexp/shared/new_ascii.rb
@@ -0,0 +1,464 @@
+# -*- encoding: binary -*-
+describe :regexp_new_ascii, shared: true do
+ it "requires one argument and creates a new regular expression object" do
+ Regexp.send(@method, '').is_a?(Regexp).should == true
+ end
+
+ it "works by default for subclasses with overridden #initialize" do
+ class RegexpSpecsSubclass < Regexp
+ def initialize(*args)
+ super
+ @args = args
+ end
+
+ attr_accessor :args
+ end
+
+ class RegexpSpecsSubclassTwo < Regexp; end
+
+ RegexpSpecsSubclass.send(@method, "hi").should be_kind_of(RegexpSpecsSubclass)
+ RegexpSpecsSubclass.send(@method, "hi").args.first.should == "hi"
+
+ RegexpSpecsSubclassTwo.send(@method, "hi").should be_kind_of(RegexpSpecsSubclassTwo)
+ end
+end
+
+describe :regexp_new_string_ascii, 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$/
+ end
+
+ it "raises a RegexpError when passed an incorrect regexp" do
+ lambda { Regexp.send(@method, "^[$", 0) }.should raise_error(RegexpError)
+ end
+
+ it "does not set Regexp options if only given one argument" do
+ r = Regexp.send(@method, 'Hi')
+ (r.options & Regexp::IGNORECASE).should == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ not_supported_on :opal do
+ (r.options & Regexp::EXTENDED).should == 0
+ end
+ end
+
+ it "does not set Regexp options if second argument is nil or false" do
+ r = Regexp.send(@method, 'Hi', nil)
+ (r.options & Regexp::IGNORECASE).should == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ not_supported_on :opal do
+ (r.options & Regexp::EXTENDED).should == 0
+ end
+
+ r = Regexp.send(@method, 'Hi', false)
+ (r.options & Regexp::IGNORECASE).should == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ not_supported_on :opal do
+ (r.options & Regexp::EXTENDED).should == 0
+ end
+ end
+
+ it "sets options from second argument if it is one of the Fixnum option constants" do
+ r = Regexp.send(@method, 'Hi', Regexp::IGNORECASE)
+ (r.options & Regexp::IGNORECASE).should_not == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ not_supported_on :opal do
+ (r.options & Regexp::EXTENDED).should == 0
+ end
+
+ r = Regexp.send(@method, 'Hi', Regexp::MULTILINE)
+ (r.options & Regexp::IGNORECASE).should == 0
+ (r.options & Regexp::MULTILINE).should_not == 0
+ not_supported_on :opal do
+ (r.options & Regexp::EXTENDED).should == 0
+ end
+
+ not_supported_on :opal do
+ r = Regexp.send(@method, 'Hi', Regexp::EXTENDED)
+ (r.options & Regexp::IGNORECASE).should == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ (r.options & Regexp::EXTENDED).should_not == 1
+ end
+ end
+
+ it "accepts a Fixnum of two or more options ORed together as the second argument" do
+ r = Regexp.send(@method, 'Hi', Regexp::IGNORECASE | Regexp::EXTENDED)
+ (r.options & Regexp::IGNORECASE).should_not == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ (r.options & Regexp::EXTENDED).should_not == 0
+ end
+
+ it "treats any non-Fixnum, non-nil, non-false second argument as IGNORECASE" do
+ r = Regexp.send(@method, 'Hi', Object.new)
+ (r.options & Regexp::IGNORECASE).should_not == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ not_supported_on :opal do
+ (r.options & Regexp::EXTENDED).should == 0
+ end
+ end
+
+ it "ignores the third argument if it is 'e' or 'euc' (case-insensitive)" do
+ lambda {
+ 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
+ lambda {
+ 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
+ lambda {
+ 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 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::ASCII_8BIT
+ Regexp.send(@method, str, nil, 'n').encoding.should == Encoding::ASCII_8BIT
+ Regexp.send(@method, str, nil, 'none').encoding.should == Encoding::ASCII_8BIT
+ Regexp.send(@method, str, nil, 'NONE').encoding.should == Encoding::ASCII_8BIT
+ end
+
+ describe "with escaped characters" do
+ it "raises a Regexp error if there is a trailing backslash" do
+ lambda { Regexp.send(@method, "\\") }.should raise_error(RegexpError)
+ end
+
+ it "does not raise a Regexp error if there is an escaped trailing backslash" do
+ lambda { Regexp.send(@method, "\\\\") }.should_not raise_error(RegexpError)
+ end
+
+ it "accepts a backspace followed by a character" do
+ Regexp.send(@method, "\\N").should == /#{"\x5c"+"N"}/
+ end
+
+ it "accepts a one-digit octal value" do
+ Regexp.send(@method, "\0").should == /#{"\x00"}/
+ end
+
+ it "accepts a two-digit octal value" do
+ Regexp.send(@method, "\11").should == /#{"\x09"}/
+ end
+
+ it "accepts a one-digit hexadecimal value" do
+ Regexp.send(@method, "\x9n").should == /#{"\x09n"}/
+ end
+
+ it "accepts a two-digit hexadecimal value" do
+ Regexp.send(@method, "\x23").should == /#{"\x23"}/
+ end
+
+ it "interprets a digit following a two-digit hexadecimal value as a character" do
+ Regexp.send(@method, "\x420").should == /#{"\x420"}/
+ end
+
+ it "raises a RegexpError if \\x is not followed by any hexadecimal digits" do
+ lambda { Regexp.send(@method, "\\" + "xn") }.should raise_error(RegexpError)
+ end
+
+ it "accepts an escaped string interpolation" do
+ Regexp.send(@method, "\#{abc}").should == /#{"\#{abc}"}/
+ end
+
+ it "accepts '\\n'" do
+ Regexp.send(@method, "\n").should == /#{"\x0a"}/
+ end
+
+ it "accepts '\\t'" do
+ Regexp.send(@method, "\t").should == /#{"\x09"}/
+ end
+
+ it "accepts '\\r'" do
+ Regexp.send(@method, "\r").should == /#{"\x0d"}/
+ end
+
+ it "accepts '\\f'" do
+ Regexp.send(@method, "\f").should == /#{"\x0c"}/
+ end
+
+ it "accepts '\\v'" do
+ Regexp.send(@method, "\v").should == /#{"\x0b"}/
+ end
+
+ it "accepts '\\a'" do
+ Regexp.send(@method, "\a").should == /#{"\x07"}/
+ end
+
+ it "accepts '\\e'" do
+ Regexp.send(@method, "\e").should == /#{"\x1b"}/
+ end
+
+ it "accepts '\\C-\\n'" do
+ Regexp.send(@method, "\C-\n").should == /#{"\x0a"}/
+ end
+
+ it "accepts '\\C-\\t'" do
+ Regexp.send(@method, "\C-\t").should == /#{"\x09"}/
+ end
+
+ it "accepts '\\C-\\r'" do
+ Regexp.send(@method, "\C-\r").should == /#{"\x0d"}/
+ end
+
+ it "accepts '\\C-\\f'" do
+ Regexp.send(@method, "\C-\f").should == /#{"\x0c"}/
+ end
+
+ it "accepts '\\C-\\v'" do
+ Regexp.send(@method, "\C-\v").should == /#{"\x0b"}/
+ end
+
+ it "accepts '\\C-\\a'" do
+ Regexp.send(@method, "\C-\a").should == /#{"\x07"}/
+ end
+
+ it "accepts '\\C-\\e'" do
+ Regexp.send(@method, "\C-\e").should == /#{"\x1b"}/
+ end
+
+ it "accepts '\\c\\n'" do
+ Regexp.send(@method, "\C-\n").should == /#{"\x0a"}/
+ end
+
+ it "accepts '\\c\\t'" do
+ Regexp.send(@method, "\C-\t").should == /#{"\x09"}/
+ end
+
+ it "accepts '\\c\\r'" do
+ Regexp.send(@method, "\C-\r").should == /#{"\x0d"}/
+ end
+
+ it "accepts '\\c\\f'" do
+ Regexp.send(@method, "\C-\f").should == /#{"\x0c"}/
+ end
+
+ it "accepts '\\c\\v'" do
+ Regexp.send(@method, "\C-\v").should == /#{"\x0b"}/
+ end
+
+ it "accepts '\\c\\a'" do
+ Regexp.send(@method, "\C-\a").should == /#{"\x07"}/
+ end
+
+ it "accepts '\\c\\e'" do
+ Regexp.send(@method, "\C-\e").should == /#{"\x1b"}/
+ end
+
+ it "accepts multiple consecutive '\\' characters" do
+ Regexp.send(@method, "\\\\\\N").should == /#{"\\\\\\"+"N"}/
+ end
+
+ it "accepts characters and escaped octal digits" do
+ Regexp.send(@method, "abc\076").should == /#{"abc\x3e"}/
+ end
+
+ it "accepts escaped octal digits and characters" do
+ Regexp.send(@method, "\076abc").should == /#{"\x3eabc"}/
+ end
+
+ it "accepts characters and escaped hexadecimal digits" do
+ Regexp.send(@method, "abc\x42").should == /#{"abc\x42"}/
+ end
+
+ it "accepts escaped hexadecimal digits and characters" do
+ Regexp.send(@method, "\x3eabc").should == /#{"\x3eabc"}/
+ end
+
+ it "accepts escaped hexadecimal and octal digits" do
+ Regexp.send(@method, "\061\x42").should == /#{"\x31\x42"}/
+ end
+
+ it "accepts \\u{H} for a single Unicode codepoint" do
+ Regexp.send(@method, "\u{f}").should == /#{"\x0f"}/
+ end
+
+ it "accepts \\u{HH} for a single Unicode codepoint" do
+ Regexp.send(@method, "\u{7f}").should == /#{"\x7f"}/
+ end
+
+ it "accepts \\u{HHH} for a single Unicode codepoint" do
+ Regexp.send(@method, "\u{07f}").should == /#{"\x7f"}/
+ end
+
+ it "accepts \\u{HHHH} for a single Unicode codepoint" do
+ Regexp.send(@method, "\u{0000}").should == /#{"\x00"}/
+ end
+
+ it "accepts \\u{HHHHH} for a single Unicode codepoint" do
+ Regexp.send(@method, "\u{00001}").should == /#{"\x01"}/
+ end
+
+ it "accepts \\u{HHHHHH} for a single Unicode codepoint" do
+ Regexp.send(@method, "\u{000000}").should == /#{"\x00"}/
+ end
+
+ it "accepts characters followed by \\u{HHHH}" do
+ Regexp.send(@method, "abc\u{3042}").should == /#{"abc\u3042"}/
+ end
+
+ it "accepts \\u{HHHH} followed by characters" do
+ Regexp.send(@method, "\u{3042}abc").should == /#{"\u3042abc"}/
+ end
+
+ it "accepts escaped hexadecimal digits followed by \\u{HHHH}" do
+ Regexp.send(@method, "\x42\u{3042}").should == /#{"\x42\u3042"}/
+ end
+
+ it "accepts escaped octal digits followed by \\u{HHHH}" do
+ Regexp.send(@method, "\056\u{3042}").should == /#{"\x2e\u3042"}/
+ end
+
+ it "accepts a combination of escaped octal and hexadecimal digits and \\u{HHHH}" do
+ Regexp.send(@method, "\056\x42\u{3042}\x52\076").should == /#{"\x2e\x42\u3042\x52\x3e"}/
+ end
+
+ it "accepts \\uHHHH for a single Unicode codepoint" do
+ Regexp.send(@method, "\u3042").should == /#{"\u3042"}/
+ end
+
+ it "accepts characters followed by \\uHHHH" do
+ Regexp.send(@method, "abc\u3042").should == /#{"abc\u3042"}/
+ end
+
+ it "accepts \\uHHHH followed by characters" do
+ Regexp.send(@method, "\u3042abc").should == /#{"\u3042abc"}/
+ end
+
+ it "accepts escaped hexadecimal digits followed by \\uHHHH" do
+ Regexp.send(@method, "\x42\u3042").should == /#{"\x42\u3042"}/
+ end
+
+ it "accepts escaped octal digits followed by \\uHHHH" do
+ Regexp.send(@method, "\056\u3042").should == /#{"\x2e\u3042"}/
+ end
+
+ it "accepts a combination of escaped octal and hexadecimal digits and \\uHHHH" do
+ Regexp.send(@method, "\056\x42\u3042\x52\076").should == /#{"\x2e\x42\u3042\x52\x3e"}/
+ end
+
+ it "raises a RegexpError if less than four digits are given for \\uHHHH" do
+ lambda { Regexp.send(@method, "\\" + "u304") }.should raise_error(RegexpError)
+ end
+
+ it "raises a RegexpError if the \\u{} escape is empty" do
+ lambda { Regexp.send(@method, "\\" + "u{}") }.should raise_error(RegexpError)
+ end
+
+ it "raises a RegexpError if more than six hexadecimal digits are given" do
+ lambda { Regexp.send(@method, "\\" + "u{0ffffff}") }.should raise_error(RegexpError)
+ end
+
+ it "returns a Regexp with US-ASCII encoding if only 7-bit ASCII characters are present regardless of the input String's encoding" do
+ Regexp.send(@method, "abc").encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns a Regexp with source String having US-ASCII encoding if only 7-bit ASCII characters are present regardless of the input String's encoding" do
+ Regexp.send(@method, "abc").source.encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns a Regexp with US-ASCII encoding if UTF-8 escape sequences using only 7-bit ASCII are present" do
+ Regexp.send(@method, "\u{61}").encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns a Regexp with source String having US-ASCII encoding if UTF-8 escape sequences using only 7-bit ASCII are present" do
+ Regexp.send(@method, "\u{61}").source.encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns a Regexp with UTF-8 encoding if any UTF-8 escape sequences outside 7-bit ASCII are present" do
+ Regexp.send(@method, "\u{ff}").encoding.should == Encoding::UTF_8
+ end
+
+ it "returns a Regexp with source String having UTF-8 encoding if any UTF-8 escape sequences outside 7-bit ASCII are present" do
+ Regexp.send(@method, "\u{ff}").source.encoding.should == Encoding::UTF_8
+ end
+
+ it "returns a Regexp with the input String's encoding" do
+ str = "\x82\xa0".force_encoding(Encoding::Shift_JIS)
+ Regexp.send(@method, str).encoding.should == Encoding::Shift_JIS
+ end
+
+ it "returns a Regexp with source String having the input String's encoding" do
+ str = "\x82\xa0".force_encoding(Encoding::Shift_JIS)
+ Regexp.send(@method, str).source.encoding.should == Encoding::Shift_JIS
+ end
+ end
+end
+
+describe :regexp_new_regexp_ascii, shared: true do
+ it "uses the argument as a literal to construct a Regexp object" do
+ Regexp.send(@method, /^hi{2,3}fo.o$/).should == /^hi{2,3}fo.o$/
+ end
+
+ it "preserves any options given in the Regexp literal" do
+ (Regexp.send(@method, /Hi/i).options & Regexp::IGNORECASE).should_not == 0
+ (Regexp.send(@method, /Hi/m).options & Regexp::MULTILINE).should_not == 0
+ not_supported_on :opal do
+ (Regexp.send(@method, /Hi/x).options & Regexp::EXTENDED).should_not == 0
+ end
+
+ not_supported_on :opal do
+ r = Regexp.send @method, /Hi/imx
+ (r.options & Regexp::IGNORECASE).should_not == 0
+ (r.options & Regexp::MULTILINE).should_not == 0
+ (r.options & Regexp::EXTENDED).should_not == 0
+ end
+
+ r = Regexp.send @method, /Hi/
+ (r.options & Regexp::IGNORECASE).should == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ not_supported_on :opal do
+ (r.options & Regexp::EXTENDED).should == 0
+ end
+ end
+
+ it "does not honour options given as additional arguments" do
+ r = nil
+ lambda {
+ r = Regexp.send @method, /hi/, Regexp::IGNORECASE
+ }.should complain(/flags ignored/)
+ (r.options & Regexp::IGNORECASE).should == 0
+ end
+
+ not_supported_on :opal do
+ it "sets the encoding to UTF-8 if the Regexp literal has the 'u' option" do
+ Regexp.send(@method, /Hi/u).encoding.should == Encoding::UTF_8
+ end
+
+ it "sets the encoding to EUC-JP if the Regexp literal has the 'e' option" do
+ Regexp.send(@method, /Hi/e).encoding.should == Encoding::EUC_JP
+ end
+
+ it "sets the encoding to Windows-31J if the Regexp literal has the 's' option" do
+ Regexp.send(@method, /Hi/s).encoding.should == Encoding::Windows_31J
+ end
+
+ it "sets the encoding to US-ASCII if the Regexp literal has the 'n' option and the source String is ASCII only" 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::ASCII_8BIT
+ end
+ end
+end
diff --git a/spec/ruby/core/regexp/shared/new_ascii_8bit.rb b/spec/ruby/core/regexp/shared/new_ascii_8bit.rb
new file mode 100644
index 0000000000..5110a08380
--- /dev/null
+++ b/spec/ruby/core/regexp/shared/new_ascii_8bit.rb
@@ -0,0 +1,553 @@
+# -*- encoding: ascii-8bit -*-
+
+describe :regexp_new_ascii_8bit, shared: true do
+ it "requires one argument and creates a new regular expression object" do
+ Regexp.send(@method, '').is_a?(Regexp).should == true
+ end
+
+ it "works by default for subclasses with overridden #initialize" do
+ class RegexpSpecsSubclass < Regexp
+ def initialize(*args)
+ super
+ @args = args
+ end
+
+ attr_accessor :args
+ end
+
+ class RegexpSpecsSubclassTwo < Regexp; end
+
+ RegexpSpecsSubclass.send(@method, "hi").should be_kind_of(RegexpSpecsSubclass)
+ RegexpSpecsSubclass.send(@method, "hi").args.first.should == "hi"
+
+ RegexpSpecsSubclassTwo.send(@method, "hi").should be_kind_of(RegexpSpecsSubclassTwo)
+ end
+end
+
+describe :regexp_new_string_ascii_8bit, 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$/
+ end
+
+ it "raises a RegexpError when passed an incorrect regexp" do
+ lambda { Regexp.send(@method, "^[$", 0) }.should raise_error(RegexpError)
+ end
+
+ it "does not set Regexp options if only given one argument" do
+ r = Regexp.send(@method, 'Hi')
+ (r.options & Regexp::IGNORECASE).should == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ not_supported_on :opal do
+ (r.options & Regexp::EXTENDED).should == 0
+ end
+ end
+
+ it "does not set Regexp options if second argument is nil or false" do
+ r = Regexp.send(@method, 'Hi', nil)
+ (r.options & Regexp::IGNORECASE).should == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ not_supported_on :opal do
+ (r.options & Regexp::EXTENDED).should == 0
+ end
+
+ r = Regexp.send(@method, 'Hi', false)
+ (r.options & Regexp::IGNORECASE).should == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ not_supported_on :opal do
+ (r.options & Regexp::EXTENDED).should == 0
+ end
+ end
+
+ it "sets options from second argument if it is one of the Fixnum option constants" do
+ r = Regexp.send(@method, 'Hi', Regexp::IGNORECASE)
+ (r.options & Regexp::IGNORECASE).should_not == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ not_supported_on :opal do
+ (r.options & Regexp::EXTENDED).should == 0
+ end
+
+ r = Regexp.send(@method, 'Hi', Regexp::MULTILINE)
+ (r.options & Regexp::IGNORECASE).should == 0
+ (r.options & Regexp::MULTILINE).should_not == 0
+ not_supported_on :opal do
+ (r.options & Regexp::EXTENDED).should == 0
+ end
+
+ not_supported_on :opal do
+ r = Regexp.send(@method, 'Hi', Regexp::EXTENDED)
+ (r.options & Regexp::IGNORECASE).should == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ (r.options & Regexp::EXTENDED).should_not == 1
+ end
+ end
+
+ it "accepts a Fixnum of two or more options ORed together as the second argument" do
+ r = Regexp.send(@method, 'Hi', Regexp::IGNORECASE | Regexp::EXTENDED)
+ (r.options & Regexp::IGNORECASE).should_not == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ (r.options & Regexp::EXTENDED).should_not == 0
+ end
+
+ it "treats any non-Fixnum, non-nil, non-false second argument as IGNORECASE" do
+ r = Regexp.send(@method, 'Hi', Object.new)
+ (r.options & Regexp::IGNORECASE).should_not == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ not_supported_on :opal do
+ (r.options & Regexp::EXTENDED).should == 0
+ end
+ end
+
+ it "ignores the third argument if it is 'e' or 'euc' (case-insensitive)" do
+ lambda {
+ 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
+ lambda {
+ 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
+ lambda {
+ 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 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::ASCII_8BIT
+ Regexp.send(@method, str, nil, 'n').encoding.should == Encoding::ASCII_8BIT
+ Regexp.send(@method, str, nil, 'none').encoding.should == Encoding::ASCII_8BIT
+ Regexp.send(@method, str, nil, 'NONE').encoding.should == Encoding::ASCII_8BIT
+ end
+
+ describe "with escaped characters" do
+ it "raises a Regexp error if there is a trailing backslash" do
+ lambda { Regexp.send(@method, "\\") }.should raise_error(RegexpError)
+ end
+
+ it "accepts a backspace followed by a character" do
+ Regexp.send(@method, "\\N").should == /#{"\x5c"+"N"}/
+ end
+
+ it "accepts a one-digit octal value" do
+ Regexp.send(@method, "\0").should == /#{"\x00"}/
+ end
+
+ it "accepts a two-digit octal value" do
+ Regexp.send(@method, "\11").should == /#{"\x09"}/
+ end
+
+ it "accepts a three-digit octal value" do
+ Regexp.send(@method, "\315").should == /#{"\xcd"}/
+ end
+
+ it "interprets a digit following a three-digit octal value as a character" do
+ Regexp.send(@method, "\3762").should == /#{"\xfe2"}/
+ end
+
+ it "accepts a one-digit hexadecimal value" do
+ Regexp.send(@method, "\x9n").should == /#{"\x09n"}/
+ end
+
+ it "accepts a two-digit hexadecimal value" do
+ Regexp.send(@method, "\x23").should == /#{"\x23"}/
+ end
+
+ it "interprets a digit following a two-digit hexadecimal value as a character" do
+ Regexp.send(@method, "\x420").should == /#{"\x420"}/
+ end
+
+ it "raises a RegexpError if \\x is not followed by any hexadecimal digits" do
+ lambda { Regexp.send(@method, "\\" + "xn") }.should raise_error(RegexpError)
+ end
+
+ it "accepts an escaped string interpolation" do
+ Regexp.send(@method, "\#{abc}").should == /#{"\#{abc}"}/
+ end
+
+ it "accepts '\\n'" do
+ Regexp.send(@method, "\n").should == /#{"\x0a"}/
+ end
+
+ it "accepts '\\t'" do
+ Regexp.send(@method, "\t").should == /#{"\x09"}/
+ end
+
+ it "accepts '\\r'" do
+ Regexp.send(@method, "\r").should == /#{"\x0d"}/
+ end
+
+ it "accepts '\\f'" do
+ Regexp.send(@method, "\f").should == /#{"\x0c"}/
+ end
+
+ it "accepts '\\v'" do
+ Regexp.send(@method, "\v").should == /#{"\x0b"}/
+ end
+
+ it "accepts '\\a'" do
+ Regexp.send(@method, "\a").should == /#{"\x07"}/
+ end
+
+ it "accepts '\\e'" do
+ Regexp.send(@method, "\e").should == /#{"\x1b"}/
+ end
+
+ it "accepts '\\C-\\n'" do
+ Regexp.send(@method, "\C-\n").should == /#{"\x0a"}/
+ end
+
+ it "accepts '\\C-\\t'" do
+ Regexp.send(@method, "\C-\t").should == /#{"\x09"}/
+ end
+
+ it "accepts '\\C-\\r'" do
+ Regexp.send(@method, "\C-\r").should == /#{"\x0d"}/
+ end
+
+ it "accepts '\\C-\\f'" do
+ Regexp.send(@method, "\C-\f").should == /#{"\x0c"}/
+ end
+
+ it "accepts '\\C-\\v'" do
+ Regexp.send(@method, "\C-\v").should == /#{"\x0b"}/
+ end
+
+ it "accepts '\\C-\\a'" do
+ Regexp.send(@method, "\C-\a").should == /#{"\x07"}/
+ end
+
+ it "accepts '\\C-\\e'" do
+ Regexp.send(@method, "\C-\e").should == /#{"\x1b"}/
+ end
+
+ it "accepts '\\c\\n'" do
+ Regexp.send(@method, "\C-\n").should == /#{"\x0a"}/
+ end
+
+ it "accepts '\\c\\t'" do
+ Regexp.send(@method, "\C-\t").should == /#{"\x09"}/
+ end
+
+ it "accepts '\\c\\r'" do
+ Regexp.send(@method, "\C-\r").should == /#{"\x0d"}/
+ end
+
+ it "accepts '\\c\\f'" do
+ Regexp.send(@method, "\C-\f").should == /#{"\x0c"}/
+ end
+
+ it "accepts '\\c\\v'" do
+ Regexp.send(@method, "\C-\v").should == /#{"\x0b"}/
+ end
+
+ it "accepts '\\c\\a'" do
+ Regexp.send(@method, "\C-\a").should == /#{"\x07"}/
+ end
+
+ it "accepts '\\c\\e'" do
+ Regexp.send(@method, "\C-\e").should == /#{"\x1b"}/
+ end
+
+ it "accepts '\\M-\\n'" do
+ Regexp.send(@method, "\M-\n").should == /#{"\x8a"}/
+ end
+
+ it "accepts '\\M-\\t'" do
+ Regexp.send(@method, "\M-\t").should == /#{"\x89"}/
+ end
+
+ it "accepts '\\M-\\r'" do
+ Regexp.send(@method, "\M-\r").should == /#{"\x8d"}/
+ end
+
+ it "accepts '\\M-\\f'" do
+ Regexp.send(@method, "\M-\f").should == /#{"\x8c"}/
+ end
+
+ it "accepts '\\M-\\v'" do
+ Regexp.send(@method, "\M-\v").should == /#{"\x8b"}/
+ end
+
+ it "accepts '\\M-\\a'" do
+ Regexp.send(@method, "\M-\a").should == /#{"\x87"}/
+ end
+
+ it "accepts '\\M-\\e'" do
+ Regexp.send(@method, "\M-\e").should == /#{"\x9b"}/
+ end
+
+ it "accepts '\\M-\\C-\\n'" do
+ Regexp.send(@method, "\M-\n").should == /#{"\x8a"}/
+ end
+
+ it "accepts '\\M-\\C-\\t'" do
+ Regexp.send(@method, "\M-\t").should == /#{"\x89"}/
+ end
+
+ it "accepts '\\M-\\C-\\r'" do
+ Regexp.send(@method, "\M-\r").should == /#{"\x8d"}/
+ end
+
+ it "accepts '\\M-\\C-\\f'" do
+ Regexp.send(@method, "\M-\f").should == /#{"\x8c"}/
+ end
+
+ it "accepts '\\M-\\C-\\v'" do
+ Regexp.send(@method, "\M-\v").should == /#{"\x8b"}/
+ end
+
+ it "accepts '\\M-\\C-\\a'" do
+ Regexp.send(@method, "\M-\a").should == /#{"\x87"}/
+ end
+
+ it "accepts '\\M-\\C-\\e'" do
+ Regexp.send(@method, "\M-\e").should == /#{"\x9b"}/
+ end
+
+ it "accepts '\\M-\\c\\n'" do
+ Regexp.send(@method, "\M-\n").should == /#{"\x8a"}/
+ end
+
+ it "accepts '\\M-\\c\\t'" do
+ Regexp.send(@method, "\M-\t").should == /#{"\x89"}/
+ end
+
+ it "accepts '\\M-\\c\\r'" do
+ Regexp.send(@method, "\M-\r").should == /#{"\x8d"}/
+ end
+
+ it "accepts '\\M-\\c\\f'" do
+ Regexp.send(@method, "\M-\f").should == /#{"\x8c"}/
+ end
+
+ it "accepts '\\M-\\c\\v'" do
+ Regexp.send(@method, "\M-\v").should == /#{"\x8b"}/
+ end
+
+ it "accepts '\\M-\\c\\a'" do
+ Regexp.send(@method, "\M-\a").should == /#{"\x87"}/
+ end
+
+ it "accepts '\\M-\\c\\e'" do
+ Regexp.send(@method, "\M-\e").should == /#{"\x9b"}/
+ end
+
+ it "accepts multiple consecutive '\\' characters" do
+ Regexp.send(@method, "\\\\\\N").should == /#{"\\\\\\"+"N"}/
+ end
+
+ it "accepts characters and escaped octal digits" do
+ Regexp.send(@method, "abc\076").should == /#{"abc\x3e"}/
+ end
+
+ it "accepts escaped octal digits and characters" do
+ Regexp.send(@method, "\076abc").should == /#{"\x3eabc"}/
+ end
+
+ it "accepts characters and escaped hexadecimal digits" do
+ Regexp.send(@method, "abc\x42").should == /#{"abc\x42"}/
+ end
+
+ it "accepts escaped hexadecimal digits and characters" do
+ Regexp.send(@method, "\x3eabc").should == /#{"\x3eabc"}/
+ end
+
+ it "accepts escaped hexadecimal and octal digits" do
+ Regexp.send(@method, "\061\x42").should == /#{"\x31\x42"}/
+ end
+
+ it "accepts \\u{H} for a single Unicode codepoint" do
+ Regexp.send(@method, "\u{f}").should == /#{"\x0f"}/
+ end
+
+ it "accepts \\u{HH} for a single Unicode codepoint" do
+ Regexp.send(@method, "\u{7f}").should == /#{"\x7f"}/
+ end
+
+ it "accepts \\u{HHH} for a single Unicode codepoint" do
+ Regexp.send(@method, "\u{07f}").should == /#{"\x7f"}/
+ end
+
+ it "accepts \\u{HHHH} for a single Unicode codepoint" do
+ Regexp.send(@method, "\u{0000}").should == /#{"\x00"}/
+ end
+
+ it "accepts \\u{HHHHH} for a single Unicode codepoint" do
+ Regexp.send(@method, "\u{00001}").should == /#{"\x01"}/
+ end
+
+ it "accepts \\u{HHHHHH} for a single Unicode codepoint" do
+ Regexp.send(@method, "\u{000000}").should == /#{"\x00"}/
+ end
+
+ it "accepts characters followed by \\u{HHHH}" do
+ Regexp.send(@method, "abc\u{3042}").should == /#{"abc\u3042"}/
+ end
+
+ it "accepts \\u{HHHH} followed by characters" do
+ Regexp.send(@method, "\u{3042}abc").should == /#{"\u3042abc"}/
+ end
+
+ it "accepts escaped hexadecimal digits followed by \\u{HHHH}" do
+ Regexp.send(@method, "\x42\u{3042}").should == /#{"\x42\u3042"}/
+ end
+
+ it "accepts escaped octal digits followed by \\u{HHHH}" do
+ Regexp.send(@method, "\056\u{3042}").should == /#{"\x2e\u3042"}/
+ end
+
+ it "accepts a combination of escaped octal and hexadecimal digits and \\u{HHHH}" do
+ Regexp.send(@method, "\056\x42\u{3042}\x52\076").should == /#{"\x2e\x42\u3042\x52\x3e"}/
+ end
+
+ it "accepts \\uHHHH for a single Unicode codepoint" do
+ Regexp.send(@method, "\u3042").should == /#{"\u3042"}/
+ end
+
+ it "accepts characters followed by \\uHHHH" do
+ Regexp.send(@method, "abc\u3042").should == /#{"abc\u3042"}/
+ end
+
+ it "accepts \\uHHHH followed by characters" do
+ Regexp.send(@method, "\u3042abc").should == /#{"\u3042abc"}/
+ end
+
+ it "accepts escaped hexadecimal digits followed by \\uHHHH" do
+ Regexp.send(@method, "\x42\u3042").should == /#{"\x42\u3042"}/
+ end
+
+ it "accepts escaped octal digits followed by \\uHHHH" do
+ Regexp.send(@method, "\056\u3042").should == /#{"\x2e\u3042"}/
+ end
+
+ it "accepts a combination of escaped octal and hexadecimal digits and \\uHHHH" do
+ Regexp.send(@method, "\056\x42\u3042\x52\076").should == /#{"\x2e\x42\u3042\x52\x3e"}/
+ end
+
+ it "raises a RegexpError if less than four digits are given for \\uHHHH" do
+ lambda { Regexp.send(@method, "\\" + "u304") }.should raise_error(RegexpError)
+ end
+
+ it "raises a RegexpError if the \\u{} escape is empty" do
+ lambda { Regexp.send(@method, "\\" + "u{}") }.should raise_error(RegexpError)
+ end
+
+ it "raises a RegexpError if more than six hexadecimal digits are given" do
+ lambda { Regexp.send(@method, "\\" + "u{0ffffff}") }.should raise_error(RegexpError)
+ end
+
+ it "returns a Regexp with US-ASCII encoding if only 7-bit ASCII characters are present regardless of the input String's encoding" do
+ Regexp.send(@method, "abc").encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns a Regexp with source String having US-ASCII encoding if only 7-bit ASCII characters are present regardless of the input String's encoding" do
+ Regexp.send(@method, "abc").source.encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns a Regexp with US-ASCII encoding if UTF-8 escape sequences using only 7-bit ASCII are present" do
+ Regexp.send(@method, "\u{61}").encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns a Regexp with source String having US-ASCII encoding if UTF-8 escape sequences using only 7-bit ASCII are present" do
+ Regexp.send(@method, "\u{61}").source.encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns a Regexp with UTF-8 encoding if any UTF-8 escape sequences outside 7-bit ASCII are present" do
+ Regexp.send(@method, "\u{ff}").encoding.should == Encoding::UTF_8
+ end
+
+ it "returns a Regexp with source String having UTF-8 encoding if any UTF-8 escape sequences outside 7-bit ASCII are present" do
+ Regexp.send(@method, "\u{ff}").source.encoding.should == Encoding::UTF_8
+ end
+
+ it "returns a Regexp with the input String's encoding" do
+ str = "\x82\xa0".force_encoding(Encoding::Shift_JIS)
+ Regexp.send(@method, str).encoding.should == Encoding::Shift_JIS
+ end
+
+ it "returns a Regexp with source String having the input String's encoding" do
+ str = "\x82\xa0".force_encoding(Encoding::Shift_JIS)
+ Regexp.send(@method, str).source.encoding.should == Encoding::Shift_JIS
+ end
+ end
+end
+
+describe :regexp_new_regexp_ascii_8bit, shared: true do
+ it "uses the argument as a literal to construct a Regexp object" do
+ Regexp.send(@method, /^hi{2,3}fo.o$/).should == /^hi{2,3}fo.o$/
+ end
+
+ it "preserves any options given in the Regexp literal" do
+ (Regexp.send(@method, /Hi/i).options & Regexp::IGNORECASE).should_not == 0
+ (Regexp.send(@method, /Hi/m).options & Regexp::MULTILINE).should_not == 0
+ not_supported_on :opal do
+ (Regexp.send(@method, /Hi/x).options & Regexp::EXTENDED).should_not == 0
+ end
+
+ not_supported_on :opal do
+ r = Regexp.send @method, /Hi/imx
+ (r.options & Regexp::IGNORECASE).should_not == 0
+ (r.options & Regexp::MULTILINE).should_not == 0
+ (r.options & Regexp::EXTENDED).should_not == 0
+ end
+
+ r = Regexp.send @method, /Hi/
+ (r.options & Regexp::IGNORECASE).should == 0
+ (r.options & Regexp::MULTILINE).should == 0
+ not_supported_on :opal do
+ (r.options & Regexp::EXTENDED).should == 0
+ end
+ end
+
+ it "does not honour options given as additional arguments" do
+ r = nil
+ lambda {
+ r = Regexp.send @method, /hi/, Regexp::IGNORECASE
+ }.should complain(/flags ignored/)
+ (r.options & Regexp::IGNORECASE).should == 0
+ end
+
+ not_supported_on :opal do
+ it "sets the encoding to UTF-8 if the Regexp literal has the 'u' option" do
+ Regexp.send(@method, /Hi/u).encoding.should == Encoding::UTF_8
+ end
+
+ it "sets the encoding to EUC-JP if the Regexp literal has the 'e' option" do
+ Regexp.send(@method, /Hi/e).encoding.should == Encoding::EUC_JP
+ end
+
+ it "sets the encoding to Windows-31J if the Regexp literal has the 's' option" do
+ Regexp.send(@method, /Hi/s).encoding.should == Encoding::Windows_31J
+ end
+
+ it "sets the encoding to US-ASCII if the Regexp literal has the 'n' option and the source String is ASCII only" 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::ASCII_8BIT
+ end
+ end
+end
diff --git a/spec/ruby/core/regexp/shared/quote.rb b/spec/ruby/core/regexp/shared/quote.rb
new file mode 100644
index 0000000000..016cb0b164
--- /dev/null
+++ b/spec/ruby/core/regexp/shared/quote.rb
@@ -0,0 +1,31 @@
+# -*- encoding: ascii-8bit -*-
+
+describe :regexp_quote, shared: true do
+ it "escapes any characters with special meaning in a regular expression" do
+ Regexp.send(@method, '\*?{}.+^[]()- ').should == '\\\\\*\?\{\}\.\+\^\[\]\(\)\-\\ '
+ Regexp.send(@method, "\*?{}.+^[]()- ").should == '\\*\\?\\{\\}\\.\\+\\^\\[\\]\\(\\)\\-\\ '
+ Regexp.send(@method, '\n\r\f\t').should == '\\\\n\\\\r\\\\f\\\\t'
+ Regexp.send(@method, "\n\r\f\t").should == '\\n\\r\\f\\t'
+ end
+
+ it "works with symbols" do
+ Regexp.send(@method, :symbol).should == 'symbol'
+ 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
+ end
+
+ it "sets the encoding of the result to the encoding of the String if any non-US-ASCII characters are present in an input String with valid encoding" do
+ str = "ã‚りãŒã¨ã†".force_encoding("utf-8")
+ str.valid_encoding?.should be_true
+ Regexp.send(@method, str).encoding.should == Encoding::UTF_8
+ end
+
+ it "sets the encoding of the result to ASCII-8BIT if any non-US-ASCII characters are present in an input String with invalid encoding" do
+ str = "\xff".force_encoding "us-ascii"
+ str.valid_encoding?.should be_false
+ Regexp.send(@method, "\xff").encoding.should == Encoding::ASCII_8BIT
+ end
+end
diff --git a/spec/ruby/core/regexp/source_spec.rb b/spec/ruby/core/regexp/source_spec.rb
new file mode 100644
index 0000000000..709fee49b3
--- /dev/null
+++ b/spec/ruby/core/regexp/source_spec.rb
@@ -0,0 +1,29 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe "Regexp#source" do
+ it "returns the original string of the pattern" do
+ not_supported_on :opal do
+ /ab+c/ix.source.should == "ab+c"
+ end
+ /x(.)xz/.source.should == "x(.)xz"
+ end
+
+ it "will remove escape characters" do
+ /foo\/bar/.source.should == "foo/bar"
+ end
+
+ not_supported_on :opal do
+ it "has US-ASCII encoding when created from an ASCII-only \\u{} literal" do
+ re = /[\u{20}-\u{7E}]/
+ re.source.encoding.should equal(Encoding::US_ASCII)
+ end
+ end
+
+ not_supported_on :opal do
+ it "has UTF-8 encoding when created from a non-ASCII-only \\u{} literal" do
+ re = /[\u{20}-\u{7EE}]/
+ re.source.encoding.should equal(Encoding::UTF_8)
+ end
+ end
+end
diff --git a/spec/ruby/core/regexp/to_s_spec.rb b/spec/ruby/core/regexp/to_s_spec.rb
new file mode 100644
index 0000000000..798eaee6c2
--- /dev/null
+++ b/spec/ruby/core/regexp/to_s_spec.rb
@@ -0,0 +1,62 @@
+require_relative '../../spec_helper'
+
+describe "Regexp#to_s" do
+ not_supported_on :opal do
+ it "displays options if included" do
+ /abc/mxi.to_s.should == "(?mix:abc)"
+ end
+ end
+
+ it "shows non-included options after a - sign" do
+ /abc/i.to_s.should == "(?i-mx:abc)"
+ end
+
+ it "shows all options as excluded if none are selected" do
+ /abc/.to_s.should == "(?-mix:abc)"
+ end
+
+ it "shows the pattern after the options" do
+ not_supported_on :opal do
+ /ab+c/mix.to_s.should == "(?mix:ab+c)"
+ end
+ /xyz/.to_s.should == "(?-mix:xyz)"
+ end
+
+ not_supported_on :opal do
+ it "displays groups with options" do
+ /(?ix:foo)(?m:bar)/.to_s.should == "(?-mix:(?ix:foo)(?m:bar))"
+ /(?ix:foo)bar/m.to_s.should == "(?m-ix:(?ix:foo)bar)"
+ end
+
+ it "displays single group with same options as main regex as the main regex" do
+ /(?i:nothing outside this group)/.to_s.should == "(?i-mx:nothing outside this group)"
+ end
+ end
+
+ not_supported_on :opal do
+ it "deals properly with uncaptured groups" do
+ /whatever(?:0d)/ix.to_s.should == "(?ix-m:whatever(?:0d))"
+ end
+ end
+
+ it "deals properly with the two types of lookahead groups" do
+ /(?=5)/.to_s.should == "(?-mix:(?=5))"
+ /(?!5)/.to_s.should == "(?-mix:(?!5))"
+ end
+
+ it "returns a string in (?xxx:yyy) notation" do
+ not_supported_on :opal do
+ /ab+c/ix.to_s.should == "(?ix-m:ab+c)"
+ /jis/s.to_s.should == "(?-mix:jis)"
+ /(?i:.)/.to_s.should == "(?i-mx:.)"
+ end
+ /(?:.)/.to_s.should == "(?-mix:.)"
+ end
+
+ not_supported_on :opal do
+ it "handles abusive option groups" do
+ /(?mmmmix-miiiix:)/.to_s.should == '(?-mix:)'
+ end
+ end
+
+end
diff --git a/spec/ruby/core/regexp/try_convert_spec.rb b/spec/ruby/core/regexp/try_convert_spec.rb
new file mode 100644
index 0000000000..be567e2130
--- /dev/null
+++ b/spec/ruby/core/regexp/try_convert_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+
+describe "Regexp.try_convert" do
+ not_supported_on :opal do
+ it "returns the argument if given a Regexp" do
+ Regexp.try_convert(/foo/s).should == /foo/s
+ end
+ end
+
+ it "returns nil if given an argument that can't be converted to a Regexp" do
+ ['', 'glark', [], Object.new, :pat].each do |arg|
+ Regexp.try_convert(arg).should be_nil
+ end
+ end
+
+ it "tries to coerce the argument by calling #to_regexp" do
+ rex = mock('regexp')
+ rex.should_receive(:to_regexp).and_return(/(p(a)t[e]rn)/)
+ Regexp.try_convert(rex).should == /(p(a)t[e]rn)/
+ end
+end
diff --git a/spec/ruby/core/regexp/union_spec.rb b/spec/ruby/core/regexp/union_spec.rb
new file mode 100644
index 0000000000..25901c6f2b
--- /dev/null
+++ b/spec/ruby/core/regexp/union_spec.rb
@@ -0,0 +1,149 @@
+# encoding: utf-8
+
+require_relative '../../spec_helper'
+
+describe "Regexp.union" do
+ it "returns /(?!)/ when passed no arguments" do
+ Regexp.union.should == /(?!)/
+ end
+
+ it "returns a regular expression that will match passed arguments" do
+ Regexp.union("penzance").should == /penzance/
+ Regexp.union("skiing", "sledding").should == /skiing|sledding/
+ not_supported_on :opal do
+ Regexp.union(/dogs/, /cats/i).should == /(?-mix:dogs)|(?i-mx:cats)/
+ end
+ end
+
+ it "quotes any string arguments" do
+ Regexp.union("n", ".").should == /n|\./
+ end
+
+ it "returns a Regexp with the encoding of an ASCII-incompatible String argument" do
+ Regexp.union("a".encode("UTF-16LE")).encoding.should == Encoding::UTF_16LE
+ end
+
+ it "returns a Regexp with the encoding of a String containing non-ASCII-compatible characters" do
+ Regexp.union("\u00A9".encode("ISO-8859-1")).encoding.should == Encoding::ISO_8859_1
+ end
+
+ it "returns a Regexp with US-ASCII encoding if all arguments are ASCII-only" do
+ Regexp.union("a".encode("UTF-8"), "b".encode("SJIS")).encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns a Regexp with the encoding of multiple non-conflicting ASCII-incompatible String arguments" do
+ Regexp.union("a".encode("UTF-16LE"), "b".encode("UTF-16LE")).encoding.should == Encoding::UTF_16LE
+ end
+
+ it "returns a Regexp with the encoding of multiple non-conflicting Strings containing non-ASCII-compatible characters" do
+ Regexp.union("\u00A9".encode("ISO-8859-1"), "\u00B0".encode("ISO-8859-1")).encoding.should == Encoding::ISO_8859_1
+ end
+
+ it "returns a Regexp with the encoding of a String containing non-ASCII-compatible characters and another ASCII-only String" do
+ Regexp.union("\u00A9".encode("ISO-8859-1"), "a".encode("UTF-8")).encoding.should == Encoding::ISO_8859_1
+ end
+
+ it "returns a Regexp with UTF-8 if one part is UTF-8" do
+ Regexp.union(/probl[éeè]me/i, /help/i).encoding.should == Encoding::UTF_8
+ end
+
+ it "returns a Regexp if an array of string with special characters is passed" do
+ Regexp.union(["+","-"]).should == /\+|\-/
+ end
+
+ it "raises ArgumentError if the arguments include conflicting ASCII-incompatible Strings" do
+ lambda {
+ Regexp.union("a".encode("UTF-16LE"), "b".encode("UTF-16BE"))
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the arguments include conflicting ASCII-incompatible Regexps" do
+ lambda {
+ Regexp.union(Regexp.new("a".encode("UTF-16LE")),
+ Regexp.new("b".encode("UTF-16BE")))
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the arguments include conflicting fixed encoding Regexps" do
+ lambda {
+ Regexp.union(Regexp.new("a".encode("UTF-8"), Regexp::FIXEDENCODING),
+ Regexp.new("b".encode("US-ASCII"), Regexp::FIXEDENCODING))
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the arguments include a fixed encoding Regexp and a String containing non-ASCII-compatible characters in a different encoding" do
+ lambda {
+ Regexp.union(Regexp.new("a".encode("UTF-8"), Regexp::FIXEDENCODING),
+ "\u00A9".encode("ISO-8859-1"))
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the arguments include a String containing non-ASCII-compatible characters and a fixed encoding Regexp in a different encoding" do
+ lambda {
+ Regexp.union("\u00A9".encode("ISO-8859-1"),
+ Regexp.new("a".encode("UTF-8"), Regexp::FIXEDENCODING))
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the arguments include an ASCII-incompatible String and an ASCII-only String" do
+ lambda {
+ Regexp.union("a".encode("UTF-16LE"), "b".encode("UTF-8"))
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the arguments include an ASCII-incompatible Regexp and an ASCII-only String" do
+ lambda {
+ Regexp.union(Regexp.new("a".encode("UTF-16LE")), "b".encode("UTF-8"))
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the arguments include an ASCII-incompatible String and an ASCII-only Regexp" do
+ lambda {
+ Regexp.union("a".encode("UTF-16LE"), Regexp.new("b".encode("UTF-8")))
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the arguments include an ASCII-incompatible Regexp and an ASCII-only Regexp" do
+ lambda {
+ Regexp.union(Regexp.new("a".encode("UTF-16LE")), Regexp.new("b".encode("UTF-8")))
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the arguments include an ASCII-incompatible String and a String containing non-ASCII-compatible characters in a different encoding" do
+ lambda {
+ Regexp.union("a".encode("UTF-16LE"), "\u00A9".encode("ISO-8859-1"))
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the arguments include an ASCII-incompatible Regexp and a String containing non-ASCII-compatible characters in a different encoding" do
+ lambda {
+ Regexp.union(Regexp.new("a".encode("UTF-16LE")), "\u00A9".encode("ISO-8859-1"))
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the arguments include an ASCII-incompatible String and a Regexp containing non-ASCII-compatible characters in a different encoding" do
+ lambda {
+ Regexp.union("a".encode("UTF-16LE"), Regexp.new("\u00A9".encode("ISO-8859-1")))
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the arguments include an ASCII-incompatible Regexp and a Regexp containing non-ASCII-compatible characters in a different encoding" do
+ lambda {
+ Regexp.union(Regexp.new("a".encode("UTF-16LE")), Regexp.new("\u00A9".encode("ISO-8859-1")))
+ }.should raise_error(ArgumentError)
+ end
+
+ it "uses to_str to convert arguments (if not Regexp)" do
+ obj = mock('pattern')
+ obj.should_receive(:to_str).and_return('foo')
+ Regexp.union(obj, "bar").should == /foo|bar/
+ end
+
+ it "accepts a single array of patterns as arguments" do
+ Regexp.union(["skiing", "sledding"]).should == /skiing|sledding/
+ not_supported_on :opal do
+ Regexp.union([/dogs/, /cats/i]).should == /(?-mix:dogs)|(?i-mx:cats)/
+ end
+ lambda{Regexp.union(["skiing", "sledding"], [/dogs/, /cats/i])}.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/signal/fixtures/trap_all.rb b/spec/ruby/core/signal/fixtures/trap_all.rb
new file mode 100644
index 0000000000..b2e85df247
--- /dev/null
+++ b/spec/ruby/core/signal/fixtures/trap_all.rb
@@ -0,0 +1,8 @@
+reserved_signals = ARGV
+
+(Signal.list.keys - reserved_signals).each do |signal|
+ Signal.trap(signal, -> {})
+ Signal.trap(signal, "DEFAULT")
+end
+
+puts "OK"
diff --git a/spec/ruby/core/signal/list_spec.rb b/spec/ruby/core/signal/list_spec.rb
new file mode 100644
index 0000000000..56ad6828fe
--- /dev/null
+++ b/spec/ruby/core/signal/list_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../spec_helper'
+
+describe "Signal.list" do
+ RUBY_SIGNALS = %w{
+ EXIT
+ HUP
+ INT
+ QUIT
+ ILL
+ TRAP
+ IOT
+ ABRT
+ EMT
+ FPE
+ KILL
+ BUS
+ SEGV
+ SYS
+ PIPE
+ ALRM
+ TERM
+ URG
+ STOP
+ TSTP
+ CONT
+ CHLD
+ CLD
+ TTIN
+ TTOU
+ IO
+ XCPU
+ XFSZ
+ VTALRM
+ PROF
+ WINCH
+ USR1
+ USR2
+ LOST
+ MSG
+ PWR
+ POLL
+ DANGER
+ MIGRATE
+ PRE
+ GRANT
+ RETRACT
+ SOUND
+ INFO
+ }
+
+ it "doesn't contain other signals than the known list" do
+ (Signal.list.keys - RUBY_SIGNALS).should == []
+ end
+
+ if Signal.list["CHLD"]
+ it "redefines CLD with CHLD if defined" do
+ Signal.list["CLD"].should == Signal.list["CHLD"]
+ end
+ end
+
+ it "includes the EXIT key with a value of zero" do
+ Signal.list["EXIT"].should == 0
+ end
+
+ it "includes the KILL key with a value of nine" do
+ Signal.list["KILL"].should == 9
+ end
+end
diff --git a/spec/ruby/core/signal/signame_spec.rb b/spec/ruby/core/signal/signame_spec.rb
new file mode 100644
index 0000000000..71b69b301a
--- /dev/null
+++ b/spec/ruby/core/signal/signame_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+
+describe "Signal.signame" do
+ it "takes a signal name with a well known signal number" do
+ Signal.signame(0).should == "EXIT"
+ end
+
+ it "returns nil if the argument is an invalid signal number" do
+ Signal.signame(-1).should == nil
+ end
+
+ it "raises a TypeError when the passed argument can't be coerced to Integer" do
+ lambda { Signal.signame("hello") }.should raise_error(TypeError)
+ end
+
+ platform_is_not :windows do
+ it "the original should take precendence over alias when looked up by number" do
+ Signal.signame(Signal.list["ABRT"]).should == "ABRT"
+ Signal.signame(Signal.list["CHLD"]).should == "CHLD"
+ end
+ end
+end
diff --git a/spec/ruby/core/signal/trap_spec.rb b/spec/ruby/core/signal/trap_spec.rb
new file mode 100644
index 0000000000..c11c900add
--- /dev/null
+++ b/spec/ruby/core/signal/trap_spec.rb
@@ -0,0 +1,203 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ describe "Signal.trap" do
+ before :each do
+ ScratchPad.clear
+
+ @proc = lambda { ScratchPad.record :proc_trap }
+ @saved_trap = Signal.trap(:HUP, @proc)
+ end
+
+ after :each do
+ Signal.trap(:HUP, @saved_trap) if @saved_trap
+ end
+
+ it "returns the previous handler" do
+ Signal.trap(:HUP, @saved_trap).should equal(@proc)
+ end
+
+ it "accepts a block in place of a proc/command argument" do
+ done = false
+
+ Signal.trap(:HUP) do
+ ScratchPad.record :block_trap
+ done = true
+ end
+
+ Process.kill :HUP, Process.pid
+ Thread.pass until done
+
+ ScratchPad.recorded.should == :block_trap
+ end
+
+ it "is possible to create a new Thread when the handler runs" do
+ done = false
+
+ Signal.trap(:HUP) do
+ thr = Thread.new { }
+ thr.join
+ ScratchPad.record(thr.group == Thread.main.group)
+
+ done = true
+ end
+
+ Process.kill :HUP, Process.pid
+ Thread.pass until done
+
+ ScratchPad.recorded.should be_true
+ end
+
+ it "ignores the signal when passed nil" do
+ Signal.trap :HUP, nil
+ Signal.trap(:HUP, @saved_trap).should be_nil
+ end
+
+ it "accepts 'DEFAULT' as a symbol in place of a proc" do
+ Signal.trap :HUP, :DEFAULT
+ Signal.trap(:HUP, :DEFAULT).should == "DEFAULT"
+ end
+
+ it "accepts 'SIG_DFL' as a symbol in place of a proc" do
+ Signal.trap :HUP, :SIG_DFL
+ Signal.trap(:HUP, :SIG_DFL).should == "DEFAULT"
+ end
+
+ it "accepts 'SIG_IGN' as a symbol in place of a proc" do
+ Signal.trap :HUP, :SIG_IGN
+ Signal.trap(:HUP, :SIG_IGN).should == "IGNORE"
+ end
+
+ it "accepts 'IGNORE' as a symbol in place of a proc" do
+ Signal.trap :HUP, :IGNORE
+ Signal.trap(:HUP, :IGNORE).should == "IGNORE"
+ end
+
+ it "accepts long names as Strings" do
+ Signal.trap "SIGHUP", @proc
+ Signal.trap("SIGHUP", @saved_trap).should equal(@proc)
+ end
+
+ it "acceps short names as Strings" do
+ Signal.trap "HUP", @proc
+ Signal.trap("HUP", @saved_trap).should equal(@proc)
+ end
+
+ it "accepts long names as Symbols" do
+ Signal.trap :SIGHUP, @proc
+ Signal.trap(:SIGHUP, @saved_trap).should equal(@proc)
+ end
+
+ it "accepts short names as Symbols" do
+ Signal.trap :HUP, @proc
+ Signal.trap(:HUP, @saved_trap).should equal(@proc)
+ end
+
+ it "accepts 'SIG_DFL' in place of a proc" do
+ Signal.trap :HUP, "SIG_DFL"
+ Signal.trap(:HUP, @saved_trap).should == "DEFAULT"
+ end
+
+ it "accepts 'DEFAULT' in place of a proc" do
+ Signal.trap :HUP, "DEFAULT"
+ Signal.trap(:HUP, @saved_trap).should == "DEFAULT"
+ end
+
+ it "accepts 'SIG_IGN' in place of a proc" do
+ Signal.trap :HUP, "SIG_IGN"
+ Signal.trap(:HUP, "SIG_IGN").should == "IGNORE"
+ end
+
+ it "accepts 'IGNORE' in place of a proc" do
+ Signal.trap :HUP, "IGNORE"
+ Signal.trap(:HUP, "IGNORE").should == "IGNORE"
+ end
+ end
+
+ describe "Signal.trap" do
+ cannot_be_trapped = %w[KILL STOP] # See man 2 signal
+ reserved_signals = %w[VTALRM]
+
+ if PlatformGuard.implementation?(:ruby)
+ reserved_signals += %w[SEGV ILL FPE BUS]
+ end
+
+ if PlatformGuard.implementation?(:truffleruby)
+ if !TruffleRuby.native?
+ reserved_signals += %w[SEGV ILL FPE USR1 QUIT]
+ end
+ end
+
+ if PlatformGuard.implementation?(:jruby)
+ reserved_signals += %w[SEGV ILL FPE BUS USR1 QUIT]
+ end
+
+ cannot_be_trapped.each do |signal|
+ it "raises ArgumentError or Errno::EINVAL for SIG#{signal}" do
+ -> {
+ trap(signal, -> {})
+ }.should raise_error(StandardError) { |e|
+ [ArgumentError, Errno::EINVAL].should include(e.class)
+ e.message.should =~ /Invalid argument|Signal already used by VM or OS/
+ }
+ end
+ end
+
+ reserved_signals.each do |signal|
+ it "raises ArgumentError for reserved signal: SIG#{signal}" do
+ -> {
+ trap(signal, -> {})
+ }.should raise_error(ArgumentError, /can't trap reserved signal|Signal already used by VM or OS/)
+ end
+ end
+
+ it "allows to register a handler for all known signals, except reserved signals" do
+ excluded = cannot_be_trapped + reserved_signals
+ out = ruby_exe(fixture(__FILE__, "trap_all.rb"), args: [*excluded, "2>&1"])
+ out.should == "OK\n"
+ $?.exitstatus.should == 0
+ end
+
+ it "returns 'DEFAULT' for the initial SIGINT handler" do
+ ruby_exe('print trap(:INT) { abort }').should == 'DEFAULT'
+ end
+
+ it "returns SYSTEM_DEFAULT if passed DEFAULT and no handler was ever set" do
+ Signal.trap("PROF", "DEFAULT").should == "SYSTEM_DEFAULT"
+ end
+
+ it "accepts 'SYSTEM_DEFAULT' and uses the OS handler for SIGPIPE" do
+ code = <<-RUBY
+ p Signal.trap('PIPE', 'SYSTEM_DEFAULT')
+ r, w = IO.pipe
+ r.close
+ loop { w.write("a"*1024) }
+ RUBY
+ out = ruby_exe(code)
+ status = $?
+ out.should == "nil\n"
+ status.signaled?.should == true
+ status.termsig.should be_kind_of(Integer)
+ Signal.signame(status.termsig).should == "PIPE"
+ end
+ end
+end
+
+describe "Signal.trap" do
+ describe "the special EXIT signal code" do
+ it "accepts the EXIT code" do
+ code = "trap(:EXIT, proc { print 1 })"
+ ruby_exe(code).should == "1"
+ end
+
+ it "runs the proc before at_exit handlers" do
+ code = "at_exit {print 1}; trap(:EXIT, proc {print 2}); at_exit {print 3}"
+ ruby_exe(code).should == "231"
+ end
+
+ it "can unset the handler" do
+ code = "trap(:EXIT, proc { print 1 }); trap(:EXIT, 'DEFAULT')"
+ ruby_exe(code).should == ""
+ end
+ end
+end
diff --git a/spec/ruby/core/sizedqueue/append_spec.rb b/spec/ruby/core/sizedqueue/append_spec.rb
new file mode 100644
index 0000000000..ff96b46d2c
--- /dev/null
+++ b/spec/ruby/core/sizedqueue/append_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/enque'
+require_relative '../../shared/sizedqueue/enque'
+
+describe "SizedQueue#<<" do
+ it_behaves_like :queue_enq, :<<, -> { SizedQueue.new(10) }
+end
+
+describe "SizedQueue#<<" do
+ it_behaves_like :sizedqueue_enq, :<<, ->(n) { SizedQueue.new(n) }
+end
diff --git a/spec/ruby/core/sizedqueue/clear_spec.rb b/spec/ruby/core/sizedqueue/clear_spec.rb
new file mode 100644
index 0000000000..abae01c6c0
--- /dev/null
+++ b/spec/ruby/core/sizedqueue/clear_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/clear'
+
+describe "SizedQueue#clear" do
+ it_behaves_like :queue_clear, :clear, -> { SizedQueue.new(10) }
+end
diff --git a/spec/ruby/core/sizedqueue/close_spec.rb b/spec/ruby/core/sizedqueue/close_spec.rb
new file mode 100644
index 0000000000..0e0af851cb
--- /dev/null
+++ b/spec/ruby/core/sizedqueue/close_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/close'
+
+describe "SizedQueue#close" do
+ it_behaves_like :queue_close, :close, -> { SizedQueue.new(10) }
+end
diff --git a/spec/ruby/core/sizedqueue/closed_spec.rb b/spec/ruby/core/sizedqueue/closed_spec.rb
new file mode 100644
index 0000000000..4b90da1faa
--- /dev/null
+++ b/spec/ruby/core/sizedqueue/closed_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/closed'
+
+describe "SizedQueue#closed?" do
+ it_behaves_like :queue_closed?, :closed?, -> { SizedQueue.new(10) }
+end
diff --git a/spec/ruby/core/sizedqueue/deq_spec.rb b/spec/ruby/core/sizedqueue/deq_spec.rb
new file mode 100644
index 0000000000..5e1bd9f746
--- /dev/null
+++ b/spec/ruby/core/sizedqueue/deq_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/deque'
+
+describe "SizedQueue#deq" do
+ it_behaves_like :queue_deq, :deq, -> { SizedQueue.new(10) }
+end
diff --git a/spec/ruby/core/sizedqueue/empty_spec.rb b/spec/ruby/core/sizedqueue/empty_spec.rb
new file mode 100644
index 0000000000..9b0d4ff013
--- /dev/null
+++ b/spec/ruby/core/sizedqueue/empty_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/empty'
+
+describe "SizedQueue#empty?" do
+ it_behaves_like :queue_empty?, :empty?, -> { SizedQueue.new(10) }
+end
diff --git a/spec/ruby/core/sizedqueue/enq_spec.rb b/spec/ruby/core/sizedqueue/enq_spec.rb
new file mode 100644
index 0000000000..11c65ec14d
--- /dev/null
+++ b/spec/ruby/core/sizedqueue/enq_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/enque'
+require_relative '../../shared/sizedqueue/enque'
+
+describe "SizedQueue#enq" do
+ it_behaves_like :queue_enq, :enq, -> { SizedQueue.new(10) }
+end
+
+describe "SizedQueue#enq" do
+ it_behaves_like :sizedqueue_enq, :enq, ->(n) { SizedQueue.new(n) }
+end
diff --git a/spec/ruby/core/sizedqueue/length_spec.rb b/spec/ruby/core/sizedqueue/length_spec.rb
new file mode 100644
index 0000000000..b93e7f8997
--- /dev/null
+++ b/spec/ruby/core/sizedqueue/length_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/length'
+
+describe "SizedQueue#length" do
+ it_behaves_like :queue_length, :length, -> { SizedQueue.new(10) }
+end
diff --git a/spec/ruby/core/sizedqueue/max_spec.rb b/spec/ruby/core/sizedqueue/max_spec.rb
new file mode 100644
index 0000000000..b65a67eeb0
--- /dev/null
+++ b/spec/ruby/core/sizedqueue/max_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/sizedqueue/max'
+
+describe "SizedQueue#max" do
+ it_behaves_like :sizedqueue_max, :max, ->(n) { SizedQueue.new(n) }
+end
+
+describe "SizedQueue#max=" do
+ it_behaves_like :sizedqueue_max=, :max=, ->(n) { SizedQueue.new(n) }
+end
diff --git a/spec/ruby/core/sizedqueue/new_spec.rb b/spec/ruby/core/sizedqueue/new_spec.rb
new file mode 100644
index 0000000000..8febbfa63b
--- /dev/null
+++ b/spec/ruby/core/sizedqueue/new_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/sizedqueue/new'
+
+describe "SizedQueue.new" do
+ it_behaves_like :sizedqueue_new, :new, ->(*n) { SizedQueue.new(*n) }
+end
diff --git a/spec/ruby/core/sizedqueue/num_waiting_spec.rb b/spec/ruby/core/sizedqueue/num_waiting_spec.rb
new file mode 100644
index 0000000000..cbbbb2d062
--- /dev/null
+++ b/spec/ruby/core/sizedqueue/num_waiting_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/sizedqueue/num_waiting'
+
+describe "SizedQueue#num_waiting" do
+ it_behaves_like :sizedqueue_num_waiting, :new, ->(n) { SizedQueue.new(n) }
+end
diff --git a/spec/ruby/core/sizedqueue/pop_spec.rb b/spec/ruby/core/sizedqueue/pop_spec.rb
new file mode 100644
index 0000000000..a0cf6f509c
--- /dev/null
+++ b/spec/ruby/core/sizedqueue/pop_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/deque'
+
+describe "SizedQueue#pop" do
+ it_behaves_like :queue_deq, :pop, -> { SizedQueue.new(10) }
+end
diff --git a/spec/ruby/core/sizedqueue/push_spec.rb b/spec/ruby/core/sizedqueue/push_spec.rb
new file mode 100644
index 0000000000..5f92c5a2b7
--- /dev/null
+++ b/spec/ruby/core/sizedqueue/push_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/enque'
+require_relative '../../shared/sizedqueue/enque'
+
+describe "SizedQueue#push" do
+ it_behaves_like :queue_enq, :push, -> { SizedQueue.new(10) }
+end
+
+describe "SizedQueue#push" do
+ it_behaves_like :sizedqueue_enq, :push, ->(n) { SizedQueue.new(n) }
+end
diff --git a/spec/ruby/core/sizedqueue/shift_spec.rb b/spec/ruby/core/sizedqueue/shift_spec.rb
new file mode 100644
index 0000000000..5138e68258
--- /dev/null
+++ b/spec/ruby/core/sizedqueue/shift_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/deque'
+
+describe "SizedQueue#shift" do
+ it_behaves_like :queue_deq, :shift, -> { SizedQueue.new(10) }
+end
diff --git a/spec/ruby/core/sizedqueue/size_spec.rb b/spec/ruby/core/sizedqueue/size_spec.rb
new file mode 100644
index 0000000000..dfa76faabe
--- /dev/null
+++ b/spec/ruby/core/sizedqueue/size_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/queue/length'
+
+describe "SizedQueue#size" do
+ it_behaves_like :queue_length, :size, -> { SizedQueue.new(10) }
+end
diff --git a/spec/ruby/core/string/allocate_spec.rb b/spec/ruby/core/string/allocate_spec.rb
new file mode 100644
index 0000000000..5b36b4fd05
--- /dev/null
+++ b/spec/ruby/core/string/allocate_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+describe "String.allocate" do
+ it "returns an instance of String" do
+ str = String.allocate
+ str.should be_an_instance_of(String)
+ end
+
+ it "returns a fully-formed String" do
+ str = String.allocate
+ str.size.should == 0
+ str << "more"
+ str.should == "more"
+ end
+
+ it "returns a binary String" do
+ String.new.encoding.should == Encoding::BINARY
+ end
+end
diff --git a/spec/ruby/core/string/append_spec.rb b/spec/ruby/core/string/append_spec.rb
new file mode 100644
index 0000000000..1e1667f617
--- /dev/null
+++ b/spec/ruby/core/string/append_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/concat'
+
+describe "String#<<" do
+ it_behaves_like :string_concat, :<<
+ it_behaves_like :string_concat_encoding, :<<
+end
diff --git a/spec/ruby/core/string/ascii_only_spec.rb b/spec/ruby/core/string/ascii_only_spec.rb
new file mode 100644
index 0000000000..3dce10fab3
--- /dev/null
+++ b/spec/ruby/core/string/ascii_only_spec.rb
@@ -0,0 +1,85 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+with_feature :encoding do
+ describe "String#ascii_only?" do
+ describe "with ASCII only characters" do
+ it "returns true if the encoding is UTF-8" do
+ [ ["hello", true],
+ ["hello".encode('UTF-8'), true],
+ ["hello".force_encoding('UTF-8'), true],
+ ].should be_computed_by(:ascii_only?)
+ end
+
+ it "returns true if the encoding is US-ASCII" do
+ "hello".force_encoding(Encoding::US_ASCII).ascii_only?.should be_true
+ "hello".encode(Encoding::US_ASCII).ascii_only?.should be_true
+ end
+
+ it "returns true for all single-character UTF-8 Strings" do
+ 0.upto(127) do |n|
+ n.chr.ascii_only?.should be_true
+ end
+ end
+ end
+
+ describe "with non-ASCII only characters" do
+ it "returns false if the encoding is ASCII-8BIT" do
+ chr = 128.chr
+ chr.encoding.should == Encoding::ASCII_8BIT
+ chr.ascii_only?.should be_false
+ end
+
+ it "returns false if the String contains any non-ASCII characters" do
+ [ ["\u{6666}", false],
+ ["hello, \u{6666}", false],
+ ["\u{6666}".encode('UTF-8'), false],
+ ["\u{6666}".force_encoding('UTF-8'), false],
+ ].should be_computed_by(:ascii_only?)
+ end
+
+ it "returns false if the encoding is US-ASCII" do
+ [ ["\u{6666}".force_encoding(Encoding::US_ASCII), false],
+ ["hello, \u{6666}".force_encoding(Encoding::US_ASCII), false],
+ ].should be_computed_by(:ascii_only?)
+ end
+ end
+
+ it "returns true for the empty String with an ASCII-compatible encoding" do
+ "".ascii_only?.should be_true
+ "".encode('UTF-8').ascii_only?.should be_true
+ end
+
+ it "returns false for the empty String with a non-ASCII-compatible encoding" do
+ "".force_encoding('UTF-16LE').ascii_only?.should be_false
+ "".encode('UTF-16BE').ascii_only?.should be_false
+ end
+
+ it "returns false for a non-empty String with non-ASCII-compatible encoding" do
+ "\x78\x00".force_encoding("UTF-16LE").ascii_only?.should be_false
+ end
+
+ it "returns false when interpolating non ascii strings" do
+ base = "EU currency is"
+ base.force_encoding(Encoding::US_ASCII)
+ euro = "\u20AC"
+ interp = "#{base} #{euro}"
+ euro.ascii_only?.should be_false
+ base.ascii_only?.should be_true
+ interp.ascii_only?.should be_false
+ end
+
+ it "returns false after appending non ASCII characters to an empty String" do
+ ("" << "λ").ascii_only?.should be_false
+ end
+
+ it "returns false when concatenating an ASCII and non-ASCII String" do
+ "".concat("λ").ascii_only?.should be_false
+ end
+
+ it "returns false when replacing an ASCII String with a non-ASCII String" do
+ "".replace("λ").ascii_only?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/core/string/b_spec.rb b/spec/ruby/core/string/b_spec.rb
new file mode 100644
index 0000000000..6599c23d73
--- /dev/null
+++ b/spec/ruby/core/string/b_spec.rb
@@ -0,0 +1,24 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe "String#b" do
+ with_feature :encoding do
+ it "returns an ASCII-8BIT encoded string" do
+ "Hello".b.should == "Hello".force_encoding(Encoding::ASCII_8BIT)
+ "ã“ã‚“ã¡ã«ã¯".b.should == "ã“ã‚“ã¡ã«ã¯".force_encoding(Encoding::ASCII_8BIT)
+ end
+
+ it "returns new string without modifying self" do
+ str = "ã“ã‚“ã¡ã«ã¯"
+ str.b.should_not equal(str)
+ str.should == "ã“ã‚“ã¡ã«ã¯"
+ end
+
+ it "copies own tainted/untrusted status to the returning value" do
+ utf_8 = "ã“ã‚“ã¡ã«ã¯".taint.untrust
+ ret = utf_8.b
+ ret.tainted?.should be_true
+ ret.untrusted?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/core/string/bytes_spec.rb b/spec/ruby/core/string/bytes_spec.rb
new file mode 100644
index 0000000000..e7d3a7fbd8
--- /dev/null
+++ b/spec/ruby/core/string/bytes_spec.rb
@@ -0,0 +1,57 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe "String#bytes" do
+ before :each do
+ @utf8 = "æ±äº¬"
+ @ascii = 'Tokyo'
+ @utf8_ascii = @utf8 + @ascii
+ end
+
+ it "returns an Array when no block is given" do
+ @utf8.bytes.should be_an_instance_of(Array)
+ end
+
+ it "yields each byte to a block if one is given, returning self" do
+ bytes = []
+ @utf8.bytes {|b| bytes << b}.should == @utf8
+ bytes.should == @utf8.bytes.to_a
+ end
+
+ it "returns #bytesize bytes" do
+ @utf8_ascii.bytes.to_a.size.should == @utf8_ascii.bytesize
+ end
+
+ it "returns bytes as Fixnums" do
+ @ascii.bytes.to_a.each {|b| b.should be_an_instance_of(Fixnum)}
+ @utf8_ascii.bytes { |b| b.should be_an_instance_of(Fixnum) }
+ end
+
+ it "agrees with #unpack('C*')" do
+ @utf8_ascii.bytes.to_a.should == @utf8_ascii.unpack("C*")
+ end
+
+ it "yields/returns no bytes for the empty string" do
+ ''.bytes.to_a.should == []
+ end
+end
+
+with_feature :encoding do
+ describe "String#bytes" do
+ before :each do
+ @utf8 = "æ±äº¬"
+ @ascii = 'Tokyo'
+ @utf8_ascii = @utf8 + @ascii
+ end
+
+ it "agrees with #getbyte" do
+ @utf8_ascii.bytes.to_a.each_with_index do |byte,index|
+ byte.should == @utf8_ascii.getbyte(index)
+ end
+ end
+
+ it "is unaffected by #force_encoding" do
+ @utf8.force_encoding('ASCII').bytes.to_a.should == @utf8.bytes.to_a
+ end
+ end
+end
diff --git a/spec/ruby/core/string/bytesize_spec.rb b/spec/ruby/core/string/bytesize_spec.rb
new file mode 100644
index 0000000000..527b4a5dd5
--- /dev/null
+++ b/spec/ruby/core/string/bytesize_spec.rb
@@ -0,0 +1,37 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+with_feature :encoding do
+ describe "#String#bytesize" do
+ it "needs to be reviewed for spec completeness"
+
+ it "returns the length of self in bytes" do
+ "hello".bytesize.should == 5
+ " ".bytesize.should == 1
+ end
+
+ it "works with strings containing single UTF-8 characters" do
+ "\u{6666}".bytesize.should == 3
+ end
+
+ it "works with pseudo-ASCII strings containing single UTF-8 characters" do
+ "\u{6666}".force_encoding('ASCII').bytesize.should == 3
+ end
+
+ it "works with strings containing UTF-8 characters" do
+ "c \u{6666}".force_encoding('UTF-8').bytesize.should == 5
+ "c \u{6666}".bytesize.should == 5
+ end
+
+ it "works with pseudo-ASCII strings containing UTF-8 characters" do
+ "c \u{6666}".force_encoding('ASCII').bytesize.should == 5
+ end
+
+ it "returns 0 for the empty string" do
+ "".bytesize.should == 0
+ "".force_encoding('ASCII').bytesize.should == 0
+ "".force_encoding('UTF-8').bytesize.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/core/string/byteslice_spec.rb b/spec/ruby/core/string/byteslice_spec.rb
new file mode 100644
index 0000000000..df99db95c6
--- /dev/null
+++ b/spec/ruby/core/string/byteslice_spec.rb
@@ -0,0 +1,29 @@
+# -*- encoding: binary -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/slice'
+
+describe "String#byteslice" do
+ it "needs to reviewed for spec completeness"
+
+ it_behaves_like :string_slice, :byteslice
+end
+
+describe "String#byteslice with index, length" do
+ it_behaves_like :string_slice_index_length, :byteslice
+end
+
+describe "String#byteslice with Range" do
+ it_behaves_like :string_slice_range, :byteslice
+end
+
+with_feature :encoding do
+ describe "String#byteslice on on non ASCII strings" do
+ it "returns byteslice of unicode strings" do
+ "\u3042".byteslice(1).should == "\x81".force_encoding("UTF-8")
+ "\u3042".byteslice(1, 2).should == "\x81\x82".force_encoding("UTF-8")
+ "\u3042".byteslice(1..2).should == "\x81\x82".force_encoding("UTF-8")
+ "\u3042".byteslice(-1).should == "\x82".force_encoding("UTF-8")
+ end
+ end
+end
diff --git a/spec/ruby/core/string/capitalize_spec.rb b/spec/ruby/core/string/capitalize_spec.rb
new file mode 100644
index 0000000000..10f9ab00a1
--- /dev/null
+++ b/spec/ruby/core/string/capitalize_spec.rb
@@ -0,0 +1,197 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#capitalize" do
+ it "returns a copy of self with the first character converted to uppercase and the remainder to lowercase" do
+ "".capitalize.should == ""
+ "h".capitalize.should == "H"
+ "H".capitalize.should == "H"
+ "hello".capitalize.should == "Hello"
+ "HELLO".capitalize.should == "Hello"
+ "123ABC".capitalize.should == "123abc"
+ end
+
+ it "taints resulting string when self is tainted" do
+ "".taint.capitalize.tainted?.should == true
+ "hello".taint.capitalize.tainted?.should == true
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "is locale insensitive (only upcases a-z and only downcases A-Z)" do
+ "ÄÖÜ".capitalize.should == "ÄÖÜ"
+ "ärger".capitalize.should == "ärger"
+ "BÄR".capitalize.should == "BÄr"
+ end
+ end
+
+ ruby_version_is '2.4' do
+ describe "full Unicode case mapping" do
+ it "works for all of Unicode with no option" do
+ "äöÜ".capitalize.should == "Äöü"
+ end
+
+ it "only capitalizes the first resulting character when upcasing a character produces a multi-character sequence" do
+ "ß".capitalize.should == "Ss"
+ end
+
+ it "updates string metadata" do
+ capitalized = "ßeT".capitalize
+
+ capitalized.should == "Sset"
+ capitalized.size.should == 4
+ capitalized.bytesize.should == 4
+ capitalized.ascii_only?.should be_true
+ end
+ end
+
+ describe "ASCII-only case mapping" do
+ it "does not capitalize non-ASCII characters" do
+ "ßet".capitalize(:ascii).should == "ßet"
+ end
+ end
+
+ describe "full Unicode case mapping adapted for Turkic languages" do
+ it "capitalizes ASCII characters according to Turkic semantics" do
+ "iSa".capitalize(:turkic).should == "İsa"
+ end
+
+ it "allows Lithuanian as an extra option" do
+ "iSa".capitalize(:turkic, :lithuanian).should == "İsa"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { "iSa".capitalize(:turkic, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "full Unicode case mapping adapted for Lithuanian" do
+ it "currently works the same as full Unicode case mapping" do
+ "iß".capitalize(:lithuanian).should == "Iß"
+ end
+
+ it "allows Turkic as an extra option (and applies Turkic semantics)" do
+ "iß".capitalize(:lithuanian, :turkic).should == "İß"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { "iß".capitalize(:lithuanian, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "does not allow the :fold option for upcasing" do
+ lambda { "abc".capitalize(:fold) }.should raise_error(ArgumentError)
+ end
+
+ it "does not allow invalid options" do
+ lambda { "abc".capitalize(:invalid_option) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "returns subclass instances when called on a subclass" do
+ StringSpecs::MyString.new("hello").capitalize.should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("Hello").capitalize.should be_an_instance_of(StringSpecs::MyString)
+ end
+end
+
+describe "String#capitalize!" do
+ it "capitalizes self in place" do
+ a = "hello"
+ a.capitalize!.should equal(a)
+ a.should == "Hello"
+ end
+
+ ruby_version_is '2.4' do
+ describe "full Unicode case mapping" do
+ it "modifies self in place for all of Unicode with no option" do
+ a = "äöÜ"
+ a.capitalize!
+ a.should == "Äöü"
+ end
+
+ it "only capitalizes the first resulting character when upcasing a character produces a multi-character sequence" do
+ a = "ß"
+ a.capitalize!
+ a.should == "Ss"
+ end
+
+ it "updates string metadata" do
+ capitalized = "ßeT"
+ capitalized.capitalize!
+
+ capitalized.should == "Sset"
+ capitalized.size.should == 4
+ capitalized.bytesize.should == 4
+ capitalized.ascii_only?.should be_true
+ end
+ end
+
+ describe "modifies self in place for ASCII-only case mapping" do
+ it "does not capitalize non-ASCII characters" do
+ a = "ßet"
+ a.capitalize!(:ascii)
+ a.should == "ßet"
+ end
+ end
+
+ describe "modifies self in place for full Unicode case mapping adapted for Turkic languages" do
+ it "capitalizes ASCII characters according to Turkic semantics" do
+ a = "iSa"
+ a.capitalize!(:turkic)
+ a.should == "İsa"
+ end
+
+ it "allows Lithuanian as an extra option" do
+ a = "iSa"
+ a.capitalize!(:turkic, :lithuanian)
+ a.should == "İsa"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { a = "iSa"; a.capitalize!(:turkic, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "modifies self in place for full Unicode case mapping adapted for Lithuanian" do
+ it "currently works the same as full Unicode case mapping" do
+ a = "iß"
+ a.capitalize!(:lithuanian)
+ a.should == "Iß"
+ end
+
+ it "allows Turkic as an extra option (and applies Turkic semantics)" do
+ a = "iß"
+ a.capitalize!(:lithuanian, :turkic)
+ a.should == "İß"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { a = "iß"; a.capitalize!(:lithuanian, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "does not allow the :fold option for upcasing" do
+ lambda { a = "abc"; a.capitalize!(:fold) }.should raise_error(ArgumentError)
+ end
+
+ it "does not allow invalid options" do
+ lambda { a = "abc"; a.capitalize!(:invalid_option) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "returns nil when no changes are made" do
+ a = "Hello"
+ a.capitalize!.should == nil
+ a.should == "Hello"
+
+ "".capitalize!.should == nil
+ "H".capitalize!.should == nil
+ end
+
+ it "raises a #{frozen_error_class} when self is frozen" do
+ ["", "Hello", "hello"].each do |a|
+ a.freeze
+ lambda { a.capitalize! }.should raise_error(frozen_error_class)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/case_compare_spec.rb b/spec/ruby/core/string/case_compare_spec.rb
new file mode 100644
index 0000000000..b83d1adb91
--- /dev/null
+++ b/spec/ruby/core/string/case_compare_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'shared/eql'
+require_relative 'shared/equal_value'
+
+describe "String#===" do
+ it_behaves_like :string_eql_value, :===
+ it_behaves_like :string_equal_value, :===
+end
diff --git a/spec/ruby/core/string/casecmp_spec.rb b/spec/ruby/core/string/casecmp_spec.rb
new file mode 100644
index 0000000000..87be999964
--- /dev/null
+++ b/spec/ruby/core/string/casecmp_spec.rb
@@ -0,0 +1,216 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#casecmp independent of case" do
+ it "returns -1 when less than other" do
+ "a".casecmp("b").should == -1
+ "A".casecmp("b").should == -1
+ end
+
+ it "returns 0 when equal to other" do
+ "a".casecmp("a").should == 0
+ "A".casecmp("a").should == 0
+ end
+
+ it "returns 1 when greater than other" do
+ "b".casecmp("a").should == 1
+ "B".casecmp("a").should == 1
+ end
+
+ it "tries to convert other to string using to_str" do
+ other = mock('x')
+ other.should_receive(:to_str).and_return("abc")
+
+ "abc".casecmp(other).should == 0
+ end
+
+ ruby_version_is ""..."2.5" do
+ it "raises a TypeError if other can't be converted to a string" do
+ lambda { "abc".casecmp(mock('abc')) }.should raise_error(TypeError)
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "returns nil if other can't be converted to a string" do
+ "abc".casecmp(mock('abc')).should be_nil
+ end
+ end
+
+ it "returns nil if incompatible encodings" do
+ "ã‚れ".casecmp("れ".encode(Encoding::EUC_JP)).should be_nil
+ end
+
+ describe "in UTF-8 mode" do
+ describe "for non-ASCII characters" do
+ before :each do
+ @upper_a_tilde = "Ã"
+ @lower_a_tilde = "ã"
+ @upper_a_umlaut = "Ä"
+ @lower_a_umlaut = "ä"
+ end
+
+ it "returns -1 when numerically less than other" do
+ @upper_a_tilde.casecmp(@lower_a_tilde).should == -1
+ @upper_a_tilde.casecmp(@upper_a_umlaut).should == -1
+ end
+
+ it "returns 0 when numerically equal to other" do
+ @upper_a_tilde.casecmp(@upper_a_tilde).should == 0
+ end
+
+ it "returns 1 when numerically greater than other" do
+ @lower_a_umlaut.casecmp(@upper_a_umlaut).should == 1
+ @lower_a_umlaut.casecmp(@lower_a_tilde).should == 1
+ end
+ end
+
+ describe "for ASCII characters" do
+ it "returns -1 when less than other" do
+ "a".casecmp("b").should == -1
+ "A".casecmp("b").should == -1
+ end
+
+ it "returns 0 when equal to other" do
+ "a".casecmp("a").should == 0
+ "A".casecmp("a").should == 0
+ end
+
+ it "returns 1 when greater than other" do
+ "b".casecmp("a").should == 1
+ "B".casecmp("a").should == 1
+ end
+ end
+ end
+
+ describe "for non-ASCII characters" do
+ before :each do
+ @upper_a_tilde = "\xc3"
+ @lower_a_tilde = "\xe3"
+ end
+
+ it "returns -1 when numerically less than other" do
+ @upper_a_tilde.casecmp(@lower_a_tilde).should == -1
+ end
+
+ it "returns 0 when equal to other" do
+ @upper_a_tilde.casecmp("\xc3").should == 0
+ end
+
+ it "returns 1 when numerically greater than other" do
+ @lower_a_tilde.casecmp(@upper_a_tilde).should == 1
+ end
+
+ ruby_version_is "2.4" do
+ it "does not case fold" do
+ "ß".casecmp("ss").should == 1
+ end
+ end
+ end
+
+ describe "when comparing a subclass instance" do
+ it "returns -1 when less than other" do
+ b = StringSpecs::MyString.new "b"
+ "a".casecmp(b).should == -1
+ "A".casecmp(b).should == -1
+ end
+
+ it "returns 0 when equal to other" do
+ a = StringSpecs::MyString.new "a"
+ "a".casecmp(a).should == 0
+ "A".casecmp(a).should == 0
+ end
+
+ it "returns 1 when greater than other" do
+ a = StringSpecs::MyString.new "a"
+ "b".casecmp(a).should == 1
+ "B".casecmp(a).should == 1
+ end
+ end
+end
+
+ruby_version_is "2.4" do
+ describe 'String#casecmp? independent of case' do
+ it 'returns true when equal to other' do
+ 'abc'.casecmp?('abc').should == true
+ 'abc'.casecmp?('ABC').should == true
+ end
+
+ it 'returns false when not equal to other' do
+ 'abc'.casecmp?('DEF').should == false
+ 'abc'.casecmp?('def').should == false
+ end
+
+ it "tries to convert other to string using to_str" do
+ other = mock('x')
+ other.should_receive(:to_str).and_return("abc")
+
+ "abc".casecmp?(other).should == true
+ end
+
+ it "returns nil if incompatible encodings" do
+ "ã‚れ".casecmp?("れ".encode(Encoding::EUC_JP)).should be_nil
+ end
+
+ describe 'for UNICODE characters' do
+ it 'returns true when downcase(:fold) on unicode' do
+ 'äöü'.casecmp?('ÄÖÜ').should == true
+ end
+ end
+
+ describe "when comparing a subclass instance" do
+ it 'returns true when equal to other' do
+ a = StringSpecs::MyString.new "a"
+ 'a'.casecmp?(a).should == true
+ 'A'.casecmp?(a).should == true
+ end
+
+ it 'returns false when not equal to other' do
+ b = StringSpecs::MyString.new "a"
+ 'b'.casecmp?(b).should == false
+ 'B'.casecmp?(b).should == false
+ end
+ end
+
+ describe "in UTF-8 mode" do
+ describe "for non-ASCII characters" do
+ before :each do
+ @upper_a_tilde = "Ã"
+ @lower_a_tilde = "ã"
+ @upper_a_umlaut = "Ä"
+ @lower_a_umlaut = "ä"
+ end
+
+ it "returns true when they are the same with normalized case" do
+ @upper_a_tilde.casecmp?(@lower_a_tilde).should == true
+ end
+
+ it "returns false when they are unrelated" do
+ @upper_a_tilde.casecmp?(@upper_a_umlaut).should == false
+ end
+
+ it "returns true when they have the same bytes" do
+ @upper_a_tilde.casecmp?(@upper_a_tilde).should == true
+ end
+ end
+ end
+
+ ruby_version_is "2.4" do
+ it "case folds" do
+ "ß".casecmp?("ss").should be_true
+ end
+ end
+
+ ruby_version_is "2.4" ... "2.5" do
+ it "raises a TypeError if other can't be converted to a string" do
+ lambda { "abc".casecmp?(mock('abc')) }.should raise_error(TypeError)
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "returns nil if other can't be converted to a string" do
+ "abc".casecmp?(mock('abc')).should be_nil
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/center_spec.rb b/spec/ruby/core/string/center_spec.rb
new file mode 100644
index 0000000000..145db01b70
--- /dev/null
+++ b/spec/ruby/core/string/center_spec.rb
@@ -0,0 +1,133 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#center with length, padding" do
+ it "returns a new string of specified length with self centered and padded with padstr" do
+ "one".center(9, '.').should == "...one..."
+ "hello".center(20, '123').should == "1231231hello12312312"
+ "middle".center(13, '-').should == "---middle----"
+
+ "".center(1, "abcd").should == "a"
+ "".center(2, "abcd").should == "aa"
+ "".center(3, "abcd").should == "aab"
+ "".center(4, "abcd").should == "abab"
+ "".center(6, "xy").should == "xyxxyx"
+ "".center(11, "12345").should == "12345123451"
+
+ "|".center(2, "abcd").should == "|a"
+ "|".center(3, "abcd").should == "a|a"
+ "|".center(4, "abcd").should == "a|ab"
+ "|".center(5, "abcd").should == "ab|ab"
+ "|".center(6, "xy").should == "xy|xyx"
+ "|".center(7, "xy").should == "xyx|xyx"
+ "|".center(11, "12345").should == "12345|12345"
+ "|".center(12, "12345").should == "12345|123451"
+
+ "||".center(3, "abcd").should == "||a"
+ "||".center(4, "abcd").should == "a||a"
+ "||".center(5, "abcd").should == "a||ab"
+ "||".center(6, "abcd").should == "ab||ab"
+ "||".center(8, "xy").should == "xyx||xyx"
+ "||".center(12, "12345").should == "12345||12345"
+ "||".center(13, "12345").should == "12345||123451"
+ end
+
+ it "pads with whitespace if no padstr is given" do
+ "two".center(5).should == " two "
+ "hello".center(20).should == " hello "
+ end
+
+ it "returns self if it's longer than or as long as the specified length" do
+ "".center(0).should == ""
+ "".center(-1).should == ""
+ "hello".center(4).should == "hello"
+ "hello".center(-1).should == "hello"
+ "this".center(3).should == "this"
+ "radiology".center(8, '-').should == "radiology"
+ end
+
+ it "taints result when self or padstr is tainted" do
+ "x".taint.center(4).tainted?.should == true
+ "x".taint.center(0).tainted?.should == true
+ "".taint.center(0).tainted?.should == true
+ "x".taint.center(4, "*").tainted?.should == true
+ "x".center(4, "*".taint).tainted?.should == true
+ end
+
+ it "calls #to_int to convert length to an integer" do
+ "_".center(3.8, "^").should == "^_^"
+
+ obj = mock('3')
+ obj.should_receive(:to_int).and_return(3)
+
+ "_".center(obj, "o").should == "o_o"
+ end
+
+ it "raises a TypeError when length can't be converted to an integer" do
+ lambda { "hello".center("x") }.should raise_error(TypeError)
+ lambda { "hello".center("x", "y") }.should raise_error(TypeError)
+ lambda { "hello".center([]) }.should raise_error(TypeError)
+ lambda { "hello".center(mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "calls #to_str to convert padstr to a String" do
+ padstr = mock('123')
+ padstr.should_receive(:to_str).and_return("123")
+
+ "hello".center(20, padstr).should == "1231231hello12312312"
+ end
+
+ it "raises a TypeError when padstr can't be converted to a string" do
+ lambda { "hello".center(20, 100) }.should raise_error(TypeError)
+ lambda { "hello".center(20, []) }.should raise_error(TypeError)
+ lambda { "hello".center(20, mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError if padstr is empty" do
+ lambda { "hello".center(10, "") }.should raise_error(ArgumentError)
+ lambda { "hello".center(0, "") }.should raise_error(ArgumentError)
+ end
+
+ it "returns subclass instances when called on subclasses" do
+ StringSpecs::MyString.new("").center(10).should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("foo").center(10).should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("foo").center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString)
+
+ "".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
+ "foo".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
+ end
+
+ it "when padding is tainted and self is untainted returns a tainted string if and only if length is longer than self" do
+ "hello".center(4, 'X'.taint).tainted?.should be_false
+ "hello".center(5, 'X'.taint).tainted?.should be_false
+ "hello".center(6, 'X'.taint).tainted?.should be_true
+ end
+
+ with_feature :encoding do
+ describe "with width" do
+ it "returns a String in the same encoding as the original" do
+ str = "abc".force_encoding Encoding::IBM437
+ result = str.center 6
+ result.should == " abc "
+ result.encoding.should equal(Encoding::IBM437)
+ end
+ end
+
+ describe "with width, pattern" do
+ it "returns a String in the compatible encoding" do
+ str = "abc".force_encoding Encoding::IBM437
+ result = str.center 6, "ã‚"
+ result.should == "ã‚abcã‚ã‚"
+ result.encoding.should equal(Encoding::UTF_8)
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ pat = "ã‚¢".encode Encoding::EUC_JP
+ lambda do
+ "ã‚れ".center 5, pat
+ end.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/chars_spec.rb b/spec/ruby/core/string/chars_spec.rb
new file mode 100644
index 0000000000..e4f26bc0cc
--- /dev/null
+++ b/spec/ruby/core/string/chars_spec.rb
@@ -0,0 +1,10 @@
+require_relative 'shared/chars'
+require_relative 'shared/each_char_without_block'
+
+describe "String#chars" do
+ it_behaves_like :string_chars, :chars
+
+ it "returns an array when no block given" do
+ "hello".chars.should == ['h', 'e', 'l', 'l', 'o']
+ end
+end
diff --git a/spec/ruby/core/string/chomp_spec.rb b/spec/ruby/core/string/chomp_spec.rb
new file mode 100644
index 0000000000..6fa8d7c6c5
--- /dev/null
+++ b/spec/ruby/core/string/chomp_spec.rb
@@ -0,0 +1,387 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#chomp" do
+ describe "when passed no argument" do
+ before do
+ # Ensure that $/ is set to the default value
+ @dollar_slash, $/ = $/, "\n"
+ end
+
+ after do
+ $/ = @dollar_slash
+ end
+
+ it "does not modify a String with no trailing carriage return or newline" do
+ "abc".chomp.should == "abc"
+ end
+
+ it "returns a copy of the String when it is not modified" do
+ str = "abc"
+ str.chomp.should_not equal(str)
+ end
+
+ it "removes one trailing newline" do
+ "abc\n\n".chomp.should == "abc\n"
+ end
+
+ it "removes one trailing carriage return" do
+ "abc\r\r".chomp.should == "abc\r"
+ end
+
+ it "removes one trailing carrige return, newline pair" do
+ "abc\r\n\r\n".chomp.should == "abc\r\n"
+ end
+
+ it "returns an empty String when self is empty" do
+ "".chomp.should == ""
+ end
+
+ it "taints the result if self is tainted" do
+ "abc".taint.chomp.tainted?.should be_true
+ end
+
+ it "returns subclass instances when called on a subclass" do
+ str = StringSpecs::MyString.new("hello\n").chomp
+ str.should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ it "removes trailing characters that match $/ when it has been assigned a value" do
+ $/ = "cdef"
+ "abcdef".chomp.should == "ab"
+ end
+ end
+
+ describe "when passed nil" do
+ it "does not modify the String" do
+ "abc\r\n".chomp(nil).should == "abc\r\n"
+ end
+
+ it "returns a copy of the String" do
+ str = "abc"
+ str.chomp(nil).should_not equal(str)
+ end
+
+ it "taints the result if self is tainted" do
+ "abc".taint.chomp(nil).tainted?.should be_true
+ end
+
+ it "returns an empty String when self is empty" do
+ "".chomp(nil).should == ""
+ end
+ end
+
+ describe "when passed ''" do
+ it "removes a final newline" do
+ "abc\n".chomp("").should == "abc"
+ end
+
+ it "removes a final carriage return, newline" do
+ "abc\r\n".chomp("").should == "abc"
+ end
+
+ it "does not remove a final carriage return" do
+ "abc\r".chomp("").should == "abc\r"
+ end
+
+ it "removes more than one trailing newlines" do
+ "abc\n\n\n".chomp("").should == "abc"
+ end
+
+ it "removes more than one trailing carriage return, newline pairs" do
+ "abc\r\n\r\n\r\n".chomp("").should == "abc"
+ end
+
+ it "taints the result if self is tainted" do
+ "abc".taint.chomp("").tainted?.should be_true
+ end
+
+ it "returns an empty String when self is empty" do
+ "".chomp("").should == ""
+ end
+ end
+
+ describe "when passed '\\n'" do
+ it "removes one trailing newline" do
+ "abc\n\n".chomp("\n").should == "abc\n"
+ end
+
+ it "removes one trailing carriage return" do
+ "abc\r\r".chomp("\n").should == "abc\r"
+ end
+
+ it "removes one trailing carrige return, newline pair" do
+ "abc\r\n\r\n".chomp("\n").should == "abc\r\n"
+ end
+
+ it "taints the result if self is tainted" do
+ "abc".taint.chomp("\n").tainted?.should be_true
+ end
+
+ it "returns an empty String when self is empty" do
+ "".chomp("\n").should == ""
+ end
+ end
+
+ describe "when passed an Object" do
+ it "calls #to_str to convert to a String" do
+ arg = mock("string chomp")
+ arg.should_receive(:to_str).and_return("bc")
+ "abc".chomp(arg).should == "a"
+ end
+
+ it "raises a TypeError if #to_str does not return a String" do
+ arg = mock("string chomp")
+ arg.should_receive(:to_str).and_return(1)
+ lambda { "abc".chomp(arg) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "when passed a String" do
+ it "removes the trailing characters if they match the argument" do
+ "abcabc".chomp("abc").should == "abc"
+ end
+
+ it "does not modify the String if the argument does not match the trailing characters" do
+ "abc".chomp("def").should == "abc"
+ end
+
+ it "returns an empty String when self is empty" do
+ "".chomp("abc").should == ""
+ end
+
+ it "taints the result if self is tainted" do
+ "abc".taint.chomp("abc").tainted?.should be_true
+ end
+
+ it "does not taint the result when the argument is tainted" do
+ "abc".chomp("abc".taint).tainted?.should be_false
+ end
+ end
+end
+
+describe "String#chomp!" do
+ describe "when passed no argument" do
+ before do
+ # Ensure that $/ is set to the default value
+ @dollar_slash, $/ = $/, "\n"
+ end
+
+ after do
+ $/ = @dollar_slash
+ end
+
+ it "modifies self" do
+ str = "abc\n"
+ str.chomp!.should equal(str)
+ end
+
+ it "returns nil if self is not modified" do
+ "abc".chomp!.should be_nil
+ end
+
+ it "removes one trailing newline" do
+ "abc\n\n".chomp!.should == "abc\n"
+ end
+
+ it "removes one trailing carriage return" do
+ "abc\r\r".chomp!.should == "abc\r"
+ end
+
+ it "removes one trailing carrige return, newline pair" do
+ "abc\r\n\r\n".chomp!.should == "abc\r\n"
+ end
+
+ it "returns nil when self is empty" do
+ "".chomp!.should be_nil
+ end
+
+ it "taints the result if self is tainted" do
+ "abc\n".taint.chomp!.tainted?.should be_true
+ end
+
+ it "returns subclass instances when called on a subclass" do
+ str = StringSpecs::MyString.new("hello\n").chomp!
+ str.should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ it "removes trailing characters that match $/ when it has been assigned a value" do
+ $/ = "cdef"
+ "abcdef".chomp!.should == "ab"
+ end
+ end
+
+ describe "when passed nil" do
+ it "returns nil" do
+ "abc\r\n".chomp!(nil).should be_nil
+ end
+
+ it "returns nil when self is empty" do
+ "".chomp!(nil).should be_nil
+ end
+ end
+
+ describe "when passed ''" do
+ it "removes a final newline" do
+ "abc\n".chomp!("").should == "abc"
+ end
+
+ it "removes a final carriage return, newline" do
+ "abc\r\n".chomp!("").should == "abc"
+ end
+
+ it "does not remove a final carriage return" do
+ "abc\r".chomp!("").should be_nil
+ end
+
+ it "removes more than one trailing newlines" do
+ "abc\n\n\n".chomp!("").should == "abc"
+ end
+
+ it "removes more than one trailing carriage return, newline pairs" do
+ "abc\r\n\r\n\r\n".chomp!("").should == "abc"
+ end
+
+ it "taints the result if self is tainted" do
+ "abc\n".taint.chomp!("").tainted?.should be_true
+ end
+
+ it "returns nil when self is empty" do
+ "".chomp!("").should be_nil
+ end
+ end
+
+ describe "when passed '\\n'" do
+ it "removes one trailing newline" do
+ "abc\n\n".chomp!("\n").should == "abc\n"
+ end
+
+ it "removes one trailing carriage return" do
+ "abc\r\r".chomp!("\n").should == "abc\r"
+ end
+
+ it "removes one trailing carrige return, newline pair" do
+ "abc\r\n\r\n".chomp!("\n").should == "abc\r\n"
+ end
+
+ it "taints the result if self is tainted" do
+ "abc\n".taint.chomp!("\n").tainted?.should be_true
+ end
+
+ it "returns nil when self is empty" do
+ "".chomp!("\n").should be_nil
+ end
+ end
+
+ describe "when passed an Object" do
+ it "calls #to_str to convert to a String" do
+ arg = mock("string chomp")
+ arg.should_receive(:to_str).and_return("bc")
+ "abc".chomp!(arg).should == "a"
+ end
+
+ it "raises a TypeError if #to_str does not return a String" do
+ arg = mock("string chomp")
+ arg.should_receive(:to_str).and_return(1)
+ lambda { "abc".chomp!(arg) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "when passed a String" do
+ it "removes the trailing characters if they match the argument" do
+ "abcabc".chomp!("abc").should == "abc"
+ end
+
+ it "returns nil if the argument does not match the trailing characters" do
+ "abc".chomp!("def").should be_nil
+ end
+
+ it "returns nil when self is empty" do
+ "".chomp!("abc").should be_nil
+ end
+
+ it "taints the result if self is tainted" do
+ "abc".taint.chomp!("abc").tainted?.should be_true
+ end
+
+ it "does not taint the result when the argument is tainted" do
+ "abc".chomp!("abc".taint).tainted?.should be_false
+ end
+ end
+
+ it "raises a #{frozen_error_class} on a frozen instance when it is modified" do
+ a = "string\n\r"
+ a.freeze
+
+ lambda { a.chomp! }.should raise_error(frozen_error_class)
+ end
+
+ # see [ruby-core:23666]
+ it "raises a #{frozen_error_class} on a frozen instance when it would not be modified" do
+ a = "string\n\r"
+ a.freeze
+ lambda { a.chomp!(nil) }.should raise_error(frozen_error_class)
+ lambda { a.chomp!("x") }.should raise_error(frozen_error_class)
+ end
+end
+
+with_feature :encoding do
+ describe "String#chomp" do
+ before :each do
+ @before_separator = $/
+ end
+
+ after :each do
+ $/ = @before_separator
+ end
+
+ it "does not modify a multi-byte character" do
+ "ã‚れ".chomp.should == "ã‚れ"
+ end
+
+ it "removes the final carriage return, newline from a multibyte String" do
+ "ã‚れ\r\n".chomp.should == "ã‚れ"
+ end
+
+ it "removes the final carriage return, newline from a non-ASCII String" do
+ str = "abc\r\n".encode "utf-32be"
+ str.chomp.should == "abc".encode("utf-32be")
+ end
+
+ it "removes the final carriage return, newline from a non-ASCII String when the record separator is changed" do
+ $/ = "\n".encode("utf-8")
+ str = "abc\r\n".encode "utf-32be"
+ str.chomp.should == "abc".encode("utf-32be")
+ end
+ end
+
+ describe "String#chomp!" do
+ before :each do
+ @before_separator = $/
+ end
+
+ after :each do
+ $/ = @before_separator
+ end
+
+ it "returns nil when the String is not modified" do
+ "ã‚れ".chomp!.should be_nil
+ end
+
+ it "removes the final carriage return, newline from a multibyte String" do
+ "ã‚れ\r\n".chomp!.should == "ã‚れ"
+ end
+
+ it "removes the final carriage return, newline from a non-ASCII String" do
+ str = "abc\r\n".encode "utf-32be"
+ str.chomp!.should == "abc".encode("utf-32be")
+ end
+
+ it "removes the final carriage return, newline from a non-ASCII String when the record separator is changed" do
+ $/ = "\n".encode("utf-8")
+ str = "abc\r\n".encode "utf-32be"
+ str.chomp!.should == "abc".encode("utf-32be")
+ end
+ end
+end
diff --git a/spec/ruby/core/string/chop_spec.rb b/spec/ruby/core/string/chop_spec.rb
new file mode 100644
index 0000000000..57c037322d
--- /dev/null
+++ b/spec/ruby/core/string/chop_spec.rb
@@ -0,0 +1,128 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#chop" do
+ it "removes the final character" do
+ "abc".chop.should == "ab"
+ end
+
+ it "removes the final carriage return" do
+ "abc\r".chop.should == "abc"
+ end
+
+ it "removes the final newline" do
+ "abc\n".chop.should == "abc"
+ end
+
+ it "removes the final carriage return, newline" do
+ "abc\r\n".chop.should == "abc"
+ end
+
+ it "removes the carrige return, newline if they are the only characters" do
+ "\r\n".chop.should == ""
+ end
+
+ it "does not remove more than the final carriage return, newline" do
+ "abc\r\n\r\n".chop.should == "abc\r\n"
+ end
+
+ with_feature :encoding do
+ it "removes a multi-byte character" do
+ "ã‚れ".chop.should == "ã‚"
+ end
+
+ it "removes the final carriage return, newline from a multibyte String" do
+ "ã‚れ\r\n".chop.should == "ã‚れ"
+ end
+
+ it "removes the final carriage return, newline from a non-ASCII String" do
+ str = "abc\r\n".encode "utf-32be"
+ str.chop.should == "abc".encode("utf-32be")
+ end
+ end
+
+ it "returns an empty string when applied to an empty string" do
+ "".chop.should == ""
+ end
+
+ it "returns a new string when applied to an empty string" do
+ s = ""
+ s.chop.should_not equal(s)
+ end
+
+ it "taints result when self is tainted" do
+ "hello".taint.chop.tainted?.should == true
+ "".taint.chop.tainted?.should == true
+ end
+
+ it "untrusts result when self is untrusted" do
+ "hello".untrust.chop.untrusted?.should == true
+ "".untrust.chop.untrusted?.should == true
+ end
+
+ it "returns subclass instances when called on a subclass" do
+ StringSpecs::MyString.new("hello\n").chop.should be_an_instance_of(StringSpecs::MyString)
+ end
+end
+
+describe "String#chop!" do
+ it "removes the final character" do
+ "abc".chop!.should == "ab"
+ end
+
+ it "removes the final carriage return" do
+ "abc\r".chop!.should == "abc"
+ end
+
+ it "removes the final newline" do
+ "abc\n".chop!.should == "abc"
+ end
+
+ it "removes the final carriage return, newline" do
+ "abc\r\n".chop!.should == "abc"
+ end
+
+ it "removes the carrige return, newline if they are the only characters" do
+ "\r\n".chop!.should == ""
+ end
+
+ it "does not remove more than the final carriage return, newline" do
+ "abc\r\n\r\n".chop!.should == "abc\r\n"
+ end
+
+ with_feature :encoding do
+ it "removes a multi-byte character" do
+ "ã‚れ".chop!.should == "ã‚"
+ end
+
+ it "removes the final carriage return, newline from a multibyte String" do
+ "ã‚れ\r\n".chop!.should == "ã‚れ"
+ end
+
+ it "removes the final carriage return, newline from a non-ASCII String" do
+ str = "abc\r\n".encode "utf-32be"
+ str.chop!.should == "abc".encode("utf-32be")
+ end
+ end
+
+ it "returns self if modifications were made" do
+ str = "hello"
+ str.chop!.should equal(str)
+ end
+
+ it "returns nil when called on an empty string" do
+ "".chop!.should be_nil
+ end
+
+ it "raises a #{frozen_error_class} on a frozen instance that is modified" do
+ lambda { "string\n\r".freeze.chop! }.should raise_error(frozen_error_class)
+ end
+
+ # see [ruby-core:23666]
+ it "raises a #{frozen_error_class} on a frozen instance that would not be modified" do
+ a = ""
+ a.freeze
+ lambda { a.chop! }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/string/chr_spec.rb b/spec/ruby/core/string/chr_spec.rb
new file mode 100644
index 0000000000..ca56955866
--- /dev/null
+++ b/spec/ruby/core/string/chr_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "String#chr" do
+ it "returns a copy of self" do
+ s = 'e'
+ s.should_not equal s.chr
+ end
+
+ it "returns a String" do
+ 'glark'.chr.should be_an_instance_of(String)
+ end
+
+ it "returns an empty String if self is an empty String" do
+ "".chr.should == ""
+ end
+
+ it "returns a 1-character String" do
+ "glark".chr.size.should == 1
+ end
+
+ it "returns the character at the start of the String" do
+ "Goodbye, world".chr.should == "G"
+ end
+
+ it "returns a String in the same encoding as self" do
+ "\x24".encode(Encoding::US_ASCII).chr.encoding.should == Encoding::US_ASCII
+ end
+
+ it "understands multi-byte characters" do
+ s = "\u{9879}"
+ s.bytesize.should == 3
+ s.chr.should == s
+ end
+
+ it "understands Strings that contain a mixture of character widths" do
+ three = "\u{8082}"
+ three.bytesize.should == 3
+ four = "\u{77082}"
+ four.bytesize.should == 4
+ "#{three}#{four}".chr.should == three
+ end
+ end
+end
diff --git a/spec/ruby/core/string/clear_spec.rb b/spec/ruby/core/string/clear_spec.rb
new file mode 100644
index 0000000000..8b0ff8a8e6
--- /dev/null
+++ b/spec/ruby/core/string/clear_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "String#clear" do
+ before :each do
+ @s = "Jolene"
+ end
+
+ it "sets self equal to the empty String" do
+ @s.clear
+ @s.should == ""
+ end
+
+ it "returns self after emptying it" do
+ cleared = @s.clear
+ cleared.should == ""
+ cleared.should equal @s
+ end
+
+ it "preserves its encoding" do
+ @s.encode!(Encoding::SHIFT_JIS)
+ @s.encoding.should == Encoding::SHIFT_JIS
+ @s.clear.encoding.should == Encoding::SHIFT_JIS
+ @s.encoding.should == Encoding::SHIFT_JIS
+ end
+
+ it "works with multibyte Strings" do
+ s = "\u{9765}\u{876}"
+ s.clear
+ s.should == ""
+ end
+
+ it "raises a #{frozen_error_class} if self is frozen" do
+ @s.freeze
+ lambda { @s.clear }.should raise_error(frozen_error_class)
+ lambda { "".freeze.clear }.should raise_error(frozen_error_class)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/clone_spec.rb b/spec/ruby/core/string/clone_spec.rb
new file mode 100644
index 0000000000..f8d40423f0
--- /dev/null
+++ b/spec/ruby/core/string/clone_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#clone" do
+ before :each do
+ ScratchPad.clear
+ @obj = StringSpecs::InitializeString.new "string"
+ end
+
+ it "calls #initialize_copy on the new instance" do
+ clone = @obj.clone
+ ScratchPad.recorded.should_not == @obj.object_id
+ ScratchPad.recorded.should == clone.object_id
+ end
+
+ it "copies instance variables" do
+ clone = @obj.clone
+ clone.ivar.should == 1
+ end
+
+ it "copies singleton methods" do
+ def @obj.special() :the_one end
+ clone = @obj.clone
+ clone.special.should == :the_one
+ end
+
+ it "copies modules included in the singleton class" do
+ class << @obj
+ include StringSpecs::StringModule
+ end
+
+ clone = @obj.clone
+ clone.repr.should == 1
+ end
+
+ it "copies constants defined in the singleton class" do
+ class << @obj
+ CLONE = :clone
+ end
+
+ clone = @obj.clone
+ (class << clone; CLONE; end).should == :clone
+ end
+
+ it "copies frozen state" do
+ @obj.freeze.clone.frozen?.should be_true
+ "".freeze.clone.frozen?.should be_true
+ end
+
+ it "does not modify the original string when changing cloned string" do
+ orig = "string"[0..100]
+ clone = orig.clone
+ orig[0] = 'x'
+ orig.should == "xtring"
+ clone.should == "string"
+ end
+end
diff --git a/spec/ruby/core/string/codepoints_spec.rb b/spec/ruby/core/string/codepoints_spec.rb
new file mode 100644
index 0000000000..bccb3d0484
--- /dev/null
+++ b/spec/ruby/core/string/codepoints_spec.rb
@@ -0,0 +1,20 @@
+# -*- encoding: binary -*-
+require_relative '../../spec_helper'
+require_relative 'shared/codepoints'
+require_relative 'shared/each_codepoint_without_block'
+
+with_feature :encoding do
+ describe "String#codepoints" do
+ it_behaves_like :string_codepoints, :codepoints
+
+ it "returns an Array when no block is given" do
+ "abc".codepoints.should == [?a.ord, ?b.ord, ?c.ord]
+ end
+
+ it "raises an ArgumentError when no block is given if self has an invalid encoding" do
+ s = "\xDF".force_encoding(Encoding::UTF_8)
+ s.valid_encoding?.should be_false
+ lambda { s.codepoints }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/comparison_spec.rb b/spec/ruby/core/string/comparison_spec.rb
new file mode 100644
index 0000000000..01199274b6
--- /dev/null
+++ b/spec/ruby/core/string/comparison_spec.rb
@@ -0,0 +1,108 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#<=> with String" do
+ it "compares individual characters based on their ascii value" do
+ ascii_order = Array.new(256) { |x| x.chr }
+ sort_order = ascii_order.sort
+ sort_order.should == ascii_order
+ end
+
+ it "returns -1 when self is less than other" do
+ ("this" <=> "those").should == -1
+ end
+
+ it "returns 0 when self is equal to other" do
+ ("yep" <=> "yep").should == 0
+ end
+
+ it "returns 1 when self is greater than other" do
+ ("yoddle" <=> "griddle").should == 1
+ end
+
+ it "considers string that comes lexicographically first to be less if strings have same size" do
+ ("aba" <=> "abc").should == -1
+ ("abc" <=> "aba").should == 1
+ end
+
+ it "doesn't consider shorter string to be less if longer string starts with shorter one" do
+ ("abc" <=> "abcd").should == -1
+ ("abcd" <=> "abc").should == 1
+ end
+
+ it "compares shorter string with corresponding number of first chars of longer string" do
+ ("abx" <=> "abcd").should == 1
+ ("abcd" <=> "abx").should == -1
+ end
+
+ it "ignores subclass differences" do
+ a = "hello"
+ b = StringSpecs::MyString.new("hello")
+
+ (a <=> b).should == 0
+ (b <=> a).should == 0
+ end
+
+ it "returns 0 if self and other are bytewise identical and have the same encoding" do
+ ("ÄÖÜ" <=> "ÄÖÜ").should == 0
+ end
+
+ it "returns 0 if self and other are bytewise identical and have the same encoding" do
+ ("ÄÖÜ" <=> "ÄÖÜ").should == 0
+ end
+
+ it "returns -1 if self is bytewise less than other" do
+ ("ÄÖÛ" <=> "ÄÖÜ").should == -1
+ end
+
+ it "returns 1 if self is bytewise greater than other" do
+ ("ÄÖÜ" <=> "ÄÖÛ").should == 1
+ end
+
+ it "ignores encoding difference" do
+ ("ÄÖÛ".force_encoding("utf-8") <=> "ÄÖÜ".force_encoding("iso-8859-1")).should == -1
+ ("ÄÖÜ".force_encoding("utf-8") <=> "ÄÖÛ".force_encoding("iso-8859-1")).should == 1
+ end
+
+ it "returns 0 with identical ASCII-compatible bytes of different encodings" do
+ ("abc".force_encoding("utf-8") <=> "abc".force_encoding("iso-8859-1")).should == 0
+ end
+
+ it "compares the indices of the encodings when the strings have identical non-ASCII-compatible bytes" do
+ xff_1 = [0xFF].pack('C').force_encoding("utf-8")
+ xff_2 = [0xFF].pack('C').force_encoding("iso-8859-1")
+ (xff_1 <=> xff_2).should == -1
+ (xff_2 <=> xff_1).should == 1
+ end
+end
+
+# Note: This is inconsistent with Array#<=> which calls #to_ary instead of
+# just using it as an indicator.
+describe "String#<=>" do
+ it "returns nil if its argument provides neither #to_str nor #<=>" do
+ ("abc" <=> mock('x')).should be_nil
+ end
+
+ it "uses the result of calling #to_str for comparison when #to_str is defined" do
+ obj = mock('x')
+ obj.should_receive(:to_str).and_return("aaa")
+
+ ("abc" <=> obj).should == 1
+ end
+
+ it "uses the result of calling #<=> on its argument when #<=> is defined but #to_str is not" do
+ obj = mock('x')
+ obj.should_receive(:<=>).and_return(-1)
+
+ ("abc" <=> obj).should == 1
+ end
+
+ it "returns nil if argument also uses an inverse comparison for <=>" do
+ obj = mock('x')
+ def obj.<=>(other); other <=> self; end
+ obj.should_receive(:<=>).once
+
+ ("abc" <=> obj).should be_nil
+ end
+end
diff --git a/spec/ruby/core/string/concat_spec.rb b/spec/ruby/core/string/concat_spec.rb
new file mode 100644
index 0000000000..27917d6c85
--- /dev/null
+++ b/spec/ruby/core/string/concat_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/concat'
+
+describe "String#concat" do
+ it_behaves_like :string_concat, :concat
+ it_behaves_like :string_concat_encoding, :concat
+
+ ruby_version_is "2.4" do
+ it "takes multiple arguments" do
+ str = "hello "
+ str.concat "wo", "", "rld"
+ str.should == "hello world"
+ end
+
+ it "concatenates the initial value when given arguments contain 2 self" do
+ str = "hello"
+ str.concat str, str
+ str.should == "hellohellohello"
+ end
+
+ it "returns self when given no arguments" do
+ str = "hello"
+ str.concat.should equal(str)
+ str.should == "hello"
+ end
+ end
+end
diff --git a/spec/ruby/core/string/count_spec.rb b/spec/ruby/core/string/count_spec.rb
new file mode 100644
index 0000000000..e82a7c77ca
--- /dev/null
+++ b/spec/ruby/core/string/count_spec.rb
@@ -0,0 +1,105 @@
+# -*- encoding: binary -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#count" do
+ it "counts occurrences of chars from the intersection of the specified sets" do
+ s = "hello\nworld\x00\x00"
+
+ s.count(s).should == s.size
+ s.count("lo").should == 5
+ s.count("eo").should == 3
+ s.count("l").should == 3
+ s.count("\n").should == 1
+ s.count("\x00").should == 2
+
+ s.count("").should == 0
+ "".count("").should == 0
+
+ s.count("l", "lo").should == s.count("l")
+ s.count("l", "lo", "o").should == s.count("")
+ s.count("helo", "hel", "h").should == s.count("h")
+ s.count("helo", "", "x").should == 0
+ end
+
+ it "raises an ArgumentError when given no arguments" do
+ lambda { "hell yeah".count }.should raise_error(ArgumentError)
+ end
+
+ it "negates sets starting with ^" do
+ s = "^hello\nworld\x00\x00"
+
+ s.count("^").should == 1 # no negation, counts ^
+
+ s.count("^leh").should == 9
+ s.count("^o").should == 12
+
+ s.count("helo", "^el").should == s.count("ho")
+ s.count("aeiou", "^e").should == s.count("aiou")
+
+ "^_^".count("^^").should == 1
+ "oa^_^o".count("a^").should == 3
+ end
+
+ it "counts all chars in a sequence" do
+ s = "hel-[()]-lo012^"
+
+ s.count("\x00-\xFF").should == s.size
+ s.count("ej-m").should == 3
+ s.count("e-h").should == 2
+
+ # no sequences
+ s.count("-").should == 2
+ s.count("e-").should == s.count("e") + s.count("-")
+ s.count("-h").should == s.count("h") + s.count("-")
+
+ s.count("---").should == s.count("-")
+
+ # see an ASCII table for reference
+ s.count("--2").should == s.count("-./012")
+ s.count("(--").should == s.count("()*+,-")
+ s.count("A-a").should == s.count("A-Z[\\]^_`a")
+
+ # negated sequences
+ s.count("^e-h").should == s.size - s.count("e-h")
+ s.count("^^-^").should == s.size - s.count("^")
+ s.count("^---").should == s.size - s.count("-")
+
+ "abcdefgh".count("a-ce-fh").should == 6
+ "abcdefgh".count("he-fa-c").should == 6
+ "abcdefgh".count("e-fha-c").should == 6
+
+ "abcde".count("ac-e").should == 4
+ "abcde".count("^ac-e").should == 1
+ end
+
+ it "raises if the given sequences are invalid" do
+ s = "hel-[()]-lo012^"
+
+ lambda { s.count("h-e") }.should raise_error(ArgumentError)
+ lambda { s.count("^h-e") }.should raise_error(ArgumentError)
+ end
+
+ it 'returns the number of occurrences of a multi-byte character' do
+ str = "\u{2605}"
+ str.count(str).should == 1
+ "asd#{str}zzz#{str}ggg".count(str).should == 2
+ end
+
+ it "calls #to_str to convert each set arg to a String" do
+ other_string = mock('lo')
+ other_string.should_receive(:to_str).and_return("lo")
+
+ other_string2 = mock('o')
+ other_string2.should_receive(:to_str).and_return("o")
+
+ s = "hello world"
+ s.count(other_string, other_string2).should == s.count("o")
+ end
+
+ it "raises a TypeError when a set arg can't be converted to a string" do
+ lambda { "hello world".count(100) }.should raise_error(TypeError)
+ lambda { "hello world".count([]) }.should raise_error(TypeError)
+ lambda { "hello world".count(mock('x')) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/string/crypt_spec.rb b/spec/ruby/core/string/crypt_spec.rb
new file mode 100644
index 0000000000..01d3830892
--- /dev/null
+++ b/spec/ruby/core/string/crypt_spec.rb
@@ -0,0 +1,73 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#crypt" do
+ # Note: MRI's documentation just says that the C stdlib function crypt() is
+ # called.
+ #
+ # I'm not sure if crypt() is guaranteed to produce the same result across
+ # different platforms. It seems that there is one standard UNIX implementation
+ # of crypt(), but that alternative implementations are possible. See
+ # http://www.unix.org.ua/orelly/networking/puis/ch08_06.htm
+ it "returns a cryptographic hash of self by applying the UNIX crypt algorithm with the specified salt" do
+ "".crypt("aa").should == "aaQSqAReePlq6"
+ "nutmeg".crypt("Mi").should == "MiqkFWCm1fNJI"
+ "ellen1".crypt("ri").should == "ri79kNd7V6.Sk"
+ "Sharon".crypt("./").should == "./UY9Q7TvYJDg"
+ "norahs".crypt("am").should == "amfIADT2iqjA."
+ "norahs".crypt("7a").should == "7azfT5tIdyh0I"
+
+ # Only uses first 8 chars of string
+ "01234567".crypt("aa").should == "aa4c4gpuvCkSE"
+ "012345678".crypt("aa").should == "aa4c4gpuvCkSE"
+ "0123456789".crypt("aa").should == "aa4c4gpuvCkSE"
+
+ # Only uses first 2 chars of salt
+ "hello world".crypt("aa").should == "aayPz4hyPS1wI"
+ "hello world".crypt("aab").should == "aayPz4hyPS1wI"
+ "hello world".crypt("aabc").should == "aayPz4hyPS1wI"
+ end
+
+ it "raises an ArgumentError when the salt is shorter than two characters" do
+ lambda { "hello".crypt("") }.should raise_error(ArgumentError)
+ lambda { "hello".crypt("f") }.should raise_error(ArgumentError)
+ lambda { "hello".crypt("\x00\x00") }.should raise_error(ArgumentError)
+ lambda { "hello".crypt("\x00a") }.should raise_error(ArgumentError)
+ lambda { "hello".crypt("a\x00") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the string contains NUL character" do
+ lambda { "poison\0null".crypt("aa") }.should raise_error(ArgumentError)
+ end
+
+ it "calls #to_str to converts the salt arg to a String" do
+ obj = mock('aa')
+ obj.should_receive(:to_str).and_return("aa")
+
+ "".crypt(obj).should == "aaQSqAReePlq6"
+ end
+
+ it "raises a type error when the salt arg can't be converted to a string" do
+ lambda { "".crypt(5) }.should raise_error(TypeError)
+ lambda { "".crypt(mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "taints the result if either salt or self is tainted" do
+ tainted_salt = "aa"
+ tainted_str = "hello"
+
+ tainted_salt.taint
+ tainted_str.taint
+
+ "hello".crypt("aa").tainted?.should == false
+ tainted_str.crypt("aa").tainted?.should == true
+ "hello".crypt(tainted_salt).tainted?.should == true
+ tainted_str.crypt(tainted_salt).tainted?.should == true
+ end
+
+ it "doesn't return subclass instances" do
+ StringSpecs::MyString.new("hello").crypt("aa").should be_an_instance_of(String)
+ "hello".crypt(StringSpecs::MyString.new("aa")).should be_an_instance_of(String)
+ StringSpecs::MyString.new("hello").crypt(StringSpecs::MyString.new("aa")).should be_an_instance_of(String)
+ end
+end
diff --git a/spec/ruby/core/string/delete_prefix_spec.rb b/spec/ruby/core/string/delete_prefix_spec.rb
new file mode 100644
index 0000000000..4b4858c9c2
--- /dev/null
+++ b/spec/ruby/core/string/delete_prefix_spec.rb
@@ -0,0 +1,81 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is '2.5' do
+ describe "String#delete_prefix" do
+ it "returns a copy of the string, with the given prefix removed" do
+ 'hello'.delete_prefix('hell').should == 'o'
+ 'hello'.delete_prefix('hello').should == ''
+ end
+
+ it "returns a copy of the string, when the prefix isn't found" do
+ s = 'hello'
+ r = s.delete_prefix('hello!')
+ r.should_not equal s
+ r.should == s
+ r = s.delete_prefix('ell')
+ r.should_not equal s
+ r.should == s
+ r = s.delete_prefix('')
+ r.should_not equal s
+ r.should == s
+ end
+
+ it "taints resulting strings when other is tainted" do
+ 'hello'.taint.delete_prefix('hell').tainted?.should == true
+ 'hello'.taint.delete_prefix('').tainted?.should == true
+ end
+
+ it "doesn't set $~" do
+ $~ = nil
+
+ 'hello'.delete_prefix('hell')
+ $~.should == nil
+ end
+
+ it "calls to_str on its argument" do
+ o = mock('x')
+ o.should_receive(:to_str).and_return 'hell'
+ 'hello'.delete_prefix(o).should == 'o'
+ end
+
+ it "returns a subclass instance when called on a subclass instance" do
+ s = StringSpecs::MyString.new('hello')
+ s.delete_prefix('hell').should be_an_instance_of(StringSpecs::MyString)
+ end
+ end
+
+ describe "String#delete_prefix!" do
+ it "removes the found prefix" do
+ s = 'hello'
+ s.delete_prefix!('hell').should equal(s)
+ s.should == 'o'
+ end
+
+ it "returns nil if no change is made" do
+ s = 'hello'
+ s.delete_prefix!('ell').should == nil
+ s.delete_prefix!('').should == nil
+ end
+
+ it "doesn't set $~" do
+ $~ = nil
+
+ 'hello'.delete_prefix!('hell')
+ $~.should == nil
+ end
+
+ it "calls to_str on its argument" do
+ o = mock('x')
+ o.should_receive(:to_str).and_return 'hell'
+ 'hello'.delete_prefix!(o).should == 'o'
+ end
+
+ it "raises a #{frozen_error_class} when self is frozen" do
+ lambda { 'hello'.freeze.delete_prefix!('hell') }.should raise_error(frozen_error_class)
+ lambda { 'hello'.freeze.delete_prefix!('') }.should raise_error(frozen_error_class)
+ lambda { ''.freeze.delete_prefix!('') }.should raise_error(frozen_error_class)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/delete_spec.rb b/spec/ruby/core/string/delete_spec.rb
new file mode 100644
index 0000000000..8725b54c3b
--- /dev/null
+++ b/spec/ruby/core/string/delete_spec.rb
@@ -0,0 +1,119 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#delete" do
+ it "returns a new string with the chars from the intersection of sets removed" do
+ s = "hello"
+ s.delete("lo").should == "he"
+ s.should == "hello"
+
+ "hello".delete("l", "lo").should == "heo"
+
+ "hell yeah".delete("").should == "hell yeah"
+ end
+
+ it "raises an ArgumentError when given no arguments" do
+ lambda { "hell yeah".delete }.should raise_error(ArgumentError)
+ end
+
+ it "negates sets starting with ^" do
+ "hello".delete("aeiou", "^e").should == "hell"
+ "hello".delete("^leh").should == "hell"
+ "hello".delete("^o").should == "o"
+ "hello".delete("^").should == "hello"
+ "^_^".delete("^^").should == "^^"
+ "oa^_^o".delete("a^").should == "o_o"
+ end
+
+ it "deletes all chars in a sequence" do
+ "hello".delete("ej-m").should == "ho"
+ "hello".delete("e-h").should == "llo"
+ "hel-lo".delete("e-").should == "hllo"
+ "hel-lo".delete("-h").should == "ello"
+ "hel-lo".delete("---").should == "hello"
+ "hel-012".delete("--2").should == "hel"
+ "hel-()".delete("(--").should == "hel"
+ "hello".delete("^e-h").should == "he"
+ "hello^".delete("^^-^").should == "^"
+ "hel--lo".delete("^---").should == "--"
+
+ "abcdefgh".delete("a-ce-fh").should == "dg"
+ "abcdefgh".delete("he-fa-c").should == "dg"
+ "abcdefgh".delete("e-fha-c").should == "dg"
+
+ "abcde".delete("ac-e").should == "b"
+ "abcde".delete("^ac-e").should == "acde"
+
+ "ABCabc[]".delete("A-a").should == "bc"
+ end
+
+ it "deletes multibyte characters" do
+ "四月".delete("月").should == "四"
+ '哥哥我倒'.delete('哥').should == "我倒"
+ end
+
+ it "respects backslash for escaping a -" do
+ 'Non-Authoritative Information'.delete(' \-\'').should ==
+ 'NonAuthoritativeInformation'
+ end
+
+ it "raises if the given ranges are invalid" do
+ not_supported_on :opal do
+ xFF = [0xFF].pack('C')
+ range = "\x00 - #{xFF}".force_encoding('utf-8')
+ lambda { "hello".delete(range).should == "" }.should raise_error(ArgumentError)
+ end
+ lambda { "hello".delete("h-e") }.should raise_error(ArgumentError)
+ lambda { "hello".delete("^h-e") }.should raise_error(ArgumentError)
+ end
+
+ it "taints result when self is tainted" do
+ "hello".taint.delete("e").tainted?.should == true
+ "hello".taint.delete("a-z").tainted?.should == true
+
+ "hello".delete("e".taint).tainted?.should == false
+ end
+
+ it "tries to convert each set arg to a string using to_str" do
+ other_string = mock('lo')
+ other_string.should_receive(:to_str).and_return("lo")
+
+ other_string2 = mock('o')
+ other_string2.should_receive(:to_str).and_return("o")
+
+ "hello world".delete(other_string, other_string2).should == "hell wrld"
+ end
+
+ it "raises a TypeError when one set arg can't be converted to a string" do
+ lambda { "hello world".delete(100) }.should raise_error(TypeError)
+ lambda { "hello world".delete([]) }.should raise_error(TypeError)
+ lambda { "hello world".delete(mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "returns subclass instances when called on a subclass" do
+ StringSpecs::MyString.new("oh no!!!").delete("!").should be_an_instance_of(StringSpecs::MyString)
+ end
+end
+
+describe "String#delete!" do
+ it "modifies self in place and returns self" do
+ a = "hello"
+ a.delete!("aeiou", "^e").should equal(a)
+ a.should == "hell"
+ end
+
+ it "returns nil if no modifications were made" do
+ a = "hello"
+ a.delete!("z").should == nil
+ a.should == "hello"
+ end
+
+ it "raises a #{frozen_error_class} when self is frozen" do
+ a = "hello"
+ a.freeze
+
+ lambda { a.delete!("") }.should raise_error(frozen_error_class)
+ lambda { a.delete!("aeiou", "^e") }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/string/delete_suffix_spec.rb b/spec/ruby/core/string/delete_suffix_spec.rb
new file mode 100644
index 0000000000..8541c1d5d2
--- /dev/null
+++ b/spec/ruby/core/string/delete_suffix_spec.rb
@@ -0,0 +1,81 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is '2.5' do
+ describe "String#delete_suffix" do
+ it "returns a copy of the string, with the given suffix removed" do
+ 'hello'.delete_suffix('ello').should == 'h'
+ 'hello'.delete_suffix('hello').should == ''
+ end
+
+ it "returns a copy of the string, when the suffix isn't found" do
+ s = 'hello'
+ r = s.delete_suffix('!hello')
+ r.should_not equal s
+ r.should == s
+ r = s.delete_suffix('ell')
+ r.should_not equal s
+ r.should == s
+ r = s.delete_suffix('')
+ r.should_not equal s
+ r.should == s
+ end
+
+ it "taints resulting strings when other is tainted" do
+ 'hello'.taint.delete_suffix('ello').tainted?.should == true
+ 'hello'.taint.delete_suffix('').tainted?.should == true
+ end
+
+ it "doesn't set $~" do
+ $~ = nil
+
+ 'hello'.delete_suffix('ello')
+ $~.should == nil
+ end
+
+ it "calls to_str on its argument" do
+ o = mock('x')
+ o.should_receive(:to_str).and_return 'ello'
+ 'hello'.delete_suffix(o).should == 'h'
+ end
+
+ it "returns a subclass instance when called on a subclass instance" do
+ s = StringSpecs::MyString.new('hello')
+ s.delete_suffix('ello').should be_an_instance_of(StringSpecs::MyString)
+ end
+ end
+
+ describe "String#delete_suffix!" do
+ it "removes the found prefix" do
+ s = 'hello'
+ s.delete_suffix!('ello').should equal(s)
+ s.should == 'h'
+ end
+
+ it "returns nil if no change is made" do
+ s = 'hello'
+ s.delete_suffix!('ell').should == nil
+ s.delete_suffix!('').should == nil
+ end
+
+ it "doesn't set $~" do
+ $~ = nil
+
+ 'hello'.delete_suffix!('ello')
+ $~.should == nil
+ end
+
+ it "calls to_str on its argument" do
+ o = mock('x')
+ o.should_receive(:to_str).and_return 'ello'
+ 'hello'.delete_suffix!(o).should == 'h'
+ end
+
+ it "raises a #{frozen_error_class} when self is frozen" do
+ lambda { 'hello'.freeze.delete_suffix!('ello') }.should raise_error(frozen_error_class)
+ lambda { 'hello'.freeze.delete_suffix!('') }.should raise_error(frozen_error_class)
+ lambda { ''.freeze.delete_suffix!('') }.should raise_error(frozen_error_class)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/downcase_spec.rb b/spec/ruby/core/string/downcase_spec.rb
new file mode 100644
index 0000000000..9fb93902b1
--- /dev/null
+++ b/spec/ruby/core/string/downcase_spec.rb
@@ -0,0 +1,200 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#downcase" do
+ it "returns a copy of self with all uppercase letters downcased" do
+ "hELLO".downcase.should == "hello"
+ "hello".downcase.should == "hello"
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "is locale insensitive (only replaces A-Z)" do
+ "ÄÖÜ".downcase.should == "ÄÖÜ"
+
+ str = Array.new(256) { |c| c.chr }.join
+ expected = Array.new(256) do |i|
+ c = i.chr
+ c.between?("A", "Z") ? c.downcase : c
+ end.join
+
+ str.downcase.should == expected
+ end
+ end
+
+ ruby_version_is '2.4' do
+ describe "full Unicode case mapping" do
+ it "works for all of Unicode with no option" do
+ "ÄÖÜ".downcase.should == "äöü"
+ end
+
+ it "updates string metadata" do
+ downcased = "\u{212A}ING".downcase
+
+ downcased.should == "king"
+ downcased.size.should == 4
+ downcased.bytesize.should == 4
+ downcased.ascii_only?.should be_true
+ end
+ end
+
+ describe "ASCII-only case mapping" do
+ it "does not downcase non-ASCII characters" do
+ "Câ„«R".downcase(:ascii).should == "câ„«r"
+ end
+ end
+
+ describe "full Unicode case mapping adapted for Turkic languages" do
+ it "downcases characters according to Turkic semantics" do
+ "İ".downcase(:turkic).should == "i"
+ end
+
+ it "allows Lithuanian as an extra option" do
+ "İ".downcase(:turkic, :lithuanian).should == "i"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { "İ".downcase(:turkic, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "full Unicode case mapping adapted for Lithuanian" do
+ it "currently works the same as full Unicode case mapping" do
+ "İS".downcase(:lithuanian).should == "i\u{307}s"
+ end
+
+ it "allows Turkic as an extra option (and applies Turkic semantics)" do
+ "İS".downcase(:lithuanian, :turkic).should == "is"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { "İS".downcase(:lithuanian, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "case folding" do
+ it "case folds special characters" do
+ "ß".downcase.should == "ß"
+ "ß".downcase(:fold).should == "ss"
+ end
+ end
+
+ it "does not allow invalid options" do
+ lambda { "ABC".downcase(:invalid_option) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "taints result when self is tainted" do
+ "".taint.downcase.tainted?.should == true
+ "x".taint.downcase.tainted?.should == true
+ "X".taint.downcase.tainted?.should == true
+ end
+
+ it "returns a subclass instance for subclasses" do
+ StringSpecs::MyString.new("FOObar").downcase.should be_an_instance_of(StringSpecs::MyString)
+ end
+end
+
+describe "String#downcase!" do
+ it "modifies self in place" do
+ a = "HeLlO"
+ a.downcase!.should equal(a)
+ a.should == "hello"
+ end
+
+ ruby_version_is '2.4' do
+ describe "full Unicode case mapping" do
+ it "modifies self in place for all of Unicode with no option" do
+ a = "ÄÖÜ"
+ a.downcase!
+ a.should == "äöü"
+ end
+
+ it "updates string metadata" do
+ downcased = "\u{212A}ING"
+ downcased.downcase!
+
+ downcased.should == "king"
+ downcased.size.should == 4
+ downcased.bytesize.should == 4
+ downcased.ascii_only?.should be_true
+ end
+ end
+
+ describe "ASCII-only case mapping" do
+ it "does not downcase non-ASCII characters" do
+ a = "Câ„«R"
+ a.downcase!(:ascii)
+ a.should == "câ„«r"
+ end
+ end
+
+ describe "full Unicode case mapping adapted for Turkic languages" do
+ it "downcases characters according to Turkic semantics" do
+ a = "İ"
+ a.downcase!(:turkic)
+ a.should == "i"
+ end
+
+ it "allows Lithuanian as an extra option" do
+ a = "İ"
+ a.downcase!(:turkic, :lithuanian)
+ a.should == "i"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { a = "İ"; a.downcase!(:turkic, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "full Unicode case mapping adapted for Lithuanian" do
+ it "currently works the same as full Unicode case mapping" do
+ a = "İS"
+ a.downcase!(:lithuanian)
+ a.should == "i\u{307}s"
+ end
+
+ it "allows Turkic as an extra option (and applies Turkic semantics)" do
+ a = "İS"
+ a.downcase!(:lithuanian, :turkic)
+ a.should == "is"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { a = "İS"; a.downcase!(:lithuanian, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "case folding" do
+ it "case folds special characters" do
+ a = "ß"
+ a.downcase!
+ a.should == "ß"
+
+ a.downcase!(:fold)
+ a.should == "ss"
+ end
+ end
+
+ it "does not allow invalid options" do
+ lambda { a = "ABC"; a.downcase!(:invalid_option) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "returns nil if no modifications were made" do
+ a = "hello"
+ a.downcase!.should == nil
+ a.should == "hello"
+ end
+
+ it "raises a #{frozen_error_class} when self is frozen" do
+ lambda { "HeLlo".freeze.downcase! }.should raise_error(frozen_error_class)
+ lambda { "hello".freeze.downcase! }.should raise_error(frozen_error_class)
+ end
+
+ with_feature :encoding do
+ it "sets the result String encoding to the source String encoding" do
+ "ABC".downcase.encoding.should equal(Encoding::UTF_8)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/dump_spec.rb b/spec/ruby/core/string/dump_spec.rb
new file mode 100644
index 0000000000..e67367b5b0
--- /dev/null
+++ b/spec/ruby/core/string/dump_spec.rb
@@ -0,0 +1,439 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#dump" do
+ it "taints the result if self is tainted" do
+ "foo".taint.dump.tainted?.should == true
+ "foo\n".taint.dump.tainted?.should == true
+ end
+
+ it "untrusts the result if self is untrusted" do
+ "foo".untrust.dump.untrusted?.should == true
+ "foo\n".untrust.dump.untrusted?.should == true
+ end
+
+ it "does not take into account if a string is frozen" do
+ "foo".freeze.dump.frozen?.should == false
+ end
+
+ it "returns a subclass instance" do
+ StringSpecs::MyString.new.dump.should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ it "wraps string with \"" do
+ "foo".dump.should == '"foo"'
+ end
+
+ it "returns a string with special characters replaced with \\<char> notation" do
+ [ ["\a", '"\\a"'],
+ ["\b", '"\\b"'],
+ ["\t", '"\\t"'],
+ ["\n", '"\\n"'],
+ ["\v", '"\\v"'],
+ ["\f", '"\\f"'],
+ ["\r", '"\\r"'],
+ ["\e", '"\\e"']
+ ].should be_computed_by(:dump)
+ end
+
+ it "returns a string with \" and \\ escaped with a backslash" do
+ [ ["\"", '"\\""'],
+ ["\\", '"\\\\"']
+ ].should be_computed_by(:dump)
+ end
+
+ it "returns a string with \\#<char> when # is followed by $, @, @@, {" do
+ [ ["\#$PATH", '"\\#$PATH"'],
+ ["\#@a", '"\\#@a"'],
+ ["\#@@a", '"\\#@@a"'],
+ ["\#{a}", '"\\#{a}"']
+ ].should be_computed_by(:dump)
+ end
+
+ it "returns a string with # not escaped when followed by any other character" do
+ [ ["#", '"#"'],
+ ["#1", '"#1"']
+ ].should be_computed_by(:dump)
+ end
+
+ it "returns a string with printable non-alphanumeric characters unescaped" do
+ [ [" ", '" "'],
+ ["!", '"!"'],
+ ["$", '"$"'],
+ ["%", '"%"'],
+ ["&", '"&"'],
+ ["'", '"\'"'],
+ ["(", '"("'],
+ [")", '")"'],
+ ["*", '"*"'],
+ ["+", '"+"'],
+ [",", '","'],
+ ["-", '"-"'],
+ [".", '"."'],
+ ["/", '"/"'],
+ [":", '":"'],
+ [";", '";"'],
+ ["<", '"<"'],
+ ["=", '"="'],
+ [">", '">"'],
+ ["?", '"?"'],
+ ["@", '"@"'],
+ ["[", '"["'],
+ ["]", '"]"'],
+ ["^", '"^"'],
+ ["_", '"_"'],
+ ["`", '"`"'],
+ ["{", '"{"'],
+ ["|", '"|"'],
+ ["}", '"}"'],
+ ["~", '"~"']
+ ].should be_computed_by(:dump)
+ end
+
+ it "returns a string with numeric characters unescaped" do
+ [ ["0", '"0"'],
+ ["1", '"1"'],
+ ["2", '"2"'],
+ ["3", '"3"'],
+ ["4", '"4"'],
+ ["5", '"5"'],
+ ["6", '"6"'],
+ ["7", '"7"'],
+ ["8", '"8"'],
+ ["9", '"9"'],
+ ].should be_computed_by(:dump)
+ end
+
+ it "returns a string with upper-case alpha characters unescaped" do
+ [ ["A", '"A"'],
+ ["B", '"B"'],
+ ["C", '"C"'],
+ ["D", '"D"'],
+ ["E", '"E"'],
+ ["F", '"F"'],
+ ["G", '"G"'],
+ ["H", '"H"'],
+ ["I", '"I"'],
+ ["J", '"J"'],
+ ["K", '"K"'],
+ ["L", '"L"'],
+ ["M", '"M"'],
+ ["N", '"N"'],
+ ["O", '"O"'],
+ ["P", '"P"'],
+ ["Q", '"Q"'],
+ ["R", '"R"'],
+ ["S", '"S"'],
+ ["T", '"T"'],
+ ["U", '"U"'],
+ ["V", '"V"'],
+ ["W", '"W"'],
+ ["X", '"X"'],
+ ["Y", '"Y"'],
+ ["Z", '"Z"']
+ ].should be_computed_by(:dump)
+ end
+
+ it "returns a string with lower-case alpha characters unescaped" do
+ [ ["a", '"a"'],
+ ["b", '"b"'],
+ ["c", '"c"'],
+ ["d", '"d"'],
+ ["e", '"e"'],
+ ["f", '"f"'],
+ ["g", '"g"'],
+ ["h", '"h"'],
+ ["i", '"i"'],
+ ["j", '"j"'],
+ ["k", '"k"'],
+ ["l", '"l"'],
+ ["m", '"m"'],
+ ["n", '"n"'],
+ ["o", '"o"'],
+ ["p", '"p"'],
+ ["q", '"q"'],
+ ["r", '"r"'],
+ ["s", '"s"'],
+ ["t", '"t"'],
+ ["u", '"u"'],
+ ["v", '"v"'],
+ ["w", '"w"'],
+ ["x", '"x"'],
+ ["y", '"y"'],
+ ["z", '"z"']
+ ].should be_computed_by(:dump)
+ end
+
+ it "returns a string with non-printing ASCII characters replaced by \\x notation" do
+ # Avoid the file encoding by computing the string with #chr.
+ [ [0000.chr, '"\\x00"'],
+ [0001.chr, '"\\x01"'],
+ [0002.chr, '"\\x02"'],
+ [0003.chr, '"\\x03"'],
+ [0004.chr, '"\\x04"'],
+ [0005.chr, '"\\x05"'],
+ [0006.chr, '"\\x06"'],
+ [0016.chr, '"\\x0E"'],
+ [0017.chr, '"\\x0F"'],
+ [0020.chr, '"\\x10"'],
+ [0021.chr, '"\\x11"'],
+ [0022.chr, '"\\x12"'],
+ [0023.chr, '"\\x13"'],
+ [0024.chr, '"\\x14"'],
+ [0025.chr, '"\\x15"'],
+ [0026.chr, '"\\x16"'],
+ [0027.chr, '"\\x17"'],
+ [0030.chr, '"\\x18"'],
+ [0031.chr, '"\\x19"'],
+ [0032.chr, '"\\x1A"'],
+ [0034.chr, '"\\x1C"'],
+ [0035.chr, '"\\x1D"'],
+ [0036.chr, '"\\x1E"'],
+ [0037.chr, '"\\x1F"'],
+ [0177.chr, '"\\x7F"'],
+ [0200.chr, '"\\x80"'],
+ [0201.chr, '"\\x81"'],
+ [0202.chr, '"\\x82"'],
+ [0203.chr, '"\\x83"'],
+ [0204.chr, '"\\x84"'],
+ [0205.chr, '"\\x85"'],
+ [0206.chr, '"\\x86"'],
+ [0207.chr, '"\\x87"'],
+ [0210.chr, '"\\x88"'],
+ [0211.chr, '"\\x89"'],
+ [0212.chr, '"\\x8A"'],
+ [0213.chr, '"\\x8B"'],
+ [0214.chr, '"\\x8C"'],
+ [0215.chr, '"\\x8D"'],
+ [0216.chr, '"\\x8E"'],
+ [0217.chr, '"\\x8F"'],
+ [0220.chr, '"\\x90"'],
+ [0221.chr, '"\\x91"'],
+ [0222.chr, '"\\x92"'],
+ [0223.chr, '"\\x93"'],
+ [0224.chr, '"\\x94"'],
+ [0225.chr, '"\\x95"'],
+ [0226.chr, '"\\x96"'],
+ [0227.chr, '"\\x97"'],
+ [0230.chr, '"\\x98"'],
+ [0231.chr, '"\\x99"'],
+ [0232.chr, '"\\x9A"'],
+ [0233.chr, '"\\x9B"'],
+ [0234.chr, '"\\x9C"'],
+ [0235.chr, '"\\x9D"'],
+ [0236.chr, '"\\x9E"'],
+ [0237.chr, '"\\x9F"'],
+ [0240.chr, '"\\xA0"'],
+ [0241.chr, '"\\xA1"'],
+ [0242.chr, '"\\xA2"'],
+ [0243.chr, '"\\xA3"'],
+ [0244.chr, '"\\xA4"'],
+ [0245.chr, '"\\xA5"'],
+ [0246.chr, '"\\xA6"'],
+ [0247.chr, '"\\xA7"'],
+ [0250.chr, '"\\xA8"'],
+ [0251.chr, '"\\xA9"'],
+ [0252.chr, '"\\xAA"'],
+ [0253.chr, '"\\xAB"'],
+ [0254.chr, '"\\xAC"'],
+ [0255.chr, '"\\xAD"'],
+ [0256.chr, '"\\xAE"'],
+ [0257.chr, '"\\xAF"'],
+ [0260.chr, '"\\xB0"'],
+ [0261.chr, '"\\xB1"'],
+ [0262.chr, '"\\xB2"'],
+ [0263.chr, '"\\xB3"'],
+ [0264.chr, '"\\xB4"'],
+ [0265.chr, '"\\xB5"'],
+ [0266.chr, '"\\xB6"'],
+ [0267.chr, '"\\xB7"'],
+ [0270.chr, '"\\xB8"'],
+ [0271.chr, '"\\xB9"'],
+ [0272.chr, '"\\xBA"'],
+ [0273.chr, '"\\xBB"'],
+ [0274.chr, '"\\xBC"'],
+ [0275.chr, '"\\xBD"'],
+ [0276.chr, '"\\xBE"'],
+ [0277.chr, '"\\xBF"'],
+ [0300.chr, '"\\xC0"'],
+ [0301.chr, '"\\xC1"'],
+ [0302.chr, '"\\xC2"'],
+ [0303.chr, '"\\xC3"'],
+ [0304.chr, '"\\xC4"'],
+ [0305.chr, '"\\xC5"'],
+ [0306.chr, '"\\xC6"'],
+ [0307.chr, '"\\xC7"'],
+ [0310.chr, '"\\xC8"'],
+ [0311.chr, '"\\xC9"'],
+ [0312.chr, '"\\xCA"'],
+ [0313.chr, '"\\xCB"'],
+ [0314.chr, '"\\xCC"'],
+ [0315.chr, '"\\xCD"'],
+ [0316.chr, '"\\xCE"'],
+ [0317.chr, '"\\xCF"'],
+ [0320.chr, '"\\xD0"'],
+ [0321.chr, '"\\xD1"'],
+ [0322.chr, '"\\xD2"'],
+ [0323.chr, '"\\xD3"'],
+ [0324.chr, '"\\xD4"'],
+ [0325.chr, '"\\xD5"'],
+ [0326.chr, '"\\xD6"'],
+ [0327.chr, '"\\xD7"'],
+ [0330.chr, '"\\xD8"'],
+ [0331.chr, '"\\xD9"'],
+ [0332.chr, '"\\xDA"'],
+ [0333.chr, '"\\xDB"'],
+ [0334.chr, '"\\xDC"'],
+ [0335.chr, '"\\xDD"'],
+ [0336.chr, '"\\xDE"'],
+ [0337.chr, '"\\xDF"'],
+ [0340.chr, '"\\xE0"'],
+ [0341.chr, '"\\xE1"'],
+ [0342.chr, '"\\xE2"'],
+ [0343.chr, '"\\xE3"'],
+ [0344.chr, '"\\xE4"'],
+ [0345.chr, '"\\xE5"'],
+ [0346.chr, '"\\xE6"'],
+ [0347.chr, '"\\xE7"'],
+ [0350.chr, '"\\xE8"'],
+ [0351.chr, '"\\xE9"'],
+ [0352.chr, '"\\xEA"'],
+ [0353.chr, '"\\xEB"'],
+ [0354.chr, '"\\xEC"'],
+ [0355.chr, '"\\xED"'],
+ [0356.chr, '"\\xEE"'],
+ [0357.chr, '"\\xEF"'],
+ [0360.chr, '"\\xF0"'],
+ [0361.chr, '"\\xF1"'],
+ [0362.chr, '"\\xF2"'],
+ [0363.chr, '"\\xF3"'],
+ [0364.chr, '"\\xF4"'],
+ [0365.chr, '"\\xF5"'],
+ [0366.chr, '"\\xF6"'],
+ [0367.chr, '"\\xF7"'],
+ [0370.chr, '"\\xF8"'],
+ [0371.chr, '"\\xF9"'],
+ [0372.chr, '"\\xFA"'],
+ [0373.chr, '"\\xFB"'],
+ [0374.chr, '"\\xFC"'],
+ [0375.chr, '"\\xFD"'],
+ [0376.chr, '"\\xFE"'],
+ [0377.chr, '"\\xFF"']
+ ].should be_computed_by(:dump)
+ end
+
+ it "returns a string with non-printing single-byte UTF-8 characters replaced by \\x notation" do
+ [ [0000.chr('utf-8'), '"\x00"'],
+ [0001.chr('utf-8'), '"\x01"'],
+ [0002.chr('utf-8'), '"\x02"'],
+ [0003.chr('utf-8'), '"\x03"'],
+ [0004.chr('utf-8'), '"\x04"'],
+ [0005.chr('utf-8'), '"\x05"'],
+ [0006.chr('utf-8'), '"\x06"'],
+ [0016.chr('utf-8'), '"\x0E"'],
+ [0017.chr('utf-8'), '"\x0F"'],
+ [0020.chr('utf-8'), '"\x10"'],
+ [0021.chr('utf-8'), '"\x11"'],
+ [0022.chr('utf-8'), '"\x12"'],
+ [0023.chr('utf-8'), '"\x13"'],
+ [0024.chr('utf-8'), '"\x14"'],
+ [0025.chr('utf-8'), '"\x15"'],
+ [0026.chr('utf-8'), '"\x16"'],
+ [0027.chr('utf-8'), '"\x17"'],
+ [0030.chr('utf-8'), '"\x18"'],
+ [0031.chr('utf-8'), '"\x19"'],
+ [0032.chr('utf-8'), '"\x1A"'],
+ [0034.chr('utf-8'), '"\x1C"'],
+ [0035.chr('utf-8'), '"\x1D"'],
+ [0036.chr('utf-8'), '"\x1E"'],
+ [0037.chr('utf-8'), '"\x1F"'],
+ [0177.chr('utf-8'), '"\x7F"']
+ ].should be_computed_by(:dump)
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "returns a string with multi-byte UTF-8 characters replaced by \\u{} notation with lower-case hex digits" do
+ [ [0200.chr('utf-8'), '"\u{80}"'],
+ [0201.chr('utf-8'), '"\u{81}"'],
+ [0202.chr('utf-8'), '"\u{82}"'],
+ [0203.chr('utf-8'), '"\u{83}"'],
+ [0204.chr('utf-8'), '"\u{84}"'],
+ [0206.chr('utf-8'), '"\u{86}"'],
+ [0207.chr('utf-8'), '"\u{87}"'],
+ [0210.chr('utf-8'), '"\u{88}"'],
+ [0211.chr('utf-8'), '"\u{89}"'],
+ [0212.chr('utf-8'), '"\u{8a}"'],
+ [0213.chr('utf-8'), '"\u{8b}"'],
+ [0214.chr('utf-8'), '"\u{8c}"'],
+ [0215.chr('utf-8'), '"\u{8d}"'],
+ [0216.chr('utf-8'), '"\u{8e}"'],
+ [0217.chr('utf-8'), '"\u{8f}"'],
+ [0220.chr('utf-8'), '"\u{90}"'],
+ [0221.chr('utf-8'), '"\u{91}"'],
+ [0222.chr('utf-8'), '"\u{92}"'],
+ [0223.chr('utf-8'), '"\u{93}"'],
+ [0224.chr('utf-8'), '"\u{94}"'],
+ [0225.chr('utf-8'), '"\u{95}"'],
+ [0226.chr('utf-8'), '"\u{96}"'],
+ [0227.chr('utf-8'), '"\u{97}"'],
+ [0230.chr('utf-8'), '"\u{98}"'],
+ [0231.chr('utf-8'), '"\u{99}"'],
+ [0232.chr('utf-8'), '"\u{9a}"'],
+ [0233.chr('utf-8'), '"\u{9b}"'],
+ [0234.chr('utf-8'), '"\u{9c}"'],
+ [0235.chr('utf-8'), '"\u{9d}"'],
+ [0236.chr('utf-8'), '"\u{9e}"'],
+ [0237.chr('utf-8'), '"\u{9f}"'],
+ ].should be_computed_by(:dump)
+ end
+ end
+
+ ruby_version_is '2.4' do
+ it "returns a string with multi-byte UTF-8 characters replaced by \\u{} notation with upper-case hex digits" do
+ [ [0200.chr('utf-8'), '"\u0080"'],
+ [0201.chr('utf-8'), '"\u0081"'],
+ [0202.chr('utf-8'), '"\u0082"'],
+ [0203.chr('utf-8'), '"\u0083"'],
+ [0204.chr('utf-8'), '"\u0084"'],
+ [0206.chr('utf-8'), '"\u0086"'],
+ [0207.chr('utf-8'), '"\u0087"'],
+ [0210.chr('utf-8'), '"\u0088"'],
+ [0211.chr('utf-8'), '"\u0089"'],
+ [0212.chr('utf-8'), '"\u008A"'],
+ [0213.chr('utf-8'), '"\u008B"'],
+ [0214.chr('utf-8'), '"\u008C"'],
+ [0215.chr('utf-8'), '"\u008D"'],
+ [0216.chr('utf-8'), '"\u008E"'],
+ [0217.chr('utf-8'), '"\u008F"'],
+ [0220.chr('utf-8'), '"\u0090"'],
+ [0221.chr('utf-8'), '"\u0091"'],
+ [0222.chr('utf-8'), '"\u0092"'],
+ [0223.chr('utf-8'), '"\u0093"'],
+ [0224.chr('utf-8'), '"\u0094"'],
+ [0225.chr('utf-8'), '"\u0095"'],
+ [0226.chr('utf-8'), '"\u0096"'],
+ [0227.chr('utf-8'), '"\u0097"'],
+ [0230.chr('utf-8'), '"\u0098"'],
+ [0231.chr('utf-8'), '"\u0099"'],
+ [0232.chr('utf-8'), '"\u009A"'],
+ [0233.chr('utf-8'), '"\u009B"'],
+ [0234.chr('utf-8'), '"\u009C"'],
+ [0235.chr('utf-8'), '"\u009D"'],
+ [0236.chr('utf-8'), '"\u009E"'],
+ [0237.chr('utf-8'), '"\u009F"'],
+ ].should be_computed_by(:dump)
+ end
+ end
+
+ it "includes .force_encoding(name) if the encoding isn't ASCII compatible" do
+ "\u{876}".encode('utf-16be').dump.end_with?(".force_encoding(\"UTF-16BE\")").should be_true
+ "\u{876}".encode('utf-16le').dump.end_with?(".force_encoding(\"UTF-16LE\")").should be_true
+ end
+
+ it "keeps origin encoding" 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
+ end
+end
diff --git a/spec/ruby/core/string/dup_spec.rb b/spec/ruby/core/string/dup_spec.rb
new file mode 100644
index 0000000000..3c6021b5c2
--- /dev/null
+++ b/spec/ruby/core/string/dup_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#dup" do
+ before :each do
+ ScratchPad.clear
+ @obj = StringSpecs::InitializeString.new "string"
+ end
+
+ it "calls #initialize_copy on the new instance" do
+ dup = @obj.dup
+ ScratchPad.recorded.should_not == @obj.object_id
+ ScratchPad.recorded.should == dup.object_id
+ end
+
+ it "copies instance variables" do
+ dup = @obj.dup
+ dup.ivar.should == 1
+ end
+
+ it "does not copy singleton methods" do
+ def @obj.special() :the_one end
+ dup = @obj.dup
+ lambda { dup.special }.should raise_error(NameError)
+ end
+
+ it "does not copy modules included in the singleton class" do
+ class << @obj
+ include StringSpecs::StringModule
+ end
+
+ dup = @obj.dup
+ lambda { dup.repr }.should raise_error(NameError)
+ end
+
+ it "does not copy constants defined in the singleton class" do
+ class << @obj
+ CLONE = :clone
+ end
+
+ dup = @obj.dup
+ lambda { class << dup; CLONE; end }.should raise_error(NameError)
+ end
+
+ it "does not modify the original string when changing dupped string" do
+ orig = "string"[0..100]
+ dup = orig.dup
+ orig[0] = 'x'
+ orig.should == "xtring"
+ dup.should == "string"
+ end
+end
diff --git a/spec/ruby/core/string/each_byte_spec.rb b/spec/ruby/core/string/each_byte_spec.rb
new file mode 100644
index 0000000000..e04dca807f
--- /dev/null
+++ b/spec/ruby/core/string/each_byte_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#each_byte" do
+ it "passes each byte in self to the given block" do
+ a = []
+ "hello\x00".each_byte { |c| a << c }
+ a.should == [104, 101, 108, 108, 111, 0]
+ end
+
+ it "keeps iterating from the old position (to new string end) when self changes" do
+ r = ""
+ s = "hello world"
+ s.each_byte do |c|
+ r << c
+ s.insert(0, "<>") if r.size < 3
+ end
+ r.should == "h><>hello world"
+
+ r = ""
+ s = "hello world"
+ s.each_byte { |c| s.slice!(-1); r << c }
+ r.should == "hello "
+
+ r = ""
+ s = "hello world"
+ s.each_byte { |c| s.slice!(0); r << c }
+ r.should == "hlowrd"
+
+ r = ""
+ s = "hello world"
+ s.each_byte { |c| s.slice!(0..-1); r << c }
+ r.should == "h"
+ end
+
+ it "returns self" do
+ s = "hello"
+ (s.each_byte {}).should equal(s)
+ end
+
+ describe "when no block is given" do
+ it "returns an enumerator" do
+ enum = "hello".each_byte
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.should == [104, 101, 108, 108, 111]
+ end
+
+ describe "returned enumerator" do
+ describe "size" do
+ it "should return the bytesize of the string" do
+ str = "hello"
+ str.each_byte.size.should == str.bytesize
+ str = "ola"
+ str.each_byte.size.should == str.bytesize
+ str = "\303\207\342\210\202\303\251\306\222g"
+ str.each_byte.size.should == str.bytesize
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/each_char_spec.rb b/spec/ruby/core/string/each_char_spec.rb
new file mode 100644
index 0000000000..aff98c0a5c
--- /dev/null
+++ b/spec/ruby/core/string/each_char_spec.rb
@@ -0,0 +1,7 @@
+require_relative 'shared/chars'
+require_relative 'shared/each_char_without_block'
+
+describe "String#each_char" do
+ it_behaves_like :string_chars, :each_char
+ it_behaves_like :string_each_char_without_block, :each_char
+end
diff --git a/spec/ruby/core/string/each_codepoint_spec.rb b/spec/ruby/core/string/each_codepoint_spec.rb
new file mode 100644
index 0000000000..41ca527653
--- /dev/null
+++ b/spec/ruby/core/string/each_codepoint_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'shared/codepoints'
+require_relative 'shared/each_codepoint_without_block'
+
+with_feature :encoding do
+ describe "String#each_codepoint" do
+ it_behaves_like :string_codepoints, :each_codepoint
+ it_behaves_like :string_each_codepoint_without_block, :each_codepoint
+ end
+end
diff --git a/spec/ruby/core/string/each_grapheme_cluster_spec.rb b/spec/ruby/core/string/each_grapheme_cluster_spec.rb
new file mode 100644
index 0000000000..5367f84887
--- /dev/null
+++ b/spec/ruby/core/string/each_grapheme_cluster_spec.rb
@@ -0,0 +1,11 @@
+require_relative 'shared/chars'
+require_relative 'shared/grapheme_clusters'
+require_relative 'shared/each_char_without_block'
+
+ruby_version_is "2.5" do
+ describe "String#each_grapheme_cluster" do
+ it_behaves_like :string_chars, :each_grapheme_cluster
+ it_behaves_like :string_grapheme_clusters, :each_grapheme_cluster
+ it_behaves_like :string_each_char_without_block, :each_grapheme_cluster
+ end
+end
diff --git a/spec/ruby/core/string/each_line_spec.rb b/spec/ruby/core/string/each_line_spec.rb
new file mode 100644
index 0000000000..90fc920bf1
--- /dev/null
+++ b/spec/ruby/core/string/each_line_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/each_line'
+require_relative 'shared/each_line_without_block'
+
+describe "String#each_line" do
+ it_behaves_like :string_each_line, :each_line
+ it_behaves_like :string_each_line_without_block, :each_line
+end
diff --git a/spec/ruby/core/string/element_reference_spec.rb b/spec/ruby/core/string/element_reference_spec.rb
new file mode 100644
index 0000000000..f6e1750c93
--- /dev/null
+++ b/spec/ruby/core/string/element_reference_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/slice'
+
+describe "String#[]" do
+ it_behaves_like :string_slice, :[]
+end
+
+describe "String#[] with index, length" do
+ it_behaves_like :string_slice_index_length, :[]
+end
+
+describe "String#[] with Range" do
+ it_behaves_like :string_slice_range, :[]
+end
+
+describe "String#[] with Regexp" do
+ it_behaves_like :string_slice_regexp, :[]
+end
+
+describe "String#[] with Regexp, index" do
+ it_behaves_like :string_slice_regexp_index, :[]
+end
+
+describe "String#[] with Regexp, group" do
+ it_behaves_like :string_slice_regexp_group, :[]
+end
+
+describe "String#[] with String" do
+ it_behaves_like :string_slice_string, :[]
+end
+
+describe "String#[] with Symbol" do
+ it_behaves_like :string_slice_symbol, :[]
+end
diff --git a/spec/ruby/core/string/element_set_spec.rb b/spec/ruby/core/string/element_set_spec.rb
new file mode 100644
index 0000000000..80ec5755c6
--- /dev/null
+++ b/spec/ruby/core/string/element_set_spec.rb
@@ -0,0 +1,612 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# TODO: Add missing String#[]= specs:
+# String#[re, idx] = obj
+
+describe "String#[]= with Fixnum index" do
+ it "replaces the char at idx with other_str" do
+ a = "hello"
+ a[0] = "bam"
+ a.should == "bamello"
+ a[-2] = ""
+ a.should == "bamelo"
+ end
+
+ it "taints self if other_str is tainted" do
+ a = "hello"
+ a[0] = "".taint
+ a.tainted?.should == true
+
+ a = "hello"
+ a[0] = "x".taint
+ a.tainted?.should == true
+ end
+
+ it "raises an IndexError without changing self if idx is outside of self" do
+ str = "hello"
+
+ lambda { str[20] = "bam" }.should raise_error(IndexError)
+ str.should == "hello"
+
+ lambda { str[-20] = "bam" }.should raise_error(IndexError)
+ str.should == "hello"
+
+ lambda { ""[-1] = "bam" }.should raise_error(IndexError)
+ end
+
+ # Behaviour verfieid correct by matz in
+ # http://redmine.ruby-lang.org/issues/show/1750
+ it "allows assignment to the zero'th element of an empty String" do
+ str = ""
+ str[0] = "bam"
+ str.should == "bam"
+ end
+
+ it "raises IndexError if the string index doesn't match a position in the string" do
+ str = "hello"
+ lambda { str['y'] = "bam" }.should raise_error(IndexError)
+ str.should == "hello"
+ end
+
+ it "raises a #{frozen_error_class} when self is frozen" do
+ a = "hello"
+ a.freeze
+
+ lambda { a[0] = "bam" }.should raise_error(frozen_error_class)
+ end
+
+ it "calls to_int on index" do
+ str = "hello"
+ str[0.5] = "hi "
+ str.should == "hi ello"
+
+ obj = mock('-1')
+ obj.should_receive(:to_int).and_return(-1)
+ str[obj] = "!"
+ str.should == "hi ell!"
+ end
+
+ it "calls #to_str to convert other to a String" do
+ other_str = mock('-test-')
+ other_str.should_receive(:to_str).and_return("-test-")
+
+ a = "abc"
+ a[1] = other_str
+ a.should == "a-test-c"
+ end
+
+ it "raises a TypeError if other_str can't be converted to a String" do
+ lambda { "test"[1] = [] }.should raise_error(TypeError)
+ lambda { "test"[1] = mock('x') }.should raise_error(TypeError)
+ lambda { "test"[1] = nil }.should raise_error(TypeError)
+ end
+
+ with_feature :encoding do
+ it "raises a TypeError if passed a Fixnum replacement" do
+ lambda { "abc"[1] = 65 }.should raise_error(TypeError)
+ end
+
+ it "raises an IndexError if the index is greater than character size" do
+ lambda { "ã‚れ"[4] = "a" }.should raise_error(IndexError)
+ end
+
+ it "calls #to_int to convert the index" do
+ index = mock("string element set")
+ index.should_receive(:to_int).and_return(1)
+
+ str = "ã‚れ"
+ str[index] = "a"
+ str.should == "ã‚a"
+ end
+
+ it "raises a TypeError if #to_int does not return an Fixnum" do
+ index = mock("string element set")
+ index.should_receive(:to_int).and_return('1')
+
+ lambda { "abc"[index] = "d" }.should raise_error(TypeError)
+ end
+
+ it "raises an IndexError if #to_int returns a value out of range" do
+ index = mock("string element set")
+ index.should_receive(:to_int).and_return(4)
+
+ lambda { "ab"[index] = "c" }.should raise_error(IndexError)
+ end
+
+ it "replaces a character with a multibyte character" do
+ str = "ã‚りãŒã¨u"
+ str[4] = "ã†"
+ str.should == "ã‚りãŒã¨ã†"
+ end
+
+ it "replaces a multibyte character with a character" do
+ str = "ã‚りãŒã¨ã†"
+ str[4] = "u"
+ str.should == "ã‚りãŒã¨u"
+ end
+
+ it "replaces a multibyte character with a multibyte character" do
+ str = "ã‚りãŒã¨ãŠ"
+ str[4] = "ã†"
+ str.should == "ã‚りãŒã¨ã†"
+ end
+
+ it "encodes the String in an encoding compatible with the replacement" do
+ str = " ".force_encoding Encoding::US_ASCII
+ rep = [160].pack('C').force_encoding Encoding::ASCII_8BIT
+ str[0] = rep
+ str.encoding.should equal(Encoding::ASCII_8BIT)
+ end
+
+ it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do
+ str = "ã‚れ"
+ rep = "ãŒ".encode Encoding::EUC_JP
+ lambda { str[0] = rep }.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+end
+
+describe "String#[]= with String index" do
+ it "replaces fewer characters with more characters" do
+ str = "abcde"
+ str["cd"] = "ghi"
+ str.should == "abghie"
+ end
+
+ it "replaces more characters with fewer characters" do
+ str = "abcde"
+ str["bcd"] = "f"
+ str.should == "afe"
+ end
+
+ it "replaces characters with no characters" do
+ str = "abcde"
+ str["cd"] = ""
+ str.should == "abe"
+ end
+
+ it "raises an IndexError if the search String is not found" do
+ str = "abcde"
+ lambda { str["g"] = "h" }.should raise_error(IndexError)
+ end
+
+ with_feature :encoding do
+ it "replaces characters with a multibyte character" do
+ str = "ã‚りgaã¨ã†"
+ str["ga"] = "ãŒ"
+ str.should == "ã‚りãŒã¨ã†"
+ end
+
+ it "replaces multibyte characters with characters" do
+ str = "ã‚りãŒã¨ã†"
+ str["ãŒ"] = "ga"
+ str.should == "ã‚りgaã¨ã†"
+ end
+
+ it "replaces multibyte characters with multibyte characters" do
+ str = "ã‚りãŒã¨ã†"
+ str["ãŒ"] = "ã‹"
+ str.should == "ã‚りã‹ã¨ã†"
+ end
+
+ it "encodes the String in an encoding compatible with the replacement" do
+ str = " ".force_encoding Encoding::US_ASCII
+ rep = [160].pack('C').force_encoding Encoding::ASCII_8BIT
+ str[" "] = rep
+ str.encoding.should equal(Encoding::ASCII_8BIT)
+ end
+
+ it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do
+ str = "ã‚れ"
+ rep = "ãŒ".encode Encoding::EUC_JP
+ lambda { str["れ"] = rep }.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+end
+
+describe "String#[]= with a Regexp index" do
+ it "replaces the matched text with the rhs" do
+ str = "hello"
+ str[/lo/] = "x"
+ str.should == "helx"
+ end
+
+ it "raises IndexError if the regexp index doesn't match a position in the string" do
+ str = "hello"
+ lambda { str[/y/] = "bam" }.should raise_error(IndexError)
+ str.should == "hello"
+ end
+
+ it "calls #to_str to convert the replacement" do
+ rep = mock("string element set regexp")
+ rep.should_receive(:to_str).and_return("b")
+
+ str = "abc"
+ str[/ab/] = rep
+ str.should == "bc"
+ end
+
+ it "checks the match before calling #to_str to convert the replacement" do
+ rep = mock("string element set regexp")
+ rep.should_not_receive(:to_str)
+
+ lambda { "abc"[/def/] = rep }.should raise_error(IndexError)
+ end
+
+ describe "with 3 arguments" do
+ it "calls #to_int to convert the second object" do
+ ref = mock("string element set regexp ref")
+ ref.should_receive(:to_int).and_return(1)
+
+ str = "abc"
+ str[/a(b)/, ref] = "x"
+ str.should == "axc"
+ end
+
+ it "raises a TypeError if #to_int does not return a Fixnum" do
+ ref = mock("string element set regexp ref")
+ ref.should_receive(:to_int).and_return(nil)
+
+ lambda { "abc"[/a(b)/, ref] = "x" }.should raise_error(TypeError)
+ end
+
+ it "uses the 2nd of 3 arguments as which capture should be replaced" do
+ str = "aaa bbb ccc"
+ str[/a (bbb) c/, 1] = "ddd"
+ str.should == "aaa ddd ccc"
+ end
+
+ it "allows the specified capture to be negative and count from the end" do
+ str = "abcd"
+ str[/(a)(b)(c)(d)/, -2] = "e"
+ str.should == "abed"
+ end
+
+ it "checks the match index before calling #to_str to convert the replacement" do
+ rep = mock("string element set regexp")
+ rep.should_not_receive(:to_str)
+
+ lambda { "abc"[/a(b)/, 2] = rep }.should raise_error(IndexError)
+ end
+
+ it "raises IndexError if the specified capture isn't available" do
+ str = "aaa bbb ccc"
+ lambda { str[/a (bbb) c/, 2] = "ddd" }.should raise_error(IndexError)
+ lambda { str[/a (bbb) c/, -2] = "ddd" }.should raise_error(IndexError)
+ end
+
+ describe "when the optional capture does not match" do
+ it "raises an IndexError before setting the replacement" do
+ str1 = "a b c"
+ str2 = str1.dup
+ lambda { str2[/a (b) (Z)?/, 2] = "d" }.should raise_error(IndexError)
+ str2.should == str1
+ end
+ end
+ end
+
+ with_feature :encoding do
+ it "replaces characters with a multibyte character" do
+ str = "ã‚りgaã¨ã†"
+ str[/ga/] = "ãŒ"
+ str.should == "ã‚りãŒã¨ã†"
+ end
+
+ it "replaces multibyte characters with characters" do
+ str = "ã‚りãŒã¨ã†"
+ str[/ãŒ/] = "ga"
+ str.should == "ã‚りgaã¨ã†"
+ end
+
+ it "replaces multibyte characters with multibyte characters" do
+ str = "ã‚りãŒã¨ã†"
+ str[/ãŒ/] = "ã‹"
+ str.should == "ã‚りã‹ã¨ã†"
+ end
+
+ it "encodes the String in an encoding compatible with the replacement" do
+ str = " ".force_encoding Encoding::US_ASCII
+ rep = [160].pack('C').force_encoding Encoding::ASCII_8BIT
+ str[/ /] = rep
+ str.encoding.should equal(Encoding::ASCII_8BIT)
+ end
+
+ it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do
+ str = "ã‚れ"
+ rep = "ãŒ".encode Encoding::EUC_JP
+ lambda { str[/れ/] = rep }.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+end
+
+describe "String#[]= with a Range index" do
+ describe "with an empty replacement" do
+ it "does not replace a character with a zero-index, zero exclude-end range" do
+ str = "abc"
+ str[0...0] = ""
+ str.should == "abc"
+ end
+
+ it "does not replace a character with a zero exclude-end range" do
+ str = "abc"
+ str[1...1] = ""
+ str.should == "abc"
+ end
+
+ it "replaces a character with zero-index, zero non-exclude-end range" do
+ str = "abc"
+ str[0..0] = ""
+ str.should == "bc"
+ end
+
+ it "replaces a character with a zero non-exclude-end range" do
+ str = "abc"
+ str[1..1] = ""
+ str.should == "ac"
+ end
+ end
+
+ it "replaces the contents with a shorter String" do
+ str = "abcde"
+ str[0..-1] = "hg"
+ str.should == "hg"
+ end
+
+ it "replaces the contents with a longer String" do
+ str = "abc"
+ str[0...4] = "uvwxyz"
+ str.should == "uvwxyz"
+ end
+
+ it "replaces a partial string" do
+ str = "abcde"
+ str[1..3] = "B"
+ str.should == "aBe"
+ end
+
+ it "raises a RangeError if negative Range begin is out of range" do
+ lambda { "abc"[-4..-2] = "x" }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError if positive Range begin is greater than String size" do
+ lambda { "abc"[4..2] = "x" }.should raise_error(RangeError)
+ end
+
+ it "uses the Range end as an index rather than a count" do
+ str = "abcdefg"
+ str[-5..3] = "xyz"
+ str.should == "abxyzefg"
+ end
+
+ it "treats a negative out-of-range Range end with a positive Range begin as a zero count" do
+ str = "abc"
+ str[1..-4] = "x"
+ str.should == "axbc"
+ end
+
+ it "treats a negative out-of-range Range end with a negative Range begin as a zero count" do
+ str = "abcd"
+ str[-1..-4] = "x"
+ str.should == "abcxd"
+ end
+
+ with_feature :encoding do
+ it "replaces characters with a multibyte character" do
+ str = "ã‚りgaã¨ã†"
+ str[2..3] = "ãŒ"
+ str.should == "ã‚りãŒã¨ã†"
+ end
+
+ it "replaces multibyte characters with characters" do
+ str = "ã‚りãŒã¨ã†"
+ str[2...3] = "ga"
+ str.should == "ã‚りgaã¨ã†"
+ end
+
+ it "replaces multibyte characters by negative indexes" do
+ str = "ã‚りãŒã¨ã†"
+ str[-3...-2] = "ga"
+ str.should == "ã‚りgaã¨ã†"
+ end
+
+ it "replaces multibyte characters with multibyte characters" do
+ str = "ã‚りãŒã¨ã†"
+ str[2..2] = "ã‹"
+ str.should == "ã‚りã‹ã¨ã†"
+ end
+
+ it "deletes a multibyte character" do
+ str = "ã‚りã¨ã†"
+ str[2..3] = ""
+ str.should == "ã‚り"
+ end
+
+ it "inserts a multibyte character" do
+ str = "ã‚りã¨ã†"
+ str[2...2] = "ãŒ"
+ str.should == "ã‚りãŒã¨ã†"
+ end
+
+ it "encodes the String in an encoding compatible with the replacement" do
+ str = " ".force_encoding Encoding::US_ASCII
+ rep = [160].pack('C').force_encoding Encoding::ASCII_8BIT
+ str[0..1] = rep
+ str.encoding.should equal(Encoding::ASCII_8BIT)
+ end
+
+ it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do
+ str = "ã‚れ"
+ rep = "ãŒ".encode Encoding::EUC_JP
+ lambda { str[0..1] = rep }.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+end
+
+describe "String#[]= with Fixnum index, count" do
+ it "starts at idx and overwrites count characters before inserting the rest of other_str" do
+ a = "hello"
+ a[0, 2] = "xx"
+ a.should == "xxllo"
+ a = "hello"
+ a[0, 2] = "jello"
+ a.should == "jellollo"
+ end
+
+ it "counts negative idx values from end of the string" do
+ a = "hello"
+ a[-1, 0] = "bob"
+ a.should == "hellbobo"
+ a = "hello"
+ a[-5, 0] = "bob"
+ a.should == "bobhello"
+ end
+
+ it "overwrites and deletes characters if count is more than the length of other_str" do
+ a = "hello"
+ a[0, 4] = "x"
+ a.should == "xo"
+ a = "hello"
+ a[0, 5] = "x"
+ a.should == "x"
+ end
+
+ it "deletes characters if other_str is an empty string" do
+ a = "hello"
+ a[0, 2] = ""
+ a.should == "llo"
+ end
+
+ it "deletes characters up to the maximum length of the existing string" do
+ a = "hello"
+ a[0, 6] = "x"
+ a.should == "x"
+ a = "hello"
+ a[0, 100] = ""
+ a.should == ""
+ end
+
+ it "appends other_str to the end of the string if idx == the length of the string" do
+ a = "hello"
+ a[5, 0] = "bob"
+ a.should == "hellobob"
+ end
+
+ it "taints self if other_str is tainted" do
+ a = "hello"
+ a[0, 0] = "".taint
+ a.tainted?.should == true
+
+ a = "hello"
+ a[1, 4] = "x".taint
+ a.tainted?.should == true
+ end
+
+ it "calls #to_int to convert the index and count objects" do
+ index = mock("string element set index")
+ index.should_receive(:to_int).and_return(-4)
+
+ count = mock("string element set count")
+ count.should_receive(:to_int).and_return(2)
+
+ str = "abcde"
+ str[index, count] = "xyz"
+ str.should == "axyzde"
+ end
+
+ it "raises a TypeError if #to_int for index does not return an Integer" do
+ index = mock("string element set index")
+ index.should_receive(:to_int).and_return("1")
+
+ lambda { "abc"[index, 2] = "xyz" }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if #to_int for count does not return an Integer" do
+ count = mock("string element set count")
+ count.should_receive(:to_int).and_return("1")
+
+ lambda { "abc"[1, count] = "xyz" }.should raise_error(TypeError)
+ end
+
+ it "calls #to_str to convert the replacement object" do
+ r = mock("string element set replacement")
+ r.should_receive(:to_str).and_return("xyz")
+
+ str = "abcde"
+ str[2, 2] = r
+ str.should == "abxyze"
+ end
+
+ it "raises a TypeError of #to_str does not return a String" do
+ r = mock("string element set replacement")
+ r.should_receive(:to_str).and_return(nil)
+
+ lambda { "abc"[1, 1] = r }.should raise_error(TypeError)
+ end
+
+ it "raises an IndexError if |idx| is greater than the length of the string" do
+ lambda { "hello"[6, 0] = "bob" }.should raise_error(IndexError)
+ lambda { "hello"[-6, 0] = "bob" }.should raise_error(IndexError)
+ end
+
+ it "raises an IndexError if count < 0" do
+ lambda { "hello"[0, -1] = "bob" }.should raise_error(IndexError)
+ lambda { "hello"[1, -1] = "bob" }.should raise_error(IndexError)
+ end
+
+ it "raises a TypeError if other_str is a type other than String" do
+ lambda { "hello"[0, 2] = nil }.should raise_error(TypeError)
+ lambda { "hello"[0, 2] = [] }.should raise_error(TypeError)
+ lambda { "hello"[0, 2] = 33 }.should raise_error(TypeError)
+ end
+
+ with_feature :encoding do
+ it "replaces characters with a multibyte character" do
+ str = "ã‚りgaã¨ã†"
+ str[2, 2] = "ãŒ"
+ str.should == "ã‚りãŒã¨ã†"
+ end
+
+ it "replaces multibyte characters with characters" do
+ str = "ã‚りãŒã¨ã†"
+ str[2, 1] = "ga"
+ str.should == "ã‚りgaã¨ã†"
+ end
+
+ it "replaces multibyte characters with multibyte characters" do
+ str = "ã‚りãŒã¨ã†"
+ str[2, 1] = "ã‹"
+ str.should == "ã‚りã‹ã¨ã†"
+ end
+
+ it "deletes a multibyte character" do
+ str = "ã‚りã¨ã†"
+ str[2, 2] = ""
+ str.should == "ã‚り"
+ end
+
+ it "inserts a multibyte character" do
+ str = "ã‚りã¨ã†"
+ str[2, 0] = "ãŒ"
+ str.should == "ã‚りãŒã¨ã†"
+ end
+
+ it "raises an IndexError if the character index is out of range of a multibyte String" do
+ lambda { "ã‚れ"[3, 0] = "り" }.should raise_error(IndexError)
+ end
+
+ it "encodes the String in an encoding compatible with the replacement" do
+ str = " ".force_encoding Encoding::US_ASCII
+ rep = [160].pack('C').force_encoding Encoding::ASCII_8BIT
+ str[0, 1] = rep
+ str.encoding.should equal(Encoding::ASCII_8BIT)
+ end
+
+ it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do
+ str = "ã‚れ"
+ rep = "ãŒ".encode Encoding::EUC_JP
+ lambda { str[0, 1] = rep }.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/empty_spec.rb b/spec/ruby/core/string/empty_spec.rb
new file mode 100644
index 0000000000..fb52070723
--- /dev/null
+++ b/spec/ruby/core/string/empty_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#empty?" do
+ it "returns true if the string has a length of zero" do
+ "hello".empty?.should == false
+ " ".empty?.should == false
+ "\x00".empty?.should == false
+ "".empty?.should == true
+ StringSpecs::MyString.new("").empty?.should == true
+ end
+end
diff --git a/spec/ruby/core/string/encode_spec.rb b/spec/ruby/core/string/encode_spec.rb
new file mode 100644
index 0000000000..f582d50794
--- /dev/null
+++ b/spec/ruby/core/string/encode_spec.rb
@@ -0,0 +1,159 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'shared/encode'
+
+with_feature :encoding do
+ describe "String#encode" do
+ before :each do
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+ end
+
+ it_behaves_like :string_encode, :encode
+
+ describe "when passed no options" do
+ it "returns a copy when Encoding.default_internal is nil" do
+ Encoding.default_internal = nil
+ str = "ã‚"
+ str.encode.should_not equal(str)
+ end
+
+ it "returns a copy for a ASCII-only String when Encoding.default_internal is nil" do
+ Encoding.default_internal = nil
+ str = "abc"
+ str.encode.should_not equal(str)
+ end
+
+ it "encodes an ascii substring of a binary string to UTF-8" do
+ x82 = [0x82].pack('C')
+ str = "#{x82}foo".force_encoding("ascii-8bit")[1..-1].encode("utf-8")
+ str.should == "foo".force_encoding("utf-8")
+ str.encoding.should equal(Encoding::UTF_8)
+ end
+ end
+
+ describe "when passed to encoding" do
+ it "returns a copy when passed the same encoding as the String" do
+ str = "ã‚"
+ str.encode(Encoding::UTF_8).should_not equal(str)
+ end
+
+ it "round trips a String" do
+ str = "abc def".force_encoding Encoding::US_ASCII
+ str.encode("utf-32be").encode("ascii").should == "abc def"
+ end
+ end
+
+ describe "when passed options" do
+ it "returns a copy when Encoding.default_internal is nil" do
+ Encoding.default_internal = nil
+ str = "ã‚"
+ str.encode(invalid: :replace).should_not equal(str)
+ end
+
+ it "normalizes newlines" do
+ "\r\nfoo".encode(universal_newline: true).should == "\nfoo"
+
+ "\rfoo".encode(universal_newline: true).should == "\nfoo"
+ end
+ end
+
+ describe "when passed to, from" do
+ it "returns a copy in the destination encoding when both encodings are the same" do
+ str = "ã‚"
+ str.force_encoding("ascii-8bit")
+ encoded = str.encode("utf-8", "utf-8")
+
+ encoded.should_not equal(str)
+ encoded.encoding.should == Encoding::UTF_8
+ end
+
+ it "returns the transcoded string" do
+ str = "\x00\x00\x00\x1F"
+ str.encode(Encoding::UTF_8, Encoding::UTF_16BE).should == "\u0000\u001f"
+ end
+ end
+
+ describe "when passed to, options" do
+ it "returns a copy when the destination encoding is the same as the String encoding" do
+ str = "ã‚"
+ str.encode(Encoding::UTF_8, undef: :replace).should_not equal(str)
+ end
+ end
+
+ describe "when passed to, from, options" do
+ it "returns a copy when both encodings are the same" do
+ str = "ã‚"
+ str.encode("utf-8", "utf-8", invalid: :replace).should_not equal(str)
+ end
+ end
+ end
+
+ describe "String#encode!" do
+ before :each do
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+ end
+
+ it_behaves_like :string_encode, :encode!
+
+ it "raises a #{frozen_error_class} when called on a frozen String" do
+ lambda { "foo".freeze.encode!("euc-jp") }.should raise_error(frozen_error_class)
+ end
+
+ # http://redmine.ruby-lang.org/issues/show/1836
+ it "raises a #{frozen_error_class} when called on a frozen String when it's a no-op" do
+ lambda { "foo".freeze.encode!("utf-8") }.should raise_error(frozen_error_class)
+ end
+
+ describe "when passed no options" do
+ it "returns self when Encoding.default_internal is nil" do
+ Encoding.default_internal = nil
+ str = "ã‚"
+ str.encode!.should equal(str)
+ end
+
+ it "returns self for a ASCII-only String when Encoding.default_internal is nil" do
+ Encoding.default_internal = nil
+ str = "abc"
+ str.encode!.should equal(str)
+ end
+ end
+
+ describe "when passed options" do
+ it "returns self for ASCII-only String when Encoding.default_internal is nil" do
+ Encoding.default_internal = nil
+ str = "abc"
+ str.encode!(invalid: :replace).should equal(str)
+ end
+ end
+
+ describe "when passed to encoding" do
+ it "returns self" do
+ str = "abc"
+ result = str.encode!(Encoding::BINARY)
+ result.encoding.should equal(Encoding::BINARY)
+ result.should equal(str)
+ end
+ end
+
+ describe "when passed to, from" do
+ it "returns self" do
+ str = "ã‚ã‚"
+ result = str.encode!("euc-jp", "utf-8")
+ result.encoding.should equal(Encoding::EUC_JP)
+ result.should equal(str)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/encoding_spec.rb b/spec/ruby/core/string/encoding_spec.rb
new file mode 100644
index 0000000000..b2861f2264
--- /dev/null
+++ b/spec/ruby/core/string/encoding_spec.rb
@@ -0,0 +1,189 @@
+# -*- encoding: us-ascii -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/iso-8859-9-encoding'
+
+with_feature :encoding do
+ describe "String#encoding" do
+ it "returns an Encoding object" do
+ String.new.encoding.should be_an_instance_of(Encoding)
+ end
+
+ it "is equal to the source encoding by default" do
+ s = StringSpecs::ISO88599Encoding.new
+ s.cedilla.encoding.should == s.source_encoding
+ end
+
+ it "returns the given encoding if #force_encoding has been called" do
+ "a".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ end
+
+ it "returns the given encoding if #encode!has been called" do
+ "a".encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ end
+ end
+
+ describe "String#encoding for US-ASCII Strings" do
+ it "returns US-ASCII if self is US-ASCII" do
+ "a".encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns US-ASCII if self is US-ASCII only, despite the default internal encoding being different" do
+ default_internal = Encoding.default_internal
+ Encoding.default_internal = Encoding::UTF_8
+ "a".encoding.should == Encoding::US_ASCII
+ Encoding.default_internal = default_internal
+ end
+
+ it "returns US-ASCII if self is US-ASCII only, despite the default external encoding being different" do
+ default_external = Encoding.default_external
+ Encoding.default_external = Encoding::UTF_8
+ "a".encoding.should == Encoding::US_ASCII
+ Encoding.default_external = default_external
+ end
+
+ it "returns US-ASCII if self is US-ASCII only, despite the default internal and external encodings being different" do
+ default_internal = Encoding.default_internal
+ default_external = Encoding.default_external
+ Encoding.default_internal = Encoding::UTF_8
+ Encoding.default_external = Encoding::UTF_8
+ "a".encoding.should == Encoding::US_ASCII
+ Encoding.default_external = default_external
+ Encoding.default_internal = default_internal
+ end
+
+ it "returns US-ASCII if self is US-ASCII only, despite the default encodings being different" do
+ default_internal = Encoding.default_internal
+ default_external = Encoding.default_external
+ Encoding.default_internal = Encoding::UTF_8
+ Encoding.default_external = Encoding::UTF_8
+ "a".encoding.should == Encoding::US_ASCII
+ Encoding.default_external = default_external
+ Encoding.default_internal = default_internal
+ end
+
+ end
+
+ describe "String#encoding for Strings with \\u escapes" do
+ it "returns UTF-8" do
+ "\u{4040}".encoding.should == Encoding::UTF_8
+ end
+
+ it "returns US-ASCII if self is US-ASCII only" do
+ s = "\u{40}"
+ s.ascii_only?.should be_true
+ s.encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns UTF-8 if self isn't US-ASCII only" do
+ s = "\u{4076}\u{619}"
+ s.ascii_only?.should be_false
+ s.encoding.should == Encoding::UTF_8
+ end
+
+ it "is not affected by the default internal encoding" do
+ default_internal = Encoding.default_internal
+ Encoding.default_internal = Encoding::ISO_8859_15
+ "\u{5050}".encoding.should == Encoding::UTF_8
+ "\u{50}".encoding.should == Encoding::US_ASCII
+ Encoding.default_internal = default_internal
+ end
+
+ it "is not affected by the default external encoding" do
+ default_external = Encoding.default_external
+ Encoding.default_external = Encoding::SHIFT_JIS
+ "\u{50}".encoding.should == Encoding::US_ASCII
+ "\u{5050}".encoding.should == Encoding::UTF_8
+ Encoding.default_external = default_external
+ end
+
+ it "is not affected by both the default internal and external encoding being set at the same time" do
+ default_internal = Encoding.default_internal
+ default_external = Encoding.default_external
+ Encoding.default_internal = Encoding::EUC_JP
+ Encoding.default_external = Encoding::SHIFT_JIS
+ "\u{50}".encoding.should == Encoding::US_ASCII
+ "\u{507}".encoding.should == Encoding::UTF_8
+ Encoding.default_external = default_external
+ Encoding.default_internal = default_internal
+ end
+
+ it "returns the given encoding if #force_encoding has been called" do
+ "\u{20}".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ "\u{2020}".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ end
+
+ it "returns the given encoding if #encode!has been called" do
+ "\u{20}".encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ "\u{2020}".encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ end
+ end
+
+ describe "String#encoding for Strings with \\x escapes" do
+
+ it "returns US-ASCII if self is US-ASCII only" do
+ s = "\x61"
+ s.ascii_only?.should be_true
+ s.encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns ASCII-8BIT when an escape creates a byte with the 8th bit set if the source encoding is US-ASCII" do
+ __ENCODING__.should == Encoding::US_ASCII
+ str = " "
+ str.encoding.should == Encoding::US_ASCII
+ str += [0xDF].pack('C')
+ str.ascii_only?.should be_false
+ str.encoding.should == Encoding::ASCII_8BIT
+ end
+
+ # TODO: Deal with case when the byte in question isn't valid in the source
+ # encoding?
+ it "returns the source encoding when an escape creates a byte with the 8th bit set if the source encoding isn't US-ASCII" do
+ fixture = StringSpecs::ISO88599Encoding.new
+ fixture.source_encoding.should == Encoding::ISO8859_9
+ fixture.x_escape.ascii_only?.should be_false
+ fixture.x_escape.encoding.should == Encoding::ISO8859_9
+ end
+
+ it "is not affected by the default internal encoding" do
+ default_internal = Encoding.default_internal
+ Encoding.default_internal = Encoding::ISO_8859_15
+ "\x50".encoding.should == Encoding::US_ASCII
+ "\x50".encoding.should == Encoding::US_ASCII
+ Encoding.default_internal = default_internal
+ end
+
+ it "is not affected by the default external encoding" do
+ default_external = Encoding.default_external
+ Encoding.default_external = Encoding::SHIFT_JIS
+ "\x50".encoding.should == Encoding::US_ASCII
+ [0xD4].pack('C').encoding.should == Encoding::ASCII_8BIT
+ Encoding.default_external = default_external
+ end
+
+ it "is not affected by both the default internal and external encoding being set at the same time" do
+ default_internal = Encoding.default_internal
+ default_external = Encoding.default_external
+ Encoding.default_internal = Encoding::EUC_JP
+ Encoding.default_external = Encoding::SHIFT_JIS
+ x50 = "\x50"
+ x50.encoding.should == Encoding::US_ASCII
+ [0xD4].pack('C').encoding.should == Encoding::ASCII_8BIT
+ Encoding.default_external = default_external
+ Encoding.default_internal = default_internal
+ end
+
+ it "returns the given encoding if #force_encoding has been called" do
+ x50 = "\x50"
+ x50.force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ xD4 = [212].pack('C')
+ xD4.force_encoding(Encoding::ISO_8859_9).encoding.should == Encoding::ISO_8859_9
+ end
+
+ it "returns the given encoding if #encode!has been called" do
+ x50 = "\x50"
+ x50.encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
+ x00 = "x\00"
+ x00.encode!(Encoding::UTF_8).encoding.should == Encoding::UTF_8
+ end
+ end
+end
diff --git a/spec/ruby/core/string/end_with_spec.rb b/spec/ruby/core/string/end_with_spec.rb
new file mode 100644
index 0000000000..7268378a38
--- /dev/null
+++ b/spec/ruby/core/string/end_with_spec.rb
@@ -0,0 +1,50 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#end_with?" do
+ it "returns true only if ends match" do
+ s = "hello"
+ s.end_with?('o').should be_true
+ s.end_with?('llo').should be_true
+ end
+
+ it 'returns false if the end does not match' do
+ s = 'hello'
+ s.end_with?('ll').should be_false
+ end
+
+ it "returns true if the search string is empty" do
+ "hello".end_with?("").should be_true
+ "".end_with?("").should be_true
+ end
+
+ it "returns true only if any ending match" do
+ "hello".end_with?('x', 'y', 'llo', 'z').should be_true
+ end
+
+ it "converts its argument using :to_str" do
+ s = "hello"
+ find = mock('o')
+ find.should_receive(:to_str).and_return("o")
+ s.end_with?(find).should be_true
+ end
+
+ it "ignores arguments not convertible to string" do
+ "hello".end_with?().should be_false
+ lambda { "hello".end_with?(1) }.should raise_error(TypeError)
+ lambda { "hello".end_with?(["o"]) }.should raise_error(TypeError)
+ lambda { "hello".end_with?(1, nil, "o") }.should raise_error(TypeError)
+ end
+
+ it "uses only the needed arguments" do
+ find = mock('h')
+ find.should_not_receive(:to_str)
+ "hello".end_with?("o",find).should be_true
+ end
+
+ it "works for multibyte strings" do
+ "céréale".end_with?("réale").should be_true
+ end
+
+end
diff --git a/spec/ruby/core/string/eql_spec.rb b/spec/ruby/core/string/eql_spec.rb
new file mode 100644
index 0000000000..397974d9fb
--- /dev/null
+++ b/spec/ruby/core/string/eql_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'shared/eql'
+
+describe "String#eql?" do
+ it_behaves_like :string_eql_value, :eql?
+
+ describe "when given a non-String" do
+ it "returns false" do
+ 'hello'.should_not eql(5)
+ not_supported_on :opal do
+ 'hello'.should_not eql(:hello)
+ end
+ 'hello'.should_not eql(mock('x'))
+ end
+
+ it "does not try to call #to_str on the given argument" do
+ (obj = mock('x')).should_not_receive(:to_str)
+ 'hello'.should_not eql(obj)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/equal_value_spec.rb b/spec/ruby/core/string/equal_value_spec.rb
new file mode 100644
index 0000000000..b9c9c372f8
--- /dev/null
+++ b/spec/ruby/core/string/equal_value_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'shared/eql'
+require_relative 'shared/equal_value'
+
+describe "String#==" do
+ it_behaves_like :string_eql_value, :==
+ it_behaves_like :string_equal_value, :==
+end
diff --git a/spec/ruby/core/string/fixtures/classes.rb b/spec/ruby/core/string/fixtures/classes.rb
new file mode 100644
index 0000000000..1cc7600abb
--- /dev/null
+++ b/spec/ruby/core/string/fixtures/classes.rb
@@ -0,0 +1,49 @@
+class Object
+ # This helper is defined here rather than in MSpec because
+ # it is only used in #unpack specs.
+ def unpack_format(count=nil, repeat=nil)
+ format = "#{instance_variable_get(:@method)}#{count}"
+ format *= repeat if repeat
+ format.dup # because it may then become tainted
+ end
+end
+
+module StringSpecs
+ class MyString < String; end
+ class MyArray < Array; end
+ class MyRange < Range; end
+
+ class SubString < String
+ attr_reader :special
+
+ def initialize(str=nil)
+ @special = str
+ end
+ end
+
+ class InitializeString < String
+ attr_reader :ivar
+
+ def initialize(other)
+ super
+ @ivar = 1
+ end
+
+ def initialize_copy(other)
+ ScratchPad.record object_id
+ end
+ end
+
+ module StringModule
+ def repr
+ 1
+ end
+ end
+
+ class StringWithRaisingConstructor < String
+ def initialize(str)
+ raise ArgumentError.new('constructor was called') unless str == 'silly:string'
+ self.replace(str)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/fixtures/freeze_magic_comment.rb b/spec/ruby/core/string/fixtures/freeze_magic_comment.rb
new file mode 100644
index 0000000000..2b87a16328
--- /dev/null
+++ b/spec/ruby/core/string/fixtures/freeze_magic_comment.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+print (+ 'frozen string').frozen? ? 'immutable' : 'mutable'
diff --git a/spec/ruby/core/string/fixtures/iso-8859-9-encoding.rb b/spec/ruby/core/string/fixtures/iso-8859-9-encoding.rb
new file mode 100644
index 0000000000..61a691ff78
--- /dev/null
+++ b/spec/ruby/core/string/fixtures/iso-8859-9-encoding.rb
@@ -0,0 +1,9 @@
+# -*- encoding: iso-8859-9 -*-
+module StringSpecs
+ class ISO88599Encoding
+ 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
+ end
+end
diff --git a/spec/ruby/core/string/fixtures/utf-8-encoding.rb b/spec/ruby/core/string/fixtures/utf-8-encoding.rb
new file mode 100644
index 0000000000..fd243ec522
--- /dev/null
+++ b/spec/ruby/core/string/fixtures/utf-8-encoding.rb
@@ -0,0 +1,7 @@
+# -*- encoding: utf-8 -*-
+module StringSpecs
+ class UTF8Encoding
+ def self.source_encoding; __ENCODING__; end
+ def self.egrave; "é"; end
+ end
+end
diff --git a/spec/ruby/core/string/force_encoding_spec.rb b/spec/ruby/core/string/force_encoding_spec.rb
new file mode 100644
index 0000000000..06e04b8d95
--- /dev/null
+++ b/spec/ruby/core/string/force_encoding_spec.rb
@@ -0,0 +1,73 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "String#force_encoding" do
+ it "accepts a String as the name of an Encoding" do
+ "abc".force_encoding('shift_jis').encoding.should == Encoding::Shift_JIS
+ end
+
+ describe "with a special encoding name" do
+ before :each do
+ @original_encoding = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_internal = @original_encoding
+ end
+
+ it "accepts valid special encoding names" do
+ Encoding.default_internal = "US-ASCII"
+ "abc".force_encoding("internal").encoding.should == Encoding::US_ASCII
+ end
+
+ it "defaults to ASCII-8BIT if special encoding name is not set" do
+ Encoding.default_internal = nil
+ "abc".force_encoding("internal").encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+
+ it "accepts an Encoding instance" do
+ "abc".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::Shift_JIS
+ end
+
+ it "calls #to_str to convert an object to an encoding name" do
+ obj = mock("force_encoding")
+ obj.should_receive(:to_str).and_return("utf-8")
+
+ "abc".force_encoding(obj).encoding.should == Encoding::UTF_8
+ end
+
+ it "raises a TypeError if #to_str does not return a String" do
+ obj = mock("force_encoding")
+ obj.should_receive(:to_str).and_return(1)
+
+ lambda { "abc".force_encoding(obj) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed nil" do
+ lambda { "abc".force_encoding(nil) }.should raise_error(TypeError)
+ end
+
+ it "returns self" do
+ str = "abc"
+ str.force_encoding('utf-8').should equal(str)
+ end
+
+ it "sets the encoding even if the String contents are invalid in that encoding" do
+ str = "\u{9765}"
+ str.force_encoding('euc-jp')
+ str.encoding.should == Encoding::EUC_JP
+ str.valid_encoding?.should be_false
+ end
+
+ it "does not transcode self" do
+ str = "\u{8612}"
+ str.dup.force_encoding('utf-16le').should_not == str.encode('utf-16le')
+ end
+
+ it "raises a #{frozen_error_class} if self is frozen" do
+ str = "abcd".freeze
+ lambda { str.force_encoding(str.encoding) }.should raise_error(frozen_error_class)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/freeze_spec.rb b/spec/ruby/core/string/freeze_spec.rb
new file mode 100644
index 0000000000..04d1e9513c
--- /dev/null
+++ b/spec/ruby/core/string/freeze_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "String#freeze" do
+
+ it "produces the same object whenever called on an instance of a literal in the source" do
+ "abc".freeze.should equal "abc".freeze
+ end
+
+ it "doesn't produce the same object for different instances of literals in the source" do
+ "abc".should_not equal "abc"
+ end
+
+ it "being a special form doesn't change the value of defined?" do
+ defined?("abc".freeze).should == "method"
+ end
+
+end
diff --git a/spec/ruby/core/string/getbyte_spec.rb b/spec/ruby/core/string/getbyte_spec.rb
new file mode 100644
index 0000000000..84286ada92
--- /dev/null
+++ b/spec/ruby/core/string/getbyte_spec.rb
@@ -0,0 +1,69 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe "String#getbyte" do
+ it "returns an Integer if given a valid index" do
+ "a".getbyte(0).should be_kind_of(Integer)
+ end
+
+ it "starts indexing at 0" do
+ "b".getbyte(0).should == 98
+
+ # copy-on-write case
+ _str1, str2 = "fooXbar".split("X")
+ str2.getbyte(0).should == 98
+ end
+
+ it "counts from the end of the String if given a negative argument" do
+ "glark".getbyte(-1).should == "glark".getbyte(4)
+
+ # copy-on-write case
+ _str1, str2 = "fooXbar".split("X")
+ str2.getbyte(-1).should == 114
+ end
+
+ it "returns an Integer between 0 and 255" do
+ "\x00".getbyte(0).should == 0
+ [0xFF].pack('C').getbyte(0).should == 255
+ 256.chr('utf-8').getbyte(0).should == 196
+ 256.chr('utf-8').getbyte(1).should == 128
+ end
+
+ it "regards a multi-byte character as having multiple bytes" do
+ chr = "\u{998}"
+ chr.bytesize.should == 3
+ chr.getbyte(0).should == 224
+ chr.getbyte(1).should == 166
+ chr.getbyte(2).should == 152
+ end
+
+ it "mirrors the output of #bytes" do
+ xDE = [0xDE].pack('C').force_encoding('utf-8')
+ str = "UTF-8 (\u{9865}} characters and hex escapes (#{xDE})"
+ str.bytes.to_a.each_with_index do |byte, index|
+ str.getbyte(index).should == byte
+ end
+ end
+
+ it "interprets bytes relative to the String's encoding" do
+ str = "\u{333}"
+ str.encode('utf-8').getbyte(0).should_not == str.encode('utf-16le').getbyte(0)
+ end
+
+ it "returns nil for out-of-bound indexes" do
+ "g".getbyte(1).should be_nil
+ end
+
+ it "regards the empty String as containing no bytes" do
+ "".getbyte(0).should be_nil
+ end
+
+ it "raises an ArgumentError unless given one argument" do
+ lambda { "glark".getbyte }.should raise_error(ArgumentError)
+ lambda { "food".getbyte(0,0) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError unless its argument can be coerced into an Integer" do
+ lambda { "a".getbyte('a') }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/string/grapheme_clusters_spec.rb b/spec/ruby/core/string/grapheme_clusters_spec.rb
new file mode 100644
index 0000000000..0cba0e4216
--- /dev/null
+++ b/spec/ruby/core/string/grapheme_clusters_spec.rb
@@ -0,0 +1,15 @@
+require_relative 'shared/chars'
+require_relative 'shared/grapheme_clusters'
+
+ruby_version_is "2.5" do
+ describe "String#grapheme_clusters" do
+ it_behaves_like :string_chars, :grapheme_clusters
+ it_behaves_like :string_grapheme_clusters, :grapheme_clusters
+
+ it "returns an array when no block given" do
+ string = "ab\u{1f3f3}\u{fe0f}\u{200d}\u{1f308}\u{1F43E}"
+ string.grapheme_clusters.should == ['a', 'b', "\u{1f3f3}\u{fe0f}\u{200d}\u{1f308}", "\u{1F43E}"]
+
+ end
+ end
+end
diff --git a/spec/ruby/core/string/gsub_spec.rb b/spec/ruby/core/string/gsub_spec.rb
new file mode 100644
index 0000000000..a94010edea
--- /dev/null
+++ b/spec/ruby/core/string/gsub_spec.rb
@@ -0,0 +1,696 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe :string_gsub_named_capture, shared: true do
+ it "replaces \\k named backreferences with the regexp's corresponding capture" do
+ str = "hello"
+
+ str.gsub(/(?<foo>[aeiou])/, '<\k<foo>>').should == "h<e>ll<o>"
+ str.gsub(/(?<foo>.)/, '\k<foo>\k<foo>').should == "hheelllloo"
+ end
+end
+
+describe "String#gsub with pattern and replacement" do
+ it "inserts the replacement around every character when the pattern collapses" do
+ "hello".gsub(//, ".").should == ".h.e.l.l.o."
+ end
+
+ it "respects unicode when the pattern collapses" do
+ str = "ã“ã«ã¡ã‚"
+ reg = %r!!
+
+ str.gsub(reg, ".").should == ".ã“.ã«.ã¡.ã‚."
+ end
+
+ it "doesn't freak out when replacing ^" do
+ "Text\n".gsub(/^/, ' ').should == " Text\n"
+ "Text\nFoo".gsub(/^/, ' ').should == " Text\n Foo"
+ end
+
+ it "returns a copy of self with all occurrences of pattern replaced with replacement" do
+ "hello".gsub(/[aeiou]/, '*').should == "h*ll*"
+
+ str = "hello homely world. hah!"
+ str.gsub(/\Ah\S+\s*/, "huh? ").should == "huh? homely world. hah!"
+
+ str = "¿por qué?"
+ str.gsub(/([a-z\d]*)/, "*").should == "*¿** **é*?*"
+ end
+
+ it "ignores a block if supplied" do
+ "food".gsub(/f/, "g") { "w" }.should == "good"
+ end
+
+ it "supports \\G which matches at the beginning of the remaining (non-matched) string" do
+ str = "hello homely world. hah!"
+ str.gsub(/\Gh\S+\s*/, "huh? ").should == "huh? huh? world. hah!"
+ end
+
+ it "supports /i for ignoring case" do
+ str = "Hello. How happy are you?"
+ str.gsub(/h/i, "j").should == "jello. jow jappy are you?"
+ str.gsub(/H/i, "j").should == "jello. jow jappy are you?"
+ end
+
+ it "doesn't interpret regexp metacharacters if pattern is a string" do
+ "12345".gsub('\d', 'a').should == "12345"
+ '\d'.gsub('\d', 'a').should == "a"
+ end
+
+ it "replaces \\1 sequences with the regexp's corresponding capture" do
+ str = "hello"
+
+ str.gsub(/([aeiou])/, '<\1>').should == "h<e>ll<o>"
+ str.gsub(/(.)/, '\1\1').should == "hheelllloo"
+
+ str.gsub(/.(.?)/, '<\0>(\1)').should == "<he>(e)<ll>(l)<o>()"
+
+ str.gsub(/.(.)+/, '\1').should == "o"
+
+ str = "ABCDEFGHIJKLabcdefghijkl"
+ re = /#{"(.)" * 12}/
+ str.gsub(re, '\1').should == "Aa"
+ str.gsub(re, '\9').should == "Ii"
+ # Only the first 9 captures can be accessed in MRI
+ str.gsub(re, '\10').should == "A0a0"
+ end
+
+ it "treats \\1 sequences without corresponding captures as empty strings" do
+ str = "hello!"
+
+ str.gsub("", '<\1>').should == "<>h<>e<>l<>l<>o<>!<>"
+ str.gsub("h", '<\1>').should == "<>ello!"
+
+ str.gsub(//, '<\1>').should == "<>h<>e<>l<>l<>o<>!<>"
+ str.gsub(/./, '\1\2\3').should == ""
+ str.gsub(/.(.{20})?/, '\1').should == ""
+ end
+
+ it "replaces \\& and \\0 with the complete match" do
+ str = "hello!"
+
+ str.gsub("", '<\0>').should == "<>h<>e<>l<>l<>o<>!<>"
+ str.gsub("", '<\&>').should == "<>h<>e<>l<>l<>o<>!<>"
+ str.gsub("he", '<\0>').should == "<he>llo!"
+ str.gsub("he", '<\&>').should == "<he>llo!"
+ str.gsub("l", '<\0>').should == "he<l><l>o!"
+ str.gsub("l", '<\&>').should == "he<l><l>o!"
+
+ str.gsub(//, '<\0>').should == "<>h<>e<>l<>l<>o<>!<>"
+ str.gsub(//, '<\&>').should == "<>h<>e<>l<>l<>o<>!<>"
+ str.gsub(/../, '<\0>').should == "<he><ll><o!>"
+ str.gsub(/../, '<\&>').should == "<he><ll><o!>"
+ str.gsub(/(.)./, '<\0>').should == "<he><ll><o!>"
+ end
+
+ it "replaces \\` with everything before the current match" do
+ str = "hello!"
+
+ str.gsub("", '<\`>').should == "<>h<h>e<he>l<hel>l<hell>o<hello>!<hello!>"
+ str.gsub("h", '<\`>').should == "<>ello!"
+ str.gsub("l", '<\`>').should == "he<he><hel>o!"
+ str.gsub("!", '<\`>').should == "hello<hello>"
+
+ str.gsub(//, '<\`>').should == "<>h<h>e<he>l<hel>l<hell>o<hello>!<hello!>"
+ str.gsub(/../, '<\`>').should == "<><he><hell>"
+ end
+
+ it "replaces \\' with everything after the current match" do
+ str = "hello!"
+
+ str.gsub("", '<\\\'>').should == "<hello!>h<ello!>e<llo!>l<lo!>l<o!>o<!>!<>"
+ str.gsub("h", '<\\\'>').should == "<ello!>ello!"
+ str.gsub("ll", '<\\\'>').should == "he<o!>o!"
+ str.gsub("!", '<\\\'>').should == "hello<>"
+
+ str.gsub(//, '<\\\'>').should == "<hello!>h<ello!>e<llo!>l<lo!>l<o!>o<!>!<>"
+ str.gsub(/../, '<\\\'>').should == "<llo!><o!><>"
+ end
+
+ it "replaces \\+ with the last paren that actually matched" do
+ str = "hello!"
+
+ str.gsub(/(.)(.)/, '\+').should == "el!"
+ str.gsub(/(.)(.)+/, '\+').should == "!"
+ str.gsub(/(.)()/, '\+').should == ""
+ str.gsub(/(.)(.{20})?/, '<\+>').should == "<h><e><l><l><o><!>"
+
+ str = "ABCDEFGHIJKLabcdefghijkl"
+ re = /#{"(.)" * 12}/
+ str.gsub(re, '\+').should == "Ll"
+ end
+
+ it "treats \\+ as an empty string if there was no captures" do
+ "hello!".gsub(/./, '\+').should == ""
+ end
+
+ it "maps \\\\ in replacement to \\" do
+ "hello".gsub(/./, '\\\\').should == '\\' * 5
+ end
+
+ it "leaves unknown \\x escapes in replacement untouched" do
+ "hello".gsub(/./, '\\x').should == '\\x' * 5
+ "hello".gsub(/./, '\\y').should == '\\y' * 5
+ end
+
+ it "leaves \\ at the end of replacement untouched" do
+ "hello".gsub(/./, 'hah\\').should == 'hah\\' * 5
+ end
+
+ it_behaves_like :string_gsub_named_capture, :gsub
+
+ it "taints the result if the original string or replacement is tainted" do
+ hello = "hello"
+ hello_t = "hello"
+ a = "a"
+ a_t = "a"
+ empty = ""
+ empty_t = ""
+
+ hello_t.taint; a_t.taint; empty_t.taint
+
+ hello_t.gsub(/./, a).tainted?.should == true
+ hello_t.gsub(/./, empty).tainted?.should == true
+
+ hello.gsub(/./, a_t).tainted?.should == true
+ hello.gsub(/./, empty_t).tainted?.should == true
+ hello.gsub(//, empty_t).tainted?.should == true
+
+ hello.gsub(//.taint, "foo").tainted?.should == false
+ end
+
+ it "handles pattern collapse" do
+ str = "ã“ã«ã¡ã‚"
+ reg = %r!!
+ str.gsub(reg, ".").should == ".ã“.ã«.ã¡.ã‚."
+ end
+
+ it "untrusts the result if the original string or replacement is untrusted" do
+ hello = "hello"
+ hello_t = "hello"
+ a = "a"
+ a_t = "a"
+ empty = ""
+ empty_t = ""
+
+ hello_t.untrust; a_t.untrust; empty_t.untrust
+
+ hello_t.gsub(/./, a).untrusted?.should == true
+ hello_t.gsub(/./, empty).untrusted?.should == true
+
+ hello.gsub(/./, a_t).untrusted?.should == true
+ hello.gsub(/./, empty_t).untrusted?.should == true
+ hello.gsub(//, empty_t).untrusted?.should == true
+
+ hello.gsub(//.untrust, "foo").untrusted?.should == false
+ end
+
+ it "tries to convert pattern to a string using to_str" do
+ pattern = mock('.')
+ def pattern.to_str() "." end
+
+ "hello.".gsub(pattern, "!").should == "hello!"
+ end
+
+ it "raises a TypeError when pattern can't be converted to a string" do
+ lambda { "hello".gsub([], "x") }.should raise_error(TypeError)
+ lambda { "hello".gsub(Object.new, "x") }.should raise_error(TypeError)
+ lambda { "hello".gsub(nil, "x") }.should raise_error(TypeError)
+ end
+
+ it "tries to convert replacement to a string using to_str" do
+ replacement = mock('hello_replacement')
+ def replacement.to_str() "hello_replacement" end
+
+ "hello".gsub(/hello/, replacement).should == "hello_replacement"
+ end
+
+ it "raises a TypeError when replacement can't be converted to a string" do
+ lambda { "hello".gsub(/[aeiou]/, []) }.should raise_error(TypeError)
+ lambda { "hello".gsub(/[aeiou]/, Object.new) }.should raise_error(TypeError)
+ lambda { "hello".gsub(/[aeiou]/, nil) }.should raise_error(TypeError)
+ end
+
+ it "returns subclass instances when called on a subclass" do
+ StringSpecs::MyString.new("").gsub(//, "").should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("").gsub(/foo/, "").should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("foo").gsub(/foo/, "").should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("foo").gsub("foo", "").should be_an_instance_of(StringSpecs::MyString)
+ 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'
+
+ 'hello.'.gsub('not', 'x')
+ $~.should == nil
+
+ 'hello.'.gsub(/.(.)/, 'x')
+ $~[0].should == 'o.'
+
+ 'hello.'.gsub(/not/, 'x')
+ $~.should == nil
+ end
+end
+
+describe "String#gsub with pattern and Hash" do
+ it "returns a copy of self with all occurrences of pattern replaced with the value of the corresponding hash key" do
+ "hello".gsub(/./, 'l' => 'L').should == "LL"
+ "hello!".gsub(/(.)(.)/, 'he' => 'she ', 'll' => 'said').should == 'she said'
+ "hello".gsub('l', 'l' => 'el').should == 'heelelo'
+ end
+
+ it "ignores keys that don't correspond to matches" do
+ "hello".gsub(/./, 'z' => 'L', 'h' => 'b', 'o' => 'ow').should == "bow"
+ end
+
+ it "returns an empty string if the pattern matches but the hash specifies no replacements" do
+ "hello".gsub(/./, 'z' => 'L').should == ""
+ end
+
+ it "ignores non-String keys" do
+ "tattoo".gsub(/(tt)/, 'tt' => 'b', tt: 'z').should == "taboo"
+ end
+
+ it "uses a key's value as many times as needed" do
+ "food".gsub(/o/, 'o' => '0').should == "f00d"
+ end
+
+ it "uses the hash's default value for missing keys" do
+ hsh = {}
+ hsh.default='?'
+ hsh['o'] = '0'
+ "food".gsub(/./, hsh).should == "?00?"
+ end
+
+ it "coerces the hash values with #to_s" do
+ hsh = {}
+ hsh.default=[]
+ hsh['o'] = 0
+ obj = mock('!')
+ obj.should_receive(:to_s).and_return('!')
+ hsh['!'] = obj
+ "food!".gsub(/./, hsh).should == "[]00[]!"
+ end
+
+ it "uses the hash's value set from default_proc for missing keys" do
+ hsh = {}
+ hsh.default_proc = lambda { |k,v| 'lamb' }
+ "food!".gsub(/./, hsh).should == "lamblamblamblamblamb"
+ end
+
+ it "sets $~ to MatchData of last match and nil when there's none for access from outside" do
+ 'hello.'.gsub('l', 'l' => 'L')
+ $~.begin(0).should == 3
+ $~[0].should == 'l'
+
+ 'hello.'.gsub('not', 'ot' => 'to')
+ $~.should == nil
+
+ 'hello.'.gsub(/.(.)/, 'o' => ' hole')
+ $~[0].should == 'o.'
+
+ 'hello.'.gsub(/not/, 'z' => 'glark')
+ $~.should == nil
+ end
+
+ it "doesn't interpolate special sequences like \\1 for the block's return value" do
+ repl = '\& \0 \1 \` \\\' \+ \\\\ foo'
+ "hello".gsub(/(.+)/, 'hello' => repl ).should == repl
+ end
+
+ it "untrusts the result if the original string is untrusted" do
+ str = "Ghana".untrust
+ str.gsub(/[Aa]na/, 'ana' => '').untrusted?.should be_true
+ end
+
+ it "untrusts the result if a hash value is untrusted" do
+ str = "Ghana"
+ str.gsub(/a$/, 'a' => 'di'.untrust).untrusted?.should be_true
+ end
+
+ it "taints the result if the original string is tainted" do
+ str = "Ghana".taint
+ str.gsub(/[Aa]na/, 'ana' => '').tainted?.should be_true
+ end
+
+ it "taints the result if a hash value is tainted" do
+ str = "Ghana"
+ str.gsub(/a$/, 'a' => 'di'.taint).tainted?.should be_true
+ end
+
+end
+
+describe "String#gsub! with pattern and Hash" do
+
+ it "returns self with all occurrences of pattern replaced with the value of the corresponding hash key" do
+ "hello".gsub!(/./, 'l' => 'L').should == "LL"
+ "hello!".gsub!(/(.)(.)/, 'he' => 'she ', 'll' => 'said').should == 'she said'
+ "hello".gsub!('l', 'l' => 'el').should == 'heelelo'
+ end
+
+ it "ignores keys that don't correspond to matches" do
+ "hello".gsub!(/./, 'z' => 'L', 'h' => 'b', 'o' => 'ow').should == "bow"
+ end
+
+ it "replaces self with an empty string if the pattern matches but the hash specifies no replacements" do
+ "hello".gsub!(/./, 'z' => 'L').should == ""
+ end
+
+ it "ignores non-String keys" do
+ "hello".gsub!(/(ll)/, 'll' => 'r', ll: 'z').should == "hero"
+ end
+
+ it "uses a key's value as many times as needed" do
+ "food".gsub!(/o/, 'o' => '0').should == "f00d"
+ end
+
+ it "uses the hash's default value for missing keys" do
+ hsh = {}
+ hsh.default='?'
+ hsh['o'] = '0'
+ "food".gsub!(/./, hsh).should == "?00?"
+ end
+
+ it "coerces the hash values with #to_s" do
+ hsh = {}
+ hsh.default=[]
+ hsh['o'] = 0
+ obj = mock('!')
+ obj.should_receive(:to_s).and_return('!')
+ hsh['!'] = obj
+ "food!".gsub!(/./, hsh).should == "[]00[]!"
+ end
+
+ it "uses the hash's value set from default_proc for missing keys" do
+ hsh = {}
+ hsh.default_proc = lambda { |k,v| 'lamb' }
+ "food!".gsub!(/./, hsh).should == "lamblamblamblamblamb"
+ end
+
+ it "sets $~ to MatchData of last match and nil when there's none for access from outside" do
+ 'hello.'.gsub!('l', 'l' => 'L')
+ $~.begin(0).should == 3
+ $~[0].should == 'l'
+
+ 'hello.'.gsub!('not', 'ot' => 'to')
+ $~.should == nil
+
+ 'hello.'.gsub!(/.(.)/, 'o' => ' hole')
+ $~[0].should == 'o.'
+
+ 'hello.'.gsub!(/not/, 'z' => 'glark')
+ $~.should == nil
+ end
+
+ it "doesn't interpolate special sequences like \\1 for the block's return value" do
+ repl = '\& \0 \1 \` \\\' \+ \\\\ foo'
+ "hello".gsub!(/(.+)/, 'hello' => repl ).should == repl
+ end
+
+ it "keeps untrusted state" do
+ str = "Ghana".untrust
+ str.gsub!(/[Aa]na/, 'ana' => '').untrusted?.should be_true
+ end
+
+ it "untrusts self if a hash value is untrusted" do
+ str = "Ghana"
+ str.gsub!(/a$/, 'a' => 'di'.untrust).untrusted?.should be_true
+ end
+
+ it "keeps tainted state" do
+ str = "Ghana".taint
+ str.gsub!(/[Aa]na/, 'ana' => '').tainted?.should be_true
+ end
+
+ it "taints self if a hash value is tainted" do
+ str = "Ghana"
+ str.gsub!(/a$/, 'a' => 'di'.taint).tainted?.should be_true
+ end
+
+end
+
+describe "String#gsub with pattern and block" do
+ it "returns a copy of self with all occurrences of pattern replaced with the block's return value" do
+ "hello".gsub(/./) { |s| s.succ + ' ' }.should == "i f m m p "
+ "hello!".gsub(/(.)(.)/) { |*a| a.inspect }.should == '["he"]["ll"]["o!"]'
+ "hello".gsub('l') { 'x'}.should == 'hexxo'
+ end
+
+ it "sets $~ for access from the block" do
+ str = "hello"
+ str.gsub(/([aeiou])/) { "<#{$~[1]}>" }.should == "h<e>ll<o>"
+ str.gsub(/([aeiou])/) { "<#{$1}>" }.should == "h<e>ll<o>"
+ str.gsub("l") { "<#{$~[0]}>" }.should == "he<l><l>o"
+
+ offsets = []
+
+ str.gsub(/([aeiou])/) do
+ md = $~
+ md.string.should == str
+ offsets << md.offset(0)
+ str
+ end.should == "hhellollhello"
+
+ offsets.should == [[1, 2], [4, 5]]
+ end
+
+ it "restores $~ after leaving the block" do
+ [/./, "l"].each do |pattern|
+ old_md = nil
+ "hello".gsub(pattern) do
+ old_md = $~
+ "ok".match(/./)
+ "x"
+ end
+
+ $~[0].should == old_md[0]
+ $~.string.should == "hello"
+ end
+ end
+
+ it "sets $~ to MatchData of last match and nil when there's none for access from outside" do
+ 'hello.'.gsub('l') { 'x' }
+ $~.begin(0).should == 3
+ $~[0].should == 'l'
+
+ 'hello.'.gsub('not') { 'x' }
+ $~.should == nil
+
+ 'hello.'.gsub(/.(.)/) { 'x' }
+ $~[0].should == 'o.'
+
+ 'hello.'.gsub(/not/) { 'x' }
+ $~.should == nil
+ end
+
+ it "doesn't interpolate special sequences like \\1 for the block's return value" do
+ repl = '\& \0 \1 \` \\\' \+ \\\\ foo'
+ "hello".gsub(/(.+)/) { repl }.should == repl
+ end
+
+ it "converts the block's return value to a string using to_s" do
+ replacement = mock('hello_replacement')
+ def replacement.to_s() "hello_replacement" end
+
+ "hello".gsub(/hello/) { replacement }.should == "hello_replacement"
+
+ obj = mock('ok')
+ def obj.to_s() "ok" end
+
+ "hello".gsub(/.+/) { obj }.should == "ok"
+ end
+
+ it "untrusts the result if the original string or replacement is untrusted" do
+ hello = "hello"
+ hello_t = "hello"
+ a = "a"
+ a_t = "a"
+ empty = ""
+ empty_t = ""
+
+ hello_t.untrust; a_t.untrust; empty_t.untrust
+
+ hello_t.gsub(/./) { a }.untrusted?.should == true
+ hello_t.gsub(/./) { empty }.untrusted?.should == true
+
+ hello.gsub(/./) { a_t }.untrusted?.should == true
+ hello.gsub(/./) { empty_t }.untrusted?.should == true
+ hello.gsub(//) { empty_t }.untrusted?.should == true
+
+ hello.gsub(//.untrust) { "foo" }.untrusted?.should == false
+ end
+
+ it "uses the compatible encoding if they are compatible" do
+ s = "hello"
+ s2 = "#{195.chr}#{192.chr}#{195.chr}"
+
+ s.gsub(/l/) { |bar| 195.chr }.encoding.should == Encoding::ASCII_8BIT
+ s2.gsub("#{192.chr}") { |bar| "hello" }.encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are not compatible" do
+ s = "hllëllo"
+ s2 = "hellö"
+
+ lambda { s.gsub(/l/) { |bar| "РуÑÑкий".force_encoding("iso-8859-5") } }.should raise_error(Encoding::CompatibilityError)
+ lambda { s2.gsub(/l/) { |bar| "РуÑÑкий".force_encoding("iso-8859-5") } }.should raise_error(Encoding::CompatibilityError)
+ end
+
+ it "replaces the incompatible part properly even if the encodings are not compatible" do
+ s = "hllëllo"
+
+ s.gsub(/ë/) { |bar| "РуÑÑкий".force_encoding("iso-8859-5") }.encoding.should == Encoding::ISO_8859_5
+ end
+
+ not_supported_on :opal do
+ it "raises an ArgumentError if encoding is not valid" do
+ x92 = [0x92].pack('C').force_encoding('utf-8')
+ lambda { "a#{x92}b".gsub(/[^\x00-\x7f]/u, '') }.should raise_error(ArgumentError)
+ end
+ end
+end
+
+describe "String#gsub with pattern and without replacement and block" do
+ it "returns an enumerator" do
+ enum = "abca".gsub(/a/)
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.should == ["a", "a"]
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ "abca".gsub(/a/).size.should == nil
+ end
+ end
+ end
+end
+
+describe "String#gsub! with pattern and replacement" do
+ it "modifies self in place and returns self" do
+ a = "hello"
+ a.gsub!(/[aeiou]/, '*').should equal(a)
+ a.should == "h*ll*"
+ end
+
+ it "modifies self in place with multi-byte characters and returns self" do
+ a = "¿por qué?"
+ a.gsub!(/([a-z\d]*)/, "*").should equal(a)
+ a.should == "*¿** **é*?*"
+ end
+
+ it "taints self if replacement is tainted" do
+ a = "hello"
+ a.gsub!(/./.taint, "foo").tainted?.should == false
+ a.gsub!(/./, "foo".taint).tainted?.should == true
+ end
+
+ it "untrusts self if replacement is untrusted" do
+ a = "hello"
+ a.gsub!(/./.untrust, "foo").untrusted?.should == false
+ a.gsub!(/./, "foo".untrust).untrusted?.should == true
+ end
+
+ it "returns nil if no modifications were made" do
+ a = "hello"
+ a.gsub!(/z/, '*').should == nil
+ a.gsub!(/z/, 'z').should == nil
+ a.should == "hello"
+ end
+
+ # See [ruby-core:23666]
+ it "raises a #{frozen_error_class} when self is frozen" do
+ s = "hello"
+ s.freeze
+
+ lambda { s.gsub!(/ROAR/, "x") }.should raise_error(frozen_error_class)
+ lambda { s.gsub!(/e/, "e") }.should raise_error(frozen_error_class)
+ lambda { s.gsub!(/[aeiou]/, '*') }.should raise_error(frozen_error_class)
+ end
+end
+
+describe "String#gsub! with pattern and block" do
+ it "modifies self in place and returns self" do
+ a = "hello"
+ a.gsub!(/[aeiou]/) { '*' }.should equal(a)
+ a.should == "h*ll*"
+ end
+
+ it "taints self if block's result is tainted" do
+ a = "hello"
+ a.gsub!(/./.taint) { "foo" }.tainted?.should == false
+ a.gsub!(/./) { "foo".taint }.tainted?.should == true
+ end
+
+ it "untrusts self if block's result is untrusted" do
+ a = "hello"
+ a.gsub!(/./.untrust) { "foo" }.untrusted?.should == false
+ a.gsub!(/./) { "foo".untrust }.untrusted?.should == true
+ end
+
+ it "returns nil if no modifications were made" do
+ a = "hello"
+ a.gsub!(/z/) { '*' }.should == nil
+ a.gsub!(/z/) { 'z' }.should == nil
+ a.should == "hello"
+ end
+
+ # See [ruby-core:23663]
+ it "raises a #{frozen_error_class} when self is frozen" do
+ s = "hello"
+ s.freeze
+
+ lambda { s.gsub!(/ROAR/) { "x" } }.should raise_error(frozen_error_class)
+ lambda { s.gsub!(/e/) { "e" } }.should raise_error(frozen_error_class)
+ lambda { s.gsub!(/[aeiou]/) { '*' } }.should raise_error(frozen_error_class)
+ end
+
+ it "uses the compatible encoding if they are compatible" do
+ s = "hello"
+ s2 = "#{195.chr}#{192.chr}#{195.chr}"
+
+ s.gsub!(/l/) { |bar| 195.chr }.encoding.should == Encoding::ASCII_8BIT
+ s2.gsub!("#{192.chr}") { |bar| "hello" }.encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are not compatible" do
+ s = "hllëllo"
+ s2 = "hellö"
+
+ lambda { s.gsub!(/l/) { |bar| "РуÑÑкий".force_encoding("iso-8859-5") } }.should raise_error(Encoding::CompatibilityError)
+ lambda { s2.gsub!(/l/) { |bar| "РуÑÑкий".force_encoding("iso-8859-5") } }.should raise_error(Encoding::CompatibilityError)
+ end
+
+ it "replaces the incompatible part properly even if the encodings are not compatible" do
+ s = "hllëllo"
+
+ s.gsub!(/ë/) { |bar| "РуÑÑкий".force_encoding("iso-8859-5") }.encoding.should == Encoding::ISO_8859_5
+ end
+
+ not_supported_on :opal do
+ it "raises an ArgumentError if encoding is not valid" do
+ x92 = [0x92].pack('C').force_encoding('utf-8')
+ lambda { "a#{x92}b".gsub!(/[^\x00-\x7f]/u, '') }.should raise_error(ArgumentError)
+ end
+ end
+end
+
+describe "String#gsub! with pattern and without replacement and block" do
+ it "returns an enumerator" do
+ enum = "abca".gsub!(/a/)
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.should == ["a", "a"]
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ "abca".gsub!(/a/).size.should == nil
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/hash_spec.rb b/spec/ruby/core/string/hash_spec.rb
new file mode 100644
index 0000000000..0b26214b55
--- /dev/null
+++ b/spec/ruby/core/string/hash_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#hash" do
+ it "returns a hash based on a string's length and content" do
+ "abc".hash.should == "abc".hash
+ "abc".hash.should_not == "cba".hash
+ end
+end
diff --git a/spec/ruby/core/string/hex_spec.rb b/spec/ruby/core/string/hex_spec.rb
new file mode 100644
index 0000000000..364e915681
--- /dev/null
+++ b/spec/ruby/core/string/hex_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# TODO: Move actual results to String#to_int() and spec in terms of it
+describe "String#hex" do
+ it "treats leading characters of self as a string of hex digits" do
+ "0a".hex.should == 10
+ "0o".hex.should == 0
+ "0x".hex.should == 0
+ "A_BAD_BABE".hex.should == 0xABADBABE
+ "0b1010".hex.should == "b1010".hex
+ "0d500".hex.should == "d500".hex
+ "abcdefG".hex.should == 0xabcdef
+ end
+
+ it "does not accept a sequence of underscores as part of a number" do
+ "a__b".hex.should == 0xa
+ "a____b".hex.should == 0xa
+ "a___f".hex.should == 0xa
+ end
+
+ it "takes an optional sign" do
+ "-1234".hex.should == -4660
+ "+1234".hex.should == 4660
+ end
+
+ it "takes an optional 0x" do
+ "0x0a".hex.should == 10
+ "0a".hex.should == 10
+ end
+
+ it "requires that the sign is in front of the 0x if present" do
+ "-0x1".hex.should == -1
+ "0x-1".hex.should == 0
+ end
+
+ it "returns 0 on error" do
+ "".hex.should == 0
+ "+-5".hex.should == 0
+ "wombat".hex.should == 0
+ "0x0x42".hex.should == 0
+ end
+
+ it "returns 0 if sequence begins with underscore" do
+ "_a".hex.should == 0
+ "___b".hex.should == 0
+ "___0xc".hex.should == 0
+ end
+end
diff --git a/spec/ruby/core/string/include_spec.rb b/spec/ruby/core/string/include_spec.rb
new file mode 100644
index 0000000000..03ef3ba802
--- /dev/null
+++ b/spec/ruby/core/string/include_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#include? with String" do
+ it "returns true if self contains other_str" do
+ "hello".include?("lo").should == true
+ "hello".include?("ol").should == false
+ end
+
+ it "ignores subclass differences" do
+ "hello".include?(StringSpecs::MyString.new("lo")).should == true
+ StringSpecs::MyString.new("hello").include?("lo").should == true
+ StringSpecs::MyString.new("hello").include?(StringSpecs::MyString.new("lo")).should == true
+ end
+
+ it "tries to convert other to string using to_str" do
+ other = mock('lo')
+ other.should_receive(:to_str).and_return("lo")
+
+ "hello".include?(other).should == true
+ end
+
+ it "raises a TypeError if other can't be converted to string" do
+ lambda { "hello".include?([]) }.should raise_error(TypeError)
+ lambda { "hello".include?('h'.ord) }.should raise_error(TypeError)
+ lambda { "hello".include?(mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ pat = "ã‚¢".encode Encoding::EUC_JP
+ lambda do
+ "ã‚れ".include?(pat)
+ end.should raise_error(Encoding::CompatibilityError)
+ end
+end
diff --git a/spec/ruby/core/string/index_spec.rb b/spec/ruby/core/string/index_spec.rb
new file mode 100644
index 0000000000..3ed27034e1
--- /dev/null
+++ b/spec/ruby/core/string/index_spec.rb
@@ -0,0 +1,316 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#index" do
+ it "raises a TypeError if passed nil" do
+ lambda { "abc".index nil }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed a boolean" do
+ lambda { "abc".index true }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed a Symbol" do
+ lambda { "abc".index :a }.should raise_error(TypeError)
+ end
+
+ it "calls #to_str to convert the first argument" do
+ char = mock("string index char")
+ char.should_receive(:to_str).and_return("b")
+ "abc".index(char).should == 1
+ end
+
+ it "calls #to_int to convert the second argument" do
+ offset = mock("string index offset")
+ offset.should_receive(:to_int).and_return(1)
+ "abc".index("c", offset).should == 2
+ end
+
+ it "raises a TypeError if passed a Fixnum" do
+ lambda { "abc".index 97 }.should raise_error(TypeError)
+ end
+end
+
+describe "String#index with String" do
+ it "behaves the same as String#index(char) for one-character strings" do
+ "blablabla hello cruel world...!".split("").uniq.each do |str|
+ chr = str[0]
+ str.index(str).should == str.index(chr)
+
+ 0.upto(str.size + 1) do |start|
+ str.index(str, start).should == str.index(chr, start)
+ end
+
+ (-str.size - 1).upto(-1) do |start|
+ str.index(str, start).should == str.index(chr, start)
+ end
+ end
+ end
+
+ it "returns the index of the first occurrence of the given substring" do
+ "blablabla".index("").should == 0
+ "blablabla".index("b").should == 0
+ "blablabla".index("bla").should == 0
+ "blablabla".index("blabla").should == 0
+ "blablabla".index("blablabla").should == 0
+
+ "blablabla".index("l").should == 1
+ "blablabla".index("la").should == 1
+ "blablabla".index("labla").should == 1
+ "blablabla".index("lablabla").should == 1
+
+ "blablabla".index("a").should == 2
+ "blablabla".index("abla").should == 2
+ "blablabla".index("ablabla").should == 2
+ end
+
+ it "doesn't set $~" do
+ $~ = nil
+
+ 'hello.'.index('ll')
+ $~.should == nil
+ end
+
+ it "ignores string subclasses" do
+ "blablabla".index(StringSpecs::MyString.new("bla")).should == 0
+ StringSpecs::MyString.new("blablabla").index("bla").should == 0
+ StringSpecs::MyString.new("blablabla").index(StringSpecs::MyString.new("bla")).should == 0
+ end
+
+ it "starts the search at the given offset" do
+ "blablabla".index("bl", 0).should == 0
+ "blablabla".index("bl", 1).should == 3
+ "blablabla".index("bl", 2).should == 3
+ "blablabla".index("bl", 3).should == 3
+
+ "blablabla".index("bla", 0).should == 0
+ "blablabla".index("bla", 1).should == 3
+ "blablabla".index("bla", 2).should == 3
+ "blablabla".index("bla", 3).should == 3
+
+ "blablabla".index("blab", 0).should == 0
+ "blablabla".index("blab", 1).should == 3
+ "blablabla".index("blab", 2).should == 3
+ "blablabla".index("blab", 3).should == 3
+
+ "blablabla".index("la", 1).should == 1
+ "blablabla".index("la", 2).should == 4
+ "blablabla".index("la", 3).should == 4
+ "blablabla".index("la", 4).should == 4
+
+ "blablabla".index("lab", 1).should == 1
+ "blablabla".index("lab", 2).should == 4
+ "blablabla".index("lab", 3).should == 4
+ "blablabla".index("lab", 4).should == 4
+
+ "blablabla".index("ab", 2).should == 2
+ "blablabla".index("ab", 3).should == 5
+ "blablabla".index("ab", 4).should == 5
+ "blablabla".index("ab", 5).should == 5
+
+ "blablabla".index("", 0).should == 0
+ "blablabla".index("", 1).should == 1
+ "blablabla".index("", 2).should == 2
+ "blablabla".index("", 7).should == 7
+ "blablabla".index("", 8).should == 8
+ "blablabla".index("", 9).should == 9
+ end
+
+ it "starts the search at offset + self.length if offset is negative" do
+ str = "blablabla"
+
+ ["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle|
+ (-str.length .. -1).each do |offset|
+ str.index(needle, offset).should ==
+ str.index(needle, offset + str.length)
+ end
+ end
+ end
+
+ it "returns nil if the substring isn't found" do
+ "blablabla".index("B").should == nil
+ "blablabla".index("z").should == nil
+ "blablabla".index("BLA").should == nil
+ "blablabla".index("blablablabla").should == nil
+ "blablabla".index("", 10).should == nil
+
+ "hello".index("he", 1).should == nil
+ "hello".index("he", 2).should == nil
+ "I’ve got a multibyte character.\n".index("\n\n").should == nil
+ end
+
+ with_feature :encoding do
+ it "returns the character index of a multibyte character" do
+ "ã‚りãŒã¨ã†".index("ãŒ").should == 2
+ end
+
+ it "returns the character index after offset" do
+ "ã‚れã‚れ".index("ã‚", 1).should == 2
+ end
+
+ it "returns the character index after a partial first match" do
+ "</</h".index("</h").should == 2
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ char = "れ".encode Encoding::EUC_JP
+ lambda do
+ "ã‚れ".index char
+ end.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+end
+
+describe "String#index with Regexp" do
+ it "behaves the same as String#index(string) for escaped string regexps" do
+ ["blablabla", "hello cruel world...!"].each do |str|
+ ["", "b", "bla", "lab", "o c", "d."].each do |needle|
+ regexp = Regexp.new(Regexp.escape(needle))
+ str.index(regexp).should == str.index(needle)
+
+ 0.upto(str.size + 1) do |start|
+ str.index(regexp, start).should == str.index(needle, start)
+ end
+
+ (-str.size - 1).upto(-1) do |start|
+ str.index(regexp, start).should == str.index(needle, start)
+ end
+ end
+ end
+ end
+
+ it "returns the index of the first match of regexp" do
+ "blablabla".index(/bla/).should == 0
+ "blablabla".index(/BLA/i).should == 0
+
+ "blablabla".index(/.{0}/).should == 0
+ "blablabla".index(/.{6}/).should == 0
+ "blablabla".index(/.{9}/).should == 0
+
+ "blablabla".index(/.*/).should == 0
+ "blablabla".index(/.+/).should == 0
+
+ "blablabla".index(/lab|b/).should == 0
+
+ not_supported_on :opal do
+ "blablabla".index(/\A/).should == 0
+ "blablabla".index(/\Z/).should == 9
+ "blablabla".index(/\z/).should == 9
+ "blablabla\n".index(/\Z/).should == 9
+ "blablabla\n".index(/\z/).should == 10
+ end
+
+ "blablabla".index(/^/).should == 0
+ "\nblablabla".index(/^/).should == 0
+ "b\nablabla".index(/$/).should == 1
+ "bl\nablabla".index(/$/).should == 2
+
+ "blablabla".index(/.l./).should == 0
+ end
+
+ it "sets $~ to MatchData of match and nil when there's none" do
+ 'hello.'.index(/.(.)/)
+ $~[0].should == 'he'
+
+ 'hello.'.index(/not/)
+ $~.should == nil
+ end
+
+ it "starts the search at the given offset" do
+ "blablabla".index(/.{0}/, 5).should == 5
+ "blablabla".index(/.{1}/, 5).should == 5
+ "blablabla".index(/.{2}/, 5).should == 5
+ "blablabla".index(/.{3}/, 5).should == 5
+ "blablabla".index(/.{4}/, 5).should == 5
+
+ "blablabla".index(/.{0}/, 3).should == 3
+ "blablabla".index(/.{1}/, 3).should == 3
+ "blablabla".index(/.{2}/, 3).should == 3
+ "blablabla".index(/.{5}/, 3).should == 3
+ "blablabla".index(/.{6}/, 3).should == 3
+
+ "blablabla".index(/.l./, 0).should == 0
+ "blablabla".index(/.l./, 1).should == 3
+ "blablabla".index(/.l./, 2).should == 3
+ "blablabla".index(/.l./, 3).should == 3
+
+ "xblaxbla".index(/x./, 0).should == 0
+ "xblaxbla".index(/x./, 1).should == 4
+ "xblaxbla".index(/x./, 2).should == 4
+
+ not_supported_on :opal do
+ "blablabla\n".index(/\Z/, 9).should == 9
+ end
+ end
+
+ it "starts the search at offset + self.length if offset is negative" do
+ str = "blablabla"
+
+ ["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle|
+ (-str.length .. -1).each do |offset|
+ str.index(needle, offset).should ==
+ str.index(needle, offset + str.length)
+ end
+ end
+ end
+
+ it "returns nil if the substring isn't found" do
+ "blablabla".index(/BLA/).should == nil
+
+ "blablabla".index(/.{10}/).should == nil
+ "blaxbla".index(/.x/, 3).should == nil
+ "blaxbla".index(/..x/, 2).should == nil
+ end
+
+ it "returns nil if the Regexp matches the empty string and the offset is out of range" do
+ "ruby".index(//,12).should be_nil
+ end
+
+ it "supports \\G which matches at the given start offset" do
+ "helloYOU.".index(/\GYOU/, 5).should == 5
+ "helloYOU.".index(/\GYOU/).should == nil
+
+ re = /\G.+YOU/
+ # The # marks where \G will match.
+ [
+ ["#hi!YOUall.", 0],
+ ["h#i!YOUall.", 1],
+ ["hi#!YOUall.", 2],
+ ["hi!#YOUall.", nil]
+ ].each do |spec|
+
+ start = spec[0].index("#")
+ str = spec[0].delete("#")
+
+ str.index(re, start).should == spec[1]
+ end
+ end
+
+ it "converts start_offset to an integer via to_int" do
+ obj = mock('1')
+ obj.should_receive(:to_int).and_return(1)
+ "RWOARW".index(/R./, obj).should == 4
+ end
+
+ with_feature :encoding do
+ it "returns the character index of a multibyte character" do
+ "ã‚りãŒã¨ã†".index(/ãŒ/).should == 2
+ end
+
+ it "returns the character index after offset" do
+ "ã‚れã‚れ".index(/ã‚/, 1).should == 2
+ end
+
+ it "treats the offset as a character index" do
+ "ã‚れã‚ã‚れ".index(/ã‚/, 3).should == 3
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ re = Regexp.new "れ".encode(Encoding::EUC_JP)
+ lambda do
+ "ã‚れ".index re
+ end.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/initialize_spec.rb b/spec/ruby/core/string/initialize_spec.rb
new file mode 100644
index 0000000000..08734cc916
--- /dev/null
+++ b/spec/ruby/core/string/initialize_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/replace'
+
+describe "String#initialize" do
+ it "is a private method" do
+ String.should have_private_instance_method(:initialize)
+ end
+
+ describe "with no arguments" do
+ it "does not change self" do
+ s = "some string"
+ s.send :initialize
+ s.should == "some string"
+ end
+
+ it "does not raise an exception when frozen" do
+ a = "hello".freeze
+ a.send(:initialize).should equal(a)
+ end
+ end
+
+ describe "with an argument" do
+ it_behaves_like :string_replace, :initialize
+ end
+end
diff --git a/spec/ruby/core/string/insert_spec.rb b/spec/ruby/core/string/insert_spec.rb
new file mode 100644
index 0000000000..2c72bb5ffd
--- /dev/null
+++ b/spec/ruby/core/string/insert_spec.rb
@@ -0,0 +1,84 @@
+# -*- encoding: utf-8 -*-
+
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#insert with index, other" do
+ it "inserts other before the character at the given index" do
+ "abcd".insert(0, 'X').should == "Xabcd"
+ "abcd".insert(3, 'X').should == "abcXd"
+ "abcd".insert(4, 'X').should == "abcdX"
+ end
+
+ it "modifies self in place" do
+ a = "abcd"
+ a.insert(4, 'X').should == "abcdX"
+ a.should == "abcdX"
+ end
+
+ it "inserts after the given character on an negative count" do
+ "abcd".insert(-5, 'X').should == "Xabcd"
+ "abcd".insert(-3, 'X').should == "abXcd"
+ "abcd".insert(-1, 'X').should == "abcdX"
+ end
+
+ it "raises an IndexError if the index is beyond string" do
+ lambda { "abcd".insert(5, 'X') }.should raise_error(IndexError)
+ lambda { "abcd".insert(-6, 'X') }.should raise_error(IndexError)
+ end
+
+ it "converts index to an integer using to_int" do
+ other = mock('-3')
+ other.should_receive(:to_int).and_return(-3)
+
+ "abcd".insert(other, "XYZ").should == "abXYZcd"
+ end
+
+ it "converts other to a string using to_str" do
+ other = mock('XYZ')
+ other.should_receive(:to_str).and_return("XYZ")
+
+ "abcd".insert(-3, other).should == "abXYZcd"
+ end
+
+ it "taints self if string to insert is tainted" do
+ str = "abcd"
+ str.insert(0, "T".taint).tainted?.should == true
+
+ str = "abcd"
+ other = mock('T')
+ def other.to_str() "T".taint end
+ str.insert(0, other).tainted?.should == true
+ end
+
+ it "raises a TypeError if other can't be converted to string" do
+ lambda { "abcd".insert(-6, Object.new)}.should raise_error(TypeError)
+ lambda { "abcd".insert(-6, []) }.should raise_error(TypeError)
+ lambda { "abcd".insert(-6, mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "raises a #{frozen_error_class} if self is frozen" do
+ str = "abcd".freeze
+ lambda { str.insert(4, '') }.should raise_error(frozen_error_class)
+ lambda { str.insert(4, 'X') }.should raise_error(frozen_error_class)
+ end
+
+ with_feature :encoding do
+ it "inserts a character into a multibyte encoded string" do
+ "ã‚りãŒã¨ã†".insert(1, 'ü').should == "ã‚üりãŒã¨ã†"
+ end
+
+ it "returns a String in the compatible encoding" do
+ str = "".force_encoding(Encoding::US_ASCII)
+ str.insert(0, "ã‚りãŒã¨ã†")
+ str.encoding.should == Encoding::UTF_8
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ pat = "ã‚¢".encode Encoding::EUC_JP
+ lambda do
+ "ã‚れ".insert 0, pat
+ end.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/inspect_spec.rb b/spec/ruby/core/string/inspect_spec.rb
new file mode 100644
index 0000000000..2cfcfe0e76
--- /dev/null
+++ b/spec/ruby/core/string/inspect_spec.rb
@@ -0,0 +1,492 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#inspect" do
+ it "taints the result if self is tainted" do
+ "foo".taint.inspect.tainted?.should == true
+ "foo\n".taint.inspect.tainted?.should == true
+ end
+
+ it "untrusts the result if self is untrusted" do
+ "foo".untrust.inspect.untrusted?.should == true
+ "foo\n".untrust.inspect.untrusted?.should == true
+ end
+
+ it "does not return a subclass instance" do
+ StringSpecs::MyString.new.inspect.should be_an_instance_of(String)
+ end
+
+ it "returns a string with special characters replaced with \\<char> notation" do
+ [ ["\a", '"\\a"'],
+ ["\b", '"\\b"'],
+ ["\t", '"\\t"'],
+ ["\n", '"\\n"'],
+ ["\v", '"\\v"'],
+ ["\f", '"\\f"'],
+ ["\r", '"\\r"'],
+ ["\e", '"\\e"']
+ ].should be_computed_by(:inspect)
+ end
+
+ it "returns a string with \" and \\ escaped with a backslash" do
+ [ ["\"", '"\\""'],
+ ["\\", '"\\\\"']
+ ].should be_computed_by(:inspect)
+ end
+
+ it "returns a string with \\#<char> when # is followed by $, @, {" do
+ [ ["\#$", '"\\#$"'],
+ ["\#@", '"\\#@"'],
+ ["\#{", '"\\#{"']
+ ].should be_computed_by(:inspect)
+ end
+
+ it "returns a string with # not escaped when followed by any other character" do
+ [ ["#", '"#"'],
+ ["#1", '"#1"']
+ ].should be_computed_by(:inspect)
+ end
+
+ it "returns a string with printable non-alphanumeric characters unescaped" do
+ [ [" ", '" "'],
+ ["!", '"!"'],
+ ["$", '"$"'],
+ ["%", '"%"'],
+ ["&", '"&"'],
+ ["'", '"\'"'],
+ ["(", '"("'],
+ [")", '")"'],
+ ["*", '"*"'],
+ ["+", '"+"'],
+ [",", '","'],
+ ["-", '"-"'],
+ [".", '"."'],
+ ["/", '"/"'],
+ [":", '":"'],
+ [";", '";"'],
+ ["<", '"<"'],
+ ["=", '"="'],
+ [">", '">"'],
+ ["?", '"?"'],
+ ["@", '"@"'],
+ ["[", '"["'],
+ ["]", '"]"'],
+ ["^", '"^"'],
+ ["_", '"_"'],
+ ["`", '"`"'],
+ ["{", '"{"'],
+ ["|", '"|"'],
+ ["}", '"}"'],
+ ["~", '"~"']
+ ].should be_computed_by(:inspect)
+ end
+
+ it "returns a string with numeric characters unescaped" do
+ [ ["0", '"0"'],
+ ["1", '"1"'],
+ ["2", '"2"'],
+ ["3", '"3"'],
+ ["4", '"4"'],
+ ["5", '"5"'],
+ ["6", '"6"'],
+ ["7", '"7"'],
+ ["8", '"8"'],
+ ["9", '"9"'],
+ ].should be_computed_by(:inspect)
+ end
+
+ it "returns a string with upper-case alpha characters unescaped" do
+ [ ["A", '"A"'],
+ ["B", '"B"'],
+ ["C", '"C"'],
+ ["D", '"D"'],
+ ["E", '"E"'],
+ ["F", '"F"'],
+ ["G", '"G"'],
+ ["H", '"H"'],
+ ["I", '"I"'],
+ ["J", '"J"'],
+ ["K", '"K"'],
+ ["L", '"L"'],
+ ["M", '"M"'],
+ ["N", '"N"'],
+ ["O", '"O"'],
+ ["P", '"P"'],
+ ["Q", '"Q"'],
+ ["R", '"R"'],
+ ["S", '"S"'],
+ ["T", '"T"'],
+ ["U", '"U"'],
+ ["V", '"V"'],
+ ["W", '"W"'],
+ ["X", '"X"'],
+ ["Y", '"Y"'],
+ ["Z", '"Z"']
+ ].should be_computed_by(:inspect)
+ end
+
+ it "returns a string with lower-case alpha characters unescaped" do
+ [ ["a", '"a"'],
+ ["b", '"b"'],
+ ["c", '"c"'],
+ ["d", '"d"'],
+ ["e", '"e"'],
+ ["f", '"f"'],
+ ["g", '"g"'],
+ ["h", '"h"'],
+ ["i", '"i"'],
+ ["j", '"j"'],
+ ["k", '"k"'],
+ ["l", '"l"'],
+ ["m", '"m"'],
+ ["n", '"n"'],
+ ["o", '"o"'],
+ ["p", '"p"'],
+ ["q", '"q"'],
+ ["r", '"r"'],
+ ["s", '"s"'],
+ ["t", '"t"'],
+ ["u", '"u"'],
+ ["v", '"v"'],
+ ["w", '"w"'],
+ ["x", '"x"'],
+ ["y", '"y"'],
+ ["z", '"z"']
+ ].should be_computed_by(:inspect)
+ end
+
+ it "returns a string with non-printing characters replaced by \\x notation" do
+ # Avoid the file encoding by computing the string with #chr.
+ [ [0001.chr, '"\\x01"'],
+ [0002.chr, '"\\x02"'],
+ [0003.chr, '"\\x03"'],
+ [0004.chr, '"\\x04"'],
+ [0005.chr, '"\\x05"'],
+ [0006.chr, '"\\x06"'],
+ [0016.chr, '"\\x0E"'],
+ [0017.chr, '"\\x0F"'],
+ [0020.chr, '"\\x10"'],
+ [0021.chr, '"\\x11"'],
+ [0022.chr, '"\\x12"'],
+ [0023.chr, '"\\x13"'],
+ [0024.chr, '"\\x14"'],
+ [0025.chr, '"\\x15"'],
+ [0026.chr, '"\\x16"'],
+ [0027.chr, '"\\x17"'],
+ [0030.chr, '"\\x18"'],
+ [0031.chr, '"\\x19"'],
+ [0032.chr, '"\\x1A"'],
+ [0034.chr, '"\\x1C"'],
+ [0035.chr, '"\\x1D"'],
+ [0036.chr, '"\\x1E"'],
+ [0037.chr, '"\\x1F"'],
+ [0177.chr, '"\\x7F"'],
+ [0200.chr, '"\\x80"'],
+ [0201.chr, '"\\x81"'],
+ [0202.chr, '"\\x82"'],
+ [0203.chr, '"\\x83"'],
+ [0204.chr, '"\\x84"'],
+ [0205.chr, '"\\x85"'],
+ [0206.chr, '"\\x86"'],
+ [0207.chr, '"\\x87"'],
+ [0210.chr, '"\\x88"'],
+ [0211.chr, '"\\x89"'],
+ [0212.chr, '"\\x8A"'],
+ [0213.chr, '"\\x8B"'],
+ [0214.chr, '"\\x8C"'],
+ [0215.chr, '"\\x8D"'],
+ [0216.chr, '"\\x8E"'],
+ [0217.chr, '"\\x8F"'],
+ [0220.chr, '"\\x90"'],
+ [0221.chr, '"\\x91"'],
+ [0222.chr, '"\\x92"'],
+ [0223.chr, '"\\x93"'],
+ [0224.chr, '"\\x94"'],
+ [0225.chr, '"\\x95"'],
+ [0226.chr, '"\\x96"'],
+ [0227.chr, '"\\x97"'],
+ [0230.chr, '"\\x98"'],
+ [0231.chr, '"\\x99"'],
+ [0232.chr, '"\\x9A"'],
+ [0233.chr, '"\\x9B"'],
+ [0234.chr, '"\\x9C"'],
+ [0235.chr, '"\\x9D"'],
+ [0236.chr, '"\\x9E"'],
+ [0237.chr, '"\\x9F"'],
+ [0240.chr, '"\\xA0"'],
+ [0241.chr, '"\\xA1"'],
+ [0242.chr, '"\\xA2"'],
+ [0243.chr, '"\\xA3"'],
+ [0244.chr, '"\\xA4"'],
+ [0245.chr, '"\\xA5"'],
+ [0246.chr, '"\\xA6"'],
+ [0247.chr, '"\\xA7"'],
+ [0250.chr, '"\\xA8"'],
+ [0251.chr, '"\\xA9"'],
+ [0252.chr, '"\\xAA"'],
+ [0253.chr, '"\\xAB"'],
+ [0254.chr, '"\\xAC"'],
+ [0255.chr, '"\\xAD"'],
+ [0256.chr, '"\\xAE"'],
+ [0257.chr, '"\\xAF"'],
+ [0260.chr, '"\\xB0"'],
+ [0261.chr, '"\\xB1"'],
+ [0262.chr, '"\\xB2"'],
+ [0263.chr, '"\\xB3"'],
+ [0264.chr, '"\\xB4"'],
+ [0265.chr, '"\\xB5"'],
+ [0266.chr, '"\\xB6"'],
+ [0267.chr, '"\\xB7"'],
+ [0270.chr, '"\\xB8"'],
+ [0271.chr, '"\\xB9"'],
+ [0272.chr, '"\\xBA"'],
+ [0273.chr, '"\\xBB"'],
+ [0274.chr, '"\\xBC"'],
+ [0275.chr, '"\\xBD"'],
+ [0276.chr, '"\\xBE"'],
+ [0277.chr, '"\\xBF"'],
+ [0300.chr, '"\\xC0"'],
+ [0301.chr, '"\\xC1"'],
+ [0302.chr, '"\\xC2"'],
+ [0303.chr, '"\\xC3"'],
+ [0304.chr, '"\\xC4"'],
+ [0305.chr, '"\\xC5"'],
+ [0306.chr, '"\\xC6"'],
+ [0307.chr, '"\\xC7"'],
+ [0310.chr, '"\\xC8"'],
+ [0311.chr, '"\\xC9"'],
+ [0312.chr, '"\\xCA"'],
+ [0313.chr, '"\\xCB"'],
+ [0314.chr, '"\\xCC"'],
+ [0315.chr, '"\\xCD"'],
+ [0316.chr, '"\\xCE"'],
+ [0317.chr, '"\\xCF"'],
+ [0320.chr, '"\\xD0"'],
+ [0321.chr, '"\\xD1"'],
+ [0322.chr, '"\\xD2"'],
+ [0323.chr, '"\\xD3"'],
+ [0324.chr, '"\\xD4"'],
+ [0325.chr, '"\\xD5"'],
+ [0326.chr, '"\\xD6"'],
+ [0327.chr, '"\\xD7"'],
+ [0330.chr, '"\\xD8"'],
+ [0331.chr, '"\\xD9"'],
+ [0332.chr, '"\\xDA"'],
+ [0333.chr, '"\\xDB"'],
+ [0334.chr, '"\\xDC"'],
+ [0335.chr, '"\\xDD"'],
+ [0336.chr, '"\\xDE"'],
+ [0337.chr, '"\\xDF"'],
+ [0340.chr, '"\\xE0"'],
+ [0341.chr, '"\\xE1"'],
+ [0342.chr, '"\\xE2"'],
+ [0343.chr, '"\\xE3"'],
+ [0344.chr, '"\\xE4"'],
+ [0345.chr, '"\\xE5"'],
+ [0346.chr, '"\\xE6"'],
+ [0347.chr, '"\\xE7"'],
+ [0350.chr, '"\\xE8"'],
+ [0351.chr, '"\\xE9"'],
+ [0352.chr, '"\\xEA"'],
+ [0353.chr, '"\\xEB"'],
+ [0354.chr, '"\\xEC"'],
+ [0355.chr, '"\\xED"'],
+ [0356.chr, '"\\xEE"'],
+ [0357.chr, '"\\xEF"'],
+ [0360.chr, '"\\xF0"'],
+ [0361.chr, '"\\xF1"'],
+ [0362.chr, '"\\xF2"'],
+ [0363.chr, '"\\xF3"'],
+ [0364.chr, '"\\xF4"'],
+ [0365.chr, '"\\xF5"'],
+ [0366.chr, '"\\xF6"'],
+ [0367.chr, '"\\xF7"'],
+ [0370.chr, '"\\xF8"'],
+ [0371.chr, '"\\xF9"'],
+ [0372.chr, '"\\xFA"'],
+ [0373.chr, '"\\xFB"'],
+ [0374.chr, '"\\xFC"'],
+ [0375.chr, '"\\xFD"'],
+ [0376.chr, '"\\xFE"'],
+ [0377.chr, '"\\xFF"']
+ ].should be_computed_by(:inspect)
+ end
+
+ it "returns a string with a NUL character replaced by \\x notation" do
+ 0.chr.inspect.should == '"\\x00"'
+ end
+
+ describe "when default external is UTF-8" do
+ before :each do
+ @extenc, Encoding.default_external = Encoding.default_external, Encoding::UTF_8
+ end
+
+ after :each do
+ Encoding.default_external = @extenc
+ end
+
+ it "returns a string with non-printing characters replaced by \\u notation for Unicode strings" do
+ [ [0001.chr('utf-8'), '"\u0001"'],
+ [0002.chr('utf-8'), '"\u0002"'],
+ [0003.chr('utf-8'), '"\u0003"'],
+ [0004.chr('utf-8'), '"\u0004"'],
+ [0005.chr('utf-8'), '"\u0005"'],
+ [0006.chr('utf-8'), '"\u0006"'],
+ [0016.chr('utf-8'), '"\u000E"'],
+ [0017.chr('utf-8'), '"\u000F"'],
+ [0020.chr('utf-8'), '"\u0010"'],
+ [0021.chr('utf-8'), '"\u0011"'],
+ [0022.chr('utf-8'), '"\u0012"'],
+ [0023.chr('utf-8'), '"\u0013"'],
+ [0024.chr('utf-8'), '"\u0014"'],
+ [0025.chr('utf-8'), '"\u0015"'],
+ [0026.chr('utf-8'), '"\u0016"'],
+ [0027.chr('utf-8'), '"\u0017"'],
+ [0030.chr('utf-8'), '"\u0018"'],
+ [0031.chr('utf-8'), '"\u0019"'],
+ [0032.chr('utf-8'), '"\u001A"'],
+ [0034.chr('utf-8'), '"\u001C"'],
+ [0035.chr('utf-8'), '"\u001D"'],
+ [0036.chr('utf-8'), '"\u001E"'],
+ [0037.chr('utf-8'), '"\u001F"'],
+ [0177.chr('utf-8'), '"\u007F"'],
+ [0200.chr('utf-8'), '"\u0080"'],
+ [0201.chr('utf-8'), '"\u0081"'],
+ [0202.chr('utf-8'), '"\u0082"'],
+ [0203.chr('utf-8'), '"\u0083"'],
+ [0204.chr('utf-8'), '"\u0084"'],
+ [0206.chr('utf-8'), '"\u0086"'],
+ [0207.chr('utf-8'), '"\u0087"'],
+ [0210.chr('utf-8'), '"\u0088"'],
+ [0211.chr('utf-8'), '"\u0089"'],
+ [0212.chr('utf-8'), '"\u008A"'],
+ [0213.chr('utf-8'), '"\u008B"'],
+ [0214.chr('utf-8'), '"\u008C"'],
+ [0215.chr('utf-8'), '"\u008D"'],
+ [0216.chr('utf-8'), '"\u008E"'],
+ [0217.chr('utf-8'), '"\u008F"'],
+ [0220.chr('utf-8'), '"\u0090"'],
+ [0221.chr('utf-8'), '"\u0091"'],
+ [0222.chr('utf-8'), '"\u0092"'],
+ [0223.chr('utf-8'), '"\u0093"'],
+ [0224.chr('utf-8'), '"\u0094"'],
+ [0225.chr('utf-8'), '"\u0095"'],
+ [0226.chr('utf-8'), '"\u0096"'],
+ [0227.chr('utf-8'), '"\u0097"'],
+ [0230.chr('utf-8'), '"\u0098"'],
+ [0231.chr('utf-8'), '"\u0099"'],
+ [0232.chr('utf-8'), '"\u009A"'],
+ [0233.chr('utf-8'), '"\u009B"'],
+ [0234.chr('utf-8'), '"\u009C"'],
+ [0235.chr('utf-8'), '"\u009D"'],
+ [0236.chr('utf-8'), '"\u009E"'],
+ [0237.chr('utf-8'), '"\u009F"'],
+ ].should be_computed_by(:inspect)
+ end
+
+ it "returns a string with a NUL character replaced by \\u notation" do
+ 0.chr('utf-8').inspect.should == '"\\u0000"'
+ end
+
+ it "returns a string with extended characters for Unicode strings" do
+ [ [0240.chr('utf-8'), '" "'],
+ [0241.chr('utf-8'), '"¡"'],
+ [0242.chr('utf-8'), '"¢"'],
+ [0243.chr('utf-8'), '"£"'],
+ [0244.chr('utf-8'), '"¤"'],
+ [0245.chr('utf-8'), '"Â¥"'],
+ [0246.chr('utf-8'), '"¦"'],
+ [0247.chr('utf-8'), '"§"'],
+ [0250.chr('utf-8'), '"¨"'],
+ [0251.chr('utf-8'), '"©"'],
+ [0252.chr('utf-8'), '"ª"'],
+ [0253.chr('utf-8'), '"«"'],
+ [0254.chr('utf-8'), '"¬"'],
+ [0255.chr('utf-8'), '"­"'],
+ [0256.chr('utf-8'), '"®"'],
+ [0257.chr('utf-8'), '"¯"'],
+ [0260.chr('utf-8'), '"°"'],
+ [0261.chr('utf-8'), '"±"'],
+ [0262.chr('utf-8'), '"²"'],
+ [0263.chr('utf-8'), '"³"'],
+ [0264.chr('utf-8'), '"´"'],
+ [0265.chr('utf-8'), '"µ"'],
+ [0266.chr('utf-8'), '"¶"'],
+ [0267.chr('utf-8'), '"·"'],
+ [0270.chr('utf-8'), '"¸"'],
+ [0271.chr('utf-8'), '"¹"'],
+ [0272.chr('utf-8'), '"º"'],
+ [0273.chr('utf-8'), '"»"'],
+ [0274.chr('utf-8'), '"¼"'],
+ [0275.chr('utf-8'), '"½"'],
+ [0276.chr('utf-8'), '"¾"'],
+ [0277.chr('utf-8'), '"¿"'],
+ [0300.chr('utf-8'), '"À"'],
+ [0301.chr('utf-8'), '"Ã"'],
+ [0302.chr('utf-8'), '"Â"'],
+ [0303.chr('utf-8'), '"Ã"'],
+ [0304.chr('utf-8'), '"Ä"'],
+ [0305.chr('utf-8'), '"Ã…"'],
+ [0306.chr('utf-8'), '"Æ"'],
+ [0307.chr('utf-8'), '"Ç"'],
+ [0310.chr('utf-8'), '"È"'],
+ [0311.chr('utf-8'), '"É"'],
+ [0312.chr('utf-8'), '"Ê"'],
+ [0313.chr('utf-8'), '"Ë"'],
+ [0314.chr('utf-8'), '"Ì"'],
+ [0315.chr('utf-8'), '"Ã"'],
+ [0316.chr('utf-8'), '"ÃŽ"'],
+ [0317.chr('utf-8'), '"Ã"'],
+ [0320.chr('utf-8'), '"Ã"'],
+ [0321.chr('utf-8'), '"Ñ"'],
+ [0322.chr('utf-8'), '"Ã’"'],
+ [0323.chr('utf-8'), '"Ó"'],
+ [0324.chr('utf-8'), '"Ô"'],
+ [0325.chr('utf-8'), '"Õ"'],
+ [0326.chr('utf-8'), '"Ö"'],
+ [0327.chr('utf-8'), '"×"'],
+ [0330.chr('utf-8'), '"Ø"'],
+ [0331.chr('utf-8'), '"Ù"'],
+ [0332.chr('utf-8'), '"Ú"'],
+ [0333.chr('utf-8'), '"Û"'],
+ [0334.chr('utf-8'), '"Ü"'],
+ [0335.chr('utf-8'), '"Ã"'],
+ [0336.chr('utf-8'), '"Þ"'],
+ [0337.chr('utf-8'), '"ß"'],
+ [0340.chr('utf-8'), '"à"'],
+ [0341.chr('utf-8'), '"á"'],
+ [0342.chr('utf-8'), '"â"'],
+ [0343.chr('utf-8'), '"ã"'],
+ [0344.chr('utf-8'), '"ä"'],
+ [0345.chr('utf-8'), '"Ã¥"'],
+ [0346.chr('utf-8'), '"æ"'],
+ [0347.chr('utf-8'), '"ç"'],
+ [0350.chr('utf-8'), '"è"'],
+ [0351.chr('utf-8'), '"é"'],
+ [0352.chr('utf-8'), '"ê"'],
+ [0353.chr('utf-8'), '"ë"'],
+ [0354.chr('utf-8'), '"ì"'],
+ [0355.chr('utf-8'), '"í"'],
+ [0356.chr('utf-8'), '"î"'],
+ [0357.chr('utf-8'), '"ï"'],
+ [0360.chr('utf-8'), '"ð"'],
+ [0361.chr('utf-8'), '"ñ"'],
+ [0362.chr('utf-8'), '"ò"'],
+ [0363.chr('utf-8'), '"ó"'],
+ [0364.chr('utf-8'), '"ô"'],
+ [0365.chr('utf-8'), '"õ"'],
+ [0366.chr('utf-8'), '"ö"'],
+ [0367.chr('utf-8'), '"÷"'],
+ [0370.chr('utf-8'), '"ø"'],
+ [0371.chr('utf-8'), '"ù"'],
+ [0372.chr('utf-8'), '"ú"'],
+ [0373.chr('utf-8'), '"û"'],
+ [0374.chr('utf-8'), '"ü"'],
+ [0375.chr('utf-8'), '"ý"'],
+ [0376.chr('utf-8'), '"þ"'],
+ [0377.chr('utf-8'), '"ÿ"']
+ ].should be_computed_by(:inspect)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/intern_spec.rb b/spec/ruby/core/string/intern_spec.rb
new file mode 100644
index 0000000000..cd7dad4359
--- /dev/null
+++ b/spec/ruby/core/string/intern_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/to_sym'
+
+describe "String#intern" do
+ it_behaves_like :string_to_sym, :intern
+end
diff --git a/spec/ruby/core/string/length_spec.rb b/spec/ruby/core/string/length_spec.rb
new file mode 100644
index 0000000000..98cee1f03d
--- /dev/null
+++ b/spec/ruby/core/string/length_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/length'
+
+describe "String#length" do
+ it_behaves_like :string_length, :length
+end
diff --git a/spec/ruby/core/string/lines_spec.rb b/spec/ruby/core/string/lines_spec.rb
new file mode 100644
index 0000000000..1c13936c30
--- /dev/null
+++ b/spec/ruby/core/string/lines_spec.rb
@@ -0,0 +1,22 @@
+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
+
+ it "returns an array when no block given" do
+ ary = "hello world".lines(' ')
+ ary.should == ["hello ", "world"]
+ end
+
+ ruby_version_is '2.4' do
+ context "when `chomp` keyword argument is passed" do
+ it "removes new line characters" do
+ "hello \nworld\n".lines(chomp: true).should == ["hello ", "world"]
+ "hello \r\nworld\r\n".lines(chomp: true).should == ["hello ", "world"]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/ljust_spec.rb b/spec/ruby/core/string/ljust_spec.rb
new file mode 100644
index 0000000000..5838a0c9f9
--- /dev/null
+++ b/spec/ruby/core/string/ljust_spec.rb
@@ -0,0 +1,116 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#ljust with length, padding" do
+ it "returns a new string of specified length with self left justified and padded with padstr" do
+ "hello".ljust(20, '1234').should == "hello123412341234123"
+
+ "".ljust(1, "abcd").should == "a"
+ "".ljust(2, "abcd").should == "ab"
+ "".ljust(3, "abcd").should == "abc"
+ "".ljust(4, "abcd").should == "abcd"
+ "".ljust(6, "abcd").should == "abcdab"
+
+ "OK".ljust(3, "abcd").should == "OKa"
+ "OK".ljust(4, "abcd").should == "OKab"
+ "OK".ljust(6, "abcd").should == "OKabcd"
+ "OK".ljust(8, "abcd").should == "OKabcdab"
+ end
+
+ it "pads with whitespace if no padstr is given" do
+ "hello".ljust(20).should == "hello "
+ end
+
+ it "returns self if it's longer than or as long as the specified length" do
+ "".ljust(0).should == ""
+ "".ljust(-1).should == ""
+ "hello".ljust(4).should == "hello"
+ "hello".ljust(-1).should == "hello"
+ "this".ljust(3).should == "this"
+ "radiology".ljust(8, '-').should == "radiology"
+ end
+
+ it "taints result when self or padstr is tainted" do
+ "x".taint.ljust(4).tainted?.should == true
+ "x".taint.ljust(0).tainted?.should == true
+ "".taint.ljust(0).tainted?.should == true
+ "x".taint.ljust(4, "*").tainted?.should == true
+ "x".ljust(4, "*".taint).tainted?.should == true
+ end
+
+ it "tries to convert length to an integer using to_int" do
+ "^".ljust(3.8, "_^").should == "^_^"
+
+ obj = mock('3')
+ obj.should_receive(:to_int).and_return(3)
+
+ "o".ljust(obj, "_o").should == "o_o"
+ end
+
+ it "raises a TypeError when length can't be converted to an integer" do
+ lambda { "hello".ljust("x") }.should raise_error(TypeError)
+ lambda { "hello".ljust("x", "y") }.should raise_error(TypeError)
+ lambda { "hello".ljust([]) }.should raise_error(TypeError)
+ lambda { "hello".ljust(mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "tries to convert padstr to a string using to_str" do
+ padstr = mock('123')
+ padstr.should_receive(:to_str).and_return("123")
+
+ "hello".ljust(10, padstr).should == "hello12312"
+ end
+
+ it "raises a TypeError when padstr can't be converted" do
+ lambda { "hello".ljust(20, []) }.should raise_error(TypeError)
+ lambda { "hello".ljust(20, Object.new)}.should raise_error(TypeError)
+ lambda { "hello".ljust(20, mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError when padstr is empty" do
+ lambda { "hello".ljust(10, '') }.should raise_error(ArgumentError)
+ end
+
+ it "returns subclass instances when called on subclasses" do
+ StringSpecs::MyString.new("").ljust(10).should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("foo").ljust(10).should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("foo").ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString)
+
+ "".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
+ "foo".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
+ end
+
+ it "when padding is tainted and self is untainted returns a tainted string if and only if length is longer than self" do
+ "hello".ljust(4, 'X'.taint).tainted?.should be_false
+ "hello".ljust(5, 'X'.taint).tainted?.should be_false
+ "hello".ljust(6, 'X'.taint).tainted?.should be_true
+ end
+
+ with_feature :encoding do
+ describe "with width" do
+ it "returns a String in the same encoding as the original" do
+ str = "abc".force_encoding Encoding::IBM437
+ result = str.ljust 5
+ result.should == "abc "
+ result.encoding.should equal(Encoding::IBM437)
+ end
+ end
+
+ describe "with width, pattern" do
+ it "returns a String in the compatible encoding" do
+ str = "abc".force_encoding Encoding::IBM437
+ result = str.ljust 5, "ã‚"
+ result.should == "abcã‚ã‚"
+ result.encoding.should equal(Encoding::UTF_8)
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ pat = "ã‚¢".encode Encoding::EUC_JP
+ lambda do
+ "ã‚れ".ljust 5, pat
+ end.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/lstrip_spec.rb b/spec/ruby/core/string/lstrip_spec.rb
new file mode 100644
index 0000000000..ecfc89dd6b
--- /dev/null
+++ b/spec/ruby/core/string/lstrip_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#lstrip" do
+ it "returns a copy of self with leading whitespace removed" do
+ " hello ".lstrip.should == "hello "
+ " hello world ".lstrip.should == "hello world "
+ "\n\r\t\n\v\r hello world ".lstrip.should == "hello world "
+ "hello".lstrip.should == "hello"
+ "\000 \000hello\000 \000".lstrip.should == "\000 \000hello\000 \000"
+ end
+
+ it "does not strip leading \\0" do
+ "\x00hello".lstrip.should == "\x00hello"
+ end
+
+ it "taints the result when self is tainted" do
+ "".taint.lstrip.tainted?.should == true
+ "ok".taint.lstrip.tainted?.should == true
+ " ok".taint.lstrip.tainted?.should == true
+ end
+end
+
+describe "String#lstrip!" do
+ it "modifies self in place and returns self" do
+ a = " hello "
+ a.lstrip!.should equal(a)
+ a.should == "hello "
+
+ a = "\000 \000hello\000 \000"
+ a.lstrip!
+ a.should == "\000 \000hello\000 \000"
+ end
+
+ it "returns nil if no modifications were made" do
+ a = "hello"
+ a.lstrip!.should == nil
+ a.should == "hello"
+ end
+
+ it "raises a #{frozen_error_class} on a frozen instance that is modified" do
+ lambda { " hello ".freeze.lstrip! }.should raise_error(frozen_error_class)
+ end
+
+ # see [ruby-core:23657]
+ it "raises a #{frozen_error_class} on a frozen instance that would not be modified" do
+ lambda { "hello".freeze.lstrip! }.should raise_error(frozen_error_class)
+ lambda { "".freeze.lstrip! }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/string/match_spec.rb b/spec/ruby/core/string/match_spec.rb
new file mode 100644
index 0000000000..7f3d9ebae5
--- /dev/null
+++ b/spec/ruby/core/string/match_spec.rb
@@ -0,0 +1,175 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe :string_match_escaped_literal, shared: true do
+ not_supported_on :opal do
+ it "matches a literal Regexp that uses ASCII-only UTF-8 escape sequences" do
+ "a b".match(/([\u{20}-\u{7e}])/)[0].should == "a"
+ end
+ end
+end
+
+describe "String#=~" do
+ it "behaves the same way as index() when given a regexp" do
+ ("rudder" =~ /udder/).should == "rudder".index(/udder/)
+ ("boat" =~ /[^fl]oat/).should == "boat".index(/[^fl]oat/)
+ ("bean" =~ /bag/).should == "bean".index(/bag/)
+ ("true" =~ /false/).should == "true".index(/false/)
+ end
+
+ it "raises a TypeError if a obj is a string" do
+ lambda { "some string" =~ "another string" }.should raise_error(TypeError)
+ lambda { "a" =~ StringSpecs::MyString.new("b") }.should raise_error(TypeError)
+ end
+
+ it "invokes obj.=~ with self if obj is neither a string nor regexp" do
+ str = "w00t"
+ obj = mock('x')
+
+ obj.should_receive(:=~).with(str).any_number_of_times.and_return(true)
+ str.should =~ obj
+
+ obj = mock('y')
+ obj.should_receive(:=~).with(str).any_number_of_times.and_return(false)
+ str.should_not =~ obj
+ end
+
+ it "sets $~ to MatchData when there is a match and nil when there's none" do
+ 'hello' =~ /./
+ $~[0].should == 'h'
+
+ 'hello' =~ /not/
+ $~.should == nil
+ end
+
+ with_feature :encoding do
+ it "returns the character index of a found match" do
+ ("ã“ã«ã¡ã‚" =~ /ã«/).should == 1
+ end
+ end
+
+end
+
+describe "String#match" do
+ it "matches the pattern against self" do
+ 'hello'.match(/(.)\1/)[0].should == 'll'
+ end
+
+ it_behaves_like :string_match_escaped_literal, :match
+
+ describe "with [pattern, position]" do
+ describe "when given a positive position" do
+ it "matches the pattern against self starting at an optional index" do
+ "01234".match(/(.).(.)/, 1).captures.should == ["1", "3"]
+ end
+
+ with_feature :encoding do
+ it "uses the start as a character offset" do
+ "零一二三四".match(/(.).(.)/, 1).captures.should == ["一", "三"]
+ end
+ end
+ end
+
+ describe "when given a negative position" do
+ it "matches the pattern against self starting at an optional index" do
+ "01234".match(/(.).(.)/, -4).captures.should == ["1", "3"]
+ end
+
+ with_feature :encoding do
+ it "uses the start as a character offset" do
+ "零一二三四".match(/(.).(.)/, -4).captures.should == ["一", "三"]
+ end
+ end
+ end
+ end
+
+ describe "when passed a block" do
+ it "yields the MatchData" do
+ "abc".match(/./) {|m| ScratchPad.record m }
+ ScratchPad.recorded.should be_kind_of(MatchData)
+ end
+
+ it "returns the block result" do
+ "abc".match(/./) { :result }.should == :result
+ end
+
+ it "does not yield if there is no match" do
+ ScratchPad.record []
+ "b".match(/a/) {|m| ScratchPad << m }
+ ScratchPad.recorded.should == []
+ end
+ end
+
+ it "tries to convert pattern to a string via to_str" do
+ obj = mock('.')
+ def obj.to_str() "." end
+ "hello".match(obj)[0].should == "h"
+
+ obj = mock('.')
+ def obj.respond_to?(type, *) true end
+ def obj.method_missing(*args) "." end
+ "hello".match(obj)[0].should == "h"
+ end
+
+ it "raises a TypeError if pattern is not a regexp or a string" do
+ lambda { 'hello'.match(10) }.should raise_error(TypeError)
+ not_supported_on :opal do
+ lambda { 'hello'.match(:ell) }.should raise_error(TypeError)
+ end
+ end
+
+ it "converts string patterns to regexps without escaping" do
+ 'hello'.match('(.)\1')[0].should == 'll'
+ end
+
+ it "returns nil if there's no match" do
+ 'hello'.match('xx').should == nil
+ end
+
+ it "matches \\G at the start of the string" do
+ 'hello'.match(/\Gh/)[0].should == 'h'
+ 'hello'.match(/\Go/).should == nil
+ end
+
+ it "sets $~ to MatchData of match or nil when there is none" do
+ 'hello'.match(/./)
+ $~[0].should == 'h'
+ Regexp.last_match[0].should == 'h'
+
+ 'hello'.match(/X/)
+ $~.should == nil
+ Regexp.last_match.should == nil
+ end
+
+ it "calls match on the regular expression" do
+ regexp = /./
+ regexp.should_receive(:match).and_return(:foo)
+ 'hello'.match(regexp).should == :foo
+ end
+end
+
+ruby_version_is "2.4" do
+ describe "String#match?" do
+ before :each do
+ # Resetting Regexp.last_match
+ /DONTMATCH/.match ''
+ end
+
+ context "when matches the given regex" do
+ it "returns true but does not set Regexp.last_match" do
+ 'string'.match?(/string/i).should be_true
+ Regexp.last_match.should be_nil
+ end
+ end
+
+ it "returns false when does not match the given regex" do
+ 'string'.match?(/STRING/).should be_false
+ end
+
+ it "takes matching position as the 2nd argument" do
+ 'string'.match?(/str/i, 0).should be_true
+ 'string'.match?(/str/i, 1).should be_false
+ end
+ end
+end
diff --git a/spec/ruby/core/string/modulo_spec.rb b/spec/ruby/core/string/modulo_spec.rb
new file mode 100644
index 0000000000..72738ece24
--- /dev/null
+++ b/spec/ruby/core/string/modulo_spec.rb
@@ -0,0 +1,807 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/hash/key_error'
+
+describe "String#%" do
+ context "when key is missing from passed-in hash" do
+ it_behaves_like :key_error, -> (obj, key) { "%{#{key}}" % obj }, { a: 5 }
+ end
+
+ it "formats multiple expressions" do
+ ("%b %x %d %s" % [10, 10, 10, 10]).should == "1010 a 10 10"
+ end
+
+ it "formats expressions mid string" do
+ ("hello %s!" % "world").should == "hello world!"
+ end
+
+ it "formats %% into %" do
+ ("%d%% %s" % [10, "of chickens!"]).should == "10% of chickens!"
+ end
+
+ describe "output's encoding" do
+ it "is the same as the format string if passed value is encoding-compatible" do
+ [Encoding::ASCII_8BIT, Encoding::US_ASCII, Encoding::UTF_8, Encoding::SHIFT_JIS].each do |encoding|
+ ("hello %s!".encode(encoding) % "world").encoding.should == encoding
+ end
+ end
+
+ it "negotiates a compatible encoding if necessary" do
+ ("hello %s" % 195.chr).encoding.should == Encoding::ASCII_8BIT
+ ("hello %s".encode("shift_jis") % "wörld").encoding.should == Encoding::UTF_8
+ end
+
+ it "raises if a compatible encoding can't be found" do
+ lambda { "hello %s".encode("utf-8") % "world".encode("UTF-16LE") }.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+
+ ruby_version_is ""..."2.5" do
+ it "formats single % character at the end as literal %" do
+ ("%" % []).should == "%"
+ ("foo%" % []).should == "foo%"
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "raises an error if single % appears at the end" do
+ lambda { ("%" % []) }.should raise_error(ArgumentError)
+ lambda { ("foo%" % [])}.should raise_error(ArgumentError)
+ end
+ end
+
+ it "formats single % character before a newline as literal %" do
+ ("%\n" % []).should == "%\n"
+ ("foo%\n" % []).should == "foo%\n"
+ ("%\n.3f" % 1.2).should == "%\n.3f"
+ end
+
+ it "formats single % character before a NUL as literal %" do
+ ("%\0" % []).should == "%\0"
+ ("foo%\0" % []).should == "foo%\0"
+ ("%\0.3f" % 1.2).should == "%\0.3f"
+ end
+
+ it "raises an error if single % appears anywhere else" do
+ lambda { (" % " % []) }.should raise_error(ArgumentError)
+ lambda { ("foo%quux" % []) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error if NULL or \\n appear anywhere else in the format string" do
+ begin
+ old_debug, $DEBUG = $DEBUG, false
+
+ lambda { "%.\n3f" % 1.2 }.should raise_error(ArgumentError)
+ lambda { "%.3\nf" % 1.2 }.should raise_error(ArgumentError)
+ lambda { "%.\03f" % 1.2 }.should raise_error(ArgumentError)
+ lambda { "%.3\0f" % 1.2 }.should raise_error(ArgumentError)
+ ensure
+ $DEBUG = old_debug
+ end
+ end
+
+ it "ignores unused arguments when $DEBUG is false" do
+ begin
+ old_debug = $DEBUG
+ $DEBUG = false
+
+ ("" % [1, 2, 3]).should == ""
+ ("%s" % [1, 2, 3]).should == "1"
+ ensure
+ $DEBUG = old_debug
+ end
+ end
+
+ it "raises an ArgumentError for unused arguments when $DEBUG is true" do
+ begin
+ old_debug = $DEBUG
+ $DEBUG = true
+ s = $stderr
+ $stderr = IOStub.new
+
+ lambda { "" % [1, 2, 3] }.should raise_error(ArgumentError)
+ lambda { "%s" % [1, 2, 3] }.should raise_error(ArgumentError)
+ ensure
+ $DEBUG = old_debug
+ $stderr = s
+ end
+ end
+
+ it "always allows unused arguments when positional argument style is used" do
+ begin
+ old_debug = $DEBUG
+ $DEBUG = false
+
+ ("%2$s" % [1, 2, 3]).should == "2"
+ $DEBUG = true
+ ("%2$s" % [1, 2, 3]).should == "2"
+ ensure
+ $DEBUG = old_debug
+ end
+ end
+
+ it "replaces trailing absolute argument specifier without type with percent sign" do
+ ("hello %1$" % "foo").should == "hello %"
+ end
+
+ it "raises an ArgumentError when given invalid argument specifiers" do
+ lambda { "%1" % [] }.should raise_error(ArgumentError)
+ lambda { "%+" % [] }.should raise_error(ArgumentError)
+ lambda { "%-" % [] }.should raise_error(ArgumentError)
+ lambda { "%#" % [] }.should raise_error(ArgumentError)
+ lambda { "%0" % [] }.should raise_error(ArgumentError)
+ lambda { "%*" % [] }.should raise_error(ArgumentError)
+ lambda { "%." % [] }.should raise_error(ArgumentError)
+ lambda { "%_" % [] }.should raise_error(ArgumentError)
+ lambda { "%0$s" % "x" }.should raise_error(ArgumentError)
+ lambda { "%*0$s" % [5, "x"] }.should raise_error(ArgumentError)
+ lambda { "%*1$.*0$1$s" % [1, 2, 3] }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when multiple positional argument tokens are given for one format specifier" do
+ lambda { "%1$1$s" % "foo" }.should raise_error(ArgumentError)
+ end
+
+ it "respects positional arguments and precision tokens given for one format specifier" do
+ ("%2$1d" % [1, 0]).should == "0"
+ ("%2$1d" % [0, 1]).should == "1"
+
+ ("%2$.2f" % [1, 0]).should == "0.00"
+ ("%2$.2f" % [0, 1]).should == "1.00"
+ end
+
+ it "allows more than one digit of position" do
+ ("%50$d" % (0..100).to_a).should == "49"
+ end
+
+ it "raises an ArgumentError when multiple width star tokens are given for one format specifier" do
+ lambda { "%**s" % [5, 5, 5] }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when a width star token is seen after a width token" do
+ lambda { "%5*s" % [5, 5] }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when multiple precision tokens are given" do
+ lambda { "%.5.5s" % 5 }.should raise_error(ArgumentError)
+ lambda { "%.5.*s" % [5, 5] }.should raise_error(ArgumentError)
+ lambda { "%.*.5s" % [5, 5] }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when there are less arguments than format specifiers" do
+ ("foo" % []).should == "foo"
+ lambda { "%s" % [] }.should raise_error(ArgumentError)
+ lambda { "%s %s" % [1] }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when absolute and relative argument numbers are mixed" do
+ lambda { "%s %1$s" % "foo" }.should raise_error(ArgumentError)
+ lambda { "%1$s %s" % "foo" }.should raise_error(ArgumentError)
+
+ lambda { "%s %2$s" % ["foo", "bar"] }.should raise_error(ArgumentError)
+ lambda { "%2$s %s" % ["foo", "bar"] }.should raise_error(ArgumentError)
+
+ lambda { "%*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
+ lambda { "%*.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
+ lambda { "%*2$.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
+ lambda { "%*.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
+ end
+
+ it "allows reuse of the one argument multiple via absolute argument numbers" do
+ ("%1$s %1$s" % "foo").should == "foo foo"
+ ("%1$s %2$s %1$s %2$s" % ["foo", "bar"]).should == "foo bar foo bar"
+ end
+
+ it "always interprets an array argument as a list of argument parameters" do
+ lambda { "%p" % [] }.should raise_error(ArgumentError)
+ ("%p" % [1]).should == "1"
+ ("%p %p" % [1, 2]).should == "1 2"
+ end
+
+ it "always interprets an array subclass argument as a list of argument parameters" do
+ lambda { "%p" % StringSpecs::MyArray[] }.should raise_error(ArgumentError)
+ ("%p" % StringSpecs::MyArray[1]).should == "1"
+ ("%p %p" % StringSpecs::MyArray[1, 2]).should == "1 2"
+ end
+
+ it "allows positional arguments for width star and precision star arguments" do
+ ("%*1$.*2$3$d" % [10, 5, 1]).should == " 00001"
+ end
+
+ it "allows negative width to imply '-' flag" do
+ ("%*1$.*2$3$d" % [-10, 5, 1]).should == "00001 "
+ ("%-*1$.*2$3$d" % [10, 5, 1]).should == "00001 "
+ ("%-*1$.*2$3$d" % [-10, 5, 1]).should == "00001 "
+ end
+
+ it "ignores negative precision" do
+ ("%*1$.*2$3$d" % [10, -5, 1]).should == " 1"
+ end
+
+ it "allows a star to take an argument number to use as the width" do
+ ("%1$*2$s" % ["a", 8]).should == " a"
+ ("%1$*10$s" % ["a",0,0,0,0,0,0,0,0,8]).should == " a"
+ end
+
+ it "calls to_int on width star and precision star tokens" do
+ w = mock('10')
+ w.should_receive(:to_int).and_return(10)
+
+ p = mock('5')
+ p.should_receive(:to_int).and_return(5)
+
+ ("%*.*f" % [w, p, 1]).should == " 1.00000"
+
+
+ w = mock('10')
+ w.should_receive(:to_int).and_return(10)
+
+ p = mock('5')
+ p.should_receive(:to_int).and_return(5)
+
+ ("%*.*d" % [w, p, 1]).should == " 00001"
+ end
+
+ it "does not call #to_a to convert the argument" do
+ x = mock("string modulo to_a")
+ x.should_not_receive(:to_a)
+ x.should_receive(:to_s).and_return("x")
+
+ ("%s" % x).should == "x"
+ end
+
+ it "calls #to_ary to convert the argument" do
+ x = mock("string modulo to_ary")
+ x.should_not_receive(:to_s)
+ x.should_receive(:to_ary).and_return(["x"])
+
+ ("%s" % x).should == "x"
+ end
+
+ it "wraps the object in an Array if #to_ary returns nil" do
+ x = mock("string modulo to_ary")
+ x.should_receive(:to_ary).and_return(nil)
+ x.should_receive(:to_s).and_return("x")
+
+ ("%s" % x).should == "x"
+ end
+
+ it "raises a TypeError if #to_ary does not return an Array" do
+ x = mock("string modulo to_ary")
+ x.should_receive(:to_ary).and_return("x")
+
+ lambda { "%s" % x }.should raise_error(TypeError)
+ end
+
+ it "tries to convert the argument to Array by calling #to_ary" do
+ obj = mock('[1,2]')
+ def obj.to_ary() [1, 2] end
+ def obj.to_s() "obj" end
+ ("%s %s" % obj).should == "1 2"
+ ("%s" % obj).should == "1"
+ end
+
+ it "doesn't return subclass instances when called on a subclass" do
+ universal = mock('0')
+ def universal.to_int() 0 end
+ def universal.to_str() "0" end
+ def universal.to_f() 0.0 end
+
+ [
+ "", "foo",
+ "%b", "%B", "%c", "%d", "%e", "%E",
+ "%f", "%g", "%G", "%i", "%o", "%p",
+ "%s", "%u", "%x", "%X"
+ ].each do |format|
+ (StringSpecs::MyString.new(format) % universal).should be_an_instance_of(String)
+ end
+ end
+
+ it "always taints the result when the format string is tainted" do
+ universal = mock('0')
+ def universal.to_int() 0 end
+ def universal.to_str() "0" end
+ def universal.to_f() 0.0 end
+
+ [
+ "", "foo",
+ "%b", "%B", "%c", "%d", "%e", "%E",
+ "%f", "%g", "%G", "%i", "%o", "%p",
+ "%s", "%u", "%x", "%X"
+ ].each do |format|
+ subcls_format = StringSpecs::MyString.new(format)
+ subcls_format.taint
+ format.taint
+
+ (format % universal).tainted?.should == true
+ (subcls_format % universal).tainted?.should == true
+ end
+ end
+
+ it "supports binary formats using %b for positive numbers" do
+ ("%b" % 10).should == "1010"
+ ("% b" % 10).should == " 1010"
+ ("%1$b" % [10, 20]).should == "1010"
+ ("%#b" % 10).should == "0b1010"
+ ("%+b" % 10).should == "+1010"
+ ("%-9b" % 10).should == "1010 "
+ ("%05b" % 10).should == "01010"
+ ("%*b" % [10, 6]).should == " 110"
+ ("%*b" % [-10, 6]).should == "110 "
+ ("%.4b" % 2).should == "0010"
+ ("%.32b" % 2147483648).should == "10000000000000000000000000000000"
+ end
+
+ it "supports binary formats using %b for negative numbers" do
+ ("%b" % -5).should == "..1011"
+ ("%0b" % -5).should == "..1011"
+ ("%.1b" % -5).should == "..1011"
+ ("%.7b" % -5).should == "..11011"
+ ("%.10b" % -5).should == "..11111011"
+ ("% b" % -5).should == "-101"
+ ("%+b" % -5).should == "-101"
+ not_supported_on :opal do
+ ("%b" % -(2 ** 64 + 5)).should ==
+ "..101111111111111111111111111111111111111111111111111111111111111011"
+ end
+ end
+
+ it "supports binary formats using %B with same behaviour as %b except for using 0B instead of 0b for #" do
+ ("%B" % 10).should == ("%b" % 10)
+ ("% B" % 10).should == ("% b" % 10)
+ ("%1$B" % [10, 20]).should == ("%1$b" % [10, 20])
+ ("%+B" % 10).should == ("%+b" % 10)
+ ("%-9B" % 10).should == ("%-9b" % 10)
+ ("%05B" % 10).should == ("%05b" % 10)
+ ("%*B" % [10, 6]).should == ("%*b" % [10, 6])
+ ("%*B" % [-10, 6]).should == ("%*b" % [-10, 6])
+
+ ("%B" % -5).should == ("%b" % -5)
+ ("%0B" % -5).should == ("%0b" % -5)
+ ("%.1B" % -5).should == ("%.1b" % -5)
+ ("%.7B" % -5).should == ("%.7b" % -5)
+ ("%.10B" % -5).should == ("%.10b" % -5)
+ ("% B" % -5).should == ("% b" % -5)
+ ("%+B" % -5).should == ("%+b" % -5)
+ not_supported_on :opal do
+ ("%B" % -(2 ** 64 + 5)).should == ("%b" % -(2 ** 64 + 5))
+ end
+
+ ("%#B" % 10).should == "0B1010"
+ end
+
+ it "supports character formats using %c" do
+ ("%c" % 10).should == "\n"
+ ("%2$c" % [10, 11, 14]).should == "\v"
+ ("%-4c" % 10).should == "\n "
+ ("%*c" % [10, 3]).should == " \003"
+ ("%c" % 42).should == "*"
+
+ lambda { "%c" % Object }.should raise_error(TypeError)
+ end
+
+ it "supports single character strings as argument for %c" do
+ ("%c" % 'A').should == "A"
+ end
+
+ it "raises an exception for multiple character strings as argument for %c" do
+ lambda { "%c" % 'AA' }.should raise_error(ArgumentError)
+ end
+
+ it "calls to_str on argument for %c formats" do
+ obj = mock('A')
+ obj.should_receive(:to_str).and_return('A')
+
+ ("%c" % obj).should == "A"
+ end
+
+ it "calls #to_ary on argument for %c formats" do
+ obj = mock('65')
+ obj.should_receive(:to_ary).and_return([65])
+ ("%c" % obj).should == ("%c" % [65])
+ end
+
+ it "calls #to_int on argument for %c formats, if the argument does not respond to #to_ary" do
+ obj = mock('65')
+ obj.should_receive(:to_int).and_return(65)
+
+ ("%c" % obj).should == ("%c" % 65)
+ end
+
+ %w(d i).each do |f|
+ format = "%" + f
+
+ it "supports integer formats using #{format}" do
+ ("%#{f}" % 10).should == "10"
+ ("% #{f}" % 10).should == " 10"
+ ("%1$#{f}" % [10, 20]).should == "10"
+ ("%+#{f}" % 10).should == "+10"
+ ("%-7#{f}" % 10).should == "10 "
+ ("%04#{f}" % 10).should == "0010"
+ ("%*#{f}" % [10, 4]).should == " 4"
+ ("%6.4#{f}" % 123).should == " 0123"
+ end
+
+ it "supports negative integers using #{format}" do
+ ("%#{f}" % -5).should == "-5"
+ ("%3#{f}" % -5).should == " -5"
+ ("%03#{f}" % -5).should == "-05"
+ ("%+03#{f}" % -5).should == "-05"
+ ("%+.2#{f}" % -5).should == "-05"
+ ("%-3#{f}" % -5).should == "-5 "
+ ("%6.4#{f}" % -123).should == " -0123"
+ end
+
+ it "supports negative integers using #{format}, giving priority to `-`" do
+ ("%-03#{f}" % -5).should == "-5 "
+ ("%+-03#{f}" % -5).should == "-5 "
+ end
+ end
+
+ it "supports float formats using %e" do
+ ("%e" % 10).should == "1.000000e+01"
+ ("% e" % 10).should == " 1.000000e+01"
+ ("%1$e" % 10).should == "1.000000e+01"
+ ("%#e" % 10).should == "1.000000e+01"
+ ("%+e" % 10).should == "+1.000000e+01"
+ ("%-7e" % 10).should == "1.000000e+01"
+ ("%05e" % 10).should == "1.000000e+01"
+ ("%*e" % [10, 9]).should == "9.000000e+00"
+ end
+
+ it "supports float formats using %e, but Inf, -Inf, and NaN are not floats" do
+ ("%e" % 1e1020).should == "Inf"
+ ("%e" % -1e1020).should == "-Inf"
+ ("%e" % -Float::NAN).should == "NaN"
+ ("%e" % Float::NAN).should == "NaN"
+ end
+
+ it "supports float formats using %E, but Inf, -Inf, and NaN are not floats" do
+ ("%E" % 1e1020).should == "Inf"
+ ("%E" % -1e1020).should == "-Inf"
+ ("%-10E" % 1e1020).should == "Inf "
+ ("%10E" % 1e1020).should == " Inf"
+ ("%+E" % 1e1020).should == "+Inf"
+ ("% E" % 1e1020).should == " Inf"
+ ("%E" % Float::NAN).should == "NaN"
+ ("%E" % -Float::NAN).should == "NaN"
+ end
+
+ it "supports float formats using %E" do
+ ("%E" % 10).should == "1.000000E+01"
+ ("% E" % 10).should == " 1.000000E+01"
+ ("%1$E" % 10).should == "1.000000E+01"
+ ("%#E" % 10).should == "1.000000E+01"
+ ("%+E" % 10).should == "+1.000000E+01"
+ ("%-7E" % 10).should == "1.000000E+01"
+ ("%05E" % 10).should == "1.000000E+01"
+ ("%*E" % [10, 9]).should == "9.000000E+00"
+ end
+
+ it "pads with spaces for %E with Inf, -Inf, and NaN" do
+ ("%010E" % -1e1020).should == " -Inf"
+ ("%010E" % 1e1020).should == " Inf"
+ ("%010E" % Float::NAN).should == " NaN"
+ end
+
+ it "supports float formats using %f" do
+ ("%f" % 10).should == "10.000000"
+ ("% f" % 10).should == " 10.000000"
+ ("%1$f" % 10).should == "10.000000"
+ ("%#f" % 10).should == "10.000000"
+ ("%#0.3f" % 10).should == "10.000"
+ ("%+f" % 10).should == "+10.000000"
+ ("%-7f" % 10).should == "10.000000"
+ ("%05f" % 10).should == "10.000000"
+ ("%0.5f" % 10).should == "10.00000"
+ ("%*f" % [10, 9]).should == " 9.000000"
+ end
+
+ it "supports float formats using %g" do
+ ("%g" % 10).should == "10"
+ ("% g" % 10).should == " 10"
+ ("%1$g" % 10).should == "10"
+ ("%#g" % 10).should == "10.0000"
+ ("%#.3g" % 10).should == "10.0"
+ ("%+g" % 10).should == "+10"
+ ("%-7g" % 10).should == "10 "
+ ("%05g" % 10).should == "00010"
+ ("%g" % 10**10).should == "1e+10"
+ ("%*g" % [10, 9]).should == " 9"
+ end
+
+ it "supports float formats using %G" do
+ ("%G" % 10).should == "10"
+ ("% G" % 10).should == " 10"
+ ("%1$G" % 10).should == "10"
+ ("%#G" % 10).should == "10.0000"
+ ("%#.3G" % 10).should == "10.0"
+ ("%+G" % 10).should == "+10"
+ ("%-7G" % 10).should == "10 "
+ ("%05G" % 10).should == "00010"
+ ("%G" % 10**10).should == "1E+10"
+ ("%*G" % [10, 9]).should == " 9"
+ end
+
+ it "supports octal formats using %o for positive numbers" do
+ ("%o" % 10).should == "12"
+ ("% o" % 10).should == " 12"
+ ("%1$o" % [10, 20]).should == "12"
+ ("%#o" % 10).should == "012"
+ ("%+o" % 10).should == "+12"
+ ("%-9o" % 10).should == "12 "
+ ("%05o" % 10).should == "00012"
+ ("%*o" % [10, 6]).should == " 6"
+ end
+
+ it "supports octal formats using %o for negative numbers" do
+ # These are incredibly wrong. -05 == -5, not 7177777...whatever
+ ("%o" % -5).should == "..73"
+ ("%0o" % -5).should == "..73"
+ ("%.4o" % 20).should == "0024"
+ ("%.1o" % -5).should == "..73"
+ ("%.7o" % -5).should == "..77773"
+ ("%.10o" % -5).should == "..77777773"
+
+ ("% o" % -26).should == "-32"
+ ("%+o" % -26).should == "-32"
+ not_supported_on :opal do
+ ("%o" % -(2 ** 64 + 5)).should == "..75777777777777777777773"
+ end
+ end
+
+ it "supports inspect formats using %p" do
+ ("%p" % 10).should == "10"
+ ("%1$p" % [10, 5]).should == "10"
+ ("%-22p" % 10).should == "10 "
+ ("%*p" % [10, 10]).should == " 10"
+ ("%p" % {capture: 1}).should == "{:capture=>1}"
+ ("%p" % "str").should == "\"str\""
+ end
+
+ it "calls inspect on arguments for %p format" do
+ obj = mock('obj')
+ def obj.inspect() "obj" end
+ ("%p" % obj).should == "obj"
+
+ # undef is not working
+ # obj = mock('obj')
+ # class << obj; undef :inspect; end
+ # def obj.method_missing(*args) "obj" end
+ # ("%p" % obj).should == "obj"
+ end
+
+ it "taints result for %p when argument.inspect is tainted" do
+ obj = mock('x')
+ def obj.inspect() "x".taint end
+
+ ("%p" % obj).tainted?.should == true
+
+ obj = mock('x'); obj.taint
+ def obj.inspect() "x" end
+
+ ("%p" % obj).tainted?.should == false
+ end
+
+ it "supports string formats using %s" do
+ ("%s" % "hello").should == "hello"
+ ("%s" % "").should == ""
+ ("%s" % 10).should == "10"
+ ("%1$s" % [10, 8]).should == "10"
+ ("%-5s" % 10).should == "10 "
+ ("%*s" % [10, 9]).should == " 9"
+ end
+
+ it "respects a space padding request not as part of the width" do
+ x = "% -5s" % ["foo"]
+ x.should == "foo "
+ end
+
+ it "calls to_s on non-String arguments for %s format" do
+ obj = mock('obj')
+ def obj.to_s() "obj" end
+
+ ("%s" % obj).should == "obj"
+
+ # undef doesn't work
+ # obj = mock('obj')
+ # class << obj; undef :to_s; end
+ # def obj.method_missing(*args) "obj" end
+ #
+ # ("%s" % obj).should == "obj"
+ end
+
+ it "taints result for %s when argument is tainted" do
+ ("%s" % "x".taint).tainted?.should == true
+ ("%s" % mock('x').taint).tainted?.should == true
+ end
+
+ # MRI crashes on this one.
+ # See http://groups.google.com/group/ruby-core-google/t/c285c18cd94c216d
+ it "raises an ArgumentError for huge precisions for %s" do
+ block = lambda { "%.25555555555555555555555555555555555555s" % "hello world" }
+ block.should raise_error(ArgumentError)
+ end
+
+ # Note: %u has been changed to an alias for %d in 1.9.
+ it "supports unsigned formats using %u" do
+ ("%u" % 10).should == "10"
+ ("% u" % 10).should == " 10"
+ ("%1$u" % [10, 20]).should == "10"
+ ("%+u" % 10).should == "+10"
+ ("%-7u" % 10).should == "10 "
+ ("%04u" % 10).should == "0010"
+ ("%*u" % [10, 4]).should == " 4"
+ end
+
+ it "formats negative values with a leading sign using %u" do
+ ("% u" % -26).should == "-26"
+ ("%+u" % -26).should == "-26"
+ end
+
+ it "supports negative bignums with %u or %d" do
+ ("%u" % -(2 ** 64 + 5)).should == "-18446744073709551621"
+ ("%d" % -(2 ** 64 + 5)).should == "-18446744073709551621"
+ end
+
+ it "supports hex formats using %x for positive numbers" do
+ ("%x" % 10).should == "a"
+ ("% x" % 10).should == " a"
+ ("%1$x" % [10, 20]).should == "a"
+ ("%#x" % 10).should == "0xa"
+ ("%+x" % 10).should == "+a"
+ ("%-9x" % 10).should == "a "
+ ("%05x" % 10).should == "0000a"
+ ("%*x" % [10, 6]).should == " 6"
+ ("%.4x" % 20).should == "0014"
+ ("%x" % 0xFFFFFFFF).should == "ffffffff"
+ end
+
+ it "supports hex formats using %x for negative numbers" do
+ ("%x" % -5).should == "..fb"
+ ("%0x" % -5).should == "..fb"
+ ("%.1x" % -5).should == "..fb"
+ ("%.7x" % -5).should == "..ffffb"
+ ("%.10x" % -5).should == "..fffffffb"
+ ("% x" % -26).should == "-1a"
+ ("%+x" % -26).should == "-1a"
+ not_supported_on :opal do
+ ("%x" % -(2 ** 64 + 5)).should == "..fefffffffffffffffb"
+ end
+ end
+
+ it "supports hex formats using %X for positive numbers" do
+ ("%X" % 10).should == "A"
+ ("% X" % 10).should == " A"
+ ("%1$X" % [10, 20]).should == "A"
+ ("%#X" % 10).should == "0XA"
+ ("%+X" % 10).should == "+A"
+ ("%-9X" % 10).should == "A "
+ ("%05X" % 10).should == "0000A"
+ ("%*X" % [10, 6]).should == " 6"
+ ("%X" % 0xFFFFFFFF).should == "FFFFFFFF"
+ end
+
+ it "supports hex formats using %X for negative numbers" do
+ ("%X" % -5).should == "..FB"
+ ("%0X" % -5).should == "..FB"
+ ("%.1X" % -5).should == "..FB"
+ ("%.7X" % -5).should == "..FFFFB"
+ ("%.10X" % -5).should == "..FFFFFFFB"
+ ("% X" % -26).should == "-1A"
+ ("%+X" % -26).should == "-1A"
+ not_supported_on :opal do
+ ("%X" % -(2 ** 64 + 5)).should == "..FEFFFFFFFFFFFFFFFB"
+ end
+ end
+
+ it "formats zero without prefix using %#x" do
+ ("%#x" % 0).should == "0"
+ end
+
+ it "formats zero without prefix using %#X" do
+ ("%#X" % 0).should == "0"
+ end
+
+ %w(b d i o u x X).each do |f|
+ format = "%" + f
+
+ it "behaves as if calling Kernel#Integer for #{format} argument, if it does not respond to #to_ary" do
+ (format % "10").should == (format % Kernel.Integer("10"))
+ (format % "0x42").should == (format % Kernel.Integer("0x42"))
+ (format % "0b1101").should == (format % Kernel.Integer("0b1101"))
+ (format % "0b1101_0000").should == (format % Kernel.Integer("0b1101_0000"))
+ (format % "0777").should == (format % Kernel.Integer("0777"))
+ lambda {
+ # see [ruby-core:14139] for more details
+ (format % "0777").should == (format % Kernel.Integer("0777"))
+ }.should_not raise_error(ArgumentError)
+
+ lambda { format % "0__7_7_7" }.should raise_error(ArgumentError)
+
+ lambda { format % "" }.should raise_error(ArgumentError)
+ lambda { format % "x" }.should raise_error(ArgumentError)
+ lambda { format % "5x" }.should raise_error(ArgumentError)
+ lambda { format % "08" }.should raise_error(ArgumentError)
+ lambda { format % "0b2" }.should raise_error(ArgumentError)
+ lambda { format % "123__456" }.should raise_error(ArgumentError)
+
+ obj = mock('5')
+ obj.should_receive(:to_i).and_return(5)
+ (format % obj).should == (format % 5)
+
+ obj = mock('6')
+ obj.stub!(:to_i).and_return(5)
+ obj.should_receive(:to_int).and_return(6)
+ (format % obj).should == (format % 6)
+ end
+ end
+
+ %w(e E f g G).each do |f|
+ format = "%" + f
+
+ it "tries to convert the passed argument to an Array using #to_ary" do
+ obj = mock('3.14')
+ obj.should_receive(:to_ary).and_return([3.14])
+ (format % obj).should == (format % [3.14])
+ end
+
+ it "behaves as if calling Kernel#Float for #{format} arguments, when the passed argument does not respond to #to_ary" do
+ (format % 10).should == (format % 10.0)
+ (format % "-10.4e-20").should == (format % -10.4e-20)
+ (format % ".5").should == (format % 0.5)
+ (format % "-.5").should == (format % -0.5)
+ # Something's strange with this spec:
+ # it works just fine in individual mode, but not when run as part of a group
+ (format % "10_1_0.5_5_5").should == (format % 1010.555)
+
+ (format % "0777").should == (format % 777)
+
+ lambda { format % "" }.should raise_error(ArgumentError)
+ lambda { format % "x" }.should raise_error(ArgumentError)
+ lambda { format % "." }.should raise_error(ArgumentError)
+ lambda { format % "10." }.should raise_error(ArgumentError)
+ lambda { format % "5x" }.should raise_error(ArgumentError)
+ lambda { format % "0b1" }.should raise_error(ArgumentError)
+ lambda { format % "10e10.5" }.should raise_error(ArgumentError)
+ lambda { format % "10__10" }.should raise_error(ArgumentError)
+ lambda { format % "10.10__10" }.should raise_error(ArgumentError)
+
+ obj = mock('5.0')
+ obj.should_receive(:to_f).and_return(5.0)
+ (format % obj).should == (format % 5.0)
+ end
+
+ it "behaves as if calling Kernel#Float for #{format} arguments, when the passed argument is hexadecimal string" do
+ (format % "0xA").should == (format % 0xA)
+ end
+
+ it "doesn't taint the result for #{format} when argument is tainted" do
+ (format % "5".taint).tainted?.should == false
+ end
+ end
+
+ describe "when format string contains %{} sections" do
+ it "replaces %{} sections with values from passed-in hash" do
+ ("%{foo}bar" % {foo: 'oof'}).should == "oofbar"
+ end
+
+ it "should raise ArgumentError if no hash given" do
+ lambda {"%{foo}" % []}.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "when format string contains %<> formats" do
+ it "uses the named argument for the format's value" do
+ ("%<foo>d" % {foo: 1}).should == "1"
+ end
+
+ it "raises KeyError if key is missing from passed-in hash" do
+ lambda {"%<foo>d" % {}}.should raise_error(KeyError)
+ end
+
+ it "should raise ArgumentError if no hash given" do
+ lambda {"%<foo>" % []}.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/multiply_spec.rb b/spec/ruby/core/string/multiply_spec.rb
new file mode 100644
index 0000000000..7d887fd93a
--- /dev/null
+++ b/spec/ruby/core/string/multiply_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/string/times'
+
+describe "String#*" do
+ it_behaves_like :string_times, :*, ->(str, times) { str * times }
+end
diff --git a/spec/ruby/core/string/new_spec.rb b/spec/ruby/core/string/new_spec.rb
new file mode 100644
index 0000000000..a65c6da2d1
--- /dev/null
+++ b/spec/ruby/core/string/new_spec.rb
@@ -0,0 +1,63 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String.new" do
+ it "returns an instance of String" do
+ str = String.new
+ str.should be_an_instance_of(String)
+ end
+
+ it "accepts an encoding argument" do
+ xA4xA2 = [0xA4, 0xA2].pack('CC').force_encoding 'utf-8'
+ str = String.new(xA4xA2, encoding: 'euc-jp')
+ str.encoding.should == Encoding::EUC_JP
+ end
+
+ ruby_version_is "2.4" do
+ it "accepts a capacity argument" do
+ String.new("", capacity: 100_000).should == ""
+ String.new("abc", capacity: 100_000).should == "abc"
+ end
+ end
+
+ it "returns a fully-formed String" do
+ str = String.new
+ str.size.should == 0
+ str << "more"
+ str.should == "more"
+ end
+
+ it "returns a new string given a string argument" do
+ str1 = "test"
+ str = String.new(str1)
+ str.should be_an_instance_of(String)
+ str.should == str1
+ str << "more"
+ str.should == "testmore"
+ end
+
+ it "returns an instance of a subclass" do
+ a = StringSpecs::MyString.new("blah")
+ a.should be_an_instance_of(StringSpecs::MyString)
+ a.should == "blah"
+ end
+
+ it "is called on subclasses" do
+ s = StringSpecs::SubString.new
+ s.special.should == nil
+ s.should == ""
+
+ s = StringSpecs::SubString.new "subclass"
+ s.special.should == "subclass"
+ s.should == ""
+ end
+
+ it "raises TypeError on inconvertible object" do
+ lambda { String.new 5 }.should raise_error(TypeError)
+ lambda { String.new nil }.should raise_error(TypeError)
+ end
+
+ it "returns a binary String" do
+ String.new.encoding.should == Encoding::BINARY
+ end
+end
diff --git a/spec/ruby/core/string/next_spec.rb b/spec/ruby/core/string/next_spec.rb
new file mode 100644
index 0000000000..fcd3e5ef90
--- /dev/null
+++ b/spec/ruby/core/string/next_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/succ'
+
+describe "String#next" do
+ it_behaves_like :string_succ, :next
+end
+
+describe "String#next!" do
+ it_behaves_like :string_succ_bang, :"next!"
+end
diff --git a/spec/ruby/core/string/oct_spec.rb b/spec/ruby/core/string/oct_spec.rb
new file mode 100644
index 0000000000..7637692217
--- /dev/null
+++ b/spec/ruby/core/string/oct_spec.rb
@@ -0,0 +1,88 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# Note: We can't completely spec this in terms of to_int() because hex()
+# allows the base to be changed by a base specifier in the string.
+# See http://groups.google.com/group/ruby-core-google/browse_frm/thread/b53e9c2003425703
+describe "String#oct" do
+ it "treats numeric digits as base-8 digits by default" do
+ "0".oct.should == 0
+ "77".oct.should == 077
+ "077".oct.should == 077
+ end
+
+ it "accepts numbers formatted as binary" do
+ "0b1010".oct.should == 0b1010
+ end
+
+ it "accepts numbers formatted as hexadecimal" do
+ "0xFF".oct.should == 0xFF
+ end
+
+ it "accepts numbers formatted as decimal" do
+ "0d500".oct.should == 500
+ end
+
+ describe "with a leading minus sign" do
+ it "treats numeric digits as base-8 digits by default" do
+ "-12348".oct.should == -01234
+ end
+
+ it "accepts numbers formatted as binary" do
+ "-0b0101".oct.should == -0b0101
+ end
+
+ it "accepts numbers formatted as hexadecimal" do
+ "-0xEE".oct.should == -0xEE
+ end
+
+ it "accepts numbers formatted as decimal" do
+ "-0d500".oct.should == -500
+ end
+ end
+
+ describe "with a leading plus sign" do
+ it "treats numeric digits as base-8 digits by default" do
+ "+12348".oct.should == 01234
+ end
+
+ it "accepts numbers formatted as binary" do
+ "+0b1010".oct.should == 0b1010
+ end
+
+ it "accepts numbers formatted as hexadecimal" do
+ "+0xFF".oct.should == 0xFF
+ end
+
+ it "accepts numbers formatted as decimal" do
+ "+0d500".oct.should == 500
+ end
+ end
+
+ it "accepts a single underscore separating digits" do
+ "755_333".oct.should == 0755_333
+ end
+
+ it "does not accept a sequence of underscores as part of a number" do
+ "7__3".oct.should == 07
+ "7___3".oct.should == 07
+ "7__5".oct.should == 07
+ end
+
+ it "ignores characters that are incorrect for the base-8 digits" do
+ "0o".oct.should == 0
+ "5678".oct.should == 0567
+ end
+
+ it "returns 0 if no characters can be interpreted as a base-8 number" do
+ "".oct.should == 0
+ "+-5".oct.should == 0
+ "wombat".oct.should == 0
+ end
+
+ it "returns 0 for strings with leading underscores" do
+ "_7".oct.should == 0
+ "_07".oct.should == 0
+ " _7".oct.should == 0
+ end
+end
diff --git a/spec/ruby/core/string/ord_spec.rb b/spec/ruby/core/string/ord_spec.rb
new file mode 100644
index 0000000000..8ba110c973
--- /dev/null
+++ b/spec/ruby/core/string/ord_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "String#ord" do
+ it "returns a Fixnum" do
+ 'a'.ord.should be_an_instance_of(Fixnum)
+ end
+
+ it "returns the codepoint of the first character in the String" do
+ 'a'.ord.should == 97
+ end
+
+
+ it "ignores subsequent characters" do
+ "\u{287}a".ord.should == "\u{287}".ord
+ end
+
+ it "understands multibyte characters" do
+ "\u{9879}".ord.should == 39033
+ end
+
+ it "is equivalent to #codepoints.first" do
+ "\u{981}\u{982}".ord.should == "\u{981}\u{982}".codepoints.first
+ end
+
+ it "raises an ArgumentError if called on an empty String" do
+ lambda { ''.ord }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/partition_spec.rb b/spec/ruby/core/string/partition_spec.rb
new file mode 100644
index 0000000000..f7ab5f85aa
--- /dev/null
+++ b/spec/ruby/core/string/partition_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#partition with String" do
+ it "returns an array of substrings based on splitting on the given string" do
+ "hello world".partition("o").should == ["hell", "o", " world"]
+ end
+
+ it "always returns 3 elements" do
+ "hello".partition("x").should == ["hello", "", ""]
+ "hello".partition("hello").should == ["", "hello", ""]
+ end
+
+ it "accepts regexp" do
+ "hello!".partition(/l./).should == ["he", "ll", "o!"]
+ end
+
+ it "sets global vars if regexp used" do
+ "hello!".partition(/(.l)(.o)/)
+ $1.should == "el"
+ $2.should == "lo"
+ end
+
+ it "converts its argument using :to_str" do
+ find = mock('l')
+ find.should_receive(:to_str).and_return("l")
+ "hello".partition(find).should == ["he","l","lo"]
+ end
+
+ it "raises an error if not convertible to string" do
+ lambda{ "hello".partition(5) }.should raise_error(TypeError)
+ lambda{ "hello".partition(nil) }.should raise_error(TypeError)
+ end
+
+ it "takes precedence over a given block" do
+ "hello world".partition("o") { true }.should == ["hell", "o", " world"]
+ end
+end
diff --git a/spec/ruby/core/string/percent_spec.rb b/spec/ruby/core/string/percent_spec.rb
new file mode 100644
index 0000000000..e701def344
--- /dev/null
+++ b/spec/ruby/core/string/percent_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require_relative '../kernel/shared/sprintf'
+require_relative '../kernel/shared/sprintf_encoding'
+
+describe "String#%" do
+ it_behaves_like :kernel_sprintf, -> (format, *args) {
+ format % args
+ }
+
+ it_behaves_like :kernel_sprintf_encoding, -> (format, *args) {
+ format % args
+ }
+end
diff --git a/spec/ruby/core/string/plus_spec.rb b/spec/ruby/core/string/plus_spec.rb
new file mode 100644
index 0000000000..a9f287a82b
--- /dev/null
+++ b/spec/ruby/core/string/plus_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/concat'
+
+describe "String#+" do
+ it "returns a new string containing the given string concatenated to self" do
+ ("" + "").should == ""
+ ("" + "Hello").should == "Hello"
+ ("Hello" + "").should == "Hello"
+ ("Ruby !" + "= Rubinius").should == "Ruby != Rubinius"
+ end
+
+ it "converts any non-String argument with #to_str" do
+ c = mock 'str'
+ c.should_receive(:to_str).any_number_of_times.and_return(' + 1 = 2')
+
+ ("1" + c).should == '1 + 1 = 2'
+ end
+
+ it "raises a TypeError when given any object that fails #to_str" do
+ lambda { "" + Object.new }.should raise_error(TypeError)
+ lambda { "" + 65 }.should raise_error(TypeError)
+ end
+
+ it "doesn't return subclass instances" do
+ (StringSpecs::MyString.new("hello") + "").should be_an_instance_of(String)
+ (StringSpecs::MyString.new("hello") + "foo").should be_an_instance_of(String)
+ (StringSpecs::MyString.new("hello") + StringSpecs::MyString.new("foo")).should be_an_instance_of(String)
+ (StringSpecs::MyString.new("hello") + StringSpecs::MyString.new("")).should be_an_instance_of(String)
+ (StringSpecs::MyString.new("") + StringSpecs::MyString.new("")).should be_an_instance_of(String)
+ ("hello" + StringSpecs::MyString.new("foo")).should be_an_instance_of(String)
+ ("hello" + StringSpecs::MyString.new("")).should be_an_instance_of(String)
+ end
+
+ it "taints the result when self or other is tainted" do
+ strs = ["", "OK", StringSpecs::MyString.new(""), StringSpecs::MyString.new("OK")]
+ strs += strs.map { |s| s.dup.taint }
+
+ strs.each do |str|
+ strs.each do |other|
+ (str + other).tainted?.should == (str.tainted? | other.tainted?)
+ end
+ end
+ end
+
+ it_behaves_like :string_concat_encoding, :+
+end
diff --git a/spec/ruby/core/string/prepend_spec.rb b/spec/ruby/core/string/prepend_spec.rb
new file mode 100644
index 0000000000..cbe83524b8
--- /dev/null
+++ b/spec/ruby/core/string/prepend_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#prepend" do
+ it "prepends the given argument to self and returns self" do
+ str = "world"
+ str.prepend("hello ").should equal(str)
+ str.should == "hello world"
+ end
+
+ it "converts the given argument to a String using to_str" do
+ obj = mock("hello")
+ obj.should_receive(:to_str).and_return("hello")
+ a = " world!".prepend(obj)
+ a.should == "hello world!"
+ end
+
+ it "raises a TypeError if the given argument can't be converted to a String" do
+ lambda { "hello ".prepend [] }.should raise_error(TypeError)
+ lambda { 'hello '.prepend mock('x') }.should raise_error(TypeError)
+ end
+
+ it "raises a #{frozen_error_class} when self is frozen" do
+ a = "hello"
+ a.freeze
+
+ lambda { a.prepend "" }.should raise_error(frozen_error_class)
+ lambda { a.prepend "test" }.should raise_error(frozen_error_class)
+ end
+
+ it "works when given a subclass instance" do
+ a = " world"
+ a.prepend StringSpecs::MyString.new("hello")
+ a.should == "hello world"
+ end
+
+ it "taints self if other is tainted" do
+ x = "x"
+ x.prepend("".taint).tainted?.should be_true
+
+ x = "x"
+ x.prepend("y".taint).tainted?.should be_true
+ end
+
+ ruby_version_is "2.4" do
+ it "takes multiple arguments" do
+ str = " world"
+ str.prepend "he", "", "llo"
+ str.should == "hello world"
+ end
+
+ it "prepends the initial value when given arguments contain 2 self" do
+ str = "hello"
+ str.prepend str, str
+ str.should == "hellohellohello"
+ end
+
+ it "returns self when given no arguments" do
+ str = "hello"
+ str.prepend.should equal(str)
+ str.should == "hello"
+ end
+ end
+end
diff --git a/spec/ruby/core/string/replace_spec.rb b/spec/ruby/core/string/replace_spec.rb
new file mode 100644
index 0000000000..ef9bab4f3c
--- /dev/null
+++ b/spec/ruby/core/string/replace_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/replace'
+
+describe "String#replace" do
+ it_behaves_like :string_replace, :replace
+end
diff --git a/spec/ruby/core/string/reverse_spec.rb b/spec/ruby/core/string/reverse_spec.rb
new file mode 100644
index 0000000000..7cb171cb81
--- /dev/null
+++ b/spec/ruby/core/string/reverse_spec.rb
@@ -0,0 +1,52 @@
+# encoding: utf-8
+
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#reverse" do
+ it "returns a new string with the characters of self in reverse order" do
+ "stressed".reverse.should == "desserts"
+ "m".reverse.should == "m"
+ "".reverse.should == ""
+ end
+
+ it "taints the result if self is tainted" do
+ "".taint.reverse.tainted?.should == true
+ "m".taint.reverse.tainted?.should == true
+ end
+
+ with_feature :encoding do
+ it "reverses a string with multi byte characters" do
+ "微軟正黑體".reverse.should == "體黑正軟微"
+ end
+ end
+
+end
+
+describe "String#reverse!" do
+ it "reverses self in place and always returns self" do
+ a = "stressed"
+ a.reverse!.should equal(a)
+ a.should == "desserts"
+
+ "".reverse!.should == ""
+ end
+
+ it "raises a #{frozen_error_class} on a frozen instance that is modified" do
+ lambda { "anna".freeze.reverse! }.should raise_error(frozen_error_class)
+ lambda { "hello".freeze.reverse! }.should raise_error(frozen_error_class)
+ end
+
+ # see [ruby-core:23666]
+ it "raises a #{frozen_error_class} on a frozen instance that would not be modified" do
+ lambda { "".freeze.reverse! }.should raise_error(frozen_error_class)
+ end
+
+ with_feature :encoding do
+ it "reverses a string with multi byte characters" do
+ str = "微軟正黑體"
+ str.reverse!
+ str.should == "體黑正軟微"
+ end
+ end
+end
diff --git a/spec/ruby/core/string/rindex_spec.rb b/spec/ruby/core/string/rindex_spec.rb
new file mode 100644
index 0000000000..98e36785e3
--- /dev/null
+++ b/spec/ruby/core/string/rindex_spec.rb
@@ -0,0 +1,368 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'fixtures/utf-8-encoding'
+
+describe "String#rindex with object" do
+ it "raises a TypeError if obj isn't a String, Fixnum or Regexp" do
+ not_supported_on :opal do
+ lambda { "hello".rindex(:sym) }.should raise_error(TypeError)
+ end
+ lambda { "hello".rindex(mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "doesn't try to convert obj to an integer via to_int" do
+ obj = mock('x')
+ obj.should_not_receive(:to_int)
+ lambda { "hello".rindex(obj) }.should raise_error(TypeError)
+ end
+
+ it "tries to convert obj to a string via to_str" do
+ obj = mock('lo')
+ def obj.to_str() "lo" end
+ "hello".rindex(obj).should == "hello".rindex("lo")
+
+ obj = mock('o')
+ def obj.respond_to?(arg, *) true end
+ def obj.method_missing(*args) "o" end
+ "hello".rindex(obj).should == "hello".rindex("o")
+ end
+end
+
+describe "String#rindex with String" do
+ it "behaves the same as String#rindex(char) for one-character strings" do
+ "blablabla hello cruel world...!".split("").uniq.each do |str|
+ chr = str[0]
+ str.rindex(str).should == str.rindex(chr)
+
+ 0.upto(str.size + 1) do |start|
+ str.rindex(str, start).should == str.rindex(chr, start)
+ end
+
+ (-str.size - 1).upto(-1) do |start|
+ str.rindex(str, start).should == str.rindex(chr, start)
+ end
+ end
+ end
+
+ it "behaves the same as String#rindex(?char) for one-character strings" do
+ "blablabla hello cruel world...!".split("").uniq.each do |str|
+ chr = str[0] =~ / / ? str[0] : eval("?#{str[0]}")
+ str.rindex(str).should == str.rindex(chr)
+
+ 0.upto(str.size + 1) do |start|
+ str.rindex(str, start).should == str.rindex(chr, start)
+ end
+
+ (-str.size - 1).upto(-1) do |start|
+ str.rindex(str, start).should == str.rindex(chr, start)
+ end
+ end
+ end
+
+ it "returns the index of the last occurrence of the given substring" do
+ "blablabla".rindex("").should == 9
+ "blablabla".rindex("a").should == 8
+ "blablabla".rindex("la").should == 7
+ "blablabla".rindex("bla").should == 6
+ "blablabla".rindex("abla").should == 5
+ "blablabla".rindex("labla").should == 4
+ "blablabla".rindex("blabla").should == 3
+ "blablabla".rindex("ablabla").should == 2
+ "blablabla".rindex("lablabla").should == 1
+ "blablabla".rindex("blablabla").should == 0
+
+ "blablabla".rindex("l").should == 7
+ "blablabla".rindex("bl").should == 6
+ "blablabla".rindex("abl").should == 5
+ "blablabla".rindex("labl").should == 4
+ "blablabla".rindex("blabl").should == 3
+ "blablabla".rindex("ablabl").should == 2
+ "blablabla".rindex("lablabl").should == 1
+ "blablabla".rindex("blablabl").should == 0
+
+ "blablabla".rindex("b").should == 6
+ "blablabla".rindex("ab").should == 5
+ "blablabla".rindex("lab").should == 4
+ "blablabla".rindex("blab").should == 3
+ "blablabla".rindex("ablab").should == 2
+ "blablabla".rindex("lablab").should == 1
+ "blablabla".rindex("blablab").should == 0
+ end
+
+ it "doesn't set $~" do
+ $~ = nil
+
+ 'hello.'.rindex('ll')
+ $~.should == nil
+ end
+
+ it "ignores string subclasses" do
+ "blablabla".rindex(StringSpecs::MyString.new("bla")).should == 6
+ StringSpecs::MyString.new("blablabla").rindex("bla").should == 6
+ StringSpecs::MyString.new("blablabla").rindex(StringSpecs::MyString.new("bla")).should == 6
+ end
+
+ it "starts the search at the given offset" do
+ "blablabla".rindex("bl", 0).should == 0
+ "blablabla".rindex("bl", 1).should == 0
+ "blablabla".rindex("bl", 2).should == 0
+ "blablabla".rindex("bl", 3).should == 3
+
+ "blablabla".rindex("bla", 0).should == 0
+ "blablabla".rindex("bla", 1).should == 0
+ "blablabla".rindex("bla", 2).should == 0
+ "blablabla".rindex("bla", 3).should == 3
+
+ "blablabla".rindex("blab", 0).should == 0
+ "blablabla".rindex("blab", 1).should == 0
+ "blablabla".rindex("blab", 2).should == 0
+ "blablabla".rindex("blab", 3).should == 3
+ "blablabla".rindex("blab", 6).should == 3
+ "blablablax".rindex("blab", 6).should == 3
+
+ "blablabla".rindex("la", 1).should == 1
+ "blablabla".rindex("la", 2).should == 1
+ "blablabla".rindex("la", 3).should == 1
+ "blablabla".rindex("la", 4).should == 4
+
+ "blablabla".rindex("lab", 1).should == 1
+ "blablabla".rindex("lab", 2).should == 1
+ "blablabla".rindex("lab", 3).should == 1
+ "blablabla".rindex("lab", 4).should == 4
+
+ "blablabla".rindex("ab", 2).should == 2
+ "blablabla".rindex("ab", 3).should == 2
+ "blablabla".rindex("ab", 4).should == 2
+ "blablabla".rindex("ab", 5).should == 5
+
+ "blablabla".rindex("", 0).should == 0
+ "blablabla".rindex("", 1).should == 1
+ "blablabla".rindex("", 2).should == 2
+ "blablabla".rindex("", 7).should == 7
+ "blablabla".rindex("", 8).should == 8
+ "blablabla".rindex("", 9).should == 9
+ "blablabla".rindex("", 10).should == 9
+ end
+
+ it "starts the search at offset + self.length if offset is negative" do
+ str = "blablabla"
+
+ ["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle|
+ (-str.length .. -1).each do |offset|
+ str.rindex(needle, offset).should ==
+ str.rindex(needle, offset + str.length)
+ end
+ end
+ end
+
+ it "returns nil if the substring isn't found" do
+ "blablabla".rindex("B").should == nil
+ "blablabla".rindex("z").should == nil
+ "blablabla".rindex("BLA").should == nil
+ "blablabla".rindex("blablablabla").should == nil
+
+ "hello".rindex("lo", 0).should == nil
+ "hello".rindex("lo", 1).should == nil
+ "hello".rindex("lo", 2).should == nil
+
+ "hello".rindex("llo", 0).should == nil
+ "hello".rindex("llo", 1).should == nil
+
+ "hello".rindex("el", 0).should == nil
+ "hello".rindex("ello", 0).should == nil
+
+ "hello".rindex("", -6).should == nil
+ "hello".rindex("", -7).should == nil
+
+ "hello".rindex("h", -6).should == nil
+ end
+
+ it "tries to convert start_offset to an integer via to_int" do
+ obj = mock('5')
+ def obj.to_int() 5 end
+ "str".rindex("st", obj).should == 0
+
+ obj = mock('5')
+ def obj.respond_to?(arg, *) true end
+ def obj.method_missing(*args) 5 end
+ "str".rindex("st", obj).should == 0
+ end
+
+ it "raises a TypeError when given offset is nil" do
+ lambda { "str".rindex("st", nil) }.should raise_error(TypeError)
+ end
+end
+
+describe "String#rindex with Regexp" do
+ it "behaves the same as String#rindex(string) for escaped string regexps" do
+ ["blablabla", "hello cruel world...!"].each do |str|
+ ["", "b", "bla", "lab", "o c", "d."].each do |needle|
+ regexp = Regexp.new(Regexp.escape(needle))
+ str.rindex(regexp).should == str.rindex(needle)
+
+ 0.upto(str.size + 1) do |start|
+ str.rindex(regexp, start).should == str.rindex(needle, start)
+ end
+
+ (-str.size - 1).upto(-1) do |start|
+ str.rindex(regexp, start).should == str.rindex(needle, start)
+ end
+ end
+ end
+ end
+
+ it "returns the index of the first match from the end of string of regexp" do
+ "blablabla".rindex(/bla/).should == 6
+ "blablabla".rindex(/BLA/i).should == 6
+
+ "blablabla".rindex(/.{0}/).should == 9
+ "blablabla".rindex(/.{1}/).should == 8
+ "blablabla".rindex(/.{2}/).should == 7
+ "blablabla".rindex(/.{6}/).should == 3
+ "blablabla".rindex(/.{9}/).should == 0
+
+ "blablabla".rindex(/.*/).should == 9
+ "blablabla".rindex(/.+/).should == 8
+
+ "blablabla".rindex(/bla|a/).should == 8
+
+ not_supported_on :opal do
+ "blablabla".rindex(/\A/).should == 0
+ "blablabla".rindex(/\Z/).should == 9
+ "blablabla".rindex(/\z/).should == 9
+ "blablabla\n".rindex(/\Z/).should == 10
+ "blablabla\n".rindex(/\z/).should == 10
+ end
+
+ "blablabla".rindex(/^/).should == 0
+ not_supported_on :opal do
+ "\nblablabla".rindex(/^/).should == 1
+ "b\nlablabla".rindex(/^/).should == 2
+ end
+ "blablabla".rindex(/$/).should == 9
+
+ "blablabla".rindex(/.l./).should == 6
+ end
+
+ it "sets $~ to MatchData of match and nil when there's none" do
+ 'hello.'.rindex(/.(.)/)
+ $~[0].should == 'o.'
+
+ 'hello.'.rindex(/not/)
+ $~.should == nil
+ end
+
+ it "starts the search at the given offset" do
+ "blablabla".rindex(/.{0}/, 5).should == 5
+ "blablabla".rindex(/.{1}/, 5).should == 5
+ "blablabla".rindex(/.{2}/, 5).should == 5
+ "blablabla".rindex(/.{3}/, 5).should == 5
+ "blablabla".rindex(/.{4}/, 5).should == 5
+
+ "blablabla".rindex(/.{0}/, 3).should == 3
+ "blablabla".rindex(/.{1}/, 3).should == 3
+ "blablabla".rindex(/.{2}/, 3).should == 3
+ "blablabla".rindex(/.{5}/, 3).should == 3
+ "blablabla".rindex(/.{6}/, 3).should == 3
+
+ "blablabla".rindex(/.l./, 0).should == 0
+ "blablabla".rindex(/.l./, 1).should == 0
+ "blablabla".rindex(/.l./, 2).should == 0
+ "blablabla".rindex(/.l./, 3).should == 3
+
+ "blablablax".rindex(/.x/, 10).should == 8
+ "blablablax".rindex(/.x/, 9).should == 8
+ "blablablax".rindex(/.x/, 8).should == 8
+
+ "blablablax".rindex(/..x/, 10).should == 7
+ "blablablax".rindex(/..x/, 9).should == 7
+ "blablablax".rindex(/..x/, 8).should == 7
+ "blablablax".rindex(/..x/, 7).should == 7
+
+ not_supported_on :opal do
+ "blablabla\n".rindex(/\Z/, 9).should == 9
+ end
+ end
+
+ it "starts the search at offset + self.length if offset is negative" do
+ str = "blablabla"
+
+ ["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle|
+ (-str.length .. -1).each do |offset|
+ str.rindex(needle, offset).should ==
+ str.rindex(needle, offset + str.length)
+ end
+ end
+ end
+
+ it "returns nil if the substring isn't found" do
+ "blablabla".rindex(/BLA/).should == nil
+ "blablabla".rindex(/.{10}/).should == nil
+ "blablablax".rindex(/.x/, 7).should == nil
+ "blablablax".rindex(/..x/, 6).should == nil
+
+ not_supported_on :opal do
+ "blablabla".rindex(/\Z/, 5).should == nil
+ "blablabla".rindex(/\z/, 5).should == nil
+ "blablabla\n".rindex(/\z/, 9).should == nil
+ end
+ end
+
+ not_supported_on :opal do
+ it "supports \\G which matches at the given start offset" do
+ "helloYOU.".rindex(/YOU\G/, 8).should == 5
+ "helloYOU.".rindex(/YOU\G/).should == nil
+
+ idx = "helloYOUall!".index("YOU")
+ re = /YOU.+\G.+/
+ # The # marks where \G will match.
+ [
+ ["helloYOU#all.", nil],
+ ["helloYOUa#ll.", idx],
+ ["helloYOUal#l.", idx],
+ ["helloYOUall#.", idx],
+ ["helloYOUall.#", nil]
+ ].each do |i|
+ start = i[0].index("#")
+ str = i[0].delete("#")
+
+ str.rindex(re, start).should == i[1]
+ end
+ end
+ end
+
+ it "tries to convert start_offset to an integer via to_int" do
+ obj = mock('5')
+ def obj.to_int() 5 end
+ "str".rindex(/../, obj).should == 1
+
+ obj = mock('5')
+ def obj.respond_to?(arg, *) true end
+ def obj.method_missing(*args); 5; end
+ "str".rindex(/../, obj).should == 1
+ end
+
+ it "raises a TypeError when given offset is nil" do
+ lambda { "str".rindex(/../, nil) }.should raise_error(TypeError)
+ end
+
+ with_feature :encoding do
+ it "returns the reverse character index of a multibyte character" do
+ "ã‚りãŒã‚ŠãŒã¨ã†".rindex("ãŒ").should == 4
+ "ã‚りãŒã‚ŠãŒã¨ã†".rindex(/ãŒ/).should == 4
+ end
+
+ it "returns the character index before the finish" do
+ "ã‚りãŒã‚ŠãŒã¨ã†".rindex("ãŒ", 3).should == 2
+ "ã‚りãŒã‚ŠãŒã¨ã†".rindex(/ãŒ/, 3).should == 2
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ re = Regexp.new "れ".encode(Encoding::EUC_JP)
+ lambda do
+ "ã‚れ".rindex re
+ end.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/rjust_spec.rb b/spec/ruby/core/string/rjust_spec.rb
new file mode 100644
index 0000000000..c2cfae7b66
--- /dev/null
+++ b/spec/ruby/core/string/rjust_spec.rb
@@ -0,0 +1,116 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#rjust with length, padding" do
+ it "returns a new string of specified length with self right justified and padded with padstr" do
+ "hello".rjust(20, '1234').should == "123412341234123hello"
+
+ "".rjust(1, "abcd").should == "a"
+ "".rjust(2, "abcd").should == "ab"
+ "".rjust(3, "abcd").should == "abc"
+ "".rjust(4, "abcd").should == "abcd"
+ "".rjust(6, "abcd").should == "abcdab"
+
+ "OK".rjust(3, "abcd").should == "aOK"
+ "OK".rjust(4, "abcd").should == "abOK"
+ "OK".rjust(6, "abcd").should == "abcdOK"
+ "OK".rjust(8, "abcd").should == "abcdabOK"
+ end
+
+ it "pads with whitespace if no padstr is given" do
+ "hello".rjust(20).should == " hello"
+ end
+
+ it "returns self if it's longer than or as long as the specified length" do
+ "".rjust(0).should == ""
+ "".rjust(-1).should == ""
+ "hello".rjust(4).should == "hello"
+ "hello".rjust(-1).should == "hello"
+ "this".rjust(3).should == "this"
+ "radiology".rjust(8, '-').should == "radiology"
+ end
+
+ it "taints result when self or padstr is tainted" do
+ "x".taint.rjust(4).tainted?.should == true
+ "x".taint.rjust(0).tainted?.should == true
+ "".taint.rjust(0).tainted?.should == true
+ "x".taint.rjust(4, "*").tainted?.should == true
+ "x".rjust(4, "*".taint).tainted?.should == true
+ end
+
+ it "tries to convert length to an integer using to_int" do
+ "^".rjust(3.8, "^_").should == "^_^"
+
+ obj = mock('3')
+ obj.should_receive(:to_int).and_return(3)
+
+ "o".rjust(obj, "o_").should == "o_o"
+ end
+
+ it "raises a TypeError when length can't be converted to an integer" do
+ lambda { "hello".rjust("x") }.should raise_error(TypeError)
+ lambda { "hello".rjust("x", "y") }.should raise_error(TypeError)
+ lambda { "hello".rjust([]) }.should raise_error(TypeError)
+ lambda { "hello".rjust(mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "tries to convert padstr to a string using to_str" do
+ padstr = mock('123')
+ padstr.should_receive(:to_str).and_return("123")
+
+ "hello".rjust(10, padstr).should == "12312hello"
+ end
+
+ it "raises a TypeError when padstr can't be converted" do
+ lambda { "hello".rjust(20, []) }.should raise_error(TypeError)
+ lambda { "hello".rjust(20, Object.new)}.should raise_error(TypeError)
+ lambda { "hello".rjust(20, mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError when padstr is empty" do
+ lambda { "hello".rjust(10, '') }.should raise_error(ArgumentError)
+ end
+
+ it "returns subclass instances when called on subclasses" do
+ StringSpecs::MyString.new("").rjust(10).should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("foo").rjust(10).should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("foo").rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString)
+
+ "".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
+ "foo".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
+ end
+
+ it "when padding is tainted and self is untainted returns a tainted string if and only if length is longer than self" do
+ "hello".rjust(4, 'X'.taint).tainted?.should be_false
+ "hello".rjust(5, 'X'.taint).tainted?.should be_false
+ "hello".rjust(6, 'X'.taint).tainted?.should be_true
+ end
+
+ with_feature :encoding do
+ describe "with width" do
+ it "returns a String in the same encoding as the original" do
+ str = "abc".force_encoding Encoding::IBM437
+ result = str.rjust 5
+ result.should == " abc"
+ result.encoding.should equal(Encoding::IBM437)
+ end
+ end
+
+ describe "with width, pattern" do
+ it "returns a String in the compatible encoding" do
+ str = "abc".force_encoding Encoding::IBM437
+ result = str.rjust 5, "ã‚"
+ result.should == "ã‚ã‚abc"
+ result.encoding.should equal(Encoding::UTF_8)
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ pat = "ã‚¢".encode Encoding::EUC_JP
+ lambda do
+ "ã‚れ".rjust 5, pat
+ end.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/rpartition_spec.rb b/spec/ruby/core/string/rpartition_spec.rb
new file mode 100644
index 0000000000..a5222e0a2a
--- /dev/null
+++ b/spec/ruby/core/string/rpartition_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#rpartition with String" do
+ it "returns an array of substrings based on splitting on the given string" do
+ "hello world".rpartition("o").should == ["hello w", "o", "rld"]
+ end
+
+ it "always returns 3 elements" do
+ "hello".rpartition("x").should == ["", "", "hello"]
+ "hello".rpartition("hello").should == ["", "hello", ""]
+ end
+
+ it "accepts regexp" do
+ "hello!".rpartition(/l./).should == ["hel", "lo", "!"]
+ end
+
+ it "affects $~" do
+ matched_string = "hello!".rpartition(/l./)[1]
+ matched_string.should == $~[0]
+ end
+
+ it "converts its argument using :to_str" do
+ find = mock('l')
+ find.should_receive(:to_str).and_return("l")
+ "hello".rpartition(find).should == ["hel","l","o"]
+ end
+
+ it "raises an error if not convertible to string" do
+ lambda{ "hello".rpartition(5) }.should raise_error(TypeError)
+ lambda{ "hello".rpartition(nil) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/string/rstrip_spec.rb b/spec/ruby/core/string/rstrip_spec.rb
new file mode 100644
index 0000000000..6376e5df33
--- /dev/null
+++ b/spec/ruby/core/string/rstrip_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#rstrip" do
+ it "returns a copy of self with trailing whitespace removed" do
+ " hello ".rstrip.should == " hello"
+ " hello world ".rstrip.should == " hello world"
+ " hello world \n\r\t\n\v\r".rstrip.should == " hello world"
+ "hello".rstrip.should == "hello"
+ "hello\x00".rstrip.should == "hello"
+ end
+
+ it "returns a copy of self with all trailing whitespace and NULL bytes removed" do
+ "\x00 \x00hello\x00 \x00".rstrip.should == "\x00 \x00hello"
+ end
+
+ it "taints the result when self is tainted" do
+ "".taint.rstrip.tainted?.should == true
+ "ok".taint.rstrip.tainted?.should == true
+ "ok ".taint.rstrip.tainted?.should == true
+ end
+end
+
+describe "String#rstrip!" do
+ it "modifies self in place and returns self" do
+ a = " hello "
+ a.rstrip!.should equal(a)
+ a.should == " hello"
+ end
+
+ it "modifies self removing trailing NULL bytes and whitespace" do
+ a = "\x00 \x00hello\x00 \x00"
+ a.rstrip!
+ a.should == "\x00 \x00hello"
+ end
+
+ it "returns nil if no modifications were made" do
+ a = "hello"
+ a.rstrip!.should == nil
+ a.should == "hello"
+ end
+
+ it "raises a #{frozen_error_class} on a frozen instance that is modified" do
+ lambda { " hello ".freeze.rstrip! }.should raise_error(frozen_error_class)
+ end
+
+ # see [ruby-core:23666]
+ it "raises a #{frozen_error_class} on a frozen instance that would not be modified" do
+ lambda { "hello".freeze.rstrip! }.should raise_error(frozen_error_class)
+ lambda { "".freeze.rstrip! }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/string/scan_spec.rb b/spec/ruby/core/string/scan_spec.rb
new file mode 100644
index 0000000000..64a1c31443
--- /dev/null
+++ b/spec/ruby/core/string/scan_spec.rb
@@ -0,0 +1,192 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#scan" do
+ it "returns an array containing all matches" do
+ "cruel world".scan(/\w+/).should == ["cruel", "world"]
+ "cruel world".scan(/.../).should == ["cru", "el ", "wor"]
+
+ # Edge case
+ "hello".scan(//).should == ["", "", "", "", "", ""]
+ "".scan(//).should == [""]
+ end
+
+ it "respects unicode when the pattern collapses to nothing" do
+ str = "ã“ã«ã¡ã‚"
+ reg = %r!!
+ str.scan(reg).should == ["", "", "", "", ""]
+ end
+
+ it "stores groups as arrays in the returned arrays" do
+ "hello".scan(/()/).should == [[""]] * 6
+ "hello".scan(/()()/).should == [["", ""]] * 6
+ "cruel world".scan(/(...)/).should == [["cru"], ["el "], ["wor"]]
+ "cruel world".scan(/(..)(..)/).should == [["cr", "ue"], ["l ", "wo"]]
+ end
+
+ it "scans for occurrences of the string if pattern is a string" do
+ "one two one two".scan('one').should == ["one", "one"]
+ "hello.".scan('.').should == ['.']
+ end
+
+ it "sets $~ to MatchData of last match and nil when there's none" do
+ 'hello.'.scan(/.(.)/)
+ $~[0].should == 'o.'
+
+ 'hello.'.scan(/not/)
+ $~.should == nil
+
+ 'hello.'.scan('l')
+ $~.begin(0).should == 3
+ $~[0].should == 'l'
+
+ 'hello.'.scan('not')
+ $~.should == nil
+ end
+
+ it "supports \\G which matches the end of the previous match / string start for first match" do
+ "one two one two".scan(/\G\w+/).should == ["one"]
+ "one two one two".scan(/\G\w+\s*/).should == ["one ", "two ", "one ", "two"]
+ "one two one two".scan(/\G\s*\w+/).should == ["one", " two", " one", " two"]
+ end
+
+ it "tries to convert pattern to a string via to_str" do
+ obj = mock('o')
+ obj.should_receive(:to_str).and_return("o")
+ "o_o".scan(obj).should == ["o", "o"]
+ end
+
+ it "raises a TypeError if pattern isn't a Regexp and can't be converted to a String" do
+ lambda { "cruel world".scan(5) }.should raise_error(TypeError)
+ not_supported_on :opal do
+ lambda { "cruel world".scan(:test) }.should raise_error(TypeError)
+ end
+ lambda { "cruel world".scan(mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "taints the results if the String argument is tainted" do
+ a = "hello hello hello".scan("hello".taint)
+ a.each { |m| m.tainted?.should be_true }
+ end
+
+ it "taints the results when passed a String argument if self is tainted" do
+ a = "hello hello hello".taint.scan("hello")
+ a.each { |m| m.tainted?.should be_true }
+ end
+
+ it "taints the results if the Regexp argument is tainted" do
+ a = "hello".scan(/./.taint)
+ a.each { |m| m.tainted?.should be_true }
+ end
+
+ it "taints the results when passed a Regexp argument if self is tainted" do
+ a = "hello".taint.scan(/./)
+ a.each { |m| m.tainted?.should be_true }
+ end
+end
+
+describe "String#scan with pattern and block" do
+ it "returns self" do
+ s = "foo"
+ s.scan(/./) {}.should equal(s)
+ s.scan(/roar/) {}.should equal(s)
+ end
+
+ it "passes each match to the block as one argument: an array" do
+ a = []
+ "cruel world".scan(/\w+/) { |*w| a << w }
+ a.should == [["cruel"], ["world"]]
+ end
+
+ it "passes groups to the block as one argument: an array" do
+ a = []
+ "cruel world".scan(/(..)(..)/) { |w| a << w }
+ a.should == [["cr", "ue"], ["l ", "wo"]]
+ end
+
+ it "sets $~ for access from the block" do
+ str = "hello"
+
+ matches = []
+ offsets = []
+
+ str.scan(/([aeiou])/) do
+ md = $~
+ md.string.should == str
+ matches << md.to_a
+ offsets << md.offset(0)
+ str
+ end
+
+ matches.should == [["e", "e"], ["o", "o"]]
+ offsets.should == [[1, 2], [4, 5]]
+
+ matches = []
+ offsets = []
+
+ str.scan("l") do
+ md = $~
+ md.string.should == str
+ matches << md.to_a
+ offsets << md.offset(0)
+ str
+ end
+
+ matches.should == [["l"], ["l"]]
+ offsets.should == [[2, 3], [3, 4]]
+ end
+
+ it "restores $~ after leaving the block" do
+ [/./, "l"].each do |pattern|
+ old_md = nil
+ "hello".scan(pattern) do
+ old_md = $~
+ "ok".match(/./)
+ "x"
+ end
+
+ $~[0].should == old_md[0]
+ $~.string.should == "hello"
+ end
+ end
+
+ it "sets $~ to MatchData of last match and nil when there's none for access from outside" do
+ 'hello.'.scan('l') { 'x' }
+ $~.begin(0).should == 3
+ $~[0].should == 'l'
+
+ 'hello.'.scan('not') { 'x' }
+ $~.should == nil
+
+ 'hello.'.scan(/.(.)/) { 'x' }
+ $~[0].should == 'o.'
+
+ 'hello.'.scan(/not/) { 'x' }
+ $~.should == nil
+ end
+
+ it "taints the results if the String argument is tainted" do
+ "hello hello hello".scan("hello".taint).each { |m| m.tainted?.should be_true }
+ end
+
+ it "taints the results when passed a String argument if self is tainted" do
+ "hello hello hello".taint.scan("hello").each { |m| m.tainted?.should be_true }
+ end
+
+ it "taints the results if the Regexp argument is tainted" do
+ "hello".scan(/./.taint).each { |m| m.tainted?.should be_true }
+ end
+
+ it "taints the results when passed a Regexp argument if self is tainted" do
+ "hello".taint.scan(/./).each { |m| m.tainted?.should be_true }
+ end
+
+ it "passes block arguments as individual arguments when blocks are provided" do
+ "a b c\na b c\na b c".scan(/(\w*) (\w*) (\w*)/) do |first,second,third|
+ first.should == 'a';
+ second.should == 'b';
+ third.should == 'c';
+ end
+ end
+end
diff --git a/spec/ruby/core/string/scrub_spec.rb b/spec/ruby/core/string/scrub_spec.rb
new file mode 100644
index 0000000000..92e7641f0b
--- /dev/null
+++ b/spec/ruby/core/string/scrub_spec.rb
@@ -0,0 +1,101 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe "String#scrub with a default replacement" do
+ it "returns self for valid strings" do
+ input = "foo"
+
+ input.scrub.should == input
+ end
+
+ it "replaces invalid byte sequences" do
+ x81 = [0x81].pack('C').force_encoding('utf-8')
+ "abc\u3042#{x81}".scrub.should == "abc\u3042\uFFFD"
+ end
+
+ it "returns a copy of self when the input encoding is BINARY" do
+ input = "foo".encode('BINARY')
+
+ input.scrub.should == "foo"
+ end
+
+
+ it "replaces invalid byte sequences when using ASCII as the input encoding" do
+ xE3x80 = [0xE3, 0x80].pack('CC').force_encoding 'utf-8'
+ input = "abc\u3042#{xE3x80}".force_encoding('ASCII')
+ input.scrub.should == "abc?????"
+ end
+end
+
+describe "String#scrub with a custom replacement" do
+ it "returns self for valid strings" do
+ input = "foo"
+
+ input.scrub("*").should == input
+ end
+
+ it "replaces invalid byte sequences" do
+ x81 = [0x81].pack('C').force_encoding('utf-8')
+ "abc\u3042#{x81}".scrub("*").should == "abc\u3042*"
+ end
+
+ it "replaces an incomplete character at the end with a single replacement" do
+ xE3x80 = [0xE3, 0x80].pack('CC').force_encoding 'utf-8'
+ xE3x80.scrub("*").should == "*"
+ end
+
+ it "raises ArgumentError for replacements with an invalid encoding" do
+ x81 = [0x81].pack('C').force_encoding('utf-8')
+ xE4 = [0xE4].pack('C').force_encoding('utf-8')
+ block = lambda { "foo#{x81}".scrub(xE4) }
+
+ block.should raise_error(ArgumentError)
+ end
+
+ it "raises TypeError when a non String replacement is given" do
+ x81 = [0x81].pack('C').force_encoding('utf-8')
+ block = lambda { "foo#{x81}".scrub(1) }
+
+ block.should raise_error(TypeError)
+ end
+end
+
+describe "String#scrub with a block" do
+ it "returns self for valid strings" do
+ input = "foo"
+
+ input.scrub { |b| "*" }.should == input
+ end
+
+ it "replaces invalid byte sequences" do
+ xE3x80 = [0xE3, 0x80].pack('CC').force_encoding 'utf-8'
+ replaced = "abc\u3042#{xE3x80}".scrub { |b| "<#{b.unpack("H*")[0]}>" }
+
+ replaced.should == "abc\u3042<e380>"
+ end
+
+ it "replaces invalid byte sequences using a custom encoding" do
+ x80x80 = [0x80, 0x80].pack('CC').force_encoding 'utf-8'
+ replaced = x80x80.scrub do |bad|
+ bad.encode(Encoding::UTF_8, Encoding::Windows_1252)
+ end
+
+ replaced.should == "€€"
+ end
+end
+
+describe "String#scrub!" do
+ it "modifies self for valid strings" do
+ x81 = [0x81].pack('C').force_encoding('utf-8')
+ input = "a#{x81}"
+ input.scrub!
+ input.should == "a\uFFFD"
+ end
+
+ it "accepts blocks" do
+ x81 = [0x81].pack('C').force_encoding('utf-8')
+ input = "a#{x81}"
+ input.scrub! { |b| "<?>" }
+ input.should == "a<?>"
+ end
+end
diff --git a/spec/ruby/core/string/setbyte_spec.rb b/spec/ruby/core/string/setbyte_spec.rb
new file mode 100644
index 0000000000..f998e61622
--- /dev/null
+++ b/spec/ruby/core/string/setbyte_spec.rb
@@ -0,0 +1,105 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe "String#setbyte" do
+ it "returns an Integer" do
+ "a".setbyte(0,1).should be_kind_of(Integer)
+ end
+
+ it "modifies the receiver" do
+ str = "glark"
+ old_id = str.object_id
+ str.setbyte(0,88)
+ str.object_id.should == old_id
+ end
+
+ it "changes the byte at the given index to the new byte" do
+ str = "a"
+ str.setbyte(0,98)
+ str.should == 'b'
+
+ # copy-on-write case
+ str1, str2 = "fooXbar".split("X")
+ str2.setbyte(0, 50)
+ str2.should == "2ar"
+ str1.should == "foo"
+ end
+
+ it "allows changing bytes in multi-byte characters" do
+ str = "\u{915}"
+ str.setbyte(1,254)
+ str.getbyte(1).should == 254
+ end
+
+ it "can invalidate a String's encoding" do
+ str = "glark"
+ str.valid_encoding?.should be_true
+ str.setbyte(2,253)
+ str.valid_encoding?.should be_false
+ end
+
+ it "regards a negative index as counting from the end of the String" do
+ str = "hedgehog"
+ str.setbyte(-3, 108)
+ str.should == "hedgelog"
+
+ # copy-on-write case
+ str1, str2 = "fooXbar".split("X")
+ str2.setbyte(-1, 50)
+ str2.should == "ba2"
+ str1.should == "foo"
+ end
+
+ it "raises an IndexError if the index is greater than the String bytesize" do
+ lambda { "?".setbyte(1, 97) }.should raise_error(IndexError)
+ end
+
+ it "raises an IndexError if the nexgative index is greater magnitude than the String bytesize" do
+ lambda { "???".setbyte(-5, 97) }.should raise_error(IndexError)
+ end
+
+ it "sets a byte at an index greater than String size" do
+ chr = "\u{998}"
+ chr.bytesize.should == 3
+ chr.setbyte(2, 150)
+ chr.should == "\xe0\xa6\x96"
+ end
+
+ it "does not modify the original string when using String.new" do
+ str1 = "hedgehog"
+ str2 = String.new(str1)
+ str2.setbyte(0, 108)
+ str2.should == "ledgehog"
+ str2.should_not == "hedgehog"
+ str1.should == "hedgehog"
+ str1.should_not == "ledgehog"
+ end
+
+ it "raises a #{frozen_error_class} if self is frozen" do
+ str = "cold".freeze
+ str.frozen?.should be_true
+ lambda { str.setbyte(3,96) }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a TypeError unless the second argument is an Integer" do
+ lambda { "a".setbyte(0,'a') }.should raise_error(TypeError)
+ end
+
+ it "calls #to_int to convert the index" do
+ index = mock("setbyte index")
+ index.should_receive(:to_int).and_return(1)
+
+ str = "hat"
+ str.setbyte(index, "i".ord)
+ str.should == "hit"
+ end
+
+ it "calls to_int to convert the value" do
+ value = mock("setbyte value")
+ value.should_receive(:to_int).and_return("i".ord)
+
+ str = "hat"
+ str.setbyte(1, value)
+ str.should == "hit"
+ end
+end
diff --git a/spec/ruby/core/string/shared/chars.rb b/spec/ruby/core/string/shared/chars.rb
new file mode 100644
index 0000000000..6a2fdb483f
--- /dev/null
+++ b/spec/ruby/core/string/shared/chars.rb
@@ -0,0 +1,80 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :string_chars, shared: true do
+ it "passes each char in self to the given block" do
+ a = []
+ "hello".send(@method) { |c| a << c }
+ a.should == ['h', 'e', 'l', 'l', 'o']
+ end
+
+ it "returns self" do
+ s = StringSpecs::MyString.new "hello"
+ s.send(@method){}.should equal(s)
+ end
+
+
+ it "is unicode aware" do
+ "\303\207\342\210\202\303\251\306\222g".send(@method).to_a.should ==
+ ["\303\207", "\342\210\202", "\303\251", "\306\222", "g"]
+ end
+
+ with_feature :encoding do
+ it "returns characters in the same encoding as self" do
+ "&%".force_encoding('Shift_JIS').send(@method).to_a.all? {|c| c.encoding.name.should == 'Shift_JIS'}
+ "&%".encode('ASCII-8BIT').send(@method).to_a.all? {|c| c.encoding.name.should == 'ASCII-8BIT'}
+ end
+
+ it "works with multibyte characters" do
+ s = "\u{8987}".force_encoding("UTF-8")
+ s.bytesize.should == 3
+ s.send(@method).to_a.should == [s]
+ end
+
+ it "works if the String's contents is invalid for its encoding" do
+ xA4 = [0xA4].pack('C')
+ xA4.force_encoding('UTF-8')
+ xA4.valid_encoding?.should be_false
+ xA4.send(@method).to_a.should == [xA4.force_encoding("UTF-8")]
+ end
+
+ it "returns a different character if the String is transcoded" do
+ s = "\u{20AC}".force_encoding('UTF-8')
+ s.encode('UTF-8').send(@method).to_a.should == ["\u{20AC}".force_encoding('UTF-8')]
+ s.encode('iso-8859-15').send(@method).to_a.should == [[0xA4].pack('C').force_encoding('iso-8859-15')]
+ s.encode('iso-8859-15').encode('UTF-8').send(@method).to_a.should == ["\u{20AC}".force_encoding('UTF-8')]
+ end
+
+ it "uses the String's encoding to determine what characters it contains" do
+ s = "\u{24B62}"
+
+ s.force_encoding('UTF-8').send(@method).to_a.should == [
+ s.force_encoding('UTF-8')
+ ]
+ s.force_encoding('BINARY').send(@method).to_a.should == [
+ [0xF0].pack('C').force_encoding('BINARY'),
+ [0xA4].pack('C').force_encoding('BINARY'),
+ [0xAD].pack('C').force_encoding('BINARY'),
+ [0xA2].pack('C').force_encoding('BINARY')
+ ]
+ s.force_encoding('SJIS').send(@method).to_a.should == [
+ [0xF0,0xA4].pack('CC').force_encoding('SJIS'),
+ [0xAD].pack('C').force_encoding('SJIS'),
+ [0xA2].pack('C').force_encoding('SJIS')
+ ]
+ end
+
+ it "taints resulting strings when self is tainted" do
+ str = "hello"
+
+ str.send(@method) do |x|
+ x.tainted?.should == false
+ end
+
+ str.dup.taint.send(@method) do |x|
+ x.tainted?.should == true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/shared/codepoints.rb b/spec/ruby/core/string/shared/codepoints.rb
new file mode 100644
index 0000000000..68f82b4468
--- /dev/null
+++ b/spec/ruby/core/string/shared/codepoints.rb
@@ -0,0 +1,56 @@
+# -*- encoding: binary -*-
+describe :string_codepoints, shared: true do
+ it "raises an ArgumentError when self has an invalid encoding and a method is called on the returned Enumerator" do
+ s = "\xDF".force_encoding(Encoding::UTF_8)
+ s.valid_encoding?.should be_false
+ lambda { s.send(@method).to_a }.should raise_error(ArgumentError)
+ end
+
+ it "yields each codepoint to the block if one is given" do
+ codepoints = []
+ "abcd".send(@method) do |codepoint|
+ codepoints << codepoint
+ end
+ codepoints.should == [97, 98, 99, 100]
+ end
+
+ it "raises an ArgumentError if self's encoding is invalid and a block is given" do
+ s = "\xDF".force_encoding(Encoding::UTF_8)
+ s.valid_encoding?.should be_false
+ lambda { s.send(@method) { } }.should raise_error(ArgumentError)
+ end
+
+ it "returns codepoints as Fixnums" do
+ "glark\u{20}".send(@method).to_a.each do |codepoint|
+ codepoint.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ it "returns one codepoint for each character" do
+ s = "\u{9876}\u{28}\u{1987}"
+ s.send(@method).to_a.size.should == s.chars.to_a.size
+ end
+
+ it "works for multibyte characters" do
+ s = "\u{9819}"
+ s.bytesize.should == 3
+ s.send(@method).to_a.should == [38937]
+ end
+
+ it "returns the codepoint corresponding to the character's position in the String's encoding" do
+ "\u{787}".send(@method).to_a.should == [1927]
+ end
+
+ it "round-trips to the original String using Integer#chr" do
+ s = "\u{13}\u{7711}\u{1010}"
+ s2 = ""
+ s.send(@method) {|n| s2 << n.chr(Encoding::UTF_8)}
+ s.should == s2
+ end
+
+ it "is synonymous with #bytes for Strings which are single-byte optimisable" do
+ s = "(){}".encode('ascii')
+ s.ascii_only?.should be_true
+ s.send(@method).to_a.should == s.bytes.to_a
+ end
+end
diff --git a/spec/ruby/core/string/shared/concat.rb b/spec/ruby/core/string/shared/concat.rb
new file mode 100644
index 0000000000..d7b9cdfec3
--- /dev/null
+++ b/spec/ruby/core/string/shared/concat.rb
@@ -0,0 +1,160 @@
+describe :string_concat, shared: true do
+ it "concatenates the given argument to self and returns self" do
+ str = 'hello '
+ str.send(@method, 'world').should equal(str)
+ str.should == "hello world"
+ end
+
+ it "converts the given argument to a String using to_str" do
+ obj = mock('world!')
+ obj.should_receive(:to_str).and_return("world!")
+ a = 'hello '.send(@method, obj)
+ a.should == 'hello world!'
+ end
+
+ it "raises a TypeError if the given argument can't be converted to a String" do
+ lambda { 'hello '.send(@method, []) }.should raise_error(TypeError)
+ lambda { 'hello '.send(@method, mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "raises a #{frozen_error_class} when self is frozen" do
+ a = "hello"
+ a.freeze
+
+ lambda { a.send(@method, "") }.should raise_error(frozen_error_class)
+ lambda { a.send(@method, "test") }.should raise_error(frozen_error_class)
+ end
+
+ it "returns a String when given a subclass instance" do
+ a = "hello"
+ a.send(@method, StringSpecs::MyString.new(" world"))
+ a.should == "hello world"
+ a.should be_an_instance_of(String)
+ end
+
+ it "returns an instance of same class when called on a subclass" do
+ str = StringSpecs::MyString.new("hello")
+ str.send(@method, " world")
+ str.should == "hello world"
+ str.should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ it "taints self if other is tainted" do
+ "x".send(@method, "".taint).tainted?.should == true
+ "x".send(@method, "y".taint).tainted?.should == true
+ end
+
+ it "untrusts self if other is untrusted" do
+ "x".send(@method, "".untrust).untrusted?.should == true
+ "x".send(@method, "y".untrust).untrusted?.should == true
+ end
+
+ describe "with Integer" do
+ it "concatencates the argument interpreted as a codepoint" do
+ b = "".send(@method, 33)
+ b.should == "!"
+
+ b.encode!(Encoding::UTF_8)
+ b.send(@method, 0x203D)
+ b.should == "!\u203D"
+ end
+
+ # #5855
+ it "returns a ASCII-8BIT string if self is US-ASCII and the argument is between 128-255 (inclusive)" do
+ a = ("".encode(Encoding::US_ASCII).send(@method, 128))
+ a.encoding.should == Encoding::ASCII_8BIT
+ a.should == 128.chr
+
+ a = ("".encode(Encoding::US_ASCII).send(@method, 255))
+ a.encoding.should == Encoding::ASCII_8BIT
+ a.should == 255.chr
+ end
+
+ it "raises RangeError if the argument is an invalid codepoint for self's encoding" do
+ lambda { "".encode(Encoding::US_ASCII).send(@method, 256) }.should raise_error(RangeError)
+ lambda { "".encode(Encoding::EUC_JP).send(@method, 0x81) }.should raise_error(RangeError)
+ end
+
+ it "raises RangeError if the argument is negative" do
+ lambda { "".send(@method, -200) }.should raise_error(RangeError)
+ lambda { "".send(@method, -bignum_value) }.should raise_error(RangeError)
+ end
+
+ it "doesn't call to_int on its argument" do
+ x = mock('x')
+ x.should_not_receive(:to_int)
+
+ lambda { "".send(@method, x) }.should raise_error(TypeError)
+ end
+
+ it "raises a #{frozen_error_class} when self is frozen" do
+ a = "hello"
+ a.freeze
+
+ lambda { a.send(@method, 0) }.should raise_error(frozen_error_class)
+ lambda { a.send(@method, 33) }.should raise_error(frozen_error_class)
+ end
+ end
+end
+
+describe :string_concat_encoding, shared: true do
+ describe "when self is in an ASCII-incompatible encoding incompatible with the argument's encoding" do
+ it "uses self's encoding if both are empty" do
+ "".encode("UTF-16LE").send(@method, "").encoding.should == Encoding::UTF_16LE
+ end
+
+ it "uses self's encoding if the argument is empty" do
+ "x".encode("UTF-16LE").send(@method, "").encoding.should == Encoding::UTF_16LE
+ end
+
+ it "uses the argument's encoding if self is empty" do
+ "".encode("UTF-16LE").send(@method, "x".encode("UTF-8")).encoding.should == Encoding::UTF_8
+ end
+
+ it "raises Encoding::CompatibilityError if neither are empty" do
+ lambda { "x".encode("UTF-16LE").send(@method, "y".encode("UTF-8")) }.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+
+ describe "when the argument is in an ASCII-incompatible encoding incompatible with self's encoding" do
+ it "uses self's encoding if both are empty" do
+ "".encode("UTF-8").send(@method, "".encode("UTF-16LE")).encoding.should == Encoding::UTF_8
+ end
+
+ it "uses self's encoding if the argument is empty" do
+ "x".encode("UTF-8").send(@method, "".encode("UTF-16LE")).encoding.should == Encoding::UTF_8
+ end
+
+ it "uses the argument's encoding if self is empty" do
+ "".encode("UTF-8").send(@method, "x".encode("UTF-16LE")).encoding.should == Encoding::UTF_16LE
+ end
+
+ it "raises Encoding::CompatibilityError if neither are empty" do
+ lambda { "x".encode("UTF-8").send(@method, "y".encode("UTF-16LE")) }.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+
+ describe "when self and the argument are in different ASCII-compatible encodings" do
+ it "uses self's encoding if both are ASCII-only" do
+ "abc".encode("UTF-8").send(@method, "123".encode("SHIFT_JIS")).encoding.should == Encoding::UTF_8
+ end
+
+ it "uses self's encoding if the argument is ASCII-only" do
+ "\u00E9".encode("UTF-8").send(@method, "123".encode("ISO-8859-1")).encoding.should == Encoding::UTF_8
+ end
+
+ it "uses the argument's encoding if self is ASCII-only" do
+ "abc".encode("UTF-8").send(@method, "\u00E9".encode("ISO-8859-1")).encoding.should == Encoding::ISO_8859_1
+ end
+
+ it "raises Encoding::CompatibilityError if neither are ASCII-only" do
+ lambda { "\u00E9".encode("UTF-8").send(@method, "\u00E9".encode("ISO-8859-1")) }.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+
+ describe "when self is ASCII-8BIT and argument is US-ASCII" do
+ it "uses ASCII-8BIT encoding" do
+ "abc".encode("ASCII-8BIT").send(@method, "123".encode("US-ASCII")).encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+end
diff --git a/spec/ruby/core/string/shared/each_char_without_block.rb b/spec/ruby/core/string/shared/each_char_without_block.rb
new file mode 100644
index 0000000000..397100ce0e
--- /dev/null
+++ b/spec/ruby/core/string/shared/each_char_without_block.rb
@@ -0,0 +1,26 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :string_each_char_without_block, shared: true do
+ describe "when no block is given" do
+ it "returns an enumerator" do
+ enum = "hello".send(@method)
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.should == ['h', 'e', 'l', 'l', 'o']
+ end
+
+ describe "returned enumerator" do
+ describe "size" do
+ it "should return the size of the string" do
+ str = "hello"
+ str.send(@method).size.should == str.size
+ str = "ola"
+ str.send(@method).size.should == str.size
+ str = "\303\207\342\210\202\303\251\306\222g"
+ str.send(@method).size.should == str.size
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/shared/each_codepoint_without_block.rb b/spec/ruby/core/string/shared/each_codepoint_without_block.rb
new file mode 100644
index 0000000000..92b7f76032
--- /dev/null
+++ b/spec/ruby/core/string/shared/each_codepoint_without_block.rb
@@ -0,0 +1,33 @@
+# -*- encoding: binary -*-
+describe :string_each_codepoint_without_block, shared: true do
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ "".send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ it "returns an Enumerator even when self has an invalid encoding" do
+ s = "\xDF".force_encoding(Encoding::UTF_8)
+ s.valid_encoding?.should be_false
+ s.send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return the size of the string" do
+ str = "hello"
+ str.send(@method).size.should == str.size
+ str = "ola"
+ str.send(@method).size.should == str.size
+ str = "\303\207\342\210\202\303\251\306\222g"
+ str.send(@method).size.should == str.size
+ end
+
+ it "should return the size of the string even when the string has an invalid encoding" do
+ s = "\xDF".force_encoding(Encoding::UTF_8)
+ s.valid_encoding?.should be_false
+ s.send(@method).size.should == 1
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/shared/each_line.rb b/spec/ruby/core/string/shared/each_line.rb
new file mode 100644
index 0000000000..19cf5e6d78
--- /dev/null
+++ b/spec/ruby/core/string/shared/each_line.rb
@@ -0,0 +1,165 @@
+describe :string_each_line, shared: true do
+ it "splits using default newline separator when none is specified" do
+ a = []
+ "one\ntwo\r\nthree".send(@method) { |s| a << s }
+ a.should == ["one\n", "two\r\n", "three"]
+
+ b = []
+ "hello\n\n\nworld".send(@method) { |s| b << s }
+ b.should == ["hello\n", "\n", "\n", "world"]
+
+ c = []
+ "\n\n\n\n\n".send(@method) {|s| c << s}
+ c.should == ["\n", "\n", "\n", "\n", "\n"]
+ end
+
+ it "splits self using the supplied record separator and passes each substring to the block" do
+ a = []
+ "one\ntwo\r\nthree".send(@method, "\n") { |s| a << s }
+ a.should == ["one\n", "two\r\n", "three"]
+
+ b = []
+ "hello\nworld".send(@method, 'l') { |s| b << s }
+ b.should == [ "hel", "l", "o\nworl", "d" ]
+
+ c = []
+ "hello\n\n\nworld".send(@method, "\n") { |s| c << s }
+ c.should == ["hello\n", "\n", "\n", "world"]
+ end
+
+ it "taints substrings that are passed to the block if self is tainted" do
+ "one\ntwo\r\nthree".taint.send(@method) { |s| s.tainted?.should == true }
+
+ "x.y.".send(@method, ".".taint) { |s| s.tainted?.should == false }
+ end
+
+ it "passes self as a whole to the block if the separator is nil" do
+ a = []
+ "one\ntwo\r\nthree".send(@method, nil) { |s| a << s }
+ a.should == ["one\ntwo\r\nthree"]
+ end
+
+ ruby_version_is ''...'2.5' do
+ it "yields paragraphs (broken by 2 or more successive newlines) when passed ''" do
+ a = []
+ "hello\nworld\n\n\nand\nuniverse\n\n\n\n\n".send(@method, '') { |s| a << s }
+ a.should == ["hello\nworld\n\n\n", "and\nuniverse\n\n\n\n\n"]
+
+ a = []
+ "hello\nworld\n\n\nand\nuniverse\n\n\n\n\ndog".send(@method, '') { |s| a << s }
+ a.should == ["hello\nworld\n\n\n", "and\nuniverse\n\n\n\n\n", "dog"]
+ end
+ end
+
+ ruby_version_is '2.5' do
+ it "yields paragraphs (broken by 2 or more successive newlines) when passed '' and replaces multiple newlines with only two ones" do
+ a = []
+ "hello\nworld\n\n\nand\nuniverse\n\n\n\n\n".send(@method, '') { |s| a << s }
+ a.should == ["hello\nworld\n\n", "and\nuniverse\n\n"]
+
+ a = []
+ "hello\nworld\n\n\nand\nuniverse\n\n\n\n\ndog".send(@method, '') { |s| a << s }
+ a.should == ["hello\nworld\n\n", "and\nuniverse\n\n", "dog"]
+ end
+ end
+
+ describe "uses $/" do
+ before :each do
+ @before_separator = $/
+ end
+
+ after :each do
+ $/ = @before_separator
+ end
+
+ it "as the separator when none is given" do
+ [
+ "", "x", "x\ny", "x\ry", "x\r\ny", "x\n\r\r\ny",
+ "hello hullo bello"
+ ].each do |str|
+ ["", "llo", "\n", "\r", nil].each do |sep|
+ expected = []
+ str.send(@method, sep) { |x| expected << x }
+
+ $/ = sep
+
+ actual = []
+ str.send(@method) { |x| actual << x }
+
+ actual.should == expected
+ end
+ end
+ end
+ end
+
+ it "yields subclass instances for subclasses" do
+ a = []
+ StringSpecs::MyString.new("hello\nworld").send(@method) { |s| a << s.class }
+ a.should == [StringSpecs::MyString, StringSpecs::MyString]
+ end
+
+ it "returns self" do
+ s = "hello\nworld"
+ (s.send(@method) {}).should equal(s)
+ end
+
+ it "tries to convert the separator to a string using to_str" do
+ separator = mock('l')
+ separator.should_receive(:to_str).and_return("l")
+
+ a = []
+ "hello\nworld".send(@method, separator) { |s| a << s }
+ a.should == [ "hel", "l", "o\nworl", "d" ]
+ end
+
+ it "does not care if the string is modified while substituting" do
+ str = "hello\nworld."
+ out = []
+ str.send(@method){|x| out << x; str[-1] = '!' }.should == "hello\nworld!"
+ out.should == ["hello\n", "world."]
+ end
+
+ it "raises a TypeError when the separator can't be converted to a string" do
+ lambda { "hello world".send(@method, false) {} }.should raise_error(TypeError)
+ lambda { "hello world".send(@method, mock('x')) {} }.should raise_error(TypeError)
+ end
+
+ it "accepts a string separator" do
+ "hello world".send(@method, ?o).to_a.should == ["hello", " wo", "rld"]
+ end
+
+ it "raises a TypeError when the separator is a symbol" do
+ lambda { "hello world".send(@method, :o).to_a }.should raise_error(TypeError)
+ end
+
+ ruby_version_is '2.4' do
+ context "when `chomp` keyword argument is passed" do
+ it "removes new line characters when separator is not specified" do
+ a = []
+ "hello \nworld\n".send(@method, chomp: true) { |s| a << s }
+ a.should == ["hello ", "world"]
+
+ a = []
+ "hello \r\nworld\r\n".send(@method, chomp: true) { |s| a << s }
+ a.should == ["hello ", "world"]
+ end
+
+ it "removes only specified separator" do
+ a = []
+ "hello world".send(@method, ' ', chomp: true) { |s| a << s }
+ a.should == ["hello", "world"]
+ end
+
+ # https://bugs.ruby-lang.org/issues/14257
+ it "ignores new line characters when separator is specified" do
+ a = []
+ "hello\n world\n".send(@method, ' ', chomp: true) { |s| a << s }
+ a.should == ["hello\n", "world\n"]
+
+ a = []
+ "hello\r\n world\r\n".send(@method, ' ', chomp: true) { |s| a << s }
+ a.should == ["hello\r\n", "world\r\n"]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/shared/each_line_without_block.rb b/spec/ruby/core/string/shared/each_line_without_block.rb
new file mode 100644
index 0000000000..8e08b0390c
--- /dev/null
+++ b/spec/ruby/core/string/shared/each_line_without_block.rb
@@ -0,0 +1,17 @@
+describe :string_each_line_without_block, shared: true do
+ describe "when no block is given" do
+ it "returns an enumerator" do
+ enum = "hello world".send(@method, ' ')
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.should == ["hello ", "world"]
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ "hello world".send(@method, ' ').size.should == nil
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/shared/encode.rb b/spec/ruby/core/string/shared/encode.rb
new file mode 100644
index 0000000000..71d46b1bd3
--- /dev/null
+++ b/spec/ruby/core/string/shared/encode.rb
@@ -0,0 +1,247 @@
+# -*- encoding: utf-8 -*-
+describe :string_encode, shared: true do
+ describe "when passed no options" do
+ it "transcodes to Encoding.default_internal when set" do
+ Encoding.default_internal = Encoding::UTF_8
+ str = [0xA4, 0xA2].pack('CC').force_encoding Encoding::EUC_JP
+ str.send(@method).should == "ã‚"
+ end
+
+ it "transcodes a 7-bit String despite no generic converting being available" do
+ lambda do
+ Encoding::Converter.new Encoding::Emacs_Mule, Encoding::ASCII_8BIT
+ end.should raise_error(Encoding::ConverterNotFoundError)
+
+ Encoding.default_internal = Encoding::Emacs_Mule
+ str = "\x79".force_encoding Encoding::ASCII_8BIT
+
+ str.send(@method).should == "y".force_encoding(Encoding::ASCII_8BIT)
+ end
+
+ it "raises an Encoding::ConverterNotFoundError when no conversion is possible" do
+ Encoding.default_internal = Encoding::Emacs_Mule
+ str = [0x80].pack('C').force_encoding Encoding::ASCII_8BIT
+ lambda { str.send(@method) }.should raise_error(Encoding::ConverterNotFoundError)
+ end
+ end
+
+ describe "when passed to encoding" do
+ it "accepts a String argument" do
+ str = [0xA4, 0xA2].pack('CC').force_encoding Encoding::EUC_JP
+ str.send(@method, "utf-8").should == "ã‚"
+ end
+
+ it "calls #to_str to convert the object to an Encoding" do
+ enc = mock("string encode encoding")
+ enc.should_receive(:to_str).and_return("utf-8")
+
+ str = [0xA4, 0xA2].pack('CC').force_encoding Encoding::EUC_JP
+ str.send(@method, enc).should == "ã‚"
+ end
+
+ it "transcodes to the passed encoding" do
+ str = [0xA4, 0xA2].pack('CC').force_encoding Encoding::EUC_JP
+ str.send(@method, Encoding::UTF_8).should == "ã‚"
+ end
+
+ it "transcodes Japanese multibyte characters" do
+ str = "ã‚ã„ã†ãˆãŠ"
+ str.send(@method, Encoding::ISO_2022_JP).should ==
+ "\e\x24\x42\x24\x22\x24\x24\x24\x26\x24\x28\x24\x2A\e\x28\x42".force_encoding(Encoding::ISO_2022_JP)
+ end
+
+ it "transcodes a 7-bit String despite no generic converting being available" do
+ lambda do
+ Encoding::Converter.new Encoding::Emacs_Mule, Encoding::ASCII_8BIT
+ end.should raise_error(Encoding::ConverterNotFoundError)
+
+ str = "\x79".force_encoding Encoding::ASCII_8BIT
+ str.send(@method, Encoding::Emacs_Mule).should == "y".force_encoding(Encoding::ASCII_8BIT)
+ end
+
+ it "raises an Encoding::ConverterNotFoundError when no conversion is possible" do
+ str = [0x80].pack('C').force_encoding Encoding::ASCII_8BIT
+ lambda do
+ str.send(@method, Encoding::Emacs_Mule)
+ end.should raise_error(Encoding::ConverterNotFoundError)
+ end
+
+ it "raises an Encoding::ConverterNotFoundError for an invalid encoding" do
+ lambda do
+ "abc".send(@method, "xyz")
+ end.should raise_error(Encoding::ConverterNotFoundError)
+ end
+ end
+
+ describe "when passed options" do
+ it "does not process transcoding options if not transcoding" do
+ result = "ã‚\ufffdã‚".send(@method, undef: :replace)
+ result.should == "ã‚\ufffdã‚"
+ end
+
+ it "calls #to_hash to convert the object" do
+ options = mock("string encode options")
+ options.should_receive(:to_hash).and_return({ undef: :replace })
+
+ result = "ã‚\ufffdã‚".send(@method, options)
+ result.should == "ã‚\ufffdã‚"
+ end
+
+ it "transcodes to Encoding.default_internal when set" do
+ Encoding.default_internal = Encoding::UTF_8
+ str = [0xA4, 0xA2].pack('CC').force_encoding Encoding::EUC_JP
+ str.send(@method, invalid: :replace).should == "ã‚"
+ end
+
+ it "raises an Encoding::ConverterNotFoundError when no conversion is possible despite 'invalid: :replace, undef: :replace'" do
+ Encoding.default_internal = Encoding::Emacs_Mule
+ str = [0x80].pack('C').force_encoding Encoding::ASCII_8BIT
+ lambda do
+ str.send(@method, invalid: :replace, undef: :replace)
+ end.should raise_error(Encoding::ConverterNotFoundError)
+ end
+
+ it "replaces invalid characters when replacing Emacs-Mule encoded strings" do
+ got = [0x80].pack('C').force_encoding('Emacs-Mule').send(@method, invalid: :replace)
+
+ got.should == "?".encode('Emacs-Mule')
+ end
+ end
+
+ describe "when passed to, from" do
+ it "transcodes between the encodings ignoring the String encoding" do
+ str = "ã‚"
+ result = [0xA6, 0xD0, 0x8F, 0xAB, 0xE4, 0x8F, 0xAB, 0xB1].pack('C8')
+ result.force_encoding Encoding::EUC_JP
+ str.send(@method, "euc-jp", "ibm437").should == result
+ end
+
+ it "calls #to_str to convert the from object to an Encoding" do
+ enc = mock("string encode encoding")
+ enc.should_receive(:to_str).and_return("ibm437")
+
+ str = "ã‚"
+ result = [0xA6, 0xD0, 0x8F, 0xAB, 0xE4, 0x8F, 0xAB, 0xB1].pack('C8')
+ result.force_encoding Encoding::EUC_JP
+
+ str.send(@method, "euc-jp", enc).should == result
+ end
+ end
+
+ describe "when passed to, options" do
+ it "replaces undefined characters in the destination encoding" do
+ result = "ã‚?ã‚".send(@method, Encoding::EUC_JP, undef: :replace)
+ # testing for: "\xA4\xA2?\xA4\xA2"
+ xA4xA2 = [0xA4, 0xA2].pack('CC')
+ result.should == "#{xA4xA2}?#{xA4xA2}".force_encoding("euc-jp")
+ end
+
+ it "replaces invalid characters in the destination encoding" do
+ xFF = [0xFF].pack('C').force_encoding('utf-8')
+ "ab#{xFF}c".send(@method, Encoding::ISO_8859_1, invalid: :replace).should == "ab?c"
+ end
+
+ it "calls #to_hash to convert the options object" do
+ options = mock("string encode options")
+ options.should_receive(:to_hash).and_return({ undef: :replace })
+
+ result = "ã‚?ã‚".send(@method, Encoding::EUC_JP, options)
+ xA4xA2 = [0xA4, 0xA2].pack('CC').force_encoding('utf-8')
+ result.should == "#{xA4xA2}?#{xA4xA2}".force_encoding("euc-jp")
+ end
+ end
+
+ describe "when passed to, from, options" do
+ it "replaces undefined characters in the destination encoding" do
+ str = "ã‚?ã‚".force_encoding Encoding::ASCII_8BIT
+ result = str.send(@method, "euc-jp", "utf-8", undef: :replace)
+ xA4xA2 = [0xA4, 0xA2].pack('CC').force_encoding('utf-8')
+ result.should == "#{xA4xA2}?#{xA4xA2}".force_encoding("euc-jp")
+ end
+
+ it "replaces invalid characters in the destination encoding" do
+ xFF = [0xFF].pack('C').force_encoding('utf-8')
+ str = "ab#{xFF}c".force_encoding Encoding::ASCII_8BIT
+ str.send(@method, "iso-8859-1", "utf-8", invalid: :replace).should == "ab?c"
+ end
+
+ it "calls #to_str to convert the to object to an encoding" do
+ to = mock("string encode to encoding")
+ to.should_receive(:to_str).and_return("iso-8859-1")
+
+ xFF = [0xFF].pack('C').force_encoding('utf-8')
+ str = "ab#{xFF}c".force_encoding Encoding::ASCII_8BIT
+ str.send(@method, to, "utf-8", invalid: :replace).should == "ab?c"
+ end
+
+ it "calls #to_str to convert the from object to an encoding" do
+ from = mock("string encode to encoding")
+ from.should_receive(:to_str).and_return("utf-8")
+
+ xFF = [0xFF].pack('C').force_encoding('utf-8')
+ str = "ab#{xFF}c".force_encoding Encoding::ASCII_8BIT
+ str.send(@method, "iso-8859-1", from, invalid: :replace).should == "ab?c"
+ end
+
+ it "calls #to_hash to convert the options object" do
+ options = mock("string encode options")
+ options.should_receive(:to_hash).and_return({ invalid: :replace })
+
+ xFF = [0xFF].pack('C').force_encoding('utf-8')
+ str = "ab#{xFF}c".force_encoding Encoding::ASCII_8BIT
+ str.send(@method, "iso-8859-1", "utf-8", options).should == "ab?c"
+ end
+ end
+
+ describe "given the xml: :text option" do
+ it "replaces all instances of '&' with '&amp;'" do
+ '& and &'.send(@method, "UTF-8", xml: :text).should == '&amp; and &amp;'
+ end
+
+ it "replaces all instances of '<' with '&lt;'" do
+ '< and <'.send(@method, "UTF-8", xml: :text).should == '&lt; and &lt;'
+ end
+
+ it "replaces all instances of '>' with '&gt;'" do
+ '> and >'.send(@method, "UTF-8", xml: :text).should == '&gt; and &gt;'
+ end
+
+ it "does not replace '\"'" do
+ '" and "'.send(@method, "UTF-8", xml: :text).should == '" and "'
+ end
+
+ it "replaces undefined characters with their upper-case hexadecimal numeric character references" do
+ 'ürst'.send(@method, Encoding::US_ASCII, xml: :text).should == '&#xFC;rst'
+ end
+ end
+
+ describe "given the xml: :attr option" do
+ it "surrounds the encoded text with double-quotes" do
+ 'abc'.send(@method, "UTF-8", xml: :attr).should == '"abc"'
+ end
+
+ it "replaces all instances of '&' with '&amp;'" do
+ '& and &'.send(@method, "UTF-8", xml: :attr).should == '"&amp; and &amp;"'
+ end
+
+ it "replaces all instances of '<' with '&lt;'" do
+ '< and <'.send(@method, "UTF-8", xml: :attr).should == '"&lt; and &lt;"'
+ end
+
+ it "replaces all instances of '>' with '&gt;'" do
+ '> and >'.send(@method, "UTF-8", xml: :attr).should == '"&gt; and &gt;"'
+ end
+
+ it "replaces all instances of '\"' with '&quot;'" do
+ '" and "'.send(@method, "UTF-8", xml: :attr).should == '"&quot; and &quot;"'
+ end
+
+ it "replaces undefined characters with their upper-case hexadecimal numeric character references" do
+ 'ürst'.send(@method, Encoding::US_ASCII, xml: :attr).should == '"&#xFC;rst"'
+ end
+ end
+
+ it "raises ArgumentError if the value of the :xml option is not :text or :attr" do
+ lambda { ''.send(@method, "UTF-8", xml: :other) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/string/shared/eql.rb b/spec/ruby/core/string/shared/eql.rb
new file mode 100644
index 0000000000..85b861f4f1
--- /dev/null
+++ b/spec/ruby/core/string/shared/eql.rb
@@ -0,0 +1,34 @@
+# -*- encoding: binary -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :string_eql_value, shared: true do
+ it "returns true if self <=> string returns 0" do
+ 'hello'.send(@method, 'hello').should be_true
+ end
+
+ it "returns false if self <=> string does not return 0" do
+ "more".send(@method, "MORE").should be_false
+ "less".send(@method, "greater").should be_false
+ end
+
+ it "ignores encoding difference of compatible string" do
+ "hello".force_encoding("utf-8").send(@method, "hello".force_encoding("iso-8859-1")).should be_true
+ end
+
+ it "considers encoding difference of incompatible string" do
+ "\xff".force_encoding("utf-8").send(@method, "\xff".force_encoding("iso-8859-1")).should be_false
+ end
+
+ it "considers encoding compatibility" do
+ "hello".force_encoding("utf-8").send(@method, "hello".force_encoding("utf-32le")).should be_false
+ end
+
+ it "ignores subclass differences" do
+ a = "hello"
+ b = StringSpecs::MyString.new("hello")
+
+ a.send(@method, b).should be_true
+ b.send(@method, a).should be_true
+ end
+end
diff --git a/spec/ruby/core/string/shared/equal_value.rb b/spec/ruby/core/string/shared/equal_value.rb
new file mode 100644
index 0000000000..d797cb1483
--- /dev/null
+++ b/spec/ruby/core/string/shared/equal_value.rb
@@ -0,0 +1,29 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :string_equal_value, shared: true do
+ it "returns false if obj does not respond to to_str" do
+ 'hello'.send(@method, 5).should be_false
+ not_supported_on :opal do
+ 'hello'.send(@method, :hello).should be_false
+ end
+ 'hello'.send(@method, mock('x')).should be_false
+ end
+
+ it "returns obj == self if obj responds to to_str" do
+ obj = Object.new
+
+ # String#== merely checks if #to_str is defined. It does
+ # not call it.
+ obj.stub!(:to_str)
+
+ # Don't use @method for :== in `obj.should_recerive(:==)`
+ obj.should_receive(:==).and_return(true)
+
+ 'hello'.send(@method, obj).should be_true
+ end
+
+ it "is not fooled by NUL characters" do
+ "abc\0def".send(@method, "abc\0xyz").should be_false
+ end
+end
diff --git a/spec/ruby/core/string/shared/grapheme_clusters.rb b/spec/ruby/core/string/shared/grapheme_clusters.rb
new file mode 100644
index 0000000000..8b666868b1
--- /dev/null
+++ b/spec/ruby/core/string/shared/grapheme_clusters.rb
@@ -0,0 +1,16 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :string_grapheme_clusters, shared: true do
+ it "passes each grapheme cluster in self to the given block" do
+ a = []
+ # test string: abc[rainbow flag emoji][paw prints]
+ "ab\u{1f3f3}\u{fe0f}\u{200d}\u{1f308}\u{1F43E}".send(@method) { |c| a << c }
+ a.should == ['a', 'b', "\u{1f3f3}\u{fe0f}\u{200d}\u{1f308}", "\u{1F43E}"]
+ end
+
+ it "returns self" do
+ s = StringSpecs::MyString.new "ab\u{1f3f3}\u{fe0f}\u{200d}\u{1f308}\u{1F43E}"
+ s.send(@method) {}.should equal(s)
+ end
+end
diff --git a/spec/ruby/core/string/shared/length.rb b/spec/ruby/core/string/shared/length.rb
new file mode 100644
index 0000000000..0e6e66ee1c
--- /dev/null
+++ b/spec/ruby/core/string/shared/length.rb
@@ -0,0 +1,28 @@
+# encoding: utf-8
+
+describe :string_length, shared: true do
+ it "returns the length of self" do
+ "".send(@method).should == 0
+ "\x00".send(@method).should == 1
+ "one".send(@method).should == 3
+ "two".send(@method).should == 3
+ "three".send(@method).should == 5
+ "four".send(@method).should == 4
+ end
+
+ with_feature :encoding do
+ it "returns the length of a string in different encodings" do
+ utf8_str = 'ã“ã«ã¡ã‚' * 100
+ utf8_str.size.should == 400
+ utf8_str.encode(Encoding::UTF_32BE).size.should == 400
+ utf8_str.encode(Encoding::SHIFT_JIS).size.should == 400
+ end
+
+ it "returns the length of the new self after encoding is changed" do
+ str = 'ã“ã«ã¡ã‚'
+ str.send(@method)
+
+ str.force_encoding('ASCII-8BIT').send(@method).should == 12
+ end
+ end
+end
diff --git a/spec/ruby/core/string/shared/replace.rb b/spec/ruby/core/string/shared/replace.rb
new file mode 100644
index 0000000000..a583b88b5a
--- /dev/null
+++ b/spec/ruby/core/string/shared/replace.rb
@@ -0,0 +1,75 @@
+describe :string_replace, shared: true do
+ it "returns self" do
+ a = "a"
+ a.send(@method, "b").should equal(a)
+ end
+
+ it "replaces the content of self with other" do
+ a = "some string"
+ a.send(@method, "another string")
+ a.should == "another string"
+ end
+
+ it "taints self if other is tainted" do
+ a = ""
+ b = "".taint
+ a.send(@method, b)
+ a.tainted?.should == true
+ end
+
+ it "does not untaint self if other is untainted" do
+ a = "".taint
+ b = ""
+ a.send(@method, b)
+ a.tainted?.should == true
+ end
+
+ it "untrusts self if other is untrusted" do
+ a = ""
+ b = "".untrust
+ a.send(@method, b)
+ a.untrusted?.should == true
+ end
+
+ it "does not trust self if other is trusted" do
+ a = "".untrust
+ b = ""
+ a.send(@method, b)
+ a.untrusted?.should == true
+ end
+
+ it "replaces the encoding of self with that of other" do
+ a = "".encode("UTF-16LE")
+ b = "".encode("UTF-8")
+ a.send(@method, b)
+ a.encoding.should == Encoding::UTF_8
+ end
+
+ it "carries over the encoding invalidity" do
+ a = "\u{8765}".force_encoding('ascii')
+ "".send(@method, a).valid_encoding?.should be_false
+ end
+
+ it "tries to convert other to string using to_str" do
+ other = mock('x')
+ other.should_receive(:to_str).and_return("converted to a string")
+ "hello".send(@method, other).should == "converted to a string"
+ end
+
+ it "raises a TypeError if other can't be converted to string" do
+ lambda { "hello".send(@method, 123) }.should raise_error(TypeError)
+ lambda { "hello".send(@method, []) }.should raise_error(TypeError)
+ lambda { "hello".send(@method, mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "raises a #{frozen_error_class} on a frozen instance that is modified" do
+ a = "hello".freeze
+ lambda { a.send(@method, "world") }.should raise_error(frozen_error_class)
+ end
+
+ # see [ruby-core:23666]
+ it "raises a #{frozen_error_class} on a frozen instance when self-replacing" do
+ a = "hello".freeze
+ lambda { a.send(@method, a) }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/string/shared/slice.rb b/spec/ruby/core/string/shared/slice.rb
new file mode 100644
index 0000000000..697fa2e530
--- /dev/null
+++ b/spec/ruby/core/string/shared/slice.rb
@@ -0,0 +1,557 @@
+describe :string_slice, shared: true do
+ it "returns the character code of the character at the given index" do
+ "hello".send(@method, 0).should == ?h
+ "hello".send(@method, -1).should == ?o
+ end
+
+ it "returns nil if index is outside of self" do
+ "hello".send(@method, 20).should == nil
+ "hello".send(@method, -20).should == nil
+
+ "".send(@method, 0).should == nil
+ "".send(@method, -1).should == nil
+ end
+
+ it "calls to_int on the given index" do
+ "hello".send(@method, 0.5).should == ?h
+
+ obj = mock('1')
+ obj.should_receive(:to_int).and_return(1)
+ "hello".send(@method, obj).should == ?e
+ end
+
+ it "raises a TypeError if the given index is nil" do
+ lambda { "hello".send(@method, nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the given index can't be converted to an Integer" do
+ lambda { "hello".send(@method, mock('x')) }.should raise_error(TypeError)
+ lambda { "hello".send(@method, {}) }.should raise_error(TypeError)
+ lambda { "hello".send(@method, []) }.should raise_error(TypeError)
+ end
+
+ it "raises a RangeError if the index is too big" do
+ lambda { "hello".send(@method, bignum_value) }.should raise_error(RangeError)
+ end
+end
+
+describe :string_slice_index_length, shared: true do
+ it "returns the substring starting at the given index with the given length" do
+ "hello there".send(@method, 0,0).should == ""
+ "hello there".send(@method, 0,1).should == "h"
+ "hello there".send(@method, 0,3).should == "hel"
+ "hello there".send(@method, 0,6).should == "hello "
+ "hello there".send(@method, 0,9).should == "hello the"
+ "hello there".send(@method, 0,12).should == "hello there"
+
+ "hello there".send(@method, 1,0).should == ""
+ "hello there".send(@method, 1,1).should == "e"
+ "hello there".send(@method, 1,3).should == "ell"
+ "hello there".send(@method, 1,6).should == "ello t"
+ "hello there".send(@method, 1,9).should == "ello ther"
+ "hello there".send(@method, 1,12).should == "ello there"
+
+ "hello there".send(@method, 3,0).should == ""
+ "hello there".send(@method, 3,1).should == "l"
+ "hello there".send(@method, 3,3).should == "lo "
+ "hello there".send(@method, 3,6).should == "lo the"
+ "hello there".send(@method, 3,9).should == "lo there"
+
+ "hello there".send(@method, 4,0).should == ""
+ "hello there".send(@method, 4,3).should == "o t"
+ "hello there".send(@method, 4,6).should == "o ther"
+ "hello there".send(@method, 4,9).should == "o there"
+
+ "foo".send(@method, 2,1).should == "o"
+ "foo".send(@method, 3,0).should == ""
+ "foo".send(@method, 3,1).should == ""
+
+ "".send(@method, 0,0).should == ""
+ "".send(@method, 0,1).should == ""
+
+ "x".send(@method, 0,0).should == ""
+ "x".send(@method, 0,1).should == "x"
+ "x".send(@method, 1,0).should == ""
+ "x".send(@method, 1,1).should == ""
+
+ "x".send(@method, -1,0).should == ""
+ "x".send(@method, -1,1).should == "x"
+
+ "hello there".send(@method, -3,2).should == "er"
+ end
+
+ it "always taints resulting strings when self is tainted" do
+ str = "hello world"
+ str.taint
+
+ str.send(@method, 0,0).tainted?.should == true
+ str.send(@method, 0,1).tainted?.should == true
+ str.send(@method, 2,1).tainted?.should == true
+ end
+
+ it "returns a string with the same encoding" do
+ s = "hello there"
+ s.send(@method, 1, 9).encoding.should == s.encoding
+
+ a = "hello".force_encoding("binary")
+ b = " there".force_encoding("ISO-8859-1")
+ c = (a + b).force_encoding(Encoding::US_ASCII)
+
+ c.send(@method, 0, 5).encoding.should == Encoding::US_ASCII
+ c.send(@method, 5, 6).encoding.should == Encoding::US_ASCII
+ c.send(@method, 1, 3).encoding.should == Encoding::US_ASCII
+ c.send(@method, 8, 2).encoding.should == Encoding::US_ASCII
+ c.send(@method, 1, 10).encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns nil if the offset falls outside of self" do
+ "hello there".send(@method, 20,3).should == nil
+ "hello there".send(@method, -20,3).should == nil
+
+ "".send(@method, 1,0).should == nil
+ "".send(@method, 1,1).should == nil
+
+ "".send(@method, -1,0).should == nil
+ "".send(@method, -1,1).should == nil
+
+ "x".send(@method, 2,0).should == nil
+ "x".send(@method, 2,1).should == nil
+
+ "x".send(@method, -2,0).should == nil
+ "x".send(@method, -2,1).should == nil
+ end
+
+ it "returns nil if the length is negative" do
+ "hello there".send(@method, 4,-3).should == nil
+ "hello there".send(@method, -4,-3).should == nil
+ end
+
+ it "calls to_int on the given index and the given length" do
+ "hello".send(@method, 0.5, 1).should == "h"
+ "hello".send(@method, 0.5, 2.5).should == "he"
+ "hello".send(@method, 1, 2.5).should == "el"
+
+ obj = mock('2')
+ obj.should_receive(:to_int).exactly(4).times.and_return(2)
+
+ "hello".send(@method, obj, 1).should == "l"
+ "hello".send(@method, obj, obj).should == "ll"
+ "hello".send(@method, 0, obj).should == "he"
+ end
+
+ it "raises a TypeError when idx or length can't be converted to an integer" do
+ lambda { "hello".send(@method, mock('x'), 0) }.should raise_error(TypeError)
+ lambda { "hello".send(@method, 0, mock('x')) }.should raise_error(TypeError)
+
+ # I'm deliberately including this here.
+ # It means that str.send(@method, other, idx) isn't supported.
+ lambda { "hello".send(@method, "", 0) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when the given index or the given length is nil" do
+ lambda { "hello".send(@method, 1, nil) }.should raise_error(TypeError)
+ lambda { "hello".send(@method, nil, 1) }.should raise_error(TypeError)
+ lambda { "hello".send(@method, nil, nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a RangeError if the index or length is too big" do
+ lambda { "hello".send(@method, bignum_value, 1) }.should raise_error(RangeError)
+ lambda { "hello".send(@method, 0, bignum_value) }.should raise_error(RangeError)
+ end
+
+ it "returns subclass instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.send(@method, 0,0).should be_an_instance_of(StringSpecs::MyString)
+ s.send(@method, 0,4).should be_an_instance_of(StringSpecs::MyString)
+ s.send(@method, 1,4).should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ it "handles repeated application" do
+ "hello world".send(@method, 6, 5).send(@method, 0, 1).should == 'w'
+ "hello world".send(@method, 6, 5).send(@method, 0, 5).should == 'world'
+
+ "hello world".send(@method, 6, 5).send(@method, 1, 1).should == 'o'
+ "hello world".send(@method, 6, 5).send(@method, 1, 4).should == 'orld'
+
+ "hello world".send(@method, 6, 5).send(@method, 4, 1).should == 'd'
+ "hello world".send(@method, 6, 5).send(@method, 5, 0).should == ''
+
+ "hello world".send(@method, 6, 0).send(@method, -1, 0).should == nil
+ "hello world".send(@method, 6, 0).send(@method, 1, 1).should == nil
+ end
+end
+
+describe :string_slice_range, shared: true do
+ it "returns the substring given by the offsets of the range" do
+ "hello there".send(@method, 1..1).should == "e"
+ "hello there".send(@method, 1..3).should == "ell"
+ "hello there".send(@method, 1...3).should == "el"
+ "hello there".send(@method, -4..-2).should == "her"
+ "hello there".send(@method, -4...-2).should == "he"
+ "hello there".send(@method, 5..-1).should == " there"
+ "hello there".send(@method, 5...-1).should == " ther"
+
+ "".send(@method, 0..0).should == ""
+
+ "x".send(@method, 0..0).should == "x"
+ "x".send(@method, 0..1).should == "x"
+ "x".send(@method, 0...1).should == "x"
+ "x".send(@method, 0..-1).should == "x"
+
+ "x".send(@method, 1..1).should == ""
+ "x".send(@method, 1..-1).should == ""
+ 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
+ "hello there".send(@method, 20..1).should == nil
+ "hello there".send(@method, -20..1).should == nil
+ "hello there".send(@method, -20..-1).should == nil
+
+ "".send(@method, -1..-1).should == nil
+ "".send(@method, -1...-1).should == nil
+ "".send(@method, -1..0).should == nil
+ "".send(@method, -1...0).should == nil
+ end
+
+ it "returns an empty string if range.begin is inside self and > real end" do
+ "hello there".send(@method, 1...1).should == ""
+ "hello there".send(@method, 4..2).should == ""
+ "hello".send(@method, 4..-4).should == ""
+ "hello there".send(@method, -5..-6).should == ""
+ "hello there".send(@method, -2..-4).should == ""
+ "hello there".send(@method, -5..-6).should == ""
+ "hello there".send(@method, -5..2).should == ""
+
+ "".send(@method, 0...0).should == ""
+ "".send(@method, 0..-1).should == ""
+ "".send(@method, 0...-1).should == ""
+
+ "x".send(@method, 0...0).should == ""
+ "x".send(@method, 0...-1).should == ""
+ "x".send(@method, 1...1).should == ""
+ "x".send(@method, 1...-1).should == ""
+ end
+
+ it "always taints resulting strings when self is tainted" do
+ str = "hello world"
+ str.taint
+
+ str.send(@method, 0..0).tainted?.should == true
+ str.send(@method, 0...0).tainted?.should == true
+ str.send(@method, 0..1).tainted?.should == true
+ str.send(@method, 0...1).tainted?.should == true
+ str.send(@method, 2..3).tainted?.should == true
+ str.send(@method, 2..0).tainted?.should == true
+ end
+
+ it "returns subclass instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.send(@method, 0...0).should be_an_instance_of(StringSpecs::MyString)
+ s.send(@method, 0..4).should be_an_instance_of(StringSpecs::MyString)
+ s.send(@method, 1..4).should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ it "calls to_int on range arguments" do
+ from = mock('from')
+ to = mock('to')
+
+ # So we can construct a range out of them...
+ from.should_receive(:<=>).twice.and_return(0)
+
+ from.should_receive(:to_int).twice.and_return(1)
+ to.should_receive(:to_int).twice.and_return(-2)
+
+ "hello there".send(@method, from..to).should == "ello ther"
+ "hello there".send(@method, from...to).should == "ello the"
+ end
+
+ it "works with Range subclasses" do
+ a = "GOOD"
+ range_incl = StringSpecs::MyRange.new(1, 2)
+ range_excl = StringSpecs::MyRange.new(-3, -1, true)
+
+ a.send(@method, range_incl).should == "OO"
+ a.send(@method, range_excl).should == "OO"
+ end
+
+ it "handles repeated application" do
+ "hello world".send(@method, 6..11).send(@method, 0..0).should == 'w'
+ "hello world".send(@method, 6..11).send(@method, 0..4).should == 'world'
+
+ "hello world".send(@method, 6..11).send(@method, 1..1).should == 'o'
+ "hello world".send(@method, 6..11).send(@method, 1..4).should == 'orld'
+
+ "hello world".send(@method, 6..11).send(@method, 4..4).should == 'd'
+ "hello world".send(@method, 6..11).send(@method, 5..4).should == ''
+
+ "hello world".send(@method, 6..5).send(@method, -1..-1).should == nil
+ "hello world".send(@method, 6..5).send(@method, 1..1).should == nil
+ end
+end
+
+describe :string_slice_regexp, shared: true do
+ it "returns the matching portion of self" do
+ "hello there".send(@method, /[aeiou](.)\1/).should == "ell"
+ "".send(@method, //).should == ""
+ end
+
+ it "returns nil if there is no match" do
+ "hello there".send(@method, /xyz/).should == nil
+ end
+
+ not_supported_on :opal do
+ it "always taints resulting strings when self or regexp is tainted" do
+ strs = ["hello world"]
+ strs += strs.map { |s| s.dup.taint }
+
+ strs.each do |str|
+ str.send(@method, //).tainted?.should == str.tainted?
+ str.send(@method, /hello/).tainted?.should == str.tainted?
+
+ tainted_re = /./
+ tainted_re.taint
+
+ str.send(@method, tainted_re).tainted?.should == true
+ end
+ end
+
+ it "returns an untrusted string if the regexp is untrusted" do
+ "hello".send(@method, /./.untrust).untrusted?.should be_true
+ end
+ end
+
+ it "returns subclass instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.send(@method, //).should be_an_instance_of(StringSpecs::MyString)
+ s.send(@method, /../).should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ it "sets $~ to MatchData when there is a match and nil when there's none" do
+ 'hello'.send(@method, /./)
+ $~[0].should == 'h'
+
+ 'hello'.send(@method, /not/)
+ $~.should == nil
+ end
+end
+
+describe :string_slice_regexp_index, shared: true do
+ it "returns the capture for the given index" do
+ "hello there".send(@method, /[aeiou](.)\1/, 0).should == "ell"
+ "hello there".send(@method, /[aeiou](.)\1/, 1).should == "l"
+ "hello there".send(@method, /[aeiou](.)\1/, -1).should == "l"
+
+ "har".send(@method, /(.)(.)(.)/, 0).should == "har"
+ "har".send(@method, /(.)(.)(.)/, 1).should == "h"
+ "har".send(@method, /(.)(.)(.)/, 2).should == "a"
+ "har".send(@method, /(.)(.)(.)/, 3).should == "r"
+ "har".send(@method, /(.)(.)(.)/, -1).should == "r"
+ "har".send(@method, /(.)(.)(.)/, -2).should == "a"
+ "har".send(@method, /(.)(.)(.)/, -3).should == "h"
+ end
+
+ it "always taints resulting strings when self or regexp is tainted" do
+ strs = ["hello world"]
+ strs += strs.map { |s| s.dup.taint }
+
+ strs.each do |str|
+ str.send(@method, //, 0).tainted?.should == str.tainted?
+ str.send(@method, /hello/, 0).tainted?.should == str.tainted?
+
+ str.send(@method, /(.)(.)(.)/, 0).tainted?.should == str.tainted?
+ str.send(@method, /(.)(.)(.)/, 1).tainted?.should == str.tainted?
+ str.send(@method, /(.)(.)(.)/, -1).tainted?.should == str.tainted?
+ str.send(@method, /(.)(.)(.)/, -2).tainted?.should == str.tainted?
+
+ tainted_re = /(.)(.)(.)/
+ tainted_re.taint
+
+ str.send(@method, tainted_re, 0).tainted?.should == true
+ str.send(@method, tainted_re, 1).tainted?.should == true
+ str.send(@method, tainted_re, -1).tainted?.should == true
+ end
+ end
+
+ not_supported_on :opal do
+ it "returns an untrusted string if the regexp is untrusted" do
+ "hello".send(@method, /(.)/.untrust, 1).untrusted?.should be_true
+ end
+ end
+
+ it "returns nil if there is no match" do
+ "hello there".send(@method, /(what?)/, 1).should == nil
+ end
+
+ it "returns nil if there is no capture for the given index" do
+ "hello there".send(@method, /[aeiou](.)\1/, 2).should == nil
+ # You can't refer to 0 using negative indices
+ "hello there".send(@method, /[aeiou](.)\1/, -2).should == nil
+ end
+
+ it "calls to_int on the given index" do
+ obj = mock('2')
+ obj.should_receive(:to_int).and_return(2)
+
+ "har".send(@method, /(.)(.)(.)/, 1.5).should == "h"
+ "har".send(@method, /(.)(.)(.)/, obj).should == "a"
+ end
+
+ it "raises a TypeError when the given index can't be converted to Integer" do
+ lambda { "hello".send(@method, /(.)(.)(.)/, mock('x')) }.should raise_error(TypeError)
+ lambda { "hello".send(@method, /(.)(.)(.)/, {}) }.should raise_error(TypeError)
+ lambda { "hello".send(@method, /(.)(.)(.)/, []) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when the given index is nil" do
+ lambda { "hello".send(@method, /(.)(.)(.)/, nil) }.should raise_error(TypeError)
+ end
+
+ it "returns subclass instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.send(@method, /(.)(.)/, 0).should be_an_instance_of(StringSpecs::MyString)
+ s.send(@method, /(.)(.)/, 1).should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ it "sets $~ to MatchData when there is a match and nil when there's none" do
+ 'hello'.send(@method, /.(.)/, 0)
+ $~[0].should == 'he'
+
+ 'hello'.send(@method, /.(.)/, 1)
+ $~[1].should == 'e'
+
+ 'hello'.send(@method, /not/, 0)
+ $~.should == nil
+ end
+end
+
+describe :string_slice_string, shared: true do
+ it "returns other_str if it occurs in self" do
+ s = "lo"
+ "hello there".send(@method, s).should == s
+ end
+
+ it "taints resulting strings when other is tainted" do
+ strs = ["", "hello world", "hello"]
+ strs += strs.map { |s| s.dup.taint }
+
+ strs.each do |str|
+ strs.each do |other|
+ r = str.send(@method, other)
+
+ r.tainted?.should == !r.nil? & other.tainted?
+ end
+ end
+ end
+
+ it "doesn't set $~" do
+ $~ = nil
+
+ 'hello'.send(@method, 'll')
+ $~.should == nil
+ end
+
+ it "returns nil if there is no match" do
+ "hello there".send(@method, "bye").should == nil
+ end
+
+ it "doesn't call to_str on its argument" do
+ o = mock('x')
+ o.should_not_receive(:to_str)
+
+ lambda { "hello".send(@method, o) }.should raise_error(TypeError)
+ end
+
+ it "returns a subclass instance when given a subclass instance" do
+ s = StringSpecs::MyString.new("el")
+ r = "hello".send(@method, s)
+ r.should == "el"
+ r.should be_an_instance_of(StringSpecs::MyString)
+ end
+end
+
+describe :string_slice_regexp_group, shared: true do
+ not_supported_on :opal do
+ it "returns the capture for the given name" do
+ "hello there".send(@method, /(?<g>[aeiou](.))/, 'g').should == "el"
+ "hello there".send(@method, /[aeiou](?<g>.)/, 'g').should == "l"
+
+ "har".send(@method, /(?<g>(.)(.)(.))/, 'g').should == "har"
+ "har".send(@method, /(?<h>.)(.)(.)/, 'h').should == "h"
+ "har".send(@method, /(.)(?<a>.)(.)/, 'a').should == "a"
+ "har".send(@method, /(.)(.)(?<r>.)/, 'r').should == "r"
+ "har".send(@method, /(?<h>.)(?<a>.)(?<r>.)/, 'r').should == "r"
+ end
+
+ it "returns the last capture for duplicate names" do
+ "hello there".send(@method, /(?<g>h)(?<g>.)/, 'g').should == "e"
+ "hello there".send(@method, /(?<g>h)(?<g>.)(?<f>.)/, 'g').should == "e"
+ end
+
+ it "returns the innermost capture for nested duplicate names" do
+ "hello there".send(@method, /(?<g>h(?<g>.))/, 'g').should == "e"
+ end
+
+ it "always taints resulting strings when self or regexp is tainted" do
+ strs = ["hello world"]
+ strs += strs.map { |s| s.dup.taint }
+
+ strs.each do |str|
+ str.send(@method, /(?<hi>hello)/, 'hi').tainted?.should == str.tainted?
+
+ str.send(@method, /(?<g>(.)(.)(.))/, 'g').tainted?.should == str.tainted?
+ str.send(@method, /(?<h>.)(.)(.)/, 'h').tainted?.should == str.tainted?
+ str.send(@method, /(.)(?<a>.)(.)/, 'a').tainted?.should == str.tainted?
+ str.send(@method, /(.)(.)(?<r>.)/, 'r').tainted?.should == str.tainted?
+ str.send(@method, /(?<h>.)(?<a>.)(?<r>.)/, 'r').tainted?.should == str.tainted?
+
+ tainted_re = /(?<a>.)(?<b>.)(?<c>.)/
+ tainted_re.taint
+
+ str.send(@method, tainted_re, 'a').tainted?.should be_true
+ str.send(@method, tainted_re, 'b').tainted?.should be_true
+ str.send(@method, tainted_re, 'c').tainted?.should be_true
+ end
+ end
+
+ it "returns nil if there is no match" do
+ "hello there".send(@method, /(?<whut>what?)/, 'whut').should be_nil
+ end
+
+ it "raises an IndexError if there is no capture for the given name" do
+ lambda do
+ "hello there".send(@method, /[aeiou](.)\1/, 'non')
+ end.should raise_error(IndexError)
+ end
+
+ it "raises a TypeError when the given name is not a String" do
+ lambda { "hello".send(@method, /(?<q>.)/, mock('x')) }.should raise_error(TypeError)
+ lambda { "hello".send(@method, /(?<q>.)/, {}) }.should raise_error(TypeError)
+ lambda { "hello".send(@method, /(?<q>.)/, []) }.should raise_error(TypeError)
+ end
+
+ it "raises an IndexError when given the empty String as a group name" do
+ lambda { "hello".send(@method, /(?<q>)/, '') }.should raise_error(IndexError)
+ end
+
+ it "returns subclass instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.send(@method, /(?<q>.)/, 'q').should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ it "sets $~ to MatchData when there is a match and nil when there's none" do
+ 'hello'.send(@method, /(?<hi>.(.))/, 'hi')
+ $~[0].should == 'he'
+
+ 'hello'.send(@method, /(?<non>not)/, 'non')
+ $~.should be_nil
+ end
+ end
+end
+
+describe :string_slice_symbol, shared: true do
+ it "raises TypeError" do
+ lambda { 'hello'.send(@method, :hello) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/string/shared/succ.rb b/spec/ruby/core/string/shared/succ.rb
new file mode 100644
index 0000000000..0ab659864d
--- /dev/null
+++ b/spec/ruby/core/string/shared/succ.rb
@@ -0,0 +1,88 @@
+# -*- encoding: binary -*-
+describe :string_succ, shared: true do
+ it "returns an empty string for empty strings" do
+ "".send(@method).should == ""
+ end
+
+ it "returns the successor by increasing the rightmost alphanumeric (digit => digit, letter => letter with same case)" do
+ "abcd".send(@method).should == "abce"
+ "THX1138".send(@method).should == "THX1139"
+
+ "<<koala>>".send(@method).should == "<<koalb>>"
+ "==A??".send(@method).should == "==B??"
+ end
+
+ it "increases non-alphanumerics (via ascii rules) if there are no alphanumerics" do
+ "***".send(@method).should == "**+"
+ "**`".send(@method).should == "**a"
+ end
+
+ it "increases the next best alphanumeric (jumping over non-alphanumerics) if there is a carry" do
+ "dz".send(@method).should == "ea"
+ "HZ".send(@method).should == "IA"
+ "49".send(@method).should == "50"
+
+ "izz".send(@method).should == "jaa"
+ "IZZ".send(@method).should == "JAA"
+ "699".send(@method).should == "700"
+
+ "6Z99z99Z".send(@method).should == "7A00a00A"
+
+ "1999zzz".send(@method).should == "2000aaa"
+ "NZ/[]ZZZ9999".send(@method).should == "OA/[]AAA0000"
+ end
+
+ it "increases the next best character if there is a carry for non-alphanumerics" do
+ "(\xFF".send(@method).should == ")\x00"
+ "`\xFF".send(@method).should == "a\x00"
+ "<\xFF\xFF".send(@method).should == "=\x00\x00"
+ end
+
+ it "adds an additional character (just left to the last increased one) if there is a carry and no character left to increase" do
+ "z".send(@method).should == "aa"
+ "Z".send(@method).should == "AA"
+ "9".send(@method).should == "10"
+
+ "zz".send(@method).should == "aaa"
+ "ZZ".send(@method).should == "AAA"
+ "99".send(@method).should == "100"
+
+ "9Z99z99Z".send(@method).should == "10A00a00A"
+
+ "ZZZ9999".send(@method).should == "AAAA0000"
+ "/[]9999".send(@method).should == "/[]10000"
+ "/[]ZZZ9999".send(@method).should == "/[]AAAA0000"
+ "Z/[]ZZZ9999".send(@method).should == "AA/[]AAA0000"
+
+ # non-alphanumeric cases
+ "\xFF".send(@method).should == "\x01\x00"
+ "\xFF\xFF".send(@method).should == "\x01\x00\x00"
+ end
+
+ it "returns subclass instances when called on a subclass" do
+ StringSpecs::MyString.new("").send(@method).should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("a").send(@method).should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("z").send(@method).should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ it "taints the result if self is tainted" do
+ ["", "a", "z", "Z", "9", "\xFF", "\xFF\xFF"].each do |s|
+ s.taint.send(@method).tainted?.should == true
+ end
+ end
+end
+
+describe :string_succ_bang, shared: true do
+ it "is equivalent to succ, but modifies self in place (still returns self)" do
+ ["", "abcd", "THX1138"].each do |s|
+ r = s.dup.send(@method)
+ s.send(@method).should equal(s)
+ s.should == r
+ end
+ end
+
+ it "raises a #{frozen_error_class} if self is frozen" do
+ lambda { "".freeze.send(@method) }.should raise_error(frozen_error_class)
+ lambda { "abcd".freeze.send(@method) }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/string/shared/to_a.rb b/spec/ruby/core/string/shared/to_a.rb
new file mode 100644
index 0000000000..bad3ea6584
--- /dev/null
+++ b/spec/ruby/core/string/shared/to_a.rb
@@ -0,0 +1,9 @@
+describe :string_to_a, shared: true do
+ it "returns an empty array for empty strings" do
+ "".send(@method).should == []
+ end
+
+ it "returns an array containing the string for non-empty strings" do
+ "hello".send(@method).should == ["hello"]
+ end
+end
diff --git a/spec/ruby/core/string/shared/to_s.rb b/spec/ruby/core/string/shared/to_s.rb
new file mode 100644
index 0000000000..a5a13e4f26
--- /dev/null
+++ b/spec/ruby/core/string/shared/to_s.rb
@@ -0,0 +1,18 @@
+describe :string_to_s, shared: true do
+ it "returns self when self.class == String" do
+ a = "a string"
+ a.should equal(a.send(@method))
+ end
+
+ it "returns a new instance of String when called on a subclass" do
+ a = StringSpecs::MyString.new("a string")
+ s = a.send(@method)
+ s.should == "a string"
+ s.should be_an_instance_of(String)
+ end
+
+ it "taints the result when self is tainted" do
+ "x".taint.send(@method).tainted?.should == true
+ StringSpecs::MyString.new("x").taint.send(@method).tainted?.should == true
+ end
+end
diff --git a/spec/ruby/core/string/shared/to_sym.rb b/spec/ruby/core/string/shared/to_sym.rb
new file mode 100644
index 0000000000..1180d64712
--- /dev/null
+++ b/spec/ruby/core/string/shared/to_sym.rb
@@ -0,0 +1,63 @@
+describe :string_to_sym, shared: true do
+ it "returns the symbol corresponding to self" do
+ "Koala".send(@method).should equal :Koala
+ 'cat'.send(@method).should equal :cat
+ '@cat'.send(@method).should equal :@cat
+ 'cat and dog'.send(@method).should equal :"cat and dog"
+ "abc=".send(@method).should equal :abc=
+ end
+
+ it "does not special case +(binary) and -(binary)" do
+ "+(binary)".send(@method).should equal :"+(binary)"
+ "-(binary)".send(@method).should equal :"-(binary)"
+ end
+
+ it "does not special case certain operators" do
+ "!@".send(@method).should equal :"!@"
+ "~@".send(@method).should equal :"~@"
+ "!(unary)".send(@method).should equal :"!(unary)"
+ "~(unary)".send(@method).should equal :"~(unary)"
+ "+(unary)".send(@method).should equal :"+(unary)"
+ "-(unary)".send(@method).should equal :"-(unary)"
+ end
+
+ it "returns a US-ASCII Symbol for a UTF-8 String containing only US-ASCII characters" do
+ sym = "foobar".send(@method)
+ sym.encoding.should == Encoding::US_ASCII
+ sym.should equal :"foobar"
+ end
+
+ it "returns a US-ASCII Symbol for a binary String containing only US-ASCII characters" do
+ sym = "foobar".b.send(@method)
+ sym.encoding.should == Encoding::US_ASCII
+ sym.should equal :"foobar"
+ end
+
+ it "returns a UTF-8 Symbol for a UTF-8 String containing non US-ASCII characters" do
+ sym = "il était une fois".send(@method)
+ sym.encoding.should == Encoding::UTF_8
+ sym.should equal :"il était une #{'fois'}"
+ end
+
+ it "returns a UTF-16LE Symbol for a UTF-16LE String containing non US-ASCII characters" do
+ utf16_str = "UtéF16".encode(Encoding::UTF_16LE)
+ sym = utf16_str.send(@method)
+ sym.encoding.should == Encoding::UTF_16LE
+ sym.to_s.should == utf16_str
+ end
+
+ it "returns a binary Symbol for a binary String containing non US-ASCII characters" do
+ binary_string = "binarí".b
+ sym = binary_string.send(@method)
+ sym.encoding.should == Encoding::BINARY
+ sym.to_s.should == binary_string
+ end
+
+ it "raises an EncodingError for UTF-8 String containing invalid bytes" do
+ invalid_utf8 = "\xC3"
+ invalid_utf8.valid_encoding?.should == false
+ -> {
+ invalid_utf8.send(@method)
+ }.should raise_error(EncodingError, /invalid/)
+ end
+end
diff --git a/spec/ruby/core/string/size_spec.rb b/spec/ruby/core/string/size_spec.rb
new file mode 100644
index 0000000000..9e1f40c5ae
--- /dev/null
+++ b/spec/ruby/core/string/size_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/length'
+
+describe "String#size" do
+ it_behaves_like :string_length, :size
+end
diff --git a/spec/ruby/core/string/slice_spec.rb b/spec/ruby/core/string/slice_spec.rb
new file mode 100644
index 0000000000..a3e9d6b74f
--- /dev/null
+++ b/spec/ruby/core/string/slice_spec.rb
@@ -0,0 +1,476 @@
+# -*- encoding: utf-8 -*-
+
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/slice'
+
+describe "String#slice" do
+ it_behaves_like :string_slice, :slice
+end
+
+describe "String#slice with index, length" do
+ it_behaves_like :string_slice_index_length, :slice
+end
+
+describe "String#slice with Range" do
+ it_behaves_like :string_slice_range, :slice
+end
+
+describe "String#slice with Regexp" do
+ it_behaves_like :string_slice_regexp, :slice
+end
+
+describe "String#slice with Regexp, index" do
+ it_behaves_like :string_slice_regexp_index, :slice
+end
+
+describe "String#slice with Regexp, group" do
+ it_behaves_like :string_slice_regexp_group, :slice
+end
+
+describe "String#slice with String" do
+ it_behaves_like :string_slice_string, :slice
+end
+
+describe "String#slice with Symbol" do
+ it_behaves_like :string_slice_symbol, :slice
+end
+
+describe "String#slice! with index" do
+ it "deletes and return the char at the given position" do
+ a = "hello"
+ a.slice!(1).should == ?e
+ a.should == "hllo"
+ a.slice!(-1).should == ?o
+ a.should == "hll"
+ end
+
+ it "returns nil if idx is outside of self" do
+ a = "hello"
+ a.slice!(20).should == nil
+ a.should == "hello"
+ a.slice!(-20).should == nil
+ a.should == "hello"
+ end
+
+ it "raises a #{frozen_error_class} if self is frozen" do
+ lambda { "hello".freeze.slice!(1) }.should raise_error(frozen_error_class)
+ lambda { "hello".freeze.slice!(10) }.should raise_error(frozen_error_class)
+ lambda { "".freeze.slice!(0) }.should raise_error(frozen_error_class)
+ end
+
+ it "calls to_int on index" do
+ "hello".slice!(0.5).should == ?h
+
+ obj = mock('1')
+ obj.should_receive(:to_int).at_least(1).and_return(1)
+ "hello".slice!(obj).should == ?e
+
+ obj = mock('1')
+ obj.should_receive(:respond_to?).at_least(1).with(:to_int, true).and_return(true)
+ obj.should_receive(:method_missing).at_least(1).with(:to_int).and_return(1)
+ "hello".slice!(obj).should == ?e
+ end
+
+ with_feature :encoding do
+
+ it "returns the character given by the character index" do
+ "hellö there".slice!(1).should == "e"
+ "hellö there".slice!(4).should == "ö"
+ "hellö there".slice!(6).should == "t"
+ end
+
+ end
+end
+
+describe "String#slice! with index, length" do
+ it "deletes and returns the substring at idx and the given length" do
+ a = "hello"
+ a.slice!(1, 2).should == "el"
+ a.should == "hlo"
+
+ a.slice!(1, 0).should == ""
+ a.should == "hlo"
+
+ a.slice!(-2, 4).should == "lo"
+ a.should == "h"
+ end
+
+ it "always taints resulting strings when self is tainted" do
+ str = "hello world"
+ str.taint
+
+ str.slice!(0, 0).tainted?.should == true
+ str.slice!(2, 1).tainted?.should == true
+ end
+
+ it "returns nil if the given position is out of self" do
+ a = "hello"
+ a.slice(10, 3).should == nil
+ a.should == "hello"
+
+ a.slice(-10, 20).should == nil
+ a.should == "hello"
+ end
+
+ it "returns nil if the length is negative" do
+ a = "hello"
+ a.slice(4, -3).should == nil
+ a.should == "hello"
+ end
+
+ it "raises a #{frozen_error_class} if self is frozen" do
+ lambda { "hello".freeze.slice!(1, 2) }.should raise_error(frozen_error_class)
+ lambda { "hello".freeze.slice!(10, 3) }.should raise_error(frozen_error_class)
+ lambda { "hello".freeze.slice!(-10, 3)}.should raise_error(frozen_error_class)
+ lambda { "hello".freeze.slice!(4, -3) }.should raise_error(frozen_error_class)
+ lambda { "hello".freeze.slice!(10, 3) }.should raise_error(frozen_error_class)
+ lambda { "hello".freeze.slice!(-10, 3)}.should raise_error(frozen_error_class)
+ lambda { "hello".freeze.slice!(4, -3) }.should raise_error(frozen_error_class)
+ end
+
+ it "calls to_int on idx and length" do
+ "hello".slice!(0.5, 2.5).should == "he"
+
+ obj = mock('2')
+ def obj.to_int() 2 end
+ "hello".slice!(obj, obj).should == "ll"
+
+ obj = mock('2')
+ def obj.respond_to?(name, *) name == :to_int; end
+ def obj.method_missing(name, *) name == :to_int ? 2 : super; end
+ "hello".slice!(obj, obj).should == "ll"
+ end
+
+ it "returns subclass instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.slice!(0, 0).should be_an_instance_of(StringSpecs::MyString)
+ s.slice!(0, 4).should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ with_feature :encoding do
+
+ it "returns the substring given by the character offsets" do
+ "hellö there".slice!(1,0).should == ""
+ "hellö there".slice!(1,3).should == "ell"
+ "hellö there".slice!(1,6).should == "ellö t"
+ "hellö there".slice!(1,9).should == "ellö ther"
+ end
+
+ it "treats invalid bytes as single bytes" do
+ xE6xCB = [0xE6,0xCB].pack('CC').force_encoding('utf-8')
+ "a#{xE6xCB}b".slice!(1, 2).should == xE6xCB
+ end
+ end
+end
+
+describe "String#slice! Range" do
+ it "deletes and return the substring given by the offsets of the range" do
+ a = "hello"
+ a.slice!(1..3).should == "ell"
+ a.should == "ho"
+ a.slice!(0..0).should == "h"
+ a.should == "o"
+ a.slice!(0...0).should == ""
+ a.should == "o"
+
+ # Edge Case?
+ "hello".slice!(-3..-9).should == ""
+ end
+
+ it "returns nil if the given range is out of self" do
+ a = "hello"
+ a.slice!(-6..-9).should == nil
+ a.should == "hello"
+
+ b = "hello"
+ b.slice!(10..20).should == nil
+ b.should == "hello"
+ end
+
+ it "always taints resulting strings when self is tainted" do
+ str = "hello world"
+ str.taint
+
+ str.slice!(0..0).tainted?.should == true
+ str.slice!(2..3).tainted?.should == true
+ end
+
+ it "returns subclass instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.slice!(0...0).should be_an_instance_of(StringSpecs::MyString)
+ s.slice!(0..4).should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ it "calls to_int on range arguments" do
+ from = mock('from')
+ to = mock('to')
+
+ # So we can construct a range out of them...
+ def from.<=>(o) 0 end
+ def to.<=>(o) 0 end
+
+ def from.to_int() 1 end
+ def to.to_int() -2 end
+
+ "hello there".slice!(from..to).should == "ello ther"
+
+ from = mock('from')
+ to = mock('to')
+
+ def from.<=>(o) 0 end
+ def to.<=>(o) 0 end
+
+ def from.respond_to?(name, *) name == :to_int; end
+ def from.method_missing(name) name == :to_int ? 1 : super; end
+ def to.respond_to?(name, *) name == :to_int; end
+ def to.method_missing(name) name == :to_int ? -2 : super; end
+
+ "hello there".slice!(from..to).should == "ello ther"
+ end
+
+ it "works with Range subclasses" do
+ a = "GOOD"
+ range_incl = StringSpecs::MyRange.new(1, 2)
+
+ a.slice!(range_incl).should == "OO"
+ end
+
+ with_feature :encoding do
+
+ it "returns the substring given by the character offsets of the range" do
+ "hellö there".slice!(1..1).should == "e"
+ "hellö there".slice!(1..3).should == "ell"
+ "hellö there".slice!(1...3).should == "el"
+ "hellö there".slice!(-4..-2).should == "her"
+ "hellö there".slice!(-4...-2).should == "he"
+ "hellö there".slice!(5..-1).should == " there"
+ "hellö there".slice!(5...-1).should == " ther"
+ end
+
+ end
+
+ it "raises a #{frozen_error_class} on a frozen instance that is modified" do
+ lambda { "hello".freeze.slice!(1..3) }.should raise_error(frozen_error_class)
+ end
+
+ # see redmine #1551
+ it "raises a #{frozen_error_class} on a frozen instance that would not be modified" do
+ lambda { "hello".freeze.slice!(10..20)}.should raise_error(frozen_error_class)
+ end
+end
+
+describe "String#slice! with Regexp" do
+ it "deletes and returns the first match from self" do
+ s = "this is a string"
+ s.slice!(/s.*t/).should == 's is a st'
+ s.should == 'thiring'
+
+ c = "hello hello"
+ c.slice!(/llo/).should == "llo"
+ c.should == "he hello"
+ end
+
+ it "returns nil if there was no match" do
+ s = "this is a string"
+ s.slice!(/zzz/).should == nil
+ s.should == "this is a string"
+ end
+
+ it "always taints resulting strings when self or regexp is tainted" do
+ strs = ["hello world"]
+ strs += strs.map { |s| s.dup.taint }
+
+ strs.each do |str|
+ str = str.dup
+ str.slice!(//).tainted?.should == str.tainted?
+ str.slice!(/hello/).tainted?.should == str.tainted?
+
+ tainted_re = /./
+ tainted_re.taint
+
+ str.slice!(tainted_re).tainted?.should == true
+ end
+ end
+
+ it "doesn't taint self when regexp is tainted" do
+ s = "hello"
+ s.slice!(/./.taint)
+ s.tainted?.should == false
+ end
+
+ it "returns subclass instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.slice!(//).should be_an_instance_of(StringSpecs::MyString)
+ s.slice!(/../).should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ with_feature :encoding do
+ it "returns the matching portion of self with a multi byte character" do
+ "hëllo there".slice!(/[ë](.)\1/).should == "ëll"
+ "".slice!(//).should == ""
+ end
+ end
+
+ it "sets $~ to MatchData when there is a match and nil when there's none" do
+ 'hello'.slice!(/./)
+ $~[0].should == 'h'
+
+ 'hello'.slice!(/not/)
+ $~.should == nil
+ end
+
+ it "raises a #{frozen_error_class} on a frozen instance that is modified" do
+ lambda { "this is a string".freeze.slice!(/s.*t/) }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} on a frozen instance that would not be modified" do
+ lambda { "this is a string".freeze.slice!(/zzz/) }.should raise_error(frozen_error_class)
+ end
+end
+
+describe "String#slice! with Regexp, index" do
+ it "deletes and returns the capture for idx from self" do
+ str = "hello there"
+ str.slice!(/[aeiou](.)\1/, 0).should == "ell"
+ str.should == "ho there"
+ str.slice!(/(t)h/, 1).should == "t"
+ str.should == "ho here"
+ end
+
+ it "always taints resulting strings when self or regexp is tainted" do
+ strs = ["hello world"]
+ strs += strs.map { |s| s.dup.taint }
+
+ strs.each do |str|
+ str = str.dup
+ str.slice!(//, 0).tainted?.should == str.tainted?
+ str.slice!(/hello/, 0).tainted?.should == str.tainted?
+
+ tainted_re = /(.)(.)(.)/
+ tainted_re.taint
+
+ str.slice!(tainted_re, 1).tainted?.should == true
+ end
+ end
+
+ it "doesn't taint self when regexp is tainted" do
+ s = "hello"
+ s.slice!(/(.)(.)/.taint, 1)
+ s.tainted?.should == false
+ end
+
+ it "returns nil if there was no match" do
+ s = "this is a string"
+ s.slice!(/x(zzz)/, 1).should == nil
+ s.should == "this is a string"
+ end
+
+ it "returns nil if there is no capture for idx" do
+ "hello there".slice!(/[aeiou](.)\1/, 2).should == nil
+ # You can't refer to 0 using negative indices
+ "hello there".slice!(/[aeiou](.)\1/, -2).should == nil
+ end
+
+ it "accepts a Float for capture index" do
+ "har".slice!(/(.)(.)(.)/, 1.5).should == "h"
+ end
+
+ it "calls #to_int to convert an Object to capture index" do
+ obj = mock('2')
+ obj.should_receive(:to_int).at_least(1).times.and_return(2)
+
+ "har".slice!(/(.)(.)(.)/, obj).should == "a"
+ end
+
+ it "returns subclass instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.slice!(/(.)(.)/, 0).should be_an_instance_of(StringSpecs::MyString)
+ s.slice!(/(.)(.)/, 1).should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ with_feature :encoding do
+ it "returns the encoding aware capture for the given index" do
+ "hår".slice!(/(.)(.)(.)/, 0).should == "hår"
+ "hår".slice!(/(.)(.)(.)/, 1).should == "h"
+ "hår".slice!(/(.)(.)(.)/, 2).should == "å"
+ "hår".slice!(/(.)(.)(.)/, 3).should == "r"
+ "hår".slice!(/(.)(.)(.)/, -1).should == "r"
+ "hår".slice!(/(.)(.)(.)/, -2).should == "å"
+ "hår".slice!(/(.)(.)(.)/, -3).should == "h"
+ end
+ end
+
+ it "sets $~ to MatchData when there is a match and nil when there's none" do
+ 'hello'[/.(.)/, 0]
+ $~[0].should == 'he'
+
+ 'hello'[/.(.)/, 1]
+ $~[1].should == 'e'
+
+ 'hello'[/not/, 0]
+ $~.should == nil
+ end
+
+ it "raises a #{frozen_error_class} if self is frozen" do
+ lambda { "this is a string".freeze.slice!(/s.*t/) }.should raise_error(frozen_error_class)
+ lambda { "this is a string".freeze.slice!(/zzz/, 0)}.should raise_error(frozen_error_class)
+ lambda { "this is a string".freeze.slice!(/(.)/, 2)}.should raise_error(frozen_error_class)
+ end
+end
+
+describe "String#slice! with String" do
+ it "removes and returns the first occurrence of other_str from self" do
+ c = "hello hello"
+ c.slice!('llo').should == "llo"
+ c.should == "he hello"
+ end
+
+ it "taints resulting strings when other is tainted" do
+ strs = ["", "hello world", "hello"]
+ strs += strs.map { |s| s.dup.taint }
+
+ strs.each do |str|
+ str = str.dup
+ strs.each do |other|
+ other = other.dup
+ r = str.slice!(other)
+
+ r.tainted?.should == !r.nil? & other.tainted?
+ end
+ end
+ end
+
+ it "doesn't set $~" do
+ $~ = nil
+
+ 'hello'.slice!('ll')
+ $~.should == nil
+ end
+
+ it "returns nil if self does not contain other" do
+ a = "hello"
+ a.slice!('zzz').should == nil
+ a.should == "hello"
+ end
+
+ it "doesn't call to_str on its argument" do
+ o = mock('x')
+ o.should_not_receive(:to_str)
+
+ lambda { "hello".slice!(o) }.should raise_error(TypeError)
+ end
+
+ it "returns a subclass instance when given a subclass instance" do
+ s = StringSpecs::MyString.new("el")
+ r = "hello".slice!(s)
+ r.should == "el"
+ r.should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ it "raises a #{frozen_error_class} if self is frozen" do
+ lambda { "hello hello".freeze.slice!('llo') }.should raise_error(frozen_error_class)
+ lambda { "this is a string".freeze.slice!('zzz')}.should raise_error(frozen_error_class)
+ lambda { "this is a string".freeze.slice!('zzz')}.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/string/split_spec.rb b/spec/ruby/core/string/split_spec.rb
new file mode 100644
index 0000000000..b89a28c149
--- /dev/null
+++ b/spec/ruby/core/string/split_spec.rb
@@ -0,0 +1,415 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#split with String" do
+ with_feature :encoding do
+ it "throws an ArgumentError if the pattern is not a valid string" do
+ str = 'проверка'
+ broken_str = 'проверка'
+ broken_str.force_encoding('binary')
+ broken_str.chop!
+ broken_str.force_encoding('utf-8')
+ lambda { str.split(broken_str) }.should raise_error(ArgumentError)
+ end
+
+ it "splits on multibyte characters" do
+ "ã‚りãŒã‚ŠãŒã¨ã†".split("ãŒ").should == ["ã‚り", "り", "ã¨ã†"]
+ end
+ end
+
+ it "returns an array of substrings based on splitting on the given string" do
+ "mellow yellow".split("ello").should == ["m", "w y", "w"]
+ end
+
+ it "suppresses trailing empty fields when limit isn't given or 0" 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"]
+ "hai".split("hai").should == []
+ ",".split(",").should == []
+ ",".split(",", 0).should == []
+ end
+
+ it "returns an array with one entry if limit is 1: the original string" do
+ "hai".split("hai", 1).should == ["hai"]
+ "x.y.z".split(".", 1).should == ["x.y.z"]
+ "hello world ".split(" ", 1).should == ["hello world "]
+ "hi!".split("", 1).should == ["hi!"]
+ end
+
+ it "returns at most limit fields when limit > 1" do
+ "hai".split("hai", 2).should == ["", ""]
+
+ "1,2".split(",", 3).should == ["1", "2"]
+
+ "1,2,,3,4,,".split(',', 2).should == ["1", "2,,3,4,,"]
+ "1,2,,3,4,,".split(',', 3).should == ["1", "2", ",3,4,,"]
+ "1,2,,3,4,,".split(',', 4).should == ["1", "2", "", "3,4,,"]
+ "1,2,,3,4,,".split(',', 5).should == ["1", "2", "", "3", "4,,"]
+ "1,2,,3,4,,".split(',', 6).should == ["1", "2", "", "3", "4", ","]
+
+ "x".split('x', 2).should == ["", ""]
+ "xx".split('x', 2).should == ["", "x"]
+ "xx".split('x', 3).should == ["", "", ""]
+ "xxx".split('x', 2).should == ["", "xx"]
+ "xxx".split('x', 3).should == ["", "", "x"]
+ "xxx".split('x', 4).should == ["", "", "", ""]
+ end
+
+ it "doesn't suppress or limit fields when limit is negative" do
+ "1,2,,3,4,,".split(',', -1).should == ["1", "2", "", "3", "4", "", ""]
+ "1,2,,3,4,,".split(',', -5).should == ["1", "2", "", "3", "4", "", ""]
+ " a b c\nd ".split(" ", -1).should == ["", "a", "b", "c\nd", ""]
+ ",".split(",", -1).should == ["", ""]
+ end
+
+ it "defaults to $; when string isn't given or nil" do
+ begin
+ old_fs = $;
+
+ [",", ":", "", "XY", nil].each do |fs|
+ $; = fs
+
+ ["x,y,z,,,", "1:2:", "aXYbXYcXY", ""].each do |str|
+ expected = str.split(fs || " ")
+
+ str.split(nil).should == expected
+ str.split.should == expected
+
+ str.split(nil, -1).should == str.split(fs || " ", -1)
+ str.split(nil, 0).should == str.split(fs || " ", 0)
+ str.split(nil, 2).should == str.split(fs || " ", 2)
+ end
+ end
+ ensure
+ $; = old_fs
+ end
+ end
+
+ it "ignores leading and continuous whitespace when string is a single space" do
+ " now's the time ".split(' ').should == ["now's", "the", "time"]
+ " now's the time ".split(' ', -1).should == ["now's", "the", "time", ""]
+ " now's the time ".split(' ', 3).should == ["now's", "the", "time "]
+
+ "\t\n a\t\tb \n\r\r\nc\v\vd\v ".split(' ').should == ["a", "b", "c", "d"]
+ "a\x00a b".split(' ').should == ["a\x00a", "b"]
+ end
+
+ describe "when limit is zero" do
+ it "ignores leading and continuous whitespace when string is a single space" do
+ " now's the time ".split(' ', 0).should == ["now's", "the", "time"]
+ end
+ end
+
+ it "splits between characters when its argument is an empty string" do
+ "hi!".split("").should == ["h", "i", "!"]
+ "hi!".split("", -1).should == ["h", "i", "!", ""]
+ "hi!".split("", 0).should == ["h", "i", "!"]
+ "hi!".split("", 1).should == ["hi!"]
+ "hi!".split("", 2).should == ["h", "i!"]
+ "hi!".split("", 3).should == ["h", "i", "!"]
+ "hi!".split("", 4).should == ["h", "i", "!", ""]
+ "hi!".split("", 5).should == ["h", "i", "!", ""]
+ end
+
+ it "tries converting its pattern argument to a string via to_str" do
+ obj = mock('::')
+ obj.should_receive(:to_str).and_return("::")
+
+ "hello::world".split(obj).should == ["hello", "world"]
+ end
+
+ it "tries converting limit to an integer via to_int" do
+ obj = mock('2')
+ obj.should_receive(:to_int).and_return(2)
+
+ "1.2.3.4".split(".", obj).should == ["1", "2.3.4"]
+ end
+
+ it "doesn't set $~" do
+ $~ = nil
+ "x.y.z".split(".")
+ $~.should == nil
+ end
+
+ it "returns the original string if no matches are found" do
+ "foo".split("bar").should == ["foo"]
+ "foo".split("bar", -1).should == ["foo"]
+ "foo".split("bar", 0).should == ["foo"]
+ "foo".split("bar", 1).should == ["foo"]
+ "foo".split("bar", 2).should == ["foo"]
+ "foo".split("bar", 3).should == ["foo"]
+ end
+
+ it "returns subclass instances based on self" do
+ ["", "x.y.z.", " x y "].each do |str|
+ ["", ".", " "].each do |pat|
+ [-1, 0, 1, 2].each do |limit|
+ StringSpecs::MyString.new(str).split(pat, limit).each do |x|
+ x.should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ str.split(StringSpecs::MyString.new(pat), limit).each do |x|
+ x.should be_an_instance_of(String)
+ end
+ end
+ end
+ end
+ end
+
+ it "does not call constructor on created subclass instances" do
+ # can't call should_not_receive on an object that doesn't yet exist
+ # so failure here is signalled by exception, not expectation failure
+
+ s = StringSpecs::StringWithRaisingConstructor.new('silly:string')
+ s.split(':').first.should == 'silly'
+ end
+
+ it "taints the resulting strings if self is tainted" do
+ ["", "x.y.z.", " x y "].each do |str|
+ ["", ".", " "].each do |pat|
+ [-1, 0, 1, 2].each do |limit|
+ str.dup.taint.split(pat).each do |x|
+ x.tainted?.should == true
+ end
+
+ str.split(pat.dup.taint).each do |x|
+ x.tainted?.should == false
+ end
+ end
+ end
+ end
+ end
+end
+
+describe "String#split with Regexp" do
+ it "divides self on regexp matches" do
+ " now's the time".split(/ /).should == ["", "now's", "", "the", "time"]
+ " x\ny ".split(/ /).should == ["", "x\ny"]
+ "1, 2.34,56, 7".split(/,\s*/).should == ["1", "2.34", "56", "7"]
+ "1x2X3".split(/x/i).should == ["1", "2", "3"]
+ end
+
+ it "treats negative limits as no limit" do
+ "".split(%r!/+!, -1).should == []
+ end
+
+ it "suppresses trailing empty fields when limit isn't given or 0" 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(/\s+/).should == ["", "a", "b", "c", "d"]
+ "hai".split(/hai/).should == []
+ ",".split(/,/).should == []
+ ",".split(/,/, 0).should == []
+ end
+
+ it "returns an array with one entry if limit is 1: the original string" do
+ "hai".split(/hai/, 1).should == ["hai"]
+ "xAyBzC".split(/[A-Z]/, 1).should == ["xAyBzC"]
+ "hello world ".split(/\s+/, 1).should == ["hello world "]
+ "hi!".split(//, 1).should == ["hi!"]
+ end
+
+ it "returns at most limit fields when limit > 1" do
+ "hai".split(/hai/, 2).should == ["", ""]
+
+ "1,2".split(/,/, 3).should == ["1", "2"]
+
+ "1,2,,3,4,,".split(/,/, 2).should == ["1", "2,,3,4,,"]
+ "1,2,,3,4,,".split(/,/, 3).should == ["1", "2", ",3,4,,"]
+ "1,2,,3,4,,".split(/,/, 4).should == ["1", "2", "", "3,4,,"]
+ "1,2,,3,4,,".split(/,/, 5).should == ["1", "2", "", "3", "4,,"]
+ "1,2,,3,4,,".split(/,/, 6).should == ["1", "2", "", "3", "4", ","]
+
+ "x".split(/x/, 2).should == ["", ""]
+ "xx".split(/x/, 2).should == ["", "x"]
+ "xx".split(/x/, 3).should == ["", "", ""]
+ "xxx".split(/x/, 2).should == ["", "xx"]
+ "xxx".split(/x/, 3).should == ["", "", "x"]
+ "xxx".split(/x/, 4).should == ["", "", "", ""]
+ end
+
+ it "doesn't suppress or limit fields when limit is negative" do
+ "1,2,,3,4,,".split(/,/, -1).should == ["1", "2", "", "3", "4", "", ""]
+ "1,2,,3,4,,".split(/,/, -5).should == ["1", "2", "", "3", "4", "", ""]
+ " a b c\nd ".split(/\s+/, -1).should == ["", "a", "b", "c", "d", ""]
+ ",".split(/,/, -1).should == ["", ""]
+ end
+
+ it "defaults to $; when regexp isn't given or nil" do
+ begin
+ old_fs = $;
+
+ [/,/, /:/, //, /XY/, /./].each do |fs|
+ $; = fs
+
+ ["x,y,z,,,", "1:2:", "aXYbXYcXY", ""].each do |str|
+ expected = str.split(fs)
+
+ str.split(nil).should == expected
+ str.split.should == expected
+
+ str.split(nil, -1).should == str.split(fs, -1)
+ str.split(nil, 0).should == str.split(fs, 0)
+ str.split(nil, 2).should == str.split(fs, 2)
+ end
+ end
+ ensure
+ $; = old_fs
+ end
+ end
+
+ it "splits between characters when regexp matches a zero-length string" do
+ "hello".split(//).should == ["h", "e", "l", "l", "o"]
+ "hello".split(//, -1).should == ["h", "e", "l", "l", "o", ""]
+ "hello".split(//, 0).should == ["h", "e", "l", "l", "o"]
+ "hello".split(//, 1).should == ["hello"]
+ "hello".split(//, 2).should == ["h", "ello"]
+ "hello".split(//, 5).should == ["h", "e", "l", "l", "o"]
+ "hello".split(//, 6).should == ["h", "e", "l", "l", "o", ""]
+ "hello".split(//, 7).should == ["h", "e", "l", "l", "o", ""]
+
+ "hi mom".split(/\s*/).should == ["h", "i", "m", "o", "m"]
+
+ "AABCCBAA".split(/(?=B)/).should == ["AA", "BCC", "BAA"]
+ "AABCCBAA".split(/(?=B)/, -1).should == ["AA", "BCC", "BAA"]
+ "AABCCBAA".split(/(?=B)/, 2).should == ["AA", "BCCBAA"]
+ end
+
+ it "respects unicode when splitting between characters" do
+ str = "ã“ã«ã¡ã‚"
+ reg = %r!!
+ ary = str.split(reg)
+ ary.size.should == 4
+ ary.should == ["ã“", "ã«", "ã¡", "ã‚"]
+ end
+
+ it "respects the encoding of the regexp when splitting between characters" do
+ str = "\303\202"
+ ary = str.split(//u)
+ ary.size.should == 1
+ ary.should == ["\303\202"]
+ end
+
+ it "includes all captures in the result array" do
+ "hello".split(/(el)/).should == ["h", "el", "lo"]
+ "hi!".split(/()/).should == ["h", "", "i", "", "!"]
+ "hi!".split(/()/, -1).should == ["h", "", "i", "", "!", "", ""]
+ "hello".split(/((el))()/).should == ["h", "el", "el", "", "lo"]
+ "AabB".split(/([a-z])+/).should == ["A", "b", "B"]
+ end
+
+ it "applies the limit to the number of split substrings, without counting captures" do
+ "aBaBa".split(/(B)()()/, 2).should == ["a", "B", "", "", "aBa"]
+ end
+
+ it "does not include non-matching captures in the result array" do
+ "hello".split(/(el)|(xx)/).should == ["h", "el", "lo"]
+ end
+
+ it "tries converting limit to an integer via to_int" do
+ obj = mock('2')
+ obj.should_receive(:to_int).and_return(2)
+
+ "1.2.3.4".split(".", obj).should == ["1", "2.3.4"]
+ end
+
+ it "returns a type error if limit can't be converted to an integer" do
+ lambda {"1.2.3.4".split(".", "three")}.should raise_error(TypeError)
+ lambda {"1.2.3.4".split(".", nil) }.should raise_error(TypeError)
+ end
+
+ it "doesn't set $~" do
+ $~ = nil
+ "x:y:z".split(/:/)
+ $~.should == nil
+ end
+
+ it "returns the original string if no matches are found" do
+ "foo".split(/bar/).should == ["foo"]
+ "foo".split(/bar/, -1).should == ["foo"]
+ "foo".split(/bar/, 0).should == ["foo"]
+ "foo".split(/bar/, 1).should == ["foo"]
+ "foo".split(/bar/, 2).should == ["foo"]
+ "foo".split(/bar/, 3).should == ["foo"]
+ end
+
+ it "returns subclass instances based on self" do
+ ["", "x:y:z:", " x y "].each do |str|
+ [//, /:/, /\s+/].each do |pat|
+ [-1, 0, 1, 2].each do |limit|
+ StringSpecs::MyString.new(str).split(pat, limit).each do |x|
+ x.should be_an_instance_of(StringSpecs::MyString)
+ end
+ end
+ end
+ end
+ end
+
+ it "does not call constructor on created subclass instances" do
+ # can't call should_not_receive on an object that doesn't yet exist
+ # so failure here is signalled by exception, not expectation failure
+
+ s = StringSpecs::StringWithRaisingConstructor.new('silly:string')
+ s.split(/:/).first.should == 'silly'
+ end
+
+ it "taints the resulting strings if self is tainted" do
+ ["", "x:y:z:", " x y "].each do |str|
+ [//, /:/, /\s+/].each do |pat|
+ [-1, 0, 1, 2].each do |limit|
+ str.dup.taint.split(pat, limit).each do |x|
+ # See the spec below for why the conditional is here
+ x.tainted?.should be_true unless x.empty?
+ end
+ end
+ end
+ end
+ end
+
+ it "taints an empty string if self is tainted" do
+ ":".taint.split(//, -1).last.tainted?.should be_true
+ end
+
+ it "doesn't taints the resulting strings if the Regexp is tainted" do
+ ["", "x:y:z:", " x y "].each do |str|
+ [//, /:/, /\s+/].each do |pat|
+ [-1, 0, 1, 2].each do |limit|
+ str.split(pat.dup.taint, limit).each do |x|
+ x.tainted?.should be_false
+ end
+ end
+ end
+ end
+ end
+
+ it "retains the encoding of the source string" 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
+
+ it "returns an ArgumentError if an invalid UTF-8 string is supplied" do
+ broken_str = 'проверка' # in russian, means "test"
+ broken_str.force_encoding('binary')
+ broken_str.chop!
+ broken_str.force_encoding('utf-8')
+ lambda{ broken_str.split(/\r\n|\r|\n/) }.should raise_error(ArgumentError)
+ end
+
+ ruby_version_is "2.6" do
+ it "yields each split substrings if a block is given" do
+ a = []
+ returned_object = "chunky bacon".split(" ") { |str| a << str.capitalize }
+
+ returned_object.should == "chunky bacon"
+ a.should == ["Chunky", "Bacon"]
+ end
+ end
+end
diff --git a/spec/ruby/core/string/squeeze_spec.rb b/spec/ruby/core/string/squeeze_spec.rb
new file mode 100644
index 0000000000..391b356227
--- /dev/null
+++ b/spec/ruby/core/string/squeeze_spec.rb
@@ -0,0 +1,113 @@
+# -*- encoding: binary -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# TODO: rewrite all these specs
+
+describe "String#squeeze" do
+ it "returns new string where runs of the same character are replaced by a single character when no args are given" do
+ "yellow moon".squeeze.should == "yelow mon"
+ end
+
+ it "only squeezes chars that are in the intersection of all sets given" do
+ "woot squeeze cheese".squeeze("eost", "queo").should == "wot squeze chese"
+ " now is the".squeeze(" ").should == " now is the"
+ end
+
+ it "negates sets starting with ^" do
+ s = "<<subbookkeeper!!!>>"
+ s.squeeze("beko", "^e").should == s.squeeze("bko")
+ s.squeeze("^<bek!>").should == s.squeeze("o")
+ s.squeeze("^o").should == s.squeeze("<bek!>")
+ s.squeeze("^").should == s
+ "^__^".squeeze("^^").should == "^_^"
+ "((^^__^^))".squeeze("_^").should == "((^_^))"
+ end
+
+ it "squeezes all chars in a sequence" do
+ s = "--subbookkeeper--"
+ s.squeeze("\x00-\xFF").should == s.squeeze
+ s.squeeze("bk-o").should == s.squeeze("bklmno")
+ s.squeeze("b-e").should == s.squeeze("bcde")
+ s.squeeze("e-").should == "-subbookkeper-"
+ s.squeeze("-e").should == "-subbookkeper-"
+ s.squeeze("---").should == "-subbookkeeper-"
+ "ook--001122".squeeze("--2").should == "ook-012"
+ "ook--(())".squeeze("(--").should == "ook-()"
+ s.squeeze("^b-e").should == "-subbokeeper-"
+ "^^__^^".squeeze("^^-^").should == "^^_^^"
+ "^^--^^".squeeze("^---").should == "^--^"
+
+ s.squeeze("b-dk-o-").should == "-subokeeper-"
+ s.squeeze("-b-dk-o").should == "-subokeeper-"
+ s.squeeze("b-d-k-o").should == "-subokeeper-"
+
+ s.squeeze("bc-e").should == "--subookkeper--"
+ s.squeeze("^bc-e").should == "-subbokeeper-"
+
+ "AABBCCaabbcc[[]]".squeeze("A-a").should == "ABCabbcc[]"
+ end
+
+ it "raises an ArgumentError when the parameter is out of sequence" do
+ s = "--subbookkeeper--"
+ lambda { s.squeeze("e-b") }.should raise_error(ArgumentError)
+ lambda { s.squeeze("^e-b") }.should raise_error(ArgumentError)
+ end
+
+ it "taints the result when self is tainted" do
+ "hello".taint.squeeze("e").tainted?.should == true
+ "hello".taint.squeeze("a-z").tainted?.should == true
+
+ "hello".squeeze("e".taint).tainted?.should == false
+ "hello".squeeze("l".taint).tainted?.should == false
+ end
+
+ it "tries to convert each set arg to a string using to_str" do
+ other_string = mock('lo')
+ other_string.should_receive(:to_str).and_return("lo")
+
+ other_string2 = mock('o')
+ other_string2.should_receive(:to_str).and_return("o")
+
+ "hello room".squeeze(other_string, other_string2).should == "hello rom"
+ end
+
+ it "raises a TypeError when one set arg can't be converted to a string" do
+ lambda { "hello world".squeeze([]) }.should raise_error(TypeError)
+ lambda { "hello world".squeeze(Object.new)}.should raise_error(TypeError)
+ lambda { "hello world".squeeze(mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "returns subclass instances when called on a subclass" do
+ StringSpecs::MyString.new("oh no!!!").squeeze("!").should be_an_instance_of(StringSpecs::MyString)
+ end
+end
+
+describe "String#squeeze!" do
+ it "modifies self in place and returns self" do
+ a = "yellow moon"
+ a.squeeze!.should equal(a)
+ a.should == "yelow mon"
+ end
+
+ it "returns nil if no modifications were made" do
+ a = "squeeze"
+ a.squeeze!("u", "sq").should == nil
+ a.squeeze!("q").should == nil
+ a.should == "squeeze"
+ end
+
+ it "raises an ArgumentError when the parameter is out of sequence" do
+ s = "--subbookkeeper--"
+ lambda { s.squeeze!("e-b") }.should raise_error(ArgumentError)
+ lambda { s.squeeze!("^e-b") }.should raise_error(ArgumentError)
+ end
+
+ it "raises a #{frozen_error_class} when self is frozen" do
+ a = "yellow moon"
+ a.freeze
+
+ lambda { a.squeeze!("") }.should raise_error(frozen_error_class)
+ lambda { a.squeeze! }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/string/start_with_spec.rb b/spec/ruby/core/string/start_with_spec.rb
new file mode 100644
index 0000000000..4e12c1a445
--- /dev/null
+++ b/spec/ruby/core/string/start_with_spec.rb
@@ -0,0 +1,76 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#start_with?" do
+ it "returns true only if beginning match" do
+ s = "hello"
+ s.start_with?('h').should be_true
+ s.start_with?('hel').should be_true
+ s.start_with?('el').should be_false
+ end
+
+ it "returns true only if any beginning match" do
+ "hello".start_with?('x', 'y', 'he', 'z').should be_true
+ end
+
+ it "returns true if the search string is empty" do
+ "hello".start_with?("").should be_true
+ "".start_with?("").should be_true
+ end
+
+ it "converts its argument using :to_str" do
+ s = "hello"
+ find = mock('h')
+ find.should_receive(:to_str).and_return("h")
+ s.start_with?(find).should be_true
+ end
+
+ it "ignores arguments not convertible to string" do
+ "hello".start_with?().should be_false
+ lambda { "hello".start_with?(1) }.should raise_error(TypeError)
+ lambda { "hello".start_with?(["h"]) }.should raise_error(TypeError)
+ lambda { "hello".start_with?(1, nil, "h") }.should raise_error(TypeError)
+ end
+
+ it "uses only the needed arguments" do
+ find = mock('h')
+ find.should_not_receive(:to_str)
+ "hello".start_with?("h",find).should be_true
+ end
+
+ it "works for multibyte strings" do
+ "céréale".start_with?("cér").should be_true
+ end
+
+ ruby_version_is "2.5" do
+ it "supports regexps" do
+ regexp = /[h1]/
+ "hello".start_with?(regexp).should be_true
+ "1337".start_with?(regexp).should be_true
+ "foxes are 1337".start_with?(regexp).should be_false
+ "chunky\n12bacon".start_with?(/12/).should be_false
+ end
+
+ it "supports regexps with ^ and $ modifiers" do
+ regexp1 = /^\d{2}/
+ regexp2 = /\d{2}$/
+ "12test".start_with?(regexp1).should be_true
+ "test12".start_with?(regexp1).should be_false
+ "12test".start_with?(regexp2).should be_false
+ "test12".start_with?(regexp2).should be_false
+ end
+
+ it "sets Regexp.last_match if it returns true" do
+ regexp = /test-(\d+)/
+ "test-1337".start_with?(regexp).should be_true
+ Regexp.last_match.should_not be_nil
+ Regexp.last_match[1].should == "1337"
+ $1.should == "1337"
+
+ "test-asdf".start_with?(regexp).should be_false
+ Regexp.last_match.should be_nil
+ $1.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/string/string_spec.rb b/spec/ruby/core/string/string_spec.rb
new file mode 100644
index 0000000000..cdefbbecbd
--- /dev/null
+++ b/spec/ruby/core/string/string_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "String" do
+ it "includes Comparable" do
+ String.include?(Comparable).should == true
+ end
+end
diff --git a/spec/ruby/core/string/strip_spec.rb b/spec/ruby/core/string/strip_spec.rb
new file mode 100644
index 0000000000..54dec794fa
--- /dev/null
+++ b/spec/ruby/core/string/strip_spec.rb
@@ -0,0 +1,60 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#strip" do
+ it "returns a new string with leading and trailing whitespace removed" do
+ " hello ".strip.should == "hello"
+ " hello world ".strip.should == "hello world"
+ "\tgoodbye\r\v\n".strip.should == "goodbye"
+ "\x00 goodbye \x00".strip.should == "\x00 goodbye"
+ end
+
+ it "returns a copy of self with trailing NULL bytes and whitespace" do
+ " \x00 goodbye \x00 ".strip.should == "\x00 goodbye"
+ end
+
+ it "taints the result when self is tainted" do
+ "".taint.strip.tainted?.should == true
+ "ok".taint.strip.tainted?.should == true
+ " ok ".taint.strip.tainted?.should == true
+ end
+end
+
+describe "String#strip!" do
+ it "modifies self in place and returns self" do
+ a = " hello "
+ a.strip!.should equal(a)
+ a.should == "hello"
+
+ a = "\tgoodbye\r\v\n"
+ a.strip!
+ a.should == "goodbye"
+
+ a = "\000 goodbye \000"
+ a.strip!
+ a.should == "\000 goodbye"
+
+ end
+
+ it "returns nil if no modifications where made" do
+ a = "hello"
+ a.strip!.should == nil
+ a.should == "hello"
+ end
+
+ it "modifies self removing trailing NULL bytes and whitespace" do
+ a = " \x00 goodbye \x00 "
+ a.strip!
+ a.should == "\x00 goodbye"
+ end
+
+ it "raises a #{frozen_error_class} on a frozen instance that is modified" do
+ lambda { " hello ".freeze.strip! }.should raise_error(frozen_error_class)
+ end
+
+ # see #1552
+ it "raises a #{frozen_error_class} on a frozen instance that would not be modified" do
+ lambda {"hello".freeze.strip! }.should raise_error(frozen_error_class)
+ lambda {"".freeze.strip! }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/string/sub_spec.rb b/spec/ruby/core/string/sub_spec.rb
new file mode 100644
index 0000000000..901d1596c5
--- /dev/null
+++ b/spec/ruby/core/string/sub_spec.rb
@@ -0,0 +1,571 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#sub with pattern, replacement" do
+ it "returns a copy of self when no modification is made" do
+ a = "hello"
+ b = a.sub(/w.*$/, "*")
+
+ b.should_not equal(a)
+ b.should == "hello"
+ end
+
+ it "returns a copy of self with all occurrences of pattern replaced with replacement" do
+ "hello".sub(/[aeiou]/, '*').should == "h*llo"
+ "hello".sub(//, ".").should == ".hello"
+ end
+
+ it "ignores a block if supplied" do
+ "food".sub(/f/, "g") { "w" }.should == "good"
+ end
+
+ it "supports \\G which matches at the beginning of the string" do
+ "hello world!".sub(/\Ghello/, "hi").should == "hi world!"
+ end
+
+ it "supports /i for ignoring case" do
+ "Hello".sub(/h/i, "j").should == "jello"
+ "hello".sub(/H/i, "j").should == "jello"
+ end
+
+ it "doesn't interpret regexp metacharacters if pattern is a string" do
+ "12345".sub('\d', 'a').should == "12345"
+ '\d'.sub('\d', 'a').should == "a"
+ end
+
+ it "replaces \\1 sequences with the regexp's corresponding capture" do
+ str = "hello"
+
+ str.sub(/([aeiou])/, '<\1>').should == "h<e>llo"
+ str.sub(/(.)/, '\1\1').should == "hhello"
+
+ str.sub(/.(.?)/, '<\0>(\1)').should == "<he>(e)llo"
+
+ str.sub(/.(.)+/, '\1').should == "o"
+
+ str = "ABCDEFGHIJKL"
+ re = /#{"(.)" * 12}/
+ str.sub(re, '\1').should == "A"
+ str.sub(re, '\9').should == "I"
+ # Only the first 9 captures can be accessed in MRI
+ str.sub(re, '\10').should == "A0"
+ end
+
+ it "treats \\1 sequences without corresponding captures as empty strings" do
+ str = "hello!"
+
+ str.sub("", '<\1>').should == "<>hello!"
+ str.sub("h", '<\1>').should == "<>ello!"
+
+ str.sub(//, '<\1>').should == "<>hello!"
+ str.sub(/./, '\1\2\3').should == "ello!"
+ str.sub(/.(.{20})?/, '\1').should == "ello!"
+ end
+
+ it "replaces \\& and \\0 with the complete match" do
+ str = "hello!"
+
+ str.sub("", '<\0>').should == "<>hello!"
+ str.sub("", '<\&>').should == "<>hello!"
+ str.sub("he", '<\0>').should == "<he>llo!"
+ str.sub("he", '<\&>').should == "<he>llo!"
+ str.sub("l", '<\0>').should == "he<l>lo!"
+ str.sub("l", '<\&>').should == "he<l>lo!"
+
+ str.sub(//, '<\0>').should == "<>hello!"
+ str.sub(//, '<\&>').should == "<>hello!"
+ str.sub(/../, '<\0>').should == "<he>llo!"
+ str.sub(/../, '<\&>').should == "<he>llo!"
+ str.sub(/(.)./, '<\0>').should == "<he>llo!"
+ end
+
+ it "replaces \\` with everything before the current match" do
+ str = "hello!"
+
+ str.sub("", '<\`>').should == "<>hello!"
+ str.sub("h", '<\`>').should == "<>ello!"
+ str.sub("l", '<\`>').should == "he<he>lo!"
+ str.sub("!", '<\`>').should == "hello<hello>"
+
+ str.sub(//, '<\`>').should == "<>hello!"
+ str.sub(/..o/, '<\`>').should == "he<he>!"
+ end
+
+ it "replaces \\' with everything after the current match" do
+ str = "hello!"
+
+ str.sub("", '<\\\'>').should == "<hello!>hello!"
+ str.sub("h", '<\\\'>').should == "<ello!>ello!"
+ str.sub("ll", '<\\\'>').should == "he<o!>o!"
+ str.sub("!", '<\\\'>').should == "hello<>"
+
+ str.sub(//, '<\\\'>').should == "<hello!>hello!"
+ str.sub(/../, '<\\\'>').should == "<llo!>llo!"
+ end
+
+ it "replaces \\\\\\+ with \\\\+" do
+ "x".sub(/x/, '\\\+').should == "\\+"
+ end
+
+ it "replaces \\+ with the last paren that actually matched" do
+ str = "hello!"
+
+ str.sub(/(.)(.)/, '\+').should == "ello!"
+ str.sub(/(.)(.)+/, '\+').should == "!"
+ str.sub(/(.)()/, '\+').should == "ello!"
+ str.sub(/(.)(.{20})?/, '<\+>').should == "<h>ello!"
+
+ str = "ABCDEFGHIJKL"
+ re = /#{"(.)" * 12}/
+ str.sub(re, '\+').should == "L"
+ end
+
+ it "treats \\+ as an empty string if there was no captures" do
+ "hello!".sub(/./, '\+').should == "ello!"
+ end
+
+ it "maps \\\\ in replacement to \\" do
+ "hello".sub(/./, '\\\\').should == '\\ello'
+ end
+
+ it "leaves unknown \\x escapes in replacement untouched" do
+ "hello".sub(/./, '\\x').should == '\\xello'
+ "hello".sub(/./, '\\y').should == '\\yello'
+ end
+
+ it "leaves \\ at the end of replacement untouched" do
+ "hello".sub(/./, 'hah\\').should == 'hah\\ello'
+ end
+
+ it "taints the result if the original string or replacement is tainted" do
+ hello = "hello"
+ hello_t = "hello"
+ a = "a"
+ a_t = "a"
+ empty = ""
+ empty_t = ""
+
+ hello_t.taint; a_t.taint; empty_t.taint
+
+ hello_t.sub(/./, a).tainted?.should == true
+ hello_t.sub(/./, empty).tainted?.should == true
+
+ hello.sub(/./, a_t).tainted?.should == true
+ hello.sub(/./, empty_t).tainted?.should == true
+ hello.sub(//, empty_t).tainted?.should == true
+
+ hello.sub(//.taint, "foo").tainted?.should == false
+ end
+
+ it "tries to convert pattern to a string using to_str" do
+ pattern = mock('.')
+ pattern.should_receive(:to_str).and_return(".")
+
+ "hello.".sub(pattern, "!").should == "hello!"
+ end
+
+ not_supported_on :opal do
+ it "raises a TypeError when pattern is a Symbol" do
+ lambda { "hello".sub(:woot, "x") }.should raise_error(TypeError)
+ end
+ end
+
+ it "raises a TypeError when pattern is an Array" do
+ lambda { "hello".sub([], "x") }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when pattern can't be converted to a string" do
+ lambda { "hello".sub(Object.new, nil) }.should raise_error(TypeError)
+ end
+
+ it "tries to convert replacement to a string using to_str" do
+ replacement = mock('hello_replacement')
+ replacement.should_receive(:to_str).and_return("hello_replacement")
+
+ "hello".sub(/hello/, replacement).should == "hello_replacement"
+ end
+
+ it "raises a TypeError when replacement can't be converted to a string" do
+ lambda { "hello".sub(/[aeiou]/, []) }.should raise_error(TypeError)
+ lambda { "hello".sub(/[aeiou]/, 99) }.should raise_error(TypeError)
+ end
+
+ it "returns subclass instances when called on a subclass" do
+ StringSpecs::MyString.new("").sub(//, "").should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("").sub(/foo/, "").should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("foo").sub(/foo/, "").should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("foo").sub("foo", "").should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ it "sets $~ to MatchData of match and nil when there's none" do
+ 'hello.'.sub('hello', 'x')
+ $~[0].should == 'hello'
+
+ 'hello.'.sub('not', 'x')
+ $~.should == nil
+
+ 'hello.'.sub(/.(.)/, 'x')
+ $~[0].should == 'he'
+
+ 'hello.'.sub(/not/, 'x')
+ $~.should == nil
+ end
+
+ it "replaces \\\\\\1 with \\1" do
+ "ababa".sub(/(b)/, '\\\1').should == "a\\1aba"
+ end
+
+ it "replaces \\\\\\\\1 with \\1" do
+ "ababa".sub(/(b)/, '\\\\1').should == "a\\1aba"
+ end
+
+ it "replaces \\\\\\\\\\1 with \\" do
+ "ababa".sub(/(b)/, '\\\\\1').should == "a\\baba"
+ end
+
+end
+
+describe "String#sub with pattern and block" do
+ it "returns a copy of self with the first occurrences of pattern replaced with the block's return value" do
+ "hi".sub(/./) { |s| s + ' ' }.should == "h i"
+ "hi!".sub(/(.)(.)/) { |*a| a.inspect }.should == '["hi"]!'
+ end
+
+ it "sets $~ for access from the block" do
+ str = "hello"
+ str.sub(/([aeiou])/) { "<#{$~[1]}>" }.should == "h<e>llo"
+ str.sub(/([aeiou])/) { "<#{$1}>" }.should == "h<e>llo"
+ str.sub("l") { "<#{$~[0]}>" }.should == "he<l>lo"
+
+ offsets = []
+
+ str.sub(/([aeiou])/) do
+ md = $~
+ md.string.should == str
+ offsets << md.offset(0)
+ str
+ end.should == "hhellollo"
+
+ offsets.should == [[1, 2]]
+ end
+
+ it "sets $~ to MatchData of last match and nil when there's none for access from outside" do
+ 'hello.'.sub('l') { 'x' }
+ $~.begin(0).should == 2
+ $~[0].should == 'l'
+
+ 'hello.'.sub('not') { 'x' }
+ $~.should == nil
+
+ 'hello.'.sub(/.(.)/) { 'x' }
+ $~[0].should == 'he'
+
+ 'hello.'.sub(/not/) { 'x' }
+ $~.should == nil
+ end
+
+ it "doesn't raise a RuntimeError if the string is modified while substituting" do
+ str = "hello"
+ str.sub(//) { str[0] = 'x' }.should == "xhello"
+ str.should == "xello"
+ end
+
+ it "doesn't interpolate special sequences like \\1 for the block's return value" do
+ repl = '\& \0 \1 \` \\\' \+ \\\\ foo'
+ "hello".sub(/(.+)/) { repl }.should == repl
+ end
+
+ it "converts the block's return value to a string using to_s" do
+ obj = mock('hello_replacement')
+ obj.should_receive(:to_s).and_return("hello_replacement")
+ "hello".sub(/hello/) { obj }.should == "hello_replacement"
+
+ obj = mock('ok')
+ obj.should_receive(:to_s).and_return("ok")
+ "hello".sub(/.+/) { obj }.should == "ok"
+ end
+
+ it "taints the result if the original string or replacement is tainted" do
+ hello = "hello"
+ hello_t = "hello"
+ a = "a"
+ a_t = "a"
+ empty = ""
+ empty_t = ""
+
+ hello_t.taint; a_t.taint; empty_t.taint
+
+ hello_t.sub(/./) { a }.tainted?.should == true
+ hello_t.sub(/./) { empty }.tainted?.should == true
+
+ hello.sub(/./) { a_t }.tainted?.should == true
+ hello.sub(/./) { empty_t }.tainted?.should == true
+ hello.sub(//) { empty_t }.tainted?.should == true
+
+ hello.sub(//.taint) { "foo" }.tainted?.should == false
+ end
+end
+
+describe "String#sub! with pattern, replacement" do
+ it "modifies self in place and returns self" do
+ a = "hello"
+ a.sub!(/[aeiou]/, '*').should equal(a)
+ a.should == "h*llo"
+ end
+
+ it "taints self if replacement is tainted" do
+ a = "hello"
+ a.sub!(/./.taint, "foo").tainted?.should == false
+ a.sub!(/./, "foo".taint).tainted?.should == true
+ end
+
+ it "returns nil if no modifications were made" do
+ a = "hello"
+ a.sub!(/z/, '*').should == nil
+ a.sub!(/z/, 'z').should == nil
+ a.should == "hello"
+ end
+
+ it "raises a #{frozen_error_class} when self is frozen" do
+ s = "hello"
+ s.freeze
+
+ lambda { s.sub!(/ROAR/, "x") }.should raise_error(frozen_error_class)
+ lambda { s.sub!(/e/, "e") }.should raise_error(frozen_error_class)
+ lambda { s.sub!(/[aeiou]/, '*') }.should raise_error(frozen_error_class)
+ end
+end
+
+describe "String#sub! with pattern and block" do
+ it "modifies self in place and returns self" do
+ a = "hello"
+ a.sub!(/[aeiou]/) { '*' }.should equal(a)
+ a.should == "h*llo"
+ end
+
+ it "sets $~ for access from the block" do
+ str = "hello"
+ str.dup.sub!(/([aeiou])/) { "<#{$~[1]}>" }.should == "h<e>llo"
+ str.dup.sub!(/([aeiou])/) { "<#{$1}>" }.should == "h<e>llo"
+ str.dup.sub!("l") { "<#{$~[0]}>" }.should == "he<l>lo"
+
+ offsets = []
+
+ str.dup.sub!(/([aeiou])/) do
+ md = $~
+ md.string.should == str
+ offsets << md.offset(0)
+ str
+ end.should == "hhellollo"
+
+ offsets.should == [[1, 2]]
+ end
+
+ it "taints self if block's result is tainted" do
+ a = "hello"
+ a.sub!(/./.taint) { "foo" }.tainted?.should == false
+ a.sub!(/./) { "foo".taint }.tainted?.should == true
+ end
+
+ it "returns nil if no modifications were made" do
+ a = "hello"
+ a.sub!(/z/) { '*' }.should == nil
+ a.sub!(/z/) { 'z' }.should == nil
+ a.should == "hello"
+ end
+
+ it "raises a RuntimeError if the string is modified while substituting" do
+ str = "hello"
+ lambda { str.sub!(//) { str << 'x' } }.should raise_error(RuntimeError)
+ end
+
+ it "raises a #{frozen_error_class} when self is frozen" do
+ s = "hello"
+ s.freeze
+
+ lambda { s.sub!(/ROAR/) { "x" } }.should raise_error(frozen_error_class)
+ lambda { s.sub!(/e/) { "e" } }.should raise_error(frozen_error_class)
+ lambda { s.sub!(/[aeiou]/) { '*' } }.should raise_error(frozen_error_class)
+ end
+end
+
+describe "String#sub with pattern and Hash" do
+
+ it "returns a copy of self with the first occurrence of pattern replaced with the value of the corresponding hash key" do
+ "hello".sub(/./, 'l' => 'L').should == "ello"
+ "hello!".sub(/(.)(.)/, 'he' => 'she ', 'll' => 'said').should == 'she llo!'
+ "hello".sub('l', 'l' => 'el').should == 'heello'
+ end
+
+ it "removes keys that don't correspond to matches" do
+ "hello".sub(/./, 'z' => 'b', 'o' => 'ow').should == "ello"
+ end
+
+ it "ignores non-String keys" do
+ "tattoo".sub(/(tt)/, 'tt' => 'b', tt: 'z').should == "taboo"
+ end
+
+ it "uses a key's value only a single time" do
+ "food".sub(/o/, 'o' => '0').should == "f0od"
+ end
+
+ it "uses the hash's default value for missing keys" do
+ hsh = {}
+ hsh.default='?'
+ hsh['o'] = '0'
+ "food".sub(/./, hsh).should == "?ood"
+ end
+
+ it "coerces the hash values with #to_s" do
+ hsh = {}
+ hsh.default=[]
+ hsh['o'] = 0
+ obj = mock('!')
+ obj.should_receive(:to_s).and_return('!')
+ hsh['f'] = obj
+ "food!".sub(/./, hsh).should == "!ood!"
+ end
+
+ it "uses the hash's value set from default_proc for missing keys" do
+ hsh = {}
+ hsh.default_proc = lambda { |k,v| 'lamb' }
+ "food!".sub(/./, hsh).should == "lambood!"
+ end
+
+ it "sets $~ to MatchData of first match and nil when there's none for access from outside" do
+ 'hello.'.sub('l', 'l' => 'L')
+ $~.begin(0).should == 2
+ $~[0].should == 'l'
+
+ 'hello.'.sub('not', 'ot' => 'to')
+ $~.should == nil
+
+ 'hello.'.sub(/.(.)/, 'o' => ' hole')
+ $~[0].should == 'he'
+
+ 'hello.'.sub(/not/, 'z' => 'glark')
+ $~.should == nil
+ end
+
+ it "doesn't interpolate special sequences like \\1 for the block's return value" do
+ repl = '\& \0 \1 \` \\\' \+ \\\\ foo'
+ "hello".sub(/(.+)/, 'hello' => repl ).should == repl
+ end
+
+ it "untrusts the result if the original string is untrusted" do
+ str = "Ghana".untrust
+ str.sub(/[Aa]na/, 'ana' => '').untrusted?.should be_true
+ end
+
+ it "untrusts the result if a hash value is untrusted" do
+ str = "Ghana"
+ str.sub(/a$/, 'a' => 'di'.untrust).untrusted?.should be_true
+ end
+
+ it "taints the result if the original string is tainted" do
+ str = "Ghana".taint
+ str.sub(/[Aa]na/, 'ana' => '').tainted?.should be_true
+ end
+
+ it "taints the result if a hash value is tainted" do
+ str = "Ghana"
+ str.sub(/a$/, 'a' => 'di'.taint).tainted?.should be_true
+ end
+
+end
+
+describe "String#sub! with pattern and Hash" do
+
+ it "returns self with the first occurrence of pattern replaced with the value of the corresponding hash key" do
+ "hello".sub!(/./, 'l' => 'L').should == "ello"
+ "hello!".sub!(/(.)(.)/, 'he' => 'she ', 'll' => 'said').should == 'she llo!'
+ "hello".sub!('l', 'l' => 'el').should == 'heello'
+ end
+
+ it "removes keys that don't correspond to matches" do
+ "hello".sub!(/./, 'z' => 'b', 'o' => 'ow').should == "ello"
+ end
+
+ it "ignores non-String keys" do
+ "hello".sub!(/(ll)/, 'll' => 'r', ll: 'z').should == "hero"
+ end
+
+ it "uses a key's value only a single time" do
+ "food".sub!(/o/, 'o' => '0').should == "f0od"
+ end
+
+ it "uses the hash's default value for missing keys" do
+ hsh = {}
+ hsh.default='?'
+ hsh['o'] = '0'
+ "food".sub!(/./, hsh).should == "?ood"
+ end
+
+ it "coerces the hash values with #to_s" do
+ hsh = {}
+ hsh.default=[]
+ hsh['o'] = 0
+ obj = mock('!')
+ obj.should_receive(:to_s).and_return('!')
+ hsh['f'] = obj
+ "food!".sub!(/./, hsh).should == "!ood!"
+ end
+
+ it "uses the hash's value set from default_proc for missing keys" do
+ hsh = {}
+ hsh.default_proc = lambda { |k,v| 'lamb' }
+ "food!".sub!(/./, hsh).should == "lambood!"
+ end
+
+ it "sets $~ to MatchData of first match and nil when there's none for access from outside" do
+ 'hello.'.sub!('l', 'l' => 'L')
+ $~.begin(0).should == 2
+ $~[0].should == 'l'
+
+ 'hello.'.sub!('not', 'ot' => 'to')
+ $~.should == nil
+
+ 'hello.'.sub!(/.(.)/, 'o' => ' hole')
+ $~[0].should == 'he'
+
+ 'hello.'.sub!(/not/, 'z' => 'glark')
+ $~.should == nil
+ end
+
+ it "doesn't interpolate special sequences like \\1 for the block's return value" do
+ repl = '\& \0 \1 \` \\\' \+ \\\\ foo'
+ "hello".sub!(/(.+)/, 'hello' => repl ).should == repl
+ end
+
+ it "keeps untrusted state" do
+ str = "Ghana".untrust
+ str.sub!(/[Aa]na/, 'ana' => '').untrusted?.should be_true
+ end
+
+ it "untrusts self if a hash value is untrusted" do
+ str = "Ghana"
+ str.sub!(/a$/, 'a' => 'di'.untrust).untrusted?.should be_true
+ end
+
+ it "keeps tainted state" do
+ str = "Ghana".taint
+ str.sub!(/[Aa]na/, 'ana' => '').tainted?.should be_true
+ end
+
+ it "taints self if a hash value is tainted" do
+ str = "Ghana"
+ str.sub!(/a$/, 'a' => 'di'.taint).tainted?.should be_true
+ end
+end
+
+describe "String#sub with pattern and without replacement and block" do
+ it "raises a ArgumentError" do
+ lambda { "abca".sub(/a/) }.should raise_error(ArgumentError)
+ end
+end
+
+describe "String#sub! with pattern and without replacement and block" do
+ it "raises a ArgumentError" do
+ lambda { "abca".sub!(/a/) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/string/succ_spec.rb b/spec/ruby/core/string/succ_spec.rb
new file mode 100644
index 0000000000..65047e0aa2
--- /dev/null
+++ b/spec/ruby/core/string/succ_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/succ'
+
+describe "String#succ" do
+ it_behaves_like :string_succ, :succ
+end
+
+describe "String#succ!" do
+ it_behaves_like :string_succ_bang, :"succ!"
+end
diff --git a/spec/ruby/core/string/sum_spec.rb b/spec/ruby/core/string/sum_spec.rb
new file mode 100644
index 0000000000..c283b7c254
--- /dev/null
+++ b/spec/ruby/core/string/sum_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#sum" do
+ it "returns a basic n-bit checksum of the characters in self" do
+ "ruby".sum.should == 450
+ "ruby".sum(8).should == 194
+ "rubinius".sum(23).should == 881
+ end
+
+ it "tries to convert n to an integer using to_int" do
+ obj = mock('8')
+ obj.should_receive(:to_int).and_return(8)
+
+ "hello".sum(obj).should == "hello".sum(8)
+ end
+
+ it "returns sum of the bytes in self if n less or equal to zero" do
+ "xyz".sum(0).should == 363
+ "xyz".sum(-10).should == 363
+ end
+end
diff --git a/spec/ruby/core/string/swapcase_spec.rb b/spec/ruby/core/string/swapcase_spec.rb
new file mode 100644
index 0000000000..bb89dee48f
--- /dev/null
+++ b/spec/ruby/core/string/swapcase_spec.rb
@@ -0,0 +1,183 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#swapcase" do
+ it "returns a new string with all uppercase chars from self converted to lowercase and vice versa" do
+ "Hello".swapcase.should == "hELLO"
+ "cYbEr_PuNk11".swapcase.should == "CyBeR_pUnK11"
+ "+++---111222???".swapcase.should == "+++---111222???"
+ end
+
+ it "taints resulting string when self is tainted" do
+ "".taint.swapcase.tainted?.should == true
+ "hello".taint.swapcase.tainted?.should == true
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "is locale insensitive (only upcases a-z and only downcases A-Z)" do
+ "ÄÖÜ".swapcase.should == "ÄÖÜ"
+ "ärger".swapcase.should == "äRGER"
+ "BÄR".swapcase.should == "bÄr"
+ end
+ end
+
+ ruby_version_is '2.4' do
+ describe "full Unicode case mapping" do
+ it "works for all of Unicode with no option" do
+ "äÖü".swapcase.should == "ÄöÜ"
+ end
+
+ it "updates string metadata" do
+ swapcased = "Aßet".swapcase
+
+ swapcased.should == "aSSET"
+ swapcased.size.should == 5
+ swapcased.bytesize.should == 5
+ swapcased.ascii_only?.should be_true
+ end
+ end
+
+ describe "ASCII-only case mapping" do
+ it "does not swapcase non-ASCII characters" do
+ "aßet".swapcase(:ascii).should == "AßET"
+ end
+ end
+
+ describe "full Unicode case mapping adapted for Turkic languages" do
+ it "swaps case of ASCII characters according to Turkic semantics" do
+ "aiS".swapcase(:turkic).should == "Aİs"
+ end
+
+ it "allows Lithuanian as an extra option" do
+ "aiS".swapcase(:turkic, :lithuanian).should == "Aİs"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { "aiS".swapcase(:turkic, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "full Unicode case mapping adapted for Lithuanian" do
+ it "currently works the same as full Unicode case mapping" do
+ "Iß".swapcase(:lithuanian).should == "iSS"
+ end
+
+ it "allows Turkic as an extra option (and applies Turkic semantics)" do
+ "iS".swapcase(:lithuanian, :turkic).should == "İs"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { "aiS".swapcase(:lithuanian, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "does not allow the :fold option for upcasing" do
+ lambda { "abc".swapcase(:fold) }.should raise_error(ArgumentError)
+ end
+
+ it "does not allow invalid options" do
+ lambda { "abc".swapcase(:invalid_option) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "returns subclass instances when called on a subclass" do
+ StringSpecs::MyString.new("").swapcase.should be_an_instance_of(StringSpecs::MyString)
+ StringSpecs::MyString.new("hello").swapcase.should be_an_instance_of(StringSpecs::MyString)
+ end
+end
+
+describe "String#swapcase!" do
+ it "modifies self in place" do
+ a = "cYbEr_PuNk11"
+ a.swapcase!.should equal(a)
+ a.should == "CyBeR_pUnK11"
+ end
+
+ ruby_version_is '2.4' do
+ describe "full Unicode case mapping" do
+ it "modifies self in place for all of Unicode with no option" do
+ a = "äÖü"
+ a.swapcase!
+ a.should == "ÄöÜ"
+ end
+
+ it "updates string metadata" do
+ swapcased = "Aßet"
+ swapcased.swapcase!
+
+ swapcased.should == "aSSET"
+ swapcased.size.should == 5
+ swapcased.bytesize.should == 5
+ swapcased.ascii_only?.should be_true
+ end
+ end
+
+ describe "modifies self in place for ASCII-only case mapping" do
+ it "does not swapcase non-ASCII characters" do
+ a = "aßet"
+ a.swapcase!(:ascii)
+ a.should == "AßET"
+ end
+ end
+
+ describe "modifies self in place for full Unicode case mapping adapted for Turkic languages" do
+ it "swaps case of ASCII characters according to Turkic semantics" do
+ a = "aiS"
+ a.swapcase!(:turkic)
+ a.should == "Aİs"
+ end
+
+ it "allows Lithuanian as an extra option" do
+ a = "aiS"
+ a.swapcase!(:turkic, :lithuanian)
+ a.should == "Aİs"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { a = "aiS"; a.swapcase!(:turkic, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "full Unicode case mapping adapted for Lithuanian" do
+ it "currently works the same as full Unicode case mapping" do
+ a = "Iß"
+ a.swapcase!(:lithuanian)
+ a.should == "iSS"
+ end
+
+ it "allows Turkic as an extra option (and applies Turkic semantics)" do
+ a = "iS"
+ a.swapcase!(:lithuanian, :turkic)
+ a.should == "İs"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { a = "aiS"; a.swapcase!(:lithuanian, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "does not allow the :fold option for upcasing" do
+ lambda { a = "abc"; a.swapcase!(:fold) }.should raise_error(ArgumentError)
+ end
+
+ it "does not allow invalid options" do
+ lambda { a = "abc"; a.swapcase!(:invalid_option) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "returns nil if no modifications were made" do
+ a = "+++---111222???"
+ a.swapcase!.should == nil
+ a.should == "+++---111222???"
+
+ "".swapcase!.should == nil
+ end
+
+ it "raises a #{frozen_error_class} when self is frozen" do
+ ["", "hello"].each do |a|
+ a.freeze
+ lambda { a.swapcase! }.should raise_error(frozen_error_class)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/to_c_spec.rb b/spec/ruby/core/string/to_c_spec.rb
new file mode 100644
index 0000000000..9c84b14f4d
--- /dev/null
+++ b/spec/ruby/core/string/to_c_spec.rb
@@ -0,0 +1,99 @@
+require_relative '../../spec_helper'
+
+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 "understands negative scientific notation for the real part" do
+ '-2e3+4i'.to_c.should == Complex(-2e3,4)
+ end
+
+ it "understands scientific notation for the imaginary part" do
+ '4+2e3i'.to_c.should == Complex(4, 2e3)
+ end
+
+ it "understands negative scientific notation for the imaginary part" do
+ '4-2e3i'.to_c.should == Complex(4, -2e3)
+ end
+
+ it "understands scientific notation for the real and imaginary part in the same String" do
+ '2e3+2e4i'.to_c.should == Complex(2e3,2e4)
+ 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)
+ 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)
+ end
+end
diff --git a/spec/ruby/core/string/to_f_spec.rb b/spec/ruby/core/string/to_f_spec.rb
new file mode 100644
index 0000000000..ca8536c30e
--- /dev/null
+++ b/spec/ruby/core/string/to_f_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# src.scan(/[+-]?[\d_]+\.[\d_]+(e[+-]?[\d_]+)?\b|[+-]?[\d_]+e[+-]?[\d_]+\b/i)
+
+describe "String#to_f" do
+ it "treats leading characters of self as a floating point number" do
+ "123.45e1".to_f.should == 1234.5
+ "45.67 degrees".to_f.should == 45.67
+ "0".to_f.should == 0.0
+ "123.45e1".to_f.should == 1234.5
+
+ ".5".to_f.should == 0.5
+ ".5e1".to_f.should == 5.0
+ "5e".to_f.should == 5.0
+ "5E".to_f.should == 5.0
+ end
+
+ it "treats special float value strings as characters" do
+ "NaN".to_f.should == 0
+ "Infinity".to_f.should == 0
+ "-Infinity".to_f.should == 0
+ end
+
+ it "allows for varying case" do
+ "123.45e1".to_f.should == 1234.5
+ "123.45E1".to_f.should == 1234.5
+ end
+
+ it "allows for varying signs" do
+ "+123.45e1".to_f.should == +123.45e1
+ "-123.45e1".to_f.should == -123.45e1
+ "123.45e+1".to_f.should == 123.45e+1
+ "123.45e-1".to_f.should == 123.45e-1
+ "+123.45e+1".to_f.should == +123.45e+1
+ "+123.45e-1".to_f.should == +123.45e-1
+ "-123.45e+1".to_f.should == -123.45e+1
+ "-123.45e-1".to_f.should == -123.45e-1
+ end
+
+ it "allows for underscores, even in the decimal side" do
+ "1_234_567.890_1".to_f.should == 1_234_567.890_1
+ end
+
+ it "returns 0 for strings with any non-digit in them" do
+ "blah".to_f.should == 0
+ "0b5".to_f.should == 0
+ "0d5".to_f.should == 0
+ "0o5".to_f.should == 0
+ "0xx5".to_f.should == 0
+ end
+
+ it "returns 0 for strings with leading underscores" do
+ "_9".to_f.should == 0
+ end
+
+ it "takes an optional sign" do
+ "-45.67 degrees".to_f.should == -45.67
+ "+45.67 degrees".to_f.should == 45.67
+ "-5_5e-5_0".to_f.should == -55e-50
+ "-".to_f.should == 0.0
+ (1.0 / "-0".to_f).to_s.should == "-Infinity"
+ end
+
+ it "returns 0.0 if the conversion fails" do
+ "bad".to_f.should == 0.0
+ "thx1138".to_f.should == 0.0
+ end
+end
diff --git a/spec/ruby/core/string/to_i_spec.rb b/spec/ruby/core/string/to_i_spec.rb
new file mode 100644
index 0000000000..f86d3a43a7
--- /dev/null
+++ b/spec/ruby/core/string/to_i_spec.rb
@@ -0,0 +1,337 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#to_i" do
+ it "returns 0 for strings with leading underscores" do
+ "_123".to_i.should == 0
+ end
+
+ it "ignores underscores in between the digits" do
+ "1_2_3asdf".to_i.should == 123
+ end
+
+ it "ignores leading whitespaces" do
+ [ " 123", " 123", "\r\n\r\n123", "\t\t123",
+ "\r\n\t\n123", " \t\n\r\t 123"].each do |str|
+ str.to_i.should == 123
+ end
+ end
+
+ it "ignores subsequent invalid characters" do
+ "123asdf".to_i.should == 123
+ "123#123".to_i.should == 123
+ "123 456".to_i.should == 123
+ end
+
+ it "returns 0 if self is no valid integer-representation" do
+ [ "++2", "+-2", "--2" ].each do |str|
+ str.to_i.should == 0
+ end
+ end
+
+ it "accepts '+' at the beginning of a String" do
+ "+0d56".to_i.should == 56
+ end
+
+ it "interprets leading characters as a number in the given base" do
+ "100110010010".to_i(2).should == 0b100110010010
+ "100110201001".to_i(3).should == 186409
+ "103110201001".to_i(4).should == 5064769
+ "103110241001".to_i(5).should == 55165126
+ "153110241001".to_i(6).should == 697341529
+ "153160241001".to_i(7).should == 3521513430
+ "153160241701".to_i(8).should == 14390739905
+ "853160241701".to_i(9).should == 269716550518
+ "853160241791".to_i(10).should == 853160241791
+
+ "F00D_BE_1337".to_i(16).should == 0xF00D_BE_1337
+ "-hello_world".to_i(32).should == -18306744
+ "abcXYZ".to_i(36).should == 623741435
+
+ ("z" * 24).to_i(36).should == 22452257707354557240087211123792674815
+
+ "5e10".to_i.should == 5
+ end
+
+ it "auto-detects base 8 via leading 0 when base = 0" do
+ "01778".to_i(0).should == 0177
+ "-01778".to_i(0).should == -0177
+ end
+
+ it "auto-detects base 2 via 0b when base = 0" do
+ "0b112".to_i(0).should == 0b11
+ "-0b112".to_i(0).should == -0b11
+ end
+
+ it "auto-detects base 10 via 0d when base = 0" do
+ "0d19A".to_i(0).should == 19
+ "-0d19A".to_i(0).should == -19
+ end
+
+ it "auto-detects base 8 via 0o when base = 0" do
+ "0o178".to_i(0).should == 0o17
+ "-0o178".to_i(0).should == -0o17
+ end
+
+ it "auto-detects base 16 via 0x when base = 0" do
+ "0xFAZ".to_i(0).should == 0xFA
+ "-0xFAZ".to_i(0).should == -0xFA
+ end
+
+ it "auto-detects base 10 with no base specifier when base = 0" do
+ "1234567890ABC".to_i(0).should == 1234567890
+ "-1234567890ABC".to_i(0).should == -1234567890
+ end
+
+ it "doesn't handle foreign base specifiers when base is > 0" do
+ [2, 3, 4, 8, 10].each do |base|
+ "0111".to_i(base).should == "111".to_i(base)
+
+ "0b11".to_i(base).should == (base == 2 ? 0b11 : 0)
+ "0d11".to_i(base).should == (base == 10 ? 0d11 : 0)
+ "0o11".to_i(base).should == (base == 8 ? 0o11 : 0)
+ "0xFA".to_i(base).should == 0
+ end
+
+ "0xD00D".to_i(16).should == 0xD00D
+
+ "0b11".to_i(16).should == 0xb11
+ "0d11".to_i(16).should == 0xd11
+ "0o11".to_i(25).should == 15026
+ "0x11".to_i(34).should == 38183
+
+ "0B11".to_i(16).should == 0xb11
+ "0D11".to_i(16).should == 0xd11
+ "0O11".to_i(25).should == 15026
+ "0X11".to_i(34).should == 38183
+ end
+
+ it "tries to convert the base to an integer using to_int" do
+ obj = mock('8')
+ obj.should_receive(:to_int).and_return(8)
+
+ "777".to_i(obj).should == 0777
+ end
+
+ it "requires that the sign if any appears before the base specifier" do
+ "0b-1".to_i( 2).should == 0
+ "0d-1".to_i(10).should == 0
+ "0o-1".to_i( 8).should == 0
+ "0x-1".to_i(16).should == 0
+
+ "0b-1".to_i(2).should == 0
+ "0o-1".to_i(8).should == 0
+ "0d-1".to_i(10).should == 0
+ "0x-1".to_i(16).should == 0
+ end
+
+ it "raises an ArgumentError for illegal bases (1, < 0 or > 36)" do
+ lambda { "".to_i(1) }.should raise_error(ArgumentError)
+ lambda { "".to_i(-1) }.should raise_error(ArgumentError)
+ lambda { "".to_i(37) }.should raise_error(ArgumentError)
+ end
+
+ it "returns a Fixnum for long strings with trailing spaces" do
+ "0 ".to_i.should == 0
+ "0 ".to_i.should be_an_instance_of(Fixnum)
+
+ "10 ".to_i.should == 10
+ "10 ".to_i.should be_an_instance_of(Fixnum)
+
+ "-10 ".to_i.should == -10
+ "-10 ".to_i.should be_an_instance_of(Fixnum)
+ end
+
+ it "returns a Fixnum for long strings with leading spaces" do
+ " 0".to_i.should == 0
+ " 0".to_i.should be_an_instance_of(Fixnum)
+
+ " 10".to_i.should == 10
+ " 10".to_i.should be_an_instance_of(Fixnum)
+
+ " -10".to_i.should == -10
+ " -10".to_i.should be_an_instance_of(Fixnum)
+ end
+
+ it "returns the correct Bignum for long strings" do
+ "245789127594125924165923648312749312749327482".to_i.should == 245789127594125924165923648312749312749327482
+ "-245789127594125924165923648312749312749327482".to_i.should == -245789127594125924165923648312749312749327482
+ end
+end
+
+describe "String#to_i with bases" do
+ it "parses a String in base 2" do
+ str = "10" * 50
+ str.to_i(2).to_s(2).should == str
+ end
+
+ it "parses a String in base 3" do
+ str = "120" * 33
+ str.to_i(3).to_s(3).should == str
+ end
+
+ it "parses a String in base 4" do
+ str = "1230" * 25
+ str.to_i(4).to_s(4).should == str
+ end
+
+ it "parses a String in base 5" do
+ str = "12340" * 20
+ str.to_i(5).to_s(5).should == str
+ end
+
+ it "parses a String in base 6" do
+ str = "123450" * 16
+ str.to_i(6).to_s(6).should == str
+ end
+
+ it "parses a String in base 7" do
+ str = "1234560" * 14
+ str.to_i(7).to_s(7).should == str
+ end
+
+ it "parses a String in base 8" do
+ str = "12345670" * 12
+ str.to_i(8).to_s(8).should == str
+ end
+
+ it "parses a String in base 9" do
+ str = "123456780" * 11
+ str.to_i(9).to_s(9).should == str
+ end
+
+ it "parses a String in base 10" do
+ str = "1234567890" * 10
+ str.to_i(10).to_s(10).should == str
+ end
+
+ it "parses a String in base 11" do
+ str = "1234567890a" * 9
+ str.to_i(11).to_s(11).should == str
+ end
+
+ it "parses a String in base 12" do
+ str = "1234567890ab" * 8
+ str.to_i(12).to_s(12).should == str
+ end
+
+ it "parses a String in base 13" do
+ str = "1234567890abc" * 7
+ str.to_i(13).to_s(13).should == str
+ end
+
+ it "parses a String in base 14" do
+ str = "1234567890abcd" * 7
+ str.to_i(14).to_s(14).should == str
+ end
+
+ it "parses a String in base 15" do
+ str = "1234567890abcde" * 6
+ str.to_i(15).to_s(15).should == str
+ end
+
+ it "parses a String in base 16" do
+ str = "1234567890abcdef" * 6
+ str.to_i(16).to_s(16).should == str
+ end
+
+ it "parses a String in base 17" do
+ str = "1234567890abcdefg" * 5
+ str.to_i(17).to_s(17).should == str
+ end
+
+ it "parses a String in base 18" do
+ str = "1234567890abcdefgh" * 5
+ str.to_i(18).to_s(18).should == str
+ end
+
+ it "parses a String in base 19" do
+ str = "1234567890abcdefghi" * 5
+ str.to_i(19).to_s(19).should == str
+ end
+
+ it "parses a String in base 20" do
+ str = "1234567890abcdefghij" * 5
+ str.to_i(20).to_s(20).should == str
+ end
+
+ it "parses a String in base 21" do
+ str = "1234567890abcdefghijk" * 4
+ str.to_i(21).to_s(21).should == str
+ end
+
+ it "parses a String in base 22" do
+ str = "1234567890abcdefghijkl" * 4
+ str.to_i(22).to_s(22).should == str
+ end
+
+ it "parses a String in base 23" do
+ str = "1234567890abcdefghijklm" * 4
+ str.to_i(23).to_s(23).should == str
+ end
+
+ it "parses a String in base 24" do
+ str = "1234567890abcdefghijklmn" * 4
+ str.to_i(24).to_s(24).should == str
+ end
+
+ it "parses a String in base 25" do
+ str = "1234567890abcdefghijklmno" * 4
+ str.to_i(25).to_s(25).should == str
+ end
+
+ it "parses a String in base 26" do
+ str = "1234567890abcdefghijklmnop" * 3
+ str.to_i(26).to_s(26).should == str
+ end
+
+ it "parses a String in base 27" do
+ str = "1234567890abcdefghijklmnopq" * 3
+ str.to_i(27).to_s(27).should == str
+ end
+
+ it "parses a String in base 28" do
+ str = "1234567890abcdefghijklmnopqr" * 3
+ str.to_i(28).to_s(28).should == str
+ end
+
+ it "parses a String in base 29" do
+ str = "1234567890abcdefghijklmnopqrs" * 3
+ str.to_i(29).to_s(29).should == str
+ end
+
+ it "parses a String in base 30" do
+ str = "1234567890abcdefghijklmnopqrst" * 3
+ str.to_i(30).to_s(30).should == str
+ end
+
+ it "parses a String in base 31" do
+ str = "1234567890abcdefghijklmnopqrstu" * 3
+ str.to_i(31).to_s(31).should == str
+ end
+
+ it "parses a String in base 32" do
+ str = "1234567890abcdefghijklmnopqrstuv" * 3
+ str.to_i(32).to_s(32).should == str
+ end
+
+ it "parses a String in base 33" do
+ str = "1234567890abcdefghijklmnopqrstuvw" * 3
+ str.to_i(33).to_s(33).should == str
+ end
+
+ it "parses a String in base 34" do
+ str = "1234567890abcdefghijklmnopqrstuvwx" * 2
+ str.to_i(34).to_s(34).should == str
+ end
+
+ it "parses a String in base 35" do
+ str = "1234567890abcdefghijklmnopqrstuvwxy" * 2
+ str.to_i(35).to_s(35).should == str
+ end
+
+ it "parses a String in base 36" do
+ str = "1234567890abcdefghijklmnopqrstuvwxyz" * 2
+ str.to_i(36).to_s(36).should == str
+ end
+end
diff --git a/spec/ruby/core/string/to_r_spec.rb b/spec/ruby/core/string/to_r_spec.rb
new file mode 100644
index 0000000000..9f174a2f55
--- /dev/null
+++ b/spec/ruby/core/string/to_r_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../../spec_helper'
+
+describe "String#to_r" do
+ it "returns a Rational object" do
+ String.new.to_r.should be_an_instance_of(Rational)
+ end
+
+ it "returns (0/1) for the empty String" do
+ "".to_r.should == Rational(0, 1)
+ end
+
+ it "returns (n/1) for a String starting with a decimal _n_" do
+ "2".to_r.should == Rational(2, 1)
+ "1765".to_r.should == Rational(1765, 1)
+ end
+
+ it "ignores trailing characters" do
+ "2 foo".to_r.should == Rational(2, 1)
+ "1765, ".to_r.should == Rational(1765, 1)
+ end
+
+ it "ignores leading spaces" do
+ " 2".to_r.should == Rational(2, 1)
+ " 1765, ".to_r.should == Rational(1765, 1)
+ end
+
+ it "does not ignore arbitrary, non-numeric leading characters" do
+ "The rational form of 33 is...".to_r.should_not == Rational(33, 1)
+ "a1765, ".to_r.should_not == Rational(1765, 1)
+ end
+
+ it "treats leading hypens as minus signs" do
+ "-20".to_r.should == Rational(-20, 1)
+ end
+
+ it "does not treat a leading period without a numeric prefix as a decimal point" do
+ ".9".to_r.should_not == Rational(8106479329266893, 9007199254740992)
+ end
+
+ it "understands decimal points" do
+ "3.33".to_r.should == Rational(333, 100)
+ "-3.33".to_r.should == Rational(-333, 100)
+ end
+
+ it "ignores underscores between numbers" do
+ "190_22".to_r.should == Rational(19022, 1)
+ "-190_22.7".to_r.should == Rational(-190227, 10)
+ end
+
+ it "understands a forward slash as separating the numerator from the denominator" do
+ "20/3".to_r.should == Rational(20, 3)
+ " -19.10/3".to_r.should == Rational(-191, 30)
+ end
+
+ it "returns (0/1) for Strings it can't parse" do
+ "glark".to_r.should == Rational(0,1)
+ end
+end
diff --git a/spec/ruby/core/string/to_s_spec.rb b/spec/ruby/core/string/to_s_spec.rb
new file mode 100644
index 0000000000..e5872745a8
--- /dev/null
+++ b/spec/ruby/core/string/to_s_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/to_s'
+
+describe "String#to_s" do
+ it_behaves_like :string_to_s, :to_s
+end
diff --git a/spec/ruby/core/string/to_str_spec.rb b/spec/ruby/core/string/to_str_spec.rb
new file mode 100644
index 0000000000..e24262a7ae
--- /dev/null
+++ b/spec/ruby/core/string/to_str_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/to_s'
+
+describe "String#to_str" do
+ it_behaves_like :string_to_s, :to_str
+end
diff --git a/spec/ruby/core/string/to_sym_spec.rb b/spec/ruby/core/string/to_sym_spec.rb
new file mode 100644
index 0000000000..f9135211ce
--- /dev/null
+++ b/spec/ruby/core/string/to_sym_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/to_sym'
+
+describe "String#to_sym" do
+ it_behaves_like :string_to_sym, :to_sym
+end
diff --git a/spec/ruby/core/string/tr_s_spec.rb b/spec/ruby/core/string/tr_s_spec.rb
new file mode 100644
index 0000000000..87635acf2c
--- /dev/null
+++ b/spec/ruby/core/string/tr_s_spec.rb
@@ -0,0 +1,136 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#tr_s" do
+ it "returns a string processed according to tr with newly duplicate characters removed" do
+ "hello".tr_s('l', 'r').should == "hero"
+ "hello".tr_s('el', '*').should == "h*o"
+ "hello".tr_s('el', 'hx').should == "hhxo"
+ "hello".tr_s('o', '.').should == "hell."
+ end
+
+ it "accepts c1-c2 notation to denote ranges of characters" do
+ "hello".tr_s('a-y', 'b-z').should == "ifmp"
+ "123456789".tr_s("2-5", "abcdefg").should == "1abcd6789"
+ "hello ^--^".tr_s("e-", "__").should == "h_llo ^_^"
+ "hello ^--^".tr_s("---", "_").should == "hello ^_^"
+ end
+
+ it "pads to_str with its last char if it is shorter than from_string" do
+ "this".tr_s("this", "x").should == "x"
+ end
+
+ it "translates chars not in from_string when it starts with a ^" do
+ "hello".tr_s('^aeiou', '*').should == "*e*o"
+ "123456789".tr_s("^345", "abc").should == "c345c"
+ "abcdefghijk".tr_s("^d-g", "9131").should == "1defg1"
+
+ "hello ^_^".tr_s("a-e^e", ".").should == "h.llo ._."
+ "hello ^_^".tr_s("^^", ".").should == ".^.^"
+ "hello ^_^".tr_s("^", "x").should == "hello x_x"
+ "hello ^-^".tr_s("^-^", "x").should == "x^-^"
+ "hello ^-^".tr_s("^^-^", "x").should == "x^x^"
+ "hello ^-^".tr_s("^---", "x").should == "x-x"
+ "hello ^-^".tr_s("^---l-o", "x").should == "xllox-x"
+ end
+
+ it "tries to convert from_str and to_str to strings using to_str" do
+ from_str = mock('ab')
+ from_str.should_receive(:to_str).and_return("ab")
+
+ to_str = mock('AB')
+ to_str.should_receive(:to_str).and_return("AB")
+
+ "bla".tr_s(from_str, to_str).should == "BlA"
+ end
+
+ it "returns subclass instances when called on a subclass" do
+ StringSpecs::MyString.new("hello").tr_s("e", "a").should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ it "taints the result when self is tainted" do
+ ["h", "hello"].each do |str|
+ tainted_str = str.dup.taint
+
+ tainted_str.tr_s("e", "a").tainted?.should == true
+
+ str.tr_s("e".taint, "a").tainted?.should == false
+ str.tr_s("e", "a".taint).tainted?.should == false
+ end
+ end
+
+ with_feature :encoding do
+ # http://redmine.ruby-lang.org/issues/show/1839
+ it "can replace a 7-bit ASCII character with a multibyte one" do
+ a = "uber"
+ a.encoding.should == Encoding::UTF_8
+ b = a.tr_s("u","ü")
+ b.should == "über"
+ b.encoding.should == Encoding::UTF_8
+ end
+
+ it "can replace multiple 7-bit ASCII characters with a multibyte one" do
+ a = "uuuber"
+ a.encoding.should == Encoding::UTF_8
+ b = a.tr_s("u","ü")
+ b.should == "über"
+ b.encoding.should == Encoding::UTF_8
+ end
+
+ it "can replace a multibyte character with a single byte one" do
+ a = "über"
+ a.encoding.should == Encoding::UTF_8
+ b = a.tr_s("ü","u")
+ b.should == "uber"
+ b.encoding.should == Encoding::UTF_8
+ end
+
+ it "can replace multiple multibyte characters with a single byte one" do
+ a = "üüüber"
+ a.encoding.should == Encoding::UTF_8
+ b = a.tr_s("ü","u")
+ b.should == "uber"
+ b.encoding.should == Encoding::UTF_8
+ end
+
+ it "does not replace a multibyte character where part of the bytes match the tr string" do
+ str = "æ¤Žåæ·±å¤"
+ a = "\u0080\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008A\u008B\u008C\u008E\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009E\u009F"
+ b = "€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“â€â€¢â€“—˜™š›œžŸ"
+ str.tr_s(a, b).should == "æ¤Žåæ·±å¤"
+ end
+
+ end
+
+end
+
+describe "String#tr_s!" do
+ it "modifies self in place" do
+ s = "hello"
+ s.tr_s!("l", "r").should == "hero"
+ s.should == "hero"
+ end
+
+ it "returns nil if no modification was made" do
+ s = "hello"
+ s.tr_s!("za", "yb").should == nil
+ s.tr_s!("", "").should == nil
+ s.should == "hello"
+ end
+
+ it "does not modify self if from_str is empty" do
+ s = "hello"
+ s.tr_s!("", "").should == nil
+ s.should == "hello"
+ s.tr_s!("", "yb").should == nil
+ s.should == "hello"
+ end
+
+ it "raises a #{frozen_error_class} if self is frozen" do
+ s = "hello".freeze
+ lambda { s.tr_s!("el", "ar") }.should raise_error(frozen_error_class)
+ lambda { s.tr_s!("l", "r") }.should raise_error(frozen_error_class)
+ lambda { s.tr_s!("", "") }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/string/tr_spec.rb b/spec/ruby/core/string/tr_spec.rb
new file mode 100644
index 0000000000..efd5a7f638
--- /dev/null
+++ b/spec/ruby/core/string/tr_spec.rb
@@ -0,0 +1,131 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#tr" do
+ it "returns a new string with the characters from from_string replaced by the ones in to_string" do
+ "hello".tr('aeiou', '*').should == "h*ll*"
+ "hello".tr('el', 'ip').should == "hippo"
+ "Lisp".tr("Lisp", "Ruby").should == "Ruby"
+ end
+
+ it "accepts c1-c2 notation to denote ranges of characters" do
+ "hello".tr('a-y', 'b-z').should == "ifmmp"
+ "123456789".tr("2-5","abcdefg").should == "1abcd6789"
+ "hello ^-^".tr("e-", "__").should == "h_llo ^_^"
+ "hello ^-^".tr("---", "_").should == "hello ^_^"
+ end
+
+ it "pads to_str with its last char if it is shorter than from_string" do
+ "this".tr("this", "x").should == "xxxx"
+ "hello".tr("a-z", "A-H.").should == "HE..."
+ end
+
+ it "raises an ArgumentError a descending range in the replacement as containing just the start character" do
+ lambda { "hello".tr("a-y", "z-b") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError a descending range in the source as empty" do
+ lambda { "hello".tr("l-a", "z") }.should raise_error(ArgumentError)
+ end
+
+ it "translates chars not in from_string when it starts with a ^" do
+ "hello".tr('^aeiou', '*').should == "*e**o"
+ "123456789".tr("^345", "abc").should == "cc345cccc"
+ "abcdefghijk".tr("^d-g", "9131").should == "111defg1111"
+
+ "hello ^_^".tr("a-e^e", ".").should == "h.llo ._."
+ "hello ^_^".tr("^^", ".").should == "......^.^"
+ "hello ^_^".tr("^", "x").should == "hello x_x"
+ "hello ^-^".tr("^-^", "x").should == "xxxxxx^-^"
+ "hello ^-^".tr("^^-^", "x").should == "xxxxxx^x^"
+ "hello ^-^".tr("^---", "x").should == "xxxxxxx-x"
+ "hello ^-^".tr("^---l-o", "x").should == "xxlloxx-x"
+ end
+
+ it "supports non-injective replacements" do
+ "hello".tr("helo", "1212").should == "12112"
+ end
+
+ it "tries to convert from_str and to_str to strings using to_str" do
+ from_str = mock('ab')
+ from_str.should_receive(:to_str).and_return("ab")
+
+ to_str = mock('AB')
+ to_str.should_receive(:to_str).and_return("AB")
+
+ "bla".tr(from_str, to_str).should == "BlA"
+ end
+
+ it "returns subclass instances when called on a subclass" do
+ StringSpecs::MyString.new("hello").tr("e", "a").should be_an_instance_of(StringSpecs::MyString)
+ end
+
+ it "taints the result when self is tainted" do
+ ["h", "hello"].each do |str|
+ tainted_str = str.dup.taint
+
+ tainted_str.tr("e", "a").tainted?.should == true
+
+ str.tr("e".taint, "a").tainted?.should == false
+ str.tr("e", "a".taint).tainted?.should == false
+ end
+ end
+
+ with_feature :encoding do
+ # http://redmine.ruby-lang.org/issues/show/1839
+ it "can replace a 7-bit ASCII character with a multibyte one" do
+ a = "uber"
+ a.encoding.should == Encoding::UTF_8
+ b = a.tr("u","ü")
+ b.should == "über"
+ b.encoding.should == Encoding::UTF_8
+ end
+
+ it "can replace a multibyte character with a single byte one" do
+ a = "über"
+ a.encoding.should == Encoding::UTF_8
+ b = a.tr("ü","u")
+ b.should == "uber"
+ b.encoding.should == Encoding::UTF_8
+ end
+
+ it "does not replace a multibyte character where part of the bytes match the tr string" do
+ str = "æ¤Žåæ·±å¤"
+ a = "\u0080\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008A\u008B\u008C\u008E\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009E\u009F"
+ b = "€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“â€â€¢â€“—˜™š›œžŸ"
+ str.tr(a, b).should == "æ¤Žåæ·±å¤"
+ end
+
+ end
+end
+
+describe "String#tr!" do
+ it "modifies self in place" do
+ s = "abcdefghijklmnopqR"
+ s.tr!("cdefg", "12").should == "ab12222hijklmnopqR"
+ s.should == "ab12222hijklmnopqR"
+ end
+
+ it "returns nil if no modification was made" do
+ s = "hello"
+ s.tr!("za", "yb").should == nil
+ s.tr!("", "").should == nil
+ s.should == "hello"
+ end
+
+ it "does not modify self if from_str is empty" do
+ s = "hello"
+ s.tr!("", "").should == nil
+ s.should == "hello"
+ s.tr!("", "yb").should == nil
+ s.should == "hello"
+ end
+
+ it "raises a #{frozen_error_class} if self is frozen" do
+ s = "abcdefghijklmnopqR".freeze
+ lambda { s.tr!("cdefg", "12") }.should raise_error(frozen_error_class)
+ lambda { s.tr!("R", "S") }.should raise_error(frozen_error_class)
+ lambda { s.tr!("", "") }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/string/try_convert_spec.rb b/spec/ruby/core/string/try_convert_spec.rb
new file mode 100644
index 0000000000..629817110e
--- /dev/null
+++ b/spec/ruby/core/string/try_convert_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String.try_convert" do
+ it "returns the argument if it's a String" do
+ x = String.new
+ String.try_convert(x).should equal(x)
+ end
+
+ it "returns the argument if it's a kind of String" do
+ x = StringSpecs::MyString.new
+ String.try_convert(x).should equal(x)
+ end
+
+ it "returns nil when the argument does not respond to #to_str" do
+ String.try_convert(Object.new).should be_nil
+ end
+
+ it "sends #to_str to the argument and returns the result if it's nil" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(nil)
+ String.try_convert(obj).should be_nil
+ end
+
+ it "sends #to_str to the argument and returns the result if it's a String" do
+ x = String.new
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(x)
+ String.try_convert(obj).should equal(x)
+ end
+
+ it "sends #to_str to the argument and returns the result if it's a kind of String" do
+ x = StringSpecs::MyString.new
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(x)
+ String.try_convert(obj).should equal(x)
+ end
+
+ it "sends #to_str to the argument and raises TypeError if it's not a kind of String" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(Object.new)
+ lambda { String.try_convert obj }.should raise_error(TypeError)
+ end
+
+ it "does not rescue exceptions raised by #to_str" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_raise(RuntimeError)
+ lambda { String.try_convert obj }.should raise_error(RuntimeError)
+ end
+end
diff --git a/spec/ruby/core/string/uminus_spec.rb b/spec/ruby/core/string/uminus_spec.rb
new file mode 100644
index 0000000000..fd9e8c1d52
--- /dev/null
+++ b/spec/ruby/core/string/uminus_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../spec_helper'
+
+describe 'String#-@' do
+ it 'returns self if the String is frozen' do
+ input = 'foo'.freeze
+ output = -input
+
+ output.should equal(input)
+ output.frozen?.should == true
+ end
+
+ it 'returns a frozen copy if the String is not frozen' do
+ input = 'foo'
+ output = -input
+
+ output.frozen?.should == true
+ output.should_not equal(input)
+ output.should == 'foo'
+ end
+
+ ruby_version_is "2.5" do
+ it "returns the same object for equal unfrozen strings" do
+ origin = "this is a string"
+ dynamic = %w(this is a string).join(' ')
+
+ origin.should_not equal(dynamic)
+ (-origin).should equal(-dynamic)
+ end
+
+ it "returns the same object when it's called on the same String literal" do
+ (-"unfrozen string").should equal(-"unfrozen string")
+ (-"unfrozen string").should_not equal(-"another unfrozen string")
+ end
+ end
+
+ ruby_version_is "2.5"..."2.6" do
+ it "does not deduplicate already frozen strings" do
+ dynamic = %w(this string is frozen).join(' ').freeze
+
+ dynamic.should_not equal("this string is frozen".freeze)
+
+ (-dynamic).should_not equal("this string is frozen".freeze)
+ (-dynamic).should_not equal(-"this string is frozen".freeze)
+ end
+ end
+
+ ruby_version_is "2.6" do
+ it "deduplicates frozen strings" do
+ dynamic = %w(this string is frozen).join(' ').freeze
+
+ dynamic.should_not equal("this string is frozen".freeze)
+
+ (-dynamic).should equal("this string is frozen".freeze)
+ (-dynamic).should equal(-"this string is frozen".freeze)
+ end
+ end
+end
diff --git a/spec/ruby/core/string/undump_spec.rb b/spec/ruby/core/string/undump_spec.rb
new file mode 100644
index 0000000000..ec13308c16
--- /dev/null
+++ b/spec/ruby/core/string/undump_spec.rb
@@ -0,0 +1,451 @@
+# encoding: utf-8
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is '2.5' do
+ describe "String#undump" do
+ it "taints the result if self is tainted" do
+ '"foo"'.taint.undump.tainted?.should == true
+ end
+
+ it "untrusts the result if self is untrusted" do
+ '"foo"'.untrust.undump.untrusted?.should == true
+ end
+
+ it "does not take into account if a string is frozen" do
+ '"foo"'.freeze.undump.frozen?.should == false
+ end
+
+ it "always returns String instance" do
+ StringSpecs::MyString.new('"foo"').undump.should be_an_instance_of(String)
+ end
+
+ it "strips outer \"" do
+ '"foo"'.undump.should == 'foo'
+ end
+
+ it "returns a string with special characters in \\<char> notation replaced with the characters" do
+ [ ['"\\a"', "\a"],
+ ['"\\b"', "\b"],
+ ['"\\t"', "\t"],
+ ['"\\n"', "\n"],
+ ['"\\v"', "\v"],
+ ['"\\f"', "\f"],
+ ['"\\r"', "\r"],
+ ['"\\e"', "\e"]
+ ].should be_computed_by(:undump)
+ end
+
+ it "returns a string with unescaped sequencies \" and \\" do
+ [ ['"\\""' , "\""],
+ ['"\\\\"', "\\"]
+ ].should be_computed_by(:undump)
+ end
+
+ it "returns a string with unescaped sequences \\#<char> when # is followed by $, @, {" do
+ [ ['"\\#$PATH"', "\#$PATH"],
+ ['"\\#@a"', "\#@a"],
+ ['"\\#@@a"', "\#@@a"],
+ ['"\\#{a}"', "\#{a}"]
+ ].should be_computed_by(:undump)
+ end
+
+ it "returns a string with # not escaped when followed by any other character" do
+ [ ['"#"', '#'],
+ ['"#1"', '#1']
+ ].should be_computed_by(:undump)
+ end
+
+ it "returns a string with printable non-alphanumeric characters" do
+ [ ['" "', ' '],
+ ['"!"', '!'],
+ ['"$"', '$'],
+ ['"%"', '%'],
+ ['"&"', '&'],
+ ['"\'"', '\''],
+ ['"("', '('],
+ ['")"', ')'],
+ ['"*"', '*'],
+ ['"+"', '+'],
+ ['","', ','],
+ ['"-"', '-'],
+ ['"."', '.'],
+ ['"/"', '/'],
+ ['":"', ':'],
+ ['";"', ';'],
+ ['"<"', '<'],
+ ['"="', '='],
+ ['">"', '>'],
+ ['"?"', '?'],
+ ['"@"', '@'],
+ ['"["', '['],
+ ['"]"', ']'],
+ ['"^"', '^'],
+ ['"_"', '_'],
+ ['"`"', '`'],
+ ['"{"', '{'],
+ ['"|"', '|'],
+ ['"}"', '}'],
+ ['"~"', '~']
+ ].should be_computed_by(:undump)
+ end
+
+ it "returns a string with numeric characters unescaped" do
+ [ ['"0"', "0"],
+ ['"1"', "1"],
+ ['"2"', "2"],
+ ['"3"', "3"],
+ ['"4"', "4"],
+ ['"5"', "5"],
+ ['"6"', "6"],
+ ['"7"', "7"],
+ ['"8"', "8"],
+ ['"9"', "9"],
+ ].should be_computed_by(:undump)
+ end
+
+ it "returns a string with upper-case alpha characters unescaped" do
+ [ ['"A"', 'A'],
+ ['"B"', 'B'],
+ ['"C"', 'C'],
+ ['"D"', 'D'],
+ ['"E"', 'E'],
+ ['"F"', 'F'],
+ ['"G"', 'G'],
+ ['"H"', 'H'],
+ ['"I"', 'I'],
+ ['"J"', 'J'],
+ ['"K"', 'K'],
+ ['"L"', 'L'],
+ ['"M"', 'M'],
+ ['"N"', 'N'],
+ ['"O"', 'O'],
+ ['"P"', 'P'],
+ ['"Q"', 'Q'],
+ ['"R"', 'R'],
+ ['"S"', 'S'],
+ ['"T"', 'T'],
+ ['"U"', 'U'],
+ ['"V"', 'V'],
+ ['"W"', 'W'],
+ ['"X"', 'X'],
+ ['"Y"', 'Y'],
+ ['"Z"', 'Z']
+ ].should be_computed_by(:undump)
+ end
+
+ it "returns a string with lower-case alpha characters unescaped" do
+ [ ['"a"', 'a'],
+ ['"b"', 'b'],
+ ['"c"', 'c'],
+ ['"d"', 'd'],
+ ['"e"', 'e'],
+ ['"f"', 'f'],
+ ['"g"', 'g'],
+ ['"h"', 'h'],
+ ['"i"', 'i'],
+ ['"j"', 'j'],
+ ['"k"', 'k'],
+ ['"l"', 'l'],
+ ['"m"', 'm'],
+ ['"n"', 'n'],
+ ['"o"', 'o'],
+ ['"p"', 'p'],
+ ['"q"', 'q'],
+ ['"r"', 'r'],
+ ['"s"', 's'],
+ ['"t"', 't'],
+ ['"u"', 'u'],
+ ['"v"', 'v'],
+ ['"w"', 'w'],
+ ['"x"', 'x'],
+ ['"y"', 'y'],
+ ['"z"', 'z']
+ ].should be_computed_by(:undump)
+ end
+
+ it "returns a string with \\x notation replaced with non-printing ASCII character" do
+ [ ['"\\x00"', 0000.chr.force_encoding('utf-8')],
+ ['"\\x01"', 0001.chr.force_encoding('utf-8')],
+ ['"\\x02"', 0002.chr.force_encoding('utf-8')],
+ ['"\\x03"', 0003.chr.force_encoding('utf-8')],
+ ['"\\x04"', 0004.chr.force_encoding('utf-8')],
+ ['"\\x05"', 0005.chr.force_encoding('utf-8')],
+ ['"\\x06"', 0006.chr.force_encoding('utf-8')],
+ ['"\\x0E"', 0016.chr.force_encoding('utf-8')],
+ ['"\\x0F"', 0017.chr.force_encoding('utf-8')],
+ ['"\\x10"', 0020.chr.force_encoding('utf-8')],
+ ['"\\x11"', 0021.chr.force_encoding('utf-8')],
+ ['"\\x12"', 0022.chr.force_encoding('utf-8')],
+ ['"\\x13"', 0023.chr.force_encoding('utf-8')],
+ ['"\\x14"', 0024.chr.force_encoding('utf-8')],
+ ['"\\x15"', 0025.chr.force_encoding('utf-8')],
+ ['"\\x16"', 0026.chr.force_encoding('utf-8')],
+ ['"\\x17"', 0027.chr.force_encoding('utf-8')],
+ ['"\\x18"', 0030.chr.force_encoding('utf-8')],
+ ['"\\x19"', 0031.chr.force_encoding('utf-8')],
+ ['"\\x1A"', 0032.chr.force_encoding('utf-8')],
+ ['"\\x1C"', 0034.chr.force_encoding('utf-8')],
+ ['"\\x1D"', 0035.chr.force_encoding('utf-8')],
+ ['"\\x1E"', 0036.chr.force_encoding('utf-8')],
+ ['"\\x1F"', 0037.chr.force_encoding('utf-8')],
+ ['"\\x7F"', 0177.chr.force_encoding('utf-8')],
+ ['"\\x80"', 0200.chr.force_encoding('utf-8')],
+ ['"\\x81"', 0201.chr.force_encoding('utf-8')],
+ ['"\\x82"', 0202.chr.force_encoding('utf-8')],
+ ['"\\x83"', 0203.chr.force_encoding('utf-8')],
+ ['"\\x84"', 0204.chr.force_encoding('utf-8')],
+ ['"\\x85"', 0205.chr.force_encoding('utf-8')],
+ ['"\\x86"', 0206.chr.force_encoding('utf-8')],
+ ['"\\x87"', 0207.chr.force_encoding('utf-8')],
+ ['"\\x88"', 0210.chr.force_encoding('utf-8')],
+ ['"\\x89"', 0211.chr.force_encoding('utf-8')],
+ ['"\\x8A"', 0212.chr.force_encoding('utf-8')],
+ ['"\\x8B"', 0213.chr.force_encoding('utf-8')],
+ ['"\\x8C"', 0214.chr.force_encoding('utf-8')],
+ ['"\\x8D"', 0215.chr.force_encoding('utf-8')],
+ ['"\\x8E"', 0216.chr.force_encoding('utf-8')],
+ ['"\\x8F"', 0217.chr.force_encoding('utf-8')],
+ ['"\\x90"', 0220.chr.force_encoding('utf-8')],
+ ['"\\x91"', 0221.chr.force_encoding('utf-8')],
+ ['"\\x92"', 0222.chr.force_encoding('utf-8')],
+ ['"\\x93"', 0223.chr.force_encoding('utf-8')],
+ ['"\\x94"', 0224.chr.force_encoding('utf-8')],
+ ['"\\x95"', 0225.chr.force_encoding('utf-8')],
+ ['"\\x96"', 0226.chr.force_encoding('utf-8')],
+ ['"\\x97"', 0227.chr.force_encoding('utf-8')],
+ ['"\\x98"', 0230.chr.force_encoding('utf-8')],
+ ['"\\x99"', 0231.chr.force_encoding('utf-8')],
+ ['"\\x9A"', 0232.chr.force_encoding('utf-8')],
+ ['"\\x9B"', 0233.chr.force_encoding('utf-8')],
+ ['"\\x9C"', 0234.chr.force_encoding('utf-8')],
+ ['"\\x9D"', 0235.chr.force_encoding('utf-8')],
+ ['"\\x9E"', 0236.chr.force_encoding('utf-8')],
+ ['"\\x9F"', 0237.chr.force_encoding('utf-8')],
+ ['"\\xA0"', 0240.chr.force_encoding('utf-8')],
+ ['"\\xA1"', 0241.chr.force_encoding('utf-8')],
+ ['"\\xA2"', 0242.chr.force_encoding('utf-8')],
+ ['"\\xA3"', 0243.chr.force_encoding('utf-8')],
+ ['"\\xA4"', 0244.chr.force_encoding('utf-8')],
+ ['"\\xA5"', 0245.chr.force_encoding('utf-8')],
+ ['"\\xA6"', 0246.chr.force_encoding('utf-8')],
+ ['"\\xA7"', 0247.chr.force_encoding('utf-8')],
+ ['"\\xA8"', 0250.chr.force_encoding('utf-8')],
+ ['"\\xA9"', 0251.chr.force_encoding('utf-8')],
+ ['"\\xAA"', 0252.chr.force_encoding('utf-8')],
+ ['"\\xAB"', 0253.chr.force_encoding('utf-8')],
+ ['"\\xAC"', 0254.chr.force_encoding('utf-8')],
+ ['"\\xAD"', 0255.chr.force_encoding('utf-8')],
+ ['"\\xAE"', 0256.chr.force_encoding('utf-8')],
+ ['"\\xAF"', 0257.chr.force_encoding('utf-8')],
+ ['"\\xB0"', 0260.chr.force_encoding('utf-8')],
+ ['"\\xB1"', 0261.chr.force_encoding('utf-8')],
+ ['"\\xB2"', 0262.chr.force_encoding('utf-8')],
+ ['"\\xB3"', 0263.chr.force_encoding('utf-8')],
+ ['"\\xB4"', 0264.chr.force_encoding('utf-8')],
+ ['"\\xB5"', 0265.chr.force_encoding('utf-8')],
+ ['"\\xB6"', 0266.chr.force_encoding('utf-8')],
+ ['"\\xB7"', 0267.chr.force_encoding('utf-8')],
+ ['"\\xB8"', 0270.chr.force_encoding('utf-8')],
+ ['"\\xB9"', 0271.chr.force_encoding('utf-8')],
+ ['"\\xBA"', 0272.chr.force_encoding('utf-8')],
+ ['"\\xBB"', 0273.chr.force_encoding('utf-8')],
+ ['"\\xBC"', 0274.chr.force_encoding('utf-8')],
+ ['"\\xBD"', 0275.chr.force_encoding('utf-8')],
+ ['"\\xBE"', 0276.chr.force_encoding('utf-8')],
+ ['"\\xBF"', 0277.chr.force_encoding('utf-8')],
+ ['"\\xC0"', 0300.chr.force_encoding('utf-8')],
+ ['"\\xC1"', 0301.chr.force_encoding('utf-8')],
+ ['"\\xC2"', 0302.chr.force_encoding('utf-8')],
+ ['"\\xC3"', 0303.chr.force_encoding('utf-8')],
+ ['"\\xC4"', 0304.chr.force_encoding('utf-8')],
+ ['"\\xC5"', 0305.chr.force_encoding('utf-8')],
+ ['"\\xC6"', 0306.chr.force_encoding('utf-8')],
+ ['"\\xC7"', 0307.chr.force_encoding('utf-8')],
+ ['"\\xC8"', 0310.chr.force_encoding('utf-8')],
+ ['"\\xC9"', 0311.chr.force_encoding('utf-8')],
+ ['"\\xCA"', 0312.chr.force_encoding('utf-8')],
+ ['"\\xCB"', 0313.chr.force_encoding('utf-8')],
+ ['"\\xCC"', 0314.chr.force_encoding('utf-8')],
+ ['"\\xCD"', 0315.chr.force_encoding('utf-8')],
+ ['"\\xCE"', 0316.chr.force_encoding('utf-8')],
+ ['"\\xCF"', 0317.chr.force_encoding('utf-8')],
+ ['"\\xD0"', 0320.chr.force_encoding('utf-8')],
+ ['"\\xD1"', 0321.chr.force_encoding('utf-8')],
+ ['"\\xD2"', 0322.chr.force_encoding('utf-8')],
+ ['"\\xD3"', 0323.chr.force_encoding('utf-8')],
+ ['"\\xD4"', 0324.chr.force_encoding('utf-8')],
+ ['"\\xD5"', 0325.chr.force_encoding('utf-8')],
+ ['"\\xD6"', 0326.chr.force_encoding('utf-8')],
+ ['"\\xD7"', 0327.chr.force_encoding('utf-8')],
+ ['"\\xD8"', 0330.chr.force_encoding('utf-8')],
+ ['"\\xD9"', 0331.chr.force_encoding('utf-8')],
+ ['"\\xDA"', 0332.chr.force_encoding('utf-8')],
+ ['"\\xDB"', 0333.chr.force_encoding('utf-8')],
+ ['"\\xDC"', 0334.chr.force_encoding('utf-8')],
+ ['"\\xDD"', 0335.chr.force_encoding('utf-8')],
+ ['"\\xDE"', 0336.chr.force_encoding('utf-8')],
+ ['"\\xDF"', 0337.chr.force_encoding('utf-8')],
+ ['"\\xE0"', 0340.chr.force_encoding('utf-8')],
+ ['"\\xE1"', 0341.chr.force_encoding('utf-8')],
+ ['"\\xE2"', 0342.chr.force_encoding('utf-8')],
+ ['"\\xE3"', 0343.chr.force_encoding('utf-8')],
+ ['"\\xE4"', 0344.chr.force_encoding('utf-8')],
+ ['"\\xE5"', 0345.chr.force_encoding('utf-8')],
+ ['"\\xE6"', 0346.chr.force_encoding('utf-8')],
+ ['"\\xE7"', 0347.chr.force_encoding('utf-8')],
+ ['"\\xE8"', 0350.chr.force_encoding('utf-8')],
+ ['"\\xE9"', 0351.chr.force_encoding('utf-8')],
+ ['"\\xEA"', 0352.chr.force_encoding('utf-8')],
+ ['"\\xEB"', 0353.chr.force_encoding('utf-8')],
+ ['"\\xEC"', 0354.chr.force_encoding('utf-8')],
+ ['"\\xED"', 0355.chr.force_encoding('utf-8')],
+ ['"\\xEE"', 0356.chr.force_encoding('utf-8')],
+ ['"\\xEF"', 0357.chr.force_encoding('utf-8')],
+ ['"\\xF0"', 0360.chr.force_encoding('utf-8')],
+ ['"\\xF1"', 0361.chr.force_encoding('utf-8')],
+ ['"\\xF2"', 0362.chr.force_encoding('utf-8')],
+ ['"\\xF3"', 0363.chr.force_encoding('utf-8')],
+ ['"\\xF4"', 0364.chr.force_encoding('utf-8')],
+ ['"\\xF5"', 0365.chr.force_encoding('utf-8')],
+ ['"\\xF6"', 0366.chr.force_encoding('utf-8')],
+ ['"\\xF7"', 0367.chr.force_encoding('utf-8')],
+ ['"\\xF8"', 0370.chr.force_encoding('utf-8')],
+ ['"\\xF9"', 0371.chr.force_encoding('utf-8')],
+ ['"\\xFA"', 0372.chr.force_encoding('utf-8')],
+ ['"\\xFB"', 0373.chr.force_encoding('utf-8')],
+ ['"\\xFC"', 0374.chr.force_encoding('utf-8')],
+ ['"\\xFD"', 0375.chr.force_encoding('utf-8')],
+ ['"\\xFE"', 0376.chr.force_encoding('utf-8')],
+ ['"\\xFF"', 0377.chr.force_encoding('utf-8')]
+ ].should be_computed_by(:undump)
+ end
+
+ it "returns a string with \\u{} notation replaced with multi-byte UTF-8 characters" do
+ [ ['"\u{80}"', 0200.chr('utf-8')],
+ ['"\u{81}"', 0201.chr('utf-8')],
+ ['"\u{82}"', 0202.chr('utf-8')],
+ ['"\u{83}"', 0203.chr('utf-8')],
+ ['"\u{84}"', 0204.chr('utf-8')],
+ ['"\u{86}"', 0206.chr('utf-8')],
+ ['"\u{87}"', 0207.chr('utf-8')],
+ ['"\u{88}"', 0210.chr('utf-8')],
+ ['"\u{89}"', 0211.chr('utf-8')],
+ ['"\u{8a}"', 0212.chr('utf-8')],
+ ['"\u{8b}"', 0213.chr('utf-8')],
+ ['"\u{8c}"', 0214.chr('utf-8')],
+ ['"\u{8d}"', 0215.chr('utf-8')],
+ ['"\u{8e}"', 0216.chr('utf-8')],
+ ['"\u{8f}"', 0217.chr('utf-8')],
+ ['"\u{90}"', 0220.chr('utf-8')],
+ ['"\u{91}"', 0221.chr('utf-8')],
+ ['"\u{92}"', 0222.chr('utf-8')],
+ ['"\u{93}"', 0223.chr('utf-8')],
+ ['"\u{94}"', 0224.chr('utf-8')],
+ ['"\u{95}"', 0225.chr('utf-8')],
+ ['"\u{96}"', 0226.chr('utf-8')],
+ ['"\u{97}"', 0227.chr('utf-8')],
+ ['"\u{98}"', 0230.chr('utf-8')],
+ ['"\u{99}"', 0231.chr('utf-8')],
+ ['"\u{9a}"', 0232.chr('utf-8')],
+ ['"\u{9b}"', 0233.chr('utf-8')],
+ ['"\u{9c}"', 0234.chr('utf-8')],
+ ['"\u{9d}"', 0235.chr('utf-8')],
+ ['"\u{9e}"', 0236.chr('utf-8')],
+ ['"\u{9f}"', 0237.chr('utf-8')],
+ ].should be_computed_by(:undump)
+ end
+
+ it "returns a string with \\uXXXX notation replaced with multi-byte UTF-8 characters" do
+ [ ['"\u0080"', 0200.chr('utf-8')],
+ ['"\u0081"', 0201.chr('utf-8')],
+ ['"\u0082"', 0202.chr('utf-8')],
+ ['"\u0083"', 0203.chr('utf-8')],
+ ['"\u0084"', 0204.chr('utf-8')],
+ ['"\u0086"', 0206.chr('utf-8')],
+ ['"\u0087"', 0207.chr('utf-8')],
+ ['"\u0088"', 0210.chr('utf-8')],
+ ['"\u0089"', 0211.chr('utf-8')],
+ ['"\u008a"', 0212.chr('utf-8')],
+ ['"\u008b"', 0213.chr('utf-8')],
+ ['"\u008c"', 0214.chr('utf-8')],
+ ['"\u008d"', 0215.chr('utf-8')],
+ ['"\u008e"', 0216.chr('utf-8')],
+ ['"\u008f"', 0217.chr('utf-8')],
+ ['"\u0090"', 0220.chr('utf-8')],
+ ['"\u0091"', 0221.chr('utf-8')],
+ ['"\u0092"', 0222.chr('utf-8')],
+ ['"\u0093"', 0223.chr('utf-8')],
+ ['"\u0094"', 0224.chr('utf-8')],
+ ['"\u0095"', 0225.chr('utf-8')],
+ ['"\u0096"', 0226.chr('utf-8')],
+ ['"\u0097"', 0227.chr('utf-8')],
+ ['"\u0098"', 0230.chr('utf-8')],
+ ['"\u0099"', 0231.chr('utf-8')],
+ ['"\u009a"', 0232.chr('utf-8')],
+ ['"\u009b"', 0233.chr('utf-8')],
+ ['"\u009c"', 0234.chr('utf-8')],
+ ['"\u009d"', 0235.chr('utf-8')],
+ ['"\u009e"', 0236.chr('utf-8')],
+ ['"\u009f"', 0237.chr('utf-8')],
+ ].should be_computed_by(:undump)
+ end
+
+ it "undumps correctly string produced from non ASCII-compatible one" do
+ s = "\u{876}".encode('utf-16be')
+ s.dump.undump.should == s
+
+ '"\\bv".force_encoding("UTF-16BE")'.undump.should == "\u0876".encode('utf-16be')
+ end
+
+ it "keeps origin encoding" do
+ '"foo"'.encode("ISO-8859-1").undump.encoding.should == Encoding::ISO_8859_1
+ '"foo"'.encode('windows-1251').undump.encoding.should == Encoding::Windows_1251
+ end
+
+ describe "Limitations" do
+ it "cannot undump non ASCII-compatible string" do
+ -> { '"foo"'.encode('utf-16le').undump }.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+
+ describe "invalid dump" do
+ it "raises RuntimeError exception if wrapping \" are missing" do
+ -> { 'foo'.undump }.should raise_error(RuntimeError, /invalid dumped string/)
+ -> { '"foo'.undump }.should raise_error(RuntimeError, /unterminated dumped string/)
+ -> { 'foo"'.undump }.should raise_error(RuntimeError, /invalid dumped string/)
+ -> { "'foo'".undump }.should raise_error(RuntimeError, /invalid dumped string/)
+ end
+
+ it "raises RuntimeError if there is incorrect \\x sequence" do
+ -> { '"\x"'.undump }.should raise_error(RuntimeError, /invalid hex escape/)
+ -> { '"\\x3y"'.undump }.should raise_error(RuntimeError, /invalid hex escape/)
+ end
+
+ it "raises RuntimeError in there is incorrect \\u sequence" do
+ -> { '"\\u"'.undump }.should raise_error(RuntimeError, /invalid Unicode escape/)
+ -> { '"\\u{"'.undump }.should raise_error(RuntimeError, /invalid Unicode escape/)
+ -> { '"\\u{3042"'.undump }.should raise_error(RuntimeError, /invalid Unicode escape/)
+ -> { '"\\u"'.undump }.should raise_error(RuntimeError, /invalid Unicode escape/)
+ end
+
+ it "raises RuntimeError if there is malformed dump of non ASCII-compatible string" do
+ -> { '"".force_encoding("ASCII-8BIT"'.undump }.should raise_error(RuntimeError, /invalid dumped string/)
+ -> { '"".force_encoding("Unknown")'.undump }.should raise_error(RuntimeError, /dumped string has unknown encoding name/)
+ -> { '"".force_encoding()'.undump }.should raise_error(RuntimeError, /invalid dumped string/)
+ end
+
+ it "raises RuntimeError if string contains \0 character" do
+ -> { "\"foo\0\"".undump }.should raise_error(RuntimeError, /string contains null byte/)
+ end
+
+ it "raises RuntimeError if string contains non ASCII character" do
+ -> { "\"\u3042\"".undump }.should raise_error(RuntimeError, /non-ASCII character detected/)
+ end
+
+ it "raises RuntimeError if there are some excessive \"" do
+ -> { '" "" "'.undump }.should raise_error(RuntimeError, /invalid dumped string/)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/unicode_normalize_spec.rb b/spec/ruby/core/string/unicode_normalize_spec.rb
new file mode 100644
index 0000000000..7a4662e0b8
--- /dev/null
+++ b/spec/ruby/core/string/unicode_normalize_spec.rb
@@ -0,0 +1,115 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+# Examples taken from http://www.unicode.org/reports/tr15/#Norm_Forms
+
+describe "String#unicode_normalize" do
+ before :each do
+ @accented_f = "\u1e9b\u0323"
+ @angstrom = "\u212b"
+ @ohm = "\u2126"
+ end
+
+ it "normalizes code points in the string according to the form that is specified" do
+ @accented_f.unicode_normalize(:nfc).should == "\u1e9b\u0323"
+ @accented_f.unicode_normalize(:nfd).should == "\u017f\u0323\u0307"
+ @accented_f.unicode_normalize(:nfkc).should == "\u1e69"
+ @accented_f.unicode_normalize(:nfkd).should == "\u0073\u0323\u0307"
+ end
+
+ it "defaults to the nfc normalization form if no forms are specified" do
+ @accented_f.unicode_normalize.should == "\u1e9b\u0323"
+ @angstrom.unicode_normalize.should == "\u00c5"
+ @ohm.unicode_normalize.should == "\u03a9"
+ end
+
+ # http://unicode.org/faq/normalization.html#6
+ context "returns normalized form of string by default" do
+ it "03D3 (Ï“) GREEK UPSILON WITH ACUTE AND HOOK SYMBOL" do
+ "\u03D3".unicode_normalize(:nfc).should == "\u03D3"
+ "\u03D3".unicode_normalize(:nfd).should == "\u03D2\u0301"
+ "\u03D3".unicode_normalize(:nfkc).should == "\u038E"
+ "\u03D3".unicode_normalize(:nfkd).should == "\u03A5\u0301"
+ end
+
+ it "03D4 (Ï”) GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL" do
+ "\u03D4".unicode_normalize(:nfc).should == "\u03D4"
+ "\u03D4".unicode_normalize(:nfd).should == "\u03D2\u0308"
+ "\u03D4".unicode_normalize(:nfkc).should == "\u03AB"
+ "\u03D4".unicode_normalize(:nfkd).should == "\u03A5\u0308"
+ end
+
+ it "1E9B (ẛ) LATIN SMALL LETTER LONG S WITH DOT ABOVE" do
+ "\u1E9B".unicode_normalize(:nfc).should == "\u1E9B"
+ "\u1E9B".unicode_normalize(:nfd).should == "\u017F\u0307"
+ "\u1E9B".unicode_normalize(:nfkc).should == "\u1E61"
+ "\u1E9B".unicode_normalize(:nfkd).should == "\u0073\u0307"
+ end
+ end
+
+ it "raises an Encoding::CompatibilityError if string is not in an unicode encoding" do
+ lambda do
+ [0xE0].pack('C').force_encoding("ISO-8859-1").unicode_normalize(:nfd)
+ end.should raise_error(Encoding::CompatibilityError)
+ end
+
+ it "raises an ArgumentError if the specified form is invalid" do
+ lambda {
+ @angstrom.unicode_normalize(:invalid_form)
+ }.should raise_error(ArgumentError)
+ end
+end
+
+describe "String#unicode_normalize!" do
+ it "normalizes code points and modifies the receiving string" do
+ angstrom = "\u212b"
+ angstrom.unicode_normalize!
+ angstrom.should == "\u00c5"
+ angstrom.should_not == "\u212b"
+ end
+
+ it "modifies original string (nfc)" do
+ str = "a\u0300"
+ str.unicode_normalize!(:nfc)
+
+ str.should_not == "a\u0300"
+ str.should == "à"
+ end
+
+ it "modifies self in place (nfd)" do
+ str = "\u00E0"
+ str.unicode_normalize!(:nfd)
+
+ str.should_not == "\u00E0"
+ str.should == "a\u0300"
+ end
+
+ it "modifies self in place (nfkc)" do
+ str = "\u1E9B\u0323"
+ str.unicode_normalize!(:nfkc)
+
+ str.should_not == "\u1E9B\u0323"
+ str.should == "\u1E69"
+ end
+
+ it "modifies self in place (nfkd)" do
+ str = "\u1E9B\u0323"
+ str.unicode_normalize!(:nfkd)
+
+ str.should_not == "\u1E9B\u0323"
+ str.should == "s\u0323\u0307"
+ end
+
+ it "raises an Encoding::CompatibilityError if the string is not in an unicode encoding" do
+ lambda {
+ [0xE0].pack('C').force_encoding("ISO-8859-1").unicode_normalize!
+ }.should raise_error(Encoding::CompatibilityError)
+ end
+
+ it "raises an ArgumentError if the specified form is invalid" do
+ ohm = "\u2126"
+ lambda {
+ ohm.unicode_normalize!(:invalid_form)
+ }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/string/unicode_normalized_spec.rb b/spec/ruby/core/string/unicode_normalized_spec.rb
new file mode 100644
index 0000000000..d383c0a91e
--- /dev/null
+++ b/spec/ruby/core/string/unicode_normalized_spec.rb
@@ -0,0 +1,74 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe "String#unicode_normalized?" do
+ before :each do
+ @nfc_normalized_str = "\u1e9b\u0323"
+ @nfd_normalized_str = "\u017f\u0323\u0307"
+ @nfkc_normalized_str = "\u1e69"
+ @nfkd_normalized_str = "\u0073\u0323\u0307"
+ end
+
+ it "returns true if string is in the specified normalization form" do
+ @nfc_normalized_str.unicode_normalized?(:nfc).should == true
+ @nfd_normalized_str.unicode_normalized?(:nfd).should == true
+ @nfkc_normalized_str.unicode_normalized?(:nfkc).should == true
+ @nfkd_normalized_str.unicode_normalized?(:nfkd).should == true
+ end
+
+ it "returns false if string is not in the supplied normalization form" do
+ @nfd_normalized_str.unicode_normalized?(:nfc).should == false
+ @nfc_normalized_str.unicode_normalized?(:nfd).should == false
+ @nfc_normalized_str.unicode_normalized?(:nfkc).should == false
+ @nfc_normalized_str.unicode_normalized?(:nfkd).should == false
+ end
+
+ it "defaults to the nfc normalization form if no forms are specified" do
+ @nfc_normalized_str.unicode_normalized?.should == true
+ @nfd_normalized_str.unicode_normalized?.should == false
+ end
+
+ it "returns true if string is empty" do
+ "".unicode_normalized?.should == true
+ end
+
+ it "returns true if string does not contain any unicode codepoints" do
+ "abc".unicode_normalized?.should == true
+ end
+
+ it "raises an Encoding::CompatibilityError if the string is not in an unicode encoding" do
+ lambda { @nfc_normalized_str.force_encoding("ISO-8859-1").unicode_normalized? }.should raise_error(Encoding::CompatibilityError)
+ end
+
+ it "raises an ArgumentError if the specified form is invalid" do
+ lambda { @nfc_normalized_str.unicode_normalized?(:invalid_form) }.should raise_error(ArgumentError)
+ end
+
+ it "returns true if str is in Unicode normalization form (nfc)" do
+ str = "a\u0300"
+ str.unicode_normalized?(:nfc).should be_false
+ str.unicode_normalize!(:nfc)
+ str.unicode_normalized?(:nfc).should be_true
+ end
+
+ it "returns true if str is in Unicode normalization form (nfd)" do
+ str = "a\u00E0"
+ str.unicode_normalized?(:nfd).should be_false
+ str.unicode_normalize!(:nfd)
+ str.unicode_normalized?(:nfd).should be_true
+ end
+
+ it "returns true if str is in Unicode normalization form (nfkc)" do
+ str = "a\u0300"
+ str.unicode_normalized?(:nfkc).should be_false
+ str.unicode_normalize!(:nfkc)
+ str.unicode_normalized?(:nfkc).should be_true
+ end
+
+ it "returns true if str is in Unicode normalization form (nfkd)" do
+ str = "a\u00E0"
+ str.unicode_normalized?(:nfkd).should be_false
+ str.unicode_normalize!(:nfkd)
+ str.unicode_normalized?(:nfkd).should be_true
+ end
+end
diff --git a/spec/ruby/core/string/unpack/a_spec.rb b/spec/ruby/core/string/unpack/a_spec.rb
new file mode 100644
index 0000000000..3de338e2e1
--- /dev/null
+++ b/spec/ruby/core/string/unpack/a_spec.rb
@@ -0,0 +1,66 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/string'
+require_relative 'shared/taint'
+
+describe "String#unpack with format 'A'" do
+ it_behaves_like :string_unpack_basic, 'A'
+ it_behaves_like :string_unpack_no_platform, 'A'
+ it_behaves_like :string_unpack_string, 'A'
+ it_behaves_like :string_unpack_Aa, 'A'
+ it_behaves_like :string_unpack_taint, 'A'
+
+ it "removes trailing space and NULL bytes from the decoded string" do
+ [ ["a\x00 b \x00", ["a\x00 b", ""]],
+ ["a\x00 b \x00 ", ["a\x00 b", ""]],
+ ["a\x00 b\x00 ", ["a\x00 b", ""]],
+ ["a\x00 b\x00", ["a\x00 b", ""]],
+ ["a\x00 b ", ["a\x00 b", ""]]
+ ].should be_computed_by(:unpack, "A*A")
+ end
+
+ it "does not remove whitespace other than space" do
+ [ ["a\x00 b\x00\f", ["a\x00 b\x00\f"]],
+ ["a\x00 b\x00\n", ["a\x00 b\x00\n"]],
+ ["a\x00 b\x00\r", ["a\x00 b\x00\r"]],
+ ["a\x00 b\x00\t", ["a\x00 b\x00\t"]],
+ ["a\x00 b\x00\v", ["a\x00 b\x00\v"]],
+ ].should be_computed_by(:unpack, "A*")
+ end
+
+ it "decodes into raw (ascii) string values" do
+ str = "str".force_encoding('UTF-8').unpack("A*")[0]
+ str.encoding.name.should == 'ASCII-8BIT'
+ end
+
+end
+
+describe "String#unpack with format 'a'" do
+ it_behaves_like :string_unpack_basic, 'a'
+ it_behaves_like :string_unpack_no_platform, 'a'
+ it_behaves_like :string_unpack_string, 'a'
+ it_behaves_like :string_unpack_Aa, 'a'
+ it_behaves_like :string_unpack_taint, 'a'
+
+ it "does not remove trailing whitespace or NULL bytes from the decoded string" do
+ [ ["a\x00 b \x00", ["a\x00 b \x00"]],
+ ["a\x00 b \x00 ", ["a\x00 b \x00 "]],
+ ["a\x00 b\x00 ", ["a\x00 b\x00 "]],
+ ["a\x00 b\x00", ["a\x00 b\x00"]],
+ ["a\x00 b ", ["a\x00 b "]],
+ ["a\x00 b\f", ["a\x00 b\f"]],
+ ["a\x00 b\n", ["a\x00 b\n"]],
+ ["a\x00 b\r", ["a\x00 b\r"]],
+ ["a\x00 b\t", ["a\x00 b\t"]],
+ ["a\x00 b\v", ["a\x00 b\v"]]
+ ].should be_computed_by(:unpack, "a*")
+ end
+
+ it "decodes into raw (ascii) string values" do
+ str = "".unpack("a*")[0]
+ str.encoding.name.should == 'ASCII-8BIT'
+ end
+
+end
diff --git a/spec/ruby/core/string/unpack/at_spec.rb b/spec/ruby/core/string/unpack/at_spec.rb
new file mode 100644
index 0000000000..a5f97b64f0
--- /dev/null
+++ b/spec/ruby/core/string/unpack/at_spec.rb
@@ -0,0 +1,29 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+
+describe "String#unpack with format '@'" do
+ it_behaves_like :string_unpack_basic, '@'
+ it_behaves_like :string_unpack_no_platform, '@'
+
+ it "moves the read index to the byte specified by the count" do
+ "\x01\x02\x03\x04".unpack("C3@2C").should == [1, 2, 3, 3]
+ end
+
+ it "implicitly has a count of zero when count is not specified" do
+ "\x01\x02\x03\x04".unpack("C2@C").should == [1, 2, 1]
+ end
+
+ it "has no effect when passed the '*' modifier" do
+ "\x01\x02\x03\x04".unpack("C2@*C").should == [1, 2, 3]
+ end
+
+ it "positions the read index one beyond the last readable byte in the String" do
+ "\x01\x02\x03\x04".unpack("C2@4C").should == [1, 2, nil]
+ end
+
+ it "raises an ArgumentError if the count exceeds the size of the String" do
+ lambda { "\x01\x02\x03\x04".unpack("C2@5C") }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/string/unpack/b_spec.rb b/spec/ruby/core/string/unpack/b_spec.rb
new file mode 100644
index 0000000000..a0bbc3d421
--- /dev/null
+++ b/spec/ruby/core/string/unpack/b_spec.rb
@@ -0,0 +1,193 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/taint'
+
+describe "String#unpack with format 'B'" do
+ it_behaves_like :string_unpack_basic, 'B'
+ it_behaves_like :string_unpack_no_platform, 'B'
+ it_behaves_like :string_unpack_taint, 'B'
+
+ it "decodes one bit from each byte for each format character starting with the most significant bit" do
+ [ ["\x00", "B", ["0"]],
+ ["\x80", "B", ["1"]],
+ ["\x0f", "B", ["0"]],
+ ["\x8f", "B", ["1"]],
+ ["\x7f", "B", ["0"]],
+ ["\xff", "B", ["1"]],
+ ["\x80\x00", "BB", ["1", "0"]],
+ ["\x8f\x00", "BB", ["1", "0"]],
+ ["\x80\x0f", "BB", ["1", "0"]],
+ ["\x80\x8f", "BB", ["1", "1"]],
+ ["\x80\x80", "BB", ["1", "1"]],
+ ["\x0f\x80", "BB", ["0", "1"]]
+ ].should be_computed_by(:unpack)
+ end
+
+ it "decodes only the number of bits in the string when passed a count" do
+ "\x83".unpack("B25").should == ["10000011"]
+ end
+
+ it "decodes multiple differing bit counts from a single string" do
+ str = "\xaa\xaa\xaa\xaa\x55\xaa\xd4\xc3\x6b\xd7\xaa\xd7\xc3\xd4\xaa\x6b\xd7\xaa"
+ array = str.unpack("B5B6B7B8B9B10B13B14B16B17")
+ array.should == ["10101", "101010", "1010101", "10101010", "010101011",
+ "1101010011", "0110101111010", "10101010110101",
+ "1100001111010100", "10101010011010111"]
+ end
+
+ it "decodes a directive with a '*' modifier after a directive with a count modifier" do
+ "\xd4\xc3\x6b\xd7".unpack("B5B*").should == ["11010", "110000110110101111010111"]
+ end
+
+ it "decodes a directive with a count modifier after a directive with a '*' modifier" do
+ "\xd4\xc3\x6b\xd7".unpack("B*B5").should == ["11010100110000110110101111010111", ""]
+ end
+
+ it "decodes the number of bits specified by the count modifier" do
+ [ ["\x00", "B0", [""]],
+ ["\x80", "B1", ["1"]],
+ ["\x7f", "B2", ["01"]],
+ ["\x8f", "B3", ["100"]],
+ ["\x7f", "B4", ["0111"]],
+ ["\xff", "B5", ["11111"]],
+ ["\xf8", "B6", ["111110"]],
+ ["\x9c", "B7", ["1001110"]],
+ ["\xbd", "B8", ["10111101"]],
+ ["\x80\x80", "B9", ["100000001"]],
+ ["\x80\x70", "B10", ["1000000001"]],
+ ["\x80\x20", "B11", ["10000000001"]],
+ ["\x8f\x10", "B12", ["100011110001"]],
+ ["\x8f\x0f", "B13", ["1000111100001"]],
+ ["\x80\x0f", "B14", ["10000000000011"]],
+ ["\x80\x8f", "B15", ["100000001000111"]],
+ ["\x0f\x81", "B16", ["0000111110000001"]]
+ ].should be_computed_by(:unpack)
+ end
+
+ it "decodes all the bits when passed the '*' modifier" do
+ [ ["", [""]],
+ ["\x00", ["00000000"]],
+ ["\x80", ["10000000"]],
+ ["\x7f", ["01111111"]],
+ ["\x81", ["10000001"]],
+ ["\x0f", ["00001111"]],
+ ["\x80\x80", ["1000000010000000"]],
+ ["\x8f\x10", ["1000111100010000"]],
+ ["\x00\x10", ["0000000000010000"]]
+ ].should be_computed_by(:unpack, "B*")
+ end
+
+ it "adds an empty string for each element requested beyond the end of the String" do
+ [ ["", ["", "", ""]],
+ ["\x80", ["1", "", ""]],
+ ["\x80\x08", ["1", "0", ""]]
+ ].should be_computed_by(:unpack, "BBB")
+ end
+
+ it "ignores NULL bytes between directives" do
+ "\x80\x00".unpack("B\x00B").should == ["1", "0"]
+ end
+
+ it "ignores spaces between directives" do
+ "\x80\x00".unpack("B B").should == ["1", "0"]
+ end
+end
+
+describe "String#unpack with format 'b'" do
+ it_behaves_like :string_unpack_basic, 'b'
+ it_behaves_like :string_unpack_no_platform, 'b'
+ it_behaves_like :string_unpack_taint, 'b'
+
+ it "decodes one bit from each byte for each format character starting with the least significant bit" do
+ [ ["\x00", "b", ["0"]],
+ ["\x01", "b", ["1"]],
+ ["\xf0", "b", ["0"]],
+ ["\xf1", "b", ["1"]],
+ ["\xfe", "b", ["0"]],
+ ["\xff", "b", ["1"]],
+ ["\x01\x00", "bb", ["1", "0"]],
+ ["\xf1\x00", "bb", ["1", "0"]],
+ ["\x01\xf0", "bb", ["1", "0"]],
+ ["\x01\xf1", "bb", ["1", "1"]],
+ ["\x01\x01", "bb", ["1", "1"]],
+ ["\xf0\x01", "bb", ["0", "1"]]
+ ].should be_computed_by(:unpack)
+ end
+
+ it "decodes only the number of bits in the string when passed a count" do
+ "\x83".unpack("b25").should == ["11000001"]
+ end
+
+ it "decodes multiple differing bit counts from a single string" do
+ str = "\xaa\xaa\xaa\xaa\x55\xaa\xd4\xc3\x6b\xd7\xaa\xd7\xc3\xd4\xaa\x6b\xd7\xaa"
+ array = str.unpack("b5b6b7b8b9b10b13b14b16b17")
+ array.should == ["01010", "010101", "0101010", "01010101", "101010100",
+ "0010101111", "1101011011101", "01010101111010",
+ "1100001100101011", "01010101110101101"]
+ end
+
+ it "decodes a directive with a '*' modifier after a directive with a count modifier" do
+ "\xd4\xc3\x6b\xd7".unpack("b5b*").should == ["00101", "110000111101011011101011"]
+ end
+
+ it "decodes a directive with a count modifier after a directive with a '*' modifier" do
+ "\xd4\xc3\x6b\xd7".unpack("b*b5").should == ["00101011110000111101011011101011", ""]
+ end
+
+ it "decodes the number of bits specified by the count modifier" do
+ [ ["\x00", "b0", [""]],
+ ["\x01", "b1", ["1"]],
+ ["\xfe", "b2", ["01"]],
+ ["\xfc", "b3", ["001"]],
+ ["\xf7", "b4", ["1110"]],
+ ["\xff", "b5", ["11111"]],
+ ["\xfe", "b6", ["011111"]],
+ ["\xce", "b7", ["0111001"]],
+ ["\xbd", "b8", ["10111101"]],
+ ["\x01\xff", "b9", ["100000001"]],
+ ["\x01\xfe", "b10", ["1000000001"]],
+ ["\x01\xfc", "b11", ["10000000001"]],
+ ["\xf1\xf8", "b12", ["100011110001"]],
+ ["\xe1\xf1", "b13", ["1000011110001"]],
+ ["\x03\xe0", "b14", ["11000000000001"]],
+ ["\x47\xc0", "b15", ["111000100000001"]],
+ ["\x81\x0f", "b16", ["1000000111110000"]]
+ ].should be_computed_by(:unpack)
+ end
+
+ it "decodes all the bits when passed the '*' modifier" do
+ [ ["", [""]],
+ ["\x00", ["00000000"]],
+ ["\x80", ["00000001"]],
+ ["\x7f", ["11111110"]],
+ ["\x81", ["10000001"]],
+ ["\x0f", ["11110000"]],
+ ["\x80\x80", ["0000000100000001"]],
+ ["\x8f\x10", ["1111000100001000"]],
+ ["\x00\x10", ["0000000000001000"]]
+ ].should be_computed_by(:unpack, "b*")
+ end
+
+ it "adds an empty string for each element requested beyond the end of the String" do
+ [ ["", ["", "", ""]],
+ ["\x01", ["1", "", ""]],
+ ["\x01\x80", ["1", "0", ""]]
+ ].should be_computed_by(:unpack, "bbb")
+ end
+
+ it "ignores NULL bytes between directives" do
+ "\x01\x00".unpack("b\x00b").should == ["1", "0"]
+ end
+
+ it "ignores spaces between directives" do
+ "\x01\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
diff --git a/spec/ruby/core/string/unpack/c_spec.rb b/spec/ruby/core/string/unpack/c_spec.rb
new file mode 100644
index 0000000000..82c0f8616d
--- /dev/null
+++ b/spec/ruby/core/string/unpack/c_spec.rb
@@ -0,0 +1,63 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+
+describe :string_unpack_8bit, shared: true do
+ it "decodes one byte for a single format character" do
+ "abc".unpack(unpack_format()).should == [97]
+ end
+
+ it "decodes two bytes for two format characters" do
+ "abc".unpack(unpack_format(nil, 2)).should == [97, 98]
+ end
+
+ it "decodes the number of bytes requested by the count modifier" do
+ "abc".unpack(unpack_format(2)).should == [97, 98]
+ end
+
+ it "decodes the remaining bytes when passed the '*' modifier" do
+ "abc".unpack(unpack_format('*')).should == [97, 98, 99]
+ end
+
+ it "decodes the remaining bytes when passed the '*' modifer after another directive" do
+ "abc".unpack(unpack_format()+unpack_format('*')).should == [97, 98, 99]
+ end
+
+ it "decodes zero bytes when no bytes remain and the '*' modifier is passed" do
+ "abc".unpack(unpack_format('*', 2)).should == [97, 98, 99]
+ end
+
+ it "adds nil for each element requested beyond the end of the String" do
+ [ ["", [nil, nil, nil]],
+ ["a", [97, nil, nil]],
+ ["ab", [97, 98, nil]]
+ ].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]
+ end
+
+ it "ignores spaces between directives" do
+ "abc".unpack(unpack_format(' ', 2)).should == [97, 98]
+ end
+end
+
+describe "String#unpack with format 'C'" do
+ it_behaves_like :string_unpack_basic, 'C'
+ it_behaves_like :string_unpack_8bit, 'C'
+
+ it "decodes a byte with most significant bit set as a positive number" do
+ "\xff\x80\x82".unpack('C*').should == [255, 128, 130]
+ end
+end
+
+describe "String#unpack with format 'c'" do
+ it_behaves_like :string_unpack_basic, 'c'
+ it_behaves_like :string_unpack_8bit, 'c'
+
+ it "decodes a byte with most significant bit set as a negative number" do
+ "\xff\x80\x82".unpack('c*').should == [-1, -128, -126]
+ end
+end
diff --git a/spec/ruby/core/string/unpack/comment_spec.rb b/spec/ruby/core/string/unpack/comment_spec.rb
new file mode 100644
index 0000000000..7e7adbf54f
--- /dev/null
+++ b/spec/ruby/core/string/unpack/comment_spec.rb
@@ -0,0 +1,25 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "String#unpack" do
+ it "ignores directives text from '#' to the first newline" do
+ "\x01\x02\x03".unpack("c#this is a comment\nc").should == [1, 2]
+ end
+
+ it "ignores directives text from '#' to the end if no newline is present" do
+ "\x01\x02\x03".unpack("c#this is a comment c").should == [1]
+ end
+
+ it "ignores comments at the start of the directives string" do
+ "\x01\x02\x03".unpack("#this is a comment\nc").should == [1]
+ end
+
+ it "ignores the entire directive string if it is a comment" do
+ "\x01\x02\x03".unpack("#this is a comment c").should == []
+ end
+
+ it "ignores multiple comments" do
+ "\x01\x02\x03".unpack("c#comment\nc#comment\nc#c").should == [1, 2, 3]
+ end
+end
diff --git a/spec/ruby/core/string/unpack/d_spec.rb b/spec/ruby/core/string/unpack/d_spec.rb
new file mode 100644
index 0000000000..0e4f57ec04
--- /dev/null
+++ b/spec/ruby/core/string/unpack/d_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/float'
+
+little_endian do
+ describe "String#unpack with format 'D'" do
+ it_behaves_like :string_unpack_basic, 'D'
+ it_behaves_like :string_unpack_double_le, 'D'
+ end
+
+ describe "String#unpack with format 'd'" do
+ it_behaves_like :string_unpack_basic, 'd'
+ it_behaves_like :string_unpack_double_le, 'd'
+ end
+end
+
+big_endian do
+ describe "String#unpack with format 'D'" do
+ it_behaves_like :string_unpack_basic, 'D'
+ it_behaves_like :string_unpack_double_be, 'D'
+ end
+
+ describe "String#unpack with format 'd'" do
+ it_behaves_like :string_unpack_basic, 'd'
+ it_behaves_like :string_unpack_double_be, 'd'
+ end
+end
diff --git a/spec/ruby/core/string/unpack/e_spec.rb b/spec/ruby/core/string/unpack/e_spec.rb
new file mode 100644
index 0000000000..c958be1c8b
--- /dev/null
+++ b/spec/ruby/core/string/unpack/e_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/float'
+
+describe "String#unpack with format 'E'" do
+ it_behaves_like :string_unpack_basic, 'E'
+ it_behaves_like :string_unpack_double_le, 'E'
+end
+
+describe "String#unpack with format 'e'" do
+ it_behaves_like :string_unpack_basic, 'e'
+ it_behaves_like :string_unpack_float_le, 'e'
+end
diff --git a/spec/ruby/core/string/unpack/f_spec.rb b/spec/ruby/core/string/unpack/f_spec.rb
new file mode 100644
index 0000000000..ec8b9d435e
--- /dev/null
+++ b/spec/ruby/core/string/unpack/f_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/float'
+
+little_endian do
+ describe "String#unpack with format 'F'" do
+ it_behaves_like :string_unpack_basic, 'F'
+ it_behaves_like :string_unpack_float_le, 'F'
+ end
+
+ describe "String#unpack with format 'f'" do
+ it_behaves_like :string_unpack_basic, 'f'
+ it_behaves_like :string_unpack_float_le, 'f'
+ end
+end
+
+big_endian do
+ describe "String#unpack with format 'F'" do
+ it_behaves_like :string_unpack_basic, 'F'
+ it_behaves_like :string_unpack_float_be, 'F'
+ end
+
+ describe "String#unpack with format 'f'" do
+ it_behaves_like :string_unpack_basic, 'f'
+ it_behaves_like :string_unpack_float_be, 'f'
+ end
+end
diff --git a/spec/ruby/core/string/unpack/g_spec.rb b/spec/ruby/core/string/unpack/g_spec.rb
new file mode 100644
index 0000000000..ffc423b152
--- /dev/null
+++ b/spec/ruby/core/string/unpack/g_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/float'
+
+describe "String#unpack with format 'G'" do
+ it_behaves_like :string_unpack_basic, 'G'
+ it_behaves_like :string_unpack_double_be, 'G'
+end
+
+describe "String#unpack with format 'g'" do
+ it_behaves_like :string_unpack_basic, 'g'
+ it_behaves_like :string_unpack_float_be, 'g'
+end
diff --git a/spec/ruby/core/string/unpack/h_spec.rb b/spec/ruby/core/string/unpack/h_spec.rb
new file mode 100644
index 0000000000..07d52149d1
--- /dev/null
+++ b/spec/ruby/core/string/unpack/h_spec.rb
@@ -0,0 +1,127 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/taint'
+
+describe "String#unpack with format 'H'" do
+ it_behaves_like :string_unpack_basic, 'H'
+ it_behaves_like :string_unpack_no_platform, 'H'
+ it_behaves_like :string_unpack_taint, 'H'
+
+ it "decodes one nibble from each byte for each format character starting with the most significant bit" do
+ [ ["\x8f", "H", ["8"]],
+ ["\xf8\x0f", "HH", ["f", "0"]]
+ ].should be_computed_by(:unpack)
+ end
+
+ it "decodes only the number of nibbles in the string when passed a count" do
+ "\xca\xfe".unpack("H5").should == ["cafe"]
+ end
+
+ it "decodes multiple differing nibble counts from a single string" do
+ array = "\xaa\x55\xaa\xd4\xc3\x6b\xd7\xaa\xd7".unpack("HH2H3H4H5")
+ array.should == ["a", "55", "aad", "c36b", "d7aad"]
+ end
+
+ it "decodes a directive with a '*' modifier after a directive with a count modifier" do
+ "\xaa\x55\xaa\xd4\xc3\x6b".unpack("H3H*").should == ["aa5", "aad4c36b"]
+ end
+
+ it "decodes a directive with a count modifier after a directive with a '*' modifier" do
+ "\xaa\x55\xaa\xd4\xc3\x6b".unpack("H*H3").should == ["aa55aad4c36b", ""]
+ end
+
+ it "decodes the number of nibbles specified by the count modifier" do
+ [ ["\xab", "H0", [""]],
+ ["\x00", "H1", ["0"]],
+ ["\x01", "H2", ["01"]],
+ ["\x01\x23", "H3", ["012"]],
+ ["\x01\x23", "H4", ["0123"]],
+ ["\x01\x23\x45", "H5", ["01234"]]
+ ].should be_computed_by(:unpack)
+ end
+
+ it "decodes all the nibbles when passed the '*' modifier" do
+ [ ["", [""]],
+ ["\xab", ["ab"]],
+ ["\xca\xfe", ["cafe"]],
+ ].should be_computed_by(:unpack, "H*")
+ end
+
+ it "adds an empty string for each element requested beyond the end of the String" do
+ [ ["", ["", "", ""]],
+ ["\x01", ["0", "", ""]],
+ ["\x01\x80", ["0", "8", ""]]
+ ].should be_computed_by(:unpack, "HHH")
+ end
+
+ it "ignores NULL bytes between directives" do
+ "\x01\x10".unpack("H\x00H").should == ["0", "1"]
+ end
+
+ it "ignores spaces between directives" do
+ "\x01\x10".unpack("H H").should == ["0", "1"]
+ end
+end
+
+describe "String#unpack with format 'h'" do
+ it_behaves_like :string_unpack_basic, 'h'
+ it_behaves_like :string_unpack_no_platform, 'h'
+ it_behaves_like :string_unpack_taint, 'h'
+
+ it "decodes one nibble from each byte for each format character starting with the least significant bit" do
+ [ ["\x8f", "h", ["f"]],
+ ["\xf8\x0f", "hh", ["8", "f"]]
+ ].should be_computed_by(:unpack)
+ end
+
+ it "decodes only the number of nibbles in the string when passed a count" do
+ "\xac\xef".unpack("h5").should == ["cafe"]
+ end
+
+ it "decodes multiple differing nibble counts from a single string" do
+ array = "\xaa\x55\xaa\xd4\xc3\x6b\xd7\xaa\xd7".unpack("hh2h3h4h5")
+ array.should == ["a", "55", "aa4", "3cb6", "7daa7"]
+ end
+
+ it "decodes a directive with a '*' modifier after a directive with a count modifier" do
+ "\xba\x55\xaa\xd4\xc3\x6b".unpack("h3h*").should == ["ab5", "aa4d3cb6"]
+ end
+
+ it "decodes a directive with a count modifier after a directive with a '*' modifier" do
+ "\xba\x55\xaa\xd4\xc3\x6b".unpack("h*h3").should == ["ab55aa4d3cb6", ""]
+ end
+
+ it "decodes the number of nibbles specified by the count modifier" do
+ [ ["\xab", "h0", [""]],
+ ["\x00", "h1", ["0"]],
+ ["\x01", "h2", ["10"]],
+ ["\x01\x23", "h3", ["103"]],
+ ["\x01\x23", "h4", ["1032"]],
+ ["\x01\x23\x45", "h5", ["10325"]]
+ ].should be_computed_by(:unpack)
+ end
+
+ it "decodes all the nibbles when passed the '*' modifier" do
+ [ ["", [""]],
+ ["\xab", ["ba"]],
+ ["\xac\xef", ["cafe"]],
+ ].should be_computed_by(:unpack, "h*")
+ end
+
+ it "adds an empty string for each element requested beyond the end of the String" do
+ [ ["", ["", "", ""]],
+ ["\x01", ["1", "", ""]],
+ ["\x01\x80", ["1", "0", ""]]
+ ].should be_computed_by(:unpack, "hhh")
+ end
+
+ it "ignores NULL bytes between directives" do
+ "\x01\x10".unpack("h\x00h").should == ["1", "0"]
+ end
+
+ it "ignores spaces between directives" do
+ "\x01\x10".unpack("h h").should == ["1", "0"]
+ end
+end
diff --git a/spec/ruby/core/string/unpack/i_spec.rb b/spec/ruby/core/string/unpack/i_spec.rb
new file mode 100644
index 0000000000..b4bbba1923
--- /dev/null
+++ b/spec/ruby/core/string/unpack/i_spec.rb
@@ -0,0 +1,152 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/integer'
+
+describe "String#unpack with format 'I'" do
+ describe "with modifier '<'" do
+ it_behaves_like :string_unpack_32bit_le, 'I<'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'I<'
+ end
+
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :string_unpack_32bit_le, 'I<_'
+ it_behaves_like :string_unpack_32bit_le, 'I_<'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'I<_'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'I_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :string_unpack_32bit_le, 'I<!'
+ it_behaves_like :string_unpack_32bit_le, 'I!<'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'I<!'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'I!<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :string_unpack_32bit_be, 'I>'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'I>'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :string_unpack_32bit_be, 'I>_'
+ it_behaves_like :string_unpack_32bit_be, 'I_>'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'I>_'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'I_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :string_unpack_32bit_be, 'I>!'
+ it_behaves_like :string_unpack_32bit_be, 'I!>'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'I>!'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'I!>'
+ end
+end
+
+describe "String#unpack with format 'i'" do
+ describe "with modifier '<'" do
+ it_behaves_like :string_unpack_32bit_le, 'i<'
+ it_behaves_like :string_unpack_32bit_le_signed, 'i<'
+ end
+
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :string_unpack_32bit_le, 'i<_'
+ it_behaves_like :string_unpack_32bit_le, 'i_<'
+ it_behaves_like :string_unpack_32bit_le_signed, 'i<_'
+ it_behaves_like :string_unpack_32bit_le_signed, 'i_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :string_unpack_32bit_le, 'i<!'
+ it_behaves_like :string_unpack_32bit_le, 'i!<'
+ it_behaves_like :string_unpack_32bit_le_signed, 'i<!'
+ it_behaves_like :string_unpack_32bit_le_signed, 'i!<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :string_unpack_32bit_be, 'i>'
+ it_behaves_like :string_unpack_32bit_be_signed, 'i>'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :string_unpack_32bit_be, 'i>_'
+ it_behaves_like :string_unpack_32bit_be, 'i_>'
+ it_behaves_like :string_unpack_32bit_be_signed, 'i>_'
+ it_behaves_like :string_unpack_32bit_be_signed, 'i_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :string_unpack_32bit_be, 'i>!'
+ it_behaves_like :string_unpack_32bit_be, 'i!>'
+ it_behaves_like :string_unpack_32bit_be_signed, 'i>!'
+ it_behaves_like :string_unpack_32bit_be_signed, 'i!>'
+ end
+end
+
+little_endian do
+ describe "String#unpack with format 'I'" do
+ it_behaves_like :string_unpack_basic, 'I'
+ it_behaves_like :string_unpack_32bit_le, 'I'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'I'
+ end
+
+ describe "String#unpack with format 'I' with modifier '_'" do
+ it_behaves_like :string_unpack_32bit_le, 'I_'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'I_'
+ end
+
+ describe "String#unpack with format 'I' with modifier '!'" do
+ it_behaves_like :string_unpack_32bit_le, 'I!'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'I!'
+ end
+
+ describe "String#unpack with format 'i'" do
+ it_behaves_like :string_unpack_basic, 'i'
+ it_behaves_like :string_unpack_32bit_le, 'i'
+ it_behaves_like :string_unpack_32bit_le_signed, 'i'
+ end
+
+ describe "String#unpack with format 'i' with modifier '_'" do
+ it_behaves_like :string_unpack_32bit_le, 'i_'
+ it_behaves_like :string_unpack_32bit_le_signed, 'i_'
+ end
+
+ describe "String#unpack with format 'i' with modifier '!'" do
+ it_behaves_like :string_unpack_32bit_le, 'i!'
+ it_behaves_like :string_unpack_32bit_le_signed, 'i!'
+ end
+end
+
+big_endian do
+ describe "String#unpack with format 'I'" do
+ it_behaves_like :string_unpack_basic, 'I'
+ it_behaves_like :string_unpack_32bit_be, 'I'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'I'
+ end
+
+ describe "String#unpack with format 'I' with modifier '_'" do
+ it_behaves_like :string_unpack_32bit_be, 'I_'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'I_'
+ end
+
+ describe "String#unpack with format 'I' with modifier '!'" do
+ it_behaves_like :string_unpack_32bit_be, 'I!'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'I!'
+ end
+
+ describe "String#unpack with format 'i'" do
+ it_behaves_like :string_unpack_basic, 'i'
+ it_behaves_like :string_unpack_32bit_be, 'i'
+ it_behaves_like :string_unpack_32bit_be_signed, 'i'
+ end
+
+ describe "String#unpack with format 'i' with modifier '_'" do
+ it_behaves_like :string_unpack_32bit_be, 'i_'
+ it_behaves_like :string_unpack_32bit_be_signed, 'i_'
+ end
+
+ describe "String#unpack with format 'i' with modifier '!'" do
+ it_behaves_like :string_unpack_32bit_be, 'i!'
+ it_behaves_like :string_unpack_32bit_be_signed, 'i!'
+ end
+end
diff --git a/spec/ruby/core/string/unpack/j_spec.rb b/spec/ruby/core/string/unpack/j_spec.rb
new file mode 100644
index 0000000000..3c2baad642
--- /dev/null
+++ b/spec/ruby/core/string/unpack/j_spec.rb
@@ -0,0 +1,272 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/integer'
+
+platform_is pointer_size: 64 do
+ little_endian do
+ describe "String#unpack with format 'J'" do
+ describe "with modifier '_'" do
+ it_behaves_like :string_unpack_64bit_le, 'J_'
+ it_behaves_like :string_unpack_64bit_le_unsigned, 'J_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :string_unpack_64bit_le, 'J!'
+ it_behaves_like :string_unpack_64bit_le_unsigned, 'J!'
+ end
+ end
+
+ describe "String#unpack with format 'j'" do
+ describe "with modifier '_'" do
+ it_behaves_like :string_unpack_64bit_le, 'j_'
+ it_behaves_like :string_unpack_64bit_le_signed, 'j_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :string_unpack_64bit_le, 'j!'
+ it_behaves_like :string_unpack_64bit_le_signed, 'j!'
+ end
+ end
+ end
+
+ big_endian do
+ describe "String#unpack with format 'J'" do
+ describe "with modifier '_'" do
+ it_behaves_like :string_unpack_64bit_be, 'J_'
+ it_behaves_like :string_unpack_64bit_be_unsigned, 'J_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :string_unpack_64bit_be, 'J!'
+ it_behaves_like :string_unpack_64bit_be_unsigned, 'J!'
+ end
+ end
+
+ describe "String#unpack with format 'j'" do
+ describe "with modifier '_'" do
+ it_behaves_like :string_unpack_64bit_be, 'j_'
+ it_behaves_like :string_unpack_64bit_be_signed, 'j_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :string_unpack_64bit_be, 'j!'
+ it_behaves_like :string_unpack_64bit_be_signed, 'j!'
+ end
+ end
+ end
+
+ describe "String#unpack with format 'J'" do
+ describe "with modifier '<'" do
+ it_behaves_like :string_unpack_64bit_le, 'J<'
+ it_behaves_like :string_unpack_64bit_le_unsigned, 'J<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :string_unpack_64bit_be, 'J>'
+ it_behaves_like :string_unpack_64bit_be_unsigned, 'J>'
+ end
+
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :string_unpack_64bit_le, 'J<_'
+ it_behaves_like :string_unpack_64bit_le, 'J_<'
+ it_behaves_like :string_unpack_64bit_le_unsigned, 'J<_'
+ it_behaves_like :string_unpack_64bit_le_unsigned, 'J_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :string_unpack_64bit_le, 'J<!'
+ it_behaves_like :string_unpack_64bit_le, 'J!<'
+ it_behaves_like :string_unpack_64bit_le_unsigned, 'J<!'
+ it_behaves_like :string_unpack_64bit_le_unsigned, 'J!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :string_unpack_64bit_be, 'J>_'
+ it_behaves_like :string_unpack_64bit_be, 'J_>'
+ it_behaves_like :string_unpack_64bit_be_unsigned, 'J>_'
+ it_behaves_like :string_unpack_64bit_be_unsigned, 'J_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :string_unpack_64bit_be, 'J>!'
+ it_behaves_like :string_unpack_64bit_be, 'J!>'
+ it_behaves_like :string_unpack_64bit_be_unsigned, 'J>!'
+ it_behaves_like :string_unpack_64bit_be_unsigned, 'J!>'
+ end
+ end
+
+ describe "String#unpack with format 'j'" do
+ describe "with modifier '<'" do
+ it_behaves_like :string_unpack_64bit_le, 'j<'
+ it_behaves_like :string_unpack_64bit_le_signed, 'j<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :string_unpack_64bit_be, 'j>'
+ it_behaves_like :string_unpack_64bit_be_signed, 'j>'
+ end
+
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :string_unpack_64bit_le, 'j<_'
+ it_behaves_like :string_unpack_64bit_le, 'j_<'
+ it_behaves_like :string_unpack_64bit_le_signed, 'j<_'
+ it_behaves_like :string_unpack_64bit_le_signed, 'j_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :string_unpack_64bit_le, 'j<!'
+ it_behaves_like :string_unpack_64bit_le, 'j!<'
+ it_behaves_like :string_unpack_64bit_le_signed, 'j<!'
+ it_behaves_like :string_unpack_64bit_le_signed, 'j!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :string_unpack_64bit_be, 'j>_'
+ it_behaves_like :string_unpack_64bit_be, 'j_>'
+ it_behaves_like :string_unpack_64bit_be_signed, 'j>_'
+ it_behaves_like :string_unpack_64bit_be_signed, 'j_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :string_unpack_64bit_be, 'j>!'
+ it_behaves_like :string_unpack_64bit_be, 'j!>'
+ it_behaves_like :string_unpack_64bit_be_signed, 'j>!'
+ it_behaves_like :string_unpack_64bit_be_signed, 'j!>'
+ end
+ end
+end
+
+platform_is pointer_size: 32 do
+ little_endian do
+ describe "String#unpack with format 'J'" do
+ describe "with modifier '_'" do
+ it_behaves_like :string_unpack_32bit_le, 'J_'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'J_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :string_unpack_32bit_le, 'J!'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'J!'
+ end
+ end
+
+ describe "String#unpack with format 'j'" do
+ describe "with modifier '_'" do
+ it_behaves_like :string_unpack_32bit_le, 'j_'
+ it_behaves_like :string_unpack_32bit_le_signed, 'j_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :string_unpack_32bit_le, 'j!'
+ it_behaves_like :string_unpack_32bit_le_signed, 'j!'
+ end
+ end
+ end
+
+ big_endian do
+ describe "String#unpack with format 'J'" do
+ describe "with modifier '_'" do
+ it_behaves_like :string_unpack_32bit_be, 'J_'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'J_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :string_unpack_32bit_be, 'J!'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'J!'
+ end
+ end
+
+ describe "String#unpack with format 'j'" do
+ describe "with modifier '_'" do
+ it_behaves_like :string_unpack_32bit_be, 'j_'
+ it_behaves_like :string_unpack_32bit_be_signed, 'j_'
+ end
+
+ describe "with modifier '!'" do
+ it_behaves_like :string_unpack_32bit_be, 'j!'
+ it_behaves_like :string_unpack_32bit_be_signed, 'j!'
+ end
+ end
+ end
+
+ describe "String#unpack with format 'J'" do
+ describe "with modifier '<'" do
+ it_behaves_like :string_unpack_32bit_le, 'J<'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'J<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :string_unpack_32bit_be, 'J>'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'J>'
+ end
+
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :string_unpack_32bit_le, 'J<_'
+ it_behaves_like :string_unpack_32bit_le, 'J_<'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'J<_'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'J_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :string_unpack_32bit_le, 'J<!'
+ it_behaves_like :string_unpack_32bit_le, 'J!<'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'J<!'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'J!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :string_unpack_32bit_be, 'J>_'
+ it_behaves_like :string_unpack_32bit_be, 'J_>'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'J>_'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'J_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :string_unpack_32bit_be, 'J>!'
+ it_behaves_like :string_unpack_32bit_be, 'J!>'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'J>!'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'J!>'
+ end
+ end
+
+ describe "String#unpack with format 'j'" do
+ describe "with modifier '<'" do
+ it_behaves_like :string_unpack_32bit_le, 'j<'
+ it_behaves_like :string_unpack_32bit_le_signed, 'j<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :string_unpack_32bit_be, 'j>'
+ it_behaves_like :string_unpack_32bit_be_signed, 'j>'
+ end
+
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :string_unpack_32bit_le, 'j<_'
+ it_behaves_like :string_unpack_32bit_le, 'j_<'
+ it_behaves_like :string_unpack_32bit_le_signed, 'j<_'
+ it_behaves_like :string_unpack_32bit_le_signed, 'j_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :string_unpack_32bit_le, 'j<!'
+ it_behaves_like :string_unpack_32bit_le, 'j!<'
+ it_behaves_like :string_unpack_32bit_le_signed, 'j<!'
+ it_behaves_like :string_unpack_32bit_le_signed, 'j!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :string_unpack_32bit_be, 'j>_'
+ it_behaves_like :string_unpack_32bit_be, 'j_>'
+ it_behaves_like :string_unpack_32bit_be_signed, 'j>_'
+ it_behaves_like :string_unpack_32bit_be_signed, 'j_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :string_unpack_32bit_be, 'j>!'
+ it_behaves_like :string_unpack_32bit_be, 'j!>'
+ it_behaves_like :string_unpack_32bit_be_signed, 'j>!'
+ it_behaves_like :string_unpack_32bit_be_signed, 'j!>'
+ end
+ end
+end
diff --git a/spec/ruby/core/string/unpack/l_spec.rb b/spec/ruby/core/string/unpack/l_spec.rb
new file mode 100644
index 0000000000..6f9fcd4fd0
--- /dev/null
+++ b/spec/ruby/core/string/unpack/l_spec.rb
@@ -0,0 +1,265 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/integer'
+
+describe "String#unpack with format 'L'" do
+ describe "with modifier '<'" do
+ it_behaves_like :string_unpack_32bit_le, 'L<'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'L<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :string_unpack_32bit_be, 'L>'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'L>'
+ end
+
+ guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :string_unpack_32bit_le, 'L<_'
+ it_behaves_like :string_unpack_32bit_le, 'L_<'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'L<_'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'L_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :string_unpack_32bit_le, 'L<!'
+ it_behaves_like :string_unpack_32bit_le, 'L!<'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'L<!'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'L!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :string_unpack_32bit_be, 'L>_'
+ it_behaves_like :string_unpack_32bit_be, 'L_>'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'L>_'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'L_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :string_unpack_32bit_be, 'L>!'
+ it_behaves_like :string_unpack_32bit_be, 'L!>'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'L>!'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'L!>'
+ end
+ end
+
+ guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :string_unpack_64bit_le, 'L<_'
+ it_behaves_like :string_unpack_64bit_le, 'L_<'
+ it_behaves_like :string_unpack_64bit_le_unsigned, 'L<_'
+ it_behaves_like :string_unpack_64bit_le_unsigned, 'L_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :string_unpack_64bit_le, 'L<!'
+ it_behaves_like :string_unpack_64bit_le, 'L!<'
+ it_behaves_like :string_unpack_64bit_le_unsigned, 'L<!'
+ it_behaves_like :string_unpack_64bit_le_unsigned, 'L!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :string_unpack_64bit_be, 'L>_'
+ it_behaves_like :string_unpack_64bit_be, 'L_>'
+ it_behaves_like :string_unpack_64bit_be_unsigned, 'L>_'
+ it_behaves_like :string_unpack_64bit_be_unsigned, 'L_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :string_unpack_64bit_be, 'L>!'
+ it_behaves_like :string_unpack_64bit_be, 'L!>'
+ it_behaves_like :string_unpack_64bit_be_unsigned, 'L>!'
+ it_behaves_like :string_unpack_64bit_be_unsigned, 'L!>'
+ end
+ end
+end
+
+describe "String#unpack with format 'l'" do
+ describe "with modifier '<'" do
+ it_behaves_like :string_unpack_32bit_le, 'l<'
+ it_behaves_like :string_unpack_32bit_le_signed, 'l<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :string_unpack_32bit_be, 'l>'
+ it_behaves_like :string_unpack_32bit_be_signed, 'l>'
+ end
+
+ guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :string_unpack_32bit_le, 'l<_'
+ it_behaves_like :string_unpack_32bit_le, 'l_<'
+ it_behaves_like :string_unpack_32bit_le_signed, 'l<_'
+ it_behaves_like :string_unpack_32bit_le_signed, 'l_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :string_unpack_32bit_le, 'l<!'
+ it_behaves_like :string_unpack_32bit_le, 'l!<'
+ it_behaves_like :string_unpack_32bit_le_signed, 'l<!'
+ it_behaves_like :string_unpack_32bit_le_signed, 'l!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :string_unpack_32bit_be, 'l>_'
+ it_behaves_like :string_unpack_32bit_be, 'l_>'
+ it_behaves_like :string_unpack_32bit_be_signed, 'l>_'
+ it_behaves_like :string_unpack_32bit_be_signed, 'l_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :string_unpack_32bit_be, 'l>!'
+ it_behaves_like :string_unpack_32bit_be, 'l!>'
+ it_behaves_like :string_unpack_32bit_be_signed, 'l>!'
+ it_behaves_like :string_unpack_32bit_be_signed, 'l!>'
+ end
+ end
+
+ guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :string_unpack_64bit_le, 'l<_'
+ it_behaves_like :string_unpack_64bit_le, 'l_<'
+ it_behaves_like :string_unpack_64bit_le_signed, 'l<_'
+ it_behaves_like :string_unpack_64bit_le_signed, 'l_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :string_unpack_64bit_le, 'l<!'
+ it_behaves_like :string_unpack_64bit_le, 'l!<'
+ it_behaves_like :string_unpack_64bit_le_signed, 'l<!'
+ it_behaves_like :string_unpack_64bit_le_signed, 'l!<'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :string_unpack_64bit_be, 'l>_'
+ it_behaves_like :string_unpack_64bit_be, 'l_>'
+ it_behaves_like :string_unpack_64bit_be_signed, 'l>_'
+ it_behaves_like :string_unpack_64bit_be_signed, 'l_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :string_unpack_64bit_be, 'l>!'
+ it_behaves_like :string_unpack_64bit_be, 'l!>'
+ it_behaves_like :string_unpack_64bit_be_signed, 'l>!'
+ it_behaves_like :string_unpack_64bit_be_signed, 'l!>'
+ end
+ end
+end
+
+little_endian do
+ describe "String#unpack with format 'L'" do
+ it_behaves_like :string_unpack_basic, 'L'
+ it_behaves_like :string_unpack_32bit_le, 'L'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'L'
+ end
+
+ describe "String#unpack with format 'l'" do
+ it_behaves_like :string_unpack_basic, 'l'
+ it_behaves_like :string_unpack_32bit_le, 'l'
+ it_behaves_like :string_unpack_32bit_le_signed, 'l'
+ end
+
+ guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do
+ describe "String#unpack with format 'L' with modifier '_'" do
+ it_behaves_like :string_unpack_32bit_le, 'L_'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'L_'
+ end
+
+ describe "String#unpack with format 'L' with modifier '!'" do
+ it_behaves_like :string_unpack_32bit_le, 'L!'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'L!'
+ end
+
+ describe "String#unpack with format 'l' with modifier '_'" do
+ it_behaves_like :string_unpack_32bit_le, 'l_'
+ it_behaves_like :string_unpack_32bit_le_signed, 'l'
+ end
+
+ describe "String#unpack with format 'l' with modifier '!'" do
+ it_behaves_like :string_unpack_32bit_le, 'l!'
+ it_behaves_like :string_unpack_32bit_le_signed, 'l'
+ end
+ end
+
+ guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do
+ describe "String#unpack with format 'L' with modifier '_'" do
+ it_behaves_like :string_unpack_64bit_le, 'L_'
+ it_behaves_like :string_unpack_64bit_le_unsigned, 'L_'
+ end
+
+ describe "String#unpack with format 'L' with modifier '!'" do
+ it_behaves_like :string_unpack_64bit_le, 'L!'
+ it_behaves_like :string_unpack_64bit_le_unsigned, 'L!'
+ end
+
+ describe "String#unpack with format 'l' with modifier '_'" do
+ it_behaves_like :string_unpack_64bit_le, 'l_'
+ it_behaves_like :string_unpack_64bit_le_signed, 'l_'
+ end
+
+ describe "String#unpack with format 'l' with modifier '!'" do
+ it_behaves_like :string_unpack_64bit_le, 'l!'
+ it_behaves_like :string_unpack_64bit_le_signed, 'l!'
+ end
+ end
+end
+
+big_endian do
+ describe "String#unpack with format 'L'" do
+ it_behaves_like :string_unpack_basic, 'L'
+ it_behaves_like :string_unpack_32bit_be, 'L'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'L'
+ end
+
+ describe "String#unpack with format 'l'" do
+ it_behaves_like :string_unpack_basic, 'l'
+ it_behaves_like :string_unpack_32bit_be, 'l'
+ it_behaves_like :string_unpack_32bit_be_signed, 'l'
+ end
+
+ guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do
+ describe "String#unpack with format 'L' with modifier '_'" do
+ it_behaves_like :string_unpack_32bit_be, 'L_'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'L_'
+ end
+
+ describe "String#unpack with format 'L' with modifier '!'" do
+ it_behaves_like :string_unpack_32bit_be, 'L!'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'L!'
+ end
+
+ describe "String#unpack with format 'l' with modifier '_'" do
+ it_behaves_like :string_unpack_32bit_be, 'l_'
+ it_behaves_like :string_unpack_32bit_be_signed, 'l'
+ end
+
+ describe "String#unpack with format 'l' with modifier '!'" do
+ it_behaves_like :string_unpack_32bit_be, 'l!'
+ it_behaves_like :string_unpack_32bit_be_signed, 'l'
+ end
+ end
+
+ guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do
+ describe "String#unpack with format 'L' with modifier '_'" do
+ it_behaves_like :string_unpack_64bit_be, 'L_'
+ it_behaves_like :string_unpack_64bit_be_unsigned, 'L_'
+ end
+
+ describe "String#unpack with format 'L' with modifier '!'" do
+ it_behaves_like :string_unpack_64bit_be, 'L!'
+ it_behaves_like :string_unpack_64bit_be_unsigned, 'L!'
+ end
+
+ describe "String#unpack with format 'l' with modifier '_'" do
+ it_behaves_like :string_unpack_64bit_be, 'l_'
+ it_behaves_like :string_unpack_64bit_be_signed, 'l_'
+ end
+
+ describe "String#unpack with format 'l' with modifier '!'" do
+ it_behaves_like :string_unpack_64bit_be, 'l!'
+ it_behaves_like :string_unpack_64bit_be_signed, 'l!'
+ end
+ end
+
+end
diff --git a/spec/ruby/core/string/unpack/m_spec.rb b/spec/ruby/core/string/unpack/m_spec.rb
new file mode 100644
index 0000000000..d714f6fbcb
--- /dev/null
+++ b/spec/ruby/core/string/unpack/m_spec.rb
@@ -0,0 +1,173 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/taint'
+
+describe "String#unpack with format 'M'" do
+ it_behaves_like :string_unpack_basic, 'M'
+ it_behaves_like :string_unpack_no_platform, 'M'
+ it_behaves_like :string_unpack_taint, 'M'
+
+ it "decodes an empty string" do
+ "".unpack("M").should == [""]
+ end
+
+ it "decodes the complete string ignoring newlines when given a single directive" do
+ "a=\nb=\nc=\n".unpack("M").should == ["abc"]
+ end
+
+ it "appends empty string to the array for directives exceeding the input size" do
+ "a=\nb=\nc=\n".unpack("MMM").should == ["abc", "", ""]
+ end
+
+ it "ignores the count or '*' modifier and decodes the entire string" do
+ [ ["a=\nb=\nc=\n", "M238", ["abc"]],
+ ["a=\nb=\nc=\n", "M*", ["abc"]]
+ ].should be_computed_by(:unpack)
+ end
+
+ it "decodes the '=' character" do
+ "=3D=\n".unpack("M").should == ["="]
+ end
+
+ it "decodes an embedded space character" do
+ "a b=\n".unpack("M").should == ["a b"]
+ end
+
+ it "decodes a space at the end of the pre-encoded string" do
+ "a =\n".unpack("M").should == ["a "]
+ end
+
+ it "decodes an embedded tab character" do
+ "a\tb=\n".unpack("M").should == ["a\tb"]
+ end
+
+ it "decodes a tab character at the end of the pre-encoded string" do
+ "a\t=\n".unpack("M").should == ["a\t"]
+ end
+
+ it "decodes an embedded newline" do
+ "a\nb=\n".unpack("M").should == ["a\nb"]
+ end
+
+ it "decodes pre-encoded byte values 33..60" do
+ [ ["!\"\#$%&'()*+,-./=\n", ["!\"\#$%&'()*+,-./"]],
+ ["0123456789=\n", ["0123456789"]],
+ [":;<=\n", [":;<"]]
+ ].should be_computed_by(:unpack, "M")
+ end
+
+ it "decodes pre-encoded byte values 62..126" do
+ [ [">?@=\n", [">?@"]],
+ ["ABCDEFGHIJKLMNOPQRSTUVWXYZ=\n", ["ABCDEFGHIJKLMNOPQRSTUVWXYZ"]],
+ ["[\\]^_`=\n", ["[\\]^_`"]],
+ ["abcdefghijklmnopqrstuvwxyz=\n", ["abcdefghijklmnopqrstuvwxyz"]],
+ ["{|}~=\n", ["{|}~"]]
+ ].should be_computed_by(:unpack, "M")
+ end
+
+ it "decodes pre-encoded byte values 0..31 except tab and newline" do
+ [ ["=00=01=02=03=04=05=06=\n", ["\x00\x01\x02\x03\x04\x05\x06"]],
+ ["=07=08=0B=0C=0D=\n", ["\a\b\v\f\r"]],
+ ["=0E=0F=10=11=12=13=14=\n", ["\x0e\x0f\x10\x11\x12\x13\x14"]],
+ ["=15=16=17=18=19=1A=\n", ["\x15\x16\x17\x18\x19\x1a"]],
+ ["=1B=\n", ["\e"]],
+ ["=1C=1D=1E=1F=\n", ["\x1c\x1d\x1e\x1f"]]
+ ].should be_computed_by(:unpack, "M")
+ end
+
+ it "decodes pre-encoded byte values 127..255" do
+ [ ["=7F=80=81=82=83=84=85=86=\n", ["\x7f\x80\x81\x82\x83\x84\x85\x86"]],
+ ["=87=88=89=8A=8B=8C=8D=8E=\n", ["\x87\x88\x89\x8a\x8b\x8c\x8d\x8e"]],
+ ["=8F=90=91=92=93=94=95=96=\n", ["\x8f\x90\x91\x92\x93\x94\x95\x96"]],
+ ["=97=98=99=9A=9B=9C=9D=9E=\n", ["\x97\x98\x99\x9a\x9b\x9c\x9d\x9e"]],
+ ["=9F=A0=A1=A2=A3=A4=A5=A6=\n", ["\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6"]],
+ ["=A7=A8=A9=AA=AB=AC=AD=AE=\n", ["\xa7\xa8\xa9\xaa\xab\xac\xad\xae"]],
+ ["=AF=B0=B1=B2=B3=B4=B5=B6=\n", ["\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6"]],
+ ["=B7=B8=B9=BA=BB=BC=BD=BE=\n", ["\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe"]],
+ ["=BF=C0=C1=C2=C3=C4=C5=C6=\n", ["\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6"]],
+ ["=C7=C8=C9=CA=CB=CC=CD=CE=\n", ["\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce"]],
+ ["=CF=D0=D1=D2=D3=D4=D5=D6=\n", ["\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6"]],
+ ["=D7=D8=D9=DA=DB=DC=DD=DE=\n", ["\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde"]],
+ ["=DF=E0=E1=E2=E3=E4=E5=E6=\n", ["\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6"]],
+ ["=E7=E8=E9=EA=EB=EC=ED=EE=\n", ["\xe7\xe8\xe9\xea\xeb\xec\xed\xee"]],
+ ["=EF=F0=F1=F2=F3=F4=F5=F6=\n", ["\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6"]],
+ ["=F7=F8=F9=FA=FB=FC=FD=FE=\n", ["\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe"]],
+ ["=FF=\n", ["\xff"]]
+ ].should be_computed_by(:unpack, "M")
+ end
+end
+
+describe "String#unpack with format 'm'" do
+ it_behaves_like :string_unpack_basic, 'm'
+ it_behaves_like :string_unpack_no_platform, 'm'
+ it_behaves_like :string_unpack_taint, 'm'
+
+ it "decodes an empty string" do
+ "".unpack("m").should == [""]
+ end
+
+ it "decodes the complete string ignoring newlines when given a single directive" do
+ "YWJj\nREVG\n".unpack("m").should == ["abcDEF"]
+ end
+
+ it "ignores the count or '*' modifier and decodes the entire string" do
+ [ ["YWJj\nREVG\n", "m238", ["abcDEF"]],
+ ["YWJj\nREVG\n", "m*", ["abcDEF"]]
+ ].should be_computed_by(:unpack)
+ end
+
+ it "appends empty string to the array for directives exceeding the input size" do
+ "YWJj\nREVG\n".unpack("mmm").should == ["abcDEF", "", ""]
+ end
+
+ it "decodes all pre-encoded ascii byte values" do
+ [ ["AAECAwQFBg==\n", ["\x00\x01\x02\x03\x04\x05\x06"]],
+ ["BwgJCgsMDQ==\n", ["\a\b\t\n\v\f\r"]],
+ ["Dg8QERITFBUW\n", ["\x0E\x0F\x10\x11\x12\x13\x14\x15\x16"]],
+ ["FxgZGhscHR4f\n", ["\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f"]],
+ ["ISIjJCUmJygpKissLS4v\n", ["!\"\#$%&'()*+,-./"]],
+ ["MDEyMzQ1Njc4OQ==\n", ["0123456789"]],
+ ["Ojs8PT4/QA==\n", [":;<=>?@"]],
+ ["QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=\n", ["ABCDEFGHIJKLMNOPQRSTUVWXYZ"]],
+ ["W1xdXl9g\n", ["[\\]^_`"]],
+ ["YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=\n", ["abcdefghijklmnopqrstuvwxyz"]],
+ ["e3x9fg==\n", ["{|}~"]],
+ ["f8KAwoHCgsKD\n", ["\x7f\xc2\x80\xc2\x81\xc2\x82\xc2\x83"]],
+ ["woTChcKGwofC\n", ["\xc2\x84\xc2\x85\xc2\x86\xc2\x87\xc2"]],
+ ["iMKJworCi8KM\n", ["\x88\xc2\x89\xc2\x8a\xc2\x8b\xc2\x8c"]],
+ ["wo3CjsKPwpDC\n", ["\xc2\x8d\xc2\x8e\xc2\x8f\xc2\x90\xc2"]],
+ ["kcKSwpPClMKV\n", ["\x91\xc2\x92\xc2\x93\xc2\x94\xc2\x95"]],
+ ["wpbCl8KYwpnC\n", ["\xc2\x96\xc2\x97\xc2\x98\xc2\x99\xc2"]],
+ ["msKbwpzCncKe\n", ["\x9a\xc2\x9b\xc2\x9c\xc2\x9d\xc2\x9e"]],
+ ["wp/CoMKhwqLC\n", ["\xc2\x9f\xc2\xa0\xc2\xa1\xc2\xa2\xc2"]],
+ ["o8KkwqXCpsKn\n", ["\xa3\xc2\xa4\xc2\xa5\xc2\xa6\xc2\xa7"]],
+ ["wqjCqcKqwqvC\n", ["\xc2\xa8\xc2\xa9\xc2\xaa\xc2\xab\xc2"]],
+ ["rMKtwq7Cr8Kw\n", ["\xac\xc2\xad\xc2\xae\xc2\xaf\xc2\xb0"]],
+ ["wrHCssKzwrTC\n", ["\xc2\xb1\xc2\xb2\xc2\xb3\xc2\xb4\xc2"]],
+ ["tcK2wrfCuMK5\n", ["\xb5\xc2\xb6\xc2\xb7\xc2\xb8\xc2\xb9"]],
+ ["wrrCu8K8wr3C\n", ["\xc2\xba\xc2\xbb\xc2\xbc\xc2\xbd\xc2"]],
+ ["vsK/w4DDgcOC\n", ["\xbe\xc2\xbf\xc3\x80\xc3\x81\xc3\x82"]],
+ ["w4PDhMOFw4bD\n", ["\xc3\x83\xc3\x84\xc3\x85\xc3\x86\xc3"]],
+ ["h8OIw4nDisOL\n", ["\x87\xc3\x88\xc3\x89\xc3\x8a\xc3\x8b"]],
+ ["w4zDjcOOw4/D\n", ["\xc3\x8c\xc3\x8d\xc3\x8e\xc3\x8f\xc3"]],
+ ["kMORw5LDk8OU\n", ["\x90\xc3\x91\xc3\x92\xc3\x93\xc3\x94"]],
+ ["w5XDlsOXw5jD\n", ["\xc3\x95\xc3\x96\xc3\x97\xc3\x98\xc3"]],
+ ["mcOaw5vDnMOd\n", ["\x99\xc3\x9a\xc3\x9b\xc3\x9c\xc3\x9d"]],
+ ["w57Dn8Ogw6HD\n", ["\xc3\x9e\xc3\x9f\xc3\xa0\xc3\xa1\xc3"]],
+ ["osOjw6TDpcOm\n", ["\xa2\xc3\xa3\xc3\xa4\xc3\xa5\xc3\xa6"]],
+ ["w6fDqMOpw6rD\n", ["\xc3\xa7\xc3\xa8\xc3\xa9\xc3\xaa\xc3"]],
+ ["q8Osw63DrsOv\n", ["\xab\xc3\xac\xc3\xad\xc3\xae\xc3\xaf"]],
+ ["w7DDscOyw7PD\n", ["\xc3\xb0\xc3\xb1\xc3\xb2\xc3\xb3\xc3"]],
+ ["tMO1w7bDt8O4\n", ["\xb4\xc3\xb5\xc3\xb6\xc3\xb7\xc3\xb8"]],
+ ["w7nDusO7w7zD\n", ["\xc3\xb9\xc3\xba\xc3\xbb\xc3\xbc\xc3"]],
+ ["vcO+w78=\n", ["\xbd\xc3\xbe\xc3\xbf"]]
+ ].should be_computed_by(:unpack, "m")
+ end
+
+ it "produces binary strings" do
+ "".unpack("m").first.encoding.should == Encoding::BINARY
+ "Ojs8PT4/QA==\n".unpack("m").first.encoding.should == Encoding::BINARY
+ end
+end
diff --git a/spec/ruby/core/string/unpack/n_spec.rb b/spec/ruby/core/string/unpack/n_spec.rb
new file mode 100644
index 0000000000..09173f4fcb
--- /dev/null
+++ b/spec/ruby/core/string/unpack/n_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/integer'
+
+describe "String#unpack with format 'N'" do
+ it_behaves_like :string_unpack_basic, 'N'
+ it_behaves_like :string_unpack_32bit_be, 'N'
+ it_behaves_like :string_unpack_32bit_be_unsigned, 'N'
+ it_behaves_like :string_unpack_no_platform, 'N'
+end
+
+describe "String#unpack with format 'n'" do
+ it_behaves_like :string_unpack_basic, 'n'
+ it_behaves_like :string_unpack_16bit_be, 'n'
+ it_behaves_like :string_unpack_16bit_be_unsigned, 'n'
+ it_behaves_like :string_unpack_no_platform, 'n'
+end
diff --git a/spec/ruby/core/string/unpack/p_spec.rb b/spec/ruby/core/string/unpack/p_spec.rb
new file mode 100644
index 0000000000..136e32adfd
--- /dev/null
+++ b/spec/ruby/core/string/unpack/p_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/taint'
+
+describe "String#unpack with format 'P'" do
+ it_behaves_like :string_unpack_basic, 'P'
+ it_behaves_like :string_unpack_taint, 'P'
+
+ it "round-trips a string through pack and unpack" do
+ ["hello"].pack("P").unpack("P5").should == ["hello"]
+ end
+
+ it "cannot unpack a string except from the same object that created it, or a duplicate of it" do
+ packed = ["hello"].pack("P")
+ packed.unpack("P5").should == ["hello"]
+ packed.dup.unpack("P5").should == ["hello"]
+ lambda { packed.to_sym.to_s.unpack("P5") }.should raise_error(ArgumentError, /no associated pointer/)
+ end
+
+ it "taints the unpacked string" do
+ ["hello"].pack("P").unpack("P5").first.tainted?.should be_true
+ end
+
+ it "reads as many characters as specified" do
+ ["hello"].pack("P").unpack("P1").should == ["h"]
+ end
+
+ it "reads only as far as a NUL character" do
+ ["hello"].pack("P").unpack("P10").should == ["hello"]
+ end
+end
+
+describe "String#unpack with format 'p'" do
+ it_behaves_like :string_unpack_basic, 'p'
+ it_behaves_like :string_unpack_taint, 'p'
+
+ it "round-trips a string through pack and unpack" do
+ ["hello"].pack("p").unpack("p").should == ["hello"]
+ end
+
+ it "cannot unpack a string except from the same object that created it, or a duplicate of it" do
+ packed = ["hello"].pack("p")
+ packed.unpack("p").should == ["hello"]
+ packed.dup.unpack("p").should == ["hello"]
+ lambda { packed.to_sym.to_s.unpack("p") }.should raise_error(ArgumentError, /no associated pointer/)
+ end
+
+ it "taints the unpacked string" do
+ ["hello"].pack("p").unpack("p").first.tainted?.should be_true
+ end
+end
diff --git a/spec/ruby/core/string/unpack/percent_spec.rb b/spec/ruby/core/string/unpack/percent_spec.rb
new file mode 100644
index 0000000000..f0684d4cbc
--- /dev/null
+++ b/spec/ruby/core/string/unpack/percent_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+
+describe "String#unpack with format '%'" do
+ it "raises an Argument Error" do
+ lambda { "abc".unpack("%") }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/string/unpack/q_spec.rb b/spec/ruby/core/string/unpack/q_spec.rb
new file mode 100644
index 0000000000..2f667d6c4d
--- /dev/null
+++ b/spec/ruby/core/string/unpack/q_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/integer'
+
+describe "String#unpack with format 'Q'" do
+ describe "with modifier '<'" do
+ it_behaves_like :string_unpack_64bit_le, 'Q<'
+ it_behaves_like :string_unpack_64bit_le_unsigned, 'Q<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :string_unpack_64bit_be, 'Q>'
+ it_behaves_like :string_unpack_64bit_be_unsigned, 'Q>'
+ end
+end
+
+describe "String#unpack with format 'q'" do
+ describe "with modifier '<'" do
+ it_behaves_like :string_unpack_64bit_le, 'q<'
+ it_behaves_like :string_unpack_64bit_le_signed, 'q<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :string_unpack_64bit_be, 'q>'
+ it_behaves_like :string_unpack_64bit_be_signed, 'q>'
+ end
+end
+
+describe "String#unpack with format 'Q'" do
+ it_behaves_like :string_unpack_basic, 'Q'
+end
+
+describe "String#unpack with format 'q'" do
+ it_behaves_like :string_unpack_basic, 'q'
+end
+
+little_endian do
+ describe "String#unpack with format 'Q'" do
+ it_behaves_like :string_unpack_64bit_le, 'Q'
+ it_behaves_like :string_unpack_64bit_le_extra, 'Q'
+ it_behaves_like :string_unpack_64bit_le_unsigned, 'Q'
+ end
+
+ describe "String#unpack with format 'q'" do
+ it_behaves_like :string_unpack_64bit_le, 'q'
+ it_behaves_like :string_unpack_64bit_le_extra, 'q'
+ it_behaves_like :string_unpack_64bit_le_signed, 'q'
+ end
+end
+
+big_endian do
+ describe "String#unpack with format 'Q'" do
+ it_behaves_like :string_unpack_64bit_be, 'Q'
+ it_behaves_like :string_unpack_64bit_be_extra, 'Q'
+ it_behaves_like :string_unpack_64bit_be_unsigned, 'Q'
+ end
+
+ describe "String#unpack with format 'q'" do
+ it_behaves_like :string_unpack_64bit_be, 'q'
+ it_behaves_like :string_unpack_64bit_be_extra, 'q'
+ it_behaves_like :string_unpack_64bit_be_signed, 'q'
+ end
+end
diff --git a/spec/ruby/core/string/unpack/s_spec.rb b/spec/ruby/core/string/unpack/s_spec.rb
new file mode 100644
index 0000000000..d331fd720e
--- /dev/null
+++ b/spec/ruby/core/string/unpack/s_spec.rb
@@ -0,0 +1,152 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/integer'
+
+describe "String#unpack with format 'S'" do
+ describe "with modifier '<'" do
+ it_behaves_like :string_unpack_16bit_le, 'S<'
+ it_behaves_like :string_unpack_16bit_le_unsigned, 'S<'
+ end
+
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :string_unpack_16bit_le, 'S<_'
+ it_behaves_like :string_unpack_16bit_le, 'S_<'
+ it_behaves_like :string_unpack_16bit_le_unsigned, 'S_<'
+ it_behaves_like :string_unpack_16bit_le_unsigned, 'S<_'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :string_unpack_16bit_le, 'S<!'
+ it_behaves_like :string_unpack_16bit_le, 'S!<'
+ it_behaves_like :string_unpack_16bit_le_unsigned, 'S!<'
+ it_behaves_like :string_unpack_16bit_le_unsigned, 'S<!'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :string_unpack_16bit_be, 'S>'
+ it_behaves_like :string_unpack_16bit_be_unsigned, 'S>'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :string_unpack_16bit_be, 'S>_'
+ it_behaves_like :string_unpack_16bit_be, 'S_>'
+ it_behaves_like :string_unpack_16bit_be_unsigned, 'S>_'
+ it_behaves_like :string_unpack_16bit_be_unsigned, 'S_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :string_unpack_16bit_be, 'S>!'
+ it_behaves_like :string_unpack_16bit_be, 'S!>'
+ it_behaves_like :string_unpack_16bit_be_unsigned, 'S>!'
+ it_behaves_like :string_unpack_16bit_be_unsigned, 'S!>'
+ end
+end
+
+describe "String#unpack with format 's'" do
+ describe "with modifier '<'" do
+ it_behaves_like :string_unpack_16bit_le, 's<'
+ it_behaves_like :string_unpack_16bit_le_signed, 's<'
+ end
+
+ describe "with modifier '<' and '_'" do
+ it_behaves_like :string_unpack_16bit_le, 's<_'
+ it_behaves_like :string_unpack_16bit_le, 's_<'
+ it_behaves_like :string_unpack_16bit_le_signed, 's<_'
+ it_behaves_like :string_unpack_16bit_le_signed, 's_<'
+ end
+
+ describe "with modifier '<' and '!'" do
+ it_behaves_like :string_unpack_16bit_le, 's<!'
+ it_behaves_like :string_unpack_16bit_le, 's!<'
+ it_behaves_like :string_unpack_16bit_le_signed, 's<!'
+ it_behaves_like :string_unpack_16bit_le_signed, 's!<'
+ end
+
+ describe "with modifier '>'" do
+ it_behaves_like :string_unpack_16bit_be, 's>'
+ it_behaves_like :string_unpack_16bit_be_signed, 's>'
+ end
+
+ describe "with modifier '>' and '_'" do
+ it_behaves_like :string_unpack_16bit_be, 's>_'
+ it_behaves_like :string_unpack_16bit_be, 's_>'
+ it_behaves_like :string_unpack_16bit_be_signed, 's>_'
+ it_behaves_like :string_unpack_16bit_be_signed, 's_>'
+ end
+
+ describe "with modifier '>' and '!'" do
+ it_behaves_like :string_unpack_16bit_be, 's>!'
+ it_behaves_like :string_unpack_16bit_be, 's!>'
+ it_behaves_like :string_unpack_16bit_be_signed, 's>!'
+ it_behaves_like :string_unpack_16bit_be_signed, 's!>'
+ end
+end
+
+little_endian do
+ describe "String#unpack with format 'S'" do
+ it_behaves_like :string_unpack_basic, 'S'
+ it_behaves_like :string_unpack_16bit_le, 'S'
+ it_behaves_like :string_unpack_16bit_le_unsigned, 'S'
+ end
+
+ describe "String#unpack with format 'S' with modifier '_'" do
+ it_behaves_like :string_unpack_16bit_le, 'S_'
+ it_behaves_like :string_unpack_16bit_le_unsigned, 'S_'
+ end
+
+ describe "String#unpack with format 'S' with modifier '!'" do
+ it_behaves_like :string_unpack_16bit_le, 'S!'
+ it_behaves_like :string_unpack_16bit_le_unsigned, 'S!'
+ end
+
+ describe "String#unpack with format 's'" do
+ it_behaves_like :string_unpack_basic, 's'
+ it_behaves_like :string_unpack_16bit_le, 's'
+ it_behaves_like :string_unpack_16bit_le_signed, 's'
+ end
+
+ describe "String#unpack with format 's' with modifier '_'" do
+ it_behaves_like :string_unpack_16bit_le, 's_'
+ it_behaves_like :string_unpack_16bit_le_signed, 's_'
+ end
+
+ describe "String#unpack with format 's' with modifier '!'" do
+ it_behaves_like :string_unpack_16bit_le, 's!'
+ it_behaves_like :string_unpack_16bit_le_signed, 's!'
+ end
+end
+
+big_endian do
+ describe "String#unpack with format 'S'" do
+ it_behaves_like :string_unpack_basic, 'S'
+ it_behaves_like :string_unpack_16bit_be, 'S'
+ it_behaves_like :string_unpack_16bit_be_unsigned, 'S'
+ end
+
+ describe "String#unpack with format 'S' with modifier '_'" do
+ it_behaves_like :string_unpack_16bit_be, 'S_'
+ it_behaves_like :string_unpack_16bit_be_unsigned, 'S_'
+ end
+
+ describe "String#unpack with format 'S' with modifier '!'" do
+ it_behaves_like :string_unpack_16bit_be, 'S!'
+ it_behaves_like :string_unpack_16bit_be_unsigned, 'S!'
+ end
+
+ describe "String#unpack with format 's'" do
+ it_behaves_like :string_unpack_basic, 's'
+ it_behaves_like :string_unpack_16bit_be, 's'
+ it_behaves_like :string_unpack_16bit_be_signed, 's'
+ end
+
+ describe "String#unpack with format 's' with modifier '_'" do
+ it_behaves_like :string_unpack_16bit_be, 's_'
+ it_behaves_like :string_unpack_16bit_be_signed, 's_'
+ end
+
+ describe "String#unpack with format 's' with modifier '!'" do
+ it_behaves_like :string_unpack_16bit_be, 's!'
+ it_behaves_like :string_unpack_16bit_be_signed, 's!'
+ end
+end
diff --git a/spec/ruby/core/string/unpack/shared/basic.rb b/spec/ruby/core/string/unpack/shared/basic.rb
new file mode 100644
index 0000000000..0ecbf615af
--- /dev/null
+++ b/spec/ruby/core/string/unpack/shared/basic.rb
@@ -0,0 +1,29 @@
+describe :string_unpack_basic, shared: true do
+ it "ignores whitespace in the format string" do
+ "abc".unpack("a \t\n\v\f\r"+unpack_format).should be_an_instance_of(Array)
+ end
+
+ it "calls #to_str to coerce the directives string" do
+ d = mock("unpack directive")
+ 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
+ lambda { "abc".unpack(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed an Integer" do
+ lambda { "abc".unpack(1) }.should raise_error(TypeError)
+ end
+end
+
+describe :string_unpack_no_platform, shared: true do
+ it "raises an ArgumentError when the format modifier is '_'" do
+ lambda { "abcdefgh".unpack(unpack_format("_")) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when the format modifier is '!'" do
+ lambda { "abcdefgh".unpack(unpack_format("!")) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/string/unpack/shared/float.rb b/spec/ruby/core/string/unpack/shared/float.rb
new file mode 100644
index 0000000000..208dc357af
--- /dev/null
+++ b/spec/ruby/core/string/unpack/shared/float.rb
@@ -0,0 +1,271 @@
+# -*- encoding: ascii-8bit -*-
+
+describe :string_unpack_float_le, shared: true do
+ it "decodes one float for a single format character" do
+ "\x8f\xc2\xb5?".unpack(unpack_format).should == [1.4199999570846558]
+ end
+
+ it "decodes a negative float" do
+ "\xcd\xcc\x08\xc2".unpack(unpack_format).should == [-34.200000762939453]
+ end
+
+ it "decodes two floats for two format characters" do
+ array = "\x9a\x999@33\xb3?".unpack(unpack_format(nil, 2))
+ array.should == [2.9000000953674316, 1.399999976158142]
+ end
+
+ it "decodes the number of floats requested by the count modifier" do
+ array = "\x9a\x999@33\xb3?33\x03A".unpack(unpack_format(3))
+ array.should == [2.9000000953674316, 1.399999976158142, 8.199999809265137]
+ end
+
+ it "decodes the remaining floats when passed the '*' modifier" do
+ array = "\x9a\x999@33\xb3?33\x03A".unpack(unpack_format("*"))
+ array.should == [2.9000000953674316, 1.399999976158142, 8.199999809265137]
+ end
+
+ it "decodes the remaining floats when passed the '*' modifier after another directive" do
+ array = "\x9a\x99\xa9@33\x13A".unpack(unpack_format()+unpack_format('*'))
+ array.should == [5.300000190734863, 9.199999809265137]
+ end
+
+ it "does not decode a float when fewer bytes than a float remain and the '*' modifier is passed" do
+ [ ["\xff", []],
+ ["\xff\x00", []],
+ ["\xff\x00\xff", []]
+ ].should be_computed_by(:unpack, unpack_format("*"))
+ end
+
+ it "adds nil for each element requested beyond the end of the String" do
+ [ ["abc", [nil, nil, nil]],
+ ["\x8f\xc2\xb5?abc", [1.4199999570846558, nil, nil]],
+ ["\x9a\x999@33\xb3?abc", [2.9000000953674316, 1.399999976158142, nil]]
+ ].should be_computed_by(:unpack, unpack_format(3))
+ end
+
+ it "decodes positive Infinity" do
+ "\x00\x00\x80\x7f".unpack(unpack_format).should == [infinity_value]
+ end
+
+ it "decodes negative Infinity" do
+ "\x00\x00\x80\xff".unpack(unpack_format).should == [-infinity_value]
+ end
+
+ it "decodes NaN" do
+ # mumble mumble NaN mumble https://bugs.ruby-lang.org/issues/5884
+ [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]
+ end
+
+ it "ignores spaces between directives" do
+ array = "\x9a\x999@33\xb3?".unpack(unpack_format(' ', 2))
+ array.should == [2.9000000953674316, 1.399999976158142]
+ end
+end
+
+describe :string_unpack_float_be, shared: true do
+ it "decodes one float for a single format character" do
+ "?\xb5\xc2\x8f".unpack(unpack_format).should == [1.4199999570846558]
+ end
+
+ it "decodes a negative float" do
+ "\xc2\x08\xcc\xcd".unpack(unpack_format).should == [-34.200000762939453]
+ end
+
+ it "decodes two floats for two format characters" do
+ array = "@9\x99\x9a?\xb333".unpack(unpack_format(nil, 2))
+ array.should == [2.9000000953674316, 1.399999976158142]
+ end
+
+ it "decodes the number of floats requested by the count modifier" do
+ array = "@9\x99\x9a?\xb333A\x0333".unpack(unpack_format(3))
+ array.should == [2.9000000953674316, 1.399999976158142, 8.199999809265137]
+ end
+
+ it "decodes the remaining floats when passed the '*' modifier" do
+ array = "@9\x99\x9a?\xb333A\x0333".unpack(unpack_format("*"))
+ array.should == [2.9000000953674316, 1.399999976158142, 8.199999809265137]
+ end
+
+ it "decodes the remaining floats when passed the '*' modifier after another directive" do
+ array = "@\xa9\x99\x9aA\x1333".unpack(unpack_format()+unpack_format('*'))
+ array.should == [5.300000190734863, 9.199999809265137]
+ end
+
+ it "does not decode a float when fewer bytes than a float remain and the '*' modifier is passed" do
+ [ ["\xff", []],
+ ["\xff\x00", []],
+ ["\xff\x00\xff", []]
+ ].should be_computed_by(:unpack, unpack_format("*"))
+ end
+
+ it "adds nil for each element requested beyond the end of the String" do
+ [ ["abc", [nil, nil, nil]],
+ ["?\xb5\xc2\x8fabc", [1.4199999570846558, nil, nil]],
+ ["@9\x99\x9a?\xb333abc", [2.9000000953674316, 1.399999976158142, nil]]
+ ].should be_computed_by(:unpack, unpack_format(3))
+ end
+
+ it "decodes positive Infinity" do
+ "\x7f\x80\x00\x00".unpack(unpack_format).should == [infinity_value]
+ end
+
+ it "decodes negative Infinity" do
+ "\xff\x80\x00\x00".unpack(unpack_format).should == [-infinity_value]
+ end
+
+ it "decodes NaN" do
+ # mumble mumble NaN mumble https://bugs.ruby-lang.org/issues/5884
+ [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]
+ end
+
+ it "ignores spaces between directives" do
+ array = "@9\x99\x9a?\xb333".unpack(unpack_format(' ', 2))
+ array.should == [2.9000000953674316, 1.399999976158142]
+ end
+end
+
+describe :string_unpack_double_le, shared: true do
+ it "decodes one double for a single format character" do
+ "\xb8\x1e\x85\xebQ\xb8\xf6?".unpack(unpack_format).should == [1.42]
+ end
+
+ it "decodes a negative double" do
+ "\x9a\x99\x99\x99\x99\x19A\xc0".unpack(unpack_format).should == [-34.2]
+ end
+
+ it "decodes two doubles for two format characters" do
+ "333333\x07@ffffff\xf6?".unpack(unpack_format(nil, 2)).should == [2.9, 1.4]
+ end
+
+ it "decodes the number of doubles requested by the count modifier" do
+ array = "333333\x07@ffffff\xf6?ffffff\x20@".unpack(unpack_format(3))
+ array.should == [2.9, 1.4, 8.2]
+ end
+
+ it "decodes the remaining doubles when passed the '*' modifier" do
+ array = "333333\x07@ffffff\xf6?ffffff\x20@".unpack(unpack_format("*"))
+ array.should == [2.9, 1.4, 8.2]
+ end
+
+ it "decodes the remaining doubles when passed the '*' modifier after another directive" do
+ array = "333333\x15@ffffff\x22@".unpack(unpack_format()+unpack_format('*'))
+ array.should == [5.3, 9.2]
+ end
+
+ it "does not decode a double when fewer bytes than a double remain and the '*' modifier is passed" do
+ [ ["\xff", []],
+ ["\xff\x00", []],
+ ["\xff\x00\xff", []],
+ ["\xff\x00\xff\x00", []],
+ ["\xff\x00\xff\x00\xff", []],
+ ["\xff\x00\xff\x00\xff\x00", []],
+ ["\xff\x00\xff\x00\xff\x00\xff", []]
+ ].should be_computed_by(:unpack, unpack_format("*"))
+ end
+
+ it "adds nil for each element requested beyond the end of the String" do
+ [ ["\xff\x00\xff\x00\xff\x00\xff", [nil, nil, nil]],
+ ["\xb8\x1e\x85\xebQ\xb8\xf6?abc", [1.42, nil, nil]],
+ ["333333\x07@ffffff\xf6?abcd", [2.9, 1.4, nil]]
+ ].should be_computed_by(:unpack, unpack_format(3))
+ end
+
+ it "decodes positive Infinity" do
+ "\x00\x00\x00\x00\x00\x00\xf0\x7f".unpack(unpack_format).should == [infinity_value]
+ end
+
+ it "decodes negative Infinity" do
+ "\x00\x00\x00\x00\x00\x00\xf0\xff".unpack(unpack_format).should == [-infinity_value]
+ end
+
+ it "decodes NaN" do
+ # mumble mumble NaN mumble https://bugs.ruby-lang.org/issues/5884
+ [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]
+ end
+
+ it "ignores spaces between directives" do
+ "333333\x07@ffffff\xf6?".unpack(unpack_format(' ', 2)).should == [2.9, 1.4]
+ end
+end
+
+describe :string_unpack_double_be, shared: true do
+ it "decodes one double for a single format character" do
+ "?\xf6\xb8Q\xeb\x85\x1e\xb8".unpack(unpack_format).should == [1.42]
+ end
+
+ it "decodes a negative double" do
+ "\xc0A\x19\x99\x99\x99\x99\x9a".unpack(unpack_format).should == [-34.2]
+ end
+
+ it "decodes two doubles for two format characters" do
+ "@\x07333333?\xf6ffffff".unpack(unpack_format(nil, 2)).should == [2.9, 1.4]
+ end
+
+ it "decodes the number of doubles requested by the count modifier" do
+ array = "@\x07333333?\xf6ffffff@\x20ffffff".unpack(unpack_format(3))
+ array.should == [2.9, 1.4, 8.2]
+ end
+
+ it "decodes the remaining doubles when passed the '*' modifier" do
+ array = "@\x07333333?\xf6ffffff@\x20ffffff".unpack(unpack_format("*"))
+ array.should == [2.9, 1.4, 8.2]
+ end
+
+ it "decodes the remaining doubles when passed the '*' modifier after another directive" do
+ array = "@\x15333333@\x22ffffff".unpack(unpack_format()+unpack_format('*'))
+ array.should == [5.3, 9.2]
+ end
+
+ it "does not decode a double when fewer bytes than a double remain and the '*' modifier is passed" do
+ [ ["\xff", []],
+ ["\xff\x00", []],
+ ["\xff\x00\xff", []],
+ ["\xff\x00\xff\x00", []],
+ ["\xff\x00\xff\x00\xff", []],
+ ["\xff\x00\xff\x00\xff\x00", []],
+ ["\xff\x00\xff\x00\xff\x00\xff", []]
+ ].should be_computed_by(:unpack, unpack_format("*"))
+ end
+
+ it "adds nil for each element requested beyond the end of the String" do
+ [ ["abcdefg", [nil, nil, nil]],
+ ["?\xf6\xb8Q\xeb\x85\x1e\xb8abc", [1.42, nil, nil]],
+ ["@\x07333333?\xf6ffffffabcd", [2.9, 1.4, nil]]
+ ].should be_computed_by(:unpack, unpack_format(3))
+ end
+
+ it "decodes positive Infinity" do
+ "\x7f\xf0\x00\x00\x00\x00\x00\x00".unpack(unpack_format).should == [infinity_value]
+ end
+
+ it "decodes negative Infinity" do
+ "\xff\xf0\x00\x00\x00\x00\x00\x00".unpack(unpack_format).should == [-infinity_value]
+ end
+
+ it "decodes NaN" do
+ # mumble mumble NaN mumble https://bugs.ruby-lang.org/issues/5884
+ [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]
+ end
+
+ it "ignores spaces between directives" do
+ "@\x07333333?\xf6ffffff".unpack(unpack_format(' ', 2)).should == [2.9, 1.4]
+ end
+end
diff --git a/spec/ruby/core/string/unpack/shared/integer.rb b/spec/ruby/core/string/unpack/shared/integer.rb
new file mode 100644
index 0000000000..03dfb5c682
--- /dev/null
+++ b/spec/ruby/core/string/unpack/shared/integer.rb
@@ -0,0 +1,339 @@
+# -*- encoding: ascii-8bit -*-
+
+describe :string_unpack_16bit_le, shared: true do
+ it "decodes one short for a single format character" do
+ "ab".unpack(unpack_format).should == [25185]
+ end
+
+ it "decodes two shorts for two format characters" do
+ "abcd".unpack(unpack_format(nil, 2)).should == [25185, 25699]
+ end
+
+ it "decodes the number of shorts requested by the count modifier" do
+ "abcdef".unpack(unpack_format(3)).should == [25185, 25699, 26213]
+ end
+
+ it "decodes the remaining shorts when passed the '*' modifier" do
+ "abcd".unpack(unpack_format('*')).should == [25185, 25699]
+ end
+
+ it "decodes the remaining shorts when passed the '*' modifier after another directive" do
+ "abcd".unpack(unpack_format()+unpack_format('*')).should == [25185, 25699]
+ end
+
+ it "does not decode a short when fewer bytes than a short remain and the '*' modifier is passed" do
+ "\xff".unpack(unpack_format('*')).should == []
+ end
+
+ it "adds nil for each element requested beyond the end of the String" do
+ [ ["", [nil, nil, nil]],
+ ["abc", [25185, nil, nil]],
+ ["abcd", [25185, 25699, nil]]
+ ].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]
+ end
+
+ it "ignores spaces between directives" do
+ "abcd".unpack(unpack_format(' ', 2)).should == [25185, 25699]
+ end
+end
+
+describe :string_unpack_16bit_le_signed, shared: true do
+ it "decodes a short with most significant bit set as a negative number" do
+ "\x00\xff".unpack(unpack_format()).should == [-256]
+ end
+end
+
+describe :string_unpack_16bit_le_unsigned, shared: true do
+ it "decodes a short with most significant bit set as a positive number" do
+ "\x00\xff".unpack(unpack_format()).should == [65280]
+ end
+end
+
+describe :string_unpack_16bit_be, shared: true do
+ it "decodes one short for a single format character" do
+ "ba".unpack(unpack_format).should == [25185]
+ end
+
+ it "decodes two shorts for two format characters" do
+ "badc".unpack(unpack_format(nil, 2)).should == [25185, 25699]
+ end
+
+ it "decodes the number of shorts requested by the count modifier" do
+ "badcfe".unpack(unpack_format(3)).should == [25185, 25699, 26213]
+ end
+
+ it "decodes the remaining shorts when passed the '*' modifier" do
+ "badc".unpack(unpack_format('*')).should == [25185, 25699]
+ end
+
+ it "decodes the remaining shorts when passed the '*' modifier after another directive" do
+ "badc".unpack(unpack_format()+unpack_format('*')).should == [25185, 25699]
+ end
+
+ it "does not decode a short when fewer bytes than a short remain and the '*' modifier is passed" do
+ "\xff".unpack(unpack_format('*')).should == []
+ end
+
+ it "adds nil for each element requested beyond the end of the String" do
+ [ ["", [nil, nil, nil]],
+ ["bac", [25185, nil, nil]],
+ ["badc", [25185, 25699, nil]]
+ ].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]
+ end
+
+ it "ignores spaces between directives" do
+ "badc".unpack(unpack_format(' ', 2)).should == [25185, 25699]
+ end
+end
+
+describe :string_unpack_16bit_be_signed, shared: true do
+ it "decodes a short with most significant bit set as a negative number" do
+ "\xff\x00".unpack(unpack_format()).should == [-256]
+ end
+end
+
+describe :string_unpack_16bit_be_unsigned, shared: true do
+ it "decodes a short with most significant bit set as a positive number" do
+ "\xff\x00".unpack(unpack_format()).should == [65280]
+ end
+end
+
+describe :string_unpack_32bit_le, shared: true do
+ it "decodes one int for a single format character" do
+ "abcd".unpack(unpack_format).should == [1684234849]
+ end
+
+ it "decodes two ints for two format characters" do
+ "abghefcd".unpack(unpack_format(nil, 2)).should == [1751605857, 1684235877]
+ end
+
+ it "decodes the number of ints requested by the count modifier" do
+ "abcedfgh".unpack(unpack_format(2)).should == [1701012065, 1751606884]
+ end
+
+ it "decodes the remaining ints when passed the '*' modifier" do
+ "acbdegfh".unpack(unpack_format('*')).should == [1684169569, 1751541605]
+ end
+
+ it "decodes the remaining ints when passed the '*' modifier after another directive" do
+ "abcdefgh".unpack(unpack_format()+unpack_format('*')).should == [1684234849, 1751606885]
+ end
+
+ it "does not decode an int when fewer bytes than an int remain and the '*' modifier is passed" do
+ "abc".unpack(unpack_format('*')).should == []
+ end
+
+ it "adds nil for each element requested beyond the end of the String" do
+ [ ["", [nil, nil, nil]],
+ ["abcde", [1684234849, nil, nil]],
+ ["abcdefg", [1684234849, nil, nil]],
+ ["abcdefgh", [1684234849, 1751606885, nil]]
+ ].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]
+ end
+
+ it "ignores spaces between directives" do
+ "abcdefgh".unpack(unpack_format(' ', 2)).should == [1684234849, 1751606885]
+ end
+end
+
+describe :string_unpack_32bit_le_signed, shared: true do
+ it "decodes an int with most significant bit set as a negative number" do
+ "\x00\xaa\x00\xff".unpack(unpack_format()).should == [-16733696]
+ end
+end
+
+describe :string_unpack_32bit_le_unsigned, shared: true do
+ it "decodes an int with most significant bit set as a positive number" do
+ "\x00\xaa\x00\xff".unpack(unpack_format()).should == [4278233600]
+ end
+end
+
+describe :string_unpack_32bit_be, shared: true do
+ it "decodes one int for a single format character" do
+ "dcba".unpack(unpack_format).should == [1684234849]
+ end
+
+ it "decodes two ints for two format characters" do
+ "hgbadcfe".unpack(unpack_format(nil, 2)).should == [1751605857, 1684235877]
+ end
+
+ it "decodes the number of ints requested by the count modifier" do
+ "ecbahgfd".unpack(unpack_format(2)).should == [1701012065, 1751606884]
+ end
+
+ it "decodes the remaining ints when passed the '*' modifier" do
+ "dbcahfge".unpack(unpack_format('*')).should == [1684169569, 1751541605]
+ end
+
+ it "decodes the remaining ints when passed the '*' modifier after another directive" do
+ "dcbahgfe".unpack(unpack_format()+unpack_format('*')).should == [1684234849, 1751606885]
+ end
+
+ it "does not decode an int when fewer bytes than an int remain and the '*' modifier is passed" do
+ "abc".unpack(unpack_format('*')).should == []
+ end
+
+ it "adds nil for each element requested beyond the end of the String" do
+ [ ["", [nil, nil, nil]],
+ ["dcbae", [1684234849, nil, nil]],
+ ["dcbaefg", [1684234849, nil, nil]],
+ ["dcbahgfe", [1684234849, 1751606885, nil]]
+ ].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]
+ end
+
+ it "ignores spaces between directives" do
+ "dcbahgfe".unpack(unpack_format(' ', 2)).should == [1684234849, 1751606885]
+ end
+end
+
+describe :string_unpack_32bit_be_signed, shared: true do
+ it "decodes an int with most significant bit set as a negative number" do
+ "\xff\x00\xaa\x00".unpack(unpack_format()).should == [-16733696]
+ end
+end
+
+describe :string_unpack_32bit_be_unsigned, shared: true do
+ it "decodes an int with most significant bit set as a positive number" do
+ "\xff\x00\xaa\x00".unpack(unpack_format()).should == [4278233600]
+ end
+end
+
+describe :string_unpack_64bit_le, shared: true do
+ it "decodes one long for a single format character" do
+ "abcdefgh".unpack(unpack_format).should == [7523094288207667809]
+ end
+
+ it "decodes two longs for two format characters" do
+ array = "abghefcdghefabcd".unpack(unpack_format(nil, 2))
+ array.should == [7233738012216484449, 7233733596956420199]
+ end
+
+ it "decodes the number of longs requested by the count modifier" do
+ array = "abcedfghefcdghef".unpack(unpack_format(2))
+ array.should == [7523094283929477729, 7378418357791581797]
+ end
+
+ it "decodes the remaining longs when passed the '*' modifier" do
+ array = "acbdegfhdegfhacb".unpack(unpack_format('*'))
+ array.should == [7522813912742519649, 7089617339433837924]
+ end
+
+ it "decodes the remaining longs when passed the '*' modifier after another directive" do
+ array = "bcahfgedhfgedbca".unpack(unpack_format()+unpack_format('*'))
+ array.should == [7234302065976107874, 7017560827710891624]
+ end
+
+ it "does not decode a long when fewer bytes than a long remain and the '*' modifier is passed" 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]
+ end
+
+ it "ignores spaces between directives" do
+ array = "abcdefghabghefcd".unpack(unpack_format(' ', 2))
+ array.should == [7523094288207667809, 7233738012216484449]
+ end
+end
+
+describe :string_unpack_64bit_le_extra, shared: true do
+ it "adds nil for each element requested beyond the end of the String" do
+ [ ["", [nil, nil, nil]],
+ ["abcdefgh", [7523094288207667809, nil, nil]],
+ ["abcdefghcdefab", [7523094288207667809, nil, nil]],
+ ["abcdefghcdefabde", [7523094288207667809, 7306072665971057763, nil]]
+ ].should be_computed_by(:unpack, unpack_format(3))
+ end
+end
+
+describe :string_unpack_64bit_le_signed, shared: true do
+ it "decodes a long with most significant bit set as a negative number" do
+ "\x00\xcc\x00\xbb\x00\xaa\x00\xff".unpack(unpack_format()).should == [-71870673923814400]
+ end
+end
+
+describe :string_unpack_64bit_le_unsigned, shared: true do
+ it "decodes a long with most significant bit set as a positive number" do
+ "\x00\xcc\x00\xbb\x00\xaa\x00\xff".unpack(unpack_format()).should == [18374873399785737216]
+ end
+end
+
+describe :string_unpack_64bit_be, shared: true do
+ it "decodes one long for a single format character" do
+ "hgfedcba".unpack(unpack_format).should == [7523094288207667809]
+ end
+
+ it "decodes two longs for two format characters" do
+ array = "dcfehgbadcbafehg".unpack(unpack_format(nil, 2))
+ array.should == [7233738012216484449, 7233733596956420199]
+ end
+
+ it "decodes the number of longs requested by the count modifier" do
+ array = "hgfdecbafehgdcfe".unpack(unpack_format(2))
+ array.should == [7523094283929477729, 7378418357791581797]
+ end
+
+ it "decodes the remaining longs when passed the '*' modifier" do
+ array = "hfgedbcabcahfged".unpack(unpack_format('*'))
+ array.should == [7522813912742519649, 7089617339433837924]
+ end
+
+ it "decodes the remaining longs when passed the '*' modifier after another directive" do
+ array = "degfhacbacbdegfh".unpack(unpack_format()+unpack_format('*'))
+ array.should == [7234302065976107874, 7017560827710891624]
+ end
+
+ it "does not decode a long when fewer bytes than a long remain and the '*' modifier is passed" 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]
+ end
+
+ it "ignores spaces between directives" do
+ array = "hgfedcbadcfehgba".unpack(unpack_format(' ', 2))
+ array.should == [7523094288207667809, 7233738012216484449]
+ end
+end
+
+describe :string_unpack_64bit_be_extra, shared: true do
+ it "adds nil for each element requested beyond the end of the String" do
+ [ ["", [nil, nil, nil]],
+ ["hgfedcba", [7523094288207667809, nil, nil]],
+ ["hgfedcbacdefab", [7523094288207667809, nil, nil]],
+ ["hgfedcbaedbafedc", [7523094288207667809, 7306072665971057763, nil]]
+ ].should be_computed_by(:unpack, unpack_format(3))
+ end
+end
+
+describe :string_unpack_64bit_be_signed, shared: true do
+ it "decodes a long with most significant bit set as a negative number" do
+ "\xff\x00\xaa\x00\xbb\x00\xcc\x00".unpack(unpack_format()).should == [-71870673923814400]
+ end
+end
+
+describe :string_unpack_64bit_be_unsigned, shared: true do
+ it "decodes a long with most significant bit set as a positive number" do
+ "\xff\x00\xaa\x00\xbb\x00\xcc\x00".unpack(unpack_format()).should == [18374873399785737216]
+ end
+end
diff --git a/spec/ruby/core/string/unpack/shared/string.rb b/spec/ruby/core/string/unpack/shared/string.rb
new file mode 100644
index 0000000000..9d85eedf26
--- /dev/null
+++ b/spec/ruby/core/string/unpack/shared/string.rb
@@ -0,0 +1,51 @@
+describe :string_unpack_string, shared: true do
+ it "returns an empty string if the input is empty" do
+ "".unpack(unpack_format).should == [""]
+ end
+
+ it "returns empty strings for repeated formats if the input is empty" do
+ "".unpack(unpack_format(nil, 3)).should == ["", "", ""]
+ end
+
+ it "returns an empty string and does not decode any bytes when the count modifier is zero" do
+ "abc".unpack(unpack_format(0)+unpack_format).should == ["", "a"]
+ end
+
+ it "implicitly has a count of one when no count is specified" do
+ "abc".unpack(unpack_format).should == ["a"]
+ end
+
+ it "decodes the number of bytes specified by the count modifier" do
+ "abc".unpack(unpack_format(3)).should == ["abc"]
+ end
+
+ it "decodes the number of bytes specified by the count modifier including whitespace bytes" do
+ [ ["a bc", ["a b", "c"]],
+ ["a\fbc", ["a\fb", "c"]],
+ ["a\nbc", ["a\nb", "c"]],
+ ["a\rbc", ["a\rb", "c"]],
+ ["a\tbc", ["a\tb", "c"]],
+ ["a\vbc", ["a\vb", "c"]]
+ ].should be_computed_by(:unpack, unpack_format(3)+unpack_format)
+ end
+
+ it "decodes past whitespace bytes when passed the '*' modifier" do
+ [ ["a b c", ["a b c"]],
+ ["a\fb c", ["a\fb c"]],
+ ["a\nb c", ["a\nb c"]],
+ ["a\rb c", ["a\rb c"]],
+ ["a\tb c", ["a\tb c"]],
+ ["a\vb c", ["a\vb c"]],
+ ].should be_computed_by(:unpack, unpack_format("*"))
+ end
+end
+
+describe :string_unpack_Aa, shared: true do
+ it "decodes the number of bytes specified by the count modifier including NULL bytes" do
+ "a\x00bc".unpack(unpack_format(3)+unpack_format).should == ["a\x00b", "c"]
+ end
+
+ it "decodes past NULL bytes when passed the '*' modifier" do
+ "a\x00b c".unpack(unpack_format("*")).should == ["a\x00b c"]
+ end
+end
diff --git a/spec/ruby/core/string/unpack/shared/taint.rb b/spec/ruby/core/string/unpack/shared/taint.rb
new file mode 100644
index 0000000000..391338192a
--- /dev/null
+++ b/spec/ruby/core/string/unpack/shared/taint.rb
@@ -0,0 +1,81 @@
+describe :string_unpack_taint, shared: true do
+ it "does not taint returned arrays if given an untainted format string" do
+ "".unpack(unpack_format(2)).tainted?.should be_false
+ end
+
+ it "does not taint returned arrays if given a tainted format string" do
+ format_string = unpack_format(2).dup
+ format_string.taint
+ "".unpack(format_string).tainted?.should be_false
+ end
+
+ it "does not taint returned strings if given an untainted format string" do
+ "".unpack(unpack_format(2)).any?(&:tainted?).should be_false
+ end
+
+ it "does not taint returned strings if given a tainted format string" do
+ format_string = unpack_format(2).dup
+ format_string.taint
+ "".unpack(format_string).any?(&:tainted?).should be_false
+ end
+
+ it "does not taint returned arrays if given an untainted packed string" do
+ "".unpack(unpack_format(2)).tainted?.should be_false
+ end
+
+ it "does not taint returned arrays if given a tainted packed string" do
+ packed_string = ""
+ packed_string.taint
+ packed_string.unpack(unpack_format(2)).tainted?.should be_false
+ end
+
+ it "does not taint returned strings if given an untainted packed string" do
+ "".unpack(unpack_format(2)).any?(&:tainted?).should be_false
+ end
+
+ it "taints returned strings if given a tainted packed string" do
+ packed_string = ""
+ packed_string.taint
+ packed_string.unpack(unpack_format(2)).all?(&:tainted?).should be_true
+ end
+
+ it "does not untrust returned arrays if given an untrusted format string" do
+ "".unpack(unpack_format(2)).untrusted?.should be_false
+ end
+
+ it "does not untrust returned arrays if given a untrusted format string" do
+ format_string = unpack_format(2).dup
+ format_string.untrust
+ "".unpack(format_string).untrusted?.should be_false
+ end
+
+ it "does not untrust returned strings if given an untainted format string" do
+ "".unpack(unpack_format(2)).any?(&:untrusted?).should be_false
+ end
+
+ it "does not untrust returned strings if given a untrusted format string" do
+ format_string = unpack_format(2).dup
+ format_string.untrust
+ "".unpack(format_string).any?(&:untrusted?).should be_false
+ end
+
+ it "does not untrust returned arrays if given an trusted packed string" do
+ "".unpack(unpack_format(2)).untrusted?.should be_false
+ end
+
+ it "does not untrust returned arrays if given a untrusted packed string" do
+ packed_string = ""
+ packed_string.untrust
+ packed_string.unpack(unpack_format(2)).untrusted?.should be_false
+ end
+
+ it "does not untrust returned strings if given an trusted packed string" do
+ "".unpack(unpack_format(2)).any?(&:untrusted?).should be_false
+ end
+
+ it "untrusts returned strings if given a untrusted packed string" do
+ packed_string = ""
+ packed_string.untrust
+ packed_string.unpack(unpack_format(2)).all?(&:untrusted?).should be_true
+ end
+end
diff --git a/spec/ruby/core/string/unpack/shared/unicode.rb b/spec/ruby/core/string/unpack/shared/unicode.rb
new file mode 100644
index 0000000000..a2b4e142b2
--- /dev/null
+++ b/spec/ruby/core/string/unpack/shared/unicode.rb
@@ -0,0 +1,60 @@
+# -*- encoding: utf-8 -*-
+
+describe :string_unpack_unicode, shared: true do
+ it "decodes Unicode codepoints as ASCII values" do
+ [ ["\x00", [0]],
+ ["\x01", [1]],
+ ["\x08", [8]],
+ ["\x0f", [15]],
+ ["\x18", [24]],
+ ["\x1f", [31]],
+ ["\x7f", [127]],
+ ["\xc2\x80", [128]],
+ ["\xc2\x81", [129]],
+ ["\xc3\xbf", [255]]
+ ].should be_computed_by(:unpack, "U")
+ end
+
+ it "decodes the number of characters specified by the count modifier" do
+ [ ["\xc2\x80\xc2\x81\xc2\x82\xc2\x83", "U1", [0x80]],
+ ["\xc2\x80\xc2\x81\xc2\x82\xc2\x83", "U2", [0x80, 0x81]],
+ ["\xc2\x80\xc2\x81\xc2\x82\xc2\x83", "U3", [0x80, 0x81, 0x82]]
+ ].should be_computed_by(:unpack)
+ end
+
+ it "implicitly has a count of one when no count modifier is passed" do
+ "\xc2\x80\xc2\x81\xc2\x82\xc2\x83".unpack("U1").should == [0x80]
+ end
+
+ it "decodes all remaining characters when passed the '*' modifier" do
+ "\xc2\x80\xc2\x81\xc2\x82\xc2\x83".unpack("U*").should == [0x80, 0x81, 0x82, 0x83]
+ end
+
+ it "decodes UTF-8 BMP codepoints" do
+ [ ["\xc2\x80", [0x80]],
+ ["\xdf\xbf", [0x7ff]],
+ ["\xe0\xa0\x80", [0x800]],
+ ["\xef\xbf\xbf", [0xffff]]
+ ].should be_computed_by(:unpack, "U")
+ end
+
+ it "decodes UTF-8 max codepoints" do
+ [ ["\xf0\x90\x80\x80", [0x10000]],
+ ["\xf3\xbf\xbf\xbf", [0xfffff]],
+ ["\xf4\x80\x80\x80", [0x100000]],
+ ["\xf4\x8f\xbf\xbf", [0x10ffff]]
+ ].should be_computed_by(:unpack, "U")
+ end
+
+ it "does not decode any items for directives exceeding the input string size" do
+ "\xc2\x80".unpack("UUUU").should == [0x80]
+ end
+
+ it "ignores NULL bytes between directives" do
+ "\x01\x02".unpack("U\x00U").should == [1, 2]
+ end
+
+ it "ignores spaces between directives" do
+ "\x01\x02".unpack("U U").should == [1, 2]
+ end
+end
diff --git a/spec/ruby/core/string/unpack/u_spec.rb b/spec/ruby/core/string/unpack/u_spec.rb
new file mode 100644
index 0000000000..ee1d6c68e7
--- /dev/null
+++ b/spec/ruby/core/string/unpack/u_spec.rb
@@ -0,0 +1,97 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/unicode'
+require_relative 'shared/taint'
+
+describe "String#unpack with format 'U'" do
+ it_behaves_like :string_unpack_basic, 'U'
+ it_behaves_like :string_unpack_no_platform, 'U'
+ it_behaves_like :string_unpack_unicode, 'U'
+ it_behaves_like :string_unpack_taint, 'U'
+
+ it "raises ArgumentError on a malformed byte sequence" do
+ lambda { "\xE3".unpack('U') }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError on a malformed byte sequence and doesn't continue when used with the * modifier" do
+ lambda { "\xE3".unpack('U*') }.should raise_error(ArgumentError)
+ end
+end
+
+describe "String#unpack with format 'u'" do
+ it_behaves_like :string_unpack_basic, 'u'
+ it_behaves_like :string_unpack_no_platform, 'u'
+ it_behaves_like :string_unpack_taint, 'u'
+
+ it "decodes an empty string as an empty string" do
+ "".unpack("u").should == [""]
+ end
+
+ it "decodes into raw (ascii) string values" do
+ str = "".unpack("u")[0]
+ str.encoding.name.should == 'ASCII-8BIT'
+
+ str = "1".force_encoding('UTF-8').unpack("u")[0]
+ str.encoding.name.should == 'ASCII-8BIT'
+ end
+
+ it "decodes the complete string ignoring newlines when given a single directive" do
+ "#86)C\n#1$5&\n".unpack("u").should == ["abcDEF"]
+ end
+
+ it "appends empty string to the array for directives exceeding the input size" do
+ "#86)C\n#1$5&\n".unpack("uuu").should == ["abcDEF", "", ""]
+ end
+
+ it "ignores the count or '*' modifier and decodes the entire string" do
+ [ ["#86)C\n#1$5&\n", "u238", ["abcDEF"]],
+ ["#86)C\n#1$5&\n", "u*", ["abcDEF"]]
+ ].should be_computed_by(:unpack)
+ end
+
+ it "decodes all ascii characters" do
+ [ ["'``$\"`P0%!@``\n", ["\x00\x01\x02\x03\x04\x05\x06"]],
+ ["'!P@)\"@L,#0``\n", ["\a\b\t\n\v\f\r"]],
+ [")\#@\\0$1(3%!46\n", ["\x0E\x0F\x10\x11\x12\x13\x14\x15\x16"]],
+ [")%Q@9&AL<'1X?\n", ["\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f"]],
+ ["/(2(C)\"4F)R@I*BLL+2XO\n", ["!\"\#$%&'()*+,-./"]],
+ ["*,\#$R,S0U-C<X.0``\n", ["0123456789"]],
+ ["'.CL\\/3X_0```\n", [":;<=>?@"]],
+ [":04)#1$5&1TA)2DM,34Y/4%%24U155E=865H`\n", ["ABCDEFGHIJKLMNOPQRSTUVWXYZ"]],
+ ["&6UQ=7E]@\n", ["[\\]^_`"]],
+ [":86)C9&5F9VAI:FML;6YO<'%R<W1U=G=X>7H`\n", ["abcdefghijklmnopqrstuvwxyz"]],
+ ["$>WQ]?@``\n", ["{|}~"]],
+ [")?\\*`PH'\"@L*#\n", ["\x7f\xc2\x80\xc2\x81\xc2\x82\xc2\x83"]],
+ [")PH3\"A<*&PH?\"\n", ["\xc2\x84\xc2\x85\xc2\x86\xc2\x87\xc2"]],
+ [")B,*)PHK\"B\\*,\n", ["\x88\xc2\x89\xc2\x8a\xc2\x8b\xc2\x8c"]],
+ [")PHW\"CL*/PI#\"\n", ["\xc2\x8d\xc2\x8e\xc2\x8f\xc2\x90\xc2"]],
+ [")D<*2PI/\"E,*5\n", ["\x91\xc2\x92\xc2\x93\xc2\x94\xc2\x95"]],
+ [")PI;\"E\\*8PIG\"\n", ["\xc2\x96\xc2\x97\xc2\x98\xc2\x99\xc2"]],
+ [")FL*;PIS\"G<*>\n", ["\x9a\xc2\x9b\xc2\x9c\xc2\x9d\xc2\x9e"]],
+ [")PI_\"H,*APJ+\"\n", ["\xc2\x9f\xc2\xa0\xc2\xa1\xc2\xa2\xc2"]],
+ [")H\\*DPJ7\"IL*G\n", ["\xa3\xc2\xa4\xc2\xa5\xc2\xa6\xc2\xa7"]],
+ [")PJC\"J<*JPJO\"\n", ["\xc2\xa8\xc2\xa9\xc2\xaa\xc2\xab\xc2"]],
+ [")K,*MPJ[\"K\\*P\n", ["\xac\xc2\xad\xc2\xae\xc2\xaf\xc2\xb0"]],
+ [")PK'\"LL*SPK3\"\n", ["\xc2\xb1\xc2\xb2\xc2\xb3\xc2\xb4\xc2"]],
+ [")M<*VPK?\"N,*Y\n", ["\xb5\xc2\xb6\xc2\xb7\xc2\xb8\xc2\xb9"]],
+ [")PKK\"N\\*\\PKW\"\n", ["\xc2\xba\xc2\xbb\xc2\xbc\xc2\xbd\xc2"]],
+ [")OL*_PX#\#@<.\"\n", ["\xbe\xc2\xbf\xc3\x80\xc3\x81\xc3\x82"]],
+ [")PX/#A,.%PX;#\n", ["\xc3\x83\xc3\x84\xc3\x85\xc3\x86\xc3"]],
+ [")A\\.(PXG#BL.+\n", ["\x87\xc3\x88\xc3\x89\xc3\x8a\xc3\x8b"]],
+ [")PXS#C<..PX_#\n", ["\xc3\x8c\xc3\x8d\xc3\x8e\xc3\x8f\xc3"]],
+ [")D,.1PY+#D\\.4\n", ["\x90\xc3\x91\xc3\x92\xc3\x93\xc3\x94"]],
+ [")PY7#EL.7PYC#\n", ["\xc3\x95\xc3\x96\xc3\x97\xc3\x98\xc3"]],
+ [")F<.:PYO#G,.=\n", ["\x99\xc3\x9a\xc3\x9b\xc3\x9c\xc3\x9d"]],
+ [")PY[#G\\.@PZ'#\n", ["\xc3\x9e\xc3\x9f\xc3\xa0\xc3\xa1\xc3"]],
+ [")HL.CPZ3#I<.F\n", ["\xa2\xc3\xa3\xc3\xa4\xc3\xa5\xc3\xa6"]],
+ [")PZ?#J,.IPZK#\n", ["\xc3\xa7\xc3\xa8\xc3\xa9\xc3\xaa\xc3"]],
+ [")J\\.LPZW#KL.O\n", ["\xab\xc3\xac\xc3\xad\xc3\xae\xc3\xaf"]],
+ [")P[##L<.RP[/#\n", ["\xc3\xb0\xc3\xb1\xc3\xb2\xc3\xb3\xc3"]],
+ [")M,.UP[;#M\\.X\n", ["\xb4\xc3\xb5\xc3\xb6\xc3\xb7\xc3\xb8"]],
+ [")P[G#NL.[P[S#\n", ["\xc3\xb9\xc3\xba\xc3\xbb\xc3\xbc\xc3"]],
+ ["%O<.^P[\\`\n", ["\xbd\xc3\xbe\xc3\xbf"]]
+ ].should be_computed_by(:unpack, "u")
+ end
+end
diff --git a/spec/ruby/core/string/unpack/v_spec.rb b/spec/ruby/core/string/unpack/v_spec.rb
new file mode 100644
index 0000000000..929e8712cb
--- /dev/null
+++ b/spec/ruby/core/string/unpack/v_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/integer'
+
+describe "String#unpack with format 'V'" do
+ it_behaves_like :string_unpack_basic, 'V'
+ it_behaves_like :string_unpack_32bit_le, 'V'
+ it_behaves_like :string_unpack_32bit_le_unsigned, 'V'
+ it_behaves_like :string_unpack_no_platform, 'V'
+end
+
+describe "String#unpack with format 'v'" do
+ it_behaves_like :string_unpack_basic, 'v'
+ it_behaves_like :string_unpack_16bit_le, 'v'
+ it_behaves_like :string_unpack_16bit_le_unsigned, 'v'
+ it_behaves_like :string_unpack_no_platform, 'v'
+end
diff --git a/spec/ruby/core/string/unpack/w_spec.rb b/spec/ruby/core/string/unpack/w_spec.rb
new file mode 100644
index 0000000000..8ff926783e
--- /dev/null
+++ b/spec/ruby/core/string/unpack/w_spec.rb
@@ -0,0 +1,25 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+
+describe "String#unpack with directive 'w'" do
+ it_behaves_like :string_unpack_basic, 'w'
+ it_behaves_like :string_unpack_no_platform, 'w'
+
+ it "decodes a BER-compressed integer" do
+ [ ["\x00", [0]],
+ ["\x01", [1]],
+ ["\xce\x0f", [9999]],
+ ["\x84\x80\x80\x80\x80\x80\x80\x80\x80\x00", [2**65]]
+ ].should be_computed_by(:unpack, "w")
+ end
+
+ it "ignores NULL bytes between directives" do
+ "\x01\x02\x03".unpack("w\x00w").should == [1, 2]
+ end
+
+ it "ignores spaces between directives" do
+ "\x01\x02\x03".unpack("w w").should == [1, 2]
+ end
+end
diff --git a/spec/ruby/core/string/unpack/x_spec.rb b/spec/ruby/core/string/unpack/x_spec.rb
new file mode 100644
index 0000000000..22b641b732
--- /dev/null
+++ b/spec/ruby/core/string/unpack/x_spec.rb
@@ -0,0 +1,62 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+
+describe "String#unpack with format 'X'" do
+ it_behaves_like :string_unpack_basic, 'X'
+ it_behaves_like :string_unpack_no_platform, 'X'
+
+ it "moves the read index back by the number of bytes specified by count" do
+ "\x01\x02\x03\x04".unpack("C3X2C").should == [1, 2, 3, 2]
+ end
+
+ it "does not change the read index when passed a count of zero" do
+ "\x01\x02\x03\x04".unpack("C3X0C").should == [1, 2, 3, 4]
+ end
+
+ it "implicitly has a count of one when count is not specified" do
+ "\x01\x02\x03\x04".unpack("C3XC").should == [1, 2, 3, 3]
+ end
+
+ it "moves the read index back by the remaining bytes when passed the '*' modifier" do
+ "abcd".unpack("C3X*C").should == [97, 98, 99, 99]
+ end
+
+ it "raises an ArgumentError when passed the '*' modifier if the remaining bytes exceed the bytes from the index to the start of the String" do
+ lambda { "abcd".unpack("CX*C") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the count exceeds the bytes from current index to the start of the String" do
+ lambda { "\x01\x02\x03\x04".unpack("C3X4C") }.should raise_error(ArgumentError)
+ end
+end
+
+describe "String#unpack with format 'x'" do
+ it_behaves_like :string_unpack_basic, 'x'
+ it_behaves_like :string_unpack_no_platform, 'x'
+
+ it "moves the read index forward by the number of bytes specified by count" do
+ "\x01\x02\x03\x04".unpack("Cx2C").should == [1, 4]
+ end
+
+ it "implicitly has a count of one when count is not specified" do
+ "\x01\x02\x03\x04".unpack("CxC").should == [1, 3]
+ end
+
+ it "does not change the read index when passed a count of zero" do
+ "\x01\x02\x03\x04".unpack("Cx0C").should == [1, 2]
+ end
+
+ it "moves the read index to the end of the string when passed the '*' modifier" do
+ "\x01\x02\x03\x04".unpack("Cx*C").should == [1, nil]
+ end
+
+ it "positions the read index one beyond the last readable byte in the String" do
+ "\x01\x02\x03\x04".unpack("C2x2C").should == [1, 2, nil]
+ end
+
+ it "raises an ArgumentError if the count exceeds the size of the String" do
+ lambda { "\x01\x02\x03\x04".unpack("C2x3C") }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/string/unpack/z_spec.rb b/spec/ruby/core/string/unpack/z_spec.rb
new file mode 100644
index 0000000000..2de624a74d
--- /dev/null
+++ b/spec/ruby/core/string/unpack/z_spec.rb
@@ -0,0 +1,23 @@
+# -*- encoding: ascii-8bit -*-
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/string'
+require_relative 'shared/taint'
+
+describe "String#unpack with format 'Z'" do
+ it_behaves_like :string_unpack_basic, 'Z'
+ it_behaves_like :string_unpack_no_platform, 'Z'
+ it_behaves_like :string_unpack_string, 'Z'
+ it_behaves_like :string_unpack_taint, 'Z'
+
+ it "stops decoding at NULL bytes when passed the '*' modifier" do
+ "a\x00\x00 b \x00c".unpack('Z*Z*Z*Z*').should == ["a", "", " b ", "c"]
+ end
+
+ it "decodes the number of bytes specified by the count modifier and truncates the decoded string at the first NULL byte" do
+ [ ["a\x00 \x00b c", ["a", " "]],
+ ["\x00a\x00 bc \x00", ["", "c"]]
+ ].should be_computed_by(:unpack, "Z5Z")
+ end
+end
diff --git a/spec/ruby/core/string/unpack1_spec.rb b/spec/ruby/core/string/unpack1_spec.rb
new file mode 100644
index 0000000000..5b2157d85a
--- /dev/null
+++ b/spec/ruby/core/string/unpack1_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.4" do
+ describe "String#unpack1" do
+ it "returns the first value of #unpack" do
+ "ABCD".unpack1('x3C').should == "ABCD".unpack('x3C')[0]
+ "\u{3042 3044 3046}".unpack1("U*").should == 0x3042
+ "aG9nZWZ1Z2E=".unpack1("m").should == "hogefuga"
+ "A".unpack1("B*").should == "01000001"
+ end
+ end
+end
diff --git a/spec/ruby/core/string/upcase_spec.rb b/spec/ruby/core/string/upcase_spec.rb
new file mode 100644
index 0000000000..347f567be9
--- /dev/null
+++ b/spec/ruby/core/string/upcase_spec.rb
@@ -0,0 +1,184 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#upcase" do
+ it "returns a copy of self with all lowercase letters upcased" do
+ "Hello".upcase.should == "HELLO"
+ "hello".upcase.should == "HELLO"
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "is locale insensitive (only replaces a-z)" do
+ "äöü".upcase.should == "äöü"
+
+ str = Array.new(256) { |c| c.chr }.join
+ expected = Array.new(256) do |i|
+ c = i.chr
+ c.between?("a", "z") ? c.upcase : c
+ end.join
+
+ str.upcase.should == expected
+ end
+ end
+
+ ruby_version_is '2.4' do
+ describe "full Unicode case mapping" do
+ it "works for all of Unicode with no option" do
+ "äöü".upcase.should == "ÄÖÜ"
+ end
+
+ it "updates string metadata" do
+ upcased = "aßet".upcase
+
+ upcased.should == "ASSET"
+ upcased.size.should == 5
+ upcased.bytesize.should == 5
+ upcased.ascii_only?.should be_true
+ end
+ end
+
+ describe "ASCII-only case mapping" do
+ it "does not upcase non-ASCII characters" do
+ "aßet".upcase(:ascii).should == "AßET"
+ end
+ end
+
+ describe "full Unicode case mapping adapted for Turkic languages" do
+ it "upcases ASCII characters according to Turkic semantics" do
+ "i".upcase(:turkic).should == "İ"
+ end
+
+ it "allows Lithuanian as an extra option" do
+ "i".upcase(:turkic, :lithuanian).should == "İ"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { "i".upcase(:turkic, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "full Unicode case mapping adapted for Lithuanian" do
+ it "currently works the same as full Unicode case mapping" do
+ "iß".upcase(:lithuanian).should == "ISS"
+ end
+
+ it "allows Turkic as an extra option (and applies Turkic semantics)" do
+ "iß".upcase(:lithuanian, :turkic).should == "İSS"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { "iß".upcase(:lithuanian, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "does not allow the :fold option for upcasing" do
+ lambda { "abc".upcase(:fold) }.should raise_error(ArgumentError)
+ end
+
+ it "does not allow invalid options" do
+ lambda { "abc".upcase(:invalid_option) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "taints result when self is tainted" do
+ "".taint.upcase.tainted?.should == true
+ "X".taint.upcase.tainted?.should == true
+ "x".taint.upcase.tainted?.should == true
+ end
+
+ it "returns a subclass instance for subclasses" do
+ StringSpecs::MyString.new("fooBAR").upcase.should be_an_instance_of(StringSpecs::MyString)
+ end
+end
+
+describe "String#upcase!" do
+ it "modifies self in place" do
+ a = "HeLlO"
+ a.upcase!.should equal(a)
+ a.should == "HELLO"
+ end
+
+ ruby_version_is '2.4' do
+ describe "full Unicode case mapping" do
+ it "modifies self in place for all of Unicode with no option" do
+ a = "äöü"
+ a.upcase!
+ a.should == "ÄÖÜ"
+ end
+
+ it "updates string metadata for self" do
+ upcased = "aßet"
+ upcased.upcase!
+
+ upcased.should == "ASSET"
+ upcased.size.should == 5
+ upcased.bytesize.should == 5
+ upcased.ascii_only?.should be_true
+ end
+ end
+
+ describe "modifies self in place for ASCII-only case mapping" do
+ it "does not upcase non-ASCII characters" do
+ a = "aßet"
+ a.upcase!(:ascii)
+ a.should == "AßET"
+ end
+ end
+
+ describe "modifies self in place for full Unicode case mapping adapted for Turkic languages" do
+ it "upcases ASCII characters according to Turkic semantics" do
+ a = "i"
+ a.upcase!(:turkic)
+ a.should == "İ"
+ end
+
+ it "allows Lithuanian as an extra option" do
+ a = "i"
+ a.upcase!(:turkic, :lithuanian)
+ a.should == "İ"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { a = "i"; a.upcase!(:turkic, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "modifies self in place for full Unicode case mapping adapted for Lithuanian" do
+ it "currently works the same as full Unicode case mapping" do
+ a = "iß"
+ a.upcase!(:lithuanian)
+ a.should == "ISS"
+ end
+
+ it "allows Turkic as an extra option (and applies Turkic semantics)" do
+ a = "iß"
+ a.upcase!(:lithuanian, :turkic)
+ a.should == "İSS"
+ end
+
+ it "does not allow any other additional option" do
+ lambda { a = "iß"; a.upcase!(:lithuanian, :ascii) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "does not allow the :fold option for upcasing" do
+ lambda { a = "abc"; a.upcase!(:fold) }.should raise_error(ArgumentError)
+ end
+
+ it "does not allow invalid options" do
+ lambda { a = "abc"; a.upcase!(:invalid_option) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "returns nil if no modifications were made" do
+ a = "HELLO"
+ a.upcase!.should == nil
+ a.should == "HELLO"
+ end
+
+ it "raises a #{frozen_error_class} when self is frozen" do
+ lambda { "HeLlo".freeze.upcase! }.should raise_error(frozen_error_class)
+ lambda { "HELLO".freeze.upcase! }.should raise_error(frozen_error_class)
+ end
+end
diff --git a/spec/ruby/core/string/uplus_spec.rb b/spec/ruby/core/string/uplus_spec.rb
new file mode 100644
index 0000000000..a7402b4549
--- /dev/null
+++ b/spec/ruby/core/string/uplus_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+
+describe 'String#+@' do
+ it 'returns an unfrozen copy of a frozen String' do
+ input = 'foo'.freeze
+ output = +input
+
+ output.frozen?.should == false
+ output.should == 'foo'
+ end
+
+ it 'returns self if the String is not frozen' do
+ input = 'foo'
+ output = +input
+
+ output.equal?(input).should == true
+ end
+
+ it 'returns mutable copy despite freeze-magic-comment in file' do
+ ruby_exe(fixture(__FILE__, "freeze_magic_comment.rb")).should == 'mutable'
+ end
+end
diff --git a/spec/ruby/core/string/upto_spec.rb b/spec/ruby/core/string/upto_spec.rb
new file mode 100644
index 0000000000..d2f1121360
--- /dev/null
+++ b/spec/ruby/core/string/upto_spec.rb
@@ -0,0 +1,98 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "String#upto" do
+ it "passes successive values, starting at self and ending at other_string, to the block" do
+ a = []
+ "*+".upto("*3") { |s| a << s }
+ a.should == ["*+", "*,", "*-", "*.", "*/", "*0", "*1", "*2", "*3"]
+ end
+
+ it "calls the block once even when start eqals stop" do
+ a = []
+ "abc".upto("abc") { |s| a << s }
+ a.should == ["abc"]
+ end
+
+ it "doesn't call block with self even if self is less than stop but stop length is less than self length" do
+ a = []
+ "25".upto("5") { |s| a << s }
+ a.should == []
+ end
+
+ it "doesn't call block if stop is less than self and stop length is less than self length" do
+ a = []
+ "25".upto("1") { |s| a << s }
+ a.should == []
+ end
+
+ it "doesn't call the block if self is greater than stop" do
+ a = []
+ "5".upto("2") { |s| a << s }
+ a.should == []
+ end
+
+ it "stops iterating as soon as the current value's character count gets higher than stop's" do
+ a = []
+ "96".upto("AA") { |s| a << s }
+ a.should == ["96", "97", "98", "99"]
+ end
+
+ it "returns self" do
+ "abc".upto("abd") { }.should == "abc"
+ "5".upto("2") { |i| i }.should == "5"
+ end
+
+ it "tries to convert other to string using to_str" do
+ other = mock('abd')
+ def other.to_str() "abd" end
+
+ a = []
+ "abc".upto(other) { |s| a << s }
+ a.should == ["abc", "abd"]
+ end
+
+ it "raises a TypeError if other can't be converted to a string" do
+ lambda { "abc".upto(123) { } }.should raise_error(TypeError)
+ lambda { "abc".upto(mock('x')){ } }.should raise_error(TypeError)
+ end
+
+
+ it "does not work with symbols" do
+ lambda { "a".upto(:c).to_a }.should raise_error(TypeError)
+ end
+
+ it "returns non-alphabetic characters in the ASCII range for single letters" do
+ "9".upto("A").to_a.should == ["9", ":", ";", "<", "=", ">", "?", "@", "A"]
+ "Z".upto("a").to_a.should == ["Z", "[", "\\", "]", "^", "_", "`", "a"]
+ "z".upto("~").to_a.should == ["z", "{", "|", "}", "~"]
+ end
+
+ it "stops before the last value if exclusive" do
+ a = []
+ "a".upto("d", true) { |s| a << s}
+ a.should == ["a", "b", "c"]
+ end
+
+ describe "on sequence of numbers" do
+ it "calls the block as Integer#upto" do
+ "8".upto("11").to_a.should == 8.upto(11).map(&:to_s)
+ end
+ end
+
+ describe "when no block is given" do
+ it "returns an enumerator" do
+ enum = "aaa".upto("baa", true)
+ enum.should be_an_instance_of(Enumerator)
+ enum.count.should == 26**2
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ "a".upto("b").size.should == nil
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/string/valid_encoding_spec.rb b/spec/ruby/core/string/valid_encoding_spec.rb
new file mode 100644
index 0000000000..c8cbbcf5ec
--- /dev/null
+++ b/spec/ruby/core/string/valid_encoding_spec.rb
@@ -0,0 +1,129 @@
+require_relative '../../spec_helper'
+
+with_feature :encoding do
+ describe "String#valid_encoding?" do
+ it "returns true if the String's encoding is valid" do
+ "a".valid_encoding?.should be_true
+ "\u{8365}\u{221}".valid_encoding?.should be_true
+ end
+
+ it "returns true if self is valid in the current encoding and other encodings" do
+ str = "\x77"
+ str.force_encoding('utf-8').valid_encoding?.should be_true
+ str.force_encoding('ascii-8bit').valid_encoding?.should be_true
+ end
+
+ it "returns true for all encodings self is valid in" do
+ str = "\u{6754}"
+ str.force_encoding('ASCII-8BIT').valid_encoding?.should be_true
+ str.force_encoding('UTF-8').valid_encoding?.should be_true
+ str.force_encoding('US-ASCII').valid_encoding?.should be_false
+ str.force_encoding('Big5').valid_encoding?.should be_false
+ str.force_encoding('CP949').valid_encoding?.should be_false
+ str.force_encoding('Emacs-Mule').valid_encoding?.should be_false
+ str.force_encoding('EUC-JP').valid_encoding?.should be_false
+ str.force_encoding('EUC-KR').valid_encoding?.should be_false
+ str.force_encoding('EUC-TW').valid_encoding?.should be_false
+ str.force_encoding('GB18030').valid_encoding?.should be_false
+ str.force_encoding('GBK').valid_encoding?.should be_false
+ str.force_encoding('ISO-8859-1').valid_encoding?.should be_true
+ str.force_encoding('ISO-8859-2').valid_encoding?.should be_true
+ str.force_encoding('ISO-8859-3').valid_encoding?.should be_true
+ str.force_encoding('ISO-8859-4').valid_encoding?.should be_true
+ str.force_encoding('ISO-8859-5').valid_encoding?.should be_true
+ str.force_encoding('ISO-8859-6').valid_encoding?.should be_true
+ str.force_encoding('ISO-8859-7').valid_encoding?.should be_true
+ str.force_encoding('ISO-8859-8').valid_encoding?.should be_true
+ str.force_encoding('ISO-8859-9').valid_encoding?.should be_true
+ str.force_encoding('ISO-8859-10').valid_encoding?.should be_true
+ str.force_encoding('ISO-8859-11').valid_encoding?.should be_true
+ str.force_encoding('ISO-8859-13').valid_encoding?.should be_true
+ str.force_encoding('ISO-8859-14').valid_encoding?.should be_true
+ str.force_encoding('ISO-8859-15').valid_encoding?.should be_true
+ str.force_encoding('ISO-8859-16').valid_encoding?.should be_true
+ str.force_encoding('KOI8-R').valid_encoding?.should be_true
+ str.force_encoding('KOI8-U').valid_encoding?.should be_true
+ str.force_encoding('Shift_JIS').valid_encoding?.should be_false
+ str.force_encoding('UTF-16BE').valid_encoding?.should be_false
+ str.force_encoding('UTF-16LE').valid_encoding?.should be_false
+ str.force_encoding('UTF-32BE').valid_encoding?.should be_false
+ str.force_encoding('UTF-32LE').valid_encoding?.should be_false
+ str.force_encoding('Windows-1251').valid_encoding?.should be_true
+ str.force_encoding('IBM437').valid_encoding?.should be_true
+ str.force_encoding('IBM737').valid_encoding?.should be_true
+ str.force_encoding('IBM775').valid_encoding?.should be_true
+ str.force_encoding('CP850').valid_encoding?.should be_true
+ str.force_encoding('IBM852').valid_encoding?.should be_true
+ str.force_encoding('CP852').valid_encoding?.should be_true
+ str.force_encoding('IBM855').valid_encoding?.should be_true
+ str.force_encoding('CP855').valid_encoding?.should be_true
+ str.force_encoding('IBM857').valid_encoding?.should be_true
+ str.force_encoding('IBM860').valid_encoding?.should be_true
+ str.force_encoding('IBM861').valid_encoding?.should be_true
+ str.force_encoding('IBM862').valid_encoding?.should be_true
+ str.force_encoding('IBM863').valid_encoding?.should be_true
+ str.force_encoding('IBM864').valid_encoding?.should be_true
+ str.force_encoding('IBM865').valid_encoding?.should be_true
+ str.force_encoding('IBM866').valid_encoding?.should be_true
+ str.force_encoding('IBM869').valid_encoding?.should be_true
+ str.force_encoding('Windows-1258').valid_encoding?.should be_true
+ str.force_encoding('GB1988').valid_encoding?.should be_true
+ str.force_encoding('macCentEuro').valid_encoding?.should be_true
+ str.force_encoding('macCroatian').valid_encoding?.should be_true
+ str.force_encoding('macCyrillic').valid_encoding?.should be_true
+ str.force_encoding('macGreek').valid_encoding?.should be_true
+ str.force_encoding('macIceland').valid_encoding?.should be_true
+ str.force_encoding('macRoman').valid_encoding?.should be_true
+ str.force_encoding('macRomania').valid_encoding?.should be_true
+ str.force_encoding('macThai').valid_encoding?.should be_true
+ str.force_encoding('macTurkish').valid_encoding?.should be_true
+ str.force_encoding('macUkraine').valid_encoding?.should be_true
+ str.force_encoding('stateless-ISO-2022-JP').valid_encoding?.should be_false
+ str.force_encoding('eucJP-ms').valid_encoding?.should be_false
+ str.force_encoding('CP51932').valid_encoding?.should be_false
+ str.force_encoding('GB2312').valid_encoding?.should be_false
+ str.force_encoding('GB12345').valid_encoding?.should be_false
+ str.force_encoding('ISO-2022-JP').valid_encoding?.should be_true
+ str.force_encoding('ISO-2022-JP-2').valid_encoding?.should be_true
+ str.force_encoding('CP50221').valid_encoding?.should be_true
+ str.force_encoding('Windows-1252').valid_encoding?.should be_true
+ str.force_encoding('Windows-1250').valid_encoding?.should be_true
+ str.force_encoding('Windows-1256').valid_encoding?.should be_true
+ str.force_encoding('Windows-1253').valid_encoding?.should be_true
+ str.force_encoding('Windows-1255').valid_encoding?.should be_true
+ str.force_encoding('Windows-1254').valid_encoding?.should be_true
+ str.force_encoding('TIS-620').valid_encoding?.should be_true
+ str.force_encoding('Windows-874').valid_encoding?.should be_true
+ str.force_encoding('Windows-1257').valid_encoding?.should be_true
+ str.force_encoding('Windows-31J').valid_encoding?.should be_false
+ str.force_encoding('MacJapanese').valid_encoding?.should be_false
+ str.force_encoding('UTF-7').valid_encoding?.should be_true
+ str.force_encoding('UTF8-MAC').valid_encoding?.should be_true
+ end
+
+ it "returns false if self is valid in one encoding, but invalid in the one it's tagged with" do
+ str = "\u{8765}"
+ str.valid_encoding?.should be_true
+ str = str.force_encoding('ascii')
+ str.valid_encoding?.should be_false
+ end
+
+ it "returns false if self contains a character invalid in the associated encoding" do
+ "abc#{[0x80].pack('C')}".force_encoding('ascii').valid_encoding?.should be_false
+ end
+
+ it "returns false if a valid String had an invalid character appended to it" do
+ str = "a"
+ str.valid_encoding?.should be_true
+ str << [0xDD].pack('C').force_encoding('utf-8')
+ str.valid_encoding?.should be_false
+ end
+
+ it "returns true if an invalid string is appended another invalid one but both make a valid string" do
+ str = [0xD0].pack('C').force_encoding('utf-8')
+ str.valid_encoding?.should be_false
+ str << [0xBF].pack('C').force_encoding('utf-8')
+ str.valid_encoding?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/core/struct/dig_spec.rb b/spec/ruby/core/struct/dig_spec.rb
new file mode 100644
index 0000000000..c7c979fabe
--- /dev/null
+++ b/spec/ruby/core/struct/dig_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+
+describe "Struct#dig" do
+ before(:each) do
+ @klass = Struct.new(:a)
+ @instance = @klass.new(@klass.new({ b: [1, 2, 3] }))
+ end
+
+ it "returns the nested value specified by the sequence of keys" do
+ @instance.dig(:a, :a).should == { b: [1, 2, 3] }
+ end
+
+ it "returns the nested value specified if the sequence includes an index" do
+ @instance.dig(:a, :a, :b, 0).should == 1
+ end
+
+ it "returns nil if any intermediate step is nil" do
+ @instance.dig(:b, 0).should == nil
+ end
+
+ it "raises a TypeError if any intermediate step does not respond to #dig" do
+ instance = @klass.new(1)
+ lambda {
+ instance.dig(:a, 3)
+ }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError if no arguments provided" do
+ lambda { @instance.dig }.should raise_error(ArgumentError)
+ end
+
+ it "calls #dig on any intermediate step with the rest of the sequence as arguments" do
+ obj = Object.new
+ instance = @klass.new(obj)
+
+ def obj.dig(*args)
+ {dug: args}
+ end
+
+ instance.dig(:a, :bar, :baz).should == { dug: [:bar, :baz] }
+ end
+end
diff --git a/spec/ruby/core/struct/dup_spec.rb b/spec/ruby/core/struct/dup_spec.rb
new file mode 100644
index 0000000000..cb54b7ceee
--- /dev/null
+++ b/spec/ruby/core/struct/dup_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Struct-based class#dup" do
+
+ # From https://github.com/jruby/jruby/issues/3686
+ it "retains an included module in the ancestor chain for the struct's singleton class" do
+ klass = Struct.new(:foo)
+ mod = Module.new do
+ def hello
+ "hello"
+ end
+ end
+
+ klass.extend(mod)
+ klass_dup = klass.dup
+ klass_dup.hello.should == "hello"
+ end
+
+end
diff --git a/spec/ruby/core/struct/each_pair_spec.rb b/spec/ruby/core/struct/each_pair_spec.rb
new file mode 100644
index 0000000000..1230ca9026
--- /dev/null
+++ b/spec/ruby/core/struct/each_pair_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/accessor'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Struct#each_pair" do
+ before :each do
+ @car = StructClasses::Car.new('Ford', 'Ranger', 2001)
+ end
+
+ it "passes each key value pair to the given block" do
+ @car.each_pair do |key, value|
+ value.should == @car[key]
+ end
+ end
+
+ context "with a block variable" do
+ it "passes an array to the given block" do
+ @car.each_pair.map { |var| var }.should == StructClasses::Car.members.zip(@car.values)
+ end
+ end
+
+ it "returns self if passed a block" do
+ @car.each_pair {}.should equal(@car)
+ end
+
+ it "returns an Enumerator if not passed a block" do
+ @car.each_pair.should be_an_instance_of(Enumerator)
+ end
+
+ it_behaves_like :struct_accessor, :each_pair
+ it_behaves_like :enumeratorized_with_origin_size, :each_pair, StructClasses::Car.new('Ford', 'Ranger')
+end
diff --git a/spec/ruby/core/struct/each_spec.rb b/spec/ruby/core/struct/each_spec.rb
new file mode 100644
index 0000000000..41c0fbd4d0
--- /dev/null
+++ b/spec/ruby/core/struct/each_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/accessor'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Struct#each" do
+ it "passes each value to the given block" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+ i = -1
+ car.each do |value|
+ value.should == car[i += 1]
+ end
+ end
+
+ it "returns self if passed a block" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+ car.each {}.should == car
+ end
+
+ it "returns an Enumerator if not passed a block" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+ car.each.should be_an_instance_of(Enumerator)
+ end
+
+ it_behaves_like :struct_accessor, :each
+ it_behaves_like :enumeratorized_with_origin_size, :each, StructClasses::Car.new('Ford', 'Ranger')
+end
diff --git a/spec/ruby/core/struct/element_reference_spec.rb b/spec/ruby/core/struct/element_reference_spec.rb
new file mode 100644
index 0000000000..28706a65aa
--- /dev/null
+++ b/spec/ruby/core/struct/element_reference_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Struct[]" do
+ it "is a synonym for new" do
+ StructClasses::Ruby['2.0', 'i686'].should be_kind_of(StructClasses::Ruby)
+ end
+end
+
+describe "Struct#[]" do
+ it "returns the attribute referenced" do
+ car = StructClasses::Car.new('Ford', 'Ranger', 1983)
+ car['make'].should == 'Ford'
+ car['model'].should == 'Ranger'
+ car['year'].should == 1983
+ car[:make].should == 'Ford'
+ car[:model].should == 'Ranger'
+ car[:year].should == 1983
+ car[0].should == 'Ford'
+ car[1].should == 'Ranger'
+ car[2].should == 1983
+ car[-3].should == 'Ford'
+ car[-2].should == 'Ranger'
+ car[-1].should == 1983
+ end
+
+ it "fails when it does not know about the requested attribute" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+ lambda { car[3] }.should raise_error(IndexError)
+ lambda { car[-4] }.should raise_error(IndexError)
+ lambda { car[:body] }.should raise_error(NameError)
+ lambda { car['wheels'] }.should raise_error(NameError)
+ end
+
+ it "fails if passed too many arguments" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+ lambda { car[:make, :model] }.should raise_error(ArgumentError)
+ end
+
+ it "fails if not passed a string, symbol, or integer" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+ lambda { car[Object.new] }.should raise_error(TypeError)
+ end
+
+ it "returns attribute names that contain hyphens" do
+ klass = Struct.new(:'current-state')
+ tuple = klass.new(0)
+ tuple['current-state'].should == 0
+ tuple[:'current-state'].should == 0
+ tuple[0].should == 0
+ end
+end
diff --git a/spec/ruby/core/struct/element_set_spec.rb b/spec/ruby/core/struct/element_set_spec.rb
new file mode 100644
index 0000000000..246e7cf3c2
--- /dev/null
+++ b/spec/ruby/core/struct/element_set_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Struct#[]=" do
+ it "assigns the passed value" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+
+ car[:model] = 'Escape'
+ car[:model].should == 'Escape'
+
+ car['model'] = 'Fusion'
+ car[:model].should == 'Fusion'
+
+ car[1] = 'Excursion'
+ car[:model].should == 'Excursion'
+
+ car[-1] = '2000-2005'
+ car[:year].should == '2000-2005'
+ end
+
+ it "fails when trying to assign attributes which don't exist" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+
+ lambda { car[:something] = true }.should raise_error(NameError)
+ lambda { car[3] = true }.should raise_error(IndexError)
+ lambda { car[-4] = true }.should raise_error(IndexError)
+ lambda { car[Object.new] = true }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/struct/eql_spec.rb b/spec/ruby/core/struct/eql_spec.rb
new file mode 100644
index 0000000000..c864b2b943
--- /dev/null
+++ b/spec/ruby/core/struct/eql_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/equal_value'
+
+describe "Struct#eql?" do
+ it_behaves_like :struct_equal_value, :eql?
+
+ it "returns false if any corresponding elements are not #eql?" do
+ car = StructClasses::Car.new("Honda", "Accord", 1998)
+ similar_car = StructClasses::Car.new("Honda", "Accord", 1998.0)
+ car.should_not eql(similar_car)
+ end
+end
diff --git a/spec/ruby/core/struct/equal_value_spec.rb b/spec/ruby/core/struct/equal_value_spec.rb
new file mode 100644
index 0000000000..0c0f7ba570
--- /dev/null
+++ b/spec/ruby/core/struct/equal_value_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/equal_value'
+
+describe "Struct#==" do
+ it_behaves_like :struct_equal_value, :==
+end
diff --git a/spec/ruby/core/struct/filter_spec.rb b/spec/ruby/core/struct/filter_spec.rb
new file mode 100644
index 0000000000..1105ed064a
--- /dev/null
+++ b/spec/ruby/core/struct/filter_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'shared/select'
+require_relative 'shared/accessor'
+require_relative '../enumerable/shared/enumeratorized'
+
+ruby_version_is "2.6" do
+ describe "Struct#filter" do
+ it_behaves_like :struct_select, :filter
+ it_behaves_like :struct_accessor, :filter
+ it_behaves_like :enumeratorized_with_origin_size, :filter, Struct.new(:foo).new
+ end
+end
diff --git a/spec/ruby/core/struct/fixtures/classes.rb b/spec/ruby/core/struct/fixtures/classes.rb
new file mode 100644
index 0000000000..6d620f9060
--- /dev/null
+++ b/spec/ruby/core/struct/fixtures/classes.rb
@@ -0,0 +1,26 @@
+module StructClasses
+
+ class Apple < Struct; end
+
+ Ruby = Struct.new(:version, :platform)
+
+ Car = Struct.new(:make, :model, :year)
+
+ class Honda < Car
+ def initialize(*args)
+ self.make = "Honda"
+ super(*args)
+ end
+ end
+
+ class SubclassX < Struct
+ end
+
+ class SubclassX
+ attr_reader :key
+ def initialize(*)
+ @key = :value
+ super
+ end
+ end
+end
diff --git a/spec/ruby/core/struct/hash_spec.rb b/spec/ruby/core/struct/hash_spec.rb
new file mode 100644
index 0000000000..71e8ccbd3a
--- /dev/null
+++ b/spec/ruby/core/struct/hash_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/accessor'
+
+describe "Struct#hash" do
+
+ it "returns the same fixnum for structs with the same content" do
+ [StructClasses::Ruby.new("1.8.6", "PPC"),
+ StructClasses::Car.new("Hugo", "Foo", "1972")].each do |stc|
+ stc.hash.should == stc.dup.hash
+ stc.hash.should be_kind_of(Fixnum)
+ end
+ end
+
+ it "returns the same value if structs are #eql?" do
+ car = StructClasses::Car.new("Honda", "Accord", "1998")
+ similar_car = StructClasses::Car.new("Honda", "Accord", "1998")
+ car.should eql(similar_car)
+ car.hash.should == similar_car.hash
+ end
+
+ it "allows for overriding methods in an included module" do
+ mod = Module.new do
+ def hash
+ "different"
+ end
+ end
+ s = Struct.new(:arg) do
+ include mod
+ end
+ s.new.hash.should == "different"
+ end
+
+ it "returns the same hash for recursive structs" do
+ car = StructClasses::Car.new("Honda", "Accord", "1998")
+ similar_car = StructClasses::Car.new("Honda", "Accord", "1998")
+ car[:make] = car
+ similar_car[:make] = car
+ car.hash.should == similar_car.hash
+ # This is because car.eql?(similar_car).
+ # Objects that are eql? must return the same hash.
+ # See the Struct#eql? specs
+ end
+
+ it_behaves_like :struct_accessor, :hash
+end
diff --git a/spec/ruby/core/struct/initialize_spec.rb b/spec/ruby/core/struct/initialize_spec.rb
new file mode 100644
index 0000000000..e82289071a
--- /dev/null
+++ b/spec/ruby/core/struct/initialize_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Struct#initialize" do
+
+ it "is private" do
+ StructClasses::Car.should have_private_instance_method(:initialize)
+ end
+
+ it 'allows valid Ruby method names for members' do
+ valid_method_names = [
+ :method1,
+ :method_1,
+ :method_1?,
+ :method_1!,
+ :a_method
+ ]
+ valid_method_names.each do |method_name|
+ klass = Struct.new(method_name)
+ instance = klass.new(:value)
+ instance.send(method_name).should == :value
+ writer_method = "#{method_name}=".to_sym
+ result = instance.send(writer_method, :new_value)
+ result.should == :new_value
+ instance.send(method_name).should == :new_value
+ end
+ end
+
+ it "does nothing when passed a set of fields equal to self" do
+ car = same_car = StructClasses::Car.new("Honda", "Accord", "1998")
+ car.instance_eval { initialize("Honda", "Accord", "1998") }
+ car.should == same_car
+ end
+
+ it "explicitly sets instance variables to nil when args not provided to initialize" do
+ car = StructClasses::Honda.new
+ car.make.should == nil # still nil despite override in Honda#initialize b/c of super order
+ end
+
+ it "can be overridden" do
+ StructClasses::SubclassX.new(:y).new.key.should == :value
+ end
+end
diff --git a/spec/ruby/core/struct/inspect_spec.rb b/spec/ruby/core/struct/inspect_spec.rb
new file mode 100644
index 0000000000..3df97c8604
--- /dev/null
+++ b/spec/ruby/core/struct/inspect_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/inspect'
+
+describe "Struct#inspect" do
+ it "returns a string representation of some kind" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+ car.inspect.should == '#<struct StructClasses::Car make="Ford", model="Ranger", year=nil>'
+
+ Whiskey = Struct.new(:name, :ounces)
+ Whiskey.new('Jack', 100).inspect.should == '#<struct Whiskey name="Jack", ounces=100>'
+ end
+
+ it_behaves_like :struct_inspect, :inspect
+end
diff --git a/spec/ruby/core/struct/instance_variables_spec.rb b/spec/ruby/core/struct/instance_variables_spec.rb
new file mode 100644
index 0000000000..f6d30ea97e
--- /dev/null
+++ b/spec/ruby/core/struct/instance_variables_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Struct#instance_variables" do
+ it "returns an empty array if only attributes are defined" do
+ car = StructClasses::Car.new("Hugo", "Foo", "1972")
+ car.instance_variables.should == []
+ end
+
+ it "returns an array with one name if an instance variable is added" do
+ car = StructClasses::Car.new("Hugo", "Foo", "1972")
+ car.instance_variables.should == []
+ car.instance_variable_set("@test", 1)
+ car.instance_variables.should == [:@test]
+ end
+end
diff --git a/spec/ruby/core/struct/length_spec.rb b/spec/ruby/core/struct/length_spec.rb
new file mode 100644
index 0000000000..1143676122
--- /dev/null
+++ b/spec/ruby/core/struct/length_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/accessor'
+
+describe "Struct#length" do
+ it "returns the number of attributes" do
+ StructClasses::Car.new('Cadillac', 'DeVille').length.should == 3
+ StructClasses::Car.new.length.should == 3
+ end
+
+ it_behaves_like :struct_accessor, :length
+end
diff --git a/spec/ruby/core/struct/members_spec.rb b/spec/ruby/core/struct/members_spec.rb
new file mode 100644
index 0000000000..1f2ff950d9
--- /dev/null
+++ b/spec/ruby/core/struct/members_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/accessor'
+
+describe "Struct#members" do
+ it "returns an array of attribute names" do
+ StructClasses::Car.new.members.should == [:make, :model, :year]
+ StructClasses::Car.new('Cadillac').members.should == [:make, :model, :year]
+ StructClasses::Ruby.members.should == [:version, :platform]
+ end
+
+ it_behaves_like :struct_accessor, :members
+end
diff --git a/spec/ruby/core/struct/new_spec.rb b/spec/ruby/core/struct/new_spec.rb
new file mode 100644
index 0000000000..01231e85fb
--- /dev/null
+++ b/spec/ruby/core/struct/new_spec.rb
@@ -0,0 +1,194 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Struct.new" do
+ it "creates a constant in Struct namespace with string as first argument" do
+ struct = Struct.new('Animal', :name, :legs, :eyeballs)
+ struct.should == Struct::Animal
+ struct.name.should == "Struct::Animal"
+ end
+
+ it "overwrites previously defined constants with string as first argument" do
+ first = Struct.new('Person', :height, :weight)
+ first.should == Struct::Person
+
+ second = nil
+ lambda {
+ second = Struct.new('Person', :hair, :sex)
+ }.should complain(/redefining constant/)
+ second.should == Struct::Person
+
+ first.members.should_not == second.members
+ end
+
+ it "calls to_str on its first argument (constant name)" do
+ obj = mock('Foo')
+ def obj.to_str() "Foo" end
+ struct = Struct.new(obj)
+ struct.should == Struct::Foo
+ struct.name.should == "Struct::Foo"
+ end
+
+ it "creates a new anonymous class with nil first argument" do
+ struct = Struct.new(nil, :foo)
+ struct.new("bar").foo.should == "bar"
+ struct.should be_kind_of(Class)
+ struct.name.should be_nil
+ end
+
+ it "creates a new anonymous class with symbol arguments" do
+ struct = Struct.new(:make, :model)
+ struct.should be_kind_of(Class)
+ struct.name.should == nil
+ end
+
+ it "does not create a constant with symbol as first argument" do
+ Struct.new(:Animal2, :name, :legs, :eyeballs)
+ Struct.const_defined?("Animal2").should be_false
+ end
+
+
+ it "fails with invalid constant name as first argument" do
+ lambda { Struct.new('animal', :name, :legs, :eyeballs) }.should raise_error(NameError)
+ end
+
+ it "raises a TypeError if object doesn't respond to to_sym" do
+ lambda { Struct.new(:animal, mock('giraffe')) }.should raise_error(TypeError)
+ lambda { Struct.new(:animal, 1.0) }.should raise_error(TypeError)
+ lambda { Struct.new(:animal, Time.now) }.should raise_error(TypeError)
+ lambda { Struct.new(:animal, Class) }.should raise_error(TypeError)
+ lambda { Struct.new(:animal, nil) }.should raise_error(TypeError)
+ lambda { Struct.new(:animal, true) }.should raise_error(TypeError)
+ lambda { Struct.new(:animal, ['chris', 'evan']) }.should raise_error(TypeError)
+ end
+
+ ruby_version_is ""..."2.5" do
+ it "raises a TypeError if an argument is a Hash" do
+ lambda { Struct.new(:animal, { name: 'chris' }) }.should raise_error(TypeError)
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "raises a ArgumentError if passed a Hash with an unknown key" do
+ lambda { Struct.new(:animal, { name: 'chris' }) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "raises a TypeError if object is not a Symbol" do
+ obj = mock(':ruby')
+ def obj.to_sym() :ruby end
+ lambda { Struct.new(:animal, obj) }.should raise_error(TypeError)
+ end
+
+ it "processes passed block with instance_eval" do
+ klass = Struct.new(:something) { @something_else = 'something else entirely!' }
+ klass.instance_variables.should include(:@something_else)
+ end
+
+ context "with a block" do
+ it "allows class to be modified via the block" do
+ klass = Struct.new(:version) do
+ def platform
+ :ruby
+ end
+ end
+ instance = klass.new('2.2')
+
+ instance.version.should == '2.2'
+ instance.platform.should == :ruby
+ end
+
+ it "passes same struct class to the block" do
+ given = nil
+ klass = Struct.new(:attr) do |block_parameter|
+ given = block_parameter
+ end
+ klass.should equal(given)
+ end
+ end
+
+ context "on subclasses" do
+ it "creates a constant in subclass' namespace" do
+ struct = StructClasses::Apple.new('Computer', :size)
+ struct.should == StructClasses::Apple::Computer
+ end
+
+ it "creates an instance" do
+ StructClasses::Ruby.new.kind_of?(StructClasses::Ruby).should == true
+ end
+
+ it "creates reader methods" do
+ StructClasses::Ruby.new.should have_method(:version)
+ StructClasses::Ruby.new.should have_method(:platform)
+ end
+
+ it "creates writer methods" do
+ StructClasses::Ruby.new.should have_method(:version=)
+ StructClasses::Ruby.new.should have_method(:platform=)
+ end
+
+ it "fails with too many arguments" do
+ lambda { StructClasses::Ruby.new('2.0', 'i686', true) }.should raise_error(ArgumentError)
+ end
+
+ it "passes a hash as a normal argument" do
+ type = Struct.new(:args)
+
+ obj = type.new(keyword: :arg)
+ obj2 = type.new(*[{keyword: :arg}])
+
+ obj.should == obj2
+ obj.args.should == {keyword: :arg}
+ obj2.args.should == {keyword: :arg}
+ end
+ end
+
+ ruby_version_is "2.5" do
+ context "keyword_init: true option" do
+ before :all do
+ @struct_with_kwa = Struct.new(:name, :legs, keyword_init: true)
+ @struct_without_kwa = Struct.new(:name, :legs, keyword_init: false)
+ end
+
+ it "creates a class that accepts keyword arguments to initialize" do
+ obj = @struct_with_kwa.new(name: "elefant", legs: 4)
+ obj.name.should == "elefant"
+ obj.legs.should == 4
+ end
+
+ describe "new class instantiation" do
+ it "accepts arguments as hash as well" do
+ obj = @struct_with_kwa.new({name: "elefant", legs: 4})
+ obj.name.should == "elefant"
+ obj.legs.should == 4
+ end
+
+ it "raises ArgumentError when passed not declared keyword argument" do
+ -> {
+ @struct_with_kwa.new(name: "elefant", legs: 4, foo: "bar")
+ }.should raise_error(ArgumentError, /unknown keywords: foo/)
+ end
+
+ it "raises ArgumentError when passed a list of arguments" do
+ -> {
+ @struct_with_kwa.new("elefant", 4)
+ }.should raise_error(ArgumentError, /wrong number of arguments/)
+ end
+
+ it "raises ArgumentError when passed a single non-hash argument" do
+ -> {
+ @struct_with_kwa.new("elefant")
+ }.should raise_error(ArgumentError, /wrong number of arguments/)
+ end
+ end
+ end
+
+ context "keyword_init: false option" do
+ it "behaves like it does without :keyword_init option" do
+ obj = @struct_without_kwa.new("elefant", 4)
+ obj.name.should == "elefant"
+ obj.legs.should == 4
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/struct/select_spec.rb b/spec/ruby/core/struct/select_spec.rb
new file mode 100644
index 0000000000..ee846ec45f
--- /dev/null
+++ b/spec/ruby/core/struct/select_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'shared/select'
+require_relative 'shared/accessor'
+require_relative '../enumerable/shared/enumeratorized'
+
+describe "Struct#select" do
+ it_behaves_like :struct_select, :select
+ it_behaves_like :struct_accessor, :select
+ it_behaves_like :enumeratorized_with_origin_size, :select, Struct.new(:foo).new
+end
diff --git a/spec/ruby/core/struct/shared/accessor.rb b/spec/ruby/core/struct/shared/accessor.rb
new file mode 100644
index 0000000000..dbf5e78f43
--- /dev/null
+++ b/spec/ruby/core/struct/shared/accessor.rb
@@ -0,0 +1,7 @@
+describe :struct_accessor, shared: true do
+ it "does not override the instance accessor method" do
+ struct = Struct.new(@method.to_sym)
+ instance = struct.new 42
+ instance.send(@method).should == 42
+ end
+end
diff --git a/spec/ruby/core/struct/shared/equal_value.rb b/spec/ruby/core/struct/shared/equal_value.rb
new file mode 100644
index 0000000000..a7e0856df5
--- /dev/null
+++ b/spec/ruby/core/struct/shared/equal_value.rb
@@ -0,0 +1,37 @@
+describe :struct_equal_value, shared: true do
+ it "returns true if the other is the same object" do
+ car = same_car = StructClasses::Car.new("Honda", "Accord", "1998")
+ car.send(@method, same_car).should == true
+ end
+
+ it "returns true if the other has all the same fields" do
+ car = StructClasses::Car.new("Honda", "Accord", "1998")
+ similar_car = StructClasses::Car.new("Honda", "Accord", "1998")
+ car.send(@method, similar_car).should == true
+ end
+
+ it "returns false if the other is a different object or has different fields" do
+ car = StructClasses::Car.new("Honda", "Accord", "1998")
+ different_car = StructClasses::Car.new("Honda", "Accord", "1995")
+ car.send(@method, different_car).should == false
+ end
+
+ it "returns false if other is of a different class" do
+ car = StructClasses::Car.new("Honda", "Accord", "1998")
+ klass = Struct.new(:make, :model, :year)
+ clone = klass.new("Honda", "Accord", "1998")
+ car.send(@method, clone).should == false
+ end
+
+ it "handles recursive structures by returning false if a difference can be found" do
+ x = StructClasses::Car.new("Honda", "Accord", "1998")
+ x[:make] = x
+ stepping = StructClasses::Car.new("Honda", "Accord", "1998")
+ stone = StructClasses::Car.new(stepping, "Accord", "1998")
+ stepping[:make] = stone
+ x.send(@method, stepping).should == true
+
+ stone[:year] = "1999" # introduce a difference
+ x.send(@method, stepping).should == false
+ end
+end
diff --git a/spec/ruby/core/struct/shared/inspect.rb b/spec/ruby/core/struct/shared/inspect.rb
new file mode 100644
index 0000000000..90594a5452
--- /dev/null
+++ b/spec/ruby/core/struct/shared/inspect.rb
@@ -0,0 +1,5 @@
+describe :struct_inspect, shared: true do
+ it "returns a string representation without the class name for anonymous structs" do
+ Struct.new(:a).new("").send(@method).should == '#<struct a="">'
+ end
+end
diff --git a/spec/ruby/core/struct/shared/select.rb b/spec/ruby/core/struct/shared/select.rb
new file mode 100644
index 0000000000..35abee461b
--- /dev/null
+++ b/spec/ruby/core/struct/shared/select.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :struct_select, shared: true do
+ it "raises an ArgumentError if given any non-block arguments" do
+ struct = StructClasses::Car.new
+ -> { struct.send(@method, 1) { } }.should raise_error(ArgumentError)
+ end
+
+ it "returns a new array of elements for which block is true" do
+ struct = StructClasses::Car.new("Toyota", "Tercel", "2000")
+ struct.send(@method) { |i| i == "2000" }.should == [ "2000" ]
+ end
+
+ it "returns an instance of Array" do
+ struct = StructClasses::Car.new("Ford", "Escort", "1995")
+ struct.send(@method) { true }.should be_an_instance_of(Array)
+ end
+
+ describe "without block" do
+ it "returns an instance of Enumerator" do
+ struct = Struct.new(:foo).new
+ struct.send(@method).should be_an_instance_of(Enumerator)
+ end
+ end
+end
diff --git a/spec/ruby/core/struct/size_spec.rb b/spec/ruby/core/struct/size_spec.rb
new file mode 100644
index 0000000000..09f260cf20
--- /dev/null
+++ b/spec/ruby/core/struct/size_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/accessor'
+
+describe "Struct#size" do
+ it "is a synonym for length" do
+ StructClasses::Car.new.size.should == StructClasses::Car.new.length
+ end
+
+ it_behaves_like :struct_accessor, :size
+end
diff --git a/spec/ruby/core/struct/struct_spec.rb b/spec/ruby/core/struct/struct_spec.rb
new file mode 100644
index 0000000000..11e619dcde
--- /dev/null
+++ b/spec/ruby/core/struct/struct_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Struct" do
+ it "includes Enumerable" do
+ Struct.include?(Enumerable).should == true
+ end
+end
+
+describe "Struct anonymous class instance methods" do
+ it "includes Enumerable" do
+ StructClasses::Car.include?(Enumerable).should == true
+ end
+
+ it "reader method should be a synonym for []" do
+ klass = Struct.new(:clock, :radio)
+ alarm = klass.new(true)
+ alarm.clock.should == alarm[:clock]
+ alarm.radio.should == alarm['radio']
+ end
+
+ it "reader method should not interfere with undefined methods" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+ lambda { car.something_weird }.should raise_error(NoMethodError)
+ end
+
+ it "writer method be a synonym for []=" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+ car.model.should == 'Ranger'
+ car.model = 'F150'
+ car.model.should == 'F150'
+ car[:model].should == 'F150'
+ car['model'].should == 'F150'
+ car[1].should == 'F150'
+ end
+end
+
+describe "Struct subclasses" do
+ it "can be subclassed" do
+ compact = Class.new StructClasses::Car
+ compact.new.class.should == compact
+ end
+end
diff --git a/spec/ruby/core/struct/to_a_spec.rb b/spec/ruby/core/struct/to_a_spec.rb
new file mode 100644
index 0000000000..cb61dc45cc
--- /dev/null
+++ b/spec/ruby/core/struct/to_a_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/accessor'
+
+describe "Struct#to_a" do
+ it "returns the values for this instance as an array" do
+ StructClasses::Car.new('Geo', 'Metro', 1995).to_a.should == ['Geo', 'Metro', 1995]
+ StructClasses::Car.new('Ford').to_a.should == ['Ford', nil, nil]
+ end
+
+ it_behaves_like :struct_accessor, :to_a
+end
diff --git a/spec/ruby/core/struct/to_h_spec.rb b/spec/ruby/core/struct/to_h_spec.rb
new file mode 100644
index 0000000000..ddfbfbca61
--- /dev/null
+++ b/spec/ruby/core/struct/to_h_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Struct#to_h" do
+ it "returns a Hash with members as keys" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+ car.to_h.should == {make: "Ford", model: "Ranger", year: nil}
+ end
+
+ it "returns a Hash that is independent from the struct" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+ car.to_h[:make] = 'Suzuki'
+ car.make.should == 'Ford'
+ end
+
+ ruby_version_is "2.6" do
+ it "converts [key, value] pairs returned by the block to a hash" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+ h = car.to_h {|k, v| [k.to_s, "#{v}".downcase]}
+ h.should == {"make" => "ford", "model" => "ranger", "year" => ""}
+ end
+ end
+end
diff --git a/spec/ruby/core/struct/to_s_spec.rb b/spec/ruby/core/struct/to_s_spec.rb
new file mode 100644
index 0000000000..94c672d3d5
--- /dev/null
+++ b/spec/ruby/core/struct/to_s_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/inspect'
+
+describe "Struct#to_s" do
+ it "is a synonym for inspect" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+ car.inspect.should == car.to_s
+ end
+
+ it_behaves_like :struct_inspect, :to_s
+end
diff --git a/spec/ruby/core/struct/values_at_spec.rb b/spec/ruby/core/struct/values_at_spec.rb
new file mode 100644
index 0000000000..f9602d53f7
--- /dev/null
+++ b/spec/ruby/core/struct/values_at_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Struct#values_at" do
+ it "returns an array of values" do
+ clazz = Struct.new(:name, :director, :year)
+ movie = clazz.new('Sympathy for Mr. Vengence', 'Chan-wook Park', 2002)
+ movie.values_at(0, 1).should == ['Sympathy for Mr. Vengence', 'Chan-wook Park']
+ movie.values_at(0..2).should == ['Sympathy for Mr. Vengence', 'Chan-wook Park', 2002]
+ end
+
+ it "fails when passed unsupported types" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+ lambda { car.values_at('make') }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/struct/values_spec.rb b/spec/ruby/core/struct/values_spec.rb
new file mode 100644
index 0000000000..b2d11725b9
--- /dev/null
+++ b/spec/ruby/core/struct/values_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Struct#values" do
+ it "is a synonym for to_a" do
+ car = StructClasses::Car.new('Nissan', 'Maxima')
+ car.values.should == car.to_a
+
+ StructClasses::Car.new.values.should == StructClasses::Car.new.to_a
+ end
+end
diff --git a/spec/ruby/core/symbol/all_symbols_spec.rb b/spec/ruby/core/symbol/all_symbols_spec.rb
new file mode 100644
index 0000000000..ef2b4f85e6
--- /dev/null
+++ b/spec/ruby/core/symbol/all_symbols_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe "Symbol.all_symbols" do
+ it "returns an array containing all the Symbols in the symbol table" do
+ all_symbols = Symbol.all_symbols
+ all_symbols.should be_an_instance_of(Array)
+ all_symbols.all? { |s| s.is_a?(Symbol) ? true : (p s; false) }.should == true
+ end
+
+ it "returns an Array containing Symbols that have been created" do
+ symbol = "symbol_specs_#{rand(5_000_000)}".to_sym
+ Symbol.all_symbols.should include(symbol)
+ end
+end
diff --git a/spec/ruby/core/symbol/capitalize_spec.rb b/spec/ruby/core/symbol/capitalize_spec.rb
new file mode 100644
index 0000000000..54aebf21ca
--- /dev/null
+++ b/spec/ruby/core/symbol/capitalize_spec.rb
@@ -0,0 +1,56 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe "Symbol#capitalize" do
+ it "returns a Symbol" do
+ :glark.capitalize.should be_an_instance_of(Symbol)
+ end
+
+ it "converts the first character to uppercase if it is ASCII" do
+ :lower.capitalize.should == :Lower
+ end
+
+ it "leaves the first character alone if it is not an alphabetical character" do
+ :"£1.20".capitalize.should == :"£1.20"
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "leaves the first character alone if it is not an alphabetical ASCII character" do
+ "\u{00DE}c".to_sym.capitalize.should == :"Þc"
+ "\u{00DF}C".to_sym.capitalize.should == :"ßc"
+ end
+ end
+
+ ruby_version_is '2.4' do
+ it "capitalizes the first character if it is Unicode" do
+ :"äöü".capitalize.should == :"Äöü"
+ :"aou".capitalize.should == :"Aou"
+ end
+ end
+
+ it "converts subsequent uppercase ASCII characters to their lowercase equivalents" do
+ :lOWER.capitalize.should == :Lower
+ end
+
+ it "leaves ASCII characters already in the correct case as they were" do
+ :Title.capitalize.should == :Title
+ end
+
+ it "works with both upper- and lowercase ASCII characters in the same Symbol" do
+ :mIxEd.capitalize.should == :Mixed
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "leaves uppercase Unicode characters as they were" do
+ "a\u{00DE}c".to_sym.capitalize.should == :"AÞc"
+ end
+ end
+
+ it "leaves lowercase Unicode characters (except in first position) as they were" do
+ "a\u{00DF}C".to_sym.capitalize.should == :"Aßc"
+ end
+
+ it "leaves non-alphabetic ASCII characters as they were" do
+ "Glark?!?".to_sym.capitalize.should == :"Glark?!?"
+ end
+end
diff --git a/spec/ruby/core/symbol/case_compare_spec.rb b/spec/ruby/core/symbol/case_compare_spec.rb
new file mode 100644
index 0000000000..0c6bc1eda5
--- /dev/null
+++ b/spec/ruby/core/symbol/case_compare_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Symbol#===" do
+ it "returns true when the argument is a Symbol" do
+ (Symbol === :ruby).should == true
+ end
+
+ it "returns false when the argument is a String" do
+ (Symbol === 'ruby').should == false
+ end
+end
diff --git a/spec/ruby/core/symbol/casecmp_spec.rb b/spec/ruby/core/symbol/casecmp_spec.rb
new file mode 100644
index 0000000000..232399b664
--- /dev/null
+++ b/spec/ruby/core/symbol/casecmp_spec.rb
@@ -0,0 +1,146 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe "Symbol#casecmp with Symbol" do
+ it "compares symbols without regard to case" do
+ :abcdef.casecmp(:abcde).should == 1
+ :aBcDeF.casecmp(:abcdef).should == 0
+ :abcdef.casecmp(:abcdefg).should == -1
+ :abcdef.casecmp(:ABCDEF).should == 0
+ end
+
+ it "doesn't consider non-ascii characters equal that aren't" do
+ # -- Latin-1 --
+ upper_a_tilde = "\xC3".b.to_sym
+ upper_a_umlaut = "\xC4".b.to_sym
+ lower_a_tilde = "\xE3".b.to_sym
+ lower_a_umlaut = "\xE4".b.to_sym
+
+ lower_a_tilde.casecmp(lower_a_umlaut).should_not == 0
+ lower_a_umlaut.casecmp(lower_a_tilde).should_not == 0
+ upper_a_tilde.casecmp(upper_a_umlaut).should_not == 0
+ upper_a_umlaut.casecmp(upper_a_tilde).should_not == 0
+
+ # -- UTF-8 --
+ upper_a_tilde = :"Ã"
+ lower_a_tilde = :"ã"
+ upper_a_umlaut = :"Ä"
+ lower_a_umlaut = :"ä"
+
+ lower_a_tilde.casecmp(lower_a_umlaut).should_not == 0
+ lower_a_umlaut.casecmp(lower_a_tilde).should_not == 0
+ upper_a_tilde.casecmp(upper_a_umlaut).should_not == 0
+ upper_a_umlaut.casecmp(upper_a_tilde).should_not == 0
+ end
+
+ it "doesn't do case mapping for non-ascii characters" do
+ # -- Latin-1 --
+ upper_a_tilde = "\xC3".b.to_sym
+ upper_a_umlaut = "\xC4".b.to_sym
+ lower_a_tilde = "\xE3".b.to_sym
+ lower_a_umlaut = "\xE4".b.to_sym
+
+ upper_a_tilde.casecmp(lower_a_tilde).should == -1
+ upper_a_umlaut.casecmp(lower_a_umlaut).should == -1
+ lower_a_tilde.casecmp(upper_a_tilde).should == 1
+ lower_a_umlaut.casecmp(upper_a_umlaut).should == 1
+
+ # -- UTF-8 --
+ upper_a_tilde = :"Ã"
+ lower_a_tilde = :"ã"
+ upper_a_umlaut = :"Ä"
+ lower_a_umlaut = :"ä"
+
+ upper_a_tilde.casecmp(lower_a_tilde).should == -1
+ upper_a_umlaut.casecmp(lower_a_umlaut).should == -1
+ lower_a_tilde.casecmp(upper_a_tilde).should == 1
+ lower_a_umlaut.casecmp(upper_a_umlaut).should == 1
+ end
+end
+
+describe "Symbol#casecmp" do
+ it "returns nil if other is a String" do
+ :abc.casecmp("abc").should be_nil
+ end
+
+ it "returns nil if other is a Fixnum" do
+ :abc.casecmp(1).should be_nil
+ end
+
+ it "returns nil if other is an object" do
+ obj = mock("string <=>")
+ :abc.casecmp(obj).should be_nil
+ end
+end
+
+ruby_version_is "2.4" do
+ describe 'Symbol#casecmp?' do
+ it "compares symbols without regard to case" do
+ :abcdef.casecmp?(:abcde).should == false
+ :aBcDeF.casecmp?(:abcdef).should == true
+ :abcdef.casecmp?(:abcdefg).should == false
+ :abcdef.casecmp?(:ABCDEF).should == true
+ end
+
+ it "doesn't consider non-ascii characters equal that aren't" do
+ # -- Latin-1 --
+ upper_a_tilde = "\xC3".b.to_sym
+ upper_a_umlaut = "\xC4".b.to_sym
+ lower_a_tilde = "\xE3".b.to_sym
+ lower_a_umlaut = "\xE4".b.to_sym
+
+ lower_a_tilde.casecmp?(lower_a_umlaut).should_not == true
+ lower_a_umlaut.casecmp?(lower_a_tilde).should_not == true
+ upper_a_tilde.casecmp?(upper_a_umlaut).should_not == true
+ upper_a_umlaut.casecmp?(upper_a_tilde).should_not == true
+
+ # -- UTF-8 --
+ upper_a_tilde = :"Ã"
+ lower_a_tilde = :"ã"
+ upper_a_umlaut = :"Ä"
+ lower_a_umlaut = :"ä"
+
+ lower_a_tilde.casecmp?(lower_a_umlaut).should_not == true
+ lower_a_umlaut.casecmp?(lower_a_tilde).should_not == true
+ upper_a_tilde.casecmp?(upper_a_umlaut).should_not == true
+ upper_a_umlaut.casecmp?(upper_a_tilde).should_not == true
+ end
+
+ it "doesn't do case mapping for non-ascii and non-unicode characters" do
+ # -- Latin-1 --
+ upper_a_tilde = "\xC3".b.to_sym
+ upper_a_umlaut = "\xC4".b.to_sym
+ lower_a_tilde = "\xE3".b.to_sym
+ lower_a_umlaut = "\xE4".b.to_sym
+
+ upper_a_tilde.casecmp?(lower_a_tilde).should == false
+ upper_a_umlaut.casecmp?(lower_a_umlaut).should == false
+ lower_a_tilde.casecmp?(upper_a_tilde).should == false
+ lower_a_umlaut.casecmp?(upper_a_umlaut).should == false
+ end
+
+ it 'does case mapping for unicode characters' do
+ # -- UTF-8 --
+ upper_a_tilde = :"Ã"
+ lower_a_tilde = :"ã"
+ upper_a_umlaut = :"Ä"
+ lower_a_umlaut = :"ä"
+
+ upper_a_tilde.casecmp?(lower_a_tilde).should == true
+ upper_a_umlaut.casecmp?(lower_a_umlaut).should == true
+ lower_a_tilde.casecmp?(upper_a_tilde).should == true
+ lower_a_umlaut.casecmp?(upper_a_umlaut).should == true
+ end
+
+ it 'returns nil when comparing characters with different encodings' do
+ # -- Latin-1 --
+ upper_a_tilde = "\xC3".b.to_sym
+
+ # -- UTF-8 --
+ lower_a_tilde = :"ã"
+
+ upper_a_tilde.casecmp?(lower_a_tilde).should == nil
+ lower_a_tilde.casecmp?(upper_a_tilde).should == nil
+ end
+ end
+end
diff --git a/spec/ruby/core/symbol/comparison_spec.rb b/spec/ruby/core/symbol/comparison_spec.rb
new file mode 100644
index 0000000000..173a7da625
--- /dev/null
+++ b/spec/ruby/core/symbol/comparison_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+
+describe "Symbol#<=> with Symbol" do
+ it "compares individual characters based on their ascii value" do
+ ascii_order = Array.new(256) { |x| x.chr.to_sym }
+ sort_order = ascii_order.sort
+ sort_order.should == ascii_order
+ end
+
+ it "returns -1 when self is less than other" do
+ (:this <=> :those).should == -1
+ end
+
+ it "returns 0 when self is equal to other" do
+ (:yep <=> :yep).should == 0
+ end
+
+ it "returns 1 when self is greater than other" do
+ (:yoddle <=> :griddle).should == 1
+ end
+
+ it "considers symbol that comes lexicographically first to be less if the symbols have same size" do
+ (:aba <=> :abc).should == -1
+ (:abc <=> :aba).should == 1
+ end
+
+ it "doesn't consider shorter string to be less if longer string starts with shorter one" do
+ (:abc <=> :abcd).should == -1
+ (:abcd <=> :abc).should == 1
+ end
+
+ it "compares shorter string with corresponding number of first chars of longer string" do
+ (:abx <=> :abcd).should == 1
+ (:abcd <=> :abx).should == -1
+ end
+end
+
+describe "Symbol#<=>" do
+ it "returns nil if other is a String" do
+ (:abc <=> "abc").should be_nil
+ end
+
+ it "returns nil if other is a Fixnum" do
+ (:abc <=> 1).should be_nil
+ end
+
+ it "returns nil if other is an object" do
+ obj = mock("string <=>")
+ (:abc <=> obj).should be_nil
+ end
+end
diff --git a/spec/ruby/core/symbol/downcase_spec.rb b/spec/ruby/core/symbol/downcase_spec.rb
new file mode 100644
index 0000000000..eb81c90d05
--- /dev/null
+++ b/spec/ruby/core/symbol/downcase_spec.rb
@@ -0,0 +1,33 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe "Symbol#downcase" do
+ it "returns a Symbol" do
+ :glark.downcase.should be_an_instance_of(Symbol)
+ end
+
+ it "converts uppercase ASCII characters to their lowercase equivalents" do
+ :lOwEr.downcase.should == :lower
+ end
+
+ it "leaves lowercase Unicode characters as they were" do
+ "\u{E0}Bc".to_sym.downcase.should == :"àbc"
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "leaves uppercase Unicode characters as they were" do
+ "\u{DE}Bc".to_sym.downcase.should == :"Þbc"
+ end
+ end
+
+ ruby_version_is '2.4' do
+ it "uncapitalizes all Unicode characters" do
+ "ÄÖÜ".to_sym.downcase.should == :"äöü"
+ "AOU".to_sym.downcase.should == :"aou"
+ end
+ end
+
+ it "leaves non-alphabetic ASCII characters as they were" do
+ "Glark?!?".to_sym.downcase.should == :"glark?!?"
+ end
+end
diff --git a/spec/ruby/core/symbol/dup_spec.rb b/spec/ruby/core/symbol/dup_spec.rb
new file mode 100644
index 0000000000..202720e8f5
--- /dev/null
+++ b/spec/ruby/core/symbol/dup_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '2.4' do
+ describe "Symbol#dup" do
+ it "returns self" do
+ :a_symbol.dup.should equal(:a_symbol)
+ end
+ end
+end
diff --git a/spec/ruby/core/symbol/element_reference_spec.rb b/spec/ruby/core/symbol/element_reference_spec.rb
new file mode 100644
index 0000000000..df6bc15ddb
--- /dev/null
+++ b/spec/ruby/core/symbol/element_reference_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/slice'
+
+describe "Symbol#[]" do
+ it_behaves_like :symbol_slice, :[]
+end
diff --git a/spec/ruby/core/symbol/empty_spec.rb b/spec/ruby/core/symbol/empty_spec.rb
new file mode 100644
index 0000000000..19c23cfe5f
--- /dev/null
+++ b/spec/ruby/core/symbol/empty_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Symbol#empty?" do
+ it "returns true if self is empty" do
+ :"".empty?.should be_true
+ end
+
+ it "returns false if self is non-empty" do
+ :"a".empty?.should be_false
+ end
+end
diff --git a/spec/ruby/core/symbol/encoding_spec.rb b/spec/ruby/core/symbol/encoding_spec.rb
new file mode 100644
index 0000000000..732fd62e26
--- /dev/null
+++ b/spec/ruby/core/symbol/encoding_spec.rb
@@ -0,0 +1,23 @@
+# encoding: utf-8
+
+require_relative '../../spec_helper'
+
+describe "Symbol#encoding for ASCII symbols" do
+ it "is US-ASCII" do
+ :foo.encoding.name.should == "US-ASCII"
+ end
+
+ it "is US-ASCII after converting to string" do
+ :foo.to_s.encoding.name.should == "US-ASCII"
+ end
+end
+
+describe "Symbol#encoding for UTF-8 symbols" do
+ it "is UTF-8" do
+ :åäö.encoding.name.should == "UTF-8"
+ end
+
+ it "is UTF-8 after converting to string" do
+ :åäö.to_s.encoding.name.should == "UTF-8"
+ end
+end
diff --git a/spec/ruby/core/symbol/equal_value_spec.rb b/spec/ruby/core/symbol/equal_value_spec.rb
new file mode 100644
index 0000000000..3fe997d02a
--- /dev/null
+++ b/spec/ruby/core/symbol/equal_value_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe "Symbol#==" do
+ it "only returns true when the other is exactly the same symbol" do
+ (:ruby == :ruby).should == true
+ (:ruby == :"ruby").should == true
+ (:ruby == :'ruby').should == true
+ (:@ruby == :@ruby).should == true
+
+ (:ruby == :@ruby).should == false
+ (:foo == :bar).should == false
+ (:ruby == 'ruby').should == false
+ end
+end
diff --git a/spec/ruby/core/symbol/fixtures/classes.rb b/spec/ruby/core/symbol/fixtures/classes.rb
new file mode 100644
index 0000000000..6552f6ee38
--- /dev/null
+++ b/spec/ruby/core/symbol/fixtures/classes.rb
@@ -0,0 +1,3 @@
+module SymbolSpecs
+ class MyRange < Range; end
+end
diff --git a/spec/ruby/core/symbol/id2name_spec.rb b/spec/ruby/core/symbol/id2name_spec.rb
new file mode 100644
index 0000000000..2caa89fc37
--- /dev/null
+++ b/spec/ruby/core/symbol/id2name_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/id2name'
+
+describe "Symbol#id2name" do
+ it_behaves_like :symbol_id2name, :id2name
+end
diff --git a/spec/ruby/core/symbol/inspect_spec.rb b/spec/ruby/core/symbol/inspect_spec.rb
new file mode 100644
index 0000000000..58402ab261
--- /dev/null
+++ b/spec/ruby/core/symbol/inspect_spec.rb
@@ -0,0 +1,105 @@
+require_relative '../../spec_helper'
+
+describe "Symbol#inspect" do
+ symbols = {
+ fred: ":fred",
+ :fred? => ":fred?",
+ :fred! => ":fred!",
+ :$ruby => ":$ruby",
+ :@ruby => ":@ruby",
+ :@@ruby => ":@@ruby",
+ :"$ruby!" => ":\"$ruby!\"",
+ :"$ruby?" => ":\"$ruby?\"",
+ :"@ruby!" => ":\"@ruby!\"",
+ :"@ruby?" => ":\"@ruby?\"",
+ :"@@ruby!" => ":\"@@ruby!\"",
+ :"@@ruby?" => ":\"@@ruby?\"",
+
+ :$-w => ":$-w",
+ :"$-ww" => ":\"$-ww\"",
+ :"$+" => ":$+",
+ :"$~" => ":$~",
+ :"$:" => ":$:",
+ :"$?" => ":$?",
+ :"$<" => ":$<",
+ :"$_" => ":$_",
+ :"$/" => ":$/",
+ :"$'" => ":$'",
+ :"$\"" => ":$\"",
+ :"$$" => ":$$",
+ :"$." => ":$.",
+ :"$," => ":$,",
+ :"$`" => ":$`",
+ :"$!" => ":$!",
+ :"$;" => ":$;",
+ :"$\\" => ":$\\",
+ :"$=" => ":$=",
+ :"$*" => ":$*",
+ :"$>" => ":$>",
+ :"$&" => ":$&",
+ :"$@" => ":$@",
+ :"$1234" => ":$1234",
+
+ :-@ => ":-@",
+ :+@ => ":+@",
+ :% => ":%",
+ :& => ":&",
+ :* => ":*",
+ :** => ":**",
+ :"/" => ":/", # lhs quoted for emacs happiness
+ :< => ":<",
+ :<= => ":<=",
+ :<=> => ":<=>",
+ :== => ":==",
+ :=== => ":===",
+ :=~ => ":=~",
+ :> => ":>",
+ :>= => ":>=",
+ :>> => ":>>",
+ :[] => ":[]",
+ :[]= => ":[]=",
+ :"\<\<" => ":\<\<",
+ :^ => ":^",
+ :"`" => ":`", # for emacs, and justice!
+ :~ => ":~",
+ :| => ":|",
+
+ :"!" => [":\"!\"", ":!" ],
+ :"!=" => [":\"!=\"", ":!="],
+ :"!~" => [":\"!~\"", ":!~"],
+ :"\$" => ":\"$\"", # for justice!
+ :"&&" => ":\"&&\"",
+ :"'" => ":\"\'\"",
+ :"," => ":\",\"",
+ :"." => ":\".\"",
+ :".." => ":\"..\"",
+ :"..." => ":\"...\"",
+ :":" => ":\":\"",
+ :"::" => ":\"::\"",
+ :";" => ":\";\"",
+ :"=" => ":\"=\"",
+ :"=>" => ":\"=>\"",
+ :"\?" => ":\"?\"", # rawr!
+ :"@" => ":\"@\"",
+ :"||" => ":\"||\"",
+ :"|||" => ":\"|||\"",
+ :"++" => ":\"++\"",
+
+ :"\"" => ":\"\\\"\"",
+ :"\"\"" => ":\"\\\"\\\"\"",
+
+ :"9" => ":\"9\"",
+ :"foo bar" => ":\"foo bar\"",
+ :"*foo" => ":\"*foo\"",
+ :"foo " => ":\"foo \"",
+ :" foo" => ":\" foo\"",
+ :" " => ":\" \"",
+ }
+
+ symbols.each do |input, expected|
+ expected = expected[1] if expected.is_a?(Array)
+ it "returns self as a symbol literal for #{expected}" do
+ input.inspect.should == expected
+ end
+ end
+end
diff --git a/spec/ruby/core/symbol/intern_spec.rb b/spec/ruby/core/symbol/intern_spec.rb
new file mode 100644
index 0000000000..ea04b87e8a
--- /dev/null
+++ b/spec/ruby/core/symbol/intern_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Symbol#intern" do
+ it "returns self" do
+ :foo.intern.should == :foo
+ end
+
+ it "returns a Symbol" do
+ :foo.intern.should be_kind_of(Symbol)
+ end
+end
diff --git a/spec/ruby/core/symbol/length_spec.rb b/spec/ruby/core/symbol/length_spec.rb
new file mode 100644
index 0000000000..27bee575ef
--- /dev/null
+++ b/spec/ruby/core/symbol/length_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/length'
+
+describe "Symbol#length" do
+ it_behaves_like :symbol_length, :length
+end
diff --git a/spec/ruby/core/symbol/match_spec.rb b/spec/ruby/core/symbol/match_spec.rb
new file mode 100644
index 0000000000..c26d0569ed
--- /dev/null
+++ b/spec/ruby/core/symbol/match_spec.rb
@@ -0,0 +1,70 @@
+require_relative '../../spec_helper'
+
+describe :symbol_match, shared: true do
+ it "returns the index of the beginning of the match" do
+ :abc.send(@method, /b/).should == 1
+ end
+
+ it "returns nil if there is no match" do
+ :a.send(@method, /b/).should be_nil
+ end
+
+ it "sets the last match pseudo-variables" do
+ :a.send(@method, /(.)/).should == 0
+ $1.should == "a"
+ end
+end
+
+describe "Symbol#=~" do
+ it_behaves_like :symbol_match, :=~
+end
+
+ruby_version_is ""..."2.4" do
+ describe "Symbol#match" do
+ it_behaves_like :symbol_match, :match
+ end
+end
+
+ruby_version_is "2.4" do
+ describe "Symbol#match" do
+ it "returns the MatchData" do
+ result = :abc.match(/b/)
+ result.should be_kind_of(MatchData)
+ result[0].should == 'b'
+ end
+
+ it "returns nil if there is no match" do
+ :a.match(/b/).should be_nil
+ end
+
+ it "sets the last match pseudo-variables" do
+ :a.match(/(.)/)[0].should == 'a'
+ $1.should == "a"
+ end
+ end
+end
+
+ruby_version_is "2.4" do
+ describe "Symbol#match?" do
+ before :each do
+ # Resetting Regexp.last_match
+ /DONTMATCH/.match ''
+ end
+
+ context "when matches the given regex" do
+ it "returns true but does not set Regexp.last_match" do
+ :string.match?(/string/i).should be_true
+ Regexp.last_match.should be_nil
+ end
+ end
+
+ it "returns false when does not match the given regex" do
+ :string.match?(/STRING/).should be_false
+ end
+
+ it "takes matching position as the 2nd argument" do
+ :string.match?(/str/i, 0).should be_true
+ :string.match?(/str/i, 1).should be_false
+ end
+ end
+end
diff --git a/spec/ruby/core/symbol/next_spec.rb b/spec/ruby/core/symbol/next_spec.rb
new file mode 100644
index 0000000000..97fe913739
--- /dev/null
+++ b/spec/ruby/core/symbol/next_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/succ'
+
+describe "Symbol#next" do
+ it_behaves_like :symbol_succ, :next
+end
diff --git a/spec/ruby/core/symbol/shared/id2name.rb b/spec/ruby/core/symbol/shared/id2name.rb
new file mode 100644
index 0000000000..47f97bd332
--- /dev/null
+++ b/spec/ruby/core/symbol/shared/id2name.rb
@@ -0,0 +1,9 @@
+describe :symbol_id2name, shared: true do
+ it "returns the string corresponding to self" do
+ :rubinius.send(@method).should == "rubinius"
+ :squash.send(@method).should == "squash"
+ :[].send(@method).should == "[]"
+ :@ruby.send(@method).should == "@ruby"
+ :@@ruby.send(@method).should == "@@ruby"
+ end
+end
diff --git a/spec/ruby/core/symbol/shared/length.rb b/spec/ruby/core/symbol/shared/length.rb
new file mode 100644
index 0000000000..692e8c57e3
--- /dev/null
+++ b/spec/ruby/core/symbol/shared/length.rb
@@ -0,0 +1,23 @@
+# -*- encoding: utf-8 -*-
+
+describe :symbol_length, shared: true do
+ it "returns 0 for empty name" do
+ :''.send(@method).should == 0
+ end
+
+ it "returns 1 for name formed by a NUL character" do
+ :"\x00".send(@method).should == 1
+ end
+
+ it "returns 3 for name formed by 3 ASCII characters" do
+ :one.send(@method).should == 3
+ end
+
+ it "returns 4 for name formed by 4 ASCII characters" do
+ :four.send(@method).should == 4
+ end
+
+ it "returns 4 for name formed by 1 multibyte and 3 ASCII characters" do
+ :"\xC3\x9Cber".send(@method).should == 4
+ end
+end
diff --git a/spec/ruby/core/symbol/shared/slice.rb b/spec/ruby/core/symbol/shared/slice.rb
new file mode 100644
index 0000000000..80e1fd3ddc
--- /dev/null
+++ b/spec/ruby/core/symbol/shared/slice.rb
@@ -0,0 +1,278 @@
+require_relative '../fixtures/classes'
+
+describe :symbol_slice, shared: true do
+ describe "with an Integer index" do
+ it "returns the character code of the element at the index" do
+ :symbol.send(@method, 1).should == ?y
+ end
+
+ it "returns nil if the index starts from the end and is greater than the length" do
+ :symbol.send(@method, -10).should be_nil
+ end
+
+ it "returns nil if the index is greater than the length" do
+ :symbol.send(@method, 42).should be_nil
+ end
+ end
+
+ describe "with an Integer index and length" do
+ describe "and a positive index and length" do
+ it "returns a slice" do
+ :symbol.send(@method, 1,3).should == "ymb"
+ end
+
+ it "returns a blank slice if the length is 0" do
+ :symbol.send(@method, 0,0).should == ""
+ :symbol.send(@method, 1,0).should == ""
+ end
+
+ it "returns a slice of all remaining characters if the given length is greater than the actual length" do
+ :symbol.send(@method, 1,100).should == "ymbol"
+ end
+
+ it "returns nil if the index is greater than the length" do
+ :symbol.send(@method, 10,1).should be_nil
+ end
+ end
+
+ describe "and a positive index and negative length" do
+ it "returns nil" do
+ :symbol.send(@method, 0,-1).should be_nil
+ :symbol.send(@method, 1,-1).should be_nil
+ end
+ end
+
+ describe "and a negative index and positive length" do
+ it "returns a slice starting from the end upto the length" do
+ :symbol.send(@method, -3,2).should == "bo"
+ end
+
+ it "returns a blank slice if the length is 0" do
+ :symbol.send(@method, -1,0).should == ""
+ end
+
+ it "returns a slice of all remaining characters if the given length is larger than the actual length" do
+ :symbol.send(@method, -4,100).should == "mbol"
+ end
+
+ it "returns nil if the index is past the start" do
+ :symbol.send(@method, -10,1).should be_nil
+ end
+ end
+
+ describe "and a negative index and negative length" do
+ it "returns nil" do
+ :symbol.send(@method, -1,-1).should be_nil
+ end
+ end
+
+ describe "and a Float length" do
+ it "converts the length to an Integer" do
+ :symbol.send(@method, 2,2.5).should == "mb"
+ end
+ end
+
+ describe "and a nil length" do
+ it "raises a TypeError" do
+ lambda { :symbol.send(@method, 1,nil) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "and a length that cannot be converted into an Integer" do
+ it "raises a TypeError when given an Array" do
+ lambda { :symbol.send(@method, 1,Array.new) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when given an Hash" do
+ lambda { :symbol.send(@method, 1,Hash.new) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when given an Object" do
+ lambda { :symbol.send(@method, 1,Object.new) }.should raise_error(TypeError)
+ end
+ end
+ end
+
+ describe "with a Float index" do
+ it "converts the index to an Integer" do
+ :symbol.send(@method, 1.5).should == ?y
+ end
+ end
+
+ describe "with a nil index" do
+ it "raises a TypeError" do
+ lambda { :symbol.send(@method, nil) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "with an index that cannot be converted into an Integer" do
+ it "raises a TypeError when given an Array" do
+ lambda { :symbol.send(@method, Array.new) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when given an Hash" do
+ lambda { :symbol.send(@method, Hash.new) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when given an Object" do
+ lambda { :symbol.send(@method, Object.new) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "with a Range slice" do
+ describe "that is within bounds" do
+ it "returns a slice if both range values begin at the start and are within bounds" do
+ :symbol.send(@method, 1..4).should == "ymbo"
+ end
+
+ it "returns a slice if the first range value begins at the start and the last begins at the end" do
+ :symbol.send(@method, 1..-1).should == "ymbol"
+ end
+
+ it "returns a slice if the first range value begins at the end and the last begins at the end" do
+ :symbol.send(@method, -4..-1).should == "mbol"
+ end
+ end
+
+ describe "that is out of bounds" do
+ it "returns nil if the first range value begins past the end" do
+ :symbol.send(@method, 10..12).should be_nil
+ end
+
+ it "returns a blank string if the first range value is within bounds and the last range value is not" do
+ :symbol.send(@method, -2..-10).should == ""
+ :symbol.send(@method, 2..-10).should == ""
+ end
+
+ it "returns nil if the first range value starts from the end and is within bounds and the last value starts from the end and is greater than the length" do
+ :symbol.send(@method, -10..-12).should be_nil
+ end
+
+ it "returns nil if the first range value starts from the end and is out of bounds and the last value starts from the end and is less than the length" do
+ :symbol.send(@method, -10..-2).should be_nil
+ end
+ end
+
+ describe "with Float values" do
+ it "converts the first value to an Integer" do
+ :symbol.send(@method, 0.5..2).should == "sym"
+ end
+
+ it "converts the last value to an Integer" do
+ :symbol.send(@method, 0..2.5).should == "sym"
+ end
+ end
+ end
+
+ describe "with a Range subclass slice" do
+ it "returns a slice" do
+ range = SymbolSpecs::MyRange.new(1, 4)
+ :symbol.send(@method, range).should == "ymbo"
+ end
+ end
+
+ describe "with a Regex slice" do
+ describe "without a capture index" do
+ it "returns a string of the match" do
+ :symbol.send(@method, /[^bol]+/).should == "sym"
+ end
+
+ it "returns nil if the expression does not match" do
+ :symbol.send(@method, /0-9/).should be_nil
+ end
+
+ it "sets $~ to the MatchData if there is a match" do
+ :symbol.send(@method, /[^bol]+/)
+ $~[0].should == "sym"
+ end
+
+ it "does not set $~ if there if there is not a match" do
+ :symbol.send(@method, /[0-9]+/)
+ $~.should be_nil
+ end
+
+ it "returns a tainted string if the regexp is tainted" do
+ :symbol.send(@method, /./.taint).tainted?.should be_true
+ end
+
+ it "returns an untrusted string if the regexp is untrusted" do
+ :symbol.send(@method, /./.untrust).untrusted?.should be_true
+ end
+ end
+
+ describe "with a capture index" do
+ it "returns a string of the complete match if the capture index is 0" do
+ :symbol.send(@method, /(sy)(mb)(ol)/, 0).should == "symbol"
+ end
+
+ it "returns a string for the matched capture at the given index" do
+ :symbol.send(@method, /(sy)(mb)(ol)/, 1).should == "sy"
+ :symbol.send(@method, /(sy)(mb)(ol)/, -1).should == "ol"
+ end
+
+ it "returns nil if there is no capture for the index" do
+ :symbol.send(@method, /(sy)(mb)(ol)/, 4).should be_nil
+ :symbol.send(@method, /(sy)(mb)(ol)/, -4).should be_nil
+ end
+
+ it "converts the index to an Integer" do
+ :symbol.send(@method, /(sy)(mb)(ol)/, 1.5).should == "sy"
+ end
+
+ it "returns a tainted string if the regexp is tainted" do
+ :symbol.send(@method, /(.)/.taint, 1).tainted?.should be_true
+ end
+
+ it "returns an untrusted string if the regexp is untrusted" do
+ :symbol.send(@method, /(.)/.untrust, 1).untrusted?.should be_true
+ end
+
+ describe "and an index that cannot be converted to an Integer" do
+ it "raises a TypeError when given an Hash" do
+ lambda { :symbol.send(@method, /(sy)(mb)(ol)/, Hash.new) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when given an Array" do
+ lambda { :symbol.send(@method, /(sy)(mb)(ol)/, Array.new) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when given an Object" do
+ lambda { :symbol.send(@method, /(sy)(mb)(ol)/, Object.new) }.should raise_error(TypeError)
+ end
+ end
+
+ it "raises a TypeError if the index is nil" do
+ lambda { :symbol.send(@method, /(sy)(mb)(ol)/, nil) }.should raise_error(TypeError)
+ end
+
+ it "sets $~ to the MatchData if there is a match" do
+ :symbol.send(@method, /(sy)(mb)(ol)/, 0)
+ $~[0].should == "symbol"
+ $~[1].should == "sy"
+ $~[2].should == "mb"
+ $~[3].should == "ol"
+ end
+
+ it "does not set $~ to the MatchData if there is not a match" do
+ :symbol.send(@method, /0-9/, 0)
+ $~.should be_nil
+ end
+ end
+ end
+
+ describe "with a String slice" do
+ it "does not set $~" do
+ $~ = nil
+ :symbol.send(@method, "sym")
+ $~.should be_nil
+ end
+
+ it "returns a string if there is match" do
+ :symbol.send(@method, "ymb").should == "ymb"
+ end
+
+ it "returns nil if there is not a match" do
+ :symbol.send(@method, "foo").should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/symbol/shared/succ.rb b/spec/ruby/core/symbol/shared/succ.rb
new file mode 100644
index 0000000000..dde298207e
--- /dev/null
+++ b/spec/ruby/core/symbol/shared/succ.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+
+describe :symbol_succ, shared: true do
+ it "returns a successor" do
+ :abcd.send(@method).should == :abce
+ :THX1138.send(@method).should == :THX1139
+ end
+
+ it "propagates a 'carry'" do
+ :"1999zzz".send(@method).should == :"2000aaa"
+ :ZZZ9999.send(@method).should == :AAAA0000
+ end
+
+ it "increments non-alphanumeric characters when no alphanumeric characters are present" do
+ :"<<koala>>".send(@method).should == :"<<koalb>>"
+ :"***".send(@method).should == :"**+"
+ end
+end
diff --git a/spec/ruby/core/symbol/size_spec.rb b/spec/ruby/core/symbol/size_spec.rb
new file mode 100644
index 0000000000..5e2aa8d4d2
--- /dev/null
+++ b/spec/ruby/core/symbol/size_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/length'
+
+describe "Symbol#size" do
+ it_behaves_like :symbol_length, :size
+end
diff --git a/spec/ruby/core/symbol/slice_spec.rb b/spec/ruby/core/symbol/slice_spec.rb
new file mode 100644
index 0000000000..d2421c474c
--- /dev/null
+++ b/spec/ruby/core/symbol/slice_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/slice'
+
+describe "Symbol#slice" do
+ it_behaves_like :symbol_slice, :slice
+end
diff --git a/spec/ruby/core/symbol/succ_spec.rb b/spec/ruby/core/symbol/succ_spec.rb
new file mode 100644
index 0000000000..694bfff862
--- /dev/null
+++ b/spec/ruby/core/symbol/succ_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/succ'
+
+describe "Symbol#succ" do
+ it_behaves_like :symbol_succ, :succ
+end
diff --git a/spec/ruby/core/symbol/swapcase_spec.rb b/spec/ruby/core/symbol/swapcase_spec.rb
new file mode 100644
index 0000000000..9aec87a6d2
--- /dev/null
+++ b/spec/ruby/core/symbol/swapcase_spec.rb
@@ -0,0 +1,41 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe "Symbol#swapcase" do
+ it "returns a Symbol" do
+ :glark.swapcase.should be_an_instance_of(Symbol)
+ end
+
+ it "converts lowercase ASCII characters to their uppercase equivalents" do
+ :lower.swapcase.should == :LOWER
+ end
+
+ it "converts uppercase ASCII characters to their lowercase equivalents" do
+ :UPPER.swapcase.should == :upper
+ end
+
+ it "works with both upper- and lowercase ASCII characters in the same Symbol" do
+ :mIxEd.swapcase.should == :MiXeD
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "leaves uppercase Unicode characters as they were" do
+ "\u{00DE}Bc".to_sym.swapcase.should == :"ÞbC"
+ end
+
+ it "leaves lowercase Unicode characters as they were" do
+ "\u{00DF}Bc".to_sym.swapcase.should == :"ßbC"
+ end
+ end
+
+ ruby_version_is '2.4' do
+ it "swaps the case for Unicode characters" do
+ "äÖü".to_sym.swapcase.should == :"ÄöÜ"
+ "aOu".to_sym.swapcase.should == :"AoU"
+ end
+ end
+
+ it "leaves non-alphabetic ASCII characters as they were" do
+ "Glark?!?".to_sym.swapcase.should == :"gLARK?!?"
+ end
+end
diff --git a/spec/ruby/core/symbol/symbol_spec.rb b/spec/ruby/core/symbol/symbol_spec.rb
new file mode 100644
index 0000000000..56ce6f4a42
--- /dev/null
+++ b/spec/ruby/core/symbol/symbol_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+describe "Symbol" do
+ it "includes Comparable" do
+ Symbol.include?(Comparable).should == true
+ end
+
+ it ".allocate raises a TypeError" do
+ lambda do
+ Symbol.allocate
+ end.should raise_error(TypeError)
+ end
+
+ it ".new is undefined" do
+ lambda do
+ Symbol.new
+ end.should raise_error(NoMethodError)
+ end
+end
diff --git a/spec/ruby/core/symbol/to_proc_spec.rb b/spec/ruby/core/symbol/to_proc_spec.rb
new file mode 100644
index 0000000000..8cf00b085f
--- /dev/null
+++ b/spec/ruby/core/symbol/to_proc_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+
+describe "Symbol#to_proc" do
+ it "returns a new Proc" do
+ proc = :to_s.to_proc
+ proc.should be_kind_of(Proc)
+ end
+
+ it "sends self to arguments passed when calling #call on the Proc" do
+ obj = mock("Receiving #to_s")
+ obj.should_receive(:to_s).and_return("Received #to_s")
+ :to_s.to_proc.call(obj).should == "Received #to_s"
+ end
+
+ it "raises an ArgumentError when calling #call on the Proc without receiver" do
+ lambda { :object_id.to_proc.call }.should raise_error(ArgumentError)
+ end
+
+ it "produces a proc that always returns [[:rest]] for #parameters" do
+ pr = :to_s.to_proc
+ pr.parameters.should == [[:rest]]
+ end
+end
+
+describe "Symbol#to_proc" do
+ before :all do
+ @klass = Class.new do
+ def m
+ yield
+ end
+
+ def to_proc
+ :m.to_proc.call(self) { :value }
+ end
+ end
+ end
+
+ it "passes along the block passed to Proc#call" do
+ @klass.new.to_proc.should == :value
+ end
+end
diff --git a/spec/ruby/core/symbol/to_s_spec.rb b/spec/ruby/core/symbol/to_s_spec.rb
new file mode 100644
index 0000000000..cd963faa28
--- /dev/null
+++ b/spec/ruby/core/symbol/to_s_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/id2name'
+
+describe "Symbol#to_s" do
+ it_behaves_like :symbol_id2name, :to_s
+end
diff --git a/spec/ruby/core/symbol/to_sym_spec.rb b/spec/ruby/core/symbol/to_sym_spec.rb
new file mode 100644
index 0000000000..e75f3d48a8
--- /dev/null
+++ b/spec/ruby/core/symbol/to_sym_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Symbol#to_sym" do
+ it "returns self" do
+ [:rubinius, :squash, :[], :@ruby, :@@ruby].each do |sym|
+ sym.to_sym.should == sym
+ end
+ end
+end
diff --git a/spec/ruby/core/symbol/upcase_spec.rb b/spec/ruby/core/symbol/upcase_spec.rb
new file mode 100644
index 0000000000..6183f3b754
--- /dev/null
+++ b/spec/ruby/core/symbol/upcase_spec.rb
@@ -0,0 +1,29 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+
+describe "Symbol#upcase" do
+ it "returns a Symbol" do
+ :glark.upcase.should be_an_instance_of(Symbol)
+ end
+
+ it "converts lowercase ASCII characters to their uppercase equivalents" do
+ :lOwEr.upcase.should == :LOWER
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "leaves lowercase Unicode characters as they were" do
+ "\u{E0}Bc".to_sym.upcase.should == :"àBC"
+ end
+ end
+
+ ruby_version_is '2.4' do
+ it "capitalizes all Unicode characters" do
+ "äöü".to_sym.upcase.should == :"ÄÖÜ"
+ "aou".to_sym.upcase.should == :"AOU"
+ end
+ end
+
+ it "leaves non-alphabetic ASCII characters as they were" do
+ "Glark?!?".to_sym.upcase.should == :"GLARK?!?"
+ end
+end
diff --git a/spec/ruby/core/systemexit/initialize_spec.rb b/spec/ruby/core/systemexit/initialize_spec.rb
new file mode 100644
index 0000000000..2cebaa7993
--- /dev/null
+++ b/spec/ruby/core/systemexit/initialize_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+
+describe "SystemExit#initialize" do
+ it "accepts a status" do
+ s = SystemExit.new 1
+ s.status.should == 1
+ s.message.should == 'SystemExit'
+ end
+
+ it "accepts a message" do
+ s = SystemExit.new 'message'
+ s.status.should == 0
+ s.message.should == 'message'
+ end
+
+ it "accepts a status and message" do
+ s = SystemExit.new 10, 'message'
+ s.status.should == 10
+ s.message.should == 'message'
+ end
+
+ it "sets the status to 0 by default" do
+ s = SystemExit.new
+ s.status.should == 0
+ end
+end
diff --git a/spec/ruby/core/systemexit/success_spec.rb b/spec/ruby/core/systemexit/success_spec.rb
new file mode 100644
index 0000000000..6d36509c80
--- /dev/null
+++ b/spec/ruby/core/systemexit/success_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+describe "SystemExit#success?" do
+ it "returns true when the status is 0" do
+ s = SystemExit.new 0
+ s.success?.should == true
+ end
+
+ it "returns false when the status is not 0" do
+ s = SystemExit.new 1
+ s.success?.should == false
+ end
+end
diff --git a/spec/ruby/core/thread/abort_on_exception_spec.rb b/spec/ruby/core/thread/abort_on_exception_spec.rb
new file mode 100644
index 0000000000..88de77b1a8
--- /dev/null
+++ b/spec/ruby/core/thread/abort_on_exception_spec.rb
@@ -0,0 +1,106 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread#abort_on_exception" do
+ before do
+ ThreadSpecs.clear_state
+ @thread = Thread.new { Thread.pass until ThreadSpecs.state == :exit }
+ end
+
+ after do
+ ThreadSpecs.state = :exit
+ @thread.join
+ end
+
+ it "is false by default" do
+ @thread.abort_on_exception.should be_false
+ end
+
+ it "returns true when #abort_on_exception= is passed true" do
+ @thread.abort_on_exception = true
+ @thread.abort_on_exception.should be_true
+ end
+end
+
+describe :thread_abort_on_exception, shared: true do
+ before do
+ @thread = Thread.new do
+ Thread.pass until ThreadSpecs.state == :run
+ raise RuntimeError, "Thread#abort_on_exception= specs"
+ end
+ end
+
+ it "causes the main thread to raise the exception raised in the thread" do
+ begin
+ ScratchPad << :before
+
+ @thread.abort_on_exception = true if @object
+ lambda do
+ ThreadSpecs.state = :run
+ # Wait for the main thread to be interrupted
+ sleep
+ end.should raise_error(RuntimeError, "Thread#abort_on_exception= specs")
+
+ ScratchPad << :after
+ rescue Exception => e
+ ScratchPad << [:rescue, e]
+ end
+
+ ScratchPad.recorded.should == [:before, :after]
+ end
+end
+
+describe "Thread#abort_on_exception=" do
+ describe "when enabled and the thread dies due to an exception" do
+ before do
+ ScratchPad.record []
+ ThreadSpecs.clear_state
+ @stderr, $stderr = $stderr, IOStub.new
+ end
+
+ after do
+ $stderr = @stderr
+ end
+
+ it_behaves_like :thread_abort_on_exception, nil, true
+ end
+end
+
+describe "Thread.abort_on_exception" do
+ before do
+ @abort_on_exception = Thread.abort_on_exception
+ end
+
+ after do
+ Thread.abort_on_exception = @abort_on_exception
+ end
+
+ it "is false by default" do
+ Thread.abort_on_exception.should == false
+ end
+
+ it "returns true when .abort_on_exception= is passed true" do
+ Thread.abort_on_exception = true
+ Thread.abort_on_exception.should be_true
+ end
+end
+
+describe "Thread.abort_on_exception=" do
+ describe "when enabled and a non-main thread dies due to an exception" do
+ before :each do
+ ScratchPad.record []
+ ThreadSpecs.clear_state
+ @stderr, $stderr = $stderr, IOStub.new
+
+ @abort_on_exception = Thread.abort_on_exception
+ Thread.abort_on_exception = true
+ end
+
+ after :each do
+ Thread.abort_on_exception = @abort_on_exception
+ $stderr = @stderr
+ end
+
+ it_behaves_like :thread_abort_on_exception, nil, false
+ end
+end
diff --git a/spec/ruby/core/thread/add_trace_func_spec.rb b/spec/ruby/core/thread/add_trace_func_spec.rb
new file mode 100644
index 0000000000..0abae81a78
--- /dev/null
+++ b/spec/ruby/core/thread/add_trace_func_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Thread#add_trace_func" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/thread/alive_spec.rb b/spec/ruby/core/thread/alive_spec.rb
new file mode 100644
index 0000000000..d4ba149d87
--- /dev/null
+++ b/spec/ruby/core/thread/alive_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread#alive?" do
+ it "can check it's own status" do
+ ThreadSpecs.status_of_current_thread.alive?.should == true
+ end
+
+ it "describes a running thread" do
+ ThreadSpecs.status_of_running_thread.alive?.should == true
+ end
+
+ it "describes a sleeping thread" do
+ ThreadSpecs.status_of_sleeping_thread.alive?.should == true
+ end
+
+ it "describes a blocked thread" do
+ ThreadSpecs.status_of_blocked_thread.alive?.should == true
+ end
+
+ it "describes a completed thread" do
+ ThreadSpecs.status_of_completed_thread.alive?.should == false
+ end
+
+ it "describes a killed thread" do
+ ThreadSpecs.status_of_killed_thread.alive?.should == false
+ end
+
+ it "describes a thread with an uncaught exception" do
+ ThreadSpecs.status_of_thread_with_uncaught_exception.alive?.should == false
+ end
+
+ it "describes a dying running thread" do
+ ThreadSpecs.status_of_dying_running_thread.alive?.should == true
+ end
+
+ it "describes a dying sleeping thread" do
+ ThreadSpecs.status_of_dying_sleeping_thread.alive?.should == true
+ end
+
+ it "returns true for a killed but still running thread" do
+ exit = false
+ t = Thread.new do
+ begin
+ sleep
+ ensure
+ Thread.pass until exit
+ end
+ end
+
+ ThreadSpecs.spin_until_sleeping(t)
+
+ t.kill
+ t.alive?.should == true
+ exit = true
+ t.join
+ end
+end
diff --git a/spec/ruby/core/thread/allocate_spec.rb b/spec/ruby/core/thread/allocate_spec.rb
new file mode 100644
index 0000000000..cd9aa1ee43
--- /dev/null
+++ b/spec/ruby/core/thread/allocate_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Thread.allocate" do
+ it "raises a TypeError" do
+ lambda {
+ Thread.allocate
+ }.should raise_error(TypeError)
+ 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
new file mode 100644
index 0000000000..d38659c257
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe 'Thread::Backtrace::Location#absolute_path' do
+ before :each do
+ @frame = ThreadBacktraceLocationSpecs.locations[0]
+ end
+
+ it 'returns the absolute path of the call frame' do
+ @frame.absolute_path.should == File.realpath(__FILE__)
+ end
+
+ context "when used in eval with a given filename" do
+ it "returns filename" do
+ code = "caller_locations(0)[0].absolute_path"
+ eval(code, nil, "foo.rb").should == "foo.rb"
+ eval(code, nil, "foo/bar.rb").should == "foo/bar.rb"
+ end
+ end
+
+ context "when used in #method_added" do
+ it "returns the user filename that defined the method" do
+ path = fixture(__FILE__, "absolute_path_method_added.rb")
+ load path
+ locations = ScratchPad.recorded
+ locations[0].absolute_path.should == path
+ # Make sure it's from the class body, not from the file top-level
+ locations[0].label.should include 'MethodAddedAbsolutePath'
+ end
+ end
+
+ platform_is_not :windows do
+ before :each do
+ @file = fixture(__FILE__, "absolute_path.rb")
+ @symlink = tmp("symlink.rb")
+ File.symlink(@file, @symlink)
+ ScratchPad.record []
+ end
+
+ after :each do
+ rm_r @symlink
+ end
+
+ it "returns a canonical path without symlinks, even when __FILE__ does not" do
+ realpath = File.realpath(@symlink)
+ realpath.should_not == @symlink
+
+ load @symlink
+ ScratchPad.recorded.should == [@symlink, realpath]
+ end
+
+ it "returns a canonical path without symlinks, even when __FILE__ is removed" do
+ realpath = File.realpath(@symlink)
+ realpath.should_not == @symlink
+
+ ScratchPad << -> { rm_r(@symlink) }
+ load @symlink
+ ScratchPad.recorded.should == [@symlink, realpath]
+ end
+ end
+end
diff --git a/spec/ruby/core/thread/backtrace/location/base_label_spec.rb b/spec/ruby/core/thread/backtrace/location/base_label_spec.rb
new file mode 100644
index 0000000000..139a68e2c8
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace/location/base_label_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe 'Thread::Backtrace::Location#base_label' do
+ before :each do
+ @frame = ThreadBacktraceLocationSpecs.locations[0]
+ end
+
+ it 'returns the base label of the call frame' do
+ @frame.base_label.should == '<top (required)>'
+ end
+
+ describe 'when call frame is inside a block' do
+ before :each do
+ @frame = ThreadBacktraceLocationSpecs.block_location[0]
+ end
+
+ it 'returns the name of the method that contains the block' do
+ @frame.base_label.should == 'block_location'
+ end
+ end
+end
diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path.rb b/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path.rb
new file mode 100644
index 0000000000..875e97ffac
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path.rb
@@ -0,0 +1,4 @@
+action = ScratchPad.recorded.pop
+ScratchPad << __FILE__
+action.call if action
+ScratchPad << caller_locations(0)[0].absolute_path
diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path_method_added.rb b/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path_method_added.rb
new file mode 100644
index 0000000000..26d6298a19
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path_method_added.rb
@@ -0,0 +1,10 @@
+module ThreadBacktraceLocationSpecs
+ class MethodAddedAbsolutePath
+ def self.method_added(name)
+ ScratchPad.record caller_locations
+ end
+
+ def foo
+ end
+ end
+end
diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/classes.rb b/spec/ruby/core/thread/backtrace/location/fixtures/classes.rb
new file mode 100644
index 0000000000..3e42d8cf81
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace/location/fixtures/classes.rb
@@ -0,0 +1,17 @@
+module ThreadBacktraceLocationSpecs
+ MODULE_LOCATION = caller_locations(0) rescue nil
+
+ def self.locations
+ caller_locations
+ end
+
+ def self.method_location
+ caller_locations(0)
+ end
+
+ def self.block_location
+ 1.times do
+ return caller_locations(0)
+ end
+ end
+end
diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/main.rb b/spec/ruby/core/thread/backtrace/location/fixtures/main.rb
new file mode 100644
index 0000000000..d2d14ac957
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace/location/fixtures/main.rb
@@ -0,0 +1,5 @@
+def example
+ caller_locations[0].path
+end
+
+print example
diff --git a/spec/ruby/core/thread/backtrace/location/inspect_spec.rb b/spec/ruby/core/thread/backtrace/location/inspect_spec.rb
new file mode 100644
index 0000000000..20e477a5a6
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace/location/inspect_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe 'Thread::Backtrace::Location#inspect' do
+ before :each do
+ @frame = ThreadBacktraceLocationSpecs.locations[0]
+ @line = __LINE__ - 1
+ end
+
+ it 'converts the call frame to a String' do
+ @frame.inspect.should include("#{__FILE__}:#{@line}:in ")
+ end
+end
diff --git a/spec/ruby/core/thread/backtrace/location/label_spec.rb b/spec/ruby/core/thread/backtrace/location/label_spec.rb
new file mode 100644
index 0000000000..be8da5646f
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace/location/label_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe 'Thread::Backtrace::Location#label' do
+ it 'returns the base label of the call frame' do
+ ThreadBacktraceLocationSpecs.locations[0].label.should include('<top (required)>')
+ end
+
+ it 'returns the method name for a method location' do
+ ThreadBacktraceLocationSpecs.method_location[0].label.should == "method_location"
+ end
+
+ it 'returns the block name for a block location' do
+ ThreadBacktraceLocationSpecs.block_location[0].label.should == "block in block_location"
+ end
+
+ it 'returns the module name for a module location' do
+ ThreadBacktraceLocationSpecs::MODULE_LOCATION[0].label.should include "ThreadBacktraceLocationSpecs"
+ end
+end
diff --git a/spec/ruby/core/thread/backtrace/location/lineno_spec.rb b/spec/ruby/core/thread/backtrace/location/lineno_spec.rb
new file mode 100644
index 0000000000..dc93d32d75
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace/location/lineno_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe 'Thread::Backtrace::Location#lineno' do
+ before :each do
+ @frame = ThreadBacktraceLocationSpecs.locations[0]
+ @line = __LINE__ - 1
+ end
+
+ it 'returns the absolute path of the call frame' do
+ @frame.lineno.should == @line
+ end
+end
diff --git a/spec/ruby/core/thread/backtrace/location/path_spec.rb b/spec/ruby/core/thread/backtrace/location/path_spec.rb
new file mode 100644
index 0000000000..b1a3439747
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace/location/path_spec.rb
@@ -0,0 +1,89 @@
+require_relative '../../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe 'Thread::Backtrace::Location#path' do
+ context 'outside a main script' do
+ it 'returns an absolute path' do
+ frame = ThreadBacktraceLocationSpecs.locations[0]
+
+ frame.path.should == __FILE__
+ end
+ end
+
+ context 'in a main script' do
+ before do
+ @script = fixture(__FILE__, 'main.rb')
+ end
+
+ context 'when the script is in the working directory' do
+ before do
+ @directory = File.dirname(@script)
+ end
+
+ context 'when using a relative script path' do
+ it 'returns a path relative to the working directory' do
+ Dir.chdir(@directory) {
+ ruby_exe('main.rb')
+ }.should == 'main.rb'
+ end
+ end
+
+ context 'when using an absolute script path' do
+ it 'returns an absolute path' do
+ Dir.chdir(@directory) {
+ ruby_exe(@script)
+ }.should == @script
+ end
+ end
+ end
+
+ context 'when the script is in a sub directory of the working directory' do
+ context 'when using a relative script path' do
+ it 'returns a path relative to the working directory' do
+ path = 'fixtures/main.rb'
+ directory = File.dirname(__FILE__)
+ Dir.chdir(directory) {
+ ruby_exe(path)
+ }.should == path
+ end
+ end
+
+ context 'when using an absolute script path' do
+ it 'returns an absolute path' do
+ ruby_exe(@script).should == @script
+ end
+ end
+ end
+
+ context 'when the script is outside of the working directory' do
+ before :each do
+ @parent_dir = tmp('path_outside_pwd')
+ @sub_dir = File.join(@parent_dir, 'sub')
+ @script = File.join(@parent_dir, 'main.rb')
+ source = fixture(__FILE__, 'main.rb')
+
+ mkdir_p(@sub_dir)
+
+ cp(source, @script)
+ end
+
+ after :each do
+ rm_r(@parent_dir)
+ end
+
+ context 'when using a relative script path' do
+ it 'returns a path relative to the working directory' do
+ Dir.chdir(@sub_dir) {
+ ruby_exe('../main.rb')
+ }.should == '../main.rb'
+ end
+ end
+
+ context 'when using an absolute path' do
+ it 'returns an absolute path' do
+ ruby_exe(@script).should == @script
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/thread/backtrace/location/to_s_spec.rb b/spec/ruby/core/thread/backtrace/location/to_s_spec.rb
new file mode 100644
index 0000000000..5911cdced0
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace/location/to_s_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe 'Thread::Backtrace::Location#to_s' do
+ before :each do
+ @frame = ThreadBacktraceLocationSpecs.locations[0]
+ @line = __LINE__ - 1
+ end
+
+ it 'converts the call frame to a String' do
+ @frame.to_s.should include("#{__FILE__}:#{@line}:in ")
+ end
+end
diff --git a/spec/ruby/core/thread/backtrace_spec.rb b/spec/ruby/core/thread/backtrace_spec.rb
new file mode 100644
index 0000000000..84ed574d5c
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+
+describe "Thread#backtrace" do
+ it "returns the current backtrace of a thread" do
+ t = Thread.new do
+ begin
+ sleep
+ rescue
+ end
+ end
+
+ Thread.pass while t.status && t.status != 'sleep'
+
+ backtrace = t.backtrace
+ backtrace.should be_kind_of(Array)
+ backtrace.first.should =~ /`sleep'/
+
+ t.raise 'finish the thread'
+ t.join
+ end
+
+ it "returns nil for dead thread" do
+ t = Thread.new {}
+ t.join
+ t.backtrace.should == nil
+ end
+
+ it "returns an array (which may be empty) immediately after the thread is created" do
+ t = Thread.new { sleep }
+ backtrace = t.backtrace
+ t.kill
+ t.join
+ backtrace.should be_kind_of(Array)
+ end
+end
diff --git a/spec/ruby/core/thread/current_spec.rb b/spec/ruby/core/thread/current_spec.rb
new file mode 100644
index 0000000000..f5ed1d95cd
--- /dev/null
+++ b/spec/ruby/core/thread/current_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread.current" do
+ it "returns a thread" do
+ current = Thread.current
+ current.should be_kind_of(Thread)
+ end
+
+ it "returns the current thread" do
+ t = Thread.new { Thread.current }
+ t.value.should equal(t)
+ Thread.current.should_not equal(t.value)
+ end
+
+ it "returns the correct thread in a Fiber" do
+ # This catches a bug where Fibers are running on a thread-pool
+ # and Fibers from a different Ruby Thread reuse the same native thread.
+ # Caching the Ruby Thread based on the native thread is not correct in that case.
+ 2.times do
+ t = Thread.new {
+ cur = Thread.current
+ Fiber.new {
+ Thread.current
+ }.resume.should equal cur
+ cur
+ }
+ t.value.should equal t
+ end
+ end
+end
diff --git a/spec/ruby/core/thread/element_reference_spec.rb b/spec/ruby/core/thread/element_reference_spec.rb
new file mode 100644
index 0000000000..4de33e1456
--- /dev/null
+++ b/spec/ruby/core/thread/element_reference_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread#[]" do
+ it "gives access to thread local values" do
+ th = Thread.new do
+ Thread.current[:value] = 5
+ end
+ th.join
+ th[:value].should == 5
+ Thread.current[:value].should == nil
+ end
+
+ it "is not shared across threads" do
+ t1 = Thread.new do
+ Thread.current[:value] = 1
+ end
+ t2 = Thread.new do
+ Thread.current[:value] = 2
+ end
+ [t1,t2].each {|x| x.join}
+ t1[:value].should == 1
+ t2[:value].should == 2
+ end
+
+ it "is accessible using strings or symbols" do
+ t1 = Thread.new do
+ Thread.current[:value] = 1
+ end
+ t2 = Thread.new do
+ Thread.current["value"] = 2
+ end
+ [t1,t2].each {|x| x.join}
+ t1[:value].should == 1
+ t1["value"].should == 1
+ t2[:value].should == 2
+ t2["value"].should == 2
+ end
+
+ it "raises exceptions on the wrong type of keys" do
+ lambda { Thread.current[nil] }.should raise_error(TypeError)
+ lambda { Thread.current[5] }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/thread/element_set_spec.rb b/spec/ruby/core/thread/element_set_spec.rb
new file mode 100644
index 0000000000..c109468a5e
--- /dev/null
+++ b/spec/ruby/core/thread/element_set_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread#[]=" do
+ after :each do
+ Thread.current[:value] = nil
+ end
+
+ it "raises a #{frozen_error_class} if the thread is frozen" do
+ Thread.new do
+ th = Thread.current
+ th.freeze
+ -> {
+ th[:foo] = "bar"
+ }.should raise_error(frozen_error_class, /frozen/)
+ end.join
+ end
+
+ it "raises exceptions on the wrong type of keys" do
+ lambda { Thread.current[nil] = true }.should raise_error(TypeError)
+ lambda { Thread.current[5] = true }.should raise_error(TypeError)
+ end
+
+ it "is not shared across fibers" do
+ fib = Fiber.new do
+ Thread.current[:value] = 1
+ Fiber.yield
+ Thread.current[:value].should == 1
+ end
+ fib.resume
+ Thread.current[:value].should be_nil
+ Thread.current[:value] = 2
+ fib.resume
+ Thread.current[:value] = 2
+ end
+
+ it "stores a local in another thread when in a fiber" do
+ fib = Fiber.new do
+ t = Thread.new do
+ sleep
+ Thread.current[:value].should == 1
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t[:value] = 1
+ t.wakeup
+ t.join
+ end
+ fib.resume
+ end
+end
diff --git a/spec/ruby/core/thread/exclusive_spec.rb b/spec/ruby/core/thread/exclusive_spec.rb
new file mode 100644
index 0000000000..9de427fb52
--- /dev/null
+++ b/spec/ruby/core/thread/exclusive_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+
+describe "Thread.exclusive" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "yields to the block" do
+ Thread.exclusive { ScratchPad.record true }
+ ScratchPad.recorded.should == true
+ end
+
+ it "returns the result of yielding" do
+ Thread.exclusive { :result }.should == :result
+ end
+
+ it "blocks the caller if another thread is also in an exclusive block" do
+ m = Mutex.new
+ q1 = Queue.new
+ q2 = Queue.new
+
+ t = Thread.new {
+ Thread.exclusive {
+ q1.push :ready
+ q2.pop
+ }
+ }
+
+ q1.pop.should == :ready
+
+ lambda { Thread.exclusive { } }.should block_caller
+
+ q2.push :done
+ t.join
+ end
+
+ it "is not recursive" do
+ Thread.exclusive do
+ lambda { Thread.exclusive { } }.should raise_error(ThreadError)
+ end
+ end
+end
diff --git a/spec/ruby/core/thread/exit_spec.rb b/spec/ruby/core/thread/exit_spec.rb
new file mode 100644
index 0000000000..c3f710920e
--- /dev/null
+++ b/spec/ruby/core/thread/exit_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/exit'
+
+describe "Thread#exit!" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "Thread.exit" do
+ it "causes the current thread to exit" do
+ thread = Thread.new { Thread.exit; sleep }
+ thread.join
+ thread.status.should be_false
+ end
+end
diff --git a/spec/ruby/core/thread/fetch_spec.rb b/spec/ruby/core/thread/fetch_spec.rb
new file mode 100644
index 0000000000..d71c938880
--- /dev/null
+++ b/spec/ruby/core/thread/fetch_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '2.5' do
+ describe 'Thread#fetch' do
+ describe 'with 2 arguments' do
+ it 'returns the value of the fiber-local variable if value has been assigned' do
+ th = Thread.new { Thread.current[:cat] = 'meow' }
+ th.join
+ th.fetch(:cat, true).should == 'meow'
+ end
+
+ it "returns the default value if fiber-local variable hasn't been assigned" do
+ th = Thread.new {}
+ th.join
+ th.fetch(:cat, true).should == true
+ end
+ end
+
+ describe 'with 1 argument' do
+ it 'raises a KeyError when the Thread does not have a fiber-local variable of the same name' do
+ th = Thread.new {}
+ th.join
+ -> { th.fetch(:cat) }.should raise_error(KeyError)
+ end
+
+ it 'returns the value of the fiber-local variable if value has been assigned' do
+ th = Thread.new { Thread.current[:cat] = 'meow' }
+ th.join
+ th.fetch(:cat).should == 'meow'
+ end
+ end
+
+ it 'raises an ArgumentError when not passed one or two arguments' do
+ -> { Thread.current.fetch() }.should raise_error(ArgumentError)
+ -> { Thread.current.fetch(1, 2, 3) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/thread/fixtures/classes.rb b/spec/ruby/core/thread/fixtures/classes.rb
new file mode 100644
index 0000000000..601e515e3e
--- /dev/null
+++ b/spec/ruby/core/thread/fixtures/classes.rb
@@ -0,0 +1,303 @@
+unless defined? Channel
+ require 'thread'
+ class Channel < Queue
+ alias receive shift
+ end
+end
+
+module ThreadSpecs
+
+ class SubThread < Thread
+ def initialize(*args)
+ super { args.first << 1 }
+ end
+ end
+
+ class Status
+ attr_reader :thread, :inspect, :status
+ def initialize(thread)
+ @thread = thread
+ @alive = thread.alive?
+ @inspect = thread.inspect
+ @status = thread.status
+ @stop = thread.stop?
+ end
+
+ def alive?
+ @alive
+ end
+
+ def stop?
+ @stop
+ end
+ end
+
+ # TODO: In the great Thread spec rewrite, abstract this
+ class << self
+ attr_accessor :state
+ end
+
+ def self.clear_state
+ @state = nil
+ end
+
+ def self.spin_until_sleeping(t)
+ Thread.pass while t.status and t.status != "sleep"
+ end
+
+ def self.sleeping_thread
+ Thread.new do
+ begin
+ sleep
+ ScratchPad.record :woken
+ rescue Object => e
+ ScratchPad.record e
+ end
+ end
+ end
+
+ def self.running_thread
+ Thread.new do
+ begin
+ ThreadSpecs.state = :running
+ loop { Thread.pass }
+ ScratchPad.record :woken
+ rescue Object => e
+ ScratchPad.record e
+ end
+ end
+ end
+
+ def self.completed_thread
+ Thread.new {}
+ end
+
+ def self.status_of_current_thread
+ Thread.new { Status.new(Thread.current) }.value
+ end
+
+ def self.status_of_running_thread
+ t = running_thread
+ Thread.pass while t.status and t.status != "run"
+ status = Status.new t
+ t.kill
+ t.join
+ status
+ end
+
+ def self.status_of_completed_thread
+ t = completed_thread
+ t.join
+ Status.new t
+ end
+
+ def self.status_of_sleeping_thread
+ t = sleeping_thread
+ Thread.pass while t.status and t.status != 'sleep'
+ status = Status.new t
+ t.run
+ t.join
+ status
+ end
+
+ def self.status_of_blocked_thread
+ m = Mutex.new
+ m.lock
+ t = Thread.new { m.lock }
+ Thread.pass while t.status and t.status != 'sleep'
+ status = Status.new t
+ m.unlock
+ t.join
+ status
+ end
+
+ def self.status_of_killed_thread
+ t = Thread.new { sleep }
+ Thread.pass while t.status and t.status != 'sleep'
+ t.kill
+ t.join
+ Status.new t
+ end
+
+ def self.status_of_thread_with_uncaught_exception
+ t = Thread.new {
+ Thread.current.report_on_exception = false
+ raise "error"
+ }
+ begin
+ t.join
+ rescue RuntimeError
+ end
+ Status.new t
+ end
+
+ def self.status_of_dying_running_thread
+ status = nil
+ t = dying_thread_ensures { status = Status.new Thread.current }
+ t.join
+ status
+ end
+
+ def self.status_of_dying_sleeping_thread
+ t = dying_thread_ensures { Thread.stop; }
+ Thread.pass while t.status and t.status != 'sleep'
+ status = Status.new t
+ t.wakeup
+ t.join
+ status
+ end
+
+ def self.status_of_dying_thread_after_sleep
+ status = nil
+ t = dying_thread_ensures {
+ Thread.stop
+ status = Status.new(Thread.current)
+ }
+ Thread.pass while t.status and t.status != 'sleep'
+ t.wakeup
+ Thread.pass while t.status and t.status == 'sleep'
+ t.join
+ status
+ end
+
+ def self.dying_thread_ensures(kill_method_name=:kill)
+ Thread.new do
+ Thread.current.report_on_exception = false
+ begin
+ Thread.current.send(kill_method_name)
+ ensure
+ yield
+ end
+ end
+ end
+
+ def self.dying_thread_with_outer_ensure(kill_method_name=:kill)
+ Thread.new do
+ Thread.current.report_on_exception = false
+ begin
+ begin
+ Thread.current.send(kill_method_name)
+ ensure
+ raise "In dying thread"
+ end
+ ensure
+ yield
+ end
+ end
+ end
+
+ def self.join_dying_thread_with_outer_ensure(kill_method_name=:kill)
+ t = dying_thread_with_outer_ensure(kill_method_name) { yield }
+ lambda { t.join }.should raise_error(RuntimeError, "In dying thread")
+ return t
+ end
+
+ def self.wakeup_dying_sleeping_thread(kill_method_name=:kill)
+ t = ThreadSpecs.dying_thread_ensures(kill_method_name) { yield }
+ Thread.pass while t.status and t.status != 'sleep'
+ t.wakeup
+ t.join
+ end
+
+ def self.critical_is_reset
+ # Create another thread to verify that it can call Thread.critical=
+ t = Thread.new do
+ initial_critical = Thread.critical
+ Thread.critical = true
+ Thread.critical = false
+ initial_critical == false && Thread.critical == false
+ end
+ v = t.value
+ t.join
+ v
+ end
+
+ def self.counter
+ @@counter
+ end
+
+ def self.counter= c
+ @@counter = c
+ end
+
+ def self.increment_counter(incr)
+ incr.times do
+ begin
+ Thread.critical = true
+ @@counter += 1
+ ensure
+ Thread.critical = false
+ end
+ end
+ end
+
+ def self.critical_thread1
+ Thread.critical = true
+ Thread.current.key?(:thread_specs).should == false
+ end
+
+ def self.critical_thread2(is_thread_stop)
+ Thread.current[:thread_specs].should == 101
+ Thread.critical.should == !is_thread_stop
+ unless is_thread_stop
+ Thread.critical = false
+ end
+ end
+
+ def self.main_thread1(critical_thread, is_thread_sleep, is_thread_stop)
+ # Thread.stop resets Thread.critical. Also, with native threads, the Thread.Stop may not have executed yet
+ # since the main thread will race with the critical thread
+ unless is_thread_stop
+ Thread.critical.should == true
+ end
+ critical_thread[:thread_specs] = 101
+ if is_thread_sleep or is_thread_stop
+ # Thread#wakeup calls are not queued up. So we need to ensure that the thread is sleeping before calling wakeup
+ Thread.pass while critical_thread.status and critical_thread.status != "sleep"
+ critical_thread.wakeup
+ end
+ end
+
+ def self.main_thread2(critical_thread)
+ Thread.pass # The join below seems to cause a deadlock with CRuby unless Thread.pass is called first
+ critical_thread.join
+ Thread.critical.should == false
+ end
+
+ def self.critical_thread_yields_to_main_thread(is_thread_sleep=false, is_thread_stop=false)
+ @@after_first_sleep = false
+
+ critical_thread = Thread.new do
+ Thread.pass while Thread.main.status and Thread.main.status != "sleep"
+ critical_thread1()
+ Thread.main.wakeup
+ yield
+ Thread.pass while @@after_first_sleep != true # Need to ensure that the next statement does not see the first sleep itself
+ Thread.pass while Thread.main.status and Thread.main.status != "sleep"
+ critical_thread2(is_thread_stop)
+ Thread.main.wakeup
+ end
+
+ sleep 5
+ @@after_first_sleep = true
+ main_thread1(critical_thread, is_thread_sleep, is_thread_stop)
+ sleep 5
+ main_thread2(critical_thread)
+ end
+
+ def self.create_critical_thread
+ Thread.new do
+ Thread.critical = true
+ yield
+ Thread.critical = false
+ end
+ end
+
+ def self.create_and_kill_critical_thread(pass_after_kill=false)
+ ThreadSpecs.create_critical_thread do
+ Thread.current.kill
+ Thread.pass if pass_after_kill
+ ScratchPad.record("status=" + Thread.current.status)
+ end
+ end
+end
diff --git a/spec/ruby/core/thread/fork_spec.rb b/spec/ruby/core/thread/fork_spec.rb
new file mode 100644
index 0000000000..a2f4181298
--- /dev/null
+++ b/spec/ruby/core/thread/fork_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/start'
+
+describe "Thread.fork" do
+ describe "Thread.start" do
+ it_behaves_like :thread_start, :fork
+ end
+end
diff --git a/spec/ruby/core/thread/group_spec.rb b/spec/ruby/core/thread/group_spec.rb
new file mode 100644
index 0000000000..59f5ac37c8
--- /dev/null
+++ b/spec/ruby/core/thread/group_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+describe "Thread#group" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/thread/initialize_spec.rb b/spec/ruby/core/thread/initialize_spec.rb
new file mode 100644
index 0000000000..80e058a120
--- /dev/null
+++ b/spec/ruby/core/thread/initialize_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread#initialize" do
+
+ describe "already initialized" do
+
+ before do
+ @t = Thread.new { sleep }
+ end
+
+ after do
+ @t.kill
+ @t.join
+ end
+
+ it "raises a ThreadError" do
+ lambda {
+ @t.instance_eval do
+ initialize {}
+ end
+ }.should raise_error(ThreadError)
+ end
+
+ end
+
+end
diff --git a/spec/ruby/core/thread/inspect_spec.rb b/spec/ruby/core/thread/inspect_spec.rb
new file mode 100644
index 0000000000..4c635b7aaa
--- /dev/null
+++ b/spec/ruby/core/thread/inspect_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread#inspect" do
+ it "can check it's own status" do
+ ThreadSpecs.status_of_current_thread.inspect.should include('run')
+ end
+
+ it "describes a running thread" do
+ ThreadSpecs.status_of_running_thread.inspect.should include('run')
+ end
+
+ it "describes a sleeping thread" do
+ ThreadSpecs.status_of_sleeping_thread.inspect.should include('sleep')
+ end
+
+ it "describes a blocked thread" do
+ ThreadSpecs.status_of_blocked_thread.inspect.should include('sleep')
+ end
+
+ it "describes a completed thread" do
+ ThreadSpecs.status_of_completed_thread.inspect.should include('dead')
+ end
+
+ it "describes a killed thread" do
+ ThreadSpecs.status_of_killed_thread.inspect.should include('dead')
+ end
+
+ it "describes a thread with an uncaught exception" do
+ ThreadSpecs.status_of_thread_with_uncaught_exception.inspect.should include('dead')
+ end
+
+ it "describes a dying sleeping thread" do
+ ThreadSpecs.status_of_dying_sleeping_thread.inspect.should include('sleep')
+ end
+
+ it "reports aborting on a killed thread" do
+ ThreadSpecs.status_of_dying_running_thread.inspect.should include('aborting')
+ end
+
+ it "reports aborting on a killed thread after sleep" do
+ ThreadSpecs.status_of_dying_thread_after_sleep.inspect.should include('aborting')
+ end
+end
diff --git a/spec/ruby/core/thread/join_spec.rb b/spec/ruby/core/thread/join_spec.rb
new file mode 100644
index 0000000000..f60f91e436
--- /dev/null
+++ b/spec/ruby/core/thread/join_spec.rb
@@ -0,0 +1,65 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread#join" do
+ it "returns the thread when it is finished" do
+ t = Thread.new {}
+ t.join.should equal(t)
+ end
+
+ it "returns the thread when it is finished when given a timeout" do
+ t = Thread.new {}
+ t.join
+ t.join(0).should equal(t)
+ end
+
+ it "coerces timeout to a Float if it is not nil" do
+ t = Thread.new {}
+ t.join
+ t.join(0).should equal(t)
+ t.join(0.0).should equal(t)
+ t.join(nil).should equal(t)
+ lambda { t.join(:foo) }.should raise_error TypeError
+ lambda { t.join("bar") }.should raise_error TypeError
+ end
+
+ it "returns nil if it is not finished when given a timeout" do
+ c = Channel.new
+ t = Thread.new { c.receive }
+ begin
+ t.join(0).should == nil
+ ensure
+ c << true
+ end
+ t.join.should == t
+ end
+
+ it "accepts a floating point timeout length" do
+ c = Channel.new
+ t = Thread.new { c.receive }
+ begin
+ t.join(0.01).should == nil
+ ensure
+ c << true
+ end
+ t.join.should == t
+ end
+
+ it "raises any exceptions encountered in the thread body" do
+ t = Thread.new {
+ Thread.current.report_on_exception = false
+ raise NotImplementedError.new("Just kidding")
+ }
+ lambda { t.join }.should raise_error(NotImplementedError)
+ end
+
+ it "returns the dead thread" do
+ t = Thread.new { Thread.current.kill }
+ t.join.should equal(t)
+ end
+
+ it "raises any uncaught exception encountered in ensure block" do
+ t = ThreadSpecs.dying_thread_ensures { raise NotImplementedError.new("Just kidding") }
+ lambda { t.join }.should raise_error(NotImplementedError)
+ end
+end
diff --git a/spec/ruby/core/thread/key_spec.rb b/spec/ruby/core/thread/key_spec.rb
new file mode 100644
index 0000000000..c4ed0f9c0d
--- /dev/null
+++ b/spec/ruby/core/thread/key_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread#key?" do
+ before :each do
+ @th = Thread.new do
+ Thread.current[:oliver] = "a"
+ end
+ @th.join
+ end
+
+ it "tests for existence of thread local variables using symbols or strings" do
+ @th.key?(:oliver).should == true
+ @th.key?("oliver").should == true
+ @th.key?(:stanley).should == false
+ @th.key?(:stanley.to_s).should == false
+ end
+
+ it "raises exceptions on the wrong type of keys" do
+ lambda { Thread.current.key? nil }.should raise_error(TypeError)
+ lambda { Thread.current.key? 5 }.should raise_error(TypeError)
+ end
+
+ it "is not shared across fibers" do
+ fib = Fiber.new do
+ Thread.current[:val1] = 1
+ Fiber.yield
+ Thread.current.key?(:val1).should be_true
+ Thread.current.key?(:val2).should be_false
+ end
+ Thread.current.key?(:val1).should_not be_true
+ fib.resume
+ Thread.current[:val2] = 2
+ fib.resume
+ Thread.current.key?(:val1).should be_false
+ Thread.current.key?(:val2).should be_true
+ end
+
+ it "stores a local in another thread when in a fiber" do
+ fib = Fiber.new do
+ t = Thread.new do
+ sleep
+ Thread.current.key?(:value).should be_true
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t[:value] = 1
+ t.wakeup
+ t.join
+ end
+ fib.resume
+ end
+end
diff --git a/spec/ruby/core/thread/keys_spec.rb b/spec/ruby/core/thread/keys_spec.rb
new file mode 100644
index 0000000000..15efda51d6
--- /dev/null
+++ b/spec/ruby/core/thread/keys_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread#keys" do
+ it "returns an array of the names of the thread-local variables as symbols" do
+ th = Thread.new do
+ Thread.current["cat"] = 'woof'
+ Thread.current[:cat] = 'meow'
+ Thread.current[:dog] = 'woof'
+ end
+ th.join
+ th.keys.sort_by {|x| x.to_s}.should == [:cat,:dog]
+ end
+
+ it "is not shared across fibers" do
+ fib = Fiber.new do
+ Thread.current[:val1] = 1
+ Fiber.yield
+ Thread.current.keys.should include(:val1)
+ Thread.current.keys.should_not include(:val2)
+ end
+ Thread.current.keys.should_not include(:val1)
+ fib.resume
+ Thread.current[:val2] = 2
+ fib.resume
+ Thread.current.keys.should include(:val2)
+ Thread.current.keys.should_not include(:val1)
+ end
+
+ it "stores a local in another thread when in a fiber" do
+ fib = Fiber.new do
+ t = Thread.new do
+ sleep
+ Thread.current.keys.should include(:value)
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t[:value] = 1
+ t.wakeup
+ t.join
+ end
+ fib.resume
+ end
+end
diff --git a/spec/ruby/core/thread/kill_spec.rb b/spec/ruby/core/thread/kill_spec.rb
new file mode 100644
index 0000000000..f932bf5232
--- /dev/null
+++ b/spec/ruby/core/thread/kill_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/exit'
+
+# This spec randomly kills mspec worker like: https://ci.appveyor.com/project/ruby/ruby/builds/19473223/job/f69derxnlo09xhuj
+# TODO: Investigate the cause or at least print helpful logs, and remove this `platform_is_not` guard.
+platform_is_not :mingw do
+ describe "Thread#kill" do
+ it_behaves_like :thread_exit, :kill
+ end
+
+ describe "Thread#kill!" do
+ it "needs to be reviewed for spec completeness"
+ end
+
+ describe "Thread.kill" do
+ it "causes the given thread to exit" do
+ thread = Thread.new { sleep }
+ Thread.pass while thread.status and thread.status != "sleep"
+ Thread.kill(thread).should == thread
+ thread.join
+ thread.status.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/core/thread/list_spec.rb b/spec/ruby/core/thread/list_spec.rb
new file mode 100644
index 0000000000..bd56cb4c52
--- /dev/null
+++ b/spec/ruby/core/thread/list_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread.list" do
+ it "includes the current and main thread" do
+ Thread.list.should include(Thread.current)
+ Thread.list.should include(Thread.main)
+ end
+
+ it "includes threads of non-default thread groups" do
+ t = Thread.new { sleep }
+ begin
+ ThreadGroup.new.add(t)
+ Thread.list.should include(t)
+ ensure
+ t.kill
+ t.join
+ end
+ end
+
+ it "does not include deceased threads" do
+ t = Thread.new { 1; }
+ t.join
+ Thread.list.should_not include(t)
+ end
+
+ it "includes waiting threads" do
+ c = Channel.new
+ t = Thread.new { c.receive }
+ begin
+ Thread.pass while t.status and t.status != 'sleep'
+ Thread.list.should include(t)
+ ensure
+ c << nil
+ t.join
+ end
+ end
+end
+
+describe "Thread.list" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/thread/main_spec.rb b/spec/ruby/core/thread/main_spec.rb
new file mode 100644
index 0000000000..ec91709576
--- /dev/null
+++ b/spec/ruby/core/thread/main_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread.main" do
+ it "returns the main thread" do
+ Thread.new { @main = Thread.main ; @current = Thread.current}.join
+ @main.should_not == @current
+ @main.should == Thread.current
+ end
+end
diff --git a/spec/ruby/core/thread/name_spec.rb b/spec/ruby/core/thread/name_spec.rb
new file mode 100644
index 0000000000..adb2d08fae
--- /dev/null
+++ b/spec/ruby/core/thread/name_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+
+describe "Thread#name" do
+ before :each do
+ @thread = Thread.new {}
+ end
+
+ after :each do
+ @thread.join
+ end
+
+ it "is nil initially" do
+ @thread.name.should == nil
+ end
+
+ it "returns the thread name" do
+ @thread.name = "thread_name"
+ @thread.name.should == "thread_name"
+ end
+end
+
+describe "Thread#name=" do
+ before :each do
+ @thread = Thread.new {}
+ end
+
+ after :each do
+ @thread.join
+ end
+
+ it "can be set to a String" do
+ @thread.name = "new thread name"
+ @thread.name.should == "new thread name"
+ end
+
+ it "raises an ArgumentError if the name includes a null byte" do
+ lambda {
+ @thread.name = "new thread\0name"
+ }.should raise_error(ArgumentError)
+ end
+
+ it "can be reset to nil" do
+ @thread.name = nil
+ @thread.name.should == nil
+ end
+
+ it "calls #to_str to convert name to String" do
+ name = mock("Thread#name")
+ name.should_receive(:to_str).and_return("a thread name")
+
+ @thread.name = name
+ @thread.name.should == "a thread name"
+ end
+end
diff --git a/spec/ruby/core/thread/new_spec.rb b/spec/ruby/core/thread/new_spec.rb
new file mode 100644
index 0000000000..c1e0e2a5ad
--- /dev/null
+++ b/spec/ruby/core/thread/new_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread.new" do
+ it "creates a thread executing the given block" do
+ c = Channel.new
+ Thread.new { c << true }.join
+ c << false
+ c.receive.should == true
+ end
+
+ it "can pass arguments to the thread block" do
+ arr = []
+ a, b, c = 1, 2, 3
+ t = Thread.new(a,b,c) {|d,e,f| arr << d << e << f }
+ t.join
+ arr.should == [a,b,c]
+ end
+
+ it "raises an exception when not given a block" do
+ lambda { Thread.new }.should raise_error(ThreadError)
+ end
+
+ it "creates a subclass of thread calls super with a block in initialize" do
+ arr = []
+ t = ThreadSpecs::SubThread.new(arr)
+ t.join
+ arr.should == [1]
+ end
+
+ it "calls #initialize and raises an error if super not used" do
+ c = Class.new(Thread) do
+ def initialize
+ end
+ end
+
+ lambda {
+ c.new
+ }.should raise_error(ThreadError)
+ end
+
+ it "calls and respects #initialize for the block to use" do
+ c = Class.new(Thread) do
+ def initialize
+ ScratchPad.record [:good]
+ super { ScratchPad << :in_thread }
+ end
+ end
+
+ t = c.new
+ t.join
+
+ ScratchPad.recorded.should == [:good, :in_thread]
+ end
+
+end
diff --git a/spec/ruby/core/thread/pass_spec.rb b/spec/ruby/core/thread/pass_spec.rb
new file mode 100644
index 0000000000..a5ac11a58c
--- /dev/null
+++ b/spec/ruby/core/thread/pass_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread.pass" do
+ it "returns nil" do
+ Thread.pass.should == nil
+ end
+end
diff --git a/spec/ruby/core/thread/priority_spec.rb b/spec/ruby/core/thread/priority_spec.rb
new file mode 100644
index 0000000000..5da6216b53
--- /dev/null
+++ b/spec/ruby/core/thread/priority_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread#priority" do
+ before :each do
+ @current_priority = Thread.current.priority
+ ThreadSpecs.clear_state
+ @thread = Thread.new { Thread.pass until ThreadSpecs.state == :exit }
+ Thread.pass until @thread.alive?
+ end
+
+ after :each do
+ ThreadSpecs.state = :exit
+ @thread.join
+ end
+
+ it "inherits the priority of the current thread while running" do
+ @thread.alive?.should be_true
+ @thread.priority.should == @current_priority
+ end
+
+ it "maintain the priority of the current thread after death" do
+ ThreadSpecs.state = :exit
+ @thread.join
+ @thread.alive?.should be_false
+ @thread.priority.should == @current_priority
+ end
+
+ it "returns an integer" do
+ @thread.priority.should be_kind_of(Integer)
+ end
+end
+
+describe "Thread#priority=" do
+ before :each do
+ ThreadSpecs.clear_state
+ @thread = Thread.new { Thread.pass until ThreadSpecs.state == :exit }
+ Thread.pass until @thread.alive?
+ end
+
+ after :each do
+ ThreadSpecs.state = :exit
+ @thread.join
+ end
+
+ describe "when set with an integer" do
+ it "returns an integer" do
+ value = (@thread.priority = 3)
+ value.should == 3
+ end
+
+ it "clamps the priority to -3..3" do
+ @thread.priority = 42
+ @thread.priority.should == 3
+ @thread.priority = -42
+ @thread.priority.should == -3
+ end
+ end
+
+ describe "when set with a non-integer" do
+ it "raises a type error" do
+ lambda{ @thread.priority = Object.new }.should raise_error(TypeError)
+ end
+ end
+
+ it "sets priority even when the thread has died" do
+ thread = Thread.new {}
+ thread.join
+ thread.priority = 3
+ thread.priority.should == 3
+ end
+end
diff --git a/spec/ruby/core/thread/raise_spec.rb b/spec/ruby/core/thread/raise_spec.rb
new file mode 100644
index 0000000000..38571854ef
--- /dev/null
+++ b/spec/ruby/core/thread/raise_spec.rb
@@ -0,0 +1,205 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/kernel/raise'
+
+describe "Thread#raise" do
+ it "ignores dead threads and returns nil" do
+ t = Thread.new { :dead }
+ Thread.pass while t.alive?
+ t.raise("Kill the thread").should == nil
+ t.join
+ end
+end
+
+describe "Thread#raise on a sleeping thread" do
+ before :each do
+ ScratchPad.clear
+ @thr = ThreadSpecs.sleeping_thread
+ Thread.pass while @thr.status and @thr.status != "sleep"
+ end
+
+ after :each do
+ @thr.kill
+ @thr.join
+ end
+
+ it "raises a RuntimeError if no exception class is given" do
+ @thr.raise
+ Thread.pass while @thr.status
+ ScratchPad.recorded.should be_kind_of(RuntimeError)
+ end
+
+ it "raises the given exception" do
+ @thr.raise Exception
+ Thread.pass while @thr.status
+ ScratchPad.recorded.should be_kind_of(Exception)
+ end
+
+ it "raises the given exception with the given message" do
+ @thr.raise Exception, "get to work"
+ Thread.pass while @thr.status
+ ScratchPad.recorded.should be_kind_of(Exception)
+ ScratchPad.recorded.message.should == "get to work"
+ end
+
+ it "raises the given exception and the backtrace is the one of the interrupted thread" do
+ @thr.raise Exception
+ Thread.pass while @thr.status
+ ScratchPad.recorded.should be_kind_of(Exception)
+ ScratchPad.recorded.backtrace[0].should include("sleep")
+ end
+
+ it "is captured and raised by Thread#value" do
+ t = Thread.new do
+ Thread.current.report_on_exception = false
+ sleep
+ end
+
+ ThreadSpecs.spin_until_sleeping(t)
+
+ t.raise
+ -> { t.value }.should raise_error(RuntimeError)
+ end
+
+ it "raises a RuntimeError when called with no arguments inside rescue" do
+ t = Thread.new do
+ Thread.current.report_on_exception = false
+ begin
+ 1/0
+ rescue ZeroDivisionError
+ sleep
+ end
+ end
+ begin
+ raise RangeError
+ rescue
+ ThreadSpecs.spin_until_sleeping(t)
+ t.raise
+ end
+ -> { t.value }.should raise_error(RuntimeError)
+ end
+
+ it "re-raises a previously rescued exception without overwriting the backtrace" do
+ t = Thread.new do
+ -> { # To make sure there is at least one entry in the call stack
+ begin
+ sleep
+ rescue => e
+ e
+ end
+ }.call
+ end
+
+ ThreadSpecs.spin_until_sleeping(t)
+
+ begin
+ initial_raise_line = __LINE__; raise 'raised'
+ rescue => raised
+ raise_again_line = __LINE__; t.raise raised
+ raised_again = t.value
+
+ raised_again.backtrace.first.should include("#{__FILE__}:#{initial_raise_line}:")
+ raised_again.backtrace.first.should_not include("#{__FILE__}:#{raise_again_line}:")
+ end
+ end
+end
+
+describe "Thread#raise on a running thread" do
+ before :each do
+ ScratchPad.clear
+ ThreadSpecs.clear_state
+
+ @thr = ThreadSpecs.running_thread
+ Thread.pass until ThreadSpecs.state == :running
+ end
+
+ after :each do
+ @thr.kill
+ @thr.join
+ end
+
+ it "raises a RuntimeError if no exception class is given" do
+ @thr.raise
+ Thread.pass while @thr.status
+ ScratchPad.recorded.should be_kind_of(RuntimeError)
+ end
+
+ it "raises the given exception" do
+ @thr.raise Exception
+ Thread.pass while @thr.status
+ ScratchPad.recorded.should be_kind_of(Exception)
+ end
+
+ it "raises the given exception with the given message" do
+ @thr.raise Exception, "get to work"
+ Thread.pass while @thr.status
+ ScratchPad.recorded.should be_kind_of(Exception)
+ ScratchPad.recorded.message.should == "get to work"
+ end
+
+ it "can go unhandled" do
+ t = Thread.new do
+ Thread.current.report_on_exception = false
+ loop { Thread.pass }
+ end
+
+ t.raise
+ -> { t.value }.should raise_error(RuntimeError)
+ end
+
+ it "raises the given argument even when there is an active exception" do
+ raised = false
+ t = Thread.new do
+ Thread.current.report_on_exception = false
+ begin
+ 1/0
+ rescue ZeroDivisionError
+ raised = true
+ loop { Thread.pass }
+ end
+ end
+ begin
+ raise "Create an active exception for the current thread too"
+ rescue
+ Thread.pass until raised
+ t.raise RangeError
+ -> { t.value }.should raise_error(RangeError)
+ end
+ end
+
+ it "raises a RuntimeError when called with no arguments inside rescue" do
+ raised = false
+ t = Thread.new do
+ Thread.current.report_on_exception = false
+ begin
+ 1/0
+ rescue ZeroDivisionError
+ raised = true
+ loop { Thread.pass }
+ end
+ end
+ begin
+ raise RangeError
+ rescue
+ Thread.pass until raised
+ t.raise
+ end
+ -> { t.value }.should raise_error(RuntimeError)
+ end
+end
+
+describe "Thread#raise on same thread" do
+ it_behaves_like :kernel_raise, :raise, Thread.current
+
+ it "raises a RuntimeError when called with no arguments inside rescue" do
+ t = Thread.new do
+ Thread.current.report_on_exception = false
+ begin
+ 1/0
+ rescue ZeroDivisionError
+ Thread.current.raise
+ end
+ end
+ -> { t.value }.should raise_error(RuntimeError)
+ end
+end
diff --git a/spec/ruby/core/thread/report_on_exception_spec.rb b/spec/ruby/core/thread/report_on_exception_spec.rb
new file mode 100644
index 0000000000..16597f3a4b
--- /dev/null
+++ b/spec/ruby/core/thread/report_on_exception_spec.rb
@@ -0,0 +1,120 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.4" do
+ describe "Thread.report_on_exception" do
+ ruby_version_is "2.4"..."2.5" do
+ it "defaults to false" do
+ ruby_exe("p Thread.report_on_exception").should == "false\n"
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "defaults to true" do
+ ruby_exe("p Thread.report_on_exception").should == "true\n"
+ end
+ end
+ end
+
+ describe "Thread.report_on_exception=" do
+ before :each do
+ @report_on_exception = Thread.report_on_exception
+ end
+
+ after :each do
+ Thread.report_on_exception = @report_on_exception
+ end
+
+ it "changes the default value for new threads" do
+ Thread.report_on_exception = true
+ Thread.report_on_exception.should == true
+ t = Thread.new {}
+ t.join
+ t.report_on_exception.should == true
+ end
+ end
+
+ describe "Thread#report_on_exception" do
+ ruby_version_is "2.5" do
+ it "returns true for the main Thread" do
+ Thread.current.report_on_exception.should == true
+ end
+
+ it "returns true for new Threads" do
+ Thread.new { Thread.current.report_on_exception }.value.should == true
+ end
+ end
+
+ it "returns whether the Thread will print a backtrace if it exits with an exception" do
+ t = Thread.new { Thread.current.report_on_exception = true }
+ t.join
+ t.report_on_exception.should == true
+
+ t = Thread.new { Thread.current.report_on_exception = false }
+ t.join
+ t.report_on_exception.should == false
+ end
+ end
+
+ describe "Thread#report_on_exception=" do
+ describe "when set to true" do
+ it "prints a backtrace on $stderr if it terminates with an exception" do
+ t = nil
+ -> {
+ t = Thread.new {
+ Thread.current.report_on_exception = true
+ raise RuntimeError, "Thread#report_on_exception specs"
+ }
+ Thread.pass while t.alive?
+ }.should output("", /Thread.+terminated with exception.+Thread#report_on_exception specs/m)
+
+ -> {
+ t.join
+ }.should raise_error(RuntimeError, "Thread#report_on_exception specs")
+ end
+ end
+
+ describe "when set to false" do
+ it "lets the thread terminates silently with an exception" do
+ t = nil
+ -> {
+ t = Thread.new {
+ Thread.current.report_on_exception = false
+ raise RuntimeError, "Thread#report_on_exception specs"
+ }
+ Thread.pass while t.alive?
+ }.should output("", "")
+
+ -> {
+ t.join
+ }.should raise_error(RuntimeError, "Thread#report_on_exception specs")
+ end
+ end
+
+ ruby_bug "#13163", "2.4"..."2.5" do
+ describe "when used in conjunction with Thread#abort_on_exception" do
+ it "first reports then send the exception back to the main Thread" do
+ t = nil
+ mutex = Mutex.new
+ mutex.lock
+ -> {
+ t = Thread.new {
+ Thread.current.abort_on_exception = true
+ Thread.current.report_on_exception = true
+ mutex.lock
+ mutex.unlock
+ raise RuntimeError, "Thread#report_on_exception specs"
+ }
+
+ -> {
+ mutex.sleep(5)
+ }.should raise_error(RuntimeError, "Thread#report_on_exception specs")
+ }.should output("", /Thread.+terminated with exception.+Thread#report_on_exception specs/m)
+
+ -> {
+ t.join
+ }.should raise_error(RuntimeError, "Thread#report_on_exception specs")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/thread/run_spec.rb b/spec/ruby/core/thread/run_spec.rb
new file mode 100644
index 0000000000..f86f793489
--- /dev/null
+++ b/spec/ruby/core/thread/run_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+require_relative 'shared/wakeup'
+
+describe "Thread#run" do
+ it_behaves_like :thread_wakeup, :run
+end
diff --git a/spec/ruby/core/thread/set_trace_func_spec.rb b/spec/ruby/core/thread/set_trace_func_spec.rb
new file mode 100644
index 0000000000..e5d8298ae0
--- /dev/null
+++ b/spec/ruby/core/thread/set_trace_func_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Thread#set_trace_func" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/thread/shared/exit.rb b/spec/ruby/core/thread/shared/exit.rb
new file mode 100644
index 0000000000..3c63517d92
--- /dev/null
+++ b/spec/ruby/core/thread/shared/exit.rb
@@ -0,0 +1,182 @@
+describe :thread_exit, shared: true do
+ before :each do
+ ScratchPad.clear
+ end
+
+ # This spec randomly kills mspec worker like: https://ci.appveyor.com/project/ruby/ruby/builds/19390874/job/wv1bsm8skd4e1pxl
+ # TODO: Investigate the cause or at least print helpful logs, and remove this `platform_is_not` guard.
+ platform_is_not :mingw do
+
+ it "kills sleeping thread" do
+ sleeping_thread = Thread.new do
+ sleep
+ ScratchPad.record :after_sleep
+ end
+ Thread.pass while sleeping_thread.status and sleeping_thread.status != "sleep"
+ sleeping_thread.send(@method)
+ sleeping_thread.join
+ ScratchPad.recorded.should == nil
+ end
+
+ it "kills current thread" do
+ thread = Thread.new do
+ Thread.current.send(@method)
+ ScratchPad.record :after_sleep
+ end
+ thread.join
+ ScratchPad.recorded.should == nil
+ end
+
+ it "runs ensure clause" do
+ thread = ThreadSpecs.dying_thread_ensures(@method) { ScratchPad.record :in_ensure_clause }
+ thread.join
+ ScratchPad.recorded.should == :in_ensure_clause
+ end
+
+ it "runs nested ensure clauses" do
+ ScratchPad.record []
+ @outer = Thread.new do
+ begin
+ @inner = Thread.new do
+ begin
+ sleep
+ ensure
+ ScratchPad << :inner_ensure_clause
+ end
+ end
+ sleep
+ ensure
+ ScratchPad << :outer_ensure_clause
+ @inner.send(@method)
+ @inner.join
+ end
+ end
+ Thread.pass while @outer.status and @outer.status != "sleep"
+ Thread.pass until @inner
+ Thread.pass while @inner.status and @inner.status != "sleep"
+ @outer.send(@method)
+ @outer.join
+ ScratchPad.recorded.should include(:inner_ensure_clause)
+ ScratchPad.recorded.should include(:outer_ensure_clause)
+ end
+
+ it "does not set $!" do
+ thread = ThreadSpecs.dying_thread_ensures(@method) { ScratchPad.record $! }
+ thread.join
+ ScratchPad.recorded.should == nil
+ end
+
+ it "cannot be rescued" do
+ thread = Thread.new do
+ begin
+ Thread.current.send(@method)
+ rescue Exception
+ ScratchPad.record :in_rescue
+ end
+ ScratchPad.record :end_of_thread_block
+ end
+
+ thread.join
+ ScratchPad.recorded.should == nil
+ end
+
+ with_feature :fiber do
+ it "kills the entire thread when a fiber is active" do
+ t = Thread.new do
+ Fiber.new do
+ sleep
+ end.resume
+ ScratchPad.record :fiber_resumed
+ end
+ Thread.pass while t.status and t.status != "sleep"
+ t.send(@method)
+ t.join
+ ScratchPad.recorded.should == nil
+ end
+ end
+
+ # This spec is a mess. It fails randomly, it hangs on MRI, it needs to be removed
+ quarantine! do
+ it "killing dying running does nothing" do
+ in_ensure_clause = false
+ exit_loop = true
+ t = ThreadSpecs.dying_thread_ensures do
+ in_ensure_clause = true
+ loop { if exit_loop then break end }
+ ScratchPad.record :after_stop
+ end
+
+ Thread.pass until in_ensure_clause == true
+ 10.times { t.send(@method); Thread.pass }
+ exit_loop = true
+ t.join
+ ScratchPad.recorded.should == :after_stop
+ end
+ end
+
+ quarantine! do
+
+ it "propagates inner exception to Thread.join if there is an outer ensure clause" do
+ thread = ThreadSpecs.dying_thread_with_outer_ensure(@method) { }
+ lambda { thread.join }.should raise_error(RuntimeError, "In dying thread")
+ end
+
+ it "runs all outer ensure clauses even if inner ensure clause raises exception" do
+ ThreadSpecs.join_dying_thread_with_outer_ensure(@method) { ScratchPad.record :in_outer_ensure_clause }
+ ScratchPad.recorded.should == :in_outer_ensure_clause
+ end
+
+ it "sets $! in outer ensure clause if inner ensure clause raises exception" do
+ ThreadSpecs.join_dying_thread_with_outer_ensure(@method) { ScratchPad.record $! }
+ ScratchPad.recorded.to_s.should == "In dying thread"
+ end
+ end
+
+ it "can be rescued by outer rescue clause when inner ensure clause raises exception" do
+ thread = Thread.new do
+ begin
+ begin
+ Thread.current.send(@method)
+ ensure
+ raise "In dying thread"
+ end
+ rescue Exception
+ ScratchPad.record $!
+ end
+ :end_of_thread_block
+ end
+
+ thread.value.should == :end_of_thread_block
+ ScratchPad.recorded.to_s.should == "In dying thread"
+ end
+
+ it "is deferred if ensure clause does Thread.stop" do
+ ThreadSpecs.wakeup_dying_sleeping_thread(@method) { Thread.stop; ScratchPad.record :after_sleep }
+ ScratchPad.recorded.should == :after_sleep
+ end
+
+ # Hangs on 1.8.6.114 OS X, possibly also on Linux
+ quarantine! do
+ it "is deferred if ensure clause sleeps" do
+ ThreadSpecs.wakeup_dying_sleeping_thread(@method) { sleep; ScratchPad.record :after_sleep }
+ ScratchPad.recorded.should == :after_sleep
+ end
+ end
+
+ # This case occurred in JRuby where native threads are used to provide
+ # the same behavior as MRI green threads. Key to this issue was the fact
+ # that the thread which called #exit in its block was also being explicitly
+ # sent #join from outside the thread. The 100.times provides a certain
+ # probability that the deadlock will occur. It was sufficient to reliably
+ # reproduce the deadlock in JRuby.
+ it "does not deadlock when called from within the thread while being joined from without" do
+ 100.times do
+ t = Thread.new { Thread.stop; Thread.current.send(@method) }
+ Thread.pass while t.status and t.status != "sleep"
+ t.wakeup.should == t
+ t.join.should == t
+ end
+ end
+
+ end # platform_is_not :mingw
+end
diff --git a/spec/ruby/core/thread/shared/start.rb b/spec/ruby/core/thread/shared/start.rb
new file mode 100644
index 0000000000..80ce063a0e
--- /dev/null
+++ b/spec/ruby/core/thread/shared/start.rb
@@ -0,0 +1,41 @@
+describe :thread_start, shared: true do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "raises an ArgumentError if not passed a block" do
+ lambda {
+ Thread.send(@method)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "spawns a new Thread running the block" do
+ run = false
+ t = Thread.send(@method) { run = true }
+ t.should be_kind_of(Thread)
+ t.join
+
+ run.should be_true
+ end
+
+ it "respects Thread subclasses" do
+ c = Class.new(Thread)
+ t = c.send(@method) { }
+ t.should be_kind_of(c)
+
+ t.join
+ end
+
+ it "does not call #initialize" do
+ c = Class.new(Thread) do
+ def initialize
+ ScratchPad.record :bad
+ end
+ end
+
+ t = c.send(@method) { }
+ t.join
+
+ ScratchPad.recorded.should == nil
+ end
+end
diff --git a/spec/ruby/core/thread/shared/wakeup.rb b/spec/ruby/core/thread/shared/wakeup.rb
new file mode 100644
index 0000000000..71838d88e5
--- /dev/null
+++ b/spec/ruby/core/thread/shared/wakeup.rb
@@ -0,0 +1,61 @@
+describe :thread_wakeup, shared: true do
+ it "can interrupt Kernel#sleep" do
+ exit_loop = false
+ after_sleep1 = false
+ after_sleep2 = false
+
+ t = Thread.new do
+ while true
+ break if exit_loop == true
+ Thread.pass
+ end
+
+ sleep
+ after_sleep1 = true
+
+ sleep
+ after_sleep2 = true
+ end
+
+ 10.times { t.send(@method); Thread.pass }
+ t.status.should_not == "sleep"
+
+ exit_loop = true
+
+ 10.times { sleep 0.1 if t.status and t.status != "sleep" }
+ after_sleep1.should == false # t should be blocked on the first sleep
+ t.send(@method)
+
+ 10.times { sleep 0.1 if after_sleep1 != true }
+ 10.times { sleep 0.1 if t.status and t.status != "sleep" }
+ after_sleep2.should == false # t should be blocked on the second sleep
+ t.send(@method)
+
+ t.join
+ end
+
+ it "does not result in a deadlock" do
+ t = Thread.new do
+ 100.times { Thread.stop }
+ end
+
+ while t.status
+ begin
+ t.send(@method)
+ rescue ThreadError
+ # The thread might die right after.
+ t.status.should == false
+ end
+ Thread.pass
+ end
+
+ t.status.should == false
+ t.join
+ end
+
+ it "raises a ThreadError when trying to wake up a dead thread" do
+ t = Thread.new { 1 }
+ t.join
+ lambda { t.send @method }.should raise_error(ThreadError)
+ end
+end
diff --git a/spec/ruby/core/thread/start_spec.rb b/spec/ruby/core/thread/start_spec.rb
new file mode 100644
index 0000000000..3dd040f98b
--- /dev/null
+++ b/spec/ruby/core/thread/start_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/start'
+
+describe "Thread.start" do
+ describe "Thread.start" do
+ it_behaves_like :thread_start, :start
+ end
+end
diff --git a/spec/ruby/core/thread/status_spec.rb b/spec/ruby/core/thread/status_spec.rb
new file mode 100644
index 0000000000..4fde663c91
--- /dev/null
+++ b/spec/ruby/core/thread/status_spec.rb
@@ -0,0 +1,60 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread#status" do
+ it "can check it's own status" do
+ ThreadSpecs.status_of_current_thread.status.should == 'run'
+ end
+
+ it "describes a running thread" do
+ ThreadSpecs.status_of_running_thread.status.should == 'run'
+ end
+
+ it "describes a sleeping thread" do
+ ThreadSpecs.status_of_sleeping_thread.status.should == 'sleep'
+ end
+
+ it "describes a blocked thread" do
+ ThreadSpecs.status_of_blocked_thread.status.should == 'sleep'
+ end
+
+ it "describes a completed thread" do
+ ThreadSpecs.status_of_completed_thread.status.should == false
+ end
+
+ it "describes a killed thread" do
+ ThreadSpecs.status_of_killed_thread.status.should == false
+ end
+
+ it "describes a thread with an uncaught exception" do
+ ThreadSpecs.status_of_thread_with_uncaught_exception.status.should == nil
+ end
+
+ it "describes a dying sleeping thread" do
+ ThreadSpecs.status_of_dying_sleeping_thread.status.should == 'sleep'
+ end
+
+ it "reports aborting on a killed thread" do
+ ThreadSpecs.status_of_dying_running_thread.status.should == 'aborting'
+ end
+
+ it "reports aborting on a killed thread after sleep" do
+ ThreadSpecs.status_of_dying_thread_after_sleep.status.should == 'aborting'
+ end
+
+ it "reports aborting on an externally killed thread that sleeps" do
+ q = Queue.new
+ t = Thread.new do
+ begin
+ q.push nil
+ sleep
+ ensure
+ q.push Thread.current.status
+ end
+ end
+ q.pop
+ t.kill
+ t.join
+ q.pop.should == 'aborting'
+ end
+end
diff --git a/spec/ruby/core/thread/stop_spec.rb b/spec/ruby/core/thread/stop_spec.rb
new file mode 100644
index 0000000000..33cf8f7b5c
--- /dev/null
+++ b/spec/ruby/core/thread/stop_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread.stop" do
+ it "causes the current thread to sleep indefinitely" do
+ t = Thread.new { Thread.stop; 5 }
+ Thread.pass while t.status and t.status != 'sleep'
+ t.status.should == 'sleep'
+ t.run
+ t.value.should == 5
+ end
+end
+
+describe "Thread#stop?" do
+ it "can check it's own status" do
+ ThreadSpecs.status_of_current_thread.stop?.should == false
+ end
+
+ it "describes a running thread" do
+ ThreadSpecs.status_of_running_thread.stop?.should == false
+ end
+
+ it "describes a sleeping thread" do
+ ThreadSpecs.status_of_sleeping_thread.stop?.should == true
+ end
+
+ it "describes a blocked thread" do
+ ThreadSpecs.status_of_blocked_thread.stop?.should == true
+ end
+
+ it "describes a completed thread" do
+ ThreadSpecs.status_of_completed_thread.stop?.should == true
+ end
+
+ it "describes a killed thread" do
+ ThreadSpecs.status_of_killed_thread.stop?.should == true
+ end
+
+ it "describes a thread with an uncaught exception" do
+ ThreadSpecs.status_of_thread_with_uncaught_exception.stop?.should == true
+ end
+
+ it "describes a dying running thread" do
+ ThreadSpecs.status_of_dying_running_thread.stop?.should == false
+ end
+
+ it "describes a dying sleeping thread" do
+ ThreadSpecs.status_of_dying_sleeping_thread.stop?.should == true
+ end
+
+ it "describes a dying thread after sleep" do
+ ThreadSpecs.status_of_dying_thread_after_sleep.stop?.should == false
+ end
+end
diff --git a/spec/ruby/core/thread/terminate_spec.rb b/spec/ruby/core/thread/terminate_spec.rb
new file mode 100644
index 0000000000..cf6cab472b
--- /dev/null
+++ b/spec/ruby/core/thread/terminate_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/exit'
+
+describe "Thread#terminate" do
+ it_behaves_like :thread_exit, :terminate
+end
diff --git a/spec/ruby/core/thread/thread_variable_get_spec.rb b/spec/ruby/core/thread/thread_variable_get_spec.rb
new file mode 100644
index 0000000000..38f90d5830
--- /dev/null
+++ b/spec/ruby/core/thread/thread_variable_get_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+
+describe "Thread#thread_variable_get" do
+ before :each do
+ @t = Thread.new { }
+ end
+
+ after :each do
+ @t.join
+ end
+
+ it "returns nil if the variable is not set" do
+ @t.thread_variable_get(:a).should be_nil
+ end
+
+ it "returns the value previously set by #[]=" do
+ @t.thread_variable_set :a, 49
+ @t.thread_variable_get(:a).should == 49
+ end
+
+ it "returns a value private to self" do
+ @t.thread_variable_set :thread_variable_get_spec, 82
+ Thread.current.thread_variable_get(:thread_variable_get_spec).should be_nil
+ end
+end
diff --git a/spec/ruby/core/thread/thread_variable_set_spec.rb b/spec/ruby/core/thread/thread_variable_set_spec.rb
new file mode 100644
index 0000000000..1338c306c7
--- /dev/null
+++ b/spec/ruby/core/thread/thread_variable_set_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+
+describe "Thread#thread_variable_set" do
+ before :each do
+ @t = Thread.new { }
+ end
+
+ after :each do
+ @t.join
+ end
+
+ it "returns the value set" do
+ (@t.thread_variable_set :a, 2).should == 2
+ end
+
+ it "sets a value that will be returned by #thread_variable_get" do
+ @t.thread_variable_set :a, 49
+ @t.thread_variable_get(:a).should == 49
+ end
+
+ it "sets a value private to self" do
+ @t.thread_variable_set :thread_variable_get_spec, 82
+ @t.thread_variable_get(:thread_variable_get_spec).should == 82
+ Thread.current.thread_variable_get(:thread_variable_get_spec).should be_nil
+ end
+end
diff --git a/spec/ruby/core/thread/thread_variable_spec.rb b/spec/ruby/core/thread/thread_variable_spec.rb
new file mode 100644
index 0000000000..6bd1950c04
--- /dev/null
+++ b/spec/ruby/core/thread/thread_variable_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+
+describe "Thread#thread_variable?" do
+ before :each do
+ @t = Thread.new { }
+ end
+
+ after :each do
+ @t.join
+ end
+
+ it "returns false if the thread variables do not contain 'key'" do
+ @t.thread_variable_set :a, 2
+ @t.thread_variable?(:b).should be_false
+ end
+
+ it "returns true if the thread variables contain 'key'" do
+ @t.thread_variable_set :a, 2
+ @t.thread_variable?(:a).should be_true
+ end
+end
diff --git a/spec/ruby/core/thread/thread_variables_spec.rb b/spec/ruby/core/thread/thread_variables_spec.rb
new file mode 100644
index 0000000000..1bd68b17f1
--- /dev/null
+++ b/spec/ruby/core/thread/thread_variables_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+
+describe "Thread#thread_variables" do
+ before :each do
+ @t = Thread.new { }
+ end
+
+ after :each do
+ @t.join
+ end
+
+ it "returns the keys of all the values set" do
+ @t.thread_variable_set :a, 2
+ @t.thread_variable_set :b, 4
+ @t.thread_variable_set :c, 6
+ @t.thread_variables.sort.should == [:a, :b, :c]
+ end
+
+ it "sets a value private to self" do
+ @t.thread_variable_set :a, 82
+ @t.thread_variable_set :b, 82
+ Thread.current.thread_variables.should_not include(:a, :b)
+ end
+
+ it "only contains user thread variables and is empty initially" do
+ Thread.current.thread_variables.should == []
+ @t.thread_variables.should == []
+ end
+end
diff --git a/spec/ruby/core/thread/value_spec.rb b/spec/ruby/core/thread/value_spec.rb
new file mode 100644
index 0000000000..30b12db125
--- /dev/null
+++ b/spec/ruby/core/thread/value_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Thread#value" do
+ it "returns the result of the block" do
+ Thread.new { 3 }.value.should == 3
+ end
+
+ it "re-raises an error for an uncaught exception" do
+ t = Thread.new {
+ Thread.current.report_on_exception = false
+ raise "Hello"
+ }
+ lambda { t.value }.should raise_error(RuntimeError, "Hello")
+ end
+
+ it "is nil for a killed thread" do
+ t = Thread.new { Thread.current.exit }
+ t.value.should == nil
+ end
+end
diff --git a/spec/ruby/core/thread/wakeup_spec.rb b/spec/ruby/core/thread/wakeup_spec.rb
new file mode 100644
index 0000000000..da5dfea377
--- /dev/null
+++ b/spec/ruby/core/thread/wakeup_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/wakeup'
+
+describe "Thread#wakeup" do
+ it_behaves_like :thread_wakeup, :wakeup
+end
diff --git a/spec/ruby/core/threadgroup/add_spec.rb b/spec/ruby/core/threadgroup/add_spec.rb
new file mode 100644
index 0000000000..5d1354e65f
--- /dev/null
+++ b/spec/ruby/core/threadgroup/add_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "ThreadGroup#add" do
+ before :each do
+ @chan1,@chan2 = Channel.new,Channel.new
+ @thread = Thread.new { @chan1 << :go; @chan2.receive }
+ @chan1.receive
+ end
+
+ after :each do
+ @chan2 << :done
+ @thread.join
+ end
+
+ # This spec randomly kills mspec worker like: https://ci.appveyor.com/project/ruby/ruby/build/9806/job/37tx2atojy96227m
+ # TODO: Investigate the cause or at least print helpful logs, and remove this `platform_is_not` guard.
+ platform_is_not :mingw do
+ it "adds the given thread to a group and returns self" do
+ @thread.group.should_not == nil
+
+ tg = ThreadGroup.new
+ tg.add(@thread).should == tg
+ @thread.group.should == tg
+ tg.list.include?(@thread).should == true
+ end
+
+ it "removes itself from any other threadgroup" do
+ tg1 = ThreadGroup.new
+ tg2 = ThreadGroup.new
+
+ tg1.add(@thread)
+ @thread.group.should == tg1
+ tg2.add(@thread)
+ @thread.group.should == tg2
+ tg2.list.include?(@thread).should == true
+ tg1.list.include?(@thread).should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/threadgroup/default_spec.rb b/spec/ruby/core/threadgroup/default_spec.rb
new file mode 100644
index 0000000000..d7d4726cc2
--- /dev/null
+++ b/spec/ruby/core/threadgroup/default_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "ThreadGroup::Default" do
+ it "is a ThreadGroup instance" do
+ ThreadGroup::Default.should be_kind_of(ThreadGroup)
+ end
+
+ it "is the ThreadGroup of the main thread" do
+ ThreadGroup::Default.should == Thread.main.group
+ end
+end
diff --git a/spec/ruby/core/threadgroup/enclose_spec.rb b/spec/ruby/core/threadgroup/enclose_spec.rb
new file mode 100644
index 0000000000..d4a4359c66
--- /dev/null
+++ b/spec/ruby/core/threadgroup/enclose_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "ThreadGroup#enclose" do
+ before :each do
+ @chan1,@chan2 = Channel.new,Channel.new
+ @thread = Thread.new { @chan1 << :go; @chan2.receive }
+ @chan1.receive
+ end
+
+ after :each do
+ @chan2 << :done
+ @thread.join
+ end
+
+ it "raises a ThreadError if attempting to move a Thread from an enclosed ThreadGroup" do
+ thread_group = ThreadGroup.new
+ default_group = @thread.group
+ thread_group.add(@thread)
+ thread_group.enclose
+ lambda do
+ default_group.add(@thread)
+ end.should raise_error(ThreadError)
+ end
+end
diff --git a/spec/ruby/core/threadgroup/enclosed_spec.rb b/spec/ruby/core/threadgroup/enclosed_spec.rb
new file mode 100644
index 0000000000..a734256a64
--- /dev/null
+++ b/spec/ruby/core/threadgroup/enclosed_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe "ThreadGroup#enclosed?" do
+ it "returns false when a ThreadGroup has not been enclosed (default state)" do
+ thread_group = ThreadGroup.new
+ thread_group.enclosed?.should be_false
+ end
+
+ it "returns true when a ThreadGroup is enclosed" do
+ thread_group = ThreadGroup.new
+ thread_group.enclose
+ thread_group.enclosed?.should be_true
+ end
+end
diff --git a/spec/ruby/core/threadgroup/fixtures/classes.rb b/spec/ruby/core/threadgroup/fixtures/classes.rb
new file mode 100644
index 0000000000..7dfe5e92d1
--- /dev/null
+++ b/spec/ruby/core/threadgroup/fixtures/classes.rb
@@ -0,0 +1,6 @@
+unless defined? Channel
+ require 'thread'
+ class Channel < Queue
+ alias receive shift
+ end
+end
diff --git a/spec/ruby/core/threadgroup/list_spec.rb b/spec/ruby/core/threadgroup/list_spec.rb
new file mode 100644
index 0000000000..5f2281af8d
--- /dev/null
+++ b/spec/ruby/core/threadgroup/list_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "ThreadGroup#list" do
+ it "returns the list of threads in the group" do
+ chan = Channel.new
+ th1 = Thread.new { chan << :go; sleep }
+ chan.receive.should == :go
+ tg = ThreadGroup.new
+ tg.add(th1)
+ tg.list.should include(th1)
+
+ th2 = Thread.new { chan << :go; sleep }
+ chan.receive.should == :go
+
+ tg.add(th2)
+ (tg.list & [th1, th2]).should include(th1, th2)
+
+ Thread.pass while th1.status and th1.status != 'sleep'
+ Thread.pass while th2.status and th2.status != 'sleep'
+ th1.run; th1.join
+ th2.run; th2.join
+ end
+end
diff --git a/spec/ruby/core/time/_dump_spec.rb b/spec/ruby/core/time/_dump_spec.rb
new file mode 100644
index 0000000000..6941a1531f
--- /dev/null
+++ b/spec/ruby/core/time/_dump_spec.rb
@@ -0,0 +1,55 @@
+# -*- encoding: binary -*-
+require_relative '../../spec_helper'
+
+describe "Time#_dump" do
+ before :each do
+ @local = Time.at(946812800)
+ @t = Time.at(946812800)
+ @t = @t.gmtime
+ @s = @t.send(:_dump)
+ end
+
+ it "is a private method" do
+ Time.should have_private_instance_method(:_dump, false)
+ end
+
+ # http://redmine.ruby-lang.org/issues/show/627
+ it "preserves the GMT flag" do
+ @t.gmt?.should == true
+ dump = @t.send(:_dump).unpack("VV").first
+ ((dump >> 30) & 0x1).should == 1
+
+ @local.gmt?.should == false
+ dump = @local.send(:_dump).unpack("VV").first
+ ((dump >> 30) & 0x1).should == 0
+ end
+
+ it "dumps a Time object to a bytestring" do
+ @s.should be_an_instance_of(String)
+ @s.should == [3222863947, 2235564032].pack("VV")
+ end
+
+ it "dumps an array with a date as first element" do
+ high = 1 << 31 |
+ (@t.gmt? ? 1 : 0) << 30 |
+ (@t.year - 1900) << 14 |
+ (@t.mon - 1) << 10 |
+ @t.mday << 5 |
+ @t.hour
+
+ high.should == @s.unpack("VV").first
+ end
+
+ it "dumps an array with a time as second element" do
+ low = @t.min << 26 |
+ @t.sec << 20 |
+ @t.usec
+ low.should == @s.unpack("VV").last
+ end
+
+ it "dumps like MRI's marshaled time format" do
+ t = Time.utc(2000, 1, 15, 20, 1, 1, 203).localtime
+
+ t.send(:_dump).should == "\364\001\031\200\313\000\020\004"
+ end
+end
diff --git a/spec/ruby/core/time/_load_spec.rb b/spec/ruby/core/time/_load_spec.rb
new file mode 100644
index 0000000000..5f6275d46c
--- /dev/null
+++ b/spec/ruby/core/time/_load_spec.rb
@@ -0,0 +1,54 @@
+# -*- encoding: binary -*-
+require_relative '../../spec_helper'
+
+describe "Time._load" do
+ it "is a private method" do
+ Time.should have_private_method(:_load, false)
+ end
+
+ # http://redmine.ruby-lang.org/issues/show/627
+ it "loads a time object in the new format" do
+ t = Time.local(2000, 1, 15, 20, 1, 1)
+ t = t.gmtime
+
+ high = 1 << 31 |
+ (t.gmt? ? 1 : 0) << 30 |
+ (t.year - 1900) << 14 |
+ (t.mon - 1) << 10 |
+ t.mday << 5 |
+ t.hour
+
+ low = t.min << 26 |
+ t.sec << 20 |
+ t.usec
+
+ Time.send(:_load, [high, low].pack("VV")).should == t
+ end
+
+ it "loads a time object in the old UNIX timestamp based format" do
+ t = Time.local(2000, 1, 15, 20, 1, 1, 203)
+ timestamp = t.to_i
+
+ high = timestamp & ((1 << 31) - 1)
+
+ low = t.usec
+
+ Time.send(:_load, [high, low].pack("VV")).should == t
+ end
+
+ it "loads MRI's marshaled time format" do
+ t = Marshal.load("\004\bu:\tTime\r\320\246\e\200\320\001\r\347")
+ t.utc
+
+ t.to_s.should == "2010-10-22 16:57:48 UTC"
+ end
+
+ with_feature :encoding do
+ it "treats the data as binary data" do
+ data = "\x04\bu:\tTime\r\fM\x1C\xC0\x00\x00\xD0\xBE"
+ data.force_encoding Encoding::UTF_8
+ t = Marshal.load(data)
+ t.to_s.should == "2013-04-08 12:47:45 UTC"
+ end
+ end
+end
diff --git a/spec/ruby/core/time/asctime_spec.rb b/spec/ruby/core/time/asctime_spec.rb
new file mode 100644
index 0000000000..a41ee531e4
--- /dev/null
+++ b/spec/ruby/core/time/asctime_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/asctime'
+
+describe "Time#asctime" do
+ it_behaves_like :time_asctime, :asctime
+end
diff --git a/spec/ruby/core/time/at_spec.rb b/spec/ruby/core/time/at_spec.rb
new file mode 100644
index 0000000000..7c66104156
--- /dev/null
+++ b/spec/ruby/core/time/at_spec.rb
@@ -0,0 +1,201 @@
+require_relative '../../spec_helper'
+
+describe "Time.at" do
+ describe "passed Numeric" do
+ it "returns a Time object representing the given number of Integer seconds since 1970-01-01 00:00:00 UTC" do
+ Time.at(1184027924).getgm.asctime.should == "Tue Jul 10 00:38:44 2007"
+ end
+
+ it "returns a Time object representing the given number of Float seconds since 1970-01-01 00:00:00 UTC" do
+ t = Time.at(10.5)
+ t.usec.should == 500000.0
+ t.should_not == Time.at(10)
+ end
+
+ it "returns a non-UTC Time" do
+ Time.at(1184027924).utc?.should == false
+ end
+
+ it "returns a subclass instance on a Time subclass" do
+ c = Class.new(Time)
+ t = c.at(0)
+ t.should be_an_instance_of(c)
+ end
+
+ it "roundtrips a Rational produced by #to_r" do
+ t = Time.now()
+ t2 = Time.at(t.to_r)
+
+ t2.should == t
+ t2.usec.should == t.usec
+ t2.nsec.should == t.nsec
+ end
+
+ describe "passed BigDecimal" do
+ it "doesn't round input value" do
+ require 'bigdecimal'
+ Time.at(BigDecimal('1.1')).to_f.should == 1.1
+ end
+ end
+ end
+
+ describe "passed Time" do
+ it "creates a new time object with the value given by time" do
+ t = Time.now
+ Time.at(t).inspect.should == t.inspect
+ end
+
+ it "creates a dup time object with the value given by time" do
+ t1 = Time.new
+ t2 = Time.at(t1)
+ t1.should_not equal t2
+ end
+
+ it "returns a UTC time if the argument is UTC" do
+ t = Time.now.getgm
+ Time.at(t).utc?.should == true
+ end
+
+ it "returns a non-UTC time if the argument is non-UTC" do
+ t = Time.now
+ Time.at(t).utc?.should == false
+ end
+
+ it "returns a subclass instance" do
+ c = Class.new(Time)
+ t = c.at(Time.now)
+ t.should be_an_instance_of(c)
+ end
+ end
+
+ describe "passed non-Time, non-Numeric" do
+ it "raises a TypeError with a String argument" do
+ lambda { Time.at("0") }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError with a nil argument" do
+ lambda { Time.at(nil) }.should raise_error(TypeError)
+ end
+
+ describe "with an argument that responds to #to_int" do
+ it "coerces using #to_int" do
+ o = mock('integer')
+ o.should_receive(:to_int).and_return(0)
+ Time.at(o).should == Time.at(0)
+ end
+ end
+
+ describe "with an argument that responds to #to_r" do
+ it "coerces using #to_r" do
+ o = mock_numeric('rational')
+ o.should_receive(:to_r).and_return(Rational(5, 2))
+ Time.at(o).should == Time.at(Rational(5, 2))
+ end
+ end
+ end
+
+ describe "passed [Integer, Numeric]" do
+ it "returns a Time object representing the given number of seconds and Integer microseconds since 1970-01-01 00:00:00 UTC" do
+ t = Time.at(10, 500000)
+ t.tv_sec.should == 10
+ t.tv_usec.should == 500000
+ end
+
+ it "returns a Time object representing the given number of seconds and Float microseconds since 1970-01-01 00:00:00 UTC" do
+ t = Time.at(10, 500.500)
+ t.tv_sec.should == 10
+ t.tv_nsec.should == 500500
+ end
+ end
+
+ describe "with a second argument that responds to #to_int" do
+ it "coerces using #to_int" do
+ o = mock('integer')
+ o.should_receive(:to_int).and_return(10)
+ Time.at(0, o).should == Time.at(0, 10)
+ end
+ end
+
+ describe "with a second argument that responds to #to_r" do
+ it "coerces using #to_r" do
+ o = mock_numeric('rational')
+ o.should_receive(:to_r).and_return(Rational(5, 2))
+ Time.at(0, o).should == Time.at(0, Rational(5, 2))
+ end
+ end
+
+ describe "passed [Integer, nil]" do
+ it "raises a TypeError" do
+ lambda { Time.at(0, nil) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "passed [Integer, String]" do
+ it "raises a TypeError" do
+ lambda { Time.at(0, "0") }.should raise_error(TypeError)
+ end
+ end
+
+ describe "passed [Time, Integer]" do
+ # #8173
+ it "raises a TypeError" do
+ lambda { Time.at(Time.now, 500000) }.should raise_error(TypeError)
+ end
+ end
+
+ ruby_version_is "2.5" do
+ describe "passed [Time, Numeric, format]" do
+ context ":nanosecond format" do
+ it "traits second argument as nanoseconds" do
+ Time.at(0, 123456789, :nanosecond).nsec.should == 123456789
+ end
+ end
+
+ context ":nsec format" do
+ it "traits second argument as nanoseconds" do
+ Time.at(0, 123456789, :nsec).nsec.should == 123456789
+ end
+ end
+
+ context ":microsecond format" do
+ it "traits second argument as microseconds" do
+ Time.at(0, 123456, :microsecond).nsec.should == 123456000
+ end
+ end
+
+ context ":usec format" do
+ it "traits second argument as microseconds" do
+ Time.at(0, 123456, :usec).nsec.should == 123456000
+ end
+ end
+
+ context ":millisecond format" do
+ it "traits second argument as milliseconds" do
+ Time.at(0, 123, :millisecond).nsec.should == 123000000
+ end
+ end
+
+ context "not supported format" do
+ it "raises ArgumentError" do
+ ->() { Time.at(0, 123456, 2) }.should raise_error(ArgumentError)
+ ->() { Time.at(0, 123456, nil) }.should raise_error(ArgumentError)
+ ->() { Time.at(0, 123456, :invalid) }.should raise_error(ArgumentError)
+ end
+
+ it "does not try to convert format to Symbol with #to_sym" do
+ format = "usec"
+ format.should_not_receive(:to_sym)
+ -> () { Time.at(0, 123456, format) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "supports Float second argument" do
+ Time.at(0, 123456789.500, :nanosecond).nsec.should == 123456789
+ Time.at(0, 123456789.500, :nsec).nsec.should == 123456789
+ Time.at(0, 123456.500, :microsecond).nsec.should == 123456500
+ Time.at(0, 123456.500, :usec).nsec.should == 123456500
+ Time.at(0, 123.500, :millisecond).nsec.should == 123500000
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/time/comparison_spec.rb b/spec/ruby/core/time/comparison_spec.rb
new file mode 100644
index 0000000000..5b53c9fb50
--- /dev/null
+++ b/spec/ruby/core/time/comparison_spec.rb
@@ -0,0 +1,104 @@
+require_relative '../../spec_helper'
+
+describe "Time#<=>" do
+ it "returns 1 if the first argument is a point in time after the second argument" do
+ (Time.now <=> Time.at(0)).should == 1
+ end
+
+ it "returns 1 if the first argument is a point in time after the second argument (down to a millisecond)" do
+ (Time.at(0, 1000) <=> Time.at(0, 0)).should == 1
+ (Time.at(1202778512, 1000) <=> Time.at(1202778512, 999)).should == 1
+ end
+
+ it "returns 1 if the first argument is a point in time after the second argument (down to a microsecond)" do
+ (Time.at(0, 100) <=> Time.at(0, 0)).should == 1
+ (Time.at(1202778512, 100) <=> Time.at(1202778512, 99)).should == 1
+ end
+
+ it "returns 0 if time is the same as other" do
+ (Time.at(1202778513) <=> Time.at(1202778513)).should == 0
+ (Time.at(100, 100) <=> Time.at(100, 100)).should == 0
+ end
+
+ it "returns -1 if the first argument is a point in time before the second argument" do
+ (Time.at(0) <=> Time.now).should == -1
+ (Time.at(100, 100) <=> Time.at(101, 100)).should == -1
+ end
+
+ it "returns -1 if the first argument is a point in time before the second argument (down to a millisecond)" do
+ (Time.at(0, 0) <=> Time.at(0, 1000)).should == -1
+ end
+
+ it "returns -1 if the first argument is a point in time before the second argument (down to a microsecond)" do
+ (Time.at(0, 0) <=> Time.at(0, 100)).should == -1
+ end
+
+ it "returns 1 if the first argument is a fraction of a microsecond after the second argument" do
+ (Time.at(100, Rational(1,1000)) <=> Time.at(100, 0)).should == 1
+ end
+
+ it "returns 0 if time is the same as other, including fractional microseconds" do
+ (Time.at(100, Rational(1,1000)) <=> Time.at(100, Rational(1,1000))).should == 0
+ end
+
+ it "returns -1 if the first argument is a fraction of a microsecond before the second argument" do
+ (Time.at(100, 0) <=> Time.at(100, Rational(1,1000))).should == -1
+ end
+
+ it "returns nil when compared to an Integer because Time does not respond to #coerce" do
+ time = Time.at(1)
+ time.respond_to?(:coerce).should == false
+ time.should_receive(:respond_to?).exactly(2).and_return(false)
+ -> {
+ (time <=> 2).should == nil
+ (2 <=> time).should == nil
+ }.should_not complain
+ end
+
+ describe "given a non-Time argument" do
+ it "returns nil if argument <=> self returns nil" do
+ t = Time.now
+ obj = mock('time')
+ obj.should_receive(:<=>).with(t).and_return(nil)
+ (t <=> obj).should == nil
+ end
+
+ it "returns -1 if argument <=> self is greater than 0" do
+ t = Time.now
+ r = mock('r')
+ r.should_receive(:>).with(0).and_return(true)
+ obj = mock('time')
+ obj.should_receive(:<=>).with(t).and_return(r)
+ (t <=> obj).should == -1
+ end
+
+ it "returns 1 if argument <=> self is not greater than 0 and is less than 0" do
+ t = Time.now
+ r = mock('r')
+ r.should_receive(:>).with(0).and_return(false)
+ r.should_receive(:<).with(0).and_return(true)
+ obj = mock('time')
+ obj.should_receive(:<=>).with(t).and_return(r)
+ (t <=> obj).should == 1
+ end
+
+ it "returns 0 if argument <=> self is neither greater than 0 nor less than 0" do
+ t = Time.now
+ r = mock('r')
+ r.should_receive(:>).with(0).and_return(false)
+ r.should_receive(:<).with(0).and_return(false)
+ obj = mock('time')
+ obj.should_receive(:<=>).with(t).and_return(r)
+ (t <=> obj).should == 0
+ end
+
+ it "returns nil if argument also uses an inverse comparison for <=>" do
+ t = Time.now
+ r = mock('r')
+ def r.<=>(other); other <=> self; end
+ r.should_receive(:<=>).once
+
+ (t <=> r).should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/time/ctime_spec.rb b/spec/ruby/core/time/ctime_spec.rb
new file mode 100644
index 0000000000..57e7cfd9ce
--- /dev/null
+++ b/spec/ruby/core/time/ctime_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/asctime'
+
+describe "Time#ctime" do
+ it_behaves_like :time_asctime, :ctime
+end
diff --git a/spec/ruby/core/time/day_spec.rb b/spec/ruby/core/time/day_spec.rb
new file mode 100644
index 0000000000..895bcd7a86
--- /dev/null
+++ b/spec/ruby/core/time/day_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/day'
+
+describe "Time#day" do
+ it_behaves_like :time_day, :day
+end
diff --git a/spec/ruby/core/time/dst_spec.rb b/spec/ruby/core/time/dst_spec.rb
new file mode 100644
index 0000000000..436240aae5
--- /dev/null
+++ b/spec/ruby/core/time/dst_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/isdst'
+
+describe "Time#dst?" do
+ it_behaves_like :time_isdst, :dst?
+end
diff --git a/spec/ruby/core/time/dup_spec.rb b/spec/ruby/core/time/dup_spec.rb
new file mode 100644
index 0000000000..ee2ff7dbba
--- /dev/null
+++ b/spec/ruby/core/time/dup_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../../spec_helper'
+
+describe "Time#dup" do
+ it "returns a Time object that represents the same time" do
+ t = Time.at(100)
+ t.dup.tv_sec.should == t.tv_sec
+ end
+
+ it "copies the gmt state flag" do
+ Time.now.gmtime.dup.gmt?.should == true
+ end
+
+ it "returns an independent Time object" do
+ t = Time.now
+ t2 = t.dup
+ t.gmtime
+
+ t2.gmt?.should == false
+ end
+
+ it "returns a subclass instance" do
+ c = Class.new(Time)
+ t = c.now
+
+ t.should be_an_instance_of(c)
+ t.dup.should be_an_instance_of(c)
+ end
+
+ it "returns a clone of Time instance" do
+ c = Time.dup
+ t = c.now
+
+ t.should be_an_instance_of(c)
+ t.should_not be_an_instance_of(Time)
+
+ t.dup.should be_an_instance_of(c)
+ t.dup.should_not be_an_instance_of(Time)
+ end
+
+ it "does not copy frozen status from the original" do
+ t = Time.now
+ t.freeze
+ t2 = t.dup
+ t2.frozen?.should be_false
+ end
+end
diff --git a/spec/ruby/core/time/eql_spec.rb b/spec/ruby/core/time/eql_spec.rb
new file mode 100644
index 0000000000..2ffb4eec96
--- /dev/null
+++ b/spec/ruby/core/time/eql_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+
+describe "Time#eql?" do
+ it "returns true if self and other have the same whole number of seconds" do
+ Time.at(100).should eql(Time.at(100))
+ end
+
+ it "returns false if self and other have differing whole numbers of seconds" do
+ Time.at(100).should_not eql(Time.at(99))
+ end
+
+ it "returns true if self and other have the same number of microseconds" do
+ Time.at(100, 100).should eql(Time.at(100, 100))
+ end
+
+ it "returns false if self and other have differing numbers of microseconds" do
+ Time.at(100, 100).should_not eql(Time.at(100, 99))
+ end
+
+ it "returns false if self and other have differing fractional microseconds" do
+ Time.at(100, Rational(100,1000)).should_not eql(Time.at(100, Rational(99,1000)))
+ end
+
+ it "returns false when given a non-time value" do
+ Time.at(100, 100).should_not eql("100")
+ Time.at(100, 100).should_not eql(100)
+ Time.at(100, 100).should_not eql(100.1)
+ end
+end
diff --git a/spec/ruby/core/time/fixtures/classes.rb b/spec/ruby/core/time/fixtures/classes.rb
new file mode 100644
index 0000000000..d89e4911c8
--- /dev/null
+++ b/spec/ruby/core/time/fixtures/classes.rb
@@ -0,0 +1,26 @@
+module TimeSpecs
+
+ class SubTime < Time; end
+
+ class MethodHolder
+ class << self
+ define_method(:now, &Time.method(:now))
+ define_method(:new, &Time.method(:new))
+ end
+ end
+
+ Timezone = Struct.new(:name, :abbr, :offset)
+ class Timezone
+ def utc_offset(t = nil)
+ offset
+ end
+
+ def local_to_utc(t)
+ t - utc_offset(t)
+ end
+
+ def utc_to_local(t)
+ t + utc_offset(t)
+ end
+ end
+end
diff --git a/spec/ruby/core/time/friday_spec.rb b/spec/ruby/core/time/friday_spec.rb
new file mode 100644
index 0000000000..27f9e1dbe5
--- /dev/null
+++ b/spec/ruby/core/time/friday_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#friday?" do
+ it "returns true if time represents Friday" do
+ Time.local(2000, 1, 7).friday?.should == true
+ end
+
+ it "returns false if time doesn't represent Friday" do
+ Time.local(2000, 1, 1).friday?.should == false
+ end
+end
diff --git a/spec/ruby/core/time/getgm_spec.rb b/spec/ruby/core/time/getgm_spec.rb
new file mode 100644
index 0000000000..b5d45b1d9f
--- /dev/null
+++ b/spec/ruby/core/time/getgm_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/getgm'
+
+describe "Time#getgm" do
+ it_behaves_like :time_getgm, :getgm
+end
diff --git a/spec/ruby/core/time/getlocal_spec.rb b/spec/ruby/core/time/getlocal_spec.rb
new file mode 100644
index 0000000000..87a412f41c
--- /dev/null
+++ b/spec/ruby/core/time/getlocal_spec.rb
@@ -0,0 +1,114 @@
+require_relative '../../spec_helper'
+
+describe "Time#getlocal" do
+ it "returns a new time which is the local representation of time" do
+ # Testing with America/Regina here because it doesn't have DST.
+ with_timezone("CST", -6) do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime.should == Time.local(2007, 1, 9, 6, 0, 0)
+ end
+ end
+
+ it "returns a Time with UTC offset specified as an Integer number of seconds" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal(3630)
+ t.should == Time.new(2007, 1, 9, 13, 0, 30, 3630)
+ t.utc_offset.should == 3630
+ end
+
+ platform_is_not :windows do
+ it "returns a new time with the correct utc_offset according to the set timezone" do
+ t = Time.new(2005, 2, 27, 22, 50, 0, -3600)
+ t.utc_offset.should == -3600
+
+ with_timezone("America/New_York") do
+ t.getlocal.utc_offset.should == -18000
+ end
+ end
+ end
+
+ describe "with an argument that responds to #to_int" do
+ it "coerces using #to_int" do
+ o = mock('integer')
+ o.should_receive(:to_int).and_return(3630)
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal(o)
+ t.should == Time.new(2007, 1, 9, 13, 0, 30, 3630)
+ t.utc_offset.should == 3630
+ end
+ end
+
+ it "returns a Time with a UTC offset of the specified number of Rational seconds" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal(Rational(7201, 2))
+ t.should == Time.new(2007, 1, 9, 13, 0, Rational(1, 2), Rational(7201, 2))
+ t.utc_offset.should eql(Rational(7201, 2))
+ end
+
+ describe "with an argument that responds to #to_r" do
+ it "coerces using #to_r" do
+ o = mock_numeric('rational')
+ o.should_receive(:to_r).and_return(Rational(7201, 2))
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal(o)
+ t.should == Time.new(2007, 1, 9, 13, 0, Rational(1, 2), Rational(7201, 2))
+ t.utc_offset.should eql(Rational(7201, 2))
+ end
+ end
+
+ it "returns a Time with a UTC offset specified as +HH:MM" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal("+01:00")
+ t.should == Time.new(2007, 1, 9, 13, 0, 0, 3600)
+ t.utc_offset.should == 3600
+ end
+
+ it "returns a Time with a UTC offset specified as -HH:MM" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal("-01:00")
+ t.should == Time.new(2007, 1, 9, 11, 0, 0, -3600)
+ t.utc_offset.should == -3600
+ end
+
+ describe "with an argument that responds to #to_str" do
+ it "coerces using #to_str" do
+ o = mock('string')
+ o.should_receive(:to_str).and_return("+01:00")
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal(o)
+ t.should == Time.new(2007, 1, 9, 13, 0, 0, 3600)
+ t.utc_offset.should == 3600
+ end
+ end
+
+ it "raises ArgumentError if the String argument is not of the form (+|-)HH:MM" do
+ t = Time.now
+ lambda { t.getlocal("3600") }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the String argument is not in an ASCII-compatible encoding" do
+ t = Time.now
+ lambda { t.getlocal("-01:00".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
+ t = Time.new
+ t.getlocal(-86400 + 1).utc_offset.should == (-86400 + 1)
+ lambda { t.getlocal(-86400) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the argument represents a value greater than or equal to 86400 seconds" do
+ t = Time.new
+ t.getlocal(86400 - 1).utc_offset.should == (86400 - 1)
+ lambda { t.getlocal(86400) }.should raise_error(ArgumentError)
+ end
+
+ ruby_version_is "2.6" do
+ describe "with a timezone argument" do
+ it "returns a Time in the timezone" do
+ zone = mock('timezone')
+ zone.should_receive(:utc_to_local).and_return(Time.utc(2000, 1, 1, 17, 30, 0))
+ t = Time.utc(2000, 1, 1, 12, 0, 0)
+ tv = t.to_i
+ t = t.getlocal(zone)
+ t.to_a[0, 6].should == [0, 30, 17, 1, 1, 2000]
+ t.utc_offset.should == 19800
+ t.to_i.should == tv
+ t.zone.should == zone
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/time/getutc_spec.rb b/spec/ruby/core/time/getutc_spec.rb
new file mode 100644
index 0000000000..0cd9c17b00
--- /dev/null
+++ b/spec/ruby/core/time/getutc_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/getgm'
+
+describe "Time#getutc" do
+ it_behaves_like :time_getgm, :getutc
+end
diff --git a/spec/ruby/core/time/gm_spec.rb b/spec/ruby/core/time/gm_spec.rb
new file mode 100644
index 0000000000..26dffbcedc
--- /dev/null
+++ b/spec/ruby/core/time/gm_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'shared/gm'
+require_relative 'shared/time_params'
+
+describe "Time.gm" do
+ it_behaves_like :time_gm, :gm
+ it_behaves_like :time_params, :gm
+ it_behaves_like :time_params_10_arg, :gm
+ it_behaves_like :time_params_microseconds, :gm
+end
diff --git a/spec/ruby/core/time/gmt_offset_spec.rb b/spec/ruby/core/time/gmt_offset_spec.rb
new file mode 100644
index 0000000000..df417e6e4e
--- /dev/null
+++ b/spec/ruby/core/time/gmt_offset_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/gmt_offset'
+
+describe "Time#gmt_offset" do
+ it_behaves_like :time_gmt_offset, :gmt_offset
+end
diff --git a/spec/ruby/core/time/gmt_spec.rb b/spec/ruby/core/time/gmt_spec.rb
new file mode 100644
index 0000000000..7e85749d5b
--- /dev/null
+++ b/spec/ruby/core/time/gmt_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+
+describe "Time#gmt?" do
+ it "returns true if time represents a time in UTC (GMT)" do
+ Time.now.gmt?.should == false
+ Time.now.gmtime.gmt?.should == true
+ end
+end
diff --git a/spec/ruby/core/time/gmtime_spec.rb b/spec/ruby/core/time/gmtime_spec.rb
new file mode 100644
index 0000000000..d965cd541d
--- /dev/null
+++ b/spec/ruby/core/time/gmtime_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/gmtime'
+
+describe "Time#gmtime" do
+ it_behaves_like :time_gmtime, :gmtime
+end
diff --git a/spec/ruby/core/time/gmtoff_spec.rb b/spec/ruby/core/time/gmtoff_spec.rb
new file mode 100644
index 0000000000..fa28520e9e
--- /dev/null
+++ b/spec/ruby/core/time/gmtoff_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/gmt_offset'
+
+describe "Time#gmtoff" do
+ it_behaves_like :time_gmt_offset, :gmtoff
+end
diff --git a/spec/ruby/core/time/hash_spec.rb b/spec/ruby/core/time/hash_spec.rb
new file mode 100644
index 0000000000..577aaf9b0a
--- /dev/null
+++ b/spec/ruby/core/time/hash_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#hash" do
+ it "returns a Fixnum" do
+ Time.at(100).hash.should be_an_instance_of(Fixnum)
+ end
+
+ it "is stable" do
+ Time.at(1234).hash.should == Time.at(1234).hash
+ end
+end
diff --git a/spec/ruby/core/time/hour_spec.rb b/spec/ruby/core/time/hour_spec.rb
new file mode 100644
index 0000000000..ca69c25adb
--- /dev/null
+++ b/spec/ruby/core/time/hour_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "Time#hour" do
+ it "returns the hour of the day (0..23) for a local Time" do
+ with_timezone("CET", 1) do
+ Time.local(1970, 1, 1, 1).hour.should == 1
+ end
+ end
+
+ it "returns the hour of the day for a UTC Time" do
+ Time.utc(1970, 1, 1, 0).hour.should == 0
+ end
+
+ it "returns the hour of the day for a Time with a fixed offset" do
+ Time.new(2012, 1, 1, 0, 0, 0, -3600).hour.should == 0
+ end
+end
diff --git a/spec/ruby/core/time/inspect_spec.rb b/spec/ruby/core/time/inspect_spec.rb
new file mode 100644
index 0000000000..01e1dfdaa6
--- /dev/null
+++ b/spec/ruby/core/time/inspect_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/inspect'
+
+describe "Time#inspect" do
+ it_behaves_like :inspect, :inspect
+end
diff --git a/spec/ruby/core/time/isdst_spec.rb b/spec/ruby/core/time/isdst_spec.rb
new file mode 100644
index 0000000000..173230ca07
--- /dev/null
+++ b/spec/ruby/core/time/isdst_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/isdst'
+
+describe "Time#isdst" do
+ it_behaves_like :time_isdst, :isdst
+end
diff --git a/spec/ruby/core/time/local_spec.rb b/spec/ruby/core/time/local_spec.rb
new file mode 100644
index 0000000000..581ed171d5
--- /dev/null
+++ b/spec/ruby/core/time/local_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/local'
+require_relative 'shared/time_params'
+
+describe "Time.local" do
+ it_behaves_like :time_local, :local
+ it_behaves_like :time_local_10_arg, :local
+ it_behaves_like :time_params, :local
+ it_behaves_like :time_params_10_arg, :local
+ it_behaves_like :time_params_microseconds, :local
+end
diff --git a/spec/ruby/core/time/localtime_spec.rb b/spec/ruby/core/time/localtime_spec.rb
new file mode 100644
index 0000000000..7942653c78
--- /dev/null
+++ b/spec/ruby/core/time/localtime_spec.rb
@@ -0,0 +1,140 @@
+require_relative '../../spec_helper'
+
+describe "Time#localtime" do
+ it "converts self to local time, modifying the receiver" do
+ # Testing with America/Regina here because it doesn't have DST.
+ with_timezone("CST", -6) do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime
+ t.should == Time.local(2007, 1, 9, 6, 0, 0)
+ end
+ end
+
+ it "returns self" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime.should equal(t)
+ end
+
+ it "converts time to the UTC offset specified as an Integer number of seconds" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime(3630)
+ t.should == Time.new(2007, 1, 9, 13, 0, 30, 3630)
+ t.utc_offset.should == 3630
+ end
+
+ describe "on a frozen time" do
+ it "does not raise an error if already in the right time zone" do
+ time = Time.now
+ time.freeze
+ time.localtime.should equal(time)
+ end
+
+ it "raises a RuntimeError if the time has a different time zone" do
+ time = Time.gm(2007, 1, 9, 12, 0, 0)
+ time.freeze
+ lambda { time.localtime }.should raise_error(RuntimeError)
+ end
+ end
+
+ describe "with an argument that responds to #to_int" do
+ it "coerces using #to_int" do
+ o = mock('integer')
+ o.should_receive(:to_int).and_return(3630)
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime(o)
+ t.should == Time.new(2007, 1, 9, 13, 0, 30, 3630)
+ t.utc_offset.should == 3630
+ end
+ end
+
+ it "returns a Time with a UTC offset of the specified number of Rational seconds" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime(Rational(7201, 2))
+ t.should == Time.new(2007, 1, 9, 13, 0, Rational(1, 2), Rational(7201, 2))
+ t.utc_offset.should eql(Rational(7201, 2))
+ end
+
+ describe "with an argument that responds to #to_r" do
+ it "coerces using #to_r" do
+ o = mock_numeric('rational')
+ o.should_receive(:to_r).and_return(Rational(7201, 2))
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime(o)
+ t.should == Time.new(2007, 1, 9, 13, 0, Rational(1, 2), Rational(7201, 2))
+ t.utc_offset.should eql(Rational(7201, 2))
+ end
+ end
+
+ it "returns a Time with a UTC offset specified as +HH:MM" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime("+01:00")
+ t.should == Time.new(2007, 1, 9, 13, 0, 0, 3600)
+ t.utc_offset.should == 3600
+ end
+
+ it "returns a Time with a UTC offset specified as -HH:MM" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime("-01:00")
+ t.should == Time.new(2007, 1, 9, 11, 0, 0, -3600)
+ t.utc_offset.should == -3600
+ 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)
+ t.utc_offset.should == -3600
+
+ with_timezone("America/New_York") do
+ t.localtime
+ end
+
+ t.utc_offset.should == -18000
+ end
+
+ it "does nothing if already in a local time zone" do
+ time = with_timezone("America/New_York") do
+ Time.new(2005, 2, 27, 22, 50, 0)
+ end
+ zone = time.zone
+
+ with_timezone("Europe/Amsterdam") do
+ time.localtime
+ end
+
+ time.zone.should == zone
+ end
+ end
+
+ describe "with an argument that responds to #to_str" do
+ it "coerces using #to_str" do
+ o = mock('string')
+ o.should_receive(:to_str).and_return("+01:00")
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime(o)
+ t.should == Time.new(2007, 1, 9, 13, 0, 0, 3600)
+ t.utc_offset.should == 3600
+ end
+ end
+
+ it "raises ArgumentError if the String argument is not of the form (+|-)HH:MM" do
+ t = Time.now
+ lambda { t.localtime("3600") }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the String argument is not in an ASCII-compatible encoding" do
+ t = Time.now
+ lambda { t.localtime("-01:00".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
+ t = Time.new
+ t.localtime(-86400 + 1).utc_offset.should == (-86400 + 1)
+ lambda { t.localtime(-86400) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the argument represents a value greater than or equal to 86400 seconds" do
+ t = Time.new
+ t.localtime(86400 - 1).utc_offset.should == (86400 - 1)
+ lambda { t.localtime(86400) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/time/mday_spec.rb b/spec/ruby/core/time/mday_spec.rb
new file mode 100644
index 0000000000..3c21939890
--- /dev/null
+++ b/spec/ruby/core/time/mday_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/day'
+
+describe "Time#mday" do
+ it_behaves_like :time_day, :mday
+end
diff --git a/spec/ruby/core/time/min_spec.rb b/spec/ruby/core/time/min_spec.rb
new file mode 100644
index 0000000000..7d087d4046
--- /dev/null
+++ b/spec/ruby/core/time/min_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "Time#min" do
+ it "returns the minute of the hour (0..59) for a local Time" do
+ with_timezone("CET", 1) do
+ Time.local(1970, 1, 1, 0, 0).min.should == 0
+ end
+ end
+
+ it "returns the minute of the hour for a UTC Time" do
+ Time.utc(1970, 1, 1, 0, 0).min.should == 0
+ end
+
+ it "returns the minute of the hour for a Time with a fixed offset" do
+ Time.new(2012, 1, 1, 0, 0, 0, -3600).min.should == 0
+ end
+end
diff --git a/spec/ruby/core/time/minus_spec.rb b/spec/ruby/core/time/minus_spec.rb
new file mode 100644
index 0000000000..7c4b21f0d5
--- /dev/null
+++ b/spec/ruby/core/time/minus_spec.rb
@@ -0,0 +1,103 @@
+require_relative '../../spec_helper'
+
+describe "Time#-" do
+ it "decrements the time by the specified amount" do
+ (Time.at(100) - 100).should == Time.at(0)
+ (Time.at(100) - Time.at(99)).should == 1.0
+ end
+
+ it "understands negative subtractions" do
+ t = Time.at(100) - -1.3
+ t.usec.should == 300000
+ t.to_i.should == 101
+ end
+
+ #see [ruby-dev:38446]
+ it "accepts arguments that can be coerced into Rational" do
+ (obj = mock_numeric('10')).should_receive(:to_r).and_return(Rational(10))
+ (Time.at(100) - obj).should == Time.at(90)
+ end
+
+ it "raises a TypeError if given argument is a coercible String" do
+ lambda { Time.now - "1" }.should raise_error(TypeError)
+ lambda { Time.now - "0.1" }.should raise_error(TypeError)
+ lambda { Time.now - "1/3" }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError on argument that can't be coerced" do
+ lambda { Time.now - Object.new }.should raise_error(TypeError)
+ lambda { Time.now - "stuff" }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError on nil argument" do
+ lambda { Time.now - nil }.should raise_error(TypeError)
+ end
+
+ it "tracks microseconds" do
+ time = Time.at(0.777777)
+ time -= 0.654321
+ time.usec.should == 123456
+ time -= 1
+ time.usec.should == 123456
+ end
+
+ it "tracks microseconds from a Rational" do
+ time = Time.at(Rational(777_777, 1_000_000))
+ time -= Rational(654_321, 1_000_000)
+ time.usec.should == 123_456
+ time -= Rational(123_456, 1_000_000)
+ time.usec.should == 0
+ end
+
+ it "tracks nanoseconds" do
+ time = Time.at(Rational(999_999_999, 1_000_000_000))
+ time -= Rational(876_543_210, 1_000_000_000)
+ time.nsec.should == 123_456_789
+ time -= Rational(123_456_789, 1_000_000_000)
+ time.nsec.should == 0
+ end
+
+ it "maintains precision" do
+ time = Time.at(10) - Rational(1_000_000_000_000_001, 1_000_000_000_000_000)
+ time.should_not == Time.at(9)
+ end
+
+ it "maintains microseconds precision" do
+ time = Time.at(10) - Rational(1, 1_000_000)
+ time.usec.should == 999_999
+ end
+
+ it "maintains nanoseconds precision" do
+ time = Time.at(10) - Rational(1, 1_000_000_000)
+ time.nsec.should == 999_999_999
+ end
+
+ it "maintains subseconds precision" do
+ time = Time.at(0) - Rational(1_000_000_000_000_001, 1_000_000_000_000_000)
+ time.subsec.should == Rational(999_999_999_999_999, 1_000_000_000_000_000)
+ end
+
+ it "returns a UTC time if self is UTC" do
+ (Time.utc(2012) - 10).utc?.should == true
+ end
+
+ it "returns a non-UTC time if self is non-UTC" do
+ (Time.local(2012) - 10).utc?.should == false
+ end
+
+ it "returns a time with the same fixed offset as self" do
+ (Time.new(2012, 1, 1, 0, 0, 0, 3600) - 10).utc_offset.should == 3600
+ end
+
+ it "does not return a subclass instance" do
+ c = Class.new(Time)
+ x = c.now + 1
+ x.should be_an_instance_of(Time)
+ end
+
+ it "returns a time with nanoseconds precision between two time objects" do
+ time1 = Time.utc(2000, 1, 2, 23, 59, 59, Rational(999999999, 1000))
+ time2 = Time.utc(2000, 1, 2, 0, 0, 0, Rational(1, 1000))
+ (time1 - time2).should == 86_399.999999998
+ end
+end
diff --git a/spec/ruby/core/time/mktime_spec.rb b/spec/ruby/core/time/mktime_spec.rb
new file mode 100644
index 0000000000..78a6a6e772
--- /dev/null
+++ b/spec/ruby/core/time/mktime_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/local'
+require_relative 'shared/time_params'
+
+describe "Time.mktime" do
+ it_behaves_like :time_local, :mktime
+ it_behaves_like :time_local_10_arg, :mktime
+ it_behaves_like :time_params, :mktime
+ it_behaves_like :time_params_10_arg, :mktime
+ it_behaves_like :time_params_microseconds, :mktime
+end
diff --git a/spec/ruby/core/time/mon_spec.rb b/spec/ruby/core/time/mon_spec.rb
new file mode 100644
index 0000000000..f41b39648b
--- /dev/null
+++ b/spec/ruby/core/time/mon_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/month'
+
+describe "Time#mon" do
+ it_behaves_like :time_month, :mon
+end
diff --git a/spec/ruby/core/time/monday_spec.rb b/spec/ruby/core/time/monday_spec.rb
new file mode 100644
index 0000000000..c5c43a5c3e
--- /dev/null
+++ b/spec/ruby/core/time/monday_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#monday?" do
+ it "returns true if time represents Monday" do
+ Time.local(2000, 1, 3).monday?.should == true
+ end
+
+ it "returns false if time doesn't represent Monday" do
+ Time.local(2000, 1, 1).monday?.should == false
+ end
+end
diff --git a/spec/ruby/core/time/month_spec.rb b/spec/ruby/core/time/month_spec.rb
new file mode 100644
index 0000000000..81e20384ab
--- /dev/null
+++ b/spec/ruby/core/time/month_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/month'
+
+describe "Time#month" do
+ it_behaves_like :time_month, :month
+end
diff --git a/spec/ruby/core/time/new_spec.rb b/spec/ruby/core/time/new_spec.rb
new file mode 100644
index 0000000000..8d32c4e492
--- /dev/null
+++ b/spec/ruby/core/time/new_spec.rb
@@ -0,0 +1,127 @@
+require_relative '../../spec_helper'
+require_relative 'shared/now'
+require_relative 'shared/local'
+require_relative 'shared/time_params'
+
+describe "Time.new" do
+ it_behaves_like :time_now, :new
+end
+
+describe "Time.new" do
+ it_behaves_like :time_local, :new
+ it_behaves_like :time_params, :new
+end
+
+describe "Time.new with a utc_offset argument" do
+ it "returns a non-UTC time" do
+ Time.new(2000, 1, 1, 0, 0, 0, 0).utc?.should == false
+ end
+
+ it "returns a Time with a UTC offset of the specified number of Integer seconds" do
+ Time.new(2000, 1, 1, 0, 0, 0, 123).utc_offset.should == 123
+ end
+
+ describe "with an argument that responds to #to_int" do
+ it "coerces using #to_int" do
+ o = mock('integer')
+ o.should_receive(:to_int).and_return(123)
+ Time.new(2000, 1, 1, 0, 0, 0, o).utc_offset.should == 123
+ end
+ end
+
+ it "returns a Time with a UTC offset of the specified number of Rational seconds" do
+ Time.new(2000, 1, 1, 0, 0, 0, Rational(5, 2)).utc_offset.should eql(Rational(5, 2))
+ end
+
+ describe "with an argument that responds to #to_r" do
+ it "coerces using #to_r" do
+ o = mock_numeric('rational')
+ o.should_receive(:to_r).and_return(Rational(5, 2))
+ Time.new(2000, 1, 1, 0, 0, 0, o).utc_offset.should eql(Rational(5, 2))
+ end
+ end
+
+ it "returns a Time with a UTC offset specified as +HH:MM" do
+ Time.new(2000, 1, 1, 0, 0, 0, "+05:30").utc_offset.should == 19800
+ end
+
+ it "returns a Time with a UTC offset specified as -HH:MM" do
+ Time.new(2000, 1, 1, 0, 0, 0, "-04:10").utc_offset.should == -15000
+ end
+
+ it "returns a Time with a UTC offset specified as +HH:MM:SS" do
+ Time.new(2000, 1, 1, 0, 0, 0, "+05:30:37").utc_offset.should == 19837
+ end
+
+ it "returns a Time with a UTC offset specified as -HH:MM" do
+ Time.new(2000, 1, 1, 0, 0, 0, "-04:10:43").utc_offset.should == -15043
+ end
+
+ describe "with an argument that responds to #to_str" do
+ it "coerces using #to_str" do
+ o = mock('string')
+ o.should_receive(:to_str).and_return("+05:30")
+ Time.new(2000, 1, 1, 0, 0, 0, o).utc_offset.should == 19800
+ 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)
+ t.utc_offset.should == -28800
+ t.zone.should == "PST"
+ end
+ end
+
+ # [Bug #8679], r47676
+ it "disallows a value for minutes greater than 59" do
+ lambda {
+ Time.new(2000, 1, 1, 0, 0, 0, "+01:60")
+ }.should raise_error(ArgumentError)
+ lambda {
+ Time.new(2000, 1, 1, 0, 0, 0, "+01:99")
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the String argument is not of the form (+|-)HH:MM" do
+ lambda { Time.new(2000, 1, 1, 0, 0, 0, "3600") }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the hour value is greater than 23" do
+ lambda { Time.new(2000, 1, 1, 0, 0, 0, "+24:00") }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the String argument is not in an ASCII-compatible encoding" do
+ lambda { 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
+ Time.new(2000, 1, 1, 0, 0, 0, -86400 + 1).utc_offset.should == (-86400 + 1)
+ lambda { Time.new(2000, 1, 1, 0, 0, 0, -86400) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the argument represents a value greater than or equal to 86400 seconds" do
+ Time.new(2000, 1, 1, 0, 0, 0, 86400 - 1).utc_offset.should == (86400 - 1)
+ lambda { Time.new(2000, 1, 1, 0, 0, 0, 86400) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the seconds argument is negative" do
+ lambda { 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
+ lambda { Time.new(2000, 1, 1, 0, 0, 0, 1000000000) }.should raise_error(ArgumentError)
+ end
+end
+
+ruby_version_is "2.6" do
+ describe "Time.new with a timezone argument" do
+ it "returns a Time correspoinding to UTC time returned by local_to_utc" do
+ zone = TimeSpecs::Timezone.new("Asia/Colombo", "MMT", (5*3600+30*60))
+ t = Time.new(2000, 1, 1, 12, 0, 0, zone)
+ t.to_a[0, 6].should == [0, 0, 12, 1, 1, 2000]
+ t.utc_offset.should == 19800
+ t.zone.should == zone
+ end
+ end
+end
diff --git a/spec/ruby/core/time/now_spec.rb b/spec/ruby/core/time/now_spec.rb
new file mode 100644
index 0000000000..7dc7951996
--- /dev/null
+++ b/spec/ruby/core/time/now_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/now'
+
+describe "Time.now" do
+ it_behaves_like :time_now, :now
+end
diff --git a/spec/ruby/core/time/nsec_spec.rb b/spec/ruby/core/time/nsec_spec.rb
new file mode 100644
index 0000000000..9338eb435a
--- /dev/null
+++ b/spec/ruby/core/time/nsec_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+
+describe "Time#nsec" do
+ it "returns 0 for a Time constructed with a whole number of seconds" do
+ Time.at(100).nsec.should == 0
+ end
+
+ it "returns the nanoseconds part of a Time constructed with a Float number of seconds" do
+ Time.at(10.75).nsec.should == 750_000_000
+ end
+
+ it "returns the nanoseconds part of a Time constructed with an Integer number of microseconds" do
+ Time.at(0, 999_999).nsec.should == 999_999_000
+ end
+
+ it "returns the nanoseconds part of a Time constructed with an Float number of microseconds" do
+ Time.at(0, 3.75).nsec.should == 3750
+ end
+
+ it "returns the nanoseconds part of a Time constructed with a Rational number of seconds" do
+ Time.at(Rational(3, 2)).nsec.should == 500_000_000
+ end
+
+ it "returns the nanoseconds part of a Time constructed with an Rational number of microseconds" do
+ Time.at(0, Rational(99, 10)).nsec.should == 9900
+ end
+
+ it "returns a positive value for dates before the epoch" do
+ Time.utc(1969, 11, 12, 13, 18, 57, 404240).nsec.should == 404240000
+ end
+end
diff --git a/spec/ruby/core/time/plus_spec.rb b/spec/ruby/core/time/plus_spec.rb
new file mode 100644
index 0000000000..0861e9c9f6
--- /dev/null
+++ b/spec/ruby/core/time/plus_spec.rb
@@ -0,0 +1,113 @@
+require_relative '../../spec_helper'
+
+describe "Time#+" do
+ it "increments the time by the specified amount" do
+ (Time.at(0) + 100).should == Time.at(100)
+ end
+
+ it "is a commutative operator" do
+ (Time.at(1.1) + 0.9).should == Time.at(0.9) + 1.1
+ end
+
+ it "adds a negative Float" do
+ t = Time.at(100) + -1.3
+ t.usec.should == 699999
+ t.to_i.should == 98
+ end
+
+ it "raises a TypeError if given argument is a coercible String" do
+ lambda { Time.now + "1" }.should raise_error(TypeError)
+ lambda { Time.now + "0.1" }.should raise_error(TypeError)
+ lambda { Time.now + "1/3" }.should raise_error(TypeError)
+ end
+
+ it "increments the time by the specified amount as rational numbers" do
+ (Time.at(Rational(11, 10)) + Rational(9, 10)).should == Time.at(2)
+ end
+
+ it "accepts arguments that can be coerced into Rational" do
+ (obj = mock_numeric('10')).should_receive(:to_r).and_return(Rational(10))
+ (Time.at(100) + obj).should == Time.at(110)
+ end
+
+ it "raises TypeError on argument that can't be coerced into Rational" do
+ lambda { Time.now + Object.new }.should raise_error(TypeError)
+ lambda { Time.now + "stuff" }.should raise_error(TypeError)
+ end
+
+ it "returns a UTC time if self is UTC" do
+ (Time.utc(2012) + 10).utc?.should == true
+ end
+
+ it "returns a non-UTC time if self is non-UTC" do
+ (Time.local(2012) + 10).utc?.should == false
+ end
+
+ it "returns a time with the same fixed offset as self" do
+ (Time.new(2012, 1, 1, 0, 0, 0, 3600) + 10).utc_offset.should == 3600
+ end
+
+ ruby_version_is "2.6" do
+ it "returns a time with the same timezone as self" do
+ zone = mock("timezone")
+ zone.should_receive(:local_to_utc).and_return(Time.utc(2012, 1, 1, 6, 30, 0))
+ zone.should_receive(:utc_to_local).and_return(Time.utc(2012, 1, 1, 12, 0, 10))
+ t = Time.new(2012, 1, 1, 12, 0, 0, zone) + 10
+ t.zone.should == zone
+ t.utc_offset.should == 19800
+ t.to_a[0, 6].should == [10, 0, 12, 1, 1, 2012]
+ t.should == Time.utc(2012, 1, 1, 6, 30, 10)
+ end
+ end
+
+ it "does not return a subclass instance" do
+ c = Class.new(Time)
+ x = c.now + 1
+ x.should be_an_instance_of(Time)
+ end
+
+ it "raises TypeError on Time argument" do
+ lambda { Time.now + Time.now }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError on nil argument" do
+ lambda { Time.now + nil }.should raise_error(TypeError)
+ end
+
+ #see [ruby-dev:38446]
+ it "tracks microseconds" do
+ time = Time.at(0)
+ time += Rational(123_456, 1_000_000)
+ time.usec.should == 123_456
+ time += Rational(654_321, 1_000_000)
+ time.usec.should == 777_777
+ end
+
+ it "tracks nanoseconds" do
+ time = Time.at(0)
+ time += Rational(123_456_789, 1_000_000_000)
+ time.nsec.should == 123_456_789
+ time += Rational(876_543_210, 1_000_000_000)
+ time.nsec.should == 999_999_999
+ end
+
+ it "maintains precision" do
+ t = Time.at(0) + Rational(8_999_999_999_999_999, 1_000_000_000_000_000)
+ t.should_not == Time.at(9)
+ end
+
+ it "maintains microseconds precision" do
+ time = Time.at(0) + Rational(8_999_999_999_999_999, 1_000_000_000_000_000)
+ time.usec.should == 999_999
+ end
+
+ it "maintains nanoseconds precision" do
+ time = Time.at(0) + Rational(8_999_999_999_999_999, 1_000_000_000_000_000)
+ time.nsec.should == 999_999_999
+ end
+
+ it "maintains subseconds precision" do
+ time = Time.at(0) + Rational(8_999_999_999_999_999, 1_000_000_000_000_000)
+ time.subsec.should == Rational(999_999_999_999_999, 1_000_000_000_000_000)
+ end
+end
diff --git a/spec/ruby/core/time/round_spec.rb b/spec/ruby/core/time/round_spec.rb
new file mode 100644
index 0000000000..0cbed04ade
--- /dev/null
+++ b/spec/ruby/core/time/round_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+
+describe "Time#round" do
+ before do
+ @time = Time.utc(2010, 3, 30, 5, 43, "25.123456789".to_r)
+ end
+
+ it "defaults to rounding to 0 places" do
+ @time.round.should == Time.utc(2010, 3, 30, 5, 43, 25.to_r)
+ end
+
+ it "rounds to 0 decimal places with an explicit argument" do
+ @time.round(0).should == Time.utc(2010, 3, 30, 5, 43, 25.to_r)
+ end
+
+ it "rounds to 7 decimal places with an explicit argument" do
+ @time.round(7).should == Time.utc(2010, 3, 30, 5, 43, "25.1234568".to_r)
+ end
+
+ it "returns an instance of Time, even if #round is called on a subclass" do
+ subclass = Class.new(Time)
+ instance = subclass.at(0)
+ instance.class.should equal subclass
+ instance.round.should be_an_instance_of(Time)
+ end
+
+ it "copies own timezone to the returning value" do
+ @time.zone.should == @time.round.zone
+
+ with_timezone "JST-9" do
+ time = Time.at 0, 1
+ time.zone.should == time.round.zone
+ end
+ end
+end
diff --git a/spec/ruby/core/time/saturday_spec.rb b/spec/ruby/core/time/saturday_spec.rb
new file mode 100644
index 0000000000..60ffea4198
--- /dev/null
+++ b/spec/ruby/core/time/saturday_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#saturday?" do
+ it "returns true if time represents Saturday" do
+ Time.local(2000, 1, 1).saturday?.should == true
+ end
+
+ it "returns false if time doesn't represent Saturday" do
+ Time.local(2000, 1, 2).saturday?.should == false
+ end
+end
diff --git a/spec/ruby/core/time/sec_spec.rb b/spec/ruby/core/time/sec_spec.rb
new file mode 100644
index 0000000000..73fc5ce1fc
--- /dev/null
+++ b/spec/ruby/core/time/sec_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Time#sec" do
+ it "returns the second of the minute(0..60) for time" do
+ Time.at(0).sec.should == 0
+ end
+end
diff --git a/spec/ruby/core/time/shared/asctime.rb b/spec/ruby/core/time/shared/asctime.rb
new file mode 100644
index 0000000000..d096666863
--- /dev/null
+++ b/spec/ruby/core/time/shared/asctime.rb
@@ -0,0 +1,6 @@
+describe :time_asctime, shared: true do
+ it "returns a canonical string representation of time" do
+ t = Time.now
+ t.send(@method).should == t.strftime("%a %b %e %H:%M:%S %Y")
+ end
+end
diff --git a/spec/ruby/core/time/shared/day.rb b/spec/ruby/core/time/shared/day.rb
new file mode 100644
index 0000000000..472dc959c1
--- /dev/null
+++ b/spec/ruby/core/time/shared/day.rb
@@ -0,0 +1,15 @@
+describe :time_day, shared: true do
+ it "returns the day of the month (1..n) for a local Time" do
+ with_timezone("CET", 1) do
+ Time.local(1970, 1, 1).send(@method).should == 1
+ end
+ end
+
+ it "returns the day of the month for a UTC Time" do
+ Time.utc(1970, 1, 1).send(@method).should == 1
+ end
+
+ it "returns the day of the month for a Time with a fixed offset" do
+ Time.new(2012, 1, 1, 0, 0, 0, -3600).send(@method).should == 1
+ end
+end
diff --git a/spec/ruby/core/time/shared/getgm.rb b/spec/ruby/core/time/shared/getgm.rb
new file mode 100644
index 0000000000..3576365772
--- /dev/null
+++ b/spec/ruby/core/time/shared/getgm.rb
@@ -0,0 +1,9 @@
+describe :time_getgm, shared: true do
+ it "returns a new time which is the utc representation of time" do
+ # Testing with America/Regina here because it doesn't have DST.
+ with_timezone("CST", -6) do
+ t = Time.local(2007, 1, 9, 6, 0, 0)
+ t.send(@method).should == Time.gm(2007, 1, 9, 12, 0, 0)
+ end
+ end
+end
diff --git a/spec/ruby/core/time/shared/gm.rb b/spec/ruby/core/time/shared/gm.rb
new file mode 100644
index 0000000000..0ee602c837
--- /dev/null
+++ b/spec/ruby/core/time/shared/gm.rb
@@ -0,0 +1,70 @@
+describe :time_gm, shared: true do
+ it "creates a time based on given values, interpreted as UTC (GMT)" do
+ Time.send(@method, 2000,"jan",1,20,15,1).inspect.should == "2000-01-01 20:15:01 UTC"
+ end
+
+ it "creates a time based on given C-style gmtime arguments, interpreted as UTC (GMT)" do
+ time = Time.send(@method, 1, 15, 20, 1, 1, 2000, :ignored, :ignored, :ignored, :ignored)
+ time.inspect.should == "2000-01-01 20:15:01 UTC"
+ end
+
+ it "interprets pre-Gregorian reform dates using Gregorian proleptic calendar" do
+ Time.send(@method, 1582, 10, 4, 12).to_i.should == -12220200000 # 2299150j
+ end
+
+ it "interprets Julian-Gregorian gap dates using Gregorian proleptic calendar" do
+ Time.send(@method, 1582, 10, 14, 12).to_i.should == -12219336000 # 2299160j
+ end
+
+ it "interprets post-Gregorian reform dates using Gregorian calendar" do
+ Time.send(@method, 1582, 10, 15, 12).to_i.should == -12219249600 # 2299161j
+ end
+
+ it "handles fractional usec close to rounding limit" do
+ time = Time.send(@method, 2000, 1, 1, 12, 30, 0, 9999r/10000)
+
+ time.usec.should == 0
+ time.nsec.should == 999
+ end
+
+ guard -> {
+ with_timezone 'right/UTC' do
+ (Time.gm(1972, 6, 30, 23, 59, 59) + 1).sec == 60
+ end
+ } do
+ it "handles real leap seconds in zone 'right/UTC'" do
+ with_timezone 'right/UTC' do
+ time = Time.send(@method, 1972, 6, 30, 23, 59, 60)
+
+ time.sec.should == 60
+ time.min.should == 59
+ time.hour.should == 23
+ time.day.should == 30
+ time.month.should == 6
+ end
+ end
+ end
+
+ it "handles bad leap seconds by carrying values forward" do
+ with_timezone 'UTC' do
+ time = Time.send(@method, 2017, 7, 5, 23, 59, 60)
+ time.sec.should == 0
+ time.min.should == 0
+ time.hour.should == 0
+ time.day.should == 6
+ time.month.should == 7
+ end
+ end
+
+ it "handles a value of 60 for seconds by carrying values forward in zone 'UTC'" do
+ with_timezone 'UTC' do
+ time = Time.send(@method, 1972, 6, 30, 23, 59, 60)
+
+ time.sec.should == 0
+ time.min.should == 0
+ time.hour.should == 0
+ time.day.should == 1
+ time.month.should == 7
+ end
+ end
+end
diff --git a/spec/ruby/core/time/shared/gmt_offset.rb b/spec/ruby/core/time/shared/gmt_offset.rb
new file mode 100644
index 0000000000..cb842be2f3
--- /dev/null
+++ b/spec/ruby/core/time/shared/gmt_offset.rb
@@ -0,0 +1,53 @@
+describe :time_gmt_offset, shared: true do
+ it "returns the offset in seconds between the timezone of time and UTC" do
+ with_timezone("AST", 3) do
+ Time.new.send(@method).should == 10800
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns the correct offset for US Eastern time zone around daylight savings time change" do
+ # "2010-03-14 01:59:59 -0500" + 1 ==> "2010-03-14 03:00:00 -0400"
+ with_timezone("EST5EDT") do
+ t = Time.local(2010,3,14,1,59,59)
+ t.send(@method).should == -5*60*60
+ (t + 1).send(@method).should == -4*60*60
+ end
+ end
+
+ it "returns the correct offset for Hawaii around daylight savings time change" do
+ # "2010-03-14 01:59:59 -1000" + 1 ==> "2010-03-14 02:00:00 -1000"
+ with_timezone("Pacific/Honolulu") do
+ t = Time.local(2010,3,14,1,59,59)
+ t.send(@method).should == -10*60*60
+ (t + 1).send(@method).should == -10*60*60
+ end
+ end
+
+ it "returns the correct offset for New Zealand around daylight savings time change" do
+ # "2010-04-04 02:59:59 +1300" + 1 ==> "2010-04-04 02:00:00 +1200"
+ with_timezone("Pacific/Auckland") do
+ t = Time.local(2010,4,4,1,59,59) + (60 * 60)
+ t.send(@method).should == 13*60*60
+ (t + 1).send(@method).should == 12*60*60
+ end
+ end
+ end
+
+ it "returns offset as Rational" do
+ Time.new(2010,4,4,1,59,59,7245).send(@method).should == 7245
+ Time.new(2010,4,4,1,59,59,7245.5).send(@method).should == Rational(14491,2)
+ end
+
+ context 'given positive offset' do
+ it 'returns a positive offset' do
+ Time.new(2013,3,17,nil,nil,nil,"+03:00").send(@method).should == 10800
+ end
+ end
+
+ context 'given negative offset' do
+ it 'returns a negative offset' do
+ Time.new(2013,3,17,nil,nil,nil,"-03:00").send(@method).should == -10800
+ end
+ end
+end
diff --git a/spec/ruby/core/time/shared/gmtime.rb b/spec/ruby/core/time/shared/gmtime.rb
new file mode 100644
index 0000000000..e684a1fd95
--- /dev/null
+++ b/spec/ruby/core/time/shared/gmtime.rb
@@ -0,0 +1,33 @@
+describe :time_gmtime, shared: true do
+ it "converts self to UTC, modifying the receiver" do
+ # Testing with America/Regina here because it doesn't have DST.
+ with_timezone("CST", -6) do
+ t = Time.local(2007, 1, 9, 6, 0, 0)
+ t.send(@method)
+ t.should == Time.gm(2007, 1, 9, 12, 0, 0)
+ end
+ end
+
+ it "returns self" do
+ with_timezone("CST", -6) do
+ t = Time.local(2007, 1, 9, 12, 0, 0)
+ t.send(@method).should equal(t)
+ end
+ end
+
+ describe "on a frozen time" do
+ it "does not raise an error if already in UTC" do
+ time = Time.gm(2007, 1, 9, 12, 0, 0)
+ time.freeze
+ time.send(@method).should equal(time)
+ end
+
+ it "raises a RuntimeError if the time is not UTC" do
+ with_timezone("CST", -6) do
+ time = Time.now
+ time.freeze
+ lambda { time.send(@method) }.should raise_error(RuntimeError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/time/shared/inspect.rb b/spec/ruby/core/time/shared/inspect.rb
new file mode 100644
index 0000000000..c707382a6e
--- /dev/null
+++ b/spec/ruby/core/time/shared/inspect.rb
@@ -0,0 +1,23 @@
+# -*- encoding: us-ascii -*-
+
+describe :inspect, shared: true do
+ it "formats the local time following the pattern 'yyyy-MM-dd HH:mm:ss Z'" do
+ with_timezone("PST", +1) do
+ Time.local(2000, 1, 1, 20, 15, 1).send(@method).should == "2000-01-01 20:15:01 +0100"
+ end
+ end
+
+ it "formats the UTC time following the pattern 'yyyy-MM-dd HH:mm:ss UTC'" do
+ Time.utc(2000, 1, 1, 20, 15, 1).send(@method).should == "2000-01-01 20:15:01 UTC"
+ end
+
+ it "formats the fixed offset time following the pattern 'yyyy-MM-dd HH:mm:ss +/-HHMM'" do
+ Time.new(2000, 1, 1, 20, 15, 01, 3600).send(@method).should == "2000-01-01 20:15:01 +0100"
+ end
+
+ with_feature :encoding do
+ it "returns a US-ASCII encoded string" do
+ Time.now.send(@method).encoding.should equal(Encoding::US_ASCII)
+ end
+ end
+end
diff --git a/spec/ruby/core/time/shared/isdst.rb b/spec/ruby/core/time/shared/isdst.rb
new file mode 100644
index 0000000000..bc6d139230
--- /dev/null
+++ b/spec/ruby/core/time/shared/isdst.rb
@@ -0,0 +1,8 @@
+describe :time_isdst, shared: true do
+ it "dst? returns whether time is during daylight saving time" do
+ with_timezone("America/Los_Angeles") do
+ Time.local(2007, 9, 9, 0, 0, 0).send(@method).should == true
+ Time.local(2007, 1, 9, 0, 0, 0).send(@method).should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/time/shared/local.rb b/spec/ruby/core/time/shared/local.rb
new file mode 100644
index 0000000000..43f331c4c1
--- /dev/null
+++ b/spec/ruby/core/time/shared/local.rb
@@ -0,0 +1,45 @@
+describe :time_local, shared: true do
+ it "creates a time based on given values, interpreted in the local time zone" do
+ with_timezone("PST", -8) do
+ Time.send(@method, 2000, "jan", 1, 20, 15, 1).to_a.should ==
+ [1, 15, 20, 1, 1, 2000, 6, 1, false, "PST"]
+ end
+ 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
+ end
+ end
+ end
+end
+
+describe :time_local_10_arg, shared: true do
+ it "creates a time based on given C-style gmtime arguments, interpreted in the local time zone" do
+ with_timezone("PST", -8) do
+ Time.send(@method, 1, 15, 20, 1, 1, 2000, :ignored, :ignored, :ignored, :ignored).to_a.should ==
+ [1, 15, 20, 1, 1, 2000, 6, 1, false, "PST"]
+ end
+ end
+
+ platform_is_not :windows do
+ it "creates the correct time just before dst change" do
+ with_timezone("America/New_York") do
+ time = Time.send(@method, 0, 30, 1, 30, 10, 2005, 0, 0, true, ENV['TZ'])
+ time.utc_offset.should == -4 * 3600
+ end
+ end
+
+ it "creates the correct time just after dst change" do
+ with_timezone("America/New_York") do
+ time = Time.send(@method, 0, 30, 1, 30, 10, 2005, 0, 0, false, ENV['TZ'])
+ time.utc_offset.should == -5 * 3600
+ end
+ end
+ end
+
+end
diff --git a/spec/ruby/core/time/shared/month.rb b/spec/ruby/core/time/shared/month.rb
new file mode 100644
index 0000000000..31ca679557
--- /dev/null
+++ b/spec/ruby/core/time/shared/month.rb
@@ -0,0 +1,15 @@
+describe :time_month, shared: true do
+ it "returns the month of the year for a local Time" do
+ with_timezone("CET", 1) do
+ Time.local(1970, 1).send(@method).should == 1
+ end
+ end
+
+ it "returns the month of the year for a UTC Time" do
+ Time.utc(1970, 1).send(@method).should == 1
+ end
+
+ it "returns the four digit year for a Time with a fixed offset" do
+ Time.new(2012, 1, 1, 0, 0, 0, -3600).send(@method).should == 1
+ end
+end
diff --git a/spec/ruby/core/time/shared/now.rb b/spec/ruby/core/time/shared/now.rb
new file mode 100644
index 0000000000..c548be283f
--- /dev/null
+++ b/spec/ruby/core/time/shared/now.rb
@@ -0,0 +1,20 @@
+require_relative '../fixtures/classes'
+
+describe :time_now, shared: true do
+ it "creates a subclass instance if called on a subclass" do
+ TimeSpecs::SubTime.send(@method).should be_an_instance_of(TimeSpecs::SubTime)
+ TimeSpecs::MethodHolder.send(@method).should be_an_instance_of(Time)
+ end
+
+ it "sets the current time" do
+ now = TimeSpecs::MethodHolder.send(@method)
+ now.to_f.should be_close(Process.clock_gettime(Process::CLOCK_REALTIME), 10.0)
+ end
+
+ it "uses the local timezone" do
+ with_timezone("PDT", -8) do
+ now = TimeSpecs::MethodHolder.send(@method)
+ now.utc_offset.should == (-8 * 60 * 60)
+ end
+ end
+end
diff --git a/spec/ruby/core/time/shared/time_params.rb b/spec/ruby/core/time/shared/time_params.rb
new file mode 100644
index 0000000000..39245116b0
--- /dev/null
+++ b/spec/ruby/core/time/shared/time_params.rb
@@ -0,0 +1,262 @@
+describe :time_params, shared: true do
+ it "accepts 1 argument (year)" do
+ Time.send(@method, 2000).should ==
+ Time.send(@method, 2000, 1, 1, 0, 0, 0)
+ end
+
+ it "accepts 2 arguments (year, month)" do
+ Time.send(@method, 2000, 2).should ==
+ Time.send(@method, 2000, 2, 1, 0, 0, 0)
+ end
+
+ it "accepts 3 arguments (year, month, day)" do
+ Time.send(@method, 2000, 2, 3).should ==
+ Time.send(@method, 2000, 2, 3, 0, 0, 0)
+ end
+
+ it "accepts 4 arguments (year, month, day, hour)" do
+ Time.send(@method, 2000, 2, 3, 4).should ==
+ Time.send(@method, 2000, 2, 3, 4, 0, 0)
+ end
+
+ it "accepts 5 arguments (year, month, day, hour, minute)" do
+ Time.send(@method, 2000, 2, 3, 4, 5).should ==
+ Time.send(@method, 2000, 2, 3, 4, 5, 0)
+ end
+
+ it "accepts a too big day of the month by going to the next month" do
+ Time.send(@method, 1999, 2, 31).should ==
+ Time.send(@method, 1999, 3, 3)
+ end
+
+ it "raises a TypeError if the year is nil" do
+ lambda { Time.send(@method, nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts nil month, day, hour, minute, and second" do
+ Time.send(@method, 2000, nil, nil, nil, nil, nil).should ==
+ Time.send(@method, 2000)
+ end
+
+ it "handles a String year" do
+ Time.send(@method, "2000").should ==
+ Time.send(@method, 2000)
+ end
+
+ it "coerces the year with #to_int" do
+ m = mock(:int)
+ m.should_receive(:to_int).and_return(1)
+ Time.send(@method, m).should == Time.send(@method, 1)
+ end
+
+ it "handles a String month given as a numeral" do
+ Time.send(@method, 2000, "12").should ==
+ Time.send(@method, 2000, 12)
+ end
+
+ it "handles a String month given as a short month name" do
+ Time.send(@method, 2000, "dec").should ==
+ Time.send(@method, 2000, 12)
+ end
+
+ it "coerces the month with #to_str" do
+ (obj = mock('12')).should_receive(:to_str).and_return("12")
+ Time.send(@method, 2008, obj).should ==
+ Time.send(@method, 2008, 12)
+ end
+
+ it "coerces the month with #to_int" do
+ m = mock(:int)
+ m.should_receive(:to_int).and_return(1)
+ Time.send(@method, 2008, m).should == Time.send(@method, 2008, 1)
+ end
+
+ it "handles a String day" do
+ Time.send(@method, 2000, 12, "15").should ==
+ Time.send(@method, 2000, 12, 15)
+ end
+
+ it "coerces the day with #to_int" do
+ m = mock(:int)
+ m.should_receive(:to_int).and_return(1)
+ Time.send(@method, 2008, 1, m).should == Time.send(@method, 2008, 1, 1)
+ end
+
+ it "handles a String hour" do
+ Time.send(@method, 2000, 12, 1, "5").should ==
+ Time.send(@method, 2000, 12, 1, 5)
+ end
+
+ it "coerces the hour with #to_int" do
+ m = mock(:int)
+ m.should_receive(:to_int).and_return(1)
+ Time.send(@method, 2008, 1, 1, m).should == Time.send(@method, 2008, 1, 1, 1)
+ end
+
+ it "handles a String minute" do
+ Time.send(@method, 2000, 12, 1, 1, "8").should ==
+ Time.send(@method, 2000, 12, 1, 1, 8)
+ end
+
+ it "coerces the minute with #to_int" do
+ m = mock(:int)
+ m.should_receive(:to_int).and_return(1)
+ Time.send(@method, 2008, 1, 1, 0, m).should == Time.send(@method, 2008, 1, 1, 0, 1)
+ end
+
+ it "handles a String second" do
+ Time.send(@method, 2000, 12, 1, 1, 1, "8").should ==
+ Time.send(@method, 2000, 12, 1, 1, 1, 8)
+ end
+
+ it "coerces the second with #to_int" do
+ m = mock(:int)
+ m.should_receive(:to_int).and_return(1)
+ Time.send(@method, 2008, 1, 1, 0, 0, m).should == Time.send(@method, 2008, 1, 1, 0, 0, 1)
+ end
+
+ it "interprets all numerals as base 10" do
+ Time.send(@method, "2000", "08", "08", "08", "08", "08").should == Time.send(@method, 2000, 8, 8, 8, 8, 8)
+ Time.send(@method, "2000", "09", "09", "09", "09", "09").should == Time.send(@method, 2000, 9, 9, 9, 9, 9)
+ end
+
+ it "handles fractional seconds as a Float" do
+ t = Time.send(@method, 2000, 1, 1, 20, 15, 1.75)
+ t.sec.should == 1
+ t.usec.should == 750000
+ end
+
+ it "handles fractional seconds as a Rational" do
+ t = Time.send(@method, 2000, 1, 1, 20, 15, Rational(99, 10))
+ t.sec.should == 9
+ t.usec.should == 900000
+ end
+
+ it "handles years from 0 as such" do
+ 0.upto(2100) do |year|
+ t = Time.send(@method, year)
+ t.year.should == year
+ end
+ end
+
+ it "accepts various year ranges" do
+ Time.send(@method, 1801, 12, 31, 23, 59, 59).wday.should == 4
+ Time.send(@method, 3000, 12, 31, 23, 59, 59).wday.should == 3
+ end
+
+ it "raises an ArgumentError for out of range month" do
+ lambda {
+ Time.send(@method, 2008, 13, 31, 23, 59, 59)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for out of range day" do
+ lambda {
+ Time.send(@method, 2008, 12, 32, 23, 59, 59)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for out of range hour" do
+ lambda {
+ Time.send(@method, 2008, 12, 31, 25, 59, 59)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for out of range minute" do
+ lambda {
+ Time.send(@method, 2008, 12, 31, 23, 61, 59)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for out of range second" do
+ lambda {
+ Time.send(@method, 2008, 12, 31, 23, 59, 61)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError when given 9 arguments" do
+ lambda { Time.send(@method, *[0]*9) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError when given 11 arguments" do
+ lambda { Time.send(@method, *[0]*11) }.should raise_error(ArgumentError)
+ end
+
+ it "returns subclass instances" do
+ c = Class.new(Time)
+ c.send(@method, 2008, "12").should be_an_instance_of(c)
+ end
+end
+
+describe :time_params_10_arg, shared: true do
+ it "handles string arguments" do
+ Time.send(@method, "1", "15", "20", "1", "1", "2000", :ignored, :ignored,
+ :ignored, :ignored).should ==
+ Time.send(@method, 1, 15, 20, 1, 1, 2000, :ignored, :ignored, :ignored, :ignored)
+ end
+
+ it "handles float arguments" do
+ Time.send(@method, 1.0, 15.0, 20.0, 1.0, 1.0, 2000.0, :ignored, :ignored,
+ :ignored, :ignored).should ==
+ Time.send(@method, 1, 15, 20, 1, 1, 2000, :ignored, :ignored, :ignored, :ignored)
+ end
+
+ it "raises an ArgumentError for out of range values" do
+ lambda {
+ Time.send(@method, 61, 59, 23, 31, 12, 2008, :ignored, :ignored, :ignored, :ignored)
+ }.should raise_error(ArgumentError) # sec
+
+ lambda {
+ Time.send(@method, 59, 61, 23, 31, 12, 2008, :ignored, :ignored, :ignored, :ignored)
+ }.should raise_error(ArgumentError) # min
+
+ lambda {
+ Time.send(@method, 59, 59, 25, 31, 12, 2008, :ignored, :ignored, :ignored, :ignored)
+ }.should raise_error(ArgumentError) # hour
+
+ lambda {
+ Time.send(@method, 59, 59, 23, 32, 12, 2008, :ignored, :ignored, :ignored, :ignored)
+ }.should raise_error(ArgumentError) # day
+
+ lambda {
+ Time.send(@method, 59, 59, 23, 31, 13, 2008, :ignored, :ignored, :ignored, :ignored)
+ }.should raise_error(ArgumentError) # month
+ end
+end
+
+describe :time_params_microseconds, shared: true do
+ it "handles microseconds" do
+ t = Time.send(@method, 2000, 1, 1, 20, 15, 1, 123)
+ t.usec.should == 123
+ end
+
+ it "raises an ArgumentError for out of range microsecond" do
+ lambda { Time.send(@method, 2000, 1, 1, 20, 15, 1, 1000000) }.should raise_error(ArgumentError)
+ end
+
+ it "handles fractional microseconds as a Float" do
+ t = Time.send(@method, 2000, 1, 1, 20, 15, 1, 1.75)
+ t.usec.should == 1
+ t.nsec.should == 1750
+ end
+
+ it "handles fractional microseconds as a Rational" do
+ t = Time.send(@method, 2000, 1, 1, 20, 15, 1, Rational(99, 10))
+ t.usec.should == 9
+ t.nsec.should == 9900
+ end
+
+ it "ignores fractional seconds if a passed whole number of microseconds" do
+ t = Time.send(@method, 2000, 1, 1, 20, 15, 1.75, 2)
+ t.sec.should == 1
+ t.usec.should == 2
+ t.nsec.should == 2000
+ end
+
+ it "ignores fractional seconds if a passed fractional number of microseconds" do
+ t = Time.send(@method, 2000, 1, 1, 20, 15, 1.75, Rational(99, 10))
+ t.sec.should == 1
+ t.usec.should == 9
+ t.nsec.should == 9900
+ end
+end
diff --git a/spec/ruby/core/time/shared/to_i.rb b/spec/ruby/core/time/shared/to_i.rb
new file mode 100644
index 0000000000..03497c700b
--- /dev/null
+++ b/spec/ruby/core/time/shared/to_i.rb
@@ -0,0 +1,9 @@
+describe :time_to_i, shared: true do
+ it "returns the value of time as an integer number of seconds since epoch" do
+ Time.at(0).send(@method).should == 0
+ end
+
+ it "doesn't return an actual number of seconds in time" do
+ Time.at(65.5).send(@method).should == 65
+ end
+end
diff --git a/spec/ruby/core/time/strftime_spec.rb b/spec/ruby/core/time/strftime_spec.rb
new file mode 100644
index 0000000000..62f9272f32
--- /dev/null
+++ b/spec/ruby/core/time/strftime_spec.rb
@@ -0,0 +1,52 @@
+# encoding: utf-8
+
+require_relative '../../spec_helper'
+require_relative '../../shared/time/strftime_for_date'
+require_relative '../../shared/time/strftime_for_time'
+
+describe "Time#strftime" do
+ before :all do
+ @new_date = lambda { |y,m,d| Time.gm(y,m,d) }
+ @new_time = lambda { |*args| Time.gm(*args) }
+ @new_time_in_zone = lambda { |zone,offset,*args|
+ with_timezone(zone, offset) do
+ Time.new(*args)
+ end
+ }
+ @new_time_with_offset = lambda { |y,m,d,h,min,s,offset|
+ Time.new(y,m,d,h,min,s,offset)
+ }
+
+ @time = @new_time[2001, 2, 3, 4, 5, 6]
+ end
+
+ it_behaves_like :strftime_date, :strftime
+ it_behaves_like :strftime_time, :strftime
+
+ # Differences with date
+ it "requires an argument" do
+ lambda { @time.strftime }.should raise_error(ArgumentError)
+ end
+
+ # %Z is zone name or empty for Time
+ it "should be able to show the timezone if available" do
+ @time.strftime("%Z").should == @time.zone
+ with_timezone("UTC", 0) do
+ Time.gm(2000).strftime("%Z").should == "UTC"
+ end
+
+ Time.new(2000, 1, 1, 0, 0, 0, 42).strftime("%Z").should == ""
+ end
+
+ # %v is %e-%^b-%Y for Time
+ it "should be able to show the commercial week" do
+ @time.strftime("%v").should == " 3-FEB-2001"
+ @time.strftime("%v").should == @time.strftime('%e-%^b-%Y')
+ end
+
+ # Date/DateTime round at creation time, but Time does it in strftime.
+ it "rounds an offset to the nearest second when formatting with %z" do
+ time = @new_time_with_offset[2012, 1, 1, 0, 0, 0, Rational(36645, 10)]
+ time.strftime("%::z").should == "+01:01:05"
+ end
+end
diff --git a/spec/ruby/core/time/subsec_spec.rb b/spec/ruby/core/time/subsec_spec.rb
new file mode 100644
index 0000000000..583a26412b
--- /dev/null
+++ b/spec/ruby/core/time/subsec_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+
+describe "Time#subsec" do
+ it "returns 0 as a Fixnum for a Time with a whole number of seconds" do
+ Time.at(100).subsec.should eql(0)
+ end
+
+ it "returns the fractional seconds as a Rational for a Time constructed with a Rational number of seconds" do
+ Time.at(Rational(3, 2)).subsec.should eql(Rational(1, 2))
+ end
+
+ it "returns the fractional seconds as a Rational for a Time constructed with a Float number of seconds" do
+ Time.at(10.75).subsec.should eql(Rational(3, 4))
+ end
+
+ it "returns the fractional seconds as a Rational for a Time constructed with an Integer number of microseconds" do
+ Time.at(0, 999999).subsec.should eql(Rational(999999, 1000000))
+ end
+
+ it "returns the fractional seconds as a Rational for a Time constructed with an Rational number of microseconds" do
+ Time.at(0, Rational(9, 10)).subsec.should eql(Rational(9, 10000000))
+ end
+
+ it "returns the fractional seconds as a Rational for a Time constructed with an Float number of microseconds" do
+ Time.at(0, 0.75).subsec.should eql(Rational(3, 4000000))
+ end
+end
diff --git a/spec/ruby/core/time/succ_spec.rb b/spec/ruby/core/time/succ_spec.rb
new file mode 100644
index 0000000000..dace9b823e
--- /dev/null
+++ b/spec/ruby/core/time/succ_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+describe "Time#succ" do
+ it "returns a new time one second later than time" do
+ -> {
+ @result = Time.at(100).succ
+ }.should complain(/Time#succ is obsolete/)
+ @result.should == Time.at(101)
+ end
+
+ it "returns a new instance" do
+ t1 = Time.at(100)
+ t2 = nil
+ -> {
+ t2 = t1.succ
+ }.should complain(/Time#succ is obsolete/)
+ t1.should_not equal t2
+ end
+end
diff --git a/spec/ruby/core/time/sunday_spec.rb b/spec/ruby/core/time/sunday_spec.rb
new file mode 100644
index 0000000000..2285583579
--- /dev/null
+++ b/spec/ruby/core/time/sunday_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#sunday?" do
+ it "returns true if time represents Sunday" do
+ Time.local(2000, 1, 2).sunday?.should == true
+ end
+
+ it "returns false if time doesn't represent Sunday" do
+ Time.local(2000, 1, 1).sunday?.should == false
+ end
+end
diff --git a/spec/ruby/core/time/thursday_spec.rb b/spec/ruby/core/time/thursday_spec.rb
new file mode 100644
index 0000000000..c82d517519
--- /dev/null
+++ b/spec/ruby/core/time/thursday_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#thursday?" do
+ it "returns true if time represents Thursday" do
+ Time.local(2000, 1, 6).thursday?.should == true
+ end
+
+ it "returns false if time doesn't represent Thursday" do
+ Time.local(2000, 1, 1).thursday?.should == false
+ end
+end
diff --git a/spec/ruby/core/time/time_spec.rb b/spec/ruby/core/time/time_spec.rb
new file mode 100644
index 0000000000..b0803a7f21
--- /dev/null
+++ b/spec/ruby/core/time/time_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Time" do
+ it "includes Comparable" do
+ Time.include?(Comparable).should == true
+ end
+end
diff --git a/spec/ruby/core/time/to_a_spec.rb b/spec/ruby/core/time/to_a_spec.rb
new file mode 100644
index 0000000000..3728b8c526
--- /dev/null
+++ b/spec/ruby/core/time/to_a_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+
+describe "Time#to_a" do
+ platform_is_not :windows do
+ it "returns a 10 element array representing the deconstructed time" do
+ # Testing with America/Regina here because it doesn't have DST.
+ with_timezone("America/Regina") do
+ Time.at(0).to_a.should == [0, 0, 18, 31, 12, 1969, 3, 365, false, "CST"]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/time/to_f_spec.rb b/spec/ruby/core/time/to_f_spec.rb
new file mode 100644
index 0000000000..6101dcf871
--- /dev/null
+++ b/spec/ruby/core/time/to_f_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Time#to_f" do
+ it "returns the float number of seconds + usecs since the epoch" do
+ Time.at(100, 100).to_f.should == 100.0001
+ end
+end
diff --git a/spec/ruby/core/time/to_i_spec.rb b/spec/ruby/core/time/to_i_spec.rb
new file mode 100644
index 0000000000..54929d1e18
--- /dev/null
+++ b/spec/ruby/core/time/to_i_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_i'
+
+describe "Time#to_i" do
+ it_behaves_like :time_to_i, :to_i
+end
diff --git a/spec/ruby/core/time/to_r_spec.rb b/spec/ruby/core/time/to_r_spec.rb
new file mode 100644
index 0000000000..6af2d9b7ea
--- /dev/null
+++ b/spec/ruby/core/time/to_r_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#to_r" do
+ it "returns the a Rational representing seconds and subseconds since the epoch" do
+ Time.at(Rational(11, 10)).to_r.should eql(Rational(11, 10))
+ end
+
+ it "returns a Rational even for a whole number of seconds" do
+ Time.at(2).to_r.should eql(Rational(2))
+ end
+end
diff --git a/spec/ruby/core/time/to_s_spec.rb b/spec/ruby/core/time/to_s_spec.rb
new file mode 100644
index 0000000000..ac6c0908ac
--- /dev/null
+++ b/spec/ruby/core/time/to_s_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/inspect'
+
+describe "Time#to_s" do
+ it_behaves_like :inspect, :to_s
+end
diff --git a/spec/ruby/core/time/tuesday_spec.rb b/spec/ruby/core/time/tuesday_spec.rb
new file mode 100644
index 0000000000..7abbdc92c0
--- /dev/null
+++ b/spec/ruby/core/time/tuesday_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#tuesday?" do
+ it "returns true if time represents Tuesday" do
+ Time.local(2000, 1, 4).tuesday?.should == true
+ end
+
+ it "returns false if time doesn't represent Tuesday" do
+ Time.local(2000, 1, 1).tuesday?.should == false
+ end
+end
diff --git a/spec/ruby/core/time/tv_nsec_spec.rb b/spec/ruby/core/time/tv_nsec_spec.rb
new file mode 100644
index 0000000000..feb7998b16
--- /dev/null
+++ b/spec/ruby/core/time/tv_nsec_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Time#tv_nsec" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/time/tv_sec_spec.rb b/spec/ruby/core/time/tv_sec_spec.rb
new file mode 100644
index 0000000000..f83e1fbfdd
--- /dev/null
+++ b/spec/ruby/core/time/tv_sec_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_i'
+
+describe "Time#tv_sec" do
+ it_behaves_like :time_to_i, :tv_sec
+end
diff --git a/spec/ruby/core/time/tv_usec_spec.rb b/spec/ruby/core/time/tv_usec_spec.rb
new file mode 100644
index 0000000000..f0de4f4a9c
--- /dev/null
+++ b/spec/ruby/core/time/tv_usec_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Time#tv_usec" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/time/usec_spec.rb b/spec/ruby/core/time/usec_spec.rb
new file mode 100644
index 0000000000..6ea52f5e79
--- /dev/null
+++ b/spec/ruby/core/time/usec_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+
+describe "Time#usec" do
+ it "returns 0 for a Time constructed with a whole number of seconds" do
+ Time.at(100).usec.should == 0
+ end
+
+ it "returns the microseconds part of a Time constructed with a Float number of seconds" do
+ Time.at(10.75).usec.should == 750_000
+ end
+
+ it "returns the microseconds part of a Time constructed with an Integer number of microseconds" do
+ Time.at(0, 999_999).usec.should == 999_999
+ end
+
+ it "returns the microseconds part of a Time constructed with an Float number of microseconds > 1" do
+ Time.at(0, 3.75).usec.should == 3
+ end
+
+ it "returns 0 for a Time constructed with an Float number of microseconds < 1" do
+ Time.at(0, 0.75).usec.should == 0
+ end
+
+ it "returns the microseconds part of a Time constructed with a Rational number of seconds" do
+ Time.at(Rational(3, 2)).usec.should == 500_000
+ end
+
+ it "returns the microseconds part of a Time constructed with an Rational number of microseconds > 1" do
+ Time.at(0, Rational(99, 10)).usec.should == 9
+ end
+
+ it "returns 0 for a Time constructed with an Rational number of microseconds < 1" do
+ Time.at(0, Rational(9, 10)).usec.should == 0
+ end
+
+ it "returns the microseconds for time created by Time#local" do
+ Time.local(1,2,3,4,5,Rational(6.78)).usec.should == 780000
+ end
+
+ it "returns a positive value for dates before the epoch" do
+ Time.utc(1969, 11, 12, 13, 18, 57, 404240).usec.should == 404240
+ end
+end
diff --git a/spec/ruby/core/time/utc_offset_spec.rb b/spec/ruby/core/time/utc_offset_spec.rb
new file mode 100644
index 0000000000..17c031b8c6
--- /dev/null
+++ b/spec/ruby/core/time/utc_offset_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/gmt_offset'
+
+describe "Time#utc_offset" do
+ it_behaves_like :time_gmt_offset, :utc_offset
+end
diff --git a/spec/ruby/core/time/utc_spec.rb b/spec/ruby/core/time/utc_spec.rb
new file mode 100644
index 0000000000..5074c8d54d
--- /dev/null
+++ b/spec/ruby/core/time/utc_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'shared/gm'
+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.utc?.should == false
+ end
+end
+
+describe "Time.utc" do
+ it_behaves_like :time_gm, :utc
+ it_behaves_like :time_params, :utc
+ it_behaves_like :time_params_10_arg, :utc
+ it_behaves_like :time_params_microseconds, :utc
+end
+
+describe "Time#utc" do
+ it_behaves_like :time_gmtime, :utc
+end
diff --git a/spec/ruby/core/time/wday_spec.rb b/spec/ruby/core/time/wday_spec.rb
new file mode 100644
index 0000000000..9f63f67de9
--- /dev/null
+++ b/spec/ruby/core/time/wday_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Time#wday" do
+ it "returns an integer representing the day of the week, 0..6, with Sunday being 0" do
+ with_timezone("GMT", 0) do
+ Time.at(0).wday.should == 4
+ end
+ end
+end
diff --git a/spec/ruby/core/time/wednesday_spec.rb b/spec/ruby/core/time/wednesday_spec.rb
new file mode 100644
index 0000000000..6a52c3577b
--- /dev/null
+++ b/spec/ruby/core/time/wednesday_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#wednesday?" do
+ it "returns true if time represents Wednesday" do
+ Time.local(2000, 1, 5).wednesday?.should == true
+ end
+
+ it "returns false if time doesn't represent Wednesday" do
+ Time.local(2000, 1, 1).wednesday?.should == false
+ end
+end
diff --git a/spec/ruby/core/time/yday_spec.rb b/spec/ruby/core/time/yday_spec.rb
new file mode 100644
index 0000000000..6ea5ff8f1b
--- /dev/null
+++ b/spec/ruby/core/time/yday_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+
+describe "Time#yday" do
+ it "returns an integer representing the day of the year, 1..366" do
+ with_timezone("UTC") do
+ Time.at(9999999).yday.should == 116
+ end
+ end
+
+ it 'returns the correct value for each day of each month' do
+ mdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+
+ yday = 1
+ mdays.each_with_index do |days, month|
+ days.times do |day|
+ Time.new(2014, month+1, day+1).yday.should == yday
+ yday += 1
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/time/year_spec.rb b/spec/ruby/core/time/year_spec.rb
new file mode 100644
index 0000000000..d2d50062c5
--- /dev/null
+++ b/spec/ruby/core/time/year_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "Time#year" do
+ it "returns the four digit year for a local Time as an Integer" do
+ with_timezone("CET", 1) do
+ Time.local(1970).year.should == 1970
+ end
+ end
+
+ it "returns the four digit year for a UTC Time as an Integer" do
+ Time.utc(1970).year.should == 1970
+ end
+
+ it "returns the four digit year for a Time with a fixed offset" do
+ Time.new(2012, 1, 1, 0, 0, 0, -3600).year.should == 2012
+ end
+end
diff --git a/spec/ruby/core/time/zone_spec.rb b/spec/ruby/core/time/zone_spec.rb
new file mode 100644
index 0000000000..45c9e852ea
--- /dev/null
+++ b/spec/ruby/core/time/zone_spec.rb
@@ -0,0 +1,90 @@
+require_relative '../../spec_helper'
+
+describe "Time#zone" do
+ platform_is_not :windows do
+ it "returns the time zone used for time" do
+ with_timezone("America/New_York") do
+ Time.new(2001, 1, 1, 0, 0, 0).zone.should == "EST"
+ Time.new(2001, 7, 1, 0, 0, 0).zone.should == "EDT"
+ %w[EST EDT].should include Time.now.zone
+ end
+ end
+ end
+
+ it "returns nil for a Time with a fixed offset" do
+ Time.new(2001, 1, 1, 0, 0, 0, "+05:00").zone.should == nil
+ end
+
+ platform_is_not :windows do
+ it "returns the correct timezone for a local time" do
+ t = Time.new(2005, 2, 27, 22, 50, 0, -3600)
+
+ with_timezone("America/New_York") do
+ t.getlocal.zone.should == "EST"
+ end
+ end
+ end
+
+ it "returns nil when getting the local time with a fixed offset" do
+ t = Time.new(2005, 2, 27, 22, 50, 0, -3600)
+
+ with_timezone("America/New_York") do
+ t.getlocal("+05:00").zone.should be_nil
+ end
+ end
+
+ describe "Encoding.default_internal is set" do
+ before :each do
+ @encoding = Encoding.default_internal
+ Encoding.default_internal = Encoding::UTF_8
+ end
+
+ after :each do
+ Encoding.default_internal = @encoding
+ end
+
+ it "returns an ASCII string" do
+ t = Time.new(2005, 2, 27, 22, 50, 0, -3600)
+
+ with_timezone("America/New_York") do
+ t.getlocal.zone.encoding.should == Encoding::US_ASCII
+ end
+ end
+
+ it "doesn't raise errors for a Time with a fixed offset" do
+ lambda {
+ Time.new(2001, 1, 1, 0, 0, 0, "+05:00").zone
+ }.should_not raise_error
+ end
+ end
+
+ it "returns UTC when called on a UTC time" do
+ Time.now.utc.zone.should == "UTC"
+ end
+
+ platform_is_not :aix, :windows do
+ it "defaults to UTC when bad zones given" do
+ with_timezone("hello-foo") do
+ Time.now.utc_offset.should == 0
+ end
+ with_timezone("1,2") do
+ Time.now.utc_offset.should == 0
+ end
+ with_timezone("Sun,Fri,2") do
+ Time.now.utc_offset.should == 0
+ end
+ end
+ end
+
+ platform_is :windows do
+ # See https://bugs.ruby-lang.org/issues/13591#note-11
+ it "defaults to UTC when bad zones given" do
+ with_timezone("1,2") do
+ Time.now.utc_offset.should == 0
+ end
+ with_timezone("12") do
+ Time.now.utc_offset.should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/tracepoint/binding_spec.rb b/spec/ruby/core/tracepoint/binding_spec.rb
new file mode 100644
index 0000000000..f37753602e
--- /dev/null
+++ b/spec/ruby/core/tracepoint/binding_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+describe 'TracePoint#binding' do
+ def test
+ secret = 42
+ end
+
+ it 'return the generated binding object from event' do
+ bindings = []
+ TracePoint.new(:return) { |tp|
+ bindings << tp.binding
+ }.enable {
+ test
+ }
+ bindings.size.should == 1
+ bindings[0].should be_kind_of(Binding)
+ bindings[0].local_variables.should == [:secret]
+ end
+end
diff --git a/spec/ruby/core/tracepoint/callee_id_spec.rb b/spec/ruby/core/tracepoint/callee_id_spec.rb
new file mode 100644
index 0000000000..39a7413648
--- /dev/null
+++ b/spec/ruby/core/tracepoint/callee_id_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is '2.4' do
+ describe "TracePoint#callee_id" do
+ it "returns the called name of the method being called" do
+ a = []
+ obj = TracePointSpec::ClassWithMethodAlias.new
+
+ TracePoint.new(:call) do |tp|
+ a << tp.callee_id
+ end.enable do
+ obj.m_alias
+ end
+
+ a.should == [:m_alias]
+ end
+ end
+end
diff --git a/spec/ruby/core/tracepoint/defined_class_spec.rb b/spec/ruby/core/tracepoint/defined_class_spec.rb
new file mode 100644
index 0000000000..72536e6a56
--- /dev/null
+++ b/spec/ruby/core/tracepoint/defined_class_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe 'TracePoint#defined_class' do
+ it 'returns class or module of the method being called' do
+ last_class_name = nil
+ TracePoint.new(:call) do |tp|
+ last_class_name = tp.defined_class
+ end.enable do
+ TracePointSpec::B.new.foo
+ last_class_name.should equal(TracePointSpec::B)
+
+ TracePointSpec::B.new.bar
+ last_class_name.should equal(TracePointSpec::A)
+
+ c = TracePointSpec::C.new
+ last_class_name.should equal(TracePointSpec::C)
+
+ c.foo
+ last_class_name.should equal(TracePointSpec::B)
+
+ c.bar
+ last_class_name.should equal(TracePointSpec::A)
+ end
+ end
+end
diff --git a/spec/ruby/core/tracepoint/disable_spec.rb b/spec/ruby/core/tracepoint/disable_spec.rb
new file mode 100644
index 0000000000..e936d3498d
--- /dev/null
+++ b/spec/ruby/core/tracepoint/disable_spec.rb
@@ -0,0 +1,73 @@
+require_relative '../../spec_helper'
+
+describe 'TracePoint#disable' do
+ def test; end
+ it 'returns true if trace was enabled' do
+ called = false
+ trace = TracePoint.new(:call) do |tp|
+ called = true
+ end
+
+ trace.enable
+ trace.disable.should be_true
+
+ # Check the TracePoint is disabled
+ called = false
+ test
+ called.should == false
+ end
+
+ it 'returns false if trace was disabled' do
+ event_name, method_name = nil
+ trace = TracePoint.new(:call) do |tp|
+ event_name = tp.event
+ method_name = tp.method_id
+ end
+
+ trace.disable.should be_false
+ event_name, method_name = nil
+ test
+ method_name.equal?(:test).should be_false
+ event_name.should equal(nil)
+ end
+
+ it 'is disabled within a block & is enabled outside the block' do
+ enabled = nil
+ trace = TracePoint.new(:line) {}
+ trace.enable
+ begin
+ trace.disable { enabled = trace.enabled? }
+ enabled.should be_false
+ trace.enabled?.should be_true
+ ensure
+ trace.disable
+ end
+ end
+
+ it 'is disabled within a block & also returns false when its called with a block' do
+ trace = TracePoint.new(:line) {}
+ trace.enable
+ begin
+ trace.disable { trace.enabled? }.should == false
+ trace.enabled?.should equal(true)
+ ensure
+ trace.disable
+ end
+ end
+
+ ruby_bug "#14057", ""..."2.5" do
+ it 'can accept param within a block but it should not yield arguments' do
+ event_name = nil
+ trace = TracePoint.new(:line) {}
+ trace.enable
+ begin
+ trace.disable do |*args|
+ args.should == []
+ end
+ trace.enabled?.should be_true
+ ensure
+ trace.disable
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/tracepoint/enable_spec.rb b/spec/ruby/core/tracepoint/enable_spec.rb
new file mode 100644
index 0000000000..b1a8628896
--- /dev/null
+++ b/spec/ruby/core/tracepoint/enable_spec.rb
@@ -0,0 +1,102 @@
+require_relative '../../spec_helper'
+
+describe 'TracePoint#enable' do
+ def test; end
+
+ describe 'without a block' do
+ it 'returns true if trace was enabled' do
+ event_name = nil
+ trace = TracePoint.new(:call) do |tp|
+ event_name = tp.event
+ end
+
+ test
+ event_name.should == nil
+
+ trace.enable
+ begin
+ test
+ event_name.should equal(:call)
+ ensure
+ trace.disable
+ end
+ end
+
+ it 'returns false if trace was disabled' do
+ event_name, method_name = nil, nil
+ trace = TracePoint.new(:call) do |tp|
+ event_name = tp.event
+ method_name = tp.method_id
+ end
+
+ trace.enable.should be_false
+ begin
+ event_name.should equal(:call)
+ test
+ method_name.equal?(:test).should be_true
+ ensure
+ trace.disable
+ end
+
+ event_name, method_name = nil
+ test
+ method_name.equal?(:test).should be_false
+ event_name.should equal(nil)
+
+ trace.enable.should be_false
+ begin
+ event_name.should equal(:call)
+ test
+ method_name.equal?(:test).should be_true
+ ensure
+ trace.disable
+ end
+ end
+ end
+
+ describe 'with a block' do
+ it 'enables the trace object within a block' do
+ event_name = nil
+ TracePoint.new(:line) do |tp|
+ event_name = tp.event
+ end.enable { event_name.should equal(:line) }
+ end
+
+ ruby_bug "#14057", ""..."2.5" do
+ it 'can accept arguments within a block but it should not yield arguments' do
+ event_name = nil
+ trace = TracePoint.new(:line) { |tp| event_name = tp.event }
+ trace.enable do |*args|
+ event_name.should equal(:line)
+ args.should == []
+ end
+ trace.enabled?.should be_false
+ end
+ end
+
+ it 'enables trace object on calling with a block if it was already enabled' do
+ enabled = nil
+ trace = TracePoint.new(:line) {}
+ trace.enable
+ begin
+ trace.enable { enabled = trace.enabled? }
+ enabled.should == true
+ ensure
+ trace.disable
+ end
+ end
+
+ it 'returns value returned by the block' do
+ trace = TracePoint.new(:line) {}
+ trace.enable { true; 'test' }.should == 'test'
+ end
+
+ it 'disables the trace object outside the block' do
+ event_name = nil
+ trace = TracePoint.new(:line) { |tp|event_name = tp.event }
+ trace.enable { '2 + 2' }
+ event_name.should equal(:line)
+ trace.enabled?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/core/tracepoint/enabled_spec.rb b/spec/ruby/core/tracepoint/enabled_spec.rb
new file mode 100644
index 0000000000..1c5d3d4bdd
--- /dev/null
+++ b/spec/ruby/core/tracepoint/enabled_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe 'TracePoint#enabled?' do
+ it 'returns true when current status of the trace is enable' do
+ trace = TracePoint.new(:call) {}
+ trace.enable do
+ trace.enabled?.should be_true
+ end
+ end
+
+ it 'returns false when current status of the trace is disabled' do
+ TracePoint.new(:call) {}.enabled?.should be_false
+ end
+end
diff --git a/spec/ruby/core/tracepoint/event_spec.rb b/spec/ruby/core/tracepoint/event_spec.rb
new file mode 100644
index 0000000000..019d0c3253
--- /dev/null
+++ b/spec/ruby/core/tracepoint/event_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe 'TracePoint#event' do
+ it 'returns the type of event' do
+ event_name = nil
+ TracePoint.new(:end, :call) do |tp|
+ event_name = tp.event
+ end.enable do
+ TracePointSpec.test
+ event_name.should equal(:call)
+
+ TracePointSpec::B.new.foo
+ event_name.should equal(:call)
+
+ class TracePointSpec::B; end
+ event_name.should equal(:end)
+ end
+
+ end
+end
diff --git a/spec/ruby/core/tracepoint/fixtures/classes.rb b/spec/ruby/core/tracepoint/fixtures/classes.rb
new file mode 100644
index 0000000000..49c70e1915
--- /dev/null
+++ b/spec/ruby/core/tracepoint/fixtures/classes.rb
@@ -0,0 +1,34 @@
+module TracePointSpec
+ class ClassWithMethodAlias
+ def m
+ end
+ alias_method :m_alias, :m
+ end
+
+ module A
+ def bar; end
+ end
+
+ class B
+ include A
+
+ def foo; end;
+ end
+
+ class C < B
+ def initialize
+ end
+
+ def foo
+ super
+ end
+
+ def bar
+ super
+ end
+ end
+
+ def self.test
+ 'test'
+ end
+end
diff --git a/spec/ruby/core/tracepoint/inspect_spec.rb b/spec/ruby/core/tracepoint/inspect_spec.rb
new file mode 100644
index 0000000000..19e345c7cf
--- /dev/null
+++ b/spec/ruby/core/tracepoint/inspect_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+
+describe 'TracePoint#inspect' do
+ it 'returns a string containing a human-readable TracePoint status' do
+ TracePoint.new(:call) {}.inspect.should ==
+ '#<TracePoint:disabled>'
+ end
+end
diff --git a/spec/ruby/core/tracepoint/lineno_spec.rb b/spec/ruby/core/tracepoint/lineno_spec.rb
new file mode 100644
index 0000000000..a4d7e77e8d
--- /dev/null
+++ b/spec/ruby/core/tracepoint/lineno_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+
+describe 'TracePoint#lineno' do
+ it 'returns the line number of the event' do
+ lineno = nil
+ TracePoint.new(:line) { |tp| lineno = tp.lineno }.enable do
+ lineno.should == 7
+ end
+ end
+end
diff --git a/spec/ruby/core/tracepoint/method_id_spec.rb b/spec/ruby/core/tracepoint/method_id_spec.rb
new file mode 100644
index 0000000000..82254d1299
--- /dev/null
+++ b/spec/ruby/core/tracepoint/method_id_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+describe 'TracePoint#method_id' do
+ def test; end
+
+ it 'returns the name at the definition of the method being called' do
+ method_name = nil
+ TracePoint.new(:call) { |tp| method_name = tp.method_id}.enable do
+ test
+ method_name.should equal(:test)
+ end
+ end
+end
diff --git a/spec/ruby/core/tracepoint/new_spec.rb b/spec/ruby/core/tracepoint/new_spec.rb
new file mode 100644
index 0000000000..77675561f6
--- /dev/null
+++ b/spec/ruby/core/tracepoint/new_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe 'TracePoint.new' do
+ it 'returns a new TracePoint object, not enabled by default' do
+ TracePoint.new(:call) {}.enabled?.should be_false
+ end
+
+ it 'includes :line event when event is not specified' do
+ event_name = nil
+ TracePoint.new() { |tp| event_name = tp.event }.enable do
+ event_name.should equal(:line)
+
+ event_name = nil
+ TracePointSpec.test
+ event_name.should equal(:line)
+
+ event_name = nil
+ TracePointSpec::B.new.foo
+ event_name.should equal(:line)
+ end
+ end
+
+ it 'converts given event name as string into symbol using to_sym' do
+ event_name = nil
+ (o = mock('return')).should_receive(:to_sym).and_return(:return)
+
+ TracePoint.new(o) { |tp| event_name = tp.event}.enable do
+ event_name.should equal(nil)
+ TracePointSpec.test
+ event_name.should equal(:return)
+ end
+ end
+
+ it 'includes multiple events when multiple event names are passed as params' do
+ event_name = nil
+ TracePoint.new(:end, :call) do |tp|
+ event_name = tp.event
+ end.enable do
+ TracePointSpec.test
+ event_name.should equal(:call)
+
+ TracePointSpec::B.new.foo
+ event_name.should equal(:call)
+
+ class TracePointSpec::B; end
+ event_name.should equal(:end)
+ end
+ end
+
+ it 'raises a TypeError when the given object is not a string/symbol' do
+ o = mock('123')
+ -> { TracePoint.new(o) {}}.should raise_error(TypeError)
+
+ o.should_receive(:to_sym).and_return(123)
+ -> { TracePoint.new(o) {}}.should raise_error(TypeError)
+ end
+
+ ruby_bug "#140740", ""..."2.5" do
+ it 'expects to be called with a block' do
+ -> { TracePoint.new(:line) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "raises a Argument error when the give argument doesn't match an event name" do
+ -> { TracePoint.new(:test) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/tracepoint/parameters_spec.rb b/spec/ruby/core/tracepoint/parameters_spec.rb
new file mode 100644
index 0000000000..550b9572c0
--- /dev/null
+++ b/spec/ruby/core/tracepoint/parameters_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.6" do
+ describe 'TracePoint#parameters' do
+ it 'returns the parameters of block' do
+ f = proc {|x, y, z| }
+ parameters = nil
+ TracePoint.new(:b_call) {|tp| parameters = tp.parameters }.enable do
+ f.call
+ parameters.should == [[:opt, :x], [:opt, :y], [:opt, :z]]
+ end
+ end
+
+ it 'returns the parameters of lambda block' do
+ f = lambda {|x, y, z| }
+ parameters = nil
+ TracePoint.new(:b_call) {|tp| parameters = tp.parameters }.enable do
+ f.call(1, 2, 3)
+ parameters.should == [[:req, :x], [:req, :y], [:req, :z]]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/tracepoint/path_spec.rb b/spec/ruby/core/tracepoint/path_spec.rb
new file mode 100644
index 0000000000..99751b0025
--- /dev/null
+++ b/spec/ruby/core/tracepoint/path_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+
+describe 'TracePoint#path' do
+ it 'returns the path of the file being run' do
+ path = nil
+ TracePoint.new(:line) { |tp| path = tp.path }.enable do
+ path.should == "#{__FILE__}"
+ end
+ end
+
+ it 'equals (eval) inside an eval for :end event' do
+ path = nil
+ TracePoint.new(:end) { |tp| path = tp.path }.enable do
+ eval("class A; end")
+ path.should == '(eval)'
+ end
+ end
+end
diff --git a/spec/ruby/core/tracepoint/raised_exception_spec.rb b/spec/ruby/core/tracepoint/raised_exception_spec.rb
new file mode 100644
index 0000000000..450717b958
--- /dev/null
+++ b/spec/ruby/core/tracepoint/raised_exception_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+
+describe 'TracePoint#raised_exception' do
+ it 'returns value from exception raised on the :raise event' do
+ raised_exception, error_result = nil
+ trace = TracePoint.new(:raise) { |tp| raised_exception = tp.raised_exception }
+ trace.enable do
+ begin
+ raise StandardError
+ rescue => e
+ error_result = e
+ end
+ raised_exception.should equal(error_result)
+ end
+ end
+end
diff --git a/spec/ruby/core/tracepoint/return_value_spec.rb b/spec/ruby/core/tracepoint/return_value_spec.rb
new file mode 100644
index 0000000000..f0ed86bd00
--- /dev/null
+++ b/spec/ruby/core/tracepoint/return_value_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+describe 'TracePoint#return_value' do
+ def test; 'test' end
+
+ it 'returns value from :return event' do
+ trace_value = nil
+ TracePoint.new(:return) { |tp| trace_value = tp.return_value}.enable do
+ test
+ trace_value.should == 'test'
+ end
+ end
+end
diff --git a/spec/ruby/core/tracepoint/self_spec.rb b/spec/ruby/core/tracepoint/self_spec.rb
new file mode 100644
index 0000000000..c76464f8d1
--- /dev/null
+++ b/spec/ruby/core/tracepoint/self_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+
+describe 'TracePoint#self' do
+ it 'return the trace object from event' do
+ trace = nil
+ TracePoint.new(:line) { |tp| trace = tp.self }.enable do
+ trace.equal?(self).should be_true
+ end
+ end
+end
diff --git a/spec/ruby/core/tracepoint/trace_spec.rb b/spec/ruby/core/tracepoint/trace_spec.rb
new file mode 100644
index 0000000000..e5798df9fb
--- /dev/null
+++ b/spec/ruby/core/tracepoint/trace_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe 'TracePoint.trace' do
+ it 'activates the trace automatically' do
+ trace = TracePoint.trace(:call) {}
+ trace.enabled?.should be_true
+ trace.disable
+ end
+end
diff --git a/spec/ruby/core/true/and_spec.rb b/spec/ruby/core/true/and_spec.rb
new file mode 100644
index 0000000000..99e69d3ae0
--- /dev/null
+++ b/spec/ruby/core/true/and_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "TrueClass#&" do
+ it "returns false if other is nil or false, otherwise true" do
+ (true & true).should == true
+ (true & false).should == false
+ (true & nil).should == false
+ (true & "").should == true
+ (true & mock('x')).should == true
+ end
+end
diff --git a/spec/ruby/core/true/dup_spec.rb b/spec/ruby/core/true/dup_spec.rb
new file mode 100644
index 0000000000..369910ab2c
--- /dev/null
+++ b/spec/ruby/core/true/dup_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '2.4' do
+ describe "TrueClass#dup" do
+ it "returns self" do
+ true.dup.should equal(true)
+ end
+ end
+end
diff --git a/spec/ruby/core/true/inspect_spec.rb b/spec/ruby/core/true/inspect_spec.rb
new file mode 100644
index 0000000000..09d1914856
--- /dev/null
+++ b/spec/ruby/core/true/inspect_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "TrueClass#inspect" do
+ it "returns the string 'true'" do
+ true.inspect.should == "true"
+ end
+end
diff --git a/spec/ruby/core/true/or_spec.rb b/spec/ruby/core/true/or_spec.rb
new file mode 100644
index 0000000000..9bf76a62b8
--- /dev/null
+++ b/spec/ruby/core/true/or_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "TrueClass#|" do
+ it "returns true" do
+ (true | true).should == true
+ (true | false).should == true
+ (true | nil).should == true
+ (true | "").should == true
+ (true | mock('x')).should == true
+ end
+end
diff --git a/spec/ruby/core/true/to_s_spec.rb b/spec/ruby/core/true/to_s_spec.rb
new file mode 100644
index 0000000000..30ca354b0c
--- /dev/null
+++ b/spec/ruby/core/true/to_s_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "TrueClass#to_s" do
+ it "returns the string 'true'" do
+ true.to_s.should == "true"
+ end
+end
diff --git a/spec/ruby/core/true/trueclass_spec.rb b/spec/ruby/core/true/trueclass_spec.rb
new file mode 100644
index 0000000000..8f3c311548
--- /dev/null
+++ b/spec/ruby/core/true/trueclass_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+describe "TrueClass" do
+ it ".allocate raises a TypeError" do
+ lambda do
+ TrueClass.allocate
+ end.should raise_error(TypeError)
+ end
+
+ it ".new is undefined" do
+ lambda do
+ TrueClass.new
+ end.should raise_error(NoMethodError)
+ end
+end
diff --git a/spec/ruby/core/true/xor_spec.rb b/spec/ruby/core/true/xor_spec.rb
new file mode 100644
index 0000000000..8f5ecd5075
--- /dev/null
+++ b/spec/ruby/core/true/xor_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "TrueClass#^" do
+ it "returns true if other is nil or false, otherwise false" do
+ (true ^ true).should == false
+ (true ^ false).should == true
+ (true ^ nil).should == true
+ (true ^ "").should == false
+ (true ^ mock('x')).should == false
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/arity_spec.rb b/spec/ruby/core/unboundmethod/arity_spec.rb
new file mode 100644
index 0000000000..cd700b9f9b
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/arity_spec.rb
@@ -0,0 +1,207 @@
+require_relative '../../spec_helper'
+
+describe "UnboundMethod#arity" do
+ SpecEvaluate.desc = "for method definition"
+
+ context "returns zero" do
+ evaluate <<-ruby do
+ def m() end
+ ruby
+
+ method(:m).unbind.arity.should == 0
+ end
+
+ evaluate <<-ruby do
+ def n(&b) end
+ ruby
+
+ method(:n).unbind.arity.should == 0
+ end
+ end
+
+ context "returns positive values" do
+ evaluate <<-ruby do
+ def m(a) end
+ def n(a, b) end
+ def o(a, b, c) end
+ def p(a, b, c, d) end
+ ruby
+
+ method(:m).unbind.arity.should == 1
+ method(:n).unbind.arity.should == 2
+ method(:o).unbind.arity.should == 3
+ method(:p).unbind.arity.should == 4
+ end
+
+ evaluate <<-ruby do
+ def m(a:) end
+ def n(a:, b:) end
+ def o(a: 1, b:, c:, d: 2) end
+ ruby
+
+ method(:m).unbind.arity.should == 1
+ method(:n).unbind.arity.should == 1
+ method(:o).unbind.arity.should == 1
+ end
+
+ evaluate <<-ruby do
+ def m(a, b:) end
+ def n(a, b:, &l) end
+ ruby
+
+ method(:m).unbind.arity.should == 2
+ method(:n).unbind.arity.should == 2
+ end
+
+ evaluate <<-ruby do
+ def m(a, b, c:, d: 1) end
+ def n(a, b, c:, d: 1, **k, &l) end
+ ruby
+
+ method(:m).unbind.arity.should == 3
+ method(:n).unbind.arity.should == 3
+ end
+ end
+
+ context "returns negative values" do
+ evaluate <<-ruby do
+ def m(a=1) end
+ def n(a=1, b=2) end
+ ruby
+
+ method(:m).unbind.arity.should == -1
+ method(:n).unbind.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(a, b=1) end
+ def n(a, b, c=1, d=2) end
+ ruby
+
+ method(:m).unbind.arity.should == -2
+ method(:n).unbind.arity.should == -3
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, *b) end
+ def n(a=1, b=2, *c) end
+ ruby
+
+ method(:m).unbind.arity.should == -1
+ method(:n).unbind.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(*) end
+ def n(*a) end
+ ruby
+
+ method(:m).unbind.arity.should == -1
+ method(:n).unbind.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(a, *) end
+ def n(a, *b) end
+ def o(a, b, *c) end
+ def p(a, b, c, *d) end
+ ruby
+
+ method(:m).unbind.arity.should == -2
+ method(:n).unbind.arity.should == -2
+ method(:o).unbind.arity.should == -3
+ method(:p).unbind.arity.should == -4
+ end
+
+ evaluate <<-ruby do
+ def m(*a, b) end
+ def n(*a, b, c) end
+ def o(*a, b, c, d) end
+ ruby
+
+ method(:m).unbind.arity.should == -2
+ method(:n).unbind.arity.should == -3
+ method(:o).unbind.arity.should == -4
+ end
+
+ evaluate <<-ruby do
+ def m(a, *b, c) end
+ def n(a, b, *c, d, e) end
+ ruby
+
+ method(:m).unbind.arity.should == -3
+ method(:n).unbind.arity.should == -5
+ end
+
+ evaluate <<-ruby do
+ def m(a, b=1, c=2, *d, e, f) end
+ def n(a, b, c=1, *d, e, f, g) end
+ ruby
+
+ method(:m).unbind.arity.should == -4
+ method(:n).unbind.arity.should == -6
+ end
+
+ evaluate <<-ruby do
+ def m(a: 1) end
+ def n(a: 1, b: 2) end
+ ruby
+
+ method(:m).unbind.arity.should == -1
+ method(:n).unbind.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, b: 2) end
+ def n(*a, b: 1) end
+ def o(a=1, b: 2) end
+ def p(a=1, *b, c: 2, &l) end
+ ruby
+
+ method(:m).unbind.arity.should == -1
+ method(:n).unbind.arity.should == -1
+ method(:o).unbind.arity.should == -1
+ method(:p).unbind.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(**k, &l) end
+ def n(*a, **k) end
+ def o(a: 1, b: 2, **k) end
+ ruby
+
+ method(:m).unbind.arity.should == -1
+ method(:n).unbind.arity.should == -1
+ method(:o).unbind.arity.should == -1
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, *b, c:, d: 2, **k, &l) end
+ ruby
+
+ method(:m).unbind.arity.should == -2
+ end
+
+ evaluate <<-ruby do
+ def m(a, b=1, *c, d, e:, f: 2, **k, &l) end
+ def n(a, b=1, *c, d:, e:, f: 2, **k, &l) end
+ def o(a=0, b=1, *c, d, e:, f: 2, **k, &l) end
+ def p(a=0, b=1, *c, d:, e:, f: 2, **k, &l) end
+ ruby
+
+ method(:m).unbind.arity.should == -4
+ method(:n).unbind.arity.should == -3
+ method(:o).unbind.arity.should == -3
+ method(:p).unbind.arity.should == -2
+ end
+ end
+
+ context "for a Method generated by respond_to_missing?" do
+ it "returns -1" do
+ obj = mock("method arity respond_to_missing")
+ obj.should_receive(:respond_to_missing?).and_return(true)
+
+ obj.method(:m).unbind.arity.should == -1
+ end
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/bind_spec.rb b/spec/ruby/core/unboundmethod/bind_spec.rb
new file mode 100644
index 0000000000..328c356906
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/bind_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "UnboundMethod#bind" do
+ before :each do
+ @normal_um = UnboundMethodSpecs::Methods.new.method(:foo).unbind
+ @parent_um = UnboundMethodSpecs::Parent.new.method(:foo).unbind
+ @child1_um = UnboundMethodSpecs::Child1.new.method(:foo).unbind
+ @child2_um = UnboundMethodSpecs::Child2.new.method(:foo).unbind
+ end
+
+ it "raises TypeError if object is not kind_of? the Module the method defined in" do
+ lambda { @normal_um.bind(UnboundMethodSpecs::B.new) }.should raise_error(TypeError)
+ end
+
+ it "returns Method for any object that is kind_of? the Module method was extracted from" do
+ @normal_um.bind(UnboundMethodSpecs::Methods.new).should be_kind_of(Method)
+ end
+
+ it "returns Method on any object when UnboundMethod is unbound from a module" do
+ UnboundMethodSpecs::Mod.instance_method(:from_mod).bind(Object.new).should be_kind_of(Method)
+ end
+
+ it "returns Method returned for obj is equal to one directly returned by obj.method" do
+ obj = UnboundMethodSpecs::Methods.new
+ @normal_um.bind(obj).should == obj.method(:foo)
+ end
+
+ it "returns a callable method" do
+ obj = UnboundMethodSpecs::Methods.new
+ @normal_um.bind(obj).call.should == obj.foo
+ end
+
+ it "binds a Parent's class method to any Child's class methods" do
+ m = UnboundMethodSpecs::Parent.method(:class_method).unbind.bind(UnboundMethodSpecs::Child1)
+ m.should be_an_instance_of(Method)
+ m.call.should == "I am UnboundMethodSpecs::Child1"
+ end
+
+ it "will raise when binding a an object singleton's method to another object" do
+ other = UnboundMethodSpecs::Parent.new
+ p = UnboundMethodSpecs::Parent.new
+ class << p
+ def singleton_method
+ :single
+ end
+ end
+ um = p.method(:singleton_method).unbind
+ lambda{ um.bind(other) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/clone_spec.rb b/spec/ruby/core/unboundmethod/clone_spec.rb
new file mode 100644
index 0000000000..098ee61476
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/clone_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "UnboundMethod#clone" do
+ it "returns a copy of the UnboundMethod" do
+ um1 = UnboundMethodSpecs::Methods.instance_method(:foo)
+ um2 = um1.clone
+
+ (um1 == um2).should == true
+ um1.bind(UnboundMethodSpecs::Methods.new).call.should == um2.bind(UnboundMethodSpecs::Methods.new).call
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/eql_spec.rb b/spec/ruby/core/unboundmethod/eql_spec.rb
new file mode 100644
index 0000000000..cf2c6b5aae
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/eql_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "UnboundMethod#eql?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/unboundmethod/equal_value_spec.rb b/spec/ruby/core/unboundmethod/equal_value_spec.rb
new file mode 100644
index 0000000000..6242b04884
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/equal_value_spec.rb
@@ -0,0 +1,101 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+context "Creating UnboundMethods" do
+ specify "there is no difference between Method#unbind and Module#instance_method" do
+ UnboundMethodSpecs::Methods.instance_method(:foo).should be_kind_of(UnboundMethod)
+ UnboundMethodSpecs::Methods.new.method(:foo).unbind.should be_kind_of(UnboundMethod)
+ end
+end
+
+describe "UnboundMethod#==" do
+ before :all do
+ @from_module = UnboundMethodSpecs::Methods.instance_method(:foo)
+ @from_unbind = UnboundMethodSpecs::Methods.new.method(:foo).unbind
+
+ @with_block = UnboundMethodSpecs::Methods.instance_method(:with_block)
+
+ @includee = UnboundMethodSpecs::Mod.instance_method(:from_mod)
+ @includer = UnboundMethodSpecs::Methods.instance_method(:from_mod)
+
+ @alias_1 = UnboundMethodSpecs::Methods.instance_method(:alias_1)
+ @alias_2 = UnboundMethodSpecs::Methods.instance_method(:alias_2)
+
+ @original_body = UnboundMethodSpecs::Methods.instance_method(:original_body)
+ @identical_body = UnboundMethodSpecs::Methods.instance_method(:identical_body)
+
+ @parent = UnboundMethodSpecs::Parent.instance_method(:foo)
+ @child1 = UnboundMethodSpecs::Child1.instance_method(:foo)
+ @child2 = UnboundMethodSpecs::Child2.instance_method(:foo)
+
+ @child1_alt = UnboundMethodSpecs::Child1.instance_method(:foo)
+
+ @discard_1 = UnboundMethodSpecs::Methods.instance_method(:discard_1)
+ @discard_2 = UnboundMethodSpecs::Methods.instance_method(:discard_2)
+
+ @method_one = UnboundMethodSpecs::Methods.instance_method(:one)
+ @method_two = UnboundMethodSpecs::Methods.instance_method(:two)
+ end
+
+ it "returns true if objects refer to the same method" do
+ (@from_module == @from_module).should == true
+ (@from_unbind == @from_unbind).should == true
+ (@from_module == @from_unbind).should == true
+ (@from_unbind == @from_module).should == true
+ (@with_block == @with_block).should == true
+ end
+
+ it "returns true if either is an alias for the other" do
+ (@from_module == @alias_1).should == true
+ (@alias_1 == @from_module).should == true
+ end
+
+ it "returns true if both are aliases for a third method" do
+ (@from_module == @alias_1).should == true
+ (@alias_1 == @from_module).should == true
+
+ (@from_module == @alias_2).should == true
+ (@alias_2 == @from_module).should == true
+
+ (@alias_1 == @alias_2).should == true
+ (@alias_2 == @alias_1).should == true
+ end
+
+ it "returns true if same method is extracted from the same subclass" do
+ (@child1 == @child1_alt).should == true
+ (@child1_alt == @child1).should == true
+ end
+
+ it "returns false if UnboundMethods are different methods" do
+ (@method_one == @method_two).should == false
+ (@method_two == @method_one).should == false
+ end
+
+ it "returns false if both have identical body but are not the same" do
+ (@original_body == @identical_body).should == false
+ (@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
+
+ 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 methods are the same but added from an included Module" do
+ (@includee == @includer).should == false
+ (@includer == @includee).should == false
+ end
+
+ it "returns false if both have same Module, same name, identical body but not the same" do
+ class UnboundMethodSpecs::Methods
+ def discard_1; :discard; end
+ end
+
+ (@discard_1 == UnboundMethodSpecs::Methods.instance_method(:discard_1)).should == false
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/fixtures/classes.rb b/spec/ruby/core/unboundmethod/fixtures/classes.rb
new file mode 100644
index 0000000000..43e21916bf
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/fixtures/classes.rb
@@ -0,0 +1,86 @@
+module UnboundMethodSpecs
+
+
+ class SourceLocation
+ def self.location # This needs to be on this line
+ :location # for the spec to pass
+ end
+
+ def self.redefined
+ :first
+ end
+
+ def self.redefined
+ :last
+ end
+
+ def original
+ end
+
+ alias :aka :original
+ end
+
+ module Mod
+ def from_mod; end
+ end
+
+ class Methods
+ include Mod
+
+ def foo
+ true
+ end
+
+ def with_block(&block); end
+
+ alias bar foo
+ alias alias_1 foo
+ alias alias_2 foo
+
+ def original_body(); :this; end
+ def identical_body(); :this; end
+
+ def one; end
+ def two(a); end
+ def three(a, b); end
+ def four(a, b, &c); end
+
+ def neg_one(*a); end
+ def neg_two(a, *b); end
+ def neg_three(a, b, *c); end
+ def neg_four(a, b, *c, &d); end
+
+ def discard_1(); :discard; end
+ def discard_2(); :discard; end
+ end
+
+ class Parent
+ def foo; end
+ def self.class_method
+ "I am #{name}"
+ end
+ end
+
+ class Child1 < Parent; end
+ class Child2 < Parent; end
+ class Child3 < Parent
+ class << self
+ alias_method :another_class_method, :class_method
+ end
+ end
+
+ class A
+ def baz(a, b)
+ return [__FILE__, self.class]
+ end
+ def overridden; end
+ end
+
+ class B < A
+ def overridden; end
+ end
+
+ class C < B
+ def overridden; end
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/hash_spec.rb b/spec/ruby/core/unboundmethod/hash_spec.rb
new file mode 100644
index 0000000000..2ea8c8d8a6
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/hash_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "UnboundMethod#hash" do
+ it "needs to be reviewed for spec completeness"
+
+ it "returns the same value for user methods that are eql?" do
+ foo, bar = UnboundMethodSpecs::Methods.instance_method(:foo), UnboundMethodSpecs::Methods.instance_method(:bar)
+ foo.hash.should == bar.hash
+ end
+
+ # See also redmine #6048
+ it "returns the same value for builtin methods that are eql?" do
+ to_s, inspect = Array.instance_method(:to_s), Array.instance_method(:inspect)
+ to_s.hash.should == inspect.hash
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/inspect_spec.rb b/spec/ruby/core/unboundmethod/inspect_spec.rb
new file mode 100644
index 0000000000..cecf542fcd
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/inspect_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/to_s'
+
+describe "UnboundMethod#inspect" do
+ it_behaves_like :unboundmethod_to_s, :inspect
+end
diff --git a/spec/ruby/core/unboundmethod/name_spec.rb b/spec/ruby/core/unboundmethod/name_spec.rb
new file mode 100644
index 0000000000..4d0fb34fc8
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/name_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "UnboundMethod#name" do
+ it "returns the name of the method" do
+ String.instance_method(:upcase).name.should == :upcase
+ end
+
+ it "returns the name even when aliased" do
+ obj = UnboundMethodSpecs::Methods.new
+ obj.method(:foo).unbind.name.should == :foo
+ obj.method(:bar).unbind.name.should == :bar
+ UnboundMethodSpecs::Methods.instance_method(:bar).name.should == :bar
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/owner_spec.rb b/spec/ruby/core/unboundmethod/owner_spec.rb
new file mode 100644
index 0000000000..5f1f4646b3
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/owner_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "UnboundMethod#owner" do
+ it "returns the owner of the method" do
+ String.instance_method(:upcase).owner.should == String
+ end
+
+ it "returns the same owner when aliased in the same classes" do
+ UnboundMethodSpecs::Methods.instance_method(:foo).owner.should == UnboundMethodSpecs::Methods
+ UnboundMethodSpecs::Methods.instance_method(:bar).owner.should == UnboundMethodSpecs::Methods
+ end
+
+ it "returns the class/module it was defined in" do
+ UnboundMethodSpecs::C.instance_method(:baz).owner.should == UnboundMethodSpecs::A
+ UnboundMethodSpecs::Methods.instance_method(:from_mod).owner.should == UnboundMethodSpecs::Mod
+ end
+
+ it "returns the new owner for aliased methods on singleton classes" do
+ parent_singleton_class = UnboundMethodSpecs::Parent.singleton_class
+ child_singleton_class = UnboundMethodSpecs::Child3.singleton_class
+
+ 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
+end
diff --git a/spec/ruby/core/unboundmethod/parameters_spec.rb b/spec/ruby/core/unboundmethod/parameters_spec.rb
new file mode 100644
index 0000000000..2a3cb18196
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/parameters_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "UnboundMethod#parameters" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/unboundmethod/shared/to_s.rb b/spec/ruby/core/unboundmethod/shared/to_s.rb
new file mode 100644
index 0000000000..3987611d3f
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/shared/to_s.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe :unboundmethod_to_s, shared: true do
+ before :each do
+ @from_module = UnboundMethodSpecs::Methods.instance_method(:from_mod)
+ @from_method = UnboundMethodSpecs::Methods.new.method(:from_mod).unbind
+ end
+
+ it "returns a String" do
+ @from_module.send(@method).should be_kind_of(String)
+ @from_method.send(@method).should be_kind_of(String)
+ end
+
+ it "the String reflects that this is an UnboundMethod object" do
+ @from_module.send(@method).should =~ /\bUnboundMethod\b/
+ @from_method.send(@method).should =~ /\bUnboundMethod\b/
+ end
+
+ 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/
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/source_location_spec.rb b/spec/ruby/core/unboundmethod/source_location_spec.rb
new file mode 100644
index 0000000000..b5e6413816
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/source_location_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "UnboundMethod#source_location" do
+ before :each do
+ @method = UnboundMethodSpecs::SourceLocation.method(:location).unbind
+ end
+
+ it "sets the first value to the path of the file in which the method was defined" do
+ file = @method.source_location.first
+ file.should be_an_instance_of(String)
+ file.should == File.realpath('../fixtures/classes.rb', __FILE__)
+ end
+
+ it "sets the last value to a Fixnum representing the line on which the method was defined" do
+ line = @method.source_location.last
+ line.should be_an_instance_of(Fixnum)
+ line.should == 5
+ end
+
+ it "returns the last place the method was defined" do
+ UnboundMethodSpecs::SourceLocation.method(:redefined).unbind.source_location.last.should == 13
+ end
+
+ it "returns the location of the original method even if it was aliased" do
+ UnboundMethodSpecs::SourceLocation.instance_method(:aka).source_location.last.should == 17
+ end
+
+ it "works for define_method methods" do
+ line = nil
+ cls = Class.new do
+ line = __LINE__ + 1
+ define_method(:foo) { }
+ end
+
+ method = cls.instance_method(:foo)
+ method.source_location[0].should =~ /#{__FILE__}/
+ method.source_location[1].should == line
+ end
+
+ it "works for define_singleton_method methods" do
+ line = nil
+ cls = Class.new do
+ line = __LINE__ + 1
+ define_singleton_method(:foo) { }
+ end
+
+ method = cls.method(:foo)
+ method.source_location[0].should =~ /#{__FILE__}/
+ method.source_location[1].should == line
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/super_method_spec.rb b/spec/ruby/core/unboundmethod/super_method_spec.rb
new file mode 100644
index 0000000000..c9fa1ec533
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/super_method_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "UnboundMethod#super_method" do
+ it "returns the method that would be called by super in the method" do
+ meth = UnboundMethodSpecs::C.instance_method(:overridden)
+ meth = meth.super_method
+ meth.should == UnboundMethodSpecs::B.instance_method(:overridden)
+ meth = meth.super_method
+ meth.should == UnboundMethodSpecs::A.instance_method(:overridden)
+ end
+
+ it "returns nil when there's no super method in the parent" do
+ method = Kernel.instance_method(:method)
+ method.super_method.should == nil
+ end
+
+ it "returns nil when the parent's method is removed" do
+ parent = Class.new { def foo; end }
+ child = Class.new(parent) { def foo; end }
+
+ method = child.instance_method(:foo)
+
+ parent.send(:undef_method, :foo)
+
+ method.super_method.should == nil
+ end
+end
diff --git a/spec/ruby/core/unboundmethod/to_s_spec.rb b/spec/ruby/core/unboundmethod/to_s_spec.rb
new file mode 100644
index 0000000000..a508229b49
--- /dev/null
+++ b/spec/ruby/core/unboundmethod/to_s_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/to_s'
+
+describe "UnboundMethod#to_s" do
+ it_behaves_like :unboundmethod_to_s, :to_s
+end
diff --git a/spec/ruby/core/warning/warn_spec.rb b/spec/ruby/core/warning/warn_spec.rb
new file mode 100644
index 0000000000..2844d97e76
--- /dev/null
+++ b/spec/ruby/core/warning/warn_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../spec_helper'
+
+describe "Warning.warn" do
+ ruby_version_is "2.4" do
+ it "complains" do
+ -> {
+ Warning.warn("Chunky bacon!")
+ }.should complain("Chunky bacon!")
+ end
+
+ it "does not add a newline" do
+ ruby_exe("Warning.warn('test')", args: "2>&1").should == "test"
+ end
+
+ it "returns nil" do
+ ruby_exe("p Warning.warn('test')", args: "2>&1").should == "testnil\n"
+ end
+
+ it "extends itself" do
+ Warning.singleton_class.ancestors.should include(Warning)
+ end
+
+ it "has Warning as the method owner" do
+ ruby_exe("p Warning.method(:warn).owner").should == "Warning\n"
+ end
+
+ it "can be overridden" do
+ code = <<-RUBY
+ $stdout.sync = true
+ $stderr.sync = true
+ def Warning.warn(msg)
+ if msg.start_with?("A")
+ puts msg.upcase
+ else
+ super
+ end
+ end
+ Warning.warn("A warning!")
+ Warning.warn("warning from stderr\n")
+ RUBY
+ ruby_exe(code, args: "2>&1").should == %Q[A WARNING!\nwarning from stderr\n]
+ end
+
+ it "is called by parser warnings" do
+ Warning.should_receive(:warn)
+ verbose = $VERBOSE
+ $VERBOSE = false
+ begin
+ eval "{ key: :value, key: :value2 }"
+ ensure
+ $VERBOSE = verbose
+ end
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "is called by Kernel.warn" do
+ Warning.should_receive(:warn)
+ verbose = $VERBOSE
+ $VERBOSE = false
+ begin
+ Kernel.warn("Chunky bacon!")
+ ensure
+ $VERBOSE = verbose
+ end
+ end
+ end
+end
diff --git a/spec/ruby/default.mspec b/spec/ruby/default.mspec
new file mode 100644
index 0000000000..f5e5f7685d
--- /dev/null
+++ b/spec/ruby/default.mspec
@@ -0,0 +1,58 @@
+# Configuration file for Ruby >= 2.0 implementations.
+
+class MSpecScript
+ # Language features specs
+ set :language, [ 'language' ]
+
+ # Core library specs
+ set :core, [ 'core' ]
+
+ # Standard library specs
+ set :library, [ 'library' ]
+
+ # Command line specs
+ set :command_line, [ 'command_line' ]
+
+ # Security specs
+ set :security, [ 'security' ]
+
+ # C extension API specs
+ set :capi, [ 'optional/capi' ]
+
+ # A list of _all_ optional specs
+ set :optional, get(:capi)
+
+ # An ordered list of the directories containing specs to run
+ set :files, get(:command_line) + get(:language) + get(:core) + get(:library) + get(:security) + get(:optional)
+
+ # This set of files is run by mspec ci
+ set :ci_files, get(:files)
+
+ # The default implementation to run the specs.
+ # TODO: this needs to be more sophisticated since the
+ # executable is not consistently named.
+ set :target, 'ruby'
+
+ set :backtrace_filter, /mspec\//
+
+ set :tags_patterns, [
+ [%r(language/), 'tags/1.9/language/'],
+ [%r(core/), 'tags/1.9/core/'],
+ [%r(command_line/), 'tags/1.9/command_line/'],
+ [%r(library/), 'tags/1.9/library/'],
+ [%r(security/), 'tags/1.9/security/'],
+ [/_spec.rb$/, '_tags.txt']
+ ]
+
+ set :toplevel_constants_excludes, [
+ /\wSpecs?$/,
+ /^CS_CONST\d/,
+ ]
+
+ # Enable features
+ MSpec.enable_feature :fiber
+ MSpec.enable_feature :fiber_library
+ MSpec.enable_feature :fork if respond_to?(:fork, true)
+ MSpec.enable_feature :encoding
+ MSpec.enable_feature :mjit if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
+end
diff --git a/spec/ruby/fixtures/basicobject/method_missing.rb b/spec/ruby/fixtures/basicobject/method_missing.rb
new file mode 100644
index 0000000000..17a0fe904c
--- /dev/null
+++ b/spec/ruby/fixtures/basicobject/method_missing.rb
@@ -0,0 +1,55 @@
+module KernelSpecs
+ module ModuleNoMM
+ class << self
+ def method_public() :module_public_method end
+
+ def method_protected() :module_private_method end
+ protected :method_protected
+
+ def method_private() :module_private_method end
+ private :method_private
+ end
+ end
+
+ module ModuleMM
+ class << self
+ def method_missing(*args) :module_method_missing end
+
+ def method_public() :module_public_method end
+
+ def method_protected() :module_private_method end
+ protected :method_protected
+
+ def method_private() :module_private_method end
+ private :method_private
+ end
+ end
+
+ class ClassNoMM
+ class << self
+ def method_public() :class_public_method end
+
+ def method_protected() :class_private_method end
+ protected :method_protected
+
+ def method_private() :class_private_method end
+ private :method_private
+ end
+
+ def method_public() :instance_public_method end
+
+ def method_protected() :instance_private_method end
+ protected :method_protected
+
+ def method_private() :instance_private_method end
+ private :method_private
+ end
+
+ class ClassMM < ClassNoMM
+ class << self
+ def method_missing(*args) :class_method_missing end
+ end
+
+ def method_missing(*args) :instance_method_missing end
+ end
+end
diff --git a/spec/ruby/fixtures/class.rb b/spec/ruby/fixtures/class.rb
new file mode 100644
index 0000000000..9609eb6f3c
--- /dev/null
+++ b/spec/ruby/fixtures/class.rb
@@ -0,0 +1,136 @@
+module ClassSpecs
+
+ def self.sclass_with_block
+ class << self
+ yield
+ end
+ end
+
+ def self.sclass_with_return
+ class << self
+ return :inner
+ end
+ return :outer
+ end
+
+ class A; end
+
+ def self.string_class_variables(obj)
+ obj.class_variables.map { |x| x.to_s }
+ end
+
+ def self.string_instance_variables(obj)
+ obj.instance_variables.map { |x| x.to_s }
+ end
+
+ class B
+ @@cvar = :cvar
+ @ivar = :ivar
+
+ end
+
+ class C
+ def self.make_class_variable
+ @@cvar = :cvar
+ end
+
+ def self.make_class_instance_variable
+ @civ = :civ
+ end
+ end
+
+ class D
+ def make_class_variable
+ @@cvar = :cvar
+ end
+ end
+
+ class E
+ def self.cmeth() :cmeth end
+ def meth() :meth end
+
+ class << self
+ def smeth() :smeth end
+ end
+
+ CONSTANT = :constant!
+ end
+
+ class F; end
+ class F
+ def meth() :meth end
+ end
+ class F
+ def another() :another end
+ end
+
+ class G
+ def override() :nothing end
+ def override() :override end
+ end
+
+ class Container
+ class A; end
+ class B; end
+ end
+
+ O = Object.new
+ class << O
+ def smeth
+ :smeth
+ end
+ end
+
+ class H
+ def self.inherited(sub)
+ track_inherited << sub
+ end
+
+ def self.track_inherited
+ @inherited_modules ||= []
+ end
+ end
+
+ class K < H; end
+
+ class I
+ class J < self
+ end
+ end
+
+ class K
+ def example_instance_method
+ end
+ def self.example_class_method
+ end
+ end
+
+ class L; end
+
+ class M < L; end
+
+ # Can't use a method here because of class definition in method body error
+ ANON_CLASS_FOR_NEW = lambda do
+ Class.new do
+ class NamedInModule
+ end
+
+ def self.get_class_name
+ NamedInModule.name
+ end
+ end
+ end
+end
+
+class Class
+ def example_instance_method_of_class; end
+ def self.example_class_method_of_class; end
+end
+class << Class
+ def example_instance_method_of_singleton_class; end
+ def self.example_class_method_of_singleton_class; end
+end
+class Object
+ def example_instance_method_of_object; end
+ def self.example_class_method_of_object; end
+end
diff --git a/spec/ruby/fixtures/class_variables.rb b/spec/ruby/fixtures/class_variables.rb
new file mode 100644
index 0000000000..02ca042230
--- /dev/null
+++ b/spec/ruby/fixtures/class_variables.rb
@@ -0,0 +1,58 @@
+module ClassVariablesSpec
+
+ class ClassA
+ @@cvar_a = :cvar_a
+
+ def cvar_a
+ @@cvar_a
+ end
+
+ def cvar_a=(val)
+ @@cvar_a = val
+ end
+ end
+
+ class ClassB < ClassA; end
+
+ # Extended in ClassC
+ module ModuleM
+ @@cvar_m = :value
+
+ def cvar_m
+ @@cvar_m
+ end
+
+ def cvar_m=(val)
+ @@cvar_m = val
+ end
+ end
+
+ # Extended in ModuleO
+ module ModuleN
+ @@cvar_n = :value
+
+ def cvar_n
+ @@cvar_n
+ end
+
+ def cvar_n=(val)
+ @@cvar_n = val
+ end
+ end
+
+ module ModuleO
+ extend ModuleN
+ end
+
+ class ClassC
+ extend ModuleM
+
+ def self.cvar_defined?
+ self.class_variable_defined?(:@@cvar)
+ end
+
+ def self.cvar_c=(val)
+ @@cvar = val
+ end
+ end
+end
diff --git a/spec/ruby/fixtures/code/a/load_fixture.bundle b/spec/ruby/fixtures/code/a/load_fixture.bundle
new file mode 100644
index 0000000000..30b53d7c0c
--- /dev/null
+++ b/spec/ruby/fixtures/code/a/load_fixture.bundle
@@ -0,0 +1 @@
+ScratchPad << :ext_bundle
diff --git a/spec/ruby/fixtures/code/a/load_fixture.dll b/spec/ruby/fixtures/code/a/load_fixture.dll
new file mode 100644
index 0000000000..77c65b315b
--- /dev/null
+++ b/spec/ruby/fixtures/code/a/load_fixture.dll
@@ -0,0 +1 @@
+ScratchPad << :ext_dll
diff --git a/spec/ruby/fixtures/code/a/load_fixture.so b/spec/ruby/fixtures/code/a/load_fixture.so
new file mode 100644
index 0000000000..4d02dea086
--- /dev/null
+++ b/spec/ruby/fixtures/code/a/load_fixture.so
@@ -0,0 +1 @@
+ScratchPad << :ext_so
diff --git a/spec/ruby/fixtures/code/b/load_fixture.rb b/spec/ruby/fixtures/code/b/load_fixture.rb
new file mode 100644
index 0000000000..4a6e9c9601
--- /dev/null
+++ b/spec/ruby/fixtures/code/b/load_fixture.rb
@@ -0,0 +1 @@
+ScratchPad << :loaded
diff --git a/spec/ruby/fixtures/code/concurrent.rb b/spec/ruby/fixtures/code/concurrent.rb
new file mode 100644
index 0000000000..054b8fc055
--- /dev/null
+++ b/spec/ruby/fixtures/code/concurrent.rb
@@ -0,0 +1,12 @@
+ScratchPad.recorded << :con_pre
+Thread.current[:in_concurrent_rb] = true
+
+if t = Thread.current[:wait_for]
+ Thread.pass until t.backtrace && t.backtrace.any? { |call| call.include? 'require' }
+end
+
+if Thread.current[:con_raise]
+ raise "con1"
+end
+
+ScratchPad.recorded << :con_post
diff --git a/spec/ruby/fixtures/code/concurrent2.rb b/spec/ruby/fixtures/code/concurrent2.rb
new file mode 100644
index 0000000000..08a7264023
--- /dev/null
+++ b/spec/ruby/fixtures/code/concurrent2.rb
@@ -0,0 +1,8 @@
+ScratchPad.recorded << :con2_pre
+
+Thread.current[:in_concurrent_rb2] = true
+
+t = Thread.current[:concurrent_require_thread]
+Thread.pass until t[:in_concurrent_rb3]
+
+ScratchPad.recorded << :con2_post
diff --git a/spec/ruby/fixtures/code/concurrent3.rb b/spec/ruby/fixtures/code/concurrent3.rb
new file mode 100644
index 0000000000..edb441d15d
--- /dev/null
+++ b/spec/ruby/fixtures/code/concurrent3.rb
@@ -0,0 +1,2 @@
+ScratchPad.recorded << :con3
+Thread.current[:in_concurrent_rb3] = true
diff --git a/spec/ruby/fixtures/code/file_fixture.rb b/spec/ruby/fixtures/code/file_fixture.rb
new file mode 100644
index 0000000000..27388c7d8d
--- /dev/null
+++ b/spec/ruby/fixtures/code/file_fixture.rb
@@ -0,0 +1 @@
+ScratchPad << __FILE__
diff --git a/spec/ruby/fixtures/code/gem/load_fixture.rb b/spec/ruby/fixtures/code/gem/load_fixture.rb
new file mode 100644
index 0000000000..e1806de201
--- /dev/null
+++ b/spec/ruby/fixtures/code/gem/load_fixture.rb
@@ -0,0 +1 @@
+ScratchPad << :loaded_gem
diff --git a/spec/ruby/fixtures/code/line_fixture.rb b/spec/ruby/fixtures/code/line_fixture.rb
new file mode 100644
index 0000000000..2a2a0568cd
--- /dev/null
+++ b/spec/ruby/fixtures/code/line_fixture.rb
@@ -0,0 +1,5 @@
+ScratchPad << __LINE__
+
+# line 3
+
+ScratchPad << __LINE__
diff --git a/spec/ruby/fixtures/code/load_ext_fixture.rb b/spec/ruby/fixtures/code/load_ext_fixture.rb
new file mode 100644
index 0000000000..4a6e9c9601
--- /dev/null
+++ b/spec/ruby/fixtures/code/load_ext_fixture.rb
@@ -0,0 +1 @@
+ScratchPad << :loaded
diff --git a/spec/ruby/fixtures/code/load_fixture b/spec/ruby/fixtures/code/load_fixture
new file mode 100644
index 0000000000..1b76dc8cad
--- /dev/null
+++ b/spec/ruby/fixtures/code/load_fixture
@@ -0,0 +1 @@
+ScratchPad << :no_ext
diff --git a/spec/ruby/fixtures/code/load_fixture.bundle b/spec/ruby/fixtures/code/load_fixture.bundle
new file mode 100644
index 0000000000..30b53d7c0c
--- /dev/null
+++ b/spec/ruby/fixtures/code/load_fixture.bundle
@@ -0,0 +1 @@
+ScratchPad << :ext_bundle
diff --git a/spec/ruby/fixtures/code/load_fixture.dll b/spec/ruby/fixtures/code/load_fixture.dll
new file mode 100644
index 0000000000..77c65b315b
--- /dev/null
+++ b/spec/ruby/fixtures/code/load_fixture.dll
@@ -0,0 +1 @@
+ScratchPad << :ext_dll
diff --git a/spec/ruby/fixtures/code/load_fixture.ext b/spec/ruby/fixtures/code/load_fixture.ext
new file mode 100644
index 0000000000..f25b8e2951
--- /dev/null
+++ b/spec/ruby/fixtures/code/load_fixture.ext
@@ -0,0 +1 @@
+ScratchPad << :no_rb_ext
diff --git a/spec/ruby/fixtures/code/load_fixture.ext.bundle b/spec/ruby/fixtures/code/load_fixture.ext.bundle
new file mode 100644
index 0000000000..30b53d7c0c
--- /dev/null
+++ b/spec/ruby/fixtures/code/load_fixture.ext.bundle
@@ -0,0 +1 @@
+ScratchPad << :ext_bundle
diff --git a/spec/ruby/fixtures/code/load_fixture.ext.dll b/spec/ruby/fixtures/code/load_fixture.ext.dll
new file mode 100644
index 0000000000..77c65b315b
--- /dev/null
+++ b/spec/ruby/fixtures/code/load_fixture.ext.dll
@@ -0,0 +1 @@
+ScratchPad << :ext_dll
diff --git a/spec/ruby/fixtures/code/load_fixture.ext.rb b/spec/ruby/fixtures/code/load_fixture.ext.rb
new file mode 100644
index 0000000000..4a6e9c9601
--- /dev/null
+++ b/spec/ruby/fixtures/code/load_fixture.ext.rb
@@ -0,0 +1 @@
+ScratchPad << :loaded
diff --git a/spec/ruby/fixtures/code/load_fixture.ext.so b/spec/ruby/fixtures/code/load_fixture.ext.so
new file mode 100644
index 0000000000..4d02dea086
--- /dev/null
+++ b/spec/ruby/fixtures/code/load_fixture.ext.so
@@ -0,0 +1 @@
+ScratchPad << :ext_so
diff --git a/spec/ruby/fixtures/code/load_fixture.rb b/spec/ruby/fixtures/code/load_fixture.rb
new file mode 100644
index 0000000000..4a6e9c9601
--- /dev/null
+++ b/spec/ruby/fixtures/code/load_fixture.rb
@@ -0,0 +1 @@
+ScratchPad << :loaded
diff --git a/spec/ruby/fixtures/code/load_fixture.so b/spec/ruby/fixtures/code/load_fixture.so
new file mode 100644
index 0000000000..4d02dea086
--- /dev/null
+++ b/spec/ruby/fixtures/code/load_fixture.so
@@ -0,0 +1 @@
+ScratchPad << :ext_so
diff --git a/spec/ruby/fixtures/code/load_fixture_and__FILE__.rb b/spec/ruby/fixtures/code/load_fixture_and__FILE__.rb
new file mode 100644
index 0000000000..27388c7d8d
--- /dev/null
+++ b/spec/ruby/fixtures/code/load_fixture_and__FILE__.rb
@@ -0,0 +1 @@
+ScratchPad << __FILE__
diff --git a/spec/ruby/fixtures/code/load_wrap_method_fixture.rb b/spec/ruby/fixtures/code/load_wrap_method_fixture.rb
new file mode 100644
index 0000000000..b617473b4d
--- /dev/null
+++ b/spec/ruby/fixtures/code/load_wrap_method_fixture.rb
@@ -0,0 +1,9 @@
+def top_level_method
+ ::ScratchPad << :load_wrap_loaded
+end
+
+begin
+ top_level_method
+rescue NameError
+ ::ScratchPad << :load_wrap_error
+end
diff --git a/spec/ruby/fixtures/code/methods_fixture.rb b/spec/ruby/fixtures/code/methods_fixture.rb
new file mode 100644
index 0000000000..d42b5e4018
--- /dev/null
+++ b/spec/ruby/fixtures/code/methods_fixture.rb
@@ -0,0 +1,364 @@
+def foo1
+end
+
+def foo2
+end
+
+def foo3
+end
+
+def foo4
+end
+
+def foo5
+end
+
+def foo6
+end
+
+def foo7
+end
+
+def foo8
+end
+
+def foo9
+end
+
+def foo10
+end
+
+def foo11
+end
+
+def foo12
+end
+
+def foo13
+end
+
+def foo14
+end
+
+def foo15
+end
+
+def foo16
+end
+
+def foo17
+end
+
+def foo18
+end
+
+def foo19
+end
+
+def foo20
+end
+
+def foo21
+end
+
+def foo22
+end
+
+def foo23
+end
+
+def foo24
+end
+
+def foo25
+end
+
+def foo26
+end
+
+def foo27
+end
+
+def foo28
+end
+
+def foo29
+end
+
+def foo30
+end
+
+def foo31
+end
+
+def foo32
+end
+
+def foo33
+end
+
+def foo34
+end
+
+def foo35
+end
+
+def foo36
+end
+
+def foo37
+end
+
+def foo38
+end
+
+def foo39
+end
+
+def foo40
+end
+
+def foo41
+end
+
+def foo42
+end
+
+def foo43
+end
+
+def foo44
+end
+
+def foo45
+end
+
+def foo46
+end
+
+def foo47
+end
+
+def foo48
+end
+
+def foo49
+end
+
+def foo50
+end
+
+def foo51
+end
+
+def foo52
+end
+
+def foo53
+end
+
+def foo54
+end
+
+def foo55
+end
+
+def foo56
+end
+
+def foo57
+end
+
+def foo58
+end
+
+def foo59
+end
+
+def foo60
+end
+
+def foo61
+end
+
+def foo62
+end
+
+def foo63
+end
+
+def foo64
+end
+
+def foo65
+end
+
+def foo66
+end
+
+def foo67
+end
+
+def foo68
+end
+
+def foo69
+end
+
+def foo70
+end
+
+def foo71
+end
+
+def foo72
+end
+
+def foo73
+end
+
+def foo74
+end
+
+def foo75
+end
+
+def foo76
+end
+
+def foo77
+end
+
+def foo78
+end
+
+def foo79
+end
+
+def foo80
+end
+
+def foo81
+end
+
+def foo82
+end
+
+def foo83
+end
+
+def foo84
+end
+
+def foo85
+end
+
+def foo86
+end
+
+def foo87
+end
+
+def foo88
+end
+
+def foo89
+end
+
+def foo90
+end
+
+def foo91
+end
+
+def foo92
+end
+
+def foo93
+end
+
+def foo94
+end
+
+def foo95
+end
+
+def foo96
+end
+
+def foo97
+end
+
+def foo98
+end
+
+def foo99
+end
+
+def foo100
+end
+
+def foo101
+end
+
+def foo102
+end
+
+def foo103
+end
+
+def foo104
+end
+
+def foo105
+end
+
+def foo106
+end
+
+def foo107
+end
+
+def foo108
+end
+
+def foo109
+end
+
+def foo110
+end
+
+def foo111
+end
+
+def foo112
+end
+
+def foo113
+end
+
+def foo114
+end
+
+def foo115
+end
+
+def foo116
+end
+
+def foo117
+end
+
+def foo118
+end
+
+def foo119
+end
+
+def foo120
+end
+
+def foo121
+end
+
+ScratchPad << :loaded
diff --git a/spec/ruby/fixtures/code/raise_fixture.rb b/spec/ruby/fixtures/code/raise_fixture.rb
new file mode 100644
index 0000000000..9419089a06
--- /dev/null
+++ b/spec/ruby/fixtures/code/raise_fixture.rb
@@ -0,0 +1 @@
+raise "Exception loading a file"
diff --git a/spec/ruby/fixtures/code/recursive_load_fixture.rb b/spec/ruby/fixtures/code/recursive_load_fixture.rb
new file mode 100644
index 0000000000..18b144d44a
--- /dev/null
+++ b/spec/ruby/fixtures/code/recursive_load_fixture.rb
@@ -0,0 +1,5 @@
+ScratchPad << :loaded
+
+if ScratchPad.recorded == [:loaded]
+ load File.expand_path("../recursive_load_fixture.rb", __FILE__)
+end
diff --git a/spec/ruby/fixtures/code/recursive_require_fixture.rb b/spec/ruby/fixtures/code/recursive_require_fixture.rb
new file mode 100644
index 0000000000..ebeba34fce
--- /dev/null
+++ b/spec/ruby/fixtures/code/recursive_require_fixture.rb
@@ -0,0 +1,3 @@
+require_relative 'recursive_require_fixture'
+
+ScratchPad << :loaded
diff --git a/spec/ruby/fixtures/code/symlink/symlink1.rb b/spec/ruby/fixtures/code/symlink/symlink1.rb
new file mode 100644
index 0000000000..6a006eef14
--- /dev/null
+++ b/spec/ruby/fixtures/code/symlink/symlink1.rb
@@ -0,0 +1 @@
+require_relative 'symlink2/symlink2'
diff --git a/spec/ruby/fixtures/code/symlink/symlink2/symlink2.rb b/spec/ruby/fixtures/code/symlink/symlink2/symlink2.rb
new file mode 100644
index 0000000000..4a6e9c9601
--- /dev/null
+++ b/spec/ruby/fixtures/code/symlink/symlink2/symlink2.rb
@@ -0,0 +1 @@
+ScratchPad << :loaded
diff --git a/spec/ruby/fixtures/code/wrap_fixture.rb b/spec/ruby/fixtures/code/wrap_fixture.rb
new file mode 100644
index 0000000000..b83a8970d7
--- /dev/null
+++ b/spec/ruby/fixtures/code/wrap_fixture.rb
@@ -0,0 +1,3 @@
+class LoadSpecWrap
+ ScratchPad << self
+end
diff --git a/spec/ruby/fixtures/code_loading.rb b/spec/ruby/fixtures/code_loading.rb
new file mode 100644
index 0000000000..d6cf0edb47
--- /dev/null
+++ b/spec/ruby/fixtures/code_loading.rb
@@ -0,0 +1,26 @@
+module CodeLoadingSpecs
+ # The #require instance method is private, so this class enables
+ # calling #require like obj.require(file). This is used to share
+ # specs between Kernel#require and Kernel.require.
+ class Method
+ def require(name)
+ super name
+ end
+
+ def load(name, wrap=false)
+ super
+ end
+ end
+
+ def self.spec_setup
+ @saved_loaded_features = $LOADED_FEATURES.clone
+ @saved_load_path = $LOAD_PATH.clone
+ ScratchPad.record []
+ end
+
+ def self.spec_cleanup
+ $LOADED_FEATURES.replace @saved_loaded_features
+ $LOAD_PATH.replace @saved_load_path
+ ScratchPad.clear
+ end
+end
diff --git a/spec/ruby/fixtures/constants.rb b/spec/ruby/fixtures/constants.rb
new file mode 100644
index 0000000000..e5b20596ef
--- /dev/null
+++ b/spec/ruby/fixtures/constants.rb
@@ -0,0 +1,288 @@
+# Contains all static code examples of all constants behavior in language and
+# library specs. The specs include language/constants_spec.rb and the specs
+# for Module#const_defined?, Module#const_get, Module#const_set,
+# Module#remove_const, Module#const_missing and Module#constants.
+#
+# Rather than defining a class structure for each example, a canonical set of
+# classes is used along with numerous constants, in most cases, a unique
+# constant for each facet of behavior. This potentially leads to some
+# redundancy but hopefully the minimal redundancy that includes reasonable
+# variety in class and module configurations, including hierarchy,
+# containment, inclusion, singletons and toplevel.
+#
+# Constants are numbered for for uniqueness. The CS_ prefix is uniformly used
+# and is to minimize clashes with other toplevel constants (see e.g. ModuleA
+# which is included in Object). Constant values are symbols. A numbered suffix
+# is used to distinguish constants with the same name defined in different
+# areas (e.g. CS_CONST10 has values :const10_1, :const10_2, etc.).
+#
+# Methods are named after the constants they reference (e.g. ClassA.const10
+# references CS_CONST10). Where it is reasonable to do so, both class and
+# instance methods are defined. This is an instance of redundancy (class
+# methods should behave no differently than instance methods) but is useful
+# for ensuring compliance in implementations.
+
+
+# This constant breaks the rule of defining all constants, classes, modules
+# inside a module namespace for the particular specs, however, it is needed
+# for completeness. No other constant of this name should be defined in the
+# specs.
+CS_CONST1 = :const1 # only defined here
+
+module ConstantSpecs
+
+ # Included at toplevel
+ module ModuleA
+ CS_CONST10 = :const10_1
+ CS_CONST12 = :const12_2
+ CS_CONST13 = :const13
+ CS_CONST21 = :const21_2
+ end
+
+ # Included in ParentA
+ module ModuleB
+ CS_CONST10 = :const10_9
+ CS_CONST11 = :const11_2
+ CS_CONST12 = :const12_1
+ end
+
+ # Included in ChildA
+ module ModuleC
+ CS_CONST10 = :const10_4
+ CS_CONST15 = :const15_1
+ end
+
+ # Included in ChildA metaclass
+ module ModuleH
+ CS_CONST10 = :const10_7
+ end
+
+ # Included in ModuleD
+ module ModuleM
+ CS_CONST10 = :const10_11
+ CS_CONST24 = :const24
+ end
+
+ # Included in ContainerA
+ module ModuleD
+ include ModuleM
+
+ CS_CONST10 = :const10_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.
+
+ class ClassA
+ CS_CONST10 = :const10_10
+ CS_CONST16 = :const16
+ CS_CONST17 = :const17_2
+ CS_CONST22 = :const22_1
+
+ def self.const_missing(const)
+ const
+ end
+
+ def self.constx; CS_CONSTX; end
+ def self.const10; CS_CONST10; end
+ def self.const16; ParentA.const16; end
+ def self.const22; ParentA.const22 { CS_CONST22 }; end
+
+ def const10; CS_CONST10; end
+ def constx; CS_CONSTX; end
+ end
+
+ class ParentA
+ include ModuleB
+
+ CS_CONST4 = :const4
+ CS_CONST10 = :const10_5
+ CS_CONST11 = :const11_1
+ CS_CONST15 = :const15_2
+ CS_CONST20 = :const20_2
+ CS_CONST21 = :const21_1
+ CS_CONST22 = :const22_2
+
+ def self.constx; CS_CONSTX; end
+ def self.const10; CS_CONST10; end
+ def self.const16; CS_CONST16; end
+ def self.const22; yield; end
+
+ def const10; CS_CONST10; end
+ def constx; CS_CONSTX; end
+ end
+
+ class ContainerA
+ include ModuleD
+
+ CS_CONST5 = :const5
+ CS_CONST10 = :const10_2
+ CS_CONST23 = :const23
+
+ class ChildA < ParentA
+ include ModuleC
+
+ class << self
+ include ModuleH
+
+ CS_CONST10 = :const10_6
+ CS_CONST14 = :const14_1
+ CS_CONST19 = :const19_1
+
+ def const19; CS_CONST19; end
+ end
+
+ CS_CONST6 = :const6
+ CS_CONST10 = :const10_3
+ CS_CONST19 = :const19_2
+
+ def self.const10; CS_CONST10; end
+ def self.const11; CS_CONST11; end
+ def self.const12; CS_CONST12; end
+ def self.const13; CS_CONST13; end
+ def self.const15; CS_CONST15; end
+ def self.const21; CS_CONST21; end
+
+ def const10; CS_CONST10; end
+ def const11; CS_CONST11; end
+ def const12; CS_CONST12; end
+ def const13; CS_CONST13; end
+ def const15; CS_CONST15; end
+ end
+
+ def self.const10; CS_CONST10; end
+
+ def const10; CS_CONST10; end
+ end
+
+ class ContainerA::ChildA
+ def self.const23; CS_CONST23; end
+ end
+
+ class ::Object
+ CS_CONST20 = :const20_1
+
+ module ConstantSpecs
+ class ContainerA
+ class ChildA
+ def self.const20; CS_CONST20; end
+ end
+ end
+ end
+ end
+
+ # Included in ParentB
+ module ModuleE
+ end
+
+ # Included in ChildB
+ module ModuleF
+ end
+
+ # Included in ContainerB
+ module ModuleG
+ end
+
+ # The following classes/modules have the same structure as the ones above
+ # but the constants are set as the specs are run.
+
+ class ClassB
+ def self.const201; CS_CONST201; end
+ def self.const209; ParentB.const209; end
+ def self.const210; ParentB.const210 { CS_CONST210 }; end
+
+ def const201; CS_CONST201; end
+ end
+
+ class ParentB
+ include ModuleE
+
+ def self.const201; CS_CONST201; end
+ def self.const209; CS_CONST209; end
+ def self.const210; yield; end
+
+ def const201; CS_CONST201; end
+ end
+
+ class ContainerB
+ include ModuleG
+
+ class ChildB < ParentB
+ include ModuleF
+
+ class << self
+ def const206; CS_CONST206; end
+ end
+
+ def self.const201; CS_CONST201; end
+ def self.const202; CS_CONST202; end
+ def self.const203; CS_CONST203; end
+ def self.const204; CS_CONST204; end
+ def self.const205; CS_CONST205; end
+ def self.const212; CS_CONST212; end
+ def self.const213; CS_CONST213; end
+
+ def const201; CS_CONST201; end
+ def const202; CS_CONST202; end
+ def const203; CS_CONST203; end
+ def const204; CS_CONST204; end
+ def const205; CS_CONST205; end
+ def const213; CS_CONST213; end
+ end
+
+ def self.const201; CS_CONST201; end
+ end
+
+ class ContainerB::ChildB
+ def self.const214; CS_CONST214; end
+ end
+
+ class ::Object
+ module ConstantSpecs
+ class ContainerB
+ class ChildB
+ def self.const211; CS_CONST211; end
+ end
+ end
+ end
+ end
+
+ # Constants
+ CS_CONST2 = :const2 # only defined here
+ CS_CONST17 = :const17_1
+
+ class << self
+ CS_CONST14 = :const14_2
+ end
+
+ # Singleton
+ a = ClassA.new
+ def a.const17; CS_CONST17; end
+ CS_CONST18 = a
+
+ b = ClassB.new
+ def b.const207; CS_CONST207; end
+ CS_CONST208 = b
+
+ # Methods
+ def self.get_const; self; end
+
+ def const10; CS_CONST10; end
+
+ class ClassC
+ CS_CONST1 = 1
+
+ class ClassE
+ CS_CONST2 = 2
+ end
+ end
+
+ class ClassD < ClassC
+ end
+
+ CS_PRIVATE = :cs_private
+ private_constant :CS_PRIVATE
+end
+
+include ConstantSpecs::ModuleA
diff --git a/spec/ruby/fixtures/enumerator/classes.rb b/spec/ruby/fixtures/enumerator/classes.rb
new file mode 100644
index 0000000000..6f285b8efa
--- /dev/null
+++ b/spec/ruby/fixtures/enumerator/classes.rb
@@ -0,0 +1,15 @@
+module EnumSpecs
+ class Numerous
+
+ include Enumerable
+
+ def initialize(*list)
+ @list = list.empty? ? [2, 5, 3, 6, 1, 4] : list
+ end
+
+ def each
+ @list.each { |i| yield i }
+ end
+ end
+
+end
diff --git a/spec/ruby/fixtures/math/common.rb b/spec/ruby/fixtures/math/common.rb
new file mode 100644
index 0000000000..024732fa7a
--- /dev/null
+++ b/spec/ruby/fixtures/math/common.rb
@@ -0,0 +1,3 @@
+class IncludesMath
+ include Math
+end
diff --git a/spec/ruby/fixtures/rational.rb b/spec/ruby/fixtures/rational.rb
new file mode 100644
index 0000000000..844d7f9820
--- /dev/null
+++ b/spec/ruby/fixtures/rational.rb
@@ -0,0 +1,14 @@
+module RationalSpecs
+ class SubNumeric < Numeric
+ def initialize(value)
+ @value = Rational(value)
+ end
+
+ def to_r
+ @value
+ end
+ end
+
+ class CoerceError < StandardError
+ end
+end
diff --git a/spec/ruby/fixtures/reflection.rb b/spec/ruby/fixtures/reflection.rb
new file mode 100644
index 0000000000..fe004f6a82
--- /dev/null
+++ b/spec/ruby/fixtures/reflection.rb
@@ -0,0 +1,352 @@
+# These modules and classes are fixtures used by the Ruby reflection specs.
+# These include specs for methods:
+#
+# Module:
+# instance_methods
+# public_instance_methods
+# protected_instance_methods
+# private_instance_methods
+#
+# Kernel:
+# methods
+# public_methods
+# protected_methods
+# private_methods
+# singleton_methods
+#
+# The following naming scheme is used to keep the method names short and still
+# communicate the relevant facts about the methods:
+#
+# X[s]_VIS
+#
+# where
+#
+# X is the name of the module or class in lower case
+# s is the literal character 's' for singleton methods
+# VIS is the first three letters of the corresponding visibility
+# pub(lic), pro(tected), pri(vate)
+#
+# For example:
+#
+# l_pub is a public method on module L
+# ls_pri is a private singleton method on module L
+
+module ReflectSpecs
+ # An object with no singleton methods.
+ def self.o
+ mock("Object with no singleton methods")
+ end
+
+ # An object with singleton methods.
+ def self.os
+ obj = mock("Object with singleton methods")
+ class << obj
+ def os_pub; :os_pub; end
+
+ def os_pro; :os_pro; end
+ protected :os_pro
+
+ def os_pri; :os_pri; end
+ private :os_pri
+ end
+ obj
+ end
+
+ # An object extended with a module.
+ def self.oe
+ obj = mock("Object extended")
+ obj.extend M
+ obj
+ end
+
+ # An object with duplicate methods extended with a module.
+ def self.oed
+ obj = mock("Object extended")
+ obj.extend M
+
+ class << obj
+ def pub; :pub; end
+
+ def pro; :pro; end
+ protected :pro
+
+ def pri; :pri; end
+ private :pri
+ end
+
+ obj
+ end
+
+ # An object extended with two modules.
+ def self.oee
+ obj = mock("Object extended twice")
+ obj.extend M
+ obj.extend N
+ obj
+ end
+
+ # An object extended with a module including a module.
+ def self.oei
+ obj = mock("Object extended, included")
+ obj.extend N
+ obj
+ end
+
+ # A simple module.
+ module L
+ class << self
+ def ls_pub; :ls_pub; end
+
+ def ls_pro; :ls_pro; end
+ protected :ls_pro
+
+ def ls_pri; :ls_pri; end
+ private :ls_pri
+ end
+
+ def l_pub; :l_pub; end
+
+ def l_pro; :l_pro; end
+ protected :l_pro
+
+ def l_pri; :l_pri; end
+ private :l_pri
+ end
+
+ # A module with no singleton methods.
+ module K
+ end
+
+ # A simple module.
+ module M
+ class << self
+ def ms_pub; :ms_pub; end
+
+ def ms_pro; :ms_pro; end
+ protected :ms_pro
+
+ def ms_pri; :ms_pri; end
+ private :ms_pri
+ end
+
+ def m_pub; :m_pub; end
+
+ def m_pro; :m_pro; end
+ protected :m_pro
+
+ def m_pri; :m_pri; end
+ private :m_pri
+
+ def pub; :pub; end
+
+ def pro; :pro; end
+ protected :pro
+
+ def pri; :pri; end
+ private :pri
+ end
+
+ # A module including a module
+ module N
+ include M
+
+ class << self
+ def ns_pub; :ns_pub; end
+
+ def ns_pro; :ns_pro; end
+ protected :ns_pro
+
+ def ns_pri; :ns_pri; end
+ private :ns_pri
+ end
+
+ def n_pub; :n_pub; end
+
+ def n_pro; :n_pro; end
+ protected :n_pro
+
+ def n_pri; :n_pri; end
+ private :n_pri
+ end
+
+ # A simple class.
+ class A
+ class << self
+ def as_pub; :as_pub; end
+
+ def as_pro; :as_pro; end
+ protected :as_pro
+
+ def as_pri; :as_pri; end
+ private :as_pri
+
+ def pub; :pub; end
+
+ def pro; :pro; end
+ protected :pro
+
+ def pri; :pri; end
+ private :pri
+ end
+
+ def a_pub; :a_pub; end
+
+ def a_pro; :a_pro; end
+ protected :a_pro
+
+ def a_pri; :a_pri; end
+ private :a_pri
+ end
+
+ # A simple subclass.
+ class B < A
+ class << self
+ def bs_pub; :bs_pub; end
+
+ def bs_pro; :bs_pro; end
+ protected :bs_pro
+
+ def bs_pri; :bs_pri; end
+ private :bs_pri
+
+ def pub; :pub; end
+
+ def pro; :pro; end
+ protected :pro
+
+ def pri; :pri; end
+ private :pri
+ end
+
+ def b_pub; :b_pub; end
+
+ def b_pro; :b_pro; end
+ protected :b_pro
+
+ def b_pri; :b_pri; end
+ private :b_pri
+ end
+
+ # A subclass including a module.
+ class C < A
+ include M
+
+ class << self
+ def cs_pub; :cs_pub; end
+
+ def cs_pro; :cs_pro; end
+ protected :cs_pro
+
+ def cs_pri; :cs_pri; end
+ private :cs_pri
+
+ def pub; :pub; end
+
+ def pro; :pro; end
+ protected :pro
+
+ def pri; :pri; end
+ private :pri
+ end
+
+ def c_pub; :c_pub; end
+
+ def c_pro; :c_pro; end
+ protected :c_pro
+
+ def c_pri; :c_pri; end
+ private :c_pri
+ end
+
+ # A simple class including a module
+ class D
+ include M
+
+ class << self
+ def ds_pub; :ds_pub; end
+
+ def ds_pro; :ds_pro; end
+ protected :ds_pro
+
+ def ds_pri; :ds_pri; end
+ private :ds_pri
+ end
+
+ def d_pub; :d_pub; end
+
+ def d_pro; :d_pro; end
+ protected :d_pro
+
+ def d_pri; :d_pri; end
+ private :d_pri
+
+ def pub; :pub; end
+
+ def pro; :pro; end
+ protected :pro
+
+ def pri; :pri; end
+ private :pri
+ end
+
+ # A subclass of a class including a module.
+ class E < D
+ class << self
+ def es_pub; :es_pub; end
+
+ def es_pro; :es_pro; end
+ protected :es_pro
+
+ def es_pri; :es_pri; end
+ private :es_pri
+ end
+
+ def e_pub; :e_pub; end
+
+ def e_pro; :e_pro; end
+ protected :e_pro
+
+ def e_pri; :e_pri; end
+ private :e_pri
+
+ def pub; :pub; end
+
+ def pro; :pro; end
+ protected :pro
+
+ def pri; :pri; end
+ private :pri
+ end
+
+ # A subclass that includes a module of a class including a module.
+ class F < D
+ include L
+
+ class << self
+ def fs_pub; :fs_pub; end
+
+ def fs_pro; :fs_pro; end
+ protected :fs_pro
+
+ def fs_pri; :fs_pri; end
+ private :fs_pri
+ end
+
+ def f_pub; :f_pub; end
+
+ def f_pro; :f_pro; end
+ protected :f_pro
+
+ def f_pri; :f_pri; end
+ private :f_pri
+ end
+
+ # Class with no singleton methods.
+ class O
+ end
+
+ # Class extended with a module.
+ class P
+ end
+ P.extend M
+end
diff --git a/spec/ruby/language/BEGIN_spec.rb b/spec/ruby/language/BEGIN_spec.rb
new file mode 100644
index 0000000000..bd7112a0f0
--- /dev/null
+++ b/spec/ruby/language/BEGIN_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../spec_helper'
+
+describe "The BEGIN keyword" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "runs in a shared scope" do
+ eval("BEGIN { var_in_begin = 'foo' }; var_in_begin").should == "foo"
+ end
+
+ it "accesses variables outside the eval scope" do
+ outside_var = 'foo'
+ eval("BEGIN { var_in_begin = outside_var }; var_in_begin").should == "foo"
+ end
+
+ it "must appear in a top-level context" do
+ lambda { eval "1.times { BEGIN { 1 } }" }.should raise_error(SyntaxError)
+ end
+
+ it "runs first in a given code unit" do
+ eval "ScratchPad << 'foo'; BEGIN { ScratchPad << 'bar' }"
+
+ ScratchPad.recorded.should == ['bar', 'foo']
+ end
+
+ it "runs multiple begins in FIFO order" do
+ eval "BEGIN { ScratchPad << 'foo' }; BEGIN { ScratchPad << 'bar' }"
+
+ ScratchPad.recorded.should == ['foo', 'bar']
+ end
+
+ it "returns the top-level script's filename for __FILE__" do
+ ruby_exe(fixture(__FILE__, "begin_file.rb")).chomp.should =~ /begin_file\.rb$/
+ end
+end
diff --git a/spec/ruby/language/README b/spec/ruby/language/README
new file mode 100644
index 0000000000..74eaf58709
--- /dev/null
+++ b/spec/ruby/language/README
@@ -0,0 +1,30 @@
+There are numerous possible way of categorizing the entities and concepts that
+make up a programming language. Ruby has a fairly large number of reserved
+words. These words significantly describe major elements of the language,
+including flow control constructs like 'for' and 'while', conditional
+execution like 'if' and 'unless', exceptional execution control like 'rescue',
+etc. There are also literals for the basic "types" like String, Regexp, Array
+and Fixnum.
+
+Behavioral specifications describe the behavior of concrete entities. Rather
+than using concepts of computation to organize these spec files, we use
+entities of the Ruby language. Consider looking at any syntactic element of a
+Ruby program. With (almost) no ambiguity, one can identify it as a literal,
+reserved word, variable, etc. There is a spec file that corresponds to each
+literal construct and most reserved words, with the exceptions noted below.
+There are also several files that are more difficult to classify: all
+predefined variables, constants, and objects (predefined_spec.rb), the
+precedence of all operators (precedence_spec.rb), the behavior of assignment
+to variables (variables_spec.rb), the behavior of subprocess execution
+(execution_spec.rb), the behavior of the raise method as it impacts the
+execution of a Ruby program (raise_spec.rb), and the block entities like
+'begin', 'do', ' { ... }' (block_spec.rb).
+
+Several reserved words and other entities are combined with the primary
+reserved word or entity to which they are related:
+
+false, true, nil, self predefined_spec.rb
+in for_spec.rb
+then, elsif if_spec.rb
+when case_spec.rb
+catch throw_spec.rb
diff --git a/spec/ruby/language/alias_spec.rb b/spec/ruby/language/alias_spec.rb
new file mode 100644
index 0000000000..04ee09f61c
--- /dev/null
+++ b/spec/ruby/language/alias_spec.rb
@@ -0,0 +1,246 @@
+require_relative '../spec_helper'
+
+class AliasObject
+ attr :foo
+ attr_reader :bar
+ attr_accessor :baz
+
+ def prep; @foo = 3; @bar = 4; end
+ def value; 5; end
+ def false_value; 6; end
+ def self.klass_method; 7; end
+end
+
+describe "The alias keyword" do
+ before :each do
+ @obj = AliasObject.new
+ @meta = class << @obj;self;end
+ end
+
+ it "creates a new name for an existing method" do
+ @meta.class_eval do
+ alias __value value
+ end
+ @obj.__value.should == 5
+ end
+
+ it "works with a simple symbol on the left-hand side" do
+ @meta.class_eval do
+ alias :a value
+ end
+ @obj.a.should == 5
+ end
+
+ it "works with a single quoted symbol on the left-hand side" do
+ @meta.class_eval do
+ alias :'a' value
+ end
+ @obj.a.should == 5
+ end
+
+ it "works with a doubule quoted symbol on the left-hand side" do
+ @meta.class_eval do
+ alias :"a" value
+ end
+ @obj.a.should == 5
+ end
+
+ it "works with an interoplated symbol on the left-hand side" do
+ @meta.class_eval do
+ alias :"#{'a'}" value
+ end
+ @obj.a.should == 5
+ end
+
+ it "works with a simple symbol on the right-hand side" do
+ @meta.class_eval do
+ alias a :value
+ end
+ @obj.a.should == 5
+ end
+
+ it "works with a single quoted symbol on the right-hand side" do
+ @meta.class_eval do
+ alias a :'value'
+ end
+ @obj.a.should == 5
+ end
+
+ it "works with a doubule quoted symbol on the right-hand side" do
+ @meta.class_eval do
+ alias a :"value"
+ end
+ @obj.a.should == 5
+ end
+
+ it "works with an interoplated symbol on the right-hand side" do
+ @meta.class_eval do
+ alias a :"#{'value'}"
+ end
+ @obj.a.should == 5
+ end
+
+ it "adds the new method to the list of methods" do
+ original_methods = @obj.methods
+ @meta.class_eval do
+ alias __value value
+ end
+ (@obj.methods - original_methods).map {|m| m.to_s }.should == ["__value"]
+ end
+
+ it "adds the new method to the list of public methods" do
+ original_methods = @obj.public_methods
+ @meta.class_eval do
+ alias __value value
+ end
+ (@obj.public_methods - original_methods).map {|m| m.to_s }.should == ["__value"]
+ end
+
+ it "overwrites an existing method with the target name" do
+ @meta.class_eval do
+ alias false_value value
+ end
+ @obj.false_value.should == 5
+ end
+
+ it "is reversible" do
+ @meta.class_eval do
+ alias __value value
+ alias value false_value
+ end
+ @obj.value.should == 6
+
+ @meta.class_eval do
+ alias value __value
+ end
+ @obj.value.should == 5
+ end
+
+ it "operates on the object's metaclass when used in instance_eval" do
+ @obj.instance_eval do
+ alias __value value
+ end
+
+ @obj.__value.should == 5
+ lambda { AliasObject.new.__value }.should raise_error(NoMethodError)
+ end
+
+ it "operates on the class/module metaclass when used in instance_eval" do
+ AliasObject.instance_eval do
+ alias __klass_method klass_method
+ end
+
+ AliasObject.__klass_method.should == 7
+ lambda { Object.__klass_method }.should raise_error(NoMethodError)
+ end
+
+ it "operates on the class/module metaclass when used in instance_exec" do
+ AliasObject.instance_exec do
+ alias __klass_method2 klass_method
+ end
+
+ AliasObject.__klass_method2.should == 7
+ lambda { Object.__klass_method2 }.should raise_error(NoMethodError)
+ end
+
+ it "operates on methods defined via attr, attr_reader, and attr_accessor" do
+ @obj.prep
+ @obj.instance_eval do
+ alias afoo foo
+ alias abar bar
+ alias abaz baz
+ end
+
+ @obj.afoo.should == 3
+ @obj.abar.should == 4
+ @obj.baz = 5
+ @obj.abaz.should == 5
+ end
+
+ it "operates on methods with splat arguments" do
+ class AliasObject2;end
+ AliasObject2.class_eval do
+ def test(*args)
+ 4
+ end
+ def test_with_check(*args)
+ test_without_check(*args)
+ end
+ alias test_without_check test
+ alias test test_with_check
+ end
+ AliasObject2.new.test(1,2,3,4,5).should == 4
+ end
+
+ it "operates on methods with splat arguments on eigenclasses" do
+ @meta.class_eval do
+ def test(*args)
+ 4
+ end
+ def test_with_check(*args)
+ test_without_check(*args)
+ end
+ alias test_without_check test
+ alias test test_with_check
+ end
+ @obj.test(1,2,3,4,5).should == 4
+ end
+
+ it "operates on methods with splat arguments defined in a superclass" do
+ alias_class = Class.new
+ alias_class.class_eval do
+ def test(*args)
+ 4
+ end
+ def test_with_check(*args)
+ test_without_check(*args)
+ end
+ end
+ sub = Class.new(alias_class) do
+ alias test_without_check test
+ alias test test_with_check
+ end
+ sub.new.test(1,2,3,4,5).should == 4
+ end
+
+ it "operates on methods with splat arguments defined in a superclass using text block for class eval" do
+ class Sub < AliasObject;end
+ AliasObject.class_eval <<-code
+ def test(*args)
+ 4
+ end
+ def test_with_check(*args)
+ test_without_check(*args)
+ end
+ alias test_without_check test
+ alias test test_with_check
+ code
+ Sub.new.test("testing").should == 4
+ end
+
+ it "is not allowed against Fixnum or String instances" do
+ lambda do
+ 1.instance_eval do
+ alias :foo :to_s
+ end
+ end.should raise_error(TypeError)
+
+ lambda do
+ :blah.instance_eval do
+ alias :foo :to_s
+ end
+ end.should raise_error(TypeError)
+ end
+
+ it "on top level defines the alias on Object" do
+ # because it defines on the default definee / current module
+ ruby_exe("def foo; end; alias bla foo; print method(:bla).owner", escape: true).should == "Object"
+ end
+
+ it "raises a NameError when passed a missing name" do
+ lambda { @meta.class_eval { alias undef_method not_exist } }.should raise_error(NameError) { |e|
+ # a NameError and not a NoMethodError
+ e.class.should == NameError
+ }
+ end
+end
diff --git a/spec/ruby/language/and_spec.rb b/spec/ruby/language/and_spec.rb
new file mode 100644
index 0000000000..55a2a3103a
--- /dev/null
+++ b/spec/ruby/language/and_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../spec_helper'
+
+describe "The '&&' statement" do
+
+ it "short-circuits evaluation at the first condition to be false" do
+ x = nil
+ true && false && x = 1
+ x.should be_nil
+ end
+
+ it "evaluates to the first condition not to be true" do
+ value = nil
+ (value && nil).should == nil
+ (value && false).should == nil
+ value = false
+ (value && nil).should == false
+ (value && false).should == false
+
+ ("yes" && 1 && nil && true).should == nil
+ ("yes" && 1 && false && true).should == false
+ end
+
+ it "evaluates to the last condition if all are true" do
+ ("yes" && 1).should == 1
+ (1 && "yes").should == "yes"
+ end
+
+ it "evaluates the full set of chained conditions during assignment" do
+ x, y = nil
+ x = 1 && y = 2
+ # "1 && y = 2" is evaluated and then assigned to x
+ x.should == 2
+ end
+
+ it "treats empty expressions as nil" do
+ (() && true).should be_nil
+ (true && ()).should be_nil
+ (() && ()).should be_nil
+ end
+
+end
+
+describe "The 'and' statement" do
+ it "short-circuits evaluation at the first condition to be false" do
+ x = nil
+ true and false and x = 1
+ x.should be_nil
+ end
+
+ it "evaluates to the first condition not to be true" do
+ value = nil
+ (value and nil).should == nil
+ (value and false).should == nil
+ value = false
+ (value and nil).should == false
+ (value and false).should == false
+
+ ("yes" and 1 and nil and true).should == nil
+ ("yes" and 1 and false and true).should == false
+ end
+
+ it "evaluates to the last condition if all are true" do
+ ("yes" and 1).should == 1
+ (1 and "yes").should == "yes"
+ end
+
+ it "when used in assignment, evaluates and assigns expressions individually" do
+ x, y = nil
+ x = 1 and y = 2
+ # evaluates (x=1) and (y=2)
+ x.should == 1
+ end
+
+ it "treats empty expressions as nil" do
+ (() and true).should be_nil
+ (true and ()).should be_nil
+ (() and ()).should be_nil
+ end
+
+end
diff --git a/spec/ruby/language/array_spec.rb b/spec/ruby/language/array_spec.rb
new file mode 100644
index 0000000000..2583cffbf7
--- /dev/null
+++ b/spec/ruby/language/array_spec.rb
@@ -0,0 +1,162 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/array'
+
+describe "Array literals" do
+ it "[] should return a new array populated with the given elements" do
+ array = [1, 'a', nil]
+ array.should be_kind_of(Array)
+ array[0].should == 1
+ array[1].should == 'a'
+ array[2].should == nil
+ end
+
+ it "[] treats empty expressions as nil elements" do
+ array = [0, (), 2, (), 4]
+ array.should be_kind_of(Array)
+ array[0].should == 0
+ array[1].should == nil
+ array[2].should == 2
+ array[3].should == nil
+ array[4].should == 4
+ end
+
+ it "[] accepts a literal hash without curly braces as its only parameter" do
+ ["foo" => :bar, baz: 42].should == [{"foo" => :bar, baz: 42}]
+ end
+
+ it "[] accepts a literal hash without curly braces as its last parameter" do
+ ["foo", "bar" => :baz].should == ["foo", {"bar" => :baz}]
+ [1, 2, 3 => 6, 4 => 24].should == [1, 2, {3 => 6, 4 => 24}]
+ end
+
+ it "[] treats splatted nil as no element" do
+ [*nil].should == []
+ [1, *nil].should == [1]
+ [1, 2, *nil].should == [1, 2]
+ [1, *nil, 3].should == [1, 3]
+ [*nil, *nil, *nil].should == []
+ end
+
+ it "evaluates each argument exactly once" do
+ se = ArraySpec::SideEffect.new
+ se.array_result(true)
+ se.array_result(false)
+ se.call_count.should == 4
+ end
+end
+
+describe "Bareword array literal" do
+ it "%w() transforms unquoted barewords into an array" do
+ a = 3
+ %w(a #{3+a} 3).should == ["a", '#{3+a}', "3"]
+ end
+
+ it "%W() transforms unquoted barewords into an array, supporting interpolation" do
+ a = 3
+ %W(a #{3+a} 3).should == ["a", '6', "3"]
+ end
+
+ it "%W() always treats interpolated expressions as a single word" do
+ a = "hello world"
+ %W(a b c #{a} d e).should == ["a", "b", "c", "hello world", "d", "e"]
+ end
+
+ it "treats consecutive whitespace characters the same as one" do
+ %w(a b c d).should == ["a", "b", "c", "d"]
+ %W(hello
+ world).should == ["hello", "world"]
+ end
+
+ it "treats whitespace as literals characters when escaped by a backslash" do
+ %w(a b\ c d e).should == ["a", "b c", "d", "e"]
+ %w(a b\
+c d).should == ["a", "b\nc", "d"]
+ %W(a\ b\tc).should == ["a ", "b\tc"]
+ %W(white\ \ \ \ \ space).should == ["white ", " ", " ", " space"]
+ end
+end
+
+describe "The unpacking splat operator (*)" do
+ it "when applied to a literal nested array, unpacks its elements into the containing array" do
+ [1, 2, *[3, 4, 5]].should == [1, 2, 3, 4, 5]
+ end
+
+ it "when applied to a nested referenced array, unpacks its elements into the containing array" do
+ splatted_array = [3, 4, 5]
+ [1, 2, *splatted_array].should == [1, 2, 3, 4, 5]
+ end
+
+ it "returns a new array containing the same values when applied to an array inside an empty array" do
+ splatted_array = [3, 4, 5]
+ [*splatted_array].should == splatted_array
+ [*splatted_array].should_not equal(splatted_array)
+ end
+
+ it "unpacks the start and count arguments in an array slice assignment" do
+ alphabet_1 = ['a'..'z'].to_a
+ alphabet_2 = alphabet_1.dup
+ start_and_count_args = [1, 10]
+
+ alphabet_1[1, 10] = 'a'
+ alphabet_2[*start_and_count_args] = 'a'
+
+ alphabet_1.should == alphabet_2
+ end
+
+ it "unpacks arguments as if they were listed statically" do
+ static = [1,2,3,4]
+ receiver = static.dup
+ args = [0,1]
+ static[0,1] = []
+ static.should == [2,3,4]
+ receiver[*args] = []
+ receiver.should == static
+ end
+
+ it "unpacks a literal array into arguments in a method call" do
+ tester = ArraySpec::Splat.new
+ tester.unpack_3args(*[1, 2, 3]).should == [1, 2, 3]
+ tester.unpack_4args(1, 2, *[3, 4]).should == [1, 2, 3, 4]
+ tester.unpack_4args("a", %w(b c), *%w(d e)).should == ["a", ["b", "c"], "d", "e"]
+ end
+
+ it "unpacks a referenced array into arguments in a method call" do
+ args = [1, 2, 3]
+ tester = ArraySpec::Splat.new
+ tester.unpack_3args(*args).should == [1, 2, 3]
+ tester.unpack_4args(0, *args).should == [0, 1, 2, 3]
+ end
+
+ it "when applied to a non-Array value attempts to coerce it to Array if the object respond_to?(:to_a)" do
+ obj = mock("pseudo-array")
+ obj.should_receive(:to_a).and_return([2, 3, 4])
+ [1, *obj].should == [1, 2, 3, 4]
+ end
+
+ it "when applied to a non-Array value uses it unchanged if it does not respond_to?(:to_a)" do
+ obj = Object.new
+ obj.should_not respond_to(:to_a)
+ [1, *obj].should == [1, obj]
+ end
+
+ it "when applied to a BasicObject coerces it to Array if it respond_to?(:to_a)" do
+ obj = BasicObject.new
+ def obj.to_a; [2, 3, 4]; end
+ [1, *obj].should == [1, 2, 3, 4]
+ end
+
+ it "can be used before other non-splat elements" do
+ a = [1, 2]
+ [0, *a, 3].should == [0, 1, 2, 3]
+ end
+
+ it "can be used multiple times in the same containing array" do
+ a = [1, 2]
+ b = [1, 0]
+ [*a, 3, *a, *b].should == [1, 2, 3, 1, 2, 1, 0]
+ end
+end
+
+describe "The packing splat operator (*)" do
+
+end
diff --git a/spec/ruby/language/block_spec.rb b/spec/ruby/language/block_spec.rb
new file mode 100644
index 0000000000..5945309cee
--- /dev/null
+++ b/spec/ruby/language/block_spec.rb
@@ -0,0 +1,865 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/block'
+
+describe "A block yielded a single" do
+ before :all do
+ def m(a) yield a end
+ end
+
+ context "Array" do
+ it "assigns the Array to a single argument" do
+ m([1, 2]) { |a| a }.should == [1, 2]
+ end
+
+ it "receives the identical Array object" do
+ ary = [1, 2]
+ m(ary) { |a| a }.should equal(ary)
+ end
+
+ it "assigns the Array to a single rest argument" do
+ m([1, 2, 3]) { |*a| a }.should == [[1, 2, 3]]
+ end
+
+ it "assigns the first element to a single argument with trailing comma" do
+ m([1, 2]) { |a, | a }.should == 1
+ end
+
+ it "assigns elements to required arguments" do
+ m([1, 2, 3]) { |a, b| [a, b] }.should == [1, 2]
+ end
+
+ it "assigns nil to unassigned required arguments" do
+ m([1, 2]) { |a, *b, c, d| [a, b, c, d] }.should == [1, [], 2, nil]
+ end
+
+ it "assigns elements to optional arguments" do
+ m([1, 2]) { |a=5, b=4, c=3| [a, b, c] }.should == [1, 2, 3]
+ end
+
+ it "assgins elements to post arguments" do
+ m([1, 2]) { |a=5, b, c, d| [a, b, c, d] }.should == [5, 1, 2, nil]
+ end
+
+ it "assigns elements to required arguments when a keyword rest argument is present" do
+ m([1, 2]) { |a, **k| [a, k] }.should == [1, {}]
+ end
+
+ it "assigns elements to mixed argument types" do
+ result = m([1, 2, 3, {x: 9}]) { |a, b=5, *c, d, e: 2, **k| [a, b, c, d, e, k] }
+ result.should == [1, 2, [], 3, 2, {x: 9}]
+ end
+
+ it "assigns symbol keys from a Hash to keyword arguments" do
+ result = m(["a" => 1, a: 10]) { |a=nil, **b| [a, b] }
+ result.should == [{"a" => 1}, a: 10]
+ end
+
+ it "assigns symbol keys from a Hash returned by #to_hash to keyword arguments" do
+ obj = mock("coerce block keyword arguments")
+ obj.should_receive(:to_hash).and_return({"a" => 1, b: 2})
+
+ result = m([obj]) { |a=nil, **b| [a, b] }
+ result.should == [{"a" => 1}, b: 2]
+ end
+
+ it "calls #to_hash on the argument but does not use the result when no keywords are present" do
+ obj = mock("coerce block keyword arguments")
+ obj.should_receive(:to_hash).and_return({"a" => 1, "b" => 2})
+
+ result = m([obj]) { |a=nil, **b| [a, b] }
+ result.should == [{"a" => 1, "b" => 2}, {}]
+ end
+
+ describe "when non-symbol keys are in a keyword arguments Hash" do
+ it "separates non-symbol keys and symbol keys" do
+ result = m(["a" => 10, b: 2]) { |a=nil, **b| [a, b] }
+ result.should == [{"a" => 10}, {b: 2}]
+ end
+ end
+
+ it "does not treat hashes with string keys as keyword arguments" do
+ result = m(["a" => 10]) { |a = nil, **b| [a, b] }
+ result.should == [{"a" => 10}, {}]
+ end
+
+ it "calls #to_hash on the last element if keyword arguments are present" do
+ obj = mock("destructure block keyword arguments")
+ obj.should_receive(:to_hash).and_return({x: 9})
+
+ result = m([1, 2, 3, obj]) { |a, *b, c, **k| [a, b, c, k] }
+ result.should == [1, [2], 3, {x: 9}]
+ end
+
+ it "assigns the last element to a non-keyword argument if #to_hash returns nil" do
+ obj = mock("destructure block keyword arguments")
+ obj.should_receive(:to_hash).and_return(nil)
+
+ result = m([1, 2, 3, obj]) { |a, *b, c, **k| [a, b, c, k] }
+ result.should == [1, [2, 3], obj, {}]
+ end
+
+ it "calls #to_hash on the last element when there are more arguments than parameters" do
+ x = mock("destructure matching block keyword argument")
+ x.should_receive(:to_hash).and_return({x: 9})
+
+ result = m([1, 2, 3, {y: 9}, 4, 5, x]) { |a, b=5, c, **k| [a, b, c, k] }
+ result.should == [1, 2, 3, {x: 9}]
+ end
+
+ it "raises a TypeError if #to_hash does not return a Hash" do
+ obj = mock("destructure block keyword arguments")
+ obj.should_receive(:to_hash).and_return(1)
+
+ lambda { m([1, 2, 3, obj]) { |a, *b, c, **k| } }.should raise_error(TypeError)
+ end
+
+ it "raises the error raised inside #to_hash" do
+ obj = mock("destructure block keyword arguments")
+ error = RuntimeError.new("error while converting to a hash")
+ obj.should_receive(:to_hash).and_raise(error)
+
+ lambda { m([1, 2, 3, obj]) { |a, *b, c, **k| } }.should raise_error(error)
+ end
+
+ it "does not call #to_ary on the Array" do
+ ary = [1, 2]
+ ary.should_not_receive(:to_ary)
+
+ m(ary) { |a, b, c| [a, b, c] }.should == [1, 2, nil]
+ end
+ end
+
+ context "Object" do
+ it "calls #to_ary on the object when taking multiple arguments" do
+ obj = mock("destructure block arguments")
+ obj.should_receive(:to_ary).and_return([1, 2])
+
+ m(obj) { |a, b, c| [a, b, c] }.should == [1, 2, nil]
+ end
+
+ it "does not call #to_ary when not taking any arguments" do
+ obj = mock("destructure block arguments")
+ obj.should_not_receive(:to_ary)
+
+ m(obj) { 1 }.should == 1
+ end
+
+ it "does not call #to_ary on the object when taking a single argument" do
+ obj = mock("destructure block arguments")
+ obj.should_not_receive(:to_ary)
+
+ m(obj) { |a| a }.should == obj
+ end
+
+ it "does not call #to_ary on the object when taking a single rest argument" do
+ obj = mock("destructure block arguments")
+ obj.should_not_receive(:to_ary)
+
+ m(obj) { |*a| a }.should == [obj]
+ end
+
+ it "receives the object if #to_ary returns nil" do
+ obj = mock("destructure block arguments")
+ obj.should_receive(:to_ary).and_return(nil)
+
+ m(obj) { |a, b, c| [a, b, c] }.should == [obj, nil, 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)
+
+ lambda { m(obj) { |a, b| } }.should raise_error(TypeError)
+ end
+ end
+end
+
+# TODO: rewrite
+describe "A block" do
+ before :each do
+ @y = BlockSpecs::Yielder.new
+ end
+
+ it "captures locals from the surrounding scope" do
+ var = 1
+ @y.z { var }.should == 1
+ end
+
+ it "allows for a leading space before the arguments" do
+ res = @y.s (:a){ 1 }
+ res.should == 1
+ end
+
+ it "allows to define a block variable with the same name as the enclosing block" do
+ o = BlockSpecs::OverwriteBlockVariable.new
+ o.z { 1 }.should == 1
+ end
+
+ it "does not capture a local when an argument has the same name" do
+ var = 1
+ @y.s(2) { |var| var }.should == 2
+ var.should == 1
+ end
+
+ it "does not capture a local when the block argument has the same name" do
+ var = 1
+ proc { |&var|
+ var.call(2)
+ }.call { |x| x }.should == 2
+ var.should == 1
+ end
+
+ describe "taking zero arguments" do
+ it "does not raise an exception when no values are yielded" do
+ @y.z { 1 }.should == 1
+ end
+
+ it "does not raise an exception when values are yielded" do
+ @y.s(0) { 1 }.should == 1
+ end
+ end
+
+ describe "taking || arguments" do
+ it "does not raise an exception when no values are yielded" do
+ @y.z { || 1 }.should == 1
+ end
+
+ it "does not raise an exception when values are yielded" do
+ @y.s(0) { || 1 }.should == 1
+ end
+ end
+
+ describe "taking |a| arguments" do
+ it "assigns nil to the argument when no values are yielded" do
+ @y.z { |a| a }.should be_nil
+ end
+
+ it "assigns the value yielded to the argument" do
+ @y.s(1) { |a| a }.should == 1
+ end
+
+ it "does not call #to_ary to convert a single yielded object to an Array" do
+ obj = mock("block yield to_ary")
+ obj.should_not_receive(:to_ary)
+
+ @y.s(obj) { |a| a }.should equal(obj)
+ end
+
+ it "assigns the first value yielded to the argument" do
+ @y.m(1, 2) { |a| a }.should == 1
+ end
+
+ it "does not destructure a single Array value" do
+ @y.s([1, 2]) { |a| a }.should == [1, 2]
+ end
+ end
+
+ describe "taking |a, b| arguments" do
+ it "assgins nil to the arguments when no values are yielded" do
+ @y.z { |a, b| [a, b] }.should == [nil, nil]
+ end
+
+ it "assigns one value yielded to the first argument" do
+ @y.s(1) { |a, b| [a, b] }.should == [1, nil]
+ end
+
+ it "assigns the first two values yielded to the arguments" do
+ @y.m(1, 2, 3) { |a, b| [a, b] }.should == [1, 2]
+ end
+
+ it "does not destructure an Array value as one of several values yielded" do
+ @y.m([1, 2], 3, 4) { |a, b| [a, b] }.should == [[1, 2], 3]
+ end
+
+ it "assigns 'nil' and 'nil' to the arguments when a single, empty Array is yielded" do
+ @y.s([]) { |a, b| [a, b] }.should == [nil, nil]
+ end
+
+ it "assigns the element of a single element Array to the first argument" do
+ @y.s([1]) { |a, b| [a, b] }.should == [1, nil]
+ @y.s([nil]) { |a, b| [a, b] }.should == [nil, nil]
+ @y.s([[]]) { |a, b| [a, b] }.should == [[], nil]
+ end
+
+ it "destructures a single Array value yielded" do
+ @y.s([1, 2, 3]) { |a, b| [a, b] }.should == [1, 2]
+ end
+
+ it "destructures a splatted Array" do
+ @y.r([[]]) { |a, b| [a, b] }.should == [nil, nil]
+ @y.r([[1]]) { |a, b| [a, b] }.should == [1, nil]
+ end
+
+ it "calls #to_ary to convert a single yielded object to an Array" do
+ obj = mock("block yield to_ary")
+ obj.should_receive(:to_ary).and_return([1, 2])
+
+ @y.s(obj) { |a, b| [a, b] }.should == [1, 2]
+ end
+
+ it "does not call #to_ary if the single yielded object is an Array" do
+ obj = [1, 2]
+ obj.should_not_receive(:to_ary)
+
+ @y.s(obj) { |a, b| [a, b] }.should == [1, 2]
+ end
+
+ it "does not call #to_ary if the object does not respond to #to_ary" do
+ obj = mock("block yield no to_ary")
+
+ @y.s(obj) { |a, b| [a, b] }.should == [obj, nil]
+ end
+
+ it "raises a TypeError if #to_ary does not return an Array" do
+ obj = mock("block yield to_ary invalid")
+ obj.should_receive(:to_ary).and_return(1)
+
+ lambda { @y.s(obj) { |a, b| } }.should raise_error(TypeError)
+ end
+
+ it "raises the original exception if #to_ary raises an exception" do
+ obj = mock("block yield to_ary raising an exception")
+ obj.should_receive(:to_ary).and_raise(ZeroDivisionError)
+
+ lambda { @y.s(obj) { |a, b| } }.should raise_error(ZeroDivisionError)
+ end
+
+ end
+
+ describe "taking |a, *b| arguments" do
+ it "assigns 'nil' and '[]' to the arguments when no values are yielded" do
+ @y.z { |a, *b| [a, b] }.should == [nil, []]
+ end
+
+ it "assigns all yielded values after the first to the rest argument" do
+ @y.m(1, 2, 3) { |a, *b| [a, b] }.should == [1, [2, 3]]
+ end
+
+ it "assigns 'nil' and '[]' to the arguments when a single, empty Array is yielded" do
+ @y.s([]) { |a, *b| [a, b] }.should == [nil, []]
+ end
+
+ it "assigns the element of a single element Array to the first argument" do
+ @y.s([1]) { |a, *b| [a, b] }.should == [1, []]
+ @y.s([nil]) { |a, *b| [a, b] }.should == [nil, []]
+ @y.s([[]]) { |a, *b| [a, b] }.should == [[], []]
+ end
+
+ it "destructures a splatted Array" do
+ @y.r([[]]) { |a, *b| [a, b] }.should == [nil, []]
+ @y.r([[1]]) { |a, *b| [a, b] }.should == [1, []]
+ end
+
+ it "destructures a single Array value assigning the remaining values to the rest argument" do
+ @y.s([1, 2, 3]) { |a, *b| [a, b] }.should == [1, [2, 3]]
+ end
+
+ it "calls #to_ary to convert a single yielded object to an Array" do
+ obj = mock("block yield to_ary")
+ obj.should_receive(:to_ary).and_return([1, 2])
+
+ @y.s(obj) { |a, *b| [a, b] }.should == [1, [2]]
+ end
+
+ it "does not call #to_ary if the single yielded object is an Array" do
+ obj = [1, 2]
+ obj.should_not_receive(:to_ary)
+
+ @y.s(obj) { |a, *b| [a, b] }.should == [1, [2]]
+ end
+
+ it "does not call #to_ary if the object does not respond to #to_ary" do
+ obj = mock("block yield no to_ary")
+
+ @y.s(obj) { |a, *b| [a, b] }.should == [obj, []]
+ end
+
+ it "raises a TypeError if #to_ary does not return an Array" do
+ obj = mock("block yield to_ary invalid")
+ obj.should_receive(:to_ary).and_return(1)
+
+ lambda { @y.s(obj) { |a, *b| } }.should raise_error(TypeError)
+ end
+ end
+
+ describe "taking |*| arguments" do
+ it "does not raise an exception when no values are yielded" do
+ @y.z { |*| 1 }.should == 1
+ end
+
+ it "does not raise an exception when values are yielded" do
+ @y.s(0) { |*| 1 }.should == 1
+ end
+
+ it "does not call #to_ary if the single yielded object is an Array" do
+ obj = [1, 2]
+ obj.should_not_receive(:to_ary)
+
+ @y.s(obj) { |*| 1 }.should == 1
+ end
+
+ it "does not call #to_ary if the object does not respond to #to_ary" do
+ obj = mock("block yield no to_ary")
+
+ @y.s(obj) { |*| 1 }.should == 1
+ end
+
+ it "does not call #to_ary to convert a single yielded object to an Array" do
+ obj = mock("block yield to_ary")
+ obj.should_not_receive(:to_ary)
+
+ @y.s(obj) { |*| 1 }.should == 1
+ end
+ end
+
+ describe "taking |*a| arguments" do
+ it "assigns '[]' to the argument when no values are yielded" do
+ @y.z { |*a| a }.should == []
+ end
+
+ it "assigns a single value yielded to the argument as an Array" do
+ @y.s(1) { |*a| a }.should == [1]
+ end
+
+ it "assigns all the values passed to the argument as an Array" do
+ @y.m(1, 2, 3) { |*a| a }.should == [1, 2, 3]
+ end
+
+ it "assigns '[[]]' to the argument when passed an empty Array" do
+ @y.s([]) { |*a| a }.should == [[]]
+ end
+
+ it "assigns a single Array value passed to the argument by wrapping it in an Array" do
+ @y.s([1, 2, 3]) { |*a| a }.should == [[1, 2, 3]]
+ end
+
+ it "does not call #to_ary if the single yielded object is an Array" do
+ obj = [1, 2]
+ obj.should_not_receive(:to_ary)
+
+ @y.s(obj) { |*a| a }.should == [[1, 2]]
+ end
+
+ it "does not call #to_ary if the object does not respond to #to_ary" do
+ obj = mock("block yield no to_ary")
+
+ @y.s(obj) { |*a| a }.should == [obj]
+ end
+
+ it "does not call #to_ary to convert a single yielded object to an Array" do
+ obj = mock("block yield to_ary")
+ obj.should_not_receive(:to_ary)
+
+ @y.s(obj) { |*a| a }.should == [obj]
+ end
+ end
+
+ describe "taking |a, | arguments" do
+ it "assigns nil to the argument when no values are yielded" do
+ @y.z { |a, | a }.should be_nil
+ end
+
+ it "assgins the argument a single value yielded" do
+ @y.s(1) { |a, | a }.should == 1
+ end
+
+ it "assigns the argument the first value yielded" do
+ @y.m(1, 2) { |a, | a }.should == 1
+ end
+
+ it "assigns the argument the first of several values yielded when it is an Array" do
+ @y.m([1, 2], 3) { |a, | a }.should == [1, 2]
+ end
+
+ it "assigns nil to the argument when passed an empty Array" do
+ @y.s([]) { |a, | a }.should be_nil
+ end
+
+ it "assigns the argument the first element of the Array when passed a single Array" do
+ @y.s([1, 2]) { |a, | a }.should == 1
+ end
+
+ it "calls #to_ary to convert a single yielded object to an Array" do
+ obj = mock("block yield to_ary")
+ obj.should_receive(:to_ary).and_return([1, 2])
+
+ @y.s(obj) { |a, | a }.should == 1
+ end
+
+ it "does not call #to_ary if the single yielded object is an Array" do
+ obj = [1, 2]
+ obj.should_not_receive(:to_ary)
+
+ @y.s(obj) { |a, | a }.should == 1
+ end
+
+ it "does not call #to_ary if the object does not respond to #to_ary" do
+ obj = mock("block yield no to_ary")
+
+ @y.s(obj) { |a, | a }.should == obj
+ end
+
+ it "raises a TypeError if #to_ary does not return an Array" do
+ obj = mock("block yield to_ary invalid")
+ obj.should_receive(:to_ary).and_return(1)
+
+ lambda { @y.s(obj) { |a, | } }.should raise_error(TypeError)
+ end
+ end
+
+ describe "taking |(a, b)| arguments" do
+ it "assigns nil to the arguments when yielded no values" do
+ @y.z { |(a, b)| [a, b] }.should == [nil, nil]
+ end
+
+ it "destructures a single Array value yielded" do
+ @y.s([1, 2]) { |(a, b)| [a, b] }.should == [1, 2]
+ end
+
+ it "destructures a single Array value yielded when shadowing an outer variable" do
+ a = 9
+ @y.s([1, 2]) { |(a, b)| [a, b] }.should == [1, 2]
+ end
+
+ it "calls #to_ary to convert a single yielded object to an Array" do
+ obj = mock("block yield to_ary")
+ obj.should_receive(:to_ary).and_return([1, 2])
+
+ @y.s(obj) { |(a, b)| [a, b] }.should == [1, 2]
+ end
+
+ it "does not call #to_ary if the single yielded object is an Array" do
+ obj = [1, 2]
+ obj.should_not_receive(:to_ary)
+
+ @y.s(obj) { |(a, b)| [a, b] }.should == [1, 2]
+ end
+
+ it "does not call #to_ary if the object does not respond to #to_ary" do
+ obj = mock("block yield no to_ary")
+
+ @y.s(obj) { |(a, b)| [a, b] }.should == [obj, nil]
+ end
+
+ it "raises a TypeError if #to_ary does not return an Array" do
+ obj = mock("block yield to_ary invalid")
+ obj.should_receive(:to_ary).and_return(1)
+
+ lambda { @y.s(obj) { |(a, b)| } }.should raise_error(TypeError)
+ end
+ end
+
+ describe "taking |(a, b), c| arguments" do
+ it "assigns nil to the arguments when yielded no values" do
+ @y.z { |(a, b), c| [a, b, c] }.should == [nil, nil, nil]
+ end
+
+ it "destructures a single one-level Array value yielded" do
+ @y.s([1, 2]) { |(a, b), c| [a, b, c] }.should == [1, nil, 2]
+ end
+
+ it "destructures a single multi-level Array value yielded" do
+ @y.s([[1, 2, 3], 4]) { |(a, b), c| [a, b, c] }.should == [1, 2, 4]
+ end
+
+ it "calls #to_ary to convert a single yielded object to an Array" do
+ obj = mock("block yield to_ary")
+ obj.should_receive(:to_ary).and_return([1, 2])
+
+ @y.s(obj) { |(a, b), c| [a, b, c] }.should == [1, nil, 2]
+ end
+
+ it "does not call #to_ary if the single yielded object is an Array" do
+ obj = [1, 2]
+ obj.should_not_receive(:to_ary)
+
+ @y.s(obj) { |(a, b), c| [a, b, c] }.should == [1, nil, 2]
+ end
+
+ it "does not call #to_ary if the object does not respond to #to_ary" do
+ obj = mock("block yield no to_ary")
+
+ @y.s(obj) { |(a, b), c| [a, b, c] }.should == [obj, nil, nil]
+ end
+
+ it "raises a TypeError if #to_ary does not return an Array" do
+ obj = mock("block yield to_ary invalid")
+ obj.should_receive(:to_ary).and_return(1)
+
+ lambda { @y.s(obj) { |(a, b), c| } }.should raise_error(TypeError)
+ end
+ end
+
+ describe "taking nested |a, (b, (c, d))|" do
+ it "assigns nil to the arguments when yielded no values" do
+ @y.m { |a, (b, (c, d))| [a, b, c, d] }.should == [nil, nil, nil, nil]
+ end
+
+ it "destructures separate yielded values" do
+ @y.m(1, 2) { |a, (b, (c, d))| [a, b, c, d] }.should == [1, 2, nil, nil]
+ end
+
+ it "destructures a nested Array value yielded" do
+ @y.m(1, [2, 3]) { |a, (b, (c, d))| [a, b, c, d] }.should == [1, 2, 3, nil]
+ end
+
+ it "destructures a single multi-level Array value yielded" do
+ @y.m(1, [2, [3, 4]]) { |a, (b, (c, d))| [a, b, c, d] }.should == [1, 2, 3, 4]
+ end
+ end
+
+ describe "taking nested |a, ((b, c), d)|" do
+ it "assigns nil to the arguments when yielded no values" do
+ @y.m { |a, ((b, c), d)| [a, b, c, d] }.should == [nil, nil, nil, nil]
+ end
+
+ it "destructures separate yielded values" do
+ @y.m(1, 2) { |a, ((b, c), d)| [a, b, c, d] }.should == [1, 2, nil, nil]
+ end
+
+ it "destructures a nested value yielded" do
+ @y.m(1, [2, 3]) { |a, ((b, c), d)| [a, b, c, d] }.should == [1, 2, nil, 3]
+ end
+
+ it "destructures a single multi-level Array value yielded" do
+ @y.m(1, [[2, 3], 4]) { |a, ((b, c), d)| [a, b, c, d] }.should == [1, 2, 3, 4]
+ end
+ end
+
+ describe "arguments with _" do
+ it "extracts arguments with _" do
+ @y.m([[1, 2, 3], 4]) { |(_, a, _), _| a }.should == 2
+ @y.m([1, [2, 3, 4]]) { |_, (_, a, _)| a }.should == 3
+ end
+
+ it "assigns the first variable named" do
+ @y.m(1, 2) { |_, _| _ }.should == 1
+ end
+ end
+
+ describe "taking identically-named arguments" do
+ it "raises a SyntaxError for standard arguments" do
+ lambda { eval "lambda { |x,x| }" }.should raise_error(SyntaxError)
+ lambda { eval "->(x,x) {}" }.should raise_error(SyntaxError)
+ lambda { eval "Proc.new { |x,x| }" }.should raise_error(SyntaxError)
+ end
+
+ it "accepts unnamed arguments" do
+ eval("lambda { |_,_| }").should be_an_instance_of(Proc)
+ eval("->(_,_) {}").should be_an_instance_of(Proc)
+ eval("Proc.new { |_,_| }").should be_an_instance_of(Proc)
+ end
+ end
+end
+
+describe "Block-local variables" do
+ it "are introduced with a semi-colon in the parameter list" do
+ [1].map {|one; bl| bl }.should == [nil]
+ end
+
+ it "can be specified in a comma-separated list after the semi-colon" do
+ [1].map {|one; bl, bl2| [bl, bl2] }.should == [[nil, nil]]
+ end
+
+ it "can not have the same name as one of the standard parameters" do
+ lambda { eval "[1].each {|foo; foo| }" }.should raise_error(SyntaxError)
+ lambda { eval "[1].each {|foo, bar; glark, bar| }" }.should raise_error(SyntaxError)
+ end
+
+ it "can not be prefixed with an asterisk" do
+ lambda { eval "[1].each {|foo; *bar| }" }.should raise_error(SyntaxError)
+ lambda do
+ eval "[1].each {|foo, bar; glark, *fnord| }"
+ end.should raise_error(SyntaxError)
+ end
+
+ it "can not be prefixed with an ampersand" do
+ lambda { eval "[1].each {|foo; &bar| }" }.should raise_error(SyntaxError)
+ lambda do
+ eval "[1].each {|foo, bar; glark, &fnord| }"
+ end.should raise_error(SyntaxError)
+ end
+
+ it "can not be assigned default values" do
+ lambda { eval "[1].each {|foo; bar=1| }" }.should raise_error(SyntaxError)
+ lambda do
+ eval "[1].each {|foo, bar; glark, fnord=:fnord| }"
+ end.should raise_error(SyntaxError)
+ end
+
+ it "need not be preceded by standard parameters" do
+ [1].map {|; foo| foo }.should == [nil]
+ [1].map {|; glark, bar| [glark, bar] }.should == [[nil, nil]]
+ end
+
+ it "only allow a single semi-colon in the parameter list" do
+ lambda { eval "[1].each {|foo; bar; glark| }" }.should raise_error(SyntaxError)
+ lambda { eval "[1].each {|; bar; glark| }" }.should raise_error(SyntaxError)
+ end
+
+ it "override shadowed variables from the outer scope" do
+ out = :out
+ [1].each {|; out| out = :in }
+ out.should == :out
+
+ a = :a
+ b = :b
+ c = :c
+ d = :d
+ {ant: :bee}.each_pair do |a, b; c, d|
+ a = :A
+ b = :B
+ c = :C
+ d = :D
+ end
+ a.should == :a
+ b.should == :b
+ c.should == :c
+ d.should == :d
+ end
+
+ it "are not automatically instantiated in the outer scope" do
+ defined?(glark).should be_nil
+ [1].each {|;glark| 1}
+ defined?(glark).should be_nil
+ end
+
+ it "are automatically instantiated in the block" do
+ [1].each do |;glark|
+ glark.should be_nil
+ end
+ end
+
+ it "are visible in deeper scopes before initialization" do
+ [1].each {|;glark|
+ [1].each {
+ defined?(glark).should_not be_nil
+ glark = 1
+ }
+ glark.should == 1
+ }
+ end
+end
+
+describe "Post-args" do
+ it "appear after a splat" do
+ proc do |*a, b|
+ [a, b]
+ end.call(1, 2, 3).should == [[1, 2], 3]
+
+ proc do |*a, b, c|
+ [a, b, c]
+ end.call(1, 2, 3).should == [[1], 2, 3]
+
+ proc do |*a, b, c, d|
+ [a, b, c, d]
+ end.call(1, 2, 3).should == [[], 1, 2, 3]
+ end
+
+ it "are required" do
+ lambda {
+ lambda do |*a, b|
+ [a, b]
+ end.call
+ }.should raise_error(ArgumentError)
+ end
+
+ describe "with required args" do
+
+ it "gathers remaining args in the splat" do
+ proc do |a, *b, c|
+ [a, b, c]
+ end.call(1, 2, 3).should == [1, [2], 3]
+ end
+
+ it "has an empty splat when there are no remaining args" do
+ proc do |a, b, *c, d|
+ [a, b, c, d]
+ end.call(1, 2, 3).should == [1, 2, [], 3]
+
+ proc do |a, *b, c, d|
+ [a, b, c, d]
+ end.call(1, 2, 3).should == [1, [], 2, 3]
+ end
+ end
+
+ describe "with optional args" do
+
+ it "gathers remaining args in the splat" do
+ proc do |a=5, *b, c|
+ [a, b, c]
+ end.call(1, 2, 3).should == [1, [2], 3]
+ end
+
+ it "overrides the optional arg before gathering in the splat" do
+ proc do |a=5, *b, c|
+ [a, b, c]
+ end.call(2, 3).should == [2, [], 3]
+
+ proc do |a=5, b=6, *c, d|
+ [a, b, c, d]
+ end.call(1, 2, 3).should == [1, 2, [], 3]
+
+ proc do |a=5, *b, c, d|
+ [a, b, c, d]
+ end.call(1, 2, 3).should == [1, [], 2, 3]
+ end
+
+ it "uses the required arg before the optional and the splat" do
+ proc do |a=5, *b, c|
+ [a, b, c]
+ end.call(3).should == [5, [], 3]
+
+ proc do |a=5, b=6, *c, d|
+ [a, b, c, d]
+ end.call(3).should == [5, 6, [], 3]
+
+ proc do |a=5, *b, c, d|
+ [a, b, c, d]
+ end.call(2, 3).should == [5, [], 2, 3]
+ end
+
+ it "overrides the optional args from left to right before gathering the splat" do
+ proc do |a=5, b=6, *c, d|
+ [a, b, c, d]
+ end.call(2, 3).should == [2, 6, [], 3]
+ end
+
+ describe "with a circular argument reference" do
+ it "shadows an existing local with the same name as the argument" do
+ a = 1
+ -> {
+ @proc = eval "proc { |a=a| a }"
+ }.should complain(/circular argument reference/)
+ @proc.call.should == nil
+ end
+
+ it "shadows an existing method with the same name as the argument" do
+ def a; 1; end
+ -> {
+ @proc = eval "proc { |a=a| a }"
+ }.should complain(/circular argument reference/)
+ @proc.call.should == nil
+ end
+
+ it "calls an existing method with the same name as the argument if explicitly using ()" do
+ def a; 1; end
+ proc { |a=a()| a }.call.should == 1
+ end
+ end
+ end
+
+ describe "with pattern matching" do
+ it "extracts matched blocks with post arguments" do
+ proc do |(a, *b, c), d, e|
+ [a, b, c, d, e]
+ end.call([1, 2, 3, 4], 5, 6).should == [1, [2, 3], 4, 5, 6]
+ end
+
+ it "allows empty splats" do
+ proc do |a, (*), b|
+ [a, b]
+ end.call([1, 2, 3]).should == [1, 3]
+ end
+ end
+end
diff --git a/spec/ruby/language/break_spec.rb b/spec/ruby/language/break_spec.rb
new file mode 100644
index 0000000000..8fd6cc6f05
--- /dev/null
+++ b/spec/ruby/language/break_spec.rb
@@ -0,0 +1,365 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/break'
+
+describe "The break statement in a block" do
+ before :each do
+ ScratchPad.record []
+ @program = BreakSpecs::Block.new
+ end
+
+ it "returns nil to method invoking the method yielding to the block when not passed an argument" do
+ @program.break_nil
+ ScratchPad.recorded.should == [:a, :aa, :b, nil, :d]
+ end
+
+ it "returns a value to the method invoking the method yielding to the block" do
+ @program.break_value
+ ScratchPad.recorded.should == [:a, :aa, :b, :break, :d]
+ end
+
+ describe "yielded inside a while" do
+ it "breaks out of the block" do
+ value = @program.break_in_block_in_while
+ ScratchPad.recorded.should == [:aa, :break]
+ value.should == :value
+ end
+ end
+
+ describe "captured and delegated to another method repeatedly" do
+ it "breaks out of the block" do
+ @program.looped_break_in_captured_block
+ ScratchPad.recorded.should == [:begin,
+ :preloop,
+ :predele,
+ :preyield,
+ :prebreak,
+ :postbreak,
+ :postyield,
+ :postdele,
+ :predele,
+ :preyield,
+ :prebreak,
+ :end]
+ end
+ end
+end
+
+describe "The break statement in a captured block" do
+ before :each do
+ ScratchPad.record []
+ @program = BreakSpecs::Block.new
+ end
+
+ describe "when the invocation of the scope creating the block is still active" do
+ it "raises a LocalJumpError when invoking the block from the scope creating the block" do
+ lambda { @program.break_in_method }.should raise_error(LocalJumpError)
+ ScratchPad.recorded.should == [:a, :xa, :d, :b]
+ end
+
+ it "raises a LocalJumpError when invoking the block from a method" do
+ lambda { @program.break_in_nested_method }.should raise_error(LocalJumpError)
+ ScratchPad.recorded.should == [:a, :xa, :cc, :aa, :b]
+ end
+
+ it "raises a LocalJumpError when yielding to the block" do
+ lambda { @program.break_in_yielding_method }.should raise_error(LocalJumpError)
+ ScratchPad.recorded.should == [:a, :xa, :cc, :aa, :b]
+ end
+ end
+
+ describe "from a scope that has returned" do
+ it "raises a LocalJumpError when calling the block from a method" do
+ lambda { @program.break_in_method_captured }.should raise_error(LocalJumpError)
+ ScratchPad.recorded.should == [:a, :za, :xa, :zd, :zb]
+ end
+
+ it "raises a LocalJumpError when yielding to the block" do
+ lambda { @program.break_in_yield_captured }.should raise_error(LocalJumpError)
+ ScratchPad.recorded.should == [:a, :za, :xa, :zd, :aa, :zb]
+ end
+ end
+
+ describe "from another thread" do
+ it "raises a LocalJumpError when getting the value from another thread" do
+ thread_with_break = Thread.new do
+ begin
+ break :break
+ rescue LocalJumpError => e
+ e
+ end
+ end
+ thread_with_break.value.should be_an_instance_of(LocalJumpError)
+ end
+ end
+end
+
+describe "The break statement in a lambda" do
+ before :each do
+ ScratchPad.record []
+ @program = BreakSpecs::Lambda.new
+ end
+
+ it "returns from the lambda" do
+ l = lambda {
+ ScratchPad << :before
+ break :foo
+ ScratchPad << :after
+ }
+ l.call.should == :foo
+ ScratchPad.recorded.should == [:before]
+ end
+
+ it "returns from the call site if the lambda is passed as a block" do
+ def mid(&b)
+ lambda {
+ ScratchPad << :before
+ b.call
+ ScratchPad << :unreachable1
+ }.call
+ ScratchPad << :unreachable2
+ end
+
+ result = [1].each do |e|
+ mid {
+ break # This breaks from mid
+ ScratchPad << :unreachable3
+ }
+ ScratchPad << :after
+ end
+ result.should == [1]
+ ScratchPad.recorded.should == [:before, :after]
+ end
+
+ describe "when the invocation of the scope creating the lambda is still active" do
+ it "returns nil when not passed an argument" do
+ @program.break_in_defining_scope false
+ ScratchPad.recorded.should == [:a, :b, nil, :d]
+ end
+
+ it "returns a value to the scope creating and calling the lambda" do
+ @program.break_in_defining_scope
+ ScratchPad.recorded.should == [:a, :b, :break, :d]
+ end
+
+ it "returns a value to the method scope below invoking the lambda" do
+ @program.break_in_nested_scope
+ ScratchPad.recorded.should == [:a, :d, :aa, :b, :break, :bb, :e]
+ end
+
+ it "returns a value to a block scope invoking the lambda in a method below" do
+ @program.break_in_nested_scope_block
+ ScratchPad.recorded.should == [:a, :d, :aa, :aaa, :bb, :b, :break, :cc, :bbb, :dd, :e]
+ end
+
+ it "returns from the lambda" do
+ @program.break_in_nested_scope_yield
+ ScratchPad.recorded.should == [:a, :d, :aaa, :b, :bbb, :e]
+ end
+ end
+
+ describe "created at the toplevel" do
+ it "returns a value when invoking from the toplevel" do
+ code = fixture __FILE__, "break_lambda_toplevel.rb"
+ ruby_exe(code).chomp.should == "a,b,break,d"
+ end
+
+ it "returns a value when invoking from a method" do
+ code = fixture __FILE__, "break_lambda_toplevel_method.rb"
+ ruby_exe(code).chomp.should == "a,d,b,break,e,f"
+ end
+
+ it "returns a value when invoking from a block" do
+ code = fixture __FILE__, "break_lambda_toplevel_block.rb"
+ ruby_exe(code).chomp.should == "a,d,f,b,break,g,e,h"
+ end
+ end
+
+ describe "from a scope that has returned" do
+ it "returns a value to the method scope invoking the lambda" do
+ @program.break_in_method
+ ScratchPad.recorded.should == [:a, :la, :ld, :lb, :break, :b]
+ end
+
+ it "returns a value to the block scope invoking the lambda in a method" do
+ @program.break_in_block_in_method
+ ScratchPad.recorded.should == [:a, :aaa, :b, :la, :ld, :lb, :break, :c, :bbb, :d]
+ end
+
+ # By passing a lambda as a block argument, the user is requesting to treat
+ # the lambda as a block, which in this case means breaking to a scope that
+ # has returned. This is a subtle and confusing semantic where a block pass
+ # is removing the lambda-ness of a lambda.
+ it "raises a LocalJumpError when yielding to a lambda passed as a block argument" do
+ @program.break_in_method_yield
+ ScratchPad.recorded.should == [:a, :la, :ld, :aaa, :lb, :bbb, :b]
+ end
+ end
+end
+
+describe "Break inside a while loop" do
+ describe "with a value" do
+ it "exits the loop and returns the value" do
+ a = while true; break; end; a.should == nil
+ a = while true; break nil; end; a.should == nil
+ a = while true; break 1; end; a.should == 1
+ a = while true; break []; end; a.should == []
+ a = while true; break [1]; end; a.should == [1]
+ end
+
+ it "passes the value returned by a method with omitted parenthesis and passed block" do
+ obj = BreakSpecs::Block.new
+ lambda { break obj.method :value do |x| x end }.call.should == :value
+ end
+ end
+
+ describe "with a splat" do
+ it "exits the loop and makes the splat an Array" do
+ a = while true; break *[1,2]; end; a.should == [1,2]
+ end
+
+ it "treats nil as an empty array" do
+ a = while true; break *nil; end; a.should == []
+ end
+
+ it "preserves an array as is" do
+ a = while true; break *[]; end; a.should == []
+ a = while true; break *[1,2]; end; a.should == [1,2]
+ a = while true; break *[nil]; end; a.should == [nil]
+ a = while true; break *[[]]; end; a.should == [[]]
+ end
+
+ it "wraps a non-Array in an Array" do
+ a = while true; break *1; end; a.should == [1]
+ end
+ end
+
+ it "stops a while loop when run" do
+ i = 0
+ while true
+ break if i == 2
+ i+=1
+ end
+ i.should == 2
+ end
+
+ it "causes a call with a block to return when run" do
+ at = 0
+ 0.upto(5) do |i|
+ at = i
+ break i if i == 2
+ end.should == 2
+ at.should == 2
+ end
+end
+
+
+# TODO: Rewrite all the specs from here to the end of the file in the style
+# above.
+describe "Executing break from within a block" do
+
+ before :each do
+ ScratchPad.clear
+ end
+
+ # Discovered in JRuby (see JRUBY-2756)
+ it "returns from the original invoking method even in case of chained calls" do
+ class BreakTest
+ # case #1: yield
+ def self.meth_with_yield(&b)
+ yield
+ fail("break returned from yield to wrong place")
+ end
+ def self.invoking_method(&b)
+ meth_with_yield(&b)
+ fail("break returned from 'meth_with_yield' method to wrong place")
+ end
+
+ # case #2: block.call
+ def self.meth_with_block_call(&b)
+ b.call
+ fail("break returned from b.call to wrong place")
+ end
+ def self.invoking_method2(&b)
+ meth_with_block_call(&b)
+ fail("break returned from 'meth_with_block_call' method to wrong place")
+ end
+ end
+
+ # this calls a method that calls another method that yields to the block
+ BreakTest.invoking_method do
+ break
+ fail("break didn't, well, break")
+ end
+
+ # this calls a method that calls another method that calls the block
+ BreakTest.invoking_method2 do
+ break
+ fail("break didn't, well, break")
+ end
+
+ res = BreakTest.invoking_method do
+ break :return_value
+ fail("break didn't, well, break")
+ end
+ res.should == :return_value
+
+ res = BreakTest.invoking_method2 do
+ break :return_value
+ fail("break didn't, well, break")
+ end
+ res.should == :return_value
+
+ end
+
+ class BreakTest2
+ def one
+ two { yield }
+ end
+
+ def two
+ yield
+ ensure
+ ScratchPad << :two_ensure
+ end
+
+ def three
+ begin
+ one { break }
+ ScratchPad << :three_post
+ ensure
+ ScratchPad << :three_ensure
+ end
+ end
+ end
+
+ it "runs ensures when continuing upward" do
+ ScratchPad.record []
+
+ bt2 = BreakTest2.new
+ bt2.one { break }
+ ScratchPad.recorded.should == [:two_ensure]
+ end
+
+ it "runs ensures when breaking from a loop" do
+ ScratchPad.record []
+
+ while true
+ begin
+ ScratchPad << :begin
+ break if true
+ ensure
+ ScratchPad << :ensure
+ end
+ end
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "doesn't run ensures in the destination method" do
+ ScratchPad.record []
+
+ bt2 = BreakTest2.new
+ bt2.three
+ ScratchPad.recorded.should == [:two_ensure, :three_post, :three_ensure]
+ end
+end
diff --git a/spec/ruby/language/case_spec.rb b/spec/ruby/language/case_spec.rb
new file mode 100644
index 0000000000..0b74efa257
--- /dev/null
+++ b/spec/ruby/language/case_spec.rb
@@ -0,0 +1,427 @@
+require_relative '../spec_helper'
+
+describe "The 'case'-construct" do
+ it "evaluates the body of the when clause matching the case target expression" do
+ case 1
+ when 2; false
+ when 1; true
+ end.should == true
+ end
+
+ it "evaluates the body of the when clause whose array expression includes the case target expression" do
+ case 2
+ when 3, 4; false
+ when 1, 2; true
+ end.should == true
+ end
+
+ it "evaluates the body of the when clause in left-to-right order if it's an array expression" do
+ @calls = []
+ def foo; @calls << :foo; end
+ def bar; @calls << :bar; end
+
+ case true
+ when foo, bar;
+ end
+
+ @calls.should == [:foo, :bar]
+ end
+
+ it "evaluates the body of the when clause whose range expression includes the case target expression" do
+ case 5
+ when 21..30; false
+ when 1..20; true
+ end.should == true
+ end
+
+ it "returns nil when no 'then'-bodies are given" do
+ case "a"
+ when "a"
+ when "b"
+ end.should == nil
+ end
+
+ it "evaluates the 'else'-body when no other expression matches" do
+ case "c"
+ when "a"; 'foo'
+ when "b"; 'bar'
+ else 'zzz'
+ end.should == 'zzz'
+ end
+
+ it "returns nil when no expression matches and 'else'-body is empty" do
+ case "c"
+ when "a"; "a"
+ when "b"; "b"
+ else
+ end.should == nil
+ end
+
+ it "returns 2 when a then body is empty" do
+ case Object.new
+ when Numeric then
+ 1
+ when String then
+ # ok
+ else
+ 2
+ end.should == 2
+ end
+
+ it "returns the statement following 'then'" do
+ case "a"
+ when "a" then 'foo'
+ when "b" then 'bar'
+ end.should == 'foo'
+ end
+
+ it "tests classes with case equality" do
+ case "a"
+ when String
+ 'foo'
+ when Symbol
+ 'bar'
+ end.should == 'foo'
+ end
+
+ it "tests with matching regexps" do
+ case "hello"
+ when /abc/; false
+ when /^hell/; true
+ end.should == true
+ end
+
+ it "tests with matching regexps and sets $~ and captures" do
+ case "foo42"
+ when /oo(\d+)/
+ $~.should be_kind_of(MatchData)
+ $1.should == "42"
+ else
+ flunk
+ end
+ $~.should be_kind_of(MatchData)
+ $1.should == "42"
+ end
+
+ it "tests with a regexp interpolated within another regexp" do
+ digits = '\d+'
+ case "foo44"
+ when /oo(#{digits})/
+ $~.should be_kind_of(MatchData)
+ $1.should == "44"
+ else
+ flunk
+ end
+ $~.should be_kind_of(MatchData)
+ $1.should == "44"
+ end
+
+ it "tests with a string interpolated in a regexp" do
+ digits_regexp = /\d+/
+ case "foo43"
+ when /oo(#{digits_regexp})/
+ $~.should be_kind_of(MatchData)
+ $1.should == "43"
+ else
+ flunk
+ end
+ $~.should be_kind_of(MatchData)
+ $1.should == "43"
+ end
+
+ it "does not test with equality when given classes" do
+ case :symbol.class
+ when Symbol
+ "bar"
+ when String
+ "bar"
+ else
+ "foo"
+ end.should == "foo"
+ end
+
+ it "takes lists of values" do
+ case 'z'
+ when 'a', 'b', 'c', 'd'
+ "foo"
+ when 'x', 'y', 'z'
+ "bar"
+ end.should == "bar"
+
+ case 'b'
+ when 'a', 'b', 'c', 'd'
+ "foo"
+ when 'x', 'y', 'z'
+ "bar"
+ end.should == "foo"
+ end
+
+ it "expands arrays to lists of values" do
+ case 'z'
+ when *['a', 'b', 'c', 'd']
+ "foo"
+ when *['x', 'y', 'z']
+ "bar"
+ end.should == "bar"
+ end
+
+ it "takes an expanded array in addition to a list of values" do
+ case 'f'
+ when 'f', *['a', 'b', 'c', 'd']
+ "foo"
+ when *['x', 'y', 'z']
+ "bar"
+ end.should == "foo"
+
+ case 'b'
+ when 'f', *['a', 'b', 'c', 'd']
+ "foo"
+ when *['x', 'y', 'z']
+ "bar"
+ end.should == "foo"
+ end
+
+ it "takes an expanded array before additional listed values" do
+ case 'f'
+ when *['a', 'b', 'c', 'd'], 'f'
+ "foo"
+ when *['x', 'y', 'z']
+ "bar"
+ end.should == 'foo'
+ end
+
+ it "expands arrays from variables before additional listed values" do
+ a = ['a', 'b', 'c']
+ case 'a'
+ when *a, 'd', 'e'
+ "foo"
+ when 'x'
+ "bar"
+ end.should == "foo"
+ end
+
+ it "expands arrays from variables before a single additional listed value" do
+ a = ['a', 'b', 'c']
+ case 'a'
+ when *a, 'd'
+ "foo"
+ when 'x'
+ "bar"
+ end.should == "foo"
+ end
+
+ it "expands multiple arrays from variables before additional listed values" do
+ a = ['a', 'b', 'c']
+ b = ['d', 'e', 'f']
+
+ case 'f'
+ when *a, *b, 'g', 'h'
+ "foo"
+ when 'x'
+ "bar"
+ end.should == "foo"
+ end
+
+ # MR: critical
+ it "concats arrays before expanding them" do
+ a = ['a', 'b', 'c', 'd']
+ b = ['f']
+
+ case 'f'
+ when 'f', *a|b
+ "foo"
+ when *['x', 'y', 'z']
+ "bar"
+ end.should == "foo"
+ end
+
+ it "never matches when clauses with no values" do
+ case nil
+ when *[]
+ "foo"
+ end.should == nil
+ end
+
+ it "lets you define a method after the case statement" do
+ case (def foo; 'foo'; end; 'f')
+ when 'a'
+ 'foo'
+ when 'f'
+ 'bar'
+ end.should == 'bar'
+ end
+
+ it "raises a SyntaxError when 'else' is used when no 'when' is given" do
+ lambda {
+ eval <<-CODE
+ case 4
+ else
+ true
+ end
+ CODE
+ }.should raise_error(SyntaxError)
+ end
+
+ it "raises a SyntaxError when 'else' is used before a 'when' was given" do
+ lambda {
+ eval <<-CODE
+ case 4
+ else
+ true
+ when 4; false
+ end
+ CODE
+ }.should raise_error(SyntaxError)
+ end
+
+ it "supports nested case statements" do
+ result = false
+ case :x
+ when Symbol
+ case :y
+ when Symbol
+ result = true
+ end
+ end
+ result.should == true
+ end
+
+ it "supports nested case statements followed by a when with a splatted array" do
+ result = false
+ case :x
+ when Symbol
+ case :y
+ when Symbol
+ result = true
+ end
+ when *[Symbol]
+ result = false
+ end
+ result.should == true
+ end
+
+ it "supports nested case statements followed by a when with a splatted non-array" do
+ result = false
+ case :x
+ when Symbol
+ case :y
+ when Symbol
+ result = true
+ end
+ when *Symbol
+ result = false
+ end
+ result.should == true
+ end
+
+ it "works even if there's only one when statement" do
+ case 1
+ when 1
+ 100
+ end.should == 100
+ end
+end
+
+describe "The 'case'-construct with no target expression" do
+ it "evaluates the body of the first clause when at least one of its condition expressions is true" do
+ case
+ when true, false; 'foo'
+ end.should == 'foo'
+ end
+
+ it "evaluates the body of the first when clause that is not false/nil" do
+ case
+ when false; 'foo'
+ when 2; 'bar'
+ when 1 == 1; 'baz'
+ end.should == 'bar'
+
+ case
+ when false; 'foo'
+ when nil; 'foo'
+ when 1 == 1; 'bar'
+ end.should == 'bar'
+ end
+
+ it "evaluates the body of the else clause if all when clauses are false/nil" do
+ case
+ when false; 'foo'
+ when nil; 'foo'
+ when 1 == 2; 'bar'
+ else 'baz'
+ end.should == 'baz'
+ end
+
+ it "evaluates multiple conditional expressions as a boolean disjunction" do
+ case
+ when true, false; 'foo'
+ else 'bar'
+ end.should == 'foo'
+
+ case
+ when false, true; 'foo'
+ else 'bar'
+ end.should == 'foo'
+ end
+
+ it "evaluates true as only 'true' when true is the first clause" do
+ case 1
+ when true; "bad"
+ when Integer; "good"
+ end.should == "good"
+ end
+
+ it "evaluates false as only 'false' when false is the first clause" do
+ case nil
+ when false; "bad"
+ when nil; "good"
+ end.should == "good"
+ end
+
+ it "treats a literal array as its own when argument, rather than a list of arguments" do
+ case 'foo'
+ when ['foo', 'foo']; 'bad'
+ when 'foo'; 'good'
+ end.should == 'good'
+ end
+
+ it "takes multiple expanded arrays" do
+ a1 = ['f', 'o', 'o']
+ a2 = ['b', 'a', 'r']
+
+ case 'f'
+ when *a1, *['x', 'y', 'z']
+ "foo"
+ when *a2, *['x', 'y', 'z']
+ "bar"
+ end.should == "foo"
+
+ case 'b'
+ when *a1, *['x', 'y', 'z']
+ "foo"
+ when *a2, *['x', 'y', 'z']
+ "bar"
+ end.should == "bar"
+ end
+
+ it "calls === even when private" do
+ klass = Class.new do
+ def ===(o)
+ true
+ end
+ private :===
+ end
+
+ case 1
+ when klass.new
+ :called
+ end.should == :called
+ end
+
+ it "accepts complex expressions within ()" do
+ case 'a'
+ when (raise if 2+2 == 3; /a/)
+ :called
+ end.should == :called
+ end
+end
diff --git a/spec/ruby/language/class_spec.rb b/spec/ruby/language/class_spec.rb
new file mode 100644
index 0000000000..a91b165ffe
--- /dev/null
+++ b/spec/ruby/language/class_spec.rb
@@ -0,0 +1,328 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/class'
+
+ClassSpecsNumber = 12
+
+module ClassSpecs
+ Number = 12
+end
+
+describe "The class keyword" do
+ it "creates a new class with semicolon" do
+ class ClassSpecsKeywordWithSemicolon; end
+ ClassSpecsKeywordWithSemicolon.should be_an_instance_of(Class)
+ end
+
+ it "does not raise a SyntaxError when opening a class without a semicolon" do
+ eval "class ClassSpecsKeywordWithoutSemicolon end"
+ ClassSpecsKeywordWithoutSemicolon.should be_an_instance_of(Class)
+ end
+end
+
+describe "A class definition" do
+ it "creates a new class" do
+ ClassSpecs::A.should be_kind_of(Class)
+ ClassSpecs::A.new.should be_kind_of(ClassSpecs::A)
+ end
+
+ it "has no class variables" do
+ ClassSpecs::A.class_variables.should == []
+ end
+
+ it "raises TypeError if constant given as class name exists and is not a Module" do
+ lambda {
+ class ClassSpecsNumber
+ end
+ }.should raise_error(TypeError)
+ end
+
+ # test case known to be detecting bugs (JRuby, MRI)
+ it "raises TypeError if the constant qualifying the class is nil" do
+ lambda {
+ class nil::Foo
+ end
+ }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError if any constant qualifying the class is not a Module" do
+ lambda {
+ class ClassSpecs::Number::MyClass
+ end
+ }.should raise_error(TypeError)
+
+ lambda {
+ class ClassSpecsNumber::MyClass
+ end
+ }.should raise_error(TypeError)
+ end
+
+ it "inherits from Object by default" do
+ ClassSpecs::A.superclass.should == Object
+ end
+
+ it "raises an error when trying to change the superclass" do
+ module ClassSpecs
+ class SuperclassResetToSubclass < L
+ end
+ lambda {
+ class SuperclassResetToSubclass < M
+ end
+ }.should raise_error(TypeError, /superclass mismatch/)
+ end
+ end
+
+ it "raises an error when reopening a class with BasicObject as superclass" do
+ module ClassSpecs
+ class SuperclassReopenedBasicObject < A
+ end
+ SuperclassReopenedBasicObject.superclass.should == A
+
+ lambda {
+ class SuperclassReopenedBasicObject < BasicObject
+ end
+ }.should raise_error(TypeError, /superclass mismatch/)
+ SuperclassReopenedBasicObject.superclass.should == A
+ end
+ end
+
+ # [Bug #12367] [ruby-core:75446]
+ it "raises an error when reopening a class with Object as superclass" do
+ module ClassSpecs
+ class SuperclassReopenedObject < A
+ end
+ SuperclassReopenedObject.superclass.should == A
+
+ lambda {
+ class SuperclassReopenedObject < Object
+ end
+ }.should raise_error(TypeError, /superclass mismatch/)
+ SuperclassReopenedObject.superclass.should == A
+ end
+ end
+
+ it "allows reopening a class without specifying the superclass" do
+ module ClassSpecs
+ class SuperclassNotGiven < A
+ end
+ SuperclassNotGiven.superclass.should == A
+
+ class SuperclassNotGiven
+ end
+ SuperclassNotGiven.superclass.should == A
+ end
+ end
+
+ it "does not allow to set the superclass even if it was not specified by the first declaration" do
+ module ClassSpecs
+ class NoSuperclassSet
+ end
+
+ lambda {
+ class NoSuperclassSet < String
+ end
+ }.should raise_error(TypeError, /superclass mismatch/)
+ end
+ end
+
+ it "allows using self as the superclass if self is a class" do
+ ClassSpecs::I::J.superclass.should == ClassSpecs::I
+
+ lambda {
+ class ShouldNotWork < self; end
+ }.should raise_error(TypeError)
+ end
+
+ it "first evaluates the superclass before checking if the class already exists" do
+ module ClassSpecs
+ class SuperclassEvaluatedFirst
+ end
+ a = SuperclassEvaluatedFirst
+
+ class SuperclassEvaluatedFirst < remove_const(:SuperclassEvaluatedFirst)
+ end
+ b = SuperclassEvaluatedFirst
+ b.superclass.should == a
+ end
+ end
+
+ it "raises a TypeError if inheriting from a metaclass" do
+ obj = mock("metaclass super")
+ meta = obj.singleton_class
+ lambda { class ClassSpecs::MetaclassSuper < meta; end }.should raise_error(TypeError)
+ end
+
+ it "allows the declaration of class variables in the body" do
+ ClassSpecs.string_class_variables(ClassSpecs::B).should == ["@@cvar"]
+ ClassSpecs::B.send(:class_variable_get, :@@cvar).should == :cvar
+ end
+
+ it "stores instance variables defined in the class body in the class object" do
+ ClassSpecs.string_instance_variables(ClassSpecs::B).should include("@ivar")
+ ClassSpecs::B.instance_variable_get(:@ivar).should == :ivar
+ end
+
+ it "allows the declaration of class variables in a class method" do
+ ClassSpecs::C.class_variables.should == []
+ ClassSpecs::C.make_class_variable
+ ClassSpecs.string_class_variables(ClassSpecs::C).should == ["@@cvar"]
+ ClassSpecs::C.remove_class_variable :@@cvar
+ end
+
+ it "allows the definition of class-level instance variables in a class method" do
+ ClassSpecs.string_instance_variables(ClassSpecs::C).should_not include("@civ")
+ ClassSpecs::C.make_class_instance_variable
+ ClassSpecs.string_instance_variables(ClassSpecs::C).should include("@civ")
+ ClassSpecs::C.remove_instance_variable :@civ
+ end
+
+ it "allows the declaration of class variables in an instance method" do
+ ClassSpecs::D.class_variables.should == []
+ ClassSpecs::D.new.make_class_variable
+ ClassSpecs.string_class_variables(ClassSpecs::D).should == ["@@cvar"]
+ ClassSpecs::D.remove_class_variable :@@cvar
+ end
+
+ it "allows the definition of instance methods" do
+ ClassSpecs::E.new.meth.should == :meth
+ end
+
+ it "allows the definition of class methods" do
+ ClassSpecs::E.cmeth.should == :cmeth
+ end
+
+ it "allows the definition of class methods using class << self" do
+ ClassSpecs::E.smeth.should == :smeth
+ end
+
+ it "allows the definition of Constants" do
+ Object.const_defined?('CONSTANT').should == false
+ ClassSpecs::E.const_defined?('CONSTANT').should == true
+ ClassSpecs::E::CONSTANT.should == :constant!
+ end
+
+ it "returns the value of the last statement in the body" do
+ class ClassSpecs::Empty; end.should == nil
+ class ClassSpecs::Twenty; 20; end.should == 20
+ class ClassSpecs::Plus; 10 + 20; end.should == 30
+ class ClassSpecs::Singleton; class << self; :singleton; end; end.should == :singleton
+ end
+
+ describe "within a block creates a new class in the lexical scope" do
+ it "for named classes at the toplevel" do
+ klass = Class.new do
+ class Howdy
+ end
+
+ def self.get_class_name
+ Howdy.name
+ end
+ end
+
+ Howdy.name.should == 'Howdy'
+ klass.get_class_name.should == 'Howdy'
+ end
+
+ it "for named classes in a module" do
+ klass = ClassSpecs::ANON_CLASS_FOR_NEW.call
+
+ ClassSpecs::NamedInModule.name.should == 'ClassSpecs::NamedInModule'
+ klass.get_class_name.should == 'ClassSpecs::NamedInModule'
+ end
+
+ it "for anonymous classes" do
+ klass = Class.new do
+ def self.get_class
+ Class.new do
+ def self.foo
+ 'bar'
+ end
+ end
+ end
+
+ def self.get_result
+ get_class.foo
+ end
+ end
+
+ klass.get_result.should == 'bar'
+ end
+
+ it "for anonymous classes assigned to a constant" do
+ klass = Class.new do
+ AnonWithConstant = Class.new
+
+ def self.get_class_name
+ AnonWithConstant.name
+ end
+ end
+
+ AnonWithConstant.name.should == 'AnonWithConstant'
+ klass.get_class_name.should == 'AnonWithConstant'
+ end
+ end
+end
+
+describe "An outer class definition" do
+ it "contains the inner classes" do
+ ClassSpecs::Container.constants.should include(:A, :B)
+ end
+end
+
+describe "A class definition extending an object (sclass)" do
+ it "allows adding methods" do
+ ClassSpecs::O.smeth.should == :smeth
+ end
+
+ it "raises a TypeError when trying to extend numbers" do
+ lambda {
+ eval <<-CODE
+ class << 1
+ def xyz
+ self
+ end
+ end
+ CODE
+ }.should raise_error(TypeError)
+ end
+
+ it "allows accessing the block of the original scope" do
+ ClassSpecs.sclass_with_block { 123 }.should == 123
+ end
+
+ it "can use return to cause the enclosing method to return" do
+ ClassSpecs.sclass_with_return.should == :inner
+ end
+end
+
+describe "Reopening a class" do
+ it "extends the previous definitions" do
+ c = ClassSpecs::F.new
+ c.meth.should == :meth
+ c.another.should == :another
+ end
+
+ it "overwrites existing methods" do
+ ClassSpecs::G.new.override.should == :override
+ end
+
+ it "raises a TypeError when superclasses mismatch" do
+ lambda { class ClassSpecs::A < Array; end }.should raise_error(TypeError)
+ end
+
+ it "adds new methods to subclasses" do
+ lambda { ClassSpecs::M.m }.should raise_error(NoMethodError)
+ class ClassSpecs::L
+ def self.m
+ 1
+ end
+ end
+ ClassSpecs::M.m.should == 1
+ ClassSpecs::L.singleton_class.send(:remove_method, :m)
+ end
+end
+
+describe "class provides hooks" do
+ it "calls inherited when a class is created" do
+ ClassSpecs::H.track_inherited.should == [ClassSpecs::K]
+ end
+end
diff --git a/spec/ruby/language/class_variable_spec.rb b/spec/ruby/language/class_variable_spec.rb
new file mode 100644
index 0000000000..98954023a2
--- /dev/null
+++ b/spec/ruby/language/class_variable_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/class_variables'
+
+describe "A class variable" do
+ after :each do
+ ClassVariablesSpec::ClassA.new.cvar_a = :cvar_a
+ end
+
+ it "can be accessed from a subclass" do
+ ClassVariablesSpec::ClassB.new.cvar_a.should == :cvar_a
+ end
+
+ it "is set in the superclass" do
+ a = ClassVariablesSpec::ClassA.new
+ b = ClassVariablesSpec::ClassB.new
+ b.cvar_a = :new_val
+
+ a.cvar_a.should == :new_val
+ end
+end
+
+describe "A class variable defined in a module" do
+ after :each do
+ ClassVariablesSpec::ClassC.cvar_m = :value
+ ClassVariablesSpec::ClassC.remove_class_variable(:@@cvar) if ClassVariablesSpec::ClassC.cvar_defined?
+ end
+
+ it "can be accessed from classes that extend the module" do
+ ClassVariablesSpec::ClassC.cvar_m.should == :value
+ end
+
+ it "is not defined in these classes" do
+ ClassVariablesSpec::ClassC.cvar_defined?.should be_false
+ end
+
+ it "is only updated in the module a method defined in the module is used" do
+ ClassVariablesSpec::ClassC.cvar_m = "new value"
+ ClassVariablesSpec::ClassC.cvar_m.should == "new value"
+
+ ClassVariablesSpec::ClassC.cvar_defined?.should be_false
+ end
+
+ it "is updated in the class when a Method defined in the class is used" do
+ ClassVariablesSpec::ClassC.cvar_c = "new value"
+ ClassVariablesSpec::ClassC.cvar_defined?.should be_true
+ end
+
+ it "can be accessed inside the class using the module methods" do
+ ClassVariablesSpec::ClassC.cvar_c = "new value"
+ ClassVariablesSpec::ClassC.cvar_m.should == :value
+ end
+
+ it "can be accessed from modules that extend the module" do
+ ClassVariablesSpec::ModuleO.cvar_n.should == :value
+ end
+
+ it "is defined in the extended module" do
+ ClassVariablesSpec::ModuleN.class_variable_defined?(:@@cvar_n).should be_true
+ end
+
+ it "is not defined in the extending module" do
+ ClassVariablesSpec::ModuleO.class_variable_defined?(:@@cvar_n).should be_false
+ end
+end
+
+describe 'A class variable definition' do
+ it "is created in a module if any of the parents do not define it" do
+ a = Class.new
+ b = Class.new(a)
+ c = Class.new(b)
+ b.class_variable_set(:@@cv, :value)
+
+ lambda { a.class_variable_get(:@@cv) }.should raise_error(NameError)
+ b.class_variable_get(:@@cv).should == :value
+ c.class_variable_get(:@@cv).should == :value
+
+ # updates the same variable
+ c.class_variable_set(:@@cv, :next)
+
+ lambda { a.class_variable_get(:@@cv) }.should raise_error(NameError)
+ b.class_variable_get(:@@cv).should == :next
+ c.class_variable_get(:@@cv).should == :next
+ end
+end
diff --git a/spec/ruby/language/constants_spec.rb b/spec/ruby/language/constants_spec.rb
new file mode 100644
index 0000000000..5b111f4e81
--- /dev/null
+++ b/spec/ruby/language/constants_spec.rb
@@ -0,0 +1,715 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/constants'
+require_relative 'fixtures/constants_sclass'
+require_relative 'fixtures/constant_visibility'
+
+# Read the documentation in fixtures/constants.rb for the guidelines and
+# rationale for the structure and organization of these specs.
+
+describe "Literal (A::X) constant resolution" do
+ describe "with statically assigned constants" do
+ it "searches the immediate class or module scope first" do
+ ConstantSpecs::ClassA::CS_CONST10.should == :const10_10
+ ConstantSpecs::ModuleA::CS_CONST10.should == :const10_1
+ ConstantSpecs::ParentA::CS_CONST10.should == :const10_5
+ ConstantSpecs::ContainerA::CS_CONST10.should == :const10_2
+ ConstantSpecs::ContainerA::ChildA::CS_CONST10.should == :const10_3
+ end
+
+ it "searches a module included in the immediate class before the superclass" do
+ ConstantSpecs::ContainerA::ChildA::CS_CONST15.should == :const15_1
+ end
+
+ it "searches the superclass before a module included in the superclass" do
+ ConstantSpecs::ContainerA::ChildA::CS_CONST11.should == :const11_1
+ end
+
+ it "searches a module included in the superclass" do
+ ConstantSpecs::ContainerA::ChildA::CS_CONST12.should == :const12_1
+ end
+
+ it "searches the superclass chain" do
+ ConstantSpecs::ContainerA::ChildA::CS_CONST13.should == :const13
+ end
+
+ it "searches Object if no class or module qualifier is given" do
+ CS_CONST1.should == :const1
+ CS_CONST10.should == :const10_1
+ end
+
+ it "searches Object after searching other scopes" do
+ module ConstantSpecs::SpecAdded1
+ CS_CONST10.should == :const10_1
+ end
+ end
+
+ it "searches Object if a toplevel qualifier (::X) is given" do
+ ::CS_CONST1.should == :const1
+ ::CS_CONST10.should == :const10_1
+ end
+
+ it "does not search the singleton class of the class or module" do
+ lambda do
+ ConstantSpecs::ContainerA::ChildA::CS_CONST14
+ end.should raise_error(NameError)
+ lambda { ConstantSpecs::CS_CONST14 }.should raise_error(NameError)
+ end
+ end
+
+ describe "with dynamically assigned constants" do
+ it "searches the immediate class or module scope first" do
+ ConstantSpecs::ClassB::CS_CONST101 = :const101_1
+ ConstantSpecs::ClassB::CS_CONST101.should == :const101_1
+
+ ConstantSpecs::ParentB::CS_CONST101 = :const101_2
+ ConstantSpecs::ParentB::CS_CONST101.should == :const101_2
+
+ ConstantSpecs::ContainerB::CS_CONST101 = :const101_3
+ ConstantSpecs::ContainerB::CS_CONST101.should == :const101_3
+
+ ConstantSpecs::ContainerB::ChildB::CS_CONST101 = :const101_4
+ ConstantSpecs::ContainerB::ChildB::CS_CONST101.should == :const101_4
+
+ ConstantSpecs::ModuleA::CS_CONST101 = :const101_5
+ ConstantSpecs::ModuleA::CS_CONST101.should == :const101_5
+ end
+
+ it "searches a module included in the immediate class before the superclass" do
+ ConstantSpecs::ParentB::CS_CONST102 = :const102_1
+ ConstantSpecs::ModuleF::CS_CONST102 = :const102_2
+ ConstantSpecs::ContainerB::ChildB::CS_CONST102.should == :const102_2
+ end
+
+ it "searches the superclass before a module included in the superclass" do
+ ConstantSpecs::ModuleE::CS_CONST103 = :const103_1
+ ConstantSpecs::ParentB::CS_CONST103 = :const103_2
+ ConstantSpecs::ContainerB::ChildB::CS_CONST103.should == :const103_2
+ end
+
+ it "searches a module included in the superclass" do
+ ConstantSpecs::ModuleA::CS_CONST104 = :const104_1
+ ConstantSpecs::ModuleE::CS_CONST104 = :const104_2
+ ConstantSpecs::ContainerB::ChildB::CS_CONST104.should == :const104_2
+ end
+
+ it "searches the superclass chain" do
+ ConstantSpecs::ModuleA::CS_CONST105 = :const105
+ ConstantSpecs::ContainerB::ChildB::CS_CONST105.should == :const105
+ end
+
+ it "searches Object if no class or module qualifier is given" do
+ CS_CONST106 = :const106
+ CS_CONST106.should == :const106
+ end
+
+ it "searches Object if a toplevel qualifier (::X) is given" do
+ ::CS_CONST107 = :const107
+ ::CS_CONST107.should == :const107
+ end
+
+ it "does not search the singleton class of the class or module" do
+ class << ConstantSpecs::ContainerB::ChildB
+ CS_CONST108 = :const108_1
+ end
+
+ lambda do
+ ConstantSpecs::ContainerB::ChildB::CS_CONST108
+ end.should raise_error(NameError)
+
+ module ConstantSpecs
+ class << self
+ CS_CONST108 = :const108_2
+ end
+ end
+
+ lambda { ConstantSpecs::CS_CONST108 }.should raise_error(NameError)
+ end
+
+ it "returns the updated value when a constant is reassigned" do
+ ConstantSpecs::ClassB::CS_CONST109 = :const109_1
+ ConstantSpecs::ClassB::CS_CONST109.should == :const109_1
+
+ -> {
+ ConstantSpecs::ClassB::CS_CONST109 = :const109_2
+ }.should complain(/already initialized constant/)
+ ConstantSpecs::ClassB::CS_CONST109.should == :const109_2
+ end
+
+ it "evaluates the right hand side before evaluating a constant path" do
+ mod = Module.new
+
+ mod.module_eval <<-EOC
+ ConstantSpecsRHS::B = begin
+ module ConstantSpecsRHS; end
+
+ "hello"
+ end
+ EOC
+
+ mod::ConstantSpecsRHS::B.should == 'hello'
+ end
+ end
+
+ it "raises a NameError if no constant is defined in the search path" do
+ lambda { ConstantSpecs::ParentA::CS_CONSTX }.should raise_error(NameError)
+ end
+
+ it "sends #const_missing to the original class or module scope" do
+ ConstantSpecs::ClassA::CS_CONSTX.should == :CS_CONSTX
+ end
+
+ it "evaluates the qualifier" do
+ ConstantSpecs.get_const::CS_CONST2.should == :const2
+ end
+
+ it "raises a TypeError if a non-class or non-module qualifier is given" do
+ lambda { CS_CONST1::CS_CONST }.should raise_error(TypeError)
+ lambda { 1::CS_CONST }.should raise_error(TypeError)
+ lambda { "mod"::CS_CONST }.should raise_error(TypeError)
+ lambda { false::CS_CONST }.should raise_error(TypeError)
+ end
+end
+
+describe "Constant resolution within methods" do
+ describe "with statically assigned constants" do
+ it "searches the immediate class or module scope first" do
+ ConstantSpecs::ClassA.const10.should == :const10_10
+ ConstantSpecs::ParentA.const10.should == :const10_5
+ ConstantSpecs::ContainerA.const10.should == :const10_2
+ ConstantSpecs::ContainerA::ChildA.const10.should == :const10_3
+
+ ConstantSpecs::ClassA.new.const10.should == :const10_10
+ ConstantSpecs::ParentA.new.const10.should == :const10_5
+ ConstantSpecs::ContainerA::ChildA.new.const10.should == :const10_3
+ end
+
+ it "searches a module included in the immediate class before the superclass" do
+ ConstantSpecs::ContainerA::ChildA.const15.should == :const15_1
+ ConstantSpecs::ContainerA::ChildA.new.const15.should == :const15_1
+ end
+
+ it "searches the superclass before a module included in the superclass" do
+ ConstantSpecs::ContainerA::ChildA.const11.should == :const11_1
+ ConstantSpecs::ContainerA::ChildA.new.const11.should == :const11_1
+ end
+
+ it "searches a module included in the superclass" do
+ ConstantSpecs::ContainerA::ChildA.const12.should == :const12_1
+ ConstantSpecs::ContainerA::ChildA.new.const12.should == :const12_1
+ end
+
+ it "searches the superclass chain" do
+ ConstantSpecs::ContainerA::ChildA.const13.should == :const13
+ ConstantSpecs::ContainerA::ChildA.new.const13.should == :const13
+ end
+
+ it "searches the lexical scope of the method not the receiver's immediate class" do
+ ConstantSpecs::ContainerA::ChildA.const19.should == :const19_1
+ end
+
+ it "searches the lexical scope of a singleton method" do
+ ConstantSpecs::CS_CONST18.const17.should == :const17_1
+ end
+
+ it "does not search the lexical scope of the caller" do
+ lambda { ConstantSpecs::ClassA.const16 }.should raise_error(NameError)
+ end
+
+ it "searches the lexical scope of a block" do
+ ConstantSpecs::ClassA.const22.should == :const22_1
+ end
+
+ it "searches Object as a lexical scope only if Object is explicitly opened" do
+ ConstantSpecs::ContainerA::ChildA.const20.should == :const20_1
+ ConstantSpecs::ContainerA::ChildA.const21.should == :const21_1
+ end
+
+ it "does not search the lexical scope of qualifying modules" do
+ lambda do
+ ConstantSpecs::ContainerA::ChildA.const23
+ end.should raise_error(NameError)
+ end
+ end
+
+ describe "with dynamically assigned constants" do
+ it "searches the immediate class or module scope first" do
+ ConstantSpecs::ModuleA::CS_CONST201 = :const201_1
+
+ class ConstantSpecs::ClassB; CS_CONST201 = :const201_2; end
+ ConstantSpecs::ParentB::CS_CONST201 = :const201_3
+ ConstantSpecs::ContainerB::CS_CONST201 = :const201_4
+ ConstantSpecs::ContainerB::ChildB::CS_CONST201 = :const201_5
+
+ ConstantSpecs::ClassB.const201.should == :const201_2
+ ConstantSpecs::ParentB.const201.should == :const201_3
+ ConstantSpecs::ContainerB.const201.should == :const201_4
+ ConstantSpecs::ContainerB::ChildB.const201.should == :const201_5
+
+ ConstantSpecs::ClassB.new.const201.should == :const201_2
+ ConstantSpecs::ParentB.new.const201.should == :const201_3
+ ConstantSpecs::ContainerB::ChildB.new.const201.should == :const201_5
+ end
+
+ it "searches a module included in the immediate class before the superclass" do
+ ConstantSpecs::ParentB::CS_CONST202 = :const202_2
+ ConstantSpecs::ContainerB::ChildB::CS_CONST202 = :const202_1
+
+ ConstantSpecs::ContainerB::ChildB.const202.should == :const202_1
+ ConstantSpecs::ContainerB::ChildB.new.const202.should == :const202_1
+ end
+
+ it "searches the superclass before a module included in the superclass" do
+ ConstantSpecs::ParentB::CS_CONST203 = :const203_1
+ ConstantSpecs::ModuleE::CS_CONST203 = :const203_2
+
+ ConstantSpecs::ContainerB::ChildB.const203.should == :const203_1
+ ConstantSpecs::ContainerB::ChildB.new.const203.should == :const203_1
+ end
+
+ it "searches a module included in the superclass" do
+ ConstantSpecs::ModuleA::CS_CONST204 = :const204_2
+ ConstantSpecs::ModuleE::CS_CONST204 = :const204_1
+
+ ConstantSpecs::ContainerB::ChildB.const204.should == :const204_1
+ ConstantSpecs::ContainerB::ChildB.new.const204.should == :const204_1
+ end
+
+ it "searches the superclass chain" do
+ ConstantSpecs::ModuleA::CS_CONST205 = :const205
+
+ ConstantSpecs::ContainerB::ChildB.const205.should == :const205
+ ConstantSpecs::ContainerB::ChildB.new.const205.should == :const205
+ end
+
+ it "searches the lexical scope of the method not the receiver's immediate class" do
+ ConstantSpecs::ContainerB::ChildB::CS_CONST206 = :const206_2
+ class ConstantSpecs::ContainerB::ChildB
+ class << self
+ CS_CONST206 = :const206_1
+ end
+ end
+
+ ConstantSpecs::ContainerB::ChildB.const206.should == :const206_1
+ end
+
+ it "searches the lexical scope of a singleton method" do
+ ConstantSpecs::CS_CONST207 = :const207_1
+ ConstantSpecs::ClassB::CS_CONST207 = :const207_2
+
+ ConstantSpecs::CS_CONST208.const207.should == :const207_1
+ end
+
+ it "does not search the lexical scope of the caller" do
+ ConstantSpecs::ClassB::CS_CONST209 = :const209
+
+ lambda { ConstantSpecs::ClassB.const209 }.should raise_error(NameError)
+ end
+
+ it "searches the lexical scope of a block" do
+ ConstantSpecs::ClassB::CS_CONST210 = :const210_1
+ ConstantSpecs::ParentB::CS_CONST210 = :const210_2
+
+ ConstantSpecs::ClassB.const210.should == :const210_1
+ end
+
+ it "searches Object as a lexical scope only if Object is explicitly opened" do
+ Object::CS_CONST211 = :const211_1
+ ConstantSpecs::ParentB::CS_CONST211 = :const211_2
+ ConstantSpecs::ContainerB::ChildB.const211.should == :const211_1
+
+ Object::CS_CONST212 = :const212_2
+ ConstantSpecs::ParentB::CS_CONST212 = :const212_1
+ ConstantSpecs::ContainerB::ChildB.const212.should == :const212_1
+ end
+
+ it "returns the updated value when a constant is reassigned" do
+ ConstantSpecs::ParentB::CS_CONST213 = :const213_1
+ ConstantSpecs::ContainerB::ChildB.const213.should == :const213_1
+ ConstantSpecs::ContainerB::ChildB.new.const213.should == :const213_1
+
+ -> {
+ ConstantSpecs::ParentB::CS_CONST213 = :const213_2
+ }.should complain(/already initialized constant/)
+ ConstantSpecs::ContainerB::ChildB.const213.should == :const213_2
+ ConstantSpecs::ContainerB::ChildB.new.const213.should == :const213_2
+ end
+
+ it "does not search the lexical scope of qualifying modules" do
+ ConstantSpecs::ContainerB::CS_CONST214 = :const214
+
+ lambda do
+ ConstantSpecs::ContainerB::ChildB.const214
+ end.should raise_error(NameError)
+ end
+ end
+
+ it "raises a NameError if no constant is defined in the search path" do
+ lambda { ConstantSpecs::ParentA.constx }.should raise_error(NameError)
+ end
+
+ it "sends #const_missing to the original class or module scope" do
+ ConstantSpecs::ClassA.constx.should == :CS_CONSTX
+ ConstantSpecs::ClassA.new.constx.should == :CS_CONSTX
+ end
+
+ describe "with ||=" do
+ it "assigns a scoped constant if previously undefined" do
+ ConstantSpecs.should_not have_constant(:OpAssignUndefined)
+ module ConstantSpecs
+ OpAssignUndefined ||= 42
+ end
+ ConstantSpecs::OpAssignUndefined.should == 42
+ ConstantSpecs::OpAssignUndefinedOutside ||= 42
+ ConstantSpecs::OpAssignUndefinedOutside.should == 42
+ ConstantSpecs.send(:remove_const, :OpAssignUndefined)
+ ConstantSpecs.send(:remove_const, :OpAssignUndefinedOutside)
+ end
+
+ it "assigns a global constant if previously undefined" do
+ OpAssignGlobalUndefined ||= 42
+ ::OpAssignGlobalUndefinedExplicitScope ||= 42
+ OpAssignGlobalUndefined.should == 42
+ ::OpAssignGlobalUndefinedExplicitScope.should == 42
+ Object.send :remove_const, :OpAssignGlobalUndefined
+ Object.send :remove_const, :OpAssignGlobalUndefinedExplicitScope
+ end
+
+ end
+
+ describe "with &&=" do
+ it "re-assigns a scoped constant if already true" do
+ module ConstantSpecs
+ OpAssignTrue = true
+ end
+ suppress_warning do
+ ConstantSpecs::OpAssignTrue &&= 1
+ end
+ ConstantSpecs::OpAssignTrue.should == 1
+ ConstantSpecs.send :remove_const, :OpAssignTrue
+ end
+
+ it "leaves scoped constant if not true" do
+ module ConstantSpecs
+ OpAssignFalse = false
+ end
+ ConstantSpecs::OpAssignFalse &&= 1
+ ConstantSpecs::OpAssignFalse.should == false
+ ConstantSpecs.send :remove_const, :OpAssignFalse
+ end
+ end
+end
+
+describe "Constant resolution within a singleton class (class << obj)" do
+ it "works like normal classes or modules" do
+ ConstantSpecs::CS_SINGLETON1.foo.should == 1
+ end
+
+ it "uses its own namespace for each object" do
+ a = ConstantSpecs::CS_SINGLETON2[0].foo
+ b = ConstantSpecs::CS_SINGLETON2[1].foo
+ [a, b].should == [1, 2]
+ end
+
+ it "uses its own namespace for nested modules" do
+ a = ConstantSpecs::CS_SINGLETON3[0].x
+ b = ConstantSpecs::CS_SINGLETON3[1].x
+ a.should_not equal(b)
+ end
+
+ it "allows nested modules to have proper resolution" do
+ a = ConstantSpecs::CS_SINGLETON4_CLASSES[0].new
+ b = ConstantSpecs::CS_SINGLETON4_CLASSES[1].new
+ [a.foo, b.foo].should == [1, 2]
+ end
+end
+
+describe "top-level constant lookup" do
+ context "on a class" do
+ ruby_version_is "" ... "2.5" do
+ it "searches Object successfully after searching other scopes" do
+ ->() {
+ String::Hash.should == Hash
+ }.should complain(/toplevel constant Hash referenced by/)
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "does not search Object after searching other scopes" do
+ ->() { String::Hash }.should raise_error(NameError)
+ end
+ end
+ end
+
+ it "searches Object unsuccessfully when searches on a module" do
+ ->() { Enumerable::Hash }.should raise_error(NameError)
+ end
+end
+
+describe "Module#private_constant marked constants" do
+
+ it "remain private even when updated" do
+ mod = Module.new
+ mod.const_set :Foo, true
+ mod.send :private_constant, :Foo
+ -> {
+ mod.const_set :Foo, false
+ }.should complain(/already initialized constant/)
+
+ lambda {mod::Foo}.should raise_error(NameError)
+ end
+
+ ruby_version_is "2.6" do
+ it "sends #const_missing to the original class or module" do
+ mod = Module.new
+ mod.const_set :Foo, true
+ mod.send :private_constant, :Foo
+ def mod.const_missing(name)
+ name == :Foo ? name : super
+ end
+
+ mod::Foo.should == :Foo
+ end
+ end
+
+ describe "in a module" do
+ it "cannot be accessed from outside the module" do
+ lambda do
+ ConstantVisibility::PrivConstModule::PRIVATE_CONSTANT_MODULE
+ end.should raise_error(NameError)
+ end
+
+ it "cannot be reopened as a module from scope where constant would be private" do
+ lambda do
+ module ConstantVisibility::ModuleContainer::PrivateModule; end
+ end.should raise_error(NameError)
+ end
+
+ it "cannot be reopened as a class from scope where constant would be private" do
+ lambda do
+ class ConstantVisibility::ModuleContainer::PrivateClass; end
+ end.should raise_error(NameError)
+ end
+
+ it "can be reopened as a module where constant is not private" do
+ module ::ConstantVisibility::ModuleContainer
+ module PrivateModule
+ X = 1
+ end
+
+ PrivateModule::X.should == 1
+ end
+ end
+
+ it "can be reopened as a class where constant is not private" do
+ module ::ConstantVisibility::ModuleContainer
+ class PrivateClass
+ X = 1
+ end
+
+ PrivateClass::X.should == 1
+ end
+ end
+
+ it "is not defined? with A::B form" do
+ defined?(ConstantVisibility::PrivConstModule::PRIVATE_CONSTANT_MODULE).should == nil
+ end
+
+ it "can be accessed from the module itself" do
+ ConstantVisibility::PrivConstModule.private_constant_from_self.should be_true
+ end
+
+ it "is defined? from the module itself" do
+ ConstantVisibility::PrivConstModule.defined_from_self.should == "constant"
+ end
+
+ it "can be accessed from lexical scope" do
+ ConstantVisibility::PrivConstModule::Nested.private_constant_from_scope.should be_true
+ end
+
+ it "is defined? from lexical scope" do
+ ConstantVisibility::PrivConstModule::Nested.defined_from_scope.should == "constant"
+ end
+
+ it "can be accessed from classes that include the module" do
+ ConstantVisibility::PrivConstModuleChild.new.private_constant_from_include.should be_true
+ end
+
+ it "is defined? from classes that include the module" do
+ ConstantVisibility::PrivConstModuleChild.new.defined_from_include.should == "constant"
+ end
+ end
+
+ describe "in a class" do
+ it "cannot be accessed from outside the class" do
+ lambda do
+ ConstantVisibility::PrivConstClass::PRIVATE_CONSTANT_CLASS
+ end.should raise_error(NameError)
+ end
+
+ it "cannot be reopened as a module" do
+ lambda do
+ module ConstantVisibility::ClassContainer::PrivateModule; end
+ end.should raise_error(NameError)
+ end
+
+ it "cannot be reopened as a class" do
+ lambda do
+ class ConstantVisibility::ClassContainer::PrivateClass; end
+ end.should raise_error(NameError)
+ end
+
+ it "can be reopened as a module where constant is not private" do
+ class ::ConstantVisibility::ClassContainer
+ module PrivateModule
+ X = 1
+ end
+
+ PrivateModule::X.should == 1
+ end
+ end
+
+ it "can be reopened as a class where constant is not private" do
+ class ::ConstantVisibility::ClassContainer
+ class PrivateClass
+ X = 1
+ end
+
+ PrivateClass::X.should == 1
+ end
+ end
+
+ it "is not defined? with A::B form" do
+ defined?(ConstantVisibility::PrivConstClass::PRIVATE_CONSTANT_CLASS).should == nil
+ end
+
+ it "can be accessed from the class itself" do
+ ConstantVisibility::PrivConstClass.private_constant_from_self.should be_true
+ end
+
+ it "is defined? from the class itself" do
+ ConstantVisibility::PrivConstClass.defined_from_self.should == "constant"
+ end
+
+ it "can be accessed from lexical scope" do
+ ConstantVisibility::PrivConstClass::Nested.private_constant_from_scope.should be_true
+ end
+
+ it "is defined? from lexical scope" do
+ ConstantVisibility::PrivConstClass::Nested.defined_from_scope.should == "constant"
+ end
+
+ it "can be accessed from subclasses" do
+ ConstantVisibility::PrivConstClassChild.new.private_constant_from_subclass.should be_true
+ end
+
+ it "is defined? from subclasses" do
+ ConstantVisibility::PrivConstClassChild.new.defined_from_subclass.should == "constant"
+ end
+ end
+
+ describe "in Object" do
+ it "cannot be accessed using ::Const form" do
+ lambda do
+ ::PRIVATE_CONSTANT_IN_OBJECT
+ end.should raise_error(NameError)
+ end
+
+ it "is not defined? using ::Const form" do
+ defined?(::PRIVATE_CONSTANT_IN_OBJECT).should == nil
+ end
+
+ it "can be accessed through the normal search" do
+ PRIVATE_CONSTANT_IN_OBJECT.should == true
+ end
+
+ it "is defined? through the normal search" do
+ defined?(PRIVATE_CONSTANT_IN_OBJECT).should == "constant"
+ end
+ end
+
+ describe "NameError by #private_constant" do
+ it "has :receiver and :name attributes" do
+ lambda do
+ ConstantVisibility::PrivConstClass::PRIVATE_CONSTANT_CLASS
+ end.should raise_error(NameError) {|e|
+ e.receiver.should == ConstantVisibility::PrivConstClass
+ e.name.should == :PRIVATE_CONSTANT_CLASS
+ }
+
+ lambda do
+ ConstantVisibility::PrivConstModule::PRIVATE_CONSTANT_MODULE
+ end.should raise_error(NameError) {|e|
+ e.receiver.should == ConstantVisibility::PrivConstModule
+ e.name.should == :PRIVATE_CONSTANT_MODULE
+ }
+ end
+
+ it "has the defined class as the :name attribute" do
+ lambda do
+ ConstantVisibility::PrivConstClassChild::PRIVATE_CONSTANT_CLASS
+ end.should raise_error(NameError) {|e|
+ e.receiver.should == ConstantVisibility::PrivConstClass
+ e.name.should == :PRIVATE_CONSTANT_CLASS
+ }
+
+ lambda do
+ ConstantVisibility::PrivConstModuleChild::PRIVATE_CONSTANT_MODULE
+ end.should raise_error(NameError) {|e|
+ ruby_bug "#14853", ""..."2.5.2" do
+ e.receiver.should == ConstantVisibility::PrivConstModule
+ end
+ e.name.should == :PRIVATE_CONSTANT_MODULE
+ }
+ end
+ end
+end
+
+describe "Module#public_constant marked constants" do
+ before :each do
+ @module = ConstantVisibility::PrivConstModule.dup
+ end
+
+ describe "in a module" do
+ it "can be accessed from outside the module" do
+ @module.send :public_constant, :PRIVATE_CONSTANT_MODULE
+ @module::PRIVATE_CONSTANT_MODULE.should == true
+ end
+
+ it "is defined? with A::B form" do
+ @module.send :public_constant, :PRIVATE_CONSTANT_MODULE
+ defined?(@module::PRIVATE_CONSTANT_MODULE).should == "constant"
+ end
+ end
+
+ describe "in a class" do
+ before :each do
+ @class = ConstantVisibility::PrivConstClass.dup
+ end
+
+ it "can be accessed from outside the class" do
+ @class.send :public_constant, :PRIVATE_CONSTANT_CLASS
+ @class::PRIVATE_CONSTANT_CLASS.should == true
+ end
+
+ it "is defined? with A::B form" do
+ @class.send :public_constant, :PRIVATE_CONSTANT_CLASS
+ defined?(@class::PRIVATE_CONSTANT_CLASS).should == "constant"
+ end
+ end
+
+ describe "in Object" do
+ after :each do
+ ConstantVisibility.reset_private_constants
+ end
+
+ it "can be accessed using ::Const form" do
+ Object.send :public_constant, :PRIVATE_CONSTANT_IN_OBJECT
+ ::PRIVATE_CONSTANT_IN_OBJECT.should == true
+ end
+
+ it "is defined? using ::Const form" do
+ Object.send :public_constant, :PRIVATE_CONSTANT_IN_OBJECT
+ defined?(::PRIVATE_CONSTANT_IN_OBJECT).should == "constant"
+ end
+ end
+end
diff --git a/spec/ruby/language/def_spec.rb b/spec/ruby/language/def_spec.rb
new file mode 100644
index 0000000000..dee60c9adf
--- /dev/null
+++ b/spec/ruby/language/def_spec.rb
@@ -0,0 +1,761 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/def'
+
+# Language-level method behaviour
+describe "Redefining a method" do
+ it "replaces the original method" do
+ def barfoo; 100; end
+ barfoo.should == 100
+
+ def barfoo; 200; end
+ barfoo.should == 200
+ end
+end
+
+describe "Defining a method at the top-level" do
+ it "defines it on Object with private visibility by default" do
+ Object.should have_private_instance_method(:some_toplevel_method, false)
+ end
+
+ it "defines it on Object with public visibility after calling public" do
+ Object.should have_public_instance_method(:public_toplevel_method, false)
+ end
+end
+
+describe "Defining an 'initialize' method" do
+ it "sets the method's visibility to private" do
+ class DefInitializeSpec
+ def initialize
+ end
+ end
+ DefInitializeSpec.should have_private_instance_method(:initialize, false)
+ end
+end
+
+describe "Defining an 'initialize_copy' method" do
+ it "sets the method's visibility to private" do
+ class DefInitializeCopySpec
+ def initialize_copy
+ end
+ end
+ DefInitializeCopySpec.should have_private_instance_method(:initialize_copy, false)
+ end
+end
+
+describe "Defining an 'initialize_dup' method" do
+ it "sets the method's visibility to private" do
+ class DefInitializeDupSpec
+ def initialize_dup
+ end
+ end
+ DefInitializeDupSpec.should have_private_instance_method(:initialize_dup, false)
+ end
+end
+
+describe "Defining an 'initialize_clone' method" do
+ it "sets the method's visibility to private" do
+ class DefInitializeCloneSpec
+ def initialize_clone
+ end
+ end
+ DefInitializeCloneSpec.should have_private_instance_method(:initialize_clone, false)
+ end
+end
+
+describe "Defining a 'respond_to_missing?' method" do
+ it "sets the method's visibility to private" do
+ class DefRespondToMissingPSpec
+ def respond_to_missing?
+ end
+ end
+ DefRespondToMissingPSpec.should have_private_instance_method(:respond_to_missing?, false)
+ end
+end
+
+describe "Defining a method" do
+ it "returns a symbol of the method name" do
+ method_name = def some_method; end
+ method_name.should == :some_method
+ end
+end
+
+describe "An instance method" do
+ it "raises an error with too few arguments" do
+ def foo(a, b); end
+ lambda { foo 1 }.should raise_error(ArgumentError, 'wrong number of arguments (given 1, expected 2)')
+ end
+
+ it "raises an error with too many arguments" do
+ def foo(a); end
+ lambda { foo 1, 2 }.should raise_error(ArgumentError, 'wrong number of arguments (given 2, expected 1)')
+ end
+end
+
+describe "An instance method definition with a splat" do
+ it "accepts an unnamed '*' argument" do
+ def foo(*); end;
+
+ foo.should == nil
+ foo(1, 2).should == nil
+ foo(1, 2, 3, 4, :a, :b, 'c', 'd').should == nil
+ end
+
+ it "accepts a named * argument" do
+ def foo(*a); a; end;
+ foo.should == []
+ foo(1, 2).should == [1, 2]
+ foo([:a]).should == [[:a]]
+ end
+
+ it "accepts non-* arguments before the * argument" do
+ def foo(a, b, c, d, e, *f); [a, b, c, d, e, f]; end
+ foo(1, 2, 3, 4, 5, 6, 7, 8).should == [1, 2, 3, 4, 5, [6, 7, 8]]
+ end
+
+ it "allows only a single * argument" do
+ lambda { eval 'def foo(a, *b, *c); end' }.should raise_error(SyntaxError)
+ end
+
+ it "requires the presence of any arguments that precede the *" do
+ def foo(a, b, *c); end
+ lambda { foo 1 }.should raise_error(ArgumentError, 'wrong number of arguments (given 1, expected 2+)')
+ end
+end
+
+describe "An instance method with a default argument" do
+ it "evaluates the default when no arguments are passed" do
+ def foo(a = 1)
+ a
+ end
+ foo.should == 1
+ foo(2).should == 2
+ end
+
+ it "evaluates the default empty expression when no arguments are passed" do
+ def foo(a = ())
+ a
+ end
+ foo.should == nil
+ foo(2).should == 2
+ end
+
+ it "assigns an empty Array to an unused splat argument" do
+ def foo(a = 1, *b)
+ [a,b]
+ end
+ foo.should == [1, []]
+ foo(2).should == [2, []]
+ end
+
+ it "evaluates the default when required arguments precede it" do
+ def foo(a, b = 2)
+ [a,b]
+ end
+ lambda { foo }.should raise_error(ArgumentError, 'wrong number of arguments (given 0, expected 1..2)')
+ foo(1).should == [1, 2]
+ end
+
+ it "prefers to assign to a default argument before a splat argument" do
+ def foo(a, b = 2, *c)
+ [a,b,c]
+ end
+ lambda { foo }.should raise_error(ArgumentError, 'wrong number of arguments (given 0, expected 1+)')
+ foo(1).should == [1,2,[]]
+ end
+
+ it "prefers to assign to a default argument when there are no required arguments" do
+ def foo(a = 1, *args)
+ [a,args]
+ end
+ foo(2,2).should == [2,[2]]
+ end
+
+ it "does not evaluate the default when passed a value and a * argument" do
+ def foo(a, b = 2, *args)
+ [a,b,args]
+ end
+ foo(2,3,3).should == [2,3,[3]]
+ end
+
+ it "shadows an existing method with the same name as the local" do
+ def bar
+ 1
+ end
+ -> {
+ eval "def foo(bar = bar)
+ bar
+ end"
+ }.should complain(/circular argument reference/)
+ foo.should == nil
+ foo(2).should == 2
+ end
+
+ it "calls a method with the same name as the local when explicitly using ()" do
+ def bar
+ 1
+ end
+ def foo(bar = bar())
+ bar
+ end
+ foo.should == 1
+ foo(2).should == 2
+ end
+end
+
+describe "A singleton method definition" do
+ it "can be declared for a local variable" do
+ a = Object.new
+ def a.foo
+ 5
+ end
+ a.foo.should == 5
+ end
+
+ it "can be declared for an instance variable" do
+ @a = Object.new
+ def @a.foo
+ 6
+ end
+ @a.foo.should == 6
+ end
+
+ it "can be declared for a global variable" do
+ $__a__ = "hi"
+ def $__a__.foo
+ 7
+ end
+ $__a__.foo.should == 7
+ end
+
+ it "can be declared with an empty method body" do
+ class DefSpec
+ def self.foo;end
+ end
+ DefSpec.foo.should == nil
+ end
+
+ it "can be redefined" do
+ obj = Object.new
+ def obj.==(other)
+ 1
+ end
+ (obj==1).should == 1
+ def obj.==(other)
+ 2
+ end
+ (obj==2).should == 2
+ end
+
+ it "raises #{frozen_error_class} if frozen" do
+ obj = Object.new
+ obj.freeze
+ lambda { def obj.foo; end }.should raise_error(frozen_error_class)
+ end
+end
+
+describe "Redefining a singleton method" do
+ it "does not inherit a previously set visibility" do
+ o = Object.new
+
+ class << o; private; def foo; end; end;
+
+ class << o; should have_private_instance_method(:foo); end
+
+ class << o; def foo; end; end;
+
+ class << o; should_not have_private_instance_method(:foo); end
+ class << o; should have_instance_method(:foo); end
+
+ end
+end
+
+describe "Redefining a singleton method" do
+ it "does not inherit a previously set visibility" do
+ o = Object.new
+
+ class << o; private; def foo; end; end;
+
+ class << o; should have_private_instance_method(:foo); end
+
+ class << o; def foo; end; end;
+
+ class << o; should_not have_private_instance_method(:foo); end
+ class << o; should have_instance_method(:foo); end
+
+ end
+end
+
+describe "A method defined with extreme default arguments" do
+ it "can redefine itself when the default is evaluated" do
+ class DefSpecs
+ def foo(x = (def foo; "hello"; end;1));x;end
+ end
+
+ d = DefSpecs.new
+ d.foo(42).should == 42
+ d.foo.should == 1
+ d.foo.should == 'hello'
+ end
+
+ it "may use an fcall as a default" do
+ def bar
+ 1
+ end
+ def foo(x = bar())
+ x
+ end
+ foo.should == 1
+ foo(2).should == 2
+ end
+
+ it "evaluates the defaults in the method's scope" do
+ def foo(x = ($foo_self = self; nil)); end
+ foo
+ $foo_self.should == self
+ end
+
+ it "may use preceding arguments as defaults" do
+ def foo(obj, width=obj.length)
+ width
+ end
+ foo('abcde').should == 5
+ end
+
+ it "may use a lambda as a default" do
+ def foo(output = 'a', prc = lambda {|n| output * n})
+ prc.call(5)
+ end
+ foo.should == 'aaaaa'
+ end
+end
+
+describe "A singleton method defined with extreme default arguments" do
+ it "may use a method definition as a default" do
+ $__a = Object.new
+ def $__a.foo(x = (def $__a.foo; "hello"; end;1));x;end
+
+ $__a.foo(42).should == 42
+ $__a.foo.should == 1
+ $__a.foo.should == 'hello'
+ end
+
+ it "may use an fcall as a default" do
+ a = Object.new
+ def a.bar
+ 1
+ end
+ def a.foo(x = bar())
+ x
+ end
+ a.foo.should == 1
+ a.foo(2).should == 2
+ end
+
+ it "evaluates the defaults in the singleton scope" do
+ a = Object.new
+ def a.foo(x = ($foo_self = self; nil)); 5 ;end
+ a.foo
+ $foo_self.should == a
+ end
+
+ it "may use preceding arguments as defaults" do
+ a = Object.new
+ def a.foo(obj, width=obj.length)
+ width
+ end
+ a.foo('abcde').should == 5
+ end
+
+ it "may use a lambda as a default" do
+ a = Object.new
+ def a.foo(output = 'a', prc = lambda {|n| output * n})
+ prc.call(5)
+ end
+ a.foo.should == 'aaaaa'
+ end
+end
+
+describe "A method definition inside a metaclass scope" do
+ it "can create a class method" do
+ class DefSpecSingleton
+ class << self
+ def a_class_method;self;end
+ end
+ end
+
+ DefSpecSingleton.a_class_method.should == DefSpecSingleton
+ lambda { Object.a_class_method }.should raise_error(NoMethodError)
+ end
+
+ it "can create a singleton method" do
+ obj = Object.new
+ class << obj
+ def a_singleton_method;self;end
+ end
+
+ obj.a_singleton_method.should == obj
+ lambda { Object.new.a_singleton_method }.should raise_error(NoMethodError)
+ end
+
+ it "raises #{frozen_error_class} if frozen" do
+ obj = Object.new
+ obj.freeze
+
+ class << obj
+ lambda { def foo; end }.should raise_error(frozen_error_class)
+ end
+ end
+end
+
+describe "A nested method definition" do
+ it "creates an instance method when evaluated in an instance method" do
+ class DefSpecNested
+ def create_instance_method
+ def an_instance_method;self;end
+ an_instance_method
+ end
+ end
+
+ obj = DefSpecNested.new
+ obj.create_instance_method.should == obj
+ obj.an_instance_method.should == obj
+
+ other = DefSpecNested.new
+ other.an_instance_method.should == other
+
+ DefSpecNested.should have_instance_method(:an_instance_method)
+ end
+
+ it "creates a class method when evaluated in a class method" do
+ class DefSpecNested
+ class << self
+ # cleanup
+ remove_method :a_class_method if method_defined? :a_class_method
+ def create_class_method
+ def a_class_method;self;end
+ a_class_method
+ end
+ end
+ end
+
+ lambda { DefSpecNested.a_class_method }.should raise_error(NoMethodError)
+ DefSpecNested.create_class_method.should == DefSpecNested
+ DefSpecNested.a_class_method.should == DefSpecNested
+ lambda { Object.a_class_method }.should raise_error(NoMethodError)
+ lambda { DefSpecNested.new.a_class_method }.should raise_error(NoMethodError)
+ end
+
+ it "creates a singleton method when evaluated in the metaclass of an instance" do
+ class DefSpecNested
+ def create_singleton_method
+ class << self
+ def a_singleton_method;self;end
+ end
+ a_singleton_method
+ end
+ end
+
+ obj = DefSpecNested.new
+ obj.create_singleton_method.should == obj
+ obj.a_singleton_method.should == obj
+
+ other = DefSpecNested.new
+ lambda { other.a_singleton_method }.should raise_error(NoMethodError)
+ end
+
+ it "creates a method in the surrounding context when evaluated in a def expr.method" do
+ class DefSpecNested
+ TARGET = Object.new
+ def TARGET.defs_method
+ def inherited_method;self;end
+ end
+ end
+
+ DefSpecNested::TARGET.defs_method
+ DefSpecNested.should have_instance_method :inherited_method
+ DefSpecNested::TARGET.should_not have_method :inherited_method
+
+ obj = DefSpecNested.new
+ obj.inherited_method.should == obj
+ end
+
+ # See http://yugui.jp/articles/846#label-3
+ it "inside an instance_eval creates a singleton method" do
+ class DefSpecNested
+ OBJ = Object.new
+ OBJ.instance_eval do
+ def create_method_in_instance_eval(a = (def arg_method; end))
+ def body_method; end
+ end
+ end
+ end
+
+ obj = DefSpecNested::OBJ
+ obj.create_method_in_instance_eval
+
+ obj.should have_method :arg_method
+ obj.should have_method :body_method
+
+ DefSpecNested.should_not have_instance_method :arg_method
+ DefSpecNested.should_not have_instance_method :body_method
+ end
+
+ it "creates an instance method inside Class.new" do
+ cls = Class.new do
+ def do_def
+ def new_def
+ 1
+ end
+ end
+ end
+
+ obj = cls.new
+ obj.do_def
+ obj.new_def.should == 1
+
+ cls.new.new_def.should == 1
+
+ -> { Object.new.new_def }.should raise_error(NoMethodError)
+ end
+end
+
+describe "A method definition always resets the visibility to public for nested definitions" do
+ it "in Class.new" do
+ cls = Class.new do
+ private
+ def do_def
+ def new_def
+ 1
+ end
+ end
+ end
+
+ obj = cls.new
+ -> { obj.do_def }.should raise_error(NoMethodError, /private/)
+ obj.send :do_def
+ obj.new_def.should == 1
+
+ cls.new.new_def.should == 1
+
+ -> { Object.new.new_def }.should raise_error(NoMethodError)
+ end
+
+ it "at the toplevel" do
+ obj = Object.new
+ -> { obj.toplevel_define_other_method }.should raise_error(NoMethodError, /private/)
+ toplevel_define_other_method
+ nested_method_in_toplevel_method.should == 42
+
+ Object.new.nested_method_in_toplevel_method.should == 42
+ end
+end
+
+describe "A method definition inside an instance_eval" do
+ it "creates a singleton method" do
+ obj = Object.new
+ obj.instance_eval do
+ def an_instance_eval_method;self;end
+ end
+ obj.an_instance_eval_method.should == obj
+
+ other = Object.new
+ lambda { other.an_instance_eval_method }.should raise_error(NoMethodError)
+ end
+
+ it "creates a singleton method when evaluated inside a metaclass" do
+ obj = Object.new
+ obj.instance_eval do
+ class << self
+ def a_metaclass_eval_method;self;end
+ end
+ end
+ obj.a_metaclass_eval_method.should == obj
+
+ other = Object.new
+ lambda { other.a_metaclass_eval_method }.should raise_error(NoMethodError)
+ end
+
+ it "creates a class method when the receiver is a class" do
+ DefSpecNested.instance_eval do
+ def an_instance_eval_class_method;self;end
+ end
+
+ DefSpecNested.an_instance_eval_class_method.should == DefSpecNested
+ lambda { Object.an_instance_eval_class_method }.should raise_error(NoMethodError)
+ end
+
+ it "creates a class method when the receiver is an anonymous class" do
+ m = Class.new
+ m.instance_eval do
+ def klass_method
+ :test
+ end
+ end
+
+ m.klass_method.should == :test
+ lambda { Object.klass_method }.should raise_error(NoMethodError)
+ end
+
+ it "creates a class method when instance_eval is within class" do
+ m = Class.new do
+ instance_eval do
+ def klass_method
+ :test
+ end
+ end
+ end
+
+ m.klass_method.should == :test
+ lambda { Object.klass_method }.should raise_error(NoMethodError)
+ end
+end
+
+describe "A method definition inside an instance_exec" do
+ it "creates a class method when the receiver is a class" do
+ DefSpecNested.instance_exec(1) do |param|
+ @stuff = param
+
+ def an_instance_exec_class_method; @stuff; end
+ end
+
+ DefSpecNested.an_instance_exec_class_method.should == 1
+ lambda { Object.an_instance_exec_class_method }.should raise_error(NoMethodError)
+ end
+
+ it "creates a class method when the receiver is an anonymous class" do
+ m = Class.new
+ m.instance_exec(1) do |param|
+ @stuff = param
+
+ def klass_method
+ @stuff
+ end
+ end
+
+ m.klass_method.should == 1
+ lambda { Object.klass_method }.should raise_error(NoMethodError)
+ end
+
+ it "creates a class method when instance_exec is within class" do
+ m = Class.new do
+ instance_exec(2) do |param|
+ @stuff = param
+
+ def klass_method
+ @stuff
+ end
+ end
+ end
+
+ m.klass_method.should == 2
+ lambda { Object.klass_method }.should raise_error(NoMethodError)
+ end
+end
+
+describe "A method definition in an eval" do
+ it "creates an instance method" do
+ class DefSpecNested
+ def eval_instance_method
+ eval "def an_eval_instance_method;self;end", binding
+ an_eval_instance_method
+ end
+ end
+
+ obj = DefSpecNested.new
+ obj.eval_instance_method.should == obj
+ obj.an_eval_instance_method.should == obj
+
+ other = DefSpecNested.new
+ other.an_eval_instance_method.should == other
+
+ lambda { Object.new.an_eval_instance_method }.should raise_error(NoMethodError)
+ end
+
+ it "creates a class method" do
+ class DefSpecNestedB
+ class << self
+ def eval_class_method
+ eval "def an_eval_class_method;self;end" #, binding
+ an_eval_class_method
+ end
+ end
+ end
+
+ DefSpecNestedB.eval_class_method.should == DefSpecNestedB
+ DefSpecNestedB.an_eval_class_method.should == DefSpecNestedB
+
+ lambda { Object.an_eval_class_method }.should raise_error(NoMethodError)
+ lambda { DefSpecNestedB.new.an_eval_class_method}.should raise_error(NoMethodError)
+ end
+
+ it "creates a singleton method" do
+ class DefSpecNested
+ def eval_singleton_method
+ class << self
+ eval "def an_eval_singleton_method;self;end", binding
+ end
+ an_eval_singleton_method
+ end
+ end
+
+ obj = DefSpecNested.new
+ obj.eval_singleton_method.should == obj
+ obj.an_eval_singleton_method.should == obj
+
+ other = DefSpecNested.new
+ lambda { other.an_eval_singleton_method }.should raise_error(NoMethodError)
+ end
+end
+
+describe "a method definition that sets more than one default parameter all to the same value" do
+ def foo(a=b=c={})
+ [a,b,c]
+ end
+ it "assigns them all the same object by default" do
+ foo.should == [{},{},{}]
+ a, b, c = foo
+ a.should eql(b)
+ a.should eql(c)
+ end
+
+ it "allows the first argument to be given, and sets the rest to null" do
+ foo(1).should == [1,nil,nil]
+ end
+
+ it "assigns the parameters different objects across different default calls" do
+ a, _b, _c = foo
+ d, _e, _f = foo
+ a.should_not equal(d)
+ end
+
+ it "only allows overriding the default value of the first such parameter in each set" do
+ lambda { foo(1,2) }.should raise_error(ArgumentError, 'wrong number of arguments (given 2, expected 0..1)')
+ end
+
+ def bar(a=b=c=1,d=2)
+ [a,b,c,d]
+ end
+
+ it "treats the argument after the multi-parameter normally" do
+ bar.should == [1,1,1,2]
+ bar(3).should == [3,nil,nil,2]
+ bar(3,4).should == [3,nil,nil,4]
+ lambda { bar(3,4,5) }.should raise_error(ArgumentError, 'wrong number of arguments (given 3, expected 0..2)')
+ end
+end
+
+describe "The def keyword" do
+ describe "within a closure" do
+ it "looks outside the closure for the visibility" do
+ module DefSpecsLambdaVisibility
+ private
+
+ lambda {
+ def some_method; end
+ }.call
+ end
+
+ DefSpecsLambdaVisibility.should have_private_instance_method("some_method")
+ end
+ end
+end
diff --git a/spec/ruby/language/defined_spec.rb b/spec/ruby/language/defined_spec.rb
new file mode 100644
index 0000000000..6616758011
--- /dev/null
+++ b/spec/ruby/language/defined_spec.rb
@@ -0,0 +1,1132 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/defined'
+
+describe "The defined? keyword for literals" do
+ it "returns 'self' for self" do
+ ret = defined?(self)
+ ret.should == "self"
+ end
+
+ it "returns 'nil' for nil" do
+ ret = defined?(nil)
+ ret.should == "nil"
+ end
+
+ it "returns 'true' for true" do
+ ret = defined?(true)
+ ret.should == "true"
+ end
+
+ it "returns 'false' for false" do
+ ret = defined?(false)
+ ret.should == "false"
+ end
+
+ describe "for a literal Array" do
+
+ it "returns 'expression' if each element is defined" do
+ ret = defined?([Object, Array])
+ ret.should == "expression"
+ end
+
+ it "returns nil if one element is not defined" do
+ ret = defined?([NonExistantConstant, Array])
+ ret.should == nil
+ end
+
+ it "returns nil if all elements are not defined" do
+ ret = defined?([NonExistantConstant, AnotherNonExistantConstant])
+ ret.should == nil
+ end
+
+ end
+end
+
+describe "The defined? keyword when called with a method name" do
+ describe "without a receiver" do
+ it "returns 'method' if the method is defined" do
+ defined?(puts).should == "method"
+ end
+
+ it "returns nil if the method is not defined" do
+ defined?(defined_specs_undefined_method).should be_nil
+ end
+
+ it "returns 'method' if the method is defined and private" do
+ obj = DefinedSpecs::Basic.new
+ obj.private_method_defined.should == "method"
+ end
+
+ it "returns 'method' if the predicate method is defined and private" do
+ obj = DefinedSpecs::Basic.new
+ obj.private_predicate_defined.should == "method"
+ end
+ end
+
+ describe "having a module as receiver" do
+ it "returns 'method' if the method is defined" do
+ defined?(Kernel.puts).should == "method"
+ end
+
+ it "returns nil if the method is private" do
+ defined?(Object.print).should be_nil
+ end
+
+ it "returns nil if the method is protected" do
+ defined?(DefinedSpecs::Basic.new.protected_method).should be_nil
+ end
+
+ it "returns nil if the method is not defined" do
+ defined?(Kernel.defined_specs_undefined_method).should be_nil
+ end
+
+ it "returns nil if the class is not defined" do
+ defined?(DefinedSpecsUndefined.puts).should be_nil
+ end
+
+ it "returns nil if the subclass is not defined" do
+ defined?(DefinedSpecs::Undefined.puts).should be_nil
+ end
+ end
+
+ describe "having a local variable as receiver" do
+ it "returns 'method' if the method is defined" do
+ obj = DefinedSpecs::Basic.new
+ defined?(obj.a_defined_method).should == "method"
+ end
+
+ it "returns nil if the method is not defined" do
+ obj = DefinedSpecs::Basic.new
+ defined?(obj.an_undefined_method).should be_nil
+ end
+
+ it "returns nil if the variable does not exist" do
+ defined?(nonexistent_local_variable.some_method).should be_nil
+ end
+
+ it "calls #respond_to_missing?" do
+ obj = mock("respond_to_missing object")
+ obj.should_receive(:respond_to_missing?).and_return(true)
+ defined?(obj.something_undefined).should == "method"
+ end
+ end
+
+ describe "having an instance variable as receiver" do
+ it "returns 'method' if the method is defined" do
+ @defined_specs_obj = DefinedSpecs::Basic.new
+ defined?(@defined_specs_obj.a_defined_method).should == "method"
+ end
+
+ it "returns nil if the method is not defined" do
+ @defined_specs_obj = DefinedSpecs::Basic.new
+ defined?(@defined_specs_obj.an_undefined_method).should be_nil
+ end
+
+ it "returns nil if the variable does not exist" do
+ defined?(@nonexistent_instance_variable.some_method).should be_nil
+ end
+ end
+
+ describe "having a global variable as receiver" do
+ it "returns 'method' if the method is defined" do
+ $defined_specs_obj = DefinedSpecs::Basic.new
+ defined?($defined_specs_obj.a_defined_method).should == "method"
+ end
+
+ it "returns nil if the method is not defined" do
+ $defined_specs_obj = DefinedSpecs::Basic.new
+ defined?($defined_specs_obj.an_undefined_method).should be_nil
+ end
+
+ it "returns nil if the variable does not exist" do
+ defined?($nonexistent_global_variable.some_method).should be_nil
+ end
+ end
+
+ describe "having a method call as a receiver" do
+ it "returns nil if evaluating the receiver raises an exception" do
+ defined?(DefinedSpecs.exception_method / 2).should be_nil
+ ScratchPad.recorded.should == :defined_specs_exception
+ end
+
+ it "returns nil if the method is not defined on the object the receiver returns" do
+ defined?(DefinedSpecs.side_effects / 2).should be_nil
+ ScratchPad.recorded.should == :defined_specs_side_effects
+ end
+
+ it "returns 'method' if the method is defined on the object the receiver returns" do
+ defined?(DefinedSpecs.fixnum_method / 2).should == "method"
+ ScratchPad.recorded.should == :defined_specs_fixnum_method
+ end
+ end
+end
+
+describe "The defined? keyword for an expression" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "returns 'assignment' for assigning a local variable" do
+ defined?(x = 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for assigning an instance variable" do
+ defined?(@defined_specs_x = 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for assigning a global variable" do
+ defined?($defined_specs_x = 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for assigning a class variable" do
+ defined?(@@defined_specs_x = 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for assigning multiple variables" do
+ defined?((a, b = 1, 2)).should == "assignment"
+ end
+
+ it "returns 'assignment' for an expression with '%='" do
+ defined?(x %= 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for an expression with '/='" do
+ defined?(x /= 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for an expression with '-='" do
+ defined?(x -= 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for an expression with '+='" do
+ defined?(x += 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for an expression with '*='" do
+ defined?(x *= 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for an expression with '|='" do
+ defined?(x |= 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for an expression with '&='" do
+ defined?(x &= 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for an expression with '^='" do
+ defined?(x ^= 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for an expression with '~='" do
+ defined?(x = 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for an expression with '<<='" do
+ defined?(x <<= 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for an expression with '>>='" do
+ defined?(x >>= 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for an expression with '||='" do
+ defined?(x ||= 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for an expression with '&&='" do
+ defined?(x &&= 2).should == "assignment"
+ end
+
+ it "returns 'assignment' for an expression with '**='" do
+ defined?(x **= 2).should == "assignment"
+ end
+
+ it "returns nil for an expression with == and an undefined method" do
+ defined?(defined_specs_undefined_method == 2).should be_nil
+ end
+
+ it "returns nil for an expression with != and an undefined method" do
+ defined?(defined_specs_undefined_method != 2).should be_nil
+ end
+
+ it "returns nil for an expression with !~ and an undefined method" do
+ defined?(defined_specs_undefined_method !~ 2).should be_nil
+ end
+
+ it "returns 'method' for an expression with '=='" do
+ x = 42
+ defined?(x == 2).should == "method"
+ end
+
+ it "returns 'method' for an expression with '!='" do
+ x = 42
+ defined?(x != 2).should == "method"
+ end
+
+ it "returns 'method' for an expression with '!~'" do
+ x = 42
+ defined?(x !~ 2).should == "method"
+ end
+
+ describe "with logical connectives" do
+ it "returns nil for an expression with '!' and an undefined method" do
+ defined?(!defined_specs_undefined_method).should be_nil
+ end
+
+ it "returns nil for an expression with '!' and an unset class variable" do
+ -> {
+ @result = defined?(!@@defined_specs_undefined_class_variable)
+ }.should complain(/class variable access from toplevel/)
+ @result.should be_nil
+ end
+
+ it "returns nil for an expression with 'not' and an undefined method" do
+ defined?(not defined_specs_undefined_method).should be_nil
+ end
+
+ it "returns nil for an expression with 'not' and an unset class variable" do
+ -> {
+ @result = defined?(not @@defined_specs_undefined_class_variable)
+ }.should complain(/class variable access from toplevel/)
+ @result.should be_nil
+ end
+
+ it "does not propagate an exception raised by a method in a 'not' expression" do
+ defined?(not DefinedSpecs.exception_method).should be_nil
+ ScratchPad.recorded.should == :defined_specs_exception
+ end
+
+ it "returns 'expression' for an expression with '&&/and' and an unset global variable" do
+ defined?($defined_specs_undefined_global_variable && true).should == "expression"
+ defined?(true && $defined_specs_undefined_global_variable).should == "expression"
+ defined?($defined_specs_undefined_global_variable and true).should == "expression"
+ end
+
+ it "returns 'expression' for an expression with '&&/and' and an unset instance variable" do
+ defined?(@defined_specs_undefined_instance_variable && true).should == "expression"
+ defined?(true && @defined_specs_undefined_instance_variable).should == "expression"
+ defined?(@defined_specs_undefined_instance_variable and true).should == "expression"
+ end
+
+ it "returns 'expression' for an expression '&&/and' regardless of its truth value" do
+ defined?(true && false).should == "expression"
+ defined?(true and false).should == "expression"
+ end
+
+ it "returns 'expression' for an expression with '||/or' and an unset global variable" do
+ defined?($defined_specs_undefined_global_variable || true).should == "expression"
+ defined?(true || $defined_specs_undefined_global_variable).should == "expression"
+ defined?($defined_specs_undefined_global_variable or true).should == "expression"
+ end
+
+ it "returns 'expression' for an expression with '||/or' and an unset instance variable" do
+ defined?(@defined_specs_undefined_instance_variable || true).should == "expression"
+ defined?(true || @defined_specs_undefined_instance_variable).should == "expression"
+ defined?(@defined_specs_undefined_instance_variable or true).should == "expression"
+ end
+
+ it "returns 'expression' for an expression '||/or' regardless of its truth value" do
+ defined?(true || false).should == "expression"
+ defined?(true or false).should == "expression"
+ end
+
+ it "returns nil for an expression with '!' and an unset global variable" do
+ defined?(!$defined_specs_undefined_global_variable).should be_nil
+ end
+
+ it "returns nil for an expression with '!' and an unset instance variable" do
+ defined?(!@defined_specs_undefined_instance_variable).should be_nil
+ end
+
+ it "returns 'method' for a 'not' expression with a method" do
+ defined?(not DefinedSpecs.side_effects).should == "method"
+ end
+
+ it "calls a method in a 'not' expression and returns 'method'" do
+ defined?(not DefinedSpecs.side_effects).should == "method"
+ ScratchPad.recorded.should == :defined_specs_side_effects
+ end
+
+ it "returns nil for an expression with 'not' and an unset global variable" do
+ defined?(not $defined_specs_undefined_global_variable).should be_nil
+ end
+
+ it "returns nil for an expression with 'not' and an unset instance variable" do
+ defined?(not @defined_specs_undefined_instance_variable).should be_nil
+ end
+
+ it "returns 'expression' for an expression with '&&/and' and an undefined method" do
+ defined?(defined_specs_undefined_method && true).should == "expression"
+ defined?(defined_specs_undefined_method and true).should == "expression"
+ end
+
+ it "returns 'expression' for an expression with '&&/and' and an unset class variable" do
+ defined?(@@defined_specs_undefined_class_variable && true).should == "expression"
+ defined?(@@defined_specs_undefined_class_variable and true).should == "expression"
+ end
+
+ it "does not call a method in an '&&' expression and returns 'expression'" do
+ defined?(DefinedSpecs.side_effects && true).should == "expression"
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "does not call a method in an 'and' expression and returns 'expression'" do
+ defined?(DefinedSpecs.side_effects and true).should == "expression"
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "returns 'expression' for an expression with '||/or' and an undefined method" do
+ defined?(defined_specs_undefined_method || true).should == "expression"
+ defined?(defined_specs_undefined_method or true).should == "expression"
+ end
+
+ it "returns 'expression' for an expression with '||/or' and an unset class variable" do
+ defined?(@@defined_specs_undefined_class_variable || true).should == "expression"
+ defined?(@@defined_specs_undefined_class_variable or true).should == "expression"
+ end
+
+ it "does not call a method in an '||' expression and returns 'expression'" do
+ defined?(DefinedSpecs.side_effects || true).should == "expression"
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "does not call a method in an 'or' expression and returns 'expression'" do
+ defined?(DefinedSpecs.side_effects or true).should == "expression"
+ ScratchPad.recorded.should be_nil
+ end
+ end
+
+ it "returns 'expression' when passed a String" do
+ defined?("garble gooble gable").should == "expression"
+ end
+
+ describe "with a dynamic String" do
+ it "returns 'expression' when the String contains a literal" do
+ defined?("garble #{42}").should == "expression"
+ end
+
+ it "returns 'expression' when the String contains a call to a defined method" do
+ defined?("garble #{DefinedSpecs.side_effects}").should == "expression"
+ end
+
+ it "returns 'expression' when the String contains a call to an undefined method" do
+ defined?("garble #{DefinedSpecs.undefined_method}").should == "expression"
+ end
+
+ it "does not call the method in the String" do
+ defined?("garble #{DefinedSpecs.dynamic_string}").should == "expression"
+ ScratchPad.recorded.should be_nil
+ end
+ end
+
+ describe "with a dynamic Regexp" do
+ it "returns 'expression' when the Regexp contains a literal" do
+ defined?(/garble #{42}/).should == "expression"
+ end
+
+ it "returns 'expression' when the Regexp contains a call to a defined method" do
+ defined?(/garble #{DefinedSpecs.side_effects}/).should == "expression"
+ end
+
+ it "returns 'expression' when the Regexp contains a call to an undefined method" do
+ defined?(/garble #{DefinedSpecs.undefined_method}/).should == "expression"
+ end
+
+ it "does not call the method in the Regexp" do
+ defined?(/garble #{DefinedSpecs.dynamic_string}/).should == "expression"
+ ScratchPad.recorded.should be_nil
+ end
+ end
+
+ it "returns 'expression' when passed a Fixnum literal" do
+ defined?(42).should == "expression"
+ end
+
+ it "returns 'expression' when passed a Bignum literal" do
+ defined?(0xdead_beef_deed_feed).should == "expression"
+ end
+
+ it "returns 'expression' when passed a Float literal" do
+ defined?(1.5).should == "expression"
+ end
+
+ it "returns 'expression' when passed a Range literal" do
+ defined?(0..2).should == "expression"
+ end
+
+ it "returns 'expression' when passed a Regexp literal" do
+ defined?(/undefined/).should == "expression"
+ end
+
+ it "returns 'expression' when passed an Array literal" do
+ defined?([1, 2]).should == "expression"
+ end
+
+ it "returns 'expression' when passed a Hash literal" do
+ defined?({a: :b}).should == "expression"
+ end
+
+ it "returns 'expression' when passed a Symbol literal" do
+ defined?(:defined_specs).should == "expression"
+ end
+end
+
+describe "The defined? keyword for variables" do
+ it "returns 'local-variable' when called with the name of a local variable" do
+ DefinedSpecs::Basic.new.local_variable_defined.should == "local-variable"
+ end
+
+ it "returns 'local-variable' when called with the name of a local variable assigned to nil" do
+ DefinedSpecs::Basic.new.local_variable_defined_nil.should == "local-variable"
+ end
+
+ it "returns nil for an instance variable that has not been read" do
+ DefinedSpecs::Basic.new.instance_variable_undefined.should be_nil
+ end
+
+ it "returns nil for an instance variable that has been read but not assigned to" do
+ DefinedSpecs::Basic.new.instance_variable_read.should be_nil
+ end
+
+ it "returns 'instance-variable' for an instance variable that has been assigned" do
+ DefinedSpecs::Basic.new.instance_variable_defined.should == "instance-variable"
+ end
+
+ it "returns 'instance-variable' for an instance variable that has been assigned to nil" do
+ DefinedSpecs::Basic.new.instance_variable_defined_nil.should == "instance-variable"
+ end
+
+ it "returns nil for a global variable that has not been read" do
+ DefinedSpecs::Basic.new.global_variable_undefined.should be_nil
+ end
+
+ it "returns nil for a global variable that has been read but not assigned to" do
+ DefinedSpecs::Basic.new.global_variable_read.should be_nil
+ end
+
+ it "returns 'global-variable' for a global variable that has been assigned nil" do
+ DefinedSpecs::Basic.new.global_variable_defined_as_nil.should == "global-variable"
+ end
+
+ # MRI appears to special case defined? for $! and $~ in that it returns
+ # 'global-variable' even when they are not set (or they are always "set"
+ # but the value may be nil). In other words, 'defined?($~)' will return
+ # 'global-variable' even if no match has been done.
+
+ it "returns 'global-variable' for $!" do
+ defined?($!).should == "global-variable"
+ end
+
+ it "returns 'global-variable for $~" do
+ defined?($~).should == "global-variable"
+ end
+
+ describe "when a String does not match a Regexp" do
+ before :each do
+ "mis-matched" =~ /z(z)z/
+ end
+
+ it "returns 'global-variable' for $~" do
+ defined?($~).should == "global-variable"
+ end
+
+ it "returns nil for $&" do
+ defined?($&).should be_nil
+ end
+
+ it "returns nil for $`" do
+ defined?($`).should be_nil
+ end
+
+ it "returns nil for $'" do
+ defined?($').should be_nil
+ end
+
+ it "returns nil for $+" do
+ defined?($+).should be_nil
+ end
+
+ it "returns nil for any last match global" do
+ defined?($1).should be_nil
+ defined?($4).should be_nil
+ defined?($7).should be_nil
+ defined?($10).should be_nil
+ defined?($200).should be_nil
+ end
+ end
+
+ describe "when a String matches a Regexp" do
+ before :each do
+ "mis-matched" =~ /s(-)m(.)/
+ end
+
+ it "returns 'global-variable' for $~" do
+ defined?($~).should == "global-variable"
+ end
+
+ it "returns 'global-variable' for $&" do
+ defined?($&).should == "global-variable"
+ end
+
+ it "returns 'global-variable' for $`" do
+ defined?($`).should == "global-variable"
+ end
+
+ it "returns 'global-variable' for $'" do
+ defined?($').should == "global-variable"
+ end
+
+ it "returns 'global-variable' for $+" do
+ defined?($+).should == "global-variable"
+ end
+
+ it "returns 'global-variable' for the capture references" do
+ defined?($1).should == "global-variable"
+ defined?($2).should == "global-variable"
+ end
+
+ it "returns nil for non-captures" do
+ defined?($4).should be_nil
+ defined?($7).should be_nil
+ defined?($10).should be_nil
+ defined?($200).should be_nil
+ end
+ end
+
+ describe "when a Regexp does not match a String" do
+ before :each do
+ /z(z)z/ =~ "mis-matched"
+ end
+
+ it "returns 'global-variable' for $~" do
+ defined?($~).should == "global-variable"
+ end
+
+ it "returns nil for $&" do
+ defined?($&).should be_nil
+ end
+
+ it "returns nil for $`" do
+ defined?($`).should be_nil
+ end
+
+ it "returns nil for $'" do
+ defined?($').should be_nil
+ end
+
+ it "returns nil for $+" do
+ defined?($+).should be_nil
+ end
+
+ it "returns nil for any last match global" do
+ defined?($1).should be_nil
+ defined?($4).should be_nil
+ defined?($7).should be_nil
+ defined?($10).should be_nil
+ defined?($200).should be_nil
+ end
+ end
+
+ describe "when a Regexp matches a String" do
+ before :each do
+ /s(-)m(.)/ =~ "mis-matched"
+ end
+
+ it "returns 'global-variable' for $~" do
+ defined?($~).should == "global-variable"
+ end
+
+ it "returns 'global-variable' for $&" do
+ defined?($&).should == "global-variable"
+ end
+
+ it "returns 'global-variable' for $`" do
+ defined?($`).should == "global-variable"
+ end
+
+ it "returns 'global-variable' for $'" do
+ defined?($').should == "global-variable"
+ end
+
+ it "returns 'global-variable' for $+" do
+ defined?($+).should == "global-variable"
+ end
+
+ it "returns 'global-variable' for the capture references" do
+ defined?($1).should == "global-variable"
+ defined?($2).should == "global-variable"
+ end
+
+ it "returns nil for non-captures" do
+ defined?($4).should be_nil
+ defined?($7).should be_nil
+ defined?($10).should be_nil
+ defined?($200).should be_nil
+ end
+ end
+ it "returns 'global-variable' for a global variable that has been assigned" do
+ DefinedSpecs::Basic.new.global_variable_defined.should == "global-variable"
+ end
+
+ it "returns nil for a class variable that has not been read" do
+ DefinedSpecs::Basic.new.class_variable_undefined.should be_nil
+ end
+
+ # There is no spec for a class variable that is read before being assigned
+ # to because setting up the code for this raises a NameError before you
+ # get to the defined? call so it really has nothing to do with 'defined?'.
+
+ it "returns 'class variable' when called with the name of a class variable" do
+ DefinedSpecs::Basic.new.class_variable_defined.should == "class variable"
+ end
+
+ it "returns 'local-variable' when called with the name of a block local" do
+ block = Proc.new { |xxx| defined?(xxx) }
+ block.call(1).should == "local-variable"
+ end
+end
+
+describe "The defined? keyword for a simple constant" do
+ it "returns 'constant' when the constant is defined" do
+ defined?(DefinedSpecs).should == "constant"
+ end
+
+ it "returns nil when the constant is not defined" do
+ defined?(DefinedSpecsUndefined).should be_nil
+ end
+
+ it "does not call Object.const_missing if the constant is not defined" do
+ Object.should_not_receive(:const_missing)
+ defined?(DefinedSpecsUndefined).should be_nil
+ end
+
+ it "returns 'constant' for an included module" do
+ DefinedSpecs::Child.module_defined.should == "constant"
+ end
+
+ it "returns 'constant' for a constant defined in an included module" do
+ DefinedSpecs::Child.module_constant_defined.should == "constant"
+ end
+end
+
+describe "The defined? keyword for a top-level constant" do
+ it "returns 'constant' when passed the name of a top-level constant" do
+ defined?(::DefinedSpecs).should == "constant"
+ end
+
+ it "returns nil if the constant is not defined" do
+ defined?(::DefinedSpecsUndefined).should be_nil
+ end
+
+ it "does not call Object.const_missing if the constant is not defined" do
+ Object.should_not_receive(:const_missing)
+ defined?(::DefinedSpecsUndefined).should be_nil
+ end
+end
+
+describe "The defined? keyword for a scoped constant" do
+ it "returns 'constant' when the scoped constant is defined" do
+ defined?(DefinedSpecs::Basic).should == "constant"
+ end
+
+ it "returns nil when the scoped constant is not defined" do
+ defined?(DefinedSpecs::Undefined).should be_nil
+ end
+
+ it "does not call .const_missing if the constant is not defined" do
+ DefinedSpecs.should_not_receive(:const_missing)
+ defined?(DefinedSpecs::UnknownChild).should be_nil
+ end
+
+ it "returns nil when an undefined constant is scoped to a defined constant" do
+ defined?(DefinedSpecs::Child::Undefined).should be_nil
+ end
+
+ it "returns nil when a constant is scoped to an undefined constant" do
+ Object.should_not_receive(:const_missing)
+ defined?(Undefined::Object).should be_nil
+ end
+
+ it "returns nil when the undefined constant is scoped to an undefined constant" do
+ defined?(DefinedSpecs::Undefined::Undefined).should be_nil
+ end
+
+ it "returns nil when a constant is defined on top-level but not on the module" do
+ defined?(DefinedSpecs::String).should be_nil
+ end
+
+ ruby_version_is ""..."2.5" do
+ it "returns 'constant' when a constant is defined on top-level but not on the class" do
+ defined?(DefinedSpecs::Basic::String).should == 'constant'
+ end
+ end
+
+ ruby_version_is "2.5" do
+ ruby_bug "#14407", "2.5.0"..."2.5.1" do
+ it "returns nil when a constant is defined on top-level but not on the class" do
+ defined?(DefinedSpecs::Basic::String).should be_nil
+ end
+ end
+ end
+
+ it "returns 'constant' if the scoped-scoped constant is defined" do
+ defined?(DefinedSpecs::Child::A).should == "constant"
+ end
+end
+
+describe "The defined? keyword for a top-level scoped constant" do
+ it "returns 'constant' when the scoped constant is defined" do
+ defined?(::DefinedSpecs::Basic).should == "constant"
+ end
+
+ it "returns nil when the scoped constant is not defined" do
+ defined?(::DefinedSpecs::Undefined).should be_nil
+ end
+
+ it "returns nil when an undefined constant is scoped to a defined constant" do
+ defined?(::DefinedSpecs::Child::Undefined).should be_nil
+ end
+
+ it "returns nil when the undefined constant is scoped to an undefined constant" do
+ defined?(::DefinedSpecs::Undefined::Undefined).should be_nil
+ end
+
+ it "returns 'constant' if the scoped-scoped constant is defined" do
+ defined?(::DefinedSpecs::Child::A).should == "constant"
+ end
+end
+
+describe "The defined? keyword for a self-send method call scoped constant" do
+ it "returns nil if the constant is not defined in the scope of the method's value" do
+ defined?(defined_specs_method::Undefined).should be_nil
+ end
+
+ it "returns 'constant' if the constant is defined in the scope of the method's value" do
+ defined?(defined_specs_method::Basic).should == "constant"
+ end
+
+ it "returns nil if the last constant is not defined in the scope chain" do
+ defined?(defined_specs_method::Basic::Undefined).should be_nil
+ end
+
+ it "returns nil if the middle constant is not defined in the scope chain" do
+ defined?(defined_specs_method::Undefined::Undefined).should be_nil
+ end
+
+ it "returns 'constant' if all the constants in the scope chain are defined" do
+ defined?(defined_specs_method::Basic::A).should == "constant"
+ end
+end
+
+describe "The defined? keyword for a receiver method call scoped constant" do
+ it "returns nil if the constant is not defined in the scope of the method's value" do
+ defined?(defined_specs_receiver.defined_method::Undefined).should be_nil
+ end
+
+ it "returns 'constant' if the constant is defined in the scope of the method's value" do
+ defined?(defined_specs_receiver.defined_method::Basic).should == "constant"
+ end
+
+ it "returns nil if the last constant is not defined in the scope chain" do
+ defined?(defined_specs_receiver.defined_method::Basic::Undefined).should be_nil
+ end
+
+ it "returns nil if the middle constant is not defined in the scope chain" do
+ defined?(defined_specs_receiver.defined_method::Undefined::Undefined).should be_nil
+ end
+
+ it "returns 'constant' if all the constants in the scope chain are defined" do
+ defined?(defined_specs_receiver.defined_method::Basic::A).should == "constant"
+ end
+end
+
+describe "The defined? keyword for a module method call scoped constant" do
+ it "returns nil if the constant is not defined in the scope of the method's value" do
+ defined?(DefinedSpecs.defined_method::Undefined).should be_nil
+ end
+
+ it "returns 'constant' if the constant scoped by the method's value is defined" do
+ defined?(DefinedSpecs.defined_method::Basic).should == "constant"
+ end
+
+ it "returns nil if the last constant in the scope chain is not defined" do
+ defined?(DefinedSpecs.defined_method::Basic::Undefined).should be_nil
+ end
+
+ it "returns nil if the middle constant in the scope chain is not defined" do
+ defined?(DefinedSpecs.defined_method::Undefined::Undefined).should be_nil
+ end
+
+ it "returns 'constant' if all the constants in the scope chain are defined" do
+ defined?(DefinedSpecs.defined_method::Basic::A).should == "constant"
+ end
+
+ it "returns nil if the outer scope constant in the receiver is not defined" do
+ defined?(Undefined::DefinedSpecs.defined_method::Basic).should be_nil
+ end
+
+ it "returns nil if the scoped constant in the receiver is not defined" do
+ defined?(DefinedSpecs::Undefined.defined_method::Basic).should be_nil
+ end
+
+ it "returns 'constant' if all the constants in the receiver are defined" do
+ defined?(Object::DefinedSpecs.defined_method::Basic).should == "constant"
+ end
+
+ it "returns 'constant' if all the constants in the receiver and scope chain are defined" do
+ defined?(Object::DefinedSpecs.defined_method::Basic::A).should == "constant"
+ end
+end
+
+describe "The defined? keyword for a variable scoped constant" do
+ after :all do
+ if Object.class_variable_defined? :@@defined_specs_obj
+ Object.__send__(:remove_class_variable, :@@defined_specs_obj)
+ end
+ end
+
+ it "returns nil if the instance scoped constant is not defined" do
+ @defined_specs_obj = DefinedSpecs::Basic
+ defined?(@defined_specs_obj::Undefined).should be_nil
+ end
+
+ it "returns 'constant' if the constant is defined in the scope of the instance variable" do
+ @defined_specs_obj = DefinedSpecs::Basic
+ defined?(@defined_specs_obj::A).should == "constant"
+ end
+
+ it "returns nil if the global scoped constant is not defined" do
+ $defined_specs_obj = DefinedSpecs::Basic
+ defined?($defined_specs_obj::Undefined).should be_nil
+ end
+
+ it "returns 'constant' if the constant is defined in the scope of the global variable" do
+ $defined_specs_obj = DefinedSpecs::Basic
+ defined?($defined_specs_obj::A).should == "constant"
+ end
+
+ it "returns nil if the class scoped constant is not defined" do
+ -> {
+ @@defined_specs_obj = DefinedSpecs::Basic
+ defined?(@@defined_specs_obj::Undefined).should be_nil
+ }.should complain(/class variable access from toplevel/)
+ end
+
+ it "returns 'constant' if the constant is defined in the scope of the class variable" do
+ -> {
+ @@defined_specs_obj = DefinedSpecs::Basic
+ defined?(@@defined_specs_obj::A).should == "constant"
+ }.should complain(/class variable access from toplevel/)
+ end
+
+ it "returns nil if the local scoped constant is not defined" do
+ defined_specs_obj = DefinedSpecs::Basic
+ defined?(defined_specs_obj::Undefined).should be_nil
+ end
+
+ it "returns 'constant' if the constant is defined in the scope of the local variable" do
+ defined_specs_obj = DefinedSpecs::Basic
+ defined?(defined_specs_obj::A).should == "constant"
+ end
+end
+
+describe "The defined? keyword for a self:: scoped constant" do
+ it "returns 'constant' for a constant explicitly scoped to self:: when set" do
+ defined?(DefinedSpecs::SelfScoped).should == "constant"
+ end
+
+ it "returns 'constant' for a constant explicitly scoped to self:: in subclass's metaclass" do
+ DefinedSpecs::Child.parent_constant_defined.should == "constant"
+ end
+end
+
+describe "The defined? keyword for yield" do
+ it "returns nil if no block is passed to a method not taking a block parameter" do
+ DefinedSpecs::Basic.new.no_yield_block.should be_nil
+ end
+
+ it "returns nil if no block is passed to a method taking a block parameter" do
+ DefinedSpecs::Basic.new.no_yield_block_parameter.should be_nil
+ end
+
+ it "returns 'yield' if a block is passed to a method not taking a block parameter" do
+ DefinedSpecs::Basic.new.yield_block.should == "yield"
+ end
+
+ it "returns 'yield' if a block is passed to a method taking a block parameter" do
+ DefinedSpecs::Basic.new.yield_block_parameter.should == "yield"
+ end
+end
+
+describe "The defined? keyword for super" do
+ it "returns nil when a superclass undef's the method" do
+ DefinedSpecs::ClassWithoutMethod.new.test.should be_nil
+ end
+
+ describe "for a method taking no arguments" do
+ it "returns nil when no superclass method exists" do
+ DefinedSpecs::Super.new.no_super_method_no_args.should be_nil
+ end
+
+ it "returns nil from a block when no superclass method exists" do
+ DefinedSpecs::Super.new.no_super_method_block_no_args.should be_nil
+ end
+
+ it "returns nil from a #define_method when no superclass method exists" do
+ DefinedSpecs::Super.new.no_super_define_method_no_args.should be_nil
+ end
+
+ it "returns nil from a block in a #define_method when no superclass method exists" do
+ DefinedSpecs::Super.new.no_super_define_method_block_no_args.should be_nil
+ end
+
+ it "returns 'super' when a superclass method exists" do
+ DefinedSpecs::Super.new.method_no_args.should == "super"
+ end
+
+ it "returns 'super' from a block when a superclass method exists" do
+ DefinedSpecs::Super.new.method_block_no_args.should == "super"
+ end
+
+ it "returns 'super' from a #define_method when a superclass method exists" do
+ DefinedSpecs::Super.new.define_method_no_args.should == "super"
+ end
+
+ it "returns 'super' from a block in a #define_method when a superclass method exists" do
+ DefinedSpecs::Super.new.define_method_block_no_args.should == "super"
+ end
+
+ it "returns 'super' when the method exists in a supermodule" do
+ DefinedSpecs::SuperWithIntermediateModules.new.method_no_args.should == "super"
+ end
+ end
+
+ describe "for a method taking arguments" do
+ it "returns nil when no superclass method exists" do
+ DefinedSpecs::Super.new.no_super_method_args.should be_nil
+ end
+
+ it "returns nil from a block when no superclass method exists" do
+ DefinedSpecs::Super.new.no_super_method_block_args.should be_nil
+ end
+
+ it "returns nil from a #define_method when no superclass method exists" do
+ DefinedSpecs::Super.new.no_super_define_method_args.should be_nil
+ end
+
+ it "returns nil from a block in a #define_method when no superclass method exists" do
+ DefinedSpecs::Super.new.no_super_define_method_block_args.should be_nil
+ end
+
+ it "returns 'super' when a superclass method exists" do
+ DefinedSpecs::Super.new.method_args.should == "super"
+ end
+
+ it "returns 'super' from a block when a superclass method exists" do
+ DefinedSpecs::Super.new.method_block_args.should == "super"
+ end
+
+ it "returns 'super' from a #define_method when a superclass method exists" do
+ DefinedSpecs::Super.new.define_method_args.should == "super"
+ end
+
+ it "returns 'super' from a block in a #define_method when a superclass method exists" do
+ DefinedSpecs::Super.new.define_method_block_args.should == "super"
+ end
+ end
+
+ describe "within an included module's method" do
+ it "returns 'super' when a superclass method exists in the including hierarchy" do
+ DefinedSpecs::Child.new.defined_super.should == "super"
+ end
+ end
+end
+
+
+describe "The defined? keyword for instance variables" do
+ it "returns 'instance-variable' if assigned" do
+ @assigned_ivar = "some value"
+ defined?(@assigned_ivar).should == "instance-variable"
+ end
+
+ it "returns nil if not assigned" do
+ defined?(@unassigned_ivar).should be_nil
+ end
+end
+
+describe "The defined? keyword for pseudo-variables" do
+ it "returns 'expression' for __FILE__" do
+ defined?(__FILE__).should == "expression"
+ end
+
+ it "returns 'expression' for __LINE__" do
+ defined?(__LINE__).should == "expression"
+ end
+
+ it "returns 'expression' for __ENCODING__" do
+ defined?(__ENCODING__).should == "expression"
+ end
+end
+
+describe "The defined? keyword for conditional expressions" do
+ it "returns 'expression' for an 'if' conditional" do
+ defined?(if x then 'x' else '' end).should == "expression"
+ end
+
+ it "returns 'expression' for an 'unless' conditional" do
+ defined?(unless x then '' else 'x' end).should == "expression"
+ end
+
+ it "returns 'expression' for ternary expressions" do
+ defined?(x ? 'x' : '').should == "expression"
+ end
+end
+
+describe "The defined? keyword for case expressions" do
+ it "returns 'expression'" do
+ defined?(case x; when 'x'; 'y' end).should == "expression"
+ end
+end
+
+describe "The defined? keyword for loop expressions" do
+ it "returns 'expression' for a 'for' expression" do
+ defined?(for n in 1..3 do true end).should == "expression"
+ end
+
+ it "returns 'expression' for a 'while' expression" do
+ defined?(while x do y end).should == "expression"
+ end
+
+ it "returns 'expression' for an 'until' expression" do
+ defined?(until x do y end).should == "expression"
+ end
+
+ it "returns 'expression' for a 'break' expression" do
+ defined?(break).should == "expression"
+ end
+
+ it "returns 'expression' for a 'next' expression" do
+ defined?(next).should == "expression"
+ end
+
+ it "returns 'expression' for a 'redo' expression" do
+ defined?(redo).should == "expression"
+ end
+
+ it "returns 'expression' for a 'retry' expression" do
+ defined?(retry).should == "expression"
+ end
+end
+
+describe "The defined? keyword for return expressions" do
+ it "returns 'expression'" do
+ defined?(return).should == "expression"
+ end
+end
+
+describe "The defined? keyword for exception expressions" do
+ it "returns 'expression'" do
+ defined?(begin 1 end).should == "expression"
+ end
+end
diff --git a/spec/ruby/language/encoding_spec.rb b/spec/ruby/language/encoding_spec.rb
new file mode 100644
index 0000000000..31b403a704
--- /dev/null
+++ b/spec/ruby/language/encoding_spec.rb
@@ -0,0 +1,36 @@
+# -*- encoding: us-ascii -*-
+require_relative '../spec_helper'
+require_relative 'fixtures/coding_us_ascii'
+require_relative 'fixtures/coding_utf_8'
+
+describe "The __ENCODING__ pseudo-variable" do
+ it "is an instance of Encoding" do
+ __ENCODING__.should be_kind_of(Encoding)
+ end
+
+ it "is US-ASCII by default" do
+ __ENCODING__.should == Encoding::US_ASCII
+ end
+
+ it "is the evaluated strings's one inside an eval" do
+ eval("__ENCODING__".force_encoding("US-ASCII")).should == Encoding::US_ASCII
+ eval("__ENCODING__".force_encoding("ASCII-8BIT")).should == Encoding::ASCII_8BIT
+ end
+
+ it "is the encoding specified by a magic comment inside an eval" do
+ code = "# encoding: ASCII-8BIT\n__ENCODING__".force_encoding("US-ASCII")
+ eval(code).should == Encoding::ASCII_8BIT
+
+ code = "# encoding: us-ascii\n__ENCODING__".force_encoding("ASCII-8BIT")
+ eval(code).should == Encoding::US_ASCII
+ end
+
+ it "is the encoding specified by a magic comment in the file" do
+ CodingUS_ASCII.encoding.should == Encoding::US_ASCII
+ CodingUTF_8.encoding.should == Encoding::UTF_8
+ end
+
+ it "raises a SyntaxError if assigned to" do
+ lambda { eval("__ENCODING__ = 1") }.should raise_error(SyntaxError)
+ end
+end
diff --git a/spec/ruby/language/ensure_spec.rb b/spec/ruby/language/ensure_spec.rb
new file mode 100644
index 0000000000..064627b2f7
--- /dev/null
+++ b/spec/ruby/language/ensure_spec.rb
@@ -0,0 +1,333 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/ensure'
+
+describe "An ensure block inside a begin block" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "is executed when an exception is raised in it's corresponding begin block" do
+ lambda {
+ begin
+ ScratchPad << :begin
+ raise EnsureSpec::Error
+ ensure
+ ScratchPad << :ensure
+ end
+ }.should raise_error(EnsureSpec::Error)
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "is executed when an exception is raised and rescued in it's corresponding begin block" do
+ begin
+ ScratchPad << :begin
+ raise "An exception occurred!"
+ rescue
+ ScratchPad << :rescue
+ ensure
+ ScratchPad << :ensure
+ end
+
+ ScratchPad.recorded.should == [:begin, :rescue, :ensure]
+ end
+
+ it "is executed even when a symbol is thrown in it's corresponding begin block" do
+ catch(:symbol) do
+ begin
+ ScratchPad << :begin
+ throw(:symbol)
+ rescue
+ ScratchPad << :rescue
+ ensure
+ ScratchPad << :ensure
+ end
+ end
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "is executed when nothing is raised or thrown in it's corresponding begin block" do
+ begin
+ ScratchPad << :begin
+ rescue
+ ScratchPad << :rescue
+ ensure
+ ScratchPad << :ensure
+ end
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "has no return value" do
+ begin
+ :begin
+ ensure
+ :ensure
+ end.should == :begin
+ end
+
+ it "sets exception cause if raises exception in block and in ensure" do
+ -> {
+ begin
+ raise "from block"
+ ensure
+ raise "from ensure"
+ end
+ }.should raise_error(RuntimeError, "from ensure") do |e|
+ e.cause.message.should == "from block"
+ end
+ end
+end
+
+describe "The value of an ensure expression," do
+ it "in no-exception scenarios, is the value of the last statement of the protected body" do
+ begin
+ v = 1
+ eval('x=1') # to prevent opts from triggering
+ v
+ ensure
+ v = 2
+ end.should == 1
+ end
+
+ it "when an exception is rescued, is the value of the rescuing block" do
+ begin
+ raise 'foo'
+ rescue
+ v = 3
+ ensure
+ v = 2
+ end.should == 3
+ end
+end
+
+describe "An ensure block inside a method" do
+ before :each do
+ @obj = EnsureSpec::Container.new
+ end
+
+ it "is executed when an exception is raised in the method" do
+ lambda { @obj.raise_in_method_with_ensure }.should raise_error(EnsureSpec::Error)
+ @obj.executed.should == [:method, :ensure]
+ end
+
+ it "is executed when an exception is raised and rescued in the method" do
+ @obj.raise_and_rescue_in_method_with_ensure
+ @obj.executed.should == [:method, :rescue, :ensure]
+ end
+
+ it "is executed even when a symbol is thrown in the method" do
+ catch(:symbol) { @obj.throw_in_method_with_ensure }
+ @obj.executed.should == [:method, :ensure]
+ end
+
+ it "has no impact on the method's implicit return value" do
+ @obj.implicit_return_in_method_with_ensure.should == :method
+ end
+
+ it "has an impact on the method's explicit return value" do
+ @obj.explicit_return_in_method_with_ensure.should == :ensure
+ end
+
+ it "has an impact on the method's explicit return value from rescue if returns explicitly" do
+ @obj.explicit_return_in_rescue_and_explicit_return_in_ensure.should == "returned in ensure"
+ end
+
+ it "has no impact on the method's explicit return value from rescue if returns implicitly" do
+ @obj.explicit_return_in_rescue_and_implicit_return_in_ensure.should == "returned in rescue"
+ end
+
+ it "suppresses exception raised in method if returns value explicitly" do
+ @obj.raise_and_explicit_return_in_ensure.should == "returned in ensure"
+ end
+
+ it "suppresses exception raised in rescue if returns value explicitly" do
+ @obj.raise_in_rescue_and_explicit_return_in_ensure.should == "returned in ensure"
+ end
+
+ it "overrides exception raised in rescue if raises exception itself" do
+ -> {
+ @obj.raise_in_rescue_and_raise_in_ensure
+ }.should raise_error(RuntimeError, "raised in ensure")
+ end
+
+ it "suppresses exception raised in method if raises exception itself" do
+ -> {
+ @obj.raise_in_method_and_raise_in_ensure
+ }.should raise_error(RuntimeError, "raised in ensure")
+ end
+end
+
+describe "An ensure block inside a class" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "is executed when an exception is raised" do
+ lambda {
+ eval <<-ruby
+ class EnsureInClassExample
+ ScratchPad << :class
+ raise EnsureSpec::Error
+ ensure
+ ScratchPad << :ensure
+ end
+ ruby
+ }.should raise_error(EnsureSpec::Error)
+
+ ScratchPad.recorded.should == [:class, :ensure]
+ end
+
+ it "is executed when an exception is raised and rescued" do
+ eval <<-ruby
+ class EnsureInClassExample
+ ScratchPad << :class
+ raise
+ rescue
+ ScratchPad << :rescue
+ ensure
+ ScratchPad << :ensure
+ end
+ ruby
+
+ ScratchPad.recorded.should == [:class, :rescue, :ensure]
+ end
+
+ it "is executed even when a symbol is thrown" do
+ catch(:symbol) do
+ eval <<-ruby
+ class EnsureInClassExample
+ ScratchPad << :class
+ throw(:symbol)
+ rescue
+ ScratchPad << :rescue
+ ensure
+ ScratchPad << :ensure
+ end
+ ruby
+ end
+
+ ScratchPad.recorded.should == [:class, :ensure]
+ end
+
+ it "is executed when nothing is raised or thrown" do
+ eval <<-ruby
+ class EnsureInClassExample
+ ScratchPad << :class
+ rescue
+ ScratchPad << :rescue
+ ensure
+ ScratchPad << :ensure
+ end
+ ruby
+
+ ScratchPad.recorded.should == [:class, :ensure]
+ end
+
+ it "has no return value" do
+ result = eval <<-ruby
+ class EnsureInClassExample
+ :class
+ ensure
+ :ensure
+ end
+ ruby
+
+ result.should == :class
+ end
+end
+
+describe "An ensure block inside {} block" do
+ it "is not allowed" do
+ lambda {
+ eval <<-ruby
+ lambda {
+ raise
+ ensure
+ }
+ ruby
+ }.should raise_error(SyntaxError)
+ end
+end
+
+ruby_version_is "2.5" do
+ describe "An ensure block inside 'do end' block" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "is executed when an exception is raised in it's corresponding begin block" do
+ lambda {
+ eval(<<-ruby).call
+ lambda do
+ ScratchPad << :begin
+ raise EnsureSpec::Error
+ ensure
+ ScratchPad << :ensure
+ end
+ ruby
+ }.should raise_error(EnsureSpec::Error)
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "is executed when an exception is raised and rescued in it's corresponding begin block" do
+ eval(<<-ruby).call
+ lambda do
+ ScratchPad << :begin
+ raise "An exception occurred!"
+ rescue
+ ScratchPad << :rescue
+ ensure
+ ScratchPad << :ensure
+ end
+ ruby
+
+ ScratchPad.recorded.should == [:begin, :rescue, :ensure]
+ end
+
+ it "is executed even when a symbol is thrown in it's corresponding begin block" do
+ catch(:symbol) do
+ eval(<<-ruby).call
+ lambda do
+ ScratchPad << :begin
+ throw(:symbol)
+ rescue
+ ScratchPad << :rescue
+ ensure
+ ScratchPad << :ensure
+ end
+ ruby
+ end
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "is executed when nothing is raised or thrown in it's corresponding begin block" do
+ eval(<<-ruby).call
+ lambda do
+ ScratchPad << :begin
+ rescue
+ ScratchPad << :rescue
+ ensure
+ ScratchPad << :ensure
+ end
+ ruby
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "has no return value" do
+ result = eval(<<-ruby).call
+ lambda do
+ :begin
+ ensure
+ :ensure
+ end
+ ruby
+
+ result.should == :begin
+ end
+ end
+end
diff --git a/spec/ruby/language/execution_spec.rb b/spec/ruby/language/execution_spec.rb
new file mode 100644
index 0000000000..4e0310946d
--- /dev/null
+++ b/spec/ruby/language/execution_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../spec_helper'
+
+describe "``" do
+ it "returns the output of the executed sub-process" do
+ ip = 'world'
+ `echo disc #{ip}`.should == "disc world\n"
+ end
+end
+
+describe "%x" do
+ it "is the same as ``" do
+ ip = 'world'
+ %x(echo disc #{ip}).should == "disc world\n"
+ end
+end
diff --git a/spec/ruby/language/file_spec.rb b/spec/ruby/language/file_spec.rb
new file mode 100644
index 0000000000..bf7175a9bc
--- /dev/null
+++ b/spec/ruby/language/file_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/code_loading'
+require_relative 'shared/__FILE__'
+
+describe "The __FILE__ pseudo-variable" do
+ it "raises a SyntaxError if assigned to" do
+ lambda { eval("__FILE__ = 1") }.should raise_error(SyntaxError)
+ end
+
+ it "equals (eval) inside an eval" do
+ eval("__FILE__").should == "(eval)"
+ end
+end
+
+describe "The __FILE__ pseudo-variable" do
+ it_behaves_like :language___FILE__, :require, CodeLoadingSpecs::Method.new
+end
+
+describe "The __FILE__ pseudo-variable" do
+ it_behaves_like :language___FILE__, :require, Kernel
+end
+
+describe "The __FILE__ pseudo-variable" do
+ it_behaves_like :language___FILE__, :load, CodeLoadingSpecs::Method.new
+end
+
+describe "The __FILE__ pseudo-variable" do
+ it_behaves_like :language___FILE__, :load, Kernel
+end
diff --git a/spec/ruby/language/fixtures/argv_encoding.rb b/spec/ruby/language/fixtures/argv_encoding.rb
new file mode 100644
index 0000000000..8192b2d9a0
--- /dev/null
+++ b/spec/ruby/language/fixtures/argv_encoding.rb
@@ -0,0 +1 @@
+p ARGV.map { |a| a.encoding.name }
diff --git a/spec/ruby/language/fixtures/array.rb b/spec/ruby/language/fixtures/array.rb
new file mode 100644
index 0000000000..c1036575ff
--- /dev/null
+++ b/spec/ruby/language/fixtures/array.rb
@@ -0,0 +1,32 @@
+module ArraySpec
+ class Splat
+ def unpack_3args(a, b, c)
+ [a, b, c]
+ end
+
+ def unpack_4args(a, b, c, d)
+ [a, b, c, d]
+ end
+ end
+
+ class SideEffect
+ def initialize()
+ @call_count = 0
+ end
+
+ attr_reader :call_count
+
+ def array_result(a_number)
+ [result(a_number), result(a_number)]
+ end
+
+ def result(a_number)
+ @call_count += 1
+ if a_number
+ 1
+ else
+ :thing
+ end
+ end
+ end
+end
diff --git a/spec/ruby/language/fixtures/begin_file.rb b/spec/ruby/language/fixtures/begin_file.rb
new file mode 100644
index 0000000000..73cae61a08
--- /dev/null
+++ b/spec/ruby/language/fixtures/begin_file.rb
@@ -0,0 +1,3 @@
+BEGIN {
+ puts __FILE__
+}
diff --git a/spec/ruby/language/fixtures/binary_symbol.rb b/spec/ruby/language/fixtures/binary_symbol.rb
new file mode 100644
index 0000000000..2ddf565820
--- /dev/null
+++ b/spec/ruby/language/fixtures/binary_symbol.rb
@@ -0,0 +1,4 @@
+# encoding: binary
+
+p :il_était.to_s.bytes
+puts :il_était.encoding.name
diff --git a/spec/ruby/language/fixtures/block.rb b/spec/ruby/language/fixtures/block.rb
new file mode 100644
index 0000000000..9848d18776
--- /dev/null
+++ b/spec/ruby/language/fixtures/block.rb
@@ -0,0 +1,57 @@
+module BlockSpecs
+ class Yielder
+ def z
+ yield
+ end
+
+ def m(*a)
+ yield(*a)
+ end
+
+ def s(a)
+ yield(a)
+ end
+
+ def r(a)
+ yield(*a)
+ end
+ end
+
+ # TODO: rewrite all specs that use Yield to use Yielder
+
+ class Yield
+ def splat(*args)
+ yield(*args)
+ end
+
+ def two_args
+ yield 1, 2
+ end
+
+ def two_arg_array
+ yield [1, 2]
+ end
+
+ def yield_splat_inside_block
+ [1, 2].send(:each_with_index) {|*args| yield(*args)}
+ end
+
+ def yield_this(obj)
+ yield obj
+ end
+ end
+
+ class OverwriteBlockVariable
+ def initialize
+ @y = Yielder.new
+ end
+
+ def method_missing(method, *args, &block)
+ self.class.send :define_method, method do |*a, &b|
+ @y.send method, *a, &b
+ end
+
+ send method, *args, &block
+ end
+ end
+end
diff --git a/spec/ruby/language/fixtures/break.rb b/spec/ruby/language/fixtures/break.rb
new file mode 100644
index 0000000000..2d07cc3d48
--- /dev/null
+++ b/spec/ruby/language/fixtures/break.rb
@@ -0,0 +1,291 @@
+module BreakSpecs
+ class Driver
+ def initialize(ensures=false)
+ @ensures = ensures
+ end
+
+ def note(value)
+ ScratchPad << value
+ end
+ end
+
+ class Block < Driver
+ def break_nil
+ note :a
+ note yielding {
+ note :b
+ break
+ note :c
+ }
+ note :d
+ end
+
+ def break_value
+ note :a
+ note yielding {
+ note :b
+ break :break
+ note :c
+ }
+ note :d
+ end
+
+ def yielding
+ note :aa
+ note yield
+ note :bb
+ end
+
+ def create_block
+ note :za
+ b = capture_block do
+ note :zb
+ break :break
+ note :zc
+ end
+ note :zd
+ b
+ end
+
+ def capture_block(&b)
+ note :xa
+ b
+ end
+
+ def break_in_method_captured
+ note :a
+ create_block.call
+ note :b
+ end
+
+ def break_in_yield_captured
+ note :a
+ yielding(&create_block)
+ note :b
+ end
+
+ def break_in_method
+ note :a
+ b = capture_block {
+ note :b
+ break :break
+ note :c
+ }
+ note :d
+ note b.call
+ note :e
+ end
+
+ def call_method(b)
+ note :aa
+ note b.call
+ note :bb
+ end
+
+ def break_in_nested_method
+ note :a
+ b = capture_block {
+ note :b
+ break :break
+ note :c
+ }
+ note :cc
+ note call_method(b)
+ note :d
+ end
+
+ def break_in_yielding_method
+ note :a
+ b = capture_block {
+ note :b
+ break :break
+ note :c
+ }
+ note :cc
+ note yielding(&b)
+ note :d
+ end
+
+ def looped_break_in_captured_block
+ note :begin
+ looped_delegate_block do |i|
+ note :prebreak
+ break if i == 1
+ note :postbreak
+ end
+ note :end
+ end
+
+ def looped_delegate_block(&block)
+ note :preloop
+ 2.times do |i|
+ note :predele
+ yield_value(i, &block)
+ note :postdele
+ end
+ note :postloop
+ end
+ private :looped_delegate_block
+
+ def yield_value(value)
+ note :preyield
+ yield value
+ note :postyield
+ end
+ private :yield_value
+
+ def method(v)
+ yield v
+ end
+
+ def invoke_yield_in_while
+ looping = true
+ while looping
+ note :aa
+ yield
+ note :bb
+ looping = false
+ end
+ note :should_not_reach_here
+ end
+
+ def break_in_block_in_while
+ invoke_yield_in_while do
+ note :break
+ break :value
+ note :c
+ end
+ end
+ end
+
+ class Lambda < Driver
+ # Cases for the invocation of the scope defining the lambda still active
+ # on the call stack when the lambda is invoked.
+ def break_in_defining_scope(value=true)
+ note :a
+ note lambda {
+ note :b
+ if value
+ break :break
+ else
+ break
+ end
+ note :c
+ }.call
+ note :d
+ end
+
+ def break_in_nested_scope
+ note :a
+ l = lambda do
+ note :b
+ break :break
+ note :c
+ end
+ note :d
+
+ invoke_lambda l
+
+ note :e
+ end
+
+ def invoke_lambda(l)
+ note :aa
+ note l.call
+ note :bb
+ end
+
+ def break_in_nested_scope_yield
+ note :a
+ l = lambda do
+ note :b
+ break :break
+ note :c
+ end
+ note :d
+
+ invoke_yield(&l)
+
+ note :e
+ end
+
+ def note_invoke_yield
+ note :aa
+ note yield
+ note :bb
+ end
+
+ def break_in_nested_scope_block
+ note :a
+ l = lambda do
+ note :b
+ break :break
+ note :c
+ end
+ note :d
+
+ invoke_lambda_block l
+
+ note :e
+ end
+
+ def invoke_yield
+ note :aaa
+ yield
+ note :bbb
+ end
+
+ def invoke_lambda_block(b)
+ note :aa
+ invoke_yield do
+ note :bb
+
+ note b.call
+
+ note :cc
+ end
+ note :dd
+ end
+
+ # Cases for the invocation of the scope defining the lambda NOT still
+ # active on the call stack when the lambda is invoked.
+ def create_lambda
+ note :la
+ l = lambda do
+ note :lb
+ break :break
+ note :lc
+ end
+ note :ld
+ l
+ end
+
+ def break_in_method
+ note :a
+
+ note create_lambda.call
+
+ note :b
+ end
+
+ def break_in_block_in_method
+ note :a
+ invoke_yield do
+ note :b
+
+ note create_lambda.call
+
+ note :c
+ end
+ note :d
+ end
+
+ def break_in_method_yield
+ note :a
+
+ invoke_yield(&create_lambda)
+
+ note :b
+ end
+ end
+end
diff --git a/spec/ruby/language/fixtures/break_lambda_toplevel.rb b/spec/ruby/language/fixtures/break_lambda_toplevel.rb
new file mode 100644
index 0000000000..05af1d3fdc
--- /dev/null
+++ b/spec/ruby/language/fixtures/break_lambda_toplevel.rb
@@ -0,0 +1,9 @@
+print "a,"
+
+print lambda {
+ print "b,"
+ break "break,"
+ print "c,"
+}.call
+
+puts "d"
diff --git a/spec/ruby/language/fixtures/break_lambda_toplevel_block.rb b/spec/ruby/language/fixtures/break_lambda_toplevel_block.rb
new file mode 100644
index 0000000000..a35cb8a8a1
--- /dev/null
+++ b/spec/ruby/language/fixtures/break_lambda_toplevel_block.rb
@@ -0,0 +1,23 @@
+print "a,"
+
+l = lambda {
+ print "b,"
+ break "break,"
+ print "c,"
+}
+
+def a(l)
+ print "d,"
+ b { l.call }
+ print "e,"
+end
+
+def b
+ print "f,"
+ print yield
+ print "g,"
+end
+
+a(l)
+
+puts "h"
diff --git a/spec/ruby/language/fixtures/break_lambda_toplevel_method.rb b/spec/ruby/language/fixtures/break_lambda_toplevel_method.rb
new file mode 100644
index 0000000000..200040d614
--- /dev/null
+++ b/spec/ruby/language/fixtures/break_lambda_toplevel_method.rb
@@ -0,0 +1,17 @@
+print "a,"
+
+l = lambda {
+ print "b,"
+ break "break,"
+ print "c,"
+}
+
+def a(l)
+ print "d,"
+ print l.call
+ print "e,"
+end
+
+a(l)
+
+puts "f"
diff --git a/spec/ruby/language/fixtures/bytes_magic_comment.rb b/spec/ruby/language/fixtures/bytes_magic_comment.rb
new file mode 100644
index 0000000000..2bc2bcfb07
--- /dev/null
+++ b/spec/ruby/language/fixtures/bytes_magic_comment.rb
@@ -0,0 +1,2 @@
+# encoding: big5
+$magic_comment_result = '§A¦n'.bytes.inspect
diff --git a/spec/ruby/language/fixtures/case_magic_comment.rb b/spec/ruby/language/fixtures/case_magic_comment.rb
new file mode 100644
index 0000000000..96f35a7c94
--- /dev/null
+++ b/spec/ruby/language/fixtures/case_magic_comment.rb
@@ -0,0 +1,2 @@
+# CoDiNg: bIg5
+$magic_comment_result = __ENCODING__.name
diff --git a/spec/ruby/language/fixtures/classes.rb b/spec/ruby/language/fixtures/classes.rb
new file mode 100644
index 0000000000..eb239e1e29
--- /dev/null
+++ b/spec/ruby/language/fixtures/classes.rb
@@ -0,0 +1,31 @@
+module LanguageSpecs
+ # Regexp support
+
+ def self.paired_delimiters
+ [%w[( )], %w[{ }], %w[< >], ["[", "]"]]
+ end
+
+ def self.non_paired_delimiters
+ %w[~ ! # $ % ^ & * _ + ` - = " ' , . ? / | \\]
+ end
+
+ def self.blanks
+ " \t"
+ end
+
+ def self.white_spaces
+ return blanks + "\f\n\r\v"
+ end
+
+ def self.non_alphanum_non_space
+ '~!@#$%^&*()+-\|{}[]:";\'<>?,./'
+ end
+
+ def self.punctuations
+ ",.?" # TODO - Need to fill in the full list
+ end
+
+ def self.get_regexp_with_substitution o
+ /#{o}/o
+ end
+end
diff --git a/spec/ruby/language/fixtures/coding_us_ascii.rb b/spec/ruby/language/fixtures/coding_us_ascii.rb
new file mode 100644
index 0000000000..7df66109d7
--- /dev/null
+++ b/spec/ruby/language/fixtures/coding_us_ascii.rb
@@ -0,0 +1,11 @@
+# encoding: us-ascii
+
+module CodingUS_ASCII
+ def self.encoding
+ __ENCODING__
+ end
+
+ def self.string_literal
+ "string literal"
+ end
+end
diff --git a/spec/ruby/language/fixtures/coding_utf_8.rb b/spec/ruby/language/fixtures/coding_utf_8.rb
new file mode 100644
index 0000000000..3d8e1d9a34
--- /dev/null
+++ b/spec/ruby/language/fixtures/coding_utf_8.rb
@@ -0,0 +1,11 @@
+# encoding: utf-8
+
+module CodingUTF_8
+ def self.encoding
+ __ENCODING__
+ end
+
+ def self.string_literal
+ "string literal"
+ end
+end
diff --git a/spec/ruby/language/fixtures/constant_visibility.rb b/spec/ruby/language/fixtures/constant_visibility.rb
new file mode 100644
index 0000000000..022554430e
--- /dev/null
+++ b/spec/ruby/language/fixtures/constant_visibility.rb
@@ -0,0 +1,98 @@
+module ConstantVisibility
+ module ModuleContainer
+ module PrivateModule
+ end
+ private_constant :PrivateModule
+
+ class PrivateClass
+ end
+ private_constant :PrivateClass
+ end
+
+ class ClassContainer
+ module PrivateModule
+ end
+ private_constant :PrivateModule
+
+ class PrivateClass
+ end
+ private_constant :PrivateClass
+ end
+
+ module PrivConstModule
+ PRIVATE_CONSTANT_MODULE = true
+ private_constant :PRIVATE_CONSTANT_MODULE
+
+ def self.private_constant_from_self
+ PRIVATE_CONSTANT_MODULE
+ end
+
+ def self.defined_from_self
+ defined? PRIVATE_CONSTANT_MODULE
+ end
+
+ module Nested
+ def self.private_constant_from_scope
+ PRIVATE_CONSTANT_MODULE
+ end
+
+ def self.defined_from_scope
+ defined? PRIVATE_CONSTANT_MODULE
+ end
+ end
+ end
+
+ class PrivConstClass
+ PRIVATE_CONSTANT_CLASS = true
+ private_constant :PRIVATE_CONSTANT_CLASS
+
+ def self.private_constant_from_self
+ PRIVATE_CONSTANT_CLASS
+ end
+
+ def self.defined_from_self
+ defined? PRIVATE_CONSTANT_CLASS
+ end
+
+ module Nested
+ def self.private_constant_from_scope
+ PRIVATE_CONSTANT_CLASS
+ end
+
+ def self.defined_from_scope
+ defined? PRIVATE_CONSTANT_CLASS
+ end
+ end
+ end
+
+ class PrivConstModuleChild
+ include PrivConstModule
+
+ def private_constant_from_include
+ PRIVATE_CONSTANT_MODULE
+ end
+
+ def defined_from_include
+ defined? PRIVATE_CONSTANT_MODULE
+ end
+ end
+
+ class PrivConstClassChild < PrivConstClass
+ def private_constant_from_subclass
+ PRIVATE_CONSTANT_CLASS
+ end
+
+ def defined_from_subclass
+ defined? PRIVATE_CONSTANT_CLASS
+ end
+ end
+
+ def self.reset_private_constants
+ Object.send :private_constant, :PRIVATE_CONSTANT_IN_OBJECT
+ end
+end
+
+class Object
+ PRIVATE_CONSTANT_IN_OBJECT = true
+ private_constant :PRIVATE_CONSTANT_IN_OBJECT
+end
diff --git a/spec/ruby/language/fixtures/constants_sclass.rb b/spec/ruby/language/fixtures/constants_sclass.rb
new file mode 100644
index 0000000000..21dc4081e2
--- /dev/null
+++ b/spec/ruby/language/fixtures/constants_sclass.rb
@@ -0,0 +1,54 @@
+module ConstantSpecs
+
+ CS_SINGLETON1 = Object.new
+ class << CS_SINGLETON1
+ CONST = 1
+ def foo
+ CONST
+ end
+ end
+
+ CS_SINGLETON2 = [Object.new, Object.new]
+ 2.times do |i|
+ obj = CS_SINGLETON2[i]
+ $spec_i = i
+ class << obj
+ CONST = ($spec_i + 1)
+ def foo
+ CONST
+ end
+ end
+ end
+
+ CS_SINGLETON3 = [Object.new, Object.new]
+ 2.times do |i|
+ obj = CS_SINGLETON3[i]
+ class << obj
+ class X
+ # creates <singleton class::X>
+ end
+
+ def x
+ X
+ end
+ end
+ end
+
+ CS_SINGLETON4 = [Object.new, Object.new]
+ CS_SINGLETON4_CLASSES = []
+ 2.times do |i|
+ obj = CS_SINGLETON4[i]
+ $spec_i = i
+ class << obj
+ class X
+ CS_SINGLETON4_CLASSES << self
+ CONST = ($spec_i + 1)
+
+ def foo
+ CONST
+ end
+ end
+ end
+ end
+
+end
diff --git a/spec/ruby/language/fixtures/def.rb b/spec/ruby/language/fixtures/def.rb
new file mode 100644
index 0000000000..e07060ed74
--- /dev/null
+++ b/spec/ruby/language/fixtures/def.rb
@@ -0,0 +1,14 @@
+def toplevel_define_other_method
+ def nested_method_in_toplevel_method
+ 42
+ end
+end
+
+def some_toplevel_method
+end
+
+public
+def public_toplevel_method
+end
+
+private
diff --git a/spec/ruby/language/fixtures/defined.rb b/spec/ruby/language/fixtures/defined.rb
new file mode 100644
index 0000000000..8b6004c19f
--- /dev/null
+++ b/spec/ruby/language/fixtures/defined.rb
@@ -0,0 +1,303 @@
+module DefinedSpecs
+ self::SelfScoped = 42
+
+ def self.side_effects
+ ScratchPad.record :defined_specs_side_effects
+ end
+
+ def self.fixnum_method
+ ScratchPad.record :defined_specs_fixnum_method
+ 42
+ end
+
+ def self.exception_method
+ ScratchPad.record :defined_specs_exception
+ raise "defined? specs exception method"
+ end
+
+ def self.defined_method
+ DefinedSpecs
+ end
+
+ class Basic
+ A = 42
+
+ def defined_method
+ DefinedSpecs
+ end
+
+ def a_defined_method
+ end
+
+ def protected_method
+ end
+ protected :protected_method
+
+ def private_method
+ end
+ private :private_method
+
+ def private_method_defined
+ defined? private_method
+ end
+
+ def private_predicate?
+ end
+ private :private_predicate?
+
+ def private_predicate_defined
+ defined? private_predicate?
+ end
+
+ def local_variable_defined
+ x = 2
+ defined? x
+ end
+
+ def local_variable_defined_nil
+ x = nil
+ defined? x
+ end
+
+ def instance_variable_undefined
+ defined? @instance_variable_undefined
+ end
+
+ def instance_variable_read
+ value = @instance_variable_read
+ defined? @instance_variable_read
+ end
+
+ def instance_variable_defined
+ @instance_variable_defined = 1
+ defined? @instance_variable_defined
+ end
+
+ def instance_variable_defined_nil
+ @instance_variable_defined_nil = nil
+ defined? @instance_variable_defined_nil
+ end
+
+ def global_variable_undefined
+ defined? $defined_specs_global_variable_undefined
+ end
+
+ def global_variable_read
+ suppress_warning do
+ value = $defined_specs_global_variable_read
+ end
+ defined? $defined_specs_global_variable_read
+ end
+
+ def global_variable_defined
+ $defined_specs_global_variable_defined = 1
+ defined? $defined_specs_global_variable_defined
+ end
+
+ def global_variable_defined_as_nil
+ $defined_specs_global_variable_defined_as_nil = nil
+ defined? $defined_specs_global_variable_defined_as_nil
+ end
+
+ def class_variable_undefined
+ defined? @@class_variable_undefined
+ end
+
+ def class_variable_defined
+ @@class_variable_defined = 1
+ defined? @@class_variable_defined
+ end
+
+ def yield_defined_method
+ defined? yield
+ end
+
+ def yield_defined_parameter_method(&block)
+ defined? yield
+ end
+
+ def no_yield_block
+ yield_defined_method
+ end
+
+ def no_yield_block_parameter
+ yield_defined_parameter_method
+ end
+
+ def yield_block
+ yield_defined_method { 42 }
+ end
+
+ def yield_block_parameter
+ yield_defined_parameter_method { 42 }
+ end
+ end
+
+ module Mixin
+ MixinConstant = 42
+
+ def defined_super
+ defined? super()
+ end
+ end
+
+ class Parent
+ ParentConstant = 42
+
+ def defined_super; end
+ end
+
+ class Child < Parent
+ include Mixin
+
+ A = 42
+
+ def self.parent_constant_defined
+ defined? self::ParentConstant
+ end
+
+ def self.module_defined
+ defined? Mixin
+ end
+
+ def self.module_constant_defined
+ defined? MixinConstant
+ end
+
+ def defined_super
+ super
+ end
+ end
+
+ class Superclass
+ def yield_method
+ yield
+ end
+
+ def method_no_args
+ end
+
+ def method_args
+ end
+
+ def method_block_no_args
+ end
+
+ def method_block_args
+ end
+
+ def define_method_no_args
+ end
+
+ def define_method_args
+ end
+
+ def define_method_block_no_args
+ end
+
+ def define_method_block_args
+ end
+ end
+
+ class Super < Superclass
+ def no_super_method_no_args
+ defined? super
+ end
+
+ def no_super_method_args
+ defined? super()
+ end
+
+ def method_no_args
+ defined? super
+ end
+
+ def method_args
+ defined? super()
+ end
+
+ def no_super_method_block_no_args
+ yield_method { defined? super }
+ end
+
+ def no_super_method_block_args
+ yield_method { defined? super() }
+ end
+
+ def method_block_no_args
+ yield_method { defined? super }
+ end
+
+ def method_block_args
+ yield_method { defined? super() }
+ end
+
+ define_method(:no_super_define_method_no_args) { defined? super }
+ define_method(:no_super_define_method_args) { defined? super() }
+ define_method(:define_method_no_args) { defined? super }
+ define_method(:define_method_args) { defined? super() }
+
+ define_method(:no_super_define_method_block_no_args) do
+ yield_method { defined? super }
+ end
+
+ define_method(:no_super_define_method_block_args) do
+ yield_method { defined? super() }
+ end
+
+ define_method(:define_method_block_no_args) do
+ yield_method { defined? super }
+ end
+
+ define_method(:define_method_block_args) do
+ yield_method { defined? super() }
+ end
+ end
+
+ class ClassWithMethod
+ def test
+ end
+ end
+
+ class ClassUndefiningMethod < ClassWithMethod
+ undef :test
+ end
+
+ class ClassWithoutMethod < ClassUndefiningMethod
+ # If an undefined method overridden in descendants
+ # define?(super) should return nil
+ def test
+ defined?(super)
+ end
+ end
+
+ module IntermediateModule1
+ def method_no_args
+ end
+ end
+
+ module IntermediateModule2
+ def method_no_args
+ defined?(super)
+ end
+ end
+
+ class SuperWithIntermediateModules
+ include IntermediateModule1
+ include IntermediateModule2
+
+ def method_no_args
+ super
+ end
+ end
+end
+
+class Object
+ def defined_specs_method
+ DefinedSpecs
+ end
+
+ def defined_specs_receiver
+ DefinedSpecs::Basic.new
+ end
+end
diff --git a/spec/ruby/language/fixtures/dollar_zero.rb b/spec/ruby/language/fixtures/dollar_zero.rb
new file mode 100644
index 0000000000..683bce8d4e
--- /dev/null
+++ b/spec/ruby/language/fixtures/dollar_zero.rb
@@ -0,0 +1,6 @@
+puts $0
+puts __FILE__
+
+if $0 == __FILE__
+ print "OK"
+end
diff --git a/spec/ruby/language/fixtures/emacs_magic_comment.rb b/spec/ruby/language/fixtures/emacs_magic_comment.rb
new file mode 100644
index 0000000000..2b09f3e74c
--- /dev/null
+++ b/spec/ruby/language/fixtures/emacs_magic_comment.rb
@@ -0,0 +1,2 @@
+# -*- encoding: big5 -*-
+$magic_comment_result = __ENCODING__.name
diff --git a/spec/ruby/language/fixtures/ensure.rb b/spec/ruby/language/fixtures/ensure.rb
new file mode 100644
index 0000000000..6047ac5bc0
--- /dev/null
+++ b/spec/ruby/language/fixtures/ensure.rb
@@ -0,0 +1,121 @@
+module EnsureSpec
+ class Container
+ attr_reader :executed
+
+ def initialize
+ @executed = []
+ end
+
+ def raise_in_method_with_ensure
+ @executed << :method
+ raise EnsureSpec::Error
+ ensure
+ @executed << :ensure
+ end
+
+ def raise_and_rescue_in_method_with_ensure
+ @executed << :method
+ raise "An Exception"
+ rescue
+ @executed << :rescue
+ ensure
+ @executed << :ensure
+ end
+
+ def throw_in_method_with_ensure
+ @executed << :method
+ throw(:symbol)
+ ensure
+ @executed << :ensure
+ end
+
+ def implicit_return_in_method_with_ensure
+ :method
+ ensure
+ :ensure
+ end
+
+ def explicit_return_in_method_with_ensure
+ return :method
+ ensure
+ return :ensure
+ end
+
+ def explicit_return_in_rescue_and_explicit_return_in_ensure
+ raise
+ rescue
+ return 2
+ ensure
+ return "returned in ensure"
+ end
+
+ def explicit_return_in_rescue_and_implicit_return_in_ensure
+ raise
+ rescue
+ return "returned in rescue"
+ ensure
+ 3
+ end
+
+ def raise_and_explicit_return_in_ensure
+ raise
+ ensure
+ return "returned in ensure"
+ end
+
+ def raise_in_rescue_and_explicit_return_in_ensure
+ raise
+ rescue
+ raise
+ ensure
+ return "returned in ensure"
+ end
+
+ def raise_in_rescue_and_raise_in_ensure
+ raise
+ rescue
+ raise "raised in rescue"
+ ensure
+ raise "raised in ensure"
+ end
+
+ def raise_in_method_and_raise_in_ensure
+ raise
+ ensure
+ raise "raised in ensure"
+ end
+ end
+end
+
+module EnsureSpec
+
+ class Test
+
+ def initialize
+ @values = []
+ end
+
+ attr_reader :values
+
+ def call_block
+ begin
+ @values << :start
+ yield
+ ensure
+ @values << :end
+ end
+ end
+
+ def do_test
+ call_block do
+ @values << :in_block
+ return :did_test
+ end
+ end
+ end
+end
+
+module EnsureSpec
+ class Error < RuntimeError
+ end
+end
diff --git a/spec/ruby/language/fixtures/file.rb b/spec/ruby/language/fixtures/file.rb
new file mode 100644
index 0000000000..7b862cfe1a
--- /dev/null
+++ b/spec/ruby/language/fixtures/file.rb
@@ -0,0 +1 @@
+ScratchPad.record __FILE__
diff --git a/spec/ruby/language/fixtures/freeze_magic_comment_across_files.rb b/spec/ruby/language/fixtures/freeze_magic_comment_across_files.rb
new file mode 100644
index 0000000000..3aed2f29b6
--- /dev/null
+++ b/spec/ruby/language/fixtures/freeze_magic_comment_across_files.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+require_relative 'freeze_magic_comment_required'
+
+p "abc".object_id == $second_literal_id
diff --git a/spec/ruby/language/fixtures/freeze_magic_comment_across_files_diff_enc.rb b/spec/ruby/language/fixtures/freeze_magic_comment_across_files_diff_enc.rb
new file mode 100644
index 0000000000..53ef959970
--- /dev/null
+++ b/spec/ruby/language/fixtures/freeze_magic_comment_across_files_diff_enc.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+require_relative 'freeze_magic_comment_required_diff_enc'
+
+p "abc".object_id != $second_literal_id
diff --git a/spec/ruby/language/fixtures/freeze_magic_comment_across_files_no_comment.rb b/spec/ruby/language/fixtures/freeze_magic_comment_across_files_no_comment.rb
new file mode 100644
index 0000000000..fc6cd5bf82
--- /dev/null
+++ b/spec/ruby/language/fixtures/freeze_magic_comment_across_files_no_comment.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+require_relative 'freeze_magic_comment_required_no_comment'
+
+p "abc".object_id != $second_literal_id
diff --git a/spec/ruby/language/fixtures/freeze_magic_comment_one_literal.rb b/spec/ruby/language/fixtures/freeze_magic_comment_one_literal.rb
new file mode 100644
index 0000000000..d35905b332
--- /dev/null
+++ b/spec/ruby/language/fixtures/freeze_magic_comment_one_literal.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+ids = Array.new(2) { "abc".object_id }
+p ids.first == ids.last
diff --git a/spec/ruby/language/fixtures/freeze_magic_comment_required.rb b/spec/ruby/language/fixtures/freeze_magic_comment_required.rb
new file mode 100644
index 0000000000..a4ff4459b1
--- /dev/null
+++ b/spec/ruby/language/fixtures/freeze_magic_comment_required.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+$second_literal_id = "abc".object_id
diff --git a/spec/ruby/language/fixtures/freeze_magic_comment_required_diff_enc.rb b/spec/ruby/language/fixtures/freeze_magic_comment_required_diff_enc.rb
new file mode 100644
index 0000000000..d0558a2251
--- /dev/null
+++ b/spec/ruby/language/fixtures/freeze_magic_comment_required_diff_enc.rb
Binary files differ
diff --git a/spec/ruby/language/fixtures/freeze_magic_comment_required_no_comment.rb b/spec/ruby/language/fixtures/freeze_magic_comment_required_no_comment.rb
new file mode 100644
index 0000000000..e09232a5f4
--- /dev/null
+++ b/spec/ruby/language/fixtures/freeze_magic_comment_required_no_comment.rb
@@ -0,0 +1 @@
+$second_literal_id = "abc".object_id
diff --git a/spec/ruby/language/fixtures/freeze_magic_comment_two_literals.rb b/spec/ruby/language/fixtures/freeze_magic_comment_two_literals.rb
new file mode 100644
index 0000000000..a4d655ad02
--- /dev/null
+++ b/spec/ruby/language/fixtures/freeze_magic_comment_two_literals.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+p "abc".object_id == "abc".object_id
diff --git a/spec/ruby/language/fixtures/hash_strings_ascii8bit.rb b/spec/ruby/language/fixtures/hash_strings_ascii8bit.rb
new file mode 100644
index 0000000000..4ac11b9930
--- /dev/null
+++ b/spec/ruby/language/fixtures/hash_strings_ascii8bit.rb
@@ -0,0 +1,7 @@
+# encoding: ascii-8bit
+
+module HashStringsASCII8BIT
+ def self.literal_hash
+ {"foo" => "bar"}
+ end
+end
diff --git a/spec/ruby/language/fixtures/hash_strings_usascii.rb b/spec/ruby/language/fixtures/hash_strings_usascii.rb
new file mode 100644
index 0000000000..18cfef7c8c
--- /dev/null
+++ b/spec/ruby/language/fixtures/hash_strings_usascii.rb
@@ -0,0 +1,7 @@
+# encoding: us-ascii
+
+module HashStringsUSASCII
+ def self.literal_hash
+ {"foo" => "bar"}
+ end
+end
diff --git a/spec/ruby/language/fixtures/hash_strings_utf8.rb b/spec/ruby/language/fixtures/hash_strings_utf8.rb
new file mode 100644
index 0000000000..7928090282
--- /dev/null
+++ b/spec/ruby/language/fixtures/hash_strings_utf8.rb
@@ -0,0 +1,7 @@
+# encoding: utf-8
+
+module HashStringsUTF8
+ def self.literal_hash
+ {"foo" => "bar"}
+ end
+end
diff --git a/spec/ruby/language/fixtures/magic_comment.rb b/spec/ruby/language/fixtures/magic_comment.rb
new file mode 100644
index 0000000000..120ef6ff4a
--- /dev/null
+++ b/spec/ruby/language/fixtures/magic_comment.rb
@@ -0,0 +1,2 @@
+# encoding: big5
+$magic_comment_result = __ENCODING__.name
diff --git a/spec/ruby/language/fixtures/match_operators.rb b/spec/ruby/language/fixtures/match_operators.rb
new file mode 100644
index 0000000000..f04c54d723
--- /dev/null
+++ b/spec/ruby/language/fixtures/match_operators.rb
@@ -0,0 +1,9 @@
+class OperatorImplementor
+ def =~(val)
+ return val
+ end
+
+ def !~(val)
+ return val
+ end
+end
diff --git a/spec/ruby/language/fixtures/metaclass.rb b/spec/ruby/language/fixtures/metaclass.rb
new file mode 100644
index 0000000000..a8f837e701
--- /dev/null
+++ b/spec/ruby/language/fixtures/metaclass.rb
@@ -0,0 +1,33 @@
+module MetaClassSpecs
+
+ def self.metaclass_of obj
+ class << obj
+ self
+ end
+ end
+
+ class A
+ def self.cheese
+ 'edam'
+ end
+ end
+
+ class B < A
+ def self.cheese
+ 'stilton'
+ end
+ end
+
+ class C
+ class << self
+ class << self
+ def ham
+ 'iberico'
+ end
+ end
+ end
+ end
+
+ class D < C; end
+
+end
diff --git a/spec/ruby/language/fixtures/module.rb b/spec/ruby/language/fixtures/module.rb
new file mode 100644
index 0000000000..33d323846e
--- /dev/null
+++ b/spec/ruby/language/fixtures/module.rb
@@ -0,0 +1,24 @@
+module ModuleSpecs
+ module Modules
+ class Klass
+ end
+
+ A = "Module"
+ B = 1
+ C = nil
+ D = true
+ E = false
+ end
+
+ module Anonymous
+ end
+
+ module IncludedInObject
+ module IncludedModuleSpecs
+ end
+ end
+end
+
+class Object
+ include ModuleSpecs::IncludedInObject
+end
diff --git a/spec/ruby/language/fixtures/next.rb b/spec/ruby/language/fixtures/next.rb
new file mode 100644
index 0000000000..fbca842334
--- /dev/null
+++ b/spec/ruby/language/fixtures/next.rb
@@ -0,0 +1,134 @@
+class NextSpecs
+ def self.yielding_method(expected)
+ yield.should == expected
+ :method_return_value
+ end
+
+ def self.yielding
+ yield
+ end
+
+ # The methods below are defined to spec the behavior of the next statement
+ # while specifically isolating whether the statement is in an Iter block or
+ # not. In a normal spec example, the code is always nested inside a block.
+ # Rather than rely on that implicit context in this case, the context is
+ # made explicit because of the interaction of next in a loop nested inside
+ # an Iter block.
+ def self.while_next(arg)
+ x = true
+ while x
+ begin
+ ScratchPad << :begin
+ x = false
+ if arg
+ next 42
+ else
+ next
+ end
+ ensure
+ ScratchPad << :ensure
+ end
+ end
+ end
+
+ def self.while_within_iter(arg)
+ yielding do
+ x = true
+ while x
+ begin
+ ScratchPad << :begin
+ x = false
+ if arg
+ next 42
+ else
+ next
+ end
+ ensure
+ ScratchPad << :ensure
+ end
+ end
+ end
+ end
+
+ def self.until_next(arg)
+ x = false
+ until x
+ begin
+ ScratchPad << :begin
+ x = true
+ if arg
+ next 42
+ else
+ next
+ end
+ ensure
+ ScratchPad << :ensure
+ end
+ end
+ end
+
+ def self.until_within_iter(arg)
+ yielding do
+ x = false
+ until x
+ begin
+ ScratchPad << :begin
+ x = true
+ if arg
+ next 42
+ else
+ next
+ end
+ ensure
+ ScratchPad << :ensure
+ end
+ end
+ end
+ end
+
+ def self.loop_next(arg)
+ x = 1
+ loop do
+ break if x == 2
+
+ begin
+ ScratchPad << :begin
+ x += 1
+ if arg
+ next 42
+ else
+ next
+ end
+ ensure
+ ScratchPad << :ensure
+ end
+ end
+ end
+
+ def self.loop_within_iter(arg)
+ yielding do
+ x = 1
+ loop do
+ break if x == 2
+
+ begin
+ ScratchPad << :begin
+ x += 1
+ if arg
+ next 42
+ else
+ next
+ end
+ ensure
+ ScratchPad << :ensure
+ end
+ end
+ end
+ end
+
+ class Block
+ def method(v)
+ yield v
+ end
+ end
+end
diff --git a/spec/ruby/language/fixtures/no_magic_comment.rb b/spec/ruby/language/fixtures/no_magic_comment.rb
new file mode 100644
index 0000000000..743a0f9503
--- /dev/null
+++ b/spec/ruby/language/fixtures/no_magic_comment.rb
@@ -0,0 +1 @@
+$magic_comment_result = __ENCODING__.name
diff --git a/spec/ruby/language/fixtures/precedence.rb b/spec/ruby/language/fixtures/precedence.rb
new file mode 100644
index 0000000000..d2295c755b
--- /dev/null
+++ b/spec/ruby/language/fixtures/precedence.rb
@@ -0,0 +1,16 @@
+module PrecedenceSpecs
+ class NonUnaryOpTest
+ def add_num(arg)
+ [1].collect { |i| arg + i +1 }
+ end
+ def sub_num(arg)
+ [1].collect { |i| arg + i -1 }
+ end
+ def add_str
+ %w[1].collect { |i| i +'1' }
+ end
+ def add_var
+ [1].collect { |i| i +i }
+ end
+ end
+end
diff --git a/spec/ruby/language/fixtures/print_magic_comment_result_at_exit.rb b/spec/ruby/language/fixtures/print_magic_comment_result_at_exit.rb
new file mode 100644
index 0000000000..aa82cf4471
--- /dev/null
+++ b/spec/ruby/language/fixtures/print_magic_comment_result_at_exit.rb
@@ -0,0 +1,3 @@
+at_exit {
+ print $magic_comment_result
+}
diff --git a/spec/ruby/language/fixtures/private.rb b/spec/ruby/language/fixtures/private.rb
new file mode 100644
index 0000000000..96f73cea3f
--- /dev/null
+++ b/spec/ruby/language/fixtures/private.rb
@@ -0,0 +1,59 @@
+module Private
+ class A
+ def foo
+ "foo"
+ end
+
+ private
+ def bar
+ "bar"
+ end
+ end
+
+ class B
+ def foo
+ "foo"
+ end
+
+ private
+
+ def self.public_defs_method; 0; end
+
+ class C
+ def baz
+ "baz"
+ end
+ end
+
+ class << self
+ def public_class_method1; 1; end
+ private
+ def private_class_method1; 1; end
+ end
+
+ def bar
+ "bar"
+ end
+ end
+
+ module D
+ private
+ def foo
+ "foo"
+ end
+ end
+
+ class E
+ include D
+ end
+
+ class G
+ def foo
+ "foo"
+ end
+ end
+
+ class H < A
+ private :foo
+ end
+end
diff --git a/spec/ruby/language/fixtures/rescue.rb b/spec/ruby/language/fixtures/rescue.rb
new file mode 100644
index 0000000000..b906e17a2f
--- /dev/null
+++ b/spec/ruby/language/fixtures/rescue.rb
@@ -0,0 +1,67 @@
+module RescueSpecs
+ def self.begin_else(raise_exception)
+ begin
+ ScratchPad << :one
+ raise "an error occurred" if raise_exception
+ rescue
+ ScratchPad << :rescue_ran
+ :rescue_val
+ else
+ ScratchPad << :else_ran
+ :val
+ end
+ end
+
+ def self.begin_else_ensure(raise_exception)
+ begin
+ ScratchPad << :one
+ raise "an error occurred" if raise_exception
+ rescue
+ ScratchPad << :rescue_ran
+ :rescue_val
+ else
+ ScratchPad << :else_ran
+ :val
+ ensure
+ ScratchPad << :ensure_ran
+ :ensure_val
+ end
+ end
+
+ def self.begin_else_return(raise_exception)
+ begin
+ ScratchPad << :one
+ raise "an error occurred" if raise_exception
+ rescue
+ ScratchPad << :rescue_ran
+ :rescue_val
+ else
+ ScratchPad << :else_ran
+ :val
+ end
+ ScratchPad << :outside_begin
+ :return_val
+ end
+
+ def self.begin_else_return_ensure(raise_exception)
+ begin
+ ScratchPad << :one
+ raise "an error occurred" if raise_exception
+ rescue
+ ScratchPad << :rescue_ran
+ :rescue_val
+ else
+ ScratchPad << :else_ran
+ :val
+ ensure
+ ScratchPad << :ensure_ran
+ :ensure_val
+ end
+ ScratchPad << :outside_begin
+ :return_val
+ end
+
+ def self.raise_standard_error
+ raise StandardError, "an error occurred"
+ end
+end
diff --git a/spec/ruby/language/fixtures/return.rb b/spec/ruby/language/fixtures/return.rb
new file mode 100644
index 0000000000..0414c356e8
--- /dev/null
+++ b/spec/ruby/language/fixtures/return.rb
@@ -0,0 +1,139 @@
+module ReturnSpecs
+ class Blocks
+ def yielding_method
+ yield
+ ScratchPad.record :after_yield
+ end
+
+ def enclosing_method
+ yielding_method do
+ ScratchPad.record :before_return
+ return :return_value
+ ScratchPad.record :after_return
+ end
+
+ ScratchPad.record :after_call
+ end
+ end
+
+ class NestedCalls < Blocks
+ def invoking_method(&b)
+ yielding_method(&b)
+ ScratchPad.record :after_invoke
+ end
+
+ def enclosing_method
+ invoking_method do
+ ScratchPad.record :before_return
+ return :return_value
+ ScratchPad.record :after_return
+ end
+ ScratchPad.record :after_invoke
+ end
+ end
+
+ class NestedBlocks < Blocks
+ def enclosing_method
+ yielding_method do
+ yielding_method do
+ ScratchPad.record :before_return
+ return :return_value
+ ScratchPad.record :after_return
+ end
+ ScratchPad.record :after_invoke1
+ end
+ ScratchPad.record :after_invoke2
+ end
+ end
+
+ class SavedInnerBlock
+ def add(&b)
+ @block = b
+ end
+
+ def outer
+ yield
+ @block.call
+ end
+
+ def inner
+ yield
+ end
+
+ def start
+ outer do
+ inner do
+ add do
+ ScratchPad.record :before_return
+ return :return_value
+ end
+ end
+ end
+
+ ScratchPad.record :bottom_of_start
+
+ return false
+ end
+ end
+
+ class ThroughDefineMethod
+ lamb = proc { |x| x.call }
+ define_method :foo, lamb
+
+ def mp(&b); b; end
+
+ def outer
+ pr = mp { return :good }
+
+ foo(pr)
+ return :bad
+ end
+ end
+
+ class DefineMethod
+ lamb = proc { return :good }
+ define_method :foo, lamb
+
+ def outer
+ val = :bad
+
+ # This is tricky, but works. If lamb properly returns, then the
+ # return value will go into val before we run the ensure.
+ #
+ # If lamb's return keeps unwinding incorrectly, val will still
+ # have it's old value.
+ #
+ # We can therefore use val to figure out what happened.
+ begin
+ val = foo()
+ ensure
+ if val != :good
+ return :bad
+ end
+ end
+
+ return val
+ end
+ end
+
+ class MethodWithBlock
+ def method1
+ return [2, 3].inject 0 do |a, b|
+ a + b
+ end
+ nil
+ end
+
+ def get_ary(count, &blk)
+ count.times.to_a do |i|
+ blk.call(i) if blk
+ end
+ end
+
+ def method2
+ return get_ary 3 do |i|
+ end
+ nil
+ end
+ end
+end
diff --git a/spec/ruby/language/fixtures/second_line_magic_comment.rb b/spec/ruby/language/fixtures/second_line_magic_comment.rb
new file mode 100644
index 0000000000..a3dd50393b
--- /dev/null
+++ b/spec/ruby/language/fixtures/second_line_magic_comment.rb
@@ -0,0 +1,3 @@
+
+# encoding: big5
+$magic_comment_result = __ENCODING__.name
diff --git a/spec/ruby/language/fixtures/second_token_magic_comment.rb b/spec/ruby/language/fixtures/second_token_magic_comment.rb
new file mode 100644
index 0000000000..8d443e68f3
--- /dev/null
+++ b/spec/ruby/language/fixtures/second_token_magic_comment.rb
@@ -0,0 +1,2 @@
+1 + 1 # encoding: big5
+$magic_comment_result = __ENCODING__.name
diff --git a/spec/ruby/language/fixtures/send.rb b/spec/ruby/language/fixtures/send.rb
new file mode 100644
index 0000000000..c3013616b2
--- /dev/null
+++ b/spec/ruby/language/fixtures/send.rb
@@ -0,0 +1,140 @@
+module LangSendSpecs
+ module_function
+
+ def fooM0; 100 end
+ def fooM1(a); [a]; end
+ def fooM2(a,b); [a,b]; end
+ def fooM3(a,b,c); [a,b,c]; end
+ def fooM4(a,b,c,d); [a,b,c,d]; end
+ def fooM5(a,b,c,d,e); [a,b,c,d,e]; end
+ def fooM0O1(a=1); [a]; end
+ def fooM1O1(a,b=1); [a,b]; end
+ def fooM2O1(a,b,c=1); [a,b,c]; end
+ def fooM3O1(a,b,c,d=1); [a,b,c,d]; end
+ def fooM4O1(a,b,c,d,e=1); [a,b,c,d,e]; end
+ def fooM0O2(a=1,b=2); [a,b]; end
+ def fooM0R(*r); r; end
+ def fooM1R(a, *r); [a, r]; end
+ def fooM0O1R(a=1, *r); [a, r]; end
+ def fooM1O1R(a, b=1, *r); [a, b, r]; end
+
+ def one(a); a; end
+ def oneb(a,&b); [a,yield(b)]; end
+ def twob(a,b,&c); [a,b,yield(c)]; end
+ def makeproc(&b) b end
+
+ def yield_now; yield; end
+
+ def double(x); x * 2 end
+ def weird_parens
+ # means double((5).to_s)
+ # NOT (double(5)).to_s
+ double (5).to_s
+ end
+
+ def rest_len(*a); a.size; end
+
+ def self.twos(a,b,*c)
+ [c.size, c.last]
+ end
+
+ class PrivateSetter
+ attr_reader :foo
+ attr_writer :foo
+ private :foo=
+
+ def call_self_foo_equals(value)
+ self.foo = value
+ end
+
+ def call_self_foo_equals_masgn(value)
+ a, self.foo = 1, value
+ end
+ end
+
+ class PrivateGetter
+ attr_reader :foo
+ private :foo
+
+ def call_self_foo
+ self.foo
+ end
+
+ def call_self_foo_or_equals(value)
+ self.foo ||= 6
+ end
+ end
+
+ class AttrSet
+ attr_reader :result
+ def []=(a, b, c, d); @result = [a,b,c,d]; end
+ end
+
+ class ToProc
+ def initialize(val)
+ @val = val
+ end
+
+ def to_proc
+ Proc.new { @val }
+ end
+ end
+
+ class ToAry
+ def initialize(obj)
+ @obj = obj
+ end
+
+ def to_ary
+ @obj
+ end
+ end
+
+ class MethodMissing
+ def initialize
+ @message = nil
+ @args = nil
+ end
+
+ attr_reader :message, :args
+
+ def method_missing(m, *a)
+ @message = m
+ @args = a
+ end
+ end
+
+ class Attr19Set
+ attr_reader :result
+ def []=(*args); @result = args; end
+ end
+
+ module_function
+
+ def fooR(*r); r; end
+ def fooM0RQ1(*r, q); [r, q]; end
+ def fooM0RQ2(*r, s, q); [r, s, q]; end
+ def fooM1RQ1(a, *r, q); [a, r, q]; end
+ def fooM1O1RQ1(a, b=9, *r, q); [a, b, r, q]; end
+ def fooM1O1RQ2(a, b=9, *r, q, t); [a, b, r, q, t]; end
+
+ def fooO1Q1(a=1, b); [a,b]; end
+ def fooM1O1Q1(a,b=2,c); [a,b,c]; end
+ def fooM2O1Q1(a,b,c=3,d); [a,b,c,d]; end
+ def fooM2O2Q1(a,b,c=3,d=4,e); [a,b,c,d,e]; end
+ def fooO4Q1(a=1,b=2,c=3,d=4,e); [a,b,c,d,e]; end
+ def fooO4Q2(a=1,b=2,c=3,d=4,e,f); [a,b,c,d,e,f]; end
+
+ def destructure2((a,b)); a+b; end
+ def destructure2b((a,b)); [a,b]; end
+ def destructure4r((a,b,*c,d,e)); [a,b,c,d,e]; end
+ def destructure4o(a=1,(b,c),d,&e); [a,b,c,d]; end
+ def destructure5o(a=1, f=2, (b,c),d,&e); [a,f,b,c,d]; end
+ def destructure7o(a=1, f=2, (b,c),(d,e), &g); [a,f,b,c,d,e]; end
+ def destructure7b(a=1, f=2, (b,c),(d,e), &g); g.call([a,f,b,c,d,e]); end
+ def destructure4os(a=1,(b,*c)); [a,b,c]; end
+end
+
+def lang_send_rest_len(*a)
+ a.size
+end
diff --git a/spec/ruby/language/fixtures/shebang_magic_comment.rb b/spec/ruby/language/fixtures/shebang_magic_comment.rb
new file mode 100755
index 0000000000..f8e5e7d8e4
--- /dev/null
+++ b/spec/ruby/language/fixtures/shebang_magic_comment.rb
@@ -0,0 +1,3 @@
+#!/usr/bin/ruby
+# encoding: big5
+$magic_comment_result = __ENCODING__.name
diff --git a/spec/ruby/language/fixtures/squiggly_heredoc.rb b/spec/ruby/language/fixtures/squiggly_heredoc.rb
new file mode 100644
index 0000000000..afc87514c7
--- /dev/null
+++ b/spec/ruby/language/fixtures/squiggly_heredoc.rb
@@ -0,0 +1,39 @@
+module SquigglyHeredocSpecs
+ def self.message
+ <<~HEREDOC
+ character density, n.:
+ The number of very weird people in the office.
+ HEREDOC
+ end
+
+ def self.blank
+ <<~HERE
+ HERE
+ end
+
+ def self.unquoted
+ <<~HERE
+ unquoted #{"interpolated"}
+ HERE
+ end
+
+ def self.doublequoted
+ <<~"HERE"
+ doublequoted #{"interpolated"}
+ HERE
+ end
+
+ def self.singlequoted
+ <<~'HERE'
+ singlequoted #{"interpolated"}
+ HERE
+ end
+
+ def self.least_indented_on_the_last_line
+ <<~HERE
+ a
+ b
+ c
+ HERE
+ end
+end
diff --git a/spec/ruby/language/fixtures/super.rb b/spec/ruby/language/fixtures/super.rb
new file mode 100644
index 0000000000..e487b15354
--- /dev/null
+++ b/spec/ruby/language/fixtures/super.rb
@@ -0,0 +1,696 @@
+module Super
+ module S1
+ class A
+ def foo(a)
+ a << "A#foo"
+ bar(a)
+ end
+ def bar(a)
+ a << "A#bar"
+ end
+ end
+ class B < A
+ def foo(a)
+ a << "B#foo"
+ super(a)
+ end
+ def bar(a)
+ a << "B#bar"
+ super(a)
+ end
+ end
+ end
+
+ module S2
+ class A
+ def baz(a)
+ a << "A#baz"
+ end
+ end
+ class B < A
+ def foo(a)
+ a << "B#foo"
+ baz(a)
+ end
+ end
+ class C < B
+ def baz(a)
+ a << "C#baz"
+ super(a)
+ end
+ end
+ end
+
+ module S3
+ class A
+ def foo(a)
+ a << "A#foo"
+ end
+ def self.foo(a)
+ a << "A.foo"
+ end
+ def self.bar(a)
+ a << "A.bar"
+ foo(a)
+ end
+ end
+ class B < A
+ def self.foo(a)
+ a << "B.foo"
+ super(a)
+ end
+ def self.bar(a)
+ a << "B.bar"
+ super(a)
+ end
+ end
+ end
+
+ module S4
+ class A
+ def foo(a)
+ a << "A#foo"
+ end
+ end
+ class B < A
+ def foo(a, b)
+ a << "B#foo(a,#{b})"
+ super(a)
+ end
+ end
+ end
+
+ class S5
+ def here
+ :good
+ end
+ end
+
+ class S6 < S5
+ def under
+ yield
+ end
+
+ def here
+ under {
+ super
+ }
+ end
+ end
+
+ class S7 < S5
+ define_method(:here) { super() }
+ end
+
+ module MS1
+ module ModA
+ def foo(a)
+ a << "ModA#foo"
+ bar(a)
+ end
+ def bar(a)
+ a << "ModA#bar"
+ end
+ end
+ class A
+ include ModA
+ end
+ module ModB
+ def bar(a)
+ a << "ModB#bar"
+ super(a)
+ end
+ end
+ class B < A
+ def foo(a)
+ a << "B#foo"
+ super(a)
+ end
+ include ModB
+ end
+ end
+
+ module MS2
+ class A
+ def baz(a)
+ a << "A#baz"
+ end
+ end
+ module ModB
+ def foo(a)
+ a << "ModB#foo"
+ baz(a)
+ end
+ end
+ class B < A
+ include ModB
+ end
+ class C < B
+ def baz(a)
+ a << "C#baz"
+ super(a)
+ end
+ end
+ end
+
+ module MultiSuperTargets
+ module M
+ def foo
+ super
+ end
+ end
+
+ class BaseA
+ def foo
+ :BaseA
+ end
+ end
+
+ class BaseB
+ def foo
+ :BaseB
+ end
+ end
+
+ class A < BaseA
+ include M
+ end
+
+ class B < BaseB
+ include M
+ end
+ end
+
+ module MS3
+ module ModA
+ def foo(a)
+ a << "ModA#foo"
+ end
+ def bar(a)
+ a << "ModA#bar"
+ foo(a)
+ end
+ end
+ class A
+ def foo(a)
+ a << "A#foo"
+ end
+ class << self
+ include ModA
+ end
+ end
+ class B < A
+ def self.foo(a)
+ a << "B.foo"
+ super(a)
+ end
+ def self.bar(a)
+ a << "B.bar"
+ super(a)
+ end
+ end
+ end
+
+ module MS4
+ module Layer1
+ def example
+ 5
+ end
+ end
+
+ module Layer2
+ include Layer1
+ def example
+ super
+ end
+ end
+
+ class A
+ include Layer2
+ public :example
+ end
+ end
+
+ class MM_A
+ undef_method :is_a?
+ end
+
+ class MM_B < MM_A
+ def is_a?(blah)
+ # should fire the method_missing below
+ super
+ end
+
+ def method_missing(*)
+ false
+ end
+ end
+
+ class Alias1
+ def name
+ [:alias1]
+ end
+ end
+
+ class Alias2 < Alias1
+ def initialize
+ @times = 0
+ end
+
+ def name
+ if @times >= 10
+ raise "runaway super"
+ end
+
+ @times += 1
+
+ # Use this so that we can see collect all supers that we see.
+ # One bug that arises is that we call Alias2#name from Alias2#name
+ # as it's superclass. In that case, either we get a runaway recursion
+ # super OR we get the return value being [:alias2, :alias2, :alias1]
+ # rather than [:alias2, :alias1].
+ #
+ # Which one depends on caches and how super is implemented.
+ [:alias2] + super
+ end
+ end
+
+ class Alias3 < Alias2
+ alias_method :name3, :name
+ # In the method table for Alias3 now should be a special alias entry
+ # that references Alias2 and Alias2#name (probably as an object).
+ #
+ # When name3 is called then, Alias2 (NOT Alias3) is presented as the
+ # current module to Alias2#name, so that when super is called,
+ # Alias2->superclass is next.
+ #
+ # Otherwise, Alias2 is next, which is where name was to begin with,
+ # causing the wrong #name method to be called.
+ end
+
+ module AliasWithSuper
+ module AS1
+ def foo
+ :a
+ end
+ end
+
+ module BS1
+ def foo
+ [:b, super]
+ end
+ end
+
+ class Base
+ extend AS1
+ extend BS1
+ end
+
+ class Trigger < Base
+ class << self
+ def foo_quux
+ foo_baz
+ end
+
+ alias_method :foo_baz, :foo
+ alias_method :foo, :foo_quux
+ end
+ end
+ end
+
+ module RestArgsWithSuper
+ class A
+ def a(*args)
+ args
+ end
+ end
+
+ class B < A
+ def a(*args)
+ args << "foo"
+
+ super
+ end
+ end
+ end
+
+ class AnonymousModuleIncludedTwiceBase
+ def self.whatever
+ mod = Module.new do
+ def a(array)
+ array << "anon"
+ super
+ end
+ end
+
+ include mod
+ end
+
+ def a(array)
+ array << "non-anon"
+ end
+ end
+
+ class AnonymousModuleIncludedTwice < AnonymousModuleIncludedTwiceBase
+ whatever
+ whatever
+ end
+
+ module ZSuperWithBlock
+ class A
+ def a
+ yield
+ end
+
+ def b(&block)
+ block.call
+ end
+
+ def c
+ yield
+ end
+ end
+
+ class B < A
+ def a
+ super { 14 }
+ end
+
+ def b
+ block_ref = lambda { 15 }
+ [super { 14 }, super(&block_ref)]
+ end
+
+ def c
+ block_ref = lambda { 16 }
+ super(&block_ref)
+ end
+ end
+ end
+
+ module ZSuperWithOptional
+ class A
+ def m(x, y, z)
+ z
+ end
+ end
+
+ class B < A
+ def m(x, y, z = 14)
+ super
+ end
+ end
+
+ class C < A
+ def m(x, y, z = 14)
+ z = 100
+ super
+ end
+ end
+ end
+
+ module ZSuperWithRest
+ class A
+ def m(*args)
+ args
+ end
+
+ def m_modified(*args)
+ args
+ end
+ end
+
+ class B < A
+ def m(*args)
+ super
+ end
+
+ def m_modified(*args)
+ args[1] = 14
+ super
+ end
+ end
+ end
+
+ module ZSuperWithRestAndOthers
+ class A
+ def m(a, b, *args)
+ args
+ end
+
+ def m_modified(a, b, *args)
+ args
+ end
+ end
+
+ class B < A
+ def m(a, b, *args)
+ super
+ end
+
+ def m_modified(a, b, *args)
+ args[1] = 14
+ super
+ end
+ end
+ end
+
+ module ZSuperWithRestReassigned
+ class A
+ def a(*args)
+ args
+ end
+ end
+
+ class B < A
+ def a(*args)
+ args = ["foo"]
+
+ super
+ end
+ end
+ end
+
+ module ZSuperWithRestReassignedWithScalar
+ class A
+ def a(*args)
+ args
+ end
+ end
+
+ class B < A
+ def a(*args)
+ args = "foo"
+
+ super
+ end
+ end
+ end
+
+ module ZSuperWithUnderscores
+ class A
+ def m(*args)
+ args
+ end
+
+ def m_modified(*args)
+ args
+ end
+ end
+
+ class B < A
+ def m(_, _)
+ super
+ end
+
+ def m_modified(_, _)
+ _ = 14
+ super
+ end
+ end
+ end
+
+ module Keywords
+ class Arguments
+ def foo(**args)
+ args
+ end
+ end
+
+ # ----
+
+ class RequiredArguments < Arguments
+ def foo(a:)
+ super
+ end
+ end
+
+ class OptionalArguments < Arguments
+ def foo(b: 'b')
+ super
+ end
+ end
+
+ class PlaceholderArguments < Arguments
+ def foo(**args)
+ super
+ end
+ end
+
+ # ----
+
+ class RequiredAndOptionalArguments < Arguments
+ def foo(a:, b: 'b')
+ super
+ end
+ end
+
+ class RequiredAndPlaceholderArguments < Arguments
+ def foo(a:, **args)
+ super
+ end
+ end
+
+ class OptionalAndPlaceholderArguments < Arguments
+ def foo(b: 'b', **args)
+ super
+ end
+ end
+
+ # ----
+
+ class RequiredAndOptionalAndPlaceholderArguments < Arguments
+ def foo(a:, b: 'b', **args)
+ super
+ end
+ end
+ end
+
+ module RegularAndKeywords
+ class Arguments
+ def foo(a, **options)
+ [a, options]
+ end
+ end
+
+ # -----
+
+ class RequiredArguments < Arguments
+ def foo(a, b:)
+ super
+ end
+ end
+
+ class OptionalArguments < Arguments
+ def foo(a, c: 'c')
+ super
+ end
+ end
+
+ class PlaceholderArguments < Arguments
+ def foo(a, **options)
+ super
+ end
+ end
+
+ # -----
+
+ class RequiredAndOptionalArguments < Arguments
+ def foo(a, b:, c: 'c')
+ super
+ end
+ end
+
+ class RequiredAndPlaceholderArguments < Arguments
+ def foo(a, b:, **options)
+ super
+ end
+ end
+
+ class OptionalAndPlaceholderArguments < Arguments
+ def foo(a, c: 'c', **options)
+ super
+ end
+ end
+
+ # -----
+
+ class RequiredAndOptionalAndPlaceholderArguments < Arguments
+ def foo(a, b:, c: 'c', **options)
+ super
+ end
+ end
+ end
+
+ module SplatAndKeywords
+ class Arguments
+ def foo(*args, **options)
+ [args, options]
+ end
+ end
+
+ class AllArguments < Arguments
+ def foo(*args, **options)
+ super
+ end
+ end
+ end
+
+ module FromBasicObject
+ def __send__(name, *args, &block)
+ super
+ end
+ end
+
+ module IntermediateBasic
+ include FromBasicObject
+ end
+
+ class IncludesFromBasic
+ include FromBasicObject
+
+ def foobar; 43; end
+ end
+
+ class IncludesIntermediate
+ include IntermediateBasic
+
+ def foobar; 42; end
+ end
+
+ module SingletonCase
+ class Base
+ def foobar(array)
+ array << :base
+ end
+ end
+
+ class Foo < Base
+ def foobar(array)
+ array << :foo
+ super
+ end
+ end
+ end
+
+ module SingletonAliasCase
+ class Base
+ def foobar(array)
+ array << :base
+ end
+
+ def alias_on_singleton
+ object = self
+ singleton = (class << object; self; end)
+ singleton.__send__(:alias_method, :new_foobar, :foobar)
+ end
+ end
+
+ class Foo < Base
+ def foobar(array)
+ array << :foo
+ super
+ end
+ end
+ end
+end
diff --git a/spec/ruby/language/fixtures/utf16-be-nobom.rb b/spec/ruby/language/fixtures/utf16-be-nobom.rb
new file mode 100644
index 0000000000..99e2ce8ce8
--- /dev/null
+++ b/spec/ruby/language/fixtures/utf16-be-nobom.rb
Binary files differ
diff --git a/spec/ruby/language/fixtures/utf16-le-nobom.rb b/spec/ruby/language/fixtures/utf16-le-nobom.rb
new file mode 100644
index 0000000000..98de9697ca
--- /dev/null
+++ b/spec/ruby/language/fixtures/utf16-le-nobom.rb
Binary files differ
diff --git a/spec/ruby/language/fixtures/utf8-bom.rb b/spec/ruby/language/fixtures/utf8-bom.rb
new file mode 100644
index 0000000000..50c223a922
--- /dev/null
+++ b/spec/ruby/language/fixtures/utf8-bom.rb
@@ -0,0 +1,2 @@
+# encoding: utf-8
+puts 'hello'
diff --git a/spec/ruby/language/fixtures/utf8-nobom.rb b/spec/ruby/language/fixtures/utf8-nobom.rb
new file mode 100644
index 0000000000..75f5563b95
--- /dev/null
+++ b/spec/ruby/language/fixtures/utf8-nobom.rb
@@ -0,0 +1,2 @@
+# encoding: utf-8
+puts 'hello'
diff --git a/spec/ruby/language/fixtures/variables.rb b/spec/ruby/language/fixtures/variables.rb
new file mode 100644
index 0000000000..07265dbb2b
--- /dev/null
+++ b/spec/ruby/language/fixtures/variables.rb
@@ -0,0 +1,85 @@
+module VariablesSpecs
+ class ParAsgn
+ attr_accessor :x
+
+ def initialize
+ @x = 0
+ end
+
+ def inc
+ @x += 1
+ end
+
+ def to_ary
+ [1,2,3,4]
+ end
+ end
+
+ class OpAsgn
+ attr_accessor :a, :b, :side_effect
+
+ def do_side_effect
+ self.side_effect = true
+ return @a
+ end
+
+ def do_more_side_effects
+ @a += 5
+ self
+ end
+
+ def do_bool_side_effects
+ @b += 1
+ self
+ end
+ end
+
+ class Hashalike
+ def [](k) k end
+ def []=(k, v) [k, v] end
+ end
+
+ def self.reverse_foo(a, b)
+ return b, a
+ end
+
+ class ArrayLike
+ def initialize(array)
+ @array = array
+ end
+
+ def to_a
+ @array
+ end
+ end
+
+ class ArraySubclass < Array
+ end
+
+ class PrivateMethods
+ private
+
+ def to_ary
+ [1, 2]
+ end
+
+ def to_a
+ [3, 4]
+ end
+ end
+
+ class ToAryNil
+ def to_ary
+ end
+ end
+
+ class Chain
+ def self.without_parenthesis a
+ a
+ end
+ end
+
+ def self.false
+ false
+ end
+end
diff --git a/spec/ruby/language/fixtures/vim_magic_comment.rb b/spec/ruby/language/fixtures/vim_magic_comment.rb
new file mode 100644
index 0000000000..60cbe7a3bf
--- /dev/null
+++ b/spec/ruby/language/fixtures/vim_magic_comment.rb
@@ -0,0 +1,2 @@
+# vim: filetype=ruby, fileencoding=big5, tabsize=3, shiftwidth=3
+$magic_comment_result = __ENCODING__.name
diff --git a/spec/ruby/language/fixtures/yield.rb b/spec/ruby/language/fixtures/yield.rb
new file mode 100644
index 0000000000..a195616640
--- /dev/null
+++ b/spec/ruby/language/fixtures/yield.rb
@@ -0,0 +1,37 @@
+module YieldSpecs
+ class Yielder
+ def z
+ yield
+ end
+
+ def ze(&block)
+ block = proc { block }
+ yield
+ end
+
+ def s(a)
+ yield(a)
+ end
+
+ def m(a, b, c)
+ yield(a, b, c)
+ end
+
+ def r(a)
+ yield(*a)
+ end
+
+ def rs(a, b, c)
+ yield(a, b, *c)
+ end
+
+ def self.define_deep(&inned_block)
+ define_method 'deep' do |v|
+ # should yield to inner_block
+ yield v
+ end
+ end
+
+ define_deep { |v| v * 2}
+ end
+end
diff --git a/spec/ruby/language/for_spec.rb b/spec/ruby/language/for_spec.rb
new file mode 100644
index 0000000000..b0b5fab7ec
--- /dev/null
+++ b/spec/ruby/language/for_spec.rb
@@ -0,0 +1,177 @@
+require_relative '../spec_helper'
+
+# for name[, name]... in expr [do]
+# body
+# end
+describe "The for expression" do
+ it "iterates over an Enumerable passing each element to the block" do
+ j = 0
+ for i in 1..3
+ j += i
+ end
+ j.should == 6
+ end
+
+ it "iterates over a list of arrays and destructures with empty comma" do
+ for i, in [[1,2]]
+ i.should == 1
+ end
+ end
+
+ it "iterates over an Hash passing each key-value pair to the block" do
+ k = 0
+ l = 0
+
+ for i, j in { 1 => 10, 2 => 20 }
+ k += i
+ l += j
+ end
+
+ k.should == 3
+ l.should == 30
+ end
+
+ it "iterates over any object responding to 'each'" do
+ class XYZ
+ def each
+ (0..10).each { |i| yield i }
+ end
+ end
+
+ j = 0
+ for i in XYZ.new
+ j += i
+ end
+ j.should == 55
+ end
+
+ it "allows an instance variable as an iterator name" do
+ m = [1,2,3]
+ n = 0
+ for @var in m
+ n += 1
+ end
+ @var.should == 3
+ n.should == 3
+ end
+
+ it "allows a class variable as an iterator name" do
+ class OFor
+ m = [1,2,3]
+ n = 0
+ for @@var in m
+ n += 1
+ end
+ @@var.should == 3
+ n.should == 3
+ end
+ end
+
+ it "allows a constant as an iterator name" do
+ class OFor
+ m = [1,2,3]
+ n = 0
+ -> {
+ for CONST in m
+ n += 1
+ end
+ }.should complain(/already initialized constant/)
+ CONST.should == 3
+ n.should == 3
+ end
+ end
+
+ # 1.9 behaviour verified by nobu in
+ # http://redmine.ruby-lang.org/issues/show/2053
+ it "yields only as many values as there are arguments" do
+ class OFor
+ def each
+ [[1,2,3], [4,5,6]].each do |a|
+ yield(a[0],a[1],a[2])
+ end
+ end
+ end
+ o = OFor.new
+ qs = []
+ for q in o
+ qs << q
+ end
+ qs.should == [1, 4]
+ q.should == 4
+ end
+
+ it "optionally takes a 'do' after the expression" do
+ j = 0
+ for i in 1..3 do
+ j += i
+ end
+ j.should == 6
+ end
+
+ it "allows body begin on the same line if do is used" do
+ j = 0
+ for i in 1..3 do j += i
+ end
+ j.should == 6
+ end
+
+ it "executes code in containing variable scope" do
+ for i in 1..2
+ a = 123
+ end
+
+ a.should == 123
+ end
+
+ it "executes code in containing variable scope with 'do'" do
+ for i in 1..2 do
+ a = 123
+ end
+
+ a.should == 123
+ end
+
+ it "returns expr" do
+ for i in 1..3; end.should == (1..3)
+ for i,j in { 1 => 10, 2 => 20 }; end.should == { 1 => 10, 2 => 20 }
+ end
+
+ it "breaks out of a loop upon 'break', returning nil" do
+ j = 0
+ for i in 1..3
+ j += i
+
+ break if i == 2
+ end.should == nil
+
+ j.should == 3
+ end
+
+ it "allows 'break' to have an argument which becomes the value of the for expression" do
+ for i in 1..3
+ break 10 if i == 2
+ end.should == 10
+ end
+
+ it "starts the next iteration with 'next'" do
+ j = 0
+ for i in 1..5
+ next if i == 2
+
+ j += i
+ end
+
+ j.should == 13
+ end
+
+ it "repeats current iteration with 'redo'" do
+ j = 0
+ for i in 1..3
+ j += i
+
+ redo if i == 2 && j < 4
+ end
+
+ j.should == 8
+ end
+end
diff --git a/spec/ruby/language/hash_spec.rb b/spec/ruby/language/hash_spec.rb
new file mode 100644
index 0000000000..475b1094ab
--- /dev/null
+++ b/spec/ruby/language/hash_spec.rb
@@ -0,0 +1,154 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/hash_strings_ascii8bit'
+require_relative 'fixtures/hash_strings_utf8'
+require_relative 'fixtures/hash_strings_usascii'
+
+describe "Hash literal" do
+ it "{} should return an empty hash" do
+ {}.size.should == 0
+ {}.should == {}
+ end
+
+ it "{} should return a new hash populated with the given elements" do
+ h = {a: 'a', 'b' => 3, 44 => 2.3}
+ h.size.should == 3
+ h.should == {a: "a", "b" => 3, 44 => 2.3}
+ end
+
+ it "treats empty expressions as nils" do
+ h = {() => ()}
+ h.keys.should == [nil]
+ h.values.should == [nil]
+ h[nil].should == nil
+
+ h = {() => :value}
+ h.keys.should == [nil]
+ h.values.should == [:value]
+ h[nil].should == :value
+
+ h = {key: ()}
+ h.keys.should == [:key]
+ h.values.should == [nil]
+ h[:key].should == nil
+ end
+
+ it "freezes string keys on initialization" do
+ key = "foo"
+ h = {key => "bar"}
+ key.reverse!
+ h["foo"].should == "bar"
+ h.keys.first.should == "foo"
+ h.keys.first.frozen?.should == true
+ key.should == "oof"
+ end
+
+ it "checks duplicated keys on initialization" do
+ -> {
+ @h = eval "{foo: :bar, foo: :foo}"
+ }.should complain(/key :foo is duplicated|duplicated key/)
+ @h.keys.size.should == 1
+ @h.should == {foo: :foo}
+ end
+
+ it "accepts a hanging comma" do
+ h = {a: 1, b: 2,}
+ h.size.should == 2
+ h.should == {a: 1, b: 2}
+ end
+
+ it "recognizes '=' at the end of the key" do
+ eval("{:a==>1}").should == {:"a=" => 1}
+ eval("{:a= =>1}").should == {:"a=" => 1}
+ eval("{:a= => 1}").should == {:"a=" => 1}
+ end
+
+ it "with '==>' in the middle raises SyntaxError" do
+ lambda { eval("{:a ==> 1}") }.should raise_error(SyntaxError)
+ end
+
+ it "constructs a new hash with the given elements" do
+ {foo: 123}.should == {foo: 123}
+ h = {rbx: :cool, specs: 'fail_sometimes'}
+ {rbx: :cool, specs: 'fail_sometimes'}.should == h
+ end
+
+ it "ignores a hanging comma" do
+ {foo: 123,}.should == {foo: 123}
+ h = {rbx: :cool, specs: 'fail_sometimes'}
+ {rbx: :cool, specs: 'fail_sometimes',}.should == h
+ end
+
+ it "accepts mixed 'key: value' and 'key => value' syntax" do
+ h = {:a => 1, :b => 2, "c" => 3}
+ {a: 1, b: 2, "c" => 3}.should == h
+ end
+
+ it "accepts mixed 'key: value', 'key => value' and '\"key\"': value' syntax" do
+ h = {:a => 1, :b => 2, "c" => 3, :d => 4}
+ eval('{a: 1, :b => 2, "c" => 3, "d": 4}').should == h
+ end
+
+ it "expands an '**{}' element into the containing Hash literal initialization" do
+ {a: 1, **{b: 2}, c: 3}.should == {a: 1, b: 2, c: 3}
+ end
+
+ it "expands an '**obj' element into the containing Hash literal initialization" do
+ h = {b: 2, c: 3}
+ {**h, a: 1}.should == {b: 2, c: 3, a: 1}
+ {a: 1, **h}.should == {a: 1, b: 2, c: 3}
+ {a: 1, **h, c: 4}.should == {a: 1, b: 2, c: 4}
+ end
+
+ it "expands a BasicObject using ** into the containing Hash literal initialization" do
+ h = BasicObject.new
+ def h.to_hash; {:b => 2, :c => 3}; end
+ {**h, a: 1}.should == {b: 2, c: 3, a: 1}
+ {a: 1, **h}.should == {a: 1, b: 2, c: 3}
+ {a: 1, **h, c: 4}.should == {a: 1, b: 2, c: 4}
+ end
+
+ it "expands an '**{}' element with the last key/value pair taking precedence" do
+ -> {
+ @h = eval "{a: 1, **{a: 2, b: 3, c: 1}, c: 3}"
+ }.should complain(/key :a is duplicated|duplicated key/)
+ @h.should == {a: 2, b: 3, c: 3}
+ end
+
+ it "merges multiple nested '**obj' in Hash literals" do
+ -> {
+ @h = eval "{a: 1, **{a: 2, **{b: 3, **{c: 4}}, **{d: 5}, }, **{d: 6}}"
+ }.should complain(/key :a is duplicated|duplicated key/)
+ @h.should == {a: 2, b: 3, c: 4, d: 6}
+ end
+
+ it "calls #to_hash to expand an '**obj' element" do
+ obj = mock("hash splat")
+ obj.should_receive(:to_hash).and_return({b: 2, d: 4})
+
+ {a: 1, **obj, c: 3}.should == {a:1, b: 2, c: 3, d: 4}
+ end
+
+ it "raises a TypeError if any splatted elements keys are not symbols" do
+ h = {1 => 2, b: 3}
+ lambda { {a: 1, **h} }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if #to_hash does not return a Hash" do
+ obj = mock("hash splat")
+ obj.should_receive(:to_hash).and_return(obj)
+
+ lambda { {**obj} }.should raise_error(TypeError)
+ end
+
+ it "does not change encoding of literal string keys during creation" do
+ ascii8bit_hash = HashStringsASCII8BIT.literal_hash
+ utf8_hash = HashStringsUTF8.literal_hash
+ usascii_hash = HashStringsUSASCII.literal_hash
+
+ ascii8bit_hash.keys.first.encoding.should == Encoding::ASCII_8BIT
+ ascii8bit_hash.keys.first.should == utf8_hash.keys.first
+ utf8_hash.keys.first.encoding.should == Encoding::UTF_8
+ utf8_hash.keys.first.should == usascii_hash.keys.first
+ usascii_hash.keys.first.encoding.should == Encoding::US_ASCII
+ end
+end
diff --git a/spec/ruby/language/heredoc_spec.rb b/spec/ruby/language/heredoc_spec.rb
new file mode 100644
index 0000000000..09a04a3cb1
--- /dev/null
+++ b/spec/ruby/language/heredoc_spec.rb
@@ -0,0 +1,85 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../spec_helper'
+
+describe "Heredoc string" do
+
+ before :each do
+ @ip = 'xxx' # used for interpolation
+ end
+
+ it "allows HEREDOC with <<identifier, interpolated" do
+ s = <<HERE
+foo bar#{@ip}
+HERE
+ s.should == "foo barxxx\n"
+ end
+
+ it 'allow HEREDOC with <<"identifier", interpolated' do
+ s = <<"HERE"
+foo bar#{@ip}
+HERE
+ s.should == "foo barxxx\n"
+ end
+
+ it "allows HEREDOC with <<'identifier', no interpolation" do
+ s = <<'HERE'
+foo bar#{@ip}
+HERE
+ s.should == 'foo bar#{@ip}' + "\n"
+ end
+
+ it "allows HEREDOC with <<-identifier, allowing to indent identifier, interpolated" do
+ s = <<-HERE
+ foo bar#{@ip}
+ HERE
+
+ s.should == " foo barxxx\n"
+ end
+
+ it 'allows HEREDOC with <<-"identifier", allowing to indent identifier, interpolated' do
+ s = <<-"HERE"
+ foo bar#{@ip}
+ HERE
+
+ s.should == " foo barxxx\n"
+ end
+
+ it "allows HEREDOC with <<-'identifier', allowing to indent identifier, no interpolation" do
+ s = <<-'HERE'
+ foo bar#{@ip}
+ HERE
+
+ s.should == ' foo bar#{@ip}' + "\n"
+ end
+
+ it "allows HEREDOC with <<~'identifier', allowing to indent identifier and content" do
+ require_relative 'fixtures/squiggly_heredoc'
+ SquigglyHeredocSpecs.message.should == "character density, n.:\n The number of very weird people in the office.\n"
+ end
+
+ it "trims trailing newline character for blank HEREDOC with <<~'identifier'" do
+ require_relative 'fixtures/squiggly_heredoc'
+ SquigglyHeredocSpecs.blank.should == ""
+ end
+
+ it 'allows HEREDOC with <<~identifier, interpolated' do
+ require_relative 'fixtures/squiggly_heredoc'
+ SquigglyHeredocSpecs.unquoted.should == "unquoted interpolated\n"
+ end
+
+ it 'allows HEREDOC with <<~"identifier", interpolated' do
+ require_relative 'fixtures/squiggly_heredoc'
+ SquigglyHeredocSpecs.doublequoted.should == "doublequoted interpolated\n"
+ end
+
+ it "allows HEREDOC with <<~'identifier', no interpolation" do
+ require_relative 'fixtures/squiggly_heredoc'
+ SquigglyHeredocSpecs.singlequoted.should == "singlequoted \#{\"interpolated\"}\n"
+ end
+
+ it "selects the least-indented line and removes its indentation from all the lines" do
+ require_relative 'fixtures/squiggly_heredoc'
+ SquigglyHeredocSpecs.least_indented_on_the_last_line.should == " a\n b\nc\n"
+ end
+end
diff --git a/spec/ruby/language/if_spec.rb b/spec/ruby/language/if_spec.rb
new file mode 100644
index 0000000000..fe207e77d5
--- /dev/null
+++ b/spec/ruby/language/if_spec.rb
@@ -0,0 +1,376 @@
+require_relative '../spec_helper'
+
+describe "The if expression" do
+ ruby_version_is '2.4' do
+ describe "accepts multiple assignments in conditional expression" do
+ before(:each) { ScratchPad.record([]) }
+ after(:each) { ScratchPad.clear }
+
+ it 'with non-nil values' do
+ ary = [1, 2]
+ eval "if (a, b = ary); ScratchPad.record [a, b]; end"
+ ScratchPad.recorded.should == [1, 2]
+ end
+
+ it 'with nil values' do
+ ary = nil
+ eval "if (a, b = ary); else; ScratchPad.record [a, b]; end"
+ ScratchPad.recorded.should == [nil, nil]
+ end
+ end
+ end
+
+ it "evaluates body if expression is true" do
+ a = []
+ if true
+ a << 123
+ end
+ a.should == [123]
+ end
+
+ it "does not evaluate body if expression is false" do
+ a = []
+ if false
+ a << 123
+ end
+ a.should == []
+ end
+
+ it "does not evaluate body if expression is empty" do
+ a = []
+ if ()
+ a << 123
+ end
+ a.should == []
+ end
+
+ it "does not evaluate else-body if expression is true" do
+ a = []
+ if true
+ a << 123
+ else
+ a << 456
+ end
+ a.should == [123]
+ end
+
+ it "evaluates only else-body if expression is false" do
+ a = []
+ if false
+ a << 123
+ else
+ a << 456
+ end
+ a.should == [456]
+ end
+
+ it "returns result of then-body evaluation if expression is true" do
+ if true
+ 123
+ end.should == 123
+ end
+
+ it "returns result of last statement in then-body if expression is true" do
+ if true
+ 'foo'
+ 'bar'
+ 'baz'
+ end.should == 'baz'
+ end
+
+ it "returns result of then-body evaluation if expression is true and else part is present" do
+ if true
+ 123
+ else
+ 456
+ end.should == 123
+ end
+
+ it "returns result of else-body evaluation if expression is false" do
+ if false
+ 123
+ else
+ 456
+ end.should == 456
+ end
+
+ it "returns nil if then-body is empty and expression is true" do
+ if true
+ end.should == nil
+ end
+
+ it "returns nil if then-body is empty, expression is true and else part is present" do
+ if true
+ else
+ 456
+ end.should == nil
+ end
+
+ it "returns nil if then-body is empty, expression is true and else part is empty" do
+ if true
+ else
+ end.should == nil
+ end
+
+ it "returns nil if else-body is empty and expression is false" do
+ if false
+ 123
+ else
+ end.should == nil
+ end
+
+ it "returns nil if else-body is empty, expression is false and then-body is empty" do
+ if false
+ else
+ end.should == nil
+ end
+
+ it "considers an expression with nil result as false" do
+ if nil
+ 123
+ else
+ 456
+ end.should == 456
+ end
+
+ it "considers a non-nil and non-boolean object in expression result as true" do
+ if mock('x')
+ 123
+ else
+ 456
+ end.should == 123
+ end
+
+ it "considers a zero integer in expression result as true" do
+ if 0
+ 123
+ else
+ 456
+ end.should == 123
+ end
+
+ it "allows starting else-body on the same line" do
+ if false
+ 123
+ else 456
+ end.should == 456
+ end
+
+ it "evaluates subsequent elsif statements and execute body of first matching" do
+ if false
+ 123
+ elsif false
+ 234
+ elsif true
+ 345
+ elsif true
+ 456
+ end.should == 345
+ end
+
+ it "evaluates else-body if no if/elsif statements match" do
+ if false
+ 123
+ elsif false
+ 234
+ elsif false
+ 345
+ else
+ 456
+ end.should == 456
+ end
+
+ it "allows 'then' after expression when then-body is on the next line" do
+ if true then
+ 123
+ end.should == 123
+
+ if true then ; 123; end.should == 123
+ end
+
+ it "allows then-body on the same line separated with 'then'" do
+ if true then 123
+ end.should == 123
+
+ if true then 123; end.should == 123
+ end
+
+ it "returns nil when then-body on the same line separated with 'then' and expression is false" do
+ if false then 123
+ end.should == nil
+
+ if false then 123; end.should == nil
+ end
+
+ it "returns nil when then-body separated by 'then' is empty and expression is true" do
+ if true then
+ end.should == nil
+
+ if true then ; end.should == nil
+ end
+
+ it "returns nil when then-body separated by 'then', expression is false and no else part" do
+ if false then
+ end.should == nil
+
+ if false then ; end.should == nil
+ end
+
+ it "evaluates then-body when then-body separated by 'then', expression is true and else part is present" do
+ if true then 123
+ else 456
+ end.should == 123
+
+ if true then 123; else 456; end.should == 123
+ end
+
+ it "evaluates else-body when then-body separated by 'then' and expression is false" do
+ if false then 123
+ else 456
+ end.should == 456
+
+ if false then 123; else 456; end.should == 456
+ end
+
+ describe "with a boolean range ('flip-flop' operator)" do
+ before :each do
+ ScratchPad.record []
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ after :each do
+ ScratchPad.clear
+ $VERBOSE = @verbose
+ end
+
+ it "mimics an awk conditional with a single-element inclusive-end range" do
+ eval "10.times { |i| ScratchPad << i if (i == 4)..(i == 4) }"
+ ScratchPad.recorded.should == [4]
+ end
+
+ it "mimics an awk conditional with a many-element inclusive-end range" do
+ eval "10.times { |i| ScratchPad << i if (i == 4)..(i == 7) }"
+ ScratchPad.recorded.should == [4, 5, 6, 7]
+ end
+
+ it "mimics a sed conditional with a zero-element exclusive-end range" do
+ eval "10.times { |i| ScratchPad << i if (i == 4)...(i == 4) }"
+ ScratchPad.recorded.should == [4, 5, 6, 7, 8, 9]
+ end
+
+ it "mimics a sed conditional with a many-element exclusive-end range" do
+ eval "10.times { |i| ScratchPad << i if (i == 4)...(i == 5) }"
+ ScratchPad.recorded.should == [4, 5]
+ end
+
+ it "allows combining two flip-flops" do
+ eval "10.times { |i| ScratchPad << i if (i == 4)...(i == 5) or (i == 7)...(i == 8) }"
+ ScratchPad.recorded.should == [4, 5, 7, 8]
+ end
+
+ it "evaluates the first conditions lazily with inclusive-end range" do
+ collector = proc { |i| ScratchPad << i }
+ eval "10.times { |i| i if collector[i]...false }"
+ ScratchPad.recorded.should == [0]
+ end
+
+ it "evaluates the first conditions lazily with exclusive-end range" do
+ collector = proc { |i| ScratchPad << i }
+ eval "10.times { |i| i if collector[i]..false }"
+ ScratchPad.recorded.should == [0]
+ end
+
+ it "evaluates the second conditions lazily with inclusive-end range" do
+ collector = proc { |i| ScratchPad << i }
+ eval "10.times { |i| i if (i == 4)...collector[i] }"
+ ScratchPad.recorded.should == [5]
+ end
+
+ it "evaluates the second conditions lazily with exclusive-end range" do
+ collector = proc { |i| ScratchPad << i }
+ eval "10.times { |i| i if (i == 4)..collector[i] }"
+ ScratchPad.recorded.should == [4]
+ end
+
+ it "scopes state by flip-flop" do
+ store_me = eval("proc { |i| ScratchPad << i if (i == 4)..(i == 7) }")
+ store_me[1]
+ store_me[4]
+ proc { store_me[1] }.call
+ store_me[7]
+ store_me[5]
+ ScratchPad.recorded.should == [4, 1, 7]
+ end
+
+ it "keeps flip-flops from interfering" do
+ a = eval "proc { |i| ScratchPad << i if (i == 4)..(i == 7) }"
+ b = eval "proc { |i| ScratchPad << i if (i == 4)..(i == 7) }"
+ 6.times(&a)
+ 6.times(&b)
+ ScratchPad.recorded.should == [4, 5, 4, 5]
+ end
+ end
+end
+
+describe "The postfix if form" do
+ it "evaluates statement if expression is true" do
+ a = []
+ a << 123 if true
+ a.should == [123]
+ end
+
+ it "does not evaluate statement if expression is false" do
+ a = []
+ a << 123 if false
+ a.should == []
+ end
+
+ it "returns result of expression if value is true" do
+ (123 if true).should == 123
+ end
+
+ it "returns nil if expression is false" do
+ (123 if false).should == nil
+ end
+
+ it "considers a nil expression as false" do
+ (123 if nil).should == nil
+ end
+
+ it "considers a non-nil object as true" do
+ (123 if mock('x')).should == 123
+ end
+
+ it "evaluates then-body in containing scope" do
+ a = 123
+ if true
+ b = a+1
+ end
+ b.should == 124
+ end
+
+ it "evaluates else-body in containing scope" do
+ a = 123
+ if false
+ b = a+1
+ else
+ b = a+2
+ end
+ b.should == 125
+ end
+
+ it "evaluates elsif-body in containing scope" do
+ a = 123
+ if false
+ b = a+1
+ elsif false
+ b = a+2
+ elsif true
+ b = a+3
+ else
+ b = a+4
+ end
+ b.should == 126
+ end
+end
diff --git a/spec/ruby/language/lambda_spec.rb b/spec/ruby/language/lambda_spec.rb
new file mode 100644
index 0000000000..3934a2bd91
--- /dev/null
+++ b/spec/ruby/language/lambda_spec.rb
@@ -0,0 +1,573 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "A lambda literal -> () { }" do
+ SpecEvaluate.desc = "for definition"
+
+ it "returns a Proc object when used in a BasicObject method" do
+ klass = Class.new(BasicObject) do
+ def create_lambda
+ -> () { }
+ end
+ end
+
+ klass.new.create_lambda.should be_an_instance_of(Proc)
+ end
+
+ it "does not execute the block" do
+ ->() { fail }.should be_an_instance_of(Proc)
+ end
+
+ it "returns a lambda" do
+ -> () { }.lambda?.should be_true
+ end
+
+ it "has its own scope for local variables" do
+ l = -> arg {
+ var = arg
+ # this would override var if it was declared outside the lambda
+ l.call(arg-1) if arg > 0
+ var
+ }
+ l.call(1).should == 1
+ end
+
+ context "assigns no local variables" do
+ evaluate <<-ruby do
+ @a = -> { }
+ @b = ->() { }
+ @c = -> () { }
+ @d = -> do end
+ ruby
+
+ @a.().should be_nil
+ @b.().should be_nil
+ @c.().should be_nil
+ @d.().should be_nil
+ end
+ end
+
+ context "assigns variables from parameters" do
+ evaluate <<-ruby do
+ @a = -> (a) { a }
+ ruby
+
+ @a.(1).should == 1
+ end
+
+ evaluate <<-ruby do
+ @a = -> ((a)) { a }
+ ruby
+
+ @a.(1).should == 1
+ @a.([1, 2, 3]).should == 1
+ end
+
+ evaluate <<-ruby do
+ @a = -> ((*a, b)) { [a, b] }
+ ruby
+
+ @a.(1).should == [[], 1]
+ @a.([1, 2, 3]).should == [[1, 2], 3]
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a={}) { a }
+ ruby
+
+ @a.().should == {}
+ @a.(2).should == 2
+ end
+
+ evaluate <<-ruby do
+ @a = -> (*) { }
+ ruby
+
+ @a.().should be_nil
+ @a.(1).should be_nil
+ @a.(1, 2, 3).should be_nil
+ end
+
+ evaluate <<-ruby do
+ @a = -> (*a) { a }
+ ruby
+
+ @a.().should == []
+ @a.(1).should == [1]
+ @a.(1, 2, 3).should == [1, 2, 3]
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a:) { a }
+ ruby
+
+ lambda { @a.() }.should raise_error(ArgumentError)
+ @a.(a: 1).should == 1
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a: 1) { a }
+ ruby
+
+ @a.().should == 1
+ @a.(a: 2).should == 2
+ end
+
+ evaluate <<-ruby do
+ @a = -> (**) { }
+ ruby
+
+ @a.().should be_nil
+ @a.(a: 1, b: 2).should be_nil
+ lambda { @a.(1) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ @a = -> (**k) { k }
+ ruby
+
+ @a.().should == {}
+ @a.(a: 1, b: 2).should == {a: 1, b: 2}
+ end
+
+ evaluate <<-ruby do
+ @a = -> (&b) { b }
+ ruby
+
+ @a.().should be_nil
+ @a.() { }.should be_an_instance_of(Proc)
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a, b) { [a, b] }
+ ruby
+
+ @a.(1, 2).should == [1, 2]
+ lambda { @a.() }.should raise_error(ArgumentError)
+ lambda { @a.(1) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ @a = -> ((a, b, *c, d), (*e, f, g), (*h)) do
+ [a, b, c, d, e, f, g, h]
+ end
+ ruby
+
+ @a.(1, 2, 3).should == [1, nil, [], nil, [], 2, nil, [3]]
+ result = @a.([1, 2, 3], [4, 5, 6, 7, 8], [9, 10])
+ result.should == [1, 2, [], 3, [4, 5, 6], 7, 8, [9, 10]]
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a, (b, (c, *d, (e, (*f)), g), (h, (i, j)))) do
+ [a, b, c, d, e, f, g, h, i, j]
+ end
+ ruby
+
+ @a.(1, 2).should == [1, 2, nil, [], nil, [nil], nil, nil, nil, nil]
+ result = @a.(1, [2, [3, 4, 5, [6, [7, 8]], 9], [10, [11, 12]]])
+ result.should == [1, 2, 3, [4, 5], 6, [7, 8], 9, 10, 11, 12]
+ end
+
+ evaluate <<-ruby do
+ @a = -> (*, **k) { k }
+ ruby
+
+ @a.().should == {}
+ @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash).and_return({a: 1})
+ @a.(h).should == {a: 1}
+ end
+
+ evaluate <<-ruby do
+ @a = -> (*, &b) { b }
+ ruby
+
+ @a.().should be_nil
+ @a.(1, 2, 3, 4).should be_nil
+ @a.(&(l = ->{})).should equal(l)
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a:, b:) { [a, b] }
+ ruby
+
+ @a.(a: 1, b: 2).should == [1, 2]
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a:, b: 1) { [a, b] }
+ ruby
+
+ @a.(a: 1).should == [1, 1]
+ @a.(a: 1, b: 2).should == [1, 2]
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a: 1, b:) { [a, b] }
+ ruby
+
+ @a.(b: 0).should == [1, 0]
+ @a.(b: 2, a: 3).should == [3, 2]
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a: @a = -> (a: 1) { a }, b:) do
+ [a, b]
+ end
+ ruby
+
+ @a.(a: 2, b: 3).should == [2, 3]
+ @a.(b: 1).should == [@a, 1]
+
+ # Note the default value of a: in the original method.
+ @a.().should == 1
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a: 1, b: 2) { [a, b] }
+ ruby
+
+ @a.().should == [1, 2]
+ @a.(b: 3, a: 4).should == [4, 3]
+ end
+
+ evaluate <<-ruby do
+ @a = -> (a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l) do
+ [a, b, c, d, e, f, g, h, k, l]
+ end
+ ruby
+
+ result = @a.(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{}))
+ result.should == [9, 8, [7], [], 6, 5, 4, 3, {}, l]
+ end
+
+ evaluate <<-ruby do
+ @a = -> a, b=1, *c, d, e:, f: 2, g:, **k, &l do
+ [a, b, c, d, e, f, g, k, l]
+ end
+ ruby
+
+ result = @a.(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{}))
+ result.should == [1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l]
+ end
+
+ describe "with circular optional argument reference" do
+ it "shadows an existing local with the same name as the argument" do
+ a = 1
+ -> {
+ @proc = eval "-> (a=a) { a }"
+ }.should complain(/circular argument reference/)
+ @proc.call.should == nil
+ end
+
+ it "shadows an existing method with the same name as the argument" do
+ def a; 1; end
+ -> {
+ @proc = eval "-> (a=a) { a }"
+ }.should complain(/circular argument reference/)
+ @proc.call.should == nil
+ end
+
+ it "calls an existing method with the same name as the argument if explicitly using ()" do
+ def a; 1; end
+ -> (a=a()) { a }.call.should == 1
+ end
+ end
+ end
+end
+
+describe "A lambda expression 'lambda { ... }'" do
+ SpecEvaluate.desc = "for definition"
+
+ it "calls the #lambda method" do
+ obj = mock("lambda definition")
+ obj.should_receive(:lambda).and_return(obj)
+
+ def obj.define
+ lambda { }
+ end
+
+ obj.define.should equal(obj)
+ end
+
+ it "does not execute the block" do
+ lambda { fail }.should be_an_instance_of(Proc)
+ end
+
+ it "returns a lambda" do
+ lambda { }.lambda?.should be_true
+ end
+
+ it "requires a block" do
+ lambda { lambda }.should raise_error(ArgumentError)
+ end
+
+ context "with an implicit block" do
+ before do
+ def meth; lambda; end
+ end
+
+ it "can be created" do
+ implicit_lambda = nil
+ -> {
+ implicit_lambda = meth { 1 }
+ }.should complain(/tried to create Proc object without a block/)
+
+ implicit_lambda.lambda?.should be_true
+ implicit_lambda.call.should == 1
+ end
+ end
+
+ context "assigns no local variables" do
+ evaluate <<-ruby do
+ @a = lambda { }
+ @b = lambda { || }
+ ruby
+
+ @a.().should be_nil
+ @b.().should be_nil
+ end
+ end
+
+ context "assigns variables from parameters" do
+ evaluate <<-ruby do
+ @a = lambda { |a| a }
+ ruby
+
+ @a.(1).should == 1
+ end
+
+ evaluate <<-ruby do
+ def m(*a) yield(*a) end
+ @a = lambda { |a| a }
+ ruby
+
+ lambda { m(&@a) }.should raise_error(ArgumentError)
+ lambda { m(1, 2, &@a) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a, | a }
+ ruby
+
+ @a.(1).should == 1
+ @a.([1, 2]).should == [1, 2]
+
+ lambda { @a.() }.should raise_error(ArgumentError)
+ lambda { @a.(1, 2) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ def m(a) yield a end
+ def m2() yield end
+
+ @a = lambda { |a, | a }
+ ruby
+
+ m(1, &@a).should == 1
+ m([1, 2], &@a).should == [1, 2]
+
+ lambda { m2(&@a) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |(a)| a }
+ ruby
+
+ @a.(1).should == 1
+ @a.([1, 2, 3]).should == 1
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |(*a, b)| [a, b] }
+ ruby
+
+ @a.(1).should == [[], 1]
+ @a.([1, 2, 3]).should == [[1, 2], 3]
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a={}| a }
+ ruby
+
+ @a.().should == {}
+ @a.(2).should == 2
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |*| }
+ ruby
+
+ @a.().should be_nil
+ @a.(1).should be_nil
+ @a.(1, 2, 3).should be_nil
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |*a| a }
+ ruby
+
+ @a.().should == []
+ @a.(1).should == [1]
+ @a.(1, 2, 3).should == [1, 2, 3]
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a:| a }
+ ruby
+
+ lambda { @a.() }.should raise_error(ArgumentError)
+ @a.(a: 1).should == 1
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a: 1| a }
+ ruby
+
+ @a.().should == 1
+ @a.(a: 2).should == 2
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |**| }
+ ruby
+
+ @a.().should be_nil
+ @a.(a: 1, b: 2).should be_nil
+ lambda { @a.(1) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |**k| k }
+ ruby
+
+ @a.().should == {}
+ @a.(a: 1, b: 2).should == {a: 1, b: 2}
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |&b| b }
+ ruby
+
+ @a.().should be_nil
+ @a.() { }.should be_an_instance_of(Proc)
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a, b| [a, b] }
+ ruby
+
+ @a.(1, 2).should == [1, 2]
+ end
+
+ evaluate <<-ruby do
+ @a = lambda do |(a, b, *c, d), (*e, f, g), (*h)|
+ [a, b, c, d, e, f, g, h]
+ end
+ ruby
+
+ @a.(1, 2, 3).should == [1, nil, [], nil, [], 2, nil, [3]]
+ result = @a.([1, 2, 3], [4, 5, 6, 7, 8], [9, 10])
+ result.should == [1, 2, [], 3, [4, 5, 6], 7, 8, [9, 10]]
+ end
+
+ evaluate <<-ruby do
+ @a = lambda do |a, (b, (c, *d, (e, (*f)), g), (h, (i, j)))|
+ [a, b, c, d, e, f, g, h, i, j]
+ end
+ ruby
+
+ @a.(1, 2).should == [1, 2, nil, [], nil, [nil], nil, nil, nil, nil]
+ result = @a.(1, [2, [3, 4, 5, [6, [7, 8]], 9], [10, [11, 12]]])
+ result.should == [1, 2, 3, [4, 5], 6, [7, 8], 9, 10, 11, 12]
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |*, **k| k }
+ ruby
+
+ @a.().should == {}
+ @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash).and_return({a: 1})
+ @a.(h).should == {a: 1}
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |*, &b| b }
+ ruby
+
+ @a.().should be_nil
+ @a.(1, 2, 3, 4).should be_nil
+ @a.(&(l = ->{})).should equal(l)
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a:, b:| [a, b] }
+ ruby
+
+ @a.(a: 1, b: 2).should == [1, 2]
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a:, b: 1| [a, b] }
+ ruby
+
+ @a.(a: 1).should == [1, 1]
+ @a.(a: 1, b: 2).should == [1, 2]
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a: 1, b:| [a, b] }
+ ruby
+
+ @a.(b: 0).should == [1, 0]
+ @a.(b: 2, a: 3).should == [3, 2]
+ end
+
+ evaluate <<-ruby do
+ @a = lambda do |a: (@a = -> (a: 1) { a }), b:|
+ [a, b]
+ end
+ ruby
+
+ @a.(a: 2, b: 3).should == [2, 3]
+ @a.(b: 1).should == [@a, 1]
+
+ # Note the default value of a: in the original method.
+ @a.().should == 1
+ end
+
+ evaluate <<-ruby do
+ @a = lambda { |a: 1, b: 2| [a, b] }
+ ruby
+
+ @a.().should == [1, 2]
+ @a.(b: 3, a: 4).should == [4, 3]
+ end
+
+ evaluate <<-ruby do
+ @a = lambda do |a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l|
+ [a, b, c, d, e, f, g, h, k, l]
+ end
+ ruby
+
+ result = @a.(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{}))
+ result.should == [9, 8, [7], [], 6, 5, 4, 3, {}, l]
+ end
+
+ evaluate <<-ruby do
+ @a = lambda do |a, b=1, *c, d, e:, f: 2, g:, **k, &l|
+ [a, b, c, d, e, f, g, k, l]
+ end
+ ruby
+
+ result = @a.(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{}))
+ result.should == [1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l]
+ end
+ end
+end
diff --git a/spec/ruby/language/line_spec.rb b/spec/ruby/language/line_spec.rb
new file mode 100644
index 0000000000..aac039a941
--- /dev/null
+++ b/spec/ruby/language/line_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/code_loading'
+require_relative 'shared/__LINE__'
+
+describe "The __LINE__ pseudo-variable" do
+ it "raises a SyntaxError if assigned to" do
+ lambda { eval("__LINE__ = 1") }.should raise_error(SyntaxError)
+ end
+
+ before :each do
+ ScratchPad.record []
+ end
+
+ after :each do
+ ScratchPad.clear
+ end
+
+ it "equals the line number of the text inside an eval" do
+ eval <<-EOC
+ScratchPad << __LINE__
+
+# line 3
+
+ScratchPad << __LINE__
+ EOC
+
+ ScratchPad.recorded.should == [1, 5]
+ end
+end
+
+describe "The __LINE__ pseudo-variable" do
+ it_behaves_like :language___LINE__, :require, CodeLoadingSpecs::Method.new
+end
+
+describe "The __LINE__ pseudo-variable" do
+ it_behaves_like :language___LINE__, :require, Kernel
+end
+
+describe "The __LINE__ pseudo-variable" do
+ it_behaves_like :language___LINE__, :load, CodeLoadingSpecs::Method.new
+end
+
+describe "The __LINE__ pseudo-variable" do
+ it_behaves_like :language___LINE__, :load, Kernel
+end
diff --git a/spec/ruby/language/loop_spec.rb b/spec/ruby/language/loop_spec.rb
new file mode 100644
index 0000000000..121872a104
--- /dev/null
+++ b/spec/ruby/language/loop_spec.rb
@@ -0,0 +1,67 @@
+require_relative '../spec_helper'
+
+describe "The loop expression" do
+ it "repeats the given block until a break is called" do
+ outer_loop = 0
+ loop do
+ outer_loop += 1
+ break if outer_loop == 10
+ end
+ outer_loop.should == 10
+ end
+
+ it "executes code in its own scope" do
+ loop do
+ inner_loop = 123
+ break
+ end
+ lambda { inner_loop }.should raise_error(NameError)
+ end
+
+ it "returns the value passed to break if interrupted by break" do
+ loop do
+ break 123
+ end.should == 123
+ end
+
+ it "returns nil if interrupted by break with no arguments" do
+ loop do
+ break
+ end.should == nil
+ end
+
+ it "skips to end of body with next" do
+ a = []
+ i = 0
+ loop do
+ break if (i+=1) >= 5
+ next if i == 3
+ a << i
+ end
+ a.should == [1, 2, 4]
+ end
+
+ it "restarts the current iteration with redo" do
+ a = []
+ loop do
+ a << 1
+ redo if a.size < 2
+ a << 2
+ break if a.size == 3
+ end
+ a.should == [1, 1, 2]
+ end
+
+ it "uses a spaghetti nightmare of redo, next and break" do
+ a = []
+ loop do
+ a << 1
+ redo if a.size == 1
+ a << 2
+ next if a.size == 3
+ a << 3
+ break if a.size > 6
+ end
+ a.should == [1, 1, 2, 1, 2, 3, 1, 2, 3]
+ end
+end
diff --git a/spec/ruby/language/magic_comment_spec.rb b/spec/ruby/language/magic_comment_spec.rb
new file mode 100644
index 0000000000..f2bf3a08e5
--- /dev/null
+++ b/spec/ruby/language/magic_comment_spec.rb
@@ -0,0 +1,92 @@
+require_relative '../spec_helper'
+
+# See core/kernel/eval_spec.rb for more magic comments specs for eval()
+describe :magic_comments, shared: true do
+ before :each do
+ @default = @method == :locale ? Encoding.find('locale') : Encoding::UTF_8
+ end
+
+ it "are optional" do
+ @object.call('no_magic_comment.rb').should == @default.name
+ end
+
+ it "are case-insensitive" do
+ @object.call('case_magic_comment.rb').should == Encoding::Big5.name
+ end
+
+ it "must be at the first line" do
+ @object.call('second_line_magic_comment.rb').should == @default.name
+ end
+
+ it "must be the first token of the line" do
+ @object.call('second_token_magic_comment.rb').should == @default.name
+ end
+
+ it "can be after the shebang" do
+ @object.call('shebang_magic_comment.rb').should == Encoding::Big5.name
+ end
+
+ it "can take Emacs style" do
+ @object.call('emacs_magic_comment.rb').should == Encoding::Big5.name
+ end
+
+ it "can take vim style" do
+ @object.call('vim_magic_comment.rb').should == Encoding::Big5.name
+ end
+
+ it "determine __ENCODING__" do
+ @object.call('magic_comment.rb').should == Encoding::Big5.name
+ end
+
+ it "do not cause bytes to be mangled by passing them through the wrong encoding" do
+ @object.call('bytes_magic_comment.rb').should == [167, 65, 166, 110].inspect
+ end
+end
+
+describe "Magic comments" do
+ describe "in stdin" do
+ it_behaves_like :magic_comments, :locale, -> file {
+ print_at_exit = fixture(__FILE__, "print_magic_comment_result_at_exit.rb")
+ ruby_exe(nil, args: "< #{fixture(__FILE__, file)}", options: "-r#{print_at_exit}")
+ }
+ end
+
+ platform_is_not :windows do
+ describe "in an -e argument" do
+ it_behaves_like :magic_comments, :locale, -> file {
+ print_at_exit = fixture(__FILE__, "print_magic_comment_result_at_exit.rb")
+ # Use UTF-8, as it is the default source encoding for files
+ code = File.read(fixture(__FILE__, file), encoding: 'utf-8')
+ IO.popen([*ruby_exe, "-r", print_at_exit, "-e", code], &:read)
+ }
+ end
+ end
+
+ describe "in the main file" do
+ it_behaves_like :magic_comments, :UTF8, -> file {
+ print_at_exit = fixture(__FILE__, "print_magic_comment_result_at_exit.rb")
+ ruby_exe(fixture(__FILE__, file), options: "-r#{print_at_exit}")
+ }
+ end
+
+ describe "in a loaded file" do
+ it_behaves_like :magic_comments, :UTF8, -> file {
+ load fixture(__FILE__, file)
+ $magic_comment_result
+ }
+ end
+
+ describe "in a required file" do
+ it_behaves_like :magic_comments, :UTF8, -> file {
+ require fixture(__FILE__, file)
+ $magic_comment_result
+ }
+ end
+
+ describe "in an eval" do
+ it_behaves_like :magic_comments, :UTF8, -> file {
+ # Use UTF-8, as it is the default source encoding for files
+ eval(File.read(fixture(__FILE__, file), encoding: 'utf-8'))
+ }
+ end
+end
diff --git a/spec/ruby/language/match_spec.rb b/spec/ruby/language/match_spec.rb
new file mode 100644
index 0000000000..36d347bd57
--- /dev/null
+++ b/spec/ruby/language/match_spec.rb
@@ -0,0 +1,74 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/match_operators'
+
+describe "The !~ operator" do
+ before :each do
+ @obj = OperatorImplementor.new
+ end
+
+ it "evaluates as a call to !~" do
+ expected = "hello world"
+
+ opval = (@obj !~ expected)
+ methodval = @obj.send(:"!~", expected)
+
+ opval.should == expected
+ methodval.should == expected
+ end
+end
+
+describe "The =~ operator" do
+ before :each do
+ @impl = OperatorImplementor.new
+ end
+
+ it "calls the =~ method" do
+ expected = "hello world"
+
+ opval = (@obj =~ expected)
+ methodval = @obj.send(:"=~", expected)
+
+ opval.should == expected
+ methodval.should == expected
+ end
+end
+
+describe "The =~ operator with named captures" do
+ before :each do
+ @regexp = /(?<matched>foo)(?<unmatched>bar)?/
+ @string = "foofoo"
+ end
+
+ describe "on syntax of /regexp/ =~ string_variable" do
+ it "sets local variables by the captured pairs" do
+ /(?<matched>foo)(?<unmatched>bar)?/ =~ @string
+ local_variables.should == [:matched, :unmatched]
+ matched.should == "foo"
+ unmatched.should == nil
+ end
+ end
+
+ describe "on syntax of string_variable =~ /regexp/" do
+ it "does not set local variables" do
+ @string =~ /(?<matched>foo)(?<unmatched>bar)?/
+ local_variables.should == []
+ end
+ end
+
+ describe "on syntax of regexp_variable =~ string_variable" do
+ it "does not set local variables" do
+ @regexp =~ @string
+ local_variables.should == []
+ end
+ end
+
+ describe "on the method calling" do
+ it "does not set local variables" do
+ @regexp.=~(@string)
+ local_variables.should == []
+
+ @regexp.send :=~, @string
+ local_variables.should == []
+ end
+ end
+end
diff --git a/spec/ruby/language/metaclass_spec.rb b/spec/ruby/language/metaclass_spec.rb
new file mode 100644
index 0000000000..a6525a3ef2
--- /dev/null
+++ b/spec/ruby/language/metaclass_spec.rb
@@ -0,0 +1,143 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/class'
+require_relative 'fixtures/metaclass'
+
+describe "self in a metaclass body (class << obj)" do
+ it "is TrueClass for true" do
+ class << true; self; end.should == TrueClass
+ end
+
+ it "is FalseClass for false" do
+ class << false; self; end.should == FalseClass
+ end
+
+ it "is NilClass for nil" do
+ class << nil; self; end.should == NilClass
+ end
+
+ it "raises a TypeError for numbers" do
+ lambda { class << 1; self; end }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError for symbols" do
+ lambda { class << :symbol; self; end }.should raise_error(TypeError)
+ end
+
+ it "is a singleton Class instance" do
+ cls = class << mock('x'); self; end
+ cls.is_a?(Class).should == true
+ cls.should_not equal(Object)
+ end
+end
+
+describe "A constant on a metaclass" do
+ before :each do
+ @object = Object.new
+ class << @object
+ CONST = self
+ end
+ end
+
+ it "can be accessed after the metaclass body is reopened" do
+ class << @object
+ CONST.should == self
+ end
+ end
+
+ it "can be accessed via self::CONST" do
+ class << @object
+ self::CONST.should == self
+ end
+ end
+
+ it "can be accessed via const_get" do
+ class << @object
+ const_get(:CONST).should == self
+ end
+ end
+
+ it "is not defined on the object's class" do
+ @object.class.const_defined?(:CONST).should be_false
+ end
+
+ it "is not defined in the metaclass opener's scope" do
+ class << @object
+ CONST
+ end
+ lambda { CONST }.should raise_error(NameError)
+ end
+
+ it "cannot be accessed via object::CONST" do
+ lambda do
+ @object::CONST
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a NameError for anonymous_module::CONST" do
+ @object = Class.new
+ class << @object
+ CONST = 100
+ end
+
+ lambda do
+ @object::CONST
+ end.should raise_error(NameError)
+ end
+
+ it "appears in the metaclass constant list" do
+ constants = class << @object; constants; end
+ constants.should include(:CONST)
+ end
+
+ it "does not appear in the object's class constant list" do
+ @object.class.constants.should_not include(:CONST)
+ end
+
+ it "is not preserved when the object is duped" do
+ @object = @object.dup
+
+ lambda do
+ class << @object; CONST; end
+ end.should raise_error(NameError)
+ end
+
+ it "is preserved when the object is cloned" do
+ @object = @object.clone
+
+ class << @object
+ CONST.should_not be_nil
+ end
+ end
+end
+
+describe "calling methods on the metaclass" do
+
+ it "calls a method on the metaclass" do
+ MetaClassSpecs::A.cheese.should == 'edam'
+ MetaClassSpecs::B.cheese.should == 'stilton'
+ end
+
+ it "calls a method on the instance's metaclass" do
+ b = MetaClassSpecs::B.new
+ b_meta = MetaClassSpecs.metaclass_of b
+ b_meta.send(:define_method, :cheese) {'cheshire'}
+ b.cheese.should == 'cheshire'
+ end
+
+ it "calls a method in deeper chains of metaclasses" do
+ b = MetaClassSpecs::B.new
+ b_meta = MetaClassSpecs.metaclass_of b
+ b_meta_meta = MetaClassSpecs.metaclass_of b_meta
+ b_meta_meta.send(:define_method, :cheese) {'gouda'}
+ b_meta.cheese.should == 'gouda'
+
+ b_meta_meta_meta = MetaClassSpecs.metaclass_of b_meta_meta
+ b_meta_meta_meta.send(:define_method, :cheese) {'wensleydale'}
+ b_meta_meta.cheese.should == 'wensleydale'
+ end
+
+ it "calls a method defined on the metaclass of the metaclass" do
+ d_meta = MetaClassSpecs::D.singleton_class
+ d_meta.ham.should == 'iberico'
+ end
+end
diff --git a/spec/ruby/language/method_spec.rb b/spec/ruby/language/method_spec.rb
new file mode 100644
index 0000000000..49837fa638
--- /dev/null
+++ b/spec/ruby/language/method_spec.rb
@@ -0,0 +1,1322 @@
+require_relative '../spec_helper'
+
+describe "A method send" do
+ evaluate <<-ruby do
+ def m(a) a end
+ ruby
+
+ a = b = m 1
+ a.should == 1
+ b.should == 1
+ end
+
+ context "with a single splatted Object argument" do
+ before :all do
+ def m(a) a end
+ end
+
+ it "does not call #to_ary" do
+ x = mock("splat argument")
+ x.should_not_receive(:to_ary)
+
+ m(*x).should equal(x)
+ end
+
+ it "calls #to_a" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return([1])
+
+ m(*x).should == 1
+ end
+
+ it "wraps the argument in an Array if #to_a returns nil" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(nil)
+
+ m(*x).should == x
+ end
+
+ it "raises a TypeError if #to_a does not return an Array" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { m(*x) }.should raise_error(TypeError)
+ end
+ end
+
+ context "with a leading splatted Object argument" do
+ before :all do
+ def m(a, b, *c, d, e) [a, b, c, d, e] end
+ end
+
+ it "does not call #to_ary" do
+ x = mock("splat argument")
+ x.should_not_receive(:to_ary)
+
+ m(*x, 1, 2, 3).should == [x, 1, [], 2, 3]
+ end
+
+ it "calls #to_a" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return([1])
+
+ m(*x, 2, 3, 4).should == [1, 2, [], 3, 4]
+ end
+
+ it "wraps the argument in an Array if #to_a returns nil" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(nil)
+
+ m(*x, 2, 3, 4).should == [x, 2, [], 3, 4]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { m(*x, 2, 3) }.should raise_error(TypeError)
+ end
+ end
+
+ context "with a middle splatted Object argument" do
+ before :all do
+ def m(a, b, *c, d, e) [a, b, c, d, e] end
+ end
+
+ it "does not call #to_ary" do
+ x = mock("splat argument")
+ x.should_not_receive(:to_ary)
+
+ m(1, 2, *x, 3, 4).should == [1, 2, [x], 3, 4]
+ end
+
+ it "calls #to_a" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return([5, 6, 7])
+
+ m(1, 2, *x, 3).should == [1, 2, [5, 6], 7, 3]
+ end
+
+ it "wraps the argument in an Array if #to_a returns nil" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(nil)
+
+ m(1, 2, *x, 4).should == [1, 2, [], x, 4]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { m(1, *x, 2, 3) }.should raise_error(TypeError)
+ end
+
+ it "copies the splatted array" do
+ args = [3, 4]
+ m(1, 2, *args, 4, 5).should == [1, 2, [3, 4], 4, 5]
+ m(1, 2, *args, 4, 5)[2].should_not equal(args)
+ end
+
+ it "allows an array being splatted to be modified by another argument" do
+ args = [3, 4]
+ m(1, args.shift, *args, 4, 5).should == [1, 3, [4], 4, 5]
+ end
+ end
+
+ context "with a trailing splatted Object argument" do
+ before :all do
+ def m(a, *b, c) [a, b, c] end
+ end
+
+ it "does not call #to_ary" do
+ x = mock("splat argument")
+ x.should_not_receive(:to_ary)
+
+ m(1, 2, *x).should == [1, [2], x]
+ end
+
+ it "calls #to_a" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return([5, 6, 7])
+
+ m(1, 2, *x).should == [1, [2, 5, 6], 7]
+ end
+
+ it "wraps the argument in an Array if #to_a returns nil" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(nil)
+
+ m(1, 2, *x, 4).should == [1, [2, x], 4]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { m(1, 2, *x) }.should raise_error(TypeError)
+ end
+ end
+end
+
+describe "An element assignment method send" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ context "with a single splatted Object argument" do
+ before :all do
+ @o = mock("element set receiver")
+ def @o.[]=(a, b) ScratchPad.record [a, b] end
+ end
+
+ it "does not call #to_ary" do
+ x = mock("splat argument")
+ x.should_not_receive(:to_ary)
+
+ (@o[*x] = 1).should == 1
+ ScratchPad.recorded.should == [x, 1]
+ end
+
+ it "calls #to_a" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return([1])
+
+ (@o[*x] = 2).should == 2
+ ScratchPad.recorded.should == [1, 2]
+ end
+
+ it "wraps the argument in an Array if #to_a returns nil" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(nil)
+
+ (@o[*x] = 1).should == 1
+ ScratchPad.recorded.should == [x, 1]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { @o[*x] = 1 }.should raise_error(TypeError)
+ end
+ end
+
+ context "with a leading splatted Object argument" do
+ before :all do
+ @o = mock("element set receiver")
+ def @o.[]=(a, b, *c, d, e) ScratchPad.record [a, b, c, d, e] end
+ end
+
+ it "does not call #to_ary" do
+ x = mock("splat argument")
+ x.should_not_receive(:to_ary)
+
+ (@o[*x, 2, 3, 4] = 1).should == 1
+ ScratchPad.recorded.should == [x, 2, [3], 4, 1]
+ end
+
+ it "calls #to_a" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return([1, 2, 3])
+
+ (@o[*x, 4, 5] = 6).should == 6
+ ScratchPad.recorded.should == [1, 2, [3, 4], 5, 6]
+ end
+
+ it "wraps the argument in an Array if #to_a returns nil" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(nil)
+
+ (@o[*x, 2, 3, 4] = 5).should == 5
+ ScratchPad.recorded.should == [x, 2, [3], 4, 5]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { @o[*x, 2, 3] = 4 }.should raise_error(TypeError)
+ end
+ end
+
+ context "with a middle splatted Object argument" do
+ before :all do
+ @o = mock("element set receiver")
+ def @o.[]=(a, b, *c, d, e) ScratchPad.record [a, b, c, d, e] end
+ end
+
+ it "does not call #to_ary" do
+ x = mock("splat argument")
+ x.should_not_receive(:to_ary)
+
+ (@o[1, *x, 2, 3] = 4).should == 4
+ ScratchPad.recorded.should == [1, x, [2], 3, 4]
+ end
+
+ it "calls #to_a" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return([2, 3])
+
+ (@o[1, *x, 4] = 5).should == 5
+ ScratchPad.recorded.should == [1, 2, [3], 4, 5]
+ end
+
+ it "wraps the argument in an Array if #to_a returns nil" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(nil)
+
+ (@o[1, 2, *x, 3] = 4).should == 4
+ ScratchPad.recorded.should == [1, 2, [x], 3, 4]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { @o[1, 2, *x, 3] = 4 }.should raise_error(TypeError)
+ end
+ end
+
+ context "with a trailing splatted Object argument" do
+ before :all do
+ @o = mock("element set receiver")
+ def @o.[]=(a, b, *c, d, e) ScratchPad.record [a, b, c, d, e] end
+ end
+
+ it "does not call #to_ary" do
+ x = mock("splat argument")
+ x.should_not_receive(:to_ary)
+
+ (@o[1, 2, 3, 4, *x] = 5).should == 5
+ ScratchPad.recorded.should == [1, 2, [3, 4], x, 5]
+ end
+
+ it "calls #to_a" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return([4, 5])
+
+ (@o[1, 2, 3, *x] = 6).should == 6
+ ScratchPad.recorded.should == [1, 2, [3, 4], 5, 6]
+ end
+
+ it "wraps the argument in an Array if #to_a returns nil" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(nil)
+
+ (@o[1, 2, 3, *x] = 4).should == 4
+ ScratchPad.recorded.should == [1, 2, [3], x, 4]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { @o[1, 2, 3, *x] = 4 }.should raise_error(TypeError)
+ end
+ end
+end
+
+describe "An attribute assignment method send" do
+ context "with a single splatted Object argument" do
+ before :all do
+ @o = mock("element set receiver")
+ def @o.m=(a, b) [a, b] end
+ end
+
+ it "does not call #to_ary" do
+ x = mock("splat argument")
+ x.should_not_receive(:to_ary)
+
+ (@o.send :m=, *x, 1).should == [x, 1]
+ end
+
+ it "calls #to_a" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return([1])
+
+ (@o.send :m=, *x, 2).should == [1, 2]
+ end
+
+ it "wraps the argument in an Array if #to_a returns nil" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(nil)
+
+ (@o.send :m=, *x, 1).should == [x, 1]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { @o.send :m=, *x, 1 }.should raise_error(TypeError)
+ end
+ end
+
+ context "with a leading splatted Object argument" do
+ before :all do
+ @o = mock("element set receiver")
+ def @o.m=(a, b, *c, d, e) [a, b, c, d, e] end
+ end
+
+ it "does not call #to_ary" do
+ x = mock("splat argument")
+ x.should_not_receive(:to_ary)
+
+ (@o.send :m=, *x, 2, 3, 4, 1).should == [x, 2, [3], 4, 1]
+ end
+
+ it "calls #to_a" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return([1, 2, 3])
+
+ (@o.send :m=, *x, 4, 5, 6).should == [1, 2, [3, 4], 5, 6]
+ end
+
+ it "wraps the argument in an Array if #to_a returns nil" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(nil)
+
+ (@o.send :m=, *x, 2, 3, 4, 5).should == [x, 2, [3], 4, 5]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { @o.send :m=, *x, 2, 3, 4 }.should raise_error(TypeError)
+ end
+ end
+
+ context "with a middle splatted Object argument" do
+ before :all do
+ @o = mock("element set receiver")
+ def @o.m=(a, b, *c, d, e) [a, b, c, d, e] end
+ end
+
+ it "does not call #to_ary" do
+ x = mock("splat argument")
+ x.should_not_receive(:to_ary)
+
+ (@o.send :m=, 1, *x, 2, 3, 4).should == [1, x, [2], 3, 4]
+ end
+
+ it "calls #to_a" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return([2, 3])
+
+ (@o.send :m=, 1, *x, 4, 5).should == [1, 2, [3], 4, 5]
+ end
+
+ it "wraps the argument in an Array if #to_a returns nil" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(nil)
+
+ (@o.send :m=, 1, 2, *x, 3, 4).should == [1, 2, [x], 3, 4]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { @o.send :m=, 1, 2, *x, 3, 4 }.should raise_error(TypeError)
+ end
+ end
+
+ context "with a trailing splatted Object argument" do
+ before :all do
+ @o = mock("element set receiver")
+ def @o.m=(a, b, *c, d, e) [a, b, c, d, e] end
+ end
+
+ it "does not call #to_ary" do
+ x = mock("splat argument")
+ x.should_not_receive(:to_ary)
+
+ (@o.send :m=, 1, 2, 3, 4, *x, 5).should == [1, 2, [3, 4], x, 5]
+ end
+
+ it "calls #to_a" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return([4, 5])
+
+ (@o.send :m=, 1, 2, 3, *x, 6).should == [1, 2, [3, 4], 5, 6]
+ end
+
+ it "wraps the argument in an Array if #to_a returns nil" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(nil)
+
+ (@o.send :m=, 1, 2, 3, *x, 4).should == [1, 2, [3], x, 4]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array" do
+ x = mock("splat argument")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { @o.send :m=, 1, 2, 3, *x, 4 }.should raise_error(TypeError)
+ end
+ end
+end
+
+describe "A method" do
+ SpecEvaluate.desc = "for definition"
+
+ context "assigns no local variables" do
+ evaluate <<-ruby do
+ def m
+ end
+ ruby
+
+ m.should be_nil
+ end
+
+ evaluate <<-ruby do
+ def m()
+ end
+ ruby
+
+ m.should be_nil
+ end
+ end
+
+ context "assigns local variables from method parameters" do
+ evaluate <<-ruby do
+ def m(a) a end
+ ruby
+
+ m((args = 1, 2, 3)).should equal(args)
+ end
+
+ evaluate <<-ruby do
+ def m((a)) a end
+ ruby
+
+ m(1).should == 1
+ m([1, 2, 3]).should == 1
+ end
+
+ evaluate <<-ruby do
+ def m((*a, b)) [a, b] end
+ ruby
+
+ m(1).should == [[], 1]
+ m([1, 2, 3]).should == [[1, 2], 3]
+ end
+
+ evaluate <<-ruby do
+ def m(a=1) a end
+ ruby
+
+ m().should == 1
+ m(2).should == 2
+ end
+
+ evaluate <<-ruby do
+ def m() end
+ ruby
+
+ m().should be_nil
+ m(*[]).should be_nil
+ m(**{}).should be_nil
+ end
+
+ evaluate <<-ruby do
+ def m(*) end
+ ruby
+
+ m().should be_nil
+ m(1).should be_nil
+ m(1, 2, 3).should be_nil
+ end
+
+ evaluate <<-ruby do
+ def m(*a) a end
+ ruby
+
+ m().should == []
+ m(1).should == [1]
+ m(1, 2, 3).should == [1, 2, 3]
+ m(*[]).should == []
+ m(**{}).should == []
+ end
+
+ evaluate <<-ruby do
+ def m(a:) a end
+ ruby
+
+ lambda { m() }.should raise_error(ArgumentError)
+ m(a: 1).should == 1
+ lambda { m("a" => 1, a: 1) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ def m(a: 1) a end
+ ruby
+
+ m().should == 1
+ m(a: 2).should == 2
+ end
+
+ evaluate <<-ruby do
+ def m(**) end
+ ruby
+
+ m().should be_nil
+ m(a: 1, b: 2).should be_nil
+ lambda { m(1) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ def m(**k) k end
+ ruby
+
+ m().should == {}
+ m(a: 1, b: 2).should == { a: 1, b: 2 }
+ m(*[]).should == {}
+ m(**{}).should == {}
+ lambda { m(2) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ def m(&b) b end
+ ruby
+
+ m { }.should be_an_instance_of(Proc)
+ end
+
+ evaluate <<-ruby do
+ def m(a, b) [a, b] end
+ ruby
+
+ m(1, 2).should == [1, 2]
+ end
+
+ evaluate <<-ruby do
+ def m(a, (b, c)) [a, b, c] end
+ ruby
+
+ m(1, 2).should == [1, 2, nil]
+ m(1, [2, 3, 4]).should == [1, 2, 3]
+ end
+
+ evaluate <<-ruby do
+ def m((a), (b)) [a, b] end
+ ruby
+
+ m(1, 2).should == [1, 2]
+ m([1, 2], [3, 4]).should == [1, 3]
+ end
+
+ evaluate <<-ruby do
+ def m((*), (*)) end
+ ruby
+
+ m(2, 3).should be_nil
+ m([2, 3, 4], [5, 6]).should be_nil
+ lambda { m a: 1 }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ def m((*a), (*b)) [a, b] end
+ ruby
+
+ m(1, 2).should == [[1], [2]]
+ m([1, 2], [3, 4]).should == [[1, 2], [3, 4]]
+ end
+
+ evaluate <<-ruby do
+ def m((a, b), (c, d))
+ [a, b, c, d]
+ end
+ ruby
+
+ m(1, 2).should == [1, nil, 2, nil]
+ m([1, 2, 3], [4, 5, 6]).should == [1, 2, 4, 5]
+ end
+
+ evaluate <<-ruby do
+ def m((a, *b), (*c, d))
+ [a, b, c, d]
+ end
+ ruby
+
+ m(1, 2).should == [1, [], [], 2]
+ m([1, 2, 3], [4, 5, 6]).should == [1, [2, 3], [4, 5], 6]
+ end
+
+ evaluate <<-ruby do
+ def m((a, b, *c, d), (*e, f, g), (*h))
+ [a, b, c, d, e, f, g, h]
+ end
+ ruby
+
+ m(1, 2, 3).should == [1, nil, [], nil, [], 2, nil, [3]]
+ result = m([1, 2, 3], [4, 5, 6, 7, 8], [9, 10])
+ result.should == [1, 2, [], 3, [4, 5, 6], 7, 8, [9, 10]]
+ end
+
+ evaluate <<-ruby do
+ def m(a, (b, (c, *d), *e))
+ [a, b, c, d, e]
+ end
+ ruby
+
+ m(1, 2).should == [1, 2, nil, [], []]
+ m(1, [2, [3, 4, 5], 6, 7, 8]).should == [1, 2, 3, [4, 5], [6, 7, 8]]
+ end
+
+ evaluate <<-ruby do
+ def m(a, (b, (c, *d, (e, (*f)), g), (h, (i, j))))
+ [a, b, c, d, e, f, g, h, i, j]
+ end
+ ruby
+
+ m(1, 2).should == [1, 2, nil, [], nil, [nil], nil, nil, nil, nil]
+ result = m(1, [2, [3, 4, 5, [6, [7, 8]], 9], [10, [11, 12]]])
+ result.should == [1, 2, 3, [4, 5], 6, [7, 8], 9, 10, 11, 12]
+ end
+
+ evaluate <<-ruby do
+ def m(a, b=1) [a, b] end
+ ruby
+
+ m(2).should == [2, 1]
+ m(1, 2).should == [1, 2]
+ end
+
+ evaluate <<-ruby do
+ def m(a, *) a end
+ ruby
+
+ m(1).should == 1
+ m(1, 2, 3).should == 1
+ end
+
+ evaluate <<-ruby do
+ def m(a, *b) [a, b] end
+ ruby
+
+ m(1).should == [1, []]
+ m(1, 2, 3).should == [1, [2, 3]]
+ end
+
+ evaluate <<-ruby do
+ def m(a, b:) [a, b] end
+ ruby
+
+ m(1, b: 2).should == [1, 2]
+ lambda { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ def m(a, b: 1) [a, b] end
+ ruby
+
+ m(2).should == [2, 1]
+ m(1, b: 2).should == [1, 2]
+ m("a" => 1, b: 2).should == [{"a" => 1, b: 2}, 1]
+ end
+
+ evaluate <<-ruby do
+ def m(a, **) a end
+ ruby
+
+ m(1).should == 1
+ m(1, a: 2, b: 3).should == 1
+ m("a" => 1, b: 2).should == {"a" => 1, b: 2}
+ end
+
+ evaluate <<-ruby do
+ def m(a, **k) [a, k] end
+ ruby
+
+ m(1).should == [1, {}]
+ m(1, a: 2, b: 3).should == [1, {a: 2, b: 3}]
+ m("a" => 1, b: 2).should == [{"a" => 1, b: 2}, {}]
+ end
+
+ evaluate <<-ruby do
+ def m(a, &b) [a, b] end
+ ruby
+
+ m(1).should == [1, nil]
+ m(1, &(l = -> {})).should == [1, l]
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, b) [a, b] end
+ ruby
+
+ m(2).should == [1, 2]
+ m(2, 3).should == [2, 3]
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, *) a end
+ ruby
+
+ m().should == 1
+ m(2, 3, 4).should == 2
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, *b) [a, b] end
+ ruby
+
+ m().should == [1, []]
+ m(2, 3, 4).should == [2, [3, 4]]
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, (b, c)) [a, b, c] end
+ ruby
+
+ m(2).should == [1, 2, nil]
+ m(2, 3).should == [2, 3, nil]
+ m(2, [3, 4, 5]).should == [2, 3, 4]
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, (b, (c, *d))) [a, b, c, d] end
+ ruby
+
+ m(2).should == [1, 2, nil, []]
+ m(2, 3).should == [2, 3, nil, []]
+ m(2, [3, [4, 5, 6], 7]).should == [2, 3, 4, [5, 6]]
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, (b, (c, *d), *e)) [a, b, c, d, e] end
+ ruby
+
+ m(2).should == [1, 2, nil, [], []]
+ m(2, [3, 4, 5, 6]).should == [2, 3, 4, [], [5, 6]]
+ m(2, [3, [4, 5, 6], 7]).should == [2, 3, 4, [5, 6], [7]]
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, (b), (c)) [a, b, c] end
+ ruby
+
+ m(2, 3).should == [1, 2, 3]
+ m(2, 3, 4).should == [2, 3, 4]
+ m(2, [3, 4], [5, 6, 7]).should == [2, 3, 5]
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, (*b), (*c)) [a, b, c] end
+ ruby
+
+ lambda { m() }.should raise_error(ArgumentError)
+ lambda { m(2) }.should raise_error(ArgumentError)
+ m(2, 3).should == [1, [2], [3]]
+ m(2, [3, 4], [5, 6]).should == [2, [3, 4], [5, 6]]
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, (b, c), (d, e)) [a, b, c, d, e] end
+ ruby
+
+ m(2, 3).should == [1, 2, nil, 3, nil]
+ m(2, [3, 4, 5], [6, 7, 8]).should == [2, 3, 4, 6, 7]
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, (b, *c), (*d, e))
+ [a, b, c, d, e]
+ end
+ ruby
+
+ m(1, 2).should == [1, 1, [], [], 2]
+ m(1, [2, 3], [4, 5, 6]).should == [1, 2, [3], [4, 5], 6]
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, (b, *c), (d, (*e, f)))
+ [a, b, c, d, e, f]
+ end
+ ruby
+
+ m(1, 2).should == [1, 1, [], 2, [], nil]
+ m(nil, nil).should == [1, nil, [], nil, [], nil]
+ result = m([1, 2, 3], [4, 5, 6], [7, 8, 9])
+ result.should == [[1, 2, 3], 4, [5, 6], 7, [], 8]
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, b:) [a, b] end
+ ruby
+
+ m(b: 2).should == [1, 2]
+ m(2, b: 1).should == [2, 1]
+ m("a" => 1, b: 2).should == [{"a" => 1}, 2]
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, b: 2) [a, b] end
+ ruby
+
+ m().should == [1, 2]
+ m(2).should == [2, 2]
+ m(b: 3).should == [1, 3]
+ m("a" => 1, b: 2).should == [{"a" => 1}, 2]
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, **) a end
+ ruby
+
+ m().should == 1
+ m(2, a: 1, b: 0).should == 2
+ m("a" => 1, a: 2).should == {"a" => 1}
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, **k) [a, k] end
+ ruby
+
+ m().should == [1, {}]
+ m(2, a: 1, b: 2).should == [2, {a: 1, b: 2}]
+ end
+
+ evaluate <<-ruby do
+ def m(a=1, &b) [a, b] end
+ ruby
+
+ m().should == [1, nil]
+ m(&(l = -> {})).should == [1, l]
+
+ p = -> {}
+ l = mock("to_proc")
+ l.should_receive(:to_proc).and_return(p)
+ m(&l).should == [1, p]
+ end
+
+ evaluate <<-ruby do
+ def m(*, a) a end
+ ruby
+
+ m(1).should == 1
+ m(1, 2, 3).should == 3
+ end
+
+ evaluate <<-ruby do
+ def m(*a, b) [a, b] end
+ ruby
+
+ m(1).should == [[], 1]
+ m(1, 2, 3).should == [[1, 2], 3]
+ end
+
+ evaluate <<-ruby do
+ def m(*, a:) a end
+ ruby
+
+ m(a: 1).should == 1
+ m(1, 2, a: 3).should == 3
+ m("a" => 1, a: 2).should == 2
+ end
+
+ evaluate <<-ruby do
+ def m(*a, b:) [a, b] end
+ ruby
+
+ m(b: 1).should == [[], 1]
+ m(1, 2, b: 3).should == [[1, 2], 3]
+ m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
+ end
+
+ evaluate <<-ruby do
+ def m(*, a: 1) a end
+ ruby
+
+ m().should == 1
+ m(1, 2).should == 1
+ m(a: 2).should == 2
+ m(1, a: 2).should == 2
+ m("a" => 1, a: 2).should == 2
+ end
+
+ evaluate <<-ruby do
+ def m(*a, b: 1) [a, b] end
+ ruby
+
+ m().should == [[], 1]
+ m(1, 2, 3, b: 4).should == [[1, 2, 3], 4]
+ m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
+
+ a = mock("splat")
+ a.should_not_receive(:to_ary)
+ m(*a).should == [[a], 1]
+ end
+
+ evaluate <<-ruby do
+ def m(*, **) end
+ ruby
+
+ m().should be_nil
+ m(a: 1, b: 2).should be_nil
+ m(1, 2, 3, a: 4, b: 5).should be_nil
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash).and_return({a: 1})
+ m(h).should be_nil
+
+ h = mock("keyword splat")
+ error = RuntimeError.new("error while converting to a hash")
+ h.should_receive(:to_hash).and_raise(error)
+ lambda { m(h) }.should raise_error(error)
+ end
+
+ evaluate <<-ruby do
+ def m(*a, **) a end
+ ruby
+
+ m().should == []
+ m(1, 2, 3, a: 4, b: 5).should == [1, 2, 3]
+ m("a" => 1, a: 1).should == [{"a" => 1}]
+ m(1, **{a: 2}).should == [1]
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash)
+ lambda { m(**h) }.should raise_error(TypeError)
+ end
+
+ evaluate <<-ruby do
+ def m(*, **k) k end
+ ruby
+
+ m().should == {}
+ m(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
+ m("a" => 1, a: 1).should == {a: 1}
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash).and_return({a: 1})
+ m(h).should == {a: 1}
+ end
+
+ evaluate <<-ruby do
+ def m(a = nil, **k) [a, k] end
+ ruby
+
+ m().should == [nil, {}]
+ m("a" => 1).should == [{"a" => 1}, {}]
+ m(a: 1).should == [nil, {a: 1}]
+ m("a" => 1, a: 1).should == [{"a" => 1}, {a: 1}]
+ m({ "a" => 1 }, a: 1).should == [{"a" => 1}, {a: 1}]
+ m({a: 1}, {}).should == [{a: 1}, {}]
+
+ h = {"a" => 1, b: 2}
+ m(h).should == [{"a" => 1}, {b: 2}]
+ h.should == {"a" => 1, b: 2}
+
+ h = {"a" => 1}
+ m(h).first.should == h
+
+ h = {}
+ r = m(h)
+ r.first.should be_nil
+ r.last.should == {}
+
+ hh = {}
+ h = mock("keyword splat empty hash")
+ h.should_receive(:to_hash).and_return(hh)
+ r = m(h)
+ r.first.should be_nil
+ r.last.should == {}
+
+ h = mock("keyword splat")
+ h.should_receive(:to_hash).and_return({"a" => 1, a: 2})
+ m(h).should == [{"a" => 1}, {a: 2}]
+ end
+
+ evaluate <<-ruby do
+ def m(*a, **k) [a, k] end
+ ruby
+
+ m().should == [[], {}]
+ m(1).should == [[1], {}]
+ m(a: 1, b: 2).should == [[], {a: 1, b: 2}]
+ m(1, 2, 3, a: 2).should == [[1, 2, 3], {a: 2}]
+
+ m("a" => 1).should == [[{"a" => 1}], {}]
+ m(a: 1).should == [[], {a: 1}]
+ m("a" => 1, a: 1).should == [[{"a" => 1}], {a: 1}]
+ m({ "a" => 1 }, a: 1).should == [[{"a" => 1}], {a: 1}]
+ m({a: 1}, {}).should == [[{a: 1}], {}]
+ m({a: 1}, {"a" => 1}).should == [[{a: 1}, {"a" => 1}], {}]
+
+ bo = BasicObject.new
+ def bo.to_a; [1, 2, 3]; end
+ def bo.to_hash; {:b => 2, :c => 3}; end
+
+ m(*bo, **bo).should == [[1, 2, 3], {:b => 2, :c => 3}]
+ end
+
+ evaluate <<-ruby do
+ def m(*, &b) b end
+ ruby
+
+ m().should be_nil
+ m(1, 2, 3, 4).should be_nil
+ m(&(l = ->{})).should equal(l)
+ end
+
+ evaluate <<-ruby do
+ def m(*a, &b) [a, b] end
+ ruby
+
+ m().should == [[], nil]
+ m(1).should == [[1], nil]
+ m(1, 2, 3, &(l = -> {})).should == [[1, 2, 3], l]
+ end
+
+ evaluate <<-ruby do
+ def m(a:, b:) [a, b] end
+ ruby
+
+ m(a: 1, b: 2).should == [1, 2]
+ lambda { m("a" => 1, a: 1, b: 2) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ def m(a:, b: 1) [a, b] end
+ ruby
+
+ m(a: 1).should == [1, 1]
+ m(a: 1, b: 2).should == [1, 2]
+ lambda { m("a" => 1, a: 1, b: 2) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ def m(a:, **) a end
+ ruby
+
+ m(a: 1).should == 1
+ m(a: 1, b: 2).should == 1
+ lambda { m("a" => 1, a: 1, b: 2) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ def m(a:, **k) [a, k] end
+ ruby
+
+ m(a: 1).should == [1, {}]
+ m(a: 1, b: 2, c: 3).should == [1, {b: 2, c: 3}]
+ lambda { m("a" => 1, a: 1, b: 2) }.should raise_error(ArgumentError)
+ end
+
+ evaluate <<-ruby do
+ def m(a:, &b) [a, b] end
+ ruby
+
+ m(a: 1).should == [1, nil]
+ m(a: 1, &(l = ->{})).should == [1, l]
+ end
+
+ evaluate <<-ruby do
+ def m(a: 1, b:) [a, b] end
+ ruby
+
+ m(b: 0).should == [1, 0]
+ m(b: 2, a: 3).should == [3, 2]
+ end
+
+ evaluate <<-ruby do
+ def m(a: def m(a: 1) a end, b:)
+ [a, b]
+ end
+ ruby
+
+ m(a: 2, b: 3).should == [2, 3]
+ m(b: 1).should == [:m, 1]
+
+ # Note the default value of a: in the original method.
+ m().should == 1
+ end
+
+ evaluate <<-ruby do
+ def m(a: 1, b: 2) [a, b] end
+ ruby
+
+ m().should == [1, 2]
+ m(b: 3, a: 4).should == [4, 3]
+ end
+
+ evaluate <<-ruby do
+ def m(a: 1, **) a end
+ ruby
+
+ m().should == 1
+ m(a: 2, b: 1).should == 2
+ end
+
+ evaluate <<-ruby do
+ def m(a: 1, **k) [a, k] end
+ ruby
+
+ m(b: 2, c: 3).should == [1, {b: 2, c: 3}]
+ end
+
+ evaluate <<-ruby do
+ def m(a: 1, &b) [a, b] end
+ ruby
+
+ m(&(l = ->{})).should == [1, l]
+ m().should == [1, nil]
+ end
+
+ evaluate <<-ruby do
+ def m(**, &b) b end
+ ruby
+
+ m(a: 1, b: 2, &(l = ->{})).should == l
+ end
+
+ evaluate <<-ruby do
+ def m(**k, &b) [k, b] end
+ ruby
+
+ m(a: 1, b: 2).should == [{ a: 1, b: 2}, nil]
+ end
+
+ evaluate <<-ruby do
+ def m(a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l)
+ [a, b, c, d, e, f, g, h, k, l]
+ end
+ ruby
+
+ result = m(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{}))
+ result.should == [9, 8, [7], [], 6, 5, 4, 3, {}, l]
+ end
+
+ evaluate <<-ruby do
+ def m(a, b=1, *c, d, e:, f: 2, g:, **k, &l)
+ [a, b, c, d, e, f, g, k, l]
+ end
+ ruby
+
+ result = m(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{}))
+ result.should == [1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l]
+ end
+
+ evaluate <<-ruby do
+ def m(a, b = nil, c = nil, d, e: nil, **f)
+ [a, b, c, d, e, f]
+ end
+ ruby
+
+ result = m(1, 2)
+ result.should == [1, nil, nil, 2, nil, {}]
+
+ result = m(1, 2, {foo: :bar})
+ result.should == [1, nil, nil, 2, nil, {foo: :bar}]
+
+ result = m(1, {foo: :bar})
+ result.should == [1, nil, nil, {foo: :bar}, nil, {}]
+ end
+ end
+
+ context "assigns keyword arguments from a passed Hash without modifying it" do
+ evaluate <<-ruby do
+ def m(a: nil); a; end
+ ruby
+
+ options = {a: 1}.freeze
+ lambda do
+ m(options).should == 1
+ end.should_not raise_error
+ options.should == {a: 1}
+ end
+ end
+end
+
+describe "A method call with a space between method name and parentheses" do
+ before(:each) do
+ def m(*args)
+ args
+ end
+
+ def n(value, &block)
+ [value, block.call]
+ end
+ end
+
+ context "when no arguments provided" do
+ it "assigns nil" do
+ args = m ()
+ args.should == [nil]
+ end
+ end
+
+ context "when a single argument provided" do
+ it "assigns it" do
+ args = m (1 == 1 ? true : false)
+ args.should == [true]
+ end
+ end
+
+ context "when 2+ arguments provided" do
+ it "raises a syntax error" do
+ lambda {
+ eval("m (1, 2)")
+ }.should raise_error(SyntaxError)
+
+ lambda {
+ eval("m (1, 2, 3)")
+ }.should raise_error(SyntaxError)
+ end
+ end
+
+ it "allows to pass a block with curly braces" do
+ args = n () { :block_value }
+ args.should == [nil, :block_value]
+
+ args = n (1) { :block_value }
+ args.should == [1, :block_value]
+ end
+
+ it "allows to pass a block with do/end" do
+ args = n () do
+ :block_value
+ end
+ args.should == [nil, :block_value]
+
+ args = n (1) do
+ :block_value
+ end
+ args.should == [1, :block_value]
+ end
+end
+
+describe "An array-dereference method ([])" do
+ SpecEvaluate.desc = "for definition"
+
+ context "received the passed-in block" do
+ evaluate <<-ruby do
+ def [](*, &b)
+ b.call
+ end
+ ruby
+ pr = proc {:ok}
+
+ self[&pr].should == :ok
+ self['foo', &pr].should == :ok
+ self.[](&pr).should == :ok
+ self.[]('foo', &pr).should == :ok
+ end
+
+ evaluate <<-ruby do
+ def [](*)
+ yield
+ end
+ ruby
+ pr = proc {:ok}
+
+ self[&pr].should == :ok
+ self['foo', &pr].should == :ok
+ self.[](&pr).should == :ok
+ self.[]('foo', &pr).should == :ok
+ end
+ end
+end
diff --git a/spec/ruby/language/module_spec.rb b/spec/ruby/language/module_spec.rb
new file mode 100644
index 0000000000..72d0046fb8
--- /dev/null
+++ b/spec/ruby/language/module_spec.rb
@@ -0,0 +1,91 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/module'
+
+describe "The module keyword" do
+ it "creates a new module without semicolon" do
+ module ModuleSpecsKeywordWithoutSemicolon end
+ ModuleSpecsKeywordWithoutSemicolon.should be_an_instance_of(Module)
+ end
+
+ it "creates a new module with a non-qualified constant name" do
+ module ModuleSpecsToplevel; end
+ ModuleSpecsToplevel.should be_an_instance_of(Module)
+ end
+
+ it "creates a new module with a qualified constant name" do
+ module ModuleSpecs::Nested; end
+ ModuleSpecs::Nested.should be_an_instance_of(Module)
+ end
+
+ it "creates a new module with a variable qualified constant name" do
+ m = Module.new
+ module m::N; end
+ m::N.should be_an_instance_of(Module)
+ end
+
+ it "reopens an existing module" do
+ module ModuleSpecs; Reopened = true; end
+ 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
+ end
+
+ it "raises a TypeError if the constant is a Class" do
+ lambda do
+ module ModuleSpecs::Modules::Klass; end
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the constant is a String" do
+ lambda { module ModuleSpecs::Modules::A; end }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the constant is a Fixnum" do
+ lambda { module ModuleSpecs::Modules::B; end }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the constant is nil" do
+ lambda { module ModuleSpecs::Modules::C; end }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the constant is true" do
+ lambda { module ModuleSpecs::Modules::D; end }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the constant is false" do
+ lambda { module ModuleSpecs::Modules::D; end }.should raise_error(TypeError)
+ end
+end
+
+describe "Assigning an anonymous module to a constant" do
+ it "sets the name of the module" do
+ mod = Module.new
+ mod.name.should be_nil
+
+ ::ModuleSpecs_CS1 = mod
+ mod.name.should == "ModuleSpecs_CS1"
+ end
+
+ it "does not set the name of a module scoped by an anonymous module" do
+ a, b = Module.new, Module.new
+ a::B = b
+ b.name.should be_nil
+ end
+
+ it "sets the name of contained modules when assigning a toplevel anonymous module" do
+ a, b, c, d = Module.new, Module.new, Module.new, Module.new
+ a::B = b
+ a::B::C = c
+ a::B::C::E = c
+ a::D = d
+
+ ::ModuleSpecs_CS2 = a
+ a.name.should == "ModuleSpecs_CS2"
+ b.name.should == "ModuleSpecs_CS2::B"
+ c.name.should == "ModuleSpecs_CS2::B::C"
+ d.name.should == "ModuleSpecs_CS2::D"
+ end
+end
diff --git a/spec/ruby/language/next_spec.rb b/spec/ruby/language/next_spec.rb
new file mode 100644
index 0000000000..e0a0265ac6
--- /dev/null
+++ b/spec/ruby/language/next_spec.rb
@@ -0,0 +1,410 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/next'
+
+describe "The next statement from within the block" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "ends block execution" do
+ a = []
+ lambda {
+ a << 1
+ next
+ a << 2
+ }.call
+ a.should == [1]
+ end
+
+ it "causes block to return nil if invoked without arguments" do
+ lambda { 123; next; 456 }.call.should == nil
+ end
+
+ it "causes block to return nil if invoked with an empty expression" do
+ lambda { next (); 456 }.call.should be_nil
+ end
+
+ it "returns the argument passed" do
+ lambda { 123; next 234; 345 }.call.should == 234
+ end
+
+ it "returns to the invoking method" do
+ NextSpecs.yielding_method(nil) { next }.should == :method_return_value
+ end
+
+ it "returns to the invoking method, with the specified value" do
+ NextSpecs.yielding_method(nil) {
+ next nil;
+ fail("next didn't end the block execution")
+ }.should == :method_return_value
+
+ NextSpecs.yielding_method(1) {
+ next 1
+ fail("next didn't end the block execution")
+ }.should == :method_return_value
+
+ NextSpecs.yielding_method([1, 2, 3]) {
+ next 1, 2, 3
+ fail("next didn't end the block execution")
+ }.should == :method_return_value
+ end
+
+ it "returns to the currently yielding method in case of chained calls" do
+ class ChainedNextTest
+ def self.meth_with_yield(&b)
+ yield.should == :next_return_value
+ :method_return_value
+ end
+ def self.invoking_method(&b)
+ meth_with_yield(&b)
+ end
+ def self.enclosing_method
+ invoking_method do
+ next :next_return_value
+ :wrong_return_value
+ end
+ end
+ end
+
+ ChainedNextTest.enclosing_method.should == :method_return_value
+ end
+
+ it "causes ensure blocks to run" do
+ [1].each do |i|
+ begin
+ ScratchPad << :begin
+ next
+ ensure
+ ScratchPad << :ensure
+ end
+ end
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "skips following code outside an exception block" do
+ 3.times do |i|
+ begin
+ ScratchPad << :begin
+ next if i == 0
+ break if i == 2
+ ScratchPad << :begin_end
+ ensure
+ ScratchPad << :ensure
+ end
+
+ ScratchPad << :after
+ end
+
+ ScratchPad.recorded.should == [
+ :begin, :ensure, :begin, :begin_end, :ensure, :after, :begin, :ensure]
+ end
+
+ it "passes the value returned by a method with omitted parenthesis and passed block" do
+ obj = NextSpecs::Block.new
+ lambda { next obj.method :value do |x| x end }.call.should == :value
+ end
+end
+
+describe "The next statement" do
+ describe "in a method" do
+ it "is invalid and raises a SyntaxError" do
+ lambda {
+ eval("def m; next; end")
+ }.should raise_error(SyntaxError)
+ end
+ end
+end
+
+describe "The next statement" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ describe "in a while loop" do
+ describe "when not passed an argument" do
+ it "causes ensure blocks to run" do
+ NextSpecs.while_next(false)
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "causes ensure blocks to run when nested in an block" do
+ NextSpecs.while_within_iter(false)
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+ end
+
+ describe "when passed an argument" do
+ it "causes ensure blocks to run" do
+ NextSpecs.while_next(true)
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "causes ensure blocks to run when nested in an block" do
+ NextSpecs.while_within_iter(true)
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+ end
+
+ it "causes nested ensure blocks to run" do
+ x = true
+ while x
+ begin
+ ScratchPad << :outer_begin
+ x = false
+ begin
+ ScratchPad << :inner_begin
+ next
+ ensure
+ ScratchPad << :inner_ensure
+ end
+ ensure
+ ScratchPad << :outer_ensure
+ end
+ end
+
+ ScratchPad.recorded.should == [:outer_begin, :inner_begin, :inner_ensure, :outer_ensure]
+ end
+
+ it "causes ensure blocks to run when mixed with break" do
+ x = 1
+ while true
+ begin
+ ScratchPad << :begin
+ break if x > 1
+ x += 1
+ next
+ ensure
+ ScratchPad << :ensure
+ end
+ end
+
+ ScratchPad.recorded.should == [:begin, :ensure, :begin, :ensure]
+ end
+ end
+
+ describe "in an until loop" do
+ describe "when not passed an argument" do
+ it "causes ensure blocks to run" do
+ NextSpecs.until_next(false)
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "causes ensure blocks to run when nested in an block" do
+ NextSpecs.until_within_iter(false)
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+ end
+
+ describe "when passed an argument" do
+ it "causes ensure blocks to run" do
+ NextSpecs.until_next(true)
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "causes ensure blocks to run when nested in an block" do
+ NextSpecs.until_within_iter(true)
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+ end
+
+ it "causes nested ensure blocks to run" do
+ x = false
+ until x
+ begin
+ ScratchPad << :outer_begin
+ x = true
+ begin
+ ScratchPad << :inner_begin
+ next
+ ensure
+ ScratchPad << :inner_ensure
+ end
+ ensure
+ ScratchPad << :outer_ensure
+ end
+ end
+
+ ScratchPad.recorded.should == [:outer_begin, :inner_begin, :inner_ensure, :outer_ensure]
+ end
+
+ it "causes ensure blocks to run when mixed with break" do
+ x = 1
+ until false
+ begin
+ ScratchPad << :begin
+ break if x > 1
+ x += 1
+ next
+ ensure
+ ScratchPad << :ensure
+ end
+ end
+
+ ScratchPad.recorded.should == [:begin, :ensure, :begin, :ensure]
+ end
+ end
+
+ describe "in a loop" do
+ describe "when not passed an argument" do
+ it "causes ensure blocks to run" do
+ NextSpecs.loop_next(false)
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "causes ensure blocks to run when nested in an block" do
+ NextSpecs.loop_within_iter(false)
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+ end
+
+ describe "when passed an argument" do
+ it "causes ensure blocks to run" do
+ NextSpecs.loop_next(true)
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "causes ensure blocks to run when nested in an block" do
+ NextSpecs.loop_within_iter(true)
+
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+ end
+
+ it "causes nested ensure blocks to run" do
+ x = 1
+ loop do
+ break if x == 2
+
+ begin
+ ScratchPad << :outer_begin
+ begin
+ ScratchPad << :inner_begin
+ x += 1
+ next
+ ensure
+ ScratchPad << :inner_ensure
+ end
+ ensure
+ ScratchPad << :outer_ensure
+ end
+ end
+
+ ScratchPad.recorded.should == [:outer_begin, :inner_begin, :inner_ensure, :outer_ensure]
+ end
+
+ it "causes ensure blocks to run when mixed with break" do
+ x = 1
+ loop do
+ begin
+ ScratchPad << :begin
+ break if x > 1
+ x += 1
+ next
+ ensure
+ ScratchPad << :ensure
+ end
+ end
+
+ ScratchPad.recorded.should == [:begin, :ensure, :begin, :ensure]
+ end
+ end
+end
+
+describe "Assignment via next" do
+ it "assigns objects" do
+ def r(val); a = yield(); val.should == a; end
+ r(nil){next}
+ r(nil){next nil}
+ r(1){next 1}
+ r([]){next []}
+ r([1]){next [1]}
+ r([nil]){next [nil]}
+ r([[]]){next [[]]}
+ r([]){next [*[]]}
+ r([1]){next [*[1]]}
+ r([1,2]){next [*[1,2]]}
+ end
+
+ it "assigns splatted objects" do
+ def r(val); a = yield(); val.should == a; end
+ r([]){next *nil}
+ r([1]){next *1}
+ r([]){next *[]}
+ r([1]){next *[1]}
+ r([nil]){next *[nil]}
+ r([[]]){next *[[]]}
+ r([]){next *[*[]]}
+ r([1]){next *[*[1]]}
+ r([1,2]){next *[*[1,2]]}
+ end
+
+ it "assigns objects to a splatted reference" do
+ def r(val); *a = yield(); val.should == a; end
+ r([nil]){next}
+ r([nil]){next nil}
+ r([1]){next 1}
+ r([]){next []}
+ r([1]){next [1]}
+ r([nil]){next [nil]}
+ r([[]]){next [[]]}
+ r([1,2]){next [1,2]}
+ r([]){next [*[]]}
+ r([1]){next [*[1]]}
+ r([1,2]){next [*[1,2]]}
+ end
+
+ it "assigns splatted objects to a splatted reference via a splatted yield" do
+ def r(val); *a = *yield(); val.should == a; end
+ r([]){next *nil}
+ r([1]){next *1}
+ r([]){next *[]}
+ r([1]){next *[1]}
+ r([nil]){next *[nil]}
+ r([[]]){next *[[]]}
+ r([1,2]){next *[1,2]}
+ r([]){next *[*[]]}
+ r([1]){next *[*[1]]}
+ r([1,2]){next *[*[1,2]]}
+ end
+
+ it "assigns objects to multiple variables" do
+ def r(val); a,b,*c = yield(); val.should == [a,b,c]; end
+ r([nil,nil,[]]){next}
+ r([nil,nil,[]]){next nil}
+ r([1,nil,[]]){next 1}
+ r([nil,nil,[]]){next []}
+ r([1,nil,[]]){next [1]}
+ r([nil,nil,[]]){next [nil]}
+ r([[],nil,[]]){next [[]]}
+ r([1,2,[]]){next [1,2]}
+ r([nil,nil,[]]){next [*[]]}
+ r([1,nil,[]]){next [*[1]]}
+ r([1,2,[]]){next [*[1,2]]}
+ end
+
+ it "assigns splatted objects to multiple variables" do
+ def r(val); a,b,*c = *yield(); val.should == [a,b,c]; end
+ r([nil,nil,[]]){next *nil}
+ r([1,nil,[]]){next *1}
+ r([nil,nil,[]]){next *[]}
+ r([1,nil,[]]){next *[1]}
+ r([nil,nil,[]]){next *[nil]}
+ r([[],nil,[]]){next *[[]]}
+ r([1,2,[]]){next *[1,2]}
+ r([nil,nil,[]]){next *[*[]]}
+ r([1,nil,[]]){next *[*[1]]}
+ r([1,2,[]]){next *[*[1,2]]}
+ end
+end
diff --git a/spec/ruby/language/not_spec.rb b/spec/ruby/language/not_spec.rb
new file mode 100644
index 0000000000..052af9b256
--- /dev/null
+++ b/spec/ruby/language/not_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../spec_helper'
+
+describe "The not keyword" do
+ it "negates a `true' value" do
+ (not true).should be_false
+ (not 'true').should be_false
+ end
+
+ it "negates a `false' value" do
+ (not false).should be_true
+ (not nil).should be_true
+ end
+
+ it "accepts an argument" do
+ not(true).should be_false
+ end
+
+ it "returns false if the argument is true" do
+ (not(true)).should be_false
+ end
+
+ it "returns true if the argument is false" do
+ (not(false)).should be_true
+ end
+
+ it "returns true if the argument is nil" do
+ (not(nil)).should be_true
+ end
+end
+
+describe "The `!' keyword" do
+ it "negates a `true' value" do
+ (!true).should be_false
+ (!'true').should be_false
+ end
+
+ it "negates a `false' value" do
+ (!false).should be_true
+ (!nil).should be_true
+ end
+
+ it "doubled turns a truthful object into `true'" do
+ (!!true).should be_true
+ (!!'true').should be_true
+ end
+
+ it "doubled turns a not truthful object into `false'" do
+ (!!false).should be_false
+ (!!nil).should be_false
+ end
+end
diff --git a/spec/ruby/language/numbers_spec.rb b/spec/ruby/language/numbers_spec.rb
new file mode 100644
index 0000000000..080c4ae6a3
--- /dev/null
+++ b/spec/ruby/language/numbers_spec.rb
@@ -0,0 +1,97 @@
+require_relative '../spec_helper'
+
+describe "A number literal" do
+
+ it "can be a sequence of decimal digits" do
+ 435.should == 435
+ end
+
+ it "can have '_' characters between digits" do
+ 4_3_5_7.should == 4357
+ end
+
+ it "cannot have a leading underscore" do
+ lambda { eval("_4_2") }.should raise_error(NameError)
+ end
+
+ it "can have a decimal point" do
+ 4.35.should == 4.35
+ end
+
+ it "must have a digit before the decimal point" do
+ 0.75.should == 0.75
+ lambda { eval(".75") }.should raise_error(SyntaxError)
+ lambda { eval("-.75") }.should raise_error(SyntaxError)
+ end
+
+ it "can have an exponent" do
+ 1.2e-3.should == 0.0012
+ end
+
+ it "can be a sequence of hexadecimal digits with a leading '0x'" do
+ 0xffff.should == 65535
+ end
+
+ it "can be a sequence of binary digits with a leading '0x'" do
+ 0b01011.should == 11
+ end
+
+ it "can be a sequence of octal digits with a leading '0'" do
+ 0377.should == 255
+ end
+
+ it "can be an integer literal with trailing 'r' to represent a Rational" do
+ eval('3r').should == Rational(3, 1)
+ eval('-3r').should == Rational(-3, 1)
+ end
+
+ it "can be an bignum literal with trailing 'r' to represent a Rational" do
+ eval('1111111111111111111111111111111111111111111111r').should == Rational(1111111111111111111111111111111111111111111111, 1)
+ eval('-1111111111111111111111111111111111111111111111r').should == Rational(-1111111111111111111111111111111111111111111111, 1)
+ end
+
+ it "can be a decimal literal with trailing 'r' to represent a Rational" do
+ eval('0.3r').should == Rational(3, 10)
+ eval('-0.3r').should == Rational(-3, 10)
+ end
+
+ it "can be a hexadecimal literal with trailing 'r' to represent a Rational" do
+ eval('0xffr').should == Rational(255, 1)
+ eval('-0xffr').should == Rational(-255, 1)
+ end
+
+ it "can be an octal literal with trailing 'r' to represent a Rational" do
+ eval('042r').should == Rational(34, 1)
+ eval('-042r').should == Rational(-34, 1)
+ end
+
+ it "can be a binary literal with trailing 'r' to represent a Rational" do
+ eval('0b1111r').should == Rational(15, 1)
+ eval('-0b1111r').should == Rational(-15, 1)
+ end
+
+ it "can be an integer literal with trailing 'i' to represent a Complex" do
+ eval('5i').should == Complex(0, 5)
+ eval('-5i').should == Complex(0, -5)
+ end
+
+ it "can be a decimal literal with trailing 'i' to represent a Complex" do
+ eval('0.6i').should == Complex(0, 0.6)
+ eval('-0.6i').should == Complex(0, -0.6)
+ end
+
+ it "can be a hexadecimal literal with trailing 'i' to represent a Complex" do
+ eval('0xffi').should == Complex(0, 255)
+ eval('-0xffi').should == Complex(0, -255)
+ end
+
+ it "can be a octal literal with trailing 'i' to represent a Complex" do
+ eval("042i").should == Complex(0, 34)
+ eval("-042i").should == Complex(0, -34)
+ end
+
+ it "can be a binary literal with trailing 'i' to represent a Complex" do
+ eval('0b1110i').should == Complex(0, 14)
+ eval('-0b1110i').should == Complex(0, -14)
+ end
+end
diff --git a/spec/ruby/language/optional_assignments_spec.rb b/spec/ruby/language/optional_assignments_spec.rb
new file mode 100644
index 0000000000..2f50136ba9
--- /dev/null
+++ b/spec/ruby/language/optional_assignments_spec.rb
@@ -0,0 +1,298 @@
+require_relative '../spec_helper'
+
+describe 'Optional variable assignments' do
+ describe 'using ||=' do
+ describe 'using a single variable' do
+ it 'assigns a new variable' do
+ a ||= 10
+
+ a.should == 10
+ end
+
+ it 're-assigns an existing variable set to false' do
+ a = false
+ a ||= 10
+
+ a.should == 10
+ end
+
+ it 're-assigns an existing variable set to nil' do
+ a = nil
+ a ||= 10
+
+ a.should == 10
+ end
+
+ it 'does not re-assign a variable with a truthy value' do
+ a = 10
+ a ||= 20
+
+ a.should == 10
+ end
+
+ it 'does not evaluate the right side when not needed' do
+ a = 10
+ a ||= raise('should not be executed')
+ a.should == 10
+ end
+
+ it 'does not re-assign a variable with a truthy value when using an inline rescue' do
+ a = 10
+ a ||= 20 rescue 30
+
+ a.should == 10
+ end
+ end
+
+ describe 'using a accessor' do
+ before do
+ klass = Class.new { attr_accessor :b }
+ @a = klass.new
+ end
+
+ it 'assigns a new variable' do
+ @a.b ||= 10
+
+ @a.b.should == 10
+ end
+
+ it 're-assigns an existing variable set to false' do
+ @a.b = false
+ @a.b ||= 10
+
+ @a.b.should == 10
+ end
+
+ it 're-assigns an existing variable set to nil' do
+ @a.b = nil
+ @a.b ||= 10
+
+ @a.b.should == 10
+ end
+
+ it 'does not re-assign a variable with a truthy value' do
+ @a.b = 10
+ @a.b ||= 20
+
+ @a.b.should == 10
+ end
+
+ it 'does not evaluate the right side when not needed' do
+ @a.b = 10
+ @a.b ||= raise('should not be executed')
+ @a.b.should == 10
+ end
+
+ it 'does not re-assign a variable with a truthy value when using an inline rescue' do
+ @a.b = 10
+ @a.b ||= 20 rescue 30
+
+ @a.b.should == 10
+ end
+ end
+ end
+
+ describe 'using &&=' do
+ describe 'using a single variable' do
+ it 'leaves new variable unassigned' do
+ a &&= 10
+
+ a.should == nil
+ end
+
+ it 'leaves false' do
+ a = false
+ a &&= 10
+
+ a.should == false
+ end
+
+ it 'leaves nil' do
+ a = nil
+ a &&= 10
+
+ a.should == nil
+ end
+
+ it 'does not evaluate the right side when not needed' do
+ a = nil
+ a &&= raise('should not be executed')
+ a.should == nil
+ end
+
+ it 'does re-assign a variable with a truthy value' do
+ a = 10
+ a &&= 20
+
+ a.should == 20
+ end
+
+ it 'does re-assign a variable with a truthy value when using an inline rescue' do
+ a = 10
+ a &&= 20 rescue 30
+
+ a.should == 20
+ end
+ end
+
+ describe 'using a single variable' do
+ before do
+ klass = Class.new { attr_accessor :b }
+ @a = klass.new
+ end
+
+ it 'leaves new variable unassigned' do
+ @a.b &&= 10
+
+ @a.b.should == nil
+ end
+
+ it 'leaves false' do
+ @a.b = false
+ @a.b &&= 10
+
+ @a.b.should == false
+ end
+
+ it 'leaves nil' do
+ @a.b = nil
+ @a.b &&= 10
+
+ @a.b.should == nil
+ end
+
+ it 'does not evaluate the right side when not needed' do
+ @a.b = nil
+ @a.b &&= raise('should not be executed')
+ @a.b.should == nil
+ end
+
+ it 'does re-assign a variable with a truthy value' do
+ @a.b = 10
+ @a.b &&= 20
+
+ @a.b.should == 20
+ end
+
+ it 'does re-assign a variable with a truthy value when using an inline rescue' do
+ @a.b = 10
+ @a.b &&= 20 rescue 30
+
+ @a.b.should == 20
+ end
+ end
+
+ describe 'using a #[]' do
+ before do
+ @a = {}
+ klass = Class.new do
+ def [](k)
+ @hash ||= {}
+ @hash[k]
+ end
+
+ def []=(k, v)
+ @hash ||= {}
+ @hash[k] = v
+ 7
+ end
+ end
+ @b = klass.new
+ end
+
+ it 'leaves new variable unassigned' do
+ @a[:k] &&= 10
+
+ @a.key?(:k).should == false
+ end
+
+ it 'leaves false' do
+ @a[:k] = false
+ @a[:k] &&= 10
+
+ @a[:k].should == false
+ end
+
+ it 'leaves nil' do
+ @a[:k] = nil
+ @a[:k] &&= 10
+
+ @a[:k].should == nil
+ end
+
+ it 'does not evaluate the right side when not needed' do
+ @a[:k] = nil
+ @a[:k] &&= raise('should not be executed')
+ @a[:k].should == nil
+ end
+
+ it 'does re-assign a variable with a truthy value' do
+ @a[:k] = 10
+ @a[:k] &&= 20
+
+ @a[:k].should == 20
+ end
+
+ it 'does re-assign a variable with a truthy value when using an inline rescue' do
+ @a[:k] = 10
+ @a[:k] &&= 20 rescue 30
+
+ @a[:k].should == 20
+ end
+
+ it 'returns the assigned value, not the result of the []= method with ||=' do
+ (@b[:k] ||= 12).should == 12
+ end
+
+ it 'returns the assigned value, not the result of the []= method with +=' do
+ @b[:k] = 17
+ (@b[:k] += 12).should == 29
+ end
+ end
+ end
+
+ describe 'using compunded constants' do
+ before :each do
+ Object.send(:remove_const, :A) if defined? Object::A
+ end
+
+ after :each do
+ Object.send(:remove_const, :A) if defined? Object::A
+ end
+
+ it 'with ||= assignments' do
+ Object::A ||= 10
+ Object::A.should == 10
+ end
+
+ it 'with ||= do not reassign' do
+ Object::A = 20
+ Object::A ||= 10
+ Object::A.should == 20
+ end
+
+ it 'with &&= assignments' do
+ Object::A = 20
+ -> {
+ Object::A &&= 10
+ }.should complain(/already initialized constant/)
+ Object::A.should == 10
+ end
+
+ it 'with &&= assignments will fail with non-existent constants' do
+ lambda { Object::A &&= 10 }.should raise_error(NameError)
+ end
+
+ it 'with operator assignments' do
+ Object::A = 20
+ -> {
+ Object::A += 10
+ }.should complain(/already initialized constant/)
+ Object::A.should == 30
+ end
+
+ it 'with operator assignments will fail with non-existent constants' do
+ lambda { Object::A += 10 }.should raise_error(NameError)
+ end
+ end
+end
diff --git a/spec/ruby/language/or_spec.rb b/spec/ruby/language/or_spec.rb
new file mode 100644
index 0000000000..88241f39e6
--- /dev/null
+++ b/spec/ruby/language/or_spec.rb
@@ -0,0 +1,90 @@
+require_relative '../spec_helper'
+
+describe "The || operator" do
+ it "evaluates to true if any of its operands are true" do
+ if false || true || nil
+ x = true
+ end
+ x.should == true
+ end
+
+ it "evaluated to false if all of its operands are false" do
+ if false || nil
+ x = true
+ end
+ x.should == nil
+ end
+
+ it "is evaluated before assignment operators" do
+ x = nil || true
+ x.should == true
+ end
+
+ it "has a lower precedence than the && operator" do
+ x = 1 || false && x = 2
+ x.should == 1
+ end
+
+ it "treats empty expressions as nil" do
+ (() || true).should be_true
+ (() || false).should be_false
+ (true || ()).should be_true
+ (false || ()).should be_nil
+ (() || ()).should be_nil
+ end
+
+ it "has a higher precedence than 'break' in 'break true || false'" do
+ # see also 'break true or false' below
+ lambda { break false || true }.call.should be_true
+ end
+
+ it "has a higher precedence than 'next' in 'next true || false'" do
+ lambda { next false || true }.call.should be_true
+ end
+
+ it "has a higher precedence than 'return' in 'return true || false'" do
+ lambda { return false || true }.call.should be_true
+ end
+end
+
+describe "The or operator" do
+ it "evaluates to true if any of its operands are true" do
+ x = nil
+ if false or true
+ x = true
+ end
+ x.should == true
+ end
+
+ it "is evaluated after variables are assigned" do
+ x = nil or true
+ x.should == nil
+ end
+
+ it "has a lower precedence than the || operator" do
+ x,y = nil
+ x = true || false or y = 1
+ y.should == nil
+ end
+
+ it "treats empty expressions as nil" do
+ (() or true).should be_true
+ (() or false).should be_false
+ (true or ()).should be_true
+ (false or ()).should be_nil
+ (() or ()).should be_nil
+ end
+
+ it "has a lower precedence than 'break' in 'break true or false'" do
+ # see also 'break true || false' above
+ lambda { eval "break true or false" }.should raise_error(SyntaxError, /void value expression/)
+ end
+
+ it "has a lower precedence than 'next' in 'next true or false'" do
+ lambda { eval "next true or false" }.should raise_error(SyntaxError, /void value expression/)
+ end
+
+ it "has a lower precedence than 'return' in 'return true or false'" do
+ lambda { eval "return true or false" }.should raise_error(SyntaxError, /void value expression/)
+ end
+end
diff --git a/spec/ruby/language/order_spec.rb b/spec/ruby/language/order_spec.rb
new file mode 100644
index 0000000000..d550f6b3f4
--- /dev/null
+++ b/spec/ruby/language/order_spec.rb
@@ -0,0 +1,75 @@
+require_relative '../spec_helper'
+
+describe "A method call" do
+ before :each do
+ @obj = Object.new
+ def @obj.foo0(&a)
+ [a ? a.call : nil]
+ end
+ def @obj.foo1(a, &b)
+ [a, b ? b.call : nil]
+ end
+ def @obj.foo2(a, b, &c)
+ [a, b, c ? c.call : nil]
+ end
+ def @obj.foo3(a, b, c, &d)
+ [a, b, c, d ? d.call : nil]
+ end
+ def @obj.foo4(a, b, c, d, &e)
+ [a, b, c, d, e ? e.call : nil]
+ end
+ end
+
+ it "evaluates the receiver first" do
+ (obj = @obj).foo1(obj = nil).should == [nil, nil]
+ (obj = @obj).foo2(obj = nil, obj = nil).should == [nil, nil, nil]
+ (obj = @obj).foo3(obj = nil, obj = nil, obj = nil).should == [nil, nil, nil, nil]
+ (obj = @obj).foo4(obj = nil, obj = nil, obj = nil, obj = nil).should == [nil, nil, nil, nil, nil]
+ end
+
+ it "evaluates arguments after receiver" do
+ a = 0
+ (a += 1; @obj).foo1(a).should == [1, nil]
+ (a += 1; @obj).foo2(a, a).should == [2, 2, nil]
+ (a += 1; @obj).foo3(a, a, a).should == [3, 3, 3, nil]
+ (a += 1; @obj).foo4(a, a, a, a).should == [4, 4, 4, 4, nil]
+ a.should == 4
+ end
+
+ it "evaluates arguments left-to-right" do
+ a = 0
+ @obj.foo1(a += 1).should == [1, nil]
+ @obj.foo2(a += 1, a += 1).should == [2, 3, nil]
+ @obj.foo3(a += 1, a += 1, a += 1).should == [4, 5, 6, nil]
+ @obj.foo4(a += 1, a += 1, a += 1, a += 1).should == [7, 8, 9, 10, nil]
+ a.should == 10
+ end
+
+ it "evaluates block pass after arguments" do
+ a = 0
+ p = proc {true}
+ @obj.foo1(a += 1, &(a += 1; p)).should == [1, true]
+ @obj.foo2(a += 1, a += 1, &(a += 1; p)).should == [3, 4, true]
+ @obj.foo3(a += 1, a += 1, a += 1, &(a += 1; p)).should == [6, 7, 8, true]
+ @obj.foo4(a += 1, a += 1, a += 1, a += 1, &(a += 1; p)).should == [10, 11, 12, 13, true]
+ a.should == 14
+ end
+
+ it "evaluates block pass after receiver" do
+ p1 = proc {true}
+ p2 = proc {false}
+ p1.should_not == p2
+
+ p = p1
+ (p = p2; @obj).foo0(&p).should == [false]
+ p = p1
+ (p = p2; @obj).foo1(1, &p).should == [1, false]
+ p = p1
+ (p = p2; @obj).foo2(1, 1, &p).should == [1, 1, false]
+ p = p1
+ (p = p2; @obj).foo3(1, 1, 1, &p).should == [1, 1, 1, false]
+ p = p1
+ (p = p2; @obj).foo4(1, 1, 1, 1, &p).should == [1, 1, 1, 1, false]
+ p = p1
+ end
+end
diff --git a/spec/ruby/language/precedence_spec.rb b/spec/ruby/language/precedence_spec.rb
new file mode 100644
index 0000000000..a342c82156
--- /dev/null
+++ b/spec/ruby/language/precedence_spec.rb
@@ -0,0 +1,449 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/precedence'
+
+# Specifying the behavior of operators in combination could
+# lead to combinatorial explosion. A better way seems to be
+# to use a technique from formal proofs that involve a set of
+# equivalent statements. Suppose you have statements A, B, C.
+# If they are claimed to be equivalent, this can be shown by
+# proving that A implies B, B implies C, and C implies A.
+# (Actually any closed circuit of implications.)
+#
+# Here, we can use a similar technique where we show starting
+# at the top that each level of operator has precedence over
+# 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
+
+# -----------------------------------------------------------------------
+# It seems that this table is not correct anymore
+# The correct table derived from MRI's parse.y is as follows:
+#
+# Operator Assoc Description
+#---------------------------------------------------------------
+# ! ~ + > Not, complement, unary plus
+# ** > Exponentiation
+# - > Unary minus
+# * / % < Multiply, divide, and modulo
+# + - < Plus and minus
+# >> << < Right and left shift
+# & < “And†(bitwise for integers)
+# ^ | < Exclusive “or†and regular “or†(bitwise for integers)
+# <= < > >= < Comparison operators
+# <=> == === != =~ !~ no Equality and pattern match operators (!=
+# and !~ may not be defined as methods)
+# && < Logical “andâ€
+# || < Logical “orâ€
+# .. ... no Range (inclusive and exclusive)
+# ? : > Ternary if-then-else
+# rescue < Rescue modifier
+# = %= /= -= += |= &= > Assignment
+# >>= <<= *= &&= ||= **=
+# defined? no Check if symbol defined
+# not > Logical negation
+# or and < Logical composition
+# if unless while until no Expression modifiers
+# -----------------------------------------------------------------------
+#
+# [] and []= seem to fall out of here, as well as begin/end
+#
+
+# TODO: Resolve these two tables with actual specs. As the comment at the
+# top suggests, these specs need to be reorganized into a single describe
+# block for each operator. The describe block should include an example
+# for associativity (if relevant), an example for any short circuit behavior
+# (e.g. &&, ||, etc.) and an example block for each operator over which the
+# instant operator has immediately higher precedence.
+
+describe "Operators" do
+ it "! ~ + is right-associative" do
+ (!!true).should == true
+ (~~0).should == 0
+ (++2).should == 2
+ end
+
+ it "** is right-associative" do
+ (2**2**3).should == 256
+ end
+
+ it "** has higher precedence than unary minus" do
+ (-2**2).should == -4
+ end
+
+ it "unary minus is right-associative" do
+ (--2).should == 2
+ end
+
+ it "unary minus has higher precedence than * / %" do
+ class UnaryMinusTest; def -@; 50; end; end
+ b = UnaryMinusTest.new
+
+ (-b * 5).should == 250
+ (-b / 5).should == 10
+ (-b % 7).should == 1
+ end
+
+ it "treats +/- as a regular send if the arguments are known locals or block locals" do
+ a = PrecedenceSpecs::NonUnaryOpTest.new
+ a.add_num(1).should == [3]
+ a.sub_num(1).should == [1]
+ a.add_str.should == ['11']
+ a.add_var.should == [2]
+ end
+
+ it "* / % are left-associative" do
+ (2*1/2).should == (2*1)/2
+ # Guard against the Mathn library
+ # TODO: Make these specs not rely on specific behaviour / result values
+ # by using mocks.
+ guard -> { !defined?(Math.rsqrt) } do
+ (2*1/2).should_not == 2*(1/2)
+ end
+
+ (10/7/5).should == (10/7)/5
+ (10/7/5).should_not == 10/(7/5)
+
+ (101 % 55 % 7).should == (101 % 55) % 7
+ (101 % 55 % 7).should_not == 101 % (55 % 7)
+
+ (50*20/7%42).should == ((50*20)/7)%42
+ (50*20/7%42).should_not == 50*(20/(7%42))
+ end
+
+ it "* / % have higher precedence than + -" do
+ (2+2*2).should == 6
+ (1+10/5).should == 3
+ (2+10%5).should == 2
+
+ (2-2*2).should == -2
+ (1-10/5).should == -1
+ (10-10%4).should == 8
+ end
+
+ it "+ - are left-associative" do
+ (2-3-4).should == -5
+ (4-3+2).should == 3
+
+ binary_plus = Class.new(String) do
+ alias_method :plus, :+
+ def +(a)
+ plus(a) + "!"
+ end
+ end
+ s = binary_plus.new("a")
+
+ (s+s+s).should == (s+s)+s
+ (s+s+s).should_not == s+(s+s)
+ end
+
+ it "+ - have higher precedence than >> <<" do
+ (2<<1+2).should == 16
+ (8>>1+2).should == 1
+ (4<<1-3).should == 1
+ (2>>1-3).should == 8
+ end
+
+ it ">> << are left-associative" do
+ (1 << 2 << 3).should == 32
+ (10 >> 1 >> 1).should == 2
+ (10 << 4 >> 1).should == 80
+ end
+
+ it ">> << have higher precedence than &" do
+ (4 & 2 << 1).should == 4
+ (2 & 4 >> 1).should == 2
+ end
+
+ it "& is left-associative" do
+ class BitwiseAndTest; def &(a); a+1; end; end
+ c = BitwiseAndTest.new
+
+ (c & 5 & 2).should == (c & 5) & 2
+ (c & 5 & 2).should_not == c & (5 & 2)
+ end
+
+ it "& has higher precedence than ^ |" do
+ (8 ^ 16 & 16).should == 24
+ (8 | 16 & 16).should == 24
+ end
+
+ it "^ | are left-associative" do
+ class OrAndXorTest; def ^(a); a+10; end; def |(a); a-10; end; end
+ d = OrAndXorTest.new
+
+ (d ^ 13 ^ 16).should == (d ^ 13) ^ 16
+ (d ^ 13 ^ 16).should_not == d ^ (13 ^ 16)
+
+ (d | 13 | 4).should == (d | 13) | 4
+ (d | 13 | 4).should_not == d | (13 | 4)
+ end
+
+ it "^ | have higher precedence than <= < > >=" do
+ (10 <= 7 ^ 7).should == false
+ (10 < 7 ^ 7).should == false
+ (10 > 7 ^ 7).should == true
+ (10 >= 7 ^ 7).should == true
+ (10 <= 7 | 7).should == false
+ (10 < 7 | 7).should == false
+ (10 > 7 | 7).should == true
+ (10 >= 7 | 7).should == true
+ end
+
+ it "<= < > >= are left-associative" do
+ class ComparisonTest
+ def <=(a); 0; end;
+ def <(a); 0; end;
+ def >(a); 0; end;
+ def >=(a); 0; end;
+ end
+
+ e = ComparisonTest.new
+
+ (e <= 0 <= 1).should == (e <= 0) <= 1
+ (e <= 0 <= 1).should_not == e <= (0 <= 1)
+
+ (e < 0 < 1).should == (e < 0) < 1
+ (e < 0 < 1).should_not == e < (0 < 1)
+
+ (e >= 0 >= 1).should == (e >= 0) >= 1
+ (e >= 0 >= 1).should_not == e >= (0 >= 1)
+
+ (e > 0 > 1).should == (e > 0) > 1
+ (e > 0 > 1).should_not == e > (0 > 1)
+ end
+
+ it "<=> == === != =~ !~ are non-associative" do
+ lambda { eval("1 <=> 2 <=> 3") }.should raise_error(SyntaxError)
+ lambda { eval("1 == 2 == 3") }.should raise_error(SyntaxError)
+ lambda { eval("1 === 2 === 3") }.should raise_error(SyntaxError)
+ lambda { eval("1 != 2 != 3") }.should raise_error(SyntaxError)
+ lambda { eval("1 =~ 2 =~ 3") }.should raise_error(SyntaxError)
+ lambda { eval("1 !~ 2 !~ 3") }.should raise_error(SyntaxError)
+ end
+
+ it "<=> == === != =~ !~ have higher precedence than &&" do
+ (false && 2 <=> 3).should == false
+ (false && 3 == false).should == false
+ (false && 3 === false).should == false
+ (false && 3 != true).should == false
+
+ class FalseClass; def =~(o); o == false; end; end
+ (false && true =~ false).should == (false && (true =~ false))
+ (false && true =~ false).should_not == ((false && true) =~ false)
+ class FalseClass; undef_method :=~; end
+
+ (false && true !~ true).should == false
+ end
+
+ # XXX: figure out how to test it
+ # (a && b) && c equals to a && (b && c) for all a,b,c values I can imagine so far
+ it "&& is left-associative"
+
+ it "&& has higher precedence than ||" do
+ (true || false && false).should == true
+ end
+
+ # XXX: figure out how to test it
+ it "|| is left-associative"
+
+ it "|| has higher precedence than .. ..." do
+ (1..false||10).should == (1..10)
+ (1...false||10).should == (1...10)
+ end
+
+ it ".. ... are non-associative" do
+ lambda { eval("1..2..3") }.should raise_error(SyntaxError)
+ lambda { eval("1...2...3") }.should raise_error(SyntaxError)
+ end
+
+ it ".. ... have higher precedence than ? :" do
+ # Use variables to avoid warnings
+ from = 1
+ to = 2
+ # These are Range instances, not flip-flop
+ suppress_warning do
+ (eval("from..to") ? 3 : 4).should == 3
+ (eval("from...to") ? 3 : 4).should == 3
+ end
+ end
+
+ it "? : is right-associative" do
+ (true ? 2 : 3 ? 4 : 5).should == 2
+ end
+
+ def oops; raise end
+
+ it "? : has higher precedence than rescue" do
+ (true ? oops : 0 rescue 10).should == 10
+ end
+
+ # XXX: figure how to test it (problem similar to || associativity)
+ it "rescue is left-associative"
+
+ it "rescue has higher precedence than =" do
+ a = oops rescue 10
+ a.should == 10
+
+ # rescue doesn't have the same sense for %= /= and friends
+ end
+
+ it "= %= /= -= += |= &= >>= <<= *= &&= ||= **= are right-associative" do
+ a = b = 10
+ a.should == 10
+ b.should == 10
+
+ a = b = 10
+ a %= b %= 3
+ a.should == 0
+ b.should == 1
+
+ a = b = 10
+ a /= b /= 2
+ a.should == 2
+ b.should == 5
+
+ a = b = 10
+ a -= b -= 2
+ a.should == 2
+ b.should == 8
+
+ a = b = 10
+ a += b += 2
+ a.should == 22
+ b.should == 12
+
+ a,b = 32,64
+ a |= b |= 2
+ a.should == 98
+ b.should == 66
+
+ a,b = 25,13
+ a &= b &= 7
+ a.should == 1
+ b.should == 5
+
+ a,b=8,2
+ a >>= b >>= 1
+ a.should == 4
+ b.should == 1
+
+ a,b=8,2
+ a <<= b <<= 1
+ a.should == 128
+ b.should == 4
+
+ a,b=8,2
+ a *= b *= 2
+ a.should == 32
+ b.should == 4
+
+ a,b=10,20
+ a &&= b &&= false
+ a.should == false
+ b.should == false
+
+ a,b=nil,nil
+ a ||= b ||= 10
+ a.should == 10
+ b.should == 10
+
+ a,b=2,3
+ a **= b **= 2
+ a.should == 512
+ b.should == 9
+ end
+
+ it "= %= /= -= += |= &= >>= <<= *= &&= ||= **= have higher precedence than defined? operator" do
+ (defined? a = 10).should == "assignment"
+ (defined? a %= 10).should == "assignment"
+ (defined? a /= 10).should == "assignment"
+ (defined? a -= 10).should == "assignment"
+ (defined? a += 10).should == "assignment"
+ (defined? a |= 10).should == "assignment"
+ (defined? a &= 10).should == "assignment"
+ (defined? a >>= 10).should == "assignment"
+ (defined? a <<= 10).should == "assignment"
+ (defined? a *= 10).should == "assignment"
+ (defined? a &&= 10).should == "assignment"
+ (defined? a ||= 10).should == "assignment"
+ (defined? a **= 10).should == "assignment"
+ end
+
+ # XXX: figure out how to test it
+ it "defined? is non-associative"
+
+ it "defined? has higher precedence than not" do
+ # does it have sense?
+ (not defined? qqq).should == true
+ end
+
+ it "not is right-associative" do
+ (not not false).should == false
+ (not not 10).should == true
+ end
+
+ it "not has higher precedence than or/and" do
+ (not false and false).should == false
+ (not false or true).should == true
+ end
+
+ # XXX: figure out how to test it
+ it "or/and are left-associative"
+
+ it "or/and have higher precedence than if unless while until modifiers" do
+ (1 if 2 and 3).should == 1
+ (1 if 2 or 3).should == 1
+
+ (1 unless false and true).should == 1
+ (1 unless false or false).should == 1
+
+ (1 while true and false).should == nil # would hang upon error
+ (1 while false or false).should == nil
+
+ ((raise until true and false) rescue 10).should == 10
+ (1 until false or true).should == nil # would hang upon error
+ end
+
+ # XXX: it seems to me they are right-associative
+ it "if unless while until are non-associative"
+end
diff --git a/spec/ruby/language/predefined/data_spec.rb b/spec/ruby/language/predefined/data_spec.rb
new file mode 100644
index 0000000000..921d236ee9
--- /dev/null
+++ b/spec/ruby/language/predefined/data_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+
+describe "The DATA constant" do
+ it "exists when the main script contains __END__" do
+ ruby_exe(fixture(__FILE__, "data1.rb")).chomp.should == "true"
+ end
+
+ it "does not exist when the main script contains no __END__" do
+ ruby_exe("puts Object.const_defined?(:DATA)").chomp.should == 'false'
+ end
+
+ it "does not exist when an included file has a __END__" do
+ ruby_exe(fixture(__FILE__, "data2.rb")).chomp.should == "false"
+ end
+
+ it "does not change when an included files also has a __END__" do
+ ruby_exe(fixture(__FILE__, "data3.rb")).chomp.should == "data 3"
+ end
+
+ it "is included in an otherwise empty file" do
+ ap = fixture(__FILE__, "print_data.rb")
+ str = ruby_exe(fixture(__FILE__, "data_only.rb"), options: "-r#{ap}")
+ str.chomp.should == "data only"
+ end
+
+ it "returns a File object with the right offset" do
+ ruby_exe(fixture(__FILE__, "data_offset.rb")).should == "File\n121\n"
+ end
+
+ it "is set even if there is no data after __END__" do
+ ruby_exe(fixture(__FILE__, "empty_data.rb")).should == "31\n\"\"\n"
+ end
+
+ it "is set even if there is no newline after __END__" do
+ path = tmp("no_newline_data.rb")
+ code = File.binread(fixture(__FILE__, "empty_data.rb"))
+ touch(path, "wb") { |f| f.write code.chomp }
+ begin
+ ruby_exe(path).should == "30\n\"\"\n"
+ ensure
+ rm_r path
+ end
+ end
+
+ it "rewinds to the head of the main script" do
+ ruby_exe(fixture(__FILE__, "data5.rb")).chomp.should == "DATA.rewind"
+ end
+end
diff --git a/spec/ruby/language/predefined/fixtures/data1.rb b/spec/ruby/language/predefined/fixtures/data1.rb
new file mode 100644
index 0000000000..cb9572255b
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/data1.rb
@@ -0,0 +1,4 @@
+puts Object.const_defined?(:DATA)
+
+__END__
+data1
diff --git a/spec/ruby/language/predefined/fixtures/data2.rb b/spec/ruby/language/predefined/fixtures/data2.rb
new file mode 100644
index 0000000000..a764ca56d1
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/data2.rb
@@ -0,0 +1,3 @@
+require_relative 'data4'
+
+p Object.const_defined?(:DATA)
diff --git a/spec/ruby/language/predefined/fixtures/data3.rb b/spec/ruby/language/predefined/fixtures/data3.rb
new file mode 100644
index 0000000000..e37313c68b
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/data3.rb
@@ -0,0 +1,6 @@
+require_relative 'data4'
+
+puts DATA.read
+
+__END__
+data 3
diff --git a/spec/ruby/language/predefined/fixtures/data4.rb b/spec/ruby/language/predefined/fixtures/data4.rb
new file mode 100644
index 0000000000..139ef80d7b
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/data4.rb
@@ -0,0 +1,4 @@
+# nothing
+
+__END__
+data 4
diff --git a/spec/ruby/language/predefined/fixtures/data5.rb b/spec/ruby/language/predefined/fixtures/data5.rb
new file mode 100644
index 0000000000..48f060e1a9
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/data5.rb
@@ -0,0 +1,5 @@
+DATA.rewind
+puts DATA.gets
+
+__END__
+data 5
diff --git a/spec/ruby/language/predefined/fixtures/data_offset.rb b/spec/ruby/language/predefined/fixtures/data_offset.rb
new file mode 100644
index 0000000000..9829b3f87f
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/data_offset.rb
@@ -0,0 +1,12 @@
+# some comment
+
+foo = <<HEREDOC
+some heredoc to make the
+spec more interesting
+HEREDOC
+
+p DATA.class
+p DATA.pos
+
+__END__
+data offset
diff --git a/spec/ruby/language/predefined/fixtures/data_only.rb b/spec/ruby/language/predefined/fixtures/data_only.rb
new file mode 100644
index 0000000000..004ac62737
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/data_only.rb
@@ -0,0 +1,2 @@
+__END__
+data only
diff --git a/spec/ruby/language/predefined/fixtures/empty_data.rb b/spec/ruby/language/predefined/fixtures/empty_data.rb
new file mode 100644
index 0000000000..c6d9bc6f1f
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/empty_data.rb
@@ -0,0 +1,3 @@
+p DATA.pos
+p DATA.read
+__END__
diff --git a/spec/ruby/language/predefined/fixtures/print_data.rb b/spec/ruby/language/predefined/fixtures/print_data.rb
new file mode 100644
index 0000000000..4a5692e6a7
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/print_data.rb
@@ -0,0 +1,3 @@
+at_exit {
+ puts DATA.read
+}
diff --git a/spec/ruby/language/predefined/fixtures/toplevel_binding_dynamic.rb b/spec/ruby/language/predefined/fixtures/toplevel_binding_dynamic.rb
new file mode 100644
index 0000000000..f7809109fa
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/toplevel_binding_dynamic.rb
@@ -0,0 +1,4 @@
+p TOPLEVEL_BINDING.local_variables.sort
+TOPLEVEL_BINDING.local_variable_set(:dynamic_set_main, 2)
+p TOPLEVEL_BINDING.local_variables.sort
+main_script = 3
diff --git a/spec/ruby/language/predefined/fixtures/toplevel_binding_dynamic_required.rb b/spec/ruby/language/predefined/fixtures/toplevel_binding_dynamic_required.rb
new file mode 100644
index 0000000000..7ccf329680
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/toplevel_binding_dynamic_required.rb
@@ -0,0 +1,2 @@
+TOPLEVEL_BINDING.local_variable_set(:dynamic_set_required, 1)
+p TOPLEVEL_BINDING.local_variables
diff --git a/spec/ruby/language/predefined/fixtures/toplevel_binding_id.rb b/spec/ruby/language/predefined/fixtures/toplevel_binding_id.rb
new file mode 100644
index 0000000000..3626ea1f10
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/toplevel_binding_id.rb
@@ -0,0 +1,4 @@
+a = TOPLEVEL_BINDING.object_id
+require_relative 'toplevel_binding_id_required'
+c = eval('TOPLEVEL_BINDING.object_id')
+p [a, $b, c].uniq.size
diff --git a/spec/ruby/language/predefined/fixtures/toplevel_binding_id_required.rb b/spec/ruby/language/predefined/fixtures/toplevel_binding_id_required.rb
new file mode 100644
index 0000000000..b31b6e32a0
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/toplevel_binding_id_required.rb
@@ -0,0 +1 @@
+$b = TOPLEVEL_BINDING.object_id
diff --git a/spec/ruby/language/predefined/fixtures/toplevel_binding_required_before.rb b/spec/ruby/language/predefined/fixtures/toplevel_binding_required_before.rb
new file mode 100644
index 0000000000..58924a5800
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/toplevel_binding_required_before.rb
@@ -0,0 +1,2 @@
+required = true
+p [:required_before, TOPLEVEL_BINDING.local_variables]
diff --git a/spec/ruby/language/predefined/fixtures/toplevel_binding_values.rb b/spec/ruby/language/predefined/fixtures/toplevel_binding_values.rb
new file mode 100644
index 0000000000..42bd67f347
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/toplevel_binding_values.rb
@@ -0,0 +1,9 @@
+p TOPLEVEL_BINDING.local_variable_get(:a)
+p TOPLEVEL_BINDING.local_variable_get(:b)
+a = 1
+p TOPLEVEL_BINDING.local_variable_get(:a)
+p TOPLEVEL_BINDING.local_variable_get(:b)
+b = 2
+a = 3
+p TOPLEVEL_BINDING.local_variable_get(:a)
+p TOPLEVEL_BINDING.local_variable_get(:b)
diff --git a/spec/ruby/language/predefined/fixtures/toplevel_binding_variables.rb b/spec/ruby/language/predefined/fixtures/toplevel_binding_variables.rb
new file mode 100644
index 0000000000..151f4340ef
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/toplevel_binding_variables.rb
@@ -0,0 +1,4 @@
+main_script = 1
+require_relative 'toplevel_binding_variables_required'
+eval('eval_var = 3')
+p TOPLEVEL_BINDING.local_variables
diff --git a/spec/ruby/language/predefined/fixtures/toplevel_binding_variables_required.rb b/spec/ruby/language/predefined/fixtures/toplevel_binding_variables_required.rb
new file mode 100644
index 0000000000..614547fe16
--- /dev/null
+++ b/spec/ruby/language/predefined/fixtures/toplevel_binding_variables_required.rb
@@ -0,0 +1,2 @@
+required = 2
+p [:required_after, TOPLEVEL_BINDING.local_variables]
diff --git a/spec/ruby/language/predefined/toplevel_binding_spec.rb b/spec/ruby/language/predefined/toplevel_binding_spec.rb
new file mode 100644
index 0000000000..69ac28618c
--- /dev/null
+++ b/spec/ruby/language/predefined/toplevel_binding_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+
+describe "The TOPLEVEL_BINDING constant" do
+ it "only includes local variables defined in the main script, not in required files or eval" do
+ binding_toplevel_variables = ruby_exe(fixture(__FILE__, "toplevel_binding_variables.rb"))
+ binding_toplevel_variables.should == "[:required_after, [:main_script]]\n[:main_script]\n"
+ end
+
+ it "has no local variables in files required before the main script" do
+ required = fixture(__FILE__, 'toplevel_binding_required_before.rb')
+ out = ruby_exe("a=1; p TOPLEVEL_BINDING.local_variables.sort; b=2", options: "-r#{required}")
+ out.should == "[:required_before, []]\n[:a, :b]\n"
+ end
+
+ it "merges local variables of the main script with dynamically-defined Binding variables" do
+ required = fixture(__FILE__, 'toplevel_binding_dynamic_required.rb')
+ out = ruby_exe(fixture(__FILE__, 'toplevel_binding_dynamic.rb'), options: "-r#{required}")
+ out.should == <<EOS
+[:dynamic_set_required]
+[:dynamic_set_required, :main_script]
+[:dynamic_set_main, :dynamic_set_required, :main_script]
+EOS
+ end
+
+ it "gets updated variables values as they are defined and set" do
+ out = ruby_exe(fixture(__FILE__, "toplevel_binding_values.rb"))
+ out.should == "nil\nnil\n1\nnil\n3\n2\n"
+ end
+
+ it "is always the same object for all top levels" do
+ binding_toplevel_id = ruby_exe(fixture(__FILE__, "toplevel_binding_id.rb"))
+ binding_toplevel_id.should == "1\n"
+ end
+end
diff --git a/spec/ruby/language/predefined_spec.rb b/spec/ruby/language/predefined_spec.rb
new file mode 100644
index 0000000000..36392c7b8a
--- /dev/null
+++ b/spec/ruby/language/predefined_spec.rb
@@ -0,0 +1,1267 @@
+require_relative '../spec_helper'
+require 'stringio'
+
+# The following tables are excerpted from Programming Ruby: The Pragmatic Programmer's Guide'
+# Second Edition by Dave Thomas, Chad Fowler, and Andy Hunt, page 319-22.
+#
+# 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
+
+
+describe "Predefined global $~" do
+ it "is set to contain the MatchData object of the last match if successful" do
+ md = /foo/.match 'foo'
+ $~.should be_kind_of(MatchData)
+ $~.should equal md
+
+ /bar/ =~ 'bar'
+ $~.should be_kind_of(MatchData)
+ $~.should_not equal md
+ end
+
+ it "is set to nil if the last match was unsuccessful" do
+ /foo/ =~ 'foo'
+ $~.nil?.should == false
+
+ /foo/ =~ 'bar'
+ $~.nil?.should == true
+ end
+
+ it "is set at the method-scoped level rather than block-scoped" do
+ obj = Object.new
+ def obj.foo; yield; end
+ def obj.foo2(&proc); proc.call; end
+
+ match2 = nil
+ match3 = nil
+ match4 = nil
+
+ match1 = /foo/.match "foo"
+
+ obj.foo { match2 = /bar/.match("bar") }
+
+ match2.should_not == nil
+ $~.should == match2
+
+ eval 'match3 = /baz/.match("baz")'
+
+ match3.should_not == nil
+ $~.should == match3
+
+ obj.foo2 { match4 = /qux/.match("qux") }
+
+ match4.should_not == nil
+ $~.should == match4
+ end
+
+ it "raises an error if assigned an object not nil or instanceof MatchData" do
+ $~ = nil
+ $~.should == nil
+ $~ = /foo/.match("foo")
+ $~.should be_an_instance_of(MatchData)
+
+ lambda { $~ = Object.new }.should raise_error(TypeError)
+ lambda { $~ = 1 }.should raise_error(TypeError)
+ end
+
+ it "changes the value of derived capture globals when assigned" do
+ "foo" =~ /(f)oo/
+ foo_match = $~
+ "bar" =~ /(b)ar/
+ $~ = foo_match
+ $1.should == "f"
+ end
+
+ it "changes the value of the derived preceding match global" do
+ "foo hello" =~ /hello/
+ foo_match = $~
+ "bar" =~ /(bar)/
+ $~ = foo_match
+ $`.should == "foo "
+ end
+
+ it "changes the value of the derived following match global" do
+ "foo hello" =~ /foo/
+ foo_match = $~
+ "bar" =~ /(bar)/
+ $~ = foo_match
+ $'.should == " hello"
+ end
+
+ it "changes the value of the derived full match global" do
+ "foo hello" =~ /foo/
+ foo_match = $~
+ "bar" =~ /(bar)/
+ $~ = foo_match
+ $&.should == "foo"
+ end
+end
+
+describe "Predefined global $&" do
+ it "is equivalent to MatchData#[0] on the last match $~" do
+ /foo/ =~ 'barfoobaz'
+ $&.should == $~[0]
+ $&.should == 'foo'
+ end
+
+ with_feature :encoding do
+ it "sets the encoding to the encoding of the source String" do
+ "abc".force_encoding(Encoding::EUC_JP) =~ /b/
+ $&.encoding.should equal(Encoding::EUC_JP)
+ end
+ end
+end
+
+describe "Predefined global $`" do
+ it "is equivalent to MatchData#pre_match on the last match $~" do
+ /foo/ =~ 'barfoobaz'
+ $`.should == $~.pre_match
+ $`.should == 'bar'
+ end
+
+ with_feature :encoding do
+ it "sets the encoding to the encoding of the source String" do
+ "abc".force_encoding(Encoding::EUC_JP) =~ /b/
+ $`.encoding.should equal(Encoding::EUC_JP)
+ end
+
+ it "sets an empty result to the encoding of the source String" do
+ "abc".force_encoding(Encoding::ISO_8859_1) =~ /a/
+ $`.encoding.should equal(Encoding::ISO_8859_1)
+ end
+ end
+end
+
+describe "Predefined global $'" do
+ it "is equivalent to MatchData#post_match on the last match $~" do
+ /foo/ =~ 'barfoobaz'
+ $'.should == $~.post_match
+ $'.should == 'baz'
+ end
+
+ with_feature :encoding do
+ it "sets the encoding to the encoding of the source String" do
+ "abc".force_encoding(Encoding::EUC_JP) =~ /b/
+ $'.encoding.should equal(Encoding::EUC_JP)
+ end
+
+ it "sets an empty result to the encoding of the source String" do
+ "abc".force_encoding(Encoding::ISO_8859_1) =~ /c/
+ $'.encoding.should equal(Encoding::ISO_8859_1)
+ end
+ end
+end
+
+describe "Predefined global $+" do
+ it "is equivalent to $~.captures.last" do
+ /(f(o)o)/ =~ 'barfoobaz'
+ $+.should == $~.captures.last
+ $+.should == 'o'
+ end
+
+ it "captures the last non nil capture" do
+ /(a)|(b)/ =~ 'a'
+ $+.should == 'a'
+ end
+
+ with_feature :encoding do
+ it "sets the encoding to the encoding of the source String" do
+ "abc".force_encoding(Encoding::EUC_JP) =~ /(b)/
+ $+.encoding.should equal(Encoding::EUC_JP)
+ end
+ end
+end
+
+describe "Predefined globals $1..N" do
+ it "are equivalent to $~[N]" do
+ /(f)(o)(o)/ =~ 'foo'
+ $1.should == $~[1]
+ $2.should == $~[2]
+ $3.should == $~[3]
+ $4.should == $~[4]
+
+ [$1, $2, $3, $4].should == ['f', 'o', 'o', nil]
+ end
+
+ it "are nil unless a match group occurs" do
+ def test(arg)
+ case arg
+ when /-(.)?/
+ $1
+ end
+ end
+ test("-").should == nil
+ end
+
+ with_feature :encoding do
+ it "sets the encoding to the encoding of the source String" do
+ "abc".force_encoding(Encoding::EUC_JP) =~ /(b)/
+ $1.encoding.should equal(Encoding::EUC_JP)
+ end
+ end
+end
+
+describe "Predefined global $stdout" do
+ before :each do
+ @old_stdout = $stdout
+ end
+
+ after :each do
+ $stdout = @old_stdout
+ end
+
+ it "raises TypeError error if assigned to nil" do
+ lambda { $stdout = nil }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError error if assigned to object that doesn't respond to #write" do
+ obj = mock('object')
+ lambda { $stdout = obj }.should raise_error(TypeError)
+
+ obj.stub!(:write)
+ $stdout = obj
+ $stdout.should equal(obj)
+ end
+end
+
+describe "Predefined global $!" do
+ # See http://jira.codehaus.org/browse/JRUBY-5550
+ it "remains nil after a failed core class \"checked\" coercion against a class that defines method_missing" do
+ $!.should == nil
+
+ obj = Class.new do
+ def method_missing(*args)
+ super
+ end
+ end.new
+
+ [obj, 'foo'].join
+
+ $!.should == nil
+ end
+
+ it "should be set to the value of $! before the begin after a successful rescue" do
+ outer = StandardError.new 'outer'
+ inner = StandardError.new 'inner'
+
+ begin
+ raise outer
+ rescue
+ $!.should == outer
+
+ # nested rescue
+ begin
+ $!.should == outer
+ raise inner
+ rescue
+ $!.should == inner
+ ensure
+ $!.should == outer
+ end
+ $!.should == outer
+ end
+ $!.should == nil
+ end
+
+ it "should be set to the value of $! before the begin after a rescue which returns" do
+ def foo
+ outer = StandardError.new 'outer'
+ inner = StandardError.new 'inner'
+
+ begin
+ raise outer
+ rescue
+ $!.should == outer
+
+ # nested rescue
+ begin
+ $!.should == outer
+ raise inner
+ rescue
+ $!.should == inner
+ return
+ ensure
+ $!.should == outer
+ end
+ $!.should == outer
+ end
+ $!.should == nil
+ end
+ foo
+ end
+
+ it "should be set to the value of $! before the begin after a successful rescue within an ensure" do
+ outer = StandardError.new 'outer'
+ inner = StandardError.new 'inner'
+
+ begin
+ begin
+ raise outer
+ ensure
+ $!.should == outer
+
+ # nested rescue
+ begin
+ $!.should == outer
+ raise inner
+ rescue
+ $!.should == inner
+ ensure
+ $!.should == outer
+ end
+ $!.should == outer
+ end
+ flunk "outer should be raised after the ensure"
+ rescue
+ $!.should == outer
+ end
+ $!.should == nil
+ end
+
+ it "should be set to the new exception after a throwing rescue" do
+ outer = StandardError.new 'outer'
+ inner = StandardError.new 'inner'
+
+ begin
+ raise outer
+ rescue
+ $!.should == outer
+
+ begin
+ # nested rescue
+ begin
+ $!.should == outer
+ raise inner
+ rescue # the throwing rescue
+ $!.should == inner
+ raise inner
+ ensure
+ $!.should == inner
+ end
+ rescue # do not make the exception fail the example
+ $!.should == inner
+ end
+ $!.should == outer
+ end
+ $!.should == nil
+ end
+
+ describe "in bodies without ensure" do
+ it "should be cleared when an exception is rescued" do
+ e = StandardError.new 'foo'
+ begin
+ raise e
+ rescue
+ $!.should == e
+ end
+ $!.should == nil
+ end
+
+ it "should be cleared when an exception is rescued even when a non-local return is present" do
+ def foo(e)
+ $!.should == e
+ yield
+ end
+ def bar
+ e = StandardError.new 'foo'
+ begin
+ raise e
+ rescue
+ $!.should == e
+ foo(e) { return }
+ end
+ end
+
+ bar
+ $!.should == nil
+ end
+
+ it "should not be cleared when an exception is not rescued" do
+ e = StandardError.new
+ begin
+ begin
+ begin
+ raise e
+ rescue TypeError
+ flunk
+ end
+ ensure
+ $!.should == e
+ end
+ rescue
+ $!.should == e
+ end
+ $!.should == nil
+ end
+
+ it "should not be cleared when an exception is rescued and rethrown" do
+ e = StandardError.new 'foo'
+ begin
+ begin
+ begin
+ raise e
+ rescue => e
+ $!.should == e
+ raise e
+ end
+ ensure
+ $!.should == e
+ end
+ rescue
+ $!.should == e
+ end
+ $!.should == nil
+ end
+ end
+
+ describe "in ensure-protected bodies" do
+ it "should be cleared when an exception is rescued" do
+ e = StandardError.new 'foo'
+ begin
+ raise e
+ rescue
+ $!.should == e
+ ensure
+ $!.should == nil
+ end
+ $!.should == nil
+ end
+
+ it "should not be cleared when an exception is not rescued" do
+ e = StandardError.new
+ begin
+ begin
+ begin
+ raise e
+ rescue TypeError
+ flunk
+ ensure
+ $!.should == e
+ end
+ ensure
+ $!.should == e
+ end
+ rescue
+ $!.should == e
+ end
+ end
+
+ it "should not be cleared when an exception is rescued and rethrown" do
+ e = StandardError.new
+ begin
+ begin
+ begin
+ raise e
+ rescue => e
+ $!.should == e
+ raise e
+ ensure
+ $!.should == e
+ end
+ ensure
+ $!.should == e
+ end
+ rescue
+ $!.should == e
+ end
+ end
+ 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.
+$. Fixnum 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
+
+describe "Predefined global $/" do
+ before :each do
+ @dollar_slash = $/
+ @dollar_dash_zero = $-0
+ end
+
+ after :each do
+ $/ = @dollar_slash
+ $-0 = @dollar_dash_zero
+ end
+
+ it "can be assigned a String" do
+ str = "abc"
+ $/ = str
+ $/.should equal(str)
+ end
+
+ it "can be assigned nil" do
+ $/ = nil
+ $/.should be_nil
+ end
+
+ it "returns the value assigned" do
+ ($/ = "xyz").should == "xyz"
+ end
+
+
+ it "changes $-0" do
+ $/ = "xyz"
+ $-0.should equal($/)
+ end
+
+ it "does not call #to_str to convert the object to a String" do
+ obj = mock("$/ value")
+ obj.should_not_receive(:to_str)
+
+ lambda { $/ = obj }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if assigned a Fixnum" do
+ lambda { $/ = 1 }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if assigned a boolean" do
+ lambda { $/ = true }.should raise_error(TypeError)
+ end
+end
+
+describe "Predefined global $-0" do
+ before :each do
+ @dollar_slash = $/
+ @dollar_dash_zero = $-0
+ end
+
+ after :each do
+ $/ = @dollar_slash
+ $-0 = @dollar_dash_zero
+ end
+
+ it "can be assigned a String" do
+ str = "abc"
+ $-0 = str
+ $-0.should equal(str)
+ end
+
+ it "can be assigned nil" do
+ $-0 = nil
+ $-0.should be_nil
+ end
+
+ it "returns the value assigned" do
+ ($-0 = "xyz").should == "xyz"
+ end
+
+ it "changes $/" do
+ $-0 = "xyz"
+ $/.should equal($-0)
+ end
+
+ it "does not call #to_str to convert the object to a String" do
+ obj = mock("$-0 value")
+ obj.should_not_receive(:to_str)
+
+ lambda { $-0 = obj }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if assigned a Fixnum" do
+ lambda { $-0 = 1 }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if assigned a boolean" do
+ lambda { $-0 = true }.should raise_error(TypeError)
+ end
+end
+
+describe "Predefined global $," do
+ after :each do
+ $, = nil
+ end
+
+ it "defaults to nil" do
+ $,.should be_nil
+ end
+
+ it "raises TypeError if assigned a non-String" do
+ lambda { $, = Object.new }.should raise_error(TypeError)
+ end
+end
+
+describe "Predefined global $." do
+ it "can be assigned an Integer" do
+ $. = 123
+ $..should == 123
+ end
+
+ it "can be assigned a Float" do
+ $. = 123.5
+ $..should == 123
+ end
+
+ it "should call #to_int to convert the object to an Integer" do
+ obj = mock("good-value")
+ obj.should_receive(:to_int).and_return(321)
+
+ $. = obj
+ $..should == 321
+ end
+
+ it "raises TypeError if object can't be converted to an Integer" do
+ obj = mock("bad-value")
+ obj.should_receive(:to_int).and_return('abc')
+
+ lambda { $. = obj }.should raise_error(TypeError)
+ end
+end
+
+describe "Predefined global $_" do
+ it "is set to the last line read by e.g. StringIO#gets" do
+ stdin = StringIO.new("foo\nbar\n", "r")
+
+ read = stdin.gets
+ read.should == "foo\n"
+ $_.should == read
+
+ read = stdin.gets
+ read.should == "bar\n"
+ $_.should == read
+
+ read = stdin.gets
+ read.should == nil
+ $_.should == read
+ end
+
+ it "is set at the method-scoped level rather than block-scoped" do
+ obj = Object.new
+ def obj.foo; yield; end
+ def obj.foo2; yield; end
+
+ stdin = StringIO.new("foo\nbar\nbaz\nqux\n", "r")
+ match = stdin.gets
+
+ obj.foo { match = stdin.gets }
+
+ match.should == "bar\n"
+ $_.should == match
+
+ eval 'match = stdin.gets'
+
+ match.should == "baz\n"
+ $_.should == match
+
+ obj.foo2 { match = stdin.gets }
+
+ match.should == "qux\n"
+ $_.should == match
+ end
+
+ it "is Thread-local" do
+ $_ = nil
+ running = false
+
+ thr = Thread.new do
+ $_ = "last line"
+ running = true
+ end
+
+ Thread.pass until running
+ $_.should be_nil
+
+ thr.join
+ end
+
+ it "can be assigned any value" do
+ $_ = nil
+ $_.should == nil
+ $_ = "foo"
+ $_.should == "foo"
+ o = Object.new
+ $_ = o
+ $_.should == o
+ $_ = 1
+ $_.should == 1
+ 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]
+$$ Fixnum 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]
+$SAFE Fixnum The current safe level. This variable’s value may never be
+ reduced by assignment. [thread] (Not implemented in Rubinius)
+$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
+describe "Execution variable $:" do
+ it "is initialized to an array of strings" do
+ $:.is_a?(Array).should == true
+ ($:.length > 0).should == true
+ end
+
+ it "does not include the current directory" do
+ $:.should_not include(".")
+ end
+
+ it "is the same object as $LOAD_PATH and $-I" do
+ $:.__id__.should == $LOAD_PATH.__id__
+ $:.__id__.should == $-I.__id__
+ end
+
+ it "can be changed via <<" do
+ $: << "foo"
+ $:.should include("foo")
+ end
+
+ it "is read-only" do
+ lambda {
+ $: = []
+ }.should raise_error(NameError)
+
+ lambda {
+ $LOAD_PATH = []
+ }.should raise_error(NameError)
+
+ lambda {
+ $-I = []
+ }.should raise_error(NameError)
+ end
+end
+
+describe "Global variable $\"" do
+ it "is an alias for $LOADED_FEATURES" do
+ $".should equal $LOADED_FEATURES
+ end
+
+ it "is read-only" do
+ lambda {
+ $" = []
+ }.should raise_error(NameError)
+
+ lambda {
+ $LOADED_FEATURES = []
+ }.should raise_error(NameError)
+ end
+end
+
+describe "Global variable $<" do
+ it "is read-only" do
+ lambda {
+ $< = nil
+ }.should raise_error(NameError)
+ end
+end
+
+describe "Global variable $FILENAME" do
+ it "is read-only" do
+ lambda {
+ $FILENAME = "-"
+ }.should raise_error(NameError)
+ end
+end
+
+describe "Global variable $?" do
+ it "is read-only" do
+ lambda {
+ $? = nil
+ }.should raise_error(NameError)
+ end
+
+ it "is thread-local" do
+ system(ruby_cmd('exit 0'))
+ Thread.new { $?.should be_nil }.join
+ end
+end
+
+describe "Global variable $-a" do
+ it "is read-only" do
+ lambda { $-a = true }.should raise_error(NameError)
+ end
+end
+
+describe "Global variable $-l" do
+ it "is read-only" do
+ lambda { $-l = true }.should raise_error(NameError)
+ end
+end
+
+describe "Global variable $-p" do
+ it "is read-only" do
+ lambda { $-p = true }.should raise_error(NameError)
+ end
+end
+
+describe "Global variable $-d" do
+ before :each do
+ @debug = $DEBUG
+ end
+
+ after :each do
+ $DEBUG = @debug
+ end
+
+ it "is an alias of $DEBUG" do
+ $DEBUG = true
+ $-d.should be_true
+ $-d = false
+ $DEBUG.should be_false
+ end
+end
+
+describe "Global variable $VERBOSE" do
+ it "converts truthy values to true" do
+ [true, 1, 0, [], ""].each do |true_value|
+ $VERBOSE = true_value
+ $VERBOSE.should be_true
+ end
+ end
+
+ it "allows false" do
+ $VERBOSE = false
+ $VERBOSE.should be_false
+ end
+
+ it "allows nil without coercing to false" do
+ $VERBOSE = nil
+ $VERBOSE.should be_nil
+ end
+end
+
+describe :verbose_global_alias, shared: true do
+ before :each do
+ @verbose = $VERBOSE
+ end
+
+ after :each do
+ $VERBOSE = @verbose
+ end
+
+ it "is an alias of $VERBOSE" do
+ $VERBOSE = true
+ eval(@method).should be_true
+ eval("#{@method} = false")
+ $VERBOSE.should be_false
+ end
+end
+
+describe "Global variable $-v" do
+ it_behaves_like :verbose_global_alias, '$-v'
+end
+
+describe "Global variable $-w" do
+ it_behaves_like :verbose_global_alias, '$-w'
+end
+
+describe "Global variable $0" do
+ before :each do
+ @orig_program_name = $0
+ end
+
+ after :each do
+ $0 = @orig_program_name
+ end
+
+ it "is the path given as the main script and the same as __FILE__" do
+ script = "fixtures/dollar_zero.rb"
+ Dir.chdir(File.dirname(__FILE__)) do
+ ruby_exe(script).should == "#{script}\n#{script}\nOK"
+ end
+ end
+
+ it "returns the program name" do
+ $0 = "rbx"
+ $0.should == "rbx"
+ end
+
+ platform_is :linux, :darwin do
+ it "actually sets the program name" do
+ title = "rubyspec-dollar0-test"
+ $0 = title
+ `ps -ocommand= -p#{$$}`.should include(title)
+ end
+ end
+
+ it "returns the given value when set" do
+ ($0 = "rbx").should == "rbx"
+ end
+
+ it "raises a TypeError when not given an object that can be coerced to a String" do
+ lambda { $0 = nil }.should raise_error(TypeError)
+ 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
+
+describe "The predefined standard objects" do
+ it "includes ARGF" do
+ Object.const_defined?(:ARGF).should == true
+ end
+
+ it "includes ARGV" do
+ Object.const_defined?(:ARGV).should == true
+ end
+
+ it "includes a hash-like object ENV" do
+ Object.const_defined?(:ENV).should == true
+ ENV.respond_to?(:[]).should == true
+ end
+end
+
+describe "The predefined standard object nil" do
+ it "is an instance of NilClass" do
+ nil.should be_kind_of(NilClass)
+ end
+
+ it "raises a SyntaxError if assigned to" do
+ lambda { eval("nil = true") }.should raise_error(SyntaxError)
+ end
+end
+
+describe "The predefined standard object true" do
+ it "is an instance of TrueClass" do
+ true.should be_kind_of(TrueClass)
+ end
+
+ it "raises a SyntaxError if assigned to" do
+ lambda { eval("true = false") }.should raise_error(SyntaxError)
+ end
+end
+
+describe "The predefined standard object false" do
+ it "is an instance of FalseClass" do
+ false.should be_kind_of(FalseClass)
+ end
+
+ it "raises a SyntaxError if assigned to" do
+ lambda { eval("false = nil") }.should raise_error(SyntaxError)
+ end
+end
+
+describe "The self pseudo-variable" do
+ it "raises a SyntaxError if assigned to" do
+ lambda { eval("self = 1") }.should raise_error(SyntaxError)
+ 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.
+NIL NilClass Synonym for nil.
+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.
+=end
+
+describe "The predefined global constants" do
+ ruby_version_is ""..."2.4" do
+ it "includes TRUE" do
+ Object.const_defined?(:TRUE).should == true
+ TRUE.should equal(true)
+ end
+
+ it "includes FALSE" do
+ Object.const_defined?(:FALSE).should == true
+ FALSE.should equal(false)
+ end
+
+ it "includes NIL" do
+ Object.const_defined?(:NIL).should == true
+ NIL.should equal(nil)
+ end
+ end
+
+ ruby_version_is "2.4" do
+ it "includes TRUE" do
+ Object.const_defined?(:TRUE).should == true
+ -> {
+ TRUE.should equal(true)
+ }.should complain(/constant ::TRUE is deprecated/)
+ end
+
+ it "includes FALSE" do
+ Object.const_defined?(:FALSE).should == true
+ -> {
+ FALSE.should equal(false)
+ }.should complain(/constant ::FALSE is deprecated/)
+ end
+
+ it "includes NIL" do
+ Object.const_defined?(:NIL).should == true
+ -> {
+ NIL.should equal(nil)
+ }.should complain(/constant ::NIL is deprecated/)
+ end
+ end
+
+ it "includes STDIN" do
+ Object.const_defined?(:STDIN).should == true
+ end
+
+ it "includes STDOUT" do
+ Object.const_defined?(:STDOUT).should == true
+ end
+
+ it "includes STDERR" do
+ Object.const_defined?(:STDERR).should == true
+ end
+
+ it "includes RUBY_VERSION" do
+ Object.const_defined?(:RUBY_VERSION).should == true
+ end
+
+ it "includes RUBY_RELEASE_DATE" do
+ Object.const_defined?(:RUBY_RELEASE_DATE).should == true
+ end
+
+ it "includes RUBY_PLATFORM" do
+ Object.const_defined?(:RUBY_PLATFORM).should == true
+ end
+
+ it "includes TOPLEVEL_BINDING" do
+ Object.const_defined?(:TOPLEVEL_BINDING).should == true
+ end
+
+end
+
+with_feature :encoding do
+ describe "The predefined global constant" do
+ before :each do
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+ end
+
+ describe "STDIN" do
+ it "has the same external encoding as Encoding.default_external" do
+ STDIN.external_encoding.should equal(Encoding.default_external)
+ end
+
+ it "has the same external encoding as Encoding.default_external when that encoding is changed" do
+ Encoding.default_external = Encoding::ISO_8859_16
+ STDIN.external_encoding.should equal(Encoding::ISO_8859_16)
+ end
+
+ it "has the encodings set by #set_encoding" do
+ code = "STDIN.set_encoding Encoding::IBM775, Encoding::IBM866; " \
+ "p [STDIN.external_encoding.name, STDIN.internal_encoding.name]"
+ ruby_exe(code).chomp.should == %{["IBM775", "IBM866"]}
+ end
+
+ it "retains the encoding set by #set_encoding when Encoding.default_external is changed" do
+ code = "STDIN.set_encoding Encoding::IBM775, Encoding::IBM866; " \
+ "Encoding.default_external = Encoding::ISO_8859_16;" \
+ "p [STDIN.external_encoding.name, STDIN.internal_encoding.name]"
+ ruby_exe(code).chomp.should == %{["IBM775", "IBM866"]}
+ end
+
+ it "has nil for the internal encoding" do
+ STDIN.internal_encoding.should be_nil
+ end
+
+ it "has nil for the internal encoding despite Encoding.default_internal being changed" do
+ Encoding.default_internal = Encoding::IBM437
+ STDIN.internal_encoding.should be_nil
+ end
+ end
+
+ describe "STDOUT" do
+ it "has nil for the external encoding" do
+ STDOUT.external_encoding.should be_nil
+ end
+
+ it "has nil for the external encoding despite Encoding.default_external being changed" do
+ Encoding.default_external = Encoding::ISO_8859_1
+ STDOUT.external_encoding.should be_nil
+ end
+
+ it "has the encodings set by #set_encoding" do
+ code = "STDOUT.set_encoding Encoding::IBM775, Encoding::IBM866; " \
+ "p [STDOUT.external_encoding.name, STDOUT.internal_encoding.name]"
+ ruby_exe(code).chomp.should == %{["IBM775", "IBM866"]}
+ end
+
+ it "has nil for the internal encoding" do
+ STDOUT.internal_encoding.should be_nil
+ end
+
+ it "has nil for the internal encoding despite Encoding.default_internal being changed" do
+ Encoding.default_internal = Encoding::IBM437
+ STDOUT.internal_encoding.should be_nil
+ end
+ end
+
+ describe "STDERR" do
+ it "has nil for the external encoding" do
+ STDERR.external_encoding.should be_nil
+ end
+
+ it "has nil for the external encoding despite Encoding.default_external being changed" do
+ Encoding.default_external = Encoding::ISO_8859_1
+ STDERR.external_encoding.should be_nil
+ end
+
+ it "has the encodings set by #set_encoding" do
+ code = "STDERR.set_encoding Encoding::IBM775, Encoding::IBM866; " \
+ "p [STDERR.external_encoding.name, STDERR.internal_encoding.name]"
+ ruby_exe(code).chomp.should == %{["IBM775", "IBM866"]}
+ end
+
+ it "has nil for the internal encoding" do
+ STDERR.internal_encoding.should be_nil
+ end
+
+ it "has nil for the internal encoding despite Encoding.default_internal being changed" do
+ Encoding.default_internal = Encoding::IBM437
+ STDERR.internal_encoding.should be_nil
+ end
+ end
+
+ describe "ARGV" do
+ it "contains Strings encoded in locale Encoding" do
+ code = fixture __FILE__, "argv_encoding.rb"
+ result = ruby_exe(code, args: "a b")
+ encoding = Encoding.default_external
+ result.chomp.should == %{["#{encoding}", "#{encoding}"]}
+ end
+ end
+ end
+end
diff --git a/spec/ruby/language/private_spec.rb b/spec/ruby/language/private_spec.rb
new file mode 100644
index 0000000000..d9cfb1c3d0
--- /dev/null
+++ b/spec/ruby/language/private_spec.rb
@@ -0,0 +1,67 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/private'
+
+describe "The private keyword" do
+ it "marks following methods as being private" do
+ a = Private::A.new
+ a.methods.should_not include(:bar)
+ lambda { a.bar }.should raise_error(NoMethodError)
+
+ b = Private::B.new
+ b.methods.should_not include(:bar)
+ lambda { b.bar }.should raise_error(NoMethodError)
+ end
+
+ # def expr.meth() methods are always public
+ it "has no effect on def expr.meth() methods" do
+ Private::B.public_defs_method.should == 0
+ end
+
+ it "is overridden when a new class is opened" do
+ c = Private::B::C.new
+ c.methods.should include(:baz)
+ c.baz
+ Private::B.public_class_method1.should == 1
+ lambda { Private::B.private_class_method1 }.should raise_error(NoMethodError)
+ end
+
+ it "is no longer in effect when the class is closed" do
+ b = Private::B.new
+ b.methods.should include(:foo)
+ b.foo
+ end
+
+ it "changes visibility of previously called method" do
+ klass = Class.new do
+ def foo
+ "foo"
+ end
+ end
+ f = klass.new
+ f.foo
+ klass.class_eval do
+ private :foo
+ end
+ lambda { f.foo }.should raise_error(NoMethodError)
+ end
+
+ it "changes visiblity of previously called methods with same send/call site" do
+ g = ::Private::G.new
+ lambda {
+ 2.times do
+ g.foo
+ module ::Private
+ class G
+ private :foo
+ end
+ end
+ end
+ }.should raise_error(NoMethodError)
+ end
+
+ it "changes the visibility of the existing method in the subclass" do
+ ::Private::A.new.foo.should == 'foo'
+ lambda {::Private::H.new.foo}.should raise_error(NoMethodError)
+ ::Private::H.new.send(:foo).should == 'foo'
+ end
+end
diff --git a/spec/ruby/language/proc_spec.rb b/spec/ruby/language/proc_spec.rb
new file mode 100644
index 0000000000..4026f3bcf5
--- /dev/null
+++ b/spec/ruby/language/proc_spec.rb
@@ -0,0 +1,220 @@
+require_relative '../spec_helper'
+
+describe "A Proc" do
+ it "captures locals from the surrounding scope" do
+ var = 1
+ lambda { var }.call.should == 1
+ end
+
+ it "does not capture a local when an argument has the same name" do
+ var = 1
+ lambda { |var| var }.call(2).should == 2
+ var.should == 1
+ end
+
+ describe "taking zero arguments" do
+ before :each do
+ @l = lambda { 1 }
+ end
+
+ it "does not raise an exception if no values are passed" do
+ @l.call.should == 1
+ end
+
+ it "raises an ArgumentErro if a value is passed" do
+ lambda { @l.call(0) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "taking || arguments" do
+ before :each do
+ @l = lambda { || 1 }
+ end
+
+ it "does not raise an exception when passed no values" do
+ @l.call.should == 1
+ end
+
+ it "raises an ArgumentError if a value is passed" do
+ lambda { @l.call(0) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "taking |a| arguments" do
+ before :each do
+ @l = lambda { |a| a }
+ end
+
+ it "assigns the value passed to the argument" do
+ @l.call(2).should == 2
+ end
+
+ it "does not destructure a single Array value" do
+ @l.call([1, 2]).should == [1, 2]
+ end
+
+ it "does not call #to_ary to convert a single passed object to an Array" do
+ obj = mock("block yield to_ary")
+ obj.should_not_receive(:to_ary)
+
+ @l.call(obj).should equal(obj)
+ end
+
+ it "raises an ArgumentError if no value is passed" do
+ lambda { @l.call }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "taking |a, b| arguments" do
+ before :each do
+ @l = lambda { |a, b| [a, b] }
+ end
+
+ it "raises an ArgumentError if passed no values" do
+ lambda { @l.call }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if passed one value" do
+ lambda { @l.call(0) }.should raise_error(ArgumentError)
+ end
+
+ it "assigns the values passed to the arguments" do
+ @l.call(1, 2).should == [1, 2]
+ end
+
+ it "does not call #to_ary to convert a single passed object to an Array" do
+ obj = mock("proc call to_ary")
+ obj.should_not_receive(:to_ary)
+
+ lambda { @l.call(obj) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "taking |a, *b| arguments" do
+ before :each do
+ @l = lambda { |a, *b| [a, b] }
+ end
+
+ it "raises an ArgumentError if passed no values" do
+ lambda { @l.call }.should raise_error(ArgumentError)
+ end
+
+ it "does not destructure a single Array value yielded" do
+ @l.call([1, 2, 3]).should == [[1, 2, 3], []]
+ end
+
+ it "assigns all passed values after the first to the rest argument" do
+ @l.call(1, 2, 3).should == [1, [2, 3]]
+ end
+
+ it "does not call #to_ary to convert a single passed object to an Array" do
+ obj = mock("block yield to_ary")
+ obj.should_not_receive(:to_ary)
+
+ @l.call(obj).should == [obj, []]
+ end
+ end
+
+ describe "taking |*| arguments" do
+ before :each do
+ @l = lambda { |*| 1 }
+ end
+
+ it "does not raise an exception when passed no values" do
+ @l.call.should == 1
+ end
+
+ it "does not raise an exception when passed multiple values" do
+ @l.call(2, 3, 4).should == 1
+ end
+
+ it "does not call #to_ary to convert a single passed object to an Array" do
+ obj = mock("block yield to_ary")
+ obj.should_not_receive(:to_ary)
+
+ @l.call(obj).should == 1
+ end
+ end
+
+ describe "taking |*a| arguments" do
+ before :each do
+ @l = lambda { |*a| a }
+ end
+
+ it "assigns [] to the argument when passed no values" do
+ @l.call.should == []
+ end
+
+ it "assigns the argument an Array wrapping one passed value" do
+ @l.call(1).should == [1]
+ end
+
+ it "assigns the argument an Array wrapping all values passed" do
+ @l.call(1, 2, 3).should == [1, 2, 3]
+ end
+
+ it "does not call #to_ary to convert a single passed object to an Array" do
+ obj = mock("block yield to_ary")
+ obj.should_not_receive(:to_ary)
+
+ @l.call(obj).should == [obj]
+ end
+ end
+
+ describe "taking |a, | arguments" do
+ before :each do
+ @l = lambda { |a, | a }
+ end
+
+ it "raises an ArgumentError when passed no values" do
+ lambda { @l.call }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed more than one value" do
+ lambda { @l.call(1, 2) }.should raise_error(ArgumentError)
+ end
+
+ it "assigns the argument the value passed" do
+ @l.call(1).should == 1
+ end
+
+ it "does not destructure when passed a single Array" do
+ @l.call([1,2]).should == [1, 2]
+ end
+
+ it "does not call #to_ary to convert a single passed object to an Array" do
+ obj = mock("block yield to_ary")
+ obj.should_not_receive(:to_ary)
+
+ @l.call(obj).should == obj
+ end
+ end
+
+ describe "taking |(a, b)| arguments" do
+ before :each do
+ @l = lambda { |(a, b)| [a, b] }
+ end
+
+ it "raises an ArgumentError when passed no values" do
+ lambda { @l.call }.should raise_error(ArgumentError)
+ end
+
+ it "destructures a single Array value yielded" do
+ @l.call([1, 2]).should == [1, 2]
+ end
+
+ it "calls #to_ary to convert a single passed object to an Array" do
+ obj = mock("block yield to_ary")
+ obj.should_receive(:to_ary).and_return([1, 2])
+
+ @l.call(obj).should == [1, 2]
+ end
+
+ it "raises a TypeError if #to_ary does not return an Array" do
+ obj = mock("block yield to_ary invalid")
+ obj.should_receive(:to_ary).and_return(1)
+
+ lambda { @l.call(obj) }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/language/redo_spec.rb b/spec/ruby/language/redo_spec.rb
new file mode 100644
index 0000000000..fe1bcef0a1
--- /dev/null
+++ b/spec/ruby/language/redo_spec.rb
@@ -0,0 +1,66 @@
+require_relative '../spec_helper'
+
+describe "The redo statement" do
+ it "restarts block execution if used within block" do
+ a = []
+ lambda {
+ a << 1
+ redo if a.size < 2
+ a << 2
+ }.call
+ a.should == [1, 1, 2]
+ end
+
+ it "re-executes the closest loop" do
+ exist = [2,3]
+ processed = []
+ order = []
+ [1,2,3,4].each do |x|
+ order << x
+ begin
+ processed << x
+ if exist.include?(x)
+ raise StandardError, "included"
+ end
+ rescue StandardError
+ exist.delete(x)
+ redo
+ end
+ end
+ processed.should == [1,2,2,3,3,4]
+ exist.should == []
+ order.should == [1,2,2,3,3,4]
+ end
+
+ it "re-executes the last step in enumeration" do
+ list = []
+ [1,2,3].each do |x|
+ list << x
+ break if list.size == 6
+ redo if x == 3
+ end
+ list.should == [1,2,3,3,3,3]
+ end
+
+ it "triggers ensure block when re-executing a block" do
+ list = []
+ [1,2,3].each do |x|
+ list << x
+ begin
+ list << 10*x
+ redo if list.count(1) == 1
+ ensure
+ list << 100*x
+ end
+ end
+ list.should == [1,10,100,1,10,100,2,20,200,3,30,300]
+ end
+
+ describe "in a method" do
+ it "is invalid and raises a SyntaxError" do
+ lambda {
+ eval("def m; redo; end")
+ }.should raise_error(SyntaxError)
+ end
+ end
+end
diff --git a/spec/ruby/language/regexp/anchors_spec.rb b/spec/ruby/language/regexp/anchors_spec.rb
new file mode 100644
index 0000000000..0129e255da
--- /dev/null
+++ b/spec/ruby/language/regexp/anchors_spec.rb
@@ -0,0 +1,179 @@
+require_relative '../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Regexps with anchors" do
+ it "supports ^ (line start anchor)" do
+ # Basic matching
+ /^foo/.match("foo").to_a.should == ["foo"]
+ /^bar/.match("foo\nbar").to_a.should == ["bar"]
+ # Basic non-matching
+ /^foo/.match(" foo").should be_nil
+ /foo^/.match("foo\n\n\n").should be_nil
+
+ # A bit advanced
+ /^^^foo/.match("foo").to_a.should == ["foo"]
+ (/^[^f]/ =~ "foo\n\n").should == "foo\n".size and $~.to_a.should == ["\n"]
+ (/($^)($^)/ =~ "foo\n\n").should == "foo\n".size and $~.to_a.should == ["", "", ""]
+
+ # Different start of line chars
+ /^bar/.match("foo\rbar").should be_nil
+ /^bar/.match("foo\0bar").should be_nil
+
+ # Trivial
+ /^/.match("foo").to_a.should == [""]
+
+ # Grouping
+ /(^foo)/.match("foo").to_a.should == ["foo", "foo"]
+ /(^)/.match("foo").to_a.should == ["", ""]
+ /(foo\n^)(^bar)/.match("foo\nbar").to_a.should == ["foo\nbar", "foo\n", "bar"]
+ end
+
+ it "does not match ^ after trailing \\n" do
+ /^(?!\A)/.match("foo\n").should be_nil # There is no (empty) line after a trailing \n
+ end
+
+ it "supports $ (line end anchor)" do
+ # Basic matching
+ /foo$/.match("foo").to_a.should == ["foo"]
+ /foo$/.match("foo\nbar").to_a.should == ["foo"]
+ # Basic non-matching
+ /foo$/.match("foo ").should be_nil
+ /$foo/.match("\n\n\nfoo").should be_nil
+
+ # A bit advanced
+ /foo$$$/.match("foo").to_a.should == ["foo"]
+ (/[^o]$/ =~ "foo\n\n").should == ("foo\n".size - 1) and $~.to_a.should == ["\n"]
+
+ # Different end of line chars
+ /foo$/.match("foo\r\nbar").should be_nil
+ /foo$/.match("foo\0bar").should be_nil
+
+ # Trivial
+ (/$/ =~ "foo").should == "foo".size and $~.to_a.should == [""]
+
+ # Grouping
+ /(foo$)/.match("foo").to_a.should == ["foo", "foo"]
+ (/($)/ =~ "foo").should == "foo".size and $~.to_a.should == ["", ""]
+ /(foo$)($\nbar)/.match("foo\nbar").to_a.should == ["foo\nbar", "foo", "\nbar"]
+ end
+
+ it "supports \\A (string start anchor)" do
+ # Basic matching
+ /\Afoo/.match("foo").to_a.should == ["foo"]
+ # Basic non-matching
+ /\Abar/.match("foo\nbar").should be_nil
+ /\Afoo/.match(" foo").should be_nil
+
+ # A bit advanced
+ /\A\A\Afoo/.match("foo").to_a.should == ["foo"]
+ /(\A\Z)(\A\Z)/.match("").to_a.should == ["", "", ""]
+
+ # Different start of line chars
+ /\Abar/.match("foo\0bar").should be_nil
+
+ # Grouping
+ /(\Afoo)/.match("foo").to_a.should == ["foo", "foo"]
+ /(\A)/.match("foo").to_a.should == ["", ""]
+ end
+
+ it "supports \\Z (string end anchor, including before trailing \\n)" do
+ # Basic matching
+ /foo\Z/.match("foo").to_a.should == ["foo"]
+ /foo\Z/.match("foo\n").to_a.should == ["foo"]
+ # Basic non-matching
+ /foo\Z/.match("foo\nbar").should be_nil
+ /foo\Z/.match("foo ").should be_nil
+
+ # A bit advanced
+ /foo\Z\Z\Z/.match("foo\n").to_a.should == ["foo"]
+ (/($\Z)($\Z)/ =~ "foo\n").should == "foo".size and $~.to_a.should == ["", "", ""]
+ (/(\z\Z)(\z\Z)/ =~ "foo\n").should == "foo\n".size and $~.to_a.should == ["", "", ""]
+
+ # Different end of line chars
+ /foo\Z/.match("foo\0bar").should be_nil
+ /foo\Z/.match("foo\r\n").should be_nil
+
+ # Grouping
+ /(foo\Z)/.match("foo").to_a.should == ["foo", "foo"]
+ (/(\Z)/ =~ "foo").should == "foo".size and $~.to_a.should == ["", ""]
+ end
+
+ it "supports \\z (string end anchor)" do
+ # Basic matching
+ /foo\z/.match("foo").to_a.should == ["foo"]
+ # Basic non-matching
+ /foo\z/.match("foo\nbar").should be_nil
+ /foo\z/.match("foo\n").should be_nil
+ /foo\z/.match("foo ").should be_nil
+
+ # A bit advanced
+ /foo\z\z\z/.match("foo").to_a.should == ["foo"]
+ (/($\z)($\z)/ =~ "foo").should == "foo".size and $~.to_a.should == ["", "", ""]
+
+ # Different end of line chars
+ /foo\z/.match("foo\0bar").should be_nil
+ /foo\z/.match("foo\r\nbar").should be_nil
+
+ # Grouping
+ /(foo\z)/.match("foo").to_a.should == ["foo", "foo"]
+ (/(\z)/ =~ "foo").should == "foo".size and $~.to_a.should == ["", ""]
+ end
+
+ it "supports \\b (word boundary)" do
+ # Basic matching
+ /foo\b/.match("foo").to_a.should == ["foo"]
+ /foo\b/.match("foo\n").to_a.should == ["foo"]
+ LanguageSpecs.white_spaces.scan(/./).each do |c|
+ /foo\b/.match("foo" + c).to_a.should == ["foo"]
+ end
+ LanguageSpecs.non_alphanum_non_space.scan(/./).each do |c|
+ /foo\b/.match("foo" + c).to_a.should == ["foo"]
+ end
+ /foo\b/.match("foo\0").to_a.should == ["foo"]
+ # Basic non-matching
+ /foo\b/.match("foobar").should be_nil
+ /foo\b/.match("foo123").should be_nil
+ /foo\b/.match("foo_").should be_nil
+ end
+
+ it "supports \\B (non-word-boundary)" do
+ # Basic matching
+ /foo\B/.match("foobar").to_a.should == ["foo"]
+ /foo\B/.match("foo123").to_a.should == ["foo"]
+ /foo\B/.match("foo_").to_a.should == ["foo"]
+ # Basic non-matching
+ /foo\B/.match("foo").should be_nil
+ /foo\B/.match("foo\n").should be_nil
+ LanguageSpecs.white_spaces.scan(/./).each do |c|
+ /foo\B/.match("foo" + c).should be_nil
+ end
+ LanguageSpecs.non_alphanum_non_space.scan(/./).each do |c|
+ /foo\B/.match("foo" + c).should be_nil
+ end
+ /foo\B/.match("foo\0").should be_nil
+ end
+
+ it "supports (?= ) (positive lookahead)" do
+ /foo.(?=bar)/.match("foo1 foo2bar").to_a.should == ["foo2"]
+ end
+
+ it "supports (?! ) (negative lookahead)" do
+ /foo.(?!bar)/.match("foo1bar foo2").to_a.should == ["foo2"]
+ end
+
+ it "supports (?!<) (negative lookbehind)" do
+ /(?<!foo)bar./.match("foobar1 bar2").to_a.should == ["bar2"]
+ end
+
+ it "supports (?<=) (positive lookbehind)" do
+ /(?<=foo)bar./.match("bar1 foobar2").to_a.should == ["bar2"]
+ end
+
+ it "supports (?<=\\b) (positive lookbehind with word boundary)" do
+ /(?<=\bfoo)bar./.match("1foobar1 foobar2").to_a.should == ["bar2"]
+ end
+
+ it "supports (?!<\\b) (negative lookbehind with word boundary)" do
+ /(?<!\bfoo)bar./.match("foobar1 1foobar2").to_a.should == ["bar2"]
+ end
+end
diff --git a/spec/ruby/language/regexp/back-references_spec.rb b/spec/ruby/language/regexp/back-references_spec.rb
new file mode 100644
index 0000000000..81015ac21e
--- /dev/null
+++ b/spec/ruby/language/regexp/back-references_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Regexps with back-references" do
+ it "saves match data in the $~ pseudo-global variable" do
+ "hello" =~ /l+/
+ $~.to_a.should == ["ll"]
+ end
+
+ it "saves captures in numbered $[1-N] variables" do
+ "1234567890" =~ /(1)(2)(3)(4)(5)(6)(7)(8)(9)(0)/
+ $~.to_a.should == ["1234567890", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
+ $1.should == "1"
+ $2.should == "2"
+ $3.should == "3"
+ $4.should == "4"
+ $5.should == "5"
+ $6.should == "6"
+ $7.should == "7"
+ $8.should == "8"
+ $9.should == "9"
+ $10.should == "0"
+ end
+
+ it "will not clobber capture variables across threads" do
+ cap1, cap2, cap3 = nil
+ "foo" =~ /(o+)/
+ cap1 = [$~.to_a, $1]
+ Thread.new do
+ cap2 = [$~.to_a, $1]
+ "bar" =~ /(a)/
+ cap3 = [$~.to_a, $1]
+ end.join
+ cap4 = [$~.to_a, $1]
+ cap1.should == [["oo", "oo"], "oo"]
+ cap2.should == [[], nil]
+ cap3.should == [["a", "a"], "a"]
+ cap4.should == [["oo", "oo"], "oo"]
+ end
+
+ it "supports \<n> (backreference to previous group match)" do
+ /(foo.)\1/.match("foo1foo1").to_a.should == ["foo1foo1", "foo1"]
+ /(foo.)\1/.match("foo1foo2").should be_nil
+ end
+
+ it "resets nested \<n> backreference before match of outer subexpression" do
+ /(a\1?){2}/.match("aaaa").to_a.should == ["aa", "a"]
+ end
+
+ it "can match an optional quote, followed by content, followed by a matching quote, as the whole string" do
+ /^("|)(.*)\1$/.match('x').to_a.should == ["x", "", "x"]
+ end
+end
diff --git a/spec/ruby/language/regexp/character_classes_spec.rb b/spec/ruby/language/regexp/character_classes_spec.rb
new file mode 100644
index 0000000000..a466f745ae
--- /dev/null
+++ b/spec/ruby/language/regexp/character_classes_spec.rb
@@ -0,0 +1,633 @@
+# coding: utf-8
+require_relative '../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Regexp with character classes" do
+ it "supports \\w (word character)" do
+ /\w/.match("a").to_a.should == ["a"]
+ /\w/.match("1").to_a.should == ["1"]
+ /\w/.match("_").to_a.should == ["_"]
+
+ # Non-matches
+ /\w/.match(LanguageSpecs.white_spaces).should be_nil
+ /\w/.match(LanguageSpecs.non_alphanum_non_space).should be_nil
+ /\w/.match("\0").should be_nil
+ end
+
+ it "supports \\W (non-word character)" do
+ /\W+/.match(LanguageSpecs.white_spaces).to_a.should == [LanguageSpecs.white_spaces]
+ /\W+/.match(LanguageSpecs.non_alphanum_non_space).to_a.should == [LanguageSpecs.non_alphanum_non_space]
+ /\W/.match("\0").to_a.should == ["\0"]
+
+ # Non-matches
+ /\W/.match("a").should be_nil
+ /\W/.match("1").should be_nil
+ /\W/.match("_").should be_nil
+ end
+
+ it "supports \\s (space character)" do
+ /\s+/.match(LanguageSpecs.white_spaces).to_a.should == [LanguageSpecs.white_spaces]
+
+ # Non-matches
+ /\s/.match("a").should be_nil
+ /\s/.match("1").should be_nil
+ /\s/.match(LanguageSpecs.non_alphanum_non_space).should be_nil
+ /\s/.match("\0").should be_nil
+ end
+
+ it "supports \\S (non-space character)" do
+ /\S/.match("a").to_a.should == ["a"]
+ /\S/.match("1").to_a.should == ["1"]
+ /\S+/.match(LanguageSpecs.non_alphanum_non_space).to_a.should == [LanguageSpecs.non_alphanum_non_space]
+ /\S/.match("\0").to_a.should == ["\0"]
+
+ # Non-matches
+ /\S/.match(LanguageSpecs.white_spaces).should be_nil
+ end
+
+ it "supports \\d (numeric digit)" do
+ /\d/.match("1").to_a.should == ["1"]
+
+ # Non-matches
+ /\d/.match("a").should be_nil
+ /\d/.match(LanguageSpecs.white_spaces).should be_nil
+ /\d/.match(LanguageSpecs.non_alphanum_non_space).should be_nil
+ /\d/.match("\0").should be_nil
+ end
+
+ it "supports \\D (non-digit)" do
+ /\D/.match("a").to_a.should == ["a"]
+ /\D+/.match(LanguageSpecs.white_spaces).to_a.should == [LanguageSpecs.white_spaces]
+ /\D+/.match(LanguageSpecs.non_alphanum_non_space).to_a.should == [LanguageSpecs.non_alphanum_non_space]
+ /\D/.match("\0").to_a.should == ["\0"]
+
+ # Non-matches
+ /\D/.match("1").should be_nil
+ end
+
+ it "supports [] (character class)" do
+ /[a-z]+/.match("fooBAR").to_a.should == ["foo"]
+ /[\b]/.match("\b").to_a.should == ["\b"] # \b inside character class is backspace
+ end
+
+ it "supports [[:alpha:][:digit:][:etc:]] (predefined character classes)" do
+ /[[:alnum:]]+/.match("a1").to_a.should == ["a1"]
+ /[[:alpha:]]+/.match("Aa1").to_a.should == ["Aa"]
+ /[[:blank:]]+/.match(LanguageSpecs.white_spaces).to_a.should == [LanguageSpecs.blanks]
+ # /[[:cntrl:]]/.match("").to_a.should == [""] # TODO: what should this match?
+ /[[:digit:]]/.match("1").to_a.should == ["1"]
+ # /[[:graph:]]/.match("").to_a.should == [""] # TODO: what should this match?
+ /[[:lower:]]+/.match("Aa1").to_a.should == ["a"]
+ /[[:print:]]+/.match(LanguageSpecs.white_spaces).to_a.should == [" "] # include all of multibyte encoded characters
+ /[[:punct:]]+/.match(LanguageSpecs.punctuations).to_a.should == [LanguageSpecs.punctuations]
+ /[[:space:]]+/.match(LanguageSpecs.white_spaces).to_a.should == [LanguageSpecs.white_spaces]
+ /[[:upper:]]+/.match("123ABCabc").to_a.should == ["ABC"]
+ /[[:xdigit:]]+/.match("xyz0123456789ABCDEFabcdefXYZ").to_a.should == ["0123456789ABCDEFabcdef"]
+
+ # Parsing
+ /[[:lower:][:digit:]A-C]+/.match("a1ABCDEF").to_a.should == ["a1ABC"] # can be composed with other constructs in the character class
+ /[^[:lower:]A-C]+/.match("abcABCDEF123def").to_a.should == ["DEF123"] # negated character class
+ /[:alnum:]+/.match("a:l:n:u:m").to_a.should == ["a:l:n:u:m"] # should behave like regular character class composed of the individual letters
+ /[\[:alnum:]+/.match("[:a:l:n:u:m").to_a.should == ["[:a:l:n:u:m"] # should behave like regular character class composed of the individual letters
+ lambda { eval('/[[:alpha:]-[:digit:]]/') }.should raise_error(SyntaxError) # can't use character class as a start value of range
+ end
+
+ it "matches ASCII characters with [[:ascii:]]" do
+ "\x00".match(/[[:ascii:]]/).to_a.should == ["\x00"]
+ "\x7F".match(/[[:ascii:]]/).to_a.should == ["\x7F"]
+ end
+
+ not_supported_on :opal do
+ it "doesn't match non-ASCII characters with [[:ascii:]]" do
+ /[[:ascii:]]/.match("\u{80}").should be_nil
+ /[[:ascii:]]/.match("\u{9898}").should be_nil
+ end
+ end
+
+ it "matches Unicode letter characters with [[:alnum:]]" do
+ "à".match(/[[:alnum:]]/).to_a.should == ["à"]
+ end
+
+ it "matches Unicode digits with [[:alnum:]]" do
+ "\u{0660}".match(/[[:alnum:]]/).to_a.should == ["\u{0660}"]
+ end
+
+ it "doesn't matches Unicode marks with [[:alnum:]]" do
+ "\u{36F}".match(/[[:alnum:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode control characters with [[:alnum:]]" do
+ "\u{16}".match(/[[:alnum:]]/).to_a.should == []
+ end
+
+ it "doesn't match Unicode punctuation characters with [[:alnum:]]" do
+ "\u{3F}".match(/[[:alnum:]]/).to_a.should == []
+ end
+
+ it "matches Unicode letter characters with [[:alpha:]]" do
+ "à".match(/[[:alpha:]]/).to_a.should == ["à"]
+ end
+
+ it "doesn't match Unicode digits with [[:alpha:]]" do
+ "\u{0660}".match(/[[:alpha:]]/).to_a.should == []
+ end
+
+ it "doesn't matches Unicode marks with [[:alpha:]]" do
+ "\u{36F}".match(/[[:alpha:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode control characters with [[:alpha:]]" do
+ "\u{16}".match(/[[:alpha:]]/).to_a.should == []
+ end
+
+ it "doesn't match Unicode punctuation characters with [[:alpha:]]" do
+ "\u{3F}".match(/[[:alpha:]]/).to_a.should == []
+ end
+
+ it "matches Unicode space characters with [[:blank:]]" do
+ "\u{1680}".match(/[[:blank:]]/).to_a.should == ["\u{1680}"]
+ end
+
+ it "doesn't match Unicode control characters with [[:blank:]]" do
+ "\u{16}".match(/[[:blank:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode punctuation characters with [[:blank:]]" do
+ "\u{3F}".match(/[[:blank:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode letter characters with [[:blank:]]" do
+ "à".match(/[[:blank:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode digits with [[:blank:]]" do
+ "\u{0660}".match(/[[:blank:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode marks with [[:blank:]]" do
+ "\u{36F}".match(/[[:blank:]]/).should be_nil
+ end
+
+ it "doesn't Unicode letter characters with [[:cntrl:]]" do
+ "à".match(/[[:cntrl:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode digits with [[:cntrl:]]" do
+ "\u{0660}".match(/[[:cntrl:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode marks with [[:cntrl:]]" do
+ "\u{36F}".match(/[[:cntrl:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode punctuation characters with [[:cntrl:]]" do
+ "\u{3F}".match(/[[:cntrl:]]/).should be_nil
+ end
+
+ it "matches Unicode control characters with [[:cntrl:]]" do
+ "\u{16}".match(/[[:cntrl:]]/).to_a.should == ["\u{16}"]
+ end
+
+ it "doesn't match Unicode format characters with [[:cntrl:]]" do
+ "\u{2060}".match(/[[:cntrl:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode private-use characters with [[:cntrl:]]" do
+ "\u{E001}".match(/[[:cntrl:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode letter characters with [[:digit:]]" do
+ "à".match(/[[:digit:]]/).should be_nil
+ end
+
+ it "matches Unicode digits with [[:digit:]]" do
+ "\u{0660}".match(/[[:digit:]]/).to_a.should == ["\u{0660}"]
+ "\u{FF12}".match(/[[:digit:]]/).to_a.should == ["\u{FF12}"]
+ end
+
+ it "doesn't match Unicode marks with [[:digit:]]" do
+ "\u{36F}".match(/[[:digit:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode punctuation characters with [[:digit:]]" do
+ "\u{3F}".match(/[[:digit:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode control characters with [[:digit:]]" do
+ "\u{16}".match(/[[:digit:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode format characters with [[:digit:]]" do
+ "\u{2060}".match(/[[:digit:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode private-use characters with [[:digit:]]" do
+ "\u{E001}".match(/[[:digit:]]/).should be_nil
+ end
+
+ it "matches Unicode letter characters with [[:graph:]]" do
+ "à".match(/[[:graph:]]/).to_a.should == ["à"]
+ end
+
+ it "matches Unicode digits with [[:graph:]]" do
+ "\u{0660}".match(/[[:graph:]]/).to_a.should == ["\u{0660}"]
+ "\u{FF12}".match(/[[:graph:]]/).to_a.should == ["\u{FF12}"]
+ end
+
+ it "matches Unicode marks with [[:graph:]]" do
+ "\u{36F}".match(/[[:graph:]]/).to_a.should ==["\u{36F}"]
+ end
+
+ it "matches Unicode punctuation characters with [[:graph:]]" do
+ "\u{3F}".match(/[[:graph:]]/).to_a.should == ["\u{3F}"]
+ end
+
+ it "doesn't match Unicode control characters with [[:graph:]]" do
+ "\u{16}".match(/[[:graph:]]/).should be_nil
+ end
+
+ it "match Unicode format characters with [[:graph:]]" do
+ "\u{2060}".match(/[[:graph:]]/).to_a.should == ["\u2060"]
+ end
+
+ it "match Unicode private-use characters with [[:graph:]]" do
+ "\u{E001}".match(/[[:graph:]]/).to_a.should == ["\u{E001}"]
+ end
+
+ it "matches Unicode lowercase letter characters with [[:lower:]]" do
+ "\u{FF41}".match(/[[:lower:]]/).to_a.should == ["\u{FF41}"]
+ "\u{1D484}".match(/[[:lower:]]/).to_a.should == ["\u{1D484}"]
+ "\u{E8}".match(/[[:lower:]]/).to_a.should == ["\u{E8}"]
+ end
+
+ it "doesn't match Unicode uppercase letter characters with [[:lower:]]" do
+ "\u{100}".match(/[[:lower:]]/).should be_nil
+ "\u{130}".match(/[[:lower:]]/).should be_nil
+ "\u{405}".match(/[[:lower:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode title-case characters with [[:lower:]]" do
+ "\u{1F88}".match(/[[:lower:]]/).should be_nil
+ "\u{1FAD}".match(/[[:lower:]]/).should be_nil
+ "\u{01C5}".match(/[[:lower:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode digits with [[:lower:]]" do
+ "\u{0660}".match(/[[:lower:]]/).should be_nil
+ "\u{FF12}".match(/[[:lower:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode marks with [[:lower:]]" do
+ "\u{36F}".match(/[[:lower:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode punctuation characters with [[:lower:]]" do
+ "\u{3F}".match(/[[:lower:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode control characters with [[:lower:]]" do
+ "\u{16}".match(/[[:lower:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode format characters with [[:lower:]]" do
+ "\u{2060}".match(/[[:lower:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode private-use characters with [[:lower:]]" do
+ "\u{E001}".match(/[[:lower:]]/).should be_nil
+ end
+
+ it "matches Unicode lowercase letter characters with [[:print:]]" do
+ "\u{FF41}".match(/[[:print:]]/).to_a.should == ["\u{FF41}"]
+ "\u{1D484}".match(/[[:print:]]/).to_a.should == ["\u{1D484}"]
+ "\u{E8}".match(/[[:print:]]/).to_a.should == ["\u{E8}"]
+ end
+
+ it "matches Unicode uppercase letter characters with [[:print:]]" do
+ "\u{100}".match(/[[:print:]]/).to_a.should == ["\u{100}"]
+ "\u{130}".match(/[[:print:]]/).to_a.should == ["\u{130}"]
+ "\u{405}".match(/[[:print:]]/).to_a.should == ["\u{405}"]
+ end
+
+ it "matches Unicode title-case characters with [[:print:]]" do
+ "\u{1F88}".match(/[[:print:]]/).to_a.should == ["\u{1F88}"]
+ "\u{1FAD}".match(/[[:print:]]/).to_a.should == ["\u{1FAD}"]
+ "\u{01C5}".match(/[[:print:]]/).to_a.should == ["\u{01C5}"]
+ end
+
+ it "matches Unicode digits with [[:print:]]" do
+ "\u{0660}".match(/[[:print:]]/).to_a.should == ["\u{0660}"]
+ "\u{FF12}".match(/[[:print:]]/).to_a.should == ["\u{FF12}"]
+ end
+
+ it "matches Unicode marks with [[:print:]]" do
+ "\u{36F}".match(/[[:print:]]/).to_a.should == ["\u{36F}"]
+ end
+
+ it "matches Unicode punctuation characters with [[:print:]]" do
+ "\u{3F}".match(/[[:print:]]/).to_a.should == ["\u{3F}"]
+ end
+
+ it "doesn't match Unicode control characters with [[:print:]]" do
+ "\u{16}".match(/[[:print:]]/).should be_nil
+ end
+
+ it "match Unicode format characters with [[:print:]]" do
+ "\u{2060}".match(/[[:print:]]/).to_a.should == ["\u{2060}"]
+ end
+
+ it "match Unicode private-use characters with [[:print:]]" do
+ "\u{E001}".match(/[[:print:]]/).to_a.should == ["\u{E001}"]
+ end
+
+
+ it "doesn't match Unicode lowercase letter characters with [[:punct:]]" do
+ "\u{FF41}".match(/[[:punct:]]/).should be_nil
+ "\u{1D484}".match(/[[:punct:]]/).should be_nil
+ "\u{E8}".match(/[[:punct:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode uppercase letter characters with [[:punct:]]" do
+ "\u{100}".match(/[[:punct:]]/).should be_nil
+ "\u{130}".match(/[[:punct:]]/).should be_nil
+ "\u{405}".match(/[[:punct:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode title-case characters with [[:punct:]]" do
+ "\u{1F88}".match(/[[:punct:]]/).should be_nil
+ "\u{1FAD}".match(/[[:punct:]]/).should be_nil
+ "\u{01C5}".match(/[[:punct:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode digits with [[:punct:]]" do
+ "\u{0660}".match(/[[:punct:]]/).should be_nil
+ "\u{FF12}".match(/[[:punct:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode marks with [[:punct:]]" do
+ "\u{36F}".match(/[[:punct:]]/).should be_nil
+ end
+
+ it "matches Unicode Pc characters with [[:punct:]]" do
+ "\u{203F}".match(/[[:punct:]]/).to_a.should == ["\u{203F}"]
+ end
+
+ it "matches Unicode Pd characters with [[:punct:]]" do
+ "\u{2E17}".match(/[[:punct:]]/).to_a.should == ["\u{2E17}"]
+ end
+
+ it "matches Unicode Ps characters with [[:punct:]]" do
+ "\u{0F3A}".match(/[[:punct:]]/).to_a.should == ["\u{0F3A}"]
+ end
+
+ it "matches Unicode Pe characters with [[:punct:]]" do
+ "\u{2046}".match(/[[:punct:]]/).to_a.should == ["\u{2046}"]
+ end
+
+ it "matches Unicode Pi characters with [[:punct:]]" do
+ "\u{00AB}".match(/[[:punct:]]/).to_a.should == ["\u{00AB}"]
+ end
+
+ it "matches Unicode Pf characters with [[:punct:]]" do
+ "\u{201D}".match(/[[:punct:]]/).to_a.should == ["\u{201D}"]
+ "\u{00BB}".match(/[[:punct:]]/).to_a.should == ["\u{00BB}"]
+ end
+
+ it "matches Unicode Po characters with [[:punct:]]" do
+ "\u{00BF}".match(/[[:punct:]]/).to_a.should == ["\u{00BF}"]
+ end
+
+ it "doesn't match Unicode format characters with [[:punct:]]" do
+ "\u{2060}".match(/[[:punct:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode private-use characters with [[:punct:]]" do
+ "\u{E001}".match(/[[:punct:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode lowercase letter characters with [[:space:]]" do
+ "\u{FF41}".match(/[[:space:]]/).should be_nil
+ "\u{1D484}".match(/[[:space:]]/).should be_nil
+ "\u{E8}".match(/[[:space:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode uppercase letter characters with [[:space:]]" do
+ "\u{100}".match(/[[:space:]]/).should be_nil
+ "\u{130}".match(/[[:space:]]/).should be_nil
+ "\u{405}".match(/[[:space:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode title-case characters with [[:space:]]" do
+ "\u{1F88}".match(/[[:space:]]/).should be_nil
+ "\u{1FAD}".match(/[[:space:]]/).should be_nil
+ "\u{01C5}".match(/[[:space:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode digits with [[:space:]]" do
+ "\u{0660}".match(/[[:space:]]/).should be_nil
+ "\u{FF12}".match(/[[:space:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode marks with [[:space:]]" do
+ "\u{36F}".match(/[[:space:]]/).should be_nil
+ end
+
+ it "matches Unicode Zs characters with [[:space:]]" do
+ "\u{205F}".match(/[[:space:]]/).to_a.should == ["\u{205F}"]
+ end
+
+ it "matches Unicode Zl characters with [[:space:]]" do
+ "\u{2028}".match(/[[:space:]]/).to_a.should == ["\u{2028}"]
+ end
+
+ it "matches Unicode Zp characters with [[:space:]]" do
+ "\u{2029}".match(/[[:space:]]/).to_a.should == ["\u{2029}"]
+ end
+
+ it "doesn't match Unicode format characters with [[:space:]]" do
+ "\u{2060}".match(/[[:space:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode private-use characters with [[:space:]]" do
+ "\u{E001}".match(/[[:space:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode lowercase characters with [[:upper:]]" do
+ "\u{FF41}".match(/[[:upper:]]/).should be_nil
+ "\u{1D484}".match(/[[:upper:]]/).should be_nil
+ "\u{E8}".match(/[[:upper:]]/).should be_nil
+ end
+
+ it "matches Unicode uppercase characters with [[:upper:]]" do
+ "\u{100}".match(/[[:upper:]]/).to_a.should == ["\u{100}"]
+ "\u{130}".match(/[[:upper:]]/).to_a.should == ["\u{130}"]
+ "\u{405}".match(/[[:upper:]]/).to_a.should == ["\u{405}"]
+ end
+
+ it "doesn't match Unicode title-case characters with [[:upper:]]" do
+ "\u{1F88}".match(/[[:upper:]]/).should be_nil
+ "\u{1FAD}".match(/[[:upper:]]/).should be_nil
+ "\u{01C5}".match(/[[:upper:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode digits with [[:upper:]]" do
+ "\u{0660}".match(/[[:upper:]]/).should be_nil
+ "\u{FF12}".match(/[[:upper:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode marks with [[:upper:]]" do
+ "\u{36F}".match(/[[:upper:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode punctuation characters with [[:upper:]]" do
+ "\u{3F}".match(/[[:upper:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode control characters with [[:upper:]]" do
+ "\u{16}".match(/[[:upper:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode format characters with [[:upper:]]" do
+ "\u{2060}".match(/[[:upper:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode private-use characters with [[:upper:]]" do
+ "\u{E001}".match(/[[:upper:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode letter characters [^a-fA-F] with [[:xdigit:]]" do
+ "à".match(/[[:xdigit:]]/).should be_nil
+ "g".match(/[[:xdigit:]]/).should be_nil
+ "X".match(/[[:xdigit:]]/).should be_nil
+ end
+
+ it "matches Unicode letter characters [a-fA-F] with [[:xdigit:]]" do
+ "a".match(/[[:xdigit:]]/).to_a.should == ["a"]
+ "F".match(/[[:xdigit:]]/).to_a.should == ["F"]
+ end
+
+ it "doesn't match Unicode digits [^0-9] with [[:xdigit:]]" do
+ "\u{0660}".match(/[[:xdigit:]]/).should be_nil
+ "\u{FF12}".match(/[[:xdigit:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode marks with [[:xdigit:]]" do
+ "\u{36F}".match(/[[:xdigit:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode punctuation characters with [[:xdigit:]]" do
+ "\u{3F}".match(/[[:xdigit:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode control characters with [[:xdigit:]]" do
+ "\u{16}".match(/[[:xdigit:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode format characters with [[:xdigit:]]" do
+ "\u{2060}".match(/[[:xdigit:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode private-use characters with [[:xdigit:]]" do
+ "\u{E001}".match(/[[:xdigit:]]/).should be_nil
+ end
+
+ it "matches Unicode lowercase characters with [[:word:]]" do
+ "\u{FF41}".match(/[[:word:]]/).to_a.should == ["\u{FF41}"]
+ "\u{1D484}".match(/[[:word:]]/).to_a.should == ["\u{1D484}"]
+ "\u{E8}".match(/[[:word:]]/).to_a.should == ["\u{E8}"]
+ end
+
+ it "matches Unicode uppercase characters with [[:word:]]" do
+ "\u{100}".match(/[[:word:]]/).to_a.should == ["\u{100}"]
+ "\u{130}".match(/[[:word:]]/).to_a.should == ["\u{130}"]
+ "\u{405}".match(/[[:word:]]/).to_a.should == ["\u{405}"]
+ end
+
+ it "matches Unicode title-case characters with [[:word:]]" do
+ "\u{1F88}".match(/[[:word:]]/).to_a.should == ["\u{1F88}"]
+ "\u{1FAD}".match(/[[:word:]]/).to_a.should == ["\u{1FAD}"]
+ "\u{01C5}".match(/[[:word:]]/).to_a.should == ["\u{01C5}"]
+ end
+
+ it "matches Unicode decimal digits with [[:word:]]" do
+ "\u{FF10}".match(/[[:word:]]/).to_a.should == ["\u{FF10}"]
+ "\u{096C}".match(/[[:word:]]/).to_a.should == ["\u{096C}"]
+ end
+
+ it "matches Unicode marks with [[:word:]]" do
+ "\u{36F}".match(/[[:word:]]/).to_a.should == ["\u{36F}"]
+ end
+
+ it "match Unicode Nl characters with [[:word:]]" do
+ "\u{16EE}".match(/[[:word:]]/).to_a.should == ["\u{16EE}"]
+ end
+
+ it "doesn't match Unicode No characters with [[:word:]]" do
+ "\u{17F0}".match(/[[:word:]]/).should be_nil
+ end
+ it "doesn't match Unicode punctuation characters with [[:word:]]" do
+ "\u{3F}".match(/[[:word:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode control characters with [[:word:]]" do
+ "\u{16}".match(/[[:word:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode format characters with [[:word:]]" do
+ "\u{2060}".match(/[[:word:]]/).should be_nil
+ end
+
+ it "doesn't match Unicode private-use characters with [[:word:]]" do
+ "\u{E001}".match(/[[:word:]]/).should be_nil
+ end
+
+ it "matches unicode named character properties" do
+ "a1".match(/\p{Alpha}/).to_a.should == ["a"]
+ end
+
+ it "matches unicode abbreviated character properties" do
+ "a1".match(/\p{L}/).to_a.should == ["a"]
+ end
+
+ it "matches unicode script properties" do
+ "a\u06E9b".match(/\p{Arabic}/).to_a.should == ["\u06E9"]
+ end
+
+ it "matches unicode Han properties" do
+ "æ¾æœ¬è¡Œå¼˜ Ruby".match(/\p{Han}+/u).to_a.should == ["æ¾æœ¬è¡Œå¼˜"]
+ end
+
+ it "matches unicode Hiragana properties" do
+ "Ruby(ルビー)ã€ã¾ã¤ã‚‚ã¨ã‚†ãã²ã‚".match(/\p{Hiragana}+/u).to_a.should == ["ã¾ã¤ã‚‚ã¨ã‚†ãã²ã‚"]
+ end
+
+ it "matches unicode Katakana properties" do
+ "Ruby(ルビー)ã€ã¾ã¤ã‚‚ã¨ã‚†ãã²ã‚".match(/\p{Katakana}+/u).to_a.should == ["ルビ"]
+ end
+
+ it "matches unicode Hangul properties" do
+ "루비(Ruby)".match(/\p{Hangul}+/u).to_a.should == ["루비"]
+ end
+
+ ruby_version_is "2.4" do
+ it "supports \\X (unicode 9.0 with UTR #51 workarounds)" do
+ # simple emoji without any fancy modifier or ZWJ
+ /\X/.match("\u{1F98A}").to_a.should == ["🦊"]
+
+ # skin tone modifier
+ /\X/.match("\u{1F918}\u{1F3FD}").to_a.should == ["🤘ðŸ½"]
+
+ # emoji joined with ZWJ
+ /\X/.match("\u{1F3F3}\u{FE0F}\u{200D}\u{1F308}").to_a.should == ["ðŸ³ï¸â€ðŸŒˆ"]
+ /\X/.match("\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F466}").to_a.should == ["👩â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦"]
+
+ # without the ZWJ
+ /\X+/.match("\u{1F3F3}\u{FE0F}\u{1F308}").to_a.should == ["ðŸ³ï¸ðŸŒˆ"]
+ /\X+/.match("\u{1F469}\u{1F469}\u{1F467}\u{1F466}").to_a.should == ["👩👩👧👦"]
+
+ # both of the ZWJ combined
+ /\X+/.match("\u{1F3F3}\u{FE0F}\u{200D}\u{1F308}\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F466}")
+ .to_a.should == ["ðŸ³ï¸â€ðŸŒˆðŸ‘©â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦"]
+ end
+ end
+end
diff --git a/spec/ruby/language/regexp/encoding_spec.rb b/spec/ruby/language/regexp/encoding_spec.rb
new file mode 100644
index 0000000000..f9979e7ea3
--- /dev/null
+++ b/spec/ruby/language/regexp/encoding_spec.rb
@@ -0,0 +1,103 @@
+# -*- encoding: binary -*-
+require_relative '../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Regexps with encoding modifiers" do
+ it "supports /e (EUC encoding)" do
+ match = /./e.match("\303\251".force_encoding(Encoding::EUC_JP))
+ match.to_a.should == ["\303\251".force_encoding(Encoding::EUC_JP)]
+ end
+
+ it "supports /e (EUC encoding) with interpolation" do
+ match = /#{/./}/e.match("\303\251".force_encoding(Encoding::EUC_JP))
+ match.to_a.should == ["\303\251".force_encoding(Encoding::EUC_JP)]
+ end
+
+ it "supports /e (EUC encoding) with interpolation /o" do
+ match = /#{/./}/e.match("\303\251".force_encoding(Encoding::EUC_JP))
+ match.to_a.should == ["\303\251".force_encoding(Encoding::EUC_JP)]
+ end
+
+ it 'uses EUC-JP as /e encoding' do
+ /./e.encoding.should == Encoding::EUC_JP
+ end
+
+ it 'preserves EUC-JP as /e encoding through interpolation' do
+ /#{/./}/e.encoding.should == Encoding::EUC_JP
+ end
+
+ it "supports /n (No encoding)" do
+ /./n.match("\303\251").to_a.should == ["\303"]
+ end
+
+ it "supports /n (No encoding) with interpolation" do
+ /#{/./}/n.match("\303\251").to_a.should == ["\303"]
+ end
+
+ it "supports /n (No encoding) with interpolation /o" do
+ /#{/./}/n.match("\303\251").to_a.should == ["\303"]
+ end
+
+ it 'uses US-ASCII as /n encoding if all chars are 7-bit' do
+ /./n.encoding.should == Encoding::US_ASCII
+ end
+
+ it 'uses ASCII-8BIT as /n encoding if not all chars are 7-bit' do
+ /\xFF/n.encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it 'preserves US-ASCII as /n encoding through interpolation if all chars are 7-bit' do
+ /.#{/./}/n.encoding.should == Encoding::US_ASCII
+ end
+
+ it 'preserves ASCII-8BIT as /n encoding through interpolation if all chars are 7-bit' do
+ /\xFF#{/./}/n.encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "supports /s (Windows_31J encoding)" do
+ match = /./s.match("\303\251".force_encoding(Encoding::Windows_31J))
+ match.to_a.should == ["\303".force_encoding(Encoding::Windows_31J)]
+ end
+
+ it "supports /s (Windows_31J encoding) with interpolation" do
+ match = /#{/./}/s.match("\303\251".force_encoding(Encoding::Windows_31J))
+ match.to_a.should == ["\303".force_encoding(Encoding::Windows_31J)]
+ end
+
+ it "supports /s (Windows_31J encoding) with interpolation and /o" do
+ match = /#{/./}/s.match("\303\251".force_encoding(Encoding::Windows_31J))
+ match.to_a.should == ["\303".force_encoding(Encoding::Windows_31J)]
+ end
+
+ it 'uses Windows-31J as /s encoding' do
+ /./s.encoding.should == Encoding::Windows_31J
+ end
+
+ it 'preserves Windows-31J as /s encoding through interpolation' do
+ /#{/./}/s.encoding.should == Encoding::Windows_31J
+ end
+
+ it "supports /u (UTF8 encoding)" do
+ /./u.match("\303\251".force_encoding('utf-8')).to_a.should == ["\u{e9}"]
+ end
+
+ it "supports /u (UTF8 encoding) with interpolation" do
+ /#{/./}/u.match("\303\251".force_encoding('utf-8')).to_a.should == ["\u{e9}"]
+ end
+
+ it "supports /u (UTF8 encoding) with interpolation and /o" do
+ /#{/./}/u.match("\303\251".force_encoding('utf-8')).to_a.should == ["\u{e9}"]
+ end
+
+ it 'uses UTF-8 as /u encoding' do
+ /./u.encoding.should == Encoding::UTF_8
+ end
+
+ it 'preserves UTF-8 as /u encoding through interpolation' do
+ /#{/./}/u.encoding.should == Encoding::UTF_8
+ end
+
+ it "selects last of multiple encoding specifiers" do
+ /foo/ensuensuens.should == /foo/s
+ end
+end
diff --git a/spec/ruby/language/regexp/escapes_spec.rb b/spec/ruby/language/regexp/escapes_spec.rb
new file mode 100644
index 0000000000..a4a9cb1793
--- /dev/null
+++ b/spec/ruby/language/regexp/escapes_spec.rb
@@ -0,0 +1,81 @@
+# -*- encoding: binary -*-
+require_relative '../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Regexps with escape characters" do
+ it "they're supported" 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
+ /\r/.match("\r").to_a.should == ["\r"] # return
+ /\f/.match("\f").to_a.should == ["\f"] # form feed
+ /\a/.match("\a").to_a.should == ["\a"] # bell
+ /\e/.match("\e").to_a.should == ["\e"] # escape
+
+ # \nnn octal char (encoded byte value)
+ end
+
+ it "support quoting meta-characters via escape sequence" do
+ /\\/.match("\\").to_a.should == ["\\"]
+ /\//.match("/").to_a.should == ["/"]
+ # parenthesis, etc
+ /\(/.match("(").to_a.should == ["("]
+ /\)/.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
+ /\?/.match("?").to_a.should == ["?"]
+ /\./.match(".").to_a.should == ["."]
+ /\*/.match("*").to_a.should == ["*"]
+ /\+/.match("+").to_a.should == ["+"]
+ # line anchors
+ /\^/.match("^").to_a.should == ["^"]
+ /\$/.match("$").to_a.should == ["$"]
+ end
+
+ it "allows any character to be escaped" do
+ /\y/.match("y").to_a.should == ["y"]
+ end
+
+ it "support \\x (hex characters)" do
+ /\xA/.match("\nxyz").to_a.should == ["\n"]
+ /\x0A/.match("\n").to_a.should == ["\n"]
+ /\xAA/.match("\nA").should be_nil
+ /\x0AA/.match("\nA").to_a.should == ["\nA"]
+ /\xAG/.match("\nG").to_a.should == ["\nG"]
+ # Non-matches
+ lambda { eval('/\xG/') }.should raise_error(SyntaxError)
+
+ # \x{7HHHHHHH} wide hexadecimal char (character code point value)
+ end
+
+ it "support \\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"]
+ /\c(\cH\ch/.match("\b\b\b").to_a.should == ["\b\b\b"]
+ /\c)\cI\ci/.match("\t\t\t").to_a.should == ["\t\t\t"]
+ /\c*\cJ\cj/.match("\n\n\n").to_a.should == ["\n\n\n"]
+ /\c+\cK\ck/.match("\v\v\v").to_a.should == ["\v\v\v"]
+ /\c,\cL\cl/.match("\f\f\f").to_a.should == ["\f\f\f"]
+ /\c-\cM\cm/.match("\r\r\r").to_a.should == ["\r\r\r"]
+
+ /\cJ/.match("\r").should be_nil
+
+ # Parsing precedence
+ /\cJ+/.match("\n\n").to_a.should == ["\n\n"] # Quantifers apply to entire escape sequence
+ /\\cJ/.match("\\cJ").to_a.should == ["\\cJ"]
+ lambda { eval('/[abc\x]/') }.should raise_error(SyntaxError) # \x is treated as a escape sequence even inside a character class
+ # Syntax error
+ lambda { eval('/\c/') }.should raise_error(SyntaxError)
+
+ # \cx control char (character code point value)
+ # \C-x control char (character code point value)
+ # \M-x meta (x|0x80) (character code point value)
+ # \M-\C-x meta control char (character code point value)
+ end
+end
diff --git a/spec/ruby/language/regexp/grouping_spec.rb b/spec/ruby/language/regexp/grouping_spec.rb
new file mode 100644
index 0000000000..61a52cba15
--- /dev/null
+++ b/spec/ruby/language/regexp/grouping_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Regexps with grouping" do
+ it "support ()" do
+ /(a)/.match("a").to_a.should == ["a", "a"]
+ end
+
+ it "allows groups to be nested" do
+ md = /(hay(st)a)ck/.match('haystack')
+ md.to_a.should == ['haystack','haysta', 'st']
+ end
+
+ it "raises a SyntaxError when parentheses aren't balanced" do
+ lambda { eval "/(hay(st)ack/" }.should raise_error(SyntaxError)
+ end
+
+ it "supports (?: ) (non-capturing group)" do
+ /(?:foo)(bar)/.match("foobar").to_a.should == ["foobar", "bar"]
+ # Parsing precedence
+ /(?:xdigit:)/.match("xdigit:").to_a.should == ["xdigit:"]
+ end
+end
diff --git a/spec/ruby/language/regexp/interpolation_spec.rb b/spec/ruby/language/regexp/interpolation_spec.rb
new file mode 100644
index 0000000000..c25a3cdb78
--- /dev/null
+++ b/spec/ruby/language/regexp/interpolation_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Regexps with interpolation" do
+
+ it "allows interpolation of strings" do
+ str = "foo|bar"
+ /#{str}/.should == /foo|bar/
+ end
+
+ it "allows interpolation of literal regexps" do
+ re = /foo|bar/
+ /#{re}/.should == /(?-mix:foo|bar)/
+ end
+
+ it "allows interpolation of any object that responds to to_s" do
+ o = Object.new
+ def o.to_s
+ "object_with_to_s"
+ end
+ /#{o}/.should == /object_with_to_s/
+ end
+
+ it "allows interpolation which mixes modifiers" do
+ re = /foo/i
+ /#{re} bar/m.should == /(?i-mx:foo) bar/m
+ end
+
+ it "allows interpolation to interact with other Regexp constructs" do
+ str = "foo)|(bar"
+ /(#{str})/.should == /(foo)|(bar)/
+
+ str = "a"
+ /[#{str}-z]/.should == /[a-z]/
+ end
+
+ it "gives precedence to escape sequences over substitution" do
+ str = "J"
+ /\c#{str}/.to_s.should == '(?-mix:\c#' + '{str})'
+ end
+
+ it "throws RegexpError for malformed interpolation" do
+ s = ""
+ lambda { /(#{s}/ }.should raise_error(RegexpError)
+ s = "("
+ lambda { /#{s}/ }.should raise_error(RegexpError)
+ end
+
+ it "allows interpolation in extended mode" do
+ var = "#comment\n foo #comment\n | bar"
+ (/#{var}/x =~ "foo").should == (/foo|bar/ =~ "foo")
+ end
+
+ it "allows escape sequences in interpolated regexps" do
+ escape_seq = %r{"\x80"}n
+ %r{#{escape_seq}}n.should == /(?-mix:"\x80")/n
+ end
+end
diff --git a/spec/ruby/language/regexp/modifiers_spec.rb b/spec/ruby/language/regexp/modifiers_spec.rb
new file mode 100644
index 0000000000..65c9c24ceb
--- /dev/null
+++ b/spec/ruby/language/regexp/modifiers_spec.rb
@@ -0,0 +1,117 @@
+require_relative '../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Regexps with modifers" do
+ it "supports /i (case-insensitive)" do
+ /foo/i.match("FOO").to_a.should == ["FOO"]
+ end
+
+ it "supports /m (multiline)" do
+ /foo.bar/m.match("foo\nbar").to_a.should == ["foo\nbar"]
+ /foo.bar/.match("foo\nbar").should be_nil
+ end
+
+ it "supports /x (extended syntax)" do
+ /\d +/x.match("abc123").to_a.should == ["123"] # Quantifiers can be separated from the expression they apply to
+ end
+
+ it "supports /o (once)" do
+ 2.times do |i|
+ /#{i}/o.should == /0/
+ end
+ end
+
+ it "invokes substitutions for /o only once" do
+ ScratchPad.record []
+ o = Object.new
+ def o.to_s
+ ScratchPad << :to_s
+ "class_with_to_s"
+ end
+ eval "2.times { /#{o}/o }"
+ ScratchPad.recorded.should == [:to_s]
+ end
+
+ it "supports modifier combinations" do
+ /foo/imox.match("foo").to_a.should == ["foo"]
+ /foo/imoximox.match("foo").to_a.should == ["foo"]
+
+ lambda { eval('/foo/a') }.should raise_error(SyntaxError)
+ end
+
+ ruby_version_is "2.4" do
+ it "supports (?~) (absent operator)" do
+ Regexp.new("(?~foo)").match("hello").to_a.should == ["hello"]
+ "foo".scan(Regexp.new("(?~foo)")).should == ["fo","o",""]
+ end
+ end
+
+ it "supports (?imx-imx) (inline modifiers)" do
+ /(?i)foo/.match("FOO").to_a.should == ["FOO"]
+ /foo(?i)/.match("FOO").should be_nil
+ # Interaction with /i
+ /(?-i)foo/i.match("FOO").should be_nil
+ /foo(?-i)/i.match("FOO").to_a.should == ["FOO"]
+ # Multiple uses
+ /foo (?i)bar (?-i)baz/.match("foo BAR baz").to_a.should == ["foo BAR baz"]
+ /foo (?i)bar (?-i)baz/.match("foo BAR BAZ").should be_nil
+
+ /(?m)./.match("\n").to_a.should == ["\n"]
+ /.(?m)/.match("\n").should be_nil
+ # Interaction with /m
+ /(?-m)./m.match("\n").should be_nil
+ /.(?-m)/m.match("\n").to_a.should == ["\n"]
+ # Multiple uses
+ /. (?m). (?-m)./.match(". \n .").to_a.should == [". \n ."]
+ /. (?m). (?-m)./.match(". \n \n").should be_nil
+
+ /(?x) foo /.match("foo").to_a.should == ["foo"]
+ / foo (?x)/.match("foo").should be_nil
+ # Interaction with /x
+ /(?-x) foo /x.match("foo").should be_nil
+ / foo (?-x)/x.match("foo").to_a.should == ["foo"]
+ # Multiple uses
+ /( foo )(?x)( bar )(?-x)( baz )/.match(" foo bar baz ").to_a.should == [" foo bar baz ", " foo ", "bar", " baz "]
+ /( foo )(?x)( bar )(?-x)( baz )/.match(" foo barbaz").should be_nil
+
+ # Parsing
+ /(?i-i)foo/.match("FOO").should be_nil
+ /(?ii)foo/.match("FOO").to_a.should == ["FOO"]
+ /(?-)foo/.match("foo").to_a.should == ["foo"]
+ lambda { eval('/(?o)/') }.should raise_error(SyntaxError)
+ end
+
+ it "supports (?imx-imx:expr) (scoped inline modifiers)" do
+ /foo (?i:bar) baz/.match("foo BAR baz").to_a.should == ["foo BAR baz"]
+ /foo (?i:bar) baz/.match("foo BAR BAZ").should be_nil
+ /foo (?-i:bar) baz/i.match("foo BAR BAZ").should be_nil
+
+ /. (?m:.) ./.match(". \n .").to_a.should == [". \n ."]
+ /. (?m:.) ./.match(". \n \n").should be_nil
+ /. (?-m:.) ./m.match("\n \n \n").should be_nil
+
+ /( foo )(?x: bar )( baz )/.match(" foo bar baz ").to_a.should == [" foo bar baz ", " foo ", " baz "]
+ /( foo )(?x: bar )( baz )/.match(" foo barbaz").should be_nil
+ /( foo )(?-x: bar )( baz )/x.match("foo bar baz").to_a.should == ["foo bar baz", "foo", "baz"]
+
+ # Parsing
+ /(?i-i:foo)/.match("FOO").should be_nil
+ /(?ii:foo)/.match("FOO").to_a.should == ["FOO"]
+ /(?-:)foo/.match("foo").to_a.should == ["foo"]
+ lambda { eval('/(?o:)/') }.should raise_error(SyntaxError)
+ end
+
+ it "supports . with /m" do
+ # Basic matching
+ /./m.match("\n").to_a.should == ["\n"]
+ end
+
+ it "supports ASII/Unicode modifiers" do
+ eval('/(?a)[[:alpha:]]+/').match("a\u3042").to_a.should == ["a"]
+ eval('/(?d)[[:alpha:]]+/').match("a\u3042").to_a.should == ["a\u3042"]
+ eval('/(?u)[[:alpha:]]+/').match("a\u3042").to_a.should == ["a\u3042"]
+ eval('/(?a)\w+/').match("a\u3042").to_a.should == ["a"]
+ eval('/(?d)\w+/').match("a\u3042").to_a.should == ["a"]
+ eval('/(?u)\w+/').match("a\u3042").to_a.should == ["a\u3042"]
+ end
+end
diff --git a/spec/ruby/language/regexp/repetition_spec.rb b/spec/ruby/language/regexp/repetition_spec.rb
new file mode 100644
index 0000000000..07004d8190
--- /dev/null
+++ b/spec/ruby/language/regexp/repetition_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Regexps with repetition" do
+ it "supports * (0 or more of previous subexpression)" do
+ /a*/.match("aaa").to_a.should == ["aaa"]
+ /a*/.match("bbb").to_a.should == [""]
+ /<.*>/.match("<a>foo</a>").to_a.should == ["<a>foo</a>"] # it is greedy
+ end
+
+ it "supports *? (0 or more of previous subexpression - lazy)" do
+ /a*?/.match("aaa").to_a.should == [""]
+ /<.*?>/.match("<a>foo</a>").to_a.should == ["<a>"]
+ end
+
+ it "supports + (1 or more of previous subexpression)" do
+ /a+/.match("aaa").to_a.should == ["aaa"]
+ /a+/.match("bbb").should be_nil
+ /<.+>/.match("<a>foo</a>").to_a.should == ["<a>foo</a>"] # it is greedy
+ end
+
+ it "supports +? (0 or more of previous subexpression - lazy)" do
+ /a+?/.match("aaa").to_a.should == ["a"]
+ /<.+?>/.match("<a>foo</a>").to_a.should == ["<a>"]
+ end
+
+ it "supports {m,n} (m to n of previous subexpression)" do
+ /a{2,4}/.match("aaaaaa").to_a.should == ["aaaa"]
+ /<.{1,}>/.match("<a>foo</a>").to_a.should == ["<a>foo</a>"] # it is greedy
+ end
+
+ it "supports {m,n}? (m to n of previous subexpression) - lazy)" do
+ /<.{1,}?>/.match("<a>foo</a>").to_a.should == ["<a>"]
+ /.([0-9]){3,5}?foo/.match("9876543210foo").to_a.should == ["543210foo", "0"]
+ end
+
+ ruby_version_is ""..."2.4" do
+ it "does not treat {m,n}+ as possessive" do
+ @regexp = eval "/foo(A{0,1}+)Abar/"
+ @regexp.match("fooAAAbar").to_a.should == ["fooAAAbar", "AA"]
+ end
+ end
+
+ ruby_version_is "2.4" do
+ it "does not treat {m,n}+ as possessive" do
+ -> {
+ @regexp = eval "/foo(A{0,1}+)Abar/"
+ }.should complain(/nested repeat operato/)
+ @regexp.match("fooAAAbar").to_a.should == ["fooAAAbar", "AA"]
+ end
+ end
+
+ it "supports ? (0 or 1 of previous subexpression)" do
+ /a?/.match("aaa").to_a.should == ["a"]
+ /a?/.match("bbb").to_a.should == [""]
+ end
+end
diff --git a/spec/ruby/language/regexp_spec.rb b/spec/ruby/language/regexp_spec.rb
new file mode 100644
index 0000000000..6da29ee7a2
--- /dev/null
+++ b/spec/ruby/language/regexp_spec.rb
@@ -0,0 +1,197 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Literal Regexps" do
+ it "matches against $_ (last input) in a conditional if no explicit matchee provided" do
+ -> {
+ eval <<-EOR
+ $_ = nil
+ (true if /foo/).should_not == true
+
+ $_ = "foo"
+ (true if /foo/).should == true
+ EOR
+ }.should complain(/regex literal in condition/)
+ end
+
+ it "yields a Regexp" do
+ /Hello/.should be_kind_of(Regexp)
+ end
+
+ it "caches the Regexp object" do
+ rs = []
+ 2.times do |i|
+ rs << /foo/
+ end
+ rs[0].should equal(rs[1])
+ end
+
+ it "throws SyntaxError for malformed literals" do
+ lambda { eval('/(/') }.should raise_error(SyntaxError)
+ end
+
+ #############################################################################
+ # %r
+ #############################################################################
+
+ it "supports paired delimiters with %r" do
+ LanguageSpecs.paired_delimiters.each do |p0, p1|
+ eval("%r#{p0} foo #{p1}").should == / foo /
+ end
+ end
+
+ it "supports grouping constructs that are also paired delimiters" do
+ LanguageSpecs.paired_delimiters.each do |p0, p1|
+ eval("%r#{p0} () [c]{1} #{p1}").should == / () [c]{1} /
+ end
+ end
+
+ it "allows second part of paired delimiters to be used as non-paired delimiters" do
+ LanguageSpecs.paired_delimiters.each do |p0, p1|
+ eval("%r#{p1} foo #{p1}").should == / foo /
+ end
+ end
+
+ it "disallows first part of paired delimiters to be used as non-paired delimiters" do
+ LanguageSpecs.paired_delimiters.each do |p0, p1|
+ lambda { eval("%r#{p0} foo #{p0}") }.should raise_error(SyntaxError)
+ end
+ end
+
+ it "supports non-paired delimiters delimiters with %r" do
+ LanguageSpecs.non_paired_delimiters.each do |c|
+ eval("%r#{c} foo #{c}").should == / foo /
+ end
+ end
+
+ it "disallows alphabets as non-paired delimiter with %r" do
+ lambda { eval('%ra foo a') }.should raise_error(SyntaxError)
+ end
+
+ it "disallows spaces after %r and delimiter" do
+ lambda { eval('%r !foo!') }.should raise_error(SyntaxError)
+ end
+
+ it "allows unescaped / to be used with %r" do
+ %r[/].to_s.should == /\//.to_s
+ end
+
+
+ #############################################################################
+ # Specs for the matching semantics
+ #############################################################################
+
+ it "supports . (any character except line terminator)" do
+ # Basic matching
+ /./.match("foo").to_a.should == ["f"]
+ # Basic non-matching
+ /./.match("").should be_nil
+ /./.match("\n").should be_nil
+ /./.match("\0").to_a.should == ["\0"]
+ end
+
+
+ it "supports | (alternations)" do
+ /a|b/.match("a").to_a.should == ["a"]
+ end
+
+ it "supports (?> ) (embedded subexpression)" do
+ /(?>foo)(?>bar)/.match("foobar").to_a.should == ["foobar"]
+ /(?>foo*)obar/.match("foooooooobar").should be_nil # it is possesive
+ end
+
+ it "supports (?# )" do
+ /foo(?#comment)bar/.match("foobar").to_a.should == ["foobar"]
+ /foo(?#)bar/.match("foobar").to_a.should == ["foobar"]
+ end
+
+ it "supports (?<= ) (positive lookbehind)" do
+ /foo.(?<=\d)/.match("fooA foo1").to_a.should == ["foo1"]
+ end
+
+ it "supports (?<! ) (negative lookbehind)" do
+ /foo.(?<!\d)/.match("foo1 fooA").to_a.should == ["fooA"]
+ end
+
+ it "supports \\g (named backreference)" do
+ /(?<foo>foo.)bar\g<foo>/.match("foo1barfoo2").to_a.should == ["foo1barfoo2", "foo2"]
+ end
+
+ it "supports character class composition" do
+ /[a-z&&[^a-c]]+/.match("abcdef").to_a.should == ["def"]
+ /[a-z&&[^d-i&&[^d-f]]]+/.match("abcdefghi").to_a.should == ["abcdef"]
+ end
+
+ it "supports possessive quantifiers" do
+ /fooA++bar/.match("fooAAAbar").to_a.should == ["fooAAAbar"]
+
+ /fooA++Abar/.match("fooAAAbar").should be_nil
+ /fooA?+Abar/.match("fooAAAbar").should be_nil
+ /fooA*+Abar/.match("fooAAAbar").should be_nil
+ end
+
+ it "supports conditional regular expressions with positional capture groups" do
+ pattern = /\A(foo)?(?(1)(T)|(F))\z/
+
+ pattern.should =~ 'fooT'
+ pattern.should =~ 'F'
+ pattern.should_not =~ 'fooF'
+ pattern.should_not =~ 'T'
+ end
+
+ it "supports conditional regular expressions with named capture groups" do
+ pattern = /\A(?<word>foo)?(?(<word>)(T)|(F))\z/
+
+ pattern.should =~ 'fooT'
+ pattern.should =~ 'F'
+ pattern.should_not =~ 'fooF'
+ 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
+
+ ruby_version_is '2.4' do
+ 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
+ char_uppercase = "\u{104B0}" # OSAGE CAPITAL LETTER A
+ /[[:upper:]]/.match(char_uppercase).to_s.should == char_uppercase
+ end
+ end
+
+ ruby_version_is ""..."2.4" do
+ it "does not support handling unicode 9.0 characters with POSIX bracket expressions" do
+ char_lowercase = "\u{104D8}" # OSAGE SMALL LETTER A
+ /[[:lower:]]/.match(char_lowercase).should == nil
+
+ char_uppercase = "\u{104B0}" # OSAGE CAPITAL LETTER A
+ /[[:upper:]]/.match(char_lowercase).should == nil
+ end
+
+ it "supports handling unicode 8.0 characters with POSIX bracket expressions" do
+ char_lowercase = "\u{A7B5}" # LATIN SMALL LETTER BETA
+ /[[:lower:]]/.match(char_lowercase).to_s.should == char_lowercase
+
+ char_uppercase = "\u{A7B4}" # LATIN CAPITAL LETTER BETA
+ /[[:upper:]]/.match(char_uppercase).to_s.should == char_uppercase
+ end
+ end
+end
diff --git a/spec/ruby/language/rescue_spec.rb b/spec/ruby/language/rescue_spec.rb
new file mode 100644
index 0000000000..7df8b6db90
--- /dev/null
+++ b/spec/ruby/language/rescue_spec.rb
@@ -0,0 +1,493 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/rescue'
+
+class SpecificExampleException < StandardError
+end
+class OtherCustomException < StandardError
+end
+class ArbitraryException < StandardError
+end
+
+exception_list = [SpecificExampleException, ArbitraryException]
+
+describe "The rescue keyword" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "can be used to handle a specific exception" do
+ begin
+ raise SpecificExampleException, "Raising this to be handled below"
+ rescue SpecificExampleException
+ :caught
+ end.should == :caught
+ end
+
+ it "can capture the raised exception in a local variable" do
+ begin
+ raise SpecificExampleException, "some text"
+ rescue SpecificExampleException => e
+ e.message.should == "some text"
+ end
+ end
+
+ it "returns value from `rescue` if an exception was raised" do
+ begin
+ raise
+ rescue
+ :caught
+ end.should == :caught
+ end
+
+ it "returns value from `else` section if no exceptions were raised" do
+ result = begin
+ :begin
+ rescue
+ :rescue
+ else
+ :else
+ ensure
+ :ensure
+ end
+
+ result.should == :else
+ end
+
+ it "can rescue multiple raised exceptions with a single rescue block" do
+ [lambda{raise ArbitraryException}, lambda{raise SpecificExampleException}].map do |block|
+ begin
+ block.call
+ rescue SpecificExampleException, ArbitraryException
+ :caught
+ end
+ end.should == [:caught, :caught]
+ end
+
+ it "can rescue a splatted list of exceptions" do
+ caught_it = false
+ begin
+ raise SpecificExampleException, "not important"
+ rescue *exception_list
+ caught_it = true
+ end
+ caught_it.should be_true
+ caught = []
+ [lambda{raise ArbitraryException}, lambda{raise SpecificExampleException}].each do |block|
+ begin
+ block.call
+ rescue *exception_list
+ caught << $!
+ end
+ end
+ caught.size.should == 2
+ exception_list.each do |exception_class|
+ caught.map{|e| e.class}.should include(exception_class)
+ end
+ end
+
+ it "can combine a splatted list of exceptions with a literal list of exceptions" do
+ caught_it = false
+ begin
+ raise SpecificExampleException, "not important"
+ rescue ArbitraryException, *exception_list
+ caught_it = true
+ end
+ caught_it.should be_true
+ caught = []
+ [lambda{raise ArbitraryException}, lambda{raise SpecificExampleException}].each do |block|
+ begin
+ block.call
+ rescue ArbitraryException, *exception_list
+ caught << $!
+ end
+ end
+ caught.size.should == 2
+ exception_list.each do |exception_class|
+ caught.map{|e| e.class}.should include(exception_class)
+ end
+ end
+
+ it "will only rescue the specified exceptions when doing a splat rescue" do
+ lambda do
+ begin
+ raise OtherCustomException, "not rescued!"
+ rescue *exception_list
+ end
+ end.should raise_error(OtherCustomException)
+ end
+
+ it "can rescue different types of exceptions in different ways" do
+ begin
+ raise Exception
+ rescue RuntimeError
+ rescue StandardError
+ rescue Exception
+ ScratchPad << :exception
+ end
+
+ ScratchPad.recorded.should == [:exception]
+ end
+
+ it "rescues exception within the first suitable section in order of declaration" do
+ begin
+ raise StandardError
+ rescue RuntimeError
+ ScratchPad << :runtime_error
+ rescue StandardError
+ ScratchPad << :standard_error
+ rescue Exception
+ ScratchPad << :exception
+ end
+
+ ScratchPad.recorded.should == [:standard_error]
+ end
+
+ it "rescues the exception in the deepest rescue block declared to handle the appropriate exception type" do
+ begin
+ begin
+ RescueSpecs.raise_standard_error
+ rescue ArgumentError
+ end
+ rescue StandardError => e
+ e.backtrace.first.should include ":in `raise_standard_error'"
+ else
+ fail("exception wasn't handled by the correct rescue block")
+ end
+ end
+
+ it "will execute an else block only if no exceptions were raised" do
+ result = begin
+ ScratchPad << :one
+ rescue
+ ScratchPad << :does_not_run
+ else
+ ScratchPad << :two
+ :val
+ end
+ result.should == :val
+ ScratchPad.recorded.should == [:one, :two]
+ end
+
+ it "will execute an else block with ensure only if no exceptions were raised" do
+ result = begin
+ ScratchPad << :one
+ rescue
+ ScratchPad << :does_not_run
+ else
+ ScratchPad << :two
+ :val
+ ensure
+ ScratchPad << :ensure
+ :ensure_val
+ end
+ result.should == :val
+ ScratchPad.recorded.should == [:one, :two, :ensure]
+ end
+
+ it "will execute an else block only if no exceptions were raised in a method" do
+ result = RescueSpecs.begin_else(false)
+ result.should == :val
+ ScratchPad.recorded.should == [:one, :else_ran]
+ end
+
+ it "will execute an else block with ensure only if no exceptions were raised in a method" do
+ result = RescueSpecs.begin_else_ensure(false)
+ result.should == :val
+ ScratchPad.recorded.should == [:one, :else_ran, :ensure_ran]
+ end
+
+ it "will execute an else block but use the outer scope return value in a method" do
+ result = RescueSpecs.begin_else_return(false)
+ result.should == :return_val
+ ScratchPad.recorded.should == [:one, :else_ran, :outside_begin]
+ end
+
+ it "will execute an else block with ensure but use the outer scope return value in a method" do
+ result = RescueSpecs.begin_else_return_ensure(false)
+ result.should == :return_val
+ ScratchPad.recorded.should == [:one, :else_ran, :ensure_ran, :outside_begin]
+ end
+
+ ruby_version_is ''...'2.6' do
+ it "will execute an else block even without rescue and ensure" do
+ lambda {
+ eval <<-ruby
+ begin
+ ScratchPad << :begin
+ else
+ ScratchPad << :else
+ end
+ ruby
+ }.should complain(/else without rescue is useless/)
+
+ ScratchPad.recorded.should == [:begin, :else]
+ end
+ end
+
+ ruby_version_is '2.6' do
+ it "raises SyntaxError when else is used without rescue and ensure" do
+ lambda {
+ eval <<-ruby
+ begin
+ ScratchPad << :begin
+ else
+ ScratchPad << :else
+ end
+ ruby
+ }.should raise_error(SyntaxError, /else without rescue is useless/)
+ end
+ end
+
+ it "will not execute an else block if an exception was raised" do
+ result = begin
+ ScratchPad << :one
+ raise "an error occurred"
+ rescue
+ ScratchPad << :two
+ :val
+ else
+ ScratchPad << :does_not_run
+ end
+ result.should == :val
+ ScratchPad.recorded.should == [:one, :two]
+ end
+
+ it "will not execute an else block with ensure if an exception was raised" do
+ result = begin
+ ScratchPad << :one
+ raise "an error occurred"
+ rescue
+ ScratchPad << :two
+ :val
+ else
+ ScratchPad << :does_not_run
+ ensure
+ ScratchPad << :ensure
+ :ensure_val
+ end
+ result.should == :val
+ ScratchPad.recorded.should == [:one, :two, :ensure]
+ end
+
+ it "will not execute an else block if an exception was raised in a method" do
+ result = RescueSpecs.begin_else(true)
+ result.should == :rescue_val
+ ScratchPad.recorded.should == [:one, :rescue_ran]
+ end
+
+ it "will not execute an else block with ensure if an exception was raised in a method" do
+ result = RescueSpecs.begin_else_ensure(true)
+ result.should == :rescue_val
+ ScratchPad.recorded.should == [:one, :rescue_ran, :ensure_ran]
+ end
+
+ it "will not execute an else block but use the outer scope return value in a method" do
+ result = RescueSpecs.begin_else_return(true)
+ result.should == :return_val
+ ScratchPad.recorded.should == [:one, :rescue_ran, :outside_begin]
+ end
+
+ it "will not execute an else block with ensure but use the outer scope return value in a method" do
+ result = RescueSpecs.begin_else_return_ensure(true)
+ result.should == :return_val
+ ScratchPad.recorded.should == [:one, :rescue_ran, :ensure_ran, :outside_begin]
+ end
+
+ it "will not rescue errors raised in an else block in the rescue block above it" do
+ lambda do
+ begin
+ ScratchPad << :one
+ rescue Exception
+ ScratchPad << :does_not_run
+ else
+ ScratchPad << :two
+ raise SpecificExampleException, "an error from else"
+ end
+ end.should raise_error(SpecificExampleException)
+ ScratchPad.recorded.should == [:one, :two]
+ end
+
+ it "parses 'a += b rescue c' as 'a += (b rescue c)'" do
+ a = 'a'
+ c = 'c'
+ a += b rescue c
+ a.should == 'ac'
+ end
+
+ context "without rescue expression" do
+ it "will rescue only StandardError and its subclasses" do
+ begin
+ raise StandardError
+ rescue
+ ScratchPad << :caught
+ end
+
+ ScratchPad.recorded.should == [:caught]
+ end
+
+ it "will not rescue exceptions except StandardError" do
+ [ Exception.new, NoMemoryError.new, ScriptError.new, SecurityError.new,
+ SignalException.new('INT'), SystemExit.new, SystemStackError.new
+ ].each do |exception|
+ lambda {
+ begin
+ raise exception
+ rescue
+ ScratchPad << :caught
+ end
+ }.should raise_error(exception.class)
+ end
+ ScratchPad.recorded.should == []
+ end
+ end
+
+ it "uses === to compare against rescued classes" do
+ rescuer = Class.new
+
+ def rescuer.===(exception)
+ true
+ end
+
+ begin
+ raise Exception
+ rescue rescuer
+ rescued = :success
+ rescue Exception
+ rescued = :failure
+ end
+
+ rescued.should == :success
+ end
+
+ it "only accepts Module or Class in rescue clauses" do
+ rescuer = 42
+ lambda {
+ begin
+ raise "error"
+ rescue rescuer
+ end
+ }.should raise_error(TypeError) { |e|
+ e.message.should =~ /class or module required for rescue clause/
+ }
+ end
+
+ it "only accepts Module or Class in splatted rescue clauses" do
+ rescuer = [42]
+ lambda {
+ begin
+ raise "error"
+ rescue *rescuer
+ end
+ }.should raise_error(TypeError) { |e|
+ e.message.should =~ /class or module required for rescue clause/
+ }
+ end
+
+ it "evaluates rescue expressions only when needed" do
+ begin
+ ScratchPad << :foo
+ rescue -> { ScratchPad << :bar; StandardError }.call
+ end
+
+ ScratchPad.recorded.should == [:foo]
+ end
+
+ it "suppresses exception from block when raises one from rescue expression" do
+ -> {
+ begin
+ raise "from block"
+ rescue (raise "from rescue expression")
+ end
+ }.should raise_error(RuntimeError, "from rescue expression") do |e|
+ e.cause.message.should == "from block"
+ end
+ end
+
+ it "should splat the handling Error classes" do
+ begin
+ raise "raise"
+ rescue *(RuntimeError) => e
+ :expected
+ end.should == :expected
+ end
+
+ it "allows rescue in class" do
+ eval <<-ruby
+ class RescueInClassExample
+ raise SpecificExampleException
+ rescue SpecificExampleException
+ ScratchPad << :caught
+ end
+ ruby
+
+ ScratchPad.recorded.should == [:caught]
+ end
+
+ it "does not allow rescue in {} block" do
+ lambda {
+ eval <<-ruby
+ lambda {
+ raise SpecificExampleException
+ rescue SpecificExampleException
+ :caught
+ }
+ ruby
+ }.should raise_error(SyntaxError)
+ end
+
+ ruby_version_is "2.5" do
+ it "allows rescue in 'do end' block" do
+ lambda = eval <<-ruby
+ lambda do
+ raise SpecificExampleException
+ rescue SpecificExampleException
+ ScratchPad << :caught
+ end.call
+ ruby
+
+ ScratchPad.recorded.should == [:caught]
+ end
+ end
+
+ ruby_version_is ""..."2.4" do
+ it "fails when using 'rescue' in method arguments" do
+ lambda { eval '1.+ (1 rescue 1)' }.should raise_error(SyntaxError)
+ end
+ end
+
+ ruby_version_is "2.4" do
+ it "allows 'rescue' in method arguments" do
+ two = eval '1.+ (raise("Error") rescue 1)'
+ two.should == 2
+ end
+
+ it "requires the 'rescue' in method arguments to be wrapped in parens" do
+ lambda { eval '1.+(1 rescue 1)' }.should raise_error(SyntaxError)
+ eval('1.+((1 rescue 1))').should == 2
+ end
+ end
+
+ describe "inline form" do
+ it "can be inlined" do
+ a = 1/0 rescue 1
+ a.should == 1
+ end
+
+ it "doesn't except rescue expression" do
+ lambda {
+ eval <<-ruby
+ a = 1 rescue RuntimeError 2
+ ruby
+ }.should raise_error(SyntaxError)
+ end
+
+ it "rescues only StandardError and its subclasses" do
+ a = raise(StandardError) rescue 1
+ a.should == 1
+
+ lambda {
+ a = raise(Exception) rescue 1
+ }.should raise_error(Exception)
+ end
+ end
+end
diff --git a/spec/ruby/language/retry_spec.rb b/spec/ruby/language/retry_spec.rb
new file mode 100644
index 0000000000..0b816daa5c
--- /dev/null
+++ b/spec/ruby/language/retry_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../spec_helper'
+
+describe "The retry statement" do
+ it "re-executes the closest block" do
+ retry_first = true
+ retry_second = true
+ results = []
+ begin
+ results << 1
+ raise
+ rescue
+ results << 2
+ if retry_first
+ results << 3
+ retry_first = false
+ retry
+ end
+ begin
+ results << 4
+ raise
+ rescue
+ results << 5
+ if retry_second
+ results << 6
+ retry_second = false
+ retry
+ end
+ end
+ end
+
+ results.should == [1, 2, 3, 1, 2, 4, 5, 6, 4, 5]
+ end
+
+ it "raises a SyntaxError when used outside of a begin statement" do
+ lambda { eval 'retry' }.should raise_error(SyntaxError)
+ end
+end
+
+describe "The retry keyword inside a begin block's rescue block" do
+ it "causes the begin block to be executed again" do
+ counter = 0
+
+ begin
+ counter += 1
+ raise "An exception"
+ rescue
+ retry unless counter == 7
+ end
+
+ counter.should == 7
+ end
+end
diff --git a/spec/ruby/language/return_spec.rb b/spec/ruby/language/return_spec.rb
new file mode 100644
index 0000000000..0a05914545
--- /dev/null
+++ b/spec/ruby/language/return_spec.rb
@@ -0,0 +1,483 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/return'
+
+describe "The return keyword" do
+ it "returns any object directly" do
+ def r; return 1; end
+ r().should == 1
+ end
+
+ it "returns an single element array directly" do
+ def r; return [1]; end
+ r().should == [1]
+ end
+
+ it "returns an multi element array directly" do
+ def r; return [1,2]; end
+ r().should == [1,2]
+ end
+
+ it "returns nil by default" do
+ def r; return; end
+ r().should be_nil
+ end
+
+ describe "in a Thread" do
+ it "raises a LocalJumpError if used to exit a thread" do
+ t = Thread.new {
+ begin
+ return
+ rescue LocalJumpError => e
+ e
+ end
+ }
+ t.value.should be_an_instance_of(LocalJumpError)
+ end
+ end
+
+ describe "when passed a splat" do
+ it "returns [] when the ary is empty" do
+ def r; ary = []; return *ary; end
+ r.should == []
+ end
+
+ it "returns the array when the array is size of 1" do
+ def r; ary = [1]; return *ary; end
+ r.should == [1]
+ end
+
+ it "returns the whole array when size is greater than 1" do
+ def r; ary = [1,2]; return *ary; end
+ r.should == [1,2]
+
+ def r; ary = [1,2,3]; return *ary; end
+ r.should == [1,2,3]
+ end
+
+ it "returns an array when used as a splat" do
+ def r; value = 1; return *value; end
+ r.should == [1]
+ end
+
+ it "calls 'to_a' on the splatted value first" do
+ def r
+ obj = Object.new
+ def obj.to_a
+ [1,2]
+ end
+
+ return *obj
+ end
+
+ r().should == [1,2]
+ end
+ end
+
+ describe "within a begin" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "executes ensure before returning" do
+ def f()
+ begin
+ ScratchPad << :begin
+ return :begin
+ ScratchPad << :after_begin
+ ensure
+ ScratchPad << :ensure
+ end
+ ScratchPad << :function
+ end
+ f().should == :begin
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "returns last value returned in ensure" do
+ def f()
+ begin
+ ScratchPad << :begin
+ return :begin
+ ScratchPad << :after_begin
+ ensure
+ ScratchPad << :ensure
+ return :ensure
+ ScratchPad << :after_ensure
+ end
+ ScratchPad << :function
+ end
+ f().should == :ensure
+ ScratchPad.recorded.should == [:begin, :ensure]
+ end
+
+ it "executes nested ensures before returning" do
+ def f()
+ begin
+ begin
+ ScratchPad << :inner_begin
+ return :inner_begin
+ ScratchPad << :after_inner_begin
+ ensure
+ ScratchPad << :inner_ensure
+ end
+ ScratchPad << :outer_begin
+ return :outer_begin
+ ScratchPad << :after_outer_begin
+ ensure
+ ScratchPad << :outer_ensure
+ end
+ ScratchPad << :function
+ end
+ f().should == :inner_begin
+ ScratchPad.recorded.should == [:inner_begin, :inner_ensure, :outer_ensure]
+ end
+
+ it "returns last value returned in nested ensures" do
+ def f()
+ begin
+ begin
+ ScratchPad << :inner_begin
+ return :inner_begin
+ ScratchPad << :after_inner_begin
+ ensure
+ ScratchPad << :inner_ensure
+ return :inner_ensure
+ ScratchPad << :after_inner_ensure
+ end
+ ScratchPad << :outer_begin
+ return :outer_begin
+ ScratchPad << :after_outer_begin
+ ensure
+ ScratchPad << :outer_ensure
+ return :outer_ensure
+ ScratchPad << :after_outer_ensure
+ end
+ ScratchPad << :function
+ end
+ f().should == :outer_ensure
+ ScratchPad.recorded.should == [:inner_begin, :inner_ensure, :outer_ensure]
+ end
+
+ it "executes the ensure clause when begin/ensure are inside a lambda" do
+ lambda do
+ begin
+ return
+ ensure
+ ScratchPad.recorded << :ensure
+ end
+ end.call
+ ScratchPad.recorded.should == [:ensure]
+ end
+ end
+
+ describe "within a block" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "causes lambda to return nil if invoked without any arguments" do
+ lambda { return; 456 }.call.should be_nil
+ end
+
+ it "causes lambda to return nil if invoked with an empty expression" do
+ lambda { return (); 456 }.call.should be_nil
+ end
+
+ it "causes lambda to return the value passed to return" do
+ lambda { return 123; 456 }.call.should == 123
+ end
+
+ it "causes the method that lexically encloses the block to return" do
+ ReturnSpecs::Blocks.new.enclosing_method.should == :return_value
+ ScratchPad.recorded.should == :before_return
+ end
+
+ it "returns from the lexically enclosing method even in case of chained calls" do
+ ReturnSpecs::NestedCalls.new.enclosing_method.should == :return_value
+ ScratchPad.recorded.should == :before_return
+ end
+
+ it "returns from the lexically enclosing method even in case of chained calls(in yield)" do
+ ReturnSpecs::NestedBlocks.new.enclosing_method.should == :return_value
+ ScratchPad.recorded.should == :before_return
+ end
+
+ it "causes the method to return even when the immediate parent has already returned" do
+ ReturnSpecs::SavedInnerBlock.new.start.should == :return_value
+ ScratchPad.recorded.should == :before_return
+ end
+
+ # jruby/jruby#3143
+ describe "downstream from a lambda" do
+ it "returns to its own return-capturing lexical enclosure" do
+ def a
+ ->{ yield }.call
+ return 2
+ end
+ def b
+ a { return 1 }
+ end
+
+ b.should == 1
+ end
+ end
+
+ end
+
+ describe "within two blocks" do
+ it "causes the method that lexically encloses the block to return" do
+ def f
+ 1.times { 1.times {return true}; false}; false
+ end
+ f.should be_true
+ end
+ end
+
+ describe "within define_method" do
+ it "goes through the method via a closure" do
+ ReturnSpecs::ThroughDefineMethod.new.outer.should == :good
+ end
+
+ it "stops at the method when the return is used directly" do
+ ReturnSpecs::DefineMethod.new.outer.should == :good
+ end
+ end
+
+ describe "invoked with a method call without parentheses with a block" do
+ it "returns the value returned from the method call" do
+ ReturnSpecs::MethodWithBlock.new.method1.should == 5
+ ReturnSpecs::MethodWithBlock.new.method2.should == [0, 1, 2]
+ end
+ end
+
+ ruby_version_is '2.4.2' do
+ describe "at top level" do
+ before :each do
+ @filename = tmp("top_return.rb")
+ ScratchPad.record []
+ end
+
+ after do
+ rm_r @filename
+ end
+
+ it "stops file execution" do
+ ruby_exe(<<-END_OF_CODE).should == "before return\n"
+ puts "before return"
+ return
+
+ puts "after return"
+ END_OF_CODE
+
+ $?.exitstatus.should == 0
+ end
+
+ describe "within if" do
+ it "is allowed" do
+ File.write(@filename, <<-END_OF_CODE)
+ ScratchPad << "before if"
+ if true
+ return
+ end
+
+ ScratchPad << "after if"
+ END_OF_CODE
+
+ load @filename
+ ScratchPad.recorded.should == ["before if"]
+ end
+ end
+
+ describe "within while loop" do
+ it "is allowed" do
+ File.write(@filename, <<-END_OF_CODE)
+ ScratchPad << "before while"
+ while true
+ return
+ end
+
+ ScratchPad << "after while"
+ END_OF_CODE
+
+ load @filename
+ ScratchPad.recorded.should == ["before while"]
+ end
+ end
+
+ describe "within a begin" do
+ it "is allowed in begin block" do
+ File.write(@filename, <<-END_OF_CODE)
+ ScratchPad << "before begin"
+ begin
+ return
+ end
+
+ ScratchPad << "after begin"
+ END_OF_CODE
+
+ load @filename
+ ScratchPad.recorded.should == ["before begin"]
+ end
+
+ it "is allowed in ensure block" do
+ File.write(@filename, <<-END_OF_CODE)
+ ScratchPad << "before begin"
+ begin
+ ensure
+ return
+ end
+
+ ScratchPad << "after begin"
+ END_OF_CODE
+
+ load @filename
+ ScratchPad.recorded.should == ["before begin"]
+ end
+
+ it "is allowed in rescue block" do
+ File.write(@filename, <<-END_OF_CODE)
+ ScratchPad << "before begin"
+ begin
+ raise
+ rescue RuntimeError
+ return
+ end
+
+ ScratchPad << "after begin"
+ END_OF_CODE
+
+ load @filename
+ ScratchPad.recorded.should == ["before begin"]
+ end
+
+ it "fires ensure block before returning" do
+ ruby_exe(<<-END_OF_CODE).should == "within ensure\n"
+ begin
+ return
+ ensure
+ puts "within ensure"
+ end
+
+ puts "after begin"
+ END_OF_CODE
+ end
+
+ ruby_bug "#14061", "2.4"..."2.6" do
+ it "fires ensure block before returning while loads file" do
+ File.write(@filename, <<-END_OF_CODE)
+ ScratchPad << "before begin"
+ begin
+ return
+ ensure
+ ScratchPad << "within ensure"
+ end
+
+ ScratchPad << "after begin"
+ END_OF_CODE
+
+ load @filename
+ ScratchPad.recorded.should == ["before begin", "within ensure"]
+ end
+ end
+
+ it "swallows exception if returns in ensure block" do
+ File.write(@filename, <<-END_OF_CODE)
+ begin
+ raise
+ ensure
+ ScratchPad << "before return"
+ return
+ end
+ END_OF_CODE
+
+ load @filename
+ ScratchPad.recorded.should == ["before return"]
+ end
+ end
+
+ describe "within a block" do
+ it "is allowed" do
+ File.write(@filename, <<-END_OF_CODE)
+ ScratchPad << "before call"
+ proc { return }.call
+
+ ScratchPad << "after call"
+ END_OF_CODE
+
+ load @filename
+ ScratchPad.recorded.should == ["before call"]
+ end
+ end
+
+ describe "within a class" do
+ ruby_version_is ""..."2.5" do
+ it "is allowed" do
+ File.write(@filename, <<-END_OF_CODE)
+ class A
+ ScratchPad << "before return"
+ return
+
+ ScratchPad << "after return"
+ end
+ END_OF_CODE
+
+ load @filename
+ ScratchPad.recorded.should == ["before return"]
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "raises a SyntaxError" do
+ File.write(@filename, <<-END_OF_CODE)
+ class A
+ ScratchPad << "before return"
+ return
+
+ ScratchPad << "after return"
+ end
+ END_OF_CODE
+
+ -> { load @filename }.should raise_error(SyntaxError)
+ end
+ end
+ end
+
+ describe "file loading" do
+ it "stops file loading and execution" do
+ File.write(@filename, <<-END_OF_CODE)
+ ScratchPad << "before return"
+ return
+ ScratchPad << "after return"
+ END_OF_CODE
+
+ load @filename
+ ScratchPad.recorded.should == ["before return"]
+ end
+ end
+
+ describe "file requiring" do
+ it "stops file loading and execution" do
+ File.write(@filename, <<-END_OF_CODE)
+ ScratchPad << "before return"
+ return
+ ScratchPad << "after return"
+ END_OF_CODE
+
+ require @filename
+ ScratchPad.recorded.should == ["before return"]
+ end
+ end
+
+ describe "return with argument" do
+ # https://bugs.ruby-lang.org/issues/14062
+ it "does not affect exit status" do
+ ruby_exe(<<-END_OF_CODE).should == ""
+ return 10
+ END_OF_CODE
+
+ $?.exitstatus.should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/language/safe_navigator_spec.rb b/spec/ruby/language/safe_navigator_spec.rb
new file mode 100644
index 0000000000..46a1a012d1
--- /dev/null
+++ b/spec/ruby/language/safe_navigator_spec.rb
@@ -0,0 +1,99 @@
+require_relative '../spec_helper'
+
+describe "Safe navigator" do
+ it "requires a method name to be provided" do
+ lambda { eval("obj&. {}") }.should raise_error(SyntaxError)
+ end
+
+ context "when context is nil" do
+ it "always returns nil" do
+ eval("nil&.unknown").should == nil
+ eval("[][10]&.unknown").should == nil
+ end
+
+ it "can be chained" do
+ eval("nil&.one&.two&.three").should == nil
+ end
+
+ it "doesn't evaluate arguments" do
+ obj = Object.new
+ obj.should_not_receive(:m)
+ eval("nil&.unknown(obj.m) { obj.m }")
+ end
+ end
+
+ context "when context is false" do
+ it "calls the method" do
+ eval("false&.to_s").should == "false"
+
+ lambda { eval("false&.unknown") }.should raise_error(NoMethodError)
+ end
+ end
+
+ context "when context is truthy" do
+ it "calls the method" do
+ eval("1&.to_s").should == "1"
+
+ lambda { eval("1&.unknown") }.should raise_error(NoMethodError)
+ end
+ end
+
+ it "takes a list of arguments" do
+ eval("[1,2,3]&.first(2)").should == [1,2]
+ end
+
+ it "takes a block" do
+ eval("[1,2]&.map { |i| i * 2 }").should == [2, 4]
+ end
+
+ it "allows assignment methods" do
+ klass = Class.new do
+ attr_reader :foo
+ def foo=(val)
+ @foo = val
+ 42
+ end
+ end
+ obj = klass.new
+
+ eval("obj&.foo = 3").should == 3
+ obj.foo.should == 3
+
+ obj = nil
+ eval("obj&.foo = 3").should == nil
+ end
+
+ it "allows assignment operators" do
+ klass = Class.new do
+ attr_accessor :m
+
+ def initialize
+ @m = 0
+ end
+ end
+
+ obj = klass.new
+
+ eval("obj&.m += 3")
+ obj.m.should == 3
+
+ obj = nil
+ eval("obj&.m += 3").should == nil
+ end
+
+ it "does not call the operator method lazily with an assignment operator" do
+ klass = Class.new do
+ attr_writer :foo
+ def foo
+ nil
+ end
+ end
+ obj = klass.new
+
+ lambda {
+ eval("obj&.foo += 3")
+ }.should raise_error(NoMethodError) { |e|
+ e.name.should == :+
+ }
+ end
+end
diff --git a/spec/ruby/language/safe_spec.rb b/spec/ruby/language/safe_spec.rb
new file mode 100644
index 0000000000..305965c1a7
--- /dev/null
+++ b/spec/ruby/language/safe_spec.rb
@@ -0,0 +1,97 @@
+require_relative '../spec_helper'
+
+describe "The $SAFE variable" do
+
+ ruby_version_is "2.6" do
+ after :each do
+ $SAFE = 0
+ end
+ end
+
+ it "is 0 by default" do
+ $SAFE.should == 0
+ proc {
+ $SAFE.should == 0
+ }.call
+ end
+
+ it "can be set to 0" do
+ proc {
+ $SAFE = 0
+ $SAFE.should == 0
+ }.call
+ end
+
+ it "can be set to 1" do
+ proc {
+ $SAFE = 1
+ $SAFE.should == 1
+ }.call
+ end
+
+ [2, 3, 4].each do |n|
+ it "cannot be set to #{n}" do
+ lambda {
+ proc {
+ $SAFE = n
+ }.call
+ }.should raise_error(ArgumentError, /\$SAFE=2 to 4 are obsolete/)
+ end
+ end
+
+ ruby_version_is ""..."2.6" do
+ it "cannot be set to values below 0" do
+ lambda {
+ proc {
+ $SAFE = -100
+ }.call
+ }.should raise_error(SecurityError, /tried to downgrade safe level from 0 to -100/)
+ end
+ end
+
+ it "cannot be set to values above 4" do
+ lambda {
+ proc {
+ $SAFE = 100
+ }.call
+ }.should raise_error(ArgumentError, /\$SAFE=2 to 4 are obsolete/)
+ end
+
+ ruby_version_is ""..."2.6" do
+ it "cannot be manually lowered" do
+ proc {
+ $SAFE = 1
+ lambda {
+ $SAFE = 0
+ }.should raise_error(SecurityError, /tried to downgrade safe level from 1 to 0/)
+ }.call
+ end
+
+ it "is automatically lowered when leaving a proc" do
+ $SAFE.should == 0
+ proc {
+ $SAFE = 1
+ }.call
+ $SAFE.should == 0
+ end
+
+ it "is automatically lowered when leaving a lambda" do
+ $SAFE.should == 0
+ lambda {
+ $SAFE = 1
+ }.call
+ $SAFE.should == 0
+ end
+ end
+
+ it "can be read when default from Thread#safe_level" do
+ Thread.current.safe_level.should == 0
+ end
+
+ it "can be read when modified from Thread#safe_level" do
+ proc {
+ $SAFE = 1
+ Thread.current.safe_level.should == 1
+ }.call
+ end
+end
diff --git a/spec/ruby/language/send_spec.rb b/spec/ruby/language/send_spec.rb
new file mode 100644
index 0000000000..8bef80173c
--- /dev/null
+++ b/spec/ruby/language/send_spec.rb
@@ -0,0 +1,552 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/send'
+
+# Why so many fixed arg tests? JRuby and I assume other Ruby impls have
+# separate call paths for simple fixed arity methods. Testing up to five
+# will verify special and generic arity code paths for all impls.
+#
+# Method naming conventions:
+# M - Manditory Args
+# O - Optional Arg
+# R - Rest Arg
+# Q - Post Manditory Args
+
+specs = LangSendSpecs
+
+describe "Invoking a method" do
+ describe "with zero arguments" do
+ it "requires no arguments passed" do
+ specs.fooM0.should == 100
+ end
+
+ it "raises ArgumentError if the method has a positive arity" do
+ lambda {
+ specs.fooM1
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "with only mandatory arguments" do
+ it "requires exactly the same number of passed values" do
+ specs.fooM1(1).should == [1]
+ specs.fooM2(1,2).should == [1,2]
+ specs.fooM3(1,2,3).should == [1,2,3]
+ specs.fooM4(1,2,3,4).should == [1,2,3,4]
+ specs.fooM5(1,2,3,4,5).should == [1,2,3,4,5]
+ end
+
+ it "raises ArgumentError if the methods arity doesn't match" do
+ lambda {
+ specs.fooM1(1,2)
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "with optional arguments" do
+ it "uses the optional argument if none is is passed" do
+ specs.fooM0O1.should == [1]
+ end
+
+ it "uses the passed argument if available" do
+ specs.fooM0O1(2).should == [2]
+ end
+
+ it "raises ArgumentError if extra arguments are passed" do
+ lambda {
+ specs.fooM0O1(2,3)
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "with mandatory and optional arguments" do
+ it "uses the passed values in left to right order" do
+ specs.fooM1O1(2).should == [2,1]
+ end
+
+ it "raises an ArgumentError if there are no values for the mandatory args" do
+ lambda {
+ specs.fooM1O1
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if too many values are passed" do
+ lambda {
+ specs.fooM1O1(1,2,3)
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "with a rest argument" do
+ it "is an empty array if there are no additional arguments" do
+ specs.fooM0R().should == []
+ specs.fooM1R(1).should == [1, []]
+ end
+
+ it "gathers unused arguments" do
+ specs.fooM0R(1).should == [1]
+ specs.fooM1R(1,2).should == [1, [2]]
+ end
+ end
+
+ it "with a block makes it available to yield" do
+ specs.oneb(10) { 200 }.should == [10,200]
+ end
+
+ it "with a block converts the block to a Proc" do
+ prc = specs.makeproc { "hello" }
+ prc.should be_kind_of(Proc)
+ prc.call.should == "hello"
+ end
+
+ it "with an object as a block uses 'to_proc' for coercion" do
+ o = LangSendSpecs::ToProc.new(:from_to_proc)
+
+ specs.makeproc(&o).call.should == :from_to_proc
+
+ specs.yield_now(&o).should == :from_to_proc
+ end
+
+ it "raises a SyntaxError with both a literal block and an object as block" do
+ lambda {
+ eval "specs.oneb(10, &l){ 42 }"
+ }.should raise_error(SyntaxError)
+ end
+
+ it "with same names as existing variables is ok" do
+ foobar = 100
+
+ def foobar; 200; end
+
+ foobar.should == 100
+ foobar().should == 200
+ end
+
+ it "with splat operator makes the object the direct arguments" do
+ a = [1,2,3]
+ specs.fooM3(*a).should == [1,2,3]
+ end
+
+ it "without parentheses works" do
+ (specs.fooM3 1,2,3).should == [1,2,3]
+ end
+
+ it "with a space separating method name and parenthesis treats expression in parenthesis as first argument" do
+ specs.weird_parens().should == "55"
+ end
+
+ describe "allows []=" do
+ before :each do
+ @obj = LangSendSpecs::AttrSet.new
+ end
+
+ it "with *args in the [] expanded to individual arguments" do
+ ary = [2,3]
+ (@obj[1, *ary] = 4).should == 4
+ @obj.result.should == [1,2,3,4]
+ end
+
+ it "with multiple *args" do
+ ary = [2,3]
+ post = [4,5]
+ (@obj[1, *ary] = *post).should == [4,5]
+ @obj.result.should == [1,2,3,[4,5]]
+ end
+
+ it "with multiple *args and does not unwrap the last splat" do
+ ary = [2,3]
+ post = [4]
+ (@obj[1, *ary] = *post).should == [4]
+ @obj.result.should == [1,2,3,[4]]
+ end
+
+ it "with a *args and multiple rhs args" do
+ ary = [2,3]
+ (@obj[1, *ary] = 4, 5).should == [4,5]
+ @obj.result.should == [1,2,3,[4,5]]
+ end
+ end
+
+ it "passes literal hashes without curly braces as the last parameter" do
+ specs.fooM3('abc', 456, 'rbx' => 'cool',
+ 'specs' => 'fail sometimes', 'oh' => 'weh').should == \
+ ['abc', 456, {'rbx' => 'cool', 'specs' => 'fail sometimes', 'oh' => 'weh'}]
+ end
+
+ it "passes a literal hash without curly braces or parens" do
+ (specs.fooM3 'abc', 456, 'rbx' => 'cool',
+ 'specs' => 'fail sometimes', 'oh' => 'weh').should == \
+ ['abc', 456, { 'rbx' => 'cool', 'specs' => 'fail sometimes', 'oh' => 'weh'}]
+ end
+
+ it "allows to literal hashes without curly braces as the only parameter" do
+ specs.fooM1(rbx: :cool, specs: :fail_sometimes).should ==
+ [{ rbx: :cool, specs: :fail_sometimes }]
+
+ (specs.fooM1 rbx: :cool, specs: :fail_sometimes).should ==
+ [{ rbx: :cool, specs: :fail_sometimes }]
+ end
+
+ describe "when the method is not available" do
+ it "invokes method_missing if it is defined" do
+ o = LangSendSpecs::MethodMissing.new
+ o.not_there(1,2)
+ o.message.should == :not_there
+ o.args.should == [1,2]
+ end
+
+ it "raises NameError if invoked as a vcall" do
+ lambda { no_such_method }.should raise_error NameError
+ end
+
+ it "should omit the method_missing call from the backtrace for NameError" do
+ lambda { no_such_method }.should raise_error { |e| e.backtrace.first.should_not include("method_missing") }
+ end
+
+ it "raises NoMethodError if invoked as an unambiguous method call" do
+ lambda { no_such_method() }.should raise_error NoMethodError
+ lambda { no_such_method(1,2,3) }.should raise_error NoMethodError
+ end
+
+ it "should omit the method_missing call from the backtrace for NoMethodError" do
+ lambda { no_such_method() }.should raise_error { |e| e.backtrace.first.should_not include("method_missing") }
+ end
+ end
+
+end
+
+describe "Invoking a public setter method" do
+ it 'returns the set value' do
+ klass = Class.new do
+ def foobar=(*)
+ 1
+ end
+ end
+
+ (klass.new.foobar = 'bar').should == 'bar'
+ (klass.new.foobar = 'bar', 'baz').should == ["bar", "baz"]
+ end
+end
+
+describe "Invoking []= methods" do
+ it 'returns the set value' do
+ klass = Class.new do
+ def []=(*)
+ 1
+ end
+ end
+
+ (klass.new[33] = 'bar').should == 'bar'
+ (klass.new[33] = 'bar', 'baz').should == ['bar', 'baz']
+ (klass.new[33, 34] = 'bar', 'baz').should == ['bar', 'baz']
+ end
+end
+
+describe "Invoking a private setter method" do
+ describe "permits self as a receiver" do
+ it "for normal assignment" do
+ receiver = LangSendSpecs::PrivateSetter.new
+ receiver.call_self_foo_equals(42)
+ receiver.foo.should == 42
+ end
+
+ it "for multiple assignment" do
+ receiver = LangSendSpecs::PrivateSetter.new
+ receiver.call_self_foo_equals_masgn(42)
+ receiver.foo.should == 42
+ end
+ end
+end
+
+describe "Invoking a private getter method" do
+ it "does not permit self as a receiver" do
+ receiver = LangSendSpecs::PrivateGetter.new
+ lambda { receiver.call_self_foo }.should raise_error(NoMethodError)
+ lambda { receiver.call_self_foo_or_equals(6) }.should raise_error(NoMethodError)
+ end
+end
+
+describe "Invoking a method" do
+ describe "with required args after the rest arguments" do
+ it "binds the required arguments first" do
+ specs.fooM0RQ1(1).should == [[], 1]
+ specs.fooM0RQ1(1,2).should == [[1], 2]
+ specs.fooM0RQ1(1,2,3).should == [[1,2], 3]
+
+ specs.fooM1RQ1(1,2).should == [1, [], 2]
+ specs.fooM1RQ1(1,2,3).should == [1, [2], 3]
+ specs.fooM1RQ1(1,2,3,4).should == [1, [2, 3], 4]
+
+ specs.fooM1O1RQ1(1,2).should == [1, 9, [], 2]
+ specs.fooM1O1RQ1(1,2,3).should == [1, 2, [], 3]
+ specs.fooM1O1RQ1(1,2,3,4).should == [1, 2, [3], 4]
+
+ specs.fooM1O1RQ2(1,2,3).should == [1, 9, [], 2, 3]
+ specs.fooM1O1RQ2(1,2,3,4).should == [1, 2, [], 3, 4]
+ specs.fooM1O1RQ2(1,2,3,4,5).should == [1, 2, [3], 4, 5]
+ end
+ end
+
+ describe "with mandatory arguments after optional arguments" do
+ it "binds the required arguments first" do
+ specs.fooO1Q1(0,1).should == [0,1]
+ specs.fooO1Q1(2).should == [1,2]
+
+ specs.fooM1O1Q1(2,3,4).should == [2,3,4]
+ specs.fooM1O1Q1(1,3).should == [1,2,3]
+
+ specs.fooM2O1Q1(1,2,4).should == [1,2,3,4]
+
+ specs.fooM2O2Q1(1,2,3,4,5).should == [1,2,3,4,5]
+ specs.fooM2O2Q1(1,2,3,5).should == [1,2,3,4,5]
+ specs.fooM2O2Q1(1,2,5).should == [1,2,3,4,5]
+
+ specs.fooO4Q1(1,2,3,4,5).should == [1,2,3,4,5]
+ specs.fooO4Q1(1,2,3,5).should == [1,2,3,4,5]
+ specs.fooO4Q1(1,2,5).should == [1,2,3,4,5]
+ specs.fooO4Q1(1,5).should == [1,2,3,4,5]
+ specs.fooO4Q1(5).should == [1,2,3,4,5]
+
+ specs.fooO4Q2(1,2,3,4,5,6).should == [1,2,3,4,5,6]
+ specs.fooO4Q2(1,2,3,5,6).should == [1,2,3,4,5,6]
+ specs.fooO4Q2(1,2,5,6).should == [1,2,3,4,5,6]
+ specs.fooO4Q2(1,5,6).should == [1,2,3,4,5,6]
+ specs.fooO4Q2(5,6).should == [1,2,3,4,5,6]
+ end
+ end
+
+ it "with .() invokes #call" do
+ q = proc { |z| z }
+ q.(1).should == 1
+
+ obj = mock("paren call")
+ obj.should_receive(:call).and_return(:called)
+ obj.().should == :called
+ end
+
+ it "allows a vestigial trailing ',' in the arguments" do
+ specs.fooM1(1,).should == [1]
+ end
+
+ it "with splat operator attempts to coerce it to an Array if the object respond_to?(:to_a)" do
+ ary = [2,3,4]
+ obj = mock("to_a")
+ obj.should_receive(:to_a).and_return(ary).twice
+ specs.fooM0R(*obj).should == ary
+ specs.fooM1R(1,*obj).should == [1, ary]
+ end
+
+ it "with splat operator * and non-Array value uses value unchanged if it does not respond_to?(:to_ary)" do
+ obj = Object.new
+ obj.should_not respond_to(:to_a)
+
+ specs.fooM0R(*obj).should == [obj]
+ specs.fooM1R(1,*obj).should == [1, [obj]]
+ end
+
+ it "accepts additional arguments after splat expansion" do
+ a = [1,2]
+ specs.fooM4(*a,3,4).should == [1,2,3,4]
+ specs.fooM4(0,*a,3).should == [0,1,2,3]
+ end
+
+ it "does not expand final array arguments after a splat expansion" do
+ a = [1, 2]
+ specs.fooM3(*a, [3, 4]).should == [1, 2, [3, 4]]
+ end
+
+ it "accepts final explicit literal Hash arguments after the splat" do
+ a = [1, 2]
+ specs.fooM0RQ1(*a, { a: 1 }).should == [[1, 2], { a: 1 }]
+ end
+
+ it "accepts final implicit literal Hash arguments after the splat" do
+ a = [1, 2]
+ specs.fooM0RQ1(*a, a: 1).should == [[1, 2], { a: 1 }]
+ end
+
+ it "accepts final Hash arguments after the splat" do
+ a = [1, 2]
+ b = { a: 1 }
+ specs.fooM0RQ1(*a, b).should == [[1, 2], { a: 1 }]
+ end
+
+ it "accepts mandatory and explicit literal Hash arguments after the splat" do
+ a = [1, 2]
+ specs.fooM0RQ2(*a, 3, { a: 1 }).should == [[1, 2], 3, { a: 1 }]
+ end
+
+ it "accepts mandatory and implicit literal Hash arguments after the splat" do
+ a = [1, 2]
+ specs.fooM0RQ2(*a, 3, a: 1).should == [[1, 2], 3, { a: 1 }]
+ end
+
+ it "accepts mandatory and Hash arguments after the splat" do
+ a = [1, 2]
+ b = { a: 1 }
+ specs.fooM0RQ2(*a, 3, b).should == [[1, 2], 3, { a: 1 }]
+ end
+
+ it "converts a final splatted explicit Hash to an Array" do
+ a = [1, 2]
+ specs.fooR(*a, 3, *{ a: 1 }).should == [1, 2, 3, [:a, 1]]
+ end
+
+ it "calls #to_a to convert a final splatted Hash object to an Array" do
+ a = [1, 2]
+ b = { a: 1 }
+ b.should_receive(:to_a).and_return([:a, 1])
+
+ specs.fooR(*a, 3, *b).should == [1, 2, 3, :a, 1]
+ end
+
+ it "accepts multiple splat expansions in the same argument list" do
+ a = [1,2,3]
+ b = 7
+ c = mock("pseudo-array")
+ c.should_receive(:to_a).and_return([0,0])
+
+ d = [4,5]
+ specs.rest_len(*a,*d,6,*b).should == 7
+ specs.rest_len(*a,*a,*a).should == 9
+ specs.rest_len(0,*a,4,*5,6,7,*c,-1).should == 11
+ end
+
+ it "expands the Array elements from the splat after executing the arguments and block if no other arguments follow the splat" do
+ def self.m(*args, &block)
+ [args, block]
+ end
+
+ args = [1, nil]
+ m(*args, &args.pop).should == [[1], nil]
+
+ args = [1, nil]
+ order = []
+ m(*(order << :args; args), &(order << :block; args.pop)).should == [[1], nil]
+ order.should == [:args, :block]
+ end
+
+ it "evaluates the splatted arguments before the block if there are other arguments after the splat" do
+ def self.m(*args, &block)
+ [args, block]
+ end
+
+ args = [1, nil]
+ m(*args, 2, &args.pop).should == [[1, nil, 2], nil]
+ end
+
+ it "expands an array to arguments grouped in parentheses" do
+ specs.destructure2([40,2]).should == 42
+ end
+
+ it "expands an array to arguments grouped in parentheses and ignores any rest arguments in the array" do
+ specs.destructure2([40,2,84]).should == 42
+ end
+
+ it "expands an array to arguments grouped in parentheses and sets not specified arguments to nil" do
+ specs.destructure2b([42]).should == [42, nil]
+ end
+
+ it "expands an array to arguments grouped in parentheses which in turn takes rest arguments" do
+ specs.destructure4r([1, 2, 3]).should == [1, 2, [], 3, nil]
+ specs.destructure4r([1, 2, 3, 4]).should == [1, 2, [], 3, 4]
+ specs.destructure4r([1, 2, 3, 4, 5]).should == [1, 2, [3], 4, 5]
+ end
+
+ it "with optional argument(s), expands an array to arguments grouped in parentheses" do
+ specs.destructure4o(1, [2, 3]).should == [1, 1, nil, [2, 3]]
+ specs.destructure4o(1, [], 2).should == [1, nil, nil, 2]
+ specs.destructure4os(1, [2, 3]).should == [1, 2, [3]]
+ specs.destructure5o(1, [2, 3]).should == [1, 2, 1, nil, [2, 3]]
+ specs.destructure7o(1, [2, 3]).should == [1, 2, 1, nil, 2, 3]
+ specs.destructure7b(1, [2, 3]) do |(a,*b,c)|
+ [a, c]
+ end.should == [1, 3]
+ end
+
+ describe "new-style hash arguments" do
+ describe "as the only parameter" do
+ it "passes without curly braces" do
+ specs.fooM1(rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should ==
+ [{ rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }]
+ end
+
+ it "passes without curly braces or parens" do
+ (specs.fooM1 rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should ==
+ [{ rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }]
+ end
+
+ it "handles a hanging comma without curly braces" do
+ specs.fooM1(abc: 123,).should == [{abc: 123}]
+ specs.fooM1(rbx: 'cool', specs: :fail_sometimes, non_sym: 1234,).should ==
+ [{ rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }]
+ end
+ end
+
+ describe "as the last parameter" do
+ it "passes without curly braces" do
+ specs.fooM3('abc', 123, rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should ==
+ ['abc', 123, { rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }]
+ end
+
+ it "passes without curly braces or parens" do
+ (specs.fooM3 'abc', 123, rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should ==
+ ['abc', 123, { rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }]
+ end
+
+ it "handles a hanging comma without curly braces" do
+ specs.fooM3('abc', 123, abc: 123,).should == ['abc', 123, {abc: 123}]
+ specs.fooM3('abc', 123, rbx: 'cool', specs: :fail_sometimes, non_sym: 1234,).should ==
+ ['abc', 123, { rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }]
+ end
+ end
+ end
+
+ describe "mixed new- and old-style hash arguments" do
+ describe "as the only parameter" do
+ it "passes without curly braces" do
+ specs.fooM1(rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should ==
+ [{ rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }]
+ end
+
+ it "passes without curly braces or parens" do
+ (specs.fooM1 rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should ==
+ [{ rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }]
+ end
+
+ it "handles a hanging comma without curly braces" do
+ specs.fooM1(rbx: 'cool', specs: :fail_sometimes, non_sym: 1234,).should ==
+ [{ rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }]
+ end
+ end
+
+ describe "as the last parameter" do
+ it "passes without curly braces" do
+ specs.fooM3('abc', 123, rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should ==
+ ['abc', 123, { rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }]
+ end
+
+ it "passes without curly braces or parens" do
+ (specs.fooM3 'abc', 123, rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should ==
+ ['abc', 123, { rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }]
+ end
+
+ it "handles a hanging comma without curly braces" do
+ specs.fooM3('abc', 123, rbx: 'cool', specs: :fail_sometimes, non_sym: 1234,).should ==
+ ['abc', 123, { rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }]
+ end
+ end
+ end
+
+end
+
+describe "allows []= with arguments after splat" do
+ before :each do
+ @obj = LangSendSpecs::Attr19Set.new
+ @ary = ["a"]
+ end
+
+ it "with *args in the [] and post args" do
+ @obj[1,*@ary,123] = 2
+ @obj.result.should == [1, "a", 123, 2]
+ end
+end
diff --git a/spec/ruby/language/shared/__FILE__.rb b/spec/ruby/language/shared/__FILE__.rb
new file mode 100644
index 0000000000..3e4f5c958d
--- /dev/null
+++ b/spec/ruby/language/shared/__FILE__.rb
@@ -0,0 +1,23 @@
+describe :language___FILE__, shared: true do
+ before :each do
+ CodeLoadingSpecs.spec_setup
+ @path = File.join(CODE_LOADING_DIR, "file_fixture.rb")
+ end
+
+ after :each do
+ CodeLoadingSpecs.spec_cleanup
+ end
+
+ it "equals the absolute path of a file loaded by an absolute path" do
+ @object.send(@method, @path).should be_true
+ ScratchPad.recorded.should == [@path]
+ end
+
+ it "equals the absolute path of a file loaded by a relative path" do
+ $LOAD_PATH << "."
+ Dir.chdir CODE_LOADING_DIR do
+ @object.send(@method, "file_fixture.rb").should be_true
+ end
+ ScratchPad.recorded.should == [@path]
+ end
+end
diff --git a/spec/ruby/language/shared/__LINE__.rb b/spec/ruby/language/shared/__LINE__.rb
new file mode 100644
index 0000000000..076b74b3ba
--- /dev/null
+++ b/spec/ruby/language/shared/__LINE__.rb
@@ -0,0 +1,15 @@
+describe :language___LINE__, shared: true do
+ before :each do
+ CodeLoadingSpecs.spec_setup
+ @path = File.expand_path("line_fixture.rb", CODE_LOADING_DIR)
+ end
+
+ after :each do
+ CodeLoadingSpecs.spec_cleanup
+ end
+
+ it "equals the line number of the text in a loaded file" do
+ @object.send(@method, @path).should be_true
+ ScratchPad.recorded.should == [1, 5]
+ end
+end
diff --git a/spec/ruby/language/singleton_class_spec.rb b/spec/ruby/language/singleton_class_spec.rb
new file mode 100644
index 0000000000..e3a0d2870b
--- /dev/null
+++ b/spec/ruby/language/singleton_class_spec.rb
@@ -0,0 +1,293 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/class'
+
+describe "A singleton class" do
+ it "is TrueClass for true" do
+ true.singleton_class.should == TrueClass
+ end
+
+ it "is FalseClass for false" do
+ false.singleton_class.should == FalseClass
+ end
+
+ it "is NilClass for nil" do
+ nil.singleton_class.should == NilClass
+ end
+
+ it "raises a TypeError for Fixnum's" do
+ lambda { 1.singleton_class }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError for symbols" do
+ lambda { :symbol.singleton_class }.should raise_error(TypeError)
+ end
+
+ it "is a singleton Class instance" do
+ o = mock('x')
+ o.singleton_class.should be_kind_of(Class)
+ o.singleton_class.should_not equal(Object)
+ o.should be_kind_of(o.singleton_class)
+ end
+
+ it "is a Class for classes" do
+ ClassSpecs::A.singleton_class.should be_kind_of(Class)
+ end
+
+ it "inherits from Class for classes" do
+ Class.should be_ancestor_of(Object.singleton_class)
+ end
+
+ it "is a subclass of Class's singleton class" do
+ ec = ClassSpecs::A.singleton_class
+ ec.should be_kind_of(Class.singleton_class)
+ end
+
+ it "is a subclass of the same level of Class's singleton class" do
+ ecec = ClassSpecs::A.singleton_class.singleton_class
+ class_ec = Class.singleton_class
+
+ ecec.should be_kind_of(class_ec.singleton_class)
+ ecec.should be_kind_of(class_ec)
+ end
+
+ it "is a subclass of a superclass's singleton class" do
+ ClassSpecs::K.singleton_class.superclass.should ==
+ ClassSpecs::H.singleton_class
+ end
+
+ it "is a subclass of the same level of superclass's singleton class" do
+ ClassSpecs::K.singleton_class.singleton_class.superclass.should ==
+ ClassSpecs::H.singleton_class.singleton_class
+ end
+
+ it "for BasicObject has Class as it's superclass" do
+ BasicObject.singleton_class.superclass.should == Class
+ end
+
+ it "for BasicObject has the proper level of superclass for Class" do
+ BasicObject.singleton_class.singleton_class.superclass.should ==
+ Class.singleton_class
+ end
+
+ it "has class String as the superclass of a String instance" do
+ "blah".singleton_class.superclass.should == String
+ end
+
+ it "doesn't have singleton class" do
+ lambda { bignum_value.singleton_class.superclass.should == Bignum }.should raise_error(TypeError)
+ end
+end
+
+describe "A constant on a singleton class" do
+ before :each do
+ @object = Object.new
+ class << @object
+ CONST = self
+ end
+ end
+
+ it "can be accessed after the singleton class body is reopened" do
+ class << @object
+ CONST.should == self
+ end
+ end
+
+ it "can be accessed via self::CONST" do
+ class << @object
+ self::CONST.should == self
+ end
+ end
+
+ it "can be accessed via const_get" do
+ class << @object
+ const_get(:CONST).should == self
+ end
+ end
+
+ it "is not defined on the object's class" do
+ @object.class.const_defined?(:CONST).should be_false
+ end
+
+ it "is not defined in the singleton class opener's scope" do
+ class << @object
+ CONST
+ end
+ lambda { CONST }.should raise_error(NameError)
+ end
+
+ it "cannot be accessed via object::CONST" do
+ lambda do
+ @object::CONST
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a NameError for anonymous_module::CONST" do
+ @object = Class.new
+ class << @object
+ CONST = 100
+ end
+
+ lambda do
+ @object::CONST
+ end.should raise_error(NameError)
+ end
+
+ it "appears in the singleton class constant list" do
+ @object.singleton_class.should have_constant(:CONST)
+ end
+
+ it "does not appear in the object's class constant list" do
+ @object.class.should_not have_constant(:CONST)
+ end
+
+ it "is not preserved when the object is duped" do
+ @object = @object.dup
+
+ lambda do
+ class << @object; CONST; end
+ end.should raise_error(NameError)
+ end
+
+ it "is preserved when the object is cloned" do
+ @object = @object.clone
+
+ class << @object
+ CONST.should_not be_nil
+ end
+ end
+end
+
+describe "Defining instance methods on a singleton class" do
+ before :each do
+ @k = ClassSpecs::K.new
+ class << @k
+ def singleton_method; 1 end
+ end
+
+ @k_sc = @k.singleton_class
+ end
+
+ it "defines public methods" do
+ @k_sc.should have_public_instance_method(:singleton_method)
+ end
+end
+
+describe "Instance methods of a singleton class" do
+ before :each do
+ k = ClassSpecs::K.new
+ @k_sc = k.singleton_class
+ @a_sc = ClassSpecs::A.new.singleton_class
+ @a_c_sc = ClassSpecs::A.singleton_class
+ end
+
+ it "include ones of the object's class" do
+ @k_sc.should have_instance_method(:example_instance_method)
+ end
+
+ it "does not include class methods of the object's class" do
+ @k_sc.should_not have_instance_method(:example_class_method)
+ end
+
+ it "include instance methods of Object" do
+ @a_sc.should have_instance_method(:example_instance_method_of_object)
+ end
+
+ it "does not include class methods of Object" do
+ @a_sc.should_not have_instance_method(:example_class_method_of_object)
+ end
+
+ describe "for a class" do
+ it "include instance methods of Class" do
+ @a_c_sc.should have_instance_method(:example_instance_method_of_class)
+ end
+
+ it "does not include class methods of Class" do
+ @a_c_sc.should_not have_instance_method(:example_class_method_of_class)
+ end
+
+ it "does not include instance methods of the singleton class of Class" do
+ @a_c_sc.should_not have_instance_method(:example_instance_method_of_singleton_class)
+ end
+
+ it "does not include class methods of the singleton class of Class" do
+ @a_c_sc.should_not have_instance_method(:example_class_method_of_singleton_class)
+ end
+ end
+
+ describe "for a singleton class" do
+ it "includes instance methods of the singleton class of Class" do
+ @a_c_sc.singleton_class.should have_instance_method(:example_instance_method_of_singleton_class)
+ end
+
+ it "does not include class methods of the singleton class of Class" do
+ @a_c_sc.singleton_class.should_not have_instance_method(:example_class_method_of_singleton_class)
+ end
+ end
+end
+
+describe "Class methods of a singleton class" do
+ before :each do
+ k = ClassSpecs::K.new
+ @k_sc = k.singleton_class
+ @a_sc = ClassSpecs::A.new.singleton_class
+ @a_c_sc = ClassSpecs::A.singleton_class
+ end
+
+ it "include ones of the object's class" do
+ @k_sc.should have_method(:example_class_method)
+ end
+
+ it "does not include instance methods of the object's class" do
+ @k_sc.should_not have_method(:example_instance_method)
+ end
+
+ it "include instance methods of Class" do
+ @a_sc.should have_method(:example_instance_method_of_class)
+ end
+
+ it "does not include class methods of Class" do
+ @a_sc.should_not have_method(:example_class_method_of_class)
+ end
+
+ describe "for a class" do
+ it "include instance methods of Class" do
+ @a_c_sc.should have_method(:example_instance_method_of_class)
+ end
+
+ it "include class methods of Class" do
+ @a_c_sc.should have_method(:example_class_method_of_class)
+ end
+
+ it "include instance methods of the singleton class of Class" do
+ @a_c_sc.should have_method(:example_instance_method_of_singleton_class)
+ end
+
+ it "does not include class methods of the singleton class of Class" do
+ @a_c_sc.should_not have_method(:example_class_method_of_singleton_class)
+ end
+ end
+
+ describe "for a singleton class" do
+ it "include instance methods of the singleton class of Class" do
+ @a_c_sc.singleton_class.should have_method(:example_instance_method_of_singleton_class)
+ end
+
+ it "include class methods of the singleton class of Class" do
+ @a_c_sc.singleton_class.should have_method(:example_class_method_of_singleton_class)
+ end
+ end
+end
+
+describe "Instantiating a singleton class" do
+ it "raises a TypeError when new is called" do
+ lambda {
+ Object.new.singleton_class.new
+ }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when allocate is called" do
+ lambda {
+ Object.new.singleton_class.allocate
+ }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/language/source_encoding_spec.rb b/spec/ruby/language/source_encoding_spec.rb
new file mode 100644
index 0000000000..a0a29f63de
--- /dev/null
+++ b/spec/ruby/language/source_encoding_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../spec_helper'
+
+describe "Source files" do
+
+ describe "encoded in UTF-8 without a BOM" do
+ it "can be parsed" do
+ ruby_exe(fixture(__FILE__, "utf8-nobom.rb"), args: "2>&1").should == "hello\n"
+ end
+ end
+
+ describe "encoded in UTF-8 with a BOM" do
+ it "can be parsed" do
+ ruby_exe(fixture(__FILE__, "utf8-bom.rb"), args: "2>&1").should == "hello\n"
+ end
+ end
+
+ describe "encoded in UTF-16 LE without a BOM" do
+ it "are parsed because empty as they contain a NUL byte before the encoding comment" do
+ ruby_exe(fixture(__FILE__, "utf16-le-nobom.rb"), args: "2>&1").should == ""
+ end
+ end
+
+ describe "encoded in UTF-16 LE with a BOM" do
+ it "are invalid because they contain an invalid UTF-8 sequence before the encoding comment" do
+ bom = "\xFF\xFE".b
+ source = "# encoding: utf-16le\nputs 'hello'\n"
+ source = bom + source.bytes.zip([0]*source.bytesize).flatten.pack('C*')
+ path = tmp("utf16-le-bom.rb")
+
+ touch(path, "wb") { |f| f.write source }
+ begin
+ ruby_exe(path, args: "2>&1").should =~ /invalid multibyte char/
+ ensure
+ rm_r path
+ end
+ end
+ end
+
+ describe "encoded in UTF-16 BE without a BOM" do
+ it "are parsed as empty because they contain a NUL byte before the encoding comment" do
+ ruby_exe(fixture(__FILE__, "utf16-be-nobom.rb"), args: "2>&1").should == ""
+ end
+ end
+
+ describe "encoded in UTF-16 BE with a BOM" do
+ it "are invalid because they contain an invalid UTF-8 sequence before the encoding comment" do
+ bom = "\xFE\xFF".b
+ source = "# encoding: utf-16be\nputs 'hello'\n"
+ source = bom + ([0]*source.bytesize).zip(source.bytes).flatten.pack('C*')
+ path = tmp("utf16-be-bom.rb")
+
+ touch(path, "wb") { |f| f.write source }
+ begin
+ ruby_exe(path, args: "2>&1").should =~ /invalid multibyte char/
+ ensure
+ rm_r path
+ end
+ end
+ end
+
+end
diff --git a/spec/ruby/language/string_spec.rb b/spec/ruby/language/string_spec.rb
new file mode 100644
index 0000000000..b198979d87
--- /dev/null
+++ b/spec/ruby/language/string_spec.rb
@@ -0,0 +1,289 @@
+# -*- encoding: binary -*-
+
+require_relative '../spec_helper'
+
+# TODO: rewrite these horrid specs. it "are..." seriously?!
+
+describe "Ruby character strings" do
+
+ before :each do
+ @ip = 'xxx' # used for interpolation
+ $ip = 'xxx'
+ end
+
+ it "don't get interpolated when put in single quotes" do
+ '#{@ip}'.should == '#{@ip}'
+ end
+
+ it 'get interpolated with #{} when put in double quotes' do
+ "#{@ip}".should == 'xxx'
+ end
+
+ it "interpolate instance variables just with the # character" do
+ "#@ip".should == 'xxx'
+ end
+
+ it "interpolate global variables just with the # character" do
+ "#$ip".should == 'xxx'
+ end
+
+ it "allows underscore as part of a variable name in a simple interpolation" do
+ @my_ip = 'xxx'
+ "#@my_ip".should == 'xxx'
+ end
+
+ it "has characters [.(=?!# end simple # interpolation" do
+ "#@ip[".should == 'xxx['
+ "#@ip.".should == 'xxx.'
+ "#@ip(".should == 'xxx('
+ "#@ip=".should == 'xxx='
+ "#@ip?".should == 'xxx?'
+ "#@ip!".should == 'xxx!'
+ "#@ip#@ip".should == 'xxxxxx'
+ end
+
+ it "don't get confused by partial interpolation character sequences" do
+ "#@".should == '#@'
+ "#@ ".should == '#@ '
+ "#@@".should == '#@@'
+ "#@@ ".should == '#@@ '
+ "#$ ".should == '#$ '
+ "#\$".should == '#$'
+ end
+
+ it "taints the result of interpolation when an interpolated value is tainted" do
+ "#{"".taint}".tainted?.should be_true
+
+ @ip.taint
+ "#@ip".tainted?.should be_true
+
+ $ip.taint
+ "#$ip".tainted?.should be_true
+ end
+
+ it "untrusts the result of interpolation when an interpolated value is untrusted" do
+ "#{"".untrust}".untrusted?.should be_true
+
+ @ip.untrust
+ "#@ip".untrusted?.should be_true
+
+ $ip.untrust
+ "#$ip".untrusted?.should be_true
+ end
+
+ it "allows using non-alnum characters as string delimiters" do
+ %(hey #{@ip}).should == "hey xxx"
+ %[hey #{@ip}].should == "hey xxx"
+ %{hey #{@ip}}.should == "hey xxx"
+ %<hey #{@ip}>.should == "hey xxx"
+ %!hey #{@ip}!.should == "hey xxx"
+ %@hey #{@ip}@.should == "hey xxx"
+ %#hey hey#.should == "hey hey"
+ %%hey #{@ip}%.should == "hey xxx"
+ %^hey #{@ip}^.should == "hey xxx"
+ %&hey #{@ip}&.should == "hey xxx"
+ %*hey #{@ip}*.should == "hey xxx"
+ %-hey #{@ip}-.should == "hey xxx"
+ %_hey #{@ip}_.should == "hey xxx"
+ %=hey #{@ip}=.should == "hey xxx"
+ %+hey #{@ip}+.should == "hey xxx"
+ %~hey #{@ip}~.should == "hey xxx"
+ %:hey #{@ip}:.should == "hey xxx"
+ %;hey #{@ip};.should == "hey xxx"
+ %"hey #{@ip}".should == "hey xxx"
+ %|hey #{@ip}|.should == "hey xxx"
+ %?hey #{@ip}?.should == "hey xxx"
+ %/hey #{@ip}/.should == "hey xxx"
+ %,hey #{@ip},.should == "hey xxx"
+ %.hey #{@ip}..should == "hey xxx"
+
+ # surprised? huh
+ %'hey #{@ip}'.should == "hey xxx"
+ %\hey #{@ip}\.should == "hey xxx"
+ %`hey #{@ip}`.should == "hey xxx"
+ %$hey #{@ip}$.should == "hey xxx"
+ end
+
+ it "using percent with 'q', stopping interpolation" do
+ %q(#{@ip}).should == '#{@ip}'
+ end
+
+ it "using percent with 'Q' to interpolate" do
+ %Q(#{@ip}).should == 'xxx'
+ end
+
+ # The backslashes :
+ #
+ # \t (tab), \n (newline), \r (carriage return), \f (form feed), \b
+ # (backspace), \a (bell), \e (escape), \s (whitespace), \nnn (octal),
+ # \xnn (hexadecimal), \cx (control x), \C-x (control x), \M-x (meta x),
+ # \M-\C-x (meta control x)
+
+ it "backslashes follow the same rules as interpolation" do
+ "\t\n\r\f\b\a\e\s\075\x62\cx".should == "\t\n\r\f\b\a\e =b\030"
+ '\t\n\r\f\b\a\e =b\030'.should == "\\t\\n\\r\\f\\b\\a\\e =b\\030"
+ end
+
+ it "calls #to_s when the object is not a String" do
+ obj = mock('to_s')
+ obj.stub!(:to_s).and_return('42')
+
+ "#{obj}".should == '42'
+ end
+
+ it "calls #to_s as a private method" do
+ obj = mock('to_s')
+ obj.stub!(:to_s).and_return('42')
+
+ class << obj
+ private :to_s
+ end
+
+ "#{obj}".should == '42'
+ end
+
+ it "uses an internal representation when #to_s doesn't return a String" do
+ obj = mock('to_s')
+ obj.stub!(:to_s).and_return(42)
+
+ # See rubyspec commit 787c132d by yugui. There is value in
+ # ensuring that this behavior works. So rather than removing
+ # this spec completely, the only thing that can be asserted
+ # is that if you interpolate an object that fails to return
+ # a String, you will still get a String and not raise an
+ # exception.
+ "#{obj}".should be_an_instance_of(String)
+ end
+
+ it "allows a dynamic string to parse a nested do...end block as an argument to a call without parens, interpolated" do
+ s = eval 'eval "#{proc do; 1; end.call}"'
+ s.should == 1
+ end
+
+ it "are produced from character shortcuts" do
+ ?z.should == 'z'
+ end
+
+ it "are produced from control character shortcuts" do
+ # Control-Z
+ ?\C-z.should == "\x1A"
+
+ # Meta-Z
+ ?\M-z.should == "\xFA"
+
+ # Meta-Control-Z
+ ?\M-\C-z.should == "\x9A"
+ end
+
+ describe "Unicode escaping" do
+ it "can be done with \\u and four hex digits" do
+ [ ["\u0000", 0x0000],
+ ["\u2020", 0x2020]
+ ].should be_computed_by(:ord)
+ end
+
+ it "can be done with \\u{} and one to six hex digits" do
+ [ ["\u{a}", 0xa],
+ ["\u{ab}", 0xab],
+ ["\u{abc}", 0xabc],
+ ["\u{1abc}", 0x1abc],
+ ["\u{12abc}", 0x12abc],
+ ["\u{100000}", 0x100000]
+ ].should be_computed_by(:ord)
+ end
+
+ # TODO: spec other source encodings
+ describe "with ASCII_8BIT source encoding" do
+ it "produces an ASCII string when escaping ASCII characters via \\u" do
+ "\u0000".encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "produces an ASCII string when escaping ASCII characters via \\u{}" do
+ "\u{0000}".encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "produces a UTF-8-encoded string when escaping non-ASCII characters via \\u" do
+ "\u1234".encoding.should == Encoding::UTF_8
+ end
+
+ it "produces a UTF-8-encoded string when escaping non-ASCII characters via \\u{}" do
+ "\u{1234}".encoding.should == Encoding::UTF_8
+ end
+ end
+ end
+end
+
+# TODO: rewrite all specs above this
+
+describe "Ruby String literals" do
+ def str_concat
+ "foo" "bar" "baz"
+ end
+
+ def long_string_literals
+ "Beautiful is better than ugly." \
+ "Explicit is better than implicit."
+ end
+
+ it "on a single line with spaces in between are concatenated together" do
+ str_concat.should == "foobarbaz"
+ end
+
+ it "on multiple lines with newlines and backslash in between are concatenated together" do
+ long_string_literals.should == "Beautiful is better than ugly.Explicit is better than implicit."
+ end
+
+ describe "with a magic frozen comment" do
+ it "produce the same object each time" do
+ ruby_exe(fixture(__FILE__, "freeze_magic_comment_one_literal.rb")).chomp.should == "true"
+ end
+
+ it "produce the same object for literals with the same content" do
+ ruby_exe(fixture(__FILE__, "freeze_magic_comment_two_literals.rb")).chomp.should == "true"
+ end
+
+ it "produce the same object for literals with the same content in different files" do
+ ruby_exe(fixture(__FILE__, "freeze_magic_comment_across_files.rb")).chomp.should == "true"
+ end
+
+ it "produce different objects for literals with the same content in different files if the other file doesn't have the comment" do
+ ruby_exe(fixture(__FILE__, "freeze_magic_comment_across_files_no_comment.rb")).chomp.should == "true"
+ end
+
+ it "produce different objects for literals with the same content in different files if they have different encodings" do
+ ruby_exe(fixture(__FILE__, "freeze_magic_comment_across_files_diff_enc.rb")).chomp.should == "true"
+ end
+ end
+
+end
+
+with_feature :encoding do
+ describe "Ruby String interpolation" do
+ it "creates a String having an Encoding compatible with all components" do
+ a = "\u3042"
+ b = "abc".encode("ascii-8bit")
+
+ str = "#{a} x #{b}"
+
+ str.should == "\xe3\x81\x82\x20\x78\x20\x61\x62\x63".force_encoding("utf-8")
+ str.encoding.should == Encoding::UTF_8
+ end
+
+ it "creates a String having the Encoding of the components when all are the same Encoding" do
+ a = "abc".force_encoding("euc-jp")
+ b = "def".force_encoding("euc-jp")
+ str = '"#{a} x #{b}"'.force_encoding("euc-jp")
+
+ result = eval(str)
+ result.should == "\x61\x62\x63\x20\x78\x20\x64\x65\x66".force_encoding("euc-jp")
+ result.encoding.should == Encoding::EUC_JP
+ end
+
+ it "raises an Encoding::CompatibilityError if the Encodings are not compatible" do
+ a = "\u3042"
+ b = "\xff".force_encoding "ascii-8bit"
+
+ lambda { "#{a} #{b}" }.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+end
diff --git a/spec/ruby/language/super_spec.rb b/spec/ruby/language/super_spec.rb
new file mode 100644
index 0000000000..c6071f21f0
--- /dev/null
+++ b/spec/ruby/language/super_spec.rb
@@ -0,0 +1,369 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/super'
+
+describe "The super keyword" do
+ it "calls the method on the calling class" do
+ Super::S1::A.new.foo([]).should == ["A#foo","A#bar"]
+ Super::S1::A.new.bar([]).should == ["A#bar"]
+ Super::S1::B.new.foo([]).should == ["B#foo","A#foo","B#bar","A#bar"]
+ Super::S1::B.new.bar([]).should == ["B#bar","A#bar"]
+ end
+
+ it "searches the full inheritance chain" do
+ Super::S2::B.new.foo([]).should == ["B#foo","A#baz"]
+ Super::S2::B.new.baz([]).should == ["A#baz"]
+ Super::S2::C.new.foo([]).should == ["B#foo","C#baz","A#baz"]
+ Super::S2::C.new.baz([]).should == ["C#baz","A#baz"]
+ end
+
+ it "searches class methods" do
+ Super::S3::A.new.foo([]).should == ["A#foo"]
+ Super::S3::A.foo([]).should == ["A.foo"]
+ Super::S3::A.bar([]).should == ["A.bar","A.foo"]
+ Super::S3::B.new.foo([]).should == ["A#foo"]
+ Super::S3::B.foo([]).should == ["B.foo","A.foo"]
+ Super::S3::B.bar([]).should == ["B.bar","A.bar","B.foo","A.foo"]
+ end
+
+ it "calls the method on the calling class including modules" do
+ Super::MS1::A.new.foo([]).should == ["ModA#foo","ModA#bar"]
+ Super::MS1::A.new.bar([]).should == ["ModA#bar"]
+ Super::MS1::B.new.foo([]).should == ["B#foo","ModA#foo","ModB#bar","ModA#bar"]
+ Super::MS1::B.new.bar([]).should == ["ModB#bar","ModA#bar"]
+ end
+
+ it "searches the full inheritance chain including modules" do
+ Super::MS2::B.new.foo([]).should == ["ModB#foo","A#baz"]
+ Super::MS2::B.new.baz([]).should == ["A#baz"]
+ Super::MS2::C.new.baz([]).should == ["C#baz","A#baz"]
+ Super::MS2::C.new.foo([]).should == ["ModB#foo","C#baz","A#baz"]
+ end
+
+ it "can resolve to different methods in an included module method" do
+ Super::MultiSuperTargets::A.new.foo.should == :BaseA
+ Super::MultiSuperTargets::B.new.foo.should == :BaseB
+ end
+
+ it "searches class methods including modules" do
+ Super::MS3::A.new.foo([]).should == ["A#foo"]
+ Super::MS3::A.foo([]).should == ["ModA#foo"]
+ Super::MS3::A.bar([]).should == ["ModA#bar","ModA#foo"]
+ Super::MS3::B.new.foo([]).should == ["A#foo"]
+ Super::MS3::B.foo([]).should == ["B.foo","ModA#foo"]
+ Super::MS3::B.bar([]).should == ["B.bar","ModA#bar","B.foo","ModA#foo"]
+ end
+
+ it "searches BasicObject from a module for methods defined there" do
+ Super::IncludesFromBasic.new.__send__(:foobar).should == 43
+ end
+
+ it "searches BasicObject through another module for methods defined there" do
+ Super::IncludesIntermediate.new.__send__(:foobar).should == 42
+ end
+
+ it "calls the correct method when the method visibility is modified" do
+ Super::MS4::A.new.example.should == 5
+ end
+
+ it "calls the correct method when the superclass argument list is different from the subclass" do
+ Super::S4::A.new.foo([]).should == ["A#foo"]
+ Super::S4::B.new.foo([],"test").should == ["B#foo(a,test)", "A#foo"]
+ end
+
+ it "raises an error error when super method does not exist" do
+ sup = Class.new
+ sub_normal = Class.new(sup) do
+ def foo
+ super()
+ end
+ end
+ sub_zsuper = Class.new(sup) do
+ def foo
+ super
+ end
+ end
+
+ lambda {sub_normal.new.foo}.should raise_error(NoMethodError, /super/)
+ lambda {sub_zsuper.new.foo}.should raise_error(NoMethodError, /super/)
+ end
+
+ it "uses given block even if arguments are passed explicitly" do
+ c1 = Class.new do
+ def m
+ yield
+ end
+ end
+ c2 = Class.new(c1) do
+ def m(v)
+ super()
+ end
+ end
+
+ c2.new.m(:dump) { :value }.should == :value
+ end
+
+ it "calls the superclass method when in a block" do
+ Super::S6.new.here.should == :good
+ end
+
+ it "calls the superclass method when initial method is defined_method'd" do
+ Super::S7.new.here.should == :good
+ end
+
+ it "can call through a define_method multiple times (caching check)" do
+ obj = Super::S7.new
+
+ 2.times do
+ obj.here.should == :good
+ end
+ end
+
+ it "supers up appropriate name even if used for multiple method names" do
+ sup = Class.new do
+ def a; "a"; end
+ def b; "b"; end
+ end
+
+ sub = Class.new(sup) do
+ [:a, :b].each do |name|
+ define_method name do
+ super()
+ end
+ end
+ end
+
+ sub.new.a.should == "a"
+ sub.new.b.should == "b"
+ sub.new.a.should == "a"
+ end
+
+ it "raises a RuntimeError when called with implicit arguments from a method defined with define_method" do
+ super_class = Class.new do
+ def a(arg)
+ arg
+ end
+ end
+
+ klass = Class.new super_class do
+ define_method :a do |arg|
+ super
+ end
+ end
+
+ lambda { klass.new.a(:a_called) }.should raise_error(RuntimeError)
+ end
+
+ # Rubinius ticket github#157
+ it "calls method_missing when a superclass method is not found" do
+ Super::MM_B.new.is_a?(Hash).should == false
+ end
+
+ # Rubinius ticket github#180
+ it "respects the original module a method is aliased from" do
+ Super::Alias3.new.name3.should == [:alias2, :alias1]
+ end
+
+ it "sees the included version of a module a method is alias from" do
+ Super::AliasWithSuper::Trigger.foo.should == [:b, :a]
+ end
+
+ it "find super from a singleton class" do
+ obj = Super::SingletonCase::Foo.new
+ def obj.foobar(array)
+ array << :singleton
+ super
+ end
+ obj.foobar([]).should == [:singleton, :foo, :base]
+ end
+
+ it "finds super on other objects if a singleton class aliased the method" do
+ orig_obj = Super::SingletonAliasCase::Foo.new
+ orig_obj.alias_on_singleton
+ orig_obj.new_foobar([]).should == [:foo, :base]
+ Super::SingletonAliasCase::Foo.new.foobar([]).should == [:foo, :base]
+ end
+
+ it "passes along modified rest args when they weren't originally empty" do
+ Super::RestArgsWithSuper::B.new.a("bar").should == ["bar", "foo"]
+ end
+
+ it "passes along modified rest args when they were originally empty" do
+ Super::RestArgsWithSuper::B.new.a.should == ["foo"]
+ end
+
+ # https://bugs.ruby-lang.org/issues/14279
+ it "passes along reassigned rest args" do
+ Super::ZSuperWithRestReassigned::B.new.a("bar").should == ["foo"]
+ end
+
+ # https://bugs.ruby-lang.org/issues/14279
+ it "wraps into array and passes along reassigned rest args with non-array scalar value" do
+ Super::ZSuperWithRestReassignedWithScalar::B.new.a("bar").should == ["foo"]
+ end
+
+ it "invokes methods from a chain of anonymous modules" do
+ Super::AnonymousModuleIncludedTwice.new.a([]).should == ["anon", "anon", "non-anon"]
+ end
+
+ it "without explicit arguments can accept a block but still pass the original arguments" do
+ Super::ZSuperWithBlock::B.new.a.should == 14
+ end
+
+ it "passes along block via reference to method expecting a reference" do
+ Super::ZSuperWithBlock::B.new.b.should == [14, 15]
+ end
+
+ it "passes along a block via reference to a method that yields" do
+ Super::ZSuperWithBlock::B.new.c.should == 16
+ end
+
+ it "without explicit arguments passes optional arguments that have a default value" do
+ Super::ZSuperWithOptional::B.new.m(1, 2).should == 14
+ end
+
+ it "without explicit arguments passes optional arguments that have a non-default value" do
+ Super::ZSuperWithOptional::B.new.m(1, 2, 3).should == 3
+ end
+
+ it "without explicit arguments passes optional arguments that have a default value but were modified" do
+ Super::ZSuperWithOptional::C.new.m(1, 2).should == 100
+ end
+
+ it "without explicit arguments passes optional arguments that have a non-default value but were modified" do
+ Super::ZSuperWithOptional::C.new.m(1, 2, 3).should == 100
+ end
+
+ it "without explicit arguments passes rest arguments" do
+ Super::ZSuperWithRest::B.new.m(1, 2, 3).should == [1, 2, 3]
+ end
+
+ it "without explicit arguments passes rest arguments including any modifications" do
+ Super::ZSuperWithRest::B.new.m_modified(1, 2, 3).should == [1, 14, 3]
+ end
+
+ it "without explicit arguments passes arguments and rest arguments" do
+ Super::ZSuperWithRestAndOthers::B.new.m(1, 2, 3, 4, 5).should == [3, 4, 5]
+ end
+
+ it "without explicit arguments passes arguments and rest arguments including any modifications" do
+ Super::ZSuperWithRestAndOthers::B.new.m_modified(1, 2, 3, 4, 5).should == [3, 14, 5]
+ end
+
+ it "without explicit arguments that are '_'" do
+ Super::ZSuperWithUnderscores::B.new.m(1, 2).should == [1, 2]
+ end
+
+ it "without explicit arguments that are '_' including any modifications" do
+ Super::ZSuperWithUnderscores::B.new.m_modified(1, 2).should == [14, 2]
+ end
+
+ describe 'when using keyword arguments' do
+ before :each do
+ @req = Super::Keywords::RequiredArguments.new
+ @opts = Super::Keywords::OptionalArguments.new
+ @etc = Super::Keywords::PlaceholderArguments.new
+
+ @req_and_opts = Super::Keywords::RequiredAndOptionalArguments.new
+ @req_and_etc = Super::Keywords::RequiredAndPlaceholderArguments.new
+ @opts_and_etc = Super::Keywords::OptionalAndPlaceholderArguments.new
+
+ @req_and_opts_and_etc = Super::Keywords::RequiredAndOptionalAndPlaceholderArguments.new
+ end
+
+ it 'does not pass any arguments to the parent when none are given' do
+ @etc.foo.should == {}
+ end
+
+ it 'passes only required arguments to the parent when no optional arguments are given' do
+ [@req, @req_and_etc].each do |obj|
+ obj.foo(a: 'a').should == {a: 'a'}
+ end
+ end
+
+ it 'passes default argument values to the parent' do
+ [@opts, @opts_and_etc].each do |obj|
+ obj.foo.should == {b: 'b'}
+ end
+
+ [@req_and_opts, @opts_and_etc, @req_and_opts_and_etc].each do |obj|
+ obj.foo(a: 'a').should == {a: 'a', b: 'b'}
+ end
+ end
+
+ it 'passes any given arguments including optional keyword arguments to the parent' do
+ [@etc, @req_and_opts, @req_and_etc, @opts_and_etc, @req_and_opts_and_etc].each do |obj|
+ obj.foo(a: 'a', b: 'b').should == {a: 'a', b: 'b'}
+ end
+
+ [@etc, @req_and_etc, @opts_and_etc, @req_and_opts_and_etc].each do |obj|
+ obj.foo(a: 'a', b: 'b', c: 'c').should == {a: 'a', b: 'b', c: 'c'}
+ end
+ end
+ end
+
+ describe 'when using regular and keyword arguments' do
+ before :each do
+ @req = Super::RegularAndKeywords::RequiredArguments.new
+ @opts = Super::RegularAndKeywords::OptionalArguments.new
+ @etc = Super::RegularAndKeywords::PlaceholderArguments.new
+
+ @req_and_opts = Super::RegularAndKeywords::RequiredAndOptionalArguments.new
+ @req_and_etc = Super::RegularAndKeywords::RequiredAndPlaceholderArguments.new
+ @opts_and_etc = Super::RegularAndKeywords::OptionalAndPlaceholderArguments.new
+
+ @req_and_opts_and_etc = Super::RegularAndKeywords::RequiredAndOptionalAndPlaceholderArguments.new
+ end
+
+ it 'passes only required regular arguments to the parent when no optional keyword arguments are given' do
+ @etc.foo('a').should == ['a', {}]
+ end
+
+ it 'passes only required regular and keyword arguments to the parent when no optional keyword arguments are given' do
+ [@req, @req_and_etc].each do |obj|
+ obj.foo('a', b: 'b').should == ['a', {b: 'b'}]
+ end
+ end
+
+ it 'passes default argument values to the parent' do
+ [@opts, @opts_and_etc].each do |obj|
+ obj.foo('a').should == ['a', {c: 'c'}]
+ end
+
+ [@req_and_opts, @opts_and_etc, @req_and_opts_and_etc].each do |obj|
+ obj.foo('a', b: 'b').should == ['a', {b: 'b', c: 'c'}]
+ end
+ end
+
+ it 'passes any given regular and keyword arguments including optional keyword arguments to the parent' do
+ [@etc, @req_and_opts, @req_and_etc, @opts_and_etc, @req_and_opts_and_etc].each do |obj|
+ obj.foo('a', b: 'b', c: 'c').should == ['a', {b: 'b', c: 'c'}]
+ end
+
+ [@etc, @req_and_etc, @opts_and_etc, @req_and_opts_and_etc].each do |obj|
+ obj.foo('a', b: 'b', c: 'c', d: 'd').should == ['a', {b: 'b', c: 'c', d: 'd'}]
+ end
+ end
+ end
+
+ describe 'when using splat and keyword arguments' do
+ before :each do
+ @all = Super::SplatAndKeywords::AllArguments.new
+ end
+
+ it 'does not pass any arguments to the parent when none are given' do
+ @all.foo.should == [[], {}]
+ end
+
+ it 'passes only splat arguments to the parent when no keyword arguments are given' do
+ @all.foo('a').should == [['a'], {}]
+ end
+
+ it 'passes only keyword arguments to the parent when no splat arguments are given' do
+ @all.foo(b: 'b').should == [[], {b: 'b'}]
+ end
+
+ it 'passes any given splat and keyword arguments to the parent' do
+ @all.foo('a', b: 'b').should == [['a'], {b: 'b'}]
+ end
+ end
+end
diff --git a/spec/ruby/language/symbol_spec.rb b/spec/ruby/language/symbol_spec.rb
new file mode 100644
index 0000000000..9ecb44ba87
--- /dev/null
+++ b/spec/ruby/language/symbol_spec.rb
@@ -0,0 +1,106 @@
+require_relative '../spec_helper'
+
+describe "A Symbol literal" do
+ it "is a ':' followed by any number of valid characters" do
+ a = :foo
+ a.should be_kind_of(Symbol)
+ a.inspect.should == ':foo'
+ end
+
+ it "is a ':' followed by any valid variable, method, or constant name" do
+ # Add more of these?
+ [ :Foo,
+ :foo,
+ :@foo,
+ :@@foo,
+ :$foo,
+ :_,
+ :~,
+ :- ,
+ :FOO,
+ :_Foo,
+ :&,
+ :_9
+ ].each { |s| s.should be_kind_of(Symbol) }
+ end
+
+ it "is a ':' followed by a single- or double-quoted string that may contain otherwise invalid characters" do
+ [ [:'foo bar', ':"foo bar"'],
+ [:'++', ':"++"'],
+ [:'9', ':"9"'],
+ [:"foo #{1 + 1}", ':"foo 2"'],
+ [:"foo\nbar", ':"foo\nbar"'],
+ ].each { |sym, str|
+ sym.should be_kind_of(Symbol)
+ sym.inspect.should == str
+ }
+ end
+
+ it 'inherits the encoding of the magic comment and can have a binary encoding' do
+ ruby_exe(fixture(__FILE__, "binary_symbol.rb"))
+ .should == "[105, 108, 95, 195, 169, 116, 97, 105, 116]\nASCII-8BIT\n"
+ end
+
+ it "may contain '::' in the string" do
+ :'Some::Class'.should be_kind_of(Symbol)
+ end
+
+ it "is converted to a literal, unquoted representation if the symbol contains only valid characters" do
+ a, b, c = :'foo', :'+', :'Foo__9'
+ a.should be_kind_of(Symbol)
+ a.inspect.should == ':foo'
+ b.should be_kind_of(Symbol)
+ b.inspect.should == ':+'
+ c.should be_kind_of(Symbol)
+ c.inspect.should == ':Foo__9'
+ end
+
+ it "can be created by the %s-delimited expression" do
+ a, b = :'foo bar', %s{foo bar}
+ b.should be_kind_of(Symbol)
+ b.inspect.should == ':"foo bar"'
+ b.should == a
+ end
+
+ it "is the same object when created from identical strings" do
+ var = "@@var"
+ [ [:symbol, :symbol],
+ [:'a string', :'a string'],
+ [:"#{var}", :"#{var}"]
+ ].each { |a, b|
+ a.should equal(b)
+ }
+ end
+
+ it "can contain null in the string" do
+ eval(':"\0" ').inspect.should == ':"\\x00"'
+ end
+
+ it "can be an empty string" do
+ c = :''
+ c.should be_kind_of(Symbol)
+ c.inspect.should == ':""'
+ end
+
+ it "can be :!, :!=, or :!~" do
+ %w{'!', '!=', '!~'}.each do |sym|
+ sym.to_sym.to_s.should == sym
+ end
+ end
+
+ it "can be created from list syntax %i{a b c} without interpolation" do
+ %i{a b #{c}}.should == [:a, :b, :"\#{c}"]
+ end
+
+ it "can be created from list syntax %I{a b c} with interpolation" do
+ %I{a b #{"c"}}.should == [:a, :b, :c]
+ end
+
+ it "with invalid bytes raises an EncodingError at parse time" do
+ ScratchPad.record []
+ -> {
+ eval 'ScratchPad << 1; :"\xC3"'
+ }.should raise_error(EncodingError, /invalid/)
+ ScratchPad.recorded.should == []
+ end
+end
diff --git a/spec/ruby/language/throw_spec.rb b/spec/ruby/language/throw_spec.rb
new file mode 100644
index 0000000000..1662789da4
--- /dev/null
+++ b/spec/ruby/language/throw_spec.rb
@@ -0,0 +1,81 @@
+require_relative '../spec_helper'
+
+describe "The throw keyword" do
+ it "abandons processing" do
+ i = 0
+ catch(:done) do
+ loop do
+ i += 1
+ throw :done if i > 4
+ end
+ i += 1
+ end
+ i.should == 5
+ end
+
+ it "supports a second parameter" do
+ msg = catch(:exit) do
+ throw :exit,:msg
+ end
+ msg.should == :msg
+ end
+
+ it "uses nil as a default second parameter" do
+ msg = catch(:exit) do
+ throw :exit
+ end
+ msg.should == nil
+ end
+
+ it "clears the current exception" do
+ catch :exit do
+ begin
+ raise "exception"
+ rescue
+ throw :exit
+ end
+ end
+ $!.should be_nil
+ end
+
+ it "allows any object as its argument" do
+ catch(1) { throw 1, 2 }.should == 2
+ o = Object.new
+ catch(o) { throw o, o }.should == o
+ end
+
+ it "does not convert strings to a symbol" do
+ lambda { catch(:exit) { throw "exit" } }.should raise_error(ArgumentError)
+ end
+
+ it "unwinds stack from within a method" do
+ def throw_method(handler, val)
+ throw handler, val
+ end
+
+ catch(:exit) do
+ throw_method(:exit, 5)
+ end.should == 5
+ end
+
+ it "unwinds stack from within a lambda" do
+ c = lambda { throw :foo, :msg }
+ catch(:foo) { c.call }.should == :msg
+ end
+
+ it "raises an ArgumentError if outside of scope of a matching catch" do
+ lambda { throw :test, 5 }.should raise_error(ArgumentError)
+ lambda { catch(:different) { throw :test, 5 } }.should raise_error(ArgumentError)
+ end
+
+ it "raises an UncaughtThrowError if used to exit a thread" do
+ catch(:what) do
+ t = Thread.new {
+ -> {
+ throw :what
+ }.should raise_error(UncaughtThrowError)
+ }
+ t.join
+ end
+ end
+end
diff --git a/spec/ruby/language/undef_spec.rb b/spec/ruby/language/undef_spec.rb
new file mode 100644
index 0000000000..7a333c46ef
--- /dev/null
+++ b/spec/ruby/language/undef_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../spec_helper'
+
+describe "The undef keyword" do
+ describe "undefines a method" do
+ before :each do
+ @undef_class = Class.new do
+ def meth(o); o; end
+ end
+ @obj = @undef_class.new
+ @obj.meth(5).should == 5
+ end
+
+ it "with an identifier" do
+ @undef_class.class_eval do
+ undef meth
+ end
+ lambda { @obj.meth(5) }.should raise_error(NoMethodError)
+ end
+
+ it "with a simple symbol" do
+ @undef_class.class_eval do
+ undef :meth
+ end
+ lambda { @obj.meth(5) }.should raise_error(NoMethodError)
+ end
+
+ it "with a single quoted symbol" do
+ @undef_class.class_eval do
+ undef :'meth'
+ end
+ lambda { @obj.meth(5) }.should raise_error(NoMethodError)
+ end
+
+ it "with a double quoted symbol" do
+ @undef_class.class_eval do
+ undef :"meth"
+ end
+ lambda { @obj.meth(5) }.should raise_error(NoMethodError)
+ end
+
+ it "with a interpolated symbol" do
+ @undef_class.class_eval do
+ undef :"#{'meth'}"
+ end
+ lambda { @obj.meth(5) }.should raise_error(NoMethodError)
+ end
+ end
+
+ it "allows undefining multiple methods at a time" do
+ undef_multiple = Class.new do
+ def method1; end
+ def method2; :nope; end
+
+ undef :method1, :method2
+ end
+
+ obj = undef_multiple.new
+ obj.respond_to?(:method1).should == false
+ obj.respond_to?(:method2).should == false
+ end
+
+ it "raises a NameError when passed a missing name" do
+ Class.new do
+ lambda {
+ undef not_exist
+ }.should raise_error(NameError) { |e|
+ # a NameError and not a NoMethodError
+ e.class.should == NameError
+ }
+ end
+ end
+end
diff --git a/spec/ruby/language/unless_spec.rb b/spec/ruby/language/unless_spec.rb
new file mode 100644
index 0000000000..98acdc083b
--- /dev/null
+++ b/spec/ruby/language/unless_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+
+describe "The unless expression" do
+ it "evaluates the unless body when the expression is false" do
+ unless false
+ a = true
+ else
+ a = false
+ end
+
+ a.should == true
+ end
+
+ it "returns the last statement in the body" do
+ unless false
+ 'foo'
+ 'bar'
+ 'baz'
+ end.should == 'baz'
+ end
+
+ it "evaluates the else body when the expression is true" do
+ unless true
+ 'foo'
+ else
+ 'bar'
+ end.should == 'bar'
+ end
+
+ it "takes an optional then after the expression" do
+ unless false then
+ 'baz'
+ end.should == 'baz'
+ end
+
+ it "does not return a value when the expression is true" do
+ unless true; end.should == nil
+ end
+
+ it "allows expression and body to be on one line (using 'then')" do
+ unless false then 'foo'; else 'bar'; end.should == 'foo'
+ end
+end
diff --git a/spec/ruby/language/until_spec.rb b/spec/ruby/language/until_spec.rb
new file mode 100644
index 0000000000..ddeb5d01e4
--- /dev/null
+++ b/spec/ruby/language/until_spec.rb
@@ -0,0 +1,234 @@
+require_relative '../spec_helper'
+
+# until bool-expr [do]
+# body
+# end
+#
+# begin
+# body
+# end until bool-expr
+#
+# expr until bool-expr
+describe "The until expression" do
+ it "runs while the expression is false" do
+ i = 0
+ until i > 9
+ i += 1
+ end
+
+ i.should == 10
+ end
+
+ it "optionally takes a 'do' after the expression" do
+ i = 0
+ until i > 9 do
+ i += 1
+ end
+
+ i.should == 10
+ end
+
+ it "allows body begin on the same line if do is used" do
+ i = 0
+ until i > 9 do i += 1
+ end
+
+ i.should == 10
+ end
+
+ it "executes code in containing variable scope" do
+ i = 0
+ until i == 1
+ a = 123
+ i = 1
+ end
+
+ a.should == 123
+ end
+
+ it "executes code in containing variable scope with 'do'" do
+ i = 0
+ until i == 1 do
+ a = 123
+ i = 1
+ end
+
+ a.should == 123
+ end
+
+ it "returns nil if ended when condition became true" do
+ i = 0
+ until i > 9
+ i += 1
+ end.should == nil
+ end
+
+ it "evaluates the body if expression is empty" do
+ a = []
+ until ()
+ a << :body_evaluated
+ break
+ end
+ a.should == [:body_evaluated]
+ end
+
+ it "stops running body if interrupted by break" do
+ i = 0
+ until i > 9
+ i += 1
+ break if i > 5
+ end
+ i.should == 6
+ end
+
+ it "returns value passed to break if interrupted by break" do
+ until false
+ break 123
+ end.should == 123
+ end
+
+ it "returns nil if interrupted by break with no arguments" do
+ until false
+ break
+ end.should == nil
+ end
+
+ it "skips to end of body with next" do
+ a = []
+ i = 0
+ until (i+=1)>=5
+ next if i==3
+ a << i
+ end
+ a.should == [1, 2, 4]
+ end
+
+ it "restarts the current iteration without reevaluating condition with redo" do
+ a = []
+ i = 0
+ j = 0
+ until (i+=1)>=3
+ a << i
+ j+=1
+ redo if j<3
+ end
+ a.should == [1, 1, 1, 2]
+ end
+end
+
+describe "The until modifier" do
+ it "runs preceding statement while the condition is false" do
+ i = 0
+ i += 1 until i > 9
+ i.should == 10
+ end
+
+ it "evaluates condition before statement execution" do
+ a = []
+ i = 0
+ a << i until (i+=1) >= 3
+ a.should == [1, 2]
+ end
+
+ it "does not run preceding statement if the condition is true" do
+ i = 0
+ i += 1 until true
+ i.should == 0
+ end
+
+ it "returns nil if ended when condition became true" do
+ i = 0
+ (i += 1 until i>9).should == nil
+ end
+
+ it "returns value passed to break if interrupted by break" do
+ (break 123 until false).should == 123
+ end
+
+ it "returns nil if interrupted by break with no arguments" do
+ (break until false).should == nil
+ end
+
+ it "skips to end of body with next" do
+ i = 0
+ j = 0
+ ((i+=1) == 3 ? next : j+=i) until i > 10
+ j.should == 63
+ end
+
+ it "restarts the current iteration without reevaluating condition with redo" do
+ i = 0
+ j = 0
+ (i+=1) == 4 ? redo : j+=i until (i+=1) > 10
+ j.should == 34
+ end
+end
+
+describe "The until modifier with begin .. end block" do
+ it "runs block while the expression is false" do
+ i = 0
+ begin
+ i += 1
+ end until i > 9
+
+ i.should == 10
+ end
+
+ it "stops running block if interrupted by break" do
+ i = 0
+ begin
+ i += 1
+ break if i > 5
+ end until i > 9
+
+ i.should == 6
+ end
+
+ it "returns value passed to break if interrupted by break" do
+ (begin; break 123; end until false).should == 123
+ end
+
+ it "returns nil if interrupted by break with no arguments" do
+ (begin; break; end until false).should == nil
+ end
+
+ it "runs block at least once (even if the expression is true)" do
+ i = 0
+ begin
+ i += 1
+ end until true
+
+ i.should == 1
+ end
+
+ it "evaluates condition after block execution" do
+ a = []
+ i = 0
+ begin
+ a << i
+ end until (i+=1)>=5
+ a.should == [0, 1, 2, 3, 4]
+ end
+
+ it "skips to end of body with next" do
+ a = []
+ i = 0
+ begin
+ next if i==3
+ a << i
+ end until (i+=1)>=5
+ a.should == [0, 1, 2, 4]
+ end
+
+ it "restart the current iteration without reevaluting condition with redo" do
+ a = []
+ i = 0
+ j = 0
+ begin
+ a << i
+ j+=1
+ redo if j<3
+ end until (i+=1)>=3
+ a.should == [0, 0, 0, 1, 2]
+ end
+end
diff --git a/spec/ruby/language/variables_spec.rb b/spec/ruby/language/variables_spec.rb
new file mode 100644
index 0000000000..4c4599bec4
--- /dev/null
+++ b/spec/ruby/language/variables_spec.rb
@@ -0,0 +1,760 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/variables'
+
+describe "Multiple assignment" do
+ context "with a single RHS value" do
+ it "assigns a simple MLHS" do
+ (a, b, c = 1).should == 1
+ [a, b, c].should == [1, nil, nil]
+ end
+
+ it "calls #to_ary to convert an Object RHS when assigning a simple MLHS" do
+ x = mock("multi-assign single RHS")
+ x.should_receive(:to_ary).and_return([1, 2])
+
+ (a, b, c = x).should == x
+ [a, b, c].should == [1, 2, nil]
+ end
+
+ it "calls #to_ary if it is private" do
+ x = mock("multi-assign single RHS")
+ x.should_receive(:to_ary).and_return([1, 2])
+ class << x; private :to_ary; end
+
+ (a, b, c = x).should == x
+ [a, b, c].should == [1, 2, nil]
+ end
+
+ it "does not call #to_ary if #respond_to? returns false" do
+ x = mock("multi-assign single RHS")
+ x.should_receive(:respond_to?).with(:to_ary, true).and_return(false)
+ x.should_not_receive(:to_ary)
+
+ (a, b, c = x).should == x
+ [a, b, c].should == [x, nil, nil]
+ end
+
+ it "wraps the Object in an Array if #to_ary returns nil" do
+ x = mock("multi-assign single RHS")
+ x.should_receive(:to_ary).and_return(nil)
+
+ (a, b, c = x).should == x
+ [a, b, c].should == [x, nil, nil]
+ end
+
+ it "raises a TypeError of #to_ary does not return an Array" do
+ x = mock("multi-assign single RHS")
+ x.should_receive(:to_ary).and_return(1)
+
+ lambda { a, b, c = x }.should raise_error(TypeError)
+ end
+
+ it "does not call #to_a to convert an Object RHS when assigning a simple MLHS" do
+ x = mock("multi-assign single RHS")
+ x.should_not_receive(:to_a)
+
+ (a, b, c = x).should == x
+ [a, b, c].should == [x, nil, nil]
+ end
+
+ it "does not call #to_ary on an Array instance" do
+ x = [1, 2]
+ x.should_not_receive(:to_ary)
+
+ (a, b = x).should == x
+ [a, b].should == [1, 2]
+ end
+
+ it "does not call #to_a on an Array instance" do
+ x = [1, 2]
+ x.should_not_receive(:to_a)
+
+ (a, b = x).should == x
+ [a, b].should == [1, 2]
+ end
+
+ it "returns the RHS when it is an Array" do
+ ary = [1, 2]
+
+ x = (a, b = ary)
+ x.should equal(ary)
+ end
+
+ it "returns the RHS when it is an Array subclass" do
+ cls = Class.new(Array)
+ ary = cls.new [1, 2]
+
+ x = (a, b = ary)
+ x.should equal(ary)
+ end
+
+ it "does not call #to_ary on an Array subclass instance" do
+ x = Class.new(Array).new [1, 2]
+ x.should_not_receive(:to_ary)
+
+ (a, b = x).should == x
+ [a, b].should == [1, 2]
+ end
+
+ it "does not call #to_a on an Array subclass instance" do
+ x = Class.new(Array).new [1, 2]
+ x.should_not_receive(:to_a)
+
+ (a, b = x).should == x
+ [a, b].should == [1, 2]
+ end
+
+ it "assigns a MLHS with a trailing comma" do
+ a, = 1
+ b, c, = []
+ [a, b, c].should == [1, nil, nil]
+ end
+
+ it "assigns a single LHS splat" do
+ (*a = 1).should == 1
+ a.should == [1]
+ end
+
+ it "calls #to_ary to convert an Object RHS" do
+ x = mock("multi-assign splat")
+ x.should_receive(:to_ary).and_return([1, 2])
+
+ (*a = x).should == x
+ a.should == [1, 2]
+ end
+
+ it "raises a TypeError if #to_ary does not return an Array" do
+ x = mock("multi-assign splat")
+ x.should_receive(:to_ary).and_return(1)
+
+ lambda { *a = x }.should raise_error(TypeError)
+ end
+
+ it "does not call #to_ary on an Array subclass" do
+ cls = Class.new(Array)
+ ary = cls.new [1, 2]
+ ary.should_not_receive(:to_ary)
+
+ (*a = ary).should == [1, 2]
+ a.should == [1, 2]
+ end
+
+ it "assigns an Array when the RHS is an Array subclass" do
+ cls = Class.new(Array)
+ ary = cls.new [1, 2]
+
+ x = (*a = ary)
+ x.should equal(ary)
+ a.should be_an_instance_of(Array)
+ end
+
+ it "calls #to_ary to convert an Object RHS with MLHS" do
+ x = mock("multi-assign splat")
+ x.should_receive(:to_ary).and_return([1, 2])
+
+ (a, *b, c = x).should == x
+ [a, b, c].should == [1, [], 2]
+ end
+
+ it "raises a TypeError if #to_ary does not return an Array with MLHS" do
+ x = mock("multi-assign splat")
+ x.should_receive(:to_ary).and_return(1)
+
+ lambda { a, *b, c = x }.should raise_error(TypeError)
+ end
+
+ it "does not call #to_a to convert an Object RHS with a MLHS" do
+ x = mock("multi-assign splat")
+ x.should_not_receive(:to_a)
+
+ (a, *b = x).should == x
+ [a, b].should == [x, []]
+ end
+
+ it "assigns a MLHS with leading splat" do
+ (*a, b, c = 1).should == 1
+ [a, b, c].should == [[], 1, nil]
+ end
+
+ it "assigns a MLHS with a middle splat" do
+ a, b, *c, d, e = 1
+ [a, b, c, d, e].should == [1, nil, [], nil, nil]
+ end
+
+ it "assigns a MLHS with a trailing splat" do
+ a, b, *c = 1
+ [a, b, c].should == [1, nil, []]
+ end
+
+ it "assigns a grouped LHS without splat" do
+ ((a, b), c), (d, (e,), (f, (g, h))) = 1
+ [a, b, c, d, e, f, g, h].should == [1, nil, nil, nil, nil, nil, nil, nil]
+ end
+
+ it "assigns a single grouped LHS splat" do
+ (*a) = nil
+ a.should == [nil]
+ end
+
+ it "assigns a grouped LHS with splats" do
+ (a, *b), c, (*d, (e, *f, g)) = 1
+ [a, b, c, d, e, f, g].should == [1, [], nil, [], nil, [], nil]
+ end
+
+ it "consumes values for an anonymous splat" do
+ (* = 1).should == 1
+ end
+
+ it "consumes values for a grouped anonymous splat" do
+ ((*) = 1).should == 1
+ end
+
+ it "does not mutate a RHS Array" do
+ x = [1, 2, 3, 4]
+ a, *b, c, d = x
+ [a, b, c, d].should == [1, [2], 3, 4]
+ x.should == [1, 2, 3, 4]
+ end
+
+ it "assigns values from a RHS method call" do
+ def x() 1 end
+
+ (a, b = x).should == 1
+ [a, b].should == [1, nil]
+ end
+
+ it "assigns values from a RHS method call with arguments" do
+ def x(a) a end
+
+ (a, b = x []).should == []
+ [a, b].should == [nil, nil]
+ end
+
+ it "assigns values from a RHS method call with receiver" do
+ x = mock("multi-assign attributes")
+ x.should_receive(:m).and_return([1, 2, 3])
+
+ a, b = x.m
+ [a, b].should == [1, 2]
+ end
+
+ it "calls #to_ary on the value returned by the method call" do
+ y = mock("multi-assign method return value")
+ y.should_receive(:to_ary).and_return([1, 2])
+
+ x = mock("multi-assign attributes")
+ x.should_receive(:m).and_return(y)
+
+ (a, b = x.m).should == y
+ [a, b].should == [1, 2]
+ end
+
+ it "raises a TypeError if #to_ary does not return an Array on a single RHS" do
+ y = mock("multi-assign method return value")
+ y.should_receive(:to_ary).and_return(1)
+
+ x = mock("multi-assign attributes")
+ x.should_receive(:m).and_return(y)
+
+ lambda { a, b = x.m }.should raise_error(TypeError)
+ end
+
+ it "assigns values from a RHS method call with receiver and arguments" do
+ x = mock("multi-assign attributes")
+ x.should_receive(:m).with(1, 2).and_return([1, 2, 3])
+
+ a, b = x.m 1, 2
+ [a, b].should == [1, 2]
+ end
+
+ it "assigns global variables" do
+ $spec_a, $spec_b = 1
+ [$spec_a, $spec_b].should == [1, nil]
+ end
+
+ it "assigns instance variables" do
+ @a, @b = 1
+ [@a, @b].should == [1, nil]
+ end
+
+ it "assigns attributes" do
+ a = mock("multi-assign attributes")
+ a.should_receive(:x=).with(1)
+ a.should_receive(:y=).with(nil)
+
+ a.x, a.y = 1
+ end
+
+ it "assigns indexed elements" do
+ a = []
+ a[1], a[2] = 1
+ a.should == [nil, 1, nil]
+ end
+
+ it "assigns constants" do
+ module VariableSpecs
+ SINGLE_RHS_1, SINGLE_RHS_2 = 1
+ [SINGLE_RHS_1, SINGLE_RHS_2].should == [1, nil]
+ end
+ end
+ end
+
+ context "with a single splatted RHS value" do
+ it "assigns a single grouped LHS splat" do
+ (*a) = *1
+ a.should == [1]
+ end
+
+ it "assigns an empty Array to a single LHS value when passed nil" do
+ (a = *nil).should == []
+ a.should == []
+ end
+
+ it "calls #to_a to convert nil to an empty Array" do
+ nil.should_receive(:to_a).and_return([])
+
+ (*a = *nil).should == []
+ a.should == []
+ end
+
+ it "does not call #to_a on an Array" do
+ ary = [1, 2]
+ ary.should_not_receive(:to_a)
+
+ (a = *ary).should == [1, 2]
+ a.should == [1, 2]
+ end
+
+ it "returns a copy of a splatted Array" do
+ ary = [1, 2]
+
+ (a = *ary).should == [1, 2]
+ a.should_not equal(ary)
+ end
+
+ it "does not call #to_a on an Array subclass" do
+ cls = Class.new(Array)
+ ary = cls.new [1, 2]
+ ary.should_not_receive(:to_a)
+
+ (a = *ary).should == [1, 2]
+ a.should == [1, 2]
+ end
+
+ it "returns an Array when the splatted object is an Array subclass" do
+ cls = Class.new(Array)
+ ary = cls.new [1, 2]
+
+ x = (a = *ary)
+
+ x.should == [1, 2]
+ x.should be_an_instance_of(Array)
+
+ a.should == [1, 2]
+ a.should be_an_instance_of(Array)
+ end
+
+ it "consumes values for an anonymous splat" do
+ a = 1
+ (* = *a).should == [1]
+ end
+
+ it "consumes values for a grouped anonymous splat" do
+ ((*) = *1).should == [1]
+ end
+
+ it "assigns a single LHS splat" do
+ x = 1
+ (*a = *x).should == [1]
+ a.should == [1]
+ end
+
+ it "calls #to_a to convert an Object RHS with a single splat LHS" do
+ x = mock("multi-assign RHS splat")
+ x.should_receive(:to_a).and_return([1, 2])
+
+ (*a = *x).should == [1, 2]
+ a.should == [1, 2]
+ end
+
+ it "calls #to_a if it is private" do
+ x = mock("multi-assign RHS splat")
+ x.should_receive(:to_a).and_return([1, 2])
+ class << x; private :to_a; end
+
+ (*a = *x).should == [1, 2]
+ a.should == [1, 2]
+ end
+
+ it "does not call #to_a if #respond_to? returns false" do
+ x = mock("multi-assign RHS splat")
+ x.should_receive(:respond_to?).with(:to_a, true).and_return(false)
+ x.should_not_receive(:to_a)
+
+ (*a = *x).should == [x]
+ a.should == [x]
+ end
+
+ it "wraps the Object in an Array if #to_a returns nil" do
+ x = mock("multi-assign RHS splat")
+ x.should_receive(:to_a).and_return(nil)
+
+ (*a = *x).should == [x]
+ a.should == [x]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array" do
+ x = mock("multi-assign RHS splat")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { *a = *x }.should raise_error(TypeError)
+ end
+
+ it "does not call #to_ary to convert an Object RHS with a single splat LHS" do
+ x = mock("multi-assign RHS splat")
+ x.should_not_receive(:to_ary)
+
+ (*a = *x).should == [x]
+ a.should == [x]
+ end
+
+ it "assigns a MLHS with leading splat" do
+ (*a, b, c = *1).should == [1]
+ [a, b, c].should == [[], 1, nil]
+ end
+
+ it "assigns a MLHS with a middle splat" do
+ a, b, *c, d, e = *1
+ [a, b, c, d, e].should == [1, nil, [], nil, nil]
+ end
+
+ it "assigns a MLHS with a trailing splat" do
+ a, b, *c = *nil
+ [a, b, c].should == [nil, nil, []]
+ end
+
+ it "calls #to_a to convert an Object RHS with a single LHS" do
+ x = mock("multi-assign RHS splat")
+ x.should_receive(:to_a).and_return([1, 2])
+
+ (a = *x).should == [1, 2]
+ a.should == [1, 2]
+ end
+
+ it "does not call #to_ary to convert an Object RHS with a single LHS" do
+ x = mock("multi-assign RHS splat")
+ x.should_not_receive(:to_ary)
+
+ (a = *x).should == [x]
+ a.should == [x]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array with a single LHS" do
+ x = mock("multi-assign splat")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { a = *x }.should raise_error(TypeError)
+ end
+
+ it "calls #to_a to convert an Object splat RHS when assigned to a simple MLHS" do
+ x = mock("multi-assign splat")
+ x.should_receive(:to_a).and_return([1, 2])
+
+ (a, b, c = *x).should == [1, 2]
+ [a, b, c].should == [1, 2, nil]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array with a simple MLHS" do
+ x = mock("multi-assign splat")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { a, b, c = *x }.should raise_error(TypeError)
+ end
+
+ it "does not call #to_ary to convert an Object splat RHS when assigned to a simple MLHS" do
+ x = mock("multi-assign splat")
+ x.should_not_receive(:to_ary)
+
+ (a, b, c = *x).should == [x]
+ [a, b, c].should == [x, nil, nil]
+ end
+
+ it "calls #to_a to convert an Object RHS with MLHS" do
+ x = mock("multi-assign splat")
+ x.should_receive(:to_a).and_return([1, 2])
+
+ a, *b, c = *x
+ [a, b, c].should == [1, [], 2]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array with MLHS" do
+ x = mock("multi-assign splat")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { a, *b, c = *x }.should raise_error(TypeError)
+ end
+
+ it "does not call #to_ary to convert an Object RHS with a MLHS" do
+ x = mock("multi-assign splat")
+ x.should_not_receive(:to_ary)
+
+ a, *b = *x
+ [a, b].should == [x, []]
+ end
+
+ it "assigns a grouped LHS without splats" do
+ ((a, b), c), (d, (e,), (f, (g, h))) = *1
+ [a, b, c, d, e, f, g, h].should == [1, nil, nil, nil, nil, nil, nil, nil]
+ end
+
+ it "assigns a grouped LHS with splats" do
+ (a, *b), c, (*d, (e, *f, g)) = *1
+ [a, b, c, d, e, f, g].should == [1, [], nil, [], nil, [], nil]
+ end
+
+ it "does not mutate a RHS Array" do
+ x = [1, 2, 3, 4]
+ a, *b, c, d = *x
+ [a, b, c, d].should == [1, [2], 3, 4]
+ x.should == [1, 2, 3, 4]
+ end
+
+ it "assigns constants" do
+ module VariableSpecs
+ (*SINGLE_SPLATTED_RHS) = *1
+ SINGLE_SPLATTED_RHS.should == [1]
+ end
+ end
+ end
+
+ context "with a MRHS value" do
+ it "consumes values for an anonymous splat" do
+ (* = 1, 2, 3).should == [1, 2, 3]
+ end
+
+ it "consumes values for a grouped anonymous splat" do
+ ((*) = 1, 2, 3).should == [1, 2, 3]
+ end
+
+ it "consumes values for multiple '_' variables" do
+ a, _, b, _, c = 1, 2, 3, 4, 5
+ [a, b, c].should == [1, 3, 5]
+ end
+
+ it "does not call #to_a to convert an Object in a MRHS" do
+ x = mock("multi-assign MRHS")
+ x.should_not_receive(:to_a)
+
+ (a, b = 1, x).should == [1, x]
+ [a, b].should == [1, x]
+ end
+
+ it "does not call #to_ary to convert an Object in a MRHS" do
+ x = mock("multi-assign MRHS")
+ x.should_not_receive(:to_ary)
+
+ (a, b = 1, x).should == [1, x]
+ [a, b].should == [1, x]
+ end
+
+ it "calls #to_a to convert a splatted Object as part of a MRHS with a splat MLHS" do
+ x = mock("multi-assign splat MRHS")
+ x.should_receive(:to_a).and_return([3, 4])
+
+ (a, *b = 1, *x).should == [1, 3, 4]
+ [a, b].should == [1, [3, 4]]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array with a splat MLHS" do
+ x = mock("multi-assign splat MRHS")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { a, *b = 1, *x }.should raise_error(TypeError)
+ end
+
+ it "does not call #to_ary to convert a splatted Object as part of a MRHS with a splat MRHS" do
+ x = mock("multi-assign splat MRHS")
+ x.should_not_receive(:to_ary)
+
+ (a, *b = 1, *x).should == [1, x]
+ [a, b].should == [1, [x]]
+ end
+
+ it "calls #to_a to convert a splatted Object as part of a MRHS" do
+ x = mock("multi-assign splat MRHS")
+ x.should_receive(:to_a).and_return([3, 4])
+
+ (a, *b = *x, 1).should == [3, 4, 1]
+ [a, b].should == [3, [4, 1]]
+ end
+
+ it "raises a TypeError if #to_a does not return an Array with a splat MRHS" do
+ x = mock("multi-assign splat MRHS")
+ x.should_receive(:to_a).and_return(1)
+
+ lambda { a, *b = *x, 1 }.should raise_error(TypeError)
+ end
+
+ it "does not call #to_ary to convert a splatted Object with a splat MRHS" do
+ x = mock("multi-assign splat MRHS")
+ x.should_not_receive(:to_ary)
+
+ (a, *b = *x, 1).should == [x, 1]
+ [a, b].should == [x, [1]]
+ end
+
+ it "assigns a grouped LHS without splat from a simple Array" do
+ ((a, b), c), (d, (e,), (f, (g, h))) = 1, 2, 3, 4, 5
+ [a, b, c, d, e, f, g, h].should == [1, nil, nil, 2, nil, nil, nil, nil]
+ end
+
+ it "assigns a grouped LHS without splat from nested Arrays" do
+ ary = [[1, 2, 3], 4], [[5], [6, 7], [8, [9, 10]]]
+ ((a, b), c), (d, (e,), (f, (g, h))) = ary
+ [a, b, c, d, e, f, g, h].should == [1, 2, 4, [5], 6, 8, 9, 10]
+ end
+
+ it "assigns a single grouped LHS splat" do
+ (*a) = 1, 2, 3
+ a.should == [1, 2, 3]
+ end
+
+ it "assigns a grouped LHS with splats from nested Arrays for simple values" do
+ (a, *b), c, (*d, (e, *f, g)) = 1, 2, 3, 4
+ [a, b, c, d, e, f, g].should == [1, [], 2, [], 3, [], nil]
+ end
+
+ it "assigns a grouped LHS with splats from nested Arrays for nested arrays" do
+ (a, *b), c, (*d, (e, *f, g)) = [1, [2, 3]], [4, 5], [6, 7, 8]
+ [a, b, c, d, e, f, g].should == [1, [[2, 3]], [4, 5], [6, 7], 8, [], nil]
+ end
+
+ it "calls #to_ary to convert an Object when the position receiving the value is a multiple assignment" do
+ x = mock("multi-assign mixed RHS")
+ x.should_receive(:to_ary).and_return([1, 2])
+
+ (a, (b, c), d, e = 1, x, 3, 4).should == [1, x, 3, 4]
+ [a, b, c, d, e].should == [1, 1, 2, 3, 4]
+ end
+
+ it "raises a TypeError if #to_ary does not return an Array" do
+ x = mock("multi-assign mixed RHS")
+ x.should_receive(:to_ary).and_return(x)
+
+ lambda { a, (b, c), d = 1, x, 3, 4 }.should raise_error(TypeError)
+ end
+
+ it "calls #to_a to convert a splatted Object value in a MRHS" do
+ x = mock("multi-assign mixed splatted RHS")
+ x.should_receive(:to_a).and_return([4, 5])
+
+ (a, *b, (c, d) = 1, 2, 3, *x).should == [1, 2, 3, 4, 5]
+ [a, b, c, d].should == [1, [2, 3, 4], 5, nil]
+
+ end
+
+ it "calls #to_ary to convert a splatted Object when the position receiving the value is a multiple assignment" do
+ x = mock("multi-assign mixed splatted RHS")
+ x.should_receive(:to_ary).and_return([4, 5])
+
+ (a, *b, (c, d) = 1, 2, 3, *x).should == [1, 2, 3, x]
+ [a, b, c, d].should == [1, [2, 3], 4, 5]
+ end
+
+ it "raises a TypeError if #to_ary does not return an Array in a MRHS" do
+ x = mock("multi-assign mixed splatted RHS")
+ x.should_receive(:to_ary).and_return(x)
+
+ lambda { a, *b, (c, d) = 1, 2, 3, *x }.should raise_error(TypeError)
+ end
+
+ it "does not call #to_ary to convert an Object when the position receiving the value is a simple variable" do
+ x = mock("multi-assign mixed RHS")
+ x.should_not_receive(:to_ary)
+
+ a, b, c, d = 1, x, 3, 4
+ [a, b, c, d].should == [1, x, 3, 4]
+ end
+
+ it "does not call #to_ary to convert an Object when the position receiving the value is a rest variable" do
+ x = mock("multi-assign mixed RHS")
+ x.should_not_receive(:to_ary)
+
+ a, *b, c, d = 1, x, 3, 4
+ [a, b, c, d].should == [1, [x], 3, 4]
+ end
+
+ it "does not call #to_ary to convert a splatted Object when the position receiving the value is a simple variable" do
+ x = mock("multi-assign mixed splatted RHS")
+ x.should_not_receive(:to_ary)
+
+ a, *b, c = 1, 2, *x
+ [a, b, c].should == [1, [2], x]
+ end
+
+ it "does not call #to_ary to convert a splatted Object when the position receiving the value is a rest variable" do
+ x = mock("multi-assign mixed splatted RHS")
+ x.should_not_receive(:to_ary)
+
+ a, b, *c = 1, 2, *x
+ [a, b, c].should == [1, 2, [x]]
+ end
+
+ it "does not mutate the assigned Array" do
+ x = ((a, *b, c, d) = 1, 2, 3, 4, 5)
+ x.should == [1, 2, 3, 4, 5]
+ end
+
+ it "assigns RHS values to LHS constants" do
+ module VariableSpecs
+ MRHS_VALUES_1, MRHS_VALUES_2 = 1, 2
+ MRHS_VALUES_1.should == 1
+ MRHS_VALUES_2.should == 2
+ end
+ end
+
+ it "assigns all RHS values as an array to a single LHS constant" do
+ module VariableSpecs
+ MRHS_VALUES = 1, 2, 3
+ MRHS_VALUES.should == [1, 2, 3]
+ end
+ end
+ end
+
+ context "with a RHS assignment value" do
+ it "consumes values for an anonymous splat" do
+ (* = (a = 1)).should == 1
+ a.should == 1
+ end
+
+ it "does not mutate a RHS Array" do
+ a, *b, c, d = (e = [1, 2, 3, 4])
+ [a, b, c, d].should == [1, [2], 3, 4]
+ e.should == [1, 2, 3, 4]
+ end
+ end
+end
+
+describe "A local variable assigned only within a conditional block" do
+ context "accessed from a later closure" do
+ it "is defined?" do
+ if VariablesSpecs.false
+ a = 1
+ end
+
+ 1.times do
+ defined?(a).should == "local-variable"
+ end
+ end
+
+ it "is nil" do
+ if VariablesSpecs.false
+ a = 1
+ end
+
+ 1.times do
+ a.inspect.should == "nil"
+ end
+ end
+ end
+end
diff --git a/spec/ruby/language/while_spec.rb b/spec/ruby/language/while_spec.rb
new file mode 100644
index 0000000000..02c52c780f
--- /dev/null
+++ b/spec/ruby/language/while_spec.rb
@@ -0,0 +1,344 @@
+require_relative '../spec_helper'
+
+# while bool-expr [do]
+# body
+# end
+#
+# begin
+# body
+# end while bool-expr
+#
+# expr while bool-expr
+describe "The while expression" do
+ it "runs while the expression is true" do
+ i = 0
+ while i < 3
+ i += 1
+ end
+ i.should == 3
+ end
+
+ it "optionally takes a 'do' after the expression" do
+ i = 0
+ while i < 3 do
+ i += 1
+ end
+
+ i.should == 3
+ end
+
+ it "allows body begin on the same line if do is used" do
+ i = 0
+ while i < 3 do i += 1
+ end
+
+ i.should == 3
+ end
+
+ it "executes code in containing variable scope" do
+ i = 0
+ while i != 1
+ a = 123
+ i = 1
+ end
+
+ a.should == 123
+ end
+
+ it "executes code in containing variable scope with 'do'" do
+ i = 0
+ while i != 1 do
+ a = 123
+ i = 1
+ end
+
+ a.should == 123
+ end
+
+ it "returns nil if ended when condition became false" do
+ i = 0
+ while i < 3
+ i += 1
+ end.should == nil
+ end
+
+ it "does not evaluate the body if expression is empty" do
+ a = []
+ while ()
+ a << :body_evaluated
+ end
+ a.should == []
+ end
+
+ it "stops running body if interrupted by break" do
+ i = 0
+ while i < 10
+ i += 1
+ break if i > 5
+ end
+ i.should == 6
+ end
+
+ it "stops running body if interrupted by break in a parenthesized element op-assign-or value" do
+ c = true
+ a = []
+ while c
+ a[1] ||=
+ (
+ break if c
+ c = false
+ )
+ end.should be_nil
+ end
+
+ it "stops running body if interrupted by break in a begin ... end element op-assign-or value" do
+ c = true
+ a = []
+ while c
+ a[1] ||= begin
+ break if c
+ c = false
+ end
+ end.should be_nil
+ end
+
+ it "stops running body if interrupted by break in a parenthesized element op-assign value" do
+ c = true
+ a = [1, 2]
+ while c
+ a[1] +=
+ (
+ break if c
+ c = false
+ )
+ end.should be_nil
+ a.should == [1, 2]
+ end
+
+ it "stops running body if interrupted by break in a begin ... end element op-assign value" do
+ c = true
+ a = [1, 2]
+ while c
+ a[1] += begin
+ break if c
+ c = false
+ end
+ end.should be_nil
+ a.should == [1, 2]
+ end
+
+ it "stops running body if interrupted by break with unless in a parenthesized attribute op-assign-or value" do
+ a = mock("attribute assignment break")
+ a.should_receive(:m).twice.and_return(nil)
+ a.should_receive(:m=)
+
+ c = d = true
+ while c
+ a.m ||=
+ (
+ break unless d
+ d = false
+ )
+ end.should be_nil
+ end
+
+ it "stops running body if interrupted by break with unless in a begin ... end attribute op-assign-or value" do
+ a = mock("attribute assignment break")
+ a.should_receive(:m).twice.and_return(nil)
+ a.should_receive(:m=)
+
+ c = d = true
+ while c
+ a.m ||= begin
+ break unless d
+ d = false
+ end
+ end.should be_nil
+ end
+
+ it "stops running body if interrupted by break in a parenthesized attribute op-assign-or value" do
+ a = mock("attribute assignment break")
+ a.should_receive(:m).and_return(nil)
+ a.should_not_receive(:m=)
+
+ c = true
+ while c
+ a.m +=
+ (
+ break if c
+ c = false
+ )
+ end.should be_nil
+ end
+
+ it "stops running body if interrupted by break in a begin ... end attribute op-assign-or value" do
+ a = mock("attribute assignment break")
+ a.should_receive(:m).and_return(nil)
+ a.should_not_receive(:m=)
+
+ c = true
+ while c
+ a.m += begin
+ break if c
+ c = false
+ end
+ end.should be_nil
+ end
+
+ it "returns value passed to break if interrupted by break" do
+ while true
+ break 123
+ end.should == 123
+ end
+
+ it "returns nil if interrupted by break with no arguments" do
+ while true
+ break
+ end.should == nil
+ end
+
+ it "skips to end of body with next" do
+ a = []
+ i = 0
+ while (i+=1)<5
+ next if i==3
+ a << i
+ end
+ a.should == [1, 2, 4]
+ end
+
+ it "restarts the current iteration without reevaluating condition with redo" do
+ a = []
+ i = 0
+ j = 0
+ while (i+=1)<3
+ a << i
+ j+=1
+ redo if j<3
+ end
+ a.should == [1, 1, 1, 2]
+ end
+end
+
+describe "The while modifier" do
+ it "runs preceding statement while the condition is true" do
+ i = 0
+ i += 1 while i < 3
+ i.should == 3
+ end
+
+ it "evaluates condition before statement execution" do
+ a = []
+ i = 0
+ a << i while (i+=1) < 3
+ a.should == [1, 2]
+ end
+
+ it "does not run preceding statement if the condition is false" do
+ i = 0
+ i += 1 while false
+ i.should == 0
+ end
+
+ it "does not run preceding statement if the condition is empty" do
+ i = 0
+ i += 1 while ()
+ i.should == 0
+ end
+
+ it "returns nil if ended when condition became false" do
+ i = 0
+ (i += 1 while i<10).should == nil
+ end
+
+ it "returns value passed to break if interrupted by break" do
+ (break 123 while true).should == 123
+ end
+
+ it "returns nil if interrupted by break with no arguments" do
+ (break while true).should == nil
+ end
+
+ it "skips to end of body with next" do
+ i = 0
+ j = 0
+ ((i+=1) == 3 ? next : j+=i) while i <= 10
+ j.should == 63
+ end
+
+ it "restarts the current iteration without reevaluating condition with redo" do
+ i = 0
+ j = 0
+ (i+=1) == 4 ? redo : j+=i while (i+=1) <= 10
+ j.should == 34
+ end
+end
+
+describe "The while modifier with begin .. end block" do
+ it "runs block while the expression is true" do
+ i = 0
+ begin
+ i += 1
+ end while i < 3
+
+ i.should == 3
+ end
+
+ it "stops running block if interrupted by break" do
+ i = 0
+ begin
+ i += 1
+ break if i > 5
+ end while i < 10
+
+ i.should == 6
+ end
+
+ it "returns value passed to break if interrupted by break" do
+ (begin; break 123; end while true).should == 123
+ end
+
+ it "returns nil if interrupted by break with no arguments" do
+ (begin; break; end while true).should == nil
+ end
+
+ it "runs block at least once (even if the expression is false)" do
+ i = 0
+ begin
+ i += 1
+ end while false
+
+ i.should == 1
+ end
+
+ it "evaluates condition after block execution" do
+ a = []
+ i = 0
+ begin
+ a << i
+ end while (i+=1)<5
+ a.should == [0, 1, 2, 3, 4]
+ end
+
+ it "skips to end of body with next" do
+ a = []
+ i = 0
+ begin
+ next if i==3
+ a << i
+ end while (i+=1)<5
+ a.should == [0, 1, 2, 4]
+ end
+
+ it "restarts the current iteration without reevaluting condition with redo" do
+ a = []
+ i = 0
+ j = 0
+ begin
+ a << i
+ j+=1
+ redo if j<3
+ end while (i+=1)<3
+ a.should == [0, 0, 0, 1, 2]
+ end
+end
diff --git a/spec/ruby/language/yield_spec.rb b/spec/ruby/language/yield_spec.rb
new file mode 100644
index 0000000000..8a2aa81819
--- /dev/null
+++ b/spec/ruby/language/yield_spec.rb
@@ -0,0 +1,179 @@
+require_relative '../spec_helper'
+require_relative 'fixtures/yield'
+
+# Note that these specs use blocks defined as { |*a| ... } to capture the
+# arguments with which the block is invoked. This is slightly confusing
+# because the outer Array is a consequence of |*a| but it is necessary to
+# clearly distinguish some behaviors.
+
+describe "The yield call" do
+ before :each do
+ @y = YieldSpecs::Yielder.new
+ end
+
+ describe "taking no arguments" do
+ it "raises a LocalJumpError when the method is not passed a block" do
+ lambda { @y.z }.should raise_error(LocalJumpError)
+ end
+
+ it "ignores assignment to the explicit block argument and calls the passed block" do
+ @y.ze { 42 }.should == 42
+ end
+ end
+
+ describe "taking a single argument" do
+ describe "when no block is given" do
+ it "raises a LocalJumpError" do
+ lambda { @y.s(1) }.should raise_error(LocalJumpError)
+ end
+ end
+
+ describe "yielding to a literal block" do
+ it "passes an empty Array when the argument is an empty Array" do
+ @y.s([]) { |*a| a }.should == [[]]
+ end
+
+ it "passes nil as a value" do
+ @y.s(nil) { |*a| a }.should == [nil]
+ end
+
+ it "passes a single value" do
+ @y.s(1) { |*a| a }.should == [1]
+ end
+
+ it "passes a single, multi-value Array" do
+ @y.s([1, 2, 3]) { |*a| a }.should == [[1, 2, 3]]
+ end
+ end
+
+ describe "yielding to a lambda" do
+ it "passes an empty Array when the argument is an empty Array" do
+ @y.s([], &lambda { |*a| a }).should == [[]]
+ end
+
+ it "passes nil as a value" do
+ @y.s(nil, &lambda { |*a| a }).should == [nil]
+ end
+
+ it "passes a single value" do
+ @y.s(1, &lambda { |*a| a }).should == [1]
+ end
+
+ it "passes a single, multi-value Array" do
+ @y.s([1, 2, 3], &lambda { |*a| a }).should == [[1, 2, 3]]
+ end
+
+ it "raises an ArgumentError if too few arguments are passed" do
+ lambda {
+ @y.s(1, &lambda { |a,b| [a,b] })
+ }.should raise_error(ArgumentError)
+ end
+
+ ruby_bug "#12705", ""..."2.5" do
+ it "should not destructure an Array into multiple arguments" do
+ lambda {
+ @y.s([1, 2], &lambda { |a,b| [a,b] })
+ }.should raise_error(ArgumentError)
+ end
+ end
+ end
+ end
+
+ describe "taking multiple arguments" do
+ it "raises a LocalJumpError when the method is not passed a block" do
+ lambda { @y.m(1, 2, 3) }.should raise_error(LocalJumpError)
+ end
+
+ it "passes the arguments to the block" do
+ @y.m(1, 2, 3) { |*a| a }.should == [1, 2, 3]
+ end
+
+ it "passes only the first argument if the block takes one parameter" do
+ @y.m(1, 2, 3) { |a| a }.should == 1
+ end
+
+ it "raises an ArgumentError if too many arguments are passed to a lambda" do
+ lambda {
+ @y.m(1, 2, 3, &lambda { |a| })
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if too few arguments are passed to a lambda" do
+ lambda {
+ @y.m(1, 2, 3, &lambda { |a,b,c,d| })
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "taking a single splatted argument" do
+ it "raises a LocalJumpError when the method is not passed a block" do
+ lambda { @y.r(0) }.should raise_error(LocalJumpError)
+ end
+
+ it "passes a single value" do
+ @y.r(1) { |*a| a }.should == [1]
+ end
+
+ it "passes no arguments when the argument is an empty Array" do
+ @y.r([]) { |*a| a }.should == []
+ end
+
+ it "passes the value when the argument is an Array containing a single value" do
+ @y.r([1]) { |*a| a }.should == [1]
+ end
+
+ it "passes the values of the Array as individual arguments" do
+ @y.r([1, 2, 3]) { |*a| a }.should == [1, 2, 3]
+ end
+
+ it "passes the element of a single element Array" do
+ @y.r([[1, 2]]) { |*a| a }.should == [[1, 2]]
+ @y.r([nil]) { |*a| a }.should == [nil]
+ @y.r([[]]) { |*a| a }.should == [[]]
+ end
+
+ it "passes no values when give nil as an argument" do
+ @y.r(nil) { |*a| a }.should == []
+ end
+ end
+
+ describe "taking multiple arguments with a splat" do
+ it "raises a LocalJumpError when the method is not passed a block" do
+ lambda { @y.rs(1, 2, [3, 4]) }.should raise_error(LocalJumpError)
+ end
+
+ it "passes the arguments to the block" do
+ @y.rs(1, 2, 3) { |*a| a }.should == [1, 2, 3]
+ end
+
+ it "does not pass an argument value if the splatted argument is an empty Array" do
+ @y.rs(1, 2, []) { |*a| a }.should == [1, 2]
+ end
+
+ it "passes the Array elements as arguments if the splatted argument is a non-empty Array" do
+ @y.rs(1, 2, [3]) { |*a| a }.should == [1, 2, 3]
+ @y.rs(1, 2, [nil]) { |*a| a }.should == [1, 2, nil]
+ @y.rs(1, 2, [[]]) { |*a| a }.should == [1, 2, []]
+ @y.rs(1, 2, [3, 4, 5]) { |*a| a }.should == [1, 2, 3, 4, 5]
+ end
+
+ it "does not pass an argument value if the splatted argument is nil" do
+ @y.rs(1, 2, nil) { |*a| a }.should == [1, 2]
+ end
+ end
+
+ describe "taking matching arguments with splats and post args" do
+ it "raises a LocalJumpError when the method is not passed a block" do
+ lambda { @y.rs(1, 2, [3, 4]) }.should raise_error(LocalJumpError)
+ end
+
+ it "passes the arguments to the block" do
+ @y.rs([1, 2], 3, 4) { |(*a, b), c, d| [a, b, c, d] }.should == [[1], 2, 3, 4]
+ end
+ end
+
+ it "uses captured block of a block used in define_method" do
+ @y.deep(2).should == 4
+ end
+
+end
diff --git a/spec/ruby/library/English/English_spec.rb b/spec/ruby/library/English/English_spec.rb
new file mode 100644
index 0000000000..32941924eb
--- /dev/null
+++ b/spec/ruby/library/English/English_spec.rb
@@ -0,0 +1,171 @@
+require_relative '../../spec_helper'
+
+require 'English'
+
+describe "English" do
+ it "aliases $ERROR_INFO to $!" do
+ begin
+ raise "error"
+ rescue
+ $ERROR_INFO.should_not be_nil
+ $ERROR_INFO.should == $!
+ end
+ $ERROR_INFO.should be_nil
+ end
+
+ it "aliases $ERROR_POSITION to $@" do
+ begin
+ raise "error"
+ rescue
+ $ERROR_POSITION.should_not be_nil
+ $ERROR_POSITION.should == $@
+ end
+ $ERROR_POSITION.should be_nil
+ end
+
+ it "aliases $FS to $;" do
+ original = $;
+ $; = ","
+ $FS.should_not be_nil
+ $FS.should == $;
+ $; = original
+ end
+
+ it "aliases $FIELD_SEPARATOR to $;" do
+ original = $;
+ $; = ","
+ $FIELD_SEPARATOR.should_not be_nil
+ $FIELD_SEPARATOR.should == $;
+ $; = original
+ end
+
+ it "aliases $OFS to $," do
+ original = $,
+ $, = "|"
+ $OFS.should_not be_nil
+ $OFS.should == $,
+ $, = original
+ end
+
+ it "aliases $OUTPUT_FIELD_SEPARATOR to $," do
+ original = $,
+ $, = "|"
+ $OUTPUT_FIELD_SEPARATOR.should_not be_nil
+ $OUTPUT_FIELD_SEPARATOR.should == $,
+ $, = original
+ end
+
+ it "aliases $RS to $/" do
+ $RS.should_not be_nil
+ $RS.should == $/
+ end
+
+ it "aliases $INPUT_RECORD_SEPARATOR to $/" do
+ $INPUT_RECORD_SEPARATOR.should_not be_nil
+ $INPUT_RECORD_SEPARATOR.should == $/
+ end
+
+ it "aliases $ORS to $\\" do
+ original = $\
+ $\ = "\t"
+ $ORS.should_not be_nil
+ $ORS.should == $\
+ $\ = original
+ end
+
+ it "aliases $OUTPUT_RECORD_SEPARATOR to $\\" do
+ original = $\
+ $\ = "\t"
+ $OUTPUT_RECORD_SEPARATOR.should_not be_nil
+ $OUTPUT_RECORD_SEPARATOR.should == $\
+ $\ = original
+ end
+
+ it "aliases $INPUT_LINE_NUMBER to $." do
+ $INPUT_LINE_NUMBER.should_not be_nil
+ $INPUT_LINE_NUMBER.should == $.
+ end
+
+ it "aliases $NR to $." do
+ $NR.should_not be_nil
+ $NR.should == $.
+ end
+
+ it "aliases $LAST_READ_LINE to $_ needs to be reviewed for spec completeness"
+
+ it "aliases $DEFAULT_OUTPUT to $>" do
+ $DEFAULT_OUTPUT.should_not be_nil
+ $DEFAULT_OUTPUT.should == $>
+ end
+
+ it "aliases $DEFAULT_INPUT to $<" do
+ $DEFAULT_INPUT.should_not be_nil
+ $DEFAULT_INPUT.should == $<
+ end
+
+ it "aliases $PID to $$" do
+ $PID.should_not be_nil
+ $PID.should == $$
+ end
+
+ it "aliases $PID to $$" do
+ $PID.should_not be_nil
+ $PID.should == $$
+ end
+
+ it "aliases $PROCESS_ID to $$" do
+ $PROCESS_ID.should_not be_nil
+ $PROCESS_ID.should == $$
+ end
+
+ it "aliases $CHILD_STATUS to $?" do
+ ruby_exe('exit 0')
+ $CHILD_STATUS.should_not be_nil
+ $CHILD_STATUS.should == $?
+ end
+
+ it "aliases $LAST_MATCH_INFO to $~" do
+ /c(a)t/ =~ "cat"
+ $LAST_MATCH_INFO.should_not be_nil
+ $LAST_MATCH_INFO.should == $~
+ end
+
+ it "aliases $IGNORECASE to $=" do
+ $VERBOSE, verbose = nil, $VERBOSE
+ begin
+ $IGNORECASE.should_not be_nil
+ $IGNORECASE.should == $=
+ ensure
+ $VERBOSE = verbose
+ end
+ end
+
+ it "aliases $ARGV to $*" do
+ $ARGV.should_not be_nil
+ $ARGV.should == $*
+ end
+
+ it "aliases $MATCH to $&" do
+ /c(a)t/ =~ "cat"
+ $MATCH.should_not be_nil
+ $MATCH.should == $&
+ end
+
+ it "aliases $PREMATCH to $`" do
+ /c(a)t/ =~ "cat"
+ $PREMATCH.should_not be_nil
+ $PREMATCH.should == $`
+ end
+
+ it "aliases $POSTMATCH to $'" do
+ /c(a)t/ =~ "cat"
+ $POSTMATCH.should_not be_nil
+ $POSTMATCH.should == $'
+ end
+
+ it "aliases $LAST_PAREN_MATCH to $+" do
+ /c(a)t/ =~ "cat"
+ $LAST_PAREN_MATCH.should_not be_nil
+ $LAST_PAREN_MATCH.should == $+
+ end
+end
diff --git a/spec/ruby/library/abbrev/abbrev_spec.rb b/spec/ruby/library/abbrev/abbrev_spec.rb
new file mode 100644
index 0000000000..60d1a953b3
--- /dev/null
+++ b/spec/ruby/library/abbrev/abbrev_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require 'abbrev'
+
+#test both Abbrev.abbrev and Array#abbrev in
+#the same manner, as they're more or less aliases
+#of one another
+
+[["Abbrev.abbrev", lambda {|a| Abbrev.abbrev(a)}],
+ ["Array#abbrev", lambda {|a| a.abbrev}]
+].each do |(name, func)|
+
+ describe name do
+ it "returns a hash of all unambiguous abbreviations of the array of strings passed in" do
+ func.call(['ruby', 'rules']).should == {"rub" => "ruby",
+ "ruby" => "ruby",
+ "rul" => "rules",
+ "rule" => "rules",
+ "rules" => "rules"}
+
+ func.call(["car", "cone"]).should == {"ca" => "car",
+ "car" => "car",
+ "co" => "cone",
+ "con" => "cone",
+ "cone" => "cone"}
+ end
+
+ it "returns an empty hash when called on an empty array" do
+ func.call([]).should == {}
+ end
+ end
+end
diff --git a/spec/ruby/library/base64/decode64_spec.rb b/spec/ruby/library/base64/decode64_spec.rb
new file mode 100644
index 0000000000..3b81203588
--- /dev/null
+++ b/spec/ruby/library/base64/decode64_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+require 'base64'
+
+describe "Base64#decode64" do
+ it "returns the Base64-decoded version of the given string" do
+ Base64.decode64("U2VuZCByZWluZm9yY2VtZW50cw==\n").should == "Send reinforcements"
+ end
+end
diff --git a/spec/ruby/library/base64/encode64_spec.rb b/spec/ruby/library/base64/encode64_spec.rb
new file mode 100644
index 0000000000..91ac41bed0
--- /dev/null
+++ b/spec/ruby/library/base64/encode64_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+require 'base64'
+
+describe "Base64#encode64" do
+ it "returns the Base64-encoded version of the given string" do
+ Base64.encode64("Now is the time for all good coders\nto learn Ruby").should ==
+ "Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g\nUnVieQ==\n"
+ end
+
+ it "returns the Base64-encoded version of the given string" do
+ Base64.encode64('Send reinforcements').should == "U2VuZCByZWluZm9yY2VtZW50cw==\n"
+ end
+end
diff --git a/spec/ruby/library/base64/urlsafe_decode64_spec.rb b/spec/ruby/library/base64/urlsafe_decode64_spec.rb
new file mode 100644
index 0000000000..1b813ee1b9
--- /dev/null
+++ b/spec/ruby/library/base64/urlsafe_decode64_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+require 'base64'
+
+describe "Base64#urlsafe_decode64" do
+ it "uses '_' instead of '/'" do
+ decoded = Base64.urlsafe_decode64("V2hlcmUgYW0gST8gV2hvIGFtIEk_IEFtIEk_IEk_")
+ decoded.should == 'Where am I? Who am I? Am I? I?'
+ end
+
+ it "uses '-' instead of '+'" do
+ decoded = Base64.urlsafe_decode64('IkJlaW5nIGRpc2ludGVncmF0ZWQgbWFrZXMgbWUgdmUtcnkgYW4tZ3J5ISIgPGh1ZmYsIGh1ZmY-')
+ decoded.should == '"Being disintegrated makes me ve-ry an-gry!" <huff, huff>'
+ end
+
+ it "does not require padding" do
+ Base64.urlsafe_decode64("MQ").should == "1"
+ end
+end
diff --git a/spec/ruby/library/base64/urlsafe_encode64_spec.rb b/spec/ruby/library/base64/urlsafe_encode64_spec.rb
new file mode 100644
index 0000000000..de1f235cea
--- /dev/null
+++ b/spec/ruby/library/base64/urlsafe_encode64_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+
+require 'base64'
+
+describe "Base64#urlsafe_encode64" do
+ it "uses '_' instead of '/'" do
+ encoded = Base64.urlsafe_encode64('Where am I? Who am I? Am I? I?')
+ encoded.should == "V2hlcmUgYW0gST8gV2hvIGFtIEk_IEFtIEk_IEk_"
+ end
+
+ it "uses '-' instead of '+'" do
+ encoded = Base64.urlsafe_encode64('"Being disintegrated makes me ve-ry an-gry!" <huff, huff>')
+ encoded.should == 'IkJlaW5nIGRpc2ludGVncmF0ZWQgbWFrZXMgbWUgdmUtcnkgYW4tZ3J5ISIgPGh1ZmYsIGh1ZmY-'
+ end
+
+ it "makes padding optional" do
+ Base64.urlsafe_encode64("1", padding: false).should == "MQ"
+ Base64.urlsafe_encode64("1").should == "MQ=="
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/BigDecimal_spec.rb b/spec/ruby/library/bigdecimal/BigDecimal_spec.rb
new file mode 100644
index 0000000000..52f026fb2b
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/BigDecimal_spec.rb
@@ -0,0 +1,132 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "Kernel#BigDecimal" do
+
+ it "creates a new object of class BigDecimal" do
+ BigDecimal("3.14159").should be_kind_of(BigDecimal)
+ (0..9).each {|i|
+ BigDecimal("1#{i}").should == 10 + i
+ BigDecimal("-1#{i}").should == -10 - i
+ BigDecimal("1E#{i}").should == 10**i
+ BigDecimal("1000000E-#{i}").should == 10**(6-i).to_f
+ # ^ to_f to avoid Rational type
+ }
+ (1..9).each {|i|
+ BigDecimal("100.#{i}").to_s.should =~ /\A0\.100#{i}E3\z/i
+ BigDecimal("-100.#{i}").to_s.should =~ /\A-0\.100#{i}E3\z/i
+ }
+ end
+
+ it "accepts significant digits >= given precision" do
+ BigDecimal("3.1415923", 10).precs[1].should >= 10
+ end
+
+ it "determines precision from initial value" do
+ pi_string = "3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593014782083152134043"
+ BigDecimal(pi_string).precs[1].should >= pi_string.size-1
+ end
+
+ it "ignores leading whitespace" do
+ BigDecimal(" \t\n \r1234").should == BigDecimal("1234")
+ BigDecimal(" \t\n \rNaN \n").nan?.should == true
+ BigDecimal(" \t\n \rInfinity \n").infinite?.should == 1
+ BigDecimal(" \t\n \r-Infinity \n").infinite?.should == -1
+ end
+
+ ruby_version_is ""..."2.6" do
+ it "ignores trailing garbage" do
+ BigDecimal("123E45ruby").should == BigDecimal("123E45")
+ BigDecimal("123x45").should == BigDecimal("123")
+ BigDecimal("123.4%E5").should == BigDecimal("123.4")
+ BigDecimal("1E2E3E4E5E").should == BigDecimal("100")
+ end
+ end
+
+ ruby_version_is "2.6" do
+ it "does not ignores trailing garbage" do
+ lambda { BigDecimal("123E45ruby") }.should raise_error(ArgumentError)
+ lambda { BigDecimal("123x45") }.should raise_error(ArgumentError)
+ lambda { BigDecimal("123.4%E5") }.should raise_error(ArgumentError)
+ lambda { BigDecimal("1E2E3E4E5E") }.should raise_error(ArgumentError)
+ end
+ end
+
+ ruby_version_is ""..."2.4" do
+ it "treats invalid strings as 0.0" do
+ BigDecimal("ruby").should == BigDecimal("0.0")
+ BigDecimal(" \t\n \r-\t\t\tInfinity \n").should == BigDecimal("0.0")
+ end
+ end
+
+ ruby_version_is "2.4" do
+ it "raises ArgumentError for invalid strings" do
+ lambda { BigDecimal("ruby") }.should raise_error(ArgumentError)
+ lambda { BigDecimal(" \t\n \r-\t\t\tInfinity \n") }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "allows omitting the integer part" do
+ BigDecimal(".123").should == BigDecimal("0.123")
+ end
+
+ ruby_version_is ""..."2.6" do
+ it "allows for underscores in all parts" do
+ reference = BigDecimal("12345.67E89")
+
+ BigDecimal("12_345.67E89").should == reference
+ BigDecimal("1_2_3_4_5_._6____7_E89").should == reference
+ BigDecimal("12345_.67E_8__9_").should == reference
+ end
+ end
+
+ ruby_version_is "2.6" do
+ it "process underscores as Float()" do
+ reference = BigDecimal("12345.67E89")
+
+ BigDecimal("12_345.67E89").should == reference
+ lambda { BigDecimal("1_2_3_4_5_._6____7_E89") }.should raise_error(ArgumentError)
+ lambda { BigDecimal("12345_.67E_8__9_") }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "accepts NaN and [+-]Infinity" do
+ BigDecimal("NaN").nan?.should == true
+
+ pos_inf = BigDecimal("Infinity")
+ pos_inf.finite?.should == false
+ pos_inf.should > 0
+ pos_inf.should == BigDecimal("+Infinity")
+
+ neg_inf = BigDecimal("-Infinity")
+ neg_inf.finite?.should == false
+ neg_inf.should < 0
+ end
+
+ it "allows for [eEdD] as exponent separator" do
+ reference = BigDecimal("12345.67E89")
+
+ BigDecimal("12345.67e89").should == reference
+ BigDecimal("12345.67E89").should == reference
+ BigDecimal("12345.67d89").should == reference
+ BigDecimal("12345.67D89").should == reference
+ end
+
+ it "allows for varying signs" do
+ reference = BigDecimal("123.456E1")
+
+ BigDecimal("+123.456E1").should == reference
+ BigDecimal("-123.456E1").should == -reference
+ BigDecimal("123.456E+1").should == reference
+ BigDecimal("12345.6E-1").should == reference
+ BigDecimal("+123.456E+1").should == reference
+ BigDecimal("+12345.6E-1").should == reference
+ BigDecimal("-123.456E+1").should == -reference
+ BigDecimal("-12345.6E-1").should == -reference
+ end
+
+ it 'raises ArgumentError when Float is used without precision' do
+ lambda { BigDecimal(1.0) }.should raise_error(ArgumentError)
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/abs_spec.rb b/spec/ruby/library/bigdecimal/abs_spec.rb
new file mode 100644
index 0000000000..ddd2bae9a9
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/abs_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#abs" do
+ before :each do
+ @one = BigDecimal("1")
+ @zero = BigDecimal("0")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+ @two = BigDecimal("2")
+ @three = BigDecimal("3")
+ @mixed = BigDecimal("1.23456789")
+ @nan = BigDecimal("NaN")
+ @infinity = BigDecimal("Infinity")
+ @infinity_minus = BigDecimal("-Infinity")
+ @one_minus = BigDecimal("-1")
+ @frac_1 = BigDecimal("1E-99999")
+ @frac_2 = BigDecimal("0.9E-99999")
+ end
+
+ it "returns the absolute value" do
+ pos_int = BigDecimal("2E5555")
+ neg_int = BigDecimal("-2E5555")
+ pos_frac = BigDecimal("2E-9999")
+ neg_frac = BigDecimal("-2E-9999")
+
+ pos_int.abs.should == pos_int
+ neg_int.abs.should == pos_int
+ pos_frac.abs.should == pos_frac
+ neg_frac.abs.should == pos_frac
+ @one.abs.should == 1
+ @two.abs.should == 2
+ @three.abs.should == 3
+ @mixed.abs.should == @mixed
+ @one_minus.abs.should == @one
+ end
+
+ it "properly handles special values" do
+ @infinity.abs.should == @infinity
+ @infinity_minus.abs.should == @infinity
+ @nan.abs.nan?.should == true # have to do it this way, since == doesn't work on NaN
+ @zero.abs.should == 0
+ @zero.abs.sign.should == BigDecimal::SIGN_POSITIVE_ZERO
+ @zero_pos.abs.should == 0
+ @zero_pos.abs.sign.should == BigDecimal::SIGN_POSITIVE_ZERO
+ @zero_neg.abs.should == 0
+ @zero_neg.abs.sign.should == BigDecimal::SIGN_POSITIVE_ZERO
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/add_spec.rb b/spec/ruby/library/bigdecimal/add_spec.rb
new file mode 100644
index 0000000000..024dd576cc
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/add_spec.rb
@@ -0,0 +1,179 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+require 'bigdecimal'
+
+describe "BigDecimal#add" do
+
+ before :each do
+ @one = BigDecimal("1")
+ @zero = BigDecimal("0")
+ @two = BigDecimal("2")
+ @three = BigDecimal("3")
+ @ten = BigDecimal("10")
+ @eleven = BigDecimal("11")
+ @nan = BigDecimal("NaN")
+ @infinity = BigDecimal("Infinity")
+ @infinity_minus = BigDecimal("-Infinity")
+ @one_minus = BigDecimal("-1")
+ @frac_1 = BigDecimal("1E-99999")
+ @frac_2 = BigDecimal("0.9E-99999")
+ @frac_3 = BigDecimal("12345E10")
+ @frac_4 = BigDecimal("98765E10")
+ @dot_ones = BigDecimal("0.1111111111")
+ end
+
+ it "returns a + b with given precision" do
+ # documentation states, that precision ist optional, but it ain't,
+ @two.add(@one, 1).should == @three
+ @one .add(@two, 1).should == @three
+ @one.add(@one_minus, 1).should == @zero
+ @ten.add(@one, 2).should == @eleven
+ @zero.add(@one, 1).should == @one
+ @frac_2.add(@frac_1, 10000).should == BigDecimal("1.9E-99999")
+ @frac_1.add(@frac_1, 10000).should == BigDecimal("2E-99999")
+ @frac_3.add(@frac_4, 0).should == BigDecimal("0.11111E16")
+ @frac_3.add(@frac_4, 1).should == BigDecimal("0.1E16")
+ @frac_3.add(@frac_4, 2).should == BigDecimal("0.11E16")
+ @frac_3.add(@frac_4, 3).should == BigDecimal("0.111E16")
+ @frac_3.add(@frac_4, 4).should == BigDecimal("0.1111E16")
+ @frac_3.add(@frac_4, 5).should == BigDecimal("0.11111E16")
+ @frac_3.add(@frac_4, 6).should == BigDecimal("0.11111E16")
+ end
+
+ it "returns a + [Fixnum value] with given precision" do
+ (1..10).each {|precision|
+ @dot_ones.add(0, precision).should == BigDecimal("0." + "1" * precision)
+ }
+ BigDecimal("0.88").add(0, 1).should == BigDecimal("0.9")
+ end
+
+ it "returns a + [Bignum value] with given precision" do
+ bignum = 10000000000000000000
+ (1..20).each {|precision|
+ @dot_ones.add(bignum, precision).should == BigDecimal("0.1E20")
+ }
+ (21..30).each {|precision|
+ @dot_ones.add(bignum, precision).should == BigDecimal(
+ "0.10000000000000000000" + "1" * (precision - 20) + "E20")
+ }
+ end
+
+# TODO:
+# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/17374
+#
+# This doesn't work on MRI and looks like a bug to me:
+# one can use BigDecimal + Float, but not Bigdecimal.add(Float)
+#
+# it "returns a + [Float value] with given precision" do
+# (1..10).each {|precision|
+# @dot_ones.add(0.0, precision).should == BigDecimal("0." + "1" * precision)
+# }
+#
+# BigDecimal("0.88").add(0.0, 1).should == BigDecimal("0.9")
+# end
+
+ it "favors the precision specified in the second argument over the global limit" do
+ BigDecimalSpecs.with_limit(1) do
+ BigDecimal('0.888').add(@zero, 3).should == BigDecimal('0.888')
+ end
+
+ BigDecimalSpecs.with_limit(2) do
+ BigDecimal('0.888').add(@zero, 1).should == BigDecimal('0.9')
+ end
+ end
+
+ it "uses the current rounding mode if rounding is needed" do
+ BigDecimalSpecs.with_rounding(BigDecimal::ROUND_UP) do
+ BigDecimal('0.111').add(@zero, 1).should == BigDecimal('0.2')
+ BigDecimal('-0.111').add(@zero, 1).should == BigDecimal('-0.2')
+ end
+ BigDecimalSpecs.with_rounding(BigDecimal::ROUND_DOWN) do
+ BigDecimal('0.999').add(@zero, 1).should == BigDecimal('0.9')
+ BigDecimal('-0.999').add(@zero, 1).should == BigDecimal('-0.9')
+ end
+ BigDecimalSpecs.with_rounding(BigDecimal::ROUND_HALF_UP) do
+ BigDecimal('0.85').add(@zero, 1).should == BigDecimal('0.9')
+ BigDecimal('-0.85').add(@zero, 1).should == BigDecimal('-0.9')
+ end
+ BigDecimalSpecs.with_rounding(BigDecimal::ROUND_HALF_DOWN) do
+ BigDecimal('0.85').add(@zero, 1).should == BigDecimal('0.8')
+ BigDecimal('-0.85').add(@zero, 1).should == BigDecimal('-0.8')
+ end
+ BigDecimalSpecs.with_rounding(BigDecimal::ROUND_HALF_EVEN) do
+ BigDecimal('0.75').add(@zero, 1).should == BigDecimal('0.8')
+ BigDecimal('0.85').add(@zero, 1).should == BigDecimal('0.8')
+ BigDecimal('-0.75').add(@zero, 1).should == BigDecimal('-0.8')
+ BigDecimal('-0.85').add(@zero, 1).should == BigDecimal('-0.8')
+ end
+ BigDecimalSpecs.with_rounding(BigDecimal::ROUND_CEILING) do
+ BigDecimal('0.85').add(@zero, 1).should == BigDecimal('0.9')
+ BigDecimal('-0.85').add(@zero, 1).should == BigDecimal('-0.8')
+ end
+ BigDecimalSpecs.with_rounding(BigDecimal::ROUND_FLOOR) do
+ BigDecimal('0.85').add(@zero, 1).should == BigDecimal('0.8')
+ BigDecimal('-0.85').add(@zero, 1).should == BigDecimal('-0.9')
+ end
+ end
+
+ it "uses the default ROUND_HALF_UP rounding if it wasn't explicitly changed" do
+ BigDecimal('0.85').add(@zero, 1).should == BigDecimal('0.9')
+ BigDecimal('-0.85').add(@zero, 1).should == BigDecimal('-0.9')
+ end
+
+ it "returns NaN if NaN is involved" do
+ @one.add(@nan, 10000).nan?.should == true
+ @nan.add(@one, 1).nan?.should == true
+ end
+
+ it "returns Infinity or -Infinity if these are involved" do
+ @zero.add(@infinity, 1).should == @infinity
+ @frac_2.add(@infinity, 1).should == @infinity
+ @one_minus.add(@infinity, 1).should == @infinity
+ @two.add(@infinity, 1).should == @infinity
+
+ @zero.add(@infinity_minus, 1).should == @infinity_minus
+ @frac_2.add(@infinity_minus, 1).should == @infinity_minus
+ @one_minus.add(@infinity_minus, 1).should == @infinity_minus
+ @two.add(@infinity_minus, 1).should == @infinity_minus
+
+ @infinity.add(@zero, 1).should == @infinity
+ @infinity.add(@frac_2, 1).should == @infinity
+ @infinity.add(@one_minus, 1).should == @infinity
+ @infinity.add(@two, 1).should == @infinity
+
+ @infinity_minus.add(@zero, 1).should == @infinity_minus
+ @infinity_minus.add(@frac_2, 1).should == @infinity_minus
+ @infinity_minus.add(@one_minus, 1).should == @infinity_minus
+ @infinity_minus.add(@two, 1).should == @infinity_minus
+
+ @infinity.add(@infinity, 10000).should == @infinity
+ @infinity_minus.add(@infinity_minus, 10000).should == @infinity_minus
+ end
+
+ it "returns NaN if Infinity + (- Infinity)" do
+ @infinity.add(@infinity_minus, 10000).nan?.should == true
+ @infinity_minus.add(@infinity, 10000).nan?.should == true
+ end
+
+ it "raises TypeError when adds nil" do
+ lambda {
+ @one.add(nil, 10)
+ }.should raise_error(TypeError)
+ lambda {
+ @one.add(nil, 0)
+ }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError when precision parameter is nil" do
+ lambda {
+ @one.add(@one, nil)
+ }.should raise_error(TypeError)
+ end
+
+ it "raises ArgumentError when precision parameter is negative" do
+ lambda {
+ @one.add(@one, -10)
+ }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/case_compare_spec.rb b/spec/ruby/library/bigdecimal/case_compare_spec.rb
new file mode 100644
index 0000000000..fac6714356
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/case_compare_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/eql'
+
+
+describe "BigDecimal#===" do
+ it_behaves_like :bigdecimal_eql, :===
+end
diff --git a/spec/ruby/library/bigdecimal/ceil_spec.rb b/spec/ruby/library/bigdecimal/ceil_spec.rb
new file mode 100644
index 0000000000..879c4fcf36
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/ceil_spec.rb
@@ -0,0 +1,104 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#ceil" do
+ before :each do
+ @zero = BigDecimal("0")
+ @one = BigDecimal("1")
+ @three = BigDecimal("3")
+ @four = BigDecimal("4")
+ @mixed = BigDecimal("1.23456789")
+ @mixed_big = BigDecimal("1.23456789E100")
+ @pos_int = BigDecimal("2E5555")
+ @neg_int = BigDecimal("-2E5555")
+ @pos_frac = BigDecimal("2E-9999")
+ @neg_frac = BigDecimal("-2E-9999")
+
+ @infinity = BigDecimal("Infinity")
+ @infinity_neg = BigDecimal("-Infinity")
+ @nan = BigDecimal("NaN")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+ end
+
+ it "returns an Integer, if n is unspecified" do
+ @mixed.ceil.kind_of?(Integer).should == true
+ end
+
+ it "returns a BigDecimal, if n is specified" do
+ @pos_int.ceil(2).kind_of?(BigDecimal).should == true
+ end
+
+ it "returns the smallest integer greater or equal to self, if n is unspecified" do
+ @pos_int.ceil.should == @pos_int
+ @neg_int.ceil.should == @neg_int
+ @pos_frac.ceil.should == BigDecimal("1")
+ @neg_frac.ceil.should == @zero
+ @zero.ceil.should == 0
+ @zero_pos.ceil.should == @zero_pos
+ @zero_neg.ceil.should == @zero_neg
+
+
+ BigDecimal('2.3').ceil.should == 3
+ BigDecimal('2.5').ceil.should == 3
+ BigDecimal('2.9999').ceil.should == 3
+ BigDecimal('-2.3').ceil.should == -2
+ BigDecimal('-2.5').ceil.should == -2
+ BigDecimal('-2.9999').ceil.should == -2
+ end
+
+ it "raise exception, if self is special value" do
+ lambda { @infinity.ceil }.should raise_error(FloatDomainError)
+ lambda { @infinity_neg.ceil }.should raise_error(FloatDomainError)
+ lambda { @nan.ceil }.should raise_error(FloatDomainError)
+ end
+
+ it "returns n digits right of the decimal point if given n > 0" do
+ @mixed.ceil(1).should == BigDecimal("1.3")
+ @mixed.ceil(5).should == BigDecimal("1.23457")
+
+ BigDecimal("-0.03").ceil(1).should == BigDecimal("0")
+ BigDecimal("0.03").ceil(1).should == BigDecimal("0.1")
+
+ BigDecimal("23.45").ceil(0).should == BigDecimal('24')
+ BigDecimal("23.45").ceil(1).should == BigDecimal('23.5')
+ BigDecimal("23.45").ceil(2).should == BigDecimal('23.45')
+
+ BigDecimal("-23.45").ceil(0).should == BigDecimal('-23')
+ BigDecimal("-23.45").ceil(1).should == BigDecimal('-23.4')
+ BigDecimal("-23.45").ceil(2).should == BigDecimal('-23.45')
+
+ BigDecimal("2E-10").ceil(0).should == @one
+ BigDecimal("2E-10").ceil(9).should == BigDecimal('1E-9')
+ BigDecimal("2E-10").ceil(10).should == BigDecimal('2E-10')
+ BigDecimal("2E-10").ceil(11).should == BigDecimal('2E-10')
+
+ (1..10).each do |n|
+ # 0.4, 0.34, 0.334, etc.
+ (@one.div(@three,20)).ceil(n).should == BigDecimal("0.#{'3'*(n-1)}4")
+ # 1.4, 1.34, 1.334, etc.
+ (@four.div(@three,20)).ceil(n).should == BigDecimal("1.#{'3'*(n-1)}4")
+ (BigDecimal('31').div(@three,20)).ceil(n).should == BigDecimal("10.#{'3'*(n-1)}4")
+ end
+ (1..10).each do |n|
+ # -0.4, -0.34, -0.334, etc.
+ (-@one.div(@three,20)).ceil(n).should == BigDecimal("-0.#{'3'* n}")
+ end
+ (1..10).each do |n|
+ (@three.div(@one,20)).ceil(n).should == @three
+ end
+ (1..10).each do |n|
+ (-@three.div(@one,20)).ceil(n).should == -@three
+ end
+ end
+
+ it "sets n digits left of the decimal point to 0, if given n < 0" do
+ BigDecimal("13345.234").ceil(-2).should == BigDecimal("13400.0")
+ @mixed_big.ceil(-99).should == BigDecimal("0.13E101")
+ @mixed_big.ceil(-100).should == BigDecimal("0.2E101")
+ @mixed_big.ceil(-95).should == BigDecimal("0.123457E101")
+ BigDecimal("1E10").ceil(-30).should == BigDecimal('1E30')
+ BigDecimal("-1E10").ceil(-30).should == @zero
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/coerce_spec.rb b/spec/ruby/library/bigdecimal/coerce_spec.rb
new file mode 100644
index 0000000000..1e5c73f969
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/coerce_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#coerce" do
+
+ it "returns [other, self] both as BigDecimal" do
+ one = BigDecimal("1.0")
+ five_point_28 = BigDecimal("5.28")
+ zero_minus = BigDecimal("-0.0")
+ some_value = 32434234234234234234
+
+ BigDecimal("1.2").coerce(1).should == [one, BigDecimal("1.2")]
+ five_point_28.coerce(1.0).should == [one, BigDecimal("5.28")]
+ one.coerce(one).should == [one, one]
+ one.coerce(2.5).should == [2.5, one]
+ BigDecimal("1").coerce(3.14).should == [3.14, one]
+ a, b = zero_minus.coerce(some_value)
+ a.should == BigDecimal(some_value.to_s)
+ b.should == zero_minus
+ a, b = one.coerce(some_value)
+ a.should == BigDecimal(some_value.to_s)
+ b.to_f.should be_close(1.0, TOLERANCE) # can we take out the to_f once BigDecimal#- is implemented?
+ b.should == one
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/comparison_spec.rb b/spec/ruby/library/bigdecimal/comparison_spec.rb
new file mode 100644
index 0000000000..a1e09b601f
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/comparison_spec.rb
@@ -0,0 +1,81 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#<=>" do
+ before :each do
+ @zero = BigDecimal("0")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+ @mixed = BigDecimal("1.23456789")
+ @mixed_big = BigDecimal("1.23456789E100")
+ @pos_int = BigDecimal("2E5555")
+ @neg_int = BigDecimal("-2E5555")
+ @pos_frac = BigDecimal("2E-9999")
+ @neg_frac = BigDecimal("-2E-9999")
+
+ @int_mock = mock('123')
+ class << @int_mock
+ def coerce(other)
+ return [other, BigDecimal('123')]
+ end
+ def >= (other)
+ BigDecimal('123') >= other
+ end
+ end
+
+ @values = [@mixed, @pos_int, @neg_int, @pos_frac, @neg_frac,
+ -2**32, -2**31, -2**30, -2**16, -2**8, -100, -10, -1,
+ @zero , 1, 2, 10, 2**8, 2**16, 2**32, @int_mock, @zero_pos, @zero_neg]
+
+ @infinity = BigDecimal("Infinity")
+ @infinity_neg = BigDecimal("-Infinity")
+ @nan = BigDecimal("NaN")
+ end
+
+
+ it "returns 0 if a == b" do
+ (@pos_int <=> @pos_int).should == 0
+ (@neg_int <=> @neg_int).should == 0
+ (@pos_frac <=> @pos_frac).should == 0
+ (@neg_frac <=> @neg_frac).should == 0
+ (@zero <=> @zero).should == 0
+ (@infinity <=> @infinity).should == 0
+ (@infinity_neg <=> @infinity_neg).should == 0
+ end
+
+ it "returns 1 if a > b" do
+ (@pos_int <=> @neg_int).should == 1
+ (@pos_frac <=> @neg_frac).should == 1
+ (@pos_frac <=> @zero).should == 1
+ @values.each { |val|
+ (@infinity <=> val).should == 1
+ }
+ end
+
+ it "returns -1 if a < b" do
+ (@zero <=> @pos_frac).should == -1
+ (@neg_int <=> @pos_frac).should == -1
+ (@pos_frac <=> @pos_int).should == -1
+ @values.each { |val|
+ (@infinity_neg <=> val).should == -1
+ }
+ end
+
+ it "returns nil if NaN is involved" do
+ @values += [@infinity, @infinity_neg, @nan]
+ @values << nil
+ @values << Object.new
+ @values.each { |val|
+ (@nan <=> val).should == nil
+ }
+ end
+
+ it "returns nil if the argument is nil" do
+ (@zero <=> nil).should == nil
+ (@infinity <=> nil).should == nil
+ (@infinity_neg <=> nil).should == nil
+ (@mixed <=> nil).should == nil
+ (@pos_int <=> nil).should == nil
+ (@neg_frac <=> nil).should == nil
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/div_spec.rb b/spec/ruby/library/bigdecimal/div_spec.rb
new file mode 100644
index 0000000000..a774376f55
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/div_spec.rb
@@ -0,0 +1,102 @@
+require_relative '../../spec_helper'
+require_relative 'shared/quo'
+require 'bigdecimal'
+
+describe "BigDecimal#div with precision set to 0" do
+ # TODO: figure out if there is a better way to do these
+ # shared specs rather than sending [0]. See other specs
+ # that share :bigdecimal_quo.
+ it_behaves_like :bigdecimal_quo, :div, [0]
+end
+
+describe "BigDecimal#div" do
+
+ before :each do
+ @one = BigDecimal("1")
+ @zero = BigDecimal("0")
+ @zero_plus = BigDecimal("+0")
+ @zero_minus = BigDecimal("-0")
+ @two = BigDecimal("2")
+ @three = BigDecimal("3")
+ @nan = BigDecimal("NaN")
+ @infinity = BigDecimal("Infinity")
+ @infinity_minus = BigDecimal("-Infinity")
+ @one_minus = BigDecimal("-1")
+ @frac_1 = BigDecimal("1E-99999")
+ @frac_2 = BigDecimal("0.9E-99999")
+ end
+
+ it "returns a / b with optional precision" do
+ @two.div(@one).should == @two
+ @one.div(@two).should == @zero
+ # ^^ is this really intended for a class with arbitrary precision?
+ @one.div(@two, 1).should == BigDecimal("0.5")
+ @one.div(@one_minus).should == @one_minus
+ @one_minus.div(@one_minus).should == @one
+ @frac_2.div(@frac_1, 1).should == BigDecimal("0.9")
+ @frac_1.div(@frac_1).should == @one
+
+ res = "0." + "3" * 1000
+ (1..100).each { |idx|
+ @one.div(@three, idx).to_s("F").should == "0." + res[2, idx]
+ }
+ end
+
+ it "raises FloatDomainError if NaN is involved" do
+ lambda { @one.div(@nan) }.should raise_error(FloatDomainError)
+ lambda { @nan.div(@one) }.should raise_error(FloatDomainError)
+ lambda { @nan.div(@nan) }.should raise_error(FloatDomainError)
+ end
+
+ it "returns 0 if divided by Infinity and no precision given" do
+ @zero.div(@infinity).should == 0
+ @frac_2.div(@infinity).should == 0
+ end
+
+ it "returns 0 if divided by Infinity with given precision" do
+ @zero.div(@infinity, 0).should == 0
+ @frac_2.div(@infinity, 1).should == 0
+ @zero.div(@infinity, 100000).should == 0
+ @frac_2.div(@infinity, 100000).should == 0
+ end
+
+ it "raises ZeroDivisionError if divided by zero and no precision given" do
+ lambda { @one.div(@zero) }.should raise_error(ZeroDivisionError)
+ lambda { @one.div(@zero_plus) }.should raise_error(ZeroDivisionError)
+ lambda { @one.div(@zero_minus) }.should raise_error(ZeroDivisionError)
+
+ lambda { @zero.div(@zero) }.should raise_error(ZeroDivisionError)
+ lambda { @zero_minus.div(@zero_plus) }.should raise_error(ZeroDivisionError)
+ lambda { @zero_minus.div(@zero_minus) }.should raise_error(ZeroDivisionError)
+ lambda { @zero_plus.div(@zero_minus) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "returns NaN if zero is divided by zero" do
+ @zero.div(@zero, 0).nan?.should == true
+ @zero_minus.div(@zero_plus, 0).nan?.should == true
+ @zero_plus.div(@zero_minus, 0).nan?.should == true
+
+ @zero.div(@zero, 10).nan?.should == true
+ @zero_minus.div(@zero_plus, 10).nan?.should == true
+ @zero_plus.div(@zero_minus, 10).nan?.should == true
+ end
+
+ it "raises FloatDomainError if (+|-) Infinity divided by 1 and no precision given" do
+ lambda { @infinity_minus.div(@one) }.should raise_error(FloatDomainError)
+ lambda { @infinity.div(@one) }.should raise_error(FloatDomainError)
+ lambda { @infinity_minus.div(@one_minus) }.should raise_error(FloatDomainError)
+ end
+
+ it "returns (+|-)Infinity if (+|-)Infinity by 1 and precision given" do
+ @infinity_minus.div(@one, 0).should == @infinity_minus
+ @infinity.div(@one, 0).should == @infinity
+ @infinity_minus.div(@one_minus, 0).should == @infinity
+ end
+
+ it "returns NaN if Infinity / ((+|-) Infinity)" do
+ @infinity.div(@infinity_minus, 100000).nan?.should == true
+ @infinity_minus.div(@infinity, 1).nan?.should == true
+ end
+
+
+end
diff --git a/spec/ruby/library/bigdecimal/divide_spec.rb b/spec/ruby/library/bigdecimal/divide_spec.rb
new file mode 100644
index 0000000000..2aac99799f
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/divide_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/quo'
+require 'bigdecimal'
+
+describe "BigDecimal#/" do
+ it_behaves_like :bigdecimal_quo, :/, []
+end
diff --git a/spec/ruby/library/bigdecimal/divmod_spec.rb b/spec/ruby/library/bigdecimal/divmod_spec.rb
new file mode 100644
index 0000000000..3a18b150dd
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/divmod_spec.rb
@@ -0,0 +1,180 @@
+require_relative '../../spec_helper'
+require_relative 'shared/modulo'
+require 'bigdecimal'
+
+module DivmodSpecs
+ def self.check_both_nan(array)
+ array.length.should == 2
+ array[0].nan?.should == true
+ array[1].nan?.should == true
+ end
+ def self.check_both_bigdecimal(array)
+ array.length.should == 2
+ array[0].kind_of?(BigDecimal).should == true
+ array[1].kind_of?(BigDecimal).should == true
+ end
+end
+
+# TODO: figure out a way to do the shared specs with helpers instead
+# of spec'ing a method that does not really exist
+describe "BigDecimal#mod_part_of_divmod" do
+ # BigDecimal#divmod[1] behaves exactly like #modulo
+ before :all do
+ class BigDecimal
+ def mod_part_of_divmod(arg)
+ divmod(arg)[1]
+ end
+ end
+ end
+
+ after :all do
+ class BigDecimal
+ undef mod_part_of_divmod
+ end
+ end
+
+ it_behaves_like :bigdecimal_modulo, :mod_part_of_divmod
+
+ it "raises ZeroDivisionError if other is zero" do
+ bd5667 = BigDecimal("5667.19")
+
+ lambda { bd5667.mod_part_of_divmod(0) }.should raise_error(ZeroDivisionError)
+ lambda { bd5667.mod_part_of_divmod(BigDecimal("0")) }.should raise_error(ZeroDivisionError)
+ lambda { @zero.mod_part_of_divmod(@zero) }.should raise_error(ZeroDivisionError)
+ end
+end
+
+describe "BigDecimal#divmod" do
+
+ before :each do
+ @a = BigDecimal("42.00000000000000000001")
+
+ @zero = BigDecimal("0")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+
+ @one = BigDecimal("1")
+ @mixed = BigDecimal("1.23456789")
+ @pos_int = BigDecimal("2E5555")
+ @neg_int = BigDecimal("-2E5555")
+ @pos_frac = BigDecimal("2E-9999")
+ @neg_frac = BigDecimal("-2E-9999")
+ @nan = BigDecimal("NaN")
+ @infinity = BigDecimal("Infinity")
+ @infinity_minus = BigDecimal("-Infinity")
+ @one_minus = BigDecimal("-1")
+ @frac_1 = BigDecimal("1E-99999")
+ @frac_2 = BigDecimal("0.9E-99999")
+
+ @special_vals = [@infinity, @infinity_minus, @nan]
+ @regular_vals = [
+ @one, @mixed, @pos_int, @neg_int, @pos_frac,
+ @neg_frac, @one_minus, @frac_1, @frac_2]
+ @zeroes = [@zero, @zero_pos, @zero_neg]
+ end
+
+ it "divides value, returns an array" do
+ res = @a.divmod(5)
+ res.kind_of?(Array).should == true
+ end
+
+ it "array contains quotient and modulus as BigDecimal" do
+ res = @a.divmod(5)
+ DivmodSpecs.check_both_bigdecimal(res)
+ res[0].should == BigDecimal('0.8E1')
+ res[1].should == BigDecimal('2.00000000000000000001')
+
+ BigDecimal('1').divmod(BigDecimal('2')).should == [0, 1]
+ BigDecimal('2').divmod(BigDecimal('1')).should == [2, 0]
+
+ BigDecimal('1').divmod(BigDecimal('-2')).should == [-1, -1]
+ BigDecimal('2').divmod(BigDecimal('-1')).should == [-2, 0]
+
+ BigDecimal('-1').divmod(BigDecimal('2')).should == [-1, 1]
+ BigDecimal('-2').divmod(BigDecimal('1')).should == [-2, 0]
+ end
+
+ it "can be reversed with * and +" do
+ # Example taken from BigDecimal documentation
+ a = BigDecimal("42")
+ b = BigDecimal("9")
+ q, m = a.divmod(b)
+ c = q * b + m
+ a.should == c
+
+ values = [@one, @one_minus, BigDecimal('2'), BigDecimal('-2'),
+ BigDecimal('5'), BigDecimal('-5'), BigDecimal('10'), BigDecimal('-10'),
+ BigDecimal('20'), BigDecimal('-20'), BigDecimal('100'), BigDecimal('-100'),
+ BigDecimal('1.23456789E10'), BigDecimal('-1.23456789E10')
+ ]
+
+ # TODO: file MRI bug:
+ # BigDecimal('1').divmod(BigDecimal('3E-9'))[0] #=> 0.3E9,
+ # but really should be 0.333333333E9
+ values << BigDecimal('1E-10')
+ values << BigDecimal('-1E-10')
+ values << BigDecimal('2E55')
+ values << BigDecimal('-2E55')
+ values << BigDecimal('2E-5555')
+ values << BigDecimal('-2E-5555')
+
+
+ values_and_zeroes = values + @zeroes
+ values_and_zeroes.each do |val1|
+ values.each do |val2|
+ res = val1.divmod(val2)
+ DivmodSpecs.check_both_bigdecimal(res)
+ res[0].should == ((val1/val2).floor)
+ res[1].should == (val1 - res[0] * val2)
+ end
+ end
+ end
+
+ it "returns an array of two NaNs if NaN is involved" do
+ (@special_vals + @regular_vals + @zeroes).each do |val|
+ DivmodSpecs.check_both_nan(val.divmod(@nan))
+ DivmodSpecs.check_both_nan(@nan.divmod(val))
+ end
+ end
+
+ it "raises ZeroDivisionError if the divisor is zero" do
+ (@special_vals + @regular_vals + @zeroes - [@nan]).each do |val|
+ @zeroes.each do |zero|
+ lambda { val.divmod(zero) }.should raise_error(ZeroDivisionError)
+ end
+ end
+ end
+
+ it "returns an array of Infinity and NaN if the dividend is Infinity" do
+ @regular_vals.each do |val|
+ array = @infinity.divmod(val)
+ array.length.should == 2
+ array[0].infinite?.should == (val > 0 ? 1 : -1)
+ array[1].nan?.should == true
+ end
+ end
+
+ it "returns an array of zero and the dividend if the divisor is Infinity" do
+ @regular_vals.each do |val|
+ array = val.divmod(@infinity)
+ array.length.should == 2
+ array[0].should == @zero
+ array[1].should == val
+ end
+ end
+
+ it "returns an array of two zero if the diviend is zero" do
+ @zeroes.each do |zero|
+ @regular_vals.each do |val|
+ zero.divmod(val).should == [@zero, @zero]
+ end
+ end
+ end
+
+ it "raises TypeError if the argument cannot be coerced to BigDecimal" do
+ lambda {
+ @one.divmod('1')
+ }.should raise_error(TypeError)
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/double_fig_spec.rb b/spec/ruby/library/bigdecimal/double_fig_spec.rb
new file mode 100644
index 0000000000..f742d68f87
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/double_fig_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal.double_fig" do
+ # The result depends on the CPU and OS
+ it "returns the number of digits a Float number is allowed to have" do
+ BigDecimal.double_fig.should_not == nil
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/eql_spec.rb b/spec/ruby/library/bigdecimal/eql_spec.rb
new file mode 100644
index 0000000000..1be5862751
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/eql_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/eql'
+
+describe "BigDecimal#eql?" do
+ it_behaves_like :bigdecimal_eql, :eql?
+end
diff --git a/spec/ruby/library/bigdecimal/equal_value_spec.rb b/spec/ruby/library/bigdecimal/equal_value_spec.rb
new file mode 100644
index 0000000000..933060eada
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/equal_value_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/eql'
+
+
+describe "BigDecimal#==" do
+ it_behaves_like :bigdecimal_eql, :==
+end
diff --git a/spec/ruby/library/bigdecimal/exponent_spec.rb b/spec/ruby/library/bigdecimal/exponent_spec.rb
new file mode 100644
index 0000000000..a349ac093f
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/exponent_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require_relative 'shared/power'
+require 'bigdecimal'
+
+describe "BigDecimal#**" do
+ it_behaves_like :bigdecimal_power, :**
+end
+
+describe "BigDecimal#exponent" do
+
+ it "returns an Integer" do
+ BigDecimal("2E100000000").exponent.kind_of?(Integer).should == true
+ BigDecimal("2E-999").exponent.kind_of?(Integer).should == true
+ end
+
+ it "is n if number can be represented as 0.xxx*10**n" do
+ BigDecimal("2E1000").exponent.should == 1001
+ 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 Fixnum" 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
+ BigDecimal("-0").exponent.should == 0
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/finite_spec.rb b/spec/ruby/library/bigdecimal/finite_spec.rb
new file mode 100644
index 0000000000..6685d589b6
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/finite_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#finite?" do
+ before :each do
+ @one = BigDecimal("1")
+ @zero = BigDecimal("0")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+ @two = BigDecimal("2")
+ @three = BigDecimal("3")
+ @nan = BigDecimal("NaN")
+ @infinity = BigDecimal("Infinity")
+ @infinity_minus = BigDecimal("-Infinity")
+ @one_minus = BigDecimal("-1")
+ @frac_1 = BigDecimal("1E-99999")
+ @frac_2 = BigDecimal("0.9E-99999")
+ @big = BigDecimal("2E40001")
+ @finite_vals = [@one, @zero, @zero_pos, @zero_neg, @two,
+ @three, @frac_1, @frac_2, @big, @one_minus]
+ end
+
+ it "is false if Infinity or NaN" do
+ @infinity.finite?.should == false
+ @infinity_minus.finite?.should == false
+ @nan.finite?.should == false
+ end
+
+ it "returns true for finite values" do
+ @finite_vals.each do |val|
+ val.finite?.should == true
+ end
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/fix_spec.rb b/spec/ruby/library/bigdecimal/fix_spec.rb
new file mode 100644
index 0000000000..af852d6471
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/fix_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#fix" do
+ before :each do
+ @zero = BigDecimal("0")
+ @mixed = BigDecimal("1.23456789")
+ @pos_int = BigDecimal("2E5555")
+ @neg_int = BigDecimal("-2E5555")
+ @pos_frac = BigDecimal("2E-9999")
+ @neg_frac = BigDecimal("-2E-9999")
+
+ @infinity = BigDecimal("Infinity")
+ @infinity_neg = BigDecimal("-Infinity")
+ @nan = BigDecimal("NaN")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+ end
+
+ it "returns a BigDecimal" do
+ BigDecimal("2E100000000").fix.kind_of?(BigDecimal).should == true
+ BigDecimal("2E-999").kind_of?(BigDecimal).should == true
+ end
+
+ it "returns the integer part of the absolute value" do
+ a = BigDecimal("2E1000")
+ a.fix.should == a
+ b = BigDecimal("-2E1000")
+ b.fix.should == b
+ BigDecimal("0.123456789E5").fix.should == BigDecimal("0.12345E5")
+ BigDecimal("-0.123456789E5").fix.should == BigDecimal("-0.12345E5")
+ end
+
+ it "correctly handles special values" do
+ @infinity.fix.should == @infinity
+ @infinity_neg.fix.should == @infinity_neg
+ @nan.fix.nan?.should == true
+ end
+
+ it "returns 0 if the absolute value is < 1" do
+ BigDecimal("0.99999").fix.should == 0
+ BigDecimal("-0.99999").fix.should == 0
+ BigDecimal("0.000000001").fix.should == 0
+ BigDecimal("-0.00000001").fix.should == 0
+ BigDecimal("-1000000").fix.should_not == 0
+ @zero.fix.should == 0
+ @zero_pos.fix.should == @zero_pos
+ @zero_neg.fix.should == @zero_neg
+ end
+
+ it "does not allow any arguments" do
+ lambda {
+ @mixed.fix(10)
+ }.should raise_error(ArgumentError)
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/fixtures/classes.rb b/spec/ruby/library/bigdecimal/fixtures/classes.rb
new file mode 100644
index 0000000000..06e4474cf0
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/fixtures/classes.rb
@@ -0,0 +1,17 @@
+module BigDecimalSpecs
+ # helper method to sure that the global limit is reset back
+ def self.with_limit(l)
+ old = BigDecimal.limit(l)
+ yield
+ ensure
+ BigDecimal.limit(old)
+ end
+
+ def self.with_rounding(r)
+ old = BigDecimal.mode(BigDecimal::ROUND_MODE)
+ BigDecimal.mode(BigDecimal::ROUND_MODE, r)
+ yield
+ ensure
+ BigDecimal.mode(BigDecimal::ROUND_MODE, old)
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/floor_spec.rb b/spec/ruby/library/bigdecimal/floor_spec.rb
new file mode 100644
index 0000000000..509cb00e10
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/floor_spec.rb
@@ -0,0 +1,100 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#floor" do
+ before :each do
+ @one = BigDecimal("1")
+ @three = BigDecimal("3")
+ @four = BigDecimal("4")
+ @zero = BigDecimal("0")
+ @mixed = BigDecimal("1.23456789")
+ @mixed_big = BigDecimal("1.23456789E100")
+ @pos_int = BigDecimal("2E5555")
+ @neg_int = BigDecimal("-2E5555")
+ @pos_frac = BigDecimal("2E-9999")
+ @neg_frac = BigDecimal("-2E-9999")
+
+ @infinity = BigDecimal("Infinity")
+ @infinity_neg = BigDecimal("-Infinity")
+ @nan = BigDecimal("NaN")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+ end
+
+ it "returns the greatest integer smaller or equal to self" do
+ @pos_int.floor.should == @pos_int
+ @neg_int.floor.should == @neg_int
+ @pos_frac.floor.should == @zero
+ @neg_frac.floor.should == BigDecimal("-1")
+ @zero.floor.should == 0
+ @zero_pos.floor.should == @zero_pos
+ @zero_neg.floor.should == @zero_neg
+
+ BigDecimal('2.3').floor.should == 2
+ BigDecimal('2.5').floor.should == 2
+ BigDecimal('2.9999').floor.should == 2
+ BigDecimal('-2.3').floor.should == -3
+ BigDecimal('-2.5').floor.should == -3
+ BigDecimal('-2.9999').floor.should == -3
+ BigDecimal('0.8').floor.should == 0
+ BigDecimal('-0.8').floor.should == -1
+ end
+
+ it "raise exception, if self is special value" do
+ lambda { @infinity.floor }.should raise_error(FloatDomainError)
+ lambda { @infinity_neg.floor }.should raise_error(FloatDomainError)
+ lambda { @nan.floor }.should raise_error(FloatDomainError)
+ end
+
+ it "returns n digits right of the decimal point if given n > 0" do
+ @mixed.floor(1).should == BigDecimal("1.2")
+ @mixed.floor(5).should == BigDecimal("1.23456")
+
+ BigDecimal("-0.03").floor(1).should == BigDecimal("-0.1")
+ BigDecimal("0.03").floor(1).should == BigDecimal("0")
+
+ BigDecimal("23.45").floor(0).should == BigDecimal('23')
+ BigDecimal("23.45").floor(1).should == BigDecimal('23.4')
+ BigDecimal("23.45").floor(2).should == BigDecimal('23.45')
+
+ BigDecimal("-23.45").floor(0).should == BigDecimal('-24')
+ BigDecimal("-23.45").floor(1).should == BigDecimal('-23.5')
+ BigDecimal("-23.45").floor(2).should == BigDecimal('-23.45')
+
+ BigDecimal("2E-10").floor(0).should == @zero
+ BigDecimal("2E-10").floor(9).should == @zero
+ BigDecimal("2E-10").floor(10).should == BigDecimal('2E-10')
+ BigDecimal("2E-10").floor(11).should == BigDecimal('2E-10')
+
+ (1..10).each do |n|
+ # 0.3, 0.33, 0.333, etc.
+ (@one.div(@three,20)).floor(n).should == BigDecimal("0.#{'3'*n}")
+ # 1.3, 1.33, 1.333, etc.
+ (@four.div(@three,20)).floor(n).should == BigDecimal("1.#{'3'*n}")
+ (BigDecimal('31').div(@three,20)).floor(n).should == BigDecimal("10.#{'3'*n}")
+ end
+ (1..10).each do |n|
+ # -0.4, -0.34, -0.334, etc.
+ (-@one.div(@three,20)).floor(n).should == BigDecimal("-0.#{'3'*(n-1)}4")
+ end
+ (1..10).each do |n|
+ (@three.div(@one,20)).floor(n).should == @three
+ end
+ (1..10).each do |n|
+ (-@three.div(@one,20)).floor(n).should == -@three
+ end
+ end
+
+ it "sets n digits left of the decimal point to 0, if given n < 0" do
+ BigDecimal("13345.234").floor(-2).should == BigDecimal("13300.0")
+ @mixed_big.floor(-99).should == BigDecimal("0.12E101")
+ @mixed_big.floor(-100).should == BigDecimal("0.1E101")
+ @mixed_big.floor(-95).should == BigDecimal("0.123456E101")
+ (1..10).each do |n|
+ BigDecimal('1.8').floor(-n).should == @zero
+ end
+ BigDecimal("1E10").floor(-30).should == @zero
+ BigDecimal("-1E10").floor(-30).should == BigDecimal('-1E30')
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/frac_spec.rb b/spec/ruby/library/bigdecimal/frac_spec.rb
new file mode 100644
index 0000000000..4a023b4ff6
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/frac_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#frac" do
+ before :each do
+ @zero = BigDecimal("0")
+ @mixed = BigDecimal("1.23456789")
+ @pos_int = BigDecimal("2E5555")
+ @neg_int = BigDecimal("-2E5555")
+ @pos_frac = BigDecimal("2E-9999")
+ @neg_frac = BigDecimal("-2E-9999")
+
+ @infinity = BigDecimal("Infinity")
+ @infinity_neg = BigDecimal("-Infinity")
+ @nan = BigDecimal("NaN")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+ end
+
+ it "returns a BigDecimal" do
+ @pos_int.frac.kind_of?(BigDecimal).should == true
+ @neg_int.frac.kind_of?(BigDecimal).should == true
+ @pos_frac.kind_of?(BigDecimal).should == true
+ @neg_frac.kind_of?(BigDecimal).should == true
+ end
+
+ it "returns the fractional part of the absolute value" do
+ @mixed.frac.should == BigDecimal("0.23456789")
+ @pos_frac.frac.should == @pos_frac
+ @neg_frac.frac.should == @neg_frac
+ end
+
+ it "returns 0 if the value is 0" do
+ @zero.frac.should == @zero
+ end
+
+ it "returns 0 if the value is an integer" do
+ @pos_int.frac.should == @zero
+ @neg_int.frac.should == @zero
+ end
+
+ it "correctly handles special values" do
+ @infinity.frac.should == @infinity
+ @infinity_neg.frac.should == @infinity_neg
+ @nan.frac.nan?.should == true
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/gt_spec.rb b/spec/ruby/library/bigdecimal/gt_spec.rb
new file mode 100644
index 0000000000..c815aa0353
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/gt_spec.rb
@@ -0,0 +1,98 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#>" do
+ before :each do
+ @zero = BigDecimal("0")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+ @mixed = BigDecimal("1.23456789")
+ @pos_int = BigDecimal("2E5555")
+ @neg_int = BigDecimal("-2E5555")
+ @pos_frac = BigDecimal("2E-9999")
+ @neg_frac = BigDecimal("-2E-9999")
+
+ @int_mock = mock('123')
+ class << @int_mock
+ def coerce(other)
+ return [other, BigDecimal('123')]
+ end
+ def > (other)
+ BigDecimal('123') > other
+ end
+ end
+
+ @values = [@mixed, @pos_int, @neg_int, @pos_frac, @neg_frac,
+ -2**32, -2**31, -2**30, -2**16, -2**8, -100, -10, -1,
+ @zero , 1, 2, 10, 10.5, 2**8, 2**16, 2**32, @int_mock, @zero_pos, @zero_neg]
+
+ @infinity = BigDecimal("Infinity")
+ @infinity_neg = BigDecimal("-Infinity")
+
+ @float_infinity = Float::INFINITY
+ @float_infinity_neg = -Float::INFINITY
+
+ @nan = BigDecimal("NaN")
+ end
+
+ it "returns true if a > b" do
+ one = BigDecimal("1")
+ two = BigDecimal("2")
+
+ frac_1 = BigDecimal("1E-99999")
+ frac_2 = BigDecimal("0.9E-99999")
+ (@zero > one).should == false
+ (two > @zero).should == true
+ (frac_2 > frac_1).should == false
+
+ (@neg_int > @pos_int).should == false
+ (@pos_int > @neg_int).should == true
+ (@neg_int > @pos_frac).should == false
+ (@pos_frac > @neg_int).should == true
+ (@zero > @zero_pos).should == false
+ (@zero > @zero_neg).should == false
+ (@zero_neg > @zero_pos).should == false
+ (@zero_pos > @zero_neg).should == false
+ end
+
+ it "properly handles infinity values" do
+ @values.each { |val|
+ (val > @infinity).should == false
+ (@infinity > val).should == true
+ (val > @infinity_neg).should == true
+ (@infinity_neg > val).should == false
+ }
+ (@infinity > @infinity).should == false
+ (@infinity_neg > @infinity_neg).should == false
+ (@infinity > @infinity_neg).should == true
+ (@infinity_neg > @infinity).should == false
+ end
+
+ ruby_bug "#13674", ""..."2.4" do
+ it "properly handles Float infinity values" do
+ @values.each { |val|
+ (val > @float_infinity).should == false
+ (@float_infinity > val).should == true
+ (val > @float_infinity_neg).should == true
+ (@float_infinity_neg > val).should == false
+ }
+ end
+ end
+
+ it "properly handles NaN values" do
+ @values += [@infinity, @infinity_neg, @nan]
+ @values.each { |val|
+ (@nan > val).should == false
+ (val > @nan).should == false
+ }
+ end
+
+ it "raises an ArgumentError if the argument can't be coerced into a BigDecimal" do
+ lambda {@zero > nil }.should raise_error(ArgumentError)
+ lambda {@infinity > nil }.should raise_error(ArgumentError)
+ lambda {@infinity_neg > nil }.should raise_error(ArgumentError)
+ lambda {@mixed > nil }.should raise_error(ArgumentError)
+ lambda {@pos_int > nil }.should raise_error(ArgumentError)
+ lambda {@neg_frac > nil }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/gte_spec.rb b/spec/ruby/library/bigdecimal/gte_spec.rb
new file mode 100644
index 0000000000..14534eec80
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/gte_spec.rb
@@ -0,0 +1,102 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#>=" do
+ before :each do
+ @zero = BigDecimal("0")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+ @mixed = BigDecimal("1.23456789")
+ @pos_int = BigDecimal("2E5555")
+ @neg_int = BigDecimal("-2E5555")
+ @pos_frac = BigDecimal("2E-9999")
+ @neg_frac = BigDecimal("-2E-9999")
+
+ @int_mock = mock('123')
+ class << @int_mock
+ def coerce(other)
+ return [other, BigDecimal('123')]
+ end
+ def >= (other)
+ BigDecimal('123') >= other
+ end
+ end
+
+ @values = [@mixed, @pos_int, @neg_int, @pos_frac, @neg_frac,
+ -2**32, -2**31, -2**30, -2**16, -2**8, -100, -10, -1,
+ @zero , 1, 2, 10, 10.5, 2**8, 2**16, 2**32, @int_mock, @zero_pos, @zero_neg]
+
+ @infinity = BigDecimal("Infinity")
+ @infinity_neg = BigDecimal("-Infinity")
+
+ @float_infinity = Float::INFINITY
+ @float_infinity_neg = -Float::INFINITY
+
+ @nan = BigDecimal("NaN")
+ end
+
+ it "returns true if a >= b" do
+ one = BigDecimal("1")
+ two = BigDecimal("2")
+
+ frac_1 = BigDecimal("1E-99999")
+ frac_2 = BigDecimal("0.9E-99999")
+
+ (@zero >= one).should == false
+ (two >= @zero).should == true
+
+ (frac_2 >= frac_1).should == false
+ (two >= two).should == true
+ (frac_1 >= frac_1).should == true
+
+ (@neg_int >= @pos_int).should == false
+ (@pos_int >= @neg_int).should == true
+ (@neg_int >= @pos_frac).should == false
+ (@pos_frac >= @neg_int).should == true
+ (@zero >= @zero_pos).should == true
+ (@zero >= @zero_neg).should == true
+ (@zero_neg >= @zero_pos).should == true
+ (@zero_pos >= @zero_neg).should == true
+ end
+
+ it "properly handles infinity values" do
+ @values.each { |val|
+ (val >= @infinity).should == false
+ (@infinity >= val).should == true
+ (val >= @infinity_neg).should == true
+ (@infinity_neg >= val).should == false
+ }
+ (@infinity >= @infinity).should == true
+ (@infinity_neg >= @infinity_neg).should == true
+ (@infinity >= @infinity_neg).should == true
+ (@infinity_neg >= @infinity).should == false
+ end
+
+ ruby_bug "#13674", ""..."2.4" do
+ it "properly handles Float infinity values" do
+ @values.each { |val|
+ (val >= @float_infinity).should == false
+ (@float_infinity >= val).should == true
+ (val >= @float_infinity_neg).should == true
+ (@float_infinity_neg >= val).should == false
+ }
+ end
+ end
+
+ it "properly handles NaN values" do
+ @values += [@infinity, @infinity_neg, @nan]
+ @values.each { |val|
+ (@nan >= val).should == false
+ (val >= @nan).should == false
+ }
+ end
+
+ it "returns nil if the argument is nil" do
+ lambda {@zero >= nil }.should raise_error(ArgumentError)
+ lambda {@infinity >= nil }.should raise_error(ArgumentError)
+ lambda {@infinity_neg >= nil }.should raise_error(ArgumentError)
+ lambda {@mixed >= nil }.should raise_error(ArgumentError)
+ lambda {@pos_int >= nil }.should raise_error(ArgumentError)
+ lambda {@neg_frac >= nil }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/infinite_spec.rb b/spec/ruby/library/bigdecimal/infinite_spec.rb
new file mode 100644
index 0000000000..025386107f
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/infinite_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#infinite?" do
+
+ it "returns 1 if self is Infinity" do
+ BigDecimal("Infinity").infinite?.should == 1
+ end
+
+ it "returns -1 if self is -Infinity" do
+ BigDecimal("-Infinity").infinite?.should == -1
+ end
+
+ it "returns not true otherwise" do
+ e2_plus = BigDecimal("2E40001")
+ e3_minus = BigDecimal("3E-20001")
+ really_small_zero = BigDecimal("0E-200000000")
+ really_big_zero = BigDecimal("0E200000000000")
+ e3_minus.infinite?.should == nil
+ e2_plus.infinite?.should == nil
+ really_small_zero.infinite?.should == nil
+ really_big_zero.infinite?.should == nil
+ BigDecimal("0.000000000000000000000000").infinite?.should == nil
+ end
+
+ it "returns not true if self is NaN" do
+ # NaN is a special value which is neither finite nor infinite.
+ nan = BigDecimal("NaN")
+ nan.infinite?.should == nil
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/inspect_spec.rb b/spec/ruby/library/bigdecimal/inspect_spec.rb
new file mode 100644
index 0000000000..7e1a8297e2
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/inspect_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#inspect" do
+
+ before :each do
+ @bigdec = BigDecimal("1234.5678")
+ end
+
+ it "returns String" do
+ @bigdec.inspect.kind_of?(String).should == true
+ end
+
+ ruby_version_is ""..."2.4" do
+ it "returns String starting with #" do
+ @bigdec.inspect[0].should == ?#
+ end
+
+ it "encloses information in angle brackets" do
+ @bigdec.inspect.should =~ /^.<.*>$/
+ end
+
+ it "is comma separated list of three items" do
+ @bigdec.inspect.should =~ /...*,.*,.*/
+ end
+
+ it "value after first comma is value as string" do
+ @bigdec.inspect.split(",")[1].should == "\'0.12345678E4\'"
+ end
+
+ it "last part is number of significant digits" do
+ signific_string = "#{@bigdec.precs[0]}(#{@bigdec.precs[1]})"
+ @bigdec.inspect.split(",")[2].should == signific_string + ">"
+ end
+
+ it "looks like this" do
+ regex = /^\#\<BigDecimal\:.*,'0\.12345678E4',[0-9]+\([0-9]+\)>$/
+ @bigdec.inspect.should =~ regex
+ end
+ end
+
+ ruby_version_is "2.4" do
+ it "looks like this" do
+ @bigdec.inspect.should == "0.12345678e4"
+ end
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/limit_spec.rb b/spec/ruby/library/bigdecimal/limit_spec.rb
new file mode 100644
index 0000000000..75cbc8b55c
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/limit_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'bigdecimal'
+
+describe "BigDecimal.limit" do
+ it "returns the value before set if the passed argument is nil or is not specified" do
+ old = BigDecimal.limit
+ BigDecimal.limit.should == 0
+ BigDecimal.limit(10).should == 0
+ BigDecimal.limit.should == 10
+ BigDecimal.limit(old)
+ end
+
+ it "uses the global limit if no precision is specified" do
+ BigDecimalSpecs.with_limit(0) do
+ (BigDecimal('0.888') + BigDecimal('0')).should == BigDecimal('0.888')
+ (BigDecimal('0.888') - BigDecimal('0')).should == BigDecimal('0.888')
+ (BigDecimal('0.888') * BigDecimal('3')).should == BigDecimal('2.664')
+ (BigDecimal('0.888') / BigDecimal('3')).should == BigDecimal('0.296')
+ end
+
+ BigDecimalSpecs.with_limit(1) do
+ (BigDecimal('0.888') + BigDecimal('0')).should == BigDecimal('0.9')
+ (BigDecimal('0.888') - BigDecimal('0')).should == BigDecimal('0.9')
+ (BigDecimal('0.888') * BigDecimal('3')).should == BigDecimal('3')
+ (BigDecimal('0.888') / BigDecimal('3')).should == BigDecimal('0.3')
+ end
+
+ BigDecimalSpecs.with_limit(2) do
+ (BigDecimal('0.888') + BigDecimal('0')).should == BigDecimal('0.89')
+ (BigDecimal('0.888') - BigDecimal('0')).should == BigDecimal('0.89')
+ (BigDecimal('0.888') * BigDecimal('3')).should == BigDecimal('2.7')
+ (BigDecimal('0.888') / BigDecimal('3')).should == BigDecimal('0.30')
+ end
+ end
+
+ it "picks the specified precision over global limit" do
+ BigDecimalSpecs.with_limit(3) do
+ BigDecimal('0.888').add(BigDecimal('0'), 2).should == BigDecimal('0.89')
+ BigDecimal('0.888').sub(BigDecimal('0'), 2).should == BigDecimal('0.89')
+ BigDecimal('0.888').mult(BigDecimal('3'), 2).should == BigDecimal('2.7')
+ BigDecimal('0.888').div(BigDecimal('3'), 2).should == BigDecimal('0.30')
+ end
+ end
+
+ it "picks the global precision when limit 0 specified" do
+ BigDecimalSpecs.with_limit(3) do
+ BigDecimal('0.8888').add(BigDecimal('0'), 0).should == BigDecimal('0.889')
+ BigDecimal('0.8888').sub(BigDecimal('0'), 0).should == BigDecimal('0.889')
+ BigDecimal('0.888').mult(BigDecimal('3'), 0).should == BigDecimal('2.66')
+ BigDecimal('0.8888').div(BigDecimal('3'), 0).should == BigDecimal('0.296')
+ end
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/lt_spec.rb b/spec/ruby/library/bigdecimal/lt_spec.rb
new file mode 100644
index 0000000000..8cf92fedd2
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/lt_spec.rb
@@ -0,0 +1,96 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#<" do
+ before :each do
+ @zero = BigDecimal("0")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+ @mixed = BigDecimal("1.23456789")
+ @pos_int = BigDecimal("2E5555")
+ @neg_int = BigDecimal("-2E5555")
+ @pos_frac = BigDecimal("2E-9999")
+ @neg_frac = BigDecimal("-2E-9999")
+
+ @int_mock = mock('123')
+ class << @int_mock
+ def coerce(other)
+ return [other, BigDecimal('123')]
+ end
+ def < (other)
+ BigDecimal('123') < other
+ end
+ end
+
+ @values = [@mixed, @pos_int, @neg_int, @pos_frac, @neg_frac,
+ -2**32, -2**31, -2**30, -2**16, -2**8, -100, -10, -1,
+ @zero , 1, 2, 10, 10.5, 2**8, 2**16, 2**32, @int_mock, @zero_pos, @zero_neg]
+
+ @infinity = BigDecimal("Infinity")
+ @infinity_neg = BigDecimal("-Infinity")
+
+ @float_infinity = Float::INFINITY
+ @float_infinity_neg = -Float::INFINITY
+
+ @nan = BigDecimal("NaN")
+ end
+
+ it "returns true if a < b" do
+ one = BigDecimal("1")
+ two = BigDecimal("2")
+ frac_1 = BigDecimal("1E-99999")
+ frac_2 = BigDecimal("0.9E-99999")
+ (@zero < one).should == true
+ (two < @zero).should == false
+ (frac_2 < frac_1).should == true
+ (@neg_int < @pos_int).should == true
+ (@pos_int < @neg_int).should == false
+ (@neg_int < @pos_frac).should == true
+ (@pos_frac < @neg_int).should == false
+ (@zero < @zero_pos).should == false
+ (@zero < @zero_neg).should == false
+ (@zero_neg < @zero_pos).should == false
+ (@zero_pos < @zero_neg).should == false
+ end
+
+ it "properly handles infinity values" do
+ @values.each { |val|
+ (val < @infinity).should == true
+ (@infinity < val).should == false
+ (val < @infinity_neg).should == false
+ (@infinity_neg < val).should == true
+ }
+ (@infinity < @infinity).should == false
+ (@infinity_neg < @infinity_neg).should == false
+ (@infinity < @infinity_neg).should == false
+ (@infinity_neg < @infinity).should == true
+ end
+
+ ruby_bug "#13674", ""..."2.4" do
+ it "properly handles Float infinity values" do
+ @values.each { |val|
+ (val < @float_infinity).should == true
+ (@float_infinity < val).should == false
+ (val < @float_infinity_neg).should == false
+ (@float_infinity_neg < val).should == true
+ }
+ end
+ end
+
+ it "properly handles NaN values" do
+ @values += [@infinity, @infinity_neg, @nan]
+ @values.each { |val|
+ (@nan < val).should == false
+ (val < @nan).should == false
+ }
+ end
+
+ it "raises an ArgumentError if the argument can't be coerced into a BigDecimal" do
+ lambda {@zero < nil }.should raise_error(ArgumentError)
+ lambda {@infinity < nil }.should raise_error(ArgumentError)
+ lambda {@infinity_neg < nil }.should raise_error(ArgumentError)
+ lambda {@mixed < nil }.should raise_error(ArgumentError)
+ lambda {@pos_int < nil }.should raise_error(ArgumentError)
+ lambda {@neg_frac < nil }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/lte_spec.rb b/spec/ruby/library/bigdecimal/lte_spec.rb
new file mode 100644
index 0000000000..eff6547369
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/lte_spec.rb
@@ -0,0 +1,102 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#<=" do
+ before :each do
+ @zero = BigDecimal("0")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+ @mixed = BigDecimal("1.23456789")
+ @pos_int = BigDecimal("2E5555")
+ @neg_int = BigDecimal("-2E5555")
+ @pos_frac = BigDecimal("2E-9999")
+ @neg_frac = BigDecimal("-2E-9999")
+
+ @int_mock = mock('123')
+ class << @int_mock
+ def coerce(other)
+ return [other, BigDecimal('123')]
+ end
+ def <= (other)
+ BigDecimal('123') <= other
+ end
+ end
+
+ @values = [@mixed, @pos_int, @neg_int, @pos_frac, @neg_frac,
+ -2**32, -2**31, -2**30, -2**16, -2**8, -100, -10, -1,
+ @zero , 1, 2, 10, 10.5, 2**8, 2**16, 2**32, @int_mock, @zero_pos, @zero_neg]
+
+ @infinity = BigDecimal("Infinity")
+ @infinity_neg = BigDecimal("-Infinity")
+
+ @float_infinity = Float::INFINITY
+ @float_infinity_neg = -Float::INFINITY
+
+ @nan = BigDecimal("NaN")
+ end
+
+ it "returns true if a <= b" do
+ one = BigDecimal("1")
+ two = BigDecimal("2")
+
+ frac_1 = BigDecimal("1E-99999")
+ frac_2 = BigDecimal("0.9E-99999")
+
+ (@zero <= one).should == true
+ (two <= @zero).should == false
+
+ (frac_2 <= frac_1).should == true
+ (two <= two).should == true
+ (frac_1 <= frac_1).should == true
+
+ (@neg_int <= @pos_int).should == true
+ (@pos_int <= @neg_int).should == false
+ (@neg_int <= @pos_frac).should == true
+ (@pos_frac <= @neg_int).should == false
+ (@zero <= @zero_pos).should == true
+ (@zero <= @zero_neg).should == true
+ (@zero_neg <= @zero_pos).should == true
+ (@zero_pos <= @zero_neg).should == true
+ end
+
+ it "properly handles infinity values" do
+ @values.each { |val|
+ (val <= @infinity).should == true
+ (@infinity <= val).should == false
+ (val <= @infinity_neg).should == false
+ (@infinity_neg <= val).should == true
+ }
+ (@infinity <= @infinity).should == true
+ (@infinity_neg <= @infinity_neg).should == true
+ (@infinity <= @infinity_neg).should == false
+ (@infinity_neg <= @infinity).should == true
+ end
+
+ ruby_bug "#13674", ""..."2.4" do
+ it "properly handles Float infinity values" do
+ @values.each { |val|
+ (val <= @float_infinity).should == true
+ (@float_infinity <= val).should == false
+ (val <= @float_infinity_neg).should == false
+ (@float_infinity_neg <= val).should == true
+ }
+ end
+ end
+
+ it "properly handles NaN values" do
+ @values += [@infinity, @infinity_neg, @nan]
+ @values.each { |val|
+ (@nan <= val).should == false
+ (val <= @nan).should == false
+ }
+ end
+
+ it "raises an ArgumentError if the argument can't be coerced into a BigDecimal" do
+ lambda {@zero <= nil }.should raise_error(ArgumentError)
+ lambda {@infinity <= nil }.should raise_error(ArgumentError)
+ lambda {@infinity_neg <= nil }.should raise_error(ArgumentError)
+ lambda {@mixed <= nil }.should raise_error(ArgumentError)
+ lambda {@pos_int <= nil }.should raise_error(ArgumentError)
+ lambda {@neg_frac <= nil }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/minus_spec.rb b/spec/ruby/library/bigdecimal/minus_spec.rb
new file mode 100644
index 0000000000..02f7f02296
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/minus_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#-" do
+
+ before :each do
+ @one = BigDecimal("1")
+ @zero = BigDecimal("0")
+ @two = BigDecimal("2")
+ @nan = BigDecimal("NaN")
+ @infinity = BigDecimal("Infinity")
+ @infinity_minus = BigDecimal("-Infinity")
+ @one_minus = BigDecimal("-1")
+ @frac_1 = BigDecimal("1E-99999")
+ @frac_2 = BigDecimal("0.9E-99999")
+ end
+
+ it "returns a - b" do
+ (@two - @one).should == @one
+ (@one - @two).should == @one_minus
+ (@one - @one_minus).should == @two
+ (@frac_2 - @frac_1).should == BigDecimal("-0.1E-99999")
+ (@two - @two).should == @zero
+ (@frac_1 - @frac_1).should == @zero
+ (BigDecimal('1.23456789') - BigDecimal('1.2')).should == BigDecimal("0.03456789")
+ end
+
+ it "returns NaN if NaN is involved" do
+ (@one - @nan).nan?.should == true
+ (@nan - @one).nan?.should == true
+ (@nan - @nan).nan?.should == true
+ (@nan - @infinity).nan?.should == true
+ (@nan - @infinity_minus).nan?.should == true
+ (@infinity - @nan).nan?.should == true
+ (@infinity_minus - @nan).nan?.should == true
+ end
+
+ it "returns NaN both operands are infinite with the same sign" do
+ (@infinity - @infinity).nan?.should == true
+ (@infinity_minus - @infinity_minus).nan?.should == true
+ end
+
+ it "returns Infinity or -Infinity if these are involved" do
+ (@infinity - @infinity_minus).should == @infinity
+ (@infinity_minus - @infinity).should == @infinity_minus
+
+ (@infinity - @zero).should == @infinity
+ (@infinity - @frac_2).should == @infinity
+ (@infinity - @two).should == @infinity
+ (@infinity - @one_minus).should == @infinity
+
+ (@zero - @infinity).should == @infinity_minus
+ (@frac_2 - @infinity).should == @infinity_minus
+ (@two - @infinity).should == @infinity_minus
+ (@one_minus - @infinity).should == @infinity_minus
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/mode_spec.rb b/spec/ruby/library/bigdecimal/mode_spec.rb
new file mode 100644
index 0000000000..1de19c9971
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/mode_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal.mode" do
+ #the default value of BigDecimal exception constants is false
+ after :each do
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, false)
+ end
+
+ it "returns the appropriate value and continue the computation if the flag is false" do
+ BigDecimal("NaN").add(BigDecimal("1"),0).nan?.should == true
+ BigDecimal("0").add(BigDecimal("Infinity"),0).should == BigDecimal("Infinity")
+ BigDecimal("1").quo(BigDecimal("0")).should == BigDecimal("Infinity")
+ end
+
+ it "returns Infinity when too big" do
+ BigDecimal("1E11111111111111111111").should == BigDecimal("Infinity")
+ (BigDecimal("1E1000000000000000000")**10).should == BigDecimal("Infinity")
+ end
+
+ it "raise an exception if the flag is true" do
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, true)
+ lambda { BigDecimal("NaN").add(BigDecimal("1"),0) }.should raise_error(FloatDomainError)
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true)
+ lambda { BigDecimal("0").add(BigDecimal("Infinity"),0) }.should raise_error(FloatDomainError)
+ BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, true)
+ lambda { BigDecimal("1").quo(BigDecimal("0")) }.should raise_error(FloatDomainError)
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, true)
+ lambda { BigDecimal("1E11111111111111111111") }.should raise_error(FloatDomainError)
+ lambda { (BigDecimal("1E1000000000000000000")**10) }.should raise_error(FloatDomainError)
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/modulo_spec.rb b/spec/ruby/library/bigdecimal/modulo_spec.rb
new file mode 100644
index 0000000000..035d31bd98
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/modulo_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'shared/modulo'
+
+describe "BigDecimal#%" do
+ it_behaves_like :bigdecimal_modulo, :%
+ it_behaves_like :bigdecimal_modulo_zerodivisionerror, :%
+end
+
+describe "BigDecimal#modulo" do
+ it_behaves_like :bigdecimal_modulo, :modulo
+ it_behaves_like :bigdecimal_modulo_zerodivisionerror, :modulo
+end
diff --git a/spec/ruby/library/bigdecimal/mult_spec.rb b/spec/ruby/library/bigdecimal/mult_spec.rb
new file mode 100644
index 0000000000..a4c1602182
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/mult_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require_relative 'shared/mult'
+require 'bigdecimal'
+
+describe "BigDecimal#mult" do
+ it_behaves_like :bigdecimal_mult, :mult, [10]
+end
+
+describe "BigDecimal#mult" do
+ before :each do
+ @one = BigDecimal "1"
+ @e3_minus = BigDecimal "3E-20001"
+ @e = BigDecimal "1.00000000000000000000123456789"
+ @tolerance = @e.sub @one, 1000
+ @tolerance2 = BigDecimal "30001E-20005"
+
+ end
+
+ it "multiply self with other with (optional) precision" do
+ @e.mult(@one, 1).should be_close(@one, @tolerance)
+ @e3_minus.mult(@one, 1).should be_close(0, @tolerance2)
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/multiply_spec.rb b/spec/ruby/library/bigdecimal/multiply_spec.rb
new file mode 100644
index 0000000000..2741d3623e
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/multiply_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require_relative 'shared/mult'
+require 'bigdecimal'
+
+describe "BigDecimal#*" do
+ it_behaves_like :bigdecimal_mult, :*, []
+end
+
+describe "BigDecimal#*" do
+ before :each do
+ @e3_minus = BigDecimal("3E-20001")
+ @e3_plus = BigDecimal("3E20001")
+ @e = BigDecimal("1.00000000000000000000123456789")
+ @one = BigDecimal("1")
+ end
+
+ it "multiply self with other" do
+ (@one * @one).should == @one
+ (@e3_minus * @e3_plus).should == BigDecimal("9")
+ # Can't do this till we implement **
+ # (@e3_minus * @e3_minus).should == @e3_minus ** 2
+ # So let's rewrite it as:
+ (@e3_minus * @e3_minus).should == BigDecimal("9E-40002")
+ (@e * @one).should == @e
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/nan_spec.rb b/spec/ruby/library/bigdecimal/nan_spec.rb
new file mode 100644
index 0000000000..705492614b
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/nan_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#nan?" do
+
+ it "returns true if self is not a number" do
+ BigDecimal("NaN").nan?.should == true
+ end
+
+ it "returns false if self is not a NaN" do
+ BigDecimal("Infinity").nan?.should == false
+ BigDecimal("-Infinity").nan?.should == false
+ BigDecimal("0").nan?.should == false
+ BigDecimal("+0").nan?.should == false
+ BigDecimal("-0").nan?.should == false
+ BigDecimal("2E40001").nan?.should == false
+ BigDecimal("3E-20001").nan?.should == false
+ BigDecimal("0E-200000000").nan?.should == false
+ BigDecimal("0E200000000000").nan?.should == false
+ BigDecimal("0.000000000000000000000000").nan?.should == false
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/nonzero_spec.rb b/spec/ruby/library/bigdecimal/nonzero_spec.rb
new file mode 100644
index 0000000000..f43c4393cd
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/nonzero_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#nonzero?" do
+
+ it "returns self if self doesn't equal zero" do
+ # documentation says, it returns true. (04/10/08)
+ e2_plus = BigDecimal("2E40001")
+ e3_minus = BigDecimal("3E-20001")
+ infinity = BigDecimal("Infinity")
+ infinity_minus = BigDecimal("-Infinity")
+ nan = BigDecimal("NaN")
+ infinity.nonzero?.should equal(infinity)
+ infinity_minus.nonzero?.should equal(infinity_minus)
+ nan.nonzero?.should equal(nan)
+ e3_minus.nonzero?.should equal(e3_minus)
+ e2_plus.nonzero?.should equal(e2_plus)
+ end
+
+ it "returns nil otherwise" do
+ # documentation states, it should return false. (04/10/08)
+ really_small_zero = BigDecimal("0E-200000000")
+ really_big_zero = BigDecimal("0E200000000000")
+ really_small_zero.nonzero?.should == nil
+ really_big_zero.nonzero?.should == nil
+ BigDecimal("0.000000000000000000000000").nonzero?.should == nil
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/plus_spec.rb b/spec/ruby/library/bigdecimal/plus_spec.rb
new file mode 100644
index 0000000000..9247ff6146
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/plus_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#+" do
+
+ before :each do
+ @one = BigDecimal("1")
+ @zero = BigDecimal("0")
+ @two = BigDecimal("2")
+ @three = BigDecimal("3")
+ @ten = BigDecimal("10")
+ @eleven = BigDecimal("11")
+ @nan = BigDecimal("NaN")
+ @infinity = BigDecimal("Infinity")
+ @infinity_minus = BigDecimal("-Infinity")
+ @one_minus = BigDecimal("-1")
+ @frac_1 = BigDecimal("1E-99999")
+ @frac_2 = BigDecimal("0.9E-99999")
+ end
+
+ it "returns a + b" do
+ (@two + @one).should == @three
+ (@one + @two).should == @three
+ (@one + @one_minus).should == @zero
+ (@zero + @one).should == @one
+ (@ten + @one).should == @eleven
+ (@frac_1 + @frac_2).should == BigDecimal("1.9E-99999")
+ (@frac_2 + @frac_1).should == BigDecimal("1.9E-99999")
+ (@frac_1 + @frac_1).should == BigDecimal("2E-99999")
+ end
+
+ it "returns NaN if NaN is involved" do
+ (@one + @nan).nan?.should == true
+ (@nan + @one).nan?.should == true
+ end
+
+ it "returns Infinity or -Infinity if these are involved" do
+ (@zero + @infinity).should == @infinity
+ (@frac_2 + @infinity).should == @infinity
+ (@two + @infinity_minus).should == @infinity_minus
+ end
+
+ it "returns NaN if Infinity + (- Infinity)" do
+ (@infinity + @infinity_minus).nan?.should == true
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/power_spec.rb b/spec/ruby/library/bigdecimal/power_spec.rb
new file mode 100644
index 0000000000..63a45a1887
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/power_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/power'
+
+describe "BigDecimal#power" do
+ it_behaves_like :bigdecimal_power, :power
+end
diff --git a/spec/ruby/library/bigdecimal/precs_spec.rb b/spec/ruby/library/bigdecimal/precs_spec.rb
new file mode 100644
index 0000000000..f9320f2b9e
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/precs_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#precs" do
+
+ before :each do
+ @infinity = BigDecimal("Infinity")
+ @infinity_neg = BigDecimal("-Infinity")
+ @nan = BigDecimal("NaN")
+ @zero = BigDecimal("0")
+ @zero_neg = BigDecimal("-0")
+
+ @arr = [BigDecimal("2E40001"), BigDecimal("3E-20001"),\
+ @infinity, @infinity_neg, @nan, @zero, @zero_neg]
+ @precision = BigDecimal::BASE.to_s.length - 1
+ end
+
+ it "returns array of two values" do
+ @arr.each do |x|
+ x.precs.kind_of?(Array).should == true
+ x.precs.size.should == 2
+ end
+ end
+
+ it "returns Integers as array values" do
+ @arr.each do |x|
+ x.precs[0].kind_of?(Integer).should == true
+ x.precs[1].kind_of?(Integer).should == true
+ end
+ end
+
+ it "returns the current value of significant digits as the first value" do
+ BigDecimal("3.14159").precs[0].should >= 6
+ BigDecimal('1').precs[0].should == BigDecimal('1' + '0' * 100).precs[0]
+ [@infinity, @infinity_neg, @nan, @zero, @zero_neg].each do |value|
+ value.precs[0].should <= @precision
+ end
+ end
+
+ it "returns the maximum number of significant digits as the second value" do
+ BigDecimal("3.14159").precs[1].should >= 6
+ BigDecimal('1').precs[1].should >= 1
+ BigDecimal('1' + '0' * 100).precs[1] >= 101
+ [@infinity, @infinity_neg, @nan, @zero, @zero_neg].each do |value|
+ value.precs[1].should >= 1
+ end
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/quo_spec.rb b/spec/ruby/library/bigdecimal/quo_spec.rb
new file mode 100644
index 0000000000..a5c5651778
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/quo_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'shared/quo'
+require 'bigdecimal'
+
+describe "BigDecimal#quo" do
+ it_behaves_like :bigdecimal_quo, :quo, []
+
+ it "returns NaN if NaN is involved" do
+ BigDecimal("1").quo(BigDecimal("NaN")).nan?.should == true
+ BigDecimal("NaN").quo(BigDecimal("1")).nan?.should == true
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/remainder_spec.rb b/spec/ruby/library/bigdecimal/remainder_spec.rb
new file mode 100644
index 0000000000..28b25f8566
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/remainder_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#remainder" do
+
+ before :each do
+ @zero = BigDecimal("0")
+ @one = BigDecimal("0")
+ @mixed = BigDecimal("1.23456789")
+ @pos_int = BigDecimal("2E5555")
+ @neg_int = BigDecimal("-2E5555")
+ @pos_frac = BigDecimal("2E-9999")
+ @neg_frac = BigDecimal("-2E-9999")
+ @nan = BigDecimal("NaN")
+ @infinity = BigDecimal("Infinity")
+ @infinity_minus = BigDecimal("-Infinity")
+ @one_minus = BigDecimal("-1")
+ @frac_1 = BigDecimal("1E-99999")
+ @frac_2 = BigDecimal("0.9E-99999")
+ end
+
+ it "it equals modulo, if both values are of same sign" do
+ BigDecimal('1234567890123456789012345679').remainder(BigDecimal('1')).should == @zero
+ BigDecimal('123456789').remainder(BigDecimal('333333333333333333333333333E-50')).should == BigDecimal('0.12233333333333333333345679E-24')
+
+ @mixed.remainder(@pos_frac).should == @mixed % @pos_frac
+ @pos_int.remainder(@pos_frac).should == @pos_int % @pos_frac
+ @neg_frac.remainder(@neg_int).should == @neg_frac % @neg_int
+ @neg_int.remainder(@neg_frac).should == @neg_int % @neg_frac
+ end
+
+ it "means self-arg*(self/arg).truncate" do
+ @mixed.remainder(@neg_frac).should == @mixed - @neg_frac * (@mixed / @neg_frac).truncate
+ @pos_int.remainder(@neg_frac).should == @pos_int - @neg_frac * (@pos_int / @neg_frac).truncate
+ @neg_frac.remainder(@pos_int).should == @neg_frac - @pos_int * (@neg_frac / @pos_int).truncate
+ @neg_int.remainder(@pos_frac).should == @neg_int - @pos_frac * (@neg_int / @pos_frac).truncate
+ end
+
+ it "returns NaN used with zero" do
+ @mixed.remainder(@zero).nan?.should == true
+ @zero.remainder(@zero).nan?.should == true
+ end
+
+ it "returns zero if used on zero" do
+ @zero.remainder(@mixed).should == @zero
+ end
+
+ it "returns NaN if NaN is involved" do
+ @nan.remainder(@nan).nan?.should == true
+ @nan.remainder(@one).nan?.should == true
+ @one.remainder(@nan).nan?.should == true
+ @infinity.remainder(@nan).nan?.should == true
+ @nan.remainder(@infinity).nan?.should == true
+ end
+
+ it "returns NaN if Infinity is involved" do
+ @infinity.remainder(@infinity).nan?.should == true
+ @infinity.remainder(@one).nan?.should == true
+ @infinity.remainder(@mixed).nan?.should == true
+ @infinity.remainder(@one_minus).nan?.should == true
+ @infinity.remainder(@frac_1).nan?.should == true
+ @one.remainder(@infinity).nan?.should == true
+
+ @infinity_minus.remainder(@infinity_minus).nan?.should == true
+ @infinity_minus.remainder(@one).nan?.should == true
+ @one.remainder(@infinity_minus).nan?.should == true
+ @frac_2.remainder(@infinity_minus).nan?.should == true
+
+ @infinity.remainder(@infinity_minus).nan?.should == true
+ @infinity_minus.remainder(@infinity).nan?.should == true
+ end
+
+ it "coerces arguments to BigDecimal if possible" do
+ @one.remainder(2).should == @one
+ end
+
+
+ it "raises TypeError if the argument cannot be coerced to BigDecimal" do
+ lambda {
+ @one.remainder('2')
+ }.should raise_error(TypeError)
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/round_spec.rb b/spec/ruby/library/bigdecimal/round_spec.rb
new file mode 100644
index 0000000000..07b96acb2b
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/round_spec.rb
@@ -0,0 +1,202 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#round" do
+ before :each do
+ @one = BigDecimal("1")
+ @two = BigDecimal("2")
+ @three = BigDecimal("3")
+
+ @neg_one = BigDecimal("-1")
+ @neg_two = BigDecimal("-2")
+ @neg_three = BigDecimal("-3")
+
+ @p1_50 = BigDecimal("1.50")
+ @p1_51 = BigDecimal("1.51")
+ @p1_49 = BigDecimal("1.49")
+ @n1_50 = BigDecimal("-1.50")
+ @n1_51 = BigDecimal("-1.51")
+ @n1_49 = BigDecimal("-1.49")
+
+ @p2_50 = BigDecimal("2.50")
+ @p2_51 = BigDecimal("2.51")
+ @p2_49 = BigDecimal("2.49")
+ @n2_50 = BigDecimal("-2.50")
+ @n2_51 = BigDecimal("-2.51")
+ @n2_49 = BigDecimal("-2.49")
+ end
+
+ after :each do
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_UP)
+ end
+
+ it "uses default rounding method unless given" do
+ @p1_50.round(0).should == @two
+ @p1_51.round(0).should == @two
+ @p1_49.round(0).should == @one
+ @n1_50.round(0).should == @neg_two
+ @n1_51.round(0).should == @neg_two
+ @n1_49.round(0).should == @neg_one
+
+ @p2_50.round(0).should == @three
+ @p2_51.round(0).should == @three
+ @p2_49.round(0).should == @two
+ @n2_50.round(0).should == @neg_three
+ @n2_51.round(0).should == @neg_three
+ @n2_49.round(0).should == @neg_two
+
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_DOWN)
+
+ @p1_50.round(0).should == @one
+ @p1_51.round(0).should == @one
+ @p1_49.round(0).should == @one
+ @n1_50.round(0).should == @neg_one
+ @n1_51.round(0).should == @neg_one
+ @n1_49.round(0).should == @neg_one
+
+ @p2_50.round(0).should == @two
+ @p2_51.round(0).should == @two
+ @p2_49.round(0).should == @two
+ @n2_50.round(0).should == @neg_two
+ @n2_51.round(0).should == @neg_two
+ @n2_49.round(0).should == @neg_two
+ end
+
+ describe "BigDecimal::ROUND_UP" do
+ it "rounds values away from zero" do
+ @p1_50.round(0, BigDecimal::ROUND_UP).should == @two
+ @p1_51.round(0, BigDecimal::ROUND_UP).should == @two
+ @p1_49.round(0, BigDecimal::ROUND_UP).should == @two
+ @n1_50.round(0, BigDecimal::ROUND_UP).should == @neg_two
+ @n1_51.round(0, BigDecimal::ROUND_UP).should == @neg_two
+ @n1_49.round(0, BigDecimal::ROUND_UP).should == @neg_two
+
+ @p2_50.round(0, BigDecimal::ROUND_UP).should == @three
+ @p2_51.round(0, BigDecimal::ROUND_UP).should == @three
+ @p2_49.round(0, BigDecimal::ROUND_UP).should == @three
+ @n2_50.round(0, BigDecimal::ROUND_UP).should == @neg_three
+ @n2_51.round(0, BigDecimal::ROUND_UP).should == @neg_three
+ @n2_49.round(0, BigDecimal::ROUND_UP).should == @neg_three
+ end
+ end
+
+ describe "BigDecimal::ROUND_DOWN" do
+ it "rounds values towards zero" do
+ @p1_50.round(0, BigDecimal::ROUND_DOWN).should == @one
+ @p1_51.round(0, BigDecimal::ROUND_DOWN).should == @one
+ @p1_49.round(0, BigDecimal::ROUND_DOWN).should == @one
+ @n1_50.round(0, BigDecimal::ROUND_DOWN).should == @neg_one
+ @n1_51.round(0, BigDecimal::ROUND_DOWN).should == @neg_one
+ @n1_49.round(0, BigDecimal::ROUND_DOWN).should == @neg_one
+
+ @p2_50.round(0, BigDecimal::ROUND_DOWN).should == @two
+ @p2_51.round(0, BigDecimal::ROUND_DOWN).should == @two
+ @p2_49.round(0, BigDecimal::ROUND_DOWN).should == @two
+ @n2_50.round(0, BigDecimal::ROUND_DOWN).should == @neg_two
+ @n2_51.round(0, BigDecimal::ROUND_DOWN).should == @neg_two
+ @n2_49.round(0, BigDecimal::ROUND_DOWN).should == @neg_two
+ end
+ end
+
+ describe "BigDecimal::ROUND_HALF_UP" do
+ it "rounds values >= 5 up, otherwise down" do
+ @p1_50.round(0, BigDecimal::ROUND_HALF_UP).should == @two
+ @p1_51.round(0, BigDecimal::ROUND_HALF_UP).should == @two
+ @p1_49.round(0, BigDecimal::ROUND_HALF_UP).should == @one
+ @n1_50.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_two
+ @n1_51.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_two
+ @n1_49.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_one
+
+ @p2_50.round(0, BigDecimal::ROUND_HALF_UP).should == @three
+ @p2_51.round(0, BigDecimal::ROUND_HALF_UP).should == @three
+ @p2_49.round(0, BigDecimal::ROUND_HALF_UP).should == @two
+ @n2_50.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_three
+ @n2_51.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_three
+ @n2_49.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_two
+ end
+ end
+
+ describe "BigDecimal::ROUND_HALF_DOWN" do
+ it "rounds values > 5 up, otherwise down" do
+ @p1_50.round(0, BigDecimal::ROUND_HALF_DOWN).should == @one
+ @p1_51.round(0, BigDecimal::ROUND_HALF_DOWN).should == @two
+ @p1_49.round(0, BigDecimal::ROUND_HALF_DOWN).should == @one
+ @n1_50.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_one
+ @n1_51.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_two
+ @n1_49.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_one
+
+ @p2_50.round(0, BigDecimal::ROUND_HALF_DOWN).should == @two
+ @p2_51.round(0, BigDecimal::ROUND_HALF_DOWN).should == @three
+ @p2_49.round(0, BigDecimal::ROUND_HALF_DOWN).should == @two
+ @n2_50.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_two
+ @n2_51.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_three
+ @n2_49.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_two
+ end
+ end
+
+ describe "BigDecimal::ROUND_CEILING" do
+ it "rounds values towards +infinity" do
+ @p1_50.round(0, BigDecimal::ROUND_CEILING).should == @two
+ @p1_51.round(0, BigDecimal::ROUND_CEILING).should == @two
+ @p1_49.round(0, BigDecimal::ROUND_CEILING).should == @two
+ @n1_50.round(0, BigDecimal::ROUND_CEILING).should == @neg_one
+ @n1_51.round(0, BigDecimal::ROUND_CEILING).should == @neg_one
+ @n1_49.round(0, BigDecimal::ROUND_CEILING).should == @neg_one
+
+ @p2_50.round(0, BigDecimal::ROUND_CEILING).should == @three
+ @p2_51.round(0, BigDecimal::ROUND_CEILING).should == @three
+ @p2_49.round(0, BigDecimal::ROUND_CEILING).should == @three
+ @n2_50.round(0, BigDecimal::ROUND_CEILING).should == @neg_two
+ @n2_51.round(0, BigDecimal::ROUND_CEILING).should == @neg_two
+ @n2_49.round(0, BigDecimal::ROUND_CEILING).should == @neg_two
+ end
+ end
+
+ describe "BigDecimal::ROUND_FLOOR" do
+ it "rounds values towards -infinity" do
+ @p1_50.round(0, BigDecimal::ROUND_FLOOR).should == @one
+ @p1_51.round(0, BigDecimal::ROUND_FLOOR).should == @one
+ @p1_49.round(0, BigDecimal::ROUND_FLOOR).should == @one
+ @n1_50.round(0, BigDecimal::ROUND_FLOOR).should == @neg_two
+ @n1_51.round(0, BigDecimal::ROUND_FLOOR).should == @neg_two
+ @n1_49.round(0, BigDecimal::ROUND_FLOOR).should == @neg_two
+
+ @p2_50.round(0, BigDecimal::ROUND_FLOOR).should == @two
+ @p2_51.round(0, BigDecimal::ROUND_FLOOR).should == @two
+ @p2_49.round(0, BigDecimal::ROUND_FLOOR).should == @two
+ @n2_50.round(0, BigDecimal::ROUND_FLOOR).should == @neg_three
+ @n2_51.round(0, BigDecimal::ROUND_FLOOR).should == @neg_three
+ @n2_49.round(0, BigDecimal::ROUND_FLOOR).should == @neg_three
+ end
+ end
+
+ describe "BigDecimal::ROUND_HALF_EVEN" do
+ it "rounds values > 5 up, < 5 down and == 5 towards even neighbor" do
+ @p1_50.round(0, BigDecimal::ROUND_HALF_EVEN).should == @two
+ @p1_51.round(0, BigDecimal::ROUND_HALF_EVEN).should == @two
+ @p1_49.round(0, BigDecimal::ROUND_HALF_EVEN).should == @one
+ @n1_50.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_two
+ @n1_51.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_two
+ @n1_49.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_one
+
+ @p2_50.round(0, BigDecimal::ROUND_HALF_EVEN).should == @two
+ @p2_51.round(0, BigDecimal::ROUND_HALF_EVEN).should == @three
+ @p2_49.round(0, BigDecimal::ROUND_HALF_EVEN).should == @two
+ @n2_50.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_two
+ @n2_51.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_three
+ @n2_49.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_two
+ end
+ end
+
+ it 'raise exception, if self is special value' do
+ lambda { BigDecimal('NaN').round }.should raise_error(FloatDomainError)
+ lambda { BigDecimal('Infinity').round }.should raise_error(FloatDomainError)
+ lambda { BigDecimal('-Infinity').round }.should raise_error(FloatDomainError)
+ end
+
+ it 'do not raise exception, if self is special value and precision is given' do
+ lambda { BigDecimal('NaN').round(2) }.should_not raise_error(FloatDomainError)
+ lambda { BigDecimal('Infinity').round(2) }.should_not raise_error(FloatDomainError)
+ lambda { BigDecimal('-Infinity').round(2) }.should_not raise_error(FloatDomainError)
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/shared/eql.rb b/spec/ruby/library/bigdecimal/shared/eql.rb
new file mode 100644
index 0000000000..8e3e388bab
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/shared/eql.rb
@@ -0,0 +1,61 @@
+require 'bigdecimal'
+
+describe :bigdecimal_eql, shared: true do
+ before :each do
+ @bg6543_21 = BigDecimal("6543.21")
+ @bg5667_19 = BigDecimal("5667.19")
+ @a = BigDecimal("1.0000000000000000000000000000000000000000005")
+ @b = BigDecimal("1.00000000000000000000000000000000000000000005")
+ @bigint = BigDecimal("1000.0")
+ @nan = BigDecimal("NaN")
+ @infinity = BigDecimal("Infinity")
+ @infinity_minus = BigDecimal("-Infinity")
+ end
+
+ it "tests for equality" do
+ @bg6543_21.send(@method, @bg6543_21).should == true
+ @a.send(@method, @a).should == true
+ @a.send(@method, @b).should == false
+ @bg6543_21.send(@method, @a).should == false
+ @bigint.send(@method, 1000).should == true
+ end
+
+ it "returns false for NaN as it is never equal to any number" do
+ @nan.send(@method, @nan).should == false
+ @a.send(@method, @nan).should == false
+ @nan.send(@method, @a).should == false
+ @nan.send(@method, @infinity).should == false
+ @nan.send(@method, @infinity_minus).should == false
+ @infinity.send(@method, @nan).should == false
+ @infinity_minus.send(@method, @nan).should == false
+ end
+
+ it "returns true for infinity values with the same sign" do
+ @infinity.send(@method, @infinity).should == true
+ @infinity.send(@method, BigDecimal("Infinity")).should == true
+ BigDecimal("Infinity").send(@method, @infinity).should == true
+
+ @infinity_minus.send(@method, @infinity_minus).should == true
+ @infinity_minus.send(@method, BigDecimal("-Infinity")).should == true
+ BigDecimal("-Infinity").send(@method, @infinity_minus).should == true
+ end
+
+ it "returns false for infinity values with different signs" do
+ @infinity.send(@method, @infinity_minus).should == false
+ @infinity_minus.send(@method, @infinity).should == false
+ end
+
+ it "returns false when infinite value compared to finite one" do
+ @infinity.send(@method, @a).should == false
+ @infinity_minus.send(@method, @a).should == false
+
+ @a.send(@method, @infinity).should == false
+ @a.send(@method, @infinity_minus).should == false
+ end
+
+ it "returns false when compared objects that can not be coerced into BigDecimal" do
+ @infinity.send(@method, nil).should == false
+ @bigint.send(@method, nil).should == false
+ @nan.send(@method, nil).should == false
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/shared/modulo.rb b/spec/ruby/library/bigdecimal/shared/modulo.rb
new file mode 100644
index 0000000000..c9691cbf37
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/shared/modulo.rb
@@ -0,0 +1,116 @@
+require 'bigdecimal'
+
+describe :bigdecimal_modulo, shared: true do
+ before :each do
+ @one = BigDecimal("1")
+ @zero = BigDecimal("0")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+ @two = BigDecimal("2")
+ @three = BigDecimal("3")
+ @mixed = BigDecimal("1.23456789")
+ @nan = BigDecimal("NaN")
+ @infinity = BigDecimal("Infinity")
+ @infinity_minus = BigDecimal("-Infinity")
+ @one_minus = BigDecimal("-1")
+ @frac_1 = BigDecimal("1E-9999")
+ @frac_2 = BigDecimal("0.9E-9999")
+ end
+
+ it "returns self modulo other" do
+ bd6543 = BigDecimal("6543.21")
+ bd5667 = BigDecimal("5667.19")
+ a = BigDecimal("1.0000000000000000000000000000000000000000005")
+ b = BigDecimal("1.00000000000000000000000000000000000000000005")
+
+ bd6543.send(@method, 137).should == BigDecimal("104.21")
+ bd5667.send(@method, bignum_value).should == 5667.19
+ bd6543.send(@method, BigDecimal("137.24")).should == BigDecimal("92.93")
+ bd6543.send(@method, 137).should be_close(6543.21.%(137), TOLERANCE)
+ bd6543.send(@method, 137).should == bd6543 % 137
+ bd5667.send(@method, bignum_value).should be_close(5667.19.%(0xffffffff), TOLERANCE)
+ bd5667.send(@method, bignum_value).should == bd5667.%(0xffffffff)
+ bd6543.send(@method, 137.24).should be_close(6543.21.%(137.24), TOLERANCE)
+ a.send(@method, b).should == BigDecimal("0.45E-42")
+ @zero.send(@method, @one).should == @zero
+ @zero.send(@method, @one_minus).should == @zero
+ @two.send(@method, @one).should == @zero
+ @one.send(@method, @two).should == @one
+ @frac_1.send(@method, @one).should == @frac_1
+ @frac_2.send(@method, @one).should == @frac_2
+ @one_minus.send(@method, @one_minus).should == @zero
+ @one_minus.send(@method, @one).should == @zero
+ @one_minus.send(@method, @two).should == @one
+ @one.send(@method, -@two).should == -@one
+
+ @one_minus.modulo(BigDecimal('0.9')).should == BigDecimal('0.8')
+ @one.modulo(BigDecimal('-0.9')).should == BigDecimal('-0.8')
+
+ @one_minus.modulo(BigDecimal('0.8')).should == BigDecimal('0.6')
+ @one.modulo(BigDecimal('-0.8')).should == BigDecimal('-0.6')
+
+ @one_minus.modulo(BigDecimal('0.6')).should == BigDecimal('0.2')
+ @one.modulo(BigDecimal('-0.6')).should == BigDecimal('-0.2')
+
+ @one_minus.modulo(BigDecimal('0.5')).should == @zero
+ @one.modulo(BigDecimal('-0.5')).should == @zero
+ @one_minus.modulo(BigDecimal('-0.5')).should == @zero
+
+ @one_minus.modulo(BigDecimal('0.4')).should == BigDecimal('0.2')
+ @one.modulo(BigDecimal('-0.4')).should == BigDecimal('-0.2')
+
+ @one_minus.modulo(BigDecimal('0.3')).should == BigDecimal('0.2')
+ @one_minus.modulo(BigDecimal('0.2')).should == @zero
+ end
+
+ it "returns a [Float value] when the argument is Float" do
+ @two.send(@method, 2.0).should == 0.0
+ @one.send(@method, 2.0).should == 1.0
+ res = @two.send(@method, 5.0)
+ res.kind_of?(BigDecimal).should == true
+ end
+
+ it "returns NaN if NaN is involved" do
+ @nan.send(@method, @nan).nan?.should == true
+ @nan.send(@method, @one).nan?.should == true
+ @one.send(@method, @nan).nan?.should == true
+ @infinity.send(@method, @nan).nan?.should == true
+ @nan.send(@method, @infinity).nan?.should == true
+ end
+
+ it "returns NaN if the dividend is Infinity" do
+ @infinity.send(@method, @infinity).nan?.should == true
+ @infinity.send(@method, @one).nan?.should == true
+ @infinity.send(@method, @mixed).nan?.should == true
+ @infinity.send(@method, @one_minus).nan?.should == true
+ @infinity.send(@method, @frac_1).nan?.should == true
+
+ @infinity_minus.send(@method, @infinity_minus).nan?.should == true
+ @infinity_minus.send(@method, @one).nan?.should == true
+
+ @infinity.send(@method, @infinity_minus).nan?.should == true
+ @infinity_minus.send(@method, @infinity).nan?.should == true
+ end
+
+ it "returns the dividend if the divisor is Infinity" do
+ @one.send(@method, @infinity).should == @one
+ @one.send(@method, @infinity_minus).should == @one
+ @frac_2.send(@method, @infinity_minus).should == @frac_2
+ end
+
+ it "raises TypeError if the argument cannot be coerced to BigDecimal" do
+ lambda {
+ @one.send(@method, '2')
+ }.should raise_error(TypeError)
+ end
+end
+
+describe :bigdecimal_modulo_zerodivisionerror, shared: true do
+ it "raises ZeroDivisionError if other is zero" do
+ bd5667 = BigDecimal("5667.19")
+
+ lambda { bd5667.send(@method, 0) }.should raise_error(ZeroDivisionError)
+ lambda { bd5667.send(@method, BigDecimal("0")) }.should raise_error(ZeroDivisionError)
+ lambda { @zero.send(@method, @zero) }.should raise_error(ZeroDivisionError)
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/shared/mult.rb b/spec/ruby/library/bigdecimal/shared/mult.rb
new file mode 100644
index 0000000000..b94c9385de
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/shared/mult.rb
@@ -0,0 +1,97 @@
+require 'bigdecimal'
+
+describe :bigdecimal_mult, shared: true do
+ before :each do
+ @zero = BigDecimal "0"
+ @zero_pos = BigDecimal "+0"
+ @zero_neg = BigDecimal "-0"
+
+ @one = BigDecimal "1"
+ @mixed = BigDecimal "1.23456789"
+ @pos_int = BigDecimal "2E5555"
+ @neg_int = BigDecimal "-2E5555"
+ @pos_frac = BigDecimal "2E-9999"
+ @neg_frac = BigDecimal "-2E-9999"
+ @nan = BigDecimal "NaN"
+ @infinity = BigDecimal "Infinity"
+ @infinity_minus = BigDecimal "-Infinity"
+ @one_minus = BigDecimal "-1"
+ @frac_1 = BigDecimal "1E-99999"
+ @frac_2 = BigDecimal "0.9E-99999"
+
+ @e3_minus = BigDecimal "3E-20001"
+ @e = BigDecimal "1.00000000000000000000123456789"
+ @tolerance = @e.sub @one, 1000
+ @tolerance2 = BigDecimal "30001E-20005"
+
+ @special_vals = [@infinity, @infinity_minus, @nan]
+ @regular_vals = [ @one, @mixed, @pos_int, @neg_int,
+ @pos_frac, @neg_frac, @one_minus,
+ @frac_1, @frac_2
+ ]
+ @zeroes = [@zero, @zero_pos, @zero_neg]
+ end
+
+ it "returns zero of appropriate sign if self or argument is zero" do
+ @zero.send(@method, @zero, *@object).sign.should == BigDecimal::SIGN_POSITIVE_ZERO
+ @zero_neg.send(@method, @zero_neg, *@object).sign.should == BigDecimal::SIGN_POSITIVE_ZERO
+ @zero.send(@method, @zero_neg, *@object).sign.should == BigDecimal::SIGN_NEGATIVE_ZERO
+ @zero_neg.send(@method, @zero, *@object).sign.should == BigDecimal::SIGN_NEGATIVE_ZERO
+
+ @one.send(@method, @zero, *@object).sign.should == BigDecimal::SIGN_POSITIVE_ZERO
+ @one.send(@method, @zero_neg, *@object).sign.should == BigDecimal::SIGN_NEGATIVE_ZERO
+
+ @zero.send(@method, @one, *@object).sign.should == BigDecimal::SIGN_POSITIVE_ZERO
+ @zero.send(@method, @one_minus, *@object).sign.should == BigDecimal::SIGN_NEGATIVE_ZERO
+ @zero_neg.send(@method, @one_minus, *@object).sign.should == BigDecimal::SIGN_POSITIVE_ZERO
+ @zero_neg.send(@method, @one, *@object).sign.should == BigDecimal::SIGN_NEGATIVE_ZERO
+ end
+
+ it "returns NaN if NaN is involved" do
+ values = @regular_vals + @zeroes
+
+ values.each do |val|
+ @nan.send(@method, val, *@object).nan?.should == true
+ val.send(@method, @nan, *@object).nan?.should == true
+ end
+ end
+
+ it "returns zero if self or argument is zero" do
+ values = @regular_vals + @zeroes
+
+ values.each do |val|
+ @zeroes.each do |zero|
+ zero.send(@method, val, *@object).should == 0
+ zero.send(@method, val, *@object).zero?.should == true
+ val.send(@method, zero, *@object).should == 0
+ val.send(@method, zero, *@object).zero?.should == true
+ end
+ end
+ end
+
+ it "returns infinite value if self or argument is infinite" do
+ values = @regular_vals
+ infs = [@infinity, @infinity_minus]
+
+ values.each do |val|
+ infs.each do |inf|
+ inf.send(@method, val, *@object).finite?.should == false
+ val.send(@method, inf, *@object).finite?.should == false
+ end
+ end
+
+ @infinity.send(@method, @infinity, *@object).infinite?.should == 1
+ @infinity_minus.send(@method, @infinity_minus, *@object).infinite?.should == 1
+ @infinity.send(@method, @infinity_minus, *@object).infinite?.should == -1
+ @infinity_minus.send(@method, @infinity, *@object).infinite?.should == -1
+ @infinity.send(@method, @one, *@object).infinite?.should == 1
+ @infinity_minus.send(@method, @one, *@object).infinite?.should == -1
+ end
+
+ it "returns NaN if the result is undefined" do
+ @zero.send(@method, @infinity, *@object).nan?.should == true
+ @zero.send(@method, @infinity_minus, *@object).nan?.should == true
+ @infinity.send(@method, @zero, *@object).nan?.should == true
+ @infinity_minus.send(@method, @zero, *@object).nan?.should == true
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/shared/power.rb b/spec/ruby/library/bigdecimal/shared/power.rb
new file mode 100644
index 0000000000..a4848fb2e2
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/shared/power.rb
@@ -0,0 +1,72 @@
+require 'bigdecimal'
+
+describe :bigdecimal_power, shared: true do
+ it "powers of self" do
+ e3_minus = BigDecimal("3E-20001")
+ e3_minus_power_2 = BigDecimal("9E-40002")
+ e3_plus = BigDecimal("3E20001")
+ e2_plus = BigDecimal("2E40001")
+ e5_minus = BigDecimal("5E-40002")
+ e = BigDecimal("1.00000000000000000000123456789")
+ one = BigDecimal("1")
+ ten = BigDecimal("10")
+ # The tolerance is dependent upon the size of BASE_FIG
+ tolerance = BigDecimal("1E-70")
+ ten_powers = BigDecimal("1E10000")
+ pi = BigDecimal("3.14159265358979")
+ e3_minus.send(@method, 2).should == e3_minus_power_2
+ e3_plus.send(@method, 0).should == 1
+ e3_minus.send(@method, 1).should == e3_minus
+ e2_plus.send(@method, -1).should == e5_minus
+ e2_plus.send(@method, -1).should == e5_minus.power(1)
+ (e2_plus.send(@method, -1) * e5_minus.send(@method, -1)).should == 1
+ e.send(@method, 2).should == e * e
+ e.send(@method, -1).should be_close(one.div(e, 120), tolerance)
+ ten.send(@method, 10000).should == ten_powers
+ pi.send(@method, 10).should be_close(Math::PI ** 10, TOLERANCE)
+ end
+
+ it "powers of 1 equal 1" do
+ one = BigDecimal("1")
+ one.send(@method, 0).should == 1
+ one.send(@method, 1).should == 1
+ one.send(@method, 10).should == 1
+ one.send(@method, -10).should == 1
+ end
+
+ it "0 to power of 0 is 1" do
+ zero = BigDecimal("0")
+ zero.send(@method, 0).should == 1
+ end
+
+ it "0 to powers < 0 is Infinity" do
+ zero = BigDecimal("0")
+ infinity = BigDecimal("Infinity")
+ zero.send(@method, -10).should == infinity
+ zero.send(@method, -1).should == infinity
+ end
+
+ it "other powers of 0 are 0" do
+ zero = BigDecimal("0")
+ zero.send(@method, 1).should == 0
+ zero.send(@method, 10).should == 0
+ end
+
+ it "returns NaN if self is NaN" do
+ BigDecimal("NaN").send(@method, -5).nan?.should == true
+ BigDecimal("NaN").send(@method, 5).nan?.should == true
+ end
+
+ it "returns 0.0 if self is infinite and argument is negative" do
+ BigDecimal("Infinity").send(@method, -5).should == 0
+ BigDecimal("-Infinity").send(@method, -5).should == 0
+ end
+
+ it "returns infinite if self is infinite and argument is positive" do
+ infinity = BigDecimal("Infinity")
+ BigDecimal("Infinity").send(@method, 4).should == infinity
+ BigDecimal("-Infinity").send(@method, 4).should == infinity
+ BigDecimal("Infinity").send(@method, 5).should == infinity
+ BigDecimal("-Infinity").send(@method, 5).should == -infinity
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/shared/quo.rb b/spec/ruby/library/bigdecimal/shared/quo.rb
new file mode 100644
index 0000000000..cb51c10d71
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/shared/quo.rb
@@ -0,0 +1,59 @@
+require 'bigdecimal'
+
+describe :bigdecimal_quo, shared: true do
+ before :each do
+ @one = BigDecimal("1")
+ @zero = BigDecimal("0")
+ @zero_plus = BigDecimal("+0")
+ @zero_minus = BigDecimal("-0")
+ @two = BigDecimal("2")
+ @three = BigDecimal("3")
+ @eleven = BigDecimal("11")
+ @nan = BigDecimal("NaN")
+ @infinity = BigDecimal("Infinity")
+ @infinity_minus = BigDecimal("-Infinity")
+ @one_minus = BigDecimal("-1")
+ @frac_1 = BigDecimal("1E-99999")
+ @frac_2 = BigDecimal("0.9E-99999")
+ end
+
+ it "returns a / b" do
+ @two.send(@method, @one, *@object).should == @two
+ @one.send(@method, @two, *@object).should == BigDecimal("0.5")
+ @eleven.send(@method, @three, *@object).should be_close(@three + (@two / @three), TOLERANCE)
+ @one.send(@method, @one_minus, *@object).should == @one_minus
+ @one_minus.send(@method, @one_minus, *@object).should == @one
+ @frac_2.send(@method, @frac_1, *@object).should == BigDecimal("0.9")
+ @frac_1.send(@method, @frac_1, *@object).should == @one
+ @one.send(@method, BigDecimal('-2E5555'), *@object).should == BigDecimal('-0.5E-5555')
+ @one.send(@method, BigDecimal('2E-5555'), *@object).should == BigDecimal('0.5E5555')
+ end
+
+ it "returns 0 if divided by Infinity" do
+ @zero.send(@method, @infinity, *@object).should == 0
+ @frac_2.send(@method, @infinity, *@object).should == 0
+ end
+
+ it "returns (+|-) Infinity if (+|-) Infinity divided by one" do
+ @infinity_minus.send(@method, @one, *@object).should == @infinity_minus
+ @infinity.send(@method, @one, *@object).should == @infinity
+ @infinity_minus.send(@method, @one_minus, *@object).should == @infinity
+ end
+
+ it "returns NaN if Infinity / ((+|-) Infinity)" do
+ @infinity.send(@method, @infinity_minus, *@object).nan?.should == true
+ @infinity_minus.send(@method, @infinity, *@object).nan?.should == true
+ end
+
+ it "returns (+|-) Infinity if divided by zero" do
+ @one.send(@method, @zero, *@object).should == @infinity
+ @one.send(@method, @zero_plus, *@object).should == @infinity
+ @one.send(@method, @zero_minus, *@object).should == @infinity_minus
+ end
+
+ it "returns NaN if zero is divided by zero" do
+ @zero.send(@method, @zero, *@object).nan?.should == true
+ @zero_minus.send(@method, @zero_plus, *@object).nan?.should == true
+ @zero_plus.send(@method, @zero_minus, *@object).nan?.should == true
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/shared/to_int.rb b/spec/ruby/library/bigdecimal/shared/to_int.rb
new file mode 100644
index 0000000000..729a25f511
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/shared/to_int.rb
@@ -0,0 +1,16 @@
+require 'bigdecimal'
+
+describe :bigdecimal_to_int , shared: true do
+ it "raises FloatDomainError if BigDecimal is infinity or NaN" do
+ lambda { BigDecimal("Infinity").send(@method) }.should raise_error(FloatDomainError)
+ lambda { BigDecimal("NaN").send(@method) }.should raise_error(FloatDomainError)
+ end
+
+ it "returns Integer or Bignum otherwise" do
+ BigDecimal("3E-20001").send(@method).should == 0
+ BigDecimal("2E4000").send(@method).should == 2 * 10 ** 4000
+ BigDecimal("2").send(@method).should == 2
+ BigDecimal("2E10").send(@method).should == 20000000000
+ BigDecimal("3.14159").send(@method).should == 3
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/sign_spec.rb b/spec/ruby/library/bigdecimal/sign_spec.rb
new file mode 100644
index 0000000000..ae2c28e9fd
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/sign_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#sign" do
+
+ it "defines several constants for signs" do
+ # are these really correct?
+ BigDecimal::SIGN_POSITIVE_INFINITE.should == 3
+ BigDecimal::SIGN_NEGATIVE_INFINITE.should == -3
+ BigDecimal::SIGN_POSITIVE_ZERO.should == 1
+ BigDecimal::SIGN_NEGATIVE_ZERO.should == -1
+ BigDecimal::SIGN_POSITIVE_FINITE.should == 2
+ BigDecimal::SIGN_NEGATIVE_FINITE.should == -2
+ end
+
+ it "returns positive value if BigDecimal greater than 0" do
+ BigDecimal("1").sign.should == BigDecimal::SIGN_POSITIVE_FINITE
+ BigDecimal("1E-20000000").sign.should == BigDecimal::SIGN_POSITIVE_FINITE
+ BigDecimal("1E200000000").sign.should == BigDecimal::SIGN_POSITIVE_FINITE
+ BigDecimal("Infinity").sign.should == BigDecimal::SIGN_POSITIVE_INFINITE
+ end
+
+ it "returns negative value if BigDecimal less than 0" do
+ BigDecimal("-1").sign.should == BigDecimal::SIGN_NEGATIVE_FINITE
+ BigDecimal("-1E-9990000").sign.should == BigDecimal::SIGN_NEGATIVE_FINITE
+ BigDecimal("-1E20000000").sign.should == BigDecimal::SIGN_NEGATIVE_FINITE
+ BigDecimal("-Infinity").sign.should == BigDecimal::SIGN_NEGATIVE_INFINITE
+ end
+
+ it "returns positive zero if BigDecimal equals positive zero" do
+ BigDecimal("0").sign.should == BigDecimal::SIGN_POSITIVE_ZERO
+ BigDecimal("0E-200000000").sign.should == BigDecimal::SIGN_POSITIVE_ZERO
+ BigDecimal("0E200000000").sign.should == BigDecimal::SIGN_POSITIVE_ZERO
+ end
+
+ it "returns negative zero if BigDecimal equals negative zero" do
+ BigDecimal("-0").sign.should == BigDecimal::SIGN_NEGATIVE_ZERO
+ BigDecimal("-0E-200000000").sign.should == BigDecimal::SIGN_NEGATIVE_ZERO
+ BigDecimal("-0E200000000").sign.should == BigDecimal::SIGN_NEGATIVE_ZERO
+ end
+
+ it "returns BigDecimal::SIGN_NaN if BigDecimal is NaN" do
+ BigDecimal("NaN").sign.should == BigDecimal::SIGN_NaN
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/split_spec.rb b/spec/ruby/library/bigdecimal/split_spec.rb
new file mode 100644
index 0000000000..f9b4bab5f7
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/split_spec.rb
@@ -0,0 +1,86 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#split" do
+
+ before :each do
+ @arr = BigDecimal("0.314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593014782083152134043E1").split
+ @arr_neg = BigDecimal("-0.314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593014782083152134043E1").split
+ @digits = "922337203685477580810101333333333333333333333333333"
+ @arr_big = BigDecimal("00#{@digits}000").split
+ @arr_big_neg = BigDecimal("-00#{@digits}000").split
+ @huge = BigDecimal('100000000000000000000000000000000000000000001E90000000').split
+
+ @infinity = BigDecimal("Infinity")
+ @infinity_neg = BigDecimal("-Infinity")
+ @nan = BigDecimal("NaN")
+ @zero = BigDecimal("0")
+ @zero_neg = BigDecimal("-0")
+ end
+
+ it "splits BigDecimal in an array with four values" do
+ @arr.size.should == 4
+ end
+
+ it "first value: 1 for numbers > 0" do
+ @arr[0].should == 1
+ @arr_big[0].should == 1
+ @zero.split[0].should == 1
+ @huge[0].should == 1
+ BigDecimal("+0").split[0].should == 1
+ BigDecimal("1E400").split[0].should == 1
+ @infinity.split[0].should == 1
+ end
+
+ it "first value: -1 for numbers < 0" do
+ @arr_neg[0].should == -1
+ @arr_big_neg[0].should == -1
+ @zero_neg.split[0].should == -1
+ BigDecimal("-1E400").split[0].should == -1
+ @infinity_neg.split[0].should == -1
+ end
+
+ it "first value: 0 if BigDecimal is NaN" do
+ BigDecimal("NaN").split[0].should == 0
+ end
+
+ it "second value: a string with the significant digits" do
+ string = "314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593014782083152134043"
+ @arr[1].should == string
+ @arr_big[1].should == @digits
+ @arr_big_neg[1].should == @digits
+ @huge[1].should == "100000000000000000000000000000000000000000001"
+ @infinity.split[1].should == @infinity.to_s
+ @nan.split[1].should == @nan.to_s
+ @infinity_neg.split[1].should == @infinity.to_s
+ @zero.split[1].should == "0"
+ BigDecimal("-0").split[1].should == "0"
+ end
+
+ it "third value: the base (currently always ten)" do
+ @arr[2].should == 10
+ @arr_neg[2].should == 10
+ @arr_big[2].should == 10
+ @arr_big_neg[2].should == 10
+ @huge[2].should == 10
+ @infinity.split[2].should == 10
+ @nan.split[2].should == 10
+ @infinity_neg.split[2].should == 10
+ @zero.split[2].should == 10
+ @zero_neg.split[2].should == 10
+ end
+
+ it "fourth value: the exponent" do
+ @arr[3].should == 1
+ @arr_neg[3].should == 1
+ @arr_big[3].should == 54
+ @arr_big_neg[3].should == 54
+ @huge[3].should == 90000045
+ @infinity.split[3].should == 0
+ @nan.split[3].should == 0
+ @infinity_neg.split[3].should == 0
+ @zero.split[3].should == 0
+ @zero_neg.split[3].should == 0
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/sqrt_spec.rb b/spec/ruby/library/bigdecimal/sqrt_spec.rb
new file mode 100644
index 0000000000..06a8348203
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/sqrt_spec.rb
@@ -0,0 +1,112 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'bigdecimal'
+
+describe "BigDecimal#sqrt" do
+ before :each do
+ @one = BigDecimal("1")
+ @zero = BigDecimal("0")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+ @two = BigDecimal("2.0")
+ @three = BigDecimal("3.0")
+ @nan = BigDecimal("NaN")
+ @infinity = BigDecimal("Infinity")
+ @infinity_minus = BigDecimal("-Infinity")
+ @one_minus = BigDecimal("-1")
+ @frac_1 = BigDecimal("1E-99999")
+ @frac_2 = BigDecimal("0.9E-99999")
+ end
+
+ it "returns square root of 2 with desired precision" do
+ string = "1.41421356237309504880168872420969807856967187537694807317667973799073247846210703885038753432764157"
+ (1..99).each { |idx|
+ @two.sqrt(idx).should be_close(BigDecimal(string), BigDecimal("1E-#{idx-1}"))
+ }
+ end
+
+ it "returns square root of 3 with desired precision" do
+ sqrt_3 = "1.732050807568877293527446341505872366942805253810380628055806979451933016908800037081146186757248575"
+ (1..99).each { |idx|
+ @three.sqrt(idx).should be_close(BigDecimal(sqrt_3), BigDecimal("1E-#{idx-1}"))
+ }
+ end
+
+ it "returns square root of 121 with desired precision" do
+ BigDecimal('121').sqrt(5).should be_close(11, 0.00001)
+ end
+
+ it "returns square root of 0.9E-99999 with desired precision" do
+ @frac_2.sqrt(1).to_s.should =~ /\A0\.3E-49999\z/i
+ end
+
+ it "raises ArgumentError when no argument is given" do
+ lambda {
+ @one.sqrt
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if a negative number is given" do
+ lambda {
+ @one.sqrt(-1)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if 2 arguments are given" do
+ lambda {
+ @one.sqrt(1, 1)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises TypeError if nil is given" do
+ lambda {
+ @one.sqrt(nil)
+ }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError if a string is given" do
+ lambda {
+ @one.sqrt("stuff")
+ }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError if a plain Object is given" do
+ lambda {
+ @one.sqrt(Object.new)
+ }.should raise_error(TypeError)
+ end
+
+ it "returns 1 if precision is 0 or 1" do
+ @one.sqrt(1).should == 1
+ @one.sqrt(0).should == 1
+ end
+
+ it "raises FloatDomainError on negative values" do
+ lambda {
+ BigDecimal('-1').sqrt(10)
+ }.should raise_error(FloatDomainError)
+ end
+
+ it "returns positive infitinity for infinity" do
+ @infinity.sqrt(1).should == @infinity
+ end
+
+ it "raises FloatDomainError for negative infinity" do
+ lambda {
+ @infinity_minus.sqrt(1)
+ }.should raise_error(FloatDomainError)
+ end
+
+ it "raises FloatDomainError for NaN" do
+ lambda {
+ @nan.sqrt(1)
+ }.should raise_error(FloatDomainError)
+ end
+
+ it "returns 0 for 0, +0.0 and -0.0" do
+ @zero.sqrt(1).should == 0
+ @zero_pos.sqrt(1).should == 0
+ @zero_neg.sqrt(1).should == 0
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/sub_spec.rb b/spec/ruby/library/bigdecimal/sub_spec.rb
new file mode 100644
index 0000000000..7f0305a1c6
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/sub_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#sub" do
+
+ before :each do
+ @one = BigDecimal("1")
+ @zero = BigDecimal("0")
+ @two = BigDecimal("2")
+ @nan = BigDecimal("NaN")
+ @infinity = BigDecimal("Infinity")
+ @infinity_minus = BigDecimal("-Infinity")
+ @one_minus = BigDecimal("-1")
+ @frac_1 = BigDecimal("1E-99999")
+ @frac_2 = BigDecimal("0.9E-99999")
+ end
+
+ it "returns a - b with given precision" do
+ # documentation states, that precision is optional
+ # but implementation raises ArgumentError if not given.
+
+ @two.sub(@one, 1).should == @one
+ @one.sub(@two, 1).should == @one_minus
+ @one.sub(@one_minus, 1).should == @two
+ @frac_2.sub(@frac_1, 1000000).should == BigDecimal("-0.1E-99999")
+ @frac_2.sub(@frac_1, 1).should == BigDecimal("-0.1E-99999")
+ # the above two examples puzzle me.
+ in_arow_one = BigDecimal("1.23456789")
+ in_arow_two = BigDecimal("1.2345678")
+ in_arow_one.sub(in_arow_two, 10).should == BigDecimal("0.9E-7")
+ @two.sub(@two,1).should == @zero
+ @frac_1.sub(@frac_1, 1000000).should == @zero
+ end
+
+ it "returns NaN if NaN is involved" do
+ @one.sub(@nan, 1).nan?.should == true
+ @nan.sub(@one, 1).nan?.should == true
+ end
+
+ it "returns NaN if both values are infinite with the same signs" do
+ @infinity.sub(@infinity, 1).nan?.should == true
+ @infinity_minus.sub(@infinity_minus, 1).nan?.should == true
+ end
+
+ it "returns Infinity or -Infinity if these are involved" do
+ @infinity.sub(@infinity_minus, 1).should == @infinity
+ @infinity_minus.sub(@infinity, 1).should == @infinity_minus
+ @zero.sub(@infinity, 1).should == @infinity_minus
+ @frac_2.sub( @infinity, 1).should == @infinity_minus
+ @two.sub(@infinity, 1).should == @infinity_minus
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/to_f_spec.rb b/spec/ruby/library/bigdecimal/to_f_spec.rb
new file mode 100644
index 0000000000..1cc25d5c40
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/to_f_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#to_f" do
+ before :each do
+ @one = BigDecimal("1")
+ @zero = BigDecimal("0")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+ @two = BigDecimal("2")
+ @three = BigDecimal("3")
+ @nan = BigDecimal("NaN")
+ @infinity = BigDecimal("Infinity")
+ @infinity_minus = BigDecimal("-Infinity")
+ @one_minus = BigDecimal("-1")
+ @frac_1 = BigDecimal("1E-99999")
+ @frac_2 = BigDecimal("0.9E-99999")
+ @vals = [@one, @zero, @two, @three, @frac_1, @frac_2]
+ @spec_vals = [@zero_pos, @zero_neg, @nan, @infinity, @infinity_minus]
+ end
+
+ it "returns number of type float" do
+ BigDecimal("3.14159").to_f.should be_kind_of(Float)
+ @vals.each { |val| val.to_f.should be_kind_of(Float) }
+ @spec_vals.each { |val| val.to_f.should be_kind_of(Float) }
+ end
+
+ it "rounds correctly to Float precision" do
+ bigdec = BigDecimal("3.141592653589793238462643383279502884197169399375")
+ bigdec.to_f.should be_close(3.14159265358979, TOLERANCE)
+ @one.to_f.should == 1.0
+ @two.to_f.should == 2.0
+ @three.to_f.should be_close(3.0, TOLERANCE)
+ @one_minus.to_f.should == -1.0
+
+ # regression test for [ruby-talk:338957]
+ BigDecimal("10.03").to_f.should == 10.03
+ end
+
+ it "properly handles special values" do
+ @zero.to_f.should == 0
+ @zero.to_f.to_s.should == "0.0"
+
+ @nan.to_f.nan?.should == true
+
+ @infinity.to_f.infinite?.should == 1
+ @infinity_minus.to_f.infinite?.should == -1
+ end
+
+ it "remembers negative zero when converted to float" do
+ @zero_neg.to_f.should == 0
+ @zero_neg.to_f.to_s.should == "-0.0"
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/to_i_spec.rb b/spec/ruby/library/bigdecimal/to_i_spec.rb
new file mode 100644
index 0000000000..09481fce15
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/to_i_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_int'
+require 'bigdecimal'
+
+describe "BigDecimal#to_i" do
+ it_behaves_like :bigdecimal_to_int, :to_i
+end
diff --git a/spec/ruby/library/bigdecimal/to_int_spec.rb b/spec/ruby/library/bigdecimal/to_int_spec.rb
new file mode 100644
index 0000000000..4df6749845
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/to_int_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_int'
+require 'bigdecimal'
+
+
+describe "BigDecimal#to_int" do
+ it_behaves_like :bigdecimal_to_int, :to_int
+end
diff --git a/spec/ruby/library/bigdecimal/to_r_spec.rb b/spec/ruby/library/bigdecimal/to_r_spec.rb
new file mode 100644
index 0000000000..91d2b33993
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/to_r_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#to_r" do
+
+ it "returns a Rational" do
+ BigDecimal("3.14159").to_r.should be_kind_of(Rational)
+ end
+
+ it "returns a Rational with bignum values" do
+ r = BigDecimal("3.141592653589793238462643").to_r
+ r.numerator.should eql(3141592653589793238462643)
+ r.denominator.should eql(1000000000000000000000000)
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/to_s_spec.rb b/spec/ruby/library/bigdecimal/to_s_spec.rb
new file mode 100644
index 0000000000..75741c5050
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/to_s_spec.rb
@@ -0,0 +1,82 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#to_s" do
+
+ before :each do
+ @bigdec_str = "3.14159265358979323846264338327950288419716939937"
+ @bigneg_str = "-3.1415926535897932384626433832795028841971693993"
+ @bigdec = BigDecimal(@bigdec_str)
+ @bigneg = BigDecimal(@bigneg_str)
+ end
+
+ it "return type is of class String" do
+ @bigdec.to_s.kind_of?(String).should == true
+ @bigneg.to_s.kind_of?(String).should == true
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "the default format looks like 0.xxxxEnn" do
+ @bigdec.to_s.should =~ /^0\.[0-9]*E[0-9]*$/
+ end
+ end
+
+ ruby_version_is '2.4' do
+ it "the default format looks like 0.xxxxenn" do
+ @bigdec.to_s.should =~ /^0\.[0-9]*e[0-9]*$/
+ end
+ end
+
+ it "takes an optional argument" do
+ lambda {@bigdec.to_s("F")}.should_not raise_error()
+ end
+
+ it "starts with + if + is supplied and value is positive" do
+ @bigdec.to_s("+").should =~ /^\+.*/
+ @bigneg.to_s("+").should_not =~ /^\+.*/
+ end
+
+ it "inserts a space every n chars, if integer n is supplied" do
+ re =\
+ /\A0\.314 159 265 358 979 323 846 264 338 327 950 288 419 716 939 937E1\z/i
+ @bigdec.to_s(3).should =~ re
+
+ str1 = '-123.45678 90123 45678 9'
+ BigDecimal("-123.45678901234567890").to_s('5F').should == str1
+ # trailing zeroes removed
+ BigDecimal("1.00000000000").to_s('1F').should == "1.0"
+ # 0 is treated as no spaces
+ BigDecimal("1.2345").to_s('0F').should == "1.2345"
+ end
+
+ it "can return a leading space for values > 0" do
+ @bigdec.to_s(" F").should =~ /\ .*/
+ @bigneg.to_s(" F").should_not =~ /\ .*/
+ end
+
+ it "removes trailing spaces in floating point notation" do
+ BigDecimal('-123.45678901234567890').to_s('F').should == "-123.4567890123456789"
+ BigDecimal('1.2500').to_s('F').should == "1.25"
+ BigDecimal('0000.00000').to_s('F').should == "0.0"
+ BigDecimal('-00.000010000').to_s('F').should == "-0.00001"
+ BigDecimal("5.00000E-2").to_s("F").should == "0.05"
+
+ BigDecimal("500000").to_s("F").should == "500000.0"
+ BigDecimal("5E2").to_s("F").should == "500.0"
+ BigDecimal("-5E100").to_s("F").should == "-5" + "0" * 100 + ".0"
+ end
+
+ it "can use engineering notation" do
+ @bigdec.to_s("E").should =~ /^0\.[0-9]*E[0-9]*$/i
+ end
+
+ it "can use conventional floating point notation" do
+ %w[f F].each do |format_char|
+ @bigdec.to_s(format_char).should == @bigdec_str
+ @bigneg.to_s(format_char).should == @bigneg_str
+ str2 = "+123.45678901 23456789"
+ BigDecimal('123.45678901234567890').to_s("+8#{format_char}").should == str2
+ end
+ end
+
+end
diff --git a/spec/ruby/library/bigdecimal/truncate_spec.rb b/spec/ruby/library/bigdecimal/truncate_spec.rb
new file mode 100644
index 0000000000..7edee771c9
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/truncate_spec.rb
@@ -0,0 +1,81 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#truncate" do
+
+ before :each do
+ @arr = ['3.14159', '8.7', "0.314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593014782083152134043E1"]
+ @big = BigDecimal("123456.789")
+ @nan = BigDecimal('NaN')
+ @infinity = BigDecimal('Infinity')
+ @infinity_negative = BigDecimal('-Infinity')
+ end
+
+ it "returns value of type Integer." do
+ @arr.each do |x|
+ BigDecimal(x).truncate.kind_of?(Integer).should == true
+ end
+ end
+
+ it "returns the integer part as a BigDecimal if no precision given" do
+ BigDecimal(@arr[0]).truncate.should == 3
+ BigDecimal(@arr[1]).truncate.should == 8
+ BigDecimal(@arr[2]).truncate.should == 3
+ BigDecimal('0').truncate.should == 0
+ BigDecimal('0.1').truncate.should == 0
+ BigDecimal('-0.1').truncate.should == 0
+ BigDecimal('1.5').truncate.should == 1
+ BigDecimal('-1.5').truncate.should == -1
+ BigDecimal('1E10').truncate.should == BigDecimal('1E10')
+ BigDecimal('-1E10').truncate.should == BigDecimal('-1E10')
+ BigDecimal('1.8888E10').truncate.should == BigDecimal('1.8888E10')
+ BigDecimal('-1E-1').truncate.should == 0
+ end
+
+ it "returns value of given precision otherwise" do
+ BigDecimal('-1.55').truncate(1).should == BigDecimal('-1.5')
+ BigDecimal('1.55').truncate(1).should == BigDecimal('1.5')
+ BigDecimal(@arr[0]).truncate(2).should == BigDecimal("3.14")
+ BigDecimal('123.456').truncate(2).should == BigDecimal("123.45")
+ BigDecimal('123.456789').truncate(4).should == BigDecimal("123.4567")
+ BigDecimal('0.456789').truncate(10).should == BigDecimal("0.456789")
+ BigDecimal('-1E-1').truncate(1).should == BigDecimal('-0.1')
+ BigDecimal('-1E-1').truncate(2).should == BigDecimal('-0.1E0')
+ BigDecimal('-1E-1').truncate.should == BigDecimal('0')
+ BigDecimal('-1E-1').truncate(0).should == BigDecimal('0')
+ BigDecimal('-1E-1').truncate(-1).should == BigDecimal('0')
+ BigDecimal('-1E-1').truncate(-2).should == BigDecimal('0')
+
+ BigDecimal(@arr[1]).truncate(1).should == BigDecimal("8.7")
+ BigDecimal(@arr[2]).truncate(100).should == BigDecimal(\
+ "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679")
+ end
+
+ it "sets n digits left of the decimal point to 0, if given n < 0" do
+ @big.truncate(-1).should == BigDecimal("123450.0")
+ @big.truncate(-2).should == BigDecimal("123400.0")
+ BigDecimal(@arr[2]).truncate(-1).should == 0
+ end
+
+ it "returns NaN if self is NaN" do
+ @nan.truncate(-1).nan?.should == true
+ @nan.truncate(+1).nan?.should == true
+ @nan.truncate(0).nan?.should == true
+ end
+
+ it "returns Infinity if self is infinite" do
+ @infinity.truncate(-1).should == @infinity
+ @infinity.truncate(+1).should == @infinity
+ @infinity.truncate(0).should == @infinity
+
+ @infinity_negative.truncate(-1).should == @infinity_negative
+ @infinity_negative.truncate(+1).should == @infinity_negative
+ @infinity_negative.truncate(0).should == @infinity_negative
+ end
+
+ it "returns the same value if self is special value" do
+ lambda { @nan.truncate }.should raise_error(FloatDomainError)
+ lambda { @infinity.truncate }.should raise_error(FloatDomainError)
+ lambda { @infinity_negative.truncate }.should raise_error(FloatDomainError)
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/uminus_spec.rb b/spec/ruby/library/bigdecimal/uminus_spec.rb
new file mode 100644
index 0000000000..5e2a786c4c
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/uminus_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#-@" do
+ before :each do
+ @one = BigDecimal("1")
+ @zero = BigDecimal("0")
+ @zero_pos = BigDecimal("+0")
+ @zero_neg = BigDecimal("-0")
+ @nan = BigDecimal("NaN")
+ @infinity = BigDecimal("Infinity")
+ @infinity_minus = BigDecimal("-Infinity")
+ @one_minus = BigDecimal("-1")
+ @frac_1 = BigDecimal("1E-99999")
+ @frac_2 = BigDecimal("0.9E-99999")
+ @big = BigDecimal("333E99999")
+ @big_neg = BigDecimal("-333E99999")
+ @values = [@one, @zero, @zero_pos, @zero_neg, @infinity,
+ @infinity_minus, @one_minus, @frac_1, @frac_2, @big, @big_neg]
+ end
+
+ it "negates self" do
+ @one.send(:-@).should == @one_minus
+ @one_minus.send(:-@).should == @one
+ @frac_1.send(:-@).should == BigDecimal("-1E-99999")
+ @frac_2.send(:-@).should == BigDecimal("-0.9E-99999")
+ @big.send(:-@).should == @big_neg
+ @big_neg.send(:-@).should == @big
+ BigDecimal("2.221").send(:-@).should == BigDecimal("-2.221")
+ BigDecimal("2E10000").send(:-@).should == BigDecimal("-2E10000")
+ some_number = BigDecimal("2455999221.5512")
+ some_number_neg = BigDecimal("-2455999221.5512")
+ some_number.send(:-@).should == some_number_neg
+ (-BigDecimal("-5.5")).should == BigDecimal("5.5")
+ another_number = BigDecimal("-8.551551551551551551")
+ another_number_pos = BigDecimal("8.551551551551551551")
+ another_number.send(:-@).should == another_number_pos
+ @values.each do |val|
+ (val.send(:-@).send(:-@)).should == val
+ end
+ end
+
+ it "properly handles special values" do
+ @infinity.send(:-@).should == @infinity_minus
+ @infinity_minus.send(:-@).should == @infinity
+ @infinity.send(:-@).infinite?.should == -1
+ @infinity_minus.send(:-@).infinite?.should == 1
+
+ @zero.send(:-@).should == @zero
+ @zero.send(:-@).sign.should == -1
+ @zero_pos.send(:-@).should == @zero
+ @zero_pos.send(:-@).sign.should == -1
+ @zero_neg.send(:-@).should == @zero
+ @zero_neg.send(:-@).sign.should == 1
+
+ @nan.send(:-@).nan?.should == true
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/uplus_spec.rb b/spec/ruby/library/bigdecimal/uplus_spec.rb
new file mode 100644
index 0000000000..77483046b7
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/uplus_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#+@" do
+ it "returns the same value with same sign (twos complement)" do
+ first = BigDecimal("34.56")
+ first.send(:+@).should == first
+ second = BigDecimal("-34.56")
+ second.send(:+@).should == second
+ third = BigDecimal("0.0")
+ third.send(:+@).should == third
+ fourth = BigDecimal("2E1000000")
+ fourth.send(:+@).should == fourth
+ fifth = BigDecimal("123456789E-1000000")
+ fifth.send(:+@).should == fifth
+ end
+end
diff --git a/spec/ruby/library/bigdecimal/zero_spec.rb b/spec/ruby/library/bigdecimal/zero_spec.rb
new file mode 100644
index 0000000000..c5d3acb8c3
--- /dev/null
+++ b/spec/ruby/library/bigdecimal/zero_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#zero?" do
+
+ it "returns true if self does equal zero" do
+ really_small_zero = BigDecimal("0E-200000000")
+ really_big_zero = BigDecimal("0E200000000000")
+ really_small_zero.zero?.should == true
+ really_big_zero.zero?.should == true
+ BigDecimal("0.000000000000000000000000").zero?.should == true
+ BigDecimal("0").zero?.should == true
+ BigDecimal("0E0").zero?.should == true
+ BigDecimal("+0").zero?.should == true
+ BigDecimal("-0").zero?.should == true
+ end
+
+ it "returns false otherwise" do
+ BigDecimal("0000000001").zero?.should == false
+ BigDecimal("2E40001").zero?.should == false
+ BigDecimal("3E-20001").zero?.should == false
+ BigDecimal("Infinity").zero?.should == false
+ BigDecimal("-Infinity").zero?.should == false
+ BigDecimal("NaN").zero?.should == false
+ end
+
+end
diff --git a/spec/ruby/library/bigmath/log_spec.rb b/spec/ruby/library/bigmath/log_spec.rb
new file mode 100644
index 0000000000..2ffbf8b6b9
--- /dev/null
+++ b/spec/ruby/library/bigmath/log_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require 'bigdecimal'
+
+describe "BigDecimal#log" do
+ it "handles high-precision Rational arguments" do
+ result = BigDecimal('0.22314354220170971436137296411949880462556361100856391620766259404746040597133837784E0')
+ r = Rational(1_234_567_890, 987_654_321)
+ BigMath.log(r, 50).should == result
+ end
+end
diff --git a/spec/ruby/library/cgi/cookie/domain_spec.rb b/spec/ruby/library/cgi/cookie/domain_spec.rb
new file mode 100644
index 0000000000..962609ebaf
--- /dev/null
+++ b/spec/ruby/library/cgi/cookie/domain_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::Cookie#domain" do
+ it "returns self's domain" do
+ cookie = CGI::Cookie.new("test-cookie")
+ cookie.domain.should be_nil
+
+ cookie = CGI::Cookie.new("name" => "test-cookie", "domain" => "example.com")
+ cookie.domain.should == "example.com"
+ end
+end
+
+describe "CGI::Cookie#domain=" do
+ it "sets self's domain" do
+ cookie = CGI::Cookie.new("test-cookie")
+ cookie.domain = "test.com"
+ cookie.domain.should == "test.com"
+
+ cookie.domain = "example.com"
+ cookie.domain.should == "example.com"
+ end
+end
diff --git a/spec/ruby/library/cgi/cookie/expires_spec.rb b/spec/ruby/library/cgi/cookie/expires_spec.rb
new file mode 100644
index 0000000000..c8f26d01cd
--- /dev/null
+++ b/spec/ruby/library/cgi/cookie/expires_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::Cookie#expires" do
+ it "returns self's expiration date" do
+ cookie = CGI::Cookie.new("test-cookie")
+ cookie.expires.should be_nil
+
+ cookie = CGI::Cookie.new("name" => "test-cookie", "expires" => Time.at(1196524602))
+ cookie.expires.should == Time.at(1196524602)
+ end
+end
+
+describe "CGI::Cookie#expires=" do
+ it "sets self's expiration date" do
+ cookie = CGI::Cookie.new("test-cookie")
+ cookie.expires = Time.at(1196524602)
+ cookie.expires.should == Time.at(1196524602)
+
+ cookie.expires = Time.at(1196525000)
+ cookie.expires.should == Time.at(1196525000)
+ end
+end
diff --git a/spec/ruby/library/cgi/cookie/initialize_spec.rb b/spec/ruby/library/cgi/cookie/initialize_spec.rb
new file mode 100644
index 0000000000..f41129a9cf
--- /dev/null
+++ b/spec/ruby/library/cgi/cookie/initialize_spec.rb
@@ -0,0 +1,147 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::Cookie#initialize when passed String" do
+ before :each do
+ @cookie = CGI::Cookie.allocate
+ end
+
+ it "sets the self's name to the passed String" do
+ @cookie.send(:initialize, "test-cookie")
+ @cookie.name.should == "test-cookie"
+ end
+
+ it "sets the self's value to an empty Array" do
+ @cookie.send(:initialize, "test-cookie")
+ @cookie.value.should == []
+ end
+
+ it "sets self to a non-secure cookie" do
+ @cookie.send(:initialize, "test")
+ @cookie.secure.should be_false
+ end
+
+ it "does set self's path to an empty String when ENV[\"SCRIPT_NAME\"] is not set" do
+ @cookie.send(:initialize, "test-cookie")
+ @cookie.path.should == ""
+ end
+
+ it "does set self's path based on ENV[\"SCRIPT_NAME\"] when ENV[\"SCRIPT_NAME\"] is set" do
+ old_script_name = ENV["SCRIPT_NAME"]
+
+ begin
+ ENV["SCRIPT_NAME"] = "some/path/script.rb"
+ @cookie.send(:initialize, "test-cookie")
+ @cookie.path.should == "some/path/"
+
+ ENV["SCRIPT_NAME"] = "script.rb"
+ @cookie.send(:initialize, "test-cookie")
+ @cookie.path.should == ""
+
+ ENV["SCRIPT_NAME"] = nil
+ @cookie.send(:initialize, "test-cookie")
+ @cookie.path.should == ""
+ ensure
+ ENV["SCRIPT_NAME"] = old_script_name
+ end
+ end
+
+ it "does not set self's expiration date" do
+ @cookie.expires.should be_nil
+ end
+
+ it "does not set self's domain" do
+ @cookie.domain.should be_nil
+ end
+end
+
+describe "CGI::Cookie#initialize when passed Hash" do
+ before :each do
+ @cookie = CGI::Cookie.allocate
+ end
+
+ it "sets self's contents based on the passed Hash" do
+ @cookie.send(:initialize,
+ 'name' => 'test-cookie',
+ 'value' => ["one", "two", "three"],
+ 'path' => 'some/path/',
+ 'domain' => 'example.com',
+ 'expires' => Time.at(1196524602),
+ 'secure' => true)
+
+ @cookie.name.should == "test-cookie"
+ @cookie.value.should == ["one", "two", "three"]
+ @cookie.path.should == "some/path/"
+ @cookie.domain.should == "example.com"
+ @cookie.expires.should == Time.at(1196524602)
+ @cookie.secure.should be_true
+ end
+
+ it "does set self's path based on ENV[\"SCRIPT_NAME\"] when the Hash has no 'path' entry" do
+ old_script_name = ENV["SCRIPT_NAME"]
+
+ begin
+ ENV["SCRIPT_NAME"] = "some/path/script.rb"
+ @cookie.send(:initialize, 'name' => 'test-cookie')
+ @cookie.path.should == "some/path/"
+
+ ENV["SCRIPT_NAME"] = "script.rb"
+ @cookie.send(:initialize, 'name' => 'test-cookie')
+ @cookie.path.should == ""
+
+ ENV["SCRIPT_NAME"] = nil
+ @cookie.send(:initialize, 'name' => 'test-cookie')
+ @cookie.path.should == ""
+ ensure
+ ENV["SCRIPT_NAME"] = old_script_name
+ end
+ end
+
+ it "tries to convert the Hash's 'value' to an Array using #Array" do
+ obj = mock("Converted To Array")
+ obj.should_receive(:to_ary).and_return(["1", "2", "3"])
+ @cookie.send(:initialize,
+ 'name' => 'test-cookie',
+ 'value' => obj)
+ @cookie.value.should == [ "1", "2", "3" ]
+
+ obj = mock("Converted To Array")
+ obj.should_receive(:to_a).and_return(["one", "two", "three"])
+ @cookie.send(:initialize,
+ 'name' => 'test-cookie',
+ 'value' => obj)
+ @cookie.value.should == [ "one", "two", "three" ]
+
+ obj = mock("Put into an Array")
+ @cookie.send(:initialize,
+ 'name' => 'test-cookie',
+ 'value' => obj)
+ @cookie.value.should == [ obj ]
+ end
+
+ it "raises a ArgumentError when the passed Hash has no 'name' entry" do
+ lambda { @cookie.send(:initialize, {}) }.should raise_error(ArgumentError)
+ lambda { @cookie.send(:initialize, "value" => "test") }.should raise_error(ArgumentError)
+ end
+end
+
+describe "CGI::Cookie#initialize when passed String, values ..." do
+ before :each do
+ @cookie = CGI::Cookie.allocate
+ end
+
+ it "sets the self's name to the passed String" do
+ @cookie.send(:initialize, "test-cookie", "one", "two", "three")
+ @cookie.name.should == "test-cookie"
+ end
+
+ it "sets the self's value to an Array containing all passed values" do
+ @cookie.send(:initialize, "test-cookie", "one", "two", "three")
+ @cookie.value.should == ["one", "two", "three"]
+ end
+
+ it "sets self to a non-secure cookie" do
+ @cookie.send(:initialize, "test", "one", "two", "three")
+ @cookie.secure.should be_false
+ end
+end
diff --git a/spec/ruby/library/cgi/cookie/name_spec.rb b/spec/ruby/library/cgi/cookie/name_spec.rb
new file mode 100644
index 0000000000..14226824c8
--- /dev/null
+++ b/spec/ruby/library/cgi/cookie/name_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::Cookie#name" do
+ it "returns self's 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"
+ 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 = "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
new file mode 100644
index 0000000000..c714aab300
--- /dev/null
+++ b/spec/ruby/library/cgi/cookie/parse_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::Cookie.parse" do
+ it "parses a raw cookie string into a hash of Cookies" 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
+ end
+
+ ruby_version_is ""..."2.4" do
+ it "uses , for cookie separators" do
+ expected = {
+ "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
+ end
+ end
+
+ ruby_version_is "2.4" do
+ it "does not use , for cookie separators" do
+ expected = {
+ "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
+ end
+ end
+
+ it "unescapes the Cookie values" do
+ cookie = "test-cookie=+%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7E"
+ expected = { "test-cookie" => [ " !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" ] }
+ CGI::Cookie.parse(cookie).should == expected
+ end
+end
diff --git a/spec/ruby/library/cgi/cookie/path_spec.rb b/spec/ruby/library/cgi/cookie/path_spec.rb
new file mode 100644
index 0000000000..8a2f05aa50
--- /dev/null
+++ b/spec/ruby/library/cgi/cookie/path_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::Cookie#path" do
+ it "returns self's path" do
+ cookie = CGI::Cookie.new("test-cookie")
+ cookie.path.should == ""
+
+ cookie = CGI::Cookie.new("name" => "test-cookie", "path" => "/some/path/")
+ cookie.path.should == "/some/path/"
+ end
+end
+
+describe "CGI::Cookie#path=" do
+ it "sets self's path" do
+ cookie = CGI::Cookie.new("test-cookie")
+ cookie.path = "/some/path/"
+ cookie.path.should == "/some/path/"
+
+ cookie.path = "/another/path/"
+ cookie.path.should == "/another/path/"
+ end
+end
diff --git a/spec/ruby/library/cgi/cookie/secure_spec.rb b/spec/ruby/library/cgi/cookie/secure_spec.rb
new file mode 100644
index 0000000000..694bc2eeed
--- /dev/null
+++ b/spec/ruby/library/cgi/cookie/secure_spec.rb
@@ -0,0 +1,70 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::Cookie#secure" do
+ before :each do
+ @cookie = CGI::Cookie.new("test-cookie")
+ end
+
+ it "returns whether self is a secure cookie or not" do
+ @cookie.secure = true
+ @cookie.secure.should be_true
+
+ @cookie.secure = false
+ @cookie.secure.should be_false
+ end
+end
+
+describe "CGI::Cookie#secure= when passed true" do
+ before :each do
+ @cookie = CGI::Cookie.new("test-cookie")
+ end
+
+ it "returns true" do
+ (@cookie.secure = true).should be_true
+ end
+
+ it "sets self to a secure cookie" do
+ @cookie.secure = true
+ @cookie.secure.should be_true
+ end
+end
+
+describe "CGI::Cookie#secure= when passed false" do
+ before :each do
+ @cookie = CGI::Cookie.new("test-cookie")
+ end
+
+ it "returns false" do
+ (@cookie.secure = false).should be_false
+ end
+
+ it "sets self to a non-secure cookie" do
+ @cookie.secure = false
+ @cookie.secure.should be_false
+ end
+end
+
+describe "CGI::Cookie#secure= when passed Object" do
+ before :each do
+ @cookie = CGI::Cookie.new("test-cookie")
+ end
+
+ it "does not change self's secure value" do
+ @cookie.secure = false
+
+ @cookie.secure = Object.new
+ @cookie.secure.should be_false
+
+ @cookie.secure = "Test"
+ @cookie.secure.should be_false
+
+ @cookie.secure = true
+
+ @cookie.secure = Object.new
+ @cookie.secure.should be_true
+
+ @cookie.secure = "Test"
+ @cookie.secure.should be_true
+ end
+end
diff --git a/spec/ruby/library/cgi/cookie/to_s_spec.rb b/spec/ruby/library/cgi/cookie/to_s_spec.rb
new file mode 100644
index 0000000000..978c2d33eb
--- /dev/null
+++ b/spec/ruby/library/cgi/cookie/to_s_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::Cookie#to_s" do
+ it "returns a String representation of self" do
+ cookie = CGI::Cookie.new("test-cookie")
+ cookie.to_s.should == "test-cookie=; path="
+
+ cookie = CGI::Cookie.new("test-cookie", "value")
+ cookie.to_s.should == "test-cookie=value; path="
+
+ cookie = CGI::Cookie.new("test-cookie", "one", "two", "three")
+ cookie.to_s.should == "test-cookie=one&two&three; path="
+
+ cookie = CGI::Cookie.new(
+ 'name' => 'test-cookie',
+ 'value' => ["one", "two", "three"],
+ 'path' => 'some/path/',
+ 'domain' => 'example.com',
+ 'expires' => Time.at(1196524602),
+ 'secure' => true)
+ cookie.to_s.should == "test-cookie=one&two&three; domain=example.com; path=some/path/; expires=Sat, 01 Dec 2007 15:56:42 GMT; secure"
+ end
+
+ it "escapes the self's values" do
+ cookie = CGI::Cookie.new("test-cookie", " !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}")
+ cookie.to_s.should == "test-cookie=+%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D; path="
+ end
+
+ ruby_version_is ""..."2.5" do
+ it "escapes tilde" do
+ cookie = CGI::Cookie.new("test-cookie", "~").to_s.should == "test-cookie=%7E; path="
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "does not escape tilde" do
+ cookie = CGI::Cookie.new("test-cookie", "~").to_s.should == "test-cookie=~; path="
+ end
+ end
+
+end
diff --git a/spec/ruby/library/cgi/cookie/value_spec.rb b/spec/ruby/library/cgi/cookie/value_spec.rb
new file mode 100644
index 0000000000..1d5da3a3ac
--- /dev/null
+++ b/spec/ruby/library/cgi/cookie/value_spec.rb
@@ -0,0 +1,76 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::Cookie#value" do
+ it "returns self's value" do
+ cookie = CGI::Cookie.new("test-cookie")
+ cookie.value.should == []
+
+ cookie = CGI::Cookie.new("test-cookie", "one")
+ cookie.value.should == ["one"]
+
+ cookie = CGI::Cookie.new("test-cookie", "one", "two", "three")
+ cookie.value.should == ["one", "two", "three"]
+
+ cookie = CGI::Cookie.new("name" => "test-cookie", "value" => ["one", "two", "three"])
+ cookie.value.should == ["one", "two", "three"]
+ end
+
+ it "is in synch with self" do
+ fail = []
+ [
+ :pop,
+ :shift,
+ [:<<, "Hello"],
+ [:push, "Hello"],
+ [:unshift, "World"],
+ [:replace, ["A", "B"]],
+ [:[]=, 1, "Set"],
+ [:delete, "first"],
+ [:delete_at, 0],
+ ].each do |method, *args|
+ cookie1 = CGI::Cookie.new("test-cookie", "first", "second")
+ cookie2 = CGI::Cookie.new("test-cookie", "first", "second")
+ cookie1.send(method, *args)
+ cookie2.value.send(method, *args)
+ fail << method unless cookie1.value == cookie2.value
+ end
+ fail.should be_empty
+ end
+end
+
+describe "CGI::Cookie#value=" do
+ before :each do
+ @cookie = CGI::Cookie.new("test-cookie")
+ end
+
+ it "sets self's value" do
+ @cookie.value = ["one"]
+ @cookie.value.should == ["one"]
+
+ @cookie.value = ["one", "two", "three"]
+ @cookie.value.should == ["one", "two", "three"]
+ end
+
+ it "automatically converts the passed Object to an Array using #Array" do
+ @cookie.value = "test"
+ @cookie.value.should == ["test"]
+
+ obj = mock("to_a")
+ obj.should_receive(:to_a).and_return(["1", "2"])
+ @cookie.value = obj
+ @cookie.value.should == ["1", "2"]
+
+ obj = mock("to_ary")
+ obj.should_receive(:to_ary).and_return(["1", "2"])
+ @cookie.value = obj
+ @cookie.value.should == ["1", "2"]
+ end
+
+ it "does keep self and the values in sync" do
+ @cookie.value = ["one", "two", "three"]
+ @cookie[0].should == "one"
+ @cookie[1].should == "two"
+ @cookie[2].should == "three"
+ end
+end
diff --git a/spec/ruby/library/cgi/escapeElement_spec.rb b/spec/ruby/library/cgi/escapeElement_spec.rb
new file mode 100644
index 0000000000..148926c453
--- /dev/null
+++ b/spec/ruby/library/cgi/escapeElement_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require 'cgi'
+
+describe "CGI.escapeElement when passed String, elements, ..." do
+ it "escapes only the tags of the passed elements in the passed String" do
+ res = CGI.escapeElement('<BR><A HREF="url"></A>', "A", "IMG")
+ res.should == "<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt;"
+
+ res = CGI.escapeElement('<BR><A HREF="url"></A>', ["A", "IMG"])
+ res.should == "<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt;"
+ end
+
+ it "is case-insensitive" do
+ res = CGI.escapeElement('<BR><A HREF="url"></A>', "a", "img")
+ res.should == '<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt;'
+
+ res = CGI.escapeElement('<br><a href="url"></a>', "A", "IMG")
+ res.should == '<br>&lt;a href=&quot;url&quot;&gt;&lt;/a&gt;'
+ end
+end
diff --git a/spec/ruby/library/cgi/escapeHTML_spec.rb b/spec/ruby/library/cgi/escapeHTML_spec.rb
new file mode 100644
index 0000000000..dcbfebe720
--- /dev/null
+++ b/spec/ruby/library/cgi/escapeHTML_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require 'cgi'
+
+describe "CGI.escapeHTML" do
+ it "escapes special HTML characters (&\"<>') in the passed argument" do
+ CGI.escapeHTML(%[& < > " ']).should == '&amp; &lt; &gt; &quot; &#39;'
+ end
+
+ it "does not escape any other characters" do
+ chars = " !\#$%()*+,-./0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
+ CGI.escapeHTML(chars).should == chars
+ end
+end
diff --git a/spec/ruby/library/cgi/escape_spec.rb b/spec/ruby/library/cgi/escape_spec.rb
new file mode 100644
index 0000000000..351bfe1070
--- /dev/null
+++ b/spec/ruby/library/cgi/escape_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require 'cgi'
+
+describe "CGI.escape" do
+ it "url-encodes the passed argument" do
+ input = " !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}"
+ expected = "+%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D"
+ CGI.escape(input).should == expected
+
+ input = "http://ja.wikipedia.org/wiki/\343\203\255\343\203\240\343\202\271\343\202\253\343\203\273\343\203\221\343\203\255\343\203\273\343\202\246\343\203\253\343\203\273\343\203\251\343\203\224\343\203\245\343\202\277"
+ expected = 'http%3A%2F%2Fja.wikipedia.org%2Fwiki%2F%E3%83%AD%E3%83%A0%E3%82%B9%E3%82%AB%E3%83%BB%E3%83%91%E3%83%AD%E3%83%BB%E3%82%A6%E3%83%AB%E3%83%BB%E3%83%A9%E3%83%94%E3%83%A5%E3%82%BF'
+ CGI.escape(input).should == expected
+ end
+
+ ruby_version_is ""..."2.5" do
+ it "escapes tilde" do
+ CGI.escape("~").should == "%7E"
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "does not escape tilde" do
+ CGI.escape("~").should == "~"
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/a_spec.rb b/spec/ruby/library/cgi/htmlextension/a_spec.rb
new file mode 100644
index 0000000000..05b4c2d14c
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/a_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#a" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed a String" do
+ it "returns an 'a'-element, using the passed String as the 'href'-attribute" do
+ output = @html.a("http://www.example.com")
+ output.should equal_element("A", "HREF" => "http://www.example.com")
+ end
+
+ it "includes the passed block's return value when passed a block" do
+ output = @html.a("http://www.example.com") { "Example" }
+ output.should equal_element("A", { "HREF" => "http://www.example.com" }, "Example")
+ end
+ end
+
+ describe "when passed a Hash" do
+ it "returns an 'a'-element, using the passed Hash for attributes" do
+ attributes = {"HREF" => "http://www.example.com", "TARGET" => "_top"}
+ @html.a(attributes).should equal_element("A", attributes)
+ end
+
+ it "includes the passed block's return value when passed a block" do
+ attributes = {"HREF" => "http://www.example.com", "TARGET" => "_top"}
+ @html.a(attributes) { "Example" }.should equal_element("A", attributes, "Example")
+ end
+ end
+
+ describe "when each HTML generation" do
+ it "returns the doctype declaration for HTML3" do
+ CGISpecs.cgi_new("html3").a.should == %(<A HREF=""></A>)
+ CGISpecs.cgi_new("html3").a { "link text" }.should == %(<A HREF="">link text</A>)
+ end
+
+ it "returns the doctype declaration for HTML4" do
+ CGISpecs.cgi_new("html4").a.should == %(<A HREF=""></A>)
+ CGISpecs.cgi_new("html4").a { "link text" }.should == %(<A HREF="">link text</A>)
+ end
+ it "returns the doctype declaration for the Transitional version of HTML4" do
+ CGISpecs.cgi_new("html4Tr").a.should == %(<A HREF=""></A>)
+ CGISpecs.cgi_new("html4Tr").a { "link text" }.should == %(<A HREF="">link text</A>)
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/base_spec.rb b/spec/ruby/library/cgi/htmlextension/base_spec.rb
new file mode 100644
index 0000000000..877ac321cd
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/base_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#base" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when bassed a String" do
+ it "returns a 'base'-element, using the passed String as the 'href'-attribute" do
+ output = @html.base("http://www.example.com")
+ output.should equal_element("BASE", {"HREF" => "http://www.example.com"}, nil, not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.base("http://www.example.com") { "Example" }
+ output.should equal_element("BASE", {"HREF" => "http://www.example.com"}, nil, not_closed: true)
+ end
+ end
+
+ describe "when passed a Hash" do
+ it "returns a 'base'-element, using the passed Hash for attributes" do
+ output = @html.base("HREF" => "http://www.example.com", "ID" => "test")
+ output.should equal_element("BASE", {"HREF" => "http://www.example.com", "ID" => "test"}, nil, not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.base("HREF" => "http://www.example.com", "ID" => "test") { "Example" }
+ output.should equal_element("BASE", {"HREF" => "http://www.example.com", "ID" => "test"}, nil, not_closed: true)
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/blockquote_spec.rb b/spec/ruby/library/cgi/htmlextension/blockquote_spec.rb
new file mode 100644
index 0000000000..a7b833b1c5
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/blockquote_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#blockquote" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed a String" do
+ it "returns a 'blockquote'-element, using the passed String for the 'cite'-attribute" do
+ output = @html.blockquote("http://www.example.com/quotes/foo.html")
+ output.should equal_element("BLOCKQUOTE", "CITE" => "http://www.example.com/quotes/foo.html")
+ end
+
+ it "includes the passed block's return value when passed a block" do
+ output = @html.blockquote("http://www.example.com/quotes/foo.html") { "Foo!" }
+ output.should equal_element("BLOCKQUOTE", { "CITE" => "http://www.example.com/quotes/foo.html" }, "Foo!")
+ end
+ end
+
+ describe "when passed a Hash" do
+ it "returns a 'blockquote'-element, using the passed Hash for attributes" do
+ output = @html.blockquote("CITE" => "http://www.example.com/quotes/foo.html", "ID" => "test")
+ output.should equal_element("BLOCKQUOTE", "CITE" => "http://www.example.com/quotes/foo.html", "ID" => "test")
+ end
+
+ it "includes the passed block's return value when passed a block" do
+ output = @html.blockquote("CITE" => "http://www.example.com/quotes/foo.html", "ID" => "test") { "Foo!" }
+ output.should equal_element("BLOCKQUOTE", {"CITE" => "http://www.example.com/quotes/foo.html", "ID" => "test"}, "Foo!")
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/br_spec.rb b/spec/ruby/library/cgi/htmlextension/br_spec.rb
new file mode 100644
index 0000000000..dfca121884
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/br_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#br" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when each HTML generation" do
+ it "returns the doctype declaration for HTML3" do
+ CGISpecs.cgi_new("html3").br.should == "<BR>"
+ end
+
+ it "returns the doctype declaration for HTML4" do
+ CGISpecs.cgi_new("html4").br.should == "<BR>"
+ end
+ it "returns the doctype declaration for the Transitional version of HTML4" do
+ CGISpecs.cgi_new("html4Tr").br.should == "<BR>"
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/caption_spec.rb b/spec/ruby/library/cgi/htmlextension/caption_spec.rb
new file mode 100644
index 0000000000..16615028b8
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/caption_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#caption" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed a String" do
+ it "returns a 'caption'-element, using the passed String for the 'align'-attribute" do
+ output = @html.caption("left")
+ output.should equal_element("CAPTION", "ALIGN" => "left")
+ end
+
+ it "includes the passed block's return value when passed a block" do
+ output = @html.caption("left") { "Capital Cities" }
+ output.should equal_element("CAPTION", {"ALIGN" => "left"}, "Capital Cities")
+ end
+ end
+
+ describe "when passed a Hash" do
+ it "returns a 'caption'-element, using the passed Hash for attributes" do
+ output = @html.caption("ALIGN" => "left", "ID" => "test")
+ output.should equal_element("CAPTION", "ALIGN" => "left", "ID" => "test")
+ end
+
+ it "includes the passed block's return value when passed a block" do
+ output = @html.caption("ALIGN" => "left", "ID" => "test") { "Capital Cities" }
+ output.should equal_element("CAPTION", {"ALIGN" => "left", "ID" => "test"}, "Capital Cities")
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/checkbox_group_spec.rb b/spec/ruby/library/cgi/htmlextension/checkbox_group_spec.rb
new file mode 100644
index 0000000000..64f852cc52
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/checkbox_group_spec.rb
@@ -0,0 +1,76 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#checkbox_group" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed name, values ..." do
+ it "returns a sequence of 'checkbox'-elements with the passed name and the passed values" do
+ output = CGISpecs.split(@html.checkbox_group("test", "foo", "bar", "baz"))
+ output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "foo"}, "foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "bar"}, "bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "baz"}, "baz", not_closed: true)
+ end
+
+ it "allows passing a value inside an Array" do
+ output = CGISpecs.split(@html.checkbox_group("test", ["foo"], "bar", ["baz"]))
+ output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "foo"}, "foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "bar"}, "bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "baz"}, "baz", not_closed: true)
+ end
+
+ it "allows passing a value as an Array containing the value and the checked state or a label" do
+ output = CGISpecs.split(@html.checkbox_group("test", ["foo"], ["bar", true], ["baz", "label for baz"]))
+ output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "foo"}, "foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "checkbox", "VALUE" => "bar"}, "bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "baz"}, "label for baz", not_closed: true)
+ end
+
+ it "allows passing a value as an Array containing the value, a label and the checked state" do
+ output = CGISpecs.split(@html.checkbox_group("test", ["foo", "label for foo", true], ["bar", "label for bar", false], ["baz", "label for baz", true]))
+ output[0].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "checkbox", "VALUE" => "foo"}, "label for foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "bar"}, "label for bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "checkbox", "VALUE" => "baz"}, "label for baz", not_closed: true)
+ end
+
+ it "returns an empty String when passed no values" do
+ @html.checkbox_group("test").should == ""
+ end
+
+ it "ignores a passed block" do
+ output = CGISpecs.split(@html.checkbox_group("test", "foo", "bar", "baz") { "test" })
+ output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "foo"}, "foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "bar"}, "bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "baz"}, "baz", not_closed: true)
+ end
+ end
+
+ describe "when passed Hash" do
+ it "uses the passed Hash to generate the checkbox sequence" do
+ output = CGISpecs.split(@html.checkbox_group("NAME" => "name", "VALUES" => ["foo", "bar", "baz"]))
+ output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "foo"}, "foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "bar"}, "bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "baz"}, "baz", not_closed: true)
+
+ output = CGISpecs.split(@html.checkbox_group("NAME" => "name", "VALUES" => [["foo"], ["bar", true], "baz"]))
+ output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "foo"}, "foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "name", "TYPE" => "checkbox", "VALUE" => "bar"}, "bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "baz"}, "baz", not_closed: true)
+
+ output = CGISpecs.split(@html.checkbox_group("NAME" => "name", "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"]))
+ output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "1"}, "Foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "name", "TYPE" => "checkbox", "VALUE" => "2"}, "Bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "Baz"}, "Baz", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = CGISpecs.split(@html.checkbox_group("NAME" => "name", "VALUES" => ["foo", "bar", "baz"]) { "test" })
+ output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "foo"}, "foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "bar"}, "bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "baz"}, "baz", not_closed: true)
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/checkbox_spec.rb b/spec/ruby/library/cgi/htmlextension/checkbox_spec.rb
new file mode 100644
index 0000000000..af76fa1da9
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/checkbox_spec.rb
@@ -0,0 +1,77 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#checkbox" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed no arguments" do
+ it "returns a checkbox-'input'-element without a name" do
+ output = @html.checkbox
+ output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "checkbox"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.checkbox { "test" }
+ output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "checkbox"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name" do
+ it "returns a checkbox-'input'-element with the passed name" do
+ output = @html.checkbox("test")
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.checkbox("test") { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox"}, "", not_closed: true)
+ end
+ end
+
+ describe "CGI::HtmlExtension#checkbox when passed name, value" do
+ it "returns a checkbox-'input'-element with the passed name and value" do
+ output = @html.checkbox("test", "test-value")
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "test-value"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.checkbox("test", "test-value") { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "test-value"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name, value, checked" do
+ it "returns a checked checkbox-'input'-element with the passed name and value when checked is true" do
+ output = @html.checkbox("test", "test-value", true)
+ output.should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "checkbox", "VALUE" => "test-value"}, "", not_closed: true)
+
+ output = @html.checkbox("test", "test-value", false)
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "test-value"}, "", not_closed: true)
+
+ output = @html.checkbox("test", "test-value", nil)
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "test-value"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.checkbox("test", "test-value", nil) { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "test-value"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed Hash" do
+ it "returns a checkbox-'input'-element using the passed Hash for attributes" do
+ attributes = {"NAME" => "test", "VALUE" => "test-value", "CHECKED" => true}
+ output = @html.checkbox(attributes)
+ output.should equal_element("INPUT", attributes, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ attributes = {"NAME" => "test", "VALUE" => "test-value", "CHECKED" => true}
+ output = @html.checkbox(attributes) { "test" }
+ output.should equal_element("INPUT", attributes, "", not_closed: true)
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/doctype_spec.rb b/spec/ruby/library/cgi/htmlextension/doctype_spec.rb
new file mode 100644
index 0000000000..9a28a8883b
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/doctype_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#doctype" do
+ describe "when each HTML generation" do
+ it "returns the doctype declaration for HTML3" do
+ expect = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">'
+ CGISpecs.cgi_new("html3").doctype.should == expect
+ end
+
+ it "returns the doctype declaration for HTML4" do
+ expect = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">'
+ CGISpecs.cgi_new("html4").doctype.should == expect
+ end
+
+ it "returns the doctype declaration for the Frameset version of HTML4" do
+ expect = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'
+ CGISpecs.cgi_new("html4Fr").doctype.should == expect
+ end
+
+ it "returns the doctype declaration for the Transitional version of HTML4" do
+ expect = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
+ CGISpecs.cgi_new("html4Tr").doctype.should == expect
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/file_field_spec.rb b/spec/ruby/library/cgi/htmlextension/file_field_spec.rb
new file mode 100644
index 0000000000..2a0632fd58
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/file_field_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#file_field" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed no arguments" do
+ it "returns a file-'input'-element without a name and a size of 20" do
+ output = @html.file_field
+ output.should equal_element("INPUT", {"SIZE" => 20, "NAME" => "", "TYPE" => "file"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.file_field { "test" }
+ output.should equal_element("INPUT", {"SIZE" => 20, "NAME" => "", "TYPE" => "file"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name" do
+ it "returns a checkbox-'input'-element with the passed name" do
+ output = @html.file_field("Example")
+ output.should equal_element("INPUT", {"SIZE" => 20, "NAME" => "Example", "TYPE" => "file"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.file_field("Example") { "test" }
+ output.should equal_element("INPUT", {"SIZE" => 20, "NAME" => "Example", "TYPE" => "file"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name, size" do
+ it "returns a checkbox-'input'-element with the passed name and size" do
+ output = @html.file_field("Example", 40)
+ output.should equal_element("INPUT", {"SIZE" => 40, "NAME" => "Example", "TYPE" => "file"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.file_field("Example", 40) { "test" }
+ output.should equal_element("INPUT", {"SIZE" => 40, "NAME" => "Example", "TYPE" => "file"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name, size, maxlength" do
+ it "returns a checkbox-'input'-element with the passed name, size and maxlength" do
+ output = @html.file_field("Example", 40, 100)
+ output.should equal_element("INPUT", {"SIZE" => 40, "NAME" => "Example", "TYPE" => "file", "MAXLENGTH" => 100}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.file_field("Example", 40, 100) { "test" }
+ output.should equal_element("INPUT", {"SIZE" => 40, "NAME" => "Example", "TYPE" => "file", "MAXLENGTH" => 100}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed a Hash" do
+ it "returns a file-'input'-element using the passed Hash for attributes" do
+ output = @html.file_field("NAME" => "test", "SIZE" => 40)
+ output.should equal_element("INPUT", {"NAME" => "test", "SIZE" => 40}, "", not_closed: true)
+
+ output = @html.file_field("NAME" => "test", "MAXLENGTH" => 100)
+ output.should equal_element("INPUT", {"NAME" => "test", "MAXLENGTH" => 100}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.file_field("NAME" => "test", "SIZE" => 40) { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "SIZE" => 40}, "", not_closed: true)
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/fixtures/common.rb b/spec/ruby/library/cgi/htmlextension/fixtures/common.rb
new file mode 100644
index 0000000000..4f951f6389
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/fixtures/common.rb
@@ -0,0 +1,15 @@
+module CGISpecs
+ def self.cgi_new(html = "html4")
+ old_request_method = ENV['REQUEST_METHOD']
+ ENV['REQUEST_METHOD'] = "GET"
+ begin
+ CGI.new(tag_maker: html)
+ ensure
+ ENV['REQUEST_METHOD'] = old_request_method
+ end
+ end
+
+ def self.split(string)
+ string.split("<").reject { |x| x.empty? }.map { |x| "<#{x}" }
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/form_spec.rb b/spec/ruby/library/cgi/htmlextension/form_spec.rb
new file mode 100644
index 0000000000..8c0ac97735
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/form_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#form" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ @html.stub!(:script_name).and_return("/path/to/some/script")
+ end
+
+ describe "when passed no arguments" do
+ it "returns a 'form'-element" do
+ output = @html.form
+ output.should equal_element("FORM", {"ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "post", "ACTION" => "/path/to/some/script"}, "")
+ end
+
+ it "includes the return value of the passed block when passed a block" do
+ output = @html.form { "test" }
+ output.should equal_element("FORM", {"ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "post", "ACTION" => "/path/to/some/script"}, "test")
+ end
+ end
+
+ describe "when passed method" do
+ it "returns a 'form'-element with the passed method" do
+ output = @html.form("get")
+ output.should equal_element("FORM", {"ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get", "ACTION" => "/path/to/some/script"}, "")
+ end
+
+ it "includes the return value of the passed block when passed a block" do
+ output = @html.form("get") { "test" }
+ output.should equal_element("FORM", {"ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get", "ACTION" => "/path/to/some/script"}, "test")
+ end
+ end
+
+ describe "when passed method, action" do
+ it "returns a 'form'-element with the passed method and the passed action" do
+ output = @html.form("get", "/some/other/script")
+ output.should equal_element("FORM", {"ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get", "ACTION" => "/some/other/script"}, "")
+ end
+
+ it "includes the return value of the passed block when passed a block" do
+ output = @html.form("get", "/some/other/script") { "test" }
+ output.should equal_element("FORM", {"ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get", "ACTION" => "/some/other/script"}, "test")
+ end
+ end
+
+ describe "when passed method, action, enctype" do
+ it "returns a 'form'-element with the passed method, action and enctype" do
+ output = @html.form("get", "/some/other/script", "multipart/form-data")
+ output.should equal_element("FORM", {"ENCTYPE" => "multipart/form-data", "METHOD" => "get", "ACTION" => "/some/other/script"}, "")
+ end
+
+ it "includes the return value of the passed block when passed a block" do
+ output = @html.form("get", "/some/other/script", "multipart/form-data") { "test" }
+ output.should equal_element("FORM", {"ENCTYPE" => "multipart/form-data", "METHOD" => "get", "ACTION" => "/some/other/script"}, "test")
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/frame_spec.rb b/spec/ruby/library/cgi/htmlextension/frame_spec.rb
new file mode 100644
index 0000000000..2ddd4e1ef0
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/frame_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require_relative 'fixtures/common'
+require 'cgi'
+
+describe "CGI::HtmlExtension#frame" do
+ before :each do
+ @html = CGISpecs.cgi_new("html4Fr")
+ end
+
+ it "initializes the HTML Generation methods for the Frameset version of HTML4" do
+ @html.frameset.should == "<FRAMESET></FRAMESET>"
+ @html.frameset { "link text" }.should == "<FRAMESET>link text</FRAMESET>"
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/frameset_spec.rb b/spec/ruby/library/cgi/htmlextension/frameset_spec.rb
new file mode 100644
index 0000000000..baeb446593
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/frameset_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require_relative 'fixtures/common'
+require 'cgi'
+
+describe "CGI::HtmlExtension#frameset" do
+ before :each do
+ @html = CGISpecs.cgi_new("html4Fr")
+ end
+
+ it "initializes the HTML Generation methods for the Frameset version of HTML4" do
+ @html.frameset.should == "<FRAMESET></FRAMESET>"
+ @html.frameset { "link text" }.should == "<FRAMESET>link text</FRAMESET>"
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/hidden_spec.rb b/spec/ruby/library/cgi/htmlextension/hidden_spec.rb
new file mode 100644
index 0000000000..52ebd8c261
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/hidden_spec.rb
@@ -0,0 +1,59 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#hidden" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed no arguments" do
+ it "returns an hidden-'input'-element without a name" do
+ output = @html.hidden
+ output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "hidden"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.hidden { "test" }
+ output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "hidden"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name" do
+ it "returns an hidden-'input'-element with the passed name" do
+ output = @html.hidden("test")
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "hidden"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.hidden("test") { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "hidden"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name, value" do
+ it "returns an hidden-'input'-element with the passed name and value" do
+ output = @html.hidden("test", "some value")
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "hidden", "VALUE" => "some value"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.hidden("test", "some value") { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "hidden", "VALUE" => "some value"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed Hash" do
+ it "returns a checkbox-'input'-element using the passed Hash for attributes" do
+ attributes = { "NAME" => "test", "VALUE" => "some value" }
+ output = @html.hidden("test", "some value")
+ output.should equal_element("INPUT", attributes, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ attributes = { "NAME" => "test", "VALUE" => "some value" }
+ output = @html.hidden("test", "some value") { "test" }
+ output.should equal_element("INPUT", attributes, "", not_closed: true)
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/html_spec.rb b/spec/ruby/library/cgi/htmlextension/html_spec.rb
new file mode 100644
index 0000000000..5d89c82086
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/html_spec.rb
@@ -0,0 +1,66 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#html" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ @html.stub!(:doctype).and_return("<!DOCTYPE SUPA-FUNKAY-RUBYSPEC-DOCTYPE>")
+ end
+
+ describe "when passed no arguments" do
+ it "returns a self's doctype and an 'html'-element" do
+ expected = '<!DOCTYPE SUPA-FUNKAY-RUBYSPEC-DOCTYPE><HTML>'
+ @html.html.should == expected
+ end
+
+ it "includes the passed block when passed a block" do
+ expected = '<!DOCTYPE SUPA-FUNKAY-RUBYSPEC-DOCTYPE><HTML>test</HTML>'
+ @html.html { "test" }.should == expected
+ end
+ end
+
+ describe "when passed 'PRETTY'" do
+ it "returns pretty output when the passed String is 'PRETTY" do
+ expected = "<!DOCTYPE SUPA-FUNKAY-RUBYSPEC-DOCTYPE>\n<HTML>\n"
+ @html.html("PRETTY").should == expected
+ end
+
+ it "includes the passed block when passed a block" do
+ expected = "<!DOCTYPE SUPA-FUNKAY-RUBYSPEC-DOCTYPE>\n<HTML>\n test\n</HTML>\n"
+ @html.html("PRETTY") { "test" }.should == expected
+ end
+ end
+
+ describe "when passed a Hash" do
+ it "returns an 'html'-element using the passed Hash for attributes" do
+ expected = '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"><HTML BLA="TEST">'
+ @html.html("DOCTYPE" => '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">', "BLA" => "TEST").should == expected
+ end
+
+ it "omits the doctype when the Hash contains a 'DOCTYPE' entry that's false or nil" do
+ @html.html("DOCTYPE" => false).should == "<HTML>"
+ @html.html("DOCTYPE" => nil).should == "<HTML>"
+ end
+ end
+
+ describe "when each HTML generation" do
+ it "returns the doctype declaration for HTML3" do
+ expect = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">'
+ CGISpecs.cgi_new("html3").html.should == expect + "<HTML>"
+ CGISpecs.cgi_new("html3").html { "html body" }.should == expect + "<HTML>html body</HTML>"
+ end
+
+ it "returns the doctype declaration for HTML4" do
+ expect = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">'
+ CGISpecs.cgi_new("html4").html.should == expect + "<HTML>"
+ CGISpecs.cgi_new("html4").html { "html body" }.should == expect + "<HTML>html body</HTML>"
+ end
+
+ it "returns the doctype declaration for the Transitional version of HTML4" do
+ expect = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
+ CGISpecs.cgi_new("html4Tr").html.should == expect + "<HTML>"
+ CGISpecs.cgi_new("html4Tr").html { "html body" }.should == expect + "<HTML>html body</HTML>"
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/image_button_spec.rb b/spec/ruby/library/cgi/htmlextension/image_button_spec.rb
new file mode 100644
index 0000000000..d14bec9ca3
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/image_button_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#image_button" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed no arguments" do
+ it "returns an image-'input'-element without a source image" do
+ output = @html.image_button
+ output.should equal_element("INPUT", {"SRC" => "", "TYPE" => "image"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.image_button { "test" }
+ output.should equal_element("INPUT", {"SRC" => "", "TYPE" => "image"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed src" do
+ it "returns an image-'input'-element with the passed src" do
+ output = @html.image_button("/path/to/image.png")
+ output.should equal_element("INPUT", {"SRC" => "/path/to/image.png", "TYPE" => "image"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.image_button("/path/to/image.png") { "test" }
+ output.should equal_element("INPUT", {"SRC" => "/path/to/image.png", "TYPE" => "image"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed src, name" do
+ it "returns an image-'input'-element with the passed src and name" do
+ output = @html.image_button("/path/to/image.png", "test")
+ output.should equal_element("INPUT", {"SRC" => "/path/to/image.png", "TYPE" => "image", "NAME" => "test"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.image_button("/path/to/image.png", "test") { "test" }
+ output.should equal_element("INPUT", {"SRC" => "/path/to/image.png", "TYPE" => "image", "NAME" => "test"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed src, name, alt" do
+ it "returns an image-'input'-element with the passed src, name and alt" do
+ output = @html.image_button("/path/to/image.png", "test", "alternative")
+ output.should equal_element("INPUT", {"SRC" => "/path/to/image.png", "TYPE" => "image", "NAME" => "test", "ALT" => "alternative"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.image_button("/path/to/image.png", "test", "alternative") { "test" }
+ output.should equal_element("INPUT", {"SRC" => "/path/to/image.png", "TYPE" => "image", "NAME" => "test", "ALT" => "alternative"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed Hash" do
+ it "returns a image-'input'-element using the passed Hash for attributes" do
+ output = @html.image_button("NAME" => "test", "VALUE" => "test-value")
+ output.should equal_element("INPUT", {"SRC" => "", "TYPE" => "image", "NAME" => "test", "VALUE" => "test-value"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.image_button("NAME" => "test", "VALUE" => "test-value") { "test" }
+ output.should equal_element("INPUT", {"SRC" => "", "TYPE" => "image", "NAME" => "test", "VALUE" => "test-value"}, "", not_closed: true)
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/img_spec.rb b/spec/ruby/library/cgi/htmlextension/img_spec.rb
new file mode 100644
index 0000000000..994ae7fedf
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/img_spec.rb
@@ -0,0 +1,83 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#img" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed no arguments" do
+ it "returns an 'img'-element without an src-url or alt-text" do
+ output = @html.img
+ output.should equal_element("IMG", { "SRC" => "", "ALT" => "" }, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.img { "test" }
+ output.should equal_element("IMG", { "SRC" => "", "ALT" => "" }, "", not_closed: true)
+ end
+ end
+
+ describe "when passed src" do
+ it "returns an 'img'-element with the passed src-url" do
+ output = @html.img("/path/to/some/image.png")
+ output.should equal_element("IMG", { "SRC" => "/path/to/some/image.png", "ALT" => "" }, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.img("/path/to/some/image.png")
+ output.should equal_element("IMG", { "SRC" => "/path/to/some/image.png", "ALT" => "" }, "", not_closed: true)
+ end
+ end
+
+ describe "when passed src, alt" do
+ it "returns an 'img'-element with the passed src-url and the passed alt-text" do
+ output = @html.img("/path/to/some/image.png", "Alternative")
+ output.should equal_element("IMG", { "SRC" => "/path/to/some/image.png", "ALT" => "Alternative" }, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.img("/path/to/some/image.png", "Alternative") { "test" }
+ output.should equal_element("IMG", { "SRC" => "/path/to/some/image.png", "ALT" => "Alternative" }, "", not_closed: true)
+ end
+ end
+
+ describe "when passed src, alt, width" do
+ it "returns an 'img'-element with the passed src-url, the passed alt-text and the passed width" do
+ output = @html.img("/path/to/some/image.png", "Alternative", 40)
+ output.should equal_element("IMG", { "SRC" => "/path/to/some/image.png", "ALT" => "Alternative", "WIDTH" => "40" }, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.img("/path/to/some/image.png", "Alternative", 40) { "test" }
+ output.should equal_element("IMG", { "SRC" => "/path/to/some/image.png", "ALT" => "Alternative", "WIDTH" => "40" }, "", not_closed: true)
+ end
+ end
+
+ describe "when passed src, alt, width, height" do
+ it "returns an 'img'-element with the passed src-url, the passed alt-text, the passed width and the passed height" do
+ output = @html.img("/path/to/some/image.png", "Alternative", 40, 60)
+ output.should equal_element("IMG", { "SRC" => "/path/to/some/image.png", "ALT" => "Alternative", "WIDTH" => "40", "HEIGHT" => "60" }, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.img { "test" }
+ output.should equal_element("IMG", { "SRC" => "", "ALT" => "" }, "", not_closed: true)
+ end
+ end
+
+ describe "when passed Hash" do
+ it "returns an 'img'-element with the passed Hash as attributes" do
+ attributes = { "SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50 }
+ output = @html.img(attributes)
+ output.should equal_element("IMG", attributes, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ attributes = { "SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50 }
+ output = @html.img(attributes) { "test" }
+ output.should equal_element("IMG", attributes, "", not_closed: true)
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/multipart_form_spec.rb b/spec/ruby/library/cgi/htmlextension/multipart_form_spec.rb
new file mode 100644
index 0000000000..0bf2042a33
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/multipart_form_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#multipart_form" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ @html.stub!(:script_name).and_return("/path/to/some/script.rb")
+ end
+
+ describe "when passed no arguments" do
+ it "returns a 'form'-element with it's enctype set to multipart" do
+ output = @html.multipart_form
+ output.should equal_element("FORM", { "ENCTYPE" => "multipart/form-data", "METHOD" => "post" }, "")
+ end
+
+ it "includes the return value of the passed block when passed a block" do
+ output = @html.multipart_form { "test" }
+ output.should equal_element("FORM", { "ENCTYPE" => "multipart/form-data", "METHOD" => "post" }, "test")
+ end
+ end
+
+ describe "when passed action" do
+ it "returns a 'form'-element with the passed action" do
+ output = @html.multipart_form("/some/other/script.rb")
+ output.should equal_element("FORM", { "ENCTYPE" => "multipart/form-data", "METHOD" => "post", "ACTION" => "/some/other/script.rb" }, "")
+ end
+
+ it "includes the return value of the passed block when passed a block" do
+ output = @html.multipart_form("/some/other/script.rb") { "test" }
+ output.should equal_element("FORM", { "ENCTYPE" => "multipart/form-data", "METHOD" => "post", "ACTION" => "/some/other/script.rb" }, "test")
+ end
+ end
+
+ describe "when passed action, enctype" do
+ it "returns a 'form'-element with the passed action and enctype" do
+ output = @html.multipart_form("/some/other/script.rb", "application/x-www-form-urlencoded")
+ output.should equal_element("FORM", { "ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "post", "ACTION" => "/some/other/script.rb" }, "")
+ end
+
+ it "includes the return value of the passed block when passed a block" do
+ output = @html.multipart_form("/some/other/script.rb", "application/x-www-form-urlencoded") { "test" }
+ output.should equal_element("FORM", { "ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "post", "ACTION" => "/some/other/script.rb" }, "test")
+ end
+ end
+
+ describe "when passed Hash" do
+ it "returns a 'form'-element with the passed Hash as attributes" do
+ output = @html.multipart_form("ID" => "test")
+ output.should equal_element("FORM", { "ENCTYPE" => "multipart/form-data", "METHOD" => "post", "ID" => "test" }, "")
+
+ output = @html.multipart_form("ID" => "test", "ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get")
+ output.should equal_element("FORM", { "ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get", "ID" => "test" }, "")
+ end
+
+ it "includes the return value of the passed block when passed a block" do
+ output = @html.multipart_form("ID" => "test") { "test" }
+ output.should equal_element("FORM", { "ENCTYPE" => "multipart/form-data", "METHOD" => "post", "ID" => "test" }, "test")
+
+ output = @html.multipart_form("ID" => "test", "ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get") { "test" }
+ output.should equal_element("FORM", { "ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get", "ID" => "test" }, "test")
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/password_field_spec.rb b/spec/ruby/library/cgi/htmlextension/password_field_spec.rb
new file mode 100644
index 0000000000..683bc428ba
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/password_field_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#password_field" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed no arguments" do
+ it "returns an password-'input'-element without a name" do
+ output = @html.password_field
+ output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "password", "SIZE" => "40"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.password_field { "test" }
+ output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "password", "SIZE" => "40"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name" do
+ it "returns an password-'input'-element with the passed name" do
+ output = @html.password_field("test")
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "SIZE" => "40"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.password_field("test") { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "SIZE" => "40"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name, value" do
+ it "returns an password-'input'-element with the passed name and value" do
+ output = @html.password_field("test", "some value")
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "VALUE" => "some value", "SIZE" => "40"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.password_field("test", "some value") { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "VALUE" => "some value", "SIZE" => "40"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name, value, size" do
+ it "returns an password-'input'-element with the passed name, value and size" do
+ output = @html.password_field("test", "some value", 60)
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "VALUE" => "some value", "SIZE" => "60"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.password_field("test", "some value", 60) { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "VALUE" => "some value", "SIZE" => "60"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name, value, size, maxlength" do
+ it "returns an password-'input'-element with the passed name, value, size and maxlength" do
+ output = @html.password_field("test", "some value", 60, 12)
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "VALUE" => "some value", "SIZE" => "60", "MAXLENGTH" => 12}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.password_field("test", "some value", 60, 12) { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "VALUE" => "some value", "SIZE" => "60", "MAXLENGTH" => 12}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed Hash" do
+ it "returns a checkbox-'input'-element using the passed Hash for attributes" do
+ output = @html.password_field("NAME" => "test", "VALUE" => "some value")
+ output.should equal_element("INPUT", { "NAME" => "test", "VALUE" => "some value", "TYPE" => "password" }, "", not_closed: true)
+
+ output = @html.password_field("TYPE" => "hidden")
+ output.should equal_element("INPUT", {"TYPE" => "password"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.password_field("NAME" => "test", "VALUE" => "some value") { "test" }
+ output.should equal_element("INPUT", { "NAME" => "test", "VALUE" => "some value", "TYPE" => "password" }, "", not_closed: true)
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/popup_menu_spec.rb b/spec/ruby/library/cgi/htmlextension/popup_menu_spec.rb
new file mode 100644
index 0000000000..3462be09b0
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/popup_menu_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+require_relative 'shared/popup_menu'
+
+describe "CGI::HtmlExtension#popup_menu" do
+ it_behaves_like :cgi_htmlextension_popup_menu, :popup_menu
+end
diff --git a/spec/ruby/library/cgi/htmlextension/radio_button_spec.rb b/spec/ruby/library/cgi/htmlextension/radio_button_spec.rb
new file mode 100644
index 0000000000..3dc3c879b5
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/radio_button_spec.rb
@@ -0,0 +1,77 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#radio_button" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed no arguments" do
+ it "returns a radio-'input'-element without a name" do
+ output = @html.radio_button
+ output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "radio"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.radio_button { "test" }
+ output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "radio"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name" do
+ it "returns a radio-'input'-element with the passed name" do
+ output = @html.radio_button("test")
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.radio_button("test") { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio"}, "", not_closed: true)
+ end
+ end
+
+ describe "CGI::HtmlExtension#checkbox when passed name, value" do
+ it "returns a radio-'input'-element with the passed name and value" do
+ output = @html.radio_button("test", "test-value")
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "test-value"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.radio_button("test", "test-value") { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "test-value"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name, value, checked" do
+ it "returns a checked radio-'input'-element with the passed name and value when checked is true" do
+ output = @html.radio_button("test", "test-value", true)
+ output.should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "radio", "VALUE" => "test-value"}, "", not_closed: true)
+
+ output = @html.radio_button("test", "test-value", false)
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "test-value"}, "", not_closed: true)
+
+ output = @html.radio_button("test", "test-value", nil)
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "test-value"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.radio_button("test", "test-value", nil) { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "test-value"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed Hash" do
+ it "returns a radio-'input'-element using the passed Hash for attributes" do
+ attributes = {"NAME" => "test", "VALUE" => "test-value", "CHECKED" => true}
+ output = @html.radio_button(attributes)
+ output.should equal_element("INPUT", attributes, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ attributes = {"NAME" => "test", "VALUE" => "test-value", "CHECKED" => true}
+ output = @html.radio_button(attributes) { "test" }
+ output.should equal_element("INPUT", attributes, "", not_closed: true)
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/radio_group_spec.rb b/spec/ruby/library/cgi/htmlextension/radio_group_spec.rb
new file mode 100644
index 0000000000..1bfd43449d
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/radio_group_spec.rb
@@ -0,0 +1,77 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#radio_group" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed name, values ..." do
+ it "returns a sequence of 'radio'-elements with the passed name and the passed values" do
+ output = CGISpecs.split(@html.radio_group("test", "foo", "bar", "baz"))
+ output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "foo"}, "foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "bar"}, "bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "baz"}, "baz", not_closed: true)
+ end
+
+ it "allows passing a value inside an Array" do
+ output = CGISpecs.split(@html.radio_group("test", ["foo"], "bar", ["baz"]))
+ output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "foo"}, "foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "bar"}, "bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "baz"}, "baz", not_closed: true)
+ end
+
+ it "allows passing a value as an Array containing the value and the checked state or a label" do
+ output = CGISpecs.split(@html.radio_group("test", ["foo"], ["bar", true], ["baz", "label for baz"]))
+ output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "foo"}, "foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "radio", "VALUE" => "bar"}, "bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "baz"}, "label for baz", not_closed: true)
+ end
+
+ # TODO: CGI does not like passing false instead of true.
+ it "allows passing a value as an Array containing the value, a label and the checked state" do
+ output = CGISpecs.split(@html.radio_group("test", ["foo", "label for foo", true], ["bar", "label for bar", false], ["baz", "label for baz", true]))
+ output[0].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "radio", "VALUE" => "foo"}, "label for foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "bar"}, "label for bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "radio", "VALUE" => "baz"}, "label for baz", not_closed: true)
+ end
+
+ it "returns an empty String when passed no values" do
+ @html.radio_group("test").should == ""
+ end
+
+ it "ignores a passed block" do
+ output = CGISpecs.split(@html.radio_group("test", "foo", "bar", "baz") { "test" })
+ output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "foo"}, "foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "bar"}, "bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "baz"}, "baz", not_closed: true)
+ end
+ end
+
+ describe "when passed Hash" do
+ it "uses the passed Hash to generate the radio sequence" do
+ output = CGISpecs.split(@html.radio_group("NAME" => "name", "VALUES" => ["foo", "bar", "baz"]))
+ output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "foo"}, "foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "bar"}, "bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "baz"}, "baz", not_closed: true)
+
+ output = CGISpecs.split(@html.radio_group("NAME" => "name", "VALUES" => [["foo"], ["bar", true], "baz"]))
+ output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "foo"}, "foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "name", "TYPE" => "radio", "VALUE" => "bar"}, "bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "baz"}, "baz", not_closed: true)
+
+ output = CGISpecs.split(@html.radio_group("NAME" => "name", "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"]))
+ output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "1"}, "Foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "name", "TYPE" => "radio", "VALUE" => "2"}, "Bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "Baz"}, "Baz", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = CGISpecs.split(@html.radio_group("NAME" => "name", "VALUES" => ["foo", "bar", "baz"]) { "test" })
+ output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "foo"}, "foo", not_closed: true)
+ output[1].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "bar"}, "bar", not_closed: true)
+ output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "baz"}, "baz", not_closed: true)
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/reset_spec.rb b/spec/ruby/library/cgi/htmlextension/reset_spec.rb
new file mode 100644
index 0000000000..86fa25fc8a
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/reset_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#reset" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed no arguments" do
+ it "returns a reset-'input'-element" do
+ output = @html.reset
+ output.should equal_element("INPUT", {"TYPE" => "reset"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.reset { "test" }
+ output.should equal_element("INPUT", {"TYPE" => "reset"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed value" do
+ it "returns a reset-'input'-element with the passed value" do
+ output = @html.reset("Example")
+ output.should equal_element("INPUT", {"TYPE" => "reset", "VALUE" => "Example"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.reset("Example") { "test" }
+ output.should equal_element("INPUT", {"TYPE" => "reset", "VALUE" => "Example"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed value, name" do
+ it "returns a reset-'input'-element with the passed value and the passed name" do
+ output = @html.reset("Example", "test-name")
+ output.should equal_element("INPUT", {"TYPE" => "reset", "VALUE" => "Example", "NAME" => "test-name"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.reset("Example", "test-name") { "test" }
+ output.should equal_element("INPUT", {"TYPE" => "reset", "VALUE" => "Example", "NAME" => "test-name"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed Hash" do
+ it "returns a reset-'input'-element with the passed value" do
+ output = @html.reset("Example")
+ output.should equal_element("INPUT", {"TYPE" => "reset", "VALUE" => "Example"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.reset("Example") { "test" }
+ output.should equal_element("INPUT", {"TYPE" => "reset", "VALUE" => "Example"}, "", not_closed: true)
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/scrolling_list_spec.rb b/spec/ruby/library/cgi/htmlextension/scrolling_list_spec.rb
new file mode 100644
index 0000000000..4eb0c86c1a
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/scrolling_list_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require_relative 'fixtures/common'
+require 'cgi'
+require_relative 'shared/popup_menu'
+
+describe "CGI::HtmlExtension#scrolling_list" do
+ it_behaves_like :cgi_htmlextension_popup_menu, :scrolling_list
+end
diff --git a/spec/ruby/library/cgi/htmlextension/shared/popup_menu.rb b/spec/ruby/library/cgi/htmlextension/shared/popup_menu.rb
new file mode 100644
index 0000000000..4787fd3f8e
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/shared/popup_menu.rb
@@ -0,0 +1,94 @@
+describe :cgi_htmlextension_popup_menu, shared: true do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed no arguments" do
+ it "returns an empty 'select'-element without a name" do
+ output = @html.send(@method)
+ output.should equal_element("SELECT", {"NAME" => ""}, "")
+ end
+
+ it "ignores a passed block" do
+ output = @html.send(@method) { "test" }
+ output.should equal_element("SELECT", {"NAME" => ""}, "")
+ end
+ end
+
+ describe "when passed name, values ..." do
+ it "returns a 'select'-element with the passed name containing 'option'-elements based on the passed values" do
+ content = @html.option("VALUE" => "foo") { "foo" }
+ content << @html.option("VALUE" => "bar") { "bar" }
+ content << @html.option("VALUE" => "baz") { "baz" }
+
+ output = @html.send(@method, "test", "foo", "bar", "baz")
+ output.should equal_element("SELECT", {"NAME" => "test"}, content)
+ end
+
+ it "allows passing values inside of arrays" do
+ content = @html.option("VALUE" => "foo") { "foo" }
+ content << @html.option("VALUE" => "bar") { "bar" }
+ content << @html.option("VALUE" => "baz") { "baz" }
+
+ output = @html.send(@method, "test", ["foo"], ["bar"], ["baz"])
+ output.should equal_element("SELECT", {"NAME" => "test"}, content)
+ end
+
+ it "allows passing a value as an Array containing the value and the select state or a label" do
+ content = @html.option("VALUE" => "foo") { "foo" }
+ content << @html.option("VALUE" => "bar", "SELECTED" => true) { "bar" }
+ content << @html.option("VALUE" => "baz") { "baz" }
+
+ output = @html.send(@method, "test", ["foo"], ["bar", true], "baz")
+ output.should equal_element("SELECT", {"NAME" => "test"}, content)
+ end
+
+ it "allows passing a value as an Array containing the value, a label and the select state" do
+ content = @html.option("VALUE" => "1") { "Foo" }
+ content << @html.option("VALUE" => "2", "SELECTED" => true) { "Bar" }
+ content << @html.option("VALUE" => "Baz") { "Baz" }
+
+ output = @html.send(@method, "test", ["1", "Foo"], ["2", "Bar", true], "Baz")
+ output.should equal_element("SELECT", {"NAME" => "test"}, content)
+ end
+
+ it "ignores a passed block" do
+ content = @html.option("VALUE" => "foo") { "foo" }
+ content << @html.option("VALUE" => "bar") { "bar" }
+ content << @html.option("VALUE" => "baz") { "baz" }
+
+ output = @html.send(@method, "test", "foo", "bar", "baz") { "woot" }
+ output.should equal_element("SELECT", {"NAME" => "test"}, content)
+ end
+ end
+
+ describe "when passed a Hash" do
+ it "uses the passed Hash to generate the 'select'-element and the 'option'-elements" do
+ attributes = {
+ "NAME" => "test", "SIZE" => 2, "MULTIPLE" => true,
+ "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"]
+ }
+
+ content = @html.option("VALUE" => "1") { "Foo" }
+ content << @html.option("VALUE" => "2", "SELECTED" => true) { "Bar" }
+ content << @html.option("VALUE" => "Baz") { "Baz" }
+
+ output = @html.send(@method, attributes)
+ output.should equal_element("SELECT", {"NAME" => "test", "SIZE" => 2, "MULTIPLE" => true}, content)
+ end
+
+ it "ignores a passed block" do
+ attributes = {
+ "NAME" => "test", "SIZE" => 2, "MULTIPLE" => true,
+ "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"]
+ }
+
+ content = @html.option("VALUE" => "1") { "Foo" }
+ content << @html.option("VALUE" => "2", "SELECTED" => true) { "Bar" }
+ content << @html.option("VALUE" => "Baz") { "Baz" }
+
+ output = @html.send(@method, attributes) { "testing" }
+ output.should equal_element("SELECT", {"NAME" => "test", "SIZE" => 2, "MULTIPLE" => true}, content)
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/submit_spec.rb b/spec/ruby/library/cgi/htmlextension/submit_spec.rb
new file mode 100644
index 0000000000..063891b959
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/submit_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#submit" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed no arguments" do
+ it "returns a submit-'input'-element" do
+ output = @html.submit
+ output.should equal_element("INPUT", {"TYPE" => "submit"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.submit { "test" }
+ output.should equal_element("INPUT", {"TYPE" => "submit"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed value" do
+ it "returns a submit-'input'-element with the passed value" do
+ output = @html.submit("Example")
+ output.should equal_element("INPUT", {"TYPE" => "submit", "VALUE" => "Example"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.submit("Example") { "test" }
+ output.should equal_element("INPUT", {"TYPE" => "submit", "VALUE" => "Example"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed value, name" do
+ it "returns a submit-'input'-element with the passed value and the passed name" do
+ output = @html.submit("Example", "test-name")
+ output.should equal_element("INPUT", {"TYPE" => "submit", "VALUE" => "Example", "NAME" => "test-name"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.submit("Example", "test-name") { "test" }
+ output.should equal_element("INPUT", {"TYPE" => "submit", "VALUE" => "Example", "NAME" => "test-name"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed Hash" do
+ it "returns a submit-'input'-element with the passed value" do
+ output = @html.submit("Example")
+ output.should equal_element("INPUT", {"TYPE" => "submit", "VALUE" => "Example"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.submit("Example") { "test" }
+ output.should equal_element("INPUT", {"TYPE" => "submit", "VALUE" => "Example"}, "", not_closed: true)
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/text_field_spec.rb b/spec/ruby/library/cgi/htmlextension/text_field_spec.rb
new file mode 100644
index 0000000000..44b5a5e69f
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/text_field_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#text_field" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed no arguments" do
+ it "returns an text-'input'-element without a name" do
+ output = @html.text_field
+ output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "text", "SIZE" => "40"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.text_field { "test" }
+ output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "text", "SIZE" => "40"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name" do
+ it "returns an text-'input'-element with the passed name" do
+ output = @html.text_field("test")
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "SIZE" => "40"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.text_field("test") { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "SIZE" => "40"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name, value" do
+ it "returns an text-'input'-element with the passed name and value" do
+ output = @html.text_field("test", "some value")
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "VALUE" => "some value", "SIZE" => "40"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.text_field("test", "some value") { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "VALUE" => "some value", "SIZE" => "40"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name, value, size" do
+ it "returns an text-'input'-element with the passed name, value and size" do
+ output = @html.text_field("test", "some value", 60)
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "VALUE" => "some value", "SIZE" => "60"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.text_field("test", "some value", 60) { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "VALUE" => "some value", "SIZE" => "60"}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed name, value, size, maxlength" do
+ it "returns an text-'input'-element with the passed name, value, size and maxlength" do
+ output = @html.text_field("test", "some value", 60, 12)
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "VALUE" => "some value", "SIZE" => "60", "MAXLENGTH" => 12}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.text_field("test", "some value", 60, 12) { "test" }
+ output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "VALUE" => "some value", "SIZE" => "60", "MAXLENGTH" => 12}, "", not_closed: true)
+ end
+ end
+
+ describe "when passed Hash" do
+ it "returns a checkbox-'input'-element using the passed Hash for attributes" do
+ output = @html.text_field("NAME" => "test", "VALUE" => "some value")
+ output.should equal_element("INPUT", { "NAME" => "test", "VALUE" => "some value", "TYPE" => "text" }, "", not_closed: true)
+
+ output = @html.text_field("TYPE" => "hidden")
+ output.should equal_element("INPUT", {"TYPE" => "text"}, "", not_closed: true)
+ end
+
+ it "ignores a passed block" do
+ output = @html.text_field("NAME" => "test", "VALUE" => "some value") { "test" }
+ output.should equal_element("INPUT", { "NAME" => "test", "VALUE" => "some value", "TYPE" => "text" }, "", not_closed: true)
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/htmlextension/textarea_spec.rb b/spec/ruby/library/cgi/htmlextension/textarea_spec.rb
new file mode 100644
index 0000000000..db84a973d2
--- /dev/null
+++ b/spec/ruby/library/cgi/htmlextension/textarea_spec.rb
@@ -0,0 +1,73 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'fixtures/common'
+
+describe "CGI::HtmlExtension#textarea" do
+ before :each do
+ @html = CGISpecs.cgi_new
+ end
+
+ describe "when passed no arguments" do
+ it "returns an 'textarea'-element without a name" do
+ output = @html.textarea
+ output.should equal_element("TEXTAREA", {"NAME" => "", "COLS" => "70", "ROWS" => "10"}, "")
+ end
+
+ it "includes the return value of the passed block when passed a block" do
+ output = @html.textarea { "Example" }
+ output.should equal_element("TEXTAREA", {"NAME" => "", "COLS" => "70", "ROWS" => "10"}, "Example")
+ end
+ end
+
+ describe "when passed name" do
+ it "returns an 'textarea'-element with the passed name" do
+ output = @html.textarea("test")
+ output.should equal_element("TEXTAREA", {"NAME" => "test", "COLS" => "70", "ROWS" => "10"}, "")
+ end
+
+ it "includes the return value of the passed block when passed a block" do
+ output = @html.textarea("test") { "Example" }
+ output.should equal_element("TEXTAREA", {"NAME" => "test", "COLS" => "70", "ROWS" => "10"}, "Example")
+ end
+ end
+
+ describe "when passed name, cols" do
+ it "returns an 'textarea'-element with the passed name and the passed amount of columns" do
+ output = @html.textarea("test", 40)
+ output.should equal_element("TEXTAREA", {"NAME" => "test", "COLS" => "40", "ROWS" => "10"}, "")
+ end
+
+ it "includes the return value of the passed block when passed a block" do
+ output = @html.textarea("test", 40) { "Example" }
+ output.should equal_element("TEXTAREA", {"NAME" => "test", "COLS" => "40", "ROWS" => "10"}, "Example")
+ end
+ end
+
+ describe "when passed name, cols, rows" do
+ it "returns an 'textarea'-element with the passed name, the passed amount of columns and the passed number of rows" do
+ output = @html.textarea("test", 40, 5)
+ output.should equal_element("TEXTAREA", {"NAME" => "test", "COLS" => "40", "ROWS" => "5"}, "")
+ end
+
+ it "includes the return value of the passed block when passed a block" do
+ output = @html.textarea("test", 40, 5) { "Example" }
+ output.should equal_element("TEXTAREA", {"NAME" => "test", "COLS" => "40", "ROWS" => "5"}, "Example")
+ end
+ end
+
+ describe "when passed Hash" do
+ it "uses the passed Hash as attributes" do
+ @html.textarea("ID" => "test").should == '<TEXTAREA ID="test"></TEXTAREA>'
+
+ attributes = {"ID" => "test-id", "NAME" => "test-name"}
+ output = @html.textarea(attributes)
+ output.should equal_element("TEXTAREA", attributes, "")
+ end
+
+ it "includes the return value of the passed block when passed a block" do
+ attributes = {"ID" => "test-id", "NAME" => "test-name"}
+ output = @html.textarea(attributes) { "test" }
+ output.should equal_element("TEXTAREA", attributes, "test")
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/http_header_spec.rb b/spec/ruby/library/cgi/http_header_spec.rb
new file mode 100644
index 0000000000..4094bebed3
--- /dev/null
+++ b/spec/ruby/library/cgi/http_header_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'cgi'
+
+require_relative 'shared/http_header'
+
+describe "CGI#http_header" do
+ it_behaves_like :cgi_http_header, :http_header
+end
diff --git a/spec/ruby/library/cgi/initialize_spec.rb b/spec/ruby/library/cgi/initialize_spec.rb
new file mode 100644
index 0000000000..f794f157f0
--- /dev/null
+++ b/spec/ruby/library/cgi/initialize_spec.rb
@@ -0,0 +1,133 @@
+require_relative '../../spec_helper'
+require 'cgi'
+
+describe "CGI#initialize" do
+ it "is private" do
+ CGI.should have_private_instance_method(:initialize)
+ end
+end
+
+describe "CGI#initialize when passed no arguments" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.allocate
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "extends self with CGI::QueryExtension" do
+ @cgi.send(:initialize)
+ @cgi.should be_kind_of(CGI::QueryExtension)
+ end
+
+ it "does not extend self with CGI::HtmlExtension" do
+ @cgi.send(:initialize)
+ @cgi.should_not be_kind_of(CGI::HtmlExtension)
+ end
+
+ it "does not extend self with any of the other HTML modules" do
+ @cgi.send(:initialize)
+ @cgi.should_not be_kind_of(CGI::Html3)
+ @cgi.should_not be_kind_of(CGI::HtmlExtension)
+ @cgi.should_not be_kind_of(CGI::Html4)
+ @cgi.should_not be_kind_of(CGI::Html4Tr)
+ @cgi.should_not be_kind_of(CGI::Html4Fr)
+ end
+
+ it "sets #cookies based on ENV['HTTP_COOKIE']" do
+ begin
+ old_env, ENV["HTTP_COOKIE"] = ENV["HTTP_COOKIE"], "test=test yay"
+ @cgi.send(:initialize)
+ @cgi.cookies.should == { "test"=>[ "test yay" ] }
+ ensure
+ ENV["HTTP_COOKIE"] = old_env
+ end
+ end
+
+ it "sets #params based on ENV['QUERY_STRING'] when ENV['REQUEST_METHOD'] is GET" do
+ begin
+ old_env_query, ENV["QUERY_STRING"] = ENV["QUERY_STRING"], "?test=a&test2=b"
+ old_env_method, ENV["REQUEST_METHOD"] = ENV["REQUEST_METHOD"], "GET"
+ @cgi.send(:initialize)
+ @cgi.params.should == { "test2" => ["b"], "?test" => ["a"] }
+ ensure
+ ENV["QUERY_STRING"] = old_env_query
+ ENV["REQUEST_METHOD"] = old_env_method
+ end
+ end
+
+ it "sets #params based on ENV['QUERY_STRING'] when ENV['REQUEST_METHOD'] is HEAD" do
+ begin
+ old_env_query, ENV["QUERY_STRING"] = ENV["QUERY_STRING"], "?test=a&test2=b"
+ old_env_method, ENV["REQUEST_METHOD"] = ENV["REQUEST_METHOD"], "HEAD"
+ @cgi.send(:initialize)
+ @cgi.params.should == { "test2" => ["b"], "?test" => ["a"] }
+ ensure
+ ENV["QUERY_STRING"] = old_env_query
+ ENV["REQUEST_METHOD"] = old_env_method
+ end
+ end
+end
+
+describe "CGI#initialize when passed type" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.allocate
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+
+ it "extends self with CGI::QueryExtension" do
+ @cgi.send(:initialize, "test")
+ @cgi.should be_kind_of(CGI::QueryExtension)
+ end
+
+ it "extends self with CGI::QueryExtension, CGI::Html3 and CGI::HtmlExtension when the passed type is 'html3'" do
+ @cgi.send(:initialize, "html3")
+ @cgi.should be_kind_of(CGI::Html3)
+ @cgi.should be_kind_of(CGI::HtmlExtension)
+ @cgi.should be_kind_of(CGI::QueryExtension)
+
+ @cgi.should_not be_kind_of(CGI::Html4)
+ @cgi.should_not be_kind_of(CGI::Html4Tr)
+ @cgi.should_not be_kind_of(CGI::Html4Fr)
+ end
+
+ it "extends self with CGI::QueryExtension, CGI::Html4 and CGI::HtmlExtension when the passed type is 'html4'" do
+ @cgi.send(:initialize, "html4")
+ @cgi.should be_kind_of(CGI::Html4)
+ @cgi.should be_kind_of(CGI::HtmlExtension)
+ @cgi.should be_kind_of(CGI::QueryExtension)
+
+ @cgi.should_not be_kind_of(CGI::Html3)
+ @cgi.should_not be_kind_of(CGI::Html4Tr)
+ @cgi.should_not be_kind_of(CGI::Html4Fr)
+ end
+
+ it "extends self with CGI::QueryExtension, CGI::Html4Tr and CGI::HtmlExtension when the passed type is 'html4Tr'" do
+ @cgi.send(:initialize, "html4Tr")
+ @cgi.should be_kind_of(CGI::Html4Tr)
+ @cgi.should be_kind_of(CGI::HtmlExtension)
+ @cgi.should be_kind_of(CGI::QueryExtension)
+
+ @cgi.should_not be_kind_of(CGI::Html3)
+ @cgi.should_not be_kind_of(CGI::Html4)
+ @cgi.should_not be_kind_of(CGI::Html4Fr)
+ end
+
+ it "extends self with CGI::QueryExtension, CGI::Html4Tr, CGI::Html4Fr and CGI::HtmlExtension when the passed type is 'html4Fr'" do
+ @cgi.send(:initialize, "html4Fr")
+ @cgi.should be_kind_of(CGI::Html4Tr)
+ @cgi.should be_kind_of(CGI::Html4Fr)
+ @cgi.should be_kind_of(CGI::HtmlExtension)
+ @cgi.should be_kind_of(CGI::QueryExtension)
+
+ @cgi.should_not be_kind_of(CGI::Html3)
+ @cgi.should_not be_kind_of(CGI::Html4)
+ end
+end
diff --git a/spec/ruby/library/cgi/out_spec.rb b/spec/ruby/library/cgi/out_spec.rb
new file mode 100644
index 0000000000..84068cdd4f
--- /dev/null
+++ b/spec/ruby/library/cgi/out_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require 'cgi'
+
+describe "CGI#out" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ $stdout, @old_stdout = IOStub.new, $stdout
+ end
+
+ after :each do
+ $stdout = @old_stdout
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "it writes a HTMl header based on the passed argument to $stdout" do
+ @cgi.out { "" }
+ $stdout.should == "Content-Type: text/html\r\nContent-Length: 0\r\n\r\n"
+ end
+
+ it "appends the block's return value to the HTML header" do
+ @cgi.out { "test!" }
+ $stdout.should == "Content-Type: text/html\r\nContent-Length: 5\r\n\r\ntest!"
+ end
+
+ it "automatically sets the Content-Length Header based on the block's return value" do
+ @cgi.out { "0123456789" }
+ $stdout.should == "Content-Type: text/html\r\nContent-Length: 10\r\n\r\n0123456789"
+ end
+
+ it "includes Cookies in the @output_cookies field" do
+ @cgi.instance_variable_set(:@output_cookies, ["multiple", "cookies"])
+ @cgi.out { "" }
+ $stdout.should == "Content-Type: text/html\r\nContent-Length: 0\r\nSet-Cookie: multiple\r\nSet-Cookie: cookies\r\n\r\n"
+ end
+end
+
+describe "CGI#out when passed no block" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "raises a LocalJumpError" do
+ lambda { @cgi.out }.should raise_error(LocalJumpError)
+ end
+end
diff --git a/spec/ruby/library/cgi/parse_spec.rb b/spec/ruby/library/cgi/parse_spec.rb
new file mode 100644
index 0000000000..04539b1226
--- /dev/null
+++ b/spec/ruby/library/cgi/parse_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require 'cgi'
+
+describe "CGI.parse when passed String" do
+ it "parses a HTTP Query String into a Hash" do
+ CGI.parse("test=test&a=b").should == { "a" => ["b"], "test" => ["test"] }
+ CGI.parse("test=1,2,3").should == { "test" => ["1,2,3"] }
+ CGI.parse("test=a&a=&b=").should == { "test" => ["a"], "a" => [""], "b" => [""] }
+ end
+
+ it "parses query strings with semicolons in place of ampersands" do
+ CGI.parse("test=test;a=b").should == { "a" => ["b"], "test" => ["test"] }
+ CGI.parse("test=a;a=;b=").should == { "test" => ["a"], "a" => [""], "b" => [""] }
+ end
+
+ it "allows passing multiple values for one key" do
+ CGI.parse("test=1&test=2&test=3").should == { "test" => ["1", "2", "3"] }
+ CGI.parse("test[]=1&test[]=2&test[]=3").should == { "test[]" => [ "1", "2", "3" ] }
+ end
+
+ it "unescapes keys and values" do
+ CGI.parse("hello%3F=hello%21").should == { "hello?" => ["hello!"] }
+ end
+end
diff --git a/spec/ruby/library/cgi/pretty_spec.rb b/spec/ruby/library/cgi/pretty_spec.rb
new file mode 100644
index 0000000000..a7b7505c15
--- /dev/null
+++ b/spec/ruby/library/cgi/pretty_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require 'cgi'
+
+describe "CGI.pretty when passed html" do
+ it "indents the passed html String with two spaces" do
+ CGI.pretty("<HTML><BODY></BODY></HTML>").should == <<-EOS
+<HTML>
+ <BODY>
+ </BODY>
+</HTML>
+EOS
+ end
+end
+
+describe "CGI.pretty when passed html, indentation_unit" do
+ it "indents the passed html String with the passed indentation_unit" do
+ CGI.pretty("<HTML><BODY></BODY></HTML>", "\t").should == <<-EOS
+<HTML>
+\t<BODY>
+\t</BODY>
+</HTML>
+EOS
+ end
+end
diff --git a/spec/ruby/library/cgi/print_spec.rb b/spec/ruby/library/cgi/print_spec.rb
new file mode 100644
index 0000000000..18ab8d673b
--- /dev/null
+++ b/spec/ruby/library/cgi/print_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require 'cgi'
+
+describe "CGI#print" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "passes all arguments to $stdout.print" do
+ $stdout.should_receive(:print).with("test")
+ @cgi.print("test")
+
+ $stdout.should_receive(:print).with("one", "two", "three", ["four", "five"])
+ @cgi.print("one", "two", "three", ["four", "five"])
+ end
+
+ it "returns the result of calling $stdout.print" do
+ $stdout.should_receive(:print).with("test").and_return(:expected)
+ @cgi.print("test").should == :expected
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/accept_charset_spec.rb b/spec/ruby/library/cgi/queryextension/accept_charset_spec.rb
new file mode 100644
index 0000000000..0487569b9c
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/accept_charset_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#accept_charset" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['HTTP_ACCEPT_CHARSET']" do
+ old_value, ENV['HTTP_ACCEPT_CHARSET'] = ENV['HTTP_ACCEPT_CHARSET'], "ISO-8859-1,utf-8;q=0.7,*;q=0.7"
+ begin
+ @cgi.accept_charset.should == "ISO-8859-1,utf-8;q=0.7,*;q=0.7"
+ ensure
+ ENV['HTTP_ACCEPT_CHARSET'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/accept_encoding_spec.rb b/spec/ruby/library/cgi/queryextension/accept_encoding_spec.rb
new file mode 100644
index 0000000000..35ff3c2b30
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/accept_encoding_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#accept_encoding" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['HTTP_ACCEPT_ENCODING']" do
+ old_value, ENV['HTTP_ACCEPT_ENCODING'] = ENV['HTTP_ACCEPT_ENCODING'], "gzip,deflate"
+ begin
+ @cgi.accept_encoding.should == "gzip,deflate"
+ ensure
+ ENV['HTTP_ACCEPT_ENCODING'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/accept_language_spec.rb b/spec/ruby/library/cgi/queryextension/accept_language_spec.rb
new file mode 100644
index 0000000000..4a15d58914
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/accept_language_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#accept_language" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['HTTP_ACCEPT_LANGUAGE']" do
+ old_value, ENV['HTTP_ACCEPT_LANGUAGE'] = ENV['HTTP_ACCEPT_LANGUAGE'], "en-us,en;q=0.5"
+ begin
+ @cgi.accept_language.should == "en-us,en;q=0.5"
+ ensure
+ ENV['HTTP_ACCEPT_LANGUAGE'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/accept_spec.rb b/spec/ruby/library/cgi/queryextension/accept_spec.rb
new file mode 100644
index 0000000000..af5209ffbe
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/accept_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#accept" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['HTTP_ACCEPT']" do
+ old_value, ENV['HTTP_ACCEPT'] = ENV['HTTP_ACCEPT'], "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
+ begin
+ @cgi.accept.should == "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
+ ensure
+ ENV['HTTP_ACCEPT'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/auth_type_spec.rb b/spec/ruby/library/cgi/queryextension/auth_type_spec.rb
new file mode 100644
index 0000000000..25318269b1
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/auth_type_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#auth_type" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['AUTH_TYPE']" do
+ old_value, ENV['AUTH_TYPE'] = ENV['AUTH_TYPE'], "Basic"
+ begin
+ @cgi.auth_type.should == "Basic"
+ ensure
+ ENV['AUTH_TYPE'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/cache_control_spec.rb b/spec/ruby/library/cgi/queryextension/cache_control_spec.rb
new file mode 100644
index 0000000000..0471307c22
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/cache_control_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#cache_control" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['HTTP_CACHE_CONTROL']" do
+ old_value, ENV['HTTP_CACHE_CONTROL'] = ENV['HTTP_CACHE_CONTROL'], "no-cache"
+ begin
+ @cgi.cache_control.should == "no-cache"
+ ensure
+ ENV['HTTP_CACHE_CONTROL'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/content_length_spec.rb b/spec/ruby/library/cgi/queryextension/content_length_spec.rb
new file mode 100644
index 0000000000..de823f7119
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/content_length_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#content_length" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['CONTENT_LENGTH'] as Integer" do
+ old_value = ENV['CONTENT_LENGTH']
+ begin
+ ENV['CONTENT_LENGTH'] = nil
+ @cgi.content_length.should be_nil
+
+ ENV['CONTENT_LENGTH'] = "100"
+ @cgi.content_length.should eql(100)
+ ensure
+ ENV['CONTENT_LENGTH'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/content_type_spec.rb b/spec/ruby/library/cgi/queryextension/content_type_spec.rb
new file mode 100644
index 0000000000..49b8389c87
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/content_type_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#content_type" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['CONTENT_TYPE']" do
+ old_value, ENV['CONTENT_TYPE'] = ENV['CONTENT_TYPE'], "text/html"
+ begin
+ @cgi.content_type.should == "text/html"
+ ensure
+ ENV['CONTENT_TYPE'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/cookies_spec.rb b/spec/ruby/library/cgi/queryextension/cookies_spec.rb
new file mode 100644
index 0000000000..4befd61ab7
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/cookies_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#cookies" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "CGI::QueryExtension#cookies=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/cgi/queryextension/element_reference_spec.rb b/spec/ruby/library/cgi/queryextension/element_reference_spec.rb
new file mode 100644
index 0000000000..6ac5b46407
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/element_reference_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#[]" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ ENV['QUERY_STRING'], @old_query_string = "one=a&two=b&two=c", ENV['QUERY_STRING']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ ENV['QUERY_STRING'] = @old_query_string
+ end
+
+ it "it returns the value for the parameter with the given key" do
+ @cgi["one"].should == "a"
+ end
+
+ it "only returns the first value for parameters with multiple values" do
+ @cgi["two"].should == "b"
+ end
+
+ it "returns a String" do
+ @cgi["one"].should be_kind_of(String)
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/from_spec.rb b/spec/ruby/library/cgi/queryextension/from_spec.rb
new file mode 100644
index 0000000000..04a992cfc7
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/from_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#from" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['HTTP_FROM']" do
+ old_value, ENV['HTTP_FROM'] = ENV['HTTP_FROM'], "googlebot(at)googlebot.com"
+ begin
+ @cgi.from.should == "googlebot(at)googlebot.com"
+ ensure
+ ENV['HTTP_FROM'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/gateway_interface_spec.rb b/spec/ruby/library/cgi/queryextension/gateway_interface_spec.rb
new file mode 100644
index 0000000000..3ead5bd8bf
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/gateway_interface_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#gateway_interface" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['GATEWAY_INTERFACE']" do
+ old_value, ENV['GATEWAY_INTERFACE'] = ENV['GATEWAY_INTERFACE'], "CGI/1.1"
+ begin
+ @cgi.gateway_interface.should == "CGI/1.1"
+ ensure
+ ENV['GATEWAY_INTERFACE'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/has_key_spec.rb b/spec/ruby/library/cgi/queryextension/has_key_spec.rb
new file mode 100644
index 0000000000..525b45b507
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/has_key_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'shared/has_key'
+
+describe "CGI::QueryExtension#has_key?" do
+ it_behaves_like :cgi_query_extension_has_key_p, :has_key?
+end
diff --git a/spec/ruby/library/cgi/queryextension/host_spec.rb b/spec/ruby/library/cgi/queryextension/host_spec.rb
new file mode 100644
index 0000000000..e820e0afc3
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/host_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#host" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['HTTP_HOST']" do
+ old_value, ENV['HTTP_HOST'] = ENV['HTTP_HOST'], "localhost"
+ begin
+ @cgi.host.should == "localhost"
+ ensure
+ ENV['HTTP_HOST'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/include_spec.rb b/spec/ruby/library/cgi/queryextension/include_spec.rb
new file mode 100644
index 0000000000..12365ea284
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/include_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'shared/has_key'
+
+describe "CGI::QueryExtension#include?" do
+ it_behaves_like :cgi_query_extension_has_key_p, :include?
+end
diff --git a/spec/ruby/library/cgi/queryextension/key_spec.rb b/spec/ruby/library/cgi/queryextension/key_spec.rb
new file mode 100644
index 0000000000..d0f1e4cee2
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/key_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require_relative 'shared/has_key'
+
+describe "CGI::QueryExtension#key?" do
+ it_behaves_like :cgi_query_extension_has_key_p, :key?
+end
diff --git a/spec/ruby/library/cgi/queryextension/keys_spec.rb b/spec/ruby/library/cgi/queryextension/keys_spec.rb
new file mode 100644
index 0000000000..14d77180fa
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/keys_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#keys" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ ENV['QUERY_STRING'], @old_query_string = "one=a&two=b", ENV['QUERY_STRING']
+
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ ENV['QUERY_STRING'] = @old_query_string
+ end
+
+ it "returns all parameter keys as an Array" do
+ @cgi.keys.sort.should == ["one", "two"]
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/multipart_spec.rb b/spec/ruby/library/cgi/queryextension/multipart_spec.rb
new file mode 100644
index 0000000000..ced4cb05a1
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/multipart_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+require "stringio"
+
+describe "CGI::QueryExtension#multipart?" do
+ before :each do
+ @old_stdin = $stdin
+
+ @old_request_method = ENV['REQUEST_METHOD']
+ @old_content_type = ENV['CONTENT_TYPE']
+ @old_content_length = ENV['CONTENT_LENGTH']
+
+ ENV['REQUEST_METHOD'] = "POST"
+ ENV["CONTENT_TYPE"] = "multipart/form-data; boundary=---------------------------1137522503144128232716531729"
+ ENV["CONTENT_LENGTH"] = "222"
+
+ $stdin = StringIO.new <<-EOS
+-----------------------------1137522503144128232716531729\r
+Content-Disposition: form-data; name="file"; filename=""\r
+Content-Type: application/octet-stream\r
+\r
+\r
+-----------------------------1137522503144128232716531729--\r
+EOS
+
+ @cgi = CGI.new
+ end
+
+ after :each do
+ $stdin = @old_stdin
+
+ ENV['REQUEST_METHOD'] = @old_request_method
+ ENV['CONTENT_TYPE'] = @old_content_type
+ ENV['CONTENT_LENGTH'] = @old_content_length
+ end
+
+ it "returns true if the current Request is a multipart request" do
+ @cgi.multipart?.should be_true
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/negotiate_spec.rb b/spec/ruby/library/cgi/queryextension/negotiate_spec.rb
new file mode 100644
index 0000000000..b6fe036042
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/negotiate_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#negotiate" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['HTTP_NEGOTIATE']" do
+ old_value, ENV['HTTP_NEGOTIATE'] = ENV['HTTP_NEGOTIATE'], "trans"
+ begin
+ @cgi.negotiate.should == "trans"
+ ensure
+ ENV['HTTP_NEGOTIATE'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/params_spec.rb b/spec/ruby/library/cgi/queryextension/params_spec.rb
new file mode 100644
index 0000000000..f4449e3c8a
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/params_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#params" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ ENV['QUERY_STRING'], @old_query_string = "one=a&two=b&two=c&three", ENV['QUERY_STRING']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['QUERY_STRING'] = @old_query_string
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns the parsed HTTP Query Params" do
+ @cgi.params.should == {"three"=>[], "two"=>["b", "c"], "one"=>["a"]}
+ end
+end
+
+describe "CGI::QueryExtension#params=" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "sets the HTTP Query Params to the passed argument" do
+ @cgi.params.should == {}
+
+ @cgi.params = {"one"=>["a"], "two"=>["b", "c"]}
+ @cgi.params.should == {"one"=>["a"], "two"=>["b", "c"]}
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/path_info_spec.rb b/spec/ruby/library/cgi/queryextension/path_info_spec.rb
new file mode 100644
index 0000000000..9785df85f1
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/path_info_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#path_info" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['PATH_INFO']" do
+ old_value, ENV['PATH_INFO'] = ENV['PATH_INFO'], "/test/path"
+ begin
+ @cgi.path_info.should == "/test/path"
+ ensure
+ ENV['PATH_INFO'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/path_translated_spec.rb b/spec/ruby/library/cgi/queryextension/path_translated_spec.rb
new file mode 100644
index 0000000000..417a749341
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/path_translated_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#path_translated" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['PATH_TRANSLATED']" do
+ old_value, ENV['PATH_TRANSLATED'] = ENV['PATH_TRANSLATED'], "/full/path/to/dir"
+ begin
+ @cgi.path_translated.should == "/full/path/to/dir"
+ ensure
+ ENV['PATH_TRANSLATED'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/pragma_spec.rb b/spec/ruby/library/cgi/queryextension/pragma_spec.rb
new file mode 100644
index 0000000000..02d5c91221
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/pragma_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#pragma" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['HTTP_PRAGMA']" do
+ old_value, ENV['HTTP_PRAGMA'] = ENV['HTTP_PRAGMA'], "no-cache"
+ begin
+ @cgi.pragma.should == "no-cache"
+ ensure
+ ENV['HTTP_PRAGMA'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/query_string_spec.rb b/spec/ruby/library/cgi/queryextension/query_string_spec.rb
new file mode 100644
index 0000000000..a6b454a7eb
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/query_string_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#query_string" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['QUERY_STRING']" do
+ old_value, ENV['QUERY_STRING'] = ENV['QUERY_STRING'], "one=a&two=b"
+ begin
+ @cgi.query_string.should == "one=a&two=b"
+ ensure
+ ENV['QUERY_STRING'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/raw_cookie2_spec.rb b/spec/ruby/library/cgi/queryextension/raw_cookie2_spec.rb
new file mode 100644
index 0000000000..3d7072e346
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/raw_cookie2_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#raw_cookie2" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['HTTP_COOKIE2']" do
+ old_value, ENV['HTTP_COOKIE2'] = ENV['HTTP_COOKIE2'], "some_cookie=data"
+ begin
+ @cgi.raw_cookie2.should == "some_cookie=data"
+ ensure
+ ENV['HTTP_COOKIE2'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/raw_cookie_spec.rb b/spec/ruby/library/cgi/queryextension/raw_cookie_spec.rb
new file mode 100644
index 0000000000..ede7b9ff87
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/raw_cookie_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#raw_cookie" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['HTTP_COOKIE']" do
+ old_value, ENV['HTTP_COOKIE'] = ENV['HTTP_COOKIE'], "some_cookie=data"
+ begin
+ @cgi.raw_cookie.should == "some_cookie=data"
+ ensure
+ ENV['HTTP_COOKIE'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/referer_spec.rb b/spec/ruby/library/cgi/queryextension/referer_spec.rb
new file mode 100644
index 0000000000..6cd5dc96f8
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/referer_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#referer" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['HTTP_REFERER']" do
+ old_value, ENV['HTTP_REFERER'] = ENV['HTTP_REFERER'], "example.com"
+ begin
+ @cgi.referer.should == "example.com"
+ ensure
+ ENV['HTTP_REFERER'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/remote_addr_spec.rb b/spec/ruby/library/cgi/queryextension/remote_addr_spec.rb
new file mode 100644
index 0000000000..0259402b23
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/remote_addr_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#remote_addr" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['REMOTE_ADDR']" do
+ old_value, ENV['REMOTE_ADDR'] = ENV['REMOTE_ADDR'], "127.0.0.1"
+ begin
+ @cgi.remote_addr.should == "127.0.0.1"
+ ensure
+ ENV['REMOTE_ADDR'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/remote_host_spec.rb b/spec/ruby/library/cgi/queryextension/remote_host_spec.rb
new file mode 100644
index 0000000000..cf77e0031b
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/remote_host_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#remote_host" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['REMOTE_HOST']" do
+ old_value, ENV['REMOTE_HOST'] = ENV['REMOTE_HOST'], "test.host"
+ begin
+ @cgi.remote_host.should == "test.host"
+ ensure
+ ENV['REMOTE_HOST'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/remote_ident_spec.rb b/spec/ruby/library/cgi/queryextension/remote_ident_spec.rb
new file mode 100644
index 0000000000..5b51d6b8ee
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/remote_ident_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#remote_ident" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['REMOTE_IDENT']" do
+ old_value, ENV['REMOTE_IDENT'] = ENV['REMOTE_IDENT'], "no-idea-what-this-is-for"
+ begin
+ @cgi.remote_ident.should == "no-idea-what-this-is-for"
+ ensure
+ ENV['REMOTE_IDENT'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/remote_user_spec.rb b/spec/ruby/library/cgi/queryextension/remote_user_spec.rb
new file mode 100644
index 0000000000..1a93bb6561
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/remote_user_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#remote_user" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['REMOTE_USER']" do
+ old_value, ENV['REMOTE_USER'] = ENV['REMOTE_USER'], "username"
+ begin
+ @cgi.remote_user.should == "username"
+ ensure
+ ENV['REMOTE_USER'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/request_method_spec.rb b/spec/ruby/library/cgi/queryextension/request_method_spec.rb
new file mode 100644
index 0000000000..eabdd6998b
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/request_method_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#request_method" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['REQUEST_METHOD']" do
+ old_value, ENV['REQUEST_METHOD'] = ENV['REQUEST_METHOD'], "GET"
+ begin
+ @cgi.request_method.should == "GET"
+ ensure
+ ENV['REQUEST_METHOD'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/script_name_spec.rb b/spec/ruby/library/cgi/queryextension/script_name_spec.rb
new file mode 100644
index 0000000000..341b7b6fea
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/script_name_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#script_name" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['SCRIPT_NAME']" do
+ old_value, ENV['SCRIPT_NAME'] = ENV['SCRIPT_NAME'], "/path/to/script.rb"
+ begin
+ @cgi.script_name.should == "/path/to/script.rb"
+ ensure
+ ENV['SCRIPT_NAME'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/server_name_spec.rb b/spec/ruby/library/cgi/queryextension/server_name_spec.rb
new file mode 100644
index 0000000000..b12aaf8c5c
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/server_name_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#server_name" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['SERVER_NAME']" do
+ old_value, ENV['SERVER_NAME'] = ENV['SERVER_NAME'], "localhost"
+ begin
+ @cgi.server_name.should == "localhost"
+ ensure
+ ENV['SERVER_NAME'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/server_port_spec.rb b/spec/ruby/library/cgi/queryextension/server_port_spec.rb
new file mode 100644
index 0000000000..0e688a99bf
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/server_port_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#server_port" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['SERVER_PORT'] as Integer" do
+ old_value = ENV['SERVER_PORT']
+ begin
+ ENV['SERVER_PORT'] = nil
+ @cgi.server_port.should be_nil
+
+ ENV['SERVER_PORT'] = "3000"
+ @cgi.server_port.should eql(3000)
+ ensure
+ ENV['SERVER_PORT'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/server_protocol_spec.rb b/spec/ruby/library/cgi/queryextension/server_protocol_spec.rb
new file mode 100644
index 0000000000..f9dcf3c5b8
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/server_protocol_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#server_protocol" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['SERVER_PROTOCOL']" do
+ old_value, ENV['SERVER_PROTOCOL'] = ENV['SERVER_PROTOCOL'], "HTTP/1.1"
+ begin
+ @cgi.server_protocol.should == "HTTP/1.1"
+ ensure
+ ENV['SERVER_PROTOCOL'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/server_software_spec.rb b/spec/ruby/library/cgi/queryextension/server_software_spec.rb
new file mode 100644
index 0000000000..df349cdf2e
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/server_software_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#server_software" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['SERVER_SOFTWARE']" do
+ old_value, ENV['SERVER_SOFTWARE'] = ENV['SERVER_SOFTWARE'], "Server/1.0.0"
+ begin
+ @cgi.server_software.should == "Server/1.0.0"
+ ensure
+ ENV['SERVER_SOFTWARE'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/shared/has_key.rb b/spec/ruby/library/cgi/queryextension/shared/has_key.rb
new file mode 100644
index 0000000000..cfac5865fa
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/shared/has_key.rb
@@ -0,0 +1,19 @@
+describe :cgi_query_extension_has_key_p, shared: true do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ ENV['QUERY_STRING'], @old_query_string = "one=a&two=b", ENV['QUERY_STRING']
+
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ ENV['QUERY_STRING'] = @old_query_string
+ end
+
+ it "returns true when the passed key exists in the HTTP Query" do
+ @cgi.send(@method, "one").should be_true
+ @cgi.send(@method, "two").should be_true
+ @cgi.send(@method, "three").should be_false
+ end
+end
diff --git a/spec/ruby/library/cgi/queryextension/user_agent_spec.rb b/spec/ruby/library/cgi/queryextension/user_agent_spec.rb
new file mode 100644
index 0000000000..ec5ef187dd
--- /dev/null
+++ b/spec/ruby/library/cgi/queryextension/user_agent_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe "CGI::QueryExtension#user_agent" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+ it "returns ENV['HTTP_USER_AGENT']" do
+ old_value, ENV['HTTP_USER_AGENT'] = ENV['HTTP_USER_AGENT'], "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; de-de) AppleWebKit/527+ (KHTML, like Gecko) Version/3.1 Safari/525.13"
+ begin
+ @cgi.user_agent.should == "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; de-de) AppleWebKit/527+ (KHTML, like Gecko) Version/3.1 Safari/525.13"
+ ensure
+ ENV['HTTP_USER_AGENT'] = old_value
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/rfc1123_date_spec.rb b/spec/ruby/library/cgi/rfc1123_date_spec.rb
new file mode 100644
index 0000000000..d94c026d9f
--- /dev/null
+++ b/spec/ruby/library/cgi/rfc1123_date_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require 'cgi'
+
+describe "CGI.rfc1123_date when passsed Time" do
+ it "returns the passed Time formatted in RFC1123 ('Sat, 01 Dec 2007 15:56:42 GMT')" do
+ input = Time.at(1196524602)
+ expected = 'Sat, 01 Dec 2007 15:56:42 GMT'
+ CGI.rfc1123_date(input).should == expected
+ end
+end
diff --git a/spec/ruby/library/cgi/shared/http_header.rb b/spec/ruby/library/cgi/shared/http_header.rb
new file mode 100644
index 0000000000..b225b5925e
--- /dev/null
+++ b/spec/ruby/library/cgi/shared/http_header.rb
@@ -0,0 +1,112 @@
+require_relative '../../../spec_helper'
+require 'cgi'
+
+describe :cgi_http_header, shared: true do
+ describe "CGI#http_header when passed no arguments" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+
+ it "returns a HTTP header specifying the Content-Type as text/html" do
+ @cgi.send(@method).should == "Content-Type: text/html\r\n\r\n"
+ end
+
+ it "includes Cookies in the @output_cookies field" do
+ @cgi.instance_variable_set(:@output_cookies, ["multiple", "cookies"])
+ @cgi.send(@method).should == "Content-Type: text/html\r\nSet-Cookie: multiple\r\nSet-Cookie: cookies\r\n\r\n"
+ end
+ end
+
+ describe "CGI#http_header when passed String" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+
+ it "returns a HTTP header specifying the Content-Type as the passed String's content" do
+ @cgi.send(@method, "text/plain").should == "Content-Type: text/plain\r\n\r\n"
+ end
+
+ it "includes Cookies in the @output_cookies field" do
+ @cgi.instance_variable_set(:@output_cookies, ["multiple", "cookies"])
+ @cgi.send(@method, "text/plain").should == "Content-Type: text/plain\r\nSet-Cookie: multiple\r\nSet-Cookie: cookies\r\n\r\n"
+ end
+ end
+
+ describe "CGI#http_header when passed Hash" do
+ before :each do
+ ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD']
+ @cgi = CGI.new
+ end
+
+ after :each do
+ ENV['REQUEST_METHOD'] = @old_request_method
+ end
+
+
+ it "returns a HTTP header based on the Hash's key/value pairs" do
+ header = @cgi.send(@method, "type" => "text/plain")
+ header.should == "Content-Type: text/plain\r\n\r\n"
+
+ header = @cgi.send(@method, "type" => "text/plain", "charset" => "UTF-8")
+ header.should == "Content-Type: text/plain; charset=UTF-8\r\n\r\n"
+
+ header = @cgi.send(@method, "nph" => true)
+ header.should include("HTTP/1.0 200 OK\r\n")
+ header.should include("Date: ")
+ header.should include("Server: ")
+ header.should include("Connection: close\r\n")
+ header.should include("Content-Type: text/html\r\n")
+
+ header = @cgi.send(@method, "status" => "OK")
+ header.should == "Status: 200 OK\r\nContent-Type: text/html\r\n\r\n"
+
+ header = @cgi.send(@method, "status" => "PARTIAL_CONTENT")
+ header.should == "Status: 206 Partial Content\r\nContent-Type: text/html\r\n\r\n"
+
+ header = @cgi.send(@method, "status" => "MULTIPLE_CHOICES")
+ header.should == "Status: 300 Multiple Choices\r\nContent-Type: text/html\r\n\r\n"
+
+ header = @cgi.send(@method, "server" => "Server Software used")
+ header.should == "Server: Server Software used\r\nContent-Type: text/html\r\n\r\n"
+
+ header = @cgi.send(@method, "connection" => "connection type")
+ header.should == "Connection: connection type\r\nContent-Type: text/html\r\n\r\n"
+
+ header = @cgi.send(@method, "length" => 103)
+ header.should == "Content-Type: text/html\r\nContent-Length: 103\r\n\r\n"
+
+ header = @cgi.send(@method, "language" => "ja")
+ header.should == "Content-Type: text/html\r\nContent-Language: ja\r\n\r\n"
+
+ header = @cgi.send(@method, "expires" => Time.at(0))
+ header.should == "Content-Type: text/html\r\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\r\n\r\n"
+
+ header = @cgi.send(@method, "cookie" => "my cookie's content")
+ header.should == "Content-Type: text/html\r\nSet-Cookie: my cookie's content\r\n\r\n"
+
+ header = @cgi.send(@method, "cookie" => ["multiple", "cookies"])
+ header.should == "Content-Type: text/html\r\nSet-Cookie: multiple\r\nSet-Cookie: cookies\r\n\r\n"
+ end
+
+ it "includes Cookies in the @output_cookies field" do
+ @cgi.instance_variable_set(:@output_cookies, ["multiple", "cookies"])
+ @cgi.send(@method, {}).should == "Content-Type: text/html\r\nSet-Cookie: multiple\r\nSet-Cookie: cookies\r\n\r\n"
+ end
+
+ it "returns a HTTP header specifying the Content-Type as text/html when passed an empty Hash" do
+ @cgi.send(@method, {}).should == "Content-Type: text/html\r\n\r\n"
+ end
+ end
+end
diff --git a/spec/ruby/library/cgi/unescapeElement_spec.rb b/spec/ruby/library/cgi/unescapeElement_spec.rb
new file mode 100644
index 0000000000..ae4d50b076
--- /dev/null
+++ b/spec/ruby/library/cgi/unescapeElement_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require 'cgi'
+
+describe "CGI.unescapeElement when passed String, elements, ..." do
+ it "unescapes only the tags of the passed elements in the passed String" do
+ res = CGI.unescapeElement("&lt;BR&gt;&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt;", "A", "IMG")
+ res.should == '&lt;BR&gt;<A HREF="url"></A>'
+
+ res = CGI.unescapeElement('&lt;BR&gt;&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt;', ["A", "IMG"])
+ res.should == '&lt;BR&gt;<A HREF="url"></A>'
+ end
+
+ it "is case-insensitive" do
+ res = CGI.unescapeElement("&lt;BR&gt;&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt;", "a", "img")
+ res.should == '&lt;BR&gt;<A HREF="url"></A>'
+
+ res = CGI.unescapeElement("&lt;br&gt;&lt;a href=&quot;url&quot;&gt;&lt;/a&gt;", "A", "IMG")
+ res.should == '&lt;br&gt;<a href="url"></a>'
+ end
+end
diff --git a/spec/ruby/library/cgi/unescapeHTML_spec.rb b/spec/ruby/library/cgi/unescapeHTML_spec.rb
new file mode 100644
index 0000000000..46387d4f01
--- /dev/null
+++ b/spec/ruby/library/cgi/unescapeHTML_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+require 'cgi'
+
+describe "CGI.unescapeHTML" do
+ it "unescapes '&amp; &lt; &gt; &quot;' to '& < > \"'" do
+ input = '&amp; &lt; &gt; &quot;'
+ expected = '& < > "'
+ CGI.unescapeHTML(input).should == expected
+ end
+
+ it "doesn't unescape other html entities such as '&copy;' or '&heart'" do
+ input = '&copy;&heart;'
+ expected = input
+ CGI.unescapeHTML(input).should == expected
+ end
+
+ it "unescapes '&#99' format entities" do
+ input = '&#34;&#38;&#39;&#60;&#62;'
+ expected = '"&\'<>'
+ CGI.unescapeHTML(input).should == expected
+ end
+
+ it "unescapes '&#x9999' format entities" do
+ input = '&#x0022;&#x0026;&#x0027;&#x003c;&#x003E;'
+ expected = '"&\'<>'
+ CGI.unescapeHTML(input).should == expected
+ end
+
+ it "leaves invalid formatted strings" do
+ input = '&&lt;&amp&gt;&quot&abcdefghijklmn'
+ expected = '&<&amp>&quot&abcdefghijklmn'
+ CGI.unescapeHTML(input).should == expected
+ end
+
+ it "leaves partial invalid &# at end of string" do
+ input = "fooooooo&#"
+ CGI.unescapeHTML(input).should == input
+ end
+end
diff --git a/spec/ruby/library/cgi/unescape_spec.rb b/spec/ruby/library/cgi/unescape_spec.rb
new file mode 100644
index 0000000000..db4834e7e8
--- /dev/null
+++ b/spec/ruby/library/cgi/unescape_spec.rb
@@ -0,0 +1,15 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require 'cgi'
+
+describe "CGI.unescape" do
+ it "url-decodes the passed argument" do
+ input = "+%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7E"
+ expected = " !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
+ CGI.unescape(input).should == expected
+
+ input = 'http%3A%2F%2Fja.wikipedia.org%2Fwiki%2F%E3%83%AD%E3%83%A0%E3%82%B9%E3%82%AB%E3%83%BB%E3%83%91%E3%83%AD%E3%83%BB%E3%82%A6%E3%83%AB%E3%83%BB%E3%83%A9%E3%83%94%E3%83%A5%E3%82%BF'
+ expected = "http://ja.wikipedia.org/wiki/\343\203\255\343\203\240\343\202\271\343\202\253\343\203\273\343\203\221\343\203\255\343\203\273\343\202\246\343\203\253\343\203\273\343\203\251\343\203\224\343\203\245\343\202\277"
+ CGI.unescape(input).should == expected
+ end
+end
diff --git a/spec/ruby/library/complex/math/acos_spec.rb b/spec/ruby/library/complex/math/acos_spec.rb
new file mode 100644
index 0000000000..c7b2362a2c
--- /dev/null
+++ b/spec/ruby/library/complex/math/acos_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/acos'
+
+describe "Math#acos" do
+ it_behaves_like :complex_math_acos, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:acos)
+ end
+end
+
+describe "Math.acos" do
+ it_behaves_like :complex_math_acos, :_, CMath
+end
diff --git a/spec/ruby/library/complex/math/acosh_spec.rb b/spec/ruby/library/complex/math/acosh_spec.rb
new file mode 100644
index 0000000000..33adb73b64
--- /dev/null
+++ b/spec/ruby/library/complex/math/acosh_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/acosh'
+
+describe "Math#acosh" do
+ it_behaves_like :complex_math_acosh, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:acosh)
+ end
+end
+
+describe "Math.acosh" do
+ it_behaves_like :complex_math_acosh, :_, CMath
+end
diff --git a/spec/ruby/library/complex/math/asin_spec.rb b/spec/ruby/library/complex/math/asin_spec.rb
new file mode 100644
index 0000000000..f0647bbc14
--- /dev/null
+++ b/spec/ruby/library/complex/math/asin_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/asin'
+
+describe "Math#asin" do
+ it_behaves_like :complex_math_asin, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:asin)
+ end
+end
+
+describe "Math.asin" do
+ it_behaves_like :complex_math_asin, :_, CMath
+end
diff --git a/spec/ruby/library/complex/math/asinh_spec.rb b/spec/ruby/library/complex/math/asinh_spec.rb
new file mode 100644
index 0000000000..ef93a9dc22
--- /dev/null
+++ b/spec/ruby/library/complex/math/asinh_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/asinh'
+
+describe "Math#asinh" do
+ it_behaves_like :complex_math_asinh, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:asinh)
+ end
+end
+
+describe "Math.asinh" do
+ it_behaves_like :complex_math_asinh, :_, CMath
+end
diff --git a/spec/ruby/library/complex/math/atan2_spec.rb b/spec/ruby/library/complex/math/atan2_spec.rb
new file mode 100644
index 0000000000..46874f89e4
--- /dev/null
+++ b/spec/ruby/library/complex/math/atan2_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/atan2'
+
+describe "Math#atan2" do
+ it_behaves_like :complex_math_atan2, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:atan2)
+ end
+end
+
+describe "Math.atan2" do
+ it_behaves_like :complex_math_atan2, :_, CMath
+end
diff --git a/spec/ruby/library/complex/math/atan_spec.rb b/spec/ruby/library/complex/math/atan_spec.rb
new file mode 100644
index 0000000000..e60c088b5e
--- /dev/null
+++ b/spec/ruby/library/complex/math/atan_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/atan'
+
+describe "Math#atan" do
+ it_behaves_like :complex_math_atan, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:atan)
+ end
+end
+
+describe "Math.atan" do
+ it_behaves_like :complex_math_atan, :_, CMath
+end
diff --git a/spec/ruby/library/complex/math/atanh_spec.rb b/spec/ruby/library/complex/math/atanh_spec.rb
new file mode 100644
index 0000000000..b8eaec3a09
--- /dev/null
+++ b/spec/ruby/library/complex/math/atanh_spec.rb
@@ -0,0 +1,17 @@
+require 'complex'
+require_relative '../../../spec_helper'
+require_relative '../../../fixtures/math/common'
+require_relative '../../../shared/math/atanh'
+require_relative 'shared/atanh'
+
+describe "Math#atanh" do
+ it_behaves_like :math_atanh_base, :atanh, IncludesMath.new
+ it_behaves_like :complex_math_atanh_complex, :atanh, IncludesMath.new
+
+ it_behaves_like :math_atanh_private, :atanh, IncludesMath.new
+end
+
+describe "Math.atanh" do
+ it_behaves_like :math_atanh_base, :atanh, CMath
+ it_behaves_like :complex_math_atanh_complex, :atanh, CMath
+end
diff --git a/spec/ruby/library/complex/math/cos_spec.rb b/spec/ruby/library/complex/math/cos_spec.rb
new file mode 100644
index 0000000000..847acb7fa2
--- /dev/null
+++ b/spec/ruby/library/complex/math/cos_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/cos'
+
+describe "Math#cos" do
+ it_behaves_like :complex_math_cos, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:cos)
+ end
+end
+
+describe "Math.cos" do
+ it_behaves_like :complex_math_cos, :_, CMath
+end
diff --git a/spec/ruby/library/complex/math/cosh_spec.rb b/spec/ruby/library/complex/math/cosh_spec.rb
new file mode 100644
index 0000000000..c0a87d4865
--- /dev/null
+++ b/spec/ruby/library/complex/math/cosh_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/cosh'
+
+describe "Math#cosh" do
+ it_behaves_like :complex_math_cosh, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:cosh)
+ end
+end
+
+describe "Math.cosh" do
+ it_behaves_like :complex_math_cosh, :_, CMath
+end
diff --git a/spec/ruby/library/complex/math/exp_spec.rb b/spec/ruby/library/complex/math/exp_spec.rb
new file mode 100644
index 0000000000..e1e51f7948
--- /dev/null
+++ b/spec/ruby/library/complex/math/exp_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/exp'
+
+describe "Math#exp" do
+ it_behaves_like :complex_math_exp, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:exp)
+ end
+end
+
+describe "Math.exp" do
+ it_behaves_like :complex_math_exp, :_, CMath
+end
diff --git a/spec/ruby/library/complex/math/fixtures/classes.rb b/spec/ruby/library/complex/math/fixtures/classes.rb
new file mode 100644
index 0000000000..443c1a9ace
--- /dev/null
+++ b/spec/ruby/library/complex/math/fixtures/classes.rb
@@ -0,0 +1,4 @@
+require 'cmath'
+class IncludesMath
+ include CMath
+end
diff --git a/spec/ruby/library/complex/math/log10_spec.rb b/spec/ruby/library/complex/math/log10_spec.rb
new file mode 100644
index 0000000000..295189ae53
--- /dev/null
+++ b/spec/ruby/library/complex/math/log10_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/log10'
+
+describe "Math#log10" do
+ it_behaves_like :complex_math_log10, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:log10)
+ end
+end
+
+describe "Math.log10" do
+ it_behaves_like :complex_math_log10, :_, CMath
+end
diff --git a/spec/ruby/library/complex/math/log_spec.rb b/spec/ruby/library/complex/math/log_spec.rb
new file mode 100644
index 0000000000..6d80d045c9
--- /dev/null
+++ b/spec/ruby/library/complex/math/log_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/log'
+
+describe "Math#log" do
+ it_behaves_like :complex_math_log, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:log)
+ end
+end
+
+describe "Math.log" do
+ it_behaves_like :complex_math_log, :_, CMath
+end
diff --git a/spec/ruby/library/complex/math/shared/acos.rb b/spec/ruby/library/complex/math/shared/acos.rb
new file mode 100644
index 0000000000..61be31571f
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/acos.rb
@@ -0,0 +1,41 @@
+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
+ lambda { @object.send(:acos!, Complex(4, 5)) }.should raise_error(TypeError)
+ end
+
+ it "raises an Errno::EDOM for numbers greater than 1.0" do
+ lambda { @object.send(:acos!, 1.0001) }.should raise_error(Errno::EDOM)
+ end
+
+ it "raises an Errno::EDOM for numbers less than -1.0" do
+ lambda { @object.send(:acos!, -1.0001) }.should raise_error(Errno::EDOM)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/acosh.rb b/spec/ruby/library/complex/math/shared/acosh.rb
new file mode 100644
index 0000000000..a2cd887f95
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/acosh.rb
@@ -0,0 +1,37 @@
+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
+ lambda { @object.send(:acosh!, 1.0 - TOLERANCE) }.should raise_error(Errno::EDOM)
+ lambda { @object.send(:acosh!, 0) }.should raise_error(Errno::EDOM)
+ lambda { @object.send(:acosh!, -1.0) }.should raise_error(Errno::EDOM)
+ end
+
+ it "raises a TypeError when passed a Complex number" do
+ lambda { @object.send(:acosh!, Complex(4, 5)) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/asin.rb b/spec/ruby/library/complex/math/shared/asin.rb
new file mode 100644
index 0000000000..1705745210
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/asin.rb
@@ -0,0 +1,47 @@
+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
+ lambda { @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
+ lambda { @object.send(:asin!, -1.0001) }.should raise_error( Errno::EDOM)
+ end
+
+ it "raises a TypeError when passed a Complex number" do
+ lambda { @object.send(:asin!, Complex(4, 5)) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/asinh.rb b/spec/ruby/library/complex/math/shared/asinh.rb
new file mode 100644
index 0000000000..b4256e7475
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/asinh.rb
@@ -0,0 +1,32 @@
+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
+ lambda { @object.send(:asinh!, Complex(4, 5)) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/atan.rb b/spec/ruby/library/complex/math/shared/atan.rb
new file mode 100644
index 0000000000..46c1897407
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/atan.rb
@@ -0,0 +1,32 @@
+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
+ lambda { @object.send(:atan!, Complex(4, 5)) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/atan2.rb b/spec/ruby/library/complex/math/shared/atan2.rb
new file mode 100644
index 0000000000..3305d695c5
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/atan2.rb
@@ -0,0 +1,34 @@
+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
+ lambda { @object.send(:atan2!, Complex(4, 5), Complex(4, 5)) }.should raise_error(TypeError)
+ lambda { @object.send(:atan2!, 4, Complex(4, 5)) }.should raise_error(TypeError)
+ lambda { @object.send(:atan2!, Complex(4, 5), 5) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/atanh.rb b/spec/ruby/library/complex/math/shared/atanh.rb
new file mode 100644
index 0000000000..ebd4d7c223
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/atanh.rb
@@ -0,0 +1,30 @@
+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
+ lambda { @object.send(:atanh!, Complex(4, 5)) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/cos.rb b/spec/ruby/library/complex/math/shared/cos.rb
new file mode 100644
index 0000000000..ddd8512231
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/cos.rb
@@ -0,0 +1,30 @@
+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
+ lambda { @object.send(:cos!, Complex(3, 4)) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/cosh.rb b/spec/ruby/library/complex/math/shared/cosh.rb
new file mode 100644
index 0000000000..a56a655cd5
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/cosh.rb
@@ -0,0 +1,28 @@
+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
+ lambda { @object.send(:cosh!, Complex(4, 5)) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/exp.rb b/spec/ruby/library/complex/math/shared/exp.rb
new file mode 100644
index 0000000000..91ab5739be
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/exp.rb
@@ -0,0 +1,28 @@
+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
+ lambda { @object.send(:exp!, Complex(1, 3)) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/log.rb b/spec/ruby/library/complex/math/shared/log.rb
new file mode 100644
index 0000000000..f3aad0d66a
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/log.rb
@@ -0,0 +1,39 @@
+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
+ lambda { @object.send(:log!, -10) }.should raise_error(Errno::EDOM)
+ end
+
+ it "raises a TypeError when passed a Complex number" do
+ lambda { @object.send(:log!, Complex(4, 5)) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/log10.rb b/spec/ruby/library/complex/math/shared/log10.rb
new file mode 100644
index 0000000000..a2cf5344bf
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/log10.rb
@@ -0,0 +1,41 @@
+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
+ lambda { @object.send(:log10!, -10) }.should raise_error(Errno::EDOM)
+ end
+
+ it "raises a TypeError when passed a Complex number" do
+ lambda { @object.send(:log10!, Complex(4, 5)) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/sin.rb b/spec/ruby/library/complex/math/shared/sin.rb
new file mode 100644
index 0000000000..418ba5d810
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/sin.rb
@@ -0,0 +1,30 @@
+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
+ lambda { @object.send(:sin!, Complex(4, 5)) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/sinh.rb b/spec/ruby/library/complex/math/shared/sinh.rb
new file mode 100644
index 0000000000..bd85bd0799
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/sinh.rb
@@ -0,0 +1,28 @@
+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
+ lambda { @object.send(:sinh!, Complex(4, 5)) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/sqrt.rb b/spec/ruby/library/complex/math/shared/sqrt.rb
new file mode 100644
index 0000000000..b6a289c3f9
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/sqrt.rb
@@ -0,0 +1,34 @@
+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
+ lambda { @object.send(:sqrt!, -4) }.should raise_error(Errno::EDOM)
+ lambda { @object.send(:sqrt!, -19.36) }.should raise_error(Errno::EDOM)
+ end
+
+ it "raises a TypeError when passed a Complex number" do
+ lambda { @object.send(:sqrt!, Complex(4, 5)) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/tan.rb b/spec/ruby/library/complex/math/shared/tan.rb
new file mode 100644
index 0000000000..df7a6ff19b
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/tan.rb
@@ -0,0 +1,28 @@
+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
+ lambda { @object.send(:tan!, Complex(4, 5)) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/shared/tanh.rb b/spec/ruby/library/complex/math/shared/tanh.rb
new file mode 100644
index 0000000000..7bf07f9a3f
--- /dev/null
+++ b/spec/ruby/library/complex/math/shared/tanh.rb
@@ -0,0 +1,32 @@
+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
+ lambda { @object.send(:tanh!, Complex(4, 5)) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/complex/math/sin_spec.rb b/spec/ruby/library/complex/math/sin_spec.rb
new file mode 100644
index 0000000000..6046cd29c3
--- /dev/null
+++ b/spec/ruby/library/complex/math/sin_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/sin'
+
+describe "Math#sin" do
+ it_behaves_like :complex_math_sin, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:sin)
+ end
+end
+
+describe "Math.sin" do
+ it_behaves_like :complex_math_sin, :_, CMath
+end
diff --git a/spec/ruby/library/complex/math/sinh_spec.rb b/spec/ruby/library/complex/math/sinh_spec.rb
new file mode 100644
index 0000000000..f5f98778b2
--- /dev/null
+++ b/spec/ruby/library/complex/math/sinh_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/sinh'
+
+describe "Math#sinh" do
+ it_behaves_like :complex_math_sinh, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:sinh)
+ end
+end
+
+describe "Math.sinh" do
+ it_behaves_like :complex_math_sinh, :_, CMath
+end
diff --git a/spec/ruby/library/complex/math/sqrt_spec.rb b/spec/ruby/library/complex/math/sqrt_spec.rb
new file mode 100644
index 0000000000..8b7050c108
--- /dev/null
+++ b/spec/ruby/library/complex/math/sqrt_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/sqrt'
+
+describe "Math#sqrt" do
+ it_behaves_like :complex_math_sqrt, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:sqrt)
+ end
+end
+
+describe "Math.sqrt" do
+ it_behaves_like :complex_math_sqrt, :_, CMath
+end
diff --git a/spec/ruby/library/complex/math/tan_spec.rb b/spec/ruby/library/complex/math/tan_spec.rb
new file mode 100644
index 0000000000..b0f18bb515
--- /dev/null
+++ b/spec/ruby/library/complex/math/tan_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/tan'
+
+describe "Math#tan" do
+ it_behaves_like :complex_math_tan, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:tan)
+ end
+end
+
+describe "Math.tan" do
+ it_behaves_like :complex_math_tan, :_, CMath
+end
diff --git a/spec/ruby/library/complex/math/tanh_spec.rb b/spec/ruby/library/complex/math/tanh_spec.rb
new file mode 100644
index 0000000000..469bba7554
--- /dev/null
+++ b/spec/ruby/library/complex/math/tanh_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'complex'
+require_relative 'shared/tanh'
+
+describe "Math#tanh" do
+ it_behaves_like :complex_math_tanh, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:tanh)
+ end
+end
+
+describe "Math.tanh" do
+ it_behaves_like :complex_math_tanh, :_, CMath
+end
diff --git a/spec/ruby/library/complex/numeric/im_spec.rb b/spec/ruby/library/complex/numeric/im_spec.rb
new file mode 100644
index 0000000000..17f4a0476b
--- /dev/null
+++ b/spec/ruby/library/complex/numeric/im_spec.rb
@@ -0,0 +1,3 @@
+require_relative '../../../spec_helper'
+
+require 'complex'
diff --git a/spec/ruby/library/conditionvariable/broadcast_spec.rb b/spec/ruby/library/conditionvariable/broadcast_spec.rb
new file mode 100644
index 0000000000..a31a0443bd
--- /dev/null
+++ b/spec/ruby/library/conditionvariable/broadcast_spec.rb
@@ -0,0 +1,67 @@
+require_relative '../../spec_helper'
+require 'thread'
+
+describe "ConditionVariable#broadcast" do
+ it "returns self if nothing to broadcast to" do
+ cv = ConditionVariable.new
+ cv.broadcast.should == cv
+ end
+
+ it "returns self if something is waiting for a broadcast" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ in_synchronize = false
+
+ th = Thread.new do
+ m.synchronize do
+ in_synchronize = true
+ cv.wait(m)
+ end
+ end
+
+ # wait for m to acquire the mutex
+ Thread.pass until in_synchronize
+ # wait until th is sleeping (ie waiting)
+ Thread.pass while th.status and th.status != "sleep"
+
+ m.synchronize { cv.broadcast }.should == cv
+
+ th.join
+ end
+
+ it "releases all threads waiting in line for this resource" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ threads = []
+ r1 = []
+ r2 = []
+
+ # large number to attempt to cause race conditions
+ 100.times do |i|
+ threads << Thread.new(i) do |tid|
+ m.synchronize do
+ r1 << tid
+ cv.wait(m)
+ r2 << tid
+ end
+ end
+ end
+
+ # wait for all threads to acquire the mutex the first time
+ Thread.pass until m.synchronize { r1.size == threads.size }
+ # wait until all threads are sleeping (ie waiting)
+ Thread.pass until threads.all? {|th| th.status == "sleep" }
+
+ r2.should be_empty
+ m.synchronize do
+ cv.broadcast
+ end
+
+ threads.each {|t| t.join }
+
+ # ensure that all threads that enter cv.wait are released
+ r2.sort.should == r1.sort
+ # note that order is not specified as broadcast results in a race
+ # condition on regaining the lock m
+ end
+end
diff --git a/spec/ruby/library/conditionvariable/marshal_dump_spec.rb b/spec/ruby/library/conditionvariable/marshal_dump_spec.rb
new file mode 100644
index 0000000000..f951a13e28
--- /dev/null
+++ b/spec/ruby/library/conditionvariable/marshal_dump_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require 'thread'
+
+describe "ConditionVariable#marshal_dump" do
+ it "raises a TypeError" do
+ cv = ConditionVariable.new
+ -> { cv.marshal_dump }.should raise_error(TypeError, /can't dump/)
+ end
+end
diff --git a/spec/ruby/library/conditionvariable/signal_spec.rb b/spec/ruby/library/conditionvariable/signal_spec.rb
new file mode 100644
index 0000000000..0dafe8527f
--- /dev/null
+++ b/spec/ruby/library/conditionvariable/signal_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../../spec_helper'
+require 'thread'
+
+describe "ConditionVariable#signal" do
+ it "returns self if nothing to signal" do
+ cv = ConditionVariable.new
+ cv.signal.should == cv
+ end
+
+ it "returns self if something is waiting for a signal" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ in_synchronize = false
+
+ th = Thread.new do
+ m.synchronize do
+ in_synchronize = true
+ cv.wait(m)
+ end
+ end
+
+ # wait for m to acquire the mutex
+ Thread.pass until in_synchronize
+ # wait until th is sleeping (ie waiting)
+ Thread.pass while th.status and th.status != "sleep"
+
+ m.synchronize { cv.signal }.should == cv
+
+ th.join
+ end
+
+ it "releases the first thread waiting in line for this resource" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ threads = []
+ r1 = []
+ r2 = []
+
+ # large number to attempt to cause race conditions
+ 100.times do |i|
+ threads << Thread.new(i) do |tid|
+ m.synchronize do
+ r1 << tid
+ cv.wait(m)
+ r2 << tid
+ end
+ end
+ end
+
+ # wait for all threads to acquire the mutex the first time
+ Thread.pass until m.synchronize { r1.size == threads.size }
+ # wait until all threads are sleeping (ie waiting)
+ Thread.pass until threads.all? {|th| th.status == "sleep" }
+
+ r2.should be_empty
+ 100.times do |i|
+ m.synchronize do
+ cv.signal
+ end
+ Thread.pass until r2.size == i+1
+ end
+
+ threads.each {|t| t.join }
+
+ # ensure that all the threads that went into the cv.wait are
+ # released in the same order
+ r2.should == r1
+ end
+end
diff --git a/spec/ruby/library/conditionvariable/wait_spec.rb b/spec/ruby/library/conditionvariable/wait_spec.rb
new file mode 100644
index 0000000000..8bdb7829d3
--- /dev/null
+++ b/spec/ruby/library/conditionvariable/wait_spec.rb
@@ -0,0 +1,123 @@
+require_relative '../../spec_helper'
+require 'thread'
+
+describe "ConditionVariable#wait" do
+ it "returns self" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ in_synchronize = false
+
+ th = Thread.new do
+ m.synchronize do
+ in_synchronize = true
+ cv.wait(m).should == cv
+ end
+ end
+
+ # wait for m to acquire the mutex
+ Thread.pass until in_synchronize
+ # wait until th is sleeping (ie waiting)
+ Thread.pass while th.status and th.status != "sleep"
+
+ m.synchronize { cv.signal }
+ th.join
+ end
+
+ it "reacquires the lock even if the thread is killed" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ in_synchronize = false
+ owned = nil
+
+ th = Thread.new do
+ m.synchronize do
+ in_synchronize = true
+ begin
+ cv.wait(m)
+ ensure
+ owned = m.owned?
+ $stderr.puts "\nThe Thread doesn't own the Mutex!" unless owned
+ end
+ end
+ end
+
+ # wait for m to acquire the mutex
+ Thread.pass until in_synchronize
+ # wait until th is sleeping (ie waiting)
+ Thread.pass while th.status and th.status != "sleep"
+
+ th.kill
+ th.join
+
+ owned.should == true
+ end
+
+ ruby_bug '#14999', ''...'2.5' do
+ it "reacquires the lock even if the thread is killed after being signaled" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ in_synchronize = false
+ owned = nil
+
+ th = Thread.new do
+ m.synchronize do
+ in_synchronize = true
+ begin
+ cv.wait(m)
+ ensure
+ owned = m.owned?
+ $stderr.puts "\nThe Thread doesn't own the Mutex!" unless owned
+ end
+ end
+ end
+
+ # wait for m to acquire the mutex
+ Thread.pass until in_synchronize
+ # wait until th is sleeping (ie waiting)
+ Thread.pass while th.status and th.status != "sleep"
+
+ m.synchronize {
+ cv.signal
+ # Wait that the thread is blocked on acquiring the Mutex
+ sleep 0.001
+ # Kill the thread, yet the thread should first acquire the Mutex before going on
+ th.kill
+ }
+
+ th.join
+ owned.should == true
+ end
+ end
+
+ it "supports multiple Threads waiting on the same ConditionVariable and Mutex" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ n_threads = 4
+ events = []
+
+ threads = n_threads.times.map {
+ Thread.new {
+ m.synchronize {
+ events << :t_in_synchronize
+ cv.wait(m)
+ }
+ }
+ }
+
+ Thread.pass until m.synchronize { events.size } == n_threads
+ Thread.pass while threads.any? { |th| th.status and th.status != "sleep" }
+ m.synchronize do
+ threads.each { |t|
+ # Cause interactions with the waiting threads.
+ # On TruffleRuby, this causes a safepoint which has interesting
+ # interactions with the ConditionVariable.
+ bt = t.backtrace
+ bt.should be_kind_of(Array)
+ bt.size.should >= 2
+ }
+ end
+
+ cv.broadcast
+ threads.each(&:join)
+ end
+end
diff --git a/spec/ruby/library/coverage/fixtures/second_class.rb b/spec/ruby/library/coverage/fixtures/second_class.rb
new file mode 100644
index 0000000000..0318ac26ff
--- /dev/null
+++ b/spec/ruby/library/coverage/fixtures/second_class.rb
@@ -0,0 +1,5 @@
+class SecondClass
+ def some_method
+ 42
+ end
+end
diff --git a/spec/ruby/library/coverage/fixtures/some_class.rb b/spec/ruby/library/coverage/fixtures/some_class.rb
new file mode 100644
index 0000000000..52629f0332
--- /dev/null
+++ b/spec/ruby/library/coverage/fixtures/some_class.rb
@@ -0,0 +1,16 @@
+
+#Class comment
+class SomeClass
+
+ # Method comment
+ def some_method
+
+ # Inline method comment
+ actual_code = true
+
+ end
+
+end
+
+# Trailing comment and extra blank line
+
diff --git a/spec/ruby/library/coverage/fixtures/spec_helper.rb b/spec/ruby/library/coverage/fixtures/spec_helper.rb
new file mode 100644
index 0000000000..19094e5c36
--- /dev/null
+++ b/spec/ruby/library/coverage/fixtures/spec_helper.rb
@@ -0,0 +1,11 @@
+module CoverageSpecs
+ # Clear old results from the result hash
+ # https://bugs.ruby-lang.org/issues/12220
+ def self.filtered_result
+ result = Coverage.result
+ ruby_version_is ""..."2.4" do
+ result = result.reject { |_k, v| v.empty? }
+ end
+ result
+ end
+end
diff --git a/spec/ruby/library/coverage/fixtures/start_coverage.rb b/spec/ruby/library/coverage/fixtures/start_coverage.rb
new file mode 100644
index 0000000000..8a0c56c50a
--- /dev/null
+++ b/spec/ruby/library/coverage/fixtures/start_coverage.rb
@@ -0,0 +1,3 @@
+2 + 2
+Coverage.start
+1 + 1
diff --git a/spec/ruby/library/coverage/peek_result_spec.rb b/spec/ruby/library/coverage/peek_result_spec.rb
new file mode 100644
index 0000000000..897fc4d978
--- /dev/null
+++ b/spec/ruby/library/coverage/peek_result_spec.rb
@@ -0,0 +1,65 @@
+require_relative '../../spec_helper'
+require fixture __FILE__, 'spec_helper'
+require 'coverage'
+
+describe 'Coverage.peek_result' do
+ before :all do
+ @class_file = fixture __FILE__, 'some_class.rb'
+ @second_class_file = fixture __FILE__, 'second_class.rb'
+ end
+
+ after :each do
+ $LOADED_FEATURES.delete(@class_file)
+ $LOADED_FEATURES.delete(@second_class_file)
+ end
+
+ it 'returns the result so far' do
+ Coverage.start
+ require @class_file.chomp('.rb')
+ result = Coverage.peek_result
+ Coverage.result
+
+ result.should == {
+ @class_file => [
+ nil, nil, 1, nil, nil, 1, nil, nil, 0, nil, nil, nil, nil, nil, nil, nil
+ ]
+ }
+ end
+
+ it 'immediate second call returns same result' do
+ Coverage.start
+ require @class_file.chomp('.rb')
+ result1 = Coverage.peek_result
+ result2 = Coverage.peek_result
+ Coverage.result
+
+ result2.should == result1
+ end
+
+ it 'second call after require returns accumulated result' do
+ Coverage.start
+ require @class_file.chomp('.rb')
+ Coverage.peek_result
+ require @second_class_file.chomp('.rb')
+ result = Coverage.peek_result
+ Coverage.result
+
+ result.should == {
+ @class_file => [
+ nil, nil, 1, nil, nil, 1, nil, nil, 0, nil, nil, nil, nil, nil, nil, nil
+ ],
+ @second_class_file => [
+ 1, 1, 0, nil, nil
+ ]
+ }
+ end
+
+ it 'call right before Coverage.result should give equal result' do
+ Coverage.start
+ require @class_file.chomp('.rb')
+ result1 = Coverage.peek_result
+ result2 = Coverage.result
+
+ result1.should == result2
+ end
+end
diff --git a/spec/ruby/library/coverage/result_spec.rb b/spec/ruby/library/coverage/result_spec.rb
new file mode 100644
index 0000000000..f964c9457a
--- /dev/null
+++ b/spec/ruby/library/coverage/result_spec.rb
@@ -0,0 +1,78 @@
+require_relative '../../spec_helper'
+require fixture __FILE__, 'spec_helper'
+require 'coverage'
+
+describe 'Coverage.result' do
+ before :all do
+ @class_file = fixture __FILE__, 'some_class.rb'
+ @config_file = fixture __FILE__, 'start_coverage.rb'
+ end
+
+ after :each do
+ $LOADED_FEATURES.delete(@class_file)
+ $LOADED_FEATURES.delete(@config_file)
+ end
+
+ it 'gives the covered files as a hash with arrays of count or nil' do
+ Coverage.start
+ require @class_file.chomp('.rb')
+ result = CoverageSpecs.filtered_result
+
+ result.should == {
+ @class_file => [
+ nil, nil, 1, nil, nil, 1, nil, nil, 0, nil, nil, nil, nil, nil, nil, nil
+ ]
+ }
+ end
+
+ it 'no requires/loads should give empty hash' do
+ Coverage.start
+ result = CoverageSpecs.filtered_result
+
+ result.should == {}
+ end
+
+ it 'second call should give exception' do
+ Coverage.start
+ require @class_file.chomp('.rb')
+ Coverage.result
+ -> { Coverage.result }
+ .should raise_error(RuntimeError, 'coverage measurement is not enabled')
+ end
+
+ it 'second run should give same result' do
+ Coverage.start
+ load @class_file
+ result1 = CoverageSpecs.filtered_result
+
+ Coverage.start
+ load @class_file
+ result2 = CoverageSpecs.filtered_result
+
+ result2.should == result1
+ end
+
+ it 'second run without load/require should give empty hash' do
+ Coverage.start
+ require @class_file.chomp('.rb')
+ Coverage.result
+
+ Coverage.start
+ result = CoverageSpecs.filtered_result
+
+ result.should == {}
+ end
+
+ it 'second Coverage.start does nothing' do
+ Coverage.start
+ require @config_file.chomp('.rb')
+ result = CoverageSpecs.filtered_result
+
+ result.should == { @config_file => [1, 1, 1] }
+ end
+
+ it 'does not include the file starting coverage since it is not tracked' do
+ require @config_file.chomp('.rb')
+ CoverageSpecs.filtered_result.should_not include(@config_file)
+ end
+end
diff --git a/spec/ruby/library/coverage/start_spec.rb b/spec/ruby/library/coverage/start_spec.rb
new file mode 100644
index 0000000000..ddfad8b47c
--- /dev/null
+++ b/spec/ruby/library/coverage/start_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'coverage'
+
+describe 'Coverage.start' do
+ it 'needs to be reviewed for spec completeness'
+end
diff --git a/spec/ruby/library/csv/basicwriter/close_on_terminate_spec.rb b/spec/ruby/library/csv/basicwriter/close_on_terminate_spec.rb
new file mode 100644
index 0000000000..599e640624
--- /dev/null
+++ b/spec/ruby/library/csv/basicwriter/close_on_terminate_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::BasicWriter#close_on_terminate" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/basicwriter/initialize_spec.rb b/spec/ruby/library/csv/basicwriter/initialize_spec.rb
new file mode 100644
index 0000000000..2c13c93f2e
--- /dev/null
+++ b/spec/ruby/library/csv/basicwriter/initialize_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::BasicWriter#initialize" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/basicwriter/terminate_spec.rb b/spec/ruby/library/csv/basicwriter/terminate_spec.rb
new file mode 100644
index 0000000000..8c53db3f0b
--- /dev/null
+++ b/spec/ruby/library/csv/basicwriter/terminate_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::BasicWriter#terminate" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/cell/data_spec.rb b/spec/ruby/library/csv/cell/data_spec.rb
new file mode 100644
index 0000000000..aa034b0b62
--- /dev/null
+++ b/spec/ruby/library/csv/cell/data_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::Cell#data" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/cell/initialize_spec.rb b/spec/ruby/library/csv/cell/initialize_spec.rb
new file mode 100644
index 0000000000..c9e506676c
--- /dev/null
+++ b/spec/ruby/library/csv/cell/initialize_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::Cell#initialize" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/fixtures/one_line.csv b/spec/ruby/library/csv/fixtures/one_line.csv
new file mode 100644
index 0000000000..d72f2010a8
--- /dev/null
+++ b/spec/ruby/library/csv/fixtures/one_line.csv
@@ -0,0 +1 @@
+1,2
diff --git a/spec/ruby/library/csv/foreach_spec.rb b/spec/ruby/library/csv/foreach_spec.rb
new file mode 100644
index 0000000000..36b3cd152f
--- /dev/null
+++ b/spec/ruby/library/csv/foreach_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'csv'
+
+describe "CSV.foreach" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/generate_line_spec.rb b/spec/ruby/library/csv/generate_line_spec.rb
new file mode 100644
index 0000000000..656365b109
--- /dev/null
+++ b/spec/ruby/library/csv/generate_line_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require 'csv'
+
+describe "CSV.generate_line" do
+
+ it "generates an empty string" do
+ result = CSV.generate_line([])
+ result.should == "\n"
+ end
+
+ it "generates the string 'foo,bar'" do
+ result = CSV.generate_line(["foo", "bar"])
+ result.should == "foo,bar\n"
+ end
+
+ it "generates the string 'foo;bar'" do
+ result = CSV.generate_line(["foo", "bar"], col_sep: ?;)
+ result.should == "foo;bar\n"
+ end
+
+ it "generates the string 'foo,,bar'" do
+ result = CSV.generate_line(["foo", nil, "bar"])
+ result.should == "foo,,bar\n"
+ end
+
+ it "generates the string 'foo;;bar'" do
+ result = CSV.generate_line(["foo", nil, "bar"], col_sep: ?;)
+ result.should == "foo;;bar\n"
+ end
+end
diff --git a/spec/ruby/library/csv/generate_row_spec.rb b/spec/ruby/library/csv/generate_row_spec.rb
new file mode 100644
index 0000000000..79dfc34000
--- /dev/null
+++ b/spec/ruby/library/csv/generate_row_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'csv'
+
+describe "CSV.generate_row" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/generate_spec.rb b/spec/ruby/library/csv/generate_spec.rb
new file mode 100644
index 0000000000..0a1e3d9604
--- /dev/null
+++ b/spec/ruby/library/csv/generate_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require 'csv'
+require 'tempfile'
+
+describe "CSV.generate" do
+
+ it "returns CSV string" do
+ csv_str = CSV.generate do |csv|
+ csv.add_row [1, 2, 3]
+ csv << [4, 5, 6]
+ end
+ csv_str.should == "1,2,3\n4,5,6\n"
+ end
+
+ it "accepts a col separator" do
+ csv_str = CSV.generate(col_sep: ";") do |csv|
+ csv.add_row [1, 2, 3]
+ csv << [4, 5, 6]
+ end
+ csv_str.should == "1;2;3\n4;5;6\n"
+ end
+
+ it "appends and returns the argument itself" do
+ str = ""
+ csv_str = CSV.generate(str) do |csv|
+ csv.add_row [1, 2, 3]
+ csv << [4, 5, 6]
+ end
+ csv_str.should equal str
+ str.should == "1,2,3\n4,5,6\n"
+ end
+end
diff --git a/spec/ruby/library/csv/iobuf/close_spec.rb b/spec/ruby/library/csv/iobuf/close_spec.rb
new file mode 100644
index 0000000000..e2fda86080
--- /dev/null
+++ b/spec/ruby/library/csv/iobuf/close_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::IOBuf#close" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/iobuf/initialize_spec.rb b/spec/ruby/library/csv/iobuf/initialize_spec.rb
new file mode 100644
index 0000000000..f08e82548a
--- /dev/null
+++ b/spec/ruby/library/csv/iobuf/initialize_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::IOBuf#initialize" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/iobuf/read_spec.rb b/spec/ruby/library/csv/iobuf/read_spec.rb
new file mode 100644
index 0000000000..b45334ee2a
--- /dev/null
+++ b/spec/ruby/library/csv/iobuf/read_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::IOBuf#read" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/iobuf/terminate_spec.rb b/spec/ruby/library/csv/iobuf/terminate_spec.rb
new file mode 100644
index 0000000000..69289e960c
--- /dev/null
+++ b/spec/ruby/library/csv/iobuf/terminate_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::IOBuf#terminate" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/ioreader/close_on_terminate_spec.rb b/spec/ruby/library/csv/ioreader/close_on_terminate_spec.rb
new file mode 100644
index 0000000000..4887ade1a1
--- /dev/null
+++ b/spec/ruby/library/csv/ioreader/close_on_terminate_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::IOReader#close_on_terminate" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/ioreader/get_row_spec.rb b/spec/ruby/library/csv/ioreader/get_row_spec.rb
new file mode 100644
index 0000000000..5fd2178b1b
--- /dev/null
+++ b/spec/ruby/library/csv/ioreader/get_row_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::IOReader#get_row" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/ioreader/initialize_spec.rb b/spec/ruby/library/csv/ioreader/initialize_spec.rb
new file mode 100644
index 0000000000..4e8c0964df
--- /dev/null
+++ b/spec/ruby/library/csv/ioreader/initialize_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::IOReader#initialize" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/ioreader/terminate_spec.rb b/spec/ruby/library/csv/ioreader/terminate_spec.rb
new file mode 100644
index 0000000000..676cd03a0f
--- /dev/null
+++ b/spec/ruby/library/csv/ioreader/terminate_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::IOReader#terminate" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/liberal_parsing_spec.rb b/spec/ruby/library/csv/liberal_parsing_spec.rb
new file mode 100644
index 0000000000..a2dda36c99
--- /dev/null
+++ b/spec/ruby/library/csv/liberal_parsing_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require 'csv'
+
+ruby_version_is '2.4' do
+ describe "CSV#liberal_parsing?" do
+ it "returns true if illegal input is handled" do
+ csv = CSV.new("", liberal_parsing: true)
+ csv.liberal_parsing?.should == true
+ end
+
+ it "returns false if illegal input is not handled" do
+ csv = CSV.new("", liberal_parsing: false)
+ csv.liberal_parsing?.should == false
+ end
+
+ it "returns false by default" do
+ csv = CSV.new("")
+ csv.liberal_parsing?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/library/csv/open_spec.rb b/spec/ruby/library/csv/open_spec.rb
new file mode 100644
index 0000000000..2d310cda3e
--- /dev/null
+++ b/spec/ruby/library/csv/open_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'csv'
+
+describe "CSV.open" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/parse_spec.rb b/spec/ruby/library/csv/parse_spec.rb
new file mode 100644
index 0000000000..fc3f04378b
--- /dev/null
+++ b/spec/ruby/library/csv/parse_spec.rb
@@ -0,0 +1,95 @@
+require_relative '../../spec_helper'
+require 'csv'
+
+describe "CSV.parse" do
+
+ it "parses '' into []" do
+ result = CSV.parse ''
+ result.should be_kind_of(Array)
+ result.should == []
+ end
+
+ it "parses '\n' into [[]]" do
+ result = CSV.parse "\n"
+ result.should == [[]]
+ end
+
+ it "parses 'foo' into [['foo']]" do
+ result = CSV.parse 'foo'
+ result.should == [['foo']]
+ end
+
+ it "parses 'foo,bar,baz' into [['foo','bar','baz']]" do
+ result = CSV.parse 'foo,bar,baz'
+ result.should == [['foo','bar','baz']]
+ end
+
+ it "parses 'foo,baz' into [[foo,nil,baz]]" do
+ result = CSV.parse 'foo,,baz'
+ result.should == [['foo',nil,'baz']]
+ end
+
+ it "parses '\nfoo' into [[],['foo']]" do
+ result = CSV.parse "\nfoo"
+ result.should == [[],['foo']]
+ end
+
+ it "parses 'foo\n' into [['foo']]" do
+ result = CSV.parse "foo\n"
+ result.should == [['foo']]
+ end
+
+ it "parses 'foo\nbar' into [['foo'],['bar']]" do
+ result = CSV.parse "foo\nbar"
+ result.should == [['foo'],['bar']]
+ end
+
+ it "parses 'foo,bar\nbaz,quz' into [['foo','bar'],['baz','quz']]" do
+ result = CSV.parse "foo,bar\nbaz,quz"
+ result.should == [['foo','bar'],['baz','quz']]
+ end
+
+ it "parses 'foo,bar'\nbaz' into [['foo','bar'],['baz']]" do
+ result = CSV.parse "foo,bar\nbaz"
+ result.should == [['foo','bar'],['baz']]
+ end
+
+ it "parses 'foo\nbar,baz' into [['foo'],['bar','baz']]" do
+ result = CSV.parse "foo\nbar,baz"
+ result.should == [['foo'],['bar','baz']]
+ end
+
+ it "parses '\n\nbar' into [[],[],'bar']]" do
+ result = CSV.parse "\n\nbar"
+ result.should == [[],[],['bar']]
+ end
+
+ it "parses 'foo' into [['foo']] with a separator of ;" do
+ result = CSV.parse "foo", col_sep: ?;
+ result.should == [['foo']]
+ end
+
+ it "parses 'foo;bar' into [['foo','bar']] with a separator of ;" do
+ result = CSV.parse "foo;bar", col_sep: ?;
+ result.should == [['foo','bar']]
+ end
+
+ it "parses 'foo;bar\nbaz;quz' into [['foo','bar'],['baz','quz']] with a separator of ;" do
+ result = CSV.parse "foo;bar\nbaz;quz", col_sep: ?;
+ result.should == [['foo','bar'],['baz','quz']]
+ end
+
+ it "raises CSV::MalformedCSVError exception if input is illegal" do
+ -> {
+ CSV.parse('"quoted" field')
+ }.should raise_error(CSV::MalformedCSVError)
+ end
+
+ ruby_version_is '2.4' do
+ it "handles illegal input with the liberal_parsing option" do
+ illegal_input = '"Johnson, Dwayne",Dwayne "The Rock" Johnson'
+ result = CSV.parse(illegal_input, liberal_parsing: true)
+ result.should == [["Johnson, Dwayne", 'Dwayne "The Rock" Johnson']]
+ end
+ end
+end
diff --git a/spec/ruby/library/csv/read_spec.rb b/spec/ruby/library/csv/read_spec.rb
new file mode 100644
index 0000000000..2e6bb65d56
--- /dev/null
+++ b/spec/ruby/library/csv/read_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'csv'
+
+describe "CSV.read" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/readlines_spec.rb b/spec/ruby/library/csv/readlines_spec.rb
new file mode 100644
index 0000000000..882266657e
--- /dev/null
+++ b/spec/ruby/library/csv/readlines_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require 'csv'
+
+describe "CSV.readlines" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "CSV#readlines" do
+ it "returns an Array of Array containing each element in a one-line CSV file" do
+ file = CSV.new "a, b, c"
+ file.readlines.should == [["a", " b", " c"]]
+ end
+
+ it "returns an Array of Arrays containing each element in a multi-line CSV file" do
+ file = CSV.new "a, b, c\nd, e, f"
+ file.readlines.should == [["a", " b", " c"], ["d", " e", " f"]]
+ end
+
+ it "returns nil for a missing value" do
+ file = CSV.new "a,, b, c"
+ file.readlines.should == [["a", nil, " b", " c"]]
+ end
+
+ it "raises CSV::MalformedCSVError exception if input is illegal" do
+ csv = CSV.new('"quoted" field')
+ -> { csv.readlines }.should raise_error(CSV::MalformedCSVError)
+ end
+
+ ruby_version_is '2.4' do
+ it "handles illegal input with the liberal_parsing option" do
+ illegal_input = '"Johnson, Dwayne",Dwayne "The Rock" Johnson'
+ csv = CSV.new(illegal_input, liberal_parsing: true)
+ result = csv.readlines
+ result.should == [["Johnson, Dwayne", 'Dwayne "The Rock" Johnson']]
+ end
+ end
+end
diff --git a/spec/ruby/library/csv/streambuf/add_buf_spec.rb b/spec/ruby/library/csv/streambuf/add_buf_spec.rb
new file mode 100644
index 0000000000..58c530c500
--- /dev/null
+++ b/spec/ruby/library/csv/streambuf/add_buf_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::StreamBuf#add_buf" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/streambuf/buf_size_spec.rb b/spec/ruby/library/csv/streambuf/buf_size_spec.rb
new file mode 100644
index 0000000000..1793c8b65e
--- /dev/null
+++ b/spec/ruby/library/csv/streambuf/buf_size_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::StreamBuf#buf_size" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/streambuf/drop_spec.rb b/spec/ruby/library/csv/streambuf/drop_spec.rb
new file mode 100644
index 0000000000..448f0a2196
--- /dev/null
+++ b/spec/ruby/library/csv/streambuf/drop_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::StreamBuf#drop" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/streambuf/element_reference_spec.rb b/spec/ruby/library/csv/streambuf/element_reference_spec.rb
new file mode 100644
index 0000000000..5a75901830
--- /dev/null
+++ b/spec/ruby/library/csv/streambuf/element_reference_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::StreamBuf#[]" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/streambuf/get_spec.rb b/spec/ruby/library/csv/streambuf/get_spec.rb
new file mode 100644
index 0000000000..2255e55e91
--- /dev/null
+++ b/spec/ruby/library/csv/streambuf/get_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::StreamBuf#get" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/streambuf/idx_is_eos_spec.rb b/spec/ruby/library/csv/streambuf/idx_is_eos_spec.rb
new file mode 100644
index 0000000000..563b8b2d4a
--- /dev/null
+++ b/spec/ruby/library/csv/streambuf/idx_is_eos_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::StreamBuf#idx_is_eos?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/streambuf/initialize_spec.rb b/spec/ruby/library/csv/streambuf/initialize_spec.rb
new file mode 100644
index 0000000000..1273c98094
--- /dev/null
+++ b/spec/ruby/library/csv/streambuf/initialize_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::StreamBuf#initialize" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/streambuf/is_eos_spec.rb b/spec/ruby/library/csv/streambuf/is_eos_spec.rb
new file mode 100644
index 0000000000..a0a3c1e0b0
--- /dev/null
+++ b/spec/ruby/library/csv/streambuf/is_eos_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::StreamBuf#is_eos?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/streambuf/read_spec.rb b/spec/ruby/library/csv/streambuf/read_spec.rb
new file mode 100644
index 0000000000..cf98c53409
--- /dev/null
+++ b/spec/ruby/library/csv/streambuf/read_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::StreamBuf#read" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/streambuf/rel_buf_spec.rb b/spec/ruby/library/csv/streambuf/rel_buf_spec.rb
new file mode 100644
index 0000000000..548e347200
--- /dev/null
+++ b/spec/ruby/library/csv/streambuf/rel_buf_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::StreamBuf#rel_buf" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/streambuf/terminate_spec.rb b/spec/ruby/library/csv/streambuf/terminate_spec.rb
new file mode 100644
index 0000000000..247b33184a
--- /dev/null
+++ b/spec/ruby/library/csv/streambuf/terminate_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::StreamBuf#terminate" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/stringreader/get_row_spec.rb b/spec/ruby/library/csv/stringreader/get_row_spec.rb
new file mode 100644
index 0000000000..5cc3447061
--- /dev/null
+++ b/spec/ruby/library/csv/stringreader/get_row_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::StringReader#get_row" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/stringreader/initialize_spec.rb b/spec/ruby/library/csv/stringreader/initialize_spec.rb
new file mode 100644
index 0000000000..4e3634847e
--- /dev/null
+++ b/spec/ruby/library/csv/stringreader/initialize_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::StringReader#initialize" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/writer/add_row_spec.rb b/spec/ruby/library/csv/writer/add_row_spec.rb
new file mode 100644
index 0000000000..2f074b45db
--- /dev/null
+++ b/spec/ruby/library/csv/writer/add_row_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::Writer#add_row" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/writer/append_spec.rb b/spec/ruby/library/csv/writer/append_spec.rb
new file mode 100644
index 0000000000..4e1f6728c2
--- /dev/null
+++ b/spec/ruby/library/csv/writer/append_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::Writer#<<" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/writer/close_spec.rb b/spec/ruby/library/csv/writer/close_spec.rb
new file mode 100644
index 0000000000..1a87094bb7
--- /dev/null
+++ b/spec/ruby/library/csv/writer/close_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::Writer#close" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/writer/create_spec.rb b/spec/ruby/library/csv/writer/create_spec.rb
new file mode 100644
index 0000000000..a4514d5578
--- /dev/null
+++ b/spec/ruby/library/csv/writer/create_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::Writer.create" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/writer/generate_spec.rb b/spec/ruby/library/csv/writer/generate_spec.rb
new file mode 100644
index 0000000000..6ea9161777
--- /dev/null
+++ b/spec/ruby/library/csv/writer/generate_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::Writer.generate" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/writer/initialize_spec.rb b/spec/ruby/library/csv/writer/initialize_spec.rb
new file mode 100644
index 0000000000..6bba8f8d0a
--- /dev/null
+++ b/spec/ruby/library/csv/writer/initialize_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::Writer#initialize" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/csv/writer/terminate_spec.rb b/spec/ruby/library/csv/writer/terminate_spec.rb
new file mode 100644
index 0000000000..77136dd018
--- /dev/null
+++ b/spec/ruby/library/csv/writer/terminate_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'csv'
+
+describe "CSV::Writer#terminate" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/accessor_spec.rb b/spec/ruby/library/date/accessor_spec.rb
new file mode 100644
index 0000000000..68a2d9f3de
--- /dev/null
+++ b/spec/ruby/library/date/accessor_spec.rb
@@ -0,0 +1,91 @@
+require 'date'
+require_relative '../../spec_helper'
+
+describe "Date#ajd" do
+ it "determines the Astronomical Julian day" do
+ Date.civil(2007, 1, 17).ajd.should == 4908235.to_r / 2
+ end
+end
+
+describe "Date#amjd" do
+ it "determines the Astronomical Modified Julian day" do
+ Date.civil(2007, 1, 17).amjd.should == 54117
+ end
+end
+
+describe "Date#day_fraction" do
+ it "determines the day fraction" do
+ Date.civil(2007, 1, 17).day_fraction.should == 0
+ end
+end
+
+describe "Date#mjd" do
+ it "determines the Modified Julian day" do
+ Date.civil(2007, 1, 17).mjd.should == 54117
+ end
+end
+
+describe "Date#ld" do
+ it "determines the Modified Julian day" do
+ Date.civil(2007, 1, 17).ld.should == 154958
+ end
+end
+
+describe "Date#year" do
+ it "determines the year" do
+ Date.civil(2007, 1, 17).year.should == 2007
+ end
+end
+
+describe "Date#yday" do
+ it "determines the year" do
+ Date.civil(2007, 1, 17).yday.should == 17
+ Date.civil(2008, 10, 28).yday.should == 302
+ end
+end
+
+describe "Date#mon" do
+ it "determines the month" do
+ Date.civil(2007, 1, 17).mon.should == 1
+ Date.civil(2008, 10, 28).mon.should == 10
+ end
+end
+
+describe "Date#mday" do
+ it "determines the day of the month" do
+ Date.civil(2007, 1, 17).mday.should == 17
+ Date.civil(2008, 10, 28).mday.should == 28
+ end
+end
+
+describe "Date#wday" do
+ it "determines the week day" do
+ Date.civil(2007, 1, 17).wday.should == 3
+ Date.civil(2008, 10, 26).wday.should == 0
+ end
+end
+
+describe "Date#cwyear" do
+ it "determines the commercial year" do
+ Date.civil(2007, 1, 17).cwyear.should == 2007
+ Date.civil(2008, 10, 28).cwyear.should == 2008
+ Date.civil(2007, 12, 31).cwyear.should == 2008
+ Date.civil(2010, 1, 1).cwyear.should == 2009
+ end
+end
+
+describe "Date#cweek" do
+ it "determines the commercial week" do
+ Date.civil(2007, 1, 17).cweek.should == 3
+ Date.civil(2008, 10, 28).cweek.should == 44
+ Date.civil(2007, 12, 31).cweek.should == 1
+ Date.civil(2010, 1, 1).cweek.should == 53
+ end
+end
+
+describe "Date#cwday" do
+ it "determines the commercial week day" do
+ Date.civil(2007, 1, 17).cwday.should == 3
+ Date.civil(2008, 10, 26).cwday.should == 7
+ end
+end
diff --git a/spec/ruby/library/date/add_month_spec.rb b/spec/ruby/library/date/add_month_spec.rb
new file mode 100644
index 0000000000..32e9de4d70
--- /dev/null
+++ b/spec/ruby/library/date/add_month_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#>>" do
+ it "adds the number of months to a Date" do
+ d = Date.civil(2007,2,27) >> 10
+ d.should == Date.civil(2007, 12, 27)
+ end
+
+ it "sets the day to the last day of a month if the day doesn't exist" do
+ d = Date.civil(2008,3,31) >> 1
+ d.should == Date.civil(2008, 4, 30)
+ end
+
+ it "returns the day of the reform if date falls within calendar reform" do
+ calendar_reform_italy = Date.new(1582, 10, 4)
+ d1 = Date.new(1582, 9, 9) >> 1
+ d2 = Date.new(1582, 9, 10) >> 1
+ d1.should == calendar_reform_italy
+ d2.should == calendar_reform_italy
+ end
+
+ it "raise a TypeError when passed a Symbol" do
+ lambda { Date.civil(2007,2,27) >> :hello }.should raise_error(TypeError)
+ end
+
+ it "raise a TypeError when passed a String" do
+ lambda { Date.civil(2007,2,27) >> "hello" }.should raise_error(TypeError)
+ end
+
+ it "raise a TypeError when passed a Date" do
+ lambda { Date.civil(2007,2,27) >> Date.new }.should raise_error(TypeError)
+ end
+
+ it "raise a TypeError when passed an Object" do
+ lambda { Date.civil(2007,2,27) >> Object.new }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/date/add_spec.rb b/spec/ruby/library/date/add_spec.rb
new file mode 100644
index 0000000000..edcff4c55b
--- /dev/null
+++ b/spec/ruby/library/date/add_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#+" do
+ it "adds the number of days to a Date" do
+ d = Date.civil(2007,2,27) + 10
+ d.should == Date.civil(2007, 3, 9)
+ end
+
+ it "adds a negative number of days to a Date" do
+ d = Date.civil(2007,2,27).+(-10)
+ d.should == Date.civil(2007, 2, 17)
+ end
+
+ it "raises a TypeError when passed a Symbol" do
+ lambda { Date.civil(2007,2,27) + :hello }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a String" do
+ lambda { Date.civil(2007,2,27) + "hello" }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a Date" do
+ lambda { Date.civil(2007,2,27) + Date.new }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed an Object" do
+ lambda { Date.civil(2007,2,27) + Object.new }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/date/ajd_spec.rb b/spec/ruby/library/date/ajd_spec.rb
new file mode 100644
index 0000000000..10f1302354
--- /dev/null
+++ b/spec/ruby/library/date/ajd_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#ajd" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/ajd_to_amjd_spec.rb b/spec/ruby/library/date/ajd_to_amjd_spec.rb
new file mode 100644
index 0000000000..948f4c2236
--- /dev/null
+++ b/spec/ruby/library/date/ajd_to_amjd_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.ajd_to_amjd" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/ajd_to_jd_spec.rb b/spec/ruby/library/date/ajd_to_jd_spec.rb
new file mode 100644
index 0000000000..e55ce9f4f2
--- /dev/null
+++ b/spec/ruby/library/date/ajd_to_jd_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.ajd_to_jd" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/amjd_spec.rb b/spec/ruby/library/date/amjd_spec.rb
new file mode 100644
index 0000000000..ad7bc14965
--- /dev/null
+++ b/spec/ruby/library/date/amjd_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#amjd" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/amjd_to_ajd_spec.rb b/spec/ruby/library/date/amjd_to_ajd_spec.rb
new file mode 100644
index 0000000000..66c26a16a8
--- /dev/null
+++ b/spec/ruby/library/date/amjd_to_ajd_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.amjd_to_ajd" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/append_spec.rb b/spec/ruby/library/date/append_spec.rb
new file mode 100644
index 0000000000..4305a00321
--- /dev/null
+++ b/spec/ruby/library/date/append_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#<<" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/asctime_spec.rb b/spec/ruby/library/date/asctime_spec.rb
new file mode 100644
index 0000000000..67d158cc01
--- /dev/null
+++ b/spec/ruby/library/date/asctime_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#asctime" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/boat_spec.rb b/spec/ruby/library/date/boat_spec.rb
new file mode 100644
index 0000000000..e4f1b797fc
--- /dev/null
+++ b/spec/ruby/library/date/boat_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#<=>" do
+ it "returns 0 when two dates are equal" do
+ (Date.civil(2000, 04, 06) <=> Date.civil(2000, 04, 06)).should == 0
+ end
+
+ it "returns -1 when self is less than another date" do
+ (Date.civil(2000, 04, 05) <=> Date.civil(2000, 04, 06)).should == -1
+ end
+
+ it "returns -1 when self is less than a Numeric" do
+ (Date.civil(2000, 04, 05) <=> Date.civil(2000, 04, 06).jd).should == -1
+ end
+
+ it "returns 1 when self is greater than another date" do
+ (Date.civil(2001, 04, 05) <=> Date.civil(2000, 04, 06)).should == 1
+ end
+
+ it "returns 1 when self is greater than a Numeric" do
+ (Date.civil(2001, 04, 05) <=> Date.civil(2000, 04, 06).jd).should == 1
+ end
+end
diff --git a/spec/ruby/library/date/case_compare_spec.rb b/spec/ruby/library/date/case_compare_spec.rb
new file mode 100644
index 0000000000..87d522ee6a
--- /dev/null
+++ b/spec/ruby/library/date/case_compare_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#===" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/civil_spec.rb b/spec/ruby/library/date/civil_spec.rb
new file mode 100644
index 0000000000..f3537c2f84
--- /dev/null
+++ b/spec/ruby/library/date/civil_spec.rb
@@ -0,0 +1,12 @@
+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"
+end
diff --git a/spec/ruby/library/date/commercial_spec.rb b/spec/ruby/library/date/commercial_spec.rb
new file mode 100644
index 0000000000..d7fc34d74a
--- /dev/null
+++ b/spec/ruby/library/date/commercial_spec.rb
@@ -0,0 +1,17 @@
+require 'date'
+require_relative '../../spec_helper'
+require_relative 'shared/commercial'
+
+describe "Date#commercial" do
+
+ it_behaves_like :date_commercial, :commercial
+
+end
+
+# reference:
+# October 1582 (the Gregorian calendar, Civil Date)
+# 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
diff --git a/spec/ruby/library/date/commercial_to_jd_spec.rb b/spec/ruby/library/date/commercial_to_jd_spec.rb
new file mode 100644
index 0000000000..9b77f1229f
--- /dev/null
+++ b/spec/ruby/library/date/commercial_to_jd_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.commercial_to_jd" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/comparison_spec.rb b/spec/ruby/library/date/comparison_spec.rb
new file mode 100644
index 0000000000..1a94b9dcd2
--- /dev/null
+++ b/spec/ruby/library/date/comparison_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#<=>" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/constants_spec.rb b/spec/ruby/library/date/constants_spec.rb
new file mode 100644
index 0000000000..dd7803052f
--- /dev/null
+++ b/spec/ruby/library/date/constants_spec.rb
@@ -0,0 +1,48 @@
+require 'date'
+require_relative '../../spec_helper'
+
+describe "Date constants" do
+
+ it "defines JULIAN" do
+ (Date::JULIAN <=> Date::Infinity.new).should == 0
+ end
+
+ it "defines GREGORIAN" do
+ (Date::GREGORIAN <=> -Date::Infinity.new).should == 0
+ end
+
+ it "defines ITALY" do
+ Date::ITALY.should == 2299161 # 1582-10-15
+ end
+
+ it "defines ENGLAND" do
+ Date::ENGLAND.should == 2361222 # 1752-09-14
+ end
+
+ it "defines MONTHNAMES" do
+ Date::MONTHNAMES.should == [nil] + %w(January February March April May June July
+ August September October November December)
+ end
+
+ it "defines DAYNAMES" do
+ Date::DAYNAMES.should == %w(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)
+ end
+
+ it "defines ABBR_MONTHNAMES" do
+ Date::ABBR_DAYNAMES.should == %w(Sun Mon Tue Wed Thu Fri Sat)
+ end
+
+ it "freezes MONTHNAMES, DAYNAMES, ABBR_MONTHNAMES, ABBR_DAYSNAMES" do
+ [Date::MONTHNAMES, Date::DAYNAMES, Date::ABBR_MONTHNAMES, Date::ABBR_DAYNAMES].each do |ary|
+ lambda {
+ ary << "Unknown"
+ }.should raise_error(frozen_error_class, /frozen/)
+ ary.compact.each do |name|
+ lambda {
+ name << "modified"
+ }.should raise_error(frozen_error_class, /frozen/)
+ end
+ end
+ end
+
+end
diff --git a/spec/ruby/library/date/conversions_spec.rb b/spec/ruby/library/date/conversions_spec.rb
new file mode 100644
index 0000000000..a9a320b0fc
--- /dev/null
+++ b/spec/ruby/library/date/conversions_spec.rb
@@ -0,0 +1,43 @@
+require 'date'
+require_relative '../../spec_helper'
+
+
+describe "Date#new_start" do
+ it "converts a date object into another with a new calendar reform" do
+ Date.civil(1582, 10, 14, Date::ENGLAND).new_start.should == Date.civil(1582, 10, 24)
+ Date.civil(1582, 10, 4, Date::ENGLAND).new_start.should == Date.civil(1582, 10, 4)
+ Date.civil(1582, 10, 15).new_start(Date::ENGLAND).should == Date.civil(1582, 10, 5, Date::ENGLAND)
+ Date.civil(1752, 9, 14).new_start(Date::ENGLAND).should == Date.civil(1752, 9, 14, Date::ENGLAND)
+ Date.civil(1752, 9, 13).new_start(Date::ENGLAND).should == Date.civil(1752, 9, 2, Date::ENGLAND)
+ end
+end
+
+describe "Date#italy" do
+ it "converts a date object into another with the Italian calendar reform" do
+ Date.civil(1582, 10, 14, Date::ENGLAND).italy.should == Date.civil(1582, 10, 24)
+ Date.civil(1582, 10, 4, Date::ENGLAND).italy.should == Date.civil(1582, 10, 4)
+ end
+end
+
+describe "Date#england" do
+ it "converts a date object into another with the English calendar reform" do
+ Date.civil(1582, 10, 15).england.should == Date.civil(1582, 10, 5, Date::ENGLAND)
+ Date.civil(1752, 9, 14).england.should == Date.civil(1752, 9, 14, Date::ENGLAND)
+ Date.civil(1752, 9, 13).england.should == Date.civil(1752, 9, 2, Date::ENGLAND)
+ end
+end
+
+describe "Date#julian" do
+ it "converts a date object into another with the Julian calendar" do
+ Date.civil(1582, 10, 15).julian.should == Date.civil(1582, 10, 5, Date::JULIAN)
+ Date.civil(1752, 9, 14).julian.should == Date.civil(1752, 9, 3, Date::JULIAN)
+ Date.civil(1752, 9, 13).julian.should == Date.civil(1752, 9, 2, Date::JULIAN)
+ end
+end
+
+describe "Date#gregorian" do
+ it "converts a date object into another with the Gregorian calendar" do
+ Date.civil(1582, 10, 4).gregorian.should == Date.civil(1582, 10, 14, Date::GREGORIAN)
+ Date.civil(1752, 9, 14).gregorian.should == Date.civil(1752, 9, 14, Date::GREGORIAN)
+ end
+end
diff --git a/spec/ruby/library/date/ctime_spec.rb b/spec/ruby/library/date/ctime_spec.rb
new file mode 100644
index 0000000000..3faa7c6380
--- /dev/null
+++ b/spec/ruby/library/date/ctime_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#ctime" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/cwday_spec.rb b/spec/ruby/library/date/cwday_spec.rb
new file mode 100644
index 0000000000..c5a39f277f
--- /dev/null
+++ b/spec/ruby/library/date/cwday_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#cwday" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/cweek_spec.rb b/spec/ruby/library/date/cweek_spec.rb
new file mode 100644
index 0000000000..6f7aab3922
--- /dev/null
+++ b/spec/ruby/library/date/cweek_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#cweek" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/cwyear_spec.rb b/spec/ruby/library/date/cwyear_spec.rb
new file mode 100644
index 0000000000..a85ee29920
--- /dev/null
+++ b/spec/ruby/library/date/cwyear_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#cwyear" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/day_fraction_spec.rb b/spec/ruby/library/date/day_fraction_spec.rb
new file mode 100644
index 0000000000..12b873773f
--- /dev/null
+++ b/spec/ruby/library/date/day_fraction_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#day_fraction" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/day_fraction_to_time_spec.rb b/spec/ruby/library/date/day_fraction_to_time_spec.rb
new file mode 100644
index 0000000000..d4741d65ec
--- /dev/null
+++ b/spec/ruby/library/date/day_fraction_to_time_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.day_fraction_to_time" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/day_spec.rb b/spec/ruby/library/date/day_spec.rb
new file mode 100644
index 0000000000..bc727c4717
--- /dev/null
+++ b/spec/ruby/library/date/day_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#day" do
+ it "returns the day" do
+ d = Date.new(2000, 7, 1).day
+ d.should == 1
+ end
+end
diff --git a/spec/ruby/library/date/downto_spec.rb b/spec/ruby/library/date/downto_spec.rb
new file mode 100644
index 0000000000..84c641ee14
--- /dev/null
+++ b/spec/ruby/library/date/downto_spec.rb
@@ -0,0 +1,18 @@
+require 'date'
+require_relative '../../spec_helper'
+
+describe "Date#downto" do
+
+ it "creates earlier dates when passed a negative step" do
+ ds = Date.civil(2000, 4, 14)
+ de = Date.civil(2000, 3, 29)
+ count = 0
+ ds.step(de, -1) do |d|
+ d.should <= ds
+ d.should >= de
+ count += 1
+ end
+ count.should == 17
+ end
+
+end
diff --git a/spec/ruby/library/date/england_spec.rb b/spec/ruby/library/date/england_spec.rb
new file mode 100644
index 0000000000..2e30530c5a
--- /dev/null
+++ b/spec/ruby/library/date/england_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#england" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/eql_spec.rb b/spec/ruby/library/date/eql_spec.rb
new file mode 100644
index 0000000000..a1819cae3a
--- /dev/null
+++ b/spec/ruby/library/date/eql_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#eql?" do
+ it "returns true if self is equal to another date" do
+ Date.civil(2007, 10, 11).eql?(Date.civil(2007, 10, 11)).should be_true
+ end
+
+ it "returns false if self is not equal to another date" do
+ Date.civil(2007, 10, 11).eql?(Date.civil(2007, 10, 12)).should be_false
+ end
+end
diff --git a/spec/ruby/library/date/format/bag/method_missing_spec.rb b/spec/ruby/library/date/format/bag/method_missing_spec.rb
new file mode 100644
index 0000000000..03e4fbcd30
--- /dev/null
+++ b/spec/ruby/library/date/format/bag/method_missing_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../../spec_helper'
+require 'date'
+
+describe "Date::Format::Bag#method_missing" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/format/bag/to_hash_spec.rb b/spec/ruby/library/date/format/bag/to_hash_spec.rb
new file mode 100644
index 0000000000..76734624b9
--- /dev/null
+++ b/spec/ruby/library/date/format/bag/to_hash_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../../spec_helper'
+require 'date'
+
+describe "Date::Format::Bag#to_hash" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/friday_spec.rb b/spec/ruby/library/date/friday_spec.rb
new file mode 100644
index 0000000000..3dc040fabe
--- /dev/null
+++ b/spec/ruby/library/date/friday_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#friday?" do
+ it "should be friday" do
+ Date.new(2000, 1, 7).friday?.should be_true
+ end
+
+ it "should not be friday" do
+ Date.new(2000, 1, 8).friday?.should be_false
+ end
+end
diff --git a/spec/ruby/library/date/gregorian_leap_spec.rb b/spec/ruby/library/date/gregorian_leap_spec.rb
new file mode 100644
index 0000000000..c3d25cf90f
--- /dev/null
+++ b/spec/ruby/library/date/gregorian_leap_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#gregorian_leap?" do
+ it "returns true if a year is a leap year in the Gregorian calendar" do
+ Date.gregorian_leap?(2000).should be_true
+ Date.gregorian_leap?(2004).should be_true
+ end
+
+ it "returns false if a year is not a leap year in the Gregorian calendar" do
+ Date.gregorian_leap?(1900).should be_false
+ Date.gregorian_leap?(1999).should be_false
+ Date.gregorian_leap?(2002).should be_false
+ end
+end
diff --git a/spec/ruby/library/date/gregorian_spec.rb b/spec/ruby/library/date/gregorian_spec.rb
new file mode 100644
index 0000000000..8b7033fe75
--- /dev/null
+++ b/spec/ruby/library/date/gregorian_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#gregorian?" do
+
+ it "marks a day before the calendar reform as Julian" do
+ Date.civil(1007, 2, 27).gregorian?.should be_false
+ Date.civil(1907, 2, 27, Date.civil(1930, 1, 1).jd).gregorian?.should be_false
+ end
+
+ it "marks a day after the calendar reform as Julian" do
+ Date.civil(2007, 2, 27).gregorian?.should == true
+ Date.civil(1607, 2, 27, Date.civil(1582, 1, 1).jd).gregorian?.should be_true
+ end
+
+end
diff --git a/spec/ruby/library/date/hash_spec.rb b/spec/ruby/library/date/hash_spec.rb
new file mode 100644
index 0000000000..4fb452d486
--- /dev/null
+++ b/spec/ruby/library/date/hash_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#hash" do
+ it "returns the same value for equal dates" do
+ Date.civil(2004, 7, 12).hash.should == Date.civil(2004, 7, 12).hash
+ end
+end
diff --git a/spec/ruby/library/date/infinity/abs_spec.rb b/spec/ruby/library/date/infinity/abs_spec.rb
new file mode 100644
index 0000000000..c08189155d
--- /dev/null
+++ b/spec/ruby/library/date/infinity/abs_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'date'
+
+describe "Date::Infinity#abs" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/infinity/coerce_spec.rb b/spec/ruby/library/date/infinity/coerce_spec.rb
new file mode 100644
index 0000000000..75e5ebeab7
--- /dev/null
+++ b/spec/ruby/library/date/infinity/coerce_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'date'
+
+describe "Date::Infinity#coerce" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/infinity/comparison_spec.rb b/spec/ruby/library/date/infinity/comparison_spec.rb
new file mode 100644
index 0000000000..a9b9d124fd
--- /dev/null
+++ b/spec/ruby/library/date/infinity/comparison_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'date'
+
+describe "Date::Infinity#<=>" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/infinity/d_spec.rb b/spec/ruby/library/date/infinity/d_spec.rb
new file mode 100644
index 0000000000..a5bd2427c7
--- /dev/null
+++ b/spec/ruby/library/date/infinity/d_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'date'
+
+describe "Date::Infinity#d" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/infinity/finite_spec.rb b/spec/ruby/library/date/infinity/finite_spec.rb
new file mode 100644
index 0000000000..8971c2213e
--- /dev/null
+++ b/spec/ruby/library/date/infinity/finite_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'date'
+
+describe "Date::Infinity#finite?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/infinity/infinite_spec.rb b/spec/ruby/library/date/infinity/infinite_spec.rb
new file mode 100644
index 0000000000..848f538672
--- /dev/null
+++ b/spec/ruby/library/date/infinity/infinite_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'date'
+
+describe "Date::Infinity#infinite?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/infinity/nan_spec.rb b/spec/ruby/library/date/infinity/nan_spec.rb
new file mode 100644
index 0000000000..b0f5d8ac7a
--- /dev/null
+++ b/spec/ruby/library/date/infinity/nan_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'date'
+
+describe "Date::Infinity#nan?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/infinity/uminus_spec.rb b/spec/ruby/library/date/infinity/uminus_spec.rb
new file mode 100644
index 0000000000..1b1f568103
--- /dev/null
+++ b/spec/ruby/library/date/infinity/uminus_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'date'
+
+describe "Date::Infinity#-@" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/infinity/uplus_spec.rb b/spec/ruby/library/date/infinity/uplus_spec.rb
new file mode 100644
index 0000000000..6a3b2d8442
--- /dev/null
+++ b/spec/ruby/library/date/infinity/uplus_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'date'
+
+describe "Date::Infinity#+@" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/infinity/zero_spec.rb b/spec/ruby/library/date/infinity/zero_spec.rb
new file mode 100644
index 0000000000..7df5518785
--- /dev/null
+++ b/spec/ruby/library/date/infinity/zero_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'date'
+
+describe "Date::Infinity#zero?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/infinity_spec.rb b/spec/ruby/library/date/infinity_spec.rb
new file mode 100644
index 0000000000..81d67ae249
--- /dev/null
+++ b/spec/ruby/library/date/infinity_spec.rb
@@ -0,0 +1,67 @@
+require 'date'
+require_relative '../../spec_helper'
+
+describe "Date::Infinity" do
+
+ it "should be able to check whether Infinity is zero" do
+ i = Date::Infinity.new
+ i.zero?.should == false
+ end
+
+ it "should be able to check whether Infinity is finite" do
+ i1 = Date::Infinity.new
+ i1.finite?.should == false
+ i2 = Date::Infinity.new(-1)
+ i2.finite?.should == false
+ i3 = Date::Infinity.new(0)
+ i3.finite?.should == false
+ end
+
+ it "should be able to check whether Infinity is infinite" do
+ i1 = Date::Infinity.new
+ i1.infinite?.should == 1
+ i2 = Date::Infinity.new(-1)
+ i2.infinite?.should == -1
+ i3 = Date::Infinity.new(0)
+ i3.infinite?.should == nil
+ end
+
+ it "should be able to check whether Infinity is not a number" do
+ i1 = Date::Infinity.new
+ i1.nan?.should == false
+ i2 = Date::Infinity.new(-1)
+ i2.nan?.should == false
+ i3 = Date::Infinity.new(0)
+ i3.nan?.should == true
+ end
+
+ it "should be able to compare Infinity objects" do
+ i1 = Date::Infinity.new
+ i2 = Date::Infinity.new(-1)
+ i3 = Date::Infinity.new(0)
+ i4 = Date::Infinity.new
+ (i4 <=> i1).should == 0
+ (i3 <=> i1).should == -1
+ (i2 <=> i1).should == -1
+ (i3 <=> i2).should == 1
+ end
+
+ it "should be able to return plus Infinity for abs" do
+ i1 = Date::Infinity.new
+ i2 = Date::Infinity.new(-1)
+ i3 = Date::Infinity.new(0)
+ (i2.abs <=> i1).should == 0
+ (i3.abs <=> i1).should == 0
+ end
+
+ it "should be able to use -@ and +@ for Date::Infinity" do
+ (Date::Infinity.new <=> +Date::Infinity.new).should == 0
+ (Date::Infinity.new(-1) <=> -Date::Infinity.new).should == 0
+ end
+
+ it "should be able to coerce a Date::Infinity object" do
+ Date::Infinity.new.coerce(1).should == [-1, 1]
+ Date::Infinity.new(0).coerce(2).should == [0, 0]
+ Date::Infinity.new(-1).coerce(1.5).should == [1, -1]
+ end
+end
diff --git a/spec/ruby/library/date/inspect_spec.rb b/spec/ruby/library/date/inspect_spec.rb
new file mode 100644
index 0000000000..81c2cc8003
--- /dev/null
+++ b/spec/ruby/library/date/inspect_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#inspect" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/iso8601_spec.rb b/spec/ruby/library/date/iso8601_spec.rb
new file mode 100644
index 0000000000..41f055e648
--- /dev/null
+++ b/spec/ruby/library/date/iso8601_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.iso8601" do
+ it "parses YYYY-MM-DD into a Date object" do
+ d = Date.iso8601("2018-01-01")
+ d.should == Date.civil(2018, 1, 1)
+ end
+
+ it "parses YYYYMMDD into a Date object" do
+ d = Date.iso8601("20180715")
+ d.should == Date.civil(2018, 7, 15)
+ end
+
+ it "parses a negative Date" do
+ d = Date.iso8601("-4712-01-01")
+ d.should == Date.civil(-4712, 1, 1)
+ end
+
+ it "parses a Symbol into a Date object" do
+ d = Date.iso8601(:'2015-10-15')
+ d.should == Date.civil(2015, 10, 15)
+ end
+
+ it "parses a StringSubclass into a Date object" do
+ d = Date.iso8601(Class.new(String).new("-4712-01-01"))
+ d.should == Date.civil(-4712, 1, 1)
+ end
+
+ it "raises an ArgumentError when passed a Symbol without a valid Date" do
+ lambda { Date.iso8601(:test) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError when passed an Object" do
+ lambda { Date.iso8601(Object.new) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/date/italy_spec.rb b/spec/ruby/library/date/italy_spec.rb
new file mode 100644
index 0000000000..9369b05180
--- /dev/null
+++ b/spec/ruby/library/date/italy_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#italy" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/jd_spec.rb b/spec/ruby/library/date/jd_spec.rb
new file mode 100644
index 0000000000..336b783e8d
--- /dev/null
+++ b/spec/ruby/library/date/jd_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'shared/jd'
+require 'date'
+
+describe "Date#jd" do
+
+ it "determines the Julian day for a Date object" do
+ Date.civil(2008, 1, 16).jd.should == 2454482
+ end
+
+end
+
+describe "Date.jd" do
+ it_behaves_like :date_jd, :jd
+end
diff --git a/spec/ruby/library/date/jd_to_ajd_spec.rb b/spec/ruby/library/date/jd_to_ajd_spec.rb
new file mode 100644
index 0000000000..f946c46b8a
--- /dev/null
+++ b/spec/ruby/library/date/jd_to_ajd_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.jd_to_ajd" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/jd_to_civil_spec.rb b/spec/ruby/library/date/jd_to_civil_spec.rb
new file mode 100644
index 0000000000..13b6e47ee2
--- /dev/null
+++ b/spec/ruby/library/date/jd_to_civil_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.jd_to_civil" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/jd_to_commercial_spec.rb b/spec/ruby/library/date/jd_to_commercial_spec.rb
new file mode 100644
index 0000000000..2256b74f2a
--- /dev/null
+++ b/spec/ruby/library/date/jd_to_commercial_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.jd_to_commercial" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/jd_to_ld_spec.rb b/spec/ruby/library/date/jd_to_ld_spec.rb
new file mode 100644
index 0000000000..5954014f85
--- /dev/null
+++ b/spec/ruby/library/date/jd_to_ld_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.jd_to_ld" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/jd_to_mjd_spec.rb b/spec/ruby/library/date/jd_to_mjd_spec.rb
new file mode 100644
index 0000000000..24eb84e171
--- /dev/null
+++ b/spec/ruby/library/date/jd_to_mjd_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.jd_to_mjd" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/jd_to_ordinal_spec.rb b/spec/ruby/library/date/jd_to_ordinal_spec.rb
new file mode 100644
index 0000000000..c7c1704948
--- /dev/null
+++ b/spec/ruby/library/date/jd_to_ordinal_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.jd_to_ordinal" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/jd_to_wday_spec.rb b/spec/ruby/library/date/jd_to_wday_spec.rb
new file mode 100644
index 0000000000..27e00b2044
--- /dev/null
+++ b/spec/ruby/library/date/jd_to_wday_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.jd_to_wday" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/julian_leap_spec.rb b/spec/ruby/library/date/julian_leap_spec.rb
new file mode 100644
index 0000000000..2ef2d65d81
--- /dev/null
+++ b/spec/ruby/library/date/julian_leap_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.julian_leap?" do
+ it "determines whether a year is a leap year in the Julian calendar" do
+ Date.julian_leap?(1900).should be_true
+ Date.julian_leap?(2000).should be_true
+ Date.julian_leap?(2004).should be_true
+ end
+
+ it "determines whether a year is not a leap year in the Julian calendar" do
+ Date.julian_leap?(1999).should be_false
+ Date.julian_leap?(2002).should be_false
+ end
+end
diff --git a/spec/ruby/library/date/julian_spec.rb b/spec/ruby/library/date/julian_spec.rb
new file mode 100644
index 0000000000..637a4739e5
--- /dev/null
+++ b/spec/ruby/library/date/julian_spec.rb
@@ -0,0 +1,16 @@
+require 'date'
+require_relative '../../spec_helper'
+
+describe "Date#julian?" do
+
+ it "marks a day before the calendar reform as Julian" do
+ Date.civil(1007, 2, 27).julian?.should == true
+ Date.civil(1907, 2, 27, Date.civil(1930, 1, 1).jd).julian?.should be_true
+ end
+
+ it "marks a day after the calendar reform as Julian" do
+ Date.civil(2007, 2, 27).julian?.should == false
+ Date.civil(1607, 2, 27, Date.civil(1582, 1, 1).jd).julian?.should be_false
+ end
+
+end
diff --git a/spec/ruby/library/date/ld_spec.rb b/spec/ruby/library/date/ld_spec.rb
new file mode 100644
index 0000000000..73a47d2382
--- /dev/null
+++ b/spec/ruby/library/date/ld_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#ld" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/ld_to_jd_spec.rb b/spec/ruby/library/date/ld_to_jd_spec.rb
new file mode 100644
index 0000000000..37abe01449
--- /dev/null
+++ b/spec/ruby/library/date/ld_to_jd_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.ld_to_jd" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/leap_spec.rb b/spec/ruby/library/date/leap_spec.rb
new file mode 100644
index 0000000000..674b191c9f
--- /dev/null
+++ b/spec/ruby/library/date/leap_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#leap?" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "Date.leap?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/mday_spec.rb b/spec/ruby/library/date/mday_spec.rb
new file mode 100644
index 0000000000..53f6f98169
--- /dev/null
+++ b/spec/ruby/library/date/mday_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#mday" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/minus_month_spec.rb b/spec/ruby/library/date/minus_month_spec.rb
new file mode 100644
index 0000000000..563da073f0
--- /dev/null
+++ b/spec/ruby/library/date/minus_month_spec.rb
@@ -0,0 +1,23 @@
+require 'date'
+require_relative '../../spec_helper'
+
+describe "Date#<<" do
+
+ it "subtracts a number of months from a date" do
+ d = Date.civil(2007,2,27) << 10
+ d.should == Date.civil(2006, 4, 27)
+ end
+
+ it "returns the last day of a month if the day doesn't exist" do
+ d = Date.civil(2008,3,31) << 1
+ d.should == Date.civil(2008, 2, 29)
+ end
+
+ it "raises an error on non numeric parameters" do
+ lambda { Date.civil(2007,2,27) << :hello }.should raise_error(TypeError)
+ lambda { Date.civil(2007,2,27) << "hello" }.should raise_error(TypeError)
+ lambda { Date.civil(2007,2,27) << Date.new }.should raise_error(TypeError)
+ lambda { Date.civil(2007,2,27) << Object.new }.should raise_error(TypeError)
+ end
+
+end
diff --git a/spec/ruby/library/date/minus_spec.rb b/spec/ruby/library/date/minus_spec.rb
new file mode 100644
index 0000000000..4cf6129f02
--- /dev/null
+++ b/spec/ruby/library/date/minus_spec.rb
@@ -0,0 +1,30 @@
+require 'date'
+require_relative '../../spec_helper'
+
+describe "Date#-" do
+
+ it "subtracts a number of days from a Date" do
+ d = Date.civil(2007, 5 ,2) - 13
+ d.should == Date.civil(2007, 4, 19)
+ end
+
+ it "subtracts a negative number of days from a Date" do
+ d = Date.civil(2007, 4, 19).-(-13)
+ d.should == Date.civil(2007, 5 ,2)
+ end
+
+ it "computes the difference between two dates" do
+ (Date.civil(2007,2,27) - Date.civil(2007,2,27)).should == 0
+ (Date.civil(2007,2,27) - Date.civil(2007,2,26)).should == 1
+ (Date.civil(2006,2,27) - Date.civil(2007,2,27)).should == -365
+ (Date.civil(2008,2,27) - Date.civil(2007,2,27)).should == 365
+
+ end
+
+ it "raises an error for non Numeric arguments" do
+ lambda { Date.civil(2007,2,27) - :hello }.should raise_error(TypeError)
+ lambda { Date.civil(2007,2,27) - "hello" }.should raise_error(TypeError)
+ lambda { Date.civil(2007,2,27) - Object.new }.should raise_error(TypeError)
+ end
+
+end
diff --git a/spec/ruby/library/date/mjd_spec.rb b/spec/ruby/library/date/mjd_spec.rb
new file mode 100644
index 0000000000..6f03af346b
--- /dev/null
+++ b/spec/ruby/library/date/mjd_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#mjd" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/mjd_to_jd_spec.rb b/spec/ruby/library/date/mjd_to_jd_spec.rb
new file mode 100644
index 0000000000..2009261103
--- /dev/null
+++ b/spec/ruby/library/date/mjd_to_jd_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.mjd_to_jd" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/mon_spec.rb b/spec/ruby/library/date/mon_spec.rb
new file mode 100644
index 0000000000..724e7d6564
--- /dev/null
+++ b/spec/ruby/library/date/mon_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#mon" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/monday_spec.rb b/spec/ruby/library/date/monday_spec.rb
new file mode 100644
index 0000000000..14a117b73c
--- /dev/null
+++ b/spec/ruby/library/date/monday_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#monday?" do
+ it "should be monday" do
+ Date.new(2000, 1, 3).monday?.should be_true
+ end
+end
diff --git a/spec/ruby/library/date/month_spec.rb b/spec/ruby/library/date/month_spec.rb
new file mode 100644
index 0000000000..e040f9a94c
--- /dev/null
+++ b/spec/ruby/library/date/month_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#month" do
+ it "returns the month" do
+ m = Date.new(2000, 7, 1).month
+ m.should == 7
+ end
+end
diff --git a/spec/ruby/library/date/new_spec.rb b/spec/ruby/library/date/new_spec.rb
new file mode 100644
index 0000000000..18120118c0
--- /dev/null
+++ b/spec/ruby/library/date/new_spec.rb
@@ -0,0 +1,8 @@
+require 'date'
+require_relative '../../spec_helper'
+require_relative 'shared/civil'
+require_relative 'shared/new_bang'
+
+describe "Date.new" do
+ it_behaves_like :date_civil, :new
+end
diff --git a/spec/ruby/library/date/new_start_spec.rb b/spec/ruby/library/date/new_start_spec.rb
new file mode 100644
index 0000000000..aef78f2320
--- /dev/null
+++ b/spec/ruby/library/date/new_start_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#new_start" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/next_day_spec.rb b/spec/ruby/library/date/next_day_spec.rb
new file mode 100644
index 0000000000..3b066630e7
--- /dev/null
+++ b/spec/ruby/library/date/next_day_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#next_day" do
+ it "returns the next day" do
+ d = Date.new(2000, 1, 4).next_day
+ d.should == Date.new(2000, 1, 5)
+ end
+
+ it "returns three days later across months" do
+ d = Date.new(2000, 1, 30).next_day(3)
+ d.should == Date.new(2000, 2, 2)
+ end
+end
diff --git a/spec/ruby/library/date/next_month_spec.rb b/spec/ruby/library/date/next_month_spec.rb
new file mode 100644
index 0000000000..6ee664433f
--- /dev/null
+++ b/spec/ruby/library/date/next_month_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#next_month" do
+ it "returns the next month" do
+ d = Date.new(2000, 7, 1).next_month
+ d.should == Date.new(2000, 8, 1)
+ end
+
+ it "returns three months later" do
+ d = Date.new(2000, 7, 1).next_month(3)
+ d.should == Date.new(2000, 10, 1)
+ end
+
+ it "returns three months later across years" do
+ d = Date.new(2000, 12, 1).next_month(3)
+ d.should == Date.new(2001, 3, 1)
+ end
+
+ it "returns last day of month two months later" do
+ d = Date.new(2000, 1, 31).next_month(2)
+ d.should == Date.new(2000, 3, 31)
+ end
+
+ it "returns last day of next month when same day does not exist" do
+ d = Date.new(2001, 1, 30).next_month
+ d.should == Date.new(2001, 2, 28)
+ end
+end
diff --git a/spec/ruby/library/date/next_spec.rb b/spec/ruby/library/date/next_spec.rb
new file mode 100644
index 0000000000..8063d6a2e4
--- /dev/null
+++ b/spec/ruby/library/date/next_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#next" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/next_year_spec.rb b/spec/ruby/library/date/next_year_spec.rb
new file mode 100644
index 0000000000..dda9a44008
--- /dev/null
+++ b/spec/ruby/library/date/next_year_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#next_year" do
+ it "returns the day of the reform if date falls within calendar reform" do
+ calendar_reform_italy = Date.new(1582, 10, 4)
+ d1 = Date.new(1581, 10, 9).next_year
+ d2 = Date.new(1581, 10, 10).next_year
+ d1.should == calendar_reform_italy
+ d2.should == calendar_reform_italy
+ end
+end
diff --git a/spec/ruby/library/date/ordinal_spec.rb b/spec/ruby/library/date/ordinal_spec.rb
new file mode 100644
index 0000000000..ec490fd49c
--- /dev/null
+++ b/spec/ruby/library/date/ordinal_spec.rb
@@ -0,0 +1,7 @@
+require 'date'
+require_relative '../../spec_helper'
+require_relative 'shared/ordinal'
+
+describe "Date.ordinal" do
+ it_behaves_like :date_ordinal, :ordinal
+end
diff --git a/spec/ruby/library/date/ordinal_to_jd_spec.rb b/spec/ruby/library/date/ordinal_to_jd_spec.rb
new file mode 100644
index 0000000000..44f4b3321e
--- /dev/null
+++ b/spec/ruby/library/date/ordinal_to_jd_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.ordinal_to_jd" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/parse_spec.rb b/spec/ruby/library/date/parse_spec.rb
new file mode 100644
index 0000000000..09e072ba3e
--- /dev/null
+++ b/spec/ruby/library/date/parse_spec.rb
@@ -0,0 +1,142 @@
+require_relative '../../spec_helper'
+require_relative 'shared/parse'
+require_relative 'shared/parse_us'
+require_relative 'shared/parse_eu'
+require 'date'
+
+describe "Date#parse" do
+ # The space separator is also different, doesn't work for only numbers
+ it "parses a day name into a Date object" do
+ d = Date.parse("friday")
+ d.should == Date.commercial(d.cwyear, d.cweek, 5)
+ end
+
+ it "parses a month name into a Date object" do
+ d = Date.parse("october")
+ d.should == Date.civil(Date.today.year, 10)
+ end
+
+ it "parses a month day into a Date object" do
+ d = Date.parse("5th")
+ d.should == Date.civil(Date.today.year, Date.today.month, 5)
+ end
+
+ # Specs using numbers
+ it "throws an argument error for a single digit" do
+ lambda{ Date.parse("1") }.should raise_error(ArgumentError)
+ end
+
+ it "parses DD as month day number" do
+ d = Date.parse("10")
+ d.should == Date.civil(Date.today.year, Date.today.month, 10)
+ end
+
+ it "parses DDD as year day number" do
+ d = Date.parse("100")
+ if Date.gregorian_leap?(Date.today.year)
+ d.should == Date.civil(Date.today.year, 4, 9)
+ else
+ d.should == Date.civil(Date.today.year, 4, 10)
+ end
+ end
+
+ it "parses MMDD as month and day" do
+ d = Date.parse("1108")
+ d.should == Date.civil(Date.today.year, 11, 8)
+ end
+
+ it "parses YYDDD as year and day number in 1969--2068" do
+ d = Date.parse("10100")
+ d.should == Date.civil(2010, 4, 10)
+ end
+
+ it "parses YYMMDD as year, month and day in 1969--2068" do
+ d = Date.parse("201023")
+ d.should == Date.civil(2020, 10, 23)
+ end
+
+ it "parses YYYYDDD as year and day number" do
+ d = Date.parse("1910100")
+ d.should == Date.civil(1910, 4, 10)
+ end
+
+ it "parses YYYYMMDD as year, month and day number" do
+ d = Date.parse("19101101")
+ d.should == Date.civil(1910, 11, 1)
+ end
+
+ it "raises a TypeError trying to parse non-String-like object" do
+ lambda { Date.parse(1) }.should raise_error(TypeError)
+ lambda { Date.parse(:invalid) }.should raise_error(TypeError)
+ end
+end
+
+describe "Date#parse with '.' separator" do
+ before :all do
+ @sep = '.'
+ end
+
+ it_should_behave_like "date_parse"
+end
+
+describe "Date#parse with '/' separator" do
+ before :all do
+ @sep = '/'
+ end
+
+ it_should_behave_like "date_parse"
+end
+
+describe "Date#parse with ' ' separator" do
+ before :all do
+ @sep = ' '
+ end
+
+ it_should_behave_like "date_parse"
+end
+
+describe "Date#parse with '/' separator US-style" do
+ before :all do
+ @sep = '/'
+ end
+
+ it_should_behave_like "date_parse_us"
+end
+
+describe "Date#parse with '-' separator EU-style" do
+ before :all do
+ @sep = '-'
+ end
+
+ it_should_behave_like "date_parse_eu"
+end
+
+describe "Date#parse(.)" do
+ it "parses YYYY.MM.DD into a Date object" do
+ d = Date.parse("2007.10.01")
+ d.year.should == 2007
+ d.month.should == 10
+ d.day.should == 1
+ end
+
+ it "parses DD.MM.YYYY into a Date object" do
+ d = Date.parse("10.01.2007")
+ d.year.should == 2007
+ d.month.should == 1
+ d.day.should == 10
+ end
+
+ it "parses YY.MM.DD into a Date object using the year 20YY" do
+ d = Date.parse("10.01.07")
+ d.year.should == 2010
+ d.month.should == 1
+ d.day.should == 7
+ end
+
+ it "parses YY.MM.DD using the year digits as 20YY when given true as additional argument" do
+ d = Date.parse("10.01.07", true)
+ d.year.should == 2010
+ d.month.should == 1
+ d.day.should == 7
+ end
+end
diff --git a/spec/ruby/library/date/plus_spec.rb b/spec/ruby/library/date/plus_spec.rb
new file mode 100644
index 0000000000..ce5e074ed7
--- /dev/null
+++ b/spec/ruby/library/date/plus_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#+" do
+ before :all do
+ @date = Date.civil(2000, 1, 1)
+ end
+
+ it "returns a new Date object that is n days later than the current one" do
+ (@date + 31).should == Date.civil(2000, 2, 1)
+ end
+
+ it "accepts a negative argument and returns a new Date that is earlier than the current one" do
+ (@date + -1).should == Date.civil(1999, 12, 31)
+ end
+
+ it "raises TypeError if argument is not Numeric" do
+ lambda { Date.today + Date.today }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/date/prev_day_spec.rb b/spec/ruby/library/date/prev_day_spec.rb
new file mode 100644
index 0000000000..cce24da875
--- /dev/null
+++ b/spec/ruby/library/date/prev_day_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#prev_day" do
+ it "returns previous day" do
+ d = Date.new(2000, 7, 2).prev_day
+ d.should == Date.new(2000, 7, 1)
+ end
+
+ it "returns three days ago across months" do
+ d = Date.new(2000, 7, 2).prev_day(3)
+ d.should == Date.new(2000, 6, 29)
+ end
+end
diff --git a/spec/ruby/library/date/prev_month_spec.rb b/spec/ruby/library/date/prev_month_spec.rb
new file mode 100644
index 0000000000..3d0d1d437d
--- /dev/null
+++ b/spec/ruby/library/date/prev_month_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#prev_month" do
+ it "returns previous month" do
+ d = Date.new(2000, 9, 1).prev_month
+ d.should == Date.new(2000, 8, 1)
+ end
+
+ it "returns three months ago" do
+ d = Date.new(2000, 10, 1).prev_month(3)
+ d.should == Date.new(2000, 7, 1)
+ end
+
+ it "returns three months ago across years" do
+ d = Date.new(2000, 1, 1).prev_month(3)
+ d.should == Date.new(1999, 10, 1)
+ end
+
+ it "returns last day of month two months ago" do
+ d = Date.new(2000, 3, 31).prev_month(2)
+ d.should == Date.new(2000, 1, 31)
+ end
+
+ it "returns last day of previous month when same day does not exist" do
+ d = Date.new(2001, 3, 30).prev_month
+ d.should == Date.new(2001, 2, 28)
+ end
+end
diff --git a/spec/ruby/library/date/prev_year_spec.rb b/spec/ruby/library/date/prev_year_spec.rb
new file mode 100644
index 0000000000..ba06dd198b
--- /dev/null
+++ b/spec/ruby/library/date/prev_year_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#prev_year" do
+ it "returns the day of the reform if date falls within calendar reform" do
+ calendar_reform_italy = Date.new(1582, 10, 4)
+ d1 = Date.new(1583, 10, 9).prev_year
+ d2 = Date.new(1583, 10, 10).prev_year
+ d1.should == calendar_reform_italy
+ d2.should == calendar_reform_italy
+ end
+end
diff --git a/spec/ruby/library/date/relationship_spec.rb b/spec/ruby/library/date/relationship_spec.rb
new file mode 100644
index 0000000000..979516e164
--- /dev/null
+++ b/spec/ruby/library/date/relationship_spec.rb
@@ -0,0 +1,20 @@
+require 'date'
+require_relative '../../spec_helper'
+
+describe "Date#===" do
+
+ it "returns 0 when comparing two equal dates" do
+ (Date.civil(2000, 04, 06) <=> Date.civil(2000, 04, 06)).should == 0
+ end
+
+ it "computes the difference between two dates" do
+ (Date.civil(2000, 04, 05) <=> Date.civil(2000, 04, 06)).should == -1
+ (Date.civil(2001, 04, 05) <=> Date.civil(2000, 04, 06)).should == 1
+ end
+
+ it "compares to another numeric" do
+ (Date.civil(2000, 04, 05) <=> Date.civil(2000, 04, 06).jd).should == -1
+ (Date.civil(2001, 04, 05) <=> Date.civil(2000, 04, 06).jd).should == 1
+ end
+
+end
diff --git a/spec/ruby/library/date/right_shift_spec.rb b/spec/ruby/library/date/right_shift_spec.rb
new file mode 100644
index 0000000000..bd7de0e3d5
--- /dev/null
+++ b/spec/ruby/library/date/right_shift_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#>>" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/saturday_spec.rb b/spec/ruby/library/date/saturday_spec.rb
new file mode 100644
index 0000000000..1527b71d00
--- /dev/null
+++ b/spec/ruby/library/date/saturday_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#saturday?" do
+ it "should be saturday" do
+ Date.new(2000, 1, 1).saturday?.should be_true
+ end
+end
diff --git a/spec/ruby/library/date/shared/civil.rb b/spec/ruby/library/date/shared/civil.rb
new file mode 100644
index 0000000000..47dbed49fc
--- /dev/null
+++ b/spec/ruby/library/date/shared/civil.rb
@@ -0,0 +1,57 @@
+describe :date_civil, shared: true do
+ it "creates a Date for -4712 by default" do
+ # the #chomp calls are necessary because of RSpec
+ d = Date.send(@method)
+ d.year.should == -4712
+ d.month.should == 1
+ d.day.should == 1
+ d.julian?.should == true
+ d.jd.should == 0
+ end
+
+ it "creates a date with arguments" do
+ d = Date.send(@method, 2000, 3, 5)
+ d.year.should == 2000
+ d.month.should == 3
+ d.day.should == 5
+ d.julian?.should == false
+ d.jd.should == 2451609
+
+ # Should also work with years far in the past and future
+
+ d = Date.send(@method, -9000, 7, 5)
+ d.year.should == -9000
+ d.month.should == 7
+ d.day.should == 5
+ d.julian?.should == true
+ d.jd.should == -1566006
+
+ d = Date.send(@method, 9000, 10, 14)
+ d.year.should == 9000
+ d.month.should == 10
+ d.day.should == 14
+ d.julian?.should == false
+ d.jd.should == 5008529
+
+ end
+
+ it "doesn't create dates for invalid arguments" do
+ lambda { Date.send(@method, 2000, 13, 31) }.should raise_error(ArgumentError)
+ lambda { Date.send(@method, 2000, 12, 32) }.should raise_error(ArgumentError)
+ lambda { Date.send(@method, 2000, 2, 30) }.should raise_error(ArgumentError)
+ lambda { Date.send(@method, 1900, 2, 29) }.should raise_error(ArgumentError)
+ lambda { Date.send(@method, 2000, 2, 29) }.should_not raise_error(ArgumentError)
+
+ lambda { Date.send(@method, 1582, 10, 14) }.should raise_error(ArgumentError)
+ lambda { Date.send(@method, 1582, 10, 15) }.should_not raise_error(ArgumentError)
+
+ end
+
+ it "creates a Date for different calendar reform dates" do
+ d1 = Date.send(@method, 1582, 10, 4)
+ d1.succ.day.should == 15
+
+ d2 = Date.send(@method, 1582, 10, 4, Date::ENGLAND)
+ d2.succ.day.should == 5
+ end
+end
diff --git a/spec/ruby/library/date/shared/commercial.rb b/spec/ruby/library/date/shared/commercial.rb
new file mode 100644
index 0000000000..354a5d5cd0
--- /dev/null
+++ b/spec/ruby/library/date/shared/commercial.rb
@@ -0,0 +1,39 @@
+describe :date_commercial, shared: true do
+ it "creates a Date for Julian Day Number day 0 by default" do
+ d = Date.send(@method)
+ d.year.should == -4712
+ d.month.should == 1
+ d.day.should == 1
+ end
+
+ it "creates a Date for the monday in the year and week given" do
+ d = Date.send(@method, 2000, 1)
+ d.year.should == 2000
+ d.month.should == 1
+ d.day.should == 3
+ d.cwday.should == 1
+ end
+
+ it "creates a Date for the correct day given the year, week and day number" do
+ d = Date.send(@method, 2004, 1, 1)
+ d.year.should == 2003
+ d.month.should == 12
+ d.day.should == 29
+ d.cwday.should == 1
+ d.cweek.should == 1
+ d.cwyear.should == 2004
+ end
+
+ it "creates only Date objects for valid weeks" do
+ lambda { Date.send(@method, 2004, 53, 1) }.should_not raise_error(ArgumentError)
+ lambda { Date.send(@method, 2004, 53, 0) }.should raise_error(ArgumentError)
+ lambda { Date.send(@method, 2004, 53, 8) }.should raise_error(ArgumentError)
+ lambda { Date.send(@method, 2004, 54, 1) }.should raise_error(ArgumentError)
+ lambda { Date.send(@method, 2004, 0, 1) }.should raise_error(ArgumentError)
+
+ lambda { Date.send(@method, 2003, 52, 1) }.should_not raise_error(ArgumentError)
+ lambda { Date.send(@method, 2003, 53, 1) }.should raise_error(ArgumentError)
+ lambda { Date.send(@method, 2003, 52, 0) }.should raise_error(ArgumentError)
+ lambda { Date.send(@method, 2003, 52, 8) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/date/shared/jd.rb b/spec/ruby/library/date/shared/jd.rb
new file mode 100644
index 0000000000..511557b4f7
--- /dev/null
+++ b/spec/ruby/library/date/shared/jd.rb
@@ -0,0 +1,14 @@
+describe :date_jd, shared: true do
+ it "constructs a Date object if passed a Julian day" do
+ Date.send(@method, 2454482).should == Date.civil(2008, 1, 16)
+ end
+
+ it "returns a Date object representing Julian day 0 (-4712-01-01) if no arguments passed" do
+ Date.send(@method).should == Date.civil(-4712, 1, 1)
+ end
+
+ it "constructs a Date object if passed a negative number" do
+ Date.send(@method, -1).should == Date.civil(-4713, 12, 31)
+ end
+
+end
diff --git a/spec/ruby/library/date/shared/new_bang.rb b/spec/ruby/library/date/shared/new_bang.rb
new file mode 100644
index 0000000000..90f1b432f0
--- /dev/null
+++ b/spec/ruby/library/date/shared/new_bang.rb
@@ -0,0 +1,14 @@
+describe :date_new_bang, shared: true do
+
+ it "returns a new Date object set to Astronomical Julian Day 0 if no arguments passed" do
+ d = Date.send(@method)
+ d.ajd.should == 0
+ end
+
+ it "accepts astronomical julian day number, offset as a fraction of a day and returns a new Date object" do
+ d = Date.send(@method, 10, 0.5)
+ d.ajd.should == 10
+ d.jd.should == 11
+ end
+
+end
diff --git a/spec/ruby/library/date/shared/ordinal.rb b/spec/ruby/library/date/shared/ordinal.rb
new file mode 100644
index 0000000000..4b182d5a25
--- /dev/null
+++ b/spec/ruby/library/date/shared/ordinal.rb
@@ -0,0 +1,22 @@
+# reference:
+# October 1582 (the Gregorian calendar, Civil Date)
+# 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
+
+describe :date_ordinal, shared: true do
+ it "constructs a Date object from an ordinal date" do
+ # October 1582 (the Gregorian calendar, Ordinal Date)
+ # S M Tu W Th F S
+ # 274 275 276 277 278 279
+ # 280 281 282 283 284 285 286
+ # 287 288 289 290 291 292 293
+ # 294
+ Date.send(@method, 1582, 274).should == Date.civil(1582, 10, 1)
+ Date.send(@method, 1582, 277).should == Date.civil(1582, 10, 4)
+ Date.send(@method, 1582, 278).should == Date.civil(1582, 10, 15)
+ Date.send(@method, 1582, 287, Date::ENGLAND).should == Date.civil(1582, 10, 14, Date::ENGLAND)
+ end
+end
diff --git a/spec/ruby/library/date/shared/parse.rb b/spec/ruby/library/date/shared/parse.rb
new file mode 100644
index 0000000000..1015285e04
--- /dev/null
+++ b/spec/ruby/library/date/shared/parse.rb
@@ -0,0 +1,54 @@
+describe :date_parse, shared: true do
+ it "can parse a mmm-YYYY string into a Date object" do
+ d = Date.parse("feb#{@sep}2008")
+ d.year.should == 2008
+ d.month.should == 2
+ d.day.should == 1
+ end
+
+ it "can parse a 'DD mmm YYYY' string into a Date object" do
+ d = Date.parse("23#{@sep}feb#{@sep}2008")
+ d.year.should == 2008
+ d.month.should == 2
+ d.day.should == 23
+ end
+
+ it "can parse a 'mmm DD YYYY' string into a Date object" do
+ d = Date.parse("23#{@sep}feb#{@sep}2008")
+ d.year.should == 2008
+ d.month.should == 2
+ d.day.should == 23
+ end
+
+ it "can parse a 'YYYY mmm DD' string into a Date object" do
+ d = Date.parse("2008#{@sep}feb#{@sep}23")
+ d.year.should == 2008
+ d.month.should == 2
+ d.day.should == 23
+ end
+
+ it "can parse a month name and day into a Date object" do
+ d = Date.parse("november#{@sep}5th")
+ d.should == Date.civil(Date.today.year, 11, 5)
+ end
+
+ it "can parse a month name, day and year into a Date object" do
+ d = Date.parse("november#{@sep}5th#{@sep}2005")
+ d.should == Date.civil(2005, 11, 5)
+ end
+
+ it "can parse a year, month name and day into a Date object" do
+ d = Date.parse("2005#{@sep}november#{@sep}5th")
+ d.should == Date.civil(2005, 11, 5)
+ end
+
+ it "can parse a year, day and month name into a Date object" do
+ d = Date.parse("5th#{@sep}november#{@sep}2005")
+ d.should == Date.civil(2005, 11, 5)
+ end
+
+ it "can handle negative year numbers" do
+ d = Date.parse("5th#{@sep}november#{@sep}-2005")
+ d.should == Date.civil(-2005, 11, 5)
+ end
+end
diff --git a/spec/ruby/library/date/shared/parse_eu.rb b/spec/ruby/library/date/shared/parse_eu.rb
new file mode 100644
index 0000000000..ecb15e3c0e
--- /dev/null
+++ b/spec/ruby/library/date/shared/parse_eu.rb
@@ -0,0 +1,37 @@
+describe :date_parse_eu, shared: true do
+ # The - separator let's it work like European format, so it as a different spec
+ it "can parse a YYYY-MM-DD string into a Date object" do
+ d = Date.parse("2007#{@sep}10#{@sep}01")
+ d.year.should == 2007
+ d.month.should == 10
+ d.day.should == 1
+ end
+
+ it "can parse a MM-DD-YYYY string into a Date object" do
+ d = Date.parse("10#{@sep}01#{@sep}2007")
+ d.year.should == 2007
+ d.month.should == 1
+ d.day.should == 10
+ end
+
+ it "can parse a MM-DD-YY string into a Date object" do
+ d = Date.parse("10#{@sep}01#{@sep}07")
+ d.year.should == 2010
+ d.month.should == 1
+ d.day.should == 7
+ end
+
+ it "can parse a MM-DD-YY string into a Date object NOT using the year digits as 20XX" do
+ d = Date.parse("10#{@sep}01#{@sep}07", false)
+ d.year.should == 10
+ d.month.should == 1
+ d.day.should == 7
+ end
+
+ it "can parse a MM-DD-YY string into a Date object using the year digits as 20XX" do
+ d = Date.parse("10#{@sep}01#{@sep}07", true)
+ d.year.should == 2010
+ d.month.should == 1
+ d.day.should == 7
+ end
+end
diff --git a/spec/ruby/library/date/shared/parse_us.rb b/spec/ruby/library/date/shared/parse_us.rb
new file mode 100644
index 0000000000..7be62b1af1
--- /dev/null
+++ b/spec/ruby/library/date/shared/parse_us.rb
@@ -0,0 +1,36 @@
+describe :date_parse_us, shared: true do
+ it "parses a YYYY#{@sep}MM#{@sep}DD string into a Date object" do
+ d = Date.parse("2007#{@sep}10#{@sep}01")
+ d.year.should == 2007
+ d.month.should == 10
+ d.day.should == 1
+ end
+
+ it "parses a MM#{@sep}DD#{@sep}YYYY string into a Date object" do
+ d = Date.parse("10#{@sep}01#{@sep}2007")
+ d.year.should == 2007
+ d.month.should == 1
+ d.day.should == 10
+ end
+
+ it "parses a MM#{@sep}DD#{@sep}YY string into a Date object" do
+ d = Date.parse("10#{@sep}01#{@sep}07")
+ d.year.should == 2010
+ d.month.should == 1
+ d.day.should == 7
+ end
+
+ it "parses a MM#{@sep}DD#{@sep}YY string into a Date object NOT using the year digits as 20XX" do
+ d = Date.parse("10#{@sep}01#{@sep}07", false)
+ d.year.should == 10
+ d.month.should == 1
+ d.day.should == 7
+ end
+
+ it "parses a MM#{@sep}DD#{@sep}YY string into a Date object using the year digits as 20XX" do
+ d = Date.parse("10#{@sep}01#{@sep}07", true)
+ d.year.should == 2010
+ d.month.should == 1
+ d.day.should == 7
+ end
+end
diff --git a/spec/ruby/library/date/shared/valid_civil.rb b/spec/ruby/library/date/shared/valid_civil.rb
new file mode 100644
index 0000000000..545c207bbe
--- /dev/null
+++ b/spec/ruby/library/date/shared/valid_civil.rb
@@ -0,0 +1,36 @@
+describe :date_valid_civil?, shared: true do
+
+ # reference:
+ # October 1582 (the Gregorian calendar, Civil Date)
+ # 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
+
+ it "returns true if it is a valid civil date" do
+ Date.send(@method, 1582, 10, 15).should be_true
+ Date.send(@method, 1582, 10, 14, Date::ENGLAND).should be_true
+ end
+
+ it "returns false if it is not a valid civil date" do
+ Date.send(@method, 1582, 10, 14).should == false
+ end
+
+ it "handles negative months and days" do
+ # October 1582 (the Gregorian calendar, Civil Date)
+ # S M Tu W Th F S
+ # -21 -20 -19 -18 -17 -16
+ # -15 -14 -13 -12 -11 -10 -9
+ # -8 -7 -6 -5 -4 -3 -2
+ # -1
+ Date.send(@method, 1582, -3, -22).should be_false
+ Date.send(@method, 1582, -3, -21).should be_true
+ Date.send(@method, 1582, -3, -18).should be_true
+ Date.send(@method, 1582, -3, -17).should be_true
+
+ Date.send(@method, 2007, -11, -10).should be_true
+ Date.send(@method, 2008, -11, -10).should be_true
+ end
+
+end
diff --git a/spec/ruby/library/date/shared/valid_commercial.rb b/spec/ruby/library/date/shared/valid_commercial.rb
new file mode 100644
index 0000000000..117dfe1d3d
--- /dev/null
+++ b/spec/ruby/library/date/shared/valid_commercial.rb
@@ -0,0 +1,34 @@
+describe :date_valid_commercial?, shared: true do
+
+ it "returns true if it is a valid commercial date" do
+ # October 1582 (the Gregorian calendar, Commercial Date)
+ # M Tu W Th F Sa Su
+ # 39: 1 2 3 4 5 6 7
+ # 40: 1 2 3 4 5 6 7
+ # 41: 1 2 3 4 5 6 7
+ Date.send(@method, 1582, 39, 4).should be_true
+ Date.send(@method, 1582, 39, 5).should be_true
+ Date.send(@method, 1582, 41, 4).should be_true
+ Date.send(@method, 1582, 41, 5).should be_true
+ Date.send(@method, 1582, 41, 4, Date::ENGLAND).should be_true
+ Date.send(@method, 1752, 37, 4, Date::ENGLAND).should be_true
+ end
+
+ it "returns false it is not a valid commercial date" do
+ Date.send(@method, 1999, 53, 1).should be_false
+ end
+
+ it "handles negative week and day numbers" do
+ # October 1582 (the Gregorian calendar, Commercial Date)
+ # M Tu W Th F Sa Su
+ # -12: -7 -6 -5 -4 -3 -2 -1
+ # -11: -7 -6 -5 -4 -3 -2 -1
+ # -10: -7 -6 -5 -4 -3 -2 -1
+ Date.send(@method, 1582, -12, -4).should be_true
+ Date.send(@method, 1582, -12, -3).should be_true
+ Date.send(@method, 2007, -44, -2).should be_true
+ Date.send(@method, 2008, -44, -2).should be_true
+ Date.send(@method, 1999, -53, -1).should be_false
+ end
+
+end
diff --git a/spec/ruby/library/date/shared/valid_jd.rb b/spec/ruby/library/date/shared/valid_jd.rb
new file mode 100644
index 0000000000..bd71f5abba
--- /dev/null
+++ b/spec/ruby/library/date/shared/valid_jd.rb
@@ -0,0 +1,15 @@
+describe :date_valid_jd?, shared: true do
+ it "returns true if passed any value other than nil" do
+ Date.send(@method, -100).should be_true
+ Date.send(@method, :number).should be_true
+ Date.send(@method, Rational(1,2)).should be_true
+ end
+
+ it "returns false if passed nil" do
+ Date.send(@method, nil).should be_false
+ end
+
+ it "returns true if passed false" do
+ Date.send(@method, false).should be_true
+ end
+end
diff --git a/spec/ruby/library/date/shared/valid_ordinal.rb b/spec/ruby/library/date/shared/valid_ordinal.rb
new file mode 100644
index 0000000000..1ed961be23
--- /dev/null
+++ b/spec/ruby/library/date/shared/valid_ordinal.rb
@@ -0,0 +1,26 @@
+describe :date_valid_ordinal?, shared: true do
+ it "determines if the date is a valid ordinal date" do
+ # October 1582 (the Gregorian calendar, Ordinal Date)
+ # S M Tu W Th F S
+ # 274 275 276 277 278 279
+ # 280 281 282 283 284 285 286
+ # 287 288 289 290 291 292 293
+ # 294
+ Date.send(@method, 1582, 277).should == true
+ Date.send(@method, 1582, 278).should == true
+ Date.send(@method, 1582, 287).should == true
+ Date.send(@method, 1582, 288).should == true
+ end
+
+ it "handles negative day numbers" do
+ # October 1582 (the Gregorian calendar, Ordinal Date)
+ # S M Tu W Th F S
+ # -82 -81 -80 -79 -78 -77
+ # -76 -75 -74 -73 -72 -71 -70
+ # -69 -68 -67 -66 -65 -64 -63
+ # -62
+ Date.send(@method, 1582, -79).should == true
+ Date.send(@method, 1582, -78).should == true
+ Date.send(@method, 2007, -100).should == true
+ end
+end
diff --git a/spec/ruby/library/date/start_spec.rb b/spec/ruby/library/date/start_spec.rb
new file mode 100644
index 0000000000..8ba272a7f3
--- /dev/null
+++ b/spec/ruby/library/date/start_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#start" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/step_spec.rb b/spec/ruby/library/date/step_spec.rb
new file mode 100644
index 0000000000..6bbd671840
--- /dev/null
+++ b/spec/ruby/library/date/step_spec.rb
@@ -0,0 +1,56 @@
+require 'date'
+require_relative '../../spec_helper'
+
+describe "Date#step" do
+
+ it "steps forward in time" do
+ ds = Date.civil(2008, 10, 11)
+ de = Date.civil(2008, 9, 29)
+ count = 0
+ de.step(ds) do |d|
+ d.should <= ds
+ d.should >= de
+ count += 1
+ end
+ count.should == 13
+
+ count = 0
+ de.step(ds, 5) do |d|
+ d.should <= ds
+ d.should >= de
+ count += 1
+ end
+ count.should == 3
+
+ count = 0
+ ds.step(de) do |d|; count += 1; end
+ count.should == 0
+
+ end
+
+ it "steps backward in time" do
+ ds = Date.civil(2000, 4, 14)
+ de = Date.civil(2000, 3, 29)
+ count = 0
+ ds.step(de, -1) do |d|
+ d.should <= ds
+ d.should >= de
+ count += 1
+ end
+ count.should == 17
+
+ count = 0
+ ds.step(de, -5) do |d|
+ d.should <= ds
+ d.should >= de
+ count += 1
+ end
+ count.should == 4
+
+ count = 0
+ de.step(ds, -1) do |d|; count += 1; end
+ count.should == 0
+
+ end
+
+end
diff --git a/spec/ruby/library/date/strftime_spec.rb b/spec/ruby/library/date/strftime_spec.rb
new file mode 100644
index 0000000000..973d4668f8
--- /dev/null
+++ b/spec/ruby/library/date/strftime_spec.rb
@@ -0,0 +1,40 @@
+require 'date'
+require_relative '../../shared/time/strftime_for_date'
+
+describe "Date#strftime" do
+ before :all do
+ @new_date = lambda { |y,m,d| Date.civil(y,m,d) }
+
+ @date = Date.civil(2000, 4, 9)
+ end
+
+ it_behaves_like :strftime_date, :strftime
+
+ # Differences with Time
+ it "should be able to print the date with no argument" do
+ @date.strftime.should == "2000-04-09"
+ @date.strftime.should == @date.to_s
+ end
+
+ # %Z is %:z for Date/DateTime
+ it "should be able to show the timezone with a : separator" do
+ @date.strftime("%Z").should == "+00:00"
+ end
+
+ # %v is %e-%b-%Y for Date/DateTime
+ it "should be able to show the commercial week" do
+ @date.strftime("%v").should == " 9-Apr-2000"
+ @date.strftime("%v").should == @date.strftime('%e-%b-%Y')
+ end
+
+ # additional conversion specifiers only in Date/DateTime
+ it 'shows the number of milliseconds since epoch' do
+ DateTime.new(1970, 1, 1).strftime('%Q').should == "0"
+ @date.strftime("%Q").should == "955238400000"
+ end
+
+ it "should be able to show a full notation" do
+ @date.strftime("%+").should == "Sun Apr 9 00:00:00 +00:00 2000"
+ @date.strftime("%+").should == @date.strftime('%a %b %e %H:%M:%S %Z %Y')
+ end
+end
diff --git a/spec/ruby/library/date/strptime_spec.rb b/spec/ruby/library/date/strptime_spec.rb
new file mode 100644
index 0000000000..c90721751e
--- /dev/null
+++ b/spec/ruby/library/date/strptime_spec.rb
@@ -0,0 +1,149 @@
+require 'date'
+require_relative '../../spec_helper'
+
+describe "Date#strptime" do
+
+ it "returns January 1, 4713 BCE when given no arguments" do
+ Date.strptime.should == Date.civil(-4712, 1, 1)
+ end
+
+ it "uses the default format when not given a date format" do
+ Date.strptime("2000-04-06").should == Date.civil(2000, 4, 6)
+ Date.civil(2000, 4, 6).strftime.should == Date.civil(2000, 4, 6).to_s
+ end
+
+ it "parses a full day name" do
+ d = Date.today
+ expected_date = Date.commercial(d.cwyear, d.cweek, 4)
+ # strptime assumed week that start on sunday, not monday
+ expected_date += 7 if d.cwday == 7
+ Date.strptime("Thursday", "%A").should == expected_date
+ end
+
+ it "parses a short day name" do
+ d = Date.today
+ expected_date = Date.commercial(d.cwyear, d.cweek, 4)
+ # strptime assumed week that start on sunday, not monday
+ expected_date += 7 if d.cwday == 7
+ Date.strptime("Thu", "%a").should == expected_date
+ end
+
+ it "parses a full month name" do
+ d = Date.today
+ Date.strptime("April", "%B").should == Date.civil(d.year, 4, 1)
+ end
+
+ it "parses a short month name" do
+ d = Date.today
+ Date.strptime("Apr", "%b").should == Date.civil(d.year, 4, 1)
+ Date.strptime("Apr", "%h").should == Date.civil(d.year, 4, 1)
+ end
+
+ it "parses a century" do
+ Date.strptime("06 20", "%y %C").should == Date.civil(2006, 1, 1)
+ end
+
+ it "parses a month day with leading zeroes" do
+ d = Date.today
+ Date.strptime("06", "%d").should == Date.civil(d.year, d.month, 6)
+ end
+
+ it "parses a month day with leading spaces" do
+ d = Date.today
+ Date.strptime(" 6", "%e").should == Date.civil(d.year, d.month, 6)
+ end
+
+ it "parses a commercial year with leading zeroes" do
+ Date.strptime("2000", "%G").should == Date.civil(2000, 1, 3)
+ Date.strptime("2002", "%G").should == Date.civil(2001, 12, 31)
+ end
+
+ it "parses a commercial year with only two digits" do
+ Date.strptime("68", "%g").should == Date.civil(2068, 1, 2)
+ Date.strptime("69", "%g").should == Date.civil(1968, 12, 30)
+ end
+
+ it "parses a year day with leading zeroes" do
+ d = Date.today
+ if Date.gregorian_leap?(Date.today.year)
+ Date.strptime("097", "%j").should == Date.civil(d.year, 4, 6)
+ else
+ Date.strptime("097", "%j").should == Date.civil(d.year, 4, 7)
+ end
+ end
+
+ it "parses a month with leading zeroes" do
+ d = Date.today
+ Date.strptime("04", "%m").should == Date.civil(d.year, 4, 1)
+ end
+
+ it "parses a week number for a week starting on Sunday" do
+ Date.strptime("2010/1", "%Y/%U").should == Date.civil(2010, 1, 3)
+ end
+
+ # See http://redmine.ruby-lang.org/repositories/diff/ruby-19?rev=24500
+ it "parses a week number for a week starting on Monday" do
+ Date.strptime("2010/1", "%Y/%W").should == Date.civil(2010, 1, 4)
+ end
+
+ it "parses a commercial week day" do
+ Date.strptime("2008 1", "%G %u").should == Date.civil(2007, 12, 31)
+ end
+
+ it "parses a commercial week" do
+ d = Date.commercial(Date.today.cwyear,1,1)
+ Date.strptime("1", "%V").should == d
+ Date.strptime("15", "%V").should == Date.commercial(d.cwyear, 15, 1)
+ end
+
+ it "parses a week day" do
+ Date.strptime("2007 4", "%Y %w").should == Date.civil(2007, 1, 4)
+ end
+
+ it "parses a year in YYYY format" do
+ Date.strptime("2007", "%Y").should == Date.civil(2007, 1, 1)
+ end
+
+ it "parses a year in YY format" do
+ Date.strptime("00", "%y").should == Date.civil(2000, 1, 1)
+ end
+
+ ############################
+ # Specs that combine stuff #
+ ############################
+
+ it "parses a full date" do
+ Date.strptime("Thu Apr 6 00:00:00 2000", "%c").should == Date.civil(2000, 4, 6)
+ Date.strptime("Thu Apr 6 00:00:00 2000", "%a %b %e %H:%M:%S %Y").should == Date.civil(2000, 4, 6)
+ end
+
+ it "parses a date with slashes" do
+ Date.strptime("04/06/00", "%D").should == Date.civil(2000, 4, 6)
+ Date.strptime("04/06/00", "%m/%d/%y").should == Date.civil(2000, 4, 6)
+ end
+
+ it "parses a date given as YYYY-MM-DD" do
+ Date.strptime("2000-04-06", "%F").should == Date.civil(2000, 4, 6)
+ Date.strptime("2000-04-06", "%Y-%m-%d").should == Date.civil(2000, 4, 6)
+ end
+
+ it "parses a commercial week" do
+ Date.strptime(" 9-Apr-2000", "%v").should == Date.civil(2000, 4, 9)
+ Date.strptime(" 9-Apr-2000", "%e-%b-%Y").should == Date.civil(2000, 4, 9)
+ end
+
+ it "parses a date given MM/DD/YY" do
+ Date.strptime("04/06/00", "%x").should == Date.civil(2000, 4, 6)
+ Date.strptime("04/06/00", "%m/%d/%y").should == Date.civil(2000, 4, 6)
+ end
+
+ it "parses a date given in full notation" do
+ Date.strptime("Sun Apr 9 00:00:00 +00:00 2000", "%+").should == Date.civil(2000, 4, 9)
+ Date.strptime("Sun Apr 9 00:00:00 +00:00 2000", "%a %b %e %H:%M:%S %Z %Y").should == Date.civil(2000, 4, 9)
+ end
+
+end
+
+describe "Date.strptime" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/succ_spec.rb b/spec/ruby/library/date/succ_spec.rb
new file mode 100644
index 0000000000..c4a902aa63
--- /dev/null
+++ b/spec/ruby/library/date/succ_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#succ" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/sunday_spec.rb b/spec/ruby/library/date/sunday_spec.rb
new file mode 100644
index 0000000000..c3a817fa86
--- /dev/null
+++ b/spec/ruby/library/date/sunday_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#sunday?" do
+ it "should be sunday" do
+ Date.new(2000, 1, 2).sunday?.should be_true
+ end
+end
diff --git a/spec/ruby/library/date/thursday_spec.rb b/spec/ruby/library/date/thursday_spec.rb
new file mode 100644
index 0000000000..74b5f40365
--- /dev/null
+++ b/spec/ruby/library/date/thursday_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#thursday?" do
+ it "should be thursday" do
+ Date.new(2000, 1, 6).thursday?.should be_true
+ end
+end
diff --git a/spec/ruby/library/date/time_to_day_fraction_spec.rb b/spec/ruby/library/date/time_to_day_fraction_spec.rb
new file mode 100644
index 0000000000..e59980e036
--- /dev/null
+++ b/spec/ruby/library/date/time_to_day_fraction_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.time_to_day_fraction" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/to_s_spec.rb b/spec/ruby/library/date/to_s_spec.rb
new file mode 100644
index 0000000000..fe7cb19a46
--- /dev/null
+++ b/spec/ruby/library/date/to_s_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#to_s" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/today_spec.rb b/spec/ruby/library/date/today_spec.rb
new file mode 100644
index 0000000000..7c6ebc9cb4
--- /dev/null
+++ b/spec/ruby/library/date/today_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.today" do
+ it "returns a Date object" do
+ Date.today.should be_kind_of Date
+ end
+
+ it "sets Date object to the current date" do
+ today = Date.today
+ now = Time.now
+ (now - today.to_time).should be_close(0.0, 24 * 60 * 60)
+ end
+end
diff --git a/spec/ruby/library/date/tuesday_spec.rb b/spec/ruby/library/date/tuesday_spec.rb
new file mode 100644
index 0000000000..052837b54e
--- /dev/null
+++ b/spec/ruby/library/date/tuesday_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#tuesday?" do
+ it "should be tuesday" do
+ Date.new(2000, 1, 4).tuesday?.should be_true
+ end
+end
diff --git a/spec/ruby/library/date/upto_spec.rb b/spec/ruby/library/date/upto_spec.rb
new file mode 100644
index 0000000000..8745be85b3
--- /dev/null
+++ b/spec/ruby/library/date/upto_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#upto" do
+ it "returns future dates for the default step value" do
+ ds = Date.civil(2008, 10, 11)
+ de = Date.civil(2008, 9, 29)
+ count = 0
+ de.upto(ds) do |d|
+ d.should <= ds
+ d.should >= de
+ count += 1
+ end
+ count.should == 13
+ end
+end
diff --git a/spec/ruby/library/date/valid_civil_spec.rb b/spec/ruby/library/date/valid_civil_spec.rb
new file mode 100644
index 0000000000..00f2c57205
--- /dev/null
+++ b/spec/ruby/library/date/valid_civil_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'shared/valid_civil'
+require 'date'
+
+describe "Date#valid_civil?" do
+
+ it_behaves_like :date_valid_civil?, :valid_civil?
+
+end
diff --git a/spec/ruby/library/date/valid_commercial_spec.rb b/spec/ruby/library/date/valid_commercial_spec.rb
new file mode 100644
index 0000000000..7e96782b6b
--- /dev/null
+++ b/spec/ruby/library/date/valid_commercial_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'shared/valid_commercial'
+require 'date'
+
+describe "Date#valid_commercial?" do
+
+ it_behaves_like :date_valid_commercial?, :valid_commercial?
+end
diff --git a/spec/ruby/library/date/valid_date_spec.rb b/spec/ruby/library/date/valid_date_spec.rb
new file mode 100644
index 0000000000..f12a71d966
--- /dev/null
+++ b/spec/ruby/library/date/valid_date_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/valid_civil'
+require 'date'
+
+describe "Date#valid_date?" do
+ it_behaves_like :date_valid_civil?, :valid_date?
+end
diff --git a/spec/ruby/library/date/valid_jd_spec.rb b/spec/ruby/library/date/valid_jd_spec.rb
new file mode 100644
index 0000000000..aecaaabcf4
--- /dev/null
+++ b/spec/ruby/library/date/valid_jd_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'shared/valid_jd'
+require 'date'
+
+describe "Date.valid_jd?" do
+
+ it_behaves_like :date_valid_jd?, :valid_jd?
+
+end
diff --git a/spec/ruby/library/date/valid_ordinal_spec.rb b/spec/ruby/library/date/valid_ordinal_spec.rb
new file mode 100644
index 0000000000..58d548c704
--- /dev/null
+++ b/spec/ruby/library/date/valid_ordinal_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'shared/valid_ordinal'
+require 'date'
+
+describe "Date.valid_ordinal?" do
+
+ it_behaves_like :date_valid_ordinal?, :valid_ordinal?
+
+end
diff --git a/spec/ruby/library/date/valid_time_spec.rb b/spec/ruby/library/date/valid_time_spec.rb
new file mode 100644
index 0000000000..87c239bedb
--- /dev/null
+++ b/spec/ruby/library/date/valid_time_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.valid_time?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/wday_spec.rb b/spec/ruby/library/date/wday_spec.rb
new file mode 100644
index 0000000000..303905ed35
--- /dev/null
+++ b/spec/ruby/library/date/wday_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#wday" do
+ it "returns the week day as a number starting with Sunday as 0" do
+ w = Date.new(2000, 1, 1).wday
+ w.should == 6
+ end
+end
diff --git a/spec/ruby/library/date/wednesday_spec.rb b/spec/ruby/library/date/wednesday_spec.rb
new file mode 100644
index 0000000000..e80ec23dd2
--- /dev/null
+++ b/spec/ruby/library/date/wednesday_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#wednesday?" do
+ it "should be wednesday" do
+ Date.new(2000, 1, 5).wednesday?.should be_true
+ end
+end
diff --git a/spec/ruby/library/date/yday_spec.rb b/spec/ruby/library/date/yday_spec.rb
new file mode 100644
index 0000000000..cfb174a4c2
--- /dev/null
+++ b/spec/ruby/library/date/yday_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#yday" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/date/year_spec.rb b/spec/ruby/library/date/year_spec.rb
new file mode 100644
index 0000000000..90d14e5a39
--- /dev/null
+++ b/spec/ruby/library/date/year_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date#year" do
+ it "returns the year" do
+ y = Date.new(2000, 7, 1).year
+ y.should == 2000
+ end
+end
diff --git a/spec/ruby/library/date/zone_to_diff_spec.rb b/spec/ruby/library/date/zone_to_diff_spec.rb
new file mode 100644
index 0000000000..354daaaee4
--- /dev/null
+++ b/spec/ruby/library/date/zone_to_diff_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.zone_to_diff" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/_strptime_spec.rb b/spec/ruby/library/datetime/_strptime_spec.rb
new file mode 100644
index 0000000000..abec26ff9f
--- /dev/null
+++ b/spec/ruby/library/datetime/_strptime_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime._strptime" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/add_spec.rb b/spec/ruby/library/datetime/add_spec.rb
new file mode 100644
index 0000000000..a6d98914bd
--- /dev/null
+++ b/spec/ruby/library/datetime/add_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime#+" do
+ it "is able to add sub-millisecond precision values" do
+ datetime = DateTime.new(2017)
+ (datetime + 0.00001).to_time.usec.should == 864000
+ end
+end
diff --git a/spec/ruby/library/datetime/civil_spec.rb b/spec/ruby/library/datetime/civil_spec.rb
new file mode 100644
index 0000000000..fb6f67f16d
--- /dev/null
+++ b/spec/ruby/library/datetime/civil_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime.civil" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/commercial_spec.rb b/spec/ruby/library/datetime/commercial_spec.rb
new file mode 100644
index 0000000000..ad97b2a80e
--- /dev/null
+++ b/spec/ruby/library/datetime/commercial_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime.commercial" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/hour_spec.rb b/spec/ruby/library/datetime/hour_spec.rb
new file mode 100644
index 0000000000..f019b60c74
--- /dev/null
+++ b/spec/ruby/library/datetime/hour_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime#hour" do
+ it "returns 0 if no argument is passed" do
+ DateTime.new.hour.should == 0
+ end
+
+ it "returns the hour given as argument" do
+ new_datetime(hour: 5).hour.should == 5
+ end
+
+ it "adds 24 to negative hours" do
+ new_datetime(hour: -10).hour.should == 14
+ end
+
+ it "raises an error for Rational" do
+ lambda { new_datetime(hour: 1 + Rational(1,2)) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error for Float" do
+ lambda { new_datetime(hour: 1.5).hour }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error for Rational" do
+ lambda { new_datetime(day: 1 + Rational(1,2)) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error, when the hour is smaller than -24" do
+ lambda { new_datetime(hour: -25) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error, when the hour is larger than 24" do
+ lambda { new_datetime(hour: 25) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error for hour fractions smaller than -24" do
+ lambda { new_datetime(hour: -24 - Rational(1,2)) }.should(
+ raise_error(ArgumentError))
+ end
+
+ it "adds 1 to day, when 24 hours given" do
+ d = new_datetime day: 1, hour: 24
+ d.hour.should == 0
+ d.day.should == 2
+ end
+end
diff --git a/spec/ruby/library/datetime/httpdate_spec.rb b/spec/ruby/library/datetime/httpdate_spec.rb
new file mode 100644
index 0000000000..68e45cbff0
--- /dev/null
+++ b/spec/ruby/library/datetime/httpdate_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime.httpdate" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/iso8601_spec.rb b/spec/ruby/library/datetime/iso8601_spec.rb
new file mode 100644
index 0000000000..457881277a
--- /dev/null
+++ b/spec/ruby/library/datetime/iso8601_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime.iso8601" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "DateTime#iso8601" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/jd_spec.rb b/spec/ruby/library/datetime/jd_spec.rb
new file mode 100644
index 0000000000..1e783f5af4
--- /dev/null
+++ b/spec/ruby/library/datetime/jd_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime.jd" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/jisx0301_spec.rb b/spec/ruby/library/datetime/jisx0301_spec.rb
new file mode 100644
index 0000000000..ab26aa2d73
--- /dev/null
+++ b/spec/ruby/library/datetime/jisx0301_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime.jisx0301" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "DateTime#jisx0301" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/min_spec.rb b/spec/ruby/library/datetime/min_spec.rb
new file mode 100644
index 0000000000..a1eaa214cb
--- /dev/null
+++ b/spec/ruby/library/datetime/min_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/min'
+
+describe "DateTime.min" do
+ it_behaves_like :datetime_min, :min
+end
diff --git a/spec/ruby/library/datetime/minute_spec.rb b/spec/ruby/library/datetime/minute_spec.rb
new file mode 100644
index 0000000000..acdfeda345
--- /dev/null
+++ b/spec/ruby/library/datetime/minute_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/min'
+
+describe "DateTime.minute" do
+ it_behaves_like :datetime_min, :minute
+end
diff --git a/spec/ruby/library/datetime/new_offset_spec.rb b/spec/ruby/library/datetime/new_offset_spec.rb
new file mode 100644
index 0000000000..bc0988f32d
--- /dev/null
+++ b/spec/ruby/library/datetime/new_offset_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime#new_offset" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/new_spec.rb b/spec/ruby/library/datetime/new_spec.rb
new file mode 100644
index 0000000000..85fbe7578b
--- /dev/null
+++ b/spec/ruby/library/datetime/new_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime.new" do
+ it "sets all values to default if passed no arguments" do
+ d = DateTime.new
+ d.year.should == -4712
+ d.month.should == 1
+ d.day.should == 1
+ d.hour.should == 0
+ d.min.should == 0
+ d.sec.should == 0
+ d.sec_fraction.should == 0
+ d.offset.should == 0
+ end
+
+ it "takes the first argument as year" do
+ DateTime.new(2011).year.should == 2011
+ end
+
+ it "takes the second argument as month" do
+ DateTime.new(2011, 2).month.should == 2
+ end
+
+ it "takes the third argument as day" do
+ DateTime.new(2011, 2, 3).day.should == 3
+ end
+
+ it "takes the forth argument as hour" do
+ DateTime.new(2011, 2, 3, 4).hour.should == 4
+ end
+
+ it "takes the fifth argument as minute" do
+ DateTime.new(1, 2, 3, 4, 5).min.should == 5
+ end
+
+ it "takes the sixth argument as second" do
+ DateTime.new(1, 2, 3, 4, 5, 6).sec.should == 6
+ end
+
+ it "takes the seventh argument as an offset" do
+ DateTime.new(1, 2, 3, 4, 5, 6, 0.7).offset.should == 0.7
+ end
+
+ it "takes the eighth argument as the date of calendar reform" do
+ DateTime.new(1, 2, 3, 4, 5, 6, 0.7, Date::ITALY).start().should == Date::ITALY
+ end
+
+ it "raises an error on invalid arguments" do
+ lambda { new_datetime(minute: 999) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/datetime/now_spec.rb b/spec/ruby/library/datetime/now_spec.rb
new file mode 100644
index 0000000000..9cdce80ef3
--- /dev/null
+++ b/spec/ruby/library/datetime/now_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime.now" do
+ it "creates an instance of DateTime" do
+ DateTime.now.should be_an_instance_of(DateTime)
+ end
+
+ it "sets the current date" do
+ (DateTime.now - Date.today).to_f.should be_close(0.0, 2.0)
+ end
+
+ it "sets the current time" do
+ dt = DateTime.now
+ now = Time.now
+ (dt.to_time - now).should be_close(0.0, 10.0)
+ end
+
+ it "grabs the local timezone" do
+ with_timezone("PDT", -8) do
+ dt = DateTime.now
+ dt.zone.should == "-08:00"
+ end
+ end
+end
diff --git a/spec/ruby/library/datetime/offset_spec.rb b/spec/ruby/library/datetime/offset_spec.rb
new file mode 100644
index 0000000000..e25e7a0f35
--- /dev/null
+++ b/spec/ruby/library/datetime/offset_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime#offset" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/ordinal_spec.rb b/spec/ruby/library/datetime/ordinal_spec.rb
new file mode 100644
index 0000000000..64b154ee9b
--- /dev/null
+++ b/spec/ruby/library/datetime/ordinal_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime.ordinal" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/parse_spec.rb b/spec/ruby/library/datetime/parse_spec.rb
new file mode 100644
index 0000000000..e5441bd9dc
--- /dev/null
+++ b/spec/ruby/library/datetime/parse_spec.rb
@@ -0,0 +1,127 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime.parse" do
+
+ it "parses a day name into a DateTime object" do
+ d = DateTime.parse("friday")
+ d.should == DateTime.commercial(d.cwyear, d.cweek, 5)
+ end
+
+ it "parses a month name into a DateTime object" do
+ d = DateTime.parse("october")
+ d.should == DateTime.civil(Date.today.year, 10)
+ end
+
+ it "parses a month day into a DateTime object" do
+ d = DateTime.parse("5th")
+ d.should == DateTime.civil(Date.today.year, Date.today.month, 5)
+ end
+
+ # Specs using numbers
+ it "throws an argument error for a single digit" do
+ lambda{ DateTime.parse("1") }.should raise_error(ArgumentError)
+ end
+
+ it "parses DD as month day number" do
+ d = DateTime.parse("10")
+ d.should == DateTime.civil(Date.today.year, Date.today.month, 10)
+ end
+
+ it "parses DDD as year day number" do
+ d = DateTime.parse("100")
+ if DateTime.gregorian_leap?(Date.today.year)
+ d.should == DateTime.civil(Date.today.year, 4, 9)
+ else
+ d.should == DateTime.civil(Date.today.year, 4, 10)
+ end
+ end
+
+ it "parses MMDD as month and day" do
+ d = DateTime.parse("1108")
+ d.should == DateTime.civil(Date.today.year, 11, 8)
+ end
+
+ it "parses YYYYMMDD as year, month and day" do
+ d = DateTime.parse("20121108")
+ d.should == DateTime.civil(2012, 11, 8)
+ end
+
+ describe "YYYY-MM-DDTHH:MM:SS format" do
+ it "parses YYYY-MM-DDTHH:MM:SS into a DateTime object" do
+ d = DateTime.parse("2012-11-08T15:43:59")
+ d.should == DateTime.civil(2012, 11, 8, 15, 43, 59)
+ end
+
+ it "throws an argument error for invalid month values" do
+ lambda{DateTime.parse("2012-13-08T15:43:59")}.should raise_error(ArgumentError)
+ end
+
+ it "throws an argument error for invalid day values" do
+ lambda{DateTime.parse("2012-12-32T15:43:59")}.should raise_error(ArgumentError)
+ end
+
+ it "throws an argument error for invalid hour values" do
+ lambda{DateTime.parse("2012-12-31T25:43:59")}.should raise_error(ArgumentError)
+ end
+
+ it "throws an argument error for invalid minute values" do
+ lambda{DateTime.parse("2012-12-31T25:43:59")}.should raise_error(ArgumentError)
+ end
+
+ it "throws an argument error for invalid second values" do
+ lambda{DateTime.parse("2012-11-08T15:43:61")}.should raise_error(ArgumentError)
+ end
+
+ end
+
+ it "parses YYDDD as year and day number in 1969--2068" do
+ d = DateTime.parse("10100")
+ d.should == DateTime.civil(2010, 4, 10)
+ end
+
+ it "parses YYMMDD as year, month and day in 1969--2068" do
+ d = DateTime.parse("201023")
+ d.should == DateTime.civil(2020, 10, 23)
+ end
+
+ it "parses YYYYDDD as year and day number" do
+ d = DateTime.parse("1910100")
+ d.should == DateTime.civil(1910, 4, 10)
+ end
+
+ it "parses YYYYMMDD as year, month and day number" do
+ d = DateTime.parse("19101101")
+ d.should == DateTime.civil(1910, 11, 1)
+ end
+end
+
+describe "DateTime.parse(.)" do
+ it "parses YYYY.MM.DD into a DateTime object" do
+ d = DateTime.parse("2007.10.01")
+ d.year.should == 2007
+ d.month.should == 10
+ d.day.should == 1
+ end
+
+ it "parses DD.MM.YYYY into a DateTime object" do
+ d = DateTime.parse("10.01.2007")
+ d.year.should == 2007
+ d.month.should == 1
+ d.day.should == 10
+ end
+
+ it "parses YY.MM.DD into a DateTime object using the year 20YY" do
+ d = DateTime.parse("10.01.07")
+ d.year.should == 2010
+ d.month.should == 1
+ d.day.should == 7
+ end
+
+ it "parses YY.MM.DD using the year digits as 20YY when given true as additional argument" do
+ d = DateTime.parse("10.01.07", true)
+ d.year.should == 2010
+ d.month.should == 1
+ d.day.should == 7
+ end
+end
diff --git a/spec/ruby/library/datetime/rfc2822_spec.rb b/spec/ruby/library/datetime/rfc2822_spec.rb
new file mode 100644
index 0000000000..70bfca60b4
--- /dev/null
+++ b/spec/ruby/library/datetime/rfc2822_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime.rfc2822" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/rfc3339_spec.rb b/spec/ruby/library/datetime/rfc3339_spec.rb
new file mode 100644
index 0000000000..f870a5f63b
--- /dev/null
+++ b/spec/ruby/library/datetime/rfc3339_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime.rfc3339" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "DateTime#rfc3339" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/rfc822_spec.rb b/spec/ruby/library/datetime/rfc822_spec.rb
new file mode 100644
index 0000000000..0cd0aacc19
--- /dev/null
+++ b/spec/ruby/library/datetime/rfc822_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime.rfc822" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/sec_fraction_spec.rb b/spec/ruby/library/datetime/sec_fraction_spec.rb
new file mode 100644
index 0000000000..40383a8ca4
--- /dev/null
+++ b/spec/ruby/library/datetime/sec_fraction_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime#sec_fraction" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/sec_spec.rb b/spec/ruby/library/datetime/sec_spec.rb
new file mode 100644
index 0000000000..f681283c8e
--- /dev/null
+++ b/spec/ruby/library/datetime/sec_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/sec'
+
+describe "DateTime.sec" do
+ it_behaves_like :datetime_sec, :sec
+end
diff --git a/spec/ruby/library/datetime/second_fraction_spec.rb b/spec/ruby/library/datetime/second_fraction_spec.rb
new file mode 100644
index 0000000000..d5393149ba
--- /dev/null
+++ b/spec/ruby/library/datetime/second_fraction_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime#second_fraction" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/second_spec.rb b/spec/ruby/library/datetime/second_spec.rb
new file mode 100644
index 0000000000..545c3f9109
--- /dev/null
+++ b/spec/ruby/library/datetime/second_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/sec'
+
+describe "DateTime#second" do
+ it_behaves_like :datetime_sec, :second
+end
diff --git a/spec/ruby/library/datetime/shared/min.rb b/spec/ruby/library/datetime/shared/min.rb
new file mode 100644
index 0000000000..e69d86ab02
--- /dev/null
+++ b/spec/ruby/library/datetime/shared/min.rb
@@ -0,0 +1,40 @@
+require 'date'
+
+describe :datetime_min, shared: true do
+ it "returns 0 if no argument is passed" do
+ DateTime.new.send(@method).should == 0
+ end
+
+ it "returns the minute passed as argument" do
+ new_datetime(minute: 5).send(@method).should == 5
+ end
+
+ it "adds 60 to negative minutes" do
+ new_datetime(minute: -20).send(@method).should == 40
+ end
+
+ it "raises an error for Rational" do
+ lambda { new_datetime minute: 5 + Rational(1,2) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error for Float" do
+ lambda { new_datetime minute: 5.5 }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error for Rational" do
+ lambda { new_datetime(hour: 2 + Rational(1,2)) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error, when the minute is smaller than -60" do
+ lambda { new_datetime(minute: -61) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error, when the minute is greater or equal than 60" do
+ lambda { new_datetime(minute: 60) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error for minute fractions smaller than -60" do
+ lambda { new_datetime(minute: -60 - Rational(1,2))}.should(
+ raise_error(ArgumentError))
+ end
+end
diff --git a/spec/ruby/library/datetime/shared/sec.rb b/spec/ruby/library/datetime/shared/sec.rb
new file mode 100644
index 0000000000..68e8af8e40
--- /dev/null
+++ b/spec/ruby/library/datetime/shared/sec.rb
@@ -0,0 +1,45 @@
+require 'date'
+
+describe :datetime_sec, shared: true do
+ it "returns 0 seconds if passed no arguments" do
+ d = DateTime.new
+ d.send(@method).should == 0
+ end
+
+ it "returns the seconds passed in the arguments" do
+ new_datetime(second: 5).send(@method).should == 5
+ end
+
+ it "adds 60 to negative values" do
+ new_datetime(second: -20).send(@method).should == 40
+ end
+
+ it "returns the absolute value of a Rational" do
+ new_datetime(second: 5 + Rational(1,2)).send(@method).should == 5
+ end
+
+ it "returns the absolute value of a float" do
+ new_datetime(second: 5.5).send(@method).should == 5
+ end
+
+ it "raises an error when minute is given as a rational" do
+ lambda { new_datetime(minute: 5 + Rational(1,2)) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error, when the second is smaller than -60" do
+ lambda { new_datetime(second: -61) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error, when the second is greater or equal than 60" do
+ lambda { new_datetime(second: 60) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error for second fractions smaller than -60" do
+ lambda { new_datetime(second: -60 - Rational(1,2))}.should(
+ raise_error(ArgumentError))
+ end
+
+ it "takes a second fraction near 60" do
+ new_datetime(second: 59 + Rational(1,2)).send(@method).should == 59
+ end
+end
diff --git a/spec/ruby/library/datetime/strftime_spec.rb b/spec/ruby/library/datetime/strftime_spec.rb
new file mode 100644
index 0000000000..f95b52fffb
--- /dev/null
+++ b/spec/ruby/library/datetime/strftime_spec.rb
@@ -0,0 +1,51 @@
+require 'date'
+require_relative '../../shared/time/strftime_for_date'
+require_relative '../../shared/time/strftime_for_time'
+
+describe "DateTime#strftime" do
+ before :all do
+ @new_date = lambda { |y,m,d| DateTime.civil(y,m,d) }
+ @new_time = lambda { |*args| DateTime.civil(*args) }
+ @new_time_in_zone = lambda { |zone,offset,*args|
+ y, m, d, h, min, s = args
+ DateTime.new(y, m||1, d||1, h||0, min||0, s||0, Rational(offset, 24))
+ }
+ @new_time_with_offset = lambda { |y,m,d,h,min,s,offset|
+ DateTime.new(y,m,d,h,min,s, Rational(offset, 86_400))
+ }
+
+ @time = DateTime.civil(2001, 2, 3, 4, 5, 6)
+ end
+
+ it_behaves_like :strftime_date, :strftime
+ it_behaves_like :strftime_time, :strftime
+
+ # Differences with Time
+ it "should be able to print the datetime with no argument" do
+ @time.strftime.should == "2001-02-03T04:05:06+00:00"
+ @time.strftime.should == @time.to_s
+ end
+
+ # %Z is %:z for Date/DateTime
+ it "should be able to show the timezone with a : separator" do
+ @time.strftime("%Z").should == "+00:00"
+ end
+
+ # %v is %e-%b-%Y for Date/DateTime
+ it "should be able to show the commercial week" do
+ @time.strftime("%v").should == " 3-Feb-2001"
+ @time.strftime("%v").should == @time.strftime('%e-%b-%Y')
+ end
+
+ # additional conversion specifiers only in Date/DateTime
+ it 'shows the number of milliseconds since epoch' do
+ DateTime.new(1970, 1, 1, 0, 0, 0).strftime("%Q").should == "0"
+ @time.strftime("%Q").should == "981173106000"
+ DateTime.civil(2001, 2, 3, 4, 5, Rational(6123, 1000)).strftime("%Q").should == "981173106123"
+ end
+
+ it "should be able to show a full notation" do
+ @time.strftime("%+").should == "Sat Feb 3 04:05:06 +00:00 2001"
+ @time.strftime("%+").should == @time.strftime('%a %b %e %H:%M:%S %Z %Y')
+ end
+end
diff --git a/spec/ruby/library/datetime/strptime_spec.rb b/spec/ruby/library/datetime/strptime_spec.rb
new file mode 100644
index 0000000000..d1e83550e4
--- /dev/null
+++ b/spec/ruby/library/datetime/strptime_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime.strptime" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/subtract_spec.rb b/spec/ruby/library/datetime/subtract_spec.rb
new file mode 100644
index 0000000000..ed88533246
--- /dev/null
+++ b/spec/ruby/library/datetime/subtract_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime#-" do
+ it "is able to subtract sub-millisecond precision values" do
+ date = DateTime.new(2017)
+ ((date + 0.00001) - date).should == Rational(1, 100000)
+ end
+end
diff --git a/spec/ruby/library/datetime/to_date_spec.rb b/spec/ruby/library/datetime/to_date_spec.rb
new file mode 100644
index 0000000000..a1b93305b8
--- /dev/null
+++ b/spec/ruby/library/datetime/to_date_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime#to_date" do
+ it "returns an instance of Date" do
+ dt = DateTime.new(2012, 12, 24, 12, 23, 00, '+05:00')
+ dt.to_date.should be_kind_of(Date)
+ end
+
+ it "maintains the same year" do
+ dt = DateTime.new(2012, 12, 24, 12, 23, 00, '+05:00')
+ dt.to_date.year.should == dt.year
+ end
+
+ it "maintains the same month" do
+ dt = DateTime.new(2012, 12, 24, 12, 23, 00, '+05:00')
+ dt.to_date.mon.should == dt.mon
+ end
+
+ it "maintains the same day" do
+ dt = DateTime.new(2012, 12, 24, 12, 23, 00, '+05:00')
+ dt.to_date.day.should == dt.day
+ end
+
+ it "maintains the same mday" do
+ dt = DateTime.new(2012, 12, 24, 12, 23, 00, '+05:00')
+ dt.to_date.mday.should == dt.mday
+ end
+
+ it "maintains the same julian day regardless of local time or zone" do
+ dt = DateTime.new(2012, 12, 24, 12, 23, 00, '+05:00')
+
+ with_timezone("Pactific/Pago_Pago", -11) do
+ dt.to_date.jd.should == dt.jd
+ end
+ end
+end
diff --git a/spec/ruby/library/datetime/to_datetime_spec.rb b/spec/ruby/library/datetime/to_datetime_spec.rb
new file mode 100644
index 0000000000..95ee29268f
--- /dev/null
+++ b/spec/ruby/library/datetime/to_datetime_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime#to_datetime" do
+ it "returns itself" do
+ dt = DateTime.new(2012, 12, 24, 12, 23, 00, '+05:00')
+ dt.to_datetime.should == dt
+ end
+end
diff --git a/spec/ruby/library/datetime/to_s_spec.rb b/spec/ruby/library/datetime/to_s_spec.rb
new file mode 100644
index 0000000000..c6b5bd90be
--- /dev/null
+++ b/spec/ruby/library/datetime/to_s_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime#to_s" do
+ it "returns a new String object" do
+ dt = DateTime.new(2012, 12, 24, 1, 2, 3, "+03:00")
+ dt.to_s.should be_kind_of(String)
+ end
+
+ it "maintains timezone regardless of local time" do
+ dt = DateTime.new(2012, 12, 24, 1, 2, 3, "+03:00")
+
+ with_timezone("Pactific/Pago_Pago", -11) do
+ dt.to_s.should == "2012-12-24T01:02:03+03:00"
+ end
+ end
+end
diff --git a/spec/ruby/library/datetime/to_time_spec.rb b/spec/ruby/library/datetime/to_time_spec.rb
new file mode 100644
index 0000000000..1844cbd7eb
--- /dev/null
+++ b/spec/ruby/library/datetime/to_time_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime#to_time" do
+ it "yields a new Time object" do
+ DateTime.now.to_time.should be_kind_of(Time)
+ end
+
+ it "returns a Time representing the same instant" do
+ datetime = DateTime.civil(3, 12, 31, 23, 58, 59)
+ time = datetime.to_time.utc
+
+ time.year.should == 3
+ time.month.should == 12
+ time.day.should == 31
+ time.hour.should == 23
+ time.min.should == 58
+ time.sec.should == 59
+ end
+
+ ruby_version_is "2.4" do
+ it "preserves the same time regardless of local time or zone" do
+ date = DateTime.new(2012, 12, 24, 12, 23, 00, '+03:00')
+
+ with_timezone("Pactific/Pago_Pago", -11) do
+ time = date.to_time
+
+ time.utc_offset.should == 3 * 3600
+ time.year.should == date.year
+ time.mon.should == date.mon
+ time.day.should == date.day
+ time.hour.should == date.hour
+ time.min.should == date.min
+ time.sec.should == date.sec
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/datetime/xmlschema_spec.rb b/spec/ruby/library/datetime/xmlschema_spec.rb
new file mode 100644
index 0000000000..42832631ed
--- /dev/null
+++ b/spec/ruby/library/datetime/xmlschema_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime.xmlschema" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "DateTime#xmlschema" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/datetime/zone_spec.rb b/spec/ruby/library/datetime/zone_spec.rb
new file mode 100644
index 0000000000..b2c10b4b3b
--- /dev/null
+++ b/spec/ruby/library/datetime/zone_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "DateTime#zone" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/delegate/delegate_class/instance_method_spec.rb b/spec/ruby/library/delegate/delegate_class/instance_method_spec.rb
new file mode 100644
index 0000000000..7e50dd81d8
--- /dev/null
+++ b/spec/ruby/library/delegate/delegate_class/instance_method_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "DelegateClass.instance_method" do
+ before :all do
+ @klass = DelegateSpecs::DelegateClass
+ @obj = @klass.new(DelegateSpecs::Simple.new)
+ end
+
+ it "returns a method object for public instance methods of the delegated class" do
+ m = @klass.instance_method(:pub)
+ m.should be_an_instance_of(UnboundMethod)
+ m.bind(@obj).call.should == :foo
+ end
+
+ it "returns a method object for protected instance methods of the delegated class" do
+ m = @klass.instance_method(:prot)
+ m.should be_an_instance_of(UnboundMethod)
+ m.bind(@obj).call.should == :protected
+ end
+
+ it "raises a NameError for a private instance methods of the delegated class" do
+ lambda {
+ @klass.instance_method(:priv)
+ }.should raise_error(NameError)
+ end
+
+ it "returns a method object for public instance methods of the DelegateClass class" do
+ m = @klass.instance_method(:extra)
+ m.should be_an_instance_of(UnboundMethod)
+ m.bind(@obj).call.should == :cheese
+ end
+
+ it "returns a method object for protected instance methods of the DelegateClass class" do
+ m = @klass.instance_method(:extra_protected)
+ m.should be_an_instance_of(UnboundMethod)
+ m.bind(@obj).call.should == :baz
+ end
+
+ it "returns a method object for private instance methods of the DelegateClass class" do
+ m = @klass.instance_method(:extra_private)
+ m.should be_an_instance_of(UnboundMethod)
+ m.bind(@obj).call.should == :bar
+ end
+
+ it "raises a NameError for an invalid method name" do
+ lambda {
+ @klass.instance_method(:invalid_and_silly_method_name)
+ }.should raise_error(NameError)
+ end
+
+end
diff --git a/spec/ruby/library/delegate/delegate_class/instance_methods_spec.rb b/spec/ruby/library/delegate/delegate_class/instance_methods_spec.rb
new file mode 100644
index 0000000000..6012ff72de
--- /dev/null
+++ b/spec/ruby/library/delegate/delegate_class/instance_methods_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "DelegateClass.instance_methods" do
+ before :all do
+ @methods = DelegateSpecs::DelegateClass.instance_methods
+ end
+
+ it "includes all public methods of the delegated class" do
+ @methods.should include :pub
+ end
+
+ it "includes all protected methods of the delegated class" do
+ @methods.should include :prot
+ end
+
+ it "includes instance methods of the DelegateClass class" do
+ @methods.should include :extra
+ @methods.should include :extra_protected
+ end
+
+ it "does not include private methods" do
+ @methods.should_not include :priv
+ @methods.should_not include :extra_private
+ end
+end
diff --git a/spec/ruby/library/delegate/delegate_class/private_instance_methods_spec.rb b/spec/ruby/library/delegate/delegate_class/private_instance_methods_spec.rb
new file mode 100644
index 0000000000..06b2115cc5
--- /dev/null
+++ b/spec/ruby/library/delegate/delegate_class/private_instance_methods_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "DelegateClass.private_instance_methods" do
+ before :all do
+ @methods = DelegateSpecs::DelegateClass.private_instance_methods
+ end
+
+ it "does not include any instance methods of the delegated class" do
+ @methods.should_not include :pub
+ @methods.should_not include :prot
+ @methods.should_not include :priv # since these are not forwarded...
+ end
+
+ it "includes private instance methods of the DelegateClass class" do
+ @methods.should include :extra_private
+ end
+
+ it "does not include public or protected instance methods of the DelegateClass class" do
+ @methods.should_not include :extra
+ @methods.should_not include :extra_protected
+ end
+end
diff --git a/spec/ruby/library/delegate/delegate_class/protected_instance_methods_spec.rb b/spec/ruby/library/delegate/delegate_class/protected_instance_methods_spec.rb
new file mode 100644
index 0000000000..ac6659ec1e
--- /dev/null
+++ b/spec/ruby/library/delegate/delegate_class/protected_instance_methods_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "DelegateClass.protected_instance_methods" do
+ before :all do
+ @methods = DelegateSpecs::DelegateClass.protected_instance_methods
+ end
+
+ it "does not include public methods of the delegated class" do
+ @methods.should_not include :pub
+ end
+
+ it "includes the protected methods of the delegated class" do
+ @methods.should include :prot
+ end
+
+ it "includes protected instance methods of the DelegateClass class" do
+ @methods.should include :extra_protected
+ end
+
+ it "does not include public instance methods of the DelegateClass class" do
+ @methods.should_not include :extra
+ end
+
+ it "does not include private methods" do
+ @methods.should_not include :priv
+ @methods.should_not include :extra_private
+ end
+end
diff --git a/spec/ruby/library/delegate/delegate_class/public_instance_methods_spec.rb b/spec/ruby/library/delegate/delegate_class/public_instance_methods_spec.rb
new file mode 100644
index 0000000000..6c0d9bcab1
--- /dev/null
+++ b/spec/ruby/library/delegate/delegate_class/public_instance_methods_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "DelegateClass.public_instance_methods" do
+ before :all do
+ @methods = DelegateSpecs::DelegateClass.public_instance_methods
+ end
+
+ it "includes all public methods of the delegated class" do
+ @methods.should include :pub
+ end
+
+ it "does not include the protected methods of the delegated class" do
+ @methods.should_not include :prot
+ end
+
+ it "includes public instance methods of the DelegateClass class" do
+ @methods.should include :extra
+ end
+
+ it "does not include private methods" do
+ @methods.should_not include :priv
+ @methods.should_not include :extra_private
+ end
+end
diff --git a/spec/ruby/library/delegate/delegate_class/respond_to_missing_spec.rb b/spec/ruby/library/delegate/delegate_class/respond_to_missing_spec.rb
new file mode 100644
index 0000000000..729cfc96c6
--- /dev/null
+++ b/spec/ruby/library/delegate/delegate_class/respond_to_missing_spec.rb
@@ -0,0 +1,23 @@
+require 'delegate'
+
+describe "DelegateClass#respond_to_missing?" do
+ it "is used for respond_to? behavior of late-bound delegated methods" do
+ # From jruby/jruby#3151:
+ # DelegateClass subtracts Delegate's public API from the target class's instance_methods
+ # to determine which methods to eagerly delegate. If respond_to_missing? shows up in
+ # instance_methods, it will get delegated and skip the delegate-aware implementation
+ # in Delegate. Any methods that must be delegated through method_missing, like methods
+ # defined after the DelegateClass is created, will fail to dispatch properly.
+
+ cls = Class.new
+ dcls = DelegateClass(cls)
+ cdcls = Class.new(dcls)
+ cdcls_obj = cdcls.new(cls.new)
+
+ cdcls_obj.respond_to?(:foo).should == false
+
+ cls.class_eval { def foo; end }
+
+ cdcls_obj.respond_to?(:foo).should == true
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/case_compare_spec.rb b/spec/ruby/library/delegate/delegator/case_compare_spec.rb
new file mode 100644
index 0000000000..b62397cecf
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/case_compare_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#===" do
+ it "is delegated" do
+ base = mock('base')
+ delegator = DelegateSpecs::Delegator.new(base)
+ base.should_receive(:===).with(42).and_return(:foo)
+ (delegator === 42).should == :foo
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/compare_spec.rb b/spec/ruby/library/delegate/delegator/compare_spec.rb
new file mode 100644
index 0000000000..7dee5c2fb5
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/compare_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#<=>" do
+ it "is delegated" do
+ base = mock('base')
+ delegator = DelegateSpecs::Delegator.new(base)
+ base.should_receive(:<=>).with(42).and_return(1)
+ (delegator <=> 42).should == 1
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/complement_spec.rb b/spec/ruby/library/delegate/delegator/complement_spec.rb
new file mode 100644
index 0000000000..10cb37c7ea
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/complement_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#~" do
+ it "is delegated" do
+ base = mock('base')
+ delegator = DelegateSpecs::Delegator.new(base)
+ base.should_receive(:~).and_return(:foo)
+ (~delegator).should == :foo
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/eql_spec.rb b/spec/ruby/library/delegate/delegator/eql_spec.rb
new file mode 100644
index 0000000000..3170d5727a
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/eql_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#eql?" do
+ ruby_version_is ""..."2.5" do
+ it "is delegated" do
+ base = mock('base')
+ delegator = DelegateSpecs::Delegator.new(base)
+ base.should_receive(:eql?).with(42).and_return(:foo)
+ delegator.eql?(42).should == :foo
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "returns true when compared with same delegator" do
+ base = mock('base')
+ delegator = DelegateSpecs::Delegator.new(base)
+
+ delegator.eql?(delegator).should be_true
+ end
+
+ it "returns true when compared with the inner object" do
+ base = mock('base')
+ delegator = DelegateSpecs::Delegator.new(base)
+
+ delegator.eql?(base).should be_true
+ end
+
+ it "returns false when compared with the delegator with other object" do
+ base = mock('base')
+ other = mock('other')
+ delegator0 = DelegateSpecs::Delegator.new(base)
+ delegator1 = DelegateSpecs::Delegator.new(other)
+
+ delegator0.eql?(delegator1).should be_false
+ end
+
+ it "returns false when compared with the other object" do
+ base = mock('base')
+ other = mock('other')
+ delegator = DelegateSpecs::Delegator.new(base)
+
+ delegator.eql?(other).should be_false
+ end
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/equal_spec.rb b/spec/ruby/library/delegate/delegator/equal_spec.rb
new file mode 100644
index 0000000000..c8711c74b5
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/equal_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#equal?" do
+ it "returns true only when compared with the delegator" do
+ obj = mock('base')
+ delegator = DelegateSpecs::Delegator.new(obj)
+ obj.should_not_receive(:equal?)
+ delegator.equal?(obj).should be_false
+ delegator.equal?(nil).should be_false
+ delegator.equal?(delegator).should be_true
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/equal_value_spec.rb b/spec/ruby/library/delegate/delegator/equal_value_spec.rb
new file mode 100644
index 0000000000..0c967d5f94
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/equal_value_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#==" do
+ before :all do
+ @base = mock('base')
+ @delegator = DelegateSpecs::Delegator.new(@base)
+ end
+
+ it "is not delegated when passed self" do
+ @base.should_not_receive(:==)
+ (@delegator == @delegator).should be_true
+ end
+
+ it "is delegated when passed the delegated object" do
+ @base.should_receive(:==).and_return(false)
+ (@delegator == @base).should be_false
+ end
+
+ it "is delegated in general" do
+ @base.should_receive(:==).and_return(true)
+ (@delegator == 42).should be_true
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/frozen_spec.rb b/spec/ruby/library/delegate/delegator/frozen_spec.rb
new file mode 100644
index 0000000000..ca83c5f77c
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/frozen_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator when frozen" do
+ before :all do
+ @array = [42, :hello]
+ @delegate = DelegateSpecs::Delegator.new(@array)
+ @delegate.freeze
+ end
+
+ it "is still readable" do
+ @delegate.should == [42, :hello]
+ @delegate.include?("bar").should be_false
+ end
+
+ it "is frozen" do
+ @delegate.frozen?.should be_true
+ end
+
+ it "is not writeable" do
+ lambda{ @delegate[0] += 2 }.should raise_error( RuntimeError )
+ end
+
+ it "creates a frozen clone" do
+ @delegate.clone.frozen?.should be_true
+ end
+
+ it "creates an unfrozen dup" do
+ @delegate.dup.frozen?.should be_false
+ end
+
+ it "causes mutative calls to raise RuntimeError" do
+ lambda{ @delegate.__setobj__("hola!") }.should raise_error( RuntimeError )
+ end
+
+ it "returns false if only the delegated object is frozen" do
+ DelegateSpecs::Delegator.new([1,2,3].freeze).frozen?.should be_false
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/hash_spec.rb b/spec/ruby/library/delegate/delegator/hash_spec.rb
new file mode 100644
index 0000000000..132cb91ccc
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/hash_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#hash" do
+ it "is delegated" do
+ base = mock('base')
+ delegator = DelegateSpecs::Delegator.new(base)
+ base.should_receive(:hash).and_return(42)
+ delegator.hash.should == 42
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/marshal_spec.rb b/spec/ruby/library/delegate/delegator/marshal_spec.rb
new file mode 100644
index 0000000000..6c75c8f573
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/marshal_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require 'delegate'
+
+describe "SimpleDelegator" do
+ before :all do
+ @obj = "hello"
+ @delegate = SimpleDelegator.new(@obj)
+ end
+
+ it "can be marshalled" do
+ m = Marshal.load(Marshal.dump(@delegate))
+ m.class.should == SimpleDelegator
+ (m == @obj).should be_true
+ end
+
+ it "can be marshalled with its instance variables intact" do
+ @delegate.instance_variable_set(:@foo, "bar")
+ m = Marshal.load(Marshal.dump(@delegate))
+ m.instance_variable_get(:@foo).should == "bar"
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/method_spec.rb b/spec/ruby/library/delegate/delegator/method_spec.rb
new file mode 100644
index 0000000000..7b66c28125
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/method_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#method" do
+ before :each do
+ @simple = DelegateSpecs::Simple.new
+ @delegate = DelegateSpecs::Delegator.new(@simple)
+ end
+
+ it "returns a method object for public methods of the delegate object" do
+ m = @delegate.method(:pub)
+ m.should be_an_instance_of(Method)
+ m.call.should == :foo
+ end
+
+ it "raises a NameError for protected methods of the delegate object" do
+ lambda {
+ -> {
+ @delegate.method(:prot)
+ }.should complain(/delegator does not forward private method #prot/)
+ }.should raise_error(NameError)
+ end
+
+ it "raises a NameError for a private methods of the delegate object" do
+ lambda {
+ -> {
+ @delegate.method(:priv)
+ }.should complain(/delegator does not forward private method #priv/)
+ }.should raise_error(NameError)
+ end
+
+ it "returns a method object for public methods of the Delegator class" do
+ m = @delegate.method(:extra)
+ m.should be_an_instance_of(Method)
+ m.call.should == :cheese
+ end
+
+ it "returns a method object for protected methods of the Delegator class" do
+ m = @delegate.method(:extra_protected)
+ m.should be_an_instance_of(Method)
+ m.call.should == :baz
+ end
+
+ it "returns a method object for private methods of the Delegator class" do
+ m = @delegate.method(:extra_private)
+ m.should be_an_instance_of(Method)
+ m.call.should == :bar
+ end
+
+ it "raises a NameError for an invalid method name" do
+ lambda {
+ @delegate.method(:invalid_and_silly_method_name)
+ }.should raise_error(NameError)
+ end
+
+ it "returns a method that respond_to_missing?" do
+ m = @delegate.method(:pub_too)
+ m.should be_an_instance_of(Method)
+ m.call.should == :pub_too
+ end
+
+ it "raises a NameError if method is no longer valid because object has changed" do
+ m = @delegate.method(:pub)
+ @delegate.__setobj__([1,2,3])
+ lambda {
+ m.call
+ }.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/methods_spec.rb b/spec/ruby/library/delegate/delegator/methods_spec.rb
new file mode 100644
index 0000000000..b9942bd230
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/methods_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#methods" do
+ before :all do
+ @simple = DelegateSpecs::Simple.new
+ class << @simple
+ def singleton_method
+ end
+ end
+
+ @delegate = DelegateSpecs::Delegator.new(@simple)
+ @methods = @delegate.methods
+ end
+
+ it "returns singleton methods when passed false" do
+ @delegate.methods(false).should include(:singleton_method)
+ end
+
+ it "includes all public methods of the delegate object" do
+ @methods.should include :pub
+ end
+
+ it "includes all protected methods of the delegate object" do
+ @methods.should include :prot
+ end
+
+ it "includes instance methods of the Delegator class" do
+ @methods.should include :extra
+ @methods.should include :extra_protected
+ end
+
+ it "does not include private methods" do
+ @methods.should_not include :priv
+ @methods.should_not include :extra_private
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/not_equal_spec.rb b/spec/ruby/library/delegate/delegator/not_equal_spec.rb
new file mode 100644
index 0000000000..6f2df21715
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/not_equal_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#!=" do
+ before :all do
+ @base = mock('base')
+ @delegator = DelegateSpecs::Delegator.new(@base)
+ end
+
+ it "is not delegated when passed self" do
+ @base.should_not_receive(:"!=")
+ (@delegator != @delegator).should be_false
+ end
+
+ it "is delegated when passed the delegated object" do
+ @base.should_receive(:"!=").and_return(true)
+ (@delegator != @base).should be_true
+ end
+
+ it "is delegated in general" do
+ @base.should_receive(:"!=").and_return(false)
+ (@delegator != 42).should be_false
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/not_spec.rb b/spec/ruby/library/delegate/delegator/not_spec.rb
new file mode 100644
index 0000000000..50105181c3
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/not_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#!" do
+ it "is delegated" do
+ base = mock('base')
+ delegator = DelegateSpecs::Delegator.new(base)
+ base.should_receive(:"!").and_return(:foo)
+ (!delegator).should == :foo
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/private_methods_spec.rb b/spec/ruby/library/delegate/delegator/private_methods_spec.rb
new file mode 100644
index 0000000000..7724b8d413
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/private_methods_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#private_methods" do
+ before :all do
+ @simple = DelegateSpecs::Simple.new
+ @delegate = DelegateSpecs::Delegator.new(@simple)
+ @methods = @delegate.private_methods
+ end
+
+ it "does not include any method of the delegate object" do # since delegates does not forward private calls
+ @methods.should_not include :priv
+ @methods.should_not include :prot
+ @methods.should_not include :pub
+ end
+
+ it "includes all private instance methods of the Delegate class" do
+ @methods.should include :extra_private
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/protected_methods_spec.rb b/spec/ruby/library/delegate/delegator/protected_methods_spec.rb
new file mode 100644
index 0000000000..fd7874fb21
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/protected_methods_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#protected_methods" do
+ before :all do
+ @simple = DelegateSpecs::Simple.new
+ @delegate = DelegateSpecs::Delegator.new(@simple)
+ @methods = @delegate.protected_methods
+ end
+
+ it "includes protected methods of the delegate object" do
+ @methods.should include :prot
+ end
+
+ it "includes protected instance methods of the Delegator class" do
+ @methods.should include :extra_protected
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/public_methods_spec.rb b/spec/ruby/library/delegate/delegator/public_methods_spec.rb
new file mode 100644
index 0000000000..18da16a613
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/public_methods_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#public_methods" do
+ before :all do
+ @simple = DelegateSpecs::Simple.new
+ @delegate = DelegateSpecs::Delegator.new(@simple)
+ @methods = @delegate.public_methods
+ end
+
+ it "includes public methods of the delegate object" do
+ @methods.should include :pub
+ end
+
+ it "includes public instance methods of the Delegator class" do
+ @methods.should include :extra
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/send_spec.rb b/spec/ruby/library/delegate/delegator/send_spec.rb
new file mode 100644
index 0000000000..b14e6996d1
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/send_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "SimpleDelegator.new" do
+ before :all do
+ @simple = DelegateSpecs::Simple.new
+ @delegate = SimpleDelegator.new(@simple)
+ end
+
+ it "forwards public method calls" do
+ @delegate.pub.should == :foo
+ end
+
+ it "forwards protected method calls" do
+ lambda{ @delegate.prot }.should raise_error( NoMethodError )
+ end
+
+ it "doesn't forward private method calls" do
+ lambda{ @delegate.priv }.should raise_error( NoMethodError )
+ end
+
+ it "doesn't forward private method calls even via send or __send__" do
+ lambda{ @delegate.send(:priv, 42) }.should raise_error( NoMethodError )
+ lambda{ @delegate.__send__(:priv, 42) }.should raise_error( NoMethodError )
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/taint_spec.rb b/spec/ruby/library/delegate/delegator/taint_spec.rb
new file mode 100644
index 0000000000..2dd0493b53
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/taint_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#taint" do
+ before :each do
+ @delegate = DelegateSpecs::Delegator.new("")
+ end
+
+ it "returns self" do
+ @delegate.taint.equal?(@delegate).should be_true
+ end
+
+ it "taints the delegator" do
+ @delegate.__setobj__(nil)
+ @delegate.taint
+ @delegate.tainted?.should be_true
+ end
+
+ it "taints the delegated object" do
+ @delegate.taint
+ @delegate.__getobj__.tainted?.should be_true
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/tap_spec.rb b/spec/ruby/library/delegate/delegator/tap_spec.rb
new file mode 100644
index 0000000000..34a88fa1d5
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/tap_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#tap" do
+ it "yield the delegator object" do
+ obj = mock('base')
+ delegator = DelegateSpecs::Delegator.new(obj)
+ obj.should_not_receive(:tap)
+ yielded = []
+ delegator.tap do |x|
+ yielded << x
+ end
+ yielded.size.should == 1
+ yielded[0].equal?(delegator).should be_true
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/trust_spec.rb b/spec/ruby/library/delegate/delegator/trust_spec.rb
new file mode 100644
index 0000000000..ba57b3ea18
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/trust_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#trust" do
+ before :each do
+ @delegate = DelegateSpecs::Delegator.new([])
+ end
+
+ it "returns self" do
+ @delegate.trust.equal?(@delegate).should be_true
+ end
+
+ it "trusts the delegator" do
+ @delegate.trust
+ @delegate.untrusted?.should be_false
+ end
+
+ it "trusts the delegated object" do
+ @delegate.trust
+ @delegate.__getobj__.untrusted?.should be_false
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/untaint_spec.rb b/spec/ruby/library/delegate/delegator/untaint_spec.rb
new file mode 100644
index 0000000000..4fa4509b53
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/untaint_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#untaint" do
+ before :each do
+ @delegate = lambda { DelegateSpecs::Delegator.new("") }.call
+ end
+
+ it "returns self" do
+ @delegate.untaint.equal?(@delegate).should be_true
+ end
+
+ it "untaints the delegator" do
+ @delegate.untaint
+ @delegate.tainted?.should be_false
+ # No additional meaningful test; that it does or not taint
+ # "for real" the delegator has no consequence
+ end
+
+ it "untaints the delegated object" do
+ @delegate.untaint
+ @delegate.__getobj__.tainted?.should be_false
+ end
+end
diff --git a/spec/ruby/library/delegate/delegator/untrust_spec.rb b/spec/ruby/library/delegate/delegator/untrust_spec.rb
new file mode 100644
index 0000000000..b7f4bc823e
--- /dev/null
+++ b/spec/ruby/library/delegate/delegator/untrust_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Delegator#untrust" do
+ before :each do
+ @delegate = DelegateSpecs::Delegator.new("")
+ end
+
+ it "returns self" do
+ @delegate.untrust.equal?(@delegate).should be_true
+ end
+
+ it "untrusts the delegator" do
+ @delegate.__setobj__(nil)
+ @delegate.untrust
+ @delegate.untrusted?.should be_true
+ end
+
+ it "untrusts the delegated object" do
+ @delegate.untrust
+ @delegate.__getobj__.untrusted?.should be_true
+ end
+end
diff --git a/spec/ruby/library/delegate/fixtures/classes.rb b/spec/ruby/library/delegate/fixtures/classes.rb
new file mode 100644
index 0000000000..3cb43eb8b1
--- /dev/null
+++ b/spec/ruby/library/delegate/fixtures/classes.rb
@@ -0,0 +1,60 @@
+require 'delegate'
+module DelegateSpecs
+ class Simple
+ def pub
+ :foo
+ end
+
+ def respond_to_missing?(method, priv=false)
+ method == :pub_too ||
+ (priv && method == :priv_too)
+ end
+
+ def method_missing(method, *args)
+ super unless respond_to_missing?(method, true)
+ method
+ end
+
+ def priv(arg=nil)
+ yield arg if block_given?
+ [:priv, arg]
+ end
+ private :priv
+
+ def prot
+ :protected
+ end
+ protected :prot
+ end
+
+ module Extra
+ def extra
+ :cheese
+ end
+
+ def extra_private
+ :bar
+ end
+ private :extra_private
+
+ def extra_protected
+ :baz
+ end
+ protected :extra_protected
+ end
+
+ class Delegator < ::Delegator
+ attr_accessor :data
+
+ attr_reader :__getobj__
+ def __setobj__(o)
+ @__getobj__ = o
+ end
+
+ include Extra
+ end
+
+ class DelegateClass < DelegateClass(Simple)
+ include Extra
+ end
+end
diff --git a/spec/ruby/library/digest/bubblebabble_spec.rb b/spec/ruby/library/digest/bubblebabble_spec.rb
new file mode 100644
index 0000000000..7e06a974a3
--- /dev/null
+++ b/spec/ruby/library/digest/bubblebabble_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+require 'digest/bubblebabble'
+
+describe "Digest.bubblebabble" do
+ it "returns a String" do
+ Digest.bubblebabble('').should be_an_instance_of(String)
+ end
+
+ it "returns a String in the The Bubble Babble Binary Data Encoding format" do
+ Digest.bubblebabble('').should == 'xexax'
+ Digest.bubblebabble('foo').should == 'xinik-zorox'
+ Digest.bubblebabble('bar').should == 'ximik-cosex'
+ Digest.bubblebabble('1234567890').should == 'xesef-disof-gytuf-katof-movif-baxux'
+ end
+
+ it "calls #to_str on an object and returns the bubble babble value of the result" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return('foo')
+ Digest.bubblebabble(obj).should == 'xinik-zorox'
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { Digest.bubblebabble(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a Fixnum" do
+ lambda { Digest.bubblebabble(9001) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/digest/hexencode_spec.rb b/spec/ruby/library/digest/hexencode_spec.rb
new file mode 100644
index 0000000000..717590dd14
--- /dev/null
+++ b/spec/ruby/library/digest/hexencode_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require 'digest'
+
+describe "Digest.hexencode" do
+ before :each do
+ @string = 'sample string'
+ @encoded = "73616d706c6520737472696e67"
+ end
+
+ it "returns '' when passed an empty String" do
+ Digest.hexencode('').should == ''
+ end
+
+ it "returns the hex-encoded value of a non-empty String" do
+ Digest.hexencode(@string).should == @encoded
+ end
+
+ it "calls #to_str on an object and returns the hex-encoded value of the result" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(@string)
+ Digest.hexencode(obj).should == @encoded
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { Digest.hexencode(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a Fixnum" do
+ lambda { Digest.hexencode(9001) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/digest/md5/append_spec.rb b/spec/ruby/library/digest/md5/append_spec.rb
new file mode 100644
index 0000000000..a7f841c883
--- /dev/null
+++ b/spec/ruby/library/digest/md5/append_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/update'
+
+describe "Digest::MD5#<<" do
+ it_behaves_like :md5_update, :<<
+end
diff --git a/spec/ruby/library/digest/md5/block_length_spec.rb b/spec/ruby/library/digest/md5/block_length_spec.rb
new file mode 100644
index 0000000000..14fb050abd
--- /dev/null
+++ b/spec/ruby/library/digest/md5/block_length_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::MD5#block_length" do
+
+ it "returns the length of digest block" do
+ cur_digest = Digest::MD5.new
+ cur_digest.block_length.should == MD5Constants::BlockLength
+ end
+
+end
diff --git a/spec/ruby/library/digest/md5/digest_bang_spec.rb b/spec/ruby/library/digest/md5/digest_bang_spec.rb
new file mode 100644
index 0000000000..7b884a16d9
--- /dev/null
+++ b/spec/ruby/library/digest/md5/digest_bang_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::MD5#digest!" do
+
+ it "returns a digest and can digest!" do
+ cur_digest = Digest::MD5.new
+ cur_digest << MD5Constants::Contents
+ cur_digest.digest!().should == MD5Constants::Digest
+ cur_digest.digest().should == MD5Constants::BlankDigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/md5/digest_length_spec.rb b/spec/ruby/library/digest/md5/digest_length_spec.rb
new file mode 100644
index 0000000000..47e071e329
--- /dev/null
+++ b/spec/ruby/library/digest/md5/digest_length_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::MD5#digest_length" do
+
+ it "returns the length of computed digests" do
+ cur_digest = Digest::MD5.new
+ cur_digest.digest_length.should == MD5Constants::DigestLength
+ end
+
+end
diff --git a/spec/ruby/library/digest/md5/digest_spec.rb b/spec/ruby/library/digest/md5/digest_spec.rb
new file mode 100644
index 0000000000..d9bbc45ee2
--- /dev/null
+++ b/spec/ruby/library/digest/md5/digest_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::MD5#digest" do
+
+ it "returns a digest" do
+ cur_digest = Digest::MD5.new
+ cur_digest.digest().should == MD5Constants::BlankDigest
+
+ # add something to check that the state is reset later
+ cur_digest << "test"
+
+ cur_digest.digest(MD5Constants::Contents).should == MD5Constants::Digest
+ # second invocation is intentional, to make sure there are no side-effects
+ cur_digest.digest(MD5Constants::Contents).should == MD5Constants::Digest
+
+ # after all is done, verify that the digest is in the original, blank state
+ cur_digest.digest.should == MD5Constants::BlankDigest
+ end
+
+end
+
+describe "Digest::MD5.digest" do
+
+ it "returns a digest" do
+ Digest::MD5.digest(MD5Constants::Contents).should == MD5Constants::Digest
+ # second invocation is intentional, to make sure there are no side-effects
+ Digest::MD5.digest(MD5Constants::Contents).should == MD5Constants::Digest
+ Digest::MD5.digest("").should == MD5Constants::BlankDigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/md5/equal_spec.rb b/spec/ruby/library/digest/md5/equal_spec.rb
new file mode 100644
index 0000000000..b0e36564cd
--- /dev/null
+++ b/spec/ruby/library/digest/md5/equal_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::MD5#==" do
+
+ it "equals itself" do
+ cur_digest = Digest::MD5.new
+ cur_digest.should == cur_digest
+ end
+
+ it "equals the string representing its hexdigest" do
+ cur_digest = Digest::MD5.new
+ cur_digest.should == MD5Constants::BlankHexdigest
+ end
+
+ it "equals the appropriate object that responds to to_str" do
+ # blank digest
+ cur_digest = Digest::MD5.new
+ obj = mock(MD5Constants::BlankHexdigest)
+ obj.should_receive(:to_str).and_return(MD5Constants::BlankHexdigest)
+ cur_digest.should == obj
+
+ # non-blank digest
+ cur_digest = Digest::MD5.new
+ cur_digest << "test"
+ d_value = cur_digest.hexdigest
+ (obj = mock(d_value)).should_receive(:to_str).and_return(d_value)
+ cur_digest.should == obj
+ end
+
+ it "equals the same digest for a different object" do
+ cur_digest = Digest::MD5.new
+ cur_digest2 = Digest::MD5.new
+ cur_digest.should == cur_digest2
+ end
+
+end
diff --git a/spec/ruby/library/digest/md5/file_spec.rb b/spec/ruby/library/digest/md5/file_spec.rb
new file mode 100644
index 0000000000..2684f50373
--- /dev/null
+++ b/spec/ruby/library/digest/md5/file_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative '../../../core/file/shared/read'
+
+describe "Digest::MD5.file" do
+
+ describe "when passed a path to a file that exists" do
+ before :each do
+ @file = tmp("md5_temp")
+ touch(@file, 'wb') {|f| f.write MD5Constants::Contents }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns a Digest::MD5 object" do
+ Digest::MD5.file(@file).should be_kind_of(Digest::MD5)
+ end
+
+ it "returns a Digest::MD5 object with the correct digest" do
+ Digest::MD5.file(@file).digest.should == MD5Constants::Digest
+ end
+
+ it "calls #to_str on an object and returns the Digest::MD5 with the result" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(@file)
+ result = Digest::MD5.file(obj)
+ result.should be_kind_of(Digest::MD5)
+ result.digest.should == MD5Constants::Digest
+ end
+ end
+
+ it_behaves_like :file_read_directory, :file, Digest::MD5
+
+ it "raises a Errno::ENOENT when passed a path that does not exist" do
+ lambda { Digest::MD5.file("") }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { Digest::MD5.file(nil) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/digest/md5/hexdigest_bang_spec.rb b/spec/ruby/library/digest/md5/hexdigest_bang_spec.rb
new file mode 100644
index 0000000000..a953eb3b4c
--- /dev/null
+++ b/spec/ruby/library/digest/md5/hexdigest_bang_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::MD5#hexdigest!" do
+
+ it "returns a hexdigest and resets the state" do
+ cur_digest = Digest::MD5.new
+
+ cur_digest << MD5Constants::Contents
+ cur_digest.hexdigest!.should == MD5Constants::Hexdigest
+ cur_digest.hexdigest.should == MD5Constants::BlankHexdigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/md5/hexdigest_spec.rb b/spec/ruby/library/digest/md5/hexdigest_spec.rb
new file mode 100644
index 0000000000..03ead68b82
--- /dev/null
+++ b/spec/ruby/library/digest/md5/hexdigest_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::MD5#hexdigest" do
+
+ it "returns a hexdigest" do
+ cur_digest = Digest::MD5.new
+ cur_digest.hexdigest.should == MD5Constants::BlankHexdigest
+
+ # add something to check that the state is reset later
+ cur_digest << "test"
+
+ cur_digest.hexdigest(MD5Constants::Contents).should == MD5Constants::Hexdigest
+ # second invocation is intentional, to make sure there are no side-effects
+ cur_digest.hexdigest(MD5Constants::Contents).should == MD5Constants::Hexdigest
+
+ # after all is done, verify that the digest is in the original, blank state
+ cur_digest.hexdigest.should == MD5Constants::BlankHexdigest
+ end
+
+end
+
+describe "Digest::MD5.hexdigest" do
+
+ it "returns a hexdigest" do
+ Digest::MD5.hexdigest(MD5Constants::Contents).should == MD5Constants::Hexdigest
+ # second invocation is intentional, to make sure there are no side-effects
+ Digest::MD5.hexdigest(MD5Constants::Contents).should == MD5Constants::Hexdigest
+ Digest::MD5.hexdigest("").should == MD5Constants::BlankHexdigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/md5/inspect_spec.rb b/spec/ruby/library/digest/md5/inspect_spec.rb
new file mode 100644
index 0000000000..decc86fba5
--- /dev/null
+++ b/spec/ruby/library/digest/md5/inspect_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::MD5#inspect" do
+
+ it "returns a Ruby object representation" do
+ cur_digest = Digest::MD5.new
+ cur_digest.inspect.should == "#<#{MD5Constants::Klass}: #{cur_digest.hexdigest()}>"
+ end
+
+end
diff --git a/spec/ruby/library/digest/md5/length_spec.rb b/spec/ruby/library/digest/md5/length_spec.rb
new file mode 100644
index 0000000000..b05b2a20fd
--- /dev/null
+++ b/spec/ruby/library/digest/md5/length_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/length'
+
+describe "Digest::MD5#length" do
+ it_behaves_like :md5_length, :length
+end
diff --git a/spec/ruby/library/digest/md5/reset_spec.rb b/spec/ruby/library/digest/md5/reset_spec.rb
new file mode 100644
index 0000000000..c937844f38
--- /dev/null
+++ b/spec/ruby/library/digest/md5/reset_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::MD5#reset" do
+
+ it "returns digest state to initial conditions" do
+ cur_digest = Digest::MD5.new
+ cur_digest.update MD5Constants::Contents
+ cur_digest.digest().should_not == MD5Constants::BlankDigest
+ cur_digest.reset
+ cur_digest.digest().should == MD5Constants::BlankDigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/md5/shared/constants.rb b/spec/ruby/library/digest/md5/shared/constants.rb
new file mode 100644
index 0000000000..fdfae56d63
--- /dev/null
+++ b/spec/ruby/library/digest/md5/shared/constants.rb
@@ -0,0 +1,16 @@
+# -*- encoding: binary -*-
+require 'digest/md5'
+
+module MD5Constants
+
+ Contents = "Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. \nIt has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. \nIt was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
+
+ Klass = ::Digest::MD5
+ BlockLength = 64
+ DigestLength = 16
+ BlankDigest = "\324\035\214\331\217\000\262\004\351\200\t\230\354\370B~"
+ Digest = "\2473\267qw\276\364\343\345\320\304\350\313\314\217n"
+ BlankHexdigest = "d41d8cd98f00b204e9800998ecf8427e"
+ Hexdigest = "a733b77177bef4e3e5d0c4e8cbcc8f6e"
+
+end
diff --git a/spec/ruby/library/digest/md5/shared/length.rb b/spec/ruby/library/digest/md5/shared/length.rb
new file mode 100644
index 0000000000..c5b2b97b58
--- /dev/null
+++ b/spec/ruby/library/digest/md5/shared/length.rb
@@ -0,0 +1,8 @@
+describe :md5_length, shared: true do
+ it "returns the length of the digest" do
+ cur_digest = Digest::MD5.new
+ cur_digest.send(@method).should == MD5Constants::BlankDigest.size
+ cur_digest << MD5Constants::Contents
+ cur_digest.send(@method).should == MD5Constants::Digest.size
+ end
+end
diff --git a/spec/ruby/library/digest/md5/shared/sample.rb b/spec/ruby/library/digest/md5/shared/sample.rb
new file mode 100644
index 0000000000..2bb4f658b1
--- /dev/null
+++ b/spec/ruby/library/digest/md5/shared/sample.rb
@@ -0,0 +1,17 @@
+# -*- encoding: binary -*-
+
+require 'digest/md5'
+
+module MD5Constants
+
+ Contents = "Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. \nIt has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. \nIt was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
+
+ Klass = ::Digest::MD5
+ BlockLength = 64
+ DigestLength = 16
+ BlankDigest = "\324\035\214\331\217\000\262\004\351\200\t\230\354\370B~"
+ Digest = "\2473\267qw\276\364\343\345\320\304\350\313\314\217n"
+ BlankHexdigest = "d41d8cd98f00b204e9800998ecf8427e"
+ Hexdigest = "a733b77177bef4e3e5d0c4e8cbcc8f6e"
+
+end
diff --git a/spec/ruby/library/digest/md5/shared/update.rb b/spec/ruby/library/digest/md5/shared/update.rb
new file mode 100644
index 0000000000..be8622aed5
--- /dev/null
+++ b/spec/ruby/library/digest/md5/shared/update.rb
@@ -0,0 +1,7 @@
+describe :md5_update, shared: true do
+ it "can update" do
+ cur_digest = Digest::MD5.new
+ cur_digest.send @method, MD5Constants::Contents
+ cur_digest.digest.should == MD5Constants::Digest
+ end
+end
diff --git a/spec/ruby/library/digest/md5/size_spec.rb b/spec/ruby/library/digest/md5/size_spec.rb
new file mode 100644
index 0000000000..22e3272d36
--- /dev/null
+++ b/spec/ruby/library/digest/md5/size_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/length'
+
+describe "Digest::MD5#size" do
+ it_behaves_like :md5_length, :size
+end
diff --git a/spec/ruby/library/digest/md5/to_s_spec.rb b/spec/ruby/library/digest/md5/to_s_spec.rb
new file mode 100644
index 0000000000..78d53d6967
--- /dev/null
+++ b/spec/ruby/library/digest/md5/to_s_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+
+require 'digest/md5'
+
+require_relative 'shared/constants'
+
+describe "Digest::MD5#to_s" do
+
+ it "returns a hexdigest" do
+ cur_digest = Digest::MD5.new
+ cur_digest.to_s.should == MD5Constants::BlankHexdigest
+ end
+
+ it "does not change the internal state" do
+ cur_digest = Digest::MD5.new
+ cur_digest.to_s.should == MD5Constants::BlankHexdigest
+ cur_digest.to_s.should == MD5Constants::BlankHexdigest
+
+ cur_digest << MD5Constants::Contents
+ cur_digest.to_s.should == MD5Constants::Hexdigest
+ cur_digest.to_s.should == MD5Constants::Hexdigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/md5/update_spec.rb b/spec/ruby/library/digest/md5/update_spec.rb
new file mode 100644
index 0000000000..4773db308c
--- /dev/null
+++ b/spec/ruby/library/digest/md5/update_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/update'
+
+describe "Digest::MD5#update" do
+ it_behaves_like :md5_update, :update
+end
diff --git a/spec/ruby/library/digest/sha1/digest_spec.rb b/spec/ruby/library/digest/sha1/digest_spec.rb
new file mode 100644
index 0000000000..03f805336f
--- /dev/null
+++ b/spec/ruby/library/digest/sha1/digest_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA1#digest" do
+
+ it "returns a digest" do
+ cur_digest = Digest::SHA1.new
+ cur_digest.digest().should == SHA1Constants::BlankDigest
+ cur_digest.digest(SHA1Constants::Contents).should == SHA1Constants::Digest
+ end
+
+end
+
+describe "Digest::SHA1.digest" do
+
+ it "returns a digest" do
+ Digest::SHA1.digest(SHA1Constants::Contents).should == SHA1Constants::Digest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha1/file_spec.rb b/spec/ruby/library/digest/sha1/file_spec.rb
new file mode 100644
index 0000000000..1ac4e1f756
--- /dev/null
+++ b/spec/ruby/library/digest/sha1/file_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative '../../../core/file/shared/read'
+
+describe "Digest::SHA1.file" do
+
+ describe "when passed a path to a file that exists" do
+ before :each do
+ @file = tmp("md5_temp")
+ touch(@file, 'wb') {|f| f.write SHA1Constants::Contents }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns a Digest::SHA1 object" do
+ Digest::SHA1.file(@file).should be_kind_of(Digest::SHA1)
+ end
+
+ it "returns a Digest::SHA1 object with the correct digest" do
+ Digest::SHA1.file(@file).digest.should == SHA1Constants::Digest
+ end
+
+ it "calls #to_str on an object and returns the Digest::SHA1 with the result" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(@file)
+ result = Digest::SHA1.file(obj)
+ result.should be_kind_of(Digest::SHA1)
+ result.digest.should == SHA1Constants::Digest
+ end
+ end
+
+ it_behaves_like :file_read_directory, :file, Digest::SHA1
+
+ it "raises a Errno::ENOENT when passed a path that does not exist" do
+ lambda { Digest::SHA1.file("") }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { Digest::SHA1.file(nil) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/digest/sha1/shared/constants.rb b/spec/ruby/library/digest/sha1/shared/constants.rb
new file mode 100644
index 0000000000..add86b1dd3
--- /dev/null
+++ b/spec/ruby/library/digest/sha1/shared/constants.rb
@@ -0,0 +1,17 @@
+# -*- encoding: binary -*-
+
+require 'digest/sha1'
+
+module SHA1Constants
+
+ Contents = "Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. \nIt has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. \nIt was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
+
+ Klass = ::Digest::SHA1
+ BlockLength = 64
+ DigestLength = 20
+ BlankDigest = "\3329\243\356^kK\r2U\277\357\225`\030\220\257\330\a\t"
+ Digest = "X!\255b\323\035\352\314a|q\344+\376\317\361V9\324\343"
+ BlankHexdigest = "da39a3ee5e6b4b0d3255bfef95601890afd80709"
+ Hexdigest = "e907d2ba21c6c74bc0efd76e44d11fb9bbb7a75e"
+
+end
diff --git a/spec/ruby/library/digest/sha256/append_spec.rb b/spec/ruby/library/digest/sha256/append_spec.rb
new file mode 100644
index 0000000000..9ca3496afc
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/append_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/update'
+
+describe "Digest::SHA256#<<" do
+ it_behaves_like :sha256_update, :<<
+end
diff --git a/spec/ruby/library/digest/sha256/block_length_spec.rb b/spec/ruby/library/digest/sha256/block_length_spec.rb
new file mode 100644
index 0000000000..1e29e832cf
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/block_length_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA256#block_length" do
+
+ it "returns the length of digest block" do
+ cur_digest = Digest::SHA256.new
+ cur_digest.block_length.should == SHA256Constants::BlockLength
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha256/digest_bang_spec.rb b/spec/ruby/library/digest/sha256/digest_bang_spec.rb
new file mode 100644
index 0000000000..690442221a
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/digest_bang_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA256#digest!" do
+
+ it "returns a digest and can digest!" do
+ cur_digest = Digest::SHA256.new
+ cur_digest << SHA256Constants::Contents
+ cur_digest.digest!().should == SHA256Constants::Digest
+ cur_digest.digest().should == SHA256Constants::BlankDigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha256/digest_length_spec.rb b/spec/ruby/library/digest/sha256/digest_length_spec.rb
new file mode 100644
index 0000000000..b5c8958e84
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/digest_length_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA256#digest_length" do
+
+ it "returns the length of computed digests" do
+ cur_digest = Digest::SHA256.new
+ cur_digest.digest_length.should == SHA256Constants::DigestLength
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha256/digest_spec.rb b/spec/ruby/library/digest/sha256/digest_spec.rb
new file mode 100644
index 0000000000..1e79f25627
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/digest_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA256#digest" do
+
+ it "returns a digest" do
+ cur_digest = Digest::SHA256.new
+ cur_digest.digest().should == SHA256Constants::BlankDigest
+
+ # add something to check that the state is reset later
+ cur_digest << "test"
+
+ cur_digest.digest(SHA256Constants::Contents).should == SHA256Constants::Digest
+ # second invocation is intentional, to make sure there are no side-effects
+ cur_digest.digest(SHA256Constants::Contents).should == SHA256Constants::Digest
+
+ # after all is done, verify that the digest is in the original, blank state
+ cur_digest.digest.should == SHA256Constants::BlankDigest
+ end
+
+end
+
+describe "Digest::SHA256.digest" do
+
+ it "returns a digest" do
+ Digest::SHA256.digest(SHA256Constants::Contents).should == SHA256Constants::Digest
+ # second invocation is intentional, to make sure there are no side-effects
+ Digest::SHA256.digest(SHA256Constants::Contents).should == SHA256Constants::Digest
+ Digest::SHA256.digest("").should == SHA256Constants::BlankDigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha256/equal_spec.rb b/spec/ruby/library/digest/sha256/equal_spec.rb
new file mode 100644
index 0000000000..84662aa068
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/equal_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA256#==" do
+
+ it "equals itself" do
+ cur_digest = Digest::SHA256.new
+ cur_digest.should == cur_digest
+ end
+
+ it "equals the string representing its hexdigest" do
+ cur_digest = Digest::SHA256.new
+ cur_digest.should == SHA256Constants::BlankHexdigest
+ end
+
+ it "equals the appropriate object that responds to to_str" do
+ # blank digest
+ cur_digest = Digest::SHA256.new
+ (obj = mock(SHA256Constants::BlankHexdigest)).should_receive(:to_str).and_return(SHA256Constants::BlankHexdigest)
+ cur_digest.should == obj
+
+ # non-blank digest
+ cur_digest = Digest::SHA256.new
+ cur_digest << "test"
+ d_value = cur_digest.hexdigest
+ (obj = mock(d_value)).should_receive(:to_str).and_return(d_value)
+ cur_digest.should == obj
+ end
+
+ it "equals the same digest for a different object" do
+ cur_digest = Digest::SHA256.new
+ cur_digest2 = Digest::SHA256.new
+ cur_digest.should == cur_digest2
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha256/file_spec.rb b/spec/ruby/library/digest/sha256/file_spec.rb
new file mode 100644
index 0000000000..3cccbb518f
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/file_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative '../../../core/file/shared/read'
+
+describe "Digest::SHA256.file" do
+
+ describe "when passed a path to a file that exists" do
+ before :each do
+ @file = tmp("md5_temp")
+ touch(@file, 'wb') {|f| f.write SHA256Constants::Contents }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns a Digest::SHA256 object" do
+ Digest::SHA256.file(@file).should be_kind_of(Digest::SHA256)
+ end
+
+ it "returns a Digest::SHA256 object with the correct digest" do
+ Digest::SHA256.file(@file).digest.should == SHA256Constants::Digest
+ end
+
+ it "calls #to_str on an object and returns the Digest::SHA256 with the result" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(@file)
+ result = Digest::SHA256.file(obj)
+ result.should be_kind_of(Digest::SHA256)
+ result.digest.should == SHA256Constants::Digest
+ end
+ end
+
+ it_behaves_like :file_read_directory, :file, Digest::SHA256
+
+ it "raises a Errno::ENOENT when passed a path that does not exist" do
+ lambda { Digest::SHA256.file("") }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { Digest::SHA256.file(nil) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/digest/sha256/hexdigest_bang_spec.rb b/spec/ruby/library/digest/sha256/hexdigest_bang_spec.rb
new file mode 100644
index 0000000000..1acd8043b3
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/hexdigest_bang_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA256#hexdigest!" do
+
+ it "returns a hexdigest and resets the state" do
+ cur_digest = Digest::SHA256.new
+
+ cur_digest << SHA256Constants::Contents
+ cur_digest.hexdigest!.should == SHA256Constants::Hexdigest
+ cur_digest.hexdigest.should == SHA256Constants::BlankHexdigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha256/hexdigest_spec.rb b/spec/ruby/library/digest/sha256/hexdigest_spec.rb
new file mode 100644
index 0000000000..4f748b33b4
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/hexdigest_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA256#hexdigest" do
+
+ it "returns a hexdigest" do
+ cur_digest = Digest::SHA256.new
+ cur_digest.hexdigest.should == SHA256Constants::BlankHexdigest
+
+ # add something to check that the state is reset later
+ cur_digest << "test"
+
+ cur_digest.hexdigest(SHA256Constants::Contents).should == SHA256Constants::Hexdigest
+ # second invocation is intentional, to make sure there are no side-effects
+ cur_digest.hexdigest(SHA256Constants::Contents).should == SHA256Constants::Hexdigest
+
+ # after all is done, verify that the digest is in the original, blank state
+ cur_digest.hexdigest.should == SHA256Constants::BlankHexdigest
+ end
+
+end
+
+describe "Digest::SHA256.hexdigest" do
+
+ it "returns a hexdigest" do
+ Digest::SHA256.hexdigest(SHA256Constants::Contents).should == SHA256Constants::Hexdigest
+ # second invocation is intentional, to make sure there are no side-effects
+ Digest::SHA256.hexdigest(SHA256Constants::Contents).should == SHA256Constants::Hexdigest
+ Digest::SHA256.hexdigest("").should == SHA256Constants::BlankHexdigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha256/inspect_spec.rb b/spec/ruby/library/digest/sha256/inspect_spec.rb
new file mode 100644
index 0000000000..ed606e4517
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/inspect_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA256#inspect" do
+
+ it "returns a Ruby object representation" do
+ cur_digest = Digest::SHA256.new
+ cur_digest.inspect.should == "#<#{SHA256Constants::Klass}: #{cur_digest.hexdigest()}>"
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha256/length_spec.rb b/spec/ruby/library/digest/sha256/length_spec.rb
new file mode 100644
index 0000000000..181ac564ad
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/length_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/length'
+
+describe "Digest::SHA256#length" do
+ it_behaves_like :sha256_length, :length
+end
diff --git a/spec/ruby/library/digest/sha256/reset_spec.rb b/spec/ruby/library/digest/sha256/reset_spec.rb
new file mode 100644
index 0000000000..f0eb4faea6
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/reset_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA256#reset" do
+
+ it "returns digest state to initial conditions" do
+ cur_digest = Digest::SHA256.new
+ cur_digest.update SHA256Constants::Contents
+ cur_digest.digest().should_not == SHA256Constants::BlankDigest
+ cur_digest.reset
+ cur_digest.digest().should == SHA256Constants::BlankDigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha256/shared/constants.rb b/spec/ruby/library/digest/sha256/shared/constants.rb
new file mode 100644
index 0000000000..dd5b48dca9
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/shared/constants.rb
@@ -0,0 +1,17 @@
+# -*- encoding: binary -*-
+
+require 'digest/sha2'
+
+module SHA256Constants
+
+ Contents = "Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. \nIt has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. \nIt was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
+
+ Klass = ::Digest::SHA256
+ BlockLength = 64
+ DigestLength = 32
+ BlankDigest = "\343\260\304B\230\374\034\024\232\373\364\310\231o\271$'\256A\344d\233\223L\244\225\231\exR\270U"
+ Digest = "\230b\265\344_\337\357\337\242\004\314\311A\211jb\350\373\254\370\365M\230B\002\372\020j\as\270\376"
+ BlankHexdigest = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
+ Hexdigest = "9862b5e45fdfefdfa204ccc941896a62e8fbacf8f54d984202fa106a0773b8fe"
+
+end
diff --git a/spec/ruby/library/digest/sha256/shared/length.rb b/spec/ruby/library/digest/sha256/shared/length.rb
new file mode 100644
index 0000000000..996673a5bd
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/shared/length.rb
@@ -0,0 +1,8 @@
+describe :sha256_length, shared: true do
+ it "returns the length of the digest" do
+ cur_digest = Digest::SHA256.new
+ cur_digest.send(@method).should == SHA256Constants::BlankDigest.size
+ cur_digest << SHA256Constants::Contents
+ cur_digest.send(@method).should == SHA256Constants::Digest.size
+ end
+end
diff --git a/spec/ruby/library/digest/sha256/shared/update.rb b/spec/ruby/library/digest/sha256/shared/update.rb
new file mode 100644
index 0000000000..0edc07935b
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/shared/update.rb
@@ -0,0 +1,7 @@
+describe :sha256_update, shared: true do
+ it "can update" do
+ cur_digest = Digest::SHA256.new
+ cur_digest.send @method, SHA256Constants::Contents
+ cur_digest.digest.should == SHA256Constants::Digest
+ end
+end
diff --git a/spec/ruby/library/digest/sha256/size_spec.rb b/spec/ruby/library/digest/sha256/size_spec.rb
new file mode 100644
index 0000000000..1028263342
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/size_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/length'
+
+describe "Digest::SHA256#size" do
+ it_behaves_like :sha256_length, :size
+end
diff --git a/spec/ruby/library/digest/sha256/to_s_spec.rb b/spec/ruby/library/digest/sha256/to_s_spec.rb
new file mode 100644
index 0000000000..8bedee3f98
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/to_s_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA256#to_s" do
+
+ it "returns a hexdigest" do
+ cur_digest = Digest::SHA256.new
+ cur_digest.to_s.should == SHA256Constants::BlankHexdigest
+ end
+
+ it "does not change the internal state" do
+ cur_digest = Digest::SHA256.new
+ cur_digest.to_s.should == SHA256Constants::BlankHexdigest
+ cur_digest.to_s.should == SHA256Constants::BlankHexdigest
+
+ cur_digest << SHA256Constants::Contents
+ cur_digest.to_s.should == SHA256Constants::Hexdigest
+ cur_digest.to_s.should == SHA256Constants::Hexdigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha256/update_spec.rb b/spec/ruby/library/digest/sha256/update_spec.rb
new file mode 100644
index 0000000000..92316eb752
--- /dev/null
+++ b/spec/ruby/library/digest/sha256/update_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/update'
+
+describe "Digest::SHA256#update" do
+ it_behaves_like :sha256_update, :update
+end
diff --git a/spec/ruby/library/digest/sha384/append_spec.rb b/spec/ruby/library/digest/sha384/append_spec.rb
new file mode 100644
index 0000000000..2bc0c5b90b
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/append_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/update'
+
+describe "Digest::SHA384#<<" do
+ it_behaves_like :sha384_update, :<<
+end
diff --git a/spec/ruby/library/digest/sha384/block_length_spec.rb b/spec/ruby/library/digest/sha384/block_length_spec.rb
new file mode 100644
index 0000000000..dff645ffb9
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/block_length_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA384#block_length" do
+
+ it "returns the length of digest block" do
+ cur_digest = Digest::SHA384.new
+ cur_digest.block_length.should == SHA384Constants::BlockLength
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha384/digest_bang_spec.rb b/spec/ruby/library/digest/sha384/digest_bang_spec.rb
new file mode 100644
index 0000000000..8711913deb
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/digest_bang_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA384#digest!" do
+
+ it "returns a digest and can digest!" do
+ cur_digest = Digest::SHA384.new
+ cur_digest << SHA384Constants::Contents
+ cur_digest.digest!().should == SHA384Constants::Digest
+ cur_digest.digest().should == SHA384Constants::BlankDigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha384/digest_length_spec.rb b/spec/ruby/library/digest/sha384/digest_length_spec.rb
new file mode 100644
index 0000000000..4067dd34af
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/digest_length_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA384#digest_length" do
+
+ it "returns the length of computed digests" do
+ cur_digest = Digest::SHA384.new
+ cur_digest.digest_length.should == SHA384Constants::DigestLength
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha384/digest_spec.rb b/spec/ruby/library/digest/sha384/digest_spec.rb
new file mode 100644
index 0000000000..d0e2825934
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/digest_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA384#digest" do
+
+ it "returns a digest" do
+ cur_digest = Digest::SHA384.new
+ cur_digest.digest().should == SHA384Constants::BlankDigest
+
+ # add something to check that the state is reset later
+ cur_digest << "test"
+
+ cur_digest.digest(SHA384Constants::Contents).should == SHA384Constants::Digest
+ # second invocation is intentional, to make sure there are no side-effects
+ cur_digest.digest(SHA384Constants::Contents).should == SHA384Constants::Digest
+
+ # after all is done, verify that the digest is in the original, blank state
+ cur_digest.digest.should == SHA384Constants::BlankDigest
+ end
+
+end
+
+describe "Digest::SHA384.digest" do
+
+ it "returns a digest" do
+ Digest::SHA384.digest(SHA384Constants::Contents).should == SHA384Constants::Digest
+ # second invocation is intentional, to make sure there are no side-effects
+ Digest::SHA384.digest(SHA384Constants::Contents).should == SHA384Constants::Digest
+ Digest::SHA384.digest("").should == SHA384Constants::BlankDigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha384/equal_spec.rb b/spec/ruby/library/digest/sha384/equal_spec.rb
new file mode 100644
index 0000000000..5d3483d79a
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/equal_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA384#==" do
+
+ it "equals itself" do
+ cur_digest = Digest::SHA384.new
+ cur_digest.should == cur_digest
+ end
+
+ it "equals the string representing its hexdigest" do
+ cur_digest = Digest::SHA384.new
+ cur_digest.should == SHA384Constants::BlankHexdigest
+ end
+
+ it "equals the appropriate object that responds to to_str" do
+ # blank digest
+ cur_digest = Digest::SHA384.new
+ (obj = mock(SHA384Constants::BlankHexdigest)).should_receive(:to_str).and_return(SHA384Constants::BlankHexdigest)
+ cur_digest.should == obj
+
+ # non-blank digest
+ cur_digest = Digest::SHA384.new
+ cur_digest << "test"
+ d_value = cur_digest.hexdigest
+ (obj = mock(d_value)).should_receive(:to_str).and_return(d_value)
+ cur_digest.should == obj
+ end
+
+ it "equals the same digest for a different object" do
+ cur_digest = Digest::SHA384.new
+ cur_digest2 = Digest::SHA384.new
+ cur_digest.should == cur_digest2
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha384/file_spec.rb b/spec/ruby/library/digest/sha384/file_spec.rb
new file mode 100644
index 0000000000..376d819040
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/file_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative '../../../core/file/shared/read'
+
+describe "Digest::SHA384.file" do
+
+ describe "when passed a path to a file that exists" do
+ before :each do
+ @file = tmp("md5_temp")
+ touch(@file, 'wb') {|f| f.write SHA384Constants::Contents }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns a Digest::SHA384 object" do
+ Digest::SHA384.file(@file).should be_kind_of(Digest::SHA384)
+ end
+
+ it "returns a Digest::SHA384 object with the correct digest" do
+ Digest::SHA384.file(@file).digest.should == SHA384Constants::Digest
+ end
+
+ it "calls #to_str on an object and returns the Digest::SHA384 with the result" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(@file)
+ result = Digest::SHA384.file(obj)
+ result.should be_kind_of(Digest::SHA384)
+ result.digest.should == SHA384Constants::Digest
+ end
+ end
+
+ it_behaves_like :file_read_directory, :file, Digest::SHA384
+
+ it "raises a Errno::ENOENT when passed a path that does not exist" do
+ lambda { Digest::SHA384.file("") }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { Digest::SHA384.file(nil) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/digest/sha384/hexdigest_bang_spec.rb b/spec/ruby/library/digest/sha384/hexdigest_bang_spec.rb
new file mode 100644
index 0000000000..8efceec3eb
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/hexdigest_bang_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA384#hexdigest!" do
+
+ it "returns a hexdigest and resets the state" do
+ cur_digest = Digest::SHA384.new
+
+ cur_digest << SHA384Constants::Contents
+ cur_digest.hexdigest!.should == SHA384Constants::Hexdigest
+ cur_digest.hexdigest.should == SHA384Constants::BlankHexdigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha384/hexdigest_spec.rb b/spec/ruby/library/digest/sha384/hexdigest_spec.rb
new file mode 100644
index 0000000000..07ea05c541
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/hexdigest_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA384#hexdigest" do
+
+ it "returns a hexdigest" do
+ cur_digest = Digest::SHA384.new
+ cur_digest.hexdigest.should == SHA384Constants::BlankHexdigest
+
+ # add something to check that the state is reset later
+ cur_digest << "test"
+
+ cur_digest.hexdigest(SHA384Constants::Contents).should == SHA384Constants::Hexdigest
+ # second invocation is intentional, to make sure there are no side-effects
+ cur_digest.hexdigest(SHA384Constants::Contents).should == SHA384Constants::Hexdigest
+
+ # after all is done, verify that the digest is in the original, blank state
+ cur_digest.hexdigest.should == SHA384Constants::BlankHexdigest
+ end
+
+end
+
+describe "Digest::SHA384.hexdigest" do
+
+ it "returns a hexdigest" do
+ Digest::SHA384.hexdigest(SHA384Constants::Contents).should == SHA384Constants::Hexdigest
+ # second invocation is intentional, to make sure there are no side-effects
+ Digest::SHA384.hexdigest(SHA384Constants::Contents).should == SHA384Constants::Hexdigest
+ Digest::SHA384.hexdigest("").should == SHA384Constants::BlankHexdigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha384/inspect_spec.rb b/spec/ruby/library/digest/sha384/inspect_spec.rb
new file mode 100644
index 0000000000..8f9f946cc5
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/inspect_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA384#inspect" do
+
+ it "returns a Ruby object representation" do
+ cur_digest = Digest::SHA384.new
+ cur_digest.inspect.should == "#<#{SHA384Constants::Klass}: #{cur_digest.hexdigest()}>"
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha384/length_spec.rb b/spec/ruby/library/digest/sha384/length_spec.rb
new file mode 100644
index 0000000000..33fed492ef
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/length_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/length'
+
+describe "Digest::SHA384#length" do
+ it_behaves_like :sha384_length, :length
+end
diff --git a/spec/ruby/library/digest/sha384/reset_spec.rb b/spec/ruby/library/digest/sha384/reset_spec.rb
new file mode 100644
index 0000000000..991b90903d
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/reset_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA384#reset" do
+
+ it "returns digest state to initial conditions" do
+ cur_digest = Digest::SHA384.new
+ cur_digest.update SHA384Constants::Contents
+ cur_digest.digest().should_not == SHA384Constants::BlankDigest
+ cur_digest.reset
+ cur_digest.digest().should == SHA384Constants::BlankDigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha384/shared/constants.rb b/spec/ruby/library/digest/sha384/shared/constants.rb
new file mode 100644
index 0000000000..3697384fc3
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/shared/constants.rb
@@ -0,0 +1,18 @@
+# -*- encoding: binary -*-
+
+require 'digest/sha2'
+
+module SHA384Constants
+
+ Contents = "Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. \nIt has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. \nIt was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
+
+
+ Klass = ::Digest::SHA384
+ BlockLength = 128
+ DigestLength = 48
+ BlankDigest = "8\260`\247Q\254\2268L\3312~\261\261\343j!\375\267\021\024\276\aCL\f\307\277c\366\341\332'N\336\277\347oe\373\325\032\322\361H\230\271["
+ Digest = "B&\266:\314\216z\361!TD\001{`\355\323\320MW%\270\272\0034n\034\026g\a\217\"\333s\202\275\002Y*\217]\207u\f\034\244\231\266f"
+ BlankHexdigest = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"
+ Hexdigest = "4226b63acc8e7af1215444017b60edd3d04d5725b8ba03346e1c1667078f22db7382bd02592a8f5d87750c1ca499b666"
+
+end
diff --git a/spec/ruby/library/digest/sha384/shared/length.rb b/spec/ruby/library/digest/sha384/shared/length.rb
new file mode 100644
index 0000000000..0c88288bcf
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/shared/length.rb
@@ -0,0 +1,8 @@
+describe :sha384_length, shared: true do
+ it "returns the length of the digest" do
+ cur_digest = Digest::SHA384.new
+ cur_digest.send(@method).should == SHA384Constants::BlankDigest.size
+ cur_digest << SHA384Constants::Contents
+ cur_digest.send(@method).should == SHA384Constants::Digest.size
+ end
+end
diff --git a/spec/ruby/library/digest/sha384/shared/update.rb b/spec/ruby/library/digest/sha384/shared/update.rb
new file mode 100644
index 0000000000..1c6e31cf6a
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/shared/update.rb
@@ -0,0 +1,7 @@
+describe :sha384_update, shared: true do
+ it "can update" do
+ cur_digest = Digest::SHA384.new
+ cur_digest.send @method, SHA384Constants::Contents
+ cur_digest.digest.should == SHA384Constants::Digest
+ end
+end
diff --git a/spec/ruby/library/digest/sha384/size_spec.rb b/spec/ruby/library/digest/sha384/size_spec.rb
new file mode 100644
index 0000000000..4c3b14f7a0
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/size_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/length'
+
+describe "Digest::SHA384#size" do
+ it_behaves_like :sha384_length, :size
+end
diff --git a/spec/ruby/library/digest/sha384/to_s_spec.rb b/spec/ruby/library/digest/sha384/to_s_spec.rb
new file mode 100644
index 0000000000..68ea9c013f
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/to_s_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA384#to_s" do
+
+ it "returns a hexdigest" do
+ cur_digest = Digest::SHA384.new
+ cur_digest.to_s.should == SHA384Constants::BlankHexdigest
+ end
+
+ it "does not change the internal state" do
+ cur_digest = Digest::SHA384.new
+ cur_digest.to_s.should == SHA384Constants::BlankHexdigest
+ cur_digest.to_s.should == SHA384Constants::BlankHexdigest
+
+ cur_digest << SHA384Constants::Contents
+ cur_digest.to_s.should == SHA384Constants::Hexdigest
+ cur_digest.to_s.should == SHA384Constants::Hexdigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha384/update_spec.rb b/spec/ruby/library/digest/sha384/update_spec.rb
new file mode 100644
index 0000000000..a1d0dd6068
--- /dev/null
+++ b/spec/ruby/library/digest/sha384/update_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/update'
+
+describe "Digest::SHA384#update" do
+ it_behaves_like :sha384_update, :update
+end
diff --git a/spec/ruby/library/digest/sha512/append_spec.rb b/spec/ruby/library/digest/sha512/append_spec.rb
new file mode 100644
index 0000000000..e5f84b56f4
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/append_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/update'
+
+describe "Digest::SHA512#<<" do
+ it_behaves_like :sha512_update, :<<
+end
diff --git a/spec/ruby/library/digest/sha512/block_length_spec.rb b/spec/ruby/library/digest/sha512/block_length_spec.rb
new file mode 100644
index 0000000000..947af841dd
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/block_length_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA512#block_length" do
+
+ it "returns the length of digest block" do
+ cur_digest = Digest::SHA512.new
+ cur_digest.block_length.should == SHA512Constants::BlockLength
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha512/digest_bang_spec.rb b/spec/ruby/library/digest/sha512/digest_bang_spec.rb
new file mode 100644
index 0000000000..981570b907
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/digest_bang_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA512#digest!" do
+
+ it "returns a digest and can digest!" do
+ cur_digest = Digest::SHA512.new
+ cur_digest << SHA512Constants::Contents
+ cur_digest.digest!().should == SHA512Constants::Digest
+ cur_digest.digest().should == SHA512Constants::BlankDigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha512/digest_length_spec.rb b/spec/ruby/library/digest/sha512/digest_length_spec.rb
new file mode 100644
index 0000000000..ff5956dd75
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/digest_length_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA512#digest_length" do
+
+ it "returns the length of computed digests" do
+ cur_digest = Digest::SHA512.new
+ cur_digest.digest_length.should == SHA512Constants::DigestLength
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha512/digest_spec.rb b/spec/ruby/library/digest/sha512/digest_spec.rb
new file mode 100644
index 0000000000..092efccc62
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/digest_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA512#digest" do
+
+ it "returns a digest" do
+ cur_digest = Digest::SHA512.new
+ cur_digest.digest().should == SHA512Constants::BlankDigest
+
+ # add something to check that the state is reset later
+ cur_digest << "test"
+
+ cur_digest.digest(SHA512Constants::Contents).should == SHA512Constants::Digest
+ # second invocation is intentional, to make sure there are no side-effects
+ cur_digest.digest(SHA512Constants::Contents).should == SHA512Constants::Digest
+
+ # after all is done, verify that the digest is in the original, blank state
+ cur_digest.digest.should == SHA512Constants::BlankDigest
+ end
+
+end
+
+describe "Digest::SHA512.digest" do
+
+ it "returns a digest" do
+ Digest::SHA512.digest(SHA512Constants::Contents).should == SHA512Constants::Digest
+ # second invocation is intentional, to make sure there are no side-effects
+ Digest::SHA512.digest(SHA512Constants::Contents).should == SHA512Constants::Digest
+ Digest::SHA512.digest("").should == SHA512Constants::BlankDigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha512/equal_spec.rb b/spec/ruby/library/digest/sha512/equal_spec.rb
new file mode 100644
index 0000000000..5100ced6e8
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/equal_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA512#==" do
+
+ it "equals itself" do
+ cur_digest = Digest::SHA512.new
+ cur_digest.should == cur_digest
+ end
+
+ it "equals the string representing its hexdigest" do
+ cur_digest = Digest::SHA512.new
+ cur_digest.should == SHA512Constants::BlankHexdigest
+ end
+
+ it "equals the appropriate object that responds to to_str" do
+ # blank digest
+ cur_digest = Digest::SHA512.new
+ (obj = mock(SHA512Constants::BlankHexdigest)).should_receive(:to_str).and_return(SHA512Constants::BlankHexdigest)
+ cur_digest.should == obj
+
+ # non-blank digest
+ cur_digest = Digest::SHA512.new
+ cur_digest << "test"
+ d_value = cur_digest.hexdigest
+ (obj = mock(d_value)).should_receive(:to_str).and_return(d_value)
+ cur_digest.should == obj
+ end
+
+ it "equals the same digest for a different object" do
+ cur_digest = Digest::SHA512.new
+ cur_digest2 = Digest::SHA512.new
+ cur_digest.should == cur_digest2
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha512/file_spec.rb b/spec/ruby/library/digest/sha512/file_spec.rb
new file mode 100644
index 0000000000..0043146efa
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/file_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative '../../../core/file/shared/read'
+
+describe "Digest::SHA512.file" do
+
+ describe "when passed a path to a file that exists" do
+ before :each do
+ @file = tmp("md5_temp")
+ touch(@file, 'wb') {|f| f.write SHA512Constants::Contents }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns a Digest::SHA512 object" do
+ Digest::SHA512.file(@file).should be_kind_of(Digest::SHA512)
+ end
+
+ it "returns a Digest::SHA512 object with the correct digest" do
+ Digest::SHA512.file(@file).digest.should == SHA512Constants::Digest
+ end
+
+ it "calls #to_str on an object and returns the Digest::SHA512 with the result" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(@file)
+ result = Digest::SHA512.file(obj)
+ result.should be_kind_of(Digest::SHA512)
+ result.digest.should == SHA512Constants::Digest
+ end
+ end
+
+ it_behaves_like :file_read_directory, :file, Digest::SHA512
+
+ it "raises a Errno::ENOENT when passed a path that does not exist" do
+ lambda { Digest::SHA512.file("") }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { Digest::SHA512.file(nil) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/digest/sha512/hexdigest_bang_spec.rb b/spec/ruby/library/digest/sha512/hexdigest_bang_spec.rb
new file mode 100644
index 0000000000..e9b0da6191
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/hexdigest_bang_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA512#hexdigest!" do
+
+ it "returns a hexdigest and resets the state" do
+ cur_digest = Digest::SHA512.new
+
+ cur_digest << SHA512Constants::Contents
+ cur_digest.hexdigest!.should == SHA512Constants::Hexdigest
+ cur_digest.hexdigest.should == SHA512Constants::BlankHexdigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha512/hexdigest_spec.rb b/spec/ruby/library/digest/sha512/hexdigest_spec.rb
new file mode 100644
index 0000000000..6e1dc3c642
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/hexdigest_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA512#hexdigest" do
+
+ it "returns a hexdigest" do
+ cur_digest = Digest::SHA512.new
+ cur_digest.hexdigest.should == SHA512Constants::BlankHexdigest
+
+ # add something to check that the state is reset later
+ cur_digest << "test"
+
+ cur_digest.hexdigest(SHA512Constants::Contents).should == SHA512Constants::Hexdigest
+ # second invocation is intentional, to make sure there are no side-effects
+ cur_digest.hexdigest(SHA512Constants::Contents).should == SHA512Constants::Hexdigest
+
+ # after all is done, verify that the digest is in the original, blank state
+ cur_digest.hexdigest.should == SHA512Constants::BlankHexdigest
+ end
+
+end
+
+describe "Digest::SHA512.hexdigest" do
+
+ it "returns a hexdigest" do
+ Digest::SHA512.hexdigest(SHA512Constants::Contents).should == SHA512Constants::Hexdigest
+ # second invocation is intentional, to make sure there are no side-effects
+ Digest::SHA512.hexdigest(SHA512Constants::Contents).should == SHA512Constants::Hexdigest
+ Digest::SHA512.hexdigest("").should == SHA512Constants::BlankHexdigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha512/inspect_spec.rb b/spec/ruby/library/digest/sha512/inspect_spec.rb
new file mode 100644
index 0000000000..54a466043a
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/inspect_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA512#inspect" do
+
+ it "returns a Ruby object representation" do
+ cur_digest = Digest::SHA512.new
+ cur_digest.inspect.should == "#<#{SHA512Constants::Klass}: #{cur_digest.hexdigest()}>"
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha512/length_spec.rb b/spec/ruby/library/digest/sha512/length_spec.rb
new file mode 100644
index 0000000000..e9fde90577
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/length_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/length'
+
+describe "Digest::SHA512#length" do
+ it_behaves_like :sha512_length, :length
+end
diff --git a/spec/ruby/library/digest/sha512/reset_spec.rb b/spec/ruby/library/digest/sha512/reset_spec.rb
new file mode 100644
index 0000000000..24a936d4ba
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/reset_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA512#reset" do
+
+ it "returns digest state to initial conditions" do
+ cur_digest = Digest::SHA512.new
+ cur_digest.update SHA512Constants::Contents
+ cur_digest.digest().should_not == SHA512Constants::BlankDigest
+ cur_digest.reset
+ cur_digest.digest().should == SHA512Constants::BlankDigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha512/shared/constants.rb b/spec/ruby/library/digest/sha512/shared/constants.rb
new file mode 100644
index 0000000000..80f5b7bc1d
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/shared/constants.rb
@@ -0,0 +1,17 @@
+# -*- encoding: binary -*-
+
+require 'digest/sha2'
+
+module SHA512Constants
+
+ Contents = "Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. \nIt has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. \nIt was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
+
+ Klass = ::Digest::SHA512
+ BlockLength = 128
+ DigestLength = 64
+ BlankDigest = "\317\203\3415~\357\270\275\361T(P\326m\200\a\326 \344\005\vW\025\334\203\364\251!\323l\351\316G\320\321<]\205\362\260\377\203\030\322\207~\354/c\2711\275GAz\201\24582z\371'\332>"
+ Digest = "\241\231\232\365\002z\241\331\242\310=\367F\272\004\326\331g\315n\251Q\222\250\374E\257\254=\325\225\003SM\350\244\234\220\233=\031\230A;\000\203\233\340\323t\333\271\222w\266\307\2678\344\255j\003\216\300"
+ BlankHexdigest = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
+ Hexdigest = "a1999af5027aa1d9a2c83df746ba04d6d967cd6ea95192a8fc45afac3dd59503534de8a49c909b3d1998413b00839be0d374dbb99277b6c7b738e4ad6a038ec0"
+
+end
diff --git a/spec/ruby/library/digest/sha512/shared/length.rb b/spec/ruby/library/digest/sha512/shared/length.rb
new file mode 100644
index 0000000000..c0609d5386
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/shared/length.rb
@@ -0,0 +1,8 @@
+describe :sha512_length, shared: true do
+ it "returns the length of the digest" do
+ cur_digest = Digest::SHA512.new
+ cur_digest.send(@method).should == SHA512Constants::BlankDigest.size
+ cur_digest << SHA512Constants::Contents
+ cur_digest.send(@method).should == SHA512Constants::Digest.size
+ end
+end
diff --git a/spec/ruby/library/digest/sha512/shared/update.rb b/spec/ruby/library/digest/sha512/shared/update.rb
new file mode 100644
index 0000000000..ca74dbf4df
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/shared/update.rb
@@ -0,0 +1,7 @@
+describe :sha512_update, shared: true do
+ it "can update" do
+ cur_digest = Digest::SHA512.new
+ cur_digest.send @method, SHA512Constants::Contents
+ cur_digest.digest.should == SHA512Constants::Digest
+ end
+end
diff --git a/spec/ruby/library/digest/sha512/size_spec.rb b/spec/ruby/library/digest/sha512/size_spec.rb
new file mode 100644
index 0000000000..6d0acdabdb
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/size_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/length'
+
+describe "Digest::SHA512#size" do
+ it_behaves_like :sha512_length, :size
+end
diff --git a/spec/ruby/library/digest/sha512/to_s_spec.rb b/spec/ruby/library/digest/sha512/to_s_spec.rb
new file mode 100644
index 0000000000..68d86241c9
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/to_s_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+
+describe "Digest::SHA512#to_s" do
+
+ it "returns a hexdigest" do
+ cur_digest = Digest::SHA512.new
+ cur_digest.to_s.should == SHA512Constants::BlankHexdigest
+ end
+
+ it "does not change the internal state" do
+ cur_digest = Digest::SHA512.new
+ cur_digest.to_s.should == SHA512Constants::BlankHexdigest
+ cur_digest.to_s.should == SHA512Constants::BlankHexdigest
+
+ cur_digest << SHA512Constants::Contents
+ cur_digest.to_s.should == SHA512Constants::Hexdigest
+ cur_digest.to_s.should == SHA512Constants::Hexdigest
+ end
+
+end
diff --git a/spec/ruby/library/digest/sha512/update_spec.rb b/spec/ruby/library/digest/sha512/update_spec.rb
new file mode 100644
index 0000000000..682d3a19bb
--- /dev/null
+++ b/spec/ruby/library/digest/sha512/update_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/constants'
+require_relative 'shared/update'
+
+describe "Digest::SHA512#update" do
+ it_behaves_like :sha512_update, :update
+end
diff --git a/spec/ruby/library/drb/fixtures/test_server.rb b/spec/ruby/library/drb/fixtures/test_server.rb
new file mode 100644
index 0000000000..9d412f4ac7
--- /dev/null
+++ b/spec/ruby/library/drb/fixtures/test_server.rb
@@ -0,0 +1,8 @@
+class TestServer
+ def add(*args)
+ args.inject {|n,v| n+v}
+ end
+ def add_yield(x)
+ return (yield x)+1
+ end
+end
diff --git a/spec/ruby/library/drb/start_service_spec.rb b/spec/ruby/library/drb/start_service_spec.rb
new file mode 100644
index 0000000000..016c8b2cff
--- /dev/null
+++ b/spec/ruby/library/drb/start_service_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/test_server'
+require 'drb'
+
+describe "DRb.start_service" do
+ before :each do
+ @server = DRb.start_service("druby://localhost:0", TestServer.new)
+ end
+
+ after :each do
+ DRb.stop_service if @server
+ end
+
+ it "runs a basic remote call" do
+ DRb.current_server.should == @server
+ obj = DRbObject.new(nil, @server.uri)
+ obj.add(1,2,3).should == 6
+ end
+
+ it "runs a basic remote call passing a block" do
+ DRb.current_server.should == @server
+ obj = DRbObject.new(nil, @server.uri)
+ obj.add_yield(2) do |i|
+ i.should == 2
+ i+1
+ end.should == 4
+ end
+end
diff --git a/spec/ruby/library/erb/def_class_spec.rb b/spec/ruby/library/erb/def_class_spec.rb
new file mode 100644
index 0000000000..88bd385f4c
--- /dev/null
+++ b/spec/ruby/library/erb/def_class_spec.rb
@@ -0,0 +1,29 @@
+require 'erb'
+require_relative '../../spec_helper'
+
+describe "ERB#def_class" do
+
+ it "return an unnamed class which has instance method to render eRuby script" do
+ input = <<'END'
+@arg1=<%=@arg1.inspect%>
+@arg2=<%=@arg2.inspect%>
+END
+ expected = <<'END'
+@arg1="foo"
+@arg2=123
+END
+ class MyClass1ForErb_
+ def initialize(arg1, arg2)
+ @arg1 = arg1; @arg2 = arg2
+ end
+ end
+ filename = 'example.rhtml'
+ #erb = ERB.new(File.read(filename))
+ erb = ERB.new(input)
+ erb.filename = filename
+ MyClass1ForErb = erb.def_class(MyClass1ForErb_, 'render()')
+ MyClass1ForErb.method_defined?(:render).should == true
+ MyClass1ForErb.new('foo', 123).render().should == expected
+ end
+
+end
diff --git a/spec/ruby/library/erb/def_method_spec.rb b/spec/ruby/library/erb/def_method_spec.rb
new file mode 100644
index 0000000000..188789a693
--- /dev/null
+++ b/spec/ruby/library/erb/def_method_spec.rb
@@ -0,0 +1,26 @@
+require 'erb'
+require_relative '../../spec_helper'
+
+describe "ERB#def_method" do
+
+ it "define module's instance method to render eRuby file" do
+ input = <<'END'
+arg1=<%= arg1.inspect %>
+arg2=<%= arg2.inspect %>
+END
+ expected = <<'END'
+arg1="foo"
+arg2=123
+END
+ #
+ filename = 'example.rhtml' # 'arg1' and 'arg2' are used in example.rhtml
+ #erb = ERB.new(File.read(filename))
+ erb = ERB.new(input)
+ class MyClass0ForErb
+ end
+ erb.def_method(MyClass0ForErb, 'render(arg1, arg2)', filename)
+ MyClass0ForErb.method_defined?(:render)
+ MyClass0ForErb.new.render('foo', 123).should == expected
+ end
+
+end
diff --git a/spec/ruby/library/erb/def_module_spec.rb b/spec/ruby/library/erb/def_module_spec.rb
new file mode 100644
index 0000000000..806e564ef0
--- /dev/null
+++ b/spec/ruby/library/erb/def_module_spec.rb
@@ -0,0 +1,27 @@
+require 'erb'
+require_relative '../../spec_helper'
+
+describe "ERB#def_module" do
+
+ it "return unnamed module which has instance method to render eRuby" do
+ input = <<'END'
+arg1=<%= arg1.inspect %>
+arg2=<%= arg2.inspect %>
+END
+ expected = <<'END'
+arg1="foo"
+arg2=123
+END
+ filename = 'example.rhtml'
+ #erb = ERB.new(File.read(filename))
+ erb = ERB.new(input)
+ erb.filename = filename
+ MyModule2ForErb = erb.def_module('render(arg1, arg2)')
+ MyModule2ForErb.method_defined?(':render')
+ class MyClass2ForErb
+ include MyModule2ForErb
+ end
+ MyClass2ForErb.new.render('foo', 123).should == expected
+ end
+
+end
diff --git a/spec/ruby/library/erb/defmethod/def_erb_method_spec.rb b/spec/ruby/library/erb/defmethod/def_erb_method_spec.rb
new file mode 100644
index 0000000000..dc1e044d9c
--- /dev/null
+++ b/spec/ruby/library/erb/defmethod/def_erb_method_spec.rb
@@ -0,0 +1,64 @@
+require 'erb'
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "ERB::DefMethod.def_erb_method" do
+
+
+ input = <<'END'
+<% for item in @items %>
+<b><%= item %></b>
+<% end %>
+END
+
+
+ it "define method to render eRuby file as an instance method of current module" do
+ expected = <<'END'
+
+<b>10</b>
+
+<b>20</b>
+
+<b>30</b>
+
+END
+ #
+ begin
+ file = tmp('_example.rhtml')
+ File.open(file, 'w') {|f| f.write(input) }
+ klass = Class.new do
+ extend ERB::DefMethod
+ def_erb_method('render()', file)
+ def initialize(items)
+ @items = items
+ end
+ end
+ klass.new([10,20,30]).render().should == expected
+ ensure
+ rm_r file
+ end
+
+ end
+
+
+ it "define method to render eRuby object as an instance method of current module" do
+ expected = <<'END'
+<b>10</b>
+<b>20</b>
+<b>30</b>
+END
+ #
+ MY_INPUT4_FOR_ERB = input
+ class MyClass4ForErb
+ extend ERB::DefMethod
+ erb = ERBSpecs.new_erb(MY_INPUT4_FOR_ERB, trim_mode: '<>')
+ def_erb_method('render()', erb)
+ def initialize(items)
+ @items = items
+ end
+ end
+ MyClass4ForErb.new([10,20,30]).render().should == expected
+ end
+
+
+end
diff --git a/spec/ruby/library/erb/filename_spec.rb b/spec/ruby/library/erb/filename_spec.rb
new file mode 100644
index 0000000000..26fed2897b
--- /dev/null
+++ b/spec/ruby/library/erb/filename_spec.rb
@@ -0,0 +1,40 @@
+require 'erb'
+require_relative '../../spec_helper'
+
+describe "ERB#filename" do
+ it "raises an exception if there are errors processing content" do
+ filename = 'foobar.rhtml'
+ erb = ERB.new('<% if true %>') # will raise SyntaxError
+ erb.filename = filename
+ lambda {
+ begin
+ erb.result(binding)
+ rescue Exception => e
+ @ex = e
+ raise e
+ end
+ }.should raise_error(SyntaxError)
+ expected = filename
+
+ @ex.message =~ /^(.*?):(\d+): /
+ $1.should == expected
+ $2.to_i.should == 1
+ end
+
+ it "uses '(erb)' as filename when filename is not set" do
+ erb = ERB.new('<% if true %>') # will raise SyntaxError
+ lambda {
+ begin
+ erb.result(binding)
+ rescue Exception => e
+ @ex = e
+ raise e
+ end
+ }.should raise_error(SyntaxError)
+ expected = '(erb)'
+
+ @ex.message =~ /^(.*?):(\d+): /
+ $1.should == expected
+ $2.to_i.should == 1
+ end
+end
diff --git a/spec/ruby/library/erb/fixtures/classes.rb b/spec/ruby/library/erb/fixtures/classes.rb
new file mode 100644
index 0000000000..03da889941
--- /dev/null
+++ b/spec/ruby/library/erb/fixtures/classes.rb
@@ -0,0 +1,9 @@
+module ERBSpecs
+ def self.new_erb(input, trim_mode: nil)
+ if ruby_version_is "2.6"
+ ERB.new(input, trim_mode: trim_mode)
+ else
+ ERB.new(input, nil, trim_mode)
+ end
+ end
+end
diff --git a/spec/ruby/library/erb/new_spec.rb b/spec/ruby/library/erb/new_spec.rb
new file mode 100644
index 0000000000..f1e5cd299e
--- /dev/null
+++ b/spec/ruby/library/erb/new_spec.rb
@@ -0,0 +1,148 @@
+require 'erb'
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "ERB.new" do
+ before :all do
+ @eruby_str = <<'END'
+<ul>
+<% list = [1,2,3] %>
+<% for item in list %>
+<% if item %>
+<li><%= item %></li>
+<% end %>
+<% end %>
+</ul>
+END
+
+ @eruby_str2 = <<'END'
+<ul>
+% list = [1,2,3]
+%for item in list
+% if item
+ <li><%= item %>
+ <% end %>
+<% end %>
+</ul>
+%%%
+END
+
+ end
+
+ it "compiles eRuby script into ruby code when trim mode is 0 or not specified" do
+ expected = "<ul>\n\n\n\n<li>1</li>\n\n\n\n<li>2</li>\n\n\n\n<li>3</li>\n\n\n</ul>\n"
+ [0, nil].each do |trim_mode|
+ ERBSpecs.new_erb(@eruby_str, trim_mode: trim_mode).result.should == expected
+ end
+ end
+
+ ruby_version_is "2.6" do
+ it "warns invalid trim_mode" do
+ lambda do
+ ERBSpecs.new_erb(@eruby_str, trim_mode: '')
+ end.should complain(/Invalid ERB trim mode/)
+ end
+ end
+
+ it "removes '\n' when trim_mode is 1 or '>'" do
+ expected = "<ul>\n<li>1</li>\n<li>2</li>\n<li>3</li>\n</ul>\n"
+ [1, '>'].each do |trim_mode|
+ ERBSpecs.new_erb(@eruby_str, trim_mode: trim_mode).result.should == expected
+ end
+ end
+
+ it "removes spaces at beginning of line and '\n' when trim_mode is 2 or '<>'" do
+ expected = "<ul>\n<li>1</li>\n<li>2</li>\n<li>3</li>\n</ul>\n"
+ [2, '<>'].each do |trim_mode|
+ ERBSpecs.new_erb(@eruby_str, trim_mode: trim_mode).result.should == expected
+ end
+ end
+
+ it "removes spaces around '<%- -%>' when trim_mode is '-'" do
+ expected = "<ul>\n <li>1 <li>2 <li>3</ul>\n"
+ input = <<'END'
+<ul>
+<%- for item in [1,2,3] -%>
+ <%- if item -%>
+ <li><%= item -%>
+ <%- end -%>
+<%- end -%>
+</ul>
+END
+
+ ERBSpecs.new_erb(input, trim_mode: '-').result.should == expected
+ end
+
+
+ it "does not support '<%-= expr %> even when trim_mode is '-'" do
+
+ input = <<'END'
+<p>
+ <%= expr -%>
+ <%-= expr -%>
+</p>
+END
+
+ lambda {
+ ERBSpecs.new_erb(input, trim_mode: '-').result
+ }.should raise_error(SyntaxError)
+ end
+
+ it "regards lines starting with '%' as '<% ... %>' when trim_mode is '%'" do
+ expected = "<ul>\n <li>1\n \n <li>2\n \n <li>3\n \n\n</ul>\n%%\n"
+ ERBSpecs.new_erb(@eruby_str2, trim_mode: "%").result.should == expected
+ end
+ it "regards lines starting with '%' as '<% ... %>' and remove \"\\n\" when trim_mode is '%>'" do
+ expected = "<ul>\n <li>1 <li>2 <li>3 </ul>\n%%\n"
+ ERBSpecs.new_erb(@eruby_str2, trim_mode: '%>').result.should == expected
+ end
+
+
+ it "regard lines starting with '%' as '<% ... %>' and remove \"\\n\" when trim_mode is '%<>'" do
+ expected = "<ul>\n <li>1\n \n <li>2\n \n <li>3\n \n</ul>\n%%\n"
+ ERBSpecs.new_erb(@eruby_str2, trim_mode: '%<>').result.should == expected
+ end
+
+
+ it "regard lines starting with '%' as '<% ... %>' and spaces around '<%- -%>' when trim_mode is '%-'" do
+ expected = "<ul>\n<li>1</li>\n<li>2</li>\n</ul>\n%%\n"
+ input = <<'END'
+<ul>
+%list = [1,2]
+%for item in list
+<li><%= item %></li>
+<% end %></ul>
+%%%
+END
+
+ ERBSpecs.new_erb(input, trim_mode: '%-').result.should == expected
+ end
+
+ it "changes '_erbout' variable name in the produced source" do
+ input = @eruby_str
+ if RUBY_VERSION >= '2.6'
+ match_erbout = ERB.new(input, trim_mode: nil).src
+ match_buf = ERB.new(input, trim_mode: nil, eoutvar: 'buf').src
+ else
+ match_erbout = ERB.new(input, nil, nil).src
+ match_buf = ERB.new(input, nil, nil, 'buf').src
+ end
+ match_erbout.gsub("_erbout", "buf").should == match_buf
+ end
+
+
+ it "ignores '<%# ... %>'" do
+ input = <<'END'
+<%# for item in list %>
+<b><%#= item %></b>
+<%# end %>
+END
+ ERBSpecs.new_erb(input).result.should == "\n<b></b>\n\n"
+ ERBSpecs.new_erb(input, trim_mode: '<>').result.should == "<b></b>\n"
+ end
+
+ it "forget local variables defined previous one" do
+ ERB.new(@eruby_str).result
+ lambda{ ERB.new("<%= list %>").result }.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/library/erb/result_spec.rb b/spec/ruby/library/erb/result_spec.rb
new file mode 100644
index 0000000000..eb9d0c5356
--- /dev/null
+++ b/spec/ruby/library/erb/result_spec.rb
@@ -0,0 +1,86 @@
+require 'erb'
+require_relative '../../spec_helper'
+
+describe "ERB#result" do
+
+
+ it "return the result of compiled ruby code" do
+ input = <<'END'
+<ul>
+<% for item in list %>
+ <li><%= item %>
+<% end %>
+</ul>
+END
+ expected = <<'END'
+<ul>
+
+ <li>AAA
+
+ <li>BBB
+
+ <li>CCC
+
+</ul>
+END
+ erb = ERB.new(input)
+ list = %w[AAA BBB CCC]
+ actual = erb.result(binding)
+ actual.should == expected
+ end
+
+
+ it "share local variables" do
+ input = "<% var = 456 %>"
+ expected = 456
+ var = 123
+ ERB.new(input).result(binding)
+ var.should == expected
+ end
+
+
+ it "is not able to h() or u() unless including ERB::Util" do
+ input = "<%=h '<>' %>"
+ lambda {
+ ERB.new(input).result()
+ }.should raise_error(NameError)
+ end
+
+
+ it "is able to h() or u() if ERB::Util is included" do
+ myerb1 = Class.new do
+ include ERB::Util
+ def main
+ input = "<%=h '<>' %>"
+ return ERB.new(input).result(binding)
+ end
+ end
+ expected = '&lt;&gt;'
+ actual = myerb1.new.main()
+ actual.should == expected
+ end
+
+
+ it "use TOPLEVEL_BINDING if binding is not passed" do
+ myerb2 = Class.new do
+ include ERB::Util
+ def main1
+ #input = "<%= binding.to_s %>"
+ input = "<%= _xxx_var_ %>"
+ return ERB.new(input).result()
+ end
+ def main2
+ input = "<%=h '<>' %>"
+ return ERB.new(input).result()
+ end
+ end
+
+ eval '_xxx_var_ = 123', TOPLEVEL_BINDING
+ expected = '123'
+ myerb2.new.main1().should == expected
+
+ lambda {
+ myerb2.new.main2()
+ }.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/library/erb/run_spec.rb b/spec/ruby/library/erb/run_spec.rb
new file mode 100644
index 0000000000..c4b82b155e
--- /dev/null
+++ b/spec/ruby/library/erb/run_spec.rb
@@ -0,0 +1,96 @@
+require 'erb'
+require_relative '../../spec_helper'
+
+describe "ERB#run" do
+ # TODO: what is this? why does it not use
+ # lambda { ... }.should output
+ def _steal_stdout
+ orig = $stdout
+ s = ''
+ def s.write(arg); self << arg.to_s; end
+ $stdout = s
+ begin
+ yield
+ ensure
+ $stdout = orig
+ end
+ return s
+ end
+
+ it "print the result of compiled ruby code" do
+ input = <<END
+<ul>
+<% for item in list %>
+ <li><%= item %>
+<% end %>
+</ul>
+END
+ expected = <<END
+<ul>
+
+ <li>AAA
+
+ <li>BBB
+
+ <li>CCC
+
+</ul>
+END
+ erb = ERB.new(input)
+ list = %w[AAA BBB CCC]
+ actual = _steal_stdout { erb.run(binding) }
+ actual.should == expected
+ end
+
+ it "share local variables" do
+ input = "<% var = 456 %>"
+ expected = 456
+ var = 123
+ _steal_stdout { ERB.new(input).run(binding) }
+ var.should == expected
+ end
+
+ it "is not able to h() or u() unless including ERB::Util" do
+ input = "<%=h '<>' %>"
+ lambda {
+ _steal_stdout { ERB.new(input).run() }
+ }.should raise_error(NameError)
+ end
+
+ it "is able to h() or u() if ERB::Util is included" do
+ myerb1 = Class.new do
+ include ERB::Util
+ def main
+ input = "<%=h '<>' %>"
+ ERB.new(input).run(binding)
+ end
+ end
+ expected = '&lt;&gt;'
+ actual = _steal_stdout { myerb1.new.main() }
+ actual.should == expected
+ end
+
+ it "use TOPLEVEL_BINDING if binding is not passed" do
+ myerb2 = Class.new do
+ include ERB::Util
+ def main1
+ #input = "<%= binding.to_s %>"
+ input = "<%= _xxx_var_ %>"
+ return ERB.new(input).run()
+ end
+ def main2
+ input = "<%=h '<>' %>"
+ return ERB.new(input).run()
+ end
+ end
+
+ eval '_xxx_var_ = 123', TOPLEVEL_BINDING
+ expected = '123'
+ actual = _steal_stdout { myerb2.new.main1() }
+ actual.should == expected
+
+ lambda {
+ _steal_stdout { myerb2.new.main2() }
+ }.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/library/erb/src_spec.rb b/spec/ruby/library/erb/src_spec.rb
new file mode 100644
index 0000000000..fc11b7e4b7
--- /dev/null
+++ b/spec/ruby/library/erb/src_spec.rb
@@ -0,0 +1,33 @@
+require 'erb'
+require_relative '../../spec_helper'
+
+describe "ERB#src" do
+
+ it "returns the compiled ruby code evaluated to a String" do
+ # note that what concrete code is emitted is not guaranteed.
+
+ input = <<'END'
+<ul>
+<% for item in list %>
+ <li><%= item %>
+<% end %>
+</ul>
+END
+
+ expected = <<'END'
+<ul>
+
+ <li>AAA
+
+ <li>BBB
+
+ <li>CCC
+
+</ul>
+END
+
+ list = %w[AAA BBB CCC]
+ eval(ERB.new(input).src).should == expected
+ end
+
+end
diff --git a/spec/ruby/library/erb/util/h_spec.rb b/spec/ruby/library/erb/util/h_spec.rb
new file mode 100644
index 0000000000..6de79cfd92
--- /dev/null
+++ b/spec/ruby/library/erb/util/h_spec.rb
@@ -0,0 +1,7 @@
+require 'erb'
+require_relative '../../../spec_helper'
+require_relative 'shared/html_escape'
+
+describe "ERB::Util.h" do
+ it_behaves_like :erb_util_html_escape, :h
+end
diff --git a/spec/ruby/library/erb/util/html_escape_spec.rb b/spec/ruby/library/erb/util/html_escape_spec.rb
new file mode 100644
index 0000000000..1c15fb8791
--- /dev/null
+++ b/spec/ruby/library/erb/util/html_escape_spec.rb
@@ -0,0 +1,7 @@
+require 'erb'
+require_relative '../../../spec_helper'
+require_relative 'shared/html_escape'
+
+describe "ERB::Util.html_escape" do
+ it_behaves_like :erb_util_html_escape, :html_escape
+end
diff --git a/spec/ruby/library/erb/util/shared/html_escape.rb b/spec/ruby/library/erb/util/shared/html_escape.rb
new file mode 100644
index 0000000000..71b378755e
--- /dev/null
+++ b/spec/ruby/library/erb/util/shared/html_escape.rb
@@ -0,0 +1,42 @@
+describe :erb_util_html_escape, shared: true do
+ it "escape (& < > \" ') to (&amp; &lt; &gt; &quot; &#39;)" do
+ input = '& < > " \''
+ expected = '&amp; &lt; &gt; &quot; &#39;'
+ ERB::Util.__send__(@method, input).should == expected
+ end
+
+ it "not escape characters except (& < > \" ')" do
+ input = (0x20..0x7E).to_a.collect {|ch| ch.chr}.join('')
+ expected = input.
+ gsub(/&/,'&amp;').
+ gsub(/</,'&lt;').
+ gsub(/>/,'&gt;').
+ gsub(/'/,'&#39;').
+ gsub(/"/,'&quot;')
+ ERB::Util.__send__(@method, input).should == expected
+ end
+
+ it "return empty string when argument is nil" do
+ input = nil
+ expected = ''
+ ERB::Util.__send__(@method, input).should == expected
+ end
+
+ it "returns string when argument is number" do
+ input = 123
+ expected = '123'
+ ERB::Util.__send__(@method, input).should == expected
+ input = 3.14159
+ expected = '3.14159'
+ ERB::Util.__send__(@method, input).should == expected
+ end
+
+ it "returns string when argument is boolean" do
+ input = true
+ expected = 'true'
+ ERB::Util.__send__(@method, input).should == expected
+ input = false
+ expected = 'false'
+ ERB::Util.__send__(@method, input).should == expected
+ end
+end
diff --git a/spec/ruby/library/erb/util/shared/url_encode.rb b/spec/ruby/library/erb/util/shared/url_encode.rb
new file mode 100644
index 0000000000..5ac6215523
--- /dev/null
+++ b/spec/ruby/library/erb/util/shared/url_encode.rb
@@ -0,0 +1,50 @@
+describe :erb_util_url_encode, shared: true do
+ it "encode characters" do
+ #input = (0x20..0x7E).to_a.collect{|ch| ch.chr}.join
+ input = " !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}"
+ expected = "%20%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D"
+ ERB::Util.__send__(@method, input).should == expected
+ end
+
+ ruby_version_is ""..."2.5" do
+ it "escapes tilde" do
+ ERB::Util.__send__(@method, "~").should == "%7E"
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "does not escape tilde" do
+ ERB::Util.__send__(@method, "~").should == "~"
+ end
+ end
+
+ it "encode unicode string" do
+ input = "http://ja.wikipedia.org/wiki/\343\203\255\343\203\240\343\202\271\343\202\253\343\203\273\343\203\221\343\203\255\343\203\273\343\202\246\343\203\253\343\203\273\343\203\251\343\203\224\343\203\245\343\202\277"
+ expected = 'http%3A%2F%2Fja.wikipedia.org%2Fwiki%2F%E3%83%AD%E3%83%A0%E3%82%B9%E3%82%AB%E3%83%BB%E3%83%91%E3%83%AD%E3%83%BB%E3%82%A6%E3%83%AB%E3%83%BB%E3%83%A9%E3%83%94%E3%83%A5%E3%82%BF'
+ ERB::Util.__send__(@method, input).should == expected
+ end
+
+ it "returns empty string when argument is nil" do
+ input = nil
+ expected = ''
+ ERB::Util.__send__(@method, input).should == expected
+ end
+
+ it "returns string when argument is number" do
+ input = 123
+ expected = '123'
+ ERB::Util.__send__(@method, input).should == expected
+ input = 3.14159
+ expected = '3.14159'
+ ERB::Util.__send__(@method, input).should == expected
+ end
+
+ it "returns string when argument is boolean" do
+ input = true
+ expected = 'true'
+ ERB::Util.__send__(@method, input).should == expected
+ input = false
+ expected = 'false'
+ ERB::Util.__send__(@method, input).should == expected
+ end
+end
diff --git a/spec/ruby/library/erb/util/u_spec.rb b/spec/ruby/library/erb/util/u_spec.rb
new file mode 100644
index 0000000000..2a08451031
--- /dev/null
+++ b/spec/ruby/library/erb/util/u_spec.rb
@@ -0,0 +1,7 @@
+require 'erb'
+require_relative '../../../spec_helper'
+require_relative 'shared/url_encode'
+
+describe "ERB::Util.u" do
+ it_behaves_like :erb_util_url_encode, :u
+end
diff --git a/spec/ruby/library/erb/util/url_encode_spec.rb b/spec/ruby/library/erb/util/url_encode_spec.rb
new file mode 100644
index 0000000000..5569a1cc64
--- /dev/null
+++ b/spec/ruby/library/erb/util/url_encode_spec.rb
@@ -0,0 +1,7 @@
+require 'erb'
+require_relative '../../../spec_helper'
+require_relative 'shared/url_encode'
+
+describe "ERB::Util.url_encode" do
+ it_behaves_like :erb_util_url_encode, :url_encode
+end
diff --git a/spec/ruby/library/etc/confstr_spec.rb b/spec/ruby/library/etc/confstr_spec.rb
new file mode 100644
index 0000000000..0c922a3a77
--- /dev/null
+++ b/spec/ruby/library/etc/confstr_spec.rb
@@ -0,0 +1,14 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require 'etc'
+
+platform_is_not :windows do
+ describe "Etc.confstr" do
+ it "returns a String for Etc::CS_PATH" do
+ Etc.confstr(Etc::CS_PATH).should be_an_instance_of(String)
+ end
+
+ it "raises Errno::EINVAL for unknown configuration variables" do
+ -> { Etc.confstr(-1) }.should raise_error(Errno::EINVAL)
+ end
+ end
+end
diff --git a/spec/ruby/library/etc/endgrent_spec.rb b/spec/ruby/library/etc/endgrent_spec.rb
new file mode 100644
index 0000000000..88de231d87
--- /dev/null
+++ b/spec/ruby/library/etc/endgrent_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/windows'
+require 'etc'
+
+describe "Etc.endgrent" do
+ it_behaves_like :etc_on_windows, :endgrent
+end
diff --git a/spec/ruby/library/etc/endpwent_spec.rb b/spec/ruby/library/etc/endpwent_spec.rb
new file mode 100644
index 0000000000..e4e564d251
--- /dev/null
+++ b/spec/ruby/library/etc/endpwent_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/windows'
+require 'etc'
+
+describe "Etc.endpwent" do
+ it_behaves_like :etc_on_windows, :endpwent
+end
diff --git a/spec/ruby/library/etc/getgrent_spec.rb b/spec/ruby/library/etc/getgrent_spec.rb
new file mode 100644
index 0000000000..45a4442262
--- /dev/null
+++ b/spec/ruby/library/etc/getgrent_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/windows'
+require 'etc'
+
+describe "Etc.getgrent" do
+ it_behaves_like :etc_on_windows, :getgrent
+end
diff --git a/spec/ruby/library/etc/getgrgid_spec.rb b/spec/ruby/library/etc/getgrgid_spec.rb
new file mode 100644
index 0000000000..04758912b3
--- /dev/null
+++ b/spec/ruby/library/etc/getgrgid_spec.rb
@@ -0,0 +1,76 @@
+require_relative '../../spec_helper'
+require 'etc'
+
+platform_is :windows do
+ describe "Etc.getgrgid" do
+ it "returns nil" do
+ Etc.getgrgid(1).should == nil
+ Etc.getgrgid(nil).should == nil
+ Etc.getgrgid('nil').should == nil
+ end
+ end
+end
+
+# TODO: verify these on non-windows, non-darwin OS
+platform_is_not :windows do
+ grpname = nil
+ guard -> {
+ grpname = IO.popen(%w'id -gn', err: IO::NULL, &:read).chomp
+ $?.success?
+ } do
+ describe "Etc.getgrgid" do
+ before :all do
+ @gid = `id -g`.strip.to_i
+ @name = grpname
+ end
+
+ it "returns a Etc::Group struct instance for the given user" do
+ gr = Etc.getgrgid(@gid)
+
+ gr.is_a?(Etc::Group).should == true
+ gr.gid.should == @gid
+ gr.name.should == @name
+ end
+
+ it "returns the Etc::Group for a given gid if it exists" do
+ grp = Etc.getgrgid(@gid)
+ grp.should be_kind_of(Etc::Group)
+ grp.gid.should == @gid
+ grp.name.should == @name
+ end
+
+ it "uses Process.gid as the default value for the argument" do
+ gr = Etc.getgrgid
+
+ gr.gid.should == @gid
+ gr.name.should == @name
+ end
+
+ it "returns the Group for a given gid if it exists" do
+ grp = Etc.getgrgid(@gid)
+ grp.should be_kind_of(Struct::Group)
+ grp.gid.should == @gid
+ grp.name.should == @name
+ end
+
+ it "raises if the group does not exist" do
+ lambda { Etc.getgrgid(9876)}.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if not passed an Integer" do
+ lambda { Etc.getgrgid("foo") }.should raise_error(TypeError)
+ lambda { Etc.getgrgid(nil) }.should raise_error(TypeError)
+ end
+
+ it "can be called safely by multiple threads" do
+ 20.times.map do
+ Thread.new do
+ 100.times do
+ Etc.getgrgid(@gid).gid.should == @gid
+ end
+ end
+ end.each(&:join)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/etc/getgrnam_spec.rb b/spec/ruby/library/etc/getgrnam_spec.rb
new file mode 100644
index 0000000000..3fe94ed5f5
--- /dev/null
+++ b/spec/ruby/library/etc/getgrnam_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require 'etc'
+
+platform_is :windows do
+ describe "Etc.getgrnam" do
+ it "returns nil" do
+ Etc.getgrnam(1).should == nil
+ Etc.getgrnam(nil).should == nil
+ Etc.getgrnam('nil').should == nil
+ end
+ end
+end
+
+platform_is_not :windows do
+ describe "Etc.getgrnam" do
+ it "returns a Etc::Group struct instance for the given group" do
+ gr_name = Etc.getgrent.name
+ Etc.endgrent
+ gr = Etc.getgrnam(gr_name)
+ gr.is_a?(Etc::Group).should == true
+ end
+
+ it "only accepts strings as argument" do
+ lambda {
+ Etc.getgrnam(123)
+ Etc.getgrnam(nil)
+ }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/etc/getlogin_spec.rb b/spec/ruby/library/etc/getlogin_spec.rb
new file mode 100644
index 0000000000..7a4fd79ae2
--- /dev/null
+++ b/spec/ruby/library/etc/getlogin_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require 'etc'
+
+describe "Etc.getlogin" do
+ it "returns the name associated with the current login activity" do
+ getlogin_null = false
+
+ # POSIX logname(1) shows getlogin(2)'s result
+ # NOTE: Etc.getlogin returns ENV['USER'] if getlogin(2) returns NULL
+ begin
+ # make Etc.getlogin to return nil if getlogin(3) returns NULL
+ envuser, ENV['USER'] = ENV['USER'], nil
+ if Etc.getlogin
+ if ENV['TRAVIS'] and platform_is(:darwin)
+ # See https://travis-ci.org/ruby/spec/jobs/285967744
+ # and https://travis-ci.org/ruby/spec/jobs/285999602
+ Etc.getlogin.should be_an_instance_of(String)
+ else
+ # Etc.getlogin returns the same result of logname(2)
+ # if it returns non NULL
+ if system("which logname", out: File::NULL, err: File::NULL)
+ Etc.getlogin.should == `logname`.chomp
+ else
+ # fallback to `id` command since `logname` is not available
+ Etc.getlogin.should == `id -un`.chomp
+ end
+ end
+ else
+ # Etc.getlogin may return nil if the login name is not set
+ # because of chroot or sudo or something.
+ Etc.getlogin.should be_nil
+ getlogin_null = true
+ end
+ ensure
+ ENV['USER'] = envuser
+ end
+
+ # if getlogin(2) returns NULL, Etc.getlogin returns ENV['USER']
+ if getlogin_null
+ Etc.getlogin.should == ENV['USER']
+ end
+ end
+end
diff --git a/spec/ruby/library/etc/getpwent_spec.rb b/spec/ruby/library/etc/getpwent_spec.rb
new file mode 100644
index 0000000000..9a911aed8f
--- /dev/null
+++ b/spec/ruby/library/etc/getpwent_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/windows'
+require 'etc'
+
+describe "Etc.getpwent" do
+ it_behaves_like :etc_on_windows, :getpwent
+end
diff --git a/spec/ruby/library/etc/getpwnam_spec.rb b/spec/ruby/library/etc/getpwnam_spec.rb
new file mode 100644
index 0000000000..2062ee51aa
--- /dev/null
+++ b/spec/ruby/library/etc/getpwnam_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require 'etc'
+
+platform_is :windows do
+ describe "Etc.getpwnam" do
+ it "returns nil" do
+ Etc.getpwnam(1).should == nil
+ Etc.getpwnam(nil).should == nil
+ Etc.getpwnam('nil').should == nil
+ end
+ end
+end
+
+platform_is_not :windows do
+ describe "Etc.getpwnam" do
+ it "returns a Etc::Passwd struct instance for the given user" do
+ pw = Etc.getpwnam(`whoami`.strip)
+ pw.is_a?(Etc::Passwd).should == true
+ end
+
+ it "only accepts strings as argument" do
+ lambda {
+ Etc.getpwnam(123)
+ Etc.getpwnam(nil)
+ }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/etc/getpwuid_spec.rb b/spec/ruby/library/etc/getpwuid_spec.rb
new file mode 100644
index 0000000000..3f6866ddc6
--- /dev/null
+++ b/spec/ruby/library/etc/getpwuid_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require 'etc'
+
+platform_is :windows do
+ describe "Etc.getpwuid" do
+ it "returns nil" do
+ Etc.getpwuid(1).should == nil
+ Etc.getpwuid(nil).should == nil
+ Etc.getpwuid('nil').should == nil
+ end
+ end
+end
+
+platform_is_not :windows do
+ describe "Etc.getpwuid" do
+ before :all do
+ @pw = Etc.getpwuid(`id -u`.strip.to_i)
+ end
+
+ it "returns a Etc::Passwd struct instance for the given user" do
+ @pw.is_a?(Etc::Passwd).should == true
+ end
+
+ it "uses Process.uid as the default value for the argument" do
+ pw = Etc.getpwuid
+ pw.should == @pw
+ end
+
+ it "only accepts integers as argument" do
+ lambda {
+ Etc.getpwuid("foo")
+ Etc.getpwuid(nil)
+ }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/etc/group_spec.rb b/spec/ruby/library/etc/group_spec.rb
new file mode 100644
index 0000000000..fdd39bda16
--- /dev/null
+++ b/spec/ruby/library/etc/group_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require_relative 'shared/windows'
+require 'etc'
+
+describe "Etc.group" do
+ it_behaves_like :etc_on_windows, :group
+
+ platform_is_not :windows do
+ it "returns a Etc::Group struct" do
+ group = Etc.group
+ begin
+ group.should be_an_instance_of(Etc::Group)
+ ensure
+ Etc.endgrent
+ end
+ end
+
+ it "raises a RuntimeError for parallel iteration" do
+ proc {
+ Etc.group do | group |
+ Etc.group do | group2 |
+ end
+ end
+ }.should raise_error(RuntimeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/etc/nprocessors_spec.rb b/spec/ruby/library/etc/nprocessors_spec.rb
new file mode 100644
index 0000000000..ec7ffc81da
--- /dev/null
+++ b/spec/ruby/library/etc/nprocessors_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require 'etc'
+
+describe "Etc.nprocessors" do
+ it "returns the number of online processors" do
+ Etc.nprocessors.should be_kind_of(Integer)
+ Etc.nprocessors.should >= 1
+ end
+end
diff --git a/spec/ruby/library/etc/passwd_spec.rb b/spec/ruby/library/etc/passwd_spec.rb
new file mode 100644
index 0000000000..d61dada451
--- /dev/null
+++ b/spec/ruby/library/etc/passwd_spec.rb
@@ -0,0 +1,15 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require 'etc'
+
+platform_is_not :windows do
+ describe "Etc.passwd" do
+ it "returns a Etc::Passwd struct" do
+ passwd = Etc.passwd
+ begin
+ passwd.should be_an_instance_of(Etc::Passwd)
+ ensure
+ Etc.endpwent
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/etc/shared/windows.rb b/spec/ruby/library/etc/shared/windows.rb
new file mode 100644
index 0000000000..8bae235199
--- /dev/null
+++ b/spec/ruby/library/etc/shared/windows.rb
@@ -0,0 +1,7 @@
+describe :etc_on_windows, shared: true do
+ platform_is :windows do
+ it "returns nil" do
+ Etc.send(@method).should == nil
+ end
+ end
+end
diff --git a/spec/ruby/library/etc/struct_group_spec.rb b/spec/ruby/library/etc/struct_group_spec.rb
new file mode 100644
index 0000000000..0b50ff578f
--- /dev/null
+++ b/spec/ruby/library/etc/struct_group_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+require 'etc'
+
+describe "Struct::Group" do
+ platform_is_not :windows do
+ grpname = IO.popen(%w'id -gn', err: IO::NULL, &:read)
+ next unless $?.success?
+ grpname.chomp!
+
+ before :all do
+ @g = Etc.getgrgid(`id -g`.strip.to_i)
+ end
+
+ it "returns group name" do
+ @g.name.should == grpname
+ end
+
+ it "returns group password" do
+ @g.passwd.is_a?(String).should == true
+ end
+
+ it "returns group id" do
+ @g.gid.should == `id -g`.strip.to_i
+ end
+
+ it "returns an array of users belonging to the group" do
+ @g.mem.is_a?(Array).should == true
+ end
+
+ it "can be compared to another object" do
+ (@g == nil).should == false
+ (@g == Object.new).should == false
+ end
+ end
+end
diff --git a/spec/ruby/library/etc/struct_passwd_spec.rb b/spec/ruby/library/etc/struct_passwd_spec.rb
new file mode 100644
index 0000000000..93ad9dfa2a
--- /dev/null
+++ b/spec/ruby/library/etc/struct_passwd_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require 'etc'
+
+describe "Struct::Passwd" do
+ platform_is_not :windows do
+ before :all do
+ @pw = Etc.getpwuid(`id -u`.strip.to_i)
+ end
+
+ it "returns user name" do
+ @pw.name.should == `id -un`.strip
+ end
+
+ it "returns user password" do
+ @pw.passwd.is_a?(String).should == true
+ end
+
+ it "returns user id" do
+ @pw.uid.should == `id -u`.strip.to_i
+ end
+
+ it "returns user group id" do
+ @pw.gid.should == `id -g`.strip.to_i
+ end
+
+ it "returns user personal information (gecos field)" do
+ @pw.gecos.is_a?(String).should == true
+ end
+
+ it "returns user home directory" do
+ @pw.dir.is_a?(String).should == true
+ end
+
+ it "returns user shell" do
+ @pw.shell.is_a?(String).should == true
+ end
+
+ it "can be compared to another object" do
+ (@pw == nil).should == false
+ (@pw == Object.new).should == false
+ end
+ end
+end
diff --git a/spec/ruby/library/etc/sysconf_spec.rb b/spec/ruby/library/etc/sysconf_spec.rb
new file mode 100644
index 0000000000..e7d59d1b22
--- /dev/null
+++ b/spec/ruby/library/etc/sysconf_spec.rb
@@ -0,0 +1,22 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require 'etc'
+
+platform_is_not :windows do
+ describe "Etc.sysconf" do
+ %w[
+ SC_ARG_MAX SC_CHILD_MAX SC_HOST_NAME_MAX SC_LOGIN_NAME_MAX SC_NGROUPS_MAX
+ SC_CLK_TCK SC_OPEN_MAX SC_PAGESIZE SC_RE_DUP_MAX SC_STREAM_MAX
+ SC_SYMLOOP_MAX SC_TTY_NAME_MAX SC_TZNAME_MAX SC_VERSION
+ ].each do |const|
+ it "returns the value of POSIX.1 system configuration variable #{const}" do
+ var = Etc.const_get(const)
+ value = Etc.sysconf(var)
+ if value.nil?
+ value.should == nil
+ else
+ value.should be_kind_of(Integer)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/etc/sysconfdir_spec.rb b/spec/ruby/library/etc/sysconfdir_spec.rb
new file mode 100644
index 0000000000..d54299c513
--- /dev/null
+++ b/spec/ruby/library/etc/sysconfdir_spec.rb
@@ -0,0 +1,8 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require 'etc'
+
+describe "Etc.sysconfdir" do
+ it "returns a String" do
+ Etc.sysconfdir.should be_an_instance_of(String)
+ end
+end
diff --git a/spec/ruby/library/etc/systmpdir_spec.rb b/spec/ruby/library/etc/systmpdir_spec.rb
new file mode 100644
index 0000000000..99c82903f8
--- /dev/null
+++ b/spec/ruby/library/etc/systmpdir_spec.rb
@@ -0,0 +1,8 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require 'etc'
+
+describe "Etc.systmpdir" do
+ it "returns a String" do
+ Etc.systmpdir.should be_an_instance_of(String)
+ end
+end
diff --git a/spec/ruby/library/expect/expect_spec.rb b/spec/ruby/library/expect/expect_spec.rb
new file mode 100644
index 0000000000..454023979f
--- /dev/null
+++ b/spec/ruby/library/expect/expect_spec.rb
@@ -0,0 +1,62 @@
+platform_is_not :windows do
+ require_relative '../../spec_helper'
+ require 'expect'
+
+ describe "IO#expect" do
+ before :each do
+ @read, @write = IO.pipe
+ end
+
+ after :each do
+ @read.close unless @read.closed?
+ @write.close unless @write.closed?
+ end
+
+ it "matches data against a Regexp" do
+ @write << "prompt> hello"
+
+ result = @read.expect(/[pf]rompt>/)
+ result.should == ["prompt>"]
+ end
+
+ it "matches data against a String" do
+ @write << "prompt> hello"
+
+ result = @read.expect("prompt>")
+ result.should == ["prompt>"]
+ end
+
+ it "returns any captures of the Regexp" do
+ @write << "prompt> hello"
+
+ result = @read.expect(/(pro)mpt(>)/)
+ result.should == ["prompt>", "pro", ">"]
+ end
+
+ it "returns raises IOError if the IO is closed" do
+ @write << "prompt> hello"
+ @read.close
+
+ lambda {
+ @read.expect("hello")
+ }.should raise_error(IOError)
+ end
+
+ it "returns nil if eof is hit" do
+ @write << "pro"
+ @write.close
+
+ @read.expect("prompt").should be_nil
+ end
+
+ it "yields the result if a block is given" do
+ @write << "prompt> hello"
+
+ res = nil
+
+ @read.expect("prompt>") { |x| res = x }
+
+ res.should == ["prompt>"]
+ end
+ end
+end
diff --git a/spec/ruby/library/fiber/alive_spec.rb b/spec/ruby/library/fiber/alive_spec.rb
new file mode 100644
index 0000000000..72663dd173
--- /dev/null
+++ b/spec/ruby/library/fiber/alive_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+
+with_feature :fiber_library do
+ require 'fiber'
+
+ describe "Fiber#alive?" do
+ it "returns true for a Fiber that hasn't had #resume called" do
+ fiber = Fiber.new { true }
+ fiber.alive?.should be_true
+ end
+
+ # FIXME: Better description?
+ it "returns true for a Fiber that's yielded to the caller" do
+ fiber = Fiber.new { Fiber.yield }
+ fiber.resume
+ fiber.alive?.should be_true
+ end
+
+ it "returns true when called from its Fiber" do
+ fiber = Fiber.new { fiber.alive?.should be_true }
+ fiber.resume
+ end
+
+ it "doesn't invoke the block associated with the Fiber" do
+ offthehook = mock('do not call')
+ offthehook.should_not_receive(:ring)
+ fiber = Fiber.new { offthehook.ring }
+ fiber.alive?
+ end
+
+ it "returns false for a Fiber that's dead" do
+ fiber = Fiber.new { true }
+ fiber.resume
+ lambda { fiber.resume }.should raise_error(FiberError)
+ fiber.alive?.should be_false
+ end
+
+ it "always returns false for a dead Fiber" do
+ fiber = Fiber.new { true }
+ fiber.resume
+ lambda { fiber.resume }.should raise_error(FiberError)
+ fiber.alive?.should be_false
+ lambda { fiber.resume }.should raise_error(FiberError)
+ fiber.alive?.should be_false
+ fiber.alive?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/library/fiber/current_spec.rb b/spec/ruby/library/fiber/current_spec.rb
new file mode 100644
index 0000000000..8b7fa7c4ca
--- /dev/null
+++ b/spec/ruby/library/fiber/current_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../spec_helper'
+
+with_feature :fiber_library do
+ require 'fiber'
+
+ describe "Fiber.current" do
+ it "returns the root Fiber when called outside of a Fiber" do
+ root = Fiber.current
+ root.should be_an_instance_of(Fiber)
+ # We can always transfer to the root Fiber; it will never die
+ 5.times do
+ root.transfer.should be_nil
+ root.alive?.should be_true
+ end
+ end
+
+ it "returns the current Fiber when called from a Fiber" do
+ fiber = Fiber.new do
+ this = Fiber.current
+ this.should be_an_instance_of(Fiber)
+ this.should == fiber
+ this.alive?.should be_true
+ end
+ fiber.resume
+ end
+
+ it "returns the current Fiber when called from a Fiber that transferred to another" do
+ states = []
+ fiber = Fiber.new do
+ states << :fiber
+ this = Fiber.current
+ this.should be_an_instance_of(Fiber)
+ this.should == fiber
+ this.alive?.should be_true
+ end
+
+ fiber2 = Fiber.new do
+ states << :fiber2
+ fiber.transfer
+ flunk
+ end
+
+ fiber3 = Fiber.new do
+ states << :fiber3
+ fiber2.transfer
+ flunk
+ end
+
+ fiber3.resume
+ states.should == [:fiber3, :fiber2, :fiber]
+ end
+ end
+end
diff --git a/spec/ruby/library/fiber/resume_spec.rb b/spec/ruby/library/fiber/resume_spec.rb
new file mode 100644
index 0000000000..9789cf5b7c
--- /dev/null
+++ b/spec/ruby/library/fiber/resume_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+with_feature :fiber_library do
+ require 'fiber'
+
+ describe "Fiber#resume" do
+ it "raises a FiberError if the Fiber has transferred control to another Fiber" do
+ fiber1 = Fiber.new { true }
+ fiber2 = Fiber.new { fiber1.transfer; Fiber.yield }
+ fiber2.resume
+ lambda { fiber2.resume }.should raise_error(FiberError)
+ end
+ end
+end
diff --git a/spec/ruby/library/fiber/transfer_spec.rb b/spec/ruby/library/fiber/transfer_spec.rb
new file mode 100644
index 0000000000..22bb568840
--- /dev/null
+++ b/spec/ruby/library/fiber/transfer_spec.rb
@@ -0,0 +1,88 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/fiber/resume'
+
+with_feature :fiber_library do
+ require 'fiber'
+
+ describe "Fiber#transfer" do
+ it_behaves_like :fiber_resume, :transfer
+ end
+
+ describe "Fiber#transfer" do
+ it "transfers control from one Fiber to another when called from a Fiber" do
+ fiber1 = Fiber.new { :fiber1 }
+ fiber2 = Fiber.new { fiber1.transfer; :fiber2 }
+ fiber2.resume.should == :fiber1
+ end
+
+ it "returns to the root Fiber when finished" do
+ f1 = Fiber.new { :fiber_1 }
+ f2 = Fiber.new { f1.transfer; :fiber_2 }
+
+ f2.transfer.should == :fiber_1
+ f2.transfer.should == :fiber_2
+ end
+
+ it "can be invoked from the same Fiber it transfers control to" do
+ states = []
+ fiber = Fiber.new { states << :start; fiber.transfer; states << :end }
+ fiber.transfer
+ states.should == [:start, :end]
+
+ states = []
+ fiber = Fiber.new { states << :start; fiber.transfer; states << :end }
+ fiber.resume
+ states.should == [:start, :end]
+ end
+
+ it "can transfer control to a Fiber that has transferred to another Fiber" do
+ states = []
+ fiber1 = Fiber.new { states << :fiber1 }
+ fiber2 = Fiber.new { states << :fiber2_start; fiber1.transfer; states << :fiber2_end}
+ fiber2.resume.should == [:fiber2_start, :fiber1]
+ fiber2.transfer.should == [:fiber2_start, :fiber1, :fiber2_end]
+ end
+
+ it "raises a FiberError when transferring to a Fiber which resumes itself" do
+ fiber = Fiber.new { fiber.resume }
+ lambda { fiber.transfer }.should raise_error(FiberError)
+ end
+
+ it "works if Fibers in different Threads each transfer to a Fiber in the same Thread" do
+ # This catches a bug where Fibers are running on a thread-pool
+ # and Fibers from a different Ruby Thread reuse the same native thread.
+ # Caching the Ruby Thread based on the native thread is not correct in that case,
+ # and the check for "fiber called across threads" in Fiber#transfer
+ # might be incorrect based on that.
+ 2.times do
+ Thread.new do
+ io_fiber = Fiber.new do |calling_fiber|
+ calling_fiber.transfer
+ end
+ io_fiber.transfer(Fiber.current)
+ value = Object.new
+ io_fiber.transfer(value).should equal value
+ end.join
+ end
+ end
+
+ it "transfers control between a non-main thread's root fiber to a child fiber and back again" do
+ states = []
+ thread = Thread.new do
+ f1 = Fiber.new do |f0|
+ states << 0
+ value2 = f0.transfer(1)
+ states << value2
+ 3
+ end
+
+ value1 = f1.transfer(Fiber.current)
+ states << value1
+ value3 = f1.transfer(2)
+ states << value3
+ end
+ thread.join
+ states.should == [0, 1, 2, 3]
+ end
+ end
+end
diff --git a/spec/ruby/library/find/find_spec.rb b/spec/ruby/library/find/find_spec.rb
new file mode 100644
index 0000000000..7cd76fa01b
--- /dev/null
+++ b/spec/ruby/library/find/find_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require 'find'
+
+describe "Find.find" do
+ before :each do
+ FindDirSpecs.create_mock_dirs
+ end
+
+ after :each do
+ FindDirSpecs.delete_mock_dirs
+ end
+
+ describe "when called without a block" do
+ it "returns an Enumerator" do
+ Find.find(FindDirSpecs.mock_dir).should be_an_instance_of(Enumerator)
+ Find.find(FindDirSpecs.mock_dir).to_a.sort.should == FindDirSpecs.expected_paths
+ end
+ end
+
+ it "should recursively yield every file in the directory" do
+ a = []
+
+ Find.find(FindDirSpecs.mock_dir) do |file|
+ a << file
+ end
+
+ a.sort.should == FindDirSpecs.expected_paths
+ end
+end
diff --git a/spec/ruby/library/find/fixtures/common.rb b/spec/ruby/library/find/fixtures/common.rb
new file mode 100644
index 0000000000..14a7edb09a
--- /dev/null
+++ b/spec/ruby/library/find/fixtures/common.rb
@@ -0,0 +1,174 @@
+module FindDirSpecs
+ def self.mock_dir(dirs = ['find_specs_mock'])
+ @mock_dir ||= tmp("")
+ File.join @mock_dir, dirs
+ end
+
+ # The names of the fixture directories and files used by
+ # various Find specs.
+ def self.mock_dir_files
+ unless @mock_dir_files
+ @mock_dir_files = %w[
+ .dotfile
+ .dotsubdir/.dotfile
+ .dotsubdir/nondotfile
+
+ deeply/.dotfile
+ deeply/nested/.dotfile.ext
+ deeply/nested/directory/structure/.ext
+ deeply/nested/directory/structure/bar
+ deeply/nested/directory/structure/baz
+ deeply/nested/directory/structure/file_one
+ deeply/nested/directory/structure/file_one.ext
+ deeply/nested/directory/structure/foo
+ deeply/nondotfile
+
+ file_one.ext
+ file_two.ext
+
+ dir_filename_ordering
+ dir/filename_ordering
+
+ nondotfile
+
+ subdir_one/.dotfile
+ subdir_one/nondotfile
+ subdir_two/nondotfile
+ subdir_two/nondotfile.ext
+
+ brace/a
+ brace/a.js
+ brace/a.erb
+ brace/a.js.rjs
+ brace/a.html.erb
+
+ special/+
+
+ special/^
+ special/$
+
+ special/(
+ special/)
+ special/[
+ special/]
+ special/{
+ special/}
+
+ special/test{1}/file[1]
+ ]
+
+ platform_is_not :windows do
+ @mock_dir_files += %w[
+ special/*
+ special/?
+
+ special/|
+ ]
+ end
+ end
+
+ @mock_dir_files
+ end
+
+ def self.create_mock_dirs
+ umask = File.umask 0
+ mock_dir_files.each do |name|
+ file = File.join mock_dir, name
+ mkdir_p File.dirname(file)
+ touch file
+ end
+ File.umask umask
+ end
+
+ def self.delete_mock_dirs
+ rm_r mock_dir
+ end
+
+ def self.expected_paths
+ unless @expected_paths
+ @expected_paths = %w[
+ .dotfile
+
+ .dotsubdir
+ .dotsubdir/.dotfile
+ .dotsubdir/nondotfile
+
+ deeply
+ deeply/.dotfile
+
+ deeply/nested
+ deeply/nested/.dotfile.ext
+
+ deeply/nested/directory
+
+ deeply/nested/directory/structure
+ deeply/nested/directory/structure/.ext
+ deeply/nested/directory/structure/bar
+ deeply/nested/directory/structure/baz
+ deeply/nested/directory/structure/file_one
+ deeply/nested/directory/structure/file_one.ext
+ deeply/nested/directory/structure/foo
+ deeply/nondotfile
+
+ file_one.ext
+ file_two.ext
+
+ dir_filename_ordering
+
+ dir
+ dir/filename_ordering
+
+ nondotfile
+
+ subdir_one
+ subdir_one/.dotfile
+ subdir_one/nondotfile
+
+ subdir_two
+ subdir_two/nondotfile
+ subdir_two/nondotfile.ext
+
+ brace
+ brace/a
+ brace/a.js
+ brace/a.erb
+ brace/a.js.rjs
+ brace/a.html.erb
+
+ special
+ special/+
+
+ special/^
+ special/$
+
+ special/(
+ special/)
+ special/[
+ special/]
+ special/{
+ special/}
+
+ special/test{1}
+ special/test{1}/file[1]
+ ]
+
+ platform_is_not :windows do
+ @expected_paths += %w[
+ special/*
+ special/?
+
+ special/|
+ ]
+ end
+
+ @expected_paths.map! do |file|
+ File.join(mock_dir, file)
+ end
+
+ @expected_paths << mock_dir
+ @expected_paths.sort!
+ end
+
+ @expected_paths
+ end
+end
diff --git a/spec/ruby/library/find/prune_spec.rb b/spec/ruby/library/find/prune_spec.rb
new file mode 100644
index 0000000000..25dc2cbf3e
--- /dev/null
+++ b/spec/ruby/library/find/prune_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'find'
+
+describe "Find.prune" do
+ it "should throw :prune" do
+ msg = catch(:prune) do
+ Find.prune
+ end
+
+ msg.should == nil
+ end
+end
diff --git a/spec/ruby/library/getoptlong/each_option_spec.rb b/spec/ruby/library/getoptlong/each_option_spec.rb
new file mode 100644
index 0000000000..c6d82af86d
--- /dev/null
+++ b/spec/ruby/library/getoptlong/each_option_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require 'getoptlong'
+require_relative 'shared/each'
+
+describe "GetoptLong#each_option" do
+ it_behaves_like :getoptlong_each, :each_option
+end
diff --git a/spec/ruby/library/getoptlong/each_spec.rb b/spec/ruby/library/getoptlong/each_spec.rb
new file mode 100644
index 0000000000..d9022f02af
--- /dev/null
+++ b/spec/ruby/library/getoptlong/each_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require 'getoptlong'
+require_relative 'shared/each'
+
+describe "GetoptLong#each" do
+ it_behaves_like :getoptlong_each, :each
+end
diff --git a/spec/ruby/library/getoptlong/error_message_spec.rb b/spec/ruby/library/getoptlong/error_message_spec.rb
new file mode 100644
index 0000000000..1ed9419f6c
--- /dev/null
+++ b/spec/ruby/library/getoptlong/error_message_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require 'getoptlong'
+
+describe "GetoptLong#error_message" do
+ it "returns nil if no error occurred" do
+ opts = GetoptLong.new
+ opts.error_message.should == nil
+ end
+
+ it "returns the error message of the last error that occurred" do
+ argv [] do
+ opts = GetoptLong.new
+ opts.quiet = true
+ opts.get
+ -> {
+ opts.ordering = GetoptLong::PERMUTE
+ }.should raise_error(ArgumentError) { |e|
+ e.message.should == "argument error"
+ opts.error_message.should == "argument error"
+ }
+ end
+ end
+end
diff --git a/spec/ruby/library/getoptlong/get_option_spec.rb b/spec/ruby/library/getoptlong/get_option_spec.rb
new file mode 100644
index 0000000000..3cb2044379
--- /dev/null
+++ b/spec/ruby/library/getoptlong/get_option_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require 'getoptlong'
+require_relative 'shared/get'
+
+describe "GetoptLong#get_option" do
+ it_behaves_like :getoptlong_get, :get_option
+end
diff --git a/spec/ruby/library/getoptlong/get_spec.rb b/spec/ruby/library/getoptlong/get_spec.rb
new file mode 100644
index 0000000000..a8ec586fc9
--- /dev/null
+++ b/spec/ruby/library/getoptlong/get_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require 'getoptlong'
+require_relative 'shared/get'
+
+describe "GetoptLong#get" do
+ it_behaves_like :getoptlong_get, :get
+end
diff --git a/spec/ruby/library/getoptlong/initialize_spec.rb b/spec/ruby/library/getoptlong/initialize_spec.rb
new file mode 100644
index 0000000000..782edbd981
--- /dev/null
+++ b/spec/ruby/library/getoptlong/initialize_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require 'getoptlong'
+
+describe "GetoptLong#initialize" do
+ it "sets ordering to REQUIRE_ORDER if ENV['POSIXLY_CORRECT'] is set" do
+ begin
+ old_env_value = ENV["POSIXLY_CORRECT"]
+ ENV["POSIXLY_CORRECT"] = ""
+
+ opt = GetoptLong.new
+ opt.ordering.should == GetoptLong::REQUIRE_ORDER
+ ensure
+ ENV["POSIXLY_CORRECT"] = old_env_value
+ end
+ end
+
+ it "sets ordering to PERMUTE if ENV['POSIXLY_CORRECT'] is not set" do
+ begin
+ old_env_value = ENV["POSIXLY_CORRECT"]
+ ENV["POSIXLY_CORRECT"] = nil
+
+ opt = GetoptLong.new
+ opt.ordering.should == GetoptLong::PERMUTE
+ ensure
+ ENV["POSIXLY_CORRECT"] = old_env_value
+ end
+ end
+end
diff --git a/spec/ruby/library/getoptlong/ordering_spec.rb b/spec/ruby/library/getoptlong/ordering_spec.rb
new file mode 100644
index 0000000000..e6b645018d
--- /dev/null
+++ b/spec/ruby/library/getoptlong/ordering_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require 'getoptlong'
+
+describe "GetoptLong#ordering=" do
+ it "raises an ArgumentError if called after processing has started" do
+ argv [ "--size", "10k", "--verbose" ] do
+ opts = GetoptLong.new([ '--size', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--verbose', GetoptLong::NO_ARGUMENT ])
+ opts.quiet = true
+ opts.get
+
+ lambda {
+ opts.ordering = GetoptLong::PERMUTE
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "raises an ArgumentError if given an invalid value" do
+ opts = GetoptLong.new
+
+ lambda {
+ opts.ordering = 12345
+ }.should raise_error(ArgumentError)
+ end
+
+ it "does not allow changing ordering to PERMUTE if ENV['POSIXLY_CORRECT'] is set" do
+ begin
+ old_env_value = ENV['POSIXLY_CORRECT']
+ ENV['POSIXLY_CORRECT'] = ""
+
+ opts = GetoptLong.new
+ opts.ordering = GetoptLong::PERMUTE
+ opts.ordering.should == GetoptLong::REQUIRE_ORDER
+ ensure
+ ENV['POSIXLY_CORRECT'] = old_env_value
+ end
+ end
+end
diff --git a/spec/ruby/library/getoptlong/set_options_spec.rb b/spec/ruby/library/getoptlong/set_options_spec.rb
new file mode 100644
index 0000000000..f2acccea28
--- /dev/null
+++ b/spec/ruby/library/getoptlong/set_options_spec.rb
@@ -0,0 +1,98 @@
+require_relative '../../spec_helper'
+require 'getoptlong'
+
+describe "GetoptLong#set_options" do
+ before :each do
+ @opts = GetoptLong.new
+ end
+
+ it "allows setting command line options" do
+ argv ["--size", "10k", "-v", "arg1", "arg2"] do
+ @opts.set_options(
+ ["--size", GetoptLong::REQUIRED_ARGUMENT],
+ ["--verbose", "-v", GetoptLong::NO_ARGUMENT]
+ )
+
+ @opts.get.should == ["--size", "10k"]
+ @opts.get.should == ["--verbose", ""]
+ @opts.get.should == nil
+ end
+ end
+
+ it "discards previously defined command line options" do
+ argv ["--size", "10k", "-v", "arg1", "arg2"] do
+ @opts.set_options(
+ ["--size", GetoptLong::REQUIRED_ARGUMENT],
+ ["--verbose", "-v", GetoptLong::NO_ARGUMENT]
+ )
+
+ @opts.set_options(
+ ["-s", "--size", GetoptLong::REQUIRED_ARGUMENT],
+ ["-v", GetoptLong::NO_ARGUMENT]
+ )
+
+ @opts.get.should == ["-s", "10k"]
+ @opts.get.should == ["-v", ""]
+ @opts.get.should == nil
+ end
+ end
+
+ it "raises an ArgumentError if too many argument flags where given" do
+ argv [] do
+ lambda {
+ @opts.set_options(["--size", GetoptLong::NO_ARGUMENT, GetoptLong::REQUIRED_ARGUMENT])
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "raises a RuntimeError if processing has already started" do
+ argv [] do
+ @opts.get
+ lambda {
+ @opts.set_options()
+ }.should raise_error(RuntimeError)
+ end
+ end
+
+ it "raises an ArgumentError if no argument flag was given" do
+ argv [] do
+ lambda {
+ @opts.set_options(["--size"])
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "raises an ArgumentError if one of the given arguments is not an Array" do
+ argv [] do
+ lambda {
+ @opts.set_options(
+ ["--size", GetoptLong::REQUIRED_ARGUMENT],
+ "test")
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "raises an ArgumentError if the same option is given twice" do
+ argv [] do
+ lambda {
+ @opts.set_options(
+ ["--size", GetoptLong::NO_ARGUMENT],
+ ["--size", GetoptLong::OPTIONAL_ARGUMENT])
+ }.should raise_error(ArgumentError)
+
+ lambda {
+ @opts.set_options(
+ ["--size", GetoptLong::NO_ARGUMENT],
+ ["-s", "--size", GetoptLong::OPTIONAL_ARGUMENT])
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "raises an ArgumentError if the given option is invalid" do
+ argv [] do
+ lambda {
+ @opts.set_options(["-size", GetoptLong::NO_ARGUMENT])
+ }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/library/getoptlong/shared/each.rb b/spec/ruby/library/getoptlong/shared/each.rb
new file mode 100644
index 0000000000..b534e24c0f
--- /dev/null
+++ b/spec/ruby/library/getoptlong/shared/each.rb
@@ -0,0 +1,18 @@
+describe :getoptlong_each, shared: true do
+ before :each do
+ @opts = GetoptLong.new(
+ [ '--size', '-s', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ],
+ [ '--query', '-q', GetoptLong::NO_ARGUMENT ],
+ [ '--check', '--valid', '-c', GetoptLong::NO_ARGUMENT ]
+ )
+ end
+
+ it "passes each argument/value pair to the block" do
+ argv [ "--size", "10k", "-v", "-q", "a.txt", "b.txt" ] do
+ pairs = []
+ @opts.send(@method) { |arg, val| pairs << [ arg, val ] }
+ pairs.should == [ [ "--size", "10k" ], [ "--verbose", "" ], [ "--query", ""] ]
+ end
+ end
+end
diff --git a/spec/ruby/library/getoptlong/shared/get.rb b/spec/ruby/library/getoptlong/shared/get.rb
new file mode 100644
index 0000000000..91a0fbaacc
--- /dev/null
+++ b/spec/ruby/library/getoptlong/shared/get.rb
@@ -0,0 +1,64 @@
+describe :getoptlong_get, shared: true do
+ before :each do
+ @opts = GetoptLong.new(
+ [ '--size', '-s', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ],
+ [ '--query', '-q', GetoptLong::NO_ARGUMENT ],
+ [ '--check', '--valid', '-c', GetoptLong::NO_ARGUMENT ]
+ )
+ @opts.quiet = true # silence using $deferr
+ end
+
+ it "returns the next option name and its argument as an Array" do
+ argv [ "--size", "10k", "-v", "-q", "a.txt", "b.txt" ] do
+ @opts.send(@method).should == [ "--size", "10k" ]
+ @opts.send(@method).should == [ "--verbose", "" ]
+ @opts.send(@method).should == [ "--query", ""]
+ @opts.send(@method).should == nil
+ end
+ end
+
+ it "shifts ARGV on each call" do
+ argv [ "--size", "10k", "-v", "-q", "a.txt", "b.txt" ] do
+ @opts.send(@method)
+ ARGV.should == [ "-v", "-q", "a.txt", "b.txt" ]
+
+ @opts.send(@method)
+ ARGV.should == [ "-q", "a.txt", "b.txt" ]
+
+ @opts.send(@method)
+ ARGV.should == [ "a.txt", "b.txt" ]
+
+ @opts.send(@method)
+ ARGV.should == [ "a.txt", "b.txt" ]
+ end
+ end
+
+ it "terminates processing when encountering '--'" do
+ argv [ "--size", "10k", "--", "-v", "-q", "a.txt", "b.txt" ] do
+ @opts.send(@method)
+ ARGV.should == ["--", "-v", "-q", "a.txt", "b.txt"]
+
+ @opts.send(@method)
+ ARGV.should == ["-v", "-q", "a.txt", "b.txt"]
+
+ @opts.send(@method)
+ ARGV.should == ["-v", "-q", "a.txt", "b.txt"]
+ end
+ end
+
+ it "raises a if an argument was required, but none given" do
+ argv [ "--size" ] do
+ lambda { @opts.send(@method) }.should raise_error(GetoptLong::MissingArgument)
+ end
+ end
+
+ ruby_version_is "2.5" do
+ # https://bugs.ruby-lang.org/issues/13858
+ it "returns multiline argument" do
+ argv [ "--size=\n10k\n" ] do
+ @opts.send(@method).should == [ "--size", "\n10k\n" ]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/getoptlong/terminate_spec.rb b/spec/ruby/library/getoptlong/terminate_spec.rb
new file mode 100644
index 0000000000..287945fe9b
--- /dev/null
+++ b/spec/ruby/library/getoptlong/terminate_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require 'getoptlong'
+
+describe "GetoptLong#terminate" do
+ before :each do
+ @opts = GetoptLong.new(
+ [ '--size', '-s', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ],
+ [ '--query', '-q', GetoptLong::NO_ARGUMENT ],
+ [ '--check', '--valid', '-c', GetoptLong::NO_ARGUMENT ]
+ )
+ end
+
+ it "terminates option processing" do
+ argv [ "--size", "10k", "-v", "-q", "a.txt", "b.txt" ] do
+ @opts.get.should == [ "--size", "10k" ]
+ @opts.terminate
+ @opts.get.should == nil
+ end
+ end
+
+ it "returns self when option processsing is terminated" do
+ @opts.terminate.should == @opts
+ end
+
+ it "returns nil when option processing was already terminated" do
+ @opts.terminate
+ @opts.terminate.should == nil
+ end
+end
diff --git a/spec/ruby/library/getoptlong/terminated_spec.rb b/spec/ruby/library/getoptlong/terminated_spec.rb
new file mode 100644
index 0000000000..01a8feddea
--- /dev/null
+++ b/spec/ruby/library/getoptlong/terminated_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require 'getoptlong'
+
+describe "GetoptLong#terminated?" do
+ it "returns true if option processing has terminated" do
+ argv [ "--size", "10k" ] do
+ opts = GetoptLong.new(["--size", GetoptLong::REQUIRED_ARGUMENT])
+ opts.terminated?.should == false
+
+ opts.get.should == ["--size", "10k"]
+ opts.terminated?.should == false
+
+ opts.get.should == nil
+ opts.terminated?.should == true
+ end
+ end
+end
diff --git a/spec/ruby/library/ipaddr/hton_spec.rb b/spec/ruby/library/ipaddr/hton_spec.rb
new file mode 100644
index 0000000000..9c0b821abf
--- /dev/null
+++ b/spec/ruby/library/ipaddr/hton_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require 'ipaddr'
+
+describe "IPAddr#hton" do
+
+ it "converts IPAddr to network byte order" do
+ addr = ''
+ IPAddr.new("1234:5678:9abc:def0:1234:5678:9abc:def0").hton.each_byte do |c|
+ addr += sprintf("%02x", c)
+ end
+ addr.should == "123456789abcdef0123456789abcdef0"
+ addr = ''
+ IPAddr.new("123.45.67.89").hton.each_byte do |c|
+ addr += sprintf("%02x", c)
+ end
+ addr.should == sprintf("%02x%02x%02x%02x", 123, 45, 67, 89)
+ end
+
+end
+
+describe "IPAddr#new_ntoh" do
+
+ it "creates a new IPAddr using hton notation" do
+ a = IPAddr.new("3ffe:505:2::")
+ IPAddr.new_ntoh(a.hton).to_s.should == "3ffe:505:2::"
+ a = IPAddr.new("192.168.2.1")
+ IPAddr.new_ntoh(a.hton).to_s.should == "192.168.2.1"
+ end
+
+end
diff --git a/spec/ruby/library/ipaddr/ipv4_conversion_spec.rb b/spec/ruby/library/ipaddr/ipv4_conversion_spec.rb
new file mode 100644
index 0000000000..9d45055d76
--- /dev/null
+++ b/spec/ruby/library/ipaddr/ipv4_conversion_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require 'ipaddr'
+
+describe "IPAddr#ipv4_compat" do
+
+ it "should ipv4_compat?" do
+ a = IPAddr.new("::192.168.1.2")
+ a.to_s.should == "::192.168.1.2"
+ a.to_string.should == "0000:0000:0000:0000:0000:0000:c0a8:0102"
+ a.family.should == Socket::AF_INET6
+ a.ipv4_compat?.should == true
+ b = a.native
+ b.to_s.should == "192.168.1.2"
+ b.family.should == Socket::AF_INET
+ b.ipv4_compat?.should == false
+
+ a = IPAddr.new("192.168.1.2")
+ b = a.ipv4_compat
+ b.to_s.should == "::192.168.1.2"
+ b.family.should == Socket::AF_INET6
+ end
+
+end
+
+describe "IPAddr#ipv4_mapped" do
+
+ it "should ipv4_mapped" do
+ a = IPAddr.new("::ffff:192.168.1.2")
+ a.to_s.should == "::ffff:192.168.1.2"
+ a.to_string.should == "0000:0000:0000:0000:0000:ffff:c0a8:0102"
+ a.family.should == Socket::AF_INET6
+ a.ipv4_mapped?.should == true
+ b = a.native
+ b.to_s.should == "192.168.1.2"
+ b.family.should == Socket::AF_INET
+ b.ipv4_mapped?.should == false
+
+ a = IPAddr.new("192.168.1.2")
+ b = a.ipv4_mapped
+ b.to_s.should == "::ffff:192.168.1.2"
+ b.family.should == Socket::AF_INET6
+ end
+
+end
diff --git a/spec/ruby/library/ipaddr/new_spec.rb b/spec/ruby/library/ipaddr/new_spec.rb
new file mode 100644
index 0000000000..77165d2857
--- /dev/null
+++ b/spec/ruby/library/ipaddr/new_spec.rb
@@ -0,0 +1,93 @@
+require_relative '../../spec_helper'
+require 'ipaddr'
+
+describe "IPAddr#new" do
+ it "initializes IPAddr" do
+ lambda{ IPAddr.new("3FFE:505:ffff::/48") }.should_not raise_error
+ lambda{ IPAddr.new("0:0:0:1::") }.should_not raise_error
+ lambda{ IPAddr.new("2001:200:300::/48") }.should_not raise_error
+ end
+
+ it "initializes IPAddr ipv6 address with short notation" do
+ a = IPAddr.new
+ a.to_s.should == "::"
+ a.to_string.should == "0000:0000:0000:0000:0000:0000:0000:0000"
+ a.family.should == Socket::AF_INET6
+ end
+
+ it "initializes IPAddr ipv6 address with long notation" do
+ a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678")
+ a.to_s.should == "123:4567:89ab:cdef:abc:def0:1234:5678"
+ a.to_string.should == "0123:4567:89ab:cdef:0abc:def0:1234:5678"
+ a.family.should == Socket::AF_INET6
+ end
+
+ it "initializes IPAddr ipv6 address with / subnet notation" do
+ a = IPAddr.new("3ffe:505:2::/48")
+ a.to_s.should == "3ffe:505:2::"
+ a.to_string.should == "3ffe:0505:0002:0000:0000:0000:0000:0000"
+ a.family.should == Socket::AF_INET6
+ a.ipv4?.should == false
+ a.ipv6?.should == true
+ a.inspect.should == "#<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>"
+ end
+
+ it "initializes IPAddr ipv6 address with mask subnet notation" do
+ a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::")
+ a.to_s.should == "3ffe:505:2::"
+ a.to_string.should == "3ffe:0505:0002:0000:0000:0000:0000:0000"
+ a.family.should == Socket::AF_INET6
+ end
+
+ it "initializes IPAddr ipv4 address with all zeroes" do
+ a = IPAddr.new("0.0.0.0")
+ a.to_s.should == "0.0.0.0"
+ a.to_string.should == "0.0.0.0"
+ a.family.should == Socket::AF_INET
+ end
+
+ it "initializes IPAddr ipv4 address" do
+ a = IPAddr.new("192.168.1.2")
+ a.to_s.should == "192.168.1.2"
+ a.to_string.should == "192.168.1.2"
+ a.family.should == Socket::AF_INET
+ a.ipv4?.should == true
+ a.ipv6?.should == false
+ end
+
+ it "initializes IPAddr ipv4 address with / subnet notation" do
+ a = IPAddr.new("192.168.1.2/24")
+ a.to_s.should == "192.168.1.0"
+ a.to_string.should == "192.168.1.0"
+ a.family.should == Socket::AF_INET
+ a.inspect.should == "#<IPAddr: IPv4:192.168.1.0/255.255.255.0>"
+ end
+
+ it "initializes IPAddr ipv4 address with subnet mask" do
+ a = IPAddr.new("192.168.1.2/255.255.255.0")
+ a.to_s.should == "192.168.1.0"
+ a.to_string.should == "192.168.1.0"
+ a.family.should == Socket::AF_INET
+ end
+
+ it "initializes IPAddr ipv4 mapped address with subnet mask" do
+ a = IPAddr.new("::1:192.168.1.2/120")
+ a.to_s.should == "::1:c0a8:100"
+ a.to_string.should == "0000:0000:0000:0000:0000:0001:c0a8:0100"
+ a.family.should == Socket::AF_INET6
+ end
+
+ it "raises on incorrect IPAddr strings" do
+ [
+ ["fe80::1%fxp0"],
+ ["::1/255.255.255.0"],
+ [IPAddr.new("::1").to_i],
+ ["::ffff:192.168.1.2/120", Socket::AF_INET],
+ ["[192.168.1.2]/120"],
+ ].each { |args|
+ lambda{
+ IPAddr.new(*args)
+ }.should raise_error(ArgumentError)
+ }
+ end
+end
diff --git a/spec/ruby/library/ipaddr/operator_spec.rb b/spec/ruby/library/ipaddr/operator_spec.rb
new file mode 100644
index 0000000000..fe94a39ae1
--- /dev/null
+++ b/spec/ruby/library/ipaddr/operator_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../../spec_helper'
+require 'ipaddr'
+
+describe "IPAddr Operator" do
+ IN6MASK32 = "ffff:ffff::"
+ IN6MASK128 = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+
+ before do
+ @in6_addr_any = IPAddr.new()
+ @a = IPAddr.new("3ffe:505:2::/48")
+ @b = IPAddr.new("0:0:0:1::")
+ @c = IPAddr.new(IN6MASK32)
+ end
+
+ it "bitwises or" do
+ (@a | @b).to_s.should == "3ffe:505:2:1::"
+ a = @a
+ a |= @b
+ a.to_s.should == "3ffe:505:2:1::"
+ @a.to_s.should == "3ffe:505:2::"
+ (@a | 0x00000000000000010000000000000000).to_s.should == "3ffe:505:2:1::"
+ end
+
+ it "bitwises and" do
+ (@a & @c).to_s.should == "3ffe:505::"
+ a = @a
+ a &= @c
+ a.to_s.should == "3ffe:505::"
+ @a.to_s.should == "3ffe:505:2::"
+ (@a & 0xffffffff000000000000000000000000).to_s.should == "3ffe:505::"
+ end
+
+ it "bitshifts right" do
+ (@a >> 16).to_s.should == "0:3ffe:505:2::"
+ a = @a
+ a >>= 16
+ a.to_s.should == "0:3ffe:505:2::"
+ @a.to_s.should == "3ffe:505:2::"
+ end
+
+ it "bitshifts left" do
+ (@a << 16).to_s.should == "505:2::"
+ a = @a
+ a <<= 16
+ a.to_s.should == "505:2::"
+ @a.to_s.should == "3ffe:505:2::"
+ end
+
+ it "inverts" do
+ a = ~@in6_addr_any
+ a.to_s.should == IN6MASK128
+ @in6_addr_any.to_s.should == "::"
+ end
+
+ it "tests for equality" do
+ @a.should == IPAddr.new("3ffe:505:2::")
+ @a.should_not == IPAddr.new("3ffe:505:3::")
+ end
+
+ ruby_version_is '2.4' do
+ # https://bugs.ruby-lang.org/issues/12799
+ it "tests for equality correctly if object cannot be converted to IPAddr" do
+ IPAddr.new("1.1.1.1").should_not == "sometext"
+ end
+ end
+
+ it "sets a mask" do
+ a = @a.mask(32)
+ a.to_s.should == "3ffe:505::"
+ @a.to_s.should == "3ffe:505:2::"
+ end
+
+ it "checks whether an address is included in a range" do
+ @a.should include(IPAddr.new("3ffe:505:2::"))
+ @a.should include(IPAddr.new("3ffe:505:2::1"))
+ @a.should_not include(IPAddr.new("3ffe:505:3::"))
+ net1 = IPAddr.new("192.168.2.0/24")
+ net1.should include(IPAddr.new("192.168.2.0"))
+ net1.should include(IPAddr.new("192.168.2.255"))
+ net1.should_not include(IPAddr.new("192.168.3.0"))
+ # test with integer parameter
+ int = (192 << 24) + (168 << 16) + (2 << 8) + 13
+
+ net1.should include(int)
+ net1.should_not include(int+255)
+ end
+end
diff --git a/spec/ruby/library/ipaddr/reverse_spec.rb b/spec/ruby/library/ipaddr/reverse_spec.rb
new file mode 100644
index 0000000000..db39b6c7a0
--- /dev/null
+++ b/spec/ruby/library/ipaddr/reverse_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require 'ipaddr'
+
+describe "IPAddr#reverse" do
+ it "generates the reverse DNS lookup entry" do
+ IPAddr.new("3ffe:505:2::f").reverse.should == "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa"
+ IPAddr.new("192.168.2.1").reverse.should == "1.2.168.192.in-addr.arpa"
+ end
+end
+
+describe "IPAddr#ip6_arpa" do
+ it "converts an IPv6 address into the reverse DNS lookup representation according to RFC3172" do
+ IPAddr.new("3ffe:505:2::f").ip6_arpa.should == "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa"
+ lambda{
+ IPAddr.new("192.168.2.1").ip6_arpa
+ }.should raise_error(ArgumentError)
+ end
+end
+
+describe "IPAddr#ip6_int" do
+ it "converts an IPv6 address into the reverse DNS lookup representation according to RFC1886" do
+ IPAddr.new("3ffe:505:2::f").ip6_int.should == "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.int"
+ lambda{
+ IPAddr.new("192.168.2.1").ip6_int
+ }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/ipaddr/to_s_spec.rb b/spec/ruby/library/ipaddr/to_s_spec.rb
new file mode 100644
index 0000000000..2a9a027909
--- /dev/null
+++ b/spec/ruby/library/ipaddr/to_s_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require 'ipaddr'
+
+describe "IPAddr#to_s" do
+
+ it "displays IPAddr using short notation" do
+ IPAddr.new("0:0:0:1::").to_s.should == "0:0:0:1::"
+ IPAddr.new("2001:200:300::/48").to_s.should == "2001:200:300::"
+ IPAddr.new("[2001:200:300::]/48").to_s.should == "2001:200:300::"
+ IPAddr.new("3ffe:505:2::1").to_s.should == "3ffe:505:2::1"
+ end
+
+end
+
+describe "IPAddr#to_string" do
+ it "displays an IPAddr using full notation" do
+ IPAddr.new("3ffe:505:2::1").to_string.should == "3ffe:0505:0002:0000:0000:0000:0000:0001"
+ end
+
+end
diff --git a/spec/ruby/library/logger/device/close_spec.rb b/spec/ruby/library/logger/device/close_spec.rb
new file mode 100644
index 0000000000..3d7ab134e0
--- /dev/null
+++ b/spec/ruby/library/logger/device/close_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/common'
+
+describe "Logger::LogDevice#close" do
+ before :each do
+ @file_path = tmp("test_log.log")
+ @log_file = File.open(@file_path, "w+")
+
+ # Avoid testing this with STDERR, we don't want to be closing that.
+ @device = Logger::LogDevice.new(@log_file)
+ end
+
+ after :each do
+ @log_file.close unless @log_file.closed?
+ rm_r @file_path
+ end
+
+ it "closes the LogDevice's stream" do
+ @device.close
+ lambda { @device.write("Test") }.should complain(/\Alog writing failed\./)
+ end
+end
diff --git a/spec/ruby/library/logger/device/new_spec.rb b/spec/ruby/library/logger/device/new_spec.rb
new file mode 100644
index 0000000000..a66c364392
--- /dev/null
+++ b/spec/ruby/library/logger/device/new_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/common'
+
+describe "Logger::LogDevice#new" do
+ before :each do
+ @file_path = tmp("test_log.log")
+ @log_file = File.open(@file_path, "w+")
+ end
+
+ after :each do
+ @log_file.close unless @log_file.closed?
+ rm_r @file_path
+ end
+
+ it "creates a new log device" do
+ l = Logger::LogDevice.new(@log_file)
+ l.dev.should be_kind_of(File)
+ end
+
+ it "receives an IO object to log there as first argument" do
+ @log_file.should be_kind_of(IO)
+ l = Logger::LogDevice.new(@log_file)
+ l.write("foo")
+ @log_file.rewind
+ @log_file.readlines.first.should == "foo"
+ end
+
+ it "creates a File if the IO object does not exist" do
+ path = tmp("test_logger_file")
+ l = Logger::LogDevice.new(path)
+ l.write("Test message")
+ l.close
+
+ File.exist?(path).should be_true
+ File.open(path) do |f|
+ f.readlines.should_not be_empty
+ end
+
+ rm_r path
+ end
+
+ it "receives options via a hash as second argument" do
+ lambda { Logger::LogDevice.new(STDERR,
+ { shift_age: 8, shift_size: 10
+ })}.should_not raise_error
+ end
+end
diff --git a/spec/ruby/library/logger/device/write_spec.rb b/spec/ruby/library/logger/device/write_spec.rb
new file mode 100644
index 0000000000..6305a623e3
--- /dev/null
+++ b/spec/ruby/library/logger/device/write_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/common'
+
+describe "Logger::LogDevice#write" do
+ before :each do
+ @file_path = tmp("test_log.log")
+ @log_file = File.open(@file_path, "w+")
+ # Avoid testing this with STDERR, we don't want to be closing that.
+ @device = Logger::LogDevice.new(@log_file)
+ end
+
+ after :each do
+ @log_file.close unless @log_file.closed?
+ rm_r @file_path
+ end
+
+ it "writes a message to the device" do
+ @device.write "This is a test message"
+ @log_file.rewind
+ @log_file.readlines.first.should == "This is a test message"
+ end
+
+ it "can create a file and writes empty message" do
+ path = tmp("you_should_not_see_me")
+ logdevice = Logger::LogDevice.new(path)
+ logdevice.write("")
+ logdevice.close
+
+ File.open(path) do |f|
+ messages = f.readlines
+ messages.size.should == 1
+ messages.first.should =~ /#.*/ # only a comment
+ end
+
+ rm_r path
+ end
+
+ it "fails if the device is already closed" do
+ @device.close
+ lambda { @device.write "foo" }.should complain(/\Alog writing failed\./)
+ end
+end
diff --git a/spec/ruby/library/logger/fixtures/common.rb b/spec/ruby/library/logger/fixtures/common.rb
new file mode 100644
index 0000000000..d369c64a24
--- /dev/null
+++ b/spec/ruby/library/logger/fixtures/common.rb
@@ -0,0 +1,9 @@
+require 'logger'
+
+module LoggerSpecs
+
+ def self.strip_date(str)
+ str.gsub(/[A-Z].*\[.*\]/, "").lstrip
+ end
+
+end
diff --git a/spec/ruby/library/logger/logger/add_spec.rb b/spec/ruby/library/logger/logger/add_spec.rb
new file mode 100644
index 0000000000..85a3b5ae3c
--- /dev/null
+++ b/spec/ruby/library/logger/logger/add_spec.rb
@@ -0,0 +1,81 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/common'
+
+describe "Logger#add" do
+ before :each do
+ @path = tmp("test_log.log")
+ @log_file = File.open(@path, "w+")
+ @logger = Logger.new(@path)
+ end
+
+ after :each do
+ @logger.close
+ @log_file.close unless @log_file.closed?
+ rm_r @path
+ end
+
+ it "writes a new message to the logger" do
+ @logger.add(Logger::WARN, "Test")
+ @log_file.rewind
+ message = @log_file.readlines.last
+ LoggerSpecs.strip_date(message).should == "WARN -- : Test\n"
+ end
+
+ it "receives a severity" do
+ @logger.log(Logger::INFO, "Info message")
+ @logger.log(Logger::DEBUG, "Debug message")
+ @logger.log(Logger::WARN, "Warn message")
+ @logger.log(Logger::ERROR, "Error message")
+ @logger.log(Logger::FATAL, "Fatal message")
+
+ @log_file.rewind
+
+ info, debug, warn, error, fatal = @log_file.readlines
+
+ LoggerSpecs.strip_date(info).should == "INFO -- : Info message\n"
+ LoggerSpecs.strip_date(debug).should == "DEBUG -- : Debug message\n"
+ LoggerSpecs.strip_date(warn).should == "WARN -- : Warn message\n"
+ LoggerSpecs.strip_date(error).should == "ERROR -- : Error message\n"
+ LoggerSpecs.strip_date(fatal).should == "FATAL -- : Fatal message\n"
+ end
+
+ it "receives a message" do
+ @logger.log(nil, "test")
+ @log_file.rewind
+ LoggerSpecs.strip_date(@log_file.readline).should == "ANY -- : test\n"
+ end
+
+ it "receives a program name" do
+ @logger.log(nil, "test", "TestApp")
+ @log_file.rewind
+ LoggerSpecs.strip_date(@log_file.readline).should == "ANY -- TestApp: test\n"
+ end
+
+ it "receives a block" do
+ lambda {
+ @logger.log(nil, "test", "TestApp") do
+ 1+1
+ end
+ }.should_not raise_error
+ end
+
+ it "calls the block if message is nil" do
+ temp = 0
+ lambda {
+ @logger.log(nil, nil, "TestApp") do
+ temp = 1+1
+ end
+ }.should_not raise_error
+ temp.should == 2
+ end
+
+ it "ignores the block if the message is not nil" do
+ temp = 0
+ lambda {
+ @logger.log(nil, "not nil", "TestApp") do
+ temp = 1+1
+ end
+ }.should_not raise_error
+ temp.should == 0
+ end
+end
diff --git a/spec/ruby/library/logger/logger/close_spec.rb b/spec/ruby/library/logger/logger/close_spec.rb
new file mode 100644
index 0000000000..45da2fa770
--- /dev/null
+++ b/spec/ruby/library/logger/logger/close_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/common'
+
+describe "Logger#close" do
+ before :each do
+ @path = tmp("test_log.log")
+ @log_file = File.open(@path, "w+")
+ @logger = Logger.new(@path)
+ end
+
+ after :each do
+ @log_file.close unless @log_file.closed?
+ rm_r @path
+ end
+
+ it "closes the logging device" do
+ @logger.close
+ lambda { @logger.add(nil, "Foo") }.should complain(/\Alog writing failed\./)
+ end
+end
diff --git a/spec/ruby/library/logger/logger/datetime_format_spec.rb b/spec/ruby/library/logger/logger/datetime_format_spec.rb
new file mode 100644
index 0000000000..39fd29bd3f
--- /dev/null
+++ b/spec/ruby/library/logger/logger/datetime_format_spec.rb
@@ -0,0 +1,60 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/common'
+
+describe "Logger#datetime_format" do
+ before :each do
+ @path = tmp("test_log.log")
+ @log_file = File.open(@path, "w+")
+ @logger = Logger.new(@path)
+ end
+
+ after :each do
+ @logger.close
+ @log_file.close unless @log_file.closed?
+ rm_r @path
+ end
+
+ it "returns the date format used for the logs" do
+ format = "%Y-%d"
+ @logger.datetime_format = format
+ @logger.datetime_format.should == format
+ end
+
+ it "returns nil logger is using the default date format" do
+ @logger.datetime_format.should == nil
+ end
+end
+
+describe "Logger#datetime_format=" do
+ before :each do
+ @path = tmp("test_log.log")
+ @log_file = File.open(@path, "w+")
+ @logger = Logger.new(@path)
+ end
+
+ after :each do
+ @logger.close
+ @log_file.close unless @log_file.closed?
+ rm_r @path
+ end
+
+ it "sets the date format for the logs" do
+ @logger.datetime_format = "%Y"
+ @logger.datetime_format.should == "%Y"
+ @logger.add(Logger::WARN, "Test message")
+ @log_file.rewind
+
+ regex = /2[0-9]{3}.*Test message/
+ @log_file.readlines.first.should =~ regex
+ end
+
+ it "follows the Time#strftime format" do
+ lambda { @logger.datetime_format = "%Y-%m" }.should_not raise_error
+
+ regex = /\d{4}-\d{2}-\d{2}oo-\w+ar/
+ @logger.datetime_format = "%Foo-%Bar"
+ @logger.add(nil, "Test message")
+ @log_file.rewind
+ @log_file.readlines.first.should =~ regex
+ end
+end
diff --git a/spec/ruby/library/logger/logger/debug_spec.rb b/spec/ruby/library/logger/logger/debug_spec.rb
new file mode 100644
index 0000000000..d92c339232
--- /dev/null
+++ b/spec/ruby/library/logger/logger/debug_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/common'
+
+describe "Logger#debug?" do
+ before :each do
+ @path = tmp("test_log.log")
+ @log_file = File.open(@path, "w+")
+ @logger = Logger.new(@path)
+ end
+
+ after :each do
+ @logger.close
+ @log_file.close unless @log_file.closed?
+ rm_r @path
+ end
+
+ it "returns true if severity level allows debug messages" do
+ @logger.level = Logger::DEBUG
+ @logger.debug?.should == true
+ end
+
+ it "returns false if severity level does not allow debug messages" do
+ @logger.level = Logger::WARN
+ @logger.debug?.should == false
+ end
+end
+
+describe "Logger#debug" do
+ before :each do
+ @path = tmp("test_log.log")
+ @log_file = File.open(@path, "w+")
+ @logger = Logger.new(@path)
+ end
+
+ after :each do
+ @logger.close
+ @log_file.close unless @log_file.closed?
+ rm_r @path
+ end
+
+ it "logs a DEBUG message" do
+ @logger.debug("test")
+ @log_file.rewind
+ LoggerSpecs.strip_date(@log_file.readlines.first).should == "DEBUG -- : test\n"
+ end
+
+ it "accepts an application name with a block" do
+ @logger.debug("MyApp") { "Test message" }
+ @log_file.rewind
+ LoggerSpecs.strip_date(@log_file.readlines.first).should == "DEBUG -- MyApp: Test message\n"
+ end
+end
diff --git a/spec/ruby/library/logger/logger/error_spec.rb b/spec/ruby/library/logger/logger/error_spec.rb
new file mode 100644
index 0000000000..d5d0bd2d2f
--- /dev/null
+++ b/spec/ruby/library/logger/logger/error_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/common'
+
+describe "Logger#error?" do
+ before :each do
+ @path = tmp("test_log.log")
+ @log_file = File.open(@path, "w+")
+ @logger = Logger.new(@path)
+ end
+
+ after :each do
+ @logger.close
+ @log_file.close unless @log_file.closed?
+ rm_r @path
+ end
+
+ it "returns true if severity level allows printing errors" do
+ @logger.level = Logger::INFO
+ @logger.error?.should == true
+ end
+
+ it "returns false if severity level does not allow errors" do
+ @logger.level = Logger::FATAL
+ @logger.error?.should == false
+ end
+end
+
+describe "Logger#error" do
+ before :each do
+ @path = tmp("test_log.log")
+ @log_file = File.open(@path, "w+")
+ @logger = Logger.new(@path)
+ end
+
+ after :each do
+ @logger.close
+ @log_file.close unless @log_file.closed?
+ rm_r @path
+ end
+
+ it "logs a ERROR message" do
+ @logger.error("test")
+ @log_file.rewind
+ LoggerSpecs.strip_date(@log_file.readlines.first).should == "ERROR -- : test\n"
+ end
+
+ it "accepts an application name with a block" do
+ @logger.error("MyApp") { "Test message" }
+ @log_file.rewind
+ LoggerSpecs.strip_date(@log_file.readlines.first).should == "ERROR -- MyApp: Test message\n"
+ end
+
+end
diff --git a/spec/ruby/library/logger/logger/fatal_spec.rb b/spec/ruby/library/logger/logger/fatal_spec.rb
new file mode 100644
index 0000000000..42d4341319
--- /dev/null
+++ b/spec/ruby/library/logger/logger/fatal_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/common'
+
+describe "Logger#fatal?" do
+ before :each do
+ @path = tmp("test_log.log")
+ @log_file = File.open(@path, "w+")
+ @logger = Logger.new(@path)
+ end
+
+ after :each do
+ @logger.close
+ @log_file.close unless @log_file.closed?
+ rm_r @path
+ end
+
+ it "returns true if severity level allows fatal messages" do
+ @logger.level = Logger::FATAL
+ @logger.fatal?.should == true
+ end
+
+ it "returns false if severity level does not allow fatal messages" do
+ @logger.level = Logger::UNKNOWN
+ @logger.fatal?.should == false
+ end
+end
+
+describe "Logger#fatal" do
+ before :each do
+ @path = tmp("test_log.log")
+ @log_file = File.open(@path, "w+")
+ @logger = Logger.new(@path)
+ end
+
+ after :each do
+ @logger.close
+ @log_file.close unless @log_file.closed?
+ rm_r @path
+ end
+
+ it "logs a FATAL message" do
+ @logger.fatal("test")
+ @log_file.rewind
+ LoggerSpecs.strip_date(@log_file.readlines.first).should == "FATAL -- : test\n"
+ end
+
+ it "accepts an application name with a block" do
+ @logger.fatal("MyApp") { "Test message" }
+ @log_file.rewind
+ LoggerSpecs.strip_date(@log_file.readlines.first).should == "FATAL -- MyApp: Test message\n"
+ end
+
+end
diff --git a/spec/ruby/library/logger/logger/info_spec.rb b/spec/ruby/library/logger/logger/info_spec.rb
new file mode 100644
index 0000000000..21eacbbb31
--- /dev/null
+++ b/spec/ruby/library/logger/logger/info_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/common'
+
+describe "Logger#info?" do
+ before :each do
+ @path = tmp("test_log.log")
+ @log_file = File.open(@path, "w+")
+ @logger = Logger.new(@path)
+ end
+
+ after :each do
+ @logger.close
+ @log_file.close unless @log_file.closed?
+ rm_r @path
+ end
+
+ it "returns true if severity level allows info messages" do
+ @logger.level = Logger::INFO
+ @logger.info?.should == true
+ end
+
+ it "returns false if severity level does not allow info messages" do
+ @logger.level = Logger::FATAL
+ @logger.info?.should == false
+ end
+end
+
+describe "Logger#info" do
+ before :each do
+ @path = tmp("test_log.log")
+ @log_file = File.open(@path, "w+")
+ @logger = Logger.new(@path)
+ end
+
+ after :each do
+ @logger.close
+ @log_file.close unless @log_file.closed?
+ rm_r @path
+ end
+
+ it "logs a INFO message" do
+ @logger.info("test")
+ @log_file.rewind
+ LoggerSpecs.strip_date(@log_file.readlines.first).should == "INFO -- : test\n"
+ end
+
+ it "accepts an application name with a block" do
+ @logger.info("MyApp") { "Test message" }
+ @log_file.rewind
+ LoggerSpecs.strip_date(@log_file.readlines.first).should == "INFO -- MyApp: Test message\n"
+ end
+
+end
diff --git a/spec/ruby/library/logger/logger/new_spec.rb b/spec/ruby/library/logger/logger/new_spec.rb
new file mode 100644
index 0000000000..cf965ac03b
--- /dev/null
+++ b/spec/ruby/library/logger/logger/new_spec.rb
@@ -0,0 +1,120 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/common'
+
+describe "Logger#new" do
+
+ before :each do
+ @file_path = tmp("test_log.log")
+ @log_file = File.open(@file_path, "w+")
+ end
+
+ after :each do
+ @log_file.close unless @log_file.closed?
+ rm_r @file_path
+ end
+
+ it "creates a new logger object" do
+ l = Logger.new(STDERR)
+ lambda { l.add(Logger::WARN, "Foo") }.should output_to_fd(/Foo/, STDERR)
+ end
+
+ it "receives a logging device as first argument" do
+ l = Logger.new(@log_file)
+ l.add(Logger::WARN, "Test message")
+
+ @log_file.rewind
+ LoggerSpecs.strip_date(@log_file.readline).should == "WARN -- : Test message\n"
+ l.close
+ end
+
+ it "receives a frequency rotation as second argument" do
+ lambda { Logger.new(@log_file, "daily") }.should_not raise_error
+ lambda { Logger.new(@log_file, "weekly") }.should_not raise_error
+ lambda { Logger.new(@log_file, "monthly") }.should_not raise_error
+ end
+
+ it "also receives a number of log files to keep as second argument" do
+ lambda { Logger.new(@log_file, 1).close }.should_not raise_error
+ end
+
+ it "receivs a maximum logfile size as third argument" do
+ # This should create 2 small log files, logfile_test and logfile_test.0
+ # in /tmp, each one with a different message.
+ path = tmp("logfile_test.log")
+
+ l = Logger.new(path, 2, 5)
+ l.add Logger::WARN, "foo"
+ l.add Logger::WARN, "bar"
+
+ File.exist?(path).should be_true
+ File.exist?(path + ".0").should be_true
+
+ # first line will be a comment so we'll have to skip it.
+ f = File.open(path)
+ f1 = File.open("#{path}.0")
+ LoggerSpecs.strip_date(f1.readlines.last).should == "WARN -- : foo\n"
+ LoggerSpecs.strip_date(f.readlines.last).should == "WARN -- : bar\n"
+
+ l.close
+ f.close
+ f1.close
+ rm_r path, "#{path}.0"
+ end
+
+ ruby_version_is "2.4" do
+ it "receives level symbol as keyword argument" do
+ logger = Logger.new(STDERR, level: :info)
+ logger.level.should == Logger::INFO
+ end
+
+ it "receives level as keyword argument" do
+ logger = Logger.new(STDERR, level: Logger::INFO)
+ logger.level.should == Logger::INFO
+ end
+
+ it "receives progname as keyword argument" do
+ progname = "progname"
+
+ logger = Logger.new(STDERR, progname: progname)
+ logger.progname.should == progname
+ end
+
+ it "receives datetime_format as keyword argument" do
+ datetime_format = "%H:%M:%S"
+
+ logger = Logger.new(STDERR, datetime_format: datetime_format)
+ logger.datetime_format.should == datetime_format
+ end
+
+ it "receives formatter as keyword argument" do
+ formatter = Class.new do
+ def call(_severity, _time, _progname, _msg); end
+ end.new
+
+ logger = Logger.new(STDERR, formatter: formatter)
+ logger.formatter.should == formatter
+ end
+
+ it "receives shift_period_suffix " do
+ shift_period_suffix = "%Y-%m-%d"
+ path = tmp("shift_period_suffix_test.log")
+ now = Time.now
+ tomorrow = Time.at(now.to_i + 60 * 60 * 24)
+ logger = Logger.new(path, 'daily', shift_period_suffix: shift_period_suffix)
+
+ logger.add Logger::INFO, 'message'
+
+ Time.stub!(:now).and_return(tomorrow)
+ logger.add Logger::INFO, 'second message'
+
+ shifted_path = "#{path}.#{now.strftime(shift_period_suffix)}"
+
+ File.exist?(shifted_path).should == true
+
+ logger.close
+
+ rm_r path, shifted_path
+ end
+ end
+
+end
diff --git a/spec/ruby/library/logger/logger/unknown_spec.rb b/spec/ruby/library/logger/logger/unknown_spec.rb
new file mode 100644
index 0000000000..4a042f9215
--- /dev/null
+++ b/spec/ruby/library/logger/logger/unknown_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/common'
+
+describe "Logger#unknown" do
+ before :each do
+ @path = tmp("test_log.log")
+ @log_file = File.open(@path, "w+")
+ @logger = Logger.new(@path)
+ end
+
+ after :each do
+ @logger.close
+ @log_file.close unless @log_file.closed?
+ rm_r @path
+ end
+
+ it "logs a message with unknown severity" do
+ @logger.unknown "Test"
+ @log_file.rewind
+ LoggerSpecs.strip_date(@log_file.readlines.first).should == "ANY -- : Test\n"
+ end
+
+ it "defaults the priority value to 5 and text value to ANY" do
+ @logger.unknown "Test"
+ @log_file.rewind
+ message = LoggerSpecs.strip_date(@log_file.readlines.first)[0..2]
+ message.should == "ANY"
+ Logger::UNKNOWN.should == 5
+ end
+
+ it "receives empty messages" do
+ lambda { @logger.unknown("") }.should_not raise_error
+ @log_file.rewind
+ LoggerSpecs.strip_date(@log_file.readlines.first).should == "ANY -- : \n"
+ end
+end
diff --git a/spec/ruby/library/logger/logger/warn_spec.rb b/spec/ruby/library/logger/logger/warn_spec.rb
new file mode 100644
index 0000000000..6617b2b41a
--- /dev/null
+++ b/spec/ruby/library/logger/logger/warn_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/common'
+
+describe "Logger#warn?" do
+ before :each do
+ @path = tmp("test_log.log")
+ @log_file = File.open(@path, "w+")
+ @logger = Logger.new(@path)
+ end
+
+ after :each do
+ @logger.close
+ @log_file.close unless @log_file.closed?
+ rm_r @path
+ end
+
+ it "returns true if severity level allows printing warn messages" do
+ @logger.level = Logger::WARN
+ @logger.warn?.should == true
+ end
+
+ it "returns false if severity level does not allow printing warn messages" do
+ @logger.level = Logger::FATAL
+ @logger.warn?.should == false
+ end
+end
+
+describe "Logger#warn" do
+ before :each do
+ @path = tmp("test_log.log")
+ @log_file = File.open(@path, "w+")
+ @logger = Logger.new(@path)
+ end
+
+ after :each do
+ @logger.close
+ @log_file.close unless @log_file.closed?
+ rm_r @path
+ end
+
+ it "logs a WARN message" do
+ @logger.warn("test")
+ @log_file.rewind
+ LoggerSpecs.strip_date(@log_file.readlines.first).should == "WARN -- : test\n"
+ end
+
+ it "accepts an application name with a block" do
+ @logger.warn("MyApp") { "Test message" }
+ @log_file.rewind
+ LoggerSpecs.strip_date(@log_file.readlines.first).should == "WARN -- MyApp: Test message\n"
+ end
+
+end
diff --git a/spec/ruby/library/logger/severity_spec.rb b/spec/ruby/library/logger/severity_spec.rb
new file mode 100644
index 0000000000..e9bc850c33
--- /dev/null
+++ b/spec/ruby/library/logger/severity_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require 'logger'
+
+describe "Logger::Severity" do
+ it "defines Logger severity constants" do
+ Logger::DEBUG.should == 0
+ Logger::INFO.should == 1
+ Logger::WARN.should == 2
+ Logger::ERROR.should == 3
+ Logger::FATAL.should == 4
+ Logger::UNKNOWN.should == 5
+ end
+end
diff --git a/spec/ruby/library/mathn/bignum/exponent_spec.rb b/spec/ruby/library/mathn/bignum/exponent_spec.rb
new file mode 100644
index 0000000000..ddd19ffdb4
--- /dev/null
+++ b/spec/ruby/library/mathn/bignum/exponent_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is ''...'2.5' do
+ require 'mathn'
+
+ describe "Bignum#**" do
+ before :each do
+ @bignum = bignum_value(47)
+ end
+
+ it "returns self raised to other (positive) power" do
+ (@bignum ** 4).should == 7237005577332262361485077344629993318496048279512298547155833600056910050625
+ (@bignum ** 1.2).should be_close(57262152889751597425762.57804, TOLERANCE)
+ end
+
+ it "returns a complex number when negative and raised to a fractional power" do
+ ((-@bignum) ** (1/3)).should be_close(Complex(1048576,1816186.907597341), TOLERANCE)
+ ((-@bignum) ** (1.0/3)).should be_close(Complex(1048576,1816186.907597341), TOLERANCE)
+ end
+ end
+end
diff --git a/spec/ruby/library/mathn/complex/Complex_spec.rb b/spec/ruby/library/mathn/complex/Complex_spec.rb
new file mode 100644
index 0000000000..105902ed5d
--- /dev/null
+++ b/spec/ruby/library/mathn/complex/Complex_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is ''...'2.5' do
+ require 'mathn'
+
+ describe "Kernel#Complex" do
+ it "returns an Integer if imaginary part is 0" do
+ Complex(42,0).should == 42
+ Complex(42,0).should be_kind_of(Fixnum)
+ Complex(bignum_value,0).should == bignum_value
+ Complex(bignum_value,0).should be_kind_of(Bignum)
+ end
+ end
+end
diff --git a/spec/ruby/library/mathn/fixnum/exponent_spec.rb b/spec/ruby/library/mathn/fixnum/exponent_spec.rb
new file mode 100644
index 0000000000..160bf7f115
--- /dev/null
+++ b/spec/ruby/library/mathn/fixnum/exponent_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is ''...'2.5' do
+ require 'mathn'
+
+ describe "Fixnum#**" do
+ it "returns self raised to other (positive) power" do
+ (2 ** 4).should == 16
+ (2 ** 1.2).should be_close(2.2973967, TOLERANCE)
+ end
+
+ it "returns a complex number when negative and raised to a fractional power" do
+ ((-8) ** (1/3)).should be_close(Complex(1, 1.73205), TOLERANCE)
+ ((-8) ** (1.0/3)).should be_close(Complex(1, 1.73205), TOLERANCE)
+ end
+ end
+end
diff --git a/spec/ruby/library/mathn/float/exponent_spec.rb b/spec/ruby/library/mathn/float/exponent_spec.rb
new file mode 100644
index 0000000000..312119db00
--- /dev/null
+++ b/spec/ruby/library/mathn/float/exponent_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is ''...'2.5' do
+ require 'mathn'
+
+ describe "Float#**" do
+ it "returns self raised to other (positive) power" do
+ (2.0 ** 4).should == 16.0
+ (2.0 ** 1.2).should be_close(2.2973967, TOLERANCE)
+ end
+
+ it "returns a complex number when negative and raised to a fractional power" do
+ ((-8.0) ** (1/3)).should be_close(Complex(1, 1.73205), TOLERANCE)
+ ((-8.0) ** (1.0/3)).should be_close(Complex(1, 1.73205), TOLERANCE)
+ end
+ end
+end
diff --git a/spec/ruby/library/mathn/integer/from_prime_division_spec.rb b/spec/ruby/library/mathn/integer/from_prime_division_spec.rb
new file mode 100644
index 0000000000..df2db6d8c1
--- /dev/null
+++ b/spec/ruby/library/mathn/integer/from_prime_division_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is ''...'2.5' do
+ require 'mathn'
+
+ describe "Integer.from_prime_division" do
+ it "reverses a prime factorization of an integer" do
+ Integer.from_prime_division([[2, 1], [3, 2], [7, 1]]).should == 126
+ end
+ end
+end
diff --git a/spec/ruby/library/mathn/integer/prime_division_spec.rb b/spec/ruby/library/mathn/integer/prime_division_spec.rb
new file mode 100644
index 0000000000..e2141490f0
--- /dev/null
+++ b/spec/ruby/library/mathn/integer/prime_division_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is ''...'2.5' do
+ require 'mathn'
+
+ describe "Integer#prime_division" do
+ it "performs a prime factorization of a positive integer" do
+ 100.prime_division.should == [[2, 2], [5, 2]]
+ end
+
+ # Proper handling of negative integers has been added to MRI trunk
+ # in revision 24091. Prior to that, all versions of MRI returned nonsense.
+ it "performs a prime factorization of a negative integer" do
+ -26.prime_division.should == [[-1, 1], [2, 1], [13, 1]]
+ end
+
+ it "raises a ZeroDivisionError when is called on zero" do
+ lambda { 0.prime_division }.should raise_error(ZeroDivisionError)
+ end
+ end
+end
diff --git a/spec/ruby/library/mathn/math/fixtures/classes.rb b/spec/ruby/library/mathn/math/fixtures/classes.rb
new file mode 100644
index 0000000000..024732fa7a
--- /dev/null
+++ b/spec/ruby/library/mathn/math/fixtures/classes.rb
@@ -0,0 +1,3 @@
+class IncludesMath
+ include Math
+end
diff --git a/spec/ruby/library/mathn/math/rsqrt_spec.rb b/spec/ruby/library/mathn/math/rsqrt_spec.rb
new file mode 100644
index 0000000000..6cb7595afe
--- /dev/null
+++ b/spec/ruby/library/mathn/math/rsqrt_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is ''...'2.5' do
+ require_relative 'shared/rsqrt'
+
+ describe "Math#rsqrt" do
+ it_behaves_like :mathn_math_rsqrt, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:rsqrt)
+ end
+ end
+
+ describe "Math.rsqrt" do
+ it_behaves_like :mathn_math_rsqrt, :_, Math
+ end
+end
diff --git a/spec/ruby/library/mathn/math/shared/rsqrt.rb b/spec/ruby/library/mathn/math/shared/rsqrt.rb
new file mode 100644
index 0000000000..0f623a7d4f
--- /dev/null
+++ b/spec/ruby/library/mathn/math/shared/rsqrt.rb
@@ -0,0 +1,21 @@
+require 'mathn'
+require_relative '../fixtures/classes'
+
+describe :mathn_math_rsqrt, shared: true do
+ it "returns the square root for Rational numbers" do
+ @object.send(:rsqrt, Rational(9, 25)).should == Rational(3, 5)
+ @object.send(:rsqrt, 16/64).should == Rational(1, 2)
+ end
+
+ it "returns the square root for positive numbers" do
+ @object.send(:rsqrt, 1).should == 1
+ @object.send(:rsqrt, 4.0).should == 2.0
+ @object.send(:rsqrt, 12.34).should == Math.sqrt!(12.34)
+ end
+
+ it "raises an Math::DomainError if the argument is a negative number" do
+ lambda { @object.send(:rsqrt, -1) }.should raise_error(Math::DomainError)
+ lambda { @object.send(:rsqrt, -4.0) }.should raise_error(Math::DomainError)
+ lambda { @object.send(:rsqrt, -16/64) }.should raise_error(Math::DomainError)
+ end
+end
diff --git a/spec/ruby/library/mathn/math/shared/sqrt.rb b/spec/ruby/library/mathn/math/shared/sqrt.rb
new file mode 100644
index 0000000000..5e6dae1d4f
--- /dev/null
+++ b/spec/ruby/library/mathn/math/shared/sqrt.rb
@@ -0,0 +1,25 @@
+require 'mathn'
+require_relative '../fixtures/classes'
+
+describe :mathn_math_sqrt, shared: true do
+ it "returns the square root for Rational numbers" do
+ @object.send(:sqrt, Rational(9, 25)).should == Rational(3, 5)
+ @object.send(:sqrt, 16/64).should == Rational(1, 2)
+ end
+
+ it "returns the square root for Complex numbers" do
+ @object.send(:sqrt, Complex(1, 0)).should == 1
+ end
+
+ it "returns the square root for positive numbers" do
+ @object.send(:sqrt, 1).should == 1
+ @object.send(:sqrt, 4.0).should == 2.0
+ @object.send(:sqrt, 12.34).should == Math.sqrt!(12.34)
+ end
+
+ it "returns the square root for negative numbers" do
+ @object.send(:sqrt, -9).should == Complex(0, 3)
+ @object.send(:sqrt, -5.29).should == Complex(0, 2.3)
+ @object.send(:sqrt, -16/64).should == Complex(0, 1/2)
+ end
+end
diff --git a/spec/ruby/library/mathn/math/sqrt_spec.rb b/spec/ruby/library/mathn/math/sqrt_spec.rb
new file mode 100644
index 0000000000..49cfe3600e
--- /dev/null
+++ b/spec/ruby/library/mathn/math/sqrt_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is ''...'2.5' do
+ require_relative 'shared/sqrt'
+
+ describe "Math#rsqrt" do
+ it_behaves_like :mathn_math_sqrt, :_, IncludesMath.new
+
+ it "is a private instance method" do
+ IncludesMath.should have_private_instance_method(:sqrt)
+ end
+ end
+
+ describe "Math.rsqrt" do
+ it_behaves_like :mathn_math_sqrt, :_, Math
+ end
+end
diff --git a/spec/ruby/library/mathn/mathn_spec.rb b/spec/ruby/library/mathn/mathn_spec.rb
new file mode 100644
index 0000000000..c9e8c05a8d
--- /dev/null
+++ b/spec/ruby/library/mathn/mathn_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "mathn" do
+ ruby_version_is "2.5" do
+ it "is no longer part of the standard library" do
+ -> { require "mathn" }.should raise_error(LoadError)
+ end
+ end
+end
diff --git a/spec/ruby/library/mathn/rational/Rational_spec.rb b/spec/ruby/library/mathn/rational/Rational_spec.rb
new file mode 100644
index 0000000000..9a34c99751
--- /dev/null
+++ b/spec/ruby/library/mathn/rational/Rational_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is ''...'2.5' do
+ require 'mathn'
+
+ describe "Kernel#Rational" do
+ it "returns an Integer if denominator divides numerator evenly" do
+ Rational(42,6).should == 7
+ Rational(42,6).should be_kind_of(Fixnum)
+ Rational(bignum_value,1).should == bignum_value
+ Rational(bignum_value,1).should be_kind_of(Bignum)
+ end
+ end
+end
diff --git a/spec/ruby/library/mathn/rational/inspect_spec.rb b/spec/ruby/library/mathn/rational/inspect_spec.rb
new file mode 100644
index 0000000000..cbf67ec0f3
--- /dev/null
+++ b/spec/ruby/library/mathn/rational/inspect_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is ''...'2.5' do
+ require 'mathn'
+
+ describe "Rational#inspect" do
+ it "returns a string representation of self" do
+ Rational(3, 4).inspect.should == "(3/4)"
+ Rational(-5, 8).inspect.should == "(-5/8)"
+ Rational(-1, -2).inspect.should == "(1/2)"
+ Rational(0, 2).inspect.should == "0"
+ Rational(bignum_value, 1).inspect.should == "#{bignum_value}"
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/I_spec.rb b/spec/ruby/library/matrix/I_spec.rb
new file mode 100644
index 0000000000..6eeffe8e98
--- /dev/null
+++ b/spec/ruby/library/matrix/I_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/identity'
+
+describe "Matrix.I" do
+ it_behaves_like :matrix_identity, :I
+end
diff --git a/spec/ruby/library/matrix/antisymmetric_spec.rb b/spec/ruby/library/matrix/antisymmetric_spec.rb
new file mode 100644
index 0000000000..534fab60ea
--- /dev/null
+++ b/spec/ruby/library/matrix/antisymmetric_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+ruby_version_is "2.6" do
+ describe "Matrix#antisymmetric?" do
+ it "returns true for an antisymmetric Matrix" do
+ Matrix[[0, -2, Complex(1, 3)], [2, 0, 5], [-Complex(1, 3), -5, 0]].antisymmetric?.should be_true
+ end
+
+ it "returns true for a 0x0 empty matrix" do
+ Matrix.empty.antisymmetric?.should be_true
+ end
+
+ it "returns false for non-antisymmetric matrices" do
+ [
+ Matrix[[1, 2, 3], [4, 5, 6], [7, 8, 9]],
+ Matrix[[1, -2, 3], [2, 0, 6], [-3, -6, 0]], # wrong diagonal element
+ Matrix[[0, 2, -3], [2, 0, 6], [-3, 6, 0]] # only signs wrong
+ ].each do |matrix|
+ matrix.antisymmetric?.should be_false
+ end
+ end
+
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ lambda {
+ rectangular_matrix.antisymmetric?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/build_spec.rb b/spec/ruby/library/matrix/build_spec.rb
new file mode 100644
index 0000000000..6819b9b617
--- /dev/null
+++ b/spec/ruby/library/matrix/build_spec.rb
@@ -0,0 +1,73 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix.build" do
+
+ it "returns a Matrix object of the given size" do
+ m = Matrix.build(3, 4){1}
+ m.should be_an_instance_of(Matrix)
+ m.row_size.should == 3
+ m.column_size.should == 4
+ end
+
+ it "builds the Matrix using the given block" do
+ Matrix.build(2, 3){|col, row| 10*col - row}.should ==
+ Matrix[[0, -1, -2], [10, 9, 8]]
+ end
+
+ it "iterates through the first row, then the second, ..." do
+ acc = []
+ Matrix.build(2, 3){|*args| acc << args}
+ acc.should == [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2]]
+ end
+
+ it "returns an Enumerator is no block is given" do
+ enum = Matrix.build(2, 1)
+ enum.should be_an_instance_of(Enumerator)
+ enum.each{1}.should == Matrix[[1], [1]]
+ end
+
+ it "requires integers as parameters" do
+ lambda { Matrix.build("1", "2"){1} }.should raise_error(TypeError)
+ lambda { Matrix.build(nil, nil){1} }.should raise_error(TypeError)
+ lambda { Matrix.build(1..2){1} }.should raise_error(TypeError)
+ end
+
+ it "requires non-negative integers" do
+ lambda { Matrix.build(-1, 1){1} }.should raise_error(ArgumentError)
+ lambda { Matrix.build(+1,-1){1} }.should raise_error(ArgumentError)
+ end
+
+ it "returns empty Matrix if one argument is zero" do
+ m = Matrix.build(0, 3){
+ raise "Should not yield"
+ }
+ m.should be_empty
+ m.column_size.should == 3
+
+ m = Matrix.build(3, 0){
+ raise "Should not yield"
+ }
+ m.should be_empty
+ m.row_size.should == 3
+ end
+
+ it "tries to calls :to_int on arguments" do
+ int = mock('int')
+ int.should_receive(:to_int).twice.and_return(2)
+ Matrix.build(int, int){ 1 }.should == Matrix[ [1,1], [1,1] ]
+ end
+
+ it "builds an nxn Matrix when given only one argument" do
+ m = Matrix.build(3){1}
+ m.row_size.should == 3
+ m.column_size.should == 3
+ end
+end
+
+describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.build(3){1}.should be_an_instance_of(MatrixSub)
+ end
+end
diff --git a/spec/ruby/library/matrix/clone_spec.rb b/spec/ruby/library/matrix/clone_spec.rb
new file mode 100644
index 0000000000..74e5bf157e
--- /dev/null
+++ b/spec/ruby/library/matrix/clone_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix#clone" do
+ before :each do
+ @a = Matrix[[1, 2], [3, 4], [5, 6]]
+ end
+
+ it "returns a shallow copy of the matrix" do
+ b = @a.clone
+ @a.should_not equal(b)
+ b.should be_kind_of(Matrix)
+ b.should == @a
+ 0.upto(@a.row_size - 1) do |i|
+ @a.row(i).should_not equal(b.row(i))
+ end
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.ins.clone.should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/coerce_spec.rb b/spec/ruby/library/matrix/coerce_spec.rb
new file mode 100644
index 0000000000..b8d5484657
--- /dev/null
+++ b/spec/ruby/library/matrix/coerce_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#coerce" do
+ it "needs to be reviewed for spec completeness"
+
+ it "allows the division of fixnum by a Matrix " do
+ (1/Matrix[[0,1],[-1,0]]).should == Matrix[[0,-1],[1,0]]
+ end
+end
diff --git a/spec/ruby/library/matrix/collect_spec.rb b/spec/ruby/library/matrix/collect_spec.rb
new file mode 100644
index 0000000000..bba640213b
--- /dev/null
+++ b/spec/ruby/library/matrix/collect_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/collect'
+
+describe "Matrix#collect" do
+ it_behaves_like :collect, :collect
+end
diff --git a/spec/ruby/library/matrix/column_size_spec.rb b/spec/ruby/library/matrix/column_size_spec.rb
new file mode 100644
index 0000000000..041914e5b9
--- /dev/null
+++ b/spec/ruby/library/matrix/column_size_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#column_size" do
+ it "returns the number of columns" do
+ Matrix[ [1,2], [3,4] ].column_size.should == 2
+ end
+
+ it "returns 0 for empty matrices" do
+ Matrix[ [], [] ].column_size.should == 0
+ Matrix[ ].column_size.should == 0
+ end
+end
diff --git a/spec/ruby/library/matrix/column_spec.rb b/spec/ruby/library/matrix/column_spec.rb
new file mode 100644
index 0000000000..46cd57f2b3
--- /dev/null
+++ b/spec/ruby/library/matrix/column_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#column" do
+ before :all do
+ @m = Matrix[[1,2,3], [2,3,4]]
+ end
+
+ it "returns a Vector when called without a block" do
+ @m.column(1).should == Vector[2,3]
+ end
+
+ it "yields each element in the column to the block" do
+ a = []
+ @m.column(1) {|n| a << n }
+ a.should == [2,3]
+ end
+
+ it "counts backwards for negative argument" do
+ @m.column(-1).should == Vector[3, 4]
+ end
+
+ it "returns self when called with a block" do
+ @m.column(0) { |x| x }.should equal(@m)
+ end
+
+ it "returns nil when out of bounds" do
+ @m.column(3).should == nil
+ end
+
+ it "never yields when out of bounds" do
+ lambda { @m.column(3){ raise } }.should_not raise_error
+ lambda { @m.column(-4){ raise } }.should_not raise_error
+ end
+end
diff --git a/spec/ruby/library/matrix/column_vector_spec.rb b/spec/ruby/library/matrix/column_vector_spec.rb
new file mode 100644
index 0000000000..47e866a8d5
--- /dev/null
+++ b/spec/ruby/library/matrix/column_vector_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix.column_vector" do
+
+ it "returns a single column Matrix when called with an Array" do
+ m = Matrix.column_vector([4,5,6])
+ m.should be_an_instance_of(Matrix)
+ m.should == Matrix[ [4],[5],[6] ]
+ end
+
+ it "returns an empty Matrix when called with an empty Array" do
+ m = Matrix.column_vector([])
+ m.should be_an_instance_of(Matrix)
+ m.row_size.should == 0
+ m.column_size.should == 1
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.column_vector([4,5,6]).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/column_vectors_spec.rb b/spec/ruby/library/matrix/column_vectors_spec.rb
new file mode 100644
index 0000000000..b0cb6f914c
--- /dev/null
+++ b/spec/ruby/library/matrix/column_vectors_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#column_vectors" do
+
+ before :each do
+ @vectors = Matrix[ [1,2], [3,4] ].column_vectors
+ end
+
+ it "returns an Array" do
+ Matrix[ [1,2], [3,4] ].column_vectors.should be_an_instance_of(Array)
+ end
+
+ it "returns an Array of Vectors" do
+ @vectors.all? {|v| v.should be_an_instance_of(Vector)}
+ end
+
+ it "returns each column as a Vector" do
+ @vectors.should == [Vector[1,3], Vector[2,4]]
+ end
+
+ it "returns an empty Array for empty matrices" do
+ Matrix[ [] ].column_vectors.should == []
+ end
+
+end
diff --git a/spec/ruby/library/matrix/columns_spec.rb b/spec/ruby/library/matrix/columns_spec.rb
new file mode 100644
index 0000000000..3095fdd7af
--- /dev/null
+++ b/spec/ruby/library/matrix/columns_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix.columns" do
+ before :each do
+ @a = [1, 2]
+ @b = [3, 4]
+ @m = Matrix.columns([@a, @b])
+ end
+
+ it "creates a Matrix from argument columns" do
+ @m.should be_an_instance_of(Matrix)
+ @m.column(0).to_a.should == @a
+ @m.column(1).to_a.should == @b
+ end
+
+ it "accepts Vectors as argument columns" do
+ m = Matrix.columns([Vector[*@a], Vector[*@b]])
+ m.should == @m
+ m.column(0).to_a.should == @a
+ m.column(1).to_a.should == @b
+ end
+
+ it "handles empty matrices" do
+ e = Matrix.columns([])
+ e.row_size.should == 0
+ e.column_size.should == 0
+ e.should == Matrix[]
+
+ v = Matrix.columns([[],[],[]])
+ v.row_size.should == 0
+ v.column_size.should == 3
+ v.should == Matrix[[], [], []].transpose
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.columns([[1]]).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/conj_spec.rb b/spec/ruby/library/matrix/conj_spec.rb
new file mode 100644
index 0000000000..ecee95c255
--- /dev/null
+++ b/spec/ruby/library/matrix/conj_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/conjugate'
+
+describe "Matrix#conj" do
+ it_behaves_like :matrix_conjugate, :conj
+end
diff --git a/spec/ruby/library/matrix/conjugate_spec.rb b/spec/ruby/library/matrix/conjugate_spec.rb
new file mode 100644
index 0000000000..682bd41d94
--- /dev/null
+++ b/spec/ruby/library/matrix/conjugate_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/conjugate'
+
+describe "Matrix#conjugate" do
+ it_behaves_like :matrix_conjugate, :conjugate
+end
diff --git a/spec/ruby/library/matrix/constructor_spec.rb b/spec/ruby/library/matrix/constructor_spec.rb
new file mode 100644
index 0000000000..2479923fb5
--- /dev/null
+++ b/spec/ruby/library/matrix/constructor_spec.rb
@@ -0,0 +1,65 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix.[]" do
+
+ it "requires arrays as parameters" do
+ lambda { Matrix[5] }.should raise_error(TypeError)
+ lambda { Matrix[nil] }.should raise_error(TypeError)
+ lambda { Matrix[1..2] }.should raise_error(TypeError)
+ lambda { Matrix[[1, 2], 3] }.should raise_error(TypeError)
+ end
+
+ it "creates an empty Matrix with no arguments" do
+ m = Matrix[]
+ m.column_size.should == 0
+ m.row_size.should == 0
+ end
+
+ it "raises for non-rectangular matrices" do
+ lambda{ Matrix[ [0], [0,1] ] }.should \
+ raise_error(Matrix::ErrDimensionMismatch)
+ lambda{ Matrix[ [0,1], [0,1,2], [0,1] ]}.should \
+ raise_error(Matrix::ErrDimensionMismatch)
+ end
+
+ it "accepts vector arguments" do
+ a = Matrix[Vector[1, 2], Vector[3, 4]]
+ a.should be_an_instance_of(Matrix)
+ a.should == Matrix[ [1, 2], [3, 4] ]
+ end
+
+ it "tries to calls :to_ary on arguments" do
+ array = mock('ary')
+ array.should_receive(:to_ary).and_return([1,2])
+ Matrix[array, [3,4] ].should == Matrix[ [1,2], [3,4] ]
+ end
+
+
+ it "returns a Matrix object" do
+ Matrix[ [1] ].should be_an_instance_of(Matrix)
+ end
+
+ it "can create an nxn Matrix" do
+ m = Matrix[ [20,30], [40.5, 9] ]
+ m.row_size.should == 2
+ m.column_size.should == 2
+ m.column(0).should == Vector[20, 40.5]
+ m.column(1).should == Vector[30, 9]
+ m.row(0).should == Vector[20, 30]
+ m.row(1).should == Vector[40.5, 9]
+ end
+
+ it "can create a 0xn Matrix" do
+ m = Matrix[ [], [], [] ]
+ m.row_size.should == 3
+ m.column_size.should == 0
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub[ [20,30], [40.5, 9] ].should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/det_spec.rb b/spec/ruby/library/matrix/det_spec.rb
new file mode 100644
index 0000000000..aa7086cacf
--- /dev/null
+++ b/spec/ruby/library/matrix/det_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/determinant'
+require 'matrix'
+
+describe "Matrix#det" do
+ it_behaves_like :determinant, :det
+end
diff --git a/spec/ruby/library/matrix/determinant_spec.rb b/spec/ruby/library/matrix/determinant_spec.rb
new file mode 100644
index 0000000000..825c9907b1
--- /dev/null
+++ b/spec/ruby/library/matrix/determinant_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/determinant'
+require 'matrix'
+
+describe "Matrix#determinant" do
+ it_behaves_like :determinant, :determinant
+end
diff --git a/spec/ruby/library/matrix/diagonal_spec.rb b/spec/ruby/library/matrix/diagonal_spec.rb
new file mode 100644
index 0000000000..f066a21c47
--- /dev/null
+++ b/spec/ruby/library/matrix/diagonal_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix.diagonal" do
+ before :each do
+ @m = Matrix.diagonal(10, 11, 12, 13, 14)
+ end
+
+ it "returns an object of type Matrix" do
+ @m.should be_kind_of(Matrix)
+ end
+
+ it "returns a square Matrix of the right size" do
+ @m.column_size.should == 5
+ @m.row_size.should == 5
+ end
+
+ it "sets the diagonal to the arguments" do
+ (0..4).each do |i|
+ @m[i, i].should == i + 10
+ end
+ end
+
+ it "fills all non-diagonal cells with 0" do
+ (0..4).each do |i|
+ (0..4).each do |j|
+ if i != j
+ @m[i, j].should == 0
+ end
+ end
+ end
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.diagonal(1).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
+
+describe "Matrix.diagonal?" do
+ it "returns true for a diagonal Matrix" do
+ Matrix.diagonal([1, 2, 3]).diagonal?.should be_true
+ end
+
+ it "returns true for a zero square Matrix" do
+ Matrix.zero(3).diagonal?.should be_true
+ end
+
+ it "returns false for a non diagonal square Matrix" do
+ Matrix[[0, 1], [0, 0]].diagonal?.should be_false
+ Matrix[[1, 2, 3], [1, 2, 3], [1, 2, 3]].diagonal?.should be_false
+ end
+
+ it "returns true for an empty 0x0 matrix" do
+ Matrix.empty(0,0).diagonal?.should be_true
+ end
+
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ lambda {
+ rectangular_matrix.diagonal?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/divide_spec.rb b/spec/ruby/library/matrix/divide_spec.rb
new file mode 100644
index 0000000000..205bc5d892
--- /dev/null
+++ b/spec/ruby/library/matrix/divide_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix#/" do
+ before :each do
+ @a = Matrix[ [1, 2], [3, 4] ]
+ @b = Matrix[ [4, 5], [6, 7] ]
+ @c = Matrix[ [1.2, 2.4], [3.6, 4.8] ]
+ end
+
+ it "returns the result of dividing self by another Matrix" do
+ (@a / @b).should be_close_to_matrix([[2.5, -1.5], [1.5, -0.5]])
+ end
+
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ it "returns the result of dividing self by a Fixnum" do
+ (@a / 2).should == Matrix[ [0, 1], [1, 2] ]
+ end
+
+ it "returns the result of dividing self by a Bignum" do
+ (@a / bignum_value).should == Matrix[ [0, 0], [0, 0] ]
+ end
+ end
+
+ it "returns the result of dividing self by a Float" do
+ (@c / 1.2).should == Matrix[ [1, 2], [3, 4] ]
+ end
+
+ it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do
+ lambda { @a / Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+
+ it "returns an instance of Matrix" do
+ (@a / @b).should be_kind_of(Matrix)
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ m = MatrixSub.ins
+ (m/m).should be_an_instance_of(MatrixSub)
+ (m/1).should be_an_instance_of(MatrixSub)
+ end
+ end
+
+ it "raises a TypeError if other is of wrong type" do
+ lambda { @a / nil }.should raise_error(TypeError)
+ lambda { @a / "a" }.should raise_error(TypeError)
+ lambda { @a / [ [1, 2] ] }.should raise_error(TypeError)
+ lambda { @a / Object.new }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/matrix/each_spec.rb b/spec/ruby/library/matrix/each_spec.rb
new file mode 100644
index 0000000000..214aae1a3a
--- /dev/null
+++ b/spec/ruby/library/matrix/each_spec.rb
@@ -0,0 +1,74 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#each" do
+ before :all do
+ @m = Matrix[ [1, 2, 3], [4, 5, 6] ]
+ @result = (1..6).to_a
+ end
+
+ it "returns an Enumerator when called without a block" do
+ enum = @m.each
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.should == @result
+ end
+
+ it "returns self" do
+ @m.each{}.should equal(@m)
+ end
+
+ it "yields the elements starting with the those of the first row" do
+ a = []
+ @m.each {|x| a << x}
+ a.should == @result
+ end
+end
+
+describe "Matrix#each with an argument" do
+ before :all do
+ @m = Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ]
+ @t = Matrix[ [1, 2], [3, 4], [5, 6], [7, 8] ]
+ end
+
+ it "raises an ArgumentError for unrecognized argument" do
+ lambda {
+ @m.each("all"){}
+ }.should raise_error(ArgumentError)
+ lambda {
+ @m.each(nil){}
+ }.should raise_error(ArgumentError)
+ lambda {
+ @m.each(:left){}
+ }.should raise_error(ArgumentError)
+ end
+
+ it "yields the rights elements when passed :diagonal" do
+ @m.each(:diagonal).to_a.should == [1, 6]
+ @t.each(:diagonal).to_a.should == [1, 4]
+ end
+
+ it "yields the rights elements when passed :off_diagonal" do
+ @m.each(:off_diagonal).to_a.should == [2, 3, 4, 5, 7, 8]
+ @t.each(:off_diagonal).to_a.should == [2, 3, 5, 6, 7, 8]
+ end
+
+ it "yields the rights elements when passed :lower" do
+ @m.each(:lower).to_a.should == [1, 5, 6]
+ @t.each(:lower).to_a.should == [1, 3, 4, 5, 6, 7, 8]
+ end
+
+ it "yields the rights elements when passed :strict_lower" do
+ @m.each(:strict_lower).to_a.should == [5]
+ @t.each(:strict_lower).to_a.should == [3, 5, 6, 7, 8]
+ end
+
+ it "yields the rights elements when passed :strict_upper" do
+ @m.each(:strict_upper).to_a.should == [2, 3, 4, 7, 8]
+ @t.each(:strict_upper).to_a.should == [2]
+ end
+
+ it "yields the rights elements when passed :upper" do
+ @m.each(:upper).to_a.should == [1, 2, 3, 4, 6, 7, 8]
+ @t.each(:upper).to_a.should == [1, 2, 4]
+ end
+end
diff --git a/spec/ruby/library/matrix/each_with_index_spec.rb b/spec/ruby/library/matrix/each_with_index_spec.rb
new file mode 100644
index 0000000000..36a1309292
--- /dev/null
+++ b/spec/ruby/library/matrix/each_with_index_spec.rb
@@ -0,0 +1,81 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#each_with_index" do
+ before :all do
+ @m = Matrix[ [1, 2, 3], [4, 5, 6] ]
+ @result = [
+ [1, 0, 0],
+ [2, 0, 1],
+ [3, 0, 2],
+ [4, 1, 0],
+ [5, 1, 1],
+ [6, 1, 2]
+ ]
+ end
+
+ it "returns an Enumerator when called without a block" do
+ enum = @m.each_with_index
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.should == @result
+ end
+
+ it "returns self" do
+ @m.each_with_index{}.should equal(@m)
+ end
+
+ it "yields the elements starting with the those of the first row" do
+ a = []
+ @m.each_with_index {|x, r, c| a << [x, r, c]}
+ a.should == @result
+ end
+end
+
+describe "Matrix#each_with_index with an argument" do
+ before :all do
+ @m = Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ]
+ @t = Matrix[ [1, 2], [3, 4], [5, 6], [7, 8] ]
+ end
+
+ it "raises an ArgumentError for unrecognized argument" do
+ lambda {
+ @m.each_with_index("all"){}
+ }.should raise_error(ArgumentError)
+ lambda {
+ @m.each_with_index(nil){}
+ }.should raise_error(ArgumentError)
+ lambda {
+ @m.each_with_index(:left){}
+ }.should raise_error(ArgumentError)
+ end
+
+ it "yields the rights elements when passed :diagonal" do
+ @m.each_with_index(:diagonal).to_a.should == [[1, 0, 0], [6, 1, 1]]
+ @t.each_with_index(:diagonal).to_a.should == [[1, 0, 0], [4, 1, 1]]
+ end
+
+ it "yields the rights elements when passed :off_diagonal" do
+ @m.each_with_index(:off_diagonal).to_a.should == [[2, 0, 1], [3, 0, 2], [4, 0, 3], [5, 1, 0], [7, 1, 2], [8, 1, 3]]
+ @t.each_with_index(:off_diagonal).to_a.should == [[2, 0, 1], [3, 1, 0], [5, 2, 0], [6, 2, 1], [7, 3, 0], [8, 3, 1]]
+ end
+
+ it "yields the rights elements when passed :lower" do
+ @m.each_with_index(:lower).to_a.should == [[1, 0, 0], [5, 1, 0], [6, 1, 1]]
+ @t.each_with_index(:lower).to_a.should == [[1, 0, 0], [3, 1, 0], [4, 1, 1], [5, 2, 0], [6, 2, 1], [7, 3, 0], [8, 3, 1]]
+ end
+
+ it "yields the rights elements when passed :strict_lower" do
+ @m.each_with_index(:strict_lower).to_a.should == [[5, 1, 0]]
+ @t.each_with_index(:strict_lower).to_a.should == [[3, 1, 0], [5, 2, 0], [6, 2, 1], [7, 3, 0], [8, 3, 1]]
+ end
+
+ it "yields the rights elements when passed :strict_upper" do
+ @m.each_with_index(:strict_upper).to_a.should == [[2, 0, 1], [3, 0, 2], [4, 0, 3], [7, 1, 2], [8, 1, 3]]
+ @t.each_with_index(:strict_upper).to_a.should == [[2, 0, 1]]
+ end
+
+ it "yields the rights elements when passed :upper" do
+ @m.each_with_index(:upper).to_a.should == [[1, 0, 0], [2, 0, 1], [3, 0, 2], [4, 0, 3], [6, 1, 1], [7, 1, 2], [8, 1, 3]]
+ @t.each_with_index(:upper).to_a.should == [[1, 0, 0], [2, 0, 1], [4, 1, 1]]
+ end
+end
diff --git a/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalue_matrix_spec.rb b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalue_matrix_spec.rb
new file mode 100644
index 0000000000..67f9dd1c19
--- /dev/null
+++ b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalue_matrix_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::EigenvalueDecomposition#eigenvalue_matrix" do
+ it "returns a diagonal matrix with the eigenvalues on the diagonal" do
+ Matrix[[14, 16], [-6, -6]].eigensystem.eigenvalue_matrix.should == Matrix[[6, 0],[0, 2]]
+ Matrix[[1, 1], [-1, 1]].eigensystem.eigenvalue_matrix.should == Matrix[[Complex(1,1), 0],[0, Complex(1,-1)]]
+ end
+end
diff --git a/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalues_spec.rb b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalues_spec.rb
new file mode 100644
index 0000000000..8d47bed5e6
--- /dev/null
+++ b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvalues_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::EigenvalueDecomposition#eigenvalues" do
+ it "returns an array of complex eigenvalues for a rotation matrix" do
+ Matrix[[ 1, 1],
+ [-1, 1]].eigensystem.eigenvalues.sort_by{|v| v.imag}.should ==
+ [ Complex(1, -1), Complex(1, 1)]
+ end
+
+ it "returns an array of real eigenvalues for a symetric matrix" do
+ Matrix[[1, 2],
+ [2, 1]].eigensystem.eigenvalues.sort.map!{|x| x.round(10)}.should ==
+ [ -1, 3 ]
+ end
+
+ it "returns an array of real eigenvalues for a matrix" do
+ Matrix[[14, 16],
+ [-6, -6]].eigensystem.eigenvalues.sort.map!{|x| x.round(10)}.should ==
+ [ 2, 6 ]
+ end
+end
diff --git a/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvector_matrix_spec.rb b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvector_matrix_spec.rb
new file mode 100644
index 0000000000..38217ca373
--- /dev/null
+++ b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvector_matrix_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::EigenvalueDecomposition#eigenvector_matrix" do
+ it "returns a complex eigenvector matrix given a rotation matrix" do
+ # Fix me: should test for linearity, not for equality
+ Matrix[[ 1, 1],
+ [-1, 1]].eigensystem.eigenvector_matrix.should ==
+ Matrix[[1, 1],
+ [Complex(0, 1), Complex(0, -1)]]
+ end
+
+ it "returns an real eigenvector matrix for a symetric matrix" do
+ # Fix me: should test for linearity, not for equality
+ Matrix[[1, 2],
+ [2, 1]].eigensystem.eigenvector_matrix.should ==
+ Matrix[[0.7071067811865475, 0.7071067811865475],
+ [-0.7071067811865475, 0.7071067811865475]]
+ end
+end
diff --git a/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvectors_spec.rb b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvectors_spec.rb
new file mode 100644
index 0000000000..968d02e27c
--- /dev/null
+++ b/spec/ruby/library/matrix/eigenvalue_decomposition/eigenvectors_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::EigenvalueDecomposition#eigenvectors" do
+ it "returns an array of complex eigenvectors for a rotation matrix" do
+ # Fix me: should test for linearity, not for equality
+ Matrix[[ 1, 1],
+ [-1, 1]].eigensystem.eigenvectors.should ==
+ [ Vector[1, Complex(0, 1)],
+ Vector[1, Complex(0, -1)]
+ ]
+ end
+
+ it "returns an array of real eigenvectors for a symetric matrix" do
+ # Fix me: should test for linearity, not for equality
+ Matrix[[1, 2],
+ [2, 1]].eigensystem.eigenvectors.should ==
+ [ Vector[0.7071067811865475, -0.7071067811865475],
+ Vector[0.7071067811865475, 0.7071067811865475]
+ ]
+ end
+end
diff --git a/spec/ruby/library/matrix/eigenvalue_decomposition/initialize_spec.rb b/spec/ruby/library/matrix/eigenvalue_decomposition/initialize_spec.rb
new file mode 100644
index 0000000000..f8964070d9
--- /dev/null
+++ b/spec/ruby/library/matrix/eigenvalue_decomposition/initialize_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::EigenvalueDecomposition#initialize" do
+ it "raises an error if argument is not a matrix" do
+ lambda {
+ Matrix::EigenvalueDecomposition.new([[]])
+ }.should raise_error(TypeError)
+ lambda {
+ Matrix::EigenvalueDecomposition.new(42)
+ }.should raise_error(TypeError)
+ end
+
+ it "raises an error if matrix is not square" do
+ lambda {
+ Matrix::EigenvalueDecomposition.new(Matrix[[1, 2]])
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+
+ it "never hangs" do
+ m = Matrix[ [0,0,0,0,0], [0,0,0,0,1], [0,0,0,1,0], [1,1,0,0,1], [1,0,1,0,1] ]
+ Matrix::EigenvalueDecomposition.new(m).should_not == "infinite loop"
+ end
+end
diff --git a/spec/ruby/library/matrix/eigenvalue_decomposition/to_a_spec.rb b/spec/ruby/library/matrix/eigenvalue_decomposition/to_a_spec.rb
new file mode 100644
index 0000000000..8be41a5720
--- /dev/null
+++ b/spec/ruby/library/matrix/eigenvalue_decomposition/to_a_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::EigenvalueDecomposition#to_a" do
+ before :each do
+ @a = Matrix[[14, 16], [-6, -6]]
+ @e = Matrix::EigenvalueDecomposition.new(@a)
+ end
+
+ it "returns an array of with [V, D, V.inv]" do
+ @e.to_a.should == [@e.v, @e.d, @e.v_inv]
+ end
+
+ it "returns a factorization" do
+ v, d, v_inv = @e.to_a
+ (v * d * v_inv).map{|e| e.round(10)}.should == @a
+ end
+end
diff --git a/spec/ruby/library/matrix/element_reference_spec.rb b/spec/ruby/library/matrix/element_reference_spec.rb
new file mode 100644
index 0000000000..b950d1c391
--- /dev/null
+++ b/spec/ruby/library/matrix/element_reference_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#[]" do
+
+ before :all do
+ @m = Matrix[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
+ end
+
+ it "returns element at (i, j)" do
+ (0..3).each do |i|
+ (0..2).each do |j|
+ @m[i, j].should == (i * 3) + j
+ end
+ end
+ end
+
+ it "returns nil for an invalid index pair" do
+ @m[8,1].should be_nil
+ @m[1,8].should be_nil
+ end
+
+end
diff --git a/spec/ruby/library/matrix/empty_spec.rb b/spec/ruby/library/matrix/empty_spec.rb
new file mode 100644
index 0000000000..ce23d926d6
--- /dev/null
+++ b/spec/ruby/library/matrix/empty_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix#empty?" do
+ it "returns true when the Matrix is empty" do
+ Matrix[ ].empty?.should be_true
+ Matrix[ [], [], [] ].empty?.should be_true
+ Matrix[ [], [], [] ].transpose.empty?.should be_true
+ end
+
+ it "returns false when the Matrix has elements" do
+ Matrix[ [1, 2] ].empty?.should be_false
+ Matrix[ [1], [2] ].empty?.should be_false
+ end
+
+ it "doesn't accept any parameter" do
+ lambda{
+ Matrix[ [1, 2] ].empty?(42)
+ }.should raise_error(ArgumentError)
+ end
+end
+
+describe "Matrix.empty" do
+ it "returns an empty matrix of the requested size" do
+ m = Matrix.empty(3, 0)
+ m.row_size.should == 3
+ m.column_size.should == 0
+
+ m = Matrix.empty(0, 3)
+ m.row_size.should == 0
+ m.column_size.should == 3
+ end
+
+ it "has arguments defaulting to 0" do
+ Matrix.empty.should == Matrix.empty(0, 0)
+ Matrix.empty(42).should == Matrix.empty(42, 0)
+ end
+
+ it "does not accept more than two parameters" do
+ lambda{
+ Matrix.empty(1, 2, 3)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error if both dimensions are > 0" do
+ lambda{
+ Matrix.empty(1, 2)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error if any dimension is < 0" do
+ lambda{
+ Matrix.empty(-2, 0)
+ }.should raise_error(ArgumentError)
+
+ lambda{
+ Matrix.empty(0, -2)
+ }.should raise_error(ArgumentError)
+ end
+
+end
+
+describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.empty(0, 1).should be_an_instance_of(MatrixSub)
+ end
+end
diff --git a/spec/ruby/library/matrix/eql_spec.rb b/spec/ruby/library/matrix/eql_spec.rb
new file mode 100644
index 0000000000..ea26c3320d
--- /dev/null
+++ b/spec/ruby/library/matrix/eql_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/equal_value'
+require 'matrix'
+
+describe "Matrix#eql?" do
+ it_behaves_like :equal, :eql?
+
+ it "returns false if some elements are == but not eql?" do
+ Matrix[[1, 2],[3, 4]].eql?(Matrix[[1, 2],[3, 4.0]]).should be_false
+ end
+end
diff --git a/spec/ruby/library/matrix/equal_value_spec.rb b/spec/ruby/library/matrix/equal_value_spec.rb
new file mode 100644
index 0000000000..10cf1e6c29
--- /dev/null
+++ b/spec/ruby/library/matrix/equal_value_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/equal_value'
+require 'matrix'
+
+describe "Matrix#==" do
+ it_behaves_like :equal, :==
+
+ it "returns true if some elements are == but not eql?" do
+ Matrix[[1, 2],[3, 4]].should == Matrix[[1, 2],[3, 4.0]]
+ end
+end
diff --git a/spec/ruby/library/matrix/exponent_spec.rb b/spec/ruby/library/matrix/exponent_spec.rb
new file mode 100644
index 0000000000..c496d9d3cc
--- /dev/null
+++ b/spec/ruby/library/matrix/exponent_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix#**" do
+
+ describe "given an integer _n_" do
+ it "multiples the Matrix by itself _n_ times" do
+ m = Matrix[ [7,6], [3,9] ]
+ (m ** 1).should == m
+ (m ** 2).should == Matrix[ [67, 96], [48,99] ]
+ (m ** 2).should == m * m
+ (m ** 3).should == m * m * m
+ (m ** 4).should == m * m * m * m
+ (m ** 5).should == m * m * m * m * m
+ end
+
+ it "raises a ErrDimensionMismatch for non square matrices" do
+ m = Matrix[ [1, 1], [1, 2], [2, 3]]
+ lambda { m ** 3 }.should raise_error(Matrix::ErrDimensionMismatch)
+ lambda { m ** 0 }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+
+ describe "that is <= 0" do
+ it "returns the inverse of **(-n)" do
+ m = Matrix[ [1, 1], [1, 2] ]
+ (m ** -2).should == Matrix[ [5, -3], [-3, 2]]
+ (m ** -4).should == (m.inverse ** 4)
+ end
+
+ it "raises a ErrDimensionMismatch for irregular matrices" do
+ m = Matrix[ [1, 1], [1, 1] ]
+ lambda { m ** -2 }.should raise_error(Matrix::ErrNotRegular)
+ lambda { m ** 0 }.should raise_error(Matrix::ErrNotRegular)
+ end
+ end
+ end
+
+ it "returns the power for non integer powers" do
+ a = Matrix[[5, 4], [4, 5]]
+ ((a ** 0.5) ** 2).round(8).should == a
+ a = Matrix[[7, 10], [15, 22]]
+ ((a ** 0.25) ** 4).round(8).should == a
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ (MatrixSub.ins ** 1).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/find_index_spec.rb b/spec/ruby/library/matrix/find_index_spec.rb
new file mode 100644
index 0000000000..27672e526b
--- /dev/null
+++ b/spec/ruby/library/matrix/find_index_spec.rb
@@ -0,0 +1,146 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#find_index without any argument" do
+ before :all do
+ @m = Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ]
+ end
+
+ it "returns an Enumerator when called without a block" do
+ enum = @m.find_index
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.should == [1, 2, 3, 4, 5, 6, 7, 8]
+ end
+
+ it "returns nil if the block is always false" do
+ @m.find_index{false}.should be_nil
+ end
+
+ it "returns the first index for which the block is true" do
+ @m.find_index{|x| x >= 3}.should == [0, 2]
+ end
+end
+
+describe "Matrix#find_index with a subselection argument" do
+ before :all do
+ @tests = [
+ [ Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ], {
+ diagonal: [1, 6] ,
+ off_diagonal: [2, 3, 4, 5, 7, 8],
+ lower: [1, 5, 6] ,
+ strict_lower: [5] ,
+ strict_upper: [2, 3, 4, 7, 8] ,
+ upper: [1, 2, 3, 4, 6, 7, 8] ,
+ }
+ ],
+ [ Matrix[ [1, 2], [3, 4], [5, 6], [7, 8] ], {
+ diagonal: [1, 4] ,
+ off_diagonal: [2, 3, 5, 6, 7, 8],
+ lower: [1, 3, 4, 5, 6, 7, 8] ,
+ strict_lower: [3, 5, 6, 7, 8] ,
+ strict_upper: [2] ,
+ upper: [1, 2, 4] ,
+ }
+ ]]
+ end
+
+ describe "and no generic argument" do
+ it "returns an Enumerator when called without a block" do
+ @tests.each do |matrix, h|
+ h.each do |selector, result|
+ matrix.find_index(selector).should be_an_instance_of(Enumerator)
+ end
+ end
+ end
+
+ it "yields the rights elements" do
+ @tests.each do |matrix, h|
+ h.each do |selector, result|
+ matrix.find_index(selector).to_a.should == result
+ end
+ end
+ end
+
+ it "returns the first index for which the block returns true" do
+ @tests.each do |matrix, h|
+ h.each do |selector, result|
+ cnt = result.size.div 2
+ which = result[cnt]
+ idx = matrix.find_index(selector){|x| cnt -= 1; x == which}
+ matrix[*idx].should == which
+ cnt.should == -1
+ end
+ end
+ end
+
+ it "returns nil if the block is always false" do
+ @tests.each do |matrix, h|
+ h.each do |selector, result|
+ matrix.find_index(selector){ nil }.should == nil
+ end
+ end
+ end
+
+ end
+
+ describe "and a generic argument" do
+ it "ignores a block" do
+ @m.find_index(42, :diagonal){raise "oups"}.should == nil
+ end
+
+ it "returns the index of the requested value" do
+ @tests.each do |matrix, h|
+ h.each do |selector, result|
+ cnt = result.size / 2
+ which = result[cnt]
+ idx = matrix.find_index(which, selector)
+ matrix[*idx].should == which
+ end
+ end
+ end
+
+ it "returns nil if the requested value is not found" do
+ @tests.each do |matrix, h|
+ h.each do |selector, result|
+ matrix.find_index(42, selector).should == nil
+ end
+ end
+ end
+ end
+
+end
+
+describe "Matrix#find_index with only a generic argument" do
+ before :all do
+ @m = Matrix[ [1, 2, 3, 4], [1, 2, 3, 4] ]
+ end
+
+ it "returns nil if the value is not found" do
+ @m.find_index(42).should be_nil
+ end
+
+ it "returns the first index for of the requested value" do
+ @m.find_index(3).should == [0, 2]
+ end
+
+ it "ignores a block" do
+ @m.find_index(4){raise "oups"}.should == [0, 3]
+ end
+end
+
+describe "Matrix#find_index with two arguments" do
+ it "raises an ArgumentError for an unrecognized last argument" do
+ lambda {
+ @m.find_index(1, "all"){}
+ }.should raise_error(ArgumentError)
+ lambda {
+ @m.find_index(1, nil){}
+ }.should raise_error(ArgumentError)
+ lambda {
+ @m.find_index(1, :left){}
+ }.should raise_error(ArgumentError)
+ lambda {
+ @m.find_index(:diagonal, 1){}
+ }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/matrix/fixtures/classes.rb b/spec/ruby/library/matrix/fixtures/classes.rb
new file mode 100644
index 0000000000..394377135f
--- /dev/null
+++ b/spec/ruby/library/matrix/fixtures/classes.rb
@@ -0,0 +1,7 @@
+require 'matrix'
+
+class MatrixSub < Matrix
+ def self.ins
+ rows([[1, 0], [0, 1]])
+ end
+end
diff --git a/spec/ruby/library/matrix/hash_spec.rb b/spec/ruby/library/matrix/hash_spec.rb
new file mode 100644
index 0000000000..fac9a06527
--- /dev/null
+++ b/spec/ruby/library/matrix/hash_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#hash" do
+
+ it "returns a Fixnum" do
+ Matrix[ [1,2] ].hash.should be_an_instance_of(Fixnum)
+ end
+
+ it "returns the same value for the same matrix" do
+ data = [ [40,5], [2,7] ]
+ Matrix[ *data ].hash.should == Matrix[ *data ].hash
+ end
+
+end
diff --git a/spec/ruby/library/matrix/hermitian_spec.rb b/spec/ruby/library/matrix/hermitian_spec.rb
new file mode 100644
index 0000000000..dda5280d64
--- /dev/null
+++ b/spec/ruby/library/matrix/hermitian_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix.hermitian?" do
+ it "returns true for a hermitian Matrix" do
+ Matrix[[1, 2, Complex(0, 3)], [2, 4, 5], [Complex(0, -3), 5, 6]].hermitian?.should be_true
+ end
+
+ it "returns true for a 0x0 empty matrix" do
+ Matrix.empty.hermitian?.should be_true
+ end
+
+ it "returns false for an asymmetric Matrix" do
+ Matrix[[1, 2],[-2, 1]].hermitian?.should be_false
+ end
+
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ lambda {
+ rectangular_matrix.hermitian?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+ end
+
+ it "returns false for a matrix with complex values on the diagonal" do
+ Matrix[[Complex(1,1)]].hermitian?.should be_false
+ Matrix[[Complex(1,0)]].hermitian?.should be_true
+ end
+end
diff --git a/spec/ruby/library/matrix/identity_spec.rb b/spec/ruby/library/matrix/identity_spec.rb
new file mode 100644
index 0000000000..646462bc47
--- /dev/null
+++ b/spec/ruby/library/matrix/identity_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/identity'
+
+describe "Matrix.identity" do
+ it_behaves_like :matrix_identity, :identity
+end
diff --git a/spec/ruby/library/matrix/imag_spec.rb b/spec/ruby/library/matrix/imag_spec.rb
new file mode 100644
index 0000000000..1c988753d8
--- /dev/null
+++ b/spec/ruby/library/matrix/imag_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/imaginary'
+
+describe "Matrix#imag" do
+ it_behaves_like :matrix_imaginary, :imag
+end
diff --git a/spec/ruby/library/matrix/imaginary_spec.rb b/spec/ruby/library/matrix/imaginary_spec.rb
new file mode 100644
index 0000000000..ceae4bbe8d
--- /dev/null
+++ b/spec/ruby/library/matrix/imaginary_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/imaginary'
+
+describe "Matrix#imaginary" do
+ it_behaves_like :matrix_imaginary, :imaginary
+end
diff --git a/spec/ruby/library/matrix/inspect_spec.rb b/spec/ruby/library/matrix/inspect_spec.rb
new file mode 100644
index 0000000000..508f478252
--- /dev/null
+++ b/spec/ruby/library/matrix/inspect_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix#inspect" do
+
+ it "returns a stringified representation of the Matrix" do
+ Matrix[ [1,2], [2,1] ].inspect.should == "Matrix[[1, 2], [2, 1]]"
+ end
+
+ it "returns 'Matrix.empty(...)' for empty matrices" do
+ Matrix[ [], [], [] ].inspect.should == "Matrix.empty(3, 0)"
+ Matrix.columns([ [], [], [] ]).inspect.should == "Matrix.empty(0, 3)"
+ end
+
+ it "calls inspect on its contents" do
+ obj = mock("some_value")
+ obj.should_receive(:inspect).and_return("some_value")
+ Matrix[ [1, 2], [3, obj] ].inspect.should == "Matrix[[1, 2], [3, some_value]]"
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns a string using the subclass' name" do
+ MatrixSub.ins.inspect.should == "MatrixSub[[1, 0], [0, 1]]"
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/inv_spec.rb b/spec/ruby/library/matrix/inv_spec.rb
new file mode 100644
index 0000000000..82879a6d82
--- /dev/null
+++ b/spec/ruby/library/matrix/inv_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'shared/inverse'
+
+describe "Matrix#inv" do
+ it_behaves_like :inverse, :inv
+end
diff --git a/spec/ruby/library/matrix/inverse_from_spec.rb b/spec/ruby/library/matrix/inverse_from_spec.rb
new file mode 100644
index 0000000000..651d56a244
--- /dev/null
+++ b/spec/ruby/library/matrix/inverse_from_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#inverse_from" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/matrix/inverse_spec.rb b/spec/ruby/library/matrix/inverse_spec.rb
new file mode 100644
index 0000000000..fa3fa7de8a
--- /dev/null
+++ b/spec/ruby/library/matrix/inverse_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'shared/inverse'
+
+describe "Matrix#inverse" do
+ it_behaves_like :inverse, :inverse
+end
diff --git a/spec/ruby/library/matrix/lower_triangular_spec.rb b/spec/ruby/library/matrix/lower_triangular_spec.rb
new file mode 100644
index 0000000000..f3aa4501f4
--- /dev/null
+++ b/spec/ruby/library/matrix/lower_triangular_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix.lower_triangular?" do
+ it "returns true for a square lower triangular Matrix" do
+ Matrix[[1, 0, 0], [1, 2, 0], [1, 2, 3]].lower_triangular?.should be_true
+ Matrix.diagonal([1, 2, 3]).lower_triangular?.should be_true
+ Matrix[[1, 0], [1, 2], [1, 2], [1, 2]].lower_triangular?.should be_true
+ Matrix[[1, 0, 0, 0], [1, 2, 0, 0]].lower_triangular?.should be_true
+ end
+
+ it "returns true for an empty Matrix" do
+ Matrix.empty(3, 0).lower_triangular?.should be_true
+ Matrix.empty(0, 3).lower_triangular?.should be_true
+ Matrix.empty(0, 0).lower_triangular?.should be_true
+ end
+
+ it "returns false for a non lower triangular square Matrix" do
+ Matrix[[0, 1], [0, 0]].lower_triangular?.should be_false
+ Matrix[[1, 2, 3], [1, 2, 3], [1, 2, 3]].lower_triangular?.should be_false
+ Matrix[[0, 1], [0, 0], [0, 0], [0, 0]].lower_triangular?.should be_false
+ Matrix[[0, 0, 0, 1], [0, 0, 0, 0]].lower_triangular?.should be_false
+ end
+end
diff --git a/spec/ruby/library/matrix/lup_decomposition/determinant_spec.rb b/spec/ruby/library/matrix/lup_decomposition/determinant_spec.rb
new file mode 100644
index 0000000000..ca2ddf18c6
--- /dev/null
+++ b/spec/ruby/library/matrix/lup_decomposition/determinant_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::LUPDecomposition#determinant" do
+ it "returns the determinant when the matrix is square" do
+ a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
+ a.lup.determinant.should == 15120 # == a.determinant
+ end
+
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[7, 8, 9], [14, 46, 51]],
+ Matrix[[7, 8], [14, 46], [28, 82]],
+ ].each do |m|
+ lup = m.lup
+ lambda {
+ lup.determinant
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/lup_decomposition/initialize_spec.rb b/spec/ruby/library/matrix/lup_decomposition/initialize_spec.rb
new file mode 100644
index 0000000000..00dc47ddb9
--- /dev/null
+++ b/spec/ruby/library/matrix/lup_decomposition/initialize_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::LUPDecomposition#initialize" do
+ it "raises an error if argument is not a matrix" do
+ lambda {
+ Matrix::LUPDecomposition.new([[]])
+ }.should raise_error(TypeError)
+ lambda {
+ Matrix::LUPDecomposition.new(42)
+ }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/matrix/lup_decomposition/l_spec.rb b/spec/ruby/library/matrix/lup_decomposition/l_spec.rb
new file mode 100644
index 0000000000..9514ab5d06
--- /dev/null
+++ b/spec/ruby/library/matrix/lup_decomposition/l_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::LUPDecomposition#l" do
+ before :each do
+ @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
+ @lu = Matrix::LUPDecomposition.new(@a)
+ @l = @lu.l
+ end
+
+ it "returns the first element of to_a" do
+ @l.should == @lu.to_a[0]
+ end
+
+ it "returns a lower triangular matrix" do
+ @l.lower_triangular?.should be_true
+ end
+end
diff --git a/spec/ruby/library/matrix/lup_decomposition/p_spec.rb b/spec/ruby/library/matrix/lup_decomposition/p_spec.rb
new file mode 100644
index 0000000000..c7b5e9196e
--- /dev/null
+++ b/spec/ruby/library/matrix/lup_decomposition/p_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::LUPDecomposition#p" do
+ before :each do
+ @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
+ @lu = Matrix::LUPDecomposition.new(@a)
+ @p = @lu.p
+ end
+
+ it "returns the third element of to_a" do
+ @p.should == @lu.to_a[2]
+ end
+
+ it "returns a permutation matrix" do
+ @p.permutation?.should be_true
+ end
+end
diff --git a/spec/ruby/library/matrix/lup_decomposition/solve_spec.rb b/spec/ruby/library/matrix/lup_decomposition/solve_spec.rb
new file mode 100644
index 0000000000..43d49de9c7
--- /dev/null
+++ b/spec/ruby/library/matrix/lup_decomposition/solve_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::LUPDecomposition#solve" do
+ describe "for rectangular matrices" do
+ it "raises an error for singular matrices" do
+ a = Matrix[[1, 2, 3], [1, 3, 5], [2, 5, 8]]
+ lu = Matrix::LUPDecomposition.new(a)
+ lambda {
+ lu.solve(a)
+ }.should raise_error(Matrix::ErrNotRegular)
+ end
+
+ describe "for non singular matrices" do
+ before :each do
+ @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
+ @lu = Matrix::LUPDecomposition.new(@a)
+ end
+
+ it "returns the appropriate empty matrix when given an empty matrix" do
+ @lu.solve(Matrix.empty(3,0)).should == Matrix.empty(3,0)
+ empty = Matrix::LUPDecomposition.new(Matrix.empty(0, 0))
+ empty.solve(Matrix.empty(0,3)).should == Matrix.empty(0,3)
+ end
+
+ it "returns the right matrix when given a matrix of the appropriate size" do
+ solution = Matrix[[1, 2, 3, 4], [0, 1, 2, 3], [-1, -2, -3, -4]]
+ values = Matrix[[-2, 4, 10, 16], [-37, -28, -19, -10], [-135, -188, -241, -294]] # == @a * solution
+ @lu.solve(values).should == solution
+ end
+
+ it "raises an error when given a matrix of the wrong size" do
+ values = Matrix[[1, 2, 3, 4], [0, 1, 2, 3]]
+ lambda {
+ @lu.solve(values)
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+
+ it "returns the right vector when given a vector of the appropriate size" do
+ solution = Vector[1, 2, -1]
+ values = Vector[14, 55, 29] # == @a * solution
+ @lu.solve(values).should == solution
+ end
+
+ it "raises an error when given a vector of the wrong size" do
+ values = Vector[14, 55]
+ lambda {
+ @lu.solve(values)
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/lup_decomposition/to_a_spec.rb b/spec/ruby/library/matrix/lup_decomposition/to_a_spec.rb
new file mode 100644
index 0000000000..9b1dccbbac
--- /dev/null
+++ b/spec/ruby/library/matrix/lup_decomposition/to_a_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::LUPDecomposition#to_a" do
+ before :each do
+ @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
+ @lu = Matrix::LUPDecomposition.new(@a)
+ @to_a = @lu.to_a
+ @l, @u, @p = @to_a
+ end
+
+ it "returns an array of three matrices" do
+ @to_a.should be_kind_of(Array)
+ @to_a.length.should == 3
+ @to_a.each{|m| m.should be_kind_of(Matrix)}
+ end
+
+ it "returns [l, u, p] such that l*u == a*p" do
+ (@l * @u).should == (@p * @a)
+ end
+
+ it "returns the right values for rectangular matrices" do
+ [
+ Matrix[[7, 8, 9], [14, 46, 51]],
+ Matrix[[4, 11], [5, 8], [3, 4]],
+ ].each do |a|
+ l, u, p = Matrix::LUPDecomposition.new(a).to_a
+ (l * u).should == (p * a)
+ end
+ end
+
+ it "has other properties implied by the specs of #l, #u and #p"
+end
diff --git a/spec/ruby/library/matrix/lup_decomposition/u_spec.rb b/spec/ruby/library/matrix/lup_decomposition/u_spec.rb
new file mode 100644
index 0000000000..ca3dfc1f00
--- /dev/null
+++ b/spec/ruby/library/matrix/lup_decomposition/u_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::LUPDecomposition#u" do
+ before :each do
+ @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]]
+ @lu = Matrix::LUPDecomposition.new(@a)
+ @u = @lu.u
+ end
+
+ it "returns the second element of to_a" do
+ @u.should == @lu.to_a[1]
+ end
+
+ it "returns an upper triangular matrix" do
+ @u.upper_triangular?.should be_true
+ end
+end
diff --git a/spec/ruby/library/matrix/map_spec.rb b/spec/ruby/library/matrix/map_spec.rb
new file mode 100644
index 0000000000..bc07c48cda
--- /dev/null
+++ b/spec/ruby/library/matrix/map_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/collect'
+
+describe "Matrix#map" do
+ it_behaves_like :collect, :map
+end
diff --git a/spec/ruby/library/matrix/minor_spec.rb b/spec/ruby/library/matrix/minor_spec.rb
new file mode 100644
index 0000000000..009826c3d6
--- /dev/null
+++ b/spec/ruby/library/matrix/minor_spec.rb
@@ -0,0 +1,85 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix#minor" do
+ before :each do
+ @matrix = Matrix[ [1,2], [3,4], [5,6] ]
+ end
+
+ describe "with start_row, nrows, start_col, ncols" do
+ it "returns the given portion of the Matrix" do
+ @matrix.minor(0,1,0,2).should == Matrix[ [1, 2] ]
+ @matrix.minor(1,2,1,1).should == Matrix[ [4], [6] ]
+ end
+
+ it "returns an empty Matrix if nrows or ncols is 0" do
+ @matrix.minor(0,0,0,0).should == Matrix[]
+ @matrix.minor(1,0,1,0).should == Matrix[]
+ @matrix.minor(1,0,1,1).should == Matrix.columns([[]])
+ @matrix.minor(1,1,1,0).should == Matrix[[]]
+ end
+
+ it "returns nil for out-of-bounds start_row/col" do
+ r = @matrix.row_size + 1
+ c = @matrix.column_size + 1
+ @matrix.minor(r,0,0,10).should == nil
+ @matrix.minor(0,10,c,9).should == nil
+ @matrix.minor(-r,0,0,10).should == nil
+ @matrix.minor(0,10,-c,9).should == nil
+ end
+
+ it "returns nil for negative nrows or ncols" do
+ @matrix.minor(0,1,0,-1).should == nil
+ @matrix.minor(0,-1,0,1).should == nil
+ end
+
+ it "start counting backwards for start_row or start_col below zero" do
+ @matrix.minor(0, 1, -1, 1).should == @matrix.minor(0, 1, 1, 1)
+ @matrix.minor(-1, 1, 0, 1).should == @matrix.minor(2, 1, 0, 1)
+ end
+
+ it "returns empty matrices for extreme start_row/col" do
+ @matrix.minor(3,10,1,10).should == Matrix.columns([[]])
+ @matrix.minor(1,10,2,10).should == Matrix[[], []]
+ @matrix.minor(3,0,0,10).should == Matrix.columns([[], []])
+ end
+
+ it "ignores big nrows or ncols" do
+ @matrix.minor(0,1,0,20).should == Matrix[ [1, 2] ]
+ @matrix.minor(1,20,1,1).should == Matrix[ [4], [6] ]
+ end
+ end
+
+ describe "with col_range, row_range" do
+ it "returns the given portion of the Matrix" do
+ @matrix.minor(0..0, 0..1).should == Matrix[ [1, 2] ]
+ @matrix.minor(1..2, 1..2).should == Matrix[ [4], [6] ]
+ @matrix.minor(1...3, 1...3).should == Matrix[ [4], [6] ]
+ end
+
+ it "returns nil if col_range or row_range is out of range" do
+ r = @matrix.row_size + 1
+ c = @matrix.column_size + 1
+ @matrix.minor(r..6, c..6).should == nil
+ @matrix.minor(0..1, c..6).should == nil
+ @matrix.minor(r..6, 0..1).should == nil
+ @matrix.minor(-r..6, -c..6).should == nil
+ @matrix.minor(0..1, -c..6).should == nil
+ @matrix.minor(-r..6, 0..1).should == nil
+ end
+
+ it "start counting backwards for col_range or row_range below zero" do
+ @matrix.minor(0..1, -2..-1).should == @matrix.minor(0..1, 0..1)
+ @matrix.minor(0..1, -2..1).should == @matrix.minor(0..1, 0..1)
+ @matrix.minor(-2..-1, 0..1).should == @matrix.minor(1..2, 0..1)
+ @matrix.minor(-2..2, 0..1).should == @matrix.minor(1..2, 0..1)
+ end
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.ins.minor(0, 1, 0, 1).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/minus_spec.rb b/spec/ruby/library/matrix/minus_spec.rb
new file mode 100644
index 0000000000..25191893a6
--- /dev/null
+++ b/spec/ruby/library/matrix/minus_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix#-" do
+ before :each do
+ @a = Matrix[ [1, 2], [3, 4] ]
+ @b = Matrix[ [4, 5], [6, 7] ]
+ end
+
+ it "returns the result of subtracting the corresponding elements of other from self" do
+ (@a - @b).should == Matrix[ [-3,-3], [-3,-3] ]
+ end
+
+ it "returns an instance of Matrix" do
+ (@a - @b).should be_kind_of(Matrix)
+ end
+
+ it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do
+ lambda { @a - Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+
+ it "raises a ExceptionForMatrix::ErrOperationNotDefined if other is a Numeric Type" do
+ lambda { @a - 2 }.should raise_error(Matrix::ErrOperationNotDefined)
+ lambda { @a - 1.2 }.should raise_error(Matrix::ErrOperationNotDefined)
+ lambda { @a - bignum_value }.should raise_error(Matrix::ErrOperationNotDefined)
+ end
+
+ it "raises a TypeError if other is of wrong type" do
+ lambda { @a - nil }.should raise_error(TypeError)
+ lambda { @a - "a" }.should raise_error(TypeError)
+ lambda { @a - [ [1, 2] ] }.should raise_error(TypeError)
+ lambda { @a - Object.new }.should raise_error(TypeError)
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ m = MatrixSub.ins
+ (m-m).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/multiply_spec.rb b/spec/ruby/library/matrix/multiply_spec.rb
new file mode 100644
index 0000000000..9b401a4dad
--- /dev/null
+++ b/spec/ruby/library/matrix/multiply_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix#*" do
+ before :each do
+ @a = Matrix[ [1, 2], [3, 4] ]
+ @b = Matrix[ [4, 5], [6, 7] ]
+ end
+
+ it "returns the result of multiplying the corresponding elements of self and a Matrix" do
+ (@a * @b).should == Matrix[ [16,19], [36,43] ]
+ end
+
+ it "returns the result of multiplying the corresponding elements of self and a Vector" do
+ (@a * Vector[1,2]).should == Vector[5, 11]
+ end
+
+ it "returns the result of multiplying the elements of self and a Fixnum" do
+ (@a * 2).should == Matrix[ [2, 4], [6, 8] ]
+ end
+
+ it "returns the result of multiplying the elements of self and a Bignum" do
+ (@a * bignum_value).should == Matrix[
+ [9223372036854775808, 18446744073709551616],
+ [27670116110564327424, 36893488147419103232]
+ ]
+ end
+
+ it "returns the result of multiplying the elements of self and a Float" do
+ (@a * 2.0).should == Matrix[ [2.0, 4.0], [6.0, 8.0] ]
+ end
+
+ it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do
+ lambda { @a * Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+
+ it "returns a zero matrix if (nx0) * (0xn)" do
+ (Matrix[[],[],[]] * Matrix.columns([[],[],[]])).should == Matrix.zero(3)
+ end
+
+ it "returns an empty matrix if (0xn) * (nx0)" do
+ (Matrix.columns([[],[],[]]) * Matrix[[],[],[]]).should == Matrix[]
+ end
+
+ it "returns a mx0 matrix if (mxn) * (nx0)" do
+ (Matrix[[1,2],[3,4],[5,6]] * Matrix[[],[]]).should == Matrix[[],[],[]]
+ end
+
+ it "returns a 0xm matrix if (0xm) * (mxn)" do
+ (Matrix.columns([[], [], []]) * Matrix[[1,2],[3,4],[5,6]]).should == Matrix.columns([[],[]])
+ end
+
+ it "raises a TypeError if other is of wrong type" do
+ lambda { @a * nil }.should raise_error(TypeError)
+ lambda { @a * "a" }.should raise_error(TypeError)
+ lambda { @a * [ [1, 2] ] }.should raise_error(TypeError)
+ lambda { @a * Object.new }.should raise_error(TypeError)
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ m = MatrixSub.ins
+ (m*m).should be_an_instance_of(MatrixSub)
+ (m*1).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/new_spec.rb b/spec/ruby/library/matrix/new_spec.rb
new file mode 100644
index 0000000000..3005066846
--- /dev/null
+++ b/spec/ruby/library/matrix/new_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix.new" do
+ it "is private" do
+ Matrix.should have_private_method(:new)
+ end
+end
diff --git a/spec/ruby/library/matrix/normal_spec.rb b/spec/ruby/library/matrix/normal_spec.rb
new file mode 100644
index 0000000000..437a3c82c9
--- /dev/null
+++ b/spec/ruby/library/matrix/normal_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix.normal?" do
+ # it "returns false for non normal matrices" do
+ # Matrix[[0, 1], [1, 2]].normal?.should == false
+ # end
+
+ it "returns true for normal matrices" do
+ Matrix[[1, 1, 0], [0, 1, 1], [1, 0, 1]].normal?.should == true
+ Matrix[[0, Complex(0, 2)], [Complex(0, -2), 0]].normal?.should == true
+ end
+
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ lambda {
+ rectangular_matrix.normal?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/orthogonal_spec.rb b/spec/ruby/library/matrix/orthogonal_spec.rb
new file mode 100644
index 0000000000..81df1d15ee
--- /dev/null
+++ b/spec/ruby/library/matrix/orthogonal_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix.orthogonal?" do
+ it "returns false for non orthogonal matrices" do
+ Matrix[[0, 1], [1, 2]].orthogonal?.should == false
+ Matrix[[1, 1, 0], [0, 1, 1], [1, 0, 1]].orthogonal?.should == false
+ end
+
+ it "returns true for orthogonal matrices" do
+ Matrix[[0, 1], [1, 0]].orthogonal?.should == true
+ end
+
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ lambda {
+ rectangular_matrix.orthogonal?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/permutation_spec.rb b/spec/ruby/library/matrix/permutation_spec.rb
new file mode 100644
index 0000000000..f6666588de
--- /dev/null
+++ b/spec/ruby/library/matrix/permutation_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#permutation?" do
+ it "returns true for a permutation Matrix" do
+ Matrix[[0, 1, 0], [0, 0, 1], [1, 0, 0]].permutation?.should be_true
+ end
+
+ it "returns false for a non permutation square Matrix" do
+ Matrix[[0, 1], [0, 0]].permutation?.should be_false
+ Matrix[[-1, 0], [0, -1]].permutation?.should be_false
+ Matrix[[1, 0], [1, 0]].permutation?.should be_false
+ Matrix[[1, 0], [1, 1]].permutation?.should be_false
+ end
+
+ it "returns true for an empty 0x0 matrix" do
+ Matrix.empty(0,0).permutation?.should be_true
+ end
+
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ lambda {
+ rectangular_matrix.permutation?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/plus_spec.rb b/spec/ruby/library/matrix/plus_spec.rb
new file mode 100644
index 0000000000..cad04f80a3
--- /dev/null
+++ b/spec/ruby/library/matrix/plus_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix#+" do
+ before :each do
+ @a = Matrix[ [1,2], [3,4] ]
+ @b = Matrix[ [4,5], [6,7] ]
+ end
+
+ it "returns the result of adding the corresponding elements of self and other" do
+ (@a + @b).should == Matrix[ [5,7], [9,11] ]
+ end
+
+ it "returns an instance of Matrix" do
+ (@a + @b).should be_kind_of(Matrix)
+ end
+
+ it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do
+ lambda { @a + Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+
+ it "raises a ExceptionForMatrix::ErrOperationNotDefined if other is a Numeric Type" do
+ lambda { @a + 2 }.should raise_error(ExceptionForMatrix::ErrOperationNotDefined)
+ lambda { @a + 1.2 }.should raise_error(ExceptionForMatrix::ErrOperationNotDefined)
+ lambda { @a + bignum_value }.should raise_error(ExceptionForMatrix::ErrOperationNotDefined)
+ end
+
+ it "raises a TypeError if other is of wrong type" do
+ lambda { @a + nil }.should raise_error(TypeError)
+ lambda { @a + "a" }.should raise_error(TypeError)
+ lambda { @a + [ [1, 2] ] }.should raise_error(TypeError)
+ lambda { @a + Object.new }.should raise_error(TypeError)
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ m = MatrixSub.ins
+ (m+m).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/rank_spec.rb b/spec/ruby/library/matrix/rank_spec.rb
new file mode 100644
index 0000000000..d4403d23ed
--- /dev/null
+++ b/spec/ruby/library/matrix/rank_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#rank" do
+ it "returns the rank of the Matrix" do
+ Matrix[ [7,6], [3,9] ].rank.should == 2
+ end
+
+ it "doesn't loop forever" do
+ Matrix[ [1,2,3], [4,5,6], [7,8,9] ].rank.should == 2
+ Matrix[ [1, 2, 0, 3], [1, -2, 3, 0], [0, 0, 4, 8], [2, 4, 0, 6] ].rank.
+ should == 3
+ end
+
+ it "works for some easy rectangular matrices" do
+ Matrix[[0,0],[0,0],[1,0]].rank.should == 1
+ Matrix[[0,1],[0,0],[1,0]].rank.should == 2
+ end
+end
diff --git a/spec/ruby/library/matrix/real_spec.rb b/spec/ruby/library/matrix/real_spec.rb
new file mode 100644
index 0000000000..38033c63c8
--- /dev/null
+++ b/spec/ruby/library/matrix/real_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix#real?" do
+ it "returns true for matrices with all real entries" do
+ Matrix[ [1, 2], [3, 4] ].real?.should be_true
+ Matrix[ [1.9, 2], [3, 4] ].real?.should be_true
+ end
+
+ it "returns true for empty matrices" do
+ Matrix.empty.real?.should be_true
+ end
+
+ it "returns false if one element is a Complex" do
+ Matrix[ [Complex(1,1), 2], [3, 4] ].real?.should be_false
+ end
+
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ it "returns false if one element is a Complex whose imaginary part is 0" do
+ Matrix[ [Complex(1,0), 2], [3, 4] ].real?.should be_false
+ end
+ end
+end
+
+describe "Matrix#real" do
+ it "returns a matrix with the real part of the elements of the receiver" do
+ Matrix[ [1, 2], [3, 4] ].real.should == Matrix[ [1, 2], [3, 4] ]
+ Matrix[ [1.9, Complex(1,1)], [Complex(-0.42, 0), 4] ].real.should == Matrix[ [1.9, 1], [-0.42, 4] ]
+ end
+
+ it "returns empty matrices on the same size if empty" do
+ Matrix.empty(0, 3).real.should == Matrix.empty(0, 3)
+ Matrix.empty(3, 0).real.should == Matrix.empty(3, 0)
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.ins.real.should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/rect_spec.rb b/spec/ruby/library/matrix/rect_spec.rb
new file mode 100644
index 0000000000..83a0404e47
--- /dev/null
+++ b/spec/ruby/library/matrix/rect_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/rectangular'
+
+describe "Matrix#rect" do
+ it_behaves_like :matrix_rectangular, :rect
+end
diff --git a/spec/ruby/library/matrix/rectangular_spec.rb b/spec/ruby/library/matrix/rectangular_spec.rb
new file mode 100644
index 0000000000..a235fac640
--- /dev/null
+++ b/spec/ruby/library/matrix/rectangular_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/rectangular'
+
+describe "Matrix#rectangular" do
+ it_behaves_like :matrix_rectangular, :rectangular
+end
diff --git a/spec/ruby/library/matrix/regular_spec.rb b/spec/ruby/library/matrix/regular_spec.rb
new file mode 100644
index 0000000000..5dd4484ce4
--- /dev/null
+++ b/spec/ruby/library/matrix/regular_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#regular?" do
+
+ it "returns false for singular matrices" do
+ m = Matrix[ [1,2,3], [3,4,3], [0,0,0] ]
+ m.regular?.should be_false
+
+ m = Matrix[ [1,2,9], [3,4,9], [1,2,9] ]
+ m.regular?.should be_false
+ end
+
+ it "returns true if the Matrix is regular" do
+ Matrix[ [0,1], [1,0] ].regular?.should be_true
+ end
+
+ it "returns true for an empty 0x0 matrix" do
+ Matrix.empty(0,0).regular?.should be_true
+ end
+
+ it "raises an error for rectangular matrices" do
+ lambda {
+ Matrix[[1], [2], [3]].regular?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+
+ lambda {
+ Matrix.empty(3,0).regular?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+end
diff --git a/spec/ruby/library/matrix/round_spec.rb b/spec/ruby/library/matrix/round_spec.rb
new file mode 100644
index 0000000000..1dc29df890
--- /dev/null
+++ b/spec/ruby/library/matrix/round_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix#round" do
+ it "returns a matrix with all entries rounded" do
+ Matrix[ [1, 2.34], [5.67, 8] ].round.should == Matrix[ [1, 2], [6, 8] ]
+ Matrix[ [1, 2.34], [5.67, 8] ].round(1).should == Matrix[ [1, 2.3], [5.7, 8] ]
+ end
+
+ it "returns empty matrices on the same size if empty" do
+ Matrix.empty(0, 3).round.should == Matrix.empty(0, 3)
+ Matrix.empty(3, 0).round(42).should == Matrix.empty(3, 0)
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.ins.round.should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/row_size_spec.rb b/spec/ruby/library/matrix/row_size_spec.rb
new file mode 100644
index 0000000000..eb3aef2e2f
--- /dev/null
+++ b/spec/ruby/library/matrix/row_size_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#row_size" do
+ it "returns the number rows" do
+ Matrix[ [1,2], [3, 4], [5, 6] ].row_size.should == 3
+ end
+
+ it "returns the number rows even for some empty matrices" do
+ Matrix[ [], [], [] ].row_size.should == 3
+ end
+
+end
diff --git a/spec/ruby/library/matrix/row_spec.rb b/spec/ruby/library/matrix/row_spec.rb
new file mode 100644
index 0000000000..1cf2c6ae2c
--- /dev/null
+++ b/spec/ruby/library/matrix/row_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#row" do
+ before :all do
+ @m = Matrix[ [1, 2], [2, 3], [3, 4] ]
+ end
+
+ it "returns a Vector when called without a block" do
+ @m.row(0).should == Vector[1,2]
+ end
+
+ it "yields the elements of the row when called with a block" do
+ a = []
+ @m.row(0) {|x| a << x}
+ a.should == [1,2]
+ end
+
+ it "counts backwards for negative argument" do
+ @m.row(-1).should == Vector[3, 4]
+ end
+
+ it "returns self when called with a block" do
+ @m.row(0) { |x| x }.should equal(@m)
+ end
+
+ it "returns nil when out of bounds" do
+ @m.row(3).should == nil
+ @m.row(-4).should == nil
+ end
+
+ it "never yields when out of bounds" do
+ lambda { @m.row(3){ raise } }.should_not raise_error
+ lambda { @m.row(-4){ raise } }.should_not raise_error
+ end
+end
diff --git a/spec/ruby/library/matrix/row_vector_spec.rb b/spec/ruby/library/matrix/row_vector_spec.rb
new file mode 100644
index 0000000000..341437ee05
--- /dev/null
+++ b/spec/ruby/library/matrix/row_vector_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix.row_vector" do
+
+ it "returns a Matrix" do
+ Matrix.row_vector([]).should be_an_instance_of(Matrix)
+ end
+
+ it "returns a single-row Matrix with the specified values" do
+ Matrix.row_vector([1,2]).should == Matrix[ [1,2] ]
+ end
+
+ it "returns a 1x0 matrix when called with an empty Array" do
+ Matrix.row_vector([]).should == Matrix[ [] ]
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.row_vector([1]).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/row_vectors_spec.rb b/spec/ruby/library/matrix/row_vectors_spec.rb
new file mode 100644
index 0000000000..6f99c439a6
--- /dev/null
+++ b/spec/ruby/library/matrix/row_vectors_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#row_vectors" do
+
+ before :each do
+ @vectors = Matrix[ [1,2], [3,4] ].row_vectors
+ end
+
+ it "returns an Array" do
+ Matrix[ [1,2], [3,4] ].row_vectors.should be_an_instance_of(Array)
+ end
+
+ it "returns an Array of Vectors" do
+ @vectors.all? {|v| v.should be_an_instance_of(Vector)}
+ end
+
+ it "returns each row as a Vector" do
+ @vectors.should == [Vector[1,2], Vector[3,4]]
+ end
+
+ it "returns an empty Array for empty matrices" do
+ Matrix[].row_vectors.should == []
+ Matrix[ [] ].row_vectors.should == [ Vector[] ]
+ end
+end
diff --git a/spec/ruby/library/matrix/rows_spec.rb b/spec/ruby/library/matrix/rows_spec.rb
new file mode 100644
index 0000000000..41ba635775
--- /dev/null
+++ b/spec/ruby/library/matrix/rows_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix.rows" do
+ before :each do
+ @a = [1, 2]
+ @b = [3, 4]
+ @m = Matrix.rows([@a, @b])
+ end
+
+ it "returns a Matrix" do
+ @m.should be_kind_of(Matrix)
+ end
+
+ it "creates a matrix from argument rows" do
+ @m.row(0).to_a.should == @a
+ @m.row(1).to_a.should == @b
+ end
+
+ it "copies the original rows by default" do
+ @a << 3
+ @b << 6
+ @m.row(0).should_not equal(@a)
+ @m.row(1).should_not equal(@b)
+ end
+
+ it "references the original rows if copy is false" do
+ @m_ref = Matrix.rows([@a, @b], false)
+ @a << 3
+ @b << 6
+ @m_ref.row(0).to_a.should == @a
+ @m_ref.row(1).to_a.should == @b
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.rows([[0, 1], [0, 1]]).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/scalar/Fail_spec.rb b/spec/ruby/library/matrix/scalar/Fail_spec.rb
new file mode 100644
index 0000000000..9d8f9bd5e8
--- /dev/null
+++ b/spec/ruby/library/matrix/scalar/Fail_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::Scalar#Fail" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/matrix/scalar/Raise_spec.rb b/spec/ruby/library/matrix/scalar/Raise_spec.rb
new file mode 100644
index 0000000000..27e11c1f77
--- /dev/null
+++ b/spec/ruby/library/matrix/scalar/Raise_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::Scalar#Raise" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/matrix/scalar/divide_spec.rb b/spec/ruby/library/matrix/scalar/divide_spec.rb
new file mode 100644
index 0000000000..5d726943fe
--- /dev/null
+++ b/spec/ruby/library/matrix/scalar/divide_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::Scalar#/" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/matrix/scalar/exponent_spec.rb b/spec/ruby/library/matrix/scalar/exponent_spec.rb
new file mode 100644
index 0000000000..8e9ef52ff2
--- /dev/null
+++ b/spec/ruby/library/matrix/scalar/exponent_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::Scalar#**" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/matrix/scalar/included_spec.rb b/spec/ruby/library/matrix/scalar/included_spec.rb
new file mode 100644
index 0000000000..cb3beb2ecd
--- /dev/null
+++ b/spec/ruby/library/matrix/scalar/included_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::Scalar.included" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/matrix/scalar/initialize_spec.rb b/spec/ruby/library/matrix/scalar/initialize_spec.rb
new file mode 100644
index 0000000000..23145ad0de
--- /dev/null
+++ b/spec/ruby/library/matrix/scalar/initialize_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::Scalar#initialize" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/matrix/scalar/minus_spec.rb b/spec/ruby/library/matrix/scalar/minus_spec.rb
new file mode 100644
index 0000000000..c727ea1659
--- /dev/null
+++ b/spec/ruby/library/matrix/scalar/minus_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::Scalar#-" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/matrix/scalar/multiply_spec.rb b/spec/ruby/library/matrix/scalar/multiply_spec.rb
new file mode 100644
index 0000000000..1a2a83d83e
--- /dev/null
+++ b/spec/ruby/library/matrix/scalar/multiply_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::Scalar#*" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/matrix/scalar/plus_spec.rb b/spec/ruby/library/matrix/scalar/plus_spec.rb
new file mode 100644
index 0000000000..c94689a702
--- /dev/null
+++ b/spec/ruby/library/matrix/scalar/plus_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Matrix::Scalar#+" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/matrix/scalar_spec.rb b/spec/ruby/library/matrix/scalar_spec.rb
new file mode 100644
index 0000000000..7fdd64c9d9
--- /dev/null
+++ b/spec/ruby/library/matrix/scalar_spec.rb
@@ -0,0 +1,67 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix.scalar" do
+
+ before :each do
+ @side = 3
+ @value = 8
+ @a = Matrix.scalar(@side, @value)
+ end
+
+ it "returns a Matrix" do
+ @a.should be_kind_of(Matrix)
+ end
+
+ it "returns a n x n matrix" do
+ @a.row_size.should == @side
+ @a.column_size.should == @side
+ end
+
+ it "initializes diagonal to value" do
+ (0...@a.row_size).each do |i|
+ @a[i, i].should == @value
+ end
+ end
+
+ it "initializes all non-diagonal values to 0" do
+ (0...@a.row_size).each do |i|
+ (0...@a.column_size).each do |j|
+ if i != j
+ @a[i, j].should == 0
+ end
+ end
+ end
+ end
+
+ before :each do
+ @side = 3
+ @value = 8
+ @a = Matrix.scalar(@side, @value)
+ end
+
+ it "returns a Matrix" do
+ @a.should be_kind_of(Matrix)
+ end
+
+ it "returns a square matrix, where the first argument specifies the side of the square" do
+ @a.row_size.should == @side
+ @a.column_size.should == @side
+ end
+
+ it "puts the second argument in all diagonal values" do
+ (0...@a.row_size).each do |i|
+ @a[i, i].should == @value
+ end
+ end
+
+ it "fills all values not on the main diagonal with 0" do
+ (0...@a.row_size).each do |i|
+ (0...@a.column_size).each do |j|
+ if i != j
+ @a[i, j].should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/shared/collect.rb b/spec/ruby/library/matrix/shared/collect.rb
new file mode 100644
index 0000000000..852f7fd6cf
--- /dev/null
+++ b/spec/ruby/library/matrix/shared/collect.rb
@@ -0,0 +1,26 @@
+require_relative '../fixtures/classes'
+require 'matrix'
+
+describe :collect, shared: true do
+ before :all do
+ @m = Matrix[ [1, 2], [1, 2] ]
+ end
+
+ it "returns an instance of Matrix" do
+ @m.send(@method){|n| n * 2 }.should be_kind_of(Matrix)
+ end
+
+ it "returns a Matrix where each element is the result of the block" do
+ @m.send(@method) { |n| n * 2 }.should == Matrix[ [2, 4], [2, 4] ]
+ end
+
+ it "returns an enumerator if no block is given" do
+ @m.send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.ins.send(@method){1}.should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/shared/conjugate.rb b/spec/ruby/library/matrix/shared/conjugate.rb
new file mode 100644
index 0000000000..d87658f855
--- /dev/null
+++ b/spec/ruby/library/matrix/shared/conjugate.rb
@@ -0,0 +1,20 @@
+require_relative '../fixtures/classes'
+require 'matrix'
+
+describe :matrix_conjugate, shared: true do
+ it "returns a matrix with all entries 'conjugated'" do
+ Matrix[ [1, 2], [3, 4] ].send(@method).should == Matrix[ [1, 2], [3, 4] ]
+ Matrix[ [1.9, Complex(1,1)], [3, 4] ].send(@method).should == Matrix[ [1.9, Complex(1,-1)], [3, 4] ]
+ end
+
+ it "returns empty matrices on the same size if empty" do
+ Matrix.empty(0, 3).send(@method).should == Matrix.empty(0, 3)
+ Matrix.empty(3, 0).send(@method).should == Matrix.empty(3, 0)
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.ins.send(@method).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/shared/determinant.rb b/spec/ruby/library/matrix/shared/determinant.rb
new file mode 100644
index 0000000000..47a58c62a6
--- /dev/null
+++ b/spec/ruby/library/matrix/shared/determinant.rb
@@ -0,0 +1,38 @@
+require 'matrix'
+
+describe :determinant, shared: true do
+ it "returns the determinant of a square Matrix" do
+ m = Matrix[ [7,6], [3,9] ]
+ m.send(@method).should == 45
+
+ m = Matrix[ [9, 8], [6,5] ]
+ m.send(@method).should == -3
+
+ m = Matrix[ [9,8,3], [4,20,5], [1,1,1] ]
+ m.send(@method).should == 95
+ end
+
+ it "returns the determinant of a single-element Matrix" do
+ m = Matrix[ [2] ]
+ m.send(@method).should == 2
+ end
+
+ it "returns 1 for an empty Matrix" do
+ m = Matrix[ ]
+ m.send(@method).should == 1
+ end
+
+ it "returns the determinant even for Matrices containing 0 as first entry" do
+ Matrix[[0,1],[1,0]].send(@method).should == -1
+ end
+
+ it "raises an error for rectangular matrices" do
+ lambda {
+ Matrix[[1], [2], [3]].send(@method)
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+
+ lambda {
+ Matrix.empty(3,0).send(@method)
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+end
diff --git a/spec/ruby/library/matrix/shared/equal_value.rb b/spec/ruby/library/matrix/shared/equal_value.rb
new file mode 100644
index 0000000000..2b2311d49e
--- /dev/null
+++ b/spec/ruby/library/matrix/shared/equal_value.rb
@@ -0,0 +1,33 @@
+require_relative '../fixtures/classes'
+require 'matrix'
+
+describe :equal, shared: true do
+ before do
+ @matrix = Matrix[ [1, 2, 3, 4, 5], [2, 3, 4, 5, 6] ]
+ end
+
+ it "returns true for self" do
+ @matrix.send(@method, @matrix).should be_true
+ end
+
+ it "returns true for equal matrices" do
+ @matrix.send(@method, Matrix[ [1, 2, 3, 4, 5], [2, 3, 4, 5, 6] ]).should be_true
+ end
+
+ it "returns false for different matrices" do
+ @matrix.send(@method, Matrix[ [42, 2, 3, 4, 5], [2, 3, 4, 5, 6] ]).should be_false
+ @matrix.send(@method, Matrix[ [1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7] ]).should be_false
+ @matrix.send(@method, Matrix[ [1, 2, 3], [2, 3, 4] ]).should be_false
+ end
+
+ it "returns false for different empty matrices" do
+ Matrix.empty(42, 0).send(@method, Matrix.empty(6, 0)).should be_false
+ Matrix.empty(0, 42).send(@method, Matrix.empty(0, 6)).should be_false
+ Matrix.empty(0, 0).send(@method, Matrix.empty(6, 0)).should be_false
+ Matrix.empty(0, 0).send(@method, Matrix.empty(0, 6)).should be_false
+ end
+
+ it "doesn't distinguish on subclasses" do
+ MatrixSub.ins.send(@method, Matrix.I(2)).should be_true
+ end
+end
diff --git a/spec/ruby/library/matrix/shared/identity.rb b/spec/ruby/library/matrix/shared/identity.rb
new file mode 100644
index 0000000000..114f86e7b0
--- /dev/null
+++ b/spec/ruby/library/matrix/shared/identity.rb
@@ -0,0 +1,19 @@
+require_relative '../fixtures/classes'
+require 'matrix'
+
+describe :matrix_identity, shared: true do
+ it "returns a Matrix" do
+ Matrix.send(@method, 2).should be_kind_of(Matrix)
+ end
+
+ it "returns a n x n identity matrix" do
+ Matrix.send(@method, 3).should == Matrix.scalar(3, 1)
+ Matrix.send(@method, 100).should == Matrix.scalar(100, 1)
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.send(@method, 2).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/shared/imaginary.rb b/spec/ruby/library/matrix/shared/imaginary.rb
new file mode 100644
index 0000000000..d28ecc69f1
--- /dev/null
+++ b/spec/ruby/library/matrix/shared/imaginary.rb
@@ -0,0 +1,20 @@
+require_relative '../fixtures/classes'
+require 'matrix'
+
+describe :matrix_imaginary, shared: true do
+ it "returns a matrix with the imaginary part of the elements of the receiver" do
+ Matrix[ [1, 2], [3, 4] ].send(@method).should == Matrix[ [0, 0], [0, 0] ]
+ Matrix[ [1.9, Complex(1,1)], [Complex(-2,0.42), 4] ].send(@method).should == Matrix[ [0, 1], [0.42, 0] ]
+ end
+
+ it "returns empty matrices on the same size if empty" do
+ Matrix.empty(0, 3).send(@method).should == Matrix.empty(0, 3)
+ Matrix.empty(3, 0).send(@method).should == Matrix.empty(3, 0)
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.ins.send(@method).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/shared/inverse.rb b/spec/ruby/library/matrix/shared/inverse.rb
new file mode 100644
index 0000000000..e1e5ae5758
--- /dev/null
+++ b/spec/ruby/library/matrix/shared/inverse.rb
@@ -0,0 +1,38 @@
+require_relative '../fixtures/classes'
+require 'matrix'
+
+describe :inverse, shared: true do
+
+ it "returns a Matrix" do
+ Matrix[ [1,2], [2,1] ].send(@method).should be_an_instance_of(Matrix)
+ end
+
+ it "returns the inverse of the Matrix" do
+ Matrix[
+ [1, 3, 3], [1, 4, 3], [1, 3, 4]
+ ].send(@method).should ==
+ Matrix[
+ [7, -3, -3], [-1, 1, 0], [-1, 0, 1]
+ ]
+ end
+
+ it "returns the inverse of the Matrix (other case)" do
+ Matrix[
+ [1, 2, 3], [0, 1, 4], [5, 6, 0]
+ ].send(@method).should be_close_to_matrix([
+ [-24, 18, 5], [20, -15, -4], [-5, 4, 1]
+ ])
+ end
+
+ it "raises a ErrDimensionMismatch if the Matrix is not square" do
+ lambda{
+ Matrix[ [1,2,3], [1,2,3] ].send(@method)
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.ins.send(@method).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/shared/rectangular.rb b/spec/ruby/library/matrix/shared/rectangular.rb
new file mode 100644
index 0000000000..3d9a0dfe8a
--- /dev/null
+++ b/spec/ruby/library/matrix/shared/rectangular.rb
@@ -0,0 +1,18 @@
+require_relative '../fixtures/classes'
+require 'matrix'
+
+describe :matrix_rectangular, shared: true do
+ it "returns [receiver.real, receiver.imag]" do
+ m = Matrix[ [1.2, Complex(1,2)], [Complex(-2,0.42), 4] ]
+ m.send(@method).should == [m.real, m.imag]
+
+ m = Matrix.empty(3, 0)
+ m.send(@method).should == [m.real, m.imag]
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns instances of that subclass" do
+ MatrixSub.ins.send(@method).each{|m| m.should be_an_instance_of(MatrixSub) }
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/shared/trace.rb b/spec/ruby/library/matrix/shared/trace.rb
new file mode 100644
index 0000000000..2a42839f5d
--- /dev/null
+++ b/spec/ruby/library/matrix/shared/trace.rb
@@ -0,0 +1,12 @@
+require 'matrix'
+
+describe :trace, shared: true do
+ it "returns the sum of diagonal elements in a square Matrix" do
+ Matrix[[7,6], [3,9]].trace.should == 16
+ end
+
+ it "returns the sum of diagonal elements in a rectangular Matrix" do
+ lambda{ Matrix[[1,2,3], [4,5,6]].trace}.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+
+end
diff --git a/spec/ruby/library/matrix/shared/transpose.rb b/spec/ruby/library/matrix/shared/transpose.rb
new file mode 100644
index 0000000000..89b1d025be
--- /dev/null
+++ b/spec/ruby/library/matrix/shared/transpose.rb
@@ -0,0 +1,19 @@
+require_relative '../fixtures/classes'
+require 'matrix'
+
+describe :matrix_transpose, shared: true do
+ it "returns a transposed matrix" do
+ Matrix[[1, 2], [3, 4], [5, 6]].send(@method).should == Matrix[[1, 3, 5], [2, 4, 6]]
+ end
+
+ it "can transpose empty matrices" do
+ m = Matrix[[], [], []]
+ m.send(@method).send(@method).should == m
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.ins.send(@method).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/singular_spec.rb b/spec/ruby/library/matrix/singular_spec.rb
new file mode 100644
index 0000000000..362f4cbb35
--- /dev/null
+++ b/spec/ruby/library/matrix/singular_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#singular?" do
+ it "returns true for singular matrices" do
+ m = Matrix[ [1,2,3], [3,4,3], [0,0,0] ]
+ m.singular?.should be_true
+
+ m = Matrix[ [1,2,9], [3,4,9], [1,2,9] ]
+ m.singular?.should be_true
+ end
+
+ it "returns false if the Matrix is regular" do
+ Matrix[ [0,1], [1,0] ].singular?.should be_false
+ end
+
+ it "returns false for an empty 0x0 matrix" do
+ Matrix.empty(0,0).singular?.should be_false
+ end
+
+ it "raises an error for rectangular matrices" do
+ lambda {
+ Matrix[[1], [2], [3]].singular?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+
+ lambda {
+ Matrix.empty(3,0).singular?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+
+end
diff --git a/spec/ruby/library/matrix/spec_helper.rb b/spec/ruby/library/matrix/spec_helper.rb
new file mode 100644
index 0000000000..d44612981a
--- /dev/null
+++ b/spec/ruby/library/matrix/spec_helper.rb
@@ -0,0 +1,35 @@
+class BeCloseToMatrixMatcher
+ def initialize(expected, tolerance = TOLERANCE)
+ SpecExpectation.matcher! rescue "Used with the balance_should_and_match branch of mspec"
+ @expected = Matrix[*expected]
+ @tolerance = tolerance
+ end
+
+ def matches?(actual)
+ @actual = actual
+ return false unless @actual.is_a? Matrix
+ return false unless @actual.row_size == @expected.row_size
+ @actual.row_size.times do |i|
+ a, e = @actual.row(i), @expected.row(i)
+ return false unless a.size == e.size
+ a.size.times do |j|
+ return false unless (a[j] - e[j]).abs < @tolerance
+ end
+ end
+ true
+ end
+
+ def failure_message
+ ["Expected #{@expected}", "to be within +/- #{@tolerance} of #{@actual}"]
+ end
+
+ def negative_failure_message
+ ["Expected #{@expected}", "not to be within +/- #{@tolerance} of #{@actual}"]
+ end
+end
+
+class Object
+ def be_close_to_matrix(expected, tolerance = TOLERANCE)
+ BeCloseToMatrixMatcher.new(expected, tolerance)
+ end
+end
diff --git a/spec/ruby/library/matrix/square_spec.rb b/spec/ruby/library/matrix/square_spec.rb
new file mode 100644
index 0000000000..25d2d1ad9c
--- /dev/null
+++ b/spec/ruby/library/matrix/square_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#square?" do
+
+ it "returns true when the Matrix is square" do
+ Matrix[ [1,2], [2,4] ].square?.should be_true
+ Matrix[ [100,3,5], [9.5, 4.9, 8], [2,0,77] ].square?.should be_true
+ end
+
+ it "returns true when the Matrix has only one element" do
+ Matrix[ [9] ].square?.should be_true
+ end
+
+ it "returns false when the Matrix is rectangular" do
+ Matrix[ [1, 2] ].square?.should be_false
+ end
+
+ it "returns false when the Matrix is rectangular" do
+ Matrix[ [1], [2] ].square?.should be_false
+ end
+
+ it "returns handles empty matrices" do
+ Matrix[].square?.should be_true
+ Matrix[[]].square?.should be_false
+ Matrix.columns([[]]).square?.should be_false
+ end
+end
diff --git a/spec/ruby/library/matrix/symmetric_spec.rb b/spec/ruby/library/matrix/symmetric_spec.rb
new file mode 100644
index 0000000000..7b7bce29fc
--- /dev/null
+++ b/spec/ruby/library/matrix/symmetric_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix.symmetric?" do
+ it "returns true for a symmetric Matrix" do
+ Matrix[[1, 2, Complex(0, 3)], [2, 4, 5], [Complex(0, 3), 5, 6]].symmetric?.should be_true
+ end
+
+ it "returns true for a 0x0 empty matrix" do
+ Matrix.empty.symmetric?.should be_true
+ end
+
+ it "returns false for an asymmetric Matrix" do
+ Matrix[[1, 2],[-2, 1]].symmetric?.should be_false
+ end
+
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ lambda {
+ rectangular_matrix.symmetric?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/t_spec.rb b/spec/ruby/library/matrix/t_spec.rb
new file mode 100644
index 0000000000..6f1a5178e0
--- /dev/null
+++ b/spec/ruby/library/matrix/t_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/transpose'
+
+describe "Matrix#transpose" do
+ it_behaves_like :matrix_transpose, :t
+end
diff --git a/spec/ruby/library/matrix/to_a_spec.rb b/spec/ruby/library/matrix/to_a_spec.rb
new file mode 100644
index 0000000000..b5d55c5d67
--- /dev/null
+++ b/spec/ruby/library/matrix/to_a_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#to_a" do
+ it "returns the array of arrays that describe the rows of the matrix" do
+ Matrix[].to_a.should == []
+ Matrix[[]].to_a.should == [[]]
+ Matrix[[1]].to_a.should == [[1]]
+ Matrix[[1, 2], [3, 4]].to_a.should == [[1, 2],[3, 4]]
+ end
+end
diff --git a/spec/ruby/library/matrix/to_s_spec.rb b/spec/ruby/library/matrix/to_s_spec.rb
new file mode 100644
index 0000000000..f529fe3bcd
--- /dev/null
+++ b/spec/ruby/library/matrix/to_s_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix#to_s" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/matrix/tr_spec.rb b/spec/ruby/library/matrix/tr_spec.rb
new file mode 100644
index 0000000000..e17bd790d7
--- /dev/null
+++ b/spec/ruby/library/matrix/tr_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/trace'
+require 'matrix'
+
+describe "Matrix#tr" do
+ it_behaves_like :trace, :tr
+end
diff --git a/spec/ruby/library/matrix/trace_spec.rb b/spec/ruby/library/matrix/trace_spec.rb
new file mode 100644
index 0000000000..290e7cb1f7
--- /dev/null
+++ b/spec/ruby/library/matrix/trace_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/trace'
+require 'matrix'
+
+describe "Matrix#trace" do
+ it_behaves_like :trace, :trace
+end
diff --git a/spec/ruby/library/matrix/transpose_spec.rb b/spec/ruby/library/matrix/transpose_spec.rb
new file mode 100644
index 0000000000..79600dd439
--- /dev/null
+++ b/spec/ruby/library/matrix/transpose_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/transpose'
+
+describe "Matrix#transpose" do
+ it_behaves_like :matrix_transpose, :transpose
+end
diff --git a/spec/ruby/library/matrix/unit_spec.rb b/spec/ruby/library/matrix/unit_spec.rb
new file mode 100644
index 0000000000..6a41d729c7
--- /dev/null
+++ b/spec/ruby/library/matrix/unit_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/identity'
+
+describe "Matrix.unit" do
+ it_behaves_like :matrix_identity, :unit
+end
diff --git a/spec/ruby/library/matrix/unitary_spec.rb b/spec/ruby/library/matrix/unitary_spec.rb
new file mode 100644
index 0000000000..921ea3dda3
--- /dev/null
+++ b/spec/ruby/library/matrix/unitary_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix.unitary?" do
+ it "returns false for non unitary matrices" do
+ Matrix[[0, 1], [1, 2]].unitary?.should == false
+ Matrix[[0, Complex(0, 2)], [Complex(0, 2), 0]].unitary?.should == false
+ Matrix[[0, Complex(0, 1)], [Complex(0, -1), 0]].unitary?.should == false
+ Matrix[[1, 1, 0], [0, 1, 1], [1, 0, 1]].unitary?.should == false
+ end
+
+ it "returns true for unitary matrices" do
+ Matrix[[0, Complex(0, 1)], [Complex(0, 1), 0]].unitary?.should == true
+ end
+
+ it "raises an error for rectangular matrices" do
+ [
+ Matrix[[0], [0]],
+ Matrix[[0, 0]],
+ Matrix.empty(0, 2),
+ Matrix.empty(2, 0),
+ ].each do |rectangular_matrix|
+ lambda {
+ rectangular_matrix.unitary?
+ }.should raise_error(Matrix::ErrDimensionMismatch)
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/upper_triangular_spec.rb b/spec/ruby/library/matrix/upper_triangular_spec.rb
new file mode 100644
index 0000000000..2514294a80
--- /dev/null
+++ b/spec/ruby/library/matrix/upper_triangular_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require 'matrix'
+
+describe "Matrix.upper_triangular?" do
+ it "returns true for an upper triangular Matrix" do
+ Matrix[[1, 2, 3], [0, 2, 3], [0, 0, 3]].upper_triangular?.should be_true
+ Matrix.diagonal([1, 2, 3]).upper_triangular?.should be_true
+ Matrix[[1, 2], [0, 2], [0, 0], [0, 0]].upper_triangular?.should be_true
+ Matrix[[1, 2, 3, 4], [0, 2, 3, 4]].upper_triangular?.should be_true
+ end
+
+ it "returns false for a non upper triangular square Matrix" do
+ Matrix[[0, 0], [1, 0]].upper_triangular?.should be_false
+ Matrix[[1, 2, 3], [1, 2, 3], [1, 2, 3]].upper_triangular?.should be_false
+ Matrix[[0, 0], [0, 0], [0, 0], [0, 1]].upper_triangular?.should be_false
+ Matrix[[0, 0, 0, 0], [1, 0, 0, 0]].upper_triangular?.should be_false
+ end
+
+ it "returns true for an empty matrix" do
+ Matrix.empty(3,0).upper_triangular?.should be_true
+ Matrix.empty(0,3).upper_triangular?.should be_true
+ Matrix.empty(0,0).upper_triangular?.should be_true
+ end
+end
diff --git a/spec/ruby/library/matrix/vector/cross_product_spec.rb b/spec/ruby/library/matrix/vector/cross_product_spec.rb
new file mode 100644
index 0000000000..5194782edd
--- /dev/null
+++ b/spec/ruby/library/matrix/vector/cross_product_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Vector#cross_product" do
+ it "returns the cross product of a vector" do
+ Vector[1, 2, 3].cross_product(Vector[0, -4, 5]).should == Vector[22, -5, -4]
+ end
+
+ it "raises an error unless both vectors have dimension 3" do
+ lambda {
+ Vector[1, 2, 3].cross_product(Vector[0, -4])
+ }.should raise_error(Vector::ErrDimensionMismatch)
+ end
+end
diff --git a/spec/ruby/library/matrix/vector/each2_spec.rb b/spec/ruby/library/matrix/vector/each2_spec.rb
new file mode 100644
index 0000000000..844b903336
--- /dev/null
+++ b/spec/ruby/library/matrix/vector/each2_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Vector.each2" do
+ before :all do
+ @v = Vector[1, 2, 3]
+ @v2 = Vector[4, 5, 6]
+ end
+
+ it "requires one argument" do
+ lambda { @v.each2(@v2, @v2){} }.should raise_error(ArgumentError)
+ lambda { @v.each2(){} }.should raise_error(ArgumentError)
+ end
+
+ describe "given one argument" do
+ it "accepts an Array argument" do
+ a = []
+ @v.each2([7, 8, 9]){|x, y| a << x << y}
+ a.should == [1, 7, 2, 8, 3, 9]
+ end
+
+ it "raises a DimensionMismatch error if the Vector size is different" do
+ lambda { @v.each2(Vector[1,2]){} }.should raise_error(Vector::ErrDimensionMismatch)
+ lambda { @v.each2(Vector[1,2,3,4]){} }.should raise_error(Vector::ErrDimensionMismatch)
+ end
+
+ it "yields arguments in sequence" do
+ a = []
+ @v.each2(@v2){|first, second| a << [first, second]}
+ a.should == [[1, 4], [2, 5], [3, 6]]
+ end
+
+ it "yield arguments in pairs" do
+ a = []
+ @v.each2(@v2){|*pair| a << pair}
+ a.should == [[1, 4], [2, 5], [3, 6]]
+ end
+
+ it "returns self when given a block" do
+ @v.each2(@v2){}.should equal(@v)
+ end
+
+ it "returns an enumerator if no block given" do
+ enum = @v.each2(@v2)
+ enum.should be_an_instance_of(Enumerator)
+ enum.to_a.should == [[1, 4], [2, 5], [3, 6]]
+ end
+ end
+end
diff --git a/spec/ruby/library/matrix/vector/eql_spec.rb b/spec/ruby/library/matrix/vector/eql_spec.rb
new file mode 100644
index 0000000000..eb2451b550
--- /dev/null
+++ b/spec/ruby/library/matrix/vector/eql_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Vector#eql?" do
+ before do
+ @vector = Vector[1, 2, 3, 4, 5]
+ end
+
+ it "returns true for self" do
+ @vector.eql?(@vector).should be_true
+ end
+
+ it "returns false when there are a pair corresponding elements which are not equal in the sense of Kernel#eql?" do
+ @vector.eql?(Vector[1, 2, 3, 4, 5.0]).should be_false
+ end
+end
diff --git a/spec/ruby/library/matrix/vector/inner_product_spec.rb b/spec/ruby/library/matrix/vector/inner_product_spec.rb
new file mode 100644
index 0000000000..584226c450
--- /dev/null
+++ b/spec/ruby/library/matrix/vector/inner_product_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Vector#inner_product" do
+ it "returns the inner product of a vector" do
+ Vector[1, 2, 3].inner_product(Vector[0, -4, 5]).should == 7
+ end
+
+ it "returns 0 for empty vectors" do
+ Vector[].inner_product(Vector[]).should == 0
+ end
+
+ it "raises an error for mismatched vectors" do
+ lambda {
+ Vector[1, 2, 3].inner_product(Vector[0, -4])
+ }.should raise_error(Vector::ErrDimensionMismatch)
+ end
+
+ it "uses the conjugate of its argument" do
+ Vector[Complex(1,2)].inner_product(Vector[Complex(3,4)]).should == Complex(11, 2)
+ end
+end
diff --git a/spec/ruby/library/matrix/vector/normalize_spec.rb b/spec/ruby/library/matrix/vector/normalize_spec.rb
new file mode 100644
index 0000000000..4ff9597c82
--- /dev/null
+++ b/spec/ruby/library/matrix/vector/normalize_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+require 'matrix'
+
+describe "Vector#normalize" do
+ it "returns a normalized copy of the vector" do
+ x = 0.2672612419124244
+ Vector[1, 2, 3].normalize.should == Vector[x, x * 2, x * 3]
+ end
+
+ it "raises an error for zero vectors" do
+ lambda {
+ Vector[].normalize
+ }.should raise_error(Vector::ZeroVectorError)
+ lambda {
+ Vector[0, 0, 0].normalize
+ }.should raise_error(Vector::ZeroVectorError)
+ end
+end
diff --git a/spec/ruby/library/matrix/zero_spec.rb b/spec/ruby/library/matrix/zero_spec.rb
new file mode 100644
index 0000000000..643c57acba
--- /dev/null
+++ b/spec/ruby/library/matrix/zero_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require 'matrix'
+
+describe "Matrix.zero" do
+ it "returns an object of type Matrix" do
+ Matrix.zero(3).should be_kind_of(Matrix)
+ end
+
+ it "creates a n x n matrix" do
+ m3 = Matrix.zero(3)
+ m3.row_size.should == 3
+ m3.column_size.should == 3
+
+ m8 = Matrix.zero(8)
+ m8.row_size.should == 8
+ m8.column_size.should == 8
+ end
+
+ it "initializes all cells to 0" do
+ size = 10
+ m = Matrix.zero(size)
+
+ (0...size).each do |i|
+ (0...size).each do |j|
+ m[i, j].should == 0
+ end
+ end
+ end
+
+ describe "for a subclass of Matrix" do
+ it "returns an instance of that subclass" do
+ MatrixSub.zero(3).should be_an_instance_of(MatrixSub)
+ end
+ end
+end
+
+describe "Matrix.zero?" do
+ it "returns true for empty matrices" do
+ Matrix.empty.zero?.should == true
+ Matrix.empty(3,0).zero?.should == true
+ Matrix.empty(0,3).zero?.should == true
+ end
+
+ it "returns true for matrices with zero entries" do
+ Matrix.zero(2,3).zero?.should == true
+ end
+
+ it "returns false for matrices with non zero entries" do
+ Matrix[[1]].zero?.should == false
+ end
+end
diff --git a/spec/ruby/library/monitor/mon_initialize_spec.rb b/spec/ruby/library/monitor/mon_initialize_spec.rb
new file mode 100644
index 0000000000..e0fe6c2d97
--- /dev/null
+++ b/spec/ruby/library/monitor/mon_initialize_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require 'monitor'
+
+describe "MonitorMixin#mon_initialize" do
+ it "can be called in initialize_copy to get a new Mutex and used with synchronize" do
+ cls = Class.new do
+ include MonitorMixin
+
+ def initialize(*array)
+ mon_initialize
+ @array = array
+ end
+
+ def to_a
+ synchronize { @array.dup }
+ end
+
+ def initialize_copy(other)
+ mon_initialize
+
+ synchronize do
+ @array = other.to_a
+ end
+ end
+ end
+
+ instance = cls.new(1, 2, 3)
+ copy = instance.dup
+ copy.should_not equal(instance)
+ end
+end
diff --git a/spec/ruby/library/net/FTPError_spec.rb b/spec/ruby/library/net/FTPError_spec.rb
new file mode 100644
index 0000000000..0c31b65dcc
--- /dev/null
+++ b/spec/ruby/library/net/FTPError_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'net/ftp'
+
+describe "Net::FTPError" do
+ it "is an Exception" do
+ Net::FTPError.should < Exception
+ end
+end
diff --git a/spec/ruby/library/net/FTPPermError_spec.rb b/spec/ruby/library/net/FTPPermError_spec.rb
new file mode 100644
index 0000000000..b43e12c503
--- /dev/null
+++ b/spec/ruby/library/net/FTPPermError_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'net/ftp'
+
+describe "Net::FTPPermError" do
+ it "is an Exception" do
+ Net::FTPPermError.should < Exception
+ end
+
+ it "is a subclass of Net::FTPError" do
+ Net::FTPPermError.should < Net::FTPError
+ end
+end
diff --git a/spec/ruby/library/net/FTPProtoError_spec.rb b/spec/ruby/library/net/FTPProtoError_spec.rb
new file mode 100644
index 0000000000..e7abbc0dd8
--- /dev/null
+++ b/spec/ruby/library/net/FTPProtoError_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'net/ftp'
+
+describe "Net::FTPProtoError" do
+ it "is an Exception" do
+ Net::FTPProtoError.should < Exception
+ end
+
+ it "is a subclass of Net::FTPError" do
+ Net::FTPPermError.should < Net::FTPError
+ end
+end
diff --git a/spec/ruby/library/net/FTPReplyError_spec.rb b/spec/ruby/library/net/FTPReplyError_spec.rb
new file mode 100644
index 0000000000..fcc7501fc1
--- /dev/null
+++ b/spec/ruby/library/net/FTPReplyError_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'net/ftp'
+
+describe "Net::FTPReplyError" do
+ it "is an Exception" do
+ Net::FTPReplyError.should < Exception
+ end
+
+ it "is a subclass of Net::FTPError" do
+ Net::FTPPermError.should < Net::FTPError
+ end
+end
diff --git a/spec/ruby/library/net/FTPTempError_spec.rb b/spec/ruby/library/net/FTPTempError_spec.rb
new file mode 100644
index 0000000000..f4b045dfb5
--- /dev/null
+++ b/spec/ruby/library/net/FTPTempError_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'net/ftp'
+
+describe "Net::FTPTempError" do
+ it "is an Exception" do
+ Net::FTPTempError.should < Exception
+ end
+
+ it "is a subclass of Net::FTPError" do
+ Net::FTPPermError.should < Net::FTPError
+ end
+end
diff --git a/spec/ruby/library/net/ftp/abort_spec.rb b/spec/ruby/library/net/ftp/abort_spec.rb
new file mode 100644
index 0000000000..ff2144a3a0
--- /dev/null
+++ b/spec/ruby/library/net/ftp/abort_spec.rb
@@ -0,0 +1,62 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#abort" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the ABOR command to the server" do
+ lambda { @ftp.abort }.should_not raise_error
+ end
+
+ it "ignores the response" do
+ @ftp.abort
+ @ftp.last_response.should == "220 Dummy FTP Server ready!\n"
+ end
+
+ it "returns the full response" do
+ @ftp.abort.should == "226 Closing data connection. (ABOR)\n"
+ end
+
+ it "does not raise any error when the response code is 225" do
+ @server.should_receive(:abor).and_respond("225 Data connection open; no transfer in progress.")
+ lambda { @ftp.abort }.should_not raise_error
+ end
+
+ it "does not raise any error when the response code is 226" do
+ @server.should_receive(:abor).and_respond("226 Closing data connection.")
+ lambda { @ftp.abort }.should_not raise_error
+ end
+
+ it "raises a Net::FTPProtoError when the response code is 500" do
+ @server.should_receive(:abor).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.abort }.should raise_error(Net::FTPProtoError)
+ end
+
+ it "raises a Net::FTPProtoError when the response code is 501" do
+ @server.should_receive(:abor).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.abort }.should raise_error(Net::FTPProtoError)
+ end
+
+ it "raises a Net::FTPProtoError when the response code is 502" do
+ @server.should_receive(:abor).and_respond("502 Command not implemented.")
+ lambda { @ftp.abort }.should raise_error(Net::FTPProtoError)
+ end
+
+ it "raises a Net::FTPProtoError when the response code is 421" do
+ @server.should_receive(:abor).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.abort }.should raise_error(Net::FTPProtoError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/acct_spec.rb b/spec/ruby/library/net/ftp/acct_spec.rb
new file mode 100644
index 0000000000..3db51fca0f
--- /dev/null
+++ b/spec/ruby/library/net/ftp/acct_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#acct" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "writes the ACCT command to the server" do
+ @ftp.acct("my_account")
+ @ftp.last_response.should == "230 User 'my_account' logged in, proceed. (ACCT)\n"
+ end
+
+ it "returns nil" do
+ @ftp.acct("my_account").should == nil
+ end
+
+ it "does not raise any error when the response code is 230" do
+ @server.should_receive(:acct).and_respond("230 User logged in, proceed.")
+ lambda { @ftp.acct("my_account") }.should_not raise_error
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:acct).and_respond("530 Not logged in.")
+ lambda { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:acct).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:acct).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 503" do
+ @server.should_receive(:acct).and_respond("503 Bad sequence of commands.")
+ lambda { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:acct).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.acct("my_account") }.should raise_error(Net::FTPTempError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/binary_spec.rb b/spec/ruby/library/net/ftp/binary_spec.rb
new file mode 100644
index 0000000000..60e312a673
--- /dev/null
+++ b/spec/ruby/library/net/ftp/binary_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#binary" do
+ it "returns true when self is in binary mode" do
+ ftp = Net::FTP.new
+ ftp.binary.should be_true
+
+ ftp.binary = false
+ ftp.binary.should be_false
+ end
+end
+
+describe "Net::FTP#binary=" do
+ it "sets self to binary mode when passed true" do
+ ftp = Net::FTP.new
+
+ ftp.binary = true
+ ftp.binary.should be_true
+
+ ftp.binary = false
+ ftp.binary.should be_false
+ end
+end
diff --git a/spec/ruby/library/net/ftp/chdir_spec.rb b/spec/ruby/library/net/ftp/chdir_spec.rb
new file mode 100644
index 0000000000..1f558c47e8
--- /dev/null
+++ b/spec/ruby/library/net/ftp/chdir_spec.rb
@@ -0,0 +1,99 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#chdir" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ describe "when switching to the parent directory" do
+ it "sends the 'CDUP' command to the server" do
+ @ftp.chdir("..")
+ @ftp.last_response.should == "200 Command okay. (CDUP)\n"
+ end
+
+ it "returns nil" do
+ @ftp.chdir("..").should be_nil
+ end
+
+ it "does not raise a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:cdup).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.chdir("..") }.should_not raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:cdup).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.chdir("..") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:cdup).and_respond("502 Command not implemented.")
+ lambda { @ftp.chdir("..") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:cdup).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.chdir("..") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:cdup).and_respond("530 Not logged in.")
+ lambda { @ftp.chdir("..") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:cdup).and_respond("550 Requested action not taken.")
+ lambda { @ftp.chdir("..") }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ it "writes the 'CWD' command with the passed directory to the socket" do
+ @ftp.chdir("test")
+ @ftp.last_response.should == "200 Command okay. (CWD test)\n"
+ end
+
+ it "returns nil" do
+ @ftp.chdir("test").should be_nil
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:cwd).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.chdir("test") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:cwd).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.chdir("test") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:cwd).and_respond("502 Command not implemented.")
+ lambda { @ftp.chdir("test") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:cwd).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.chdir("test") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:cwd).and_respond("530 Not logged in.")
+ lambda { @ftp.chdir("test") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:cwd).and_respond("550 Requested action not taken.")
+ lambda { @ftp.chdir("test") }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/close_spec.rb b/spec/ruby/library/net/ftp/close_spec.rb
new file mode 100644
index 0000000000..95c72b29ed
--- /dev/null
+++ b/spec/ruby/library/net/ftp/close_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#close" do
+ before :each do
+ @socket = mock("Socket")
+ @socket.stub!(:closed?).and_return(false)
+ @socket.stub!(:read_timeout).and_return(60)
+ @socket.stub!(:read_timeout=).and_return(3)
+
+ @ftp = Net::FTP.new
+ @ftp.instance_variable_set(:@sock, @socket)
+ end
+
+ it "closes the socket" do
+ @socket.should_receive(:close)
+ @ftp.close.should be_nil
+ end
+
+ it "does not try to close the socket if it has already been closed" do
+ @socket.should_receive(:closed?).and_return(true)
+ @socket.should_not_receive(:close)
+ @ftp.close.should be_nil
+ end
+
+ it "does not try to close the socket if it is nil" do
+ @ftp.instance_variable_set(:@sock, nil)
+ @ftp.close.should be_nil
+ end
+end
diff --git a/spec/ruby/library/net/ftp/closed_spec.rb b/spec/ruby/library/net/ftp/closed_spec.rb
new file mode 100644
index 0000000000..1f3e69b0c1
--- /dev/null
+++ b/spec/ruby/library/net/ftp/closed_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#closed?" do
+ before :each do
+ @socket = mock("Socket")
+
+ @ftp = Net::FTP.new
+ @ftp.instance_variable_set(:@sock, @socket)
+ end
+
+ it "returns true when the socket is closed" do
+ @socket.should_receive(:closed?).and_return(true)
+ @ftp.closed?.should be_true
+ end
+
+ it "returns true when the socket is nil" do
+ @ftp.instance_variable_set(:@sock, nil)
+ @ftp.closed?.should be_true
+ end
+end
diff --git a/spec/ruby/library/net/ftp/connect_spec.rb b/spec/ruby/library/net/ftp/connect_spec.rb
new file mode 100644
index 0000000000..981751233e
--- /dev/null
+++ b/spec/ruby/library/net/ftp/connect_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+# TODO: Add specs for using the SOCKSSocket
+describe "Net::FTP#connect" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ end
+
+ after :each do
+ @server.connect_message = nil
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "tries to connect to the FTP Server on the given host and port" do
+ lambda { @ftp.connect(@server.hostname, @server.server_port) }.should_not raise_error
+ end
+
+ it "returns nil" do
+ @ftp.connect(@server.hostname, @server.server_port).should be_nil
+ end
+
+ it "prints a small debug line when in debug mode" do
+ @ftp.debug_mode = true
+ lambda { @ftp.connect(@server.hostname, @server.server_port) }.should output(/#{"connect: "}#{@server.hostname}#{", "}#{@server.server_port}#{"\\nget: 220 Dummy FTP Server ready!"}/)
+ @ftp.debug_mode = false
+ end
+
+ it "does not raise any error when the response code is 220" do
+ @server.connect_message = "220 Dummy FTP Server ready!"
+ lambda { @ftp.connect(@server.hostname, @server.server_port) }.should_not raise_error
+ end
+
+ it "raises a Net::FTPReplyError when the response code is 120" do
+ @server.connect_message = "120 Service ready in nnn minutes."
+ lambda { @ftp.connect(@server.hostname, @server.server_port) }.should raise_error(Net::FTPReplyError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.connect_message = "421 Service not available, closing control connection."
+ lambda { @ftp.connect(@server.hostname, @server.server_port) }.should raise_error(Net::FTPTempError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/debug_mode_spec.rb b/spec/ruby/library/net/ftp/debug_mode_spec.rb
new file mode 100644
index 0000000000..f49da55ed1
--- /dev/null
+++ b/spec/ruby/library/net/ftp/debug_mode_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#debug_mode" do
+ it "returns true when self is in debug mode" do
+ ftp = Net::FTP.new
+ ftp.debug_mode.should be_false
+
+ ftp.debug_mode = true
+ ftp.debug_mode.should be_true
+ end
+end
+
+describe "Net::FTP#debug_mode=" do
+ it "sets self into debug mode when passed true" do
+ ftp = Net::FTP.new
+ ftp.debug_mode = true
+ ftp.debug_mode.should be_true
+
+ ftp.debug_mode = false
+ ftp.debug_mode.should be_false
+ end
+end
diff --git a/spec/ruby/library/net/ftp/default_passive_spec.rb b/spec/ruby/library/net/ftp/default_passive_spec.rb
new file mode 100644
index 0000000000..30eb8f9da2
--- /dev/null
+++ b/spec/ruby/library/net/ftp/default_passive_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#default_passive" do
+ it "is true by default" do
+ ruby_exe(fixture(__FILE__, "default_passive.rb")).should == "true\ntrue\n"
+ end
+end
diff --git a/spec/ruby/library/net/ftp/delete_spec.rb b/spec/ruby/library/net/ftp/delete_spec.rb
new file mode 100644
index 0000000000..fa0eddb312
--- /dev/null
+++ b/spec/ruby/library/net/ftp/delete_spec.rb
@@ -0,0 +1,59 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#delete" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the DELE command with the passed filename to the server" do
+ @ftp.delete("test.file")
+ @ftp.last_response.should == "250 Requested file action okay, completed. (DELE test.file)\n"
+ end
+
+ it "raises a Net::FTPTempError when the response code is 450" do
+ @server.should_receive(:dele).and_respond("450 Requested file action not taken.")
+ lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:dele).and_respond("550 Requested action not taken.")
+ lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:dele).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:dele).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:dele).and_respond("502 Command not implemented.")
+ lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:dele).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:dele).and_respond("530 Not logged in.")
+ lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/dir_spec.rb b/spec/ruby/library/net/ftp/dir_spec.rb
new file mode 100644
index 0000000000..47ac7b8d9b
--- /dev/null
+++ b/spec/ruby/library/net/ftp/dir_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/list'
+
+describe "Net::FTP#dir" do
+ it_behaves_like :net_ftp_list, :dir
+end
diff --git a/spec/ruby/library/net/ftp/fixtures/default_passive.rb b/spec/ruby/library/net/ftp/fixtures/default_passive.rb
new file mode 100644
index 0000000000..b6995d6f34
--- /dev/null
+++ b/spec/ruby/library/net/ftp/fixtures/default_passive.rb
@@ -0,0 +1,3 @@
+require "net/ftp"
+puts Net::FTP.default_passive
+puts Net::FTP.new.passive
diff --git a/spec/ruby/library/net/ftp/fixtures/passive.rb b/spec/ruby/library/net/ftp/fixtures/passive.rb
new file mode 100644
index 0000000000..6b5cde82df
--- /dev/null
+++ b/spec/ruby/library/net/ftp/fixtures/passive.rb
@@ -0,0 +1,2 @@
+require "net/ftp"
+print Net::FTP.new.passive
diff --git a/spec/ruby/library/net/ftp/fixtures/putbinaryfile b/spec/ruby/library/net/ftp/fixtures/putbinaryfile
new file mode 100644
index 0000000000..f3130c6e43
--- /dev/null
+++ b/spec/ruby/library/net/ftp/fixtures/putbinaryfile
@@ -0,0 +1,3 @@
+This is an example file
+which is going to be transmitted
+using #putbinaryfile.
diff --git a/spec/ruby/library/net/ftp/fixtures/puttextfile b/spec/ruby/library/net/ftp/fixtures/puttextfile
new file mode 100644
index 0000000000..b4f3b2b62d
--- /dev/null
+++ b/spec/ruby/library/net/ftp/fixtures/puttextfile
@@ -0,0 +1,3 @@
+This is an example file
+which is going to be transmitted
+using #puttextfile.
diff --git a/spec/ruby/library/net/ftp/fixtures/server.rb b/spec/ruby/library/net/ftp/fixtures/server.rb
new file mode 100644
index 0000000000..65339cfaf9
--- /dev/null
+++ b/spec/ruby/library/net/ftp/fixtures/server.rb
@@ -0,0 +1,277 @@
+module NetFTPSpecs
+ class DummyFTP
+ attr_accessor :connect_message
+ attr_reader :login_user, :login_pass, :login_acct
+
+ # hostname or IP address
+ attr_reader :hostname
+ # port number
+ attr_reader :server_port
+
+ def initialize
+ @hostname = "localhost"
+ @server = TCPServer.new(@hostname, 0)
+ @server_port = @server.addr[1]
+
+ @handlers = {}
+ @commands = []
+ @connect_message = nil
+ end
+
+ def serve_once
+ @thread = Thread.new do
+ @socket = @server.accept
+ @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 1)
+ begin
+ handle_request
+ ensure
+ @socket.close
+ end
+ end
+ end
+
+ def handle_request
+ # Send out the welcome message.
+ response @connect_message || "220 Dummy FTP Server ready!"
+
+ begin
+ while command = @socket.gets
+ command, argument = command.chomp.split(" ", 2)
+
+ if command == "QUIT"
+ self.response("221 OK, bye")
+ break
+ elsif proc_handler = @handlers[command.downcase.to_sym]
+ if argument.nil?
+ proc_handler.call(self)
+ else
+ proc_handler.call(self, argument)
+ end
+ else
+ if argument.nil?
+ self.send(command.downcase.to_sym)
+ else
+ self.send(command.downcase.to_sym, argument)
+ end
+ end
+ end
+ rescue => e
+ self.error_response("Exception: #{e} #{e.backtrace.inspect}")
+ end
+ end
+
+ def error_response(text)
+ self.response("451 #{text}")
+ end
+
+ def response(text)
+ @socket.puts(text) unless @socket.closed?
+ end
+
+ def stop
+ @datasocket.close unless @datasocket.nil? || @datasocket.closed?
+ @server.close
+ @thread.join
+ end
+
+
+ ##
+ def handle(sym, &block)
+ @handlers[sym] = block
+ end
+
+ def should_receive(method)
+ @handler_for = method
+ self
+ end
+
+ def and_respond(text)
+ @handlers[@handler_for] = lambda { |s, *args| s.response(text) }
+ end
+
+ ##
+ # FTP methods
+ ##
+
+ def abor
+ self.response("226 Closing data connection. (ABOR)")
+ end
+
+ def acct(account)
+ @login_acct = account
+ self.response("230 User '#{account}' logged in, proceed. (ACCT)")
+ end
+
+ def cdup
+ self.response("200 Command okay. (CDUP)")
+ end
+
+ def cwd(dir)
+ self.response("200 Command okay. (CWD #{dir})")
+ end
+
+ def dele(file)
+ self.response("250 Requested file action okay, completed. (DELE #{file})")
+ end
+
+ def eprt(arg)
+ _, _, host, port = arg.split("|")
+
+ @datasocket = TCPSocket.new(host, port)
+ self.response("200 port opened")
+ end
+
+ def help(param = :default)
+ if param == :default
+ self.response("211 System status, or system help reply. (HELP)")
+ else
+ self.response("211 System status, or system help reply. (HELP #{param})")
+ end
+ end
+
+ def list(folder)
+ self.response("150 opening ASCII connection for file list")
+ @datasocket.puts("-rw-r--r-- 1 spec staff 507 17 Jul 18:41 last_response_code.rb")
+ @datasocket.puts("-rw-r--r-- 1 spec staff 50 17 Jul 18:41 list.rb")
+ @datasocket.puts("-rw-r--r-- 1 spec staff 48 17 Jul 18:41 pwd.rb")
+ @datasocket.close()
+ self.response("226 transfer complete (LIST #{folder})")
+ end
+
+ def mdtm(filename)
+ self.response("213 19980705132316")
+ end
+
+ def mkd(foldername)
+ self.response(%Q{257 "#{foldername.gsub('"', '""')}" created.})
+ end
+
+ def nlst(folder = nil)
+ self.response("150 opening ASCII connection for file list")
+ @datasocket.puts("last_response_code.rb")
+ @datasocket.puts("list.rb")
+ @datasocket.puts("pwd.rb")
+ @datasocket.close()
+ self.response("226 transfer complete (NLST#{folder ? " #{folder}" : ""})")
+ end
+
+ def noop
+ self.response("200 Command okay. (NOOP)")
+ end
+
+ def pass(password)
+ @login_pass = password
+ self.response("230 User logged in, proceed. (PASS #{password})")
+ end
+
+ def port(arg)
+ nums = arg.split(",")
+
+ if nums[0] == "::1"
+ # IPv6
+ port = nums[1].to_i * 256 + nums[2].to_i
+ host = nums[0]
+ else
+ # IPv4
+ port = nums[4].to_i * 256 + nums[5].to_i
+ host = nums[0..3].join(".")
+ end
+
+ @datasocket = TCPSocket.new(host, port)
+ self.response("200 port opened")
+ end
+
+ def pwd
+ self.response('257 "/some/dir/" - current directory')
+ end
+
+ def retr(file)
+ self.response("125 Data transfer starting")
+ if @restart_at && @restart_at == 20
+ @datasocket.puts("of the file named '#{file}'.")
+ @restart_at = nil
+ else
+ @datasocket.puts("This is the content")
+ @datasocket.puts("of the file named '#{file}'.")
+ end
+ @datasocket.close()
+ self.response("226 Closing data connection. (RETR #{file})")
+ end
+
+ def rest(at_bytes)
+ @restart_at = at_bytes.to_i
+ self.response("350 Requested file action pending further information. (REST)")
+ end
+
+ def rmd(folder)
+ self.response("250 Requested file action okay, completed. (RMD #{folder})")
+ end
+
+ def rnfr(from)
+ @rename_from = from
+ self.response("350 Requested file action pending further information.")
+ end
+
+ def rnto(to)
+ self.response("250 Requested file action okay, completed. (Renamed #{@rename_from} to #{to})")
+ @rename_from = nil
+ end
+
+ def site(param)
+ self.response("200 Command okay. (SITE #{param})")
+ end
+
+ def size(filename)
+ if filename == "binary"
+ self.response("213 24")
+ else
+ self.response("213 1024")
+ end
+ end
+
+ def stat(param = :default)
+ if param == :default
+ self.response("211 System status, or system help reply. (STAT)")
+ else
+ self.response("211 System status, or system help reply. (STAT #{param})")
+ end
+ end
+
+ def stor(file)
+ tmp_file = tmp("#{file}file", false)
+
+ self.response("125 Data transfer starting.")
+
+ mode = @restart_at ? "a" : "w"
+
+ File.open(tmp_file, mode + "b") do |f|
+ loop do
+ data = @datasocket.recv(1024)
+ break if !data || data.empty?
+ f << data
+ end
+ end
+
+ #@datasocket.close()
+ self.response("200 OK, Data received. (STOR #{file})")
+ end
+
+ def appe(file)
+ @restart_at = true
+ stor(file)
+ end
+
+ def syst
+ self.response("215 FTP Dummy Server (SYST)")
+ end
+
+ def type(type)
+ self.response("200 TYPE switched to #{type}")
+ end
+
+ def user(name)
+ @login_user = name
+ self.response("230 User logged in, proceed. (USER #{name})")
+ end
+ end
+end
diff --git a/spec/ruby/library/net/ftp/get_spec.rb b/spec/ruby/library/net/ftp/get_spec.rb
new file mode 100644
index 0000000000..c4672b55a5
--- /dev/null
+++ b/spec/ruby/library/net/ftp/get_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/gettextfile'
+require_relative 'shared/getbinaryfile'
+
+describe "Net::FTP#get (binary mode)" do
+ before :each do
+ @binary_mode = true
+ end
+
+ it_behaves_like :net_ftp_getbinaryfile, :get
+end
+
+describe "Net::FTP#get (text mode)" do
+ before :each do
+ @binary_mode = false
+ end
+
+ it_behaves_like :net_ftp_gettextfile, :get
+end
diff --git a/spec/ruby/library/net/ftp/getbinaryfile_spec.rb b/spec/ruby/library/net/ftp/getbinaryfile_spec.rb
new file mode 100644
index 0000000000..155851b53c
--- /dev/null
+++ b/spec/ruby/library/net/ftp/getbinaryfile_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/getbinaryfile'
+
+describe "Net::FTP#getbinaryfile" do
+ it_behaves_like :net_ftp_getbinaryfile, :getbinaryfile
+end
diff --git a/spec/ruby/library/net/ftp/getdir_spec.rb b/spec/ruby/library/net/ftp/getdir_spec.rb
new file mode 100644
index 0000000000..eea35ac130
--- /dev/null
+++ b/spec/ruby/library/net/ftp/getdir_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'shared/pwd'
+
+describe "Net::FTP#getdir" do
+ it_behaves_like :net_ftp_pwd, :getdir
+end
diff --git a/spec/ruby/library/net/ftp/gettextfile_spec.rb b/spec/ruby/library/net/ftp/gettextfile_spec.rb
new file mode 100644
index 0000000000..79395ae009
--- /dev/null
+++ b/spec/ruby/library/net/ftp/gettextfile_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/gettextfile'
+
+describe "Net::FTP#gettextfile" do
+ it_behaves_like :net_ftp_gettextfile, :gettextfile
+end
diff --git a/spec/ruby/library/net/ftp/help_spec.rb b/spec/ruby/library/net/ftp/help_spec.rb
new file mode 100644
index 0000000000..c7a089a588
--- /dev/null
+++ b/spec/ruby/library/net/ftp/help_spec.rb
@@ -0,0 +1,66 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#help" do
+ def with_connection
+ yield
+ end
+
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "writes the HELP command to the server" do
+ @ftp.help
+ @ftp.last_response.should == "211 System status, or system help reply. (HELP)\n"
+ end
+
+ it "returns the server's response" do
+ @ftp.help.should == "211 System status, or system help reply. (HELP)\n"
+ end
+
+ it "writes the HELP command with an optional parameter to the socket" do
+ @ftp.help("some parameter").should == "211 System status, or system help reply. (HELP some parameter)\n"
+ end
+
+ it "does not raise any error when the response code is 211" do
+ @server.should_receive(:help).and_respond("211 System status, or system help reply.")
+ lambda { @ftp.help }.should_not raise_error
+ end
+
+ it "does not raise any error when the response code is 214" do
+ @server.should_receive(:help).and_respond("214 Help message.")
+ lambda { @ftp.help }.should_not raise_error
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:help).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.help }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:help).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.help }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:help).and_respond("502 Command not implemented.")
+ lambda { @ftp.help }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:help).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.help }.should raise_error(Net::FTPTempError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/initialize_spec.rb b/spec/ruby/library/net/ftp/initialize_spec.rb
new file mode 100644
index 0000000000..cd6252ac31
--- /dev/null
+++ b/spec/ruby/library/net/ftp/initialize_spec.rb
@@ -0,0 +1,409 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#initialize" do
+ before :each do
+ @ftp = Net::FTP.allocate
+ @ftp.stub!(:connect)
+ @port_args = []
+ ruby_version_is "2.5" do
+ @port_args << 21
+ end
+ end
+
+ it "is private" do
+ Net::FTP.should have_private_instance_method(:initialize)
+ end
+
+ it "sets self into binary mode" do
+ @ftp.binary.should be_nil
+ @ftp.send(:initialize)
+ @ftp.binary.should be_true
+ end
+
+ it "sets self into active mode" do
+ @ftp.passive.should be_nil
+ @ftp.send(:initialize)
+ @ftp.passive.should be_false
+ end
+
+ it "sets self into non-debug mode" do
+ @ftp.debug_mode.should be_nil
+ @ftp.send(:initialize)
+ @ftp.debug_mode.should be_false
+ end
+
+ it "sets self to not resume file uploads/downloads" do
+ @ftp.resume.should be_nil
+ @ftp.send(:initialize)
+ @ftp.resume.should be_false
+ end
+
+ describe "when passed no arguments" do
+ it "does not try to connect" do
+ @ftp.should_not_receive(:connect)
+ @ftp.send(:initialize)
+ end
+ end
+
+ describe "when passed host" do
+ it "tries to connect to the passed host" do
+ @ftp.should_receive(:connect).with("localhost", *@port_args)
+ @ftp.send(:initialize, "localhost")
+ end
+ end
+
+ describe "when passed host, user" do
+ it "tries to connect to the passed host" do
+ @ftp.should_receive(:connect).with("localhost", *@port_args)
+ @ftp.send(:initialize, "localhost")
+ end
+
+ it "tries to login with the passed username" do
+ @ftp.should_receive(:login).with("rubyspec", nil, nil)
+ @ftp.send(:initialize, "localhost", "rubyspec")
+ end
+ end
+
+ describe "when passed host, user, password" do
+ it "tries to connect to the passed host" do
+ @ftp.should_receive(:connect).with("localhost", *@port_args)
+ @ftp.send(:initialize, "localhost")
+ end
+
+ it "tries to login with the passed username and password" do
+ @ftp.should_receive(:login).with("rubyspec", "rocks", nil)
+ @ftp.send(:initialize, "localhost", "rubyspec", "rocks")
+ end
+ end
+
+ describe "when passed host, user" do
+ it "tries to connect to the passed host" do
+ @ftp.should_receive(:connect).with("localhost", *@port_args)
+ @ftp.send(:initialize, "localhost")
+ end
+
+ it "tries to login with the passed username, password and account" do
+ @ftp.should_receive(:login).with("rubyspec", "rocks", "account")
+ @ftp.send(:initialize, "localhost", "rubyspec", "rocks", "account")
+ end
+ end
+
+ ruby_version_is '2.4' do
+ before :each do
+ @ftp.stub!(:login)
+ end
+
+ describe 'when the host' do
+ describe 'is set' do
+ describe 'and port option' do
+ describe 'is set' do
+ it 'tries to connect to the host on the specified port' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ port: 8080 })
+ @ftp.should_receive(:connect).with('localhost', 8080)
+
+ @ftp.send(:initialize, 'localhost', options)
+ end
+ end
+
+ describe 'is not set' do
+ it 'tries to connect to the host without a port' do
+ @ftp.should_receive(:connect).with("localhost", *@port_args)
+
+ @ftp.send(:initialize, 'localhost')
+ end
+ end
+ end
+
+ describe 'when the username option' do
+ describe 'is set' do
+ describe 'and the password option' do
+ describe 'is set' do
+ describe 'and the account option' do
+ describe 'is set' do
+ it 'tries to log in with the supplied parameters' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ username: 'a', password: 'topsecret', account: 'b' })
+ @ftp.should_receive(:login).with('a', 'topsecret', 'b')
+
+ @ftp.send(:initialize, 'localhost', options)
+ end
+ end
+
+ describe 'is unset' do
+ it 'tries to log in with the supplied parameters' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ username: 'a', password: 'topsecret' })
+ @ftp.should_receive(:login).with('a', 'topsecret', nil)
+
+ @ftp.send(:initialize, 'localhost', options)
+ end
+ end
+ end
+ end
+
+ describe 'is unset' do
+ describe 'and the account option' do
+ describe 'is set' do
+ it 'tries to log in with the supplied parameters' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ username: 'a', account: 'b' })
+ @ftp.should_receive(:login).with('a', nil, 'b')
+
+ @ftp.send(:initialize, 'localhost', options)
+ end
+ end
+
+ describe 'is unset' do
+ it 'tries to log in with the supplied parameters' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ username: 'a'})
+ @ftp.should_receive(:login).with('a', nil, nil)
+
+ @ftp.send(:initialize, 'localhost', options)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ describe 'is not set' do
+ it 'does not try to log in' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({})
+ @ftp.should_not_receive(:login)
+
+ @ftp.send(:initialize, 'localhost', options)
+ end
+ end
+ end
+ end
+
+ describe 'is unset' do
+ it 'does not try to connect' do
+ @ftp.should_not_receive(:connect)
+
+ @ftp.send(:initialize)
+ end
+
+ it 'does not try to log in' do
+ @ftp.should_not_receive(:login)
+
+ @ftp.send(:initialize)
+ end
+ end
+ end
+
+ describe 'when the passive option' do
+ describe 'is set' do
+ describe 'to true' do
+ it 'sets passive to true' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ passive: true })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.passive.should == true
+ end
+ end
+
+ describe 'to false' do
+ it 'sets passive to false' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ passive: false })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.passive.should == false
+ end
+ end
+ end
+
+ describe 'is unset' do
+ it 'sets passive to false' do
+ @ftp.send(:initialize)
+ @ftp.passive.should == false
+ end
+ end
+ end
+
+ describe 'when the debug_mode option' do
+ describe 'is set' do
+ describe 'to true' do
+ it 'sets debug_mode to true' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ debug_mode: true })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.debug_mode.should == true
+ end
+ end
+
+ describe 'to false' do
+ it 'sets debug_mode to false' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ debug_mode: false })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.debug_mode.should == false
+ end
+ end
+ end
+
+ describe 'is unset' do
+ it 'sets debug_mode to false' do
+ @ftp.send(:initialize)
+ @ftp.debug_mode.should == false
+ end
+ end
+ end
+
+ describe 'when the open_timeout option' do
+ describe 'is set' do
+ it 'sets open_timeout to the specified value' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ open_timeout: 42 })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.open_timeout.should == 42
+ end
+ end
+
+ describe 'is not set' do
+ it 'sets open_timeout to nil' do
+ @ftp.send(:initialize)
+ @ftp.open_timeout.should == nil
+ end
+ end
+ end
+
+ describe 'when the read_timeout option' do
+ describe 'is set' do
+ it 'sets read_timeout to the specified value' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ read_timeout: 100 })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.read_timeout.should == 100
+ end
+ end
+
+ describe 'is not set' do
+ it 'sets read_timeout to the default value' do
+ @ftp.send(:initialize)
+ @ftp.read_timeout.should == 60
+ end
+ end
+ end
+
+ describe 'when the ssl_handshake_timeout option' do
+ describe 'is set' do
+ it 'sets ssl_handshake_timeout to the specified value' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ ssl_handshake_timeout: 23 })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.ssl_handshake_timeout.should == 23
+ end
+ end
+
+ describe 'is not set' do
+ it 'sets ssl_handshake_timeout to nil' do
+ @ftp.send(:initialize)
+ @ftp.ssl_handshake_timeout.should == nil
+ end
+ end
+ end
+
+ describe 'when the ssl option' do
+ describe 'is set' do
+ describe "and the ssl option's value is true" do
+ it 'initializes ssl_context to a blank SSLContext object' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ ssl: true })
+
+ ssl_context = OpenSSL::SSL::SSLContext.allocate
+ ssl_context.stub!(:set_params)
+
+ OpenSSL::SSL::SSLContext.should_receive(:new).and_return(ssl_context)
+ ssl_context.should_receive(:set_params).with({})
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.instance_variable_get(:@ssl_context).should == ssl_context
+ end
+ end
+
+ describe "and the ssl option's value is a hash" do
+ it 'initializes ssl_context to a configured SSLContext object' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ ssl: {key: 'value'} })
+
+ ssl_context = OpenSSL::SSL::SSLContext.allocate
+ ssl_context.stub!(:set_params)
+
+ OpenSSL::SSL::SSLContext.should_receive(:new).and_return(ssl_context)
+ ssl_context.should_receive(:set_params).with({key: 'value'})
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.instance_variable_get(:@ssl_context).should == ssl_context
+ end
+ end
+
+ describe 'and private_data_connection' do
+ describe 'is set' do
+ it 'sets private_data_connection to that value' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ ssl: true, private_data_connection: 'true' })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.instance_variable_get(:@private_data_connection).should == 'true'
+ end
+ end
+
+ describe 'is not set' do
+ it 'sets private_data_connection to nil' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ ssl: true })
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.instance_variable_get(:@private_data_connection).should == true
+ end
+ end
+ end
+ end
+
+ describe 'is not set' do
+ it 'sets ssl_context to nil' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({})
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.instance_variable_get(:@ssl_context).should == nil
+ end
+
+ describe 'private_data_connection' do
+ describe 'is set' do
+ it 'raises an ArgumentError' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({ private_data_connection: true })
+
+ -> {
+ @ftp.send(:initialize, nil, options)
+ }.should raise_error(ArgumentError, /private_data_connection can be set to true only when ssl is enabled/)
+ end
+ end
+
+ describe 'is not set' do
+ it 'sets private_data_connection to false' do
+ options = mock('ftp initialize options')
+ options.should_receive(:to_hash).and_return({})
+
+ @ftp.send(:initialize, nil, options)
+ @ftp.instance_variable_get(:@private_data_connection).should == false
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/net/ftp/last_response_code_spec.rb b/spec/ruby/library/net/ftp/last_response_code_spec.rb
new file mode 100644
index 0000000000..3eb20f7ad8
--- /dev/null
+++ b/spec/ruby/library/net/ftp/last_response_code_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'shared/last_response_code'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#last_response_code" do
+ it_behaves_like :net_ftp_last_response_code, :last_response_code
+end
diff --git a/spec/ruby/library/net/ftp/last_response_spec.rb b/spec/ruby/library/net/ftp/last_response_spec.rb
new file mode 100644
index 0000000000..ada665d59c
--- /dev/null
+++ b/spec/ruby/library/net/ftp/last_response_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#last_response" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "returns the last response" do
+ @ftp.last_response.should == "220 Dummy FTP Server ready!\n"
+ @ftp.help
+ @ftp.last_response.should == "211 System status, or system help reply. (HELP)\n"
+ end
+end
diff --git a/spec/ruby/library/net/ftp/lastresp_spec.rb b/spec/ruby/library/net/ftp/lastresp_spec.rb
new file mode 100644
index 0000000000..273e216e8b
--- /dev/null
+++ b/spec/ruby/library/net/ftp/lastresp_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'shared/last_response_code'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#lastresp" do
+ it_behaves_like :net_ftp_last_response_code, :lastresp
+end
diff --git a/spec/ruby/library/net/ftp/list_spec.rb b/spec/ruby/library/net/ftp/list_spec.rb
new file mode 100644
index 0000000000..6175172923
--- /dev/null
+++ b/spec/ruby/library/net/ftp/list_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/list'
+
+describe "Net::FTP#list" do
+ it_behaves_like :net_ftp_list, :list
+end
diff --git a/spec/ruby/library/net/ftp/login_spec.rb b/spec/ruby/library/net/ftp/login_spec.rb
new file mode 100644
index 0000000000..c4f14f8747
--- /dev/null
+++ b/spec/ruby/library/net/ftp/login_spec.rb
@@ -0,0 +1,195 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#login" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ describe "when passed no arguments" do
+ it "sends the USER command with 'anonymous' as name to the server" do
+ @ftp.login
+ @server.login_user.should == "anonymous"
+ end
+
+ it "sends 'anonymous@' as a password when required" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @ftp.login
+ @server.login_pass.should == "anonymous@"
+ end
+
+ it "raises a Net::FTPReplyError when the server requests an account" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @server.should_receive(:pass).and_respond("332 Need account for login.")
+ lambda { @ftp.login }.should raise_error(Net::FTPReplyError)
+ end
+ end
+
+ describe "when passed name" do
+ it "sends the USER command with the passed name to the server" do
+ @ftp.login("rubyspec")
+ @server.login_user.should == "rubyspec"
+ end
+
+ it "raises a Net::FTPReplyError when the server requests a password, but none was given" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ lambda { @ftp.login("rubyspec") }.should raise_error(Net::FTPReplyError)
+ end
+
+ it "raises a Net::FTPReplyError when the server requests an account, but none was given" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @server.should_receive(:pass).and_respond("332 Need account for login.")
+ lambda { @ftp.login("rubyspec") }.should raise_error(Net::FTPReplyError)
+ end
+ end
+
+ describe "when passed name, password" do
+ it "sends the USER command with the passed name to the server" do
+ @ftp.login("rubyspec", "rocks")
+ @server.login_user.should == "rubyspec"
+ end
+
+ it "sends the passed password when required" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @ftp.login("rubyspec", "rocks")
+ @server.login_pass.should == "rocks"
+ end
+
+ it "raises a Net::FTPReplyError when the server requests an account" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @server.should_receive(:pass).and_respond("332 Need account for login.")
+ lambda { @ftp.login("rubyspec", "rocks") }.should raise_error(Net::FTPReplyError)
+ end
+ end
+
+ describe "when passed name, password, account" do
+ it "sends the USER command with the passed name to the server" do
+ @ftp.login("rubyspec", "rocks", "account")
+ @server.login_user.should == "rubyspec"
+ end
+
+ it "sends the passed password when required" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @ftp.login("rubyspec", "rocks", "account")
+ @server.login_pass.should == "rocks"
+ end
+
+ it "sends the passed account when required" do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @server.should_receive(:pass).and_respond("332 Need account for login.")
+ @ftp.login("rubyspec", "rocks", "account")
+ @server.login_acct.should == "account"
+ end
+ end
+
+ describe "when the USER command fails" do
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:user).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:user).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:user).and_respond("502 Command not implemented.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:user).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:user).and_respond("530 Not logged in.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when the PASS command fails" do
+ before :each do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ end
+
+ it "does not raise an Error when the response code is 202" do
+ @server.should_receive(:pass).and_respond("202 Command not implemented, superfluous at this site.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should_not raise_error
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:pass).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:pass).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:pass).and_respond("502 Command not implemented.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:pass).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:pass).and_respond("530 Not logged in.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when the ACCT command fails" do
+ before :each do
+ @server.should_receive(:user).and_respond("331 User name okay, need password.")
+ @server.should_receive(:pass).and_respond("332 Need account for login.")
+ end
+
+ it "does not raise an Error when the response code is 202" do
+ @server.should_receive(:acct).and_respond("202 Command not implemented, superfluous at this site.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should_not raise_error
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:acct).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:acct).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:acct).and_respond("502 Command not implemented.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:acct).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:acct).and_respond("530 Not logged in.")
+ lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/ftp/ls_spec.rb b/spec/ruby/library/net/ftp/ls_spec.rb
new file mode 100644
index 0000000000..e729eb9481
--- /dev/null
+++ b/spec/ruby/library/net/ftp/ls_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/list'
+
+describe "Net::FTP#ls" do
+ it_behaves_like :net_ftp_list, :ls
+end
diff --git a/spec/ruby/library/net/ftp/mdtm_spec.rb b/spec/ruby/library/net/ftp/mdtm_spec.rb
new file mode 100644
index 0000000000..ddcc06d708
--- /dev/null
+++ b/spec/ruby/library/net/ftp/mdtm_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#mdtm" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the MDTM with the passed filename command to the server" do
+ @ftp.mdtm("test.file")
+ @ftp.last_response.should == "213 19980705132316\n"
+ end
+
+ it "returns the last modification time of the passed file" do
+ @ftp.mdtm("test.file").should == "19980705132316"
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:mdtm).and_respond("550 Requested action not taken.")
+ lambda { @ftp.mdtm("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:mdtm).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.mdtm("test.file") }.should raise_error(Net::FTPTempError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/mkdir_spec.rb b/spec/ruby/library/net/ftp/mkdir_spec.rb
new file mode 100644
index 0000000000..0d8314bfa3
--- /dev/null
+++ b/spec/ruby/library/net/ftp/mkdir_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#mkdir" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the MKD command with the passed pathname to the server" do
+ @ftp.mkdir("test.folder")
+ @ftp.last_response.should == %{257 "test.folder" created.\n}
+ end
+
+ it "returns the path to the newly created directory" do
+ @ftp.mkdir("test.folder").should == "test.folder"
+ @ftp.mkdir("/absolute/path/to/test.folder").should == "/absolute/path/to/test.folder"
+ @ftp.mkdir("relative/path/to/test.folder").should == "relative/path/to/test.folder"
+ @ftp.mkdir('/usr/dm/foo"bar').should == '/usr/dm/foo"bar'
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:mkd).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:mkd).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:mkd).and_respond("502 Command not implemented.")
+ lambda { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:mkd).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:mkd).and_respond("530 Not logged in.")
+ lambda { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:mkd).and_respond("550 Requested action not taken.")
+ lambda { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/mtime_spec.rb b/spec/ruby/library/net/ftp/mtime_spec.rb
new file mode 100644
index 0000000000..e05e92f58f
--- /dev/null
+++ b/spec/ruby/library/net/ftp/mtime_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#mtime" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the MDTM with the passed filename command to the server" do
+ @ftp.mtime("test.file")
+ @ftp.last_response.should == "213 19980705132316\n"
+ end
+
+ describe "when passed filename" do
+ it "returns the last modification time of the passed file as a Time object in the local time" do
+ @ftp.mtime("test.file").should == Time.gm("1998", "07", "05", "13", "23", "16")
+ end
+ end
+
+ describe "when passed filename, local_time" do
+ it "returns the last modification time as a Time object in UTC when local_time is true" do
+ @ftp.mtime("test.file", true).should == Time.local("1998", "07", "05", "13", "23", "16")
+ end
+
+ it "returns the last modification time as a Time object in the local time when local_time is false" do
+ @ftp.mtime("test.file", false).should == Time.gm("1998", "07", "05", "13", "23", "16")
+ end
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:mdtm).and_respond("550 Requested action not taken.")
+ lambda { @ftp.mtime("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:mdtm).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.mtime("test.file") }.should raise_error(Net::FTPTempError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/nlst_spec.rb b/spec/ruby/library/net/ftp/nlst_spec.rb
new file mode 100644
index 0000000000..36ca7a6127
--- /dev/null
+++ b/spec/ruby/library/net/ftp/nlst_spec.rb
@@ -0,0 +1,92 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#nlst" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.passive = false
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ describe "when passed no arguments" do
+ it "returns an Array containing a list of files in the current dir" do
+ @ftp.nlst.should == ["last_response_code.rb", "list.rb", "pwd.rb"]
+ @ftp.last_response.should == "226 transfer complete (NLST)\n"
+ end
+ end
+
+ describe "when passed dir" do
+ it "returns an Array containing a list of files in the passed dir" do
+ @ftp.nlst("test.folder").should == ["last_response_code.rb", "list.rb", "pwd.rb"]
+ @ftp.last_response.should == "226 transfer complete (NLST test.folder)\n"
+ end
+ end
+
+ describe "when the NLST command fails" do
+ it "raises a Net::FTPTempError when the response code is 450" do
+ @server.should_receive(:nlst).and_respond("450 Requested file action not taken..")
+ lambda { @ftp.nlst }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:nlst).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.nlst }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:nlst).and_respond("501 Syntax error, command unrecognized.")
+ lambda { @ftp.nlst }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:nlst).and_respond("502 Command not implemented.")
+ lambda { @ftp.nlst }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:nlst).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.nlst }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:nlst).and_respond("530 Not logged in.")
+ lambda { @ftp.nlst }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when opening the data port fails" do
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
+ @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.nlst }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
+ @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.nlst }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
+ @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.nlst }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:eprt).and_respond("530 Not logged in.")
+ @server.should_receive(:port).and_respond("530 Not logged in.")
+ lambda { @ftp.nlst }.should raise_error(Net::FTPPermError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/ftp/noop_spec.rb b/spec/ruby/library/net/ftp/noop_spec.rb
new file mode 100644
index 0000000000..eca8911b7b
--- /dev/null
+++ b/spec/ruby/library/net/ftp/noop_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#noop" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the NOOP command to the server" do
+ @ftp.noop
+ @ftp.last_response.should == "200 Command okay. (NOOP)\n"
+ end
+
+ it "returns nil" do
+ @ftp.noop.should be_nil
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:noop).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.noop }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:noop).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.noop }.should raise_error(Net::FTPTempError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/open_spec.rb b/spec/ruby/library/net/ftp/open_spec.rb
new file mode 100644
index 0000000000..7be02ff373
--- /dev/null
+++ b/spec/ruby/library/net/ftp/open_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP.open" do
+ before :each do
+ @ftp = mock("Net::FTP instance")
+ Net::FTP.stub!(:new).and_return(@ftp)
+ end
+
+ describe "when passed no block" do
+ it "returns a new Net::FTP instance" do
+ Net::FTP.open("localhost").should equal(@ftp)
+ end
+
+ it "passes the passed arguments down to Net::FTP.new" do
+ Net::FTP.should_receive(:new).with("localhost", "user", "password", "account")
+ Net::FTP.open("localhost", "user", "password", "account")
+ end
+ end
+
+ describe "when passed a block" do
+ before :each do
+ @ftp.stub!(:close)
+ end
+
+ it "yields a new Net::FTP instance to the passed block" do
+ yielded = false
+ Net::FTP.open("localhost") do |ftp|
+ yielded = true
+ ftp.should equal(@ftp)
+ end
+ yielded.should be_true
+ end
+
+ it "closes the Net::FTP instance after yielding" do
+ Net::FTP.open("localhost") do |ftp|
+ ftp.should_receive(:close)
+ end
+ end
+
+ it "closes the Net::FTP instance even if an exception is raised while yielding" do
+ begin
+ Net::FTP.open("localhost") do |ftp|
+ ftp.should_receive(:close)
+ raise ArgumentError, "some exception"
+ end
+ rescue ArgumentError
+ end
+ end
+
+ it "returns the block's return value" do
+ Net::FTP.open("localhost") { :test }.should == :test
+ end
+ end
+end
diff --git a/spec/ruby/library/net/ftp/passive_spec.rb b/spec/ruby/library/net/ftp/passive_spec.rb
new file mode 100644
index 0000000000..e8db030cbd
--- /dev/null
+++ b/spec/ruby/library/net/ftp/passive_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#passive" do
+ it "returns true when self is in passive mode" do
+ ftp = Net::FTP.new
+ ftp.passive.should be_false
+
+ ftp.passive = true
+ ftp.passive.should be_true
+ end
+
+ it "is the value of Net::FTP.default_value by default" do
+ ruby_exe(fixture(__FILE__, "passive.rb")).should == "true"
+ end
+end
+
+describe "Net::FTP#passive=" do
+ it "sets self to passive mode when passed true" do
+ ftp = Net::FTP.new
+
+ ftp.passive = true
+ ftp.passive.should be_true
+
+ ftp.passive = false
+ ftp.passive.should be_false
+ end
+end
diff --git a/spec/ruby/library/net/ftp/put_spec.rb b/spec/ruby/library/net/ftp/put_spec.rb
new file mode 100644
index 0000000000..f1f85b5d05
--- /dev/null
+++ b/spec/ruby/library/net/ftp/put_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/puttextfile'
+require_relative 'shared/putbinaryfile'
+
+describe "Net::FTP#put (binary mode)" do
+ before :each do
+ @binary_mode = true
+ end
+
+ it_behaves_like :net_ftp_putbinaryfile, :put
+end
+
+describe "Net::FTP#put (text mode)" do
+ before :each do
+ @binary_mode = false
+ end
+
+ it_behaves_like :net_ftp_puttextfile, :put
+end
diff --git a/spec/ruby/library/net/ftp/putbinaryfile_spec.rb b/spec/ruby/library/net/ftp/putbinaryfile_spec.rb
new file mode 100644
index 0000000000..cb1c7bef5a
--- /dev/null
+++ b/spec/ruby/library/net/ftp/putbinaryfile_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/putbinaryfile'
+
+describe "Net::FTP#putbinaryfile" do
+ it_behaves_like :net_ftp_putbinaryfile, :putbinaryfile
+end
diff --git a/spec/ruby/library/net/ftp/puttextfile_spec.rb b/spec/ruby/library/net/ftp/puttextfile_spec.rb
new file mode 100644
index 0000000000..00a930afd7
--- /dev/null
+++ b/spec/ruby/library/net/ftp/puttextfile_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+require_relative 'shared/puttextfile'
+
+describe "Net::FTP#puttextfile" do
+ it_behaves_like :net_ftp_puttextfile, :puttextfile
+end
diff --git a/spec/ruby/library/net/ftp/pwd_spec.rb b/spec/ruby/library/net/ftp/pwd_spec.rb
new file mode 100644
index 0000000000..f515cd29a0
--- /dev/null
+++ b/spec/ruby/library/net/ftp/pwd_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#pwd" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the PWD command to the server" do
+ @ftp.pwd
+ @ftp.last_response.should == "257 \"/some/dir/\" - current directory\n"
+ end
+
+ it "returns the current directory" do
+ @ftp.pwd.should == "/some/dir/"
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:pwd).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.pwd }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:pwd).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.pwd }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:pwd).and_respond("502 Command not implemented.")
+ lambda { @ftp.pwd }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:pwd).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.pwd }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:pwd).and_respond("550 Requested action not taken.")
+ lambda { @ftp.pwd }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/quit_spec.rb b/spec/ruby/library/net/ftp/quit_spec.rb
new file mode 100644
index 0000000000..fdc3a2f795
--- /dev/null
+++ b/spec/ruby/library/net/ftp/quit_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#quit" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the QUIT command to the server" do
+ @ftp.quit
+ @ftp.last_response.should == "221 OK, bye\n"
+ end
+
+ it "does not close the socket automagically" do
+ @ftp.quit
+ @ftp.closed?.should be_false
+ end
+
+ it "returns nil" do
+ @ftp.quit.should be_nil
+ end
+end
diff --git a/spec/ruby/library/net/ftp/rename_spec.rb b/spec/ruby/library/net/ftp/rename_spec.rb
new file mode 100644
index 0000000000..e1fd4e47cf
--- /dev/null
+++ b/spec/ruby/library/net/ftp/rename_spec.rb
@@ -0,0 +1,94 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#rename" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ describe "when passed from_name, to_name" do
+ it "sends the RNFR command with the passed from_name and the RNTO command with the passed to_name to the server" do
+ @ftp.rename("from.file", "to.file")
+ @ftp.last_response.should == "250 Requested file action okay, completed. (Renamed from.file to to.file)\n"
+ end
+
+ it "returns something" do
+ @ftp.rename("from.file", "to.file").should be_nil
+ end
+ end
+
+ describe "when the RNFR command fails" do
+ it "raises a Net::FTPTempError when the response code is 450" do
+ @server.should_receive(:rnfr).and_respond("450 Requested file action not taken.")
+ lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:rnfr).and_respond("550 Requested action not taken.")
+ lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:rnfr).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:rnfr).and_respond("502 Command not implemented.")
+ lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:rnfr).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:rnfr).and_respond("530 Not logged in.")
+ lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when the RNTO command fails" do
+ it "raises a Net::FTPPermError when the response code is 532" do
+ @server.should_receive(:rnfr).and_respond("532 Need account for storing files.")
+ lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 553" do
+ @server.should_receive(:rnto).and_respond("553 Requested action not taken.")
+ lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:rnto).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:rnto).and_respond("502 Command not implemented.")
+ lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:rnto).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:rnto).and_respond("530 Not logged in.")
+ lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/ftp/resume_spec.rb b/spec/ruby/library/net/ftp/resume_spec.rb
new file mode 100644
index 0000000000..51b1cff2a4
--- /dev/null
+++ b/spec/ruby/library/net/ftp/resume_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#resume" do
+ it "returns true when self is set to resume uploads/downloads" do
+ ftp = Net::FTP.new
+ ftp.resume.should be_false
+
+ ftp.resume = true
+ ftp.resume.should be_true
+ end
+end
+
+describe "Net::FTP#resume=" do
+ it "sets self to resume uploads/downloads when set to true" do
+ ftp = Net::FTP.new
+ ftp.resume = true
+ ftp.resume.should be_true
+
+ ftp.resume = false
+ ftp.resume.should be_false
+ end
+end
diff --git a/spec/ruby/library/net/ftp/retrbinary_spec.rb b/spec/ruby/library/net/ftp/retrbinary_spec.rb
new file mode 100644
index 0000000000..30e2484af5
--- /dev/null
+++ b/spec/ruby/library/net/ftp/retrbinary_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#retrbinary" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the passed command to the server" do
+ @ftp.retrbinary("RETR test", 4096) {}
+ @ftp.last_response.should == "226 Closing data connection. (RETR test)\n"
+ end
+
+ it "yields the received content as binary blocks of the passed size" do
+ res = []
+ @ftp.retrbinary("RETR test", 10) { |bin| res << bin }
+ res.should == [ "This is th", "e content\n", "of the fil", "e named 't", "est'.\n" ]
+ end
+end
diff --git a/spec/ruby/library/net/ftp/retrlines_spec.rb b/spec/ruby/library/net/ftp/retrlines_spec.rb
new file mode 100644
index 0000000000..546df3844e
--- /dev/null
+++ b/spec/ruby/library/net/ftp/retrlines_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#retrlines" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the passed command over the socket" do
+ @ftp.retrlines("LIST test.dir") {}
+ @ftp.last_response.should == "226 transfer complete (LIST test.dir)\n"
+ end
+
+ it "yields each received line to the passed block" do
+ res = []
+ @ftp.retrlines("LIST test.dir") { |x| res << x }
+ res.should == [
+ "-rw-r--r-- 1 spec staff 507 17 Jul 18:41 last_response_code.rb",
+ "-rw-r--r-- 1 spec staff 50 17 Jul 18:41 list.rb",
+ "-rw-r--r-- 1 spec staff 48 17 Jul 18:41 pwd.rb"
+ ]
+ end
+end
diff --git a/spec/ruby/library/net/ftp/return_code_spec.rb b/spec/ruby/library/net/ftp/return_code_spec.rb
new file mode 100644
index 0000000000..605f66ec6e
--- /dev/null
+++ b/spec/ruby/library/net/ftp/return_code_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#return_code" do
+ before :each do
+ @ftp = Net::FTP.new
+ end
+
+ it "outputs a warning and returns a newline" do
+ lambda do
+ @ftp.return_code.should == "\n"
+ end.should complain(/warning: Net::FTP#return_code is obsolete and do nothing/)
+ end
+end
+
+describe "Net::FTP#return_code=" do
+ before :each do
+ @ftp = Net::FTP.new
+ end
+
+ it "outputs a warning" do
+ lambda { @ftp.return_code = 123 }.should complain(/warning: Net::FTP#return_code= is obsolete and do nothing/)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/rmdir_spec.rb b/spec/ruby/library/net/ftp/rmdir_spec.rb
new file mode 100644
index 0000000000..ab92a8678b
--- /dev/null
+++ b/spec/ruby/library/net/ftp/rmdir_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#rmdir" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the RMD command with the passed pathname to the server" do
+ @ftp.rmdir("test.folder")
+ @ftp.last_response.should == "250 Requested file action okay, completed. (RMD test.folder)\n"
+ end
+
+ it "returns nil" do
+ @ftp.rmdir("test.folder").should be_nil
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:rmd).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:rmd).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:rmd).and_respond("502 Command not implemented.")
+ lambda { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:rmd).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:rmd).and_respond("530 Not logged in.")
+ lambda { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:rmd).and_respond("550 Requested action not taken.")
+ lambda { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/sendcmd_spec.rb b/spec/ruby/library/net/ftp/sendcmd_spec.rb
new file mode 100644
index 0000000000..631bc10c84
--- /dev/null
+++ b/spec/ruby/library/net/ftp/sendcmd_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#sendcmd" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the passed command to the server" do
+ @ftp.sendcmd("HELP")
+ @ftp.last_response.should == "211 System status, or system help reply. (HELP)\n"
+ end
+
+ it "returns the server's response" do
+ @ftp.sendcmd("HELP").should == "211 System status, or system help reply. (HELP)\n"
+ end
+
+ it "raises no error when the response code is 1xx, 2xx or 3xx" do
+ @server.should_receive(:help).and_respond("120 Service ready in nnn minutes.")
+ lambda { @ftp.sendcmd("HELP") }.should_not raise_error
+
+ @server.should_receive(:help).and_respond("200 Command okay.")
+ lambda { @ftp.sendcmd("HELP") }.should_not raise_error
+
+ @server.should_receive(:help).and_respond("350 Requested file action pending further information.")
+ lambda { @ftp.sendcmd("HELP") }.should_not raise_error
+ end
+
+ it "raises a Net::FTPTempError when the response code is 4xx" do
+ @server.should_receive(:help).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.sendcmd("HELP") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 5xx" do
+ @server.should_receive(:help).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.sendcmd("HELP") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPProtoError when the response code is not between 1xx-5xx" do
+ @server.should_receive(:help).and_respond("999 Invalid response.")
+ lambda { @ftp.sendcmd("HELP") }.should raise_error(Net::FTPProtoError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/set_socket_spec.rb b/spec/ruby/library/net/ftp/set_socket_spec.rb
new file mode 100644
index 0000000000..3aa5686326
--- /dev/null
+++ b/spec/ruby/library/net/ftp/set_socket_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+
+describe "Net::FTP#set_socket" do
+ # TODO: I won't spec this method, as it is not used
+ # anywhere and it should be private anyway.
+ #it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/net/ftp/shared/getbinaryfile.rb b/spec/ruby/library/net/ftp/shared/getbinaryfile.rb
new file mode 100644
index 0000000000..2252935b2d
--- /dev/null
+++ b/spec/ruby/library/net/ftp/shared/getbinaryfile.rb
@@ -0,0 +1,150 @@
+describe :net_ftp_getbinaryfile, shared: :true do
+ before :each do
+ @fixture_file = File.dirname(__FILE__) + "/../fixtures/getbinaryfile"
+ @tmp_file = tmp("getbinaryfile")
+
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ @ftp.binary = @binary_mode
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+
+ rm_r @tmp_file
+ end
+
+ it "sends the RETR command to the server" do
+ @ftp.send(@method, "test", @tmp_file)
+ @ftp.last_response.should == "226 Closing data connection. (RETR test)\n"
+ end
+
+ it "returns nil" do
+ @ftp.send(@method, "test", @tmp_file).should be_nil
+ end
+
+ it "saves the contents of the passed remote file to the passed local file" do
+ @ftp.send(@method, "test", @tmp_file)
+ File.read(@tmp_file).should == "This is the content\nof the file named 'test'.\n"
+ end
+
+ describe "when passed a block" do
+ it "yields the received content as binary blocks of the passed size" do
+ res = []
+ @ftp.send(@method, "test", @tmp_file, 10) { |bin| res << bin }
+ res.should == [ "This is th", "e content\n", "of the fil", "e named 't", "est'.\n" ]
+ end
+ end
+
+ describe "when resuming an existing file" do
+ before :each do
+ @tmp_file = tmp("getbinaryfile_resume")
+
+ File.open(@tmp_file, "wb") do |f|
+ f << "This is the content\n"
+ end
+
+ @ftp.resume = true
+ end
+
+ it "saves the remaining content of the passed remote file to the passed local file" do
+ @ftp.send(@method, "test", @tmp_file)
+ File.read(@tmp_file).should == "This is the content\nof the file named 'test'.\n"
+ end
+
+ describe "and the REST command fails" do
+ it "raises a Net::FTPProtoError when the response code is 550" do
+ @server.should_receive(:rest).and_respond("Requested action not taken.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPProtoError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:rest).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:rest).and_respond("501 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:rest).and_respond("502 Command not implemented.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:rest).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:rest).and_respond("530 Not logged in.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+ end
+ end
+
+ describe "when the RETR command fails" do
+ it "raises a Net::FTPTempError when the response code is 450" do
+ @server.should_receive(:retr).and_respond("450 Requested file action not taken.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPProtoError when the response code is 550" do
+ @server.should_receive(:retr).and_respond("Requested action not taken.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPProtoError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:retr).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:retr).and_respond("501 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:retr).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:retr).and_respond("530 Not logged in.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when opening the data port fails" do
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
+ @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
+ @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
+ @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:eprt).and_respond("530 Not logged in.")
+ @server.should_receive(:port).and_respond("530 Not logged in.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/ftp/shared/gettextfile.rb b/spec/ruby/library/net/ftp/shared/gettextfile.rb
new file mode 100644
index 0000000000..6219581d12
--- /dev/null
+++ b/spec/ruby/library/net/ftp/shared/gettextfile.rb
@@ -0,0 +1,100 @@
+describe :net_ftp_gettextfile, shared: :true do
+ before :each do
+ @tmp_file = tmp("gettextfile")
+
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ @ftp.binary = @binary_mode
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+
+ rm_r @tmp_file
+ end
+
+ it "sends the RETR command to the server" do
+ @ftp.send(@method, "test", @tmp_file)
+ @ftp.last_response.should == "226 Closing data connection. (RETR test)\n"
+ end
+
+ it "returns nil" do
+ @ftp.send(@method, "test", @tmp_file).should be_nil
+ end
+
+ it "saves the contents of the passed remote file to the passed local file" do
+ @ftp.send(@method, "test", @tmp_file)
+ File.read(@tmp_file).should == "This is the content\nof the file named 'test'.\n"
+ end
+
+ describe "when passed a block" do
+ it "yields each line of the retrieved file to the passed block" do
+ res = []
+ @ftp.send(@method, "test", @tmp_file) { |line| res << line }
+ res.should == [ "This is the content", "of the file named 'test'."]
+ end
+ end
+
+ describe "when the RETR command fails" do
+ it "raises a Net::FTPTempError when the response code is 450" do
+ @server.should_receive(:retr).and_respond("450 Requested file action not taken.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPProtoError when the response code is 550" do
+ @server.should_receive(:retr).and_respond("Requested action not taken.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPProtoError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:retr).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:retr).and_respond("501 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:retr).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:retr).and_respond("530 Not logged in.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when opening the data port fails" do
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
+ @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
+ @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
+ @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:eprt).and_respond("530 Not logged in.")
+ @server.should_receive(:port).and_respond("530 Not logged in.")
+ lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/ftp/shared/last_response_code.rb b/spec/ruby/library/net/ftp/shared/last_response_code.rb
new file mode 100644
index 0000000000..4fe53677db
--- /dev/null
+++ b/spec/ruby/library/net/ftp/shared/last_response_code.rb
@@ -0,0 +1,25 @@
+describe :net_ftp_last_response_code, shared: true do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "returns the response code for the last response" do
+ @server.should_receive(:help).and_respond("200 Command okay.")
+ @ftp.help
+ @ftp.send(@method).should == "200"
+
+ @server.should_receive(:help).and_respond("212 Directory status.")
+ @ftp.help
+ @ftp.send(@method).should == "212"
+ end
+end
diff --git a/spec/ruby/library/net/ftp/shared/list.rb b/spec/ruby/library/net/ftp/shared/list.rb
new file mode 100644
index 0000000000..50ca8ad119
--- /dev/null
+++ b/spec/ruby/library/net/ftp/shared/list.rb
@@ -0,0 +1,104 @@
+describe :net_ftp_list, shared: true do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.passive = false
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ describe "when passed a block" do
+ it "yields each file in the list of files in the passed dir" do
+ expected = [
+ "-rw-r--r-- 1 spec staff 507 17 Jul 18:41 last_response_code.rb",
+ "-rw-r--r-- 1 spec staff 50 17 Jul 18:41 list.rb",
+ "-rw-r--r-- 1 spec staff 48 17 Jul 18:41 pwd.rb"
+ ]
+
+ res = []
+ @ftp.send(@method, "test.folder") { |line| res << line}
+ res.should == expected
+
+ @ftp.last_response.should == "226 transfer complete (LIST test.folder)\n"
+ end
+ end
+
+ describe "when passed no block" do
+ it "returns an Array containing a list of files in the passed dir" do
+ expected = [
+ "-rw-r--r-- 1 spec staff 507 17 Jul 18:41 last_response_code.rb",
+ "-rw-r--r-- 1 spec staff 50 17 Jul 18:41 list.rb",
+ "-rw-r--r-- 1 spec staff 48 17 Jul 18:41 pwd.rb"
+ ]
+
+ @ftp.send(@method, "test.folder").should == expected
+
+ @ftp.last_response.should == "226 transfer complete (LIST test.folder)\n"
+ end
+ end
+
+ describe "when the LIST command fails" do
+ it "raises a Net::FTPTempError when the response code is 450" do
+ @server.should_receive(:list).and_respond("450 Requested file action not taken..")
+ lambda { @ftp.send(@method) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:list).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:list).and_respond("501 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:list).and_respond("502 Command not implemented.")
+ lambda { @ftp.send(@method) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:list).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.send(@method) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:list).and_respond("530 Not logged in.")
+ lambda { @ftp.send(@method) }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when opening the data port fails" do
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
+ @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
+ @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.send(@method) }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
+ @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.send(@method) }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:eprt).and_respond("530 Not logged in.")
+ @server.should_receive(:port).and_respond("530 Not logged in.")
+ lambda { @ftp.send(@method) }.should raise_error(Net::FTPPermError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/ftp/shared/putbinaryfile.rb b/spec/ruby/library/net/ftp/shared/putbinaryfile.rb
new file mode 100644
index 0000000000..74eaf320ae
--- /dev/null
+++ b/spec/ruby/library/net/ftp/shared/putbinaryfile.rb
@@ -0,0 +1,167 @@
+describe :net_ftp_putbinaryfile, shared: :true do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @local_fixture_file = File.dirname(__FILE__) + "/../fixtures/putbinaryfile"
+ @remote_tmp_file = tmp("binaryfile", false)
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ @ftp.binary = @binary_mode
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+
+ rm_r @remote_tmp_file
+ end
+
+ it "sends the STOR command to the server" do
+ @ftp.send(@method, @local_fixture_file, "binary")
+ @ftp.last_response.should == "200 OK, Data received. (STOR binary)\n"
+ end
+
+ it "sends the contents of the passed local_file, without modifications" do
+ @ftp.send(@method, @local_fixture_file, "binary")
+
+ remote_lines = File.readlines(@remote_tmp_file)
+ local_lines = File.readlines(@local_fixture_file)
+
+ remote_lines.should == local_lines
+ end
+
+ it "returns nil" do
+ @ftp.send(@method, @local_fixture_file, "binary").should be_nil
+ end
+
+ describe "when passed a block" do
+ it "yields the transmitted content as binary blocks of the passed size" do
+ res = []
+ @ftp.send(@method, @local_fixture_file, "binary", 10) { |x| res << x }
+ res.should == [
+ "This is an", " example f",
+ "ile\nwhich ", "is going t",
+ "o be trans", "mitted\nusi",
+ "ng #putbin", "aryfile.\n"
+ ]
+ end
+ end
+
+ describe "when resuming an existing file" do
+ before :each do
+ File.open(@remote_tmp_file, "w") do |f|
+ f << "This is an example file\n"
+ end
+
+ @ftp.resume = true
+ end
+
+ it "sends the remaining content of the passed local_file to the passed remote_file" do
+ @ftp.send(@method, @local_fixture_file, "binary")
+ File.read(@remote_tmp_file).should == File.read(@local_fixture_file)
+ end
+
+ describe "and the APPE command fails" do
+ it "raises a Net::FTPProtoError when the response code is 550" do
+ @server.should_receive(:appe).and_respond("Requested action not taken.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPProtoError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:appe).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:appe).and_respond("501 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:appe).and_respond("502 Command not implemented.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:appe).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:appe).and_respond("530 Not logged in.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+ end
+ end
+
+ describe "when the STOR command fails" do
+ it "raises a Net::FTPPermError when the response code is 532" do
+ @server.should_receive(:stor).and_respond("532 Need account for storing files.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 450" do
+ @server.should_receive(:stor).and_respond("450 Requested file action not taken.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 452" do
+ @server.should_receive(:stor).and_respond("452 Requested action not taken.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 553" do
+ @server.should_receive(:stor).and_respond("553 Requested action not taken.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:stor).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:stor).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:stor).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:stor).and_respond("530 Not logged in.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when opening the data port fails" do
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
+ @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
+ @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
+ @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:eprt).and_respond("530 Not logged in.")
+ @server.should_receive(:port).and_respond("530 Not logged in.")
+ lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/ftp/shared/puttextfile.rb b/spec/ruby/library/net/ftp/shared/puttextfile.rb
new file mode 100644
index 0000000000..9bfdc7c41e
--- /dev/null
+++ b/spec/ruby/library/net/ftp/shared/puttextfile.rb
@@ -0,0 +1,120 @@
+describe :net_ftp_puttextfile, shared: true do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @local_fixture_file = File.dirname(__FILE__) + "/../fixtures/puttextfile"
+ @remote_tmp_file = tmp("textfile", false)
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ @ftp.binary = @binary_mode
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+
+ rm_r @remote_tmp_file
+ end
+
+ it "sends the STOR command to the server" do
+ @ftp.send(@method, @local_fixture_file, "text")
+ @ftp.last_response.should == "200 OK, Data received. (STOR text)\n"
+ end
+
+ it "sends the contents of the passed local_file, using \\r\\n as the newline separator" do
+ @ftp.send(@method, @local_fixture_file, "text")
+
+ remote_lines = open(@remote_tmp_file, "rb") {|f| f.read }
+ local_lines = open(@local_fixture_file, "rb") {|f| f.read }
+
+ remote_lines.should_not == local_lines
+ remote_lines.should == local_lines.gsub("\n", "\r\n")
+ end
+
+ it "returns nil" do
+ @ftp.send(@method, @local_fixture_file, "text").should be_nil
+ end
+
+ describe "when passed a block" do
+ it "yields each transmitted line" do
+ res = []
+ @ftp.send(@method, @local_fixture_file, "text") { |x| res << x }
+ res.should == [
+ "This is an example file\r\n",
+ "which is going to be transmitted\r\n",
+ "using #puttextfile.\r\n"
+ ]
+ end
+ end
+
+ describe "when the STOR command fails" do
+ it "raises a Net::FTPPermError when the response code is 532" do
+ @server.should_receive(:stor).and_respond("532 Need account for storing files.")
+ lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 450" do
+ @server.should_receive(:stor).and_respond("450 Requested file action not taken.")
+ lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 452" do
+ @server.should_receive(:stor).and_respond("452 Requested action not taken.")
+ lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 553" do
+ @server.should_receive(:stor).and_respond("553 Requested action not taken.")
+ lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:stor).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:stor).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:stor).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:stor).and_respond("530 Not logged in.")
+ lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+ end
+
+ describe "when opening the data port fails" do
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.")
+ @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.")
+ @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.")
+ @server.should_receive(:port).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:eprt).and_respond("530 Not logged in.")
+ @server.should_receive(:port).and_respond("530 Not logged in.")
+ lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/ftp/shared/pwd.rb b/spec/ruby/library/net/ftp/shared/pwd.rb
new file mode 100644
index 0000000000..951d020f2d
--- /dev/null
+++ b/spec/ruby/library/net/ftp/shared/pwd.rb
@@ -0,0 +1,3 @@
+describe :net_ftp_pwd, shared: true do
+
+end
diff --git a/spec/ruby/library/net/ftp/site_spec.rb b/spec/ruby/library/net/ftp/site_spec.rb
new file mode 100644
index 0000000000..85aa2609c1
--- /dev/null
+++ b/spec/ruby/library/net/ftp/site_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#site" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the SITE command with the passed argument to the server" do
+ @ftp.site("param")
+ @ftp.last_response.should == "200 Command okay. (SITE param)\n"
+ end
+
+ it "returns nil" do
+ @ftp.site("param").should be_nil
+ end
+
+ it "does not raise an error when the response code is 202" do
+ @server.should_receive(:site).and_respond("202 Command not implemented, superfluous at this site.")
+ lambda { @ftp.site("param") }.should_not raise_error
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:site).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.site("param") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:site).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.site("param") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:site).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.site("param") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:site).and_respond("530 Requested action not taken.")
+ lambda { @ftp.site("param") }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/size_spec.rb b/spec/ruby/library/net/ftp/size_spec.rb
new file mode 100644
index 0000000000..eb3ee90d0f
--- /dev/null
+++ b/spec/ruby/library/net/ftp/size_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#size" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the SIZE command to the server" do
+ @ftp.size("test.file")
+ @ftp.last_response.should == "213 1024\n"
+ end
+
+ it "returns the size of the passed file as Integer" do
+ @ftp.size("test.file").should eql(1024)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:size).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.size("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:size).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.size("test.file") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:size).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.size("test.file") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 550" do
+ @server.should_receive(:size).and_respond("550 Requested action not taken.")
+ lambda { @ftp.size("test.file") }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/spec_helper.rb b/spec/ruby/library/net/ftp/spec_helper.rb
new file mode 100644
index 0000000000..c87d16218b
--- /dev/null
+++ b/spec/ruby/library/net/ftp/spec_helper.rb
@@ -0,0 +1,5 @@
+require "net/ftp"
+
+if defined?(Net::FTP.default_passive)
+ Net::FTP.default_passive = false
+end
diff --git a/spec/ruby/library/net/ftp/status_spec.rb b/spec/ruby/library/net/ftp/status_spec.rb
new file mode 100644
index 0000000000..22d0d47254
--- /dev/null
+++ b/spec/ruby/library/net/ftp/status_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#status" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the STAT command to the server" do
+ @ftp.status
+ @ftp.last_response.should == "211 System status, or system help reply. (STAT)\n"
+ end
+
+ ruby_version_is "2.4" do
+ it "sends the STAT command with an optional parameter to the server" do
+ @ftp.status("/pub").should == "211 System status, or system help reply. (STAT /pub)\n"
+ end
+ end
+
+ it "returns the received information" do
+ @ftp.status.should == "211 System status, or system help reply. (STAT)\n"
+ end
+
+ it "does not raise an error when the response code is 212" do
+ @server.should_receive(:stat).and_respond("212 Directory status.")
+ lambda { @ftp.status }.should_not raise_error
+ end
+
+ it "does not raise an error when the response code is 213" do
+ @server.should_receive(:stat).and_respond("213 File status.")
+ lambda { @ftp.status }.should_not raise_error
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:stat).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.status }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:stat).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.status }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:stat).and_respond("502 Command not implemented.")
+ lambda { @ftp.status }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:stat).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.status }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 530" do
+ @server.should_receive(:stat).and_respond("530 Requested action not taken.")
+ lambda { @ftp.status }.should raise_error(Net::FTPPermError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/storbinary_spec.rb b/spec/ruby/library/net/ftp/storbinary_spec.rb
new file mode 100644
index 0000000000..eb65db1e8d
--- /dev/null
+++ b/spec/ruby/library/net/ftp/storbinary_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#storbinary" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @local_fixture_file = File.dirname(__FILE__) + "/fixtures/putbinaryfile"
+ @tmp_file = tmp("binaryfile", false)
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+
+ rm_r @tmp_file
+ end
+
+ it "sends the passed command and the passed File object's content to the server" do
+ File.open(@local_fixture_file) do |f|
+ f.binmode
+
+ @ftp.storbinary("STOR binary", f, 4096) {}
+ @ftp.last_response.should == "200 OK, Data received. (STOR binary)\n"
+ end
+ end
+
+ it "yields the transmitted content as binary blocks of the passed size" do
+ File.open(@local_fixture_file) do |f|
+ f.binmode
+
+ res = []
+ @ftp.storbinary("STOR binary", f, 10) { |x| res << x }
+ res.should == [
+ "This is an", " example f",
+ "ile\nwhich ", "is going t",
+ "o be trans", "mitted\nusi",
+ "ng #putbin", "aryfile.\n"
+ ]
+ end
+ end
+end
diff --git a/spec/ruby/library/net/ftp/storlines_spec.rb b/spec/ruby/library/net/ftp/storlines_spec.rb
new file mode 100644
index 0000000000..4affa37eee
--- /dev/null
+++ b/spec/ruby/library/net/ftp/storlines_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#storlines" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @local_fixture_file = File.dirname(__FILE__) + "/fixtures/puttextfile"
+ @tmp_file = tmp("textfile", false)
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+
+ rm_r @tmp_file
+ end
+
+ it "sends the passed command and the passed File object's content to the server" do
+ File.open(@local_fixture_file) do |f|
+ @ftp.storlines("STOR text", f) {}
+ @ftp.last_response.should == "200 OK, Data received. (STOR text)\n"
+ end
+ end
+
+ it "yields each line of the transmitted content" do
+ File.open(@local_fixture_file) do |f|
+ res = []
+ @ftp.storlines("STOR text", f) { |x| res << x }
+ res.should == [
+ "This is an example file\r\n",
+ "which is going to be transmitted\r\n",
+ "using #puttextfile.\r\n"
+ ]
+ end
+ end
+end
diff --git a/spec/ruby/library/net/ftp/system_spec.rb b/spec/ruby/library/net/ftp/system_spec.rb
new file mode 100644
index 0000000000..73a5ddc72f
--- /dev/null
+++ b/spec/ruby/library/net/ftp/system_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#system" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the SYST command to the server" do
+ @ftp.system
+ @ftp.last_response.should =~ /\A215 FTP Dummy Server \(SYST\)\Z/
+ end
+
+ it "returns the received information" do
+ @ftp.system.should =~ /\AFTP Dummy Server \(SYST\)\Z/
+ end
+
+ it "raises a Net::FTPPermError when the response code is 500" do
+ @server.should_receive(:syst).and_respond("500 Syntax error, command unrecognized.")
+ lambda { @ftp.system }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 501" do
+ @server.should_receive(:syst).and_respond("501 Syntax error in parameters or arguments.")
+ lambda { @ftp.system }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 502" do
+ @server.should_receive(:syst).and_respond("502 Command not implemented.")
+ lambda { @ftp.system }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 421" do
+ @server.should_receive(:syst).and_respond("421 Service not available, closing control connection.")
+ lambda { @ftp.system }.should raise_error(Net::FTPTempError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/voidcmd_spec.rb b/spec/ruby/library/net/ftp/voidcmd_spec.rb
new file mode 100644
index 0000000000..5e82b70a78
--- /dev/null
+++ b/spec/ruby/library/net/ftp/voidcmd_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#voidcmd" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "sends the passed command to the server" do
+ @server.should_receive(:help).and_respond("2xx Does not raise.")
+ lambda { @ftp.voidcmd("HELP") }.should_not raise_error
+ end
+
+ it "returns nil" do
+ @server.should_receive(:help).and_respond("2xx Does not raise.")
+ @ftp.voidcmd("HELP").should be_nil
+ end
+
+ it "raises a Net::FTPReplyError when the response code is 1xx" do
+ @server.should_receive(:help).and_respond("1xx Does raise a Net::FTPReplyError.")
+ lambda { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPReplyError)
+ end
+
+ it "raises a Net::FTPReplyError when the response code is 3xx" do
+ @server.should_receive(:help).and_respond("3xx Does raise a Net::FTPReplyError.")
+ lambda { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPReplyError)
+ end
+
+ it "raises a Net::FTPTempError when the response code is 4xx" do
+ @server.should_receive(:help).and_respond("4xx Does raise a Net::FTPTempError.")
+ lambda { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPTempError)
+ end
+
+ it "raises a Net::FTPPermError when the response code is 5xx" do
+ @server.should_receive(:help).and_respond("5xx Does raise a Net::FTPPermError.")
+ lambda { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPPermError)
+ end
+
+ it "raises a Net::FTPProtoError when the response code is not valid" do
+ @server.should_receive(:help).and_respond("999 Does raise a Net::FTPProtoError.")
+ lambda { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPProtoError)
+ end
+end
diff --git a/spec/ruby/library/net/ftp/welcome_spec.rb b/spec/ruby/library/net/ftp/welcome_spec.rb
new file mode 100644
index 0000000000..a0d7483949
--- /dev/null
+++ b/spec/ruby/library/net/ftp/welcome_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require_relative 'spec_helper'
+require_relative 'fixtures/server'
+
+describe "Net::FTP#welcome" do
+ before :each do
+ @server = NetFTPSpecs::DummyFTP.new
+ @server.serve_once
+
+ @ftp = Net::FTP.new
+ @ftp.connect(@server.hostname, @server.server_port)
+ end
+
+ after :each do
+ @ftp.quit rescue nil
+ @ftp.close
+ @server.stop
+ end
+
+ it "returns the server's welcome message" do
+ @ftp.welcome.should be_nil
+ @ftp.login
+ @ftp.welcome.should == "230 User logged in, proceed. (USER anonymous)\n"
+ end
+end
diff --git a/spec/ruby/library/net/http/HTTPBadResponse_spec.rb b/spec/ruby/library/net/http/HTTPBadResponse_spec.rb
new file mode 100644
index 0000000000..be644968f5
--- /dev/null
+++ b/spec/ruby/library/net/http/HTTPBadResponse_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPBadResponse" do
+ it "is a subclass of StandardError" do
+ Net::HTTPBadResponse.should < StandardError
+ end
+end
diff --git a/spec/ruby/library/net/http/HTTPClientExcepton_spec.rb b/spec/ruby/library/net/http/HTTPClientExcepton_spec.rb
new file mode 100644
index 0000000000..08347464ec
--- /dev/null
+++ b/spec/ruby/library/net/http/HTTPClientExcepton_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+ruby_version_is "2.6" do
+ describe "Net::HTTPClientException" do
+ it "is a subclass of Net::ProtoServerError" do
+ Net::HTTPClientException.should < Net::ProtoServerError
+ end
+
+ it "includes the Net::HTTPExceptions module" do
+ Net::HTTPClientException.should < Net::HTTPExceptions
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/HTTPError_spec.rb b/spec/ruby/library/net/http/HTTPError_spec.rb
new file mode 100644
index 0000000000..ab5f491bb7
--- /dev/null
+++ b/spec/ruby/library/net/http/HTTPError_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPError" do
+ it "is a subclass of Net::ProtocolError" do
+ Net::HTTPError.should < Net::ProtocolError
+ end
+
+ it "includes the Net::HTTPExceptions module" do
+ Net::HTTPError.should < Net::HTTPExceptions
+ end
+end
diff --git a/spec/ruby/library/net/http/HTTPFatalError_spec.rb b/spec/ruby/library/net/http/HTTPFatalError_spec.rb
new file mode 100644
index 0000000000..6ab36bff6c
--- /dev/null
+++ b/spec/ruby/library/net/http/HTTPFatalError_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPFatalError" do
+ it "is a subclass of Net::ProtoFatalError" do
+ Net::HTTPFatalError.should < Net::ProtoFatalError
+ end
+
+ it "includes the Net::HTTPExceptions module" do
+ Net::HTTPFatalError.should < Net::HTTPExceptions
+ end
+end
diff --git a/spec/ruby/library/net/http/HTTPHeaderSyntaxError_spec.rb b/spec/ruby/library/net/http/HTTPHeaderSyntaxError_spec.rb
new file mode 100644
index 0000000000..38e9454f99
--- /dev/null
+++ b/spec/ruby/library/net/http/HTTPHeaderSyntaxError_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPHeaderSyntaxError" do
+ it "is a subclass of StandardError" do
+ Net::HTTPHeaderSyntaxError.should < StandardError
+ end
+end
diff --git a/spec/ruby/library/net/http/HTTPRetriableError_spec.rb b/spec/ruby/library/net/http/HTTPRetriableError_spec.rb
new file mode 100644
index 0000000000..3a4bb9146c
--- /dev/null
+++ b/spec/ruby/library/net/http/HTTPRetriableError_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPRetriableError" do
+ it "is a subclass of Net::ProtoRetriableError" do
+ Net::HTTPRetriableError.should < Net::ProtoRetriableError
+ end
+
+ it "includes the Net::HTTPExceptions module" do
+ Net::HTTPRetriableError.should < Net::HTTPExceptions
+ end
+end
diff --git a/spec/ruby/library/net/http/HTTPServerException_spec.rb b/spec/ruby/library/net/http/HTTPServerException_spec.rb
new file mode 100644
index 0000000000..3960effadd
--- /dev/null
+++ b/spec/ruby/library/net/http/HTTPServerException_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+require 'net/http'
+
+ruby_version_is ""..."2.6" do
+ describe "Net::HTTPServerException" do
+ it "is a subclass of Net::ProtoServerError" do
+ Net::HTTPServerException.should < Net::ProtoServerError
+ end
+
+ it "includes the Net::HTTPExceptions module" do
+ Net::HTTPServerException.should < Net::HTTPExceptions
+ end
+ end
+end
+
+ruby_version_is "2.6" do
+ describe "Net::HTTPServerException" do
+ it "is a subclass of Net::ProtoServerError and is warned as deprecated" do
+ lambda { Net::HTTPServerException.should < Net::ProtoServerError }.should complain(/warning: constant Net::HTTPServerException is deprecated/)
+ end
+
+ it "includes the Net::HTTPExceptions module and is warned as deprecated" do
+ lambda { Net::HTTPServerException.should < Net::HTTPExceptions }.should complain(/warning: constant Net::HTTPServerException is deprecated/)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/Proxy_spec.rb b/spec/ruby/library/net/http/http/Proxy_spec.rb
new file mode 100644
index 0000000000..f85ccc0ee9
--- /dev/null
+++ b/spec/ruby/library/net/http/http/Proxy_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.Proxy" do
+ it "returns a new subclass of Net::HTTP" do
+ Net::HTTP.Proxy("localhost").should < Net::HTTP
+ end
+
+ it "returns Net::HTTP when the passed address is nil" do
+ Net::HTTP.Proxy(nil).should == Net::HTTP
+ end
+
+ it "sets the returned subclasses' proxy options based on the passed arguments" do
+ http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
+ http_with_proxy.proxy_address.should == "localhost"
+ http_with_proxy.proxy_port.should eql(1234)
+ http_with_proxy.proxy_user.should == "rspec"
+ http_with_proxy.proxy_pass.should == "rocks"
+ end
+end
+
+describe "Net::HTTP#proxy?" do
+ describe "when self is no proxy class instance" do
+ it "returns false" do
+ Net::HTTP.new("localhost", 3333).proxy?.should be_false
+ end
+ end
+
+ describe "when self is a proxy class instance" do
+ it "returns false" do
+ http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
+ http_with_proxy.new("localhost", 3333).proxy?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/active_spec.rb b/spec/ruby/library/net/http/http/active_spec.rb
new file mode 100644
index 0000000000..ef657243ba
--- /dev/null
+++ b/spec/ruby/library/net/http/http/active_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/started'
+
+describe "Net::HTTP#active?" do
+ it_behaves_like :net_http_started_p, :active?
+end
diff --git a/spec/ruby/library/net/http/http/address_spec.rb b/spec/ruby/library/net/http/http/address_spec.rb
new file mode 100644
index 0000000000..5fce76d767
--- /dev/null
+++ b/spec/ruby/library/net/http/http/address_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP#address" do
+ it "returns the current host name" do
+ net = Net::HTTP.new("localhost")
+ net.address.should == "localhost"
+ end
+end
diff --git a/spec/ruby/library/net/http/http/close_on_empty_response_spec.rb b/spec/ruby/library/net/http/http/close_on_empty_response_spec.rb
new file mode 100644
index 0000000000..a97b7b6c43
--- /dev/null
+++ b/spec/ruby/library/net/http/http/close_on_empty_response_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP#close_on_empty_response" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "Net::HTTP#close_on_empty_response=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/net/http/http/copy_spec.rb b/spec/ruby/library/net/http/http/copy_spec.rb
new file mode 100644
index 0000000000..5ebfdc334e
--- /dev/null
+++ b/spec/ruby/library/net/http/http/copy_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#copy" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends a COPY request to the passed path and returns the response" do
+ response = @http.copy("/request")
+ response.should be_kind_of(Net::HTTPResponse)
+ response.body.should == "Request type: COPY"
+ end
+end
diff --git a/spec/ruby/library/net/http/http/default_port_spec.rb b/spec/ruby/library/net/http/http/default_port_spec.rb
new file mode 100644
index 0000000000..30db18efae
--- /dev/null
+++ b/spec/ruby/library/net/http/http/default_port_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.default_port" do
+ it "returns 80" do
+ Net::HTTP.http_default_port.should eql(80)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/delete_spec.rb b/spec/ruby/library/net/http/http/delete_spec.rb
new file mode 100644
index 0000000000..160c653115
--- /dev/null
+++ b/spec/ruby/library/net/http/http/delete_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#delete" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends a DELETE request to the passed path and returns the response" do
+ response = @http.delete("/request")
+ response.should be_kind_of(Net::HTTPResponse)
+ response.body.should == "Request type: DELETE"
+ end
+end
diff --git a/spec/ruby/library/net/http/http/finish_spec.rb b/spec/ruby/library/net/http/http/finish_spec.rb
new file mode 100644
index 0000000000..b031c58e2c
--- /dev/null
+++ b/spec/ruby/library/net/http/http/finish_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#finish" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.new("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when self has been started" do
+ it "closes the tcp connection" do
+ @http.start
+ @http.finish
+ @http.started?.should be_false
+ end
+ end
+
+ describe "when self has not been started yet" do
+ it "raises an IOError" do
+ lambda { @http.finish }.should raise_error(IOError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/fixtures/http_server.rb b/spec/ruby/library/net/http/http/fixtures/http_server.rb
new file mode 100644
index 0000000000..49e8f437cc
--- /dev/null
+++ b/spec/ruby/library/net/http/http/fixtures/http_server.rb
@@ -0,0 +1,105 @@
+require 'webrick'
+require 'webrick/httpservlet/abstract'
+
+module NetHTTPSpecs
+ class NullWriter
+ def <<(s) end
+ def puts(*args) end
+ def print(*args) end
+ def printf(*args) end
+ end
+
+ class SpecServlet < WEBrick::HTTPServlet::AbstractServlet
+ def handle(req, res)
+ reply(req, res)
+ end
+
+ %w{ do_GET do_HEAD do_POST do_PUT do_PROPPATCH do_LOCK do_UNLOCK
+ do_OPTIONS do_PROPFIND do_DELETE do_MOVE do_COPY
+ do_MKCOL do_TRACE }.each do |method|
+ alias_method method.to_sym, :handle
+ end
+ end
+
+ class RequestServlet < SpecServlet
+ def reply(req, res)
+ res.content_type = "text/plain"
+ res.body = "Request type: #{req.request_method}"
+ end
+ end
+
+ class RequestBodyServlet < SpecServlet
+ def reply(req, res)
+ res.content_type = "text/plain"
+ res.body = req.body
+ end
+ end
+
+ class RequestHeaderServlet < SpecServlet
+ def reply(req, res)
+ res.content_type = "text/plain"
+ res.body = req.header.inspect
+ end
+ end
+
+ class RequestBasicAuthServlet < SpecServlet
+ def reply(req, res)
+ res.content_type = "text/plain"
+
+ WEBrick::HTTPAuth.basic_auth(req, res, "realm") do |user, pass|
+ res.body = "username: #{user}\npassword: #{pass}"
+ true
+ end
+ end
+ end
+
+ class << self
+ @server = nil
+ @server_thread = nil
+
+ def port
+ raise "server not started" unless @server
+ @server.config[:Port]
+ end
+
+ def start_server
+ server_config = {
+ BindAddress: "127.0.0.1",
+ Port: 0,
+ Logger: WEBrick::Log.new(NullWriter.new),
+ AccessLog: [],
+ ServerType: Thread
+ }
+
+ @server = WEBrick::HTTPServer.new(server_config)
+
+ @server.mount_proc('/') do |req, res|
+ res.content_type = "text/plain"
+ res.body = "This is the index page."
+ end
+ @server.mount('/request', RequestServlet)
+ @server.mount("/request/body", RequestBodyServlet)
+ @server.mount("/request/header", RequestHeaderServlet)
+ @server.mount("/request/basic_auth", RequestBasicAuthServlet)
+
+ @server_thread = @server.start
+ end
+
+ def stop_server
+ if @server
+ begin
+ @server.shutdown
+ rescue Errno::EPIPE
+ # Because WEBrick is not thread-safe and only catches IOError
+ end
+ @server = nil
+ end
+ if @server_thread
+ @server_thread.join
+ @server_thread = nil
+ end
+ timeout = WEBrick::Utils::TimeoutHandler
+ timeout.terminate if timeout.respond_to?(:terminate)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/get2_spec.rb b/spec/ruby/library/net/http/http/get2_spec.rb
new file mode 100644
index 0000000000..71dfc3d39b
--- /dev/null
+++ b/spec/ruby/library/net/http/http/get2_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_get'
+
+describe "Net::HTTP#get2" do
+ it_behaves_like :net_ftp_request_get, :get2
+end
diff --git a/spec/ruby/library/net/http/http/get_print_spec.rb b/spec/ruby/library/net/http/http/get_print_spec.rb
new file mode 100644
index 0000000000..6174e3eb21
--- /dev/null
+++ b/spec/ruby/library/net/http/http/get_print_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP.get_print" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @port = NetHTTPSpecs.port
+ end
+
+ after :each do
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when passed URI" do
+ it "it prints the body of the specified uri to $stdout" do
+ lambda do
+ Net::HTTP.get_print URI.parse("http://localhost:#{@port}/")
+ end.should output(/This is the index page\./)
+ end
+ end
+
+ describe "when passed host, path, port" do
+ it "it prints the body of the specified uri to $stdout" do
+ lambda do
+ Net::HTTP.get_print 'localhost', "/", @port
+ end.should output(/This is the index page\./)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/get_response_spec.rb b/spec/ruby/library/net/http/http/get_response_spec.rb
new file mode 100644
index 0000000000..941b35e773
--- /dev/null
+++ b/spec/ruby/library/net/http/http/get_response_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP.get_response" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @port = NetHTTPSpecs.port
+ end
+
+ after :each do
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when passed URI" do
+ it "returns the response for the specified uri" do
+ res = Net::HTTP.get_response(URI.parse("http://localhost:#{@port}/"))
+ res.content_type.should == "text/plain"
+ res.body.should == "This is the index page."
+ end
+ end
+
+ describe "when passed host, path, port" do
+ it "returns the response for the specified host-path-combination" do
+ res = Net::HTTP.get_response('localhost', "/", @port)
+ res.content_type.should == "text/plain"
+ res.body.should == "This is the index page."
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/get_spec.rb b/spec/ruby/library/net/http/http/get_spec.rb
new file mode 100644
index 0000000000..e6cd50fa27
--- /dev/null
+++ b/spec/ruby/library/net/http/http/get_spec.rb
@@ -0,0 +1,95 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP.get when passed URI" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @port = NetHTTPSpecs.port
+ end
+
+ after :each do
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when passed URI" do
+ it "returns the body of the specified uri" do
+ Net::HTTP.get(URI.parse("http://localhost:#{@port}/")).should == "This is the index page."
+ end
+ end
+
+ describe "when passed host, path, port" do
+ it "returns the body of the specified host-path-combination" do
+ Net::HTTP.get('localhost', "/", @port).should == "This is the index page."
+ end
+ end
+end
+
+quarantine! do # These specs fail frequently with CHECK_LEAKS=true
+describe "Net::HTTP.get" do
+ describe "when reading gzipped contents" do
+ def start_threads
+ require 'zlib'
+
+ server = nil
+ server_thread = Thread.new do
+ server = TCPServer.new("127.0.0.1", 0)
+ begin
+ c = server.accept
+ ensure
+ server.close
+ end
+ c.print "HTTP/1.1 200\r\n"
+ c.print "Content-Type: text/plain\r\n"
+ c.print "Content-Encoding: gzip\r\n"
+ s = StringIO.new
+ z = Zlib::GzipWriter.new(s)
+ begin
+ z.write 'Hello World!'
+ ensure
+ z.close
+ end
+ c.print "Content-Length: #{s.length}\r\n\r\n"
+ # Write partial gzip content
+ c.write s.string.byteslice(0..-2)
+ c.flush
+ c
+ end
+ Thread.pass until server && server_thread.stop?
+
+ client_thread = Thread.new do
+ Thread.current.report_on_exception = false
+ Net::HTTP.get("127.0.0.1", '/', server.connect_address.ip_port)
+ end
+ Thread.pass until client_thread.stop?
+
+ [server_thread, client_thread]
+ end
+
+ it "propagates exceptions interrupting the thread and does not replace it with Zlib::BufError" do
+ my_exception = Class.new(RuntimeError)
+ server_thread, client_thread = start_threads
+ socket = server_thread.value
+ begin
+ client_thread.raise my_exception, "my exception"
+ -> { client_thread.value }.should raise_error(my_exception)
+ ensure
+ socket.close
+ end
+ end
+
+ ruby_version_is "2.8" do # https://bugs.ruby-lang.org/issues/13882#note-6
+ it "lets the kill Thread exception goes through and does not replace it with Zlib::BufError" do
+ server_thread, client_thread = start_threads
+ socket = server_thread.value
+ begin
+ client_thread.kill
+ client_thread.value.should == nil
+ ensure
+ socket.close
+ end
+ end
+ end
+ end
+end
+end
diff --git a/spec/ruby/library/net/http/http/head2_spec.rb b/spec/ruby/library/net/http/http/head2_spec.rb
new file mode 100644
index 0000000000..606798e4af
--- /dev/null
+++ b/spec/ruby/library/net/http/http/head2_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_head'
+
+describe "Net::HTTP#head2" do
+ it_behaves_like :net_ftp_request_head, :head2
+end
diff --git a/spec/ruby/library/net/http/http/head_spec.rb b/spec/ruby/library/net/http/http/head_spec.rb
new file mode 100644
index 0000000000..925a8e6043
--- /dev/null
+++ b/spec/ruby/library/net/http/http/head_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#head" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends a HEAD request to the passed path and returns the response" do
+ response = @http.head("/request")
+ # HEAD requests have no responses
+ response.body.should be_nil
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.head("/request").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/http_default_port_spec.rb b/spec/ruby/library/net/http/http/http_default_port_spec.rb
new file mode 100644
index 0000000000..cf7f73e630
--- /dev/null
+++ b/spec/ruby/library/net/http/http/http_default_port_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.http_default_port" do
+ it "returns 80" do
+ Net::HTTP.http_default_port.should eql(80)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/https_default_port_spec.rb b/spec/ruby/library/net/http/http/https_default_port_spec.rb
new file mode 100644
index 0000000000..fbf0bd1abc
--- /dev/null
+++ b/spec/ruby/library/net/http/http/https_default_port_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.https_default_port" do
+ it "returns 443" do
+ Net::HTTP.https_default_port.should eql(443)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/initialize_spec.rb b/spec/ruby/library/net/http/http/initialize_spec.rb
new file mode 100644
index 0000000000..7683713a0e
--- /dev/null
+++ b/spec/ruby/library/net/http/http/initialize_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP#initialize" do
+ it "is private" do
+ Net::HTTP.should have_private_instance_method(:initialize)
+ end
+
+ describe "when passed address" do
+ before :each do
+ @net = Net::HTTP.allocate
+ @net.send(:initialize, "localhost")
+ end
+
+ it "sets the new Net::HTTP instance's address to the passed address" do
+ @net.address.should == "localhost"
+ end
+
+ it "sets the new Net::HTTP instance's port to the default HTTP port" do
+ @net.port.should eql(Net::HTTP.default_port)
+ end
+
+ it "does not start the new Net::HTTP instance" do
+ @net.started?.should be_false
+ end
+ end
+
+ describe "when passed address, port" do
+ before :each do
+ @net = Net::HTTP.allocate
+ @net.send(:initialize, "localhost", 3333)
+ end
+
+ it "sets the new Net::HTTP instance's address to the passed address" do
+ @net.address.should == "localhost"
+ end
+
+ it "sets the new Net::HTTP instance's port to the passed port" do
+ @net.port.should eql(3333)
+ end
+
+ it "does not start the new Net::HTTP instance" do
+ @net.started?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/inspect_spec.rb b/spec/ruby/library/net/http/http/inspect_spec.rb
new file mode 100644
index 0000000000..b1e799ca34
--- /dev/null
+++ b/spec/ruby/library/net/http/http/inspect_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#inspect" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @port = NetHTTPSpecs.port
+ @http = Net::HTTP.new("localhost", @port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "returns a String representation of self" do
+ @http.inspect.should be_kind_of(String)
+ @http.inspect.should == "#<Net::HTTP localhost:#{@port} open=false>"
+
+ @http.start
+ @http.inspect.should == "#<Net::HTTP localhost:#{@port} open=true>"
+ end
+end
diff --git a/spec/ruby/library/net/http/http/is_version_1_1_spec.rb b/spec/ruby/library/net/http/http/is_version_1_1_spec.rb
new file mode 100644
index 0000000000..f37695b777
--- /dev/null
+++ b/spec/ruby/library/net/http/http/is_version_1_1_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'shared/version_1_1'
+
+describe "Net::HTTP.is_version_1_1?" do
+ it_behaves_like :net_http_version_1_1_p, :is_version_1_1?
+end
diff --git a/spec/ruby/library/net/http/http/is_version_1_2_spec.rb b/spec/ruby/library/net/http/http/is_version_1_2_spec.rb
new file mode 100644
index 0000000000..82dbdc87aa
--- /dev/null
+++ b/spec/ruby/library/net/http/http/is_version_1_2_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'shared/version_1_2'
+
+describe "Net::HTTP.is_version_1_2?" do
+ it_behaves_like :net_http_version_1_2_p, :is_version_1_2?
+end
diff --git a/spec/ruby/library/net/http/http/lock_spec.rb b/spec/ruby/library/net/http/http/lock_spec.rb
new file mode 100644
index 0000000000..bb76607a8b
--- /dev/null
+++ b/spec/ruby/library/net/http/http/lock_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#lock" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends a LOCK request to the passed path and returns the response" do
+ response = @http.lock("/request", "test=test")
+ response.should be_kind_of(Net::HTTPResponse)
+ response.body.should == "Request type: LOCK"
+ end
+end
diff --git a/spec/ruby/library/net/http/http/mkcol_spec.rb b/spec/ruby/library/net/http/http/mkcol_spec.rb
new file mode 100644
index 0000000000..33017625e2
--- /dev/null
+++ b/spec/ruby/library/net/http/http/mkcol_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#mkcol" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends a MKCOL request to the passed path and returns the response" do
+ response = @http.mkcol("/request")
+ response.should be_kind_of(Net::HTTPResponse)
+ response.body.should == "Request type: MKCOL"
+ end
+end
diff --git a/spec/ruby/library/net/http/http/move_spec.rb b/spec/ruby/library/net/http/http/move_spec.rb
new file mode 100644
index 0000000000..4d6b828150
--- /dev/null
+++ b/spec/ruby/library/net/http/http/move_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#head" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends a MOVE request to the passed path and returns the response" do
+ response = @http.move("/request")
+ # HEAD requests have no responses
+ response.body.should == "Request type: MOVE"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.move("/request").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/new_spec.rb b/spec/ruby/library/net/http/http/new_spec.rb
new file mode 100644
index 0000000000..491d1d01fd
--- /dev/null
+++ b/spec/ruby/library/net/http/http/new_spec.rb
@@ -0,0 +1,86 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.new" do
+ describe "when passed address" do
+ before :each do
+ @http = Net::HTTP.new("localhost")
+ end
+
+ it "returns a Net::HTTP instance" do
+ @http.proxy?.should be_false
+ @http.instance_of?(Net::HTTP).should be_true
+ end
+
+ it "sets the new Net::HTTP instance's address to the passed address" do
+ @http.address.should == "localhost"
+ end
+
+ it "sets the new Net::HTTP instance's port to the default HTTP port" do
+ @http.port.should eql(Net::HTTP.default_port)
+ end
+
+ it "does not start the new Net::HTTP instance" do
+ @http.started?.should be_false
+ end
+ end
+
+ describe "when passed address, port" do
+ before :each do
+ @http = Net::HTTP.new("localhost", 3333)
+ end
+
+ it "returns a Net::HTTP instance" do
+ @http.proxy?.should be_false
+ @http.instance_of?(Net::HTTP).should be_true
+ end
+
+ it "sets the new Net::HTTP instance's address to the passed address" do
+ @http.address.should == "localhost"
+ end
+
+ it "sets the new Net::HTTP instance's port to the passed port" do
+ @http.port.should eql(3333)
+ end
+
+ it "does not start the new Net::HTTP instance" do
+ @http.started?.should be_false
+ end
+ end
+
+ describe "when passed address, port, *proxy_options" do
+ it "returns a Net::HTTP instance" do
+ http = Net::HTTP.new("localhost", 3333, "localhost")
+ http.proxy?.should be_true
+ http.instance_of?(Net::HTTP).should be_true
+ http.should be_kind_of(Net::HTTP)
+ end
+
+ it "correctly sets the passed Proxy options" do
+ http = Net::HTTP.new("localhost", 3333, "localhost")
+ http.proxy_address.should == "localhost"
+ http.proxy_port.should eql(80)
+ http.proxy_user.should be_nil
+ http.proxy_pass.should be_nil
+
+ http = Net::HTTP.new("localhost", 3333, "localhost", 1234)
+ http.proxy_address.should == "localhost"
+ http.proxy_port.should eql(1234)
+ http.proxy_user.should be_nil
+ http.proxy_pass.should be_nil
+
+ http = Net::HTTP.new("localhost", 3333, "localhost", 1234, "rubyspec")
+ http.proxy_address.should == "localhost"
+ http.proxy_port.should eql(1234)
+ http.proxy_user.should == "rubyspec"
+ http.proxy_pass.should be_nil
+
+ http = Net::HTTP.new("localhost", 3333, "localhost", 1234, "rubyspec", "rocks")
+ http.proxy_address.should == "localhost"
+ http.proxy_port.should eql(1234)
+ http.proxy_user.should == "rubyspec"
+ http.proxy_pass.should == "rocks"
+ end
+ end
+
+end
diff --git a/spec/ruby/library/net/http/http/newobj_spec.rb b/spec/ruby/library/net/http/http/newobj_spec.rb
new file mode 100644
index 0000000000..b261dcc5db
--- /dev/null
+++ b/spec/ruby/library/net/http/http/newobj_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.newobj" do
+ before :each do
+ @net = Net::HTTP.newobj("localhost")
+ end
+
+ describe "when passed address" do
+ it "returns a new Net::HTTP instance" do
+ @net.should be_kind_of(Net::HTTP)
+ end
+
+ it "sets the new Net::HTTP instance's address to the passed address" do
+ @net.address.should == "localhost"
+ end
+
+ it "sets the new Net::HTTP instance's port to the default HTTP port" do
+ @net.port.should eql(Net::HTTP.default_port)
+ end
+
+ it "does not start the new Net::HTTP instance" do
+ @net.started?.should be_false
+ end
+ end
+
+ describe "when passed address, port" do
+ before :each do
+ @net = Net::HTTP.newobj("localhost", 3333)
+ end
+
+ it "returns a new Net::HTTP instance" do
+ @net.should be_kind_of(Net::HTTP)
+ end
+
+ it "sets the new Net::HTTP instance's address to the passed address" do
+ @net.address.should == "localhost"
+ end
+
+ it "sets the new Net::HTTP instance's port to the passed port" do
+ @net.port.should eql(3333)
+ end
+
+ it "does not start the new Net::HTTP instance" do
+ @net.started?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/open_timeout_spec.rb b/spec/ruby/library/net/http/http/open_timeout_spec.rb
new file mode 100644
index 0000000000..44b33615e6
--- /dev/null
+++ b/spec/ruby/library/net/http/http/open_timeout_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP#open_timeout" do
+ it "returns the seconds to wait till the connection is open" do
+ net = Net::HTTP.new("localhost")
+ net.open_timeout.should eql(60)
+ net.open_timeout = 10
+ net.open_timeout.should eql(10)
+ end
+end
+
+describe "Net::HTTP#open_timeout=" do
+ it "sets the seconds to wait till the connection is open" do
+ net = Net::HTTP.new("localhost")
+ net.open_timeout = 10
+ net.open_timeout.should eql(10)
+ end
+
+ it "returns the newly set value" do
+ net = Net::HTTP.new("localhost")
+ (net.open_timeout = 10).should eql(10)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/options_spec.rb b/spec/ruby/library/net/http/http/options_spec.rb
new file mode 100644
index 0000000000..d798e69197
--- /dev/null
+++ b/spec/ruby/library/net/http/http/options_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#options" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends an options request to the passed path and returns the response" do
+ response = @http.options("/request")
+
+ response.body.should == "Request type: OPTIONS"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.options("/request").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/port_spec.rb b/spec/ruby/library/net/http/http/port_spec.rb
new file mode 100644
index 0000000000..7de295ca75
--- /dev/null
+++ b/spec/ruby/library/net/http/http/port_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP#port" do
+ it "returns the current port number" do
+ net = Net::HTTP.new("localhost", 3333)
+ net.port.should eql(3333)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/post2_spec.rb b/spec/ruby/library/net/http/http/post2_spec.rb
new file mode 100644
index 0000000000..eab9a6a1d2
--- /dev/null
+++ b/spec/ruby/library/net/http/http/post2_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_post'
+
+describe "Net::HTTP#post2" do
+ it_behaves_like :net_ftp_request_post, :post2
+end
diff --git a/spec/ruby/library/net/http/http/post_form_spec.rb b/spec/ruby/library/net/http/http/post_form_spec.rb
new file mode 100644
index 0000000000..891e05e7af
--- /dev/null
+++ b/spec/ruby/library/net/http/http/post_form_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP.post_form when passed URI" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @port = NetHTTPSpecs.port
+ end
+
+ after :each do
+ NetHTTPSpecs.stop_server
+ end
+
+ it "POSTs the passed form data to the given uri" do
+ uri = URI.parse("http://localhost:#{@port}/request/body")
+ data = { test: :data }
+
+ res = Net::HTTP.post_form(uri, data)
+ res.body.should == "test=data"
+ end
+end
diff --git a/spec/ruby/library/net/http/http/post_spec.rb b/spec/ruby/library/net/http/http/post_spec.rb
new file mode 100644
index 0000000000..c8d41b9617
--- /dev/null
+++ b/spec/ruby/library/net/http/http/post_spec.rb
@@ -0,0 +1,76 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require 'uri'
+require_relative 'fixtures/http_server'
+
+ruby_version_is '2.4' do
+ describe "Net::HTTP.post" do
+ before :each do
+ NetHTTPSpecs.start_server
+ end
+
+ after :each do
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends post request to the specified URI and returns response" do
+ response = Net::HTTP.post(
+ URI("http://localhost:#{NetHTTPSpecs.port}/request"),
+ '{ "q": "ruby", "max": "50" }',
+ "Content-Type" => "application/json")
+ response.body.should == "Request type: POST"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ response = Net::HTTP.post(URI("http://localhost:#{NetHTTPSpecs.port}/request"), "test=test")
+ response.should be_kind_of(Net::HTTPResponse)
+ end
+
+ it "sends Content-Type: application/x-www-form-urlencoded by default" do
+ response = Net::HTTP.post(URI("http://localhost:#{NetHTTPSpecs.port}/request/header"), "test=test")
+ response.body.should include('"content-type"=>["application/x-www-form-urlencoded"]')
+ end
+
+ it "does not support HTTP Basic Auth" do
+ response = Net::HTTP.post(
+ URI("http://john:qwerty@localhost:#{NetHTTPSpecs.port}/request/basic_auth"),
+ "test=test")
+ response.body.should == "username: \npassword: "
+ end
+ end
+end
+
+describe "Net::HTTP#post" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends an post request to the passed path and returns the response" do
+ response = @http.post("/request", "test=test")
+ response.body.should == "Request type: POST"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.post("/request", "test=test").should be_kind_of(Net::HTTPResponse)
+ end
+
+ describe "when passed a block" do
+ it "yields fragments of the response body to the passed block" do
+ str = ""
+ @http.post("/request", "test=test") do |res|
+ str << res
+ end
+ str.should == "Request type: POST"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.post("/request", "test=test") {}.should be_kind_of(Net::HTTPResponse)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/propfind_spec.rb b/spec/ruby/library/net/http/http/propfind_spec.rb
new file mode 100644
index 0000000000..5240171618
--- /dev/null
+++ b/spec/ruby/library/net/http/http/propfind_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#propfind" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends an propfind request to the passed path and returns the response" do
+ response = @http.propfind("/request", "test=test")
+ response.body.should == "Request type: PROPFIND"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.propfind("/request", "test=test").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/proppatch_spec.rb b/spec/ruby/library/net/http/http/proppatch_spec.rb
new file mode 100644
index 0000000000..7a761a9f23
--- /dev/null
+++ b/spec/ruby/library/net/http/http/proppatch_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#proppatch" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends an proppatch request to the passed path and returns the response" do
+ response = @http.proppatch("/request", "test=test")
+ response.body.should == "Request type: PROPPATCH"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.proppatch("/request", "test=test").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/proxy_address_spec.rb b/spec/ruby/library/net/http/http/proxy_address_spec.rb
new file mode 100644
index 0000000000..8d152b8d44
--- /dev/null
+++ b/spec/ruby/library/net/http/http/proxy_address_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.proxy_address" do
+ describe "when self is no proxy class" do
+ it "returns nil" do
+ Net::HTTP.proxy_address.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class" do
+ it "returns the address for self's proxy connection" do
+ Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_address.should == "localhost"
+ end
+ end
+end
+
+describe "Net::HTTP#proxy_address" do
+ describe "when self is no proxy class instance" do
+ it "returns nil" do
+ Net::HTTP.new("localhost", 3333).proxy_address.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class instance" do
+ it "returns the password for self's proxy connection" do
+ http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
+ http_with_proxy.new("localhost", 3333).proxy_address.should == "localhost"
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/proxy_class_spec.rb b/spec/ruby/library/net/http/http/proxy_class_spec.rb
new file mode 100644
index 0000000000..2aab804ed3
--- /dev/null
+++ b/spec/ruby/library/net/http/http/proxy_class_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.proxy_class?" do
+ it "returns true if sels is a class created with Net::HTTP.Proxy" do
+ Net::HTTP.proxy_class?.should be_false
+ Net::HTTP.Proxy("localhost").proxy_class?.should be_true
+ end
+end
diff --git a/spec/ruby/library/net/http/http/proxy_pass_spec.rb b/spec/ruby/library/net/http/http/proxy_pass_spec.rb
new file mode 100644
index 0000000000..94a0034544
--- /dev/null
+++ b/spec/ruby/library/net/http/http/proxy_pass_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.proxy_pass" do
+ describe "when self is no proxy class" do
+ it "returns nil" do
+ Net::HTTP.proxy_pass.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class" do
+ it "returns nil if no password was set for self's proxy connection" do
+ Net::HTTP.Proxy("localhost").proxy_pass.should be_nil
+ end
+
+ it "returns the password for self's proxy connection" do
+ Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_pass.should == "rocks"
+ end
+ end
+end
+
+describe "Net::HTTP#proxy_pass" do
+ describe "when self is no proxy class instance" do
+ it "returns nil" do
+ Net::HTTP.new("localhost", 3333).proxy_pass.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class instance" do
+ it "returns nil if no password was set for self's proxy connection" do
+ Net::HTTP.Proxy("localhost").new("localhost", 3333).proxy_pass.should be_nil
+ end
+
+ it "returns the password for self's proxy connection" do
+ http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
+ http_with_proxy.new("localhost", 3333).proxy_pass.should == "rocks"
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/proxy_port_spec.rb b/spec/ruby/library/net/http/http/proxy_port_spec.rb
new file mode 100644
index 0000000000..339f7ee850
--- /dev/null
+++ b/spec/ruby/library/net/http/http/proxy_port_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.proxy_port" do
+ describe "when self is no proxy class" do
+ it "returns nil" do
+ Net::HTTP.proxy_port.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class" do
+ it "returns 80 if no port was set for self's proxy connection" do
+ Net::HTTP.Proxy("localhost").proxy_port.should eql(80)
+ end
+
+ it "returns the port for self's proxy connection" do
+ Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_port.should eql(1234)
+ end
+ end
+end
+
+describe "Net::HTTP#proxy_port" do
+ describe "when self is no proxy class instance" do
+ it "returns nil" do
+ Net::HTTP.new("localhost", 3333).proxy_port.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class instance" do
+ it "returns 80 if no port was set for self's proxy connection" do
+ Net::HTTP.Proxy("localhost").new("localhost", 3333).proxy_port.should eql(80)
+ end
+
+ it "returns the port for self's proxy connection" do
+ http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
+ http_with_proxy.new("localhost", 3333).proxy_port.should eql(1234)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/proxy_user_spec.rb b/spec/ruby/library/net/http/http/proxy_user_spec.rb
new file mode 100644
index 0000000000..01fda400e9
--- /dev/null
+++ b/spec/ruby/library/net/http/http/proxy_user_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.proxy_user" do
+ describe "when self is no proxy class" do
+ it "returns nil" do
+ Net::HTTP.proxy_user.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class" do
+ it "returns nil if no username was set for self's proxy connection" do
+ Net::HTTP.Proxy("localhost").proxy_user.should be_nil
+ end
+
+ it "returns the username for self's proxy connection" do
+ Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_user.should == "rspec"
+ end
+ end
+end
+
+describe "Net::HTTP#proxy_user" do
+ describe "when self is no proxy class instance" do
+ it "returns nil" do
+ Net::HTTP.new("localhost", 3333).proxy_user.should be_nil
+ end
+ end
+
+ describe "when self is a proxy class instance" do
+ it "returns nil if no username was set for self's proxy connection" do
+ Net::HTTP.Proxy("localhost").new("localhost", 3333).proxy_user.should be_nil
+ end
+
+ it "returns the username for self's proxy connection" do
+ http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks")
+ http_with_proxy.new("localhost", 3333).proxy_user.should == "rspec"
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/put2_spec.rb b/spec/ruby/library/net/http/http/put2_spec.rb
new file mode 100644
index 0000000000..0ee3590639
--- /dev/null
+++ b/spec/ruby/library/net/http/http/put2_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_put'
+
+describe "Net::HTTP#put2" do
+ it_behaves_like :net_ftp_request_put, :put2
+end
diff --git a/spec/ruby/library/net/http/http/put_spec.rb b/spec/ruby/library/net/http/http/put_spec.rb
new file mode 100644
index 0000000000..3ca0d0963e
--- /dev/null
+++ b/spec/ruby/library/net/http/http/put_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#put" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends an put request to the passed path and returns the response" do
+ response = @http.put("/request", "test=test")
+ response.body.should == "Request type: PUT"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.put("/request", "test=test").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/read_timeout_spec.rb b/spec/ruby/library/net/http/http/read_timeout_spec.rb
new file mode 100644
index 0000000000..e23ee76025
--- /dev/null
+++ b/spec/ruby/library/net/http/http/read_timeout_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP#read_timeout" do
+ it "returns the seconds to wait until reading one block" do
+ net = Net::HTTP.new("localhost")
+ net.read_timeout.should eql(60)
+ net.read_timeout = 10
+ net.read_timeout.should eql(10)
+ end
+end
+
+describe "Net::HTTP#read_timeout=" do
+ it "sets the seconds to wait till the connection is open" do
+ net = Net::HTTP.new("localhost")
+ net.read_timeout = 10
+ net.read_timeout.should eql(10)
+ end
+
+ it "returns the newly set value" do
+ net = Net::HTTP.new("localhost")
+ (net.read_timeout = 10).should eql(10)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/request_get_spec.rb b/spec/ruby/library/net/http/http/request_get_spec.rb
new file mode 100644
index 0000000000..f53a2e9d65
--- /dev/null
+++ b/spec/ruby/library/net/http/http/request_get_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_get'
+
+describe "Net::HTTP#request_get" do
+ it_behaves_like :net_ftp_request_get, :get2
+end
diff --git a/spec/ruby/library/net/http/http/request_head_spec.rb b/spec/ruby/library/net/http/http/request_head_spec.rb
new file mode 100644
index 0000000000..dc47557b9d
--- /dev/null
+++ b/spec/ruby/library/net/http/http/request_head_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_head'
+
+describe "Net::HTTP#request_head" do
+ it_behaves_like :net_ftp_request_head, :request_head
+end
diff --git a/spec/ruby/library/net/http/http/request_post_spec.rb b/spec/ruby/library/net/http/http/request_post_spec.rb
new file mode 100644
index 0000000000..0b408fa84d
--- /dev/null
+++ b/spec/ruby/library/net/http/http/request_post_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_post'
+
+describe "Net::HTTP#request_post" do
+ it_behaves_like :net_ftp_request_post, :request_post
+end
diff --git a/spec/ruby/library/net/http/http/request_put_spec.rb b/spec/ruby/library/net/http/http/request_put_spec.rb
new file mode 100644
index 0000000000..987b52ceb0
--- /dev/null
+++ b/spec/ruby/library/net/http/http/request_put_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/request_put'
+
+describe "Net::HTTP#request_put" do
+ it_behaves_like :net_ftp_request_put, :request_put
+end
diff --git a/spec/ruby/library/net/http/http/request_spec.rb b/spec/ruby/library/net/http/http/request_spec.rb
new file mode 100644
index 0000000000..e63dde9c8d
--- /dev/null
+++ b/spec/ruby/library/net/http/http/request_spec.rb
@@ -0,0 +1,109 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#request" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when passed request_object" do
+ it "makes a HTTP Request based on the passed request_object" do
+ response = @http.request(Net::HTTP::Get.new("/request"), "test=test")
+ response.body.should == "Request type: GET"
+
+ response = @http.request(Net::HTTP::Head.new("/request"), "test=test")
+ response.body.should be_nil
+
+ response = @http.request(Net::HTTP::Post.new("/request"), "test=test")
+ response.body.should == "Request type: POST"
+
+ response = @http.request(Net::HTTP::Put.new("/request"), "test=test")
+ response.body.should == "Request type: PUT"
+
+ response = @http.request(Net::HTTP::Proppatch.new("/request"), "test=test")
+ response.body.should == "Request type: PROPPATCH"
+
+ response = @http.request(Net::HTTP::Lock.new("/request"), "test=test")
+ response.body.should == "Request type: LOCK"
+
+ response = @http.request(Net::HTTP::Unlock.new("/request"), "test=test")
+ response.body.should == "Request type: UNLOCK"
+
+ # TODO: Does not work?
+ #response = @http.request(Net::HTTP::Options.new("/request"), "test=test")
+ #response.body.should be_nil
+
+ response = @http.request(Net::HTTP::Propfind.new("/request"), "test=test")
+ response.body.should == "Request type: PROPFIND"
+
+ response = @http.request(Net::HTTP::Delete.new("/request"), "test=test")
+ response.body.should == "Request type: DELETE"
+
+ response = @http.request(Net::HTTP::Move.new("/request"), "test=test")
+ response.body.should == "Request type: MOVE"
+
+ response = @http.request(Net::HTTP::Copy.new("/request"), "test=test")
+ response.body.should == "Request type: COPY"
+
+ response = @http.request(Net::HTTP::Mkcol.new("/request"), "test=test")
+ response.body.should == "Request type: MKCOL"
+
+ response = @http.request(Net::HTTP::Trace.new("/request"), "test=test")
+ response.body.should == "Request type: TRACE"
+ end
+ end
+
+ describe "when passed request_object and request_body" do
+ it "sends the passed request_body when making the HTTP Request" do
+ response = @http.request(Net::HTTP::Get.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Head.new("/request/body"), "test=test")
+ response.body.should be_nil
+
+ response = @http.request(Net::HTTP::Post.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Put.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Proppatch.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Lock.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Unlock.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ # TODO: Does not work?
+ #response = @http.request(Net::HTTP::Options.new("/request/body"), "test=test")
+ #response.body.should be_nil
+
+ response = @http.request(Net::HTTP::Propfind.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Delete.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Move.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Copy.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Mkcol.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+
+ response = @http.request(Net::HTTP::Trace.new("/request/body"), "test=test")
+ response.body.should == "test=test"
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/request_types_spec.rb b/spec/ruby/library/net/http/http/request_types_spec.rb
new file mode 100644
index 0000000000..99d754d3d1
--- /dev/null
+++ b/spec/ruby/library/net/http/http/request_types_spec.rb
@@ -0,0 +1,254 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP::Get" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Get.should < Net::HTTPRequest
+ end
+
+ it "represents the 'GET'-Request-Method" do
+ Net::HTTP::Get::METHOD.should == "GET"
+ end
+
+ it "has no Request Body" do
+ Net::HTTP::Get::REQUEST_HAS_BODY.should be_false
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Get::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Head" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Head.should < Net::HTTPRequest
+ end
+
+ it "represents the 'HEAD'-Request-Method" do
+ Net::HTTP::Head::METHOD.should == "HEAD"
+ end
+
+ it "has no Request Body" do
+ Net::HTTP::Head::REQUEST_HAS_BODY.should be_false
+ end
+
+ it "has no Response Body" do
+ Net::HTTP::Head::RESPONSE_HAS_BODY.should be_false
+ end
+end
+
+describe "Net::HTTP::Post" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Post.should < Net::HTTPRequest
+ end
+
+ it "represents the 'POST'-Request-Method" do
+ Net::HTTP::Post::METHOD.should == "POST"
+ end
+
+ it "has a Request Body" do
+ Net::HTTP::Post::REQUEST_HAS_BODY.should be_true
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Post::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Put" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Put.should < Net::HTTPRequest
+ end
+
+ it "represents the 'PUT'-Request-Method" do
+ Net::HTTP::Put::METHOD.should == "PUT"
+ end
+
+ it "has a Request Body" do
+ Net::HTTP::Put::REQUEST_HAS_BODY.should be_true
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Put::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Delete" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Delete.should < Net::HTTPRequest
+ end
+
+ it "represents the 'DELETE'-Request-Method" do
+ Net::HTTP::Delete::METHOD.should == "DELETE"
+ end
+
+ it "has no Request Body" do
+ Net::HTTP::Delete::REQUEST_HAS_BODY.should be_false
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Delete::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Options" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Options.should < Net::HTTPRequest
+ end
+
+ it "represents the 'OPTIONS'-Request-Method" do
+ Net::HTTP::Options::METHOD.should == "OPTIONS"
+ end
+
+ it "has no Request Body" do
+ Net::HTTP::Options::REQUEST_HAS_BODY.should be_false
+ end
+
+ it "has no Response Body" do
+ Net::HTTP::Options::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Trace" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Trace.should < Net::HTTPRequest
+ end
+
+ it "represents the 'TRACE'-Request-Method" do
+ Net::HTTP::Trace::METHOD.should == "TRACE"
+ end
+
+ it "has no Request Body" do
+ Net::HTTP::Trace::REQUEST_HAS_BODY.should be_false
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Trace::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Propfind" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Propfind.should < Net::HTTPRequest
+ end
+
+ it "represents the 'PROPFIND'-Request-Method" do
+ Net::HTTP::Propfind::METHOD.should == "PROPFIND"
+ end
+
+ it "has a Request Body" do
+ Net::HTTP::Propfind::REQUEST_HAS_BODY.should be_true
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Propfind::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Proppatch" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Proppatch.should < Net::HTTPRequest
+ end
+
+ it "represents the 'PROPPATCH'-Request-Method" do
+ Net::HTTP::Proppatch::METHOD.should == "PROPPATCH"
+ end
+
+ it "has a Request Body" do
+ Net::HTTP::Proppatch::REQUEST_HAS_BODY.should be_true
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Proppatch::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Mkcol" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Mkcol.should < Net::HTTPRequest
+ end
+
+ it "represents the 'MKCOL'-Request-Method" do
+ Net::HTTP::Mkcol::METHOD.should == "MKCOL"
+ end
+
+ it "has a Request Body" do
+ Net::HTTP::Mkcol::REQUEST_HAS_BODY.should be_true
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Mkcol::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Copy" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Copy.should < Net::HTTPRequest
+ end
+
+ it "represents the 'COPY'-Request-Method" do
+ Net::HTTP::Copy::METHOD.should == "COPY"
+ end
+
+ it "has no Request Body" do
+ Net::HTTP::Copy::REQUEST_HAS_BODY.should be_false
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Copy::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Move" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Move.should < Net::HTTPRequest
+ end
+
+ it "represents the 'MOVE'-Request-Method" do
+ Net::HTTP::Move::METHOD.should == "MOVE"
+ end
+
+ it "has no Request Body" do
+ Net::HTTP::Move::REQUEST_HAS_BODY.should be_false
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Move::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Lock" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Lock.should < Net::HTTPRequest
+ end
+
+ it "represents the 'LOCK'-Request-Method" do
+ Net::HTTP::Lock::METHOD.should == "LOCK"
+ end
+
+ it "has a Request Body" do
+ Net::HTTP::Lock::REQUEST_HAS_BODY.should be_true
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Lock::RESPONSE_HAS_BODY.should be_true
+ end
+end
+
+describe "Net::HTTP::Unlock" do
+ it "is a subclass of Net::HTTPRequest" do
+ Net::HTTP::Unlock.should < Net::HTTPRequest
+ end
+
+ it "represents the 'UNLOCK'-Request-Method" do
+ Net::HTTP::Unlock::METHOD.should == "UNLOCK"
+ end
+
+ it "has a Request Body" do
+ Net::HTTP::Unlock::REQUEST_HAS_BODY.should be_true
+ end
+
+ it "has a Response Body" do
+ Net::HTTP::Unlock::RESPONSE_HAS_BODY.should be_true
+ end
+end
diff --git a/spec/ruby/library/net/http/http/send_request_spec.rb b/spec/ruby/library/net/http/http/send_request_spec.rb
new file mode 100644
index 0000000000..03fd32e470
--- /dev/null
+++ b/spec/ruby/library/net/http/http/send_request_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#send_request" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+
+ # HEAD is special so handled separately
+ @methods = %w[
+ GET POST PUT DELETE
+ OPTIONS
+ PROPFIND PROPPATCH LOCK UNLOCK
+ ]
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ # TODO: Does only work with GET and POST requests
+ describe "when passed type, path" do
+ it "sends a HTTP Request of the passed type to the passed path" do
+ response = @http.send_request("HEAD", "/request")
+ response.body.should be_nil
+
+ @methods.each do |method|
+ response = @http.send_request(method, "/request")
+ response.body.should == "Request type: #{method}"
+ end
+ end
+ end
+
+ describe "when passed type, path, body" do
+ it "sends a HTTP Request with the passed body" do
+ response = @http.send_request("HEAD", "/request/body", "test=test")
+ response.body.should be_nil
+
+ @methods.each do |method|
+ response = @http.send_request(method, "/request/body", "test=test")
+ response.body.should == "test=test"
+ end
+ end
+ end
+
+ describe "when passed type, path, body, headers" do
+ it "sends a HTTP Request with the passed headers" do
+ referer = 'https://www.ruby-lang.org/'.freeze
+
+ response = @http.send_request("HEAD", "/request/header", "test=test", "referer" => referer)
+ response.body.should be_nil
+
+ @methods.each do |method|
+ response = @http.send_request(method, "/request/header", "test=test", "referer" => referer)
+ response.body.should include('"referer"=>["' + referer + '"]')
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/set_debug_output_spec.rb b/spec/ruby/library/net/http/http/set_debug_output_spec.rb
new file mode 100644
index 0000000000..e0fe4796da
--- /dev/null
+++ b/spec/ruby/library/net/http/http/set_debug_output_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require "stringio"
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#set_debug_output when passed io" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.new("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sets the passed io as output stream for debugging" do
+ io = StringIO.new
+
+ @http.set_debug_output(io)
+ @http.start
+ io.string.should_not be_empty
+ size = io.string.size
+
+ @http.get("/")
+ io.string.size.should > size
+ end
+
+ it "outputs a warning when the connection has already been started" do
+ @http.start
+ lambda { @http.set_debug_output(StringIO.new) }.should complain(/Net::HTTP#set_debug_output called after HTTP started/)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/shared/request_get.rb b/spec/ruby/library/net/http/http/shared/request_get.rb
new file mode 100644
index 0000000000..b0eca665d6
--- /dev/null
+++ b/spec/ruby/library/net/http/http/shared/request_get.rb
@@ -0,0 +1,41 @@
+describe :net_ftp_request_get, shared: true do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when passed no block" do
+ it "sends a GET request to the passed path and returns the response" do
+ response = @http.send(@method, "/request")
+ response.body.should == "Request type: GET"
+ end
+
+ it "returns a Net::HTTPResponse object" do
+ response = @http.send(@method, "/request")
+ response.should be_kind_of(Net::HTTPResponse)
+ end
+ end
+
+ describe "when passed a block" do
+ it "sends a GET request to the passed path and returns the response" do
+ response = @http.send(@method, "/request") {}
+ response.body.should == "Request type: GET"
+ end
+
+ it "yields the response to the passed block" do
+ @http.send(@method, "/request") do |response|
+ response.body.should == "Request type: GET"
+ end
+ end
+
+ it "returns a Net::HTTPResponse object" do
+ response = @http.send(@method, "/request") {}
+ response.should be_kind_of(Net::HTTPResponse)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/shared/request_head.rb b/spec/ruby/library/net/http/http/shared/request_head.rb
new file mode 100644
index 0000000000..0e669de9ac
--- /dev/null
+++ b/spec/ruby/library/net/http/http/shared/request_head.rb
@@ -0,0 +1,41 @@
+describe :net_ftp_request_head, shared: true do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when passed no block" do
+ it "sends a head request to the passed path and returns the response" do
+ response = @http.send(@method, "/request")
+ response.body.should be_nil
+ end
+
+ it "returns a Net::HTTPResponse object" do
+ response = @http.send(@method, "/request")
+ response.should be_kind_of(Net::HTTPResponse)
+ end
+ end
+
+ describe "when passed a block" do
+ it "sends a head request to the passed path and returns the response" do
+ response = @http.send(@method, "/request") {}
+ response.body.should be_nil
+ end
+
+ it "yields the response to the passed block" do
+ @http.send(@method, "/request") do |response|
+ response.body.should be_nil
+ end
+ end
+
+ it "returns a Net::HTTPResponse object" do
+ response = @http.send(@method, "/request") {}
+ response.should be_kind_of(Net::HTTPResponse)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/shared/request_post.rb b/spec/ruby/library/net/http/http/shared/request_post.rb
new file mode 100644
index 0000000000..06c5e139f9
--- /dev/null
+++ b/spec/ruby/library/net/http/http/shared/request_post.rb
@@ -0,0 +1,41 @@
+describe :net_ftp_request_post, shared: true do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when passed no block" do
+ it "sends a post request to the passed path and returns the response" do
+ response = @http.send(@method, "/request", "test=test")
+ response.body.should == "Request type: POST"
+ end
+
+ it "returns a Net::HTTPResponse object" do
+ response = @http.send(@method, "/request", "test=test")
+ response.should be_kind_of(Net::HTTPResponse)
+ end
+ end
+
+ describe "when passed a block" do
+ it "sends a post request to the passed path and returns the response" do
+ response = @http.send(@method, "/request", "test=test") {}
+ response.body.should == "Request type: POST"
+ end
+
+ it "yields the response to the passed block" do
+ @http.send(@method, "/request", "test=test") do |response|
+ response.body.should == "Request type: POST"
+ end
+ end
+
+ it "returns a Net::HTTPResponse object" do
+ response = @http.send(@method, "/request", "test=test") {}
+ response.should be_kind_of(Net::HTTPResponse)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/shared/request_put.rb b/spec/ruby/library/net/http/http/shared/request_put.rb
new file mode 100644
index 0000000000..6ae791d7e7
--- /dev/null
+++ b/spec/ruby/library/net/http/http/shared/request_put.rb
@@ -0,0 +1,41 @@
+describe :net_ftp_request_put, shared: true do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when passed no block" do
+ it "sends a put request to the passed path and returns the response" do
+ response = @http.send(@method, "/request", "test=test")
+ response.body.should == "Request type: PUT"
+ end
+
+ it "returns a Net::HTTPResponse object" do
+ response = @http.send(@method, "/request", "test=test")
+ response.should be_kind_of(Net::HTTPResponse)
+ end
+ end
+
+ describe "when passed a block" do
+ it "sends a put request to the passed path and returns the response" do
+ response = @http.send(@method, "/request", "test=test") {}
+ response.body.should == "Request type: PUT"
+ end
+
+ it "yields the response to the passed block" do
+ @http.send(@method, "/request", "test=test") do |response|
+ response.body.should == "Request type: PUT"
+ end
+ end
+
+ it "returns a Net::HTTPResponse object" do
+ response = @http.send(@method, "/request", "test=test") {}
+ response.should be_kind_of(Net::HTTPResponse)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/shared/started.rb b/spec/ruby/library/net/http/http/shared/started.rb
new file mode 100644
index 0000000000..9ff6272c31
--- /dev/null
+++ b/spec/ruby/library/net/http/http/shared/started.rb
@@ -0,0 +1,26 @@
+describe :net_http_started_p, shared: true do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.new("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "returns true when self has been started" do
+ @http.start
+ @http.send(@method).should be_true
+ end
+
+ it "returns false when self has not been started yet" do
+ @http.send(@method).should be_false
+ end
+
+ it "returns false when self has been stopped again" do
+ @http.start
+ @http.finish
+ @http.send(@method).should be_false
+ end
+end
diff --git a/spec/ruby/library/net/http/http/shared/version_1_1.rb b/spec/ruby/library/net/http/http/shared/version_1_1.rb
new file mode 100644
index 0000000000..db3d6a986d
--- /dev/null
+++ b/spec/ruby/library/net/http/http/shared/version_1_1.rb
@@ -0,0 +1,6 @@
+describe :net_http_version_1_1_p, shared: true do
+ it "returns the state of net/http 1.1 features" do
+ Net::HTTP.version_1_2
+ Net::HTTP.send(@method).should be_false
+ end
+end
diff --git a/spec/ruby/library/net/http/http/shared/version_1_2.rb b/spec/ruby/library/net/http/http/shared/version_1_2.rb
new file mode 100644
index 0000000000..b044182c60
--- /dev/null
+++ b/spec/ruby/library/net/http/http/shared/version_1_2.rb
@@ -0,0 +1,6 @@
+describe :net_http_version_1_2_p, shared: true do
+ it "returns the state of net/http 1.2 features" do
+ Net::HTTP.version_1_2
+ Net::HTTP.send(@method).should be_true
+ end
+end
diff --git a/spec/ruby/library/net/http/http/socket_type_spec.rb b/spec/ruby/library/net/http/http/socket_type_spec.rb
new file mode 100644
index 0000000000..5c844ddf94
--- /dev/null
+++ b/spec/ruby/library/net/http/http/socket_type_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP.socket_type" do
+ it "returns BufferedIO" do
+ Net::HTTP.socket_type.should == Net::BufferedIO
+ end
+end
diff --git a/spec/ruby/library/net/http/http/start_spec.rb b/spec/ruby/library/net/http/http/start_spec.rb
new file mode 100644
index 0000000000..e23f9183f7
--- /dev/null
+++ b/spec/ruby/library/net/http/http/start_spec.rb
@@ -0,0 +1,111 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP.start" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @port = NetHTTPSpecs.port
+ end
+
+ after :each do
+ NetHTTPSpecs.stop_server
+ end
+
+ describe "when not passed a block" do
+ before :each do
+ @http = Net::HTTP.start("localhost", @port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ end
+
+ it "returns a new Net::HTTP object for the passed address and port" do
+ @http.should be_kind_of(Net::HTTP)
+ @http.address.should == "localhost"
+ @http.port.should == @port
+ end
+
+ it "opens the tcp connection" do
+ @http.started?.should be_true
+ end
+ end
+
+ describe "when passed a block" do
+ it "returns the blocks return value" do
+ Net::HTTP.start("localhost", @port) { :test }.should == :test
+ end
+
+ it "yields the new Net::HTTP object to the block" do
+ yielded = false
+ Net::HTTP.start("localhost", @port) do |net|
+ yielded = true
+ net.should be_kind_of(Net::HTTP)
+ end
+ yielded.should be_true
+ end
+
+ it "opens the tcp connection before yielding" do
+ Net::HTTP.start("localhost", @port) { |http| http.started?.should be_true }
+ end
+
+ it "closes the tcp connection after yielding" do
+ net = nil
+ Net::HTTP.start("localhost", @port) { |x| net = x }
+ net.started?.should be_false
+ end
+ end
+end
+
+describe "Net::HTTP#start" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.new("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "returns self" do
+ @http.start.should equal(@http)
+ end
+
+ it "opens the tcp connection" do
+ @http.start
+ @http.started?.should be_true
+ end
+
+ describe "when self has already been started" do
+ it "raises an IOError" do
+ @http.start
+ lambda { @http.start }.should raise_error(IOError)
+ end
+ end
+
+ describe "when passed a block" do
+ it "returns the blocks return value" do
+ @http.start { :test }.should == :test
+ end
+
+ it "yields the new Net::HTTP object to the block" do
+ yielded = false
+ @http.start do |http|
+ yielded = true
+ http.should equal(@http)
+ end
+ yielded.should be_true
+ end
+
+ it "opens the tcp connection before yielding" do
+ @http.start { |http| http.started?.should be_true }
+ end
+
+ it "closes the tcp connection after yielding" do
+ @http.start { }
+ @http.started?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/http/started_spec.rb b/spec/ruby/library/net/http/http/started_spec.rb
new file mode 100644
index 0000000000..ea441ed16a
--- /dev/null
+++ b/spec/ruby/library/net/http/http/started_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+require_relative 'shared/started'
+
+describe "Net::HTTP#started?" do
+ it_behaves_like :net_http_started_p, :started?
+end
diff --git a/spec/ruby/library/net/http/http/trace_spec.rb b/spec/ruby/library/net/http/http/trace_spec.rb
new file mode 100644
index 0000000000..94a1bf6655
--- /dev/null
+++ b/spec/ruby/library/net/http/http/trace_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#trace" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends a TRACE request to the passed path and returns the response" do
+ response = @http.trace("/request")
+ response.body.should == "Request type: TRACE"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.trace("/request").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/unlock_spec.rb b/spec/ruby/library/net/http/http/unlock_spec.rb
new file mode 100644
index 0000000000..a4f1b7a1d1
--- /dev/null
+++ b/spec/ruby/library/net/http/http/unlock_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/http_server'
+
+describe "Net::HTTP#unlock" do
+ before :each do
+ NetHTTPSpecs.start_server
+ @http = Net::HTTP.start("localhost", NetHTTPSpecs.port)
+ end
+
+ after :each do
+ @http.finish if @http.started?
+ NetHTTPSpecs.stop_server
+ end
+
+ it "sends an UNLOCK request to the passed path and returns the response" do
+ response = @http.unlock("/request", "test=test")
+ response.body.should == "Request type: UNLOCK"
+ end
+
+ it "returns a Net::HTTPResponse" do
+ @http.unlock("/request", "test=test").should be_kind_of(Net::HTTPResponse)
+ end
+end
diff --git a/spec/ruby/library/net/http/http/use_ssl_spec.rb b/spec/ruby/library/net/http/http/use_ssl_spec.rb
new file mode 100644
index 0000000000..be1ec7fa25
--- /dev/null
+++ b/spec/ruby/library/net/http/http/use_ssl_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTP#use_ssl?" do
+ it "returns false" do
+ http = Net::HTTP.new("localhost")
+ http.use_ssl?.should be_false
+ end
+end
diff --git a/spec/ruby/library/net/http/http/version_1_1_spec.rb b/spec/ruby/library/net/http/http/version_1_1_spec.rb
new file mode 100644
index 0000000000..1c069e9ea6
--- /dev/null
+++ b/spec/ruby/library/net/http/http/version_1_1_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'shared/version_1_1'
+
+describe "Net::HTTP.version_1_1?" do
+ it_behaves_like :net_http_version_1_1_p, :version_1_1?
+end
diff --git a/spec/ruby/library/net/http/http/version_1_2_spec.rb b/spec/ruby/library/net/http/http/version_1_2_spec.rb
new file mode 100644
index 0000000000..4e601462c9
--- /dev/null
+++ b/spec/ruby/library/net/http/http/version_1_2_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'shared/version_1_2'
+
+describe "Net::HTTP.version_1_2" do
+ it "turns on net/http 1.2 features" do
+ Net::HTTP.version_1_2
+
+ Net::HTTP.version_1_2?.should be_true
+ Net::HTTP.version_1_1?.should be_false
+ end
+
+ it "returns true" do
+ Net::HTTP.version_1_2.should be_true
+ end
+end
+
+describe "Net::HTTP.version_1_2?" do
+ it_behaves_like :net_http_version_1_2_p, :version_1_2?
+end
diff --git a/spec/ruby/library/net/http/httpexceptions/fixtures/classes.rb b/spec/ruby/library/net/http/httpexceptions/fixtures/classes.rb
new file mode 100644
index 0000000000..abe8855eff
--- /dev/null
+++ b/spec/ruby/library/net/http/httpexceptions/fixtures/classes.rb
@@ -0,0 +1,5 @@
+module NetHTTPExceptionsSpecs
+ class Simple < StandardError
+ include Net::HTTPExceptions
+ end
+end
diff --git a/spec/ruby/library/net/http/httpexceptions/initialize_spec.rb b/spec/ruby/library/net/http/httpexceptions/initialize_spec.rb
new file mode 100644
index 0000000000..8e3fd8cc02
--- /dev/null
+++ b/spec/ruby/library/net/http/httpexceptions/initialize_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPExceptions#initialize when passed message, response" do
+ before :each do
+ @exception = NetHTTPExceptionsSpecs::Simple.new("error message", "a http response")
+ end
+
+ it "calls super with the passed message" do
+ @exception.message.should == "error message"
+ end
+
+ it "sets self's response to the passed response" do
+ @exception.response.should == "a http response"
+ end
+end
diff --git a/spec/ruby/library/net/http/httpexceptions/response_spec.rb b/spec/ruby/library/net/http/httpexceptions/response_spec.rb
new file mode 100644
index 0000000000..205b2bc212
--- /dev/null
+++ b/spec/ruby/library/net/http/httpexceptions/response_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPExceptions#response" do
+ it "returns self's response" do
+ exception = NetHTTPExceptionsSpecs::Simple.new("error message", "a http response")
+ exception.response.should == "a http response"
+ end
+end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/body_exist_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/body_exist_spec.rb
new file mode 100644
index 0000000000..ab56176a5c
--- /dev/null
+++ b/spec/ruby/library/net/http/httpgenericrequest/body_exist_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPGenericRequest#body_exist?" do
+ it "returns true when the response is expected to have a body" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.body_exist?.should be_true
+
+ request = Net::HTTPGenericRequest.new("POST", true, false, "/some/path")
+ request.body_exist?.should be_false
+ end
+
+ describe "when $VERBOSE is true" do
+ it "emits a warning" do
+ request = Net::HTTPGenericRequest.new("POST", true, false, "/some/path")
+ lambda {
+ $VERBOSE = true
+ request.body_exist?
+ }.should complain(/body_exist\? is obsolete/)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/body_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/body_spec.rb
new file mode 100644
index 0000000000..a215895b57
--- /dev/null
+++ b/spec/ruby/library/net/http/httpgenericrequest/body_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require "stringio"
+
+describe "Net::HTTPGenericRequest#body" do
+ it "returns self's request body" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.body.should be_nil
+
+ request.body = "Some Content"
+ request.body.should == "Some Content"
+ end
+end
+
+describe "Net::HTTPGenericRequest#body=" do
+ before :each do
+ @request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ end
+
+ it "sets self's body content to the passed String" do
+ @request.body = "Some Content"
+ @request.body.should == "Some Content"
+ end
+
+ it "sets self's body stream to nil" do
+ @request.body_stream = StringIO.new("")
+ @request.body = "Some Content"
+ @request.body_stream.should be_nil
+ end
+end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/body_stream_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/body_stream_spec.rb
new file mode 100644
index 0000000000..c2a60e6836
--- /dev/null
+++ b/spec/ruby/library/net/http/httpgenericrequest/body_stream_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require "stringio"
+
+describe "Net::HTTPGenericRequest#body_stream" do
+ it "returns self's body stream Object" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.body_stream.should be_nil
+
+ stream = StringIO.new("test")
+ request.body_stream = stream
+ request.body_stream.should equal(stream)
+ end
+end
+
+describe "Net::HTTPGenericRequest#body_stream=" do
+ before :each do
+ @request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ @stream = StringIO.new("test")
+ end
+
+ it "sets self's body stream to the passed Object" do
+ @request.body_stream = @stream
+ @request.body_stream.should equal(@stream)
+ end
+
+ it "sets self's body to nil" do
+ @request.body = "Some Content"
+ @request.body_stream = @stream
+ @request.body.should be_nil
+ end
+end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/exec_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/exec_spec.rb
new file mode 100644
index 0000000000..90e158a159
--- /dev/null
+++ b/spec/ruby/library/net/http/httpgenericrequest/exec_spec.rb
@@ -0,0 +1,131 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require "stringio"
+
+describe "Net::HTTPGenericRequest#exec when passed socket, version, path" do
+ before :each do
+ @socket = StringIO.new("")
+ @buffered_socket = Net::BufferedIO.new(@socket)
+ end
+
+ it "executes the request over the socket to the path using the HTTP version" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+
+ request.exec(@buffered_socket, "1.1", "/some/path")
+ str = @socket.string
+
+ str.should =~ %r[POST /some/path HTTP/1.1\r\n]
+ str.should =~ %r[Accept: \*/\*\r\n]
+ str[-4..-1].should == "\r\n\r\n"
+
+ request = Net::HTTPGenericRequest.new("GET", true, true, "/some/path",
+ "Content-Type" => "text/html")
+
+ request.exec(@buffered_socket, "1.0", "/some/other/path")
+ str = @socket.string
+
+ str.should =~ %r[GET /some/other/path HTTP/1.0\r\n]
+ str.should =~ %r[Accept: \*/\*\r\n]
+ str.should =~ %r[Content-Type: text/html\r\n]
+ str[-4..-1].should == "\r\n\r\n"
+ end
+
+ describe "when a request body is set" do
+ it "sets the 'Content-Type' header to 'application/x-www-form-urlencoded' unless the 'Content-Type' header is supplied" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.body = "Some Content"
+
+ request.exec(@buffered_socket, "1.1", "/some/other/path")
+ str = @socket.string
+
+ str.should =~ %r[POST /some/other/path HTTP/1.1\r\n]
+ str.should =~ %r[Accept: \*/\*\r\n]
+ str.should =~ %r[Content-Type: application/x-www-form-urlencoded\r\n]
+ str.should =~ %r[Content-Length: 12\r\n]
+ str[-16..-1].should == "\r\n\r\nSome Content"
+ end
+
+ it "correctly sets the 'Content-Length' header and includes the body" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path",
+ "Content-Type" => "text/html")
+ request.body = "Some Content"
+
+ request.exec(@buffered_socket, "1.1", "/some/other/path")
+ str = @socket.string
+
+ str.should =~ %r[POST /some/other/path HTTP/1.1\r\n]
+ str.should =~ %r[Accept: \*/\*\r\n]
+ str.should =~ %r[Content-Type: text/html\r\n]
+ str.should =~ %r[Content-Length: 12\r\n]
+ str[-16..-1].should == "\r\n\r\nSome Content"
+ end
+ end
+
+ describe "when a body stream is set" do
+ it "sets the 'Content-Type' header to 'application/x-www-form-urlencoded' unless the 'Content-Type' header is supplied" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path",
+ "Content-Length" => "10")
+ request.body_stream = StringIO.new("a" * 20)
+
+ request.exec(@buffered_socket, "1.1", "/some/other/path")
+ str = @socket.string
+
+ str.should =~ %r[POST /some/other/path HTTP/1.1\r\n]
+ str.should =~ %r[Accept: \*/\*\r\n]
+ str.should =~ %r[Content-Type: application/x-www-form-urlencoded\r\n]
+ str.should =~ %r[Content-Length: 10\r\n]
+ str[-24..-1].should == "\r\n\r\naaaaaaaaaaaaaaaaaaaa"
+ end
+
+ it "sends the whole stream, regardless of the 'Content-Length' header" do
+ request = Net::HTTPGenericRequest.new("POST", true, true,"/some/path",
+ "Content-Type" => "text/html",
+ "Content-Length" => "10")
+ request.body_stream = StringIO.new("a" * 20)
+
+ request.exec(@buffered_socket, "1.1", "/some/other/path")
+ str = @socket.string
+
+ str.should =~ %r[POST /some/other/path HTTP/1.1\r\n]
+ str.should =~ %r[Accept: \*/\*\r\n]
+ str.should =~ %r[Content-Type: text/html\r\n]
+ str.should =~ %r[Content-Length: 10\r\n]
+ str[-24..-1].should == "\r\n\r\naaaaaaaaaaaaaaaaaaaa"
+ end
+
+ it "sends the request in chunks when 'Transfer-Encoding' is set to 'chunked'" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path",
+ "Content-Type" => "text/html",
+ "Transfer-Encoding" => "chunked")
+ datasize = 1024 * 10
+ request.body_stream = StringIO.new("a" * datasize)
+
+ request.exec(@buffered_socket, "1.1", "/some/other/path")
+ str = @socket.string
+
+ str.should =~ %r[POST /some/other/path HTTP/1.1\r\n]
+ str.should =~ %r[Accept: \*/\*\r\n]
+ str.should =~ %r[Content-Type: text/html\r\n]
+ str.should =~ %r[Transfer-Encoding: chunked\r\n]
+ str =~ %r[\r\n\r\n]
+ str = $'
+ while datasize > 0
+ chunk_size_line, str = str.split(/\r\n/, 2)
+ chunk_size = chunk_size_line[/\A[0-9A-Fa-f]+/].to_i(16)
+ str.slice!(0, chunk_size).should == 'a' * chunk_size
+ datasize -= chunk_size
+ str.slice!(0, 2).should == "\r\n"
+ end
+ datasize.should == 0
+ str.should == %"0\r\n\r\n"
+ end
+
+ it "raises an ArgumentError when the 'Content-Length' is not set or 'Transfer-Encoding' is not set to 'chunked'" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path",
+ "Content-Type" => "text/html")
+ request.body_stream = StringIO.new("Some Content")
+
+ lambda { request.exec(@buffered_socket, "1.1", "/some/other/path") }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/inspect_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/inspect_spec.rb
new file mode 100644
index 0000000000..36240949c3
--- /dev/null
+++ b/spec/ruby/library/net/http/httpgenericrequest/inspect_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPGenericRequest#inspect" do
+ it "returns a String representation of self" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.inspect.should == "#<Net::HTTPGenericRequest POST>"
+
+ request = Net::HTTPGenericRequest.new("GET", false, true, "/some/path")
+ request.inspect.should == "#<Net::HTTPGenericRequest GET>"
+
+ request = Net::HTTPGenericRequest.new("BLA", true, true, "/some/path")
+ request.inspect.should == "#<Net::HTTPGenericRequest BLA>"
+
+ # Subclasses
+ request = Net::HTTP::Get.new("/some/path")
+ request.inspect.should == "#<Net::HTTP::Get GET>"
+
+ request = Net::HTTP::Post.new("/some/path")
+ request.inspect.should == "#<Net::HTTP::Post POST>"
+
+ request = Net::HTTP::Trace.new("/some/path")
+ request.inspect.should == "#<Net::HTTP::Trace TRACE>"
+ end
+end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/method_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/method_spec.rb
new file mode 100644
index 0000000000..3f7c2cbf2b
--- /dev/null
+++ b/spec/ruby/library/net/http/httpgenericrequest/method_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPGenericRequest#method" do
+ it "returns self's request method" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.method.should == "POST"
+
+ request = Net::HTTPGenericRequest.new("GET", false, true, "/some/path")
+ request.method.should == "GET"
+
+ request = Net::HTTPGenericRequest.new("BLA", true, true, "/some/path")
+ request.method.should == "BLA"
+ end
+end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/path_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/path_spec.rb
new file mode 100644
index 0000000000..fc4cf9af53
--- /dev/null
+++ b/spec/ruby/library/net/http/httpgenericrequest/path_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPGenericRequest#path" do
+ it "returns self's request path" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.path.should == "/some/path"
+
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/other/path")
+ request.path.should == "/some/other/path"
+ end
+end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/request_body_permitted_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/request_body_permitted_spec.rb
new file mode 100644
index 0000000000..50cd1ff637
--- /dev/null
+++ b/spec/ruby/library/net/http/httpgenericrequest/request_body_permitted_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPGenericRequest#request_body_permitted?" do
+ it "returns true when the request is expected to have a body" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.request_body_permitted?.should be_true
+
+ request = Net::HTTPGenericRequest.new("POST", false, true, "/some/path")
+ request.request_body_permitted?.should be_false
+ end
+end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/response_body_permitted_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/response_body_permitted_spec.rb
new file mode 100644
index 0000000000..0c4165d0ab
--- /dev/null
+++ b/spec/ruby/library/net/http/httpgenericrequest/response_body_permitted_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPGenericRequest#response_body_permitted?" do
+ it "returns true when the response is expected to have a body" do
+ request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ request.response_body_permitted?.should be_true
+
+ request = Net::HTTPGenericRequest.new("POST", true, false, "/some/path")
+ request.response_body_permitted?.should be_false
+ end
+end
diff --git a/spec/ruby/library/net/http/httpgenericrequest/set_body_internal_spec.rb b/spec/ruby/library/net/http/httpgenericrequest/set_body_internal_spec.rb
new file mode 100644
index 0000000000..ef2edc019a
--- /dev/null
+++ b/spec/ruby/library/net/http/httpgenericrequest/set_body_internal_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPGenericRequest#set_body_internal when passed string" do
+ before :each do
+ @request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path")
+ end
+
+ it "sets self's body to the passed string" do
+ @request.set_body_internal("Some Content")
+ @request.body.should == "Some Content"
+ end
+
+ it "raises an ArgumentError when the body or body_stream of self have already been set" do
+ @request.body = "Some Content"
+ lambda { @request.set_body_internal("Some other Content") }.should raise_error(ArgumentError)
+
+ @request.body_stream = "Some Content"
+ lambda { @request.set_body_internal("Some other Content") }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/add_field_spec.rb b/spec/ruby/library/net/http/httpheader/add_field_spec.rb
new file mode 100644
index 0000000000..882d5ac5bb
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/add_field_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#add_field when passed key, value" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "adds the passed value to the header entry with the passed key" do
+ @headers.add_field("My-Header", "a")
+ @headers.get_fields("My-Header").should == ["a"]
+
+ @headers.add_field("My-Header", "b")
+ @headers.get_fields("My-Header").should == ["a", "b"]
+
+ @headers.add_field("My-Header", "c")
+ @headers.get_fields("My-Header").should == ["a", "b", "c"]
+ end
+
+ it "is case-insensitive" do
+ @headers.add_field("My-Header", "a")
+ @headers.get_fields("My-Header").should == ["a"]
+
+ @headers.add_field("my-header", "b")
+ @headers.get_fields("My-Header").should == ["a", "b"]
+
+ @headers.add_field("MY-HEADER", "c")
+ @headers.get_fields("My-Header").should == ["a", "b", "c"]
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/basic_auth_spec.rb b/spec/ruby/library/net/http/httpheader/basic_auth_spec.rb
new file mode 100644
index 0000000000..fa2a710fe1
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/basic_auth_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#basic_auth when passed account, password" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "sets the 'Authorization' Header entry for basic authorization" do
+ @headers.basic_auth("rubyspec", "rocks")
+ @headers["Authorization"].should == "Basic cnVieXNwZWM6cm9ja3M="
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/canonical_each_spec.rb b/spec/ruby/library/net/http/httpheader/canonical_each_spec.rb
new file mode 100644
index 0000000000..0dddd3049b
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/canonical_each_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/each_capitalized'
+
+describe "Net::HTTPHeader#canonical_each" do
+ it_behaves_like :net_httpheader_each_capitalized, :canonical_each
+end
diff --git a/spec/ruby/library/net/http/httpheader/chunked_spec.rb b/spec/ruby/library/net/http/httpheader/chunked_spec.rb
new file mode 100644
index 0000000000..96b758981b
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/chunked_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#chunked?" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns true if the 'Transfer-Encoding' header entry is set to chunked" do
+ @headers.chunked?.should be_false
+
+ @headers["Transfer-Encoding"] = "bla"
+ @headers.chunked?.should be_false
+
+ @headers["Transfer-Encoding"] = "blachunkedbla"
+ @headers.chunked?.should be_false
+
+ @headers["Transfer-Encoding"] = "chunked"
+ @headers.chunked?.should be_true
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/content_length_spec.rb b/spec/ruby/library/net/http/httpheader/content_length_spec.rb
new file mode 100644
index 0000000000..44c971c98e
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/content_length_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#content_length" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns nil if no 'Content-Length' header entry is set" do
+ @headers.content_length.should be_nil
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when the 'Content-Length' header entry has an invalid format" do
+ @headers["Content-Length"] = "invalid"
+ lambda { @headers.content_length }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+
+ it "returns the value of the 'Content-Length' header entry as an Integer" do
+ @headers["Content-Length"] = "123"
+ @headers.content_length.should eql(123)
+
+ @headers["Content-Length"] = "123valid"
+ @headers.content_length.should eql(123)
+
+ @headers["Content-Length"] = "valid123"
+ @headers.content_length.should eql(123)
+ end
+end
+
+describe "Net::HTTPHeader#content_length=" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "removes the 'Content-Length' entry if passed false or nil" do
+ @headers["Content-Length"] = "123"
+ @headers.content_length = nil
+ @headers["Content-Length"].should be_nil
+ end
+
+ it "sets the 'Content-Length' entry to the passed value" do
+ @headers.content_length = "123"
+ @headers["Content-Length"].should == "123"
+
+ @headers.content_length = "123valid"
+ @headers["Content-Length"].should == "123"
+ end
+
+ it "sets the 'Content-Length' entry to 0 if the passed value is not valid" do
+ @headers.content_length = "invalid123"
+ @headers["Content-Length"].should == "0"
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/content_range_spec.rb b/spec/ruby/library/net/http/httpheader/content_range_spec.rb
new file mode 100644
index 0000000000..23dc92348b
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/content_range_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#content_range" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns a Range object that represents the 'Content-Range' header entry" do
+ @headers["Content-Range"] = "bytes 0-499/1234"
+ @headers.content_range.should == (0..499)
+
+ @headers["Content-Range"] = "bytes 500-1233/1234"
+ @headers.content_range.should == (500..1233)
+ end
+
+ it "returns nil when there is no 'Content-Range' header entry" do
+ @headers.content_range.should be_nil
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when the 'Content-Range' has an invalid format" do
+ @headers["Content-Range"] = "invalid"
+ lambda { @headers.content_range }.should raise_error(Net::HTTPHeaderSyntaxError)
+
+ @headers["Content-Range"] = "bytes 123-abc"
+ lambda { @headers.content_range }.should raise_error(Net::HTTPHeaderSyntaxError)
+
+ @headers["Content-Range"] = "bytes abc-123"
+ lambda { @headers.content_range }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/content_type_spec.rb b/spec/ruby/library/net/http/httpheader/content_type_spec.rb
new file mode 100644
index 0000000000..1f8b4ba326
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/content_type_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_content_type'
+
+describe "Net::HTTPHeader#content_type" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns the content type string, as per 'Content-Type' header entry" do
+ @headers["Content-Type"] = "text/html"
+ @headers.content_type.should == "text/html"
+
+ @headers["Content-Type"] = "text/html;charset=utf-8"
+ @headers.content_type.should == "text/html"
+ end
+
+ it "returns nil if the 'Content-Type' header entry does not exist" do
+ @headers.content_type.should be_nil
+ end
+end
+
+describe "Net::HTTPHeader#content_type=" do
+ it_behaves_like :net_httpheader_set_content_type, :content_type=
+end
diff --git a/spec/ruby/library/net/http/httpheader/delete_spec.rb b/spec/ruby/library/net/http/httpheader/delete_spec.rb
new file mode 100644
index 0000000000..603ae198de
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/delete_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#delete when passed key" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "removes the header entry with the passed key" do
+ @headers["My-Header"] = "test"
+ @headers.delete("My-Header")
+
+ @headers["My-Header"].should be_nil
+ @headers.size.should eql(0)
+ end
+
+ it "returns the removed values" do
+ @headers["My-Header"] = "test"
+ @headers.delete("My-Header").should == ["test"]
+ end
+
+ it "is case-insensitive" do
+ @headers["My-Header"] = "test"
+ @headers.delete("my-header")
+
+ @headers["My-Header"].should be_nil
+ @headers.size.should eql(0)
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/each_capitalized_name_spec.rb b/spec/ruby/library/net/http/httpheader/each_capitalized_name_spec.rb
new file mode 100644
index 0000000000..1af2c6939c
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/each_capitalized_name_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#each_capitalized_name" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ @headers["My-Header"] = "test"
+ @headers.add_field("My-Other-Header", "a")
+ @headers.add_field("My-Other-Header", "b")
+ end
+
+ describe "when passed a block" do
+ it "yields each header key to the passed block (keys capitalized)" do
+ res = []
+ @headers.each_capitalized_name do |key|
+ res << key
+ end
+ res.sort.should == ["My-Header", "My-Other-Header"]
+ end
+ end
+
+ describe "when passed no block" do
+ it "returns an Enumerator" do
+ enumerator = @headers.each_capitalized_name
+ enumerator.should be_an_instance_of(Enumerator)
+
+ res = []
+ enumerator.each do |key|
+ res << key
+ end
+ res.sort.should == ["My-Header", "My-Other-Header"]
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/each_capitalized_spec.rb b/spec/ruby/library/net/http/httpheader/each_capitalized_spec.rb
new file mode 100644
index 0000000000..961a2d051f
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/each_capitalized_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/each_capitalized'
+
+describe "Net::HTTPHeader#each_capitalized" do
+ it_behaves_like :net_httpheader_each_capitalized, :each_capitalized
+end
diff --git a/spec/ruby/library/net/http/httpheader/each_header_spec.rb b/spec/ruby/library/net/http/httpheader/each_header_spec.rb
new file mode 100644
index 0000000000..19634a001b
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/each_header_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/each_header'
+
+describe "Net::HTTPHeader#each_header" do
+ it_behaves_like :net_httpheader_each_header, :each_header
+end
diff --git a/spec/ruby/library/net/http/httpheader/each_key_spec.rb b/spec/ruby/library/net/http/httpheader/each_key_spec.rb
new file mode 100644
index 0000000000..ebb17d2eac
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/each_key_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/each_name'
+
+describe "Net::HTTPHeader#each_key" do
+ it_behaves_like :net_httpheader_each_name, :each_key
+end
diff --git a/spec/ruby/library/net/http/httpheader/each_name_spec.rb b/spec/ruby/library/net/http/httpheader/each_name_spec.rb
new file mode 100644
index 0000000000..f4533ebcfb
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/each_name_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/each_name'
+
+describe "Net::HTTPHeader#each_name" do
+ it_behaves_like :net_httpheader_each_name, :each_name
+end
diff --git a/spec/ruby/library/net/http/httpheader/each_spec.rb b/spec/ruby/library/net/http/httpheader/each_spec.rb
new file mode 100644
index 0000000000..7ba8434f75
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/each_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/each_header'
+
+describe "Net::HTTPHeader#each" do
+ it_behaves_like :net_httpheader_each_header, :each
+end
diff --git a/spec/ruby/library/net/http/httpheader/each_value_spec.rb b/spec/ruby/library/net/http/httpheader/each_value_spec.rb
new file mode 100644
index 0000000000..3224de7703
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/each_value_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#each_value" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ @headers["My-Header"] = "test"
+ @headers.add_field("My-Other-Header", "a")
+ @headers.add_field("My-Other-Header", "b")
+ end
+
+ describe "when passed a block" do
+ it "yields each header entry's joined values" do
+ res = []
+ @headers.each_value do |value|
+ res << value
+ end
+ res.sort.should == ["a, b", "test"]
+ end
+ end
+
+ describe "when passed no block" do
+ it "returns an Enumerator" do
+ enumerator = @headers.each_value
+ enumerator.should be_an_instance_of(Enumerator)
+
+ res = []
+ enumerator.each do |key|
+ res << key
+ end
+ res.sort.should == ["a, b", "test"]
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/element_reference_spec.rb b/spec/ruby/library/net/http/httpheader/element_reference_spec.rb
new file mode 100644
index 0000000000..4a35e20d20
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/element_reference_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#[] when passed key" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns the value of the header entry with the passed key" do
+ @headers["My-Header"] = "test"
+ @headers["My-Header"].should == "test"
+ @headers["My-Other-Header"] = "another test"
+ @headers["My-Other-Header"].should == "another test"
+ end
+
+ it "is case-insensitive" do
+ @headers["My-Header"] = "test"
+
+ @headers['My-Header'].should == "test"
+ @headers['my-Header'].should == "test"
+ @headers['My-header'].should == "test"
+ @headers['my-header'].should == "test"
+ @headers['MY-HEADER'].should == "test"
+ end
+
+ it "returns multi-element values joined together" do
+ @headers["My-Header"] = "test"
+ @headers.add_field("My-Header", "another test")
+ @headers.add_field("My-Header", "and one more")
+
+ @headers["My-Header"].should == "test, another test, and one more"
+ end
+
+ it "returns nil for non-existing entries" do
+ @headers["My-Header"].should be_nil
+ @headers["My-Other-Header"].should be_nil
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/element_set_spec.rb b/spec/ruby/library/net/http/httpheader/element_set_spec.rb
new file mode 100644
index 0000000000..e9ad64fafc
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/element_set_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#[]= when passed key, value" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "sets the header entry with the passed key to the passed value" do
+ @headers["My-Header"] = "test"
+ @headers["My-Header"].should == "test"
+
+ @headers["My-Header"] = "overwritten"
+ @headers["My-Header"].should == "overwritten"
+
+ @headers["My-Other-Header"] = "another test"
+ @headers["My-Other-Header"].should == "another test"
+ end
+
+ it "is case-insensitive" do
+ @headers['My-Header'] = "test"
+ @headers['my-Header'] = "another test"
+ @headers['My-header'] = "and one more test"
+ @headers['my-header'] = "and another one"
+ @headers['MY-HEADER'] = "last one"
+
+ @headers["My-Header"].should == "last one"
+ @headers.size.should eql(1)
+ end
+
+ it "removes the header entry with the passed key when the value is false or nil" do
+ @headers['My-Header'] = "test"
+ @headers['My-Header'] = nil
+ @headers['My-Header'].should be_nil
+
+ @headers['My-Header'] = "test"
+ @headers['My-Header'] = false
+ @headers['My-Header'].should be_nil
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/fetch_spec.rb b/spec/ruby/library/net/http/httpheader/fetch_spec.rb
new file mode 100644
index 0000000000..4d608cdb0d
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/fetch_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#fetch" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ describe "when passed key" do
+ it "returns the header entry for the passed key" do
+ @headers["My-Header"] = "test"
+ @headers.fetch("My-Header").should == "test"
+
+ @headers.add_field("My-Other-Header", "a")
+ @headers.add_field("My-Other-Header", "b")
+ @headers.add_field("My-Other-Header", "c")
+ @headers.fetch("My-Other-Header").should == "a, b, c"
+ end
+
+ it "is case-insensitive" do
+ @headers["My-Header"] = "test"
+ @headers.fetch("my-header").should == "test"
+ @headers.fetch("MY-HEADER").should == "test"
+ end
+
+ it "returns nil when there is no entry for the passed key" do
+ lambda { @headers.fetch("my-header") }.should raise_error(IndexError)
+ end
+ end
+
+ describe "when passed key, default" do
+ it "returns the header entry for the passed key" do
+ @headers["My-Header"] = "test"
+ @headers.fetch("My-Header", "bla").should == "test"
+
+ @headers.add_field("My-Other-Header", "a")
+ @headers.add_field("My-Other-Header", "b")
+ @headers.add_field("My-Other-Header", "c")
+ @headers.fetch("My-Other-Header", "bla").should == "a, b, c"
+ end
+
+ # TODO: This raises a NoMethodError: undefined method `join' for "bla":String
+ it "returns the default value when there is no entry for the passed key" do
+ @headers.fetch("My-Header", "bla").should == "bla"
+ end
+ end
+
+ describe "when passed key and block" do
+ it "returns the header entry for the passed key" do
+ @headers["My-Header"] = "test"
+ @headers.fetch("My-Header") {}.should == "test"
+
+ @headers.add_field("My-Other-Header", "a")
+ @headers.add_field("My-Other-Header", "b")
+ @headers.add_field("My-Other-Header", "c")
+ -> {
+ @result = @headers.fetch("My-Other-Header", "bla") {}
+ }.should complain(/block supersedes default value argument/)
+ @result.should == "a, b, c"
+ end
+
+ # TODO: This raises a NoMethodError: undefined method `join' for "redaeh-ym":String
+ it "yieldsand returns the block's return value when there is no entry for the passed key" do
+ @headers.fetch("My-Header") { |key| key.reverse }.should == "redaeh-ym"
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/fixtures/classes.rb b/spec/ruby/library/net/http/httpheader/fixtures/classes.rb
new file mode 100644
index 0000000000..b5ec6abd75
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/fixtures/classes.rb
@@ -0,0 +1,11 @@
+module NetHTTPHeaderSpecs
+ class Example
+ include Net::HTTPHeader
+
+ attr_accessor :body
+
+ def initialize
+ initialize_http_header({})
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/form_data_spec.rb b/spec/ruby/library/net/http/httpheader/form_data_spec.rb
new file mode 100644
index 0000000000..9b29a03159
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/form_data_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_form_data'
+
+describe "Net::HTTPHeader#form_data=" do
+ it_behaves_like :net_httpheader_set_form_data, :form_data=
+end
diff --git a/spec/ruby/library/net/http/httpheader/get_fields_spec.rb b/spec/ruby/library/net/http/httpheader/get_fields_spec.rb
new file mode 100644
index 0000000000..1b623bf2a3
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/get_fields_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#get_fields when passed key" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns an Array containing the values of the header entry with the passed key" do
+ @headers["My-Header"] = "a"
+ @headers.get_fields("My-Header").should == ["a"]
+
+ @headers.add_field("My-Header", "b")
+ @headers.get_fields("My-Header").should == ["a", "b"]
+ end
+
+ it "returns a copy of the header entry values" do
+ @headers["My-Header"] = "a"
+
+ @headers.get_fields("My-Header").clear
+ @headers.get_fields("My-Header").should == ["a"]
+
+ @headers.get_fields("My-Header") << "b"
+ @headers.get_fields("My-Header").should == ["a"]
+ end
+
+ it "returns nil for non-existing header entries" do
+ @headers.get_fields("My-Header").should be_nil
+ @headers.get_fields("My-Other-header").should be_nil
+ end
+
+ it "is case-insensitive" do
+ @headers["My-Header"] = "test"
+ @headers.get_fields("My-Header").should == ["test"]
+ @headers.get_fields("my-header").should == ["test"]
+ @headers.get_fields("MY-HEADER").should == ["test"]
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/initialize_http_header_spec.rb b/spec/ruby/library/net/http/httpheader/initialize_http_header_spec.rb
new file mode 100644
index 0000000000..92a0b82d60
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/initialize_http_header_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#initialize_http_header when passed Hash" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.allocate
+ end
+
+ it "initializes the HTTP Header using the passed Hash" do
+ @headers.initialize_http_header("My-Header" => "test", "My-Other-Header" => "another test")
+ @headers["My-Header"].should == "test"
+ @headers["My-Other-Header"].should == "another test"
+ end
+
+ it "complains about duplicate keys when in verbose mode" do
+ lambda do
+ $VERBOSE = true
+ @headers.initialize_http_header("My-Header" => "test", "my-header" => "another test")
+ end.should complain(/duplicated HTTP header/)
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/key_spec.rb b/spec/ruby/library/net/http/httpheader/key_spec.rb
new file mode 100644
index 0000000000..9099024229
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/key_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#key? when passed key" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns true if the header entry with the passed key exists" do
+ @headers.key?("My-Header").should be_false
+ @headers["My-Header"] = "test"
+ @headers.key?("My-Header").should be_true
+ end
+
+ it "is case-insensitive" do
+ @headers["My-Header"] = "test"
+ @headers.key?("my-header").should be_true
+ @headers.key?("MY-HEADER").should be_true
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/length_spec.rb b/spec/ruby/library/net/http/httpheader/length_spec.rb
new file mode 100644
index 0000000000..0d1da149f4
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/length_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/size'
+
+describe "Net::HTTPHeader#length" do
+ it_behaves_like :net_httpheader_size, :length
+end
diff --git a/spec/ruby/library/net/http/httpheader/main_type_spec.rb b/spec/ruby/library/net/http/httpheader/main_type_spec.rb
new file mode 100644
index 0000000000..3e18de6b5b
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/main_type_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#main_type" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns the 'main-content-type', as per 'Content-Type' header entry" do
+ @headers["Content-Type"] = "text/html"
+ @headers.main_type.should == "text"
+
+ @headers["Content-Type"] = "application/pdf"
+ @headers.main_type.should == "application"
+
+ @headers["Content-Type"] = "text/html;charset=utf-8"
+ @headers.main_type.should == "text"
+ end
+
+ it "returns nil if the 'Content-Type' header entry does not exist" do
+ @headers.main_type.should be_nil
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/proxy_basic_auth_spec.rb b/spec/ruby/library/net/http/httpheader/proxy_basic_auth_spec.rb
new file mode 100644
index 0000000000..8b576ee164
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/proxy_basic_auth_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#proxy_basic_auth when passed account, password" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "sets the 'Proxy-Authorization' Header entry for basic authorization" do
+ @headers.proxy_basic_auth("rubyspec", "rocks")
+ @headers["Proxy-Authorization"].should == "Basic cnVieXNwZWM6cm9ja3M="
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/range_length_spec.rb b/spec/ruby/library/net/http/httpheader/range_length_spec.rb
new file mode 100644
index 0000000000..4415c8e5ba
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/range_length_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#range_length" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns the length of the Range represented by the 'Content-Range' header entry" do
+ @headers["Content-Range"] = "bytes 0-499/1234"
+ @headers.range_length.should eql(500)
+
+ @headers["Content-Range"] = "bytes 500-1233/1234"
+ @headers.range_length.should eql(734)
+ end
+
+ it "returns nil when there is no 'Content-Range' header entry" do
+ @headers.range_length.should be_nil
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when the 'Content-Range' has an invalid format" do
+ @headers["Content-Range"] = "invalid"
+ lambda { @headers.range_length }.should raise_error(Net::HTTPHeaderSyntaxError)
+
+ @headers["Content-Range"] = "bytes 123-abc"
+ lambda { @headers.range_length }.should raise_error(Net::HTTPHeaderSyntaxError)
+
+ @headers["Content-Range"] = "bytes abc-123"
+ lambda { @headers.range_length }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/range_spec.rb b/spec/ruby/library/net/http/httpheader/range_spec.rb
new file mode 100644
index 0000000000..5f06e03a1c
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/range_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_range'
+
+describe "Net::HTTPHeader#range" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns a Range object that represents the 'Range' header entry" do
+ @headers["Range"] = "bytes=0-499"
+ @headers.range.should == [0..499]
+
+ @headers["Range"] = "bytes=500-1233"
+ @headers.range.should == [500..1233]
+
+ @headers["Range"] = "bytes=10-"
+ @headers.range.should == [10..-1]
+
+ @headers["Range"] = "bytes=-10"
+ @headers.range.should == [-10..-1]
+ end
+
+ it "returns nil when there is no 'Range' header entry" do
+ @headers.range.should be_nil
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when the 'Range' has an invalid format" do
+ @headers["Range"] = "invalid"
+ lambda { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError)
+
+ @headers["Range"] = "bytes 123-abc"
+ lambda { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError)
+
+ @headers["Range"] = "bytes abc-123"
+ lambda { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when the 'Range' was not specified" do
+ @headers["Range"] = "bytes=-"
+ lambda { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+end
+
+describe "Net::HTTPHeader#range=" do
+ it_behaves_like :net_httpheader_set_range, :range=
+end
diff --git a/spec/ruby/library/net/http/httpheader/set_content_type_spec.rb b/spec/ruby/library/net/http/httpheader/set_content_type_spec.rb
new file mode 100644
index 0000000000..125f7a7e0d
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/set_content_type_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_content_type'
+
+describe "Net::HTTPHeader#set_content_type" do
+ it_behaves_like :net_httpheader_set_content_type, :set_content_type
+end
diff --git a/spec/ruby/library/net/http/httpheader/set_form_data_spec.rb b/spec/ruby/library/net/http/httpheader/set_form_data_spec.rb
new file mode 100644
index 0000000000..14c66ae01c
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/set_form_data_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_form_data'
+
+describe "Net::HTTPHeader#set_form_data" do
+ it_behaves_like :net_httpheader_set_form_data, :set_form_data
+end
diff --git a/spec/ruby/library/net/http/httpheader/set_range_spec.rb b/spec/ruby/library/net/http/httpheader/set_range_spec.rb
new file mode 100644
index 0000000000..85b9c50422
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/set_range_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_range'
+
+describe "Net::HTTPHeader#set_range" do
+ it_behaves_like :net_httpheader_set_range, :set_range
+end
diff --git a/spec/ruby/library/net/http/httpheader/shared/each_capitalized.rb b/spec/ruby/library/net/http/httpheader/shared/each_capitalized.rb
new file mode 100644
index 0000000000..3bac409876
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/shared/each_capitalized.rb
@@ -0,0 +1,31 @@
+describe :net_httpheader_each_capitalized, shared: true do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ @headers["my-header"] = "test"
+ @headers.add_field("my-Other-Header", "a")
+ @headers.add_field("My-Other-header", "b")
+ end
+
+ describe "when passed a block" do
+ it "yields each header entry to the passed block (capitalized keys, values joined)" do
+ res = []
+ @headers.send(@method) do |key, value|
+ res << [key, value]
+ end
+ res.sort.should == [["My-Header", "test"], ["My-Other-Header", "a, b"]]
+ end
+ end
+
+ describe "when passed no block" do
+ it "returns an Enumerator" do
+ enumerator = @headers.send(@method)
+ enumerator.should be_an_instance_of(Enumerator)
+
+ res = []
+ enumerator.each do |*key|
+ res << key
+ end
+ res.sort.should == [["My-Header", "test"], ["My-Other-Header", "a, b"]]
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/shared/each_header.rb b/spec/ruby/library/net/http/httpheader/shared/each_header.rb
new file mode 100644
index 0000000000..6bf3a6ddfe
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/shared/each_header.rb
@@ -0,0 +1,31 @@
+describe :net_httpheader_each_header, shared: true do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ @headers["My-Header"] = "test"
+ @headers.add_field("My-Other-Header", "a")
+ @headers.add_field("My-Other-Header", "b")
+ end
+
+ describe "when passed a block" do
+ it "yields each header entry to the passed block (keys in lower case, values joined)" do
+ res = []
+ @headers.send(@method) do |key, value|
+ res << [key, value]
+ end
+ res.sort.should == [["my-header", "test"], ["my-other-header", "a, b"]]
+ end
+ end
+
+ describe "when passed no block" do
+ it "returns an Enumerator" do
+ enumerator = @headers.send(@method)
+ enumerator.should be_an_instance_of(Enumerator)
+
+ res = []
+ enumerator.each do |*key|
+ res << key
+ end
+ res.sort.should == [["my-header", "test"], ["my-other-header", "a, b"]]
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/shared/each_name.rb b/spec/ruby/library/net/http/httpheader/shared/each_name.rb
new file mode 100644
index 0000000000..efc6a09dfd
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/shared/each_name.rb
@@ -0,0 +1,31 @@
+describe :net_httpheader_each_name, shared: true do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ @headers["My-Header"] = "test"
+ @headers.add_field("My-Other-Header", "a")
+ @headers.add_field("My-Other-Header", "b")
+ end
+
+ describe "when passed a block" do
+ it "yields each header key to the passed block (keys in lower case)" do
+ res = []
+ @headers.send(@method) do |key|
+ res << key
+ end
+ res.sort.should == ["my-header", "my-other-header"]
+ end
+ end
+
+ describe "when passed no block" do
+ it "returns an Enumerator" do
+ enumerator = @headers.send(@method)
+ enumerator.should be_an_instance_of(Enumerator)
+
+ res = []
+ enumerator.each do |key|
+ res << key
+ end
+ res.sort.should == ["my-header", "my-other-header"]
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/shared/set_content_type.rb b/spec/ruby/library/net/http/httpheader/shared/set_content_type.rb
new file mode 100644
index 0000000000..b7359bdca6
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/shared/set_content_type.rb
@@ -0,0 +1,18 @@
+describe :net_httpheader_set_content_type, shared: true do
+ describe "when passed type, params" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "sets the 'Content-Type' header entry based on the passed type and params" do
+ @headers.send(@method, "text/html")
+ @headers["Content-Type"].should == "text/html"
+
+ @headers.send(@method, "text/html", "charset" => "utf-8")
+ @headers["Content-Type"].should == "text/html; charset=utf-8"
+
+ @headers.send(@method, "text/html", "charset" => "utf-8", "rubyspec" => "rocks")
+ @headers["Content-Type"].split(/; /).sort.should == %w[charset=utf-8 rubyspec=rocks text/html]
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/shared/set_form_data.rb b/spec/ruby/library/net/http/httpheader/shared/set_form_data.rb
new file mode 100644
index 0000000000..db20b18803
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/shared/set_form_data.rb
@@ -0,0 +1,27 @@
+describe :net_httpheader_set_form_data, shared: true do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ describe "when passed params" do
+ it "automatically set the 'Content-Type' to 'application/x-www-form-urlencoded'" do
+ @headers.send(@method, "cmd" => "search", "q" => "ruby", "max" => "50")
+ @headers["Content-Type"].should == "application/x-www-form-urlencoded"
+ end
+
+ it "sets self's body based on the passed form parameters" do
+ @headers.send(@method, "cmd" => "search", "q" => "ruby", "max" => "50")
+ @headers.body.split("&").sort.should == ["cmd=search", "max=50", "q=ruby"]
+ end
+ end
+
+ describe "when passed params, separator" do
+ it "sets self's body based on the passed form parameters and the passed separator" do
+ @headers.send(@method, {"cmd" => "search", "q" => "ruby", "max" => "50"}, "&")
+ @headers.body.split("&").sort.should == ["cmd=search", "max=50", "q=ruby"]
+
+ @headers.send(@method, {"cmd" => "search", "q" => "ruby", "max" => "50"}, ";")
+ @headers.body.split(";").sort.should == ["cmd=search", "max=50", "q=ruby"]
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/shared/set_range.rb b/spec/ruby/library/net/http/httpheader/shared/set_range.rb
new file mode 100644
index 0000000000..6a98edbe10
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/shared/set_range.rb
@@ -0,0 +1,89 @@
+describe :net_httpheader_set_range, shared: true do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ describe "when passed nil" do
+ it "returns nil" do
+ @headers.send(@method, nil).should be_nil
+ end
+
+ it "deletes the 'Range' header entry" do
+ @headers["Range"] = "bytes 0-499/1234"
+ @headers.send(@method, nil)
+ @headers["Range"].should be_nil
+ end
+ end
+
+ describe "when passed Numeric" do
+ it "sets the 'Range' header entry based on the passed Numeric" do
+ @headers.send(@method, 10)
+ @headers["Range"].should == "bytes=0-9"
+
+ @headers.send(@method, -10)
+ @headers["Range"].should == "bytes=-10"
+
+ @headers.send(@method, 10.9)
+ @headers["Range"].should == "bytes=0-9"
+ end
+ end
+
+ describe "when passed Range" do
+ it "sets the 'Range' header entry based on the passed Range" do
+ @headers.send(@method, 10..200)
+ @headers["Range"].should == "bytes=10-200"
+
+ @headers.send(@method, 1..5)
+ @headers["Range"].should == "bytes=1-5"
+
+ @headers.send(@method, 1...5)
+ @headers["Range"].should == "bytes=1-4"
+
+ @headers.send(@method, 234..567)
+ @headers["Range"].should == "bytes=234-567"
+
+ @headers.send(@method, -5..-1)
+ @headers["Range"].should == "bytes=-5"
+
+ @headers.send(@method, 1..-1)
+ @headers["Range"].should == "bytes=1-"
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when the first Range element is negative" do
+ lambda { @headers.send(@method, -10..5) }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when the last Range element is negative" do
+ lambda { @headers.send(@method, 10..-5) }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when the last Range element is smaller than the first" do
+ lambda { @headers.send(@method, 10..5) }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+ end
+
+ describe "when passed start, end" do
+ it "sets the 'Range' header entry based on the passed start and length values" do
+ @headers.send(@method, 10, 200)
+ @headers["Range"].should == "bytes=10-209"
+
+ @headers.send(@method, 1, 5)
+ @headers["Range"].should == "bytes=1-5"
+
+ @headers.send(@method, 234, 567)
+ @headers["Range"].should == "bytes=234-800"
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when start is negative" do
+ lambda { @headers.send(@method, -10, 5) }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when start + length is negative" do
+ lambda { @headers.send(@method, 10, -15) }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+
+ it "raises a Net::HTTPHeaderSyntaxError when length is negative" do
+ lambda { @headers.send(@method, 10, -4) }.should raise_error(Net::HTTPHeaderSyntaxError)
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/shared/size.rb b/spec/ruby/library/net/http/httpheader/shared/size.rb
new file mode 100644
index 0000000000..e2b1e4c22b
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/shared/size.rb
@@ -0,0 +1,18 @@
+describe :net_httpheader_size, shared: true do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns the number of header entries in self" do
+ @headers.send(@method).should eql(0)
+
+ @headers["a"] = "b"
+ @headers.send(@method).should eql(1)
+
+ @headers["b"] = "b"
+ @headers.send(@method).should eql(2)
+
+ @headers["c"] = "c"
+ @headers.send(@method).should eql(3)
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/size_spec.rb b/spec/ruby/library/net/http/httpheader/size_spec.rb
new file mode 100644
index 0000000000..a7d78f96e0
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/size_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+require_relative 'shared/size'
+
+describe "Net::HTTPHeader#size" do
+ it_behaves_like :net_httpheader_size, :size
+end
diff --git a/spec/ruby/library/net/http/httpheader/sub_type_spec.rb b/spec/ruby/library/net/http/httpheader/sub_type_spec.rb
new file mode 100644
index 0000000000..3c73ca0027
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/sub_type_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#sub_type" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns the 'sub-content-type', as per 'Content-Type' header entry" do
+ @headers["Content-Type"] = "text/html"
+ @headers.sub_type.should == "html"
+
+ @headers["Content-Type"] = "application/pdf"
+ @headers.sub_type.should == "pdf"
+
+ @headers["Content-Type"] = "text/html;charset=utf-8"
+ @headers.sub_type.should == "html"
+ end
+
+ it "returns nil if no 'sub-content-type' is set" do
+ @headers["Content-Type"] = "text"
+ @headers.sub_type.should be_nil
+
+ @headers["Content-Type"] = "text;charset=utf-8"
+ @headers.sub_type.should be_nil
+ end
+
+ it "returns nil if the 'Content-Type' header entry does not exist" do
+ @headers.sub_type.should be_nil
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/to_hash_spec.rb b/spec/ruby/library/net/http/httpheader/to_hash_spec.rb
new file mode 100644
index 0000000000..8c1d36c30a
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/to_hash_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#to_hash" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns a Hash representing all Header entries (keys in lower case, values as arrays)" do
+ @headers.to_hash.should == {}
+
+ @headers["My-Header"] = "test"
+ @headers.to_hash.should == { "my-header" => ["test"] }
+
+ @headers.add_field("My-Header", "another test")
+ @headers.to_hash.should == { "my-header" => ["test", "another test"] }
+ end
+
+ it "does not allow modifying the headers from the returned hash" do
+ @headers.to_hash["my-header"] = ["test"]
+ @headers.to_hash.should == {}
+ @headers.key?("my-header").should be_false
+ end
+end
diff --git a/spec/ruby/library/net/http/httpheader/type_params_spec.rb b/spec/ruby/library/net/http/httpheader/type_params_spec.rb
new file mode 100644
index 0000000000..e77be7ea85
--- /dev/null
+++ b/spec/ruby/library/net/http/httpheader/type_params_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'fixtures/classes'
+
+describe "Net::HTTPHeader#type_params" do
+ before :each do
+ @headers = NetHTTPHeaderSpecs::Example.new
+ end
+
+ it "returns additional 'Content-Type' information as a Hash" do
+ @headers["Content-Type"] = "text/html;charset=utf-8"
+ @headers.type_params.should == {"charset" => "utf-8"}
+
+ @headers["Content-Type"] = "text/html; charset=utf-8; rubyspec=rocks"
+ @headers.type_params.should == {"charset" => "utf-8", "rubyspec" => "rocks"}
+ end
+
+ it "returns an empty Hash when no additional 'Content-Type' information is set" do
+ @headers.type_params.should == {}
+
+ @headers["Content-Type"] = "text/html"
+ @headers.type_params.should == {}
+ end
+end
diff --git a/spec/ruby/library/net/http/httprequest/initialize_spec.rb b/spec/ruby/library/net/http/httprequest/initialize_spec.rb
new file mode 100644
index 0000000000..88e9fb1c77
--- /dev/null
+++ b/spec/ruby/library/net/http/httprequest/initialize_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+module NetHTTPRequestSpecs
+ class TestRequest < Net::HTTPRequest
+ METHOD = "TEST"
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = true
+ end
+end
+
+describe "Net::HTTPRequest#initialize" do
+ before :each do
+ @req = NetHTTPRequestSpecs::TestRequest.allocate
+ end
+
+ it "uses the METHOD constants to set the request method" do
+ @req.send(:initialize, "/some/path")
+ @req.method.should == "TEST"
+ end
+
+ it "uses the REQUEST_HAS_BODY to set whether the Request has a body or not" do
+ @req.send(:initialize, "/some/path")
+ @req.request_body_permitted?.should be_false
+ end
+
+ it "uses the RESPONSE_HAS_BODY to set whether the Response can have a body or not" do
+ @req.send(:initialize, "/some/path")
+ @req.response_body_permitted?.should be_true
+ end
+
+ describe "when passed path" do
+ it "sets self's path to the passed path" do
+ @req.send(:initialize, "/some/path")
+ @req.path.should == "/some/path"
+ end
+ end
+
+ describe "when passed path, headers" do
+ it "uses the passed headers Hash to initialize self's header entries" do
+ @req.send(:initialize, "/some/path", "Content-Type" => "text/html")
+ @req["Content-Type"].should == "text/html"
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/body_permitted_spec.rb b/spec/ruby/library/net/http/httpresponse/body_permitted_spec.rb
new file mode 100644
index 0000000000..68a0454f79
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/body_permitted_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse.body_permitted?" do
+ it "returns true if this response type can have a response body" do
+ Net::HTTPUnknownResponse.body_permitted?.should == true
+ Net::HTTPInformation.body_permitted?.should == false
+ Net::HTTPSuccess.body_permitted?.should == true
+ Net::HTTPRedirection.body_permitted?.should == true
+ Net::HTTPClientError.body_permitted?.should == true
+ Net::HTTPServerError.body_permitted?.should == true
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/body_spec.rb b/spec/ruby/library/net/http/httpresponse/body_spec.rb
new file mode 100644
index 0000000000..79569441f1
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/body_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'shared/body'
+
+describe "Net::HTTPResponse#body" do
+ it_behaves_like :net_httpresponse_body, :body
+end
diff --git a/spec/ruby/library/net/http/httpresponse/code_spec.rb b/spec/ruby/library/net/http/httpresponse/code_spec.rb
new file mode 100644
index 0000000000..114277cb43
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/code_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#code" do
+ it "returns the result code string" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.code.should == "???"
+
+ res = Net::HTTPInformation.new("1.0", "1xx", "test response")
+ res.code.should == "1xx"
+
+ res = Net::HTTPSuccess.new("1.0", "2xx", "test response")
+ res.code.should == "2xx"
+
+ res = Net::HTTPRedirection.new("1.0", "3xx", "test response")
+ res.code.should == "3xx"
+
+ res = Net::HTTPClientError.new("1.0", "4xx", "test response")
+ res.code.should == "4xx"
+
+ res = Net::HTTPServerError.new("1.0", "5xx", "test response")
+ res.code.should == "5xx"
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/code_type_spec.rb b/spec/ruby/library/net/http/httpresponse/code_type_spec.rb
new file mode 100644
index 0000000000..fa2d572e9a
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/code_type_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#code_type" do
+ it "returns self's class" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.code_type.should == Net::HTTPUnknownResponse
+
+ res = Net::HTTPInformation.new("1.0", "1xx", "test response")
+ res.code_type.should == Net::HTTPInformation
+
+ res = Net::HTTPSuccess.new("1.0", "2xx", "test response")
+ res.code_type.should == Net::HTTPSuccess
+
+ res = Net::HTTPRedirection.new("1.0", "3xx", "test response")
+ res.code_type.should == Net::HTTPRedirection
+
+ res = Net::HTTPClientError.new("1.0", "4xx", "test response")
+ res.code_type.should == Net::HTTPClientError
+
+ res = Net::HTTPServerError.new("1.0", "5xx", "test response")
+ res.code_type.should == Net::HTTPServerError
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/entity_spec.rb b/spec/ruby/library/net/http/httpresponse/entity_spec.rb
new file mode 100644
index 0000000000..f1639042c1
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/entity_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require_relative 'shared/body'
+
+describe "Net::HTTPResponse#entity" do
+ it_behaves_like :net_httpresponse_body, :entity
+end
diff --git a/spec/ruby/library/net/http/httpresponse/error_spec.rb b/spec/ruby/library/net/http/httpresponse/error_spec.rb
new file mode 100644
index 0000000000..a3a3cee162
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/error_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#error!" do
+ it "raises self's class 'EXCEPTION_TYPE' Exception" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ lambda { res.error! }.should raise_error(Net::HTTPError)
+
+ res = Net::HTTPInformation.new("1.0", "1xx", "test response")
+ lambda { res.error! }.should raise_error(Net::HTTPError)
+
+ res = Net::HTTPSuccess.new("1.0", "2xx", "test response")
+ lambda { res.error! }.should raise_error(Net::HTTPError)
+
+ res = Net::HTTPRedirection.new("1.0", "3xx", "test response")
+ lambda { res.error! }.should raise_error(Net::HTTPRetriableError)
+
+ res = Net::HTTPClientError.new("1.0", "4xx", "test response")
+ ruby_version_is ""..."2.6" do
+ lambda { res.error! }.should raise_error(Net::HTTPServerException)
+ end
+ ruby_version_is "2.6" do
+ lambda { res.error! }.should raise_error(Net::HTTPClientException)
+ end
+
+ res = Net::HTTPServerError.new("1.0", "5xx", "test response")
+ lambda { res.error! }.should raise_error(Net::HTTPFatalError)
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/error_type_spec.rb b/spec/ruby/library/net/http/httpresponse/error_type_spec.rb
new file mode 100644
index 0000000000..6705f8b1aa
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/error_type_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#error_type" do
+ it "returns self's class 'EXCEPTION_TYPE' constant" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.error_type.should == Net::HTTPError
+
+ res = Net::HTTPInformation.new("1.0", "1xx", "test response")
+ res.error_type.should == Net::HTTPError
+
+ res = Net::HTTPSuccess.new("1.0", "2xx", "test response")
+ res.error_type.should == Net::HTTPError
+
+ res = Net::HTTPRedirection.new("1.0", "3xx", "test response")
+ res.error_type.should == Net::HTTPRetriableError
+
+ res = Net::HTTPClientError.new("1.0", "4xx", "test response")
+ ruby_version_is ""..."2.6" do
+ res.error_type.should == Net::HTTPServerException
+ end
+ ruby_version_is "2.6" do
+ res.error_type.should == Net::HTTPClientException
+ end
+
+ res = Net::HTTPServerError.new("1.0", "5xx", "test response")
+ res.error_type.should == Net::HTTPFatalError
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/exception_type_spec.rb b/spec/ruby/library/net/http/httpresponse/exception_type_spec.rb
new file mode 100644
index 0000000000..c0812cd322
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/exception_type_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse.exception_type" do
+ it "returns self's 'EXCEPTION_TYPE' constant" do
+ Net::HTTPUnknownResponse.exception_type.should == Net::HTTPError
+ Net::HTTPInformation.exception_type.should == Net::HTTPError
+ Net::HTTPSuccess.exception_type.should == Net::HTTPError
+ Net::HTTPRedirection.exception_type.should == Net::HTTPRetriableError
+ ruby_version_is ""..."2.6" do
+ Net::HTTPClientError.exception_type.should == Net::HTTPServerException
+ end
+ ruby_version_is "2.6" do
+ Net::HTTPClientError.exception_type.should == Net::HTTPClientException
+ end
+ Net::HTTPServerError.exception_type.should == Net::HTTPFatalError
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/header_spec.rb b/spec/ruby/library/net/http/httpresponse/header_spec.rb
new file mode 100644
index 0000000000..a9615feda8
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/header_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#header" do
+ it "returns self" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.response.should equal(res)
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/http_version_spec.rb b/spec/ruby/library/net/http/httpresponse/http_version_spec.rb
new file mode 100644
index 0000000000..db85696d77
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/http_version_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#http_version" do
+ it "returns self's http version" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.http_version.should == "1.0"
+
+ res = Net::HTTPUnknownResponse.new("1.1", "???", "test response")
+ res.http_version.should == "1.1"
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/initialize_spec.rb b/spec/ruby/library/net/http/httpresponse/initialize_spec.rb
new file mode 100644
index 0000000000..eb77e2e277
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/initialize_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#initialize when passed http_version, response_code, response_message" do
+ it "sets self http_version, response_code and response_message to the passed values" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.http_version.should == "1.0"
+ res.code.should == "???"
+ res.message.should == "test response"
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/inspect_spec.rb b/spec/ruby/library/net/http/httpresponse/inspect_spec.rb
new file mode 100644
index 0000000000..1e1a2c7cc7
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/inspect_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require "stringio"
+
+describe "Net::HTTPResponse#inspect" do
+ it "returns a String representation of self" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.inspect.should == "#<Net::HTTPUnknownResponse ??? test response readbody=false>"
+
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ socket = Net::BufferedIO.new(StringIO.new("test body"))
+ res.reading_body(socket, true) {}
+ res.inspect.should == "#<Net::HTTPUnknownResponse ??? test response readbody=true>"
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/message_spec.rb b/spec/ruby/library/net/http/httpresponse/message_spec.rb
new file mode 100644
index 0000000000..ae8e3678a1
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/message_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#message" do
+ it "returns self's response message" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.message.should == "test response"
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/msg_spec.rb b/spec/ruby/library/net/http/httpresponse/msg_spec.rb
new file mode 100644
index 0000000000..0e5e7eb4aa
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/msg_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#msg" do
+ it "returns self's response message" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.message.should == "test response"
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/read_body_spec.rb b/spec/ruby/library/net/http/httpresponse/read_body_spec.rb
new file mode 100644
index 0000000000..500155eb5b
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/read_body_spec.rb
@@ -0,0 +1,85 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#read_body" do
+ before :each do
+ @res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ @socket = Net::BufferedIO.new(StringIO.new("test body"))
+ end
+
+ describe "when passed no arguments" do
+ it "returns the read body" do
+ @res.reading_body(@socket, true) do
+ @res.read_body.should == "test body"
+ end
+ end
+
+ it "returns the previously read body if called a second time" do
+ @res.reading_body(@socket, true) do
+ @res.read_body.should equal(@res.read_body)
+ end
+ end
+ end
+
+ describe "when passed a buffer" do
+ it "reads the body to the passed buffer" do
+ @res.reading_body(@socket, true) do
+ buffer = ""
+ @res.read_body(buffer)
+ buffer.should == "test body"
+ end
+ end
+
+ it "returns the passed buffer" do
+ @res.reading_body(@socket, true) do
+ buffer = ""
+ @res.read_body(buffer).should equal(buffer)
+ end
+ end
+
+ it "raises an IOError if called a second time" do
+ @res.reading_body(@socket, true) do
+ @res.read_body("")
+ lambda { @res.read_body("") }.should raise_error(IOError)
+ end
+ end
+ end
+
+ describe "when passed a block" do
+ it "reads the body and yields it to the passed block (in chunks)" do
+ @res.reading_body(@socket, true) do
+ yielded = false
+
+ buffer = ""
+ @res.read_body do |body|
+ yielded = true
+ buffer << body
+ end
+
+ yielded.should be_true
+ buffer.should == "test body"
+ end
+ end
+
+ it "returns the ReadAdapter" do
+ @res.reading_body(@socket, true) do
+ @res.read_body { nil }.should be_kind_of(Net::ReadAdapter)
+ end
+ end
+
+ it "raises an IOError if called a second time" do
+ @res.reading_body(@socket, true) do
+ @res.read_body {}
+ lambda { @res.read_body {} }.should raise_error(IOError)
+ end
+ end
+ end
+
+ describe "when passed buffer and block" do
+ it "rauses an ArgumentError" do
+ @res.reading_body(@socket, true) do
+ lambda { @res.read_body("") {} }.should raise_error(ArgumentError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/read_header_spec.rb b/spec/ruby/library/net/http/httpresponse/read_header_spec.rb
new file mode 100644
index 0000000000..6af8c6bd6a
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/read_header_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#read_header" do
+ it "returns self" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.response.should equal(res)
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/read_new_spec.rb b/spec/ruby/library/net/http/httpresponse/read_new_spec.rb
new file mode 100644
index 0000000000..73c7ddc100
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/read_new_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse.read_new" do
+ it "creates a HTTPResponse object based on the response read from the passed socket" do
+ socket = Net::BufferedIO.new(StringIO.new(<<EOS))
+HTTP/1.1 200 OK
+Content-Type: text/html; charset=utf-8
+
+test-body
+EOS
+ response = Net::HTTPResponse.read_new(socket)
+
+ response.should be_kind_of(Net::HTTPOK)
+ response.code.should == "200"
+ response["Content-Type"].should == "text/html; charset=utf-8"
+
+ response.reading_body(socket, true) do
+ response.body.should == "test-body\n"
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/reading_body_spec.rb b/spec/ruby/library/net/http/httpresponse/reading_body_spec.rb
new file mode 100644
index 0000000000..ebdab891e1
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/reading_body_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+require "stringio"
+
+describe "Net::HTTPResponse#reading_body" do
+ before :each do
+ @res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ @socket = Net::BufferedIO.new(StringIO.new("test body"))
+ end
+
+ describe "when body_allowed is true" do
+ it "reads and returns the response body for self from the passed socket" do
+ @res.reading_body(@socket, true) {}.should == "test body"
+ @res.body.should == "test body"
+ end
+
+ it "yields the passed block before reading the body" do
+ yielded = false
+
+ @res.reading_body(@socket, true) do
+ @res.inspect.should == "#<Net::HTTPUnknownResponse ??? test response readbody=false>"
+ yielded = true
+ end
+
+ yielded.should be_true
+ end
+
+ describe "but the response type is not allowed to have a body" do
+ before :each do
+ @res = Net::HTTPInformation.new("1.0", "???", "test response")
+ end
+
+ it "returns nil" do
+ @res.reading_body(@socket, false) {}.should be_nil
+ @res.body.should be_nil
+ end
+
+ it "yields the passed block" do
+ yielded = false
+ @res.reading_body(@socket, true) { yielded = true }
+ yielded.should be_true
+ end
+ end
+ end
+
+ describe "when body_allowed is false" do
+ it "returns nil" do
+ @res.reading_body(@socket, false) {}.should be_nil
+ @res.body.should be_nil
+ end
+
+ it "yields the passed block" do
+ yielded = false
+ @res.reading_body(@socket, true) { yielded = true }
+ yielded.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/response_spec.rb b/spec/ruby/library/net/http/httpresponse/response_spec.rb
new file mode 100644
index 0000000000..f6035f3695
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/response_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#response" do
+ it "returns self" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ res.response.should equal(res)
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/shared/body.rb b/spec/ruby/library/net/http/httpresponse/shared/body.rb
new file mode 100644
index 0000000000..91d5fe6375
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/shared/body.rb
@@ -0,0 +1,18 @@
+describe :net_httpresponse_body, shared: true do
+ before :each do
+ @res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ @socket = Net::BufferedIO.new(StringIO.new("test body"))
+ end
+
+ it "returns the read body" do
+ @res.reading_body(@socket, true) do
+ @res.send(@method).should == "test body"
+ end
+ end
+
+ it "returns the previously read body if called a second time" do
+ @res.reading_body(@socket, true) do
+ @res.send(@method).should equal(@res.send(@method))
+ end
+ end
+end
diff --git a/spec/ruby/library/net/http/httpresponse/value_spec.rb b/spec/ruby/library/net/http/httpresponse/value_spec.rb
new file mode 100644
index 0000000000..1b19d1d6f7
--- /dev/null
+++ b/spec/ruby/library/net/http/httpresponse/value_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../../../spec_helper'
+require 'net/http'
+
+describe "Net::HTTPResponse#value" do
+ it "raises an HTTP error for non 2xx HTTP Responses" do
+ res = Net::HTTPUnknownResponse.new("1.0", "???", "test response")
+ lambda { res.value }.should raise_error(Net::HTTPError)
+
+ res = Net::HTTPInformation.new("1.0", "1xx", "test response")
+ lambda { res.value }.should raise_error(Net::HTTPError)
+
+ res = Net::HTTPSuccess.new("1.0", "2xx", "test response")
+ lambda { res.value }.should_not raise_error(Net::HTTPError)
+
+ res = Net::HTTPRedirection.new("1.0", "3xx", "test response")
+ lambda { res.value }.should raise_error(Net::HTTPRetriableError)
+
+ res = Net::HTTPClientError.new("1.0", "4xx", "test response")
+ ruby_version_is ""..."2.6" do
+ lambda { res.value }.should raise_error(Net::HTTPServerException)
+ end
+ ruby_version_is "2.6" do
+ lambda { res.value }.should raise_error(Net::HTTPClientException)
+ end
+
+ res = Net::HTTPServerError.new("1.0", "5xx", "test response")
+ lambda { res.value }.should raise_error(Net::HTTPFatalError)
+ end
+end
diff --git a/spec/ruby/library/observer/add_observer_spec.rb b/spec/ruby/library/observer/add_observer_spec.rb
new file mode 100644
index 0000000000..5217ae6dc4
--- /dev/null
+++ b/spec/ruby/library/observer/add_observer_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Observer#add_observer" do
+
+ before :each do
+ @observable = ObservableSpecs.new
+ @observer = ObserverCallbackSpecs.new
+ end
+
+ it "adds the observer" do
+ @observer.value.should == nil
+ @observable.changed
+ @observable.notify_observers("test")
+ @observer.value.should == nil
+
+ @observable.add_observer(@observer)
+ @observable.changed
+ @observable.notify_observers("test2")
+ @observer.value.should == "test2"
+ end
+
+end
diff --git a/spec/ruby/library/observer/count_observers_spec.rb b/spec/ruby/library/observer/count_observers_spec.rb
new file mode 100644
index 0000000000..c93674196d
--- /dev/null
+++ b/spec/ruby/library/observer/count_observers_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Observer#count_observers" do
+ before :each do
+ @observable = ObservableSpecs.new
+ @observer = ObserverCallbackSpecs.new
+ @observer2 = ObserverCallbackSpecs.new
+ end
+
+ it "returns the number of observers" do
+ @observable.count_observers.should == 0
+ @observable.add_observer(@observer)
+ @observable.count_observers.should == 1
+ @observable.add_observer(@observer2)
+ @observable.count_observers.should == 2
+ end
+
+ it "returns the number of unique observers" do
+ 2.times { @observable.add_observer(@observer) }
+ @observable.count_observers.should == 1
+ end
+end
diff --git a/spec/ruby/library/observer/delete_observer_spec.rb b/spec/ruby/library/observer/delete_observer_spec.rb
new file mode 100644
index 0000000000..52be1a6cba
--- /dev/null
+++ b/spec/ruby/library/observer/delete_observer_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Observer#delete_observer" do
+ before :each do
+ @observable = ObservableSpecs.new
+ @observer = ObserverCallbackSpecs.new
+ end
+
+ it "deletes the observer" do
+ @observable.add_observer(@observer)
+ @observable.delete_observer(@observer)
+
+ @observable.changed
+ @observable.notify_observers("test")
+ @observer.value.should == nil
+ end
+
+end
diff --git a/spec/ruby/library/observer/delete_observers_spec.rb b/spec/ruby/library/observer/delete_observers_spec.rb
new file mode 100644
index 0000000000..186e93a013
--- /dev/null
+++ b/spec/ruby/library/observer/delete_observers_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Observer#delete_observers" do
+ before :each do
+ @observable = ObservableSpecs.new
+ @observer = ObserverCallbackSpecs.new
+ end
+
+ it "deletes the observers" do
+ @observable.add_observer(@observer)
+ @observable.delete_observers
+
+ @observable.changed
+ @observable.notify_observers("test")
+ @observer.value.should == nil
+ end
+
+end
diff --git a/spec/ruby/library/observer/fixtures/classes.rb b/spec/ruby/library/observer/fixtures/classes.rb
new file mode 100644
index 0000000000..70cd1b1be2
--- /dev/null
+++ b/spec/ruby/library/observer/fixtures/classes.rb
@@ -0,0 +1,17 @@
+require 'observer'
+
+class ObserverCallbackSpecs
+ attr_reader :value
+
+ def initialize
+ @value = nil
+ end
+
+ def update(value)
+ @value = value
+ end
+end
+
+class ObservableSpecs
+ include Observable
+end
diff --git a/spec/ruby/library/observer/notify_observers_spec.rb b/spec/ruby/library/observer/notify_observers_spec.rb
new file mode 100644
index 0000000000..86422fc4a0
--- /dev/null
+++ b/spec/ruby/library/observer/notify_observers_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Observer#notify_observers" do
+
+ before :each do
+ @observable = ObservableSpecs.new
+ @observer = ObserverCallbackSpecs.new
+ @observable.add_observer(@observer)
+ end
+
+ it "must call changed before notifying observers" do
+ @observer.value.should == nil
+ @observable.notify_observers("test")
+ @observer.value.should == nil
+ end
+
+ it "verifies observer responds to update" do
+ lambda {
+ @observable.add_observer(@observable)
+ }.should raise_error(NoMethodError)
+ end
+
+ it "receives the callback" do
+ @observer.value.should == nil
+ @observable.changed
+ @observable.notify_observers("test")
+ @observer.value.should == "test"
+ end
+
+end
diff --git a/spec/ruby/library/open3/capture2_spec.rb b/spec/ruby/library/open3/capture2_spec.rb
new file mode 100644
index 0000000000..f707281d7f
--- /dev/null
+++ b/spec/ruby/library/open3/capture2_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'open3'
+
+describe "Open3.capture2" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/open3/capture2e_spec.rb b/spec/ruby/library/open3/capture2e_spec.rb
new file mode 100644
index 0000000000..7dd42f3491
--- /dev/null
+++ b/spec/ruby/library/open3/capture2e_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'open3'
+
+describe "Open3.capture2e" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/open3/capture3_spec.rb b/spec/ruby/library/open3/capture3_spec.rb
new file mode 100644
index 0000000000..55c858c03f
--- /dev/null
+++ b/spec/ruby/library/open3/capture3_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'open3'
+
+describe "Open3.capture3" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/open3/pipeline_r_spec.rb b/spec/ruby/library/open3/pipeline_r_spec.rb
new file mode 100644
index 0000000000..e1b476f856
--- /dev/null
+++ b/spec/ruby/library/open3/pipeline_r_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'open3'
+
+describe "Open3.pipeline_r" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/open3/pipeline_rw_spec.rb b/spec/ruby/library/open3/pipeline_rw_spec.rb
new file mode 100644
index 0000000000..8d889a200a
--- /dev/null
+++ b/spec/ruby/library/open3/pipeline_rw_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'open3'
+
+describe "Open3.pipeline_rw" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/open3/pipeline_spec.rb b/spec/ruby/library/open3/pipeline_spec.rb
new file mode 100644
index 0000000000..5dc628dcaf
--- /dev/null
+++ b/spec/ruby/library/open3/pipeline_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'open3'
+
+describe "Open3.pipeline" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/open3/pipeline_start_spec.rb b/spec/ruby/library/open3/pipeline_start_spec.rb
new file mode 100644
index 0000000000..af426807fe
--- /dev/null
+++ b/spec/ruby/library/open3/pipeline_start_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'open3'
+
+describe "Open3.pipeline_start" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/open3/pipeline_w_spec.rb b/spec/ruby/library/open3/pipeline_w_spec.rb
new file mode 100644
index 0000000000..0c2a3ca4e7
--- /dev/null
+++ b/spec/ruby/library/open3/pipeline_w_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'open3'
+
+describe "Open3.pipeline_w" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/open3/popen2_spec.rb b/spec/ruby/library/open3/popen2_spec.rb
new file mode 100644
index 0000000000..a1a251660a
--- /dev/null
+++ b/spec/ruby/library/open3/popen2_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'open3'
+
+describe "Open3.popen2" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/open3/popen2e_spec.rb b/spec/ruby/library/open3/popen2e_spec.rb
new file mode 100644
index 0000000000..e65607160c
--- /dev/null
+++ b/spec/ruby/library/open3/popen2e_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'open3'
+
+describe "Open3.popen2e" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/open3/popen3_spec.rb b/spec/ruby/library/open3/popen3_spec.rb
new file mode 100644
index 0000000000..9afb5f5382
--- /dev/null
+++ b/spec/ruby/library/open3/popen3_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require 'open3'
+
+describe "Open3.popen3" do
+ it "returns in, out, err and a thread waiting the process" do
+ stdin, out, err, waiter = Open3.popen3(ruby_cmd("print :foo"))
+ begin
+ stdin.should be_kind_of IO
+ out.should be_kind_of IO
+ err.should be_kind_of IO
+ waiter.should be_kind_of Thread
+
+ out.read.should == "foo"
+ ensure
+ stdin.close
+ out.close
+ err.close
+ waiter.join
+ end
+ end
+
+ it "executes a process with a pipe to read stdout" do
+ Open3.popen3(ruby_cmd("print :foo")) do |stdin, out, err|
+ out.read.should == "foo"
+ end
+ end
+
+ it "executes a process with a pipe to read stderr" do
+ Open3.popen3(ruby_cmd("STDERR.print :foo")) do |stdin, out, err|
+ err.read.should == "foo"
+ end
+ end
+
+ it "executes a process with a pipe to write stdin" do
+ Open3.popen3(ruby_cmd("print STDIN.read")) do |stdin, out, err|
+ stdin.write("foo")
+ stdin.close
+ out.read.should == "foo"
+ end
+ end
+
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/openssl/cipher_spec.rb b/spec/ruby/library/openssl/cipher_spec.rb
new file mode 100644
index 0000000000..f66f25f9c6
--- /dev/null
+++ b/spec/ruby/library/openssl/cipher_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'shared/constants'
+require 'openssl'
+
+describe "OpenSSL::Cipher's CipherError" do
+ it "exists under OpenSSL::Cipher namespace" do
+ OpenSSL::Cipher.should have_constant :CipherError
+ end
+end
diff --git a/spec/ruby/library/openssl/config/freeze_spec.rb b/spec/ruby/library/openssl/config/freeze_spec.rb
new file mode 100644
index 0000000000..69c96cf85e
--- /dev/null
+++ b/spec/ruby/library/openssl/config/freeze_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../../spec_helper'
+require_relative '../shared/constants'
+
+require 'openssl'
+
+describe "OpenSSL::Config#freeze" do
+ it "needs to be reviewed for completeness"
+
+ it "freezes" do
+ c = OpenSSL::Config.new
+ lambda {
+ c['foo'] = [ ['key', 'value'] ]
+ }.should_not raise_error
+ c.freeze
+ c.frozen?.should be_true
+ lambda {
+ c['foo'] = [ ['key', 'value'] ]
+ }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/openssl/hmac/digest_spec.rb b/spec/ruby/library/openssl/hmac/digest_spec.rb
new file mode 100644
index 0000000000..22bc7023bc
--- /dev/null
+++ b/spec/ruby/library/openssl/hmac/digest_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../../spec_helper'
+require_relative '../shared/constants'
+require 'openssl'
+
+describe "OpenSSL::HMAC.digest" do
+ it "returns an SHA1 digest" do
+ cur_digest = OpenSSL::Digest::SHA1.new
+ cur_digest.digest.should == HMACConstants::BlankSHA1Digest
+ digest = OpenSSL::HMAC.digest(cur_digest,
+ HMACConstants::Key,
+ HMACConstants::Contents)
+ digest.should == HMACConstants::SHA1Digest
+ end
+end
+
+# Should add in similar specs for MD5, RIPEMD160, and SHA256
diff --git a/spec/ruby/library/openssl/hmac/hexdigest_spec.rb b/spec/ruby/library/openssl/hmac/hexdigest_spec.rb
new file mode 100644
index 0000000000..7f73b2db09
--- /dev/null
+++ b/spec/ruby/library/openssl/hmac/hexdigest_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../../spec_helper'
+require_relative '../shared/constants'
+require 'openssl'
+
+describe "OpenSSL::HMAC.hexdigest" do
+ it "returns an SHA1 hex digest" do
+ cur_digest = OpenSSL::Digest::SHA1.new
+ cur_digest.hexdigest.should == HMACConstants::BlankSHA1HexDigest
+ hexdigest = OpenSSL::HMAC.hexdigest(cur_digest,
+ HMACConstants::Key,
+ HMACConstants::Contents)
+ hexdigest.should == HMACConstants::SHA1Hexdigest
+ end
+end
+
+# Should add in similar specs for MD5, RIPEMD160, and SHA256
diff --git a/spec/ruby/library/openssl/random/pseudo_bytes_spec.rb b/spec/ruby/library/openssl/random/pseudo_bytes_spec.rb
new file mode 100644
index 0000000000..c5e450ce3d
--- /dev/null
+++ b/spec/ruby/library/openssl/random/pseudo_bytes_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/random_bytes'
+
+if defined?(OpenSSL::Random.pseudo_bytes)
+ describe "OpenSSL::Random.pseudo_bytes" do
+ it_behaves_like :openssl_random_bytes, :pseudo_bytes
+ end
+end
diff --git a/spec/ruby/library/openssl/random/random_bytes_spec.rb b/spec/ruby/library/openssl/random/random_bytes_spec.rb
new file mode 100644
index 0000000000..2678885da4
--- /dev/null
+++ b/spec/ruby/library/openssl/random/random_bytes_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/random_bytes'
+
+describe "OpenSSL::Random.random_bytes" do
+ it_behaves_like :openssl_random_bytes, :random_bytes
+end
diff --git a/spec/ruby/library/openssl/random/shared/random_bytes.rb b/spec/ruby/library/openssl/random/shared/random_bytes.rb
new file mode 100644
index 0000000000..3a3a6b2bc4
--- /dev/null
+++ b/spec/ruby/library/openssl/random/shared/random_bytes.rb
@@ -0,0 +1,29 @@
+require_relative '../../../../spec_helper'
+require 'openssl'
+
+describe :openssl_random_bytes, shared: true do |cmd|
+ it "generates a random binary string of specified length" do
+ (1..64).each do |idx|
+ bytes = OpenSSL::Random.send(@method, idx)
+ bytes.should be_kind_of(String)
+ bytes.length.should == idx
+ end
+ end
+
+ it "generates different binary strings with subsequent invocations" do
+ # quick and dirty check, but good enough
+ values = []
+ 256.times do
+ val = OpenSSL::Random.send(@method, 16)
+ # make sure the random bytes are not repeating
+ values.include?(val).should == false
+ values << val
+ end
+ end
+
+ it "raises ArgumentError on negative arguments" do
+ lambda {
+ OpenSSL::Random.send(@method, -1)
+ }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/openssl/shared/constants.rb b/spec/ruby/library/openssl/shared/constants.rb
new file mode 100644
index 0000000000..0bed4156a1
--- /dev/null
+++ b/spec/ruby/library/openssl/shared/constants.rb
@@ -0,0 +1,11 @@
+# -*- encoding: binary -*-
+module HMACConstants
+
+ Contents = "Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. \nIt has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. \nIt was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
+ Key = 'sekrit'
+
+ BlankSHA1Digest = "\3329\243\356^kK\r2U\277\357\225`\030\220\257\330\a\t"
+ SHA1Digest = "\236\022\323\341\037\236\262n\344\t\372:\004J\242\330\257\270\363\264"
+ BlankSHA1HexDigest = "da39a3ee5e6b4b0d3255bfef95601890afd80709"
+ SHA1Hexdigest = "9e12d3e11f9eb26ee409fa3a044aa2d8afb8f3b4"
+end
diff --git a/spec/ruby/library/openssl/x509/name/parse_spec.rb b/spec/ruby/library/openssl/x509/name/parse_spec.rb
new file mode 100644
index 0000000000..6a43f5aea3
--- /dev/null
+++ b/spec/ruby/library/openssl/x509/name/parse_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../../../spec_helper'
+require 'openssl'
+
+describe "OpenSSL::X509::Name.parse" do
+ it "parses a /-delimited string of key-value pairs into a Name" do
+ dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org"
+ name = OpenSSL::X509::Name.parse(dn)
+
+ name.to_s.should == dn
+
+ ary = name.to_a
+
+ ary[0][0].should == "DC"
+ ary[1][0].should == "DC"
+ ary[2][0].should == "CN"
+ ary[0][1].should == "org"
+ ary[1][1].should == "ruby-lang"
+ ary[2][1].should == "www.ruby-lang.org"
+ ary[0][2].should == OpenSSL::ASN1::IA5STRING
+ ary[1][2].should == OpenSSL::ASN1::IA5STRING
+ ary[2][2].should == OpenSSL::ASN1::UTF8STRING
+ end
+
+ it "parses a comma-delimited string of key-value pairs into a name" do
+ dn = "DC=org, DC=ruby-lang, CN=www.ruby-lang.org"
+ name = OpenSSL::X509::Name.parse(dn)
+
+ name.to_s.should == "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org"
+
+ ary = name.to_a
+
+ ary[0][1].should == "org"
+ ary[1][1].should == "ruby-lang"
+ ary[2][1].should == "www.ruby-lang.org"
+ end
+
+ it "raises TypeError if the given string contains no key/value pairs" do
+ lambda do
+ OpenSSL::X509::Name.parse("hello")
+ end.should raise_error(TypeError)
+ end
+
+ it "raises OpenSSL::X509::NameError if the given string contains invalid keys" do
+ lambda do
+ OpenSSL::X509::Name.parse("hello=goodbye")
+ end.should raise_error(OpenSSL::X509::NameError)
+ end
+end
diff --git a/spec/ruby/library/openstruct/delete_field_spec.rb b/spec/ruby/library/openstruct/delete_field_spec.rb
new file mode 100644
index 0000000000..9ac80196cc
--- /dev/null
+++ b/spec/ruby/library/openstruct/delete_field_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require 'ostruct'
+
+describe "OpenStruct#delete_field" do
+ before :each do
+ @os = OpenStruct.new(name: "John Smith", age: 70, pension: 300)
+ end
+
+ it "removes the named field from self's method/value table" do
+ @os.delete_field(:name)
+ @os[:name].should be_nil
+ end
+
+ it "does remove the accessor methods" do
+ @os.delete_field(:name)
+ @os.respond_to?(:name).should be_false
+ @os.respond_to?(:name=).should be_false
+ end
+end
diff --git a/spec/ruby/library/openstruct/element_reference_spec.rb b/spec/ruby/library/openstruct/element_reference_spec.rb
new file mode 100644
index 0000000000..c425991b0f
--- /dev/null
+++ b/spec/ruby/library/openstruct/element_reference_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require "ostruct"
+
+describe "OpenStruct#[]" do
+ before :each do
+ @os = OpenStruct.new
+ end
+
+ it "returns the associated value" do
+ @os.foo = 42
+ @os[:foo].should == 42
+ end
+end
diff --git a/spec/ruby/library/openstruct/element_set_spec.rb b/spec/ruby/library/openstruct/element_set_spec.rb
new file mode 100644
index 0000000000..eeb5a8b318
--- /dev/null
+++ b/spec/ruby/library/openstruct/element_set_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require "ostruct"
+
+describe "OpenStruct#[]=" do
+ before :each do
+ @os = OpenStruct.new
+ end
+
+ it "sets the associated value" do
+ @os[:foo] = 42
+ @os.foo.should == 42
+ end
+end
diff --git a/spec/ruby/library/openstruct/equal_value_spec.rb b/spec/ruby/library/openstruct/equal_value_spec.rb
new file mode 100644
index 0000000000..103ac13588
--- /dev/null
+++ b/spec/ruby/library/openstruct/equal_value_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require "ostruct"
+require_relative 'fixtures/classes'
+
+describe "OpenStruct#==" do
+ before :each do
+ @os = OpenStruct.new(name: "John")
+ end
+
+ it "returns false when the passed argument is no OpenStruct" do
+ (@os == Object.new).should be_false
+ (@os == "Test").should be_false
+ (@os == 10).should be_false
+ (@os == :sym).should be_false
+ end
+
+ it "returns true when self and other are equal method/value wise" do
+ (@os == @os).should be_true
+ (@os == OpenStruct.new(name: "John")).should be_true
+ (@os == OpenStructSpecs::OpenStructSub.new(name: "John")).should be_true
+
+ (@os == OpenStruct.new(name: "Jonny")).should be_false
+ (@os == OpenStructSpecs::OpenStructSub.new(name: "Jonny")).should be_false
+
+ (@os == OpenStruct.new(name: "John", age: 20)).should be_false
+ (@os == OpenStructSpecs::OpenStructSub.new(name: "John", age: 20)).should be_false
+ end
+end
diff --git a/spec/ruby/library/openstruct/fixtures/classes.rb b/spec/ruby/library/openstruct/fixtures/classes.rb
new file mode 100644
index 0000000000..da42e8511d
--- /dev/null
+++ b/spec/ruby/library/openstruct/fixtures/classes.rb
@@ -0,0 +1,4 @@
+module OpenStructSpecs
+ class OpenStructSub < OpenStruct
+ end
+end
diff --git a/spec/ruby/library/openstruct/frozen_spec.rb b/spec/ruby/library/openstruct/frozen_spec.rb
new file mode 100644
index 0000000000..27931fe5ac
--- /dev/null
+++ b/spec/ruby/library/openstruct/frozen_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require 'ostruct'
+
+describe "OpenStruct.new when frozen" do
+ before :each do
+ @os = OpenStruct.new(name: "John Smith", age: 70, pension: 300).freeze
+ end
+ #
+ # method_missing case handled in method_missing_spec.rb
+ #
+ it "is still readable" do
+ @os.age.should eql(70)
+ @os.pension.should eql(300)
+ @os.name.should == "John Smith"
+ end
+
+ it "is not writeable" do
+ lambda{ @os.age = 42 }.should raise_error( RuntimeError )
+ end
+
+ it "cannot create new fields" do
+ lambda{ @os.state = :new }.should raise_error( RuntimeError )
+ end
+
+ it "creates a frozen clone" do
+ f = @os.clone
+ f.age.should == 70
+ lambda{ f.age = 0 }.should raise_error( RuntimeError )
+ lambda{ f.state = :newer }.should raise_error( RuntimeError )
+ end
+
+ it "creates an unfrozen dup" do
+ d = @os.dup
+ d.age.should == 70
+ d.age = 42
+ d.age.should == 42
+ end
+end
diff --git a/spec/ruby/library/openstruct/initialize_spec.rb b/spec/ruby/library/openstruct/initialize_spec.rb
new file mode 100644
index 0000000000..dee5de48c6
--- /dev/null
+++ b/spec/ruby/library/openstruct/initialize_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'ostruct'
+
+describe "OpenStruct#initialize" do
+ it "is private" do
+ OpenStruct.should have_private_instance_method(:initialize)
+ end
+end
diff --git a/spec/ruby/library/openstruct/inspect_spec.rb b/spec/ruby/library/openstruct/inspect_spec.rb
new file mode 100644
index 0000000000..e2fed41528
--- /dev/null
+++ b/spec/ruby/library/openstruct/inspect_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'ostruct'
+require_relative 'fixtures/classes'
+require_relative 'shared/inspect'
+
+describe "OpenStruct#inspect" do
+ it_behaves_like :ostruct_inspect, :inspect
+end
diff --git a/spec/ruby/library/openstruct/marshal_dump_spec.rb b/spec/ruby/library/openstruct/marshal_dump_spec.rb
new file mode 100644
index 0000000000..5c38fd959e
--- /dev/null
+++ b/spec/ruby/library/openstruct/marshal_dump_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require "ostruct"
+
+describe "OpenStruct#marshal_dump" do
+ it "returns the method/value table" do
+ os = OpenStruct.new("age" => 20, "name" => "John")
+ os.marshal_dump.should == { age: 20, name: "John" }
+ end
+end
diff --git a/spec/ruby/library/openstruct/marshal_load_spec.rb b/spec/ruby/library/openstruct/marshal_load_spec.rb
new file mode 100644
index 0000000000..e07c4cef05
--- /dev/null
+++ b/spec/ruby/library/openstruct/marshal_load_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require "ostruct"
+
+describe "OpenStruct#marshal_load when passed [Hash]" do
+ it "defines methods based on the passed Hash" do
+ os = OpenStruct.new
+ os.marshal_load(age: 20, name: "John")
+
+ os.age.should eql(20)
+ os.name.should == "John"
+ end
+end
diff --git a/spec/ruby/library/openstruct/method_missing_spec.rb b/spec/ruby/library/openstruct/method_missing_spec.rb
new file mode 100644
index 0000000000..eefe30661a
--- /dev/null
+++ b/spec/ruby/library/openstruct/method_missing_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../../spec_helper'
+require "ostruct"
+
+describe "OpenStruct#method_missing when called with a method name ending in '='" do
+ before :each do
+ @os = OpenStruct.new
+ end
+
+ it "raises an ArgumentError when not passed any additional arguments" do
+ lambda { @os.method_missing(:test=) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError when self is frozen" do
+ @os.freeze
+ lambda { @os.method_missing(:test=, "test") }.should raise_error(RuntimeError)
+ end
+
+ it "creates accessor methods" do
+ @os.method_missing(:test=, "test")
+ @os.respond_to?(:test=).should be_true
+ @os.respond_to?(:test).should be_true
+
+ @os.test.should == "test"
+ @os.test = "changed"
+ @os.test.should == "changed"
+ end
+
+ it "updates the method/value table with the passed method/value" do
+ @os.method_missing(:test=, "test")
+ @os.send(:table)[:test].should == "test"
+ end
+end
+
+describe "OpenStruct#method_missing when passed additional arguments" do
+ it "raises a NoMethodError" do
+ os = OpenStruct.new
+ lambda { os.method_missing(:test, 1, 2, 3) }.should raise_error(NoMethodError)
+ end
+end
+
+describe "OpenStruct#method_missing when not passed any additional arguments" do
+ it "returns the value for the passed method from the method/value table" do
+ os = OpenStruct.new(age: 20)
+ os.method_missing(:age).should eql(20)
+ os.method_missing(:name).should be_nil
+ end
+end
diff --git a/spec/ruby/library/openstruct/new_spec.rb b/spec/ruby/library/openstruct/new_spec.rb
new file mode 100644
index 0000000000..5d2cacea40
--- /dev/null
+++ b/spec/ruby/library/openstruct/new_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require 'ostruct'
+
+describe "OpenStruct.new when passed [Hash]" do
+ before :each do
+ @os = OpenStruct.new(name: "John Smith", age: 70, pension: 300)
+ end
+
+ it "creates an attribute for each key of the passed Hash" do
+ @os.age.should eql(70)
+ @os.pension.should eql(300)
+ @os.name.should == "John Smith"
+ end
+end
+
+describe "OpenStruct.new when passed no arguments" do
+ it "returns a new OpenStruct Object without any attributes" do
+ OpenStruct.new.to_s.should == "#<OpenStruct>"
+ end
+end
diff --git a/spec/ruby/library/openstruct/shared/inspect.rb b/spec/ruby/library/openstruct/shared/inspect.rb
new file mode 100644
index 0000000000..ffcd690e1f
--- /dev/null
+++ b/spec/ruby/library/openstruct/shared/inspect.rb
@@ -0,0 +1,20 @@
+describe :ostruct_inspect, shared: true do
+ it "returns a String representation of self" do
+ os = OpenStruct.new(name: "John Smith")
+ os.send(@method).should == "#<OpenStruct name=\"John Smith\">"
+
+ os = OpenStruct.new(age: 20, name: "John Smith")
+ os.send(@method).should be_kind_of(String)
+ end
+
+ it "correctly handles self-referential OpenStructs" do
+ os = OpenStruct.new
+ os.self = os
+ os.send(@method).should == "#<OpenStruct self=#<OpenStruct ...>>"
+ end
+
+ it "correctly handles OpenStruct subclasses" do
+ os = OpenStructSpecs::OpenStructSub.new(name: "John Smith")
+ os.send(@method).should == "#<OpenStructSpecs::OpenStructSub name=\"John Smith\">"
+ end
+end
diff --git a/spec/ruby/library/openstruct/to_h_spec.rb b/spec/ruby/library/openstruct/to_h_spec.rb
new file mode 100644
index 0000000000..1a8a87a94d
--- /dev/null
+++ b/spec/ruby/library/openstruct/to_h_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require 'ostruct'
+
+describe "OpenStruct#to_h" do
+ before :each do
+ @h = {name: "John Smith", age: 70, pension: 300}
+ @os = OpenStruct.new(@h)
+ @to_h = @os.to_h
+ end
+
+ it "returns a Hash with members as keys" do
+ @to_h.should == @h
+ end
+
+ it "returns a Hash with keys as symbols" do
+ os = OpenStruct.new("name" => "John Smith", "age" => 70)
+ os.pension = 300
+ os.to_h.should == @h
+ end
+
+ it "does not return the hash used as initializer" do
+ @to_h.should_not equal(@h)
+ end
+
+ it "returns a Hash that is independent from the struct" do
+ @to_h[:age] = 71
+ @os.age.should == 70
+ end
+
+ ruby_version_is "2.6" do
+ it "converts [key, value] pairs returned by the block to a hash" do
+ h = @os.to_h {|key, value| [key.to_s, value * 2]}
+ h.should == {"name" => "John SmithJohn Smith", "age" => 140, "pension" => 600}
+ end
+ end
+end
diff --git a/spec/ruby/library/openstruct/to_s_spec.rb b/spec/ruby/library/openstruct/to_s_spec.rb
new file mode 100644
index 0000000000..73d91bf981
--- /dev/null
+++ b/spec/ruby/library/openstruct/to_s_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'ostruct'
+require_relative 'fixtures/classes'
+require_relative 'shared/inspect'
+
+describe "OpenStruct#to_s" do
+ it_behaves_like :ostruct_inspect, :to_s
+end
diff --git a/spec/ruby/library/optionparser/order_spec.rb b/spec/ruby/library/optionparser/order_spec.rb
new file mode 100644
index 0000000000..9d8f48d320
--- /dev/null
+++ b/spec/ruby/library/optionparser/order_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require 'optparse'
+
+describe "OptionParser#order" do
+ ruby_version_is '2.4' do
+ it "accepts `into` keyword argument and stores result in it" do
+ options = {}
+ parser = OptionParser.new do |opts|
+ opts.on("-v", "--[no-]verbose", "Run verbosely")
+ opts.on("-r", "--require LIBRARY", "Require the LIBRARY before executing your script")
+ end
+ parser.order %w[--verbose --require optparse], into: options
+
+ options.should == { verbose: true, require: "optparse" }
+ end
+ end
+end
+
+describe "OptionParser#order!" do
+ ruby_version_is '2.4' do
+ it "accepts `into` keyword argument and stores result in it" do
+ options = {}
+ parser = OptionParser.new do |opts|
+ opts.on("-v", "--[no-]verbose", "Run verbosely")
+ opts.on("-r", "--require LIBRARY", "Require the LIBRARY before executing your script")
+ end
+ parser.order! %w[--verbose --require optparse], into: options
+
+ options.should == { verbose: true, require: "optparse" }
+ end
+ end
+end
diff --git a/spec/ruby/library/optionparser/parse_spec.rb b/spec/ruby/library/optionparser/parse_spec.rb
new file mode 100644
index 0000000000..5b105a7d0e
--- /dev/null
+++ b/spec/ruby/library/optionparser/parse_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require 'optparse'
+
+describe "OptionParser#parse" do
+ ruby_version_is '2.4' do
+ it "accepts `into` keyword argument and stores result in it" do
+ options = {}
+ parser = OptionParser.new do |opts|
+ opts.on("-v", "--[no-]verbose", "Run verbosely")
+ opts.on("-r", "--require LIBRARY", "Require the LIBRARY before executing your script")
+ end
+ parser.parse %w[--verbose --require optparse], into: options
+
+ options.should == { verbose: true, require: "optparse" }
+ end
+ end
+end
+
+describe "OptionParser#parse!" do
+ ruby_version_is '2.4' do
+ it "accepts `into` keyword argument and stores result in it" do
+ options = {}
+ parser = OptionParser.new do |opts|
+ opts.on("-v", "--[no-]verbose", "Run verbosely")
+ opts.on("-r", "--require LIBRARY", "Require the LIBRARY before executing your script")
+ end
+ parser.parse! %w[--verbose --require optparse], into: options
+
+ options.should == { verbose: true, require: "optparse" }
+ end
+ end
+end
diff --git a/spec/ruby/library/pathname/absolute_spec.rb b/spec/ruby/library/pathname/absolute_spec.rb
new file mode 100644
index 0000000000..dce3ae72ee
--- /dev/null
+++ b/spec/ruby/library/pathname/absolute_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+require 'pathname'
+
+describe "Pathname#absolute?" do
+
+ it "returns true for the root directory" do
+ Pathname.new('/').absolute?.should == true
+ end
+
+ it "returns true for a dir starting with a slash" do
+ Pathname.new('/usr/local/bin').absolute?.should == true
+ end
+
+ it "returns false for a dir not starting with a slash" do
+ Pathname.new('fish').absolute?.should == false
+ end
+
+ it "returns false for a dir not starting with a slash" do
+ Pathname.new('fish/dog/cow').absolute?.should == false
+ end
+
+end
diff --git a/spec/ruby/library/pathname/empty_spec.rb b/spec/ruby/library/pathname/empty_spec.rb
new file mode 100644
index 0000000000..6f46486a69
--- /dev/null
+++ b/spec/ruby/library/pathname/empty_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+require 'pathname'
+
+ruby_version_is '2.4' do
+ describe 'Pathname#empty?' do
+ before :all do
+ @file = tmp 'new_file_path_name.txt'
+ touch @file
+ @dir = tmp 'new_directory_path_name'
+ Dir.mkdir @dir
+ end
+
+ after :all do
+ rm_r @file
+ rm_r @dir
+ end
+
+ it 'returns true when file is not empty' do
+ Pathname.new(__FILE__).empty?.should be_false
+ end
+
+ it 'returns false when the directory is not empty' do
+ Pathname.new(__dir__).empty?.should be_false
+ end
+
+ it 'return true when file is empty' do
+ Pathname.new(@file).empty?.should be_true
+ end
+
+ it 'returns true when directory is empty' do
+ Pathname.new(@dir).empty?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/library/pathname/equal_value_spec.rb b/spec/ruby/library/pathname/equal_value_spec.rb
new file mode 100644
index 0000000000..92d4767e76
--- /dev/null
+++ b/spec/ruby/library/pathname/equal_value_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require 'pathname'
+
+describe "Pathname#==" do
+
+ it "returns true when identical paths are used" do
+ (Pathname.new('') == Pathname.new('')).should == true
+ end
+
+ it "returns true when identical paths are used" do
+ (Pathname.new('') == Pathname.new('/usr/local/bin')).should == false
+ end
+
+end
diff --git a/spec/ruby/library/pathname/hash_spec.rb b/spec/ruby/library/pathname/hash_spec.rb
new file mode 100644
index 0000000000..da1b8f4f76
--- /dev/null
+++ b/spec/ruby/library/pathname/hash_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require 'pathname'
+
+describe "Pathname#hash" do
+
+ it "is equal to the hash of the pathname" do
+ Pathname.new('/usr/local/bin/').hash.should == '/usr/local/bin/'.hash
+ end
+
+ it "is not equal the hash of a different pathname" do
+ Pathname.new('/usr/local/bin/').hash.should_not == '/usr/bin/'.hash
+ end
+
+end
diff --git a/spec/ruby/library/pathname/join_spec.rb b/spec/ruby/library/pathname/join_spec.rb
new file mode 100644
index 0000000000..a0877777fc
--- /dev/null
+++ b/spec/ruby/library/pathname/join_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require 'pathname'
+
+describe "Pathname#join" do
+ it "without separators" do
+ Pathname.new('/usr').join(Pathname.new('foo')).should == Pathname.new('/usr/foo')
+ end
+
+ it "with separators" do
+ Pathname.new('/usr').join(Pathname.new('/foo')).should == Pathname.new('/foo')
+ end
+
+ it "with a string" do
+ Pathname.new('/usr').join('foo').should == Pathname.new('/usr/foo')
+ end
+
+ it "with root" do
+ Pathname.new('/usr').join(Pathname.new('/')).should == Pathname.new('/')
+ end
+
+ it "with a relative path" do
+ Pathname.new('/usr').join(Pathname.new('../foo')).should == Pathname.new('/foo')
+ end
+
+ it "a relative path with current" do
+ Pathname.new('.').join(Pathname.new('foo')).should == Pathname.new('foo')
+ end
+
+ it "an absolute path with current" do
+ Pathname.new('.').join(Pathname.new('/foo')).should == Pathname.new('/foo')
+ end
+
+ it "a prefixed relative path with current" do
+ Pathname.new('.').join(Pathname.new('./foo')).should == Pathname.new('foo')
+ end
+
+ it "multiple paths" do
+ Pathname.new('.').join(Pathname.new('./foo'), 'bar').should == Pathname.new('foo/bar')
+ end
+end
diff --git a/spec/ruby/library/pathname/new_spec.rb b/spec/ruby/library/pathname/new_spec.rb
new file mode 100644
index 0000000000..4f519af398
--- /dev/null
+++ b/spec/ruby/library/pathname/new_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require 'pathname'
+
+describe "Pathname.new" do
+ it "returns a new Pathname Object with 1 argument" do
+ Pathname.new('').should be_kind_of(Pathname)
+ end
+
+ it "raises an ArgumentError when called with \0" do
+ lambda { Pathname.new("\0")}.should raise_error(ArgumentError)
+ end
+
+ it "is tainted if path is tainted" do
+ path = '/usr/local/bin'.taint
+ Pathname.new(path).tainted?.should == true
+ end
+
+ it "raises a TypeError if not passed a String type" do
+ lambda { Pathname.new(nil) }.should raise_error(TypeError)
+ lambda { Pathname.new(0) }.should raise_error(TypeError)
+ lambda { Pathname.new(true) }.should raise_error(TypeError)
+ lambda { Pathname.new(false) }.should raise_error(TypeError)
+ end
+
+ it "initializes with an object with to_path" do
+ Pathname.new(mock_to_path('foo')).should == Pathname.new('foo')
+ end
+end
diff --git a/spec/ruby/library/pathname/parent_spec.rb b/spec/ruby/library/pathname/parent_spec.rb
new file mode 100644
index 0000000000..3843bb22ed
--- /dev/null
+++ b/spec/ruby/library/pathname/parent_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require 'pathname'
+
+describe "Pathname#parent" do
+
+ it "has parent of root as root" do
+ Pathname.new('/').parent.to_s.should == '/'
+ end
+
+ it "has parent of /usr/ as root" do
+ Pathname.new('/usr/').parent.to_s.should == '/'
+ end
+
+ it "has parent of /usr/local as root" do
+ Pathname.new('/usr/local').parent.to_s.should == '/usr'
+ end
+
+end
diff --git a/spec/ruby/library/pathname/realdirpath_spec.rb b/spec/ruby/library/pathname/realdirpath_spec.rb
new file mode 100644
index 0000000000..a9e44e354e
--- /dev/null
+++ b/spec/ruby/library/pathname/realdirpath_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require 'pathname'
+
+describe "Pathname#realdirpath" do
+
+ it "returns a Pathname" do
+ Pathname.pwd.realdirpath.should be_an_instance_of(Pathname)
+ end
+
+end
diff --git a/spec/ruby/library/pathname/realpath_spec.rb b/spec/ruby/library/pathname/realpath_spec.rb
new file mode 100644
index 0000000000..f2c654308e
--- /dev/null
+++ b/spec/ruby/library/pathname/realpath_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require 'pathname'
+
+describe "Pathname#realpath" do
+
+ it "returns a Pathname" do
+ Pathname.pwd.realpath.should be_an_instance_of(Pathname)
+ end
+
+end
diff --git a/spec/ruby/library/pathname/relative_path_from_spec.rb b/spec/ruby/library/pathname/relative_path_from_spec.rb
new file mode 100644
index 0000000000..1a941be926
--- /dev/null
+++ b/spec/ruby/library/pathname/relative_path_from_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require 'pathname'
+
+describe "Pathname#relative_path_from" do
+ def relative_path_str(dest, base)
+ Pathname.new(dest).relative_path_from(Pathname.new(base)).to_s
+ end
+
+ it "raises an error when the two paths do not share a common prefix" do
+ lambda { relative_path_str('/usr', 'foo') }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error when the base directory has .." do
+ lambda { relative_path_str('a', '..') }.should raise_error(ArgumentError)
+ end
+
+ it "returns a path relative from root" do
+ relative_path_str('/usr', '/').should == 'usr'
+ end
+
+ it 'returns 1 level up when both paths are relative' do
+ relative_path_str('a', 'b').should == '../a'
+ relative_path_str('a', 'b/').should == '../a'
+ end
+
+ it 'returns a relative path when both are absolute' do
+ relative_path_str('/a', '/b').should == '../a'
+ end
+
+ it "returns a path relative to the current directory" do
+ relative_path_str('/usr/bin/ls', '/usr').should == 'bin/ls'
+ end
+
+ it 'returns a . when base and dest are the same' do
+ relative_path_str('/usr', '/usr').should == '.'
+ end
+
+ it 'returns the same directory with a non clean base that matches the current dir' do
+ relative_path_str('/usr', '/stuff/..').should == 'usr'
+ end
+
+ it 'returns a relative path with a non clean base that matches a different dir' do
+ relative_path_str('/usr', '/stuff/../foo').should == '../usr'
+ end
+
+ it 'returns current and pattern when only those patterns are used' do
+ relative_path_str('.', '.').should == '.'
+ relative_path_str('..', '..').should == '.'
+ relative_path_str('..', '.').should == '..'
+ end
+end
diff --git a/spec/ruby/library/pathname/relative_spec.rb b/spec/ruby/library/pathname/relative_spec.rb
new file mode 100644
index 0000000000..1a08891e6c
--- /dev/null
+++ b/spec/ruby/library/pathname/relative_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+require 'pathname'
+
+describe "Pathname#relative?" do
+
+ it "returns false for the root directory" do
+ Pathname.new('/').relative?.should == false
+ end
+
+ it "returns false for a dir starting with a slash" do
+ Pathname.new('/usr/local/bin').relative?.should == false
+ end
+
+ it "returns true for a dir not starting with a slash" do
+ Pathname.new('fish').relative?.should == true
+ end
+
+ it "returns true for a dir not starting with a slash" do
+ Pathname.new('fish/dog/cow').relative?.should == true
+ end
+
+end
diff --git a/spec/ruby/library/pathname/root_spec.rb b/spec/ruby/library/pathname/root_spec.rb
new file mode 100644
index 0000000000..5fec0ee956
--- /dev/null
+++ b/spec/ruby/library/pathname/root_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require 'pathname'
+
+describe "Pathname#root?" do
+
+ it "returns true for root directories" do
+ Pathname.new('/').root?.should == true
+ end
+
+ it "returns false for empty string" do
+ Pathname.new('').root?.should == false
+ end
+
+ it "returns false for a top level directory" do
+ Pathname.new('/usr').root?.should == false
+ end
+
+ it "returns false for a top level with .. appended directory" do
+ Pathname.new('/usr/..').root?.should == false
+ end
+
+ it "returns false for a directory below top level" do
+ Pathname.new('/usr/local/bin/').root?.should == false
+ end
+
+end
diff --git a/spec/ruby/library/pathname/sub_spec.rb b/spec/ruby/library/pathname/sub_spec.rb
new file mode 100644
index 0000000000..ad2900f62b
--- /dev/null
+++ b/spec/ruby/library/pathname/sub_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require 'pathname'
+
+describe "Pathname#sub" do
+
+ it "replaces the pattern with rest" do
+ Pathname.new('/usr/local/bin/').sub(/local/, 'fish').to_s.should == '/usr/fish/bin/'
+ end
+
+ it "returns a new object" do
+ p = Pathname.new('/usr/local/bin/')
+ p.sub(/local/, 'fish').should_not == p
+ end
+
+end
diff --git a/spec/ruby/library/pp/pp_spec.rb b/spec/ruby/library/pp/pp_spec.rb
new file mode 100644
index 0000000000..dba20d190d
--- /dev/null
+++ b/spec/ruby/library/pp/pp_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require 'pp'
+
+describe "PP.pp" do
+ it 'works with default arguments' do
+ array = [1, 2, 3]
+
+ lambda {
+ PP.pp array
+ }.should output "[1, 2, 3]\n"
+ end
+
+ it 'allows specifying out explicitly' do
+ array = [1, 2, 3]
+ other_out = IOStub.new
+
+ lambda {
+ PP.pp array, other_out
+ }.should output "" # no output on stdout
+
+ other_out.to_s.should == "[1, 2, 3]\n"
+ end
+
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/prime/each_spec.rb b/spec/ruby/library/prime/each_spec.rb
new file mode 100644
index 0000000000..b99cf7cf0e
--- /dev/null
+++ b/spec/ruby/library/prime/each_spec.rb
@@ -0,0 +1,167 @@
+require_relative '../../spec_helper'
+require 'prime'
+
+describe :prime_each, shared: true do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "enumerates primes" do
+ primes = Prime.instance
+ result = []
+
+ primes.each { |p|
+ result << p
+ break if p > 10
+ }
+
+ result.should == [2, 3, 5, 7, 11]
+ end
+
+ it "yields ascending primes to the block" do
+ previous = 1
+ @object.each do |prime|
+ break if prime > 1000
+ ScratchPad << prime
+ prime.should > previous
+ previous = prime
+ end
+
+ all_prime = true
+ ScratchPad.recorded.all? do |prime|
+ all_prime &&= (2..Math.sqrt(prime)).all? { |d| prime % d != 0 }
+ end
+
+ all_prime.should be_true
+ end
+
+ it "returns the last evaluated expression in the passed block" do
+ @object.each { break :value }.should equal(:value)
+ end
+
+ describe "when not passed a block" do
+ before :each do
+ @prime_enum = @object.each
+ end
+
+ it "returns an object that is Enumerable" do
+ @prime_enum.each.should be_kind_of(Enumerable)
+ end
+
+ it "returns an object that responds to #with_index" do
+ @prime_enum.should respond_to(:with_index)
+ end
+
+ it "returns an object that responds to #with_object" do
+ @prime_enum.should respond_to(:with_object)
+ end
+
+ it "returns an object that responds to #next" do
+ @prime_enum.should respond_to(:next)
+ end
+
+ it "returns an object that responds to #rewind" do
+ @prime_enum.should respond_to(:rewind)
+ end
+
+ it "yields primes starting at 2 independent of prior enumerators" do
+ @prime_enum.next.should == 2
+ @prime_enum.next.should == 3
+
+ @object.each { |prime| break prime }.should == 2
+ end
+
+ it "returns an enumerator that yields previous primes when #rewind is called" do
+ @prime_enum.next.should == 2
+ @prime_enum.next.should == 3
+ @prime_enum.rewind
+ @prime_enum.next.should == 2
+ end
+
+ it "returns independent enumerators" do
+ enum = @object.each
+ enum.next.should == 2
+ enum.next.should == 3
+
+ @prime_enum.next.should == 2
+
+ enum.next.should == 5
+ end
+ end
+end
+
+describe :prime_each_with_arguments, shared: true do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "yields ascending primes less than or equal to the argument" do
+ bound = 1000
+ previous = 1
+ @object.each(bound) do |prime|
+ ScratchPad << prime
+ prime.should > previous
+ previous = prime
+ end
+
+ ScratchPad.recorded.all? do |prime|
+ (2..Math.sqrt(prime)).all? { |d| prime % d != 0 }
+ end.should be_true
+
+ ScratchPad.recorded.all? { |prime| prime <= bound }.should be_true
+ end
+
+ it "returns nil when no prime is generated" do
+ @object.each(1) { :value }.should be_nil
+ end
+
+ it "yields primes starting at 2 independent of prior enumeration" do
+ @object.each(10) { |prime| prime }.should == 7
+ @object.each(10) { |prime| break prime }.should == 2
+ end
+
+ it "accepts a pseudo-prime generator as the second argument" do
+ generator = mock('very bad pseudo-prime generator')
+ generator.should_receive(:upper_bound=).with(100)
+ generator.should_receive(:each).and_yield(2).and_yield(3).and_yield(4)
+
+ @object.each(100, generator) { |prime| ScratchPad << prime }
+ ScratchPad.recorded.should == [2, 3, 4]
+ end
+
+ describe "when not passed a block" do
+ it "returns an object that returns primes less than or equal to the bound" do
+ bound = 100
+ @object.each(bound).all? { |prime| prime <= bound }.should be_true
+ end
+ end
+end
+
+describe "Prime.each" do
+ it_behaves_like :prime_each, :each, Prime
+end
+
+describe "Prime.each" do
+ it_behaves_like :prime_each_with_arguments, :each, Prime
+end
+
+describe "Prime#each with Prime.instance" do
+ it_behaves_like :prime_each, :each, Prime.instance
+end
+
+describe "Prime#each with Prime.instance" do
+ it_behaves_like :prime_each_with_arguments, :each, Prime.instance
+end
+
+describe "Prime#each with Prime.instance" do
+ before :each do
+ @object = Prime.instance
+ end
+
+ it_behaves_like :prime_each, :each
+
+ it "resets the enumerator with each call" do
+ @object.each { |prime| break if prime > 10 }
+ @object.each { |prime| break prime }.should == 2
+ end
+end
diff --git a/spec/ruby/library/prime/instance_spec.rb b/spec/ruby/library/prime/instance_spec.rb
new file mode 100644
index 0000000000..156337c46a
--- /dev/null
+++ b/spec/ruby/library/prime/instance_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require 'prime'
+
+describe "Prime.instance" do
+ it "returns a object representing the set of prime numbers" do
+ Prime.instance.should be_kind_of(Prime)
+ end
+
+ it "returns a object with no obsolete features" do
+ Prime.instance.should_not respond_to(:succ)
+ Prime.instance.should_not respond_to(:next)
+ end
+
+ it "does not complain anything" do
+ lambda { Prime.instance }.should_not complain
+ end
+
+ it "raises a ArgumentError when is called with some arguments" do
+ lambda { Prime.instance(1) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/prime/int_from_prime_division_spec.rb b/spec/ruby/library/prime/int_from_prime_division_spec.rb
new file mode 100644
index 0000000000..5abb7221df
--- /dev/null
+++ b/spec/ruby/library/prime/int_from_prime_division_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require 'prime'
+
+describe "Prime.int_from_prime_division" do
+ it "returns the product of the given factorization" do
+ Prime.int_from_prime_division([[2,3], [3,3], [5,3], [7,3], [11,3], [13,3], [17,3]]).
+ should == 2**3 * 3**3 * 5**3 * 7**3 * 11**3 * 13**3 * 17**3
+ end
+
+ it "returns 1 for an empty factorization" do
+ Prime.int_from_prime_division([]).should == 1
+ end
+end
diff --git a/spec/ruby/library/prime/integer/each_prime_spec.rb b/spec/ruby/library/prime/integer/each_prime_spec.rb
new file mode 100644
index 0000000000..a71296b0df
--- /dev/null
+++ b/spec/ruby/library/prime/integer/each_prime_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+require 'prime'
+
+describe "Integer.each_prime" do
+ it "is transferred to Prime.each" do
+ Prime.should_receive(:each).with(100).and_yield(2).and_yield(3).and_yield(5)
+ yielded = []
+ Integer.each_prime(100) do |prime|
+ yielded << prime
+ end
+ yielded.should == [2,3,5]
+ end
+end
diff --git a/spec/ruby/library/prime/integer/from_prime_division_spec.rb b/spec/ruby/library/prime/integer/from_prime_division_spec.rb
new file mode 100644
index 0000000000..e0e74fb336
--- /dev/null
+++ b/spec/ruby/library/prime/integer/from_prime_division_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+require 'prime'
+
+describe "Integer.from_prime_division" do
+ it "returns the product of the given factorization" do
+ Integer.from_prime_division([[2,3], [3,3], [5,3], [7,3], [11,3], [13,3], [17,3]]).
+ should == 2**3 * 3**3 * 5**3 * 7**3 * 11**3 * 13**3 * 17**3
+ end
+
+ it "returns 1 for an empty factorization" do
+ Integer.from_prime_division([]).should == 1
+ end
+end
diff --git a/spec/ruby/library/prime/integer/prime_division_spec.rb b/spec/ruby/library/prime/integer/prime_division_spec.rb
new file mode 100644
index 0000000000..60ba21037f
--- /dev/null
+++ b/spec/ruby/library/prime/integer/prime_division_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../../spec_helper'
+require 'prime'
+
+describe "Integer#prime_division" do
+ it "returns an array of a prime factor and a corresponding exponent" do
+ (2*3*5*7*11*13*17).prime_division.should ==
+ [[2,1], [3,1], [5,1], [7,1], [11,1], [13,1], [17,1]]
+ end
+
+ it "returns an empty array for 1" do
+ 1.prime_division.should == []
+ end
+ it "returns an empty array for -1" do
+ -1.prime_division.should == [[-1, 1]]
+ end
+ it "raises ZeroDivisionError for 0" do
+ lambda { 0.prime_division }.should raise_error(ZeroDivisionError)
+ end
+end
diff --git a/spec/ruby/library/prime/integer/prime_spec.rb b/spec/ruby/library/prime/integer/prime_spec.rb
new file mode 100644
index 0000000000..53de76d5ab
--- /dev/null
+++ b/spec/ruby/library/prime/integer/prime_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+require 'prime'
+
+describe "Integer#prime?" do
+ it "returns a true value for prime numbers" do
+ 2.prime?.should be_true
+ 3.prime?.should be_true
+ (2**31-1).prime?.should be_true # 8th Mersenne prime (M8)
+ end
+
+ it "returns a false value for composite numbers" do
+ 4.prime?.should be_false
+ 15.prime?.should be_false
+ (2**32-1).prime?.should be_false
+ ( (2**17-1)*(2**19-1) ).prime?.should be_false # M6*M7
+ end
+end
diff --git a/spec/ruby/library/prime/next_spec.rb b/spec/ruby/library/prime/next_spec.rb
new file mode 100644
index 0000000000..39c4ae16ae
--- /dev/null
+++ b/spec/ruby/library/prime/next_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/next'
+require 'prime'
+
+describe "Prime#next" do
+ it_behaves_like :prime_next, :next
+end
diff --git a/spec/ruby/library/prime/prime_division_spec.rb b/spec/ruby/library/prime/prime_division_spec.rb
new file mode 100644
index 0000000000..151b046f44
--- /dev/null
+++ b/spec/ruby/library/prime/prime_division_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require 'prime'
+
+describe "Prime.prime_division" do
+ it "returns an array of a prime factor and a corresponding exponent" do
+ Prime.prime_division(2*3*5*7*11*13*17).should ==
+ [[2,1], [3,1], [5,1], [7,1], [11,1], [13,1], [17,1]]
+ end
+
+ it "returns an empty array for 1" do
+ Prime.prime_division(1).should == []
+ end
+
+ it "returns [[-1, 1]] for -1" do
+ Prime.prime_division(-1).should == [[-1, 1]]
+ end
+
+ it "includes [[-1, 1]] in the divisors of a negative number" do
+ Prime.prime_division(-10).should include([-1, 1])
+ end
+
+ it "raises ZeroDivisionError for 0" do
+ lambda { Prime.prime_division(0) }.should raise_error(ZeroDivisionError)
+ end
+end
diff --git a/spec/ruby/library/prime/prime_spec.rb b/spec/ruby/library/prime/prime_spec.rb
new file mode 100644
index 0000000000..0896c7f0f3
--- /dev/null
+++ b/spec/ruby/library/prime/prime_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require 'prime'
+
+describe "Prime#prime?" do
+ it "returns a true value for prime numbers" do
+ Prime.prime?(2).should be_true
+ Prime.prime?(3).should be_true
+ Prime.prime?(2**31-1).should be_true # 8th Mersenne prime (M8)
+ end
+
+ it "returns a false value for composite numbers" do
+ Prime.prime?(4).should be_false
+ Prime.prime?(15).should be_false
+ Prime.prime?(2**32-1).should be_false
+ Prime.prime?( (2**17-1)*(2**19-1) ).should be_false # M6*M7
+ end
+end
diff --git a/spec/ruby/library/prime/shared/next.rb b/spec/ruby/library/prime/shared/next.rb
new file mode 100644
index 0000000000..f79b2c051e
--- /dev/null
+++ b/spec/ruby/library/prime/shared/next.rb
@@ -0,0 +1,8 @@
+describe :prime_next, shared: true do
+ it "returns the element at the current position and moves forward" do
+ p = Prime.instance.each
+ p.next.should == 2
+ p.next.should == 3
+ p.next.next.should == 6
+ end
+end
diff --git a/spec/ruby/library/prime/succ_spec.rb b/spec/ruby/library/prime/succ_spec.rb
new file mode 100644
index 0000000000..34c18d2ba0
--- /dev/null
+++ b/spec/ruby/library/prime/succ_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/next'
+require 'prime'
+
+describe "Prime#succ" do
+ it_behaves_like :prime_next, :succ
+end
diff --git a/spec/ruby/library/rbconfig/rbconfig_spec.rb b/spec/ruby/library/rbconfig/rbconfig_spec.rb
new file mode 100644
index 0000000000..caa557c32c
--- /dev/null
+++ b/spec/ruby/library/rbconfig/rbconfig_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require 'rbconfig'
+
+describe 'RbConfig::CONFIG values' do
+ it 'are all strings' do
+ RbConfig::CONFIG.each do |k, v|
+ k.should be_kind_of String
+ v.should be_kind_of String
+ end
+ end
+end
diff --git a/spec/ruby/library/rbconfig/sizeof/sizeof_spec.rb b/spec/ruby/library/rbconfig/sizeof/sizeof_spec.rb
new file mode 100644
index 0000000000..f2582dc4fd
--- /dev/null
+++ b/spec/ruby/library/rbconfig/sizeof/sizeof_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../../spec_helper'
+require 'rbconfig/sizeof'
+
+describe "RbConfig::SIZEOF" do
+ it "is a Hash" do
+ RbConfig::SIZEOF.should be_kind_of(Hash)
+ end
+
+ it "has string keys and integer values" do
+ RbConfig::SIZEOF.each do |key, value|
+ key.should be_kind_of String
+ value.should be_kind_of Integer
+ end
+ end
+
+ it "contains the sizeof(void*)" do
+ (RbConfig::SIZEOF["void*"] * 8).should == PlatformGuard::POINTER_SIZE
+ end
+
+ it "contains the sizeof(float) and sizeof(double)" do
+ RbConfig::SIZEOF["float"].should == 4
+ RbConfig::SIZEOF["double"].should == 8
+ end
+
+ it "contains the size of short, int and long" do
+ RbConfig::SIZEOF["short"].should > 0
+ RbConfig::SIZEOF["int"].should > 0
+ RbConfig::SIZEOF["long"].should > 0
+ end
+end
diff --git a/spec/ruby/library/readline/basic_quote_characters_spec.rb b/spec/ruby/library/readline/basic_quote_characters_spec.rb
new file mode 100644
index 0000000000..216899d875
--- /dev/null
+++ b/spec/ruby/library/readline/basic_quote_characters_spec.rb
@@ -0,0 +1,18 @@
+require_relative 'spec_helper'
+
+platform_is_not :darwin do
+ with_feature :readline do
+ describe "Readline.basic_quote_characters" do
+ it "returns not nil" do
+ Readline.basic_quote_characters.should_not be_nil
+ end
+ end
+
+ describe "Readline.basic_quote_characters=" do
+ it "returns the passed string" do
+ Readline.basic_quote_characters = "test"
+ Readline.basic_quote_characters.should == "test"
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/basic_word_break_characters_spec.rb b/spec/ruby/library/readline/basic_word_break_characters_spec.rb
new file mode 100644
index 0000000000..daa0e1cb76
--- /dev/null
+++ b/spec/ruby/library/readline/basic_word_break_characters_spec.rb
@@ -0,0 +1,16 @@
+require_relative 'spec_helper'
+
+with_feature :readline do
+ describe "Readline.basic_word_break_characters" do
+ it "returns not nil" do
+ Readline.basic_word_break_characters.should_not be_nil
+ end
+ end
+
+ describe "Readline.basic_word_break_characters=" do
+ it "returns the passed string" do
+ Readline.basic_word_break_characters = "test"
+ Readline.basic_word_break_characters.should == "test"
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/completer_quote_characters_spec.rb b/spec/ruby/library/readline/completer_quote_characters_spec.rb
new file mode 100644
index 0000000000..86c58f3cf6
--- /dev/null
+++ b/spec/ruby/library/readline/completer_quote_characters_spec.rb
@@ -0,0 +1,16 @@
+require_relative 'spec_helper'
+
+with_feature :readline do
+ describe "Readline.completer_quote_characters" do
+ it "returns nil" do
+ Readline.completer_quote_characters.should be_nil
+ end
+ end
+
+ describe "Readline.completer_quote_characters=" do
+ it "returns the passed string" do
+ Readline.completer_quote_characters = "test"
+ Readline.completer_quote_characters.should == "test"
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/completer_word_break_characters_spec.rb b/spec/ruby/library/readline/completer_word_break_characters_spec.rb
new file mode 100644
index 0000000000..c72f1135c4
--- /dev/null
+++ b/spec/ruby/library/readline/completer_word_break_characters_spec.rb
@@ -0,0 +1,16 @@
+require_relative 'spec_helper'
+
+with_feature :readline do
+ describe "Readline.completer_word_break_characters" do
+ it "returns nil" do
+ Readline.completer_word_break_characters.should be_nil
+ end
+ end
+
+ describe "Readline.completer_word_break_characters=" do
+ it "returns the passed string" do
+ Readline.completer_word_break_characters = "test"
+ Readline.completer_word_break_characters.should == "test"
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/completion_append_character_spec.rb b/spec/ruby/library/readline/completion_append_character_spec.rb
new file mode 100644
index 0000000000..615b523f4e
--- /dev/null
+++ b/spec/ruby/library/readline/completion_append_character_spec.rb
@@ -0,0 +1,16 @@
+require_relative 'spec_helper'
+
+with_feature :readline do
+ describe "Readline.completion_append_character" do
+ it "returns not nil" do
+ Readline.completion_append_character.should_not be_nil
+ end
+ end
+
+ describe "Readline.completion_append_character=" do
+ it "returns the first character of the passed string" do
+ Readline.completion_append_character = "test"
+ Readline.completion_append_character.should == "t"
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/completion_case_fold_spec.rb b/spec/ruby/library/readline/completion_case_fold_spec.rb
new file mode 100644
index 0000000000..966f5d6c79
--- /dev/null
+++ b/spec/ruby/library/readline/completion_case_fold_spec.rb
@@ -0,0 +1,18 @@
+require_relative 'spec_helper'
+
+with_feature :readline do
+ describe "Readline.completion_case_fold" do
+ it "returns nil" do
+ Readline.completion_case_fold.should be_nil
+ end
+ end
+
+ describe "Readline.completion_case_fold=" do
+ it "returns the passed boolean" do
+ Readline.completion_case_fold = true
+ Readline.completion_case_fold.should == true
+ Readline.completion_case_fold = false
+ Readline.completion_case_fold.should == false
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/completion_proc_spec.rb b/spec/ruby/library/readline/completion_proc_spec.rb
new file mode 100644
index 0000000000..721acaa1d0
--- /dev/null
+++ b/spec/ruby/library/readline/completion_proc_spec.rb
@@ -0,0 +1,22 @@
+require_relative 'spec_helper'
+
+with_feature :readline do
+ describe "Readline.completion_proc" do
+ it "returns nil" do
+ Readline.completion_proc.should be_nil
+ end
+ end
+
+ describe "Readline.completion_proc=" do
+ it "returns the passed Proc" do
+ proc = Proc.new do |e|
+ end
+ Readline.completion_proc = proc
+ Readline.completion_proc.should == proc
+ end
+
+ it "returns an ArgumentError if not given an Proc or #call" do
+ lambda { Readline.completion_proc = "test" }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/constants_spec.rb b/spec/ruby/library/readline/constants_spec.rb
new file mode 100644
index 0000000000..8fee274866
--- /dev/null
+++ b/spec/ruby/library/readline/constants_spec.rb
@@ -0,0 +1,18 @@
+require_relative 'spec_helper'
+
+with_feature :readline do
+ # Note: additional specs for HISTORY are in 'history' subdir.
+ describe "Readline::HISTORY" do
+ it "is defined" do
+ Readline.const_defined?(:HISTORY).should == true
+ end
+ end
+
+ describe "Readline::VERSION" do
+ it "is defined and is a non-empty String" do
+ Readline.const_defined?(:VERSION).should == true
+ Readline::VERSION.should be_kind_of(String)
+ Readline::VERSION.should_not be_empty
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/emacs_editing_mode_spec.rb b/spec/ruby/library/readline/emacs_editing_mode_spec.rb
new file mode 100644
index 0000000000..f7e8eda982
--- /dev/null
+++ b/spec/ruby/library/readline/emacs_editing_mode_spec.rb
@@ -0,0 +1,11 @@
+require_relative 'spec_helper'
+
+platform_is_not :darwin do
+ with_feature :readline do
+ describe "Readline.emacs_editing_mode" do
+ it "returns nil" do
+ Readline.emacs_editing_mode.should be_nil
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/filename_quote_characters_spec.rb b/spec/ruby/library/readline/filename_quote_characters_spec.rb
new file mode 100644
index 0000000000..de8ce700a8
--- /dev/null
+++ b/spec/ruby/library/readline/filename_quote_characters_spec.rb
@@ -0,0 +1,18 @@
+require_relative 'spec_helper'
+
+platform_is_not :darwin do
+ with_feature :readline do
+ describe "Readline.filename_quote_characters" do
+ it "returns nil" do
+ Readline.filename_quote_characters.should be_nil
+ end
+ end
+
+ describe "Readline.filename_quote_characters=" do
+ it "returns the passed string" do
+ Readline.filename_quote_characters = "test"
+ Readline.filename_quote_characters.should == "test"
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/history/append_spec.rb b/spec/ruby/library/readline/history/append_spec.rb
new file mode 100644
index 0000000000..ee26b01255
--- /dev/null
+++ b/spec/ruby/library/readline/history/append_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../spec_helper'
+
+with_feature :readline do
+ describe "Readline::HISTORY.<<" do
+ it "appends the given Object to the history" do
+ Readline::HISTORY << "1"
+ Readline::HISTORY.size.should == 1
+
+ Readline::HISTORY << "2"
+ Readline::HISTORY.size.should == 2
+
+ Readline::HISTORY.pop.should == "2"
+ Readline::HISTORY.pop.should == "1"
+ end
+
+ it "tries to convert the passed Object to a String using #to_str" do
+ obj = mock("Converted to String")
+ obj.should_receive(:to_str).and_return("converted")
+
+ Readline::HISTORY << obj
+ Readline::HISTORY.pop.should == "converted"
+ end
+
+ it "raises a TypeError when the passed Object can't be converted to a String" do
+ lambda { Readline::HISTORY << mock("Object") }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/history/delete_at_spec.rb b/spec/ruby/library/readline/history/delete_at_spec.rb
new file mode 100644
index 0000000000..407006c20c
--- /dev/null
+++ b/spec/ruby/library/readline/history/delete_at_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../spec_helper'
+
+with_feature :readline do
+ describe "Readline::HISTORY.delete_at" do
+ it "deletes and returns the history entry at the specified index" do
+ Readline::HISTORY.push("1", "2", "3")
+
+ Readline::HISTORY.delete_at(1).should == "2"
+ Readline::HISTORY.size.should == 2
+
+ Readline::HISTORY.delete_at(1).should == "3"
+ Readline::HISTORY.size.should == 1
+
+ Readline::HISTORY.delete_at(0).should == "1"
+ Readline::HISTORY.size.should == 0
+
+
+ Readline::HISTORY.push("1", "2", "3", "4")
+
+ Readline::HISTORY.delete_at(-2).should == "3"
+ Readline::HISTORY.size.should == 3
+
+ Readline::HISTORY.delete_at(-2).should == "2"
+ Readline::HISTORY.size.should == 2
+
+ Readline::HISTORY.delete_at(0).should == "1"
+ Readline::HISTORY.size.should == 1
+
+ Readline::HISTORY.delete_at(0).should == "4"
+ Readline::HISTORY.size.should == 0
+ end
+
+ it "raises an IndexError when the given index is greater than the history size" do
+ lambda { Readline::HISTORY.delete_at(10) }.should raise_error(IndexError)
+ lambda { Readline::HISTORY.delete_at(-10) }.should raise_error(IndexError)
+ end
+
+ it "taints the returned strings" do
+ Readline::HISTORY.push("1", "2", "3")
+ Readline::HISTORY.delete_at(0).tainted?.should be_true
+ Readline::HISTORY.delete_at(0).tainted?.should be_true
+ Readline::HISTORY.delete_at(0).tainted?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/history/each_spec.rb b/spec/ruby/library/readline/history/each_spec.rb
new file mode 100644
index 0000000000..4b87df7640
--- /dev/null
+++ b/spec/ruby/library/readline/history/each_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../spec_helper'
+
+with_feature :readline do
+ describe "Readline::HISTORY.each" do
+ before :each do
+ Readline::HISTORY.push("1", "2", "3")
+ end
+
+ after :each do
+ Readline::HISTORY.pop
+ Readline::HISTORY.pop
+ Readline::HISTORY.pop
+ end
+
+ it "yields each item in the history" do
+ result = []
+ Readline::HISTORY.each do |x|
+ result << x
+ end
+ result.should == ["1", "2", "3"]
+ end
+
+ it "yields tainted Objects" do
+ Readline::HISTORY.each do |x|
+ x.tainted?.should be_true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/history/element_reference_spec.rb b/spec/ruby/library/readline/history/element_reference_spec.rb
new file mode 100644
index 0000000000..a85d069111
--- /dev/null
+++ b/spec/ruby/library/readline/history/element_reference_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../spec_helper'
+
+with_feature :readline do
+ describe "Readline::HISTORY.[]" do
+ before :each do
+ Readline::HISTORY.push("1", "2", "3")
+ end
+
+ after :each do
+ Readline::HISTORY.pop
+ Readline::HISTORY.pop
+ Readline::HISTORY.pop
+ end
+
+ it "returns tainted objects" do
+ Readline::HISTORY[0].tainted?.should be_true
+ Readline::HISTORY[1].tainted?.should be_true
+ end
+
+ it "returns the history item at the passed index" do
+ Readline::HISTORY[0].should == "1"
+ Readline::HISTORY[1].should == "2"
+ Readline::HISTORY[2].should == "3"
+
+ Readline::HISTORY[-1].should == "3"
+ Readline::HISTORY[-2].should == "2"
+ Readline::HISTORY[-3].should == "1"
+ end
+
+ it "raises an IndexError when there is no item at the passed index" do
+ lambda { Readline::HISTORY[-10] }.should raise_error(IndexError)
+ lambda { Readline::HISTORY[-9] }.should raise_error(IndexError)
+ lambda { Readline::HISTORY[-8] }.should raise_error(IndexError)
+
+ lambda { Readline::HISTORY[8] }.should raise_error(IndexError)
+ lambda { Readline::HISTORY[9] }.should raise_error(IndexError)
+ lambda { Readline::HISTORY[10] }.should raise_error(IndexError)
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/history/element_set_spec.rb b/spec/ruby/library/readline/history/element_set_spec.rb
new file mode 100644
index 0000000000..67ecf0ef2b
--- /dev/null
+++ b/spec/ruby/library/readline/history/element_set_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+
+with_feature :readline do
+ describe "Readline::HISTORY.[]=" do
+ before :each do
+ Readline::HISTORY.push("1", "2", "3")
+ end
+
+ after :each do
+ Readline::HISTORY.pop
+ Readline::HISTORY.pop
+ Readline::HISTORY.pop
+ end
+
+ it "returns the new value for the passed index" do
+ (Readline::HISTORY[1] = "second test").should == "second test"
+ end
+
+ it "raises an IndexError when there is no item at the passed positive index" do
+ lambda { Readline::HISTORY[10] = "test" }.should raise_error(IndexError)
+ end
+
+ it "sets the item at the given index" do
+ Readline::HISTORY[0] = "test"
+ Readline::HISTORY[0].should == "test"
+
+ Readline::HISTORY[1] = "second test"
+ Readline::HISTORY[1].should == "second test"
+ end
+
+ it "raises an IndexError when there is no item at the passed negative index" do
+ lambda { Readline::HISTORY[10] = "test" }.should raise_error(IndexError)
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/history/empty_spec.rb b/spec/ruby/library/readline/history/empty_spec.rb
new file mode 100644
index 0000000000..31d01d9601
--- /dev/null
+++ b/spec/ruby/library/readline/history/empty_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../spec_helper'
+
+with_feature :readline do
+ describe "Readline::HISTORY.empty?" do
+ it "returns true when the history is empty" do
+ Readline::HISTORY.should be_empty
+ Readline::HISTORY.push("test")
+ Readline::HISTORY.should_not be_empty
+ Readline::HISTORY.pop
+ Readline::HISTORY.should be_empty
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/history/history_spec.rb b/spec/ruby/library/readline/history/history_spec.rb
new file mode 100644
index 0000000000..927dd52ebf
--- /dev/null
+++ b/spec/ruby/library/readline/history/history_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :readline do
+ describe "Readline::HISTORY" do
+ it "is extended with the Enumerable module" do
+ Readline::HISTORY.should be_kind_of(Enumerable)
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/history/length_spec.rb b/spec/ruby/library/readline/history/length_spec.rb
new file mode 100644
index 0000000000..9427d10a00
--- /dev/null
+++ b/spec/ruby/library/readline/history/length_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :readline do
+ require_relative 'shared/size'
+
+ describe "Readline::HISTORY.length" do
+ it_behaves_like :readline_history_size, :length
+ end
+end
diff --git a/spec/ruby/library/readline/history/pop_spec.rb b/spec/ruby/library/readline/history/pop_spec.rb
new file mode 100644
index 0000000000..3a4c3579d0
--- /dev/null
+++ b/spec/ruby/library/readline/history/pop_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../spec_helper'
+
+with_feature :readline do
+ describe "Readline::HISTORY.pop" do
+ it "returns nil when the history is empty" do
+ Readline::HISTORY.pop.should be_nil
+ end
+
+ it "returns and removes the last item from the history" do
+ Readline::HISTORY.push("1", "2", "3")
+ Readline::HISTORY.size.should == 3
+
+ Readline::HISTORY.pop.should == "3"
+ Readline::HISTORY.size.should == 2
+
+ Readline::HISTORY.pop.should == "2"
+ Readline::HISTORY.size.should == 1
+
+ Readline::HISTORY.pop.should == "1"
+ Readline::HISTORY.size.should == 0
+ end
+
+ it "taints the returned strings" do
+ Readline::HISTORY.push("1", "2", "3")
+ Readline::HISTORY.pop.tainted?.should be_true
+ Readline::HISTORY.pop.tainted?.should be_true
+ Readline::HISTORY.pop.tainted?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/history/push_spec.rb b/spec/ruby/library/readline/history/push_spec.rb
new file mode 100644
index 0000000000..f91121f351
--- /dev/null
+++ b/spec/ruby/library/readline/history/push_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../spec_helper'
+
+with_feature :readline do
+ describe "Readline::HISTORY.push" do
+ it "pushes all passed Objects into the history" do
+ Readline::HISTORY.push("1", "2", "3")
+ Readline::HISTORY.size.should == 3
+
+ Readline::HISTORY.pop.should == "3"
+ Readline::HISTORY.pop.should == "2"
+ Readline::HISTORY.pop.should == "1"
+ end
+
+ it "tries to convert the passed Object to a String using #to_str" do
+ obj = mock("Converted to String")
+ obj.should_receive(:to_str).and_return("converted")
+
+ Readline::HISTORY.push(obj)
+ Readline::HISTORY.pop.should == "converted"
+ end
+
+ it "raises a TypeError when the passed Object can't be converted to a String" do
+ lambda { Readline::HISTORY.push(mock("Object")) }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/history/shared/size.rb b/spec/ruby/library/readline/history/shared/size.rb
new file mode 100644
index 0000000000..1d6df86f78
--- /dev/null
+++ b/spec/ruby/library/readline/history/shared/size.rb
@@ -0,0 +1,14 @@
+describe :readline_history_size, shared: true do
+ it "returns the size of the history" do
+ Readline::HISTORY.send(@method).should == 0
+ Readline::HISTORY.push("1", "2", "")
+ Readline::HISTORY.send(@method).should == 3
+
+ Readline::HISTORY.pop
+ Readline::HISTORY.send(@method).should == 2
+
+ Readline::HISTORY.pop
+ Readline::HISTORY.pop
+ Readline::HISTORY.send(@method).should == 0
+ end
+end
diff --git a/spec/ruby/library/readline/history/shift_spec.rb b/spec/ruby/library/readline/history/shift_spec.rb
new file mode 100644
index 0000000000..fdc637fc35
--- /dev/null
+++ b/spec/ruby/library/readline/history/shift_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../spec_helper'
+
+with_feature :readline do
+ describe "Readline::HISTORY.shift" do
+ it "returns nil when the history is empty" do
+ Readline::HISTORY.shift.should be_nil
+ end
+
+ it "returns and removes the first item from the history" do
+ Readline::HISTORY.push("1", "2", "3")
+ Readline::HISTORY.size.should == 3
+
+ Readline::HISTORY.shift.should == "1"
+ Readline::HISTORY.size.should == 2
+
+ Readline::HISTORY.shift.should == "2"
+ Readline::HISTORY.size.should == 1
+
+ Readline::HISTORY.shift.should == "3"
+ Readline::HISTORY.size.should == 0
+ end
+
+ it "taints the returned strings" do
+ Readline::HISTORY.push("1", "2", "3")
+ Readline::HISTORY.shift.tainted?.should be_true
+ Readline::HISTORY.shift.tainted?.should be_true
+ Readline::HISTORY.shift.tainted?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/history/size_spec.rb b/spec/ruby/library/readline/history/size_spec.rb
new file mode 100644
index 0000000000..c55253ccea
--- /dev/null
+++ b/spec/ruby/library/readline/history/size_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :readline do
+ require_relative 'shared/size'
+
+ describe "Readline::HISTORY.size" do
+ it_behaves_like :readline_history_size, :size
+ end
+end
diff --git a/spec/ruby/library/readline/history/to_s_spec.rb b/spec/ruby/library/readline/history/to_s_spec.rb
new file mode 100644
index 0000000000..ee338f2ab4
--- /dev/null
+++ b/spec/ruby/library/readline/history/to_s_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :readline do
+ describe "Readline::HISTORY.to_s" do
+ it "returns 'HISTORY'" do
+ Readline::HISTORY.to_s.should == "HISTORY"
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/readline_spec.rb b/spec/ruby/library/readline/readline_spec.rb
new file mode 100644
index 0000000000..f716d7b2df
--- /dev/null
+++ b/spec/ruby/library/readline/readline_spec.rb
@@ -0,0 +1,31 @@
+require_relative 'spec_helper'
+
+with_feature :readline do
+ describe "Readline.readline" do
+ before :each do
+ @file = tmp('readline')
+ @out = tmp('out.txt')
+ touch(@file) { |f|
+ f.puts "test"
+ }
+ @options = { options: "-rreadline", args: [@out, "< #{@file}"] }
+ end
+
+ after :each do
+ rm_r @file, @out
+ end
+
+ # Somehow those specs block on Windows
+ platform_is_not :windows do
+ it "returns the input string" do
+ ruby_exe('File.write ARGV[0], Readline.readline', @options)
+ File.read(@out).should == "test"
+ end
+
+ it "taints the returned strings" do
+ ruby_exe('File.write ARGV[0], Readline.readline.tainted?', @options)
+ File.read(@out).should == "true"
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/readline/spec_helper.rb b/spec/ruby/library/readline/spec_helper.rb
new file mode 100644
index 0000000000..56077c53c4
--- /dev/null
+++ b/spec/ruby/library/readline/spec_helper.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+begin
+ require 'readline'
+rescue LoadError
+else
+ # rb-readline behaves quite differently
+ unless defined?(RbReadline)
+ MSpec.enable_feature :readline
+ end
+end
diff --git a/spec/ruby/library/readline/vi_editing_mode_spec.rb b/spec/ruby/library/readline/vi_editing_mode_spec.rb
new file mode 100644
index 0000000000..6622962ceb
--- /dev/null
+++ b/spec/ruby/library/readline/vi_editing_mode_spec.rb
@@ -0,0 +1,11 @@
+require_relative 'spec_helper'
+
+platform_is_not :darwin do
+ with_feature :readline do
+ describe "Readline.vi_editing_mode" do
+ it "returns nil" do
+ Readline.vi_editing_mode.should be_nil
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/resolv/get_address_spec.rb b/spec/ruby/library/resolv/get_address_spec.rb
new file mode 100644
index 0000000000..3506a652b6
--- /dev/null
+++ b/spec/ruby/library/resolv/get_address_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require 'resolv'
+
+describe "Resolv#getaddress" do
+ platform_is_not :windows do
+ it "resolves localhost" do
+ res = Resolv.new([Resolv::Hosts.new])
+
+ lambda {
+ res.getaddress("localhost")
+ }.should_not raise_error(Resolv::ResolvError)
+ end
+ end
+
+ it "raises ResolvError if the name can not be looked up" do
+ res = Resolv.new([])
+ lambda {
+ res.getaddress("should.raise.error.")
+ }.should raise_error(Resolv::ResolvError)
+ end
+end
diff --git a/spec/ruby/library/resolv/get_addresses_spec.rb b/spec/ruby/library/resolv/get_addresses_spec.rb
new file mode 100644
index 0000000000..c484161163
--- /dev/null
+++ b/spec/ruby/library/resolv/get_addresses_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require 'resolv'
+
+describe "Resolv#getaddresses" do
+ platform_is_not :windows do
+ it "resolves localhost" do
+ res = Resolv.new([Resolv::Hosts.new])
+
+ addresses = res.getaddresses("localhost")
+ addresses.should_not == nil
+ addresses.size.should > 0
+ end
+ end
+end
diff --git a/spec/ruby/library/resolv/get_name_spec.rb b/spec/ruby/library/resolv/get_name_spec.rb
new file mode 100644
index 0000000000..0433836b5f
--- /dev/null
+++ b/spec/ruby/library/resolv/get_name_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require 'resolv'
+
+describe "Resolv#getname" do
+ platform_is_not :windows do
+ it "resolves 127.0.0.1" do
+ lambda {
+ Resolv.getname("127.0.0.1")
+ }.should_not raise_error(Resolv::ResolvError)
+ end
+ end
+
+ it "raises ResolvError when there is no result" do
+ res = Resolv.new([])
+ lambda {
+ res.getname("should.raise.error")
+ }.should raise_error(Resolv::ResolvError)
+ end
+end
diff --git a/spec/ruby/library/resolv/get_names_spec.rb b/spec/ruby/library/resolv/get_names_spec.rb
new file mode 100644
index 0000000000..fa77ba771f
--- /dev/null
+++ b/spec/ruby/library/resolv/get_names_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require 'resolv'
+
+describe "Resolv#getnames" do
+ platform_is_not :windows do
+ it "resolves 127.0.0.1" do
+ res = Resolv.new([Resolv::Hosts.new])
+
+ names = res.getnames("127.0.0.1")
+ names.should_not == nil
+ names.size.should > 0
+ end
+ end
+end
diff --git a/spec/ruby/library/rexml/attribute/clone_spec.rb b/spec/ruby/library/rexml/attribute/clone_spec.rb
new file mode 100644
index 0000000000..9a4a4079e1
--- /dev/null
+++ b/spec/ruby/library/rexml/attribute/clone_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attribute#clone" do
+ it "returns a copy of this Attribute" do
+ orig = REXML::Attribute.new("name", "value&&")
+ orig.should == orig.clone
+ orig.clone.to_s.should == orig.to_s
+ orig.clone.to_string.should == orig.to_string
+ end
+end
diff --git a/spec/ruby/library/rexml/attribute/element_spec.rb b/spec/ruby/library/rexml/attribute/element_spec.rb
new file mode 100644
index 0000000000..832e7e9a41
--- /dev/null
+++ b/spec/ruby/library/rexml/attribute/element_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attribute#element" do
+ it "returns the parent element" do
+ e = REXML::Element.new "root"
+
+ REXML::Attribute.new("name", "value", e).element.should == e
+ REXML::Attribute.new("name", "default_constructor").element.should == nil
+ end
+end
+
+describe "REXML::Attribute#element=" do
+ it "sets the parent element" do
+ e = REXML::Element.new "root"
+ f = REXML::Element.new "temp"
+ a = REXML::Attribute.new("name", "value", e)
+ a.element.should == e
+
+ a.element = f
+ a.element.should == f
+ end
+end
diff --git a/spec/ruby/library/rexml/attribute/equal_value_spec.rb b/spec/ruby/library/rexml/attribute/equal_value_spec.rb
new file mode 100644
index 0000000000..8bf2c0a3a1
--- /dev/null
+++ b/spec/ruby/library/rexml/attribute/equal_value_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attribute#==" do
+ it "returns true if other has equal name and value" do
+ a1 = REXML::Attribute.new("foo", "bar")
+ a1.should == a1.clone
+
+ a2 = REXML::Attribute.new("foo", "bar")
+ a1.should == a2
+
+ a3 = REXML::Attribute.new("foo", "bla")
+ a1.should_not == a3
+
+ a4 = REXML::Attribute.new("baz", "bar")
+ a1.should_not == a4
+ end
+end
diff --git a/spec/ruby/library/rexml/attribute/hash_spec.rb b/spec/ruby/library/rexml/attribute/hash_spec.rb
new file mode 100644
index 0000000000..dd71b28108
--- /dev/null
+++ b/spec/ruby/library/rexml/attribute/hash_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attribute#hash" do
+ # These are not really complete, any idea on how to make them more
+ # "testable" will be appreciated.
+ it "returns a hashcode made of the name and value of self" do
+ a = REXML::Attribute.new("name", "value")
+ a.hash.should be_kind_of(Numeric)
+ b = REXML::Attribute.new(a)
+ a.hash.should == b.hash
+ end
+end
diff --git a/spec/ruby/library/rexml/attribute/initialize_spec.rb b/spec/ruby/library/rexml/attribute/initialize_spec.rb
new file mode 100644
index 0000000000..3452b6c864
--- /dev/null
+++ b/spec/ruby/library/rexml/attribute/initialize_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attribute#initialize" do
+ before :each do
+ @e = REXML::Element.new "root"
+ @name = REXML::Attribute.new("name", "Nicko")
+ @e.add_attribute @name
+ end
+
+ it "receives two strings for name and value" do
+ @e.attributes["name"].should == "Nicko"
+ @e.add_attribute REXML::Attribute.new("last_name", nil)
+ @e.attributes["last_name"].should == ""
+ end
+
+ it "receives an Attribute and clones it" do
+ copy = REXML::Attribute.new(@name)
+ copy.should == @name
+ end
+
+ it "recives a parent node" do
+ last_name = REXML::Attribute.new("last_name", "McBrain", @e)
+ last_name.element.should == @e
+
+ last_name = REXML::Attribute.new(@name, @e)
+ last_name.element.should == @e
+ end
+end
diff --git a/spec/ruby/library/rexml/attribute/inspect_spec.rb b/spec/ruby/library/rexml/attribute/inspect_spec.rb
new file mode 100644
index 0000000000..632b477cca
--- /dev/null
+++ b/spec/ruby/library/rexml/attribute/inspect_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attribute#inspect" do
+ it "returns the name and value as a string" do
+ a = REXML::Attribute.new("my_name", "my_value")
+ a.inspect.should == "my_name='my_value'"
+ end
+
+ it "accepts attributes with no value" do
+ a = REXML::Attribute.new("my_name")
+ a.inspect.should == "my_name=''"
+ end
+
+ it "does not escape text" do
+ a = REXML::Attribute.new("name", "<>")
+ a.inspect.should == "name='<>'"
+ end
+end
diff --git a/spec/ruby/library/rexml/attribute/namespace_spec.rb b/spec/ruby/library/rexml/attribute/namespace_spec.rb
new file mode 100644
index 0000000000..9d50770735
--- /dev/null
+++ b/spec/ruby/library/rexml/attribute/namespace_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attribute#namespace" do
+ it "returns the namespace url" do
+ e = REXML::Element.new("root")
+ e.add_attribute REXML::Attribute.new("xmlns:ns", "http://some_uri")
+ e.namespace("ns").should == "http://some_uri"
+ end
+
+ it "returns nil if namespace is not defined" do
+ e = REXML::Element.new("root")
+ e.add_attribute REXML::Attribute.new("test", "value")
+ e.namespace("test").should == nil
+ e.namespace("ns").should == nil
+ end
+
+ it "defaults arg to nil" do
+ e = REXML::Element.new("root")
+ e.add_attribute REXML::Attribute.new("xmlns:ns", "http://some_uri")
+ e.namespace.should == ""
+ e.namespace("ns").should == "http://some_uri"
+ end
+end
diff --git a/spec/ruby/library/rexml/attribute/node_type_spec.rb b/spec/ruby/library/rexml/attribute/node_type_spec.rb
new file mode 100644
index 0000000000..664d7cfa03
--- /dev/null
+++ b/spec/ruby/library/rexml/attribute/node_type_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attribute#node_type" do
+ it "always returns :attribute" do
+ attr = REXML::Attribute.new("foo", "bar")
+ attr.node_type.should == :attribute
+ REXML::Attribute.new(attr).node_type.should == :attribute
+ end
+end
diff --git a/spec/ruby/library/rexml/attribute/prefix_spec.rb b/spec/ruby/library/rexml/attribute/prefix_spec.rb
new file mode 100644
index 0000000000..2a47f74ad1
--- /dev/null
+++ b/spec/ruby/library/rexml/attribute/prefix_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attribute#prefix" do
+ it "returns the namespace of the Attribute" do
+ ans = REXML::Attribute.new("ns:someattr", "some_value")
+ out = REXML::Attribute.new("out:something", "some_other_value")
+
+ ans.prefix.should == "ns"
+ out.prefix.should == "out"
+ end
+
+ it "returns an empty string for Attributes with no prefixes" do
+ attr = REXML::Attribute.new("foo", "bar")
+
+ attr.prefix.should == ""
+ end
+end
diff --git a/spec/ruby/library/rexml/attribute/remove_spec.rb b/spec/ruby/library/rexml/attribute/remove_spec.rb
new file mode 100644
index 0000000000..b97b860a5f
--- /dev/null
+++ b/spec/ruby/library/rexml/attribute/remove_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attribute#remove" do
+ before :each do
+ @e = REXML::Element.new "Root"
+ @attr = REXML::Attribute.new("foo", "bar")
+ end
+
+ it "deletes this Attribute from parent" do
+ @e.add_attribute(@attr)
+ @e.attributes["foo"].should_not == nil
+ @attr.remove
+ @e.attributes["foo"].should == nil
+ end
+
+ it "does not anything if element has no parent" do
+ lambda {@attr.remove}.should_not raise_error(Exception)
+ end
+end
diff --git a/spec/ruby/library/rexml/attribute/to_s_spec.rb b/spec/ruby/library/rexml/attribute/to_s_spec.rb
new file mode 100644
index 0000000000..e1ce48ec33
--- /dev/null
+++ b/spec/ruby/library/rexml/attribute/to_s_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attribute#to_s" do
+ it "returns the value of the Attribute" do
+ REXML::Attribute.new("name", "some_value").to_s.should == "some_value"
+ end
+
+ it "returns the escaped value if it was created from Attribute" do
+ orig = REXML::Attribute.new("name", "<&>")
+ copy = REXML::Attribute.new(orig)
+ copy.to_s.should == "&lt;&amp;&gt;"
+ end
+end
diff --git a/spec/ruby/library/rexml/attribute/to_string_spec.rb b/spec/ruby/library/rexml/attribute/to_string_spec.rb
new file mode 100644
index 0000000000..420913afeb
--- /dev/null
+++ b/spec/ruby/library/rexml/attribute/to_string_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attribute#to_string" do
+ it "returns the attribute as XML" do
+ attr = REXML::Attribute.new("name", "value")
+ attr_empty = REXML::Attribute.new("name")
+ attr_ns = REXML::Attribute.new("xmlns:ns", "http://uri")
+
+ attr.to_string.should == "name='value'"
+ attr_empty.to_string.should == "name=''"
+ attr_ns.to_string.should == "xmlns:ns='http://uri'"
+ end
+end
diff --git a/spec/ruby/library/rexml/attribute/value_spec.rb b/spec/ruby/library/rexml/attribute/value_spec.rb
new file mode 100644
index 0000000000..7763976881
--- /dev/null
+++ b/spec/ruby/library/rexml/attribute/value_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attribute#value" do
+ it "returns the value of the Attribute unnormalized" do
+ attr = REXML::Attribute.new("name", "value")
+ attr_ents = REXML::Attribute.new("name", "<&>")
+ attr_empty = REXML::Attribute.new("name")
+
+ attr.value.should == "value"
+ attr_ents.value.should == "<&>"
+ attr_empty.value.should == ""
+ end
+end
diff --git a/spec/ruby/library/rexml/attribute/write_spec.rb b/spec/ruby/library/rexml/attribute/write_spec.rb
new file mode 100644
index 0000000000..7ada7460f9
--- /dev/null
+++ b/spec/ruby/library/rexml/attribute/write_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attribute#write" do
+ before :each do
+ @attr = REXML::Attribute.new("name", "Charlotte")
+ @s = ""
+ end
+
+ it "writes the name and value to output" do
+ @attr.write(@s)
+ @s.should == "name='Charlotte'"
+ end
+
+ it "currently ignores the second argument" do
+ @attr.write(@s, 3)
+ @s.should == "name='Charlotte'"
+
+ @s = ""
+ @attr.write(@s, "foo")
+ @s.should == "name='Charlotte'"
+ end
+end
diff --git a/spec/ruby/library/rexml/attribute/xpath_spec.rb b/spec/ruby/library/rexml/attribute/xpath_spec.rb
new file mode 100644
index 0000000000..beefb036cc
--- /dev/null
+++ b/spec/ruby/library/rexml/attribute/xpath_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attribute#xpath" do
+
+ before :each do
+ @e = REXML::Element.new "root"
+ @attr = REXML::Attribute.new("year", "1989")
+ end
+
+ it "returns the path for Attribute" do
+ @e.add_attribute @attr
+ @attr.xpath.should == "root/@year"
+ end
+
+ it "raises an error if attribute has no parent" do
+ lambda { @attr.xpath }.should raise_error(Exception)
+ end
+end
diff --git a/spec/ruby/library/rexml/attributes/add_spec.rb b/spec/ruby/library/rexml/attributes/add_spec.rb
new file mode 100644
index 0000000000..32a927e10b
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/add_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/add'
+require 'rexml/document'
+
+describe "REXML::Attributes#add" do
+ it_behaves_like :rexml_attribute_add, :add
+end
diff --git a/spec/ruby/library/rexml/attributes/append_spec.rb b/spec/ruby/library/rexml/attributes/append_spec.rb
new file mode 100644
index 0000000000..f1b08f7d6a
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/append_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/add'
+require 'rexml/document'
+
+describe "REXML::Attributes#<<" do
+ it_behaves_like :rexml_attribute_add, :<<
+end
diff --git a/spec/ruby/library/rexml/attributes/delete_all_spec.rb b/spec/ruby/library/rexml/attributes/delete_all_spec.rb
new file mode 100644
index 0000000000..9340b5693b
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/delete_all_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attributes#delete_all" do
+ before :each do
+ @e = REXML::Element.new("root")
+ end
+
+ it "deletes all attributes that match name" do
+ uri = REXML::Attribute.new("uri", "http://something")
+ @e.attributes << uri
+ @e.attributes.delete_all("uri")
+ @e.attributes.should be_empty
+ @e.attributes["uri"].should == nil
+ end
+
+ it "deletes all attributes that match name with a namespace" do
+ ns_uri = REXML::Attribute.new("xmlns:uri", "http://something_here_too")
+ @e.attributes << ns_uri
+ @e.attributes.delete_all("xmlns:uri")
+ @e.attributes.should be_empty
+ @e.attributes["xmlns:uri"].should == nil
+ end
+
+ it "returns the removed attribute" do
+ uri = REXML::Attribute.new("uri", "http://something_here_too")
+ @e.attributes << uri
+ attrs = @e.attributes.delete_all("uri")
+ attrs.first.should == uri
+ end
+end
diff --git a/spec/ruby/library/rexml/attributes/delete_spec.rb b/spec/ruby/library/rexml/attributes/delete_spec.rb
new file mode 100644
index 0000000000..495e4085ef
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/delete_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attributes#delete" do
+ before :each do
+ @e = REXML::Element.new("root")
+ @name = REXML::Attribute.new("name", "Pepe")
+ end
+
+ it "takes an attribute name and deletes the attribute" do
+ @e.attributes.delete("name")
+ @e.attributes["name"].should be_nil
+ @e.attributes.should be_empty
+ end
+
+ it "takes an Attribute and deletes it" do
+ @e.attributes.delete(@name)
+ @e.attributes["name"].should be_nil
+ @e.attributes.should be_empty
+ end
+
+ it "returns the element with the attribute removed" do
+ ret_val = @e.attributes.delete(@name)
+ ret_val.should == @e
+ ret_val.attributes.should be_empty
+ end
+end
diff --git a/spec/ruby/library/rexml/attributes/each_attribute_spec.rb b/spec/ruby/library/rexml/attributes/each_attribute_spec.rb
new file mode 100644
index 0000000000..e84c8dbf51
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/each_attribute_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attributes#each_attribute" do
+ it "iterates over the attributes yielding actual Attribute objects" do
+ e = REXML::Element.new("root")
+ name = REXML::Attribute.new("name", "Joe")
+ ns_uri = REXML::Attribute.new("xmlns:ns", "http://some_uri")
+ e.add_attribute name
+ e.add_attribute ns_uri
+
+ attributes = []
+
+ e.attributes.each_attribute do |attr|
+ attributes << attr
+ end
+
+ attributes = attributes.sort_by {|a| a.name }
+ attributes.first.should == name
+ attributes.last.should == ns_uri
+ end
+end
diff --git a/spec/ruby/library/rexml/attributes/each_spec.rb b/spec/ruby/library/rexml/attributes/each_spec.rb
new file mode 100644
index 0000000000..ed60634b90
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/each_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attributes#each" do
+ before :each do
+ @e = REXML::Element.new("root")
+ @name = REXML::Attribute.new("name", "Joe")
+ @ns_uri = REXML::Attribute.new("xmlns:ns", "http://some_uri")
+ @e.add_attribute @name
+ @e.add_attribute @ns_uri
+ end
+
+ it "iterates over the attributes yielding expanded-name/value" do
+ attributes = []
+ @e.attributes.each do |attr|
+ attr.should be_kind_of(Array)
+ attributes << attr
+ end
+ attributes = attributes.sort_by {|a| a.first }
+ attributes.first.should == ["name", "Joe"]
+ attributes.last.should == ["xmlns:ns", "http://some_uri"]
+ end
+end
diff --git a/spec/ruby/library/rexml/attributes/element_reference_spec.rb b/spec/ruby/library/rexml/attributes/element_reference_spec.rb
new file mode 100644
index 0000000000..dac7952669
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/element_reference_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attributes#[]" do
+ before :each do
+ @e = REXML::Element.new("root")
+ @lang = REXML::Attribute.new("language", "english")
+ @e.attributes << @lang
+ end
+
+ it "returns the value of an attribute" do
+ @e.attributes["language"].should == "english"
+ end
+
+ it "returns nil if the attribute does not exist" do
+ @e.attributes["chunky bacon"].should == nil
+ end
+end
diff --git a/spec/ruby/library/rexml/attributes/element_set_spec.rb b/spec/ruby/library/rexml/attributes/element_set_spec.rb
new file mode 100644
index 0000000000..1ed94dd2a1
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/element_set_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attributes#[]=" do
+ before :each do
+ @e = REXML::Element.new("song")
+ @name = REXML::Attribute.new("name", "Holy Smoke!")
+ @e.attributes << @name
+ end
+
+ it "sets an attribute" do
+ @e.attributes["author"] = "_why's foxes"
+ @e.attributes["author"].should == "_why's foxes"
+ end
+
+ it "overwrites an existing attribute" do
+ @e.attributes["name"] = "Chunky Bacon"
+ @e.attributes["name"].should == "Chunky Bacon"
+ end
+
+ it "deletes an attribute is value is nil" do
+ @e.attributes["name"] = nil
+ @e.attributes.length.should == 0
+ end
+end
diff --git a/spec/ruby/library/rexml/attributes/get_attribute_ns_spec.rb b/spec/ruby/library/rexml/attributes/get_attribute_ns_spec.rb
new file mode 100644
index 0000000000..1664d6e42a
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/get_attribute_ns_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attributes#get_attribute_ns" do
+ it "returns an attribute by name and namespace" do
+ e = REXML::Element.new("root")
+ attr = REXML::Attribute.new("xmlns:ns", "http://some_url")
+ e.attributes << attr
+ attr.prefix.should == "xmlns"
+ # This might be a bug in Attribute, commenting until those specs
+ # are ready
+ # e.attributes.get_attribute_ns(attr.prefix, "name").should == "http://some_url"
+ end
+end
diff --git a/spec/ruby/library/rexml/attributes/get_attribute_spec.rb b/spec/ruby/library/rexml/attributes/get_attribute_spec.rb
new file mode 100644
index 0000000000..cfe58c1b9e
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/get_attribute_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attributes#get_attribute" do
+ before :each do
+ @e = REXML::Element.new("root")
+ @name = REXML::Attribute.new("name", "Dave")
+ @e.attributes << @name
+ end
+
+ it "fetches an attributes" do
+ @e.attributes.get_attribute("name").should == @name
+ end
+
+ it "fetches an namespaced attribute" do
+ ns_name = REXML::Attribute.new("im:name", "Murray")
+ @e.attributes << ns_name
+ @e.attributes.get_attribute("name").should == @name
+ @e.attributes.get_attribute("im:name").should == ns_name
+ end
+
+ it "returns an Attribute" do
+ @e.attributes.get_attribute("name").should be_kind_of(REXML::Attribute)
+ end
+
+ it "returns nil if it attribute does not exist" do
+ @e.attributes.get_attribute("fake").should be_nil
+ end
+end
diff --git a/spec/ruby/library/rexml/attributes/initialize_spec.rb b/spec/ruby/library/rexml/attributes/initialize_spec.rb
new file mode 100644
index 0000000000..f18bd20c69
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/initialize_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attributes#initialize" do
+ it "is auto initialized by Element" do
+ e = REXML::Element.new "root"
+ e.attributes.should be_kind_of(REXML::Attributes)
+
+ e.attributes << REXML::Attribute.new("name", "Paul")
+ e.attributes["name"].should == "Paul"
+ end
+
+ it "receives a parent node" do
+ e = REXML::Element.new "root"
+ e.attributes << REXML::Attribute.new("name", "Vic")
+ e.attributes["name"].should == "Vic"
+ end
+end
diff --git a/spec/ruby/library/rexml/attributes/length_spec.rb b/spec/ruby/library/rexml/attributes/length_spec.rb
new file mode 100644
index 0000000000..3a8361b8d7
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/length_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/length'
+require 'rexml/document'
+
+describe "REXML::Attributes#length" do
+ it_behaves_like :rexml_attribute_length, :length
+end
diff --git a/spec/ruby/library/rexml/attributes/namespaces_spec.rb b/spec/ruby/library/rexml/attributes/namespaces_spec.rb
new file mode 100644
index 0000000000..9e329fef6f
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/namespaces_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attributes#namespaces" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/rexml/attributes/prefixes_spec.rb b/spec/ruby/library/rexml/attributes/prefixes_spec.rb
new file mode 100644
index 0000000000..4675095aad
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/prefixes_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attributes#prefixes" do
+ before :each do
+ @e = REXML::Element.new("root")
+ a1 = REXML::Attribute.new("xmlns:a", "bar")
+ a2 = REXML::Attribute.new("xmlns:b", "bla")
+ a3 = REXML::Attribute.new("xmlns:c", "baz")
+ @e.attributes << a1
+ @e.attributes << a2
+ @e.attributes << a3
+
+ @e.attributes << REXML::Attribute.new("xmlns", "foo")
+ end
+
+ it "returns an array with the prefixes of each attribute" do
+ @e.attributes.prefixes.sort.should == ["a", "b", "c"]
+ end
+
+ it "does not include the default namespace" do
+ @e.attributes.prefixes.include?("xmlns").should == false
+ end
+end
diff --git a/spec/ruby/library/rexml/attributes/shared/add.rb b/spec/ruby/library/rexml/attributes/shared/add.rb
new file mode 100644
index 0000000000..872f149f45
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/shared/add.rb
@@ -0,0 +1,17 @@
+describe :rexml_attribute_add, shared: true do
+ before :each do
+ @e = REXML::Element.new("root")
+ @attr = REXML::Attributes.new(@e)
+ @name = REXML::Attribute.new("name", "Joe")
+ end
+
+ it "adds an attribute" do
+ @attr.send(@method, @name)
+ @attr["name"].should == "Joe"
+ end
+
+ it "replaces an existing attribute" do
+ @attr.send(@method, REXML::Attribute.new("name", "Bruce"))
+ @attr["name"].should == "Bruce"
+ end
+end
diff --git a/spec/ruby/library/rexml/attributes/shared/length.rb b/spec/ruby/library/rexml/attributes/shared/length.rb
new file mode 100644
index 0000000000..7848f9bf33
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/shared/length.rb
@@ -0,0 +1,13 @@
+require_relative '../../../../spec_helper'
+require 'rexml/document'
+
+describe :rexml_attribute_length, shared: true do
+ it "returns the number of attributes" do
+ e = REXML::Element.new("root")
+ e.attributes.send(@method).should == 0
+
+ e.attributes << REXML::Attribute.new("name", "John")
+ e.attributes << REXML::Attribute.new("another_name", "Leo")
+ e.attributes.send(@method).should == 2
+ end
+end
diff --git a/spec/ruby/library/rexml/attributes/size_spec.rb b/spec/ruby/library/rexml/attributes/size_spec.rb
new file mode 100644
index 0000000000..3b1df9510d
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/size_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/length'
+require 'rexml/document'
+
+describe "REXML::Attributes#size" do
+ it_behaves_like :rexml_attribute_length, :size
+end
diff --git a/spec/ruby/library/rexml/attributes/to_a_spec.rb b/spec/ruby/library/rexml/attributes/to_a_spec.rb
new file mode 100644
index 0000000000..1fbf71b683
--- /dev/null
+++ b/spec/ruby/library/rexml/attributes/to_a_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Attributes#to_a" do
+ it "returns an array with the attributes" do
+ e = REXML::Element.new("root")
+ name = REXML::Attribute.new("name", "Dave")
+ last = REXML::Attribute.new("last_name", "Murray")
+
+ e.attributes << name
+ e.attributes << last
+
+ e.attributes.to_a.sort{|a,b|a.to_s<=>b.to_s}.should == [name, last]
+ end
+
+ it "returns an empty array if it has no attributes" do
+ REXML::Element.new("root").attributes.to_a.should == []
+ end
+end
diff --git a/spec/ruby/library/rexml/cdata/clone_spec.rb b/spec/ruby/library/rexml/cdata/clone_spec.rb
new file mode 100644
index 0000000000..7d3cfda902
--- /dev/null
+++ b/spec/ruby/library/rexml/cdata/clone_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::CData#clone" do
+ it "makes a copy of itself" do
+ c = REXML::CData.new("some text")
+ c.clone.to_s.should == c.to_s
+ c.clone.should == c
+ end
+end
diff --git a/spec/ruby/library/rexml/cdata/initialize_spec.rb b/spec/ruby/library/rexml/cdata/initialize_spec.rb
new file mode 100644
index 0000000000..0184440d87
--- /dev/null
+++ b/spec/ruby/library/rexml/cdata/initialize_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::CData#initialize" do
+ it "creates a new CData object" do
+ c = REXML::CData.new("some text")
+ c.should be_kind_of(REXML::CData)
+ c.should be_kind_of(REXML::Text)
+ end
+
+ it "respects whitespace if whitespace is true" do
+ c = REXML::CData.new("whitespace test", true)
+ c1 = REXML::CData.new("whitespace test", false)
+
+ c.to_s.should == "whitespace test"
+ c1.to_s.should == "whitespace test"
+ end
+
+ it "receives parent as third argument" do
+ e = REXML::Element.new("root")
+ REXML::CData.new("test", true, e)
+ e.to_s.should == "<root><![CDATA[test]]></root>"
+ end
+end
diff --git a/spec/ruby/library/rexml/cdata/shared/to_s.rb b/spec/ruby/library/rexml/cdata/shared/to_s.rb
new file mode 100644
index 0000000000..f8c4951c95
--- /dev/null
+++ b/spec/ruby/library/rexml/cdata/shared/to_s.rb
@@ -0,0 +1,11 @@
+describe :rexml_cdata_to_s, shared: true do
+ it "returns the contents of the CData" do
+ c = REXML::CData.new("some text")
+ c.send(@method).should == "some text"
+ end
+
+ it "does not escape text" do
+ c1 = REXML::CData.new("some& text\n")
+ c1.send(@method).should == "some& text\n"
+ end
+end
diff --git a/spec/ruby/library/rexml/cdata/to_s_spec.rb b/spec/ruby/library/rexml/cdata/to_s_spec.rb
new file mode 100644
index 0000000000..ff3076e55e
--- /dev/null
+++ b/spec/ruby/library/rexml/cdata/to_s_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/to_s'
+require 'rexml/document'
+
+describe "REXML::CData#to_s" do
+ it_behaves_like :rexml_cdata_to_s, :to_s
+end
diff --git a/spec/ruby/library/rexml/cdata/value_spec.rb b/spec/ruby/library/rexml/cdata/value_spec.rb
new file mode 100644
index 0000000000..6e8f8587a1
--- /dev/null
+++ b/spec/ruby/library/rexml/cdata/value_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/to_s'
+require 'rexml/document'
+
+describe "REXML::CData#value" do
+ it_behaves_like :rexml_cdata_to_s, :value
+end
diff --git a/spec/ruby/library/rexml/document/add_element_spec.rb b/spec/ruby/library/rexml/document/add_element_spec.rb
new file mode 100644
index 0000000000..42981d6465
--- /dev/null
+++ b/spec/ruby/library/rexml/document/add_element_spec.rb
@@ -0,0 +1,31 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Document#add_element" do
+ it "adds arg1 with attributes arg2 as root node" do
+ d = REXML::Document.new
+ e = REXML::Element.new("root")
+ d.add_element e
+ d.root.should == e
+ end
+
+ it "sets arg2 as arg1's attributes" do
+ d = REXML::Document.new
+ e = REXML::Element.new("root")
+ attr = {"foo" => "bar"}
+ d.add_element(e,attr)
+ d.root.attributes["foo"].should == attr["foo"]
+ end
+
+ it "accepts a node name as arg1 and adds it as root" do
+ d = REXML::Document.new
+ d.add_element "foo"
+ d.root.name.should == "foo"
+ end
+
+ it "sets arg1's context to the root's context" do
+ d = REXML::Document.new("", {"foo" => "bar"})
+ d.add_element "foo"
+ d.root.context.should == d.context
+ end
+end
diff --git a/spec/ruby/library/rexml/document/add_spec.rb b/spec/ruby/library/rexml/document/add_spec.rb
new file mode 100644
index 0000000000..a200da1e3d
--- /dev/null
+++ b/spec/ruby/library/rexml/document/add_spec.rb
@@ -0,0 +1,57 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+# This spec defines Document#add and Document#<<
+
+describe :rexml_document_add, shared: true do
+ before :each do
+ @doc = REXML::Document.new("<root/>")
+ @decl = REXML::XMLDecl.new("1.0")
+ end
+
+ it "sets document's XML declaration" do
+ @doc.send(@method, @decl)
+ @doc.xml_decl.should == @decl
+ end
+
+ it "inserts XML declaration as first node" do
+ @doc.send(@method, @decl)
+ @doc.children[0].version.should == "1.0"
+ end
+
+ it "overwrites existing XML declaration" do
+ @doc.send(@method, @decl)
+ @doc.send(@method, REXML::XMLDecl.new("2.0"))
+ @doc.xml_decl.version.should == "2.0"
+ end
+
+ it "sets document DocType" do
+ @doc.send(@method, REXML::DocType.new("transitional"))
+ @doc.doctype.name.should == "transitional"
+ end
+
+ it "overwrites existing DocType" do
+ @doc.send(@method, REXML::DocType.new("transitional"))
+ @doc.send(@method, REXML::DocType.new("strict"))
+ @doc.doctype.name.should == "strict"
+ end
+
+ it "adds root node unless it exists" do
+ d = REXML::Document.new("")
+ elem = REXML::Element.new "root"
+ d.send(@method, elem)
+ d.root.should == elem
+ end
+
+ it "refuses to add second root" do
+ lambda { @doc.send(@method, REXML::Element.new("foo")) }.should raise_error(RuntimeError)
+ end
+end
+
+describe "REXML::Document#add" do
+ it_behaves_like :rexml_document_add, :add
+end
+
+describe "REXML::Document#<<" do
+ it_behaves_like :rexml_document_add, :<<
+end
diff --git a/spec/ruby/library/rexml/document/clone_spec.rb b/spec/ruby/library/rexml/document/clone_spec.rb
new file mode 100644
index 0000000000..4aebb6f156
--- /dev/null
+++ b/spec/ruby/library/rexml/document/clone_spec.rb
@@ -0,0 +1,20 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+# According to the MRI documentation (http://www.ruby-doc.org/stdlib/libdoc/rexml/rdoc/index.html),
+# clone's behavior "should be obvious". Apparently "obvious" means cloning
+# only the attributes and the context of the document, not its children.
+describe "REXML::Document#clone" do
+ it "clones document attributes" do
+ d = REXML::Document.new("foo")
+ d.attributes["foo"] = "bar"
+ e = d.clone
+ e.attributes.should == d.attributes
+ end
+
+ it "clones document context" do
+ d = REXML::Document.new("foo", {"foo" => "bar"})
+ e = d.clone
+ e.context.should == d.context
+ end
+end
diff --git a/spec/ruby/library/rexml/document/doctype_spec.rb b/spec/ruby/library/rexml/document/doctype_spec.rb
new file mode 100644
index 0000000000..b919b071e1
--- /dev/null
+++ b/spec/ruby/library/rexml/document/doctype_spec.rb
@@ -0,0 +1,15 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Document#doctype" do
+ it "returns the doctype" do
+ d = REXML::Document.new
+ dt = REXML::DocType.new("foo")
+ d.add dt
+ d.doctype.should == dt
+ end
+
+ it "returns nil if there's no doctype" do
+ REXML::Document.new.doctype.should == nil
+ end
+end
diff --git a/spec/ruby/library/rexml/document/encoding_spec.rb b/spec/ruby/library/rexml/document/encoding_spec.rb
new file mode 100644
index 0000000000..343e0ee45f
--- /dev/null
+++ b/spec/ruby/library/rexml/document/encoding_spec.rb
@@ -0,0 +1,22 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Document#encoding" do
+ before :each do
+ @doc = REXML::Document.new
+ end
+
+ it "returns encoding from XML declaration" do
+ @doc.add REXML::XMLDecl.new(nil, "UTF-16", nil)
+ @doc.encoding.should == "UTF-16"
+ end
+
+ it "returns encoding from XML declaration (for UTF-16 as well)" do
+ @doc.add REXML::XMLDecl.new("1.0", "UTF-8", nil)
+ @doc.encoding.should == "UTF-8"
+ end
+
+ it "uses UTF-8 as default encoding" do
+ @doc.encoding.should == "UTF-8"
+ end
+end
diff --git a/spec/ruby/library/rexml/document/expanded_name_spec.rb b/spec/ruby/library/rexml/document/expanded_name_spec.rb
new file mode 100644
index 0000000000..1225d13fb0
--- /dev/null
+++ b/spec/ruby/library/rexml/document/expanded_name_spec.rb
@@ -0,0 +1,16 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe :document_expanded_name, shared: true do
+ it "returns an empty string for root" do # root nodes have no expanded name
+ REXML::Document.new.send(@method).should == ""
+ end
+end
+
+describe "REXML::Document#expanded_name" do
+ it_behaves_like :document_expanded_name, :expanded_name
+end
+
+describe "REXML::Document#name" do
+ it_behaves_like :document_expanded_name, :name
+end
diff --git a/spec/ruby/library/rexml/document/new_spec.rb b/spec/ruby/library/rexml/document/new_spec.rb
new file mode 100644
index 0000000000..39e806a472
--- /dev/null
+++ b/spec/ruby/library/rexml/document/new_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Document#new" do
+
+ it "initializes context of {} unless specified" do
+ d = REXML::Document.new("<foo />")
+ d.context.should == {}
+ end
+
+ it "has empty attributes if source is nil" do
+ d = REXML::Document.new(nil)
+ d.elements.should be_empty
+ end
+
+ it "can use other document context" do
+ s = REXML::Document.new("")
+ d = REXML::Document.new(s)
+ d.context.should == s.context
+ end
+
+ it "clones source attributes" do
+ s = REXML::Document.new("<root />")
+ s.attributes["some_attr"] = "some_val"
+ d = REXML::Document.new(s)
+ d.attributes.should == s.attributes
+ end
+
+ it "raises an error if source is not a Document, String or IO" do
+ lambda {REXML::Document.new(3)}.should raise_error(RuntimeError)
+ end
+
+ it "does not perform XML validation" do
+ REXML::Document.new("Invalid document").should be_kind_of(REXML::Document)
+ end
+end
diff --git a/spec/ruby/library/rexml/document/node_type_spec.rb b/spec/ruby/library/rexml/document/node_type_spec.rb
new file mode 100644
index 0000000000..85a4d507aa
--- /dev/null
+++ b/spec/ruby/library/rexml/document/node_type_spec.rb
@@ -0,0 +1,8 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Document#node_type" do
+ it "returns :document" do
+ REXML::Document.new.node_type.should == :document
+ end
+end
diff --git a/spec/ruby/library/rexml/document/root_spec.rb b/spec/ruby/library/rexml/document/root_spec.rb
new file mode 100644
index 0000000000..3c24e79b2d
--- /dev/null
+++ b/spec/ruby/library/rexml/document/root_spec.rb
@@ -0,0 +1,12 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Document#root" do
+ it "returns document root tag name" do
+ REXML::Document.new("<foo/>").root.name.should == "foo"
+ end
+
+ it "returns nil if there is not root" do
+ REXML::Document.new.root.should == nil
+ end
+end
diff --git a/spec/ruby/library/rexml/document/stand_alone_spec.rb b/spec/ruby/library/rexml/document/stand_alone_spec.rb
new file mode 100644
index 0000000000..4ac24329d6
--- /dev/null
+++ b/spec/ruby/library/rexml/document/stand_alone_spec.rb
@@ -0,0 +1,19 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Document#stand_alone?" do
+ it "returns the XMLDecl standalone value" do
+ d = REXML::Document.new
+ decl = REXML::XMLDecl.new("1.0", "UTF-8", "yes")
+ d.add decl
+ d.stand_alone?.should == "yes"
+ end
+
+ # According to the docs this should return the default XMLDecl but that
+ # will carry some more problems when printing the document. Currently, it
+ # returns nil. See http://www.ruby-forum.com/topic/146812#650061
+ it "returns the default value when no XML declaration present" do
+ REXML::Document.new.stand_alone?.should == nil
+ end
+
+end
diff --git a/spec/ruby/library/rexml/document/version_spec.rb b/spec/ruby/library/rexml/document/version_spec.rb
new file mode 100644
index 0000000000..983b4b9af0
--- /dev/null
+++ b/spec/ruby/library/rexml/document/version_spec.rb
@@ -0,0 +1,14 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Document#version" do
+ it "returns XML version from declaration" do
+ d = REXML::Document.new
+ d.add REXML::XMLDecl.new("1.1")
+ d.version.should == "1.1"
+ end
+
+ it "returns the default version when declaration is not present" do
+ REXML::Document.new.version.should == REXML::XMLDecl::DEFAULT_VERSION
+ end
+end
diff --git a/spec/ruby/library/rexml/document/write_spec.rb b/spec/ruby/library/rexml/document/write_spec.rb
new file mode 100644
index 0000000000..efa94b7117
--- /dev/null
+++ b/spec/ruby/library/rexml/document/write_spec.rb
@@ -0,0 +1,35 @@
+require 'rexml/document'
+require 'rexml/formatters/transitive'
+require_relative '../../../spec_helper'
+
+# Maybe this can be cleaned
+describe "REXML::Document#write" do
+ before :each do
+ @d = REXML::Document.new
+ city = REXML::Element.new "Springfield"
+ street = REXML::Element.new "EvergreenTerrace"
+ address = REXML::Element.new "House742"
+ @d << city << street << address
+ @str = ""
+ end
+
+ it "returns document source as string" do
+ @d.write(@str)
+ @str.should == "<Springfield><EvergreenTerrace><House742/></EvergreenTerrace></Springfield>"
+ end
+
+ it "returns document indented" do
+ @d.write(@str, 2)
+ @str.should =~ /\s*<Springfield>\s*<EvergreenTerrace>\s*<House742\/>\s*<\/EvergreenTerrace>\s*<\/Springfield>/
+ end
+
+ it "returns document with transitive support" do
+ @d.write(@str, 2, true)
+ @str.should =~ /\s*<Springfield\s*><EvergreenTerrace\s*><House742\s*\/><\/EvergreenTerrace\s*><\/Springfield\s*>/
+ end
+
+ it "returns document with support for IE" do
+ @d.write(@str, -1, false, true)
+ @str.should == "<Springfield><EvergreenTerrace><House742 /></EvergreenTerrace></Springfield>"
+ end
+end
diff --git a/spec/ruby/library/rexml/document/xml_decl_spec.rb b/spec/ruby/library/rexml/document/xml_decl_spec.rb
new file mode 100644
index 0000000000..30288a150b
--- /dev/null
+++ b/spec/ruby/library/rexml/document/xml_decl_spec.rb
@@ -0,0 +1,15 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Document#xml_decl" do
+ it "returns XML declaration of the document" do
+ d = REXML::Document.new
+ decl = REXML::XMLDecl.new("1.0", "UTF-16", "yes")
+ d.add decl
+ d.xml_decl.should == decl
+ end
+
+ it "returns default XML declaration unless present" do
+ REXML::Document.new.xml_decl.should == REXML::XMLDecl.new
+ end
+end
diff --git a/spec/ruby/library/rexml/element/add_attribute_spec.rb b/spec/ruby/library/rexml/element/add_attribute_spec.rb
new file mode 100644
index 0000000000..d15fb81ef1
--- /dev/null
+++ b/spec/ruby/library/rexml/element/add_attribute_spec.rb
@@ -0,0 +1,41 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#add_attribute" do
+ before :each do
+ @person = REXML::Element.new "person"
+ @person.attributes["name"] = "Bill"
+ end
+
+ it "adds a new attribute" do
+ @person.add_attribute("age", "17")
+ @person.attributes["age"].should == "17"
+ end
+
+ it "overwrites an existing attribute" do
+ @person.add_attribute("name", "Bill")
+ @person.attributes["name"].should == "Bill"
+ end
+
+ it "accepts a pair of strings" do
+ @person.add_attribute("male", "true")
+ @person.attributes["male"].should == "true"
+ end
+
+ it "accepts an Attribute for key" do
+ attr = REXML::Attribute.new("male", "true")
+ @person.add_attribute attr
+ @person.attributes["male"].should == "true"
+ end
+
+ it "ignores value if key is an Attribute" do
+ attr = REXML::Attribute.new("male", "true")
+ @person.add_attribute(attr, "false")
+ @person.attributes["male"].should == "true"
+ end
+
+ it "returns the attribute added" do
+ attr = REXML::Attribute.new("name", "Tony")
+ @person.add_attribute(attr).should == attr
+ end
+end
diff --git a/spec/ruby/library/rexml/element/add_attributes_spec.rb b/spec/ruby/library/rexml/element/add_attributes_spec.rb
new file mode 100644
index 0000000000..b462fdf5fe
--- /dev/null
+++ b/spec/ruby/library/rexml/element/add_attributes_spec.rb
@@ -0,0 +1,22 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#add_attributes" do
+ before :each do
+ @person = REXML::Element.new "person"
+ @person.attributes["name"] = "Bill"
+ end
+
+ it "adds multiple attributes from a hash" do
+ @person.add_attributes({"name" => "Joe", "age" => "27"})
+ @person.attributes["name"].should == "Joe"
+ @person.attributes["age"].should == "27"
+ end
+
+ it "adds multiple attributes from an array" do
+ attrs = { "name" => "Joe", "age" => "27"}
+ @person.add_attributes attrs.to_a
+ @person.attributes["name"].should == "Joe"
+ @person.attributes["age"].should == "27"
+ end
+end
diff --git a/spec/ruby/library/rexml/element/add_element_spec.rb b/spec/ruby/library/rexml/element/add_element_spec.rb
new file mode 100644
index 0000000000..8e2ffe3148
--- /dev/null
+++ b/spec/ruby/library/rexml/element/add_element_spec.rb
@@ -0,0 +1,39 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+
+describe "REXML::Element#add_element" do
+ before :each do
+ @root = REXML::Element.new("root")
+ end
+
+ it "adds a child without attributes" do
+ name = REXML::Element.new("name")
+ @root.add_element name
+ @root.elements["name"].name.should == name.name
+ @root.elements["name"].attributes.should == name.attributes
+ @root.elements["name"].context.should == name.context
+ end
+
+ it "adds a child with attributes" do
+ person = REXML::Element.new("person")
+ @root.add_element(person, {"name" => "Madonna"})
+ @root.elements["person"].name.should == person.name
+ @root.elements["person"].attributes.should == person.attributes
+ @root.elements["person"].context.should == person.context
+ end
+
+ it "adds a child with name" do
+ @root.add_element "name"
+ @root.elements["name"].name.should == "name"
+ @root.elements["name"].attributes.should == {}
+ @root.elements["name"].context.should == nil
+ end
+
+ it "returns the added child" do
+ name = @root.add_element "name"
+ @root.elements["name"].name.should == name.name
+ @root.elements["name"].attributes.should == name.attributes
+ @root.elements["name"].context.should == name.context
+ end
+end
diff --git a/spec/ruby/library/rexml/element/add_namespace_spec.rb b/spec/ruby/library/rexml/element/add_namespace_spec.rb
new file mode 100644
index 0000000000..77c00eec46
--- /dev/null
+++ b/spec/ruby/library/rexml/element/add_namespace_spec.rb
@@ -0,0 +1,23 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#add_namespace" do
+ before :each do
+ @elem = REXML::Element.new("person")
+ end
+
+ it "adds a namespace to element" do
+ @elem.add_namespace("foo", "bar")
+ @elem.namespace("foo").should == "bar"
+ end
+
+ it "accepts a prefix string as prefix" do
+ @elem.add_namespace("xmlns:foo", "bar")
+ @elem.namespace("foo").should == "bar"
+ end
+
+ it "uses prefix as URI if uri is nil" do
+ @elem.add_namespace("some_uri", nil)
+ @elem.namespace.should == "some_uri"
+ end
+end
diff --git a/spec/ruby/library/rexml/element/add_text_spec.rb b/spec/ruby/library/rexml/element/add_text_spec.rb
new file mode 100644
index 0000000000..54e127bf4b
--- /dev/null
+++ b/spec/ruby/library/rexml/element/add_text_spec.rb
@@ -0,0 +1,24 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#add_text" do
+ before :each do
+ @name = REXML::Element.new "Name"
+ end
+
+ it "adds text to an element" do
+ @name.add_text "Ringo"
+ @name.to_s.should == "<Name>Ringo</Name>"
+ end
+
+ it "accepts a Text" do
+ @name.add_text(REXML::Text.new("Ringo"))
+ @name.to_s.should == "<Name>Ringo</Name>"
+ end
+
+ it "joins the new text with the old one" do
+ @name.add_text "Ringo"
+ @name.add_text " Starr"
+ @name.to_s.should == "<Name>Ringo Starr</Name>"
+ end
+end
diff --git a/spec/ruby/library/rexml/element/attribute_spec.rb b/spec/ruby/library/rexml/element/attribute_spec.rb
new file mode 100644
index 0000000000..e40d612ef3
--- /dev/null
+++ b/spec/ruby/library/rexml/element/attribute_spec.rb
@@ -0,0 +1,17 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#attribute" do
+ it "returns an attribute by name" do
+ person = REXML::Element.new "Person"
+ attribute = REXML::Attribute.new("drink", "coffee")
+ person.add_attribute(attribute)
+ person.attribute("drink").should == attribute
+ end
+
+ it "supports attributes inside namespaces" do
+ e = REXML::Element.new("element")
+ e.add_attributes({"xmlns:ns" => "http://some_uri"})
+ e.attribute("ns", "ns").to_s.should == "http://some_uri"
+ end
+end
diff --git a/spec/ruby/library/rexml/element/attributes_spec.rb b/spec/ruby/library/rexml/element/attributes_spec.rb
new file mode 100644
index 0000000000..8959b769a8
--- /dev/null
+++ b/spec/ruby/library/rexml/element/attributes_spec.rb
@@ -0,0 +1,19 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#attributes" do
+ it "returns element's Attributes" do
+ p = REXML::Element.new "Person"
+
+ name = REXML::Attribute.new("name", "John")
+ attrs = REXML::Attributes.new(p)
+ attrs.add name
+
+ p.add_attribute name
+ p.attributes.should == attrs
+ end
+
+ it "returns an empty hash if element has no attributes" do
+ REXML::Element.new("Person").attributes.should == {}
+ end
+end
diff --git a/spec/ruby/library/rexml/element/cdatas_spec.rb b/spec/ruby/library/rexml/element/cdatas_spec.rb
new file mode 100644
index 0000000000..a371a5734b
--- /dev/null
+++ b/spec/ruby/library/rexml/element/cdatas_spec.rb
@@ -0,0 +1,24 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#cdatas" do
+ before :each do
+ @e = REXML::Element.new("Root")
+ end
+
+ it "returns the array of children cdatas" do
+ c = REXML::CData.new("Primary")
+ d = REXML::CData.new("Secondary")
+ @e << c
+ @e << d
+ @e.cdatas.should == [c, d]
+ end
+
+ it "freezes the returned array" do
+ @e.cdatas.frozen?.should == true
+ end
+
+ it "returns an empty array if element has no cdata" do
+ @e.cdatas.should == []
+ end
+end
diff --git a/spec/ruby/library/rexml/element/clone_spec.rb b/spec/ruby/library/rexml/element/clone_spec.rb
new file mode 100644
index 0000000000..d26392db41
--- /dev/null
+++ b/spec/ruby/library/rexml/element/clone_spec.rb
@@ -0,0 +1,29 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#clone" do
+ before :each do
+ @e = REXML::Element.new "a"
+ end
+ it "creates a copy of element" do
+ @e.clone.to_s.should == @e.to_s
+ end
+
+ it "copies the attributes" do
+ @e.add_attribute("foo", "bar")
+ @e.clone.to_s.should == @e.to_s
+ end
+
+ it "does not copy the text" do
+ @e.add_text "some text..."
+ @e.clone.to_s.should_not == @e
+ @e.clone.to_s.should == "<a/>"
+ end
+
+ it "does not copy the child elements" do
+ b = REXML::Element.new "b"
+ @e << b
+ @e.clone.should_not == @e
+ @e.clone.to_s.should == "<a/>"
+ end
+end
diff --git a/spec/ruby/library/rexml/element/comments_spec.rb b/spec/ruby/library/rexml/element/comments_spec.rb
new file mode 100644
index 0000000000..9dac2cc5b8
--- /dev/null
+++ b/spec/ruby/library/rexml/element/comments_spec.rb
@@ -0,0 +1,20 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#comments" do
+ before :each do
+ @e = REXML::Element.new "root"
+ @c1 = REXML::Comment.new "this is a comment"
+ @c2 = REXML::Comment.new "this is another comment"
+ @e << @c1
+ @e << @c2
+ end
+
+ it "returns the array of comments" do
+ @e.comments.should == [@c1, @c2]
+ end
+
+ it "returns a frozen object" do
+ @e.comments.frozen?.should == true
+ end
+end
diff --git a/spec/ruby/library/rexml/element/delete_attribute_spec.rb b/spec/ruby/library/rexml/element/delete_attribute_spec.rb
new file mode 100644
index 0000000000..5c55c5efda
--- /dev/null
+++ b/spec/ruby/library/rexml/element/delete_attribute_spec.rb
@@ -0,0 +1,39 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#delete_attribute" do
+ before :each do
+ @e = REXML::Element.new("Person")
+ @attr = REXML::Attribute.new("name", "Sean")
+ @e.add_attribute(@attr)
+ end
+
+ it "deletes an attribute from the element" do
+ @e.delete_attribute("name")
+ @e.attributes["name"].should be_nil
+ end
+
+# Bug was filled with a patch in Ruby's tracker #20298
+ quarantine! do
+ it "receives an Attribute" do
+ @e.add_attribute(@attr)
+ @e.delete_attribute(@attr)
+ @e.attributes["name"].should be_nil
+ end
+ end
+
+ # Docs say that it returns the removed attribute but then examples
+ # show it returns the element with the attribute removed.
+ # Also fixed in #20298
+ it "returns the element with the attribute removed" do
+ elem = @e.delete_attribute("name")
+ elem.attributes.should be_empty
+ elem.to_s.should eql("<Person/>")
+ end
+
+ it "returns nil if the attribute does not exist" do
+ @e.delete_attribute("name")
+ at = @e.delete_attribute("name")
+ at.should be_nil
+ end
+end
diff --git a/spec/ruby/library/rexml/element/delete_element_spec.rb b/spec/ruby/library/rexml/element/delete_element_spec.rb
new file mode 100644
index 0000000000..9417229bd4
--- /dev/null
+++ b/spec/ruby/library/rexml/element/delete_element_spec.rb
@@ -0,0 +1,49 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#delete_element" do
+ before :each do
+ @root = REXML::Element.new("root")
+ end
+
+ it "deletes the child element" do
+ node = REXML::Element.new("some_node")
+ @root.add_element node
+ @root.delete_element node
+ @root.elements.size.should == 0
+ end
+
+ it "deletes a child via XPath" do
+ @root.add_element "some_node"
+ @root.delete_element "some_node"
+ @root.elements.size.should == 0
+ end
+
+ it "deletes the child at index" do
+ @root.add_element "some_node"
+ @root.delete_element 1
+ @root.elements.size.should == 0
+ end
+
+ # According to the docs this should return the deleted element
+ # but it won't if it's an Element.
+ it "deletes Element and returns it" do
+ node = REXML::Element.new("some_node")
+ @root.add_element node
+ del_node = @root.delete_element node
+ del_node.should == node
+ end
+
+ # Note how passing the string will return the removed element
+ # but passing the Element as above won't.
+ it "deletes an element and returns it" do
+ node = REXML::Element.new("some_node")
+ @root.add_element node
+ del_node = @root.delete_element "some_node"
+ del_node.should == node
+ end
+
+ it "returns nil unless element exists" do
+ @root.delete_element("something").should == nil
+ end
+end
diff --git a/spec/ruby/library/rexml/element/delete_namespace_spec.rb b/spec/ruby/library/rexml/element/delete_namespace_spec.rb
new file mode 100644
index 0000000000..8683a40cab
--- /dev/null
+++ b/spec/ruby/library/rexml/element/delete_namespace_spec.rb
@@ -0,0 +1,25 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#delete_namespace" do
+
+ before :each do
+ @doc = REXML::Document.new "<a xmlns:foo='bar' xmlns='twiddle'/>"
+ end
+
+ it "deletes a namespace from the element" do
+ @doc.root.delete_namespace 'foo'
+ @doc.root.namespace("foo").should be_nil
+ @doc.root.to_s.should == "<a xmlns='twiddle'/>"
+ end
+
+ it "deletes default namespace when called with no args" do
+ @doc.root.delete_namespace
+ @doc.root.namespace.should be_empty
+ @doc.root.to_s.should == "<a xmlns:foo='bar'/>"
+ end
+
+ it "returns the element" do
+ @doc.root.delete_namespace.should == @doc.root
+ end
+end
diff --git a/spec/ruby/library/rexml/element/document_spec.rb b/spec/ruby/library/rexml/element/document_spec.rb
new file mode 100644
index 0000000000..24773580f2
--- /dev/null
+++ b/spec/ruby/library/rexml/element/document_spec.rb
@@ -0,0 +1,16 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#document" do
+
+ it "returns the element's document" do
+ d = REXML::Document.new("<root><elem/></root>")
+ d << REXML::XMLDecl.new
+ d.root.document.should == d
+ d.root.document.to_s.should == d.to_s
+ end
+
+ it "returns nil if it belongs to no document" do
+ REXML::Element.new("standalone").document.should be_nil
+ end
+end
diff --git a/spec/ruby/library/rexml/element/each_element_with_attribute_spec.rb b/spec/ruby/library/rexml/element/each_element_with_attribute_spec.rb
new file mode 100644
index 0000000000..3c5da3f015
--- /dev/null
+++ b/spec/ruby/library/rexml/element/each_element_with_attribute_spec.rb
@@ -0,0 +1,35 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#each_element_with_attributes" do
+ before :each do
+ @document = REXML::Element.new("people")
+ @father = REXML::Element.new("Person")
+ @father.attributes["name"] = "Joe"
+ @son = REXML::Element.new("Child")
+ @son.attributes["name"] = "Fred"
+ @document.root << @father
+ @document.root << @son
+ @childs = []
+ end
+
+ it "returns childs with attribute" do
+ @document.each_element_with_attribute("name") { |elem| @childs << elem }
+ @childs[0].should == @father
+ @childs[1].should == @son
+ end
+
+ it "takes attribute value as second argument" do
+ @document.each_element_with_attribute("name", "Fred"){ |elem| elem.should == @son }
+ end
+
+ it "takes max number of childs as third argument" do
+ @document.each_element_with_attribute("name", nil, 1) { |elem| @childs << elem }
+ @childs.size.should == 1
+ @childs[0].should == @father
+ end
+
+ it "takes XPath filter as fourth argument" do
+ @document.each_element_with_attribute("name", nil, 0, "Child"){ |elem| elem.should == @son}
+ end
+end
diff --git a/spec/ruby/library/rexml/element/each_element_with_text_spec.rb b/spec/ruby/library/rexml/element/each_element_with_text_spec.rb
new file mode 100644
index 0000000000..5f9e5b85dc
--- /dev/null
+++ b/spec/ruby/library/rexml/element/each_element_with_text_spec.rb
@@ -0,0 +1,31 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#each_element_with_text" do
+ before :each do
+ @document = REXML::Element.new("people")
+
+ @joe = REXML::Element.new("Person")
+ @joe.text = "Joe"
+ @fred = REXML::Element.new("Person")
+ @fred.text = "Fred"
+ @another = REXML::Element.new("AnotherPerson")
+ @another.text = "Fred"
+ @document.root << @joe
+ @document.root << @fred
+ @document.root << @another
+ @childs = []
+ end
+
+ it "returns childs with text" do
+ @document.each_element_with_text("Joe"){|c| c.should == @joe}
+ end
+
+ it "takes max as second argument" do
+ @document.each_element_with_text("Fred", 1){ |c| c.should == @fred}
+ end
+
+ it "takes XPath filter as third argument" do
+ @document.each_element_with_text("Fred", 0, "Person"){ |c| c.should == @fred}
+ end
+end
diff --git a/spec/ruby/library/rexml/element/element_reference_spec.rb b/spec/ruby/library/rexml/element/element_reference_spec.rb
new file mode 100644
index 0000000000..db94303b1e
--- /dev/null
+++ b/spec/ruby/library/rexml/element/element_reference_spec.rb
@@ -0,0 +1,22 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#[]" do
+
+ before :each do
+ @doc = REXML::Document.new("<root foo='bar'></root>")
+ @child = REXML::Element.new("child")
+ @doc.root.add_element @child
+ end
+
+ ruby_version_is "2.4" do
+ it "return attribute value if argument is string or symbol" do
+ @doc.root[:foo].should == 'bar'
+ @doc.root['foo'].should == 'bar'
+ end
+
+ it "return nth element if argument is int" do
+ @doc.root[0].should == @child
+ end
+ end
+end
diff --git a/spec/ruby/library/rexml/element/get_text_spec.rb b/spec/ruby/library/rexml/element/get_text_spec.rb
new file mode 100644
index 0000000000..8ee9ea0824
--- /dev/null
+++ b/spec/ruby/library/rexml/element/get_text_spec.rb
@@ -0,0 +1,18 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#get_text" do
+ before :each do
+ @doc = REXML::Document.new "<p>some text<b>this is bold!</b> more text</p>"
+ end
+
+ it "returns the first text child node" do
+ @doc.root.get_text.value.should == "some text"
+ @doc.root.get_text.should be_kind_of(REXML::Text)
+ end
+
+ it "returns text from an element matching path" do
+ @doc.root.get_text("b").value.should == "this is bold!"
+ @doc.root.get_text("b").should be_kind_of(REXML::Text)
+ end
+end
diff --git a/spec/ruby/library/rexml/element/has_attributes_spec.rb b/spec/ruby/library/rexml/element/has_attributes_spec.rb
new file mode 100644
index 0000000000..f89ec675f5
--- /dev/null
+++ b/spec/ruby/library/rexml/element/has_attributes_spec.rb
@@ -0,0 +1,17 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#has_attributes?" do
+ before :each do
+ @e = REXML::Element.new("test_elem")
+ end
+
+ it "returns true when element has any attributes" do
+ @e.add_attribute("name", "Joe")
+ @e.has_attributes?.should be_true
+ end
+
+ it "returns false if element has no attributes" do
+ @e.has_attributes?.should be_false
+ end
+end
diff --git a/spec/ruby/library/rexml/element/has_elements_spec.rb b/spec/ruby/library/rexml/element/has_elements_spec.rb
new file mode 100644
index 0000000000..dc5fc9c25b
--- /dev/null
+++ b/spec/ruby/library/rexml/element/has_elements_spec.rb
@@ -0,0 +1,18 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#has_elements?" do
+ before :each do
+ @e = REXML::Element.new("root")
+ end
+
+ it "returns true if element has child elements" do
+ child = REXML::Element.new("child")
+ @e << child
+ @e.has_elements?.should be_true
+ end
+
+ it "returns false if element doesn't have child elements" do
+ @e.has_elements?.should be_false
+ end
+end
diff --git a/spec/ruby/library/rexml/element/has_text_spec.rb b/spec/ruby/library/rexml/element/has_text_spec.rb
new file mode 100644
index 0000000000..e9d5a176cb
--- /dev/null
+++ b/spec/ruby/library/rexml/element/has_text_spec.rb
@@ -0,0 +1,16 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#has_text?" do
+
+ it "returns true if element has a Text child" do
+ e = REXML::Element.new("Person")
+ e.text = "My text"
+ e.has_text?.should be_true
+ end
+
+ it "returns false if it has no Text childs" do
+ e = REXML::Element.new("Person")
+ e.has_text?.should be_false
+ end
+end
diff --git a/spec/ruby/library/rexml/element/inspect_spec.rb b/spec/ruby/library/rexml/element/inspect_spec.rb
new file mode 100644
index 0000000000..f45edd0b1f
--- /dev/null
+++ b/spec/ruby/library/rexml/element/inspect_spec.rb
@@ -0,0 +1,27 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#inspect" do
+
+ before :each do
+ @name = REXML::Element.new "name"
+ end
+
+ it "returns the node as a string" do
+ @name.inspect.should == "<name/>"
+ end
+
+ it "inserts '...' if the node has children" do
+ e = REXML::Element.new "last_name"
+ @name << e
+ @name.inspect.should == "<name> ... </>"
+ # This might make more sense but differs from MRI's default behavior
+ # @name.inspect.should == "<name> ... </name>"
+ end
+
+ it "inserts the attributes in the string" do
+ @name.add_attribute "language"
+ @name.attributes["language"] = "english"
+ @name.inspect.should == "<name language='english'/>"
+ end
+end
diff --git a/spec/ruby/library/rexml/element/instructions_spec.rb b/spec/ruby/library/rexml/element/instructions_spec.rb
new file mode 100644
index 0000000000..aa2d192e7c
--- /dev/null
+++ b/spec/ruby/library/rexml/element/instructions_spec.rb
@@ -0,0 +1,21 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#instructions" do
+ before :each do
+ @elem = REXML::Element.new("root")
+ end
+ it "returns the Instruction children nodes" do
+ inst = REXML::Instruction.new("xml-stylesheet", "href='headlines.css'")
+ @elem << inst
+ @elem.instructions.first.should == inst
+ end
+
+ it "returns an empty array if it has no Instruction children" do
+ @elem.instructions.should be_empty
+ end
+
+ it "freezes the returned array" do
+ @elem.instructions.frozen?.should be_true
+ end
+end
diff --git a/spec/ruby/library/rexml/element/namespace_spec.rb b/spec/ruby/library/rexml/element/namespace_spec.rb
new file mode 100644
index 0000000000..89662f3599
--- /dev/null
+++ b/spec/ruby/library/rexml/element/namespace_spec.rb
@@ -0,0 +1,27 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#namespace" do
+ before :each do
+ @doc = REXML::Document.new("<a xmlns='1' xmlns:y='2'><b/><c xmlns:z='3'/></a>")
+ @elem = @doc.elements["//b"]
+ end
+
+ it "returns the default namespace" do
+ @elem.namespace.should == "1"
+ end
+
+ it "accepts a namespace prefix" do
+ @elem.namespace("y").should == "2"
+ @doc.elements["//c"].namespace("z").should == "3"
+ end
+
+ it "returns an empty String if default namespace is not defined" do
+ e = REXML::Document.new("<a/>")
+ e.root.namespace.should be_empty
+ end
+
+ it "returns nil if namespace is not defined" do
+ @elem.namespace("z").should be_nil
+ end
+end
diff --git a/spec/ruby/library/rexml/element/namespaces_spec.rb b/spec/ruby/library/rexml/element/namespaces_spec.rb
new file mode 100644
index 0000000000..a84c1d1dab
--- /dev/null
+++ b/spec/ruby/library/rexml/element/namespaces_spec.rb
@@ -0,0 +1,32 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#namespaces" do
+ before :each do
+ doc = REXML::Document.new("<a xmlns='1' xmlns:y='2'><b/><c xmlns:z='3'/></a>")
+ @elem = doc.elements["//c"]
+ end
+
+ it "returns a hash of the namespaces" do
+ ns = {"y"=>"2", "z"=>"3", "xmlns"=>"1"}
+ @elem.namespaces.keys.sort.should == ns.keys.sort
+ @elem.namespaces.values.sort.should == ns.values.sort
+ end
+
+ it "returns an empty hash if no namespaces exist" do
+ e = REXML::Element.new "element"
+ e.namespaces.kind_of?(Hash).should == true
+ e.namespaces.should be_empty
+ end
+
+ it "uses namespace prefixes as keys" do
+ prefixes = ["y", "z", "xmlns"]
+ @elem.namespaces.keys.sort.should == prefixes.sort
+ end
+
+ it "uses namespace values as the hash values" do
+ values = ["2", "3", "1"]
+ @elem.namespaces.values.sort.should == values.sort
+ end
+
+end
diff --git a/spec/ruby/library/rexml/element/new_spec.rb b/spec/ruby/library/rexml/element/new_spec.rb
new file mode 100644
index 0000000000..4ffdf4dabe
--- /dev/null
+++ b/spec/ruby/library/rexml/element/new_spec.rb
@@ -0,0 +1,35 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#new" do
+
+ it "creates element from tag name" do
+ REXML::Element.new("foo").name.should == "foo"
+ end
+
+ it "creates element with default attributes" do
+ e = REXML::Element.new
+ e.name.should == REXML::Element::UNDEFINED
+ e.context.should == nil
+ e.parent.should == nil
+ end
+
+ it "creates element from another element" do
+ e = REXML::Element.new "foo"
+ f = REXML::Element.new e
+ e.name.should == f.name
+ e.context.should == f.context
+ e.parent.should == f.parent
+ end
+
+ it "takes parent as second argument" do
+ parent = REXML::Element.new "foo"
+ child = REXML::Element.new "bar", parent
+ child.parent.should == parent
+ end
+
+ it "takes context as third argument" do
+ context = {"some_key" => "some_value"}
+ REXML::Element.new("foo", nil, context).context.should == context
+ end
+end
diff --git a/spec/ruby/library/rexml/element/next_element_spec.rb b/spec/ruby/library/rexml/element/next_element_spec.rb
new file mode 100644
index 0000000000..5b6d6cad9b
--- /dev/null
+++ b/spec/ruby/library/rexml/element/next_element_spec.rb
@@ -0,0 +1,19 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#next_element" do
+ before :each do
+ @a = REXML::Element.new "a"
+ @b = REXML::Element.new "b"
+ @c = REXML::Element.new "c"
+ @a.root << @b
+ @a.root << @c
+ end
+ it "returns next existing element" do
+ @a.elements["b"].next_element.should == @c
+ end
+
+ it "returns nil on last element" do
+ @a.elements["c"].next_element.should == nil
+ end
+end
diff --git a/spec/ruby/library/rexml/element/node_type_spec.rb b/spec/ruby/library/rexml/element/node_type_spec.rb
new file mode 100644
index 0000000000..bcab9e126d
--- /dev/null
+++ b/spec/ruby/library/rexml/element/node_type_spec.rb
@@ -0,0 +1,8 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#node_type" do
+ it "returns :element" do
+ REXML::Element.new("MyElem").node_type.should == :element
+ end
+end
diff --git a/spec/ruby/library/rexml/element/prefixes_spec.rb b/spec/ruby/library/rexml/element/prefixes_spec.rb
new file mode 100644
index 0000000000..b6edf9a847
--- /dev/null
+++ b/spec/ruby/library/rexml/element/prefixes_spec.rb
@@ -0,0 +1,23 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#prefixes" do
+ before :each do
+ doc = REXML::Document.new("<a xmlns='1' xmlns:y='2'><b/><c xmlns:z='3'/></a>")
+ @elem = doc.elements["//c"]
+ end
+
+ it "returns an array of the prefixes of the namespaces" do
+ @elem.prefixes.should == ["y", "z"]
+ end
+
+ it "does not include the default namespace" do
+ @elem.prefixes.include?("xmlns").should == false
+ end
+
+ it "returns an empty array if no namespace was defined" do
+ doc = REXML::Document.new "<root><something/></root>"
+ root = doc.elements["//root"]
+ root.prefixes.should == []
+ end
+end
diff --git a/spec/ruby/library/rexml/element/previous_element_spec.rb b/spec/ruby/library/rexml/element/previous_element_spec.rb
new file mode 100644
index 0000000000..2fe79d955f
--- /dev/null
+++ b/spec/ruby/library/rexml/element/previous_element_spec.rb
@@ -0,0 +1,20 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#previous_element" do
+ before :each do
+ @a = REXML::Element.new "a"
+ @b = REXML::Element.new "b"
+ @c = REXML::Element.new "c"
+ @a.root << @b
+ @a.root << @c
+ end
+
+ it "returns previous element" do
+ @a.elements["c"].previous_element.should == @b
+ end
+
+ it "returns nil on first element" do
+ @a.elements["b"].previous_element.should == nil
+ end
+end
diff --git a/spec/ruby/library/rexml/element/raw_spec.rb b/spec/ruby/library/rexml/element/raw_spec.rb
new file mode 100644
index 0000000000..404ccce5f4
--- /dev/null
+++ b/spec/ruby/library/rexml/element/raw_spec.rb
@@ -0,0 +1,24 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#raw" do
+ it "returns true if raw mode is set to all" do
+ REXML::Element.new("MyElem", nil, {raw: :all}).raw.should == true
+ end
+
+ it "returns true if raw mode is set to expanded_name" do
+ REXML::Element.new("MyElem", nil, {raw: "MyElem"}).raw.should == true
+ end
+
+ it "returns false if raw mode is not set" do
+ REXML::Element.new("MyElem", nil, {raw: ""}).raw.should == false
+ end
+
+ it "returns false if raw is not :all or expanded_name" do
+ REXML::Element.new("MyElem", nil, {raw: "Something"}).raw.should == false
+ end
+
+ it "returns nil if context is not set" do
+ REXML::Element.new("MyElem").raw.should == nil
+ end
+end
diff --git a/spec/ruby/library/rexml/element/root_spec.rb b/spec/ruby/library/rexml/element/root_spec.rb
new file mode 100644
index 0000000000..1e0669033e
--- /dev/null
+++ b/spec/ruby/library/rexml/element/root_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Element#root" do
+ before :each do
+ @doc = REXML::Document.new
+ @root = REXML::Element.new "root"
+ @node = REXML::Element.new "node"
+ @doc << @root << @node
+ end
+
+ it "returns first child on documents" do
+ @doc.root.should == @root
+ end
+
+ it "returns self on root nodes" do
+ @root.root.should == @root
+ end
+
+ it "returns parent's root on child nodes" do
+ @node.root.should == @root
+ end
+
+ it "returns self on standalone nodes" do
+ e = REXML::Element.new "Elem" # Note that it doesn't have a parent node
+ e.root.should == e
+ end
+end
diff --git a/spec/ruby/library/rexml/element/text_spec.rb b/spec/ruby/library/rexml/element/text_spec.rb
new file mode 100644
index 0000000000..7c290c4cda
--- /dev/null
+++ b/spec/ruby/library/rexml/element/text_spec.rb
@@ -0,0 +1,46 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#text" do
+ before :each do
+ @e = REXML::Element.new "name"
+ @e.text = "John"
+ end
+
+ it "returns the text node of element" do
+ @e.text.should == "John"
+ end
+
+ it "returns the text node value" do
+ t = REXML::Text.new "Joe"
+ @e.text = t
+ @e.text.should == "Joe"
+ @e.text.should_not == t
+ end
+
+ it "returns nil if no text is attached" do
+ elem = REXML::Element.new "name"
+ elem.text.should == nil
+ end
+end
+
+describe "REXML::Element#text=" do
+ before :each do
+ @e = REXML::Element.new "name"
+ @e.text = "John"
+ end
+
+ it "sets the text node" do
+ @e.to_s.should == "<name>John</name>"
+ end
+
+ it "replaces existing text" do
+ @e.text = "Joe"
+ @e.to_s.should == "<name>Joe</name>"
+ end
+
+ it "receives nil as an argument" do
+ @e.text = nil
+ @e.to_s.should == "<name/>"
+ end
+end
diff --git a/spec/ruby/library/rexml/element/texts_spec.rb b/spec/ruby/library/rexml/element/texts_spec.rb
new file mode 100644
index 0000000000..7975833c89
--- /dev/null
+++ b/spec/ruby/library/rexml/element/texts_spec.rb
@@ -0,0 +1,16 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#texts" do
+
+ it "returns an array of the Text children" do
+ e = REXML::Element.new("root")
+ e.add_text "First"
+ e.add_text "Second"
+ e.texts.should == ["FirstSecond"]
+ end
+
+ it "returns an empty array if it has no Text children" do
+ REXML::Element.new("root").texts.should == []
+ end
+end
diff --git a/spec/ruby/library/rexml/element/whitespace_spec.rb b/spec/ruby/library/rexml/element/whitespace_spec.rb
new file mode 100644
index 0000000000..dc785ae5ce
--- /dev/null
+++ b/spec/ruby/library/rexml/element/whitespace_spec.rb
@@ -0,0 +1,23 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe "REXML::Element#whitespace" do
+ it "returns true if whitespace is respected in the element" do
+ e = REXML::Element.new("root")
+ e.whitespace.should be_true
+
+ e = REXML::Element.new("root", nil, respect_whitespace: :all)
+ e.whitespace.should be_true
+
+ e = REXML::Element.new("root", nil, respect_whitespace: ["root"])
+ e.whitespace.should be_true
+ end
+
+ it "returns false if whitespace is ignored inside element" do
+ e = REXML::Element.new("root", nil, compress_whitespace: :all)
+ e.whitespace.should be_false
+
+ e = REXML::Element.new("root", nil, compress_whitespace: ["root"])
+ e.whitespace.should be_false
+ end
+end
diff --git a/spec/ruby/library/rexml/node/each_recursive_spec.rb b/spec/ruby/library/rexml/node/each_recursive_spec.rb
new file mode 100644
index 0000000000..dd4aa9a2f2
--- /dev/null
+++ b/spec/ruby/library/rexml/node/each_recursive_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Node#each_recursive" do
+ before :each do
+ @doc = REXML::Document.new
+ @doc << REXML::XMLDecl.new
+ @root = REXML::Element.new "root"
+ @child1 = REXML::Element.new "child1"
+ @child2 = REXML::Element.new "child2"
+ @root << @child1
+ @root << @child2
+ @doc << @root
+ end
+
+ it "visits all subnodes of self" do
+ nodes = []
+ @doc.each_recursive { |node| nodes << node}
+ nodes.should == [@root, @child1, @child2]
+ end
+end
diff --git a/spec/ruby/library/rexml/node/find_first_recursive_spec.rb b/spec/ruby/library/rexml/node/find_first_recursive_spec.rb
new file mode 100644
index 0000000000..ba46f2ca35
--- /dev/null
+++ b/spec/ruby/library/rexml/node/find_first_recursive_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Node#find_first_recursive" do
+ before :each do
+ @e = REXML::Element.new("root")
+ @node1 = REXML::Element.new("node")
+ @node2 = REXML::Element.new("another node")
+ @subnode = REXML::Element.new("another node")
+ @node1 << @subnode
+ @e << @node1
+ @e << @node2
+ end
+
+ it "finds the first element that matches block" do
+ found = @e.find_first_recursive { |n| n.to_s == "<node><another node/></node>"}
+ found.should == @node1
+ end
+
+ it "visits the nodes in preorder" do
+ found = @e.find_first_recursive { |n| n.to_s == "<another node/>"}
+ found.should == @subnode
+ found.should_not == @node2
+ end
+end
diff --git a/spec/ruby/library/rexml/node/index_in_parent_spec.rb b/spec/ruby/library/rexml/node/index_in_parent_spec.rb
new file mode 100644
index 0000000000..092851e3e7
--- /dev/null
+++ b/spec/ruby/library/rexml/node/index_in_parent_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Node#index_in_parent" do
+ it "returns the index (starting from 1) of self in parent" do
+ e = REXML::Element.new("root")
+ node1 = REXML::Element.new("node")
+ node2 = REXML::Element.new("another node")
+ e << node1
+ e << node2
+
+ node1.index_in_parent.should == 1
+ node2.index_in_parent.should == 2
+ end
+end
diff --git a/spec/ruby/library/rexml/node/next_sibling_node_spec.rb b/spec/ruby/library/rexml/node/next_sibling_node_spec.rb
new file mode 100644
index 0000000000..2e8601627d
--- /dev/null
+++ b/spec/ruby/library/rexml/node/next_sibling_node_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Node#next_sibling_node" do
+ before :each do
+ @e = REXML::Element.new("root")
+ @node1 = REXML::Element.new("node")
+ @node2 = REXML::Element.new("another node")
+ @e << @node1
+ @e << @node2
+ end
+
+ it "returns the next child node in parent" do
+ @node1.next_sibling_node.should == @node2
+ end
+
+ it "returns nil if there are no more child nodes next" do
+ @node2.next_sibling_node.should == nil
+ @e.next_sibling_node.should == nil
+ end
+end
diff --git a/spec/ruby/library/rexml/node/parent_spec.rb b/spec/ruby/library/rexml/node/parent_spec.rb
new file mode 100644
index 0000000000..d88ba69657
--- /dev/null
+++ b/spec/ruby/library/rexml/node/parent_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Node#parent?" do
+ it "returns true for Elements" do
+ e = REXML::Element.new("foo")
+ e.parent?.should == true
+ end
+
+ it "returns true for Documents" do
+ e = REXML::Document.new
+ e.parent?.should == true
+ end
+
+ # This includes attributes, CDatas and declarations.
+ it "returns false for Texts" do
+ e = REXML::Text.new("foo")
+ e.parent?.should == false
+ end
+end
diff --git a/spec/ruby/library/rexml/node/previous_sibling_node_spec.rb b/spec/ruby/library/rexml/node/previous_sibling_node_spec.rb
new file mode 100644
index 0000000000..8b96f1565a
--- /dev/null
+++ b/spec/ruby/library/rexml/node/previous_sibling_node_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Node#previous_sibling_node" do
+ before :each do
+ @e = REXML::Element.new("root")
+ @node1 = REXML::Element.new("node")
+ @node2 = REXML::Element.new("another node")
+ @e << @node1
+ @e << @node2
+ end
+
+ it "returns the previous child node in parent" do
+ @node2.previous_sibling_node.should == @node1
+ end
+
+ it "returns nil if there are no more child nodes before" do
+ @node1.previous_sibling_node.should == nil
+ @e.previous_sibling_node.should == nil
+ end
+end
diff --git a/spec/ruby/library/rexml/shared/each_element.rb b/spec/ruby/library/rexml/shared/each_element.rb
new file mode 100644
index 0000000000..2e0871161d
--- /dev/null
+++ b/spec/ruby/library/rexml/shared/each_element.rb
@@ -0,0 +1,36 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe :rexml_each_element, shared: true do
+ before :each do
+ @e = REXML::Element.new "root"
+ s1 = REXML::Element.new "node1"
+ s2 = REXML::Element.new "node2"
+ s3 = REXML::Element.new "node3"
+ s4 = REXML::Element.new "sub_node"
+ @e << s1
+ @e << s2
+ @e << s3
+ @e << s4
+ end
+
+ it "iterates through element" do
+ str = ""
+ @e.send(@method) { |elem| str << elem.name << " " }
+ str.should == "node1 node2 node3 sub_node "
+ end
+
+ it "iterates through element filtering with XPath" do
+ str = ""
+ @e.send(@method, "/*"){ |e| str << e.name << " "}
+ str.should == "node1 node2 node3 sub_node "
+ end
+end
+
+describe "REXML::Element#each_element" do
+ it_behaves_like :rexml_each_element, :each_element
+end
+
+describe "REXML::Elements#each" do
+ it_behaves_like :rexml_each_element, :each
+end
diff --git a/spec/ruby/library/rexml/shared/elements_to_a.rb b/spec/ruby/library/rexml/shared/elements_to_a.rb
new file mode 100644
index 0000000000..388250d8b3
--- /dev/null
+++ b/spec/ruby/library/rexml/shared/elements_to_a.rb
@@ -0,0 +1,34 @@
+require 'rexml/document'
+require_relative '../../../spec_helper'
+
+describe :rexml_elements_to_a, shared: true do
+ before :each do
+ @e = REXML::Element.new "root"
+ @first = REXML::Element.new("FirstChild")
+ @second = REXML::Element.new("SecondChild")
+ @e << @first
+ @e << @second
+ end
+
+ it "returns elements that match xpath" do
+ @e.elements.send(@method, "FirstChild").first.should == @first
+ end
+
+ # According to the docs REXML::Element#get_elements is an alias for
+ # REXML::Elements.to_a. Implementation wise there's a difference, get_elements
+ # always needs the first param (even if it's nil).
+ # A patch was submitted:
+ # http://rubyforge.org/tracker/index.php?func=detail&aid=19354&group_id=426&atid=1698
+ it "returns all childs if xpath is nil" do
+ @e.elements.send(@method).should == [@first, @second]
+ end
+
+end
+
+describe "REXML::REXML::Elements#to_a" do
+ it_behaves_like :rexml_elements_to_a, :to_a
+end
+
+describe "REXML::REXML::Element#get_elements" do
+ it_behaves_like :rexml_elements_to_a, :get_elements
+end
diff --git a/spec/ruby/library/rexml/text/append_spec.rb b/spec/ruby/library/rexml/text/append_spec.rb
new file mode 100644
index 0000000000..de281fb0b0
--- /dev/null
+++ b/spec/ruby/library/rexml/text/append_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Text#<<" do
+ it "appends a string to this text node" do
+ text = REXML::Text.new("foo")
+ text << "bar"
+ text.should == "foobar"
+ end
+end
diff --git a/spec/ruby/library/rexml/text/clone_spec.rb b/spec/ruby/library/rexml/text/clone_spec.rb
new file mode 100644
index 0000000000..8031e140c7
--- /dev/null
+++ b/spec/ruby/library/rexml/text/clone_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Text#clone" do
+ it "creates a copy of this node" do
+ text = REXML::Text.new("foo")
+ text.clone.should == "foo"
+ text.clone.should == text
+ end
+end
diff --git a/spec/ruby/library/rexml/text/comparison_spec.rb b/spec/ruby/library/rexml/text/comparison_spec.rb
new file mode 100644
index 0000000000..8bc5d66a03
--- /dev/null
+++ b/spec/ruby/library/rexml/text/comparison_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Text#<=>" do
+ before :each do
+ @first = REXML::Text.new("abc")
+ @last = REXML::Text.new("def")
+ end
+
+ it "returns -1 if lvalue is less than rvalue" do
+ val = @first <=> @last
+ val.should == -1
+ end
+
+ it "returns -1 if lvalue is greater than rvalue" do
+ val = @last <=> @first
+ val.should == 1
+ end
+
+ it "returns 0 if both values are equal" do
+ tmp = REXML::Text.new("tmp")
+ val = tmp <=> tmp
+ val.should == 0
+ end
+end
diff --git a/spec/ruby/library/rexml/text/empty_spec.rb b/spec/ruby/library/rexml/text/empty_spec.rb
new file mode 100644
index 0000000000..d0b66b7a2a
--- /dev/null
+++ b/spec/ruby/library/rexml/text/empty_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Text#empty?" do
+ it "returns true if the text is empty" do
+ REXML::Text.new("").empty?.should == true
+ end
+
+ it "returns false if the text is not empty" do
+ REXML::Text.new("some_text").empty?.should == false
+ end
+end
diff --git a/spec/ruby/library/rexml/text/indent_text_spec.rb b/spec/ruby/library/rexml/text/indent_text_spec.rb
new file mode 100644
index 0000000000..1b0ee5ab16
--- /dev/null
+++ b/spec/ruby/library/rexml/text/indent_text_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Text#indent_text" do
+ before :each do
+ @t = REXML::Text.new("")
+ end
+ it "indents a string with default parameters" do
+ @t.indent_text("foo").should == "\tfoo"
+ end
+
+ it "accepts a custom indentation level as second argument" do
+ @t.indent_text("foo", 2, "\t", true).should == "\t\tfoo"
+ end
+
+ it "accepts a custom separator as third argument" do
+ @t.indent_text("foo", 1, "\n", true).should == "\nfoo"
+ end
+
+ it "accepts a fourth parameter to skip the first line" do
+ @t.indent_text("foo", 1, "\t", false).should == "foo"
+ end
+end
diff --git a/spec/ruby/library/rexml/text/inspect_spec.rb b/spec/ruby/library/rexml/text/inspect_spec.rb
new file mode 100644
index 0000000000..0d66088a64
--- /dev/null
+++ b/spec/ruby/library/rexml/text/inspect_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Text#inspect" do
+ it "inspects the string attribute as a string" do
+ REXML::Text.new("a text").inspect.should == "a text".inspect
+ end
+end
diff --git a/spec/ruby/library/rexml/text/new_spec.rb b/spec/ruby/library/rexml/text/new_spec.rb
new file mode 100644
index 0000000000..a1fdcddf28
--- /dev/null
+++ b/spec/ruby/library/rexml/text/new_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Text.new" do
+
+ it "creates a Text child node with no parent" do
+ t = REXML::Text.new("test")
+ t.should be_kind_of(REXML::Child)
+ t.should == "test"
+ t.parent.should == nil
+ end
+
+ it "respects whitespace if second argument is true" do
+ t = REXML::Text.new("testing whitespace", true)
+ t.should == "testing whitespace"
+ t = REXML::Text.new(" ", true)
+ t.should == " "
+ end
+
+ it "receives a parent as third argument" do
+ e = REXML::Element.new("root")
+ t = REXML::Text.new("test", false, e)
+ t.parent.should == e
+ e.to_s.should == "<root>test</root>"
+ end
+
+ it "expects escaped text if raw is true" do
+ t = REXML::Text.new("&lt;&amp;&gt;", false, nil, true)
+ t.should == "&lt;&amp;&gt;"
+
+ lambda{ REXML::Text.new("<&>", false, nil, true)}.should raise_error(Exception)
+ end
+
+ it "uses raw value of the parent if raw is nil" do
+ e1 = REXML::Element.new("root", nil, { raw: :all})
+ lambda {REXML::Text.new("<&>", false, e1)}.should raise_error(Exception)
+
+ e2 = REXML::Element.new("root", nil, { raw: []})
+ e2.raw.should be_false
+ t1 = REXML::Text.new("<&>", false, e2)
+ t1.should == "&lt;&amp;&gt;"
+ end
+
+ it "escapes the values if raw is false" do
+ t = REXML::Text.new("<&>", false, nil, false)
+ t.should == "&lt;&amp;&gt;"
+ end
+end
diff --git a/spec/ruby/library/rexml/text/node_type_spec.rb b/spec/ruby/library/rexml/text/node_type_spec.rb
new file mode 100644
index 0000000000..1c25a74dad
--- /dev/null
+++ b/spec/ruby/library/rexml/text/node_type_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Text#node_type" do
+ it "returns :text" do
+ REXML::Text.new("test").node_type.should == :text
+ end
+end
diff --git a/spec/ruby/library/rexml/text/normalize_spec.rb b/spec/ruby/library/rexml/text/normalize_spec.rb
new file mode 100644
index 0000000000..ce3b2b3b5f
--- /dev/null
+++ b/spec/ruby/library/rexml/text/normalize_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Text.normalize" do
+ it "escapes a string with <, >, &, ' and \" " do
+ REXML::Text.normalize("< > & \" '").should == "&lt; &gt; &amp; &quot; &apos;"
+ end
+end
diff --git a/spec/ruby/library/rexml/text/read_with_substitution_spec.rb b/spec/ruby/library/rexml/text/read_with_substitution_spec.rb
new file mode 100644
index 0000000000..a9b4d30bc5
--- /dev/null
+++ b/spec/ruby/library/rexml/text/read_with_substitution_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Text.read_with_substitution" do
+ it "reads a text and escapes entities" do
+ REXML::Text.read_with_substitution("&lt; &gt; &amp; &quot; &apos;").should == "< > & \" '"
+ end
+
+ it "accepts an regex for invalid expressions and raises an error if text matches" do
+ lambda {REXML::Text.read_with_substitution("this is illegal", /illegal/)}.should raise_error(Exception)
+ end
+end
diff --git a/spec/ruby/library/rexml/text/to_s_spec.rb b/spec/ruby/library/rexml/text/to_s_spec.rb
new file mode 100644
index 0000000000..14d7399a60
--- /dev/null
+++ b/spec/ruby/library/rexml/text/to_s_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Text#to_s" do
+ it "returns the string of this Text node" do
+ u = REXML::Text.new("sean russell", false, nil, true)
+ u.to_s.should == "sean russell"
+
+ t = REXML::Text.new("some test text")
+ t.to_s.should == "some test text"
+ end
+
+ it "escapes the text" do
+ t = REXML::Text.new("& < >")
+ t.to_s.should == "&amp; &lt; &gt;"
+ end
+end
diff --git a/spec/ruby/library/rexml/text/unnormalize_spec.rb b/spec/ruby/library/rexml/text/unnormalize_spec.rb
new file mode 100644
index 0000000000..3072809c13
--- /dev/null
+++ b/spec/ruby/library/rexml/text/unnormalize_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Text.unnormalize" do
+ it "unescapes a string with the values defined in SETUTITSBUS" do
+ REXML::Text.unnormalize("&lt; &gt; &amp; &quot; &apos;").should == "< > & \" '"
+ end
+end
diff --git a/spec/ruby/library/rexml/text/value_spec.rb b/spec/ruby/library/rexml/text/value_spec.rb
new file mode 100644
index 0000000000..b0545b3cbd
--- /dev/null
+++ b/spec/ruby/library/rexml/text/value_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Text#value" do
+ it "returns the text value of this node" do
+ REXML::Text.new("test").value.should == "test"
+ end
+
+ it "does not escape entities" do
+ REXML::Text.new("& \"").value.should == "& \""
+ end
+
+ it "follows the respect_whitespace attribute" do
+ REXML::Text.new("test bar", false).value.should == "test bar"
+ REXML::Text.new("test bar", true).value.should == "test bar"
+ end
+
+ it "ignores the raw attribute" do
+ REXML::Text.new("sean russell", false, nil, true).value.should == "sean russell"
+ end
+end
+
+describe "REXML::Text#value=" do
+ before :each do
+ @t = REXML::Text.new("new")
+ end
+
+ it "sets the text of the node" do
+ @t.value = "another text"
+ @t.to_s.should == "another text"
+ end
+
+ it "escapes entities" do
+ @t.value = "<a>"
+ @t.to_s.should == "&lt;a&gt;"
+ end
+end
diff --git a/spec/ruby/library/rexml/text/wrap_spec.rb b/spec/ruby/library/rexml/text/wrap_spec.rb
new file mode 100644
index 0000000000..0b60fd4151
--- /dev/null
+++ b/spec/ruby/library/rexml/text/wrap_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Text#wrap" do
+ before :each do
+ @t = REXML::Text.new("abc def")
+ end
+
+ it "wraps the text at width" do
+ @t.wrap("abc def", 3, false).should == "abc\ndef"
+ end
+
+ it "returns the string if width is greater than the size of the string" do
+ @t.wrap("abc def", 10, false).should == "abc def"
+ end
+
+ it "takes a newline at the beginning option as the third parameter" do
+ @t.wrap("abc def", 3, true).should == "\nabc\ndef"
+ end
+end
diff --git a/spec/ruby/library/rexml/text/write_with_substitution_spec.rb b/spec/ruby/library/rexml/text/write_with_substitution_spec.rb
new file mode 100644
index 0000000000..ee79489d86
--- /dev/null
+++ b/spec/ruby/library/rexml/text/write_with_substitution_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../../spec_helper'
+require 'rexml/document'
+
+describe "REXML::Text#write_with_substitution" do
+ before :each do
+ @t = REXML::Text.new("test")
+ @f = tmp("rexml_spec")
+ @file = File.open(@f, "w+")
+ end
+
+ after :each do
+ @file.close
+ rm_r @f
+ end
+
+ it "writes out the input to a String" do
+ s = ""
+ @t.write_with_substitution(s, "some text")
+ s.should == "some text"
+ end
+
+ it "writes out the input to an IO" do
+ @t.write_with_substitution(@file, "some text")
+ @file.rewind
+ @file.gets.should == "some text"
+ end
+
+ it "escapes characters" do
+ @t.write_with_substitution(@file, "& < >")
+ @file.rewind
+ @file.gets.should == "&amp; &lt; &gt;"
+ end
+end
diff --git a/spec/ruby/library/scanf/io/block_scanf_spec.rb b/spec/ruby/library/scanf/io/block_scanf_spec.rb
new file mode 100644
index 0000000000..2c1e242cf6
--- /dev/null
+++ b/spec/ruby/library/scanf/io/block_scanf_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/block_scanf'
+require 'scanf'
+
+describe "IO#block_scanf" do
+ it_behaves_like :scanf_io_block_scanf, :block_scanf
+end
diff --git a/spec/ruby/library/scanf/io/fixtures/date.txt b/spec/ruby/library/scanf/io/fixtures/date.txt
new file mode 100644
index 0000000000..a1bd635c0c
--- /dev/null
+++ b/spec/ruby/library/scanf/io/fixtures/date.txt
@@ -0,0 +1,4 @@
+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
new file mode 100644
index 0000000000..3b18e512db
--- /dev/null
+++ b/spec/ruby/library/scanf/io/fixtures/helloworld.txt
@@ -0,0 +1 @@
+hello world
diff --git a/spec/ruby/library/scanf/io/scanf_spec.rb b/spec/ruby/library/scanf/io/scanf_spec.rb
new file mode 100644
index 0000000000..94e999335e
--- /dev/null
+++ b/spec/ruby/library/scanf/io/scanf_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/block_scanf'
+require 'scanf'
+
+describe "IO#scanf" do
+ before :each do
+ @hw = File.open(File.dirname(__FILE__) + '/fixtures/helloworld.txt', 'r')
+ @data = File.open(File.dirname(__FILE__) + '/fixtures/date.txt', 'r')
+ end
+
+ after :each do
+ @hw.close unless @hw.closed?
+ @data.close unless @data.closed?
+ end
+
+ it "returns an array containing the input converted in the specified type" do
+ @hw.scanf("%s%s").should == ["hello", "world"]
+ @data.scanf("%s%d").should == ["Beethoven", 1770]
+ end
+
+ it "returns an array containing the input converted in the specified type with given maximum field width" do
+ @hw.scanf("%2s").should == ["he"]
+ @data.scanf("%2c").should == ["Be"]
+ end
+
+ it "returns an empty array when a wrong specifier is passed" do
+ @hw.scanf("%a").should == []
+ @hw.scanf("%1").should == []
+ @data.scanf("abc").should == []
+ end
+end
+
+describe "IO#scanf with block" do
+ it_behaves_like :scanf_io_block_scanf, :scanf
+end
diff --git a/spec/ruby/library/scanf/io/shared/block_scanf.rb b/spec/ruby/library/scanf/io/shared/block_scanf.rb
new file mode 100644
index 0000000000..8c5bffb93b
--- /dev/null
+++ b/spec/ruby/library/scanf/io/shared/block_scanf.rb
@@ -0,0 +1,28 @@
+require 'scanf'
+
+describe :scanf_io_block_scanf, shared: true do
+ before :each do
+ @data= File.open(File.dirname(__FILE__) + '/../fixtures/date.txt', 'r')
+ 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
new file mode 100644
index 0000000000..d5c42cb395
--- /dev/null
+++ b/spec/ruby/library/scanf/string/block_scanf_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/block_scanf'
+require 'scanf'
+
+describe "String#block_scanf" do
+ it_behaves_like :scanf_string_block_scanf, :block_scanf
+end
diff --git a/spec/ruby/library/scanf/string/scanf_spec.rb b/spec/ruby/library/scanf/string/scanf_spec.rb
new file mode 100644
index 0000000000..fbe2590476
--- /dev/null
+++ b/spec/ruby/library/scanf/string/scanf_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/block_scanf'
+require 'scanf'
+
+describe "String#scanf" do
+ it "returns an array containing the input converted in the specified type" do
+ "hello world".scanf("%s").should == ["hello"]
+ "hello world".scanf("%s%d").should == ["hello"]
+ "hello world".scanf("%s%c").should == ["hello", " "]
+ "hello world".scanf("%c%s").should == ["h", "ello"]
+ "hello world".scanf("%s%s").should == ["hello", "world"]
+ "hello world".scanf("%c").should == ["h"]
+ "123".scanf("%s").should == ["123"]
+ "123".scanf("%c").should == ["1"]
+ "123".scanf("%d").should == [123]
+ "123".scanf("%u").should == [123]
+ "123".scanf("%o").should == [83]
+ "123".scanf("%x").should == [291]
+ "123".scanf("%i").should == [123]
+ "0123".scanf("%i").should == [83]
+ "123".scanf("%f").should == [123.0]
+ "0X123".scanf("%i").should == [291]
+ "0x123".scanf("%i").should == [291]
+ end
+
+ it "returns an array containing the input converted in the specified type with given maximum field width" do
+ "hello world".scanf("%2s").should == ["he"]
+ "hello world".scanf("%2c").should == ["he"]
+ "123".scanf("%2s").should == ["12"]
+ "123".scanf("%2c").should == ["12"]
+ "123".scanf("%2d").should == [12]
+ "123".scanf("%2u").should == [12]
+ "123".scanf("%2o").should == [10]
+ "123".scanf("%2x").should == [18]
+ "123".scanf("%2i").should == [12]
+ "0123".scanf("%2i").should == [1]
+ "123".scanf("%2f").should == [12.0]
+ "0X123".scanf("%2i").should == [0]
+ "0X123".scanf("%3i").should == [1]
+ "0X123".scanf("%4i").should == [18]
+ end
+
+ it "returns an empty array when a wrong specifier is passed" do
+ "hello world".scanf("%a").should == []
+ "123".scanf("%1").should == []
+ "123".scanf("abc").should == []
+ "123".scanf(:d).should == []
+ end
+end
+
+describe "String#scanf with block" do
+ it_behaves_like :scanf_string_block_scanf, :scanf
+end
diff --git a/spec/ruby/library/scanf/string/shared/block_scanf.rb b/spec/ruby/library/scanf/string/shared/block_scanf.rb
new file mode 100644
index 0000000000..25ab3f442a
--- /dev/null
+++ b/spec/ruby/library/scanf/string/shared/block_scanf.rb
@@ -0,0 +1,25 @@
+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/securerandom/base64_spec.rb b/spec/ruby/library/securerandom/base64_spec.rb
new file mode 100644
index 0000000000..c0ddbf85ce
--- /dev/null
+++ b/spec/ruby/library/securerandom/base64_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../../spec_helper'
+
+require 'securerandom'
+
+describe "SecureRandom.base64" do
+ it "generates a random base64 string out of specified number of random bytes" do
+ (16..128).each do |idx|
+ base64 = SecureRandom.base64(idx)
+ base64.should be_kind_of(String)
+ base64.length.should < 2 * idx
+ base64.should =~ /^[A-Za-z0-9\+\/]+={0,2}$/
+ end
+
+ base64 = SecureRandom.base64(16.5)
+ base64.should be_kind_of(String)
+ base64.length.should < 2 * 16
+ end
+
+ it "returns an empty string when argument is 0" do
+ SecureRandom.base64(0).should == ""
+ end
+
+ it "generates different base64 strings with subsequent invocations" do
+ # quick and dirty check, but good enough
+ values = []
+ 256.times do
+ base64 = SecureRandom.base64
+ # make sure the random values are not repeating
+ values.include?(base64).should == false
+ values << base64
+ end
+ end
+
+ it "generates a random base64 string out of 32 random bytes" do
+ SecureRandom.base64.should be_kind_of(String)
+ SecureRandom.base64.length.should < 32 * 2
+ end
+
+ it "treats nil argument as default one and generates a random base64 string" do
+ SecureRandom.base64(nil).should be_kind_of(String)
+ SecureRandom.base64(nil).length.should < 32 * 2
+ end
+
+ it "raises ArgumentError on negative arguments" do
+ lambda {
+ SecureRandom.base64(-1)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock("to_int")
+ obj.should_receive(:to_int).and_return(5)
+ SecureRandom.base64(obj).size.should < 10
+ end
+end
diff --git a/spec/ruby/library/securerandom/hex_spec.rb b/spec/ruby/library/securerandom/hex_spec.rb
new file mode 100644
index 0000000000..1e647edebc
--- /dev/null
+++ b/spec/ruby/library/securerandom/hex_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+
+require 'securerandom'
+
+describe "SecureRandom.hex" do
+ it "generates a random hex string of length twice the specified argement" do
+ (1..64).each do |idx|
+ hex = SecureRandom.hex(idx)
+ hex.should be_kind_of(String)
+ hex.length.should == 2 * idx
+ end
+
+ base64 = SecureRandom.hex(5.5)
+ base64.should be_kind_of(String)
+ base64.length.should eql(10)
+ end
+
+ it "returns an empty string when argument is 0" do
+ SecureRandom.hex(0).should == ""
+ end
+
+ it "generates different hex strings with subsequent invocations" do
+ # quick and dirty check, but good enough
+ values = []
+ 256.times do
+ hex = SecureRandom.hex
+ # make sure the random values are not repeating
+ values.include?(hex).should == false
+ values << hex
+ end
+ end
+
+ it "generates a random hex string of length 32 if no argument is provided" do
+ SecureRandom.hex.should be_kind_of(String)
+ SecureRandom.hex.length.should == 32
+ end
+
+ it "treats nil argument as default one and generates a random hex string of length 32" do
+ SecureRandom.hex(nil).should be_kind_of(String)
+ SecureRandom.hex(nil).length.should == 32
+ end
+
+ it "raises ArgumentError on negative arguments" do
+ lambda {
+ SecureRandom.hex(-1)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock("to_int")
+ obj.should_receive(:to_int).and_return(5)
+ SecureRandom.hex(obj).size.should eql(10)
+ end
+end
diff --git a/spec/ruby/library/securerandom/random_bytes_spec.rb b/spec/ruby/library/securerandom/random_bytes_spec.rb
new file mode 100644
index 0000000000..7ab949f5d4
--- /dev/null
+++ b/spec/ruby/library/securerandom/random_bytes_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+
+require 'securerandom'
+
+describe "SecureRandom.random_bytes" do
+ it "generates a random binary string of length 16 if no argument is provided" do
+ bytes = SecureRandom.random_bytes
+ bytes.should be_kind_of(String)
+ bytes.length.should == 16
+ end
+
+ it "generates a random binary string of length 16 if argument is nil" do
+ bytes = SecureRandom.random_bytes(nil)
+ bytes.should be_kind_of(String)
+ bytes.length.should == 16
+ end
+
+ it "generates a random binary string of specified length" do
+ (1..64).each do |idx|
+ bytes = SecureRandom.random_bytes(idx)
+ bytes.should be_kind_of(String)
+ bytes.length.should == idx
+ end
+
+ SecureRandom.random_bytes(2.2).length.should eql(2)
+ end
+
+ it "generates different binary strings with subsequent invocations" do
+ # quick and dirty check, but good enough
+ values = []
+ 256.times do
+ val = SecureRandom.random_bytes
+ # make sure the random bytes are not repeating
+ values.include?(val).should == false
+ values << val
+ end
+ end
+
+ it "raises ArgumentError on negative arguments" do
+ lambda {
+ SecureRandom.random_bytes(-1)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock("to_int")
+ obj.should_receive(:to_int).and_return(5)
+ SecureRandom.random_bytes(obj).size.should eql(5)
+ end
+end
diff --git a/spec/ruby/library/securerandom/random_number_spec.rb b/spec/ruby/library/securerandom/random_number_spec.rb
new file mode 100644
index 0000000000..6b3279e6d7
--- /dev/null
+++ b/spec/ruby/library/securerandom/random_number_spec.rb
@@ -0,0 +1,93 @@
+require_relative '../../spec_helper'
+
+require 'securerandom'
+
+describe "SecureRandom.random_number" do
+ it "generates a random positive number smaller then the positive integer argument" do
+ (1..64).each do |idx|
+ num = SecureRandom.random_number(idx)
+ num.should be_kind_of(Fixnum)
+ (0 <= num).should == true
+ (num < idx).should == true
+ end
+ end
+
+ it "generates a random (potentially bignum) integer value for bignum argument" do
+ max = 12345678901234567890
+ 11.times do
+ num = SecureRandom.random_number max
+ num.should be_kind_of(Integer)
+ (0 <= num).should == true
+ (num < max).should == true
+ end
+ end
+
+ it "generates a random float number between 0.0 and 1.0 if no argument provided" do
+ 64.times do
+ num = SecureRandom.random_number
+ num.should be_kind_of(Float)
+ (0.0 <= num).should == true
+ (num < 1.0).should == true
+ end
+ end
+
+ it "generates a random value in given (integer) range limits" do
+ 64.times do
+ num = SecureRandom.random_number 11...13
+ num.should be_kind_of(Integer)
+ (11 <= num).should == true
+ (num < 13).should == true
+ end
+ end
+
+ it "generates a random value in given big (integer) range limits" do
+ lower = 12345678901234567890
+ upper = 12345678901234567890 + 5
+ 32.times do
+ num = SecureRandom.random_number lower..upper
+ num.should be_kind_of(Integer)
+ (lower <= num).should == true
+ (num <= upper).should == true
+ end
+ end
+
+ it "generates a random value in given (float) range limits" do
+ 64.times do
+ num = SecureRandom.random_number 0.6..0.9
+ num.should be_kind_of(Float)
+ (0.6 <= num).should == true
+ (num <= 0.9).should == true
+ end
+ end
+
+ it "generates a random float number between 0.0 and 1.0 if argument is negative" do
+ num = SecureRandom.random_number(-10)
+ num.should be_kind_of(Float)
+ (0.0 <= num).should == true
+ (num < 1.0).should == true
+ end
+
+ it "generates a random float number between 0.0 and 1.0 if argument is negative float" do
+ num = SecureRandom.random_number(-11.1)
+ num.should be_kind_of(Float)
+ (0.0 <= num).should == true
+ (num < 1.0).should == true
+ end
+
+ it "generates different float numbers with subsequent invocations" do
+ # quick and dirty check, but good enough
+ values = []
+ 256.times do
+ val = SecureRandom.random_number
+ # make sure the random values are not repeating
+ values.include?(val).should == false
+ values << val
+ end
+ end
+
+ it "raises ArgumentError if the argument is non-numeric" do
+ lambda {
+ SecureRandom.random_number(Object.new)
+ }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/add_spec.rb b/spec/ruby/library/set/add_spec.rb
new file mode 100644
index 0000000000..68356cc111
--- /dev/null
+++ b/spec/ruby/library/set/add_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require 'set'
+require_relative 'shared/add'
+
+describe "Set#add" do
+ it_behaves_like :set_add, :add
+end
+
+describe "Set#add?" do
+ before :each do
+ @set = Set.new
+ end
+
+ it "adds the passed Object to self" do
+ @set.add?("cat")
+ @set.should include("cat")
+ end
+
+ it "returns self when the Object has not yet been added to self" do
+ @set.add?("cat").should equal(@set)
+ end
+
+ it "returns nil when the Object has already been added to self" do
+ @set.add?("cat")
+ @set.add?("cat").should be_nil
+ end
+end
diff --git a/spec/ruby/library/set/append_spec.rb b/spec/ruby/library/set/append_spec.rb
new file mode 100644
index 0000000000..8b3498b779
--- /dev/null
+++ b/spec/ruby/library/set/append_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require 'set'
+require_relative 'shared/add'
+
+describe "Set#<<" do
+ it_behaves_like :set_add, :<<
+end
diff --git a/spec/ruby/library/set/case_compare_spec.rb b/spec/ruby/library/set/case_compare_spec.rb
new file mode 100644
index 0000000000..193006dbda
--- /dev/null
+++ b/spec/ruby/library/set/case_compare_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require_relative 'shared/include'
+require 'set'
+
+ruby_version_is "2.5" do
+ describe "Set#===" do
+ it_behaves_like :set_include, :===
+
+ it "is an alias for include?" do
+ set = Set.new
+ set.method(:===).should == set.method(:include?)
+ end
+ end
+end
diff --git a/spec/ruby/library/set/case_equality_spec.rb b/spec/ruby/library/set/case_equality_spec.rb
new file mode 100644
index 0000000000..875630612e
--- /dev/null
+++ b/spec/ruby/library/set/case_equality_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'shared/include'
+require 'set'
+
+ruby_version_is "2.5" do
+ describe "Set#===" do
+ it_behaves_like :set_include, :===
+ end
+end
diff --git a/spec/ruby/library/set/classify_spec.rb b/spec/ruby/library/set/classify_spec.rb
new file mode 100644
index 0000000000..ec600c91d6
--- /dev/null
+++ b/spec/ruby/library/set/classify_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#classify" do
+ before :each do
+ @set = Set["one", "two", "three", "four"]
+ end
+
+ it "yields each Object in self" do
+ res = []
+ @set.classify { |x| res << x }
+ res.sort.should == ["one", "two", "three", "four"].sort
+ end
+
+ it "returns an Enumerator when passed no block" do
+ enum = @set.classify
+ enum.should be_an_instance_of(Enumerator)
+
+ classified = enum.each { |x| x.length }
+ classified.should == { 3 => Set["one", "two"], 4 => Set["four"], 5 => Set["three"] }
+ end
+
+ it "classifies the Objects in self based on the block's return value" do
+ classified = @set.classify { |x| x.length }
+ classified.should == { 3 => Set["one", "two"], 4 => Set["four"], 5 => Set["three"] }
+ end
+end
diff --git a/spec/ruby/library/set/clear_spec.rb b/spec/ruby/library/set/clear_spec.rb
new file mode 100644
index 0000000000..2b1c9c5b5a
--- /dev/null
+++ b/spec/ruby/library/set/clear_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#clear" do
+ before :each do
+ @set = Set["one", "two", "three", "four"]
+ end
+
+ it "removes all elements from self" do
+ @set.clear
+ @set.should be_empty
+ end
+
+ it "returns self" do
+ @set.clear.should equal(@set)
+ end
+end
diff --git a/spec/ruby/library/set/collect_spec.rb b/spec/ruby/library/set/collect_spec.rb
new file mode 100644
index 0000000000..f8813a9331
--- /dev/null
+++ b/spec/ruby/library/set/collect_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require 'set'
+require_relative 'shared/collect'
+
+describe "Set#collect!" do
+ it_behaves_like :set_collect_bang, :collect!
+end
diff --git a/spec/ruby/library/set/compare_by_identity_spec.rb b/spec/ruby/library/set/compare_by_identity_spec.rb
new file mode 100644
index 0000000000..363a108935
--- /dev/null
+++ b/spec/ruby/library/set/compare_by_identity_spec.rb
@@ -0,0 +1,147 @@
+require_relative '../../spec_helper'
+require 'set'
+
+ruby_version_is '2.4' do
+ describe "Set#compare_by_identity" do
+ it "compares its members by identity" do
+ a = "a"
+ b1 = "b"
+ b2 = "b"
+
+ set = Set.new
+ set.compare_by_identity
+ set.merge([a, a, b1, b2])
+ set.to_a.sort.should == [a, b1, b2].sort
+ end
+
+ it "causes future comparisons on the receiver to be made by identity" do
+ elt = [1]
+ set = Set.new
+ set << elt
+ set.member?(elt.dup).should be_true
+ set.compare_by_identity
+ set.member?(elt.dup).should be_false
+ end
+
+ it "rehashes internally so that old members can be looked up" do
+ set = Set.new
+ (1..10).each { |k| set << k }
+ o = Object.new
+ def o.hash; 123; end
+ set << o
+ set.compare_by_identity
+ set.member?(o).should be_true
+ end
+
+ it "returns self" do
+ set = Set.new
+ result = set.compare_by_identity
+ result.should equal(set)
+ end
+
+ it "is idempotent and has no effect on an already compare_by_identity set" do
+ set = Set.new.compare_by_identity
+ set << :foo
+ set.compare_by_identity.should equal(set)
+ set.compare_by_identity?.should == true
+ set.to_a.should == [:foo]
+ end
+
+ it "uses the semantics of BasicObject#equal? to determine members identity" do
+ :a.equal?(:a).should == true
+ Set.new.compare_by_identity.merge([:a, :a]).to_a.should == [:a]
+
+ ary1 = [1]
+ ary2 = [1]
+ ary1.equal?(ary2).should == false
+ Set.new.compare_by_identity.merge([ary1, ary2]).to_a.sort.should == [ary1, ary2].sort
+ end
+
+ it "uses #equal? semantics, but doesn't actually call #equal? to determine identity" do
+ set = Set.new.compare_by_identity
+ obj = mock("equal")
+ obj.should_not_receive(:equal?)
+ set << :foo
+ set << obj
+ set.to_a.should == [:foo, obj]
+ end
+
+ it "does not call #hash on members" do
+ elt = mock("element")
+ elt.should_not_receive(:hash)
+ set = Set.new.compare_by_identity
+ set << elt
+ set.member?(elt).should be_true
+ end
+
+ it "regards #dup'd objects as having different identities" do
+ a1 = "a"
+ a2 = a1.dup
+
+ set = Set.new.compare_by_identity
+ set.merge([a1, a2])
+ set.to_a.sort.should == [a1, a2].sort
+ end
+
+ it "regards #clone'd objects as having different identities" do
+ a1 = "a"
+ a2 = a1.clone
+
+ set = Set.new.compare_by_identity
+ set.merge([a1, a2])
+ set.to_a.sort.should == [a1, a2].sort
+ end
+
+ it "raises a #{frozen_error_class} on frozen sets" do
+ set = Set.new.freeze
+ lambda {
+ set.compare_by_identity
+ }.should raise_error(frozen_error_class, /frozen Hash/)
+ end
+
+ it "persists over #dups" do
+ set = Set.new.compare_by_identity
+ set << :a
+ set_dup = set.dup
+ set_dup.should == set
+ set_dup << :a
+ set_dup.to_a.should == [:a]
+ end
+
+ it "persists over #clones" do
+ set = Set.new.compare_by_identity
+ set << :a
+ set_clone = set.clone
+ set_clone.should == set
+ set_clone << :a
+ set_clone.to_a.should == [:a]
+ end
+
+ it "is not equal to set what does not compare by identity" do
+ Set.new([1, 2]).should == Set.new([1, 2])
+ Set.new([1, 2]).should_not == Set.new([1, 2]).compare_by_identity
+ end
+ end
+end
+
+ruby_version_is '2.4' do
+ describe "Set#compare_by_identity?" do
+ it "returns false by default" do
+ Set.new.compare_by_identity?.should == false
+ end
+
+ it "returns true once #compare_by_identity has been invoked on self" do
+ set = Set.new
+ set.compare_by_identity
+ set.compare_by_identity?.should == true
+ end
+
+ it "returns true when called multiple times on the same set" do
+ set = Set.new
+ set.compare_by_identity
+ set.compare_by_identity?.should == true
+ set.compare_by_identity?.should == true
+ set.compare_by_identity?.should == true
+ end
+ end
+end
diff --git a/spec/ruby/library/set/constructor_spec.rb b/spec/ruby/library/set/constructor_spec.rb
new file mode 100644
index 0000000000..bb84861514
--- /dev/null
+++ b/spec/ruby/library/set/constructor_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set[]" do
+ it "returns a new Set populated with the passed Objects" do
+ set = Set[1, 2, 3]
+
+ set.instance_of?(Set).should be_true
+ set.size.should eql(3)
+
+ set.should include(1)
+ set.should include(2)
+ set.should include(3)
+ end
+end
diff --git a/spec/ruby/library/set/delete_if_spec.rb b/spec/ruby/library/set/delete_if_spec.rb
new file mode 100644
index 0000000000..33caeeaab7
--- /dev/null
+++ b/spec/ruby/library/set/delete_if_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#delete_if" do
+ before :each do
+ @set = Set["one", "two", "three"]
+ end
+
+ it "yields every element of self" do
+ ret = []
+ @set.delete_if { |x| ret << x }
+ ret.sort.should == ["one", "two", "three"].sort
+ end
+
+ it "deletes every element from self for which the passed block returns true" do
+ @set.delete_if { |x| x.size == 3 }
+ @set.size.should eql(1)
+
+ @set.should_not include("one")
+ @set.should_not include("two")
+ @set.should include("three")
+ end
+
+ it "returns self" do
+ @set.delete_if { |x| x }.should equal(@set)
+ end
+
+ it "returns an Enumerator when passed no block" do
+ enum = @set.delete_if
+ enum.should be_an_instance_of(Enumerator)
+
+ enum.each { |x| x.size == 3 }
+
+ @set.should_not include("one")
+ @set.should_not include("two")
+ @set.should include("three")
+ end
+end
diff --git a/spec/ruby/library/set/delete_spec.rb b/spec/ruby/library/set/delete_spec.rb
new file mode 100644
index 0000000000..b12524384a
--- /dev/null
+++ b/spec/ruby/library/set/delete_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#delete" do
+ before :each do
+ @set = Set["a", "b", "c"]
+ end
+
+ it "deletes the passed Object from self" do
+ @set.delete("a")
+ @set.should_not include("a")
+ end
+
+ it "returns self" do
+ @set.delete("a").should equal(@set)
+ @set.delete("x").should equal(@set)
+ end
+end
+
+describe "Set#delete?" do
+ before :each do
+ @set = Set["a", "b", "c"]
+ end
+
+ it "deletes the passed Object from self" do
+ @set.delete?("a")
+ @set.should_not include("a")
+ end
+
+ it "returns self when the passed Object is in self" do
+ @set.delete?("a").should equal(@set)
+ end
+
+ it "returns nil when the passed Object is not in self" do
+ @set.delete?("x").should be_nil
+ end
+end
diff --git a/spec/ruby/library/set/difference_spec.rb b/spec/ruby/library/set/difference_spec.rb
new file mode 100644
index 0000000000..422f2ed3c7
--- /dev/null
+++ b/spec/ruby/library/set/difference_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require 'set'
+require_relative 'shared/difference'
+
+describe "Set#difference" do
+ it_behaves_like :set_difference, :difference
+end
diff --git a/spec/ruby/library/set/divide_spec.rb b/spec/ruby/library/set/divide_spec.rb
new file mode 100644
index 0000000000..fdd8cd9622
--- /dev/null
+++ b/spec/ruby/library/set/divide_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#divide" do
+ it "divides self into a set of subsets based on the blocks return values" do
+ set = Set["one", "two", "three", "four", "five"].divide { |x| x.length }
+ set.map { |x| x.to_a.sort }.sort.should == [["five", "four"], ["one", "two"], ["three"]]
+ end
+
+ it "yields each Object to the block" do
+ ret = []
+ Set["one", "two", "three", "four", "five"].divide { |x| ret << x }
+ ret.sort.should == ["five", "four", "one", "three", "two"]
+ end
+
+ # BUG: Does not raise a LocalJumpError, but a NoMethodError
+ #
+ # it "raises a LocalJumpError when not passed a block" do
+ # lambda { Set[1].divide }.should raise_error(LocalJumpError)
+ # end
+end
+
+describe "Set#divide when passed a block with an arity of 2" do
+ it "divides self into a set of subsets based on the blocks return values" do
+ set = Set[1, 3, 4, 6, 9, 10, 11].divide { |x, y| (x - y).abs == 1 }
+ set.map{ |x| x.to_a.sort }.sort.should == [[1], [3, 4], [6], [9, 10, 11]]
+ end
+
+ it "yields each two Object to the block" do
+ ret = []
+ Set[1, 2].divide { |x, y| ret << [x, y] }
+ ret.sort.should == [[1, 1], [1, 2], [2, 1], [2, 2]]
+ end
+end
diff --git a/spec/ruby/library/set/each_spec.rb b/spec/ruby/library/set/each_spec.rb
new file mode 100644
index 0000000000..9bb5ead03a
--- /dev/null
+++ b/spec/ruby/library/set/each_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#each" do
+ before :each do
+ @set = Set[1, 2, 3]
+ end
+
+ it "yields each Object in self" do
+ ret = []
+ @set.each { |x| ret << x }
+ ret.sort.should == [1, 2, 3]
+ end
+
+ it "returns self" do
+ @set.each { |x| x }.should equal(@set)
+ end
+
+ it "returns an Enumerator when not passed a block" do
+ enum = @set.each
+
+ ret = []
+ enum.each { |x| ret << x }
+ ret.sort.should == [1, 2, 3]
+ end
+end
diff --git a/spec/ruby/library/set/empty_spec.rb b/spec/ruby/library/set/empty_spec.rb
new file mode 100644
index 0000000000..1789a664c7
--- /dev/null
+++ b/spec/ruby/library/set/empty_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#empty?" do
+ it "returns true if self is empty" do
+ Set[].empty?.should be_true
+ Set[1].empty?.should be_false
+ Set[1,2,3].empty?.should be_false
+ end
+end
diff --git a/spec/ruby/library/set/enumerable/to_set_spec.rb b/spec/ruby/library/set/enumerable/to_set_spec.rb
new file mode 100644
index 0000000000..6d81710a2f
--- /dev/null
+++ b/spec/ruby/library/set/enumerable/to_set_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "Emumerable#to_set" do
+ it "returns a new Set created from self" do
+ [1, 2, 3].to_set.should == Set[1, 2, 3]
+ {a: 1, b: 2}.to_set.should == Set[[:b, 2], [:a, 1]]
+ end
+
+ it "allows passing an alternate class for Set" do
+ sorted_set = [1, 2, 3].to_set(SortedSet)
+ sorted_set.should == SortedSet[1, 2, 3]
+ sorted_set.instance_of?(SortedSet).should == true
+ end
+
+ it "passes down passed blocks" do
+ [1, 2, 3].to_set { |x| x * x }.should == Set[1, 4, 9]
+ end
+end
diff --git a/spec/ruby/library/set/eql_spec.rb b/spec/ruby/library/set/eql_spec.rb
new file mode 100644
index 0000000000..dd8e633775
--- /dev/null
+++ b/spec/ruby/library/set/eql_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#eql?" do
+ it "returns true when the passed argument is a Set and contains the same elements" do
+ Set[].should eql(Set[])
+ Set[1, 2, 3].should eql(Set[1, 2, 3])
+ Set[1, 2, 3].should eql(Set[3, 2, 1])
+ Set["a", :b, ?c].should eql(Set[?c, :b, "a"])
+
+ Set[1, 2, 3].should_not eql(Set[1.0, 2, 3])
+ Set[1, 2, 3].should_not eql(Set[2, 3])
+ Set[1, 2, 3].should_not eql(Set[])
+ end
+end
diff --git a/spec/ruby/library/set/equal_value_spec.rb b/spec/ruby/library/set/equal_value_spec.rb
new file mode 100644
index 0000000000..10e0efab1b
--- /dev/null
+++ b/spec/ruby/library/set/equal_value_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#==" do
+ it "returns true when the passed Object is a Set and self and the Object contain the same elements" do
+ Set[].should == Set[]
+ Set[1, 2, 3].should == Set[1, 2, 3]
+ Set["1", "2", "3"].should == Set["1", "2", "3"]
+
+ Set[1, 2, 3].should_not == Set[1.0, 2, 3]
+ Set[1, 2, 3].should_not == [1, 2, 3]
+ end
+
+ it "does not depend on the order of the elements" do
+ Set[1, 2, 3].should == Set[3, 2, 1]
+ Set[:a, "b", ?c].should == Set[?c, "b", :a]
+ end
+
+ it "does not depend on the order of nested Sets" do
+ Set[Set[1], Set[2], Set[3]].should == Set[Set[3], Set[2], Set[1]]
+
+ set1 = Set[Set["a", "b"], Set["c", "d"], Set["e", "f"]]
+ set2 = Set[Set["c", "d"], Set["a", "b"], Set["e", "f"]]
+ set1.should == set2
+ end
+end
diff --git a/spec/ruby/library/set/exclusion_spec.rb b/spec/ruby/library/set/exclusion_spec.rb
new file mode 100644
index 0000000000..407aa70032
--- /dev/null
+++ b/spec/ruby/library/set/exclusion_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#^" do
+ before :each do
+ @set = Set[1, 2, 3, 4]
+ end
+
+ it "returns a new Set containing elements that are not in both self and the passed Enumberable" do
+ (@set ^ Set[3, 4, 5]).should == Set[1, 2, 5]
+ (@set ^ [3, 4, 5]).should == Set[1, 2, 5]
+ end
+
+ it "raises an ArgumentError when passed a non-Enumerable" do
+ lambda { @set ^ 3 }.should raise_error(ArgumentError)
+ lambda { @set ^ Object.new }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/filter_spec.rb b/spec/ruby/library/set/filter_spec.rb
new file mode 100644
index 0000000000..a4dfe70d52
--- /dev/null
+++ b/spec/ruby/library/set/filter_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'shared/select'
+
+ruby_version_is "2.6" do
+ describe "Set#filter!" do
+ it_behaves_like :set_select_bang, :filter!
+ end
+end
diff --git a/spec/ruby/library/set/flatten_merge_spec.rb b/spec/ruby/library/set/flatten_merge_spec.rb
new file mode 100644
index 0000000000..b4494b13a7
--- /dev/null
+++ b/spec/ruby/library/set/flatten_merge_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#flatten_merge" do
+ it "is protected" do
+ Set.should have_protected_instance_method("flatten_merge")
+ end
+
+ it "flattens the passed Set and merges it into self" do
+ set1 = Set[1, 2]
+ set2 = Set[3, 4, Set[5, 6]]
+
+ set1.send(:flatten_merge, set2).should == Set[1, 2, 3, 4, 5, 6]
+ end
+
+ it "raises an ArgumentError when trying to flatten a recursive Set" do
+ set1 = Set[1, 2, 3]
+ set2 = Set[5, 6, 7]
+ set2 << set2
+
+ lambda { set1.send(:flatten_merge, set2) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/flatten_spec.rb b/spec/ruby/library/set/flatten_spec.rb
new file mode 100644
index 0000000000..4a037bbd5e
--- /dev/null
+++ b/spec/ruby/library/set/flatten_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#flatten" do
+ it "returns a copy of self with each included Set flattened" do
+ set = Set[1, 2, Set[3, 4, Set[5, 6, Set[7, 8]]], 9, 10]
+ flattened_set = set.flatten
+
+ flattened_set.should_not equal(set)
+ flattened_set.should == Set[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ end
+
+ it "raises an ArgumentError when self is recursive" do
+ (set = Set[]) << set
+ lambda { set.flatten }.should raise_error(ArgumentError)
+ end
+end
+
+describe "Set#flatten!" do
+ it "flattens self" do
+ set = Set[1, 2, Set[3, 4, Set[5, 6, Set[7, 8]]], 9, 10]
+ set.flatten!
+ set.should == Set[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ end
+
+ it "returns self when self was modified" do
+ set = Set[1, 2, Set[3, 4]]
+ set.flatten!.should equal(set)
+ end
+
+ it "returns nil when self was not modified" do
+ set = Set[1, 2, 3, 4]
+ set.flatten!.should be_nil
+ end
+
+ it "raises an ArgumentError when self is recursive" do
+ (set = Set[]) << set
+ lambda { set.flatten! }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/hash_spec.rb b/spec/ruby/library/set/hash_spec.rb
new file mode 100644
index 0000000000..47c43c05f1
--- /dev/null
+++ b/spec/ruby/library/set/hash_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#hash" do
+ it "is static" do
+ Set[].hash.should == Set[].hash
+ Set[1, 2, 3].hash.should == Set[1, 2, 3].hash
+ Set[:a, "b", ?c].hash.should == Set[?c, "b", :a].hash
+
+ Set[].hash.should_not == Set[1, 2, 3].hash
+ Set[1, 2, 3].hash.should_not == Set[:a, "b", ?c].hash
+ end
+end
diff --git a/spec/ruby/library/set/include_spec.rb b/spec/ruby/library/set/include_spec.rb
new file mode 100644
index 0000000000..68532d9a04
--- /dev/null
+++ b/spec/ruby/library/set/include_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/include'
+require 'set'
+
+describe "Set#include?" do
+ it_behaves_like :set_include, :include?
+end
diff --git a/spec/ruby/library/set/initialize_spec.rb b/spec/ruby/library/set/initialize_spec.rb
new file mode 100644
index 0000000000..887cae45fc
--- /dev/null
+++ b/spec/ruby/library/set/initialize_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#initialize" do
+ it "is private" do
+ Set.should have_private_instance_method(:initialize)
+ end
+
+ it "adds all elements of the passed Enumerable to self" do
+ s = Set.new([1, 2, 3])
+ s.size.should eql(3)
+ s.should include(1)
+ s.should include(2)
+ s.should include(3)
+ end
+
+ it "should initialize with empty array and set" do
+ s = Set.new([])
+ s.size.should eql(0)
+
+ s = Set.new({})
+ s.size.should eql(0)
+ end
+
+ it "preprocesses all elements by a passed block before adding to self" do
+ s = Set.new([1, 2, 3]) { |x| x * x }
+ s.size.should eql(3)
+ s.should include(1)
+ s.should include(4)
+ s.should include(9)
+ end
+
+ it "should initialize with empty array and block" do
+ s = Set.new([]) { |x| x * x }
+ s.size.should eql(0)
+ end
+
+ it "should initialize with empty set and block" do
+ s = Set.new(Set.new) { |x| x * x }
+ s.size.should eql(0)
+ end
+
+ it "should initialize with just block" do
+ s = Set.new { |x| x * x }
+ s.size.should eql(0)
+ s.should eql(Set.new)
+ end
+end
diff --git a/spec/ruby/library/set/inspect_spec.rb b/spec/ruby/library/set/inspect_spec.rb
new file mode 100644
index 0000000000..4060c63b95
--- /dev/null
+++ b/spec/ruby/library/set/inspect_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/inspect'
+require 'set'
+
+describe "Set#inspect" do
+ it_behaves_like :set_inspect, :inspect
+end
diff --git a/spec/ruby/library/set/intersection_spec.rb b/spec/ruby/library/set/intersection_spec.rb
new file mode 100644
index 0000000000..792c2d8f07
--- /dev/null
+++ b/spec/ruby/library/set/intersection_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/intersection'
+require 'set'
+
+describe "Set#intersection" do
+ it_behaves_like :set_intersection, :intersection
+end
+
+describe "Set#&" do
+ it_behaves_like :set_intersection, :&
+end
diff --git a/spec/ruby/library/set/keep_if_spec.rb b/spec/ruby/library/set/keep_if_spec.rb
new file mode 100644
index 0000000000..7edc80769f
--- /dev/null
+++ b/spec/ruby/library/set/keep_if_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#keep_if" do
+ before :each do
+ @set = Set["one", "two", "three"]
+ end
+
+ it "yields every element of self" do
+ ret = []
+ @set.keep_if { |x| ret << x }
+ ret.sort.should == ["one", "two", "three"].sort
+ end
+
+ it "keeps every element from self for which the passed block returns true" do
+ @set.keep_if { |x| x.size != 3 }
+ @set.size.should eql(1)
+
+ @set.should_not include("one")
+ @set.should_not include("two")
+ @set.should include("three")
+ end
+
+ it "returns self" do
+ @set.keep_if {}.should equal(@set)
+ end
+
+ it "returns an Enumerator when passed no block" do
+ enum = @set.keep_if
+ enum.should be_an_instance_of(Enumerator)
+
+ enum.each { |x| x.size != 3 }
+
+ @set.should_not include("one")
+ @set.should_not include("two")
+ @set.should include("three")
+ end
+end
diff --git a/spec/ruby/library/set/length_spec.rb b/spec/ruby/library/set/length_spec.rb
new file mode 100644
index 0000000000..fef63d25a7
--- /dev/null
+++ b/spec/ruby/library/set/length_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/length'
+require 'set'
+
+describe "Set#length" do
+ it_behaves_like :set_length, :length
+end
diff --git a/spec/ruby/library/set/map_spec.rb b/spec/ruby/library/set/map_spec.rb
new file mode 100644
index 0000000000..e60e98b179
--- /dev/null
+++ b/spec/ruby/library/set/map_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require 'set'
+require_relative 'shared/collect'
+
+describe "Set#map!" do
+ it_behaves_like :set_collect_bang, :map!
+end
diff --git a/spec/ruby/library/set/member_spec.rb b/spec/ruby/library/set/member_spec.rb
new file mode 100644
index 0000000000..5b56a38ab9
--- /dev/null
+++ b/spec/ruby/library/set/member_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/include'
+require 'set'
+
+describe "Set#member?" do
+ it_behaves_like :set_include, :member?
+end
diff --git a/spec/ruby/library/set/merge_spec.rb b/spec/ruby/library/set/merge_spec.rb
new file mode 100644
index 0000000000..5c65af0a9b
--- /dev/null
+++ b/spec/ruby/library/set/merge_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#merge" do
+ it "adds the elements of the passed Enumerable to self" do
+ Set[:a, :b].merge(Set[:b, :c, :d]).should == Set[:a, :b, :c, :d]
+ Set[1, 2].merge([3, 4]).should == Set[1, 2, 3, 4]
+ end
+
+ it "returns self" do
+ set = Set[1, 2]
+ set.merge([3, 4]).should equal(set)
+ end
+
+ it "raises an ArgumentError when passed a non-Enumerable" do
+ lambda { Set[1, 2].merge(1) }.should raise_error(ArgumentError)
+ lambda { Set[1, 2].merge(Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/minus_spec.rb b/spec/ruby/library/set/minus_spec.rb
new file mode 100644
index 0000000000..3fe0b6a2cc
--- /dev/null
+++ b/spec/ruby/library/set/minus_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require 'set'
+require_relative 'shared/difference'
+
+describe "Set#-" do
+ it_behaves_like :set_difference, :-
+end
diff --git a/spec/ruby/library/set/plus_spec.rb b/spec/ruby/library/set/plus_spec.rb
new file mode 100644
index 0000000000..3e70d3269d
--- /dev/null
+++ b/spec/ruby/library/set/plus_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/union'
+require 'set'
+
+describe "Set#+" do
+ it_behaves_like :set_union, :+
+end
diff --git a/spec/ruby/library/set/pretty_print_cycle_spec.rb b/spec/ruby/library/set/pretty_print_cycle_spec.rb
new file mode 100644
index 0000000000..4f440353e5
--- /dev/null
+++ b/spec/ruby/library/set/pretty_print_cycle_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#pretty_print_cycle" do
+ it "passes the 'pretty print' representation of a self-referencing Set to the pretty print writer" do
+ pp = mock("PrettyPrint")
+ pp.should_receive(:text).with("#<Set: {...}>")
+ Set[1, 2, 3].pretty_print_cycle(pp)
+ end
+end
diff --git a/spec/ruby/library/set/pretty_print_spec.rb b/spec/ruby/library/set/pretty_print_spec.rb
new file mode 100644
index 0000000000..f2392e6968
--- /dev/null
+++ b/spec/ruby/library/set/pretty_print_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#pretty_print" do
+ it "passes the 'pretty print' representation of self to the pretty print writer" do
+ pp = mock("PrettyPrint")
+ set = Set[1, 2, 3]
+
+ pp.should_receive(:text).with("#<Set: {")
+ pp.should_receive(:text).with("}>")
+
+ pp.should_receive(:nest).with(1).and_yield
+ pp.should_receive(:seplist).with(set)
+
+ set.pretty_print(pp)
+ end
+end
diff --git a/spec/ruby/library/set/proper_subset_spec.rb b/spec/ruby/library/set/proper_subset_spec.rb
new file mode 100644
index 0000000000..d925e80a8b
--- /dev/null
+++ b/spec/ruby/library/set/proper_subset_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#proper_subset?" do
+ before :each do
+ @set = Set[1, 2, 3, 4]
+ end
+
+ it "returns true if passed a Set that self is a proper subset of" do
+ Set[].proper_subset?(@set).should be_true
+ Set[].proper_subset?(Set[1, 2, 3]).should be_true
+ Set[].proper_subset?(Set["a", :b, ?c]).should be_true
+
+ Set[1, 2, 3].proper_subset?(@set).should be_true
+ Set[1, 3].proper_subset?(@set).should be_true
+ Set[1, 2].proper_subset?(@set).should be_true
+ Set[1].proper_subset?(@set).should be_true
+
+ Set[5].proper_subset?(@set).should be_false
+ Set[1, 5].proper_subset?(@set).should be_false
+ Set[nil].proper_subset?(@set).should be_false
+ Set["test"].proper_subset?(@set).should be_false
+
+ @set.proper_subset?(@set).should be_false
+ Set[].proper_subset?(Set[]).should be_false
+ end
+
+ it "raises an ArgumentError when passed a non-Set" do
+ lambda { Set[].proper_subset?([]) }.should raise_error(ArgumentError)
+ lambda { Set[].proper_subset?(1) }.should raise_error(ArgumentError)
+ lambda { Set[].proper_subset?("test") }.should raise_error(ArgumentError)
+ lambda { Set[].proper_subset?(Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/proper_superset_spec.rb b/spec/ruby/library/set/proper_superset_spec.rb
new file mode 100644
index 0000000000..cdfd44f32f
--- /dev/null
+++ b/spec/ruby/library/set/proper_superset_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#proper_superset?" do
+ before :each do
+ @set = Set[1, 2, 3, 4]
+ end
+
+ it "returns true if passed a Set that self is a proper superset of" do
+ @set.proper_superset?(Set[]).should be_true
+ Set[1, 2, 3].proper_superset?(Set[]).should be_true
+ Set["a", :b, ?c].proper_superset?(Set[]).should be_true
+
+ @set.proper_superset?(Set[1, 2, 3]).should be_true
+ @set.proper_superset?(Set[1, 3]).should be_true
+ @set.proper_superset?(Set[1, 2]).should be_true
+ @set.proper_superset?(Set[1]).should be_true
+
+ @set.proper_superset?(Set[5]).should be_false
+ @set.proper_superset?(Set[1, 5]).should be_false
+ @set.proper_superset?(Set[nil]).should be_false
+ @set.proper_superset?(Set["test"]).should be_false
+
+ @set.proper_superset?(@set).should be_false
+ Set[].proper_superset?(Set[]).should be_false
+ end
+
+ it "raises an ArgumentError when passed a non-Set" do
+ lambda { Set[].proper_superset?([]) }.should raise_error(ArgumentError)
+ lambda { Set[].proper_superset?(1) }.should raise_error(ArgumentError)
+ lambda { Set[].proper_superset?("test") }.should raise_error(ArgumentError)
+ lambda { Set[].proper_superset?(Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/reject_spec.rb b/spec/ruby/library/set/reject_spec.rb
new file mode 100644
index 0000000000..9131f960ad
--- /dev/null
+++ b/spec/ruby/library/set/reject_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#reject!" do
+ before :each do
+ @set = Set["one", "two", "three"]
+ end
+
+ it "yields every element of self" do
+ ret = []
+ @set.reject! { |x| ret << x }
+ ret.sort.should == ["one", "two", "three"].sort
+ end
+
+ it "deletes every element from self for which the passed block returns true" do
+ @set.reject! { |x| x.size == 3 }
+ @set.size.should eql(1)
+
+ @set.should_not include("one")
+ @set.should_not include("two")
+ @set.should include("three")
+ end
+
+ it "returns self when self was modified" do
+ @set.reject! { |x| true }.should equal(@set)
+ end
+
+ it "returns nil when self was not modified" do
+ @set.reject! { |x| false }.should be_nil
+ end
+
+ it "returns an Enumerator when passed no block" do
+ enum = @set.reject!
+ enum.should be_an_instance_of(Enumerator)
+
+ enum.each { |x| x.size == 3 }
+
+ @set.should_not include("one")
+ @set.should_not include("two")
+ @set.should include("three")
+ end
+end
diff --git a/spec/ruby/library/set/replace_spec.rb b/spec/ruby/library/set/replace_spec.rb
new file mode 100644
index 0000000000..7511066c9c
--- /dev/null
+++ b/spec/ruby/library/set/replace_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#replace" do
+ before :each do
+ @set = Set[:a, :b, :c]
+ end
+
+ it "replaces the contents with other and returns self" do
+ @set.replace(Set[1, 2, 3]).should == @set
+ @set.should == Set[1, 2, 3]
+ end
+
+ it "accepts any enumerable as other" do
+ @set.replace([1, 2, 3]).should == Set[1, 2, 3]
+ end
+end
diff --git a/spec/ruby/library/set/select_spec.rb b/spec/ruby/library/set/select_spec.rb
new file mode 100644
index 0000000000..b458ffacaa
--- /dev/null
+++ b/spec/ruby/library/set/select_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/select'
+
+describe "Set#select!" do
+ it_behaves_like :set_select_bang, :select!
+end
diff --git a/spec/ruby/library/set/shared/add.rb b/spec/ruby/library/set/shared/add.rb
new file mode 100644
index 0000000000..9e797f5df9
--- /dev/null
+++ b/spec/ruby/library/set/shared/add.rb
@@ -0,0 +1,14 @@
+describe :set_add, shared: true do
+ before :each do
+ @set = Set.new
+ end
+
+ it "adds the passed Object to self" do
+ @set.send(@method, "dog")
+ @set.should include("dog")
+ end
+
+ it "returns self" do
+ @set.send(@method, "dog").should equal(@set)
+ end
+end
diff --git a/spec/ruby/library/set/shared/collect.rb b/spec/ruby/library/set/shared/collect.rb
new file mode 100644
index 0000000000..bc58c231be
--- /dev/null
+++ b/spec/ruby/library/set/shared/collect.rb
@@ -0,0 +1,20 @@
+describe :set_collect_bang, shared: true do
+ before :each do
+ @set = Set[1, 2, 3, 4, 5]
+ end
+
+ it "yields each Object in self" do
+ res = []
+ @set.send(@method) { |x| res << x }
+ res.sort.should == [1, 2, 3, 4, 5].sort
+ end
+
+ it "returns self" do
+ @set.send(@method) { |x| x }.should equal(@set)
+ end
+
+ it "replaces self with the return values of the block" do
+ @set.send(@method) { |x| x * 2 }
+ @set.should == Set[2, 4, 6, 8, 10]
+ end
+end
diff --git a/spec/ruby/library/set/shared/difference.rb b/spec/ruby/library/set/shared/difference.rb
new file mode 100644
index 0000000000..52807709c3
--- /dev/null
+++ b/spec/ruby/library/set/shared/difference.rb
@@ -0,0 +1,15 @@
+describe :set_difference, shared: true do
+ before :each do
+ @set = Set[:a, :b, :c]
+ end
+
+ it "returns a new Set containing self's elements excluding the elements in the passed Enumerable" do
+ @set.send(@method, Set[:a, :b]).should == Set[:c]
+ @set.send(@method, [:b, :c]).should == Set[:a]
+ end
+
+ it "raises an ArgumentError when passed a non-Enumerable" do
+ lambda { @set.send(@method, 1) }.should raise_error(ArgumentError)
+ lambda { @set.send(@method, Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/shared/include.rb b/spec/ruby/library/set/shared/include.rb
new file mode 100644
index 0000000000..b4d95cde24
--- /dev/null
+++ b/spec/ruby/library/set/shared/include.rb
@@ -0,0 +1,29 @@
+describe :set_include, shared: true do
+ it "returns true when self contains the passed Object" do
+ set = Set[:a, :b, :c]
+ set.send(@method, :a).should be_true
+ set.send(@method, :e).should be_false
+ end
+
+ describe "member equality" do
+ it "is checked using both #hash and #eql?" do
+ obj = Object.new
+ obj_another = Object.new
+
+ def obj.hash; 42 end
+ def obj_another.hash; 42 end
+ def obj_another.eql?(o) hash == o.hash end
+
+ set = Set["a", "b", "c", obj]
+ set.send(@method, obj_another).should == true
+ end
+
+ it "is not checked using #==" do
+ obj = Object.new
+ set = Set["a", "b", "c"]
+
+ obj.should_not_receive(:==)
+ set.send(@method, obj)
+ end
+ end
+end
diff --git a/spec/ruby/library/set/shared/inspect.rb b/spec/ruby/library/set/shared/inspect.rb
new file mode 100644
index 0000000000..69fbdd12f6
--- /dev/null
+++ b/spec/ruby/library/set/shared/inspect.rb
@@ -0,0 +1,15 @@
+describe "set_inspect", shared: true do
+ it "returns a String representation of self" do
+ Set[].send(@method).should be_kind_of(String)
+ Set[nil, false, true].send(@method).should be_kind_of(String)
+ Set[1, 2, 3].send(@method).should be_kind_of(String)
+ Set["1", "2", "3"].send(@method).should be_kind_of(String)
+ Set[:a, "b", Set[?c]].send(@method).should be_kind_of(String)
+ end
+
+ it "correctly handles self-references" do
+ (set = Set[]) << set
+ set.send(@method).should be_kind_of(String)
+ set.send(@method).should include("#<Set: {...}>")
+ end
+end
diff --git a/spec/ruby/library/set/shared/intersection.rb b/spec/ruby/library/set/shared/intersection.rb
new file mode 100644
index 0000000000..ed0db7457d
--- /dev/null
+++ b/spec/ruby/library/set/shared/intersection.rb
@@ -0,0 +1,15 @@
+describe :set_intersection, shared: true do
+ before :each do
+ @set = Set[:a, :b, :c]
+ end
+
+ it "returns a new Set containing only elements shared by self and the passed Enumerable" do
+ @set.send(@method, Set[:b, :c, :d, :e]).should == Set[:b, :c]
+ @set.send(@method, [:b, :c, :d]).should == Set[:b, :c]
+ end
+
+ it "raises an ArgumentError when passed a non-Enumerable" do
+ lambda { @set.send(@method, 1) }.should raise_error(ArgumentError)
+ lambda { @set.send(@method, Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/shared/length.rb b/spec/ruby/library/set/shared/length.rb
new file mode 100644
index 0000000000..a8fcee9f39
--- /dev/null
+++ b/spec/ruby/library/set/shared/length.rb
@@ -0,0 +1,6 @@
+describe :set_length, shared: true do
+ it "returns the number of elements in the set" do
+ set = Set[:a, :b, :c]
+ set.send(@method).should == 3
+ end
+end
diff --git a/spec/ruby/library/set/shared/select.rb b/spec/ruby/library/set/shared/select.rb
new file mode 100644
index 0000000000..2108d398b4
--- /dev/null
+++ b/spec/ruby/library/set/shared/select.rb
@@ -0,0 +1,42 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe :set_select_bang, shared: true do
+ before :each do
+ @set = Set["one", "two", "three"]
+ end
+
+ it "yields every element of self" do
+ ret = []
+ @set.send(@method) { |x| ret << x }
+ ret.sort.should == ["one", "two", "three"].sort
+ end
+
+ it "keeps every element from self for which the passed block returns true" do
+ @set.send(@method) { |x| x.size != 3 }
+ @set.size.should eql(1)
+
+ @set.should_not include("one")
+ @set.should_not include("two")
+ @set.should include("three")
+ end
+
+ it "returns self when self was modified" do
+ @set.send(@method) { false }.should equal(@set)
+ end
+
+ it "returns nil when self was not modified" do
+ @set.send(@method) { true }.should be_nil
+ end
+
+ it "returns an Enumerator when passed no block" do
+ enum = @set.send(@method)
+ enum.should be_an_instance_of(Enumerator)
+
+ enum.each { |x| x.size != 3 }
+
+ @set.should_not include("one")
+ @set.should_not include("two")
+ @set.should include("three")
+ end
+end
diff --git a/spec/ruby/library/set/shared/union.rb b/spec/ruby/library/set/shared/union.rb
new file mode 100644
index 0000000000..81920f5687
--- /dev/null
+++ b/spec/ruby/library/set/shared/union.rb
@@ -0,0 +1,15 @@
+describe :set_union, shared: true do
+ before :each do
+ @set = Set[:a, :b, :c]
+ end
+
+ it "returns a new Set containing all elements of self and the passed Enumerable" do
+ @set.send(@method, Set[:b, :d, :e]).should == Set[:a, :b, :c, :d, :e]
+ @set.send(@method, [:b, :e]).should == Set[:a, :b, :c, :e]
+ end
+
+ it "raises an ArgumentError when passed a non-Enumerable" do
+ lambda { @set.send(@method, 1) }.should raise_error(ArgumentError)
+ lambda { @set.send(@method, Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/size_spec.rb b/spec/ruby/library/set/size_spec.rb
new file mode 100644
index 0000000000..3c8cb38517
--- /dev/null
+++ b/spec/ruby/library/set/size_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/length'
+require 'set'
+
+describe "Set#size" do
+ it_behaves_like :set_length, :size
+end
diff --git a/spec/ruby/library/set/sortedset/add_spec.rb b/spec/ruby/library/set/sortedset/add_spec.rb
new file mode 100644
index 0000000000..721ac7fac0
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/add_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../../spec_helper'
+require 'set'
+require_relative 'shared/add'
+
+describe "SortedSet#add" do
+ it_behaves_like :sorted_set_add, :add
+
+ it "takes only values which responds <=>" do
+ obj = mock('no_comparison_operator')
+ obj.stub!(:respond_to?).with(:<=>).and_return(false)
+ lambda { SortedSet["hello"].add(obj) }.should raise_error(ArgumentError)
+ end
+
+ it "raises on incompatible <=> comparison" do
+ # Use #to_a here as elements are sorted only when needed.
+ # Therefore the <=> incompatibility is only noticed on sorting.
+ lambda { SortedSet['1', '2'].add(3).to_a }.should raise_error(ArgumentError)
+ end
+end
+
+describe "SortedSet#add?" do
+ before :each do
+ @set = SortedSet.new
+ end
+
+ it "adds the passed Object to self" do
+ @set.add?("cat")
+ @set.should include("cat")
+ end
+
+ it "returns self when the Object has not yet been added to self" do
+ @set.add?("cat").should equal(@set)
+ end
+
+ it "returns nil when the Object has already been added to self" do
+ @set.add?("cat")
+ @set.add?("cat").should be_nil
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/append_spec.rb b/spec/ruby/library/set/sortedset/append_spec.rb
new file mode 100644
index 0000000000..ebcceba962
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/append_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require 'set'
+require_relative 'shared/add'
+
+describe "SortedSet#<<" do
+ it_behaves_like :sorted_set_add, :<<
+end
diff --git a/spec/ruby/library/set/sortedset/case_equality_spec.rb b/spec/ruby/library/set/sortedset/case_equality_spec.rb
new file mode 100644
index 0000000000..5627917677
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/case_equality_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/include'
+require 'set'
+
+ruby_version_is "2.5" do
+ describe "SortedSet#===" do
+ it_behaves_like :sorted_set_include, :===
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/classify_spec.rb b/spec/ruby/library/set/sortedset/classify_spec.rb
new file mode 100644
index 0000000000..62b26d5d28
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/classify_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#classify" do
+ before :each do
+ @set = SortedSet["one", "two", "three", "four"]
+ end
+
+ it "yields each Object in self in sorted order" do
+ res = []
+ @set.classify { |x| res << x }
+ res.should == ["one", "two", "three", "four"].sort
+ end
+
+ it "returns an Enumerator when passed no block" do
+ enum = @set.classify
+ enum.should be_an_instance_of(Enumerator)
+
+ classified = enum.each { |x| x.length }
+ classified.should == { 3 => SortedSet["one", "two"], 4 => SortedSet["four"], 5 => SortedSet["three"] }
+ end
+
+ it "classifies the Objects in self based on the block's return value" do
+ classified = @set.classify { |x| x.length }
+ classified.should == { 3 => SortedSet["one", "two"], 4 => SortedSet["four"], 5 => SortedSet["three"] }
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/clear_spec.rb b/spec/ruby/library/set/sortedset/clear_spec.rb
new file mode 100644
index 0000000000..11b5db2095
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/clear_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#clear" do
+ before :each do
+ @set = SortedSet["one", "two", "three", "four"]
+ end
+
+ it "removes all elements from self" do
+ @set.clear
+ @set.should be_empty
+ end
+
+ it "returns self" do
+ @set.clear.should equal(@set)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/collect_spec.rb b/spec/ruby/library/set/sortedset/collect_spec.rb
new file mode 100644
index 0000000000..21ead4fe55
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/collect_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require 'set'
+require_relative 'shared/collect'
+
+describe "SortedSet#collect!" do
+ it_behaves_like :sorted_set_collect_bang, :collect!
+end
diff --git a/spec/ruby/library/set/sortedset/constructor_spec.rb b/spec/ruby/library/set/sortedset/constructor_spec.rb
new file mode 100644
index 0000000000..953144dbdb
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/constructor_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet[]" do
+ it "returns a new SortedSet populated with the passed Objects" do
+ set = SortedSet[1, 2, 3]
+
+ set.instance_of?(SortedSet).should be_true
+ set.size.should eql(3)
+
+ set.should include(1)
+ set.should include(2)
+ set.should include(3)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/delete_if_spec.rb b/spec/ruby/library/set/sortedset/delete_if_spec.rb
new file mode 100644
index 0000000000..1ff689376a
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/delete_if_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#delete_if" do
+ before :each do
+ @set = SortedSet["one", "two", "three"]
+ end
+
+ it "yields each Object in self in sorted order" do
+ ret = []
+ @set.delete_if { |x| ret << x }
+ ret.should == ["one", "two", "three"].sort
+ end
+
+ it "deletes every element from self for which the passed block returns true" do
+ @set.delete_if { |x| x.size == 3 }
+ @set.size.should eql(1)
+
+ @set.should_not include("one")
+ @set.should_not include("two")
+ @set.should include("three")
+ end
+
+ it "returns self" do
+ @set.delete_if { |x| x }.should equal(@set)
+ end
+
+ it "returns an Enumerator when passed no block" do
+ enum = @set.delete_if
+ enum.should be_an_instance_of(Enumerator)
+
+ enum.each { |x| x.size == 3 }
+
+ @set.should_not include("one")
+ @set.should_not include("two")
+ @set.should include("three")
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/delete_spec.rb b/spec/ruby/library/set/sortedset/delete_spec.rb
new file mode 100644
index 0000000000..71583c7f13
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/delete_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#delete" do
+ before :each do
+ @set = SortedSet["a", "b", "c"]
+ end
+
+ it "deletes the passed Object from self" do
+ @set.delete("a")
+ @set.should_not include("a")
+ end
+
+ it "returns self" do
+ @set.delete("a").should equal(@set)
+ @set.delete("x").should equal(@set)
+ end
+end
+
+describe "SortedSet#delete?" do
+ before :each do
+ @set = SortedSet["a", "b", "c"]
+ end
+
+ it "deletes the passed Object from self" do
+ @set.delete?("a")
+ @set.should_not include("a")
+ end
+
+ it "returns self when the passed Object is in self" do
+ @set.delete?("a").should equal(@set)
+ end
+
+ it "returns nil when the passed Object is not in self" do
+ @set.delete?("x").should be_nil
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/difference_spec.rb b/spec/ruby/library/set/sortedset/difference_spec.rb
new file mode 100644
index 0000000000..c3d679aff8
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/difference_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require 'set'
+require_relative 'shared/difference'
+
+describe "SortedSet#difference" do
+ it_behaves_like :sorted_set_difference, :difference
+end
diff --git a/spec/ruby/library/set/sortedset/divide_spec.rb b/spec/ruby/library/set/sortedset/divide_spec.rb
new file mode 100644
index 0000000000..4b2135a8c9
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/divide_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#divide" do
+ it "divides self into a set of subsets based on the blocks return values" do
+ set = SortedSet["one", "two", "three", "four", "five"].divide { |x| x.length }
+ set.map { |x| x.to_a }.to_a.sort.should == [["five", "four"], ["one", "two"], ["three"]]
+ end
+
+ it "yields each Object in self in sorted order" do
+ ret = []
+ SortedSet["one", "two", "three", "four", "five"].divide { |x| ret << x }
+ ret.should == ["one", "two", "three", "four", "five"].sort
+ end
+
+ # BUG: Does not raise a LocalJumpError, but a NoMethodError
+ #
+ # it "raises a LocalJumpError when not passed a block" do
+ # lambda { SortedSet[1].divide }.should raise_error(LocalJumpError)
+ # end
+end
+
+describe "SortedSet#divide when passed a block with an arity of 2" do
+ it "divides self into a set of subsets based on the blocks return values" do
+ set = SortedSet[1, 3, 4, 6, 9, 10, 11].divide { |x, y| (x - y).abs == 1 }
+ set.map { |x| x.to_a }.to_a.sort.should == [[1], [3, 4], [6], [9, 10, 11]]
+ end
+
+ it "yields each two Objects to the block" do
+ ret = []
+ SortedSet[1, 2].divide { |x, y| ret << [x, y] }
+ ret.should == [[1, 1], [1, 2], [2, 1], [2, 2]]
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/each_spec.rb b/spec/ruby/library/set/sortedset/each_spec.rb
new file mode 100644
index 0000000000..bcf0d74d1f
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/each_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#each" do
+ before :each do
+ @set = SortedSet[1, 2, 3]
+ end
+
+ it "yields each Object in self in sorted order" do
+ ret = []
+ SortedSet["one", "two", "three"].each { |x| ret << x }
+ ret.should == ["one", "two", "three"].sort
+ end
+
+ it "returns self" do
+ @set.each { |x| x }.should equal(@set)
+ end
+
+ it "returns an Enumerator when not passed a block" do
+ enum = @set.each
+
+ ret = []
+ enum.each { |x| ret << x }
+ ret.sort.should == [1, 2, 3]
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/empty_spec.rb b/spec/ruby/library/set/sortedset/empty_spec.rb
new file mode 100644
index 0000000000..deb3b567fb
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/empty_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#empty?" do
+ it "returns true if self is empty" do
+ SortedSet[].empty?.should be_true
+ SortedSet[1].empty?.should be_false
+ SortedSet[1,2,3].empty?.should be_false
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/eql_spec.rb b/spec/ruby/library/set/sortedset/eql_spec.rb
new file mode 100644
index 0000000000..b22858a362
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/eql_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#eql?" do
+ it "returns true when the passed argument is a SortedSet and contains the same elements" do
+ SortedSet[].should eql(SortedSet[])
+ SortedSet[1, 2, 3].should eql(SortedSet[1, 2, 3])
+ SortedSet[1, 2, 3].should eql(SortedSet[3, 2, 1])
+
+# SortedSet["a", :b, ?c].should eql(SortedSet[?c, :b, "a"])
+
+ SortedSet[1, 2, 3].should_not eql(SortedSet[1.0, 2, 3])
+ SortedSet[1, 2, 3].should_not eql(SortedSet[2, 3])
+ SortedSet[1, 2, 3].should_not eql(SortedSet[])
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/equal_value_spec.rb b/spec/ruby/library/set/sortedset/equal_value_spec.rb
new file mode 100644
index 0000000000..cb1b7c9443
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/equal_value_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#==" do
+ it "returns true when the passed Object is a SortedSet and self and the Object contain the same elements" do
+ SortedSet[].should == SortedSet[]
+ SortedSet[1, 2, 3].should == SortedSet[1, 2, 3]
+ SortedSet["1", "2", "3"].should == SortedSet["1", "2", "3"]
+
+ SortedSet[1, 2, 3].should_not == SortedSet[1.0, 2, 3]
+ SortedSet[1, 2, 3].should_not == [1, 2, 3]
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/exclusion_spec.rb b/spec/ruby/library/set/sortedset/exclusion_spec.rb
new file mode 100644
index 0000000000..59f7615e83
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/exclusion_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#^" do
+ before :each do
+ @set = SortedSet[1, 2, 3, 4]
+ end
+
+ it "returns a new SortedSet containing elements that are not in both self and the passed Enumberable" do
+ (@set ^ SortedSet[3, 4, 5]).should == SortedSet[1, 2, 5]
+ (@set ^ [3, 4, 5]).should == SortedSet[1, 2, 5]
+ end
+
+ it "raises an ArgumentError when passed a non-Enumerable" do
+ lambda { @set ^ 3 }.should raise_error(ArgumentError)
+ lambda { @set ^ Object.new }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/filter_spec.rb b/spec/ruby/library/set/sortedset/filter_spec.rb
new file mode 100644
index 0000000000..cfaa8b2729
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/filter_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/select'
+require 'set'
+
+ruby_version_is "2.6" do
+ describe "SortedSet#filter!" do
+ it_behaves_like :sorted_set_select_bang, :filter!
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/flatten_merge_spec.rb b/spec/ruby/library/set/sortedset/flatten_merge_spec.rb
new file mode 100644
index 0000000000..9a8ed13f00
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/flatten_merge_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#flatten_merge" do
+ it "is protected" do
+ SortedSet.should have_protected_instance_method("flatten_merge")
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/flatten_spec.rb b/spec/ruby/library/set/sortedset/flatten_spec.rb
new file mode 100644
index 0000000000..ca4e4637e6
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/flatten_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+# Note: Flatten make little sens on sorted sets, because SortedSets are not (by default)
+# comparable. For a SortedSet to be both valid and nested, we need to define a comparison operator:
+module SortedSet_FlattenSpecs
+ class ComparableSortedSet < SortedSet
+ def <=>(other)
+ return puts "#{other} vs #{self}" unless other.is_a?(ComparableSortedSet)
+ to_a <=> other.to_a
+ end
+ end
+end
+
+describe "SortedSet#flatten" do
+ it "returns a copy of self with each included SortedSet flattened" do
+ klass = SortedSet_FlattenSpecs::ComparableSortedSet
+ set = klass[klass[1,2], klass[3,4], klass[5,6,7], klass[8]]
+ flattened_set = set.flatten
+
+ flattened_set.should_not equal(set)
+ flattened_set.should == klass[1, 2, 3, 4, 5, 6, 7, 8]
+ end
+end
+
+describe "SortedSet#flatten!" do
+ it "flattens self" do
+ klass = SortedSet_FlattenSpecs::ComparableSortedSet
+ set = klass[klass[1,2], klass[3,4], klass[5,6,7], klass[8]]
+ set.flatten!
+ set.should == klass[1, 2, 3, 4, 5, 6, 7, 8]
+ end
+
+ it "returns self when self was modified" do
+ klass = SortedSet_FlattenSpecs::ComparableSortedSet
+ set = klass[klass[1,2], klass[3,4]]
+ set.flatten!.should equal(set)
+ end
+
+ it "returns nil when self was not modified" do
+ set = SortedSet[1, 2, 3, 4]
+ set.flatten!.should be_nil
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/hash_spec.rb b/spec/ruby/library/set/sortedset/hash_spec.rb
new file mode 100644
index 0000000000..7833c68a10
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/hash_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#hash" do
+ it "is static" do
+ SortedSet[].hash.should == SortedSet[].hash
+ SortedSet[1, 2, 3].hash.should == SortedSet[1, 2, 3].hash
+ SortedSet["a", "b", "c"].hash.should == SortedSet["c", "b", "a"].hash
+
+ SortedSet[].hash.should_not == SortedSet[1, 2, 3].hash
+ SortedSet[1, 2, 3].hash.should_not == SortedSet["a", "b", "c"].hash
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/include_spec.rb b/spec/ruby/library/set/sortedset/include_spec.rb
new file mode 100644
index 0000000000..030a9e146a
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/include_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/include'
+require 'set'
+
+describe "SortedSet#include?" do
+ it_behaves_like :sorted_set_include, :include?
+end
diff --git a/spec/ruby/library/set/sortedset/initialize_spec.rb b/spec/ruby/library/set/sortedset/initialize_spec.rb
new file mode 100644
index 0000000000..cdf756f6b8
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/initialize_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#initialize" do
+ it "is private" do
+ SortedSet.should have_private_instance_method("initialize")
+ end
+
+ it "adds all elements of the passed Enumerable to self" do
+ s = SortedSet.new([1, 2, 3])
+ s.size.should eql(3)
+ s.should include(1)
+ s.should include(2)
+ s.should include(3)
+ end
+
+ it "preprocesses all elements by a passed block before adding to self" do
+ s = SortedSet.new([1, 2, 3]) { |x| x * x }
+ s.size.should eql(3)
+ s.should include(1)
+ s.should include(4)
+ s.should include(9)
+ end
+
+ it "raises on incompatible <=> comparison" do
+ # Use #to_a here as elements are sorted only when needed.
+ # Therefore the <=> incompatibility is only noticed on sorting.
+ lambda { SortedSet.new(['00', nil]).to_a }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/inspect_spec.rb b/spec/ruby/library/set/sortedset/inspect_spec.rb
new file mode 100644
index 0000000000..7103bee3f5
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/inspect_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#inspect" do
+ it "returns a String representation of self" do
+ SortedSet[].inspect.should be_kind_of(String)
+ SortedSet[1, 2, 3].inspect.should be_kind_of(String)
+ SortedSet["1", "2", "3"].inspect.should be_kind_of(String)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/intersection_spec.rb b/spec/ruby/library/set/sortedset/intersection_spec.rb
new file mode 100644
index 0000000000..6ff9c80ce1
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/intersection_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/intersection'
+require 'set'
+
+describe "SortedSet#intersection" do
+ it_behaves_like :sorted_set_intersection, :intersection
+end
+
+describe "SortedSet#&" do
+ it_behaves_like :sorted_set_intersection, :&
+end
diff --git a/spec/ruby/library/set/sortedset/keep_if_spec.rb b/spec/ruby/library/set/sortedset/keep_if_spec.rb
new file mode 100644
index 0000000000..2235eb3766
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/keep_if_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#keep_if" do
+ before :each do
+ @set = SortedSet["one", "two", "three"]
+ end
+
+ it "yields each Object in self in sorted order" do
+ ret = []
+ @set.keep_if { |x| ret << x }
+ ret.should == ["one", "two", "three"].sort
+ end
+
+ it "keeps every element from self for which the passed block returns true" do
+ @set.keep_if { |x| x.size != 3 }
+ @set.to_a.should == ["three"]
+ end
+
+ it "returns self" do
+ @set.keep_if {}.should equal(@set)
+ end
+
+ it "returns an Enumerator when passed no block" do
+ enum = @set.keep_if
+ enum.should be_an_instance_of(Enumerator)
+
+ enum.each { |x| x.size != 3 }
+ @set.to_a.should == ["three"]
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/length_spec.rb b/spec/ruby/library/set/sortedset/length_spec.rb
new file mode 100644
index 0000000000..5f138dd6f7
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/length_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/length'
+require 'set'
+
+describe "SortedSet#length" do
+ it_behaves_like :sorted_set_length, :length
+end
diff --git a/spec/ruby/library/set/sortedset/map_spec.rb b/spec/ruby/library/set/sortedset/map_spec.rb
new file mode 100644
index 0000000000..1d7b5954e5
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/map_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require 'set'
+require_relative 'shared/collect'
+
+describe "SortedSet#map!" do
+ it_behaves_like :sorted_set_collect_bang, :map!
+end
diff --git a/spec/ruby/library/set/sortedset/member_spec.rb b/spec/ruby/library/set/sortedset/member_spec.rb
new file mode 100644
index 0000000000..d6005557ab
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/member_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/include'
+require 'set'
+
+describe "SortedSet#member?" do
+ it_behaves_like :sorted_set_include, :member?
+end
diff --git a/spec/ruby/library/set/sortedset/merge_spec.rb b/spec/ruby/library/set/sortedset/merge_spec.rb
new file mode 100644
index 0000000000..f98f2c83ce
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/merge_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#merge" do
+ it "adds the elements of the passed Enumerable to self" do
+ SortedSet["a", "b"].merge(SortedSet["b", "c", "d"]).should == SortedSet["a", "b", "c", "d"]
+ SortedSet[1, 2].merge([3, 4]).should == SortedSet[1, 2, 3, 4]
+ end
+
+ it "returns self" do
+ set = SortedSet[1, 2]
+ set.merge([3, 4]).should equal(set)
+ end
+
+ it "raises an ArgumentError when passed a non-Enumerable" do
+ lambda { SortedSet[1, 2].merge(1) }.should raise_error(ArgumentError)
+ lambda { SortedSet[1, 2].merge(Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/minus_spec.rb b/spec/ruby/library/set/sortedset/minus_spec.rb
new file mode 100644
index 0000000000..ffb8ee85d1
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/minus_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require 'set'
+require_relative 'shared/difference'
+
+describe "SortedSet#-" do
+ it_behaves_like :sorted_set_difference, :-
+end
diff --git a/spec/ruby/library/set/sortedset/plus_spec.rb b/spec/ruby/library/set/sortedset/plus_spec.rb
new file mode 100644
index 0000000000..355c775d7e
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/plus_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/union'
+require 'set'
+
+describe "SortedSet#+" do
+ it_behaves_like :sorted_set_union, :+
+end
diff --git a/spec/ruby/library/set/sortedset/pretty_print_cycle_spec.rb b/spec/ruby/library/set/sortedset/pretty_print_cycle_spec.rb
new file mode 100644
index 0000000000..fe15f1bf7d
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/pretty_print_cycle_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#pretty_print_cycle" do
+ it "passes the 'pretty print' representation of a self-referencing SortedSet to the pretty print writer" do
+ pp = mock("PrettyPrint")
+ pp.should_receive(:text).with("#<SortedSet: {...}>")
+ SortedSet[1, 2, 3].pretty_print_cycle(pp)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/pretty_print_spec.rb b/spec/ruby/library/set/sortedset/pretty_print_spec.rb
new file mode 100644
index 0000000000..601ff4d182
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/pretty_print_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#pretty_print" do
+ it "passes the 'pretty print' representation of self to the pretty print writer" do
+ pp = mock("PrettyPrint")
+ set = SortedSet[1, 2, 3]
+
+ pp.should_receive(:text).with("#<SortedSet: {")
+ pp.should_receive(:text).with("}>")
+
+ pp.should_receive(:nest).with(1).and_yield
+ pp.should_receive(:seplist).with(set)
+
+ set.pretty_print(pp)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/proper_subset_spec.rb b/spec/ruby/library/set/sortedset/proper_subset_spec.rb
new file mode 100644
index 0000000000..3b3c2e5c4c
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/proper_subset_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#proper_subset?" do
+ before :each do
+ @set = SortedSet[1, 2, 3, 4]
+ end
+
+ it "returns true if passed a SortedSet that self is a proper subset of" do
+ SortedSet[].proper_subset?(@set).should be_true
+ SortedSet[].proper_subset?(SortedSet[1, 2, 3]).should be_true
+ SortedSet[].proper_subset?(SortedSet["a", "b", "c"]).should be_true
+
+ SortedSet[1, 2, 3].proper_subset?(@set).should be_true
+ SortedSet[1, 3].proper_subset?(@set).should be_true
+ SortedSet[1, 2].proper_subset?(@set).should be_true
+ SortedSet[1].proper_subset?(@set).should be_true
+
+ SortedSet[5].proper_subset?(@set).should be_false
+ SortedSet[1, 5].proper_subset?(@set).should be_false
+ SortedSet["test"].proper_subset?(@set).should be_false
+
+ @set.proper_subset?(@set).should be_false
+ SortedSet[].proper_subset?(SortedSet[]).should be_false
+ end
+
+ it "raises an ArgumentError when passed a non-SortedSet" do
+ lambda { SortedSet[].proper_subset?([]) }.should raise_error(ArgumentError)
+ lambda { SortedSet[].proper_subset?(1) }.should raise_error(ArgumentError)
+ lambda { SortedSet[].proper_subset?("test") }.should raise_error(ArgumentError)
+ lambda { SortedSet[].proper_subset?(Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/proper_superset_spec.rb b/spec/ruby/library/set/sortedset/proper_superset_spec.rb
new file mode 100644
index 0000000000..72ae71e2f1
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/proper_superset_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#proper_superset?" do
+ before :each do
+ @set = SortedSet[1, 2, 3, 4]
+ end
+
+ it "returns true if passed a SortedSet that self is a proper superset of" do
+ @set.proper_superset?(SortedSet[]).should be_true
+ SortedSet[1, 2, 3].proper_superset?(SortedSet[]).should be_true
+ SortedSet["a", "b", "c"].proper_superset?(SortedSet[]).should be_true
+
+ @set.proper_superset?(SortedSet[1, 2, 3]).should be_true
+ @set.proper_superset?(SortedSet[1, 3]).should be_true
+ @set.proper_superset?(SortedSet[1, 2]).should be_true
+ @set.proper_superset?(SortedSet[1]).should be_true
+
+ @set.proper_superset?(SortedSet[5]).should be_false
+ @set.proper_superset?(SortedSet[1, 5]).should be_false
+ @set.proper_superset?(SortedSet["test"]).should be_false
+
+ @set.proper_superset?(@set).should be_false
+ SortedSet[].proper_superset?(SortedSet[]).should be_false
+ end
+
+ it "raises an ArgumentError when passed a non-SortedSet" do
+ lambda { SortedSet[].proper_superset?([]) }.should raise_error(ArgumentError)
+ lambda { SortedSet[].proper_superset?(1) }.should raise_error(ArgumentError)
+ lambda { SortedSet[].proper_superset?("test") }.should raise_error(ArgumentError)
+ lambda { SortedSet[].proper_superset?(Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/reject_spec.rb b/spec/ruby/library/set/sortedset/reject_spec.rb
new file mode 100644
index 0000000000..da2a76e7a7
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/reject_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#reject!" do
+ before :each do
+ @set = SortedSet["one", "two", "three"]
+ end
+
+ it "yields each Object in self in sorted order" do
+ res = []
+ @set.reject! { |x| res << x }
+ res.should == ["one", "two", "three"].sort
+ end
+
+ it "deletes every element from self for which the passed block returns true" do
+ @set.reject! { |x| x.size == 3 }
+ @set.size.should eql(1)
+
+ @set.should_not include("one")
+ @set.should_not include("two")
+ @set.should include("three")
+ end
+
+ it "returns self when self was modified" do
+ @set.reject! { |x| true }.should equal(@set)
+ end
+
+ it "returns nil when self was not modified" do
+ @set.reject! { |x| false }.should be_nil
+ end
+
+ it "returns an Enumerator when passed no block" do
+ enum = @set.reject!
+ enum.should be_an_instance_of(Enumerator)
+
+ enum.each { |x| x.size == 3 }
+
+ @set.should_not include("one")
+ @set.should_not include("two")
+ @set.should include("three")
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/replace_spec.rb b/spec/ruby/library/set/sortedset/replace_spec.rb
new file mode 100644
index 0000000000..2a7fa73efb
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/replace_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#replace" do
+ before :each do
+ @set = SortedSet["a", "b", "c"]
+ end
+
+ it "replaces the contents with other and returns self" do
+ @set.replace(SortedSet[1, 2, 3]).should == @set
+ @set.should == SortedSet[1, 2, 3]
+ end
+
+ it "accepts any enumerable as other" do
+ @set.replace([1, 2, 3]).should == SortedSet[1, 2, 3]
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/select_spec.rb b/spec/ruby/library/set/sortedset/select_spec.rb
new file mode 100644
index 0000000000..68326cd02c
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/select_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/select'
+require 'set'
+
+describe "SortedSet#select!" do
+ it_behaves_like :sorted_set_select_bang, :select!
+end
diff --git a/spec/ruby/library/set/sortedset/shared/add.rb b/spec/ruby/library/set/sortedset/shared/add.rb
new file mode 100644
index 0000000000..95ef1b090e
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/shared/add.rb
@@ -0,0 +1,14 @@
+describe :sorted_set_add, shared: true do
+ before :each do
+ @set = SortedSet.new
+ end
+
+ it "adds the passed Object to self" do
+ @set.send(@method, "dog")
+ @set.should include("dog")
+ end
+
+ it "returns self" do
+ @set.send(@method, "dog").should equal(@set)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/shared/collect.rb b/spec/ruby/library/set/sortedset/shared/collect.rb
new file mode 100644
index 0000000000..e53304d427
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/shared/collect.rb
@@ -0,0 +1,20 @@
+describe :sorted_set_collect_bang, shared: true do
+ before :each do
+ @set = SortedSet[1, 2, 3, 4, 5]
+ end
+
+ it "yields each Object in self in sorted order" do
+ res = []
+ SortedSet["one", "two", "three"].send(@method) { |x| res << x; x }
+ res.should == ["one", "two", "three"].sort
+ end
+
+ it "returns self" do
+ @set.send(@method) { |x| x }.should equal(@set)
+ end
+
+ it "replaces self with the return values of the block" do
+ @set.send(@method) { |x| x * 2 }
+ @set.should == SortedSet[2, 4, 6, 8, 10]
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/shared/difference.rb b/spec/ruby/library/set/sortedset/shared/difference.rb
new file mode 100644
index 0000000000..cf50ff0eb2
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/shared/difference.rb
@@ -0,0 +1,15 @@
+describe :sorted_set_difference, shared: true do
+ before :each do
+ @set = SortedSet["a", "b", "c"]
+ end
+
+ it "returns a new SortedSet containing self's elements excluding the elements in the passed Enumerable" do
+ @set.send(@method, SortedSet["a", "b"]).should == SortedSet["c"]
+ @set.send(@method, ["b", "c"]).should == SortedSet["a"]
+ end
+
+ it "raises an ArgumentError when passed a non-Enumerable" do
+ lambda { @set.send(@method, 1) }.should raise_error(ArgumentError)
+ lambda { @set.send(@method, Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/shared/include.rb b/spec/ruby/library/set/sortedset/shared/include.rb
new file mode 100644
index 0000000000..cd1758819d
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/shared/include.rb
@@ -0,0 +1,7 @@
+describe :sorted_set_include, shared: true do
+ it "returns true when self contains the passed Object" do
+ set = SortedSet["a", "b", "c"]
+ set.send(@method, "a").should be_true
+ set.send(@method, "e").should be_false
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/shared/intersection.rb b/spec/ruby/library/set/sortedset/shared/intersection.rb
new file mode 100644
index 0000000000..d3cfa96656
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/shared/intersection.rb
@@ -0,0 +1,15 @@
+describe :sorted_set_intersection, shared: true do
+ before :each do
+ @set = SortedSet["a", "b", "c"]
+ end
+
+ it "returns a new SortedSet containing only elements shared by self and the passed Enumerable" do
+ @set.send(@method, SortedSet["b", "c", "d", "e"]).should == SortedSet["b", "c"]
+ @set.send(@method, ["b", "c", "d"]).should == SortedSet["b", "c"]
+ end
+
+ it "raises an ArgumentError when passed a non-Enumerable" do
+ lambda { @set.send(@method, 1) }.should raise_error(ArgumentError)
+ lambda { @set.send(@method, Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/shared/length.rb b/spec/ruby/library/set/sortedset/shared/length.rb
new file mode 100644
index 0000000000..d1dfee1cff
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/shared/length.rb
@@ -0,0 +1,6 @@
+describe :sorted_set_length, shared: true do
+ it "returns the number of elements in the set" do
+ set = SortedSet["a", "b", "c"]
+ set.send(@method).should == 3
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/shared/select.rb b/spec/ruby/library/set/sortedset/shared/select.rb
new file mode 100644
index 0000000000..e13311eda5
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/shared/select.rb
@@ -0,0 +1,35 @@
+require_relative '../../../../spec_helper'
+require 'set'
+
+describe :sorted_set_select_bang, shared: true do
+ before :each do
+ @set = SortedSet["one", "two", "three"]
+ end
+
+ it "yields each Object in self in sorted order" do
+ res = []
+ @set.send(@method) { |x| res << x }
+ res.should == ["one", "two", "three"].sort
+ end
+
+ it "keeps every element from self for which the passed block returns true" do
+ @set.send(@method) { |x| x.size != 3 }
+ @set.to_a.should == ["three"]
+ end
+
+ it "returns self when self was modified" do
+ @set.send(@method) { false }.should equal(@set)
+ end
+
+ it "returns nil when self was not modified" do
+ @set.send(@method) { true }.should be_nil
+ end
+
+ it "returns an Enumerator when passed no block" do
+ enum = @set.send(@method)
+ enum.should be_an_instance_of(Enumerator)
+
+ enum.each { |x| x.size != 3 }
+ @set.to_a.should == ["three"]
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/shared/union.rb b/spec/ruby/library/set/sortedset/shared/union.rb
new file mode 100644
index 0000000000..4ff07ef5cc
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/shared/union.rb
@@ -0,0 +1,15 @@
+describe :sorted_set_union, shared: true do
+ before :each do
+ @set = SortedSet["a", "b", "c"]
+ end
+
+ it "returns a new SortedSet containing all elements of self and the passed Enumerable" do
+ @set.send(@method, SortedSet["b", "d", "e"]).should == SortedSet["a", "b", "c", "d", "e"]
+ @set.send(@method, ["b", "e"]).should == SortedSet["a", "b", "c", "e"]
+ end
+
+ it "raises an ArgumentError when passed a non-Enumerable" do
+ lambda { @set.send(@method, 1) }.should raise_error(ArgumentError)
+ lambda { @set.send(@method, Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/size_spec.rb b/spec/ruby/library/set/sortedset/size_spec.rb
new file mode 100644
index 0000000000..13e5085b0c
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/size_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/length'
+require 'set'
+
+describe "SortedSet#size" do
+ it_behaves_like :sorted_set_length, :size
+end
diff --git a/spec/ruby/library/set/sortedset/subset_spec.rb b/spec/ruby/library/set/sortedset/subset_spec.rb
new file mode 100644
index 0000000000..cdada2cdfb
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/subset_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#subset?" do
+ before :each do
+ @set = SortedSet[1, 2, 3, 4]
+ end
+
+ it "returns true if passed a SortedSet that is equal to self or self is a subset of" do
+ @set.subset?(@set).should be_true
+ SortedSet[].subset?(SortedSet[]).should be_true
+
+ SortedSet[].subset?(@set).should be_true
+ SortedSet[].subset?(SortedSet[1, 2, 3]).should be_true
+ SortedSet[].subset?(SortedSet["a", "b", "c"]).should be_true
+
+ SortedSet[1, 2, 3].subset?(@set).should be_true
+ SortedSet[1, 3].subset?(@set).should be_true
+ SortedSet[1, 2].subset?(@set).should be_true
+ SortedSet[1].subset?(@set).should be_true
+
+ SortedSet[5].subset?(@set).should be_false
+ SortedSet[1, 5].subset?(@set).should be_false
+ SortedSet["test"].subset?(@set).should be_false
+ end
+
+ it "raises an ArgumentError when passed a non-SortedSet" do
+ lambda { SortedSet[].subset?([]) }.should raise_error(ArgumentError)
+ lambda { SortedSet[].subset?(1) }.should raise_error(ArgumentError)
+ lambda { SortedSet[].subset?("test") }.should raise_error(ArgumentError)
+ lambda { SortedSet[].subset?(Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/subtract_spec.rb b/spec/ruby/library/set/sortedset/subtract_spec.rb
new file mode 100644
index 0000000000..64d66c3688
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/subtract_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#subtract" do
+ before :each do
+ @set = SortedSet["a", "b", "c"]
+ end
+
+ it "deletes any elements contained in other and returns self" do
+ @set.subtract(SortedSet["b", "c"]).should == @set
+ @set.should == SortedSet["a"]
+ end
+
+ it "accepts any enumerable as other" do
+ @set.subtract(["c"]).should == SortedSet["a", "b"]
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/superset_spec.rb b/spec/ruby/library/set/sortedset/superset_spec.rb
new file mode 100644
index 0000000000..663b179a0c
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/superset_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#superset?" do
+ before :each do
+ @set = SortedSet[1, 2, 3, 4]
+ end
+
+ it "returns true if passed a SortedSet that equals self or self is a proper superset of" do
+ @set.superset?(@set).should be_true
+ SortedSet[].superset?(SortedSet[]).should be_true
+
+ @set.superset?(SortedSet[]).should be_true
+ SortedSet[1, 2, 3].superset?(SortedSet[]).should be_true
+ SortedSet["a", "b", "c"].superset?(SortedSet[]).should be_true
+
+ @set.superset?(SortedSet[1, 2, 3]).should be_true
+ @set.superset?(SortedSet[1, 3]).should be_true
+ @set.superset?(SortedSet[1, 2]).should be_true
+ @set.superset?(SortedSet[1]).should be_true
+
+ @set.superset?(SortedSet[5]).should be_false
+ @set.superset?(SortedSet[1, 5]).should be_false
+ @set.superset?(SortedSet["test"]).should be_false
+ end
+
+ it "raises an ArgumentError when passed a non-SortedSet" do
+ lambda { SortedSet[].superset?([]) }.should raise_error(ArgumentError)
+ lambda { SortedSet[].superset?(1) }.should raise_error(ArgumentError)
+ lambda { SortedSet[].superset?("test") }.should raise_error(ArgumentError)
+ lambda { SortedSet[].superset?(Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/to_a_spec.rb b/spec/ruby/library/set/sortedset/to_a_spec.rb
new file mode 100644
index 0000000000..ba37d18cdb
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/to_a_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+require 'set'
+
+describe "SortedSet#to_a" do
+ it "returns an array containing elements" do
+ set = SortedSet.new [1, 2, 3]
+ set.to_a.should == [1, 2, 3]
+ end
+
+ it "returns a sorted array containing elements" do
+ set = SortedSet[2, 3, 1]
+ set.to_a.should == [1, 2, 3]
+
+ set = SortedSet.new [5, 6, 4, 4]
+ set.to_a.should == [4, 5, 6]
+ end
+end
diff --git a/spec/ruby/library/set/sortedset/union_spec.rb b/spec/ruby/library/set/sortedset/union_spec.rb
new file mode 100644
index 0000000000..eb77600fee
--- /dev/null
+++ b/spec/ruby/library/set/sortedset/union_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/union'
+require 'set'
+
+describe "SortedSet#union" do
+ it_behaves_like :sorted_set_union, :union
+end
+
+describe "SortedSet#|" do
+ it_behaves_like :sorted_set_union, :|
+end
diff --git a/spec/ruby/library/set/subset_spec.rb b/spec/ruby/library/set/subset_spec.rb
new file mode 100644
index 0000000000..e1792ce9b9
--- /dev/null
+++ b/spec/ruby/library/set/subset_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#subset?" do
+ before :each do
+ @set = Set[1, 2, 3, 4]
+ end
+
+ it "returns true if passed a Set that is equal to self or self is a subset of" do
+ @set.subset?(@set).should be_true
+ Set[].subset?(Set[]).should be_true
+
+ Set[].subset?(@set).should be_true
+ Set[].subset?(Set[1, 2, 3]).should be_true
+ Set[].subset?(Set["a", :b, ?c]).should be_true
+
+ Set[1, 2, 3].subset?(@set).should be_true
+ Set[1, 3].subset?(@set).should be_true
+ Set[1, 2].subset?(@set).should be_true
+ Set[1].subset?(@set).should be_true
+
+ Set[5].subset?(@set).should be_false
+ Set[1, 5].subset?(@set).should be_false
+ Set[nil].subset?(@set).should be_false
+ Set["test"].subset?(@set).should be_false
+ end
+
+ it "raises an ArgumentError when passed a non-Set" do
+ lambda { Set[].subset?([]) }.should raise_error(ArgumentError)
+ lambda { Set[].subset?(1) }.should raise_error(ArgumentError)
+ lambda { Set[].subset?("test") }.should raise_error(ArgumentError)
+ lambda { Set[].subset?(Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/subtract_spec.rb b/spec/ruby/library/set/subtract_spec.rb
new file mode 100644
index 0000000000..56713de8b3
--- /dev/null
+++ b/spec/ruby/library/set/subtract_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#subtract" do
+ before :each do
+ @set = Set[:a, :b, :c]
+ end
+
+ it "deletes any elements contained in other and returns self" do
+ @set.subtract(Set[:b, :c]).should == @set
+ @set.should == Set[:a]
+ end
+
+ it "accepts any enumerable as other" do
+ @set.subtract([:c]).should == Set[:a, :b]
+ end
+end
diff --git a/spec/ruby/library/set/superset_spec.rb b/spec/ruby/library/set/superset_spec.rb
new file mode 100644
index 0000000000..a95fab8a42
--- /dev/null
+++ b/spec/ruby/library/set/superset_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#superset?" do
+ before :each do
+ @set = Set[1, 2, 3, 4]
+ end
+
+ it "returns true if passed a Set that equals self or self is a proper superset of" do
+ @set.superset?(@set).should be_true
+ Set[].superset?(Set[]).should be_true
+
+ @set.superset?(Set[]).should be_true
+ Set[1, 2, 3].superset?(Set[]).should be_true
+ Set["a", :b, ?c].superset?(Set[]).should be_true
+
+ @set.superset?(Set[1, 2, 3]).should be_true
+ @set.superset?(Set[1, 3]).should be_true
+ @set.superset?(Set[1, 2]).should be_true
+ @set.superset?(Set[1]).should be_true
+
+ @set.superset?(Set[5]).should be_false
+ @set.superset?(Set[1, 5]).should be_false
+ @set.superset?(Set[nil]).should be_false
+ @set.superset?(Set["test"]).should be_false
+ end
+
+ it "raises an ArgumentError when passed a non-Set" do
+ lambda { Set[].superset?([]) }.should raise_error(ArgumentError)
+ lambda { Set[].superset?(1) }.should raise_error(ArgumentError)
+ lambda { Set[].superset?("test") }.should raise_error(ArgumentError)
+ lambda { Set[].superset?(Object.new) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/set/to_a_spec.rb b/spec/ruby/library/set/to_a_spec.rb
new file mode 100644
index 0000000000..689e44f38a
--- /dev/null
+++ b/spec/ruby/library/set/to_a_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'set'
+
+describe "Set#to_a" do
+ it "returns an array containing elements of self" do
+ Set[1, 2, 3].to_a.sort.should == [1, 2, 3]
+ end
+end
diff --git a/spec/ruby/library/set/to_s_spec.rb b/spec/ruby/library/set/to_s_spec.rb
new file mode 100644
index 0000000000..ca2806d669
--- /dev/null
+++ b/spec/ruby/library/set/to_s_spec.rb
@@ -0,0 +1,13 @@
+require_relative 'shared/inspect'
+require 'set'
+
+ruby_version_is "2.5" do
+ describe "Set#to_s" do
+ it_behaves_like :set_inspect, :to_s
+
+ it "is an alias of inspect" do
+ set = Set.new
+ set.method(:to_s).should == set.method(:inspect)
+ end
+ end
+end
diff --git a/spec/ruby/library/set/union_spec.rb b/spec/ruby/library/set/union_spec.rb
new file mode 100644
index 0000000000..20fe0ddca3
--- /dev/null
+++ b/spec/ruby/library/set/union_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/union'
+require 'set'
+
+describe "Set#union" do
+ it_behaves_like :set_union, :union
+end
+
+describe "Set#|" do
+ it_behaves_like :set_union, :|
+end
diff --git a/spec/ruby/library/shellwords/shellwords_spec.rb b/spec/ruby/library/shellwords/shellwords_spec.rb
new file mode 100644
index 0000000000..b245fe862d
--- /dev/null
+++ b/spec/ruby/library/shellwords/shellwords_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require 'shellwords'
+include Shellwords
+
+describe "Shellwords#shellwords" do
+ it "honors quoted strings" do
+ shellwords('a "b b" a').should == ['a', 'b b', 'a']
+ end
+
+ it "honors escaped double quotes" do
+ shellwords('a "\"b\" c" d').should == ['a', '"b" c', 'd']
+ end
+
+ it "honors escaped single quotes" do
+ shellwords("a \"'b' c\" d").should == ['a', "'b' c", 'd']
+ end
+
+ it "honors escaped spaces" do
+ shellwords('a b\ c d').should == ['a', 'b c', 'd']
+ end
+
+ it "raises ArgumentError when double quoted strings are misquoted" do
+ lambda { shellwords('a "b c d e') }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError when single quoted strings are misquoted" do
+ lambda { shellwords("a 'b c d e") }.should raise_error(ArgumentError)
+ end
+
+ ruby_version_is '2.4' do
+ # https://bugs.ruby-lang.org/issues/10055
+ it "matches POSIX sh behavior for backslashes within double quoted strings" do
+ shellsplit('printf "%s\n"').should == ['printf', '%s\n']
+ end
+ end
+end
diff --git a/spec/ruby/library/singleton/allocate_spec.rb b/spec/ruby/library/singleton/allocate_spec.rb
new file mode 100644
index 0000000000..689ecc57c6
--- /dev/null
+++ b/spec/ruby/library/singleton/allocate_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Singleton.allocate" do
+ it "is a private method" do
+ lambda { SingletonSpecs::MyClass.allocate }.should raise_error(NoMethodError)
+ end
+end
diff --git a/spec/ruby/library/singleton/clone_spec.rb b/spec/ruby/library/singleton/clone_spec.rb
new file mode 100644
index 0000000000..7392d7ff6e
--- /dev/null
+++ b/spec/ruby/library/singleton/clone_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Singleton#clone" do
+ it "is prevented" do
+ lambda { SingletonSpecs::MyClass.instance.clone }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/singleton/dump_spec.rb b/spec/ruby/library/singleton/dump_spec.rb
new file mode 100644
index 0000000000..333e3bc4b0
--- /dev/null
+++ b/spec/ruby/library/singleton/dump_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Singleton#_dump" do
+
+ it "returns an empty string" do
+ SingletonSpecs::MyClass.instance.send(:_dump).should == ""
+ end
+
+ it "returns an empty string from a singleton subclass" do
+ SingletonSpecs::MyClassChild.instance.send(:_dump).should == ""
+ end
+
+end
diff --git a/spec/ruby/library/singleton/dup_spec.rb b/spec/ruby/library/singleton/dup_spec.rb
new file mode 100644
index 0000000000..c5c02a0357
--- /dev/null
+++ b/spec/ruby/library/singleton/dup_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Singleton#dup" do
+ it "is prevented" do
+ lambda { SingletonSpecs::MyClass.instance.dup }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/singleton/fixtures/classes.rb b/spec/ruby/library/singleton/fixtures/classes.rb
new file mode 100644
index 0000000000..c718ebaec8
--- /dev/null
+++ b/spec/ruby/library/singleton/fixtures/classes.rb
@@ -0,0 +1,18 @@
+require 'singleton'
+
+module SingletonSpecs
+ class MyClass
+ attr_accessor :data
+ include Singleton
+ end
+
+ class NewSpec
+ include Singleton
+ end
+
+ class MyClassChild < MyClass
+ end
+
+ class NotInstantiated < MyClass
+ end
+end
diff --git a/spec/ruby/library/singleton/instance_spec.rb b/spec/ruby/library/singleton/instance_spec.rb
new file mode 100644
index 0000000000..1679728d4c
--- /dev/null
+++ b/spec/ruby/library/singleton/instance_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Singleton.instance" do
+ it "returns an instance of the singleton class" do
+ SingletonSpecs::MyClass.instance.should be_kind_of(SingletonSpecs::MyClass)
+ end
+
+ it "returns the same instance for multiple calls to instance" do
+ SingletonSpecs::MyClass.instance.should equal(SingletonSpecs::MyClass.instance)
+ end
+
+ it "returns an instance of the singleton's subclasses" do
+ SingletonSpecs::MyClassChild.instance.should be_kind_of(SingletonSpecs::MyClassChild)
+ end
+
+ it "returns the same instance for multiple class to instance on subclasses" do
+ SingletonSpecs::MyClassChild.instance.should equal(SingletonSpecs::MyClassChild.instance)
+ end
+
+ it "returns an instance of the singleton's clone" do
+ klone = SingletonSpecs::MyClassChild.clone
+ klone.instance.should be_kind_of(klone)
+ end
+
+ it "returns the same instance for multiple class to instance on clones" do
+ klone = SingletonSpecs::MyClassChild.clone
+ klone.instance.should equal(klone.instance)
+ end
+end
diff --git a/spec/ruby/library/singleton/load_spec.rb b/spec/ruby/library/singleton/load_spec.rb
new file mode 100644
index 0000000000..4c753f9e7a
--- /dev/null
+++ b/spec/ruby/library/singleton/load_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# TODO: change to a.should be_equal(b)
+# TODO: write spec for cloning classes and calling private methods
+# TODO: write spec for private_methods not showing up via extended
+describe "Singleton._load" do
+ it "returns the singleton instance for anything passed in" do
+ klass = SingletonSpecs::MyClass
+ klass._load("").should equal(klass.instance)
+ klass._load("42").should equal(klass.instance)
+ klass._load(42).should equal(klass.instance)
+ end
+
+ it "returns the singleton instance for anything passed in to subclass" do
+ subklass = SingletonSpecs::MyClassChild
+ subklass._load("").should equal(subklass.instance)
+ subklass._load("42").should equal(subklass.instance)
+ subklass._load(42).should equal(subklass.instance)
+ end
+end
diff --git a/spec/ruby/library/singleton/new_spec.rb b/spec/ruby/library/singleton/new_spec.rb
new file mode 100644
index 0000000000..babd50fbc3
--- /dev/null
+++ b/spec/ruby/library/singleton/new_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Singleton.new" do
+ it "is a private method" do
+ lambda { SingletonSpecs::NewSpec.new }.should raise_error(NoMethodError)
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/afamily_spec.rb b/spec/ruby/library/socket/addrinfo/afamily_spec.rb
new file mode 100644
index 0000000000..7229dab9de
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/afamily_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#afamily" do
+ describe "for an ipv4 socket" do
+
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns Socket::AF_INET" do
+ @addrinfo.afamily.should == Socket::AF_INET
+ end
+
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns Socket::AF_INET6" do
+ @addrinfo.afamily.should == Socket::AF_INET6
+ end
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns Socket::AF_UNIX" do
+ @addrinfo.afamily.should == Socket::AF_UNIX
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/bind_spec.rb b/spec/ruby/library/socket/addrinfo/bind_spec.rb
new file mode 100644
index 0000000000..6f78890a4d
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/bind_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Addrinfo#bind" do
+
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 0)
+ end
+
+ after :each do
+ @socket.close unless @socket.closed?
+ end
+
+ it "returns a bound socket when no block is given" do
+ @socket = @addrinfo.bind
+ @socket.should be_kind_of(Socket)
+ @socket.closed?.should be_false
+ end
+
+ it "yields the socket if a block is given" do
+ @addrinfo.bind do |sock|
+ @socket = sock
+ sock.should be_kind_of(Socket)
+ end
+ @socket.closed?.should be_true
+ end
+
+end
diff --git a/spec/ruby/library/socket/addrinfo/canonname_spec.rb b/spec/ruby/library/socket/addrinfo/canonname_spec.rb
new file mode 100644
index 0000000000..a1cc8b3980
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/canonname_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Addrinfo#canonname" do
+
+ before :each do
+ @addrinfos = Addrinfo.getaddrinfo("localhost", 80, :INET, :STREAM, nil, Socket::AI_CANONNAME)
+ end
+
+ it "returns the canonical name for a host" do
+ canonname = @addrinfos.map { |a| a.canonname }.find { |name| name and name.include?("localhost") }
+ if canonname
+ canonname.should include("localhost")
+ else
+ canonname.should == nil
+ end
+ end
+
+ describe 'when the canonical name is not available' do
+ it 'returns nil' do
+ addr = Addrinfo.new(Socket.sockaddr_in(0, '127.0.0.1'))
+
+ addr.canonname.should be_nil
+ end
+ end
+
+end
diff --git a/spec/ruby/library/socket/addrinfo/connect_from_spec.rb b/spec/ruby/library/socket/addrinfo/connect_from_spec.rb
new file mode 100644
index 0000000000..55fce2e159
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/connect_from_spec.rb
@@ -0,0 +1,75 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo#connect_from' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ @addr = Addrinfo.tcp(ip_address, @port)
+ end
+
+ after do
+ @socket.close if @socket
+ @server.close
+ end
+
+ describe 'using separate arguments' do
+ it 'returns a Socket when no block is given' do
+ @socket = @addr.connect_from(ip_address, 0)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket when a block is given' do
+ @addr.connect_from(ip_address, 0) do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'treats the last argument as a set of options if it is a Hash' do
+ @socket = @addr.connect_from(ip_address, 0, timeout: 2)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'binds the socket to the local address' do
+ @socket = @addr.connect_from(ip_address, 0)
+
+ @socket.local_address.ip_address.should == ip_address
+
+ @socket.local_address.ip_port.should > 0
+ @socket.local_address.ip_port.should_not == @port
+ end
+ end
+
+ describe 'using an Addrinfo as the 1st argument' do
+ before do
+ @from_addr = Addrinfo.tcp(ip_address, 0)
+ end
+
+ it 'returns a Socket when no block is given' do
+ @socket = @addr.connect_from(@from_addr)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket when a block is given' do
+ @addr.connect_from(@from_addr) do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'treats the last argument as a set of options if it is a Hash' do
+ @socket = @addr.connect_from(@from_addr, timeout: 2)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'binds the socket to the local address' do
+ @socket = @addr.connect_from(@from_addr)
+
+ @socket.local_address.ip_address.should == ip_address
+
+ @socket.local_address.ip_port.should > 0
+ @socket.local_address.ip_port.should_not == @port
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/connect_spec.rb b/spec/ruby/library/socket/addrinfo/connect_spec.rb
new file mode 100644
index 0000000000..1c2dc609ca
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/connect_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo#connect' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @socket.close if @socket
+ @server.close
+ end
+
+ it 'returns a Socket when no block is given' do
+ addr = Addrinfo.tcp(ip_address, @port)
+ @socket = addr.connect
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields a Socket when a block is given' do
+ addr = Addrinfo.tcp(ip_address, @port)
+ addr.connect do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'accepts a Hash of options' do
+ addr = Addrinfo.tcp(ip_address, @port)
+ @socket = addr.connect(timeout: 2)
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/connect_to_spec.rb b/spec/ruby/library/socket/addrinfo/connect_to_spec.rb
new file mode 100644
index 0000000000..69666da19b
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/connect_to_spec.rb
@@ -0,0 +1,75 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo#connect_to' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ @addr = Addrinfo.tcp(ip_address, 0)
+ end
+
+ after do
+ @socket.close if @socket
+ @server.close
+ end
+
+ describe 'using separate arguments' do
+ it 'returns a Socket when no block is given' do
+ @socket = @addr.connect_to(ip_address, @port)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket when a block is given' do
+ @addr.connect_to(ip_address, @port) do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'treats the last argument as a set of options if it is a Hash' do
+ @socket = @addr.connect_to(ip_address, @port, timeout: 2)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'binds the Addrinfo to the local address' do
+ @socket = @addr.connect_to(ip_address, @port)
+
+ @socket.local_address.ip_address.should == ip_address
+
+ @socket.local_address.ip_port.should > 0
+ @socket.local_address.ip_port.should_not == @port
+ end
+ end
+
+ describe 'using an Addrinfo as the 1st argument' do
+ before do
+ @to_addr = Addrinfo.tcp(ip_address, @port)
+ end
+
+ it 'returns a Socket when no block is given' do
+ @socket = @addr.connect_to(@to_addr)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket when a block is given' do
+ @addr.connect_to(@to_addr) do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'treats the last argument as a set of options if it is a Hash' do
+ @socket = @addr.connect_to(@to_addr, timeout: 2)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'binds the socket to the local address' do
+ @socket = @addr.connect_to(@to_addr)
+
+ @socket.local_address.ip_address.should == ip_address
+
+ @socket.local_address.ip_port.should > 0
+ @socket.local_address.ip_port.should_not == @port
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/family_addrinfo_spec.rb b/spec/ruby/library/socket/addrinfo/family_addrinfo_spec.rb
new file mode 100644
index 0000000000..d3419daaaf
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/family_addrinfo_spec.rb
@@ -0,0 +1,115 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#family_addrinfo' do
+ it 'raises ArgumentError if no arguments are given' do
+ addr = Addrinfo.tcp('127.0.0.1', 0)
+
+ lambda { addr.family_addrinfo }.should raise_error(ArgumentError)
+ end
+
+ describe 'using multiple arguments' do
+ describe 'with an IP Addrinfo' do
+ before do
+ @source = Addrinfo.tcp('127.0.0.1', 0)
+ end
+
+ it 'raises ArgumentError if only 1 argument is given' do
+ lambda { @source.family_addrinfo('127.0.0.1') }.should raise_error(ArgumentError)
+ end
+
+ it 'raises ArgumentError if more than 2 arguments are given' do
+ lambda { @source.family_addrinfo('127.0.0.1', 0, 666) }.should raise_error(ArgumentError)
+ end
+
+ it 'returns an Addrinfo when a host and port are given' do
+ addr = @source.family_addrinfo('127.0.0.1', 0)
+
+ addr.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @source.family_addrinfo('127.0.0.1', 0)
+ end
+
+ it 'uses the same address family as the source Addrinfo' do
+ @addr.afamily.should == @source.afamily
+ end
+
+ it 'uses the same protocol family as the source Addrinfo' do
+ @addr.pfamily.should == @source.pfamily
+ end
+
+ it 'uses the same socket type as the source Addrinfo' do
+ @addr.socktype.should == @source.socktype
+ end
+
+ it 'uses the same protocol as the source Addrinfo' do
+ @addr.protocol.should == @source.protocol
+ end
+ end
+ end
+
+ with_feature :unix_socket do
+ describe 'with a UNIX Addrinfo' do
+ before do
+ @source = Addrinfo.unix('cats')
+ end
+
+ it 'raises ArgumentError if more than 1 argument is given' do
+ lambda { @source.family_addrinfo('foo', 'bar') }.should raise_error(ArgumentError)
+ end
+
+ it 'returns an Addrinfo when a UNIX socket path is given' do
+ addr = @source.family_addrinfo('dogs')
+
+ addr.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @source.family_addrinfo('dogs')
+ end
+
+ it 'uses AF_UNIX as the address family' do
+ @addr.afamily.should == Socket::AF_UNIX
+ end
+
+ it 'uses PF_UNIX as the protocol family' do
+ @addr.pfamily.should == Socket::PF_UNIX
+ end
+
+ it 'uses the given socket path' do
+ @addr.unix_path.should == 'dogs'
+ end
+ end
+ end
+ end
+ end
+
+ describe 'using an Addrinfo as the 1st argument' do
+ before do
+ @source = Addrinfo.tcp('127.0.0.1', 0)
+ end
+
+ it 'returns the input Addrinfo' do
+ input = Addrinfo.tcp('127.0.0.2', 0)
+ @source.family_addrinfo(input).should == input
+ end
+
+ it 'raises ArgumentError if more than 1 argument is given' do
+ input = Addrinfo.tcp('127.0.0.2', 0)
+ lambda { @source.family_addrinfo(input, 666) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the protocol families don't match" do
+ input = Addrinfo.tcp('::1', 0)
+ lambda { @source.family_addrinfo(input) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the socket types don't match" do
+ input = Addrinfo.udp('127.0.0.1', 0)
+ lambda { @source.family_addrinfo(input) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/foreach_spec.rb b/spec/ruby/library/socket/addrinfo/foreach_spec.rb
new file mode 100644
index 0000000000..6ec8fab905
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/foreach_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo.foreach' do
+ it 'yields Addrinfo instances to the supplied block' do
+ Addrinfo.foreach('127.0.0.1', 80) do |addr|
+ addr.should be_an_instance_of(Addrinfo)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/getaddrinfo_spec.rb b/spec/ruby/library/socket/addrinfo/getaddrinfo_spec.rb
new file mode 100644
index 0000000000..67fad73815
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/getaddrinfo_spec.rb
@@ -0,0 +1,91 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo.getaddrinfo' do
+ it 'returns an Array of Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('127.0.0.1', 80)
+
+ array.should be_an_instance_of(Array)
+ array[0].should be_an_instance_of(Addrinfo)
+ end
+
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ it 'sets the IP address of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo(ip_address, 80)
+
+ array[0].ip_address.should == ip_address
+ end
+
+ it 'sets the port of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo(ip_address, 80)
+
+ array[0].ip_port.should == 80
+ end
+
+ it 'sets the address family of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo(ip_address, 80)
+
+ array[0].afamily.should == family
+ end
+
+ it 'sets the protocol family of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo(ip_address, 80)
+
+ array[0].pfamily.should == family
+ end
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ it 'sets a custom protocol family of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('::1', 80, Socket::PF_INET6)
+
+ array[0].pfamily.should == Socket::PF_INET6
+ end
+
+ it 'sets a corresponding address family based on a custom protocol family' do
+ array = Addrinfo.getaddrinfo('::1', 80, Socket::PF_INET6)
+
+ array[0].afamily.should == Socket::AF_INET6
+ end
+ end
+
+ platform_is_not :windows do
+ it 'sets the default socket type of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('127.0.0.1', 80)
+ possible = [Socket::SOCK_STREAM, Socket::SOCK_DGRAM]
+
+ possible.should include(array[0].socktype)
+ end
+ end
+
+ it 'sets a custom socket type of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('127.0.0.1', 80, nil, Socket::SOCK_DGRAM)
+
+ array[0].socktype.should == Socket::SOCK_DGRAM
+ end
+
+ platform_is_not :windows do
+ it 'sets the default socket protocol of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('127.0.0.1', 80)
+ possible = [Socket::IPPROTO_TCP, Socket::IPPROTO_UDP]
+
+ possible.should include(array[0].protocol)
+ end
+ end
+
+ platform_is_not :'solaris2.10' do # i386-solaris
+ it 'sets a custom socket protocol of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('127.0.0.1', 80, nil, nil, Socket::IPPROTO_UDP)
+
+ array[0].protocol.should == Socket::IPPROTO_UDP
+ end
+ end
+
+ platform_is_not :solaris do
+ it 'sets the canonical name when AI_CANONNAME is given as a flag' do
+ array = Addrinfo.getaddrinfo('localhost', 80, nil, nil, nil, Socket::AI_CANONNAME)
+
+ array[0].canonname.should be_an_instance_of(String)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb b/spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb
new file mode 100644
index 0000000000..c5284f1c0f
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo#getnameinfo' do
+ describe 'using an IP Addrinfo' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @addr = Addrinfo.tcp(ip_address, 21)
+ end
+
+ it 'returns the node and service names' do
+ host, service = @addr.getnameinfo
+ service.should == 'ftp'
+ end
+
+ it 'accepts flags as an Integer as the first argument' do
+ host, service = @addr.getnameinfo(Socket::NI_NUMERICSERV)
+ service.should == '21'
+ end
+ end
+ end
+
+ platform_is :linux do
+ with_feature :unix_socket do
+ describe 'using a UNIX Addrinfo' do
+ before do
+ @addr = Addrinfo.unix('cats')
+ @host = Socket.gethostname
+ end
+
+ it 'returns the hostname and UNIX socket path' do
+ host, path = @addr.getnameinfo
+
+ host.should == @host
+ path.should == 'cats'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/initialize_spec.rb b/spec/ruby/library/socket/addrinfo/initialize_spec.rb
new file mode 100644
index 0000000000..460fc28d49
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/initialize_spec.rb
@@ -0,0 +1,587 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#initialize" do
+
+ describe "with a sockaddr string" do
+
+ describe "without a family" do
+ before :each do
+ @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1"))
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "2001:db8::1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 25
+ end
+
+ it "returns the Socket::UNSPEC pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_UNSPEC
+ end
+
+ it 'returns AF_INET as the default address family' do
+ addr = Addrinfo.new(Socket.sockaddr_in(80, '127.0.0.1'))
+
+ addr.afamily.should == Socket::AF_INET
+ end
+
+ it "returns the INET6 afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET6
+ end
+
+ it "returns the 0 socket type" do
+ @addrinfo.socktype.should == 0
+ end
+
+ it "returns the 0 protocol" do
+ @addrinfo.protocol.should == 0
+ end
+ end
+
+ describe "with a family given" do
+ before :each do
+ @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1"), Socket::PF_INET6)
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "2001:db8::1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 25
+ end
+
+ it "returns the Socket::UNSPEC pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_INET6
+ end
+
+ it "returns the INET6 afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET6
+ end
+
+ it "returns the 0 socket type" do
+ @addrinfo.socktype.should == 0
+ end
+
+ it "returns the 0 protocol" do
+ @addrinfo.protocol.should == 0
+ end
+ end
+
+ describe "with a family and socket type" do
+ before :each do
+ @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1"), Socket::PF_INET6, Socket::SOCK_STREAM)
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "2001:db8::1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 25
+ end
+
+ it "returns the Socket::UNSPEC pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_INET6
+ end
+
+ it "returns the INET6 afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET6
+ end
+
+ it "returns the 0 socket type" do
+ @addrinfo.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it "returns the 0 protocol" do
+ @addrinfo.protocol.should == 0
+ end
+ end
+
+ describe "with a family, socket type and protocol" do
+ before :each do
+ @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1"), Socket::PF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP)
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "2001:db8::1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 25
+ end
+
+ it "returns the Socket::UNSPEC pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_INET6
+ end
+
+ it "returns the INET6 afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET6
+ end
+
+ it "returns the specified socket type" do
+ @addrinfo.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it "returns the specified protocol" do
+ @addrinfo.protocol.should == Socket::IPPROTO_TCP
+ end
+ end
+
+ end
+
+ describe "with a sockaddr array" do
+
+ describe "without a family" do
+ before :each do
+ @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"])
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "127.0.0.1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 46102
+ end
+
+ it "returns the Socket::PF_INET pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_INET
+ end
+
+ it "returns the INET6 afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET
+ end
+
+ it "returns the 0 socket type" do
+ @addrinfo.socktype.should == 0
+ end
+
+ it "returns the 0 protocol" do
+ @addrinfo.protocol.should == 0
+ end
+ end
+
+ describe 'with a valid IP address' do
+ # Uses AF_INET6 since AF_INET is the default, making it a better test
+ # that Addrinfo actually sets the family correctly.
+ before do
+ @sockaddr = ['AF_INET6', 80, 'hostname', '::1']
+ end
+
+ it 'returns an Addrinfo with the correct IP' do
+ addr = Addrinfo.new(@sockaddr)
+
+ addr.ip_address.should == '::1'
+ end
+
+ it 'returns an Addrinfo with the correct address family' do
+ addr = Addrinfo.new(@sockaddr)
+
+ addr.afamily.should == Socket::AF_INET6
+ end
+
+ it 'returns an Addrinfo with the correct protocol family' do
+ addr = Addrinfo.new(@sockaddr)
+
+ addr.pfamily.should == Socket::PF_INET6
+ end
+
+ it 'returns an Addrinfo with the correct port' do
+ addr = Addrinfo.new(@sockaddr)
+
+ addr.ip_port.should == 80
+ end
+ end
+
+ describe 'with an invalid IP address' do
+ it 'raises SocketError' do
+ block = lambda { Addrinfo.new(['AF_INET6', 80, 'hostname', '127.0.0.1']) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+
+ describe "with a family given" do
+ before :each do
+ @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"], Socket::PF_INET)
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "127.0.0.1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 46102
+ end
+
+ it "returns the Socket::UNSPEC pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_INET
+ end
+
+ it "returns the INET6 afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET
+ end
+
+ it "returns the 0 socket type" do
+ @addrinfo.socktype.should == 0
+ end
+
+ it "returns the 0 protocol" do
+ @addrinfo.protocol.should == 0
+ end
+ end
+
+ describe "with a family and socket type" do
+ before :each do
+ @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"], Socket::PF_INET, Socket::SOCK_STREAM)
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "127.0.0.1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 46102
+ end
+
+ it "returns the Socket::UNSPEC pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_INET
+ end
+
+ it "returns the INET6 afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET
+ end
+
+ it "returns the 0 socket type" do
+ @addrinfo.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it "returns the 0 protocol" do
+ @addrinfo.protocol.should == 0
+ end
+
+ [:SOCK_STREAM, :SOCK_DGRAM, :SOCK_RAW].each do |type|
+ it "overwrites the socket type #{type}" do
+ sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1']
+
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(sockaddr, nil, value)
+
+ addr.socktype.should == value
+ end
+ end
+
+ with_feature :sock_packet do
+ [:SOCK_SEQPACKET].each do |type|
+ it "overwrites the socket type #{type}" do
+ sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1']
+
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(sockaddr, nil, value)
+
+ addr.socktype.should == value
+ end
+ end
+ end
+
+ it "raises SocketError when using SOCK_RDM" do
+ sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1']
+ value = Socket::SOCK_RDM
+ block = lambda { Addrinfo.new(sockaddr, nil, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+
+ describe "with a family, socket type and protocol" do
+ before :each do
+ @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"], Socket::PF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP)
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "127.0.0.1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 46102
+ end
+
+ it "returns the Socket::UNSPEC pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_INET
+ end
+
+ it "returns the INET6 afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET
+ end
+
+ it "returns the 0 socket type" do
+ @addrinfo.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it "returns the specified protocol" do
+ @addrinfo.protocol.should == Socket::IPPROTO_TCP
+ end
+ end
+ end
+
+ describe 'using an Array with extra arguments' do
+ describe 'with the AF_INET6 address family and an explicit protocol family' do
+ before do
+ @sockaddr = ['AF_INET6', 80, 'hostname', '127.0.0.1']
+ end
+
+ it "raises SocketError when using any Socket constant except except AF_INET(6)/PF_INET(6)" do
+ Socket.constants.grep(/(^AF_|^PF_)(?!INET)/).each do |constant|
+ value = Socket.const_get(constant)
+ -> {
+ Addrinfo.new(@sockaddr, value)
+ }.should raise_error(SocketError)
+ end
+ end
+ end
+
+ describe 'with the AF_INET address family and an explicit socket protocol' do
+ before do
+ @sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1']
+ end
+
+ describe 'and no socket type is given' do
+ valid = [:IPPROTO_IP, :IPPROTO_UDP, :IPPROTO_HOPOPTS]
+
+ valid.each do |type|
+ it "overwrites the protocol when using #{type}" do
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(@sockaddr, nil, nil, value)
+
+ addr.protocol.should == value
+ end
+ end
+
+ platform_is_not :windows, :aix, :solaris do
+ (Socket.constants.grep(/^IPPROTO/) - valid).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = lambda { Addrinfo.new(@sockaddr, nil, nil, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+
+ describe 'and the socket type is set to SOCK_DGRAM' do
+ before do
+ @socktype = Socket::SOCK_DGRAM
+ end
+
+ valid = [:IPPROTO_IP, :IPPROTO_UDP, :IPPROTO_HOPOPTS]
+
+ valid.each do |type|
+ it "overwrites the protocol when using #{type}" do
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(@sockaddr, nil, @socktype, value)
+
+ addr.protocol.should == value
+ end
+ end
+
+ platform_is_not :windows, :aix, :solaris do
+ (Socket.constants.grep(/^IPPROTO/) - valid).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = lambda { Addrinfo.new(@sockaddr, nil, @socktype, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+
+ with_feature :sock_packet do
+ describe 'and the socket type is set to SOCK_PACKET' do
+ before do
+ @socktype = Socket::SOCK_PACKET
+ end
+
+ Socket.constants.grep(/^IPPROTO/).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = lambda { Addrinfo.new(@sockaddr, nil, @socktype, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+
+ describe 'and the socket type is set to SOCK_RAW' do
+ before do
+ @socktype = Socket::SOCK_RAW
+ end
+
+ Socket.constants.grep(/^IPPROTO/).each do |type|
+ it "overwrites the protocol when using #{type}" do
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(@sockaddr, nil, @socktype, value)
+
+ addr.protocol.should == value
+ end
+ end
+ end
+
+ describe 'and the socket type is set to SOCK_RDM' do
+ before do
+ @socktype = Socket::SOCK_RDM
+ end
+
+ Socket.constants.grep(/^IPPROTO/).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = lambda { Addrinfo.new(@sockaddr, nil, @socktype, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+
+ platform_is :linux do
+ describe 'and the socket type is set to SOCK_SEQPACKET' do
+ before do
+ @socktype = Socket::SOCK_SEQPACKET
+ end
+
+ valid = [:IPPROTO_IP, :IPPROTO_HOPOPTS]
+
+ valid.each do |type|
+ it "overwrites the protocol when using #{type}" do
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(@sockaddr, nil, @socktype, value)
+
+ addr.protocol.should == value
+ end
+ end
+
+ (Socket.constants.grep(/^IPPROTO/) - valid).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = lambda { Addrinfo.new(@sockaddr, nil, @socktype, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+
+ describe 'and the socket type is set to SOCK_STREAM' do
+ before do
+ @socktype = Socket::SOCK_STREAM
+ end
+
+ valid = [:IPPROTO_IP, :IPPROTO_TCP, :IPPROTO_HOPOPTS]
+
+ valid.each do |type|
+ it "overwrites the protocol when using #{type}" do
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(@sockaddr, nil, @socktype, value)
+
+ addr.protocol.should == value
+ end
+ end
+
+ platform_is_not :windows, :aix, :solaris do
+ (Socket.constants.grep(/^IPPROTO/) - valid).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = lambda { Addrinfo.new(@sockaddr, nil, @socktype, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ describe 'with Symbols' do
+ before do
+ @sockaddr = Socket.sockaddr_in(80, '127.0.0.1')
+ end
+
+ it 'returns an Addrinfo with :PF_INET family' do
+ addr = Addrinfo.new(@sockaddr, :PF_INET)
+
+ addr.pfamily.should == Socket::PF_INET
+ end
+
+ it 'returns an Addrinfo with :INET family' do
+ addr = Addrinfo.new(@sockaddr, :INET)
+
+ addr.pfamily.should == Socket::PF_INET
+ end
+
+ it 'returns an Addrinfo with :SOCK_STREAM as the socket type' do
+ addr = Addrinfo.new(@sockaddr, nil, :SOCK_STREAM)
+
+ addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'returns an Addrinfo with :STREAM as the socket type' do
+ addr = Addrinfo.new(@sockaddr, nil, :STREAM)
+
+ addr.socktype.should == Socket::SOCK_STREAM
+ end
+ end
+
+ describe 'with Strings' do
+ before do
+ @sockaddr = Socket.sockaddr_in(80, '127.0.0.1')
+ end
+
+ it 'returns an Addrinfo with "PF_INET" family' do
+ addr = Addrinfo.new(@sockaddr, 'PF_INET')
+
+ addr.pfamily.should == Socket::PF_INET
+ end
+
+ it 'returns an Addrinfo with "INET" family' do
+ addr = Addrinfo.new(@sockaddr, 'INET')
+
+ addr.pfamily.should == Socket::PF_INET
+ end
+
+ it 'returns an Addrinfo with "SOCK_STREAM" as the socket type' do
+ addr = Addrinfo.new(@sockaddr, nil, 'SOCK_STREAM')
+
+ addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'returns an Addrinfo with "STREAM" as the socket type' do
+ addr = Addrinfo.new(@sockaddr, nil, 'STREAM')
+
+ addr.socktype.should == Socket::SOCK_STREAM
+ end
+ end
+
+ with_feature :unix_socket do
+ describe 'using separate arguments for a Unix socket' do
+ before do
+ @sockaddr = Socket.pack_sockaddr_un('socket')
+ end
+
+ it 'returns an Addrinfo with the correct unix path' do
+ Addrinfo.new(@sockaddr).unix_path.should == 'socket'
+ end
+
+ it 'returns an Addrinfo with the correct protocol family' do
+ Addrinfo.new(@sockaddr).pfamily.should == Socket::PF_UNSPEC
+ end
+
+ it 'returns an Addrinfo with the correct address family' do
+ Addrinfo.new(@sockaddr).afamily.should == Socket::AF_UNIX
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/inspect_sockaddr_spec.rb b/spec/ruby/library/socket/addrinfo/inspect_sockaddr_spec.rb
new file mode 100644
index 0000000000..70ca4dd4d7
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/inspect_sockaddr_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../spec_helper'
+
+
+describe 'Addrinfo#inspect_sockaddr' do
+ describe 'using an IPv4 address' do
+ it 'returns a String containing the IP address and port number' do
+ addr = Addrinfo.tcp('127.0.0.1', 80)
+
+ addr.inspect_sockaddr.should == '127.0.0.1:80'
+ end
+
+ it 'returns a String containing just the IP address when no port is given' do
+ addr = Addrinfo.tcp('127.0.0.1', 0)
+
+ addr.inspect_sockaddr.should == '127.0.0.1'
+ end
+ end
+
+ describe 'using an IPv6 address' do
+ before :each do
+ @ip = '2001:0db8:85a3:0000:0000:8a2e:0370:7334'
+ end
+
+ it 'returns a String containing the IP address and port number' do
+ Addrinfo.tcp('::1', 80).inspect_sockaddr.should == '[::1]:80'
+ Addrinfo.tcp(@ip, 80).inspect_sockaddr.should == '[2001:db8:85a3::8a2e:370:7334]:80'
+ end
+
+ it 'returns a String containing just the IP address when no port is given' do
+ Addrinfo.tcp('::1', 0).inspect_sockaddr.should == '::1'
+ Addrinfo.tcp(@ip, 0).inspect_sockaddr.should == '2001:db8:85a3::8a2e:370:7334'
+ end
+ end
+
+ with_feature :unix_socket do
+ describe 'using a UNIX path' do
+ it 'returns a String containing the UNIX path' do
+ addr = Addrinfo.unix('/foo/bar')
+
+ addr.inspect_sockaddr.should == '/foo/bar'
+ end
+
+ it 'returns a String containing the UNIX path when using a relative path' do
+ addr = Addrinfo.unix('foo')
+
+ addr.inspect_sockaddr.should == 'UNIX foo'
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/inspect_spec.rb b/spec/ruby/library/socket/addrinfo/inspect_spec.rb
new file mode 100644
index 0000000000..98e1e83ffa
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/inspect_spec.rb
@@ -0,0 +1,65 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#inspect' do
+ describe 'using an IPv4 Addrinfo' do
+ it 'returns a String when using a TCP Addrinfo' do
+ addr = Addrinfo.tcp('127.0.0.1', 80)
+
+ addr.inspect.should == '#<Addrinfo: 127.0.0.1:80 TCP>'
+ end
+
+ it 'returns a String when using an UDP Addrinfo' do
+ addr = Addrinfo.udp('127.0.0.1', 80)
+
+ addr.inspect.should == '#<Addrinfo: 127.0.0.1:80 UDP>'
+ end
+
+ it 'returns a String when using an Addrinfo without a port' do
+ addr = Addrinfo.ip('127.0.0.1')
+
+ addr.inspect.should == '#<Addrinfo: 127.0.0.1>'
+ end
+ end
+
+ describe 'using an IPv6 Addrinfo' do
+ it 'returns a String when using a TCP Addrinfo' do
+ addr = Addrinfo.tcp('::1', 80)
+
+ addr.inspect.should == '#<Addrinfo: [::1]:80 TCP>'
+ end
+
+ it 'returns a String when using an UDP Addrinfo' do
+ addr = Addrinfo.udp('::1', 80)
+
+ addr.inspect.should == '#<Addrinfo: [::1]:80 UDP>'
+ end
+
+ it 'returns a String when using an Addrinfo without a port' do
+ addr = Addrinfo.ip('::1')
+
+ addr.inspect.should == '#<Addrinfo: ::1>'
+ end
+ end
+
+ with_feature :unix_socket do
+ describe 'using a UNIX Addrinfo' do
+ it 'returns a String' do
+ addr = Addrinfo.unix('/foo')
+
+ addr.inspect.should == '#<Addrinfo: /foo SOCK_STREAM>'
+ end
+
+ it 'returns a String when using a relative UNIX path' do
+ addr = Addrinfo.unix('foo')
+
+ addr.inspect.should == '#<Addrinfo: UNIX foo SOCK_STREAM>'
+ end
+
+ it 'returns a String when using a DGRAM socket' do
+ addr = Addrinfo.unix('/foo', Socket::SOCK_DGRAM)
+
+ addr.inspect.should == '#<Addrinfo: /foo SOCK_DGRAM>'
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ip_address_spec.rb b/spec/ruby/library/socket/addrinfo/ip_address_spec.rb
new file mode 100644
index 0000000000..004de37254
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ip_address_spec.rb
@@ -0,0 +1,85 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ip_address" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns the ip address" do
+ @addrinfo.ip_address.should == "127.0.0.1"
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns the ip address" do
+ @addrinfo.ip_address.should == "::1"
+ end
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "raises an exception" do
+ lambda { @addrinfo.ip_address }.should raise_error(SocketError)
+ end
+ end
+ end
+
+ describe 'with an Array as the socket address' do
+ it 'returns the IP as a String' do
+ sockaddr = ['AF_INET', 80, 'localhost', '127.0.0.1']
+ addr = Addrinfo.new(sockaddr)
+
+ addr.ip_address.should == '127.0.0.1'
+ end
+ end
+
+ describe 'without an IP address' do
+ before do
+ @ips = ['127.0.0.1', '0.0.0.0', '::1']
+ end
+
+ # Both these cases seem to return different values at times on MRI. Since
+ # this is network dependent we can't rely on an exact IP being returned.
+ it 'returns the local IP address when using an empty String as the IP' do
+ sockaddr = Socket.sockaddr_in(80, '')
+ addr = Addrinfo.new(sockaddr)
+
+ @ips.include?(addr.ip_address).should == true
+ end
+
+ it 'returns the local IP address when using nil as the IP' do
+ sockaddr = Socket.sockaddr_in(80, nil)
+ addr = Addrinfo.new(sockaddr)
+
+ @ips.include?(addr.ip_address).should == true
+ end
+ end
+
+ # On MRI calling Addrinfo#ip_address with AF_UNSPEC as the address family is
+ # supposed to raise a SocketError. MRI however doesn't provide a way to
+ # actually initialize an Addrinfo with AF_UNSPEC, nor does it allow stubbing
+ # of any methods since Addrinfo doesn't use any Ruby methods for checking the
+ # IP address. As a result we can only run this test on Rubinius.
+ with_feature :pure_ruby_addrinfo do
+ describe 'with a non IPv4 or IPv6 address' do
+ it 'raises SocketError' do
+ sockaddr = Socket.sockaddr_in(80, '127.0.0.1')
+ addr = Addrinfo.new(sockaddr)
+
+ addr.stub!(:ipv4?).and_return(false)
+ addr.stub!(:ipv6?).and_return(false)
+
+ lambda { addr.ip_address }.should raise_error(SocketError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ip_port_spec.rb b/spec/ruby/library/socket/addrinfo/ip_port_spec.rb
new file mode 100644
index 0000000000..6cf9e7a18e
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ip_port_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ip_port" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns the port" do
+ @addrinfo.ip_port.should == 80
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns the port" do
+ @addrinfo.ip_port.should == 80
+ end
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "raises an exception" do
+ lambda { @addrinfo.ip_port }.should raise_error(SocketError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ip_spec.rb b/spec/ruby/library/socket/addrinfo/ip_spec.rb
new file mode 100644
index 0000000000..80e7a62df7
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ip_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Addrinfo#ip?" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns true" do
+ @addrinfo.ip?.should be_true
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns true" do
+ @addrinfo.ip?.should be_true
+ end
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ip?.should be_false
+ end
+ end
+ end
+end
+
+describe 'Addrinfo.ip' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ it 'returns an Addrinfo instance' do
+ Addrinfo.ip(ip_address).should be_an_instance_of(Addrinfo)
+ end
+
+ it 'sets the IP address' do
+ Addrinfo.ip(ip_address).ip_address.should == ip_address
+ end
+
+ it 'sets the port to 0' do
+ Addrinfo.ip(ip_address).ip_port.should == 0
+ end
+
+ it 'sets the address family' do
+ Addrinfo.ip(ip_address).afamily.should == family
+ end
+
+ it 'sets the protocol family' do
+ Addrinfo.ip(ip_address).pfamily.should == family
+ end
+
+ it 'sets the socket type to 0' do
+ Addrinfo.ip(ip_address).socktype.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ip_unpack_spec.rb b/spec/ruby/library/socket/addrinfo/ip_unpack_spec.rb
new file mode 100644
index 0000000000..57ae79a6c8
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ip_unpack_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ip_unpack" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns the ip address and port pair" do
+ @addrinfo.ip_unpack.should == ["127.0.0.1", 80]
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns the ip address and port pair" do
+ @addrinfo.ip_unpack.should == ["::1", 80]
+ end
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "raises an exception" do
+ lambda { @addrinfo.ip_unpack }.should raise_error(SocketError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv4_loopback_spec.rb b/spec/ruby/library/socket/addrinfo/ipv4_loopback_spec.rb
new file mode 100644
index 0000000000..f5bab711db
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv4_loopback_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ipv4_loopback?" do
+ describe "for an ipv4 socket" do
+ it "returns true for the loopback address" do
+ Addrinfo.ip('127.0.0.1').ipv4_loopback?.should == true
+ Addrinfo.ip('127.0.0.2').ipv4_loopback?.should == true
+ Addrinfo.ip('127.255.0.1').ipv4_loopback?.should == true
+ Addrinfo.ip('127.255.255.255').ipv4_loopback?.should == true
+ end
+
+ it "returns false for another address" do
+ Addrinfo.ip('255.255.255.0').ipv4_loopback?.should be_false
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @loopback = Addrinfo.tcp("::1", 80)
+ @other = Addrinfo.tcp("::", 80)
+ end
+
+ it "returns false for the loopback address" do
+ @loopback.ipv4_loopback?.should be_false
+ end
+
+ it "returns false for another address" do
+ @other.ipv4_loopback?.should be_false
+ end
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ipv4_loopback?.should be_false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv4_multicast_spec.rb b/spec/ruby/library/socket/addrinfo/ipv4_multicast_spec.rb
new file mode 100644
index 0000000000..81a68d3d13
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv4_multicast_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ipv4_multicast?" do
+ it 'returns true for a multicast address' do
+ Addrinfo.ip('224.0.0.0').ipv4_multicast?.should == true
+ Addrinfo.ip('224.0.0.9').ipv4_multicast?.should == true
+ Addrinfo.ip('239.255.255.250').ipv4_multicast?.should == true
+ end
+
+ it 'returns false for a regular addrss' do
+ Addrinfo.ip('8.8.8.8').ipv4_multicast?.should == false
+ end
+
+ it 'returns false for an IPv6 address' do
+ Addrinfo.ip('::1').ipv4_multicast?.should == false
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ipv4_multicast?.should be_false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv4_private_spec.rb b/spec/ruby/library/socket/addrinfo/ipv4_private_spec.rb
new file mode 100644
index 0000000000..733577609e
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv4_private_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ipv4_private?" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @private = Addrinfo.tcp("10.0.0.1", 80)
+ @other = Addrinfo.tcp("0.0.0.0", 80)
+ end
+
+ it "returns true for a private address" do
+ Addrinfo.ip('10.0.0.0').ipv4_private?.should == true
+ Addrinfo.ip('10.0.0.5').ipv4_private?.should == true
+
+ Addrinfo.ip('172.16.0.0').ipv4_private?.should == true
+ Addrinfo.ip('172.16.0.5').ipv4_private?.should == true
+
+ Addrinfo.ip('192.168.0.0').ipv4_private?.should == true
+ Addrinfo.ip('192.168.0.5').ipv4_private?.should == true
+ end
+
+ it "returns false for a public address" do
+ @other.ipv4_private?.should be_false
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @other = Addrinfo.tcp("::", 80)
+ end
+
+ it "returns false" do
+ @other.ipv4_private?.should be_false
+ end
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ipv4_private?.should be_false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv4_spec.rb b/spec/ruby/library/socket/addrinfo/ipv4_spec.rb
new file mode 100644
index 0000000000..7cba8209b6
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv4_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ipv4?" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("10.0.0.1", 80)
+ end
+
+ it "returns true" do
+ @addrinfo.ipv4?.should be_true
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns false" do
+ @addrinfo.ipv4?.should be_false
+ end
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ipv4?.should be_false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_linklocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_linklocal_spec.rb
new file mode 100644
index 0000000000..291b4d7d6b
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_linklocal_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+guard -> { SocketSpecs.ipv6_available? } do
+ describe 'Addrinfo#ipv6_linklocal?' do
+ platform_is_not :aix do
+ it 'returns true for a link-local address' do
+ Addrinfo.ip('fe80::').ipv6_linklocal?.should == true
+ Addrinfo.ip('fe81::').ipv6_linklocal?.should == true
+ Addrinfo.ip('fe8f::').ipv6_linklocal?.should == true
+ Addrinfo.ip('fe80::1').ipv6_linklocal?.should == true
+ end
+ end
+
+ it 'returns false for a regular address' do
+ Addrinfo.ip('::1').ipv6_linklocal?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_linklocal?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_loopback_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_loopback_spec.rb
new file mode 100644
index 0000000000..9ff8f107bf
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_loopback_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ipv6_loopback?" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @loopback = Addrinfo.tcp("127.0.0.1", 80)
+ @other = Addrinfo.tcp("0.0.0.0", 80)
+ end
+
+ it "returns false for the loopback address" do
+ @loopback.ipv6_loopback?.should be_false
+ end
+
+ it "returns false for another address" do
+ @other.ipv6_loopback?.should be_false
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @loopback = Addrinfo.tcp("::1", 80)
+ @other = Addrinfo.tcp("::", 80)
+ end
+
+ it "returns true for the loopback address" do
+ @loopback.ipv6_loopback?.should be_true
+ end
+
+ it "returns false for another address" do
+ @other.ipv6_loopback?.should be_false
+ end
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ipv6_loopback?.should be_false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_mc_global_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_mc_global_spec.rb
new file mode 100644
index 0000000000..9c8e4dccb4
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_mc_global_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_mc_global?' do
+ it 'returns true for a multi-cast address in the global scope' do
+ Addrinfo.ip('ff1e::').ipv6_mc_global?.should == true
+ Addrinfo.ip('fffe::').ipv6_mc_global?.should == true
+ Addrinfo.ip('ff0e::').ipv6_mc_global?.should == true
+ Addrinfo.ip('ff1e::1').ipv6_mc_global?.should == true
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_mc_global?.should == false
+ Addrinfo.ip('ff1a::').ipv6_mc_global?.should == false
+ Addrinfo.ip('ff1f::1').ipv6_mc_global?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_mc_global?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_mc_linklocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_mc_linklocal_spec.rb
new file mode 100644
index 0000000000..dd52a9ad8b
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_mc_linklocal_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_mc_linklocal?' do
+ it 'returns true for a multi-cast link-local address' do
+ Addrinfo.ip('ff12::').ipv6_mc_linklocal?.should == true
+ Addrinfo.ip('ff02::').ipv6_mc_linklocal?.should == true
+ Addrinfo.ip('fff2::').ipv6_mc_linklocal?.should == true
+ Addrinfo.ip('ff12::1').ipv6_mc_linklocal?.should == true
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_mc_linklocal?.should == false
+ Addrinfo.ip('fff1::').ipv6_mc_linklocal?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_mc_linklocal?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_mc_nodelocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_mc_nodelocal_spec.rb
new file mode 100644
index 0000000000..b3cf5ffbab
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_mc_nodelocal_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_mc_nodelocal?' do
+ it 'returns true for a multi-cast node-local address' do
+ Addrinfo.ip('ff11::').ipv6_mc_nodelocal?.should == true
+ Addrinfo.ip('ff01::').ipv6_mc_nodelocal?.should == true
+ Addrinfo.ip('fff1::').ipv6_mc_nodelocal?.should == true
+ Addrinfo.ip('ff11::1').ipv6_mc_nodelocal?.should == true
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_mc_nodelocal?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_mc_nodelocal?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_mc_orglocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_mc_orglocal_spec.rb
new file mode 100644
index 0000000000..90653dd49c
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_mc_orglocal_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_mc_orglocal?' do
+ it 'returns true for a multi-cast org-local address' do
+ Addrinfo.ip('ff18::').ipv6_mc_orglocal?.should == true
+ Addrinfo.ip('ff08::').ipv6_mc_orglocal?.should == true
+ Addrinfo.ip('fff8::').ipv6_mc_orglocal?.should == true
+ Addrinfo.ip('ff18::1').ipv6_mc_orglocal?.should == true
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_mc_orglocal?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_mc_orglocal?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_mc_sitelocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_mc_sitelocal_spec.rb
new file mode 100644
index 0000000000..686b625e0d
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_mc_sitelocal_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_mc_sitelocal?' do
+ it 'returns true for a multi-cast site-local address' do
+ Addrinfo.ip('ff15::').ipv6_mc_sitelocal?.should == true
+ Addrinfo.ip('ff05::').ipv6_mc_sitelocal?.should == true
+ Addrinfo.ip('fff5::').ipv6_mc_sitelocal?.should == true
+ Addrinfo.ip('ff15::1').ipv6_mc_sitelocal?.should == true
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_mc_sitelocal?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_mc_sitelocal?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_multicast_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_multicast_spec.rb
new file mode 100644
index 0000000000..c25322869c
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_multicast_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ipv6_multicast?" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @multicast = Addrinfo.tcp("224.0.0.1", 80)
+ @other = Addrinfo.tcp("0.0.0.0", 80)
+ end
+
+ it "returns true for a multicast address" do
+ @multicast.ipv6_multicast?.should be_false
+ end
+
+ it "returns false for another address" do
+ @other.ipv6_multicast?.should be_false
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ it "returns true for a multicast address" do
+ Addrinfo.ip('ff00::').ipv6_multicast?.should == true
+ Addrinfo.ip('ff00::1').ipv6_multicast?.should == true
+ Addrinfo.ip('ff08::1').ipv6_multicast?.should == true
+ Addrinfo.ip('fff8::1').ipv6_multicast?.should == true
+
+ Addrinfo.ip('ff02::').ipv6_multicast?.should == true
+ Addrinfo.ip('ff02::1').ipv6_multicast?.should == true
+ Addrinfo.ip('ff0f::').ipv6_multicast?.should == true
+ end
+
+ it "returns false for another address" do
+ Addrinfo.ip('::1').ipv6_multicast?.should == false
+ Addrinfo.ip('fe80::').ipv6_multicast?.should == false
+ end
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ipv6_multicast?.should be_false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_sitelocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_sitelocal_spec.rb
new file mode 100644
index 0000000000..dd202a1749
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_sitelocal_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+guard -> { SocketSpecs.ipv6_available? } do
+ describe 'Addrinfo#ipv6_sitelocal?' do
+ platform_is_not :aix do
+ it 'returns true for a site-local address' do
+ Addrinfo.ip('feef::').ipv6_sitelocal?.should == true
+ Addrinfo.ip('fee0::').ipv6_sitelocal?.should == true
+ Addrinfo.ip('fee2::').ipv6_sitelocal?.should == true
+ Addrinfo.ip('feef::1').ipv6_sitelocal?.should == true
+ end
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_sitelocal?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_sitelocal?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_spec.rb
new file mode 100644
index 0000000000..131e38849c
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ipv6?" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("10.0.0.1", 80)
+ end
+
+ it "returns true" do
+ @addrinfo.ipv6?.should be_false
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns false" do
+ @addrinfo.ipv6?.should be_true
+ end
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ipv6?.should be_false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_to_ipv4_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_to_ipv4_spec.rb
new file mode 100644
index 0000000000..6dfaf531ae
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_to_ipv4_spec.rb
@@ -0,0 +1,71 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+guard -> { SocketSpecs.ipv6_available? } do
+ describe 'Addrinfo#ipv6_to_ipv4' do
+ it 'returns an Addrinfo for ::192.168.1.1' do
+ addr = Addrinfo.ip('::192.168.1.1').ipv6_to_ipv4
+
+ addr.should be_an_instance_of(Addrinfo)
+
+ addr.afamily.should == Socket::AF_INET
+ addr.ip_address.should == '192.168.1.1'
+ end
+
+ platform_is_not :aix do
+ it 'returns an Addrinfo for ::0.0.1.1' do
+ addr = Addrinfo.ip('::0.0.1.1').ipv6_to_ipv4
+
+ addr.should be_an_instance_of(Addrinfo)
+
+ addr.afamily.should == Socket::AF_INET
+ addr.ip_address.should == '0.0.1.1'
+ end
+
+ it 'returns an Addrinfo for ::0.0.1.0' do
+ addr = Addrinfo.ip('::0.0.1.0').ipv6_to_ipv4
+
+ addr.should be_an_instance_of(Addrinfo)
+
+ addr.afamily.should == Socket::AF_INET
+ addr.ip_address.should == '0.0.1.0'
+ end
+
+ it 'returns an Addrinfo for ::0.1.0.0' do
+ addr = Addrinfo.ip('::0.1.0.0').ipv6_to_ipv4
+
+ addr.should be_an_instance_of(Addrinfo)
+
+ addr.afamily.should == Socket::AF_INET
+ addr.ip_address.should == '0.1.0.0'
+ end
+ end
+
+ it 'returns an Addrinfo for ::ffff:192.168.1.1' do
+ addr = Addrinfo.ip('::ffff:192.168.1.1').ipv6_to_ipv4
+
+ addr.should be_an_instance_of(Addrinfo)
+
+ addr.afamily.should == Socket::AF_INET
+ addr.ip_address.should == '192.168.1.1'
+ end
+
+ it 'returns nil for ::0.0.0.1' do
+ Addrinfo.ip('::0.0.0.1').ipv6_to_ipv4.should be_nil
+ end
+
+ it 'returns nil for a pure IPv6 Addrinfo' do
+ Addrinfo.ip('::1').ipv6_to_ipv4.should be_nil
+ end
+
+ it 'returns nil for an IPv4 Addrinfo' do
+ Addrinfo.ip('192.168.1.1').ipv6_to_ipv4.should be_nil
+ end
+
+ with_feature :unix_socket do
+ it 'returns nil for a UNIX Addrinfo' do
+ Addrinfo.unix('foo').ipv6_to_ipv4.should be_nil
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_unique_local_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_unique_local_spec.rb
new file mode 100644
index 0000000000..b80a15fcb0
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_unique_local_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_unique_local?' do
+ it 'returns true for an unique local IPv6 address' do
+ Addrinfo.ip('fc00::').ipv6_unique_local?.should == true
+ Addrinfo.ip('fd00::').ipv6_unique_local?.should == true
+ Addrinfo.ip('fcff::').ipv6_unique_local?.should == true
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_unique_local?.should == false
+ Addrinfo.ip('fe00::').ipv6_unique_local?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_unique_local?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_unspecified_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_unspecified_spec.rb
new file mode 100644
index 0000000000..dd79d57503
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_unspecified_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_unspecified?' do
+ it 'returns true for an unspecified IPv6 address' do
+ Addrinfo.ip('::').ipv6_unspecified?.should == true
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_unspecified?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_unspecified?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_v4compat_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_v4compat_spec.rb
new file mode 100644
index 0000000000..ab8388a21b
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_v4compat_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_v4compat?' do
+ it 'returns true for an IPv4 compatible address' do
+ Addrinfo.ip('::127.0.0.1').ipv6_v4compat?.should == true
+ Addrinfo.ip('::192.168.1.1').ipv6_v4compat?.should == true
+ end
+
+ it 'returns false for an IPv4 mapped address' do
+ Addrinfo.ip('::ffff:192.168.1.1').ipv6_v4compat?.should == false
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_v4compat?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_v4compat?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_v4mapped_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_v4mapped_spec.rb
new file mode 100644
index 0000000000..82b272c165
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_v4mapped_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_v4mapped?' do
+ it 'returns true for an IPv4 compatible address' do
+ Addrinfo.ip('::ffff:192.168.1.1').ipv6_v4mapped?.should == true
+ end
+
+ it 'returns false for an IPv4 compatible address' do
+ Addrinfo.ip('::192.168.1.1').ipv6_v4mapped?.should == false
+ Addrinfo.ip('::127.0.0.1').ipv6_v4mapped?.should == false
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_v4mapped?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_v4mapped?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/listen_spec.rb b/spec/ruby/library/socket/addrinfo/listen_spec.rb
new file mode 100644
index 0000000000..714a96ed6c
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/listen_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#listen' do
+ before do
+ @addr = Addrinfo.tcp('127.0.0.1', 0)
+ @socket = nil
+ end
+
+ after do
+ @socket.close if @socket
+ end
+
+ it 'returns a Socket when no block is given' do
+ @socket = @addr.listen
+
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket if a block is given' do
+ @addr.listen do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes the socket if a block is given' do
+ socket = nil
+
+ @addr.listen do |sock|
+ socket = sock
+ end
+
+ socket.closed?.should == true
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/marshal_dump_spec.rb b/spec/ruby/library/socket/addrinfo/marshal_dump_spec.rb
new file mode 100644
index 0000000000..c4220a6f3e
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/marshal_dump_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#marshal_dump' do
+ describe 'using an IP Addrinfo' do
+ before do
+ @addr = Addrinfo.getaddrinfo('localhost', 80, :INET, :STREAM,
+ Socket::IPPROTO_TCP, Socket::AI_CANONNAME)[0]
+ end
+
+ it 'returns an Array' do
+ @addr.marshal_dump.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @addr.marshal_dump
+ end
+
+ it 'includes the address family as the 1st value' do
+ @array[0].should == 'AF_INET'
+ end
+
+ it 'includes the IP address as the 2nd value' do
+ @array[1].should == [@addr.ip_address, @addr.ip_port.to_s]
+ end
+
+ it 'includes the protocol family as the 3rd value' do
+ @array[2].should == 'PF_INET'
+ end
+
+ it 'includes the socket type as the 4th value' do
+ @array[3].should == 'SOCK_STREAM'
+ end
+
+ platform_is_not :'solaris2.10' do # i386-solaris
+ it 'includes the protocol as the 5th value' do
+ @array[4].should == 'IPPROTO_TCP'
+ end
+ end
+
+ it 'includes the canonical name as the 6th value' do
+ @array[5].should == @addr.canonname
+ end
+ end
+ end
+
+ with_feature :unix_socket do
+ describe 'using a UNIX Addrinfo' do
+ before do
+ @addr = Addrinfo.unix('foo')
+ end
+
+ it 'returns an Array' do
+ @addr.marshal_dump.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @addr.marshal_dump
+ end
+
+ it 'includes the address family as the 1st value' do
+ @array[0].should == 'AF_UNIX'
+ end
+
+ it 'includes the UNIX path as the 2nd value' do
+ @array[1].should == @addr.unix_path
+ end
+
+ it 'includes the protocol family as the 3rd value' do
+ @array[2].should == 'PF_UNIX'
+ end
+
+ it 'includes the socket type as the 4th value' do
+ @array[3].should == 'SOCK_STREAM'
+ end
+
+ it 'includes the protocol as the 5th value' do
+ @array[4].should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/marshal_load_spec.rb b/spec/ruby/library/socket/addrinfo/marshal_load_spec.rb
new file mode 100644
index 0000000000..aa20865224
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/marshal_load_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#marshal_load' do
+ describe 'using an IP address' do
+ it 'returns a new Addrinfo' do
+ source = Addrinfo.getaddrinfo('localhost', 80, :INET, :STREAM,
+ Socket::IPPROTO_TCP, Socket::AI_CANONNAME)[0]
+
+ addr = Marshal.load(Marshal.dump(source))
+
+ addr.afamily.should == source.afamily
+ addr.pfamily.should == source.pfamily
+ addr.socktype.should == source.socktype
+ addr.protocol.should == source.protocol
+ addr.ip_address.should == source.ip_address
+ addr.ip_port.should == source.ip_port
+ addr.canonname.should == source.canonname
+ end
+ end
+
+ with_feature :unix_socket do
+ describe 'using a UNIX socket' do
+ it 'returns a new Addrinfo' do
+ source = Addrinfo.unix('foo')
+ addr = Marshal.load(Marshal.dump(source))
+
+ addr.afamily.should == source.afamily
+ addr.pfamily.should == source.pfamily
+ addr.socktype.should == source.socktype
+ addr.protocol.should == source.protocol
+ addr.unix_path.should == source.unix_path
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/pfamily_spec.rb b/spec/ruby/library/socket/addrinfo/pfamily_spec.rb
new file mode 100644
index 0000000000..984744a964
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/pfamily_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#pfamily" do
+ it 'returns PF_UNSPEC as the default socket family' do
+ sockaddr = Socket.pack_sockaddr_in(80, 'localhost')
+
+ Addrinfo.new(sockaddr).pfamily.should == Socket::PF_UNSPEC
+ end
+
+ describe "for an ipv4 socket" do
+
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns Socket::PF_INET" do
+ @addrinfo.pfamily.should == Socket::PF_INET
+ end
+
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns Socket::PF_INET6" do
+ @addrinfo.pfamily.should == Socket::PF_INET6
+ end
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns Socket::PF_UNIX" do
+ @addrinfo.pfamily.should == Socket::PF_UNIX
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/protocol_spec.rb b/spec/ruby/library/socket/addrinfo/protocol_spec.rb
new file mode 100644
index 0000000000..ea143fc4a8
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/protocol_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#protocol" do
+ it 'returns 0 by default' do
+ Addrinfo.ip('127.0.0.1').protocol.should == 0
+ end
+
+ it 'returns a custom protocol when given' do
+ Addrinfo.tcp('127.0.0.1', 80).protocol.should == Socket::IPPROTO_TCP
+ Addrinfo.tcp('::1', 80).protocol.should == Socket::IPPROTO_TCP
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns 0" do
+ @addrinfo.protocol.should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb b/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb
new file mode 100644
index 0000000000..c32da5986d
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb
@@ -0,0 +1,51 @@
+describe :socket_addrinfo_to_sockaddr, :shared => true do
+
+ describe "for an ipv4 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns a sockaddr packed structure" do
+ @addrinfo.send(@method).should == Socket.sockaddr_in(80, '127.0.0.1')
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns a sockaddr packed structure" do
+ @addrinfo.send(@method).should == Socket.sockaddr_in(80, '::1')
+ end
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns a sockaddr packed structure" do
+ @addrinfo.send(@method).should == Socket.sockaddr_un('/tmp/sock')
+ end
+ end
+ end
+
+ describe 'using a Addrinfo with just an IP address' do
+ it 'returns a String' do
+ addr = Addrinfo.ip('127.0.0.1')
+
+ addr.send(@method).should == Socket.sockaddr_in(0, '127.0.0.1')
+ end
+ end
+
+ describe 'using a Addrinfo without an IP and port' do
+ it 'returns a String' do
+ addr = Addrinfo.new(['AF_INET', 0, '', ''])
+
+ addr.send(@method).should == Socket.sockaddr_in(0, '')
+ end
+ end
+
+end
diff --git a/spec/ruby/library/socket/addrinfo/socktype_spec.rb b/spec/ruby/library/socket/addrinfo/socktype_spec.rb
new file mode 100644
index 0000000000..b994bea140
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/socktype_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#socktype" do
+ it 'returns 0 by default' do
+ Addrinfo.ip('127.0.0.1').socktype.should == 0
+ end
+
+ it 'returns the socket type when given' do
+ Addrinfo.tcp('127.0.0.1', 80).socktype.should == Socket::SOCK_STREAM
+ end
+
+ with_feature :unix_socket do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns Socket::SOCK_STREAM" do
+ @addrinfo.socktype.should == Socket::SOCK_STREAM
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/tcp_spec.rb b/spec/ruby/library/socket/addrinfo/tcp_spec.rb
new file mode 100644
index 0000000000..c74c9c21c2
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/tcp_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo.tcp' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ it 'returns an Addrinfo instance' do
+ Addrinfo.tcp(ip_address, 80).should be_an_instance_of(Addrinfo)
+ end
+
+ it 'sets the IP address' do
+ Addrinfo.tcp(ip_address, 80).ip_address.should == ip_address
+ end
+
+ it 'sets the port' do
+ Addrinfo.tcp(ip_address, 80).ip_port.should == 80
+ end
+
+ it 'sets the address family' do
+ Addrinfo.tcp(ip_address, 80).afamily.should == family
+ end
+
+ it 'sets the protocol family' do
+ Addrinfo.tcp(ip_address, 80).pfamily.should == family
+ end
+
+ it 'sets the socket type' do
+ Addrinfo.tcp(ip_address, 80).socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'sets the socket protocol' do
+ Addrinfo.tcp(ip_address, 80).protocol.should == Socket::IPPROTO_TCP
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/to_s_spec.rb b/spec/ruby/library/socket/addrinfo/to_s_spec.rb
new file mode 100644
index 0000000000..ddf994e051
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/to_s_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../spec_helper'
+require_relative 'shared/to_sockaddr'
+
+describe "Addrinfo#to_s" do
+ it_behaves_like :socket_addrinfo_to_sockaddr, :to_s
+end
diff --git a/spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb b/spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb
new file mode 100644
index 0000000000..b9f75454bd
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../spec_helper'
+require_relative 'shared/to_sockaddr'
+
+describe "Addrinfo#to_sockaddr" do
+ it_behaves_like :socket_addrinfo_to_sockaddr, :to_sockaddr
+end
diff --git a/spec/ruby/library/socket/addrinfo/udp_spec.rb b/spec/ruby/library/socket/addrinfo/udp_spec.rb
new file mode 100644
index 0000000000..b05cbf9b0b
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/udp_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo.udp' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ it 'returns an Addrinfo instance' do
+ Addrinfo.udp(ip_address, 80).should be_an_instance_of(Addrinfo)
+ end
+
+ it 'sets the IP address' do
+ Addrinfo.udp(ip_address, 80).ip_address.should == ip_address
+ end
+
+ it 'sets the port' do
+ Addrinfo.udp(ip_address, 80).ip_port.should == 80
+ end
+
+ it 'sets the address family' do
+ Addrinfo.udp(ip_address, 80).afamily.should == family
+ end
+
+ it 'sets the protocol family' do
+ Addrinfo.udp(ip_address, 80).pfamily.should == family
+ end
+
+ it 'sets the socket type' do
+ Addrinfo.udp(ip_address, 80).socktype.should == Socket::SOCK_DGRAM
+ end
+
+ platform_is_not :solaris do
+ it 'sets the socket protocol' do
+ Addrinfo.udp(ip_address, 80).protocol.should == Socket::IPPROTO_UDP
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/unix_path_spec.rb b/spec/ruby/library/socket/addrinfo/unix_path_spec.rb
new file mode 100644
index 0000000000..0f25881724
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/unix_path_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../spec_helper'
+
+with_feature :unix_socket do
+ describe "Addrinfo#unix_path" do
+ describe "for an ipv4 socket" do
+
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "raises an exception" do
+ lambda { @addrinfo.unix_path }.should raise_error(SocketError)
+ end
+
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "raises an exception" do
+ lambda { @addrinfo.unix_path }.should raise_error(SocketError)
+ end
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns the socket path" do
+ @addrinfo.unix_path.should == "/tmp/sock"
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/unix_spec.rb b/spec/ruby/library/socket/addrinfo/unix_spec.rb
new file mode 100644
index 0000000000..4596ece17e
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/unix_spec.rb
@@ -0,0 +1,71 @@
+require_relative '../spec_helper'
+
+with_feature :unix_socket do
+ describe 'Addrinfo.unix' do
+ it 'returns an Addrinfo instance' do
+ Addrinfo.unix('socket').should be_an_instance_of(Addrinfo)
+ end
+
+ it 'sets the IP address' do
+ Addrinfo.unix('socket').unix_path.should == 'socket'
+ end
+
+ it 'sets the address family' do
+ Addrinfo.unix('socket').afamily.should == Socket::AF_UNIX
+ end
+
+ it 'sets the protocol family' do
+ Addrinfo.unix('socket').pfamily.should == Socket::PF_UNIX
+ end
+
+ it 'sets the socket type' do
+ Addrinfo.unix('socket').socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'sets a custom socket type' do
+ addr = Addrinfo.unix('socket', Socket::SOCK_DGRAM)
+
+ addr.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'sets the socket protocol to 0' do
+ Addrinfo.unix('socket').protocol.should == 0
+ end
+ end
+end
+
+describe "Addrinfo#unix?" do
+ describe "for an ipv4 socket" do
+
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns false" do
+ @addrinfo.unix?.should be_false
+ end
+
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns false" do
+ @addrinfo.unix?.should be_false
+ end
+ end
+
+ platform_is_not :windows do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns true" do
+ @addrinfo.unix?.should be_true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/cmsg_is_spec.rb b/spec/ruby/library/socket/ancillarydata/cmsg_is_spec.rb
new file mode 100644
index 0000000000..bf93cd977e
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/cmsg_is_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#cmsg_is?' do
+ describe 'using :INET, :IP, :TTL as the family, level, and type' do
+ before do
+ @data = Socket::AncillaryData.new(:INET, :IP, :TTL, '')
+ end
+
+ it 'returns true when comparing with IPPROTO_IP and IP_TTL' do
+ @data.cmsg_is?(Socket::IPPROTO_IP, Socket::IP_TTL).should == true
+ end
+
+ it 'returns true when comparing with :IP and :TTL' do
+ @data.cmsg_is?(:IP, :TTL).should == true
+ end
+
+ with_feature :pktinfo do
+ it 'returns false when comparing with :IP and :PKTINFO' do
+ @data.cmsg_is?(:IP, :PKTINFO).should == false
+ end
+ end
+
+ it 'returns false when comparing with :SOCKET and :RIGHTS' do
+ @data.cmsg_is?(:SOCKET, :RIGHTS).should == false
+ end
+
+ it 'raises SocketError when comparign with :IPV6 and :RIGHTS' do
+ lambda { @data.cmsg_is?(:IPV6, :RIGHTS) }.should raise_error(SocketError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/data_spec.rb b/spec/ruby/library/socket/ancillarydata/data_spec.rb
new file mode 100644
index 0000000000..5a1a446dd5
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/data_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#data' do
+ it 'returns the data as a String' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, 'ugh').data.should == 'ugh'
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/family_spec.rb b/spec/ruby/library/socket/ancillarydata/family_spec.rb
new file mode 100644
index 0000000000..975f0d2538
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/family_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#family' do
+ it 'returns the family as an Integer' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, '').family.should == Socket::AF_INET
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/initialize_spec.rb b/spec/ruby/library/socket/ancillarydata/initialize_spec.rb
new file mode 100644
index 0000000000..d4788d0f68
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/initialize_spec.rb
@@ -0,0 +1,284 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#initialize' do
+ describe 'using Integers for the family, level, and type' do
+ before do
+ @data = Socket::AncillaryData
+ .new(Socket::AF_INET, Socket::IPPROTO_IP, Socket::IP_RECVTTL, 'ugh')
+ end
+
+ it 'sets the address family' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the message level' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the message type' do
+ @data.type.should == Socket::IP_RECVTTL
+ end
+
+ it 'sets the data' do
+ @data.data.should == 'ugh'
+ end
+ end
+
+ describe 'using Symbols for the family, level, and type' do
+ before do
+ @data = Socket::AncillaryData.new(:INET, :IPPROTO_IP, :RECVTTL, 'ugh')
+ end
+
+ it 'sets the address family' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the message level' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the message type' do
+ @data.type.should == Socket::IP_RECVTTL
+ end
+
+ it 'sets the data' do
+ @data.data.should == 'ugh'
+ end
+ end
+
+ describe 'using Strings for the family, level, and type' do
+ before do
+ @data = Socket::AncillaryData.new('INET', 'IPPROTO_IP', 'RECVTTL', 'ugh')
+ end
+
+ it 'sets the address family' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the message level' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the message type' do
+ @data.type.should == Socket::IP_RECVTTL
+ end
+
+ it 'sets the data' do
+ @data.data.should == 'ugh'
+ end
+ end
+
+ describe 'using custom objects with a to_str method for the family, level, and type' do
+ before do
+ fmock = mock(:family)
+ lmock = mock(:level)
+ tmock = mock(:type)
+ dmock = mock(:data)
+
+ fmock.stub!(:to_str).and_return('INET')
+ lmock.stub!(:to_str).and_return('IP')
+ tmock.stub!(:to_str).and_return('RECVTTL')
+ dmock.stub!(:to_str).and_return('ugh')
+
+ @data = Socket::AncillaryData.new(fmock, lmock, tmock, dmock)
+ end
+
+ it 'sets the address family' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the message level' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the message type' do
+ @data.type.should == Socket::IP_RECVTTL
+ end
+
+ it 'sets the data' do
+ @data.data.should == 'ugh'
+ end
+ end
+
+ describe 'using :AF_INET as the family and :SOCKET as the level' do
+ it 'sets the type to SCM_RIGHTS when using :RIGHTS as the type argument' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, '').type.should == Socket::SCM_RIGHTS
+ end
+
+ platform_is_not :"solaris2.10", :aix do
+ it 'sets the type to SCM_TIMESTAMP when using :TIMESTAMP as the type argument' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :TIMESTAMP, '').type.should == Socket::SCM_TIMESTAMP
+ end
+ end
+
+ it 'raises TypeError when using a numeric string as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :IGMP, Socket::SCM_RIGHTS.to_s, '')
+ }.should raise_error(TypeError)
+ end
+
+ it 'raises SocketError when using :RECVTTL as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :SOCKET, :RECVTTL, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :MOO as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :SOCKET, :MOO, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :IP_RECVTTL as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :SOCKET, :IP_RECVTTL, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_INET as the family and :SOCKET as the level' do
+ it 'sets the type to SCM_RIGHTS when using :RIGHTS as the type argument' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, '').type.should == Socket::SCM_RIGHTS
+ end
+ end
+
+ describe 'using :AF_INET as the family and :IP as the level' do
+ it 'sets the type to IP_RECVTTL when using :RECVTTL as the type argument' do
+ Socket::AncillaryData.new(:INET, :IP, :RECVTTL, '').type.should == Socket::IP_RECVTTL
+ end
+
+ with_feature :ip_mtu do
+ it 'sets the type to IP_MTU when using :MTU as the type argument' do
+ Socket::AncillaryData.new(:INET, :IP, :MTU, '').type.should == Socket::IP_MTU
+ end
+ end
+
+ it 'raises SocketError when using :RIGHTS as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :IP, :RIGHTS, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :MOO as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :IP, :MOO, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_INET as the family and :IPV6 as the level' do
+ it 'sets the type to IPV6_CHECKSUM when using :CHECKSUM as the type argument' do
+ Socket::AncillaryData.new(:INET, :IPV6, :CHECKSUM, '').type.should == Socket::IPV6_CHECKSUM
+ end
+
+ with_feature :ipv6_nexthop do
+ it 'sets the type to IPV6_NEXTHOP when using :NEXTHOP as the type argument' do
+ Socket::AncillaryData.new(:INET, :IPV6, :NEXTHOP, '').type.should == Socket::IPV6_NEXTHOP
+ end
+ end
+
+ it 'raises SocketError when using :RIGHTS as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :IPV6, :RIGHTS, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :MOO as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :IPV6, :MOO, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_INET as the family and :TCP as the level' do
+ with_feature :tcp_cork do
+ it 'sets the type to TCP_CORK when using :CORK as the type argument' do
+ Socket::AncillaryData.new(:INET, :TCP, :CORK, '').type.should == Socket::TCP_CORK
+ end
+ end
+
+ with_feature :tcp_info do
+ it 'sets the type to TCP_INFO when using :INFO as the type argument' do
+ Socket::AncillaryData.new(:INET, :TCP, :INFO, '').type.should == Socket::TCP_INFO
+ end
+ end
+
+ it 'raises SocketError when using :RIGHTS as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :TCP, :RIGHTS, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :MOO as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :TCP, :MOO, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_INET as the family and :UDP as the level' do
+ with_feature :udp_cork do
+ it 'sets the type to UDP_CORK when using :CORK as the type argument' do
+ Socket::AncillaryData.new(:INET, :UDP, :CORK, '').type.should == Socket::UDP_CORK
+ end
+ end
+
+ it 'raises SocketError when using :RIGHTS as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :UDP, :RIGHTS, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :MOO as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :UDP, :MOO, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_UNIX as the family and :SOCKET as the level' do
+ it 'sets the type to SCM_RIGHTS when using :RIGHTS as the type argument' do
+ Socket::AncillaryData.new(:UNIX, :SOCKET, :RIGHTS, '').type.should == Socket::SCM_RIGHTS
+ end
+
+ it 'raises SocketError when using :CORK sa the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:UNIX, :SOCKET, :CORK, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_UNIX as the family and :IP as the level' do
+ it 'raises SocketError' do
+ lambda {
+ Socket::AncillaryData.new(:UNIX, :IP, :RECVTTL, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_UNIX as the family and :IPV6 as the level' do
+ it 'raises SocketError' do
+ lambda {
+ Socket::AncillaryData.new(:UNIX, :IPV6, :NEXTHOP, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_UNIX as the family and :TCP as the level' do
+ it 'raises SocketError' do
+ lambda {
+ Socket::AncillaryData.new(:UNIX, :TCP, :CORK, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_UNIX as the family and :UDP as the level' do
+ it 'raises SocketError' do
+ lambda {
+ Socket::AncillaryData.new(:UNIX, :UDP, :CORK, '')
+ }.should raise_error(SocketError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/int_spec.rb b/spec/ruby/library/socket/ancillarydata/int_spec.rb
new file mode 100644
index 0000000000..0d7c5e3652
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/int_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData.int' do
+ before do
+ @data = Socket::AncillaryData.int(:INET, :SOCKET, :RIGHTS, 4)
+ end
+
+ it 'returns a Socket::AncillaryData' do
+ @data.should be_an_instance_of(Socket::AncillaryData)
+ end
+
+ it 'sets the family to AF_INET' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the level SOL_SOCKET' do
+ @data.level.should == Socket::SOL_SOCKET
+ end
+
+ it 'sets the type SCM_RIGHTS' do
+ @data.type.should == Socket::SCM_RIGHTS
+ end
+
+ it 'sets the data to a packed String' do
+ @data.data.should == [4].pack('I')
+ end
+ end
+
+ describe 'Socket::AncillaryData#int' do
+ it 'returns the data as an Integer' do
+ data = Socket::AncillaryData.int(:UNIX, :SOCKET, :RIGHTS, 4)
+
+ data.int.should == 4
+ end
+
+ it 'raises when the data is not an Integer' do
+ data = Socket::AncillaryData.new(:UNIX, :SOCKET, :RIGHTS, 'ugh')
+
+ lambda { data.int }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/ip_pktinfo_spec.rb b/spec/ruby/library/socket/ancillarydata/ip_pktinfo_spec.rb
new file mode 100644
index 0000000000..809d087161
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/ip_pktinfo_spec.rb
@@ -0,0 +1,145 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data, :pktinfo do
+ describe 'Socket::AncillaryData.ip_pktinfo' do
+ describe 'with a source address and index' do
+ before do
+ @data = Socket::AncillaryData.ip_pktinfo(Addrinfo.ip('127.0.0.1'), 4)
+ end
+
+ it 'returns a Socket::AncillaryData' do
+ @data.should be_an_instance_of(Socket::AncillaryData)
+ end
+
+ it 'sets the family to AF_INET' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the level to IPPROTO_IP' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the type to IP_PKTINFO' do
+ @data.type.should == Socket::IP_PKTINFO
+ end
+ end
+
+ describe 'with a source address, index, and destination address' do
+ before do
+ source = Addrinfo.ip('127.0.0.1')
+ dest = Addrinfo.ip('127.0.0.5')
+ @data = Socket::AncillaryData.ip_pktinfo(source, 4, dest)
+ end
+
+ it 'returns a Socket::AncillaryData' do
+ @data.should be_an_instance_of(Socket::AncillaryData)
+ end
+
+ it 'sets the family to AF_INET' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the level to IPPROTO_IP' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the type to IP_PKTINFO' do
+ @data.type.should == Socket::IP_PKTINFO
+ end
+ end
+ end
+
+ describe 'Socket::AncillaryData#ip_pktinfo' do
+ describe 'using an Addrinfo without a port number' do
+ before do
+ @source = Addrinfo.ip('127.0.0.1')
+ @dest = Addrinfo.ip('127.0.0.5')
+ @data = Socket::AncillaryData.ip_pktinfo(@source, 4, @dest)
+ end
+
+ it 'returns an Array' do
+ @data.ip_pktinfo.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @info = @data.ip_pktinfo
+ end
+
+ it 'stores an Addrinfo at index 0' do
+ @info[0].should be_an_instance_of(Addrinfo)
+ end
+
+ it 'stores the ifindex at index 1' do
+ @info[1].should be_kind_of(Integer)
+ end
+
+ it 'stores an Addrinfo at index 2' do
+ @info[2].should be_an_instance_of(Addrinfo)
+ end
+ end
+
+ describe 'the source Addrinfo' do
+ before do
+ @addr = @data.ip_pktinfo[0]
+ end
+
+ it 'uses the correct IP address' do
+ @addr.ip_address.should == '127.0.0.1'
+ end
+
+ it 'is not the same object as the input Addrinfo' do
+ @addr.should_not == @source
+ end
+ end
+
+ describe 'the ifindex' do
+ it 'is an Integer' do
+ @data.ip_pktinfo[1].should == 4
+ end
+ end
+
+ describe 'the destination Addrinfo' do
+ before do
+ @addr = @data.ip_pktinfo[2]
+ end
+
+ it 'uses the correct IP address' do
+ @addr.ip_address.should == '127.0.0.5'
+ end
+
+ it 'is not the same object as the input Addrinfo' do
+ @addr.should_not == @dest
+ end
+ end
+ end
+
+ describe 'using an Addrinfo with a port number' do
+ before do
+ @source = Addrinfo.tcp('127.0.0.1', 80)
+ @dest = Addrinfo.tcp('127.0.0.5', 85)
+ @data = Socket::AncillaryData.ip_pktinfo(@source, 4, @dest)
+ end
+
+ describe 'the source Addrinfo' do
+ before do
+ @addr = @data.ip_pktinfo[0]
+ end
+
+ it 'does not contain a port number' do
+ @addr.ip_port.should == 0
+ end
+ end
+
+ describe 'the destination Addrinfo' do
+ before do
+ @addr = @data.ip_pktinfo[2]
+ end
+
+ it 'does not contain a port number' do
+ @addr.ip_port.should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_addr_spec.rb b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_addr_spec.rb
new file mode 100644
index 0000000000..f70fe27d6a
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_addr_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data, :ipv6_pktinfo do
+ describe 'Socket::AncillaryData#ipv6_pktinfo_addr' do
+ it 'returns an Addrinfo' do
+ data = Socket::AncillaryData.ipv6_pktinfo(Addrinfo.ip('::1'), 4)
+
+ data.ipv6_pktinfo_addr.should be_an_instance_of(Addrinfo)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_ifindex_spec.rb b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_ifindex_spec.rb
new file mode 100644
index 0000000000..bda37eec98
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_ifindex_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data, :ipv6_pktinfo do
+ describe 'Socket::AncillaryData#ipv6_pktinfo_ifindex' do
+ it 'returns an Addrinfo' do
+ data = Socket::AncillaryData.ipv6_pktinfo(Addrinfo.ip('::1'), 4)
+
+ data.ipv6_pktinfo_ifindex.should == 4
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_spec.rb b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_spec.rb
new file mode 100644
index 0000000000..dfaa5bf0f5
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_spec.rb
@@ -0,0 +1,89 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data, :ipv6_pktinfo do
+ describe 'Socket::AncillaryData.ipv6_pktinfo' do
+ before do
+ @data = Socket::AncillaryData.ipv6_pktinfo(Addrinfo.ip('::1'), 4)
+ end
+
+ it 'returns a Socket::AncillaryData' do
+ @data.should be_an_instance_of(Socket::AncillaryData)
+ end
+
+ it 'sets the family to AF_INET' do
+ @data.family.should == Socket::AF_INET6
+ end
+
+ it 'sets the level to IPPROTO_IP' do
+ @data.level.should == Socket::IPPROTO_IPV6
+ end
+
+ it 'sets the type to IP_PKTINFO' do
+ @data.type.should == Socket::IPV6_PKTINFO
+ end
+ end
+
+ describe 'Socket::AncillaryData#ipv6_pktinfo' do
+ describe 'using an Addrinfo without a port number' do
+ before do
+ @source = Addrinfo.ip('::1')
+ @data = Socket::AncillaryData.ipv6_pktinfo(@source, 4)
+ end
+
+ it 'returns an Array' do
+ @data.ipv6_pktinfo.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @info = @data.ipv6_pktinfo
+ end
+
+ it 'stores an Addrinfo at index 0' do
+ @info[0].should be_an_instance_of(Addrinfo)
+ end
+
+ it 'stores the ifindex at index 1' do
+ @info[1].should be_kind_of(Integer)
+ end
+ end
+
+ describe 'the source Addrinfo' do
+ before do
+ @addr = @data.ipv6_pktinfo[0]
+ end
+
+ it 'uses the correct IP address' do
+ @addr.ip_address.should == '::1'
+ end
+
+ it 'is not the same object as the input Addrinfo' do
+ @addr.should_not == @source
+ end
+ end
+
+ describe 'the ifindex' do
+ it 'is an Integer' do
+ @data.ipv6_pktinfo[1].should == 4
+ end
+ end
+ end
+
+ describe 'using an Addrinfo with a port number' do
+ before do
+ @source = Addrinfo.tcp('::1', 80)
+ @data = Socket::AncillaryData.ipv6_pktinfo(@source, 4)
+ end
+
+ describe 'the source Addrinfo' do
+ before do
+ @addr = @data.ipv6_pktinfo[0]
+ end
+
+ it 'does not contain a port number' do
+ @addr.ip_port.should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/level_spec.rb b/spec/ruby/library/socket/ancillarydata/level_spec.rb
new file mode 100644
index 0000000000..a2ff216f9d
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/level_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#level' do
+ it 'returns the level as an Integer' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, '').level.should == Socket::SOL_SOCKET
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/type_spec.rb b/spec/ruby/library/socket/ancillarydata/type_spec.rb
new file mode 100644
index 0000000000..972beeeca0
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/type_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#type' do
+ it 'returns the type as an Integer' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, '').type.should == Socket::SCM_RIGHTS
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/unix_rights_spec.rb b/spec/ruby/library/socket/ancillarydata/unix_rights_spec.rb
new file mode 100644
index 0000000000..34d954af81
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/unix_rights_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData.unix_rights' do
+ describe 'using a list of IO objects' do
+ before do
+ @data = Socket::AncillaryData.unix_rights(STDOUT, STDERR)
+ end
+
+ it 'sets the family to AF_UNIX' do
+ @data.family.should == Socket::AF_UNIX
+ end
+
+ it 'sets the level to SOL_SOCKET' do
+ @data.level.should == Socket::SOL_SOCKET
+ end
+
+ it 'sets the type to SCM_RIGHTS' do
+ @data.type.should == Socket::SCM_RIGHTS
+ end
+
+ it 'sets the data to a String containing the file descriptors' do
+ @data.data.unpack('I*').should == [STDOUT.fileno, STDERR.fileno]
+ end
+ end
+
+ describe 'using non IO objects' do
+ it 'raises TypeError' do
+ lambda { Socket::AncillaryData.unix_rights(10) }.should raise_error(TypeError)
+ end
+ end
+ end
+
+ describe 'Socket::AncillaryData#unix_rights' do
+ it 'returns the data as an Array of IO objects' do
+ data = Socket::AncillaryData.unix_rights(STDOUT, STDERR)
+
+ data.unix_rights.should == [STDOUT, STDERR]
+ end
+
+ it 'returns nil when the data is not a list of file descriptors' do
+ data = Socket::AncillaryData.new(:UNIX, :SOCKET, :RIGHTS, '')
+
+ data.unix_rights.should be_nil
+ end
+
+ it 'raises TypeError when the level is not SOL_SOCKET' do
+ data = Socket::AncillaryData.new(:INET, :IP, :RECVTTL, '')
+
+ lambda { data.unix_rights }.should raise_error(TypeError)
+ end
+
+ platform_is_not :"solaris2.10", :aix do
+ it 'raises TypeError when the type is not SCM_RIGHTS' do
+ data = Socket::AncillaryData.new(:INET, :SOCKET, :TIMESTAMP, '')
+
+ lambda { data.unix_rights }.should raise_error(TypeError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/close_read_spec.rb b/spec/ruby/library/socket/basicsocket/close_read_spec.rb
new file mode 100644
index 0000000000..c989fcaf72
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/close_read_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::BasicSocket#close_read" do
+ before :each do
+ @server = TCPServer.new(0)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ end
+
+ it "closes the reading end of the socket" do
+ @server.close_read
+ lambda { @server.read }.should raise_error(IOError)
+ end
+
+ it 'does not raise when called on a socket already closed for reading' do
+ @server.close_read
+ @server.close_read
+ lambda { @server.read }.should raise_error(IOError)
+ end
+
+ it 'does not fully close the socket' do
+ @server.close_read
+ @server.closed?.should be_false
+ end
+
+ it "fully closes the socket if it was already closed for writing" do
+ @server.close_write
+ @server.close_read
+ @server.closed?.should be_true
+ end
+
+ it 'raises IOError when called on a fully closed socket' do
+ @server.close
+ lambda { @server.close_read }.should raise_error(IOError)
+ end
+
+ it "returns nil" do
+ @server.close_read.should be_nil
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/close_write_spec.rb b/spec/ruby/library/socket/basicsocket/close_write_spec.rb
new file mode 100644
index 0000000000..f37e0e5074
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/close_write_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::BasicSocket#close_write" do
+ before :each do
+ @server = TCPServer.new(0)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ end
+
+ it "closes the writing end of the socket" do
+ @server.close_write
+ lambda { @server.write("foo") }.should raise_error(IOError)
+ end
+
+ it 'does not raise when called on a socket already closed for writing' do
+ @server.close_write
+ @server.close_write
+ lambda { @server.write("foo") }.should raise_error(IOError)
+ end
+
+ it 'does not fully close the socket' do
+ @server.close_write
+ @server.closed?.should be_false
+ end
+
+ it "does not prevent reading" do
+ @server.close_write
+ @server.read(0).should == ""
+ end
+
+ it "fully closes the socket if it was already closed for reading" do
+ @server.close_read
+ @server.close_write
+ @server.closed?.should be_true
+ end
+
+ it 'raises IOError when called on a fully closed socket' do
+ @server.close
+ lambda { @server.close_write }.should raise_error(IOError)
+ end
+
+ it "returns nil" do
+ @server.close_write.should be_nil
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/connect_address_spec.rb b/spec/ruby/library/socket/basicsocket/connect_address_spec.rb
new file mode 100644
index 0000000000..18baac7f17
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/connect_address_spec.rb
@@ -0,0 +1,154 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#connect_address' do
+ describe 'using an unbound socket' do
+ after do
+ @sock.close
+ end
+
+ it 'raises SocketError' do
+ @sock = Socket.new(:INET, :STREAM)
+
+ lambda { @sock.connect_address }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using a socket bound to 0.0.0.0' do
+ before do
+ @sock = Socket.new(:INET, :STREAM)
+ @sock.bind(Socket.sockaddr_in(0, '0.0.0.0'))
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.connect_address.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses 127.0.0.1 as the IP address' do
+ @sock.connect_address.ip_address.should == '127.0.0.1'
+ end
+
+ it 'uses the correct port number' do
+ @sock.connect_address.ip_port.should > 0
+ end
+
+ it 'uses AF_INET as the address family' do
+ @sock.connect_address.afamily.should == Socket::AF_INET
+ end
+
+ it 'uses PF_INET as the address family' do
+ @sock.connect_address.pfamily.should == Socket::PF_INET
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.connect_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.connect_address.protocol.should == 0
+ end
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ describe 'using a socket bound to ::' do
+ before do
+ @sock = Socket.new(:INET6, :STREAM)
+ @sock.bind(Socket.sockaddr_in(0, '::'))
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.connect_address.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses ::1 as the IP address' do
+ @sock.connect_address.ip_address.should == '::1'
+ end
+
+ it 'uses the correct port number' do
+ @sock.connect_address.ip_port.should > 0
+ end
+
+ it 'uses AF_INET6 as the address family' do
+ @sock.connect_address.afamily.should == Socket::AF_INET6
+ end
+
+ it 'uses PF_INET6 as the address family' do
+ @sock.connect_address.pfamily.should == Socket::PF_INET6
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.connect_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.connect_address.protocol.should == 0
+ end
+ end
+ end
+
+ with_feature :unix_socket do
+ platform_is_not :aix do
+ describe 'using an unbound UNIX socket' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @server.close
+ rm_r(@path)
+ end
+
+ it 'raises SocketError' do
+ lambda { @client.connect_address }.should raise_error(SocketError)
+ end
+ end
+ end
+
+ describe 'using a bound UNIX socket' do
+ before do
+ @path = SocketSpecs.socket_path
+ @sock = UNIXServer.new(@path)
+ end
+
+ after do
+ @sock.close
+ rm_r(@path)
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.connect_address.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses the correct socket path' do
+ @sock.connect_address.unix_path.should == @path
+ end
+
+ it 'uses AF_UNIX as the address family' do
+ @sock.connect_address.afamily.should == Socket::AF_UNIX
+ end
+
+ it 'uses PF_UNIX as the protocol family' do
+ @sock.connect_address.pfamily.should == Socket::PF_UNIX
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.connect_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.connect_address.protocol.should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/do_not_reverse_lookup_spec.rb b/spec/ruby/library/socket/basicsocket/do_not_reverse_lookup_spec.rb
new file mode 100644
index 0000000000..85a66275f8
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/do_not_reverse_lookup_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket.do_not_reverse_lookup" do
+ before :each do
+ @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
+ @server = TCPServer.new('127.0.0.1', 0)
+ @port = @server.addr[1]
+ @socket = TCPSocket.new('127.0.0.1', @port)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @socket.close unless @socket.closed?
+ BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup
+ end
+
+ it "defaults to true" do
+ BasicSocket.do_not_reverse_lookup.should be_true
+ end
+
+ it "causes 'peeraddr' to avoid name lookups" do
+ @socket.do_not_reverse_lookup = true
+ BasicSocket.do_not_reverse_lookup = true
+ @socket.peeraddr.should == ["AF_INET", @port, "127.0.0.1", "127.0.0.1"]
+ end
+
+ it "looks for hostnames when set to false" do
+ @socket.do_not_reverse_lookup = false
+ BasicSocket.do_not_reverse_lookup = false
+ @socket.peeraddr[2].should == SocketSpecs.hostname
+ end
+
+ it "looks for numeric addresses when set to true" do
+ @socket.do_not_reverse_lookup = true
+ BasicSocket.do_not_reverse_lookup = true
+ @socket.peeraddr[2].should == "127.0.0.1"
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/for_fd_spec.rb b/spec/ruby/library/socket/basicsocket/for_fd_spec.rb
new file mode 100644
index 0000000000..9c9e6a8b55
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/for_fd_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket.for_fd" do
+ before :each do
+ @server = TCPServer.new(0)
+ @s2 = nil
+ end
+
+ after :each do
+ @socket1.close if @socket1
+ @server.close if @server
+ end
+
+ it "return a Socket instance wrapped around the descriptor" do
+ @s2 = TCPServer.for_fd(@server.fileno)
+ @s2.autoclose = false
+ @s2.should be_kind_of(TCPServer)
+ @s2.fileno.should == @server.fileno
+ end
+
+ it 'returns a new socket for a file descriptor' do
+ @socket1 = Socket.new(:INET, :DGRAM)
+ socket2 = Socket.for_fd(@socket1.fileno)
+ socket2.autoclose = false
+
+ socket2.should be_an_instance_of(Socket)
+ socket2.fileno.should == @socket1.fileno
+ end
+
+ it 'sets the socket into binary mode' do
+ @socket1 = Socket.new(:INET, :DGRAM)
+ socket2 = Socket.for_fd(@socket1.fileno)
+ socket2.autoclose = false
+
+ socket2.binmode?.should be_true
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/getpeereid_spec.rb b/spec/ruby/library/socket/basicsocket/getpeereid_spec.rb
new file mode 100644
index 0000000000..9eeb6d0e0b
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/getpeereid_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'BasicSocket#getpeereid' do
+ with_feature :unix_socket do
+ describe 'using a UNIXSocket' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @server.close
+
+ rm_r(@path)
+ end
+
+ it 'returns an Array with the user and group ID' do
+ @client.getpeereid.should == [Process.euid, Process.egid]
+ end
+ end
+ end
+
+ describe 'using an IPSocket' do
+ after do
+ @sock.close
+ end
+
+ it 'raises NoMethodError' do
+ @sock = TCPServer.new('127.0.0.1', 0)
+ lambda { @sock.getpeereid }.should raise_error(NoMethodError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/getpeername_spec.rb b/spec/ruby/library/socket/basicsocket/getpeername_spec.rb
new file mode 100644
index 0000000000..23c73056cd
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/getpeername_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::BasicSocket#getpeername" do
+
+ before :each do
+ @server = TCPServer.new("127.0.0.1", 0)
+ @port = @server.addr[1]
+ @client = TCPSocket.new("127.0.0.1", @port)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @client.close unless @client.closed?
+ end
+
+ it "returns the sockaddr of the other end of the connection" do
+ server_sockaddr = Socket.pack_sockaddr_in(@port, "127.0.0.1")
+ @client.getpeername.should == server_sockaddr
+ end
+
+ it 'raises Errno::ENOTCONN for a disconnected socket' do
+ lambda { @server.getpeername }.should raise_error(Errno::ENOTCONN)
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/getsockname_spec.rb b/spec/ruby/library/socket/basicsocket/getsockname_spec.rb
new file mode 100644
index 0000000000..a2c5980a9e
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/getsockname_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::BasicSocket#getsockname" do
+ after :each do
+ @socket.closed?.should be_false
+ @socket.close
+ end
+
+ it "returns the sockaddr associacted with the socket" do
+ @socket = TCPServer.new("127.0.0.1", 0)
+ sockaddr = Socket.unpack_sockaddr_in(@socket.getsockname)
+ sockaddr.should == [@socket.addr[1], "127.0.0.1"]
+ end
+
+ it "works on sockets listening in ipaddr_any" do
+ @socket = TCPServer.new(0)
+ sockaddr = Socket.unpack_sockaddr_in(@socket.getsockname)
+ ["::", "0.0.0.0", "::ffff:0.0.0.0"].include?(sockaddr[1]).should be_true
+ sockaddr[0].should == @socket.addr[1]
+ end
+
+ it 'returns a default socket address for a disconnected socket' do
+ @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ sockaddr = Socket.unpack_sockaddr_in(@socket.getsockname)
+ sockaddr.should == [0, "0.0.0.0"]
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/getsockopt_spec.rb b/spec/ruby/library/socket/basicsocket/getsockopt_spec.rb
new file mode 100644
index 0000000000..aad21ca535
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/getsockopt_spec.rb
@@ -0,0 +1,188 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket#getsockopt" do
+ before :each do
+ @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ end
+
+ after :each do
+ @sock.closed?.should be_false
+ @sock.close
+ end
+
+ platform_is_not :aix do
+ # A known bug in AIX. getsockopt(2) does not properly set
+ # the fifth argument for SO_TYPE, SO_OOBINLINE, SO_BROADCAST, etc.
+
+ it "gets a socket option Socket::SO_TYPE" do
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE).to_s
+ n.should == [Socket::SOCK_STREAM].pack("i")
+ end
+
+ it "gets a socket option Socket::SO_OOBINLINE" do
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should == [0].pack("i")
+ end
+ end
+
+ it "gets a socket option Socket::SO_LINGER" do
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER).to_s
+ if (n.size == 8) # linger struct on some platforms, not just a value
+ n.should == [0, 0].pack("ii")
+ else
+ n.should == [0].pack("i")
+ end
+ end
+
+ it "gets a socket option Socket::SO_SNDBUF" do
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ n.unpack('i')[0].should > 0
+ end
+
+ it "raises a SystemCallError with an invalid socket option" do
+ lambda { @sock.getsockopt Socket::SOL_SOCKET, -1 }.should raise_error(Errno::ENOPROTOOPT)
+ end
+
+ it 'returns a Socket::Option using a constant' do
+ opt = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE)
+
+ opt.should be_an_instance_of(Socket::Option)
+ end
+
+ it 'returns a Socket::Option for a boolean option' do
+ opt = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR)
+
+ opt.bool.should == false
+ end
+
+ it 'returns a Socket::Option for a numeric option' do
+ opt = @sock.getsockopt(Socket::IPPROTO_IP, Socket::IP_TTL)
+
+ opt.int.should be_kind_of(Integer)
+ end
+
+ it 'returns a Socket::Option for a struct option' do
+ opt = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER)
+
+ opt.linger.should == [false, 0]
+ end
+
+ it 'raises Errno::ENOPROTOOPT when requesting an invalid option' do
+ lambda { @sock.getsockopt(Socket::SOL_SOCKET, -1) }.should raise_error(Errno::ENOPROTOOPT)
+ end
+
+ describe 'using Symbols as arguments' do
+ it 'returns a Socket::Option for arguments :SOCKET and :TYPE' do
+ opt = @sock.getsockopt(:SOCKET, :TYPE)
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_TYPE
+ end
+
+ it 'returns a Socket::Option for arguments :IP and :TTL' do
+ opt = @sock.getsockopt(:IP, :TTL)
+
+ opt.level.should == Socket::IPPROTO_IP
+ opt.optname.should == Socket::IP_TTL
+ end
+
+ it 'returns a Socket::Option for arguments :SOCKET and :REUSEADDR' do
+ opt = @sock.getsockopt(:SOCKET, :REUSEADDR)
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_REUSEADDR
+ end
+
+ it 'returns a Socket::Option for arguments :SOCKET and :LINGER' do
+ opt = @sock.getsockopt(:SOCKET, :LINGER)
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_LINGER
+ end
+
+ with_feature :udp_cork do
+ it 'returns a Socket::Option for arguments :UDP and :CORK' do
+ sock = Socket.new(:INET, :DGRAM)
+ begin
+ opt = sock.getsockopt(:UDP, :CORK)
+
+ opt.level.should == Socket::IPPROTO_UDP
+ opt.optname.should == Socket::UDP_CORK
+ ensure
+ sock.close
+ end
+ end
+ end
+ end
+
+ describe 'using Strings as arguments' do
+ it 'returns a Socket::Option for arguments "SOCKET" and "TYPE"' do
+ opt = @sock.getsockopt("SOCKET", "TYPE")
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_TYPE
+ end
+
+ it 'returns a Socket::Option for arguments "IP" and "TTL"' do
+ opt = @sock.getsockopt("IP", "TTL")
+
+ opt.level.should == Socket::IPPROTO_IP
+ opt.optname.should == Socket::IP_TTL
+ end
+
+ it 'returns a Socket::Option for arguments "SOCKET" and "REUSEADDR"' do
+ opt = @sock.getsockopt("SOCKET", "REUSEADDR")
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_REUSEADDR
+ end
+
+ it 'returns a Socket::Option for arguments "SOCKET" and "LINGER"' do
+ opt = @sock.getsockopt("SOCKET", "LINGER")
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_LINGER
+ end
+
+ with_feature :udp_cork do
+ it 'returns a Socket::Option for arguments "UDP" and "CORK"' do
+ sock = Socket.new("INET", "DGRAM")
+ begin
+ opt = sock.getsockopt("UDP", "CORK")
+
+ opt.level.should == Socket::IPPROTO_UDP
+ opt.optname.should == Socket::UDP_CORK
+ ensure
+ sock.close
+ end
+ end
+ end
+ end
+
+ describe 'using a String based option' do
+ it 'allows unpacking of a boolean option' do
+ opt = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR).to_s
+
+ opt.unpack('i').should == [0]
+ end
+
+ it 'allows unpacking of a numeric option' do
+ opt = @sock.getsockopt(Socket::IPPROTO_IP, Socket::IP_TTL).to_s
+ array = opt.unpack('i')
+
+ array[0].should be_kind_of(Integer)
+ array[0].should > 0
+ end
+
+ it 'allows unpacking of a struct option' do
+ opt = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER).to_s
+
+ if opt.bytesize == 8
+ opt.unpack('ii').should == [0, 0]
+ else
+ opt.unpack('i').should == [0]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/ioctl_spec.rb b/spec/ruby/library/socket/basicsocket/ioctl_spec.rb
new file mode 100644
index 0000000000..615d92bea8
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/ioctl_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../spec_helper'
+
+describe "Socket::BasicSocket#ioctl" do
+ platform_is :linux do
+ it "passes data from and to a String correctly" do
+ s = Socket.new Socket::AF_INET, Socket::SOCK_DGRAM, 0
+ # /usr/include/net/if.h, structure ifreq
+ # The structure is 32 bytes on x86, 40 bytes on x86_64
+ if_name = ['lo'].pack('a16')
+ buffer = if_name + 'z' * 24
+ # SIOCGIFADDR in /usr/include/bits/ioctls.h
+ s.ioctl 0x8915, buffer
+ s.close
+
+ # Interface name should remain unchanged.
+ buffer[0, 16].should == if_name
+ # lo should have an IPv4 address of 127.0.0.1
+ buffer[16, 2].unpack('S!').first.should == Socket::AF_INET
+ buffer[20, 4].should == "\x7f\0\0\x01"
+ end
+ end
+
+ platform_is :freebsd do
+ it "passes data from and to a String correctly" do
+ s = Socket.new Socket::AF_INET, Socket::SOCK_DGRAM, 0
+ # /usr/include/net/if.h, structure ifreq
+ # The structure is 32 bytes on x86, 40 bytes on x86_64
+ if_name = ['lo0'].pack('a16')
+ buffer = if_name + 'z' * 24
+ # SIOCGIFADDR in /usr/include/bits/ioctls.h
+ s.ioctl 0xc0206921, buffer
+ s.close
+
+ # Interface name should remain unchanged.
+ buffer[0, 16].should == if_name
+ # lo should have an IPv4 address of 127.0.0.1
+ buffer[16, 1].unpack('C').first.should == 16
+ buffer[17, 1].unpack('C').first.should == Socket::AF_INET
+ buffer[20, 4].should == "\x7f\0\0\x01"
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb b/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb
new file mode 100644
index 0000000000..1b6027d26c
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb
@@ -0,0 +1,65 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::BasicSocket#recv_nonblock" do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before :each do
+ @s1 = Socket.new(family, :DGRAM)
+ @s2 = Socket.new(family, :DGRAM)
+ end
+
+ after :each do
+ @s1.close unless @s1.closed?
+ @s2.close unless @s2.closed?
+ end
+
+ platform_is_not :windows do
+ describe 'using an unbound socket' do
+ it 'raises an exception extending IO::WaitReadable' do
+ lambda { @s1.recv_nonblock(1) }.should raise_error(IO::WaitReadable)
+ end
+ end
+ end
+
+ it "raises an exception extending IO::WaitReadable if there's no data available" do
+ @s1.bind(Socket.pack_sockaddr_in(0, ip_address))
+ lambda {
+ @s1.recv_nonblock(5)
+ }.should raise_error(IO::WaitReadable) { |e|
+ platform_is_not :windows do
+ e.should be_kind_of(Errno::EAGAIN)
+ end
+ platform_is :windows do
+ e.should be_kind_of(Errno::EWOULDBLOCK)
+ end
+ }
+ end
+
+ it "receives data after it's ready" do
+ @s1.bind(Socket.pack_sockaddr_in(0, ip_address))
+ @s2.send("aaa", 0, @s1.getsockname)
+ IO.select([@s1], nil, nil, 2)
+ @s1.recv_nonblock(5).should == "aaa"
+ end
+
+ it "allows an output buffer as third argument" do
+ @s1.bind(Socket.pack_sockaddr_in(0, ip_address))
+ @s2.send("data", 0, @s1.getsockname)
+ IO.select([@s1], nil, nil, 2)
+
+ buf = "foo"
+ @s1.recv_nonblock(5, 0, buf)
+ buf.should == "data"
+ end
+
+ it "does not block if there's no data available" do
+ @s1.bind(Socket.pack_sockaddr_in(0, ip_address))
+ @s2.send("a", 0, @s1.getsockname)
+ IO.select([@s1], nil, nil, 2)
+ @s1.recv_nonblock(1).should == "a"
+ lambda {
+ @s1.recv_nonblock(5)
+ }.should raise_error(IO::WaitReadable)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/recv_spec.rb b/spec/ruby/library/socket/basicsocket/recv_spec.rb
new file mode 100644
index 0000000000..a277dc2d97
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/recv_spec.rb
@@ -0,0 +1,159 @@
+# -*- encoding: binary -*-
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket#recv" do
+
+ before :each do
+ @server = TCPServer.new('127.0.0.1', 0)
+ @port = @server.addr[1]
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ ScratchPad.clear
+ end
+
+ it "receives a specified number of bytes of a message from another socket" do
+ t = Thread.new do
+ client = @server.accept
+ ScratchPad.record client.recv(10)
+ client.recv(1) # this recv is important
+ client.close
+ end
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.send('hello', 0)
+ socket.close
+
+ t.join
+ ScratchPad.recorded.should == 'hello'
+ end
+
+ platform_is_not :solaris do
+ it "accepts flags to specify unusual receiving behaviour" do
+ t = Thread.new do
+ client = @server.accept
+
+ # in-band data (TCP), doesn't receive the flag.
+ ScratchPad.record client.recv(10)
+
+ # this recv is important (TODO: explain)
+ client.recv(10)
+ client.close
+ end
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.send('helloU', Socket::MSG_OOB)
+ socket.shutdown(1)
+ t.join
+ socket.close
+ ScratchPad.recorded.should == 'hello'
+ end
+ end
+
+ it "gets lines delimited with a custom separator" do
+ t = Thread.new do
+ client = @server.accept
+ ScratchPad.record client.gets("\377")
+
+ # this call is important (TODO: explain)
+ client.gets(nil)
+ client.close
+ end
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.write("firstline\377secondline\377")
+ socket.close
+
+ t.join
+ ScratchPad.recorded.should == "firstline\377"
+ end
+
+ it "allows an output buffer as third argument" do
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.write("data")
+
+ client = @server.accept
+ buf = "foo"
+ begin
+ client.recv(4, 0, buf)
+ ensure
+ client.close
+ end
+ buf.should == "data"
+
+ socket.close
+ end
+end
+
+describe 'BasicSocket#recv' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'using an unbound socket' do
+ it 'blocks the caller' do
+ lambda { @server.recv(4) }.should block_caller
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ describe 'without any data available' do
+ it 'blocks the caller' do
+ lambda { @server.recv(4) }.should block_caller
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'reads the given amount of bytes' do
+ @client.write('hello')
+
+ @server.recv(2).should == 'he'
+ end
+
+ it 'reads the given amount of bytes when it exceeds the data size' do
+ @client.write('he')
+
+ @server.recv(6).should == 'he'
+ end
+
+ it 'blocks the caller when called twice without new data being available' do
+ @client.write('hello')
+
+ @server.recv(2).should == 'he'
+
+ lambda { @server.recv(4) }.should block_caller
+ end
+
+ it 'takes a peek at the data when using the MSG_PEEK flag' do
+ @client.write('hello')
+
+ @server.recv(2, Socket::MSG_PEEK).should == 'he'
+ @server.recv(2).should == 'he'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/recvmsg_nonblock_spec.rb b/spec/ruby/library/socket/basicsocket/recvmsg_nonblock_spec.rb
new file mode 100644
index 0000000000..8f6b75029c
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/recvmsg_nonblock_spec.rb
@@ -0,0 +1,205 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'BasicSocket#recvmsg_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a disconnected socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ describe 'using an unbound socket' do
+ it 'raises an exception extending IO::WaitReadable' do
+ lambda { @server.recvmsg_nonblock }.should raise_error(IO::WaitReadable)
+ end
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ describe 'without any data available' do
+ it 'raises an exception extending IO::WaitReadable' do
+ lambda { @server.recvmsg_nonblock }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.connect(@server.getsockname)
+
+ @client.write('hello')
+
+ IO.select([@server], nil, nil, 5)
+ end
+
+ it 'returns an Array containing the data, an Addrinfo and the flags' do
+ @server.recvmsg_nonblock.should be_an_instance_of(Array)
+ end
+
+ describe 'without a maximum message length' do
+ it 'reads all the available data' do
+ @server.recvmsg_nonblock[0].should == 'hello'
+ end
+ end
+
+ describe 'with a maximum message length' do
+ platform_is_not :windows do
+ it 'reads up to the maximum amount of bytes' do
+ @server.recvmsg_nonblock(2)[0].should == 'he'
+ end
+ end
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @server.recvmsg_nonblock
+ end
+
+ it 'stores the message at index 0' do
+ @array[0].should == 'hello'
+ end
+
+ it 'stores an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+
+ platform_is_not :windows do
+ it 'stores the flags at index 2' do
+ @array[2].should be_kind_of(Integer)
+ end
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @array[1]
+ end
+
+ it 'uses the IP address of the client' do
+ @addr.ip_address.should == @client.local_address.ip_address
+ end
+
+ it 'uses the correct address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses the correct protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses the correct socket type' do
+ @addr.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses the port number of the client' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'using a connected socket' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without any data available' do
+ it 'raises IO::WaitReadable' do
+ lambda {
+ socket, _ = @server.accept
+ begin
+ socket.recvmsg_nonblock
+ ensure
+ socket.close
+ end
+ }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+
+ @socket, _ = @server.accept
+ IO.select([@socket])
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'returns an Array containing the data, an Addrinfo and the flags' do
+ @socket.recvmsg_nonblock.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @socket.recvmsg_nonblock
+ end
+
+ it 'stores the message at index 0' do
+ @array[0].should == 'hello'
+ end
+
+ it 'stores an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+
+ it 'stores the flags at index 2' do
+ @array[2].should be_kind_of(Integer)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @array[1]
+ end
+
+ it 'raises when receiving the ip_address message' do
+ lambda { @addr.ip_address }.should raise_error(SocketError)
+ end
+
+ it 'uses the correct address family' do
+ @addr.afamily.should == Socket::AF_UNSPEC
+ end
+
+ it 'uses 0 for the protocol family' do
+ @addr.pfamily.should == 0
+ end
+
+ it 'uses the correct socket type' do
+ @addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'raises when receiving the ip_port message' do
+ lambda { @addr.ip_port }.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/recvmsg_spec.rb b/spec/ruby/library/socket/basicsocket/recvmsg_spec.rb
new file mode 100644
index 0000000000..58eb8d0360
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/recvmsg_spec.rb
@@ -0,0 +1,197 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'BasicSocket#recvmsg' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a disconnected socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ describe 'using an unbound socket' do
+ it 'blocks the caller' do
+ lambda { @server.recvmsg }.should block_caller
+ end
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ describe 'without any data available' do
+ it 'blocks the caller' do
+ lambda { @server.recvmsg }.should block_caller
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.connect(@server.getsockname)
+
+ @client.write('hello')
+ end
+
+ it 'returns an Array containing the data, an Addrinfo and the flags' do
+ @server.recvmsg.should be_an_instance_of(Array)
+ end
+
+ describe 'without a maximum message length' do
+ it 'reads all the available data' do
+ @server.recvmsg[0].should == 'hello'
+ end
+ end
+
+ describe 'with a maximum message length' do
+ it 'reads up to the maximum amount of bytes' do
+ @server.recvmsg(2)[0].should == 'he'
+ end
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @server.recvmsg
+ end
+
+ it 'stores the message at index 0' do
+ @array[0].should == 'hello'
+ end
+
+ it 'stores an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+
+ platform_is_not :windows do
+ it 'stores the flags at index 2' do
+ @array[2].should be_kind_of(Integer)
+ end
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @array[1]
+ end
+
+ it 'uses the IP address of the client' do
+ @addr.ip_address.should == @client.local_address.ip_address
+ end
+
+ it 'uses the correct address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses the correct protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses the correct socket type' do
+ @addr.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses the port number of the client' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'using a connected socket' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without any data available' do
+ it 'blocks the caller' do
+ socket, _ = @server.accept
+ begin
+ lambda { socket.recvmsg }.should block_caller
+ ensure
+ socket.close
+ end
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ @socket, _ = @server.accept
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'returns an Array containing the data, an Addrinfo and the flags' do
+ @socket.recvmsg.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @socket.recvmsg
+ end
+
+ it 'stores the message at index 0' do
+ @array[0].should == 'hello'
+ end
+
+ it 'stores an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+
+ it 'stores the flags at index 2' do
+ @array[2].should be_kind_of(Integer)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @array[1]
+ end
+
+ it 'raises when receiving the ip_address message' do
+ lambda { @addr.ip_address }.should raise_error(SocketError)
+ end
+
+ it 'uses the correct address family' do
+ @addr.afamily.should == Socket::AF_UNSPEC
+ end
+
+ it 'returns 0 for the protocol family' do
+ @addr.pfamily.should == 0
+ end
+
+ it 'uses the correct socket type' do
+ @addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'raises when receiving the ip_port message' do
+ lambda { @addr.ip_port }.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/send_spec.rb b/spec/ruby/library/socket/basicsocket/send_spec.rb
new file mode 100644
index 0000000000..c4845fc09e
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/send_spec.rb
@@ -0,0 +1,212 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket#send" do
+ before :each do
+ @server = TCPServer.new('127.0.0.1', 0)
+ @port = @server.addr[1]
+ @socket = TCPSocket.new('127.0.0.1', @port)
+ end
+
+ after :each do
+ @server.closed?.should be_false
+ @socket.closed?.should be_false
+
+ @server.close
+ @socket.close
+ end
+
+ it "sends a message to another socket and returns the number of bytes sent" do
+ data = ""
+ t = Thread.new do
+ client = @server.accept
+ loop do
+ got = client.recv(5)
+ break if got.empty?
+ data << got
+ end
+ client.close
+ end
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ @socket.send('hello', 0).should == 5
+ @socket.shutdown(1) # indicate, that we are done sending
+ @socket.recv(10)
+
+ t.join
+ data.should == 'hello'
+ end
+
+ platform_is_not :solaris, :windows do
+ it "accepts flags to specify unusual sending behaviour" do
+ data = nil
+ peek_data = nil
+ t = Thread.new do
+ client = @server.accept
+ peek_data = client.recv(6, Socket::MSG_PEEK)
+ data = client.recv(6)
+ client.recv(10) # this recv is important
+ client.close
+ end
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ @socket.send('helloU', Socket::MSG_PEEK | Socket::MSG_OOB).should == 6
+ @socket.shutdown # indicate, that we are done sending
+
+ t.join
+ peek_data.should == "hello"
+ data.should == 'hello'
+ end
+ end
+
+ it "accepts a sockaddr as recipient address" do
+ data = ""
+ t = Thread.new do
+ client = @server.accept
+ loop do
+ got = client.recv(5)
+ break if got.empty?
+ data << got
+ end
+ client.close
+ end
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ sockaddr = Socket.pack_sockaddr_in(@port, "127.0.0.1")
+ @socket.send('hello', 0, sockaddr).should == 5
+ @socket.shutdown # indicate, that we are done sending
+
+ t.join
+ data.should == 'hello'
+ end
+end
+
+describe 'BasicSocket#send' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a disconnected socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without a destination address' do
+ it "raises #{SocketSpecs.dest_addr_req_error}" do
+ lambda { @client.send('hello', 0) }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+
+ describe 'with a destination address as a String' do
+ it 'returns the amount of sent bytes' do
+ @client.send('hello', 0, @server.getsockname).should == 5
+ end
+
+ it 'does not persist the connection after writing to the socket' do
+ @client.send('hello', 0, @server.getsockname)
+
+ lambda { @client.send('hello', 0) }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+
+ describe 'with a destination address as an Addrinfo' do
+ it 'returns the amount of sent bytes' do
+ @client.send('hello', 0, @server.connect_address).should == 5
+ end
+ end
+ end
+
+ describe 'using a connected UDP socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without a destination address argument' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'returns the amount of bytes written' do
+ @client.send('hello', 0).should == 5
+ end
+ end
+
+ describe 'with a destination address argument' do
+ before do
+ @alt_server = Socket.new(family, :DGRAM)
+
+ @alt_server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @alt_server.close
+ end
+
+ it 'sends the message to the given address instead' do
+ @client.send('hello', 0, @alt_server.getsockname).should == 5
+
+ lambda { @server.recv(5) }.should block_caller
+
+ @alt_server.recv(5).should == 'hello'
+ end
+
+ it 'does not persist the alternative connection after writing to the socket' do
+ @client.send('hello', 0, @alt_server.getsockname)
+
+ @client.connect(@server.getsockname)
+ @client.send('world', 0)
+
+ @server.recv(5).should == 'world'
+ end
+ end
+ end
+
+ platform_is_not :darwin, :windows do
+ describe 'using a connected TCP socket' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'using the MSG_OOB flag' do
+ it 'sends an out-of-band message' do
+ socket, _ = @server.accept
+ socket.setsockopt(:SOCKET, :OOBINLINE, true)
+ @client.send('a', Socket::MSG_OOB).should == 1
+ begin
+ socket.recv(10).should == 'a'
+ ensure
+ socket.close
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/sendmsg_nonblock_spec.rb b/spec/ruby/library/socket/basicsocket/sendmsg_nonblock_spec.rb
new file mode 100644
index 0000000000..de5e2aa749
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/sendmsg_nonblock_spec.rb
@@ -0,0 +1,104 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'BasicSocket#sendmsg_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a disconnected socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without a destination address' do
+ it "raises #{SocketSpecs.dest_addr_req_error}" do
+ lambda { @client.sendmsg_nonblock('hello') }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+
+ describe 'with a destination address as a String' do
+ it 'returns the amount of sent bytes' do
+ @client.sendmsg_nonblock('hello', 0, @server.getsockname).should == 5
+ end
+ end
+
+ describe 'with a destination address as an Addrinfo' do
+ it 'returns the amount of sent bytes' do
+ @client.sendmsg_nonblock('hello', 0, @server.connect_address).should == 5
+ end
+ end
+ end
+
+ describe 'using a connected UDP socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without a destination address argument' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'returns the amount of bytes written' do
+ @client.sendmsg_nonblock('hello').should == 5
+ end
+ end
+
+ describe 'with a destination address argument' do
+ before do
+ @alt_server = Socket.new(family, :DGRAM)
+ @alt_server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @alt_server.close
+ end
+
+ it 'sends the message to the given address instead' do
+ @client.sendmsg_nonblock('hello', 0, @alt_server.getsockname).should == 5
+ lambda { @server.recv(5) }.should block_caller
+ @alt_server.recv(5).should == 'hello'
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'using a connected TCP socket' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'raises IO::WaitWritable when the underlying buffer is full' do
+ lambda {
+ 10.times { @client.sendmsg_nonblock('hello' * 1_000_000) }
+ }.should raise_error(IO::WaitWritable)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/sendmsg_spec.rb b/spec/ruby/library/socket/basicsocket/sendmsg_spec.rb
new file mode 100644
index 0000000000..f2c11f443a
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/sendmsg_spec.rb
@@ -0,0 +1,111 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'BasicSocket#sendmsg' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a disconnected socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ describe 'without a destination address' do
+ it "raises #{SocketSpecs.dest_addr_req_error}" do
+ lambda { @client.sendmsg('hello') }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+ end
+
+ describe 'with a destination address as a String' do
+ it 'returns the amount of sent bytes' do
+ @client.sendmsg('hello', 0, @server.getsockname).should == 5
+ end
+ end
+
+ describe 'with a destination address as an Addrinfo' do
+ it 'returns the amount of sent bytes' do
+ @client.sendmsg('hello', 0, @server.connect_address).should == 5
+ end
+ end
+ end
+
+ describe 'using a connected UDP socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without a destination address argument' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'returns the amount of bytes written' do
+ @client.sendmsg('hello').should == 5
+ end
+ end
+
+ describe 'with a destination address argument' do
+ before do
+ @alt_server = Socket.new(family, :DGRAM)
+
+ @alt_server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @alt_server.close
+ end
+
+ it 'sends the message to the given address instead' do
+ @client.sendmsg('hello', 0, @alt_server.getsockname).should == 5
+
+ lambda { @server.recv(5) }.should block_caller
+
+ @alt_server.recv(5).should == 'hello'
+ end
+ end
+ end
+
+ platform_is_not :windows do # spurious
+ describe 'using a connected TCP socket' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'blocks when the underlying buffer is full' do
+ # Buffer sizes may differ per platform, so sadly this is the only
+ # reliable way of testing blocking behaviour.
+ lambda do
+ 10.times { @client.sendmsg('hello' * 1_000_000) }
+ end.should block_caller
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/setsockopt_spec.rb b/spec/ruby/library/socket/basicsocket/setsockopt_spec.rb
new file mode 100644
index 0000000000..1471b03798
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/setsockopt_spec.rb
@@ -0,0 +1,336 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket#setsockopt" do
+
+ before :each do
+ @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ end
+
+ after :each do
+ @sock.close unless @sock.closed?
+ end
+
+ it "sets the socket linger to 0" do
+ linger = [0, 0].pack("ii")
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, linger).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER).to_s
+
+ if (n.size == 8) # linger struct on some platforms, not just a value
+ n.should == [0, 0].pack("ii")
+ else
+ n.should == [0].pack("i")
+ end
+ end
+
+ it "sets the socket linger to some positive value" do
+ linger = [64, 64].pack("ii")
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, linger).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER).to_s
+ if (n.size == 8) # linger struct on some platforms, not just a value
+ a = n.unpack('ii')
+ a[0].should_not == 0
+ a[1].should == 64
+ else
+ n.should == [64].pack("i")
+ end
+ end
+
+ platform_is_not :windows do
+ it "raises EINVAL if passed wrong linger value" do
+ lambda do
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, 0)
+ end.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ platform_is_not :aix do
+ # A known bug in AIX. getsockopt(2) does not properly set
+ # the fifth argument for SO_TYPE, SO_OOBINLINE, SO_BROADCAST, etc.
+
+ it "sets the socket option Socket::SO_OOBINLINE" do
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, true).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should_not == [0].pack("i")
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, false).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should == [0].pack("i")
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 1).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should_not == [0].pack("i")
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 0).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should == [0].pack("i")
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 2).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should_not == [0].pack("i")
+
+ platform_is_not :windows do
+ lambda {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "")
+ }.should raise_error(SystemCallError)
+ end
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "blah").should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should_not == [0].pack("i")
+
+ platform_is_not :windows do
+ lambda {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "0")
+ }.should raise_error(SystemCallError)
+ end
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "\x00\x00\x00\x00").should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should == [0].pack("i")
+
+ platform_is_not :windows do
+ lambda {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "1")
+ }.should raise_error(SystemCallError)
+ end
+
+ platform_is_not :windows do
+ lambda {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "\x00\x00\x00")
+ }.should raise_error(SystemCallError)
+ end
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, [1].pack('i')).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should_not == [0].pack("i")
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, [0].pack('i')).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should == [0].pack("i")
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, [1000].pack('i')).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should_not == [0].pack("i")
+ end
+ end
+
+ it "sets the socket option Socket::SO_SNDBUF" do
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, 4000).should == 0
+ sndbuf = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ # might not always be possible to set to exact size
+ sndbuf.unpack('i')[0].should >= 4000
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, true).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ n.unpack('i')[0].should >= 1
+
+ lambda {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, nil).should == 0
+ }.should raise_error(TypeError)
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, 1).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ n.unpack('i')[0].should >= 1
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, 2).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ n.unpack('i')[0].should >= 2
+
+ lambda {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "")
+ }.should raise_error(SystemCallError)
+
+ lambda {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "bla")
+ }.should raise_error(SystemCallError)
+
+ lambda {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "0")
+ }.should raise_error(SystemCallError)
+
+ lambda {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "1")
+ }.should raise_error(SystemCallError)
+
+ lambda {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "\x00\x00\x00")
+ }.should raise_error(SystemCallError)
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "\x00\x00\x01\x00").should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ n.unpack('i')[0].should >= "\x00\x00\x01\x00".unpack('i')[0]
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, [4000].pack('i')).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ n.unpack('i')[0].should >= 4000
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, [1000].pack('i')).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ n.unpack('i')[0].should >= 1000
+ end
+
+ platform_is_not :aix do
+ describe 'accepts Socket::Option as argument' do
+ it 'boolean' do
+ option = Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true)
+ @sock.setsockopt(option).should == 0
+ @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE).bool.should == true
+ end
+
+ it 'int' do
+ option = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1)
+ @sock.setsockopt(option).should == 0
+ @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE).bool.should == true
+ end
+ end
+ end
+
+ platform_is :aix do
+ describe 'accepts Socket::Option as argument' do
+ it 'boolean' do
+ option = Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true)
+ @sock.setsockopt(option).should == 0
+ end
+
+ it 'int' do
+ option = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1)
+ @sock.setsockopt(option).should == 0
+ end
+ end
+ end
+
+ describe 'accepts Socket::Option as argument' do
+ it 'linger' do
+ option = Socket::Option.linger(true, 10)
+ @sock.setsockopt(option).should == 0
+ onoff, seconds = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER).linger
+ seconds.should == 10
+ # Both results can be produced depending on the OS and value of Socket::SO_LINGER
+ [true, Socket::SO_LINGER].should include(onoff)
+ end
+ end
+end
+
+describe 'BasicSocket#setsockopt' do
+ describe 'using a STREAM socket' do
+ before do
+ @socket = Socket.new(:INET, :STREAM)
+ end
+
+ after do
+ @socket.close
+ end
+
+ describe 'using separate arguments with Symbols' do
+ it 'raises TypeError when the first argument is nil' do
+ lambda { @socket.setsockopt(nil, :REUSEADDR, true) }.should raise_error(TypeError)
+ end
+
+ it 'sets a boolean option' do
+ @socket.setsockopt(:SOCKET, :REUSEADDR, true).should == 0
+ @socket.getsockopt(:SOCKET, :REUSEADDR).bool.should == true
+ end
+
+ it 'sets an integer option' do
+ @socket.setsockopt(:IP, :TTL, 255).should == 0
+ @socket.getsockopt(:IP, :TTL).int.should == 255
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ it 'sets an IPv6 boolean option' do
+ socket = Socket.new(:INET6, :STREAM)
+ begin
+ socket.setsockopt(:IPV6, :V6ONLY, true).should == 0
+ socket.getsockopt(:IPV6, :V6ONLY).bool.should == true
+ ensure
+ socket.close
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ it 'raises Errno::EINVAL when setting an invalid option value' do
+ lambda { @socket.setsockopt(:SOCKET, :OOBINLINE, 'bla') }.should raise_error(Errno::EINVAL)
+ end
+ end
+ end
+
+ describe 'using separate arguments with Symbols' do
+ it 'sets a boolean option' do
+ @socket.setsockopt('SOCKET', 'REUSEADDR', true).should == 0
+ @socket.getsockopt(:SOCKET, :REUSEADDR).bool.should == true
+ end
+
+ it 'sets an integer option' do
+ @socket.setsockopt('IP', 'TTL', 255).should == 0
+ @socket.getsockopt(:IP, :TTL).int.should == 255
+ end
+ end
+
+ describe 'using separate arguments with constants' do
+ it 'sets a boolean option' do
+ @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true).should == 0
+ @socket.getsockopt(:SOCKET, :REUSEADDR).bool.should == true
+ end
+
+ it 'sets an integer option' do
+ @socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_TTL, 255).should == 0
+ @socket.getsockopt(:IP, :TTL).int.should == 255
+ end
+ end
+
+ describe 'using separate arguments with custom objects' do
+ it 'sets a boolean option' do
+ level = mock(:level)
+ name = mock(:name)
+
+ level.stub!(:to_str).and_return('SOCKET')
+ name.stub!(:to_str).and_return('REUSEADDR')
+
+ @socket.setsockopt(level, name, true).should == 0
+ end
+ end
+
+ describe 'using a Socket::Option as the first argument' do
+ it 'sets a boolean option' do
+ @socket.setsockopt(Socket::Option.bool(:INET, :SOCKET, :REUSEADDR, true)).should == 0
+ @socket.getsockopt(:SOCKET, :REUSEADDR).bool.should == true
+ end
+
+ it 'sets an integer option' do
+ @socket.setsockopt(Socket::Option.int(:INET, :IP, :TTL, 255)).should == 0
+ @socket.getsockopt(:IP, :TTL).int.should == 255
+ end
+
+ it 'raises ArgumentError when passing 2 arguments' do
+ option = Socket::Option.bool(:INET, :SOCKET, :REUSEADDR, true)
+ lambda { @socket.setsockopt(option, :REUSEADDR) }.should raise_error(ArgumentError)
+ end
+
+ it 'raises TypeError when passing 3 arguments' do
+ option = Socket::Option.bool(:INET, :SOCKET, :REUSEADDR, true)
+ lambda { @socket.setsockopt(option, :REUSEADDR, true) }.should raise_error(TypeError)
+ end
+ end
+ end
+
+ with_feature :unix_socket do
+ describe 'using a UNIX socket' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close
+ rm_r @path
+ end
+
+ it 'sets a boolean option' do
+ @server.setsockopt(:SOCKET, :REUSEADDR, true)
+ @server.getsockopt(:SOCKET, :REUSEADDR).bool.should == true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/shutdown_spec.rb b/spec/ruby/library/socket/basicsocket/shutdown_spec.rb
new file mode 100644
index 0000000000..87a32ae1dc
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/shutdown_spec.rb
@@ -0,0 +1,155 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+platform_is_not :windows do # hangs
+ describe "Socket::BasicSocket#shutdown" do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM)
+ @client = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'using an Integer' do
+ it 'shuts down a socket for reading' do
+ @client.shutdown(Socket::SHUT_RD)
+
+ @client.recv(1).should be_empty
+ end
+
+ it 'shuts down a socket for writing' do
+ @client.shutdown(Socket::SHUT_WR)
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'shuts down a socket for reading and writing' do
+ @client.shutdown(Socket::SHUT_RDWR)
+
+ @client.recv(1).should be_empty
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'raises ArgumentError when using an invalid option' do
+ lambda { @server.shutdown(666) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe 'using a Symbol' do
+ it 'shuts down a socket for reading using :RD' do
+ @client.shutdown(:RD)
+
+ @client.recv(1).should be_empty
+ end
+
+ it 'shuts down a socket for reading using :SHUT_RD' do
+ @client.shutdown(:SHUT_RD)
+
+ @client.recv(1).should be_empty
+ end
+
+ it 'shuts down a socket for writing using :WR' do
+ @client.shutdown(:WR)
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'shuts down a socket for writing using :SHUT_WR' do
+ @client.shutdown(:SHUT_WR)
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'shuts down a socket for reading and writing' do
+ @client.shutdown(:RDWR)
+
+ @client.recv(1).should be_empty
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'raises ArgumentError when using an invalid option' do
+ lambda { @server.shutdown(:Nope) }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using a String' do
+ it 'shuts down a socket for reading using "RD"' do
+ @client.shutdown('RD')
+
+ @client.recv(1).should be_empty
+ end
+
+ it 'shuts down a socket for reading using "SHUT_RD"' do
+ @client.shutdown('SHUT_RD')
+
+ @client.recv(1).should be_empty
+ end
+
+ it 'shuts down a socket for writing using "WR"' do
+ @client.shutdown('WR')
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'shuts down a socket for writing using "SHUT_WR"' do
+ @client.shutdown('SHUT_WR')
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'raises ArgumentError when using an invalid option' do
+ lambda { @server.shutdown('Nope') }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using an object that responds to #to_str' do
+ before do
+ @dummy = mock(:dummy)
+ end
+
+ it 'shuts down a socket for reading using "RD"' do
+ @dummy.stub!(:to_str).and_return('RD')
+
+ @client.shutdown(@dummy)
+
+ @client.recv(1).should be_empty
+ end
+
+ it 'shuts down a socket for reading using "SHUT_RD"' do
+ @dummy.stub!(:to_str).and_return('SHUT_RD')
+
+ @client.shutdown(@dummy)
+
+ @client.recv(1).should be_empty
+ end
+
+ it 'shuts down a socket for reading and writing' do
+ @dummy.stub!(:to_str).and_return('RDWR')
+
+ @client.shutdown(@dummy)
+
+ @client.recv(1).should be_empty
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+ end
+
+ describe 'using an object that does not respond to #to_str' do
+ it 'raises TypeError' do
+ lambda { @server.shutdown(mock(:dummy)) }.should raise_error(TypeError)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/constants/constants_spec.rb b/spec/ruby/library/socket/constants/constants_spec.rb
new file mode 100644
index 0000000000..710af12828
--- /dev/null
+++ b/spec/ruby/library/socket/constants/constants_spec.rb
@@ -0,0 +1,108 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::Constants" do
+ it "defines socket types" do
+ consts = ["SOCK_DGRAM", "SOCK_RAW", "SOCK_RDM", "SOCK_SEQPACKET", "SOCK_STREAM"]
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ it "defines protocol families" do
+ consts = ["PF_INET6", "PF_INET", "PF_UNIX", "PF_UNSPEC"]
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ platform_is_not :aix do
+ it "defines PF_IPX protocol" do
+ Socket::Constants.should have_constant("PF_IPX")
+ end
+ end
+
+ it "defines address families" do
+ consts = ["AF_INET6", "AF_INET", "AF_UNIX", "AF_UNSPEC"]
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ platform_is_not :aix do
+ it "defines AF_IPX address" do
+ Socket::Constants.should have_constant("AF_IPX")
+ end
+ end
+
+ it "defines send/receive options" do
+ consts = ["MSG_DONTROUTE", "MSG_OOB", "MSG_PEEK"]
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ it "defines socket level options" do
+ consts = ["SOL_SOCKET"]
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ it "defines socket options" do
+ consts = ["SO_BROADCAST", "SO_DEBUG", "SO_DONTROUTE", "SO_ERROR", "SO_KEEPALIVE", "SO_LINGER",
+ "SO_OOBINLINE", "SO_RCVBUF", "SO_REUSEADDR", "SO_SNDBUF", "SO_TYPE"]
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ it "defines multicast options" do
+ consts = ["IP_ADD_MEMBERSHIP",
+ "IP_MULTICAST_LOOP", "IP_MULTICAST_TTL"]
+ platform_is_not :windows do
+ consts += ["IP_DEFAULT_MULTICAST_LOOP", "IP_DEFAULT_MULTICAST_TTL"]
+ end
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ platform_is_not :solaris, :windows, :aix do
+ it "defines multicast options" do
+ consts = ["IP_MAX_MEMBERSHIPS"]
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+ end
+
+ it "defines TCP options" do
+ consts = ["TCP_NODELAY"]
+ platform_is_not :windows do
+ consts << "TCP_MAXSEG"
+ end
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ platform_is_not :windows do
+ it 'defines SCM options' do
+ Socket::Constants.should have_constant('SCM_RIGHTS')
+ end
+
+ it 'defines error options' do
+ consts = ["EAI_ADDRFAMILY", "EAI_NODATA"]
+
+ # FreeBSD (11.1, at least) obsoletes EAI_ADDRFAMILY and EAI_NODATA
+ platform_is :freebsd do
+ consts = %w(EAI_MEMORY)
+ end
+
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/fixtures/classes.rb b/spec/ruby/library/socket/fixtures/classes.rb
new file mode 100644
index 0000000000..b73fd0fa4c
--- /dev/null
+++ b/spec/ruby/library/socket/fixtures/classes.rb
@@ -0,0 +1,148 @@
+require 'socket'
+
+module SocketSpecs
+ # helper to get the hostname associated to 127.0.0.1 or the given ip
+ def self.hostname(ip = "127.0.0.1")
+ # Calculate each time, without caching, since the result might
+ # depend on things like do_not_reverse_lookup mode, which is
+ # changing from test to test
+ Socket.getaddrinfo(ip, nil)[0][2]
+ end
+
+ def self.hostname_reverse_lookup(ip = "127.0.0.1")
+ Socket.getaddrinfo(ip, nil, 0, 0, 0, 0, true)[0][2]
+ end
+
+ def self.addr(which=:ipv4)
+ case which
+ when :ipv4
+ host = "127.0.0.1"
+ when :ipv6
+ host = "::1"
+ end
+ Socket.getaddrinfo(host, nil)[0][3]
+ end
+
+ def self.reserved_unused_port
+ # https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
+ 0
+ end
+
+ def self.sockaddr_in(port, host)
+ Socket::SockAddr_In.new(Socket.sockaddr_in(port, host))
+ end
+
+ def self.socket_path
+ path = tmp("unix.sock", false)
+ # Check for too long unix socket path (max 108 bytes including \0 => 107)
+ # Note that Linux accepts not null-terminated paths but the man page advises against it.
+ if path.bytesize > 107
+ path = "/tmp/unix_server_spec.socket"
+ end
+ rm_socket(path)
+ path
+ end
+
+ def self.rm_socket(path)
+ File.delete(path) if File.exist?(path)
+ end
+
+ def self.ipv6_available?
+ @ipv6_available ||= begin
+ server = TCPServer.new('::1', 0)
+ rescue Errno::EAFNOSUPPORT, Errno::EADDRNOTAVAIL, SocketError
+ :no
+ else
+ server.close
+ :yes
+ end
+ @ipv6_available == :yes
+ end
+
+ def self.each_ip_protocol
+ describe 'using IPv4' do
+ yield Socket::AF_INET, '127.0.0.1', 'AF_INET'
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ describe 'using IPv6' do
+ yield Socket::AF_INET6, '::1', 'AF_INET6'
+ end
+ end
+ end
+
+ def self.loop_with_timeout(timeout = 5)
+ require 'timeout'
+ time = Time.now
+
+ loop do
+ if Time.now - time >= timeout
+ raise TimeoutError, "Did not succeed within #{timeout} seconds"
+ end
+
+ sleep 0.01 # necessary on OSX; don't know why
+ yield
+ end
+ end
+
+ def self.wait_until_success(timeout = 5)
+ loop_with_timeout(timeout) do
+ begin
+ return yield
+ rescue
+ end
+ end
+ end
+
+ def self.dest_addr_req_error
+ error = Errno::EDESTADDRREQ
+ platform_is :windows do
+ error = Errno::ENOTCONN
+ end
+ error
+ end
+
+ # TCPServer echo server accepting one connection
+ class SpecTCPServer
+ attr_reader :hostname, :port
+
+ def initialize
+ @hostname = SocketSpecs.hostname
+ @server = TCPServer.new @hostname, 0
+ @port = @server.addr[1]
+
+ log "SpecTCPServer starting on #{@hostname}:#{@port}"
+
+ @thread = Thread.new do
+ socket = @server.accept
+ log "SpecTCPServer accepted connection: #{socket}"
+ service socket
+ end
+ end
+
+ def service(socket)
+ begin
+ data = socket.recv(1024)
+
+ return if data.empty?
+ log "SpecTCPServer received: #{data.inspect}"
+
+ return if data == "QUIT"
+
+ socket.send data, 0
+ ensure
+ socket.close
+ end
+ end
+
+ def shutdown
+ log "SpecTCPServer shutting down"
+ @thread.join
+ @server.close
+ end
+
+ def log(message)
+ @logger.puts message if @logger
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/fixtures/send_io.txt b/spec/ruby/library/socket/fixtures/send_io.txt
new file mode 100644
index 0000000000..eaaa1eb3ec
--- /dev/null
+++ b/spec/ruby/library/socket/fixtures/send_io.txt
@@ -0,0 +1 @@
+This data is magic.
diff --git a/spec/ruby/library/socket/ipsocket/addr_spec.rb b/spec/ruby/library/socket/ipsocket/addr_spec.rb
new file mode 100644
index 0000000000..07f4cb20c9
--- /dev/null
+++ b/spec/ruby/library/socket/ipsocket/addr_spec.rb
@@ -0,0 +1,105 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::IPSocket#addr" do
+ before :each do
+ @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
+ @socket = TCPServer.new("127.0.0.1", 0)
+ end
+
+ after :each do
+ @socket.close unless @socket.closed?
+ BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup
+ end
+
+ it "returns an array with the socket's information" do
+ @socket.do_not_reverse_lookup = false
+ BasicSocket.do_not_reverse_lookup = false
+ addrinfo = @socket.addr
+ addrinfo[0].should == "AF_INET"
+ addrinfo[1].should be_kind_of(Integer)
+ addrinfo[2].should == SocketSpecs.hostname
+ addrinfo[3].should == "127.0.0.1"
+ end
+
+ it "returns an address in the array if do_not_reverse_lookup is true" do
+ @socket.do_not_reverse_lookup = true
+ BasicSocket.do_not_reverse_lookup = true
+ addrinfo = @socket.addr
+ addrinfo[0].should == "AF_INET"
+ addrinfo[1].should be_kind_of(Integer)
+ addrinfo[2].should == "127.0.0.1"
+ addrinfo[3].should == "127.0.0.1"
+ end
+
+ it "returns an address in the array if passed false" do
+ addrinfo = @socket.addr(false)
+ addrinfo[0].should == "AF_INET"
+ addrinfo[1].should be_kind_of(Integer)
+ addrinfo[2].should == "127.0.0.1"
+ addrinfo[3].should == "127.0.0.1"
+ end
+end
+
+describe 'Socket::IPSocket#addr' do
+ SocketSpecs.each_ip_protocol do |family, ip_address, family_name|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'without reverse lookups' do
+ before do
+ @hostname = Socket.getaddrinfo(ip_address, nil)[0][2]
+ end
+
+ it 'returns an Array containing address information' do
+ @server.addr.should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'with reverse lookups' do
+ before do
+ @hostname = Socket.getaddrinfo(ip_address, nil, nil, 0, 0, 0, true)[0][2]
+ end
+
+ describe 'using true as the argument' do
+ it 'returns an Array containing address information' do
+ @server.addr(true).should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'using :hostname as the argument' do
+ it 'returns an Array containing address information' do
+ @server.addr(:hostname).should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'using :cats as the argument' do
+ it 'raises ArgumentError' do
+ lambda { @server.addr(:cats) }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe 'with do_not_reverse_lookup disabled on socket level' do
+ before do
+ @server.do_not_reverse_lookup = false
+
+ @hostname = Socket.getaddrinfo(ip_address, nil, nil, 0, 0, 0, true)[0][2]
+ end
+
+ after do
+ @server.do_not_reverse_lookup = true
+ end
+
+ it 'returns an Array containing address information' do
+ @server.addr.should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ipsocket/getaddress_spec.rb b/spec/ruby/library/socket/ipsocket/getaddress_spec.rb
new file mode 100644
index 0000000000..2ee35af5b5
--- /dev/null
+++ b/spec/ruby/library/socket/ipsocket/getaddress_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::IPSocket#getaddress" do
+
+ it "returns the IP address of hostname" do
+ addr_local = IPSocket.getaddress(SocketSpecs.hostname)
+ ["127.0.0.1", "::1"].include?(addr_local).should == true
+ end
+
+ it "returns the IP address when passed an IP" do
+ IPSocket.getaddress("127.0.0.1").should == "127.0.0.1"
+ IPSocket.getaddress("0.0.0.0").should == "0.0.0.0"
+ IPSocket.getaddress('::1').should == '::1'
+ end
+
+ # There is no way to make this fail-proof on all machines, because
+ # DNS servers like opendns return A records for ANY host, including
+ # traditionally invalidly named ones.
+ quarantine! do
+ it "raises an error on unknown hostnames" do
+ lambda {
+ IPSocket.getaddress("rubyspecdoesntexist.fallingsnow.net")
+ }.should raise_error(SocketError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ipsocket/peeraddr_spec.rb b/spec/ruby/library/socket/ipsocket/peeraddr_spec.rb
new file mode 100644
index 0000000000..26aa61d1c4
--- /dev/null
+++ b/spec/ruby/library/socket/ipsocket/peeraddr_spec.rb
@@ -0,0 +1,117 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::IPSocket#peeraddr" do
+ before :each do
+ @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
+ @server = TCPServer.new("127.0.0.1", 0)
+ @port = @server.addr[1]
+ @client = TCPSocket.new("127.0.0.1", @port)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @client.close unless @client.closed?
+ BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup
+ end
+
+ it "raises error if socket is not connected" do
+ lambda {
+ @server.peeraddr
+ }.should raise_error(Errno::ENOTCONN)
+ end
+
+ it "returns an array of information on the peer" do
+ @client.do_not_reverse_lookup = false
+ BasicSocket.do_not_reverse_lookup = false
+ addrinfo = @client.peeraddr
+ addrinfo[0].should == "AF_INET"
+ addrinfo[1].should == @port
+ addrinfo[2].should == SocketSpecs.hostname
+ addrinfo[3].should == "127.0.0.1"
+ end
+
+ it "returns an IP instead of hostname if do_not_reverse_lookup is true" do
+ @client.do_not_reverse_lookup = true
+ BasicSocket.do_not_reverse_lookup = true
+ addrinfo = @client.peeraddr
+ addrinfo[0].should == "AF_INET"
+ addrinfo[1].should == @port
+ addrinfo[2].should == "127.0.0.1"
+ addrinfo[3].should == "127.0.0.1"
+ end
+
+ it "returns an IP instead of hostname if passed false" do
+ addrinfo = @client.peeraddr(false)
+ addrinfo[0].should == "AF_INET"
+ addrinfo[1].should == @port
+ addrinfo[2].should == "127.0.0.1"
+ addrinfo[3].should == "127.0.0.1"
+ end
+end
+
+describe 'Socket::IPSocket#peeraddr' do
+ SocketSpecs.each_ip_protocol do |family, ip_address, family_name|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ @client = TCPSocket.new(ip_address, @port)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without reverse lookups' do
+ before do
+ @hostname = Socket.getaddrinfo(ip_address, nil)[0][2]
+ end
+
+ it 'returns an Array containing address information' do
+ @client.peeraddr.should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'with reverse lookups' do
+ before do
+ @hostname = Socket.getaddrinfo(ip_address, nil, nil, 0, 0, 0, true)[0][2]
+ end
+
+ describe 'using true as the argument' do
+ it 'returns an Array containing address information' do
+ @client.peeraddr(true).should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'using :hostname as the argument' do
+ it 'returns an Array containing address information' do
+ @client.peeraddr(:hostname).should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'using :cats as the argument' do
+ it 'raises ArgumentError' do
+ lambda { @client.peeraddr(:cats) }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe 'with do_not_reverse_lookup disabled on socket level' do
+ before do
+ @client.do_not_reverse_lookup = false
+
+ @hostname = Socket.getaddrinfo(ip_address, nil, nil, 0, 0, 0, true)[0][2]
+ @port = @client.local_address.ip_port
+ end
+
+ after do
+ @client.do_not_reverse_lookup = true
+ end
+
+ it 'returns an Array containing address information' do
+ @client.addr.should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ipsocket/recvfrom_spec.rb b/spec/ruby/library/socket/ipsocket/recvfrom_spec.rb
new file mode 100644
index 0000000000..3bcb7b8f02
--- /dev/null
+++ b/spec/ruby/library/socket/ipsocket/recvfrom_spec.rb
@@ -0,0 +1,123 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::IPSocket#recvfrom" do
+ before :each do
+ @server = TCPServer.new("127.0.0.1", 0)
+ @port = @server.addr[1]
+ @client = TCPSocket.new("127.0.0.1", @port)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @client.close unless @client.closed?
+ end
+
+ it "reads data from the connection" do
+ data = nil
+ t = Thread.new do
+ client = @server.accept
+ begin
+ data = client.recvfrom(6)
+ ensure
+ client.close
+ end
+ end
+
+ @client.send('hello', 0)
+ @client.shutdown rescue nil
+ # shutdown may raise Errno::ENOTCONN when sent data is pending.
+ t.join
+
+ data.first.should == 'hello'
+ end
+
+ it "reads up to len bytes" do
+ data = nil
+ t = Thread.new do
+ client = @server.accept
+ begin
+ data = client.recvfrom(3)
+ ensure
+ client.close
+ end
+ end
+
+ @client.send('hello', 0)
+ @client.shutdown rescue nil
+ t.join
+
+ data.first.should == 'hel'
+ end
+
+ it "returns an array with the data and connection info" do
+ data = nil
+ t = Thread.new do
+ client = @server.accept
+ data = client.recvfrom(3)
+ client.close
+ end
+
+ @client.send('hello', 0)
+ @client.shutdown rescue nil
+ t.join
+
+ data.size.should == 2
+ data.first.should == "hel"
+ # This does not apply to every platform, dependant on recvfrom(2)
+ # data.last.should == nil
+ end
+end
+
+describe 'Socket::IPSocket#recvfrom' do
+ SocketSpecs.each_ip_protocol do |family, ip_address, family_name|
+ before do
+ @server = UDPSocket.new(family)
+ @client = UDPSocket.new(family)
+
+ @server.bind(ip_address, 0)
+ @client.connect(ip_address, @server.connect_address.ip_port)
+
+ @hostname = Socket.getaddrinfo(ip_address, nil)[0][2]
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns an Array containing up to N bytes and address information' do
+ @client.write('hello')
+
+ port = @client.local_address.ip_port
+ ret = @server.recvfrom(2)
+
+ ret.should == ['he', [family_name, port, @hostname, ip_address]]
+ end
+
+ it 'allows specifying of flags when receiving data' do
+ @client.write('hello')
+
+ @server.recvfrom(2, Socket::MSG_PEEK)[0].should == 'he'
+
+ @server.recvfrom(2)[0].should == 'he'
+ end
+
+ describe 'using reverse lookups' do
+ before do
+ @server.do_not_reverse_lookup = false
+
+ @hostname = Socket.getaddrinfo(ip_address, nil, 0, 0, 0, 0, true)[0][2]
+ end
+
+ it 'includes the hostname in the address Array' do
+ @client.write('hello')
+
+ port = @client.local_address.ip_port
+ ret = @server.recvfrom(2)
+
+ ret.should == ['he', [family_name, port, @hostname, ip_address]]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/option/bool_spec.rb b/spec/ruby/library/socket/option/bool_spec.rb
new file mode 100644
index 0000000000..c4f8a277ba
--- /dev/null
+++ b/spec/ruby/library/socket/option/bool_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::Option.bool" do
+ it "creates a new Socket::Option" do
+ so = Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true)
+ so.should be_an_instance_of(Socket::Option)
+ so.family.should == Socket::AF_INET
+ so.level.should == Socket::SOL_SOCKET
+ so.optname.should == Socket::SO_KEEPALIVE
+ so.data.should == [1].pack('i')
+ end
+end
+
+describe "Socket::Option#bool" do
+ it "returns boolean value" do
+ Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true).bool.should == true
+ Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, false).bool.should == false
+ end
+
+ platform_is_not :windows do
+ it 'raises TypeError when called on a non boolean option' do
+ opt = Socket::Option.linger(1, 4)
+ lambda { opt.bool }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/option/initialize_spec.rb b/spec/ruby/library/socket/option/initialize_spec.rb
new file mode 100644
index 0000000000..0d4621b71e
--- /dev/null
+++ b/spec/ruby/library/socket/option/initialize_spec.rb
@@ -0,0 +1,83 @@
+require_relative '../spec_helper'
+
+describe 'Socket::Option#initialize' do
+ before do
+ @bool = [0].pack('i')
+ end
+
+ describe 'using Integers' do
+ it 'returns a Socket::Option' do
+ opt = Socket::Option
+ .new(Socket::AF_INET, Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, @bool)
+
+ opt.should be_an_instance_of(Socket::Option)
+
+ opt.family.should == Socket::AF_INET
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_KEEPALIVE
+ opt.data.should == @bool
+ end
+ end
+
+ describe 'using Symbols' do
+ it 'returns a Socket::Option' do
+ opt = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, @bool)
+
+ opt.should be_an_instance_of(Socket::Option)
+
+ opt.family.should == Socket::AF_INET
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_KEEPALIVE
+ opt.data.should == @bool
+ end
+
+ it 'raises when using an invalid address family' do
+ lambda {
+ Socket::Option.new(:INET2, :SOCKET, :KEEPALIVE, @bool)
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises when using an invalid level' do
+ lambda {
+ Socket::Option.new(:INET, :CATS, :KEEPALIVE, @bool)
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises when using an invalid option name' do
+ lambda {
+ Socket::Option.new(:INET, :SOCKET, :CATS, @bool)
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using Strings' do
+ it 'returns a Socket::Option' do
+ opt = Socket::Option.new('INET', 'SOCKET', 'KEEPALIVE', @bool)
+
+ opt.should be_an_instance_of(Socket::Option)
+
+ opt.family.should == Socket::AF_INET
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_KEEPALIVE
+ opt.data.should == @bool
+ end
+
+ it 'raises when using an invalid address family' do
+ lambda {
+ Socket::Option.new('INET2', 'SOCKET', 'KEEPALIVE', @bool)
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises when using an invalid level' do
+ lambda {
+ Socket::Option.new('INET', 'CATS', 'KEEPALIVE', @bool)
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises when using an invalid option name' do
+ lambda {
+ Socket::Option.new('INET', 'SOCKET', 'CATS', @bool)
+ }.should raise_error(SocketError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/option/inspect_spec.rb b/spec/ruby/library/socket/option/inspect_spec.rb
new file mode 100644
index 0000000000..ebea940d2f
--- /dev/null
+++ b/spec/ruby/library/socket/option/inspect_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+
+describe 'Socket::Option#inspect' do
+ it 'correctly returns SO_LINGER value' do
+ value = Socket::Option.linger(nil, 0).inspect
+ value.should == '#<Socket::Option: UNSPEC SOCKET LINGER off 0sec>'
+
+ value = Socket::Option.linger(false, 30).inspect
+ value.should == '#<Socket::Option: UNSPEC SOCKET LINGER off 30sec>'
+
+ value = Socket::Option.linger(true, 0).inspect
+ value.should == '#<Socket::Option: UNSPEC SOCKET LINGER on 0sec>'
+
+ value = Socket::Option.linger(true, 30).inspect
+ value.should == '#<Socket::Option: UNSPEC SOCKET LINGER on 30sec>'
+ end
+end
diff --git a/spec/ruby/library/socket/option/int_spec.rb b/spec/ruby/library/socket/option/int_spec.rb
new file mode 100644
index 0000000000..5c67ec26d8
--- /dev/null
+++ b/spec/ruby/library/socket/option/int_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::Option.int" do
+ it "creates a new Socket::Option" do
+ so = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 5)
+ so.should be_an_instance_of(Socket::Option)
+ so.family.should == Socket::Constants::AF_INET
+ so.level.should == Socket::Constants::SOL_SOCKET
+ so.optname.should == Socket::Constants::SO_KEEPALIVE
+ so.data.should == [5].pack('i')
+ end
+
+ it 'returns a Socket::Option' do
+ opt = Socket::Option.int(:INET, :IP, :TTL, 4)
+
+ opt.should be_an_instance_of(Socket::Option)
+
+ opt.family.should == Socket::AF_INET
+ opt.level.should == Socket::IPPROTO_IP
+ opt.optname.should == Socket::IP_TTL
+ opt.data.should == [4].pack('i')
+ end
+end
+
+describe "Socket::Option#int" do
+ it "returns int value" do
+ so = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 17)
+ so.int.should == 17
+
+ so = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 32765)
+ so.int.should == 32765
+
+ Socket::Option.int(:INET, :IP, :TTL, 4).int.should == 4
+ end
+
+ platform_is_not :windows do
+ it 'raises TypeError when called on a non integer option' do
+ opt = Socket::Option.linger(1, 4)
+ lambda { opt.int }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/option/linger_spec.rb b/spec/ruby/library/socket/option/linger_spec.rb
new file mode 100644
index 0000000000..94467ebf71
--- /dev/null
+++ b/spec/ruby/library/socket/option/linger_spec.rb
@@ -0,0 +1,76 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+option_pack = 'i*'
+platform_is :windows do
+ option_pack = 's*'
+end
+
+describe "Socket::Option.linger" do
+ it "creates a new Socket::Option for SO_LINGER" do
+ so = Socket::Option.linger(1, 10)
+ so.should be_an_instance_of(Socket::Option)
+
+ so.family.should == Socket::Constants::AF_UNSPEC
+ so.level.should == Socket::Constants::SOL_SOCKET
+ so.optname.should == Socket::Constants::SO_LINGER
+
+ so.data.should == [1, 10].pack(option_pack)
+ end
+
+ it "accepts boolean as onoff argument" do
+ so = Socket::Option.linger(false, 0)
+ so.data.should == [0, 0].pack(option_pack)
+
+ so = Socket::Option.linger(true, 1)
+ so.data.should == [1, 1].pack(option_pack)
+ end
+end
+
+describe "Socket::Option#linger" do
+ it "returns linger option" do
+ so = Socket::Option.linger(0, 5)
+ ary = so.linger
+ ary[0].should be_false
+ ary[1].should == 5
+
+ so = Socket::Option.linger(false, 4)
+ ary = so.linger
+ ary[0].should be_false
+ ary[1].should == 4
+
+ so = Socket::Option.linger(1, 10)
+ ary = so.linger
+ ary[0].should be_true
+ ary[1].should == 10
+
+ so = Socket::Option.linger(true, 9)
+ ary = so.linger
+ ary[0].should be_true
+ ary[1].should == 9
+ end
+
+ it "raises TypeError if not a SO_LINGER" do
+ so = Socket::Option.int(:AF_UNSPEC, :SOL_SOCKET, :KEEPALIVE, 1)
+ lambda { so.linger }.should raise_error(TypeError)
+ end
+
+ it 'raises TypeError when called on a non SOL_SOCKET/SO_LINGER option' do
+ opt = Socket::Option.int(:INET, :IP, :TTL, 4)
+
+ lambda { opt.linger }.should raise_error(TypeError)
+ end
+
+ platform_is_not :windows do
+ it "raises TypeError if option has not good size" do
+ so = Socket::Option.int(:AF_UNSPEC, :SOL_SOCKET, :LINGER, 1)
+ lambda { so.linger }.should raise_error(TypeError)
+ end
+ end
+
+ it 'raises TypeError when called on a non linger option' do
+ opt = Socket::Option.new(:INET, :SOCKET, :LINGER, '')
+
+ lambda { opt.linger }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/socket/option/new_spec.rb b/spec/ruby/library/socket/option/new_spec.rb
new file mode 100644
index 0000000000..f3b7b31c91
--- /dev/null
+++ b/spec/ruby/library/socket/option/new_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::Option.new" do
+ it "should accept integers" do
+ so = Socket::Option.new(Socket::AF_INET, Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, [0].pack('i'))
+ so.family.should == Socket::AF_INET
+ so.level.should == Socket::SOL_SOCKET
+ so.optname.should == Socket::SO_KEEPALIVE
+ end
+
+ it "should accept symbols" do
+ so = Socket::Option.new(:AF_INET, :SOL_SOCKET, :SO_KEEPALIVE, [0].pack('i'))
+ so.family.should == Socket::AF_INET
+ so.level.should == Socket::SOL_SOCKET
+ so.optname.should == Socket::SO_KEEPALIVE
+
+ so = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [0].pack('i'))
+ so.family.should == Socket::AF_INET
+ so.level.should == Socket::SOL_SOCKET
+ so.optname.should == Socket::SO_KEEPALIVE
+ end
+
+ it "should raise error on unknown family" do
+ lambda { Socket::Option.new(:INET4, :SOCKET, :KEEPALIVE, [0].pack('i')) }.should raise_error(SocketError)
+ end
+
+ it "should raise error on unknown level" do
+ lambda { Socket::Option.new(:INET, :ROCKET, :KEEPALIVE, [0].pack('i')) }.should raise_error(SocketError)
+ end
+
+ it "should raise error on unknown option name" do
+ lambda { Socket::Option.new(:INET, :SOCKET, :ALIVE, [0].pack('i')) }.should raise_error(SocketError)
+ end
+end
diff --git a/spec/ruby/library/socket/shared/pack_sockaddr.rb b/spec/ruby/library/socket/shared/pack_sockaddr.rb
new file mode 100644
index 0000000000..40b0d8d59e
--- /dev/null
+++ b/spec/ruby/library/socket/shared/pack_sockaddr.rb
@@ -0,0 +1,99 @@
+# coding: utf-8
+describe :socket_pack_sockaddr_in, shared: true do
+ it "packs and unpacks" do
+ sockaddr_in = Socket.public_send(@method, 0, nil)
+ port, addr = Socket.unpack_sockaddr_in(sockaddr_in)
+ ["127.0.0.1", "::1"].include?(addr).should == true
+ port.should == 0
+
+ sockaddr_in = Socket.public_send(@method, 0, '')
+ Socket.unpack_sockaddr_in(sockaddr_in).should == [0, '0.0.0.0']
+
+ sockaddr_in = Socket.public_send(@method, 80, '127.0.0.1')
+ Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '127.0.0.1']
+
+ sockaddr_in = Socket.public_send(@method, '80', '127.0.0.1')
+ Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '127.0.0.1']
+
+ sockaddr_in = Socket.public_send(@method, nil, '127.0.0.1')
+ Socket.unpack_sockaddr_in(sockaddr_in).should == [0, '127.0.0.1']
+ 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')
+
+ str.should be_an_instance_of(String)
+ str.bytesize.should == 16
+ end
+ end
+
+ platform_is_not :solaris do
+ describe 'using an IPv6 address' do
+ it 'returns a String of 28 bytes' do
+ str = Socket.public_send(@method, 80, '::1')
+
+ str.should be_an_instance_of(String)
+ str.bytesize.should == 28
+ end
+ end
+ end
+
+ platform_is :solaris do
+ describe 'using an IPv6 address' do
+ it 'returns a String of 32 bytes' do
+ str = Socket.public_send(@method, 80, '::1')
+
+ str.should be_an_instance_of(String)
+ str.bytesize.should == 32
+ end
+ end
+ end
+end
+
+describe :socket_pack_sockaddr_un, shared: true do
+ with_feature :unix_socket do
+ it 'should be idempotent' do
+ bytes = Socket.public_send(@method, '/tmp/foo').bytes
+ bytes[2..9].should == [47, 116, 109, 112, 47, 102, 111, 111]
+ bytes[10..-1].all?(&:zero?).should == true
+ end
+
+ it "packs and unpacks" do
+ sockaddr_un = Socket.public_send(@method, '/tmp/s')
+ Socket.unpack_sockaddr_un(sockaddr_un).should == '/tmp/s'
+ end
+
+ it "handles correctly paths with multibyte chars" do
+ sockaddr_un = Socket.public_send(@method, '/home/ваÑÑ/sock')
+ path = Socket.unpack_sockaddr_un(sockaddr_un).encode('UTF-8', 'UTF-8')
+ path.should == '/home/ваÑÑ/sock'
+ end
+ end
+
+ platform_is :linux do
+ it 'returns a String of 110 bytes' do
+ str = Socket.public_send(@method, '/tmp/test.sock')
+
+ str.should be_an_instance_of(String)
+ str.bytesize.should == 110
+ end
+ end
+
+ platform_is :bsd do
+ it 'returns a String of 106 bytes' do
+ str = Socket.public_send(@method, '/tmp/test.sock')
+
+ str.should be_an_instance_of(String)
+ str.bytesize.should == 106
+ end
+ end
+
+ platform_is_not :windows, :aix do
+ it "raises ArgumentError for paths that are too long" do
+ # AIX doesn't raise error
+ long_path = 'a' * 110
+ lambda { Socket.public_send(@method, long_path) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/shared/partially_closable_sockets.rb b/spec/ruby/library/socket/shared/partially_closable_sockets.rb
new file mode 100644
index 0000000000..1bdff08bf6
--- /dev/null
+++ b/spec/ruby/library/socket/shared/partially_closable_sockets.rb
@@ -0,0 +1,13 @@
+describe "partially closable sockets", shared: true do
+ it "if the write end is closed then the other side can read past EOF without blocking" do
+ @s1.write("foo")
+ @s1.close_write
+ @s2.read("foo".size + 1).should == "foo"
+ end
+
+ it "closing the write end ensures that the other side can read until EOF" do
+ @s1.write("hello world")
+ @s1.close_write
+ @s2.read.should == "hello world"
+ end
+end
diff --git a/spec/ruby/library/socket/shared/socketpair.rb b/spec/ruby/library/socket/shared/socketpair.rb
new file mode 100644
index 0000000000..08db2e59b8
--- /dev/null
+++ b/spec/ruby/library/socket/shared/socketpair.rb
@@ -0,0 +1,138 @@
+describe :socket_socketpair, shared: true do
+ platform_is_not :windows do
+ it "ensures the returned sockets are connected" do
+ s1, s2 = Socket.public_send(@method, Socket::AF_UNIX, 1, 0)
+ s1.puts("test")
+ s2.gets.should == "test\n"
+ s1.close
+ s2.close
+ end
+
+ it "responses with array of two sockets" do
+ begin
+ s1, s2 = Socket.public_send(@method, :UNIX, :STREAM)
+
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ ensure
+ s1.close
+ s2.close
+ end
+ end
+
+ describe 'using an Integer as the 1st and 2nd argument' do
+ it 'returns two Socket objects' do
+ s1, s2 = Socket.public_send(@method, Socket::AF_UNIX, Socket::SOCK_STREAM)
+
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ s1.close
+ s2.close
+ end
+ end
+
+ describe 'using a Symbol as the 1st and 2nd argument' do
+ it 'returns two Socket objects' do
+ s1, s2 = Socket.public_send(@method, :UNIX, :STREAM)
+
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ s1.close
+ s2.close
+ end
+
+ it 'raises SocketError for an unknown address family' do
+ lambda { Socket.public_send(@method, :CATS, :STREAM) }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError for an unknown socket type' do
+ lambda { Socket.public_send(@method, :UNIX, :CATS) }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using a String as the 1st and 2nd argument' do
+ it 'returns two Socket objects' do
+ s1, s2 = Socket.public_send(@method, 'UNIX', 'STREAM')
+
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ s1.close
+ s2.close
+ end
+
+ it 'raises SocketError for an unknown address family' do
+ lambda { Socket.public_send(@method, 'CATS', 'STREAM') }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError for an unknown socket type' do
+ lambda { Socket.public_send(@method, 'UNIX', 'CATS') }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using an object that responds to #to_str as the 1st and 2nd argument' do
+ it 'returns two Socket objects' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return('UNIX')
+ type.stub!(:to_str).and_return('STREAM')
+
+ s1, s2 = Socket.public_send(@method, family, type)
+
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ s1.close
+ s2.close
+ end
+
+ it 'raises TypeError when #to_str does not return a String' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return(Socket::AF_UNIX)
+ type.stub!(:to_str).and_return(Socket::SOCK_STREAM)
+
+ lambda { Socket.public_send(@method, family, type) }.should raise_error(TypeError)
+ end
+
+ it 'raises SocketError for an unknown address family' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return('CATS')
+ type.stub!(:to_str).and_return('STREAM')
+
+ lambda { Socket.public_send(@method, family, type) }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError for an unknown socket type' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return('UNIX')
+ type.stub!(:to_str).and_return('CATS')
+
+ lambda { Socket.public_send(@method, family, type) }.should raise_error(SocketError)
+ end
+ end
+
+ it 'accepts a custom protocol as an Integer as the 3rd argument' do
+ s1, s2 = Socket.public_send(@method, :UNIX, :STREAM, Socket::IPPROTO_IP)
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ s1.close
+ s2.close
+ end
+
+ it 'connects the returned Socket objects' do
+ s1, s2 = Socket.public_send(@method, :UNIX, :STREAM)
+ begin
+ s1.write('hello')
+ s2.recv(5).should == 'hello'
+ ensure
+ s1.close
+ s2.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/accept_loop_spec.rb b/spec/ruby/library/socket/socket/accept_loop_spec.rb
new file mode 100644
index 0000000000..ee11569da3
--- /dev/null
+++ b/spec/ruby/library/socket/socket/accept_loop_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../spec_helper'
+
+describe 'Socket.accept_loop' do
+ before do
+ @server = Socket.new(:INET, :STREAM)
+ @client = Socket.new(:INET, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, '127.0.0.1'))
+ @server.listen(1)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'using an Array of Sockets' do
+ describe 'without any available connections' do
+ # FIXME windows randomly hangs here forever
+ # https://ci.appveyor.com/project/ruby/ruby/builds/20817932/job/dor2ipny7ru4erpa
+ platform_is_not :windows do
+ it 'blocks the caller' do
+ lambda { Socket.accept_loop([@server]) }.should block_caller
+ end
+ end
+ end
+
+ describe 'with available connections' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'yields a Socket and an Addrinfo' do
+ conn = nil
+ addr = nil
+
+ Socket.accept_loop([@server]) do |connection, address|
+ conn = connection
+ addr = address
+ break
+ end
+
+ begin
+ conn.should be_an_instance_of(Socket)
+ addr.should be_an_instance_of(Addrinfo)
+ ensure
+ conn.close
+ end
+ end
+ end
+ end
+
+ describe 'using separate Socket arguments' do
+ describe 'without any available connections' do
+ it 'blocks the caller' do
+ lambda { Socket.accept_loop(@server) }.should block_caller
+ end
+ end
+
+ describe 'with available connections' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'yields a Socket and an Addrinfo' do
+ conn = nil
+ addr = nil
+
+ Socket.accept_loop(@server) do |connection, address|
+ conn = connection
+ addr = address
+ break
+ end
+
+ begin
+ conn.should be_an_instance_of(Socket)
+ addr.should be_an_instance_of(Addrinfo)
+ ensure
+ conn.close
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/accept_nonblock_spec.rb b/spec/ruby/library/socket/socket/accept_nonblock_spec.rb
new file mode 100644
index 0000000000..3ef219ed05
--- /dev/null
+++ b/spec/ruby/library/socket/socket/accept_nonblock_spec.rb
@@ -0,0 +1,138 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket#accept_nonblock" do
+ before :each do
+ @hostname = "127.0.0.1"
+ @addr = Socket.sockaddr_in(0, @hostname)
+ @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ @socket.bind(@addr)
+ @socket.listen(1)
+ end
+
+ after :each do
+ @socket.close
+ end
+
+ it "raises IO::WaitReadable if the connection is not accepted yet" do
+ lambda {
+ @socket.accept_nonblock
+ }.should raise_error(IO::WaitReadable) { |e|
+ platform_is_not :windows do
+ e.should be_kind_of(Errno::EAGAIN)
+ end
+ platform_is :windows do
+ e.should be_kind_of(Errno::EWOULDBLOCK)
+ end
+ }
+ end
+
+ it 'returns :wait_readable in exceptionless mode' do
+ @socket.accept_nonblock(exception: false).should == :wait_readable
+ end
+end
+
+describe 'Socket#accept_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM, 0)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+ end
+
+ after do
+ @server.close unless @server.closed?
+ end
+
+ describe 'using an unbound socket' do
+ it 'raises Errno::EINVAL' do
+ lambda { @server.accept_nonblock }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ describe "using a bound socket that's not listening" do
+ before do
+ @server.bind(@sockaddr)
+ end
+
+ it 'raises Errno::EINVAL' do
+ lambda { @server.accept_nonblock }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ describe 'using a closed socket' do
+ it 'raises IOError' do
+ @server.close
+
+ lambda { @server.accept_nonblock }.should raise_error(IOError)
+ end
+ end
+
+ describe "using a bound socket that's listening" do
+ before do
+ @server.bind(@sockaddr)
+ @server.listen(1)
+ end
+
+ describe 'without a connected client' do
+ it 'raises IO::WaitReadable' do
+ lambda { @server.accept_nonblock }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'with a connected client' do
+ before do
+ addr = Socket.sockaddr_in(@server.local_address.ip_port, ip_address)
+ @client = Socket.new(family, :STREAM, 0)
+
+ @client.connect(addr)
+
+ platform_is(:darwin, :freebsd, :solaris) { IO.select([@server]) }
+ end
+
+ after do
+ @socket.close if @socket
+ @client.close
+ end
+
+ it 'returns an Array containing a Socket and an Addrinfo' do
+ @socket, addrinfo = @server.accept_nonblock
+
+ @socket.should be_an_instance_of(Socket)
+ addrinfo.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @socket, @addr = @server.accept_nonblock
+ end
+
+ it 'uses AF_INET as the address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'uses the same IP address as the client Socket' do
+ @addr.ip_address.should == @client.local_address.ip_address
+ end
+
+ it 'uses the same port as the client Socket' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/accept_spec.rb b/spec/ruby/library/socket/socket/accept_spec.rb
new file mode 100644
index 0000000000..a0778caa23
--- /dev/null
+++ b/spec/ruby/library/socket/socket/accept_spec.rb
@@ -0,0 +1,122 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#accept' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM, 0)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+ end
+
+ after do
+ @server.close unless @server.closed?
+ end
+
+ platform_is :linux do # hangs on other platforms
+ describe 'using an unbound socket' do
+ it 'raises Errno::EINVAL' do
+ lambda { @server.accept }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ describe "using a bound socket that's not listening" do
+ before do
+ @server.bind(@sockaddr)
+ end
+
+ it 'raises Errno::EINVAL' do
+ lambda { @server.accept }.should raise_error(Errno::EINVAL)
+ end
+ end
+ end
+
+ describe 'using a closed socket' do
+ it 'raises IOError' do
+ @server.close
+
+ lambda { @server.accept }.should raise_error(IOError)
+ end
+ end
+
+ describe "using a bound socket that's listening" do
+ before do
+ @server.bind(@sockaddr)
+ @server.listen(1)
+
+ server_ip = @server.local_address.ip_port
+ @server_addr = Socket.sockaddr_in(server_ip, ip_address)
+ end
+
+ describe 'without a connected client' do
+ it 'blocks the caller until a connection is available' do
+ client = Socket.new(family, :STREAM, 0)
+ thread = Thread.new do
+ @server.accept
+ end
+
+ client.connect(@server_addr)
+
+ thread.join(5)
+ value = thread.value
+ begin
+ value.should be_an_instance_of(Array)
+ ensure
+ client.close
+ value[0].close
+ end
+ end
+ end
+
+ describe 'with a connected client' do
+ before do
+ addr = Socket.sockaddr_in(@server.local_address.ip_port, ip_address)
+ @client = Socket.new(family, :STREAM, 0)
+
+ @client.connect(addr)
+ end
+
+ after do
+ @socket.close if @socket
+ @client.close
+ end
+
+ it 'returns an Array containing a Socket and an Addrinfo' do
+ @socket, addrinfo = @server.accept
+
+ @socket.should be_an_instance_of(Socket)
+ addrinfo.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @socket, @addr = @server.accept
+ end
+
+ it 'uses AF_INET as the address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'uses the same IP address as the client Socket' do
+ @addr.ip_address.should == @client.local_address.ip_address
+ end
+
+ it 'uses the same port as the client Socket' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/bind_spec.rb b/spec/ruby/library/socket/socket/bind_spec.rb
new file mode 100644
index 0000000000..338b19bbfc
--- /dev/null
+++ b/spec/ruby/library/socket/socket/bind_spec.rb
@@ -0,0 +1,144 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket#bind on SOCK_DGRAM socket" do
+ before :each do
+ @sock = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
+ @sockaddr = Socket.pack_sockaddr_in(0, "127.0.0.1")
+ end
+
+ after :each do
+ @sock.closed?.should be_false
+ @sock.close
+ end
+
+ it "binds to a port" do
+ lambda { @sock.bind(@sockaddr) }.should_not raise_error
+ end
+
+ it "returns 0 if successful" do
+ @sock.bind(@sockaddr).should == 0
+ end
+
+ it "raises Errno::EINVAL when already bound" do
+ @sock.bind(@sockaddr)
+
+ lambda { @sock.bind(@sockaddr) }.should raise_error(Errno::EINVAL)
+ end
+
+ it "raises Errno::EADDRNOTAVAIL when the specified sockaddr is not available from the local machine" do
+ sockaddr1 = Socket.pack_sockaddr_in(0, "4.3.2.1")
+ lambda { @sock.bind(sockaddr1) }.should raise_error(Errno::EADDRNOTAVAIL)
+ end
+
+ platform_is_not :windows, :cygwin do
+ as_user do
+ it "raises Errno::EACCES when the current user does not have permission to bind" do
+ sockaddr1 = Socket.pack_sockaddr_in(1, "127.0.0.1")
+ lambda { @sock.bind(sockaddr1) }.should raise_error(Errno::EACCES)
+ end
+ end
+ end
+end
+
+describe "Socket#bind on SOCK_STREAM socket" do
+ before :each do
+ @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
+ @sockaddr = Socket.pack_sockaddr_in(0, "127.0.0.1")
+ end
+
+ after :each do
+ @sock.closed?.should be_false
+ @sock.close
+ end
+
+ it "binds to a port" do
+ lambda { @sock.bind(@sockaddr) }.should_not raise_error
+ end
+
+ it "returns 0 if successful" do
+ @sock.bind(@sockaddr).should == 0
+ end
+
+ it "raises Errno::EINVAL when already bound" do
+ @sock.bind(@sockaddr)
+
+ lambda { @sock.bind(@sockaddr) }.should raise_error(Errno::EINVAL)
+ end
+
+ it "raises Errno::EADDRNOTAVAIL when the specified sockaddr is not available from the local machine" do
+ sockaddr1 = Socket.pack_sockaddr_in(0, "4.3.2.1")
+ lambda { @sock.bind(sockaddr1) }.should raise_error(Errno::EADDRNOTAVAIL)
+ end
+
+ platform_is_not :windows, :cygwin do
+ as_user do
+ it "raises Errno::EACCES when the current user does not have permission to bind" do
+ sockaddr1 = Socket.pack_sockaddr_in(1, "127.0.0.1")
+ lambda { @sock.bind(sockaddr1) }.should raise_error(Errno::EACCES)
+ end
+ end
+ end
+end
+
+describe 'Socket#bind' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a packed socket address' do
+ before do
+ @socket = Socket.new(family, :DGRAM)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'returns 0 when successfully bound' do
+ @socket.bind(@sockaddr).should == 0
+ end
+
+ it 'raises Errno::EINVAL when binding to an already bound port' do
+ @socket.bind(@sockaddr)
+
+ lambda { @socket.bind(@sockaddr) }.should raise_error(Errno::EINVAL)
+ end
+
+ it 'raises Errno::EADDRNOTAVAIL when the specified sockaddr is not available' do
+ ip = family == Socket::AF_INET ? '4.3.2.1' : '::2'
+ sockaddr1 = Socket.sockaddr_in(0, ip)
+
+ lambda { @socket.bind(sockaddr1) }.should raise_error(Errno::EADDRNOTAVAIL)
+ end
+
+ platform_is_not :windows do
+ it 'raises Errno::EACCES when the user is not allowed to bind to the port' do
+ sockaddr1 = Socket.pack_sockaddr_in(1, ip_address)
+
+ lambda { @socket.bind(sockaddr1); }.should raise_error(Errno::EACCES)
+ end
+ end
+ end
+
+ describe 'using an Addrinfo' do
+ before do
+ @addr = Addrinfo.udp(ip_address, 0)
+ @socket = Socket.new(@addr.afamily, @addr.socktype)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'binds to an Addrinfo' do
+ @socket.bind(@addr).should == 0
+ @socket.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses a new Addrinfo for the local address' do
+ @socket.bind(@addr)
+ @socket.local_address.should_not == @addr
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/connect_nonblock_spec.rb b/spec/ruby/library/socket/socket/connect_nonblock_spec.rb
new file mode 100644
index 0000000000..c56bebee62
--- /dev/null
+++ b/spec/ruby/library/socket/socket/connect_nonblock_spec.rb
@@ -0,0 +1,149 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket#connect_nonblock" do
+ before :each do
+ @hostname = "127.0.0.1"
+ @server = TCPServer.new(@hostname, 0) # started, but no accept
+ @addr = Socket.sockaddr_in(@server.addr[1], @hostname)
+ @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ @thread = nil
+ end
+
+ after :each do
+ @socket.close
+ @server.close
+ @thread.join if @thread
+ end
+
+ platform_is_not :solaris do
+ it "connects the socket to the remote side" do
+ port = nil
+ accept = false
+ @thread = Thread.new do
+ server = TCPServer.new(@hostname, 0)
+ port = server.addr[1]
+ Thread.pass until accept
+ conn = server.accept
+ conn << "hello!"
+ conn.close
+ server.close
+ end
+
+ Thread.pass until port
+
+ addr = Socket.sockaddr_in(port, @hostname)
+ begin
+ @socket.connect_nonblock(addr)
+ rescue Errno::EINPROGRESS
+ end
+
+ accept = true
+ IO.select nil, [@socket]
+
+ begin
+ @socket.connect_nonblock(addr)
+ rescue Errno::EISCONN
+ # Not all OS's use this errno, so we trap and ignore it
+ end
+
+ @socket.read(6).should == "hello!"
+ end
+ end
+
+ platform_is_not :freebsd, :solaris, :aix do
+ it "raises Errno::EINPROGRESS when the connect would block" do
+ lambda do
+ @socket.connect_nonblock(@addr)
+ end.should raise_error(Errno::EINPROGRESS)
+ end
+
+ it "raises Errno::EINPROGRESS with IO::WaitWritable mixed in when the connect would block" do
+ lambda do
+ @socket.connect_nonblock(@addr)
+ end.should raise_error(IO::WaitWritable)
+ end
+
+ it "returns :wait_writable in exceptionless mode when the connect would block" do
+ @socket.connect_nonblock(@addr, exception: false).should == :wait_writable
+ end
+ end
+end
+
+describe 'Socket#connect_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a DGRAM socket' do
+ before do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+
+ @server.bind(@sockaddr)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns 0 when successfully connected using a String' do
+ @client.connect_nonblock(@server.getsockname).should == 0
+ end
+
+ it 'returns 0 when successfully connected using an Addrinfo' do
+ @client.connect_nonblock(@server.connect_address).should == 0
+ end
+
+ it 'raises TypeError when passed an Integer' do
+ lambda { @client.connect_nonblock(666) }.should raise_error(TypeError)
+ end
+ end
+
+ describe 'using a STREAM socket' do
+ before do
+ @server = Socket.new(family, :STREAM)
+ @client = Socket.new(family, :STREAM)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ it 'raises Errno::EISCONN when already connected' do
+ @server.listen(1)
+ @client.connect(@server.getsockname).should == 0
+
+ lambda {
+ @client.connect_nonblock(@server.getsockname)
+
+ # A second call needed if non-blocking sockets become default
+ # XXX honestly I don't expect any real code to care about this spec
+ # as it's too implementation-dependent and checking for connect()
+ # errors is futile anyways because of TOCTOU
+ @client.connect_nonblock(@server.getsockname)
+ }.should raise_error(Errno::EISCONN)
+ end
+
+ it 'returns 0 when already connected in exceptionless mode' do
+ @server.listen(1)
+ @client.connect(@server.getsockname).should == 0
+
+ @client.connect_nonblock(@server.getsockname, exception: false).should == 0
+ end
+ end
+
+ platform_is_not :freebsd, :solaris do
+ it 'raises IO:EINPROGRESSWaitWritable when the connection would block' do
+ @server.bind(@sockaddr)
+
+ lambda {
+ @client.connect_nonblock(@server.getsockname)
+ }.should raise_error(IO::EINPROGRESSWaitWritable)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/connect_spec.rb b/spec/ruby/library/socket/socket/connect_spec.rb
new file mode 100644
index 0000000000..df5cc5bf34
--- /dev/null
+++ b/spec/ruby/library/socket/socket/connect_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#connect' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM)
+ @client = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns 0 when connected successfully using a String' do
+ @server.listen(1)
+
+ @client.connect(@server.getsockname).should == 0
+ end
+
+ it 'returns 0 when connected successfully using an Addrinfo' do
+ @server.listen(1)
+
+ @client.connect(@server.connect_address).should == 0
+ end
+
+ it 'raises Errno::EISCONN when already connected' do
+ @server.listen(1)
+
+ @client.connect(@server.getsockname).should == 0
+
+ lambda {
+ @client.connect(@server.getsockname)
+
+ # A second call needed if non-blocking sockets become default
+ # XXX honestly I don't expect any real code to care about this spec
+ # as it's too implementation-dependent and checking for connect()
+ # errors is futile anyways because of TOCTOU
+ @client.connect(@server.getsockname)
+ }.should raise_error(Errno::EISCONN)
+ end
+
+ platform_is_not :darwin do
+ it 'raises Errno::ECONNREFUSED or Errno::ETIMEDOUT when the connection failed' do
+ begin
+ @client.connect(@server.getsockname)
+ rescue => e
+ [Errno::ECONNREFUSED, Errno::ETIMEDOUT].include?(e.class).should == true
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/for_fd_spec.rb b/spec/ruby/library/socket/socket/for_fd_spec.rb
new file mode 100644
index 0000000000..e89228d436
--- /dev/null
+++ b/spec/ruby/library/socket/socket/for_fd_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket.for_fd" do
+ before :each do
+ @server = TCPServer.new("127.0.0.1", 0)
+ @port = @server.addr[1]
+ @client = TCPSocket.open("127.0.0.1", @port)
+ end
+
+ after :each do
+ @socket.close
+ @client.close
+ @host.close
+ @server.close
+ end
+
+ it "creates a new Socket that aliases the existing Socket's file descriptor" do
+ @socket = Socket.for_fd(@client.fileno)
+ @socket.autoclose = false
+ @socket.fileno.should == @client.fileno
+
+ @socket.send("foo", 0)
+ @client.send("bar", 0)
+
+ @host = @server.accept
+ @host.read(3).should == "foo"
+ @host.read(3).should == "bar"
+ end
+end
diff --git a/spec/ruby/library/socket/socket/getaddrinfo_spec.rb b/spec/ruby/library/socket/socket/getaddrinfo_spec.rb
new file mode 100644
index 0000000000..e0eff3cef4
--- /dev/null
+++ b/spec/ruby/library/socket/socket/getaddrinfo_spec.rb
@@ -0,0 +1,373 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket.getaddrinfo" do
+ before :each do
+ @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
+ BasicSocket.do_not_reverse_lookup = true
+ end
+
+ after :each do
+ BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup
+ end
+
+ platform_is_not :solaris, :windows do
+ it "gets the address information" do
+ expected = []
+ # The check for AP_INET6's class is needed because ipaddr.rb adds
+ # fake AP_INET6 even in case when IPv6 is not really supported.
+ # Without such check, this test might fail when ipaddr was required
+ # by some other specs.
+ if (Socket.constants.include? 'AF_INET6') &&
+ (Socket::AF_INET6.class != Object) then
+ expected.concat [
+ ['AF_INET6', 9, SocketSpecs.hostname, '::1', Socket::AF_INET6,
+ Socket::SOCK_DGRAM, Socket::IPPROTO_UDP],
+ ['AF_INET6', 9, SocketSpecs.hostname, '::1', Socket::AF_INET6,
+ Socket::SOCK_STREAM, Socket::IPPROTO_TCP],
+ ['AF_INET6', 9, SocketSpecs.hostname, 'fe80::1%lo0', Socket::AF_INET6,
+ Socket::SOCK_DGRAM, Socket::IPPROTO_UDP],
+ ['AF_INET6', 9, SocketSpecs.hostname, 'fe80::1%lo0', Socket::AF_INET6,
+ Socket::SOCK_STREAM, Socket::IPPROTO_TCP],
+ ]
+ end
+
+ expected.concat [
+ ['AF_INET', 9, SocketSpecs.hostname, '127.0.0.1', Socket::AF_INET,
+ Socket::SOCK_DGRAM, Socket::IPPROTO_UDP],
+ ['AF_INET', 9, SocketSpecs.hostname, '127.0.0.1', Socket::AF_INET,
+ Socket::SOCK_STREAM, Socket::IPPROTO_TCP],
+ ]
+
+ addrinfo = Socket.getaddrinfo SocketSpecs.hostname, 'discard'
+ addrinfo.each do |a|
+ case a.last
+ when Socket::IPPROTO_UDP, Socket::IPPROTO_TCP
+ expected.should include(a)
+ else
+ # don't check this. It's some weird protocol we don't know about
+ # so we can't spec it.
+ end
+ end
+ end
+
+ # #getaddrinfo will return a INADDR_ANY address (0.0.0.0 or "::")
+ # if it's a passive socket. In the case of non-passive
+ # sockets (AI_PASSIVE not set) it should return the loopback
+ # address (127.0.0.1 or "::1").
+
+ it "accepts empty addresses for IPv4 passive sockets" do
+ res = Socket.getaddrinfo(nil, "discard",
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ Socket::IPPROTO_TCP,
+ Socket::AI_PASSIVE)
+
+ expected = [["AF_INET", 9, "0.0.0.0", "0.0.0.0", Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP]]
+ res.should == expected
+ end
+
+ it "accepts empty addresses for IPv4 non-passive sockets" do
+ res = Socket.getaddrinfo(nil, "discard",
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ Socket::IPPROTO_TCP,
+ 0)
+
+ expected = [["AF_INET", 9, "127.0.0.1", "127.0.0.1", Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP]]
+ res.should == expected
+ end
+
+
+ it "accepts empty addresses for IPv6 passive sockets" do
+ res = Socket.getaddrinfo(nil, "discard",
+ Socket::AF_INET6,
+ Socket::SOCK_STREAM,
+ Socket::IPPROTO_TCP,
+ Socket::AI_PASSIVE)
+
+ expected = [
+ ["AF_INET6", 9, "::", "::", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP],
+ ["AF_INET6", 9, "0:0:0:0:0:0:0:0", "0:0:0:0:0:0:0:0", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP]
+ ]
+ res.each { |a| expected.should include(a) }
+ end
+
+ it "accepts empty addresses for IPv6 non-passive sockets" do
+ res = Socket.getaddrinfo(nil, "discard",
+ Socket::AF_INET6,
+ Socket::SOCK_STREAM,
+ Socket::IPPROTO_TCP,
+ 0)
+
+ expected = [
+ ["AF_INET6", 9, "::1", "::1", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP],
+ ["AF_INET6", 9, "0:0:0:0:0:0:0:1", "0:0:0:0:0:0:0:1", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP]
+ ]
+ res.each { |a| expected.should include(a) }
+ end
+ end
+end
+
+describe 'Socket.getaddrinfo' do
+ describe 'without global reverse lookups' do
+ it 'returns an Array' do
+ Socket.getaddrinfo(nil, 'ftp').should be_an_instance_of(Array)
+ end
+
+ it 'accepts an Integer as the address family' do
+ array = Socket.getaddrinfo(nil, 'ftp', Socket::AF_INET)[0]
+
+ array[0].should == 'AF_INET'
+ array[1].should == 21
+ array[2].should == '127.0.0.1'
+ array[3].should == '127.0.0.1'
+ array[4].should == Socket::AF_INET
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts an Integer as the address family using IPv6' do
+ array = Socket.getaddrinfo(nil, 'ftp', Socket::AF_INET6)[0]
+
+ array[0].should == 'AF_INET6'
+ array[1].should == 21
+ array[2].should == '::1'
+ array[3].should == '::1'
+ array[4].should == Socket::AF_INET6
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts a Symbol as the address family' do
+ array = Socket.getaddrinfo(nil, 'ftp', :INET)[0]
+
+ array[0].should == 'AF_INET'
+ array[1].should == 21
+ array[2].should == '127.0.0.1'
+ array[3].should == '127.0.0.1'
+ array[4].should == Socket::AF_INET
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts a Symbol as the address family using IPv6' do
+ array = Socket.getaddrinfo(nil, 'ftp', :INET6)[0]
+
+ array[0].should == 'AF_INET6'
+ array[1].should == 21
+ array[2].should == '::1'
+ array[3].should == '::1'
+ array[4].should == Socket::AF_INET6
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts a String as the address family' do
+ array = Socket.getaddrinfo(nil, 'ftp', 'INET')[0]
+
+ array[0].should == 'AF_INET'
+ array[1].should == 21
+ array[2].should == '127.0.0.1'
+ array[3].should == '127.0.0.1'
+ array[4].should == Socket::AF_INET
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts a String as the address family using IPv6' do
+ array = Socket.getaddrinfo(nil, 'ftp', 'INET6')[0]
+
+ array[0].should == 'AF_INET6'
+ array[1].should == 21
+ array[2].should == '::1'
+ array[3].should == '::1'
+ array[4].should == Socket::AF_INET6
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts an object responding to #to_str as the host' do
+ dummy = mock(:dummy)
+
+ dummy.stub!(:to_str).and_return('127.0.0.1')
+
+ array = Socket.getaddrinfo(dummy, 'ftp')[0]
+
+ array[0].should == 'AF_INET'
+ array[1].should == 21
+ array[2].should == '127.0.0.1'
+ array[3].should == '127.0.0.1'
+ array[4].should == Socket::AF_INET
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts an object responding to #to_str as the address family' do
+ dummy = mock(:dummy)
+
+ dummy.stub!(:to_str).and_return('INET')
+
+ array = Socket.getaddrinfo(nil, 'ftp', dummy)[0]
+
+ array[0].should == 'AF_INET'
+ array[1].should == 21
+ array[2].should == '127.0.0.1'
+ array[3].should == '127.0.0.1'
+ array[4].should == Socket::AF_INET
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts an Integer as the socket type' do
+ *array, proto = Socket.getaddrinfo(nil, 'ftp', :INET, Socket::SOCK_STREAM)[0]
+ array.should == [
+ 'AF_INET',
+ 21,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ]
+ [0, Socket::IPPROTO_TCP].should include(proto)
+ end
+
+ it 'accepts a Symbol as the socket type' do
+ *array, proto = Socket.getaddrinfo(nil, 'ftp', :INET, :STREAM)[0]
+ array.should == [
+ 'AF_INET',
+ 21,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ]
+ [0, Socket::IPPROTO_TCP].should include(proto)
+ end
+
+ it 'accepts a String as the socket type' do
+ *array, proto = Socket.getaddrinfo(nil, 'ftp', :INET, 'STREAM')[0]
+ array.should == [
+ 'AF_INET',
+ 21,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ]
+ [0, Socket::IPPROTO_TCP].should include(proto)
+ end
+
+ it 'accepts an object responding to #to_str as the socket type' do
+ dummy = mock(:dummy)
+
+ dummy.stub!(:to_str).and_return('STREAM')
+
+ *array, proto = Socket.getaddrinfo(nil, 'ftp', :INET, dummy)[0]
+ array.should == [
+ 'AF_INET',
+ 21,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ]
+ [0, Socket::IPPROTO_TCP].should include(proto)
+ end
+
+ platform_is_not :windows do
+ it 'accepts an Integer as the protocol family' do
+ *array, proto = Socket.getaddrinfo(nil, 'discard', :INET, :DGRAM, Socket::IPPROTO_UDP)[0]
+ array.should == [
+ 'AF_INET',
+ 9,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_DGRAM,
+ ]
+ [0, Socket::IPPROTO_UDP].should include(proto)
+ end
+ end
+
+ it 'accepts an Integer as the flags' do
+ *array, proto = Socket.getaddrinfo(nil, 'ftp', :INET, :STREAM,
+ Socket::IPPROTO_TCP, Socket::AI_PASSIVE)[0]
+ array.should == [
+ 'AF_INET',
+ 21,
+ '0.0.0.0',
+ '0.0.0.0',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ]
+ [0, Socket::IPPROTO_TCP].should include(proto)
+ end
+
+ it 'performs a reverse lookup when the reverse_lookup argument is true' do
+ addr = Socket.getaddrinfo(nil, 'ftp', :INET, :STREAM,
+ Socket::IPPROTO_TCP, 0, true)[0]
+
+ addr[0].should == 'AF_INET'
+ addr[1].should == 21
+
+ addr[2].should be_an_instance_of(String)
+ addr[2].should_not == addr[3]
+
+ addr[3].should == '127.0.0.1'
+ end
+
+ it 'performs a reverse lookup when the reverse_lookup argument is :hostname' do
+ addr = Socket.getaddrinfo(nil, 'ftp', :INET, :STREAM,
+ Socket::IPPROTO_TCP, 0, :hostname)[0]
+
+ addr[0].should == 'AF_INET'
+ addr[1].should == 21
+
+ addr[2].should be_an_instance_of(String)
+ addr[2].should_not == addr[3]
+
+ addr[3].should == '127.0.0.1'
+ end
+
+ it 'performs a reverse lookup when the reverse_lookup argument is :numeric' do
+ *array, proto = Socket.getaddrinfo(nil, 'ftp', :INET, :STREAM,
+ Socket::IPPROTO_TCP, 0, :numeric)[0]
+ array.should == [
+ 'AF_INET',
+ 21,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ]
+ [0, Socket::IPPROTO_TCP].should include(proto)
+ end
+ end
+
+ describe 'with global reverse lookups' do
+ before do
+ @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
+ BasicSocket.do_not_reverse_lookup = false
+ end
+
+ after do
+ BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup
+ end
+
+ it 'returns an address honoring the global lookup option' do
+ addr = Socket.getaddrinfo(nil, 'ftp', :INET)[0]
+
+ addr[0].should == 'AF_INET'
+ addr[1].should == 21
+
+ # We don't have control over this value and there's no way to test this
+ # without relying on Socket.getaddrinfo()'s own behaviour (meaning this
+ # test would faily any way of the method was not implemented correctly).
+ addr[2].should be_an_instance_of(String)
+ addr[2].should_not == addr[3]
+
+ addr[3].should == '127.0.0.1'
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/gethostbyaddr_spec.rb b/spec/ruby/library/socket/socket/gethostbyaddr_spec.rb
new file mode 100644
index 0000000000..639a318132
--- /dev/null
+++ b/spec/ruby/library/socket/socket/gethostbyaddr_spec.rb
@@ -0,0 +1,124 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require 'ipaddr'
+
+describe 'Socket.gethostbyaddr' do
+ describe 'using an IPv4 address' do
+ before do
+ @addr = IPAddr.new('127.0.0.1').hton
+ end
+
+ describe 'without an explicit address family' do
+ it 'returns an Array' do
+ Socket.gethostbyaddr(@addr).should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = Socket.gethostbyaddr(@addr)
+ end
+
+ # RubyCI Solaris 11x defines 127.0.0.1 as unstable11x
+ platform_is_not :"solaris2.11" do
+ it 'includes the hostname as the first value' do
+ @array[0].should == SocketSpecs.hostname_reverse_lookup
+ end
+ end
+
+ it 'includes the aliases as the 2nd value' do
+ @array[1].should be_an_instance_of(Array)
+
+ @array[1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @array[2].should == Socket::AF_INET
+ end
+
+ it 'includes all address strings as the remaining values' do
+ @array[3].should == @addr
+
+ @array[4..-1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+ end
+ end
+
+ describe 'with an explicit address family' do
+ it 'returns an Array when using an Integer as the address family' do
+ Socket.gethostbyaddr(@addr, Socket::AF_INET).should be_an_instance_of(Array)
+ end
+
+ it 'returns an Array when using a Symbol as the address family' do
+ Socket.gethostbyaddr(@addr, :INET).should be_an_instance_of(Array)
+ end
+
+ it 'raises SocketError when the address is not supported by the family' do
+ lambda { Socket.gethostbyaddr(@addr, :INET6) }.should raise_error(SocketError)
+ end
+ end
+ end
+
+ guard -> { SocketSpecs.ipv6_available? && platform_is_not(:aix) } do
+ describe 'using an IPv6 address' do
+ before do
+ @addr = IPAddr.new('::1').hton
+ end
+
+ describe 'without an explicit address family' do
+ it 'returns an Array' do
+ Socket.gethostbyaddr(@addr).should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = Socket.gethostbyaddr(@addr)
+ end
+
+ it 'includes the hostname as the first value' do
+ @array[0].should == SocketSpecs.hostname_reverse_lookup("::1")
+ end
+
+ it 'includes the aliases as the 2nd value' do
+ @array[1].should be_an_instance_of(Array)
+
+ @array[1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @array[2].should == Socket::AF_INET6
+ end
+
+ it 'includes all address strings as the remaining values' do
+ @array[3].should be_an_instance_of(String)
+
+ @array[4..-1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+ end
+ end
+
+ describe 'with an explicit address family' do
+ it 'returns an Array when using an Integer as the address family' do
+ Socket.gethostbyaddr(@addr, Socket::AF_INET6).should be_an_instance_of(Array)
+ end
+
+ it 'returns an Array when using a Symbol as the address family' do
+ Socket.gethostbyaddr(@addr, :INET6).should be_an_instance_of(Array)
+ end
+
+ platform_is_not :windows do
+ it 'raises SocketError when the address is not supported by the family' do
+ lambda { Socket.gethostbyaddr(@addr, :INET) }.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/gethostbyname_spec.rb b/spec/ruby/library/socket/socket/gethostbyname_spec.rb
new file mode 100644
index 0000000000..9367030e25
--- /dev/null
+++ b/spec/ruby/library/socket/socket/gethostbyname_spec.rb
@@ -0,0 +1,135 @@
+# -*- encoding: binary -*-
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket#gethostbyname" do
+ it "returns broadcast address info for '<broadcast>'" do
+ addr = Socket.gethostbyname('<broadcast>');
+ addr.should == ["255.255.255.255", [], 2, "\377\377\377\377"]
+ end
+
+ it "returns broadcast address info for '<any>'" do
+ addr = Socket.gethostbyname('<any>');
+ addr.should == ["0.0.0.0", [], 2, "\000\000\000\000"]
+ end
+end
+
+describe 'Socket.gethostbyname' do
+ it 'returns an Array' do
+ Socket.gethostbyname('127.0.0.1').should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = Socket.gethostbyname('127.0.0.1')
+ end
+
+ it 'includes the hostname as the first value' do
+ @array[0].should == '127.0.0.1'
+ end
+
+ it 'includes the aliases as the 2nd value' do
+ @array[1].should be_an_instance_of(Array)
+
+ @array[1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+
+ it 'includes the address type as the 3rd value' do
+ possible = [Socket::AF_INET, Socket::AF_INET6]
+
+ possible.include?(@array[2]).should == true
+ end
+
+ it 'includes the address strings as the remaining values' do
+ @array[3].should be_an_instance_of(String)
+
+ @array[4..-1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+ end
+
+ describe 'using <broadcast> as the input address' do
+ describe 'the returned Array' do
+ before do
+ @addr = Socket.gethostbyname('<broadcast>')
+ end
+
+ it 'includes the broadcast address as the first value' do
+ @addr[0].should == '255.255.255.255'
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @addr[2].should == Socket::AF_INET
+ end
+
+ it 'includes the address string as the 4th value' do
+ @addr[3].should == [255, 255, 255, 255].pack('C4')
+ end
+ end
+ end
+
+ describe 'using <any> as the input address' do
+ describe 'the returned Array' do
+ before do
+ @addr = Socket.gethostbyname('<any>')
+ end
+
+ it 'includes the wildcard address as the first value' do
+ @addr[0].should == '0.0.0.0'
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @addr[2].should == Socket::AF_INET
+ end
+
+ it 'includes the address string as the 4th value' do
+ @addr[3].should == [0, 0, 0, 0].pack('C4')
+ end
+ end
+ end
+
+ describe 'using an IPv4 address' do
+ describe 'the returned Array' do
+ before do
+ @addr = Socket.gethostbyname('127.0.0.1')
+ end
+
+ it 'includes the IP address as the first value' do
+ @addr[0].should == '127.0.0.1'
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @addr[2].should == Socket::AF_INET
+ end
+
+ it 'includes the address string as the 4th value' do
+ @addr[3].should == [127, 0, 0, 1].pack('C4')
+ end
+ end
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ describe 'using an IPv6 address' do
+ describe 'the returned Array' do
+ before do
+ @addr = Socket.gethostbyname('::1')
+ end
+
+ it 'includes the IP address as the first value' do
+ @addr[0].should == '::1'
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @addr[2].should == Socket::AF_INET6
+ end
+
+ it 'includes the address string as the 4th value' do
+ @addr[3].should == [0, 0, 0, 0, 0, 0, 0, 1].pack('n8')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/gethostname_spec.rb b/spec/ruby/library/socket/socket/gethostname_spec.rb
new file mode 100644
index 0000000000..4b79747b27
--- /dev/null
+++ b/spec/ruby/library/socket/socket/gethostname_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket.gethostname" do
+ it "returns the host name" do
+ Socket.gethostname.should == `hostname`.strip
+ end
+end
diff --git a/spec/ruby/library/socket/socket/getifaddrs_spec.rb b/spec/ruby/library/socket/socket/getifaddrs_spec.rb
new file mode 100644
index 0000000000..7df542abe6
--- /dev/null
+++ b/spec/ruby/library/socket/socket/getifaddrs_spec.rb
@@ -0,0 +1,117 @@
+require_relative '../spec_helper'
+
+platform_is_not :aix, :"solaris2.10" do
+describe 'Socket.getifaddrs' do
+ before do
+ @ifaddrs = Socket.getifaddrs
+ end
+
+ it 'returns an Array' do
+ @ifaddrs.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ it 'should not be empty' do
+ @ifaddrs.should_not be_empty
+ end
+
+ it 'contains instances of Socket::Ifaddr' do
+ @ifaddrs.each do |ifaddr|
+ ifaddr.should be_an_instance_of(Socket::Ifaddr)
+ end
+ end
+ end
+
+ describe 'each returned Socket::Ifaddr' do
+ it 'has an interface index' do
+ @ifaddrs.each do |ifaddr|
+ ifaddr.ifindex.should be_kind_of(Integer)
+ end
+ end
+
+ it 'has an interface name' do
+ @ifaddrs.each do |ifaddr|
+ ifaddr.name.should be_an_instance_of(String)
+ end
+ end
+
+ it 'has a set of flags' do
+ @ifaddrs.each do |ifaddr|
+ ifaddr.flags.should be_kind_of(Integer)
+ end
+ end
+ end
+
+ describe 'the Socket::Ifaddr address' do
+ before do
+ @addrs = @ifaddrs.map(&:addr).compact
+ end
+
+ it 'is an Addrinfo' do
+ @addrs.all? do |addr|
+ addr.should be_an_instance_of(Addrinfo)
+ true
+ end.should be_true
+ end
+
+ it 'has an address family' do
+ @addrs.all? do |addr|
+ addr.afamily.should be_kind_of(Integer)
+ addr.afamily.should_not == Socket::AF_UNSPEC
+ true
+ end.should be_true
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'the Socket::Ifaddr broadcast address' do
+ before do
+ @addrs = @ifaddrs.map(&:broadaddr).compact
+ end
+
+ it 'is an Addrinfo' do
+ @addrs.all? do |addr|
+ addr.should be_an_instance_of(Addrinfo)
+ true
+ end.should be_true
+ end
+
+ it 'has an address family' do
+ @addrs.all? do |addr|
+ addr.afamily.should be_kind_of(Integer)
+ addr.afamily.should_not == Socket::AF_UNSPEC
+ true
+ end.should be_true
+ end
+ end
+
+ describe 'the Socket::Ifaddr netmask address' do
+ before do
+ @addrs = @ifaddrs.map(&:netmask).compact.select(&:ip?)
+ end
+
+ it 'is an Addrinfo' do
+ @addrs.all? do |addr|
+ addr.should be_an_instance_of(Addrinfo)
+ true
+ end.should be_true
+ end
+
+ it 'has an address family' do
+ @addrs.all? do |addr|
+ addr.afamily.should be_kind_of(Integer)
+ addr.afamily.should_not == Socket::AF_UNSPEC
+ true
+ end.should be_true
+ end
+
+ it 'has an IP address' do
+ @addrs.all? do |addr|
+ addr.ip_address.should be_an_instance_of(String)
+ true
+ end.should be_true
+ end
+ end
+ end
+end
+end
diff --git a/spec/ruby/library/socket/socket/getnameinfo_spec.rb b/spec/ruby/library/socket/socket/getnameinfo_spec.rb
new file mode 100644
index 0000000000..fbbbcb53c5
--- /dev/null
+++ b/spec/ruby/library/socket/socket/getnameinfo_spec.rb
@@ -0,0 +1,147 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket.getnameinfo" do
+ before :each do
+ @reverse_lookup = BasicSocket.do_not_reverse_lookup
+ BasicSocket.do_not_reverse_lookup = true
+ end
+
+ after :each do
+ BasicSocket.do_not_reverse_lookup = @reverse_lookup
+ end
+
+ it "gets the name information and don't resolve it" do
+ sockaddr = Socket.sockaddr_in 3333, '127.0.0.1'
+ name_info = Socket.getnameinfo(sockaddr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV)
+ name_info.should == ['127.0.0.1', "3333"]
+ end
+
+ def should_be_valid_dns_name(name)
+ # http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address
+ # ftp://ftp.rfc-editor.org/in-notes/rfc3696.txt
+ # http://domainkeys.sourceforge.net/underscore.html
+ valid_dns = /^(([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9\-_]*[a-zA-Z0-9_])\.)*([A-Za-z_]|[A-Za-z_][A-Za-z0-9\-_]*[A-Za-z0-9_])\.?$/
+ name.should =~ valid_dns
+ end
+
+ it "gets the name information and resolve the host" do
+ sockaddr = Socket.sockaddr_in 3333, '127.0.0.1'
+ name_info = Socket.getnameinfo(sockaddr, Socket::NI_NUMERICSERV)
+ should_be_valid_dns_name(name_info[0])
+ name_info[1].should == 3333.to_s
+ end
+
+ it "gets the name information and resolves the service" do
+ sockaddr = Socket.sockaddr_in 9, '127.0.0.1'
+ name_info = Socket.getnameinfo(sockaddr)
+ name_info.size.should == 2
+ should_be_valid_dns_name(name_info[0])
+ # see http://www.iana.org/assignments/port-numbers
+ name_info[1].should == 'discard'
+ end
+
+ it "gets a 3-element array and doesn't resolve hostname" do
+ name_info = Socket.getnameinfo(["AF_INET", 3333, '127.0.0.1'], Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV)
+ name_info.should == ['127.0.0.1', "3333"]
+ end
+
+ it "gets a 3-element array and resolves the service" do
+ name_info = Socket.getnameinfo ["AF_INET", 9, '127.0.0.1']
+ name_info[1].should == 'discard'
+ end
+
+ it "gets a 4-element array and doesn't resolve hostname" do
+ name_info = Socket.getnameinfo(["AF_INET", 3333, 'foo', '127.0.0.1'], Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV)
+ name_info.should == ['127.0.0.1', "3333"]
+ end
+
+ it "gets a 4-element array and resolves the service" do
+ name_info = Socket.getnameinfo ["AF_INET", 9, 'foo', '127.0.0.1']
+ name_info[1].should == 'discard'
+ end
+end
+
+describe 'Socket.getnameinfo' do
+ describe 'using a String as the first argument' do
+ before do
+ @addr = Socket.sockaddr_in(21, '127.0.0.1')
+ end
+
+ it 'raises SocketError or TypeError when using an invalid String' do
+ lambda { Socket.getnameinfo('cats') }.should raise_error(Exception) { |e|
+ [SocketError, TypeError].should include(e.class)
+ }
+ end
+
+ describe 'without custom flags' do
+ it 'returns an Array containing the hostname and service name' do
+ Socket.getnameinfo(@addr).should == [SocketSpecs.hostname_reverse_lookup, 'ftp']
+ end
+ end
+
+ describe 'using NI_NUMERICHOST as the flag' do
+ it 'returns an Array containing the numeric hostname and service name' do
+ array = Socket.getnameinfo(@addr, Socket::NI_NUMERICHOST)
+
+ %w{127.0.0.1 ::1}.include?(array[0]).should == true
+
+ array[1].should == 'ftp'
+ end
+ end
+ end
+
+ SocketSpecs.each_ip_protocol do |family, ip_address, family_name|
+ before do
+ @hostname = SocketSpecs.hostname_reverse_lookup(ip_address)
+ end
+
+ describe 'using a 3 element Array as the first argument' do
+ before do
+ @addr = [family_name, 21, @hostname]
+ end
+
+ it 'raises ArgumentError when using an invalid Array' do
+ lambda { Socket.getnameinfo([family_name]) }.should raise_error(ArgumentError)
+ end
+
+ platform_is_not :windows do
+ describe 'using NI_NUMERICHOST as the flag' do
+ it 'returns an Array containing the numeric hostname and service name' do
+ Socket.getnameinfo(@addr, Socket::NI_NUMERICHOST).should == [ip_address, 'ftp']
+ end
+ end
+ end
+ end
+
+ describe 'using a 4 element Array as the first argument' do
+ before do
+ @addr = [family_name, 21, ip_address, ip_address]
+ end
+
+ describe 'without custom flags' do
+ it 'returns an Array containing the hostname and service name' do
+ array = Socket.getnameinfo(@addr)
+ array.should be_an_instance_of(Array)
+ array[0].should == @hostname
+ array[1].should == 'ftp'
+ end
+
+ it 'uses the 3rd value as the hostname if the 4th is not present' do
+ addr = [family_name, 21, ip_address, nil]
+
+ array = Socket.getnameinfo(addr)
+ array.should be_an_instance_of(Array)
+ array[0].should == @hostname
+ array[1].should == 'ftp'
+ end
+ end
+
+ describe 'using NI_NUMERICHOST as the flag' do
+ it 'returns an Array containing the numeric hostname and service name' do
+ Socket.getnameinfo(@addr, Socket::NI_NUMERICHOST).should == [ip_address, 'ftp']
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/getservbyname_spec.rb b/spec/ruby/library/socket/socket/getservbyname_spec.rb
new file mode 100644
index 0000000000..9479b26228
--- /dev/null
+++ b/spec/ruby/library/socket/socket/getservbyname_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket#getservbyname" do
+ it "returns the port for service 'discard'" do
+ Socket.getservbyname('discard').should == 9
+ end
+
+ it "returns the port for service 'discard' with protocol 'tcp'" do
+ Socket.getservbyname('discard', 'tcp').should == 9
+ end
+
+ it 'returns the port for service "ftp"' do
+ Socket.getservbyname('ftp').should == 21
+ end
+
+ it 'returns the port for service "ftp" with protocol "tcp"' do
+ Socket.getservbyname('ftp', 'tcp').should == 21
+ end
+
+ it "returns the port for service 'domain' with protocol 'udp'" do
+ Socket.getservbyname('domain', 'udp').should == 53
+ end
+
+ it "returns the port for service 'daytime'" do
+ Socket.getservbyname('daytime').should == 13
+ end
+
+ it "raises a SocketError when the service or port is invalid" do
+ lambda { Socket.getservbyname('invalid') }.should raise_error(SocketError)
+ end
+end
diff --git a/spec/ruby/library/socket/socket/getservbyport_spec.rb b/spec/ruby/library/socket/socket/getservbyport_spec.rb
new file mode 100644
index 0000000000..9be2ac527e
--- /dev/null
+++ b/spec/ruby/library/socket/socket/getservbyport_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../spec_helper'
+
+describe 'Socket.getservbyport' do
+ platform_is_not :windows do
+ it 'returns the service name as a String' do
+ Socket.getservbyport(514).should == 'shell'
+ end
+ end
+
+ platform_is :windows do
+ it 'returns the service name as a String' do
+ Socket.getservbyport(514).should == 'cmd'
+ end
+ end
+
+ it 'returns the service name when using a custom protocol name' do
+ Socket.getservbyport(514, 'udp').should == 'syslog'
+ end
+
+ it 'raises SocketError for an unknown port number' do
+ lambda { Socket.getservbyport(0) }.should raise_error(SocketError)
+ end
+end
diff --git a/spec/ruby/library/socket/socket/initialize_spec.rb b/spec/ruby/library/socket/socket/initialize_spec.rb
new file mode 100644
index 0000000000..2343c6e289
--- /dev/null
+++ b/spec/ruby/library/socket/socket/initialize_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../spec_helper'
+
+describe 'Socket#initialize' do
+ before do
+ @socket = nil
+ end
+
+ after do
+ @socket.close if @socket
+ end
+
+ describe 'using an Integer as the 1st and 2nd arguments' do
+ it 'returns a Socket' do
+ @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'using Symbols as the 1st and 2nd arguments' do
+ it 'returns a Socket' do
+ @socket = Socket.new(:INET, :STREAM)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'using Strings as the 1st and 2nd arguments' do
+ it 'returns a Socket' do
+ @socket = Socket.new('INET', 'STREAM')
+
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'using objects that respond to #to_str' do
+ it 'returns a Socket' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return('AF_INET')
+ type.stub!(:to_str).and_return('STREAM')
+
+ @socket = Socket.new(family, type)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'raises TypeError when the #to_str method does not return a String' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return(Socket::AF_INET)
+ type.stub!(:to_str).and_return(Socket::SOCK_STREAM)
+
+ lambda { Socket.new(family, type) }.should raise_error(TypeError)
+ end
+ end
+
+ describe 'using a custom protocol' do
+ it 'returns a Socket when using an Integer' do
+ @socket = Socket.new(:INET, :STREAM, Socket::IPPROTO_TCP)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'raises TypeError when using a Symbol' do
+ lambda { Socket.new(:INET, :STREAM, :TCP) }.should raise_error(TypeError)
+ end
+ end
+
+ it 'sets the do_not_reverse_lookup option' do
+ @socket = Socket.new(:INET, :STREAM)
+
+ @socket.do_not_reverse_lookup.should == Socket.do_not_reverse_lookup
+ end
+
+ it "sets basic IO accessors" do
+ @socket = Socket.new(:INET, :STREAM)
+ @socket.lineno.should == 0
+ end
+
+ it "sets the socket to binary mode" do
+ @socket = Socket.new(:INET, :STREAM)
+ @socket.binmode?.should be_true
+ end
+end
diff --git a/spec/ruby/library/socket/socket/ip_address_list_spec.rb b/spec/ruby/library/socket/socket/ip_address_list_spec.rb
new file mode 100644
index 0000000000..f97c2d7f85
--- /dev/null
+++ b/spec/ruby/library/socket/socket/ip_address_list_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../spec_helper'
+
+describe 'Socket.ip_address_list' do
+ it 'returns an Array' do
+ Socket.ip_address_list.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = Socket.ip_address_list
+ end
+
+ it 'is not empty' do
+ @array.should_not be_empty
+ end
+
+ it 'contains Addrinfo objects' do
+ @array.each do |klass|
+ klass.should be_an_instance_of(Addrinfo)
+ end
+ end
+ end
+
+ describe 'each returned Addrinfo' do
+ before do
+ @array = Socket.ip_address_list
+ end
+
+ it 'has a non-empty IP address' do
+ @array.each do |addr|
+ addr.ip_address.should be_an_instance_of(String)
+ addr.ip_address.should_not be_empty
+ end
+ end
+
+ it 'has an address family' do
+ families = [Socket::AF_INET, Socket::AF_INET6]
+
+ @array.each do |addr|
+ families.include?(addr.afamily).should == true
+ end
+ end
+
+ it 'uses 0 as the port number' do
+ @array.each do |addr|
+ addr.ip_port.should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/ipv6only_bang_spec.rb b/spec/ruby/library/socket/socket/ipv6only_bang_spec.rb
new file mode 100644
index 0000000000..4f429c089e
--- /dev/null
+++ b/spec/ruby/library/socket/socket/ipv6only_bang_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+guard -> { SocketSpecs.ipv6_available? } do
+ describe 'Socket#ipv6only!' do
+ before do
+ @socket = Socket.new(:INET6, :DGRAM)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'enables IPv6 only mode' do
+ @socket.ipv6only!
+
+ @socket.getsockopt(:IPV6, :V6ONLY).bool.should == true
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/listen_spec.rb b/spec/ruby/library/socket/socket/listen_spec.rb
new file mode 100644
index 0000000000..d0f9c70c4e
--- /dev/null
+++ b/spec/ruby/library/socket/socket/listen_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket#listen" do
+ before :each do
+ @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ end
+
+ after :each do
+ @socket.closed?.should be_false
+ @socket.close
+ end
+
+ it "verifies we can listen for incoming connections" do
+ sockaddr = Socket.pack_sockaddr_in(0, "127.0.0.1")
+ @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
+ @socket.bind(sockaddr)
+ @socket.listen(1).should == 0
+ end
+end
+
+describe 'Socket#listen' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a DGRAM socket' do
+ before do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'raises Errno::EOPNOTSUPP' do
+ lambda { @server.listen(1) }.should raise_error(Errno::EOPNOTSUPP)
+ end
+ end
+
+ describe 'using a STREAM socket' do
+ before do
+ @server = Socket.new(family, :STREAM)
+ @client = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns 0' do
+ @server.listen(1).should == 0
+ end
+
+ it "raises when the given argument can't be coerced to an Integer" do
+ lambda { @server.listen('cats') }.should raise_error(TypeError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/local_address_spec.rb b/spec/ruby/library/socket/socket/local_address_spec.rb
new file mode 100644
index 0000000000..3687f93a0c
--- /dev/null
+++ b/spec/ruby/library/socket/socket/local_address_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+
+describe 'Socket#local_address' do
+ before do
+ @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP)
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_INET as the address family' do
+ @sock.local_address.afamily.should == Socket::AF_INET
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @sock.local_address.pfamily.should == Socket::PF_INET
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.local_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0.0.0.0 as the IP address' do
+ @sock.local_address.ip_address.should == '0.0.0.0'
+ end
+
+ platform_is_not :windows do
+ it 'uses 0 as the port' do
+ @sock.local_address.ip_port.should == 0
+ end
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.local_address.protocol.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/new_spec.rb b/spec/ruby/library/socket/socket/new_spec.rb
new file mode 100644
index 0000000000..b2ec607f6a
--- /dev/null
+++ b/spec/ruby/library/socket/socket/new_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
diff --git a/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb b/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb
new file mode 100644
index 0000000000..63d4724453
--- /dev/null
+++ b/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/pack_sockaddr'
+
+describe "Socket#pack_sockaddr_in" do
+ it_behaves_like :socket_pack_sockaddr_in, :pack_sockaddr_in
+end
diff --git a/spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb b/spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb
new file mode 100644
index 0000000000..1ee0bc6157
--- /dev/null
+++ b/spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/pack_sockaddr'
+
+describe "Socket#pack_sockaddr_un" do
+ it_behaves_like :socket_pack_sockaddr_un, :pack_sockaddr_un
+end
diff --git a/spec/ruby/library/socket/socket/pair_spec.rb b/spec/ruby/library/socket/socket/pair_spec.rb
new file mode 100644
index 0000000000..292eacd38d
--- /dev/null
+++ b/spec/ruby/library/socket/socket/pair_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/socketpair'
+
+describe "Socket#pair" do
+ it_behaves_like :socket_socketpair, :pair
+end
diff --git a/spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb b/spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb
new file mode 100644
index 0000000000..c1239ae637
--- /dev/null
+++ b/spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb
@@ -0,0 +1,114 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#recvfrom_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ describe 'using an unbound socket' do
+ it 'raises IO::WaitReadable' do
+ lambda { @server.recvfrom_nonblock(1) }.should raise_error(IO::WaitReadable)
+ end
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @client.connect(@server.getsockname)
+ end
+
+ describe 'without any data available' do
+ it 'raises IO::WaitReadable' do
+ lambda { @server.recvfrom_nonblock(1) }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+
+ platform_is(:darwin, :freebsd) { IO.select([@server]) }
+ end
+
+ platform_is_not :windows do
+ it 'returns an Array containing the data and an Addrinfo' do
+ ret = @server.recvfrom_nonblock(1)
+
+ ret.should be_an_instance_of(Array)
+ ret.length.should == 2
+ end
+ end
+
+ describe 'the returned data' do
+ it 'is the same as the sent data' do
+ 5.times do
+ @client.write('hello')
+
+ platform_is(:darwin, :freebsd) { IO.select([@server]) }
+
+ msg, _ = @server.recvfrom_nonblock(5)
+
+ msg.should == 'hello'
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'the returned Array' do
+ before do
+ @array = @server.recvfrom_nonblock(1)
+ end
+
+ it 'contains the data at index 0' do
+ @array[0].should == 'h'
+ end
+
+ it 'contains an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @server.recvfrom_nonblock(1)[1]
+ end
+
+ it 'uses AF_INET as the address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses SOCK_DGRAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'uses the IP address of the client' do
+ @addr.ip_address.should == ip_address
+ end
+
+ it 'uses the port of the client' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/recvfrom_spec.rb b/spec/ruby/library/socket/socket/recvfrom_spec.rb
new file mode 100644
index 0000000000..7f0714511c
--- /dev/null
+++ b/spec/ruby/library/socket/socket/recvfrom_spec.rb
@@ -0,0 +1,92 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#recvfrom' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'using an unbound socket' do
+ it 'blocks the caller' do
+ lambda { @server.recvfrom(1) }.should block_caller
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @client.connect(@server.getsockname)
+ end
+
+ describe 'without any data available' do
+ it 'blocks the caller' do
+ lambda { @server.recvfrom(1) }.should block_caller
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ it 'returns an Array containing the data and an Addrinfo' do
+ ret = @server.recvfrom(1)
+
+ ret.should be_an_instance_of(Array)
+ ret.length.should == 2
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @server.recvfrom(1)
+ end
+
+ it 'contains the data at index 0' do
+ @array[0].should == 'h'
+ end
+
+ it 'contains an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @server.recvfrom(1)[1]
+ end
+
+ it 'uses AF_INET as the address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses SOCK_DGRAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'uses the IP address of the client' do
+ @addr.ip_address.should == ip_address
+ end
+
+ it 'uses the port of the client' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/remote_address_spec.rb b/spec/ruby/library/socket/socket/remote_address_spec.rb
new file mode 100644
index 0000000000..24d60d7f58
--- /dev/null
+++ b/spec/ruby/library/socket/socket/remote_address_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#remote_address' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @host = @server.local_address.ip_address
+ @port = @server.local_address.ip_port
+ @client = Socket.new(family, :STREAM, Socket::IPPROTO_TCP)
+
+ @client.connect(Socket.sockaddr_in(@port, @host))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns an Addrinfo' do
+ @client.remote_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_INET as the address family' do
+ @client.remote_address.afamily.should == family
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @client.remote_address.pfamily.should == family
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @client.remote_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses the correct IP address' do
+ @client.remote_address.ip_address.should == @host
+ end
+
+ it 'uses the correct port' do
+ @client.remote_address.ip_port.should == @port
+ end
+
+ it 'uses 0 as the protocol' do
+ @client.remote_address.protocol.should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/sockaddr_in_spec.rb b/spec/ruby/library/socket/socket/sockaddr_in_spec.rb
new file mode 100644
index 0000000000..8ee956ac26
--- /dev/null
+++ b/spec/ruby/library/socket/socket/sockaddr_in_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/pack_sockaddr'
+
+describe "Socket#sockaddr_in" do
+ it_behaves_like :socket_pack_sockaddr_in, :sockaddr_in
+end
diff --git a/spec/ruby/library/socket/socket/sockaddr_un_spec.rb b/spec/ruby/library/socket/socket/sockaddr_un_spec.rb
new file mode 100644
index 0000000000..8922ff4d6d
--- /dev/null
+++ b/spec/ruby/library/socket/socket/sockaddr_un_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/pack_sockaddr'
+
+describe "Socket#sockaddr_un" do
+ it_behaves_like :socket_pack_sockaddr_un, :sockaddr_un
+end
diff --git a/spec/ruby/library/socket/socket/socket_spec.rb b/spec/ruby/library/socket/socket/socket_spec.rb
new file mode 100644
index 0000000000..5a3d6733e0
--- /dev/null
+++ b/spec/ruby/library/socket/socket/socket_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket" do
+ it "inherits from BasicSocket and IO" do
+ Socket.superclass.should == BasicSocket
+ BasicSocket.superclass.should == IO
+ end
+end
+
+describe "The socket class hierarchy" do
+ it "has an IPSocket in parallel to Socket" do
+ Socket.ancestors.include?(IPSocket).should == false
+ IPSocket.ancestors.include?(Socket).should == false
+ IPSocket.superclass.should == BasicSocket
+ end
+
+ it "has TCPSocket and UDPSocket subclasses of IPSocket" do
+ TCPSocket.superclass.should == IPSocket
+ UDPSocket.superclass.should == IPSocket
+ end
+
+ platform_is_not :windows do
+ it "has a UNIXSocket in parallel to Socket" do
+ Socket.ancestors.include?(UNIXSocket).should == false
+ UNIXSocket.ancestors.include?(Socket).should == false
+ UNIXSocket.superclass.should == BasicSocket
+ end
+ end
+end
+
+platform_is_not :windows do
+ describe "Server class hierarchy" do
+ it "contains UNIXServer" do
+ UNIXServer.superclass.should == UNIXSocket
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/socketpair_spec.rb b/spec/ruby/library/socket/socket/socketpair_spec.rb
new file mode 100644
index 0000000000..5b8311124e
--- /dev/null
+++ b/spec/ruby/library/socket/socket/socketpair_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/socketpair'
+
+describe "Socket#socketpair" do
+ it_behaves_like :socket_socketpair, :socketpair
+end
diff --git a/spec/ruby/library/socket/socket/sysaccept_spec.rb b/spec/ruby/library/socket/socket/sysaccept_spec.rb
new file mode 100644
index 0000000000..f039096707
--- /dev/null
+++ b/spec/ruby/library/socket/socket/sysaccept_spec.rb
@@ -0,0 +1,93 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#sysaccept' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+ end
+
+ after do
+ @server.close
+ end
+
+ platform_is :linux do # hangs on other platforms
+ describe 'using an unbound socket' do
+ it 'raises Errno::EINVAL' do
+ lambda { @server.sysaccept }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ describe "using a bound socket that's not listening" do
+ before do
+ @server.bind(@sockaddr)
+ end
+
+ it 'raises Errno::EINVAL' do
+ lambda { @server.sysaccept }.should raise_error(Errno::EINVAL)
+ end
+ end
+ end
+
+ describe "using a bound socket that's listening" do
+ before do
+ @server.bind(@sockaddr)
+ @server.listen(1)
+
+ server_ip = @server.local_address.ip_port
+ @server_addr = Socket.sockaddr_in(server_ip, ip_address)
+ end
+
+ after do
+ Socket.for_fd(@fd).close if @fd
+ end
+
+ describe 'without a connected client' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ end
+
+ after do
+ @client.close
+ end
+
+ it 'blocks the caller until a connection is available' do
+ thread = Thread.new do
+ @fd, _ = @server.sysaccept
+ end
+
+ @client.connect(@server_addr)
+
+ thread.join(5)
+
+ thread.value.should be_an_instance_of(Array)
+ end
+ end
+
+ describe 'with a connected client' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ end
+
+ it 'returns an Array containing an Integer and an Addrinfo' do
+ @fd, addrinfo = @server.sysaccept
+
+ @fd.should be_kind_of(Integer)
+ addrinfo.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'returns a new file descriptor' do
+ @fd, _ = @server.sysaccept
+
+ @fd.should_not == @client.fileno
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/tcp_server_loop_spec.rb b/spec/ruby/library/socket/socket/tcp_server_loop_spec.rb
new file mode 100644
index 0000000000..617e3d445c
--- /dev/null
+++ b/spec/ruby/library/socket/socket/tcp_server_loop_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket.tcp_server_loop' do
+ describe 'when no connections are available' do
+ it 'blocks the caller' do
+ lambda { Socket.tcp_server_loop('127.0.0.1', 0) }.should block_caller
+ end
+ end
+
+ describe 'when a connection is available' do
+ before do
+ @client = Socket.new(:INET, :STREAM)
+ @port = 9998
+ end
+
+ after do
+ @sock.close if @sock
+ @client.close
+ end
+
+ it 'yields a Socket and an Addrinfo' do
+ @sock, addr = nil
+
+ thread = Thread.new do
+ Socket.tcp_server_loop('127.0.0.1', @port) do |socket, addrinfo|
+ @sock = socket
+ addr = addrinfo
+
+ break
+ end
+ end
+
+ SocketSpecs.wait_until_success do
+ @client.connect(Socket.sockaddr_in(@port, '127.0.0.1'))
+ end
+
+ # At this point the connection has been set up but the thread may not yet
+ # have returned, thus we'll need to wait a little longer for it to
+ # complete.
+ thread.join(2)
+
+ @sock.should be_an_instance_of(Socket)
+ addr.should be_an_instance_of(Addrinfo)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/tcp_server_sockets_spec.rb b/spec/ruby/library/socket/socket/tcp_server_sockets_spec.rb
new file mode 100644
index 0000000000..10c030a8ce
--- /dev/null
+++ b/spec/ruby/library/socket/socket/tcp_server_sockets_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../spec_helper'
+
+describe 'Socket.tcp_server_sockets' do
+ describe 'without a block' do
+ before do
+ @sockets = nil
+ end
+
+ after do
+ @sockets.each(&:close)
+ end
+
+ it 'returns an Array of Socket objects' do
+ @sockets = Socket.tcp_server_sockets(0)
+
+ @sockets.should be_an_instance_of(Array)
+ @sockets[0].should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'with a block' do
+ it 'yields the sockets to the supplied block' do
+ Socket.tcp_server_sockets(0) do |sockets|
+ sockets.should be_an_instance_of(Array)
+ sockets[0].should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes all sockets after the block returns' do
+ sockets = nil
+
+ Socket.tcp_server_sockets(0) { |socks| sockets = socks }
+
+ sockets.each do |socket|
+ socket.closed?.should == true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/tcp_spec.rb b/spec/ruby/library/socket/socket/tcp_spec.rb
new file mode 100644
index 0000000000..29f166ffc5
--- /dev/null
+++ b/spec/ruby/library/socket/socket/tcp_spec.rb
@@ -0,0 +1,70 @@
+require_relative '../spec_helper'
+
+describe 'Socket.tcp' do
+ before do
+ @server = Socket.new(:INET, :STREAM)
+ @client = nil
+
+ @server.bind(Socket.sockaddr_in(0, '127.0.0.1'))
+ @server.listen(1)
+
+ @host = @server.connect_address.ip_address
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @client.close if @client && !@client.closed?
+ @client = nil
+
+ @server.close
+ end
+
+ it 'returns a Socket when no block is given' do
+ @client = Socket.tcp(@host, @port)
+
+ @client.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket when a block is given' do
+ Socket.tcp(@host, @port) do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes the Socket automatically when a block is given' do
+ Socket.tcp(@host, @port) do |socket|
+ @socket = socket
+ end
+
+ @socket.closed?.should == true
+ end
+
+ it 'binds to a local address and port when specified' do
+ @client = Socket.tcp(@host, @port, @host, 0)
+
+ @client.local_address.ip_address.should == @host
+
+ @client.local_address.ip_port.should > 0
+ @client.local_address.ip_port.should_not == @port
+ end
+
+ it 'raises ArgumentError when 6 arguments are provided' do
+ lambda {
+ Socket.tcp(@host, @port, @host, 0, {:connect_timeout => 1}, 10)
+ }.should raise_error(ArgumentError)
+ end
+
+ it 'connects to the server' do
+ @client = Socket.tcp(@host, @port)
+
+ @client.write('hello')
+
+ connection, _ = @server.accept
+
+ begin
+ connection.recv(5).should == 'hello'
+ ensure
+ connection.close
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/udp_server_loop_on_spec.rb b/spec/ruby/library/socket/socket/udp_server_loop_on_spec.rb
new file mode 100644
index 0000000000..1cb82d72be
--- /dev/null
+++ b/spec/ruby/library/socket/socket/udp_server_loop_on_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../spec_helper'
+
+describe 'Socket.udp_server_loop_on' do
+ before do
+ @server = Socket.new(:INET, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, '127.0.0.1'))
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'when no connections are available' do
+ it 'blocks the caller' do
+ lambda { Socket.udp_server_loop_on([@server]) }.should block_caller
+ end
+ end
+
+ describe 'when a connection is available' do
+ before do
+ @client = Socket.new(:INET, :DGRAM)
+ end
+
+ after do
+ @client.close
+ end
+
+ it 'yields the message and a Socket::UDPSource' do
+ msg = nil
+ src = nil
+
+ @client.connect(@server.getsockname)
+ @client.write('hello')
+
+ Socket.udp_server_loop_on([@server]) do |message, source|
+ msg = message
+ src = source
+
+ break
+ end
+
+ msg.should == 'hello'
+ src.should be_an_instance_of(Socket::UDPSource)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/udp_server_loop_spec.rb b/spec/ruby/library/socket/socket/udp_server_loop_spec.rb
new file mode 100644
index 0000000000..95f73a5c35
--- /dev/null
+++ b/spec/ruby/library/socket/socket/udp_server_loop_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket.udp_server_loop' do
+ describe 'when no connections are available' do
+ it 'blocks the caller' do
+ lambda { Socket.udp_server_loop('127.0.0.1', 0) }.should block_caller
+ end
+ end
+
+ describe 'when a connection is available' do
+ before do
+ @client = Socket.new(:INET, :DGRAM)
+ @port = 9997
+ end
+
+ after do
+ @client.close
+ end
+
+ it 'yields the message and a Socket::UDPSource' do
+ msg, src = nil
+
+ Thread.new do
+ Socket.udp_server_loop('127.0.0.1', @port) do |message, source|
+ msg = message
+ src = source
+
+ break
+ end
+ end
+
+ # Because this will return even if the server is up and running (it's UDP
+ # after all) we'll have to write and wait until "msg" is set.
+ @client.connect(Socket.sockaddr_in(@port, '127.0.0.1'))
+
+ SocketSpecs.loop_with_timeout do
+ SocketSpecs.wait_until_success { @client.write('hello') }
+
+ break if msg
+ end
+
+ msg.should == 'hello'
+ src.should be_an_instance_of(Socket::UDPSource)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/udp_server_recv_spec.rb b/spec/ruby/library/socket/socket/udp_server_recv_spec.rb
new file mode 100644
index 0000000000..f7165dd25d
--- /dev/null
+++ b/spec/ruby/library/socket/socket/udp_server_recv_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../spec_helper'
+
+describe 'Socket.udp_server_recv' do
+ before do
+ @server = Socket.new(:INET, :DGRAM)
+ @client = Socket.new(:INET, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, '127.0.0.1'))
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'yields the message and a Socket::UDPSource' do
+ msg = nil
+ src = nil
+
+ @client.write('hello')
+
+ # FreeBSD sockets are not instanteous over loopback and
+ # will EAGAIN on recv.
+ platform_is :darwin, :freebsd do
+ IO.select([@server])
+ end
+
+ Socket.udp_server_recv([@server]) do |message, source|
+ msg = message
+ src = source
+ break
+ end
+
+ msg.should == 'hello'
+ src.should be_an_instance_of(Socket::UDPSource)
+ end
+end
diff --git a/spec/ruby/library/socket/socket/udp_server_sockets_spec.rb b/spec/ruby/library/socket/socket/udp_server_sockets_spec.rb
new file mode 100644
index 0000000000..3aeb472dda
--- /dev/null
+++ b/spec/ruby/library/socket/socket/udp_server_sockets_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../spec_helper'
+
+describe 'Socket.udp_server_sockets' do
+ describe 'without a block' do
+ before do
+ @sockets = nil
+ end
+
+ after do
+ @sockets.each(&:close)
+ end
+
+ it 'returns an Array of Socket objects' do
+ @sockets = Socket.udp_server_sockets(0)
+
+ @sockets.should be_an_instance_of(Array)
+ @sockets[0].should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'with a block' do
+ it 'yields the sockets to the supplied block' do
+ Socket.udp_server_sockets(0) do |sockets|
+ sockets.should be_an_instance_of(Array)
+ sockets[0].should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes all sockets after the block returns' do
+ sockets = nil
+
+ Socket.udp_server_sockets(0) { |socks| sockets = socks }
+
+ sockets.each do |socket|
+ socket.closed?.should == true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/unix_server_loop_spec.rb b/spec/ruby/library/socket/socket/unix_server_loop_spec.rb
new file mode 100644
index 0000000000..52c535cd45
--- /dev/null
+++ b/spec/ruby/library/socket/socket/unix_server_loop_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'Socket.unix_server_loop' do
+ before do
+ @path = SocketSpecs.socket_path
+ end
+
+ after do
+ rm_r(@path) if File.file?(@path)
+ end
+
+ describe 'when no connections are available' do
+ it 'blocks the caller' do
+ lambda { Socket.unix_server_loop(@path) }.should block_caller
+ end
+ end
+
+ describe 'when a connection is available' do
+ before do
+ @client = nil
+ end
+
+ after do
+ @sock.close if @sock
+ @client.close if @client
+ end
+
+ it 'yields a Socket and an Addrinfo' do
+ @sock, addr = nil
+
+ thread = Thread.new do
+ Socket.unix_server_loop(@path) do |socket, addrinfo|
+ @sock = socket
+ addr = addrinfo
+
+ break
+ end
+ end
+
+ @client = SocketSpecs.wait_until_success { Socket.unix(@path) }
+
+ thread.join(2)
+
+ @sock.should be_an_instance_of(Socket)
+ addr.should be_an_instance_of(Addrinfo)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/unix_server_socket_spec.rb b/spec/ruby/library/socket/socket/unix_server_socket_spec.rb
new file mode 100644
index 0000000000..fc357740fa
--- /dev/null
+++ b/spec/ruby/library/socket/socket/unix_server_socket_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'Socket.unix_server_socket' do
+ before do
+ @path = SocketSpecs.socket_path
+ end
+
+ after do
+ rm_r(@path)
+ end
+
+ describe 'when no block is given' do
+ before do
+ @socket = nil
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'returns a Socket' do
+ @socket = Socket.unix_server_socket(@path)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'when a block is given' do
+ it 'yields a Socket' do
+ Socket.unix_server_socket(@path) do |sock|
+ sock.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes the Socket when the block returns' do
+ socket = nil
+
+ Socket.unix_server_socket(@path) do |sock|
+ socket = sock
+ end
+
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/unix_spec.rb b/spec/ruby/library/socket/socket/unix_spec.rb
new file mode 100644
index 0000000000..add54a097d
--- /dev/null
+++ b/spec/ruby/library/socket/socket/unix_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'Socket.unix' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @socket = nil
+ end
+
+ after do
+ @server.close
+ @socket.close if @socket
+
+ rm_r(@path)
+ end
+
+ describe 'when no block is given' do
+ it 'returns a Socket' do
+ @socket = Socket.unix(@path)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'when a block is given' do
+ it 'yields a Socket' do
+ Socket.unix(@path) do |sock|
+ sock.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes the Socket when the block returns' do
+ socket = nil
+
+ Socket.unix(@path) do |sock|
+ socket = sock
+ end
+
+ socket.closed?.should == true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/unpack_sockaddr_in_spec.rb b/spec/ruby/library/socket/socket/unpack_sockaddr_in_spec.rb
new file mode 100644
index 0000000000..579ae307df
--- /dev/null
+++ b/spec/ruby/library/socket/socket/unpack_sockaddr_in_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket.unpack_sockaddr_in" do
+ it "decodes the host name and port number of a packed sockaddr_in" do
+ sockaddr = Socket.sockaddr_in 3333, '127.0.0.1'
+ Socket.unpack_sockaddr_in(sockaddr).should == [3333, '127.0.0.1']
+ end
+
+ it "gets the hostname and port number from a passed Addrinfo" do
+ addrinfo = Addrinfo.tcp('127.0.0.1', 3333)
+ Socket.unpack_sockaddr_in(addrinfo).should == [3333, '127.0.0.1']
+ end
+
+ describe 'using an IPv4 address' do
+ it 'returns an Array containing the port and IP address' do
+ port = 80
+ ip = '127.0.0.1'
+ addr = Socket.pack_sockaddr_in(port, ip)
+
+ Socket.unpack_sockaddr_in(addr).should == [port, ip]
+ end
+ end
+
+ describe 'using an IPv6 address' do
+ it 'returns an Array containing the port and IP address' do
+ port = 80
+ ip = '::1'
+ addr = Socket.pack_sockaddr_in(port, ip)
+
+ Socket.unpack_sockaddr_in(addr).should == [port, ip]
+ end
+ end
+
+ with_feature :unix_socket do
+ it "raises an ArgumentError when the sin_family is not AF_INET" do
+ sockaddr = Socket.sockaddr_un '/tmp/x'
+ lambda { Socket.unpack_sockaddr_in sockaddr }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed addrinfo is not AF_INET/AF_INET6" do
+ addrinfo = Addrinfo.unix('/tmp/sock')
+ lambda { Socket.unpack_sockaddr_in(addrinfo) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/unpack_sockaddr_un_spec.rb b/spec/ruby/library/socket/socket/unpack_sockaddr_un_spec.rb
new file mode 100644
index 0000000000..39e33c90e9
--- /dev/null
+++ b/spec/ruby/library/socket/socket/unpack_sockaddr_un_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'Socket.unpack_sockaddr_un' do
+ it 'decodes sockaddr to unix path' do
+ sockaddr = Socket.sockaddr_un('/tmp/sock')
+ Socket.unpack_sockaddr_un(sockaddr).should == '/tmp/sock'
+ end
+
+ it 'returns unix path from a passed Addrinfo' do
+ addrinfo = Addrinfo.unix('/tmp/sock')
+ Socket.unpack_sockaddr_un(addrinfo).should == '/tmp/sock'
+ end
+
+ it 'raises an ArgumentError when the sin_family is not AF_UNIX' do
+ sockaddr = Socket.sockaddr_in(0, '127.0.0.1')
+ lambda { Socket.unpack_sockaddr_un(sockaddr) }.should raise_error(ArgumentError)
+ end
+
+ it 'raises an ArgumentError when passed addrinfo is not AF_UNIX' do
+ addrinfo = Addrinfo.tcp('127.0.0.1', 0)
+ lambda { Socket.unpack_sockaddr_un(addrinfo) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/spec_helper.rb b/spec/ruby/library/socket/spec_helper.rb
new file mode 100644
index 0000000000..8976937ac7
--- /dev/null
+++ b/spec/ruby/library/socket/spec_helper.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require 'socket'
+
+if %w[rbx truffleruby].include?(RUBY_ENGINE)
+ MSpec.enable_feature :pure_ruby_addrinfo
+end
+
+MSpec.enable_feature :sock_packet if Socket.const_defined?(:SOCK_PACKET)
+MSpec.enable_feature :unix_socket unless PlatformGuard.windows?
+MSpec.enable_feature :udp_cork if Socket.const_defined?(:UDP_CORK)
+MSpec.enable_feature :tcp_cork if Socket.const_defined?(:TCP_CORK)
+MSpec.enable_feature :pktinfo if Socket.const_defined?(:IP_PKTINFO)
+MSpec.enable_feature :ipv6_pktinfo if Socket.const_defined?(:IPV6_PKTINFO)
+MSpec.enable_feature :ip_mtu if Socket.const_defined?(:IP_MTU)
+MSpec.enable_feature :ipv6_nexthop if Socket.const_defined?(:IPV6_NEXTHOP)
+MSpec.enable_feature :tcp_info if Socket.const_defined?(:TCP_INFO)
+MSpec.enable_feature :ancillary_data if Socket.const_defined?(:AncillaryData)
diff --git a/spec/ruby/library/socket/tcpserver/accept_nonblock_spec.rb b/spec/ruby/library/socket/tcpserver/accept_nonblock_spec.rb
new file mode 100644
index 0000000000..27d6a0d08e
--- /dev/null
+++ b/spec/ruby/library/socket/tcpserver/accept_nonblock_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::TCPServer.accept_nonblock" do
+ before :each do
+ @server = TCPServer.new("127.0.0.1", 0)
+ @port = @server.addr[1]
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ end
+
+ it "accepts non blocking connections" do
+ @server.listen(5)
+ lambda {
+ @server.accept_nonblock
+ }.should raise_error(IO::WaitReadable)
+
+ c = TCPSocket.new("127.0.0.1", @port)
+ sleep 0.1
+ s = @server.accept_nonblock
+
+ port, address = Socket.unpack_sockaddr_in(s.getsockname)
+
+ port.should == @port
+ address.should == "127.0.0.1"
+ s.should be_kind_of(TCPSocket)
+
+ c.close
+ s.close
+ end
+
+ it "raises an IOError if the socket is closed" do
+ @server.close
+ lambda { @server.accept }.should raise_error(IOError)
+ end
+
+ describe 'without a connected client' do
+ it 'raises error' do
+ lambda { @server.accept_nonblock }.should raise_error(IO::WaitReadable)
+ end
+
+ it 'returns :wait_readable in exceptionless mode' do
+ @server.accept_nonblock(exception: false).should == :wait_readable
+ end
+ end
+end
+
+describe 'TCPServer#accept_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'without a connected client' do
+ it 'raises IO::WaitReadable' do
+ lambda { @server.accept_nonblock }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ platform_is_not :windows do # spurious
+ describe 'with a connected client' do
+ before do
+ @client = TCPSocket.new(ip_address, @server.connect_address.ip_port)
+ end
+
+ after do
+ @socket.close if @socket
+ @client.close
+ end
+
+ it 'returns a TCPSocket' do
+ @socket = @server.accept_nonblock
+ @socket.should be_an_instance_of(TCPSocket)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpserver/accept_spec.rb b/spec/ruby/library/socket/tcpserver/accept_spec.rb
new file mode 100644
index 0000000000..f0ef9cc727
--- /dev/null
+++ b/spec/ruby/library/socket/tcpserver/accept_spec.rb
@@ -0,0 +1,99 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "TCPServer#accept" do
+ before :each do
+ @server = TCPServer.new("127.0.0.1", 0)
+ @port = @server.addr[1]
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ end
+
+ it "accepts a connection and returns a TCPSocket" do
+ data = nil
+ t = Thread.new do
+ client = @server.accept
+ client.should be_kind_of(TCPSocket)
+ data = client.read(5)
+ client << "goodbye"
+ client.close
+ end
+ Thread.pass while t.status and t.status != "sleep"
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.write('hello')
+ socket.shutdown(1) # we are done with sending
+ socket.read.should == 'goodbye'
+ t.join
+ data.should == 'hello'
+ socket.close
+ end
+
+ it "can be interrupted by Thread#kill" do
+ t = Thread.new { @server.accept }
+
+ Thread.pass while t.status and t.status != "sleep"
+
+ # kill thread, ensure it dies in a reasonable amount of time
+ t.kill
+ a = 0
+ while t.alive? and a < 5000
+ sleep 0.001
+ a += 1
+ end
+ a.should < 5000
+ end
+
+ it "can be interrupted by Thread#raise" do
+ t = Thread.new {
+ -> {
+ @server.accept
+ }.should raise_error(Exception, "interrupted")
+ }
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.raise Exception, "interrupted"
+ t.join
+ end
+
+ it "raises an IOError if the socket is closed" do
+ @server.close
+ lambda { @server.accept }.should raise_error(IOError)
+ end
+end
+
+describe 'TCPServer#accept' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'without a connected client' do
+ it 'blocks the caller' do
+ lambda { @server.accept }.should block_caller
+ end
+ end
+
+ describe 'with a connected client' do
+ before do
+ @client = TCPSocket.new(ip_address, @server.connect_address.ip_port)
+ end
+
+ after do
+ @socket.close if @socket
+ @client.close
+ end
+
+ it 'returns a TCPSocket' do
+ @socket = @server.accept
+ @socket.should be_an_instance_of(TCPSocket)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpserver/gets_spec.rb b/spec/ruby/library/socket/tcpserver/gets_spec.rb
new file mode 100644
index 0000000000..936295dce2
--- /dev/null
+++ b/spec/ruby/library/socket/tcpserver/gets_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "TCPServer#gets" do
+ before :each do
+ @server = TCPServer.new(SocketSpecs.hostname, 0)
+ end
+
+ after :each do
+ @server.close
+ end
+
+ it "raises Errno::ENOTCONN on gets" do
+ lambda { @server.gets }.should raise_error(Errno::ENOTCONN)
+ end
+end
diff --git a/spec/ruby/library/socket/tcpserver/initialize_spec.rb b/spec/ruby/library/socket/tcpserver/initialize_spec.rb
new file mode 100644
index 0000000000..412bdbfb9d
--- /dev/null
+++ b/spec/ruby/library/socket/tcpserver/initialize_spec.rb
@@ -0,0 +1,101 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPServer#initialize' do
+ describe 'with a single Integer argument' do
+ before do
+ @server = TCPServer.new(0)
+ end
+
+ after do
+ @server.close
+ end
+
+ it 'sets the port to the given argument' do
+ @server.local_address.ip_port.should be_kind_of(Integer)
+ @server.local_address.ip_port.should > 0
+ end
+
+ platform_is_not :windows do
+ it 'sets the hostname to 0.0.0.0 or ::' do
+ a = @server.local_address
+ a.ip_address.should == (a.ipv6? ? '::' : '0.0.0.0')
+ end
+ end
+
+ it "sets the socket to binmode" do
+ @server.binmode?.should be_true
+ end
+ end
+
+ describe 'with a single String argument containing a numeric value' do
+ before do
+ @server = TCPServer.new('0')
+ end
+
+ after do
+ @server.close
+ end
+
+ it 'sets the port to the given argument' do
+ @server.local_address.ip_port.should be_kind_of(Integer)
+ @server.local_address.ip_port.should > 0
+ end
+
+ platform_is_not :windows do
+ it 'sets the hostname to 0.0.0.0 or ::' do
+ a = @server.local_address
+ a.ip_address.should == (a.ipv6? ? '::' : '0.0.0.0')
+ end
+ end
+ end
+
+ describe 'with a single String argument containing a non numeric value' do
+ it 'raises SocketError' do
+ lambda { TCPServer.new('cats') }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'with a String and an Integer' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ end
+
+ after do
+ @server.close
+ end
+
+ it 'sets the port to the given port argument' do
+ @server.local_address.ip_port.should be_kind_of(Integer)
+ @server.local_address.ip_port.should > 0
+ end
+
+ it 'sets the hostname to the given host argument' do
+ @server.local_address.ip_address.should == ip_address
+ end
+ end
+ end
+
+ describe 'with a String and a custom object' do
+ before do
+ dummy = mock(:dummy)
+ dummy.stub!(:to_str).and_return('0')
+
+ @server = TCPServer.new('127.0.0.1', dummy)
+ end
+
+ after do
+ @server.close
+ end
+
+ it 'sets the port to the given port argument' do
+ @server.local_address.ip_port.should be_kind_of(Integer)
+ @server.local_address.ip_port.should > 0
+ end
+
+ it 'sets the hostname to the given host argument' do
+ @server.local_address.ip_address.should == '127.0.0.1'
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpserver/listen_spec.rb b/spec/ruby/library/socket/tcpserver/listen_spec.rb
new file mode 100644
index 0000000000..e266decd61
--- /dev/null
+++ b/spec/ruby/library/socket/tcpserver/listen_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPServer#listen' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ end
+
+ after do
+ @server.close
+ end
+
+ it 'returns 0' do
+ @server.listen(1).should == 0
+ end
+
+ it "raises when the given argument can't be coerced to an Integer" do
+ lambda { @server.listen('cats') }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpserver/new_spec.rb b/spec/ruby/library/socket/tcpserver/new_spec.rb
new file mode 100644
index 0000000000..4717b95a2c
--- /dev/null
+++ b/spec/ruby/library/socket/tcpserver/new_spec.rb
@@ -0,0 +1,96 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "TCPServer.new" do
+ after :each do
+ @server.close if @server && !@server.closed?
+ end
+
+ it "binds to a host and a port" do
+ @server = TCPServer.new('127.0.0.1', 0)
+ addr = @server.addr
+ addr[0].should == 'AF_INET'
+ addr[1].should be_kind_of(Integer)
+ # on some platforms (Mac), MRI
+ # returns comma at the end.
+ addr[2].should =~ /^#{SocketSpecs.hostname}\b/
+ addr[3].should == '127.0.0.1'
+ end
+
+ it "binds to localhost and a port with either IPv4 or IPv6" do
+ @server = TCPServer.new(SocketSpecs.hostname, 0)
+ addr = @server.addr
+ addr[1].should be_kind_of(Integer)
+ if addr[0] == 'AF_INET'
+ addr[2].should =~ /^#{SocketSpecs.hostname}\b/
+ addr[3].should == '127.0.0.1'
+ else
+ addr[2].should =~ /^#{SocketSpecs.hostname('::1')}\b/
+ addr[3].should == '::1'
+ end
+ end
+
+ it "binds to INADDR_ANY if the hostname is empty" do
+ @server = TCPServer.new('', 0)
+ addr = @server.addr
+ addr[0].should == 'AF_INET'
+ addr[1].should be_kind_of(Integer)
+ addr[2].should == '0.0.0.0'
+ addr[3].should == '0.0.0.0'
+ end
+
+ it "binds to INADDR_ANY if the hostname is empty and the port is a string" do
+ @server = TCPServer.new('', 0)
+ addr = @server.addr
+ addr[0].should == 'AF_INET'
+ addr[1].should be_kind_of(Integer)
+ addr[2].should == '0.0.0.0'
+ addr[3].should == '0.0.0.0'
+ end
+
+ it "coerces port to string, then determines port from that number or service name" do
+ lambda { TCPServer.new(SocketSpecs.hostname, Object.new) }.should raise_error(TypeError)
+
+ port = Object.new
+ port.should_receive(:to_str).and_return("0")
+
+ @server = TCPServer.new(SocketSpecs.hostname, port)
+ addr = @server.addr
+ addr[1].should be_kind_of(Integer)
+
+ # TODO: This should also accept strings like 'https', but I don't know how to
+ # pick such a service port that will be able to reliably bind...
+ end
+
+ it "raises Errno::EADDRNOTAVAIL when the address is unknown" do
+ lambda { TCPServer.new("1.2.3.4", 0) }.should raise_error(Errno::EADDRNOTAVAIL)
+ end
+
+ # There is no way to make this fail-proof on all machines, because
+ # DNS servers like opendns return A records for ANY host, including
+ # traditionally invalidly named ones.
+ quarantine! do
+ it "raises a SocketError when the host is unknown" do
+ lambda {
+ TCPServer.new("--notavalidname", 0)
+ }.should raise_error(SocketError)
+ end
+ end
+
+ it "raises Errno::EADDRINUSE when address is already in use" do
+ @server = TCPServer.new('127.0.0.1', 0)
+ lambda {
+ @server = TCPServer.new('127.0.0.1', @server.addr[1])
+ }.should raise_error(Errno::EADDRINUSE)
+ end
+
+ platform_is_not :windows, :aix do
+ # A known bug in AIX. getsockopt(2) does not properly set
+ # the fifth argument for SO_REUSEADDR.
+ it "sets SO_REUSEADDR on the resulting server" do
+ @server = TCPServer.new('127.0.0.1', 0)
+ @server.getsockopt(:SOCKET, :REUSEADDR).data.should_not == "\x00\x00\x00\x00"
+ @server.getsockopt(:SOCKET, :REUSEADDR).int.should_not == 0
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpserver/sysaccept_spec.rb b/spec/ruby/library/socket/tcpserver/sysaccept_spec.rb
new file mode 100644
index 0000000000..5543b67755
--- /dev/null
+++ b/spec/ruby/library/socket/tcpserver/sysaccept_spec.rb
@@ -0,0 +1,66 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "TCPServer#sysaccept" do
+ before :each do
+ @server = TCPServer.new(SocketSpecs.hostname, 0)
+ @port = @server.addr[1]
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ end
+
+ it 'blocks if no connections' do
+ lambda { @server.sysaccept }.should block_caller
+ end
+
+ it 'returns file descriptor of an accepted connection' do
+ begin
+ sock = TCPSocket.new(SocketSpecs.hostname, @port)
+
+ fd = @server.sysaccept
+
+ fd.should be_kind_of(Integer)
+ ensure
+ sock.close if sock && !sock.closed?
+ IO.for_fd(fd).close if fd
+ end
+ end
+end
+
+describe 'TCPServer#sysaccept' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'without a connected client' do
+ it 'blocks the caller' do
+ lambda { @server.sysaccept }.should block_caller
+ end
+ end
+
+ describe 'with a connected client' do
+ before do
+ @client = TCPSocket.new(ip_address, @server.connect_address.ip_port)
+ end
+
+ after do
+ Socket.for_fd(@fd).close if @fd
+ @client.close
+ end
+
+ it 'returns a new file descriptor as an Integer' do
+ @fd = @server.sysaccept
+
+ @fd.should be_kind_of(Integer)
+ @fd.should_not == @client.fileno
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/gethostbyname_spec.rb b/spec/ruby/library/socket/tcpsocket/gethostbyname_spec.rb
new file mode 100644
index 0000000000..703abff81c
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/gethostbyname_spec.rb
@@ -0,0 +1,111 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+# TODO: verify these for windows
+describe "TCPSocket#gethostbyname" do
+ before :each do
+ @host_info = TCPSocket.gethostbyname(SocketSpecs.hostname)
+ end
+
+ it "returns an array elements of information on the hostname" do
+ @host_info.should be_kind_of(Array)
+ end
+
+ platform_is_not :windows do
+ it "returns the canonical name as first value" do
+ @host_info[0].should == SocketSpecs.hostname
+ end
+
+ it "returns the address type as the third value" do
+ address_type = @host_info[2]
+ [Socket::AF_INET, Socket::AF_INET6].include?(address_type).should be_true
+ end
+
+ it "returns the IP address as the fourth value" do
+ ip = @host_info[3]
+ ["127.0.0.1", "::1"].include?(ip).should be_true
+ end
+ end
+
+ platform_is :windows do
+ quarantine! do # name lookup seems not working on Windows CI
+ it "returns the canonical name as first value" do
+ host = "#{ENV['COMPUTERNAME'].downcase}"
+ host << ".#{ENV['USERDNSDOMAIN'].downcase}" if ENV['USERDNSDOMAIN']
+ @host_info[0].should == host
+ end
+ end
+
+ it "returns the address type as the third value" do
+ @host_info[2].should == Socket::AF_INET
+ end
+
+ it "returns the IP address as the fourth value" do
+ @host_info[3].should == "127.0.0.1"
+ end
+ end
+
+ it "returns any aliases to the address as second value" do
+ @host_info[1].should be_kind_of(Array)
+ end
+end
+
+describe 'TCPSocket#gethostbyname' do
+ it 'returns an Array' do
+ TCPSocket.gethostbyname('127.0.0.1').should be_an_instance_of(Array)
+ end
+
+ describe 'using a hostname' do
+ describe 'the returned Array' do
+ before do
+ @array = TCPSocket.gethostbyname('127.0.0.1')
+ end
+
+ it 'includes the canonical name as the 1st value' do
+ @array[0].should == '127.0.0.1'
+ end
+
+ it 'includes an array of alternative hostnames as the 2nd value' do
+ @array[1].should be_an_instance_of(Array)
+ end
+
+ it 'includes the address family as the 3rd value' do
+ @array[2].should be_kind_of(Integer)
+ end
+
+ it 'includes the IP addresses as all the remaining values' do
+ ips = %w{::1 127.0.0.1}
+
+ ips.include?(@array[3]).should == true
+
+ # Not all machines might have both IPv4 and IPv6 set up, so this value is
+ # optional.
+ ips.include?(@array[4]).should == true if @array[4]
+ end
+ end
+ end
+
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'the returned Array' do
+ before do
+ @array = TCPSocket.gethostbyname(ip_address)
+ end
+
+ it 'includes the IP address as the 1st value' do
+ @array[0].should == ip_address
+ end
+
+ it 'includes an empty list of aliases as the 2nd value' do
+ @array[1].should == []
+ end
+
+ it 'includes the address family as the 3rd value' do
+ @array[2].should == family
+ end
+
+ it 'includes the IP address as the 4th value' do
+ @array[3].should == ip_address
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/initialize_spec.rb b/spec/ruby/library/socket/tcpsocket/initialize_spec.rb
new file mode 100644
index 0000000000..a3593f0d5f
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/initialize_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPSocket#initialize' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'when no server is listening on the given address' do
+ it 'raises Errno::ECONNREFUSED' do
+ lambda { TCPSocket.new(ip_address, 666) }.should raise_error(Errno::ECONNREFUSED)
+ end
+ end
+
+ describe 'when a server is listening on the given address' do
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @client.close if @client
+ @server.close
+ end
+
+ it 'returns a TCPSocket when using an Integer as the port' do
+ @client = TCPSocket.new(ip_address, @port)
+ @client.should be_an_instance_of(TCPSocket)
+ end
+
+ it 'returns a TCPSocket when using a String as the port' do
+ @client = TCPSocket.new(ip_address, @port.to_s)
+ @client.should be_an_instance_of(TCPSocket)
+ end
+
+ it 'raises SocketError when the port number is a non numeric String' do
+ lambda { TCPSocket.new(ip_address, 'cats') }.should raise_error(SocketError)
+ end
+
+ it 'set the socket to binmode' do
+ @client = TCPSocket.new(ip_address, @port)
+ @client.binmode?.should be_true
+ end
+
+ it 'connects to the right address' do
+ @client = TCPSocket.new(ip_address, @port)
+
+ @client.remote_address.ip_address.should == @server.local_address.ip_address
+ @client.remote_address.ip_port.should == @server.local_address.ip_port
+ end
+
+ describe 'using a local address and service' do
+ it 'binds the client socket to the local address and service' do
+ @client = TCPSocket.new(ip_address, @port, ip_address, 0)
+
+ @client.local_address.ip_address.should == ip_address
+
+ @client.local_address.ip_port.should > 0
+ @client.local_address.ip_port.should_not == @port
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/local_address_spec.rb b/spec/ruby/library/socket/tcpsocket/local_address_spec.rb
new file mode 100644
index 0000000000..ce66d5ff8f
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/local_address_spec.rb
@@ -0,0 +1,73 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPSocket#local_address' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @host = @server.connect_address.ip_address
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'using an explicit hostname' do
+ before do
+ @sock = TCPSocket.new(@host, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_INET as the address family' do
+ @sock.local_address.afamily.should == family
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @sock.local_address.pfamily.should == family
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.local_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses the correct IP address' do
+ @sock.local_address.ip_address.should == @host
+ end
+
+ it 'uses a randomly assigned local port' do
+ @sock.local_address.ip_port.should > 0
+ @sock.local_address.ip_port.should_not == @port
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.local_address.protocol.should == 0
+ end
+ end
+ end
+
+ describe 'using an implicit hostname' do
+ before do
+ @sock = TCPSocket.new(nil, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct IP address' do
+ @sock.local_address.ip_address.should == @host
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/new_spec.rb b/spec/ruby/library/socket/tcpsocket/new_spec.rb
new file mode 100644
index 0000000000..4924468be7
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/new_spec.rb
@@ -0,0 +1,5 @@
+require_relative 'shared/new'
+
+describe "TCPSocket.new" do
+ it_behaves_like :tcpsocket_new, :new
+end
diff --git a/spec/ruby/library/socket/tcpsocket/open_spec.rb b/spec/ruby/library/socket/tcpsocket/open_spec.rb
new file mode 100644
index 0000000000..31b630a23b
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/open_spec.rb
@@ -0,0 +1,5 @@
+require_relative 'shared/new'
+
+describe "TCPSocket.open" do
+ it_behaves_like :tcpsocket_new, :open
+end
diff --git a/spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb b/spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb
new file mode 100644
index 0000000000..a381627a39
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/partially_closable_sockets'
+
+describe "TCPSocket partial closability" do
+
+ before :each do
+ @server = TCPServer.new("127.0.0.1", 0)
+ @s1 = TCPSocket.new("127.0.0.1", @server.addr[1])
+ @s2 = @server.accept
+ end
+
+ after :each do
+ @server.close
+ @s1.close
+ @s2.close
+ end
+
+ it_should_behave_like "partially closable sockets"
+
+end
diff --git a/spec/ruby/library/socket/tcpsocket/recv_nonblock_spec.rb b/spec/ruby/library/socket/tcpsocket/recv_nonblock_spec.rb
new file mode 100644
index 0000000000..bfd815c658
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/recv_nonblock_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "TCPSocket#recv_nonblock" do
+ before :each do
+ @server = SocketSpecs::SpecTCPServer.new
+ @hostname = @server.hostname
+ end
+
+ after :each do
+ if @socket
+ @socket.write "QUIT"
+ @socket.close
+ end
+ @server.shutdown
+ end
+
+ it "returns a String read from the socket" do
+ @socket = TCPSocket.new @hostname, @server.port
+ @socket.write "TCPSocket#recv_nonblock"
+
+ # Wait for the server to echo. This spec is testing the return
+ # value, not the non-blocking behavior.
+ #
+ # TODO: Figure out a good way to test non-blocking.
+ IO.select([@socket])
+ @socket.recv_nonblock(50).should == "TCPSocket#recv_nonblock"
+ end
+
+ it 'returns :wait_readable in exceptionless mode' do
+ @socket = TCPSocket.new @hostname, @server.port
+ @socket.recv_nonblock(50, exception: false).should == :wait_readable
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/recv_spec.rb b/spec/ruby/library/socket/tcpsocket/recv_spec.rb
new file mode 100644
index 0000000000..f380db670d
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/recv_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPSocket#recv' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @client = TCPSocket.new(ip_address, @server.connect_address.ip_port)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns the message data' do
+ @client.write('hello')
+
+ socket = @server.accept
+
+ begin
+ socket.recv(5).should == 'hello'
+ ensure
+ socket.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/remote_address_spec.rb b/spec/ruby/library/socket/tcpsocket/remote_address_spec.rb
new file mode 100644
index 0000000000..eb9dabc075
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/remote_address_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPSocket#remote_address' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @host = @server.connect_address.ip_address
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'using an explicit hostname' do
+ before do
+ @sock = TCPSocket.new(@host, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.remote_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_INET as the address family' do
+ @sock.remote_address.afamily.should == family
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @sock.remote_address.pfamily.should == family
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.remote_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses the correct IP address' do
+ @sock.remote_address.ip_address.should == @host
+ end
+
+ it 'uses the correct port' do
+ @sock.remote_address.ip_port.should == @port
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.remote_address.protocol.should == 0
+ end
+ end
+ end
+
+ describe 'using an implicit hostname' do
+ before do
+ @sock = TCPSocket.new(nil, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct IP address' do
+ @sock.remote_address.ip_address.should == @host
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/setsockopt_spec.rb b/spec/ruby/library/socket/tcpsocket/setsockopt_spec.rb
new file mode 100644
index 0000000000..8b728b7522
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/setsockopt_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "TCPSocket#setsockopt" do
+ before :each do
+ @server = SocketSpecs::SpecTCPServer.new
+ @hostname = @server.hostname
+ @sock = TCPSocket.new @hostname, @server.port
+ end
+
+ after :each do
+ @sock.close unless @sock.closed?
+ @server.shutdown
+ end
+
+ describe "using constants" do
+ it "sets the TCP nodelay to 1" do
+ @sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1).should == 0
+ end
+ end
+
+ describe "using symbols" do
+ it "sets the TCP nodelay to 1" do
+ @sock.setsockopt(:IPPROTO_TCP, :TCP_NODELAY, 1).should == 0
+ end
+
+ context "without prefix" do
+ it "sets the TCP nodelay to 1" do
+ @sock.setsockopt(:TCP, :NODELAY, 1).should == 0
+ end
+ end
+ end
+
+ describe "using strings" do
+ it "sets the TCP nodelay to 1" do
+ @sock.setsockopt('IPPROTO_TCP', 'TCP_NODELAY', 1).should == 0
+ end
+
+ context "without prefix" do
+ it "sets the TCP nodelay to 1" do
+ @sock.setsockopt('TCP', 'NODELAY', 1).should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/shared/new.rb b/spec/ruby/library/socket/tcpsocket/shared/new.rb
new file mode 100644
index 0000000000..d0358923c9
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/shared/new.rb
@@ -0,0 +1,79 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/classes'
+
+describe :tcpsocket_new, shared: true do
+ it "requires a hostname and a port as arguments" do
+ lambda { TCPSocket.send(@method) }.should raise_error(ArgumentError)
+ end
+
+ it "refuses the connection when there is no server to connect to" do
+ lambda do
+ TCPSocket.send(@method, SocketSpecs.hostname, SocketSpecs.reserved_unused_port)
+ end.should raise_error(SystemCallError) {|e|
+ [Errno::ECONNREFUSED, Errno::EADDRNOTAVAIL].should include(e.class)
+ }
+ end
+
+ describe "with a running server" do
+ before :each do
+ @server = SocketSpecs::SpecTCPServer.new
+ @hostname = @server.hostname
+ end
+
+ after :each do
+ if @socket
+ @socket.write "QUIT"
+ @socket.close
+ end
+ @server.shutdown
+ end
+
+ it "silently ignores 'nil' as the third parameter" do
+ @socket = TCPSocket.send(@method, @hostname, @server.port, nil)
+ @socket.should be_an_instance_of(TCPSocket)
+ end
+
+ it "connects to a listening server with host and port" do
+ @socket = TCPSocket.send(@method, @hostname, @server.port)
+ @socket.should be_an_instance_of(TCPSocket)
+ end
+
+ it "connects to a server when passed local_host argument" do
+ @socket = TCPSocket.send(@method, @hostname, @server.port, @hostname)
+ @socket.should be_an_instance_of(TCPSocket)
+ end
+
+ it "connects to a server when passed local_host and local_port arguments" do
+ server = TCPServer.new(SocketSpecs.hostname, 0)
+ begin
+ available_port = server.addr[1]
+ ensure
+ server.close
+ end
+ @socket = TCPSocket.send(@method, @hostname, @server.port,
+ @hostname, available_port)
+ @socket.should be_an_instance_of(TCPSocket)
+ end
+
+ it "has an address once it has connected to a listening server" do
+ @socket = TCPSocket.send(@method, @hostname, @server.port)
+ @socket.should be_an_instance_of(TCPSocket)
+
+ # TODO: Figure out how to abstract this. You can get AF_INET
+ # from 'Socket.getaddrinfo(hostname, nil)[0][3]' but socket.addr
+ # will return AF_INET6. At least this check will weed out clearly
+ # erroneous values.
+ @socket.addr[0].should =~ /^AF_INET6?/
+
+ case @socket.addr[0]
+ when 'AF_INET'
+ @socket.addr[3].should == SocketSpecs.addr(:ipv4)
+ when 'AF_INET6'
+ @socket.addr[3].should == SocketSpecs.addr(:ipv6)
+ end
+
+ @socket.addr[1].should be_kind_of(Integer)
+ @socket.addr[2].should =~ /^#{@hostname}/
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/bind_spec.rb b/spec/ruby/library/socket/udpsocket/bind_spec.rb
new file mode 100644
index 0000000000..4dbdb285f6
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/bind_spec.rb
@@ -0,0 +1,83 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UDPSocket#bind" do
+ before :each do
+ @socket = UDPSocket.new
+ end
+
+ after :each do
+ @socket.close unless @socket.closed?
+ end
+
+ it "binds the socket to a port" do
+ @socket.bind(SocketSpecs.hostname, 0)
+ @socket.addr[1].should be_kind_of(Integer)
+ end
+
+ it "raises Errno::EINVAL when already bound" do
+ @socket.bind(SocketSpecs.hostname, 0)
+
+ lambda {
+ @socket.bind(SocketSpecs.hostname, @socket.addr[1])
+ }.should raise_error(Errno::EINVAL)
+ end
+
+ it "receives a hostname and a port" do
+ @socket.bind(SocketSpecs.hostname, 0)
+
+ port, host = Socket.unpack_sockaddr_in(@socket.getsockname)
+
+ host.should == "127.0.0.1"
+ port.should == @socket.addr[1]
+ end
+
+ it "binds to INADDR_ANY if the hostname is empty" do
+ @socket.bind("", 0).should == 0
+ port, host = Socket.unpack_sockaddr_in(@socket.getsockname)
+ host.should == "0.0.0.0"
+ port.should == @socket.addr[1]
+ end
+end
+
+describe 'UDPSocket#bind' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @socket = UDPSocket.new(family)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'binds to an address and port' do
+ @socket.bind(ip_address, 0).should == 0
+
+ @socket.local_address.ip_address.should == ip_address
+ @socket.local_address.ip_port.should > 0
+ end
+
+ it 'binds to an address and port using String arguments' do
+ @socket.bind(ip_address, '0').should == 0
+
+ @socket.local_address.ip_address.should == ip_address
+ @socket.local_address.ip_port.should > 0
+ end
+
+ it 'can receive data after being bound to an address' do
+ @socket.bind(ip_address, 0)
+
+ addr = @socket.connect_address
+ client = UDPSocket.new(family)
+
+ client.connect(addr.ip_address, addr.ip_port)
+ client.write('hello')
+
+ begin
+ @socket.recv(6).should == 'hello'
+ ensure
+ client.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/connect_spec.rb b/spec/ruby/library/socket/udpsocket/connect_spec.rb
new file mode 100644
index 0000000000..d92bdeb981
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/connect_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UDPSocket#connect' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @socket = UDPSocket.new(family)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'connects to an address even when it is not used' do
+ @socket.connect(ip_address, 9996).should == 0
+ end
+
+ it 'can send data after connecting' do
+ receiver = UDPSocket.new(family)
+
+ receiver.bind(ip_address, 0)
+
+ addr = receiver.connect_address
+
+ @socket.connect(addr.ip_address, addr.ip_port)
+ @socket.write('hello')
+
+ begin
+ receiver.recv(6).should == 'hello'
+ ensure
+ receiver.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/initialize_spec.rb b/spec/ruby/library/socket/udpsocket/initialize_spec.rb
new file mode 100644
index 0000000000..06f7b5ef1c
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/initialize_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../spec_helper'
+
+describe 'UDPSocket#initialize' do
+ after do
+ @socket.close if @socket
+ end
+
+ it 'initializes a new UDPSocket' do
+ @socket = UDPSocket.new
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'initializes a new UDPSocket using an Integer' do
+ @socket = UDPSocket.new(Socket::AF_INET)
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'initializes a new UDPSocket using a Symbol' do
+ @socket = UDPSocket.new(:INET)
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'initializes a new UDPSocket using a String' do
+ @socket = UDPSocket.new('INET')
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'sets the socket to binmode' do
+ @socket = UDPSocket.new(:INET)
+ @socket.binmode?.should be_true
+ end
+
+ it 'raises Errno::EAFNOSUPPORT when given an invalid address family' do
+ lambda { UDPSocket.new(666) }.should raise_error(Errno::EAFNOSUPPORT)
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/inspect_spec.rb b/spec/ruby/library/socket/udpsocket/inspect_spec.rb
new file mode 100644
index 0000000000..201e8b3fc6
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/inspect_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../spec_helper'
+
+describe 'UDPSocket#inspect' do
+ before do
+ @socket = UDPSocket.new
+ @socket.bind('127.0.0.1', 0)
+ end
+
+ after do
+ @socket.close
+ end
+
+ ruby_version_is ""..."2.5" do
+ it 'returns a String with the fd' do
+ @socket.inspect.should == "#<UDPSocket:fd #{@socket.fileno}>"
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it 'returns a String with the fd, family, address and port' do
+ port = @socket.addr[1]
+ @socket.inspect.should == "#<UDPSocket:fd #{@socket.fileno}, AF_INET, 127.0.0.1, #{port}>"
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/local_address_spec.rb b/spec/ruby/library/socket/udpsocket/local_address_spec.rb
new file mode 100644
index 0000000000..92e4cc10c7
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/local_address_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UDPSocket#local_address' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :DGRAM, Socket::IPPROTO_UDP)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+
+ @host = @server.connect_address.ip_address
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'using an explicit hostname' do
+ before do
+ @sock = UDPSocket.new(family)
+
+ @sock.connect(@host, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct address family' do
+ @sock.local_address.afamily.should == family
+ end
+
+ it 'uses the correct protocol family' do
+ @sock.local_address.pfamily.should == family
+ end
+
+ it 'uses SOCK_DGRAM as the socket type' do
+ @sock.local_address.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses the correct IP address' do
+ @sock.local_address.ip_address.should == @host
+ end
+
+ it 'uses a randomly assigned local port' do
+ @sock.local_address.ip_port.should > 0
+ @sock.local_address.ip_port.should_not == @port
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.local_address.protocol.should == 0
+ end
+ end
+ end
+
+ describe 'using an implicit hostname' do
+ before do
+ @sock = UDPSocket.new(family)
+
+ @sock.connect(nil, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct IP address' do
+ @sock.local_address.ip_address.should == @host
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/new_spec.rb b/spec/ruby/library/socket/udpsocket/new_spec.rb
new file mode 100644
index 0000000000..157ff138be
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/new_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UDPSocket.new' do
+ after :each do
+ @socket.close if @socket && !@socket.closed?
+ end
+
+ it 'without arguments' do
+ @socket = UDPSocket.new
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'using Integer argument' do
+ @socket = UDPSocket.new(Socket::AF_INET)
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'using Symbol argument' do
+ @socket = UDPSocket.new(:INET)
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'using String argument' do
+ @socket = UDPSocket.new('INET')
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'raises Errno::EAFNOSUPPORT or Errno::EPROTONOSUPPORT if unsupported family passed' do
+ lambda { UDPSocket.new(-1) }.should raise_error(SystemCallError) { |e|
+ [Errno::EAFNOSUPPORT, Errno::EPROTONOSUPPORT].should include(e.class)
+ }
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/open_spec.rb b/spec/ruby/library/socket/udpsocket/open_spec.rb
new file mode 100644
index 0000000000..e4dbb2ee2a
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/open_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UDPSocket.open" do
+ after :each do
+ @socket.close if @socket && !@socket.closed?
+ end
+
+ it "allows calls to open without arguments" do
+ @socket = UDPSocket.open
+ @socket.should be_kind_of(UDPSocket)
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb b/spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb
new file mode 100644
index 0000000000..015109a052
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb
@@ -0,0 +1,90 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UDPSocket#recvfrom_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address, family_name|
+ before do
+ @server = UDPSocket.new(family)
+ @client = UDPSocket.new(family)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ describe 'using an unbound socket' do
+ it 'raises IO::WaitReadable' do
+ lambda { @server.recvfrom_nonblock(1) }.should raise_error(IO::WaitReadable)
+ end
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(ip_address, 0)
+
+ addr = @server.connect_address
+
+ @client.connect(addr.ip_address, addr.ip_port)
+ end
+
+ describe 'without any data available' do
+ it 'raises IO::WaitReadable' do
+ lambda { @server.recvfrom_nonblock(1) }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+
+ platform_is(:darwin, :freebsd) { IO.select([@server]) }
+ end
+
+ it 'returns an Array containing the data and an Array' do
+ @server.recvfrom_nonblock(1).should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @server.recvfrom_nonblock(1)
+ end
+
+ it 'contains the data at index 0' do
+ @array[0].should == 'h'
+ end
+
+ it 'contains an Array at index 1' do
+ @array[1].should be_an_instance_of(Array)
+ end
+ end
+
+ describe 'the returned address Array' do
+ before do
+ @addr = @server.recvfrom_nonblock(1)[1]
+ end
+
+ it 'uses the correct address family' do
+ @addr[0].should == family_name
+ end
+
+ it 'uses the port of the client' do
+ @addr[1].should == @client.local_address.ip_port
+ end
+
+ it 'uses the hostname of the client' do
+ @addr[2].should == ip_address
+ end
+
+ it 'uses the IP address of the client' do
+ @addr[3].should == ip_address
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/remote_address_spec.rb b/spec/ruby/library/socket/udpsocket/remote_address_spec.rb
new file mode 100644
index 0000000000..94889ce560
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/remote_address_spec.rb
@@ -0,0 +1,79 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UDPSocket#remote_address' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :DGRAM, Socket::IPPROTO_UDP)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+
+ @host = @server.connect_address.ip_address
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'using an explicit hostname' do
+ before do
+ @sock = UDPSocket.new(family)
+
+ @sock.connect(@host, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.remote_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct address family' do
+ @sock.remote_address.afamily.should == family
+ end
+
+ it 'uses the correct protocol family' do
+ @sock.remote_address.pfamily.should == family
+ end
+
+ it 'uses SOCK_DGRAM as the socket type' do
+ @sock.remote_address.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses the correct IP address' do
+ @sock.remote_address.ip_address.should == @host
+ end
+
+ it 'uses the correct port' do
+ @sock.remote_address.ip_port.should == @port
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.remote_address.protocol.should == 0
+ end
+ end
+ end
+
+ describe 'using an implicit hostname' do
+ before do
+ @sock = UDPSocket.new(family)
+
+ @sock.connect(nil, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct IP address' do
+ @sock.remote_address.ip_address.should == @host
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/send_spec.rb b/spec/ruby/library/socket/udpsocket/send_spec.rb
new file mode 100644
index 0000000000..431129723e
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/send_spec.rb
@@ -0,0 +1,154 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UDPSocket#send" do
+ before :each do
+ @port = nil
+ @server_thread = Thread.new do
+ @server = UDPSocket.open
+ begin
+ @server.bind(nil, 0)
+ @port = @server.addr[1]
+ begin
+ @msg = @server.recvfrom_nonblock(64)
+ rescue IO::WaitReadable
+ IO.select([@server])
+ retry
+ end
+ ensure
+ @server.close if !@server.closed?
+ end
+ end
+ Thread.pass while @server_thread.status and !@port
+ end
+
+ after :each do
+ @server_thread.join
+ end
+
+ it "sends data in ad hoc mode" do
+ @socket = UDPSocket.open
+ @socket.send("ad hoc", 0, SocketSpecs.hostname, @port)
+ @socket.close
+ @server_thread.join
+
+ @msg[0].should == "ad hoc"
+ @msg[1][0].should == "AF_INET"
+ @msg[1][1].should be_kind_of(Integer)
+ @msg[1][3].should == "127.0.0.1"
+ end
+
+ it "sends data in ad hoc mode (with port given as a String)" do
+ @socket = UDPSocket.open
+ @socket.send("ad hoc", 0, SocketSpecs.hostname, @port.to_s)
+ @socket.close
+ @server_thread.join
+
+ @msg[0].should == "ad hoc"
+ @msg[1][0].should == "AF_INET"
+ @msg[1][1].should be_kind_of(Integer)
+ @msg[1][3].should == "127.0.0.1"
+ end
+
+ it "sends data in connection mode" do
+ @socket = UDPSocket.open
+ @socket.connect(SocketSpecs.hostname, @port)
+ @socket.send("connection-based", 0)
+ @socket.close
+ @server_thread.join
+
+ @msg[0].should == "connection-based"
+ @msg[1][0].should == "AF_INET"
+ @msg[1][1].should be_kind_of(Integer)
+ @msg[1][3].should == "127.0.0.1"
+ end
+
+ it "raises EMSGSIZE if data is too too big" do
+ @socket = UDPSocket.open
+ begin
+ lambda do
+ @socket.send('1' * 100_000, 0, SocketSpecs.hostname, @port.to_s)
+ end.should raise_error(Errno::EMSGSIZE)
+ ensure
+ @socket.send("ad hoc", 0, SocketSpecs.hostname, @port)
+ @socket.close
+ @server_thread.join
+ end
+ end
+end
+
+describe 'UDPSocket#send' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = UDPSocket.new(family)
+ @client = UDPSocket.new(family)
+
+ @server.bind(ip_address, 0)
+
+ @addr = @server.connect_address
+ end
+
+ after do
+ @server.close
+ @client.close
+ end
+
+ describe 'using a disconnected socket' do
+ describe 'without a destination address' do
+ it "raises #{SocketSpecs.dest_addr_req_error}" do
+ lambda { @client.send('hello', 0) }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+
+ describe 'with a destination address as separate arguments' do
+ it 'returns the amount of sent bytes' do
+ @client.send('hello', 0, @addr.ip_address, @addr.ip_port).should == 5
+ end
+
+ it 'does not persist the connection after sending data' do
+ @client.send('hello', 0, @addr.ip_address, @addr.ip_port)
+
+ lambda { @client.send('hello', 0) }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+
+ describe 'with a destination address as a single String argument' do
+ it 'returns the amount of sent bytes' do
+ @client.send('hello', 0, @server.getsockname).should == 5
+ end
+ end
+ end
+
+ describe 'using a connected socket' do
+ describe 'without an explicit destination address' do
+ before do
+ @client.connect(@addr.ip_address, @addr.ip_port)
+ end
+
+ it 'returns the amount of bytes written' do
+ @client.send('hello', 0).should == 5
+ end
+ end
+
+ describe 'with an explicit destination address' do
+ before do
+ @alt_server = UDPSocket.new(family)
+
+ @alt_server.bind(ip_address, 0)
+ end
+
+ after do
+ @alt_server.close
+ end
+
+ it 'sends the data to the given address instead' do
+ @client.send('hello', 0, @alt_server.getsockname).should == 5
+
+ lambda { @server.recv(5) }.should block_caller
+
+ @alt_server.recv(5).should == 'hello'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/write_spec.rb b/spec/ruby/library/socket/udpsocket/write_spec.rb
new file mode 100644
index 0000000000..e960de1baf
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/write_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UDPSocket#write" do
+ it "raises EMSGSIZE if msg is too long" do
+ begin
+ host = SocketSpecs.hostname
+ s1 = UDPSocket.new
+ s1.bind(host, 0)
+ s2 = UDPSocket.new
+ s2.connect(host, s1.addr[1])
+
+ lambda do
+ s2.write('1' * 100_000)
+ end.should raise_error(Errno::EMSGSIZE)
+ ensure
+ s1.close if s1 && !s1.closed?
+ s2.close if s2 && !s2.closed?
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb b/spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb
new file mode 100644
index 0000000000..3ebe38a090
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb
@@ -0,0 +1,92 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXServer#accept_nonblock" do
+
+ platform_is_not :windows do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @client = UNIXSocket.open(@path)
+
+ @socket = @server.accept_nonblock
+ @client.send("foobar", 0)
+ end
+
+ after :each do
+ @socket.close
+ @client.close
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "accepts a connection in a non-blocking way" do
+ data = @socket.recvfrom(6).first
+ data.should == "foobar"
+ end
+
+ it "returns a UNIXSocket" do
+ @socket.should be_kind_of(UNIXSocket)
+ end
+
+ it 'returns :wait_readable in exceptionless mode' do
+ @server.accept_nonblock(exception: false).should == :wait_readable
+ end
+ end
+end
+
+with_feature :unix_socket do
+ describe 'UNIXServer#accept_nonblock' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close
+ rm_r(@path)
+ end
+
+ describe 'without a client' do
+ it 'raises IO::WaitReadable' do
+ lambda { @server.accept_nonblock }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ describe 'with a client' do
+ before do
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @socket.close if @socket
+ end
+
+ describe 'without any data' do
+ it 'returns a UNIXSocket' do
+ @socket = @server.accept_nonblock
+ @socket.should be_an_instance_of(UNIXSocket)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ it 'returns a UNIXSocket' do
+ @socket = @server.accept_nonblock
+ @socket.should be_an_instance_of(UNIXSocket)
+ end
+
+ describe 'the returned UNIXSocket' do
+ it 'can read the data written' do
+ @socket = @server.accept_nonblock
+ @socket.recv(5).should == 'hello'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/accept_spec.rb b/spec/ruby/library/socket/unixserver/accept_spec.rb
new file mode 100644
index 0000000000..a1570f7013
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/accept_spec.rb
@@ -0,0 +1,117 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+platform_is_not :windows do
+ describe "UNIXServer#accept" do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ end
+
+ after :each do
+ @server.close if @server
+ SocketSpecs.rm_socket @path
+ end
+
+ it "accepts what is written by the client" do
+ client = UNIXSocket.open(@path)
+
+ client.send('hello', 0)
+
+ sock = @server.accept
+ begin
+ data, info = sock.recvfrom(5)
+
+ data.should == 'hello'
+ info.should_not be_empty
+ ensure
+ sock.close
+ client.close
+ end
+ end
+
+ it "can be interrupted by Thread#kill" do
+ t = Thread.new {
+ @server.accept
+ }
+ Thread.pass while t.status and t.status != "sleep"
+
+ # kill thread, ensure it dies in a reasonable amount of time
+ t.kill
+ a = 0
+ while t.alive? and a < 5000
+ sleep 0.001
+ a += 1
+ end
+ a.should < 5000
+ end
+
+ it "can be interrupted by Thread#raise" do
+ t = Thread.new {
+ -> {
+ @server.accept
+ }.should raise_error(Exception, "interrupted")
+ }
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.raise Exception, "interrupted"
+ t.join
+ end
+ end
+end
+
+with_feature :unix_socket do
+ describe 'UNIXServer#accept' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close
+ rm_r(@path)
+ end
+
+ describe 'without a client' do
+ it 'blocks the calling thread' do
+ lambda { @server.accept }.should block_caller
+ end
+ end
+
+ describe 'with a client' do
+ before do
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @socket.close if @socket
+ end
+
+ describe 'without any data' do
+ it 'returns a UNIXSocket' do
+ @socket = @server.accept
+ @socket.should be_an_instance_of(UNIXSocket)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ it 'returns a UNIXSocket' do
+ @socket = @server.accept
+ @socket.should be_an_instance_of(UNIXSocket)
+ end
+
+ describe 'the returned UNIXSocket' do
+ it 'can read the data written' do
+ @socket = @server.accept
+ @socket.recv(5).should == 'hello'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/for_fd_spec.rb b/spec/ruby/library/socket/unixserver/for_fd_spec.rb
new file mode 100644
index 0000000000..4f3816ad37
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/for_fd_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+platform_is_not :windows do
+ describe "UNIXServer#for_fd" do
+ before :each do
+ @unix_path = SocketSpecs.socket_path
+ @unix = UNIXServer.new(@unix_path)
+ end
+
+ after :each do
+ @unix.close if @unix
+ SocketSpecs.rm_socket @unix_path
+ end
+
+ it "can calculate the path" do
+ b = UNIXServer.for_fd(@unix.fileno)
+ b.autoclose = false
+
+ b.path.should == @unix_path
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/initialize_spec.rb b/spec/ruby/library/socket/unixserver/initialize_spec.rb
new file mode 100644
index 0000000000..cb186ceb76
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/initialize_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'UNIXServer#initialize' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close if @server
+ rm_r @path
+ end
+
+ it 'returns a new UNIXServer' do
+ @server.should be_an_instance_of(UNIXServer)
+ end
+
+ it 'sets the socket to binmode' do
+ @server.binmode?.should be_true
+ end
+
+ it 'raises Errno::EADDRINUSE when the socket is already in use' do
+ lambda { UNIXServer.new(@path) }.should raise_error(Errno::EADDRINUSE)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/listen_spec.rb b/spec/ruby/library/socket/unixserver/listen_spec.rb
new file mode 100644
index 0000000000..b90b3bbb09
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/listen_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'UNIXServer#listen' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close
+
+ rm_r(@path)
+ end
+
+ it 'returns 0' do
+ @server.listen(1).should == 0
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/new_spec.rb b/spec/ruby/library/socket/unixserver/new_spec.rb
new file mode 100644
index 0000000000..f831f40bc6
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/new_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../spec_helper'
+require_relative 'shared/new'
+
+describe "UNIXServer.new" do
+ it_behaves_like :unixserver_new, :new
+end
diff --git a/spec/ruby/library/socket/unixserver/open_spec.rb b/spec/ruby/library/socket/unixserver/open_spec.rb
new file mode 100644
index 0000000000..f2506d9f6f
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/open_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/new'
+
+describe "UNIXServer.open" do
+ it_behaves_like :unixserver_new, :open
+
+ platform_is_not :windows do
+ before :each do
+ @path = SocketSpecs.socket_path
+ end
+
+ after :each do
+ @server.close if @server
+ @server = nil
+ SocketSpecs.rm_socket @path
+ end
+
+ it "yields the new UNIXServer object to the block, if given" do
+ UNIXServer.open(@path) do |unix|
+ unix.path.should == @path
+ unix.addr.should == ["AF_UNIX", @path]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/shared/new.rb b/spec/ruby/library/socket/unixserver/shared/new.rb
new file mode 100644
index 0000000000..35395826c9
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/shared/new.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/classes'
+
+describe :unixserver_new, shared: true do
+ platform_is_not :windows do
+ before :each do
+ @path = SocketSpecs.socket_path
+ end
+
+ after :each do
+ @server.close if @server
+ @server = nil
+ SocketSpecs.rm_socket @path
+ end
+
+ it "creates a new UNIXServer" do
+ @server = UNIXServer.send(@method, @path)
+ @server.path.should == @path
+ @server.addr.should == ["AF_UNIX", @path]
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/sysaccept_spec.rb b/spec/ruby/library/socket/unixserver/sysaccept_spec.rb
new file mode 100644
index 0000000000..65a35c91a9
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/sysaccept_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'UNIXServer#sysaccept' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close
+
+ rm_r(@path)
+ end
+
+ describe 'without a client' do
+ it 'blocks the calling thread' do
+ lambda { @server.sysaccept }.should block_caller
+ end
+ end
+
+ describe 'with a client' do
+ before do
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ Socket.for_fd(@fd).close if @fd
+ @client.close
+ end
+
+ describe 'without any data' do
+ it 'returns an Integer' do
+ @fd = @server.sysaccept
+ @fd.should be_kind_of(Integer)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ it 'returns an Integer' do
+ @fd = @server.sysaccept
+ @fd.should be_kind_of(Integer)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/addr_spec.rb b/spec/ruby/library/socket/unixsocket/addr_spec.rb
new file mode 100644
index 0000000000..e8431bea16
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/addr_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXSocket#addr" do
+
+ platform_is_not :windows do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @client = UNIXSocket.open(@path)
+ end
+
+ after :each do
+ @client.close
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "returns an array" do
+ @client.addr.should be_kind_of(Array)
+ end
+
+ it "returns the address family of this socket in an array" do
+ @client.addr[0].should == "AF_UNIX"
+ @server.addr[0].should == "AF_UNIX"
+ end
+
+ it "returns the path of the socket in an array if it's a server" do
+ @server.addr[1].should == @path
+ end
+
+ it "returns an empty string for path if it's a client" do
+ @client.addr[1].should == ""
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/initialize_spec.rb b/spec/ruby/library/socket/unixsocket/initialize_spec.rb
new file mode 100644
index 0000000000..a6217d404f
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/initialize_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'UNIXSocket#initialize' do
+ describe 'using a non existing path' do
+ it 'raises Errno::ENOENT' do
+ lambda { UNIXSocket.new(SocketSpecs.socket_path) }.should raise_error(Errno::ENOENT)
+ end
+ end
+
+ describe 'using an existing socket path' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @socket = UNIXSocket.new(@path)
+ end
+
+ after do
+ @socket.close
+ @server.close
+ rm_r(@path)
+ end
+
+ it 'returns a new UNIXSocket' do
+ @socket.should be_an_instance_of(UNIXSocket)
+ end
+
+ it 'sets the socket path to an empty String' do
+ @socket.path.should == ''
+ end
+
+ it 'sets the socket to binmode' do
+ @socket.binmode?.should be_true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/inspect_spec.rb b/spec/ruby/library/socket/unixsocket/inspect_spec.rb
new file mode 100644
index 0000000000..d2e3cabbd3
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/inspect_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXSocket#inspect" do
+ platform_is_not :windows do
+ it "returns sockets fd for unnamed sockets" do
+ begin
+ s1, s2 = UNIXSocket.socketpair
+ s1.inspect.should == "#<UNIXSocket:fd #{s1.fileno}>"
+ s2.inspect.should == "#<UNIXSocket:fd #{s2.fileno}>"
+ ensure
+ s1.close
+ s2.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/local_address_spec.rb b/spec/ruby/library/socket/unixsocket/local_address_spec.rb
new file mode 100644
index 0000000000..56b3ccc557
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/local_address_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'UNIXSocket#local_address' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @server.close
+
+ rm_r(@path)
+ end
+
+ it 'returns an Addrinfo' do
+ @client.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ platform_is_not :aix do
+ it 'uses AF_UNIX as the address family' do
+ @client.local_address.afamily.should == Socket::AF_UNIX
+ end
+
+ it 'uses PF_UNIX as the protocol family' do
+ @client.local_address.pfamily.should == Socket::PF_UNIX
+ end
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @client.local_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ platform_is_not :aix do
+ it 'uses an empty socket path' do
+ @client.local_address.unix_path.should == ''
+ end
+ end
+
+ it 'uses 0 as the protocol' do
+ @client.local_address.protocol.should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/new_spec.rb b/spec/ruby/library/socket/unixsocket/new_spec.rb
new file mode 100644
index 0000000000..05a6b3eda2
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/new_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../spec_helper'
+require_relative 'shared/new'
+
+describe "UNIXSocket.new" do
+ it_behaves_like :unixsocket_new, :new
+end
diff --git a/spec/ruby/library/socket/unixsocket/open_spec.rb b/spec/ruby/library/socket/unixsocket/open_spec.rb
new file mode 100644
index 0000000000..ad5048fedd
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/open_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/new'
+
+describe "UNIXSocket.open" do
+ it_behaves_like :unixsocket_new, :open
+end
+
+describe "UNIXSocket.open" do
+ platform_is_not :windows do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ end
+
+ after :each do
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "opens a unix socket on the specified file and yields it to the block" do
+ UNIXSocket.open(@path) do |client|
+ client.addr[0].should == "AF_UNIX"
+ client.closed?.should == false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/pair_spec.rb b/spec/ruby/library/socket/unixsocket/pair_spec.rb
new file mode 100644
index 0000000000..845ff76ecc
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/pair_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/partially_closable_sockets'
+
+describe "UNIXSocket#pair" do
+ platform_is_not :windows do
+
+ it_should_behave_like "partially closable sockets"
+
+ before :each do
+ @s1, @s2 = UNIXSocket.pair
+ end
+
+ after :each do
+ @s1.close
+ @s2.close
+ end
+
+ it "returns a pair of connected sockets" do
+ @s1.puts "foo"
+ @s2.gets.should == "foo\n"
+ end
+
+ it "returns sockets with no name" do
+ @s1.path.should == @s2.path
+ @s1.path.should == ""
+ end
+
+ it "returns sockets with no address" do
+ @s1.addr.should == ["AF_UNIX", ""]
+ @s2.addr.should == ["AF_UNIX", ""]
+ end
+
+ it "returns sockets with no peeraddr" do
+ @s1.peeraddr.should == ["AF_UNIX", ""]
+ @s2.peeraddr.should == ["AF_UNIX", ""]
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/partially_closable_spec.rb b/spec/ruby/library/socket/unixsocket/partially_closable_spec.rb
new file mode 100644
index 0000000000..78a64fe6be
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/partially_closable_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/partially_closable_sockets'
+
+platform_is_not :windows do
+ describe "UNIXSocket partial closability" do
+
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @s1 = UNIXSocket.new(@path)
+ @s2 = @server.accept
+ end
+
+ after :each do
+ @server.close
+ @s1.close
+ @s2.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it_should_behave_like "partially closable sockets"
+
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/path_spec.rb b/spec/ruby/library/socket/unixsocket/path_spec.rb
new file mode 100644
index 0000000000..317ffc0975
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/path_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXSocket#path" do
+
+ platform_is_not :windows do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @client = UNIXSocket.open(@path)
+ end
+
+ after :each do
+ @client.close
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "returns the path of the socket if it's a server" do
+ @server.path.should == @path
+ end
+
+ it "returns an empty string for path if it's a client" do
+ @client.path.should == ""
+ end
+ end
+
+end
diff --git a/spec/ruby/library/socket/unixsocket/peeraddr_spec.rb b/spec/ruby/library/socket/unixsocket/peeraddr_spec.rb
new file mode 100644
index 0000000000..ec57b40970
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/peeraddr_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXSocket#peeraddr" do
+
+ platform_is_not :windows do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @client = UNIXSocket.open(@path)
+ end
+
+ after :each do
+ @client.close
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "returns the address familly and path of the server end of the connection" do
+ @client.peeraddr.should == ["AF_UNIX", @path]
+ end
+
+ it "raises an error in server sockets" do
+ lambda {
+ @server.peeraddr
+ }.should raise_error(Errno::ENOTCONN)
+ end
+ end
+
+end
diff --git a/spec/ruby/library/socket/unixsocket/recv_io_spec.rb b/spec/ruby/library/socket/unixsocket/recv_io_spec.rb
new file mode 100644
index 0000000000..533f02a0fa
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/recv_io_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXSocket#recv_io" do
+
+ platform_is_not :windows do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @client = UNIXSocket.open(@path)
+
+ @send_io_path = File.expand_path('../../fixtures/send_io.txt', __FILE__)
+ @file = File.open(@send_io_path)
+ end
+
+ after :each do
+ @io.close if @io
+ @socket.close if @socket
+
+ @file.close
+ @client.close
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "reads an IO object across the socket" do
+ @client.send_io(@file)
+
+ @socket = @server.accept
+ @io = @socket.recv_io
+
+ @io.read.should == File.read(@send_io_path)
+ end
+
+ it "takes an optional class to use" do
+ @client.send_io(@file)
+
+ @socket = @server.accept
+ @io = @socket.recv_io(File)
+
+ @io.should be_an_instance_of(File)
+ end
+ end
+end
+
+with_feature :unix_socket do
+ describe 'UNIXSocket#recv_io' do
+ before do
+ @file = File.open('/dev/null', 'w')
+ @client, @server = UNIXSocket.socketpair
+ end
+
+ after do
+ @client.close
+ @server.close
+ @io.close if @io
+ @file.close
+ end
+
+ describe 'without a custom class' do
+ it 'returns an IO' do
+ @client.send_io(@file)
+
+ @io = @server.recv_io
+ @io.should be_an_instance_of(IO)
+ end
+ end
+
+ describe 'with a custom class' do
+ it 'returns an instance of the custom class' do
+ @client.send_io(@file)
+
+ @io = @server.recv_io(File)
+ @io.should be_an_instance_of(File)
+ end
+ end
+
+ describe 'with a custom mode' do
+ it 'opens the IO using the given mode' do
+ @client.send_io(@file)
+
+ @io = @server.recv_io(File, File::WRONLY)
+ @io.should be_an_instance_of(File)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb b/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb
new file mode 100644
index 0000000000..3e2eb69885
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb
@@ -0,0 +1,98 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXSocket#recvfrom" do
+ platform_is_not :windows do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @client = UNIXSocket.open(@path)
+ end
+
+ after :each do
+ @client.close
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "receives len bytes from sock" do
+ @client.send("foobar", 0)
+ sock = @server.accept
+ sock.recvfrom(6).first.should == "foobar"
+ sock.close
+ end
+
+ it "returns an array with data and information on the sender" do
+ @client.send("foobar", 0)
+ sock = @server.accept
+ data = sock.recvfrom(6)
+ data.first.should == "foobar"
+ data.last.should == ["AF_UNIX", ""]
+ sock.close
+ end
+
+ it "uses different message options" do
+ @client.send("foobar", Socket::MSG_PEEK)
+ sock = @server.accept
+ peek_data = sock.recvfrom(6, Socket::MSG_PEEK) # Does not retrieve the message
+ real_data = sock.recvfrom(6)
+
+ real_data.should == peek_data
+ peek_data.should == ["foobar", ["AF_UNIX", ""]]
+ sock.close
+ end
+ end
+end
+
+
+with_feature :unix_socket do
+ describe 'UNIXSocket#recvfrom' do
+ describe 'using a socket pair' do
+ before do
+ @client, @server = UNIXSocket.socketpair
+ @client.write('hello')
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns an Array containing the data and address information' do
+ @server.recvfrom(5).should == ['hello', ['AF_UNIX', '']]
+ end
+ end
+
+ # These specs are taken from the rdoc examples on UNIXSocket#recvfrom.
+ describe 'using a UNIX socket constructed using UNIXSocket.for_fd' do
+ before do
+ @path1 = SocketSpecs.socket_path
+ @path2 = SocketSpecs.socket_path + '2'
+ rm_r(@path2)
+
+ @client_raw = Socket.new(:UNIX, :DGRAM)
+ @client_raw.bind(Socket.sockaddr_un(@path1))
+
+ @server_raw = Socket.new(:UNIX, :DGRAM)
+ @server_raw.bind(Socket.sockaddr_un(@path2))
+
+ @socket = UNIXSocket.for_fd(@server_raw.fileno)
+ @socket.autoclose = false
+ end
+
+ after do
+ @client_raw.close
+ @server_raw.close # also closes @socket
+
+ rm_r @path1
+ rm_r @path2
+ end
+
+ it 'returns an Array containing the data and address information' do
+ @client_raw.send('hello', 0, Socket.sockaddr_un(@path2))
+
+ @socket.recvfrom(5).should == ['hello', ['AF_UNIX', @path1]]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/remote_address_spec.rb b/spec/ruby/library/socket/unixsocket/remote_address_spec.rb
new file mode 100644
index 0000000000..0b416254d0
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/remote_address_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'UNIXSocket#remote_address' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @server.close
+
+ rm_r(@path)
+ end
+
+ it 'returns an Addrinfo' do
+ @client.remote_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_UNIX as the address family' do
+ @client.remote_address.afamily.should == Socket::AF_UNIX
+ end
+
+ it 'uses PF_UNIX as the protocol family' do
+ @client.remote_address.pfamily.should == Socket::PF_UNIX
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @client.remote_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses the correct socket path' do
+ @client.remote_address.unix_path.should == @path
+ end
+
+ it 'uses 0 as the protocol' do
+ @client.remote_address.protocol.should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/send_io_spec.rb b/spec/ruby/library/socket/unixsocket/send_io_spec.rb
new file mode 100644
index 0000000000..a2a7d26539
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/send_io_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXSocket#send_io" do
+
+ platform_is_not :windows do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @client = UNIXSocket.open(@path)
+
+ @send_io_path = File.expand_path('../../fixtures/send_io.txt', __FILE__)
+ @file = File.open(@send_io_path)
+ end
+
+ after :each do
+ @io.close if @io
+ @socket.close if @socket
+
+ @file.close
+ @client.close
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "sends the fd for an IO object across the socket" do
+ @client.send_io(@file)
+
+ @socket = @server.accept
+ @io = @socket.recv_io
+
+ @io.read.should == File.read(@send_io_path)
+ end
+ end
+end
+
+with_feature :unix_socket do
+ describe 'UNIXSocket#send_io' do
+ before do
+ @file = File.open('/dev/null', 'w')
+ @client, @server = UNIXSocket.socketpair
+ end
+
+ after do
+ @client.close
+ @server.close
+ @io.close if @io
+ @file.close
+ end
+
+ it 'sends an IO object' do
+ @client.send_io(@file)
+
+ @io = @server.recv_io
+ @io.should be_an_instance_of(IO)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/shared/new.rb b/spec/ruby/library/socket/unixsocket/shared/new.rb
new file mode 100644
index 0000000000..76a4e1701e
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/shared/new.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/classes'
+
+describe :unixsocket_new, shared: true do
+ platform_is_not :windows do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ end
+
+ after :each do
+ @client.close if @client
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "opens a unix socket on the specified file" do
+ @client = UNIXSocket.send(@method, @path)
+
+ @client.addr[0].should == "AF_UNIX"
+ @client.closed?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/socketpair_spec.rb b/spec/ruby/library/socket/unixsocket/socketpair_spec.rb
new file mode 100644
index 0000000000..3e9646f76b
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/socketpair_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../spec_helper'
+
+with_feature :unix_socket do
+ describe 'UNIXSocket.socketpair' do
+ before do
+ @s1, @s2 = UNIXSocket.socketpair
+ end
+
+ after do
+ @s1.close
+ @s2.close
+ end
+
+ it 'returns two UNIXSockets' do
+ @s1.should be_an_instance_of(UNIXSocket)
+ @s2.should be_an_instance_of(UNIXSocket)
+ end
+
+ it 'connects the sockets to each other' do
+ @s1.write('hello')
+
+ @s2.recv(5).should == 'hello'
+ end
+
+ it 'sets the socket paths to empty Strings' do
+ @s1.path.should == ''
+ @s2.path.should == ''
+ end
+
+ it 'sets the socket addresses to empty Strings' do
+ @s1.addr.should == ['AF_UNIX', '']
+ @s2.addr.should == ['AF_UNIX', '']
+ end
+
+ it 'sets the socket peer addresses to empty Strings' do
+ @s1.peeraddr.should == ['AF_UNIX', '']
+ @s2.peeraddr.should == ['AF_UNIX', '']
+ end
+ end
+end
diff --git a/spec/ruby/library/stringio/append_spec.rb b/spec/ruby/library/stringio/append_spec.rb
new file mode 100644
index 0000000000..08500bc520
--- /dev/null
+++ b/spec/ruby/library/stringio/append_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#<< when passed [Object]" do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "returns self" do
+ (@io << "just testing").should equal(@io)
+ end
+
+ it "writes the passed argument onto self" do
+ (@io << "just testing")
+ @io.string.should == "just testing"
+ (@io << " and more testing")
+ @io.string.should == "just testing and more testing"
+ end
+
+ it "writes the passed argument at the current position" do
+ @io.pos = 5
+ @io << "<test>"
+ @io.string.should == "examp<test>"
+ end
+
+ it "pads self with \\000 when the current position is after the end" do
+ @io.pos = 15
+ @io << "just testing"
+ @io.string.should == "example\000\000\000\000\000\000\000\000just testing"
+ end
+
+ it "taints self's String when the passed argument is tainted" do
+ (@io << "test".taint)
+ @io.string.tainted?.should be_true
+ end
+
+ it "does not taint self when the passed argument is tainted" do
+ (@io << "test".taint)
+ @io.tainted?.should be_false
+ end
+
+ it "updates self's position" do
+ @io << "test"
+ @io.pos.should eql(4)
+ end
+
+ it "tries to convert the passed argument to a String using #to_s" do
+ obj = mock("to_s")
+ obj.should_receive(:to_s).and_return("Test")
+
+ (@io << obj).string.should == "Testple"
+ end
+end
+
+describe "StringIO#<< when self is not writable" do
+ it "raises an IOError" do
+ io = StringIO.new("test", "r")
+ lambda { io << "test" }.should raise_error(IOError)
+
+ io = StringIO.new("test")
+ io.close_write
+ lambda { io << "test" }.should raise_error(IOError)
+ end
+end
+
+describe "StringIO#<< when in append mode" do
+ before :each do
+ @io = StringIO.new("example", "a")
+ end
+
+ it "appends the passed argument to the end of self, ignoring current position" do
+ (@io << ", just testing")
+ @io.string.should == "example, just testing"
+
+ @io.pos = 3
+ (@io << " and more testing")
+ @io.string.should == "example, just testing and more testing"
+ end
+
+ it "correctly updates self's position" do
+ @io << ", testing"
+ @io.pos.should eql(16)
+ end
+end
diff --git a/spec/ruby/library/stringio/binmode_spec.rb b/spec/ruby/library/stringio/binmode_spec.rb
new file mode 100644
index 0000000000..5d9a8c41df
--- /dev/null
+++ b/spec/ruby/library/stringio/binmode_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#binmode" do
+ it "returns self" do
+ io = StringIO.new("example")
+ io.binmode.should equal(io)
+ end
+end
diff --git a/spec/ruby/library/stringio/bytes_spec.rb b/spec/ruby/library/stringio/bytes_spec.rb
new file mode 100644
index 0000000000..692fba997f
--- /dev/null
+++ b/spec/ruby/library/stringio/bytes_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require 'stringio'
+require_relative 'shared/each_byte'
+
+describe "StringIO#bytes" do
+ it_behaves_like :stringio_each_byte, :bytes
+end
+
+describe "StringIO#bytes when self is not readable" do
+ it_behaves_like :stringio_each_byte_not_readable, :bytes
+end
diff --git a/spec/ruby/library/stringio/chars_spec.rb b/spec/ruby/library/stringio/chars_spec.rb
new file mode 100644
index 0000000000..7dc55d4b37
--- /dev/null
+++ b/spec/ruby/library/stringio/chars_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require 'stringio'
+require_relative 'shared/each_char'
+
+describe "StringIO#chars" do
+ it_behaves_like :stringio_each_char, :chars
+end
+
+describe "StringIO#chars when self is not readable" do
+ it_behaves_like :stringio_each_char_not_readable, :chars
+end
diff --git a/spec/ruby/library/stringio/close_read_spec.rb b/spec/ruby/library/stringio/close_read_spec.rb
new file mode 100644
index 0000000000..05d6c9f7d2
--- /dev/null
+++ b/spec/ruby/library/stringio/close_read_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#close_read" do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "returns nil" do
+ @io.close_read.should be_nil
+ end
+
+ it "prevents further reading" do
+ @io.close_read
+ lambda { @io.read(1) }.should raise_error(IOError)
+ end
+
+ it "allows further writing" do
+ @io.close_read
+ @io.write("x").should == 1
+ end
+
+ it "raises an IOError when in write-only mode" do
+ io = StringIO.new("example", "w")
+ lambda { io.close_read }.should raise_error(IOError)
+
+ io = StringIO.new("example")
+ io.close_read
+ io.close_read.should == nil
+ end
+end
diff --git a/spec/ruby/library/stringio/close_spec.rb b/spec/ruby/library/stringio/close_spec.rb
new file mode 100644
index 0000000000..a5a931aff1
--- /dev/null
+++ b/spec/ruby/library/stringio/close_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#close" do
+ before :each do
+ @io = StringIOSpecs.build
+ end
+
+ it "returns nil" do
+ @io.close.should be_nil
+ end
+
+ it "prevents further reading and/or writing" do
+ @io.close
+ lambda { @io.read(1) }.should raise_error(IOError)
+ lambda { @io.write('x') }.should raise_error(IOError)
+ end
+
+ it "does not raise anything when self was already closed" do
+ @io.close
+ lambda { @io.close }.should_not raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/library/stringio/close_write_spec.rb b/spec/ruby/library/stringio/close_write_spec.rb
new file mode 100644
index 0000000000..8a7ac12581
--- /dev/null
+++ b/spec/ruby/library/stringio/close_write_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#close_write" do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "returns nil" do
+ @io.close_write.should be_nil
+ end
+
+ it "prevents further writing" do
+ @io.close_write
+ lambda { @io.write('x') }.should raise_error(IOError)
+ end
+
+ it "allows further reading" do
+ @io.close_write
+ @io.read(1).should == 'e'
+ end
+
+ it "raises an IOError when in read-only mode" do
+ io = StringIO.new("example", "r")
+ lambda { io.close_write }.should raise_error(IOError)
+
+ io = StringIO.new("example")
+ io.close_write
+ io.close_write.should == nil
+ end
+end
diff --git a/spec/ruby/library/stringio/closed_read_spec.rb b/spec/ruby/library/stringio/closed_read_spec.rb
new file mode 100644
index 0000000000..cb4267ac98
--- /dev/null
+++ b/spec/ruby/library/stringio/closed_read_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#closed_read?" do
+ it "returns true if self is not readable" do
+ io = StringIO.new("example", "r+")
+ io.close_write
+ io.closed_read?.should be_false
+ io.close_read
+ io.closed_read?.should be_true
+ end
+end
diff --git a/spec/ruby/library/stringio/closed_spec.rb b/spec/ruby/library/stringio/closed_spec.rb
new file mode 100644
index 0000000000..ca8a2232a8
--- /dev/null
+++ b/spec/ruby/library/stringio/closed_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#closed?" do
+ it "returns true if self is completely closed" do
+ io = StringIO.new("example", "r+")
+ io.close_read
+ io.closed?.should be_false
+ io.close_write
+ io.closed?.should be_true
+
+ io = StringIO.new("example", "r+")
+ io.close
+ io.closed?.should be_true
+ end
+end
diff --git a/spec/ruby/library/stringio/closed_write_spec.rb b/spec/ruby/library/stringio/closed_write_spec.rb
new file mode 100644
index 0000000000..5c111affd8
--- /dev/null
+++ b/spec/ruby/library/stringio/closed_write_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#closed_write?" do
+ it "returns true if self is not writable" do
+ io = StringIO.new("example", "r+")
+ io.close_read
+ io.closed_write?.should be_false
+ io.close_write
+ io.closed_write?.should be_true
+ end
+end
diff --git a/spec/ruby/library/stringio/codepoints_spec.rb b/spec/ruby/library/stringio/codepoints_spec.rb
new file mode 100644
index 0000000000..cc2e5d1b5d
--- /dev/null
+++ b/spec/ruby/library/stringio/codepoints_spec.rb
@@ -0,0 +1,9 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/codepoints'
+
+# See redmine #1667
+describe "StringIO#codepoints" do
+ it_behaves_like :stringio_codepoints, :codepoints
+end
diff --git a/spec/ruby/library/stringio/each_byte_spec.rb b/spec/ruby/library/stringio/each_byte_spec.rb
new file mode 100644
index 0000000000..6f82a32441
--- /dev/null
+++ b/spec/ruby/library/stringio/each_byte_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require 'stringio'
+require_relative 'shared/each_byte'
+
+describe "StringIO#each_byte" do
+ it_behaves_like :stringio_each_byte, :each_byte
+end
+
+describe "StringIO#each_byte when self is not readable" do
+ it_behaves_like :stringio_each_byte_not_readable, :each_byte
+end
diff --git a/spec/ruby/library/stringio/each_char_spec.rb b/spec/ruby/library/stringio/each_char_spec.rb
new file mode 100644
index 0000000000..7305162ee6
--- /dev/null
+++ b/spec/ruby/library/stringio/each_char_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require 'stringio'
+require_relative 'shared/each_char'
+
+describe "StringIO#each_char" do
+ it_behaves_like :stringio_each_char, :each_char
+end
+
+describe "StringIO#each_char when self is not readable" do
+ it_behaves_like :stringio_each_char_not_readable, :chars
+end
diff --git a/spec/ruby/library/stringio/each_codepoint_spec.rb b/spec/ruby/library/stringio/each_codepoint_spec.rb
new file mode 100644
index 0000000000..85aa34d9d8
--- /dev/null
+++ b/spec/ruby/library/stringio/each_codepoint_spec.rb
@@ -0,0 +1,9 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/codepoints'
+
+# See redmine #1667
+describe "StringIO#each_codepoint" do
+ it_behaves_like :stringio_codepoints, :codepoints
+end
diff --git a/spec/ruby/library/stringio/each_line_spec.rb b/spec/ruby/library/stringio/each_line_spec.rb
new file mode 100644
index 0000000000..f6eae02690
--- /dev/null
+++ b/spec/ruby/library/stringio/each_line_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/each'
+
+describe "StringIO#each_line when passed a separator" do
+ it_behaves_like :stringio_each_separator, :each_line
+end
+
+describe "StringIO#each_line when passed no arguments" do
+ it_behaves_like :stringio_each_no_arguments, :each_line
+end
+
+describe "StringIO#each_line when self is not readable" do
+ it_behaves_like :stringio_each_not_readable, :each_line
+end
+
+ruby_version_is "2.4" do
+ describe "StringIO#each_line when passed chomp" do
+ it_behaves_like :stringio_each_chomp, :each_line
+ end
+end
diff --git a/spec/ruby/library/stringio/each_spec.rb b/spec/ruby/library/stringio/each_spec.rb
new file mode 100644
index 0000000000..4ecaba3dad
--- /dev/null
+++ b/spec/ruby/library/stringio/each_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/each'
+
+describe "StringIO#each when passed a separator" do
+ it_behaves_like :stringio_each_separator, :each
+end
+
+describe "StringIO#each when passed no arguments" do
+ it_behaves_like :stringio_each_no_arguments, :each
+end
+
+describe "StringIO#each when self is not readable" do
+ it_behaves_like :stringio_each_not_readable, :each
+end
+
+ruby_version_is "2.4" do
+ describe "StringIO#each when passed chomp" do
+ it_behaves_like :stringio_each_chomp, :each
+ end
+end
diff --git a/spec/ruby/library/stringio/eof_spec.rb b/spec/ruby/library/stringio/eof_spec.rb
new file mode 100644
index 0000000000..af0170977c
--- /dev/null
+++ b/spec/ruby/library/stringio/eof_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/eof'
+
+describe "StringIO#eof?" do
+ it_behaves_like :stringio_eof, :eof?
+end
+
+describe "StringIO#eof" do
+ it_behaves_like :stringio_eof, :eof
+end
diff --git a/spec/ruby/library/stringio/external_encoding_spec.rb b/spec/ruby/library/stringio/external_encoding_spec.rb
new file mode 100644
index 0000000000..6c5edb1713
--- /dev/null
+++ b/spec/ruby/library/stringio/external_encoding_spec.rb
@@ -0,0 +1,25 @@
+require 'stringio'
+require_relative '../../spec_helper'
+
+describe "StringIO#external_encoding" do
+ it "gets the encoding of the underlying String" do
+ io = StringIO.new
+ io.set_encoding Encoding::EUC_JP
+ io.external_encoding.should == Encoding::EUC_JP
+ end
+
+ it "changes to match string if string's encoding is changed" do
+ io = StringIO.new
+ io.string.force_encoding(Encoding::EUC_JP)
+ io.external_encoding.should == Encoding::EUC_JP
+ end
+
+ it "does not set the encoding of its buffer string if the string is frozen" do
+ str = "foo".freeze
+ enc = str.encoding
+ io = StringIO.new(str)
+ io.set_encoding Encoding::EUC_JP
+ io.external_encoding.should == Encoding::EUC_JP
+ str.encoding.should == enc
+ end
+end
diff --git a/spec/ruby/library/stringio/fcntl_spec.rb b/spec/ruby/library/stringio/fcntl_spec.rb
new file mode 100644
index 0000000000..e4133c0d06
--- /dev/null
+++ b/spec/ruby/library/stringio/fcntl_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#fcntl" do
+ it "raises a NotImplementedError" do
+ lambda { StringIO.new("boom").fcntl }.should raise_error(NotImplementedError)
+ end
+end
diff --git a/spec/ruby/library/stringio/fileno_spec.rb b/spec/ruby/library/stringio/fileno_spec.rb
new file mode 100644
index 0000000000..eea03a5af3
--- /dev/null
+++ b/spec/ruby/library/stringio/fileno_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/each'
+
+describe "StringIO#fileno" do
+ it "returns nil" do
+ StringIO.new("nuffin").fileno.should be_nil
+ end
+end
diff --git a/spec/ruby/library/stringio/fixtures/classes.rb b/spec/ruby/library/stringio/fixtures/classes.rb
new file mode 100644
index 0000000000..bb8dc354cc
--- /dev/null
+++ b/spec/ruby/library/stringio/fixtures/classes.rb
@@ -0,0 +1,15 @@
+require 'stringio'
+
+class StringSubclass < String; end
+
+module StringIOSpecs
+ def self.build
+ str = <<-EOS
+ each
+ peach
+ pear
+ plum
+ EOS
+ StringIO.new(str)
+ end
+end
diff --git a/spec/ruby/library/stringio/flush_spec.rb b/spec/ruby/library/stringio/flush_spec.rb
new file mode 100644
index 0000000000..17a16dfdd5
--- /dev/null
+++ b/spec/ruby/library/stringio/flush_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#flush" do
+ it "returns self" do
+ io = StringIO.new("flush")
+ io.flush.should equal(io)
+ end
+end
diff --git a/spec/ruby/library/stringio/fsync_spec.rb b/spec/ruby/library/stringio/fsync_spec.rb
new file mode 100644
index 0000000000..8fb2b59a24
--- /dev/null
+++ b/spec/ruby/library/stringio/fsync_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#fsync" do
+ it "returns zero" do
+ io = StringIO.new("fsync")
+ io.fsync.should eql(0)
+ end
+end
diff --git a/spec/ruby/library/stringio/getbyte_spec.rb b/spec/ruby/library/stringio/getbyte_spec.rb
new file mode 100644
index 0000000000..3daa3d8e02
--- /dev/null
+++ b/spec/ruby/library/stringio/getbyte_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require 'stringio'
+require_relative 'shared/getc'
+
+describe "StringIO#getbyte" do
+ it_behaves_like :stringio_getc, :getbyte
+
+ it "returns the 8-bit byte at the current position" do
+ io = StringIO.new("example")
+
+ io.getbyte.should == 101
+ io.getbyte.should == 120
+ io.getbyte.should == 97
+ end
+end
+
+describe "StringIO#getbyte when self is not readable" do
+ it_behaves_like :stringio_getc_not_readable, :getbyte
+end
diff --git a/spec/ruby/library/stringio/getc_spec.rb b/spec/ruby/library/stringio/getc_spec.rb
new file mode 100644
index 0000000000..263d418316
--- /dev/null
+++ b/spec/ruby/library/stringio/getc_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require 'stringio'
+require_relative 'shared/getc'
+
+describe "StringIO#getc" do
+ it_behaves_like :stringio_getc, :getc
+
+ it "returns the character at the current position" do
+ io = StringIO.new("example")
+
+ io.getc.should == ?e
+ io.getc.should == ?x
+ io.getc.should == ?a
+ end
+end
+
+describe "StringIO#getc when self is not readable" do
+ it_behaves_like :stringio_getc_not_readable, :getc
+end
diff --git a/spec/ruby/library/stringio/getch_spec.rb b/spec/ruby/library/stringio/getch_spec.rb
new file mode 100644
index 0000000000..06670a178c
--- /dev/null
+++ b/spec/ruby/library/stringio/getch_spec.rb
@@ -0,0 +1,46 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require 'stringio'
+require_relative 'shared/getc'
+
+# This method is added by io/console on require.
+describe "StringIO#getch" do
+ require 'io/console'
+
+ it_behaves_like :stringio_getc, :getch
+
+ it "returns the character at the current position" do
+ io = StringIO.new("example")
+
+ io.getch.should == ?e
+ io.getch.should == ?x
+ io.getch.should == ?a
+ end
+
+ with_feature :encoding do
+ it "increments #pos by the byte size of the character in multibyte strings" do
+ io = StringIO.new("föóbar")
+
+ io.getch; io.pos.should == 1 # "f" has byte size 1
+ io.getch; io.pos.should == 3 # "ö" has byte size 2
+ io.getch; io.pos.should == 5 # "ó" has byte size 2
+ io.getch; io.pos.should == 6 # "b" has byte size 1
+ end
+ end
+
+ it "returns nil at the end of the string" do
+ # empty string case
+ io = StringIO.new("")
+ io.getch.should == nil
+ io.getch.should == nil
+
+ # non-empty string case
+ io = StringIO.new("a")
+ io.getch # skip one
+ io.getch.should == nil
+ end
+
+ describe "StringIO#getch when self is not readable" do
+ it_behaves_like :stringio_getc_not_readable, :getch
+ end
+end
diff --git a/spec/ruby/library/stringio/gets_spec.rb b/spec/ruby/library/stringio/gets_spec.rb
new file mode 100644
index 0000000000..7fe00d8d19
--- /dev/null
+++ b/spec/ruby/library/stringio/gets_spec.rb
@@ -0,0 +1,247 @@
+require_relative '../../spec_helper'
+require "stringio"
+
+describe "StringIO#gets when passed [separator]" do
+ before :each do
+ @io = StringIO.new("this>is>an>example")
+ end
+
+ it "returns the data read till the next occurrence of the passed separator" do
+ @io.gets(">").should == "this>"
+ @io.gets(">").should == "is>"
+ @io.gets(">").should == "an>"
+ @io.gets(">").should == "example"
+ end
+
+ it "sets $_ to the read content" do
+ @io.gets(">")
+ $_.should == "this>"
+ @io.gets(">")
+ $_.should == "is>"
+ @io.gets(">")
+ $_.should == "an>"
+ @io.gets(">")
+ $_.should == "example"
+ @io.gets(">")
+ $_.should be_nil
+ end
+
+ it "accepts string as separator" do
+ @io.gets("is>")
+ $_.should == "this>"
+ @io.gets("an>")
+ $_.should == "is>an>"
+ @io.gets("example")
+ $_.should == "example"
+ @io.gets("ple")
+ $_.should be_nil
+ end
+
+ it "updates self's lineno by one" do
+ @io.gets(">")
+ @io.lineno.should eql(1)
+
+ @io.gets(">")
+ @io.lineno.should eql(2)
+
+ @io.gets(">")
+ @io.lineno.should eql(3)
+ end
+
+ it "returns the next paragraph when the passed separator is an empty String" do
+ io = StringIO.new("this is\n\nan example")
+ io.gets("").should == "this is\n\n"
+ io.gets("").should == "an example"
+ end
+
+ it "returns the remaining content starting at the current position when passed nil" do
+ io = StringIO.new("this is\n\nan example")
+ io.pos = 5
+ io.gets(nil).should == "is\n\nan example"
+ end
+
+ it "tries to convert the passed separator to a String using #to_str" do
+ obj = mock('to_str')
+ obj.should_receive(:to_str).and_return(">")
+ @io.gets(obj).should == "this>"
+ end
+end
+
+describe "StringIO#gets when passed no argument" do
+ before :each do
+ @io = StringIO.new("this is\nan example\nfor StringIO#gets")
+ end
+
+ it "returns the data read till the next occurrence of $/ or till eof" do
+ @io.gets.should == "this is\n"
+
+ begin
+ old_sep, $/ = $/, " "
+ @io.gets.should == "an "
+ @io.gets.should == "example\nfor "
+ @io.gets.should == "StringIO#gets"
+ ensure
+ $/ = old_sep
+ end
+ end
+
+ it "sets $_ to the read content" do
+ @io.gets
+ $_.should == "this is\n"
+ @io.gets
+ $_.should == "an example\n"
+ @io.gets
+ $_.should == "for StringIO#gets"
+ @io.gets
+ $_.should be_nil
+ end
+
+ it "updates self's position" do
+ @io.gets
+ @io.pos.should eql(8)
+
+ @io.gets
+ @io.pos.should eql(19)
+
+ @io.gets
+ @io.pos.should eql(36)
+ end
+
+ it "updates self's lineno" do
+ @io.gets
+ @io.lineno.should eql(1)
+
+ @io.gets
+ @io.lineno.should eql(2)
+
+ @io.gets
+ @io.lineno.should eql(3)
+ end
+
+ it "returns nil if self is at the end" do
+ @io.pos = 36
+ @io.gets.should be_nil
+ @io.gets.should be_nil
+ end
+end
+
+describe "StringIO#gets when passed [limit]" do
+ before :each do
+ @io = StringIO.new("this>is>an>example")
+ end
+
+ it "returns the data read until the limit is met" do
+ @io.gets(4).should == "this"
+ @io.gets(3).should == ">is"
+ @io.gets(5).should == ">an>e"
+ @io.gets(6).should == "xample"
+ end
+
+ it "sets $_ to the read content" do
+ @io.gets(4)
+ $_.should == "this"
+ @io.gets(3)
+ $_.should == ">is"
+ @io.gets(5)
+ $_.should == ">an>e"
+ @io.gets(6)
+ $_.should == "xample"
+ @io.gets(3)
+ $_.should be_nil
+ end
+
+ it "updates self's lineno by one" do
+ @io.gets(3)
+ @io.lineno.should eql(1)
+
+ @io.gets(3)
+ @io.lineno.should eql(2)
+
+ @io.gets(3)
+ @io.lineno.should eql(3)
+ end
+
+ it "tries to convert the passed limit to an Integer using #to_int" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(4)
+ @io.gets(obj).should == "this"
+ end
+
+ it "returns a blank string when passed a limit of 0" do
+ @io.gets(0).should == ""
+ end
+end
+
+describe "StringIO#gets when passed [separator] and [limit]" do
+ before :each do
+ @io = StringIO.new("this>is>an>example")
+ end
+
+ it "returns the data read until the limit is consumed or the separator is met" do
+ @io.gets('>', 8).should == "this>"
+ @io.gets('>', 2).should == "is"
+ @io.gets('>', 10).should == ">"
+ @io.gets('>', 6).should == "an>"
+ @io.gets('>', 5).should == "examp"
+ end
+
+ it "sets $_ to the read content" do
+ @io.gets('>', 8)
+ $_.should == "this>"
+ @io.gets('>', 2)
+ $_.should == "is"
+ @io.gets('>', 10)
+ $_.should == ">"
+ @io.gets('>', 6)
+ $_.should == "an>"
+ @io.gets('>', 5)
+ $_.should == "examp"
+ end
+
+ it "updates self's lineno by one" do
+ @io.gets('>', 3)
+ @io.lineno.should eql(1)
+
+ @io.gets('>', 3)
+ @io.lineno.should eql(2)
+
+ @io.gets('>', 3)
+ @io.lineno.should eql(3)
+ end
+
+ it "tries to convert the passed separator to a String using #to_str" do
+ obj = mock('to_str')
+ obj.should_receive(:to_str).and_return('>')
+ @io.gets(obj, 5).should == "this>"
+ end
+
+ it "does not raise TypeError if passed separator is nil" do
+ @io.gets(nil, 5).should == "this>"
+ end
+
+ it "tries to convert the passed limit to an Integer using #to_int" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(5)
+ @io.gets('>', obj).should == "this>"
+ end
+end
+
+describe "StringIO#gets when in write-only mode" do
+ it "raises an IOError" do
+ io = StringIO.new("xyz", "w")
+ lambda { io.gets }.should raise_error(IOError)
+
+ io = StringIO.new("xyz")
+ io.close_read
+ lambda { io.gets }.should raise_error(IOError)
+ end
+end
+
+ruby_version_is "2.4" do
+ describe "StringIO#gets when passed [chomp]" do
+ it "returns the data read without a trailing newline character" do
+ io = StringIO.new("this>is>an>example\n")
+ io.gets(chomp: true).should == "this>is>an>example"
+ end
+ end
+end
diff --git a/spec/ruby/library/stringio/initialize_spec.rb b/spec/ruby/library/stringio/initialize_spec.rb
new file mode 100644
index 0000000000..bbb11b8f31
--- /dev/null
+++ b/spec/ruby/library/stringio/initialize_spec.rb
@@ -0,0 +1,185 @@
+require_relative '../../spec_helper'
+require 'stringio'
+
+describe "StringIO#initialize when passed [Object, mode]" do
+ before :each do
+ @io = StringIO.allocate
+ end
+
+ it "uses the passed Object as the StringIO backend" do
+ @io.send(:initialize, str = "example", "r")
+ @io.string.should equal(str)
+ end
+
+ it "sets the mode based on the passed mode" do
+ io = StringIO.allocate
+ io.send(:initialize, "example", "r")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_true
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", "rb")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_true
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", "r+")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", "rb+")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", "w")
+ io.closed_read?.should be_true
+ io.closed_write?.should be_false
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", "wb")
+ io.closed_read?.should be_true
+ io.closed_write?.should be_false
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", "w+")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", "wb+")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", "a")
+ io.closed_read?.should be_true
+ io.closed_write?.should be_false
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", "ab")
+ io.closed_read?.should be_true
+ io.closed_write?.should be_false
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", "a+")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", "ab+")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+ end
+
+ it "allows passing the mode as an Integer" do
+ io = StringIO.allocate
+ io.send(:initialize, "example", IO::RDONLY)
+ io.closed_read?.should be_false
+ io.closed_write?.should be_true
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", IO::RDWR)
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", IO::WRONLY)
+ io.closed_read?.should be_true
+ io.closed_write?.should be_false
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", IO::WRONLY | IO::TRUNC)
+ io.closed_read?.should be_true
+ io.closed_write?.should be_false
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", IO::RDWR | IO::TRUNC)
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", IO::WRONLY | IO::APPEND)
+ io.closed_read?.should be_true
+ io.closed_write?.should be_false
+
+ io = StringIO.allocate
+ io.send(:initialize, "example", IO::RDWR | IO::APPEND)
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+ end
+
+ it "raises a #{frozen_error_class} when passed a frozen String in truncate mode as StringIO backend" do
+ io = StringIO.allocate
+ lambda { io.send(:initialize, "example".freeze, IO::TRUNC) }.should raise_error(frozen_error_class)
+ end
+
+ it "tries to convert the passed mode to a String using #to_str" do
+ obj = mock('to_str')
+ obj.should_receive(:to_str).and_return("r")
+ @io.send(:initialize, "example", obj)
+
+ @io.closed_read?.should be_false
+ @io.closed_write?.should be_true
+ end
+
+ it "raises an Errno::EACCES error when passed a frozen string with a write-mode" do
+ (str = "example").freeze
+ lambda { @io.send(:initialize, str, "r+") }.should raise_error(Errno::EACCES)
+ lambda { @io.send(:initialize, str, "w") }.should raise_error(Errno::EACCES)
+ lambda { @io.send(:initialize, str, "a") }.should raise_error(Errno::EACCES)
+ end
+end
+
+describe "StringIO#initialize when passed [Object]" do
+ before :each do
+ @io = StringIO.allocate
+ end
+
+ it "uses the passed Object as the StringIO backend" do
+ @io.send(:initialize, str = "example")
+ @io.string.should equal(str)
+ end
+
+ it "sets the mode to read-write" do
+ @io.send(:initialize, "example")
+ @io.closed_read?.should be_false
+ @io.closed_write?.should be_false
+ end
+
+ it "tries to convert the passed Object to a String using #to_str" do
+ obj = mock('to_str')
+ obj.should_receive(:to_str).and_return("example")
+ @io.send(:initialize, obj)
+ @io.string.should == "example"
+ end
+
+ it "automatically sets the mode to read-only when passed a frozen string" do
+ (str = "example").freeze
+ @io.send(:initialize, str)
+ @io.closed_read?.should be_false
+ @io.closed_write?.should be_true
+ end
+end
+
+describe "StringIO#initialize when passed no arguments" do
+ before :each do
+ @io = StringIO.allocate
+ end
+
+ it "is private" do
+ StringIO.should have_private_instance_method(:initialize)
+ end
+
+ it "sets the mode to read-write" do
+ @io.send(:initialize, "example")
+ @io.closed_read?.should be_false
+ @io.closed_write?.should be_false
+ end
+
+ it "uses an empty String as the StringIO backend" do
+ @io.send(:initialize)
+ @io.string.should == ""
+ end
+end
diff --git a/spec/ruby/library/stringio/internal_encoding_spec.rb b/spec/ruby/library/stringio/internal_encoding_spec.rb
new file mode 100644
index 0000000000..2035cf25a9
--- /dev/null
+++ b/spec/ruby/library/stringio/internal_encoding_spec.rb
@@ -0,0 +1,10 @@
+require 'stringio'
+require_relative '../../spec_helper'
+
+describe "StringIO#internal_encoding" do
+ it "returns nil" do
+ io = StringIO.new
+ io.set_encoding Encoding::UTF_8
+ io.internal_encoding.should == nil
+ end
+end
diff --git a/spec/ruby/library/stringio/isatty_spec.rb b/spec/ruby/library/stringio/isatty_spec.rb
new file mode 100644
index 0000000000..1ef33978b5
--- /dev/null
+++ b/spec/ruby/library/stringio/isatty_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/isatty'
+
+describe "StringIO#isatty" do
+ it_behaves_like :stringio_isatty, :isatty
+end
diff --git a/spec/ruby/library/stringio/length_spec.rb b/spec/ruby/library/stringio/length_spec.rb
new file mode 100644
index 0000000000..d3070f50a7
--- /dev/null
+++ b/spec/ruby/library/stringio/length_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/length'
+
+describe "StringIO#length" do
+ it_behaves_like :stringio_length, :length
+end
diff --git a/spec/ruby/library/stringio/lineno_spec.rb b/spec/ruby/library/stringio/lineno_spec.rb
new file mode 100644
index 0000000000..c620a1a686
--- /dev/null
+++ b/spec/ruby/library/stringio/lineno_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require "stringio"
+
+describe "StringIO#lineno" do
+ before :each do
+ @io = StringIO.new("this\nis\nan\nexample")
+ end
+
+ it "returns the number of lines read" do
+ @io.gets
+ @io.gets
+ @io.gets
+ @io.lineno.should eql(3)
+ end
+end
+
+describe "StringIO#lineno=" do
+ before :each do
+ @io = StringIO.new("this\nis\nan\nexample")
+ end
+
+ it "sets the current line number, but has no impact on the position" do
+ @io.lineno = 3
+ @io.pos.should eql(0)
+
+ @io.gets.should == "this\n"
+ @io.lineno.should eql(4)
+ @io.pos.should eql(5)
+ end
+end
diff --git a/spec/ruby/library/stringio/lines_spec.rb b/spec/ruby/library/stringio/lines_spec.rb
new file mode 100644
index 0000000000..dd5773f5a3
--- /dev/null
+++ b/spec/ruby/library/stringio/lines_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require 'stringio'
+require_relative 'shared/each'
+
+describe "StringIO#lines when passed a separator" do
+ it_behaves_like :stringio_each_separator, :lines
+end
+
+describe "StringIO#lines when passed no arguments" do
+ it_behaves_like :stringio_each_no_arguments, :lines
+end
+
+describe "StringIO#lines when self is not readable" do
+ it_behaves_like :stringio_each_not_readable, :lines
+end
+
+ruby_version_is "2.4" do
+ describe "StringIO#lines when passed chomp" do
+ it_behaves_like :stringio_each_chomp, :lines
+ end
+end
diff --git a/spec/ruby/library/stringio/open_spec.rb b/spec/ruby/library/stringio/open_spec.rb
new file mode 100644
index 0000000000..f8f3feabee
--- /dev/null
+++ b/spec/ruby/library/stringio/open_spec.rb
@@ -0,0 +1,207 @@
+require_relative '../../spec_helper'
+require 'stringio'
+
+describe "StringIO.open when passed [Object, mode]" do
+ it "uses the passed Object as the StringIO backend" do
+ io = StringIO.open(str = "example", "r")
+ io.string.should equal(str)
+ end
+
+ it "returns the blocks return value when yielding" do
+ ret = StringIO.open("example", "r") { :test }
+ ret.should equal(:test)
+ end
+
+ it "yields self to the passed block" do
+ io = nil
+ StringIO.open("example", "r") { |strio| io = strio }
+ io.should be_kind_of(StringIO)
+ end
+
+ it "closes self after yielding" do
+ io = nil
+ StringIO.open("example", "r") { |strio| io = strio }
+ io.closed?.should be_true
+ end
+
+ it "even closes self when an exception is raised while yielding" do
+ io = nil
+ begin
+ StringIO.open("example", "r") do |strio|
+ io = strio
+ raise "Error"
+ end
+ rescue
+ end
+ io.closed?.should be_true
+ end
+
+ it "sets self's string to nil after yielding" do
+ io = nil
+ StringIO.open("example", "r") { |strio| io = strio }
+ io.string.should be_nil
+ end
+
+ it "even sets self's string to nil when an exception is raised while yielding" do
+ io = nil
+ begin
+ StringIO.open("example", "r") do |strio|
+ io = strio
+ raise "Error"
+ end
+ rescue
+ end
+ io.string.should be_nil
+ end
+
+ it "sets the mode based on the passed mode" do
+ io = StringIO.open("example", "r")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_true
+
+ io = StringIO.open("example", "rb")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_true
+
+ io = StringIO.open("example", "r+")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+
+ io = StringIO.open("example", "rb+")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+
+ io = StringIO.open("example", "w")
+ io.closed_read?.should be_true
+ io.closed_write?.should be_false
+
+ io = StringIO.open("example", "wb")
+ io.closed_read?.should be_true
+ io.closed_write?.should be_false
+
+ io = StringIO.open("example", "w+")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+
+ io = StringIO.open("example", "wb+")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+
+ io = StringIO.open("example", "a")
+ io.closed_read?.should be_true
+ io.closed_write?.should be_false
+
+ io = StringIO.open("example", "ab")
+ io.closed_read?.should be_true
+ io.closed_write?.should be_false
+
+ io = StringIO.open("example", "a+")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+
+ io = StringIO.open("example", "ab+")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+ end
+
+ it "allows passing the mode as an Integer" do
+ io = StringIO.open("example", IO::RDONLY)
+ io.closed_read?.should be_false
+ io.closed_write?.should be_true
+
+ io = StringIO.open("example", IO::RDWR)
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+
+ io = StringIO.open("example", IO::WRONLY)
+ io.closed_read?.should be_true
+ io.closed_write?.should be_false
+
+ io = StringIO.open("example", IO::WRONLY | IO::TRUNC)
+ io.closed_read?.should be_true
+ io.closed_write?.should be_false
+
+ io = StringIO.open("example", IO::RDWR | IO::TRUNC)
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+
+ io = StringIO.open("example", IO::WRONLY | IO::APPEND)
+ io.closed_read?.should be_true
+ io.closed_write?.should be_false
+
+ io = StringIO.open("example", IO::RDWR | IO::APPEND)
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+ end
+
+ it "raises a #{frozen_error_class} when passed a frozen String in truncate mode as StringIO backend" do
+ lambda { StringIO.open("example".freeze, IO::TRUNC) }.should raise_error(frozen_error_class)
+ end
+
+ it "tries to convert the passed mode to a String using #to_str" do
+ obj = mock('to_str')
+ obj.should_receive(:to_str).and_return("r")
+ io = StringIO.open("example", obj)
+
+ io.closed_read?.should be_false
+ io.closed_write?.should be_true
+ end
+
+ it "raises an Errno::EACCES error when passed a frozen string with a write-mode" do
+ (str = "example").freeze
+ lambda { StringIO.open(str, "r+") }.should raise_error(Errno::EACCES)
+ lambda { StringIO.open(str, "w") }.should raise_error(Errno::EACCES)
+ lambda { StringIO.open(str, "a") }.should raise_error(Errno::EACCES)
+ end
+end
+
+describe "StringIO.open when passed [Object]" do
+ it "uses the passed Object as the StringIO backend" do
+ io = StringIO.open(str = "example")
+ io.string.should equal(str)
+ end
+
+ it "yields self to the passed block" do
+ io = nil
+ ret = StringIO.open("example") { |strio| io = strio }
+ io.should equal(ret)
+ end
+
+ it "sets the mode to read-write" do
+ io = StringIO.open("example")
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+ end
+
+ it "tries to convert the passed Object to a String using #to_str" do
+ obj = mock('to_str')
+ obj.should_receive(:to_str).and_return("example")
+ io = StringIO.open(obj)
+ io.string.should == "example"
+ end
+
+ it "automatically sets the mode to read-only when passed a frozen string" do
+ (str = "example").freeze
+ io = StringIO.open(str)
+ io.closed_read?.should be_false
+ io.closed_write?.should be_true
+ end
+end
+
+describe "StringIO.open when passed no arguments" do
+ it "yields self to the passed block" do
+ io = nil
+ ret = StringIO.open { |strio| io = strio }
+ io.should equal(ret)
+ end
+
+ it "sets the mode to read-write" do
+ io = StringIO.open
+ io.closed_read?.should be_false
+ io.closed_write?.should be_false
+ end
+
+ it "uses an empty String as the StringIO backend" do
+ StringIO.open.string.should == ""
+ end
+end
diff --git a/spec/ruby/library/stringio/path_spec.rb b/spec/ruby/library/stringio/path_spec.rb
new file mode 100644
index 0000000000..5439cf4b53
--- /dev/null
+++ b/spec/ruby/library/stringio/path_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#path" do
+ it "is not defined" do
+ lambda { StringIO.new("path").path }.should raise_error(NoMethodError)
+ end
+end
diff --git a/spec/ruby/library/stringio/pid_spec.rb b/spec/ruby/library/stringio/pid_spec.rb
new file mode 100644
index 0000000000..08f2d7ab1a
--- /dev/null
+++ b/spec/ruby/library/stringio/pid_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#pid" do
+ it "returns nil" do
+ StringIO.new("pid").pid.should be_nil
+ end
+end
diff --git a/spec/ruby/library/stringio/pos_spec.rb b/spec/ruby/library/stringio/pos_spec.rb
new file mode 100644
index 0000000000..8d99294f31
--- /dev/null
+++ b/spec/ruby/library/stringio/pos_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/tell'
+
+describe "StringIO#pos" do
+ it_behaves_like :stringio_tell, :pos
+end
+
+describe "StringIO#pos=" do
+ before :each do
+ @io = StringIOSpecs.build
+ end
+
+ it "updates the current byte offset" do
+ @io.pos = 26
+ @io.read(1).should == "r"
+ end
+
+ it "raises an EINVAL if given a negative argument" do
+ lambda { @io.pos = -10 }.should raise_error(Errno::EINVAL)
+ end
+
+ it "updates the current byte offset after reaching EOF" do
+ @io.read
+ @io.pos = 26
+ @io.read(1).should == "r"
+ end
+end
diff --git a/spec/ruby/library/stringio/print_spec.rb b/spec/ruby/library/stringio/print_spec.rb
new file mode 100644
index 0000000000..144a219509
--- /dev/null
+++ b/spec/ruby/library/stringio/print_spec.rb
@@ -0,0 +1,100 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#print" do
+ before :each do
+ @io = StringIO.new('example')
+ end
+
+ it "prints $_ when passed no arguments" do
+ $_ = nil
+ @io.print
+ @io.string.should == "example"
+
+ $_ = "blah"
+ @io.print
+ @io.string.should == "blahple"
+ end
+
+ it "prints the passed arguments to self" do
+ @io.print(5, 6, 7, 8)
+ @io.string.should == "5678ple"
+ end
+
+ it "tries to convert the passed Object to a String using #to_s" do
+ obj = mock("to_s")
+ obj.should_receive(:to_s).and_return("to_s")
+ @io.print(obj)
+ @io.string.should == "to_sple"
+ end
+
+ it "returns nil" do
+ @io.print(1, 2, 3).should be_nil
+ end
+
+ it "pads self with \\000 when the current position is after the end" do
+ @io.pos = 10
+ @io.print(1, 2, 3)
+ @io.string.should == "example\000\000\000123"
+ end
+
+ it "honors the output record separator global" do
+ old_rs, $\ = $\, 'x'
+
+ begin
+ @io.print(5, 6, 7, 8)
+ @io.string.should == '5678xle'
+ ensure
+ $\ = old_rs
+ end
+ end
+
+ it "updates the current position" do
+ @io.print(1, 2, 3)
+ @io.pos.should eql(3)
+
+ @io.print(1, 2, 3)
+ @io.pos.should eql(6)
+ end
+
+ it "correctly updates the current position when honoring the output record separator global" do
+ old_rs, $\ = $\, 'x'
+
+ begin
+ @io.print(5, 6, 7, 8)
+ @io.pos.should eql(5)
+ ensure
+ $\ = old_rs
+ end
+ end
+end
+
+describe "StringIO#print when in append mode" do
+ before :each do
+ @io = StringIO.new("example", "a")
+ end
+
+ it "appends the passed argument to the end of self" do
+ @io.print(", just testing")
+ @io.string.should == "example, just testing"
+
+ @io.print(" and more testing")
+ @io.string.should == "example, just testing and more testing"
+ end
+
+ it "correctly updates self's position" do
+ @io.print(", testing")
+ @io.pos.should eql(16)
+ end
+end
+
+describe "StringIO#print when self is not writable" do
+ it "raises an IOError" do
+ io = StringIO.new("test", "r")
+ lambda { io.print("test") }.should raise_error(IOError)
+
+ io = StringIO.new("test")
+ io.close_write
+ lambda { io.print("test") }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/library/stringio/printf_spec.rb b/spec/ruby/library/stringio/printf_spec.rb
new file mode 100644
index 0000000000..3978896621
--- /dev/null
+++ b/spec/ruby/library/stringio/printf_spec.rb
@@ -0,0 +1,70 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../core/kernel/shared/sprintf'
+
+describe "StringIO#printf" do
+ before :each do
+ @io = StringIO.new('example')
+ end
+
+ it "returns nil" do
+ @io.printf("%d %04x", 123, 123).should be_nil
+ end
+
+ it "pads self with \\000 when the current position is after the end" do
+ @io.pos = 10
+ @io.printf("%d", 123)
+ @io.string.should == "example\000\000\000123"
+ end
+
+ it "performs format conversion" do
+ @io.printf("%d %04x", 123, 123)
+ @io.string.should == "123 007b"
+ end
+
+ it "updates the current position" do
+ @io.printf("%d %04x", 123, 123)
+ @io.pos.should eql(8)
+
+ @io.printf("%d %04x", 123, 123)
+ @io.pos.should eql(16)
+ end
+
+ describe "formatting" do
+ it_behaves_like :kernel_sprintf, -> (format, *args) {
+ io = StringIO.new
+ io.printf(format, *args)
+ io.string
+ }
+ end
+end
+
+describe "StringIO#printf when in append mode" do
+ before :each do
+ @io = StringIO.new("example", "a")
+ end
+
+ it "appends the passed argument to the end of self" do
+ @io.printf("%d %04x", 123, 123)
+ @io.string.should == "example123 007b"
+
+ @io.printf("%d %04x", 123, 123)
+ @io.string.should == "example123 007b123 007b"
+ end
+
+ it "correctly updates self's position" do
+ @io.printf("%d %04x", 123, 123)
+ @io.pos.should eql(15)
+ end
+end
+
+describe "StringIO#printf when self is not writable" do
+ it "raises an IOError" do
+ io = StringIO.new("test", "r")
+ lambda { io.printf("test") }.should raise_error(IOError)
+
+ io = StringIO.new("test")
+ io.close_write
+ lambda { io.printf("test") }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/library/stringio/putc_spec.rb b/spec/ruby/library/stringio/putc_spec.rb
new file mode 100644
index 0000000000..eae5481d6e
--- /dev/null
+++ b/spec/ruby/library/stringio/putc_spec.rb
@@ -0,0 +1,88 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#putc when passed [String]" do
+ before :each do
+ @io = StringIO.new('example')
+ end
+
+ it "overwrites the character at the current position" do
+ @io.putc("t")
+ @io.string.should == "txample"
+
+ @io.pos = 3
+ @io.putc("t")
+ @io.string.should == "txatple"
+ end
+
+ it "only writes the first character from the passed String" do
+ @io.putc("test")
+ @io.string.should == "txample"
+ end
+
+ it "returns the passed String" do
+ str = "test"
+ @io.putc(str).should equal(str)
+ end
+
+ it "correctly updates the current position" do
+ @io.putc("t")
+ @io.pos.should == 1
+
+ @io.putc("test")
+ @io.pos.should == 2
+
+ @io.putc("t")
+ @io.pos.should == 3
+ end
+end
+
+describe "StringIO#putc when passed [Object]" do
+ before :each do
+ @io = StringIO.new('example')
+ end
+
+ it "it writes the passed Integer % 256 to self" do
+ @io.putc(333) # 333 % 256 == ?M
+ @io.string.should == "Mxample"
+
+ @io.putc(-450) # -450 % 256 == ?>
+ @io.string.should == "M>ample"
+ end
+
+ it "pads self with \\000 when the current position is after the end" do
+ @io.pos = 10
+ @io.putc(?A)
+ @io.string.should == "example\000\000\000A"
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(116)
+ @io.putc(obj)
+ @io.string.should == "txample"
+ end
+
+ it "raises a TypeError when the passed argument can't be coerced to Integer" do
+ lambda { @io.putc(Object.new) }.should raise_error(TypeError)
+ end
+end
+
+describe "StringIO#putc when in append mode" do
+ it "appends to the end of self" do
+ io = StringIO.new("test", "a")
+ io.putc(?t)
+ io.string.should == "testt"
+ end
+end
+
+describe "StringIO#putc when self is not writable" do
+ it "raises an IOError" do
+ io = StringIO.new("test", "r")
+ lambda { io.putc(?a) }.should raise_error(IOError)
+
+ io = StringIO.new("test")
+ io.close_write
+ lambda { io.putc("t") }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/library/stringio/puts_spec.rb b/spec/ruby/library/stringio/puts_spec.rb
new file mode 100644
index 0000000000..396d9f25f4
--- /dev/null
+++ b/spec/ruby/library/stringio/puts_spec.rb
@@ -0,0 +1,159 @@
+# -*- encoding: utf-8 -*-
+
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#puts when passed an Array" do
+ before :each do
+ @io = StringIO.new
+ end
+
+ it "writes each element of the passed Array to self, separated by a newline" do
+ @io.puts([1, 2, 3, 4])
+ @io.string.should == "1\n2\n3\n4\n"
+
+ @io.puts([1, 2], [3, 4])
+ @io.string.should == "1\n2\n3\n4\n1\n2\n3\n4\n"
+ end
+
+ it "flattens nested Arrays" do
+ @io.puts([1, [2, [3, [4]]]])
+ @io.string.should == "1\n2\n3\n4\n"
+ end
+
+ it "handles self-recursive arrays correctly" do
+ (ary = [5])
+ ary << ary
+ @io.puts(ary)
+ @io.string.should == "5\n[...]\n"
+ end
+
+ it "does not honor the global output record separator $\\" do
+ begin
+ old_rs, $\ = $\, "test"
+ @io.puts([1, 2, 3, 4])
+ @io.string.should == "1\n2\n3\n4\n"
+ ensure
+ $\ = old_rs
+ end
+ end
+
+ it "first tries to convert each Array element to an Array using #to_ary" do
+ obj = mock("Object")
+ obj.should_receive(:to_ary).and_return(["to_ary"])
+ @io.puts([obj])
+ @io.string.should == "to_ary\n"
+ end
+
+ it "then tries to convert each Array element to a String using #to_s" do
+ obj = mock("Object")
+ obj.should_receive(:to_s).and_return("to_s")
+ @io.puts([obj])
+ @io.string.should == "to_s\n"
+ end
+end
+
+describe "StringIO#puts when passed 1 or more objects" do
+ before :each do
+ @io = StringIO.new
+ end
+
+ it "does not honor the global output record separator $\\" do
+ begin
+ old_rs, $\ = $\, "test"
+ @io.puts(1, 2, 3, 4)
+ @io.string.should == "1\n2\n3\n4\n"
+ ensure
+ $\ = old_rs
+ end
+ end
+
+ it "does not put a \\n after each Objects that end in a newline" do
+ @io.puts("1\n", "2\n", "3\n")
+ @io.string.should == "1\n2\n3\n"
+ end
+
+ it "first tries to convert each Object to an Array using #to_ary" do
+ obj = mock("Object")
+ obj.should_receive(:to_ary).and_return(["to_ary"])
+ @io.puts(obj)
+ @io.string.should == "to_ary\n"
+ end
+
+ it "then tries to convert each Object to a String using #to_s" do
+ obj = mock("Object")
+ obj.should_receive(:to_s).and_return("to_s")
+ @io.puts(obj)
+ @io.string.should == "to_s\n"
+ end
+
+ it "prints a newline when passed an empty string" do
+ @io.puts ''
+ @io.string.should == "\n"
+ end
+end
+
+describe "StringIO#puts when passed no arguments" do
+ before :each do
+ @io = StringIO.new
+ end
+
+ it "returns nil" do
+ @io.puts.should be_nil
+ end
+
+ it "prints a newline" do
+ @io.puts
+ @io.string.should == "\n"
+ end
+
+ it "does not honor the global output record separator $\\" do
+ begin
+ old_rs, $\ = $\, "test"
+ @io.puts
+ @io.string.should == "\n"
+ ensure
+ $\ = old_rs
+ end
+ end
+end
+
+describe "StringIO#puts when in append mode" do
+ before :each do
+ @io = StringIO.new("example", "a")
+ end
+
+ it "appends the passed argument to the end of self" do
+ @io.puts(", just testing")
+ @io.string.should == "example, just testing\n"
+
+ @io.puts(" and more testing")
+ @io.string.should == "example, just testing\n and more testing\n"
+ end
+
+ it "correctly updates self's position" do
+ @io.puts(", testing")
+ @io.pos.should eql(17)
+ end
+end
+
+describe "StringIO#puts when self is not writable" do
+ it "raises an IOError" do
+ io = StringIO.new("test", "r")
+ lambda { io.puts }.should raise_error(IOError)
+
+ io = StringIO.new("test")
+ io.close_write
+ lambda { io.puts }.should raise_error(IOError)
+ end
+end
+
+describe "StringIO#puts when passed an encoded string" do
+ it "stores the bytes unmodified" do
+ io = StringIO.new("")
+ io.puts "\x00\x01\x02"
+ io.puts "æåø"
+
+ io.string.should == "\x00\x01\x02\næåø\n"
+ end
+end
diff --git a/spec/ruby/library/stringio/read_nonblock_spec.rb b/spec/ruby/library/stringio/read_nonblock_spec.rb
new file mode 100644
index 0000000000..7a79eef3c5
--- /dev/null
+++ b/spec/ruby/library/stringio/read_nonblock_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+require "stringio"
+require_relative 'shared/read'
+require_relative 'shared/sysread'
+
+describe "StringIO#read_nonblock when passed length, buffer" do
+ it_behaves_like :stringio_read, :read_nonblock
+end
+
+describe "StringIO#read_nonblock when passed length" do
+ it_behaves_like :stringio_read_length, :read_nonblock
+end
+
+describe "StringIO#read_nonblock when passed nil" do
+ it_behaves_like :stringio_read_nil, :read_nonblock
+end
+
+describe "StringIO#read_nonblock when passed length" do
+ it_behaves_like :stringio_sysread_length, :read_nonblock
+end
+
+describe "StringIO#read_nonblock" do
+
+ it "accepts an exception option" do
+ stringio = StringIO.new('foo')
+ stringio.read_nonblock(3, exception: false).should == 'foo'
+ end
+
+end
diff --git a/spec/ruby/library/stringio/read_spec.rb b/spec/ruby/library/stringio/read_spec.rb
new file mode 100644
index 0000000000..52ab3dcf47
--- /dev/null
+++ b/spec/ruby/library/stringio/read_spec.rb
@@ -0,0 +1,62 @@
+require_relative '../../spec_helper'
+require "stringio"
+require_relative 'shared/read'
+
+describe "StringIO#read when passed length, buffer" do
+ it_behaves_like :stringio_read, :read
+end
+
+describe "StringIO#read when passed [length]" do
+ it_behaves_like :stringio_read_length, :read
+end
+
+describe "StringIO#read when passed no arguments" do
+ it_behaves_like :stringio_read_no_arguments, :read
+
+ it "returns an empty string if at EOF" do
+ @io.read.should == "example"
+ @io.read.should == ""
+ end
+end
+
+describe "StringIO#read when passed nil" do
+ it_behaves_like :stringio_read_nil, :read
+
+ it "returns an empty string if at EOF" do
+ @io.read(nil).should == "example"
+ @io.read(nil).should == ""
+ end
+end
+
+describe "StringIO#read when self is not readable" do
+ it_behaves_like :stringio_read_not_readable, :read
+end
+
+describe "StringIO#read when passed [length]" do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "returns nil when self's position is at the end" do
+ @io.pos = 7
+ @io.read(10).should be_nil
+ end
+
+ it "returns an empty String when length is 0" do
+ @io.read(0).should == ""
+ end
+end
+
+describe "StringIO#read when passed length and a buffer" do
+ before :each do
+ @io = StringIO.new("abcdefghijklmnopqrstuvwxyz")
+ end
+
+ it "reads [length] characters into the buffer" do
+ buf = "foo"
+ result = @io.read(10, buf)
+
+ buf.should == "abcdefghij"
+ result.should equal(buf)
+ end
+end
diff --git a/spec/ruby/library/stringio/readbyte_spec.rb b/spec/ruby/library/stringio/readbyte_spec.rb
new file mode 100644
index 0000000000..41a0911293
--- /dev/null
+++ b/spec/ruby/library/stringio/readbyte_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require 'stringio'
+require_relative 'shared/readchar'
+
+describe "StringIO#readbyte" do
+ it_behaves_like :stringio_readchar, :readbyte
+
+ it "reads the next 8-bit byte from self's current position" do
+ io = StringIO.new("example")
+
+ io.readbyte.should == 101
+
+ io.pos = 4
+ io.readbyte.should == 112
+ end
+end
+
+describe "StringIO#readbyte when self is not readable" do
+ it_behaves_like :stringio_readchar_not_readable, :readbyte
+end
diff --git a/spec/ruby/library/stringio/readchar_spec.rb b/spec/ruby/library/stringio/readchar_spec.rb
new file mode 100644
index 0000000000..38944819a2
--- /dev/null
+++ b/spec/ruby/library/stringio/readchar_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require 'stringio'
+require_relative 'shared/readchar'
+
+describe "StringIO#readchar" do
+ it_behaves_like :stringio_readchar, :readchar
+
+ it "reads the next 8-bit byte from self's current position" do
+ io = StringIO.new("example")
+
+ io.readchar.should == ?e
+
+ io.pos = 4
+ io.readchar.should == ?p
+ end
+end
+
+describe "StringIO#readchar when self is not readable" do
+ it_behaves_like :stringio_readchar_not_readable, :readchar
+end
diff --git a/spec/ruby/library/stringio/readline_spec.rb b/spec/ruby/library/stringio/readline_spec.rb
new file mode 100644
index 0000000000..fc1e75b67e
--- /dev/null
+++ b/spec/ruby/library/stringio/readline_spec.rb
@@ -0,0 +1,131 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+
+describe "StringIO#readline when passed [separator]" do
+ before :each do
+ @io = StringIO.new("this>is>an>example")
+ end
+
+ it "returns the data read till the next occurrence of the passed separator" do
+ @io.readline(">").should == "this>"
+ @io.readline(">").should == "is>"
+ @io.readline(">").should == "an>"
+ @io.readline(">").should == "example"
+ end
+
+ it "sets $_ to the read content" do
+ @io.readline(">")
+ $_.should == "this>"
+ @io.readline(">")
+ $_.should == "is>"
+ @io.readline(">")
+ $_.should == "an>"
+ @io.readline(">")
+ $_.should == "example"
+ end
+
+ it "updates self's lineno by one" do
+ @io.readline(">")
+ @io.lineno.should eql(1)
+
+ @io.readline(">")
+ @io.lineno.should eql(2)
+
+ @io.readline(">")
+ @io.lineno.should eql(3)
+ end
+
+ it "returns the next paragraph when the passed separator is an empty String" do
+ io = StringIO.new("this is\n\nan example")
+ io.readline("").should == "this is\n\n"
+ io.readline("").should == "an example"
+ end
+
+ it "returns the remaining content starting at the current position when passed nil" do
+ io = StringIO.new("this is\n\nan example")
+ io.pos = 5
+ io.readline(nil).should == "is\n\nan example"
+ end
+
+ it "tries to convert the passed separator to a String using #to_str" do
+ obj = mock('to_str')
+ obj.should_receive(:to_str).and_return(">")
+ @io.readline(obj).should == "this>"
+ end
+end
+
+describe "StringIO#readline when passed no argument" do
+ before :each do
+ @io = StringIO.new("this is\nan example\nfor StringIO#readline")
+ end
+
+ it "returns the data read till the next occurrence of $/ or till eof" do
+ @io.readline.should == "this is\n"
+
+ begin
+ old_sep, $/ = $/, " "
+ @io.readline.should == "an "
+ @io.readline.should == "example\nfor "
+ @io.readline.should == "StringIO#readline"
+ ensure
+ $/ = old_sep
+ end
+ end
+
+ it "sets $_ to the read content" do
+ @io.readline
+ $_.should == "this is\n"
+ @io.readline
+ $_.should == "an example\n"
+ @io.readline
+ $_.should == "for StringIO#readline"
+ end
+
+ it "updates self's position" do
+ @io.readline
+ @io.pos.should eql(8)
+
+ @io.readline
+ @io.pos.should eql(19)
+
+ @io.readline
+ @io.pos.should eql(40)
+ end
+
+ it "updates self's lineno" do
+ @io.readline
+ @io.lineno.should eql(1)
+
+ @io.readline
+ @io.lineno.should eql(2)
+
+ @io.readline
+ @io.lineno.should eql(3)
+ end
+
+ it "raises an IOError if self is at the end" do
+ @io.pos = 40
+ lambda { @io.readline }.should raise_error(IOError)
+ end
+end
+
+describe "StringIO#readline when in write-only mode" do
+ it "raises an IOError" do
+ io = StringIO.new("xyz", "w")
+ lambda { io.readline }.should raise_error(IOError)
+
+ io = StringIO.new("xyz")
+ io.close_read
+ lambda { io.readline }.should raise_error(IOError)
+ end
+end
+
+ruby_version_is "2.4" do
+ describe "StringIO#readline when passed [chomp]" do
+ it "returns the data read without a trailing newline character" do
+ io = StringIO.new("this>is>an>example\n")
+ io.readline(chomp: true).should == "this>is>an>example"
+ end
+ end
+end
diff --git a/spec/ruby/library/stringio/readlines_spec.rb b/spec/ruby/library/stringio/readlines_spec.rb
new file mode 100644
index 0000000000..c15f81b846
--- /dev/null
+++ b/spec/ruby/library/stringio/readlines_spec.rb
@@ -0,0 +1,101 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#readlines when passed [separator]" do
+ before :each do
+ @io = StringIO.new("this>is>an>example")
+ end
+
+ it "returns an Array containing lines based on the passed separator" do
+ @io.readlines(">").should == ["this>", "is>", "an>", "example"]
+ end
+
+ it "updates self's position based on the number of read bytes" do
+ @io.readlines(">")
+ @io.pos.should eql(18)
+ end
+
+ it "updates self's lineno based on the number of read lines" do
+ @io.readlines(">")
+ @io.lineno.should eql(4)
+ end
+
+ it "does not change $_" do
+ $_ = "test"
+ @io.readlines(">")
+ $_.should == "test"
+ end
+
+ it "returns an Array containing all paragraphs when the passed separator is an empty String" do
+ io = StringIO.new("this is\n\nan example")
+ io.readlines("").should == ["this is\n\n", "an example"]
+ end
+
+ it "returns the remaining content as one line starting at the current position when passed nil" do
+ io = StringIO.new("this is\n\nan example")
+ io.pos = 5
+ io.readlines(nil).should == ["is\n\nan example"]
+ end
+
+ it "tries to convert the passed separator to a String using #to_str" do
+ obj = mock('to_str')
+ obj.stub!(:to_str).and_return(">")
+ @io.readlines(obj).should == ["this>", "is>", "an>", "example"]
+ end
+end
+
+describe "StringIO#readlines when passed no argument" do
+ before :each do
+ @io = StringIO.new("this is\nan example\nfor StringIO#readlines")
+ end
+
+ it "returns an Array containing lines based on $/" do
+ begin
+ old_sep, $/ = $/, " "
+ @io.readlines.should == ["this ", "is\nan ", "example\nfor ", "StringIO#readlines"]
+ ensure
+ $/ = old_sep
+ end
+ end
+
+ it "updates self's position based on the number of read bytes" do
+ @io.readlines
+ @io.pos.should eql(41)
+ end
+
+ it "updates self's lineno based on the number of read lines" do
+ @io.readlines
+ @io.lineno.should eql(3)
+ end
+
+ it "does not change $_" do
+ $_ = "test"
+ @io.readlines(">")
+ $_.should == "test"
+ end
+
+ it "returns an empty Array when self is at the end" do
+ @io.pos = 41
+ @io.readlines.should == []
+ end
+end
+
+describe "StringIO#readlines when in write-only mode" do
+ it "raises an IOError" do
+ io = StringIO.new("xyz", "w")
+ lambda { io.readlines }.should raise_error(IOError)
+
+ io = StringIO.new("xyz")
+ io.close_read
+ lambda { io.readlines }.should raise_error(IOError)
+ end
+end
+
+ruby_version_is "2.4" do
+ describe "StringIO#readlines when passed [chomp]" do
+ it "returns the data read without a trailing newline character" do
+ io = StringIO.new("this>is\nan>example\r\n")
+ io.readlines(chomp: true).should == ["this>is", "an>example"]
+ end
+ end
+end
diff --git a/spec/ruby/library/stringio/readpartial_spec.rb b/spec/ruby/library/stringio/readpartial_spec.rb
new file mode 100644
index 0000000000..54e2f47004
--- /dev/null
+++ b/spec/ruby/library/stringio/readpartial_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#readpartial" do
+ before :each do
+ @string = StringIO.new('Stop, look, listen')
+ end
+
+ after :each do
+ @string.close unless @string.closed?
+ end
+
+ it "raises IOError on closed stream" do
+ @string.close
+ lambda { @string.readpartial(10) }.should raise_error(IOError)
+ end
+
+ it "reads at most the specified number of bytes" do
+
+ # buffered read
+ @string.read(1).should == 'S'
+ # return only specified number, not the whole buffer
+ @string.readpartial(1).should == "t"
+ end
+
+ it "reads after ungetc with data in the buffer" do
+ c = @string.getc
+ @string.ungetc(c)
+ @string.readpartial(4).should == "Stop"
+ @string.readpartial(3).should == ", l"
+ end
+
+ it "reads after ungetc without data in the buffer" do
+ @string = StringIO.new
+ @string.write("f").should == 1
+ @string.rewind
+ c = @string.getc
+ c.should == 'f'
+ @string.ungetc(c).should == nil
+
+ @string.readpartial(2).should == "f"
+ @string.rewind
+ # now, also check that the ungot char is cleared and
+ # not returned again
+ @string.write("b").should == 1
+ @string.rewind
+ @string.readpartial(2).should == "b"
+ end
+
+ it "discards the existing buffer content upon successful read" do
+ buffer = "existing"
+ @string.readpartial(11, buffer)
+ buffer.should == "Stop, look,"
+ end
+
+ it "raises EOFError on EOF" do
+ @string.readpartial(18).should == 'Stop, look, listen'
+ lambda { @string.readpartial(10) }.should raise_error(EOFError)
+ end
+
+ it "discards the existing buffer content upon error" do
+ buffer = 'hello'
+ @string.readpartial(100)
+ lambda { @string.readpartial(1, buffer) }.should raise_error(EOFError)
+ buffer.should be_empty
+ end
+
+ it "raises IOError if the stream is closed" do
+ @string.close
+ lambda { @string.readpartial(1) }.should raise_error(IOError)
+ end
+
+ it "raises ArgumentError if the negative argument is provided" do
+ lambda { @string.readpartial(-1) }.should raise_error(ArgumentError)
+ end
+
+ it "immediately returns an empty string if the length argument is 0" do
+ @string.readpartial(0).should == ""
+ end
+end
diff --git a/spec/ruby/library/stringio/reopen_spec.rb b/spec/ruby/library/stringio/reopen_spec.rb
new file mode 100644
index 0000000000..51aff43ba2
--- /dev/null
+++ b/spec/ruby/library/stringio/reopen_spec.rb
@@ -0,0 +1,288 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#reopen when passed [Object, Integer]" do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "reopens self with the passed Object in the passed mode" do
+ @io.reopen("reopened", IO::RDONLY)
+ @io.closed_read?.should be_false
+ @io.closed_write?.should be_true
+ @io.string.should == "reopened"
+
+ @io.reopen("reopened, twice", IO::WRONLY)
+ @io.closed_read?.should be_true
+ @io.closed_write?.should be_false
+ @io.string.should == "reopened, twice"
+
+ @io.reopen("reopened, another time", IO::RDWR)
+ @io.closed_read?.should be_false
+ @io.closed_write?.should be_false
+ @io.string.should == "reopened, another time"
+ end
+
+ # NOTE: WEIRD!
+ it "does not taint self when the passed Object was tainted" do
+ @io.reopen("reopened".taint, IO::RDONLY)
+ @io.tainted?.should be_false
+
+ @io.reopen("reopened".taint, IO::WRONLY)
+ @io.tainted?.should be_false
+ end
+
+ it "tries to convert the passed Object to a String using #to_str" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return("to_str")
+ @io.reopen(obj, IO::RDWR)
+ @io.string.should == "to_str"
+ end
+
+ it "raises a TypeError when the passed Object can't be converted to a String" do
+ lambda { @io.reopen(Object.new, IO::RDWR) }.should raise_error(TypeError)
+ end
+
+ it "raises an Errno::EACCES when trying to reopen self with a frozen String in write-mode" do
+ lambda { @io.reopen("burn".freeze, IO::WRONLY) }.should raise_error(Errno::EACCES)
+ lambda { @io.reopen("burn".freeze, IO::WRONLY | IO::APPEND) }.should raise_error(Errno::EACCES)
+ end
+
+ it "raises a #{frozen_error_class} when trying to reopen self with a frozen String in truncate-mode" do
+ lambda { @io.reopen("burn".freeze, IO::RDONLY | IO::TRUNC) }.should raise_error(frozen_error_class)
+ end
+
+ it "does not raise IOError when passed a frozen String in read-mode" do
+ @io.reopen("burn".freeze, IO::RDONLY)
+ @io.string.should == "burn"
+ end
+end
+
+describe "StringIO#reopen when passed [Object, Object]" do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "reopens self with the passed Object in the passed mode" do
+ @io.reopen("reopened", "r")
+ @io.closed_read?.should be_false
+ @io.closed_write?.should be_true
+ @io.string.should == "reopened"
+
+ @io.reopen("reopened, twice", "r+")
+ @io.closed_read?.should be_false
+ @io.closed_write?.should be_false
+ @io.string.should == "reopened, twice"
+
+ @io.reopen("reopened, another", "w+")
+ @io.closed_read?.should be_false
+ @io.closed_write?.should be_false
+ @io.string.should == ""
+
+ @io.reopen("reopened, another time", "r+")
+ @io.closed_read?.should be_false
+ @io.closed_write?.should be_false
+ @io.string.should == "reopened, another time"
+ end
+
+ it "truncates the passed String when opened in truncate mode" do
+ @io.reopen(str = "reopened", "w")
+ str.should == ""
+ end
+
+ # NOTE: WEIRD!
+ it "does not taint self when the passed Object was tainted" do
+ @io.reopen("reopened".taint, "r")
+ @io.tainted?.should be_false
+
+ @io.reopen("reopened".taint, "w")
+ @io.tainted?.should be_false
+ end
+
+ it "tries to convert the passed Object to a String using #to_str" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return("to_str")
+ @io.reopen(obj, "r")
+ @io.string.should == "to_str"
+ end
+
+ it "raises a TypeError when the passed Object can't be converted to a String using #to_str" do
+ lambda { @io.reopen(Object.new, "r") }.should raise_error(TypeError)
+ end
+
+ it "resets self's position to 0" do
+ @io.read(5)
+ @io.reopen("reopened")
+ @io.pos.should eql(0)
+ end
+
+ it "resets self's line number to 0" do
+ @io.gets
+ @io.reopen("reopened")
+ @io.lineno.should eql(0)
+ end
+
+ it "tries to convert the passed mode Object to an Integer using #to_str" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return("r")
+ @io.reopen("reopened", obj)
+ @io.closed_read?.should be_false
+ @io.closed_write?.should be_true
+ @io.string.should == "reopened"
+ end
+
+ it "raises an Errno::EACCES error when trying to reopen self with a frozen String in write-mode" do
+ lambda { @io.reopen("burn".freeze, 'w') }.should raise_error(Errno::EACCES)
+ lambda { @io.reopen("burn".freeze, 'w+') }.should raise_error(Errno::EACCES)
+ lambda { @io.reopen("burn".freeze, 'a') }.should raise_error(Errno::EACCES)
+ lambda { @io.reopen("burn".freeze, "r+") }.should raise_error(Errno::EACCES)
+ end
+
+ it "does not raise IOError if a frozen string is passed in read mode" do
+ @io.reopen("burn".freeze, "r")
+ @io.string.should == "burn"
+ end
+end
+
+describe "StringIO#reopen when passed [String]" do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "reopens self with the passed String in read-write mode" do
+ @io.close
+
+ @io.reopen("reopened")
+
+ @io.closed_write?.should be_false
+ @io.closed_read?.should be_false
+
+ @io.string.should == "reopened"
+ end
+
+ # NOTE: WEIRD!
+ it "does not taint self when the passed Object was tainted" do
+ @io.reopen("reopened".taint)
+ @io.tainted?.should be_false
+ end
+
+ it "resets self's position to 0" do
+ @io.read(5)
+ @io.reopen("reopened")
+ @io.pos.should eql(0)
+ end
+
+ it "resets self's line number to 0" do
+ @io.gets
+ @io.reopen("reopened")
+ @io.lineno.should eql(0)
+ end
+end
+
+describe "StringIO#reopen when passed [Object]" do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "raises a TypeError when passed an Object that can't be converted to a StringIO" do
+ lambda { @io.reopen(Object.new) }.should raise_error(TypeError)
+ end
+
+ it "does not try to convert the passed Object to a String using #to_str" do
+ obj = mock("not to_str")
+ obj.should_not_receive(:to_str)
+ lambda { @io.reopen(obj) }.should raise_error(TypeError)
+ end
+
+ it "tries to convert the passed Object to a StringIO using #to_strio" do
+ obj = mock("to_strio")
+ obj.should_receive(:to_strio).and_return(StringIO.new("to_strio"))
+ @io.reopen(obj)
+ @io.string.should == "to_strio"
+ end
+
+ # NOTE: WEIRD!
+ it "taints self when the passed Object was tainted" do
+ @io.reopen(StringIO.new("reopened").taint)
+ @io.tainted?.should be_true
+ end
+end
+
+describe "StringIO#reopen when passed no arguments" do
+ before :each do
+ @io = StringIO.new("example\nsecond line")
+ end
+
+ it "resets self's mode to read-write" do
+ @io.close
+ @io.reopen
+ @io.closed_read?.should be_false
+ @io.closed_write?.should be_false
+ end
+
+ it "resets self's position to 0" do
+ @io.read(5)
+ @io.reopen
+ @io.pos.should eql(0)
+ end
+
+ it "resets self's line number to 0" do
+ @io.gets
+ @io.reopen
+ @io.lineno.should eql(0)
+ end
+end
+
+# NOTE: Some reopen specs disabled due to MRI bugs. See:
+# http://rubyforge.org/tracker/index.php?func=detail&aid=13919&group_id=426&atid=1698
+# for details.
+describe "StringIO#reopen" do
+ before :each do
+ @io = StringIO.new('hello','a')
+ end
+
+ # TODO: find out if this is really a bug
+ it "reopens a stream when given a String argument" do
+ @io.reopen('goodbye').should == @io
+ @io.string.should == 'goodbye'
+ @io << 'x'
+ @io.string.should == 'xoodbye'
+ end
+
+ it "reopens a stream in append mode when flagged as such" do
+ @io.reopen('goodbye', 'a').should == @io
+ @io.string.should == 'goodbye'
+ @io << 'x'
+ @io.string.should == 'goodbyex'
+ end
+
+ it "reopens and truncate when reopened in write mode" do
+ @io.reopen('goodbye', 'wb').should == @io
+ @io.string.should == ''
+ @io << 'x'
+ @io.string.should == 'x'
+ end
+
+ it "truncates the given string, not a copy" do
+ str = 'goodbye'
+ @io.reopen(str, 'w')
+ @io.string.should == ''
+ str.should == ''
+ end
+
+ it "taints self if the provided StringIO argument is tainted" do
+ new_io = StringIO.new("tainted")
+ new_io.taint
+ @io.reopen(new_io)
+ @io.tainted?.should == true
+ end
+
+ it "does not truncate the content even when the StringIO argument is in the truncate mode" do
+ orig_io = StringIO.new("Original StringIO", IO::RDWR|IO::TRUNC)
+ orig_io.write("BLAH") # make sure the content is not empty
+
+ @io.reopen(orig_io)
+ @io.string.should == "BLAH"
+ end
+
+end
diff --git a/spec/ruby/library/stringio/rewind_spec.rb b/spec/ruby/library/stringio/rewind_spec.rb
new file mode 100644
index 0000000000..5f885ecf81
--- /dev/null
+++ b/spec/ruby/library/stringio/rewind_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#rewind" do
+ before :each do
+ @io = StringIO.new("hello\nworld")
+ @io.pos = 3
+ @io.lineno = 1
+ end
+
+ it "returns 0" do
+ @io.rewind.should eql(0)
+ end
+
+ it "resets the position" do
+ @io.rewind
+ @io.pos.should == 0
+ end
+
+ it "resets the line number" do
+ @io.rewind
+ @io.lineno.should == 0
+ end
+end
diff --git a/spec/ruby/library/stringio/seek_spec.rb b/spec/ruby/library/stringio/seek_spec.rb
new file mode 100644
index 0000000000..b963b77eef
--- /dev/null
+++ b/spec/ruby/library/stringio/seek_spec.rb
@@ -0,0 +1,67 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#seek" do
+ before :each do
+ @io = StringIO.new("12345678")
+ end
+
+ it "seeks from the current position when whence is IO::SEEK_CUR" do
+ @io.pos = 1
+ @io.seek(1, IO::SEEK_CUR)
+ @io.pos.should eql(2)
+
+ @io.seek(-1, IO::SEEK_CUR)
+ @io.pos.should eql(1)
+ end
+
+ it "seeks from the end of self when whence is IO::SEEK_END" do
+ @io.seek(3, IO::SEEK_END)
+ @io.pos.should eql(11) # Outside of the StringIO's content
+
+ @io.seek(-2, IO::SEEK_END)
+ @io.pos.should eql(6)
+ end
+
+ it "seeks to an absolute position when whence is IO::SEEK_SET" do
+ @io.seek(5, IO::SEEK_SET)
+ @io.pos.should == 5
+
+ @io.pos = 3
+ @io.seek(5, IO::SEEK_SET)
+ @io.pos.should == 5
+ end
+
+ it "raises an Errno::EINVAL error on negative amounts when whence is IO::SEEK_SET" do
+ lambda { @io.seek(-5, IO::SEEK_SET) }.should raise_error(Errno::EINVAL)
+ end
+
+ it "raises an Errno::EINVAL error on incorrect whence argument" do
+ lambda { @io.seek(0, 3) }.should raise_error(Errno::EINVAL)
+ lambda { @io.seek(0, -1) }.should raise_error(Errno::EINVAL)
+ lambda { @io.seek(0, 2**16) }.should raise_error(Errno::EINVAL)
+ lambda { @io.seek(0, -2**16) }.should raise_error(Errno::EINVAL)
+ end
+
+ it "tries to convert the passed Object to a String using #to_int" do
+ obj = mock("to_int")
+ obj.should_receive(:to_int).and_return(2)
+ @io.seek(obj)
+ @io.pos.should eql(2)
+ end
+
+ it "raises a TypeError when the passed Object can't be converted to an Integer" do
+ lambda { @io.seek(Object.new) }.should raise_error(TypeError)
+ end
+end
+
+describe "StringIO#seek when self is closed" do
+ before :each do
+ @io = StringIO.new("example")
+ @io.close
+ end
+
+ it "raises an IOError" do
+ lambda { @io.seek(5) }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/library/stringio/set_encoding_spec.rb b/spec/ruby/library/stringio/set_encoding_spec.rb
new file mode 100644
index 0000000000..17aa7df23e
--- /dev/null
+++ b/spec/ruby/library/stringio/set_encoding_spec.rb
@@ -0,0 +1,10 @@
+require 'stringio'
+require_relative '../../spec_helper'
+
+describe "StringIO#set_encoding" do
+ it "sets the encoding of the underlying String" do
+ io = StringIO.new
+ io.set_encoding Encoding::UTF_8
+ io.string.encoding.should == Encoding::UTF_8
+ end
+end
diff --git a/spec/ruby/library/stringio/shared/codepoints.rb b/spec/ruby/library/stringio/shared/codepoints.rb
new file mode 100644
index 0000000000..c8ca03329f
--- /dev/null
+++ b/spec/ruby/library/stringio/shared/codepoints.rb
@@ -0,0 +1,45 @@
+# -*- encoding: utf-8 -*-
+describe :stringio_codepoints, shared: true do
+ before :each do
+ @io = StringIO.new("∂φ/∂x = gaîté")
+ @enum = @io.send(@method)
+ end
+
+ it "returns an Enumerator" do
+ @enum.should be_an_instance_of(Enumerator)
+ end
+
+ it "yields each codepoint code in turn" do
+ @enum.to_a.should == [8706, 966, 47, 8706, 120, 32, 61, 32, 103, 97, 238, 116, 233]
+ end
+
+ it "yields each codepoint starting from the current position" do
+ @io.pos = 15
+ @enum.to_a.should == [238, 116, 233]
+ end
+
+ it "raises an error if reading invalid sequence" do
+ @io.pos = 1 # inside of a multibyte sequence
+ lambda { @enum.first }.should raise_error(ArgumentError)
+ end
+
+ it "raises an IOError if not readable" do
+ @io.close_read
+ lambda { @enum.to_a }.should raise_error(IOError)
+
+ io = StringIO.new("xyz", "w")
+ lambda { io.send(@method).to_a }.should raise_error(IOError)
+ end
+
+
+ it "calls the given block" do
+ r = []
+ @io.send(@method){|c| r << c }
+ r.should == [8706, 966, 47, 8706, 120, 32, 61, 32, 103, 97, 238, 116, 233]
+ end
+
+ it "returns self" do
+ @io.send(@method) {|l| l }.should equal(@io)
+ end
+
+end
diff --git a/spec/ruby/library/stringio/shared/each.rb b/spec/ruby/library/stringio/shared/each.rb
new file mode 100644
index 0000000000..55ed27c1c7
--- /dev/null
+++ b/spec/ruby/library/stringio/shared/each.rb
@@ -0,0 +1,114 @@
+describe :stringio_each_separator, shared: true do
+ before :each do
+ @io = StringIO.new("a b c d e\n1 2 3 4 5")
+ end
+
+ it "uses the passed argument as the line separator" do
+ seen = []
+ @io.send(@method, " ") {|s| seen << s}
+ seen.should == ["a ", "b ", "c ", "d ", "e\n1 ", "2 ", "3 ", "4 ", "5"]
+ end
+
+ it "does not change $_" do
+ $_ = "test"
+ @io.send(@method, " ") { |s| s}
+ $_.should == "test"
+ end
+
+ it "returns self" do
+ @io.send(@method) {|l| l }.should equal(@io)
+ end
+
+ it "tries to convert the passed separator to a String using #to_str" do
+ obj = mock("to_str")
+ obj.stub!(:to_str).and_return(" ")
+
+ seen = []
+ @io.send(@method, obj) { |l| seen << l }
+ seen.should == ["a ", "b ", "c ", "d ", "e\n1 ", "2 ", "3 ", "4 ", "5"]
+ end
+
+ it "yields self's content starting from the current position when the passed separator is nil" do
+ seen = []
+ io = StringIO.new("1 2 1 2 1 2")
+ io.pos = 2
+ io.send(@method, nil) {|s| seen << s}
+ seen.should == ["2 1 2 1 2"]
+ end
+
+ it "yields each paragraph when passed an empty String as separator" do
+ seen = []
+ io = StringIO.new("para1\n\npara2\n\n\npara3")
+ io.send(@method, "") {|s| seen << s}
+ seen.should == ["para1\n\n", "para2\n\n", "para3"]
+ end
+end
+
+describe :stringio_each_no_arguments, shared: true do
+ before :each do
+ @io = StringIO.new("a b c d e\n1 2 3 4 5")
+ end
+
+ it "yields each line to the passed block" do
+ seen = []
+ @io.send(@method) {|s| seen << s }
+ seen.should == ["a b c d e\n", "1 2 3 4 5"]
+ end
+
+ it "yields each line starting from the current position" do
+ seen = []
+ @io.pos = 4
+ @io.send(@method) {|s| seen << s }
+ seen.should == ["c d e\n", "1 2 3 4 5"]
+ end
+
+ it "does not change $_" do
+ $_ = "test"
+ @io.send(@method) { |s| s}
+ $_.should == "test"
+ end
+
+ it "uses $/ as the default line separator" do
+ seen = []
+ begin
+ old_rs, $/ = $/, " "
+ @io.send(@method) {|s| seen << s }
+ seen.should eql(["a ", "b ", "c ", "d ", "e\n1 ", "2 ", "3 ", "4 ", "5"])
+ ensure
+ $/ = old_rs
+ end
+ end
+
+ it "returns self" do
+ @io.send(@method) {|l| l }.should equal(@io)
+ end
+
+ it "returns an Enumerator when passed no block" do
+ enum = @io.send(@method)
+ enum.instance_of?(Enumerator).should be_true
+
+ seen = []
+ enum.each { |b| seen << b }
+ seen.should == ["a b c d e\n", "1 2 3 4 5"]
+ end
+end
+
+describe :stringio_each_not_readable, shared: true do
+ it "raises an IOError" do
+ io = StringIO.new("a b c d e", "w")
+ lambda { io.send(@method) { |b| b } }.should raise_error(IOError)
+
+ io = StringIO.new("a b c d e")
+ io.close_read
+ lambda { io.send(@method) { |b| b } }.should raise_error(IOError)
+ end
+end
+
+describe :stringio_each_chomp, shared: true do
+ it "yields each line with removed newline characters to the passed block" do
+ seen = []
+ io = StringIO.new("a b \rc d e\n1 2 3 4 5\r\nthe end")
+ io.send(@method, chomp: true) {|s| seen << s }
+ seen.should == ["a b \rc d e", "1 2 3 4 5", "the end"]
+ end
+end
diff --git a/spec/ruby/library/stringio/shared/each_byte.rb b/spec/ruby/library/stringio/shared/each_byte.rb
new file mode 100644
index 0000000000..1dc48ee437
--- /dev/null
+++ b/spec/ruby/library/stringio/shared/each_byte.rb
@@ -0,0 +1,48 @@
+describe :stringio_each_byte, shared: true do
+ before :each do
+ @io = StringIO.new("xyz")
+ end
+
+ it "yields each character code in turn" do
+ seen = []
+ @io.send(@method) { |b| seen << b }
+ seen.should == [120, 121, 122]
+ end
+
+ it "updates the position before each yield" do
+ seen = []
+ @io.send(@method) { |b| seen << @io.pos }
+ seen.should == [1, 2, 3]
+ end
+
+ it "does not yield if the current position is out of bounds" do
+ @io.pos = 1000
+ seen = nil
+ @io.send(@method) { |b| seen = b }
+ seen.should be_nil
+ end
+
+ it "returns self" do
+ @io.send(@method) {}.should equal(@io)
+ end
+
+ it "returns an Enumerator when passed no block" do
+ enum = @io.send(@method)
+ enum.instance_of?(Enumerator).should be_true
+
+ seen = []
+ enum.each { |b| seen << b }
+ seen.should == [120, 121, 122]
+ end
+end
+
+describe :stringio_each_byte_not_readable, shared: true do
+ it "raises an IOError" do
+ io = StringIO.new("xyz", "w")
+ lambda { io.send(@method) { |b| b } }.should raise_error(IOError)
+
+ io = StringIO.new("xyz")
+ io.close_read
+ lambda { io.send(@method) { |b| b } }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/library/stringio/shared/each_char.rb b/spec/ruby/library/stringio/shared/each_char.rb
new file mode 100644
index 0000000000..35efdcb749
--- /dev/null
+++ b/spec/ruby/library/stringio/shared/each_char.rb
@@ -0,0 +1,36 @@
+# -*- encoding: utf-8 -*-
+describe :stringio_each_char, shared: true do
+ before :each do
+ @io = StringIO.new("xyz äöü")
+ end
+
+ it "yields each character code in turn" do
+ seen = []
+ @io.send(@method) { |c| seen << c }
+ seen.should == ["x", "y", "z", " ", "ä", "ö", "ü"]
+ end
+
+ it "returns self" do
+ @io.send(@method) {}.should equal(@io)
+ end
+
+ it "returns an Enumerator when passed no block" do
+ enum = @io.send(@method)
+ enum.instance_of?(Enumerator).should be_true
+
+ seen = []
+ enum.each { |c| seen << c }
+ seen.should == ["x", "y", "z", " ", "ä", "ö", "ü"]
+ end
+end
+
+describe :stringio_each_char_not_readable, shared: true do
+ it "raises an IOError" do
+ io = StringIO.new("xyz", "w")
+ lambda { io.send(@method) { |b| b } }.should raise_error(IOError)
+
+ io = StringIO.new("xyz")
+ io.close_read
+ lambda { io.send(@method) { |b| b } }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/library/stringio/shared/eof.rb b/spec/ruby/library/stringio/shared/eof.rb
new file mode 100644
index 0000000000..e0368a2892
--- /dev/null
+++ b/spec/ruby/library/stringio/shared/eof.rb
@@ -0,0 +1,24 @@
+describe :stringio_eof, shared: true do
+ before :each do
+ @io = StringIO.new("eof")
+ end
+
+ it "returns true when self's position is greater than or equal to self's size" do
+ @io.pos = 3
+ @io.send(@method).should be_true
+
+ @io.pos = 6
+ @io.send(@method).should be_true
+ end
+
+ it "returns false when self's position is less than self's size" do
+ @io.pos = 0
+ @io.send(@method).should be_false
+
+ @io.pos = 1
+ @io.send(@method).should be_false
+
+ @io.pos = 2
+ @io.send(@method).should be_false
+ end
+end
diff --git a/spec/ruby/library/stringio/shared/getc.rb b/spec/ruby/library/stringio/shared/getc.rb
new file mode 100644
index 0000000000..3e064f9c1e
--- /dev/null
+++ b/spec/ruby/library/stringio/shared/getc.rb
@@ -0,0 +1,43 @@
+describe :stringio_getc, shared: true do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "increases self's position by one" do
+ @io.send(@method)
+ @io.pos.should eql(1)
+
+ @io.send(@method)
+ @io.pos.should eql(2)
+
+ @io.send(@method)
+ @io.pos.should eql(3)
+ end
+
+ it "returns nil when called at the end of self" do
+ @io.pos = 7
+ @io.send(@method).should be_nil
+ @io.send(@method).should be_nil
+ @io.send(@method).should be_nil
+ end
+
+ it "does not increase self's position when called at the end of file" do
+ @io.pos = 7
+ @io.send(@method)
+ @io.pos.should eql(7)
+
+ @io.send(@method)
+ @io.pos.should eql(7)
+ end
+end
+
+describe :stringio_getc_not_readable, shared: true do
+ it "raises an IOError" do
+ io = StringIO.new("xyz", "w")
+ lambda { io.send(@method) }.should raise_error(IOError)
+
+ io = StringIO.new("xyz")
+ io.close_read
+ lambda { io.send(@method) }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/library/stringio/shared/isatty.rb b/spec/ruby/library/stringio/shared/isatty.rb
new file mode 100644
index 0000000000..3da5999953
--- /dev/null
+++ b/spec/ruby/library/stringio/shared/isatty.rb
@@ -0,0 +1,5 @@
+describe :stringio_isatty, shared: true do
+ it "returns false" do
+ StringIO.new('tty').send(@method).should be_false
+ end
+end
diff --git a/spec/ruby/library/stringio/shared/length.rb b/spec/ruby/library/stringio/shared/length.rb
new file mode 100644
index 0000000000..60a4eb1bdd
--- /dev/null
+++ b/spec/ruby/library/stringio/shared/length.rb
@@ -0,0 +1,5 @@
+describe :stringio_length, shared: true do
+ it "returns the length of the wrapped string" do
+ StringIO.new("example").send(@method).should == 7
+ end
+end
diff --git a/spec/ruby/library/stringio/shared/read.rb b/spec/ruby/library/stringio/shared/read.rb
new file mode 100644
index 0000000000..604bf880e5
--- /dev/null
+++ b/spec/ruby/library/stringio/shared/read.rb
@@ -0,0 +1,121 @@
+describe :stringio_read, shared: true do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "returns the passed buffer String" do
+ # Note: Rubinius bug:
+ # @io.send(@method, 7, buffer = "").should equal(buffer)
+ ret = @io.send(@method, 7, buffer = "")
+ ret.should equal(buffer)
+ end
+
+ it "reads length bytes and writes them to the buffer String" do
+ @io.send(@method, 7, buffer = "")
+ buffer.should == "example"
+ end
+
+ it "tries to convert the passed buffer Object to a String using #to_str" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(buffer = "")
+
+ @io.send(@method, 7, obj)
+ buffer.should == "example"
+ end
+
+ it "raises a TypeError when the passed buffer Object can't be converted to a String" do
+ lambda { @io.send(@method, 7, Object.new) }.should raise_error(TypeError)
+ end
+
+ it "raises a #{frozen_error_class} error when passed a frozen String as buffer" do
+ lambda { @io.send(@method, 7, "".freeze) }.should raise_error(frozen_error_class)
+ end
+end
+
+describe :stringio_read_length, shared: true do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "reads length bytes from the current position and returns them" do
+ @io.pos = 3
+ @io.send(@method, 4).should == "mple"
+ end
+
+ it "reads at most the whole content" do
+ @io.send(@method, 999).should == "example"
+ end
+
+ it "correctly updates the position" do
+ @io.send(@method, 3)
+ @io.pos.should eql(3)
+
+ @io.send(@method, 999)
+ @io.pos.should eql(7)
+ end
+
+ it "tries to convert the passed length to an Integer using #to_int" do
+ obj = mock("to_int")
+ obj.should_receive(:to_int).and_return(7)
+ @io.send(@method, obj).should == "example"
+ end
+
+ it "raises a TypeError when the passed length can't be converted to an Integer" do
+ lambda { @io.send(@method, Object.new) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when the passed length is negative" do
+ lambda { @io.send(@method, -2) }.should raise_error(ArgumentError)
+ end
+
+ it "returns a binary String" do
+ @io.send(@method, 4).encoding.should == Encoding::ASCII_8BIT
+ end
+end
+
+describe :stringio_read_no_arguments, shared: true do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "reads the whole content starting from the current position" do
+ @io.send(@method).should == "example"
+
+ @io.pos = 3
+ @io.send(@method).should == "mple"
+ end
+
+ it "correctly updates the current position" do
+ @io.send(@method)
+ @io.pos.should eql(7)
+ end
+end
+
+describe :stringio_read_nil, shared: true do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "returns the remaining content from the current position" do
+ @io.send(@method, nil).should == "example"
+
+ @io.pos = 4
+ @io.send(@method, nil).should == "ple"
+ end
+
+ it "updates the current position" do
+ @io.send(@method, nil)
+ @io.pos.should eql(7)
+ end
+end
+
+describe :stringio_read_not_readable, shared: true do
+ it "raises an IOError" do
+ io = StringIO.new("test", "w")
+ lambda { io.send(@method) }.should raise_error(IOError)
+
+ io = StringIO.new("test")
+ io.close_read
+ lambda { io.send(@method) }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/library/stringio/shared/readchar.rb b/spec/ruby/library/stringio/shared/readchar.rb
new file mode 100644
index 0000000000..19194f0680
--- /dev/null
+++ b/spec/ruby/library/stringio/shared/readchar.rb
@@ -0,0 +1,29 @@
+describe :stringio_readchar, shared: true do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "correctly updates the current position" do
+ @io.send(@method)
+ @io.pos.should == 1
+
+ @io.send(@method)
+ @io.pos.should == 2
+ end
+
+ it "raises an EOFError when self is at the end" do
+ @io.pos = 7
+ lambda { @io.send(@method) }.should raise_error(EOFError)
+ end
+end
+
+describe :stringio_readchar_not_readable, shared: true do
+ it "raises an IOError" do
+ io = StringIO.new("a b c d e", "w")
+ lambda { io.send(@method) }.should raise_error(IOError)
+
+ io = StringIO.new("a b c d e")
+ io.close_read
+ lambda { io.send(@method) }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/library/stringio/shared/sysread.rb b/spec/ruby/library/stringio/shared/sysread.rb
new file mode 100644
index 0000000000..9800b2339b
--- /dev/null
+++ b/spec/ruby/library/stringio/shared/sysread.rb
@@ -0,0 +1,15 @@
+describe :stringio_sysread_length, :shared => true do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "returns an empty String when passed 0 and no data remains" do
+ @io.send(@method, 8).should == "example"
+ @io.send(@method, 0).should == ""
+ end
+
+ it "raises an EOFError when passed length > 0 and no data remains" do
+ @io.read.should == "example"
+ lambda { @io.sysread(1) }.should raise_error(EOFError)
+ end
+end
diff --git a/spec/ruby/library/stringio/shared/tell.rb b/spec/ruby/library/stringio/shared/tell.rb
new file mode 100644
index 0000000000..852c51c192
--- /dev/null
+++ b/spec/ruby/library/stringio/shared/tell.rb
@@ -0,0 +1,12 @@
+describe :stringio_tell, shared: true do
+ before :each do
+ @io = StringIOSpecs.build
+ end
+
+ it "returns the current byte offset" do
+ @io.getc
+ @io.send(@method).should == 1
+ @io.read(7)
+ @io.send(@method).should == 8
+ end
+end
diff --git a/spec/ruby/library/stringio/shared/write.rb b/spec/ruby/library/stringio/shared/write.rb
new file mode 100644
index 0000000000..bcb548bbd0
--- /dev/null
+++ b/spec/ruby/library/stringio/shared/write.rb
@@ -0,0 +1,87 @@
+describe :stringio_write, shared: true do
+ before :each do
+ @io = StringIO.new('12345')
+ end
+
+ it "tries to convert the passed Object to a String using #to_s" do
+ obj = mock("to_s")
+ obj.should_receive(:to_s).and_return("to_s")
+ @io.send(@method, obj)
+ @io.string.should == "to_s5"
+ end
+end
+
+describe :stringio_write_string, shared: true do
+ before :each do
+ @io = StringIO.new('12345')
+ end
+
+ # TODO: RDoc says that #write appends at the current position.
+ it "writes the passed String at the current buffer position" do
+ @io.pos = 2
+ @io.send(@method, 'x').should == 1
+ @io.string.should == '12x45'
+ @io.send(@method, 7).should == 1
+ @io.string.should == '12x75'
+ end
+
+ it "pads self with \\000 when the current position is after the end" do
+ @io.pos = 8
+ @io.send(@method, 'x')
+ @io.string.should == "12345\000\000\000x"
+ @io.send(@method, 9)
+ @io.string.should == "12345\000\000\000x9"
+ end
+
+ it "returns the number of bytes written" do
+ @io.send(@method, '').should == 0
+ @io.send(@method, nil).should == 0
+ str = "1" * 100
+ @io.send(@method, str).should == 100
+ end
+
+ it "updates self's position" do
+ @io.send(@method, 'test')
+ @io.pos.should eql(4)
+ end
+
+ it "taints self's String when the passed argument is tainted" do
+ @io.send(@method, "test".taint)
+ @io.string.tainted?.should be_true
+ end
+
+ it "does not taint self when the passed argument is tainted" do
+ @io.send(@method, "test".taint)
+ @io.tainted?.should be_false
+ end
+end
+
+describe :stringio_write_not_writable, shared: true do
+ it "raises an IOError" do
+ io = StringIO.new("test", "r")
+ lambda { io.send(@method, "test") }.should raise_error(IOError)
+
+ io = StringIO.new("test")
+ io.close_write
+ lambda { io.send(@method, "test") }.should raise_error(IOError)
+ end
+end
+
+describe :stringio_write_append, shared: true do
+ before :each do
+ @io = StringIO.new("example", "a")
+ end
+
+ it "appends the passed argument to the end of self" do
+ @io.send(@method, ", just testing")
+ @io.string.should == "example, just testing"
+
+ @io.send(@method, " and more testing")
+ @io.string.should == "example, just testing and more testing"
+ end
+
+ it "correctly updates self's position" do
+ @io.send(@method, ", testing")
+ @io.pos.should eql(16)
+ end
+end
diff --git a/spec/ruby/library/stringio/size_spec.rb b/spec/ruby/library/stringio/size_spec.rb
new file mode 100644
index 0000000000..f674d22db9
--- /dev/null
+++ b/spec/ruby/library/stringio/size_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/length'
+
+describe "StringIO#size" do
+ it_behaves_like :stringio_length, :size
+end
diff --git a/spec/ruby/library/stringio/string_spec.rb b/spec/ruby/library/stringio/string_spec.rb
new file mode 100644
index 0000000000..384986c161
--- /dev/null
+++ b/spec/ruby/library/stringio/string_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#string" do
+ it "returns the underlying string" do
+ io = StringIO.new(str = "hello")
+ io.string.should equal(str)
+ end
+end
+
+describe "StringIO#string=" do
+ before :each do
+ @io = StringIO.new("example\nstring")
+ end
+
+ it "returns the passed String" do
+ str = "test"
+ (@io.string = str).should equal(str)
+ end
+
+ it "changes the underlying string" do
+ str = "hello"
+ @io.string = str
+ @io.string.should equal(str)
+ end
+
+ it "resets the position" do
+ @io.pos = 1
+ @io.string = "other"
+ @io.pos.should eql(0)
+ end
+
+ it "resets the line number" do
+ @io.lineno = 1
+ @io.string = "other"
+ @io.lineno.should eql(0)
+ end
+
+ it "tries to convert the passed Object to a String using #to_str" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return("to_str")
+
+ @io.string = obj
+ @io.string.should == "to_str"
+ end
+
+ it "raises a TypeError when the passed Object can't be converted to an Integer" do
+ lambda { @io.seek(Object.new) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/stringio/stringio_spec.rb b/spec/ruby/library/stringio/stringio_spec.rb
new file mode 100644
index 0000000000..5ef42b3390
--- /dev/null
+++ b/spec/ruby/library/stringio/stringio_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require "stringio"
+
+describe "StringIO" do
+ it "includes the Enumerable module" do
+ StringIO.should include(Enumerable)
+ end
+end
diff --git a/spec/ruby/library/stringio/sync_spec.rb b/spec/ruby/library/stringio/sync_spec.rb
new file mode 100644
index 0000000000..e717a5697b
--- /dev/null
+++ b/spec/ruby/library/stringio/sync_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#sync" do
+ it "returns true" do
+ StringIO.new('').sync.should be_true
+ end
+end
+
+describe "StringIO#sync=" do
+ before :each do
+ @io = StringIO.new('')
+ end
+
+ it "does not change 'sync' status" do
+ @io.sync = false
+ @io.sync.should be_true
+ end
+end
diff --git a/spec/ruby/library/stringio/sysread_spec.rb b/spec/ruby/library/stringio/sysread_spec.rb
new file mode 100644
index 0000000000..ee69801444
--- /dev/null
+++ b/spec/ruby/library/stringio/sysread_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require "stringio"
+require_relative 'shared/read'
+
+describe "StringIO#sysread when passed length, buffer" do
+ it_behaves_like :stringio_read, :sysread
+end
+
+describe "StringIO#sysread when passed [length]" do
+ it_behaves_like :stringio_read_length, :sysread
+end
+
+describe "StringIO#sysread when passed no arguments" do
+ it_behaves_like :stringio_read_no_arguments, :sysread
+
+ it "returns an empty String if at EOF" do
+ @io.sysread.should == "example"
+ @io.sysread.should == ""
+ end
+end
+
+describe "StringIO#sysread when self is not readable" do
+ it_behaves_like :stringio_read_not_readable, :sysread
+end
+
+describe "StringIO#sysread when passed nil" do
+ it_behaves_like :stringio_read_nil, :sysread
+
+ it "returns an empty String if at EOF" do
+ @io.sysread(nil).should == "example"
+ @io.sysread(nil).should == ""
+ end
+end
+
+describe "StringIO#sysread when passed [length]" do
+ before :each do
+ @io = StringIO.new("example")
+ end
+
+ it "raises an EOFError when self's position is at the end" do
+ @io.pos = 7
+ lambda { @io.sysread(10) }.should raise_error(EOFError)
+ end
+
+ it "returns an empty String when length is 0" do
+ @io.sysread(0).should == ""
+ end
+end
diff --git a/spec/ruby/library/stringio/syswrite_spec.rb b/spec/ruby/library/stringio/syswrite_spec.rb
new file mode 100644
index 0000000000..c4891e669b
--- /dev/null
+++ b/spec/ruby/library/stringio/syswrite_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/write'
+
+describe "StringIO#syswrite when passed [Object]" do
+ it_behaves_like :stringio_write, :syswrite
+end
+
+describe "StringIO#syswrite when passed [String]" do
+ it_behaves_like :stringio_write_string, :syswrite
+end
+
+describe "StringIO#syswrite when self is not writable" do
+ it_behaves_like :stringio_write_not_writable, :syswrite
+end
+
+describe "StringIO#syswrite when in append mode" do
+ it_behaves_like :stringio_write_append, :syswrite
+end
diff --git a/spec/ruby/library/stringio/tell_spec.rb b/spec/ruby/library/stringio/tell_spec.rb
new file mode 100644
index 0000000000..8350ee6f4d
--- /dev/null
+++ b/spec/ruby/library/stringio/tell_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/tell'
+
+describe "StringIO#tell" do
+ it_behaves_like :stringio_tell, :tell
+end
diff --git a/spec/ruby/library/stringio/truncate_spec.rb b/spec/ruby/library/stringio/truncate_spec.rb
new file mode 100644
index 0000000000..d0d61af70a
--- /dev/null
+++ b/spec/ruby/library/stringio/truncate_spec.rb
@@ -0,0 +1,70 @@
+require_relative '../../spec_helper'
+require "stringio"
+
+describe "StringIO#truncate when passed [length]" do
+ before :each do
+ @io = StringIO.new('123456789')
+ end
+
+ # TODO: Report to Ruby-Core: The RDoc says it always returns 0
+ it "returns the passed length" do
+ @io.truncate(4).should eql(4)
+ @io.truncate(10).should eql(10)
+ end
+
+ it "truncated the underlying string down to the passed length" do
+ @io.truncate(4)
+ @io.string.should == "1234"
+ end
+
+ it "does not create a copy of the underlying string" do
+ io = StringIO.new(str = "123456789")
+ io.truncate(4)
+ io.string.should equal(str)
+ end
+
+ it "does not change the position" do
+ @io.pos = 7
+ @io.truncate(4)
+ @io.pos.should eql(7)
+ end
+
+ it "can grow a string to a larger size, padding it with \\000" do
+ @io.truncate(12)
+ @io.string.should == "123456789\000\000\000"
+ end
+
+ it "raises an Errno::EINVAL when the passed length is negative" do
+ lambda { @io.truncate(-1) }.should raise_error(Errno::EINVAL)
+ lambda { @io.truncate(-10) }.should raise_error(Errno::EINVAL)
+ end
+
+ it "tries to convert the passed length to an Integer using #to_int" do
+ obj = mock("to_int")
+ obj.should_receive(:to_int).and_return(4)
+
+ @io.truncate(obj)
+ @io.string.should == "1234"
+ end
+
+ it "returns the passed length Object, NOT the result of #to_int" do
+ obj = mock("to_int")
+ obj.should_receive(:to_int).and_return(4)
+ @io.truncate(obj).should equal(obj)
+ end
+
+ it "raises a TypeError when the passed length can't be converted to an Integer" do
+ lambda { @io.truncate(Object.new) }.should raise_error(TypeError)
+ end
+end
+
+describe "StringIO#truncate when self is not writable" do
+ it "raises an IOError" do
+ io = StringIO.new("test", "r")
+ lambda { io.truncate(2) }.should raise_error(IOError)
+
+ io = StringIO.new("test")
+ io.close_write
+ lambda { io.truncate(2) }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/library/stringio/tty_spec.rb b/spec/ruby/library/stringio/tty_spec.rb
new file mode 100644
index 0000000000..c6293dcbd7
--- /dev/null
+++ b/spec/ruby/library/stringio/tty_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/isatty'
+
+describe "StringIO#tty?" do
+ it_behaves_like :stringio_isatty, :tty?
+end
diff --git a/spec/ruby/library/stringio/ungetbyte_spec.rb b/spec/ruby/library/stringio/ungetbyte_spec.rb
new file mode 100644
index 0000000000..701463e621
--- /dev/null
+++ b/spec/ruby/library/stringio/ungetbyte_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'stringio'
+
+describe "StringIO#ungetbyte" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/stringio/ungetc_spec.rb b/spec/ruby/library/stringio/ungetc_spec.rb
new file mode 100644
index 0000000000..8a9205f390
--- /dev/null
+++ b/spec/ruby/library/stringio/ungetc_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "StringIO#ungetc when passed [char]" do
+ before :each do
+ @io = StringIO.new('1234')
+ end
+
+ it "writes the passed char before the current position" do
+ @io.pos = 1
+ @io.ungetc(?A)
+ @io.string.should == 'A234'
+ end
+
+ it "returns nil" do
+ @io.pos = 1
+ @io.ungetc(?A).should be_nil
+ end
+
+ it "decreases the current position by one" do
+ @io.pos = 2
+ @io.ungetc(?A)
+ @io.pos.should eql(1)
+ end
+
+ it "pads with \\000 when the current position is after the end" do
+ @io.pos = 15
+ @io.ungetc(?A)
+ @io.string.should == "1234\000\000\000\000\000\000\000\000\000\000A"
+ end
+
+ it "tries to convert the passed argument to an String using #to_str" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(?A)
+
+ @io.pos = 1
+ @io.ungetc(obj)
+ @io.string.should == "A234"
+ end
+
+ it "raises a TypeError when the passed length can't be converted to an Integer or String" do
+ lambda { @io.ungetc(Object.new) }.should raise_error(TypeError)
+ end
+end
+
+describe "StringIO#ungetc when self is not readable" do
+ it "raises an IOError" do
+ io = StringIO.new("test", "w")
+ io.pos = 1
+ lambda { io.ungetc(?A) }.should raise_error(IOError)
+
+ io = StringIO.new("test")
+ io.pos = 1
+ io.close_read
+ lambda { io.ungetc(?A) }.should raise_error(IOError)
+ end
+end
+
+# Note: This is incorrect.
+#
+# describe "StringIO#ungetc when self is not writable" do
+# it "raises an IOError" do
+# io = StringIO.new("test", "r")
+# io.pos = 1
+# lambda { io.ungetc(?A) }.should raise_error(IOError)
+#
+# io = StringIO.new("test")
+# io.pos = 1
+# io.close_write
+# lambda { io.ungetc(?A) }.should raise_error(IOError)
+# end
+# end
diff --git a/spec/ruby/library/stringio/write_nonblock_spec.rb b/spec/ruby/library/stringio/write_nonblock_spec.rb
new file mode 100644
index 0000000000..4f4c5039fe
--- /dev/null
+++ b/spec/ruby/library/stringio/write_nonblock_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/write'
+
+describe "StringIO#write_nonblock when passed [Object]" do
+ it_behaves_like :stringio_write, :write_nonblock
+end
+
+describe "StringIO#write_nonblock when passed [String]" do
+ it_behaves_like :stringio_write_string, :write_nonblock
+end
+
+describe "StringIO#write_nonblock when self is not writable" do
+ it_behaves_like :stringio_write_not_writable, :write_nonblock
+end
+
+describe "StringIO#write_nonblock when in append mode" do
+ it_behaves_like :stringio_write_append, :write_nonblock
+end
diff --git a/spec/ruby/library/stringio/write_spec.rb b/spec/ruby/library/stringio/write_spec.rb
new file mode 100644
index 0000000000..3f755c32b4
--- /dev/null
+++ b/spec/ruby/library/stringio/write_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/write'
+
+describe "StringIO#write when passed [Object]" do
+ it_behaves_like :stringio_write, :write
+end
+
+describe "StringIO#write when passed [String]" do
+ it_behaves_like :stringio_write_string, :write
+end
+
+describe "StringIO#write when self is not writable" do
+ it_behaves_like :stringio_write_not_writable, :write
+end
+
+describe "StringIO#write when in append mode" do
+ it_behaves_like :stringio_write_append, :write
+end
diff --git a/spec/ruby/library/stringscanner/append_spec.rb b/spec/ruby/library/stringscanner/append_spec.rb
new file mode 100644
index 0000000000..378c62e2b1
--- /dev/null
+++ b/spec/ruby/library/stringscanner/append_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/concat'
+require 'strscan'
+
+describe "StringScanner#<<" do
+ it_behaves_like :strscan_concat, :<<
+end
+
+describe "StringScanner#<< when passed a Fixnum" do
+ it_behaves_like :strscan_concat_fixnum, :<<
+end
diff --git a/spec/ruby/library/stringscanner/beginning_of_line_spec.rb b/spec/ruby/library/stringscanner/beginning_of_line_spec.rb
new file mode 100644
index 0000000000..3f6f0da75f
--- /dev/null
+++ b/spec/ruby/library/stringscanner/beginning_of_line_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/bol'
+require 'strscan'
+
+describe "StringScanner#beginning_of_line?" do
+ it_behaves_like :strscan_bol, :beginning_of_line?
+end
diff --git a/spec/ruby/library/stringscanner/bol_spec.rb b/spec/ruby/library/stringscanner/bol_spec.rb
new file mode 100644
index 0000000000..d31766e0e2
--- /dev/null
+++ b/spec/ruby/library/stringscanner/bol_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/bol'
+require 'strscan'
+
+describe "StringScanner#bol?" do
+ it_behaves_like :strscan_bol, :bol?
+end
diff --git a/spec/ruby/library/stringscanner/check_spec.rb b/spec/ruby/library/stringscanner/check_spec.rb
new file mode 100644
index 0000000000..c7f788f0b3
--- /dev/null
+++ b/spec/ruby/library/stringscanner/check_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#check" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the value that scan would return, without advancing the scan pointer" do
+ @s.check(/This/).should == "This"
+ @s.matched.should == "This"
+ @s.pos.should == 0
+ @s.check(/is/).should == nil
+ @s.matched.should == nil
+ end
+end
diff --git a/spec/ruby/library/stringscanner/check_until_spec.rb b/spec/ruby/library/stringscanner/check_until_spec.rb
new file mode 100644
index 0000000000..e92ae5a2e9
--- /dev/null
+++ b/spec/ruby/library/stringscanner/check_until_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#check_until" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the same value of scan_until, but don't advances the scan pointer" do
+ @s.check_until(/a/).should == "This is a"
+ @s.pos.should == 0
+ @s.matched.should == "a"
+ @s.check_until(/test/).should == "This is a test"
+ end
+end
diff --git a/spec/ruby/library/stringscanner/clear_spec.rb b/spec/ruby/library/stringscanner/clear_spec.rb
new file mode 100644
index 0000000000..70c60f7039
--- /dev/null
+++ b/spec/ruby/library/stringscanner/clear_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require_relative 'shared/terminate'
+require 'strscan'
+
+describe "StringScanner#clear" do
+ it_behaves_like :strscan_terminate, :clear
+
+ it "warns in verbose mode that the method is obsolete" do
+ s = StringScanner.new("abc")
+ lambda {
+ $VERBOSE = true
+ s.clear
+ }.should complain(/clear.*obsolete.*terminate/)
+
+ lambda {
+ $VERBOSE = false
+ s.clear
+ }.should_not complain
+ end
+end
diff --git a/spec/ruby/library/stringscanner/concat_spec.rb b/spec/ruby/library/stringscanner/concat_spec.rb
new file mode 100644
index 0000000000..17a699dd2e
--- /dev/null
+++ b/spec/ruby/library/stringscanner/concat_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/concat'
+require 'strscan'
+
+describe "StringScanner#concat" do
+ it_behaves_like :strscan_concat, :concat
+end
+
+describe "StringScanner#concat when passed a Fixnum" do
+ it_behaves_like :strscan_concat_fixnum, :concat
+end
diff --git a/spec/ruby/library/stringscanner/dup_spec.rb b/spec/ruby/library/stringscanner/dup_spec.rb
new file mode 100644
index 0000000000..3b426f138e
--- /dev/null
+++ b/spec/ruby/library/stringscanner/dup_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#dup" do
+ before :each do
+ @string = "this is a test"
+ @orig_s = StringScanner.new(@string)
+ end
+
+ it "copies the passed StringScanner's content to self" do
+ s = @orig_s.dup
+ s.string.should == @string
+ end
+
+ it "copies the passed StringSCanner's position to self" do
+ @orig_s.pos = 5
+ s = @orig_s.dup
+ s.pos.should eql(5)
+ end
+
+ it "copies previous match state" do
+ @orig_s.scan(/\w+/)
+ @orig_s.scan(/\s/)
+
+ @orig_s.pre_match.should == "this"
+
+ s = @orig_s.dup
+ s.pre_match.should == "this"
+
+ s.unscan
+ s.scan(/\s/).should == " "
+ end
+
+ it "copies the passed StringScanner scan pointer to self" do
+ @orig_s.terminate
+ s = @orig_s.dup
+ s.eos?.should be_true
+ end
+end
diff --git a/spec/ruby/library/stringscanner/element_reference_spec.rb b/spec/ruby/library/stringscanner/element_reference_spec.rb
new file mode 100644
index 0000000000..c3fab16bbc
--- /dev/null
+++ b/spec/ruby/library/stringscanner/element_reference_spec.rb
@@ -0,0 +1,60 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#[]" do
+ before :each do
+ @s = StringScanner.new("Fri Jun 13 2008 22:43")
+ end
+
+ it "returns nil if there is no current match" do
+ @s[0].should be_nil
+ end
+
+ it "returns the n-th subgroup in the most recent match" do
+ @s.scan(/(\w+) (\w+) (\d+) /)
+ @s[0].should == "Fri Jun 13 "
+ @s[1].should == "Fri"
+ @s[2].should == "Jun"
+ @s[3].should == "13"
+ @s[-3].should == "Fri"
+ @s[-2].should == "Jun"
+ @s[-1].should == "13"
+ end
+
+ it "returns nil if index is outside of self" do
+ @s.scan(/(\w+) (\w+) (\d+) /)
+ @s[5].should == nil
+ @s[-5].should == nil
+ end
+
+ it "calls to_int on the given index" do
+ @s.scan(/(\w+) (\w+) (\d+) /)
+ @s[0.5].should == "Fri Jun 13 "
+ end
+
+ it "raises a TypeError if the given index is nil" do
+ @s.scan(/(\w+) (\w+) (\d+) /)
+ lambda { @s[nil]}.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when a Range is as argument" do
+ @s.scan(/(\w+) (\w+) (\d+) /)
+ lambda { @s[0..2]}.should raise_error(TypeError)
+ end
+
+ it "raises a IndexError when there's no named capture" do
+ @s.scan(/(\w+) (\w+) (\d+) /)
+ lambda { @s["wday"]}.should raise_error(IndexError)
+ lambda { @s[:wday]}.should raise_error(IndexError)
+ end
+
+ it "returns named capture" do
+ @s.scan(/(?<wday>\w+) (?<month>\w+) (?<day>\d+) /)
+ @s["wday"].should == "Fri"
+ @s["month"].should == "Jun"
+ @s["day"].should == "13"
+ @s[:wday].should == "Fri"
+ @s[:month].should == "Jun"
+ @s[:day].should == "13"
+ end
+end
diff --git a/spec/ruby/library/stringscanner/empty_spec.rb b/spec/ruby/library/stringscanner/empty_spec.rb
new file mode 100644
index 0000000000..bba51364c8
--- /dev/null
+++ b/spec/ruby/library/stringscanner/empty_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require_relative 'shared/eos'
+require 'strscan'
+
+describe "StringScanner#empty?" do
+ it_behaves_like :strscan_eos, :empty?
+
+ it "warns in verbose mode that the method is obsolete" do
+ s = StringScanner.new("abc")
+ lambda {
+ $VERBOSE = true
+ s.empty?
+ }.should complain(/empty?.*obsolete.*eos?/)
+
+ lambda {
+ $VERBOSE = false
+ s.empty?
+ }.should_not complain
+ end
+end
diff --git a/spec/ruby/library/stringscanner/eos_spec.rb b/spec/ruby/library/stringscanner/eos_spec.rb
new file mode 100644
index 0000000000..b58ee1e473
--- /dev/null
+++ b/spec/ruby/library/stringscanner/eos_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/eos'
+require 'strscan'
+
+describe "StringScanner#eos?" do
+ it_behaves_like :strscan_eos, :eos?
+end
diff --git a/spec/ruby/library/stringscanner/exist_spec.rb b/spec/ruby/library/stringscanner/exist_spec.rb
new file mode 100644
index 0000000000..beafadc07b
--- /dev/null
+++ b/spec/ruby/library/stringscanner/exist_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#exist?" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the index of the first occurrence of the given pattern" do
+ @s.exist?(/s/).should == 4
+ @s.scan(/This is/)
+ @s.exist?(/s/).should == 6
+ end
+
+ it "returns 0 if the pattern is empty" do
+ @s.exist?(//).should == 0
+ end
+
+ it "returns nil if the pattern isn't found in the string" do
+ @s.exist?(/S/).should == nil
+ @s.scan(/This is/)
+ @s.exist?(/i/).should == nil
+ end
+end
diff --git a/spec/ruby/library/stringscanner/get_byte_spec.rb b/spec/ruby/library/stringscanner/get_byte_spec.rb
new file mode 100644
index 0000000000..29e2f557de
--- /dev/null
+++ b/spec/ruby/library/stringscanner/get_byte_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/get_byte'
+require 'strscan'
+
+describe "StringScanner#get_byte" do
+ it_behaves_like :strscan_get_byte, :get_byte
+end
diff --git a/spec/ruby/library/stringscanner/getbyte_spec.rb b/spec/ruby/library/stringscanner/getbyte_spec.rb
new file mode 100644
index 0000000000..a71c5ee781
--- /dev/null
+++ b/spec/ruby/library/stringscanner/getbyte_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require_relative 'shared/get_byte'
+require_relative 'shared/extract_range'
+require 'strscan'
+
+describe "StringScanner#getbyte" do
+ it_behaves_like :strscan_get_byte, :getbyte
+
+ it "warns in verbose mode that the method is obsolete" do
+ s = StringScanner.new("abc")
+ lambda {
+ $VERBOSE = true
+ s.getbyte
+ }.should complain(/getbyte.*obsolete.*get_byte/)
+
+ lambda {
+ $VERBOSE = false
+ s.getbyte
+ }.should_not complain
+ end
+
+ it_behaves_like :extract_range, :getbyte
+end
diff --git a/spec/ruby/library/stringscanner/getch_spec.rb b/spec/ruby/library/stringscanner/getch_spec.rb
new file mode 100644
index 0000000000..a6be0d4221
--- /dev/null
+++ b/spec/ruby/library/stringscanner/getch_spec.rb
@@ -0,0 +1,35 @@
+# -*- encoding: binary -*-
+require_relative '../../spec_helper'
+require_relative 'shared/extract_range'
+require 'strscan'
+
+describe "StringScanner#getch" do
+ it "scans one character and returns it" do
+ s = StringScanner.new('abc')
+ s.getch.should == "a"
+ s.getch.should == "b"
+ s.getch.should == "c"
+ end
+
+ it "is multi-byte character sensitive" do
+ # Japanese hiragana "A" in EUC-JP
+ src = "\244\242".force_encoding("euc-jp")
+
+ s = StringScanner.new(src)
+ s.getch.should == src
+ end
+
+ it "returns nil at the end of the string" do
+ # empty string case
+ s = StringScanner.new('')
+ s.getch.should == nil
+ s.getch.should == nil
+
+ # non-empty string case
+ s = StringScanner.new('a')
+ s.getch # skip one
+ s.getch.should == nil
+ end
+
+ it_behaves_like :extract_range, :getch
+end
diff --git a/spec/ruby/library/stringscanner/initialize_spec.rb b/spec/ruby/library/stringscanner/initialize_spec.rb
new file mode 100644
index 0000000000..07f71572ce
--- /dev/null
+++ b/spec/ruby/library/stringscanner/initialize_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#initialize" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "is a private method" do
+ StringScanner.should have_private_instance_method(:initialize)
+ end
+
+ it "returns an instance of StringScanner" do
+ @s.should be_kind_of(StringScanner)
+ @s.tainted?.should be_false
+ @s.eos?.should be_false
+ end
+
+ it "converts the argument into a string using #to_str" do
+ m = mock(:str)
+
+ s = "test"
+ m.should_receive(:to_str).and_return(s)
+
+ scan = StringScanner.new(m)
+ scan.string.should == s
+ end
+end
diff --git a/spec/ruby/library/stringscanner/inspect_spec.rb b/spec/ruby/library/stringscanner/inspect_spec.rb
new file mode 100644
index 0000000000..ff6b97eb91
--- /dev/null
+++ b/spec/ruby/library/stringscanner/inspect_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#inspect" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns a String object" do
+ @s.inspect.should be_kind_of(String)
+ end
+
+ it "returns a string that represents the StringScanner object" do
+ @s.inspect.should == "#<StringScanner 0/14 @ \"This ...\">"
+ @s.scan_until(/is/)
+ @s.inspect.should == "#<StringScanner 4/14 \"This\" @ \" is a...\">"
+ @s.terminate
+ @s.inspect.should == "#<StringScanner fin>"
+ end
+end
diff --git a/spec/ruby/library/stringscanner/match_spec.rb b/spec/ruby/library/stringscanner/match_spec.rb
new file mode 100644
index 0000000000..ec59680914
--- /dev/null
+++ b/spec/ruby/library/stringscanner/match_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#match?" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the length of the match and the scan pointer is not advanced" do
+ @s.match?(/\w+/).should == 4
+ @s.match?(/\w+/).should == 4
+ @s.pos.should == 0
+ end
+
+ it "returns nil if there's no match" do
+ @s.match?(/\d+/).should == nil
+ @s.match?(/\s+/).should == nil
+ end
+
+ it "effects pre_match" do
+ @s.scan(/\w+/)
+ @s.scan(/\s/)
+
+ @s.pre_match.should == "This"
+ @s.match?(/\w+/)
+ @s.pre_match.should == "This "
+ end
+end
diff --git a/spec/ruby/library/stringscanner/matched_size_spec.rb b/spec/ruby/library/stringscanner/matched_size_spec.rb
new file mode 100644
index 0000000000..a36bd3aafe
--- /dev/null
+++ b/spec/ruby/library/stringscanner/matched_size_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/matched_size'
+require 'strscan'
+
+describe "StringScanner#matched_size" do
+ it_behaves_like :strscan_matched_size, :matched_size
+end
diff --git a/spec/ruby/library/stringscanner/matched_spec.rb b/spec/ruby/library/stringscanner/matched_spec.rb
new file mode 100644
index 0000000000..c020bd3eae
--- /dev/null
+++ b/spec/ruby/library/stringscanner/matched_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'shared/extract_range_matched'
+require 'strscan'
+
+describe "StringScanner#matched" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the last matched string" do
+ @s.match?(/\w+/)
+ @s.matched.should == "This"
+ @s.getch
+ @s.matched.should == "T"
+ @s.get_byte
+ @s.matched.should == "h"
+ end
+
+ it "returns nil if there's no match" do
+ @s.match?(/\d+/)
+ @s.matched.should == nil
+ end
+
+ it_behaves_like :extract_range_matched, :matched
+end
+
+describe "StringScanner#matched?" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns true if the last match was successful" do
+ @s.match?(/\w+/)
+ @s.matched?.should be_true
+ end
+
+ it "returns false if there's no match" do
+ @s.match?(/\d+/)
+ @s.matched?.should be_false
+ end
+end
diff --git a/spec/ruby/library/stringscanner/must_C_version_spec.rb b/spec/ruby/library/stringscanner/must_C_version_spec.rb
new file mode 100644
index 0000000000..fcc5b596f6
--- /dev/null
+++ b/spec/ruby/library/stringscanner/must_C_version_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner.must_C_version" do
+ it "returns self" do
+ StringScanner.must_C_version.should == StringScanner
+ end
+end
diff --git a/spec/ruby/library/stringscanner/peek_spec.rb b/spec/ruby/library/stringscanner/peek_spec.rb
new file mode 100644
index 0000000000..cbb5630ff9
--- /dev/null
+++ b/spec/ruby/library/stringscanner/peek_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/peek'
+require 'strscan'
+
+describe "StringScanner#peek" do
+ it_behaves_like :strscan_peek, :peek
+end
diff --git a/spec/ruby/library/stringscanner/peep_spec.rb b/spec/ruby/library/stringscanner/peep_spec.rb
new file mode 100644
index 0000000000..887522a8ae
--- /dev/null
+++ b/spec/ruby/library/stringscanner/peep_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require_relative 'shared/peek'
+require 'strscan'
+
+describe "StringScanner#peep" do
+ it_behaves_like :strscan_peek, :peep
+
+ it "warns in verbose mode that the method is obsolete" do
+ s = StringScanner.new("abc")
+ lambda {
+ $VERBOSE = true
+ s.peep(1)
+ }.should complain(/peep.*obsolete.*peek/)
+
+ lambda {
+ $VERBOSE = false
+ s.peep(1)
+ }.should_not complain
+ end
+end
diff --git a/spec/ruby/library/stringscanner/pointer_spec.rb b/spec/ruby/library/stringscanner/pointer_spec.rb
new file mode 100644
index 0000000000..bc0c0c50b7
--- /dev/null
+++ b/spec/ruby/library/stringscanner/pointer_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/pos'
+require 'strscan'
+
+describe "StringScanner#pointer" do
+ it_behaves_like :strscan_pos, :pointer
+end
+
+describe "StringScanner#pointer=" do
+ it_behaves_like :strscan_pos_set, :pointer=
+end
diff --git a/spec/ruby/library/stringscanner/pos_spec.rb b/spec/ruby/library/stringscanner/pos_spec.rb
new file mode 100644
index 0000000000..275fecf0f3
--- /dev/null
+++ b/spec/ruby/library/stringscanner/pos_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/pos'
+require 'strscan'
+
+describe "StringScanner#pos" do
+ it_behaves_like :strscan_pos, :pos
+end
+
+describe "StringScanner#pos=" do
+ it_behaves_like :strscan_pos_set, :pos=
+end
diff --git a/spec/ruby/library/stringscanner/post_match_spec.rb b/spec/ruby/library/stringscanner/post_match_spec.rb
new file mode 100644
index 0000000000..720dc3530d
--- /dev/null
+++ b/spec/ruby/library/stringscanner/post_match_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require_relative 'shared/extract_range_matched'
+require 'strscan'
+
+describe "StringScanner#post_match" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the post-match (in the regular expression sense) of the last scan" do
+ @s.post_match.should == nil
+ @s.scan(/\w+\s/)
+ @s.post_match.should == "is a test"
+ @s.getch
+ @s.post_match.should == "s a test"
+ @s.get_byte
+ @s.post_match.should == " a test"
+ @s.get_byte
+ @s.post_match.should == "a test"
+ end
+
+ it "returns nil if there's no match" do
+ @s.scan(/\s+/)
+ @s.post_match.should == nil
+ end
+
+ it_behaves_like :extract_range_matched, :post_match
+end
diff --git a/spec/ruby/library/stringscanner/pre_match_spec.rb b/spec/ruby/library/stringscanner/pre_match_spec.rb
new file mode 100644
index 0000000000..1c2c511d5c
--- /dev/null
+++ b/spec/ruby/library/stringscanner/pre_match_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'shared/extract_range_matched'
+require 'strscan'
+
+describe "StringScanner#pre_match" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the pre-match (in the regular expression sense) of the last scan" do
+ @s.pre_match.should == nil
+ @s.scan(/\w+\s/)
+ @s.pre_match.should == ""
+ @s.getch
+ @s.pre_match.should == "This "
+ @s.get_byte
+ @s.pre_match.should == "This i"
+ @s.get_byte
+ @s.pre_match.should == "This is"
+ end
+
+ it "returns nil if there's no match" do
+ @s.scan(/\s+/)
+ @s.pre_match.should == nil
+ end
+
+ it "is more than just the data from the last match" do
+ @s.scan(/\w+/)
+ @s.scan_until(/a te/)
+ @s.pre_match.should == "This is "
+ end
+
+ it "is not changed when the scanner's position changes" do
+ @s.scan_until(/\s+/)
+ @s.pre_match.should == "This"
+ @s.pos -= 1
+ @s.pre_match.should == "This"
+ end
+
+ it_behaves_like :extract_range_matched, :pre_match
+end
diff --git a/spec/ruby/library/stringscanner/reset_spec.rb b/spec/ruby/library/stringscanner/reset_spec.rb
new file mode 100644
index 0000000000..a10ca2d838
--- /dev/null
+++ b/spec/ruby/library/stringscanner/reset_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#reset" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "reset the scan pointer and clear matching data" do
+ @s.scan(/This/)
+ @s.reset
+ @s.pos.should == 0
+ @s.matched.should == nil
+ end
+end
diff --git a/spec/ruby/library/stringscanner/rest_size_spec.rb b/spec/ruby/library/stringscanner/rest_size_spec.rb
new file mode 100644
index 0000000000..e62e3a8f8c
--- /dev/null
+++ b/spec/ruby/library/stringscanner/rest_size_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/rest_size'
+require 'strscan'
+
+describe "StringScanner#rest_size" do
+ it_behaves_like :strscan_rest_size, :rest_size
+end
diff --git a/spec/ruby/library/stringscanner/rest_spec.rb b/spec/ruby/library/stringscanner/rest_spec.rb
new file mode 100644
index 0000000000..67072f880d
--- /dev/null
+++ b/spec/ruby/library/stringscanner/rest_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require_relative 'shared/extract_range_matched'
+require 'strscan'
+
+describe "StringScanner#rest" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the rest of the string" do
+ @s.scan(/This\s+/)
+ @s.rest.should == "is a test"
+ end
+
+ it "returns self in the reset position" do
+ @s.reset
+ @s.rest.should == @s.string
+ end
+
+ it "returns an empty string in the terminate position" do
+ @s.terminate
+ @s.rest.should == ""
+ end
+
+ it_behaves_like :extract_range_matched, :rest
+
+end
+
+describe "StringScanner#rest?" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns true if there is more data in the string" do
+ @s.rest?.should be_true
+ @s.scan(/This/)
+ @s.rest?.should be_true
+ end
+
+ it "returns false if there is no more data in the string" do
+ @s.terminate
+ @s.rest?.should be_false
+ end
+
+ it "is the opposite of eos?" do
+ @s.rest?.should_not == @s.eos?
+ end
+end
diff --git a/spec/ruby/library/stringscanner/restsize_spec.rb b/spec/ruby/library/stringscanner/restsize_spec.rb
new file mode 100644
index 0000000000..87edf35ad6
--- /dev/null
+++ b/spec/ruby/library/stringscanner/restsize_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require_relative 'shared/rest_size'
+require 'strscan'
+
+describe "StringScanner#restsize" do
+ it_behaves_like :strscan_rest_size, :restsize
+
+ it "warns in verbose mode that the method is obsolete" do
+ s = StringScanner.new("abc")
+ lambda {
+ $VERBOSE = true
+ s.restsize
+ }.should complain(/restsize.*obsolete.*rest_size/)
+
+ lambda {
+ $VERBOSE = false
+ s.restsize
+ }.should_not complain
+ end
+end
diff --git a/spec/ruby/library/stringscanner/scan_full_spec.rb b/spec/ruby/library/stringscanner/scan_full_spec.rb
new file mode 100644
index 0000000000..ed34d7d3f6
--- /dev/null
+++ b/spec/ruby/library/stringscanner/scan_full_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#scan_full" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the number of bytes advanced" do
+ orig_pos = @s.pos
+ @s.scan_full(/This/, false, false).should == 4
+ @s.pos.should == orig_pos
+ end
+
+ it "returns the number of bytes advanced and advances the scan pointer if the second argument is true" do
+ @s.scan_full(/This/, true, false).should == 4
+ @s.pos.should == 4
+ end
+
+ it "returns the matched string if the third argument is true" do
+ orig_pos = @s.pos
+ @s.scan_full(/This/, false, true).should == "This"
+ @s.pos.should == orig_pos
+ end
+
+ it "returns the matched string if the third argument is true and advances the scan pointer if the second argument is true" do
+ @s.scan_full(/This/, true, true).should == "This"
+ @s.pos.should == 4
+ end
+end
diff --git a/spec/ruby/library/stringscanner/scan_spec.rb b/spec/ruby/library/stringscanner/scan_spec.rb
new file mode 100644
index 0000000000..23de7f07cb
--- /dev/null
+++ b/spec/ruby/library/stringscanner/scan_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#scan" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the matched string" do
+ @s.scan(/\w+/).should == "This"
+ @s.scan(/.../).should == " is"
+ @s.scan(//).should == ""
+ @s.scan(/\s+/).should == " "
+ end
+
+ it "treats ^ as matching from the beginning of the current position" do
+ @s.scan(/\w+/).should == "This"
+ @s.scan(/^\d/).should be_nil
+ @s.scan(/^\s/).should == " "
+ end
+
+ it "returns nil if there's no match" do
+ @s.scan(/\d/).should == nil
+ end
+
+ it "returns nil when there is no more to scan" do
+ @s.scan(/[\w\s]+/).should == "This is a test"
+ @s.scan(/\w+/).should be_nil
+ end
+
+ it "returns an empty string when the pattern matches empty" do
+ @s.scan(/.*/).should == "This is a test"
+ @s.scan(/.*/).should == ""
+ @s.scan(/./).should be_nil
+ end
+
+ it "raises a TypeError if pattern isn't a Regexp" do
+ lambda { @s.scan("aoeu") }.should raise_error(TypeError)
+ lambda { @s.scan(5) }.should raise_error(TypeError)
+ lambda { @s.scan(:test) }.should raise_error(TypeError)
+ lambda { @s.scan(mock('x')) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/stringscanner/scan_until_spec.rb b/spec/ruby/library/stringscanner/scan_until_spec.rb
new file mode 100644
index 0000000000..94239db50e
--- /dev/null
+++ b/spec/ruby/library/stringscanner/scan_until_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#scan_until" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the substring up to and including the end of the match" do
+ @s.scan_until(/a/).should == "This is a"
+ @s.pre_match.should == "This is "
+ @s.post_match.should == " test"
+ end
+
+ it "returns nil if there's no match" do
+ @s.scan_until(/\d/).should == nil
+ end
+
+ it "can match anchors properly" do
+ @s.scan(/T/)
+ @s.scan_until(/^h/).should == "h"
+ end
+end
diff --git a/spec/ruby/library/stringscanner/search_full_spec.rb b/spec/ruby/library/stringscanner/search_full_spec.rb
new file mode 100644
index 0000000000..da9067e012
--- /dev/null
+++ b/spec/ruby/library/stringscanner/search_full_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#search_full" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the number of bytes advanced" do
+ orig_pos = @s.pos
+ @s.search_full(/This/, false, false).should == 4
+ @s.pos.should == orig_pos
+ end
+
+ it "returns the number of bytes advanced and advances the scan pointer if the second argument is true" do
+ @s.search_full(/This/, true, false).should == 4
+ @s.pos.should == 4
+ end
+
+ it "returns the matched string if the third argument is true" do
+ orig_pos = @s.pos
+ @s.search_full(/This/, false, true).should == "This"
+ @s.pos.should == orig_pos
+ end
+
+ it "returns the matched string if the third argument is true and advances the scan pointer if the second argument is true" do
+ @s.search_full(/This/, true, true).should == "This"
+ @s.pos.should == 4
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/bol.rb b/spec/ruby/library/stringscanner/shared/bol.rb
new file mode 100644
index 0000000000..ebcdd7938f
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/bol.rb
@@ -0,0 +1,25 @@
+describe :strscan_bol, shared: true do
+ it "returns true if the scan pointer is at the beginning of the line, false otherwise" do
+ s = StringScanner.new("This is a test")
+ s.send(@method).should be_true
+ s.scan(/This/)
+ s.send(@method).should be_false
+ s.terminate
+ s.send(@method).should be_false
+
+ s = StringScanner.new("hello\nworld")
+ s.bol?.should be_true
+ s.scan(/\w+/)
+ s.bol?.should be_false
+ s.scan(/\n/)
+ s.bol?.should be_true
+ s.unscan
+ s.bol?.should be_false
+ end
+
+ it "returns true if the scan pointer is at the end of the line of an empty string." do
+ s = StringScanner.new('')
+ s.terminate
+ s.send(@method).should be_true
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/concat.rb b/spec/ruby/library/stringscanner/shared/concat.rb
new file mode 100644
index 0000000000..28788d3ff1
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/concat.rb
@@ -0,0 +1,30 @@
+describe :strscan_concat, shared: true do
+ it "concatenates the given argument to self and returns self" do
+ s = StringScanner.new("hello ")
+ s.send(@method, 'world').should == s
+ s.string.should == "hello world"
+ s.eos?.should be_false
+ end
+
+ it "raises a TypeError if the given argument can't be converted to a String" do
+ lambda { StringScanner.new('hello').send(@method, :world) }.should raise_error(TypeError)
+ lambda { StringScanner.new('hello').send(@method, mock('x')) }.should raise_error(TypeError)
+ end
+end
+
+describe :strscan_concat_fixnum, shared: true do
+ it "raises a TypeError" do
+ a = StringScanner.new("hello world")
+ lambda { a.send(@method, 333) }.should raise_error(TypeError)
+ b = StringScanner.new("")
+ lambda { b.send(@method, (256 * 3 + 64)) }.should raise_error(TypeError)
+ lambda { b.send(@method, -200) }.should raise_error(TypeError)
+ end
+
+ it "doesn't call to_int on the argument" do
+ x = mock('x')
+ x.should_not_receive(:to_int)
+
+ lambda { "".send(@method, x) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/eos.rb b/spec/ruby/library/stringscanner/shared/eos.rb
new file mode 100644
index 0000000000..ea04c764a2
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/eos.rb
@@ -0,0 +1,17 @@
+describe :strscan_eos, shared: true do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns true if the scan pointer is at the end of the string" do
+ @s.terminate
+ @s.send(@method).should be_true
+
+ s = StringScanner.new('')
+ s.send(@method).should be_true
+ end
+
+ it "returns false if the scan pointer is not at the end of the string" do
+ @s.send(@method).should be_false
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/extract_range.rb b/spec/ruby/library/stringscanner/shared/extract_range.rb
new file mode 100644
index 0000000000..7e98540b1a
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/extract_range.rb
@@ -0,0 +1,22 @@
+describe :extract_range, shared: true do
+ it "returns an instance of String when passed a String subclass" do
+ cls = Class.new(String)
+ sub = cls.new("abc")
+
+ s = StringScanner.new(sub)
+ ch = s.send(@method)
+ ch.should_not be_kind_of(cls)
+ ch.should be_an_instance_of(String)
+ end
+
+ it "taints the returned String if the input was tainted" do
+ str = 'abc'
+ str.taint
+
+ s = StringScanner.new(str)
+
+ s.send(@method).tainted?.should be_true
+ s.send(@method).tainted?.should be_true
+ s.send(@method).tainted?.should be_true
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/extract_range_matched.rb b/spec/ruby/library/stringscanner/shared/extract_range_matched.rb
new file mode 100644
index 0000000000..fe695e8ac1
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/extract_range_matched.rb
@@ -0,0 +1,22 @@
+describe :extract_range_matched, shared: true do
+ it "returns an instance of String when passed a String subclass" do
+ cls = Class.new(String)
+ sub = cls.new("abc")
+
+ s = StringScanner.new(sub)
+ s.scan(/\w{1}/)
+
+ ch = s.send(@method)
+ ch.should_not be_kind_of(cls)
+ ch.should be_an_instance_of(String)
+ end
+
+ it "taints the returned String if the input was tainted" do
+ str = 'abc'
+ str.taint
+
+ s = StringScanner.new(str)
+ s.scan(/\w{1}/)
+ s.send(@method).tainted?.should be_true
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/get_byte.rb b/spec/ruby/library/stringscanner/shared/get_byte.rb
new file mode 100644
index 0000000000..763ab6f4a4
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/get_byte.rb
@@ -0,0 +1,29 @@
+# -*- encoding: binary -*-
+describe :strscan_get_byte, shared: true do
+ it "scans one byte and returns it" do
+ s = StringScanner.new('abc5.')
+ s.send(@method).should == 'a'
+ s.send(@method).should == 'b'
+ s.send(@method).should == 'c'
+ s.send(@method).should == '5'
+ s.send(@method).should == '.'
+ end
+
+ it "is not multi-byte character sensitive" do
+ s = StringScanner.new("\244\242")
+ s.send(@method).should == "\244"
+ s.send(@method).should == "\242"
+ end
+
+ it "returns nil at the end of the string" do
+ # empty string case
+ s = StringScanner.new('')
+ s.send(@method).should == nil
+ s.send(@method).should == nil
+
+ # non-empty string case
+ s = StringScanner.new('a')
+ s.send(@method) # skip one
+ s.send(@method).should == nil
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/matched_size.rb b/spec/ruby/library/stringscanner/shared/matched_size.rb
new file mode 100644
index 0000000000..92174733f7
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/matched_size.rb
@@ -0,0 +1,21 @@
+describe :strscan_matched_size, shared: true do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the size of the most recent match" do
+ @s.check(/This/)
+ @s.send(@method).should == 4
+ @s.send(@method).should == 4
+ @s.scan(//)
+ @s.send(@method).should == 0
+ end
+
+ it "returns nil if there was no recent match" do
+ @s.send(@method).should == nil
+ @s.check(/\d+/)
+ @s.send(@method).should == nil
+ @s.terminate
+ @s.send(@method).should == nil
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/peek.rb b/spec/ruby/library/stringscanner/shared/peek.rb
new file mode 100644
index 0000000000..418ebb6536
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/peek.rb
@@ -0,0 +1,47 @@
+describe :strscan_peek, shared: true do
+ before :each do
+ @s = StringScanner.new('This is a test')
+ end
+
+ it "returns at most the specified number of bytes from the current position" do
+ @s.send(@method, 4).should == "This"
+ @s.pos.should == 0
+ @s.pos = 5
+ @s.send(@method, 2).should == "is"
+ @s.send(@method, 1000).should == "is a test"
+
+ s = StringScanner.new("été")
+ s.send(@method, 2).should == "é"
+ end
+
+ it "returns an empty string when the passed argument is zero" do
+ @s.send(@method, 0).should == ""
+ end
+
+ it "raises a ArgumentError when the passed argument is negative" do
+ lambda { @s.send(@method, -2) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a RangeError when the passed argument is a Bignum" do
+ lambda { @s.send(@method, bignum_value) }.should raise_error(RangeError)
+ end
+
+ it "returns an instance of String when passed a String subclass" do
+ cls = Class.new(String)
+ sub = cls.new("abc")
+
+ s = StringScanner.new(sub)
+
+ ch = s.send(@method, 1)
+ ch.should_not be_kind_of(cls)
+ ch.should be_an_instance_of(String)
+ end
+
+ it "taints the returned String if the input was tainted" do
+ str = 'abc'
+ str.taint
+
+ s = StringScanner.new(str)
+ s.send(@method, 1).tainted?.should be_true
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/pos.rb b/spec/ruby/library/stringscanner/shared/pos.rb
new file mode 100644
index 0000000000..80ded17b0f
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/pos.rb
@@ -0,0 +1,52 @@
+describe :strscan_pos, shared: true do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the position of the scan pointer" do
+ @s.send(@method).should == 0
+ @s.scan_until(/This is/)
+ @s.send(@method).should == 7
+ @s.get_byte
+ @s.send(@method).should == 8
+ @s.terminate
+ @s.send(@method).should == 14
+ end
+
+ it "returns 0 in the reset position" do
+ @s.reset
+ @s.send(@method).should == 0
+ end
+
+ it "returns the length of the string in the terminate position" do
+ @s.terminate
+ @s.send(@method).should == @s.string.length
+ end
+end
+
+describe :strscan_pos_set, shared: true do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "modify the scan pointer" do
+ @s.send(@method, 5)
+ @s.rest.should == "is a test"
+ end
+
+ it "positions from the end if the argument is negative" do
+ @s.send(@method, -2)
+ @s.rest.should == "st"
+ @s.pos.should == 12
+ end
+
+ it "raises a RangeError if position too far backward" do
+ lambda {
+ @s.send(@method, -20)
+ }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError when the passed argument is out of range" do
+ lambda { @s.send(@method, 20) }.should raise_error(RangeError)
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/rest_size.rb b/spec/ruby/library/stringscanner/shared/rest_size.rb
new file mode 100644
index 0000000000..4c4f49e45c
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/rest_size.rb
@@ -0,0 +1,18 @@
+describe :strscan_rest_size, shared: true do
+ before :each do
+ @s = StringScanner.new('This is a test')
+ end
+
+ it "returns the length of the rest of the string" do
+ @s.send(@method).should == 14
+ @s.scan(/This/)
+ @s.send(@method).should == 10
+ @s.terminate
+ @s.send(@method).should == 0
+ end
+
+ it "is equivalent to rest.size" do
+ @s.scan(/This/)
+ @s.send(@method).should == @s.rest.size
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/terminate.rb b/spec/ruby/library/stringscanner/shared/terminate.rb
new file mode 100644
index 0000000000..bf41d097e2
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/terminate.rb
@@ -0,0 +1,8 @@
+describe :strscan_terminate, shared: true do
+ it "set the scan pointer to the end of the string and clear matching data." do
+ s = StringScanner.new('This is a test')
+ s.send(@method)
+ s.bol?.should be_false
+ s.eos?.should be_true
+ end
+end
diff --git a/spec/ruby/library/stringscanner/skip_spec.rb b/spec/ruby/library/stringscanner/skip_spec.rb
new file mode 100644
index 0000000000..473361782c
--- /dev/null
+++ b/spec/ruby/library/stringscanner/skip_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#skip" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns length of the match" do
+ @s.skip(/\w+/).should == 4
+ @s.skip(/\s+\w+/).should == 3
+ end
+
+ it "returns nil if there's no match" do
+ @s.skip(/\s+/).should == nil
+ @s.skip(/\d+/).should == nil
+ end
+end
diff --git a/spec/ruby/library/stringscanner/skip_until_spec.rb b/spec/ruby/library/stringscanner/skip_until_spec.rb
new file mode 100644
index 0000000000..73eb91b8ad
--- /dev/null
+++ b/spec/ruby/library/stringscanner/skip_until_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#skip_until" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the number of bytes advanced and advances the scan pointer until pattern is matched and consumed" do
+ @s.skip_until(/a/).should == 9
+ @s.pos.should == 9
+ @s.matched.should == "a"
+ end
+
+ it "returns nil if no match was found" do
+ @s.skip_until(/d+/).should == nil
+ end
+end
diff --git a/spec/ruby/library/stringscanner/string_spec.rb b/spec/ruby/library/stringscanner/string_spec.rb
new file mode 100644
index 0000000000..28e2f0ed37
--- /dev/null
+++ b/spec/ruby/library/stringscanner/string_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#string" do
+ before :each do
+ @string = "This is a test"
+ @s = StringScanner.new(@string)
+ end
+
+ it "returns the string being scanned" do
+ @s.string.should == "This is a test"
+ @s << " case"
+ @s.string.should == "This is a test case"
+ end
+
+ it "returns the identical object passed in" do
+ @s.string.equal?(@string).should be_true
+ end
+end
+
+describe "StringScanner#string=" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "changes the string being scanned to the argument and resets the scanner" do
+ @s.string = "Hello world"
+ @s.string.should == "Hello world"
+ end
+
+ it "converts the argument into a string using #to_str" do
+ m = mock(:str)
+
+ s = "test"
+ m.should_receive(:to_str).and_return(s)
+
+ @s.string = m
+ @s.string.should == s
+ end
+end
diff --git a/spec/ruby/library/stringscanner/terminate_spec.rb b/spec/ruby/library/stringscanner/terminate_spec.rb
new file mode 100644
index 0000000000..249023f1ab
--- /dev/null
+++ b/spec/ruby/library/stringscanner/terminate_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/terminate'
+require 'strscan'
+
+describe "StringScanner#terminate" do
+ it_behaves_like :strscan_terminate, :terminate
+end
diff --git a/spec/ruby/library/stringscanner/unscan_spec.rb b/spec/ruby/library/stringscanner/unscan_spec.rb
new file mode 100644
index 0000000000..ea5e6f4ef4
--- /dev/null
+++ b/spec/ruby/library/stringscanner/unscan_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#unscan" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "set the scan pointer to the previous position" do
+ @s.scan(/This/)
+ @s.unscan
+ @s.matched.should == nil
+ @s.pos.should == 0
+ end
+
+ it "remember only one previous position" do
+ @s.scan(/This/)
+ pos = @s.pos
+ @s.scan(/ is/)
+ @s.unscan
+ @s.pos.should == pos
+ end
+
+ it "raises a ScanError when the previous match had failed" do
+ lambda { @s.unscan }.should raise_error(ScanError)
+ lambda { @s.scan(/\d/); @s.unscan }.should raise_error(ScanError)
+ end
+end
diff --git a/spec/ruby/library/syslog/alert_spec.rb b/spec/ruby/library/syslog/alert_spec.rb
new file mode 100644
index 0000000000..edff789dc9
--- /dev/null
+++ b/spec/ruby/library/syslog/alert_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require_relative 'shared/log'
+ require 'syslog'
+
+ describe "Syslog.alert" do
+ it_behaves_like :syslog_log, :alert
+ end
+end
diff --git a/spec/ruby/library/syslog/close_spec.rb b/spec/ruby/library/syslog/close_spec.rb
new file mode 100644
index 0000000000..b1591045e3
--- /dev/null
+++ b/spec/ruby/library/syslog/close_spec.rb
@@ -0,0 +1,58 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require 'syslog'
+
+ describe "Syslog.close" do
+ platform_is_not :windows do
+
+ before :each do
+ Syslog.opened?.should be_false
+ end
+
+ after :each do
+ Syslog.opened?.should be_false
+ end
+
+ it "closes the log" do
+ Syslog.opened?.should be_false
+ Syslog.open
+ Syslog.opened?.should be_true
+ Syslog.close
+ Syslog.opened?.should be_false
+ end
+
+ it "raises a RuntimeError if the log's already closed" do
+ lambda { Syslog.close }.should raise_error(RuntimeError)
+ end
+
+ it "it does not work inside blocks" do
+ lambda {
+ Syslog.open { |s| s.close }
+ }.should raise_error(RuntimeError)
+ Syslog.opened?.should == false
+ end
+
+ it "sets the identity to nil" do
+ Syslog.open("rubyspec")
+ Syslog.ident.should == "rubyspec"
+ Syslog.close
+ Syslog.ident.should be_nil
+ end
+
+ it "sets the options to nil" do
+ Syslog.open("rubyspec", Syslog::LOG_PID)
+ Syslog.options.should == Syslog::LOG_PID
+ Syslog.close
+ Syslog.options.should == nil
+ end
+
+ it "sets the facility to nil" do
+ Syslog.open
+ Syslog.facility.should == 8
+ Syslog.close
+ Syslog.facility.should == nil
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/syslog/constants_spec.rb b/spec/ruby/library/syslog/constants_spec.rb
new file mode 100644
index 0000000000..2b9524c53d
--- /dev/null
+++ b/spec/ruby/library/syslog/constants_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require 'syslog'
+
+ describe "Syslog::Constants" do
+ platform_is_not :windows, :solaris, :aix do
+ before :all do
+ @constants = %w(LOG_AUTHPRIV LOG_USER LOG_LOCAL2 LOG_NOTICE LOG_NDELAY
+ LOG_SYSLOG LOG_ALERT LOG_FTP LOG_LOCAL5 LOG_ERR LOG_AUTH
+ LOG_LOCAL1 LOG_ODELAY LOG_NEWS LOG_DAEMON LOG_LOCAL4
+ LOG_CRIT LOG_INFO LOG_PERROR LOG_LOCAL0 LOG_CONS LOG_LPR
+ LOG_LOCAL7 LOG_WARNING LOG_CRON LOG_LOCAL3 LOG_EMERG
+ LOG_NOWAIT LOG_UUCP LOG_PID LOG_KERN LOG_MAIL LOG_LOCAL6
+ LOG_DEBUG)
+ end
+
+ it "includes the Syslog constants" do
+ @constants.each do |c|
+ Syslog::Constants.should have_constant(c)
+ end
+ end
+ end
+
+ # The masks are defined in <syslog.h>
+
+ describe "Syslog::Constants.LOG_MASK" do
+ it "returns the mask value for a priority" do
+ Syslog::Constants.LOG_MASK(Syslog::LOG_DEBUG).should == 128
+ Syslog::Constants.LOG_MASK(Syslog::LOG_WARNING).should == 16
+ end
+ end
+
+ describe "Syslog::Constants.LOG_UPTO" do
+ it "returns a mask for the priorities up to a given argument" do
+ Syslog::Constants.LOG_UPTO(Syslog::LOG_ALERT).should == 3
+ Syslog::Constants.LOG_UPTO(Syslog::LOG_DEBUG).should == 255
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/syslog/crit_spec.rb b/spec/ruby/library/syslog/crit_spec.rb
new file mode 100644
index 0000000000..5d3904f719
--- /dev/null
+++ b/spec/ruby/library/syslog/crit_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require_relative 'shared/log'
+ require 'syslog'
+
+ describe "Syslog.crit" do
+ it_behaves_like :syslog_log, :crit
+ end
+end
diff --git a/spec/ruby/library/syslog/debug_spec.rb b/spec/ruby/library/syslog/debug_spec.rb
new file mode 100644
index 0000000000..d03e8a88c9
--- /dev/null
+++ b/spec/ruby/library/syslog/debug_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require_relative 'shared/log'
+ require 'syslog'
+
+ describe "Syslog.debug" do
+ it_behaves_like :syslog_log, :debug
+ end
+end
diff --git a/spec/ruby/library/syslog/emerg_spec.rb b/spec/ruby/library/syslog/emerg_spec.rb
new file mode 100644
index 0000000000..2ab4d60291
--- /dev/null
+++ b/spec/ruby/library/syslog/emerg_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require_relative 'shared/log'
+ require 'syslog'
+
+ describe "Syslog.emerg" do
+ # Some way needs do be found to prevent this spec
+ # from causing output on all open terminals. If this
+ # is not possible, this spec may need a special guard
+ # that only runs when requested.
+ quarantine! do
+ it_behaves_like :syslog_log, :emerg
+ end
+ end
+end
diff --git a/spec/ruby/library/syslog/err_spec.rb b/spec/ruby/library/syslog/err_spec.rb
new file mode 100644
index 0000000000..43e876ed37
--- /dev/null
+++ b/spec/ruby/library/syslog/err_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require_relative 'shared/log'
+ require 'syslog'
+
+ describe "Syslog.err" do
+ it_behaves_like :syslog_log, :err
+ end
+end
diff --git a/spec/ruby/library/syslog/facility_spec.rb b/spec/ruby/library/syslog/facility_spec.rb
new file mode 100644
index 0000000000..550ca70b11
--- /dev/null
+++ b/spec/ruby/library/syslog/facility_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require 'syslog'
+
+ describe "Syslog.facility" do
+ platform_is_not :windows do
+
+ before :each do
+ Syslog.opened?.should be_false
+ end
+
+ after :each do
+ Syslog.opened?.should be_false
+ end
+
+ it "returns the logging facility" do
+ Syslog.open("rubyspec", 3, Syslog::LOG_MAIL)
+ Syslog.facility.should == Syslog::LOG_MAIL
+ Syslog.close
+ end
+
+ it "returns nil if the log is closed" do
+ Syslog.opened?.should be_false
+ Syslog.facility.should == nil
+ end
+
+ it "defaults to LOG_USER" do
+ Syslog.open
+ Syslog.facility.should == Syslog::LOG_USER
+ Syslog.close
+ end
+
+ it "resets after each open call" do
+ Syslog.open
+ Syslog.facility.should == Syslog::LOG_USER
+
+ Syslog.open!("rubyspec", 3, Syslog::LOG_MAIL)
+ Syslog.facility.should == Syslog::LOG_MAIL
+ Syslog.close
+
+ Syslog.open
+ Syslog.facility.should == Syslog::LOG_USER
+ Syslog.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/syslog/ident_spec.rb b/spec/ruby/library/syslog/ident_spec.rb
new file mode 100644
index 0000000000..e8345ebb49
--- /dev/null
+++ b/spec/ruby/library/syslog/ident_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require 'syslog'
+
+ describe "Syslog.ident" do
+ platform_is_not :windows do
+
+ before :each do
+ Syslog.opened?.should be_false
+ end
+
+ after :each do
+ Syslog.opened?.should be_false
+ end
+
+ it "returns the logging identity" do
+ Syslog.open("rubyspec")
+ Syslog.ident.should == "rubyspec"
+ Syslog.close
+ end
+
+ it "returns nil if the log is closed" do
+ Syslog.opened?.should == false
+ Syslog.ident.should == nil
+ end
+
+ it "defaults to $0" do
+ Syslog.open
+ Syslog.ident.should == $0
+ Syslog.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/syslog/info_spec.rb b/spec/ruby/library/syslog/info_spec.rb
new file mode 100644
index 0000000000..f2d535299c
--- /dev/null
+++ b/spec/ruby/library/syslog/info_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require_relative 'shared/log'
+ require 'syslog'
+
+ describe "Syslog.info" do
+ it_behaves_like :syslog_log, :info
+ end
+end
diff --git a/spec/ruby/library/syslog/inspect_spec.rb b/spec/ruby/library/syslog/inspect_spec.rb
new file mode 100644
index 0000000000..f45231f8e3
--- /dev/null
+++ b/spec/ruby/library/syslog/inspect_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require 'syslog'
+
+ describe "Syslog.inspect" do
+ platform_is_not :windows do
+
+ before :each do
+ Syslog.opened?.should be_false
+ end
+
+ after :each do
+ Syslog.opened?.should be_false
+ end
+
+ it "returns a string a closed log" do
+ Syslog.inspect.should =~ /opened=false/
+ end
+
+ it "returns a string for an opened log" do
+ Syslog.open
+ Syslog.inspect.should =~ /opened=true.*/
+ Syslog.close
+ end
+
+ it "includes the ident, options, facility and mask" do
+ Syslog.open("rubyspec", Syslog::LOG_PID, Syslog::LOG_USER)
+ inspect_str = Syslog.inspect.split ", "
+ inspect_str[0].should =~ /opened=true/
+ inspect_str[1].should == "ident=\"rubyspec\""
+ inspect_str[2].should == "options=#{Syslog::LOG_PID}"
+ inspect_str[3].should == "facility=#{Syslog::LOG_USER}"
+ inspect_str[4].should == "mask=255>"
+ Syslog.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/syslog/instance_spec.rb b/spec/ruby/library/syslog/instance_spec.rb
new file mode 100644
index 0000000000..891296c52d
--- /dev/null
+++ b/spec/ruby/library/syslog/instance_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require 'syslog'
+
+ describe "Syslog.instance" do
+ platform_is_not :windows do
+ it "returns the module" do
+ Syslog.instance.should == Syslog
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/syslog/log_spec.rb b/spec/ruby/library/syslog/log_spec.rb
new file mode 100644
index 0000000000..11b0e053b1
--- /dev/null
+++ b/spec/ruby/library/syslog/log_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require 'syslog'
+
+ describe "Syslog.log" do
+ platform_is_not :windows, :darwin, :solaris, :aix do
+
+ before :each do
+ Syslog.opened?.should be_false
+ end
+
+ after :each do
+ Syslog.opened?.should be_false
+ end
+
+ it "receives a priority as first argument" do
+ lambda {
+ Syslog.open("rubyspec", Syslog::LOG_PERROR) do |s|
+ s.log(Syslog::LOG_ALERT, "Hello")
+ s.log(Syslog::LOG_CRIT, "World")
+ end
+ }.should output_to_fd(/\Arubyspec(?::| \d+ - -) Hello\nrubyspec(?::| \d+ - -) World\n\z/, $stderr)
+ end
+
+ it "accepts undefined priorites" do
+ lambda {
+ Syslog.open("rubyspec", Syslog::LOG_PERROR) do |s|
+ s.log(1337, "Hello")
+ end
+ # use a regex since it'll output unknown facility/priority messages
+ }.should output_to_fd(/rubyspec(?::| \d+ - -) Hello\n\z/, $stderr)
+ end
+
+ it "fails with TypeError on nil log messages" do
+ Syslog.open do |s|
+ lambda { s.log(1, nil) }.should raise_error(TypeError)
+ end
+ end
+
+ it "fails if the log is closed" do
+ lambda {
+ Syslog.log(Syslog::LOG_ALERT, "test")
+ }.should raise_error(RuntimeError)
+ end
+
+ it "accepts printf parameters" do
+ lambda {
+ Syslog.open("rubyspec", Syslog::LOG_PERROR) do |s|
+ s.log(Syslog::LOG_ALERT, "%s x %d", "chunky bacon", 2)
+ end
+ }.should output_to_fd(/rubyspec(?::| \d+ - -) chunky bacon x 2\n\z/, $stderr)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/syslog/mask_spec.rb b/spec/ruby/library/syslog/mask_spec.rb
new file mode 100644
index 0000000000..8caa5e69b8
--- /dev/null
+++ b/spec/ruby/library/syslog/mask_spec.rb
@@ -0,0 +1,113 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require 'syslog'
+
+ describe "Syslog.mask" do
+ platform_is_not :windows do
+
+ before :each do
+ Syslog.opened?.should be_false
+ end
+
+ after :each do
+ Syslog.opened?.should be_false
+ # make sure we return the mask to the default value
+ Syslog.open { |s| s.mask = 255 }
+ end
+
+ it "returns the log priority mask" do
+ Syslog.open("rubyspec") do
+ Syslog.mask.should == 255
+ Syslog.mask = 3
+ Syslog.mask.should == 3
+ Syslog.mask = 255
+ end
+ end
+
+ it "defaults to 255" do
+ Syslog.open do |s|
+ s.mask.should == 255
+ end
+ end
+
+ it "returns nil if the log is closed" do
+ Syslog.opened?.should == false
+ Syslog.mask.should == nil
+ end
+
+ platform_is :darwin do
+ it "resets if the log is reopened" do
+ Syslog.open
+ Syslog.mask.should == 255
+ Syslog.mask = 64
+
+ Syslog.reopen("rubyspec") do
+ Syslog.mask.should == 255
+ end
+
+ Syslog.open do
+ Syslog.mask.should == 255
+ end
+ end
+ end
+
+ platform_is_not :darwin do
+ it "persists if the log is reopened" do
+ Syslog.open
+ Syslog.mask.should == 255
+ Syslog.mask = 64
+
+ Syslog.reopen("rubyspec") do
+ Syslog.mask.should == 64
+ end
+
+ Syslog.open do
+ Syslog.mask.should == 64
+ end
+ end
+ end
+ end
+ end
+
+ describe "Syslog.mask=" do
+ platform_is_not :windows do
+
+ before :each do
+ Syslog.opened?.should be_false
+ end
+
+ after :each do
+ Syslog.opened?.should be_false
+ # make sure we return the mask to the default value
+ Syslog.open { |s| s.mask = 255 }
+ end
+
+ it "sets the log priority mask" do
+ Syslog.open do
+ Syslog.mask = 64
+ Syslog.mask.should == 64
+ end
+ end
+
+ it "raises an error if the log is closed" do
+ lambda { Syslog.mask = 1337 }.should raise_error(RuntimeError)
+ end
+
+ it "only accepts numbers" do
+ Syslog.open do
+
+ Syslog.mask = 1337
+ Syslog.mask.should == 1337
+
+ Syslog.mask = 3.1416
+ Syslog.mask.should == 3
+
+ lambda { Syslog.mask = "oh hai" }.should raise_error(TypeError)
+ lambda { Syslog.mask = "43" }.should raise_error(TypeError)
+
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/syslog/notice_spec.rb b/spec/ruby/library/syslog/notice_spec.rb
new file mode 100644
index 0000000000..a2134e0140
--- /dev/null
+++ b/spec/ruby/library/syslog/notice_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require_relative 'shared/log'
+ require 'syslog'
+
+ describe "Syslog.notice" do
+ it_behaves_like :syslog_log, :notice
+ end
+end
diff --git a/spec/ruby/library/syslog/open_spec.rb b/spec/ruby/library/syslog/open_spec.rb
new file mode 100644
index 0000000000..641d0979cd
--- /dev/null
+++ b/spec/ruby/library/syslog/open_spec.rb
@@ -0,0 +1,92 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require_relative 'shared/reopen'
+ require 'syslog'
+
+ describe "Syslog.open" do
+ platform_is_not :windows do
+
+ before :each do
+ Syslog.opened?.should be_false
+ end
+
+ after :each do
+ Syslog.opened?.should be_false
+ end
+
+ it "returns the module" do
+ Syslog.open.should == Syslog
+ Syslog.close
+ Syslog.open("Test", 5, 9).should == Syslog
+ Syslog.close
+ end
+
+ it "receives an identity as first argument" do
+ Syslog.open("rubyspec")
+ Syslog.ident.should == "rubyspec"
+ Syslog.close
+ end
+
+ it "defaults the identity to $0" do
+ Syslog.open
+ Syslog.ident.should == $0
+ Syslog.close
+ end
+
+ it "receives the logging options as second argument" do
+ Syslog.open("rubyspec", Syslog::LOG_PID)
+ Syslog.options.should == Syslog::LOG_PID
+ Syslog.close
+ end
+
+ it "defaults the logging options to LOG_PID | LOG_CONS" do
+ Syslog.open
+ Syslog.options.should == Syslog::LOG_PID | Syslog::LOG_CONS
+ Syslog.close
+ end
+
+ it "receives a facility as third argument" do
+ Syslog.open("rubyspec", Syslog::LOG_PID, 0)
+ Syslog.facility.should == 0
+ Syslog.close
+ end
+
+ it "defaults the facility to LOG_USER" do
+ Syslog.open
+ Syslog.facility.should == Syslog::LOG_USER
+ Syslog.close
+ end
+
+ it "receives a block and calls it with the module" do
+ Syslog.open("rubyspec", 3, 8) do |s|
+ s.should == Syslog
+ s.ident.should == "rubyspec"
+ s.options.should == 3
+ s.facility.should == Syslog::LOG_USER
+ end
+ end
+
+ it "closes the log if after it receives a block" do
+ Syslog.open{ }
+ Syslog.opened?.should be_false
+ end
+
+ it "raises an error if the log is opened" do
+ Syslog.open
+ lambda {
+ Syslog.open
+ }.should raise_error(RuntimeError, /syslog already open/)
+ lambda {
+ Syslog.close
+ Syslog.open
+ }.should_not raise_error
+ Syslog.close
+ end
+ end
+ end
+
+ describe "Syslog.open!" do
+ it_behaves_like :syslog_reopen, :open!
+ end
+end
diff --git a/spec/ruby/library/syslog/opened_spec.rb b/spec/ruby/library/syslog/opened_spec.rb
new file mode 100644
index 0000000000..94432e65a4
--- /dev/null
+++ b/spec/ruby/library/syslog/opened_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require 'syslog'
+
+ describe "Syslog.opened?" do
+ platform_is_not :windows do
+
+ before :each do
+ Syslog.opened?.should be_false
+ end
+
+ after :each do
+ Syslog.opened?.should be_false
+ end
+
+ it "returns true if the log is opened" do
+ Syslog.open
+ Syslog.opened?.should be_true
+ Syslog.close
+ end
+
+ it "returns false otherwise" do
+ Syslog.opened?.should be_false
+ Syslog.open
+ Syslog.close
+ Syslog.opened?.should be_false
+ end
+
+ it "works inside a block" do
+ Syslog.open do |s|
+ s.opened?.should be_true
+ Syslog.opened?.should be_true
+ end
+ Syslog.opened?.should be_false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/syslog/options_spec.rb b/spec/ruby/library/syslog/options_spec.rb
new file mode 100644
index 0000000000..83ba43503e
--- /dev/null
+++ b/spec/ruby/library/syslog/options_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require 'syslog'
+
+ describe "Syslog.options" do
+ platform_is_not :windows do
+
+ before :each do
+ Syslog.opened?.should be_false
+ end
+
+ after :each do
+ Syslog.opened?.should be_false
+ end
+
+ it "returns the logging options" do
+ Syslog.open("rubyspec", Syslog::LOG_PID)
+ Syslog.options.should == Syslog::LOG_PID
+ Syslog.close
+ end
+
+ it "returns nil when the log is closed" do
+ Syslog.opened?.should be_false
+ Syslog.options.should == nil
+ end
+
+ it "defaults to LOG_PID | LOG_CONS" do
+ Syslog.open
+ Syslog.options.should == Syslog::LOG_PID | Syslog::LOG_CONS
+ Syslog.close
+ end
+
+ it "resets after each open call" do
+ Syslog.open
+ Syslog.options.should == Syslog::LOG_PID | Syslog::LOG_CONS
+
+ Syslog.open!("rubyspec", Syslog::LOG_PID)
+ Syslog.options.should == Syslog::LOG_PID
+ Syslog.close
+
+ Syslog.open
+ Syslog.options.should == Syslog::LOG_PID | Syslog::LOG_CONS
+ Syslog.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/syslog/reopen_spec.rb b/spec/ruby/library/syslog/reopen_spec.rb
new file mode 100644
index 0000000000..a78529fa1f
--- /dev/null
+++ b/spec/ruby/library/syslog/reopen_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require_relative 'shared/reopen'
+ require 'syslog'
+
+ describe "Syslog.reopen" do
+ it_behaves_like :syslog_reopen, :reopen
+ end
+end
diff --git a/spec/ruby/library/syslog/shared/log.rb b/spec/ruby/library/syslog/shared/log.rb
new file mode 100644
index 0000000000..b8b812a13c
--- /dev/null
+++ b/spec/ruby/library/syslog/shared/log.rb
@@ -0,0 +1,40 @@
+describe :syslog_log, shared: true do
+ platform_is_not :windows, :darwin, :solaris, :aix do
+ before :each do
+ Syslog.opened?.should be_false
+ end
+
+ after :each do
+ Syslog.opened?.should be_false
+ end
+
+ it "logs a message" do
+ lambda {
+ Syslog.open("rubyspec", Syslog::LOG_PERROR) do
+ Syslog.send(@method, "Hello")
+ end
+ }.should output_to_fd(/\Arubyspec(?::| \d+ - -) Hello\n\z/, $stderr)
+ end
+
+ it "accepts sprintf arguments" do
+ lambda {
+ Syslog.open("rubyspec", Syslog::LOG_PERROR) do
+ Syslog.send(@method, "Hello %s", "world")
+ Syslog.send(@method, "%d dogs", 2)
+ end
+ }.should output_to_fd(/\Arubyspec(?::| \d+ - -) Hello world\nrubyspec(?::| \d+ - -) 2 dogs\n\z/, $stderr)
+ end
+
+ it "works as an alias for Syslog.log" do
+ level = Syslog.const_get "LOG_#{@method.to_s.upcase}"
+ response = "rubyspec: Hello\n"
+ lambda {
+ Syslog.open("rubyspec", Syslog::LOG_PERROR) do
+ Syslog.send(@method, "Hello")
+ Syslog.log(level, "Hello")
+ end
+ # make sure the same thing is written to $stderr.
+ }.should output_to_fd(/\A(?:rubyspec(?::| \d+ - -) Hello\n){2}\z/, $stderr)
+ end
+ end
+end
diff --git a/spec/ruby/library/syslog/shared/reopen.rb b/spec/ruby/library/syslog/shared/reopen.rb
new file mode 100644
index 0000000000..fd30ab824a
--- /dev/null
+++ b/spec/ruby/library/syslog/shared/reopen.rb
@@ -0,0 +1,40 @@
+describe :syslog_reopen, shared: true do
+ platform_is_not :windows do
+ before :each do
+ Syslog.opened?.should be_false
+ end
+
+ after :each do
+ Syslog.opened?.should be_false
+ end
+
+ it "reopens the log" do
+ Syslog.open
+ lambda { Syslog.send(@method)}.should_not raise_error
+ Syslog.opened?.should be_true
+ Syslog.close
+ end
+
+ it "fails with RuntimeError if the log is closed" do
+ lambda { Syslog.send(@method)}.should raise_error(RuntimeError)
+ end
+
+ it "receives the same parameters as Syslog.open" do
+ Syslog.open
+ Syslog.send(@method, "rubyspec", 3, 8) do |s|
+ s.should == Syslog
+ s.ident.should == "rubyspec"
+ s.options.should == 3
+ s.facility.should == Syslog::LOG_USER
+ s.opened?.should be_true
+ end
+ Syslog.opened?.should be_false
+ end
+
+ it "returns the module" do
+ Syslog.open
+ Syslog.send(@method).should == Syslog
+ Syslog.close
+ end
+ end
+end
diff --git a/spec/ruby/library/syslog/warning_spec.rb b/spec/ruby/library/syslog/warning_spec.rb
new file mode 100644
index 0000000000..eeca603136
--- /dev/null
+++ b/spec/ruby/library/syslog/warning_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ require_relative 'shared/log'
+ require 'syslog'
+
+ describe "Syslog.warning" do
+ it_behaves_like :syslog_log, :warning
+ end
+end
diff --git a/spec/ruby/library/tempfile/_close_spec.rb b/spec/ruby/library/tempfile/_close_spec.rb
new file mode 100644
index 0000000000..c08f425b6f
--- /dev/null
+++ b/spec/ruby/library/tempfile/_close_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require 'tempfile'
+
+describe "Tempfile#_close" do
+ before :each do
+ @tempfile = Tempfile.new("specs")
+ end
+
+ after :each do
+ @tempfile.close!
+ end
+
+ it "is protected" do
+ Tempfile.should have_protected_instance_method(:_close)
+ end
+
+ it "closes self" do
+ @tempfile.send(:_close)
+ @tempfile.closed?.should be_true
+ end
+end
diff --git a/spec/ruby/library/tempfile/callback_spec.rb b/spec/ruby/library/tempfile/callback_spec.rb
new file mode 100644
index 0000000000..c0b1518326
--- /dev/null
+++ b/spec/ruby/library/tempfile/callback_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'tempfile'
+
+describe "Tempfile.callback" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/tempfile/close_spec.rb b/spec/ruby/library/tempfile/close_spec.rb
new file mode 100644
index 0000000000..6cd55beecf
--- /dev/null
+++ b/spec/ruby/library/tempfile/close_spec.rb
@@ -0,0 +1,57 @@
+require_relative '../../spec_helper'
+require 'tempfile'
+
+describe "Tempfile#close when passed no argument or [false]" do
+ before :each do
+ @tempfile = Tempfile.new("specs", tmp(""))
+ end
+
+ after :each do
+ @tempfile.close!
+ end
+
+ it "closes self" do
+ @tempfile.close
+ @tempfile.closed?.should be_true
+ end
+
+ it "does not unlink self" do
+ path = @tempfile.path
+ @tempfile.close
+ File.exist?(path).should be_true
+ end
+end
+
+describe "Tempfile#close when passed [true]" do
+ before :each do
+ @tempfile = Tempfile.new("specs", tmp(""))
+ end
+
+ it "closes self" do
+ @tempfile.close(true)
+ @tempfile.closed?.should be_true
+ end
+
+ it "unlinks self" do
+ path = @tempfile.path
+ @tempfile.close(true)
+ File.exist?(path).should be_false
+ end
+end
+
+describe "Tempfile#close!" do
+ before :each do
+ @tempfile = Tempfile.new("specs", tmp(""))
+ end
+
+ it "closes self" do
+ @tempfile.close!
+ @tempfile.closed?.should be_true
+ end
+
+ it "unlinks self" do
+ path = @tempfile.path
+ @tempfile.close!
+ File.exist?(path).should be_false
+ end
+end
diff --git a/spec/ruby/library/tempfile/delete_spec.rb b/spec/ruby/library/tempfile/delete_spec.rb
new file mode 100644
index 0000000000..0332b44dde
--- /dev/null
+++ b/spec/ruby/library/tempfile/delete_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/unlink'
+require 'tempfile'
+
+describe "Tempfile#delete" do
+ it_behaves_like :tempfile_unlink, :delete
+end
diff --git a/spec/ruby/library/tempfile/initialize_spec.rb b/spec/ruby/library/tempfile/initialize_spec.rb
new file mode 100644
index 0000000000..a3614971a1
--- /dev/null
+++ b/spec/ruby/library/tempfile/initialize_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require 'tempfile'
+
+describe "Tempfile#initialize" do
+ before :each do
+ @tempfile = Tempfile.allocate
+ end
+
+ after :each do
+ @tempfile.close!
+ end
+
+ it "opens a new tempfile with the passed name in the passed directory" do
+ @tempfile.send(:initialize, "basename", tmp(""))
+ File.exist?(@tempfile.path).should be_true
+
+ tmpdir = tmp("")
+ path = @tempfile.path
+
+ platform_is :windows do
+ # on Windows, both types of slashes are OK,
+ # but the tmp helper always uses '/'
+ path.gsub!('\\', '/')
+ end
+
+ path[0, tmpdir.length].should == tmpdir
+ path.should include("basename")
+ end
+
+ platform_is_not :windows do
+ it "sets the permisssions on the tempfile to 0600" do
+ @tempfile.send(:initialize, "basename", tmp(""))
+ File.stat(@tempfile.path).mode.should == 0100600
+ end
+ end
+
+ it "accepts encoding options" do
+ @tempfile.send(:initialize, ['shiftjis', 'yml'], encoding: 'SHIFT_JIS')
+ @tempfile.external_encoding.should == Encoding::Shift_JIS
+ end
+end
diff --git a/spec/ruby/library/tempfile/length_spec.rb b/spec/ruby/library/tempfile/length_spec.rb
new file mode 100644
index 0000000000..bc622b9a70
--- /dev/null
+++ b/spec/ruby/library/tempfile/length_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/length'
+require 'tempfile'
+
+describe "Tempfile#length" do
+ it_behaves_like :tempfile_length, :length
+end
diff --git a/spec/ruby/library/tempfile/open_spec.rb b/spec/ruby/library/tempfile/open_spec.rb
new file mode 100644
index 0000000000..c4c3d91051
--- /dev/null
+++ b/spec/ruby/library/tempfile/open_spec.rb
@@ -0,0 +1,89 @@
+require_relative '../../spec_helper'
+require 'tempfile'
+
+describe "Tempfile#open" do
+ before :each do
+ @tempfile = Tempfile.new("specs")
+ @tempfile.puts("Test!")
+ end
+
+ after :each do
+ @tempfile.close!
+ end
+
+ it "reopens self" do
+ @tempfile.close
+ @tempfile.open
+ @tempfile.closed?.should be_false
+ end
+
+ it "reopens self in read and write mode and does not truncate" do
+ @tempfile.open
+ @tempfile.puts("Another Test!")
+
+ @tempfile.open
+ @tempfile.readline.should == "Another Test!\n"
+ end
+end
+
+describe "Tempfile.open" do
+ after :each do
+ @tempfile.close! if @tempfile
+ end
+
+ it "returns a new, open Tempfile instance" do
+ @tempfile = Tempfile.open("specs")
+ # Delegation messes up .should be_an_instance_of(Tempfile)
+ @tempfile.instance_of?(Tempfile).should be_true
+ end
+
+ it "is passed an array [base, suffix] as first argument" do
+ Tempfile.open(["specs", ".tt"]) { |tempfile| @tempfile = tempfile }
+ @tempfile.path.should =~ /specs.*\.tt$/
+ end
+
+ it "passes the third argument (options) to open" do
+ Tempfile.open("specs", Dir.tmpdir, encoding: "IBM037:IBM037", binmode: true) do |tempfile|
+ @tempfile = tempfile
+ tempfile.external_encoding.should == Encoding.find("IBM037")
+ tempfile.binmode?.should be_true
+ end
+ end
+end
+
+describe "Tempfile.open when passed a block" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ after :each do
+ # Tempfile.open with block does not unlink
+ @tempfile.close! if @tempfile
+ end
+
+ it "yields a new, open Tempfile instance to the block" do
+ Tempfile.open("specs") do |tempfile|
+ @tempfile = tempfile
+ ScratchPad.record :yielded
+
+ # Delegation messes up .should be_an_instance_of(Tempfile)
+ tempfile.instance_of?(Tempfile).should be_true
+ tempfile.closed?.should be_false
+ end
+
+ ScratchPad.recorded.should == :yielded
+ end
+
+ it "returns the value of the block" do
+ value = Tempfile.open("specs") do |tempfile|
+ @tempfile = tempfile
+ "return"
+ end
+ value.should == "return"
+ end
+
+ it "closes the yielded Tempfile after the block" do
+ Tempfile.open("specs") { |tempfile| @tempfile = tempfile }
+ @tempfile.closed?.should be_true
+ end
+end
diff --git a/spec/ruby/library/tempfile/path_spec.rb b/spec/ruby/library/tempfile/path_spec.rb
new file mode 100644
index 0000000000..07f75b3e10
--- /dev/null
+++ b/spec/ruby/library/tempfile/path_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require 'tempfile'
+
+describe "Tempfile#path" do
+ before :each do
+ @tempfile = Tempfile.new("specs", tmp(""))
+ end
+
+ after :each do
+ @tempfile.close!
+ end
+
+ it "returns the path to the tempfile" do
+ tmpdir = tmp("")
+ path = @tempfile.path
+
+ platform_is :windows do
+ # on Windows, both types of slashes are OK,
+ # but the tmp helper always uses '/'
+ path.gsub!('\\', '/')
+ end
+
+ path[0, tmpdir.length].should == tmpdir
+ path.should include("specs")
+ end
+end
diff --git a/spec/ruby/library/tempfile/shared/length.rb b/spec/ruby/library/tempfile/shared/length.rb
new file mode 100644
index 0000000000..4d18d1f385
--- /dev/null
+++ b/spec/ruby/library/tempfile/shared/length.rb
@@ -0,0 +1,21 @@
+describe :tempfile_length, shared: true do
+ before :each do
+ @tempfile = Tempfile.new("specs")
+ end
+
+ after :each do
+ @tempfile.close!
+ end
+
+ it "returns the size of self" do
+ @tempfile.send(@method).should eql(0)
+ @tempfile.print("Test!")
+ @tempfile.send(@method).should eql(5)
+ end
+
+ it "returns the size of self even if self is closed" do
+ @tempfile.print("Test!")
+ @tempfile.close
+ @tempfile.send(@method).should eql(5)
+ end
+end
diff --git a/spec/ruby/library/tempfile/shared/unlink.rb b/spec/ruby/library/tempfile/shared/unlink.rb
new file mode 100644
index 0000000000..2b575fd391
--- /dev/null
+++ b/spec/ruby/library/tempfile/shared/unlink.rb
@@ -0,0 +1,12 @@
+describe :tempfile_unlink, shared: true do
+ before :each do
+ @tempfile = Tempfile.new("specs")
+ end
+
+ it "unlinks self" do
+ @tempfile.close
+ path = @tempfile.path
+ @tempfile.send(@method)
+ File.exist?(path).should be_false
+ end
+end
diff --git a/spec/ruby/library/tempfile/size_spec.rb b/spec/ruby/library/tempfile/size_spec.rb
new file mode 100644
index 0000000000..f4824601c7
--- /dev/null
+++ b/spec/ruby/library/tempfile/size_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/length'
+require 'tempfile'
+
+describe "Tempfile#size" do
+ it_behaves_like :tempfile_length, :size
+end
diff --git a/spec/ruby/library/tempfile/unlink_spec.rb b/spec/ruby/library/tempfile/unlink_spec.rb
new file mode 100644
index 0000000000..eac7df8472
--- /dev/null
+++ b/spec/ruby/library/tempfile/unlink_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/unlink'
+require 'tempfile'
+
+describe "Tempfile#unlink" do
+ it_behaves_like :tempfile_unlink, :unlink
+end
diff --git a/spec/ruby/library/thread/queue_spec.rb b/spec/ruby/library/thread/queue_spec.rb
new file mode 100644
index 0000000000..c7e2bb1b50
--- /dev/null
+++ b/spec/ruby/library/thread/queue_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+
+describe "Thread::Queue" do
+ it "is the same class as ::Queue" do
+ Thread.should have_constant(:Queue)
+ Thread::Queue.should equal ::Queue
+ end
+end
diff --git a/spec/ruby/library/thread/sizedqueue_spec.rb b/spec/ruby/library/thread/sizedqueue_spec.rb
new file mode 100644
index 0000000000..6151ff437c
--- /dev/null
+++ b/spec/ruby/library/thread/sizedqueue_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+
+describe "Thread::SizedQueue" do
+ it "is the same class as ::SizedQueue" do
+ Thread.should have_constant(:SizedQueue)
+ Thread::SizedQueue.should equal ::SizedQueue
+ end
+end
diff --git a/spec/ruby/library/time/httpdate_spec.rb b/spec/ruby/library/time/httpdate_spec.rb
new file mode 100644
index 0000000000..90953a9307
--- /dev/null
+++ b/spec/ruby/library/time/httpdate_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require 'time'
+
+describe "Time.httpdate" do
+ it "parses RFC-2616 strings" do
+ t = Time.utc(1994, 11, 6, 8, 49, 37)
+ t.should == Time.httpdate("Sun, 06 Nov 1994 08:49:37 GMT")
+
+ # relies on Time.parse (not yet implemented)
+ # t.should == Time.httpdate("Sunday, 06-Nov-94 08:49:37 GMT")
+
+ t.should == Time.httpdate("Sun Nov 6 08:49:37 1994")
+ Time.utc(1995, 11, 15, 6, 25, 24).should == Time.httpdate("Wed, 15 Nov 1995 06:25:24 GMT")
+ Time.utc(1995, 11, 15, 4, 58, 8).should == Time.httpdate("Wed, 15 Nov 1995 04:58:08 GMT")
+ Time.utc(1994, 11, 15, 8, 12, 31).should == Time.httpdate("Tue, 15 Nov 1994 08:12:31 GMT")
+ Time.utc(1994, 12, 1, 16, 0, 0).should == Time.httpdate("Thu, 01 Dec 1994 16:00:00 GMT")
+ Time.utc(1994, 10, 29, 19, 43, 31).should == Time.httpdate("Sat, 29 Oct 1994 19:43:31 GMT")
+ Time.utc(1994, 11, 15, 12, 45, 26).should == Time.httpdate("Tue, 15 Nov 1994 12:45:26 GMT")
+ Time.utc(1999, 12, 31, 23, 59, 59).should == Time.httpdate("Fri, 31 Dec 1999 23:59:59 GMT")
+ end
+end
diff --git a/spec/ruby/library/time/iso8601_spec.rb b/spec/ruby/library/time/iso8601_spec.rb
new file mode 100644
index 0000000000..4a9eb45613
--- /dev/null
+++ b/spec/ruby/library/time/iso8601_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/xmlschema'
+require 'time'
+
+describe "Time.xmlschema" do
+ it_behaves_like :time_xmlschema, :iso8601
+end
diff --git a/spec/ruby/library/time/rfc2822_spec.rb b/spec/ruby/library/time/rfc2822_spec.rb
new file mode 100644
index 0000000000..7fc5e9a64b
--- /dev/null
+++ b/spec/ruby/library/time/rfc2822_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/rfc2822'
+require 'time'
+
+describe "Time.rfc2822" do
+ it_behaves_like :time_rfc2822, :rfc2822
+end
diff --git a/spec/ruby/library/time/rfc822_spec.rb b/spec/ruby/library/time/rfc822_spec.rb
new file mode 100644
index 0000000000..da77e6ee77
--- /dev/null
+++ b/spec/ruby/library/time/rfc822_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/rfc2822'
+require 'time'
+
+describe "Time.rfc822" do
+ it_behaves_like :time_rfc2822, :rfc822
+end
diff --git a/spec/ruby/library/time/shared/rfc2822.rb b/spec/ruby/library/time/shared/rfc2822.rb
new file mode 100644
index 0000000000..b7bf0fb5f5
--- /dev/null
+++ b/spec/ruby/library/time/shared/rfc2822.rb
@@ -0,0 +1,65 @@
+describe :time_rfc2822, shared: true do
+ it "parses RFC-822 strings" do
+ t1 = (Time.utc(1976, 8, 26, 14, 30) + 4 * 3600)
+ t2 = Time.rfc2822("26 Aug 76 14:30 EDT")
+ t1.should == t2
+
+ t3 = Time.utc(1976, 8, 27, 9, 32) + 7 * 3600
+ t4 = Time.rfc2822("27 Aug 76 09:32 PDT")
+ t3.should == t4
+ end
+
+ it "parses RFC-2822 strings" do
+ t1 = Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600
+ t2 = Time.rfc2822("Fri, 21 Nov 1997 09:55:06 -0600")
+ t1.should == t2
+
+ t3 = Time.utc(2003, 7, 1, 10, 52, 37) - 2 * 3600
+ t4 = Time.rfc2822("Tue, 1 Jul 2003 10:52:37 +0200")
+ t3.should == t4
+
+ t5 = Time.utc(1997, 11, 21, 10, 1, 10) + 6 * 3600
+ t6 = Time.rfc2822("Fri, 21 Nov 1997 10:01:10 -0600")
+ t5.should == t6
+
+ t7 = Time.utc(1997, 11, 21, 11, 0, 0) + 6 * 3600
+ t8 = Time.rfc2822("Fri, 21 Nov 1997 11:00:00 -0600")
+ t7.should == t8
+
+ t9 = Time.utc(1997, 11, 24, 14, 22, 1) + 8 * 3600
+ t10 = Time.rfc2822("Mon, 24 Nov 1997 14:22:01 -0800")
+ t9.should == t10
+
+ begin
+ Time.at(-1)
+ rescue ArgumentError
+ # ignore
+ else
+ t11 = Time.utc(1969, 2, 13, 23, 32, 54) + 3 * 3600 + 30 * 60
+ t12 = Time.rfc2822("Thu, 13 Feb 1969 23:32:54 -0330")
+ t11.should == t12
+
+ t13 = Time.utc(1969, 2, 13, 23, 32, 0) + 3 * 3600 + 30 * 60
+ t14 = Time.rfc2822(" Thu,
+ 13
+ Feb
+ 1969
+ 23:32
+ -0330 (Newfoundland Time)")
+ t13.should == t14
+ end
+
+ t15 = Time.utc(1997, 11, 21, 9, 55, 6)
+ t16 = Time.rfc2822("21 Nov 97 09:55:06 GMT")
+ t15.should == t16
+
+ t17 = Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600
+ t18 = Time.rfc2822("Fri, 21 Nov 1997 09 : 55 : 06 -0600")
+ t17.should == t18
+
+ lambda {
+ # inner comment is not supported.
+ Time.rfc2822("Fri, 21 Nov 1997 09(comment): 55 : 06 -0600")
+ }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/time/shared/xmlschema.rb b/spec/ruby/library/time/shared/xmlschema.rb
new file mode 100644
index 0000000000..44d33cda7e
--- /dev/null
+++ b/spec/ruby/library/time/shared/xmlschema.rb
@@ -0,0 +1,53 @@
+describe :time_xmlschema, shared: true do
+ it "parses ISO-8601 strings" do
+ t = Time.utc(1985, 4, 12, 23, 20, 50, 520000)
+ s = "1985-04-12T23:20:50.52Z"
+ t.should == Time.xmlschema(s)
+ #s.should == t.xmlschema(2)
+
+ t = Time.utc(1996, 12, 20, 0, 39, 57)
+ s = "1996-12-19T16:39:57-08:00"
+ t.should == Time.xmlschema(s)
+ # There is no way to generate time string with arbitrary timezone.
+ s = "1996-12-20T00:39:57Z"
+ t.should == Time.xmlschema(s)
+ #assert_equal(s, t.xmlschema)
+
+ t = Time.utc(1990, 12, 31, 23, 59, 60)
+ s = "1990-12-31T23:59:60Z"
+ t.should == Time.xmlschema(s)
+ # leap second is representable only if timezone file has it.
+ s = "1990-12-31T15:59:60-08:00"
+ t.should == Time.xmlschema(s)
+
+ begin
+ Time.at(-1)
+ rescue ArgumentError
+ # ignore
+ else
+ t = Time.utc(1937, 1, 1, 11, 40, 27, 870000)
+ s = "1937-01-01T12:00:27.87+00:20"
+ t.should == Time.xmlschema(s)
+ end
+
+ # more
+
+ # (Time.utc(1999, 5, 31, 13, 20, 0) + 5 * 3600).should == Time.xmlschema("1999-05-31T13:20:00-05:00")
+ # (Time.local(2000, 1, 20, 12, 0, 0)).should == Time.xmlschema("2000-01-20T12:00:00")
+ # (Time.utc(2000, 1, 20, 12, 0, 0)).should == Time.xmlschema("2000-01-20T12:00:00Z")
+ # (Time.utc(2000, 1, 20, 12, 0, 0) - 12 * 3600).should == Time.xmlschema("2000-01-20T12:00:00+12:00")
+ # (Time.utc(2000, 1, 20, 12, 0, 0) + 13 * 3600).should == Time.xmlschema("2000-01-20T12:00:00-13:00")
+ # (Time.utc(2000, 3, 4, 23, 0, 0) - 3 * 3600).should == Time.xmlschema("2000-03-04T23:00:00+03:00")
+ # (Time.utc(2000, 3, 4, 20, 0, 0)).should == Time.xmlschema("2000-03-04T20:00:00Z")
+ # (Time.local(2000, 1, 15, 0, 0, 0)).should == Time.xmlschema("2000-01-15T00:00:00")
+ # (Time.local(2000, 2, 15, 0, 0, 0)).should == Time.xmlschema("2000-02-15T00:00:00")
+ # (Time.local(2000, 1, 15, 12, 0, 0)).should == Time.xmlschema("2000-01-15T12:00:00")
+ # (Time.utc(2000, 1, 16, 12, 0, 0)).should == Time.xmlschema("2000-01-16T12:00:00Z")
+ # (Time.local(2000, 1, 1, 12, 0, 0)).should == Time.xmlschema("2000-01-01T12:00:00")
+ # (Time.utc(1999, 12, 31, 23, 0, 0)).should == Time.xmlschema("1999-12-31T23:00:00Z")
+ # (Time.local(2000, 1, 16, 12, 0, 0)).should == Time.xmlschema("2000-01-16T12:00:00")
+ # (Time.local(2000, 1, 16, 0, 0, 0)).should == Time.xmlschema("2000-01-16T00:00:00")
+ # (Time.utc(2000, 1, 12, 12, 13, 14)).should == Time.xmlschema("2000-01-12T12:13:14Z")
+ # (Time.utc(2001, 4, 17, 19, 23, 17, 300000)).should == Time.xmlschema("2001-04-17T19:23:17.3Z")
+ end
+end
diff --git a/spec/ruby/library/time/to_date_spec.rb b/spec/ruby/library/time/to_date_spec.rb
new file mode 100644
index 0000000000..baeafe0847
--- /dev/null
+++ b/spec/ruby/library/time/to_date_spec.rb
@@ -0,0 +1,42 @@
+
+require_relative '../../spec_helper'
+require 'time'
+
+describe "Time#to_date" do
+ it "yields accurate julian date for ambiguous pre-Gregorian reform value" do
+ Time.utc(1582, 10, 4).to_date.jd.should == Date::ITALY - 11 # 2299150j
+ end
+
+ it "yields accurate julian date for Julian-Gregorian gap value" do
+ Time.utc(1582, 10, 14).to_date.jd.should == Date::ITALY - 1 # 2299160j
+ end
+
+ it "yields accurate julian date for post-Gregorian reform value" do
+ Time.utc(1582, 10, 15).to_date.jd.should == Date::ITALY # 2299161j
+ end
+
+ it "yields same julian day regardless of UTC time value" do
+ Time.utc(1582, 10, 15, 00, 00, 00).to_date.jd.should == Date::ITALY
+ Time.utc(1582, 10, 15, 23, 59, 59).to_date.jd.should == Date::ITALY
+ end
+
+ it "yields same julian day regardless of local time or zone" do
+
+ with_timezone("Pacific/Pago_Pago", -11) do
+ Time.local(1582, 10, 15, 00, 00, 00).to_date.jd.should == Date::ITALY
+ Time.local(1582, 10, 15, 23, 59, 59).to_date.jd.should == Date::ITALY
+ end
+
+ with_timezone("Asia/Kamchatka", +12) do
+ Time.local(1582, 10, 15, 00, 00, 00).to_date.jd.should == Date::ITALY
+ Time.local(1582, 10, 15, 23, 59, 59).to_date.jd.should == Date::ITALY
+ end
+
+ end
+
+ it "yields date with default Calendar reform day" do
+ Time.utc(1582, 10, 4).to_date.start.should == Date::ITALY
+ Time.utc(1582, 10, 14).to_date.start.should == Date::ITALY
+ Time.utc(1582, 10, 15).to_date.start.should == Date::ITALY
+ end
+end
diff --git a/spec/ruby/library/time/to_datetime_spec.rb b/spec/ruby/library/time/to_datetime_spec.rb
new file mode 100644
index 0000000000..0e37a61108
--- /dev/null
+++ b/spec/ruby/library/time/to_datetime_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+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)
+ datetime = time.to_datetime
+ datetime.year.should == 3
+ datetime.month.should == 12
+ datetime.day.should == 31
+ datetime.hour.should == 23
+ datetime.min.should == 58
+ datetime.sec.should == 59
+ end
+
+ it "roundtrips" do
+ time = Time.utc(3, 12, 31, 23, 58, 59)
+ datetime = time.to_datetime
+ datetime.to_time.utc.should == time
+ end
+
+ it "yields a DateTime with the default Calendar reform day" do
+ Time.utc(1582, 10, 4, 1, 2, 3).to_datetime.start.should == Date::ITALY
+ Time.utc(1582, 10, 14, 1, 2, 3).to_datetime.start.should == Date::ITALY
+ Time.utc(1582, 10, 15, 1, 2, 3).to_datetime.start.should == Date::ITALY
+ end
+end
diff --git a/spec/ruby/library/time/to_time_spec.rb b/spec/ruby/library/time/to_time_spec.rb
new file mode 100644
index 0000000000..5602915551
--- /dev/null
+++ b/spec/ruby/library/time/to_time_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require 'time'
+
+ruby_version_is "2.4" do
+ describe "Time#to_time" do
+ it "returns itself in the same timezone" do
+ time = Time.new(2012, 2, 21, 10, 11, 12)
+
+ with_timezone("America/Regina") do
+ time.to_time.should equal time
+ end
+
+ time2 = Time.utc(2012, 2, 21, 10, 11, 12)
+ time2.to_time.should equal time2
+ end
+ end
+end
diff --git a/spec/ruby/library/time/xmlschema_spec.rb b/spec/ruby/library/time/xmlschema_spec.rb
new file mode 100644
index 0000000000..4279311199
--- /dev/null
+++ b/spec/ruby/library/time/xmlschema_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/xmlschema'
+require 'time'
+
+describe "Time.xmlschema" do
+ it_behaves_like :time_xmlschema, :xmlschema
+end
diff --git a/spec/ruby/library/timeout/error_spec.rb b/spec/ruby/library/timeout/error_spec.rb
new file mode 100644
index 0000000000..6c236e5128
--- /dev/null
+++ b/spec/ruby/library/timeout/error_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'timeout'
+
+describe "Timeout::Error" do
+ it "is a subclass of RuntimeError" do
+ RuntimeError.should be_ancestor_of(Timeout::Error)
+ end
+end
diff --git a/spec/ruby/library/timeout/timeout_spec.rb b/spec/ruby/library/timeout/timeout_spec.rb
new file mode 100644
index 0000000000..5297de2823
--- /dev/null
+++ b/spec/ruby/library/timeout/timeout_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require 'timeout'
+
+describe "Timeout.timeout" do
+ it "raises Timeout::Error when it times out with no specified error type" do
+ lambda {
+ Timeout.timeout(1) do
+ sleep 3
+ end
+ }.should raise_error(Timeout::Error)
+ end
+
+ it "raises specified error type when it times out" do
+ lambda do
+ Timeout.timeout(1, StandardError) do
+ sleep 3
+ end
+ end.should raise_error(StandardError)
+ end
+
+ it "does not wait too long" do
+ before_time = Time.now
+ lambda do
+ Timeout.timeout(1, StandardError) do
+ sleep 3
+ end
+ end.should raise_error(StandardError)
+
+ (Time.now - before_time).should be_close(1.0, 0.5)
+ end
+
+ it "returns back the last value in the block" do
+ Timeout.timeout(1) do
+ 42
+ end.should == 42
+ end
+end
diff --git a/spec/ruby/library/tmpdir/dir/mktmpdir_spec.rb b/spec/ruby/library/tmpdir/dir/mktmpdir_spec.rb
new file mode 100644
index 0000000000..6f56f60425
--- /dev/null
+++ b/spec/ruby/library/tmpdir/dir/mktmpdir_spec.rb
@@ -0,0 +1,117 @@
+require_relative '../../../spec_helper'
+require "tmpdir"
+
+describe "Dir.mktmpdir when passed no arguments" do
+ after :each do
+ Dir.rmdir @tmpdir if File.directory? @tmpdir
+ end
+
+ it "returns the path to the created tmp-dir" do
+ Dir.stub!(:mkdir)
+ Dir.should_receive(:tmpdir).and_return("/tmp")
+ @tmpdir = Dir.mktmpdir
+ @tmpdir.should =~ /^\/tmp\//
+ end
+
+ it "creates a new writable directory in the path provided by Dir.tmpdir" do
+ Dir.should_receive(:tmpdir).and_return(tmp(""))
+ @tmpdir = Dir.mktmpdir
+ File.directory?(@tmpdir).should be_true
+ File.writable?(@tmpdir).should be_true
+ end
+end
+
+describe "Dir.mktmpdir when passed a block" do
+ before :each do
+ @real_tmp_root = tmp('')
+ Dir.stub!(:tmpdir).and_return(@real_tmp_root)
+ FileUtils.stub!(:remove_entry)
+ FileUtils.stub!(:remove_entry_secure)
+ end
+
+ after :each do
+ Dir.rmdir @tmpdir if File.directory? @tmpdir
+ end
+
+ it "yields the path to the passed block" do
+ Dir.stub!(:mkdir)
+ called = nil
+ Dir.mktmpdir do |path|
+ @tmpdir = path
+ called = true
+ path.start_with?(@real_tmp_root).should be_true
+ end
+ called.should be_true
+ end
+
+ it "creates the tmp-dir before yielding" do
+ Dir.should_receive(:tmpdir).and_return(tmp(""))
+ Dir.mktmpdir do |path|
+ @tmpdir = path
+ File.directory?(path).should be_true
+ File.writable?(path).should be_true
+ end
+ end
+
+ it "removes the tmp-dir after executing the block" do
+ Dir.stub!(:mkdir)
+ Dir.mktmpdir do |path|
+ @tmpdir = path
+ FileUtils.should_receive(:remove_entry).with(path)
+ end
+ end
+
+ it "returns the blocks return value" do
+ Dir.stub!(:mkdir)
+ result = Dir.mktmpdir do |path|
+ @tmpdir = path
+ :test
+ end
+ result.should equal(:test)
+ end
+end
+
+describe "Dir.mktmpdir when passed [String]" do
+ before :each do
+ Dir.stub!(:mkdir)
+ Dir.stub!(:tmpdir).and_return("/tmp")
+ end
+
+ after :each do
+ Dir.rmdir @tmpdir if File.directory? @tmpdir
+ end
+
+ it "uses the passed String as a prefix to the tmp-directory" do
+ prefix = "before"
+ @tmpdir = Dir.mktmpdir(prefix)
+ @tmpdir.should =~ /^\/tmp\/#{prefix}/
+ end
+end
+
+describe "Dir.mktmpdir when passed [Array]" do
+ before :each do
+ Dir.stub!(:mkdir)
+ Dir.stub!(:tmpdir).and_return("/tmp")
+ FileUtils.stub!(:remove_entry_secure)
+ end
+
+ after :each do
+ Dir.rmdir @tmpdir if File.directory? @tmpdir
+ end
+
+ it "uses the first element of the passed Array as a prefix and the scond element as a suffix to the tmp-directory" do
+ prefix = "before"
+ suffix = "after"
+
+ @tmpdir = Dir.mktmpdir([prefix, suffix])
+ @tmpdir.should =~ /#{suffix}$/
+ end
+end
+
+describe "Dir.mktmpdir when passed [Object]" do
+ it "raises an ArgumentError" do
+ lambda { Dir.mktmpdir(Object.new) }.should raise_error(ArgumentError)
+ lambda { Dir.mktmpdir(:symbol) }.should raise_error(ArgumentError)
+ lambda { Dir.mktmpdir(10) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/tmpdir/dir/tmpdir_spec.rb b/spec/ruby/library/tmpdir/dir/tmpdir_spec.rb
new file mode 100644
index 0000000000..f4ab5e40b8
--- /dev/null
+++ b/spec/ruby/library/tmpdir/dir/tmpdir_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require "tmpdir"
+
+describe "Dir.tmpdir" do
+ it "returns the path to a writable and readable directory" do
+ dir = Dir.tmpdir
+ File.directory?(dir).should be_true
+ File.writable?(dir).should be_true
+ end
+end
diff --git a/spec/ruby/library/uri/decode_www_form_component_spec.rb b/spec/ruby/library/uri/decode_www_form_component_spec.rb
new file mode 100644
index 0000000000..075cec1087
--- /dev/null
+++ b/spec/ruby/library/uri/decode_www_form_component_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'uri'
+
+describe "URI.decode_www_form_component" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/decode_www_form_spec.rb b/spec/ruby/library/uri/decode_www_form_spec.rb
new file mode 100644
index 0000000000..8dd37e514f
--- /dev/null
+++ b/spec/ruby/library/uri/decode_www_form_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'uri'
+
+describe "URI.decode_www_form" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/encode_www_form_component_spec.rb b/spec/ruby/library/uri/encode_www_form_component_spec.rb
new file mode 100644
index 0000000000..a0508b207c
--- /dev/null
+++ b/spec/ruby/library/uri/encode_www_form_component_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'uri'
+
+describe "URI.encode_www_form_component" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/encode_www_form_spec.rb b/spec/ruby/library/uri/encode_www_form_spec.rb
new file mode 100644
index 0000000000..7f4aecf89b
--- /dev/null
+++ b/spec/ruby/library/uri/encode_www_form_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'uri'
+
+describe "URI.encode_www_form" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/eql_spec.rb b/spec/ruby/library/uri/eql_spec.rb
new file mode 100644
index 0000000000..2bbf8fd40c
--- /dev/null
+++ b/spec/ruby/library/uri/eql_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/normalization'
+require_relative 'shared/eql'
+require 'uri'
+
+describe "URI#eql?" do
+ it_behaves_like :uri_eql, :eql?
+
+ it_behaves_like :uri_eql_against_other_types, :eql?
+end
diff --git a/spec/ruby/library/uri/equality_spec.rb b/spec/ruby/library/uri/equality_spec.rb
new file mode 100644
index 0000000000..1c247ce291
--- /dev/null
+++ b/spec/ruby/library/uri/equality_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/normalization'
+require_relative 'shared/eql'
+require 'uri'
+
+describe "URI#==" do
+ it "ignores capitalization of host names" do
+ URI("http://exAMPLE.cOm").should == URI("http://example.com")
+ end
+
+ it "ignores capitalization of scheme" do
+ URI("hTTp://example.com").should == URI("http://example.com")
+ end
+
+ it "treats a blank path and a path of '/' as the same" do
+ URI("http://example.com").should == URI("http://example.com/")
+ end
+
+ it "is case sensitive in all components of the URI but the host and scheme" do
+ URI("http://example.com/paTH").should_not == URI("http://example.com/path")
+ URI("http://uSer@example.com").should_not == URI("http://user@example.com")
+ URI("http://example.com/path?quERy").should_not == URI("http://example.com/path?query")
+ URI("http://example.com/#fragMENT").should_not == URI("http://example.com/#fragment")
+ end
+
+ it "differentiates based on port number" do
+ URI("http://example.com:8080").should_not == URI("http://example.com")
+ end
+
+ # Note: The previous tests will be included in following ones
+
+ it_behaves_like :uri_eql, :==
+
+ it_behaves_like :uri_eql_against_other_types, :==
+
+ quarantine! do # Quarantined until redmine:2542 is accepted
+ it "returns true only if the normalized forms are equivalent" do
+ URISpec::NORMALIZED_FORMS.each do |form|
+ normal_uri = URI(form[:normalized])
+ form[:equivalent].each do |same|
+ URI(same).should == normal_uri
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/uri/escape/decode_spec.rb b/spec/ruby/library/uri/escape/decode_spec.rb
new file mode 100644
index 0000000000..b4ef799411
--- /dev/null
+++ b/spec/ruby/library/uri/escape/decode_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Escape#decode" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/escape/encode_spec.rb b/spec/ruby/library/uri/escape/encode_spec.rb
new file mode 100644
index 0000000000..2b61b7c152
--- /dev/null
+++ b/spec/ruby/library/uri/escape/encode_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Escape#encode" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/escape/escape_spec.rb b/spec/ruby/library/uri/escape/escape_spec.rb
new file mode 100644
index 0000000000..dddbc60707
--- /dev/null
+++ b/spec/ruby/library/uri/escape/escape_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Escape#escape" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/escape/unescape_spec.rb b/spec/ruby/library/uri/escape/unescape_spec.rb
new file mode 100644
index 0000000000..7d574d13c1
--- /dev/null
+++ b/spec/ruby/library/uri/escape/unescape_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Escape#unescape" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/extract_spec.rb b/spec/ruby/library/uri/extract_spec.rb
new file mode 100644
index 0000000000..1294a480f1
--- /dev/null
+++ b/spec/ruby/library/uri/extract_spec.rb
@@ -0,0 +1,86 @@
+require_relative '../../spec_helper'
+require 'uri'
+
+describe "URI.extract" do
+ it "behaves according to its documentation" do
+ URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.").should == ["http://foo.example.org/bla", "mailto:test@example.com"]
+ end
+
+ it "treats contiguous URIs as a single URI" do
+ URI.extract('http://example.jphttp://example.jp').should == ['http://example.jphttp://example.jp']
+ end
+
+ it "treats pretty much anything with a colon as a URI" do
+ URI.extract('From: XXX [mailto:xxx@xxx.xxx.xxx]').should == ['From:', 'mailto:xxx@xxx.xxx.xxx]']
+ end
+
+ it "wraps a URI string in an array" do
+ URI.extract("http://github.com/brixen/rubyspec/tree/master").should == ["http://github.com/brixen/rubyspec/tree/master"]
+ end
+
+ it "pulls a variety of protocol URIs from a string" do
+ URI.extract("this is a string, it has http://rubini.us/ in it").should == ["http://rubini.us/"]
+ URI.extract("mailto:spambait@example.com").should == ["mailto:spambait@example.com"]
+ URI.extract("ftp://ruby-lang.org/").should == ["ftp://ruby-lang.org/"]
+ URI.extract("https://mail.google.com").should == ["https://mail.google.com"]
+ URI.extract("anything://example.com/").should == ["anything://example.com/"]
+ end
+
+ it "pulls all URIs within a string in order into an array when a block is not given" do
+ URI.extract("1.3. Example URI
+
+ The following examples illustrate URI that are in common use.
+
+ ftp://ftp.is.co.za/rfc/rfc1808.txt
+ -- ftp scheme for File Transfer Protocol services
+
+ gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles
+ -- gopher scheme for Gopher and Gopher+ Protocol services
+
+ http://www.math.uio.no/faq/compression-faq/part1.html
+ -- http scheme for Hypertext Transfer Protocol services
+
+ mailto:mduerst@ifi.unizh.ch
+ -- mailto scheme for electronic mail addresses
+
+ news:comp.infosystems.www.servers.unix
+ -- news scheme for USENET news groups and articles
+
+ telnet://melvyl.ucop.edu/
+ -- telnet scheme for interactive services via the TELNET Protocol
+ ").should == ["ftp://ftp.is.co.za/rfc/rfc1808.txt","gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles","http://www.math.uio.no/faq/compression-faq/part1.html","mailto:mduerst@ifi.unizh.ch","news:comp.infosystems.www.servers.unix","telnet://melvyl.ucop.edu/"]
+ end
+
+ it "yields each URI in the given string in order to a block, if given, and returns nil" do
+ results = ["http://foo.example.org/bla", "mailto:test@example.com"]
+ URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.") {|uri|
+ uri.should == results.shift
+ }.should == nil
+ results.should == []
+ end
+
+ it "allows the user to specify a list of acceptable protocols of URIs to scan for" do
+ URI.extract("1.3. Example URI
+
+ The following examples illustrate URI that are in common use.
+
+ ftp://ftp.is.co.za/rfc/rfc1808.txt
+ -- ftp scheme for File Transfer Protocol services
+
+ gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles
+ -- gopher scheme for Gopher and Gopher+ Protocol services
+
+ http://www.math.uio.no/faq/compression-faq/part1.html
+ -- http scheme for Hypertext Transfer Protocol services
+
+ mailto:mduerst@ifi.unizh.ch
+ -- mailto scheme for electronic mail addresses
+
+ news:comp.infosystems.www.servers.unix
+ -- news scheme for USENET news groups and articles
+
+ telnet://melvyl.ucop.edu/
+ -- telnet scheme for interactive services via the TELNET Protocol
+ ", ["http","ftp","mailto"]).should == ["ftp://ftp.is.co.za/rfc/rfc1808.txt","http://www.math.uio.no/faq/compression-faq/part1.html","mailto:mduerst@ifi.unizh.ch"]
+ end
+end
diff --git a/spec/ruby/library/uri/fixtures/classes.rb b/spec/ruby/library/uri/fixtures/classes.rb
new file mode 100644
index 0000000000..e1179307cc
--- /dev/null
+++ b/spec/ruby/library/uri/fixtures/classes.rb
@@ -0,0 +1,11 @@
+require 'uri'
+
+module URISpec
+ def self.components(uri)
+ result = {}
+ uri.component.each do |component|
+ result[component] = uri.send(component)
+ end
+ result
+ end
+end
diff --git a/spec/ruby/library/uri/fixtures/normalization.rb b/spec/ruby/library/uri/fixtures/normalization.rb
new file mode 100644
index 0000000000..cbc26c9b48
--- /dev/null
+++ b/spec/ruby/library/uri/fixtures/normalization.rb
@@ -0,0 +1,54 @@
+module URISpec
+ # Not an exhaustive list. Refer to rfc3986
+ NORMALIZED_FORMS = [
+ { normalized: "http://example.com/",
+ equivalent: %w{ hTTp://example.com/
+ http://exaMple.com/
+ http://exa%4dple.com/
+ http://exa%4Dple.com/
+ http://exa%6dple.com/
+ http://exa%6Dple.com/
+ http://@example.com/
+ http://example.com:/
+ http://example.com:80/
+ http://example.com
+ },
+ different: %w{ http://example.com/#
+ http://example.com/?
+ http://example.com:8888/
+ http:///example.com
+ http:example.com
+ https://example.com/
+ },
+ },
+ { normalized: "http://example.com/index.html",
+ equivalent: %w{ http://example.com/index.ht%6dl
+ http://example.com/index.ht%6Dl
+ },
+ different: %w{ http://example.com/index.hTMl
+ http://example.com/index.ht%4dl
+ http://example.com/index
+ http://example.com/
+ http://example.com/
+ },
+ },
+ { normalized: "http://example.com/x?y#z",
+ equivalent: %w{ http://example.com/x?y#%7a
+ http://example.com/x?y#%7A
+ http://example.com/x?%79#z
+ },
+ different: %w{ http://example.com/x?Y#z
+ http://example.com/x?y#Z
+ http://example.com/x?y=#z
+ http://example.com/x?y
+ http://example.com/x#z
+ },
+ },
+ { normalized: "http://example.com/x?q=a%20b",
+ equivalent: %w{
+ },
+ different: %w{ http://example.com/x?q=a+b
+ },
+ },
+ ]
+end
diff --git a/spec/ruby/library/uri/ftp/build_spec.rb b/spec/ruby/library/uri/ftp/build_spec.rb
new file mode 100644
index 0000000000..9e0fb44cf1
--- /dev/null
+++ b/spec/ruby/library/uri/ftp/build_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::FTP.build" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/ftp/merge_spec.rb b/spec/ruby/library/uri/ftp/merge_spec.rb
new file mode 100644
index 0000000000..7a9997bbac
--- /dev/null
+++ b/spec/ruby/library/uri/ftp/merge_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::FTP#merge" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/ftp/new2_spec.rb b/spec/ruby/library/uri/ftp/new2_spec.rb
new file mode 100644
index 0000000000..eb1b149c81
--- /dev/null
+++ b/spec/ruby/library/uri/ftp/new2_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::FTP.new2" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/ftp/path_spec.rb b/spec/ruby/library/uri/ftp/path_spec.rb
new file mode 100644
index 0000000000..5fec7f11b6
--- /dev/null
+++ b/spec/ruby/library/uri/ftp/path_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::FTP#path=" do
+ before :each do
+ @url = URI.parse('ftp://example.com')
+ end
+
+ it "does not require a leading /" do
+ @url.path = 'foo'
+ @url.path.should == 'foo'
+ end
+
+ it "does not strip the leading /" do
+ @url.path = '/foo'
+ @url.path.should == '/foo'
+ end
+end
+
+describe "URI::FTP#path" do
+ it "unescapes the leading /" do
+ url = URI.parse('ftp://example.com/%2Ffoo')
+
+ url.path.should == '/foo'
+ end
+end
diff --git a/spec/ruby/library/uri/ftp/set_typecode_spec.rb b/spec/ruby/library/uri/ftp/set_typecode_spec.rb
new file mode 100644
index 0000000000..31067930c0
--- /dev/null
+++ b/spec/ruby/library/uri/ftp/set_typecode_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::FTP#set_typecode" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/ftp/to_s_spec.rb b/spec/ruby/library/uri/ftp/to_s_spec.rb
new file mode 100644
index 0000000000..3b4ff2d906
--- /dev/null
+++ b/spec/ruby/library/uri/ftp/to_s_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+
+describe "URI::FTP#to_s" do
+ before :each do
+ @url = URI.parse('ftp://example.com')
+ end
+
+ it "escapes the leading /" do
+ @url.path = '/foo'
+
+ @url.to_s.should == 'ftp://example.com/%2Ffoo'
+ end
+end
diff --git a/spec/ruby/library/uri/ftp/typecode_spec.rb b/spec/ruby/library/uri/ftp/typecode_spec.rb
new file mode 100644
index 0000000000..1f2bb02252
--- /dev/null
+++ b/spec/ruby/library/uri/ftp/typecode_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::FTP#typecode" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::FTP#typecode=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/absolute_spec.rb b/spec/ruby/library/uri/generic/absolute_spec.rb
new file mode 100644
index 0000000000..fe4b48d067
--- /dev/null
+++ b/spec/ruby/library/uri/generic/absolute_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#absolute" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic#absolute?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/build2_spec.rb b/spec/ruby/library/uri/generic/build2_spec.rb
new file mode 100644
index 0000000000..9abd1d80ef
--- /dev/null
+++ b/spec/ruby/library/uri/generic/build2_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic.build2" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/build_spec.rb b/spec/ruby/library/uri/generic/build_spec.rb
new file mode 100644
index 0000000000..50c27674ce
--- /dev/null
+++ b/spec/ruby/library/uri/generic/build_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic.build" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/coerce_spec.rb b/spec/ruby/library/uri/generic/coerce_spec.rb
new file mode 100644
index 0000000000..f695e560ac
--- /dev/null
+++ b/spec/ruby/library/uri/generic/coerce_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#coerce" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/component_ary_spec.rb b/spec/ruby/library/uri/generic/component_ary_spec.rb
new file mode 100644
index 0000000000..b39752f8d9
--- /dev/null
+++ b/spec/ruby/library/uri/generic/component_ary_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#component_ary" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/component_spec.rb b/spec/ruby/library/uri/generic/component_spec.rb
new file mode 100644
index 0000000000..f92409a0b0
--- /dev/null
+++ b/spec/ruby/library/uri/generic/component_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#component" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic.component" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/default_port_spec.rb b/spec/ruby/library/uri/generic/default_port_spec.rb
new file mode 100644
index 0000000000..4e10e34c9d
--- /dev/null
+++ b/spec/ruby/library/uri/generic/default_port_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#default_port" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic.default_port" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/eql_spec.rb b/spec/ruby/library/uri/generic/eql_spec.rb
new file mode 100644
index 0000000000..df9987b524
--- /dev/null
+++ b/spec/ruby/library/uri/generic/eql_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#eql?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/equal_value_spec.rb b/spec/ruby/library/uri/generic/equal_value_spec.rb
new file mode 100644
index 0000000000..bd2feb86d4
--- /dev/null
+++ b/spec/ruby/library/uri/generic/equal_value_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#==" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/fragment_spec.rb b/spec/ruby/library/uri/generic/fragment_spec.rb
new file mode 100644
index 0000000000..20126b207a
--- /dev/null
+++ b/spec/ruby/library/uri/generic/fragment_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#fragment" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic#fragment=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/hash_spec.rb b/spec/ruby/library/uri/generic/hash_spec.rb
new file mode 100644
index 0000000000..286c1ab38d
--- /dev/null
+++ b/spec/ruby/library/uri/generic/hash_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#hash" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/hierarchical_spec.rb b/spec/ruby/library/uri/generic/hierarchical_spec.rb
new file mode 100644
index 0000000000..df9bbae202
--- /dev/null
+++ b/spec/ruby/library/uri/generic/hierarchical_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#hierarchical?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/host_spec.rb b/spec/ruby/library/uri/generic/host_spec.rb
new file mode 100644
index 0000000000..f2076d2bc1
--- /dev/null
+++ b/spec/ruby/library/uri/generic/host_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#host" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic#host=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/inspect_spec.rb b/spec/ruby/library/uri/generic/inspect_spec.rb
new file mode 100644
index 0000000000..4ff81eef82
--- /dev/null
+++ b/spec/ruby/library/uri/generic/inspect_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#inspect" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/merge_spec.rb b/spec/ruby/library/uri/generic/merge_spec.rb
new file mode 100644
index 0000000000..017873cc90
--- /dev/null
+++ b/spec/ruby/library/uri/generic/merge_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#merge" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic#merge!" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/minus_spec.rb b/spec/ruby/library/uri/generic/minus_spec.rb
new file mode 100644
index 0000000000..ad8f816839
--- /dev/null
+++ b/spec/ruby/library/uri/generic/minus_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#-" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/normalize_spec.rb b/spec/ruby/library/uri/generic/normalize_spec.rb
new file mode 100644
index 0000000000..d70a77c044
--- /dev/null
+++ b/spec/ruby/library/uri/generic/normalize_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#normalize" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic#normalize!" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/opaque_spec.rb b/spec/ruby/library/uri/generic/opaque_spec.rb
new file mode 100644
index 0000000000..e6d40da52b
--- /dev/null
+++ b/spec/ruby/library/uri/generic/opaque_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#opaque" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic#opaque=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/password_spec.rb b/spec/ruby/library/uri/generic/password_spec.rb
new file mode 100644
index 0000000000..18db503883
--- /dev/null
+++ b/spec/ruby/library/uri/generic/password_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#password" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic#password=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/path_spec.rb b/spec/ruby/library/uri/generic/path_spec.rb
new file mode 100644
index 0000000000..d84975c579
--- /dev/null
+++ b/spec/ruby/library/uri/generic/path_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#path" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic#path=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/plus_spec.rb b/spec/ruby/library/uri/generic/plus_spec.rb
new file mode 100644
index 0000000000..e6d2222dac
--- /dev/null
+++ b/spec/ruby/library/uri/generic/plus_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#+" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/port_spec.rb b/spec/ruby/library/uri/generic/port_spec.rb
new file mode 100644
index 0000000000..6e5ef01493
--- /dev/null
+++ b/spec/ruby/library/uri/generic/port_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#port" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic#port=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/query_spec.rb b/spec/ruby/library/uri/generic/query_spec.rb
new file mode 100644
index 0000000000..528cc3be02
--- /dev/null
+++ b/spec/ruby/library/uri/generic/query_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#query" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic#query=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/registry_spec.rb b/spec/ruby/library/uri/generic/registry_spec.rb
new file mode 100644
index 0000000000..aece265a07
--- /dev/null
+++ b/spec/ruby/library/uri/generic/registry_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#registry" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic#registry=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/relative_spec.rb b/spec/ruby/library/uri/generic/relative_spec.rb
new file mode 100644
index 0000000000..a7de1f306a
--- /dev/null
+++ b/spec/ruby/library/uri/generic/relative_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#relative?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/route_from_spec.rb b/spec/ruby/library/uri/generic/route_from_spec.rb
new file mode 100644
index 0000000000..fd69816edf
--- /dev/null
+++ b/spec/ruby/library/uri/generic/route_from_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#route_from" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/route_to_spec.rb b/spec/ruby/library/uri/generic/route_to_spec.rb
new file mode 100644
index 0000000000..7ab9aff2e8
--- /dev/null
+++ b/spec/ruby/library/uri/generic/route_to_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#route_to" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/scheme_spec.rb b/spec/ruby/library/uri/generic/scheme_spec.rb
new file mode 100644
index 0000000000..7922a8e977
--- /dev/null
+++ b/spec/ruby/library/uri/generic/scheme_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#scheme" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic#scheme=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/select_spec.rb b/spec/ruby/library/uri/generic/select_spec.rb
new file mode 100644
index 0000000000..99aef83f99
--- /dev/null
+++ b/spec/ruby/library/uri/generic/select_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#select" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/set_fragment_spec.rb b/spec/ruby/library/uri/generic/set_fragment_spec.rb
new file mode 100644
index 0000000000..2476315f08
--- /dev/null
+++ b/spec/ruby/library/uri/generic/set_fragment_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#set_fragment" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/set_host_spec.rb b/spec/ruby/library/uri/generic/set_host_spec.rb
new file mode 100644
index 0000000000..c7f5c6884e
--- /dev/null
+++ b/spec/ruby/library/uri/generic/set_host_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#set_host" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/set_opaque_spec.rb b/spec/ruby/library/uri/generic/set_opaque_spec.rb
new file mode 100644
index 0000000000..8a494a7ee2
--- /dev/null
+++ b/spec/ruby/library/uri/generic/set_opaque_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#set_opaque" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/set_password_spec.rb b/spec/ruby/library/uri/generic/set_password_spec.rb
new file mode 100644
index 0000000000..93b05fe911
--- /dev/null
+++ b/spec/ruby/library/uri/generic/set_password_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#set_password" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/set_path_spec.rb b/spec/ruby/library/uri/generic/set_path_spec.rb
new file mode 100644
index 0000000000..6d9f59d1a5
--- /dev/null
+++ b/spec/ruby/library/uri/generic/set_path_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#set_path" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/set_port_spec.rb b/spec/ruby/library/uri/generic/set_port_spec.rb
new file mode 100644
index 0000000000..2c8a4edd22
--- /dev/null
+++ b/spec/ruby/library/uri/generic/set_port_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#set_port" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/set_query_spec.rb b/spec/ruby/library/uri/generic/set_query_spec.rb
new file mode 100644
index 0000000000..3f3453ba8e
--- /dev/null
+++ b/spec/ruby/library/uri/generic/set_query_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#set_query" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/set_registry_spec.rb b/spec/ruby/library/uri/generic/set_registry_spec.rb
new file mode 100644
index 0000000000..44afe246d1
--- /dev/null
+++ b/spec/ruby/library/uri/generic/set_registry_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#set_registry" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/set_scheme_spec.rb b/spec/ruby/library/uri/generic/set_scheme_spec.rb
new file mode 100644
index 0000000000..ffa29da446
--- /dev/null
+++ b/spec/ruby/library/uri/generic/set_scheme_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#set_scheme" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/set_user_spec.rb b/spec/ruby/library/uri/generic/set_user_spec.rb
new file mode 100644
index 0000000000..9a39e1f4c3
--- /dev/null
+++ b/spec/ruby/library/uri/generic/set_user_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#set_user" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/set_userinfo_spec.rb b/spec/ruby/library/uri/generic/set_userinfo_spec.rb
new file mode 100644
index 0000000000..76878204d2
--- /dev/null
+++ b/spec/ruby/library/uri/generic/set_userinfo_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#set_userinfo" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/to_s_spec.rb b/spec/ruby/library/uri/generic/to_s_spec.rb
new file mode 100644
index 0000000000..8c90d7645b
--- /dev/null
+++ b/spec/ruby/library/uri/generic/to_s_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#to_s" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/use_registry_spec.rb b/spec/ruby/library/uri/generic/use_registry_spec.rb
new file mode 100644
index 0000000000..bdfe27c048
--- /dev/null
+++ b/spec/ruby/library/uri/generic/use_registry_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic.use_registry" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/user_spec.rb b/spec/ruby/library/uri/generic/user_spec.rb
new file mode 100644
index 0000000000..345412ca29
--- /dev/null
+++ b/spec/ruby/library/uri/generic/user_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#user" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic#user=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/generic/userinfo_spec.rb b/spec/ruby/library/uri/generic/userinfo_spec.rb
new file mode 100644
index 0000000000..4bf111079c
--- /dev/null
+++ b/spec/ruby/library/uri/generic/userinfo_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Generic#userinfo" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::Generic#userinfo=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/http/build_spec.rb b/spec/ruby/library/uri/http/build_spec.rb
new file mode 100644
index 0000000000..d34cf83ecf
--- /dev/null
+++ b/spec/ruby/library/uri/http/build_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::HTTP.build" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/http/request_uri_spec.rb b/spec/ruby/library/uri/http/request_uri_spec.rb
new file mode 100644
index 0000000000..7b05147d36
--- /dev/null
+++ b/spec/ruby/library/uri/http/request_uri_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::HTTP.request_uri" do
+ it "returns a string of the path + query" do
+ URI("http://reddit.com/r/ruby/").request_uri.should == "/r/ruby/"
+ URI("http://reddit.com/r/ruby/search?q=rubinius").request_uri.should == "/r/ruby/search?q=rubinius"
+ end
+
+ it "returns '/' if the path of the URI is blank" do
+ URI("http://ruby.reddit.com").request_uri.should == "/"
+ end
+end
+describe "URI::HTTP#request_uri" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/join_spec.rb b/spec/ruby/library/uri/join_spec.rb
new file mode 100644
index 0000000000..6033ecf44c
--- /dev/null
+++ b/spec/ruby/library/uri/join_spec.rb
@@ -0,0 +1,59 @@
+require_relative '../../spec_helper'
+require 'uri'
+
+describe "URI.join" do
+ it "returns a URI object of the concatenation of a protocol and domain, and a path" do
+ URI.join("http://localhost/","main.rbx").should == URI.parse("http://localhost/main.rbx")
+ end
+
+ it "accepts URI objects" do
+ URI.join(URI("http://localhost/"),"main.rbx").should == URI.parse("http://localhost/main.rbx")
+ URI.join("http://localhost/",URI("main.rbx")).should == URI.parse("http://localhost/main.rbx")
+ URI.join(URI("http://localhost/"),URI("main.rbx")).should == URI.parse("http://localhost/main.rbx")
+ end
+
+ it "accepts string-like arguments with to_str" do
+ str = mock('string-like')
+ str.should_receive(:to_str).and_return("http://ruby-lang.org")
+ str2 = mock('string-like also')
+ str2.should_receive(:to_str).and_return("foo/bar")
+ URI.join(str, str2).should == URI.parse("http://ruby-lang.org/foo/bar")
+ end
+
+ it "raises an error if given no argument" do
+ lambda {
+ URI.join
+ }.should raise_error(ArgumentError)
+ end
+
+ it "doesn't create redundant '/'s" do
+ URI.join("http://localhost/", "/main.rbx").should == URI.parse("http://localhost/main.rbx")
+ end
+
+ it "discards arguments given before an absolute uri" do
+ URI.join("http://localhost/a/b/c/d", "http://ruby-lang.com/foo", "bar").should == URI.parse("http://ruby-lang.com/bar")
+ end
+
+ it "resolves .. in paths" do
+ URI.join("http://localhost/a/b/c/d", "../../e/f", "g/h/../i").to_s.should == "http://localhost/a/e/g/i"
+ end
+end
+
+
+# assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo/bar'))
+# assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo', 'bar'))
+# assert_equal(URI.parse('http://foo/bar/'), URI.join('http://foo', 'bar/'))
+#
+# assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', 'baz'))
+# assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', '/baz'))
+# assert_equal(URI.parse('http://foo/baz/'), URI.join('http://foo', 'bar', '/baz/'))
+# assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/', 'baz'))
+# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar', 'baz', 'hoge'))
+#
+# assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/baz'))
+# assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge'))
+# assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge'))
+# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge'))
+# assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge'))
+# assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge'))
+# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge'))
diff --git a/spec/ruby/library/uri/ldap/attributes_spec.rb b/spec/ruby/library/uri/ldap/attributes_spec.rb
new file mode 100644
index 0000000000..88e3328bad
--- /dev/null
+++ b/spec/ruby/library/uri/ldap/attributes_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::LDAP#attributes" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::LDAP#attributes=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/ldap/build_spec.rb b/spec/ruby/library/uri/ldap/build_spec.rb
new file mode 100644
index 0000000000..8d0e312d1a
--- /dev/null
+++ b/spec/ruby/library/uri/ldap/build_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::LDAP.build" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/ldap/dn_spec.rb b/spec/ruby/library/uri/ldap/dn_spec.rb
new file mode 100644
index 0000000000..a5ac02e891
--- /dev/null
+++ b/spec/ruby/library/uri/ldap/dn_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::LDAP#dn" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::LDAP#dn=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/ldap/extensions_spec.rb b/spec/ruby/library/uri/ldap/extensions_spec.rb
new file mode 100644
index 0000000000..473222eb7a
--- /dev/null
+++ b/spec/ruby/library/uri/ldap/extensions_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::LDAP#extensions" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::LDAP#extensions=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/ldap/filter_spec.rb b/spec/ruby/library/uri/ldap/filter_spec.rb
new file mode 100644
index 0000000000..d0b7fcc384
--- /dev/null
+++ b/spec/ruby/library/uri/ldap/filter_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::LDAP#filter" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::LDAP#filter=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/ldap/hierarchical_spec.rb b/spec/ruby/library/uri/ldap/hierarchical_spec.rb
new file mode 100644
index 0000000000..5471c53d76
--- /dev/null
+++ b/spec/ruby/library/uri/ldap/hierarchical_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::LDAP#hierarchical?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/ldap/scope_spec.rb b/spec/ruby/library/uri/ldap/scope_spec.rb
new file mode 100644
index 0000000000..5ea5581671
--- /dev/null
+++ b/spec/ruby/library/uri/ldap/scope_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::LDAP#scope" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::LDAP#scope=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/ldap/set_attributes_spec.rb b/spec/ruby/library/uri/ldap/set_attributes_spec.rb
new file mode 100644
index 0000000000..fdaaa8344a
--- /dev/null
+++ b/spec/ruby/library/uri/ldap/set_attributes_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::LDAP#set_attributes" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/ldap/set_dn_spec.rb b/spec/ruby/library/uri/ldap/set_dn_spec.rb
new file mode 100644
index 0000000000..c50ee6a98d
--- /dev/null
+++ b/spec/ruby/library/uri/ldap/set_dn_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::LDAP#set_dn" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/ldap/set_extensions_spec.rb b/spec/ruby/library/uri/ldap/set_extensions_spec.rb
new file mode 100644
index 0000000000..5a39da4607
--- /dev/null
+++ b/spec/ruby/library/uri/ldap/set_extensions_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::LDAP#set_extensions" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/ldap/set_filter_spec.rb b/spec/ruby/library/uri/ldap/set_filter_spec.rb
new file mode 100644
index 0000000000..c3ede20bb4
--- /dev/null
+++ b/spec/ruby/library/uri/ldap/set_filter_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::LDAP#set_filter" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/ldap/set_scope_spec.rb b/spec/ruby/library/uri/ldap/set_scope_spec.rb
new file mode 100644
index 0000000000..43f3f68f86
--- /dev/null
+++ b/spec/ruby/library/uri/ldap/set_scope_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::LDAP#set_scope" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/mailto/build_spec.rb b/spec/ruby/library/uri/mailto/build_spec.rb
new file mode 100644
index 0000000000..80af931c8c
--- /dev/null
+++ b/spec/ruby/library/uri/mailto/build_spec.rb
@@ -0,0 +1,98 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Mailto.build" do
+ it "conforms to the MatzRuby tests" do
+ ok = []
+ bad = []
+
+ # RFC2368, 6. Examples
+ # mailto:chris@example.com
+ ok << ["mailto:chris@example.com"]
+ ok[-1] << ["chris@example.com", nil]
+ ok[-1] << {to: "chris@example.com"}
+
+ # mailto:infobot@example.com?subject=current-issue
+ ok << ["mailto:infobot@example.com?subject=current-issue"]
+ ok[-1] << ["infobot@example.com", ["subject=current-issue"]]
+ ok[-1] << {to: "infobot@example.com",
+ headers: ["subject=current-issue"]}
+
+ # mailto:infobot@example.com?body=send%20current-issue
+ ok << ["mailto:infobot@example.com?body=send%20current-issue"]
+ ok[-1] << ["infobot@example.com", ["body=send%20current-issue"]]
+ ok[-1] << {to: "infobot@example.com",
+ headers: ["body=send%20current-issue"]}
+
+ # mailto:infobot@example.com?body=send%20current-issue%0D%0Asend%20index
+ ok << ["mailto:infobot@example.com?body=send%20current-issue%0D%0Asend%20index"]
+ ok[-1] << ["infobot@example.com",
+ ["body=send%20current-issue%0D%0Asend%20index"]]
+ ok[-1] << {to: "infobot@example.com",
+ headers: ["body=send%20current-issue%0D%0Asend%20index"]}
+
+ # mailto:foobar@example.com?In-Reply-To=%3c3469A91.D10AF4C@example.com
+ ok << ["mailto:foobar@example.com?In-Reply-To=%3c3469A91.D10AF4C@example.com"]
+ ok[-1] << ["foobar@example.com",
+ ["In-Reply-To=%3c3469A91.D10AF4C@example.com"]]
+ ok[-1] << {to: "foobar@example.com",
+ headers: ["In-Reply-To=%3c3469A91.D10AF4C@example.com"]}
+
+ # mailto:majordomo@example.com?body=subscribe%20bamboo-l
+ ok << ["mailto:majordomo@example.com?body=subscribe%20bamboo-l"]
+ ok[-1] << ["majordomo@example.com", ["body=subscribe%20bamboo-l"]]
+ ok[-1] << {to: "majordomo@example.com",
+ headers: ["body=subscribe%20bamboo-l"]}
+
+ # mailto:joe@example.com?cc=bob@example.com&body=hello
+ ok << ["mailto:joe@example.com?cc=bob@example.com&body=hello"]
+ ok[-1] << ["joe@example.com", ["cc=bob@example.com", "body=hello"]]
+ ok[-1] << {to: "joe@example.com",
+ headers: ["cc=bob@example.com", "body=hello"]}
+
+ # mailto:?to=joe@example.com&cc=bob@example.com&body=hello
+ ok << ["mailto:?to=joe@example.com&cc=bob@example.com&body=hello"]
+ ok[-1] << [nil,
+ ["to=joe@example.com", "cc=bob@example.com", "body=hello"]]
+ ok[-1] << {headers: ["to=joe@example.com", "cc=bob@example.com", "body=hello"]}
+
+ # mailto:gorby%25kremvax@example.com
+ ok << ["mailto:gorby%25kremvax@example.com"]
+ ok[-1] << ["gorby%25kremvax@example.com", nil]
+ ok[-1] << {to: "gorby%25kremvax@example.com"}
+
+ # mailto:unlikely%3Faddress@example.com?blat=foop
+ ok << ["mailto:unlikely%3Faddress@example.com?blat=foop"]
+ ok[-1] << ["unlikely%3Faddress@example.com", ["blat=foop"]]
+ ok[-1] << {to: "unlikely%3Faddress@example.com",
+ headers: ["blat=foop"]}
+
+ ok_all = ok.flatten.join("\0")
+
+ # mailto:joe@example.com?cc=bob@example.com?body=hello ; WRONG!
+ bad << ["joe@example.com", ["cc=bob@example.com?body=hello"]]
+
+ # mailto:javascript:alert()
+ bad << ["javascript:alert()", []]
+
+ # '=' which is in hname or hvalue is wrong.
+ bad << ["foo@example.jp?subject=1+1=2", []]
+
+ ok.each do |x|
+ URI::MailTo.build(x[1]).to_s.should == x[0]
+ URI::MailTo.build(x[2]).to_s.should == x[0]
+ end
+
+ bad.each do |x|
+ lambda { URI::MailTo.build(x) }.should raise_error(URI::InvalidComponentError)
+ end
+
+ ok.flatten.join("\0").should == ok_all
+ end
+end
+
+
+
+describe "URI::MailTo.build" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/mailto/headers_spec.rb b/spec/ruby/library/uri/mailto/headers_spec.rb
new file mode 100644
index 0000000000..8aefec0e75
--- /dev/null
+++ b/spec/ruby/library/uri/mailto/headers_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::MailTo#headers" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::MailTo#headers=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/mailto/set_headers_spec.rb b/spec/ruby/library/uri/mailto/set_headers_spec.rb
new file mode 100644
index 0000000000..b6ce1a694b
--- /dev/null
+++ b/spec/ruby/library/uri/mailto/set_headers_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::MailTo#set_headers" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/mailto/set_to_spec.rb b/spec/ruby/library/uri/mailto/set_to_spec.rb
new file mode 100644
index 0000000000..eabc47f9a8
--- /dev/null
+++ b/spec/ruby/library/uri/mailto/set_to_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::MailTo#set_to" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/mailto/to_mailtext_spec.rb b/spec/ruby/library/uri/mailto/to_mailtext_spec.rb
new file mode 100644
index 0000000000..3763a2d402
--- /dev/null
+++ b/spec/ruby/library/uri/mailto/to_mailtext_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::MailTo#to_mailtext" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/mailto/to_rfc822text_spec.rb b/spec/ruby/library/uri/mailto/to_rfc822text_spec.rb
new file mode 100644
index 0000000000..2843b46848
--- /dev/null
+++ b/spec/ruby/library/uri/mailto/to_rfc822text_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::MailTo#to_rfc822text" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/mailto/to_s_spec.rb b/spec/ruby/library/uri/mailto/to_s_spec.rb
new file mode 100644
index 0000000000..746e8356eb
--- /dev/null
+++ b/spec/ruby/library/uri/mailto/to_s_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::MailTo#to_s" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/mailto/to_spec.rb b/spec/ruby/library/uri/mailto/to_spec.rb
new file mode 100644
index 0000000000..68dfadd359
--- /dev/null
+++ b/spec/ruby/library/uri/mailto/to_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::MailTo#to" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "URI::MailTo#to=" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/merge_spec.rb b/spec/ruby/library/uri/merge_spec.rb
new file mode 100644
index 0000000000..e9644a7fd0
--- /dev/null
+++ b/spec/ruby/library/uri/merge_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require 'uri'
+
+describe "URI#merge" do
+ it "returns the receiver and the argument, joined as per URI.join" do
+ URI("http://localhost/").merge("main.rbx").should == URI.parse("http://localhost/main.rbx")
+ URI("http://localhost/a/b/c/d").merge("http://ruby-lang.com/foo").should == URI.parse("http://ruby-lang.com/foo")
+ URI("http://localhost/a/b/c/d").merge("../../e/f").to_s.should == "http://localhost/a/e/f"
+ end
+
+ it "accepts URI objects as argument" do
+ URI("http://localhost/").merge(URI("main.rbx")).should == URI.parse("http://localhost/main.rbx")
+ end
+
+ it "accepts a string-like argument" do
+ str = mock('string-like')
+ str.should_receive(:to_str).and_return("foo/bar")
+ URI("http://localhost/").merge(str).should == URI.parse("http://localhost/foo/bar")
+ end
+end
diff --git a/spec/ruby/library/uri/normalize_spec.rb b/spec/ruby/library/uri/normalize_spec.rb
new file mode 100644
index 0000000000..3d4451990a
--- /dev/null
+++ b/spec/ruby/library/uri/normalize_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/normalization'
+require 'uri'
+
+describe "URI#normalize" do
+ it "adds a / onto the end of the URI if the path is blank" do
+ no_path = URI("http://example.com")
+ no_path.to_s.should_not == "http://example.com/"
+ no_path.normalize.to_s.should == "http://example.com/"
+ end
+
+ it "downcases the host of the URI" do
+ uri = URI("http://exAMPLE.cOm/")
+ uri.to_s.should_not == "http://example.com/"
+ uri.normalize.to_s.should == "http://example.com/"
+ end
+
+ # The previous tests are included by the one below
+
+ quarantine! do # Quarantined until redmine:2542 is accepted
+ it "respects RFC 3986" do
+ URISpec::NORMALIZED_FORMS.each do |form|
+ normal_uri = URI(form[:normalized])
+ normalized = normal_uri.normalize.to_s
+ normal_uri.to_s.should == normalized
+ form[:equivalent].each do |same|
+ URI(same).normalize.to_s.should == normalized
+ end
+ form[:different].each do |other|
+ URI(other).normalize.to_s.should_not == normalized
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/uri/parse_spec.rb b/spec/ruby/library/uri/parse_spec.rb
new file mode 100644
index 0000000000..e9ec59b490
--- /dev/null
+++ b/spec/ruby/library/uri/parse_spec.rb
@@ -0,0 +1,203 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "URI.parse" do
+
+ it "returns a URI::HTTP object when parsing an HTTP URI" do
+ URI.parse("http://www.example.com/").should be_kind_of(URI::HTTP)
+ end
+
+ it "populates the components of a parsed URI::HTTP, setting the port to 80 by default" do
+ # general case
+ URISpec.components(URI.parse("http://user:pass@example.com/path/?query=val&q2=val2#fragment")).should == {
+ scheme: "http",
+ userinfo: "user:pass",
+ host: "example.com",
+ port: 80,
+ path: "/path/",
+ query: "query=val&q2=val2",
+ fragment: "fragment"
+ }
+
+ # multiple paths
+ URISpec.components(URI.parse("http://a/b/c/d;p?q")).should == {
+ scheme: "http",
+ userinfo: nil,
+ host: "a",
+ port: 80,
+ path: "/b/c/d;p",
+ query: "q",
+ fragment: nil
+ }
+
+ # multi-level domain
+ URISpec.components(URI.parse('http://www.math.uio.no/faq/compression-faq/part1.html')).should == {
+ scheme: "http",
+ userinfo: nil,
+ host: "www.math.uio.no",
+ port: 80,
+ path: "/faq/compression-faq/part1.html",
+ query: nil,
+ fragment: nil
+ }
+ end
+
+ it "parses out the port number of a URI, when given" do
+ URI.parse("http://example.com:8080/").port.should == 8080
+ end
+
+ it "returns a URI::HTTPS object when parsing an HTTPS URI" do
+ URI.parse("https://important-intern-net.net").should be_kind_of(URI::HTTPS)
+ end
+
+ it "sets the port of a parsed https URI to 443 by default" do
+ URI.parse("https://example.com/").port.should == 443
+ end
+
+ it "populates the components of a parsed URI::FTP object" do
+ # generic, empty password.
+ url = URI.parse("ftp://anonymous@ruby-lang.org/pub/ruby/1.8/ruby-1.8.6.tar.bz2;type=i")
+ url.should be_kind_of(URI::FTP)
+ URISpec.components(url).should == {
+ scheme: "ftp",
+ userinfo: "anonymous",
+ host: "ruby-lang.org",
+ port: 21,
+ path: "pub/ruby/1.8/ruby-1.8.6.tar.bz2",
+ typecode: "i"
+ }
+
+ # multidomain, no user or password
+ url = URI.parse('ftp://ftp.is.co.za/rfc/rfc1808.txt')
+ url.should be_kind_of(URI::FTP)
+ URISpec.components(url).should == {
+ scheme: "ftp",
+ userinfo: nil,
+ host: "ftp.is.co.za",
+ port: 21,
+ path: "rfc/rfc1808.txt",
+ typecode: nil
+ }
+
+ # empty user
+ url = URI.parse('ftp://:pass@localhost/')
+ url.should be_kind_of(URI::FTP)
+ URISpec.components(url).should == {
+ scheme: "ftp",
+ userinfo: ":pass",
+ host: "localhost",
+ port: 21,
+ path: "",
+ typecode: nil
+ }
+ url.password.should == "pass"
+ end
+
+ it "returns a URI::LDAP object when parsing an LDAP URI" do
+ #taken from http://www.faqs.org/rfcs/rfc2255.html 'cause I don't really know what an LDAP url looks like
+ ldap_uris = %w{ ldap:///o=University%20of%20Michigan,c=US ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress ldap://host.com:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen) ldap://ldap.itd.umich.edu/c=GB?objectClass?one ldap://ldap.question.com/o=Question%3f,c=US?mail ldap://ldap.netscape.com/o=Babsco,c=US??(int=%5c00%5c00%5c00%5c04) ldap:///??sub??bindname=cn=Manager%2co=Foo ldap:///??sub??!bindname=cn=Manager%2co=Foo }
+ ldap_uris.each do |ldap_uri|
+ URI.parse(ldap_uri).should be_kind_of(URI::LDAP)
+ end
+ end
+
+ it "populates the components of a parsed URI::LDAP object" do
+ URISpec.components(URI.parse("ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress?scope?filter?extensions")).should == {
+ scheme: "ldap",
+ host: "ldap.itd.umich.edu",
+ port: 389,
+ dn: "o=University%20of%20Michigan,c=US",
+ attributes: "postalAddress",
+ scope: "scope",
+ filter: "filter",
+ extensions: "extensions"
+ }
+ end
+
+ it "returns a URI::MailTo object when passed a mailto URI" do
+ URI.parse("mailto:spam@mailinator.com").should be_kind_of(URI::MailTo)
+ end
+
+ it "populates the components of a parsed URI::MailTo object" do
+ URISpec.components(URI.parse("mailto:spam@mailinator.com?subject=Discounts%20On%20Imported%20methods!!!&body=Exciting%20offer")).should == {
+ scheme: "mailto",
+ to: "spam@mailinator.com",
+ headers: [["subject","Discounts%20On%20Imported%20methods!!!"],
+ ["body", "Exciting%20offer"]]
+ }
+ end
+
+ # TODO
+ # Test registry
+ it "does its best to extract components from URI::Generic objects" do
+ # generic
+ URISpec.components(URI("scheme://userinfo@host/path?query#fragment")).should == {
+ scheme: "scheme",
+ userinfo: "userinfo",
+ host: "host",
+ port: nil,
+ path: "/path",
+ query: "query",
+ fragment: "fragment",
+ registry: nil,
+ opaque: nil
+ }
+
+ # gopher
+ gopher = URI.parse('gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles')
+ gopher.should be_kind_of(URI::Generic)
+
+ URISpec.components(gopher).should == {
+ scheme: "gopher",
+ userinfo: nil,
+ host: "spinaltap.micro.umn.edu",
+ port: nil,
+ path: "/00/Weather/California/Los%20Angeles",
+ query: nil,
+ fragment: nil,
+ registry: nil,
+ opaque: nil
+ }
+
+ # news
+ news = URI.parse('news:comp.infosystems.www.servers.unix')
+ news.should be_kind_of(URI::Generic)
+ URISpec.components(news).should == {
+ scheme: "news",
+ userinfo: nil,
+ host: nil,
+ port: nil,
+ path: nil,
+ query: nil,
+ fragment: nil,
+ registry: nil,
+ opaque: "comp.infosystems.www.servers.unix"
+ }
+
+ # telnet
+ telnet = URI.parse('telnet://melvyl.ucop.edu/')
+ telnet.should be_kind_of(URI::Generic)
+ URISpec.components(telnet).should == {
+ scheme: "telnet",
+ userinfo: nil,
+ host: "melvyl.ucop.edu",
+ port: nil,
+ path: "/",
+ query: nil,
+ fragment: nil,
+ registry: nil,
+ opaque: nil
+ }
+
+ # files
+ file_l = URI.parse('file:///foo/bar.txt')
+ file_l.should be_kind_of(URI::Generic)
+ file = URI.parse('file:/foo/bar.txt')
+ file.should be_kind_of(URI::Generic)
+ end
+
+ it "doesn't raise errors on URIs which has underscore in reg_name" do
+ URI.parse('http://a_b:80/').host.should == "a_b"
+ URI.parse('http://a_b/').host.should == "a_b"
+ end
+end
diff --git a/spec/ruby/library/uri/parser/escape_spec.rb b/spec/ruby/library/uri/parser/escape_spec.rb
new file mode 100644
index 0000000000..66853d9fcb
--- /dev/null
+++ b/spec/ruby/library/uri/parser/escape_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Parser#escape" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/parser/extract_spec.rb b/spec/ruby/library/uri/parser/extract_spec.rb
new file mode 100644
index 0000000000..20d4565b08
--- /dev/null
+++ b/spec/ruby/library/uri/parser/extract_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../shared/extract'
+require 'uri'
+
+describe "URI::Parser#extract" do
+ it_behaves_like :uri_extract, :extract, URI::Parser.new
+end
diff --git a/spec/ruby/library/uri/parser/inspect_spec.rb b/spec/ruby/library/uri/parser/inspect_spec.rb
new file mode 100644
index 0000000000..44fbd4077c
--- /dev/null
+++ b/spec/ruby/library/uri/parser/inspect_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Parser#split" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/parser/join_spec.rb b/spec/ruby/library/uri/parser/join_spec.rb
new file mode 100644
index 0000000000..0c9230be76
--- /dev/null
+++ b/spec/ruby/library/uri/parser/join_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../shared/join'
+require 'uri'
+
+describe "URI::Parser#join" do
+ it_behaves_like :uri_join, :join, URI::Parser.new
+end
diff --git a/spec/ruby/library/uri/parser/make_regexp_spec.rb b/spec/ruby/library/uri/parser/make_regexp_spec.rb
new file mode 100644
index 0000000000..0631d13ee6
--- /dev/null
+++ b/spec/ruby/library/uri/parser/make_regexp_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Parser#make_regexp" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/parser/parse_spec.rb b/spec/ruby/library/uri/parser/parse_spec.rb
new file mode 100644
index 0000000000..df126eab6d
--- /dev/null
+++ b/spec/ruby/library/uri/parser/parse_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/parse'
+
+describe "URI::Parser#parse" do
+ it_behaves_like :uri_parse, :parse, URI::Parser.new
+end
diff --git a/spec/ruby/library/uri/parser/split_spec.rb b/spec/ruby/library/uri/parser/split_spec.rb
new file mode 100644
index 0000000000..44fbd4077c
--- /dev/null
+++ b/spec/ruby/library/uri/parser/split_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Parser#split" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/parser/unescape_spec.rb b/spec/ruby/library/uri/parser/unescape_spec.rb
new file mode 100644
index 0000000000..e18d2eb9d3
--- /dev/null
+++ b/spec/ruby/library/uri/parser/unescape_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Parser#unescape" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/plus_spec.rb b/spec/ruby/library/uri/plus_spec.rb
new file mode 100644
index 0000000000..146cfddf90
--- /dev/null
+++ b/spec/ruby/library/uri/plus_spec.rb
@@ -0,0 +1,459 @@
+require_relative '../../spec_helper'
+require 'uri'
+
+#an alias of URI#merge
+describe "URI#+" do
+ it "replaces the end of the path of the URI when added to a string that looks like a relative path" do
+ (URI('http://foo') + 'bar').should == URI("http://foo/bar")
+ (URI('http://foo/baz') + 'bar').should == URI("http://foo/bar")
+ (URI('http://foo/baz/') + 'bar').should == URI("http://foo/baz/bar")
+ (URI('mailto:foo@example.com') + "#bar").should == URI("mailto:foo@example.com#bar")
+ end
+
+ it "replaces the entire path of the URI when added to a string that begins with a /" do
+ (URI('http://foo/baz/') + '/bar').should == URI("http://foo/bar")
+ end
+
+ it "replaces the entire url when added to a string that looks like a full url" do
+ (URI.parse('http://a/b') + 'http://x/y').should == URI("http://x/y")
+ (URI.parse('telnet:example.com') + 'http://x/y').should == URI("http://x/y")
+ end
+
+ it "canonicalizes the URI's path, removing ../'s" do
+ (URI.parse('http://a/b/c/../') + "./").should == URI("http://a/b/")
+ (URI.parse('http://a/b/c/../') + ".").should == URI("http://a/b/")
+ (URI.parse('http://a/b/c/') + "../").should == URI("http://a/b/")
+ (URI.parse('http://a/b/c/../../') + "./").should == URI("http://a/")
+ (URI.parse('http://a/b/c/') + "../e/").should == URI("http://a/b/e/")
+ (URI.parse('http://a/b/c/') + "../e/../").should == URI("http://a/b/")
+ (URI.parse('http://a/b/../c/') + ".").should == URI("http://a/c/")
+
+ (URI.parse('http://a/b/c/../../../') + ".").should == URI("http://a/")
+ end
+
+ it "doesn't conconicalize the path when adding to the empty string" do
+ (URI.parse('http://a/b/c/../') + "").should == URI("http://a/b/c/../")
+ end
+
+ it "raises a URI::BadURIError when adding two relative URIs" do
+ lambda {URI.parse('a/b/c') + "d"}.should raise_error(URI::BadURIError)
+ end
+
+ #Todo: make more BDD?
+ it "conforms to the merge specifications from rfc 2396" do
+ @url = 'http://a/b/c/d;p?q'
+ @base_url = URI.parse(@url)
+
+# http://a/b/c/d;p?q
+# g:h = g:h
+ url = @base_url.merge('g:h')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'g:h'
+ url = @base_url.route_to('g:h')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'g:h'
+
+# http://a/b/c/d;p?q
+# g = http://a/b/c/g
+ url = @base_url.merge('g')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g'
+ url = @base_url.route_to('http://a/b/c/g')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'g'
+
+# http://a/b/c/d;p?q
+# ./g = http://a/b/c/g
+ url = @base_url.merge('./g')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g'
+ url = @base_url.route_to('http://a/b/c/g')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should_not == './g' # ok
+ url.to_s.should == 'g'
+
+# http://a/b/c/d;p?q
+# g/ = http://a/b/c/g/
+ url = @base_url.merge('g/')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g/'
+ url = @base_url.route_to('http://a/b/c/g/')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'g/'
+
+# http://a/b/c/d;p?q
+# /g = http://a/g
+ url = @base_url.merge('/g')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/g'
+ url = @base_url.route_to('http://a/g')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should_not == '/g' # ok
+ url.to_s.should == '../../g'
+
+# http://a/b/c/d;p?q
+# //g = http://g
+ url = @base_url.merge('//g')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://g'
+ url = @base_url.route_to('http://g')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == '//g'
+
+# http://a/b/c/d;p?q
+# ?y = http://a/b/c/?y
+ url = @base_url.merge('?y')
+ url.should be_kind_of(URI::HTTP)
+
+ url.to_s.should == 'http://a/b/c/d;p?y'
+
+ url = @base_url.route_to('http://a/b/c/?y')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == '?y'
+
+# http://a/b/c/d;p?q
+# g?y = http://a/b/c/g?y
+ url = @base_url.merge('g?y')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g?y'
+ url = @base_url.route_to('http://a/b/c/g?y')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'g?y'
+
+# http://a/b/c/d;p?q
+# #s = (current document)#s
+ url = @base_url.merge('#s')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == @base_url.to_s + '#s'
+ url = @base_url.route_to(@base_url.to_s + '#s')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == '#s'
+
+# http://a/b/c/d;p?q
+# g#s = http://a/b/c/g#s
+ url = @base_url.merge('g#s')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g#s'
+ url = @base_url.route_to('http://a/b/c/g#s')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'g#s'
+
+# http://a/b/c/d;p?q
+# g?y#s = http://a/b/c/g?y#s
+ url = @base_url.merge('g?y#s')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g?y#s'
+ url = @base_url.route_to('http://a/b/c/g?y#s')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'g?y#s'
+
+# http://a/b/c/d;p?q
+# ;x = http://a/b/c/;x
+ url = @base_url.merge(';x')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/;x'
+ url = @base_url.route_to('http://a/b/c/;x')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == ';x'
+
+# http://a/b/c/d;p?q
+# g;x = http://a/b/c/g;x
+ url = @base_url.merge('g;x')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g;x'
+ url = @base_url.route_to('http://a/b/c/g;x')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'g;x'
+
+# http://a/b/c/d;p?q
+# g;x?y#s = http://a/b/c/g;x?y#s
+ url = @base_url.merge('g;x?y#s')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g;x?y#s'
+ url = @base_url.route_to('http://a/b/c/g;x?y#s')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'g;x?y#s'
+
+# http://a/b/c/d;p?q
+# . = http://a/b/c/
+ url = @base_url.merge('.')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/'
+ url = @base_url.route_to('http://a/b/c/')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should_not == '.' # ok
+ url.to_s.should == './'
+
+# http://a/b/c/d;p?q
+# ./ = http://a/b/c/
+ url = @base_url.merge('./')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/'
+ url = @base_url.route_to('http://a/b/c/')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == './'
+
+# http://a/b/c/d;p?q
+# .. = http://a/b/
+ url = @base_url.merge('..')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/'
+ url = @base_url.route_to('http://a/b/')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should_not == '..' # ok
+ url.to_s.should == '../'
+
+# http://a/b/c/d;p?q
+# ../ = http://a/b/
+ url = @base_url.merge('../')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/'
+ url = @base_url.route_to('http://a/b/')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == '../'
+
+# http://a/b/c/d;p?q
+# ../g = http://a/b/g
+ url = @base_url.merge('../g')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/g'
+ url = @base_url.route_to('http://a/b/g')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == '../g'
+
+# http://a/b/c/d;p?q
+# ../.. = http://a/
+ url = @base_url.merge('../..')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/'
+ url = @base_url.route_to('http://a/')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should_not == '../..' # ok
+ url.to_s.should == '../../'
+
+# http://a/b/c/d;p?q
+# ../../ = http://a/
+ url = @base_url.merge('../../')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/'
+ url = @base_url.route_to('http://a/')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == '../../'
+
+# http://a/b/c/d;p?q
+# ../../g = http://a/g
+ url = @base_url.merge('../../g')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/g'
+ url = @base_url.route_to('http://a/g')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == '../../g'
+
+# http://a/b/c/d;p?q
+# <> = (current document)
+ url = @base_url.merge('')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/d;p?q'
+ url = @base_url.route_to('http://a/b/c/d;p?q')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == ''
+
+# http://a/b/c/d;p?q
+# /./g = http://a/./g
+ url = @base_url.merge('/./g')
+ url.should be_kind_of(URI::HTTP)
+
+ url.to_s.should == 'http://a/g'
+
+ url = @base_url.route_to('http://a/./g')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == '/./g'
+
+# http://a/b/c/d;p?q
+# /../g = http://a/../g
+ url = @base_url.merge('/../g')
+ url.should be_kind_of(URI::HTTP)
+
+ url.to_s.should == 'http://a/g'
+
+ url = @base_url.route_to('http://a/../g')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == '/../g'
+
+# http://a/b/c/d;p?q
+# g. = http://a/b/c/g.
+ url = @base_url.merge('g.')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g.'
+ url = @base_url.route_to('http://a/b/c/g.')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'g.'
+
+# http://a/b/c/d;p?q
+# .g = http://a/b/c/.g
+ url = @base_url.merge('.g')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/.g'
+ url = @base_url.route_to('http://a/b/c/.g')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == '.g'
+
+# http://a/b/c/d;p?q
+# g.. = http://a/b/c/g..
+ url = @base_url.merge('g..')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g..'
+ url = @base_url.route_to('http://a/b/c/g..')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'g..'
+
+# http://a/b/c/d;p?q
+# ..g = http://a/b/c/..g
+ url = @base_url.merge('..g')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/..g'
+ url = @base_url.route_to('http://a/b/c/..g')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == '..g'
+
+# http://a/b/c/d;p?q
+# ../../../g = http://a/../g
+ url = @base_url.merge('../../../g')
+ url.should be_kind_of(URI::HTTP)
+
+ url.to_s.should == 'http://a/g'
+
+ url = @base_url.route_to('http://a/../g')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should_not == '../../../g' # ok? yes, it confuses you
+ url.to_s.should == '/../g' # and it is clearly
+
+# http://a/b/c/d;p?q
+# ../../../../g = http://a/../../g
+ url = @base_url.merge('../../../../g')
+ url.should be_kind_of(URI::HTTP)
+
+ url.to_s.should == 'http://a/g'
+
+ url = @base_url.route_to('http://a/../../g')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should_not == '../../../../g' # ok? yes, it confuses you
+ url.to_s.should == '/../../g' # and it is clearly
+
+# http://a/b/c/d;p?q
+# ./../g = http://a/b/g
+ url = @base_url.merge('./../g')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/g'
+ url = @base_url.route_to('http://a/b/g')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should_not == './../g' # ok
+ url.to_s.should == '../g'
+
+# http://a/b/c/d;p?q
+# ./g/. = http://a/b/c/g/
+ url = @base_url.merge('./g/.')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g/'
+ url = @base_url.route_to('http://a/b/c/g/')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should_not == './g/.' # ok
+ url.to_s.should == 'g/'
+
+# http://a/b/c/d;p?q
+# g/./h = http://a/b/c/g/h
+ url = @base_url.merge('g/./h')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g/h'
+ url = @base_url.route_to('http://a/b/c/g/h')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should_not == 'g/./h' # ok
+ url.to_s.should == 'g/h'
+
+# http://a/b/c/d;p?q
+# g/../h = http://a/b/c/h
+ url = @base_url.merge('g/../h')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/h'
+ url = @base_url.route_to('http://a/b/c/h')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should_not == 'g/../h' # ok
+ url.to_s.should == 'h'
+
+# http://a/b/c/d;p?q
+# g;x=1/./y = http://a/b/c/g;x=1/y
+ url = @base_url.merge('g;x=1/./y')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g;x=1/y'
+ url = @base_url.route_to('http://a/b/c/g;x=1/y')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should_not == 'g;x=1/./y' # ok
+ url.to_s.should == 'g;x=1/y'
+
+# http://a/b/c/d;p?q
+# g;x=1/../y = http://a/b/c/y
+ url = @base_url.merge('g;x=1/../y')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/y'
+ url = @base_url.route_to('http://a/b/c/y')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should_not == 'g;x=1/../y' # ok
+ url.to_s.should == 'y'
+
+# http://a/b/c/d;p?q
+# g?y/./x = http://a/b/c/g?y/./x
+ url = @base_url.merge('g?y/./x')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g?y/./x'
+ url = @base_url.route_to('http://a/b/c/g?y/./x')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'g?y/./x'
+
+# http://a/b/c/d;p?q
+# g?y/../x = http://a/b/c/g?y/../x
+ url = @base_url.merge('g?y/../x')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g?y/../x'
+ url = @base_url.route_to('http://a/b/c/g?y/../x')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'g?y/../x'
+
+# http://a/b/c/d;p?q
+# g#s/./x = http://a/b/c/g#s/./x
+ url = @base_url.merge('g#s/./x')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g#s/./x'
+ url = @base_url.route_to('http://a/b/c/g#s/./x')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'g#s/./x'
+
+# http://a/b/c/d;p?q
+# g#s/../x = http://a/b/c/g#s/../x
+ url = @base_url.merge('g#s/../x')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http://a/b/c/g#s/../x'
+ url = @base_url.route_to('http://a/b/c/g#s/../x')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'g#s/../x'
+
+# http://a/b/c/d;p?q
+# http:g = http:g ; for validating parsers
+# | http://a/b/c/g ; for backwards compatibility
+ url = @base_url.merge('http:g')
+ url.should be_kind_of(URI::HTTP)
+ url.to_s.should == 'http:g'
+ url = @base_url.route_to('http:g')
+ url.should be_kind_of(URI::Generic)
+ url.to_s.should == 'http:g'
+ end
+end
+
+#TODO: incorporate these tests:
+#
+# u = URI.parse('http://foo/bar/baz')
+# assert_equal(nil, u.merge!(""))
+# assert_equal(nil, u.merge!(u))
+# assert(nil != u.merge!("."))
+# assert_equal('http://foo/bar/', u.to_s)
+# assert(nil != u.merge!("../baz"))
+# assert_equal('http://foo/baz', u.to_s)
diff --git a/spec/ruby/library/uri/regexp_spec.rb b/spec/ruby/library/uri/regexp_spec.rb
new file mode 100644
index 0000000000..6e8b3df4d0
--- /dev/null
+++ b/spec/ruby/library/uri/regexp_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require 'uri'
+
+#I'm more or less ok with these limited tests, as the more extensive extract tests
+#use URI.regexp
+describe "URI.regexp" do
+ it "behaves according to the MatzRuby tests" do
+ URI.regexp.should == URI.regexp
+ 'x http:// x'.slice(URI.regexp).should == 'http://'
+ 'x http:// x'.slice(URI.regexp(['http'])).should == 'http://'
+ 'x http:// x ftp://'.slice(URI.regexp(['http'])).should == 'http://'
+ 'http://'.slice(URI.regexp([])).should == nil
+ ''.slice(URI.regexp).should == nil
+ 'xxxx'.slice(URI.regexp).should == nil
+ ':'.slice(URI.regexp).should == nil
+ 'From:'.slice(URI.regexp).should == 'From:'
+ end
+end
diff --git a/spec/ruby/library/uri/route_from_spec.rb b/spec/ruby/library/uri/route_from_spec.rb
new file mode 100644
index 0000000000..501f455775
--- /dev/null
+++ b/spec/ruby/library/uri/route_from_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../spec_helper'
+require 'uri'
+
+describe "URI#route_from" do
+
+ #this could be split out a good bit better
+ it "gives the minimal difference between the current URI and the target" do
+ URI("http://example.com/a.html").route_from('http://example.com/a.html').to_s.should == ""
+ URI("http://example.com/a.html").route_from('http://example.com/b.html').to_s.should == "a.html"
+ URI("http://example.com/a/").route_from('http://example.com/b/').to_s.should == "../a/"
+ URI("http://example.com/b/").route_from('http://example.com/a/c').to_s.should == "../b/"
+ URI("http://example.com/b/").route_from('http://example.com/a/b/').to_s.should == "../../b/"
+ URI("http://example.com/b/").route_from('http://EXAMPLE.cOm/a/b/').to_s.should == "../../b/"
+ URI("http://example.net/b/").route_from('http://example.com/a/b/').to_s.should == "//example.net/b/"
+ URI("mailto:foo@example.com#bar").route_from('mailto:foo@example.com').to_s.should == "#bar"
+ end
+
+ it "accepts a string-like argument" do
+ str = mock('string-like')
+ str.should_receive(:to_str).and_return("http://example.com/b.html")
+ URI("http://example.com/a.html").route_from(str).to_s.should == "a.html"
+ end
+end
diff --git a/spec/ruby/library/uri/route_to_spec.rb b/spec/ruby/library/uri/route_to_spec.rb
new file mode 100644
index 0000000000..ae9d38d23d
--- /dev/null
+++ b/spec/ruby/library/uri/route_to_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require 'uri'
+
+describe "URI#route_to" do
+
+ #this could be split out a good bit better
+ it "gives the minimal difference between the current URI and the target" do
+ URI("http://example.com/a.html").route_to('http://example.com/a.html').to_s.should == ""
+ URI("http://example.com/a.html").route_to('http://example.com/b.html').to_s.should == "b.html"
+ URI("http://example.com/a/").route_to('http://example.com/b/').to_s.should == "../b/"
+ URI("http://example.com/a/c").route_to('http://example.com/b/').to_s.should == "../b/"
+ URI("http://example.com/a/b/").route_to('http://example.com/b/').to_s.should == "../../b/"
+ URI("http://example.com/a/b/").route_to('http://EXAMPLE.cOm/b/').to_s.should == "../../b/"
+ URI("http://example.com/a/b/").route_to('http://example.net/b/').to_s.should == "//example.net/b/"
+ URI("mailto:foo@example.com").route_to('mailto:foo@example.com#bar').to_s.should == "#bar"
+
+ #this was a little surprising to me
+ URI("mailto:foo@example.com#bar").route_to('mailto:foo@example.com').to_s.should == ""
+ end
+
+ it "accepts a string-like argument" do
+ str = mock('string-like')
+ str.should_receive(:to_str).and_return("http://example.com/b.html")
+ URI("http://example.com/a.html").route_to(str).to_s.should == "b.html"
+ end
+end
diff --git a/spec/ruby/library/uri/select_spec.rb b/spec/ruby/library/uri/select_spec.rb
new file mode 100644
index 0000000000..6e8c68cf40
--- /dev/null
+++ b/spec/ruby/library/uri/select_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+require 'uri'
+
+describe "URI#select" do
+ it "takes any number of component names as symbols, and returns an array of those components" do
+ URI("http://host:8080/path/").select.should == []
+ URI("http://host:8080/path/").select(:scheme,:host,:port,:path).should == [
+ "http","host",8080,"/path/"]
+ end
+
+ it "returns nil for any valid component that isn't set and doesn't have a default" do
+ uri = URI("http://host")
+ uri.select(:userinfo, :query, :fragment).should == [nil] * 3
+ uri.select(:port, :path).should == [80, '']
+ end
+
+ it "raises an ArgumentError if a component is requested that isn't valid under the given scheme" do
+ [
+ lambda {URI("mailto:spam@mailinator.com").select(:path)},
+ lambda {URI("http://blog.blag.web").select(:typecode)},
+ ].each do |select_lambda|
+ select_lambda.should raise_error(ArgumentError)
+ end
+ end
+
+ it "raises an ArgumentError if given strings rather than symbols" do
+ lambda {
+ URI("http://host:8080/path/").select("scheme","host","port",'path')
+ }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/uri/set_component_spec.rb b/spec/ruby/library/uri/set_component_spec.rb
new file mode 100644
index 0000000000..71ed6af278
--- /dev/null
+++ b/spec/ruby/library/uri/set_component_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../../spec_helper'
+require 'uri'
+
+#TODO: make this more BDD
+describe "URI#select" do
+ it "conforms to the MatzRuby tests" do
+ uri = URI.parse('http://foo:bar@baz')
+ (uri.user = 'oof').should == 'oof'
+ uri.to_s.should == 'http://oof:bar@baz'
+ (uri.password = 'rab').should == 'rab'
+ uri.to_s.should == 'http://oof:rab@baz'
+ (uri.userinfo = 'foo').should == 'foo'
+ uri.to_s.should == 'http://foo:rab@baz'
+ (uri.userinfo = ['foo', 'bar']).should == ['foo', 'bar']
+ uri.to_s.should == 'http://foo:bar@baz'
+ (uri.userinfo = ['foo']).should == ['foo']
+ uri.to_s.should == 'http://foo:bar@baz'
+ (uri.host = 'zab').should == 'zab'
+ uri.to_s.should == 'http://foo:bar@zab'
+ (uri.port = 8080).should == 8080
+ uri.to_s.should == 'http://foo:bar@zab:8080'
+ (uri.path = '/').should == '/'
+ uri.to_s.should == 'http://foo:bar@zab:8080/'
+ (uri.query = 'a=1').should == 'a=1'
+ uri.to_s.should == 'http://foo:bar@zab:8080/?a=1'
+ (uri.fragment = 'b123').should == 'b123'
+ uri.to_s.should == 'http://foo:bar@zab:8080/?a=1#b123'
+
+ uri = URI.parse('http://example.com')
+ lambda { uri.password = 'bar' }.should raise_error(URI::InvalidURIError)
+ uri.userinfo = 'foo:bar'
+ uri.to_s.should == 'http://foo:bar@example.com'
+ lambda { uri.registry = 'bar' }.should raise_error(URI::InvalidURIError)
+ lambda { uri.opaque = 'bar' }.should raise_error(URI::InvalidURIError)
+
+ uri = URI.parse('mailto:foo@example.com')
+ lambda { uri.user = 'bar' }.should raise_error(URI::InvalidURIError)
+ lambda { uri.password = 'bar' }.should raise_error(URI::InvalidURIError)
+ lambda { uri.userinfo = ['bar', 'baz'] }.should raise_error(URI::InvalidURIError)
+ lambda { uri.host = 'bar' }.should raise_error(URI::InvalidURIError)
+ lambda { uri.port = 'bar' }.should raise_error(URI::InvalidURIError)
+ lambda { uri.path = 'bar' }.should raise_error(URI::InvalidURIError)
+ lambda { uri.query = 'bar' }.should raise_error(URI::InvalidURIError)
+ end
+end
diff --git a/spec/ruby/library/uri/shared/eql.rb b/spec/ruby/library/uri/shared/eql.rb
new file mode 100644
index 0000000000..2cc960d39a
--- /dev/null
+++ b/spec/ruby/library/uri/shared/eql.rb
@@ -0,0 +1,17 @@
+describe :uri_eql, shared: true do
+ it "returns false if the normalized forms are different" do
+ URISpec::NORMALIZED_FORMS.each do |form|
+ normal_uri = URI(form[:normalized])
+ form[:different].each do |other|
+ URI(other).send(@method, normal_uri).should be_false
+ end
+ end
+ end
+end
+
+describe :uri_eql_against_other_types, shared: true do
+ it "returns false for when compared to non-uri objects" do
+ URI("http://example.com/").send(@method, "http://example.com/").should be_false
+ URI("http://example.com/").send(@method, nil).should be_false
+ end
+end
diff --git a/spec/ruby/library/uri/shared/extract.rb b/spec/ruby/library/uri/shared/extract.rb
new file mode 100644
index 0000000000..efe60ae4b9
--- /dev/null
+++ b/spec/ruby/library/uri/shared/extract.rb
@@ -0,0 +1,83 @@
+describe :uri_extract, shared: true do
+ it "behaves according to its documentation" do
+ @object.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.").should == ["http://foo.example.org/bla", "mailto:test@example.com"]
+ end
+
+ it "treats contiguous URIs as a single URI" do
+ @object.extract('http://example.jphttp://example.jp').should == ['http://example.jphttp://example.jp']
+ end
+
+ it "treats pretty much anything with a colon as a URI" do
+ @object.extract('From: XXX [mailto:xxx@xxx.xxx.xxx]').should == ['From:', 'mailto:xxx@xxx.xxx.xxx]']
+ end
+
+ it "wraps a URI string in an array" do
+ @object.extract("http://github.com/brixen/rubyspec/tree/master").should == ["http://github.com/brixen/rubyspec/tree/master"]
+ end
+
+ it "pulls a variety of protocol URIs from a string" do
+ @object.extract("this is a string, it has http://rubini.us/ in it").should == ["http://rubini.us/"]
+ @object.extract("mailto:spambait@example.com").should == ["mailto:spambait@example.com"]
+ @object.extract("ftp://ruby-lang.org/").should == ["ftp://ruby-lang.org/"]
+ @object.extract("https://mail.google.com").should == ["https://mail.google.com"]
+ @object.extract("anything://example.com/").should == ["anything://example.com/"]
+ end
+
+ it "pulls all URIs within a string in order into an array when a block is not given" do
+ @object.extract("1.3. Example URI
+
+ The following examples illustrate URI that are in common use.
+
+ ftp://ftp.is.co.za/rfc/rfc1808.txt
+ -- ftp scheme for File Transfer Protocol services
+
+ gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles
+ -- gopher scheme for Gopher and Gopher+ Protocol services
+
+ http://www.math.uio.no/faq/compression-faq/part1.html
+ -- http scheme for Hypertext Transfer Protocol services
+
+ mailto:mduerst@ifi.unizh.ch
+ -- mailto scheme for electronic mail addresses
+
+ news:comp.infosystems.www.servers.unix
+ -- news scheme for USENET news groups and articles
+
+ telnet://melvyl.ucop.edu/
+ -- telnet scheme for interactive services via the TELNET Protocol
+ ").should == ["ftp://ftp.is.co.za/rfc/rfc1808.txt","gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles","http://www.math.uio.no/faq/compression-faq/part1.html","mailto:mduerst@ifi.unizh.ch","news:comp.infosystems.www.servers.unix","telnet://melvyl.ucop.edu/"]
+ end
+
+ it "yields each URI in the given string in order to a block, if given, and returns nil" do
+ results = ["http://foo.example.org/bla", "mailto:test@example.com"]
+ @object.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.") {|uri|
+ uri.should == results.shift
+ }.should == nil
+ results.should == []
+ end
+
+ it "allows the user to specify a list of acceptable protocols of URIs to scan for" do
+ @object.extract("1.3. Example URI
+
+ The following examples illustrate URI that are in common use.
+
+ ftp://ftp.is.co.za/rfc/rfc1808.txt
+ -- ftp scheme for File Transfer Protocol services
+
+ gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles
+ -- gopher scheme for Gopher and Gopher+ Protocol services
+
+ http://www.math.uio.no/faq/compression-faq/part1.html
+ -- http scheme for Hypertext Transfer Protocol services
+
+ mailto:mduerst@ifi.unizh.ch
+ -- mailto scheme for electronic mail addresses
+
+ news:comp.infosystems.www.servers.unix
+ -- news scheme for USENET news groups and articles
+
+ telnet://melvyl.ucop.edu/
+ -- telnet scheme for interactive services via the TELNET Protocol
+ ", ["http","ftp","mailto"]).should == ["ftp://ftp.is.co.za/rfc/rfc1808.txt","http://www.math.uio.no/faq/compression-faq/part1.html","mailto:mduerst@ifi.unizh.ch"]
+ end
+end
diff --git a/spec/ruby/library/uri/shared/join.rb b/spec/ruby/library/uri/shared/join.rb
new file mode 100644
index 0000000000..ff85b57a80
--- /dev/null
+++ b/spec/ruby/library/uri/shared/join.rb
@@ -0,0 +1,56 @@
+describe :uri_join, shared: true do
+ it "returns a URI object of the concatenation of a protocol and domain, and a path" do
+ @object.join("http://localhost/","main.rbx").should == URI.parse("http://localhost/main.rbx")
+ end
+
+ it "accepts URI objects" do
+ @object.join(URI("http://localhost/"),"main.rbx").should == URI.parse("http://localhost/main.rbx")
+ @object.join("http://localhost/",URI("main.rbx")).should == URI.parse("http://localhost/main.rbx")
+ @object.join(URI("http://localhost/"),URI("main.rbx")).should == URI.parse("http://localhost/main.rbx")
+ end
+
+ it "accepts string-like arguments with to_str" do
+ str = mock('string-like')
+ str.should_receive(:to_str).and_return("http://ruby-lang.org")
+ str2 = mock('string-like also')
+ str2.should_receive(:to_str).and_return("foo/bar")
+ @object.join(str, str2).should == URI.parse("http://ruby-lang.org/foo/bar")
+ end
+
+ it "raises an error if given no argument" do
+ lambda {
+ @object.join
+ }.should raise_error(ArgumentError)
+ end
+
+ it "doesn't create redundant '/'s" do
+ @object.join("http://localhost/", "/main.rbx").should == URI.parse("http://localhost/main.rbx")
+ end
+
+ it "discards arguments given before an absolute uri" do
+ @object.join("http://localhost/a/b/c/d", "http://ruby-lang.com/foo", "bar").should == URI.parse("http://ruby-lang.com/bar")
+ end
+
+ it "resolves .. in paths" do
+ @object.join("http://localhost/a/b/c/d", "../../e/f", "g/h/../i").to_s.should == "http://localhost/a/e/g/i"
+ end
+end
+
+
+# assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo/bar'))
+# assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo', 'bar'))
+# assert_equal(URI.parse('http://foo/bar/'), URI.join('http://foo', 'bar/'))
+#
+# assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', 'baz'))
+# assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', '/baz'))
+# assert_equal(URI.parse('http://foo/baz/'), URI.join('http://foo', 'bar', '/baz/'))
+# assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/', 'baz'))
+# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar', 'baz', 'hoge'))
+#
+# assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/baz'))
+# assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge'))
+# assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge'))
+# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge'))
+# assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge'))
+# assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge'))
+# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge'))
diff --git a/spec/ruby/library/uri/shared/parse.rb b/spec/ruby/library/uri/shared/parse.rb
new file mode 100644
index 0000000000..5ecbffcaf2
--- /dev/null
+++ b/spec/ruby/library/uri/shared/parse.rb
@@ -0,0 +1,199 @@
+describe :uri_parse, shared: true do
+ it "returns a URI::HTTP object when parsing an HTTP URI" do
+ @object.parse("http://www.example.com/").should be_kind_of(URI::HTTP)
+ end
+
+ it "populates the components of a parsed URI::HTTP, setting the port to 80 by default" do
+ # general case
+ URISpec.components(@object.parse("http://user:pass@example.com/path/?query=val&q2=val2#fragment")).should == {
+ scheme: "http",
+ userinfo: "user:pass",
+ host: "example.com",
+ port: 80,
+ path: "/path/",
+ query: "query=val&q2=val2",
+ fragment: "fragment"
+ }
+
+ # multiple paths
+ URISpec.components(@object.parse("http://a/b/c/d;p?q")).should == {
+ scheme: "http",
+ userinfo: nil,
+ host: "a",
+ port: 80,
+ path: "/b/c/d;p",
+ query: "q",
+ fragment: nil
+ }
+
+ # multi-level domain
+ URISpec.components(@object.parse('http://www.math.uio.no/faq/compression-faq/part1.html')).should == {
+ scheme: "http",
+ userinfo: nil,
+ host: "www.math.uio.no",
+ port: 80,
+ path: "/faq/compression-faq/part1.html",
+ query: nil,
+ fragment: nil
+ }
+ end
+
+ it "parses out the port number of a URI, when given" do
+ @object.parse("http://example.com:8080/").port.should == 8080
+ end
+
+ it "returns a URI::HTTPS object when parsing an HTTPS URI" do
+ @object.parse("https://important-intern-net.net").should be_kind_of(URI::HTTPS)
+ end
+
+ it "sets the port of a parsed https URI to 443 by default" do
+ @object.parse("https://example.com/").port.should == 443
+ end
+
+ it "populates the components of a parsed URI::FTP object" do
+ # generic, empty password.
+ url = @object.parse("ftp://anonymous@ruby-lang.org/pub/ruby/1.8/ruby-1.8.6.tar.bz2;type=i")
+ url.should be_kind_of(URI::FTP)
+ URISpec.components(url).should == {
+ scheme: "ftp",
+ userinfo: "anonymous",
+ host: "ruby-lang.org",
+ port: 21,
+ path: "pub/ruby/1.8/ruby-1.8.6.tar.bz2",
+ typecode: "i"
+ }
+
+ # multidomain, no user or password
+ url = @object.parse('ftp://ftp.is.co.za/rfc/rfc1808.txt')
+ url.should be_kind_of(URI::FTP)
+ URISpec.components(url).should == {
+ scheme: "ftp",
+ userinfo: nil,
+ host: "ftp.is.co.za",
+ port: 21,
+ path: "rfc/rfc1808.txt",
+ typecode: nil
+ }
+
+ # empty user
+ url = @object.parse('ftp://:pass@localhost/')
+ url.should be_kind_of(URI::FTP)
+ URISpec.components(url).should == {
+ scheme: "ftp",
+ userinfo: ":pass",
+ host: "localhost",
+ port: 21,
+ path: "",
+ typecode: nil
+ }
+ url.password.should == "pass"
+ end
+
+ it "returns a URI::LDAP object when parsing an LDAP URI" do
+ #taken from http://www.faqs.org/rfcs/rfc2255.html 'cause I don't really know what an LDAP url looks like
+ ldap_uris = %w{ ldap:///o=University%20of%20Michigan,c=US ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress ldap://host.com:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen) ldap://ldap.itd.umich.edu/c=GB?objectClass?one ldap://ldap.question.com/o=Question%3f,c=US?mail ldap://ldap.netscape.com/o=Babsco,c=US??(int=%5c00%5c00%5c00%5c04) ldap:///??sub??bindname=cn=Manager%2co=Foo ldap:///??sub??!bindname=cn=Manager%2co=Foo }
+ ldap_uris.each do |ldap_uri|
+ @object.parse(ldap_uri).should be_kind_of(URI::LDAP)
+ end
+ end
+
+ it "populates the components of a parsed URI::LDAP object" do
+ URISpec.components(@object.parse("ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress?scope?filter?extensions")).should == {
+ scheme: "ldap",
+ host: "ldap.itd.umich.edu",
+ port: 389,
+ dn: "o=University%20of%20Michigan,c=US",
+ attributes: "postalAddress",
+ scope: "scope",
+ filter: "filter",
+ extensions: "extensions"
+ }
+ end
+
+ it "returns a URI::MailTo object when passed a mailto URI" do
+ @object.parse("mailto:spam@mailinator.com").should be_kind_of(URI::MailTo)
+ end
+
+ it "populates the components of a parsed URI::MailTo object" do
+ URISpec.components(@object.parse("mailto:spam@mailinator.com?subject=Discounts%20On%20Imported%20methods!!!&body=Exciting%20offer")).should == {
+ scheme: "mailto",
+ to: "spam@mailinator.com",
+ headers: [["subject","Discounts%20On%20Imported%20methods!!!"],
+ ["body", "Exciting%20offer"]]
+ }
+ end
+
+ # TODO
+ # Test registry
+ it "does its best to extract components from URI::Generic objects" do
+ # generic
+ URISpec.components(URI("scheme://userinfo@host/path?query#fragment")).should == {
+ scheme: "scheme",
+ userinfo: "userinfo",
+ host: "host",
+ port: nil,
+ path: "/path",
+ query: "query",
+ fragment: "fragment",
+ registry: nil,
+ opaque: nil
+ }
+
+ # gopher
+ gopher = @object.parse('gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles')
+ gopher.should be_kind_of(URI::Generic)
+
+ URISpec.components(gopher).should == {
+ scheme: "gopher",
+ userinfo: nil,
+ host: "spinaltap.micro.umn.edu",
+ port: nil,
+ path: "/00/Weather/California/Los%20Angeles",
+ query: nil,
+ fragment: nil,
+ registry: nil,
+ opaque: nil
+ }
+
+ # news
+ news = @object.parse('news:comp.infosystems.www.servers.unix')
+ news.should be_kind_of(URI::Generic)
+ URISpec.components(news).should == {
+ scheme: "news",
+ userinfo: nil,
+ host: nil,
+ port: nil,
+ path: nil,
+ query: nil,
+ fragment: nil,
+ registry: nil,
+ opaque: "comp.infosystems.www.servers.unix"
+ }
+
+ # telnet
+ telnet = @object.parse('telnet://melvyl.ucop.edu/')
+ telnet.should be_kind_of(URI::Generic)
+ URISpec.components(telnet).should == {
+ scheme: "telnet",
+ userinfo: nil,
+ host: "melvyl.ucop.edu",
+ port: nil,
+ path: "/",
+ query: nil,
+ fragment: nil,
+ registry: nil,
+ opaque: nil
+ }
+
+ # files
+ file_l = @object.parse('file:///foo/bar.txt')
+ file_l.should be_kind_of(URI::Generic)
+ file = @object.parse('file:/foo/bar.txt')
+ file.should be_kind_of(URI::Generic)
+ end
+
+ it "raises errors on malformed URIs" do
+ lambda { @object.parse('http://a_b:80/') }.should raise_error(URI::InvalidURIError)
+ lambda { @object.parse('http://a_b/') }.should raise_error(URI::InvalidURIError)
+ end
+end
diff --git a/spec/ruby/library/uri/split_spec.rb b/spec/ruby/library/uri/split_spec.rb
new file mode 100644
index 0000000000..9ad37e3b1f
--- /dev/null
+++ b/spec/ruby/library/uri/split_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require 'uri'
+
+describe "URI.split" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/uri/uri_spec.rb b/spec/ruby/library/uri/uri_spec.rb
new file mode 100644
index 0000000000..93ba07a207
--- /dev/null
+++ b/spec/ruby/library/uri/uri_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+require 'uri'
+
+#the testing is light here as this is an alias for URI.parse
+
+#we're just testing that the method ends up in the right place
+describe "the URI method" do
+ it "parses a given URI, returning a URI object" do
+ result = URI.parse("http://ruby-lang.org")
+ URI("http://ruby-lang.org").should == result
+ Kernel::URI("http://ruby-lang.org").should == result
+ end
+
+ it "converts its argument with to_str" do
+ str = mock('string-like')
+ str.should_receive(:to_str).and_return("http://ruby-lang.org")
+ URI(str).should == URI.parse("http://ruby-lang.org")
+ end
+
+ it "returns the argument if it is a URI object" do
+ result = URI.parse("http://ruby-lang.org")
+ URI(result).should equal(result)
+ end
+
+ #apparently this was a concern? imported from MRI tests
+ it "does not add a URI method to Object instances" do
+ lambda {Object.new.URI("http://ruby-lang.org/")}.should raise_error(NoMethodError)
+ end
+end
diff --git a/spec/ruby/library/uri/util/make_components_hash_spec.rb b/spec/ruby/library/uri/util/make_components_hash_spec.rb
new file mode 100644
index 0000000000..6d26b81130
--- /dev/null
+++ b/spec/ruby/library/uri/util/make_components_hash_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../../spec_helper'
+require 'uri'
+
+describe "URI::Util.make_components_hash" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/weakref/__getobj___spec.rb b/spec/ruby/library/weakref/__getobj___spec.rb
new file mode 100644
index 0000000000..4ed0bc6afb
--- /dev/null
+++ b/spec/ruby/library/weakref/__getobj___spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "WeakRef#__getobj__" do
+ it "returns the object if it is reachable" do
+ obj = Object.new
+ ref = WeakRef.new(obj)
+ ref.__getobj__.should equal(obj)
+ end
+
+ it "raises WeakRef::RefError if the object is no longer reachable" do
+ ref = WeakRefSpec.make_dead_weakref
+ lambda {
+ ref.__getobj__
+ }.should raise_error(WeakRef::RefError)
+ end
+end
diff --git a/spec/ruby/library/weakref/allocate_spec.rb b/spec/ruby/library/weakref/allocate_spec.rb
new file mode 100644
index 0000000000..76e81c4e5e
--- /dev/null
+++ b/spec/ruby/library/weakref/allocate_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'weakref'
+
+describe "WeakRef#allocate" do
+ it "assigns nil as the reference" do
+ lambda { WeakRef.allocate.__getobj__ }.should raise_error(WeakRef::RefError)
+ end
+end
diff --git a/spec/ruby/library/weakref/fixtures/classes.rb b/spec/ruby/library/weakref/fixtures/classes.rb
new file mode 100644
index 0000000000..560c58b041
--- /dev/null
+++ b/spec/ruby/library/weakref/fixtures/classes.rb
@@ -0,0 +1,24 @@
+require 'weakref'
+
+# From MRI test_weakref.rb
+class WeakRefSpec
+ def self.make_weakref(level = 10)
+ if level > 0
+ make_weakref(level - 1)
+ else
+ WeakRef.new(Object.new)
+ end
+ end
+
+ def self.make_dead_weakref
+ weaks = []
+ weak = nil
+ 10_000.times do
+ weaks << make_weakref
+ GC.start
+ GC.start
+ break if weak = weaks.find { |w| !w.weakref_alive? }
+ end
+ weak
+ end
+end
diff --git a/spec/ruby/library/weakref/new_spec.rb b/spec/ruby/library/weakref/new_spec.rb
new file mode 100644
index 0000000000..6290e61fe3
--- /dev/null
+++ b/spec/ruby/library/weakref/new_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require 'weakref'
+
+describe "WeakRef#new" do
+ it "creates a subclass correctly" do
+ wr2 = Class.new(WeakRef) {
+ def __getobj__
+ :dummy
+ end
+ }
+ wr2.new(Object.new).__getobj__.should == :dummy
+ end
+end
diff --git a/spec/ruby/library/weakref/send_spec.rb b/spec/ruby/library/weakref/send_spec.rb
new file mode 100644
index 0000000000..079958ac88
--- /dev/null
+++ b/spec/ruby/library/weakref/send_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require 'weakref'
+
+describe "WeakRef#__send__" do
+ module WeakRefSpecs
+ class << self
+ def delegated_method
+ :result
+ end
+
+ def protected_method
+ :result
+ end
+ protected :protected_method
+
+ def private_method
+ :result
+ end
+ private :private_method
+ end
+ end
+
+ it "delegates to public methods of the weakly-referenced object" do
+ wr = WeakRef.new(WeakRefSpecs)
+ wr.delegated_method.should == :result
+ end
+
+ it "delegates to protected methods of the weakly-referenced object" do
+ wr = WeakRef.new(WeakRefSpecs)
+ lambda { wr.protected_method }.should raise_error(NameError)
+ end
+
+ it "does not delegate to private methods of the weakly-referenced object" do
+ wr = WeakRef.new(WeakRefSpecs)
+ lambda { wr.private_method }.should raise_error(NameError)
+ end
+end
diff --git a/spec/ruby/library/weakref/weakref_alive_spec.rb b/spec/ruby/library/weakref/weakref_alive_spec.rb
new file mode 100644
index 0000000000..5bd7c3166b
--- /dev/null
+++ b/spec/ruby/library/weakref/weakref_alive_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "WeakRef#weakref_alive?" do
+ it "returns true if the object is reachable" do
+ obj = Object.new
+ ref = WeakRef.new(obj)
+ ref.weakref_alive?.should == true
+ end
+
+ it "returns a falsey value if the object is no longer reachable" do
+ ref = WeakRefSpec.make_dead_weakref
+ [false, nil].should include(ref.weakref_alive?)
+ end
+end
diff --git a/spec/ruby/library/win32ole/fixtures/classes.rb b/spec/ruby/library/win32ole/fixtures/classes.rb
new file mode 100644
index 0000000000..f61cf6ba69
--- /dev/null
+++ b/spec/ruby/library/win32ole/fixtures/classes.rb
@@ -0,0 +1,22 @@
+require 'win32ole'
+
+module WIN32OLESpecs
+ MSXML_AVAILABLE = WIN32OLE_TYPELIB.typelibs.any? { |t| t.name.start_with?('Microsoft XML') }
+ SYSTEM_MONITOR_CONTROL_AVAILABLE = WIN32OLE_TYPELIB.typelibs.any? { |t| t.name.start_with?('System Monitor Control') }
+
+ def self.new_ole(name)
+ tries = 0
+ begin
+ WIN32OLE.new(name)
+ rescue WIN32OLERuntimeError => e
+ if tries < 3
+ tries += 1
+ $stderr.puts "WIN32OLESpecs#new_ole retry (#{tries}): #{e.class}: #{e.message}"
+ sleep(2 ** tries)
+ retry
+ else
+ raise
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/win32ole/fixtures/event.xml b/spec/ruby/library/win32ole/fixtures/event.xml
new file mode 100644
index 0000000000..23f3d2b126
--- /dev/null
+++ b/spec/ruby/library/win32ole/fixtures/event.xml
@@ -0,0 +1,4 @@
+<program>
+ <name>Ruby</name>
+ <version>trunk</version>
+</program>
diff --git a/spec/ruby/library/win32ole/win32ole/_getproperty_spec.rb b/spec/ruby/library/win32ole/win32ole/_getproperty_spec.rb
new file mode 100644
index 0000000000..940eebfb91
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/_getproperty_spec.rb
@@ -0,0 +1,14 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ describe "WIN32OLE#_getproperty" do
+ before :each do
+ @dict = WIN32OLESpecs.new_ole('Scripting.Dictionary')
+ end
+
+ it "gets value" do
+ @dict.add('key', 'value')
+ @dict._getproperty(0, ['key'], [WIN32OLE::VARIANT::VT_BSTR]).should == 'value'
+ end
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole/_invoke_spec.rb b/spec/ruby/library/win32ole/win32ole/_invoke_spec.rb
new file mode 100644
index 0000000000..9809f89e7c
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/_invoke_spec.rb
@@ -0,0 +1,21 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ describe "WIN32OLE#_invoke" do
+ before :each do
+ @shell = WIN32OLESpecs.new_ole 'Shell.application'
+ end
+
+ it "raises ArgumentError if insufficient number of arguments are given" do
+ lambda { @shell._invoke() }.should raise_error ArgumentError
+ lambda { @shell._invoke(0) }.should raise_error ArgumentError
+ lambda { @shell._invoke(0, []) }.should raise_error ArgumentError
+ end
+
+ it "dispatches the method bound to a specific ID" do
+ @shell._invoke(0x60020002, [37], [WIN32OLE::VARIANT::VT_VARIANT]).title.should =~ /System32/i
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole/codepage_spec.rb b/spec/ruby/library/win32ole/win32ole/codepage_spec.rb
new file mode 100644
index 0000000000..4e0cf5ca55
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/codepage_spec.rb
@@ -0,0 +1,13 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ describe "WIN32OLE.codepage=" do
+ it "sets codepage" do
+ cp = WIN32OLE.codepage
+ WIN32OLE.codepage = WIN32OLE::CP_UTF8
+ WIN32OLE.codepage.should == WIN32OLE::CP_UTF8
+ WIN32OLE.codepage = cp
+ end
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole/connect_spec.rb b/spec/ruby/library/win32ole/win32ole/connect_spec.rb
new file mode 100644
index 0000000000..590ef7688c
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/connect_spec.rb
@@ -0,0 +1,15 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ describe "WIN32OLE.connect" do
+ it "creates WIN32OLE object given valid argument" do
+ obj = WIN32OLE.connect("winmgmts:")
+ obj.should be_kind_of WIN32OLE
+ end
+
+ it "raises TypeError when given invalid argument" do
+ lambda { WIN32OLE.connect 1 }.should raise_error TypeError
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole/const_load_spec.rb b/spec/ruby/library/win32ole/win32ole/const_load_spec.rb
new file mode 100644
index 0000000000..cacc7a2b22
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/const_load_spec.rb
@@ -0,0 +1,32 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ describe "WIN32OLE.const_load when passed Shell.Application OLE object" do
+ before :each do
+ @win32ole = WIN32OLESpecs.new_ole 'Shell.Application'
+ end
+
+ it "loads constant SsfWINDOWS into WIN32OLE namespace" do
+ WIN32OLE.const_defined?(:SsfWINDOWS).should be_false
+ WIN32OLE.const_load @win32ole
+ WIN32OLE.const_defined?(:SsfWINDOWS).should be_true
+ end
+ end
+
+ describe "WIN32OLE.const_load when namespace is specified" do
+ before :each do
+ module WIN32OLE_RUBYSPEC; end
+ @win32ole = WIN32OLESpecs.new_ole 'Shell.Application'
+ end
+
+ it "loads constants into given namespace" do
+ module WIN32OLE_RUBYSPEC; end
+
+ WIN32OLE_RUBYSPEC.const_defined?(:SsfWINDOWS).should be_false
+ WIN32OLE.const_load @win32ole, WIN32OLE_RUBYSPEC
+ WIN32OLE_RUBYSPEC.const_defined?(:SsfWINDOWS).should be_true
+
+ end
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole/constants_spec.rb b/spec/ruby/library/win32ole/win32ole/constants_spec.rb
new file mode 100644
index 0000000000..978b7ade92
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/constants_spec.rb
@@ -0,0 +1,42 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ describe "WIN32OLE class" do
+ it "defines constant CP_ACP" do
+ WIN32OLE::CP_ACP.should == 0
+ end
+
+ it "defines constant CP_OEMCP" do
+ WIN32OLE::CP_OEMCP.should == 1
+ end
+
+ it "defines constant CP_MACCP" do
+ WIN32OLE::CP_MACCP.should == 2
+ end
+
+ it "defines constant CP_THREAD_ACP" do
+ WIN32OLE::CP_THREAD_ACP.should == 3
+ end
+
+ it "defines constant CP_SYMBOL" do
+ WIN32OLE::CP_SYMBOL.should == 42
+ end
+
+ it "defines constant CP_UTF7" do
+ WIN32OLE::CP_UTF7.should == 65000
+ end
+
+ it "defines constant CP_UTF8" do
+ WIN32OLE::CP_UTF8.should == 65001
+ end
+
+ it "defines constant LOCALE_SYSTEM_DEFAULT" do
+ WIN32OLE::LOCALE_SYSTEM_DEFAULT.should == 0x0800
+ end
+
+ it "defines constant LOCALE_USER_DEFAULT" do
+ WIN32OLE::LOCALE_USER_DEFAULT.should == 0x0400
+ end
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole/create_guid_spec.rb b/spec/ruby/library/win32ole/win32ole/create_guid_spec.rb
new file mode 100644
index 0000000000..2e18b6ab11
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/create_guid_spec.rb
@@ -0,0 +1,9 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ describe "WIN32OLE.create_guid" do
+ it "generates guid with valid format" do
+ WIN32OLE.create_guid.should =~ /^\{[A-Z0-9]{8}\-[A-Z0-9]{4}\-[A-Z0-9]{4}\-[A-Z0-9]{4}\-[A-Z0-9]{12}/
+ end
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole/invoke_spec.rb b/spec/ruby/library/win32ole/win32ole/invoke_spec.rb
new file mode 100644
index 0000000000..08a5156e05
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/invoke_spec.rb
@@ -0,0 +1,14 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ describe "WIN32OLE#invoke" do
+ before :each do
+ @dict = WIN32OLESpecs.new_ole('Scripting.Dictionary')
+ end
+
+ it "get value by invoking 'Item' OLE method" do
+ @dict.add('key', 'value')
+ @dict.invoke('Item', 'key').should == 'value'
+ end
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole/locale_spec.rb b/spec/ruby/library/win32ole/win32ole/locale_spec.rb
new file mode 100644
index 0000000000..a0376ce123
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/locale_spec.rb
@@ -0,0 +1,29 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ describe "WIN32OLE.locale" do
+ it "gets locale" do
+ WIN32OLE.locale.should == WIN32OLE::LOCALE_SYSTEM_DEFAULT
+ end
+ end
+
+ describe "WIN32OLE.locale=" do
+ it "sets locale to Japanese, if available" do
+ begin
+ begin
+ WIN32OLE.locale = 1041
+ rescue WIN32OLERuntimeError
+ STDERR.puts("\n#{__FILE__}:#{__LINE__}:#{self.class.name}.test_s_locale_set is skipped(Japanese locale is not installed)")
+ return
+ end
+
+ WIN32OLE.locale.should == 1041
+ WIN32OLE.locale = WIN32OLE::LOCALE_SYSTEM_DEFAULT
+ lambda { WIN32OLE.locale = 111 }.should raise_error WIN32OLERuntimeError
+ WIN32OLE.locale.should == WIN32OLE::LOCALE_SYSTEM_DEFAULT
+ ensure
+ WIN32OLE.locale.should == WIN32OLE::LOCALE_SYSTEM_DEFAULT
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole/new_spec.rb b/spec/ruby/library/win32ole/win32ole/new_spec.rb
new file mode 100644
index 0000000000..d1c3e02593
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/new_spec.rb
@@ -0,0 +1,25 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ describe "WIN32OLESpecs.new_ole" do
+ it "creates a WIN32OLE object from OLE server name" do
+ shell = WIN32OLESpecs.new_ole 'Shell.Application'
+ shell.should be_kind_of WIN32OLE
+ end
+
+ it "creates a WIN32OLE object from valid CLSID" do
+ shell = WIN32OLESpecs.new_ole("{13709620-C279-11CE-A49E-444553540000}")
+ shell.should be_kind_of WIN32OLE
+ end
+
+ it "raises TypeError if argument cannot be converted to String" do
+ lambda { WIN32OLESpecs.new_ole(42) }.should raise_error( TypeError )
+ end
+
+ it "raises WIN32OLERuntimeError if invalid string is given" do
+ lambda { WIN32OLE.new('foo') }.should raise_error( WIN32OLERuntimeError )
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole/ole_func_methods_spec.rb b/spec/ruby/library/win32ole/win32ole/ole_func_methods_spec.rb
new file mode 100644
index 0000000000..5488c4fd29
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/ole_func_methods_spec.rb
@@ -0,0 +1,21 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ describe "WIN32OLE#ole_func_methods" do
+ before :each do
+ @dict = WIN32OLESpecs.new_ole('Scripting.Dictionary')
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @dict.ole_func_methods(1) }.should raise_error ArgumentError
+ end
+
+ it "returns an array of WIN32OLE_METHODs" do
+ @dict.ole_func_methods.all? { |m| m.kind_of? WIN32OLE_METHOD }.should be_true
+ end
+
+ it "contains a 'AddRef' method for Scripting Dictionary" do
+ @dict.ole_func_methods.map { |m| m.name }.include?('AddRef').should be_true
+ end
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole/ole_get_methods_spec.rb b/spec/ruby/library/win32ole/win32ole/ole_get_methods_spec.rb
new file mode 100644
index 0000000000..a991624a23
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/ole_get_methods_spec.rb
@@ -0,0 +1,16 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ describe "WIN32OLE#ole_get_methods" do
+
+ before :each do
+ @win32ole = WIN32OLESpecs.new_ole('Shell.Application')
+ end
+
+ it "returns an array of WIN32OLE_METHOD objects" do
+ @win32ole.ole_get_methods.all? {|m| m.kind_of? WIN32OLE_METHOD}.should be_true
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole/ole_method_help_spec.rb b/spec/ruby/library/win32ole/win32ole/ole_method_help_spec.rb
new file mode 100644
index 0000000000..8a26d79a20
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/ole_method_help_spec.rb
@@ -0,0 +1,10 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+ require_relative 'shared/ole_method'
+
+ describe "WIN32OLE#ole_method_help" do
+ it_behaves_like :win32ole_ole_method, :ole_method_help
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole/ole_method_spec.rb b/spec/ruby/library/win32ole/win32ole/ole_method_spec.rb
new file mode 100644
index 0000000000..f82a212f5d
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/ole_method_spec.rb
@@ -0,0 +1,10 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+ require_relative 'shared/ole_method'
+
+ describe "WIN32OLE#ole_method" do
+ it_behaves_like :win32ole_ole_method, :ole_method
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole/ole_methods_spec.rb b/spec/ruby/library/win32ole/win32ole/ole_methods_spec.rb
new file mode 100644
index 0000000000..b2baac265e
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/ole_methods_spec.rb
@@ -0,0 +1,21 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ describe "WIN32OLE#ole_methods" do
+ before :each do
+ @dict = WIN32OLESpecs.new_ole('Scripting.Dictionary')
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @dict.ole_methods(1) }.should raise_error ArgumentError
+ end
+
+ it "returns an array of WIN32OLE_METHODs" do
+ @dict.ole_methods.all? { |m| m.kind_of? WIN32OLE_METHOD }.should be_true
+ end
+
+ it "contains a 'AddRef' method for Scripting Dictionary" do
+ @dict.ole_methods.map { |m| m.name }.include?('AddRef').should be_true
+ end
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole/ole_obj_help_spec.rb b/spec/ruby/library/win32ole/win32ole/ole_obj_help_spec.rb
new file mode 100644
index 0000000000..2d31aac9eb
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/ole_obj_help_spec.rb
@@ -0,0 +1,18 @@
+
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ describe "WIN32OLE#ole_obj_help" do
+ before :each do
+ @dict = WIN32OLESpecs.new_ole('Scripting.Dictionary')
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @dict.ole_obj_help(1) }.should raise_error ArgumentError
+ end
+
+ it "returns an instance of WIN32OLE_TYPE" do
+ @dict.ole_obj_help.kind_of?(WIN32OLE_TYPE).should be_true
+ end
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole/ole_put_methods_spec.rb b/spec/ruby/library/win32ole/win32ole/ole_put_methods_spec.rb
new file mode 100644
index 0000000000..45af41c6c2
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/ole_put_methods_spec.rb
@@ -0,0 +1,21 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ describe "WIN32OLE#ole_put_methods" do
+ before :each do
+ @dict = WIN32OLESpecs.new_ole('Scripting.Dictionary')
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @dict.ole_put_methods(1) }.should raise_error ArgumentError
+ end
+
+ it "returns an array of WIN32OLE_METHODs" do
+ @dict.ole_put_methods.all? { |m| m.kind_of? WIN32OLE_METHOD }.should be_true
+ end
+
+ it "contains a 'Key' method for Scripting Dictionary" do
+ @dict.ole_put_methods.map { |m| m.name }.include?('Key').should be_true
+ end
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole/setproperty_spec.rb b/spec/ruby/library/win32ole/win32ole/setproperty_spec.rb
new file mode 100644
index 0000000000..7409823f20
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/setproperty_spec.rb
@@ -0,0 +1,10 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+ require_relative 'shared/setproperty'
+
+ describe "WIN32OLE#setproperty" do
+ it_behaves_like :win32ole_setproperty, :setproperty
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole/shared/ole_method.rb b/spec/ruby/library/win32ole/win32ole/shared/ole_method.rb
new file mode 100644
index 0000000000..26566a0b14
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/shared/ole_method.rb
@@ -0,0 +1,19 @@
+platform_is :windows do
+ require_relative '../../fixtures/classes'
+
+ describe :win32ole_ole_method, shared: true do
+ before :each do
+ @dict = WIN32OLESpecs.new_ole('Scripting.Dictionary')
+ end
+
+ it "raises ArgumentError if no argument is given" do
+ lambda { @dict.send(@method) }.should raise_error ArgumentError
+ end
+
+ it "returns the WIN32OLE_METHOD 'Add' if given 'Add'" do
+ result = @dict.send(@method, "Add")
+ result.kind_of?(WIN32OLE_METHOD).should be_true
+ result.name.should == 'Add'
+ end
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole/shared/setproperty.rb b/spec/ruby/library/win32ole/win32ole/shared/setproperty.rb
new file mode 100644
index 0000000000..b850d21933
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole/shared/setproperty.rb
@@ -0,0 +1,23 @@
+platform_is :windows do
+ require_relative '../../fixtures/classes'
+
+ describe :win32ole_setproperty, shared: true do
+ before :each do
+ @dict = WIN32OLESpecs.new_ole('Scripting.Dictionary')
+ end
+
+ it "raises ArgumentError if no argument is given" do
+ lambda { @dict.send(@method) }.should raise_error ArgumentError
+ end
+
+ it "sets key to newkey and returns nil" do
+ oldkey = 'oldkey'
+ newkey = 'newkey'
+ @dict.add(oldkey, 'value')
+ result = @dict.send(@method, 'Key', oldkey, newkey)
+ result.should == nil
+ @dict[oldkey].should == nil
+ @dict[newkey].should == 'value'
+ end
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_event/new_spec.rb b/spec/ruby/library/win32ole/win32ole_event/new_spec.rb
new file mode 100644
index 0000000000..4ce11a716b
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_event/new_spec.rb
@@ -0,0 +1,33 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+
+ guard -> { WIN32OLESpecs::MSXML_AVAILABLE } do
+ describe "WIN32OLE_EVENT.new" do
+ before :all do
+ @xml_dom = WIN32OLESpecs.new_ole('MSXML.DOMDocument')
+ end
+
+ after :all do
+ @xml_dom = nil
+ end
+
+ it "raises TypeError given invalid argument" do
+ lambda { WIN32OLE_EVENT.new "A" }.should raise_error TypeError
+ end
+
+ it "raises RuntimeError if event does not exist" do
+ lambda { WIN32OLE_EVENT.new(@xml_dom, 'A') }.should raise_error RuntimeError
+ end
+
+ it "raises RuntimeError if OLE object has no events" do
+ dict = WIN32OLESpecs.new_ole('Scripting.Dictionary')
+ lambda { WIN32OLE_EVENT.new(dict) }.should raise_error RuntimeError
+ end
+
+ it "creates WIN32OLE_EVENT object" do
+ ev = WIN32OLE_EVENT.new(@xml_dom)
+ ev.should be_kind_of WIN32OLE_EVENT
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_event/on_event_spec.rb b/spec/ruby/library/win32ole/win32ole_event/on_event_spec.rb
new file mode 100644
index 0000000000..feb26b0637
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_event/on_event_spec.rb
@@ -0,0 +1,70 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+ guard -> { WIN32OLESpecs::MSXML_AVAILABLE } do
+
+ def handler_global(event, *args)
+ @event_global += event
+ end
+
+ def handler_specific(*args)
+ @event_specific = "specific"
+ end
+
+ def handler_spec_alt(*args)
+ @event_spec_alt = "spec_alt"
+ end
+
+ describe "WIN32OLE_EVENT#on_event" do
+ before :all do
+ @fn_xml = File.absolute_path "../fixtures/event.xml", __dir__
+ end
+
+ before :each do
+ @xml_dom = WIN32OLESpecs.new_ole 'MSXML.DOMDocument'
+ @xml_dom.async = true
+ @ev = WIN32OLE_EVENT.new @xml_dom
+ @event_global = ''
+ @event_specific = ''
+ @event_spec_alt = ''
+ end
+
+ after :each do
+ @xml_dom = nil
+ @ev = nil
+ end
+
+ it "sets global event handler properly, and the handler is invoked by event loop" do
+ @ev.on_event { |*args| handler_global(*args) }
+ @xml_dom.loadXML "<program><name>Ruby</name><version>trunk</version></program>"
+ WIN32OLE_EVENT.message_loop
+ @event_global.should =~ /onreadystatechange/
+ end
+
+ it "accepts a String argument and the handler is invoked by event loop" do
+ @ev.on_event("onreadystatechange") { |*args| @event = 'foo' }
+ @xml_dom.loadXML "<program><name>Ruby</name><version>trunk</version></program>"
+ WIN32OLE_EVENT.message_loop
+ @event.should =~ /foo/
+ end
+
+ it "accepts a Symbol argument and the handler is invoked by event loop" do
+ @ev.on_event(:onreadystatechange) { |*args| @event = 'bar' }
+ @xml_dom.loadXML "<program><name>Ruby</name><version>trunk</version></program>"
+ WIN32OLE_EVENT.message_loop
+ @event.should =~ /bar/
+ end
+
+ it "accepts a specific event handler and overrides a global event handler" do
+ @ev.on_event { |*args| handler_global(*args) }
+ @ev.on_event("onreadystatechange") { |*args| handler_specific(*args) }
+ @ev.on_event("onreadystatechange") { |*args| handler_spec_alt(*args) }
+ @xml_dom.load @fn_xml
+ WIN32OLE_EVENT.message_loop
+ @event_global.should == 'ondataavailable'
+ @event_global.should_not =~ /onreadystatechange/
+ @event_specific.should == ''
+ @event_spec_alt.should == "spec_alt"
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/dispid_spec.rb b/spec/ruby/library/win32ole/win32ole_method/dispid_spec.rb
new file mode 100644
index 0000000000..840cdf72b8
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/dispid_spec.rb
@@ -0,0 +1,20 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#dispid" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ @m = WIN32OLE_METHOD.new(ole_type, "namespace")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @m.dispid(0) }.should raise_error ArgumentError
+ end
+
+ it "returns expected dispatch ID for Shell's 'namespace' method" do
+ @m.dispid.should == 1610743810 # value found in MRI's test
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/event_interface_spec.rb b/spec/ruby/library/win32ole/win32ole_method/event_interface_spec.rb
new file mode 100644
index 0000000000..1b88d80ff9
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/event_interface_spec.rb
@@ -0,0 +1,28 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+ guard -> { WIN32OLESpecs::SYSTEM_MONITOR_CONTROL_AVAILABLE } do
+
+ describe "WIN32OLE_METHOD#event_interface" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("System Monitor Control", "SystemMonitor")
+ @on_dbl_click_method = WIN32OLE_METHOD.new(ole_type, "OnDblClick")
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ @namespace_method = WIN32OLE_METHOD.new(ole_type, "namespace")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @on_dbl_click_method.event_interface(1) }.should raise_error ArgumentError
+ end
+
+ it "returns expected string for System Monitor Control's 'OnDblClick' method" do
+ @on_dbl_click_method.event_interface.should == "DISystemMonitorEvents"
+ end
+
+ it "returns nil if method has no event interface" do
+ @namespace_method.event_interface.should be_nil
+ end
+
+ end
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/event_spec.rb b/spec/ruby/library/win32ole/win32ole_method/event_spec.rb
new file mode 100644
index 0000000000..81628805d0
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/event_spec.rb
@@ -0,0 +1,22 @@
+platform_is :windows do
+ require_relative '../fixtures/classes'
+ guard -> { WIN32OLESpecs::SYSTEM_MONITOR_CONTROL_AVAILABLE } do
+
+ describe "WIN32OLE_METHOD#event?" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("System Monitor Control", "SystemMonitor")
+ @on_dbl_click_method = WIN32OLE_METHOD.new(ole_type, "OnDblClick")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @on_dbl_click_method.event?(1) }.should raise_error ArgumentError
+ end
+
+ it "returns true for System Monitor Control's 'OnDblClick' method" do
+ @on_dbl_click_method.event?.should be_true
+ end
+
+ end
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/helpcontext_spec.rb b/spec/ruby/library/win32ole/win32ole_method/helpcontext_spec.rb
new file mode 100644
index 0000000000..a0008fa7d5
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/helpcontext_spec.rb
@@ -0,0 +1,26 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#helpcontext" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject")
+ @get_file_version = WIN32OLE_METHOD.new(ole_type, "GetFileVersion")
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File")
+ @m_file_name = WIN32OLE_METHOD.new(ole_type, "name")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @get_file_version.helpcontext(1) }.should raise_error ArgumentError
+ end
+
+ it "returns expected value for FileSystemObject's 'GetFileVersion' method" do
+ @get_file_version.helpcontext.should == 0
+ end
+
+ it "returns expected value for Scripting Runtime's 'name' method" do
+ @m_file_name.helpcontext.should == 2181996 # value indicated in MRI's test
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/helpfile_spec.rb b/spec/ruby/library/win32ole/win32ole_method/helpfile_spec.rb
new file mode 100644
index 0000000000..72cc4da16b
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/helpfile_spec.rb
@@ -0,0 +1,20 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#helpfile" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File")
+ @m_file_name = WIN32OLE_METHOD.new(ole_type, "name")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @m_file_name.helpfile(1) }.should raise_error ArgumentError
+ end
+
+ it "returns expected value for Scripting Runtime's 'File' method" do
+ @m_file_name.helpfile.should =~ /VBENLR.*\.CHM$/i
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/helpstring_spec.rb b/spec/ruby/library/win32ole/win32ole_method/helpstring_spec.rb
new file mode 100644
index 0000000000..60105d0aa2
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/helpstring_spec.rb
@@ -0,0 +1,20 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#helpstring" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File")
+ @m_file_name = WIN32OLE_METHOD.new(ole_type, "name")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @m_file_name.helpstring(1) }.should raise_error ArgumentError
+ end
+
+ it "returns expected value for Scripting Runtime's 'File' method" do
+ @m_file_name.helpstring.should == "Get name of file" # value indicated in MRI's test
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/invkind_spec.rb b/spec/ruby/library/win32ole/win32ole_method/invkind_spec.rb
new file mode 100644
index 0000000000..cf0a74bbce
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/invkind_spec.rb
@@ -0,0 +1,20 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#invkind" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File")
+ @m_file_name = WIN32OLE_METHOD.new(ole_type, "name")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @m_file_name.invkind(1) }.should raise_error ArgumentError
+ end
+
+ it "returns expected value for Scripting Runtime's 'name' method" do
+ @m_file_name.invkind.should == 2
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/invoke_kind_spec.rb b/spec/ruby/library/win32ole/win32ole_method/invoke_kind_spec.rb
new file mode 100644
index 0000000000..4d2af8fb0c
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/invoke_kind_spec.rb
@@ -0,0 +1,20 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#invoke_kind" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File")
+ @m_file_name = WIN32OLE_METHOD.new(ole_type, "name")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @m_file_name.invoke_kind(1) }.should raise_error ArgumentError
+ end
+
+ it "returns expected value for Scripting Runtime's 'name' method" do
+ @m_file_name.invoke_kind.should =~ /^(UNKNOWN|PROPERTY|PROPERTYGET|PROPERTYPUT|PROPERTYPUTREF|FUNC)$/
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/name_spec.rb b/spec/ruby/library/win32ole/win32ole_method/name_spec.rb
new file mode 100644
index 0000000000..cd5404fc54
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/name_spec.rb
@@ -0,0 +1,11 @@
+require_relative 'shared/name'
+
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#name" do
+ it_behaves_like :win32ole_method_name, :name
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/new_spec.rb b/spec/ruby/library/win32ole/win32ole_method/new_spec.rb
new file mode 100644
index 0000000000..f904107c6c
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/new_spec.rb
@@ -0,0 +1,33 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD.new" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ it "raises TypeError when given non-strings" do
+ lambda { WIN32OLE_METHOD.new(1, 2) }.should raise_error TypeError
+ end
+
+ it "raises ArgumentError if only 1 argument is given" do
+ lambda { WIN32OLE_METHOD.new("hello") }.should raise_error ArgumentError
+ lambda { WIN32OLE_METHOD.new(@ole_type) }.should raise_error ArgumentError
+ end
+
+ it "returns a valid WIN32OLE_METHOD object" do
+ WIN32OLE_METHOD.new(@ole_type, "Open").should be_kind_of WIN32OLE_METHOD
+ WIN32OLE_METHOD.new(@ole_type, "open").should be_kind_of WIN32OLE_METHOD
+ end
+
+ it "raises WIN32OLERuntimeError if the method does not exist" do
+ lambda { WIN32OLE_METHOD.new(@ole_type, "NonexistentMethod") }.should raise_error WIN32OLERuntimeError
+ end
+
+ it "raises TypeError if second argument is not a String" do
+ lambda { WIN32OLE_METHOD.new(@ole_type, 5) }.should raise_error TypeError
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/offset_vtbl_spec.rb b/spec/ruby/library/win32ole/win32ole_method/offset_vtbl_spec.rb
new file mode 100644
index 0000000000..f94e48c051
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/offset_vtbl_spec.rb
@@ -0,0 +1,21 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#offset_vtbl" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File")
+ @m_file_name = WIN32OLE_METHOD.new(ole_type, "name")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @m_file_name.offset_vtbl(1) }.should raise_error ArgumentError
+ end
+
+ it "returns expected value for Scripting Runtime's 'name' method" do
+ pointer_size = PlatformGuard::POINTER_SIZE
+ @m_file_name.offset_vtbl.should == pointer_size
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/params_spec.rb b/spec/ruby/library/win32ole/win32ole_method/params_spec.rb
new file mode 100644
index 0000000000..08c7f04bdd
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/params_spec.rb
@@ -0,0 +1,28 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#params" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File")
+ @m_file_name = WIN32OLE_METHOD.new(ole_type, "name")
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ @m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @m_file_name.params(1) }.should raise_error ArgumentError
+ end
+
+ it "returns empty array for Scripting Runtime's 'name' method" do
+ @m_file_name.params.should be_kind_of Array
+ @m_file_name.params.should be_empty
+ end
+
+ it "returns 4-element array of WIN32OLE_PARAM for Shell's 'BrowseForFolder' method" do
+ @m_browse_for_folder.params.all? { |p| p.kind_of? WIN32OLE_PARAM }.should be_true
+ @m_browse_for_folder.params.size == 4
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/return_type_detail_spec.rb b/spec/ruby/library/win32ole/win32ole_method/return_type_detail_spec.rb
new file mode 100644
index 0000000000..b8f2bbe084
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/return_type_detail_spec.rb
@@ -0,0 +1,21 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#return_type_detail" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ @m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @m_browse_for_folder.return_type_detail(1) }.should raise_error ArgumentError
+ end
+
+ it "returns expected value for Shell Control's 'BrowseForFolder' method" do
+ @m_browse_for_folder.return_type_detail.should be_kind_of Array
+ @m_browse_for_folder.return_type_detail.should == ['PTR', 'USERDEFINED', 'Folder']
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/return_type_spec.rb b/spec/ruby/library/win32ole/win32ole_method/return_type_spec.rb
new file mode 100644
index 0000000000..b68481fe59
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/return_type_spec.rb
@@ -0,0 +1,20 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#return_type" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File")
+ @m_file_name = WIN32OLE_METHOD.new(ole_type, "name")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @m_file_name.return_type(1) }.should raise_error ArgumentError
+ end
+
+ it "returns expected value for Scripting Runtime's 'name' method" do
+ @m_file_name.return_type.should == 'BSTR'
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/return_vtype_spec.rb b/spec/ruby/library/win32ole/win32ole_method/return_vtype_spec.rb
new file mode 100644
index 0000000000..f236de01f9
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/return_vtype_spec.rb
@@ -0,0 +1,20 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#return_vtype" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ @m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @m_browse_for_folder.return_vtype(1) }.should raise_error ArgumentError
+ end
+
+ it "returns expected value for Shell Control's 'BrowseForFolder' method" do
+ @m_browse_for_folder.return_vtype.should == 26
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/shared/name.rb b/spec/ruby/library/win32ole/win32ole_method/shared/name.rb
new file mode 100644
index 0000000000..2be6478a6e
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/shared/name.rb
@@ -0,0 +1,20 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe :win32ole_method_name, shared: true do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File")
+ @m_file_name = WIN32OLE_METHOD.new(ole_type, "name")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @m_file_name.send(@method, 1) }.should raise_error ArgumentError
+ end
+
+ it "returns expected value for Scripting Runtime's 'name' method" do
+ @m_file_name.send(@method).should == 'Name' # note the capitalization
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/size_opt_params_spec.rb b/spec/ruby/library/win32ole/win32ole_method/size_opt_params_spec.rb
new file mode 100644
index 0000000000..a63c50474c
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/size_opt_params_spec.rb
@@ -0,0 +1,20 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#size_opt_params" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ @m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @m_browse_for_folder.size_opt_params(1) }.should raise_error ArgumentError
+ end
+
+ it "returns expected value for Shell Control's 'BrowseForFolder' method" do
+ @m_browse_for_folder.size_opt_params.should == 1
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/size_params_spec.rb b/spec/ruby/library/win32ole/win32ole_method/size_params_spec.rb
new file mode 100644
index 0000000000..fe93d5bc66
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/size_params_spec.rb
@@ -0,0 +1,20 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#size_params" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ @m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @m_browse_for_folder.size_params(1) }.should raise_error ArgumentError
+ end
+
+ it "returns expected value for Shell Control's 'BrowseForFolder' method" do
+ @m_browse_for_folder.size_params.should == 4
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/to_s_spec.rb b/spec/ruby/library/win32ole/win32ole_method/to_s_spec.rb
new file mode 100644
index 0000000000..ecb3c08038
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/to_s_spec.rb
@@ -0,0 +1,11 @@
+require_relative 'shared/name'
+
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#name" do
+ it_behaves_like :win32ole_method_name, :to_s
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_method/visible_spec.rb b/spec/ruby/library/win32ole/win32ole_method/visible_spec.rb
new file mode 100644
index 0000000000..b49fac6066
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_method/visible_spec.rb
@@ -0,0 +1,20 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_METHOD#visible?" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ @m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @m_browse_for_folder.visible?(1) }.should raise_error ArgumentError
+ end
+
+ it "returns true for Shell Control's 'BrowseForFolder' method" do
+ @m_browse_for_folder.visible?.should be_true
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_param/default_spec.rb b/spec/ruby/library/win32ole/win32ole_param/default_spec.rb
new file mode 100644
index 0000000000..7a1337ec7c
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_param/default_spec.rb
@@ -0,0 +1,31 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_PARAM#default" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder")
+ @params = m_browse_for_folder.params
+
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject")
+ m_copyfile = WIN32OLE_METHOD.new(ole_type, "CopyFile")
+ @param_overwritefiles = m_copyfile.params[2]
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @params[0].default(1) }.should raise_error ArgumentError
+ end
+
+ it "returns nil for each of WIN32OLE_PARAM for Shell's 'BrowseForFolder' method" do
+ @params.each do |p|
+ p.default.should be_nil
+ end
+ end
+
+ it "returns true for 3rd parameter of FileSystemObject's 'CopyFile' method" do
+ @param_overwritefiles.default.should == true # not be_true
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_param/input_spec.rb b/spec/ruby/library/win32ole/win32ole_param/input_spec.rb
new file mode 100644
index 0000000000..bdf4bccc79
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_param/input_spec.rb
@@ -0,0 +1,21 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_PARAM#input?" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject")
+ m_copyfile = WIN32OLE_METHOD.new(ole_type, "CopyFile")
+ @param_overwritefiles = m_copyfile.params[2]
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @param_overwritefiles.input?(1) }.should raise_error ArgumentError
+ end
+
+ it "returns true for 3rd parameter of FileSystemObject's 'CopyFile' method" do
+ @param_overwritefiles.input?.should == true
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_param/name_spec.rb b/spec/ruby/library/win32ole/win32ole_param/name_spec.rb
new file mode 100644
index 0000000000..0c20c24720
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_param/name_spec.rb
@@ -0,0 +1,11 @@
+require_relative 'shared/name'
+
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_PARAM#name" do
+ it_behaves_like :win32ole_param_name, :name
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_param/ole_type_detail_spec.rb b/spec/ruby/library/win32ole/win32ole_param/ole_type_detail_spec.rb
new file mode 100644
index 0000000000..3ba51c02f1
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_param/ole_type_detail_spec.rb
@@ -0,0 +1,21 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_PARAM#ole_type_detail" do
+ before :each do
+ ole_type_detail = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject")
+ m_copyfile = WIN32OLE_METHOD.new(ole_type_detail, "CopyFile")
+ @param_overwritefiles = m_copyfile.params[2]
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @param_overwritefiles.ole_type_detail(1) }.should raise_error ArgumentError
+ end
+
+ it "returns ['BOOL'] for 3rd parameter of FileSystemObject's 'CopyFile' method" do
+ @param_overwritefiles.ole_type_detail.should == ['BOOL']
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_param/ole_type_spec.rb b/spec/ruby/library/win32ole/win32ole_param/ole_type_spec.rb
new file mode 100644
index 0000000000..52bee2c9b8
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_param/ole_type_spec.rb
@@ -0,0 +1,21 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_PARAM#ole_type" do
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject")
+ m_copyfile = WIN32OLE_METHOD.new(ole_type, "CopyFile")
+ @param_overwritefiles = m_copyfile.params[2]
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @param_overwritefiles.ole_type(1) }.should raise_error ArgumentError
+ end
+
+ it "returns 'BOOL' for 3rd parameter of FileSystemObject's 'CopyFile' method" do
+ @param_overwritefiles.ole_type.should == 'BOOL'
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_param/optional_spec.rb b/spec/ruby/library/win32ole/win32ole_param/optional_spec.rb
new file mode 100644
index 0000000000..2476df8641
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_param/optional_spec.rb
@@ -0,0 +1,21 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_PARAM#optional?" do
+ before :each do
+ ole_type_detail = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject")
+ m_copyfile = WIN32OLE_METHOD.new(ole_type_detail, "CopyFile")
+ @param_overwritefiles = m_copyfile.params[2]
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @param_overwritefiles.optional?(1) }.should raise_error ArgumentError
+ end
+
+ it "returns true for 3rd parameter of FileSystemObject's 'CopyFile' method" do
+ @param_overwritefiles.optional?.should be_true
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_param/retval_spec.rb b/spec/ruby/library/win32ole/win32ole_param/retval_spec.rb
new file mode 100644
index 0000000000..90946c0774
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_param/retval_spec.rb
@@ -0,0 +1,21 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_PARAM#retval?" do
+ before :each do
+ ole_type_detail = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject")
+ m_copyfile = WIN32OLE_METHOD.new(ole_type_detail, "CopyFile")
+ @param_overwritefiles = m_copyfile.params[2]
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @param_overwritefiles.retval?(1) }.should raise_error ArgumentError
+ end
+
+ it "returns false for 3rd parameter of FileSystemObject's 'CopyFile' method" do
+ @param_overwritefiles.retval?.should be_false
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_param/shared/name.rb b/spec/ruby/library/win32ole/win32ole_param/shared/name.rb
new file mode 100644
index 0000000000..b7892d92fb
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_param/shared/name.rb
@@ -0,0 +1,21 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe :win32ole_param_name, shared: true do
+ before :each do
+ ole_type_detail = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject")
+ m_copyfile = WIN32OLE_METHOD.new(ole_type_detail, "CopyFile")
+ @param_overwritefiles = m_copyfile.params[2]
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @param_overwritefiles.send(@method, 1) }.should raise_error ArgumentError
+ end
+
+ it "returns expected value for Scripting Runtime's 'name' method" do
+ @param_overwritefiles.send(@method).should == 'OverWriteFiles' # note the capitalization
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_param/to_s_spec.rb b/spec/ruby/library/win32ole/win32ole_param/to_s_spec.rb
new file mode 100644
index 0000000000..5b4b4c1c80
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_param/to_s_spec.rb
@@ -0,0 +1,11 @@
+require_relative 'shared/name'
+
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_PARAM#to_s" do
+ it_behaves_like :win32ole_param_name, :to_s
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/guid_spec.rb b/spec/ruby/library/win32ole/win32ole_type/guid_spec.rb
new file mode 100644
index 0000000000..25907c8e32
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/guid_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE#guid for Shell Controls" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ after :each do
+ @ole_type = nil
+ end
+
+ it "returns String with expected format" do
+ @ole_type.guid.should =~ /\A\{[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\}\z/
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/helpcontext_spec.rb b/spec/ruby/library/win32ole/win32ole_type/helpcontext_spec.rb
new file mode 100644
index 0000000000..d436835188
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/helpcontext_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE#helpcontext for Shell Controls" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ after :each do
+ @ole_type = nil
+ end
+
+ it "returns an Integer" do
+ @ole_type.helpcontext.should be_kind_of Integer
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/helpfile_spec.rb b/spec/ruby/library/win32ole/win32ole_type/helpfile_spec.rb
new file mode 100644
index 0000000000..01e6945138
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/helpfile_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE#helpfile for Shell Controls" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ after :each do
+ @ole_type = nil
+ end
+
+ it "returns an empty string" do
+ @ole_type.helpfile.should be_empty
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/helpstring_spec.rb b/spec/ruby/library/win32ole/win32ole_type/helpstring_spec.rb
new file mode 100644
index 0000000000..3bd2cbe5dd
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/helpstring_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE#helpstring for Shell Controls" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ after :each do
+ @ole_type = nil
+ end
+
+ it "returns expected string" do
+ @ole_type.helpstring.should == "Shell Object Type Information"
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/major_version_spec.rb b/spec/ruby/library/win32ole/win32ole_type/major_version_spec.rb
new file mode 100644
index 0000000000..7dae16617d
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/major_version_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE#major_version for Shell Controls" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ after :each do
+ @ole_type = nil
+ end
+
+ it "returns an Integer" do
+ @ole_type.major_version.should be_kind_of Integer
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/minor_version_spec.rb b/spec/ruby/library/win32ole/win32ole_type/minor_version_spec.rb
new file mode 100644
index 0000000000..ff412dd100
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/minor_version_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE#minor_version for Shell Controls" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ after :each do
+ @ole_type = nil
+ end
+
+ it "returns an Integer" do
+ @ole_type.minor_version.should be_kind_of Integer
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/name_spec.rb b/spec/ruby/library/win32ole/win32ole_type/name_spec.rb
new file mode 100644
index 0000000000..b7a28c553a
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/name_spec.rb
@@ -0,0 +1,11 @@
+require_relative 'shared/name'
+
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE#name" do
+ it_behaves_like :win32ole_type_name, :name
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/new_spec.rb b/spec/ruby/library/win32ole/win32ole_type/new_spec.rb
new file mode 100644
index 0000000000..9443a64c75
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/new_spec.rb
@@ -0,0 +1,37 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE.new" do
+ it "raises ArgumentError with no argument" do
+ lambda { WIN32OLE_TYPE.new }.should raise_error ArgumentError
+ end
+
+ it "raises ArgumentError with invalid string" do
+ lambda { WIN32OLE_TYPE.new("foo") }.should raise_error ArgumentError
+ end
+
+ it "raises TypeError if second argument is not a String" do
+ lambda { WIN32OLE_TYPE.new(1,2) }.should raise_error TypeError
+ lambda { WIN32OLE_TYPE.new('Microsoft Shell Controls And Automation',2) }.
+ should raise_error TypeError
+ end
+
+ it "raise WIN32OLERuntimeError if OLE object specified is not found" do
+ lambda { WIN32OLE_TYPE.new('Microsoft Shell Controls And Automation','foo') }.
+ should raise_error WIN32OLERuntimeError
+ lambda { WIN32OLE_TYPE.new('Microsoft Shell Controls And Automation','Application') }.
+ should raise_error WIN32OLERuntimeError
+ end
+
+ it "creates WIN32OLE_TYPE object from name and valid type" do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ ole_type.should be_kind_of WIN32OLE_TYPE
+ end
+
+ it "creates WIN32OLE_TYPE object from CLSID and valid type" do
+ ole_type2 = WIN32OLE_TYPE.new("{13709620-C279-11CE-A49E-444553540000}", "Shell")
+ ole_type2.should be_kind_of WIN32OLE_TYPE
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/ole_classes_spec.rb b/spec/ruby/library/win32ole/win32ole_type/ole_classes_spec.rb
new file mode 100644
index 0000000000..0ce0fc98a4
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/ole_classes_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE.ole_classes for Shell Controls" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ after :each do
+ @ole_type = nil
+ end
+
+ it "returns array of WIN32OLE_TYPEs" do
+ WIN32OLE_TYPE.ole_classes("Microsoft Shell Controls And Automation").all? {|e| e.kind_of? WIN32OLE_TYPE }.should be_true
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/ole_methods_spec.rb b/spec/ruby/library/win32ole/win32ole_type/ole_methods_spec.rb
new file mode 100644
index 0000000000..9265549d20
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/ole_methods_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE#ole_methods for Shell Controls" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ after :each do
+ @ole_type = nil
+ end
+
+ it "returns an Integer" do
+ @ole_type.ole_methods.all? { |m| m.kind_of? WIN32OLE_METHOD }.should be_true
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/ole_type_spec.rb b/spec/ruby/library/win32ole/win32ole_type/ole_type_spec.rb
new file mode 100644
index 0000000000..2bc19aa85e
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/ole_type_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE#ole_type for Shell Controls" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ after :each do
+ @ole_type = nil
+ end
+
+ it "returns string 'Class'" do
+ @ole_type.ole_type.should == "Class"
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/progid_spec.rb b/spec/ruby/library/win32ole/win32ole_type/progid_spec.rb
new file mode 100644
index 0000000000..f0d80ba39e
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/progid_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE#progid for Shell Controls" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ after :each do
+ @ole_type = nil
+ end
+
+ it "returns expected string" do
+ @ole_type.progid.should == "Shell.Application.1"
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/progids_spec.rb b/spec/ruby/library/win32ole/win32ole_type/progids_spec.rb
new file mode 100644
index 0000000000..0cdd3514e3
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/progids_spec.rb
@@ -0,0 +1,14 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE.progids" do
+ it "raises ArgumentError if an argument is given" do
+ lambda { WIN32OLE_TYPE.progids(1) }.should raise_error ArgumentError
+ end
+
+ it "returns an array containing 'Shell.Explorer'" do
+ WIN32OLE_TYPE.progids().include?('Shell.Explorer').should be_true
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/shared/name.rb b/spec/ruby/library/win32ole/win32ole_type/shared/name.rb
new file mode 100644
index 0000000000..6484ef0ef8
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/shared/name.rb
@@ -0,0 +1,19 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe :win32ole_type_name, shared: true do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants")
+ end
+
+ it "raises ArgumentError if argument is given" do
+ lambda { @ole_type.send(@method, 1) }.should raise_error ArgumentError
+ end
+
+ it "returns a String" do
+ @ole_type.send(@method).should == 'ShellSpecialFolderConstants'
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/src_type_spec.rb b/spec/ruby/library/win32ole/win32ole_type/src_type_spec.rb
new file mode 100644
index 0000000000..71e304d80a
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/src_type_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE#src_type for Shell Controls" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ after :each do
+ @ole_type = nil
+ end
+
+ it "returns nil" do
+ @ole_type.src_type.should be_nil
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/to_s_spec.rb b/spec/ruby/library/win32ole/win32ole_type/to_s_spec.rb
new file mode 100644
index 0000000000..b713990ed2
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/to_s_spec.rb
@@ -0,0 +1,11 @@
+require_relative 'shared/name'
+
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE#to_s" do
+ it_behaves_like :win32ole_type_name, :to_s
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/typekind_spec.rb b/spec/ruby/library/win32ole/win32ole_type/typekind_spec.rb
new file mode 100644
index 0000000000..35f3562721
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/typekind_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE#typekind for Shell Controls" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ after :each do
+ @ole_type = nil
+ end
+
+ it "returns an Integer" do
+ @ole_type.typekind.should be_kind_of Integer
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/typelibs_spec.rb b/spec/ruby/library/win32ole/win32ole_type/typelibs_spec.rb
new file mode 100644
index 0000000000..3a28c0496c
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/typelibs_spec.rb
@@ -0,0 +1,22 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE.typelibs for Shell Controls" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ after :each do
+ @ole_type = nil
+ end
+
+ it "raises ArgumentError if any argument is give" do
+ lambda { WIN32OLE_TYPE.typelibs(1) }.should raise_error ArgumentError
+ end
+
+ it "returns array of type libraries" do
+ WIN32OLE_TYPE.typelibs().include?("Microsoft Shell Controls And Automation").should be_true
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/variables_spec.rb b/spec/ruby/library/win32ole/win32ole_type/variables_spec.rb
new file mode 100644
index 0000000000..fbf3dd0341
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/variables_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE#variables for Shell Controls" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ after :each do
+ @ole_type = nil
+ end
+
+ it "returns an empty array" do
+ @ole_type.variables.should == []
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_type/visible_spec.rb b/spec/ruby/library/win32ole/win32ole_type/visible_spec.rb
new file mode 100644
index 0000000000..403b2b843b
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_type/visible_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_TYPE#visible? for Shell Controls" do
+ before :each do
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ after :each do
+ @ole_type = nil
+ end
+
+ it "returns true" do
+ @ole_type.visible?.should be_true
+ end
+
+ end
+end
diff --git a/spec/ruby/library/win32ole/win32ole_variable/name_spec.rb b/spec/ruby/library/win32ole/win32ole_variable/name_spec.rb
new file mode 100644
index 0000000000..8bac1a9891
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_variable/name_spec.rb
@@ -0,0 +1,11 @@
+require_relative 'shared/name'
+
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_VARIABLE#name" do
+ it_behaves_like :win32ole_variable_new, :name
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_variable/ole_type_detail_spec.rb b/spec/ruby/library/win32ole/win32ole_variable/ole_type_detail_spec.rb
new file mode 100644
index 0000000000..dab4edabaa
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_variable/ole_type_detail_spec.rb
@@ -0,0 +1,19 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_VARIABLE#ole_type_detail" do
+ # not sure how WIN32OLE_VARIABLE objects are supposed to be generated
+ # WIN32OLE_VARIABLE.new even seg faults in some cases
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants")
+ @var = ole_type.variables[0]
+ end
+
+ it "returns a nonempty Array" do
+ @var.ole_type_detail.should be_kind_of Array
+ @var.ole_type_detail.should_not be_empty
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_variable/ole_type_spec.rb b/spec/ruby/library/win32ole/win32ole_variable/ole_type_spec.rb
new file mode 100644
index 0000000000..d08acc9bde
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_variable/ole_type_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_VARIABLE#ole_type" do
+ # not sure how WIN32OLE_VARIABLE objects are supposed to be generated
+ # WIN32OLE_VARIABLE.new even seg faults in some cases
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants")
+ @var = ole_type.variables[0]
+ end
+
+ it "returns a String" do
+ @var.ole_type.should be_kind_of String
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_variable/shared/name.rb b/spec/ruby/library/win32ole/win32ole_variable/shared/name.rb
new file mode 100644
index 0000000000..033e830fac
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_variable/shared/name.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe :win32ole_variable_new, shared: true do
+ # not sure how WIN32OLE_VARIABLE objects are supposed to be generated
+ # WIN32OLE_VARIABLE.new even seg faults in some cases
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants")
+ @var = ole_type.variables[0]
+ end
+
+ it "returns a String" do
+ @var.send(@method).should be_kind_of String
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_variable/to_s_spec.rb b/spec/ruby/library/win32ole/win32ole_variable/to_s_spec.rb
new file mode 100644
index 0000000000..000ac14d7e
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_variable/to_s_spec.rb
@@ -0,0 +1,11 @@
+require_relative 'shared/name'
+
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_VARIABLE#to_s" do
+ it_behaves_like :win32ole_variable_new, :to_s
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_variable/value_spec.rb b/spec/ruby/library/win32ole/win32ole_variable/value_spec.rb
new file mode 100644
index 0000000000..c15f64c2c5
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_variable/value_spec.rb
@@ -0,0 +1,19 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_VARIABLE#value" do
+ # not sure how WIN32OLE_VARIABLE objects are supposed to be generated
+ # WIN32OLE_VARIABLE.new even seg faults in some cases
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants")
+ @var = ole_type.variables[0]
+ end
+
+ it "returns a Integer" do
+ # according to doc, this could return nil
+ @var.value.should be_kind_of Integer
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_variable/variable_kind_spec.rb b/spec/ruby/library/win32ole/win32ole_variable/variable_kind_spec.rb
new file mode 100644
index 0000000000..4cca7f8874
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_variable/variable_kind_spec.rb
@@ -0,0 +1,19 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_VARIABLE#variable_kind" do
+ # not sure how WIN32OLE_VARIABLE objects are supposed to be generated
+ # WIN32OLE_VARIABLE.new even seg faults in some cases
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants")
+ @var = ole_type.variables[0]
+ end
+
+ it "returns a String" do
+ @var.variable_kind.should be_kind_of String
+ @var.variable_kind.should == 'CONSTANT'
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_variable/varkind_spec.rb b/spec/ruby/library/win32ole/win32ole_variable/varkind_spec.rb
new file mode 100644
index 0000000000..56cd1c337a
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_variable/varkind_spec.rb
@@ -0,0 +1,19 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_VARIABLE#varkind" do
+ # TODO review
+ # not sure how WIN32OLE_VARIABLE objects are supposed to be generated
+ # WIN32OLE_VARIABLE.new even seg faults in some cases
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants")
+ @var = ole_type.variables[0]
+ end
+
+ it "returns an Integer" do
+ @var.varkind.should be_kind_of Integer
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/win32ole/win32ole_variable/visible_spec.rb b/spec/ruby/library/win32ole/win32ole_variable/visible_spec.rb
new file mode 100644
index 0000000000..7f7a557b57
--- /dev/null
+++ b/spec/ruby/library/win32ole/win32ole_variable/visible_spec.rb
@@ -0,0 +1,18 @@
+platform_is :windows do
+ require 'win32ole'
+
+ describe "WIN32OLE_VARIABLE#visible?" do
+ # not sure how WIN32OLE_VARIABLE objects are supposed to be generated
+ # WIN32OLE_VARIABLE.new even seg faults in some cases
+ before :each do
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants")
+ @var = ole_type.variables[0]
+ end
+
+ it "returns a String" do
+ @var.visible?.should be_true
+ end
+
+ end
+
+end
diff --git a/spec/ruby/library/yaml/add_builtin_type_spec.rb b/spec/ruby/library/yaml/add_builtin_type_spec.rb
new file mode 100644
index 0000000000..44c820940f
--- /dev/null
+++ b/spec/ruby/library/yaml/add_builtin_type_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
diff --git a/spec/ruby/library/yaml/add_domain_type_spec.rb b/spec/ruby/library/yaml/add_domain_type_spec.rb
new file mode 100644
index 0000000000..44c820940f
--- /dev/null
+++ b/spec/ruby/library/yaml/add_domain_type_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
diff --git a/spec/ruby/library/yaml/add_private_type_spec.rb b/spec/ruby/library/yaml/add_private_type_spec.rb
new file mode 100644
index 0000000000..44c820940f
--- /dev/null
+++ b/spec/ruby/library/yaml/add_private_type_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
diff --git a/spec/ruby/library/yaml/add_ruby_type_spec.rb b/spec/ruby/library/yaml/add_ruby_type_spec.rb
new file mode 100644
index 0000000000..44c820940f
--- /dev/null
+++ b/spec/ruby/library/yaml/add_ruby_type_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
diff --git a/spec/ruby/library/yaml/detect_implicit_spec.rb b/spec/ruby/library/yaml/detect_implicit_spec.rb
new file mode 100644
index 0000000000..44c820940f
--- /dev/null
+++ b/spec/ruby/library/yaml/detect_implicit_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
diff --git a/spec/ruby/library/yaml/dump_spec.rb b/spec/ruby/library/yaml/dump_spec.rb
new file mode 100644
index 0000000000..5af794b7f8
--- /dev/null
+++ b/spec/ruby/library/yaml/dump_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+# TODO: WTF is this using a global?
+describe "YAML.dump" do
+ after :each do
+ rm_r $test_file
+ end
+
+ it "converts an object to YAML and write result to io when io provided" do
+ File.open($test_file, 'w' ) do |io|
+ YAML.dump( ['badger', 'elephant', 'tiger'], io )
+ end
+ YAML.load_file($test_file).should == ['badger', 'elephant', 'tiger']
+ end
+
+ it "returns a string containing dumped YAML when no io provided" do
+ YAML.dump( :locked ).should match_yaml("--- :locked\n")
+ end
+
+ it "returns the same string that #to_yaml on objects" do
+ ["a", "b", "c"].to_yaml.should == YAML.dump(["a", "b", "c"])
+ end
+
+ it "dumps strings into YAML strings" do
+ YAML.dump("str").should match_yaml("--- str\n")
+ end
+
+ it "dumps hashes into YAML key-values" do
+ YAML.dump({ "a" => "b" }).should match_yaml("--- \na: b\n")
+ end
+
+ it "dumps Arrays into YAML collection" do
+ YAML.dump(["a", "b", "c"]).should match_yaml("--- \n- a\n- b\n- c\n")
+ end
+
+ it "dumps an OpenStruct" do
+ require "ostruct"
+ os = OpenStruct.new("age" => 20, "name" => "John")
+ YAML.dump(os).should match_yaml("--- !ruby/object:OpenStruct\ntable:\n :age: 20\n :name: John\n")
+ end
+
+ it "dumps a File without any state" do
+ file = File.new(__FILE__)
+ begin
+ YAML.dump(file).should match_yaml("--- !ruby/object:File {}\n")
+ ensure
+ file.close
+ end
+ end
+end
diff --git a/spec/ruby/library/yaml/dump_stream_spec.rb b/spec/ruby/library/yaml/dump_stream_spec.rb
new file mode 100644
index 0000000000..9d30fef819
--- /dev/null
+++ b/spec/ruby/library/yaml/dump_stream_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "YAML.dump_stream" do
+ it "returns a YAML stream containing the objects passed" do
+ YAML.dump_stream('foo', 20, [], {}).should match_yaml("--- foo\n--- 20\n--- []\n\n--- {}\n\n")
+ end
+end
diff --git a/spec/ruby/library/yaml/each_node_spec.rb b/spec/ruby/library/yaml/each_node_spec.rb
new file mode 100644
index 0000000000..44c820940f
--- /dev/null
+++ b/spec/ruby/library/yaml/each_node_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
diff --git a/spec/ruby/library/yaml/emitter_spec.rb b/spec/ruby/library/yaml/emitter_spec.rb
new file mode 100644
index 0000000000..44c820940f
--- /dev/null
+++ b/spec/ruby/library/yaml/emitter_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
diff --git a/spec/ruby/library/yaml/fixtures/common.rb b/spec/ruby/library/yaml/fixtures/common.rb
new file mode 100644
index 0000000000..1d868806f1
--- /dev/null
+++ b/spec/ruby/library/yaml/fixtures/common.rb
@@ -0,0 +1,10 @@
+begin
+ require 'syck'
+rescue LoadError
+ # do nothing
+end
+
+require 'yaml'
+
+$test_file = tmp("yaml_test_file")
+$test_parse_file = File.dirname(__FILE__) + "/test_yaml.yml"
diff --git a/spec/ruby/library/yaml/fixtures/example_class.rb b/spec/ruby/library/yaml/fixtures/example_class.rb
new file mode 100644
index 0000000000..751435a305
--- /dev/null
+++ b/spec/ruby/library/yaml/fixtures/example_class.rb
@@ -0,0 +1,5 @@
+class FooBar
+ def initialize(name)
+ @name = name
+ end
+end
diff --git a/spec/ruby/library/yaml/fixtures/strings.rb b/spec/ruby/library/yaml/fixtures/strings.rb
new file mode 100644
index 0000000000..6f66dc3659
--- /dev/null
+++ b/spec/ruby/library/yaml/fixtures/strings.rb
@@ -0,0 +1,36 @@
+$complex_key_1 = <<EOY
+ ? # PLAY SCHEDULE
+ - Detroit Tigers
+ - Chicago Cubs
+ :
+ - 2001-07-23
+
+ ? [ New York Yankees,
+ Atlanta Braves ]
+ : [ 2001-07-02, 2001-08-12,
+ 2001-08-14 ]
+EOY
+
+$to_yaml_hash =
+<<EOY
+-
+ avg: 0.278
+ hr: 65
+ name: Mark McGwire
+-
+ avg: 0.288
+ hr: 63
+ name: Sammy Sosa
+EOY
+
+$multidocument = <<EOY
+---
+- Mark McGwire
+- Sammy Sosa
+- Ken Griffey
+
+# Team ranking
+---
+- Chicago Cubs
+- St Louis Cardinals
+EOY
diff --git a/spec/ruby/library/yaml/fixtures/test_yaml.yml b/spec/ruby/library/yaml/fixtures/test_yaml.yml
new file mode 100644
index 0000000000..efe3b5cc1a
--- /dev/null
+++ b/spec/ruby/library/yaml/fixtures/test_yaml.yml
@@ -0,0 +1,2 @@
+project:
+ name: RubySpec
diff --git a/spec/ruby/library/yaml/generic_parser_spec.rb b/spec/ruby/library/yaml/generic_parser_spec.rb
new file mode 100644
index 0000000000..44c820940f
--- /dev/null
+++ b/spec/ruby/library/yaml/generic_parser_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
diff --git a/spec/ruby/library/yaml/load_documents_spec.rb b/spec/ruby/library/yaml/load_documents_spec.rb
new file mode 100644
index 0000000000..27edbcaa86
--- /dev/null
+++ b/spec/ruby/library/yaml/load_documents_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'fixtures/strings'
+require_relative 'shared/each_document'
+
+ruby_version_is ''...'2.5' do
+ describe "YAML.load_documents" do
+ it_behaves_like :yaml_each_document, :load_documents
+ end
+end
diff --git a/spec/ruby/library/yaml/load_file_spec.rb b/spec/ruby/library/yaml/load_file_spec.rb
new file mode 100644
index 0000000000..2363c08120
--- /dev/null
+++ b/spec/ruby/library/yaml/load_file_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "YAML.load_file" do
+ after :each do
+ rm_r $test_file
+ end
+
+ it "returns a hash" do
+ File.open($test_file,'w' ){|io| YAML.dump( {"bar"=>2, "car"=>1}, io ) }
+ YAML.load_file($test_file).should == {"bar"=>2, "car"=>1}
+ end
+end
diff --git a/spec/ruby/library/yaml/load_spec.rb b/spec/ruby/library/yaml/load_spec.rb
new file mode 100644
index 0000000000..1e16bed4e6
--- /dev/null
+++ b/spec/ruby/library/yaml/load_spec.rb
@@ -0,0 +1,137 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'fixtures/strings'
+
+describe "YAML.load" do
+ after :each do
+ rm_r $test_file
+ end
+
+ it "returns a document from current io stream when io provided" do
+ File.open($test_file, 'w') do |io|
+ YAML.dump( ['badger', 'elephant', 'tiger'], io )
+ end
+ File.open($test_file) { |yf| YAML.load( yf ) }.should == ['badger', 'elephant', 'tiger']
+ end
+
+ it "loads strings" do
+ strings = ["str",
+ " str",
+ "'str'",
+ "str",
+ " str",
+ "'str'",
+ "\"str\"",
+ "\n str",
+ "--- str",
+ "---\nstr",
+ "--- \nstr",
+ "--- \n str",
+ "--- 'str'"
+ ]
+ strings.each do |str|
+ YAML.load(str).should == "str"
+ end
+ end
+
+ it "loads strings with chars from non-base Unicode plane" do
+ # We add these strings as bytes and force the encoding for safety
+ # as bugs in parsing unicode characters can obscure bugs in this
+ # area.
+
+ yaml_and_strings = {
+ # "--- 🌵" => "🌵"
+ [45, 45, 45, 32, 240, 159, 140, 181] =>
+ [240, 159, 140, 181],
+ # "--- 🌵 and some text" => "🌵 and some text"
+ [45, 45, 45, 32, 240, 159, 140, 181, 32, 97, 110, 100, 32, 115, 111, 109, 101, 32, 116, 101, 120, 116] =>
+ [240, 159, 140, 181, 32, 97, 110, 100, 32, 115, 111, 109, 101, 32, 116, 101, 120, 116],
+ # "--- Some text 🌵 and some text" => "Some text 🌵 and some text"
+ [45, 45, 45, 32, 83, 111, 109, 101, 32, 116, 101, 120, 116, 32, 240, 159, 140, 181, 32, 97, 110, 100, 32, 115, 111, 109, 101, 32, 116, 101, 120, 116] =>
+ [83, 111, 109, 101, 32, 116, 101, 120, 116, 32, 240, 159, 140, 181, 32, 97, 110, 100, 32, 115, 111, 109, 101, 32, 116, 101, 120, 116]
+ }
+ yaml_and_strings.each do |yaml, str|
+ YAML.load(yaml.pack("C*").force_encoding("UTF-8")).should == str.pack("C*").force_encoding("UTF-8")
+ end
+ end
+
+ it "fails on invalid keys" do
+ if YAML.to_s == "Psych"
+ error = Psych::SyntaxError
+ else
+ error = ArgumentError
+ end
+ lambda { YAML.load("key1: value\ninvalid_key") }.should raise_error(error)
+ end
+
+ it "accepts symbols" do
+ YAML.load( "--- :locked" ).should == :locked
+ end
+
+ it "accepts numbers" do
+ YAML.load("47").should == 47
+ YAML.load("-1").should == -1
+ end
+
+ it "accepts collections" do
+ expected = ["a", "b", "c"]
+ YAML.load("--- \n- a\n- b\n- c\n").should == expected
+ YAML.load("--- [a, b, c]").should == expected
+ YAML.load("[a, b, c]").should == expected
+ end
+
+ it "parses start markers" do
+ YAML.load("---\n").should == nil
+ YAML.load("--- ---\n").should == "---"
+ YAML.load("--- abc").should == "abc"
+ end
+
+ it "works with block sequence shortcuts" do
+ block_seq = "- - - one\n - two\n - three"
+ YAML.load(block_seq).should == [[["one", "two", "three"]]]
+ end
+
+ it "works on complex keys" do
+ require 'date'
+ expected = {
+ [ 'Detroit Tigers', 'Chicago Cubs' ] => [ Date.new( 2001, 7, 23 ) ],
+ [ 'New York Yankees', 'Atlanta Braves' ] => [ Date.new( 2001, 7, 2 ),
+ Date.new( 2001, 8, 12 ),
+ Date.new( 2001, 8, 14 ) ]
+ }
+ YAML.load($complex_key_1).should == expected
+ end
+
+ it "loads a symbol key that contains spaces" do
+ string = ":user name: This is the user name."
+ expected = { :"user name" => "This is the user name."}
+ YAML.load(string).should == expected
+ end
+
+ describe "with iso8601 timestamp" do
+ it "computes the microseconds" do
+ [ [YAML.load("2011-03-22t23:32:11.2233+01:00"), 223300],
+ [YAML.load("2011-03-22t23:32:11.0099+01:00"), 9900],
+ [YAML.load("2011-03-22t23:32:11.000076+01:00"), 76]
+ ].should be_computed_by(:usec)
+ end
+
+ it "rounds values smaller than 1 usec to 0 " do
+ YAML.load("2011-03-22t23:32:11.000000342222+01:00").usec.should == 0
+ end
+ end
+
+ it "loads an OpenStruct" do
+ require "ostruct"
+ os = OpenStruct.new("age" => 20, "name" => "John")
+ loaded = YAML.load("--- !ruby/object:OpenStruct\ntable:\n :age: 20\n :name: John\n")
+ loaded.should == os
+ end
+
+ it "loads a File but raise an error when used as it is uninitialized" do
+ loaded = YAML.load("--- !ruby/object:File {}\n")
+ lambda {
+ loaded.read(1)
+ }.should raise_error(IOError)
+ end
+end
diff --git a/spec/ruby/library/yaml/load_stream_spec.rb b/spec/ruby/library/yaml/load_stream_spec.rb
new file mode 100644
index 0000000000..689653c8cd
--- /dev/null
+++ b/spec/ruby/library/yaml/load_stream_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'fixtures/strings'
+require_relative 'shared/each_document'
+
+describe "YAML.load_stream" do
+ it_behaves_like :yaml_each_document, :load_stream
+end
diff --git a/spec/ruby/library/yaml/object_maker_spec.rb b/spec/ruby/library/yaml/object_maker_spec.rb
new file mode 100644
index 0000000000..44c820940f
--- /dev/null
+++ b/spec/ruby/library/yaml/object_maker_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
diff --git a/spec/ruby/library/yaml/parse_documents_spec.rb b/spec/ruby/library/yaml/parse_documents_spec.rb
new file mode 100644
index 0000000000..44c820940f
--- /dev/null
+++ b/spec/ruby/library/yaml/parse_documents_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
diff --git a/spec/ruby/library/yaml/parse_file_spec.rb b/spec/ruby/library/yaml/parse_file_spec.rb
new file mode 100644
index 0000000000..8d307c5daf
--- /dev/null
+++ b/spec/ruby/library/yaml/parse_file_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "YAML#parse_file" do
+ quarantine! do
+ it "returns a YAML::Syck::Map object after parsing a YAML file" do
+ YAML.parse_file($test_parse_file).should be_kind_of(YAML::Syck::Map)
+ end
+ end
+end
diff --git a/spec/ruby/library/yaml/parse_spec.rb b/spec/ruby/library/yaml/parse_spec.rb
new file mode 100644
index 0000000000..d5dbfdcee2
--- /dev/null
+++ b/spec/ruby/library/yaml/parse_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "YAML#parse with an empty string" do
+ it "returns false" do
+ YAML.parse('').should be_false
+ end
+end
+
+describe "YAML#parse" do
+ before :each do
+ @string_yaml = "foo".to_yaml
+ end
+
+ it "returns the value from the object" do
+ if YAML.to_s == "Psych"
+ YAML.parse(@string_yaml).to_ruby.should == "foo"
+ else
+ YAML.parse(@string_yaml).value.should == "foo"
+ end
+ end
+end
diff --git a/spec/ruby/library/yaml/parser_spec.rb b/spec/ruby/library/yaml/parser_spec.rb
new file mode 100644
index 0000000000..44c820940f
--- /dev/null
+++ b/spec/ruby/library/yaml/parser_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
diff --git a/spec/ruby/library/yaml/quick_emit_spec.rb b/spec/ruby/library/yaml/quick_emit_spec.rb
new file mode 100644
index 0000000000..44c820940f
--- /dev/null
+++ b/spec/ruby/library/yaml/quick_emit_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
diff --git a/spec/ruby/library/yaml/read_type_class_spec.rb b/spec/ruby/library/yaml/read_type_class_spec.rb
new file mode 100644
index 0000000000..44c820940f
--- /dev/null
+++ b/spec/ruby/library/yaml/read_type_class_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
diff --git a/spec/ruby/library/yaml/shared/each_document.rb b/spec/ruby/library/yaml/shared/each_document.rb
new file mode 100644
index 0000000000..999123dc2a
--- /dev/null
+++ b/spec/ruby/library/yaml/shared/each_document.rb
@@ -0,0 +1,18 @@
+describe :yaml_each_document, shared: true do
+ it "calls the block on each successive document" do
+ documents = []
+ YAML.send(@method, $multidocument) do |doc|
+ documents << doc
+ end
+ documents.should == [["Mark McGwire", "Sammy Sosa", "Ken Griffey"],
+ ["Chicago Cubs", "St Louis Cardinals"]]
+ end
+
+ it "works on files" do
+ File.open($test_parse_file, "r") do |file|
+ YAML.send(@method, file) do |doc|
+ doc.should == {"project"=>{"name"=>"RubySpec"}}
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/yaml/tagurize_spec.rb b/spec/ruby/library/yaml/tagurize_spec.rb
new file mode 100644
index 0000000000..cc1b757ce9
--- /dev/null
+++ b/spec/ruby/library/yaml/tagurize_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+ruby_version_is ''...'2.5' do
+ describe "YAML.tagurize" do
+ it "converts a type_id to a taguri" do
+ YAML.tagurize('wtf').should == "tag:yaml.org,2002:wtf"
+ YAML.tagurize(1).should == 1
+ end
+ end
+end
diff --git a/spec/ruby/library/yaml/to_yaml_spec.rb b/spec/ruby/library/yaml/to_yaml_spec.rb
new file mode 100644
index 0000000000..9713657a26
--- /dev/null
+++ b/spec/ruby/library/yaml/to_yaml_spec.rb
@@ -0,0 +1,99 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative 'fixtures/example_class'
+
+describe "Object#to_yaml" do
+
+ it "returns the YAML representation of an Array object" do
+ %w( 30 ruby maz irb 99 ).to_yaml.gsub("'", '"').should match_yaml("--- \n- \"30\"\n- ruby\n- maz\n- irb\n- \"99\"\n")
+ end
+
+ it "returns the YAML representation of a Hash object" do
+ { "a" => "b"}.to_yaml.should match_yaml("--- \na: b\n")
+ end
+
+ it "returns the YAML representation of a Class object" do
+ FooBar.new("baz").to_yaml.should match_yaml("--- !ruby/object:FooBar\nname: baz\n")
+
+ end
+
+ it "returns the YAML representation of a Date object" do
+ require 'date'
+ Date.parse('1997/12/30').to_yaml.should match_yaml("--- 1997-12-30\n")
+ end
+
+ it "returns the YAML representation of a FalseClass" do
+ false_klass = false
+ false_klass.should be_kind_of(FalseClass)
+ false_klass.to_yaml.should match_yaml("--- false\n")
+ end
+
+ it "returns the YAML representation of a Float object" do
+ float = 1.2
+ float.should be_kind_of(Float)
+ float.to_yaml.should match_yaml("--- 1.2\n")
+ end
+
+ it "returns the YAML representation of an Integer object" do
+ int = 20
+ int.should be_kind_of(Integer)
+ int.to_yaml.should match_yaml("--- 20\n")
+ end
+
+ it "returns the YAML representation of a NilClass object" do
+ nil_klass = nil
+ nil_klass.should be_kind_of(NilClass)
+ nil_klass.to_yaml.should match_yaml("--- \n")
+ end
+
+ it "returns the YAML representation of a RegExp object" do
+ Regexp.new('^a-z+:\\s+\w+').to_yaml.should match_yaml("--- !ruby/regexp /^a-z+:\\s+\\w+/\n")
+ end
+
+ it "returns the YAML representation of a String object" do
+ "I love Ruby".to_yaml.should match_yaml("--- I love Ruby\n")
+ end
+
+ it "returns the YAML representation of a Struct object" do
+ Person = Struct.new(:name, :gender)
+ Person.new("Jane", "female").to_yaml.should match_yaml("--- !ruby/struct:Person\nname: Jane\ngender: female\n")
+ end
+
+ it "returns the YAML representation of a Symbol object" do
+ :symbol.to_yaml.should match_yaml("--- :symbol\n")
+ end
+
+ it "returns the YAML representation of a Time object" do
+ Time.utc(2000,"jan",1,20,15,1).to_yaml.sub(/\.0+/, "").should match_yaml("--- 2000-01-01 20:15:01 Z\n")
+ end
+
+ it "returns the YAML representation of a TrueClass" do
+ true_klass = true
+ true_klass.should be_kind_of(TrueClass)
+ true_klass.to_yaml.should match_yaml("--- true\n")
+ end
+
+ it "returns the YAML representation of a Error object" do
+ StandardError.new("foobar").to_yaml.should match_yaml("--- !ruby/exception:StandardError\nmessage: foobar\n")
+ end
+
+ it "returns the YAML representation for Range objects" do
+ yaml = Range.new(1,3).to_yaml
+ yaml.include?("!ruby/range").should be_true
+ yaml.include?("begin: 1").should be_true
+ yaml.include?("end: 3").should be_true
+ yaml.include?("excl: false").should be_true
+ end
+
+ it "returns the YAML representation of numeric constants" do
+ nan_value.to_yaml.downcase.should match_yaml("--- .nan\n")
+ infinity_value.to_yaml.downcase.should match_yaml("--- .inf\n")
+ (-infinity_value).to_yaml.downcase.should match_yaml("--- -.inf\n")
+ (0.0).to_yaml.should match_yaml("--- 0.0\n")
+ end
+
+ it "returns the YAML representation of an array of hashes" do
+ players = [{"a" => "b"}, {"b" => "c"}]
+ players.to_yaml.should match_yaml("--- \n- a: b\n- b: c\n")
+ end
+end
diff --git a/spec/ruby/library/yaml/transfer_spec.rb b/spec/ruby/library/yaml/transfer_spec.rb
new file mode 100644
index 0000000000..44c820940f
--- /dev/null
+++ b/spec/ruby/library/yaml/transfer_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
diff --git a/spec/ruby/library/yaml/try_implicit_spec.rb b/spec/ruby/library/yaml/try_implicit_spec.rb
new file mode 100644
index 0000000000..44c820940f
--- /dev/null
+++ b/spec/ruby/library/yaml/try_implicit_spec.rb
@@ -0,0 +1,2 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
diff --git a/spec/ruby/library/zlib/adler32_spec.rb b/spec/ruby/library/zlib/adler32_spec.rb
new file mode 100644
index 0000000000..1da12aece0
--- /dev/null
+++ b/spec/ruby/library/zlib/adler32_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../../spec_helper'
+require 'zlib'
+
+describe "Zlib.adler32" do
+ it "calculates Adler checksum for string" do
+ Zlib.adler32("").should == 1
+ Zlib.adler32(" ").should == 2162721
+ Zlib.adler32("123456789").should == 152961502
+ Zlib.adler32("!@#\{$\}%^&**()").should == 365495023
+ Zlib.adler32("to be or not to be" * 22).should == 3979904837
+ Zlib.adler32("0").should == 3211313
+ Zlib.adler32((2**32).to_s).should == 193331739
+ Zlib.adler32((2**64).to_s).should == 723452953
+ end
+
+ it "calculates Adler checksum for string and initial Adler value" do
+ test_string = "This is a test string! How exciting!%?"
+ Zlib.adler32(test_string, 0).should == 63900955
+ Zlib.adler32(test_string, 1).should == 66391324
+ Zlib.adler32(test_string, 2**8).should == 701435419
+ Zlib.adler32(test_string, 2**16).should == 63966491
+ lambda { Zlib.adler32(test_string, 2**128) }.should raise_error(RangeError)
+ end
+
+ it "calculates the Adler checksum for string and initial Adler value for Bignums" do
+ test_string = "This is a test string! How exciting!%?"
+ Zlib.adler32(test_string, 2**30).should == 1137642779
+ end
+
+ it "assumes that the initial value is given to adler, if adler is omitted" do
+ orig_crc = Zlib.adler32
+ Zlib.adler32("").should == Zlib.adler32("", orig_crc)
+ Zlib.adler32(" ").should == Zlib.adler32(" ", orig_crc)
+ Zlib.adler32("123456789").should == Zlib.adler32("123456789", orig_crc)
+ Zlib.adler32("!@#\{$\}%^&**()").should == Zlib.adler32("!@#\{$\}%^&**()", orig_crc)
+ Zlib.adler32("to be or not to be" * 22).should == Zlib.adler32("to be or not to be" * 22, orig_crc)
+ Zlib.adler32("0").should == Zlib.adler32("0", orig_crc)
+ Zlib.adler32((2**32).to_s).should == Zlib.adler32((2**32).to_s, orig_crc)
+ Zlib.adler32((2**64).to_s).should == Zlib.adler32((2**64).to_s, orig_crc)
+ end
+
+ it "it returns the CRC initial value, if string is omitted" do
+ Zlib.adler32.should == 1
+ end
+
+end
diff --git a/spec/ruby/library/zlib/crc32_spec.rb b/spec/ruby/library/zlib/crc32_spec.rb
new file mode 100644
index 0000000000..02f95fd92c
--- /dev/null
+++ b/spec/ruby/library/zlib/crc32_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+require 'zlib'
+
+describe "Zlib.crc32" do
+ it "calculates CRC checksum for string" do
+ Zlib.crc32("").should == 0
+ Zlib.crc32(" ").should == 3916222277
+ Zlib.crc32("123456789").should == 3421780262
+ Zlib.crc32("!@#\{$\}%^&**()").should == 2824518887
+ Zlib.crc32("to be or not to be" * 22).should == 1832379978
+ Zlib.crc32("0").should == 4108050209
+ Zlib.crc32((2**32).to_s).should == 3267533297
+ Zlib.crc32((2**64).to_s).should == 653721760
+ end
+
+ it "calculates CRC checksum for string and initial CRC value" do
+ test_string = "This is a test string! How exciting!%?"
+ # Zlib.crc32(test_string, -2**28).should == 3230195786
+ # Zlib.crc32(test_string, -2**20).should == 2770207303
+ # Zlib.crc32(test_string, -2**16).should == 2299432960
+ # Zlib.crc32(test_string, -2**8).should == 861809849
+ # Zlib.crc32(test_string, -1).should == 2170124077
+ Zlib.crc32(test_string, 0).should == 3864990561
+ Zlib.crc32(test_string, 1).should == 1809313411
+ Zlib.crc32(test_string, 2**8).should == 1722745982
+ Zlib.crc32(test_string, 2**16).should == 1932511220
+ Zlib.crc32("p", ~305419896).should == 4046865307
+ Zlib.crc32("p", -305419897).should == 4046865307
+ lambda { Zlib.crc32(test_string, 2**128) }.should raise_error(RangeError)
+ end
+
+ it "calculates the CRC checksum for string and initial CRC value for Bignums" do
+ test_string = "This is a test string! How exciting!%?"
+ # Zlib.crc32(test_string, -2**30).should == 277228695
+ Zlib.crc32(test_string, 2**30).should == 46597132
+ end
+
+ it "assumes that the initial value is given to crc, if crc is omitted" do
+ orig_crc = Zlib.crc32
+ Zlib.crc32("").should == Zlib.crc32("", orig_crc)
+ Zlib.crc32(" ").should == Zlib.crc32(" ", orig_crc)
+ Zlib.crc32("123456789").should == Zlib.crc32("123456789", orig_crc)
+ Zlib.crc32("!@#\{$\}%^&**()").should == Zlib.crc32("!@#\{$\}%^&**()", orig_crc)
+ Zlib.crc32("to be or not to be" * 22).should == Zlib.crc32("to be or not to be" * 22, orig_crc)
+ Zlib.crc32("0").should == Zlib.crc32("0", orig_crc)
+ Zlib.crc32((2**32).to_s).should == Zlib.crc32((2**32).to_s, orig_crc)
+ Zlib.crc32((2**64).to_s).should == Zlib.crc32((2**64).to_s, orig_crc)
+ end
+
+ it "it returns the CRC initial value, if string is omitted" do
+ Zlib.crc32.should == 0
+ end
+
+end
diff --git a/spec/ruby/library/zlib/crc_table_spec.rb b/spec/ruby/library/zlib/crc_table_spec.rb
new file mode 100644
index 0000000000..f7fc2749fa
--- /dev/null
+++ b/spec/ruby/library/zlib/crc_table_spec.rb
@@ -0,0 +1,75 @@
+require_relative '../../spec_helper'
+require "zlib"
+
+describe "Zlib.crc_table" do
+
+ it "returns the same value as zlib's get_crc_table()" do
+ Zlib.crc_table.should == [
+ 0, 1996959894, 3993919788, 2567524794,
+ 124634137, 1886057615, 3915621685, 2657392035,
+ 249268274, 2044508324, 3772115230, 2547177864,
+ 162941995, 2125561021, 3887607047, 2428444049,
+ 498536548, 1789927666, 4089016648, 2227061214,
+ 450548861, 1843258603, 4107580753, 2211677639,
+ 325883990, 1684777152, 4251122042, 2321926636,
+ 335633487, 1661365465, 4195302755, 2366115317,
+ 997073096, 1281953886, 3579855332, 2724688242,
+ 1006888145, 1258607687, 3524101629, 2768942443,
+ 901097722, 1119000684, 3686517206, 2898065728,
+ 853044451, 1172266101, 3705015759, 2882616665,
+ 651767980, 1373503546, 3369554304, 3218104598,
+ 565507253, 1454621731, 3485111705, 3099436303,
+ 671266974, 1594198024, 3322730930, 2970347812,
+ 795835527, 1483230225, 3244367275, 3060149565,
+ 1994146192, 31158534, 2563907772, 4023717930,
+ 1907459465, 112637215, 2680153253, 3904427059,
+ 2013776290, 251722036, 2517215374, 3775830040,
+ 2137656763, 141376813, 2439277719, 3865271297,
+ 1802195444, 476864866, 2238001368, 4066508878,
+ 1812370925, 453092731, 2181625025, 4111451223,
+ 1706088902, 314042704, 2344532202, 4240017532,
+ 1658658271, 366619977, 2362670323, 4224994405,
+ 1303535960, 984961486, 2747007092, 3569037538,
+ 1256170817, 1037604311, 2765210733, 3554079995,
+ 1131014506, 879679996, 2909243462, 3663771856,
+ 1141124467, 855842277, 2852801631, 3708648649,
+ 1342533948, 654459306, 3188396048, 3373015174,
+ 1466479909, 544179635, 3110523913, 3462522015,
+ 1591671054, 702138776, 2966460450, 3352799412,
+ 1504918807, 783551873, 3082640443, 3233442989,
+ 3988292384, 2596254646, 62317068, 1957810842,
+ 3939845945, 2647816111, 81470997, 1943803523,
+ 3814918930, 2489596804, 225274430, 2053790376,
+ 3826175755, 2466906013, 167816743, 2097651377,
+ 4027552580, 2265490386, 503444072, 1762050814,
+ 4150417245, 2154129355, 426522225, 1852507879,
+ 4275313526, 2312317920, 282753626, 1742555852,
+ 4189708143, 2394877945, 397917763, 1622183637,
+ 3604390888, 2714866558, 953729732, 1340076626,
+ 3518719985, 2797360999, 1068828381, 1219638859,
+ 3624741850, 2936675148, 906185462, 1090812512,
+ 3747672003, 2825379669, 829329135, 1181335161,
+ 3412177804, 3160834842, 628085408, 1382605366,
+ 3423369109, 3138078467, 570562233, 1426400815,
+ 3317316542, 2998733608, 733239954, 1555261956,
+ 3268935591, 3050360625, 752459403, 1541320221,
+ 2607071920, 3965973030, 1969922972, 40735498,
+ 2617837225, 3943577151, 1913087877, 83908371,
+ 2512341634, 3803740692, 2075208622, 213261112,
+ 2463272603, 3855990285, 2094854071, 198958881,
+ 2262029012, 4057260610, 1759359992, 534414190,
+ 2176718541, 4139329115, 1873836001, 414664567,
+ 2282248934, 4279200368, 1711684554, 285281116,
+ 2405801727, 4167216745, 1634467795, 376229701,
+ 2685067896, 3608007406, 1308918612, 956543938,
+ 2808555105, 3495958263, 1231636301, 1047427035,
+ 2932959818, 3654703836, 1088359270, 936918000,
+ 2847714899, 3736837829, 1202900863, 817233897,
+ 3183342108, 3401237130, 1404277552, 615818150,
+ 3134207493, 3453421203, 1423857449, 601450431,
+ 3009837614, 3294710456, 1567103746, 711928724,
+ 3020668471, 3272380065, 1510334235, 755167117,
+ ]
+ end
+
+end
diff --git a/spec/ruby/library/zlib/deflate/append_spec.rb b/spec/ruby/library/zlib/deflate/append_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/deflate/append_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/deflate/deflate_spec.rb b/spec/ruby/library/zlib/deflate/deflate_spec.rb
new file mode 100644
index 0000000000..828880f8d8
--- /dev/null
+++ b/spec/ruby/library/zlib/deflate/deflate_spec.rb
@@ -0,0 +1,128 @@
+require 'zlib'
+require_relative '../../../spec_helper'
+
+describe "Zlib::Deflate.deflate" do
+ it "deflates some data" do
+ data = Array.new(10,0).pack('C*')
+
+ zipped = Zlib::Deflate.deflate data
+
+ zipped.should == [120, 156, 99, 96, 128, 1, 0, 0, 10, 0, 1].pack('C*')
+ end
+
+ it "deflates lots of data" do
+ data = "\000" * 32 * 1024
+
+ zipped = Zlib::Deflate.deflate data
+
+ zipped.should == ([120, 156, 237, 193, 1, 1, 0, 0] +
+ [0, 128, 144, 254, 175, 238, 8, 10] +
+ Array.new(31, 0) +
+ [24, 128, 0, 0, 1]).pack('C*')
+ end
+
+ it "deflates chunked data" do
+ random_generator = Random.new(0)
+ deflated = ''
+
+ Zlib::Deflate.deflate(random_generator.bytes(20000)) do |chunk|
+ deflated << chunk
+ end
+
+ deflated.length.should == 20016
+ end
+end
+
+describe "Zlib::Deflate#deflate" do
+ before :each do
+ @deflator = Zlib::Deflate.new
+ end
+
+ it "deflates some data" do
+ data = "\000" * 10
+
+ zipped = @deflator.deflate data, Zlib::FINISH
+ @deflator.finish
+
+ zipped.should == [120, 156, 99, 96, 128, 1, 0, 0, 10, 0, 1].pack('C*')
+ end
+
+ it "deflates lots of data" do
+ data = "\000" * 32 * 1024
+
+ zipped = @deflator.deflate data, Zlib::FINISH
+ @deflator.finish
+
+ zipped.should == ([120, 156, 237, 193, 1, 1, 0, 0] +
+ [0, 128, 144, 254, 175, 238, 8, 10] +
+ Array.new(31, 0) +
+ [24, 128, 0, 0, 1]).pack('C*')
+ end
+end
+
+describe "Zlib::Deflate#deflate" do
+
+ before :each do
+ @deflator = Zlib::Deflate.new
+ @random_generator = Random.new(0)
+ @original = ''
+ @chunks = []
+ end
+
+ describe "without break" do
+
+ before do
+ 2.times do
+ @input = @random_generator.bytes(20000)
+ @original << @input
+ @deflator.deflate(@input) do |chunk|
+ @chunks << chunk
+ end
+ end
+ end
+
+ it "deflates chunked data" do
+ @deflator.finish
+ @chunks.map { |chunk| chunk.length }.should == [16384, 16384]
+ end
+
+ it "deflates chunked data with final chunk" do
+ final = @deflator.finish
+ final.length.should == 7253
+ end
+
+ it "deflates chunked data without errors" do
+ final = @deflator.finish
+ @chunks << final
+ @original.should == Zlib.inflate(@chunks.join)
+ end
+
+ end
+
+ describe "with break" do
+ before :each do
+ @input = @random_generator.bytes(20000)
+ @deflator.deflate(@input) do |chunk|
+ @chunks << chunk
+ break
+ end
+ end
+
+ it "deflates only first chunk" do
+ @deflator.finish
+ @chunks.map { |chunk| chunk.length }.should == [16384]
+ end
+
+ it "deflates chunked data with final chunk" do
+ final = @deflator.finish
+ final.length.should == 3632
+ end
+
+ it "deflates chunked data without errors" do
+ final = @deflator.finish
+ @chunks << final
+ @input.should == Zlib.inflate(@chunks.join)
+ end
+
+ end
+end
diff --git a/spec/ruby/library/zlib/deflate/flush_spec.rb b/spec/ruby/library/zlib/deflate/flush_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/deflate/flush_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/deflate/new_spec.rb b/spec/ruby/library/zlib/deflate/new_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/deflate/new_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/deflate/params_spec.rb b/spec/ruby/library/zlib/deflate/params_spec.rb
new file mode 100644
index 0000000000..0b1cca8c8a
--- /dev/null
+++ b/spec/ruby/library/zlib/deflate/params_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+require 'zlib'
+
+describe "Zlib::Deflate#params" do
+ it "changes the deflate parameters" do
+ data = 'abcdefghijklm'
+
+ d = Zlib::Deflate.new Zlib::NO_COMPRESSION, Zlib::MAX_WBITS,
+ Zlib::DEF_MEM_LEVEL, Zlib::DEFAULT_STRATEGY
+
+ d << data.slice!(0..10)
+ d.params Zlib::BEST_COMPRESSION, Zlib::DEFAULT_STRATEGY
+ d << data
+
+ Zlib::Inflate.inflate(d.finish).should == 'abcdefghijklm'
+ end
+end
diff --git a/spec/ruby/library/zlib/deflate/set_dictionary_spec.rb b/spec/ruby/library/zlib/deflate/set_dictionary_spec.rb
new file mode 100644
index 0000000000..0e461229c7
--- /dev/null
+++ b/spec/ruby/library/zlib/deflate/set_dictionary_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require 'zlib'
+
+describe "Zlib::Deflate#set_dictionary" do
+ it "sets the dictionary" do
+ d = Zlib::Deflate.new
+ d.set_dictionary 'aaaaaaaaaa'
+ d << 'abcdefghij'
+
+ d.finish.should == [120, 187, 20, 225, 3, 203, 75, 76,
+ 74, 78, 73, 77, 75, 207, 200, 204,
+ 2, 0, 21, 134, 3, 248].pack('C*')
+ end
+end
diff --git a/spec/ruby/library/zlib/deflate_spec.rb b/spec/ruby/library/zlib/deflate_spec.rb
new file mode 100644
index 0000000000..01538dd4e6
--- /dev/null
+++ b/spec/ruby/library/zlib/deflate_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require "zlib"
+
+describe "Zlib#deflate" do
+ it "deflates some data" do
+ Zlib.deflate("1" * 10).should == [120, 156, 51, 52, 132, 1, 0, 10, 145, 1, 235].pack('C*')
+ end
+end
diff --git a/spec/ruby/library/zlib/gzipfile/close_spec.rb b/spec/ruby/library/zlib/gzipfile/close_spec.rb
new file mode 100644
index 0000000000..27eb34cdd9
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipfile/close_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe "Zlib::GzipFile#close" do
+ it "finishes the stream and closes the io" do
+ io = StringIO.new "".b
+ Zlib::GzipWriter.wrap io do |gzio|
+ gzio.close
+
+ gzio.closed?.should == true
+
+ lambda { gzio.orig_name }.should \
+ raise_error(Zlib::GzipFile::Error, 'closed gzip stream')
+ lambda { gzio.comment }.should \
+ raise_error(Zlib::GzipFile::Error, 'closed gzip stream')
+ end
+
+ io.string[10..-1].should == ([3] + Array.new(9,0)).pack('C*')
+ end
+end
diff --git a/spec/ruby/library/zlib/gzipfile/closed_spec.rb b/spec/ruby/library/zlib/gzipfile/closed_spec.rb
new file mode 100644
index 0000000000..b885add485
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipfile/closed_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe "Zlib::GzipFile#closed?" do
+ it "returns the closed status" do
+ io = StringIO.new
+ Zlib::GzipWriter.wrap io do |gzio|
+ gzio.closed?.should == false
+
+ gzio.close
+
+ gzio.closed?.should == true
+ end
+ end
+end
diff --git a/spec/ruby/library/zlib/gzipfile/comment_spec.rb b/spec/ruby/library/zlib/gzipfile/comment_spec.rb
new file mode 100644
index 0000000000..7493f74f1c
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipfile/comment_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe "Zlib::GzipFile#comment" do
+ before :each do
+ @io = StringIO.new
+ end
+
+ it "returns the name" do
+ Zlib::GzipWriter.wrap @io do |gzio|
+ gzio.comment = 'name'
+
+ gzio.comment.should == 'name'
+ end
+ end
+
+ it "raises an error on a closed stream" do
+ Zlib::GzipWriter.wrap @io do |gzio|
+ gzio.close
+
+ lambda { gzio.comment }.should \
+ raise_error(Zlib::GzipFile::Error, 'closed gzip stream')
+ end
+ end
+end
diff --git a/spec/ruby/library/zlib/gzipfile/crc_spec.rb b/spec/ruby/library/zlib/gzipfile/crc_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipfile/crc_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipfile/finish_spec.rb b/spec/ruby/library/zlib/gzipfile/finish_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipfile/finish_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipfile/level_spec.rb b/spec/ruby/library/zlib/gzipfile/level_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipfile/level_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipfile/mtime_spec.rb b/spec/ruby/library/zlib/gzipfile/mtime_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipfile/mtime_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipfile/orig_name_spec.rb b/spec/ruby/library/zlib/gzipfile/orig_name_spec.rb
new file mode 100644
index 0000000000..cae5305355
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipfile/orig_name_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe "Zlib::GzipFile#orig_name" do
+ before :each do
+ @io = StringIO.new
+ end
+
+ it "returns the name" do
+ Zlib::GzipWriter.wrap @io do |gzio|
+ gzio.orig_name = 'name'
+
+ gzio.orig_name.should == 'name'
+ end
+ end
+
+ it "raises an error on a closed stream" do
+ Zlib::GzipWriter.wrap @io do |gzio|
+ gzio.close
+
+ lambda { gzio.orig_name }.should \
+ raise_error(Zlib::GzipFile::Error, 'closed gzip stream')
+ end
+ end
+end
diff --git a/spec/ruby/library/zlib/gzipfile/os_code_spec.rb b/spec/ruby/library/zlib/gzipfile/os_code_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipfile/os_code_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipfile/sync_spec.rb b/spec/ruby/library/zlib/gzipfile/sync_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipfile/sync_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipfile/to_io_spec.rb b/spec/ruby/library/zlib/gzipfile/to_io_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipfile/to_io_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipfile/wrap_spec.rb b/spec/ruby/library/zlib/gzipfile/wrap_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipfile/wrap_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipreader/each_byte_spec.rb b/spec/ruby/library/zlib/gzipreader/each_byte_spec.rb
new file mode 100644
index 0000000000..d2c43ecaa4
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/each_byte_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe "GzipReader#each_byte" do
+
+ before :each do
+ @data = '12345abcde'
+ @zip = [31, 139, 8, 0, 44, 220, 209, 71, 0, 3, 51, 52, 50, 54, 49, 77,
+ 76, 74, 78, 73, 5, 0, 157, 5, 0, 36, 10, 0, 0, 0].pack('C*')
+
+ @io = StringIO.new @zip
+ ScratchPad.clear
+ end
+
+ it "calls the given block for each byte in the stream, passing the byte as an argument" do
+ gz = Zlib::GzipReader.new @io
+
+ ScratchPad.record []
+ gz.each_byte { |b| ScratchPad << b }
+
+ ScratchPad.recorded.should == [49, 50, 51, 52, 53, 97, 98, 99, 100, 101]
+ end
+
+ it "returns an enumerator, which yields each byte in the stream, when no block is passed" do
+ gz = Zlib::GzipReader.new @io
+ enum = gz.each_byte
+
+ ScratchPad.record []
+ while true
+ begin
+ ScratchPad << enum.next
+ rescue StopIteration
+ break
+ end
+ end
+
+ ScratchPad.recorded.should == [49, 50, 51, 52, 53, 97, 98, 99, 100, 101]
+ end
+
+ it "increments position before calling the block" do
+ gz = Zlib::GzipReader.new @io
+
+ i = 1
+ gz.each_byte do |ignore|
+ gz.pos.should == i
+ i += 1
+ end
+ end
+
+end
diff --git a/spec/ruby/library/zlib/gzipreader/each_line_spec.rb b/spec/ruby/library/zlib/gzipreader/each_line_spec.rb
new file mode 100644
index 0000000000..b576788e27
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/each_line_spec.rb
@@ -0,0 +1,5 @@
+require_relative 'shared/each'
+
+describe "GzipReader#each_line" do
+ it_behaves_like :gzipreader_each, :each_line
+end
diff --git a/spec/ruby/library/zlib/gzipreader/each_spec.rb b/spec/ruby/library/zlib/gzipreader/each_spec.rb
new file mode 100644
index 0000000000..f9b90be316
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/each_spec.rb
@@ -0,0 +1,5 @@
+require_relative 'shared/each'
+
+describe "GzipReader#each" do
+ it_behaves_like :gzipreader_each, :each
+end
diff --git a/spec/ruby/library/zlib/gzipreader/eof_spec.rb b/spec/ruby/library/zlib/gzipreader/eof_spec.rb
new file mode 100644
index 0000000000..e33d8e3133
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/eof_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe "GzipReader#eof?" do
+
+ before :each do
+ @data = '{"a":1234}'
+ @zip = [31, 139, 8, 0, 0, 0, 0, 0, 0, 3, 171, 86, 74, 84, 178, 50,
+ 52, 50, 54, 169, 5, 0, 196, 20, 118, 213, 10, 0, 0, 0].pack('C*')
+ @io = StringIO.new @zip
+ end
+
+ it "returns true when at EOF" do
+ gz = Zlib::GzipReader.new @io
+ gz.eof?.should be_false
+ gz.read
+ gz.eof?.should be_true
+ end
+
+ it "returns true when at EOF with the exact length of uncompressed data" do
+ gz = Zlib::GzipReader.new @io
+ gz.eof?.should be_false
+ gz.read(10)
+ gz.eof?.should be_true
+ end
+
+ it "returns true when at EOF with a length greater than the size of uncompressed data" do
+ gz = Zlib::GzipReader.new @io
+ gz.eof?.should be_false
+ gz.read(11)
+ gz.eof?.should be_true
+ end
+
+ it "returns false when at EOF when there's data left in the buffer to read" do
+ gz = Zlib::GzipReader.new @io
+ gz.read(9)
+ gz.eof?.should be_false
+ gz.read
+ gz.eof?.should be_true
+ end
+
+ # This is especially important for JRuby, since eof? there
+ # is more than just a simple accessor.
+ it "does not affect the reading data" do
+ gz = Zlib::GzipReader.new @io
+ 0.upto(9) do |i|
+ gz.eof?.should be_false
+ gz.read(1).should == @data[i, 1]
+ end
+ gz.eof?.should be_true
+ gz.read().should == ""
+ gz.eof?.should be_true
+ end
+
+end
diff --git a/spec/ruby/library/zlib/gzipreader/getc_spec.rb b/spec/ruby/library/zlib/gzipreader/getc_spec.rb
new file mode 100644
index 0000000000..90b5ffe37a
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/getc_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe "GzipReader#getc" do
+
+ before :each do
+ @data = '12345abcde'
+ @zip = [31, 139, 8, 0, 44, 220, 209, 71, 0, 3, 51, 52, 50, 54, 49, 77,
+ 76, 74, 78, 73, 5, 0, 157, 5, 0, 36, 10, 0, 0, 0].pack('C*')
+ @io = StringIO.new @zip
+ end
+
+ it "returns the next character from the stream" do
+ gz = Zlib::GzipReader.new @io
+ gz.pos.should == 0
+
+ gz.getc.should == '1'
+ gz.getc.should == '2'
+ gz.getc.should == '3'
+ gz.getc.should == '4'
+ gz.getc.should == '5'
+ end
+
+ it "increments position" do
+ gz = Zlib::GzipReader.new @io
+ (0..@data.size).each do |i|
+ gz.pos.should == i
+ gz.getc
+ end
+ end
+
+ it "returns nil at the end of the stream" do
+ gz = Zlib::GzipReader.new @io
+ gz.read
+ pos = gz.pos
+ gz.getc.should be_nil
+ gz.pos.should == pos
+ end
+
+end
diff --git a/spec/ruby/library/zlib/gzipreader/gets_spec.rb b/spec/ruby/library/zlib/gzipreader/gets_spec.rb
new file mode 100644
index 0000000000..8e4465e49c
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/gets_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../../../spec_helper'
+require 'zlib'
+require 'stringio'
+
+describe 'GzipReader#gets' do
+ describe 'with "" separator' do
+ it 'reads paragraphs skipping newlines' do
+ # gz contains "\n\n\n\n\n123\n45\n\n\n\n\nabc\nde\n\n\n\n\n"
+ gz = Zlib::GzipReader.new(
+ StringIO.new(
+ [31, 139, 8, 0, 223, 152, 48, 89, 0, 3, 227, 226, 2, 2, 67, 35,
+ 99, 46, 19, 83, 16, 139, 43, 49, 41, 153, 43, 37, 21, 204, 4, 0,
+ 32, 119, 45, 184, 27, 0, 0, 0].pack('C*')
+ )
+ )
+
+ gz.gets('').should == "123\n45\n\n"
+ gz.gets('').should == "abc\nde\n\n"
+ gz.eof?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/library/zlib/gzipreader/lineno_spec.rb b/spec/ruby/library/zlib/gzipreader/lineno_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/lineno_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipreader/new_spec.rb b/spec/ruby/library/zlib/gzipreader/new_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/new_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipreader/open_spec.rb b/spec/ruby/library/zlib/gzipreader/open_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/open_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipreader/pos_spec.rb b/spec/ruby/library/zlib/gzipreader/pos_spec.rb
new file mode 100644
index 0000000000..df7c78ad8f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/pos_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe "GzipReader#pos" do
+
+ before :each do
+ @data = '12345abcde'
+ @zip = [31, 139, 8, 0, 44, 220, 209, 71, 0, 3, 51, 52, 50, 54, 49, 77,
+ 76, 74, 78, 73, 5, 0, 157, 5, 0, 36, 10, 0, 0, 0].pack('C*')
+ @io = StringIO.new @zip
+ end
+
+ it "returns the position" do
+ gz = Zlib::GzipReader.new @io
+
+ gz.pos.should == 0
+
+ gz.read 5
+ gz.pos.should == 5
+
+ gz.read
+ gz.pos.should == @data.length
+ end
+
+end
diff --git a/spec/ruby/library/zlib/gzipreader/read_spec.rb b/spec/ruby/library/zlib/gzipreader/read_spec.rb
new file mode 100644
index 0000000000..a90b2bf498
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/read_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe "GzipReader#read" do
+
+ before :each do
+ @data = '12345abcde'
+ @zip = [31, 139, 8, 0, 44, 220, 209, 71, 0, 3, 51, 52, 50, 54, 49, 77,
+ 76, 74, 78, 73, 5, 0, 157, 5, 0, 36, 10, 0, 0, 0].pack('C*')
+ @io = StringIO.new @zip
+ end
+
+ it "with no arguments reads the entire content of a gzip file" do
+ gz = Zlib::GzipReader.new @io
+ gz.read.should == @data
+ end
+
+ it "with nil length argument reads the entire content of a gzip file" do
+ gz = Zlib::GzipReader.new @io
+ gz.read(nil).should == @data
+ end
+
+ it "reads the contents up to a certain size" do
+ gz = Zlib::GzipReader.new @io
+ gz.read(5).should == @data[0...5]
+ gz.read(5).should == @data[5...10]
+ end
+
+ it "does not accept a negative length to read" do
+ gz = Zlib::GzipReader.new @io
+ lambda {
+ gz.read(-1)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "returns an empty string if a 0 length is given" do
+ gz = Zlib::GzipReader.new @io
+ gz.read(0).should == ""
+ end
+
+ it "respects :external_encoding option" do
+ gz = Zlib::GzipReader.new(@io, external_encoding: 'UTF-8')
+ gz.read.encoding.should == Encoding::UTF_8
+
+ @io.rewind
+ gz = Zlib::GzipReader.new(@io, external_encoding: 'UTF-16LE')
+ gz.read.encoding.should == Encoding::UTF_16LE
+ end
+
+ describe "at the end of data" do
+ it "returns empty string if length prameter is not specified or 0" do
+ gz = Zlib::GzipReader.new @io
+ gz.read # read till the end
+ gz.read(0).should == ""
+ gz.read().should == ""
+ gz.read(nil).should == ""
+ end
+
+ it "returns nil if length prameter is positive" do
+ gz = Zlib::GzipReader.new @io
+ gz.read # read till the end
+ gz.read(1).should be_nil
+ gz.read(2**16).should be_nil
+ end
+ end
+
+end
diff --git a/spec/ruby/library/zlib/gzipreader/readchar_spec.rb b/spec/ruby/library/zlib/gzipreader/readchar_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/readchar_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipreader/readline_spec.rb b/spec/ruby/library/zlib/gzipreader/readline_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/readline_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipreader/readlines_spec.rb b/spec/ruby/library/zlib/gzipreader/readlines_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/readlines_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipreader/readpartial_spec.rb b/spec/ruby/library/zlib/gzipreader/readpartial_spec.rb
new file mode 100644
index 0000000000..a367e5b856
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/readpartial_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe 'GzipReader#readpartial' do
+ before :each do
+ @data = '12345abcde'
+ @zip = [31, 139, 8, 0, 44, 220, 209, 71, 0, 3, 51, 52, 50, 54, 49, 77,
+ 76, 74, 78, 73, 5, 0, 157, 5, 0, 36, 10, 0, 0, 0].pack('C*')
+ @io = StringIO.new(@zip)
+ end
+
+ it 'accepts nil buffer' do
+ gz = Zlib::GzipReader.new(@io)
+ gz.readpartial(5, nil).should == '12345'
+ end
+end
diff --git a/spec/ruby/library/zlib/gzipreader/rewind_spec.rb b/spec/ruby/library/zlib/gzipreader/rewind_spec.rb
new file mode 100644
index 0000000000..2e22458b43
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/rewind_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe "GzipReader#rewind" do
+
+ before :each do
+ @data = '12345abcde'
+ @zip = [31, 139, 8, 0, 44, 220, 209, 71, 0, 3, 51, 52, 50, 54, 49, 77,
+ 76, 74, 78, 73, 5, 0, 157, 5, 0, 36, 10, 0, 0, 0].pack('C*')
+ @io = StringIO.new @zip
+ ScratchPad.clear
+ end
+
+ it "resets the position of the stream pointer" do
+ gz = Zlib::GzipReader.new @io
+ gz.read
+ gz.pos.should == @data.length
+
+ gz.rewind
+ gz.pos.should == 0
+ gz.lineno.should == 0
+ end
+
+ it "resets the position of the stream pointer to data previously read" do
+ gz = Zlib::GzipReader.new @io
+ first_read = gz.read
+ gz.rewind
+ first_read.should == gz.read
+ end
+
+ it "invokes seek method on the associated IO object" do
+ # first, prepare the mock object:
+ (obj = mock("io")).should_receive(:get_io).any_number_of_times.and_return(@io)
+ def obj.read(args); get_io.read(args); end
+ def obj.seek(pos, whence = 0)
+ ScratchPad.record :seek
+ get_io.seek(pos, whence)
+ end
+
+ gz = Zlib::GzipReader.new(obj)
+ gz.rewind()
+
+ ScratchPad.recorded.should == :seek
+ gz.pos.should == 0
+ gz.read.should == "12345abcde"
+ end
+end
diff --git a/spec/ruby/library/zlib/gzipreader/shared/each.rb b/spec/ruby/library/zlib/gzipreader/shared/each.rb
new file mode 100644
index 0000000000..11c15d8a66
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/shared/each.rb
@@ -0,0 +1,51 @@
+require_relative '../../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe :gzipreader_each, shared: true do
+
+ before :each do
+ @data = "firstline\nsecondline\n\nforthline"
+ @zip = [31, 139, 8, 0, 244, 125, 128, 88, 2, 255, 75, 203, 44, 42, 46, 201,
+ 201, 204, 75, 229, 42, 78, 77, 206, 207, 75, 1, 51, 185, 210,242,
+ 139, 74, 50, 64, 76, 0, 180, 54, 61, 111, 31, 0, 0, 0].pack('C*')
+
+ @io = StringIO.new @zip
+ @gzreader = Zlib::GzipReader.new @io
+ end
+
+ after :each do
+ ScratchPad.clear
+ end
+
+ it "calls the given block for each line in the stream, passing the line as an argument" do
+ ScratchPad.record []
+ @gzreader.send(@method) { |b| ScratchPad << b }
+
+ ScratchPad.recorded.should == ["firstline\n", "secondline\n", "\n", "forthline"]
+ end
+
+ it "returns an enumerator, which yields each byte in the stream, when no block is passed" do
+ enum = @gzreader.send(@method)
+
+ ScratchPad.record []
+ while true
+ begin
+ ScratchPad << enum.next
+ rescue StopIteration
+ break
+ end
+ end
+
+ ScratchPad.recorded.should == ["firstline\n", "secondline\n", "\n", "forthline"]
+ end
+
+ it "increments position before calling the block" do
+ i = 0
+ @gzreader.send(@method) do |line|
+ i += line.length
+ @gzreader.pos.should == i
+ end
+ end
+
+end
diff --git a/spec/ruby/library/zlib/gzipreader/tell_spec.rb b/spec/ruby/library/zlib/gzipreader/tell_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/tell_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipreader/ungetbyte_spec.rb b/spec/ruby/library/zlib/gzipreader/ungetbyte_spec.rb
new file mode 100644
index 0000000000..f29927e20b
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/ungetbyte_spec.rb
@@ -0,0 +1,122 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe 'GzipReader#ungetbyte' do
+ before :each do
+ @data = '12345abcde'
+ @zip = [31, 139, 8, 0, 44, 220, 209, 71, 0, 3, 51, 52, 50, 54, 49, 77,
+ 76, 74, 78, 73, 5, 0, 157, 5, 0, 36, 10, 0, 0, 0].pack('C*')
+ @io = StringIO.new @zip
+ end
+
+ describe 'at the start of the stream' do
+ before :each do
+ @gz = Zlib::GzipReader.new(@io)
+ end
+
+ describe 'with an integer' do
+ it 'prepends the byte to the stream' do
+ @gz.ungetbyte 0x21
+ @gz.read.should == '!12345abcde'
+ end
+
+ ruby_bug "#13616", ""..."2.6" do
+ it 'decrements pos' do
+ @gz.ungetbyte 0x21
+ @gz.pos.should == -1
+ end
+ end
+ end
+
+ quarantine! do # https://bugs.ruby-lang.org/issues/13675
+ describe 'with nil' do
+ it 'does not prepend anything to the stream' do
+ @gz.ungetbyte nil
+ @gz.read.should == '12345abcde'
+ end
+
+ it 'does not decrement pos' do
+ @gz.ungetbyte nil
+ @gz.pos.should == 0
+ end
+ end
+ end
+ end
+
+ describe 'in the middle of the stream' do
+ before :each do
+ @gz = Zlib::GzipReader.new(@io)
+ @gz.read 5
+ end
+
+ describe 'with an integer' do
+ it 'inserts the corresponding character into the stream' do
+ @gz.ungetbyte 0x21
+ @gz.read.should == '!abcde'
+ end
+
+ it 'decrements pos' do
+ @gz.ungetbyte 0x21
+ @gz.pos.should == 4
+ end
+ end
+
+ quarantine! do # https://bugs.ruby-lang.org/issues/13675
+ describe 'with nil' do
+ it 'does not insert anything into the stream' do
+ @gz.ungetbyte nil
+ @gz.read.should == 'abcde'
+ end
+
+ it 'does not decrement pos' do
+ @gz.ungetbyte nil
+ @gz.pos.should == 5
+ end
+ end
+ end
+ end
+
+ describe 'at the end of the stream' do
+ before :each do
+ @gz = Zlib::GzipReader.new(@io)
+ @gz.read
+ end
+
+ describe 'with an integer' do
+ it 'appends the corresponding character to the stream' do
+ @gz.ungetbyte 0x21
+ @gz.read.should == '!'
+ end
+
+ it 'decrements pos' do
+ @gz.ungetbyte 0x21
+ @gz.pos.should == 9
+ end
+
+ it 'makes eof? false' do
+ @gz.ungetbyte 0x21
+ @gz.eof?.should be_false
+ end
+ end
+
+ quarantine! do # https://bugs.ruby-lang.org/issues/13675
+ describe 'with nil' do
+ it 'does not append anything to the stream' do
+ @gz.ungetbyte nil
+ @gz.read.should == ''
+ end
+
+ it 'does not decrement pos' do
+ @gz.ungetbyte nil
+ @gz.pos.should == 10
+ end
+
+ it 'does not make eof? false' do
+ @gz.ungetbyte nil
+ @gz.eof?.should be_true
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/zlib/gzipreader/ungetc_spec.rb b/spec/ruby/library/zlib/gzipreader/ungetc_spec.rb
new file mode 100644
index 0000000000..d749d58cca
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/ungetc_spec.rb
@@ -0,0 +1,292 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe 'GzipReader#ungetc' do
+ before :each do
+ @data = '12345abcde'
+ @zip = [31, 139, 8, 0, 44, 220, 209, 71, 0, 3, 51, 52, 50, 54, 49, 77,
+ 76, 74, 78, 73, 5, 0, 157, 5, 0, 36, 10, 0, 0, 0].pack('C*')
+ @io = StringIO.new @zip
+ end
+
+ describe 'at the start of the stream' do
+ before :each do
+ @gz = Zlib::GzipReader.new(@io, external_encoding: Encoding::UTF_8)
+ end
+
+ describe 'with a single-byte character' do
+ it 'prepends the character to the stream' do
+ @gz.ungetc 'x'
+ @gz.read.should == 'x12345abcde'
+ end
+
+ ruby_bug "#13616", ""..."2.6" do
+ it 'decrements pos' do
+ @gz.ungetc 'x'
+ @gz.pos.should == -1
+ end
+ end
+ end
+
+ describe 'with a multi-byte character' do
+ it 'prepends the character to the stream' do
+ @gz.ungetc 'Å·'
+ @gz.read.should == 'Å·12345abcde'
+ end
+
+ ruby_bug "#13616", ""..."2.6" do
+ it 'decrements pos' do
+ @gz.ungetc 'Å·'
+ @gz.pos.should == -2
+ end
+ end
+ end
+
+ describe 'with a multi-character string' do
+ it 'prepends the characters to the stream' do
+ @gz.ungetc 'xŷž'
+ @gz.read.should == 'xŷž12345abcde'
+ end
+
+ ruby_bug "#13616", ""..."2.6" do
+ it 'decrements pos' do
+ @gz.ungetc 'xŷž'
+ @gz.pos.should == -5
+ end
+ end
+ end
+
+ describe 'with an integer' do
+ it 'prepends the corresponding character to the stream' do
+ @gz.ungetc 0x21
+ @gz.read.should == '!12345abcde'
+ end
+
+ ruby_bug "#13616", ""..."2.6" do
+ it 'decrements pos' do
+ @gz.ungetc 0x21
+ @gz.pos.should == -1
+ end
+ end
+ end
+
+ describe 'with an empty string' do
+ it 'does not prepend anything to the stream' do
+ @gz.ungetc ''
+ @gz.read.should == '12345abcde'
+ end
+
+ it 'does not decrement pos' do
+ @gz.ungetc ''
+ @gz.pos.should == 0
+ end
+ end
+
+ quarantine! do # https://bugs.ruby-lang.org/issues/13675
+ describe 'with nil' do
+ it 'does not prepend anything to the stream' do
+ @gz.ungetc nil
+ @gz.read.should == '12345abcde'
+ end
+
+ it 'does not decrement pos' do
+ @gz.ungetc nil
+ @gz.pos.should == 0
+ end
+ end
+ end
+ end
+
+ describe 'in the middle of the stream' do
+ before :each do
+ @gz = Zlib::GzipReader.new(@io, external_encoding: Encoding::UTF_8)
+ @gz.read 5
+ end
+
+ describe 'with a single-byte character' do
+ it 'inserts the character into the stream' do
+ @gz.ungetc 'x'
+ @gz.read.should == 'xabcde'
+ end
+
+ it 'decrements pos' do
+ @gz.ungetc 'x'
+ @gz.pos.should == 4
+ end
+ end
+
+ describe 'with a multi-byte character' do
+ it 'inserts the character into the stream' do
+ @gz.ungetc 'Å·'
+ @gz.read.should == 'Å·abcde'
+ end
+
+ it 'decrements pos' do
+ @gz.ungetc 'Å·'
+ @gz.pos.should == 3
+ end
+ end
+
+ describe 'with a multi-character string' do
+ it 'inserts the characters into the stream' do
+ @gz.ungetc 'xŷž'
+ @gz.read.should == 'xŷžabcde'
+ end
+
+ it 'decrements pos' do
+ @gz.ungetc 'xŷž'
+ @gz.pos.should == 0
+ end
+ end
+
+ describe 'with an integer' do
+ it 'inserts the corresponding character into the stream' do
+ @gz.ungetc 0x21
+ @gz.read.should == '!abcde'
+ end
+
+ it 'decrements pos' do
+ @gz.ungetc 0x21
+ @gz.pos.should == 4
+ end
+ end
+
+ describe 'with an empty string' do
+ it 'does not insert anything into the stream' do
+ @gz.ungetc ''
+ @gz.read.should == 'abcde'
+ end
+
+ it 'does not decrement pos' do
+ @gz.ungetc ''
+ @gz.pos.should == 5
+ end
+ end
+
+ quarantine! do # https://bugs.ruby-lang.org/issues/13675
+ describe 'with nil' do
+ it 'does not insert anything into the stream' do
+ @gz.ungetc nil
+ @gz.read.should == 'abcde'
+ end
+
+ it 'does not decrement pos' do
+ @gz.ungetc nil
+ @gz.pos.should == 5
+ end
+ end
+ end
+ end
+
+ describe 'at the end of the stream' do
+ before :each do
+ @gz = Zlib::GzipReader.new(@io, external_encoding: Encoding::UTF_8)
+ @gz.read
+ end
+
+ describe 'with a single-byte character' do
+ it 'appends the character to the stream' do
+ @gz.ungetc 'x'
+ @gz.read.should == 'x'
+ end
+
+ it 'decrements pos' do
+ @gz.ungetc 'x'
+ @gz.pos.should == 9
+ end
+
+ it 'makes eof? false' do
+ @gz.ungetc 'x'
+ @gz.eof?.should be_false
+ end
+ end
+
+ describe 'with a multi-byte character' do
+ it 'appends the character to the stream' do
+ @gz.ungetc 'Å·'
+ @gz.read.should == 'Å·'
+ end
+
+ it 'decrements pos' do
+ @gz.ungetc 'Å·'
+ @gz.pos.should == 8
+ end
+
+ it 'makes eof? false' do
+ @gz.ungetc 'Å·'
+ @gz.eof?.should be_false
+ end
+ end
+
+ describe 'with a multi-character string' do
+ it 'appends the characters to the stream' do
+ @gz.ungetc 'xŷž'
+ @gz.read.should == 'xŷž'
+ end
+
+ it 'decrements pos' do
+ @gz.ungetc 'xŷž'
+ @gz.pos.should == 5
+ end
+
+ it 'makes eof? false' do
+ @gz.ungetc 'xŷž'
+ @gz.eof?.should be_false
+ end
+ end
+
+ describe 'with an integer' do
+ it 'appends the corresponding character to the stream' do
+ @gz.ungetc 0x21
+ @gz.read.should == '!'
+ end
+
+ it 'decrements pos' do
+ @gz.ungetc 0x21
+ @gz.pos.should == 9
+ end
+
+ it 'makes eof? false' do
+ @gz.ungetc 0x21
+ @gz.eof?.should be_false
+ end
+ end
+
+ describe 'with an empty string' do
+ it 'does not append anything to the stream' do
+ @gz.ungetc ''
+ @gz.read.should == ''
+ end
+
+ it 'does not decrement pos' do
+ @gz.ungetc ''
+ @gz.pos.should == 10
+ end
+
+ it 'does not make eof? false' do
+ @gz.ungetc ''
+ @gz.eof?.should be_true
+ end
+ end
+
+ quarantine! do # https://bugs.ruby-lang.org/issues/13675
+ describe 'with nil' do
+ it 'does not append anything to the stream' do
+ @gz.ungetc nil
+ @gz.read.should == ''
+ end
+
+ it 'does not decrement pos' do
+ @gz.ungetc nil
+ @gz.pos.should == 10
+ end
+
+ it 'does not make eof? false' do
+ @gz.ungetc nil
+ @gz.eof?.should be_true
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/zlib/gzipreader/unused_spec.rb b/spec/ruby/library/zlib/gzipreader/unused_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipreader/unused_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipwriter/append_spec.rb b/spec/ruby/library/zlib/gzipwriter/append_spec.rb
new file mode 100644
index 0000000000..9404dbd8b2
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipwriter/append_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe "Zlib::GzipWriter#<<" do
+ before :each do
+ @io = StringIO.new
+ end
+
+ it "returns self" do
+ Zlib::GzipWriter.wrap @io do |gzio|
+ (gzio << "test").should equal(gzio)
+ end
+ end
+
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/library/zlib/gzipwriter/comment_spec.rb b/spec/ruby/library/zlib/gzipwriter/comment_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipwriter/comment_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipwriter/flush_spec.rb b/spec/ruby/library/zlib/gzipwriter/flush_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipwriter/flush_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipwriter/mtime_spec.rb b/spec/ruby/library/zlib/gzipwriter/mtime_spec.rb
new file mode 100644
index 0000000000..96e2b18368
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipwriter/mtime_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe "Zlib::GzipWriter#mtime=" do
+ before :each do
+ @io = StringIO.new
+ end
+
+ it "sets mtime using Integer" do
+ Zlib::GzipWriter.wrap @io do |gzio|
+ gzio.mtime = 1
+
+ gzio.mtime.should == Time.at(1)
+ end
+
+ @io.string[4, 4].should == [1,0,0,0].pack('C*')
+ end
+
+ it "sets mtime using Time" do
+ Zlib::GzipWriter.wrap @io do |gzio|
+ gzio.mtime = Time.at 1
+
+ gzio.mtime.should == Time.at(1)
+ end
+
+ @io.string[4, 4].should == [1,0,0,0].pack('C*')
+ end
+
+ it "raises if the header was written" do
+ Zlib::GzipWriter.wrap @io do |gzio|
+ gzio.write ''
+
+ lambda { gzio.mtime = nil }.should \
+ raise_error(Zlib::GzipFile::Error, 'header is already written')
+ end
+ end
+end
diff --git a/spec/ruby/library/zlib/gzipwriter/new_spec.rb b/spec/ruby/library/zlib/gzipwriter/new_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipwriter/new_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipwriter/open_spec.rb b/spec/ruby/library/zlib/gzipwriter/open_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipwriter/open_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipwriter/orig_name_spec.rb b/spec/ruby/library/zlib/gzipwriter/orig_name_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipwriter/orig_name_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipwriter/pos_spec.rb b/spec/ruby/library/zlib/gzipwriter/pos_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipwriter/pos_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipwriter/print_spec.rb b/spec/ruby/library/zlib/gzipwriter/print_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipwriter/print_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipwriter/printf_spec.rb b/spec/ruby/library/zlib/gzipwriter/printf_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipwriter/printf_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipwriter/putc_spec.rb b/spec/ruby/library/zlib/gzipwriter/putc_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipwriter/putc_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipwriter/puts_spec.rb b/spec/ruby/library/zlib/gzipwriter/puts_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipwriter/puts_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipwriter/tell_spec.rb b/spec/ruby/library/zlib/gzipwriter/tell_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipwriter/tell_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/gzipwriter/write_spec.rb b/spec/ruby/library/zlib/gzipwriter/write_spec.rb
new file mode 100644
index 0000000000..f694ce7cb7
--- /dev/null
+++ b/spec/ruby/library/zlib/gzipwriter/write_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../../spec_helper'
+require 'stringio'
+require 'zlib'
+
+describe "GzipWriter#write" do
+ before :each do
+ @data = '12345abcde'
+ @zip = [31, 139, 8, 0, 44, 220, 209, 71, 0, 3, 51, 52, 50, 54, 49, 77,
+ 76, 74, 78, 73, 5, 0, 157, 5, 0, 36, 10, 0, 0, 0].pack('C*')
+ @io = StringIO.new "".b
+ end
+
+ it "writes some compressed data" do
+ Zlib::GzipWriter.wrap @io do |gzio|
+ gzio.write @data
+ end
+
+ # skip gzip header for now
+ @io.string.unpack('C*')[10..-1].should == @zip.unpack('C*')[10..-1]
+ end
+
+ it "returns the number of bytes in the input" do
+ Zlib::GzipWriter.wrap @io do |gzio|
+ gzio.write(@data).should == @data.size
+ end
+ end
+
+ it "handles inputs of 2^23 bytes" do
+ input = '.'.b * (2 ** 23)
+
+ Zlib::GzipWriter.wrap @io do |gzio|
+ gzio.write input
+ end
+ @io.string.size.should == 8176
+ end
+end
diff --git a/spec/ruby/library/zlib/inflate/append_spec.rb b/spec/ruby/library/zlib/inflate/append_spec.rb
new file mode 100644
index 0000000000..e1c357f82e
--- /dev/null
+++ b/spec/ruby/library/zlib/inflate/append_spec.rb
@@ -0,0 +1,60 @@
+require_relative '../../../spec_helper'
+require 'zlib'
+
+describe "Zlib::Inflate#<<" do
+ before :all do
+ @foo_deflated = [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*')
+ end
+
+ before :each do
+ @z = Zlib::Inflate.new
+ end
+
+ after :each do
+ @z.close unless @z.closed?
+ end
+
+ it "appends data to the input stream" do
+ @z << @foo_deflated
+ @z.finish.should == 'foo'
+ end
+
+ it "treats nil argument as the end of compressed data" do
+ @z = Zlib::Inflate.new
+ @z << @foo_deflated << nil
+ @z.finish.should == 'foo'
+ end
+
+ it "just passes through the data after nil argument" do
+ @z = Zlib::Inflate.new
+ @z << @foo_deflated << nil
+ @z << "-after_nil_data"
+ @z.finish.should == 'foo-after_nil_data'
+ end
+
+ it "properly handles data in chunks" do
+ # add bytes, one by one
+ @foo_deflated.each_byte { |d| @z << d.chr}
+ @z.finish.should == "foo"
+ end
+
+ it "properly handles incomplete data" do
+ # add bytes, one by one
+ @foo_deflated[0, 5].each_byte { |d| @z << d.chr}
+ lambda { @z.finish }.should raise_error(Zlib::BufError)
+ end
+
+ it "properly handles excessive data, byte-by-byte" do
+ # add bytes, one by one
+ data = @foo_deflated * 2
+ data.each_byte { |d| @z << d.chr}
+ @z.finish.should == "foo" + @foo_deflated
+ end
+
+ it "properly handles excessive data, in one go" do
+ # add bytes, one by one
+ data = @foo_deflated * 2
+ @z << data
+ @z.finish.should == "foo" + @foo_deflated
+ end
+end
diff --git a/spec/ruby/library/zlib/inflate/finish_spec.rb b/spec/ruby/library/zlib/inflate/finish_spec.rb
new file mode 100644
index 0000000000..f6e592fb6b
--- /dev/null
+++ b/spec/ruby/library/zlib/inflate/finish_spec.rb
@@ -0,0 +1,28 @@
+require 'zlib'
+
+describe "Zlib::Inflate#finish" do
+
+ before do
+ @zeros = Zlib::Deflate.deflate("0" * 100_000)
+ @inflator = Zlib::Inflate.new
+ @chunks = []
+
+ @inflator.inflate(@zeros) do |chunk|
+ @chunks << chunk
+ break
+ end
+
+ @inflator.finish do |chunk|
+ @chunks << chunk
+ end
+ end
+
+ it "inflates chunked data" do
+ @chunks.map { |chunk| chunk.length }.should == [16384, 16384, 16384, 16384, 16384, 16384, 1696]
+ end
+
+ it "each chunk should have the same prefix" do
+ @chunks.all? { |chunk| chunk =~ /\A0+\z/ }.should be_true
+ end
+
+end
diff --git a/spec/ruby/library/zlib/inflate/inflate_spec.rb b/spec/ruby/library/zlib/inflate/inflate_spec.rb
new file mode 100644
index 0000000000..7d66fb039a
--- /dev/null
+++ b/spec/ruby/library/zlib/inflate/inflate_spec.rb
@@ -0,0 +1,152 @@
+require 'zlib'
+require_relative '../../../spec_helper'
+
+describe "Zlib::Inflate#inflate" do
+
+ before :each do
+ @inflator = Zlib::Inflate.new
+ end
+ it "inflates some data" do
+ data = [120, 156, 99, 96, 128, 1, 0, 0, 10, 0, 1].pack('C*')
+ unzipped = @inflator.inflate data
+ @inflator.finish
+
+ unzipped.should == "\000" * 10
+ end
+
+ it "inflates lots of data" do
+ data = [120, 156, 237, 193, 1, 1, 0, 0] +
+ [0, 128, 144, 254, 175, 238, 8, 10] +
+ Array.new(31, 0) +
+ [24, 128, 0, 0, 1]
+
+ unzipped = @inflator.inflate data.pack('C*')
+ @inflator.finish
+
+ unzipped.should == "\000" * 32 * 1024
+ end
+
+ it "works in pass-through mode, once finished" do
+ data = [120, 156, 99, 96, 128, 1, 0, 0, 10, 0, 1]
+ @inflator.inflate data.pack('C*')
+ @inflator.finish # this is a precondition
+
+ out = @inflator.inflate('uncompressed_data')
+ out << @inflator.finish
+ out.should == 'uncompressed_data'
+
+ @inflator << ('uncompressed_data') << nil
+ @inflator.finish.should == 'uncompressed_data'
+ end
+
+end
+
+describe "Zlib::Inflate.inflate" do
+
+ it "inflates some data" do
+ data = [120, 156, 99, 96, 128, 1, 0, 0, 10, 0, 1]
+ unzipped = Zlib::Inflate.inflate data.pack('C*')
+
+ unzipped.should == "\000" * 10
+ end
+
+ it "inflates lots of data" do
+ data = [120, 156, 237, 193, 1, 1, 0, 0] +
+ [0, 128, 144, 254, 175, 238, 8, 10] +
+ Array.new(31,0) +
+ [24, 128, 0, 0, 1]
+
+ zipped = Zlib::Inflate.inflate data.pack('C*')
+
+ zipped.should == "\000" * 32 * 1024
+ end
+
+ it "properly handles data in chunks" do
+ data = [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*')
+ z = Zlib::Inflate.new
+ # add bytes, one by one
+ result = ""
+ data.each_byte { |d| result << z.inflate(d.chr)}
+ result << z.finish
+ result.should == "foo"
+ end
+
+ it "properly handles incomplete data" do
+ data = [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*')[0,5]
+ z = Zlib::Inflate.new
+ # add bytes, one by one, but not all
+ result = ""
+ data.each_byte { |d| result << z.inflate(d.chr)}
+ lambda { result << z.finish }.should raise_error(Zlib::BufError)
+ end
+
+ it "properly handles excessive data, byte-by-byte" do
+ main_data = [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*')
+ data = main_data * 2
+ result = ""
+
+ z = Zlib::Inflate.new
+ # add bytes, one by one
+ data.each_byte { |d| result << z.inflate(d.chr)}
+ result << z.finish
+
+ # the first chunk is inflated to its completion,
+ # the second chunk is just passed through.
+ result.should == "foo" + main_data
+ end
+
+ it "properly handles excessive data, in one go" do
+ main_data = [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*')
+ data = main_data * 2
+ result = ""
+
+ z = Zlib::Inflate.new
+ result << z.inflate(data)
+ result << z.finish
+
+ # the first chunk is inflated to its completion,
+ # the second chunk is just passed through.
+ result.should == "foo" + main_data
+ end
+end
+
+describe "Zlib::Inflate#inflate" do
+
+ before do
+ @zeros = Zlib::Deflate.deflate("0" * 100_000)
+ @inflator = Zlib::Inflate.new
+ @chunks = []
+ end
+
+ describe "without break" do
+
+ before do
+ @inflator.inflate(@zeros) do |chunk|
+ @chunks << chunk
+ end
+ end
+
+ it "inflates chunked data" do
+ @chunks.map { |chunk| chunk.size }.should == [16384, 16384, 16384, 16384, 16384, 16384, 1696]
+ end
+
+ it "properly handles chunked data" do
+ @chunks.all? { |chunk| chunk =~ /\A0+\z/ }.should be_true
+ end
+ end
+
+ describe "with break" do
+
+ before do
+ @inflator.inflate(@zeros) do |chunk|
+ @chunks << chunk
+ break
+ end
+ end
+
+ it "inflates chunked break" do
+ output = @inflator.inflate nil
+ (100_000 - @chunks.first.length).should == output.length
+ end
+ end
+end
diff --git a/spec/ruby/library/zlib/inflate/new_spec.rb b/spec/ruby/library/zlib/inflate/new_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/inflate/new_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/inflate/set_dictionary_spec.rb b/spec/ruby/library/zlib/inflate/set_dictionary_spec.rb
new file mode 100644
index 0000000000..251cec44bb
--- /dev/null
+++ b/spec/ruby/library/zlib/inflate/set_dictionary_spec.rb
@@ -0,0 +1,20 @@
+# -*- encoding: binary -*-
+require_relative '../../../spec_helper'
+require 'zlib'
+
+describe "Zlib::Inflate#set_dictionary" do
+ it "sets the inflate dictionary" do
+ deflated = "x\273\024\341\003\313KLJNIMK\317\310\314\002\000\025\206\003\370"
+
+ i = Zlib::Inflate.new
+
+ begin
+ i << deflated
+ flunk 'Zlib::NeedDict not raised'
+ rescue Zlib::NeedDict
+ i.set_dictionary 'aaaaaaaaaa'
+ end
+
+ i.finish.should == 'abcdefghij'
+ end
+end
diff --git a/spec/ruby/library/zlib/inflate/sync_point_spec.rb b/spec/ruby/library/zlib/inflate/sync_point_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/inflate/sync_point_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/inflate/sync_spec.rb b/spec/ruby/library/zlib/inflate/sync_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/inflate/sync_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/inflate_spec.rb b/spec/ruby/library/zlib/inflate_spec.rb
new file mode 100644
index 0000000000..393bea4f3c
--- /dev/null
+++ b/spec/ruby/library/zlib/inflate_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require "zlib"
+
+describe "Zlib#inflate" do
+ it "inflates some data" do
+ Zlib.inflate([120, 156, 51, 52, 132, 1, 0, 10, 145, 1, 235].pack('C*')).should == "1" * 10
+ end
+end
diff --git a/spec/ruby/library/zlib/zlib_version_spec.rb b/spec/ruby/library/zlib/zlib_version_spec.rb
new file mode 100644
index 0000000000..e724feaa39
--- /dev/null
+++ b/spec/ruby/library/zlib/zlib_version_spec.rb
@@ -0,0 +1 @@
+require_relative '../../spec_helper'
diff --git a/spec/ruby/library/zlib/zstream/adler_spec.rb b/spec/ruby/library/zlib/zstream/adler_spec.rb
new file mode 100644
index 0000000000..55ac8ae79e
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/adler_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../../spec_helper'
+require 'zlib'
+
+describe "Zlib::ZStream#adler" do
+ it "generates hash" do
+ z = Zlib::Deflate.new
+ z << "foo"
+ z.finish
+ z.adler.should == 0x02820145
+ end
+end
diff --git a/spec/ruby/library/zlib/zstream/avail_in_spec.rb b/spec/ruby/library/zlib/zstream/avail_in_spec.rb
new file mode 100644
index 0000000000..eddae15830
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/avail_in_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+require 'zlib'
+
+describe "Zlib::ZStream#avail_in" do
+ it "returns bytes in the input buffer" do
+ z = Zlib::Deflate.new
+ z.avail_in.should == 0
+ end
+end
diff --git a/spec/ruby/library/zlib/zstream/avail_out_spec.rb b/spec/ruby/library/zlib/zstream/avail_out_spec.rb
new file mode 100644
index 0000000000..2e5a394ec0
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/avail_out_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+require 'zlib'
+
+describe "Zlib::ZStream#avail_out" do
+ it "returns bytes in the output buffer" do
+ z = Zlib::Deflate.new
+ z.avail_out.should == 0
+ end
+end
diff --git a/spec/ruby/library/zlib/zstream/close_spec.rb b/spec/ruby/library/zlib/zstream/close_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/close_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/zstream/closed_spec.rb b/spec/ruby/library/zlib/zstream/closed_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/closed_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/zstream/data_type_spec.rb b/spec/ruby/library/zlib/zstream/data_type_spec.rb
new file mode 100644
index 0000000000..8be96adf7c
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/data_type_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../../spec_helper'
+require 'zlib'
+
+describe "Zlib::ZStream#data_type" do
+ it "returns the type of the data in the stream" do
+ z = Zlib::Deflate.new
+ [Zlib::ASCII, Zlib::BINARY, Zlib::UNKNOWN].include?(z.data_type).should == true
+ end
+end
diff --git a/spec/ruby/library/zlib/zstream/end_spec.rb b/spec/ruby/library/zlib/zstream/end_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/end_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/zstream/ended_spec.rb b/spec/ruby/library/zlib/zstream/ended_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/ended_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/zstream/finish_spec.rb b/spec/ruby/library/zlib/zstream/finish_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/finish_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/zstream/finished_spec.rb b/spec/ruby/library/zlib/zstream/finished_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/finished_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/zstream/flush_next_in_spec.rb b/spec/ruby/library/zlib/zstream/flush_next_in_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/flush_next_in_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/zstream/flush_next_out_spec.rb b/spec/ruby/library/zlib/zstream/flush_next_out_spec.rb
new file mode 100644
index 0000000000..59a0622903
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/flush_next_out_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require 'zlib'
+
+describe "Zlib::ZStream#flush_next_out" do
+
+ it "flushes the stream and flushes the output buffer" do
+ zs = Zlib::Inflate.new
+ zs << [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*')
+
+ zs.flush_next_out.should == 'foo'
+ zs.finished?.should == true
+ zs.flush_next_out.should == ''
+ end
+end
diff --git a/spec/ruby/library/zlib/zstream/reset_spec.rb b/spec/ruby/library/zlib/zstream/reset_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/reset_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/zstream/stream_end_spec.rb b/spec/ruby/library/zlib/zstream/stream_end_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/stream_end_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/zstream/total_in_spec.rb b/spec/ruby/library/zlib/zstream/total_in_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/total_in_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/library/zlib/zstream/total_out_spec.rb b/spec/ruby/library/zlib/zstream/total_out_spec.rb
new file mode 100644
index 0000000000..e15f14f95f
--- /dev/null
+++ b/spec/ruby/library/zlib/zstream/total_out_spec.rb
@@ -0,0 +1 @@
+require_relative '../../../spec_helper'
diff --git a/spec/ruby/optional/capi/README b/spec/ruby/optional/capi/README
new file mode 100644
index 0000000000..57b0c51f01
--- /dev/null
+++ b/spec/ruby/optional/capi/README
@@ -0,0 +1,16 @@
+C-API Specs
+
+These specs test the C-API from Ruby. The following are conventions for the
+specs:
+
+1. Put specs for functions related to a Ruby class in a file named according
+ to the class. For example, for rb_ary_new function, put the specs in
+ optional/capi/array_spec.rb
+2. Put the C file containing the C functions for array_spec.rb in
+ optional/capi/ext/array_spec.c
+3. Add a '#define HAVE_RB_ARY_NEW 1' to rubyspec.h
+4. Name the C extension class 'CApiArraySpecs'.
+5. Name the C functions 'array_spec_rb_ary_new'.
+6. Wrap the code in the optional/capi/ext/array_spec.c in
+ '#ifdef HAVE_RB_ARY_NEW'
+7. Attach the C function to the class using the name 'rb_ary_new'
diff --git a/spec/ruby/optional/capi/array_spec.rb b/spec/ruby/optional/capi/array_spec.rb
new file mode 100644
index 0000000000..56373efe34
--- /dev/null
+++ b/spec/ruby/optional/capi/array_spec.rb
@@ -0,0 +1,463 @@
+require_relative 'spec_helper'
+
+load_extension("array")
+
+describe :rb_ary_new2, shared: true do
+ it "returns an empty array" do
+ @s.send(@method, 5).should == []
+ end
+
+ it "raises an ArgumentError when the given argument is negative" do
+ lambda { @s.send(@method, -1) }.should raise_error(ArgumentError)
+ end
+end
+
+describe "C-API Array function" do
+ before :each do
+ @s = CApiArraySpecs.new
+ end
+
+ describe "rb_Array" do
+ it "returns obj if it is an array" do
+ arr = @s.rb_Array([1,2])
+ arr.should == [1, 2]
+ end
+
+ it "tries to convert obj to an array" do
+ arr = @s.rb_Array({"bar" => "foo"})
+ arr.should == [["bar", "foo"]]
+ end
+
+ it "returns obj wrapped in an array if it cannot be converted to an array" do
+ arr = @s.rb_Array("a")
+ arr.should == ["a"]
+ end
+ end
+
+ describe "rb_ary_new" do
+ it "returns an empty array" do
+ @s.rb_ary_new.should == []
+ end
+ end
+
+ describe "rb_ary_new2" do
+ it_behaves_like :rb_ary_new2, :rb_ary_new2
+ end
+
+ describe "rb_ary_new_capa" do
+ it_behaves_like :rb_ary_new2, :rb_ary_new_capa
+ end
+
+ describe "rb_ary_new3" do
+ it "returns an array with the passed cardinality and varargs" do
+ @s.rb_ary_new3(1,2,3).should == [1,2,3]
+ end
+ end
+
+ describe "rb_ary_new_from_args" do
+ it "returns an array with the passed cardinality and varargs" do
+ @s.rb_ary_new_from_args(1,2,3).should == [1,2,3]
+ end
+ end
+
+ describe "rb_ary_new4" do
+ it "returns an array with the passed values" do
+ @s.rb_ary_new4(1,2,3).should == [1,2,3]
+ end
+ end
+
+ describe "rb_ary_new_from_values" do
+ it "returns an array with the passed values" do
+ @s.rb_ary_new_from_values(1,2,3).should == [1,2,3]
+ end
+ end
+
+ describe "rb_ary_push" do
+ it "adds an element to the array" do
+ @s.rb_ary_push([], 4).should == [4]
+ end
+ end
+
+ describe "rb_ary_cat" do
+ it "pushes the given objects onto the end of the array" do
+ @s.rb_ary_cat([1, 2], 3, 4).should == [1, 2, 3, 4]
+ end
+
+ it "raises a #{frozen_error_class} if the array is frozen" do
+ lambda { @s.rb_ary_cat([].freeze, 1) }.should raise_error(frozen_error_class)
+ end
+ end
+
+ describe "rb_ary_pop" do
+ it "removes and returns the last element in the array" do
+ a = [1,2,3]
+ @s.rb_ary_pop(a).should == 3
+ a.should == [1,2]
+ end
+ end
+
+ describe "rb_ary_join" do
+ it "joins elements of an array with a string" do
+ a = [1,2,3]
+ b = ","
+ @s.rb_ary_join(a,b).should == "1,2,3"
+ end
+ end
+
+ describe "rb_ary_to_s" do
+ it "creates an Array literal representation as a String" do
+ @s.rb_ary_to_s([1,2,3]).should == "[1, 2, 3]"
+ @s.rb_ary_to_s([]).should == "[]"
+ end
+ end
+
+ describe "rb_ary_reverse" do
+ it "reverses the order of elements in the array" do
+ a = [1,2,3]
+ @s.rb_ary_reverse(a)
+ a.should == [3,2,1]
+ end
+
+ it "returns the original array" do
+ a = [1,2,3]
+ @s.rb_ary_reverse(a).should equal(a)
+ end
+ end
+
+ describe "rb_ary_rotate" do
+ it "rotates the array so that the element at the specified position comes first" do
+ @s.rb_ary_rotate([1, 2, 3, 4], 2).should == [3, 4, 1, 2]
+ @s.rb_ary_rotate([1, 2, 3, 4], -3).should == [2, 3, 4, 1]
+ end
+
+ it "raises a #{frozen_error_class} if the array is frozen" do
+ lambda { @s.rb_ary_rotate([].freeze, 1) }.should raise_error(frozen_error_class)
+ end
+ end
+
+ describe "rb_ary_entry" do
+ it "returns nil when passed an empty array" do
+ @s.rb_ary_entry([], 0).should == nil
+ end
+
+ it "returns elements from the end when passed a negative index" do
+ @s.rb_ary_entry([1, 2, 3], -1).should == 3
+ @s.rb_ary_entry([1, 2, 3], -2).should == 2
+ end
+
+ it "returns nil if the index is out of range" do
+ @s.rb_ary_entry([1, 2, 3], 3).should == nil
+ @s.rb_ary_entry([1, 2, 3], -10).should == nil
+ end
+ end
+
+ describe "rb_ary_clear" do
+ it "removes all elements from the array" do
+ @s.rb_ary_clear([]).should == []
+ @s.rb_ary_clear([1, 2, 3]).should == []
+ end
+ end
+
+ describe "rb_ary_dup" do
+ it "duplicates the array" do
+ @s.rb_ary_dup([]).should == []
+
+ a = [1, 2, 3]
+ b = @s.rb_ary_dup(a)
+
+ b.should == a
+ b.should_not equal(a)
+ end
+ end
+
+ describe "rb_ary_unshift" do
+ it "prepends the element to the array" do
+ a = [1, 2, 3]
+ @s.rb_ary_unshift(a, "a").should == ["a", 1, 2, 3]
+ a.should == ['a', 1, 2, 3]
+ end
+ end
+
+ describe "rb_ary_shift" do
+ it "removes and returns the first element" do
+ a = [5, 1, 1, 5, 4]
+ @s.rb_ary_shift(a).should == 5
+ a.should == [1, 1, 5, 4]
+ end
+
+ it "returns nil when the array is empty" do
+ @s.rb_ary_shift([]).should == nil
+ end
+ end
+
+ describe "rb_ary_store" do
+ it "overwrites the element at the given position" do
+ a = [1, 2, 3]
+ @s.rb_ary_store(a, 1, 5)
+ a.should == [1, 5, 3]
+ end
+
+ it "writes to elements offset from the end if passed a negative index" do
+ a = [1, 2, 3]
+ @s.rb_ary_store(a, -1, 5)
+ a.should == [1, 2, 5]
+ end
+
+ it "raises an IndexError if the negative index is greater than the length" do
+ a = [1, 2, 3]
+ lambda { @s.rb_ary_store(a, -10, 5) }.should raise_error(IndexError)
+ end
+
+ it "enlarges the array as needed" do
+ a = []
+ @s.rb_ary_store(a, 2, 7)
+ a.should == [nil, nil, 7]
+ end
+
+ it "raises a #{frozen_error_class} if the array is frozen" do
+ a = [1, 2, 3].freeze
+ lambda { @s.rb_ary_store(a, 1, 5) }.should raise_error(frozen_error_class)
+ end
+ end
+
+ describe "rb_ary_concat" do
+ it "concats two arrays" do
+ a = [5, 1, 1, 5, 4]
+ b = [2, 3]
+ @s.rb_ary_concat(a, b).should == [5, 1, 1, 5, 4, 2, 3]
+ end
+ end
+
+ describe "rb_ary_plus" do
+ it "adds two arrays together" do
+ @s.rb_ary_plus([10], [20]).should == [10, 20]
+ end
+ end
+
+ describe "RARRAY_PTR" do
+ it "returns a pointer to a C array of the array's elements" do
+ a = [1, 2, 3]
+ b = []
+ @s.RARRAY_PTR_iterate(a) do |e|
+ b << e
+ end
+ a.should == b
+ end
+
+ it "allows assigning to the elements of the C array" do
+ a = [1, 2, 3]
+ @s.RARRAY_PTR_assign(a, :set)
+ a.should == [:set, :set, :set]
+ end
+ end
+
+ describe "RARRAY_LEN" do
+ it "returns the size of the array" do
+ @s.RARRAY_LEN([1, 2, 3]).should == 3
+ end
+ end
+
+ describe "RARRAY_AREF" do
+ # This macro does NOT do any bounds checking!
+ it "returns an element from the array" do
+ @s.RARRAY_AREF([1, 2, 3], 1).should == 2
+ end
+ end
+
+ describe "rb_assoc_new" do
+ it "returns an array containing the two elements" do
+ @s.rb_assoc_new(1, 2).should == [1, 2]
+ @s.rb_assoc_new(:h, [:a, :b]).should == [:h, [:a, :b]]
+ end
+ end
+
+ describe "rb_ary_includes" do
+ it "returns true if the array includes the element" do
+ @s.rb_ary_includes([1, 2, 3], 2).should be_true
+ end
+
+ it "returns false if the array does not include the element" do
+ @s.rb_ary_includes([1, 2, 3], 4).should be_false
+ end
+ end
+
+ describe "rb_ary_aref" do
+ it "returns the element at the given index" do
+ @s.rb_ary_aref([:me, :you], 0).should == :me
+ @s.rb_ary_aref([:me, :you], 1).should == :you
+ end
+
+ it "returns nil for an out of range index" do
+ @s.rb_ary_aref([1, 2, 3], 6).should be_nil
+ end
+
+ it "returns a new array where the first argument is the index and the second is the length" do
+ @s.rb_ary_aref([1, 2, 3, 4], 0, 2).should == [1, 2]
+ @s.rb_ary_aref([1, 2, 3, 4], -4, 3).should == [1, 2, 3]
+ end
+
+ it "accepts a range" do
+ @s.rb_ary_aref([1, 2, 3, 4], 0..-1).should == [1, 2, 3, 4]
+ end
+
+ it "returns nil when the start of a range is out of bounds" do
+ @s.rb_ary_aref([1, 2, 3, 4], 6..10).should be_nil
+ end
+
+ it "returns an empty array when the start of a range equals the last element" do
+ @s.rb_ary_aref([1, 2, 3, 4], 4..10).should == []
+ end
+ end
+
+ describe "rb_iterate" do
+ it "calls an callback function as a block passed to an method" do
+ s = [1,2,3,4]
+ s2 = @s.rb_iterate(s)
+
+ s2.should == s
+
+ # Make sure they're different objects
+ s2.equal?(s).should be_false
+ end
+
+ it "calls a function with the other function available as a block" do
+ h = {a: 1, b: 2}
+
+ @s.rb_iterate_each_pair(h).sort.should == [1,2]
+ end
+
+ it "calls a function which can yield into the original block" do
+ s2 = []
+
+ o = Object.new
+ def o.each
+ yield 1
+ yield 2
+ yield 3
+ yield 4
+ end
+
+ @s.rb_iterate_then_yield(o) { |x| s2 << x }
+
+ s2.should == [1,2,3,4]
+ end
+ end
+
+ describe "rb_ary_delete" do
+ it "removes an element from an array and returns it" do
+ ary = [1, 2, 3, 4]
+ @s.rb_ary_delete(ary, 3).should == 3
+ ary.should == [1, 2, 4]
+ end
+
+ it "returns nil if the element is not in the array" do
+ ary = [1, 2, 3, 4]
+ @s.rb_ary_delete(ary, 5).should be_nil
+ ary.should == [1, 2, 3, 4]
+ end
+ end
+
+ describe "rb_mem_clear" do
+ it "sets elements of a C array to nil" do
+ @s.rb_mem_clear(1).should == nil
+ end
+ end
+
+ describe "rb_ary_freeze" do
+ it "freezes the object exactly like Kernel#freeze" do
+ ary = [1,2]
+ @s.rb_ary_freeze(ary)
+ ary.frozen?.should be_true
+ end
+ end
+
+ describe "rb_ary_delete_at" do
+ before :each do
+ @array = [1, 2, 3, 4]
+ end
+
+ it "removes an element from an array at a positive index" do
+ @s.rb_ary_delete_at(@array, 2).should == 3
+ @array.should == [1, 2, 4]
+ end
+
+ it "removes an element from an array at a negative index" do
+ @s.rb_ary_delete_at(@array, -3).should == 2
+ @array.should == [1, 3, 4]
+ end
+
+ it "returns nil if the index is out of bounds" do
+ @s.rb_ary_delete_at(@array, 4).should be_nil
+ @array.should == [1, 2, 3, 4]
+ end
+
+ it "returns nil if the negative index is out of bounds" do
+ @s.rb_ary_delete_at(@array, -5).should be_nil
+ @array.should == [1, 2, 3, 4]
+ end
+ end
+
+ describe "rb_ary_to_ary" do
+
+ describe "with an array" do
+
+ it "returns the given array" do
+ array = [1, 2, 3]
+ @s.rb_ary_to_ary(array).should equal(array)
+ end
+
+ end
+
+ describe "with an object that responds to to_ary" do
+
+ it "calls to_ary on the object" do
+ obj = mock('to_ary')
+ obj.stub!(:to_ary).and_return([1, 2, 3])
+ @s.rb_ary_to_ary(obj).should == [1, 2, 3]
+ end
+
+ end
+
+ describe "with an object that responds to to_a" do
+
+ it "returns the original object in an array" do
+ obj = mock('to_a')
+ obj.stub!(:to_a).and_return([1, 2, 3])
+ @s.rb_ary_to_ary(obj).should == [obj]
+ end
+
+ end
+
+ describe "with an object that doesn't respond to to_ary" do
+
+ it "returns the original object in an array" do
+ obj = mock('no_to_ary')
+ @s.rb_ary_to_ary(obj).should == [obj]
+ end
+
+ end
+
+ end
+
+ describe "rb_ary_subseq" do
+ it "returns a subsequence of the given array" do
+ @s.rb_ary_subseq([1, 2, 3, 4, 5], 1, 3).should == [2, 3, 4]
+ end
+
+ it "returns an empty array for a subsequence of 0 elements" do
+ @s.rb_ary_subseq([1, 2, 3, 4, 5], 1, 0).should == []
+ end
+
+ it "returns nil if the begin index is out of bound" do
+ @s.rb_ary_subseq([1, 2, 3, 4, 5], 6, 3).should be_nil
+ end
+
+ it "returns the existing subsequence of the length is out of bounds" do
+ @s.rb_ary_subseq([1, 2, 3, 4, 5], 4, 3).should == [5]
+ end
+
+ it "returns nil if the size is negative" do
+ @s.rb_ary_subseq([1, 2, 3, 4, 5], 1, -1).should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/bignum_spec.rb b/spec/ruby/optional/capi/bignum_spec.rb
new file mode 100644
index 0000000000..ee95550680
--- /dev/null
+++ b/spec/ruby/optional/capi/bignum_spec.rb
@@ -0,0 +1,214 @@
+require_relative 'spec_helper'
+
+load_extension("bignum")
+
+def ensure_bignum(n)
+ raise "Bignum#coerce returned Fixnum" if fixnum_min <= n && n <= fixnum_max
+ n
+end
+
+full_range_longs = (fixnum_max == 2**(0.size * 8 - 1) - 1)
+
+describe "CApiBignumSpecs" do
+ before :each do
+ @s = CApiBignumSpecs.new
+
+ if full_range_longs
+ @max_long = 2**(0.size * 8 - 1) - 1
+ @min_long = -@max_long - 1
+ @max_ulong = ensure_bignum(2**(0.size * 8) - 1)
+ else
+ @max_long = ensure_bignum(2**(0.size * 8 - 1) - 1)
+ @min_long = ensure_bignum(-@max_long - 1)
+ @max_ulong = ensure_bignum(2**(0.size * 8) - 1)
+ end
+ end
+
+ describe "rb_big2long" do
+ unless full_range_longs
+ it "converts a Bignum" do
+ @s.rb_big2long(@max_long).should == @max_long
+ @s.rb_big2long(@min_long).should == @min_long
+ end
+ end
+
+ it "raises RangeError if passed Bignum overflow long" do
+ lambda { @s.rb_big2long(ensure_bignum(@max_long + 1)) }.should raise_error(RangeError)
+ lambda { @s.rb_big2long(ensure_bignum(@min_long - 1)) }.should raise_error(RangeError)
+ end
+ end
+
+ describe "rb_big2ll" do
+ unless full_range_longs
+ it "converts a Bignum" do
+ @s.rb_big2ll(@max_long).should == @max_long
+ @s.rb_big2ll(@min_long).should == @min_long
+ end
+ end
+
+ it "raises RangeError if passed Bignum overflow long" do
+ lambda { @s.rb_big2ll(ensure_bignum(@max_long << 40)) }.should raise_error(RangeError)
+ lambda { @s.rb_big2ll(ensure_bignum(@min_long << 40)) }.should raise_error(RangeError)
+ end
+ end
+
+ describe "rb_big2ulong" do
+ it "converts a Bignum" do
+ @s.rb_big2ulong(@max_ulong).should == @max_ulong
+ end
+
+ unless full_range_longs
+ it "wraps around if passed a negative bignum" do
+ @s.rb_big2ulong(ensure_bignum(@min_long + 1)).should == -(@min_long - 1)
+ @s.rb_big2ulong(ensure_bignum(@min_long)).should == -(@min_long)
+ end
+ end
+
+ it "raises RangeError if passed Bignum overflow long" do
+ lambda { @s.rb_big2ulong(ensure_bignum(@max_ulong + 1)) }.should raise_error(RangeError)
+ lambda { @s.rb_big2ulong(ensure_bignum(@min_long - 1)) }.should raise_error(RangeError)
+ end
+ end
+
+ describe "rb_big2dbl" do
+ it "converts a Bignum to a double value" do
+ @s.rb_big2dbl(ensure_bignum(Float::MAX.to_i)).eql?(Float::MAX).should == true
+ end
+
+ it "returns Infinity if the number is too big for a double" do
+ huge_bignum = ensure_bignum(Float::MAX.to_i * 2)
+ @s.rb_big2dbl(huge_bignum).should == infinity_value
+ end
+
+ it "returns -Infinity if the number is negative and too big for a double" do
+ huge_bignum = -ensure_bignum(Float::MAX.to_i * 2)
+ @s.rb_big2dbl(huge_bignum).should == -infinity_value
+ end
+ end
+
+ describe "rb_big2str" do
+
+ it "converts a Bignum to a string with base 10" do
+ @s.rb_big2str(ensure_bignum(2**70), 10).eql?("1180591620717411303424").should == true
+ end
+
+ it "converts a Bignum to a string with a different base" do
+ @s.rb_big2str(ensure_bignum(2**70), 16).eql?("400000000000000000").should == true
+ end
+ end
+
+ describe "rb_big_cmp" do
+ it "compares a Bignum with a Bignum" do
+ @s.rb_big_cmp(bignum_value, bignum_value(1)).should == -1
+ end
+
+ it "compares a Bignum with a Fixnum" do
+ @s.rb_big_cmp(bignum_value, 5).should == 1
+ end
+ end
+
+ describe "rb_big_pack" do
+ it "packs a Bignum into an unsigned long" do
+ val = @s.rb_big_pack(@max_ulong)
+ val.should == @max_ulong
+ end
+
+ platform_is wordsize: 64 do
+ it "packs max_ulong into 2 ulongs to allow sign bit" do
+ val = @s.rb_big_pack_length(@max_ulong)
+ val.should == 2
+ val = @s.rb_big_pack_array(@max_ulong, 2)
+ val[0].should == @max_ulong
+ val[1].should == 0
+ end
+
+ it "packs a 72-bit positive Bignum into 2 unsigned longs" do
+ num = 2 ** 71
+ val = @s.rb_big_pack_length(num)
+ val.should == 2
+ end
+
+ it "packs a 72-bit positive Bignum into correct 2 longs" do
+ num = 2 ** 71 + 1
+ val = @s.rb_big_pack_array(num, 2)
+ val[0].should == 1;
+ val[1].should == 0x80;
+ end
+
+ it "packs a 72-bit negative Bignum into correct 2 longs" do
+ num = -(2 ** 71 + 1)
+ val = @s.rb_big_pack_array(num, @s.rb_big_pack_length(num))
+ val[0].should == @max_ulong;
+ val[1].should == @max_ulong - 0x80;
+ end
+
+ it "packs lower order bytes into least significant bytes of longs for positive bignum" do
+ num = 0
+ 32.times { |i| num += i << (i * 8) }
+ val = @s.rb_big_pack_array(num, @s.rb_big_pack_length(num))
+ val.size.should == 4
+ 32.times do |i|
+ a_long = val[i/8]
+ a_byte = (a_long >> ((i % 8) * 8)) & 0xff
+ a_byte.should == i
+ end
+ end
+
+ it "packs lower order bytes into least significant bytes of longs for negative bignum" do
+ num = 0
+ 32.times { |i| num += i << (i * 8) }
+ num = -num
+ val = @s.rb_big_pack_array(num, @s.rb_big_pack_length(num))
+ val.size.should == 4
+ expected_bytes = [0x00, 0xff, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
+ 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
+ 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8,
+ 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0 ]
+ 32.times do |i|
+ a_long = val[i/8]
+ a_byte = (a_long >> ((i % 8) * 8)) & 0xff
+ a_byte.should == expected_bytes[i]
+ end
+ end
+ end
+ end
+
+ describe "rb_dbl2big" do
+ it "returns a Fixnum for a Fixnum input value" do
+ val = @s.rb_dbl2big(2)
+
+ val.kind_of?(Fixnum).should == true
+ val.should == 2
+ end
+
+ it "returns a Fixnum for a Float input value" do
+ val = @s.rb_dbl2big(2.5)
+
+ val.kind_of?(Fixnum).should == true
+ val.should == 2
+ end
+
+ it "returns a Bignum for a large enough Float input value" do
+ input = 219238102380912830988.5 # chosen by fair dice roll
+ val = @s.rb_dbl2big(input)
+
+ val.kind_of?(Bignum).should == true
+
+ # This value is based on the output of a simple C extension that uses
+ # rb_dbl2big() to convert the above input value to a Bignum.
+ val.should == 219238102380912836608
+ end
+
+ it "raises FloatDomainError for Infinity values" do
+ inf = 1.0 / 0
+
+ lambda { @s.rb_dbl2big(inf) }.should raise_error(FloatDomainError)
+ end
+
+ it "raises FloatDomainError for NaN values" do
+ nan = 0.0 / 0
+
+ lambda { @s.rb_dbl2big(nan) }.should raise_error(FloatDomainError)
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/boolean_spec.rb b/spec/ruby/optional/capi/boolean_spec.rb
new file mode 100644
index 0000000000..351419cbec
--- /dev/null
+++ b/spec/ruby/optional/capi/boolean_spec.rb
@@ -0,0 +1,33 @@
+require_relative 'spec_helper'
+
+load_extension("boolean")
+
+describe "CApiBooleanSpecs" do
+ before :each do
+ @b = CApiBooleanSpecs.new
+ end
+
+ describe "a true value from Ruby" do
+ it "is truthy in C" do
+ @b.is_true(true).should == 1
+ end
+ end
+
+ describe "a true value from Qtrue" do
+ it "is truthy in C" do
+ @b.is_true(@b.q_true).should == 1
+ end
+ end
+
+ describe "a false value from Ruby" do
+ it "is falsey in C" do
+ @b.is_true(false).should == 2
+ end
+ end
+
+ describe "a false value from Qfalse" do
+ it "is falsey in C" do
+ @b.is_true(@b.q_false).should == 2
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/class_spec.rb b/spec/ruby/optional/capi/class_spec.rb
new file mode 100644
index 0000000000..bab4f52831
--- /dev/null
+++ b/spec/ruby/optional/capi/class_spec.rb
@@ -0,0 +1,376 @@
+require_relative 'spec_helper'
+require_relative 'fixtures/class'
+
+load_extension("class")
+compile_extension("class_under_autoload")
+compile_extension("class_id_under_autoload")
+
+autoload :ClassUnderAutoload, "#{object_path}/class_under_autoload_spec"
+autoload :ClassIdUnderAutoload, "#{object_path}/class_id_under_autoload_spec"
+
+describe :rb_path_to_class, shared: true do
+ it "returns a class or module from a scoped String" do
+ @s.send(@method, "CApiClassSpecs::A::B").should equal(CApiClassSpecs::A::B)
+ end
+
+ it "resolves autoload constants" do
+ @s.send(@method, "CApiClassSpecs::A::D").name.should == "CApiClassSpecs::A::D"
+ end
+
+ it "raises an ArgumentError if a constant in the path does not exist" do
+ lambda { @s.send(@method, "CApiClassSpecs::NotDefined::B") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the final constant does not exist" do
+ lambda { @s.send(@method, "CApiClassSpecs::NotDefined") }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if the constant is not a class or module" do
+ lambda { @s.send(@method, "CApiClassSpecs::A::C") }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError even if a constant in the path exists on toplevel" do
+ lambda { @s.send(@method, "CApiClassSpecs::Object") }.should raise_error(ArgumentError)
+ end
+end
+
+describe "C-API Class function" do
+ before :each do
+ @s = CApiClassSpecs.new
+ end
+
+ describe "rb_class_new_instance" do
+ it "allocates and initializes a new object" do
+ o = @s.rb_class_new_instance(0, nil, CApiClassSpecs::Alloc)
+ o.class.should == CApiClassSpecs::Alloc
+ o.initialized.should be_true
+ end
+
+ it "passes arguments to the #initialize method" do
+ o = @s.rb_class_new_instance(2, [:one, :two], CApiClassSpecs::Alloc)
+ o.arguments.should == [:one, :two]
+ end
+ end
+
+ describe "rb_include_module" do
+ it "includes a module into a class" do
+ c = Class.new
+ o = c.new
+ lambda { o.included? }.should raise_error(NameError)
+ @s.rb_include_module(c, CApiClassSpecs::M)
+ o.included?.should be_true
+ end
+ end
+
+ describe "rb_define_attr" do
+ before :each do
+ @a = CApiClassSpecs::Attr.new
+ end
+
+ it "defines an attr_reader when passed true, false" do
+ @s.rb_define_attr(CApiClassSpecs::Attr, :foo, true, false)
+ @a.foo.should == 1
+ lambda { @a.foo = 5 }.should raise_error(NameError)
+ end
+
+ it "defines an attr_writer when passed false, true" do
+ @s.rb_define_attr(CApiClassSpecs::Attr, :bar, false, true)
+ lambda { @a.bar }.should raise_error(NameError)
+ @a.bar = 5
+ @a.instance_variable_get(:@bar).should == 5
+ end
+
+ it "defines an attr_accessor when passed true, true" do
+ @s.rb_define_attr(CApiClassSpecs::Attr, :baz, true, true)
+ @a.baz.should == 3
+ @a.baz = 6
+ @a.baz.should == 6
+ end
+ end
+
+ describe "rb_call_super" do
+ it "calls the method in the superclass" do
+ @s.define_call_super_method CApiClassSpecs::Sub, "call_super_method"
+ obj = CApiClassSpecs::Sub.new
+ obj.call_super_method.should == :super_method
+ end
+
+ it "calls the method in the superclass with the correct self" do
+ @s.define_call_super_method CApiClassSpecs::SubSelf, "call_super_method"
+ obj = CApiClassSpecs::SubSelf.new
+ obj.call_super_method.should equal obj
+ end
+
+ it "calls the method in the superclass through two native levels" do
+ @s.define_call_super_method CApiClassSpecs::Sub, "call_super_method"
+ @s.define_call_super_method CApiClassSpecs::SubSub, "call_super_method"
+ obj = CApiClassSpecs::SubSub.new
+ obj.call_super_method.should == :super_method
+ end
+ end
+
+ describe "rb_class2name" do
+ it "returns the class name" do
+ @s.rb_class2name(CApiClassSpecs).should == "CApiClassSpecs"
+ end
+
+ it "returns a string for an anonymous class" do
+ @s.rb_class2name(Class.new).should be_kind_of(String)
+ end
+ end
+
+ describe "rb_class_path" do
+ it "returns a String of a class path with no scope modifiers" do
+ @s.rb_class_path(Array).should == "Array"
+ end
+
+ it "returns a String of a class path with scope modifiers" do
+ @s.rb_class_path(File::Stat).should == "File::Stat"
+ end
+ end
+
+ describe "rb_class_name" do
+ it "returns the class name" do
+ @s.rb_class_name(CApiClassSpecs).should == "CApiClassSpecs"
+ end
+
+ it "returns a string for an anonymous class" do
+ @s.rb_class_name(Class.new).should be_kind_of(String)
+ end
+ end
+
+ describe "rb_path2class" do
+ it_behaves_like :rb_path_to_class, :rb_path2class
+ end
+
+ describe "rb_path_to_class" do
+ it_behaves_like :rb_path_to_class, :rb_path_to_class
+ end
+
+ describe "rb_cvar_defined" do
+ it "returns false when the class variable is not defined" do
+ @s.rb_cvar_defined(CApiClassSpecs::CVars, "@@nocvar").should be_false
+ end
+
+ it "returns true when the class variable is defined" do
+ @s.rb_cvar_defined(CApiClassSpecs::CVars, "@@cvar").should be_true
+ end
+
+ it "returns true if the class instance variable is defined" do
+ @s.rb_cvar_defined(CApiClassSpecs::CVars, "@c_ivar").should be_true
+ end
+ end
+
+ describe "rb_cv_set" do
+ it "sets a class variable" do
+ o = CApiClassSpecs::CVars.new
+ o.new_cv.should be_nil
+ @s.rb_cv_set(CApiClassSpecs::CVars, "@@new_cv", 1)
+ o.new_cv.should == 1
+ CApiClassSpecs::CVars.remove_class_variable :@@new_cv
+ end
+ end
+
+ describe "rb_cv_get" do
+ it "returns the value of the class variable" do
+ @s.rb_cvar_get(CApiClassSpecs::CVars, "@@cvar").should == :cvar
+ end
+
+ it "raises a NameError if the class variable is not defined" do
+ lambda {
+ @s.rb_cv_get(CApiClassSpecs::CVars, "@@no_cvar")
+ }.should raise_error(NameError, /class variable @@no_cvar/)
+ end
+ end
+
+ describe "rb_cvar_set" do
+ it "sets a class variable" do
+ o = CApiClassSpecs::CVars.new
+ o.new_cvar.should be_nil
+ @s.rb_cvar_set(CApiClassSpecs::CVars, "@@new_cvar", 1)
+ o.new_cvar.should == 1
+ CApiClassSpecs::CVars.remove_class_variable :@@new_cvar
+ end
+
+ end
+
+ describe "rb_define_class" do
+ before :each do
+ @cls = @s.rb_define_class("ClassSpecDefineClass", CApiClassSpecs::Super)
+ end
+
+ it "creates a subclass of the superclass" do
+ @cls.should be_kind_of(Class)
+ ClassSpecDefineClass.should equal(@cls)
+ @cls.superclass.should == CApiClassSpecs::Super
+ end
+
+ it "sets the class name" do
+ @cls.name.should == "ClassSpecDefineClass"
+ end
+
+ it "calls #inherited on the superclass" do
+ CApiClassSpecs::Super.should_receive(:inherited)
+ @s.rb_define_class("ClassSpecDefineClass2", CApiClassSpecs::Super)
+ Object.send(:remove_const, :ClassSpecDefineClass2)
+ end
+
+ it "raises a TypeError when given a non class object to superclass" do
+ lambda {
+ @s.rb_define_class("ClassSpecDefineClass3", Module.new)
+ }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when given a mismatched class to superclass" do
+ lambda {
+ @s.rb_define_class("ClassSpecDefineClass", Object)
+ }.should raise_error(TypeError)
+ end
+
+ ruby_version_is "2.4" do
+ it "raises a ArgumentError when given NULL as superclass" do
+ lambda {
+ @s.rb_define_class("ClassSpecDefineClass4", nil)
+ }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe "rb_define_class_under" do
+ it "creates a subclass of the superclass contained in a module" do
+ cls = @s.rb_define_class_under(CApiClassSpecs,
+ "ClassUnder1",
+ CApiClassSpecs::Super)
+ cls.should be_kind_of(Class)
+ CApiClassSpecs::Super.should be_ancestor_of(CApiClassSpecs::ClassUnder1)
+ end
+
+ it "sets the class name" do
+ cls = @s.rb_define_class_under(CApiClassSpecs, "ClassUnder3", Object)
+ cls.name.should == "CApiClassSpecs::ClassUnder3"
+ end
+
+ it "calls #inherited on the superclass" do
+ CApiClassSpecs::Super.should_receive(:inherited)
+ @s.rb_define_class_under(CApiClassSpecs, "ClassUnder4", CApiClassSpecs::Super)
+ CApiClassSpecs.send(:remove_const, :ClassUnder4)
+ end
+
+ it "raises a TypeError when given a non class object to superclass" do
+ lambda { @s.rb_define_class_under(CApiClassSpecs,
+ "ClassUnder5",
+ Module.new)
+ }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when given a mismatched class to superclass" do
+ CApiClassSpecs::ClassUnder6 = Class.new(CApiClassSpecs::Super)
+ lambda { @s.rb_define_class_under(CApiClassSpecs,
+ "ClassUnder6",
+ Class.new)
+ }.should raise_error(TypeError)
+ end
+
+ it "defines a class for an existing Autoload" do
+ ClassUnderAutoload.name.should == "ClassUnderAutoload"
+ end
+
+ it "raises a TypeError if class is defined and its superclass mismatches the given one" do
+ lambda { @s.rb_define_class_under(CApiClassSpecs, "Sub", Object) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "rb_define_class_id_under" do
+ it "creates a subclass of the superclass contained in a module" do
+ cls = @s.rb_define_class_id_under(CApiClassSpecs, :ClassIdUnder1, CApiClassSpecs::Super)
+ cls.should be_kind_of(Class)
+ CApiClassSpecs::Super.should be_ancestor_of(CApiClassSpecs::ClassIdUnder1)
+ end
+
+ it "sets the class name" do
+ cls = @s.rb_define_class_id_under(CApiClassSpecs, :ClassIdUnder3, Object)
+ cls.name.should == "CApiClassSpecs::ClassIdUnder3"
+ end
+
+ it "calls #inherited on the superclass" do
+ CApiClassSpecs::Super.should_receive(:inherited)
+ @s.rb_define_class_id_under(CApiClassSpecs, :ClassIdUnder4, CApiClassSpecs::Super)
+ CApiClassSpecs.send(:remove_const, :ClassIdUnder4)
+ end
+
+ it "defines a class for an existing Autoload" do
+ ClassIdUnderAutoload.name.should == "ClassIdUnderAutoload"
+ end
+
+ it "raises a TypeError if class is defined and its superclass mismatches the given one" do
+ lambda { @s.rb_define_class_id_under(CApiClassSpecs, :Sub, Object) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "rb_define_class_variable" do
+ it "sets a class variable" do
+ o = CApiClassSpecs::CVars.new
+ o.rbdcv_cvar.should be_nil
+ @s.rb_define_class_variable(CApiClassSpecs::CVars, "@@rbdcv_cvar", 1)
+ o.rbdcv_cvar.should == 1
+ CApiClassSpecs::CVars.remove_class_variable :@@rbdcv_cvar
+ end
+ end
+
+ describe "rb_cvar_get" do
+ it "returns the value of the class variable" do
+ @s.rb_cvar_get(CApiClassSpecs::CVars, "@@cvar").should == :cvar
+ end
+
+ it "raises a NameError if the class variable is not defined" do
+ lambda {
+ @s.rb_cvar_get(CApiClassSpecs::CVars, "@@no_cvar")
+ }.should raise_error(NameError, /class variable @@no_cvar/)
+ end
+ end
+
+ describe "rb_class_new" do
+ it "returns an new subclass of the superclass" do
+ subclass = @s.rb_class_new(CApiClassSpecs::NewClass)
+ CApiClassSpecs::NewClass.should be_ancestor_of(subclass)
+ end
+
+ it "raises a TypeError if passed Class as the superclass" do
+ lambda { @s.rb_class_new(Class) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed a singleton class as the superclass" do
+ metaclass = Object.new.singleton_class
+ lambda { @s.rb_class_new(metaclass) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "rb_class_superclass" do
+ it "returns the superclass of a class" do
+ cls = @s.rb_class_superclass(CApiClassSpecs::Sub)
+ cls.should == CApiClassSpecs::Super
+ end
+
+ it "returns nil if the class has no superclass" do
+ @s.rb_class_superclass(BasicObject).should be_nil
+ end
+ end
+
+ describe "rb_class_real" do
+ it "returns the class of an object ignoring the singleton class" do
+ obj = CApiClassSpecs::Sub.new
+ def obj.some_method() end
+
+ @s.rb_class_real(obj).should == CApiClassSpecs::Sub
+ end
+
+ it "returns the class of an object ignoring included modules" do
+ obj = CApiClassSpecs::SubM.new
+ @s.rb_class_real(obj).should == CApiClassSpecs::SubM
+ end
+
+ it "returns 0 if passed 0" do
+ @s.rb_class_real(0).should == 0
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/complex_spec.rb b/spec/ruby/optional/capi/complex_spec.rb
new file mode 100644
index 0000000000..3d8142d172
--- /dev/null
+++ b/spec/ruby/optional/capi/complex_spec.rb
@@ -0,0 +1,45 @@
+require_relative 'spec_helper'
+
+load_extension("complex")
+
+describe :rb_Complex, shared: true do
+ it "creates a new Complex with numerator and denominator" do
+ @r.send(@method, 1, 2).should == Complex(1, 2)
+ end
+end
+
+describe :rb_complex_new, shared: true do
+ it "creates a normalized Complex" do
+ r = @r.send(@method, 10, 4)
+ r.real.should == 10
+ r.imag.should == 4
+ end
+end
+
+describe "CApiComplexSpecs" do
+ before :each do
+ @r = CApiComplexSpecs.new
+ end
+
+ describe "rb_Complex" do
+ it_behaves_like :rb_Complex, :rb_Complex
+ end
+
+ describe "rb_Complex2" do
+ it_behaves_like :rb_Complex, :rb_Complex2
+ end
+
+ describe "rb_Complex1" do
+ it "creates a new Complex with real and imaginary of 0" do
+ @r.rb_Complex1(5).should == Complex(5, 0)
+ end
+ end
+
+ describe "rb_complex_new" do
+ it_behaves_like :rb_complex_new, :rb_complex_new
+ end
+
+ describe "rb_complex_new2" do
+ it_behaves_like :rb_complex_new, :rb_complex_new2
+ end
+end
diff --git a/spec/ruby/optional/capi/constants_spec.rb b/spec/ruby/optional/capi/constants_spec.rb
new file mode 100644
index 0000000000..a99fdac7e5
--- /dev/null
+++ b/spec/ruby/optional/capi/constants_spec.rb
@@ -0,0 +1,270 @@
+require_relative 'spec_helper'
+
+load_extension("constants")
+
+describe "C-API constant" do
+ before :each do
+ @s = CApiConstantsSpecs.new
+ end
+
+ specify "rb_cArray references the Array class" do
+ @s.rb_cArray.should == Array
+ end
+
+ ruby_version_is ""..."2.4" do
+ specify "rb_cBignum references the Bignum class" do
+ @s.rb_cBignum.should == Bignum
+ end
+ end
+
+ specify "rb_cClass references the Class class" do
+ @s.rb_cClass.should == Class
+ end
+
+ specify "rb_mComparable references the Comparable module" do
+ @s.rb_mComparable.should == Comparable
+ end
+
+ ruby_version_is ""..."2.5" do
+ specify "rb_cData references the Data class" do
+ @s.rb_cData.should == Data
+ end
+ end
+
+ specify "rb_mEnumerable references the Enumerable module" do
+ @s.rb_mEnumerable.should == Enumerable
+ end
+
+ specify "rb_cFalseClass references the FalseClass class" do
+ @s.rb_cFalseClass.should == FalseClass
+ end
+
+ specify "rb_cFile references the File class" do
+ @s.rb_cFile.should == File
+ end
+
+ ruby_version_is ""..."2.4" do
+ specify "rb_cFixnum references the Fixnum class" do
+ @s.rb_cFixnum.should == Fixnum
+ end
+ end
+
+ specify "rb_cFloat references the Float class" do
+ @s.rb_cFloat.should == Float
+ end
+
+ specify "rb_cHash references the Hash class" do
+ @s.rb_cHash.should == Hash
+ end
+
+ specify "rb_cInteger references the Integer class" do
+ @s.rb_cInteger.should == Integer
+ end
+
+ specify "rb_cIO references the IO class" do
+ @s.rb_cIO.should == IO
+ end
+
+ specify "rb_mKernel references the Kernel module" do
+ @s.rb_mKernel.should == Kernel
+ end
+
+ specify "rb_cMatch references the MatchData class" do
+ @s.rb_cMatch.should == MatchData
+ end
+
+ specify "rb_cModule references the Module class" do
+ @s.rb_cModule.should == Module
+ end
+
+ specify "rb_cNilClass references the NilClass class" do
+ @s.rb_cNilClass.should == NilClass
+ end
+
+ specify "rb_cNumeric references the Numeric class" do
+ @s.rb_cNumeric.should == Numeric
+ end
+
+ specify "rb_cObject references the Object class" do
+ @s.rb_cObject.should == Object
+ end
+
+ specify "rb_cRange references the Range class" do
+ @s.rb_cRange.should == Range
+ end
+
+ specify "rb_cRegexp references the Regexp class" do
+ @s.rb_cRegexp.should == Regexp
+ end
+
+ specify "rb_cString references the String class" do
+ @s.rb_cString.should == String
+ end
+
+ specify "rb_cStruct references the Struct class" do
+ @s.rb_cStruct.should == Struct
+ end
+
+ specify "rb_cSymbol references the Symbol class" do
+ @s.rb_cSymbol.should == Symbol
+ end
+
+ specify "rb_cTime references the Time class" do
+ @s.rb_cTime.should == Time
+ end
+
+ specify "rb_cThread references the Thread class" do
+ @s.rb_cThread.should == Thread
+ end
+
+ specify "rb_cTrueClass references the TrueClass class" do
+ @s.rb_cTrueClass.should == TrueClass
+ end
+
+ specify "rb_cProc references the Proc class" do
+ @s.rb_cProc.should == Proc
+ end
+
+ specify "rb_cMethod references the Method class" do
+ @s.rb_cMethod.should == Method
+ end
+
+ specify "rb_cDir references the Dir class" do
+ @s.rb_cDir.should == Dir
+ end
+
+end
+
+describe "C-API exception constant" do
+ before :each do
+ @s = CApiConstantsSpecs.new
+ end
+
+ specify "rb_eArgError references the ArgumentError class" do
+ @s.rb_eArgError.should == ArgumentError
+ end
+
+ specify "rb_eEOFError references the EOFError class" do
+ @s.rb_eEOFError.should == EOFError
+ end
+
+ specify "rb_eErrno references the Errno module" do
+ @s.rb_mErrno.should == Errno
+ end
+
+ specify "rb_eException references the Exception class" do
+ @s.rb_eException.should == Exception
+ end
+
+ specify "rb_eFloatDomainError references the FloatDomainError class" do
+ @s.rb_eFloatDomainError.should == FloatDomainError
+ end
+
+ specify "rb_eIndexError references the IndexError class" do
+ @s.rb_eIndexError.should == IndexError
+ end
+
+ specify "rb_eInterrupt references the Interrupt class" do
+ @s.rb_eInterrupt.should == Interrupt
+ end
+
+ specify "rb_eIOError references the IOError class" do
+ @s.rb_eIOError.should == IOError
+ end
+
+ specify "rb_eLoadError references the LoadError class" do
+ @s.rb_eLoadError.should == LoadError
+ end
+
+ specify "rb_eLocalJumpError references the LocalJumpError class" do
+ @s.rb_eLocalJumpError.should == LocalJumpError
+ end
+
+ specify "rb_eMathDomainError references the Math::DomainError class" do
+ @s.rb_eMathDomainError.should == Math::DomainError
+ end
+
+ specify "rb_eEncCompatError references the Encoding::CompatibilityError" do
+ @s.rb_eEncCompatError.should == Encoding::CompatibilityError
+ end
+
+ specify "rb_eNameError references the NameError class" do
+ @s.rb_eNameError.should == NameError
+ end
+
+ specify "rb_eNoMemError references the NoMemoryError class" do
+ @s.rb_eNoMemError.should == NoMemoryError
+ end
+
+ specify "rb_eNoMethodError references the NoMethodError class" do
+ @s.rb_eNoMethodError.should == NoMethodError
+ end
+
+ specify "rb_eNotImpError references the NotImplementedError class" do
+ @s.rb_eNotImpError.should == NotImplementedError
+ end
+
+ specify "rb_eRangeError references the RangeError class" do
+ @s.rb_eRangeError.should == RangeError
+ end
+
+ specify "rb_eRegexpError references the RegexpError class" do
+ @s.rb_eRegexpError.should == RegexpError
+ end
+
+ specify "rb_eRuntimeError references the RuntimeError class" do
+ @s.rb_eRuntimeError.should == RuntimeError
+ end
+
+ specify "rb_eScriptError references the ScriptError class" do
+ @s.rb_eScriptError.should == ScriptError
+ end
+
+ specify "rb_eSecurityError references the SecurityError class" do
+ @s.rb_eSecurityError.should == SecurityError
+ end
+
+ specify "rb_eSignal references the SignalException class" do
+ @s.rb_eSignal.should == SignalException
+ end
+
+ specify "rb_eStandardError references the StandardError class" do
+ @s.rb_eStandardError.should == StandardError
+ end
+
+ specify "rb_eSyntaxError references the SyntaxError class" do
+ @s.rb_eSyntaxError.should == SyntaxError
+ end
+
+ specify "rb_eSystemCallError references the SystemCallError class" do
+ @s.rb_eSystemCallError.should == SystemCallError
+ end
+
+ specify "rb_eSystemExit references the SystemExit class" do
+ @s.rb_eSystemExit.should == SystemExit
+ end
+
+ specify "rb_eSysStackError references the SystemStackError class" do
+ @s.rb_eSysStackError.should == SystemStackError
+ end
+
+ specify "rb_eTypeError references the TypeError class" do
+ @s.rb_eTypeError.should == TypeError
+ end
+
+ specify "rb_eThreadError references the ThreadError class" do
+ @s.rb_eThreadError.should == ThreadError
+ end
+
+ specify "rb_mWaitReadable references the IO::WaitReadable module" do
+ @s.rb_mWaitReadable.should == IO::WaitReadable
+ end
+
+ specify "rb_mWaitWritable references the IO::WaitWritable module" do
+ @s.rb_mWaitWritable.should == IO::WaitWritable
+ end
+
+ specify "rb_eZeroDivError references the ZeroDivisionError class" do
+ @s.rb_eZeroDivError.should == ZeroDivisionError
+ end
+end
diff --git a/spec/ruby/optional/capi/data_spec.rb b/spec/ruby/optional/capi/data_spec.rb
new file mode 100644
index 0000000000..b7d1b8fd65
--- /dev/null
+++ b/spec/ruby/optional/capi/data_spec.rb
@@ -0,0 +1,41 @@
+require_relative 'spec_helper'
+
+load_extension("data")
+
+describe "CApiAllocSpecs (a class with an alloc func defined)" do
+ it "calls the alloc func" do
+ @s = CApiAllocSpecs.new
+ @s.wrapped_data.should == 42 # not defined in initialize
+ end
+end
+
+describe "CApiWrappedStruct" do
+ before :each do
+ @s = CApiWrappedStructSpecs.new
+ end
+
+ it "wraps with Data_Wrap_Struct and Data_Get_Struct returns data" do
+ a = @s.wrap_struct(1024)
+ @s.get_struct(a).should == 1024
+ end
+
+ describe "RDATA()" do
+ it "returns the struct data" do
+ a = @s.wrap_struct(1024)
+ @s.get_struct_rdata(a).should == 1024
+ end
+
+ it "allows changing the wrapped struct" do
+ a = @s.wrap_struct(1024)
+ @s.change_struct(a, 100)
+ @s.get_struct(a).should == 100
+ end
+ end
+
+ describe "DATA_PTR" do
+ it "returns the struct data" do
+ a = @s.wrap_struct(1024)
+ @s.get_struct_data_ptr(a).should == 1024
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/encoding_spec.rb b/spec/ruby/optional/capi/encoding_spec.rb
new file mode 100644
index 0000000000..583b33d736
--- /dev/null
+++ b/spec/ruby/optional/capi/encoding_spec.rb
@@ -0,0 +1,479 @@
+# -*- encoding: utf-8 -*-
+require_relative 'spec_helper'
+require_relative 'fixtures/encoding'
+
+load_extension('encoding')
+
+describe :rb_enc_get_index, shared: true do
+ it "returns the index of the encoding of a String" do
+ @s.send(@method, "string").should >= 0
+ end
+
+ it "returns the index of the encoding of a Regexp" do
+ @s.send(@method, /regexp/).should >= 0
+ end
+end
+
+describe :rb_enc_set_index, shared: true do
+ it "sets the object's encoding to the Encoding specified by the index" do
+ obj = "abc"
+ result = @s.send(@method, obj, 2)
+
+ # This is used because indexes should be considered implementation
+ # dependent. So a pair is returned:
+ # [rb_enc_find_index()->name, rb_enc_get(obj)->name]
+ result.first.should == result.last
+ end
+
+ it "associates an encoding with a subclass of String" do
+ str = CApiEncodingSpecs::S.new "abc"
+ result = @s.send(@method, str, 1)
+ result.first.should == result.last
+ end
+
+ ruby_version_is "2.6" do
+ it "raises an ArgumentError for a non-encoding capable object" do
+ obj = Object.new
+ -> {
+ result = @s.send(@method, obj, 1)
+ }.should raise_error(ArgumentError, "cannot set encoding on non-encoding capable object")
+ end
+ end
+end
+
+describe "C-API Encoding function" do
+ before :each do
+ @s = CApiEncodingSpecs.new
+ end
+
+ ruby_version_is "2.6" do
+ describe "rb_enc_alias" do
+ it "creates an alias for an existing Encoding" do
+ @s.rb_enc_alias("ZOMGWTFBBQ", "UTF-8").should >= 0
+ Encoding.find("ZOMGWTFBBQ").name.should == "UTF-8"
+ 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"
+ end
+
+ it "returns the encoding of an Encoding specified with lower case" do
+ @s.rb_enc_find("utf-8").should == "UTF-8"
+ end
+ end
+
+ describe "rb_enc_find_index" do
+ it "returns the index of an Encoding" do
+ @s.rb_enc_find_index("UTF-8").should >= 0
+ end
+
+ it "returns the index of an Encoding specified with lower case" do
+ @s.rb_enc_find_index("utf-8").should >= 0
+ end
+
+ it "returns -1 for an non existing encoding" do
+ @s.rb_enc_find_index("non-existent-encoding").should == -1
+ end
+ end
+
+ describe "rb_enc_from_index" do
+ it "returns an Encoding" do
+ @s.rb_enc_from_index(0).should be_an_instance_of(String)
+ end
+ end
+
+ describe "rb_usascii_encoding" do
+ it "returns the encoding for Encoding::US_ASCII" do
+ @s.rb_usascii_encoding.should == "US-ASCII"
+ end
+ end
+
+ describe "rb_ascii8bit_encoding" do
+ it "returns the encoding for Encoding::ASCII_8BIT" do
+ @s.rb_ascii8bit_encoding.should == "ASCII-8BIT"
+ end
+ end
+
+ describe "rb_utf8_encoding" do
+ it "returns the encoding for Encoding::UTF_8" do
+ @s.rb_utf8_encoding.should == "UTF-8"
+ end
+ end
+
+ describe "rb_enc_from_encoding" do
+ it "returns an Encoding instance from an encoding data structure" do
+ @s.rb_enc_from_encoding("UTF-8").should == Encoding::UTF_8
+ end
+ end
+
+ describe "rb_locale_encoding" do
+ it "returns the encoding for the current locale" do
+ @s.rb_locale_encoding.should == Encoding.find('locale').name
+ end
+ end
+
+ describe "rb_filesystem_encoding" do
+ it "returns the encoding for the current filesystem" do
+ @s.rb_filesystem_encoding.should == Encoding.find('filesystem').name
+ end
+ end
+
+ describe "rb_enc_get" do
+ it "returns the encoding ossociated with an object" do
+ str = "abc".encode Encoding::ASCII_8BIT
+ @s.rb_enc_get(str).should == "ASCII-8BIT"
+ end
+ end
+
+ describe "rb_obj_encoding" do
+ it "returns the encoding ossociated with an object" do
+ str = "abc".encode Encoding::ASCII_8BIT
+ @s.rb_obj_encoding(str).should == Encoding::ASCII_8BIT
+ end
+ end
+
+ describe "rb_enc_get_index" do
+ it_behaves_like :rb_enc_get_index, :rb_enc_get_index
+
+ it "returns the index of the encoding of a Symbol" do
+ @s.rb_enc_get_index(:symbol).should >= 0
+ end
+
+ it "returns -1 as the index of nil" do
+ @s.rb_enc_get_index(nil).should == -1
+ end
+
+ it "returns -1 as the index for immediates" do
+ @s.rb_enc_get_index(1).should == -1
+ end
+
+ ruby_version_is "2.6" do
+ it "returns -1 for an object without an encoding" do
+ obj = Object.new
+ @s.rb_enc_get_index(obj).should == -1
+ end
+ end
+ end
+
+ describe "rb_enc_set_index" do
+ it_behaves_like :rb_enc_set_index, :rb_enc_set_index
+ end
+
+ describe "rb_enc_str_new" do
+ it "returns a String in US-ASCII encoding when high bits are set" do
+ xEE = [0xEE].pack('C').force_encoding('utf-8')
+ result = @s.rb_enc_str_new(xEE, 1, Encoding::US_ASCII)
+ result.encoding.should equal(Encoding::US_ASCII)
+ end
+ end
+
+ describe "rb_enc_str_coderange" do
+ describe "when the encoding is ASCII-8BIT" do
+ it "returns ENC_CODERANGE_7BIT if there are no high bits set" do
+ result = @s.rb_enc_str_coderange("abc".force_encoding("ascii-8bit"))
+ result.should == :coderange_7bit
+ end
+
+ it "returns ENC_CODERANGE_VALID if there are high bits set" do
+ xEE = [0xEE].pack('C').force_encoding('utf-8')
+ result = @s.rb_enc_str_coderange(xEE.force_encoding("ascii-8bit"))
+ result.should == :coderange_valid
+ end
+ end
+
+ describe "when the encoding is UTF-8" do
+ it "returns ENC_CODERANGE_7BIT if there are no high bits set" do
+ result = @s.rb_enc_str_coderange("abc".force_encoding("utf-8"))
+ result.should == :coderange_7bit
+ end
+
+ it "returns ENC_CODERANGE_VALID if there are high bits set in a valid string" do
+ result = @s.rb_enc_str_coderange("\xE3\x81\x82".force_encoding("utf-8"))
+ result.should == :coderange_valid
+ end
+
+ it "returns ENC_CODERANGE_BROKEN if there are high bits set in an invalid string" do
+ result = @s.rb_enc_str_coderange([0xEE].pack('C').force_encoding("utf-8"))
+ result.should == :coderange_broken
+ end
+ end
+
+ describe "when the encoding is US-ASCII" do
+ it "returns ENC_CODERANGE_7BIT if there are no high bits set" do
+ result = @s.rb_enc_str_coderange("abc".force_encoding("us-ascii"))
+ result.should == :coderange_7bit
+ end
+
+ it "returns ENC_CODERANGE_BROKEN if there are high bits set" do
+ result = @s.rb_enc_str_coderange([0xEE].pack('C').force_encoding("us-ascii"))
+ result.should == :coderange_broken
+ end
+ end
+ end
+
+ describe "ENCODING_GET" do
+ it_behaves_like :rb_enc_get_index, :ENCODING_GET
+ end
+
+ describe "ENCODING_SET" do
+ it_behaves_like :rb_enc_set_index, :ENCODING_SET
+ end
+
+ describe "ENC_CODERANGE_ASCIIONLY" do
+ it "returns true if the object encoding is only ASCII" do
+ str = "abc".force_encoding("us-ascii")
+ str.valid_encoding? # make sure to set the coderange
+ @s.ENC_CODERANGE_ASCIIONLY(str).should be_true
+ end
+
+ it "returns false if the object encoding is not ASCII only" do
+ str = "ã‚りãŒã¨ã†".force_encoding("utf-8")
+ @s.ENC_CODERANGE_ASCIIONLY(str).should be_false
+ end
+ end
+
+ describe "rb_to_encoding" do
+ it "returns the encoding for the Encoding instance passed" do
+ @s.rb_to_encoding(Encoding::BINARY).should == "ASCII-8BIT"
+ end
+
+ it "returns the correct encoding for a replicated encoding" do
+ @s.rb_to_encoding(Encoding::IBM857).should == "IBM857"
+ end
+
+ it "returns the encoding when passed a String" do
+ @s.rb_to_encoding("ASCII").should == "US-ASCII"
+ end
+
+ it "calls #to_str to convert the argument to a String" do
+ obj = mock("rb_to_encoding Encoding name")
+ obj.should_receive(:to_str).and_return("utf-8")
+
+ @s.rb_to_encoding(obj).should == "UTF-8"
+ end
+ end
+
+ describe "rb_to_encoding_index" do
+ it "returns the index of the encoding for the Encoding instance passed" do
+ @s.rb_to_encoding_index(Encoding::BINARY).should >= 0
+ end
+
+ it "returns the index of the encoding when passed a String" do
+ @s.rb_to_encoding_index("ASCII").should >= 0
+ end
+
+ it "returns the index of the dummy encoding of an Object" do
+ index = Encoding.list.index(Encoding::UTF_16)
+ @s.rb_to_encoding_index(Encoding::UTF_16.name).should == index
+ end
+
+ it "calls #to_str to convert the argument to a String" do
+ obj = mock("rb_to_encoding Encoding name")
+ obj.should_receive(:to_str).and_return("utf-8")
+
+ @s.rb_to_encoding_index(obj).should >= 0
+ end
+ end
+
+ describe "rb_enc_compatible" do
+ it "returns 0 if the encodings of the Strings are not compatible" do
+ a = [0xff].pack('C').force_encoding "ascii-8bit"
+ b = "\u3042".encode("utf-8")
+ @s.rb_enc_compatible(a, b).should == 0
+ end
+
+ # The coverage of this sucks, but there is not a simple way (yet?) to
+ # easily share the specs between rb_enc_compatible and
+ # Encoding.compatible?
+ it "returns the same value as Encoding.compatible? if the Strings have a compatible encoding" do
+ a = "abc".force_encoding("us-ascii")
+ b = "\u3042".encode("utf-8")
+ @s.rb_enc_compatible(a, b).should == Encoding.compatible?(a, b)
+ end
+ end
+
+ describe "rb_enc_copy" do
+ before :each do
+ @obj = "rb_enc_copy".encode(Encoding::US_ASCII)
+ end
+
+ it "sets the encoding of a String to that of the second argument" do
+ @s.rb_enc_copy("string", @obj).encoding.should == Encoding::US_ASCII
+ end
+
+ it "raises a RuntimeError if the second argument is a Symbol" do
+ lambda { @s.rb_enc_copy(:symbol, @obj) }.should raise_error(RuntimeError)
+ end
+
+ it "sets the encoding of a Regexp to that of the second argument" do
+ @s.rb_enc_copy(/regexp/, @obj).encoding.should == Encoding::US_ASCII
+ end
+ end
+
+ describe "rb_default_internal_encoding" do
+ before :each do
+ @default = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_internal = @default
+ end
+
+ it "returns 0 if Encoding.default_internal is nil" do
+ Encoding.default_internal = nil
+ @s.rb_default_internal_encoding.should be_nil
+ end
+
+ it "returns the encoding for Encoding.default_internal" do
+ Encoding.default_internal = "US-ASCII"
+ @s.rb_default_internal_encoding.should == "US-ASCII"
+ Encoding.default_internal = "UTF-8"
+ @s.rb_default_internal_encoding.should == "UTF-8"
+ end
+ end
+
+ describe "rb_default_external_encoding" do
+ before :each do
+ @default = Encoding.default_external
+ end
+
+ after :each do
+ Encoding.default_external = @default
+ end
+
+ it "returns the encoding for Encoding.default_external" do
+ Encoding.default_external = "BINARY"
+ @s.rb_default_external_encoding.should == "ASCII-8BIT"
+ end
+ end
+
+ describe "rb_enc_associate" do
+ it "sets the encoding of a String to the encoding" do
+ @s.rb_enc_associate("string", "ASCII-8BIT").encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "raises a RuntimeError if the argument is Symbol" do
+ lambda { @s.rb_enc_associate(:symbol, "US-ASCII") }.should raise_error(RuntimeError)
+ end
+
+ it "sets the encoding of a Regexp to the encoding" do
+ @s.rb_enc_associate(/regexp/, "ASCII-8BIT").encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "sets the encoding of a String to a default when the encoding is NULL" do
+ @s.rb_enc_associate("string", nil).encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+
+ describe "rb_enc_associate_index" do
+ it "sets the encoding of a String to the encoding" do
+ index = @s.rb_enc_find_index("ASCII-8BIT")
+ enc = @s.rb_enc_associate_index("string", index).encoding
+ enc.should == Encoding::ASCII_8BIT
+ end
+
+ it "sets the encoding of a Regexp to the encoding" do
+ index = @s.rb_enc_find_index("UTF-8")
+ enc = @s.rb_enc_associate_index(/regexp/, index).encoding
+ enc.should == Encoding::UTF_8
+ end
+
+ it "sets the encoding of a Symbol to the encoding" do
+ index = @s.rb_enc_find_index("UTF-8")
+ lambda { @s.rb_enc_associate_index(:symbol, index) }.should raise_error(RuntimeError)
+ end
+ end
+
+ describe "rb_ascii8bit_encindex" do
+ it "returns an index for the ASCII-8BIT encoding" do
+ @s.rb_ascii8bit_encindex().should >= 0
+ end
+ end
+
+ describe "rb_utf8_encindex" do
+ it "returns an index for the UTF-8 encoding" do
+ @s.rb_utf8_encindex().should >= 0
+ end
+ end
+
+ describe "rb_usascii_encindex" do
+ it "returns an index for the US-ASCII encoding" do
+ @s.rb_usascii_encindex().should >= 0
+ end
+ end
+
+ describe "rb_locale_encindex" do
+ it "returns an index for the locale encoding" do
+ @s.rb_locale_encindex().should >= 0
+ end
+ end
+
+ describe "rb_filesystem_encindex" do
+ it "returns an index for the filesystem encoding" do
+ @s.rb_filesystem_encindex().should >= 0
+ end
+ end
+
+ describe "rb_enc_to_index" do
+ it "returns an index for the encoding" do
+ @s.rb_enc_to_index("UTF-8").should >= 0
+ end
+
+ it "returns a non-negative int if the encoding is not defined" do
+ # Encoding indexes are an implementation detail and not guaranteed
+ # across implementations.
+ @s.rb_enc_to_index("FTU-81").should >= 0
+ end
+ end
+
+ describe "rb_enc_nth" do
+ it "returns the byte index of the given character index" do
+ @s.rb_enc_nth("hüllo", 3).should == 4
+ end
+ end
+
+ describe "rb_enc_codepoint_len" do
+ it "raises ArgumentError if an empty string is given" do
+ lambda do
+ @s.rb_enc_codepoint_len("")
+ end.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if an invalid byte sequence is given" do
+ lambda do
+ @s.rb_enc_codepoint_len([0xa0, 0xa1].pack('CC').force_encoding('utf-8')) # Invalid sequence identifier
+ end.should raise_error(ArgumentError)
+ end
+
+ it "returns codepoint 0x24 and length 1 for character '$'" do
+ codepoint, length = @s.rb_enc_codepoint_len("$")
+
+ codepoint.should == 0x24
+ length.should == 1
+ end
+
+ it "returns codepoint 0xA2 and length 2 for character '¢'" do
+ codepoint, length = @s.rb_enc_codepoint_len("¢")
+
+ codepoint.should == 0xA2
+ length.should == 2
+ end
+
+ it "returns codepoint 0x20AC and length 3 for character '€'" do
+ codepoint, length = @s.rb_enc_codepoint_len("€")
+
+ codepoint.should == 0x20AC
+ length.should == 3
+ end
+
+ it "returns codepoint 0x24B62 and length 4 for character '𤭢'" do
+ codepoint, length = @s.rb_enc_codepoint_len("𤭢")
+
+ codepoint.should == 0x24B62
+ length.should == 4
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/enumerator_spec.rb b/spec/ruby/optional/capi/enumerator_spec.rb
new file mode 100644
index 0000000000..9ed68c9063
--- /dev/null
+++ b/spec/ruby/optional/capi/enumerator_spec.rb
@@ -0,0 +1,66 @@
+require_relative 'spec_helper'
+
+load_extension("enumerator")
+
+describe "C-API Enumerator function" do
+ before :each do
+ @s = CApiEnumeratorSpecs.new
+ end
+
+ describe "rb_enumeratorize" do
+ before do
+ @enumerable = [1, 2, 3]
+ end
+
+ it "constructs a new Enumerator for the given object, method and arguments" do
+ enumerator = @s.rb_enumeratorize(@enumerable, :each, :arg1, :arg2)
+ enumerator.class.should == Enumerator
+ end
+
+ it "enumerates the given object" do
+ enumerator = @s.rb_enumeratorize(@enumerable, :each)
+ enumerated = []
+ enumerator.each { |i| enumerated << i }
+ enumerated.should == @enumerable
+ end
+
+ it "uses the given method for enumeration" do
+ enumerator = @s.rb_enumeratorize(@enumerable, :awesome_each)
+ @enumerable.should_receive(:awesome_each)
+ enumerator.each {}
+ end
+
+ it "passes the given arguments to the enumeration method" do
+ enumerator = @s.rb_enumeratorize(@enumerable, :each, :arg1, :arg2)
+ @enumerable.should_receive(:each).with(:arg1, :arg2)
+ enumerator.each {}
+ end
+ end
+
+ describe "rb_enumeratorize_with_size" do
+
+ it "enumerates the given object" do
+ enumerator = @s.rb_enumeratorize_with_size(@enumerable, :each)
+ enumerated = []
+ enumerator.each { |i| enumerated << i }
+ enumerated.should == @enumerable
+ end
+
+ it "uses the given method for enumeration" do
+ enumerator = @s.rb_enumeratorize_with_size(@enumerable, :awesome_each)
+ @enumerable.should_receive(:awesome_each)
+ enumerator.each {}
+ end
+
+ it "passes the given arguments to the enumeration method" do
+ enumerator = @s.rb_enumeratorize_with_size(@enumerable, :each, :arg1, :arg2)
+ @enumerable.should_receive(:each).with(:arg1, :arg2)
+ enumerator.each {}
+ end
+
+ it "uses the size function to report the size" do
+ enumerator = @s.rb_enumeratorize_with_size(@enumerable, :each, :arg1, :arg2)
+ enumerator.size.should == 7
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/exception_spec.rb b/spec/ruby/optional/capi/exception_spec.rb
new file mode 100644
index 0000000000..fb0d4fb54d
--- /dev/null
+++ b/spec/ruby/optional/capi/exception_spec.rb
@@ -0,0 +1,58 @@
+require_relative 'spec_helper'
+
+load_extension("exception")
+
+describe "C-API Exception function" do
+ before :each do
+ @s = CApiExceptionSpecs.new
+ end
+
+ describe "rb_exc_new" do
+ it "creates an exception from a C string and length" do
+ @s.rb_exc_new('foo').to_s.should == 'foo'
+ end
+ end
+
+ describe "rb_exc_new2" do
+ it "creates an exception from a C string" do
+ @s.rb_exc_new2('foo').to_s.should == 'foo'
+ end
+ end
+
+ describe "rb_exc_new3" do
+ it "creates an exception from a Ruby string" do
+ @s.rb_exc_new3('foo').to_s.should == 'foo'
+ end
+ end
+
+ describe "rb_exc_raise" do
+ it "raises passed exception" do
+ runtime_error = RuntimeError.new '42'
+ lambda { @s.rb_exc_raise(runtime_error) }.should raise_error(RuntimeError, '42')
+ end
+
+ it "raises an exception with an empty backtrace" do
+ runtime_error = RuntimeError.new '42'
+ runtime_error.set_backtrace []
+ lambda { @s.rb_exc_raise(runtime_error) }.should raise_error(RuntimeError, '42')
+ end
+ end
+
+ describe "rb_set_errinfo" do
+ after :each do
+ @s.rb_set_errinfo(nil)
+ end
+
+ it "accepts nil" do
+ @s.rb_set_errinfo(nil).should be_nil
+ end
+
+ it "accepts an Exception instance" do
+ @s.rb_set_errinfo(Exception.new).should be_nil
+ end
+
+ it "raises a TypeError if the object is not nil or an Exception instance" do
+ lambda { @s.rb_set_errinfo("error") }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/ext/.gitignore b/spec/ruby/optional/capi/ext/.gitignore
new file mode 100644
index 0000000000..577d117bb1
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/.gitignore
@@ -0,0 +1,9 @@
+# signature of implementation that
+# last compiled an extension
+*.sig
+
+# build artifacts
+*.o
+*.so
+*.bundle
+*.dll
diff --git a/spec/ruby/optional/capi/ext/array_spec.c b/spec/ruby/optional/capi/ext/array_spec.c
new file mode 100644
index 0000000000..8bc144195c
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/array_spec.c
@@ -0,0 +1,452 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_ARRAY
+static VALUE array_spec_rb_Array(VALUE self, VALUE object) {
+ return rb_Array(object);
+}
+#endif
+
+#if defined(HAVE_RARRAY_LEN) && defined(HAVE_RARRAY_PTR)
+static VALUE array_spec_RARRAY_PTR_iterate(VALUE self, VALUE array) {
+ int i;
+ VALUE* ptr;
+
+ ptr = RARRAY_PTR(array);
+ for(i = 0; i < RARRAY_LEN(array); i++) {
+ rb_yield(ptr[i]);
+ }
+ return Qnil;
+}
+
+static VALUE array_spec_RARRAY_PTR_assign(VALUE self, VALUE array, VALUE value) {
+ int i;
+ VALUE* ptr;
+
+ ptr = RARRAY_PTR(array);
+ for(i = 0; i < RARRAY_LEN(array); i++) {
+ ptr[i] = value;
+ }
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RARRAY_LEN
+static VALUE array_spec_RARRAY_LEN(VALUE self, VALUE array) {
+ return INT2FIX(RARRAY_LEN(array));
+}
+#endif
+
+#ifdef HAVE_RARRAY_AREF
+static VALUE array_spec_RARRAY_AREF(VALUE self, VALUE array, VALUE index) {
+ return RARRAY_AREF(array, FIX2INT(index));
+}
+#endif
+
+#ifdef HAVE_RB_ARY_AREF
+static VALUE array_spec_rb_ary_aref(int argc, VALUE *argv, VALUE self) {
+ VALUE ary, args;
+ rb_scan_args(argc, argv, "1*", &ary, &args);
+ return rb_ary_aref((int)RARRAY_LEN(args), RARRAY_PTR(args), ary);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_CLEAR
+static VALUE array_spec_rb_ary_clear(VALUE self, VALUE array) {
+ return rb_ary_clear(array);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_DELETE
+static VALUE array_spec_rb_ary_delete(VALUE self, VALUE array, VALUE item) {
+ return rb_ary_delete(array, item);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_DELETE_AT
+static VALUE array_spec_rb_ary_delete_at(VALUE self, VALUE array, VALUE index) {
+ return rb_ary_delete_at(array, NUM2LONG(index));
+}
+#endif
+
+#ifdef HAVE_RB_ARY_DUP
+static VALUE array_spec_rb_ary_dup(VALUE self, VALUE array) {
+ return rb_ary_dup(array);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_ENTRY
+static VALUE array_spec_rb_ary_entry(VALUE self, VALUE array, VALUE offset) {
+ return rb_ary_entry(array, FIX2INT(offset));
+}
+#endif
+
+#ifdef HAVE_RB_ARY_INCLUDES
+static VALUE array_spec_rb_ary_includes(VALUE self, VALUE ary, VALUE item) {
+ return rb_ary_includes(ary, item);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_JOIN
+static VALUE array_spec_rb_ary_join(VALUE self, VALUE array1, VALUE array2) {
+ return rb_ary_join(array1, array2);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_TO_S
+static VALUE array_spec_rb_ary_to_s(VALUE self, VALUE array) {
+ return rb_ary_to_s(array);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_NEW
+static VALUE array_spec_rb_ary_new(VALUE self) {
+ VALUE ret;
+ ret = rb_ary_new();
+ return ret;
+}
+#endif
+
+#ifdef HAVE_RB_ARY_NEW2
+static VALUE array_spec_rb_ary_new2(VALUE self, VALUE length) {
+ return rb_ary_new2(NUM2LONG(length));
+}
+#endif
+
+#ifdef HAVE_RB_ARY_NEW_CAPA
+static VALUE array_spec_rb_ary_new_capa(VALUE self, VALUE length) {
+ return rb_ary_new_capa(NUM2LONG(length));
+}
+#endif
+
+#ifdef HAVE_RB_ARY_NEW3
+static VALUE array_spec_rb_ary_new3(VALUE self, VALUE first, VALUE second, VALUE third) {
+ return rb_ary_new3(3, first, second, third);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_NEW_FROM_ARGS
+static VALUE array_spec_rb_ary_new_from_args(VALUE self, VALUE first, VALUE second, VALUE third) {
+ return rb_ary_new_from_args(3, first, second, third);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_NEW4
+static VALUE array_spec_rb_ary_new4(VALUE self, VALUE first, VALUE second, VALUE third) {
+ VALUE values[3];
+ values[0] = first;
+ values[1] = second;
+ values[2] = third;
+ return rb_ary_new4(3, values);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_NEW_FROM_VALUES
+static VALUE array_spec_rb_ary_new_from_values(VALUE self, VALUE first, VALUE second, VALUE third) {
+ VALUE values[3];
+ values[0] = first;
+ values[1] = second;
+ values[2] = third;
+ return rb_ary_new_from_values(3, values);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_POP
+static VALUE array_spec_rb_ary_pop(VALUE self, VALUE array) {
+ return rb_ary_pop(array);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_PUSH
+static VALUE array_spec_rb_ary_push(VALUE self, VALUE array, VALUE item) {
+ rb_ary_push(array, item);
+ return array;
+}
+#endif
+
+#ifdef HAVE_RB_ARY_CAT
+static VALUE array_spec_rb_ary_cat(int argc, VALUE *argv, VALUE self) {
+ VALUE ary, args;
+ rb_scan_args(argc, argv, "1*", &ary, &args);
+ return rb_ary_cat(ary, RARRAY_PTR(args), RARRAY_LEN(args));
+}
+#endif
+
+#ifdef HAVE_RB_ARY_REVERSE
+static VALUE array_spec_rb_ary_reverse(VALUE self, VALUE array) {
+ return rb_ary_reverse(array);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_ROTATE
+static VALUE array_spec_rb_ary_rotate(VALUE self, VALUE array, VALUE count) {
+ return rb_ary_rotate(array, NUM2LONG(count));
+}
+#endif
+
+#ifdef HAVE_RB_ARY_SHIFT
+static VALUE array_spec_rb_ary_shift(VALUE self, VALUE array) {
+ return rb_ary_shift(array);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_STORE
+static VALUE array_spec_rb_ary_store(VALUE self, VALUE array, VALUE offset, VALUE value) {
+ rb_ary_store(array, FIX2INT(offset), value);
+
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_ARY_CONCAT
+static VALUE array_spec_rb_ary_concat(VALUE self, VALUE array1, VALUE array2) {
+ return rb_ary_concat(array1, array2);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_PLUS
+static VALUE array_spec_rb_ary_plus(VALUE self, VALUE array1, VALUE array2) {
+ return rb_ary_plus(array1, array2);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_UNSHIFT
+static VALUE array_spec_rb_ary_unshift(VALUE self, VALUE array, VALUE val) {
+ return rb_ary_unshift(array, val);
+}
+#endif
+
+#ifdef HAVE_RB_ASSOC_NEW
+static VALUE array_spec_rb_assoc_new(VALUE self, VALUE first, VALUE second) {
+ return rb_assoc_new(first, second);
+}
+#endif
+
+#if defined(HAVE_RB_ITERATE) && defined(HAVE_RB_EACH)
+static VALUE copy_ary(VALUE el, VALUE new_ary) {
+ return rb_ary_push(new_ary, el);
+}
+
+static VALUE array_spec_rb_iterate(VALUE self, VALUE ary) {
+ VALUE new_ary = rb_ary_new();
+
+ rb_iterate(rb_each, ary, copy_ary, new_ary);
+
+ return new_ary;
+}
+
+static VALUE sub_pair(VALUE el, VALUE holder) {
+ return rb_ary_push(holder, rb_ary_entry(el, 1));
+}
+
+static VALUE each_pair(VALUE obj) {
+ return rb_funcall(obj, rb_intern("each_pair"), 0);
+}
+
+static VALUE array_spec_rb_iterate_each_pair(VALUE self, VALUE obj) {
+ VALUE new_ary = rb_ary_new();
+
+ rb_iterate(each_pair, obj, sub_pair, new_ary);
+
+ return new_ary;
+}
+
+static VALUE iter_yield(VALUE el, VALUE ary) {
+ rb_yield(el);
+ return Qnil;
+}
+
+static VALUE array_spec_rb_iterate_then_yield(VALUE self, VALUE obj) {
+ rb_iterate(rb_each, obj, iter_yield, obj);
+ return Qnil;
+}
+#endif
+
+#if defined(HAVE_RB_MEM_CLEAR)
+static VALUE array_spec_rb_mem_clear(VALUE self, VALUE obj) {
+ VALUE ary[1];
+ ary[0] = obj;
+ rb_mem_clear(ary, 1);
+ return ary[0];
+}
+#endif
+
+#ifdef HAVE_RB_ARY_FREEZE
+static VALUE array_spec_rb_ary_freeze(VALUE self, VALUE ary) {
+ return rb_ary_freeze(ary);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_TO_ARY
+static VALUE array_spec_rb_ary_to_ary(VALUE self, VALUE ary) {
+ return rb_ary_to_ary(ary);
+}
+#endif
+
+#ifdef HAVE_RB_ARY_SUBSEQ
+static VALUE array_spec_rb_ary_subseq(VALUE self, VALUE ary, VALUE begin, VALUE len) {
+ return rb_ary_subseq(ary, FIX2LONG(begin), FIX2LONG(len));
+}
+#endif
+
+void Init_array_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiArraySpecs", rb_cObject);
+
+#ifdef HAVE_RB_ARRAY
+ rb_define_method(cls, "rb_Array", array_spec_rb_Array, 1);
+#endif
+
+#ifdef HAVE_RARRAY_LEN
+ rb_define_method(cls, "RARRAY_LEN", array_spec_RARRAY_LEN, 1);
+#endif
+
+#if defined(HAVE_RARRAY_LEN) && defined(HAVE_RARRAY_PTR)
+ rb_define_method(cls, "RARRAY_PTR_iterate", array_spec_RARRAY_PTR_iterate, 1);
+ rb_define_method(cls, "RARRAY_PTR_assign", array_spec_RARRAY_PTR_assign, 2);
+#endif
+
+#ifdef HAVE_RARRAY_AREF
+ rb_define_method(cls, "RARRAY_AREF", array_spec_RARRAY_AREF, 2);
+#endif
+
+#ifdef HAVE_RB_ARY_AREF
+ rb_define_method(cls, "rb_ary_aref", array_spec_rb_ary_aref, -1);
+#endif
+
+#ifdef HAVE_RB_ARY_CLEAR
+ rb_define_method(cls, "rb_ary_clear", array_spec_rb_ary_clear, 1);
+#endif
+
+#ifdef HAVE_RB_ARY_DELETE
+ rb_define_method(cls, "rb_ary_delete", array_spec_rb_ary_delete, 2);
+#endif
+
+#ifdef HAVE_RB_ARY_DELETE_AT
+ rb_define_method(cls, "rb_ary_delete_at", array_spec_rb_ary_delete_at, 2);
+#endif
+
+#ifdef HAVE_RB_ARY_DUP
+ rb_define_method(cls, "rb_ary_dup", array_spec_rb_ary_dup, 1);
+#endif
+
+#ifdef HAVE_RB_ARY_ENTRY
+ rb_define_method(cls, "rb_ary_entry", array_spec_rb_ary_entry, 2);
+#endif
+
+#ifdef HAVE_RB_ARY_INCLUDES
+ rb_define_method(cls, "rb_ary_includes", array_spec_rb_ary_includes, 2);
+#endif
+
+#ifdef HAVE_RB_ARY_JOIN
+ rb_define_method(cls, "rb_ary_join", array_spec_rb_ary_join, 2);
+#endif
+
+#ifdef HAVE_RB_ARY_TO_S
+ rb_define_method(cls, "rb_ary_to_s", array_spec_rb_ary_to_s, 1);
+#endif
+
+#ifdef HAVE_RB_ARY_NEW
+ rb_define_method(cls, "rb_ary_new", array_spec_rb_ary_new, 0);
+#endif
+
+#ifdef HAVE_RB_ARY_NEW2
+ rb_define_method(cls, "rb_ary_new2", array_spec_rb_ary_new2, 1);
+#endif
+
+#ifdef HAVE_RB_ARY_NEW_CAPA
+ rb_define_method(cls, "rb_ary_new_capa", array_spec_rb_ary_new_capa, 1);
+#endif
+
+#ifdef HAVE_RB_ARY_NEW3
+ rb_define_method(cls, "rb_ary_new3", array_spec_rb_ary_new3, 3);
+#endif
+
+#ifdef HAVE_RB_ARY_NEW_FROM_ARGS
+ rb_define_method(cls, "rb_ary_new_from_args", array_spec_rb_ary_new_from_args, 3);
+#endif
+
+#ifdef HAVE_RB_ARY_NEW4
+ rb_define_method(cls, "rb_ary_new4", array_spec_rb_ary_new4, 3);
+#endif
+
+#ifdef HAVE_RB_ARY_NEW_FROM_VALUES
+ rb_define_method(cls, "rb_ary_new_from_values", array_spec_rb_ary_new_from_values, 3);
+#endif
+
+#ifdef HAVE_RB_ARY_POP
+ rb_define_method(cls, "rb_ary_pop", array_spec_rb_ary_pop, 1);
+#endif
+
+#ifdef HAVE_RB_ARY_PUSH
+ rb_define_method(cls, "rb_ary_push", array_spec_rb_ary_push, 2);
+#endif
+
+#ifdef HAVE_RB_ARY_CAT
+ rb_define_method(cls, "rb_ary_cat", array_spec_rb_ary_cat, -1);
+#endif
+
+#ifdef HAVE_RB_ARY_REVERSE
+ rb_define_method(cls, "rb_ary_reverse", array_spec_rb_ary_reverse, 1);
+#endif
+
+#ifdef HAVE_RB_ARY_ROTATE
+ rb_define_method(cls, "rb_ary_rotate", array_spec_rb_ary_rotate, 2);
+#endif
+
+#ifdef HAVE_RB_ARY_SHIFT
+ rb_define_method(cls, "rb_ary_shift", array_spec_rb_ary_shift, 1);
+#endif
+
+#ifdef HAVE_RB_ARY_STORE
+ rb_define_method(cls, "rb_ary_store", array_spec_rb_ary_store, 3);
+#endif
+
+#ifdef HAVE_RB_ARY_CONCAT
+ rb_define_method(cls, "rb_ary_concat", array_spec_rb_ary_concat, 2);
+#endif
+
+#ifdef HAVE_RB_ARY_PLUS
+ rb_define_method(cls, "rb_ary_plus", array_spec_rb_ary_plus, 2);
+#endif
+
+#ifdef HAVE_RB_ARY_UNSHIFT
+ rb_define_method(cls, "rb_ary_unshift", array_spec_rb_ary_unshift, 2);
+#endif
+
+#ifdef HAVE_RB_ASSOC_NEW
+ rb_define_method(cls, "rb_assoc_new", array_spec_rb_assoc_new, 2);
+#endif
+
+#if defined(HAVE_RB_ITERATE) && defined(HAVE_RB_EACH)
+ rb_define_method(cls, "rb_iterate", array_spec_rb_iterate, 1);
+ rb_define_method(cls, "rb_iterate_each_pair", array_spec_rb_iterate_each_pair, 1);
+ rb_define_method(cls, "rb_iterate_then_yield", array_spec_rb_iterate_then_yield, 1);
+#endif
+
+#if defined(HAVE_RB_MEM_CLEAR)
+ rb_define_method(cls, "rb_mem_clear", array_spec_rb_mem_clear, 1);
+#endif
+
+#ifdef HAVE_RB_ARY_FREEZE
+ rb_define_method(cls, "rb_ary_freeze", array_spec_rb_ary_freeze, 1);
+#endif
+
+#ifdef HAVE_RB_ARY_TO_ARY
+ rb_define_method(cls, "rb_ary_to_ary", array_spec_rb_ary_to_ary, 1);
+#endif
+
+#ifdef HAVE_RB_ARY_SUBSEQ
+ rb_define_method(cls, "rb_ary_subseq", array_spec_rb_ary_subseq, 3);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/bignum_spec.c b/spec/ruby/optional/capi/ext/bignum_spec.c
new file mode 100644
index 0000000000..ab3b36eadc
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/bignum_spec.c
@@ -0,0 +1,149 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_BIG2DBL
+static VALUE bignum_spec_rb_big2dbl(VALUE self, VALUE num) {
+ return rb_float_new(rb_big2dbl(num));
+}
+#endif
+
+#ifdef HAVE_RB_DBL2BIG
+static VALUE bignum_spec_rb_dbl2big(VALUE self, VALUE num) {
+ double dnum = NUM2DBL(num);
+
+ return rb_dbl2big(dnum);
+}
+#endif
+
+#ifdef HAVE_RB_BIG2LL
+static VALUE bignum_spec_rb_big2ll(VALUE self, VALUE num) {
+ return rb_ll2inum(rb_big2ll(num));
+}
+#endif
+
+#ifdef HAVE_RB_BIG2LONG
+static VALUE bignum_spec_rb_big2long(VALUE self, VALUE num) {
+ return LONG2NUM(rb_big2long(num));
+}
+#endif
+
+#ifdef HAVE_RB_BIG2STR
+static VALUE bignum_spec_rb_big2str(VALUE self, VALUE num, VALUE base) {
+ return rb_big2str(num, FIX2INT(base));
+}
+#endif
+
+#ifdef HAVE_RB_BIG2ULONG
+static VALUE bignum_spec_rb_big2ulong(VALUE self, VALUE num) {
+ return ULONG2NUM(rb_big2ulong(num));
+}
+#endif
+
+#ifdef HAVE_RB_BIG_CMP
+static VALUE bignum_spec_rb_big_cmp(VALUE self, VALUE x, VALUE y) {
+ return rb_big_cmp(x, y);
+}
+#endif
+
+#ifdef HAVE_RB_BIG_PACK
+static VALUE bignum_spec_rb_big_pack(VALUE self, VALUE val) {
+ unsigned long buff;
+
+ rb_big_pack(val, &buff, 1);
+
+ return ULONG2NUM(buff);
+}
+#endif
+
+#if HAVE_ABSINT_SIZE
+static VALUE bignum_spec_rb_big_pack_length(VALUE self, VALUE val) {
+ long long_len;
+ int leading_bits = 0;
+ int divisor = SIZEOF_LONG;
+ size_t len = rb_absint_size(val, &leading_bits);
+ if (leading_bits == 0) {
+ len += 1;
+ }
+
+ long_len = len / divisor + ((len % divisor == 0) ? 0 : 1);
+ return LONG2NUM(long_len);
+}
+#endif
+
+#ifdef HAVE_RB_BIG_PACK
+static VALUE bignum_spec_rb_big_pack_array(VALUE self, VALUE val, VALUE len) {
+ int i;
+ long long_len = NUM2LONG(len);
+
+ VALUE ary = rb_ary_new_capa(long_len);
+ unsigned long *buf = malloc(long_len * SIZEOF_LONG);
+
+ /* The array should be filled with recognisable junk so we can check
+ it is all cleared properly. */
+
+ for (i = 0; i < long_len; i++) {
+#if SIZEOF_LONG == 8
+ buf[i] = 0xfedcba9876543210L;
+#else
+ buf[i] = 0xfedcba98L;
+#endif
+ }
+
+ rb_big_pack(val, buf, long_len);
+ for (i = 0; i < long_len; i++) {
+ rb_ary_store(ary, i, ULONG2NUM(buf[i]));
+ }
+ free(buf);
+ return ary;
+}
+#endif
+
+void Init_bignum_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiBignumSpecs", rb_cObject);
+
+#ifdef HAVE_RB_BIG2DBL
+ rb_define_method(cls, "rb_big2dbl", bignum_spec_rb_big2dbl, 1);
+#endif
+
+#ifdef HAVE_RB_DBL2BIG
+ rb_define_method(cls, "rb_dbl2big", bignum_spec_rb_dbl2big, 1);
+#endif
+
+#ifdef HAVE_RB_BIG2LL
+ rb_define_method(cls, "rb_big2ll", bignum_spec_rb_big2ll, 1);
+#endif
+
+#ifdef HAVE_RB_BIG2LONG
+ rb_define_method(cls, "rb_big2long", bignum_spec_rb_big2long, 1);
+#endif
+
+#ifdef HAVE_RB_BIG2STR
+ rb_define_method(cls, "rb_big2str", bignum_spec_rb_big2str, 2);
+#endif
+
+#ifdef HAVE_RB_BIG2ULONG
+ rb_define_method(cls, "rb_big2ulong", bignum_spec_rb_big2ulong, 1);
+#endif
+
+#ifdef HAVE_RB_BIG_CMP
+ rb_define_method(cls, "rb_big_cmp", bignum_spec_rb_big_cmp, 2);
+#endif
+
+#ifdef HAVE_RB_BIG_PACK
+ rb_define_method(cls, "rb_big_pack", bignum_spec_rb_big_pack, 1);
+ rb_define_method(cls, "rb_big_pack_array", bignum_spec_rb_big_pack_array, 2);
+#endif
+
+#ifdef HAVE_ABSINT_SIZE
+ rb_define_method(cls, "rb_big_pack_length", bignum_spec_rb_big_pack_length, 1);
+#endif
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
diff --git a/spec/ruby/optional/capi/ext/boolean_spec.c b/spec/ruby/optional/capi/ext/boolean_spec.c
new file mode 100644
index 0000000000..94d79c0fc0
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/boolean_spec.c
@@ -0,0 +1,34 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static VALUE boolean_spec_is_true(VALUE self, VALUE boolean) {
+ if (boolean) {
+ return INT2NUM(1);
+ } else {
+ return INT2NUM(2);
+ }
+}
+
+static VALUE boolean_spec_q_true(VALUE self) {
+ return Qtrue;
+}
+
+static VALUE boolean_spec_q_false(VALUE self) {
+ return Qfalse;
+}
+
+void Init_boolean_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiBooleanSpecs", rb_cObject);
+ rb_define_method(cls, "is_true", boolean_spec_is_true, 1);
+ rb_define_method(cls, "q_true", boolean_spec_q_true, 0);
+ rb_define_method(cls, "q_false", boolean_spec_q_false, 0);
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
diff --git a/spec/ruby/optional/capi/ext/class_id_under_autoload_spec.c b/spec/ruby/optional/capi/ext/class_id_under_autoload_spec.c
new file mode 100644
index 0000000000..64393a9397
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/class_id_under_autoload_spec.c
@@ -0,0 +1,5 @@
+#include "ruby.h"
+
+void Init_class_id_under_autoload_spec(void) {
+ rb_define_class_id_under(rb_cObject, rb_intern("ClassIdUnderAutoload"), rb_cObject);
+}
diff --git a/spec/ruby/optional/capi/ext/class_spec.c b/spec/ruby/optional/capi/ext/class_spec.c
new file mode 100644
index 0000000000..e3860df1da
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/class_spec.c
@@ -0,0 +1,261 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_CALL_SUPER
+static VALUE class_spec_call_super_method(VALUE self) {
+ return rb_call_super(0, 0);
+}
+
+static VALUE class_spec_define_call_super_method(VALUE self, VALUE obj, VALUE str_name) {
+ rb_define_method(obj, RSTRING_PTR(str_name), class_spec_call_super_method, 0);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_CLASS_PATH
+static VALUE class_spec_rb_class_path(VALUE self, VALUE klass) {
+ return rb_class_path(klass);
+}
+#endif
+
+#ifdef HAVE_RB_CLASS_NAME
+static VALUE class_spec_rb_class_name(VALUE self, VALUE klass) {
+ return rb_class_name(klass);
+}
+#endif
+
+#ifdef HAVE_RB_CLASS2NAME
+static VALUE class_spec_rb_class2name(VALUE self, VALUE klass) {
+ return rb_str_new2( rb_class2name(klass) );
+}
+#endif
+
+#ifdef HAVE_RB_PATH2CLASS
+static VALUE class_spec_rb_path2class(VALUE self, VALUE path) {
+ return rb_path2class(RSTRING_PTR(path));
+}
+#endif
+
+#ifdef HAVE_RB_PATH_TO_CLASS
+static VALUE class_spec_rb_path_to_class(VALUE self, VALUE path) {
+ return rb_path_to_class(path);
+}
+#endif
+
+#ifdef HAVE_RB_CLASS_NEW
+static VALUE class_spec_rb_class_new(VALUE self, VALUE super) {
+ return rb_class_new(super);
+}
+#endif
+
+#ifdef HAVE_RB_CLASS_NEW_INSTANCE
+static VALUE class_spec_rb_class_new_instance(VALUE self,
+ VALUE nargs, VALUE args,
+ VALUE klass) {
+ int c_nargs = FIX2INT(nargs);
+ VALUE *c_args = alloca(sizeof(VALUE) * c_nargs);
+ int i;
+
+ for (i = 0; i < c_nargs; i++)
+ c_args[i] = rb_ary_entry(args, i);
+
+ return rb_class_new_instance(c_nargs, c_args, klass);
+}
+#endif
+
+#ifdef HAVE_RB_CLASS_REAL
+static VALUE class_spec_rb_class_real(VALUE self, VALUE object) {
+ if(rb_type_p(object, T_FIXNUM)) {
+ return INT2FIX(rb_class_real(FIX2INT(object)));
+ } else {
+ return rb_class_real(CLASS_OF(object));
+ }
+}
+#endif
+
+#ifdef HAVE_RB_CLASS_SUPERCLASS
+static VALUE class_spec_rb_class_superclass(VALUE self, VALUE klass) {
+ return rb_class_superclass(klass);
+}
+#endif
+
+#ifdef HAVE_RB_CVAR_DEFINED
+static VALUE class_spec_cvar_defined(VALUE self, VALUE klass, VALUE id) {
+ ID as_id = rb_intern(StringValuePtr(id));
+ return rb_cvar_defined(klass, as_id);
+}
+#endif
+
+#ifdef HAVE_RB_CVAR_GET
+static VALUE class_spec_cvar_get(VALUE self, VALUE klass, VALUE name) {
+ return rb_cvar_get(klass, rb_intern(StringValuePtr(name)));
+}
+#endif
+
+#ifdef HAVE_RB_CVAR_SET
+static VALUE class_spec_cvar_set(VALUE self, VALUE klass, VALUE name, VALUE val) {
+ rb_cvar_set(klass, rb_intern(StringValuePtr(name)), val);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_CV_GET
+static VALUE class_spec_cv_get(VALUE self, VALUE klass, VALUE name) {
+ return rb_cv_get(klass, StringValuePtr(name));
+}
+#endif
+
+#ifdef HAVE_RB_CV_SET
+static VALUE class_spec_cv_set(VALUE self, VALUE klass, VALUE name, VALUE val) {
+ rb_cv_set(klass, StringValuePtr(name), val);
+
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_ATTR
+VALUE class_spec_define_attr(VALUE self, VALUE klass, VALUE sym, VALUE read, VALUE write) {
+ int int_read, int_write;
+ int_read = read == Qtrue ? 1 : 0;
+ int_write = write == Qtrue ? 1 : 0;
+ rb_define_attr(klass, rb_id2name(SYM2ID(sym)), int_read, int_write);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_CLASS
+static VALUE class_spec_rb_define_class(VALUE self, VALUE name, VALUE super) {
+ if(NIL_P(super)) super = 0;
+ return rb_define_class(RSTRING_PTR(name), super);
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_CLASS_UNDER
+static VALUE class_spec_rb_define_class_under(VALUE self, VALUE outer,
+ VALUE name, VALUE super) {
+ if(NIL_P(super)) super = 0;
+ return rb_define_class_under(outer, RSTRING_PTR(name), super);
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_CLASS_ID_UNDER
+static VALUE class_spec_rb_define_class_id_under(VALUE self, VALUE outer,
+ VALUE name, VALUE super) {
+ if(NIL_P(super)) super = 0;
+ return rb_define_class_id_under(outer, SYM2ID(name), super);
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_CLASS_VARIABLE
+static VALUE class_spec_define_class_variable(VALUE self, VALUE klass, VALUE name, VALUE val) {
+ rb_define_class_variable(klass, StringValuePtr(name), val);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_INCLUDE_MODULE
+static VALUE class_spec_include_module(VALUE self, VALUE klass, VALUE module) {
+ rb_include_module(klass, module);
+ return klass;
+}
+#endif
+
+void Init_class_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiClassSpecs", rb_cObject);
+
+#ifdef HAVE_RB_CALL_SUPER
+ rb_define_method(cls, "define_call_super_method", class_spec_define_call_super_method, 2);
+#endif
+
+#ifdef HAVE_RB_CLASS_PATH
+ rb_define_method(cls, "rb_class_path", class_spec_rb_class_path, 1);
+#endif
+
+#ifdef HAVE_RB_CLASS_NAME
+ rb_define_method(cls, "rb_class_name", class_spec_rb_class_name, 1);
+#endif
+
+#ifdef HAVE_RB_CLASS2NAME
+ rb_define_method(cls, "rb_class2name", class_spec_rb_class2name, 1);
+#endif
+
+#ifdef HAVE_RB_PATH2CLASS
+ rb_define_method(cls, "rb_path2class", class_spec_rb_path2class, 1);
+#endif
+
+#ifdef HAVE_RB_PATH_TO_CLASS
+ rb_define_method(cls, "rb_path_to_class", class_spec_rb_path_to_class, 1);
+#endif
+
+#ifdef HAVE_RB_CLASS_NEW
+ rb_define_method(cls, "rb_class_new", class_spec_rb_class_new, 1);
+#endif
+
+#ifdef HAVE_RB_CLASS_NEW_INSTANCE
+ rb_define_method(cls, "rb_class_new_instance", class_spec_rb_class_new_instance, 3);
+#endif
+
+#ifdef HAVE_RB_CLASS_REAL
+ rb_define_method(cls, "rb_class_real", class_spec_rb_class_real, 1);
+#endif
+
+#ifdef HAVE_RB_CLASS_SUPERCLASS
+ rb_define_method(cls, "rb_class_superclass", class_spec_rb_class_superclass, 1);
+#endif
+
+#ifdef HAVE_RB_CVAR_DEFINED
+ rb_define_method(cls, "rb_cvar_defined", class_spec_cvar_defined, 2);
+#endif
+
+#ifdef HAVE_RB_CVAR_GET
+ rb_define_method(cls, "rb_cvar_get", class_spec_cvar_get, 2);
+#endif
+
+#ifdef HAVE_RB_CVAR_SET
+ rb_define_method(cls, "rb_cvar_set", class_spec_cvar_set, 3);
+#endif
+
+#ifdef HAVE_RB_CV_GET
+ rb_define_method(cls, "rb_cv_get", class_spec_cv_get, 2);
+#endif
+
+#ifdef HAVE_RB_CV_SET
+ rb_define_method(cls, "rb_cv_set", class_spec_cv_set, 3);
+#endif
+
+#ifdef HAVE_RB_DEFINE_ATTR
+ rb_define_method(cls, "rb_define_attr", class_spec_define_attr, 4);
+#endif
+
+#ifdef HAVE_RB_DEFINE_CLASS
+ rb_define_method(cls, "rb_define_class", class_spec_rb_define_class, 2);
+#endif
+
+#ifdef HAVE_RB_DEFINE_CLASS_UNDER
+ rb_define_method(cls, "rb_define_class_under", class_spec_rb_define_class_under, 3);
+#endif
+
+#ifdef HAVE_RB_DEFINE_CLASS_ID_UNDER
+ rb_define_method(cls, "rb_define_class_id_under", class_spec_rb_define_class_id_under, 3);
+#endif
+
+#ifdef HAVE_RB_DEFINE_CLASS_VARIABLE
+ rb_define_method(cls, "rb_define_class_variable", class_spec_define_class_variable, 3);
+#endif
+
+#ifdef HAVE_RB_INCLUDE_MODULE
+ rb_define_method(cls, "rb_include_module", class_spec_include_module, 2);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/class_under_autoload_spec.c b/spec/ruby/optional/capi/ext/class_under_autoload_spec.c
new file mode 100644
index 0000000000..120dec7327
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/class_under_autoload_spec.c
@@ -0,0 +1,5 @@
+#include "ruby.h"
+
+void Init_class_under_autoload_spec(void) {
+ rb_define_class_under(rb_cObject, "ClassUnderAutoload", rb_cObject);
+}
diff --git a/spec/ruby/optional/capi/ext/complex_spec.c b/spec/ruby/optional/capi/ext/complex_spec.c
new file mode 100644
index 0000000000..476bbce31c
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/complex_spec.c
@@ -0,0 +1,76 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_COMPLEX
+static VALUE complex_spec_rb_Complex(VALUE self, VALUE num, VALUE den) {
+ return rb_Complex(num, den);
+}
+#endif
+
+#ifdef HAVE_RB_COMPLEX1
+static VALUE complex_spec_rb_Complex1(VALUE self, VALUE num) {
+ return rb_Complex1(num);
+}
+#endif
+
+#ifdef HAVE_RB_COMPLEX2
+static VALUE complex_spec_rb_Complex2(VALUE self, VALUE num, VALUE den) {
+ return rb_Complex2(num, den);
+}
+#endif
+
+#ifdef HAVE_RB_COMPLEX_NEW
+static VALUE complex_spec_rb_complex_new(VALUE self, VALUE num, VALUE den) {
+ return rb_complex_new(num, den);
+}
+#endif
+
+#ifdef HAVE_RB_COMPLEX_NEW1
+static VALUE complex_spec_rb_complex_new1(VALUE self, VALUE num) {
+ return rb_complex_new1(num);
+}
+#endif
+
+#ifdef HAVE_RB_COMPLEX_NEW2
+static VALUE complex_spec_rb_complex_new2(VALUE self, VALUE num, VALUE den) {
+ return rb_complex_new2(num, den);
+}
+#endif
+
+void Init_complex_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiComplexSpecs", rb_cObject);
+
+#ifdef HAVE_RB_COMPLEX
+ rb_define_method(cls, "rb_Complex", complex_spec_rb_Complex, 2);
+#endif
+
+#ifdef HAVE_RB_COMPLEX1
+ rb_define_method(cls, "rb_Complex1", complex_spec_rb_Complex1, 1);
+#endif
+
+#ifdef HAVE_RB_COMPLEX2
+ rb_define_method(cls, "rb_Complex2", complex_spec_rb_Complex2, 2);
+#endif
+
+#ifdef HAVE_RB_COMPLEX_NEW
+ rb_define_method(cls, "rb_complex_new", complex_spec_rb_complex_new, 2);
+#endif
+
+#ifdef HAVE_RB_COMPLEX_NEW1
+ rb_define_method(cls, "rb_complex_new1", complex_spec_rb_complex_new1, 1);
+#endif
+
+#ifdef HAVE_RB_COMPLEX_NEW2
+ rb_define_method(cls, "rb_complex_new2", complex_spec_rb_complex_new2, 2);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/spec/ruby/optional/capi/ext/constants_spec.c b/spec/ruby/optional/capi/ext/constants_spec.c
new file mode 100644
index 0000000000..7751b12224
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/constants_spec.c
@@ -0,0 +1,646 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_CARRAY
+static VALUE constants_spec_rb_cArray(VALUE self) {
+ return rb_cArray;
+}
+#endif
+
+#ifdef HAVE_RB_CBIGNUM
+static VALUE constants_spec_rb_cBignum(VALUE self) {
+ return rb_cBignum;
+}
+#endif
+
+#ifdef HAVE_RB_CCLASS
+static VALUE constants_spec_rb_cClass(VALUE self) {
+ return rb_cClass;
+}
+#endif
+
+#ifdef HAVE_RB_CDATA
+static VALUE constants_spec_rb_cData(VALUE self) {
+ return rb_cData;
+}
+#endif
+
+#ifdef HAVE_RB_CFALSECLASS
+static VALUE constants_spec_rb_cFalseClass(VALUE self) {
+ return rb_cFalseClass;
+}
+#endif
+
+#ifdef HAVE_RB_CFILE
+static VALUE constants_spec_rb_cFile(VALUE self) {
+ return rb_cFile;
+}
+#endif
+
+#ifdef HAVE_RB_CFIXNUM
+static VALUE constants_spec_rb_cFixnum(VALUE self) {
+ return rb_cFixnum;
+}
+#endif
+
+#ifdef HAVE_RB_CFLOAT
+static VALUE constants_spec_rb_cFloat(VALUE self) {
+ return rb_cFloat;
+}
+#endif
+
+#ifdef HAVE_RB_CHASH
+static VALUE constants_spec_rb_cHash(VALUE self) {
+ return rb_cHash;
+}
+#endif
+
+#ifdef HAVE_RB_CINTEGER
+static VALUE constants_spec_rb_cInteger(VALUE self) {
+ return rb_cInteger;
+}
+#endif
+
+#ifdef HAVE_RB_CIO
+static VALUE constants_spec_rb_cIO(VALUE self) {
+ return rb_cIO;
+}
+#endif
+
+#ifdef HAVE_RB_CMODULE
+static VALUE constants_spec_rb_cModule(VALUE self) {
+ return rb_cModule;
+}
+#endif
+
+#ifdef HAVE_RB_CMATCH
+static VALUE constants_spec_rb_cMatch(VALUE self) {
+ return rb_cMatch;
+}
+#endif
+
+#ifdef HAVE_RB_CNILCLASS
+static VALUE constants_spec_rb_cNilClass(VALUE self) {
+ return rb_cNilClass;
+}
+#endif
+
+#ifdef HAVE_RB_CNUMERIC
+static VALUE constants_spec_rb_cNumeric(VALUE self) {
+ return rb_cNumeric;
+}
+#endif
+
+#ifdef HAVE_RB_COBJECT
+static VALUE constants_spec_rb_cObject(VALUE self) {
+ return rb_cObject;
+}
+#endif
+
+#ifdef HAVE_RB_CRANGE
+static VALUE constants_spec_rb_cRange(VALUE self) {
+ return rb_cRange;
+}
+#endif
+
+#ifdef HAVE_RB_CREGEXP
+static VALUE constants_spec_rb_cRegexp(VALUE self) {
+ return rb_cRegexp;
+}
+#endif
+
+#ifdef HAVE_RB_CSTRING
+static VALUE constants_spec_rb_cString(VALUE self) {
+ return rb_cString;
+}
+#endif
+
+#ifdef HAVE_RB_CSTRUCT
+static VALUE constants_spec_rb_cStruct(VALUE self) {
+ return rb_cStruct;
+}
+#endif
+
+#ifdef HAVE_RB_CSYMBOL
+static VALUE constants_spec_rb_cSymbol(VALUE self) {
+ return rb_cSymbol;
+}
+#endif
+
+#ifdef HAVE_RB_CTIME
+static VALUE constants_spec_rb_cTime(VALUE self) {
+ return rb_cTime;
+}
+#endif
+
+#ifdef HAVE_RB_CTHREAD
+static VALUE constants_spec_rb_cThread(VALUE self) {
+ return rb_cThread;
+}
+#endif
+
+#ifdef HAVE_RB_CTRUECLASS
+static VALUE constants_spec_rb_cTrueClass(VALUE self) {
+ return rb_cTrueClass;
+}
+#endif
+
+#ifdef HAVE_RB_CPROC
+static VALUE constants_spec_rb_cProc(VALUE self) {
+ return rb_cProc;
+}
+#endif
+
+#ifdef HAVE_RB_CMETHOD
+static VALUE constants_spec_rb_cMethod(VALUE self) {
+ return rb_cMethod;
+}
+#endif
+
+#ifdef HAVE_RB_CENUMERATOR
+static VALUE constants_spec_rb_cEnumerator(VALUE self) {
+ return rb_cEnumerator;
+}
+#endif
+
+#ifdef HAVE_RB_MCOMPARABLE
+static VALUE constants_spec_rb_mComparable(VALUE self) {
+ return rb_mComparable;
+}
+#endif
+
+#ifdef HAVE_RB_MENUMERABLE
+static VALUE constants_spec_rb_mEnumerable(VALUE self) {
+ return rb_mEnumerable;
+}
+#endif
+
+#ifdef HAVE_RB_MKERNEL
+static VALUE constants_spec_rb_mKernel(VALUE self) {
+ return rb_mKernel;
+}
+#endif
+
+#ifdef HAVE_RB_EARGERROR
+static VALUE constants_spec_rb_eArgError(VALUE self) {
+ return rb_eArgError;
+}
+#endif
+
+#ifdef HAVE_RB_EEOFERROR
+static VALUE constants_spec_rb_eEOFError(VALUE self) {
+ return rb_eEOFError;
+}
+#endif
+
+#ifdef HAVE_RB_MERRNO
+static VALUE constants_spec_rb_mErrno(VALUE self) {
+ return rb_mErrno;
+}
+#endif
+
+#ifdef HAVE_RB_EEXCEPTION
+static VALUE constants_spec_rb_eException(VALUE self) {
+ return rb_eException;
+}
+#endif
+
+#ifdef HAVE_RB_EFLOATDOMAINERROR
+static VALUE constants_spec_rb_eFloatDomainError(VALUE self) {
+ return rb_eFloatDomainError;
+}
+#endif
+
+#ifdef HAVE_RB_EINDEXERROR
+static VALUE constants_spec_rb_eIndexError(VALUE self) {
+ return rb_eIndexError;
+}
+#endif
+
+#ifdef HAVE_RB_EINTERRUPT
+static VALUE constants_spec_rb_eInterrupt(VALUE self) {
+ return rb_eInterrupt;
+}
+#endif
+
+#ifdef HAVE_RB_EIOERROR
+static VALUE constants_spec_rb_eIOError(VALUE self) {
+ return rb_eIOError;
+}
+#endif
+
+#ifdef HAVE_RB_ELOADERROR
+static VALUE constants_spec_rb_eLoadError(VALUE self) {
+ return rb_eLoadError;
+}
+#endif
+
+#ifdef HAVE_RB_ELOCALJUMPERROR
+static VALUE constants_spec_rb_eLocalJumpError(VALUE self) {
+ return rb_eLocalJumpError;
+}
+#endif
+
+#ifdef HAVE_RB_ENAMEERROR
+static VALUE constants_spec_rb_eNameError(VALUE self) {
+ return rb_eNameError;
+}
+#endif
+
+#ifdef HAVE_RB_ENOMEMERROR
+static VALUE constants_spec_rb_eNoMemError(VALUE self) {
+ return rb_eNoMemError;
+}
+#endif
+
+#ifdef HAVE_RB_ENOMETHODERROR
+static VALUE constants_spec_rb_eNoMethodError(VALUE self) {
+ return rb_eNoMethodError;
+}
+#endif
+
+#ifdef HAVE_RB_ENOTIMPERROR
+static VALUE constants_spec_rb_eNotImpError(VALUE self) {
+ return rb_eNotImpError;
+}
+#endif
+
+#ifdef HAVE_RB_ERANGEERROR
+static VALUE constants_spec_rb_eRangeError(VALUE self) {
+ return rb_eRangeError;
+}
+#endif
+
+#ifdef HAVE_RB_EREGEXPERROR
+static VALUE constants_spec_rb_eRegexpError(VALUE self) {
+ return rb_eRegexpError;
+}
+#endif
+
+#ifdef HAVE_RB_ERUNTIMEERROR
+static VALUE constants_spec_rb_eRuntimeError(VALUE self) {
+ return rb_eRuntimeError;
+}
+#endif
+
+#ifdef HAVE_RB_ESCRIPTERROR
+static VALUE constants_spec_rb_eScriptError(VALUE self) {
+ return rb_eScriptError;
+}
+#endif
+
+#ifdef HAVE_RB_ESECURITYERROR
+static VALUE constants_spec_rb_eSecurityError(VALUE self) {
+ return rb_eSecurityError;
+}
+#endif
+
+#ifdef HAVE_RB_ESIGNAL
+static VALUE constants_spec_rb_eSignal(VALUE self) {
+ return rb_eSignal;
+}
+#endif
+
+#ifdef HAVE_RB_ESTANDARDERROR
+static VALUE constants_spec_rb_eStandardError(VALUE self) {
+ return rb_eStandardError;
+}
+#endif
+
+#ifdef HAVE_RB_ESYNTAXERROR
+static VALUE constants_spec_rb_eSyntaxError(VALUE self) {
+ return rb_eSyntaxError;
+}
+#endif
+
+#ifdef HAVE_RB_ESYSTEMCALLERROR
+static VALUE constants_spec_rb_eSystemCallError(VALUE self) {
+ return rb_eSystemCallError;
+}
+#endif
+
+#ifdef HAVE_RB_ESYSTEMEXIT
+static VALUE constants_spec_rb_eSystemExit(VALUE self) {
+ return rb_eSystemExit;
+}
+#endif
+
+#ifdef HAVE_RB_ESYSSTACKERROR
+static VALUE constants_spec_rb_eSysStackError(VALUE self) {
+ return rb_eSysStackError;
+}
+#endif
+
+#ifdef HAVE_RB_ETYPEERROR
+static VALUE constants_spec_rb_eTypeError(VALUE self) {
+ return rb_eTypeError;
+}
+#endif
+
+#ifdef HAVE_RB_ETHREADERROR
+static VALUE constants_spec_rb_eThreadError(VALUE self) {
+ return rb_eThreadError;
+}
+#endif
+
+#ifdef HAVE_RB_EZERODIVERROR
+static VALUE constants_spec_rb_eZeroDivError(VALUE self) {
+ return rb_eZeroDivError;
+}
+#endif
+
+#ifdef HAVE_RB_EMATHDOMAINERROR
+static VALUE constants_spec_rb_eMathDomainError(VALUE self) {
+ return rb_eMathDomainError;
+}
+#endif
+
+#ifdef HAVE_RB_EENCCOMPATERROR
+static VALUE constants_spec_rb_eEncCompatError(VALUE self) {
+ return rb_eEncCompatError;
+}
+#endif
+
+#ifdef HAVE_RB_MWAITREADABLE
+static VALUE constants_spec_rb_mWaitReadable(VALUE self) {
+ return rb_mWaitReadable;
+}
+#endif
+
+#ifdef HAVE_RB_MWAITWRITABLE
+static VALUE constants_spec_rb_mWaitWritable(VALUE self) {
+ return rb_mWaitWritable;
+}
+#endif
+
+#ifdef HAVE_RB_CDIR
+static VALUE constants_spec_rb_cDir(VALUE self) {
+ return rb_cDir;
+}
+#endif
+
+void Init_constants_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiConstantsSpecs", rb_cObject);
+
+#ifdef HAVE_RB_CARRAY
+ rb_define_method(cls, "rb_cArray", constants_spec_rb_cArray, 0);
+#endif
+
+#ifdef HAVE_RB_CBIGNUM
+ rb_define_method(cls, "rb_cBignum", constants_spec_rb_cBignum, 0);
+#endif
+
+#ifdef HAVE_RB_CCLASS
+ rb_define_method(cls, "rb_cClass", constants_spec_rb_cClass, 0);
+#endif
+
+#ifdef HAVE_RB_CDATA
+ rb_define_method(cls, "rb_cData", constants_spec_rb_cData, 0);
+#endif
+
+#ifdef HAVE_RB_CFALSECLASS
+ rb_define_method(cls, "rb_cFalseClass", constants_spec_rb_cFalseClass, 0);
+#endif
+
+#ifdef HAVE_RB_CFILE
+ rb_define_method(cls, "rb_cFile", constants_spec_rb_cFile, 0);
+#endif
+
+#ifdef HAVE_RB_CFIXNUM
+ rb_define_method(cls, "rb_cFixnum", constants_spec_rb_cFixnum, 0);
+#endif
+
+#ifdef HAVE_RB_CFLOAT
+ rb_define_method(cls, "rb_cFloat", constants_spec_rb_cFloat, 0);
+#endif
+
+#ifdef HAVE_RB_CHASH
+ rb_define_method(cls, "rb_cHash", constants_spec_rb_cHash, 0);
+#endif
+
+#ifdef HAVE_RB_CINTEGER
+ rb_define_method(cls, "rb_cInteger", constants_spec_rb_cInteger, 0);
+#endif
+
+#ifdef HAVE_RB_CIO
+ rb_define_method(cls, "rb_cIO", constants_spec_rb_cIO, 0);
+#endif
+
+#ifdef HAVE_RB_CMATCH
+ rb_define_method(cls, "rb_cMatch", constants_spec_rb_cMatch, 0);
+#endif
+
+#ifdef HAVE_RB_CMODULE
+ rb_define_method(cls, "rb_cModule", constants_spec_rb_cModule, 0);
+#endif
+
+#ifdef HAVE_RB_CNILCLASS
+ rb_define_method(cls, "rb_cNilClass", constants_spec_rb_cNilClass, 0);
+#endif
+
+#ifdef HAVE_RB_CNUMERIC
+ rb_define_method(cls, "rb_cNumeric", constants_spec_rb_cNumeric, 0);
+#endif
+
+#ifdef HAVE_RB_COBJECT
+ rb_define_method(cls, "rb_cObject", constants_spec_rb_cObject, 0);
+#endif
+
+#ifdef HAVE_RB_CRANGE
+ rb_define_method(cls, "rb_cRange", constants_spec_rb_cRange, 0);
+#endif
+
+#ifdef HAVE_RB_CREGEXP
+ rb_define_method(cls, "rb_cRegexp", constants_spec_rb_cRegexp, 0);
+#endif
+
+#ifdef HAVE_RB_CSTRING
+ rb_define_method(cls, "rb_cString", constants_spec_rb_cString, 0);
+#endif
+
+#ifdef HAVE_RB_CSTRUCT
+ rb_define_method(cls, "rb_cStruct", constants_spec_rb_cStruct, 0);
+#endif
+
+#ifdef HAVE_RB_CSYMBOL
+ rb_define_method(cls, "rb_cSymbol", constants_spec_rb_cSymbol, 0);
+#endif
+
+#ifdef HAVE_RB_CTIME
+ rb_define_method(cls, "rb_cTime", constants_spec_rb_cTime, 0);
+#endif
+
+#ifdef HAVE_RB_CTHREAD
+ rb_define_method(cls, "rb_cThread", constants_spec_rb_cThread, 0);
+#endif
+
+#ifdef HAVE_RB_CTRUECLASS
+ rb_define_method(cls, "rb_cTrueClass", constants_spec_rb_cTrueClass, 0);
+#endif
+
+#ifdef HAVE_RB_CPROC
+ rb_define_method(cls, "rb_cProc", constants_spec_rb_cProc, 0);
+#endif
+
+#ifdef HAVE_RB_CMETHOD
+ rb_define_method(cls, "rb_cMethod", constants_spec_rb_cMethod, 0);
+#endif
+
+#ifdef HAVE_RB_CENUMERATOR
+ rb_define_method(cls, "rb_cEnumerator", constants_spec_rb_cEnumerator, 0);
+#endif
+
+#ifdef HAVE_RB_MCOMPARABLE
+ rb_define_method(cls, "rb_mComparable", constants_spec_rb_mComparable, 0);
+#endif
+
+#ifdef HAVE_RB_MENUMERABLE
+ rb_define_method(cls, "rb_mEnumerable", constants_spec_rb_mEnumerable, 0);
+#endif
+
+#ifdef HAVE_RB_MKERNEL
+ rb_define_method(cls, "rb_mKernel", constants_spec_rb_mKernel, 0);
+#endif
+
+#ifdef HAVE_RB_EARGERROR
+ rb_define_method(cls, "rb_eArgError", constants_spec_rb_eArgError, 0);
+#endif
+
+#ifdef HAVE_RB_EEOFERROR
+ rb_define_method(cls, "rb_eEOFError", constants_spec_rb_eEOFError, 0);
+#endif
+
+#ifdef HAVE_RB_MERRNO
+ rb_define_method(cls, "rb_mErrno", constants_spec_rb_mErrno, 0);
+#endif
+
+#ifdef HAVE_RB_EEXCEPTION
+ rb_define_method(cls, "rb_eException", constants_spec_rb_eException, 0);
+#endif
+
+#ifdef HAVE_RB_EFLOATDOMAINERROR
+ rb_define_method(cls, "rb_eFloatDomainError", constants_spec_rb_eFloatDomainError, 0);
+#endif
+
+#ifdef HAVE_RB_EINDEXERROR
+ rb_define_method(cls, "rb_eIndexError", constants_spec_rb_eIndexError, 0);
+#endif
+
+#ifdef HAVE_RB_EINTERRUPT
+ rb_define_method(cls, "rb_eInterrupt", constants_spec_rb_eInterrupt, 0);
+#endif
+
+#ifdef HAVE_RB_EIOERROR
+ rb_define_method(cls, "rb_eIOError", constants_spec_rb_eIOError, 0);
+#endif
+
+#ifdef HAVE_RB_ELOADERROR
+ rb_define_method(cls, "rb_eLoadError", constants_spec_rb_eLoadError, 0);
+#endif
+
+#ifdef HAVE_RB_ELOCALJUMPERROR
+ rb_define_method(cls, "rb_eLocalJumpError", constants_spec_rb_eLocalJumpError, 0);
+#endif
+
+#ifdef HAVE_RB_ENAMEERROR
+ rb_define_method(cls, "rb_eNameError", constants_spec_rb_eNameError, 0);
+#endif
+
+#ifdef HAVE_RB_ENOMEMERROR
+ rb_define_method(cls, "rb_eNoMemError", constants_spec_rb_eNoMemError, 0);
+#endif
+
+#ifdef HAVE_RB_ENOMETHODERROR
+ rb_define_method(cls, "rb_eNoMethodError", constants_spec_rb_eNoMethodError, 0);
+#endif
+
+#ifdef HAVE_RB_ENOTIMPERROR
+ rb_define_method(cls, "rb_eNotImpError", constants_spec_rb_eNotImpError, 0);
+#endif
+
+#ifdef HAVE_RB_ERANGEERROR
+ rb_define_method(cls, "rb_eRangeError", constants_spec_rb_eRangeError, 0);
+#endif
+
+#ifdef HAVE_RB_EREGEXPERROR
+ rb_define_method(cls, "rb_eRegexpError", constants_spec_rb_eRegexpError, 0);
+#endif
+
+#ifdef HAVE_RB_ERUNTIMEERROR
+ rb_define_method(cls, "rb_eRuntimeError", constants_spec_rb_eRuntimeError, 0);
+#endif
+
+#ifdef HAVE_RB_ESCRIPTERROR
+ rb_define_method(cls, "rb_eScriptError", constants_spec_rb_eScriptError, 0);
+#endif
+
+#ifdef HAVE_RB_ESECURITYERROR
+ rb_define_method(cls, "rb_eSecurityError", constants_spec_rb_eSecurityError, 0);
+#endif
+
+#ifdef HAVE_RB_ESIGNAL
+ rb_define_method(cls, "rb_eSignal", constants_spec_rb_eSignal, 0);
+#endif
+
+#ifdef HAVE_RB_ESTANDARDERROR
+ rb_define_method(cls, "rb_eStandardError", constants_spec_rb_eStandardError, 0);
+#endif
+
+#ifdef HAVE_RB_ESYNTAXERROR
+ rb_define_method(cls, "rb_eSyntaxError", constants_spec_rb_eSyntaxError, 0);
+#endif
+
+#ifdef HAVE_RB_ESYSTEMCALLERROR
+ rb_define_method(cls, "rb_eSystemCallError", constants_spec_rb_eSystemCallError, 0);
+#endif
+
+#ifdef HAVE_RB_ESYSTEMEXIT
+ rb_define_method(cls, "rb_eSystemExit", constants_spec_rb_eSystemExit, 0);
+#endif
+
+#ifdef HAVE_RB_ESYSSTACKERROR
+ rb_define_method(cls, "rb_eSysStackError", constants_spec_rb_eSysStackError, 0);
+#endif
+
+#ifdef HAVE_RB_ETYPEERROR
+ rb_define_method(cls, "rb_eTypeError", constants_spec_rb_eTypeError, 0);
+#endif
+
+#ifdef HAVE_RB_ETHREADERROR
+ rb_define_method(cls, "rb_eThreadError", constants_spec_rb_eThreadError, 0);
+#endif
+
+#ifdef HAVE_RB_EZERODIVERROR
+ rb_define_method(cls, "rb_eZeroDivError", constants_spec_rb_eZeroDivError, 0);
+#endif
+
+#ifdef HAVE_RB_EMATHDOMAINERROR
+ rb_define_method(cls, "rb_eMathDomainError", constants_spec_rb_eMathDomainError, 0);
+#endif
+
+#ifdef HAVE_RB_EENCCOMPATERROR
+ rb_define_method(cls, "rb_eEncCompatError", constants_spec_rb_eEncCompatError, 0);
+#endif
+
+#ifdef HAVE_RB_MWAITREADABLE
+ rb_define_method(cls, "rb_mWaitReadable", constants_spec_rb_mWaitReadable, 0);
+#endif
+
+#ifdef HAVE_RB_MWAITWRITABLE
+ rb_define_method(cls, "rb_mWaitWritable", constants_spec_rb_mWaitWritable, 0);
+#endif
+
+#ifdef HAVE_RB_CDIR
+ rb_define_method(cls, "rb_cDir", constants_spec_rb_cDir, 0);
+#endif
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/data_spec.c b/spec/ruby/optional/capi/ext/data_spec.c
new file mode 100644
index 0000000000..ac6b51b454
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/data_spec.c
@@ -0,0 +1,90 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(HAVE_RDATA) && defined(HAVE_DATA_WRAP_STRUCT)
+struct sample_wrapped_struct {
+ int foo;
+};
+
+void sample_wrapped_struct_free(void* st) {
+ free(st);
+}
+
+void sample_wrapped_struct_mark(void* st) {
+}
+
+VALUE sdaf_alloc_func(VALUE klass) {
+ struct sample_wrapped_struct* bar = malloc(sizeof(struct sample_wrapped_struct));
+ bar->foo = 42;
+ return Data_Wrap_Struct(klass, &sample_wrapped_struct_mark, &sample_wrapped_struct_free, bar);
+}
+
+VALUE sdaf_get_struct(VALUE self) {
+ struct sample_wrapped_struct* bar;
+ Data_Get_Struct(self, struct sample_wrapped_struct, bar);
+
+ return INT2FIX((*bar).foo);
+}
+
+VALUE sws_wrap_struct(VALUE self, VALUE val) {
+ struct sample_wrapped_struct* bar = malloc(sizeof(struct sample_wrapped_struct));
+ bar->foo = FIX2INT(val);
+ return Data_Wrap_Struct(rb_cObject, &sample_wrapped_struct_mark, &sample_wrapped_struct_free, bar);
+}
+
+VALUE sws_get_struct(VALUE self, VALUE obj) {
+ struct sample_wrapped_struct* bar;
+ Data_Get_Struct(obj, struct sample_wrapped_struct, bar);
+
+ return INT2FIX((*bar).foo);
+}
+
+VALUE sws_get_struct_rdata(VALUE self, VALUE obj) {
+ struct sample_wrapped_struct* bar;
+ bar = (struct sample_wrapped_struct*) RDATA(obj)->data;
+ return INT2FIX(bar->foo);
+}
+
+VALUE sws_get_struct_data_ptr(VALUE self, VALUE obj) {
+ struct sample_wrapped_struct* bar;
+ bar = (struct sample_wrapped_struct*) DATA_PTR(obj);
+ return INT2FIX(bar->foo);
+}
+
+VALUE sws_change_struct(VALUE self, VALUE obj, VALUE new_val) {
+ struct sample_wrapped_struct *old_struct, *new_struct;
+ new_struct = malloc(sizeof(struct sample_wrapped_struct));
+ new_struct->foo = FIX2INT(new_val);
+ old_struct = RDATA(obj)->data;
+ free(old_struct);
+ RDATA(obj)->data = new_struct;
+ return Qnil;
+}
+#endif
+
+void Init_data_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiAllocSpecs", rb_cObject);
+
+#if defined(HAVE_RDATA) && defined(HAVE_DATA_WRAP_STRUCT)
+ rb_define_alloc_func(cls, sdaf_alloc_func);
+ rb_define_method(cls, "wrapped_data", sdaf_get_struct, 0);
+
+ cls = rb_define_class("CApiWrappedStructSpecs", rb_cObject);
+ rb_define_method(cls, "wrap_struct", sws_wrap_struct, 1);
+ rb_define_method(cls, "get_struct", sws_get_struct, 1);
+ rb_define_method(cls, "get_struct_rdata", sws_get_struct_rdata, 1);
+ rb_define_method(cls, "get_struct_data_ptr", sws_get_struct_data_ptr, 1);
+ rb_define_method(cls, "change_struct", sws_change_struct, 2);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/encoding_spec.c b/spec/ruby/optional/capi/ext/encoding_spec.c
new file mode 100644
index 0000000000..9ab893b86a
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/encoding_spec.c
@@ -0,0 +1,417 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#include "ruby/encoding.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_ENC_CODERANGE_ASCIIONLY
+static VALUE encoding_spec_ENC_CODERANGE_ASCIIONLY(VALUE self, VALUE obj) {
+ if(ENC_CODERANGE_ASCIIONLY(obj)) {
+ return Qtrue;
+ } else {
+ return Qfalse;
+ }
+}
+#endif
+
+#ifdef HAVE_RB_USASCII_ENCODING
+static VALUE encoding_spec_rb_usascii_encoding(VALUE self) {
+ return rb_str_new2(rb_usascii_encoding()->name);
+}
+#endif
+
+#ifdef HAVE_RB_USASCII_ENCINDEX
+static VALUE encoding_spec_rb_usascii_encindex(VALUE self) {
+ return INT2NUM(rb_usascii_encindex());
+}
+#endif
+
+#ifdef HAVE_RB_ASCII8BIT_ENCODING
+static VALUE encoding_spec_rb_ascii8bit_encoding(VALUE self) {
+ return rb_str_new2(rb_ascii8bit_encoding()->name);
+}
+#endif
+
+#ifdef HAVE_RB_ASCII8BIT_ENCINDEX
+static VALUE encoding_spec_rb_ascii8bit_encindex(VALUE self) {
+ return INT2NUM(rb_ascii8bit_encindex());
+}
+#endif
+
+#ifdef HAVE_RB_UTF8_ENCODING
+static VALUE encoding_spec_rb_utf8_encoding(VALUE self) {
+ return rb_str_new2(rb_utf8_encoding()->name);
+}
+#endif
+
+#ifdef HAVE_RB_UTF8_ENCINDEX
+static VALUE encoding_spec_rb_utf8_encindex(VALUE self) {
+ return INT2NUM(rb_utf8_encindex());
+}
+#endif
+
+#ifdef HAVE_RB_LOCALE_ENCODING
+static VALUE encoding_spec_rb_locale_encoding(VALUE self) {
+ return rb_str_new2(rb_locale_encoding()->name);
+}
+#endif
+
+#ifdef HAVE_RB_LOCALE_ENCINDEX
+static VALUE encoding_spec_rb_locale_encindex(VALUE self) {
+ return INT2NUM(rb_locale_encindex());
+}
+#endif
+
+#ifdef HAVE_RB_FILESYSTEM_ENCODING
+static VALUE encoding_spec_rb_filesystem_encoding(VALUE self) {
+ return rb_str_new2(rb_filesystem_encoding()->name);
+}
+#endif
+
+#ifdef HAVE_RB_FILESYSTEM_ENCINDEX
+static VALUE encoding_spec_rb_filesystem_encindex(VALUE self) {
+ return INT2NUM(rb_filesystem_encindex());
+}
+#endif
+
+#ifdef HAVE_RB_DEFAULT_INTERNAL_ENCODING
+static VALUE encoding_spec_rb_default_internal_encoding(VALUE self) {
+ rb_encoding* enc = rb_default_internal_encoding();
+ if(enc == 0) return Qnil;
+ return rb_str_new2(enc->name);
+}
+#endif
+
+#ifdef HAVE_RB_DEFAULT_EXTERNAL_ENCODING
+static VALUE encoding_spec_rb_default_external_encoding(VALUE self) {
+ rb_encoding* enc = rb_default_external_encoding();
+ if(enc == 0) return Qnil;
+ return rb_str_new2(enc->name);
+}
+#endif
+
+#ifdef HAVE_RB_ENC_ALIAS
+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
+
+#if defined(HAVE_RB_ENC_ASSOCIATE) && defined(HAVE_RB_ENC_FIND)
+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)));
+}
+#endif
+
+#if defined(HAVE_RB_ENC_ASSOCIATE_INDEX) && defined(HAVE_RB_ENC_FIND_INDEX)
+static VALUE encoding_spec_rb_enc_associate_index(VALUE self, VALUE obj, VALUE index) {
+ return rb_enc_associate_index(obj, FIX2INT(index));
+}
+#endif
+
+#ifdef HAVE_RB_ENC_COMPATIBLE
+static VALUE encoding_spec_rb_enc_compatible(VALUE self, VALUE a, VALUE b) {
+ rb_encoding* enc = rb_enc_compatible(a, b);
+
+ if(!enc) return INT2FIX(0);
+
+ return rb_enc_from_encoding(enc);
+}
+#endif
+
+#ifdef HAVE_RB_ENC_COPY
+static VALUE encoding_spec_rb_enc_copy(VALUE self, VALUE dest, VALUE src) {
+ rb_enc_copy(dest, src);
+ return dest;
+}
+#endif
+
+#ifdef HAVE_RB_ENC_FIND
+static VALUE encoding_spec_rb_enc_find(VALUE self, VALUE name) {
+ return rb_str_new2(rb_enc_find(RSTRING_PTR(name))->name);
+}
+#endif
+
+#ifdef HAVE_RB_ENC_FIND_INDEX
+static VALUE encoding_spec_rb_enc_find_index(VALUE self, VALUE name) {
+ return INT2NUM(rb_enc_find_index(RSTRING_PTR(name)));
+}
+#endif
+
+#ifdef HAVE_RB_ENC_FROM_INDEX
+static VALUE encoding_spec_rb_enc_from_index(VALUE self, VALUE index) {
+ return rb_str_new2(rb_enc_from_index(NUM2INT(index))->name);
+}
+#endif
+
+#ifdef HAVE_RB_ENC_FROM_ENCODING
+static VALUE encoding_spec_rb_enc_from_encoding(VALUE self, VALUE name) {
+ return rb_enc_from_encoding(rb_enc_find(RSTRING_PTR(name)));
+}
+#endif
+
+#ifdef HAVE_RB_ENC_GET
+static VALUE encoding_spec_rb_enc_get(VALUE self, VALUE obj) {
+ return rb_str_new2(rb_enc_get(obj)->name);
+}
+#endif
+
+#ifdef HAVE_RB_OBJ_ENCODING
+static VALUE encoding_spec_rb_obj_encoding(VALUE self, VALUE obj) {
+ return rb_obj_encoding(obj);
+}
+#endif
+
+#ifdef HAVE_RB_ENC_GET_INDEX
+static VALUE encoding_spec_rb_enc_get_index(VALUE self, VALUE obj) {
+ return INT2NUM(rb_enc_get_index(obj));
+}
+#endif
+
+#if defined(HAVE_RB_ENC_SET_INDEX) && defined(HAVE_RB_ENC_FROM_INDEX)
+static VALUE encoding_spec_rb_enc_set_index(VALUE self, VALUE obj, VALUE index) {
+ int i = NUM2INT(index);
+
+ rb_encoding* enc = rb_enc_from_index(i);
+ rb_enc_set_index(obj, i);
+
+ return rb_ary_new3(2, rb_str_new2(rb_enc_name(enc)),
+ rb_str_new2(rb_enc_name(rb_enc_get(obj))));
+}
+#endif
+
+#ifdef HAVE_RB_ENC_STR_CODERANGE
+static VALUE encoding_spec_rb_enc_str_coderange(VALUE self, VALUE str) {
+ int coderange = rb_enc_str_coderange(str);
+
+ switch(coderange) {
+ case ENC_CODERANGE_UNKNOWN:
+ return ID2SYM(rb_intern("coderange_unknown"));
+ case ENC_CODERANGE_7BIT:
+ return ID2SYM(rb_intern("coderange_7bit"));
+ case ENC_CODERANGE_VALID:
+ return ID2SYM(rb_intern("coderange_valid"));
+ case ENC_CODERANGE_BROKEN:
+ return ID2SYM(rb_intern("coderange_broken"));
+ default:
+ return ID2SYM(rb_intern("coderange_unrecognized"));
+ }
+}
+#endif
+
+#ifdef HAVE_RB_ENC_STR_NEW
+static VALUE encoding_spec_rb_enc_str_new(VALUE self, VALUE str, VALUE len, VALUE enc) {
+ return rb_enc_str_new(RSTRING_PTR(str), FIX2INT(len), rb_to_encoding(enc));
+}
+#endif
+
+#ifdef HAVE_ENCODING_GET
+static VALUE encoding_spec_ENCODING_GET(VALUE self, VALUE obj) {
+ return INT2NUM(ENCODING_GET(obj));
+}
+#endif
+
+#ifdef HAVE_ENCODING_SET
+static VALUE encoding_spec_ENCODING_SET(VALUE self, VALUE obj, VALUE index) {
+ int i = NUM2INT(index);
+
+ rb_encoding* enc = rb_enc_from_index(i);
+ ENCODING_SET(obj, i);
+
+ return rb_ary_new3(2, rb_str_new2(rb_enc_name(enc)),
+ rb_str_new2(rb_enc_name(rb_enc_get(obj))));
+}
+#endif
+
+#if defined(HAVE_RB_ENC_TO_INDEX) && defined(HAVE_RB_ENC_FIND)
+static VALUE encoding_spec_rb_enc_to_index(VALUE self, VALUE name) {
+ return INT2NUM(rb_enc_to_index(NIL_P(name) ? NULL : rb_enc_find(RSTRING_PTR(name))));
+}
+#endif
+
+#ifdef HAVE_RB_TO_ENCODING
+static VALUE encoding_spec_rb_to_encoding(VALUE self, VALUE obj) {
+ return rb_str_new2(rb_to_encoding(obj)->name);
+}
+#endif
+
+#ifdef HAVE_RB_TO_ENCODING_INDEX
+static VALUE encoding_spec_rb_to_encoding_index(VALUE self, VALUE obj) {
+ return INT2NUM(rb_to_encoding_index(obj));
+}
+#endif
+
+#ifdef HAVE_RB_ENC_NTH
+static VALUE encoding_spec_rb_enc_nth(VALUE self, VALUE str, VALUE index) {
+ char* start = RSTRING_PTR(str);
+ char* end = start + RSTRING_LEN(str);
+ char* ptr = rb_enc_nth(start, end, FIX2LONG(index), rb_enc_get(str));
+ return LONG2NUM(ptr - start);
+}
+#endif
+
+#ifdef HAVE_RB_ENC_CODEPOINT_LEN
+static VALUE encoding_spec_rb_enc_codepoint_len(VALUE self, VALUE str) {
+ char* start = RSTRING_PTR(str);
+ char* end = start + RSTRING_LEN(str);
+
+ int len;
+ unsigned int codepoint = rb_enc_codepoint_len(start, end, &len, rb_enc_get(str));
+
+ return rb_ary_new3(2, LONG2NUM(codepoint), LONG2NUM(len));
+}
+#endif
+
+void Init_encoding_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiEncodingSpecs", rb_cObject);
+
+#ifdef HAVE_ENC_CODERANGE_ASCIIONLY
+ rb_define_method(cls, "ENC_CODERANGE_ASCIIONLY",
+ encoding_spec_ENC_CODERANGE_ASCIIONLY, 1);
+#endif
+
+#ifdef HAVE_RB_USASCII_ENCODING
+ rb_define_method(cls, "rb_usascii_encoding", encoding_spec_rb_usascii_encoding, 0);
+#endif
+
+#ifdef HAVE_RB_USASCII_ENCINDEX
+ rb_define_method(cls, "rb_usascii_encindex", encoding_spec_rb_usascii_encindex, 0);
+#endif
+
+#ifdef HAVE_RB_ASCII8BIT_ENCODING
+ rb_define_method(cls, "rb_ascii8bit_encoding", encoding_spec_rb_ascii8bit_encoding, 0);
+#endif
+
+#ifdef HAVE_RB_ASCII8BIT_ENCINDEX
+ rb_define_method(cls, "rb_ascii8bit_encindex", encoding_spec_rb_ascii8bit_encindex, 0);
+#endif
+
+#ifdef HAVE_RB_UTF8_ENCODING
+ rb_define_method(cls, "rb_utf8_encoding", encoding_spec_rb_utf8_encoding, 0);
+#endif
+
+#ifdef HAVE_RB_UTF8_ENCINDEX
+ rb_define_method(cls, "rb_utf8_encindex", encoding_spec_rb_utf8_encindex, 0);
+#endif
+
+#ifdef HAVE_RB_LOCALE_ENCODING
+ rb_define_method(cls, "rb_locale_encoding", encoding_spec_rb_locale_encoding, 0);
+#endif
+
+#ifdef HAVE_RB_LOCALE_ENCINDEX
+ rb_define_method(cls, "rb_locale_encindex", encoding_spec_rb_locale_encindex, 0);
+#endif
+
+#ifdef HAVE_RB_FILESYSTEM_ENCODING
+ rb_define_method(cls, "rb_filesystem_encoding", encoding_spec_rb_filesystem_encoding, 0);
+#endif
+
+#ifdef HAVE_RB_FILESYSTEM_ENCINDEX
+ rb_define_method(cls, "rb_filesystem_encindex", encoding_spec_rb_filesystem_encindex, 0);
+#endif
+
+#ifdef HAVE_RB_DEFAULT_INTERNAL_ENCODING
+ rb_define_method(cls, "rb_default_internal_encoding",
+ encoding_spec_rb_default_internal_encoding, 0);
+#endif
+
+#ifdef HAVE_RB_DEFAULT_EXTERNAL_ENCODING
+ rb_define_method(cls, "rb_default_external_encoding",
+ encoding_spec_rb_default_external_encoding, 0);
+#endif
+
+#ifdef HAVE_RB_ENC_ALIAS
+ rb_define_method(cls, "rb_enc_alias", encoding_spec_rb_enc_alias, 2);
+#endif
+
+#ifdef HAVE_RB_ENC_ASSOCIATE
+ rb_define_method(cls, "rb_enc_associate", encoding_spec_rb_enc_associate, 2);
+#endif
+
+#ifdef HAVE_RB_ENC_ASSOCIATE_INDEX
+ rb_define_method(cls, "rb_enc_associate_index", encoding_spec_rb_enc_associate_index, 2);
+#endif
+
+#ifdef HAVE_RB_ENC_COMPATIBLE
+ rb_define_method(cls, "rb_enc_compatible", encoding_spec_rb_enc_compatible, 2);
+#endif
+
+#ifdef HAVE_RB_ENC_COPY
+ rb_define_method(cls, "rb_enc_copy", encoding_spec_rb_enc_copy, 2);
+#endif
+
+#ifdef HAVE_RB_ENC_FIND
+ rb_define_method(cls, "rb_enc_find", encoding_spec_rb_enc_find, 1);
+#endif
+
+#ifdef HAVE_RB_ENC_FIND_INDEX
+ rb_define_method(cls, "rb_enc_find_index", encoding_spec_rb_enc_find_index, 1);
+#endif
+
+#ifdef HAVE_RB_ENC_FROM_INDEX
+ rb_define_method(cls, "rb_enc_from_index", encoding_spec_rb_enc_from_index, 1);
+#endif
+
+#ifdef HAVE_RB_ENC_FROM_ENCODING
+ rb_define_method(cls, "rb_enc_from_encoding", encoding_spec_rb_enc_from_encoding, 1);
+#endif
+
+#ifdef HAVE_RB_ENC_GET
+ rb_define_method(cls, "rb_enc_get", encoding_spec_rb_enc_get, 1);
+#endif
+
+#ifdef HAVE_RB_OBJ_ENCODING
+ rb_define_method(cls, "rb_obj_encoding", encoding_spec_rb_obj_encoding, 1);
+#endif
+
+#ifdef HAVE_RB_ENC_GET_INDEX
+ rb_define_method(cls, "rb_enc_get_index", encoding_spec_rb_enc_get_index, 1);
+#endif
+
+#if defined(HAVE_RB_ENC_SET_INDEX) && defined(HAVE_RB_ENC_FROM_INDEX)
+ rb_define_method(cls, "rb_enc_set_index", encoding_spec_rb_enc_set_index, 2);
+#endif
+
+#ifdef HAVE_RB_ENC_STR_CODERANGE
+ rb_define_method(cls, "rb_enc_str_coderange", encoding_spec_rb_enc_str_coderange, 1);
+#endif
+
+#ifdef HAVE_RB_ENC_STR_NEW
+ rb_define_method(cls, "rb_enc_str_new", encoding_spec_rb_enc_str_new, 3);
+#endif
+
+#ifdef HAVE_ENCODING_GET
+ rb_define_method(cls, "ENCODING_GET", encoding_spec_ENCODING_GET, 1);
+#endif
+
+#ifdef HAVE_ENCODING_SET
+ rb_define_method(cls, "ENCODING_SET", encoding_spec_ENCODING_SET, 2);
+#endif
+
+#if defined(HAVE_RB_ENC_TO_INDEX) && defined(HAVE_RB_ENC_FIND)
+ rb_define_method(cls, "rb_enc_to_index", encoding_spec_rb_enc_to_index, 1);
+#endif
+
+#ifdef HAVE_RB_TO_ENCODING
+ rb_define_method(cls, "rb_to_encoding", encoding_spec_rb_to_encoding, 1);
+#endif
+
+#ifdef HAVE_RB_TO_ENCODING_INDEX
+ rb_define_method(cls, "rb_to_encoding_index", encoding_spec_rb_to_encoding_index, 1);
+#endif
+
+#ifdef HAVE_RB_ENC_NTH
+ rb_define_method(cls, "rb_enc_nth", encoding_spec_rb_enc_nth, 2);
+#endif
+
+#ifdef HAVE_RB_ENC_CODEPOINT_LEN
+ rb_define_method(cls, "rb_enc_codepoint_len", encoding_spec_rb_enc_codepoint_len, 1);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/enumerator_spec.c b/spec/ruby/optional/capi/ext/enumerator_spec.c
new file mode 100644
index 0000000000..d9e34afd96
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/enumerator_spec.c
@@ -0,0 +1,42 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_ENUMERATORIZE
+VALUE enumerator_spec_rb_enumeratorize(int argc, VALUE *argv, VALUE self) {
+ VALUE obj, meth, args;
+ rb_scan_args(argc, argv, "2*", &obj, &meth, &args);
+ return rb_enumeratorize(obj, meth, (int)RARRAY_LEN(args), RARRAY_PTR(args));
+}
+#endif
+
+#ifdef HAVE_RB_ENUMERATORIZE_WITH_SIZE
+VALUE enumerator_spec_size_fn(VALUE obj, VALUE args, VALUE anEnum) {
+ return INT2NUM(7);
+}
+
+VALUE enumerator_spec_rb_enumeratorize_with_size(int argc, VALUE *argv, VALUE self) {
+ VALUE obj, meth, args;
+ rb_scan_args(argc, argv, "2*", &obj, &meth, &args);
+ return rb_enumeratorize_with_size(obj, meth, (int)RARRAY_LEN(args), RARRAY_PTR(args), enumerator_spec_size_fn);
+}
+#endif
+
+void Init_enumerator_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiEnumeratorSpecs", rb_cObject);
+
+#ifdef HAVE_RB_ENUMERATORIZE
+ rb_define_method(cls, "rb_enumeratorize", enumerator_spec_rb_enumeratorize, -1);
+#endif
+#ifdef HAVE_RB_ENUMERATORIZE_WITH_SIZE
+ rb_define_method(cls, "rb_enumeratorize_with_size", enumerator_spec_rb_enumeratorize_with_size, -1);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/exception_spec.c b/spec/ruby/optional/capi/ext/exception_spec.c
new file mode 100644
index 0000000000..b37f74f03e
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/exception_spec.c
@@ -0,0 +1,72 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_EXC_NEW
+VALUE exception_spec_rb_exc_new(VALUE self, VALUE str) {
+ char *cstr = StringValuePtr(str);
+ return rb_exc_new(rb_eException, cstr, strlen(cstr));
+}
+#endif
+
+#ifdef HAVE_RB_EXC_NEW2
+VALUE exception_spec_rb_exc_new2(VALUE self, VALUE str) {
+ char *cstr = StringValuePtr(str);
+ return rb_exc_new2(rb_eException, cstr);
+}
+#endif
+
+#ifdef HAVE_RB_EXC_NEW3
+VALUE exception_spec_rb_exc_new3(VALUE self, VALUE str) {
+ return rb_exc_new3(rb_eException, str);
+}
+#endif
+
+#ifdef HAVE_RB_EXC_RAISE
+VALUE exception_spec_rb_exc_raise(VALUE self, VALUE exc) {
+ if (self != Qundef) rb_exc_raise(exc);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_SET_ERRINFO
+VALUE exception_spec_rb_set_errinfo(VALUE self, VALUE exc) {
+ rb_set_errinfo(exc);
+ return Qnil;
+}
+#endif
+
+void Init_exception_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiExceptionSpecs", rb_cObject);
+
+#ifdef HAVE_RB_EXC_NEW
+ rb_define_method(cls, "rb_exc_new", exception_spec_rb_exc_new, 1);
+#endif
+
+#ifdef HAVE_RB_EXC_NEW2
+ rb_define_method(cls, "rb_exc_new2", exception_spec_rb_exc_new2, 1);
+#endif
+
+#ifdef HAVE_RB_EXC_NEW3
+ rb_define_method(cls, "rb_exc_new3", exception_spec_rb_exc_new3, 1);
+#endif
+
+#ifdef HAVE_RB_EXC_RAISE
+ rb_define_method(cls, "rb_exc_raise", exception_spec_rb_exc_raise, 1);
+#endif
+
+#ifdef HAVE_RB_SET_ERRINFO
+ rb_define_method(cls, "rb_set_errinfo", exception_spec_rb_set_errinfo, 1);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/file_spec.c b/spec/ruby/optional/capi/ext/file_spec.c
new file mode 100644
index 0000000000..98f8db1595
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/file_spec.c
@@ -0,0 +1,44 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_FILE_OPEN
+VALUE file_spec_rb_file_open(VALUE self, VALUE name, VALUE mode) {
+ return rb_file_open(RSTRING_PTR(name), RSTRING_PTR(mode));
+}
+#endif
+
+#ifdef HAVE_RB_FILE_OPEN_STR
+VALUE file_spec_rb_file_open_str(VALUE self, VALUE name, VALUE mode) {
+ return rb_file_open_str(name, RSTRING_PTR(mode));
+}
+#endif
+
+#ifdef HAVE_FILEPATHVALUE
+VALUE file_spec_FilePathValue(VALUE self, VALUE obj) {
+ return FilePathValue(obj);
+}
+#endif
+
+void Init_file_spec(void) {
+ VALUE cls = rb_define_class("CApiFileSpecs", rb_cObject);
+
+#ifdef HAVE_RB_FILE_OPEN
+ rb_define_method(cls, "rb_file_open", file_spec_rb_file_open, 2);
+#endif
+
+#ifdef HAVE_RB_FILE_OPEN_STR
+ rb_define_method(cls, "rb_file_open_str", file_spec_rb_file_open_str, 2);
+#endif
+
+#ifdef HAVE_FILEPATHVALUE
+ rb_define_method(cls, "FilePathValue", file_spec_FilePathValue, 1);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/fixnum_spec.c b/spec/ruby/optional/capi/ext/fixnum_spec.c
new file mode 100644
index 0000000000..c3a207b387
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/fixnum_spec.c
@@ -0,0 +1,52 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static VALUE fixnum_spec_FIX2INT(VALUE self, VALUE value) {
+ int i = FIX2INT(value);
+ return INT2NUM(i);
+}
+
+static VALUE fixnum_spec_FIX2UINT(VALUE self, VALUE value) {
+ unsigned int i = FIX2UINT(value);
+ return UINT2NUM(i);
+}
+
+#ifdef HAVE_RB_FIX2UINT
+static VALUE fixnum_spec_rb_fix2uint(VALUE self, VALUE value) {
+ unsigned long i = rb_fix2uint(value);
+ return ULONG2NUM(i);
+}
+#endif
+
+#ifdef HAVE_RB_FIX2INT
+static VALUE fixnum_spec_rb_fix2int(VALUE self, VALUE value) {
+ long i = rb_fix2int(value);
+ return LONG2NUM(i);
+}
+#endif
+
+void Init_fixnum_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiFixnumSpecs", rb_cObject);
+
+ rb_define_method(cls, "FIX2INT", fixnum_spec_FIX2INT, 1);
+ rb_define_method(cls, "FIX2UINT", fixnum_spec_FIX2UINT, 1);
+
+#ifdef HAVE_RB_FIX2UINT
+ rb_define_method(cls, "rb_fix2uint", fixnum_spec_rb_fix2uint, 1);
+#endif
+
+#ifdef HAVE_RB_FIX2INT
+ rb_define_method(cls, "rb_fix2int", fixnum_spec_rb_fix2int, 1);
+#endif
+
+ (void)cls;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/float_spec.c b/spec/ruby/optional/capi/ext/float_spec.c
new file mode 100644
index 0000000000..15c74e62c9
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/float_spec.c
@@ -0,0 +1,54 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#include <math.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_FLOAT_NEW
+static VALUE float_spec_new_zero(VALUE self) {
+ double flt = 0;
+ return rb_float_new(flt);
+}
+
+static VALUE float_spec_new_point_five(VALUE self) {
+ double flt = 0.555;
+ return rb_float_new(flt);
+}
+#endif
+
+#ifdef HAVE_RB_RFLOAT
+static VALUE float_spec_rb_Float(VALUE self, VALUE float_str) {
+ return rb_Float(float_str);
+}
+#endif
+
+#ifdef HAVE_RFLOAT_VALUE
+static VALUE float_spec_RFLOAT_VALUE(VALUE self, VALUE float_h) {
+ return rb_float_new(RFLOAT_VALUE(float_h));
+}
+#endif
+
+void Init_float_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiFloatSpecs", rb_cObject);
+
+#ifdef HAVE_RB_FLOAT_NEW
+ rb_define_method(cls, "new_zero", float_spec_new_zero, 0);
+ rb_define_method(cls, "new_point_five", float_spec_new_point_five, 0);
+#endif
+
+#ifdef HAVE_RB_RFLOAT
+ rb_define_method(cls, "rb_Float", float_spec_rb_Float, 1);
+#endif
+
+#ifdef HAVE_RFLOAT_VALUE
+ rb_define_method(cls, "RFLOAT_VALUE", float_spec_RFLOAT_VALUE, 1);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/gc_spec.c b/spec/ruby/optional/capi/ext/gc_spec.c
new file mode 100644
index 0000000000..05341bb01d
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/gc_spec.c
@@ -0,0 +1,72 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_GC_REGISTER_ADDRESS
+VALUE registered_tagged_value;
+VALUE registered_reference_value;
+
+static VALUE registered_tagged_address(VALUE self) {
+ return registered_tagged_value;
+}
+
+static VALUE registered_reference_address(VALUE self) {
+ return registered_reference_value;
+}
+#endif
+
+#ifdef HAVE_RB_GC_ENABLE
+static VALUE gc_spec_rb_gc_enable() {
+ return rb_gc_enable();
+}
+#endif
+
+#ifdef HAVE_RB_GC_DISABLE
+static VALUE gc_spec_rb_gc_disable() {
+ return rb_gc_disable();
+}
+#endif
+
+#ifdef HAVE_RB_GC
+static VALUE gc_spec_rb_gc() {
+ rb_gc();
+ return Qnil;
+}
+#endif
+
+
+void Init_gc_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiGCSpecs", rb_cObject);
+
+#ifdef HAVE_RB_GC_REGISTER_ADDRESS
+ 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_define_method(cls, "registered_tagged_address", registered_tagged_address, 0);
+ rb_define_method(cls, "registered_reference_address", registered_reference_address, 0);
+#endif
+
+#ifdef HAVE_RB_GC_ENABLE
+ rb_define_method(cls, "rb_gc_enable", gc_spec_rb_gc_enable, 0);
+#endif
+
+#ifdef HAVE_RB_GC_DISABLE
+ rb_define_method(cls, "rb_gc_disable", gc_spec_rb_gc_disable, 0);
+#endif
+
+#ifdef HAVE_RB_GC
+ rb_define_method(cls, "rb_gc", gc_spec_rb_gc, 0);
+#endif
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/globals_spec.c b/spec/ruby/optional/capi/ext/globals_spec.c
new file mode 100644
index 0000000000..0c28a7f8ab
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/globals_spec.c
@@ -0,0 +1,199 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_DEFINE_HOOKED_VARIABLE
+VALUE g_hooked_var;
+
+void var_2x_setter(VALUE val, ID id, VALUE *var) {
+ *var = INT2NUM(NUM2INT(val) * 2);
+}
+
+static VALUE sb_define_hooked_variable(VALUE self, VALUE var_name) {
+ rb_define_hooked_variable(StringValuePtr(var_name), &g_hooked_var, 0, var_2x_setter);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_READONLY_VARIABLE
+VALUE g_ro_var;
+
+static VALUE sb_define_readonly_variable(VALUE self, VALUE var_name, VALUE val) {
+ g_ro_var = val;
+ rb_define_readonly_variable(StringValuePtr(var_name), &g_ro_var);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_VARIABLE
+VALUE g_var;
+
+static VALUE sb_get_global_value(VALUE self) {
+ return g_var;
+}
+
+static VALUE sb_define_variable(VALUE self, VALUE var_name, VALUE val) {
+ g_var = val;
+ rb_define_variable(StringValuePtr(var_name), &g_var);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_F_GLOBAL_VARIABLES
+static VALUE sb_f_global_variables(VALUE self) {
+ return rb_f_global_variables();
+}
+#endif
+
+#ifdef HAVE_RB_GV_GET
+static VALUE sb_gv_get(VALUE self, VALUE var) {
+ return rb_gv_get(StringValuePtr(var));
+}
+#endif
+
+#ifdef HAVE_RB_GV_SET
+static VALUE sb_gv_set(VALUE self, VALUE var, VALUE val) {
+ return rb_gv_set(StringValuePtr(var), val);
+}
+#endif
+
+#ifdef HAVE_RB_STDIN
+static VALUE global_spec_rb_stdin(VALUE self) {
+ return rb_stdin;
+}
+#endif
+
+#ifdef HAVE_RB_STDOUT
+static VALUE global_spec_rb_stdout(VALUE self) {
+ return rb_stdout;
+}
+#endif
+
+#ifdef HAVE_RB_STDERR
+static VALUE global_spec_rb_stderr(VALUE self) {
+ return rb_stderr;
+}
+#endif
+
+#ifdef HAVE_RB_DEFOUT
+static VALUE global_spec_rb_defout(VALUE self) {
+ return rb_defout;
+}
+#endif
+
+#ifdef HAVE_RB_RS
+static VALUE global_spec_rb_rs(VALUE self) {
+ return rb_rs;
+}
+#endif
+
+#ifdef HAVE_RB_DEFAULT_RS
+static VALUE global_spec_rb_default_rs(VALUE self) {
+ return rb_default_rs;
+}
+#endif
+
+#ifdef HAVE_RB_OUTPUT_RS
+static VALUE global_spec_rb_output_rs(VALUE self) {
+ return rb_output_rs;
+}
+#endif
+
+#ifdef HAVE_RB_OUTPUT_FS
+static VALUE global_spec_rb_output_fs(VALUE self) {
+ return rb_output_fs;
+}
+#endif
+
+#ifdef HAVE_RB_LASTLINE_SET
+static VALUE global_spec_rb_lastline_set(VALUE self, VALUE line) {
+ rb_lastline_set(line);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_LASTLINE_GET
+static VALUE global_spec_rb_lastline_get(VALUE self) {
+ return rb_lastline_get();
+}
+#endif
+
+void Init_globals_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiGlobalSpecs", rb_cObject);
+
+#ifdef HAVE_RB_DEFINE_HOOKED_VARIABLE
+ g_hooked_var = Qnil;
+ rb_define_method(cls, "rb_define_hooked_variable_2x", sb_define_hooked_variable, 1);
+#endif
+
+#ifdef HAVE_RB_DEFINE_READONLY_VARIABLE
+ g_ro_var = Qnil;
+ rb_define_method(cls, "rb_define_readonly_variable", sb_define_readonly_variable, 2);
+#endif
+
+#ifdef HAVE_RB_DEFINE_VARIABLE
+ g_var = Qnil;
+ rb_define_method(cls, "rb_define_variable", sb_define_variable, 2);
+ rb_define_method(cls, "sb_get_global_value", sb_get_global_value, 0);
+#endif
+
+#ifdef HAVE_RB_F_GLOBAL_VARIABLES
+ rb_define_method(cls, "rb_f_global_variables", sb_f_global_variables, 0);
+#endif
+
+#ifdef HAVE_RB_GV_GET
+ rb_define_method(cls, "sb_gv_get", sb_gv_get, 1);
+#endif
+
+#ifdef HAVE_RB_GV_SET
+ rb_define_method(cls, "sb_gv_set", sb_gv_set, 2);
+#endif
+
+#ifdef HAVE_RB_STDIN
+ rb_define_method(cls, "rb_stdin", global_spec_rb_stdin, 0);
+#endif
+
+#ifdef HAVE_RB_STDOUT
+ rb_define_method(cls, "rb_stdout", global_spec_rb_stdout, 0);
+#endif
+
+#ifdef HAVE_RB_STDERR
+ rb_define_method(cls, "rb_stderr", global_spec_rb_stderr, 0);
+#endif
+
+#ifdef HAVE_RB_DEFOUT
+ rb_define_method(cls, "rb_defout", global_spec_rb_defout, 0);
+#endif
+
+#ifdef HAVE_RB_RS
+ rb_define_method(cls, "rb_rs", global_spec_rb_rs, 0);
+#endif
+
+#ifdef HAVE_RB_DEFAULT_RS
+ rb_define_method(cls, "rb_default_rs", global_spec_rb_default_rs, 0);
+#endif
+
+#ifdef HAVE_RB_OUTPUT_RS
+ rb_define_method(cls, "rb_output_rs", global_spec_rb_output_rs, 0);
+#endif
+
+#ifdef HAVE_RB_OUTPUT_FS
+ rb_define_method(cls, "rb_output_fs", global_spec_rb_output_fs, 0);
+#endif
+
+#ifdef HAVE_RB_LASTLINE_SET
+ rb_define_method(cls, "rb_lastline_set", global_spec_rb_lastline_set, 1);
+#endif
+
+#ifdef HAVE_RB_LASTLINE_GET
+ rb_define_method(cls, "rb_lastline_get", global_spec_rb_lastline_get, 0);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/hash_spec.c b/spec/ruby/optional/capi/ext/hash_spec.c
new file mode 100644
index 0000000000..73e7ef5c13
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/hash_spec.c
@@ -0,0 +1,218 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_HASH
+VALUE hash_spec_rb_hash(VALUE self, VALUE hash) {
+ return rb_hash(hash);
+}
+#endif
+
+#ifdef HAVE_RB_HASH2
+VALUE hash_spec_rb_Hash(VALUE self, VALUE val) {
+ return rb_Hash(val);
+}
+#endif
+
+#ifdef HAVE_RB_HASH_DUP
+VALUE hash_spec_rb_hash_dup(VALUE self, VALUE hash) {
+ return rb_hash_dup(hash);
+}
+#endif
+
+#ifdef HAVE_RB_HASH_FETCH
+VALUE hash_spec_rb_hash_fetch(VALUE self, VALUE hash, VALUE key) {
+ return rb_hash_fetch(hash, key);
+}
+#endif
+
+#ifdef HAVE_RB_HASH_FREEZE
+VALUE hash_spec_rb_hash_freeze(VALUE self, VALUE hash) {
+ return rb_hash_freeze(hash);
+}
+#endif
+
+#ifdef HAVE_RB_HASH_AREF
+VALUE hash_spec_rb_hash_aref(VALUE self, VALUE hash, VALUE key) {
+ return rb_hash_aref(hash, key);
+}
+
+VALUE hash_spec_rb_hash_aref_nil(VALUE self, VALUE hash, VALUE key) {
+ VALUE ret = rb_hash_aref(hash, key);
+ return NIL_P(ret) ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_HASH_ASET
+VALUE hash_spec_rb_hash_aset(VALUE self, VALUE hash, VALUE key, VALUE val) {
+ return rb_hash_aset(hash, key, val);
+}
+#endif
+
+#ifdef HAVE_RB_HASH_CLEAR
+VALUE hash_spec_rb_hash_clear(VALUE self, VALUE hash) {
+ return rb_hash_clear(hash);
+}
+#endif
+
+#ifdef HAVE_RB_HASH_DELETE
+VALUE hash_spec_rb_hash_delete(VALUE self, VALUE hash, VALUE key) {
+ return rb_hash_delete(hash, key);
+}
+#endif
+
+#ifdef HAVE_RB_HASH_DELETE_IF
+VALUE hash_spec_rb_hash_delete_if(VALUE self, VALUE hash) {
+ return rb_hash_delete_if(hash);
+}
+#endif
+
+#ifdef HAVE_RB_HASH_FOREACH
+static int foreach_i(VALUE key, VALUE val, VALUE other) {
+ rb_hash_aset(other, key, val);
+ return 0; /* ST_CONTINUE; */
+}
+
+static int foreach_stop_i(VALUE key, VALUE val, VALUE other) {
+ rb_hash_aset(other, key, val);
+ return 1; /* ST_STOP; */
+}
+
+static int foreach_delete_i(VALUE key, VALUE val, VALUE other) {
+ rb_hash_aset(other, key, val);
+ return 2; /* ST_DELETE; */
+}
+
+VALUE hash_spec_rb_hash_foreach(VALUE self, VALUE hsh) {
+ VALUE other = rb_hash_new();
+ rb_hash_foreach(hsh, foreach_i, other);
+ return other;
+}
+
+VALUE hash_spec_rb_hash_foreach_stop(VALUE self, VALUE hsh) {
+ VALUE other = rb_hash_new();
+ rb_hash_foreach(hsh, foreach_stop_i, other);
+ return other;
+}
+
+VALUE hash_spec_rb_hash_foreach_delete(VALUE self, VALUE hsh) {
+ VALUE other = rb_hash_new();
+ rb_hash_foreach(hsh, foreach_delete_i, other);
+ return other;
+}
+#endif
+
+#ifdef HAVE_RB_HASH_LOOKUP
+VALUE hash_spec_rb_hash_lookup(VALUE self, VALUE hash, VALUE key) {
+ return rb_hash_lookup(hash, key);
+}
+
+VALUE hash_spec_rb_hash_lookup_nil(VALUE self, VALUE hash, VALUE key) {
+ VALUE ret = rb_hash_lookup(hash, key);
+ return ret == Qnil ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_HASH_LOOKUP2
+VALUE hash_spec_rb_hash_lookup2(VALUE self, VALUE hash, VALUE key, VALUE def) {
+ return rb_hash_lookup2(hash, key, def);
+}
+#endif
+
+#ifdef HAVE_RB_HASH_NEW
+VALUE hash_spec_rb_hash_new(VALUE self) {
+ return rb_hash_new();
+}
+#endif
+
+#ifdef HAVE_RB_HASH_SIZE
+VALUE hash_spec_rb_hash_size(VALUE self, VALUE hash) {
+ return rb_hash_size(hash);
+}
+#endif
+
+#ifdef HAVE_RB_HASH_SET_IFNONE
+VALUE hash_spec_rb_hash_set_ifnone(VALUE self, VALUE hash, VALUE def) {
+ return rb_hash_set_ifnone(hash, def);
+}
+#endif
+
+void Init_hash_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiHashSpecs", rb_cObject);
+
+#ifdef HAVE_RB_HASH
+ rb_define_method(cls, "rb_hash", hash_spec_rb_hash, 1);
+#endif
+
+#ifdef HAVE_RB_HASH2
+ rb_define_method(cls, "rb_Hash", hash_spec_rb_Hash, 1);
+#endif
+
+#ifdef HAVE_RB_HASH_DUP
+ rb_define_method(cls, "rb_hash_dup", hash_spec_rb_hash_dup, 1);
+#endif
+
+#ifdef HAVE_RB_HASH_FREEZE
+ rb_define_method(cls, "rb_hash_freeze", hash_spec_rb_hash_freeze, 1);
+#endif
+
+#ifdef HAVE_RB_HASH_AREF
+ rb_define_method(cls, "rb_hash_aref", hash_spec_rb_hash_aref, 2);
+ rb_define_method(cls, "rb_hash_aref_nil", hash_spec_rb_hash_aref_nil, 2);
+#endif
+
+#ifdef HAVE_RB_HASH_ASET
+ rb_define_method(cls, "rb_hash_aset", hash_spec_rb_hash_aset, 3);
+#endif
+
+#ifdef HAVE_RB_HASH_CLEAR
+ rb_define_method(cls, "rb_hash_clear", hash_spec_rb_hash_clear, 1);
+#endif
+
+#ifdef HAVE_RB_HASH_DELETE
+ rb_define_method(cls, "rb_hash_delete", hash_spec_rb_hash_delete, 2);
+#endif
+
+#ifdef HAVE_RB_HASH_DELETE_IF
+ rb_define_method(cls, "rb_hash_delete_if", hash_spec_rb_hash_delete_if, 1);
+#endif
+
+#ifdef HAVE_RB_HASH_FETCH
+ rb_define_method(cls, "rb_hash_fetch", hash_spec_rb_hash_fetch, 2);
+#endif
+
+#ifdef HAVE_RB_HASH_FOREACH
+ rb_define_method(cls, "rb_hash_foreach", hash_spec_rb_hash_foreach, 1);
+ rb_define_method(cls, "rb_hash_foreach_stop", hash_spec_rb_hash_foreach_stop, 1);
+ rb_define_method(cls, "rb_hash_foreach_delete", hash_spec_rb_hash_foreach_delete, 1);
+#endif
+
+#ifdef HAVE_RB_HASH_LOOKUP
+ rb_define_method(cls, "rb_hash_lookup_nil", hash_spec_rb_hash_lookup_nil, 2);
+ rb_define_method(cls, "rb_hash_lookup", hash_spec_rb_hash_lookup, 2);
+#endif
+
+#ifdef HAVE_RB_HASH_LOOKUP2
+ rb_define_method(cls, "rb_hash_lookup2", hash_spec_rb_hash_lookup2, 3);
+#endif
+
+#ifdef HAVE_RB_HASH_NEW
+ rb_define_method(cls, "rb_hash_new", hash_spec_rb_hash_new, 0);
+#endif
+
+#ifdef HAVE_RB_HASH_SIZE
+ rb_define_method(cls, "rb_hash_size", hash_spec_rb_hash_size, 1);
+#endif
+
+#ifdef HAVE_RB_HASH_SET_IFNONE
+ rb_define_method(cls, "rb_hash_set_ifnone", hash_spec_rb_hash_set_ifnone, 2);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/integer_spec.c b/spec/ruby/optional/capi/ext/integer_spec.c
new file mode 100644
index 0000000000..821d8373c9
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/integer_spec.c
@@ -0,0 +1,40 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_INTEGER_PACK
+static VALUE integer_spec_rb_integer_pack(VALUE self, VALUE value,
+ VALUE words, VALUE numwords, VALUE wordsize, VALUE nails, VALUE flags)
+{
+ int result = rb_integer_pack(value, (void*)RSTRING_PTR(words), FIX2INT(numwords),
+ FIX2INT(wordsize), FIX2INT(nails), FIX2INT(flags));
+ return INT2FIX(result);
+}
+#endif
+
+void Init_integer_spec(void) {
+#ifdef HAVE_RB_INTEGER_PACK
+ VALUE cls;
+ cls = rb_define_class("CApiIntegerSpecs", rb_cObject);
+
+ rb_define_const(cls, "MSWORD", INT2NUM(INTEGER_PACK_MSWORD_FIRST));
+ rb_define_const(cls, "LSWORD", INT2NUM(INTEGER_PACK_LSWORD_FIRST));
+ rb_define_const(cls, "MSBYTE", INT2NUM(INTEGER_PACK_MSBYTE_FIRST));
+ rb_define_const(cls, "LSBYTE", INT2NUM(INTEGER_PACK_LSBYTE_FIRST));
+ rb_define_const(cls, "NATIVE", INT2NUM(INTEGER_PACK_NATIVE_BYTE_ORDER));
+ rb_define_const(cls, "PACK_2COMP", INT2NUM(INTEGER_PACK_2COMP));
+ rb_define_const(cls, "LITTLE_ENDIAN", INT2NUM(INTEGER_PACK_LITTLE_ENDIAN));
+ rb_define_const(cls, "BIG_ENDIAN", INT2NUM(INTEGER_PACK_BIG_ENDIAN));
+ rb_define_const(cls, "FORCE_BIGNUM", INT2NUM(INTEGER_PACK_FORCE_BIGNUM));
+ rb_define_const(cls, "NEGATIVE", INT2NUM(INTEGER_PACK_NEGATIVE));
+
+ rb_define_method(cls, "rb_integer_pack", integer_spec_rb_integer_pack, 6);
+#endif
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
diff --git a/spec/ruby/optional/capi/ext/io_spec.c b/spec/ruby/optional/capi/ext/io_spec.c
new file mode 100644
index 0000000000..955fe11e7d
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/io_spec.c
@@ -0,0 +1,327 @@
+#include "ruby.h"
+#include "rubyspec.h"
+#include "ruby/io.h"
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static int set_non_blocking(int fd) {
+#if defined(O_NONBLOCK) && defined(F_GETFL)
+ int flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1)
+ flags = 0;
+ return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+#elif defined(FIOBIO)
+ int flags = 1;
+ return ioctl(fd, FIOBIO, &flags);
+#else
+# define SET_NON_BLOCKING_FAILS_ALWAYS 1
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+#ifdef HAVE_GET_OPEN_FILE
+static int io_spec_get_fd(VALUE io) {
+ rb_io_t* fp;
+ GetOpenFile(io, fp);
+ return fp->fd;
+}
+
+VALUE io_spec_GetOpenFile_fd(VALUE self, VALUE io) {
+ return INT2NUM(io_spec_get_fd(io));
+}
+#endif
+
+#ifdef HAVE_RB_IO_ADDSTR
+VALUE io_spec_rb_io_addstr(VALUE self, VALUE io, VALUE str) {
+ return rb_io_addstr(io, str);
+}
+#endif
+
+#ifdef HAVE_RB_IO_PRINTF
+VALUE io_spec_rb_io_printf(VALUE self, VALUE io, VALUE ary) {
+ long argc = RARRAY_LEN(ary);
+ VALUE *argv = alloca(sizeof(VALUE) * argc);
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ argv[i] = rb_ary_entry(ary, i);
+ }
+
+ return rb_io_printf((int)argc, argv, io);
+}
+#endif
+
+#ifdef HAVE_RB_IO_PRINT
+VALUE io_spec_rb_io_print(VALUE self, VALUE io, VALUE ary) {
+ long argc = RARRAY_LEN(ary);
+ VALUE *argv = alloca(sizeof(VALUE) * argc);
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ argv[i] = rb_ary_entry(ary, i);
+ }
+
+ return rb_io_print((int)argc, argv, io);
+}
+#endif
+
+#ifdef HAVE_RB_IO_PUTS
+VALUE io_spec_rb_io_puts(VALUE self, VALUE io, VALUE ary) {
+ long argc = RARRAY_LEN(ary);
+ VALUE *argv = alloca(sizeof(VALUE) * argc);
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ argv[i] = rb_ary_entry(ary, i);
+ }
+
+ return rb_io_puts((int)argc, argv, io);
+}
+#endif
+
+#ifdef HAVE_RB_IO_WRITE
+VALUE io_spec_rb_io_write(VALUE self, VALUE io, VALUE str) {
+ return rb_io_write(io, str);
+}
+#endif
+
+#ifdef HAVE_RB_IO_CHECK_IO
+VALUE io_spec_rb_io_check_io(VALUE self, VALUE io) {
+ return rb_io_check_io(io);
+}
+#endif
+
+#ifdef HAVE_RB_IO_CHECK_READABLE
+VALUE io_spec_rb_io_check_readable(VALUE self, VALUE io) {
+ rb_io_t* fp;
+ GetOpenFile(io, fp);
+ rb_io_check_readable(fp);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_IO_CHECK_WRITABLE
+VALUE io_spec_rb_io_check_writable(VALUE self, VALUE io) {
+ rb_io_t* fp;
+ GetOpenFile(io, fp);
+ rb_io_check_writable(fp);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_IO_CHECK_CLOSED
+VALUE io_spec_rb_io_check_closed(VALUE self, VALUE io) {
+ rb_io_t* fp;
+ GetOpenFile(io, fp);
+ rb_io_check_closed(fp);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_IO_TAINT_CHECK
+VALUE io_spec_rb_io_taint_check(VALUE self, VALUE io) {
+ /*rb_io_t* fp;
+ GetOpenFile(io, fp);*/
+ rb_io_taint_check(io);
+ return io;
+}
+#endif
+
+#ifdef HAVE_RB_IO_WAIT_READABLE
+#define RB_IO_WAIT_READABLE_BUF 13
+
+#if SET_NON_BLOCKING_FAILS_ALWAYS
+NORETURN(VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p));
+#endif
+
+VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p) {
+ int fd = io_spec_get_fd(io);
+# if !SET_NON_BLOCKING_FAILS_ALWAYS
+ char buf[RB_IO_WAIT_READABLE_BUF];
+ int ret, saved_errno;
+# endif
+
+ if (set_non_blocking(fd) == -1)
+ rb_sys_fail("set_non_blocking failed");
+
+# if !SET_NON_BLOCKING_FAILS_ALWAYS
+ if(RTEST(read_p)) {
+ if (read(fd, buf, RB_IO_WAIT_READABLE_BUF) != -1) {
+ return Qnil;
+ }
+ saved_errno = errno;
+ rb_ivar_set(self, rb_intern("@write_data"), Qtrue);
+ errno = saved_errno;
+ }
+
+ ret = rb_io_wait_readable(fd);
+
+ if(RTEST(read_p)) {
+ ssize_t r = read(fd, buf, RB_IO_WAIT_READABLE_BUF);
+ if (r != RB_IO_WAIT_READABLE_BUF) {
+ perror("read");
+ return SSIZET2NUM(r);
+ }
+ rb_ivar_set(self, rb_intern("@read_data"),
+ rb_str_new(buf, RB_IO_WAIT_READABLE_BUF));
+ }
+
+ return ret ? Qtrue : Qfalse;
+# else
+ UNREACHABLE;
+# endif
+}
+#endif
+
+#ifdef HAVE_RB_IO_WAIT_WRITABLE
+VALUE io_spec_rb_io_wait_writable(VALUE self, VALUE io) {
+ int ret = rb_io_wait_writable(io_spec_get_fd(io));
+ return ret ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_THREAD_WAIT_FD
+VALUE io_spec_rb_thread_wait_fd(VALUE self, VALUE io) {
+ rb_thread_wait_fd(io_spec_get_fd(io));
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_THREAD_FD_WRITABLE
+VALUE io_spec_rb_thread_fd_writable(VALUE self, VALUE io) {
+ rb_thread_fd_writable(io_spec_get_fd(io));
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_IO_BINMODE
+VALUE io_spec_rb_io_binmode(VALUE self, VALUE io) {
+ return rb_io_binmode(io);
+}
+#endif
+
+#ifdef HAVE_RB_FD_FIX_CLOEXEC
+VALUE io_spec_rb_fd_fix_cloexec(VALUE self, VALUE io) {
+ rb_fd_fix_cloexec(io_spec_get_fd(io));
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_CLOEXEC_OPEN
+VALUE io_spec_rb_cloexec_open(VALUE self, VALUE path, VALUE flags, VALUE mode) {
+ const char *pathname = StringValuePtr(path);
+ int fd = rb_cloexec_open(pathname, FIX2INT(flags), FIX2INT(mode));
+ return rb_funcall(rb_cIO, rb_intern("for_fd"), 1, INT2FIX(fd));
+}
+#endif
+
+#ifdef HAVE_RB_IO_CLOSE
+VALUE io_spec_rb_io_close(VALUE self, VALUE io) {
+ return rb_io_close(io);
+}
+#endif
+
+/*
+ * this is needed to ensure rb_io_wait_*able functions behave
+ * predictably because errno may be set to unexpected values
+ * otherwise.
+ */
+static VALUE io_spec_errno_set(VALUE self, VALUE val) {
+ int e = NUM2INT(val);
+ errno = e;
+ return val;
+}
+
+void Init_io_spec(void) {
+ VALUE cls = rb_define_class("CApiIOSpecs", rb_cObject);
+
+#ifdef HAVE_GET_OPEN_FILE
+ rb_define_method(cls, "GetOpenFile_fd", io_spec_GetOpenFile_fd, 1);
+#endif
+
+#ifdef HAVE_RB_IO_ADDSTR
+ rb_define_method(cls, "rb_io_addstr", io_spec_rb_io_addstr, 2);
+#endif
+
+#ifdef HAVE_RB_IO_PRINTF
+ rb_define_method(cls, "rb_io_printf", io_spec_rb_io_printf, 2);
+#endif
+
+#ifdef HAVE_RB_IO_PRINT
+ rb_define_method(cls, "rb_io_print", io_spec_rb_io_print, 2);
+#endif
+
+#ifdef HAVE_RB_IO_PUTS
+ rb_define_method(cls, "rb_io_puts", io_spec_rb_io_puts, 2);
+#endif
+
+#ifdef HAVE_RB_IO_WRITE
+ rb_define_method(cls, "rb_io_write", io_spec_rb_io_write, 2);
+#endif
+
+#ifdef HAVE_RB_IO_CLOSE
+ rb_define_method(cls, "rb_io_close", io_spec_rb_io_close, 1);
+#endif
+
+#ifdef HAVE_RB_IO_CHECK_IO
+ rb_define_method(cls, "rb_io_check_io", io_spec_rb_io_check_io, 1);
+#endif
+
+#ifdef HAVE_RB_IO_CHECK_READABLE
+ rb_define_method(cls, "rb_io_check_readable", io_spec_rb_io_check_readable, 1);
+#endif
+
+#ifdef HAVE_RB_IO_CHECK_WRITABLE
+ rb_define_method(cls, "rb_io_check_writable", io_spec_rb_io_check_writable, 1);
+#endif
+
+#ifdef HAVE_RB_IO_CHECK_CLOSED
+ rb_define_method(cls, "rb_io_check_closed", io_spec_rb_io_check_closed, 1);
+#endif
+
+#ifdef HAVE_RB_IO_TAINT_CHECK
+ rb_define_method(cls, "rb_io_taint_check", io_spec_rb_io_taint_check, 1);
+#endif
+
+#ifdef HAVE_RB_IO_WAIT_READABLE
+ rb_define_method(cls, "rb_io_wait_readable", io_spec_rb_io_wait_readable, 2);
+#endif
+
+#ifdef HAVE_RB_IO_WAIT_WRITABLE
+ rb_define_method(cls, "rb_io_wait_writable", io_spec_rb_io_wait_writable, 1);
+#endif
+
+#ifdef HAVE_RB_THREAD_WAIT_FD
+ rb_define_method(cls, "rb_thread_wait_fd", io_spec_rb_thread_wait_fd, 1);
+#endif
+
+#ifdef HAVE_RB_THREAD_FD_WRITABLE
+ rb_define_method(cls, "rb_thread_fd_writable", io_spec_rb_thread_fd_writable, 1);
+#endif
+
+#ifdef HAVE_RB_IO_BINMODE
+ rb_define_method(cls, "rb_io_binmode", io_spec_rb_io_binmode, 1);
+#endif
+
+#ifdef HAVE_RB_FD_FIX_CLOEXEC
+ rb_define_method(cls, "rb_fd_fix_cloexec", io_spec_rb_fd_fix_cloexec, 1);
+#endif
+
+#ifdef HAVE_RB_CLOEXEC_OPEN
+ rb_define_method(cls, "rb_cloexec_open", io_spec_rb_cloexec_open, 3);
+#endif
+
+ rb_define_method(cls, "errno=", io_spec_errno_set, 1);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/kernel_spec.c b/spec/ruby/optional/capi/ext/kernel_spec.c
new file mode 100644
index 0000000000..c87ed5e8f2
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/kernel_spec.c
@@ -0,0 +1,461 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#include <errno.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+VALUE kernel_spec_call_proc(VALUE arg_array) {
+ VALUE arg = rb_ary_pop(arg_array);
+ VALUE proc = rb_ary_pop(arg_array);
+ return rb_funcall(proc, rb_intern("call"), 1, arg);
+}
+
+#ifdef HAVE_RB_BLOCK_GIVEN_P
+static VALUE kernel_spec_rb_block_given_p(VALUE self) {
+ return rb_block_given_p() ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_NEED_BLOCK
+VALUE kernel_spec_rb_need_block(VALUE self) {
+ rb_need_block();
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_BLOCK_PROC
+VALUE kernel_spec_rb_block_proc(VALUE self) {
+ return rb_block_proc();
+}
+#endif
+
+#ifdef HAVE_RB_BLOCK_CALL
+
+VALUE block_call_inject(VALUE yield_value, VALUE data2) {
+ /* yield_value yields the first block argument */
+ VALUE elem = yield_value;
+ VALUE elem_incr = INT2FIX(FIX2INT(elem) + 1);
+ return elem_incr;
+}
+
+VALUE kernel_spec_rb_block_call(VALUE self, VALUE ary) {
+ return rb_block_call(ary, rb_intern("map"), 0, NULL, block_call_inject, Qnil);
+}
+
+VALUE block_call_inject_multi_arg(VALUE yield_value, VALUE data2, int argc, VALUE argv[]) {
+ /* yield_value yields the first block argument */
+ VALUE sum = yield_value;
+ VALUE elem = argv[1];
+
+ return INT2FIX(FIX2INT(sum) + FIX2INT(elem));
+}
+
+VALUE kernel_spec_rb_block_call_multi_arg(VALUE self, VALUE ary) {
+ VALUE method_args[1];
+ method_args[0] = INT2FIX(0);
+ return rb_block_call(ary, rb_intern("inject"), 1, method_args, block_call_inject_multi_arg, Qnil);
+}
+
+VALUE kernel_spec_rb_block_call_no_func(VALUE self, VALUE ary) {
+ return rb_block_call(ary, rb_intern("map"), 0, NULL, NULL, Qnil);
+}
+
+#endif
+
+#ifdef HAVE_RB_FRAME_THIS_FUNC
+VALUE kernel_spec_rb_frame_this_func(VALUE self) {
+ return ID2SYM(rb_frame_this_func());
+}
+#endif
+
+#ifdef HAVE_RB_ENSURE
+VALUE kernel_spec_rb_ensure(VALUE self, VALUE main_proc, VALUE arg,
+ VALUE ensure_proc, VALUE arg2) {
+ VALUE main_array, ensure_array;
+
+ main_array = rb_ary_new();
+ rb_ary_push(main_array, main_proc);
+ rb_ary_push(main_array, arg);
+
+ ensure_array = rb_ary_new();
+ rb_ary_push(ensure_array, ensure_proc);
+ rb_ary_push(ensure_array, arg2);
+
+ return rb_ensure(kernel_spec_call_proc, main_array,
+ kernel_spec_call_proc, ensure_array);
+}
+#endif
+
+#ifdef HAVE_RB_CATCH
+VALUE kernel_spec_call_proc_with_catch(VALUE arg, VALUE data) {
+ return rb_funcall(data, rb_intern("call"), 0);
+}
+
+VALUE kernel_spec_rb_catch(VALUE self, VALUE sym, VALUE main_proc) {
+ return rb_catch(StringValuePtr(sym), kernel_spec_call_proc_with_catch, main_proc);
+}
+#endif
+
+#ifdef HAVE_RB_CATCH_OBJ
+VALUE kernel_spec_call_proc_with_catch_obj(VALUE arg, VALUE data) {
+ return rb_funcall(data, rb_intern("call"), 0);
+}
+
+VALUE kernel_spec_rb_catch_obj(VALUE self, VALUE obj, VALUE main_proc) {
+ return rb_catch_obj(obj, kernel_spec_call_proc_with_catch, main_proc);
+}
+#endif
+
+#ifdef HAVE_RB_EVAL_STRING
+VALUE kernel_spec_rb_eval_string(VALUE self, VALUE str) {
+ return rb_eval_string(RSTRING_PTR(str));
+}
+#endif
+
+#ifdef HAVE_RB_RAISE
+VALUE kernel_spec_rb_raise(VALUE self, VALUE hash) {
+ rb_hash_aset(hash, ID2SYM(rb_intern("stage")), ID2SYM(rb_intern("before")));
+ if (self != Qundef)
+ rb_raise(rb_eTypeError, "Wrong argument type %s (expected %s)", "Integer", "String");
+ rb_hash_aset(hash, ID2SYM(rb_intern("stage")), ID2SYM(rb_intern("after")));
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_THROW
+VALUE kernel_spec_rb_throw(VALUE self, VALUE result) {
+ if (self != Qundef) rb_throw("foo", result);
+ return ID2SYM(rb_intern("rb_throw_failed"));
+}
+#endif
+
+#ifdef HAVE_RB_THROW_OBJ
+VALUE kernel_spec_rb_throw_obj(VALUE self, VALUE obj, VALUE result) {
+ if (self != Qundef) rb_throw_obj(obj, result);
+ return ID2SYM(rb_intern("rb_throw_failed"));
+}
+#endif
+
+#ifdef HAVE_RB_RESCUE
+VALUE kernel_spec_call_proc_with_raised_exc(VALUE arg_array, VALUE raised_exc) {
+ VALUE argv[2];
+ int argc;
+
+ VALUE arg = rb_ary_pop(arg_array);
+ VALUE proc = rb_ary_pop(arg_array);
+
+ argv[0] = arg;
+ argv[1] = raised_exc;
+
+ argc = 2;
+
+ return rb_funcall2(proc, rb_intern("call"), argc, argv);
+}
+
+VALUE kernel_spec_rb_rescue(VALUE self, VALUE main_proc, VALUE arg,
+ VALUE raise_proc, VALUE arg2) {
+ VALUE main_array, raise_array;
+
+ main_array = rb_ary_new();
+ rb_ary_push(main_array, main_proc);
+ rb_ary_push(main_array, arg);
+
+ raise_array = rb_ary_new();
+ rb_ary_push(raise_array, raise_proc);
+ rb_ary_push(raise_array, arg2);
+
+ return rb_rescue(kernel_spec_call_proc, main_array,
+ kernel_spec_call_proc_with_raised_exc, raise_array);
+}
+#endif
+
+#ifdef HAVE_RB_RESCUE2
+VALUE kernel_spec_rb_rescue2(int argc, VALUE *args, VALUE self) {
+ VALUE main_array, raise_array;
+
+ main_array = rb_ary_new();
+ rb_ary_push(main_array, args[0]);
+ rb_ary_push(main_array, args[1]);
+
+ raise_array = rb_ary_new();
+ rb_ary_push(raise_array, args[2]);
+ rb_ary_push(raise_array, args[3]);
+
+ return rb_rescue2(kernel_spec_call_proc, main_array,
+ kernel_spec_call_proc, raise_array, args[4], args[5], (VALUE)0);
+}
+#endif
+
+#ifdef HAVE_RB_PROTECT
+static VALUE kernel_spec_rb_protect_yield(VALUE self, VALUE obj, VALUE ary) {
+ int status = 0;
+ VALUE res = rb_protect(rb_yield, obj, &status);
+ rb_ary_store(ary, 0, INT2NUM(23));
+ if (status) {
+ rb_jump_tag(status);
+ }
+ return res;
+}
+#endif
+
+#ifdef HAVE_RB_SYS_FAIL
+VALUE kernel_spec_rb_sys_fail(VALUE self, VALUE msg) {
+ errno = 1;
+ if(msg == Qnil) {
+ rb_sys_fail(0);
+ } else if (self != Qundef) {
+ rb_sys_fail(StringValuePtr(msg));
+ }
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_SYSERR_FAIL
+VALUE kernel_spec_rb_syserr_fail(VALUE self, VALUE err, VALUE msg) {
+ if(msg == Qnil) {
+ rb_syserr_fail(NUM2INT(err), NULL);
+ } else if (self != Qundef) {
+ rb_syserr_fail(NUM2INT(err), StringValuePtr(msg));
+ }
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_WARN
+VALUE kernel_spec_rb_warn(VALUE self, VALUE msg) {
+ rb_warn("%s", StringValuePtr(msg));
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_YIELD
+static VALUE kernel_spec_rb_yield(VALUE self, VALUE obj) {
+ return rb_yield(obj);
+}
+
+static VALUE kernel_spec_rb_yield_each(int argc, VALUE *args, VALUE self) {
+ int i;
+ for(i = 0; i < 4; i++) {
+ rb_yield(INT2FIX(i));
+ }
+ return INT2FIX(4);
+}
+
+static VALUE kernel_spec_rb_yield_define_each(VALUE self, VALUE cls) {
+ rb_define_method(cls, "each", kernel_spec_rb_yield_each, -1);
+ return Qnil;
+}
+
+static int kernel_cb(const void *a, const void *b) {
+ rb_yield(Qtrue);
+ return 0;
+}
+
+static VALUE kernel_indirected(int (*compar)(const void *, const void *)) {
+ int bob[] = { 1, 1, 2, 3, 5, 8, 13 };
+ qsort(bob, 7, sizeof(int), compar);
+ return Qfalse;
+}
+
+static VALUE kernel_spec_rb_yield_indirected(VALUE self, VALUE obj) {
+ return kernel_indirected(kernel_cb);
+}
+#endif
+
+#ifdef HAVE_RB_YIELD_SPLAT
+static VALUE kernel_spec_rb_yield_splat(VALUE self, VALUE ary) {
+ return rb_yield_splat(ary);
+}
+#endif
+
+#ifdef HAVE_RB_YIELD_VALUES
+static VALUE kernel_spec_rb_yield_values(VALUE self, VALUE obj1, VALUE obj2) {
+ return rb_yield_values(2, obj1, obj2);
+}
+#endif
+
+#ifdef HAVE_RB_EXEC_RECURSIVE
+static VALUE do_rec(VALUE obj, VALUE arg, int is_rec) {
+ if(is_rec) {
+ return obj;
+ } else if(arg == Qtrue) {
+ return rb_exec_recursive(do_rec, obj, Qnil);
+ } else {
+ return Qnil;
+ }
+}
+
+static VALUE kernel_spec_rb_exec_recursive(VALUE self, VALUE obj) {
+ return rb_exec_recursive(do_rec, obj, Qtrue);
+}
+#endif
+
+#ifdef HAVE_RB_SET_END_PROC
+static void write_io(VALUE io) {
+ rb_funcall(io, rb_intern("write"), 1, rb_str_new2("e"));
+}
+
+static VALUE kernel_spec_rb_set_end_proc(VALUE self, VALUE io) {
+ rb_set_end_proc(write_io, io);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_F_SPRINTF
+static VALUE kernel_spec_rb_f_sprintf(VALUE self, VALUE ary) {
+ return rb_f_sprintf((int)RARRAY_LEN(ary), RARRAY_PTR(ary));
+}
+#endif
+
+#ifdef HAVE_RB_MAKE_BACKTRACE
+static VALUE kernel_spec_rb_make_backtrace(VALUE self) {
+ return rb_make_backtrace();
+}
+#endif
+
+#ifdef HAVE_RB_OBJ_METHOD
+static VALUE kernel_spec_rb_obj_method(VALUE self, VALUE obj, VALUE method) {
+ return rb_obj_method(obj, method);
+}
+#endif
+
+#ifdef HAVE_RB_FUNCALL3
+static VALUE kernel_spec_rb_funcall3(VALUE self, VALUE obj, VALUE method) {
+ return rb_funcall3(obj, SYM2ID(method), 0, NULL);
+}
+#endif
+
+#ifdef HAVE_RB_FUNCALL_WITH_BLOCK
+static VALUE kernel_spec_rb_funcall_with_block(VALUE self, VALUE obj, VALUE method, VALUE block) {
+ return rb_funcall_with_block(obj, SYM2ID(method), 0, NULL, block);
+}
+#endif
+
+void Init_kernel_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiKernelSpecs", rb_cObject);
+
+#ifdef HAVE_RB_BLOCK_GIVEN_P
+ rb_define_method(cls, "rb_block_given_p", kernel_spec_rb_block_given_p, 0);
+#endif
+
+#ifdef HAVE_RB_NEED_BLOCK
+ rb_define_method(cls, "rb_need_block", kernel_spec_rb_need_block, 0);
+#endif
+
+#ifdef HAVE_RB_BLOCK_CALL
+ rb_define_method(cls, "rb_block_call", kernel_spec_rb_block_call, 1);
+ rb_define_method(cls, "rb_block_call_multi_arg", kernel_spec_rb_block_call_multi_arg, 1);
+ rb_define_method(cls, "rb_block_call_no_func", kernel_spec_rb_block_call_no_func, 1);
+#endif
+
+#ifdef HAVE_RB_BLOCK_PROC
+ rb_define_method(cls, "rb_block_proc", kernel_spec_rb_block_proc, 0);
+#endif
+
+#ifdef HAVE_RB_FRAME_THIS_FUNC
+ rb_define_method(cls, "rb_frame_this_func_test", kernel_spec_rb_frame_this_func, 0);
+ rb_define_method(cls, "rb_frame_this_func_test_again", kernel_spec_rb_frame_this_func, 0);
+#endif
+
+#ifdef HAVE_RB_ENSURE
+ rb_define_method(cls, "rb_ensure", kernel_spec_rb_ensure, 4);
+#endif
+
+#ifdef HAVE_RB_EVAL_STRING
+ rb_define_method(cls, "rb_eval_string", kernel_spec_rb_eval_string, 1);
+#endif
+
+#ifdef HAVE_RB_RAISE
+ rb_define_method(cls, "rb_raise", kernel_spec_rb_raise, 1);
+#endif
+
+#ifdef HAVE_RB_THROW
+ rb_define_method(cls, "rb_throw", kernel_spec_rb_throw, 1);
+#endif
+
+#ifdef HAVE_RB_THROW_OBJ
+ rb_define_method(cls, "rb_throw_obj", kernel_spec_rb_throw_obj, 2);
+#endif
+
+#ifdef HAVE_RB_RESCUE
+ rb_define_method(cls, "rb_rescue", kernel_spec_rb_rescue, 4);
+#endif
+
+#ifdef HAVE_RB_RESCUE2
+ rb_define_method(cls, "rb_rescue2", kernel_spec_rb_rescue2, -1);
+#endif
+
+#ifdef HAVE_RB_PROTECT
+ rb_define_method(cls, "rb_protect_yield", kernel_spec_rb_protect_yield, 2);
+#endif
+
+#ifdef HAVE_RB_CATCH
+ rb_define_method(cls, "rb_catch", kernel_spec_rb_catch, 2);
+#endif
+
+#ifdef HAVE_RB_CATCH_OBJ
+ rb_define_method(cls, "rb_catch_obj", kernel_spec_rb_catch_obj, 2);
+#endif
+
+#ifdef HAVE_RB_SYS_FAIL
+ rb_define_method(cls, "rb_sys_fail", kernel_spec_rb_sys_fail, 1);
+#endif
+
+#ifdef HAVE_RB_SYSERR_FAIL
+ rb_define_method(cls, "rb_syserr_fail", kernel_spec_rb_syserr_fail, 2);
+#endif
+
+#ifdef HAVE_RB_WARN
+ rb_define_method(cls, "rb_warn", kernel_spec_rb_warn, 1);
+#endif
+
+#ifdef HAVE_RB_YIELD
+ rb_define_method(cls, "rb_yield", kernel_spec_rb_yield, 1);
+ rb_define_method(cls, "rb_yield_indirected", kernel_spec_rb_yield_indirected, 1);
+ rb_define_method(cls, "rb_yield_define_each", kernel_spec_rb_yield_define_each, 1);
+#endif
+
+#ifdef HAVE_RB_YIELD_VALUES
+ rb_define_method(cls, "rb_yield_values", kernel_spec_rb_yield_values, 2);
+#endif
+
+#ifdef HAVE_RB_YIELD_SPLAT
+ rb_define_method(cls, "rb_yield_splat", kernel_spec_rb_yield_splat, 1);
+#endif
+
+#ifdef HAVE_RB_EXEC_RECURSIVE
+ rb_define_method(cls, "rb_exec_recursive", kernel_spec_rb_exec_recursive, 1);
+#endif
+
+#ifdef HAVE_RB_SET_END_PROC
+ rb_define_method(cls, "rb_set_end_proc", kernel_spec_rb_set_end_proc, 1);
+#endif
+
+#ifdef HAVE_RB_F_SPRINTF
+ rb_define_method(cls, "rb_f_sprintf", kernel_spec_rb_f_sprintf, 1);
+#endif
+
+#ifdef HAVE_RB_MAKE_BACKTRACE
+ rb_define_method(cls, "rb_make_backtrace", kernel_spec_rb_make_backtrace, 0);
+#endif
+
+#ifdef HAVE_RB_OBJ_METHOD
+ rb_define_method(cls, "rb_obj_method", kernel_spec_rb_obj_method, 2);
+#endif
+
+#ifdef HAVE_RB_FUNCALL3
+ rb_define_method(cls, "rb_funcall3", kernel_spec_rb_funcall3, 2);
+#endif
+
+#ifdef HAVE_RB_FUNCALL_WITH_BLOCK
+ rb_define_method(cls, "rb_funcall_with_block", kernel_spec_rb_funcall_with_block, 3);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/marshal_spec.c b/spec/ruby/optional/capi/ext/marshal_spec.c
new file mode 100644
index 0000000000..dbb6e71abf
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/marshal_spec.c
@@ -0,0 +1,36 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_MARSHAL_DUMP
+VALUE marshal_spec_rb_marshal_dump(VALUE self, VALUE obj, VALUE port) {
+ return rb_marshal_dump(obj, port);
+}
+#endif
+
+#ifdef HAVE_RB_MARSHAL_LOAD
+VALUE marshal_spec_rb_marshal_load(VALUE self, VALUE data) {
+ return rb_marshal_load(data);
+}
+#endif
+
+void Init_marshal_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiMarshalSpecs", rb_cObject);
+
+#ifdef HAVE_RB_MARSHAL_DUMP
+ rb_define_method(cls, "rb_marshal_dump", marshal_spec_rb_marshal_dump, 2);
+#endif
+
+#ifdef HAVE_RB_MARSHAL_LOAD
+ rb_define_method(cls, "rb_marshal_load", marshal_spec_rb_marshal_load, 1);
+#endif
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/module_spec.c b/spec/ruby/optional/capi/ext/module_spec.c
new file mode 100644
index 0000000000..e408404a09
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/module_spec.c
@@ -0,0 +1,262 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static VALUE module_specs_test_method(VALUE self) {
+ return ID2SYM(rb_intern("test_method"));
+}
+
+#ifdef HAVE_RB_CONST_DEFINED
+static VALUE module_specs_const_defined(VALUE self, VALUE klass, VALUE id) {
+ return rb_const_defined(klass, SYM2ID(id)) ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_CONST_DEFINED_AT
+static VALUE module_specs_const_defined_at(VALUE self, VALUE klass, VALUE id) {
+ return rb_const_defined_at(klass, SYM2ID(id)) ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_CONST_GET
+static VALUE module_specs_const_get(VALUE self, VALUE klass, VALUE val) {
+ return rb_const_get(klass, SYM2ID(val));
+}
+#endif
+
+#ifdef HAVE_RB_CONST_GET_AT
+static VALUE module_specs_const_get_at(VALUE self, VALUE klass, VALUE val) {
+ return rb_const_get_at(klass, SYM2ID(val));
+}
+#endif
+
+#ifdef HAVE_RB_CONST_GET_FROM
+static VALUE module_specs_const_get_from(VALUE self, VALUE klass, VALUE val) {
+ return rb_const_get_from(klass, SYM2ID(val));
+}
+#endif
+
+#ifdef HAVE_RB_CONST_SET
+static VALUE module_specs_const_set(VALUE self, VALUE klass, VALUE name, VALUE val) {
+ rb_const_set(klass, SYM2ID(name), val);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_ALIAS
+static VALUE module_specs_rb_define_alias(VALUE self, VALUE obj,
+ VALUE new_name, VALUE old_name) {
+
+ rb_define_alias(obj, RSTRING_PTR(new_name), RSTRING_PTR(old_name));
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_ALIAS
+static VALUE module_specs_rb_alias(VALUE self, VALUE obj,
+ VALUE new_name, VALUE old_name) {
+
+ rb_alias(obj, SYM2ID(new_name), SYM2ID(old_name));
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_MODULE
+static VALUE module_specs_rb_define_module(VALUE self, VALUE name) {
+ return rb_define_module(RSTRING_PTR(name));
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_MODULE_UNDER
+static VALUE module_specs_rb_define_module_under(VALUE self, VALUE outer, VALUE name) {
+ return rb_define_module_under(outer, RSTRING_PTR(name));
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_CONST
+static VALUE module_specs_define_const(VALUE self, VALUE klass, VALUE str_name, VALUE val) {
+ rb_define_const(klass, RSTRING_PTR(str_name), val);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_GLOBAL_CONST
+static VALUE module_specs_define_global_const(VALUE self, VALUE str_name, VALUE obj) {
+ rb_define_global_const(RSTRING_PTR(str_name), obj);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_GLOBAL_FUNCTION
+static VALUE module_specs_rb_define_global_function(VALUE self, VALUE str_name) {
+ rb_define_global_function(RSTRING_PTR(str_name), module_specs_test_method, 0);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_METHOD
+static VALUE module_specs_rb_define_method(VALUE self, VALUE cls, VALUE str_name) {
+ rb_define_method(cls, RSTRING_PTR(str_name), module_specs_test_method, 0);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_MODULE_FUNCTION
+static VALUE module_specs_rb_define_module_function(VALUE self, VALUE cls, VALUE str_name) {
+ rb_define_module_function(cls, RSTRING_PTR(str_name), module_specs_test_method, 0);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_PRIVATE_METHOD
+static VALUE module_specs_rb_define_private_method(VALUE self, VALUE cls, VALUE str_name) {
+ rb_define_private_method(cls, RSTRING_PTR(str_name), module_specs_test_method, 0);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_PROTECTED_METHOD
+static VALUE module_specs_rb_define_protected_method(VALUE self, VALUE cls, VALUE str_name) {
+ rb_define_protected_method(cls, RSTRING_PTR(str_name), module_specs_test_method, 0);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_DEFINE_SINGLETON_METHOD
+static VALUE module_specs_rb_define_singleton_method(VALUE self, VALUE cls, VALUE str_name) {
+ rb_define_singleton_method(cls, RSTRING_PTR(str_name), module_specs_test_method, 0);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_UNDEF_METHOD
+static VALUE module_specs_rb_undef_method(VALUE self, VALUE cls, VALUE str_name) {
+ rb_undef_method(cls, RSTRING_PTR(str_name));
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_UNDEF
+static VALUE module_specs_rb_undef(VALUE self, VALUE cls, VALUE symbol_name) {
+ rb_undef(cls, SYM2ID(symbol_name));
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_CLASS2NAME
+static VALUE module_specs_rbclass2name(VALUE self, VALUE klass) {
+ return rb_str_new2(rb_class2name(klass));
+}
+#endif
+
+#ifdef HAVE_RB_MOD_ANCESTORS
+static VALUE module_specs_rb_mod_ancestors(VALUE self, VALUE klass) {
+ return rb_mod_ancestors(klass);
+}
+#endif
+
+void Init_module_spec(void) {
+ VALUE cls;
+
+ cls = rb_define_class("CApiModuleSpecs", rb_cObject);
+
+#ifdef HAVE_RB_CONST_DEFINED
+ rb_define_method(cls, "rb_const_defined", module_specs_const_defined, 2);
+#endif
+
+#ifdef HAVE_RB_CONST_DEFINED_AT
+ rb_define_method(cls, "rb_const_defined_at", module_specs_const_defined_at, 2);
+#endif
+
+#ifdef HAVE_RB_CONST_GET
+ rb_define_method(cls, "rb_const_get", module_specs_const_get, 2);
+#endif
+
+#ifdef HAVE_RB_CONST_GET_AT
+ rb_define_method(cls, "rb_const_get_at", module_specs_const_get_at, 2);
+#endif
+
+#ifdef HAVE_RB_CONST_GET_FROM
+ rb_define_method(cls, "rb_const_get_from", module_specs_const_get_from, 2);
+#endif
+
+#ifdef HAVE_RB_CONST_SET
+ rb_define_method(cls, "rb_const_set", module_specs_const_set, 3);
+#endif
+
+#ifdef HAVE_RB_DEFINE_ALIAS
+ rb_define_method(cls, "rb_define_alias", module_specs_rb_define_alias, 3);
+#endif
+
+#ifdef HAVE_RB_ALIAS
+ rb_define_method(cls, "rb_alias", module_specs_rb_alias, 3);
+#endif
+
+#ifdef HAVE_RB_DEFINE_MODULE
+ rb_define_method(cls, "rb_define_module", module_specs_rb_define_module, 1);
+#endif
+
+#ifdef HAVE_RB_DEFINE_MODULE_UNDER
+ rb_define_method(cls, "rb_define_module_under", module_specs_rb_define_module_under, 2);
+#endif
+
+#ifdef HAVE_RB_DEFINE_CONST
+ rb_define_method(cls, "rb_define_const", module_specs_define_const, 3);
+#endif
+
+#ifdef HAVE_RB_DEFINE_GLOBAL_CONST
+ rb_define_method(cls, "rb_define_global_const", module_specs_define_global_const, 2);
+#endif
+
+#ifdef HAVE_RB_DEFINE_GLOBAL_FUNCTION
+ rb_define_method(cls, "rb_define_global_function",
+ module_specs_rb_define_global_function, 1);
+#endif
+
+#ifdef HAVE_RB_DEFINE_METHOD
+ rb_define_method(cls, "rb_define_method", module_specs_rb_define_method, 2);
+#endif
+
+#ifdef HAVE_RB_DEFINE_MODULE_FUNCTION
+ rb_define_method(cls, "rb_define_module_function",
+ module_specs_rb_define_module_function, 2);
+#endif
+
+#ifdef HAVE_RB_DEFINE_PRIVATE_METHOD
+ rb_define_method(cls, "rb_define_private_method",
+ module_specs_rb_define_private_method, 2);
+#endif
+
+#ifdef HAVE_RB_DEFINE_PROTECTED_METHOD
+ rb_define_method(cls, "rb_define_protected_method",
+ module_specs_rb_define_protected_method, 2);
+#endif
+
+#ifdef HAVE_RB_DEFINE_SINGLETON_METHOD
+ rb_define_method(cls, "rb_define_singleton_method",
+ module_specs_rb_define_singleton_method, 2);
+#endif
+
+#ifdef HAVE_RB_UNDEF_METHOD
+ rb_define_method(cls, "rb_undef_method", module_specs_rb_undef_method, 2);
+#endif
+
+#ifdef HAVE_RB_UNDEF
+ rb_define_method(cls, "rb_undef", module_specs_rb_undef, 2);
+#endif
+
+#ifdef HAVE_RB_CLASS2NAME
+ rb_define_method(cls, "rb_class2name", module_specs_rbclass2name, 1);
+#endif
+
+#ifdef HAVE_RB_MOD_ANCESTORS
+ rb_define_method(cls, "rb_mod_ancestors", module_specs_rb_mod_ancestors, 1);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/module_under_autoload_spec.c b/spec/ruby/optional/capi/ext/module_under_autoload_spec.c
new file mode 100644
index 0000000000..c8f19a287b
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/module_under_autoload_spec.c
@@ -0,0 +1,7 @@
+#include "ruby.h"
+
+void Init_module_under_autoload_spec(void) {
+ VALUE specs = rb_const_get(rb_cObject, rb_intern("CApiModuleSpecs"));
+ rb_define_module_under(specs, "ModuleUnderAutoload");
+ rb_define_module_under(specs, "RubyUnderAutoload");
+}
diff --git a/spec/ruby/optional/capi/ext/mutex_spec.c b/spec/ruby/optional/capi/ext/mutex_spec.c
new file mode 100644
index 0000000000..d5ce06e124
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/mutex_spec.c
@@ -0,0 +1,91 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_MUTEX_NEW
+VALUE mutex_spec_rb_mutex_new(VALUE self) {
+ return rb_mutex_new();
+}
+#endif
+
+#ifdef HAVE_RB_MUTEX_LOCKED_P
+VALUE mutex_spec_rb_mutex_locked_p(VALUE self, VALUE mutex) {
+ return rb_mutex_locked_p(mutex);
+}
+#endif
+
+#ifdef HAVE_RB_MUTEX_TRYLOCK
+VALUE mutex_spec_rb_mutex_trylock(VALUE self, VALUE mutex) {
+ return rb_mutex_trylock(mutex);
+}
+#endif
+
+#ifdef HAVE_RB_MUTEX_LOCK
+VALUE mutex_spec_rb_mutex_lock(VALUE self, VALUE mutex) {
+ return rb_mutex_lock(mutex);
+}
+#endif
+
+#ifdef HAVE_RB_MUTEX_UNLOCK
+VALUE mutex_spec_rb_mutex_unlock(VALUE self, VALUE mutex) {
+ return rb_mutex_unlock(mutex);
+}
+#endif
+
+#ifdef HAVE_RB_MUTEX_SLEEP
+VALUE mutex_spec_rb_mutex_sleep(VALUE self, VALUE mutex, VALUE timeout) {
+ return rb_mutex_sleep(mutex, timeout);
+}
+#endif
+
+#ifdef HAVE_RB_MUTEX_SYNCHRONIZE
+
+VALUE mutex_spec_rb_mutex_callback(VALUE arg) {
+ return rb_funcall(arg, rb_intern("call"), 0);
+}
+
+VALUE mutex_spec_rb_mutex_synchronize(VALUE self, VALUE mutex, VALUE value) {
+ return rb_mutex_synchronize(mutex, mutex_spec_rb_mutex_callback, value);
+}
+#endif
+
+void Init_mutex_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiMutexSpecs", rb_cObject);
+
+#ifdef HAVE_RB_MUTEX_NEW
+ rb_define_method(cls, "rb_mutex_new", mutex_spec_rb_mutex_new, 0);
+#endif
+
+#ifdef HAVE_RB_MUTEX_LOCKED_P
+ rb_define_method(cls, "rb_mutex_locked_p", mutex_spec_rb_mutex_locked_p, 1);
+#endif
+
+#ifdef HAVE_RB_MUTEX_TRYLOCK
+ rb_define_method(cls, "rb_mutex_trylock", mutex_spec_rb_mutex_trylock, 1);
+#endif
+
+#ifdef HAVE_RB_MUTEX_LOCK
+ rb_define_method(cls, "rb_mutex_lock", mutex_spec_rb_mutex_lock, 1);
+#endif
+
+#ifdef HAVE_RB_MUTEX_UNLOCK
+ rb_define_method(cls, "rb_mutex_unlock", mutex_spec_rb_mutex_unlock, 1);
+#endif
+
+#ifdef HAVE_RB_MUTEX_SLEEP
+ rb_define_method(cls, "rb_mutex_sleep", mutex_spec_rb_mutex_sleep, 2);
+#endif
+
+#ifdef HAVE_RB_MUTEX_SYNCHRONIZE
+ rb_define_method(cls, "rb_mutex_synchronize", mutex_spec_rb_mutex_synchronize, 2);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/spec/ruby/optional/capi/ext/numeric_spec.c b/spec/ruby/optional/capi/ext/numeric_spec.c
new file mode 100644
index 0000000000..2f66c7bac7
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/numeric_spec.c
@@ -0,0 +1,217 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static VALUE numeric_spec_size_of_VALUE(VALUE self) {
+ return INT2FIX(sizeof(VALUE));
+}
+
+static VALUE numeric_spec_size_of_long_long(VALUE self) {
+ return INT2FIX(sizeof(LONG_LONG));
+}
+
+#ifdef HAVE_NUM2CHR
+static VALUE numeric_spec_NUM2CHR(VALUE self, VALUE value) {
+ return INT2FIX(NUM2CHR(value));
+}
+#endif
+
+#ifdef HAVE_RB_INT2INUM
+static VALUE numeric_spec_rb_int2inum_14(VALUE self) {
+ return rb_int2inum(14);
+}
+#endif
+
+#ifdef HAVE_RB_UINT2INUM
+static VALUE numeric_spec_rb_uint2inum_14(VALUE self) {
+ return rb_uint2inum(14);
+}
+
+static VALUE numeric_spec_rb_uint2inum_n14(VALUE self) {
+ return rb_uint2inum(-14);
+}
+#endif
+
+#ifdef HAVE_RB_INTEGER
+static VALUE numeric_spec_rb_Integer(VALUE self, VALUE str) {
+ return rb_Integer(str);
+}
+#endif
+
+#ifdef HAVE_RB_LL2INUM
+static VALUE numeric_spec_rb_ll2inum_14(VALUE self) {
+ return rb_ll2inum(14);
+}
+#endif
+
+#ifdef HAVE_RB_ULL2INUM
+static VALUE numeric_spec_rb_ull2inum_14(VALUE self) {
+ return rb_ull2inum(14);
+}
+
+static VALUE numeric_spec_rb_ull2inum_n14(VALUE self) {
+ return rb_ull2inum(-14);
+}
+#endif
+
+#ifdef HAVE_RB_NUM2DBL
+static VALUE numeric_spec_rb_num2dbl(VALUE self, VALUE num) {
+ return rb_float_new(rb_num2dbl(num));
+}
+#endif
+
+#ifdef HAVE_RB_NUM2INT
+static VALUE numeric_spec_rb_num2int(VALUE self, VALUE num) {
+ return LONG2NUM(rb_num2int(num));
+}
+#endif
+
+#ifdef HAVE_RB_INT2NUM
+static VALUE numeric_spec_rb_int2num(VALUE self, VALUE num) {
+ return INT2NUM(rb_num2long(num));
+}
+#endif
+
+#ifdef HAVE_RB_NUM2LONG
+static VALUE numeric_spec_rb_num2long(VALUE self, VALUE num) {
+ return LONG2NUM(rb_num2long(num));
+}
+#endif
+
+#ifdef HAVE_RB_NUM2UINT
+static VALUE numeric_spec_rb_num2uint(VALUE self, VALUE num) {
+ return ULONG2NUM(rb_num2uint(num));
+}
+#endif
+
+#ifdef HAVE_RB_NUM2ULONG
+static VALUE numeric_spec_rb_num2ulong(VALUE self, VALUE num) {
+ return ULONG2NUM(rb_num2ulong(num));
+}
+#endif
+
+#ifdef HAVE_RB_NUM_ZERODIV
+static VALUE numeric_spec_rb_num_zerodiv(VALUE self) {
+ rb_num_zerodiv();
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_CMPINT
+static VALUE numeric_spec_rb_cmpint(VALUE self, VALUE val, VALUE b) {
+ return INT2FIX(rb_cmpint(val, val, b));
+}
+#endif
+
+#ifdef HAVE_RB_NUM_COERCE_BIN
+static VALUE numeric_spec_rb_num_coerce_bin(VALUE self, VALUE x, VALUE y, VALUE op) {
+ return rb_num_coerce_bin(x, y, SYM2ID(op));
+}
+#endif
+
+#ifdef HAVE_RB_NUM_COERCE_CMP
+static VALUE numeric_spec_rb_num_coerce_cmp(VALUE self, VALUE x, VALUE y, VALUE op) {
+ return rb_num_coerce_cmp(x, y, SYM2ID(op));
+}
+#endif
+
+#ifdef HAVE_RB_NUM_COERCE_RELOP
+static VALUE numeric_spec_rb_num_coerce_relop(VALUE self, VALUE x, VALUE y, VALUE op) {
+ return rb_num_coerce_relop(x, y, SYM2ID(op));
+}
+#endif
+
+#ifdef HAVE_RB_ABSINT_SINGLEBIT_P
+static VALUE numeric_spec_rb_absint_singlebit_p(VALUE self, VALUE num) {
+ return INT2FIX(rb_absint_singlebit_p(num));
+}
+#endif
+
+void Init_numeric_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiNumericSpecs", rb_cObject);
+
+ rb_define_method(cls, "size_of_VALUE", numeric_spec_size_of_VALUE, 0);
+ rb_define_method(cls, "size_of_long_long", numeric_spec_size_of_long_long, 0);
+
+#ifdef HAVE_NUM2CHR
+ rb_define_method(cls, "NUM2CHR", numeric_spec_NUM2CHR, 1);
+#endif
+
+#ifdef HAVE_RB_INT2INUM
+ rb_define_method(cls, "rb_int2inum_14", numeric_spec_rb_int2inum_14, 0);
+#endif
+
+#ifdef HAVE_RB_UINT2INUM
+ rb_define_method(cls, "rb_uint2inum_14", numeric_spec_rb_uint2inum_14, 0);
+ rb_define_method(cls, "rb_uint2inum_n14", numeric_spec_rb_uint2inum_n14, 0);
+#endif
+
+#ifdef HAVE_RB_INTEGER
+ rb_define_method(cls, "rb_Integer", numeric_spec_rb_Integer, 1);
+#endif
+
+#ifdef HAVE_RB_LL2INUM
+ rb_define_method(cls, "rb_ll2inum_14", numeric_spec_rb_ll2inum_14, 0);
+#endif
+
+#ifdef HAVE_RB_ULL2INUM
+ rb_define_method(cls, "rb_ull2inum_14", numeric_spec_rb_ull2inum_14, 0);
+ rb_define_method(cls, "rb_ull2inum_n14", numeric_spec_rb_ull2inum_n14, 0);
+#endif
+
+#ifdef HAVE_RB_NUM2DBL
+ rb_define_method(cls, "rb_num2dbl", numeric_spec_rb_num2dbl, 1);
+#endif
+
+#ifdef HAVE_RB_NUM2INT
+ rb_define_method(cls, "rb_num2int", numeric_spec_rb_num2int, 1);
+#endif
+
+#ifdef HAVE_RB_NUM2LONG
+ rb_define_method(cls, "rb_num2long", numeric_spec_rb_num2long, 1);
+#endif
+
+#ifdef HAVE_RB_INT2NUM
+ rb_define_method(cls, "rb_int2num", numeric_spec_rb_int2num, 1);
+#endif
+
+#ifdef HAVE_RB_NUM2UINT
+ rb_define_method(cls, "rb_num2uint", numeric_spec_rb_num2uint, 1);
+#endif
+
+#ifdef HAVE_RB_NUM2ULONG
+ rb_define_method(cls, "rb_num2ulong", numeric_spec_rb_num2ulong, 1);
+#endif
+
+#ifdef HAVE_RB_NUM_ZERODIV
+ rb_define_method(cls, "rb_num_zerodiv", numeric_spec_rb_num_zerodiv, 0);
+#endif
+
+#ifdef HAVE_RB_CMPINT
+ rb_define_method(cls, "rb_cmpint", numeric_spec_rb_cmpint, 2);
+#endif
+
+#ifdef HAVE_RB_NUM_COERCE_BIN
+ rb_define_method(cls, "rb_num_coerce_bin", numeric_spec_rb_num_coerce_bin, 3);
+#endif
+
+#ifdef HAVE_RB_NUM_COERCE_CMP
+ rb_define_method(cls, "rb_num_coerce_cmp", numeric_spec_rb_num_coerce_cmp, 3);
+#endif
+
+#ifdef HAVE_RB_NUM_COERCE_RELOP
+ rb_define_method(cls, "rb_num_coerce_relop", numeric_spec_rb_num_coerce_relop, 3);
+#endif
+
+#ifdef HAVE_RB_ABSINT_SINGLEBIT_P
+rb_define_method(cls, "rb_absint_singlebit_p", numeric_spec_rb_absint_singlebit_p, 1);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/object_spec.c b/spec/ruby/optional/capi/ext/object_spec.c
new file mode 100644
index 0000000000..45a28169ef
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/object_spec.c
@@ -0,0 +1,646 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_FL_ABLE
+static VALUE object_spec_FL_ABLE(VALUE self, VALUE obj) {
+ if (FL_ABLE(obj)) {
+ return Qtrue;
+ } else {
+ return Qfalse;
+ }
+}
+#endif
+
+#ifdef HAVE_FL_TEST
+static int object_spec_FL_TEST_flag(VALUE flag_string) {
+ char *flag_cstr = StringValueCStr(flag_string);
+ if (strcmp(flag_cstr, "FL_TAINT") == 0) {
+ return FL_TAINT;
+ } else if (strcmp(flag_cstr, "FL_FREEZE") == 0) {
+ return FL_FREEZE;
+ }
+ return 0;
+}
+
+static VALUE object_spec_FL_TEST(VALUE self, VALUE obj, VALUE flag) {
+ return INT2FIX(FL_TEST(obj, object_spec_FL_TEST_flag(flag)));
+}
+#endif
+
+#ifdef HAVE_OBJ_TAINT
+static VALUE object_spec_OBJ_TAINT(VALUE self, VALUE obj) {
+ OBJ_TAINT(obj);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_OBJ_TAINTED
+static VALUE object_spec_OBJ_TAINTED(VALUE self, VALUE obj) {
+ return OBJ_TAINTED(obj) ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_OBJ_INFECT
+static VALUE object_spec_OBJ_INFECT(VALUE self, VALUE host, VALUE source) {
+ OBJ_INFECT(host, source);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_ANY_TO_S
+static VALUE object_spec_rb_any_to_s(VALUE self, VALUE obj) {
+ return rb_any_to_s(obj);
+}
+#endif
+
+#ifdef HAVE_RB_ATTR_GET
+static VALUE so_attr_get(VALUE self, VALUE obj, VALUE attr) {
+ return rb_attr_get(obj, SYM2ID(attr));
+}
+#endif
+
+#ifdef HAVE_RB_OBJ_INSTANCE_VARIABLES
+static VALUE object_spec_rb_obj_instance_variables(VALUE self, VALUE obj) {
+ return rb_obj_instance_variables(obj);
+}
+#endif
+
+#ifdef HAVE_RB_CHECK_ARRAY_TYPE
+static VALUE so_check_array_type(VALUE self, VALUE ary) {
+ return rb_check_array_type(ary);
+}
+#endif
+
+#ifdef HAVE_RB_CHECK_CONVERT_TYPE
+static VALUE so_check_convert_type(VALUE self, VALUE obj, VALUE klass, VALUE method) {
+ return rb_check_convert_type(obj, T_ARRAY, RSTRING_PTR(klass), RSTRING_PTR(method));
+}
+#endif
+
+#ifdef HAVE_RB_CHECK_TO_INTEGER
+static VALUE so_check_to_integer(VALUE self, VALUE obj, VALUE method) {
+ return rb_check_to_integer(obj, RSTRING_PTR(method));
+}
+#endif
+
+#ifdef HAVE_RB_CHECK_FROZEN
+static VALUE object_spec_rb_check_frozen(VALUE self, VALUE obj) {
+ rb_check_frozen(obj);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_CHECK_STRING_TYPE
+static VALUE so_check_string_type(VALUE self, VALUE str) {
+ return rb_check_string_type(str);
+}
+#endif
+
+#ifdef HAVE_RB_CLASS_OF
+static VALUE so_rbclassof(VALUE self, VALUE obj) {
+ return rb_class_of(obj);
+}
+#endif
+
+#ifdef HAVE_RB_CONVERT_TYPE
+static VALUE so_convert_type(VALUE self, VALUE obj, VALUE klass, VALUE method) {
+ return rb_convert_type(obj, T_ARRAY, RSTRING_PTR(klass), RSTRING_PTR(method));
+}
+#endif
+
+#ifdef HAVE_RB_EXTEND_OBJECT
+static VALUE object_spec_rb_extend_object(VALUE self, VALUE obj, VALUE mod) {
+ rb_extend_object(obj, mod);
+ return obj;
+}
+#endif
+
+#ifdef HAVE_RB_INSPECT
+static VALUE so_inspect(VALUE self, VALUE obj) {
+ return rb_inspect(obj);
+}
+#endif
+
+#ifdef HAVE_RB_OBJ_ALLOC
+static VALUE so_rb_obj_alloc(VALUE self, VALUE klass) {
+ return rb_obj_alloc(klass);
+}
+#endif
+
+#ifdef HAVE_RB_OBJ_DUP
+static VALUE so_rb_obj_dup(VALUE self, VALUE klass) {
+ return rb_obj_dup(klass);
+}
+#endif
+
+#ifdef HAVE_RB_OBJ_CALL_INIT
+static VALUE so_rb_obj_call_init(VALUE self, VALUE object,
+ VALUE nargs, VALUE args) {
+ int c_nargs = FIX2INT(nargs);
+ VALUE *c_args = alloca(sizeof(VALUE) * c_nargs);
+ int i;
+
+ for (i = 0; i < c_nargs; i++)
+ c_args[i] = rb_ary_entry(args, i);
+
+ rb_obj_call_init(object, c_nargs, c_args);
+
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_OBJ_CLASSNAME
+static VALUE so_rbobjclassname(VALUE self, VALUE obj) {
+ return rb_str_new2(rb_obj_classname(obj));
+}
+
+#endif
+
+#ifdef HAVE_RB_OBJ_FREEZE
+static VALUE object_spec_rb_obj_freeze(VALUE self, VALUE obj) {
+ return rb_obj_freeze(obj);
+}
+#endif
+
+#ifdef HAVE_RB_OBJ_FROZEN_P
+static VALUE object_spec_rb_obj_frozen_p(VALUE self, VALUE obj) {
+ return rb_obj_frozen_p(obj);
+}
+#endif
+
+#ifdef HAVE_RB_OBJ_ID
+static VALUE object_spec_rb_obj_id(VALUE self, VALUE obj) {
+ return rb_obj_id(obj);
+}
+#endif
+
+#ifdef HAVE_RB_OBJ_IS_INSTANCE_OF
+static VALUE so_instance_of(VALUE self, VALUE obj, VALUE klass) {
+ return rb_obj_is_instance_of(obj, klass);
+}
+#endif
+
+#ifdef HAVE_RB_OBJ_IS_KIND_OF
+static VALUE so_kind_of(VALUE self, VALUE obj, VALUE klass) {
+ return rb_obj_is_kind_of(obj, klass);
+}
+#endif
+
+#ifdef HAVE_RB_OBJ_METHOD_ARITY
+static VALUE object_specs_rb_obj_method_arity(VALUE self, VALUE obj, VALUE mid) {
+ return INT2FIX(rb_obj_method_arity(obj, SYM2ID(mid)));
+}
+#endif
+
+#ifdef HAVE_RB_OBJ_TAINT
+static VALUE object_spec_rb_obj_taint(VALUE self, VALUE obj) {
+ return rb_obj_taint(obj);
+}
+#endif
+
+#ifdef HAVE_RB_REQUIRE
+static VALUE so_require(VALUE self) {
+ rb_require("fixtures/foo");
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_RESPOND_TO
+static VALUE so_respond_to(VALUE self, VALUE obj, VALUE sym) {
+ return rb_respond_to(obj, SYM2ID(sym)) ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_OBJ_RESPOND_TO
+static VALUE so_obj_respond_to(VALUE self, VALUE obj, VALUE sym, VALUE priv) {
+ return rb_obj_respond_to(obj, SYM2ID(sym), priv == Qtrue ? 1 : 0) ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_METHOD_BOUNDP
+static VALUE object_spec_rb_method_boundp(VALUE self, VALUE obj, VALUE method, VALUE exclude_private) {
+ ID id = SYM2ID(method);
+ return rb_method_boundp(obj, id, exclude_private == Qtrue ? 1 : 0) ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_SPECIAL_CONST_P
+static VALUE object_spec_rb_special_const_p(VALUE self, VALUE value) {
+ if (rb_special_const_p(value)) {
+ return Qtrue;
+ } else {
+ return Qfalse;
+ }
+}
+#endif
+
+#ifdef HAVE_RB_TO_ID
+static VALUE so_to_id(VALUE self, VALUE obj) {
+ return ID2SYM(rb_to_id(obj));
+}
+#endif
+
+#ifdef HAVE_RTEST
+static VALUE object_spec_RTEST(VALUE self, VALUE value) {
+ return RTEST(value) ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_TYPE
+static VALUE so_is_type_nil(VALUE self, VALUE obj) {
+ if(TYPE(obj) == T_NIL) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static VALUE so_is_type_object(VALUE self, VALUE obj) {
+ if(TYPE(obj) == T_OBJECT) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static VALUE so_is_type_array(VALUE self, VALUE obj) {
+ if(TYPE(obj) == T_ARRAY) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static VALUE so_is_type_module(VALUE self, VALUE obj) {
+ if(TYPE(obj) == T_MODULE) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static VALUE so_is_type_class(VALUE self, VALUE obj) {
+ if(TYPE(obj) == T_CLASS) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static VALUE so_is_type_data(VALUE self, VALUE obj) {
+ if(TYPE(obj) == T_DATA) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_TYPE_P
+static VALUE so_is_rb_type_p_nil(VALUE self, VALUE obj) {
+ if(rb_type_p(obj, T_NIL)) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static VALUE so_is_rb_type_p_object(VALUE self, VALUE obj) {
+ if(rb_type_p(obj, T_OBJECT)) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static VALUE so_is_rb_type_p_array(VALUE self, VALUE obj) {
+ if(rb_type_p(obj, T_ARRAY)) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static VALUE so_is_rb_type_p_module(VALUE self, VALUE obj) {
+ if(rb_type_p(obj, T_MODULE)) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static VALUE so_is_rb_type_p_class(VALUE self, VALUE obj) {
+ if(rb_type_p(obj, T_CLASS)) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static VALUE so_is_rb_type_p_data(VALUE self, VALUE obj) {
+ if(rb_type_p(obj, T_DATA)) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+#endif
+
+#ifdef HAVE_BUILTIN_TYPE
+static VALUE so_is_builtin_type_object(VALUE self, VALUE obj) {
+ if(BUILTIN_TYPE(obj) == T_OBJECT) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static VALUE so_is_builtin_type_array(VALUE self, VALUE obj) {
+ if(BUILTIN_TYPE(obj) == T_ARRAY) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static VALUE so_is_builtin_type_module(VALUE self, VALUE obj) {
+ if(BUILTIN_TYPE(obj) == T_MODULE) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static VALUE so_is_builtin_type_class(VALUE self, VALUE obj) {
+ if(BUILTIN_TYPE(obj) == T_CLASS) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static VALUE so_is_builtin_type_data(VALUE self, VALUE obj) {
+ if(BUILTIN_TYPE(obj) == T_DATA) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_TO_INT
+static VALUE object_spec_rb_to_int(VALUE self, VALUE obj) {
+ return rb_to_int(obj);
+}
+#endif
+
+#ifdef HAVE_RB_OBJ_INSTANCE_EVAL
+static VALUE object_spec_rb_obj_instance_eval(VALUE self, VALUE obj) {
+ return rb_obj_instance_eval(0, NULL, obj);
+}
+#endif
+
+#ifdef HAVE_RB_IV_GET
+static VALUE object_spec_rb_iv_get(VALUE self, VALUE obj, VALUE name) {
+ return rb_iv_get(obj, RSTRING_PTR(name));
+}
+#endif
+
+#ifdef HAVE_RB_IV_SET
+static VALUE object_spec_rb_iv_set(VALUE self, VALUE obj, VALUE name, VALUE value) {
+ return rb_iv_set(obj, RSTRING_PTR(name), value);
+}
+#endif
+
+#ifdef HAVE_RB_IVAR_GET
+static VALUE object_spec_rb_ivar_get(VALUE self, VALUE obj, VALUE sym_name) {
+ return rb_ivar_get(obj, SYM2ID(sym_name));
+}
+#endif
+
+#ifdef HAVE_RB_IVAR_SET
+static VALUE object_spec_rb_ivar_set(VALUE self, VALUE obj, VALUE sym_name, VALUE value) {
+ return rb_ivar_set(obj, SYM2ID(sym_name), value);
+}
+#endif
+
+#ifdef HAVE_RB_IVAR_DEFINED
+static VALUE object_spec_rb_ivar_defined(VALUE self, VALUE obj, VALUE sym_name) {
+ return rb_ivar_defined(obj, SYM2ID(sym_name));
+}
+#endif
+
+#ifdef HAVE_RB_EQUAL
+static VALUE object_spec_rb_equal(VALUE self, VALUE a, VALUE b) {
+ return rb_equal(a, b);
+}
+#endif
+
+#ifdef HAVE_RB_CLASS_INHERITED_P
+static VALUE object_spec_rb_class_inherited_p(VALUE self, VALUE mod, VALUE arg) {
+ return rb_class_inherited_p(mod, arg);
+}
+#endif
+
+
+void Init_object_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiObjectSpecs", rb_cObject);
+
+#ifdef HAVE_FL_ABLE
+ rb_define_method(cls, "FL_ABLE", object_spec_FL_ABLE, 1);
+#endif
+
+#ifdef HAVE_FL_TEST
+ rb_define_method(cls, "FL_TEST", object_spec_FL_TEST, 2);
+#endif
+
+#ifdef HAVE_OBJ_TAINT
+ rb_define_method(cls, "OBJ_TAINT", object_spec_OBJ_TAINT, 1);
+#endif
+
+#ifdef HAVE_OBJ_TAINTED
+ rb_define_method(cls, "OBJ_TAINTED", object_spec_OBJ_TAINTED, 1);
+#endif
+
+#ifdef HAVE_OBJ_INFECT
+ rb_define_method(cls, "OBJ_INFECT", object_spec_OBJ_INFECT, 2);
+#endif
+
+#ifdef HAVE_RB_ANY_TO_S
+ rb_define_method(cls, "rb_any_to_s", object_spec_rb_any_to_s, 1);
+#endif
+
+#ifdef HAVE_RB_ATTR_GET
+ rb_define_method(cls, "rb_attr_get", so_attr_get, 2);
+#endif
+
+#ifdef HAVE_RB_OBJ_INSTANCE_VARIABLES
+ rb_define_method(cls, "rb_obj_instance_variables", object_spec_rb_obj_instance_variables, 1);
+#endif
+
+#ifdef HAVE_RB_CHECK_ARRAY_TYPE
+ rb_define_method(cls, "rb_check_array_type", so_check_array_type, 1);
+#endif
+
+#ifdef HAVE_RB_CHECK_CONVERT_TYPE
+ rb_define_method(cls, "rb_check_convert_type", so_check_convert_type, 3);
+#endif
+
+#ifdef HAVE_RB_CHECK_TO_INTEGER
+ rb_define_method(cls, "rb_check_to_integer", so_check_to_integer, 2);
+#endif
+
+#ifdef HAVE_RB_CHECK_FROZEN
+ rb_define_method(cls, "rb_check_frozen", object_spec_rb_check_frozen, 1);
+#endif
+
+#ifdef HAVE_RB_CHECK_STRING_TYPE
+ rb_define_method(cls, "rb_check_string_type", so_check_string_type, 1);
+#endif
+
+#ifdef HAVE_RB_CLASS_OF
+ rb_define_method(cls, "rb_class_of", so_rbclassof, 1);
+#endif
+
+#ifdef HAVE_RB_CONVERT_TYPE
+ rb_define_method(cls, "rb_convert_type", so_convert_type, 3);
+#endif
+
+#ifdef HAVE_RB_EXTEND_OBJECT
+ rb_define_method(cls, "rb_extend_object", object_spec_rb_extend_object, 2);
+#endif
+
+#ifdef HAVE_RB_INSPECT
+ rb_define_method(cls, "rb_inspect", so_inspect, 1);
+#endif
+
+#ifdef HAVE_RB_OBJ_ALLOC
+ rb_define_method(cls, "rb_obj_alloc", so_rb_obj_alloc, 1);
+#endif
+
+#ifdef HAVE_RB_OBJ_ALLOC
+ rb_define_method(cls, "rb_obj_dup", so_rb_obj_dup, 1);
+#endif
+
+#ifdef HAVE_RB_OBJ_CALL_INIT
+ rb_define_method(cls, "rb_obj_call_init", so_rb_obj_call_init, 3);
+#endif
+
+#ifdef HAVE_RB_OBJ_CLASSNAME
+ rb_define_method(cls, "rb_obj_classname", so_rbobjclassname, 1);
+#endif
+
+#ifdef HAVE_RB_OBJ_FREEZE
+ rb_define_method(cls, "rb_obj_freeze", object_spec_rb_obj_freeze, 1);
+#endif
+
+#ifdef HAVE_RB_OBJ_FROZEN_P
+ rb_define_method(cls, "rb_obj_frozen_p", object_spec_rb_obj_frozen_p, 1);
+#endif
+
+#ifdef HAVE_RB_OBJ_ID
+ rb_define_method(cls, "rb_obj_id", object_spec_rb_obj_id, 1);
+#endif
+
+#ifdef HAVE_RB_OBJ_IS_INSTANCE_OF
+ rb_define_method(cls, "rb_obj_is_instance_of", so_instance_of, 2);
+#endif
+
+#ifdef HAVE_RB_OBJ_IS_KIND_OF
+ rb_define_method(cls, "rb_obj_is_kind_of", so_kind_of, 2);
+#endif
+
+#ifdef HAVE_RB_OBJ_METHOD_ARITY
+ rb_define_method(cls, "rb_obj_method_arity", object_specs_rb_obj_method_arity, 2);
+#endif
+
+#ifdef HAVE_RB_OBJ_TAINT
+ rb_define_method(cls, "rb_obj_taint", object_spec_rb_obj_taint, 1);
+#endif
+
+#ifdef HAVE_RB_REQUIRE
+ rb_define_method(cls, "rb_require", so_require, 0);
+#endif
+
+#ifdef HAVE_RB_RESPOND_TO
+ rb_define_method(cls, "rb_respond_to", so_respond_to, 2);
+#endif
+
+#ifdef HAVE_RB_METHOD_BOUNDP
+ rb_define_method(cls, "rb_method_boundp", object_spec_rb_method_boundp, 3);
+#endif
+
+#ifdef HAVE_RB_OBJ_RESPOND_TO
+ rb_define_method(cls, "rb_obj_respond_to", so_obj_respond_to, 3);
+#endif
+
+#ifdef HAVE_RB_SPECIAL_CONST_P
+ rb_define_method(cls, "rb_special_const_p", object_spec_rb_special_const_p, 1);
+#endif
+
+#ifdef HAVE_RB_STR_NEW2
+#endif
+
+#ifdef HAVE_RB_TO_ID
+ rb_define_method(cls, "rb_to_id", so_to_id, 1);
+#endif
+
+#ifdef HAVE_RTEST
+ rb_define_method(cls, "RTEST", object_spec_RTEST, 1);
+#endif
+
+#ifdef HAVE_TYPE
+ rb_define_method(cls, "rb_is_type_nil", so_is_type_nil, 1);
+ rb_define_method(cls, "rb_is_type_object", so_is_type_object, 1);
+ rb_define_method(cls, "rb_is_type_array", so_is_type_array, 1);
+ rb_define_method(cls, "rb_is_type_module", so_is_type_module, 1);
+ rb_define_method(cls, "rb_is_type_class", so_is_type_class, 1);
+ rb_define_method(cls, "rb_is_type_data", so_is_type_data, 1);
+#endif
+
+#ifdef HAVE_RB_TYPE_P
+ rb_define_method(cls, "rb_is_rb_type_p_nil", so_is_rb_type_p_nil, 1);
+ rb_define_method(cls, "rb_is_rb_type_p_object", so_is_rb_type_p_object, 1);
+ rb_define_method(cls, "rb_is_rb_type_p_array", so_is_rb_type_p_array, 1);
+ rb_define_method(cls, "rb_is_rb_type_p_module", so_is_rb_type_p_module, 1);
+ rb_define_method(cls, "rb_is_rb_type_p_class", so_is_rb_type_p_class, 1);
+ rb_define_method(cls, "rb_is_rb_type_p_data", so_is_rb_type_p_data, 1);
+#endif
+
+#ifdef HAVE_BUILTIN_TYPE
+ rb_define_method(cls, "rb_is_builtin_type_object", so_is_builtin_type_object, 1);
+ rb_define_method(cls, "rb_is_builtin_type_array", so_is_builtin_type_array, 1);
+ rb_define_method(cls, "rb_is_builtin_type_module", so_is_builtin_type_module, 1);
+ rb_define_method(cls, "rb_is_builtin_type_class", so_is_builtin_type_class, 1);
+ rb_define_method(cls, "rb_is_builtin_type_data", so_is_builtin_type_data, 1);
+#endif
+
+#ifdef HAVE_RB_TO_INT
+ rb_define_method(cls, "rb_to_int", object_spec_rb_to_int, 1);
+#endif
+
+#ifdef HAVE_RB_EQUAL
+ rb_define_method(cls, "rb_equal", object_spec_rb_equal, 2);
+#endif
+
+#ifdef HAVE_RB_CLASS_INHERITED_P
+ rb_define_method(cls, "rb_class_inherited_p", object_spec_rb_class_inherited_p, 2);
+#endif
+
+#ifdef HAVE_RB_OBJ_INSTANCE_EVAL
+ rb_define_method(cls, "rb_obj_instance_eval", object_spec_rb_obj_instance_eval, 1);
+#endif
+
+#ifdef HAVE_RB_IV_GET
+ rb_define_method(cls, "rb_iv_get", object_spec_rb_iv_get, 2);
+#endif
+
+#ifdef HAVE_RB_IV_SET
+ rb_define_method(cls, "rb_iv_set", object_spec_rb_iv_set, 3);
+#endif
+
+#ifdef HAVE_RB_IVAR_GET
+ rb_define_method(cls, "rb_ivar_get", object_spec_rb_ivar_get, 2);
+#endif
+
+#ifdef HAVE_RB_IVAR_SET
+ rb_define_method(cls, "rb_ivar_set", object_spec_rb_ivar_set, 3);
+#endif
+
+#ifdef HAVE_RB_IVAR_DEFINED
+ rb_define_method(cls, "rb_ivar_defined", object_spec_rb_ivar_defined, 2);
+#endif
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/proc_spec.c b/spec/ruby/optional/capi/ext/proc_spec.c
new file mode 100644
index 0000000000..f9c0f6b1b9
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/proc_spec.c
@@ -0,0 +1,85 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_PROC_NEW
+VALUE proc_spec_rb_proc_new_function(VALUE args) {
+ return rb_funcall(args, rb_intern("inspect"), 0);
+}
+
+VALUE proc_spec_rb_proc_new(VALUE self) {
+ return rb_proc_new(proc_spec_rb_proc_new_function, Qnil);
+}
+#endif
+
+#ifdef HAVE_RB_PROC_ARITY
+VALUE proc_spec_rb_proc_arity(VALUE self, VALUE prc) {
+ return INT2FIX(rb_proc_arity(prc));
+}
+#endif
+
+#ifdef HAVE_RB_PROC_CALL
+VALUE proc_spec_rb_proc_call(VALUE self, VALUE prc, VALUE args) {
+ return rb_proc_call(prc, args);
+}
+#endif
+
+/* This helper is not strictly necessary but reflects the code in wxRuby that
+ * originally exposed issues with this Proc.new behavior.
+ */
+VALUE proc_spec_rb_Proc_new_helper(void) {
+ return rb_funcall(rb_cProc, rb_intern("new"), 0);
+}
+
+VALUE proc_spec_rb_Proc_new(VALUE self, VALUE scenario) {
+ switch(FIX2INT(scenario)) {
+ case 0:
+ return proc_spec_rb_Proc_new_helper();
+ case 1:
+ rb_funcall(self, rb_intern("call_nothing"), 0);
+ return proc_spec_rb_Proc_new_helper();
+ case 2:
+ return rb_funcall(self, rb_intern("call_Proc_new"), 0);
+ case 3:
+ return rb_funcall(self, rb_intern("call_rb_Proc_new"), 0);
+ case 4:
+ return rb_funcall(self, rb_intern("call_rb_Proc_new_with_block"), 0);
+ case 5:
+ rb_funcall(self, rb_intern("call_rb_Proc_new_with_block"), 0);
+ return proc_spec_rb_Proc_new_helper();
+ case 6:
+ return rb_funcall(self, rb_intern("call_block_given?"), 0);
+ default:
+ rb_raise(rb_eException, "invalid scenario");
+ }
+
+ return Qnil;
+}
+
+void Init_proc_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiProcSpecs", rb_cObject);
+
+#ifdef HAVE_RB_PROC_NEW
+ rb_define_method(cls, "rb_proc_new", proc_spec_rb_proc_new, 0);
+#endif
+
+#ifdef HAVE_RB_PROC_ARITY
+ rb_define_method(cls, "rb_proc_arity", proc_spec_rb_proc_arity, 1);
+#endif
+
+#ifdef HAVE_RB_PROC_CALL
+ rb_define_method(cls, "rb_proc_call", proc_spec_rb_proc_call, 2);
+#endif
+
+ rb_define_method(cls, "rb_Proc_new", proc_spec_rb_Proc_new, 1);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/range_spec.c b/spec/ruby/optional/capi/ext/range_spec.c
new file mode 100644
index 0000000000..6dc2d579fd
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/range_spec.c
@@ -0,0 +1,66 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_RANGE_NEW
+VALUE range_spec_rb_range_new(int argc, VALUE* argv, VALUE self) {
+ int exclude_end = 0;
+ if(argc == 3) {
+ exclude_end = RTEST(argv[2]);
+ }
+ return rb_range_new(argv[0], argv[1], exclude_end);
+}
+#endif
+
+#ifdef HAVE_RB_RANGE_VALUES
+VALUE range_spec_rb_range_values(VALUE self, VALUE range) {
+ VALUE beg;
+ VALUE end;
+ int excl;
+ VALUE ary = rb_ary_new();
+ rb_range_values(range, &beg, &end, &excl);
+ rb_ary_store(ary, 0, beg);
+ rb_ary_store(ary, 1, end);
+ rb_ary_store(ary, 2, excl ? Qtrue : Qfalse);
+ return ary;
+}
+#endif
+
+#ifdef HAVE_RB_RANGE_BEG_LEN
+VALUE range_spec_rb_range_beg_len(VALUE self, VALUE range, VALUE begpv, VALUE lenpv, VALUE lenv, VALUE errv) {
+ long begp = FIX2LONG(begpv);
+ long lenp = FIX2LONG(lenpv);
+ long len = FIX2LONG(lenv);
+ int err = FIX2INT(errv);
+ VALUE ary = rb_ary_new();
+ VALUE res = rb_range_beg_len(range, &begp, &lenp, len, err);
+ rb_ary_store(ary, 0, LONG2FIX(begp));
+ rb_ary_store(ary, 1, LONG2FIX(lenp));
+ rb_ary_store(ary, 2, res);
+ return ary;
+}
+#endif
+
+void Init_range_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiRangeSpecs", rb_cObject);
+
+#ifdef HAVE_RB_RANGE_NEW
+ rb_define_method(cls, "rb_range_new", range_spec_rb_range_new, -1);
+#endif
+
+#ifdef HAVE_RB_RANGE_VALUES
+ rb_define_method(cls, "rb_range_values", range_spec_rb_range_values, 1);
+#endif
+
+#ifdef HAVE_RB_RANGE_BEG_LEN
+ rb_define_method(cls, "rb_range_beg_len", range_spec_rb_range_beg_len, 5);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/rational_spec.c b/spec/ruby/optional/capi/ext/rational_spec.c
new file mode 100644
index 0000000000..9f349261a0
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/rational_spec.c
@@ -0,0 +1,95 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_RATIONAL
+static VALUE rational_spec_rb_Rational(VALUE self, VALUE num, VALUE den) {
+ return rb_Rational(num, den);
+}
+#endif
+
+#ifdef HAVE_RB_RATIONAL1
+static VALUE rational_spec_rb_Rational1(VALUE self, VALUE num) {
+ return rb_Rational1(num);
+}
+#endif
+
+#ifdef HAVE_RB_RATIONAL2
+static VALUE rational_spec_rb_Rational2(VALUE self, VALUE num, VALUE den) {
+ return rb_Rational2(num, den);
+}
+#endif
+
+#ifdef HAVE_RB_RATIONAL_NEW
+static VALUE rational_spec_rb_rational_new(VALUE self, VALUE num, VALUE den) {
+ return rb_rational_new(num, den);
+}
+#endif
+
+#ifdef HAVE_RB_RATIONAL_NEW1
+static VALUE rational_spec_rb_rational_new1(VALUE self, VALUE num) {
+ return rb_rational_new1(num);
+}
+#endif
+
+#ifdef HAVE_RB_RATIONAL_NEW2
+static VALUE rational_spec_rb_rational_new2(VALUE self, VALUE num, VALUE den) {
+ return rb_rational_new2(num, den);
+}
+#endif
+
+#ifdef HAVE_RB_RATIONAL_NUM
+static VALUE rational_spec_rb_rational_num(VALUE self, VALUE rational) {
+ return rb_rational_num(rational);
+}
+#endif
+
+#ifdef HAVE_RB_RATIONAL_DEN
+static VALUE rational_spec_rb_rational_den(VALUE self, VALUE rational) {
+ return rb_rational_den(rational);
+}
+#endif
+
+void Init_rational_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiRationalSpecs", rb_cObject);
+
+#ifdef HAVE_RB_RATIONAL
+ rb_define_method(cls, "rb_Rational", rational_spec_rb_Rational, 2);
+#endif
+
+#ifdef HAVE_RB_RATIONAL1
+ rb_define_method(cls, "rb_Rational1", rational_spec_rb_Rational1, 1);
+#endif
+
+#ifdef HAVE_RB_RATIONAL2
+ rb_define_method(cls, "rb_Rational2", rational_spec_rb_Rational2, 2);
+#endif
+
+#ifdef HAVE_RB_RATIONAL_NEW
+ rb_define_method(cls, "rb_rational_new", rational_spec_rb_rational_new, 2);
+#endif
+
+#ifdef HAVE_RB_RATIONAL_NEW1
+ rb_define_method(cls, "rb_rational_new1", rational_spec_rb_rational_new1, 1);
+#endif
+
+#ifdef HAVE_RB_RATIONAL_NEW2
+ rb_define_method(cls, "rb_rational_new2", rational_spec_rb_rational_new2, 2);
+#endif
+
+#ifdef HAVE_RB_RATIONAL_NUM
+ rb_define_method(cls, "rb_rational_num", rational_spec_rb_rational_num, 1);
+#endif
+
+#ifdef HAVE_RB_RATIONAL_DEN
+ rb_define_method(cls, "rb_rational_den", rational_spec_rb_rational_den, 1);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/regexp_spec.c b/spec/ruby/optional/capi/ext/regexp_spec.c
new file mode 100644
index 0000000000..1058293444
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/regexp_spec.c
@@ -0,0 +1,84 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#include "ruby/re.h"
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_REG_NEW
+VALUE regexp_spec_re(VALUE self) {
+ return rb_reg_new("a", 1, 0);
+}
+#endif
+
+#ifdef HAVE_RB_REG_NTH_MATCH
+VALUE regexp_spec_reg_1st_match(VALUE self, VALUE md) {
+ return rb_reg_nth_match(1, md);
+}
+#endif
+
+#ifdef HAVE_RB_REG_OPTIONS
+VALUE regexp_spec_rb_reg_options(VALUE self, VALUE regexp) {
+ return INT2FIX(rb_reg_options(regexp));
+}
+#endif
+
+#ifdef HAVE_RB_REG_REGCOMP
+VALUE regexp_spec_rb_reg_regcomp(VALUE self, VALUE str) {
+ return rb_reg_regcomp(str);
+}
+#endif
+
+#ifdef HAVE_RB_REG_MATCH
+VALUE regexp_spec_reg_match(VALUE self, VALUE re, VALUE str) {
+ return rb_reg_match(re, str);
+}
+#endif
+
+#ifdef HAVE_RB_BACKREF_GET
+VALUE regexp_spec_backref_get(VALUE self) {
+ return rb_backref_get();
+}
+#endif
+
+VALUE regexp_spec_match(VALUE self, VALUE regexp, VALUE str) {
+ return rb_funcall(regexp, rb_intern("match"), 1, str);
+}
+
+void Init_regexp_spec(void) {
+ VALUE cls = rb_define_class("CApiRegexpSpecs", rb_cObject);
+
+ rb_define_method(cls, "match", regexp_spec_match, 2);
+
+#ifdef HAVE_RB_REG_NEW
+ rb_define_method(cls, "a_re", regexp_spec_re, 0);
+#endif
+
+#ifdef HAVE_RB_REG_NTH_MATCH
+ rb_define_method(cls, "a_re_1st_match", regexp_spec_reg_1st_match, 1);
+#endif
+
+#ifdef HAVE_RB_REG_MATCH
+ rb_define_method(cls, "rb_reg_match", regexp_spec_reg_match, 2);
+#endif
+
+#ifdef HAVE_RB_BACKREF_GET
+ rb_define_method(cls, "rb_backref_get", regexp_spec_backref_get, 0);
+#endif
+
+#ifdef HAVE_RB_REG_OPTIONS
+ rb_define_method(cls, "rb_reg_options", regexp_spec_rb_reg_options, 1);
+#endif
+
+#ifdef HAVE_RB_REG_REGCOMP
+ rb_define_method(cls, "rb_reg_regcomp", regexp_spec_rb_reg_regcomp, 1);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/rubyspec.h b/spec/ruby/optional/capi/ext/rubyspec.h
new file mode 100644
index 0000000000..e5a90bfc88
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/rubyspec.h
@@ -0,0 +1,624 @@
+#ifndef RUBYSPEC_H
+#define RUBYSPEC_H
+
+/* Define convenience macros similar to the mspec guards to assist
+ * with version incompatibilities.
+ */
+
+#include <ruby.h>
+#ifdef HAVE_RUBY_VERSION_H
+# include <ruby/version.h>
+#else
+# include <version.h>
+#endif
+
+#ifndef RUBY_VERSION_MAJOR
+#define RUBY_VERSION_MAJOR RUBY_API_VERSION_MAJOR
+#define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
+#define RUBY_VERSION_TEENY RUBY_API_VERSION_TEENY
+#endif
+
+#define RUBY_VERSION_BEFORE(major,minor,teeny) \
+ ((RUBY_VERSION_MAJOR < (major)) || \
+ (RUBY_VERSION_MAJOR == (major) && RUBY_VERSION_MINOR < (minor)) || \
+ (RUBY_VERSION_MAJOR == (major) && RUBY_VERSION_MINOR == (minor) && RUBY_VERSION_TEENY < (teeny)))
+
+#if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 6)
+#define RUBY_VERSION_IS_2_6
+#endif
+
+#if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 4)
+#define RUBY_VERSION_IS_2_4
+#endif
+
+#if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 3)
+#define RUBY_VERSION_IS_2_3
+#endif
+
+/* Define all function flags */
+
+/* Array */
+#define HAVE_RB_ARRAY 1
+#define HAVE_RARRAY_AREF 1
+#define HAVE_RARRAY_LEN 1
+#define HAVE_RARRAY_PTR 1
+#define HAVE_RB_ARY_AREF 1
+#define HAVE_RB_ARY_CLEAR 1
+#define HAVE_RB_ARY_DELETE 1
+#define HAVE_RB_ARY_DELETE_AT 1
+#define HAVE_RB_ARY_DUP 1
+#define HAVE_RB_ARY_ENTRY 1
+#define HAVE_RB_ARY_FREEZE 1
+#define HAVE_RB_ARY_INCLUDES 1
+#define HAVE_RB_ARY_JOIN 1
+#define HAVE_RB_ARY_NEW 1
+#define HAVE_RB_ARY_NEW2 1
+#define HAVE_RB_ARY_NEW_CAPA 1
+#define HAVE_RB_ARY_NEW3 1
+#define HAVE_RB_ARY_NEW_FROM_ARGS 1
+#define HAVE_RB_ARY_NEW4 1
+#define HAVE_RB_ARY_NEW_FROM_VALUES 1
+#define HAVE_RB_ARY_POP 1
+#define HAVE_RB_ARY_PUSH 1
+#define HAVE_RB_ARY_CAT 1
+#define HAVE_RB_ARY_REVERSE 1
+#define HAVE_RB_ARY_ROTATE 1
+#define HAVE_RB_ARY_SHIFT 1
+#define HAVE_RB_ARY_STORE 1
+#define HAVE_RB_ARY_CONCAT 1
+#define HAVE_RB_ARY_PLUS 1
+#define HAVE_RB_ARY_TO_ARY 1
+#define HAVE_RB_ARY_SUBSEQ 1
+#define HAVE_RB_ARY_TO_S 1
+#define HAVE_RB_ARY_UNSHIFT 1
+#define HAVE_RB_ASSOC_NEW 1
+
+#define HAVE_RB_EACH 1
+#define HAVE_RB_ITERATE 1
+#define HAVE_RB_MEM_CLEAR 1
+
+/* Bignum */
+#define HAVE_ABSINT_SIZE 1
+#define HAVE_RB_BIG2DBL 1
+#define HAVE_RB_DBL2BIG 1
+#define HAVE_RB_BIG2LL 1
+#define HAVE_RB_BIG2LONG 1
+#define HAVE_RB_BIG2STR 1
+#define HAVE_RB_BIG2ULONG 1
+#define HAVE_RB_BIG_CMP 1
+#define HAVE_RB_BIG_PACK 1
+
+/* Class */
+#define HAVE_RB_CALL_SUPER 1
+#define HAVE_RB_CLASS2NAME 1
+#define HAVE_RB_CLASS_NAME 1
+#define HAVE_RB_CLASS_NEW 1
+#define HAVE_RB_CLASS_NEW_INSTANCE 1
+#define HAVE_RB_CLASS_PATH 1
+#define HAVE_RB_CLASS_REAL 1
+#define HAVE_RB_CVAR_DEFINED 1
+#define HAVE_RB_CVAR_GET 1
+#define HAVE_RB_CVAR_SET 1
+#define HAVE_RB_CV_GET 1
+#define HAVE_RB_CV_SET 1
+#define HAVE_RB_DEFINE_ATTR 1
+#define HAVE_RB_DEFINE_CLASS_VARIABLE 1
+#define HAVE_RB_INCLUDE_MODULE 1
+#define HAVE_RB_PATH2CLASS 1
+#define HAVE_RB_PATH_TO_CLASS 1
+#define HAVE_RB_CLASS_SUPERCLASS 1
+
+/* Complex */
+#define HAVE_RB_COMPLEX 1
+#define HAVE_RB_COMPLEX1 1
+#define HAVE_RB_COMPLEX2 1
+#define HAVE_RB_COMPLEX_NEW 1
+#define HAVE_RB_COMPLEX_NEW1 1
+#define HAVE_RB_COMPLEX_NEW2 1
+
+/* Constants */
+#define HAVE_RB_CARRAY 1
+#ifndef RUBY_INTEGER_UNIFICATION
+#define HAVE_RB_CBIGNUM 1
+#endif
+#define HAVE_RB_CCLASS 1
+#define HAVE_RB_CDATA 1
+#define HAVE_RB_CFALSECLASS 1
+#define HAVE_RB_CFILE 1
+#ifndef RUBY_INTEGER_UNIFICATION
+#define HAVE_RB_CFIXNUM 1
+#endif
+#define HAVE_RB_CFLOAT 1
+#define HAVE_RB_CHASH 1
+#define HAVE_RB_CINTEGER 1
+#define HAVE_RB_CIO 1
+#define HAVE_RB_CMATCH 1
+#define HAVE_RB_CMODULE 1
+#define HAVE_RB_CNILCLASS 1
+#define HAVE_RB_CNUMERIC 1
+#define HAVE_RB_COBJECT 1
+#define HAVE_RB_CPROC 1
+#define HAVE_RB_CMETHOD 1
+#define HAVE_RB_CRANGE 1
+#define HAVE_RB_CREGEXP 1
+#define HAVE_RB_CSTRING 1
+#define HAVE_RB_CSTRUCT 1
+#define HAVE_RB_CSYMBOL 1
+#define HAVE_RB_CTIME 1
+#define HAVE_RB_CTHREAD 1
+#define HAVE_RB_CTRUECLASS 1
+#define HAVE_RB_CNUMERATOR 1
+#define HAVE_RB_EARGERROR 1
+#define HAVE_RB_EEOFERROR 1
+#define HAVE_RB_EEXCEPTION 1
+#define HAVE_RB_EFLOATDOMAINERROR 1
+#define HAVE_RB_EINDEXERROR 1
+#define HAVE_RB_EINTERRUPT 1
+#define HAVE_RB_EIOERROR 1
+#define HAVE_RB_ELOADERROR 1
+#define HAVE_RB_ELOCALJUMPERROR 1
+#define HAVE_RB_EMATHDOMAINERROR 1
+#define HAVE_RB_ENAMEERROR 1
+#define HAVE_RB_ENOMEMERROR 1
+#define HAVE_RB_ENOMETHODERROR 1
+#define HAVE_RB_ENOTIMPERROR 1
+#define HAVE_RB_ERANGEERROR 1
+#define HAVE_RB_EREGEXPERROR 1
+#define HAVE_RB_ERUNTIMEERROR 1
+#define HAVE_RB_ESCRIPTERROR 1
+#define HAVE_RB_ESECURITYERROR 1
+#define HAVE_RB_ESIGNAL 1
+#define HAVE_RB_ESTANDARDERROR 1
+#define HAVE_RB_ESYNTAXERROR 1
+#define HAVE_RB_ESYSSTACKERROR 1
+#define HAVE_RB_ESYSTEMCALLERROR 1
+#define HAVE_RB_ESYSTEMEXIT 1
+#define HAVE_RB_ETHREADERROR 1
+#define HAVE_RB_ETYPEERROR 1
+#define HAVE_RB_EZERODIVERROR 1
+#define HAVE_RB_MCOMPARABLE 1
+#define HAVE_RB_MENUMERABLE 1
+#define HAVE_RB_MERRNO 1
+#define HAVE_RB_MKERNEL 1
+#define HAVE_RB_CDIR 1
+
+/* Data */
+#define HAVE_DATA_WRAP_STRUCT 1
+#define HAVE_RDATA 1
+
+#define HAVE_TYPEDDATA_WRAP_STRUCT 1
+#define HAVE_RTYPEDDATA
+
+/* Encoding */
+#define HAVE_ENCODING_GET 1
+#define HAVE_ENCODING_SET 1
+#define HAVE_ENC_CODERANGE_ASCIIONLY 1
+
+#define HAVE_RB_ASCII8BIT_ENCODING 1
+#define HAVE_RB_ASCII8BIT_ENCINDEX 1
+#define HAVE_RB_USASCII_ENCODING 1
+#define HAVE_RB_USASCII_ENCINDEX 1
+#define HAVE_RB_UTF8_ENCODING 1
+#define HAVE_RB_UTF8_ENCINDEX 1
+#define HAVE_RB_LOCALE_ENCODING 1
+#define HAVE_RB_LOCALE_ENCINDEX 1
+#define HAVE_RB_FILESYSTEM_ENCODING 1
+#define HAVE_RB_FILESYSTEM_ENCINDEX 1
+
+#define HAVE_RB_DEFAULT_INTERNAL_ENCODING 1
+#define HAVE_RB_DEFAULT_EXTERNAL_ENCODING 1
+
+#ifdef RUBY_VERSION_IS_2_6
+#define HAVE_RB_ENC_ALIAS 1
+#endif
+#define HAVE_RB_ENC_ASSOCIATE 1
+#define HAVE_RB_ENC_ASSOCIATE_INDEX 1
+#define HAVE_RB_ENC_CODEPOINT_LEN 1
+#define HAVE_RB_ENC_COMPATIBLE 1
+#define HAVE_RB_ENC_COPY 1
+#define HAVE_RB_ENC_FIND 1
+#define HAVE_RB_ENC_FIND_INDEX 1
+#define HAVE_RB_ENC_FROM_ENCODING 1
+#define HAVE_RB_ENC_FROM_INDEX 1
+#define HAVE_RB_ENC_GET 1
+#define HAVE_RB_ENC_GET_INDEX 1
+#define HAVE_RB_ENC_SET_INDEX 1
+#define HAVE_RB_ENC_STR_CODERANGE 1
+#define HAVE_RB_ENC_STR_NEW 1
+#define HAVE_RB_ENC_TO_INDEX 1
+#define HAVE_RB_OBJ_ENCODING 1
+
+#define HAVE_RB_STR_ENCODE 1
+#define HAVE_RB_STR_NEW_CSTR 1
+#define HAVE_RB_USASCII_STR_NEW 1
+#define HAVE_RB_USASCII_STR_NEW_CSTR 1
+#define HAVE_RB_EXTERNAL_STR_NEW 1
+#define HAVE_RB_EXTERNAL_STR_NEW_CSTR 1
+#define HAVE_RB_EXTERNAL_STR_NEW_WITH_ENC 1
+
+#define HAVE_RB_TO_ENCODING 1
+#define HAVE_RB_TO_ENCODING_INDEX 1
+#define HAVE_RB_ENC_NTH 1
+
+#define HAVE_RB_EENCCOMPATERROR 1
+
+#define HAVE_RB_MWAITREADABLE 1
+#define HAVE_RB_MWAITWRITABLE 1
+
+#define HAVE_RSTRING_LENINT 1
+#define HAVE_TIMET2NUM 1
+
+#define HAVE_RB_LONG2INT 1
+#define HAVE_RB_INTERN3 1
+
+#define HAVE_RB_ITER_BREAK 1
+#define HAVE_RB_SOURCEFILE 1
+#define HAVE_RB_SOURCELINE 1
+#define HAVE_RB_METHOD_BOUNDP 1
+
+/* Enumerable */
+#define HAVE_RB_ENUMERATORIZE 1
+#define HAVE_RB_ENUMERATORIZE_WITH_SIZE 1
+
+/* Exception */
+#define HAVE_RB_EXC_NEW 1
+#define HAVE_RB_EXC_NEW2 1
+#define HAVE_RB_EXC_NEW3 1
+#define HAVE_RB_EXC_RAISE 1
+#define HAVE_RB_SET_ERRINFO 1
+
+/* File */
+#define HAVE_RB_FILE_OPEN 1
+#define HAVE_RB_FILE_OPEN_STR 1
+#define HAVE_FILEPATHVALUE 1
+
+/* Float */
+#define HAVE_RB_FLOAT_NEW 1
+#define HAVE_RB_RFLOAT 1
+#define HAVE_RFLOAT_VALUE 1
+
+/* Globals */
+#define HAVE_RB_DEFAULT_RS 1
+#define HAVE_RB_DEFINE_HOOKED_VARIABLE 1
+#define HAVE_RB_DEFINE_READONLY_VARIABLE 1
+#define HAVE_RB_DEFINE_VARIABLE 1
+#define HAVE_RB_F_GLOBAL_VARIABLES 1
+#define HAVE_RB_GV_GET 1
+#define HAVE_RB_GV_SET 1
+#define HAVE_RB_RS 1
+#define HAVE_RB_OUTPUT_RS 1
+#define HAVE_RB_OUTPUT_FS 1
+#define HAVE_RB_STDERR 1
+#define HAVE_RB_STDIN 1
+#define HAVE_RB_STDOUT 1
+#define HAVE_RB_DEFOUT 1
+
+#define HAVE_RB_LASTLINE_SET 1
+#define HAVE_RB_LASTLINE_GET 1
+
+/* Hash */
+#define HAVE_RB_HASH 1
+#define HAVE_RB_HASH2 1
+#define HAVE_RB_HASH_DUP 1
+#define HAVE_RB_HASH_FREEZE 1
+#define HAVE_RB_HASH_AREF 1
+#define HAVE_RB_HASH_ASET 1
+#define HAVE_RB_HASH_CLEAR 1
+#define HAVE_RB_HASH_DELETE 1
+#define HAVE_RB_HASH_DELETE_IF 1
+#define HAVE_RB_HASH_FETCH 1
+#define HAVE_RB_HASH_FOREACH 1
+#define HAVE_RB_HASH_LOOKUP 1
+#define HAVE_RB_HASH_LOOKUP2 1
+#define HAVE_RB_HASH_NEW 1
+#define HAVE_RB_HASH_SET_IFNONE 1
+#define HAVE_RB_HASH_SIZE 1
+
+/* Integer */
+#define HAVE_RB_INTEGER_PACK 1
+
+/* IO */
+#define HAVE_GET_OPEN_FILE 1
+#define HAVE_RB_IO_ADDSTR 1
+#define HAVE_RB_IO_CHECK_IO 1
+#define HAVE_RB_IO_CHECK_CLOSED 1
+#define HAVE_RB_IO_TAINT_CHECK 1
+#define HAVE_RB_IO_CHECK_READABLE 1
+#define HAVE_RB_IO_CHECK_WRITABLE 1
+#define HAVE_RB_IO_CLOSE 1
+#define HAVE_RB_IO_PRINT 1
+#define HAVE_RB_IO_PRINTF 1
+#define HAVE_RB_IO_PUTS 1
+#define HAVE_RB_IO_WAIT_READABLE 1
+#define HAVE_RB_IO_WAIT_WRITABLE 1
+#define HAVE_RB_IO_WRITE 1
+#define HAVE_RB_IO_BINMODE 1
+
+#define HAVE_RB_THREAD_FD_WRITABLE 1
+#define HAVE_RB_THREAD_WAIT_FD 1
+
+#define HAVE_RB_MUTEX_NEW 1
+#define HAVE_RB_MUTEX_LOCKED_P 1
+#define HAVE_RB_MUTEX_TRYLOCK 1
+#define HAVE_RB_MUTEX_LOCK 1
+#define HAVE_RB_MUTEX_UNLOCK 1
+#define HAVE_RB_MUTEX_SLEEP 1
+#define HAVE_RB_MUTEX_SYNCHRONIZE 1
+
+#define HAVE_RB_FD_FIX_CLOEXEC 1
+#define HAVE_RB_CLOEXEC_OPEN 1
+
+/* Kernel */
+#define HAVE_RB_BLOCK_GIVEN_P 1
+#define HAVE_RB_BLOCK_PROC 1
+#define HAVE_RB_BLOCK_CALL 1
+#define HAVE_RB_ENSURE 1
+#define HAVE_RB_EVAL_STRING 1
+#define HAVE_RB_EXEC_RECURSIVE 1
+#define HAVE_RB_FRAME_THIS_FUNC 1
+#define HAVE_RB_F_SPRINTF 1
+#define HAVE_RB_NEED_BLOCK 1
+#define HAVE_RB_RAISE 1
+#define HAVE_RB_RESCUE 1
+#define HAVE_RB_RESCUE2 1
+#define HAVE_RB_SET_END_PROC 1
+#define HAVE_RB_SYS_FAIL 1
+#define HAVE_RB_SYSERR_FAIL 1
+#define HAVE_RB_MAKE_BACKTRACE 1
+#define HAVE_RB_THROW 1
+#define HAVE_RB_CATCH 1
+#define HAVE_RB_THROW_OBJ 1
+#define HAVE_RB_CATCH_OBJ 1
+#define HAVE_RB_WARN 1
+#define HAVE_RB_YIELD 1
+#define HAVE_RB_YIELD_SPLAT 1
+#define HAVE_RB_YIELD_VALUES 1
+#define HAVE_RB_FUNCALL3 1
+#define HAVE_RB_FUNCALL_WITH_BLOCK 1
+#define HAVE_RB_PROTECT 1
+
+/* GC */
+#define HAVE_RB_GC_REGISTER_ADDRESS 1
+#define HAVE_RB_GC_ENABLE 1
+#define HAVE_RB_GC_DISABLE 1
+#define HAVE_RB_GC 1
+
+/* Marshal */
+#define HAVE_RB_MARSHAL_DUMP 1
+#define HAVE_RB_MARSHAL_LOAD 1
+
+/* Module */
+#define HAVE_RB_ALIAS 1
+#define HAVE_RB_CONST_DEFINED 1
+#define HAVE_RB_CONST_DEFINED_AT 1
+#define HAVE_RB_CONST_GET 1
+#define HAVE_RB_CONST_GET_AT 1
+#define HAVE_RB_CONST_GET_FROM 1
+#define HAVE_RB_CONST_SET 1
+#define HAVE_RB_DEFINE_ALIAS 1
+#define HAVE_RB_DEFINE_CLASS 1
+#define HAVE_RB_DEFINE_CLASS_UNDER 1
+#define HAVE_RB_DEFINE_CLASS_ID_UNDER 1
+#define HAVE_RB_DEFINE_CONST 1
+#define HAVE_RB_DEFINE_GLOBAL_CONST 1
+#define HAVE_RB_DEFINE_GLOBAL_FUNCTION 1
+#define HAVE_RB_DEFINE_METHOD 1
+#define HAVE_RB_DEFINE_MODULE_FUNCTION 1
+#define HAVE_RB_DEFINE_MODULE 1
+#define HAVE_RB_DEFINE_MODULE_UNDER 1
+#define HAVE_RB_DEFINE_PRIVATE_METHOD 1
+#define HAVE_RB_DEFINE_PROTECTED_METHOD 1
+#define HAVE_RB_DEFINE_SINGLETON_METHOD 1
+#define HAVE_RB_MOD_ANCESTORS 1
+#define HAVE_RB_UNDEF 1
+#define HAVE_RB_UNDEF_METHOD 1
+
+/* Numeric */
+#define HAVE_NUM2CHR 1
+#define HAVE_RB_CMPINT 1
+#define HAVE_RB_INT2INUM 1
+#define HAVE_RB_UINT2INUM 1
+#define HAVE_RB_INTEGER 1
+#define HAVE_RB_LL2INUM 1
+#define HAVE_RB_ULL2INUM 1
+#define HAVE_RB_NUM2DBL 1
+#if SIZEOF_INT < SIZEOF_LONG
+#define HAVE_RB_NUM2INT 1
+#define HAVE_RB_NUM2UINT 1
+#endif
+#define HAVE_RB_NUM2LONG 1
+#define HAVE_RB_INT2NUM 1
+#define HAVE_RB_NUM2ULONG 1
+#define HAVE_RB_NUM_COERCE_BIN 1
+#define HAVE_RB_NUM_COERCE_CMP 1
+#define HAVE_RB_NUM_COERCE_RELOP 1
+#define HAVE_RB_ABSINT_SINGLEBIT_P 1
+#define HAVE_RB_NUM_ZERODIV 1
+
+/* Fixnum */
+#if SIZEOF_INT < SIZEOF_LONG
+#define HAVE_RB_FIX2UINT 1
+#define HAVE_RB_FIX2INT 1
+#endif
+
+/* Object */
+#define HAVE_FL_ABLE 1
+#define HAVE_FL_TEST 1
+#define HAVE_OBJ_TAINT 1
+#define HAVE_OBJ_TAINTED 1
+#define HAVE_OBJ_INFECT 1
+#define HAVE_RB_ANY_TO_S 1
+#define HAVE_RB_ATTR_GET 1
+#define HAVE_RB_OBJ_INSTANCE_VARIABLES 1
+#define HAVE_RB_CHECK_ARRAY_TYPE 1
+#define HAVE_RB_CHECK_CONVERT_TYPE 1
+#define HAVE_RB_CHECK_TO_INTEGER 1
+#define HAVE_RB_CHECK_FROZEN 1
+#define HAVE_RB_CHECK_STRING_TYPE 1
+#define HAVE_RB_CLASS_OF 1
+#define HAVE_RB_CONVERT_TYPE 1
+#define HAVE_RB_EQUAL 1
+#define HAVE_RB_CLASS_INHERITED_P 1
+#define HAVE_RB_EXTEND_OBJECT 1
+#define HAVE_RB_INSPECT 1
+#define HAVE_RB_IVAR_DEFINED 1
+#define HAVE_RB_IVAR_GET 1
+#define HAVE_RB_IVAR_SET 1
+#define HAVE_RB_IV_GET 1
+#define HAVE_RB_IV_SET 1
+#define HAVE_RB_OBJ_ALLOC 1
+#define HAVE_RB_OBJ_CALL_INIT 1
+#define HAVE_RB_OBJ_CLASSNAME 1
+#define HAVE_RB_OBJ_DUP 1
+#define HAVE_RB_OBJ_FREEZE 1
+#define HAVE_RB_OBJ_FROZEN_P 1
+#define HAVE_RB_OBJ_ID 1
+#define HAVE_RB_OBJ_INSTANCE_EVAL 1
+#define HAVE_RB_OBJ_IS_INSTANCE_OF 1
+#define HAVE_RB_OBJ_IS_KIND_OF 1
+#define HAVE_RB_OBJ_TAINT 1
+#define HAVE_RB_OBJ_METHOD 1
+#define HAVE_RB_OBJ_METHOD_ARITY 1
+#define HAVE_RB_REQUIRE 1
+#define HAVE_RB_RESPOND_TO 1
+#define HAVE_RB_OBJ_RESPOND_TO 1
+#define HAVE_RB_SPECIAL_CONST_P 1
+#define HAVE_RB_TO_ID 1
+#define HAVE_RB_TO_INT 1
+#define HAVE_RTEST 1
+#define HAVE_TYPE 1
+#define HAVE_RB_TYPE_P 1
+#define HAVE_BUILTIN_TYPE 1
+
+/* Proc */
+#define HAVE_RB_PROC_NEW 1
+#define HAVE_RB_PROC_ARITY 1
+#define HAVE_RB_PROC_CALL 1
+
+/* Range */
+#define HAVE_RB_RANGE_NEW 1
+#define HAVE_RB_RANGE_VALUES 1
+#define HAVE_RB_RANGE_BEG_LEN 1
+
+/* Rational */
+#define HAVE_RB_RATIONAL 1
+#define HAVE_RB_RATIONAL1 1
+#define HAVE_RB_RATIONAL2 1
+#define HAVE_RB_RATIONAL_NEW 1
+#define HAVE_RB_RATIONAL_NEW1 1
+#define HAVE_RB_RATIONAL_NEW2 1
+#define HAVE_RB_RATIONAL_NUM 1
+#define HAVE_RB_RATIONAL_DEN 1
+
+/* Regexp */
+#define HAVE_RB_BACKREF_GET 1
+#define HAVE_RB_REG_MATCH 1
+#define HAVE_RB_REG_NEW 1
+#define HAVE_RB_REG_NTH_MATCH 1
+#define HAVE_RB_REG_OPTIONS 1
+#define HAVE_RB_REG_REGCOMP 1
+
+/* st */
+#define HAVE_RB_ST 1
+
+/* String */
+#define HAVE_RB_CSTR2INUM 1
+#define HAVE_RB_CSTR_TO_INUM 1
+#define HAVE_RB_STR2INUM 1
+#define HAVE_RB_STR_APPEND 1
+#define HAVE_RB_STR_BUF_CAT 1
+#define HAVE_RB_STR_BUF_NEW 1
+#define HAVE_RB_STR_BUF_NEW2 1
+#define HAVE_RB_STR_CAT 1
+#define HAVE_RB_STR_CAT2 1
+#define HAVE_RB_STR_CMP 1
+#define HAVE_RB_STR_DUP 1
+#define HAVE_RB_STR_FLUSH 1
+#define HAVE_RB_STR_FREEZE 1
+#define HAVE_RB_STR_HASH 1
+#define HAVE_RB_STR_UPDATE 1
+#define HAVE_RB_STR_INSPECT 1
+#define HAVE_RB_STR_INTERN 1
+#define HAVE_RB_STR_NEW 1
+#define HAVE_RB_STR_NEW2 1
+#define HAVE_RB_STR_NEW3 1
+#define HAVE_RB_STR_NEW4 1
+#define HAVE_RB_STR_NEW5 1
+#define HAVE_RB_TAINTED_STR_NEW 1
+#define HAVE_RB_TAINTED_STR_NEW2 1
+#define HAVE_RB_STR_PLUS 1
+#define HAVE_RB_STR_TIMES 1
+#define HAVE_RB_STR_RESIZE 1
+#define HAVE_RB_STR_SET_LEN 1
+#define HAVE_RB_STR_SPLIT 1
+#define HAVE_RB_STR_SUBSTR 1
+#define HAVE_RB_STR_TO_STR 1
+#define HAVE_RSTRING_LEN 1
+#define HAVE_RSTRING_PTR 1
+#define HAVE_STRINGVALUE 1
+
+#define HAVE_RB_STR_FREE 1
+#define HAVE_RB_SPRINTF 1
+#define HAVE_RB_LOCALE_STR_NEW 1
+#define HAVE_RB_LOCALE_STR_NEW_CSTR 1
+#define HAVE_RB_STR_CONV_ENC 1
+#define HAVE_RB_STR_CONV_ENC_OPTS 1
+#define HAVE_RB_STR_EXPORT 1
+#define HAVE_RB_STR_EXPORT_LOCALE 1
+#define HAVE_RB_STR_LENGTH 1
+#define HAVE_RB_STR_EQUAL 1
+#define HAVE_RB_STR_SUBSEQ 1
+#define HAVE_RB_VSPRINTF 1
+#define HAVE_RB_STRING 1
+#define HAVE_SAFE_STRING_VALUE 1
+#define HAVE_RB_STRING_VALUE_CSTR 1
+
+/* Struct */
+#define HAVE_RB_STRUCT_AREF 1
+#define HAVE_RB_STRUCT_ASET 1
+#define HAVE_RB_STRUCT_DEFINE 1
+#define HAVE_RB_STRUCT_DEFINE_UNDER 1
+#define HAVE_RB_STRUCT_NEW 1
+#define HAVE_RB_STRUCT_GETMEMBER 1
+#define HAVE_RB_STRUCT_S_MEMBERS 1
+#define HAVE_RB_STRUCT_MEMBERS 1
+#ifdef RUBY_VERSION_IS_2_4
+#define HAVE_RB_STRUCT_SIZE 1
+#endif
+
+/* Symbol */
+#define HAVE_RB_ID2NAME 1
+#define HAVE_RB_ID2STR 1
+#define HAVE_RB_INTERN_STR 1
+#define HAVE_RB_INTERN 1
+#define HAVE_RB_IS_CLASS_ID 1
+#define HAVE_RB_IS_CONST_ID 1
+#define HAVE_RB_IS_INSTANCE_ID 1
+#define HAVE_RB_SYM2STR 1
+
+/* Thread */
+#define HAVE_RB_THREAD_ALONE 1
+#define HAVE_RB_THREAD_CALL_WITHOUT_GVL 1
+#define HAVE_RB_THREAD_CURRENT 1
+#define HAVE_RB_THREAD_LOCAL_AREF 1
+#define HAVE_RB_THREAD_LOCAL_ASET 1
+#define HAVE_RB_THREAD_WAIT_FOR 1
+#define HAVE_RB_THREAD_WAKEUP 1
+#define HAVE_RB_THREAD_CREATE 1
+
+/* Time */
+#define HAVE_RB_TIME_NEW 1
+#define HAVE_RB_TIME_NANO_NEW 1
+#define HAVE_RB_TIME_NUM_NEW 1
+#define HAVE_RB_TIME_INTERVAL 1
+#define HAVE_RB_TIME_TIMEVAL 1
+#define HAVE_RB_TIME_TIMESPEC 1
+#ifdef RUBY_VERSION_IS_2_3
+#define HAVE_RB_TIMESPEC_NOW 1
+#define HAVE_RB_TIME_TIMESPEC_NEW 1
+#endif
+
+/* Util */
+#define HAVE_RB_SCAN_ARGS 1
+
+#endif
diff --git a/spec/ruby/optional/capi/ext/st_spec.c b/spec/ruby/optional/capi/ext/st_spec.c
new file mode 100644
index 0000000000..4e59698d77
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/st_spec.c
@@ -0,0 +1,93 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#include <string.h>
+#include <stdarg.h>
+
+#ifdef HAVE_RB_ST
+#include <ruby/st.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_ST
+
+#if SIZEOF_LONG == SIZEOF_VOIDP
+# define ST2NUM(x) ULONG2NUM(x)
+#else
+# define ST2NUM(x) ULL2NUM(x)
+#endif
+
+VALUE st_spec_st_init_numtable(VALUE self) {
+ st_table *tbl = st_init_numtable();
+ st_index_t entries = tbl->num_entries;
+ st_free_table(tbl);
+ return ST2NUM(entries);
+}
+
+VALUE st_spec_st_init_numtable_with_size(VALUE self) {
+ st_table *tbl = st_init_numtable_with_size(128);
+ st_index_t entries = tbl->num_entries;
+ st_free_table(tbl);
+ return ST2NUM(entries);
+}
+
+VALUE st_spec_st_insert(VALUE self) {
+ st_index_t entries;
+ st_table *tbl = st_init_numtable_with_size(128);
+ st_insert(tbl, 1, 1);
+ entries = tbl->num_entries;
+ st_free_table(tbl);
+ return ST2NUM(entries);
+}
+
+static int sum(st_data_t key, st_data_t value, st_data_t arg) {
+ *(int*)arg += (int)value;
+ return ST_CONTINUE;
+}
+
+VALUE st_spec_st_foreach(VALUE self) {
+ int total = 0;
+ st_table *tbl = st_init_numtable_with_size(128);
+ st_insert(tbl, 1, 3);
+ st_insert(tbl, 2, 4);
+ st_foreach(tbl, sum, (st_data_t)&total);
+ st_free_table(tbl);
+ return INT2FIX(total);
+}
+
+VALUE st_spec_st_lookup(VALUE self) {
+ st_data_t result = (st_data_t)0;
+ st_table *tbl = st_init_numtable_with_size(128);
+ st_insert(tbl, 7, 42);
+ st_insert(tbl, 2, 4);
+ st_lookup(tbl, (st_data_t)7, &result);
+ st_free_table(tbl);
+#if SIZEOF_LONG == SIZEOF_VOIDP
+ return ULONG2NUM(result);
+#else
+ return ULL2NUM(result);
+#endif
+}
+
+#endif
+
+void Init_st_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiStSpecs", rb_cObject);
+
+#ifdef HAVE_RB_ST
+ rb_define_method(cls, "st_init_numtable", st_spec_st_init_numtable, 0);
+ rb_define_method(cls, "st_init_numtable_with_size", st_spec_st_init_numtable_with_size, 0);
+ rb_define_method(cls, "st_insert", st_spec_st_insert, 0);
+ rb_define_method(cls, "st_foreach", st_spec_st_foreach, 0);
+ rb_define_method(cls, "st_lookup", st_spec_st_lookup, 0);
+#endif
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/string_spec.c b/spec/ruby/optional/capi/ext/string_spec.c
new file mode 100644
index 0000000000..a6ce02b25c
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/string_spec.c
@@ -0,0 +1,750 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#include <string.h>
+#include <stdarg.h>
+
+#ifdef HAVE_RUBY_ENCODING_H
+#include "ruby/encoding.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Make sure the RSTRING_PTR and the bytes are in native memory.
+ * On TruffleRuby RSTRING_PTR and the bytes remain in managed memory
+ * until they must be written to native memory.
+ * In some specs we want to test using the native memory. */
+char* NATIVE_RSTRING_PTR(VALUE str) {
+ char* ptr = RSTRING_PTR(str);
+ char** native = malloc(sizeof(char*));
+ *native = ptr;
+ ptr = *native;
+ free(native);
+ return ptr;
+}
+
+#ifdef HAVE_RB_CSTR2INUM
+VALUE string_spec_rb_cstr2inum(VALUE self, VALUE str, VALUE inum) {
+ int num = FIX2INT(inum);
+ return rb_cstr2inum(RSTRING_PTR(str), num);
+}
+#endif
+
+#ifdef HAVE_RB_CSTR_TO_INUM
+static VALUE string_spec_rb_cstr_to_inum(VALUE self, VALUE str, VALUE inum, VALUE badcheck) {
+ int num = FIX2INT(inum);
+ return rb_cstr_to_inum(RSTRING_PTR(str), num, RTEST(badcheck));
+}
+#endif
+
+#ifdef HAVE_RB_STR2INUM
+VALUE string_spec_rb_str2inum(VALUE self, VALUE str, VALUE inum) {
+ int num = FIX2INT(inum);
+ return rb_str2inum(str, num);
+}
+#endif
+
+#ifdef HAVE_RB_STR_APPEND
+VALUE string_spec_rb_str_append(VALUE self, VALUE str, VALUE str2) {
+ return rb_str_append(str, str2);
+}
+#endif
+
+#ifdef HAVE_RB_STR_SET_LEN
+VALUE string_spec_rb_str_set_len(VALUE self, VALUE str, VALUE len) {
+ rb_str_set_len(str, NUM2LONG(len));
+
+ return str;
+}
+
+VALUE string_spec_rb_str_set_len_RSTRING_LEN(VALUE self, VALUE str, VALUE len) {
+ rb_str_set_len(str, NUM2LONG(len));
+
+ return INT2FIX(RSTRING_LEN(str));
+}
+#endif
+
+#ifdef HAVE_RB_STR_BUF_NEW
+VALUE string_spec_rb_str_buf_new(VALUE self, VALUE len, VALUE str) {
+ VALUE buf;
+
+ buf = rb_str_buf_new(NUM2LONG(len));
+
+ if(RTEST(str)) {
+ snprintf(RSTRING_PTR(buf), NUM2LONG(len), "%s", RSTRING_PTR(str));
+ }
+
+ return buf;
+}
+
+VALUE string_spec_rb_str_capacity(VALUE self, VALUE str) {
+ return SIZET2NUM(rb_str_capacity(str));
+}
+#endif
+
+#ifdef HAVE_RB_STR_BUF_NEW2
+VALUE string_spec_rb_str_buf_new2(VALUE self) {
+ return rb_str_buf_new2("hello\0invisible");
+}
+#endif
+
+#ifdef HAVE_RB_STR_BUF_CAT
+VALUE string_spec_rb_str_buf_cat(VALUE self, VALUE str) {
+ const char *question_mark = "?";
+ rb_str_buf_cat(str, question_mark, strlen(question_mark));
+ return str;
+}
+#endif
+
+#ifdef HAVE_RB_STR_CAT
+VALUE string_spec_rb_str_cat(VALUE self, VALUE str) {
+ return rb_str_cat(str, "?", 1);
+}
+#endif
+
+#ifdef HAVE_RB_STR_CAT2
+VALUE string_spec_rb_str_cat2(VALUE self, VALUE str) {
+ return rb_str_cat2(str, "?");
+}
+#endif
+
+#ifdef HAVE_RB_STR_CMP
+VALUE string_spec_rb_str_cmp(VALUE self, VALUE str1, VALUE str2) {
+ return INT2NUM(rb_str_cmp(str1, str2));
+}
+#endif
+
+#ifdef HAVE_RB_STR_CONV_ENC
+VALUE string_spec_rb_str_conv_enc(VALUE self, VALUE str, VALUE from, VALUE to) {
+ rb_encoding* from_enc;
+ rb_encoding* to_enc;
+
+ from_enc = rb_to_encoding(from);
+
+ if(NIL_P(to)) {
+ to_enc = 0;
+ } else {
+ to_enc = rb_to_encoding(to);
+ }
+
+ return rb_str_conv_enc(str, from_enc, to_enc);
+}
+#endif
+
+#ifdef HAVE_RB_STR_CONV_ENC_OPTS
+VALUE string_spec_rb_str_conv_enc_opts(VALUE self, VALUE str, VALUE from, VALUE to,
+ VALUE ecflags, VALUE ecopts)
+{
+ rb_encoding* from_enc;
+ rb_encoding* to_enc;
+
+ from_enc = rb_to_encoding(from);
+
+ if(NIL_P(to)) {
+ to_enc = 0;
+ } else {
+ to_enc = rb_to_encoding(to);
+ }
+
+ return rb_str_conv_enc_opts(str, from_enc, to_enc, FIX2INT(ecflags), ecopts);
+}
+#endif
+
+#ifdef HAVE_RB_STR_EXPORT
+VALUE string_spec_rb_str_export(VALUE self, VALUE str) {
+ return rb_str_export(str);
+}
+#endif
+
+#ifdef HAVE_RB_STR_EXPORT_LOCALE
+VALUE string_spec_rb_str_export_locale(VALUE self, VALUE str) {
+ return rb_str_export_locale(str);
+}
+#endif
+
+#ifdef HAVE_RB_STR_DUP
+VALUE string_spec_rb_str_dup(VALUE self, VALUE str) {
+ return rb_str_dup(str);
+}
+#endif
+
+#ifdef HAVE_RB_STR_FREEZE
+VALUE string_spec_rb_str_freeze(VALUE self, VALUE str) {
+ return rb_str_freeze(str);
+}
+#endif
+
+#ifdef HAVE_RB_STR_INSPECT
+VALUE string_spec_rb_str_inspect(VALUE self, VALUE str) {
+ return rb_str_inspect(str);
+}
+#endif
+
+#ifdef HAVE_RB_STR_INTERN
+VALUE string_spec_rb_str_intern(VALUE self, VALUE str) {
+ return rb_str_intern(str);
+}
+#endif
+
+#ifdef HAVE_RB_STR_LENGTH
+VALUE string_spec_rb_str_length(VALUE self, VALUE str) {
+ return rb_str_length(str);
+}
+#endif
+
+#ifdef HAVE_RB_STR_NEW
+VALUE string_spec_rb_str_new(VALUE self, VALUE str, VALUE len) {
+ return rb_str_new(RSTRING_PTR(str), FIX2INT(len));
+}
+
+VALUE string_spec_rb_str_new_native(VALUE self, VALUE str, VALUE len) {
+ return rb_str_new(NATIVE_RSTRING_PTR(str), FIX2INT(len));
+}
+
+VALUE string_spec_rb_str_new_offset(VALUE self, VALUE str, VALUE offset, VALUE len) {
+ return rb_str_new(RSTRING_PTR(str) + FIX2INT(offset), FIX2INT(len));
+}
+#endif
+
+#ifdef HAVE_RB_STR_NEW2
+VALUE string_spec_rb_str_new2(VALUE self, VALUE str) {
+ if(NIL_P(str)) {
+ return rb_str_new2("");
+ } else {
+ return rb_str_new2(RSTRING_PTR(str));
+ }
+}
+#endif
+
+#ifdef HAVE_RB_STR_ENCODE
+VALUE string_spec_rb_str_encode(VALUE self, VALUE str, VALUE enc, VALUE flags, VALUE opts) {
+ return rb_str_encode(str, enc, FIX2INT(flags), opts);
+}
+#endif
+
+#ifdef HAVE_RB_STR_NEW_CSTR
+VALUE string_spec_rb_str_new_cstr(VALUE self, VALUE str) {
+ if(NIL_P(str)) {
+ return rb_str_new_cstr("");
+ } else {
+ return rb_str_new_cstr(RSTRING_PTR(str));
+ }
+}
+#endif
+
+#ifdef HAVE_RB_EXTERNAL_STR_NEW
+VALUE string_spec_rb_external_str_new(VALUE self, VALUE str) {
+ return rb_external_str_new(RSTRING_PTR(str), RSTRING_LEN(str));
+}
+#endif
+
+#ifdef HAVE_RB_EXTERNAL_STR_NEW_CSTR
+VALUE string_spec_rb_external_str_new_cstr(VALUE self, VALUE str) {
+ return rb_external_str_new_cstr(RSTRING_PTR(str));
+}
+#endif
+
+#ifdef HAVE_RB_EXTERNAL_STR_NEW_WITH_ENC
+VALUE string_spec_rb_external_str_new_with_enc(VALUE self, VALUE str, VALUE len, VALUE encoding) {
+ return rb_external_str_new_with_enc(RSTRING_PTR(str), FIX2LONG(len), rb_to_encoding(encoding));
+}
+#endif
+
+#ifdef HAVE_RB_LOCALE_STR_NEW
+VALUE string_spec_rb_locale_str_new(VALUE self, VALUE str, VALUE len) {
+ return rb_locale_str_new(RSTRING_PTR(str), FIX2INT(len));
+}
+#endif
+
+#ifdef HAVE_RB_LOCALE_STR_NEW_CSTR
+VALUE string_spec_rb_locale_str_new_cstr(VALUE self, VALUE str) {
+ return rb_locale_str_new_cstr(RSTRING_PTR(str));
+}
+#endif
+
+#ifdef HAVE_RB_STR_NEW3
+VALUE string_spec_rb_str_new3(VALUE self, VALUE str) {
+ return rb_str_new3(str);
+}
+#endif
+
+#ifdef HAVE_RB_STR_NEW4
+VALUE string_spec_rb_str_new4(VALUE self, VALUE str) {
+ return rb_str_new4(str);
+}
+#endif
+
+#ifdef HAVE_RB_STR_NEW5
+VALUE string_spec_rb_str_new5(VALUE self, VALUE str, VALUE ptr, VALUE len) {
+ return rb_str_new5(str, RSTRING_PTR(ptr), FIX2INT(len));
+}
+#endif
+
+#ifdef HAVE_RB_TAINTED_STR_NEW
+VALUE string_spec_rb_tainted_str_new(VALUE self, VALUE str, VALUE len) {
+ return rb_tainted_str_new(RSTRING_PTR(str), FIX2INT(len));
+}
+#endif
+
+#ifdef HAVE_RB_TAINTED_STR_NEW2
+VALUE string_spec_rb_tainted_str_new2(VALUE self, VALUE str) {
+ return rb_tainted_str_new2(RSTRING_PTR(str));
+}
+#endif
+
+#ifdef HAVE_RB_STR_PLUS
+VALUE string_spec_rb_str_plus(VALUE self, VALUE str1, VALUE str2) {
+ return rb_str_plus(str1, str2);
+}
+#endif
+
+#ifdef HAVE_RB_STR_TIMES
+VALUE string_spec_rb_str_times(VALUE self, VALUE str, VALUE times) {
+ return rb_str_times(str, times);
+}
+#endif
+
+#ifdef HAVE_RB_STR_RESIZE
+VALUE string_spec_rb_str_resize(VALUE self, VALUE str, VALUE size) {
+ return rb_str_resize(str, FIX2INT(size));
+}
+
+VALUE string_spec_rb_str_resize_RSTRING_LEN(VALUE self, VALUE str, VALUE size) {
+ VALUE modified = rb_str_resize(str, FIX2INT(size));
+ return INT2FIX(RSTRING_LEN(modified));
+}
+#endif
+
+#ifdef HAVE_RB_STR_SPLIT
+VALUE string_spec_rb_str_split(VALUE self, VALUE str) {
+ return rb_str_split(str, ",");
+}
+#endif
+
+#ifdef HAVE_RB_STR_SUBSEQ
+VALUE string_spec_rb_str_subseq(VALUE self, VALUE str, VALUE beg, VALUE len) {
+ return rb_str_subseq(str, FIX2INT(beg), FIX2INT(len));
+}
+#endif
+
+#ifdef HAVE_RB_STR_SUBSTR
+VALUE string_spec_rb_str_substr(VALUE self, VALUE str, VALUE beg, VALUE len) {
+ return rb_str_substr(str, FIX2INT(beg), FIX2INT(len));
+}
+#endif
+
+#ifdef HAVE_RB_STR_TO_STR
+VALUE string_spec_rb_str_to_str(VALUE self, VALUE arg) {
+ return rb_str_to_str(arg);
+}
+#endif
+
+#ifdef HAVE_RSTRING_LEN
+VALUE string_spec_RSTRING_LEN(VALUE self, VALUE str) {
+ return INT2FIX(RSTRING_LEN(str));
+}
+#endif
+
+#ifdef HAVE_RSTRING_LENINT
+VALUE string_spec_RSTRING_LENINT(VALUE self, VALUE str) {
+ return INT2FIX(RSTRING_LENINT(str));
+}
+#endif
+
+#ifdef HAVE_RSTRING_PTR
+VALUE string_spec_RSTRING_PTR_iterate(VALUE self, VALUE str) {
+ int i;
+ char* ptr;
+
+ ptr = RSTRING_PTR(str);
+ for(i = 0; i < RSTRING_LEN(str); i++) {
+ rb_yield(CHR2FIX(ptr[i]));
+ }
+ return Qnil;
+}
+
+VALUE string_spec_RSTRING_PTR_assign(VALUE self, VALUE str, VALUE chr) {
+ int i;
+ char c;
+ char* ptr;
+
+ ptr = RSTRING_PTR(str);
+ c = FIX2INT(chr);
+
+ for(i = 0; i < RSTRING_LEN(str); i++) {
+ ptr[i] = c;
+ }
+ return Qnil;
+}
+
+VALUE string_spec_RSTRING_PTR_set(VALUE self, VALUE str, VALUE i, VALUE chr) {
+ RSTRING_PTR(str)[FIX2INT(i)] = (char) FIX2INT(chr);
+ return str;
+}
+
+VALUE string_spec_RSTRING_PTR_after_funcall(VALUE self, VALUE str, VALUE cb) {
+ /* Silence gcc 4.3.2 warning about computed value not used */
+ if(RSTRING_PTR(str)) { /* force it out */
+ rb_funcall(cb, rb_intern("call"), 1, str);
+ }
+
+ return rb_str_new2(RSTRING_PTR(str));
+}
+
+VALUE string_spec_RSTRING_PTR_after_yield(VALUE self, VALUE str) {
+ char* ptr = NATIVE_RSTRING_PTR(str);
+ long len = RSTRING_LEN(str);
+ VALUE from_rstring_ptr;
+
+ ptr[0] = '1';
+ rb_yield(str);
+ ptr[2] = '2';
+
+ from_rstring_ptr = rb_str_new(ptr, len);
+ return from_rstring_ptr;
+}
+#endif
+
+#ifdef HAVE_STRINGVALUE
+VALUE string_spec_StringValue(VALUE self, VALUE str) {
+ return StringValue(str);
+}
+#endif
+
+#ifdef HAVE_SAFE_STRING_VALUE
+static VALUE string_spec_SafeStringValue(VALUE self, VALUE str) {
+ SafeStringValue(str);
+ return str;
+}
+#endif
+
+#ifdef HAVE_RB_STR_HASH
+static VALUE string_spec_rb_str_hash(VALUE self, VALUE str) {
+ st_index_t val = rb_str_hash(str);
+
+#if SIZEOF_LONG == SIZEOF_VOIDP || SIZEOF_LONG_LONG == SIZEOF_VOIDP
+ return LONG2FIX((long)val);
+#else
+# error unsupported platform
+#endif
+}
+#endif
+
+#ifdef HAVE_RB_STR_UPDATE
+static VALUE string_spec_rb_str_update(VALUE self, VALUE str, VALUE beg, VALUE end, VALUE replacement) {
+ rb_str_update(str, FIX2LONG(beg), FIX2LONG(end), replacement);
+ return str;
+}
+#endif
+
+#ifdef HAVE_RB_STR_FREE
+static VALUE string_spec_rb_str_free(VALUE self, VALUE str) {
+ rb_str_free(str);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_SPRINTF
+static VALUE string_spec_rb_sprintf1(VALUE self, VALUE str, VALUE repl) {
+ return rb_sprintf(RSTRING_PTR(str), RSTRING_PTR(repl));
+}
+static VALUE string_spec_rb_sprintf2(VALUE self, VALUE str, VALUE repl1, VALUE repl2) {
+ return rb_sprintf(RSTRING_PTR(str), RSTRING_PTR(repl1), RSTRING_PTR(repl2));
+}
+#endif
+
+#ifdef HAVE_RB_VSPRINTF
+static VALUE string_spec_rb_vsprintf_worker(char* fmt, ...) {
+ va_list varargs;
+ VALUE str;
+
+ va_start(varargs, fmt);
+ str = rb_vsprintf(fmt, varargs);
+ va_end(varargs);
+
+ return str;
+}
+
+static VALUE string_spec_rb_vsprintf(VALUE self, VALUE fmt, VALUE str, VALUE i, VALUE f) {
+ return string_spec_rb_vsprintf_worker(RSTRING_PTR(fmt), RSTRING_PTR(str),
+ FIX2INT(i), RFLOAT_VALUE(f));
+}
+#endif
+
+#ifdef HAVE_RB_STR_EQUAL
+VALUE string_spec_rb_str_equal(VALUE self, VALUE str1, VALUE str2) {
+ return rb_str_equal(str1, str2);
+}
+#endif
+
+#ifdef HAVE_RB_USASCII_STR_NEW
+static VALUE string_spec_rb_usascii_str_new(VALUE self, VALUE str, VALUE len) {
+ return rb_usascii_str_new(RSTRING_PTR(str), NUM2INT(len));
+}
+#endif
+
+#ifdef HAVE_RB_USASCII_STR_NEW_CSTR
+static VALUE string_spec_rb_usascii_str_new_cstr(VALUE self, VALUE str) {
+ return rb_usascii_str_new_cstr(RSTRING_PTR(str));
+}
+#endif
+
+#ifdef HAVE_RB_STRING
+static VALUE string_spec_rb_String(VALUE self, VALUE val) {
+ return rb_String(val);
+}
+#endif
+
+#ifdef HAVE_RB_STRING_VALUE_CSTR
+static VALUE string_spec_rb_string_value_cstr(VALUE self, VALUE str) {
+ char *c_str = rb_string_value_cstr(&str);
+ return c_str ? Qtrue : Qfalse;
+}
+#endif
+
+void Init_string_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiStringSpecs", rb_cObject);
+
+#ifdef HAVE_RB_CSTR2INUM
+ rb_define_method(cls, "rb_cstr2inum", string_spec_rb_cstr2inum, 2);
+#endif
+
+#ifdef HAVE_RB_CSTR_TO_INUM
+ rb_define_method(cls, "rb_cstr_to_inum", string_spec_rb_cstr_to_inum, 3);
+#endif
+
+#ifdef HAVE_RB_STR2INUM
+ rb_define_method(cls, "rb_str2inum", string_spec_rb_str2inum, 2);
+#endif
+
+#ifdef HAVE_RB_STR_APPEND
+ rb_define_method(cls, "rb_str_append", string_spec_rb_str_append, 2);
+#endif
+
+#ifdef HAVE_RB_STR_BUF_NEW
+ rb_define_method(cls, "rb_str_buf_new", string_spec_rb_str_buf_new, 2);
+ rb_define_method(cls, "rb_str_capacity", string_spec_rb_str_capacity, 1);
+#endif
+
+#ifdef HAVE_RB_STR_BUF_NEW2
+ rb_define_method(cls, "rb_str_buf_new2", string_spec_rb_str_buf_new2, 0);
+#endif
+
+#ifdef HAVE_RB_STR_BUF_CAT
+ rb_define_method(cls, "rb_str_buf_cat", string_spec_rb_str_buf_cat, 1);
+#endif
+
+#ifdef HAVE_RB_STR_CAT
+ rb_define_method(cls, "rb_str_cat", string_spec_rb_str_cat, 1);
+#endif
+
+#ifdef HAVE_RB_STR_CAT2
+ rb_define_method(cls, "rb_str_cat2", string_spec_rb_str_cat2, 1);
+#endif
+
+#ifdef HAVE_RB_STR_CMP
+ rb_define_method(cls, "rb_str_cmp", string_spec_rb_str_cmp, 2);
+#endif
+
+#ifdef HAVE_RB_STR_CONV_ENC
+ rb_define_method(cls, "rb_str_conv_enc", string_spec_rb_str_conv_enc, 3);
+#endif
+
+#ifdef HAVE_RB_STR_CONV_ENC_OPTS
+ rb_define_method(cls, "rb_str_conv_enc_opts", string_spec_rb_str_conv_enc_opts, 5);
+#endif
+
+#ifdef HAVE_RB_STR_EXPORT
+ rb_define_method(cls, "rb_str_export", string_spec_rb_str_export, 1);
+#endif
+
+#ifdef HAVE_RB_STR_EXPORT_LOCALE
+ rb_define_method(cls, "rb_str_export_locale", string_spec_rb_str_export_locale, 1);
+#endif
+
+#ifdef HAVE_RB_STR_DUP
+ rb_define_method(cls, "rb_str_dup", string_spec_rb_str_dup, 1);
+#endif
+
+#ifdef HAVE_RB_STR_FREEZE
+ rb_define_method(cls, "rb_str_freeze", string_spec_rb_str_freeze, 1);
+#endif
+
+#ifdef HAVE_RB_STR_INSPECT
+ rb_define_method(cls, "rb_str_inspect", string_spec_rb_str_inspect, 1);
+#endif
+
+#ifdef HAVE_RB_STR_INTERN
+ rb_define_method(cls, "rb_str_intern", string_spec_rb_str_intern, 1);
+#endif
+
+#ifdef HAVE_RB_STR_LENGTH
+ rb_define_method(cls, "rb_str_length", string_spec_rb_str_length, 1);
+#endif
+
+#ifdef HAVE_RB_STR_NEW
+ rb_define_method(cls, "rb_str_new", string_spec_rb_str_new, 2);
+ rb_define_method(cls, "rb_str_new_native", string_spec_rb_str_new_native, 2);
+ rb_define_method(cls, "rb_str_new_offset", string_spec_rb_str_new_offset, 3);
+#endif
+
+#ifdef HAVE_RB_STR_NEW2
+ rb_define_method(cls, "rb_str_new2", string_spec_rb_str_new2, 1);
+#endif
+
+#ifdef HAVE_RB_STR_ENCODE
+ rb_define_method(cls, "rb_str_encode", string_spec_rb_str_encode, 4);
+#endif
+
+#ifdef HAVE_RB_STR_NEW_CSTR
+ rb_define_method(cls, "rb_str_new_cstr", string_spec_rb_str_new_cstr, 1);
+#endif
+
+#ifdef HAVE_RB_EXTERNAL_STR_NEW
+ rb_define_method(cls, "rb_external_str_new", string_spec_rb_external_str_new, 1);
+#endif
+
+#ifdef HAVE_RB_EXTERNAL_STR_NEW_CSTR
+ rb_define_method(cls, "rb_external_str_new_cstr",
+ string_spec_rb_external_str_new_cstr, 1);
+#endif
+
+#ifdef HAVE_RB_EXTERNAL_STR_NEW_WITH_ENC
+ rb_define_method(cls, "rb_external_str_new_with_enc", string_spec_rb_external_str_new_with_enc, 3);
+#endif
+
+#ifdef HAVE_RB_LOCALE_STR_NEW
+ rb_define_method(cls, "rb_locale_str_new", string_spec_rb_locale_str_new, 2);
+#endif
+
+#ifdef HAVE_RB_LOCALE_STR_NEW_CSTR
+ rb_define_method(cls, "rb_locale_str_new_cstr", string_spec_rb_locale_str_new_cstr, 1);
+#endif
+
+#ifdef HAVE_RB_STR_NEW3
+ rb_define_method(cls, "rb_str_new3", string_spec_rb_str_new3, 1);
+#endif
+
+#ifdef HAVE_RB_STR_NEW4
+ rb_define_method(cls, "rb_str_new4", string_spec_rb_str_new4, 1);
+#endif
+
+#ifdef HAVE_RB_STR_NEW5
+ rb_define_method(cls, "rb_str_new5", string_spec_rb_str_new5, 3);
+#endif
+
+#ifdef HAVE_RB_TAINTED_STR_NEW
+ rb_define_method(cls, "rb_tainted_str_new", string_spec_rb_tainted_str_new, 2);
+#endif
+
+#ifdef HAVE_RB_TAINTED_STR_NEW2
+ rb_define_method(cls, "rb_tainted_str_new2", string_spec_rb_tainted_str_new2, 1);
+#endif
+
+#ifdef HAVE_RB_STR_PLUS
+ rb_define_method(cls, "rb_str_plus", string_spec_rb_str_plus, 2);
+#endif
+
+#ifdef HAVE_RB_STR_TIMES
+ rb_define_method(cls, "rb_str_times", string_spec_rb_str_times, 2);
+#endif
+
+#ifdef HAVE_RB_STR_RESIZE
+ rb_define_method(cls, "rb_str_resize", string_spec_rb_str_resize, 2);
+ rb_define_method(cls, "rb_str_resize_RSTRING_LEN",
+ string_spec_rb_str_resize_RSTRING_LEN, 2);
+#endif
+
+#ifdef HAVE_RB_STR_SET_LEN
+ rb_define_method(cls, "rb_str_set_len", string_spec_rb_str_set_len, 2);
+ rb_define_method(cls, "rb_str_set_len_RSTRING_LEN",
+ string_spec_rb_str_set_len_RSTRING_LEN, 2);
+#endif
+
+#ifdef HAVE_RB_STR_SPLIT
+ rb_define_method(cls, "rb_str_split", string_spec_rb_str_split, 1);
+#endif
+
+#ifdef HAVE_RB_STR_SUBSEQ
+ rb_define_method(cls, "rb_str_subseq", string_spec_rb_str_subseq, 3);
+#endif
+
+#ifdef HAVE_RB_STR_SUBSTR
+ rb_define_method(cls, "rb_str_substr", string_spec_rb_str_substr, 3);
+#endif
+
+#ifdef HAVE_RB_STR_TO_STR
+ rb_define_method(cls, "rb_str_to_str", string_spec_rb_str_to_str, 1);
+#endif
+
+#ifdef HAVE_RSTRING_LEN
+ rb_define_method(cls, "RSTRING_LEN", string_spec_RSTRING_LEN, 1);
+#endif
+
+#ifdef HAVE_RSTRING_LENINT
+ rb_define_method(cls, "RSTRING_LENINT", string_spec_RSTRING_LENINT, 1);
+#endif
+
+#ifdef HAVE_RSTRING_PTR
+ rb_define_method(cls, "RSTRING_PTR_iterate", string_spec_RSTRING_PTR_iterate, 1);
+ rb_define_method(cls, "RSTRING_PTR_assign", string_spec_RSTRING_PTR_assign, 2);
+ rb_define_method(cls, "RSTRING_PTR_set", string_spec_RSTRING_PTR_set, 3);
+ 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);
+#endif
+
+#ifdef HAVE_STRINGVALUE
+ rb_define_method(cls, "StringValue", string_spec_StringValue, 1);
+#endif
+
+#ifdef HAVE_SAFE_STRING_VALUE
+ rb_define_method(cls, "SafeStringValue", string_spec_SafeStringValue, 1);
+#endif
+
+#ifdef HAVE_RB_STR_HASH
+ rb_define_method(cls, "rb_str_hash", string_spec_rb_str_hash, 1);
+#endif
+
+#ifdef HAVE_RB_STR_UPDATE
+ rb_define_method(cls, "rb_str_update", string_spec_rb_str_update, 4);
+#endif
+
+#ifdef HAVE_RB_STR_FREE
+ rb_define_method(cls, "rb_str_free", string_spec_rb_str_free, 1);
+#endif
+
+#ifdef HAVE_RB_SPRINTF
+ rb_define_method(cls, "rb_sprintf1", string_spec_rb_sprintf1, 2);
+ rb_define_method(cls, "rb_sprintf2", string_spec_rb_sprintf2, 3);
+#endif
+
+#ifdef HAVE_RB_VSPRINTF
+ rb_define_method(cls, "rb_vsprintf", string_spec_rb_vsprintf, 4);
+#endif
+
+#ifdef HAVE_RB_STR_EQUAL
+ rb_define_method(cls, "rb_str_equal", string_spec_rb_str_equal, 2);
+#endif
+
+#ifdef HAVE_RB_USASCII_STR_NEW
+ rb_define_method(cls, "rb_usascii_str_new", string_spec_rb_usascii_str_new, 2);
+#endif
+
+#ifdef HAVE_RB_USASCII_STR_NEW_CSTR
+ rb_define_method(cls, "rb_usascii_str_new_cstr", string_spec_rb_usascii_str_new_cstr, 1);
+#endif
+
+#ifdef HAVE_RB_STRING
+ rb_define_method(cls, "rb_String", string_spec_rb_String, 1);
+#endif
+
+#ifdef HAVE_RB_STRING_VALUE_CSTR
+ rb_define_method(cls, "rb_string_value_cstr", string_spec_rb_string_value_cstr, 1);
+#endif
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/struct_spec.c b/spec/ruby/optional/capi/ext/struct_spec.c
new file mode 100644
index 0000000000..8f373d9f48
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/struct_spec.c
@@ -0,0 +1,131 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#include "ruby/intern.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_STRUCT_AREF
+static VALUE struct_spec_rb_struct_aref(VALUE self, VALUE st, VALUE key) {
+ return rb_struct_aref(st, key);
+}
+#endif
+
+#ifdef HAVE_RB_STRUCT_GETMEMBER
+static VALUE struct_spec_rb_struct_getmember(VALUE self, VALUE st, VALUE key) {
+ return rb_struct_getmember(st, SYM2ID(key));
+}
+#endif
+
+#ifdef HAVE_RB_STRUCT_S_MEMBERS
+static VALUE struct_spec_rb_struct_s_members(VALUE self, VALUE klass)
+{
+ return rb_ary_dup(rb_struct_s_members(klass));
+}
+#endif
+
+#ifdef HAVE_RB_STRUCT_MEMBERS
+static VALUE struct_spec_rb_struct_members(VALUE self, VALUE st)
+{
+ return rb_ary_dup(rb_struct_members(st));
+}
+#endif
+
+#ifdef HAVE_RB_STRUCT_ASET
+static VALUE struct_spec_rb_struct_aset(VALUE self, VALUE st, VALUE key, VALUE value) {
+ return rb_struct_aset(st, key, value);
+}
+#endif
+
+#ifdef HAVE_RB_STRUCT_DEFINE
+/* Only allow setting three attributes, should be sufficient for testing. */
+static VALUE struct_spec_struct_define(VALUE self, VALUE name,
+ VALUE attr1, VALUE attr2, VALUE attr3) {
+
+ const char *a1 = StringValuePtr(attr1);
+ const char *a2 = StringValuePtr(attr2);
+ const char *a3 = StringValuePtr(attr3);
+ char *nm = NULL;
+
+ if (name != Qnil) nm = StringValuePtr(name);
+
+ return rb_struct_define(nm, a1, a2, a3, NULL);
+}
+#endif
+
+#ifdef HAVE_RB_STRUCT_DEFINE_UNDER
+/* Only allow setting three attributes, should be sufficient for testing. */
+static VALUE struct_spec_struct_define_under(VALUE self, VALUE outer,
+ VALUE name, VALUE attr1, VALUE attr2, VALUE attr3) {
+
+ const char *nm = StringValuePtr(name);
+ const char *a1 = StringValuePtr(attr1);
+ const char *a2 = StringValuePtr(attr2);
+ const char *a3 = StringValuePtr(attr3);
+
+ return rb_struct_define_under(outer, nm, a1, a2, a3, NULL);
+}
+#endif
+
+#ifdef HAVE_RB_STRUCT_NEW
+static VALUE struct_spec_rb_struct_new(VALUE self, VALUE klass,
+ VALUE a, VALUE b, VALUE c)
+{
+
+ return rb_struct_new(klass, a, b, c);
+}
+#endif
+
+#ifdef HAVE_RB_STRUCT_SIZE
+static VALUE struct_spec_rb_struct_size(VALUE self, VALUE st)
+{
+ return rb_struct_size(st);
+}
+#endif
+
+void Init_struct_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiStructSpecs", rb_cObject);
+
+#ifdef HAVE_RB_STRUCT_AREF
+ rb_define_method(cls, "rb_struct_aref", struct_spec_rb_struct_aref, 2);
+#endif
+
+#ifdef HAVE_RB_STRUCT_GETMEMBER
+ rb_define_method(cls, "rb_struct_getmember", struct_spec_rb_struct_getmember, 2);
+#endif
+
+#ifdef HAVE_RB_STRUCT_S_MEMBERS
+ rb_define_method(cls, "rb_struct_s_members", struct_spec_rb_struct_s_members, 1);
+#endif
+
+#ifdef HAVE_RB_STRUCT_MEMBERS
+ rb_define_method(cls, "rb_struct_members", struct_spec_rb_struct_members, 1);
+#endif
+
+#ifdef HAVE_RB_STRUCT_ASET
+ rb_define_method(cls, "rb_struct_aset", struct_spec_rb_struct_aset, 3);
+#endif
+
+#ifdef HAVE_RB_STRUCT_DEFINE
+ rb_define_method(cls, "rb_struct_define", struct_spec_struct_define, 4);
+#endif
+
+#ifdef HAVE_RB_STRUCT_DEFINE_UNDER
+ rb_define_method(cls, "rb_struct_define_under", struct_spec_struct_define_under, 5);
+#endif
+
+#ifdef HAVE_RB_STRUCT_NEW
+ rb_define_method(cls, "rb_struct_new", struct_spec_rb_struct_new, 4);
+#endif
+
+#ifdef HAVE_RB_STRUCT_SIZE
+ rb_define_method(cls, "rb_struct_size", struct_spec_rb_struct_size, 1);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/symbol_spec.c b/spec/ruby/optional/capi/ext/symbol_spec.c
new file mode 100644
index 0000000000..7ffa7cf9b1
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/symbol_spec.c
@@ -0,0 +1,138 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef HAVE_RUBY_ENCODING_H
+#include "ruby/encoding.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_INTERN
+VALUE symbol_spec_rb_intern(VALUE self, VALUE string) {
+ return ID2SYM(rb_intern(RSTRING_PTR(string)));
+}
+
+VALUE symbol_spec_rb_intern2(VALUE self, VALUE string, VALUE len) {
+ return ID2SYM(rb_intern2(RSTRING_PTR(string), FIX2LONG(len)));
+}
+
+VALUE symbol_spec_rb_intern_const(VALUE self, VALUE string) {
+ return ID2SYM(rb_intern_const(RSTRING_PTR(string)));
+}
+
+VALUE symbol_spec_rb_intern_c_compare(VALUE self, VALUE string, VALUE sym) {
+ ID symbol = rb_intern(RSTRING_PTR(string));
+ return (SYM2ID(sym) == symbol) ? Qtrue : Qfalse;
+}
+
+VALUE symbol_spec_rb_intern2_c_compare(VALUE self, VALUE string, VALUE len, VALUE sym) {
+ ID symbol = rb_intern2(RSTRING_PTR(string), FIX2LONG(len));
+ return (SYM2ID(sym) == symbol) ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_INTERN3
+VALUE symbol_spec_rb_intern3(VALUE self, VALUE string, VALUE len, VALUE enc) {
+ return ID2SYM(rb_intern3(RSTRING_PTR(string), FIX2LONG(len), rb_enc_get(enc)));
+}
+
+VALUE symbol_spec_rb_intern3_c_compare(VALUE self, VALUE string, VALUE len, VALUE enc, VALUE sym) {
+ ID symbol = rb_intern3(RSTRING_PTR(string), FIX2LONG(len), rb_enc_get(enc));
+ return (SYM2ID(sym) == symbol) ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_ID2NAME
+VALUE symbol_spec_rb_id2name(VALUE self, VALUE symbol) {
+ const char* c_str = rb_id2name(SYM2ID(symbol));
+ return rb_str_new(c_str, strlen(c_str));
+}
+#endif
+
+#ifdef HAVE_RB_ID2STR
+VALUE symbol_spec_rb_id2str(VALUE self, VALUE symbol) {
+ return rb_id2str(SYM2ID(symbol));
+}
+#endif
+
+#ifdef HAVE_RB_INTERN_STR
+VALUE symbol_spec_rb_intern_str(VALUE self, VALUE str) {
+ return ID2SYM(rb_intern_str(str));
+}
+#endif
+
+#ifdef HAVE_RB_IS_CLASS_ID
+VALUE symbol_spec_rb_is_class_id(VALUE self, VALUE sym) {
+ return rb_is_class_id(SYM2ID(sym)) ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_IS_CONST_ID
+VALUE symbol_spec_rb_is_const_id(VALUE self, VALUE sym) {
+ return rb_is_const_id(SYM2ID(sym)) ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_IS_INSTANCE_ID
+VALUE symbol_spec_rb_is_instance_id(VALUE self, VALUE sym) {
+ return rb_is_instance_id(SYM2ID(sym)) ? Qtrue : Qfalse;
+}
+#endif
+
+#ifdef HAVE_RB_SYM2STR
+VALUE symbol_spec_rb_sym2str(VALUE self, VALUE sym) {
+ return rb_sym2str(sym);
+}
+#endif
+
+void Init_symbol_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiSymbolSpecs", rb_cObject);
+
+#ifdef HAVE_RB_INTERN
+ rb_define_method(cls, "rb_intern", symbol_spec_rb_intern, 1);
+ rb_define_method(cls, "rb_intern2", symbol_spec_rb_intern2, 2);
+ rb_define_method(cls, "rb_intern_const", symbol_spec_rb_intern_const, 1);
+ rb_define_method(cls, "rb_intern_c_compare", symbol_spec_rb_intern_c_compare, 2);
+ rb_define_method(cls, "rb_intern2_c_compare", symbol_spec_rb_intern2_c_compare, 3);
+#endif
+
+#ifdef HAVE_RB_INTERN3
+ rb_define_method(cls, "rb_intern3", symbol_spec_rb_intern3, 3);
+ rb_define_method(cls, "rb_intern3_c_compare", symbol_spec_rb_intern3_c_compare, 4);
+#endif
+
+#ifdef HAVE_RB_ID2NAME
+ rb_define_method(cls, "rb_id2name", symbol_spec_rb_id2name, 1);
+#endif
+
+#ifdef HAVE_RB_ID2STR
+ rb_define_method(cls, "rb_id2str", symbol_spec_rb_id2str, 1);
+#endif
+
+#ifdef HAVE_RB_INTERN_STR
+ rb_define_method(cls, "rb_intern_str", symbol_spec_rb_intern_str, 1);
+#endif
+
+#ifdef HAVE_RB_IS_CLASS_ID
+ rb_define_method(cls, "rb_is_class_id", symbol_spec_rb_is_class_id, 1);
+#endif
+
+#ifdef HAVE_RB_IS_CONST_ID
+ rb_define_method(cls, "rb_is_const_id", symbol_spec_rb_is_const_id, 1);
+#endif
+
+#ifdef HAVE_RB_IS_INSTANCE_ID
+ rb_define_method(cls, "rb_is_instance_id", symbol_spec_rb_is_instance_id, 1);
+#endif
+
+#ifdef HAVE_RB_SYM2STR
+ rb_define_method(cls, "rb_sym2str", symbol_spec_rb_sym2str, 1);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/thread_spec.c b/spec/ruby/optional/capi/ext/thread_spec.c
new file mode 100644
index 0000000000..fed1b1f3b0
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/thread_spec.c
@@ -0,0 +1,188 @@
+#include "ruby.h"
+#include "ruby/thread.h"
+#include "rubyspec.h"
+
+#include <math.h>
+#include <errno.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if defined(_WIN32)
+#define pipe(p) rb_w32_pipe(p)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_THREAD_ALONE
+static VALUE thread_spec_rb_thread_alone() {
+ return rb_thread_alone() ? Qtrue : Qfalse;
+}
+#endif
+
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
+/* This is unblocked by unblock_func(). */
+static void* blocking_gvl_func(void* data) {
+ int rfd = *(int *)data;
+ char dummy = ' ';
+ ssize_t r;
+
+ do {
+ r = read(rfd, &dummy, 1);
+ } while (r == -1 && errno == EINTR);
+
+ close(rfd);
+
+ return (void*)((r == 1 && dummy == 'A') ? Qtrue : Qfalse);
+}
+
+static void unblock_gvl_func(void *data) {
+ int wfd = *(int *)data;
+ char dummy = 'A';
+ ssize_t r;
+
+ do {
+ r = write(wfd, &dummy, 1);
+ } while (r == -1 && errno == EINTR);
+
+ close(wfd);
+}
+
+/* Returns true if the thread is interrupted. */
+static VALUE thread_spec_rb_thread_call_without_gvl(VALUE self) {
+ int fds[2];
+ void* ret;
+
+ if (pipe(fds) == -1) {
+ rb_raise(rb_eRuntimeError, "could not create pipe");
+ }
+ ret = rb_thread_call_without_gvl(blocking_gvl_func, &fds[0],
+ unblock_gvl_func, &fds[1]);
+ return (VALUE)ret;
+}
+
+/* This is unblocked by a signal. */
+static void* blocking_gvl_func_for_udf_io(void *data) {
+ int rfd = (int)(size_t)data;
+ char dummy;
+
+ if (read(rfd, &dummy, 1) == -1 && errno == EINTR) {
+ return (void*)Qtrue;
+ } else {
+ return (void*)Qfalse;
+ }
+}
+
+/* Returns true if the thread is interrupted. */
+static VALUE thread_spec_rb_thread_call_without_gvl_with_ubf_io(VALUE self) {
+ int fds[2];
+ void* ret;
+
+ if (pipe(fds) == -1) {
+ rb_raise(rb_eRuntimeError, "could not create pipe");
+ }
+
+ ret = rb_thread_call_without_gvl(blocking_gvl_func_for_udf_io,
+ (void*)(size_t)fds[0], RUBY_UBF_IO, 0);
+ close(fds[0]);
+ close(fds[1]);
+ return (VALUE)ret;
+}
+#endif
+
+#ifdef HAVE_RB_THREAD_CURRENT
+static VALUE thread_spec_rb_thread_current() {
+ return rb_thread_current();
+}
+#endif
+
+#ifdef HAVE_RB_THREAD_LOCAL_AREF
+static VALUE thread_spec_rb_thread_local_aref(VALUE self, VALUE thr, VALUE sym) {
+ return rb_thread_local_aref(thr, SYM2ID(sym));
+}
+#endif
+
+#ifdef HAVE_RB_THREAD_LOCAL_ASET
+static VALUE thread_spec_rb_thread_local_aset(VALUE self, VALUE thr, VALUE sym, VALUE value) {
+ return rb_thread_local_aset(thr, SYM2ID(sym), value);
+}
+#endif
+
+#ifdef HAVE_RB_THREAD_WAKEUP
+static VALUE thread_spec_rb_thread_wakeup(VALUE self, VALUE thr) {
+ return rb_thread_wakeup(thr);
+}
+#endif
+
+#ifdef HAVE_RB_THREAD_WAIT_FOR
+static VALUE thread_spec_rb_thread_wait_for(VALUE self, VALUE s, VALUE ms) {
+ struct timeval tv;
+ tv.tv_sec = NUM2INT(s);
+ tv.tv_usec = NUM2INT(ms);
+ rb_thread_wait_for(tv);
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_THREAD_CREATE
+
+VALUE thread_spec_call_proc(VALUE arg_array) {
+ VALUE arg = rb_ary_pop(arg_array);
+ VALUE proc = rb_ary_pop(arg_array);
+ return rb_funcall(proc, rb_intern("call"), 1, arg);
+}
+
+static VALUE thread_spec_rb_thread_create(VALUE self, VALUE proc, VALUE arg) {
+ VALUE args = rb_ary_new();
+ rb_ary_push(args, proc);
+ rb_ary_push(args, arg);
+
+ return rb_thread_create(thread_spec_call_proc, (void*)args);
+}
+#endif
+
+
+void Init_thread_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiThreadSpecs", rb_cObject);
+
+#ifdef HAVE_RB_THREAD_ALONE
+ rb_define_method(cls, "rb_thread_alone", thread_spec_rb_thread_alone, 0);
+#endif
+
+#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
+ rb_define_method(cls, "rb_thread_call_without_gvl", thread_spec_rb_thread_call_without_gvl, 0);
+ rb_define_method(cls, "rb_thread_call_without_gvl_with_ubf_io", thread_spec_rb_thread_call_without_gvl_with_ubf_io, 0);
+#endif
+
+#ifdef HAVE_RB_THREAD_CURRENT
+ rb_define_method(cls, "rb_thread_current", thread_spec_rb_thread_current, 0);
+#endif
+
+#ifdef HAVE_RB_THREAD_LOCAL_AREF
+ rb_define_method(cls, "rb_thread_local_aref", thread_spec_rb_thread_local_aref, 2);
+#endif
+
+#ifdef HAVE_RB_THREAD_LOCAL_ASET
+ rb_define_method(cls, "rb_thread_local_aset", thread_spec_rb_thread_local_aset, 3);
+#endif
+
+#ifdef HAVE_RB_THREAD_WAKEUP
+ rb_define_method(cls, "rb_thread_wakeup", thread_spec_rb_thread_wakeup, 1);
+#endif
+
+#ifdef HAVE_RB_THREAD_WAIT_FOR
+ rb_define_method(cls, "rb_thread_wait_for", thread_spec_rb_thread_wait_for, 2);
+#endif
+
+#ifdef HAVE_RB_THREAD_CREATE
+ rb_define_method(cls, "rb_thread_create", thread_spec_rb_thread_create, 2);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/time_spec.c b/spec/ruby/optional/capi/ext/time_spec.c
new file mode 100644
index 0000000000..6b51120f1b
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/time_spec.c
@@ -0,0 +1,127 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_TIME_NEW
+static VALUE time_spec_rb_time_new(VALUE self, VALUE sec, VALUE usec) {
+ return rb_time_new(NUM2TIMET(sec), NUM2LONG(usec));
+}
+#endif
+
+#ifdef HAVE_RB_TIME_NANO_NEW
+static VALUE time_spec_rb_time_nano_new(VALUE self, VALUE sec, VALUE nsec) {
+ return rb_time_nano_new(NUM2TIMET(sec), NUM2LONG(nsec));
+}
+#endif
+
+#ifdef HAVE_RB_TIME_NUM_NEW
+static VALUE time_spec_rb_time_num_new(VALUE self, VALUE ts, VALUE offset) {
+ return rb_time_num_new(ts, offset);
+}
+#endif
+
+#ifdef HAVE_RB_TIME_INTERVAL
+static VALUE time_spec_rb_time_interval(VALUE self, VALUE ts) {
+ struct timeval interval = rb_time_interval(ts);
+ VALUE ary = rb_ary_new();
+ rb_ary_push(ary, TIMET2NUM(interval.tv_sec));
+ rb_ary_push(ary, TIMET2NUM(interval.tv_usec));
+ return ary;
+}
+#endif
+
+#ifdef HAVE_RB_TIME_TIMEVAL
+static VALUE time_spec_rb_time_timeval(VALUE self, VALUE ts) {
+ struct timeval tv = rb_time_timeval(ts);
+ VALUE ary = rb_ary_new();
+ rb_ary_push(ary, TIMET2NUM(tv.tv_sec));
+ rb_ary_push(ary, TIMET2NUM(tv.tv_usec));
+ return ary;
+}
+#endif
+
+#ifdef HAVE_RB_TIME_TIMESPEC
+static VALUE time_spec_rb_time_timespec(VALUE self, VALUE time) {
+ struct timespec ts = rb_time_timespec(time);
+ VALUE ary = rb_ary_new();
+ rb_ary_push(ary, TIMET2NUM(ts.tv_sec));
+ rb_ary_push(ary, TIMET2NUM(ts.tv_nsec));
+ return ary;
+}
+#endif
+
+#ifdef HAVE_RB_TIME_TIMESPEC_NEW
+static VALUE time_spec_rb_time_timespec_new(VALUE self, VALUE sec, VALUE nsec, VALUE offset) {
+ struct timespec ts;
+ ts.tv_sec = NUM2TIMET(sec);
+ ts.tv_nsec = NUM2LONG(nsec);
+
+ return rb_time_timespec_new(&ts, NUM2INT(offset));
+}
+#endif
+
+#ifdef HAVE_RB_TIMESPEC_NOW
+static VALUE time_spec_rb_time_from_timspec_now(VALUE self, VALUE offset) {
+ struct timespec ts;
+ rb_timespec_now(&ts);
+
+ return rb_time_timespec_new(&ts, NUM2INT(offset));
+}
+#endif
+
+#ifdef HAVE_TIMET2NUM
+static VALUE time_spec_TIMET2NUM(VALUE self) {
+ time_t t = 10;
+ return TIMET2NUM(t);
+}
+#endif
+
+void Init_time_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiTimeSpecs", rb_cObject);
+
+#ifdef HAVE_RB_TIME_NEW
+ rb_define_method(cls, "rb_time_new", time_spec_rb_time_new, 2);
+#endif
+
+#ifdef HAVE_TIMET2NUM
+ rb_define_method(cls, "TIMET2NUM", time_spec_TIMET2NUM, 0);
+#endif
+
+#ifdef HAVE_RB_TIME_NANO_NEW
+ rb_define_method(cls, "rb_time_nano_new", time_spec_rb_time_nano_new, 2);
+#endif
+
+#ifdef HAVE_RB_TIME_NUM_NEW
+ rb_define_method(cls, "rb_time_num_new", time_spec_rb_time_num_new, 2);
+#endif
+
+#ifdef HAVE_RB_TIME_INTERVAL
+ rb_define_method(cls, "rb_time_interval", time_spec_rb_time_interval, 1);
+#endif
+
+#ifdef HAVE_RB_TIME_TIMEVAL
+ rb_define_method(cls, "rb_time_timeval", time_spec_rb_time_timeval, 1);
+#endif
+
+#ifdef HAVE_RB_TIME_TIMESPEC
+ rb_define_method(cls, "rb_time_timespec", time_spec_rb_time_timespec, 1);
+#endif
+
+#ifdef HAVE_RB_TIME_TIMESPEC_NEW
+ rb_define_method(cls, "rb_time_timespec_new", time_spec_rb_time_timespec_new, 3);
+#endif
+
+#ifdef HAVE_RB_TIMESPEC_NOW
+ rb_define_method(cls, "rb_time_from_timespec", time_spec_rb_time_from_timspec_now, 1);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/typed_data_spec.c b/spec/ruby/optional/capi/ext/typed_data_spec.c
new file mode 100644
index 0000000000..2a82f0133f
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/typed_data_spec.c
@@ -0,0 +1,170 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(HAVE_RTYPEDDATA) && defined(HAVE_TYPEDDATA_WRAP_STRUCT)
+struct sample_typed_wrapped_struct_parent {
+ int foo;
+};
+
+void sample_typed_wrapped_struct_parent_free(void* st) {
+ free(st);
+}
+
+void sample_typed_wrapped_struct_parent_mark(void* st) {
+}
+
+size_t sample_typed_wrapped_struct_parent_memsize(const void* st) {
+ return sizeof(struct sample_typed_wrapped_struct_parent);
+}
+
+static const rb_data_type_t sample_typed_wrapped_struct_parent_data_type = {
+ "sample_typed_wrapped_struct_parent",
+ {
+ sample_typed_wrapped_struct_parent_mark,
+ sample_typed_wrapped_struct_parent_free,
+ sample_typed_wrapped_struct_parent_memsize,
+ },
+};
+
+struct sample_typed_wrapped_struct {
+ int foo;
+};
+
+void sample_typed_wrapped_struct_free(void* st) {
+ free(st);
+}
+
+void sample_typed_wrapped_struct_mark(void* st) {
+}
+
+size_t sample_typed_wrapped_struct_memsize(const void* st) {
+ return sizeof(struct sample_typed_wrapped_struct);
+}
+
+static const rb_data_type_t sample_typed_wrapped_struct_data_type = {
+ "sample_typed_wrapped_struct",
+ {
+ sample_typed_wrapped_struct_mark,
+ sample_typed_wrapped_struct_free,
+ sample_typed_wrapped_struct_memsize,
+ },
+ &sample_typed_wrapped_struct_parent_data_type,
+};
+
+struct sample_typed_wrapped_struct_other {
+ int foo;
+};
+
+void sample_typed_wrapped_struct_other_free(void* st) {
+ free(st);
+}
+
+void sample_typed_wrapped_struct_other_mark(void* st) {
+}
+
+size_t sample_typed_wrapped_struct_other_memsize(const void* st) {
+ return sizeof(struct sample_typed_wrapped_struct_other);
+}
+
+static const rb_data_type_t sample_typed_wrapped_struct_other_data_type = {
+ "sample_typed_wrapped_struct_other",
+ {
+ sample_typed_wrapped_struct_other_mark,
+ sample_typed_wrapped_struct_other_free,
+ sample_typed_wrapped_struct_other_memsize,
+ },
+};
+
+
+VALUE sdaf_alloc_typed_func(VALUE klass) {
+ struct sample_typed_wrapped_struct* bar = (struct sample_typed_wrapped_struct *)malloc(sizeof(struct sample_typed_wrapped_struct));
+ bar->foo = 42;
+ return TypedData_Wrap_Struct(klass, &sample_typed_wrapped_struct_data_type, bar);
+}
+
+VALUE sdaf_typed_get_struct(VALUE self) {
+ struct sample_typed_wrapped_struct* bar;
+ TypedData_Get_Struct(self, struct sample_typed_wrapped_struct, &sample_typed_wrapped_struct_data_type, bar);
+
+ return INT2FIX((*bar).foo);
+}
+
+VALUE sws_typed_wrap_struct(VALUE self, VALUE val) {
+ struct sample_typed_wrapped_struct* bar = (struct sample_typed_wrapped_struct *)malloc(sizeof(struct sample_typed_wrapped_struct));
+ bar->foo = FIX2INT(val);
+ return TypedData_Wrap_Struct(rb_cObject, &sample_typed_wrapped_struct_data_type, bar);
+}
+
+VALUE sws_typed_get_struct(VALUE self, VALUE obj) {
+ struct sample_typed_wrapped_struct* bar;
+ TypedData_Get_Struct(obj, struct sample_typed_wrapped_struct, &sample_typed_wrapped_struct_data_type, bar);
+
+ return INT2FIX((*bar).foo);
+}
+
+VALUE sws_typed_get_struct_different_type(VALUE self, VALUE obj) {
+ struct sample_typed_wrapped_struct_other* bar;
+ TypedData_Get_Struct(obj, struct sample_typed_wrapped_struct_other, &sample_typed_wrapped_struct_other_data_type, bar);
+
+ return INT2FIX((*bar).foo);
+}
+
+VALUE sws_typed_get_struct_parent_type(VALUE self, VALUE obj) {
+ struct sample_typed_wrapped_struct_parent* bar;
+ TypedData_Get_Struct(obj, struct sample_typed_wrapped_struct_parent, &sample_typed_wrapped_struct_parent_data_type, bar);
+
+ return INT2FIX((*bar).foo);
+}
+
+VALUE sws_typed_get_struct_rdata(VALUE self, VALUE obj) {
+ struct sample_typed_wrapped_struct* bar;
+ bar = (struct sample_typed_wrapped_struct*) RTYPEDDATA(obj)->data;
+ return INT2FIX(bar->foo);
+}
+
+VALUE sws_typed_get_struct_data_ptr(VALUE self, VALUE obj) {
+ struct sample_typed_wrapped_struct* bar;
+ bar = (struct sample_typed_wrapped_struct*) DATA_PTR(obj);
+ return INT2FIX(bar->foo);
+}
+
+VALUE sws_typed_change_struct(VALUE self, VALUE obj, VALUE new_val) {
+ struct sample_typed_wrapped_struct *old_struct, *new_struct;
+ new_struct = (struct sample_typed_wrapped_struct *)malloc(sizeof(struct sample_typed_wrapped_struct));
+ new_struct->foo = FIX2INT(new_val);
+ old_struct = RTYPEDDATA(obj)->data;
+ free(old_struct);
+ RTYPEDDATA(obj)->data = new_struct;
+ return Qnil;
+}
+#endif
+
+void Init_typed_data_spec(void) {
+ VALUE cls;
+ cls = rb_define_class("CApiAllocTypedSpecs", rb_cObject);
+
+#if defined(HAVE_RTYPEDDATA) && defined(HAVE_TYPEDDATA_WRAP_STRUCT)
+ rb_define_alloc_func(cls, sdaf_alloc_typed_func);
+ rb_define_method(cls, "typed_wrapped_data", sdaf_typed_get_struct, 0);
+
+ cls = rb_define_class("CApiWrappedTypedStructSpecs", rb_cObject);
+ rb_define_method(cls, "typed_wrap_struct", sws_typed_wrap_struct, 1);
+ rb_define_method(cls, "typed_get_struct", sws_typed_get_struct, 1);
+ rb_define_method(cls, "typed_get_struct_other", sws_typed_get_struct_different_type, 1);
+ rb_define_method(cls, "typed_get_struct_parent", sws_typed_get_struct_parent_type, 1);
+ rb_define_method(cls, "typed_get_struct_rdata", sws_typed_get_struct_rdata, 1);
+ rb_define_method(cls, "typed_get_struct_data_ptr", sws_typed_get_struct_data_ptr, 1);
+ rb_define_method(cls, "typed_change_struct", sws_typed_change_struct, 2);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/spec/ruby/optional/capi/ext/util_spec.c b/spec/ruby/optional/capi/ext/util_spec.c
new file mode 100644
index 0000000000..50795b51af
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/util_spec.c
@@ -0,0 +1,95 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_RB_SCAN_ARGS
+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);
+ }
+
+ result = rb_scan_args(argc, args, RSTRING_PTR(fmt), &a1, &a2, &a3, &a4, &a5, &a6);
+
+ switch(NUM2INT(expected)) {
+ case 6:
+ rb_ary_unshift(acc, a6);
+ case 5:
+ rb_ary_unshift(acc, a5);
+ case 4:
+ rb_ary_unshift(acc, a4);
+ case 3:
+ rb_ary_unshift(acc, a3);
+ case 2:
+ rb_ary_unshift(acc, a2);
+ case 1:
+ rb_ary_unshift(acc, a1);
+ break;
+ default:
+ rb_raise(rb_eException, "unexpected number of arguments returned by rb_scan_args");
+ }
+
+ return INT2NUM(result);
+}
+#endif
+
+#ifdef HAVE_RB_LONG2INT
+static VALUE util_spec_rb_long2int(VALUE self, VALUE n) {
+ return INT2NUM(rb_long2int(NUM2LONG(n)));
+}
+#endif
+
+#ifdef HAVE_RB_ITER_BREAK
+static VALUE util_spec_rb_iter_break(VALUE self) {
+ rb_iter_break();
+ return Qnil;
+}
+#endif
+
+#ifdef HAVE_RB_SOURCEFILE
+static VALUE util_spec_rb_sourcefile(VALUE self) {
+ return rb_str_new2(rb_sourcefile());
+}
+#endif
+
+#ifdef HAVE_RB_SOURCELINE
+static VALUE util_spec_rb_sourceline(VALUE self) {
+ return INT2NUM(rb_sourceline());
+}
+#endif
+
+void Init_util_spec(void) {
+ VALUE cls = rb_define_class("CApiUtilSpecs", rb_cObject);
+
+#ifdef HAVE_RB_SCAN_ARGS
+ rb_define_method(cls, "rb_scan_args", util_spec_rb_scan_args, 4);
+#endif
+
+#ifdef HAVE_RB_LONG2INT
+ rb_define_method(cls, "rb_long2int", util_spec_rb_long2int, 1);
+#endif
+
+#ifdef HAVE_RB_ITER_BREAK
+ rb_define_method(cls, "rb_iter_break", util_spec_rb_iter_break, 0);
+#endif
+
+#ifdef HAVE_RB_SOURCEFILE
+ rb_define_method(cls, "rb_sourcefile", util_spec_rb_sourcefile, 0);
+#endif
+
+#ifdef HAVE_RB_SOURCELINE
+ rb_define_method(cls, "rb_sourceline", util_spec_rb_sourceline, 0);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/file_spec.rb b/spec/ruby/optional/capi/file_spec.rb
new file mode 100644
index 0000000000..534a03b484
--- /dev/null
+++ b/spec/ruby/optional/capi/file_spec.rb
@@ -0,0 +1,89 @@
+require_relative 'spec_helper'
+
+load_extension('file')
+
+describe :rb_file_open, shared: true do
+ it "raises an ArgumentError if passed an empty mode string" do
+ touch @name
+ lambda { @s.rb_file_open(@name, "") }.should raise_error(ArgumentError)
+ end
+
+ it "opens a file in read-only mode with 'r'" do
+ touch(@name) { |f| f.puts "readable" }
+ @file = @s.send(@method, @name, "r")
+ @file.should be_an_instance_of(File)
+ @file.read.chomp.should == "readable"
+ end
+
+ it "creates and opens a non-existent file with 'w'" do
+ @file = @s.send(@method, @name, "w")
+ @file.write "writable"
+ @file.flush
+ File.read(@name).should == "writable"
+ end
+
+ it "truncates an existing file with 'w'" do
+ touch(@name) { |f| f.puts "existing" }
+ @file = @s.send(@method, @name, "w")
+ File.read(@name).should == ""
+ end
+end
+
+describe "C-API File function" do
+ before :each do
+ @s = CApiFileSpecs.new
+ @name = tmp("rb_file_open")
+ end
+
+ after :each do
+ @file.close if @file and !@file.closed?
+ rm_r @name
+ end
+
+ describe "rb_file_open" do
+ it_behaves_like :rb_file_open, :rb_file_open
+ end
+
+ describe "rb_file_open_str" do
+ it_behaves_like :rb_file_open, :rb_file_open_str
+ end
+
+ describe "rb_file_open_str" do
+ it "calls #to_path to convert on object to a path" do
+ path = mock("rb_file_open_str to_path")
+ path.should_receive(:to_path).and_return(@name)
+ @file = @s.rb_file_open_str(path, "w")
+ end
+
+ it "calls #to_str to convert an object to a path if #to_path isn't defined" do
+ path = mock("rb_file_open_str to_str")
+ path.should_receive(:to_str).and_return(@name)
+ @file = @s.rb_file_open_str(path, "w")
+ end
+ end
+
+ describe "FilePathValue" do
+ it "returns a String argument unchanged" do
+ obj = "path"
+ @s.FilePathValue(obj).should eql(obj)
+ end
+
+ it "does not call #to_str on a String" do
+ obj = "path"
+ obj.should_not_receive(:to_str)
+ @s.FilePathValue(obj).should eql(obj)
+ end
+
+ it "calls #to_path to convert an object to a String" do
+ obj = mock("FilePathValue to_path")
+ obj.should_receive(:to_path).and_return("path")
+ @s.FilePathValue(obj).should == "path"
+ end
+
+ it "calls #to_str to convert an object to a String if #to_path isn't defined" do
+ obj = mock("FilePathValue to_str")
+ obj.should_receive(:to_str).and_return("path")
+ @s.FilePathValue(obj).should == "path"
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/fixnum_spec.rb b/spec/ruby/optional/capi/fixnum_spec.rb
new file mode 100644
index 0000000000..14258ba9c8
--- /dev/null
+++ b/spec/ruby/optional/capi/fixnum_spec.rb
@@ -0,0 +1,124 @@
+require_relative 'spec_helper'
+
+load_extension("fixnum")
+
+describe "CApiFixnumSpecs" do
+ before :each do
+ @s = CApiFixnumSpecs.new
+ end
+
+ describe "FIX2INT" do
+ it "converts a Fixnum to a native int" do
+ @s.FIX2INT(42).should == 42
+ @s.FIX2INT(-14).should == -14
+ end
+
+ max_int = (1 << 31) - 1
+ min_int = -(1 << 31)
+
+ guard -> { fixnum_min <= min_int and max_int <= fixnum_max } do
+ it "converts a Fixnum representing the minimum and maximum native int" do
+ @s.FIX2INT(max_int).should == max_int
+ @s.FIX2INT(min_int).should == min_int
+ end
+ end
+
+ end
+
+ describe "FIX2UINT" do
+ it "converts a Fixnum to a native int" do
+ @s.FIX2UINT(42).should == 42
+ @s.FIX2UINT(0).should == 0
+ end
+
+ max_uint = (1 << 32) - 1
+
+ guard -> { max_uint <= fixnum_max } do
+ it "converts a Fixnum representing the maximum native uint" do
+ @s.FIX2UINT(max_uint).should == max_uint
+ end
+ end
+
+ end
+
+ platform_is wordsize: 64 do
+ describe "rb_fix2uint" do
+ it "raises a TypeError if passed nil" do
+ lambda { @s.rb_fix2uint(nil) }.should raise_error(TypeError)
+ end
+
+ it "converts a Fixnum" do
+ @s.rb_fix2uint(0).should == 0
+ @s.rb_fix2uint(1).should == 1
+ end
+
+ it "converts the maximum uint value" do
+ @s.rb_fix2uint(0xffff_ffff).should == 0xffff_ffff
+ end
+
+ it "converts a Float" do
+ @s.rb_fix2uint(25.4567).should == 25
+ end
+
+ it "raises a RangeError if the value does not fit a native uint" do
+ # Interestingly, on MRI rb_fix2uint(-1) is allowed
+ lambda { @s.rb_fix2uint(0xffff_ffff+1) }.should raise_error(RangeError)
+ lambda { @s.rb_fix2uint(-(1 << 31) - 1) }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError if the value is more than 32bits" do
+ lambda { @s.rb_fix2uint(0xffff_ffff+1) }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError if the value is more than 64bits" do
+ lambda { @s.rb_fix2uint(0xffff_ffff_ffff_ffff+1) }.should raise_error(RangeError)
+ end
+ end
+
+ describe "rb_fix2int" do
+ it "raises a TypeError if passed nil" do
+ lambda { @s.rb_fix2int(nil) }.should raise_error(TypeError)
+ end
+
+ it "converts a Fixnum" do
+ @s.rb_fix2int(-1).should == -1
+ @s.rb_fix2int(1).should == 1
+ end
+
+ it "converts the minimum int value" do
+ @s.rb_fix2int(-(1 << 31)).should == -(1 << 31)
+ end
+
+ it "converts the maximum int value" do
+ @s.rb_fix2int(0x7fff_ffff).should == 0x7fff_ffff
+ end
+
+ it "converts a Float" do
+ @s.rb_fix2int(25.4567).should == 25
+ end
+
+ it "converts a negative Bignum into an signed number" do
+ @s.rb_fix2int(-2147442171).should == -2147442171
+ end
+
+ it "raises a RangeError if the value does not fit a native int" do
+ lambda { @s.rb_fix2int(0x7fff_ffff+1) }.should raise_error(RangeError)
+ lambda { @s.rb_fix2int(-(1 << 31) - 1) }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError if the value is more than 32bits" do
+ lambda { @s.rb_fix2int(0xffff_ffff+1) }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError if the value is more than 64bits" do
+ lambda { @s.rb_fix2int(0xffff_ffff_ffff_ffff+1) }.should raise_error(RangeError)
+ end
+
+ it "calls #to_int to coerce the value" do
+ obj = mock("number")
+ obj.should_receive(:to_int).and_return(2)
+ @s.rb_fix2int(obj).should == 2
+ end
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/fixtures/class.rb b/spec/ruby/optional/capi/fixtures/class.rb
new file mode 100644
index 0000000000..dbb0b69967
--- /dev/null
+++ b/spec/ruby/optional/capi/fixtures/class.rb
@@ -0,0 +1,91 @@
+class CApiClassSpecs
+ module M
+ def included?
+ true
+ end
+ end
+
+ class Alloc
+ attr_reader :initialized
+ attr_reader :arguments
+
+ def initialize(*args)
+ @initialized = true
+ @arguments = args
+ end
+ end
+
+ class Attr
+ def initialize
+ @foo, @bar, @baz = 1, 2, 3
+ end
+ end
+
+ class CVars
+ @@cvar = :cvar
+ @c_ivar = :c_ivar
+
+ def new_cv
+ @@new_cv if defined? @@new_cv
+ end
+
+ def new_cvar
+ @@new_cvar if defined? @@new_cvar
+ end
+
+ def rbdcv_cvar
+ @@rbdcv_cvar if defined? @@rbdcv_cvar
+ end
+ end
+
+ class Inherited
+ def self.inherited(klass)
+ klass
+ end
+ end
+
+ class NewClass
+ def self.inherited(klass)
+ raise "#{name}.inherited called"
+ end
+ end
+
+ class Super
+ def call_super_method
+ :super_method
+ end
+ end
+
+ class Sub < Super
+ def call_super_method
+ :subclass_method
+ end
+ end
+
+ class SubM < Super
+ include M
+ end
+
+ class SubSub < Sub
+ def call_super_method
+ :subsubclass_method
+ end
+ end
+
+ class SuperSelf
+ def call_super_method
+ self
+ end
+ end
+
+ class SubSelf < SuperSelf
+ end
+
+ class A
+ C = 1
+ autoload :D, File.expand_path('../path_to_class.rb', __FILE__)
+
+ class B
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/fixtures/const_get.rb b/spec/ruby/optional/capi/fixtures/const_get.rb
new file mode 100644
index 0000000000..b261a07f7e
--- /dev/null
+++ b/spec/ruby/optional/capi/fixtures/const_get.rb
@@ -0,0 +1,5 @@
+class CApiModuleSpecs
+ class A
+ D = 123
+ end
+end
diff --git a/spec/ruby/optional/capi/fixtures/const_get_at.rb b/spec/ruby/optional/capi/fixtures/const_get_at.rb
new file mode 100644
index 0000000000..700b570607
--- /dev/null
+++ b/spec/ruby/optional/capi/fixtures/const_get_at.rb
@@ -0,0 +1,5 @@
+class CApiModuleSpecs
+ class A
+ B = 123
+ end
+end
diff --git a/spec/ruby/optional/capi/fixtures/const_get_from.rb b/spec/ruby/optional/capi/fixtures/const_get_from.rb
new file mode 100644
index 0000000000..9e297daba7
--- /dev/null
+++ b/spec/ruby/optional/capi/fixtures/const_get_from.rb
@@ -0,0 +1,5 @@
+class CApiModuleSpecs
+ class A
+ C = 123
+ end
+end
diff --git a/spec/ruby/optional/capi/fixtures/const_get_object.rb b/spec/ruby/optional/capi/fixtures/const_get_object.rb
new file mode 100644
index 0000000000..03d9365a3e
--- /dev/null
+++ b/spec/ruby/optional/capi/fixtures/const_get_object.rb
@@ -0,0 +1,3 @@
+class Object
+ CApiModuleSpecsAutoload = 123
+end
diff --git a/spec/ruby/optional/capi/fixtures/encoding.rb b/spec/ruby/optional/capi/fixtures/encoding.rb
new file mode 100644
index 0000000000..0858807aa0
--- /dev/null
+++ b/spec/ruby/optional/capi/fixtures/encoding.rb
@@ -0,0 +1,3 @@
+class CApiEncodingSpecs
+ class S < String; end
+end
diff --git a/spec/ruby/optional/capi/fixtures/foo.rb b/spec/ruby/optional/capi/fixtures/foo.rb
new file mode 100644
index 0000000000..bc4e8f3f7d
--- /dev/null
+++ b/spec/ruby/optional/capi/fixtures/foo.rb
@@ -0,0 +1 @@
+$foo = 7
diff --git a/spec/ruby/optional/capi/fixtures/module.rb b/spec/ruby/optional/capi/fixtures/module.rb
new file mode 100644
index 0000000000..ba90eb7181
--- /dev/null
+++ b/spec/ruby/optional/capi/fixtures/module.rb
@@ -0,0 +1,35 @@
+class Object
+ autoload :CApiModuleSpecsAutoload, File.expand_path('../const_get_object.rb', __FILE__)
+
+ module CApiModuleSpecsModuleA
+ X = 1
+ end
+end
+
+class CApiModuleSpecs
+ class A
+ autoload :B, File.expand_path('../const_get_at.rb', __FILE__)
+ autoload :C, File.expand_path('../const_get_from.rb', __FILE__)
+ autoload :D, File.expand_path('../const_get.rb', __FILE__)
+
+ X = 1
+ end
+
+ class B < A
+ Y = 2
+ end
+
+ class C
+ Z = 3
+ end
+
+ module M
+ end
+
+ class Super
+ end
+
+ autoload :ModuleUnderAutoload, "#{object_path}/module_under_autoload_spec"
+ autoload :RubyUnderAutoload, File.expand_path('../module_autoload', __FILE__)
+
+end
diff --git a/spec/ruby/optional/capi/fixtures/module_autoload.rb b/spec/ruby/optional/capi/fixtures/module_autoload.rb
new file mode 100644
index 0000000000..8130a24421
--- /dev/null
+++ b/spec/ruby/optional/capi/fixtures/module_autoload.rb
@@ -0,0 +1,4 @@
+class CApiModuleSpecs
+ module RubyUnderAutoload
+ end
+end
diff --git a/spec/ruby/optional/capi/fixtures/path_to_class.rb b/spec/ruby/optional/capi/fixtures/path_to_class.rb
new file mode 100644
index 0000000000..acd577ff24
--- /dev/null
+++ b/spec/ruby/optional/capi/fixtures/path_to_class.rb
@@ -0,0 +1,6 @@
+class CApiClassSpecs
+ class A
+ module D
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/fixtures/proc.rb b/spec/ruby/optional/capi/fixtures/proc.rb
new file mode 100644
index 0000000000..fbe37312da
--- /dev/null
+++ b/spec/ruby/optional/capi/fixtures/proc.rb
@@ -0,0 +1,20 @@
+class CApiProcSpecs
+ def call_nothing
+ end
+
+ def call_Proc_new
+ Proc.new
+ end
+
+ def call_block_given?
+ block_given?
+ end
+
+ def call_rb_Proc_new
+ rb_Proc_new(0)
+ end
+
+ def call_rb_Proc_new_with_block
+ rb_Proc_new(0) { :calling_with_block }
+ end
+end
diff --git a/spec/ruby/optional/capi/float_spec.rb b/spec/ruby/optional/capi/float_spec.rb
new file mode 100644
index 0000000000..8381945315
--- /dev/null
+++ b/spec/ruby/optional/capi/float_spec.rb
@@ -0,0 +1,30 @@
+require_relative 'spec_helper'
+
+load_extension("float")
+
+describe "CApiFloatSpecs" do
+ before :each do
+ @f = CApiFloatSpecs.new
+ end
+
+ describe "rb_float_new" do
+ it "creates a new float" do
+ ((@f.new_zero - 0).abs < 0.000001).should == true
+ ((@f.new_point_five - 0.555).abs < 0.000001).should == true
+ end
+ end
+
+ describe "RFLOAT_VALUE" do
+ it "returns the C double value of the Float" do
+ @f.RFLOAT_VALUE(2.3).should == 2.3
+ end
+ end
+
+ describe "rb_Float" do
+ it "creates a new Float from a String" do
+ f = @f.rb_Float("101.99")
+ f.should be_kind_of(Float)
+ f.should eql(101.99)
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/gc_spec.rb b/spec/ruby/optional/capi/gc_spec.rb
new file mode 100644
index 0000000000..1c34675f0d
--- /dev/null
+++ b/spec/ruby/optional/capi/gc_spec.rb
@@ -0,0 +1,54 @@
+require_relative 'spec_helper'
+
+load_extension("gc")
+
+describe "CApiGCSpecs" do
+ before :each 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)
+ end
+
+ describe "rb_gc_enable" do
+
+ after do
+ GC.enable
+ end
+
+ it "enables GC when disabled" do
+ GC.disable
+ @f.rb_gc_enable.should be_true
+ end
+
+ it "GC stays enabled when enabled" do
+ GC.enable
+ @f.rb_gc_enable.should be_false
+ end
+
+ it "disables GC when enabled" do
+ GC.enable
+ @f.rb_gc_disable.should be_false
+ end
+
+ it "GC stays disabled when disabled" do
+ GC.disable
+ @f.rb_gc_disable.should be_true
+ end
+ end
+
+ describe "rb_gc" do
+
+ it "increases gc count" do
+ gc_count = GC.count
+ @f.rb_gc
+ GC.count.should > gc_count
+ end
+
+ end
+
+end
diff --git a/spec/ruby/optional/capi/globals_spec.rb b/spec/ruby/optional/capi/globals_spec.rb
new file mode 100644
index 0000000000..068b372230
--- /dev/null
+++ b/spec/ruby/optional/capi/globals_spec.rb
@@ -0,0 +1,224 @@
+require_relative 'spec_helper'
+require "stringio"
+
+load_extension("globals")
+
+describe "CApiGlobalSpecs" do
+ before :each do
+ @f = CApiGlobalSpecs.new
+ end
+
+ it "correctly gets global values" do
+ @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
+
+ it "returns $~" do
+ 'a' =~ /a/
+ @f.sb_gv_get("$~").to_a.should == ['a']
+ @f.sb_gv_get("~").to_a.should == ['a']
+ end
+
+ it "correctly sets global values" do
+ @f.sb_gv_get("$BLAH").should == nil
+ @f.sb_gv_set("$BLAH", 10)
+ begin
+ @f.sb_gv_get("$BLAH").should == 10
+ ensure
+ @f.sb_gv_set("$BLAH", nil)
+ end
+ end
+
+ it "lists all global variables" do
+ @f.rb_f_global_variables.should == Kernel.global_variables
+ end
+
+ it "rb_define_variable should define a new global variable" do
+ @f.rb_define_variable("my_gvar", "ABC")
+ $my_gvar.should == "ABC"
+ $my_gvar = "XYZ"
+ @f.sb_get_global_value.should == "XYZ"
+ end
+
+ it "rb_define_readonly_variable should define a new readonly global variable" do
+ @f.rb_define_readonly_variable("ro_gvar", 15)
+ $ro_gvar.should == 15
+ lambda { $ro_gvar = 10 }.should raise_error(NameError)
+ end
+
+ it "rb_define_hooked_variable should define a C hooked global variable" do
+ @f.rb_define_hooked_variable_2x("$hooked_gvar")
+ $hooked_gvar = 2
+ $hooked_gvar.should == 4
+ end
+
+ describe "rb_rs" do
+ before :each do
+ @dollar_slash = $/
+ end
+
+ after :each do
+ $/ = @dollar_slash
+ end
+
+ it "returns \\n by default" do
+ @f.rb_rs.should == "\n"
+ end
+
+ it "returns the value of $/" do
+ $/ = "foo"
+ @f.rb_rs.should == "foo"
+ end
+ end
+
+ context "rb_std streams" do
+ before :each do
+ @name = tmp("rb_std_streams")
+ @stream = new_io @name
+ end
+
+ after :each do
+ @stream.close
+ rm_r @name
+ end
+
+ describe "rb_stdin" do
+ after :each do
+ $stdin = STDIN
+ end
+
+ it "returns $stdin" do
+ $stdin = @stream
+ @f.rb_stdin.should equal($stdin)
+ end
+ end
+
+ describe "rb_stdout" do
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "returns $stdout" do
+ $stdout = @stream
+ @f.rb_stdout.should equal($stdout)
+ end
+ end
+
+ describe "rb_stderr" do
+ after :each do
+ $stderr = STDERR
+ end
+
+ it "returns $stderr" do
+ $stderr = @stream
+ @f.rb_stderr.should equal($stderr)
+ end
+ end
+
+ describe "rb_defout" do
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "returns $stdout" do
+ $stdout = @stream
+ @f.rb_defout.should equal($stdout)
+ end
+ end
+ end
+
+ describe "rb_default_rs" do
+ it "returns \\n" do
+ @f.rb_default_rs.should == "\n"
+ end
+ end
+
+ describe "rb_output_rs" do
+ before :each do
+ @dollar_backslash = $\
+ end
+
+ after :each do
+ $\ = @dollar_backslash
+ end
+
+ it "returns nil by default" do
+ @f.rb_output_rs.should be_nil
+ end
+
+ it "returns the value of $\\" do
+ $\ = "foo"
+ @f.rb_output_rs.should == "foo"
+ end
+ end
+
+ describe "rb_output_fs" do
+ before :each do
+ @dollar_comma = $,
+ end
+
+ after :each do
+ $, = @dollar_comma
+ end
+
+ it "returns nil by default" do
+ @f.rb_output_fs.should be_nil
+ end
+
+ it "returns the value of $\\" do
+ $, = "foo"
+ @f.rb_output_fs.should == "foo"
+ end
+ end
+
+ describe "rb_lastline_set" do
+ it "sets the value of $_" do
+ @f.rb_lastline_set("last line")
+ $_.should == "last line"
+ end
+
+ it "sets a Thread-local value" do
+ $_ = nil
+ running = false
+
+ thr = Thread.new do
+ @f.rb_lastline_set("last line")
+ $_.should == "last line"
+ running = true
+ end
+
+ Thread.pass while thr.status and !running
+ $_.should be_nil
+
+ thr.join
+ end
+ end
+
+ describe "rb_lastline_get" do
+ before do
+ @io = StringIO.new("last line")
+ end
+
+ it "gets the value of $_" do
+ @io.gets
+ @f.rb_lastline_get.should == "last line"
+ end
+
+ it "gets a Thread-local value" do
+ $_ = nil
+ running = false
+
+ thr = Thread.new do
+ @io.gets
+ @f.rb_lastline_get.should == "last line"
+ running = true
+ end
+
+ Thread.pass while thr.status and !running
+ $_.should be_nil
+
+ thr.join
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/hash_spec.rb b/spec/ruby/optional/capi/hash_spec.rb
new file mode 100644
index 0000000000..f584e73006
--- /dev/null
+++ b/spec/ruby/optional/capi/hash_spec.rb
@@ -0,0 +1,252 @@
+require_relative 'spec_helper'
+require_relative '../../shared/hash/key_error'
+
+load_extension("hash")
+
+describe "C-API Hash function" do
+ before :each do
+ @s = CApiHashSpecs.new
+ end
+
+ describe "rb_hash" do
+ it "calls #hash on the object" do
+ obj = mock("rb_hash")
+ obj.should_receive(:hash).and_return(5)
+ @s.rb_hash(obj).should == 5
+ end
+
+ it "converts a Bignum returned by #hash to a Fixnum" do
+ obj = mock("rb_hash bignum")
+ obj.should_receive(:hash).and_return(bignum_value)
+
+ # The actual conversion is an implementation detail.
+ # We only care that ultimately we get a Fixnum instance.
+ @s.rb_hash(obj).should be_an_instance_of(Fixnum)
+ end
+
+ it "calls #to_int to converts a value returned by #hash to a Fixnum" do
+ obj = mock("rb_hash to_int")
+ obj.should_receive(:hash).and_return(obj)
+ obj.should_receive(:to_int).and_return(12)
+
+ @s.rb_hash(obj).should == 12
+ end
+
+ it "raises a TypeError if the object does not implement #to_int" do
+ obj = mock("rb_hash no to_int")
+ obj.should_receive(:hash).and_return(nil)
+
+ lambda { @s.rb_hash(obj) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "rb_hash_new" do
+ it "returns a new hash" do
+ @s.rb_hash_new.should == {}
+ end
+
+ it "creates a hash with no default proc" do
+ @s.rb_hash_new {}.default_proc.should be_nil
+ end
+ end
+
+ describe "rb_hash_dup" do
+ it "returns a copy of the hash" do
+ hsh = {}
+ dup = @s.rb_hash_dup(hsh)
+ dup.should == hsh
+ dup.should_not equal(hsh)
+ end
+ end
+
+ describe "rb_hash_freeze" do
+ it "freezes the hash" do
+ @s.rb_hash_freeze({}).frozen?.should be_true
+ end
+ end
+
+ describe "rb_hash_aref" do
+ it "returns the value associated with the key" do
+ hsh = {chunky: 'bacon'}
+ @s.rb_hash_aref(hsh, :chunky).should == 'bacon'
+ end
+
+ it "returns the default value if it exists" do
+ hsh = Hash.new(0)
+ @s.rb_hash_aref(hsh, :chunky).should == 0
+ @s.rb_hash_aref_nil(hsh, :chunky).should be_false
+ end
+
+ it "returns nil if the key does not exist" do
+ hsh = { }
+ @s.rb_hash_aref(hsh, :chunky).should be_nil
+ @s.rb_hash_aref_nil(hsh, :chunky).should be_true
+ end
+ end
+
+ describe "rb_hash_aset" do
+ it "adds the key/value pair and returns the value" do
+ hsh = {}
+ @s.rb_hash_aset(hsh, :chunky, 'bacon').should == 'bacon'
+ hsh.should == {chunky: 'bacon'}
+ end
+ end
+
+ describe "rb_hash_clear" do
+ it "returns self that cleared keys and values" do
+ hsh = { :key => 'value' }
+ @s.rb_hash_clear(hsh).should equal(hsh)
+ hsh.should == {}
+ end
+ end
+
+ describe "rb_hash_delete" do
+ it "removes the key and returns the value" do
+ hsh = {chunky: 'bacon'}
+ @s.rb_hash_delete(hsh, :chunky).should == 'bacon'
+ hsh.should == {}
+ end
+ end
+
+ describe "rb_hash_delete_if" do
+ it "removes an entry if the block returns true" do
+ h = { a: 1, b: 2, c: 3 }
+ @s.rb_hash_delete_if(h) { |k, v| v == 2 }
+ h.should == { a: 1, c: 3 }
+ end
+
+ it "returns an Enumerator when no block is passed" do
+ @s.rb_hash_delete_if({a: 1}).should be_an_instance_of(Enumerator)
+ end
+ end
+
+ describe "rb_hash_fetch" do
+ before :each do
+ @hsh = {:a => 1, :b => 2}
+ end
+
+ it "returns the value associated with the key" do
+ @s.rb_hash_fetch(@hsh, :b).should == 2
+ end
+
+ it "raises a KeyError if the key is not found and default is set" do
+ @hsh.default = :d
+ lambda { @s.rb_hash_fetch(@hsh, :c) }.should raise_error(KeyError)
+ end
+
+ it "raises a KeyError if the key is not found and no default is set" do
+ lambda { @s.rb_hash_fetch(@hsh, :c) }.should raise_error(KeyError)
+ end
+
+ context "when key is not found" do
+ it_behaves_like :key_error, -> (obj, key) {
+ @s.rb_hash_fetch(obj, key)
+ }, { a: 1 }
+ end
+ end
+
+ describe "rb_hash_foreach" do
+ it "iterates over the hash" do
+ hsh = {name: "Evan", sign: :libra}
+
+ out = @s.rb_hash_foreach(hsh)
+ out.equal?(hsh).should == false
+ out.should == hsh
+ end
+
+ it "stops via the callback" do
+ hsh = {name: "Evan", sign: :libra}
+
+ out = @s.rb_hash_foreach_stop(hsh)
+ out.size.should == 1
+ end
+
+ it "deletes via the callback" do
+ hsh = {name: "Evan", sign: :libra}
+
+ out = @s.rb_hash_foreach_delete(hsh)
+ out.should == {name: "Evan", sign: :libra}
+ hsh.should == {}
+ end
+ end
+
+ describe "rb_hash_size" do
+ it "returns the size of the hash" do
+ hsh = {fast: 'car', good: 'music'}
+ @s.rb_hash_size(hsh).should == 2
+ end
+
+ it "returns zero for an empty hash" do
+ @s.rb_hash_size({}).should == 0
+ end
+ end
+
+ describe "rb_hash_lookup" do
+ it "returns the value associated with the key" do
+ hsh = {chunky: 'bacon'}
+ @s.rb_hash_lookup(hsh, :chunky).should == 'bacon'
+ end
+
+ it "does not return the default value if it exists" do
+ hsh = Hash.new(0)
+ @s.rb_hash_lookup(hsh, :chunky).should be_nil
+ @s.rb_hash_lookup_nil(hsh, :chunky).should be_true
+ end
+
+ it "returns nil if the key does not exist" do
+ hsh = { }
+ @s.rb_hash_lookup(hsh, :chunky).should be_nil
+ @s.rb_hash_lookup_nil(hsh, :chunky).should be_true
+ end
+
+ describe "rb_hash_lookup2" do
+ it "returns the value associated with the key" do
+ hash = {chunky: 'bacon'}
+
+ @s.rb_hash_lookup2(hash, :chunky, nil).should == 'bacon'
+ end
+
+ it "returns the default value if the key does not exist" do
+ hash = {}
+
+ @s.rb_hash_lookup2(hash, :chunky, 10).should == 10
+ end
+ end
+ end
+
+ describe "rb_hash_set_ifnone" do
+ it "sets the default value of non existing keys" do
+ hash = {}
+
+ @s.rb_hash_set_ifnone(hash, 10)
+
+ hash[:chunky].should == 10
+ end
+ end
+
+ describe "rb_Hash" do
+ it "returns an empty hash when the argument is nil" do
+ @s.rb_Hash(nil).should == {}
+ end
+
+ it "returns an empty hash when the argument is []" do
+ @s.rb_Hash([]).should == {}
+ end
+
+ it "tries to convert the passed argument to a hash by calling #to_hash" do
+ h = BasicObject.new
+ def h.to_hash; {"bar" => "foo"}; end
+ @s.rb_Hash(h).should == {"bar" => "foo"}
+ end
+
+ it "raises a TypeError if the argument does not respond to #to_hash" do
+ lambda { @s.rb_Hash(42) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if #to_hash does not return a hash" do
+ h = BasicObject.new
+ def h.to_hash; 42; end
+ lambda { @s.rb_Hash(h) }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/integer_spec.rb b/spec/ruby/optional/capi/integer_spec.rb
new file mode 100644
index 0000000000..56f7ca3034
--- /dev/null
+++ b/spec/ruby/optional/capi/integer_spec.rb
@@ -0,0 +1,275 @@
+# -*- encoding: binary -*-
+require_relative 'spec_helper'
+
+load_extension("integer")
+
+describe "CApiIntegerSpecs" do
+ before :each do
+ @s = CApiIntegerSpecs.new
+ end
+
+ describe "rb_integer_pack" do
+ it "converts zero" do
+ words = "\000" * 9
+ result = @s.rb_integer_pack(0, words, 1, 9, 0,
+ CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 0
+ words.should == "\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ end
+
+ describe "without two's complement flag" do
+ before :each do
+ @value = 0x9876_abcd_4532_ef01_0123_4567_89ab_cdef
+ @words = "\000" * 16
+ end
+
+ describe "with big endian output" do
+ it "converts a positive number" do
+ result = @s.rb_integer_pack(@value, @words, 2, 8, 0,
+ CApiIntegerSpecs::BIG_ENDIAN)
+ result.should == 1
+ @words.should == "\x98\x76\xAB\xCD\x45\x32\xEF\x01\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+ end
+
+ it "converts a negative number" do
+ result = @s.rb_integer_pack(-@value, @words, 2, 8, 0,
+ CApiIntegerSpecs::BIG_ENDIAN)
+ result.should == -1
+ @words.should == "\x98\x76\xAB\xCD\x45\x32\xEF\x01\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+ end
+
+ it "converts a negative number exactly -2**(numwords*wordsize*8)" do
+ result = @s.rb_integer_pack(-2**(2*8*8), @words, 2, 8, 0,
+ CApiIntegerSpecs::BIG_ENDIAN)
+ result.should == -2
+ @words.should == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ end
+ end
+
+ describe "with little endian output" do
+ it "converts a positive number" do
+ result = @s.rb_integer_pack(@value, @words, 2, 8, 0,
+ CApiIntegerSpecs::LITTLE_ENDIAN)
+ result.should == 1
+ @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x01\x01\xEF\x32\x45\xCD\xAB\x76\x98"
+ end
+
+ it "converts a negative number" do
+ result = @s.rb_integer_pack(-@value, @words, 2, 8, 0,
+ CApiIntegerSpecs::LITTLE_ENDIAN)
+ result.should == -1
+ @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x01\x01\xEF\x32\x45\xCD\xAB\x76\x98"
+ end
+
+ it "converts a negative number exactly -2**(numwords*wordsize*8)" do
+ result = @s.rb_integer_pack(-2**(2*8*8), @words, 2, 8, 0,
+ CApiIntegerSpecs::LITTLE_ENDIAN)
+ result.should == -2
+ @words.should == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ end
+ end
+ end
+
+ describe "with two's complement flag" do
+ describe "with input less than 64 bits" do
+ before :each do
+ @value = 0x0123_4567_89ab_cdef
+ @words = "\000" * 8
+ end
+
+ describe "with big endian output" do
+ it "converts a positive number" do
+ result = @s.rb_integer_pack(@value, @words, 1, 8, 0,
+ CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 1
+ @words.should == "\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+ end
+
+ it "converts a negative number" do
+ result = @s.rb_integer_pack(-@value, @words, 1, 8, 0,
+ CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP)
+ result.should == -1
+ @words.should == "\xFE\xDC\xBA\x98\x76\x54\x32\x11"
+ end
+ end
+
+ describe "with little endian output" do
+ it "converts a positive number" do
+ result = @s.rb_integer_pack(@value, @words, 1, 8, 0,
+ CApiIntegerSpecs::LITTLE_ENDIAN|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 1
+ @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x01"
+ end
+
+ it "converts a negative number" do
+ result = @s.rb_integer_pack(-@value, @words, 1, 8, 0,
+ CApiIntegerSpecs::LITTLE_ENDIAN|CApiIntegerSpecs::PACK_2COMP)
+ result.should == -1
+ @words.should == "\x11\x32\x54\x76\x98\xBA\xDC\xFE"
+ end
+ end
+
+ describe "with native endian output" do
+ big_endian do
+ it "converts a positive number" do
+ result = @s.rb_integer_pack(@value, @words, 1, 8, 0,
+ CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 1
+ @words.should == "\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+ end
+
+ it "converts a negative number" do
+ result = @s.rb_integer_pack(-@value, @words, 1, 8, 0,
+ CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP)
+ result.should == -1
+ @words.should == "\xFE\xDC\xBA\x98\x76\x54\x32\x11"
+ end
+ end
+
+ little_endian do
+ it "converts a positive number" do
+ result = @s.rb_integer_pack(@value, @words, 1, 8, 0,
+ CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 1
+ @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x01"
+ end
+
+ it "converts a negative number" do
+ result = @s.rb_integer_pack(-@value, @words, 1, 8, 0,
+ CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP)
+ result.should == -1
+ @words.should == "\x11\x32\x54\x76\x98\xBA\xDC\xFE"
+ end
+ end
+ end
+ end
+
+ describe "with input greater than 64 bits" do
+ before :each do
+ @value = 0x9876_abcd_4532_ef01_0123_4567_89ab_cdef
+ @words = "\000" * 16
+ end
+
+ describe "with big endian output" do
+ it "converts a positive number" do
+ result = @s.rb_integer_pack(@value, @words, 2, 8, 0,
+ CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 1
+ @words.should == "\x98\x76\xAB\xCD\x45\x32\xEF\x01\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+ end
+
+ it "converts a negative number" do
+ result = @s.rb_integer_pack(-@value, @words, 2, 8, 0,
+ CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP)
+ result.should == -1
+ @words.should == "\x67\x89\x54\x32\xBA\xCD\x10\xFE\xFE\xDC\xBA\x98\x76\x54\x32\x11"
+ end
+
+ describe "with overflow" do
+ before :each do
+ @words = "\000" * 9
+ end
+
+ it "converts a positive number" do
+ result = @s.rb_integer_pack(@value, @words, 1, 9, 0,
+ CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 2
+ @words.should == "\x01\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+ end
+
+ it "converts a negative number" do
+ result = @s.rb_integer_pack(-@value, @words, 1, 9, 0,
+ CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP)
+ result.should == -2
+ @words.should == "\xFE\xFE\xDC\xBA\x98\x76\x54\x32\x11"
+ end
+
+ it "converts a negative number exactly -2**(numwords*wordsize*8)" do
+ result = @s.rb_integer_pack(-2**(9*8), @words, 1, 9, 0,
+ CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP)
+ result.should == -1
+ @words.should == "\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ end
+ end
+ end
+
+ describe "with little endian output" do
+ it "converts a positive number" do
+ result = @s.rb_integer_pack(@value, @words, 2, 8, 0,
+ CApiIntegerSpecs::LITTLE_ENDIAN|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 1
+ @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x01\x01\xEF\x32\x45\xCD\xAB\x76\x98"
+ end
+
+ it "converts a negative number" do
+ result = @s.rb_integer_pack(-@value, @words, 2, 8, 0,
+ CApiIntegerSpecs::LITTLE_ENDIAN|CApiIntegerSpecs::PACK_2COMP)
+ result.should == -1
+ @words.should == "\x11\x32\x54\x76\x98\xBA\xDC\xFE\xFE\x10\xCD\xBA\x32\x54\x89\x67"
+ end
+
+ describe "with overflow" do
+ before :each do
+ @words = "\000" * 9
+ end
+
+ it "converts a positive number" do
+ result = @s.rb_integer_pack(@value, @words, 1, 9, 0,
+ CApiIntegerSpecs::LITTLE_ENDIAN|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 2
+ @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x01\x01"
+ end
+
+ it "converts a negative number" do
+ result = @s.rb_integer_pack(-@value, @words, 1, 9, 0,
+ CApiIntegerSpecs::LITTLE_ENDIAN|CApiIntegerSpecs::PACK_2COMP)
+ result.should == -2
+ @words.should == "\x11\x32\x54\x76\x98\xBA\xDC\xFE\xFE"
+ end
+
+ it "converts a negative number exactly -2**(numwords*wordsize*8)" do
+ result = @s.rb_integer_pack(-2**(9*8), @words, 1, 9, 0,
+ CApiIntegerSpecs::LITTLE_ENDIAN|CApiIntegerSpecs::PACK_2COMP)
+ result.should == -1
+ @words.should == "\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ end
+ end
+ end
+
+ describe "with native endian output" do
+ big_endian do
+ it "converts a positive number" do
+ result = @s.rb_integer_pack(@value, @words, 1, 16, 0,
+ CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 1
+ @words.should == "\x98\x76\xAB\xCD\x45\x32\xEF\x01\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+ end
+
+ it "converts a negative number" do
+ result = @s.rb_integer_pack(-@value, @words, 1, 16, 0,
+ CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP)
+ result.should == -1
+ @words.should == "\x67\x89\x54\x32\xBA\xCD\x10\xFE\xFE\xDC\xBA\x98\x76\x54\x32\x11"
+ end
+ end
+
+ little_endian do
+ it "converts a positive number" do
+ result = @s.rb_integer_pack(@value, @words, 1, 16, 0,
+ CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 1
+ @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x01\x01\xEF\x32\x45\xCD\xAB\x76\x98"
+ end
+
+ it "converts a negative number" do
+ result = @s.rb_integer_pack(-@value, @words, 1, 16, 0,
+ CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP)
+ result.should == -1
+ @words.should == "\x11\x32\x54\x76\x98\xBA\xDC\xFE\xFE\x10\xCD\xBA\x32\x54\x89\x67"
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/io_spec.rb b/spec/ruby/optional/capi/io_spec.rb
new file mode 100644
index 0000000000..a832c6a93b
--- /dev/null
+++ b/spec/ruby/optional/capi/io_spec.rb
@@ -0,0 +1,347 @@
+require_relative 'spec_helper'
+
+load_extension('io')
+
+describe "C-API IO function" do
+ before :each do
+ @o = CApiIOSpecs.new
+
+ @name = tmp("c_api_rb_io_specs")
+ touch @name
+
+ @io = new_io @name, fmode("w:utf-8")
+ @io.sync = true
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @name
+ end
+
+ describe "rb_io_addstr" do
+ it "calls #to_s to convert the object to a String" do
+ obj = mock("rb_io_addstr string")
+ obj.should_receive(:to_s).and_return("rb_io_addstr data")
+
+ @o.rb_io_addstr(@io, obj)
+ File.read(@name).should == "rb_io_addstr data"
+ end
+
+ it "writes the String to the IO" do
+ @o.rb_io_addstr(@io, "rb_io_addstr data")
+ File.read(@name).should == "rb_io_addstr data"
+ end
+
+ it "returns the io" do
+ @o.rb_io_addstr(@io, "rb_io_addstr data").should eql(@io)
+ end
+ end
+
+ describe "rb_io_printf" do
+ it "calls #to_str to convert the format object to a String" do
+ obj = mock("rb_io_printf format")
+ obj.should_receive(:to_str).and_return("%s")
+
+ @o.rb_io_printf(@io, [obj, "rb_io_printf"])
+ File.read(@name).should == "rb_io_printf"
+ end
+
+ it "calls #to_s to convert the object to a String" do
+ obj = mock("rb_io_printf string")
+ obj.should_receive(:to_s).and_return("rb_io_printf")
+
+ @o.rb_io_printf(@io, ["%s", obj])
+ File.read(@name).should == "rb_io_printf"
+ end
+
+ it "writes the Strings to the IO" do
+ @o.rb_io_printf(@io, ["%s_%s_%s", "rb", "io", "printf"])
+ File.read(@name).should == "rb_io_printf"
+ end
+ end
+
+ describe "rb_io_print" do
+ it "calls #to_s to convert the object to a String" do
+ obj = mock("rb_io_print string")
+ obj.should_receive(:to_s).and_return("rb_io_print")
+
+ @o.rb_io_print(@io, [obj])
+ File.read(@name).should == "rb_io_print"
+ end
+
+ it "writes the Strings to the IO with no separator" do
+ @o.rb_io_print(@io, ["rb_", "io_", "print"])
+ File.read(@name).should == "rb_io_print"
+ end
+ end
+
+ describe "rb_io_puts" do
+ it "calls #to_s to convert the object to a String" do
+ obj = mock("rb_io_puts string")
+ obj.should_receive(:to_s).and_return("rb_io_puts")
+
+ @o.rb_io_puts(@io, [obj])
+ File.read(@name).should == "rb_io_puts\n"
+ end
+
+ it "writes the Strings to the IO separated by newlines" do
+ @o.rb_io_puts(@io, ["rb", "io", "write"])
+ File.read(@name).should == "rb\nio\nwrite\n"
+ end
+ end
+
+ describe "rb_io_write" do
+ it "calls #to_s to convert the object to a String" do
+ obj = mock("rb_io_write string")
+ obj.should_receive(:to_s).and_return("rb_io_write")
+
+ @o.rb_io_write(@io, obj)
+ File.read(@name).should == "rb_io_write"
+ end
+
+ it "writes the String to the IO" do
+ @o.rb_io_write(@io, "rb_io_write")
+ File.read(@name).should == "rb_io_write"
+ end
+ end
+end
+
+describe "C-API IO function" do
+ before :each do
+ @o = CApiIOSpecs.new
+
+ @name = tmp("c_api_io_specs")
+ touch @name
+
+ @io = new_io @name, fmode("r:utf-8")
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @name
+ end
+
+ describe "rb_io_close" do
+ it "closes an IO object" do
+ @io.closed?.should be_false
+ @o.rb_io_close(@io)
+ @io.closed?.should be_true
+ end
+ end
+
+ describe "rb_io_check_io" do
+ it "returns the IO object if it is valid" do
+ @o.rb_io_check_io(@io).should == @io
+ end
+
+ it "returns nil for non IO objects" do
+ @o.rb_io_check_io({}).should be_nil
+ end
+ end
+
+ describe "rb_io_check_closed" do
+ it "does not raise an exception if the IO is not closed" do
+ # The MRI function is void, so we use should_not raise_error
+ lambda { @o.rb_io_check_closed(@io) }.should_not raise_error
+ end
+
+ it "raises an error if the IO is closed" do
+ @io.close
+ lambda { @o.rb_io_check_closed(@io) }.should raise_error(IOError)
+ end
+ end
+
+ # NOTE: unlike the name might suggest in MRI this function checks if an
+ # object is frozen, *not* if it's tainted.
+ describe "rb_io_taint_check" do
+ it "does not raise an exception if the IO is not frozen" do
+ lambda { @o.rb_io_taint_check(@io) }.should_not raise_error
+ end
+
+ it "raises an exception if the IO is frozen" do
+ @io.freeze
+
+ lambda { @o.rb_io_taint_check(@io) }.should raise_error(RuntimeError)
+ end
+ end
+
+ describe "GetOpenFile" do
+ it "allows access to the system fileno" do
+ @o.GetOpenFile_fd($stdin).should == 0
+ @o.GetOpenFile_fd($stdout).should == 1
+ @o.GetOpenFile_fd($stderr).should == 2
+ @o.GetOpenFile_fd(@io).should == @io.fileno
+ end
+ end
+
+ describe "rb_io_binmode" do
+ it "returns self" do
+ @o.rb_io_binmode(@io).should == @io
+ end
+
+ it "sets binmode" do
+ @o.rb_io_binmode(@io)
+ @io.binmode?.should be_true
+ end
+ end
+end
+
+describe "C-API IO function" do
+ before :each do
+ @o = CApiIOSpecs.new
+ @r_io, @w_io = IO.pipe
+
+ @name = tmp("c_api_io_specs")
+ touch @name
+ @rw_io = new_io @name, fmode("w+")
+ end
+
+ after :each do
+ @r_io.close unless @r_io.closed?
+ @w_io.close unless @w_io.closed?
+ @rw_io.close unless @rw_io.closed?
+ rm_r @name
+ end
+
+ describe "rb_io_check_readable" do
+ it "does not raise an exception if the IO is opened for reading" do
+ # The MRI function is void, so we use should_not raise_error
+ lambda { @o.rb_io_check_readable(@r_io) }.should_not raise_error
+ end
+
+ it "does not raise an exception if the IO is opened for read and write" do
+ lambda { @o.rb_io_check_readable(@rw_io) }.should_not raise_error
+ end
+
+ it "raises an IOError if the IO is not opened for reading" do
+ lambda { @o.rb_io_check_readable(@w_io) }.should raise_error(IOError)
+ end
+
+ end
+
+ describe "rb_io_check_writable" do
+ it "does not raise an exeption if the IO is opened for writing" do
+ # The MRI function is void, so we use should_not raise_error
+ lambda { @o.rb_io_check_writable(@w_io) }.should_not raise_error
+ end
+
+ it "does not raise an exception if the IO is opened for read and write" do
+ lambda { @o.rb_io_check_writable(@rw_io) }.should_not raise_error
+ end
+
+ it "raises an IOError if the IO is not opened for reading" do
+ lambda { @o.rb_io_check_writable(@r_io) }.should raise_error(IOError)
+ end
+ end
+
+ describe "rb_io_wait_writable" do
+ it "returns false if there is no error condition" do
+ @o.errno = 0
+ @o.rb_io_wait_writable(@w_io).should be_false
+ end
+
+ it "raises an IOError if the IO is closed" do
+ @w_io.close
+ lambda { @o.rb_io_wait_writable(@w_io) }.should raise_error(IOError)
+ end
+ end
+
+ describe "rb_thread_fd_writable" do
+ it "waits til an fd is ready for writing" do
+ @o.rb_thread_fd_writable(@w_io).should be_nil
+ end
+ end
+
+ platform_is_not :windows do
+ describe "rb_io_wait_readable" do
+ it "returns false if there is no error condition" do
+ @o.errno = 0
+ @o.rb_io_wait_readable(@r_io, false).should be_false
+ end
+
+ it "raises and IOError if passed a closed stream" do
+ @r_io.close
+ lambda {
+ @o.rb_io_wait_readable(@r_io, false)
+ }.should raise_error(IOError)
+ end
+
+ it "blocks until the io is readable and returns true" do
+ @o.instance_variable_set :@write_data, false
+ thr = Thread.new do
+ Thread.pass until @o.instance_variable_get(:@write_data)
+ @w_io.write "rb_io_wait_readable"
+ end
+
+ @o.errno = Errno::EAGAIN.new.errno
+ @o.rb_io_wait_readable(@r_io, true).should be_true
+ @o.instance_variable_get(:@read_data).should == "rb_io_wait_re"
+
+ thr.join
+ end
+ end
+ end
+
+ describe "rb_thread_wait_fd" do
+ it "waits til an fd is ready for reading" do
+ start = false
+ thr = Thread.new do
+ start = true
+ sleep 0.05
+ @w_io.write "rb_io_wait_readable"
+ end
+
+ Thread.pass until start
+
+ @o.rb_thread_wait_fd(@r_io).should be_nil
+
+ thr.join
+ end
+ end
+
+end
+
+describe "rb_fd_fix_cloexec" do
+
+ before :each do
+ @o = CApiIOSpecs.new
+
+ @name = tmp("c_api_rb_io_specs")
+ touch @name
+
+ @io = new_io @name, fmode("w:utf-8")
+ @io.close_on_exec = false
+ @io.sync = true
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @name
+ end
+
+ it "sets close_on_exec on the IO" do
+ @o.rb_fd_fix_cloexec(@io)
+ @io.close_on_exec?.should be_true
+ end
+
+end
+
+describe "rb_cloexec_open" do
+ before :each do
+ @o = CApiIOSpecs.new
+ @name = tmp("c_api_rb_io_specs")
+ touch @name
+
+ @io = nil
+ end
+
+ after :each do
+ @io.close unless @io.nil? || @io.closed?
+ rm_r @name
+ end
+
+ it "sets close_on_exec on the newly-opened IO" do
+ @io = @o.rb_cloexec_open(@name, 0, 0)
+ @io.close_on_exec?.should be_true
+ end
+end
diff --git a/spec/ruby/optional/capi/kernel_spec.rb b/spec/ruby/optional/capi/kernel_spec.rb
new file mode 100644
index 0000000000..e50ae9c4e4
--- /dev/null
+++ b/spec/ruby/optional/capi/kernel_spec.rb
@@ -0,0 +1,539 @@
+require_relative 'spec_helper'
+
+load_extension("kernel")
+
+describe "C-API Kernel function" do
+ before :each do
+ @s = CApiKernelSpecs.new
+ end
+
+ describe "rb_block_given_p" do
+ it "returns false if no block is passed" do
+ @s.rb_block_given_p.should == false
+ end
+
+ it "returns true if a block is passed" do
+ (@s.rb_block_given_p { puts "FOO" } ).should == true
+ end
+ end
+
+ describe "rb_need_block" do
+ it "raises a LocalJumpError if no block is given" do
+ lambda { @s.rb_need_block }.should raise_error(LocalJumpError)
+ end
+
+ it "does not raise a LocalJumpError if a block is given" do
+ @s.rb_need_block { }.should == nil
+ end
+ end
+
+ describe "rb_block_call" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "calls the block with a single argument" do
+ ary = [1, 3, 5]
+ @s.rb_block_call(ary).should == [2, 4, 6]
+ end
+
+ it "calls the block with multiple arguments in argc / argv" do
+ ary = [1, 3, 5]
+ @s.rb_block_call_multi_arg(ary).should == 9
+ end
+
+ it "calls the method with no function callback and no block" do
+ ary = [1, 3, 5]
+ @s.rb_block_call_no_func(ary).should be_kind_of(Enumerator)
+ end
+
+ it "calls the method with no function callback and a block" do
+ ary = [1, 3, 5]
+ @s.rb_block_call_no_func(ary) do |i|
+ i + 1
+ end.should == [2, 4, 6]
+ end
+ end
+
+ describe "rb_frame_this_func" do
+ it "returns the name of the method called" do
+ @s.rb_frame_this_func_test.should == :rb_frame_this_func_test
+ @s.rb_frame_this_func_test_again.should == :rb_frame_this_func_test_again
+ end
+ end
+
+ describe "rb_raise" do
+ it "raises an exception" do
+ lambda { @s.rb_raise({}) }.should raise_error(TypeError)
+ end
+
+ it "terminates the function at the point it was called" do
+ h = {}
+ lambda { @s.rb_raise(h) }.should raise_error(TypeError)
+ h[:stage].should == :before
+ end
+ end
+
+ describe "rb_throw" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "sets the return value of the catch block to the specified value" do
+ catch(:foo) do
+ @s.rb_throw(:return_value)
+ end.should == :return_value
+ end
+
+ it "terminates the function at the point it was called" do
+ catch(:foo) do
+ ScratchPad << :before_throw
+ @s.rb_throw(:thrown_value)
+ ScratchPad << :after_throw
+ end.should == :thrown_value
+ ScratchPad.recorded.should == [:before_throw]
+ end
+
+ it "raises an ArgumentError if there is no catch block for the symbol" do
+ lambda { @s.rb_throw(nil) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "rb_throw_obj" do
+ before :each do
+ ScratchPad.record []
+ @tag = Object.new
+ end
+
+ it "sets the return value of the catch block to the specified value" do
+ catch(@tag) do
+ @s.rb_throw_obj(@tag, :thrown_value)
+ end.should == :thrown_value
+ end
+
+ it "terminates the function at the point it was called" do
+ catch(@tag) do
+ ScratchPad << :before_throw
+ @s.rb_throw_obj(@tag, :thrown_value)
+ ScratchPad << :after_throw
+ end.should == :thrown_value
+ ScratchPad.recorded.should == [:before_throw]
+ end
+
+ it "raises an ArgumentError if there is no catch block for the symbol" do
+ lambda { @s.rb_throw(nil) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "rb_warn" do
+ before :each do
+ @stderr, $stderr = $stderr, IOStub.new
+ @verbose = $VERBOSE
+ end
+
+ after :each do
+ $stderr = @stderr
+ $VERBOSE = @verbose
+ end
+
+ it "prints a message to $stderr if $VERBOSE evaluates to true" do
+ $VERBOSE = true
+ @s.rb_warn("This is a warning")
+ $stderr.should =~ /This is a warning/
+ end
+
+ it "prints a message to $stderr if $VERBOSE evaluates to false" do
+ $VERBOSE = false
+ @s.rb_warn("This is a warning")
+ $stderr.should =~ /This is a warning/
+ end
+ end
+
+ describe "rb_sys_fail" do
+ it "raises an exception from the value of errno" do
+ lambda do
+ @s.rb_sys_fail("additional info")
+ end.should raise_error(SystemCallError, /additional info/)
+ end
+
+ it "can take a NULL message" do
+ lambda do
+ @s.rb_sys_fail(nil)
+ end.should raise_error(Errno::EPERM)
+ end
+ end
+
+ describe "rb_syserr_fail" do
+ it "raises an exception from the given error" do
+ lambda do
+ @s.rb_syserr_fail(Errno::EINVAL::Errno, "additional info")
+ end.should raise_error(Errno::EINVAL, /additional info/)
+ end
+
+ it "can take a NULL message" do
+ lambda do
+ @s.rb_syserr_fail(Errno::EINVAL::Errno, nil)
+ end.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ describe "rb_yield" do
+ it "yields passed argument" do
+ ret = nil
+ @s.rb_yield(1) { |z| ret = z }
+ ret.should == 1
+ end
+
+ it "returns the result from block evaluation" do
+ @s.rb_yield(1) { |z| z * 1000 }.should == 1000
+ end
+
+ it "raises LocalJumpError when no block is given" do
+ lambda { @s.rb_yield(1) }.should raise_error(LocalJumpError)
+ end
+
+ it "rb_yield to a block that breaks does not raise an error" do
+ @s.rb_yield(1) { break }.should == nil
+ end
+
+ it "rb_yield to a block that breaks with a value returns the value" do
+ @s.rb_yield(1) { break 73 }.should == 73
+ end
+
+ platform_is_not :"solaris2.10" do # NOTE: i386-pc-solaris2.10
+ it "rb_yield through a callback to a block that breaks with a value returns the value" do
+ @s.rb_yield_indirected(1) { break 73 }.should == 73
+ end
+ end
+
+ it "rb_yield to block passed to enumerator" do
+ enum_class = Class.new do
+ include Enumerable
+ end
+ @s.rb_yield_define_each(enum_class)
+ res = enum_class.new.collect { |i| i * 2}
+ res.should == [0, 2, 4, 6]
+ end
+
+ end
+
+ describe "rb_yield_values" do
+ it "yields passed arguments" do
+ ret = nil
+ @s.rb_yield_values(1, 2) { |x, y| ret = x + y }
+ ret.should == 3
+ end
+
+ it "returns the result from block evaluation" do
+ @s.rb_yield_values(1, 2) { |x, y| x + y }.should == 3
+ end
+
+ it "raises LocalJumpError when no block is given" do
+ lambda { @s.rb_yield_splat([1, 2]) }.should raise_error(LocalJumpError)
+ end
+ end
+
+ describe "rb_yield_splat" do
+ it "yields with passed array's contents" do
+ ret = nil
+ @s.rb_yield_splat([1, 2]) { |x, y| ret = x + y }
+ ret.should == 3
+ end
+
+ it "returns the result from block evaluation" do
+ @s.rb_yield_splat([1, 2]) { |x, y| x + y }.should == 3
+ end
+
+ it "raises LocalJumpError when no block is given" do
+ lambda { @s.rb_yield_splat([1, 2]) }.should raise_error(LocalJumpError)
+ end
+ end
+
+ describe "rb_protect" do
+ it "will run a function with an argument" do
+ proof = [] # Hold proof of work performed after the yield.
+ res = @s.rb_protect_yield(7, proof) { |x| x + 1 }
+ res.should == 8
+ proof[0].should == 23
+ end
+
+ it "will allow cleanup code to run after break" do
+ proof = [] # Hold proof of work performed after the yield.
+ @s.rb_protect_yield(7, proof) { |x| break }
+ proof[0].should == 23
+ end
+
+ it "will allow cleanup code to run after break with value" do
+ proof = [] # Hold proof of work performed after the yield.
+ res = @s.rb_protect_yield(7, proof) { |x| break x + 1 }
+ res.should == 8
+ proof[0].should == 23
+ end
+
+ it "will allow cleanup code to run after a raise" do
+ proof = [] # Hold proof of work performed after the yield.
+ lambda do
+ @s.rb_protect_yield(7, proof) { |x| raise NameError}
+ end.should raise_error(NameError)
+ proof[0].should == 23
+ end
+ end
+
+ describe "rb_rescue" do
+ before :each do
+ @proc = lambda { |x| x }
+ @raise_proc_returns_sentinel = lambda {|*_| :raise_proc_executed }
+ @raise_proc_returns_arg = lambda {|*a| a }
+ @arg_error_proc = lambda { |*_| raise ArgumentError, '' }
+ @std_error_proc = lambda { |*_| raise StandardError, '' }
+ @exc_error_proc = lambda { |*_| raise Exception, '' }
+ end
+
+ it "executes passed function" do
+ @s.rb_rescue(@proc, :no_exc, @raise_proc_returns_arg, :exc).should == :no_exc
+ end
+
+ it "executes passed 'raise function' if a StandardError exception is raised" do
+ @s.rb_rescue(@arg_error_proc, nil, @raise_proc_returns_sentinel, :exc).should == :raise_proc_executed
+ @s.rb_rescue(@std_error_proc, nil, @raise_proc_returns_sentinel, :exc).should == :raise_proc_executed
+ end
+
+ it "passes the user supplied argument to the 'raise function' if a StandardError exception is raised" do
+ arg1, _ = @s.rb_rescue(@arg_error_proc, nil, @raise_proc_returns_arg, :exc1)
+ arg1.should == :exc1
+
+ arg2, _ = @s.rb_rescue(@std_error_proc, nil, @raise_proc_returns_arg, :exc2)
+ arg2.should == :exc2
+ end
+
+ it "passes the raised exception to the 'raise function' if a StandardError exception is raised" do
+ _, exc1 = @s.rb_rescue(@arg_error_proc, nil, @raise_proc_returns_arg, :exc)
+ exc1.class.should == ArgumentError
+
+ _, exc2 = @s.rb_rescue(@std_error_proc, nil, @raise_proc_returns_arg, :exc)
+ exc2.class.should == StandardError
+ end
+
+ it "raises an exception if passed function raises an exception other than StandardError" do
+ lambda { @s.rb_rescue(@exc_error_proc, nil, @raise_proc_returns_arg, nil) }.should raise_error(Exception)
+ end
+
+ it "raises an exception if any exception is raised inside 'raise function'" do
+ lambda { @s.rb_rescue(@std_error_proc, nil, @std_error_proc, nil) }.should raise_error(StandardError)
+ end
+
+ it "makes $! available only during 'raise function' execution" do
+ @s.rb_rescue(@std_error_proc, nil, lambda { |*_| $! }, nil).class.should == StandardError
+ $!.should == nil
+ end
+
+ it "returns the break value if the passed function yields to a block with a break" do
+ def proc_caller
+ @s.rb_rescue(lambda { |*_| yield }, nil, @proc, nil)
+ end
+
+ proc_caller { break :value }.should == :value
+ end
+ end
+
+ describe "rb_rescue2" do
+ it "only rescues if one of the passed exceptions is raised" do
+ proc = lambda { |x| x }
+ arg_error_proc = lambda { |*_| raise ArgumentError, '' }
+ run_error_proc = lambda { |*_| raise RuntimeError, '' }
+ type_error_proc = lambda { |*_| raise TypeError, '' }
+ @s.rb_rescue2(arg_error_proc, :no_exc, proc, :exc, ArgumentError, RuntimeError).should == :exc
+ @s.rb_rescue2(run_error_proc, :no_exc, proc, :exc, ArgumentError, RuntimeError).should == :exc
+ lambda {
+ @s.rb_rescue2(type_error_proc, :no_exc, proc, :exc, ArgumentError, RuntimeError)
+ }.should raise_error(TypeError)
+ end
+ end
+
+ describe "rb_catch" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "executes passed function" do
+ @s.rb_catch("foo", lambda { 1 }).should == 1
+ end
+
+ it "terminates the function at the point it was called" do
+ proc = lambda do
+ ScratchPad << :before_throw
+ throw :thrown_value
+ ScratchPad << :after_throw
+ end
+ @s.rb_catch("thrown_value", proc).should be_nil
+ ScratchPad.recorded.should == [:before_throw]
+ end
+
+ it "raises an ArgumentError if the throw symbol isn't caught" do
+ lambda { @s.rb_catch("foo", lambda { throw :bar }) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "rb_catch_obj" do
+
+ before :each do
+ ScratchPad.record []
+ @tag = Object.new
+ end
+
+ it "executes passed function" do
+ @s.rb_catch_obj(@tag, lambda { 1 }).should == 1
+ end
+
+ it "terminates the function at the point it was called" do
+ proc = lambda do
+ ScratchPad << :before_throw
+ throw @tag
+ ScratchPad << :after_throw
+ end
+ @s.rb_catch_obj(@tag, proc).should be_nil
+ ScratchPad.recorded.should == [:before_throw]
+ end
+
+ it "raises an ArgumentError if the throw symbol isn't caught" do
+ lambda { @s.rb_catch("foo", lambda { throw :bar }) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "rb_ensure" do
+ it "executes passed function and returns its value" do
+ proc = lambda { |x| x }
+ @s.rb_ensure(proc, :proc, proc, :ensure_proc).should == :proc
+ end
+
+ it "executes passed 'ensure function' when no exception is raised" do
+ foo = nil
+ proc = lambda { |*_| }
+ ensure_proc = lambda { |x| foo = x }
+ @s.rb_ensure(proc, nil, ensure_proc, :foo)
+ foo.should == :foo
+ end
+
+ it "executes passed 'ensure function' when an exception is raised" do
+ foo = nil
+ raise_proc = lambda { raise '' }
+ ensure_proc = lambda { |x| foo = x }
+ @s.rb_ensure(raise_proc, nil, ensure_proc, :foo) rescue nil
+ foo.should == :foo
+ end
+
+ it "raises the same exception raised inside passed function" do
+ raise_proc = lambda { |*_| raise RuntimeError, 'foo' }
+ proc = lambda { |*_| }
+ lambda { @s.rb_ensure(raise_proc, nil, proc, nil) }.should raise_error(RuntimeError, 'foo')
+ end
+ end
+
+ describe "rb_eval_string" do
+ it "evaluates a string of ruby code" do
+ @s.rb_eval_string("1+1").should == 2
+ end
+ end
+
+ describe "rb_block_proc" do
+ it "converts the implicit block into a proc" do
+ proc = @s.rb_block_proc() { 1+1 }
+ proc.should be_kind_of(Proc)
+ proc.call.should == 2
+ end
+ end
+
+ describe "rb_exec_recursive" do
+ it "detects recursive invocations of a method and indicates as such" do
+ s = "hello"
+ @s.rb_exec_recursive(s).should == s
+ end
+ end
+
+ platform_is_not :windows do
+ describe "rb_set_end_proc" do
+ before :each do
+ @r, @w = IO.pipe
+ end
+
+ after :each do
+ @r.close
+ @w.close
+ Process.wait @pid
+ end
+
+ it "runs a C function on shutdown" do
+ @pid = fork {
+ @s.rb_set_end_proc(@w)
+ }
+
+ @r.read(1).should == "e"
+ end
+ end
+ end
+
+ describe "rb_f_sprintf" do
+ it "returns a string according to format and arguments" do
+ @s.rb_f_sprintf(["%d %f %s", 10, 2.5, "test"]).should == "10 2.500000 test"
+ end
+ end
+
+ describe "rb_make_backtrace" do
+ it "returns a caller backtrace" do
+ backtrace = @s.rb_make_backtrace
+ lines = backtrace.select {|l| l =~ /#{__FILE__}/ }
+ lines.should_not be_empty
+ end
+ end
+
+ describe "rb_obj_method" do
+ it "returns the method object for a symbol" do
+ method = @s.rb_obj_method("test", :size)
+ method.owner.should == String
+ method.name.to_sym.should == :size
+ end
+
+ it "returns the method object for a string" do
+ method = @s.rb_obj_method("test", "size")
+ method.owner.should == String
+ method.name.to_sym.should == :size
+ end
+ end
+
+ describe "rb_funcall3" do
+ before :each do
+ @obj = Object.new
+ class << @obj
+ def method_public; :method_public end
+ def method_private; :method_private end
+ private :method_private
+ end
+ end
+
+ it "calls a public method" do
+ @s.rb_funcall3(@obj, :method_public).should == :method_public
+ end
+ it "does not call a private method" do
+ lambda { @s.rb_funcall3(@obj, :method_private) }.should raise_error(NoMethodError, /private/)
+ end
+ end
+
+ describe 'rb_funcall_with_block' do
+ before :each do
+ @obj = Object.new
+ class << @obj
+ def method_public; yield end
+ def method_private; yield end
+ private :method_private
+ end
+ end
+
+ it "calls a method with block" do
+ @s.rb_funcall_with_block(@obj, :method_public, proc { :result }).should == :result
+ end
+
+ it "does not call a private method" do
+ lambda { @s.rb_funcall_with_block(@obj, :method_private, proc { :result }) }.should raise_error(NoMethodError, /private/)
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/marshal_spec.rb b/spec/ruby/optional/capi/marshal_spec.rb
new file mode 100644
index 0000000000..f15b6b705a
--- /dev/null
+++ b/spec/ruby/optional/capi/marshal_spec.rb
@@ -0,0 +1,46 @@
+require_relative 'spec_helper'
+
+load_extension("marshal")
+
+describe "CApiMarshalSpecs" do
+ before :each do
+ @s = CApiMarshalSpecs.new
+ end
+
+ describe "rb_marshal_dump" do
+ before :each do
+ @obj = "foo"
+ end
+
+ it "marshals an object" do
+ expected = Marshal.dump(@obj)
+
+ @s.rb_marshal_dump(@obj, nil).should == expected
+ end
+
+ it "marshals an object and write to an IO when passed" do
+ expected_io = IOStub.new
+ test_io = IOStub.new
+
+ Marshal.dump(@obj, expected_io)
+
+ @s.rb_marshal_dump(@obj, test_io)
+
+ test_io.should == expected_io
+ end
+
+ end
+
+ describe "rb_marshal_load" do
+ before :each do
+ @obj = "foo"
+ @data = Marshal.dump(@obj)
+ end
+
+ it "unmarshals an object" do
+ @s.rb_marshal_load(@data).should == @obj
+ end
+
+ end
+
+end
diff --git a/spec/ruby/optional/capi/module_spec.rb b/spec/ruby/optional/capi/module_spec.rb
new file mode 100644
index 0000000000..b94e96e846
--- /dev/null
+++ b/spec/ruby/optional/capi/module_spec.rb
@@ -0,0 +1,373 @@
+require_relative 'spec_helper'
+require_relative 'fixtures/module'
+
+load_extension('module')
+compile_extension("module_under_autoload")
+
+describe "CApiModule" do
+
+ before :each do
+ @m = CApiModuleSpecs.new
+ end
+
+ describe "rb_define_global_const" do
+ it "defines a constant on Object" do
+ @m.rb_define_global_const("CApiModuleSpecsGlobalConst", 7)
+ ::CApiModuleSpecsGlobalConst.should == 7
+ Object.send :remove_const, :CApiModuleSpecsGlobalConst
+ end
+ end
+
+ describe "rb_const_set given a symbol name and a value" do
+ it "sets a new constant on a module" do
+ @m.rb_const_set(CApiModuleSpecs::C, :W, 7)
+ CApiModuleSpecs::C::W.should == 7
+ end
+
+ it "sets an existing constant's value" do
+ -> {
+ @m.rb_const_set(CApiModuleSpecs::C, :Z, 8)
+ }.should complain(/already initialized constant/)
+ CApiModuleSpecs::C::Z.should == 8
+ end
+
+ it "allows arbitrary names, including constant names not valid in Ruby" do
+ -> {
+ CApiModuleSpecs::C.const_set(:_INVALID, 1)
+ }.should raise_error(NameError, /wrong constant name/)
+
+ @m.rb_const_set(CApiModuleSpecs::C, :_INVALID, 2)
+ @m.rb_const_get(CApiModuleSpecs::C, :_INVALID).should == 2
+
+ # Ruby-level should still not allow access
+ -> {
+ CApiModuleSpecs::C.const_get(:_INVALID)
+ }.should raise_error(NameError, /wrong constant name/)
+ end
+ end
+
+ describe "rb_define_module" do
+ it "returns the module if it is already defined" do
+ mod = @m.rb_define_module("CApiModuleSpecsModuleA")
+ mod.const_get(:X).should == 1
+ end
+
+ it "raises a TypeError if the constant is not a module" do
+ ::CApiModuleSpecsGlobalConst = 7
+ lambda { @m.rb_define_module("CApiModuleSpecsGlobalConst") }.should raise_error(TypeError)
+ Object.send :remove_const, :CApiModuleSpecsGlobalConst
+ end
+
+ it "defines a new module at toplevel" do
+ mod = @m.rb_define_module("CApiModuleSpecsModuleB")
+ mod.should be_kind_of(Module)
+ mod.name.should == "CApiModuleSpecsModuleB"
+ ::CApiModuleSpecsModuleB.should be_kind_of(Module)
+ Object.send :remove_const, :CApiModuleSpecsModuleB
+ end
+ end
+
+ describe "rb_define_module_under" do
+ it "creates a new module inside the inner class" do
+ mod = @m.rb_define_module_under(CApiModuleSpecs, "ModuleSpecsModuleUnder1")
+ mod.should be_kind_of(Module)
+ end
+
+ it "sets the module name" do
+ mod = @m.rb_define_module_under(CApiModuleSpecs, "ModuleSpecsModuleUnder2")
+ mod.name.should == "CApiModuleSpecs::ModuleSpecsModuleUnder2"
+ end
+ end
+
+ describe "rb_define_module_under" do
+ it "defines a module for an existing Autoload with an extension" do
+ CApiModuleSpecs::ModuleUnderAutoload.name.should == "CApiModuleSpecs::ModuleUnderAutoload"
+ end
+
+ it "defines a module for an existing Autoload with a ruby object" do
+ CApiModuleSpecs::RubyUnderAutoload.name.should == "CApiModuleSpecs::RubyUnderAutoload"
+ end
+ end
+
+ describe "rb_define_const given a String name and a value" do
+ it "defines a new constant on a module" do
+ @m.rb_define_const(CApiModuleSpecs::C, "V", 7)
+ CApiModuleSpecs::C::V.should == 7
+ end
+
+ it "sets an existing constant's value" do
+ -> {
+ @m.rb_define_const(CApiModuleSpecs::C, "Z", 9)
+ }.should complain(/already initialized constant/)
+ CApiModuleSpecs::C::Z.should == 9
+ end
+ end
+
+ describe "rb_const_defined" do
+ # The fixture converts C boolean test to Ruby 'true' / 'false'
+ it "returns C non-zero if a constant is defined" do
+ @m.rb_const_defined(CApiModuleSpecs::A, :X).should be_true
+ end
+
+ it "returns C non-zero if a constant is defined in Object" do
+ @m.rb_const_defined(CApiModuleSpecs::A, :Module).should be_true
+ end
+ end
+
+ describe "rb_const_defined_at" do
+ # The fixture converts C boolean test to Ruby 'true' / 'false'
+ it "returns C non-zero if a constant is defined" do
+ @m.rb_const_defined_at(CApiModuleSpecs::A, :X).should be_true
+ end
+
+ it "does not search in ancestors for the constant" do
+ @m.rb_const_defined_at(CApiModuleSpecs::B, :X).should be_false
+ end
+
+ it "does not search in Object" do
+ @m.rb_const_defined_at(CApiModuleSpecs::A, :Module).should be_false
+ end
+ end
+
+ describe "rb_const_get" do
+ it "returns a constant defined in the module" do
+ @m.rb_const_get(CApiModuleSpecs::A, :X).should == 1
+ end
+
+ it "returns a constant defined at toplevel" do
+ @m.rb_const_get(CApiModuleSpecs::A, :Fixnum).should == Fixnum
+ end
+
+ it "returns a constant defined in a superclass" do
+ @m.rb_const_get(CApiModuleSpecs::B, :X).should == 1
+ end
+
+ it "calls #const_missing if the constant is not defined in the class or ancestors" do
+ CApiModuleSpecs::A.should_receive(:const_missing).with(:CApiModuleSpecsUndefined)
+ @m.rb_const_get(CApiModuleSpecs::A, :CApiModuleSpecsUndefined)
+ end
+
+ it "resolves autoload constants in classes" do
+ @m.rb_const_get(CApiModuleSpecs::A, :D).should == 123
+ end
+
+ it "resolves autoload constants in Object" do
+ @m.rb_const_get(Object, :CApiModuleSpecsAutoload).should == 123
+ end
+
+ it "allows arbitrary names, including constant names not valid in Ruby" do
+ -> {
+ CApiModuleSpecs::A.const_get(:_INVALID)
+ }.should raise_error(NameError, /wrong constant name/)
+
+ -> {
+ @m.rb_const_get(CApiModuleSpecs::A, :_INVALID)
+ }.should raise_error(NameError, /uninitialized constant/)
+ end
+ end
+
+ describe "rb_const_get_from" do
+ it "returns a constant defined in the module" do
+ @m.rb_const_get_from(CApiModuleSpecs::B, :Y).should == 2
+ end
+
+ it "returns a constant defined in a superclass" do
+ @m.rb_const_get_from(CApiModuleSpecs::B, :X).should == 1
+ end
+
+ it "calls #const_missing if the constant is not defined in the class or ancestors" do
+ CApiModuleSpecs::M.should_receive(:const_missing).with(:Fixnum)
+ @m.rb_const_get_from(CApiModuleSpecs::M, :Fixnum)
+ end
+
+ it "resolves autoload constants" do
+ @m.rb_const_get_from(CApiModuleSpecs::A, :C).should == 123
+ end
+ end
+
+ describe "rb_const_get_at" do
+ it "returns a constant defined in the module" do
+ @m.rb_const_get_at(CApiModuleSpecs::B, :Y).should == 2
+ end
+
+ it "resolves autoload constants" do
+ @m.rb_const_get_at(CApiModuleSpecs::A, :B).should == 123
+ end
+
+ it "calls #const_missing if the constant is not defined in the module" do
+ CApiModuleSpecs::B.should_receive(:const_missing).with(:X)
+ @m.rb_const_get_at(CApiModuleSpecs::B, :X)
+ end
+ end
+
+ describe "rb_define_alias" do
+ it "defines an alias for an existing method" do
+ cls = Class.new do
+ def method_to_be_aliased
+ :method_to_be_aliased
+ end
+ end
+
+ @m.rb_define_alias cls, "method_alias", "method_to_be_aliased"
+ cls.new.method_alias.should == :method_to_be_aliased
+ end
+ end
+
+ describe "rb_alias" do
+ it "defines an alias for an existing method" do
+ cls = Class.new do
+ def method_to_be_aliased
+ :method_to_be_aliased
+ end
+ end
+
+ @m.rb_alias cls, :method_alias, :method_to_be_aliased
+ cls.new.method_alias.should == :method_to_be_aliased
+ end
+ end
+
+ describe "rb_define_global_function" do
+ it "defines a method on Kernel" do
+ @m.rb_define_global_function("module_specs_global_function")
+ Kernel.should have_method(:module_specs_global_function)
+ module_specs_global_function.should == :test_method
+ end
+ end
+
+ describe "rb_define_method" do
+ it "defines a method on a class" do
+ cls = Class.new
+ @m.rb_define_method(cls, "test_method")
+ cls.should have_instance_method(:test_method)
+ cls.new.test_method.should == :test_method
+ end
+
+ it "defines a method on a module" do
+ mod = Module.new
+ @m.rb_define_method(mod, "test_method")
+ mod.should have_instance_method(:test_method)
+ end
+ end
+
+ describe "rb_define_module_function" do
+ before :each do
+ @mod = Module.new
+ @m.rb_define_module_function @mod, "test_module_function"
+ end
+
+ it "defines a module function" do
+ @mod.test_module_function.should == :test_method
+ end
+
+ it "defines a private instance method" do
+ cls = Class.new
+ cls.include(@mod)
+
+ cls.should have_private_instance_method(:test_module_function)
+ end
+ end
+
+ describe "rb_define_private_method" do
+ it "defines a private method on a class" do
+ cls = Class.new
+ @m.rb_define_private_method(cls, "test_method")
+ cls.should have_private_instance_method(:test_method)
+ cls.new.send(:test_method).should == :test_method
+ end
+
+ it "defines a private method on a module" do
+ mod = Module.new
+ @m.rb_define_private_method(mod, "test_method")
+ mod.should have_private_instance_method(:test_method)
+ end
+ end
+
+ describe "rb_define_protected_method" do
+ it "defines a protected method on a class" do
+ cls = Class.new
+ @m.rb_define_protected_method(cls, "test_method")
+ cls.should have_protected_instance_method(:test_method)
+ cls.new.send(:test_method).should == :test_method
+ end
+
+ it "defines a protected method on a module" do
+ mod = Module.new
+ @m.rb_define_protected_method(mod, "test_method")
+ mod.should have_protected_instance_method(:test_method)
+ end
+ end
+
+ describe "rb_define_singleton_method" do
+ it "defines a method on the singleton class" do
+ cls = Class.new
+ a = cls.new
+ @m.rb_define_singleton_method a, "module_specs_singleton_method"
+ a.module_specs_singleton_method.should == :test_method
+ lambda { cls.new.module_specs_singleton_method }.should raise_error(NoMethodError)
+ end
+ end
+
+ describe "rb_undef_method" do
+ before :each do
+ @class = Class.new do
+ def ruby_test_method
+ :ruby_test_method
+ end
+ end
+ end
+
+ it "undef'ines a method on a class" do
+ @class.new.ruby_test_method.should == :ruby_test_method
+ @m.rb_undef_method @class, "ruby_test_method"
+ @class.should_not have_instance_method(:ruby_test_method)
+ end
+
+ it "does not raise exceptions when passed a missing name" do
+ lambda { @m.rb_undef_method @class, "not_exist" }.should_not raise_error
+ end
+
+ describe "when given a frozen Class" do
+ before :each do
+ @frozen = @class.dup.freeze
+ end
+
+ it "raises a #{frozen_error_class} when passed a name" do
+ lambda { @m.rb_undef_method @frozen, "ruby_test_method" }.should raise_error(frozen_error_class)
+ end
+
+ it "raises a #{frozen_error_class} when passed a missing name" do
+ lambda { @m.rb_undef_method @frozen, "not_exist" }.should raise_error(frozen_error_class)
+ end
+ end
+ end
+
+ describe "rb_undef" do
+ it "undef'ines a method on a class" do
+ cls = Class.new do
+ def ruby_test_method
+ :ruby_test_method
+ end
+ end
+
+ cls.new.ruby_test_method.should == :ruby_test_method
+ @m.rb_undef cls, :ruby_test_method
+ cls.should_not have_instance_method(:ruby_test_method)
+ end
+ end
+
+ describe "rb_class2name" do
+ it "returns the module name" do
+ @m.rb_class2name(CApiModuleSpecs::M).should == "CApiModuleSpecs::M"
+ end
+ end
+
+ describe "rb_mod_ancestors" do
+ it "returns an array of ancestors" do
+ one = Module.new
+ two = Module.new do
+ include one
+ end
+ @m.rb_mod_ancestors(two).should == [two, one]
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/mutex_spec.rb b/spec/ruby/optional/capi/mutex_spec.rb
new file mode 100644
index 0000000000..8844d311f2
--- /dev/null
+++ b/spec/ruby/optional/capi/mutex_spec.rb
@@ -0,0 +1,88 @@
+require_relative 'spec_helper'
+
+load_extension("mutex")
+
+describe "C-API Mutex functions" do
+ before :each do
+ @s = CApiMutexSpecs.new
+ @m = Mutex.new
+ end
+
+ describe "rb_mutex_new" do
+ it "creates a new mutex" do
+ @s.rb_mutex_new.should be_an_instance_of(Mutex)
+ end
+ end
+
+ describe "rb_mutex_locked_p" do
+ it "returns false if the mutex is not locked" do
+ @s.rb_mutex_locked_p(@m).should be_false
+ end
+
+ it "returns true if the mutex is locked" do
+ @m.lock
+ @s.rb_mutex_locked_p(@m).should be_true
+ end
+ end
+
+ describe "rb_mutex_trylock" do
+ it "locks the mutex if not locked" do
+ @s.rb_mutex_trylock(@m).should be_true
+ @m.locked?.should be_true
+ end
+
+ it "returns false if the mutex is already locked" do
+ @m.lock
+ @s.rb_mutex_trylock(@m).should be_false
+ @m.locked?.should be_true
+ end
+ end
+
+ describe "rb_mutex_lock" do
+ it "returns when the mutex isn't locked" do
+ @s.rb_mutex_lock(@m).should == @m
+ @m.locked?.should be_true
+ end
+
+ it "throws an exception when already locked in the same thread" do
+ @m.lock
+ lambda { @s.rb_mutex_lock(@m) }.should raise_error(ThreadError)
+ @m.locked?.should be_true
+ end
+ end
+
+ describe "rb_mutex_unlock" do
+ it "raises an exception when not locked" do
+ lambda { @s.rb_mutex_unlock(@m) }.should raise_error(ThreadError)
+ @m.locked?.should be_false
+ end
+
+ it "unlocks the mutex when locked" do
+ @m.lock
+ @s.rb_mutex_unlock(@m).should == @m
+ @m.locked?.should be_false
+ end
+ end
+
+ describe "rb_mutex_sleep" do
+ it "throws an exception when the mutex is not locked" do
+ lambda { @s.rb_mutex_sleep(@m, 0.1) }.should raise_error(ThreadError)
+ @m.locked?.should be_false
+ end
+
+ it "sleeps when the mutex is locked" do
+ @m.lock
+ start = Time.now
+ @s.rb_mutex_sleep(@m, 0.1)
+ (Time.now - start).should be_close(0.1, 0.2)
+ @m.locked?.should be_true
+ end
+ end
+
+ describe "rb_mutex_synchronize" do
+ it "calls the function while the mutex is locked" do
+ callback = lambda { @m.locked?.should be_true }
+ @s.rb_mutex_synchronize(@m, callback)
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/numeric_spec.rb b/spec/ruby/optional/capi/numeric_spec.rb
new file mode 100644
index 0000000000..5301cbcf10
--- /dev/null
+++ b/spec/ruby/optional/capi/numeric_spec.rb
@@ -0,0 +1,489 @@
+require_relative 'spec_helper'
+
+load_extension("numeric")
+
+describe "CApiNumericSpecs" do
+ before :each do
+ @s = CApiNumericSpecs.new
+ end
+
+ platform_is wordsize: 64 do
+ describe "rb_num2int" do
+ it "raises a TypeError if passed nil" do
+ lambda { @s.rb_num2int(nil) }.should raise_error(TypeError)
+ end
+
+ it "converts a Float" do
+ @s.rb_num2int(4.2).should == 4
+ end
+
+ it "converts a Bignum" do
+ @s.rb_num2int(0x7fff_ffff).should == 0x7fff_ffff
+ end
+
+ it "converts a Fixnum" do
+ @s.rb_num2int(5).should == 5
+ end
+
+ it "converts -1 to an signed number" do
+ @s.rb_num2int(-1).should == -1
+ end
+
+ it "converts a negative Bignum into an signed number" do
+ @s.rb_num2int(-2147442171).should == -2147442171
+ end
+
+ it "raises a RangeError if the value is more than 32bits" do
+ lambda { @s.rb_num2int(0xffff_ffff+1) }.should raise_error(RangeError)
+ end
+
+ it "calls #to_int to coerce the value" do
+ obj = mock("number")
+ obj.should_receive(:to_int).and_return(2)
+ @s.rb_num2long(obj).should == 2
+ end
+ end
+ end
+
+ platform_is wordsize: 64 do
+ describe "rb_num2uint" do
+ it "raises a TypeError if passed nil" do
+ lambda { @s.rb_num2uint(nil) }.should raise_error(TypeError)
+ end
+
+ it "converts a Float" do
+ @s.rb_num2uint(4.2).should == 4
+ end
+
+ it "converts a Bignum" do
+ @s.rb_num2uint(0xffff_ffff).should == 0xffff_ffff
+ end
+
+ it "converts a Fixnum" do
+ @s.rb_num2uint(5).should == 5
+ end
+
+ it "converts a negative number to the complement" do
+ @s.rb_num2uint(-1).should == 18446744073709551615
+ end
+
+ it "converts a signed int value to the complement" do
+ @s.rb_num2uint(-0x8000_0000).should == 18446744071562067968
+ end
+
+ it "raises a RangeError if the value is more than 32bits" do
+ lambda { @s.rb_num2uint(0xffff_ffff+1) }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError if the value is less than 32bits negative" do
+ lambda { @s.rb_num2uint(-0x8000_0000-1) }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError if the value is more than 64bits" do
+ lambda do
+ @s.rb_num2uint(0xffff_ffff_ffff_ffff+1)
+ end.should raise_error(RangeError)
+ end
+
+ it "calls #to_int to coerce the value" do
+ obj = mock("number")
+ obj.should_receive(:to_int).and_return(2)
+ @s.rb_num2uint(obj).should == 2
+ end
+ end
+ end
+
+ describe "rb_num2long" do
+ it "raises a TypeError if passed nil" do
+ lambda { @s.rb_num2long(nil) }.should raise_error(TypeError)
+ end
+
+ it "converts a Float" do
+ @s.rb_num2long(4.2).should == 4
+ end
+
+ it "converts a Bignum" do
+ @s.rb_num2long(0x7fff_ffff).should == 0x7fff_ffff
+ end
+
+ it "converts a Fixnum" do
+ @s.rb_num2long(5).should == 5
+ end
+
+ platform_is wordsize: 32 do
+ it "converts -1 to an signed number" do
+ @s.rb_num2long(-1).should == -1
+ end
+
+ it "converts a negative Bignum into an signed number" do
+ @s.rb_num2long(-2147442171).should == -2147442171
+ end
+
+ it "raises a RangeError if the value is more than 32bits" do
+ lambda { @s.rb_num2long(0xffff_ffff+1) }.should raise_error(RangeError)
+ end
+ end
+
+ platform_is wordsize: 64 do
+ it "converts -1 to an signed number" do
+ @s.rb_num2long(-1).should == -1
+ end
+
+ it "converts a negative Bignum into an signed number" do
+ @s.rb_num2long(-9223372036854734331).should == -9223372036854734331
+ end
+
+ it "raises a RangeError if the value is more than 64bits" do
+ lambda do
+ @s.rb_num2long(0xffff_ffff_ffff_ffff+1)
+ end.should raise_error(RangeError)
+ end
+ end
+
+ it "calls #to_int to coerce the value" do
+ obj = mock("number")
+ obj.should_receive(:to_int).and_return(2)
+ @s.rb_num2long(obj).should == 2
+ end
+ end
+
+ describe "rb_int2num" do
+ it "raises a TypeError if passed nil" do
+ lambda { @s.rb_int2num(nil) }.should raise_error(TypeError)
+ end
+
+ it "converts a Float" do
+ @s.rb_int2num(4.2).should == 4
+ end
+
+ it "raises a RangeError when passed a Bignum" do
+ lambda { @s.rb_int2num(bignum_value) }.should raise_error(RangeError)
+ end
+
+ it "converts a Fixnum" do
+ @s.rb_int2num(5).should == 5
+ end
+
+ it "converts a negative Fixnum" do
+ @s.rb_int2num(-11).should == -11
+ end
+ end
+
+ describe "rb_num2ulong" do
+ it "raises a TypeError if passed nil" do
+ lambda { @s.rb_num2ulong(nil) }.should raise_error(TypeError)
+ end
+
+ it "converts a Float" do
+ @s.rb_num2ulong(4.2).should == 4
+ end
+
+ it "converts a Bignum" do
+ @s.rb_num2ulong(0xffff_ffff).should == 0xffff_ffff
+ end
+
+ it "converts a Fixnum" do
+ @s.rb_num2ulong(5).should == 5
+ end
+
+ platform_is wordsize: 32 do
+ it "converts -1 to an unsigned number" do
+ @s.rb_num2ulong(-1).should == 4294967295
+ end
+
+ it "converts a negative Bignum into an unsigned number" do
+ @s.rb_num2ulong(-2147442171).should == 2147525125
+ end
+
+ it "converts positive Bignums if the values is less than 64bits" do
+ @s.rb_num2ulong(0xffff_ffff).should == 0xffff_ffff
+ @s.rb_num2ulong(2**30).should == 2**30
+ @s.rb_num2ulong(fixnum_max+1).should == fixnum_max+1
+ @s.rb_num2ulong(fixnum_max).should == fixnum_max
+ end
+
+ it "raises a RangeError if the value is more than 32bits" do
+ lambda { @s.rb_num2ulong(0xffff_ffff+1) }.should raise_error(RangeError)
+ end
+ end
+
+ platform_is wordsize: 64 do
+ it "converts -1 to an unsigned number" do
+ @s.rb_num2ulong(-1).should == 18446744073709551615
+ end
+
+ it "converts a negative Bignum into an unsigned number" do
+ @s.rb_num2ulong(-9223372036854734331).should == 9223372036854817285
+ end
+
+ it "converts positive Bignums if the values is less than 64bits" do
+ @s.rb_num2ulong(0xffff_ffff_ffff_ffff).should == 0xffff_ffff_ffff_ffff
+ @s.rb_num2ulong(2**62).should == 2**62
+ @s.rb_num2ulong(fixnum_max+1).should == fixnum_max+1
+ @s.rb_num2ulong(fixnum_max).should == fixnum_max
+ end
+
+ it "raises a RangeError if the value is more than 64bits" do
+ lambda do
+ @s.rb_num2ulong(0xffff_ffff_ffff_ffff+1)
+ end.should raise_error(RangeError)
+ end
+ end
+
+ it "calls #to_int to coerce the value" do
+ obj = mock("number")
+ obj.should_receive(:to_int).and_return(2)
+ @s.rb_num2ulong(obj).should == 2
+ end
+ end
+
+ describe "rb_Integer" do
+ it "creates an Integer from a String" do
+ i = @s.rb_Integer("8675309")
+ i.should be_kind_of(Integer)
+ i.should eql(8675309)
+ end
+ end
+
+ describe "rb_ll2inum" do
+ it "creates a Fixnum from a small signed long long" do
+ i = @s.rb_ll2inum_14()
+ i.should be_kind_of(Fixnum)
+ i.should eql(14)
+ end
+ end
+
+ describe "rb_ull2inum" do
+ it "creates a Fixnum from a small unsigned long long" do
+ i = @s.rb_ull2inum_14()
+ i.should be_kind_of(Fixnum)
+ i.should eql(14)
+ end
+
+ it "creates a positive Bignum from a negative long long" do
+ i = @s.rb_ull2inum_n14()
+ i.should be_kind_of(Bignum)
+ i.should eql(2 ** (@s.size_of_long_long * 8) - 14)
+ end
+ end
+
+ describe "rb_int2inum" do
+ it "creates a Fixnum from a long" do
+ i = @s.rb_int2inum_14()
+ i.should be_kind_of(Fixnum)
+ i.should eql(14)
+ end
+ end
+
+ describe "rb_uint2inum" do
+ it "creates a Fixnum from a long" do
+ i = @s.rb_uint2inum_14()
+ i.should be_kind_of(Fixnum)
+ i.should eql(14)
+ end
+
+ it "creates a positive Bignum from a negative long" do
+ i = @s.rb_uint2inum_n14()
+ i.should be_kind_of(Bignum)
+ i.should eql(2 ** (@s.size_of_VALUE * 8) - 14)
+ end
+ end
+
+ describe "rb_num2dbl" do
+ it "raises a TypeError if passed nil" do
+ lambda { @s.rb_num2dbl(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed a String" do
+ lambda { @s.rb_num2dbl("1.2") }.should raise_error(TypeError)
+ end
+
+ it "converts a Float" do
+ @s.rb_num2dbl(4.2).should == 4.2
+ end
+
+ it "converts a Bignum" do
+ @s.rb_num2dbl(2**70).should == (2**70).to_f
+ end
+
+ it "converts a Fixnum" do
+ @s.rb_num2dbl(5).should == 5.0
+ end
+
+ it "calls #to_f to coerce the value" do
+ obj = mock("number")
+ obj.should_receive(:to_f).and_return(2.0)
+ @s.rb_num2dbl(obj).should == 2.0
+ end
+ end
+
+ describe "NUM2CHR" do
+ it "returns the first character of a String" do
+ @s.NUM2CHR("Abc").should == 65
+ end
+
+ it "returns the least significant byte of an Integer" do
+ @s.NUM2CHR(0xa7c).should == 0x07c
+ end
+
+ it "returns the least significant byte of a Float converted to an Integer" do
+ @s.NUM2CHR(0xa7c.to_f).should == 0x07c
+ end
+
+ it "raises a TypeError when passed an empty String" do
+ lambda { @s.NUM2CHR("") }.should raise_error(TypeError)
+ end
+ end
+
+ describe "rb_num_zerodiv" do
+ it "raises a RuntimeError" do
+ lambda { @s.rb_num_zerodiv() }.should raise_error(ZeroDivisionError, 'divided by 0')
+ end
+ end
+
+ describe "rb_cmpint" do
+ it "returns a Fixnum if passed one" do
+ @s.rb_cmpint(1, 2).should == 1
+ end
+
+ it "uses > to check if the value is greater than 1" do
+ m = mock("number")
+ m.should_receive(:>).and_return(true)
+ @s.rb_cmpint(m, 4).should == 1
+ end
+
+ it "uses < to check if the value is less than 1" do
+ m = mock("number")
+ m.should_receive(:>).and_return(false)
+ m.should_receive(:<).and_return(true)
+ @s.rb_cmpint(m, 4).should == -1
+ end
+
+ it "returns 0 if < and > are false" do
+ m = mock("number")
+ m.should_receive(:>).and_return(false)
+ m.should_receive(:<).and_return(false)
+ @s.rb_cmpint(m, 4).should == 0
+ end
+
+ it "raises an ArgumentError when passed nil" do
+ lambda {
+ @s.rb_cmpint(nil, 4)
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "rb_num_coerce_bin" do
+ it "calls #coerce on the first argument" do
+ obj = mock("rb_num_coerce_bin")
+ obj.should_receive(:coerce).with(2).and_return([1, 2])
+
+ @s.rb_num_coerce_bin(2, obj, :+).should == 3
+ end
+
+ it "calls the specified method on the first argument returned by #coerce" do
+ obj = mock("rb_num_coerce_bin")
+ obj.should_receive(:coerce).with(2).and_return([obj, 2])
+ obj.should_receive(:+).with(2).and_return(3)
+
+ @s.rb_num_coerce_bin(2, obj, :+).should == 3
+ end
+
+ it "raises a TypeError if #coerce does not return an Array" do
+ obj = mock("rb_num_coerce_bin")
+ obj.should_receive(:coerce).with(2).and_return(nil)
+
+ lambda { @s.rb_num_coerce_bin(2, obj, :+) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "rb_num_coerce_cmp" do
+ it "calls #coerce on the first argument" do
+ obj = mock("rb_num_coerce_cmp")
+ obj.should_receive(:coerce).with(2).and_return([1, 2])
+
+ @s.rb_num_coerce_cmp(2, obj, :<=>).should == -1
+ end
+
+ it "calls the specified method on the first argument returned by #coerce" do
+ obj = mock("rb_num_coerce_cmp")
+ obj.should_receive(:coerce).with(2).and_return([obj, 2])
+ obj.should_receive(:<=>).with(2).and_return(-1)
+
+ @s.rb_num_coerce_cmp(2, obj, :<=>).should == -1
+ end
+
+ ruby_version_is ""..."2.5" do
+ it "returns nil if passed nil" do
+ -> {
+ @result = @s.rb_num_coerce_cmp(nil, 2, :<=>)
+ }.should complain(/comparison operators will no more rescue exceptions/)
+ @result.should be_nil
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "lets the exception go through if #coerce raises an exception" do
+ obj = mock("rb_num_coerce_cmp")
+ obj.should_receive(:coerce).with(2).and_raise(RuntimeError.new("my error"))
+ -> {
+ @s.rb_num_coerce_cmp(2, obj, :<=>)
+ }.should raise_error(RuntimeError, "my error")
+ end
+ end
+
+ it "returns nil if #coerce does not return an Array" do
+ obj = mock("rb_num_coerce_cmp")
+ obj.should_receive(:coerce).with(2).and_return(nil)
+
+ @s.rb_num_coerce_cmp(2, obj, :<=>).should be_nil
+ end
+ end
+
+ describe "rb_num_coerce_relop" do
+ it "calls #coerce on the first argument" do
+ obj = mock("rb_num_coerce_relop")
+ obj.should_receive(:coerce).with(2).and_return([1, 2])
+
+ @s.rb_num_coerce_relop(2, obj, :<).should be_true
+ end
+
+ it "calls the specified method on the first argument returned by #coerce" do
+ obj = mock("rb_num_coerce_relop")
+ obj.should_receive(:coerce).with(2).and_return([obj, 2])
+ obj.should_receive(:<).with(2).and_return(false)
+
+ @s.rb_num_coerce_relop(2, obj, :<).should be_false
+ end
+
+ it "raises an ArgumentError if #<op> returns nil" do
+ obj = mock("rb_num_coerce_relop")
+ obj.should_receive(:coerce).with(2).and_return([obj, 2])
+ obj.should_receive(:<).with(2).and_return(nil)
+
+ lambda { @s.rb_num_coerce_relop(2, obj, :<) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if #coerce does not return an Array" do
+ obj = mock("rb_num_coerce_relop")
+ obj.should_receive(:coerce).with(2).and_return(nil)
+
+ lambda { @s.rb_num_coerce_relop(2, obj, :<) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "rb_absint_singlebit_p" do
+ it "returns 1 if absolute value fits into a bit" do
+ @s.rb_absint_singlebit_p(1).should == 1
+ @s.rb_absint_singlebit_p(2).should == 1
+ @s.rb_absint_singlebit_p(3).should == 0
+ @s.rb_absint_singlebit_p(-1).should == 1
+ @s.rb_absint_singlebit_p(-2).should == 1
+ @s.rb_absint_singlebit_p(-3).should == 0
+ @s.rb_absint_singlebit_p(bignum_value).should == 1
+ @s.rb_absint_singlebit_p(bignum_value(1)).should == 0
+ @s.rb_absint_singlebit_p(-bignum_value).should == 1
+ @s.rb_absint_singlebit_p(-bignum_value(1)).should == 0
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/object_spec.rb b/spec/ruby/optional/capi/object_spec.rb
new file mode 100644
index 0000000000..541b58b48c
--- /dev/null
+++ b/spec/ruby/optional/capi/object_spec.rb
@@ -0,0 +1,857 @@
+require_relative 'spec_helper'
+
+load_extension("object")
+
+# TODO: fix all these specs
+
+class CApiObjectSpecs
+ class Alloc
+ attr_reader :initialized, :arguments
+
+ def initialize(*args)
+ @initialized = true
+ @arguments = args
+ end
+ end
+
+ class SubArray < ::Array
+ def to_array
+ self
+ end
+ end
+end
+
+describe "CApiObject" do
+
+ before do
+ @o = CApiObjectSpecs.new
+ end
+
+ class ObjectTest
+ def initialize
+ @foo = 7
+ end
+
+ def foo
+ end
+
+ def private_foo
+ end
+ private :private_foo
+ end
+
+ class AryChild < Array
+ end
+
+ class StrChild < String
+ end
+
+ class DescObjectTest < ObjectTest
+ end
+
+ class MethodArity
+ def one; end
+ def two(a); end
+ def three(*a); end
+ def four(a, b); end
+ def five(a, b, *c); end
+ def six(a, b, *c, &d); end
+ end
+
+ describe "rb_obj_alloc" do
+ it "allocates a new uninitialized object" do
+ o = @o.rb_obj_alloc(CApiObjectSpecs::Alloc)
+ o.class.should == CApiObjectSpecs::Alloc
+ o.initialized.should be_nil
+ end
+ end
+
+ describe "rb_obj_dup" do
+ it "duplicates an object" do
+ obj1 = ObjectTest.new
+ obj2 = @o.rb_obj_dup(obj1)
+
+ obj2.class.should == obj1.class
+
+ obj2.foo.should == obj1.foo
+
+ obj2.should_not equal(obj1)
+ end
+ end
+
+ describe "rb_obj_call_init" do
+ it "sends #initialize" do
+ o = @o.rb_obj_alloc(CApiObjectSpecs::Alloc)
+ o.initialized.should be_nil
+
+ @o.rb_obj_call_init(o, 2, [:one, :two])
+ o.initialized.should be_true
+ o.arguments.should == [:one, :two]
+ end
+ end
+
+ describe "rb_is_instance_of" do
+ it "returns true if an object is an instance" do
+ @o.rb_obj_is_instance_of(ObjectTest.new, ObjectTest).should == true
+ @o.rb_obj_is_instance_of(DescObjectTest.new, ObjectTest).should == false
+ end
+ end
+
+ describe "rb_is_kind_of" do
+ it "returns true if an object is an instance or descendent" do
+ @o.rb_obj_is_kind_of(ObjectTest.new, ObjectTest).should == true
+ @o.rb_obj_is_kind_of(DescObjectTest.new, ObjectTest).should == true
+ @o.rb_obj_is_kind_of(Object.new, ObjectTest).should == false
+ end
+ end
+
+ describe "rb_respond_to" do
+ it "returns 1 if respond_to? is true and 0 if respond_to? is false" do
+ @o.rb_respond_to(ObjectTest.new, :foo).should == true
+ @o.rb_respond_to(ObjectTest.new, :bar).should == false
+ end
+ end
+
+ describe "rb_obj_respond_to" do
+ it "returns true if respond_to? is true and false if respond_to? is false" do
+ @o.rb_obj_respond_to(ObjectTest.new, :foo, true).should == true
+ @o.rb_obj_respond_to(ObjectTest.new, :bar, true).should == false
+ @o.rb_obj_respond_to(ObjectTest.new, :private_foo, false).should == false
+ @o.rb_obj_respond_to(ObjectTest.new, :private_foo, true).should == true
+ end
+ end
+
+ describe "rb_obj_method_arity" do
+ before :each do
+ @obj = MethodArity.new
+ end
+
+ it "returns 0 when the method takes no arguments" do
+ @o.rb_obj_method_arity(@obj, :one).should == 0
+ end
+
+ it "returns 1 when the method takes a single, required argument" do
+ @o.rb_obj_method_arity(@obj, :two).should == 1
+ end
+
+ it "returns -1 when the method takes a variable number of arguments" do
+ @o.rb_obj_method_arity(@obj, :three).should == -1
+ end
+
+ it "returns 2 when the method takes two required arguments" do
+ @o.rb_obj_method_arity(@obj, :four).should == 2
+ end
+
+ it "returns -N-1 when the method takes N required and variable additional arguments" do
+ @o.rb_obj_method_arity(@obj, :five).should == -3
+ end
+
+ it "returns -N-1 when the method takes N required, variable additional, and a block argument" do
+ @o.rb_obj_method_arity(@obj, :six).should == -3
+ end
+ end
+
+ describe "rb_method_boundp" do
+ it "returns true when the given method is bound" do
+ @o.rb_method_boundp(Object, :class, true).should == true
+ @o.rb_method_boundp(Object, :class, false).should == true
+ @o.rb_method_boundp(Object, :initialize, true).should == false
+ @o.rb_method_boundp(Object, :initialize, false).should == true
+ end
+
+ it "returns false when the given method is not bound" do
+ @o.rb_method_boundp(Object, :foo, true).should == false
+ @o.rb_method_boundp(Object, :foo, false).should == false
+ end
+ end
+
+ describe "rb_to_id" do
+ it "returns a symbol representation of the object" do
+ @o.rb_to_id("foo").should == :foo
+ @o.rb_to_id(:foo).should == :foo
+ end
+ end
+
+ describe "rb_require" do
+ before :each do
+ @saved_loaded_features = $LOADED_FEATURES.dup
+ $foo = nil
+ end
+
+ after :each do
+ $foo = nil
+ $LOADED_FEATURES.replace @saved_loaded_features
+ end
+
+ it "requires a ruby file" do
+ $:.unshift File.dirname(__FILE__)
+ @o.rb_require()
+ $foo.should == 7
+ end
+ end
+
+ describe "rb_attr_get" do
+ it "gets an instance variable" do
+ o = ObjectTest.new
+ @o.rb_attr_get(o, :@foo).should == 7
+ end
+ end
+
+ describe "rb_obj_instance_variables" do
+ it "returns an array with instance variable names as symbols" do
+ o = ObjectTest.new
+ @o.rb_obj_instance_variables(o).should include(:@foo)
+ end
+ end
+
+ describe "rb_check_convert_type" do
+ it "returns the passed object and does not call the converting method if the object is the specified type" do
+ ary = [1, 2]
+ ary.should_not_receive(:to_ary)
+
+ @o.rb_check_convert_type(ary, "Array", "to_ary").should equal(ary)
+ end
+
+ it "returns the passed object and does not call the converting method if the object is a subclass of the specified type" do
+ obj = CApiObjectSpecs::SubArray.new
+ obj.should_not_receive(:to_array)
+
+ @o.rb_check_convert_type(obj, "Array", "to_array").should equal(obj)
+ end
+
+ it "returns nil if the converting method returns nil" do
+ obj = mock("rb_check_convert_type")
+ obj.should_receive(:to_array).and_return(nil)
+
+ @o.rb_check_convert_type(obj, "Array", "to_array").should be_nil
+ end
+
+ it "raises a TypeError if the converting method returns an object that is not the specified type" do
+ obj = mock("rb_check_convert_type")
+ obj.should_receive(:to_array).and_return("string")
+
+ lambda do
+ @o.rb_check_convert_type(obj, "Array", "to_array")
+ end.should raise_error(TypeError)
+ end
+ end
+
+ describe "rb_convert_type" do
+ it "returns the passed object and does not call the converting method if the object is the specified type" do
+ ary = [1, 2]
+ ary.should_not_receive(:to_ary)
+
+ @o.rb_convert_type(ary, "Array", "to_ary").should equal(ary)
+ end
+
+ it "returns the passed object and does not call the converting method if the object is a subclass of the specified type" do
+ obj = CApiObjectSpecs::SubArray.new
+ obj.should_not_receive(:to_array)
+
+ @o.rb_convert_type(obj, "Array", "to_array").should equal(obj)
+ end
+
+ it "raises a TypeError if the converting method returns nil" do
+ obj = mock("rb_convert_type")
+ obj.should_receive(:to_array).and_return(nil)
+
+ lambda do
+ @o.rb_convert_type(obj, "Array", "to_array")
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the converting method returns an object that is not the specified type" do
+ obj = mock("rb_convert_type")
+ obj.should_receive(:to_array).and_return("string")
+
+ lambda do
+ @o.rb_convert_type(obj, "Array", "to_array")
+ end.should raise_error(TypeError)
+ end
+ end
+
+ describe "rb_check_array_type" do
+ it "returns the argument if it's an Array" do
+ x = Array.new
+ @o.rb_check_array_type(x).should equal(x)
+ end
+
+ it "returns the argument if it's a kind of Array" do
+ x = AryChild.new
+ @o.rb_check_array_type(x).should equal(x)
+ end
+
+ it "returns nil when the argument does not respond to #to_ary" do
+ @o.rb_check_array_type(Object.new).should be_nil
+ end
+
+ it "sends #to_ary to the argument and returns the result if it's nil" do
+ obj = mock("to_ary")
+ obj.should_receive(:to_ary).and_return(nil)
+ @o.rb_check_array_type(obj).should be_nil
+ end
+
+ it "sends #to_ary to the argument and returns the result if it's an Array" do
+ x = Array.new
+ obj = mock("to_ary")
+ obj.should_receive(:to_ary).and_return(x)
+ @o.rb_check_array_type(obj).should equal(x)
+ end
+
+ it "sends #to_ary to the argument and returns the result if it's a kind of Array" do
+ x = AryChild.new
+ obj = mock("to_ary")
+ obj.should_receive(:to_ary).and_return(x)
+ @o.rb_check_array_type(obj).should equal(x)
+ end
+
+ it "sends #to_ary to the argument and raises TypeError if it's not a kind of Array" do
+ obj = mock("to_ary")
+ obj.should_receive(:to_ary).and_return(Object.new)
+ lambda { @o.rb_check_array_type obj }.should raise_error(TypeError)
+ end
+
+ it "does not rescue exceptions raised by #to_ary" do
+ obj = mock("to_ary")
+ obj.should_receive(:to_ary).and_raise(frozen_error_class)
+ lambda { @o.rb_check_array_type obj }.should raise_error(frozen_error_class)
+ end
+ end
+
+ describe "rb_check_string_type" do
+ it "returns the argument if it's a String" do
+ x = String.new
+ @o.rb_check_string_type(x).should equal(x)
+ end
+
+ it "returns the argument if it's a kind of String" do
+ x = StrChild.new
+ @o.rb_check_string_type(x).should equal(x)
+ end
+
+ it "returns nil when the argument does not respond to #to_str" do
+ @o.rb_check_string_type(Object.new).should be_nil
+ end
+
+ it "sends #to_str to the argument and returns the result if it's nil" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(nil)
+ @o.rb_check_string_type(obj).should be_nil
+ end
+
+ it "sends #to_str to the argument and returns the result if it's a String" do
+ x = String.new
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(x)
+ @o.rb_check_string_type(obj).should equal(x)
+ end
+
+ it "sends #to_str to the argument and returns the result if it's a kind of String" do
+ x = StrChild.new
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(x)
+ @o.rb_check_string_type(obj).should equal(x)
+ end
+
+ it "sends #to_str to the argument and raises TypeError if it's not a kind of String" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_return(Object.new)
+ lambda { @o.rb_check_string_type obj }.should raise_error(TypeError)
+ end
+
+ it "does not rescue exceptions raised by #to_str" do
+ obj = mock("to_str")
+ obj.should_receive(:to_str).and_raise(RuntimeError)
+ lambda { @o.rb_check_string_type obj }.should raise_error(RuntimeError)
+ end
+ end
+
+ describe "rb_check_to_integer" do
+ it "returns the object when passed a Fixnum" do
+ @o.rb_check_to_integer(5, "to_int").should equal(5)
+ end
+
+ it "returns the object when passed a Bignum" do
+ @o.rb_check_to_integer(bignum_value, "to_int").should == bignum_value
+ end
+
+ it "calls the converting method and returns a Fixnum value" do
+ obj = mock("rb_check_to_integer")
+ obj.should_receive(:to_integer).and_return(10)
+
+ @o.rb_check_to_integer(obj, "to_integer").should equal(10)
+ end
+
+ it "calls the converting method and returns a Bignum value" do
+ obj = mock("rb_check_to_integer")
+ obj.should_receive(:to_integer).and_return(bignum_value)
+
+ @o.rb_check_to_integer(obj, "to_integer").should == bignum_value
+ end
+
+ it "returns nil when the converting method returns nil" do
+ obj = mock("rb_check_to_integer")
+ obj.should_receive(:to_integer).and_return(nil)
+
+ @o.rb_check_to_integer(obj, "to_integer").should be_nil
+ end
+
+ it "returns nil when the converting method does not return an Integer" do
+ obj = mock("rb_check_to_integer")
+ obj.should_receive(:to_integer).and_return("string")
+
+ @o.rb_check_to_integer(obj, "to_integer").should be_nil
+ end
+ end
+
+ describe "FL_ABLE" do
+ it "returns correct boolean for type" do
+ @o.FL_ABLE(Object.new).should be_true
+ @o.FL_ABLE(true).should be_false
+ @o.FL_ABLE(nil).should be_false
+ @o.FL_ABLE(1).should be_false
+ end
+ end
+
+ describe "FL_TEST" do
+ it "returns correct status for FL_TAINT" do
+ obj = Object.new
+ @o.FL_TEST(obj, "FL_TAINT").should == 0
+ obj.taint
+ @o.FL_TEST(obj, "FL_TAINT").should_not == 0
+ end
+
+ it "returns correct status for FL_FREEZE" do
+ obj = Object.new
+ @o.FL_TEST(obj, "FL_FREEZE").should == 0
+ obj.freeze
+ @o.FL_TEST(obj, "FL_FREEZE").should_not == 0
+ end
+ end
+
+ describe "rb_inspect" do
+ it "returns a string with the inspect representation" do
+ @o.rb_inspect(nil).should == "nil"
+ @o.rb_inspect(0).should == '0'
+ @o.rb_inspect([1,2,3]).should == '[1, 2, 3]'
+ @o.rb_inspect("0").should == '"0"'
+ end
+ end
+
+ describe "rb_class_of" do
+ it "returns the class of an object" do
+ @o.rb_class_of(nil).should == NilClass
+ @o.rb_class_of(0).should == Fixnum
+ @o.rb_class_of(0.1).should == Float
+ @o.rb_class_of(ObjectTest.new).should == ObjectTest
+ end
+
+ it "returns the singleton class if it exists" do
+ o = ObjectTest.new
+ @o.rb_class_of(o).should equal ObjectTest
+ s = o.singleton_class
+ @o.rb_class_of(o).should equal s
+ end
+ end
+
+ describe "rb_obj_classname" do
+ it "returns the class name of an object" do
+ @o.rb_obj_classname(nil).should == 'NilClass'
+ @o.rb_obj_classname(0).should == Fixnum.to_s
+ @o.rb_obj_classname(0.1).should == 'Float'
+ @o.rb_obj_classname(ObjectTest.new).should == 'ObjectTest'
+ end
+ end
+
+ describe "rb_type" do
+ it "returns the type constant for the object" do
+ class DescArray < Array
+ end
+ @o.rb_is_type_nil(nil).should == true
+ @o.rb_is_type_object([]).should == false
+ @o.rb_is_type_object(ObjectTest.new).should == true
+ @o.rb_is_type_array([]).should == true
+ @o.rb_is_type_array(DescArray.new).should == true
+ @o.rb_is_type_module(ObjectTest).should == false
+ @o.rb_is_type_class(ObjectTest).should == true
+ @o.rb_is_type_data(Time.now).should == true
+ end
+ end
+
+ describe "rb_type_p" do
+ it "returns whether object is of the given type" do
+ class DescArray < Array
+ end
+ @o.rb_is_rb_type_p_nil(nil).should == true
+ @o.rb_is_rb_type_p_object([]).should == false
+ @o.rb_is_rb_type_p_object(ObjectTest.new).should == true
+ @o.rb_is_rb_type_p_array([]).should == true
+ @o.rb_is_rb_type_p_array(DescArray.new).should == true
+ @o.rb_is_rb_type_p_module(ObjectTest).should == false
+ @o.rb_is_rb_type_p_class(ObjectTest).should == true
+ @o.rb_is_rb_type_p_data(Time.now).should == true
+ end
+ end
+
+ describe "BUILTIN_TYPE" do
+ it "returns the type constant for the object" do
+ class DescArray < Array
+ end
+ @o.rb_is_builtin_type_object([]).should == false
+ @o.rb_is_builtin_type_object(ObjectTest.new).should == true
+ @o.rb_is_builtin_type_array([]).should == true
+ @o.rb_is_builtin_type_array(DescArray.new).should == true
+ @o.rb_is_builtin_type_module(ObjectTest).should == false
+ @o.rb_is_builtin_type_class(ObjectTest).should == true
+ @o.rb_is_builtin_type_data(Time.now).should == true
+ end
+ end
+
+ describe "RTEST" do
+ it "returns C false if passed Qfalse" do
+ @o.RTEST(false).should be_false
+ end
+
+ it "returns C false if passed Qnil" do
+ @o.RTEST(nil).should be_false
+ end
+
+ it "returns C true if passed Qtrue" do
+ @o.RTEST(true).should be_true
+ end
+
+ it "returns C true if passed a Symbol" do
+ @o.RTEST(:test).should be_true
+ end
+
+ it "returns C true if passed an Object" do
+ @o.RTEST(Object.new).should be_true
+ end
+ end
+
+ describe "rb_special_const_p" do
+ it "returns true if passed Qfalse" do
+ @o.rb_special_const_p(false).should be_true
+ end
+
+ it "returns true if passed Qtrue" do
+ @o.rb_special_const_p(true).should be_true
+ end
+
+ it "returns true if passed Qnil" do
+ @o.rb_special_const_p(nil).should be_true
+ end
+
+ it "returns true if passed a Symbol" do
+ @o.rb_special_const_p(:test).should be_true
+ end
+
+ it "returns true if passed a Fixnum" do
+ @o.rb_special_const_p(10).should be_true
+ end
+
+ it "returns false if passed an Object" do
+ @o.rb_special_const_p(Object.new).should be_false
+ end
+ end
+
+ describe "rb_extend_object" do
+ it "adds the module's instance methods to the object" do
+ module CApiObjectSpecs::Extend
+ def reach
+ :extended
+ end
+ end
+
+ obj = mock("extended object")
+ @o.rb_extend_object(obj, CApiObjectSpecs::Extend)
+ obj.reach.should == :extended
+ end
+ end
+
+ describe "OBJ_TAINT" do
+ it "taints the object" do
+ obj = mock("tainted")
+ @o.OBJ_TAINT(obj)
+ obj.tainted?.should be_true
+ end
+ end
+
+ describe "OBJ_TAINTED" do
+ it "returns C true if the object is tainted" do
+ obj = mock("tainted")
+ obj.taint
+ @o.OBJ_TAINTED(obj).should be_true
+ end
+
+ it "returns C false if the object is not tainted" do
+ obj = mock("untainted")
+ @o.OBJ_TAINTED(obj).should be_false
+ end
+ end
+
+ describe "OBJ_INFECT" do
+ it "does not taint the first argument if the second argument is not tainted" do
+ host = mock("host")
+ source = mock("source")
+ @o.OBJ_INFECT(host, source)
+ host.tainted?.should be_false
+ end
+
+ it "taints the first argument if the second argument is tainted" do
+ host = mock("host")
+ source = mock("source").taint
+ @o.OBJ_INFECT(host, source)
+ host.tainted?.should be_true
+ end
+
+ it "does not untrust the first argument if the second argument is trusted" do
+ host = mock("host")
+ source = mock("source")
+ @o.OBJ_INFECT(host, source)
+ host.untrusted?.should be_false
+ end
+
+ it "untrusts the first argument if the second argument is untrusted" do
+ host = mock("host")
+ source = mock("source").untrust
+ @o.OBJ_INFECT(host, source)
+ host.untrusted?.should be_true
+ end
+
+ it "propagates both taint and distrust" do
+ host = mock("host")
+ source = mock("source").taint.untrust
+ @o.OBJ_INFECT(host, source)
+ host.tainted?.should be_true
+ host.untrusted?.should be_true
+ end
+ end
+
+ describe "rb_obj_freeze" do
+ it "freezes the object passed to it" do
+ obj = ""
+ @o.rb_obj_freeze(obj).should == obj
+ obj.frozen?.should be_true
+ end
+ end
+
+ describe "rb_obj_instance_eval" do
+ it "evaluates the block in the object context, that includes private methods" do
+ obj = ObjectTest
+ lambda do
+ @o.rb_obj_instance_eval(obj) { include Kernel }
+ end.should_not raise_error(NoMethodError)
+ end
+ end
+
+ describe "rb_obj_frozen_p" do
+ it "returns true if object passed to it is frozen" do
+ obj = ""
+ obj.freeze
+ @o.rb_obj_frozen_p(obj).should == true
+ end
+
+ it "returns false if object passed to it is not frozen" do
+ obj = ""
+ @o.rb_obj_frozen_p(obj).should == false
+ end
+ end
+
+ describe "rb_obj_taint" do
+ it "marks the object passed as tainted" do
+ obj = ""
+ obj.tainted?.should == false
+ @o.rb_obj_taint(obj)
+ obj.tainted?.should == true
+ end
+
+ it "raises a #{frozen_error_class} if the object passed is frozen" do
+ lambda { @o.rb_obj_taint("".freeze) }.should raise_error(frozen_error_class)
+ end
+ end
+
+ describe "rb_check_frozen" do
+ it "raises a #{frozen_error_class} if the obj is frozen" do
+ lambda { @o.rb_check_frozen("".freeze) }.should raise_error(frozen_error_class)
+ end
+
+ it "does nothing when object isn't frozen" do
+ obj = ""
+ lambda { @o.rb_check_frozen(obj) }.should_not raise_error(TypeError)
+ end
+ end
+
+ describe "rb_any_to_s" do
+ it "converts an Integer to string" do
+ obj = 1
+ i = @o.rb_any_to_s(obj)
+ i.should be_kind_of(String)
+ end
+
+ it "converts an Object to string" do
+ obj = Object.new
+ i = @o.rb_any_to_s(obj)
+ i.should be_kind_of(String)
+ end
+ end
+
+ describe "rb_to_int" do
+ it "returns self when called on an Integer" do
+ @o.rb_to_int(5).should == 5
+ end
+
+ it "returns self when called on a Bignum" do
+ @o.rb_to_int(bignum_value).should == bignum_value
+ end
+
+ it "calls #to_int to convert and object to an integer" do
+ x = mock("to_int")
+ x.should_receive(:to_int).and_return(5)
+ @o.rb_to_int(x).should == 5
+ end
+
+ it "converts a Float to an Integer by truncation" do
+ @o.rb_to_int(1.35).should == 1
+ end
+
+ it "raises a TypeError if #to_int does not return an Integer" do
+ x = mock("to_int")
+ x.should_receive(:to_int).and_return("5")
+ lambda { @o.rb_to_int(x) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if called with nil" do
+ lambda { @o.rb_to_int(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if called with true" do
+ lambda { @o.rb_to_int(true) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if called with false" do
+ lambda { @o.rb_to_int(false) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if called with a String" do
+ lambda { @o.rb_to_int("1") }.should raise_error(TypeError)
+ end
+ end
+
+ describe "rb_equal" do
+ it "returns true if the arguments are the same exact object" do
+ s = "hello"
+ @o.rb_equal(s, s).should be_true
+ end
+
+ it "calls == to check equality and coerces to true/false" do
+ m = mock("string")
+ m.should_receive(:==).and_return(8)
+ @o.rb_equal(m, "hello").should be_true
+
+ m2 = mock("string")
+ m2.should_receive(:==).and_return(nil)
+ @o.rb_equal(m2, "hello").should be_false
+ end
+ end
+
+ describe "rb_class_inherited_p" do
+
+ it "returns true if mod equals arg" do
+ @o.rb_class_inherited_p(Array, Array).should be_true
+ end
+
+ it "returns true if mod is a subclass of arg" do
+ @o.rb_class_inherited_p(Array, Object).should be_true
+ end
+
+ it "returns nil if mod is not a subclass of arg" do
+ @o.rb_class_inherited_p(Array, Hash).should be_nil
+ end
+
+ it "raises a TypeError if arg is no class or module" do
+ lambda{
+ @o.rb_class_inherited_p(1, 2)
+ }.should raise_error(TypeError)
+ end
+
+ end
+
+ describe "instance variable access" do
+ before do
+ @test = ObjectTest.new
+ end
+
+ describe "rb_iv_get" do
+ it "returns the instance variable on an object" do
+ @o.rb_iv_get(@test, "@foo").should == @test.instance_eval { @foo }
+ end
+
+ it "returns nil if the instance variable has not been initialized" do
+ @o.rb_iv_get(@test, "@bar").should == nil
+ end
+ end
+
+ describe "rb_iv_set" do
+ it "sets and returns the instance variable on an object" do
+ @o.rb_iv_set(@test, "@foo", 42).should == 42
+ @test.instance_eval { @foo }.should == 42
+ end
+
+ it "sets and returns the instance variable with a bare name" do
+ @o.rb_iv_set(@test, "foo", 42).should == 42
+ @o.rb_iv_get(@test, "foo").should == 42
+ @test.instance_eval { @foo }.should == 7
+ end
+ end
+
+ describe "rb_ivar_get" do
+ it "returns the instance variable on an object" do
+ @o.rb_ivar_get(@test, :@foo).should == @test.instance_eval { @foo }
+ end
+
+ it "returns nil if the instance variable has not been initialized" do
+ @o.rb_ivar_get(@test, :@bar).should == nil
+ end
+
+ it "returns nil if the instance variable has not been initialized and is not a valid Ruby name" do
+ @o.rb_ivar_get(@test, :bar).should == nil
+ end
+
+ it 'returns the instance variable when it is not a valid Ruby name' do
+ @o.rb_ivar_set(@test, :foo, 27)
+ @o.rb_ivar_get(@test, :foo).should == 27
+ end
+ end
+
+ describe "rb_ivar_set" do
+ it "sets and returns the instance variable on an object" do
+ @o.rb_ivar_set(@test, :@foo, 42).should == 42
+ @test.instance_eval { @foo }.should == 42
+ end
+
+ it "sets and returns the instance variable on an object" do
+ @o.rb_ivar_set(@test, :@foo, 42).should == 42
+ @test.instance_eval { @foo }.should == 42
+ end
+
+ it 'sets and returns the instance variable when it is not a valid Ruby name' do
+ @o.rb_ivar_set(@test, :foo, 27).should == 27
+ end
+ end
+
+ describe "rb_ivar_defined" do
+ it "returns true if the instance variable is defined" do
+ @o.rb_ivar_defined(@test, :@foo).should == true
+ end
+
+ it "returns false if the instance variable is not defined" do
+ @o.rb_ivar_defined(@test, :@bar).should == false
+ end
+
+ it "does not throw an error if the instance variable is not a valid Ruby name" do
+ @o.rb_ivar_defined(@test, :bar).should == false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/proc_spec.rb b/spec/ruby/optional/capi/proc_spec.rb
new file mode 100644
index 0000000000..94c9276c09
--- /dev/null
+++ b/spec/ruby/optional/capi/proc_spec.rb
@@ -0,0 +1,112 @@
+require_relative 'spec_helper'
+require_relative 'fixtures/proc'
+
+load_extension("proc")
+
+describe "C-API Proc function" do
+ before :each do
+ @p = CApiProcSpecs.new
+ @prc = @p.rb_proc_new
+ end
+
+ describe "rb_proc_new" do
+ it "returns a new valid Proc" do
+ @prc.kind_of?(Proc).should == true
+ end
+
+ it "calls the C function wrapped by the Proc instance when sent #call" do
+ @prc.call(:foo_bar).should == ":foo_bar"
+ @prc.call([:foo, :bar]).should == "[:foo, :bar]"
+ end
+
+ it "calls the C function wrapped by the Proc instance when sent #[]" do
+ @prc[:foo_bar].should == ":foo_bar"
+ @prc[[:foo, :bar]].should == "[:foo, :bar]"
+ end
+
+ it "returns a Proc instance correctly described in #inspect without source location" do
+ @prc.inspect.should =~ /^#<Proc:([^ :@]*?)>$/
+ end
+
+ it "returns a Proc instance with #arity == -1" do
+ @prc.arity.should == -1
+ end
+
+ it "shouldn't be equal to another one" do
+ @prc.should_not == @p.rb_proc_new
+ end
+
+ it "returns a Proc instance with #source_location == nil" do
+ @prc.source_location.should == nil
+ end
+ end
+
+ describe "rb_proc_arity" do
+ it "returns the correct arity" do
+ prc = Proc.new {|a,b,c|}
+ @p.rb_proc_arity(prc).should == 3
+ end
+ end
+
+ describe "rb_proc_call" do
+ it "calls the Proc" do
+ prc = Proc.new {|a,b| a * b }
+ @p.rb_proc_call(prc, [6, 7]).should == 42
+ end
+ end
+end
+
+describe "C-API when calling Proc.new from a C function" do
+ before :each do
+ @p = CApiProcSpecs.new
+ end
+
+ # In the scenarios below: X -> Y means execution context X called to Y.
+ # For example: Ruby -> C means a Ruby code called a C function.
+ #
+ # X -> Y <- X -> Z means execution context X called Y which returned to X,
+ # then X called Z.
+ # For example: C -> Ruby <- C -> Ruby means a C function called into Ruby
+ # code which returned to C, then C called into Ruby code again.
+
+ # Ruby -> C -> rb_funcall(Proc.new)
+ it "returns the Proc passed by the Ruby code calling the C function" do
+ prc = @p.rb_Proc_new(0) { :called }
+ prc.call.should == :called
+ end
+
+ # Ruby -> C -> Ruby <- C -> rb_funcall(Proc.new)
+ it "returns the Proc passed to the Ruby method when the C function calls other Ruby methods before calling Proc.new" do
+ prc = @p.rb_Proc_new(1) { :called }
+ prc.call.should == :called
+ end
+
+ # Ruby -> C -> Ruby -> Proc.new
+ it "raises an ArgumentError when the C function calls a Ruby method that calls Proc.new" do
+ def @p.Proc_new() Proc.new end
+ lambda { @p.rb_Proc_new(2) { :called } }.should raise_error(ArgumentError)
+ end
+
+ # Ruby -> C -> Ruby -> C -> rb_funcall(Proc.new)
+ it "raises an ArgumentError when the C function calls a Ruby method and that method calls a C function that calls Proc.new" do
+ def @p.redispatch() rb_Proc_new(0) end
+ lambda { @p.rb_Proc_new(3) { :called } }.should raise_error(ArgumentError)
+ end
+
+ # Ruby -> C -> Ruby -> C (with new block) -> rb_funcall(Proc.new)
+ it "returns the most recent Proc passed when the Ruby method called the C function" do
+ prc = @p.rb_Proc_new(4) { :called }
+ prc.call.should == :calling_with_block
+ end
+
+ # Ruby -> C -> Ruby -> C (with new block) <- Ruby <- C -> # rb_funcall(Proc.new)
+ it "returns the Proc passed from the original Ruby call to the C function" do
+ prc = @p.rb_Proc_new(5) { :called }
+ prc.call.should == :called
+ end
+
+ # Ruby -> C -> Ruby -> block_given?
+ it "returns false from block_given? in a Ruby method called by the C function" do
+ @p.rb_Proc_new(6).should be_false
+ end
+end
diff --git a/spec/ruby/optional/capi/rake_helper.rb b/spec/ruby/optional/capi/rake_helper.rb
new file mode 100644
index 0000000000..c13f1189c5
--- /dev/null
+++ b/spec/ruby/optional/capi/rake_helper.rb
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rake/clean'
+
+input = "#{$cwd}/#{$ext_name}.c"
+common = "-I shotgun/lib/subtend -g #{input}"
+
+case PLATFORM
+when /darwin/
+ output = "#{$cwd}/#{$ext_name}.bundle"
+ build_cmd = "cc -bundle -undefined suppress -flat_namespace #{common} -o #{output}"
+else
+ output = "#{$cwd}/#{$ext_name}.so"
+ build_cmd = "cc -shared #{common} -o #{output}"
+end
+
+CLOBBER.include(output)
+
+task default: [output]
+
+file output => [input] do
+ sh build_cmd
+end
diff --git a/spec/ruby/optional/capi/range_spec.rb b/spec/ruby/optional/capi/range_spec.rb
new file mode 100644
index 0000000000..46df4eec99
--- /dev/null
+++ b/spec/ruby/optional/capi/range_spec.rb
@@ -0,0 +1,95 @@
+require_relative 'spec_helper'
+
+load_extension("range")
+
+describe "C-API Range function" do
+ before :each do
+ @s = CApiRangeSpecs.new
+ end
+
+ describe "rb_range_new" do
+ it "constructs a range using the given start and end" do
+ range = @s.rb_range_new('a', 'c')
+ range.should == ('a'..'c')
+
+ range.first.should == 'a'
+ range.last.should == 'c'
+ end
+
+ it "includes the end object when the third parameter is omitted or false" do
+ @s.rb_range_new('a', 'c').to_a.should == ['a', 'b', 'c']
+ @s.rb_range_new(1, 3).to_a.should == [1, 2, 3]
+
+ @s.rb_range_new('a', 'c', false).to_a.should == ['a', 'b', 'c']
+ @s.rb_range_new(1, 3, false).to_a.should == [1, 2, 3]
+
+ @s.rb_range_new('a', 'c', true).to_a.should == ['a', 'b']
+ @s.rb_range_new(1, 3, 1).to_a.should == [1, 2]
+
+ @s.rb_range_new(1, 3, mock('[1,2]')).to_a.should == [1, 2]
+ @s.rb_range_new(1, 3, :test).to_a.should == [1, 2]
+ end
+
+ it "raises an ArgumentError when the given start and end can't be compared by using #<=>" do
+ lambda { @s.rb_range_new(1, mock('x')) }.should raise_error(ArgumentError)
+ lambda { @s.rb_range_new(mock('x'), mock('y')) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "rb_range_values" do
+ it "stores the range properties" do
+ beg, fin, excl = @s.rb_range_values(10..20)
+ beg.should == 10
+ fin.should == 20
+ excl.should be_false
+ end
+
+ it "stores the range properties of non-Range object" do
+ range_like = mock('range')
+
+ def range_like.begin
+ 10
+ end
+
+ def range_like.end
+ 20
+ end
+
+ def range_like.exclude_end?
+ false
+ end
+
+ beg, fin, excl = @s.rb_range_values(range_like)
+ beg.should == 10
+ fin.should == 20
+ excl.should be_false
+ end
+ end
+
+ describe "rb_range_beg_len" do
+ it "returns correct begin, length and result" do
+ r = 2..5
+ begp, lenp, result = @s.rb_range_beg_len(r, 0, 0, 10, 0)
+ result.should be_true
+ begp.should == 2
+ lenp.should == 4
+ end
+
+ it "returns nil when not in range" do
+ r = 2..5
+ begp, lenp, result = @s.rb_range_beg_len(r, 0, 0, 1, 0)
+ result.should be_nil
+ end
+
+ it "raises a RangeError when not in range and err is 1" do
+ r = -5..-1
+ lambda { @s.rb_range_beg_len(r, 0, 0, 1, 1) }.should raise_error(RangeError)
+ end
+
+ it "returns nil when not in range and err is 0" do
+ r = -5..-1
+ begp, lenp, result = @s.rb_range_beg_len(r, 0, 0, 1, 0)
+ result.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/rational_spec.rb b/spec/ruby/optional/capi/rational_spec.rb
new file mode 100644
index 0000000000..1c241ac48e
--- /dev/null
+++ b/spec/ruby/optional/capi/rational_spec.rb
@@ -0,0 +1,57 @@
+require_relative 'spec_helper'
+
+load_extension("rational")
+
+describe :rb_Rational, shared: true do
+ it "creates a new Rational with numerator and denominator" do
+ @r.send(@method, 1, 2).should == Rational(1, 2)
+ end
+end
+
+describe :rb_rational_new, shared: true do
+ it "creates a normalized Rational" do
+ r = @r.send(@method, 10, 4)
+ r.numerator.should == 5
+ r.denominator.should == 2
+ end
+end
+
+describe "CApiRationalSpecs" do
+ before :each do
+ @r = CApiRationalSpecs.new
+ end
+
+ describe "rb_Rational" do
+ it_behaves_like :rb_Rational, :rb_Rational
+ end
+
+ describe "rb_Rational2" do
+ it_behaves_like :rb_Rational, :rb_Rational2
+ end
+
+ describe "rb_Rational1" do
+ it "creates a new Rational with numerator and denominator of 1" do
+ @r.rb_Rational1(5).should == Rational(5, 1)
+ end
+ end
+
+ describe "rb_rational_new" do
+ it_behaves_like :rb_rational_new, :rb_rational_new
+ end
+
+ describe "rb_rational_new2" do
+ it_behaves_like :rb_rational_new, :rb_rational_new2
+ end
+
+ describe "rb_rational_num" do
+ it "returns the numerator of a Rational" do
+ @r.rb_rational_num(Rational(7, 2)).should == 7
+ end
+ end
+
+ describe "rb_rational_den" do
+ it "returns the denominator of a Rational" do
+ @r.rb_rational_den(Rational(7, 2)).should == 2
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/regexp_spec.rb b/spec/ruby/optional/capi/regexp_spec.rb
new file mode 100644
index 0000000000..52aae6bb01
--- /dev/null
+++ b/spec/ruby/optional/capi/regexp_spec.rb
@@ -0,0 +1,71 @@
+require_relative 'spec_helper'
+
+load_extension("regexp")
+
+describe "C-API Regexp function" do
+ before :each do
+ @p = CApiRegexpSpecs.new
+ end
+
+ describe "rb_reg_new" do
+ it "returns a new valid Regexp" do
+ my_re = @p.a_re
+ my_re.kind_of?(Regexp).should == true
+ ('1a' =~ my_re).should == 1
+ ('1b' =~ my_re).should == nil
+ my_re.source.should == 'a'
+ end
+ end
+
+ describe "rb_reg_nth_match" do
+ it "returns a the appropriate match data entry" do
+ @p.a_re_1st_match(/([ab])/.match("a")).should == 'a'
+ @p.a_re_1st_match(/([ab])/.match("b")).should == 'b'
+ @p.a_re_1st_match(/[ab]/.match("a")).should == nil
+ @p.a_re_1st_match(/[ab]/.match("c")).should == nil
+ end
+ end
+
+ describe "rb_reg_options" do
+ it "returns the options used to create the regexp" do
+ @p.rb_reg_options(/42/im).should == //im.options
+ @p.rb_reg_options(/42/i).should == //i.options
+ @p.rb_reg_options(/42/m).should == //m.options
+ end
+ end
+
+ describe "rb_reg_regcomp" do
+ it "creates a valid regexp from a string" do
+ regexp = /\b([A-Z0-9._%+-]+)\.{2,4}/
+ @p.rb_reg_regcomp(regexp.source).should == regexp
+ end
+ end
+
+ it "allows matching in C, properly setting the back references" do
+ mail_regexp = /\b([A-Z0-9._%+-]+)@([A-Z0-9.-]+\.[A-Z]{2,4})\b/i
+ name = "john.doe"
+ domain = "example.com"
+ @p.match(mail_regexp, "#{name}@#{domain}")
+ $1.should == name
+ $2.should == domain
+ end
+
+ describe "rb_reg_match" do
+ it "returns the matched position or nil" do
+ @p.rb_reg_match(/a/, 'ab').should == 0
+ @p.rb_reg_match(/b/, 'ab').should == 1
+ @p.rb_reg_match(/c/, 'ab').should == nil
+ end
+ end
+
+ describe "rb_backref_get" do
+ it "returns the last MatchData" do
+ md = /a/.match('ab')
+ @p.rb_backref_get.should == md
+ md = /b/.match('ab')
+ @p.rb_backref_get.should == md
+ md = /c/.match('ab')
+ @p.rb_backref_get.should == md
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/spec_helper.rb b/spec/ruby/optional/capi/spec_helper.rb
new file mode 100644
index 0000000000..eda4964b69
--- /dev/null
+++ b/spec/ruby/optional/capi/spec_helper.rb
@@ -0,0 +1,132 @@
+# Require the main spec_helper.rb at the end to let `ruby ...spec.rb` work
+
+# MRI magic to use built but not installed ruby
+$extmk = false
+
+require 'rbconfig'
+
+OBJDIR ||= File.expand_path("../../../ext/#{RUBY_ENGINE}/#{RUBY_VERSION}", __FILE__)
+
+def object_path
+ mkdir_p(OBJDIR)
+ OBJDIR
+end
+
+def compile_extension(name)
+ debug = false
+ run_mkmf_in_process = RUBY_ENGINE == 'truffleruby'
+
+ core_ext_dir = File.expand_path("../ext", __FILE__)
+
+ spec_caller_location = caller_locations.find { |c| c.path.end_with?('_spec.rb') }
+ spec_file_path = spec_caller_location.path
+ spec_ext_dir = File.expand_path("../ext", spec_file_path)
+
+ ext = "#{name}_spec"
+ lib = "#{object_path}/#{ext}.#{RbConfig::CONFIG['DLEXT']}"
+ ruby_header = "#{RbConfig::CONFIG['rubyhdrdir']}/ruby.h"
+
+ if RbConfig::CONFIG["ENABLE_SHARED"] == "yes"
+ if PlatformGuard.windows?
+ libruby_so = "#{RbConfig::CONFIG['bindir']}/#{RbConfig::CONFIG['LIBRUBY_SO']}"
+ else
+ libruby_so = "#{RbConfig::CONFIG['libdir']}/#{RbConfig::CONFIG['LIBRUBY_SO']}"
+ end
+ end
+
+ begin
+ mtime = File.mtime(lib)
+ rescue Errno::ENOENT
+ # not found, then compile
+ else
+ case # if lib is older than headers, source or libruby, then recompile
+ when mtime <= File.mtime("#{core_ext_dir}/rubyspec.h")
+ when mtime <= File.mtime("#{spec_ext_dir}/#{ext}.c")
+ when mtime <= File.mtime(ruby_header)
+ when libruby_so && mtime <= File.mtime(libruby_so)
+ else
+ return lib # up-to-date
+ end
+ end
+
+ # Copy needed source files to tmpdir
+ tmpdir = tmp("cext_#{name}")
+ Dir.mkdir(tmpdir)
+ begin
+ ["#{core_ext_dir}/rubyspec.h", "#{spec_ext_dir}/#{ext}.c"].each do |file|
+ cp file, "#{tmpdir}/#{File.basename(file)}"
+ end
+
+ Dir.chdir(tmpdir) do
+ if run_mkmf_in_process
+ required = require 'mkmf'
+ # Reinitialize mkmf if already required
+ init_mkmf unless required
+ create_makefile(ext, tmpdir)
+ else
+ File.write("extconf.rb", "require 'mkmf'\n" +
+ "$ruby = ENV.values_at('RUBY_EXE', 'RUBY_FLAGS').join(' ')\n" +
+ # MRI magic to consider building non-bundled extensions
+ "$extout = nil\n" +
+ "create_makefile(#{ext.inspect})\n")
+ output = ruby_exe("extconf.rb")
+ raise "extconf failed:\n#{output}" unless $?.success?
+ $stderr.puts output if debug
+ end
+
+ # Do not capture stderr as we want to show compiler warnings
+ make, opts = setup_make
+ output = IO.popen([make, "V=1", "DESTDIR=", opts], &:read)
+ raise "#{make} failed:\n#{output}" unless $?.success?
+ $stderr.puts output if debug
+
+ cp File.basename(lib), lib
+ end
+ ensure
+ rm_r tmpdir
+ end
+
+ File.chmod(0755, lib)
+ lib
+end
+
+def setup_make
+ make = ENV['MAKE']
+ make ||= (RbConfig::CONFIG['host_os'].include?("mswin") ? "nmake" : "make")
+ make_flags = ENV["MAKEFLAGS"] || ''
+
+ # suppress logo of nmake.exe to stderr
+ if File.basename(make, ".*").downcase == "nmake" and !make_flags.include?("l")
+ ENV["MAKEFLAGS"] = "l#{make_flags}"
+ end
+
+ opts = {}
+ if /(?:\A|\s)--jobserver-(?:auth|fds)=(\d+),(\d+)/ =~ make_flags
+ begin
+ r = IO.for_fd($1.to_i(10), "rb", autoclose: false)
+ w = IO.for_fd($2.to_i(10), "wb", autoclose: false)
+ rescue Errno::EBADF
+ else
+ opts[r] = r
+ opts[w] = w
+ end
+ end
+
+ [make, opts]
+end
+
+def load_extension(name)
+ require compile_extension(name)
+rescue LoadError => e
+ if %r{/usr/sbin/execerror ruby "\(ld 3 1 main ([/a-zA-Z0-9_\-.]+_spec\.so)"} =~ e.message
+ system('/usr/sbin/execerror', "#{RbConfig::CONFIG["bindir"]}/ruby", "(ld 3 1 main #{$1}")
+ end
+ raise
+end
+
+# Constants
+CAPI_SIZEOF_LONG = [0].pack('l!').size
+
+# Require the main spec_helper.rb only here so load_extension() is defined
+# when running specs with `ruby ...spec.rb`
+require_relative '../../spec_helper'
diff --git a/spec/ruby/optional/capi/st_spec.rb b/spec/ruby/optional/capi/st_spec.rb
new file mode 100644
index 0000000000..8ca2950d83
--- /dev/null
+++ b/spec/ruby/optional/capi/st_spec.rb
@@ -0,0 +1,41 @@
+# encoding: utf-8
+require_relative 'spec_helper'
+
+load_extension('st')
+
+describe "st hash table function" do
+ before :each do
+ @s = CApiStSpecs.new
+ end
+
+ describe "st_init_numtable" do
+ it "initializes without error" do
+ @s.st_init_numtable.should == 0
+ end
+ end
+
+ describe "st_init_numtable_with_size" do
+ it "initializes without error" do
+ @s.st_init_numtable_with_size.should == 0
+ end
+ end
+
+ describe "st_insert" do
+ it "returns size 1 after insert" do
+ @s.st_insert.should == 1
+ end
+ end
+
+ describe "st_foreach" do
+ it "iterates over each pair of key and value" do
+ @s.st_foreach.should == 7
+ end
+ end
+
+ describe "st_lookup" do
+ it "returns the expected value" do
+ @s.st_lookup.should == 42
+ end
+ end
+
+end
diff --git a/spec/ruby/optional/capi/string_spec.rb b/spec/ruby/optional/capi/string_spec.rb
new file mode 100644
index 0000000000..2344dd37d7
--- /dev/null
+++ b/spec/ruby/optional/capi/string_spec.rb
@@ -0,0 +1,887 @@
+# encoding: utf-8
+require_relative 'spec_helper'
+require_relative '../../shared/string/times'
+
+load_extension('string')
+
+describe :rb_str_new2, shared: true do
+ it "returns a new string object calling strlen on the passed C string" do
+ # Hardcoded to pass const char * = "hello\0invisible"
+ @s.send(@method, "hello\0not used").should == "hello"
+ end
+
+ it "encodes the string with ASCII_8BIT" do
+ @s.send(@method, "hello").encoding.should == Encoding::ASCII_8BIT
+ end
+end
+
+describe "C-API String function" do
+ before :each do
+ @s = CApiStringSpecs.new
+ end
+
+ class ValidTostrTest
+ def to_str
+ "ruby"
+ end
+ end
+
+ class InvalidTostrTest
+ def to_str
+ []
+ end
+ end
+
+ describe "rb_str_set_len" do
+ before :each do
+ # Make a completely new copy of the string
+ # for every example (#dup doesn't cut it).
+ @str = "abcdefghij"[0..-1]
+ end
+
+ it "reduces the size of the string" do
+ @s.rb_str_set_len(@str, 5).should == "abcde"
+ end
+
+ it "inserts a NULL byte at the length" do
+ @s.rb_str_set_len(@str, 5).should == "abcde"
+ @s.rb_str_set_len(@str, 8).should == "abcde\x00gh"
+ end
+
+ it "updates the byte size and character size" do
+ @s.rb_str_set_len(@str, 4)
+ @str.bytesize.should == 4
+ @str.size.should == 4
+ @str.should == "abcd"
+ end
+
+ it "updates the string's attributes visible in C code" do
+ @s.rb_str_set_len_RSTRING_LEN(@str, 4).should == 4
+ end
+
+ it "can reveal characters written from C with RSTRING_PTR" do
+ @s.rb_str_set_len(@str, 1)
+ @str.should == "a"
+
+ @str.force_encoding(Encoding::UTF_8)
+ @s.RSTRING_PTR_set(@str, 1, 'B'.ord)
+ @s.RSTRING_PTR_set(@str, 2, 'C'.ord)
+ @s.rb_str_set_len(@str, 3)
+
+ @str.bytesize.should == 3
+ @str.should == "aBC"
+ 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)
+ buf.should == ""
+ buf.bytesize.should == 0
+ buf.size.should == 0
+ @s.RSTRING_LEN(buf).should == 0
+ end
+
+ it "returns a string with the given capacity" do
+ buf = @s.rb_str_buf_new(256, nil)
+ @s.rb_str_capacity(buf).should == 256
+ end
+
+ it "returns a string that can be appended to" do
+ str = @s.rb_str_buf_new(10, "defg")
+ str << "abcde"
+ str.should == "abcde"
+ end
+
+ it "returns a string that can be concatenated to another string" do
+ str = @s.rb_str_buf_new(10, "defg")
+ ("abcde" + str).should == "abcde"
+ end
+
+ it "returns a string whose bytes can be accessed by RSTRING_PTR" do
+ str = @s.rb_str_buf_new(10, "abcdefghi")
+ @s.rb_str_new(str, 10).should == "abcdefghi\x00"
+ end
+
+ it "returns a string that can be modified by rb_str_set_len" do
+ str = @s.rb_str_buf_new(10, "abcdef")
+ @s.rb_str_set_len(str, 4)
+ str.should == "abcd"
+
+ @s.rb_str_set_len(str, 8)
+ str[0, 6].should == "abcd\x00f"
+ @s.RSTRING_LEN(str).should == 8
+ end
+
+ it "can be used as a general buffer and reveal characters with rb_str_set_len" do
+ str = @s.rb_str_buf_new(10, "abcdef")
+
+ @s.RSTRING_PTR_set(str, 0, 195)
+ @s.RSTRING_PTR_set(str, 1, 169)
+ @s.rb_str_set_len(str, 2)
+
+ str.force_encoding(Encoding::UTF_8)
+ str.bytesize.should == 2
+ str.size.should == 1
+ str.should == "é"
+ end
+ end
+
+ describe "rb_str_buf_new2" do
+ it "returns a new string object calling strlen on the passed C string" do
+ # Hardcoded to pass const char * = "hello\0invisible"
+ @s.rb_str_buf_new2.should == "hello"
+ end
+ end
+
+ describe "rb_str_new" do
+ it "creates a new String with ASCII-8BIT Encoding" do
+ @s.rb_str_new("", 0).encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "returns a new string object from a char buffer of len characters" do
+ @s.rb_str_new("hello", 3).should == "hel"
+ end
+
+ it "returns an empty string if len is 0" do
+ @s.rb_str_new("hello", 0).should == ""
+ end
+
+ it "copy length bytes and does not stop at the first \\0 byte" do
+ @s.rb_str_new("he\x00llo", 6).should == "he\x00llo"
+ @s.rb_str_new_native("he\x00llo", 6).should == "he\x00llo"
+ end
+
+ it "returns a string from an offset char buffer" do
+ @s.rb_str_new_offset("hello", 1, 3).should == "ell"
+ end
+ end
+
+ describe "rb_str_new2" do
+ it_behaves_like :rb_str_new2, :rb_str_new2
+ end
+
+ describe "rb_str_new_cstr" do
+ it_behaves_like :rb_str_new2, :rb_str_new_cstr
+ end
+
+ describe "rb_usascii_str_new" do
+ it "creates a new String with US-ASCII Encoding from a char buffer of len characters" do
+ str = "abc".force_encoding("us-ascii")
+ result = @s.rb_usascii_str_new("abcdef", 3)
+ result.should == str
+ result.encoding.should == Encoding::US_ASCII
+ end
+ end
+
+ describe "rb_usascii_str_new_cstr" do
+ it "creates a new String with US-ASCII Encoding" do
+ str = "abc".force_encoding("us-ascii")
+ result = @s.rb_usascii_str_new_cstr("abc")
+ result.should == str
+ result.encoding.should == Encoding::US_ASCII
+ end
+ end
+
+ describe "rb_str_encode" do
+ it "returns a String in the destination encoding" do
+ result = @s.rb_str_encode("abc", Encoding::ISO_8859_1, 0, nil)
+ result.encoding.should == Encoding::ISO_8859_1
+ end
+
+ it "transcodes the String" do
+ result = @s.rb_str_encode("ã‚りãŒã¨ã†", "euc-jp", 0, nil)
+ euc_jp = [0xa4, 0xa2, 0xa4, 0xea, 0xa4, 0xac, 0xa4, 0xc8, 0xa4, 0xa6].pack('C*').force_encoding("euc-jp")
+ result.should == euc_jp
+ result.encoding.should == Encoding::EUC_JP
+ end
+
+ it "returns a dup of the original String" do
+ a = "abc"
+ b = @s.rb_str_encode("abc", "us-ascii", 0, nil)
+ a.should_not equal(b)
+ end
+
+ it "returns a duplicate of the original when the encoding doesn't change" do
+ a = "abc"
+ b = @s.rb_str_encode("abc", Encoding::UTF_8, 0, nil)
+ a.should_not equal(b)
+ end
+
+ it "accepts encoding flags" do
+ xFF = [0xFF].pack('C').force_encoding('utf-8')
+ result = @s.rb_str_encode("a#{xFF}c", "us-ascii",
+ Encoding::Converter::INVALID_REPLACE, nil)
+ result.should == "a?c"
+ result.encoding.should == Encoding::US_ASCII
+ end
+
+ it "accepts an encoding options Hash specifying replacement String" do
+ # Yeah, MRI aborts with rb_bug() if the options Hash is not frozen
+ options = { replace: "b" }.freeze
+ xFF = [0xFF].pack('C').force_encoding('utf-8')
+ result = @s.rb_str_encode("a#{xFF}c", "us-ascii",
+ Encoding::Converter::INVALID_REPLACE,
+ options)
+ result.should == "abc"
+ result.encoding.should == Encoding::US_ASCII
+ end
+ end
+
+ describe "rb_str_new3" do
+ it "returns a copy of the string" do
+ str1 = "hi"
+ str2 = @s.rb_str_new3 str1
+ str1.should == str2
+ str1.should_not equal str2
+ end
+ end
+
+ describe "rb_str_new4" do
+ it "returns the original string if it is already frozen" do
+ str1 = "hi"
+ str1.freeze
+ str2 = @s.rb_str_new4 str1
+ str1.should == str2
+ str1.should equal(str2)
+ str1.frozen?.should == true
+ str2.frozen?.should == true
+ end
+
+ it "returns a frozen copy of the string" do
+ str1 = "hi"
+ str2 = @s.rb_str_new4 str1
+ str1.should == str2
+ str1.should_not equal(str2)
+ str2.frozen?.should == true
+ end
+ end
+
+ describe "rb_str_dup" do
+ it "returns a copy of the string" do
+ str1 = "hi"
+ str2 = @s.rb_str_dup str1
+ str1.should == str2
+ str1.should_not equal str2
+ end
+ end
+
+ describe "rb_str_new5" do
+ it "returns a new string with the same class as the passed string" do
+ string_class = Class.new(String)
+ template_string = string_class.new("hello world")
+ new_string = @s.rb_str_new5(template_string, "hello world", 11)
+
+ new_string.should == "hello world"
+ new_string.class.should == string_class
+ end
+ end
+
+ describe "rb_tainted_str_new" do
+ it "creates a new tainted String" do
+ newstring = @s.rb_tainted_str_new("test", 4)
+ newstring.should == "test"
+ newstring.tainted?.should be_true
+ end
+ end
+
+ describe "rb_tainted_str_new2" do
+ it "creates a new tainted String" do
+ newstring = @s.rb_tainted_str_new2("test")
+ newstring.should == "test"
+ newstring.tainted?.should be_true
+ end
+ end
+
+ describe "rb_str_append" do
+ it "appends a string to another string" do
+ @s.rb_str_append("Hello", " Goodbye").should == "Hello Goodbye"
+ end
+
+ it "raises a TypeError trying to append non-String-like object" do
+ lambda { @s.rb_str_append("Hello", 32323)}.should raise_error(TypeError)
+ end
+
+ it "changes Encoding if a string is appended to an empty string" do
+ string = "パスタ".encode(Encoding::ISO_2022_JP)
+ @s.rb_str_append("", string).encoding.should == Encoding::ISO_2022_JP
+ end
+ end
+
+ describe "rb_str_plus" do
+ it "returns a new string from concatenating two other strings" do
+ @s.rb_str_plus("Hello", " Goodbye").should == "Hello Goodbye"
+ end
+ end
+
+ describe "rb_str_times" do
+ it_behaves_like :string_times, :rb_str_times, ->(str, times) { @s.rb_str_times(str, times) }
+ end
+
+ describe "rb_str_buf_cat" do
+ it "concatenates a C string to a ruby string" do
+ @s.rb_str_buf_cat("Your house is on fire").should == "Your house is on fire?"
+ end
+ end
+
+ describe "rb_str_cat" do
+ it "concatenates a C string to ruby string" do
+ @s.rb_str_cat("Your house is on fire").should == "Your house is on fire?"
+ end
+ end
+
+ describe "rb_str_cat2" do
+ it "concatenates a C string to a ruby string" do
+ @s.rb_str_cat2("Your house is on fire").should == "Your house is on fire?"
+ end
+ end
+
+ describe "rb_str_cmp" do
+ it "returns 0 if two strings are identical" do
+ @s.rb_str_cmp("ppp", "ppp").should == 0
+ end
+
+ it "returns -1 if the first string is shorter than the second" do
+ @s.rb_str_cmp("xxx", "xxxx").should == -1
+ end
+
+ it "returns -1 if the first string is lexically less than the second" do
+ @s.rb_str_cmp("xxx", "yyy").should == -1
+ end
+
+ it "returns 1 if the first string is longer than the second" do
+ @s.rb_str_cmp("xxxx", "xxx").should == 1
+ end
+
+ it "returns 1 if the first string is lexically greater than the second" do
+ @s.rb_str_cmp("yyy", "xxx").should == 1
+ end
+ end
+
+ describe "rb_str_split" do
+ it "splits strings over a splitter" do
+ @s.rb_str_split("Hello,Goodbye").should == ["Hello", "Goodbye"]
+ end
+ end
+
+ describe "rb_str2inum" do
+ it "converts a string to a number given a base" do
+ @s.rb_str2inum("10", 10).should == 10
+ @s.rb_str2inum("A", 16).should == 10
+ end
+ end
+
+ describe "rb_cstr2inum" do
+ it "converts a C string to a Fixnum given a base" do
+ @s.rb_cstr2inum("10", 10).should == 10
+ @s.rb_cstr2inum("10", 16).should == 16
+ end
+
+ it "converts a C string to a Bignum given a base" do
+ @s.rb_cstr2inum(bignum_value.to_s, 10).should == bignum_value
+ end
+
+ it "converts a C string to a Fixnum non-strictly if base is not 0" do
+ @s.rb_cstr2inum("1234a", 10).should == 1234
+ end
+
+ it "converts a C string to a Fixnum strictly if base is 0" do
+ lambda { @s.rb_cstr2inum("1234a", 0) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "rb_cstr_to_inum" do
+ it "converts a C string to a Fixnum given a base" do
+ @s.rb_cstr_to_inum("1234", 10, true).should == 1234
+ end
+
+ it "converts a C string to a Bignum given a base" do
+ @s.rb_cstr_to_inum(bignum_value.to_s, 10, true).should == bignum_value
+ end
+
+ it "converts a C string to a Fixnum non-strictly" do
+ @s.rb_cstr_to_inum("1234a", 10, false).should == 1234
+ end
+
+ it "converts a C string to a Fixnum strictly" do
+ lambda { @s.rb_cstr_to_inum("1234a", 10, true) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "rb_str_subseq" do
+ it "returns a byte-indexed substring" do
+ str = "\x00\x01\x02\x03\x04".force_encoding("binary")
+ @s.rb_str_subseq(str, 1, 2).should == "\x01\x02".force_encoding("binary")
+ end
+ end
+
+ describe "rb_str_substr" do
+ it "returns a substring" do
+ "hello".length.times do |time|
+ @s.rb_str_substr("hello", 0, time + 1).should == "hello"[0..time]
+ end
+ end
+ end
+
+ describe "rb_str_to_str" do
+ it "calls #to_str to coerce the value to a String" do
+ @s.rb_str_to_str("foo").should == "foo"
+ @s.rb_str_to_str(ValidTostrTest.new).should == "ruby"
+ end
+
+ it "raises a TypeError if coercion fails" do
+ lambda { @s.rb_str_to_str(0) }.should raise_error(TypeError)
+ lambda { @s.rb_str_to_str(InvalidTostrTest.new) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "RSTRING_PTR" do
+ it "returns a pointer to the string's contents" do
+ str = "abc"
+ chars = []
+ @s.RSTRING_PTR_iterate(str) do |c|
+ chars << c
+ end
+ chars.should == [97, 98, 99]
+ end
+
+ it "allows changing the characters in the string" do
+ str = "abc"
+ @s.RSTRING_PTR_assign(str, 'A'.ord)
+ str.should == "AAA"
+ end
+
+ it "reflects changes after a rb_funcall" do
+ lamb = proc { |s| s.replace "NEW CONTENT" }
+
+ str = "beforebefore"
+
+ ret = @s.RSTRING_PTR_after_funcall(str, lamb)
+
+ str.should == "NEW CONTENT"
+ ret.should == str
+ end
+
+ it "reflects changes from native memory and from String#setbyte in bounds" do
+ str = "abc"
+ from_rstring_ptr = @s.RSTRING_PTR_after_yield(str) { str.setbyte(1, 'B'.ord) }
+ from_rstring_ptr.should == "1B2"
+ str.should == "1B2"
+ end
+
+ it "returns a pointer to the contents of encoded pointer-sized string" do
+ s = "70パク".
+ encode(Encoding::UTF_16LE).
+ force_encoding(Encoding::UTF_16LE).
+ encode(Encoding::UTF_8)
+
+ chars = []
+ @s.RSTRING_PTR_iterate(s) do |c|
+ chars << c
+ end
+ chars.should == [55, 48, 227, 131, 145, 227, 130, 175]
+ end
+ end
+
+ describe "RSTRING_LEN" do
+ it "returns the size of the string" do
+ @s.RSTRING_LEN("gumdrops").should == 8
+ end
+ end
+
+ describe "RSTRING_LENINT" do
+ it "returns the size of a string" do
+ @s.RSTRING_LENINT("silly").should == 5
+ end
+ end
+
+ describe :string_value_macro, shared: true do
+ before :each do
+ @s = CApiStringSpecs.new
+ end
+
+ it "does not call #to_str on a String" do
+ str = "genuine"
+ str.should_not_receive(:to_str)
+ @s.send(@method, str)
+ end
+
+ it "does not call #to_s on a String" do
+ str = "genuine"
+ str.should_not_receive(:to_str)
+ @s.send(@method, str)
+ end
+
+ it "calls #to_str on non-String objects" do
+ str = mock("fake")
+ str.should_receive(:to_str).and_return("wannabe")
+ @s.send(@method, str).should == "wannabe"
+ end
+
+ it "does not call #to_s on non-String objects" do
+ str = mock("fake")
+ str.should_not_receive(:to_s)
+ lambda { @s.send(@method, str) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "StringValue" do
+ it_behaves_like :string_value_macro, :StringValue
+ end
+
+ describe "SafeStringValue" do
+ it "raises for tained string when $SAFE is 1" do
+ begin
+ Thread.new {
+ $SAFE = 1
+ lambda {
+ @s.SafeStringValue("str".taint)
+ }.should raise_error(SecurityError)
+ }.join
+ ensure
+ $SAFE = 0
+ end
+ end
+
+ it_behaves_like :string_value_macro, :SafeStringValue
+ end
+
+ describe "rb_str_resize" do
+ it "reduces the size of the string" do
+ str = @s.rb_str_resize("test", 2)
+ str.size.should == 2
+ @s.RSTRING_LEN(str).should == 2
+ str.should == "te"
+ end
+
+ it "updates the string's attributes visible in C code" do
+ @s.rb_str_resize_RSTRING_LEN("test", 2).should == 2
+ end
+
+ it "increases the size of the string" do
+ expected = "test".force_encoding("US-ASCII")
+ str = @s.rb_str_resize(expected.dup, 12)
+ str.size.should == 12
+ @s.RSTRING_LEN(str).should == 12
+ str[0, 4].should == expected
+ end
+ end
+
+ describe "rb_str_inspect" do
+ it "returns the equivalent of calling #inspect on the String" do
+ @s.rb_str_inspect("value").should == %["value"]
+ end
+ end
+
+ describe "rb_str_intern" do
+ it "returns a symbol created from the string" do
+ @s.rb_str_intern("symbol").should == :symbol
+ end
+
+ it "returns a symbol even if passed an empty string" do
+ @s.rb_str_intern("").should == "".to_sym
+ end
+
+ it "returns a symbol even if the passed string contains NULL characters" do
+ @s.rb_str_intern("no\0no").should == "no\0no".to_sym
+ end
+ end
+
+ describe "rb_str_freeze" do
+ it "freezes the string" do
+ s = ""
+ @s.rb_str_freeze(s).should == s
+ s.frozen?.should be_true
+ end
+ end
+
+ describe "rb_str_hash" do
+ it "hashes the string into a number" do
+ s = "hello"
+ @s.rb_str_hash(s).should be_kind_of(Integer)
+ end
+ end
+
+ describe "rb_str_update" do
+ it "splices the replacement string into the original at the given location" do
+ @s.rb_str_update("hello", 2, 3, "wuh").should == "hewuh"
+ end
+ end
+end
+
+describe "rb_str_free" do
+ # This spec only really exists to make sure the symbol
+ # is available. There is no guarantee this even does
+ # anything at all
+ it "indicates data for a string might be freed" do
+ @s.rb_str_free("xyz").should be_nil
+ end
+end
+
+describe :rb_external_str_new, shared: true do
+ it "returns a String in the default external encoding" do
+ Encoding.default_external = "UTF-8"
+ @s.send(@method, "abc").encoding.should == Encoding::UTF_8
+ end
+
+ it "returns an ASCII-8BIT encoded string if any non-ascii bytes are present and default external is US-ASCII" do
+ Encoding.default_external = "US-ASCII"
+ x80 = [0x80].pack('C')
+ @s.send(@method, "#{x80}abc").encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "returns a tainted String" do
+ @s.send(@method, "abc").tainted?.should be_true
+ end
+end
+
+describe "C-API String function" do
+ before :each do
+ @s = CApiStringSpecs.new
+ @external = Encoding.default_external
+ @internal = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ Encoding.default_internal = @internal
+ end
+
+ describe "rb_str_length" do
+ it "returns the string's length" do
+ @s.rb_str_length("dewdrops").should == 8
+ end
+
+ it "counts characters in multi byte encodings" do
+ @s.rb_str_length("düwdrops").should == 8
+ end
+ end
+
+ describe "rb_str_equal" do
+ it "compares two same strings" do
+ s = "hello"
+ @s.rb_str_equal(s, "hello").should be_true
+ end
+
+ it "compares two different strings" do
+ s = "hello"
+ @s.rb_str_equal(s, "hella").should be_false
+ end
+ end
+
+ describe "rb_external_str_new" do
+ it_behaves_like :rb_external_str_new, :rb_external_str_new
+ end
+
+ describe "rb_external_str_new_cstr" do
+ it_behaves_like :rb_external_str_new, :rb_external_str_new_cstr
+ end
+
+ describe "rb_external_str_new_with_enc" do
+ it "returns a String in the specified encoding" do
+ s = @s.rb_external_str_new_with_enc("abc", 3, Encoding::UTF_8)
+ s.encoding.should == Encoding::UTF_8
+ end
+
+ it "returns an ASCII-8BIT encoded String if any non-ascii bytes are present and the specified encoding is US-ASCII" do
+ x80 = [0x80].pack('C')
+ s = @s.rb_external_str_new_with_enc("#{x80}abc", 4, Encoding::US_ASCII)
+ s.encoding.should == Encoding::ASCII_8BIT
+ end
+
+
+# it "transcodes a String to Encoding.default_internal if it is set" do
+# Encoding.default_internal = Encoding::EUC_JP
+#
+# - a = "\xE3\x81\x82\xe3\x82\x8c".force_encoding("utf-8")
+# + a = [0xE3, 0x81, 0x82, 0xe3, 0x82, 0x8c].pack('C6').force_encoding("utf-8")
+# s = @s.rb_external_str_new_with_enc(a, a.bytesize, Encoding::UTF_8)
+# -
+# - s.should == "\xA4\xA2\xA4\xEC".force_encoding("euc-jp")
+# + x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4')#.force_encoding('ascii-8bit')
+# + s.should == x
+# s.encoding.should equal(Encoding::EUC_JP)
+# end
+
+ it "transcodes a String to Encoding.default_internal if it is set" do
+ Encoding.default_internal = Encoding::EUC_JP
+
+ a = [0xE3, 0x81, 0x82, 0xe3, 0x82, 0x8c].pack('C6').force_encoding("utf-8")
+ s = @s.rb_external_str_new_with_enc(a, a.bytesize, Encoding::UTF_8)
+ x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('euc-jp')
+ s.should == x
+ s.encoding.should equal(Encoding::EUC_JP)
+ end
+
+ it "returns a tainted String" do
+ s = @s.rb_external_str_new_with_enc("abc", 3, Encoding::US_ASCII)
+ s.tainted?.should be_true
+ end
+ end
+
+ describe "rb_locale_str_new" do
+ it "returns a String with 'locale' encoding" do
+ s = @s.rb_locale_str_new("abc", 3)
+ s.should == "abc".force_encoding(Encoding.find("locale"))
+ s.encoding.should equal(Encoding.find("locale"))
+ end
+ end
+
+ describe "rb_locale_str_new_cstr" do
+ it "returns a String with 'locale' encoding" do
+ s = @s.rb_locale_str_new_cstr("abc")
+ s.should == "abc".force_encoding(Encoding.find("locale"))
+ s.encoding.should equal(Encoding.find("locale"))
+ end
+ end
+
+ describe "rb_str_conv_enc" do
+ it "returns the original String when to encoding is not specified" do
+ a = "abc".force_encoding("us-ascii")
+ @s.rb_str_conv_enc(a, Encoding::US_ASCII, nil).should equal(a)
+ end
+
+ it "returns the original String if a transcoding error occurs" do
+ a = [0xEE].pack('C').force_encoding("utf-8")
+ @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP).should equal(a)
+ end
+
+ it "returns a transcoded String" do
+ a = "\xE3\x81\x82\xE3\x82\x8C".force_encoding("utf-8")
+ result = @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP)
+ x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('utf-8')
+ result.should == x.force_encoding("euc-jp")
+ result.encoding.should equal(Encoding::EUC_JP)
+ end
+
+ describe "when the String encoding is equal to the destination encoding" do
+ it "returns the original String" do
+ a = "abc".force_encoding("us-ascii")
+ @s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::US_ASCII).should equal(a)
+ end
+
+ it "returns the original String if the destination encoding is ASCII compatible and the String has no high bits set" do
+ a = "abc".encode("us-ascii")
+ @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::US_ASCII).should equal(a)
+ end
+
+ it "returns the origin String if the destination encoding is ASCII-8BIT" do
+ a = "abc".force_encoding("ascii-8bit")
+ @s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::ASCII_8BIT).should equal(a)
+ end
+ end
+ end
+
+ describe "rb_str_conv_enc_opts" do
+ it "returns the original String when to encoding is not specified" do
+ a = "abc".force_encoding("us-ascii")
+ @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII, nil, 0, nil).should equal(a)
+ end
+
+ it "returns the original String if a transcoding error occurs" do
+ a = [0xEE].pack('C').force_encoding("utf-8")
+ @s.rb_str_conv_enc_opts(a, Encoding::UTF_8,
+ Encoding::EUC_JP, 0, nil).should equal(a)
+ end
+
+ it "returns a transcoded String" do
+ a = "\xE3\x81\x82\xE3\x82\x8C".force_encoding("utf-8")
+ result = @s.rb_str_conv_enc_opts(a, Encoding::UTF_8, Encoding::EUC_JP, 0, nil)
+ x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('utf-8')
+ result.should == x.force_encoding("euc-jp")
+ result.encoding.should equal(Encoding::EUC_JP)
+ end
+
+ describe "when the String encoding is equal to the destination encoding" do
+ it "returns the original String" do
+ a = "abc".force_encoding("us-ascii")
+ @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII,
+ Encoding::US_ASCII, 0, nil).should equal(a)
+ end
+
+ it "returns the original String if the destination encoding is ASCII compatible and the String has no high bits set" do
+ a = "abc".encode("us-ascii")
+ @s.rb_str_conv_enc_opts(a, Encoding::UTF_8,
+ Encoding::US_ASCII, 0, nil).should equal(a)
+ end
+
+ it "returns the origin String if the destination encoding is ASCII-8BIT" do
+ a = "abc".force_encoding("ascii-8bit")
+ @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII,
+ Encoding::ASCII_8BIT, 0, nil).should equal(a)
+ end
+ end
+ end
+
+ describe "rb_str_export" do
+ it "returns the original String with the external encoding" do
+ Encoding.default_external = Encoding::ISO_8859_1
+ s = @s.rb_str_export("Hëllo")
+ s.encoding.should equal(Encoding::ISO_8859_1)
+ end
+ end
+
+ describe "rb_str_export_locale" do
+ it "returns the original String with the locale encoding" do
+ s = @s.rb_str_export_locale("abc")
+ s.should == "abc".force_encoding(Encoding.find("locale"))
+ s.encoding.should equal(Encoding.find("locale"))
+ end
+ end
+
+ describe "rb_sprintf" do
+ it "replaces the parts like sprintf" do
+ @s.rb_sprintf1("Awesome %s is replaced", "string").should == "Awesome string is replaced"
+ @s.rb_sprintf1("%s", "TestFoobarTest").should == "TestFoobarTest"
+ end
+
+ it "accepts multiple arguments" do
+ s = "Awesome %s is here with %s"
+ @s.rb_sprintf2(s, "string", "content").should == "Awesome string is here with content"
+ end
+ end
+
+ describe "rb_vsprintf" do
+ it "returns a formatted String from a variable number of arguments" do
+ s = @s.rb_vsprintf("%s, %d, %.2f", "abc", 42, 2.7);
+ s.should == "abc, 42, 2.70"
+ end
+ end
+
+ describe "rb_String" do
+ it "returns the passed argument if it is a string" do
+ @s.rb_String("a").should == "a"
+ end
+
+ it "tries to convert the passed argument to a string by calling #to_str first" do
+ @s.rb_String(ValidTostrTest.new).should == "ruby"
+ end
+
+ it "raises a TypeError if #to_str does not return a string" do
+ lambda { @s.rb_String(InvalidTostrTest.new) }.should raise_error(TypeError)
+ end
+
+ it "tries to convert the passed argument to a string by calling #to_s" do
+ @s.rb_String({"bar" => "foo"}).should == '{"bar"=>"foo"}'
+ end
+ end
+
+ describe "rb_string_value_cstr" do
+ it "returns a non-null pointer for a simple string" do
+ @s.rb_string_value_cstr("Hello").should == true
+ end
+
+ it "returns a non-null pointer for a UTF-16 string" do
+ @s.rb_string_value_cstr("Hello".encode('UTF-16BE')).should == true
+ end
+
+ it "raises an error if a string contains a null" do
+ lambda { @s.rb_string_value_cstr("Hello\0 with a null.") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an error if a UTF-16 string contains a null" do
+ lambda { @s.rb_string_value_cstr("Hello\0 with a null.".encode('UTF-16BE')) }.should raise_error(ArgumentError)
+ end
+
+ end
+end
diff --git a/spec/ruby/optional/capi/struct_spec.rb b/spec/ruby/optional/capi/struct_spec.rb
new file mode 100644
index 0000000000..b3acd02b61
--- /dev/null
+++ b/spec/ruby/optional/capi/struct_spec.rb
@@ -0,0 +1,213 @@
+require_relative 'spec_helper'
+
+load_extension("struct")
+
+describe "C-API Struct function" do
+ before :each do
+ @s = CApiStructSpecs.new
+ @struct = @s.rb_struct_define("CAPIStruct", "a", "b", "c")
+ end
+
+ after :each do
+ Struct.send(:remove_const, :CAPIStruct)
+ end
+
+ describe "rb_struct_define" do
+ it "creates accessors for the struct members" do
+ instance = @struct.new
+ instance.a = 1
+ instance.b = 2
+ instance.c = 3
+ instance.a.should == 1
+ instance.b.should == 2
+ instance.c.should == 3
+ end
+
+ it "has a value of nil for the member of a newly created instance" do
+ # Verify that attributes are on an instance basis
+ Struct::CAPIStruct.new.b.should be_nil
+ end
+
+ it "creates a constant scoped under Struct for the named Struct" do
+ Struct.should have_constant(:CAPIStruct)
+ end
+
+ it "returns the member names as Symbols" do
+ @struct.members.should == [:a, :b, :c]
+ end
+ end
+end
+
+describe "C-API Struct function" do
+ before :each do
+ @s = CApiStructSpecs.new
+ @struct = @s.rb_struct_define(nil, "a", "b", "c")
+ end
+
+ describe "rb_struct_define for an anonymous struct" do
+ it "creates accessors for the struct members" do
+ instance = @struct.new
+ instance.a = 1
+ instance.b = 2
+ instance.c = 3
+ instance.a.should == 1
+ instance.b.should == 2
+ instance.c.should == 3
+ end
+
+ it "returns the member names as Symbols" do
+ @struct.members.should == [:a, :b, :c]
+ end
+ end
+end
+
+describe "C-API Struct function" do
+ before :each do
+ @s = CApiStructSpecs.new
+ @struct = @s.rb_struct_define_under(CApiStructSpecs, "CAPIStructUnder", "a", "b", "c")
+ end
+
+ describe "rb_struct_define_under" do
+ it "creates accessors for the struct members" do
+ instance = @struct.new
+ instance.a = 1
+ instance.b = 2
+ instance.c = 3
+ instance.a.should == 1
+ instance.b.should == 2
+ instance.c.should == 3
+ end
+
+ it "has a value of nil for the member of a newly created instance" do
+ # Verify that attributes are on an instance basis
+ CApiStructSpecs::CAPIStructUnder.new.b.should be_nil
+ end
+
+ it "does not create a constant scoped under Struct for the named Struct" do
+ Struct.should_not have_constant(:CAPIStructUnder)
+ end
+
+ it "creates a constant scoped under the namespace of the given class" do
+ CApiStructSpecs.should have_constant(:CAPIStructUnder)
+ end
+
+ it "returns the member names as Symbols" do
+ @struct.members.should == [:a, :b, :c]
+ end
+ end
+end
+
+describe "C-API Struct function" do
+ before :each do
+ @s = CApiStructSpecs.new
+ @klass = Struct.new(:a, :b, :c)
+ @struct = @klass.new
+ end
+
+ describe "rb_struct_define" do
+ it "raises an ArgumentError if arguments contain duplicate member name" do
+ lambda { @s.rb_struct_define(nil, "a", "b", "a") }.should raise_error(ArgumentError)
+ end
+
+ it "raises a NameError if an invalid constant name is given" do
+ lambda { @s.rb_struct_define("foo", "a", "b", "c") }.should raise_error(NameError)
+ end
+ end
+
+ describe "rb_struct_aref" do
+ it "returns the value of a struct member with a symbol key" do
+ @struct[:a] = 2
+ @s.rb_struct_aref(@struct, :a).should == 2
+ end
+
+ it "returns the value of a struct member with a string key" do
+ @struct[:b] = 2
+ @s.rb_struct_aref(@struct, "b").should == 2
+ end
+
+ it "returns the value of a struct member by index" do
+ @struct[:c] = 3
+ @s.rb_struct_aref(@struct, 2).should == 3
+ end
+
+ it "raises a NameError if the struct member does not exist" do
+ lambda { @s.rb_struct_aref(@struct, :d) }.should raise_error(NameError)
+ end
+
+ it "raises an IndexError if the given index is out of range" do
+ lambda { @s.rb_struct_aref(@struct, -4) }.should raise_error(IndexError)
+ lambda { @s.rb_struct_aref(@struct, 3) }.should raise_error(IndexError)
+ end
+ end
+
+ describe "rb_struct_getmember" do
+ it "returns the value of a struct member" do
+ @struct[:a] = 2
+ @s.rb_struct_getmember(@struct, :a).should == 2
+ end
+
+ it "raises a NameError if the struct member does not exist" do
+ lambda { @s.rb_struct_getmember(@struct, :d) }.should raise_error(NameError)
+ end
+ end
+
+ describe "rb_struct_s_members" do
+ it "returns the struct members as an array of symbols" do
+ @s.rb_struct_s_members(@klass).should == [:a, :b, :c]
+ end
+ end
+
+ describe "rb_struct_members" do
+ it "returns the struct members as an array of symbols" do
+ @s.rb_struct_members(@struct).should == [:a, :b, :c]
+ end
+ end
+
+ describe "rb_struct_aset" do
+ it "sets the value of a struct member with a symbol key" do
+ @s.rb_struct_aset(@struct, :a, 1)
+ @struct[:a].should == 1
+ end
+
+ it "sets the value of a struct member with a string key" do
+ @s.rb_struct_aset(@struct, "b", 1)
+ @struct[:b].should == 1
+ end
+
+ it "sets the value of a struct member by index" do
+ @s.rb_struct_aset(@struct, 2, 1)
+ @struct[:c].should == 1
+ end
+
+ it "raises a NameError if the struct member does not exist" do
+ lambda { @s.rb_struct_aset(@struct, :d, 1) }.should raise_error(NameError)
+ end
+
+ it "raises an IndexError if the given index is out of range" do
+ lambda { @s.rb_struct_aset(@struct, -4, 1) }.should raise_error(IndexError)
+ lambda { @s.rb_struct_aset(@struct, 3, 1) }.should raise_error(IndexError)
+ end
+
+ it "raises a #{frozen_error_class} if the struct is frozen" do
+ @struct.freeze
+ lambda { @s.rb_struct_aset(@struct, :a, 1) }.should raise_error(frozen_error_class)
+ end
+ end
+
+ describe "rb_struct_new" do
+ it "creates a new instance of a struct" do
+ i = @s.rb_struct_new(@klass, 1, 2, 3)
+ i.a.should == 1
+ i.b.should == 2
+ i.c.should == 3
+ end
+ end
+
+ ruby_version_is "2.4" do
+ describe "rb_struct_size" do
+ it "returns the number of struct members" do
+ @s.rb_struct_size(@struct).should == 3
+ end
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/symbol_spec.rb b/spec/ruby/optional/capi/symbol_spec.rb
new file mode 100644
index 0000000000..ddc748c8d8
--- /dev/null
+++ b/spec/ruby/optional/capi/symbol_spec.rb
@@ -0,0 +1,133 @@
+# -*- encoding: utf-8 -*-
+require_relative 'spec_helper'
+
+load_extension('symbol')
+
+describe "C-API Symbol function" do
+ before :each do
+ @s = CApiSymbolSpecs.new
+ end
+
+ describe "rb_intern" do
+ it "converts a string to a symbol, uniquely" do
+ @s.rb_intern("test_symbol").should == :test_symbol
+ @s.rb_intern_c_compare("test_symbol", :test_symbol).should == true
+ end
+ end
+
+ describe "rb_intern2" do
+ it "converts a string to a symbol, uniquely, for a string of given length" do
+ @s.rb_intern2("test_symbol", 4).should == :test
+ @s.rb_intern2_c_compare("test_symbol", 4, :test).should == true
+ end
+ end
+
+ describe "rb_intern3" do
+ it "converts a multibyte symbol with the encoding" do
+ sym = @s.rb_intern3("Ω", 2, Encoding::UTF_8)
+ sym.encoding.should == Encoding::UTF_8
+ sym.should == :Ω
+ @s.rb_intern3_c_compare("Ω", 2, Encoding::UTF_8, :Ω).should == true
+ end
+
+ it "converts an ascii compatible symbol with the ascii encoding" do
+ sym = @s.rb_intern3("foo", 3, Encoding::UTF_8)
+ sym.encoding.should == Encoding::US_ASCII
+ sym.should == :foo
+ end
+
+ it "should respect the symbol encoding via rb_intern3" do
+ :Ω.to_s.encoding.should == Encoding::UTF_8
+ end
+ end
+
+ describe "rb_intern_const" do
+ it "converts a string to a Symbol" do
+ @s.rb_intern_const("test").should == :test
+ end
+ end
+
+ describe "rb_id2name" do
+ it "converts a symbol to a C char array" do
+ @s.rb_id2name(:test_symbol).should == "test_symbol"
+ end
+ end
+
+ describe "rb_id2str" do
+ it "converts a symbol to a Ruby string" do
+ @s.rb_id2str(:test_symbol).should == "test_symbol"
+ end
+
+ it "creates a string with the same encoding as the symbol" do
+ str = "test_symbol".encode(Encoding::UTF_16LE)
+ @s.rb_id2str(str.to_sym).encoding.should == Encoding::UTF_16LE
+ end
+ end
+
+ describe "rb_intern_str" do
+ it "converts a Ruby String to a Symbol" do
+ str = "test_symbol"
+ @s.rb_intern_str(str).should == :test_symbol
+ end
+ end
+
+ describe "rb_is_const_id" do
+ it "returns true given a const-like symbol" do
+ @s.rb_is_const_id(:Foo).should == true
+ end
+
+ it "returns false given an ivar-like symbol" do
+ @s.rb_is_const_id(:@foo).should == false
+ end
+
+ it "returns false given a cvar-like symbol" do
+ @s.rb_is_const_id(:@@foo).should == false
+ end
+
+ it "returns false given an undecorated symbol" do
+ @s.rb_is_const_id(:foo).should == false
+ end
+ end
+
+ describe "rb_is_instance_id" do
+ it "returns false given a const-like symbol" do
+ @s.rb_is_instance_id(:Foo).should == false
+ end
+
+ it "returns true given an ivar-like symbol" do
+ @s.rb_is_instance_id(:@foo).should == true
+ end
+
+ it "returns false given a cvar-like symbol" do
+ @s.rb_is_instance_id(:@@foo).should == false
+ end
+
+ it "returns false given an undecorated symbol" do
+ @s.rb_is_instance_id(:foo).should == false
+ end
+ end
+
+ describe "rb_is_class_id" do
+ it "returns false given a const-like symbol" do
+ @s.rb_is_class_id(:Foo).should == false
+ end
+
+ it "returns false given an ivar-like symbol" do
+ @s.rb_is_class_id(:@foo).should == false
+ end
+
+ it "returns true given a cvar-like symbol" do
+ @s.rb_is_class_id(:@@foo).should == true
+ end
+
+ it "returns false given an undecorated symbol" do
+ @s.rb_is_class_id(:foo).should == false
+ end
+ end
+
+ describe "rb_sym2str" do
+ it "converts a Symbol to a String" do
+ @s.rb_sym2str(:bacon).should == "bacon"
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/thread_spec.rb b/spec/ruby/optional/capi/thread_spec.rb
new file mode 100644
index 0000000000..52070198fd
--- /dev/null
+++ b/spec/ruby/optional/capi/thread_spec.rb
@@ -0,0 +1,149 @@
+require_relative 'spec_helper'
+require_relative '../../core/thread/shared/wakeup'
+
+load_extension("thread")
+
+class Thread
+ def self.capi_thread_specs=(t)
+ @@capi_thread_specs = t
+ end
+
+ def call_capi_rb_thread_wakeup
+ @@capi_thread_specs.rb_thread_wakeup(self)
+ end
+end
+
+describe "C-API Thread function" do
+ before :each do
+ @t = CApiThreadSpecs.new
+ ScratchPad.clear
+ Thread.capi_thread_specs = @t
+ end
+
+ describe "rb_thread_wait_for" do
+ it "sleeps the current thread for the give amount of time" do
+ start = Time.now
+ @t.rb_thread_wait_for(0, 100_000)
+ (Time.now - start).should be_close(0.1, 0.2)
+ end
+ end
+
+ describe "rb_thread_alone" do
+ it "returns true if there is only one thread" do
+ pred = Thread.list.size == 1
+ @t.rb_thread_alone.should == pred
+ end
+ end
+
+ describe "rb_thread_current" do
+ it "equals Thread.current" do
+ @t.rb_thread_current.should == Thread.current
+ end
+ end
+
+ describe "rb_thread_local_aref" do
+ it "returns the value of a thread-local variable" do
+ thr = Thread.current
+ sym = :thread_capi_specs_aref
+ thr[sym] = 1
+ @t.rb_thread_local_aref(thr, sym).should == 1
+ end
+
+ it "returns nil if the value has not been set" do
+ @t.rb_thread_local_aref(Thread.current, :thread_capi_specs_undefined).should be_nil
+ end
+ end
+
+ describe "rb_thread_local_aset" do
+ it "sets the value of a thread-local variable" do
+ thr = Thread.current
+ sym = :thread_capi_specs_aset
+ @t.rb_thread_local_aset(thr, sym, 2).should == 2
+ thr[sym].should == 2
+ end
+ end
+
+ describe "rb_thread_wakeup" do
+ it_behaves_like :thread_wakeup, :call_capi_rb_thread_wakeup
+ end
+
+ describe "rb_thread_create" do
+ it "creates a new thread" do
+ obj = Object.new
+ proc = lambda { |x| ScratchPad.record x }
+ thr = @t.rb_thread_create(proc, obj)
+ thr.should be_kind_of(Thread)
+ thr.join
+ ScratchPad.recorded.should == obj
+ end
+
+ it "handles throwing an exception in the thread" do
+ prc = lambda { |x|
+ Thread.current.report_on_exception = false
+ raise "my error"
+ }
+ thr = @t.rb_thread_create(prc, nil)
+ thr.should be_kind_of(Thread)
+
+ lambda {
+ thr.join
+ }.should raise_error(RuntimeError, "my error")
+ end
+
+ it "sets the thread's group" do
+ thr = @t.rb_thread_create(lambda { |x| }, nil)
+ begin
+ thread_group = thr.group
+ thread_group.should be_an_instance_of(ThreadGroup)
+ ensure
+ thr.join
+ end
+ end
+ end
+
+ describe "rb_thread_call_without_gvl" do
+ it "runs a C function with the global lock unlocked" do
+ thr = Thread.new do
+ @t.rb_thread_call_without_gvl
+ end
+
+ # Wait until it's blocking...
+ Thread.pass while thr.status and thr.status != "sleep"
+
+ # The thread status is set to sleep by rb_thread_call_without_gvl(),
+ # but the thread might not be in the blocking read(2) yet, so wait a bit.
+ sleep 0.1
+
+ # Wake it up, causing the unblock function to be run.
+ thr.wakeup
+
+ # Make sure it stopped
+ thr.join(1).should_not be_nil
+
+ # And we got a proper value
+ thr.value.should be_true
+ end
+
+ it "runs a C function with the global lock unlocked amd unlocks IO with the generic RUBY_UBF_IO" do
+ thr = Thread.new do
+ @t.rb_thread_call_without_gvl_with_ubf_io
+ end
+
+ # Wait until it's blocking...
+ Thread.pass while thr.status and thr.status != "sleep"
+
+ # The thread status is set to sleep by rb_thread_call_without_gvl(),
+ # but the thread might not be in the blocking read(2) yet, so wait a bit.
+ sleep 0.1
+
+ # Wake it up, causing the unblock function to be run.
+ thr.wakeup
+
+ # Make sure it stopped
+ thr.join(1).should_not be_nil
+
+ # And we got a proper value
+ thr.value.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/time_spec.rb b/spec/ruby/optional/capi/time_spec.rb
new file mode 100644
index 0000000000..4a59c98100
--- /dev/null
+++ b/spec/ruby/optional/capi/time_spec.rb
@@ -0,0 +1,300 @@
+require_relative 'spec_helper'
+
+load_extension("time")
+
+describe "CApiTimeSpecs" do
+ before :each do
+ @s = CApiTimeSpecs.new
+ end
+
+ describe "rb_time_new" do
+ it "creates a Time from the sec and usec" do
+ usec = CAPI_SIZEOF_LONG == 8 ? 4611686018427387903 : 1413123123
+ @s.rb_time_new(1232141421, usec).should == Time.at(1232141421, usec)
+ end
+ end
+
+ describe "TIMET2NUM" do
+ it "returns an Integer" do
+ @s.TIMET2NUM.should be_kind_of(Integer)
+ end
+ end
+
+ describe "rb_time_nano_new" do
+ it "creates a Time from the sec and usec" do
+ time = @s.rb_time_nano_new(1232141421, 1413123123)
+ time.to_i.should == 1232141422
+ time.nsec.should == 413123123
+ end
+ end
+
+ describe "rb_time_num_new" do
+ it "creates a Time in the local zone with only a timestamp" do
+ with_timezone("Europe/Amsterdam") do
+ time = @s.rb_time_num_new(1232141421, nil)
+ time.should be_an_instance_of(Time)
+ time.to_i.should == 1232141421
+ platform_is_not :windows do
+ time.gmt_offset.should == 3600
+ end
+ end
+ end
+
+ it "creates a Time with the given offset" do
+ with_timezone("Europe/Amsterdam") do
+ time = @s.rb_time_num_new(1232141421, 7200)
+ time.should be_an_instance_of(Time)
+ time.to_i.should == 1232141421
+ time.gmt_offset.should == 7200
+ end
+ end
+
+ it "creates a Time with a Float timestamp" do
+ with_timezone("Europe/Amsterdam") do
+ time = @s.rb_time_num_new(1.5, 7200)
+ time.should be_an_instance_of(Time)
+ time.to_i.should == 1
+ time.nsec.should == 500000000
+ time.gmt_offset.should == 7200
+ end
+ end
+
+ it "creates a Time with a Rational timestamp" do
+ with_timezone("Europe/Amsterdam") do
+ time = @s.rb_time_num_new(Rational(3, 2), 7200)
+ time.should be_an_instance_of(Time)
+ time.to_i.should == 1
+ time.nsec.should == 500000000
+ time.gmt_offset.should == 7200
+ end
+ end
+ end
+
+ describe "rb_time_interval" do
+ it "creates a timeval interval for a Fixnum" do
+ sec, usec = @s.rb_time_interval(1232141421)
+ sec.should be_kind_of(Integer)
+ sec.should == 1232141421
+ usec.should be_kind_of(Integer)
+ usec.should == 0
+ end
+
+ it "creates a timeval interval for a Float" do
+ sec, usec = @s.rb_time_interval(1.5)
+ sec.should be_kind_of(Integer)
+ sec.should == 1
+ usec.should be_kind_of(Integer)
+ usec.should == 500000
+ end
+
+ it "creates a timeval interval for a Rational" do
+ sec, usec = @s.rb_time_interval(Rational(3, 2))
+ sec.should be_kind_of(Integer)
+ sec.should == 1
+ usec.should be_kind_of(Integer)
+ usec.should == 500000
+ end
+
+ it "throws an argument error for a negative value" do
+ lambda { @s.rb_time_interval(-1232141421) }.should raise_error(ArgumentError)
+ lambda { @s.rb_time_interval(Rational(-3, 2)) }.should raise_error(ArgumentError)
+ lambda { @s.rb_time_interval(-1.5) }.should raise_error(ArgumentError)
+ end
+
+ end
+
+ describe "rb_time_interval" do
+ it "creates a timeval interval for a Fixnum" do
+ sec, usec = @s.rb_time_interval(1232141421)
+ sec.should be_kind_of(Integer)
+ sec.should == 1232141421
+ usec.should be_kind_of(Integer)
+ usec.should == 0
+ end
+
+ it "creates a timeval interval for a Float" do
+ sec, usec = @s.rb_time_interval(1.5)
+ sec.should be_kind_of(Integer)
+ sec.should == 1
+ usec.should be_kind_of(Integer)
+ usec.should == 500000
+ end
+
+ it "creates a timeval interval for a Rational" do
+ sec, usec = @s.rb_time_interval(Rational(3, 2))
+ sec.should be_kind_of(Integer)
+ sec.should == 1
+ usec.should be_kind_of(Integer)
+ usec.should == 500000
+ end
+
+ it "throws an argument error for a negative value" do
+ lambda { @s.rb_time_interval(-1232141421) }.should raise_error(ArgumentError)
+ lambda { @s.rb_time_interval(Rational(-3, 2)) }.should raise_error(ArgumentError)
+ lambda { @s.rb_time_interval(-1.5) }.should raise_error(ArgumentError)
+ end
+
+ it "throws an argument error when given a Time instance" do
+ lambda { @s.rb_time_interval(Time.now) }.should raise_error(TypeError)
+ end
+
+ end
+
+ describe "rb_time_timeval" do
+ it "creates a timeval for a Fixnum" do
+ sec, usec = @s.rb_time_timeval(1232141421)
+ sec.should be_kind_of(Integer)
+ sec.should == 1232141421
+ usec.should be_kind_of(Integer)
+ usec.should == 0
+ end
+
+ it "creates a timeval for a Float" do
+ sec, usec = @s.rb_time_timeval(1.5)
+ sec.should be_kind_of(Integer)
+ sec.should == 1
+ usec.should be_kind_of(Integer)
+ usec.should == 500000
+ end
+
+ it "creates a timeval for a Rational" do
+ sec, usec = @s.rb_time_timeval(Rational(3, 2))
+ sec.should be_kind_of(Integer)
+ sec.should == 1
+ usec.should be_kind_of(Integer)
+ usec.should == 500000
+ end
+
+ platform_is_not :mingw32 do
+ it "creates a timeval for a negative Fixnum" do
+ sec, usec = @s.rb_time_timeval(-1232141421)
+ sec.should be_kind_of(Integer)
+ sec.should == -1232141421
+ usec.should be_kind_of(Integer)
+ usec.should == 0
+ end
+
+ it "creates a timeval for a negative Float" do
+ sec, usec = @s.rb_time_timeval(-1.5)
+ sec.should be_kind_of(Integer)
+ sec.should == -2
+ usec.should be_kind_of(Integer)
+ usec.should == 500000
+ end
+
+ it "creates a timeval for a negative Rational" do
+ sec, usec = @s.rb_time_timeval(Rational(-3, 2))
+ sec.should be_kind_of(Integer)
+ sec.should == -2
+ usec.should be_kind_of(Integer)
+ usec.should == 500000
+ end
+ end
+
+ it "creates a timeval from a Time object" do
+ t = Time.now
+ sec, usec = @s.rb_time_timeval(t)
+ sec.should == t.to_i
+ usec.should == t.nsec.div(1000)
+ end
+ end
+
+ describe "rb_time_timespec" do
+ it "creates a timespec for a Fixnum" do
+ sec, nsec = @s.rb_time_timespec(1232141421)
+ sec.should be_kind_of(Integer)
+ sec.should == 1232141421
+ nsec.should be_kind_of(Integer)
+ nsec.should == 0
+ end
+
+ it "creates a timespec for a Float" do
+ sec, nsec = @s.rb_time_timespec(1.5)
+ sec.should be_kind_of(Integer)
+ sec.should == 1
+ nsec.should be_kind_of(Integer)
+ nsec.should == 500000000
+ end
+
+ it "creates a timespec for a Rational" do
+ sec, nsec = @s.rb_time_timespec(Rational(3, 2))
+ sec.should be_kind_of(Integer)
+ sec.should == 1
+ nsec.should be_kind_of(Integer)
+ nsec.should == 500000000
+ end
+
+ platform_is_not :mingw32 do
+ it "creates a timespec for a negative Fixnum" do
+ sec, nsec = @s.rb_time_timespec(-1232141421)
+ sec.should be_kind_of(Integer)
+ sec.should == -1232141421
+ nsec.should be_kind_of(Integer)
+ nsec.should == 0
+ end
+
+ it "creates a timespec for a negative Float" do
+ sec, nsec = @s.rb_time_timespec(-1.5)
+ sec.should be_kind_of(Integer)
+ sec.should == -2
+ nsec.should be_kind_of(Integer)
+ nsec.should == 500000000
+ end
+
+ it "creates a timespec for a negative Rational" do
+ sec, nsec = @s.rb_time_timespec(Rational(-3, 2))
+ sec.should be_kind_of(Integer)
+ sec.should == -2
+ nsec.should be_kind_of(Integer)
+ nsec.should == 500000000
+ end
+ end
+
+ it "creates a timespec from a Time object" do
+ t = Time.now
+ sec, nsec = @s.rb_time_timespec(t)
+ sec.should == t.to_i
+ nsec.should == t.nsec
+ end
+ end
+
+ describe "rb_time_timespec_new" do
+ it "returns a time object with the given timespec and UTC offset" do
+ @s.rb_time_timespec_new(1447087832, 476451125, 32400).should == Time.at(1447087832, 476451.125).localtime(32400)
+ end
+
+ describe "when offset given is within range of -86400 and 86400 (exclusive)" do
+ it "sets time's is_gmt to false" do
+ @s.rb_time_timespec_new(1447087832, 476451125, 0).gmt?.should be_false
+ end
+
+ it "sets time's offset to the offset given" do
+ @s.rb_time_timespec_new(1447087832, 476451125, 86399).gmtoff.should == 86399
+ end
+ end
+
+ it "returns time object in UTC if offset given equals INT_MAX - 1" do
+ @s.rb_time_timespec_new(1447087832, 476451125, 0x7ffffffe).utc?.should be_true
+ end
+
+ it "returns time object in localtime if offset given equals INT_MAX" do
+ @s.rb_time_timespec_new(1447087832, 476451125, 0x7fffffff).should == Time.at(1447087832, 476451.125).localtime
+ t = Time.now
+ @s.rb_time_timespec_new(t.tv_sec, t.tv_nsec, 0x7fffffff).gmtoff.should == t.gmtoff
+ end
+
+ it "raises an ArgumentError if offset passed is not within range of -86400 and 86400 (exclusive)" do
+ lambda { @s.rb_time_timespec_new(1447087832, 476451125, 86400) }.should raise_error(ArgumentError)
+ lambda { @s.rb_time_timespec_new(1447087832, 476451125, -86400) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "rb_timespec_now" do
+ it "fills a struct timespec with the current time" do
+ now = Time.now
+ time = @s.rb_time_from_timespec(now.utc_offset)
+ time.should be_an_instance_of(Time)
+ (time - now).should be_close(0, 10)
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/typed_data_spec.rb b/spec/ruby/optional/capi/typed_data_spec.rb
new file mode 100644
index 0000000000..b0c0cd48ed
--- /dev/null
+++ b/spec/ruby/optional/capi/typed_data_spec.rb
@@ -0,0 +1,51 @@
+require_relative 'spec_helper'
+
+load_extension("typed_data")
+
+describe "CApiAllocTypedSpecs (a class with an alloc func defined)" do
+ it "calls the alloc func" do
+ @s = CApiAllocTypedSpecs.new
+ @s.typed_wrapped_data.should == 42 # not defined in initialize
+ end
+end
+
+describe "CApiWrappedTypedStruct" do
+ before :each do
+ @s = CApiWrappedTypedStructSpecs.new
+ end
+
+ it "wraps and unwraps data" do
+ a = @s.typed_wrap_struct(1024)
+ @s.typed_get_struct(a).should == 1024
+ end
+
+ it "throws an exception for a wrong type" do
+ a = @s.typed_wrap_struct(1024)
+ lambda { @s.typed_get_struct_other(a) }.should raise_error(TypeError)
+ end
+
+ it "unwraps data for a parent type" do
+ a = @s.typed_wrap_struct(1024)
+ @s.typed_get_struct_parent(a).should == 1024
+ end
+
+ describe "RTYPEDATA" do
+ it "returns the struct data" do
+ a = @s.typed_wrap_struct(1024)
+ @s.typed_get_struct_rdata(a).should == 1024
+ end
+
+ it "can be used to change the wrapped struct" do
+ a = @s.typed_wrap_struct(1024)
+ @s.typed_change_struct(a, 100)
+ @s.typed_get_struct(a).should == 100
+ end
+ end
+
+ describe "DATA_PTR" do
+ it "returns the struct data" do
+ a = @s.typed_wrap_struct(1024)
+ @s.typed_get_struct_data_ptr(a).should == 1024
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/util_spec.rb b/spec/ruby/optional/capi/util_spec.rb
new file mode 100644
index 0000000000..da2f758bec
--- /dev/null
+++ b/spec/ruby/optional/capi/util_spec.rb
@@ -0,0 +1,201 @@
+require_relative 'spec_helper'
+
+load_extension('util')
+
+describe "C-API Util function" do
+ before :each do
+ @o = CApiUtilSpecs.new
+ end
+
+ describe "rb_scan_args" do
+ before :each do
+ @prc = lambda { 1 }
+ @acc = []
+ ScratchPad.record @acc
+ end
+
+ it "assigns the required arguments scanned" do
+ @o.rb_scan_args([1, 2], "2", 2, @acc).should == 2
+ ScratchPad.recorded.should == [1, 2]
+ end
+
+ it "raises an ArgumentError if there are insufficient arguments" do
+ lambda { @o.rb_scan_args([1, 2], "3", 0, @acc) }.should raise_error(ArgumentError)
+ end
+
+ it "assigns the required and optional arguments scanned" do
+ @o.rb_scan_args([1, 2], "11", 2, @acc).should == 2
+ ScratchPad.recorded.should == [1, 2]
+ end
+
+ it "assigns the optional arguments scanned" do
+ @o.rb_scan_args([1, 2], "02", 2, @acc).should == 2
+ ScratchPad.recorded.should == [1, 2]
+ end
+
+ it "assigns nil for optional arguments that are not present" do
+ @o.rb_scan_args([1], "03", 3, @acc).should == 1
+ ScratchPad.recorded.should == [1, nil, nil]
+ end
+
+ it "assigns the required and optional arguments and splats the rest" do
+ @o.rb_scan_args([1, 2, 3, 4], "11*", 3, @acc).should == 4
+ ScratchPad.recorded.should == [1, 2, [3, 4]]
+ end
+
+ it "assigns the required and optional arguments and and empty Array when there are no arguments to splat" do
+ @o.rb_scan_args([1, 2], "11*", 3, @acc).should == 2
+ ScratchPad.recorded.should == [1, 2, []]
+ end
+
+ it "assigns required, optional arguments scanned and the passed block" do
+ @o.rb_scan_args([1, 2], "11&", 3, @acc, &@prc).should == 2
+ ScratchPad.recorded.should == [1, 2, @prc]
+ end
+
+ it "assigns required, optional, splatted arguments scanned and the passed block" do
+ @o.rb_scan_args([1, 2, 3, 4], "11*&", 4, @acc, &@prc).should == 4
+ ScratchPad.recorded.should == [1, 2, [3, 4], @prc]
+ end
+
+ it "assigns required arguments, nil for missing optional arguments and the passed block" do
+ @o.rb_scan_args([1], "12&", 4, @acc, &@prc).should == 1
+ ScratchPad.recorded.should == [1, nil, nil, @prc]
+ end
+
+ it "assigns required, splatted arguments and the passed block" do
+ @o.rb_scan_args([1, 2, 3], "1*&", 3, @acc, &@prc).should == 3
+ ScratchPad.recorded.should == [1, [2, 3], @prc]
+ end
+
+ it "assigns post-splat arguments" do
+ @o.rb_scan_args([1, 2, 3], "00*1", 2, @acc).should == 3
+ ScratchPad.recorded.should == [[1, 2], 3]
+ end
+
+ it "assigns required, optional, splat and post-splat arguments" do
+ @o.rb_scan_args([1, 2, 3, 4, 5], "11*1", 4, @acc).should == 5
+ ScratchPad.recorded.should == [1, 2, [3, 4], 5]
+ end
+
+ it "assigns required, splat, post-splat arguments" do
+ @o.rb_scan_args([1, 2, 3, 4], "10*1", 3, @acc).should == 4
+ ScratchPad.recorded.should == [1, [2, 3], 4]
+ end
+
+ it "assigns optional, splat, post-splat arguments" do
+ @o.rb_scan_args([1, 2, 3, 4], "01*1", 3, @acc).should == 4
+ ScratchPad.recorded.should == [1, [2, 3], 4]
+ end
+
+ it "assigns required, optional, splat, post-splat and block arguments" do
+ @o.rb_scan_args([1, 2, 3, 4, 5], "11*1&", 5, @acc, &@prc).should == 5
+ ScratchPad.recorded.should == [1, 2, [3, 4], 5, @prc]
+ end
+
+ it "assigns Hash arguments" do
+ h = {a: 1, b: 2}
+ @o.rb_scan_args([h], "0:", 1, @acc).should == 0
+ ScratchPad.recorded.should == [h]
+ end
+
+ it "assigns required and Hash arguments" do
+ h = {a: 1, b: 2}
+ @o.rb_scan_args([1, h], "1:", 2, @acc).should == 1
+ ScratchPad.recorded.should == [1, h]
+ end
+
+ it "assigns required and Hash arguments with optional Hash" do
+ @o.rb_scan_args([1], "1:", 2, @acc).should == 1
+ ScratchPad.recorded.should == [1, nil]
+ end
+
+ it "assigns required and Hash arguments with nil Hash" do
+ @o.rb_scan_args([1, nil], "1:", 2, @acc).should == 1
+ ScratchPad.recorded.should == [1, nil]
+ end
+
+ it "assigns required, optional, splat, post-splat, Hash and block arguments" do
+ h = {a: 1, b: 2}
+ @o.rb_scan_args([1, 2, 3, 4, 5, h], "11*1:&", 6, @acc, &@prc).should == 5
+ ScratchPad.recorded.should == [1, 2, [3, 4], 5, h, @prc]
+ end
+
+ # r43934
+ it "rejects non-keyword arguments" do
+ h = {1 => 2, 3 => 4}
+ lambda {
+ @o.rb_scan_args([h], "0:", 1, @acc)
+ }.should raise_error(ArgumentError)
+ ScratchPad.recorded.should == []
+ end
+
+ it "rejects required and non-keyword arguments" do
+ h = {1 => 2, 3 => 4}
+ lambda {
+ @o.rb_scan_args([1, h], "1:", 2, @acc)
+ }.should raise_error(ArgumentError)
+ ScratchPad.recorded.should == []
+ end
+
+ it "considers the hash as a post argument when there is a splat" do
+ h = {1 => 2, 3 => 4}
+ @o.rb_scan_args([1, 2, 3, 4, 5, h], "11*1:&", 6, @acc, &@prc).should == 6
+ ScratchPad.recorded.should == [1, 2, [3, 4, 5], h, nil, @prc]
+ end
+ end
+
+ platform_is wordsize: 64 do
+ describe "rb_long2int" do
+ it "raises a RangeError if the value is outside the range of a C int" do
+ lambda { @o.rb_long2int(0xffff_ffff_ffff) }.should raise_error(RangeError)
+ end
+ end
+
+ it "returns the C int value" do
+ @o.rb_long2int(1234).should == 1234
+ end
+ end
+
+ # #7896
+ describe "rb_iter_break" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "breaks a loop" do
+ 3.times do |i|
+ if i == 2
+ @o.rb_iter_break
+ end
+ ScratchPad << i
+ end
+ ScratchPad.recorded.should == [0, 1]
+ end
+
+ it "breaks the inner loop" do
+ 3.times do |i|
+ 3.times do |j|
+ if i == 1
+ @o.rb_iter_break
+ end
+ ScratchPad << [i, j]
+ end
+ end
+ ScratchPad.recorded.should == [[0, 0], [0, 1], [0, 2], [2, 0], [2, 1], [2, 2]]
+ end
+ end
+
+ describe "rb_sourcefile" do
+ it "returns the current ruby file" do
+ @o.rb_sourcefile.should == __FILE__
+ end
+ end
+
+ describe "rb_sourceline" do
+ it "returns the current ruby file" do
+ @o.rb_sourceline.should be_kind_of(Fixnum)
+ end
+ end
+
+end
diff --git a/spec/ruby/security/cve_2010_1330_spec.rb b/spec/ruby/security/cve_2010_1330_spec.rb
new file mode 100644
index 0000000000..c41a5e0a2e
--- /dev/null
+++ b/spec/ruby/security/cve_2010_1330_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../spec_helper'
+
+describe "String#gsub" do
+
+ it "resists CVE-2010-1330 by raising an exception on invalid UTF-8 bytes" do
+ # This original vulnerability talked about KCODE, which is no longer
+ # used. Instead we are forcing encodings here. But I think the idea is the
+ # same - we want to check that Ruby implementations raise an error on
+ # #gsub on a string in the UTF-8 encoding but with invalid an UTF-8 byte
+ # sequence.
+
+ str = "\xF6<script>"
+ str.force_encoding Encoding::ASCII_8BIT
+ str.gsub(/</, "&lt;").should == "\xF6&lt;script>".b
+ str.force_encoding Encoding::UTF_8
+ lambda {
+ str.gsub(/</, "&lt;")
+ }.should raise_error(ArgumentError, /invalid byte sequence in UTF-8/)
+ end
+
+end
diff --git a/spec/ruby/security/cve_2011_4815_spec.rb b/spec/ruby/security/cve_2011_4815_spec.rb
new file mode 100644
index 0000000000..02ef10d562
--- /dev/null
+++ b/spec/ruby/security/cve_2011_4815_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../spec_helper'
+
+describe :resists_cve_2011_4815, shared: true do
+ it "resists CVE-2011-4815 by having different hash codes in different processes" do
+ eval("(#{@method}).hash.to_s").should_not == ruby_exe("print (#{@method}).hash")
+ end
+end
+
+describe "Object#hash" do
+ it_behaves_like :resists_cve_2011_4815, 'Object.new'
+end
+
+describe "Integer#hash with a small value" do
+ it_behaves_like :resists_cve_2011_4815, '14'
+end
+
+describe "Integer#hash with a large value" do
+ it_behaves_like :resists_cve_2011_4815, '100000000000000000000000000000'
+end
+
+describe "Float#hash" do
+ it_behaves_like :resists_cve_2011_4815, '3.14'
+end
+
+describe "Rational#hash" do
+ it_behaves_like :resists_cve_2011_4815, 'Rational(1, 2)'
+end
+
+describe "Complex#hash" do
+ it_behaves_like :resists_cve_2011_4815, 'Complex(1, 2)'
+end
+
+describe "String#hash" do
+ it_behaves_like :resists_cve_2011_4815, '"abc"'
+end
+
+describe "Symbol#hash" do
+ ruby_bug "#13376", "2.3.0"..."2.3.4" do
+ it_behaves_like :resists_cve_2011_4815, ':a'
+ end
+end
+
+describe "Array#hash" do
+ it_behaves_like :resists_cve_2011_4815, '[1, 2, 3]'
+end
+
+describe "Hash#hash" do
+ it_behaves_like :resists_cve_2011_4815, '{a: 1, b: 2, c: 3}'
+end
diff --git a/spec/ruby/security/cve_2013_4164_spec.rb b/spec/ruby/security/cve_2013_4164_spec.rb
new file mode 100644
index 0000000000..94578cc7ce
--- /dev/null
+++ b/spec/ruby/security/cve_2013_4164_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../spec_helper'
+
+require 'json'
+
+describe "String#to_f" do
+
+ it "resists CVE-2013-4164 by converting very long Strings to a Float" do
+ "1.#{'1'*1000000}".to_f.should be_close(1.1111111111111112, TOLERANCE)
+ end
+
+end
+
+describe "JSON.parse" do
+
+ it "resists CVE-2013-4164 by converting very long Strings to a Float" do
+ JSON.parse("[1.#{'1'*1000000}]").first.should be_close(1.1111111111111112, TOLERANCE)
+ end
+
+end
diff --git a/spec/ruby/security/cve_2014_8080_spec.rb b/spec/ruby/security/cve_2014_8080_spec.rb
new file mode 100644
index 0000000000..e9d7fd320c
--- /dev/null
+++ b/spec/ruby/security/cve_2014_8080_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../spec_helper'
+
+require 'rexml/document'
+
+describe "REXML::Document.new" do
+
+ it "resists CVE-2014-8080 by raising an exception when entity expansion has grown too large" do
+ xml = <<XML
+ <?xml version="1.0" encoding="UTF-8" ?>
+ <!DOCTYPE x [
+ <!ENTITY % x0 "xxxxxxxxxx">
+ <!ENTITY % x1 "%x0;%x0;%x0;%x0;%x0;%x0;%x0;%x0;%x0;%x0;">
+ <!ENTITY % x2 "%x1;%x1;%x1;%x1;%x1;%x1;%x1;%x1;%x1;%x1;">
+ <!ENTITY % x3 "%x2;%x2;%x2;%x2;%x2;%x2;%x2;%x2;%x2;%x2;">
+ <!ENTITY % x4 "%x3;%x3;%x3;%x3;%x3;%x3;%x3;%x3;%x3;%x3;">
+ <!ENTITY % x5 "%x4;%x4;%x4;%x4;%x4;%x4;%x4;%x4;%x4;%x4;">
+ <!ENTITY % x6 "%x5;%x5;%x5;%x5;%x5;%x5;%x5;%x5;%x5;%x5;">
+ <!ENTITY % x7 "%x6;%x6;%x6;%x6;%x6;%x6;%x6;%x6;%x6;%x6;">
+ <!ENTITY % x8 "%x7;%x7;%x7;%x7;%x7;%x7;%x7;%x7;%x7;%x7;">
+ <!ENTITY % x9 "%x8;%x8;%x8;%x8;%x8;%x8;%x8;%x8;%x8;%x8;">
+ ]>
+ <x>
+ %x9;%x9;%x9;%x9;%x9;%x9;%x9;%x9;%x9;%x9;
+ </x>
+XML
+
+ lambda {
+ REXML::Document.new(xml).doctype.entities['x9'].value
+ }.should raise_error(REXML::ParseException, /entity expansion has grown too large/)
+ end
+
+end
diff --git a/spec/ruby/security/cve_2017_17742_spec.rb b/spec/ruby/security/cve_2017_17742_spec.rb
new file mode 100644
index 0000000000..72776cb497
--- /dev/null
+++ b/spec/ruby/security/cve_2017_17742_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../spec_helper'
+
+require "webrick"
+require "stringio"
+require "net/http"
+
+describe "WEBrick" do
+ describe "resists CVE-2017-17742" do
+ it "for a response splitting headers" do
+ config = WEBrick::Config::HTTP
+ res = WEBrick::HTTPResponse.new config
+ res['X-header'] = "malicious\r\nCookie: hack"
+ io = StringIO.new
+ res.send_response io
+ io.rewind
+ res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
+ res.code.should == '500'
+ io.string.should_not =~ /hack/
+ end
+
+ it "for a response splitting cookie headers" do
+ user_input = "malicious\r\nCookie: hack"
+ config = WEBrick::Config::HTTP
+ res = WEBrick::HTTPResponse.new config
+ res.cookies << WEBrick::Cookie.new('author', user_input)
+ io = StringIO.new
+ res.send_response io
+ io.rewind
+ res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
+ res.code.should == '500'
+ io.string.should_not =~ /hack/
+ end
+ end
+end
diff --git a/spec/ruby/security/cve_2018_16396_spec.rb b/spec/ruby/security/cve_2018_16396_spec.rb
new file mode 100644
index 0000000000..e462e0022d
--- /dev/null
+++ b/spec/ruby/security/cve_2018_16396_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../spec_helper'
+
+describe "Array#pack" do
+
+ it "resists CVE-2018-16396 by tainting output based on input" do
+ "aAZBbHhuMmPp".each_char do |f|
+ ["123456".taint].pack(f).tainted?.should be_true
+ end
+ end
+
+end
+
+describe "String#unpack" do
+
+ it "resists CVE-2018-16396 by tainting output based on input" do
+ "aAZBbHhuMm".each_char do |f|
+ "123456".taint.unpack(f).first.tainted?.should be_true
+ end
+ end
+
+end
diff --git a/spec/ruby/security/cve_2018_6914_spec.rb b/spec/ruby/security/cve_2018_6914_spec.rb
new file mode 100644
index 0000000000..a40225799d
--- /dev/null
+++ b/spec/ruby/security/cve_2018_6914_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../spec_helper'
+
+require 'tempfile'
+
+describe "CVE-2018-6914 is resisted by" do
+ before :all do
+ @traversal_path = Array.new(Dir.pwd.split('/').count, '..').join('/') + Dir.pwd + '/'
+ @traversal_path.delete!(':') if /mswin|mingw/ =~ RUBY_PLATFORM
+ end
+
+ it "Tempfile.open by deleting separators" do
+ begin
+ expect = Dir.glob(@traversal_path + '*').count
+ t = Tempfile.open([@traversal_path, 'foo'])
+ actual = Dir.glob(@traversal_path + '*').count
+ actual.should == expect
+ ensure
+ t.close!
+ end
+ end
+
+ it "Tempfile.new by deleting separators" do
+ begin
+ expect = Dir.glob(@traversal_path + '*').count
+ t = Tempfile.new(@traversal_path + 'foo')
+ actual = Dir.glob(@traversal_path + '*').count
+ actual.should == expect
+ ensure
+ t.close!
+ end
+ end
+
+ it "Tempfile.create by deleting separators" do
+ expect = Dir.glob(@traversal_path + '*').count
+ Tempfile.create(@traversal_path + 'foo') do
+ actual = Dir.glob(@traversal_path + '*').count
+ actual.should == expect
+ end
+ end
+
+ it "Dir.mktmpdir by deleting separators" do
+ expect = Dir.glob(@traversal_path + '*').count
+ Dir.mktmpdir(@traversal_path + 'foo') do
+ actual = Dir.glob(@traversal_path + '*').count
+ actual.should == expect
+ end
+ end
+
+ it "Dir.mktmpdir with an array by deleting separators" do
+ expect = Dir.glob(@traversal_path + '*').count
+ Dir.mktmpdir([@traversal_path, 'foo']) do
+ actual = Dir.glob(@traversal_path + '*').count
+ actual.should == expect
+ end
+ end
+end
diff --git a/spec/ruby/security/cve_2018_8778_spec.rb b/spec/ruby/security/cve_2018_8778_spec.rb
new file mode 100644
index 0000000000..a264a8581e
--- /dev/null
+++ b/spec/ruby/security/cve_2018_8778_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../spec_helper'
+
+describe "String#unpack" do
+
+ it "resists CVE-2018-8778 by raising an exception when a position indicator is larger than a native integer" do
+ pos = (1 << PlatformGuard::POINTER_SIZE) - 99
+ lambda {
+ "0123456789".unpack("@#{pos}C10")
+ }.should raise_error(RangeError, /pack length too big/)
+ end
+
+end
diff --git a/spec/ruby/security/cve_2018_8779_spec.rb b/spec/ruby/security/cve_2018_8779_spec.rb
new file mode 100644
index 0000000000..9659b321ee
--- /dev/null
+++ b/spec/ruby/security/cve_2018_8779_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../spec_helper'
+
+require 'socket'
+require 'tempfile'
+
+platform_is_not :windows do
+ describe "CVE-2018-8779 is resisted by" do
+ before :each do
+ tmpfile = Tempfile.new("s")
+ @path = tmpfile.path
+ tmpfile.close(true)
+ end
+
+ after :each do
+ File.unlink @path if @path && File.socket?(@path)
+ end
+
+ it "UNIXServer.open by raising an exception when there is a NUL byte" do
+ lambda {
+ UNIXServer.open(@path+"\0")
+ }.should raise_error(ArgumentError, /(path name|string) contains null byte/)
+ end
+
+ it "UNIXSocket.open by raising an exception when there is a NUL byte" do
+ lambda {
+ UNIXSocket.open(@path+"\0")
+ }.should raise_error(ArgumentError, /(path name|string) contains null byte/)
+ end
+ end
+end
diff --git a/spec/ruby/security/cve_2018_8780_spec.rb b/spec/ruby/security/cve_2018_8780_spec.rb
new file mode 100644
index 0000000000..febb1de51d
--- /dev/null
+++ b/spec/ruby/security/cve_2018_8780_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../spec_helper'
+
+describe "CVE-2018-8780 is resisted by" do
+ before :all do
+ @root = File.realpath(tmp(""))
+ end
+
+ it "Dir.glob by raising an exception when there is a NUL byte" do
+ lambda {
+ Dir.glob([[@root, File.join(@root, "*")].join("\0")])
+ }.should raise_error(ArgumentError, /(path name|string) contains null byte/)
+ end
+
+ it "Dir.entries by raising an exception when there is a NUL byte" do
+ lambda {
+ Dir.entries(@root+"\0")
+ }.should raise_error(ArgumentError, /(path name|string) contains null byte/)
+ end
+
+ it "Dir.foreach by raising an exception when there is a NUL byte" do
+ lambda {
+ Dir.foreach(@root+"\0").to_a
+ }.should raise_error(ArgumentError, /(path name|string) contains null byte/)
+ end
+
+ ruby_version_is "2.4" do
+ it "Dir.empty? by raising an exception when there is a NUL byte" do
+ lambda {
+ Dir.empty?(@root+"\0")
+ }.should raise_error(ArgumentError, /(path name|string) contains null byte/)
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "Dir.children by raising an exception when there is a NUL byte" do
+ lambda {
+ Dir.children(@root+"\0")
+ }.should raise_error(ArgumentError, /(path name|string) contains null byte/)
+ end
+
+ it "Dir.each_child by raising an exception when there is a NUL byte" do
+ lambda {
+ Dir.each_child(@root+"\0").to_a
+ }.should raise_error(ArgumentError, /(path name|string) contains null byte/)
+ end
+ end
+end
diff --git a/spec/ruby/shared/basicobject/method_missing.rb b/spec/ruby/shared/basicobject/method_missing.rb
new file mode 100644
index 0000000000..0759dd9606
--- /dev/null
+++ b/spec/ruby/shared/basicobject/method_missing.rb
@@ -0,0 +1,124 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/basicobject/method_missing'
+
+describe :method_missing_defined_module, shared: true do
+ describe "for a Module with #method_missing defined" do
+ it "is not called when a defined method is called" do
+ @object.method_public.should == :module_public_method
+ end
+
+ it "is called when a not defined method is called" do
+ @object.not_defined_method.should == :module_method_missing
+ end
+
+ it "is called when a protected method is called" do
+ @object.method_protected.should == :module_method_missing
+ end
+
+ it "is called when a private method is called" do
+ @object.method_private.should == :module_method_missing
+ end
+ end
+end
+
+describe :method_missing_module, shared: true do
+ describe "for a Module" do
+ it "raises a NoMethodError when an undefined method is called" do
+ lambda { @object.no_such_method }.should raise_error(NoMethodError)
+ end
+
+ it "raises a NoMethodError when a protected method is called" do
+ lambda { @object.method_protected }.should raise_error(NoMethodError)
+ end
+
+ it "raises a NoMethodError when a private method is called" do
+ lambda { @object.method_private }.should raise_error(NoMethodError)
+ end
+ end
+end
+
+describe :method_missing_defined_class, shared: true do
+ describe "for a Class with #method_missing defined" do
+ it "is not called when a defined method is called" do
+ @object.method_public.should == :class_public_method
+ end
+
+ it "is called when an undefined method is called" do
+ @object.no_such_method.should == :class_method_missing
+ end
+
+ it "is called when an protected method is called" do
+ @object.method_protected.should == :class_method_missing
+ end
+
+ it "is called when an private method is called" do
+ @object.method_private.should == :class_method_missing
+ end
+ end
+end
+
+describe :method_missing_class, shared: true do
+ describe "for a Class" do
+ it "raises a NoMethodError when an undefined method is called" do
+ lambda { @object.no_such_method }.should raise_error(NoMethodError)
+ end
+
+ it "raises a NoMethodError when a protected method is called" do
+ lambda { @object.method_protected }.should raise_error(NoMethodError)
+ end
+
+ it "raises a NoMethodError when a private method is called" do
+ lambda { @object.method_private }.should raise_error(NoMethodError)
+ end
+ end
+end
+
+describe :method_missing_defined_instance, shared: true do
+ describe "for an instance with #method_missing defined" do
+ before :each do
+ @instance = @object.new
+ end
+
+ it "is not called when a defined method is called" do
+ @instance.method_public.should == :instance_public_method
+ end
+
+ it "is called when an undefined method is called" do
+ @instance.no_such_method.should == :instance_method_missing
+ end
+
+ it "is called when an protected method is called" do
+ @instance.method_protected.should == :instance_method_missing
+ end
+
+ it "is called when an private method is called" do
+ @instance.method_private.should == :instance_method_missing
+ end
+ end
+end
+
+describe :method_missing_instance, shared: true do
+ describe "for an instance" do
+ it "raises a NoMethodError when an undefined method is called" do
+ lambda { @object.new.no_such_method }.should raise_error(NoMethodError)
+ end
+
+ it "raises a NoMethodError when a protected method is called" do
+ lambda { @object.new.method_protected }.should raise_error(NoMethodError)
+ end
+
+ it "raises a NoMethodError when a private method is called" do
+ lambda { @object.new.method_private }.should raise_error(NoMethodError)
+ end
+
+ it 'sets the receiver of the raised NoMethodError' do
+ obj = @object.new
+
+ begin
+ obj.method_private
+ rescue NoMethodError => error
+ (error.receiver == obj).should == true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/shared/basicobject/send.rb b/spec/ruby/shared/basicobject/send.rb
new file mode 100644
index 0000000000..2b79ab4c2c
--- /dev/null
+++ b/spec/ruby/shared/basicobject/send.rb
@@ -0,0 +1,117 @@
+module SendSpecs
+end
+
+describe :basicobject_send, shared: true do
+ it "invokes the named method" do
+ class SendSpecs::Foo
+ def bar
+ 'done'
+ end
+ end
+ SendSpecs::Foo.new.send(@method, :bar).should == 'done'
+ end
+
+ it "accepts a String method name" do
+ class SendSpecs::Foo
+ def bar
+ 'done'
+ end
+ end
+ SendSpecs::Foo.new.send(@method, 'bar').should == 'done'
+ end
+
+ it "invokes a class method if called on a class" do
+ class SendSpecs::Foo
+ def self.bar
+ 'done'
+ end
+ end
+ SendSpecs::Foo.send(@method, :bar).should == 'done'
+ end
+
+ it "raises a TypeError if the method name is not a string or symbol" do
+ -> { SendSpecs.send(@method, nil) }.should raise_error(TypeError, /not a symbol nor a string/)
+ -> { SendSpecs.send(@method, 42) }.should raise_error(TypeError, /not a symbol nor a string/)
+ -> { SendSpecs.send(@method, 3.14) }.should raise_error(TypeError, /not a symbol nor a string/)
+ -> { SendSpecs.send(@method, true) }.should raise_error(TypeError, /not a symbol nor a string/)
+ end
+
+ it "raises a NameError if the corresponding method can't be found" do
+ class SendSpecs::Foo
+ def bar
+ 'done'
+ end
+ end
+ lambda { SendSpecs::Foo.new.send(@method, :syegsywhwua) }.should raise_error(NameError)
+ end
+
+ it "raises a NameError if the corresponding singleton method can't be found" do
+ class SendSpecs::Foo
+ def self.bar
+ 'done'
+ end
+ end
+ lambda { SendSpecs::Foo.send(@method, :baz) }.should raise_error(NameError)
+ end
+
+ it "raises an ArgumentError if no arguments are given" do
+ class SendSpecs::Foo; end
+ lambda { SendSpecs::Foo.new.send @method }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if called with more arguments than available parameters" do
+ class SendSpecs::Foo
+ def bar; end
+ end
+
+ lambda { SendSpecs::Foo.new.send(@method, :bar, :arg) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if called with fewer arguments than required parameters" do
+ class SendSpecs::Foo
+ def foo(arg); end
+ end
+
+ lambda { SendSpecs::Foo.new.send(@method, :foo) }.should raise_error(ArgumentError)
+ end
+
+ it "succeeds if passed an arbitrary number of arguments as a splat parameter" do
+ class SendSpecs::Foo
+ def baz(*args) args end
+ end
+
+ begin
+ SendSpecs::Foo.new.send(@method, :baz).should == []
+ SendSpecs::Foo.new.send(@method, :baz, :quux).should == [:quux]
+ SendSpecs::Foo.new.send(@method, :baz, :quux, :foo).should == [:quux, :foo]
+ rescue
+ fail
+ end
+ end
+
+ it "succeeds when passing 1 or more arguments as a required and a splat parameter" do
+ class SendSpecs::Foo
+ def baz(first, *rest) [first, *rest] end
+ end
+
+ SendSpecs::Foo.new.send(@method, :baz, :quux).should == [:quux]
+ SendSpecs::Foo.new.send(@method, :baz, :quux, :foo).should == [:quux, :foo]
+ end
+
+ it "succeeds when passing 0 arguments to a method with one parameter with a default" do
+ class SendSpecs::Foo
+ def foo(first = true) first end
+ end
+
+ begin
+ SendSpecs::Foo.new.send(@method, :foo).should == true
+ SendSpecs::Foo.new.send(@method, :foo, :arg).should == :arg
+ rescue
+ fail
+ end
+ end
+
+ it "has a negative arity" do
+ method(@method).arity.should < 0
+ end
+end
diff --git a/spec/ruby/shared/enumerator/each.rb b/spec/ruby/shared/enumerator/each.rb
new file mode 100644
index 0000000000..bbab86fed7
--- /dev/null
+++ b/spec/ruby/shared/enumerator/each.rb
@@ -0,0 +1,89 @@
+# -*- encoding: us-ascii -*-
+
+describe :enum_each, shared: true do
+ before :each do
+ object_each_with_arguments = Object.new
+ def object_each_with_arguments.each_with_arguments(arg, *args)
+ yield arg, *args
+ :method_returned
+ end
+
+ @enum_with_arguments = object_each_with_arguments.to_enum(:each_with_arguments, :arg0, :arg1, :arg2)
+
+ @enum_with_yielder = Enumerator.new {|y| y.yield :ok}
+ end
+
+ it "yields each element of self to the given block" do
+ acc = []
+ [1,2,3].to_enum.each {|e| acc << e }
+ acc.should == [1,2,3]
+ end
+
+ it "calls #each on the object given in the constructor by default" do
+ each = mock('each')
+ each.should_receive(:each)
+ each.to_enum.each {|e| e }
+ end
+
+ it "calls #each on the underlying object until it's exhausted" do
+ each = mock('each')
+ each.should_receive(:each).and_yield(1).and_yield(2).and_yield(3)
+ acc = []
+ each.to_enum.each {|e| acc << e }
+ acc.should == [1,2,3]
+ end
+
+ it "calls the method given in the constructor instead of #each" do
+ each = mock('peach')
+ each.should_receive(:peach)
+ each.to_enum(:peach).each {|e| e }
+ end
+
+ it "calls the method given in the constructor until it's exhausted" do
+ each = mock('each')
+ each.should_receive(:each).and_yield(1).and_yield(2).and_yield(3)
+ acc = []
+ each.to_enum.each {|e| acc << e }
+ acc.should == [1,2,3]
+ end
+
+ it "raises a NoMethodError if the object doesn't respond to #each" do
+ enum = Object.new.to_enum
+ lambda do
+ enum.each { |e| e }
+ end.should raise_error(NoMethodError)
+ end
+
+ it "returns self if not given arguments and not given a block" do
+ @enum_with_arguments.each.should equal(@enum_with_arguments)
+
+ @enum_with_yielder.each.should equal(@enum_with_yielder)
+ end
+
+ it "returns the same value from receiver.each if block is given" do
+ @enum_with_arguments.each {}.should equal(:method_returned)
+ end
+
+ it "passes given arguments at initialized to receiver.each" do
+ @enum_with_arguments.each.to_a.should == [[:arg0, :arg1, :arg2]]
+ end
+
+ it "requires multiple arguments" do
+ Enumerator.instance_method(:each).arity.should < 0
+ end
+
+ it "appends given arguments to receiver.each" do
+ @enum_with_arguments.each(:each0, :each1).to_a.should == [[:arg0, :arg1, :arg2, :each0, :each1]]
+ @enum_with_arguments.each(:each2, :each3).to_a.should == [[:arg0, :arg1, :arg2, :each2, :each3]]
+ end
+
+ it "returns the same value from receiver.each if block and arguments are given" do
+ @enum_with_arguments.each(:each1, :each2) {}.should equal(:method_returned)
+ end
+
+ it "returns new Enumerator if given arguments but not given a block" do
+ ret = @enum_with_arguments.each 1
+ ret.should be_an_instance_of(Enumerator)
+ ret.should_not equal(@enum_with_arguments)
+ end
+end
diff --git a/spec/ruby/shared/enumerator/enum_cons.rb b/spec/ruby/shared/enumerator/enum_cons.rb
new file mode 100644
index 0000000000..fec0f4766e
--- /dev/null
+++ b/spec/ruby/shared/enumerator/enum_cons.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/enumerator/classes'
+
+describe :enum_cons, shared: true do
+ it "returns an enumerator of the receiver with iteration of each_cons for each array of n concecutive elements" do
+ a = []
+ enum = EnumSpecs::Numerous.new.enum_cons(3)
+ enum.each {|x| a << x}
+ enum.should be_an_instance_of(Enumerator)
+ a.should == [[2, 5, 3], [5, 3, 6], [3, 6, 1], [6, 1, 4]]
+ end
+end
diff --git a/spec/ruby/shared/enumerator/enum_for.rb b/spec/ruby/shared/enumerator/enum_for.rb
new file mode 100644
index 0000000000..9030ffbd7d
--- /dev/null
+++ b/spec/ruby/shared/enumerator/enum_for.rb
@@ -0,0 +1,50 @@
+describe :enum_for, shared: true do
+ it "is defined in Kernel" do
+ Kernel.method_defined?(@method).should be_true
+ end
+
+ it "returns a new enumerator" do
+ "abc".send(@method).should be_an_instance_of(Enumerator)
+ end
+
+ it "defaults the first argument to :each" do
+ enum = [1,2].send(@method)
+ enum.map { |v| v }.should == [1,2].each { |v| v }
+ end
+
+ it "exposes multi-arg yields as an array" do
+ o = Object.new
+ def o.each
+ yield :a
+ yield :b1, :b2
+ yield [:c]
+ yield :d1, :d2
+ yield :e1, :e2, :e3
+ end
+
+ enum = o.send(@method)
+ enum.next.should == :a
+ enum.next.should == [:b1, :b2]
+ enum.next.should == [:c]
+ enum.next.should == [:d1, :d2]
+ enum.next.should == [:e1, :e2, :e3]
+ end
+
+ it "uses the passed block's value to calculate the size of the enumerator" do
+ Object.new.enum_for { 100 }.size.should == 100
+ end
+
+ it "defers the evaluation of the passed block until #size is called" do
+ ScratchPad.record []
+
+ enum = Object.new.enum_for do
+ ScratchPad << :called
+ 100
+ end
+
+ ScratchPad.recorded.should be_empty
+
+ enum.size
+ ScratchPad.recorded.should == [:called]
+ end
+end
diff --git a/spec/ruby/shared/enumerator/new.rb b/spec/ruby/shared/enumerator/new.rb
new file mode 100644
index 0000000000..2eb39e0abd
--- /dev/null
+++ b/spec/ruby/shared/enumerator/new.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+
+describe :enum_new, shared: true do
+ it "creates a new custom enumerator with the given object, iterator and arguments" do
+ enum = Enumerator.new(1, :upto, 3)
+ enum.should be_an_instance_of(Enumerator)
+ end
+
+ it "creates a new custom enumerator that responds to #each" do
+ enum = Enumerator.new(1, :upto, 3)
+ enum.respond_to?(:each).should == true
+ end
+
+ it "creates a new custom enumerator that runs correctly" do
+ Enumerator.new(1, :upto, 3).map{|x|x}.should == [1,2,3]
+ end
+
+ it "aliases the second argument to :each" do
+ Enumerator.new(1..2).to_a.should == Enumerator.new(1..2, :each).to_a
+ end
+
+ it "doesn't check for the presence of the iterator method" do
+ Enumerator.new(nil).should be_an_instance_of(Enumerator)
+ end
+
+ it "uses the latest define iterator method" do
+ class StrangeEach
+ def each
+ yield :foo
+ end
+ end
+ enum = Enumerator.new(StrangeEach.new)
+ enum.to_a.should == [:foo]
+ class StrangeEach
+ def each
+ yield :bar
+ end
+ end
+ enum.to_a.should == [:bar]
+ end
+
+end
diff --git a/spec/ruby/shared/enumerator/next.rb b/spec/ruby/shared/enumerator/next.rb
new file mode 100644
index 0000000000..dd9a2a0f81
--- /dev/null
+++ b/spec/ruby/shared/enumerator/next.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+
+describe :enum_next, shared: true do
+
+ before :each do
+ @enum = 1.upto(3)
+ end
+
+ it "returns the next element of the enumeration" do
+ @enum.next.should == 1
+ @enum.next.should == 2
+ @enum.next.should == 3
+ end
+
+ it "raises a StopIteration exception at the end of the stream" do
+ 3.times { @enum.next }
+ lambda { @enum.next }.should raise_error(StopIteration)
+ end
+
+ it "cannot be called again until the enumerator is rewound" do
+ 3.times { @enum.next }
+ lambda { @enum.next }.should raise_error(StopIteration)
+ lambda { @enum.next }.should raise_error(StopIteration)
+ lambda { @enum.next }.should raise_error(StopIteration)
+ @enum.rewind
+ @enum.next.should == 1
+ end
+end
diff --git a/spec/ruby/shared/enumerator/rewind.rb b/spec/ruby/shared/enumerator/rewind.rb
new file mode 100644
index 0000000000..a3c14ec496
--- /dev/null
+++ b/spec/ruby/shared/enumerator/rewind.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+
+describe :enum_rewind, shared: true do
+
+ before :each do
+ @enum = 1.upto(3)
+ end
+
+ it "resets the enumerator to its initial state" do
+ @enum.next.should == 1
+ @enum.next.should == 2
+ @enum.rewind
+ @enum.next.should == 1
+ end
+
+ it "returns self" do
+ @enum.rewind.should == @enum
+ end
+
+ it "has no effect on a new enumerator" do
+ @enum.rewind
+ @enum.next.should == 1
+ end
+
+ it "has no effect if called multiple, consecutive times" do
+ @enum.next.should == 1
+ @enum.rewind
+ @enum.rewind
+ @enum.next.should == 1
+ end
+
+ it "works with peek to reset the position" do
+ @enum.next
+ @enum.next
+ @enum.rewind
+ @enum.next
+ @enum.peek.should == 2
+ end
+end
diff --git a/spec/ruby/shared/enumerator/with_index.rb b/spec/ruby/shared/enumerator/with_index.rb
new file mode 100644
index 0000000000..4f459bd9a8
--- /dev/null
+++ b/spec/ruby/shared/enumerator/with_index.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+
+describe :enum_with_index, shared: true do
+
+ require_relative '../../fixtures/enumerator/classes'
+
+ before :each do
+ @enum = [1, 2, 3, 4].to_enum
+ end
+
+ it "passes each element and its index to block" do
+ @a = []
+ @enum.send(@method) { |o, i| @a << [o, i] }
+ @a.should == [[1, 0], [2, 1], [3, 2], [4, 3]]
+ end
+
+ it "returns the object being enumerated when given a block" do
+ [1, 2, 3, 4].should == @enum.send(@method) { |o, i| :glark }
+ end
+
+ it "binds splat arguments properly" do
+ acc = []
+ @enum.send(@method) { |*b| c,d = b; acc << c; acc << d }
+ [1, 0, 2, 1, 3, 2, 4, 3].should == acc
+ end
+
+ it "returns an enumerator if no block is supplied" do
+ ewi = @enum.send(@method)
+ ewi.should be_an_instance_of(Enumerator)
+ ewi.to_a.should == [[1, 0], [2, 1], [3, 2], [4, 3]]
+ end
+end
diff --git a/spec/ruby/shared/enumerator/with_object.rb b/spec/ruby/shared/enumerator/with_object.rb
new file mode 100644
index 0000000000..c2e3a79366
--- /dev/null
+++ b/spec/ruby/shared/enumerator/with_object.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+
+describe :enum_with_object, shared: true do
+ before :each do
+ @enum = [:a, :b].to_enum
+ @memo = ''
+ @block_params = @enum.send(@method, @memo).to_a
+ end
+
+ it "receives an argument" do
+ @enum.method(@method).arity.should == 1
+ end
+
+ context "with block" do
+ it "returns the given object" do
+ ret = @enum.send(@method, @memo) do |elm, memo|
+ # nothing
+ end
+ ret.should equal(@memo)
+ end
+
+ context "the block parameter" do
+ it "passes each element to first parameter" do
+ @block_params[0][0].should equal(:a)
+ @block_params[1][0].should equal(:b)
+ end
+
+ it "passes the given object to last parameter" do
+ @block_params[0][1].should equal(@memo)
+ @block_params[1][1].should equal(@memo)
+ end
+ end
+ end
+
+ context "without block" do
+ it "returns new Enumerator" do
+ ret = @enum.send(@method, @memo)
+ ret.should be_an_instance_of(Enumerator)
+ ret.should_not equal(@enum)
+ end
+ end
+end
diff --git a/spec/ruby/shared/fiber/resume.rb b/spec/ruby/shared/fiber/resume.rb
new file mode 100644
index 0000000000..e2d30d781b
--- /dev/null
+++ b/spec/ruby/shared/fiber/resume.rb
@@ -0,0 +1,79 @@
+describe :fiber_resume, shared: true do
+ it "can be invoked from the root Fiber" do
+ fiber = Fiber.new { :fiber }
+ fiber.send(@method).should == :fiber
+ end
+
+ it "raises a FiberError if invoked from a different Thread" do
+ fiber = Fiber.new { 42 }
+ Thread.new do
+ -> {
+ fiber.send(@method)
+ }.should raise_error(FiberError)
+ end.join
+
+ # Check the Fiber can still be used
+ fiber.send(@method).should == 42
+ end
+
+ it "passes control to the beginning of the block on first invocation" do
+ invoked = false
+ fiber = Fiber.new { invoked = true }
+ fiber.send(@method)
+ invoked.should be_true
+ end
+
+ it "returns the last value encountered on first invocation" do
+ fiber = Fiber.new { 1+1; true }
+ fiber.send(@method).should be_true
+ end
+
+ it "runs until the end of the block" do
+ obj = mock('obj')
+ obj.should_receive(:do).once
+ fiber = Fiber.new { 1 + 2; a = "glark"; obj.do }
+ fiber.send(@method)
+ end
+
+ it "runs until Fiber.yield" do
+ obj = mock('obj')
+ obj.should_not_receive(:do)
+ fiber = Fiber.new { 1 + 2; Fiber.yield; obj.do }
+ fiber.send(@method)
+ end
+
+ it "resumes from the last call to Fiber.yield on subsequent invocations" do
+ fiber = Fiber.new { Fiber.yield :first; :second }
+ fiber.send(@method).should == :first
+ fiber.send(@method).should == :second
+ end
+
+ it "accepts any number of arguments" do
+ fiber = Fiber.new { |a| }
+ lambda { fiber.send(@method, *(1..10).to_a) }.should_not raise_error
+ end
+
+ it "sets the block parameters to its arguments on the first invocation" do
+ first = mock('first')
+ first.should_receive(:arg).with(:first).twice
+ fiber = Fiber.new { |arg| first.arg arg; Fiber.yield; first.arg arg; }
+ fiber.send(@method, :first)
+ fiber.send(@method, :second)
+ end
+
+ it "raises a FiberError if the Fiber is dead" do
+ fiber = Fiber.new { true }
+ fiber.send(@method)
+ lambda { fiber.send(@method) }.should raise_error(FiberError)
+ end
+
+ it "raises a LocalJumpError if the block includes a return statement" do
+ fiber = Fiber.new { return; }
+ lambda { fiber.send(@method) }.should raise_error(LocalJumpError)
+ end
+
+ it "raises a LocalJumpError if the block includes a break statement" do
+ fiber = Fiber.new { break; }
+ lambda { fiber.send(@method) }.should raise_error(LocalJumpError)
+ end
+end
diff --git a/spec/ruby/shared/file/blockdev.rb b/spec/ruby/shared/file/blockdev.rb
new file mode 100644
index 0000000000..b0b3ea040a
--- /dev/null
+++ b/spec/ruby/shared/file/blockdev.rb
@@ -0,0 +1,9 @@
+describe :file_blockdev, shared: true do
+ it "returns true/false depending if the named file is a block device" do
+ @object.send(@method, tmp("")).should == false
+ end
+
+ it "accepts an object that has a #to_path method" do
+ @object.send(@method, mock_to_path(tmp(""))).should == false
+ end
+end
diff --git a/spec/ruby/shared/file/chardev.rb b/spec/ruby/shared/file/chardev.rb
new file mode 100644
index 0000000000..8a7a89fd05
--- /dev/null
+++ b/spec/ruby/shared/file/chardev.rb
@@ -0,0 +1,9 @@
+describe :file_chardev, shared: true do
+ it "returns true/false depending if the named file is a char device" do
+ @object.send(@method, tmp("")).should == false
+ end
+
+ it "accepts an object that has a #to_path method" do
+ @object.send(@method, mock_to_path(tmp(""))).should == false
+ end
+end
diff --git a/spec/ruby/shared/file/directory.rb b/spec/ruby/shared/file/directory.rb
new file mode 100644
index 0000000000..67e939bf16
--- /dev/null
+++ b/spec/ruby/shared/file/directory.rb
@@ -0,0 +1,66 @@
+describe :file_directory, shared: true do
+ before :each do
+ @dir = tmp("file_directory")
+ @file = tmp("file_directory.txt")
+
+ mkdir_p @dir
+ touch @file
+ end
+
+ after :each do
+ rm_r @dir, @file
+ end
+
+ it "returns true if the argument is a directory" do
+ @object.send(@method, @dir).should be_true
+ end
+
+ it "returns false if the argument is not a directory" do
+ @object.send(@method, @file).should be_false
+ end
+
+ it "accepts an object that has a #to_path method" do
+ @object.send(@method, mock_to_path(@dir)).should be_true
+ end
+
+ it "raises a TypeError when passed an Integer" do
+ lambda { @object.send(@method, 1) }.should raise_error(TypeError)
+ lambda { @object.send(@method, bignum_value) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { @object.send(@method, nil) }.should raise_error(TypeError)
+ end
+end
+
+describe :file_directory_io, shared: true do
+ before :each do
+ @dir = tmp("file_directory_io")
+ @file = tmp("file_directory_io.txt")
+
+ mkdir_p @dir
+ touch @file
+ end
+
+ after :each do
+ rm_r @dir, @file
+ end
+
+ it "returns false if the argument is an IO that's not a directory" do
+ @object.send(@method, STDIN).should be_false
+ end
+
+ platform_is_not :windows do
+ it "returns true if the argument is an IO that is a directory" do
+ File.open(@dir, "r") do |f|
+ @object.send(@method, f).should be_true
+ end
+ end
+ end
+
+ it "calls #to_io to convert a non-IO object" do
+ io = mock('FileDirectoryIO')
+ io.should_receive(:to_io).and_return(STDIN)
+ @object.send(@method, io).should be_false
+ end
+end
diff --git a/spec/ruby/shared/file/executable.rb b/spec/ruby/shared/file/executable.rb
new file mode 100644
index 0000000000..5b21fb0d97
--- /dev/null
+++ b/spec/ruby/shared/file/executable.rb
@@ -0,0 +1,48 @@
+describe :file_executable, shared: true do
+ before :each do
+ @file1 = tmp('temp1.txt')
+ @file2 = tmp('temp2.txt')
+
+ touch @file1
+ touch @file2
+
+ File.chmod(0755, @file1)
+ end
+
+ after :each do
+ rm_r @file1, @file2
+ end
+
+ platform_is_not :windows do
+ it "returns true if named file is executable by the effective user id of the process, otherwise false" do
+ @object.send(@method, '/etc/passwd').should == false
+ @object.send(@method, @file1).should == true
+ @object.send(@method, @file2).should == false
+ end
+
+ it "returns true if the argument is an executable file" do
+ @object.send(@method, @file1).should == true
+ @object.send(@method, @file2).should == false
+ end
+
+ it "accepts an object that has a #to_path method" do
+ @object.send(@method, mock_to_path(@file1)).should == true
+ end
+ end
+
+ it "raises an ArgumentError if not passed one argument" do
+ lambda { @object.send(@method) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if not passed a String type" do
+ lambda { @object.send(@method, 1) }.should raise_error(TypeError)
+ lambda { @object.send(@method, nil) }.should raise_error(TypeError)
+ lambda { @object.send(@method, false) }.should raise_error(TypeError)
+ end
+end
+
+describe :file_executable_missing, shared: true do
+ it "returns false if the file does not exist" do
+ @object.send(@method, 'fake_file').should == false
+ end
+end
diff --git a/spec/ruby/shared/file/executable_real.rb b/spec/ruby/shared/file/executable_real.rb
new file mode 100644
index 0000000000..2b1bf8f585
--- /dev/null
+++ b/spec/ruby/shared/file/executable_real.rb
@@ -0,0 +1,46 @@
+describe :file_executable_real, shared: true do
+ before :each do
+ @file1 = tmp('temp1.txt.exe')
+ @file2 = tmp('temp2.txt')
+
+ touch @file1
+ touch @file2
+
+ File.chmod(0755, @file1)
+ end
+
+ after :each do
+ rm_r @file1, @file2
+ end
+
+ platform_is_not :windows do
+ it "returns true if the file its an executable" do
+ @object.send(@method, @file1).should == true
+ @object.send(@method, @file2).should == false
+ end
+
+ it "accepts an object that has a #to_path method" do
+ @object.send(@method, mock_to_path(@file1)).should == true
+ end
+ end
+
+ it "returns true if named file is readable by the real user id of the process, otherwise false" do
+ @object.send(@method, @file1).should == true
+ end
+
+ it "raises an ArgumentError if not passed one argument" do
+ lambda { @object.send(@method) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if not passed a String type" do
+ lambda { @object.send(@method, 1) }.should raise_error(TypeError)
+ lambda { @object.send(@method, nil) }.should raise_error(TypeError)
+ lambda { @object.send(@method, false) }.should raise_error(TypeError)
+ end
+end
+
+describe :file_executable_real_missing, shared: true do
+ it "returns false if the file does not exist" do
+ @object.send(@method, 'fake_file').should == false
+ end
+end
diff --git a/spec/ruby/shared/file/exist.rb b/spec/ruby/shared/file/exist.rb
new file mode 100644
index 0000000000..1557f01a82
--- /dev/null
+++ b/spec/ruby/shared/file/exist.rb
@@ -0,0 +1,24 @@
+describe :file_exist, shared: true do
+ it "returns true if the file exist" do
+ @object.send(@method, __FILE__).should == true
+ @object.send(@method, 'a_fake_file').should == false
+ end
+
+ it "returns true if the file exist using the alias exists?" do
+ @object.send(@method, __FILE__).should == true
+ @object.send(@method, 'a_fake_file').should == false
+ end
+
+ it "raises an ArgumentError if not passed one argument" do
+ lambda { @object.send(@method) }.should raise_error(ArgumentError)
+ lambda { @object.send(@method, __FILE__, __FILE__) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if not passed a String type" do
+ lambda { @object.send(@method, nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts an object that has a #to_path method" do
+ @object.send(@method, mock_to_path(__FILE__)).should == true
+ end
+end
diff --git a/spec/ruby/shared/file/file.rb b/spec/ruby/shared/file/file.rb
new file mode 100644
index 0000000000..095bd63fff
--- /dev/null
+++ b/spec/ruby/shared/file/file.rb
@@ -0,0 +1,45 @@
+describe :file_file, shared: true do
+ before :each do
+ platform_is :windows do
+ @null = "NUL"
+ @dir = "C:\\"
+ end
+
+ platform_is_not :windows do
+ @null = "/dev/null"
+ @dir = "/bin"
+ end
+
+ @file = tmp("test.txt")
+ touch @file
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns true if the named file exists and is a regular file." do
+ @object.send(@method, @file).should == true
+ @object.send(@method, @dir).should == false
+ end
+
+ it "accepts an object that has a #to_path method" do
+ @object.send(@method, mock_to_path(@file)).should == true
+ end
+
+ platform_is_not :windows do
+ it "returns true if the null device exists and is a regular file." do
+ @object.send(@method, @null).should == false # May fail on MS Windows
+ end
+ end
+
+ it "raises an ArgumentError if not passed one argument" do
+ lambda { @object.send(@method) }.should raise_error(ArgumentError)
+ lambda { @object.send(@method, @null, @file) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if not passed a String type" do
+ lambda { @object.send(@method, nil) }.should raise_error(TypeError)
+ lambda { @object.send(@method, 1) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/shared/file/grpowned.rb b/spec/ruby/shared/file/grpowned.rb
new file mode 100644
index 0000000000..91a6483030
--- /dev/null
+++ b/spec/ruby/shared/file/grpowned.rb
@@ -0,0 +1,40 @@
+describe :file_grpowned, shared: true do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.puts "file_content" }
+ File.chown(nil, Process.gid, @file) rescue nil
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ platform_is_not :windows do
+ it "returns true if the file exist" do
+ @object.send(@method, @file).should be_true
+ end
+
+ it "accepts an object that has a #to_path method" do
+ @object.send(@method, mock_to_path(@file)).should be_true
+ end
+
+ it 'takes non primary groups into account' do
+ group = (Process.groups - [Process.egid]).first
+
+ if group
+ File.chown(nil, group, @file)
+
+ @object.send(@method, @file).should == true
+ else
+ # No supplementary groups
+ 1.should == 1
+ end
+ end
+ end
+
+ platform_is :windows do
+ it "returns false if the file exist" do
+ @object.send(@method, @file).should be_false
+ end
+ end
+end
diff --git a/spec/ruby/shared/file/identical.rb b/spec/ruby/shared/file/identical.rb
new file mode 100644
index 0000000000..e89cd309ea
--- /dev/null
+++ b/spec/ruby/shared/file/identical.rb
@@ -0,0 +1,47 @@
+describe :file_identical, shared: true do
+ before :each do
+ @file1 = tmp('file_identical.txt')
+ @file2 = tmp('file_identical2.txt')
+ @link = tmp('file_identical.lnk')
+ @non_exist = 'non_exist'
+
+ touch(@file1) { |f| f.puts "file1" }
+ touch(@file2) { |f| f.puts "file2" }
+
+ rm_r @link
+ File.link(@file1, @link)
+ end
+
+ after :each do
+ rm_r @link, @file1, @file2
+ end
+
+ it "returns true for a file and its link" do
+ @object.send(@method, @file1, @link).should == true
+ end
+
+ it "returns false if any of the files doesn't exist" do
+ @object.send(@method, @file1, @non_exist).should be_false
+ @object.send(@method, @non_exist, @file1).should be_false
+ @object.send(@method, @non_exist, @non_exist).should be_false
+ end
+
+ it "accepts an object that has a #to_path method" do
+ @object.send(@method, mock_to_path(@file1), mock_to_path(@link)).should == true
+ end
+
+ it "raises an ArgumentError if not passed two arguments" do
+ lambda { @object.send(@method, @file1, @file2, @link) }.should raise_error(ArgumentError)
+ lambda { @object.send(@method, @file1) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if not passed String types" do
+ lambda { @object.send(@method, 1,1) }.should raise_error(TypeError)
+ end
+
+ it "returns true if both named files are identical" do
+ @object.send(@method, @file1, @file1).should be_true
+ @object.send(@method, @link, @link).should be_true
+ @object.send(@method, @file1, @file2).should be_false
+ end
+end
diff --git a/spec/ruby/shared/file/owned.rb b/spec/ruby/shared/file/owned.rb
new file mode 100644
index 0000000000..4a08a4ed89
--- /dev/null
+++ b/spec/ruby/shared/file/owned.rb
@@ -0,0 +1,3 @@
+describe :file_owned, shared: true do
+ it "accepts an object that has a #to_path method"
+end
diff --git a/spec/ruby/shared/file/pipe.rb b/spec/ruby/shared/file/pipe.rb
new file mode 100644
index 0000000000..7e150b9167
--- /dev/null
+++ b/spec/ruby/shared/file/pipe.rb
@@ -0,0 +1,3 @@
+describe :file_pipe, shared: true do
+ it "accepts an object that has a #to_path method"
+end
diff --git a/spec/ruby/shared/file/readable.rb b/spec/ruby/shared/file/readable.rb
new file mode 100644
index 0000000000..74f58caaff
--- /dev/null
+++ b/spec/ruby/shared/file/readable.rb
@@ -0,0 +1,30 @@
+describe :file_readable, shared: true do
+ before :each do
+ @file = tmp('i_exist')
+ platform_is :windows do
+ @file2 = File.join(ENV["WINDIR"], "system32/drivers/etc/services").tr(File::SEPARATOR, File::ALT_SEPARATOR)
+ end
+ platform_is_not :windows do
+ @file2 = "/etc/passwd"
+ end
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns true if named file is readable by the effective user id of the process, otherwise false" do
+ @object.send(@method, @file2).should == true
+ File.open(@file,'w') { @object.send(@method, @file).should == true }
+ end
+
+ it "accepts an object that has a #to_path method" do
+ @object.send(@method, mock_to_path(@file2)).should == true
+ end
+end
+
+describe :file_readable_missing, shared: true do
+ it "returns false if the file does not exist" do
+ @object.send(@method, 'fake_file').should == false
+ end
+end
diff --git a/spec/ruby/shared/file/readable_real.rb b/spec/ruby/shared/file/readable_real.rb
new file mode 100644
index 0000000000..b6e53ac76d
--- /dev/null
+++ b/spec/ruby/shared/file/readable_real.rb
@@ -0,0 +1,23 @@
+describe :file_readable_real, shared: true do
+ before :each do
+ @file = tmp('i_exist')
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns true if named file is readable by the real user id of the process, otherwise false" do
+ File.open(@file,'w') { @object.send(@method, @file).should == true }
+ end
+
+ 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
+end
+
+describe :file_readable_real_missing, shared: true do
+ it "returns false if the file does not exist" do
+ @object.send(@method, 'fake_file').should == false
+ end
+end
diff --git a/spec/ruby/shared/file/setgid.rb b/spec/ruby/shared/file/setgid.rb
new file mode 100644
index 0000000000..9893795832
--- /dev/null
+++ b/spec/ruby/shared/file/setgid.rb
@@ -0,0 +1,2 @@
+describe :file_setgid, shared: true do
+end
diff --git a/spec/ruby/shared/file/setuid.rb b/spec/ruby/shared/file/setuid.rb
new file mode 100644
index 0000000000..6401674a94
--- /dev/null
+++ b/spec/ruby/shared/file/setuid.rb
@@ -0,0 +1,2 @@
+describe :file_setuid, shared: true do
+end
diff --git a/spec/ruby/shared/file/size.rb b/spec/ruby/shared/file/size.rb
new file mode 100644
index 0000000000..bb95190fc0
--- /dev/null
+++ b/spec/ruby/shared/file/size.rb
@@ -0,0 +1,124 @@
+describe :file_size, shared: true do
+ before :each do
+ @exists = tmp('i_exist')
+ touch(@exists) { |f| f.write 'rubinius' }
+ end
+
+ after :each do
+ rm_r @exists
+ end
+
+ it "returns the size of the file if it exists and is not empty" do
+ @object.send(@method, @exists).should == 8
+ end
+
+ it "accepts a String-like (to_str) parameter" do
+ obj = mock("file")
+ obj.should_receive(:to_str).and_return(@exists)
+
+ @object.send(@method, obj).should == 8
+ end
+
+ it "accepts an object that has a #to_path method" do
+ @object.send(@method, mock_to_path(@exists)).should == 8
+ end
+end
+
+describe :file_size_to_io, shared: true do
+ before :each do
+ @exists = tmp('i_exist')
+ touch(@exists) { |f| f.write 'rubinius' }
+ @file = File.open(@exists, 'r')
+ end
+
+ after :each do
+ @file.close unless @file.closed?
+ rm_r @exists
+ end
+
+ it "calls #to_io to convert the argument to an IO" do
+ obj = mock("io like")
+ obj.should_receive(:to_io).and_return(@file)
+
+ @object.send(@method, obj).should == 8
+ end
+end
+
+describe :file_size_raise_when_missing, shared: true do
+ before :each do
+ # TODO: missing_file
+ @missing = tmp("i_dont_exist")
+ rm_r @missing
+ end
+
+ after :each do
+ rm_r @missing
+ end
+
+ it "raises an error if file_name doesn't exist" do
+ lambda {@object.send(@method, @missing)}.should raise_error(Errno::ENOENT)
+ end
+end
+
+describe :file_size_nil_when_missing, shared: true do
+ before :each do
+ # TODO: missing_file
+ @missing = tmp("i_dont_exist")
+ rm_r @missing
+ end
+
+ after :each do
+ rm_r @missing
+ end
+
+ it "returns nil if file_name doesn't exist or has 0 size" do
+ @object.send(@method, @missing).should == nil
+ end
+end
+
+describe :file_size_0_when_empty, shared: true do
+ before :each do
+ @empty = tmp("i_am_empty")
+ touch @empty
+ end
+
+ after :each do
+ rm_r @empty
+ end
+
+ it "returns 0 if the file is empty" do
+ @object.send(@method, @empty).should == 0
+ end
+end
+
+describe :file_size_nil_when_empty, shared: true do
+ before :each do
+ @empty = tmp("i_am_empt")
+ touch @empty
+ end
+
+ after :each do
+ rm_r @empty
+ end
+
+ it "returns nil if file_name is empty" do
+ @object.send(@method, @empty).should == nil
+ end
+end
+
+describe :file_size_with_file_argument, shared: true do
+ before :each do
+ @exists = tmp('i_exist')
+ touch(@exists) { |f| f.write 'rubinius' }
+ end
+
+ after :each do
+ rm_r @exists
+ end
+
+ it "accepts a File argument" do
+ File.open(@exists) do |f|
+ @object.send(@method, f).should == 8
+ end
+ end
+end
diff --git a/spec/ruby/shared/file/socket.rb b/spec/ruby/shared/file/socket.rb
new file mode 100644
index 0000000000..55a1cfd284
--- /dev/null
+++ b/spec/ruby/shared/file/socket.rb
@@ -0,0 +1,3 @@
+describe :file_socket, shared: true do
+ it "accepts an object that has a #to_path method"
+end
diff --git a/spec/ruby/shared/file/sticky.rb b/spec/ruby/shared/file/sticky.rb
new file mode 100644
index 0000000000..38bb6ed26b
--- /dev/null
+++ b/spec/ruby/shared/file/sticky.rb
@@ -0,0 +1,29 @@
+describe :file_sticky, shared: true do
+ before :each do
+ @dir = tmp('sticky_dir')
+ Dir.rmdir(@dir) if File.exist?(@dir)
+ end
+
+ after :each do
+ Dir.rmdir(@dir) if File.exist?(@dir)
+ end
+
+ platform_is_not :windows, :darwin, :freebsd, :netbsd, :openbsd, :solaris, :aix do
+ it "returns true if the named file has the sticky bit, otherwise false" do
+ Dir.mkdir @dir, 01755
+
+ @object.send(@method, @dir).should == true
+ @object.send(@method, '/').should == false
+ end
+ end
+
+ it "accepts an object that has a #to_path method"
+end
+
+describe :file_sticky_missing, shared: true do
+ platform_is_not :windows do
+ it "returns false if the file dies not exist" do
+ @object.send(@method, 'fake_file').should == false
+ end
+ end
+end
diff --git a/spec/ruby/shared/file/symlink.rb b/spec/ruby/shared/file/symlink.rb
new file mode 100644
index 0000000000..d1c1dc94df
--- /dev/null
+++ b/spec/ruby/shared/file/symlink.rb
@@ -0,0 +1,46 @@
+describe :file_symlink, shared: true do
+ before :each do
+ @file = tmp("test.txt")
+ @link = tmp("test.lnk")
+
+ rm_r @link
+ touch @file
+ end
+
+ after :each do
+ rm_r @link, @file
+ end
+
+ platform_is_not :windows do
+ it "returns true if the file is a link" do
+ File.symlink(@file, @link)
+ @object.send(@method, @link).should == true
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.symlink(@file, @link)
+ @object.send(@method, mock_to_path(@link)).should == true
+ end
+ end
+end
+
+describe :file_symlink_nonexistent, shared: true do
+ before :each do
+ @file = tmp("test.txt")
+ @link = tmp("test.lnk")
+
+ rm_r @link
+ touch @file
+ end
+
+ after :each do
+ rm_r @link
+ rm_r @file
+ end
+
+ platform_is_not :windows do
+ it "returns false if the file does not exist" do
+ @object.send(@method, "non_existent_link").should == false
+ end
+ end
+end
diff --git a/spec/ruby/shared/file/world_readable.rb b/spec/ruby/shared/file/world_readable.rb
new file mode 100644
index 0000000000..0fd5a28397
--- /dev/null
+++ b/spec/ruby/shared/file/world_readable.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+
+describe :file_world_readable, shared: true do
+
+ before :each do
+ @file = tmp('world-readable')
+ touch @file
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ platform_is_not :windows do
+ it "returns nil if the file is chmod 600" do
+ File.chmod(0600, @file)
+ @object.world_readable?(@file).should be_nil
+ end
+
+ it "returns nil if the file is chmod 000" do
+ File.chmod(0000, @file)
+ @object.world_readable?(@file).should be_nil
+ end
+
+ it "returns nil if the file is chmod 700" do
+ File.chmod(0700, @file)
+ @object.world_readable?(@file).should be_nil
+ end
+ end
+
+ # We don't specify what the Fixnum is because it's system dependent
+ it "returns a Fixnum if the file is chmod 644" do
+ File.chmod(0644, @file)
+ @object.world_readable?(@file).should be_an_instance_of(Fixnum)
+ end
+
+ it "returns a Fixnum if the file is a directory and chmod 644" do
+ dir = rand().to_s + '-ww'
+ Dir.mkdir(dir)
+ Dir.exist?(dir).should be_true
+ File.chmod(0644, dir)
+ @object.world_readable?(dir).should be_an_instance_of(Fixnum)
+ Dir.rmdir(dir)
+ end
+
+ it "coerces the argument with #to_path" do
+ @object.world_readable?(mock_to_path(@file))
+ end
+end
diff --git a/spec/ruby/shared/file/world_writable.rb b/spec/ruby/shared/file/world_writable.rb
new file mode 100644
index 0000000000..da8af0bc2a
--- /dev/null
+++ b/spec/ruby/shared/file/world_writable.rb
@@ -0,0 +1,49 @@
+require_relative '../../spec_helper'
+
+describe :file_world_writable, shared: true do
+
+ before :each do
+ @file = tmp('world-writable')
+ touch @file
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ platform_is_not :windows do
+ it "returns nil if the file is chmod 600" do
+ File.chmod(0600, @file)
+ @object.world_writable?(@file).should be_nil
+ end
+
+ it "returns nil if the file is chmod 000" do
+ File.chmod(0000, @file)
+ @object.world_writable?(@file).should be_nil
+ end
+
+ it "returns nil if the file is chmod 700" do
+ File.chmod(0700, @file)
+ @object.world_writable?(@file).should be_nil
+ end
+
+ # We don't specify what the Fixnum is because it's system dependent
+ it "returns a Fixnum if the file is chmod 777" do
+ File.chmod(0777, @file)
+ @object.world_writable?(@file).should be_an_instance_of(Fixnum)
+ end
+
+ it "returns a Fixnum if the file is a directory and chmod 777" do
+ dir = rand().to_s + '-ww'
+ Dir.mkdir(dir)
+ Dir.exist?(dir).should be_true
+ File.chmod(0777, dir)
+ @object.world_writable?(dir).should be_an_instance_of(Fixnum)
+ Dir.rmdir(dir)
+ end
+ end
+
+ it "coerces the argument with #to_path" do
+ @object.world_writable?(mock_to_path(@file))
+ end
+end
diff --git a/spec/ruby/shared/file/writable.rb b/spec/ruby/shared/file/writable.rb
new file mode 100644
index 0000000000..902d545da1
--- /dev/null
+++ b/spec/ruby/shared/file/writable.rb
@@ -0,0 +1,28 @@
+describe :file_writable, shared: true do
+ before :each do
+ @file = tmp('i_exist')
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns true if named file is writable by the effective user id of the process, otherwise false" do
+ platform_is_not :windows do
+ as_user do
+ @object.send(@method, "/etc/passwd").should == false
+ end
+ end
+ File.open(@file,'w') { @object.send(@method, @file).should == true }
+ end
+
+ 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
+end
+
+describe :file_writable_missing, shared: true do
+ it "returns false if the file does not exist" do
+ @object.send(@method, 'fake_file').should == false
+ end
+end
diff --git a/spec/ruby/shared/file/writable_real.rb b/spec/ruby/shared/file/writable_real.rb
new file mode 100644
index 0000000000..3730befb7a
--- /dev/null
+++ b/spec/ruby/shared/file/writable_real.rb
@@ -0,0 +1,33 @@
+describe :file_writable_real, shared: true do
+ before :each do
+ @file = tmp('i_exist')
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns true if named file is writable by the real user id of the process, otherwise false" do
+ File.open(@file,'w') { @object.send(@method, @file).should == true }
+ end
+
+ 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
+
+ it "raises an ArgumentError if not passed one argument" do
+ lambda { File.writable_real? }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if not passed a String type" do
+ lambda { @object.send(@method, 1) }.should raise_error(TypeError)
+ lambda { @object.send(@method, nil) }.should raise_error(TypeError)
+ lambda { @object.send(@method, false) }.should raise_error(TypeError)
+ end
+end
+
+describe :file_writable_real_missing, shared: true do
+ it "returns false if the file does not exist" do
+ @object.send(@method, 'fake_file').should == false
+ end
+end
diff --git a/spec/ruby/shared/file/zero.rb b/spec/ruby/shared/file/zero.rb
new file mode 100644
index 0000000000..cf014d4722
--- /dev/null
+++ b/spec/ruby/shared/file/zero.rb
@@ -0,0 +1,68 @@
+describe :file_zero, shared: true do
+ before :each do
+ @zero_file = tmp("test.txt")
+ @nonzero_file = tmp("test2.txt")
+ @dir = tmp("dir")
+
+ Dir.mkdir @dir
+ touch @zero_file
+ touch(@nonzero_file) { |f| f.puts "hello" }
+ end
+
+ after :each do
+ rm_r @zero_file, @nonzero_file
+ rm_r @dir
+ end
+
+ it "returns true if the file is empty" do
+ @object.send(@method, @zero_file).should == true
+ end
+
+ it "returns false if the file is not empty" do
+ @object.send(@method, @nonzero_file).should == false
+ end
+
+ it "accepts an object that has a #to_path method" do
+ @object.send(@method, mock_to_path(@zero_file)).should == true
+ end
+
+ platform_is :windows do
+ it "returns true for NUL" do
+ @object.send(@method, 'NUL').should == true
+ @object.send(@method, 'nul').should == true
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns true for /dev/null" do
+ @object.send(@method, File.realpath('/dev/null')).should == true
+ end
+ end
+
+ it "raises an ArgumentError if not passed one argument" do
+ lambda { File.zero? }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if not passed a String type" do
+ lambda { @object.send(@method, nil) }.should raise_error(TypeError)
+ lambda { @object.send(@method, true) }.should raise_error(TypeError)
+ lambda { @object.send(@method, false) }.should raise_error(TypeError)
+ end
+
+ it "returns true inside a block opening a file if it is empty" do
+ File.open(@zero_file,'w') do
+ @object.send(@method, @zero_file).should == true
+ end
+ end
+
+ # See https://bugs.ruby-lang.org/issues/449 for background
+ it "returns true or false for a directory" do
+ @object.send(@method, @dir).should be_true_or_false
+ end
+end
+
+describe :file_zero_missing, shared: true do
+ it "returns false if the file does not exist" do
+ @object.send(@method, 'fake_file').should == false
+ end
+end
diff --git a/spec/ruby/shared/hash/key_error.rb b/spec/ruby/shared/hash/key_error.rb
new file mode 100644
index 0000000000..061c88c483
--- /dev/null
+++ b/spec/ruby/shared/hash/key_error.rb
@@ -0,0 +1,25 @@
+describe :key_error, shared: true do
+ it "raises a KeyError" do
+ -> {
+ @method.call(@object, 'foo')
+ }.should raise_error(KeyError)
+ end
+
+ ruby_version_is "2.5" do
+ it "sets the Hash as the receiver of KeyError" do
+ -> {
+ @method.call(@object, 'foo')
+ }.should raise_error(KeyError) { |err|
+ err.receiver.should equal(@object)
+ }
+ end
+
+ it "sets the unmatched key as the key of KeyError" do
+ -> {
+ @method.call(@object, 'foo')
+ }.should raise_error(KeyError) { |err|
+ err.key.to_s.should == 'foo'
+ }
+ end
+ end
+end
diff --git a/spec/ruby/shared/io/putc.rb b/spec/ruby/shared/io/putc.rb
new file mode 100644
index 0000000000..5f620f183f
--- /dev/null
+++ b/spec/ruby/shared/io/putc.rb
@@ -0,0 +1,57 @@
+# -*- encoding: ascii-8bit -*-
+describe :io_putc, shared: true do
+ after :each do
+ @io.close if @io && !@io.closed?
+ @io_object = nil
+ rm_r @name
+ end
+
+ describe "with a Fixnum argument" do
+ it "writes one character as a String" do
+ @io.should_receive(:write).with("A")
+ @io_object.send(@method, 65).should == 65
+ end
+
+ it "writes the low byte as a String" do
+ @io.should_receive(:write).with("A")
+ @io_object.send(@method, 0x2441).should == 0x2441
+ end
+ end
+
+ describe "with a String argument" do
+ it "writes one character" do
+ @io.should_receive(:write).with("B")
+ @io_object.send(@method, "B").should == "B"
+ end
+
+ it "writes the first character" do
+ @io.should_receive(:write).with("R")
+ @io_object.send(@method, "Ruby").should == "Ruby"
+ end
+ end
+
+ it "calls #to_int to convert an object to an Integer" do
+ obj = mock("kernel putc")
+ obj.should_receive(:to_int).and_return(65)
+
+ @io.should_receive(:write).with("A")
+ @io_object.send(@method, obj).should == obj
+ end
+
+ it "raises IOError on a closed stream" do
+ @io.close
+ lambda { @io_object.send(@method, "a") }.should raise_error(IOError)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { @io_object.send(@method, nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed false" do
+ lambda { @io_object.send(@method, false) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed true" do
+ lambda { @io_object.send(@method, true) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/shared/kernel/equal.rb b/spec/ruby/shared/kernel/equal.rb
new file mode 100644
index 0000000000..0a70aec639
--- /dev/null
+++ b/spec/ruby/shared/kernel/equal.rb
@@ -0,0 +1,54 @@
+# These examples hold for BasicObject#equal?, BasicObject#== and Kernel#eql?
+describe :object_equal, shared: true do
+ it "returns true if other is identical to self" do
+ obj = Object.new
+ obj.__send__(@method, obj).should be_true
+ end
+
+ it "returns false if other is not identical to self" do
+ a = Object.new
+ b = Object.new
+ a.__send__(@method, b).should be_false
+ end
+
+ it "returns true only if self and other are the same object" do
+ o1 = mock('o1')
+ o2 = mock('o2')
+ o1.__send__(@method, o1).should == true
+ o2.__send__(@method, o2).should == true
+ o1.__send__(@method, o2).should == false
+ end
+
+ it "returns true for the same immediate object" do
+ o1 = 1
+ o2 = :hola
+ 1.__send__(@method, o1).should == true
+ :hola.__send__(@method, o2).should == true
+ end
+
+ it "returns false for nil and any other object" do
+ o1 = mock('o1')
+ nil.__send__(@method, nil).should == true
+ o1.__send__(@method, nil).should == false
+ nil.__send__(@method, o1).should == false
+ end
+
+ it "returns false for objects of different classes" do
+ :hola.__send__(@method, 1).should == false
+ end
+
+ it "returns true only if self and other are the same boolean" do
+ true.__send__(@method, true).should == true
+ false.__send__(@method, false).should == true
+
+ true.__send__(@method, false).should == false
+ false.__send__(@method, true).should == false
+ end
+
+ it "returns true for integers of initially different ranges" do
+ big42 = (bignum_value * 42 / bignum_value)
+ 42.__send__(@method, big42).should == true
+ long42 = (1 << 35) * 42 / (1 << 35)
+ 42.__send__(@method, long42).should == true
+ end
+end
diff --git a/spec/ruby/shared/kernel/object_id.rb b/spec/ruby/shared/kernel/object_id.rb
new file mode 100644
index 0000000000..7acdb27554
--- /dev/null
+++ b/spec/ruby/shared/kernel/object_id.rb
@@ -0,0 +1,80 @@
+# These examples hold for both BasicObject#__id__ and Kernel#object_id.
+describe :object_id, shared: true do
+ it "returns an integer" do
+ o1 = @object.new
+ o1.__send__(@method).should be_kind_of(Integer)
+ end
+
+ it "returns the same value on all calls to id for a given object" do
+ o1 = @object.new
+ o1.__send__(@method).should == o1.__send__(@method)
+ end
+
+ it "returns different values for different objects" do
+ o1 = @object.new
+ o2 = @object.new
+ o1.__send__(@method).should_not == o2.__send__(@method)
+ end
+
+ it "returns the same value for two Fixnums with the same value" do
+ o1 = 1
+ o2 = 1
+ o1.send(@method).should == o2.send(@method)
+ end
+
+ it "returns the same value for two Symbol literals" do
+ o1 = :hello
+ o2 = :hello
+ o1.send(@method).should == o2.send(@method)
+ end
+
+ it "returns the same value for two true literals" do
+ o1 = true
+ o2 = true
+ o1.send(@method).should == o2.send(@method)
+ end
+
+ it "returns the same value for two false literals" do
+ o1 = false
+ o2 = false
+ o1.send(@method).should == o2.send(@method)
+ end
+
+ it "returns the same value for two nil literals" do
+ o1 = nil
+ o2 = nil
+ o1.send(@method).should == o2.send(@method)
+ end
+
+ it "returns a different value for two Bignum literals" do
+ o1 = 2e100.to_i
+ o2 = 2e100.to_i
+ o1.send(@method).should_not == o2.send(@method)
+ end
+
+ it "returns a different value for two String literals" do
+ o1 = "hello"
+ o2 = "hello"
+ o1.send(@method).should_not == o2.send(@method)
+ end
+
+ it "returns a different value for an object and its dup" do
+ o1 = mock("object")
+ o2 = o1.dup
+ o1.send(@method).should_not == o2.send(@method)
+ end
+
+ it "returns a different value for two numbers near the 32 bit Fixnum limit" do
+ o1 = -1
+ o2 = 2 ** 30 - 1
+
+ o1.send(@method).should_not == o2.send(@method)
+ end
+
+ it "returns a different value for two numbers near the 64 bit Fixnum limit" do
+ o1 = -1
+ o2 = 2 ** 62 - 1
+
+ o1.send(@method).should_not == o2.send(@method)
+ end
+end
diff --git a/spec/ruby/shared/kernel/raise.rb b/spec/ruby/shared/kernel/raise.rb
new file mode 100644
index 0000000000..3cff55ac6e
--- /dev/null
+++ b/spec/ruby/shared/kernel/raise.rb
@@ -0,0 +1,85 @@
+describe :kernel_raise, shared: true do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "aborts execution" do
+ lambda do
+ @object.raise Exception, "abort"
+ ScratchPad.record :no_abort
+ end.should raise_error(Exception, "abort")
+
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "raises RuntimeError if no exception class is given" do
+ lambda { @object.raise }.should raise_error(RuntimeError)
+ end
+
+ it "raises a given Exception instance" do
+ error = RuntimeError.new
+ lambda { @object.raise(error) }.should raise_error(error)
+ end
+
+ it "raises a RuntimeError if string given" do
+ lambda { @object.raise("a bad thing") }.should raise_error(RuntimeError)
+ end
+
+ it "raises a TypeError when passed a non-Exception object" do
+ lambda { @object.raise(Object.new) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed true" do
+ lambda { @object.raise(true) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed false" do
+ lambda { @object.raise(false) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { @object.raise(nil) }.should raise_error(TypeError)
+ end
+
+ it "re-raises the previously rescued exception if no exception is specified" do
+ lambda do
+ begin
+ @object.raise Exception, "outer"
+ ScratchPad.record :no_abort
+ rescue
+ begin
+ @object.raise StandardError, "inner"
+ rescue
+ end
+
+ @object.raise
+ ScratchPad.record :no_reraise
+ end
+ end.should raise_error(Exception, "outer")
+
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "re-raises a previously rescued exception without overwriting the backtrace" do
+ begin
+ initial_raise_line = __LINE__; @object.raise 'raised'
+ rescue => raised
+ begin
+ raise_again_line = __LINE__; @object.raise raised
+ rescue => raised_again
+ # This spec is written using #backtrace and matching the line number
+ # from the string, as backtrace_locations is a more advanced
+ # method that is not always supported by implementations.
+
+ raised_again.backtrace.first.should include("#{__FILE__}:#{initial_raise_line}:")
+ raised_again.backtrace.first.should_not include("#{__FILE__}:#{raise_again_line}:")
+ end
+ end
+ end
+
+ it "allows Exception, message, and backtrace parameters" do
+ lambda do
+ @object.raise(ArgumentError, "message", caller)
+ end.should raise_error(ArgumentError, "message")
+ end
+end
diff --git a/spec/ruby/shared/math/atanh.rb b/spec/ruby/shared/math/atanh.rb
new file mode 100644
index 0000000000..1d1a6ebd74
--- /dev/null
+++ b/spec/ruby/shared/math/atanh.rb
@@ -0,0 +1,44 @@
+describe :math_atanh_base, shared: true do
+ it "returns a float" do
+ @object.send(@method, 0.5).should be_an_instance_of(Float)
+ end
+
+ it "returns the inverse hyperbolic tangent of the argument" do
+ @object.send(@method, 0.0).should == 0.0
+ @object.send(@method, -0.0).should == -0.0
+ @object.send(@method, 0.5).should be_close(0.549306144334055, TOLERANCE)
+ @object.send(@method, -0.2).should be_close(-0.202732554054082, TOLERANCE)
+ end
+
+ it "raises a TypeError if the argument is nil" do
+ lambda { @object.send(@method, nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the argument is not a Numeric" do
+ lambda { @object.send(@method, "test") }.should raise_error(TypeError)
+ end
+
+ it "returns Infinity if x == 1.0" do
+ @object.send(@method, 1.0).should == Float::INFINITY
+ end
+
+ it "return -Infinity if x == -1.0" do
+ @object.send(@method, -1.0).should == -Float::INFINITY
+ end
+end
+
+describe :math_atanh_private, shared: true do
+ it "is a private instance method" do
+ Math.should have_private_instance_method(@method)
+ end
+end
+
+describe :math_atanh_no_complex, shared: true do
+ it "raises a Math::DomainError for arguments greater than 1.0" do
+ lambda { @object.send(@method, 1.0 + Float::EPSILON) }.should raise_error(Math::DomainError)
+ end
+
+ it "raises a Math::DomainError for arguments less than -1.0" do
+ lambda { @object.send(@method, -1.0 - Float::EPSILON) }.should raise_error(Math::DomainError)
+ end
+end
diff --git a/spec/ruby/shared/process/abort.rb b/spec/ruby/shared/process/abort.rb
new file mode 100644
index 0000000000..1a25aeffc2
--- /dev/null
+++ b/spec/ruby/shared/process/abort.rb
@@ -0,0 +1,36 @@
+describe :process_abort, shared: true do
+ before :each do
+ @stderr, $stderr = $stderr, IOStub.new
+ end
+
+ after :each do
+ $stderr = @stderr
+ end
+
+ it "raises a SystemExit exception" do
+ lambda { @object.abort }.should raise_error(SystemExit)
+ end
+
+ it "sets the exception message to the given message" do
+ lambda { @object.abort "message" }.should raise_error { |e| e.message.should == "message" }
+ end
+
+ it "sets the exception status code of 1" do
+ lambda { @object.abort }.should raise_error { |e| e.status.should == 1 }
+ end
+
+ it "prints the specified message to STDERR" do
+ lambda { @object.abort "a message" }.should raise_error(SystemExit)
+ $stderr.should =~ /a message/
+ end
+
+ it "coerces the argument with #to_str" do
+ str = mock('to_str')
+ str.should_receive(:to_str).any_number_of_times.and_return("message")
+ lambda { @object.abort str }.should raise_error(SystemExit, "message")
+ end
+
+ it "raises TypeError when given a non-String object" do
+ lambda { @object.abort 123 }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/shared/process/exit.rb b/spec/ruby/shared/process/exit.rb
new file mode 100644
index 0000000000..7d567c8195
--- /dev/null
+++ b/spec/ruby/shared/process/exit.rb
@@ -0,0 +1,94 @@
+describe :process_exit, shared: true do
+ it "raises a SystemExit with status 0" do
+ lambda { @object.exit }.should raise_error(SystemExit) { |e|
+ e.status.should == 0
+ }
+ end
+
+ it "raises a SystemExit with the specified status" do
+ [-2**16, -2**8, -8, -1, 0, 1 , 8, 2**8, 2**16].each do |value|
+ lambda { @object.exit(value) }.should raise_error(SystemExit) { |e|
+ e.status.should == value
+ }
+ end
+ end
+
+ it "raises a SystemExit with the specified boolean status" do
+ { true => 0, false => 1 }.each do |value, status|
+ lambda { @object.exit(value) }.should raise_error(SystemExit) { |e|
+ e.status.should == status
+ }
+ end
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock('5')
+ obj.should_receive(:to_int).and_return(5)
+ lambda { @object.exit(obj) }.should raise_error(SystemExit) { |e|
+ e.status.should == 5
+ }
+ end
+
+ it "converts the passed Float argument to an Integer" do
+ { -2.2 => -2, -0.1 => 0, 5.5 => 5, 827.999 => 827 }.each do |value, status|
+ lambda { @object.exit(value) }.should raise_error(SystemExit) { |e|
+ e.status.should == status
+ }
+ end
+ end
+
+ it "raises TypeError if can't convert the argument to an Integer" do
+ lambda { @object.exit(Object.new) }.should raise_error(TypeError)
+ lambda { @object.exit('0') }.should raise_error(TypeError)
+ lambda { @object.exit([0]) }.should raise_error(TypeError)
+ lambda { @object.exit(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises the SystemExit in the main thread if it reaches the top-level handler of another thread" do
+ ScratchPad.record []
+
+ ready = false
+ t = Thread.new {
+ Thread.pass until ready
+
+ begin
+ @object.exit 42
+ rescue SystemExit => e
+ ScratchPad << :in_thread
+ raise e
+ end
+ }
+
+ begin
+ ready = true
+ sleep
+ rescue SystemExit
+ ScratchPad << :in_main
+ end
+
+ ScratchPad.recorded.should == [:in_thread, :in_main]
+
+ # the thread also keeps the exception as its value
+ lambda { t.value }.should raise_error(SystemExit)
+ end
+end
+
+describe :process_exit!, shared: true do
+ it "exits with the given status" do
+ out = ruby_exe("#{@object}.send(:exit!, 21)", args: '2>&1')
+ out.should == ""
+ $?.exitstatus.should == 21
+ end
+
+ it "exits when called from a thread" do
+ out = ruby_exe("Thread.new { #{@object}.send(:exit!, 21) }.join; sleep", args: '2>&1')
+ out.should == ""
+ $?.exitstatus.should == 21
+ end
+
+ it "exits when called from a fiber" do
+ out = ruby_exe("Fiber.new { #{@object}.send(:exit!, 21) }.resume", args: '2>&1')
+ out.should == ""
+ $?.exitstatus.should == 21
+ end
+end
diff --git a/spec/ruby/shared/process/fork.rb b/spec/ruby/shared/process/fork.rb
new file mode 100644
index 0000000000..c2c2aee9bf
--- /dev/null
+++ b/spec/ruby/shared/process/fork.rb
@@ -0,0 +1,90 @@
+describe :process_fork, shared: true do
+ platform_is :windows do
+ it "returns false from #respond_to?" do
+ # Workaround for Kernel::Method being public and losing the "non-respond_to? magic"
+ mod = @object.class.name == "KernelSpecs::Method" ? Object.new : @object
+ mod.respond_to?(:fork).should be_false
+ mod.respond_to?(:fork, true).should be_false
+ end
+
+ it "raises a NotImplementedError when called" do
+ lambda { @object.fork }.should raise_error(NotImplementedError)
+ end
+ end
+
+ platform_is_not :windows do
+ before :each do
+ @file = tmp('i_exist')
+ rm_r @file
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns status zero" do
+ pid = Process.fork { exit! 0 }
+ _, result = Process.wait2(pid)
+ result.exitstatus.should == 0
+ end
+
+ it "returns status zero" do
+ pid = Process.fork { exit 0 }
+ _, result = Process.wait2(pid)
+ result.exitstatus.should == 0
+ end
+
+ it "returns status zero" do
+ pid = Process.fork {}
+ _, result = Process.wait2(pid)
+ result.exitstatus.should == 0
+ end
+
+ it "returns status non-zero" do
+ pid = Process.fork { exit! 42 }
+ _, result = Process.wait2(pid)
+ result.exitstatus.should == 42
+ end
+
+ it "returns status non-zero" do
+ pid = Process.fork { exit 42 }
+ _, result = Process.wait2(pid)
+ result.exitstatus.should == 42
+ end
+
+ it "returns nil for the child process" do
+ child_id = @object.fork
+ if child_id == nil
+ touch(@file) { |f| f.write 'rubinius' }
+ Process.exit!
+ else
+ Process.waitpid(child_id)
+ end
+ File.exist?(@file).should == true
+ end
+
+ it "runs a block in a child process" do
+ pid = @object.fork {
+ touch(@file) { |f| f.write 'rubinius' }
+ Process.exit!
+ }
+ Process.waitpid(pid)
+ File.exist?(@file).should == true
+ end
+
+ it "marks threads from the parent as killed" do
+ t = Thread.new { sleep }
+ pid = @object.fork {
+ touch(@file) do |f|
+ f.write Thread.current.alive?
+ f.write t.alive?
+ end
+ Process.exit!
+ }
+ Process.waitpid(pid)
+ t.kill
+ t.join
+ File.read(@file).should == "truefalse"
+ end
+ end
+end
diff --git a/spec/ruby/shared/queue/clear.rb b/spec/ruby/shared/queue/clear.rb
new file mode 100644
index 0000000000..5db5a5b497
--- /dev/null
+++ b/spec/ruby/shared/queue/clear.rb
@@ -0,0 +1,12 @@
+describe :queue_clear, shared: true do
+ it "removes all objects from the queue" do
+ queue = @object.call
+ queue << Object.new
+ queue << 1
+ queue.empty?.should be_false
+ queue.clear
+ queue.empty?.should be_true
+ end
+
+ # TODO: test for atomicity of Queue#clear
+end
diff --git a/spec/ruby/shared/queue/close.rb b/spec/ruby/shared/queue/close.rb
new file mode 100644
index 0000000000..0e7c69acba
--- /dev/null
+++ b/spec/ruby/shared/queue/close.rb
@@ -0,0 +1,14 @@
+describe :queue_close, shared: true do
+ it "may be called multiple times" do
+ q = @object.call
+ q.close
+ q.closed?.should be_true
+ q.close # no effect
+ q.closed?.should be_true
+ end
+
+ it "returns self" do
+ q = @object.call
+ q.close.should == q
+ end
+end
diff --git a/spec/ruby/shared/queue/closed.rb b/spec/ruby/shared/queue/closed.rb
new file mode 100644
index 0000000000..b3cea0c524
--- /dev/null
+++ b/spec/ruby/shared/queue/closed.rb
@@ -0,0 +1,12 @@
+describe :queue_closed?, shared: true do
+ it "returns false initially" do
+ queue = @object.call
+ queue.closed?.should be_false
+ end
+
+ it "returns true when the queue is closed" do
+ queue = @object.call
+ queue.close
+ queue.closed?.should be_true
+ end
+end
diff --git a/spec/ruby/shared/queue/deque.rb b/spec/ruby/shared/queue/deque.rb
new file mode 100644
index 0000000000..3590f367ba
--- /dev/null
+++ b/spec/ruby/shared/queue/deque.rb
@@ -0,0 +1,85 @@
+describe :queue_deq, shared: true do
+ it "removes an item from the queue" do
+ q = @object.call
+ q << Object.new
+ q.size.should == 1
+ q.send @method
+ q.size.should == 0
+ end
+
+ it "returns items in the order they were added" do
+ q = @object.call
+ q << 1
+ q << 2
+ q.send(@method).should == 1
+ q.send(@method).should == 2
+ end
+
+ it "blocks the thread until there are items in the queue" do
+ q = @object.call
+ v = 0
+
+ th = Thread.new do
+ q.send(@method)
+ v = 1
+ end
+
+ v.should == 0
+ q << Object.new
+ th.join
+ v.should == 1
+ end
+
+ it "removes an item from a closed queue" do
+ q = @object.call
+ q << 1
+ q.close
+ q.send(@method).should == 1
+ end
+
+ it "returns nil for a closed empty queue" do
+ q = @object.call
+ q.close
+ q.send(@method).should == nil
+ end
+
+ it "returns nil for an empty queue that becomes closed" do
+ q = @object.call
+
+ t = Thread.new {
+ q.send(@method).should == nil
+ }
+
+ Thread.pass until t.status == "sleep" && q.num_waiting == 1
+ q.close
+ t.join
+ end
+
+ describe "in non-blocking mode" do
+ it "removes an item from the queue" do
+ q = @object.call
+ q << Object.new
+ q.size.should == 1
+ q.send(@method, true)
+ q.size.should == 0
+ end
+
+ it "raises a ThreadError if the queue is empty" do
+ q = @object.call
+ lambda { q.send(@method, true) }.should raise_error(ThreadError)
+ end
+
+ it "removes an item from a closed queue" do
+ q = @object.call
+ q << 1
+ q.close
+ q.send(@method, true).should == 1
+ end
+
+ it "raises a ThreadError for a closed empty queue" do
+ q = @object.call
+ q.close
+ lambda { q.send(@method, true) }.should raise_error(ThreadError)
+ end
+ end
+end
diff --git a/spec/ruby/shared/queue/empty.rb b/spec/ruby/shared/queue/empty.rb
new file mode 100644
index 0000000000..4acd831d48
--- /dev/null
+++ b/spec/ruby/shared/queue/empty.rb
@@ -0,0 +1,12 @@
+describe :queue_empty?, shared: true do
+ it "returns true on an empty Queue" do
+ queue = @object.call
+ queue.empty?.should be_true
+ end
+
+ it "returns false when Queue is not empty" do
+ queue = @object.call
+ queue << Object.new
+ queue.empty?.should be_false
+ end
+end
diff --git a/spec/ruby/shared/queue/enque.rb b/spec/ruby/shared/queue/enque.rb
new file mode 100644
index 0000000000..ad413e1f46
--- /dev/null
+++ b/spec/ruby/shared/queue/enque.rb
@@ -0,0 +1,18 @@
+describe :queue_enq, shared: true do
+ it "adds an element to the Queue" do
+ q = @object.call
+ q.size.should == 0
+ q.send @method, Object.new
+ q.size.should == 1
+ q.send @method, Object.new
+ q.size.should == 2
+ end
+
+ it "is an error for a closed queue" do
+ q = @object.call
+ q.close
+ lambda {
+ q.send @method, Object.new
+ }.should raise_error(ClosedQueueError)
+ end
+end
diff --git a/spec/ruby/shared/queue/length.rb b/spec/ruby/shared/queue/length.rb
new file mode 100644
index 0000000000..a0143a4e19
--- /dev/null
+++ b/spec/ruby/shared/queue/length.rb
@@ -0,0 +1,9 @@
+describe :queue_length, shared: true do
+ it "returns the number of elements" do
+ q = @object.call
+ q.send(@method).should == 0
+ q << Object.new
+ q << Object.new
+ q.send(@method).should == 2
+ end
+end
diff --git a/spec/ruby/shared/queue/num_waiting.rb b/spec/ruby/shared/queue/num_waiting.rb
new file mode 100644
index 0000000000..b054951e45
--- /dev/null
+++ b/spec/ruby/shared/queue/num_waiting.rb
@@ -0,0 +1,16 @@
+describe :queue_num_waiting, shared: true do
+ it "reports the number of threads waiting on the queue" do
+ q = @object.call
+ threads = []
+
+ 5.times do |i|
+ q.num_waiting.should == i
+ t = Thread.new { q.deq }
+ Thread.pass until q.num_waiting == i+1
+ threads << t
+ end
+
+ threads.each { q.enq Object.new }
+ threads.each {|t| t.join }
+ end
+end
diff --git a/spec/ruby/shared/rational/Rational.rb b/spec/ruby/shared/rational/Rational.rb
new file mode 100644
index 0000000000..752225269e
--- /dev/null
+++ b/spec/ruby/shared/rational/Rational.rb
@@ -0,0 +1,103 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/rational'
+
+describe :kernel_Rational, shared: true do
+ describe "passed Integer" do
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ it "returns a new Rational number with 1 as the denominator" do
+ Rational(1).should eql(Rational(1, 1))
+ Rational(-3).should eql(Rational(-3, 1))
+ Rational(bignum_value).should eql(Rational(bignum_value, 1))
+ end
+ end
+ end
+
+ describe "passed two integers" do
+ it "returns a new Rational number" do
+ rat = Rational(1, 2)
+ rat.numerator.should == 1
+ rat.denominator.should == 2
+ rat.should be_an_instance_of(Rational)
+
+ rat = Rational(-3, -5)
+ rat.numerator.should == 3
+ rat.denominator.should == 5
+ rat.should be_an_instance_of(Rational)
+
+ rat = Rational(bignum_value, 3)
+ rat.numerator.should == bignum_value
+ rat.denominator.should == 3
+ rat.should be_an_instance_of(Rational)
+ end
+
+ it "reduces the Rational" do
+ rat = Rational(2, 4)
+ rat.numerator.should == 1
+ rat.denominator.should == 2
+
+ rat = Rational(3, 9)
+ rat.numerator.should == 1
+ rat.denominator.should == 3
+ end
+ end
+
+ describe "when passed a String" do
+ it "converts the String to a Rational using the same method as String#to_r" do
+ r = Rational(13, 25)
+ s_r = ".52".to_r
+ r_s = Rational(".52")
+
+ r_s.should == r
+ r_s.should == s_r
+ end
+
+ it "scales the Rational value of the first argument by the Rational value of the second" do
+ Rational(".52", ".6").should == Rational(13, 15)
+ Rational(".52", "1.6").should == Rational(13, 40)
+ end
+
+ it "does not use the same method as Float#to_r" do
+ r = Rational(3, 5)
+ f_r = 0.6.to_r
+ r_s = Rational("0.6")
+
+ r_s.should == r
+ r_s.should_not == f_r
+ 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)
+
+ 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
+ lambda { Rational(Complex(1, 2)) }.should raise_error(RangeError)
+ end
+ end
+
+ it "raises a TypeError if the first argument is nil" do
+ lambda { Rational(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the second argument is nil" do
+ lambda { Rational(1, nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the first argument is a Symbol" do
+ lambda { Rational(:sym) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the second argument is a Symbol" do
+ lambda { Rational(1, :sym) }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/shared/rational/abs.rb b/spec/ruby/shared/rational/abs.rb
new file mode 100644
index 0000000000..8beb20da7e
--- /dev/null
+++ b/spec/ruby/shared/rational/abs.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe :rational_abs, shared: true do
+ it "returns self's absolute value" do
+ Rational(3, 4).send(@method).should == Rational(3, 4)
+ Rational(-3, 4).send(@method).should == Rational(3, 4)
+ Rational(3, -4).send(@method).should == Rational(3, 4)
+
+ Rational(bignum_value, -bignum_value).send(@method).should == Rational(bignum_value, bignum_value)
+ end
+end
diff --git a/spec/ruby/shared/rational/arithmetic_exception_in_coerce.rb b/spec/ruby/shared/rational/arithmetic_exception_in_coerce.rb
new file mode 100644
index 0000000000..9377814732
--- /dev/null
+++ b/spec/ruby/shared/rational/arithmetic_exception_in_coerce.rb
@@ -0,0 +1,33 @@
+require_relative '../../fixtures/rational'
+
+describe :rational_arithmetic_exception_in_coerce, shared: true do
+ ruby_version_is ""..."2.5" do
+ it "rescues exception (StandardError and subclasses) raised in other#coerce and raises TypeError" do
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(RationalSpecs::CoerceError)
+
+ # e.g. Rational(3, 4) + b
+ -> { Rational(3, 4).send(@method, b) }.should raise_error(TypeError, /MockObject can't be coerced into Rational/)
+ end
+
+ it "does not rescue Exception and StandardError siblings raised in other#coerce" do
+ [Exception, NoMemoryError].each do |exception|
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(exception)
+
+ # e.g. Rational(3, 4) + b
+ -> { Rational(3, 4).send(@method, b) }.should raise_error(exception)
+ end
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "does not rescue exception raised in other#coerce" do
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(RationalSpecs::CoerceError)
+
+ # e.g. Rational(3, 4) + b
+ -> { Rational(3, 4).send(@method, b) }.should raise_error(RationalSpecs::CoerceError)
+ end
+ end
+end
diff --git a/spec/ruby/shared/rational/ceil.rb b/spec/ruby/shared/rational/ceil.rb
new file mode 100644
index 0000000000..f1cf60d2be
--- /dev/null
+++ b/spec/ruby/shared/rational/ceil.rb
@@ -0,0 +1,45 @@
+require_relative '../../spec_helper'
+
+describe :rational_ceil, shared: true do
+ before do
+ @rational = Rational(2200, 7)
+ end
+
+ describe "with no arguments (precision = 0)" do
+ it "returns an Integer" do
+ @rational.ceil.should be_kind_of(Integer)
+ end
+
+ it "returns the truncated value toward positive infinity" do
+ @rational.ceil.should == 315
+ Rational(1, 2).ceil.should == 1
+ Rational(-1, 2).ceil.should == 0
+ end
+ end
+
+ describe "with a precision < 0" do
+ it "returns an Integer" do
+ @rational.ceil(-2).should be_kind_of(Integer)
+ @rational.ceil(-1).should be_kind_of(Integer)
+ end
+
+ it "moves the truncation point n decimal places left" do
+ @rational.ceil(-3).should == 1000
+ @rational.ceil(-2).should == 400
+ @rational.ceil(-1).should == 320
+ end
+ end
+
+ describe "with precision > 0" do
+ it "returns a Rational" do
+ @rational.ceil(1).should be_kind_of(Rational)
+ @rational.ceil(2).should be_kind_of(Rational)
+ end
+
+ it "moves the truncation point n decimal places right" do
+ @rational.ceil(1).should == Rational(3143, 10)
+ @rational.ceil(2).should == Rational(31429, 100)
+ @rational.ceil(3).should == Rational(157143, 500)
+ end
+ end
+end
diff --git a/spec/ruby/shared/rational/coerce.rb b/spec/ruby/shared/rational/coerce.rb
new file mode 100644
index 0000000000..0d6c440993
--- /dev/null
+++ b/spec/ruby/shared/rational/coerce.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+
+describe :rational_coerce, shared: true do
+ it "returns the passed argument, self as Float, when given a Float" do
+ result = Rational(3, 4).coerce(1.0)
+ result.should == [1.0, 0.75]
+ result.first.is_a?(Float).should be_true
+ result.last.is_a?(Float).should be_true
+ end
+
+ it "returns the passed argument, self as Rational, when given an Integer" do
+ result = Rational(3, 4).coerce(10)
+ result.should == [Rational(10, 1), Rational(3, 4)]
+ result.first.is_a?(Rational).should be_true
+ result.last.is_a?(Rational).should be_true
+ end
+
+ it "returns [argument, self] when given a Rational" do
+ Rational(3, 7).coerce(Rational(9, 2)).should == [Rational(9, 2), Rational(3, 7)]
+ end
+end
diff --git a/spec/ruby/shared/rational/comparison.rb b/spec/ruby/shared/rational/comparison.rb
new file mode 100644
index 0000000000..0c8b3d0ac1
--- /dev/null
+++ b/spec/ruby/shared/rational/comparison.rb
@@ -0,0 +1,117 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/rational'
+
+describe :rational_cmp_rat, shared: true do
+ it "returns 1 when self is greater than the passed argument" do
+ (Rational(4, 4) <=> Rational(3, 4)).should equal(1)
+ (Rational(-3, 4) <=> Rational(-4, 4)).should equal(1)
+ end
+
+ it "returns 0 when self is equal to the passed argument" do
+ (Rational(4, 4) <=> Rational(4, 4)).should equal(0)
+ (Rational(-3, 4) <=> Rational(-3, 4)).should equal(0)
+ end
+
+ it "returns -1 when self is less than the passed argument" do
+ (Rational(3, 4) <=> Rational(4, 4)).should equal(-1)
+ (Rational(-4, 4) <=> Rational(-3, 4)).should equal(-1)
+ end
+end
+
+describe :rational_cmp_int, shared: true do
+ it "returns 1 when self is greater than the passed argument" do
+ (Rational(4, 4) <=> 0).should equal(1)
+ (Rational(4, 4) <=> -10).should equal(1)
+ (Rational(-3, 4) <=> -1).should equal(1)
+ end
+
+ it "returns 0 when self is equal to the passed argument" do
+ (Rational(4, 4) <=> 1).should equal(0)
+ (Rational(-8, 4) <=> -2).should equal(0)
+ end
+
+ it "returns -1 when self is less than the passed argument" do
+ (Rational(3, 4) <=> 1).should equal(-1)
+ (Rational(-4, 4) <=> 0).should equal(-1)
+ end
+end
+
+describe :rational_cmp_float, shared: true do
+ it "returns 1 when self is greater than the passed argument" do
+ (Rational(4, 4) <=> 0.5).should equal(1)
+ (Rational(4, 4) <=> -1.5).should equal(1)
+ (Rational(-3, 4) <=> -0.8).should equal(1)
+ end
+
+ it "returns 0 when self is equal to the passed argument" do
+ (Rational(4, 4) <=> 1.0).should equal(0)
+ (Rational(-6, 4) <=> -1.5).should equal(0)
+ end
+
+ it "returns -1 when self is less than the passed argument" do
+ (Rational(3, 4) <=> 1.2).should equal(-1)
+ (Rational(-4, 4) <=> 0.5).should equal(-1)
+ end
+end
+
+describe :rational_cmp_coerce, shared: true do
+ it "calls #coerce on the passed argument with self" do
+ rational = Rational(3, 4)
+
+ obj = mock("Object")
+ obj.should_receive(:coerce).with(rational).and_return([1, 2])
+
+ rational <=> obj
+ end
+
+ it "calls #<=> on the coerced Rational with the coerced Object" do
+ rational = Rational(3, 4)
+
+ coerced_rational = mock("Coerced Rational")
+ coerced_rational.should_receive(:<=>).and_return(:result)
+
+ coerced_obj = mock("Coerced Object")
+
+ obj = mock("Object")
+ obj.should_receive(:coerce).and_return([coerced_rational, coerced_obj])
+
+ (rational <=> obj).should == :result
+ end
+end
+
+describe :rational_cmp_coerce_exception, shared: true do
+ ruby_version_is ""..."2.5" do
+ it "rescues exception (StandardError and subclasses) raised in other#coerce and returns nil" do
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(RationalSpecs::CoerceError)
+
+ -> {
+ (Rational(3, 4) <=> b).should == nil
+ }.should complain(/Numerical comparison operators will no more rescue exceptions of #coerce/)
+ end
+
+ it "does not rescue Exception and StandardError siblings raised in other#coerce" do
+ [Exception, NoMemoryError].each do |exception|
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(exception)
+
+ -> { Rational(3, 4) <=> b }.should raise_error(exception)
+ end
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it "does not rescue exception raised in other#coerce" do
+ b = mock("numeric with failed #coerce")
+ b.should_receive(:coerce).and_raise(RationalSpecs::CoerceError)
+
+ -> { Rational(3, 4) <=> b }.should raise_error(RationalSpecs::CoerceError)
+ end
+ end
+end
+
+describe :rational_cmp_other, shared: true do
+ it "returns nil" do
+ (Rational <=> mock("Object")).should be_nil
+ end
+end
diff --git a/spec/ruby/shared/rational/denominator.rb b/spec/ruby/shared/rational/denominator.rb
new file mode 100644
index 0000000000..10d46aacb3
--- /dev/null
+++ b/spec/ruby/shared/rational/denominator.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe :rational_denominator, shared: true do
+ it "returns the denominator" do
+ Rational(3, 4).denominator.should equal(4)
+ Rational(3, -4).denominator.should equal(4)
+
+ Rational(1, bignum_value).denominator.should == bignum_value
+ end
+
+ it "returns 1 if no denominator was given" do
+ Rational(80).denominator.should == 1
+ end
+end
diff --git a/spec/ruby/shared/rational/div.rb b/spec/ruby/shared/rational/div.rb
new file mode 100644
index 0000000000..2bf9b80eb5
--- /dev/null
+++ b/spec/ruby/shared/rational/div.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+
+describe :rational_div_rat, shared: true do
+ it "performs integer division and returns the result" do
+ Rational(2, 3).div(Rational(2, 3)).should == 1
+ Rational(-2, 9).div(Rational(-9, 2)).should == 0
+ end
+
+ it "raises a ZeroDivisionError when the argument has a numerator of 0" do
+ lambda { Rational(3, 4).div(Rational(0, 3)) }.should raise_error(ZeroDivisionError)
+ end
+
+ it "raises a ZeroDivisionError when the argument has a numerator of 0.0" do
+ lambda { Rational(3, 4).div(Rational(0.0, 3)) }.should raise_error(ZeroDivisionError)
+ end
+end
+
+describe :rational_div_float, shared: true do
+ it "performs integer division and returns the result" do
+ Rational(2, 3).div(30.333).should == 0
+ Rational(2, 9).div(Rational(-8.6)).should == -1
+ Rational(3.12).div(0.5).should == 6
+ end
+
+ it "raises a ZeroDivisionError when the argument is 0.0" do
+ lambda { Rational(3, 4).div(0.0) }.should raise_error(ZeroDivisionError)
+ end
+end
+
+describe :rational_div_int, shared: true do
+ it "performs integer division and returns the result" do
+ Rational(2, 1).div(1).should == 2
+ Rational(25, 5).div(-50).should == -1
+ end
+
+ it "raises a ZeroDivisionError when the argument is 0" do
+ lambda { Rational(3, 4).div(0) }.should raise_error(ZeroDivisionError)
+ end
+end
+
+describe :rational_div, shared: true do
+ it "returns an Integer" do
+ Rational(229, 21).div(82).should be_kind_of(Integer)
+ end
+
+ it "raises an ArgumentError if passed more than one argument" do
+ lambda { Rational(3, 4).div(2,3) }.should raise_error(ArgumentError)
+ end
+
+ # See http://redmine.ruby-lang.org/issues/show/1648
+ it "raises a TypeError if passed a non-numeric argument" do
+ lambda { Rational(3, 4).div([]) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/shared/rational/divide.rb b/spec/ruby/shared/rational/divide.rb
new file mode 100644
index 0000000000..86d30c39d8
--- /dev/null
+++ b/spec/ruby/shared/rational/divide.rb
@@ -0,0 +1,71 @@
+require_relative '../../spec_helper'
+
+describe :rational_divide_rat, shared: true do
+ it "returns self divided by other as a Rational" do
+ Rational(3, 4).send(@method, Rational(3, 4)).should eql(Rational(1, 1))
+ Rational(2, 4).send(@method, Rational(1, 4)).should eql(Rational(2, 1))
+
+ Rational(2, 4).send(@method, 2).should == Rational(1, 4)
+ Rational(6, 7).send(@method, -2).should == Rational(-3, 7)
+ end
+
+ it "raises a ZeroDivisionError when passed a Rational with a numerator of 0" do
+ lambda { Rational(3, 4).send(@method, Rational(0, 1)) }.should raise_error(ZeroDivisionError)
+ end
+end
+
+describe :rational_divide_int, shared: true do
+ it "returns self divided by other as a Rational" do
+ Rational(3, 4).send(@method, 2).should eql(Rational(3, 8))
+ Rational(2, 4).send(@method, 2).should eql(Rational(1, 4))
+ Rational(6, 7).send(@method, -2).should eql(Rational(-3, 7))
+ end
+
+ it "raises a ZeroDivisionError when passed 0" do
+ lambda { Rational(3, 4).send(@method, 0) }.should raise_error(ZeroDivisionError)
+ end
+end
+
+describe :rational_divide_float, shared: true do
+ it "returns self divided by other as a Float" do
+ Rational(3, 4).send(@method, 0.75).should eql(1.0)
+ Rational(3, 4).send(@method, 0.25).should eql(3.0)
+ Rational(3, 4).send(@method, 0.3).should eql(2.5)
+
+ Rational(-3, 4).send(@method, 0.3).should eql(-2.5)
+ Rational(3, -4).send(@method, 0.3).should eql(-2.5)
+ Rational(3, 4).send(@method, -0.3).should eql(-2.5)
+ end
+
+ it "returns infinity when passed 0" do
+ Rational(3, 4).send(@method, 0.0).infinite?.should eql(1)
+ Rational(-3, -4).send(@method, 0.0).infinite?.should eql(1)
+
+ Rational(-3, 4).send(@method, 0.0).infinite?.should eql(-1)
+ Rational(3, -4).send(@method, 0.0).infinite?.should eql(-1)
+ end
+end
+
+describe :rational_divide, shared: true do
+ it "calls #coerce on the passed argument with self" do
+ rational = Rational(3, 4)
+ obj = mock("Object")
+ obj.should_receive(:coerce).with(rational).and_return([1, 2])
+
+ rational.send(@method, obj)
+ end
+
+ it "calls #/ on the coerced Rational with the coerced Object" do
+ rational = Rational(3, 4)
+
+ coerced_rational = mock("Coerced Rational")
+ coerced_rational.should_receive(:/).and_return(:result)
+
+ coerced_obj = mock("Coerced Object")
+
+ obj = mock("Object")
+ obj.should_receive(:coerce).and_return([coerced_rational, coerced_obj])
+
+ rational.send(@method, obj).should == :result
+ end
+end
diff --git a/spec/ruby/shared/rational/divmod.rb b/spec/ruby/shared/rational/divmod.rb
new file mode 100644
index 0000000000..9433674007
--- /dev/null
+++ b/spec/ruby/shared/rational/divmod.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+
+describe :rational_divmod_rat, shared: true do
+ it "returns the quotient as Integer and the remainder as Rational" do
+ Rational(7, 4).divmod(Rational(1, 2)).should eql([3, Rational(1, 4)])
+ Rational(7, 4).divmod(Rational(-1, 2)).should eql([-4, Rational(-1, 4)])
+ Rational(0, 4).divmod(Rational(4, 3)).should eql([0, Rational(0, 1)])
+
+ Rational(bignum_value, 4).divmod(Rational(4, 3)).should eql([1729382256910270464, Rational(0, 1)])
+ end
+
+ it "raises a ZeroDivisonError when passed a Rational with a numerator of 0" do
+ lambda { Rational(7, 4).divmod(Rational(0, 3)) }.should raise_error(ZeroDivisionError)
+ end
+end
+
+describe :rational_divmod_int, shared: true do
+ it "returns the quotient as Integer and the remainder as Rational" do
+ Rational(7, 4).divmod(2).should eql([0, Rational(7, 4)])
+ Rational(7, 4).divmod(-2).should eql([-1, Rational(-1, 4)])
+
+ Rational(bignum_value, 4).divmod(3).should == [768614336404564650, Rational(2, 1)]
+ end
+
+ it "raises a ZeroDivisionError when passed 0" do
+ lambda { Rational(7, 4).divmod(0) }.should raise_error(ZeroDivisionError)
+ end
+end
+
+describe :rational_divmod_float, shared: true do
+ it "returns the quotient as Integer and the remainder as Float" do
+ Rational(7, 4).divmod(0.5).should eql([3, 0.25])
+ end
+
+ it "returns the quotient as Integer and the remainder as Float" do
+ Rational(7, 4).divmod(-0.5).should eql([-4, -0.25])
+ end
+
+ it "raises a ZeroDivisionError when passed 0" do
+ lambda { Rational(7, 4).divmod(0.0) }.should raise_error(ZeroDivisionError)
+ end
+end
diff --git a/spec/ruby/shared/rational/equal_value.rb b/spec/ruby/shared/rational/equal_value.rb
new file mode 100644
index 0000000000..b2e7e09415
--- /dev/null
+++ b/spec/ruby/shared/rational/equal_value.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+
+describe :rational_equal_value_rat, shared: true do
+ it "returns true if self has the same numerator and denominator as the passed argument" do
+ (Rational(3, 4) == Rational(3, 4)).should be_true
+ (Rational(-3, -4) == Rational(3, 4)).should be_true
+ (Rational(-4, 5) == Rational(4, -5)).should be_true
+
+ (Rational(bignum_value, 3) == Rational(bignum_value, 3)).should be_true
+ (Rational(-bignum_value, 3) == Rational(bignum_value, -3)).should be_true
+ end
+end
+
+describe :rational_equal_value_int, shared: true do
+ it "returns true if self has the passed argument as numerator and a denominator of 1" do
+ # Rational(x, y) reduces x and y automatically
+ (Rational(4, 2) == 2).should be_true
+ (Rational(-4, 2) == -2).should be_true
+ (Rational(4, -2) == -2).should be_true
+ end
+end
+
+describe :rational_equal_value_float, shared: true do
+ it "converts self to a Float and compares it with the passed argument" do
+ (Rational(3, 4) == 0.75).should be_true
+ (Rational(4, 2) == 2.0).should be_true
+ (Rational(-4, 2) == -2.0).should be_true
+ (Rational(4, -2) == -2.0).should be_true
+ end
+end
+
+describe :rational_equal_value, shared: true do
+ it "returns the result of calling #== with self on the passed argument" do
+ obj = mock("Object")
+ obj.should_receive(:==).and_return(:result)
+
+ (Rational(3, 4) == obj).should_not be_false
+ end
+end
diff --git a/spec/ruby/shared/rational/exponent.rb b/spec/ruby/shared/rational/exponent.rb
new file mode 100644
index 0000000000..ac8237c291
--- /dev/null
+++ b/spec/ruby/shared/rational/exponent.rb
@@ -0,0 +1,178 @@
+require_relative '../../spec_helper'
+
+describe :rational_exponent, shared: true do
+ describe "when passed Rational" do
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ it "returns Rational(1) if the exponent is Rational(0)" do
+ (Rational(0) ** Rational(0)).should eql(Rational(1))
+ (Rational(1) ** Rational(0)).should eql(Rational(1))
+ (Rational(3, 4) ** Rational(0)).should eql(Rational(1))
+ (Rational(-1) ** Rational(0)).should eql(Rational(1))
+ (Rational(-3, 4) ** Rational(0)).should eql(Rational(1))
+ (Rational(bignum_value) ** Rational(0)).should eql(Rational(1))
+ (Rational(-bignum_value) ** Rational(0)).should eql(Rational(1))
+ end
+
+ it "returns self raised to the argument as a Rational if the exponent's denominator is 1" do
+ (Rational(3, 4) ** Rational(1, 1)).should eql(Rational(3, 4))
+ (Rational(3, 4) ** Rational(2, 1)).should eql(Rational(9, 16))
+ (Rational(3, 4) ** Rational(-1, 1)).should eql(Rational(4, 3))
+ (Rational(3, 4) ** Rational(-2, 1)).should eql(Rational(16, 9))
+ end
+
+ it "returns self raised to the argument as a Float if the exponent's denominator is not 1" do
+ (Rational(3, 4) ** Rational(4, 3)).should be_close(0.681420222312052, TOLERANCE)
+ (Rational(3, 4) ** Rational(-4, 3)).should be_close(1.46752322173095, TOLERANCE)
+ (Rational(3, 4) ** Rational(4, -3)).should be_close(1.46752322173095, TOLERANCE)
+ end
+
+ it "returns a complex number when self is negative and the passed argument is not 0" do
+ (Rational(-3, 4) ** Rational(-4, 3)).should be_close(Complex(-0.7337616108654732, 1.2709123906625817), TOLERANCE)
+ end
+ end
+ end
+
+ describe "when passed Integer" do
+ it "returns the Rational value of self raised to the passed argument" do
+ (Rational(3, 4) ** 4).should == Rational(81, 256)
+ (Rational(3, 4) ** -4).should == Rational(256, 81)
+ (Rational(-3, 4) ** -4).should == Rational(256, 81)
+ (Rational(3, -4) ** -4).should == Rational(256, 81)
+
+ (Rational(bignum_value, 4) ** 4).should == Rational(28269553036454149273332760011886696253239742350009903329945699220681916416, 1)
+ (Rational(3, bignum_value) ** -4).should == Rational(7237005577332262213973186563042994240829374041602535252466099000494570602496, 81)
+ (Rational(-bignum_value, 4) ** -4).should == Rational(1, 28269553036454149273332760011886696253239742350009903329945699220681916416)
+ (Rational(3, -bignum_value) ** -4).should == Rational(7237005577332262213973186563042994240829374041602535252466099000494570602496, 81)
+ end
+
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ it "returns Rational(1, 1) when the passed argument is 0" do
+ (Rational(3, 4) ** 0).should eql(Rational(1, 1))
+ (Rational(-3, 4) ** 0).should eql(Rational(1, 1))
+ (Rational(3, -4) ** 0).should eql(Rational(1, 1))
+
+ (Rational(bignum_value, 4) ** 0).should eql(Rational(1, 1))
+ (Rational(3, -bignum_value) ** 0).should eql(Rational(1, 1))
+ end
+ end
+ end
+
+ describe "when passed Bignum" do
+ # #5713
+ it "returns Rational(0) when self is Rational(0) and the exponent is positive" do
+ (Rational(0) ** bignum_value).should eql(Rational(0))
+ end
+
+ it "raises ZeroDivisionError when self is Rational(0) and the exponent is negative" do
+ lambda { Rational(0) ** -bignum_value }.should raise_error(ZeroDivisionError)
+ end
+
+ it "returns Rational(1) when self is Rational(1)" do
+ (Rational(1) ** bignum_value).should eql(Rational(1))
+ (Rational(1) ** -bignum_value).should eql(Rational(1))
+ end
+
+ it "returns Rational(1) when self is Rational(-1) and the exponent is positive and even" do
+ (Rational(-1) ** bignum_value(0)).should eql(Rational(1))
+ (Rational(-1) ** bignum_value(2)).should eql(Rational(1))
+ end
+
+ it "returns Rational(-1) when self is Rational(-1) and the exponent is positive and odd" do
+ (Rational(-1) ** bignum_value(1)).should eql(Rational(-1))
+ (Rational(-1) ** bignum_value(3)).should eql(Rational(-1))
+ end
+
+ it "returns positive Infinity when self is > 1" do
+ (Rational(2) ** bignum_value).infinite?.should == 1
+ (Rational(fixnum_max) ** bignum_value).infinite?.should == 1
+ end
+
+ it "returns 0.0 when self is > 1 and the exponent is negative" do
+ (Rational(2) ** -bignum_value).should eql(0.0)
+ (Rational(fixnum_max) ** -bignum_value).should eql(0.0)
+ end
+
+ # Fails on linux due to pow() bugs in glibc: http://sources.redhat.com/bugzilla/show_bug.cgi?id=3866
+ platform_is_not :linux do
+ it "returns positive Infinity when self < -1" do
+ (Rational(-2) ** bignum_value).infinite?.should == 1
+ (Rational(-2) ** (bignum_value + 1)).infinite?.should == 1
+ (Rational(fixnum_min) ** bignum_value).infinite?.should == 1
+ end
+
+ it "returns 0.0 when self is < -1 and the exponent is negative" do
+ (Rational(-2) ** -bignum_value).should eql(0.0)
+ (Rational(fixnum_min) ** -bignum_value).should eql(0.0)
+ end
+ end
+ end
+
+ describe "when passed Float" do
+ it "returns self converted to Float and raised to the passed argument" do
+ (Rational(3, 1) ** 3.0).should eql(27.0)
+ (Rational(3, 1) ** 1.5).should be_close(5.19615242270663, TOLERANCE)
+ (Rational(3, 1) ** -1.5).should be_close(0.192450089729875, TOLERANCE)
+ end
+
+ it "returns a complex number if self is negative and the passed argument is not 0" do
+ (Rational(-3, 2) ** 1.5).should be_close(Complex(0.0, -1.8371173070873836), TOLERANCE)
+ (Rational(3, -2) ** 1.5).should be_close(Complex(0.0, -1.8371173070873836), TOLERANCE)
+ (Rational(3, -2) ** -1.5).should be_close(Complex(0.0, 0.5443310539518174), TOLERANCE)
+ end
+
+ it "returns Complex(1.0) when the passed argument is 0.0" do
+ (Rational(3, 4) ** 0.0).should == Complex(1.0)
+ (Rational(-3, 4) ** 0.0).should == Complex(1.0)
+ (Rational(-3, 4) ** 0.0).should == Complex(1.0)
+ end
+ end
+
+ it "calls #coerce on the passed argument with self" do
+ rational = Rational(3, 4)
+ obj = mock("Object")
+ obj.should_receive(:coerce).with(rational).and_return([1, 2])
+
+ rational ** obj
+ end
+
+ it "calls #** on the coerced Rational with the coerced Object" do
+ rational = Rational(3, 4)
+
+ coerced_rational = mock("Coerced Rational")
+ coerced_rational.should_receive(:**).and_return(:result)
+
+ coerced_obj = mock("Coerced Object")
+
+ obj = mock("Object")
+ obj.should_receive(:coerce).and_return([coerced_rational, coerced_obj])
+
+ (rational ** obj).should == :result
+ end
+
+ it "raises ZeroDivisionError for Rational(0, 1) passed a negative Integer" do
+ [-1, -4, -9999].each do |exponent|
+ lambda { Rational(0, 1) ** exponent }.should raise_error(ZeroDivisionError, "divided by 0")
+ end
+ end
+
+ it "raises ZeroDivisionError for Rational(0, 1) passed a negative Rational with denominator 1" do
+ [Rational(-1, 1), Rational(-3, 1)].each do |exponent|
+ lambda { Rational(0, 1) ** exponent }.should raise_error(ZeroDivisionError, "divided by 0")
+ end
+ end
+
+ # #7513
+ it "raises ZeroDivisionError for Rational(0, 1) passed a negative Rational" do
+ lambda { Rational(0, 1) ** Rational(-3, 2) }.should raise_error(ZeroDivisionError, "divided by 0")
+ end
+
+ platform_is_not :solaris do # See https://github.com/ruby/spec/issues/134
+ it "returns Infinity for Rational(0, 1) passed a negative Float" do
+ [-1.0, -3.0, -3.14].each do |exponent|
+ (Rational(0, 1) ** exponent).infinite?.should == 1
+ end
+ end
+ end
+end
diff --git a/spec/ruby/shared/rational/fdiv.rb b/spec/ruby/shared/rational/fdiv.rb
new file mode 100644
index 0000000000..6911ade8ac
--- /dev/null
+++ b/spec/ruby/shared/rational/fdiv.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe :rational_fdiv, shared: true do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/shared/rational/floor.rb b/spec/ruby/shared/rational/floor.rb
new file mode 100644
index 0000000000..ddf7fdbd17
--- /dev/null
+++ b/spec/ruby/shared/rational/floor.rb
@@ -0,0 +1,45 @@
+require_relative '../../spec_helper'
+
+describe :rational_floor, shared: true do
+ before do
+ @rational = Rational(2200, 7)
+ end
+
+ describe "with no arguments (precision = 0)" do
+ it "returns an integer" do
+ @rational.floor.should be_kind_of(Integer)
+ end
+
+ it "returns the truncated value toward negative infinity" do
+ @rational.floor.should == 314
+ Rational(1, 2).floor.should == 0
+ Rational(-1, 2).floor.should == -1
+ end
+ end
+
+ describe "with a precision < 0" do
+ it "returns an integer" do
+ @rational.floor(-2).should be_kind_of(Integer)
+ @rational.floor(-1).should be_kind_of(Integer)
+ end
+
+ it "moves the truncation point n decimal places left" do
+ @rational.floor(-3).should == 0
+ @rational.floor(-2).should == 300
+ @rational.floor(-1).should == 310
+ end
+ end
+
+ describe "with a precision > 0" do
+ it "returns a Rational" do
+ @rational.floor(1).should be_kind_of(Rational)
+ @rational.floor(2).should be_kind_of(Rational)
+ end
+
+ it "moves the truncation point n decimal places right" do
+ @rational.floor(1).should == Rational(1571, 5)
+ @rational.floor(2).should == Rational(7857, 25)
+ @rational.floor(3).should == Rational(62857, 200)
+ end
+ end
+end
diff --git a/spec/ruby/shared/rational/hash.rb b/spec/ruby/shared/rational/hash.rb
new file mode 100644
index 0000000000..50f21cec20
--- /dev/null
+++ b/spec/ruby/shared/rational/hash.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe :rational_hash, shared: true do
+ # BUG: Rational(2, 3).hash == Rational(3, 2).hash
+ it "is static" do
+ Rational(2, 3).hash.should == Rational(2, 3).hash
+ Rational(2, 4).hash.should_not == Rational(2, 3).hash
+ end
+end
diff --git a/spec/ruby/shared/rational/inspect.rb b/spec/ruby/shared/rational/inspect.rb
new file mode 100644
index 0000000000..19691a2f25
--- /dev/null
+++ b/spec/ruby/shared/rational/inspect.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe :rational_inspect, shared: true do
+ it "returns a string representation of self" do
+ Rational(3, 4).inspect.should == "(3/4)"
+ Rational(-5, 8).inspect.should == "(-5/8)"
+ Rational(-1, -2).inspect.should == "(1/2)"
+
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ Rational(bignum_value, 1).inspect.should == "(#{bignum_value}/1)"
+ end
+ end
+end
diff --git a/spec/ruby/shared/rational/marshal_dump.rb b/spec/ruby/shared/rational/marshal_dump.rb
new file mode 100644
index 0000000000..09782b45a5
--- /dev/null
+++ b/spec/ruby/shared/rational/marshal_dump.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe :rational_marshal_dump, shared: true do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/shared/rational/marshal_load.rb b/spec/ruby/shared/rational/marshal_load.rb
new file mode 100644
index 0000000000..20bdd6fdf4
--- /dev/null
+++ b/spec/ruby/shared/rational/marshal_load.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe :rational_marshal_load, shared: true do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/shared/rational/minus.rb b/spec/ruby/shared/rational/minus.rb
new file mode 100644
index 0000000000..0a0946fdb9
--- /dev/null
+++ b/spec/ruby/shared/rational/minus.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+
+describe :rational_minus_rat, shared: true do
+ it "returns the result of subtracting other from self as a Rational" do
+ (Rational(3, 4) - Rational(0, 1)).should eql(Rational(3, 4))
+ (Rational(3, 4) - Rational(1, 4)).should eql(Rational(1, 2))
+
+ (Rational(3, 4) - Rational(2, 1)).should eql(Rational(-5, 4))
+ end
+end
+
+describe :rational_minus_int, shared: true do
+ it "returns the result of subtracting other from self as a Rational" do
+ (Rational(3, 4) - 1).should eql(Rational(-1, 4))
+ (Rational(3, 4) - 2).should eql(Rational(-5, 4))
+ end
+end
+
+describe :rational_minus_float, shared: true do
+ it "returns the result of subtracting other from self as a Float" do
+ (Rational(3, 4) - 0.2).should eql(0.55)
+ (Rational(3, 4) - 2.5).should eql(-1.75)
+ end
+end
+
+describe :rational_minus, shared: true do
+ it "calls #coerce on the passed argument with self" do
+ rational = Rational(3, 4)
+ obj = mock("Object")
+ obj.should_receive(:coerce).with(rational).and_return([1, 2])
+
+ rational - obj
+ end
+
+ it "calls #- on the coerced Rational with the coerced Object" do
+ rational = Rational(3, 4)
+
+ coerced_rational = mock("Coerced Rational")
+ coerced_rational.should_receive(:-).and_return(:result)
+
+ coerced_obj = mock("Coerced Object")
+
+ obj = mock("Object")
+ obj.should_receive(:coerce).and_return([coerced_rational, coerced_obj])
+
+ (rational - obj).should == :result
+ end
+end
diff --git a/spec/ruby/shared/rational/modulo.rb b/spec/ruby/shared/rational/modulo.rb
new file mode 100644
index 0000000000..39abaed5fe
--- /dev/null
+++ b/spec/ruby/shared/rational/modulo.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+
+describe :rational_modulo, shared: true do
+ it "returns the remainder when this value is divided by other" do
+ Rational(2, 3).send(@method, Rational(2, 3)).should == Rational(0, 1)
+ Rational(4, 3).send(@method, Rational(2, 3)).should == Rational(0, 1)
+ Rational(2, -3).send(@method, Rational(-2, 3)).should == Rational(0, 1)
+ Rational(0, -1).send(@method, -1).should == Rational(0, 1)
+
+ Rational(7, 4).send(@method, Rational(1, 2)).should == Rational(1, 4)
+ Rational(7, 4).send(@method, 1).should == Rational(3, 4)
+ Rational(7, 4).send(@method, Rational(1, 7)).should == Rational(1, 28)
+
+ Rational(3, 4).send(@method, -1).should == Rational(-1, 4)
+ Rational(1, -5).send(@method, -1).should == Rational(-1, 5)
+ end
+
+ it "returns a Float value when the argument is Float" do
+ Rational(7, 4).send(@method, 1.0).should be_kind_of(Float)
+ Rational(7, 4).send(@method, 1.0).should == 0.75
+ Rational(7, 4).send(@method, 0.26).should be_close(0.19, 0.0001)
+ end
+
+ it "raises ZeroDivisionError on zero denominator" do
+ lambda {
+ Rational(3, 5).send(@method, Rational(0, 1))
+ }.should raise_error(ZeroDivisionError)
+
+ lambda {
+ Rational(0, 1).send(@method, Rational(0, 1))
+ }.should raise_error(ZeroDivisionError)
+
+ lambda {
+ Rational(3, 5).send(@method, 0)
+ }.should raise_error(ZeroDivisionError)
+ end
+
+ it "raises a ZeroDivisionError when the argument is 0.0" do
+ lambda {
+ Rational(3, 5).send(@method, 0.0)
+ }.should raise_error(ZeroDivisionError)
+ end
+end
diff --git a/spec/ruby/shared/rational/multiply.rb b/spec/ruby/shared/rational/multiply.rb
new file mode 100644
index 0000000000..9c861cf79d
--- /dev/null
+++ b/spec/ruby/shared/rational/multiply.rb
@@ -0,0 +1,62 @@
+require_relative '../../spec_helper'
+
+describe :rational_multiply_rat, shared: true do
+ it "returns self divided by other as a Rational" do
+ (Rational(3, 4) * Rational(3, 4)).should eql(Rational(9, 16))
+ (Rational(2, 4) * Rational(1, 4)).should eql(Rational(1, 8))
+
+ (Rational(3, 4) * Rational(0, 1)).should eql(Rational(0, 4))
+ end
+end
+
+describe :rational_multiply_int, shared: true do
+ it "returns self divided by other as a Rational" do
+ (Rational(3, 4) * 2).should eql(Rational(3, 2))
+ (Rational(2, 4) * 2).should eql(Rational(1, 1))
+ (Rational(6, 7) * -2).should eql(Rational(-12, 7))
+
+ (Rational(3, 4) * 0).should eql(Rational(0, 4))
+ end
+end
+
+describe :rational_multiply_float, shared: true do
+ it "returns self divided by other as a Float" do
+ (Rational(3, 4) * 0.75).should eql(0.5625)
+ (Rational(3, 4) * 0.25).should eql(0.1875)
+ (Rational(3, 4) * 0.3).should be_close(0.225, TOLERANCE)
+
+ (Rational(-3, 4) * 0.3).should be_close(-0.225, TOLERANCE)
+ (Rational(3, -4) * 0.3).should be_close(-0.225, TOLERANCE)
+ (Rational(3, 4) * -0.3).should be_close(-0.225, TOLERANCE)
+
+ (Rational(3, 4) * 0.0).should eql(0.0)
+ (Rational(-3, -4) * 0.0).should eql(0.0)
+
+ (Rational(-3, 4) * 0.0).should eql(0.0)
+ (Rational(3, -4) * 0.0).should eql(0.0)
+ end
+end
+
+describe :rational_multiply, shared: true do
+ it "calls #coerce on the passed argument with self" do
+ rational = Rational(3, 4)
+ obj = mock("Object")
+ obj.should_receive(:coerce).with(rational).and_return([1, 2])
+
+ rational * obj
+ end
+
+ it "calls #* on the coerced Rational with the coerced Object" do
+ rational = Rational(3, 4)
+
+ coerced_rational = mock("Coerced Rational")
+ coerced_rational.should_receive(:*).and_return(:result)
+
+ coerced_obj = mock("Coerced Object")
+
+ obj = mock("Object")
+ obj.should_receive(:coerce).and_return([coerced_rational, coerced_obj])
+
+ (rational * obj).should == :result
+ end
+end
diff --git a/spec/ruby/shared/rational/numerator.rb b/spec/ruby/shared/rational/numerator.rb
new file mode 100644
index 0000000000..50d768168c
--- /dev/null
+++ b/spec/ruby/shared/rational/numerator.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+
+describe :rational_numerator, shared: true do
+ it "returns the numerator" do
+ Rational(3, 4).numerator.should equal(3)
+ Rational(3, -4).numerator.should equal(-3)
+
+ Rational(bignum_value, 1).numerator.should == bignum_value
+ end
+end
diff --git a/spec/ruby/shared/rational/plus.rb b/spec/ruby/shared/rational/plus.rb
new file mode 100644
index 0000000000..b126360ee4
--- /dev/null
+++ b/spec/ruby/shared/rational/plus.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+
+describe :rational_plus_rat, shared: true do
+ it "returns the result of subtracting other from self as a Rational" do
+ (Rational(3, 4) + Rational(0, 1)).should eql(Rational(3, 4))
+ (Rational(3, 4) + Rational(1, 4)).should eql(Rational(1, 1))
+
+ (Rational(3, 4) + Rational(2, 1)).should eql(Rational(11, 4))
+ end
+end
+
+describe :rational_plus_int, shared: true do
+ it "returns the result of subtracting other from self as a Rational" do
+ (Rational(3, 4) + 1).should eql(Rational(7, 4))
+ (Rational(3, 4) + 2).should eql(Rational(11, 4))
+ end
+end
+
+describe :rational_plus_float, shared: true do
+ it "returns the result of subtracting other from self as a Float" do
+ (Rational(3, 4) + 0.2).should eql(0.95)
+ (Rational(3, 4) + 2.5).should eql(3.25)
+ end
+end
+
+describe :rational_plus, shared: true do
+ it "calls #coerce on the passed argument with self" do
+ rational = Rational(3, 4)
+ obj = mock("Object")
+ obj.should_receive(:coerce).with(rational).and_return([1, 2])
+
+ rational + obj
+ end
+
+ it "calls #+ on the coerced Rational with the coerced Object" do
+ rational = Rational(3, 4)
+
+ coerced_rational = mock("Coerced Rational")
+ coerced_rational.should_receive(:+).and_return(:result)
+
+ coerced_obj = mock("Coerced Object")
+
+ obj = mock("Object")
+ obj.should_receive(:coerce).and_return([coerced_rational, coerced_obj])
+
+ (rational + obj).should == :result
+ end
+end
diff --git a/spec/ruby/shared/rational/quo.rb b/spec/ruby/shared/rational/quo.rb
new file mode 100644
index 0000000000..53b32fed2f
--- /dev/null
+++ b/spec/ruby/shared/rational/quo.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe :rational_quo, shared: true do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/shared/rational/remainder.rb b/spec/ruby/shared/rational/remainder.rb
new file mode 100644
index 0000000000..dd907608db
--- /dev/null
+++ b/spec/ruby/shared/rational/remainder.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe :rational_remainder, shared: true do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/shared/rational/round.rb b/spec/ruby/shared/rational/round.rb
new file mode 100644
index 0000000000..36ed476350
--- /dev/null
+++ b/spec/ruby/shared/rational/round.rb
@@ -0,0 +1,99 @@
+require_relative '../../spec_helper'
+
+describe :rational_round, shared: true do
+ before do
+ @rational = Rational(2200, 7)
+ end
+
+ describe "with no arguments (precision = 0)" do
+ it "returns an integer" do
+ @rational.round.should be_kind_of(Integer)
+ Rational(0, 1).round(0).should be_kind_of(Integer)
+ Rational(124, 1).round(0).should be_kind_of(Integer)
+ end
+
+ it "returns the truncated value toward the nearest integer" do
+ @rational.round.should == 314
+ Rational(0, 1).round(0).should == 0
+ Rational(2, 1).round(0).should == 2
+ end
+
+ it "returns the rounded value toward the nearest integer" do
+ Rational(1, 2).round.should == 1
+ Rational(-1, 2).round.should == -1
+ Rational(3, 2).round.should == 2
+ Rational(-3, 2).round.should == -2
+ Rational(5, 2).round.should == 3
+ Rational(-5, 2).round.should == -3
+ end
+ end
+
+ describe "with a precision < 0" do
+ it "returns an integer" do
+ @rational.round(-2).should be_kind_of(Integer)
+ @rational.round(-1).should be_kind_of(Integer)
+ Rational(0, 1).round(-1).should be_kind_of(Integer)
+ Rational(2, 1).round(-1).should be_kind_of(Integer)
+ end
+
+ it "moves the truncation point n decimal places left" do
+ @rational.round(-3).should == 0
+ @rational.round(-2).should == 300
+ @rational.round(-1).should == 310
+ end
+ end
+
+ describe "with a precision > 0" do
+ it "returns a Rational" do
+ @rational.round(1).should be_kind_of(Rational)
+ @rational.round(2).should be_kind_of(Rational)
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ Rational(0, 1).round(1).should be_kind_of(Rational)
+ Rational(2, 1).round(1).should be_kind_of(Rational)
+ end
+ end
+
+ it "moves the truncation point n decimal places right" do
+ @rational.round(1).should == Rational(3143, 10)
+ @rational.round(2).should == Rational(31429, 100)
+ @rational.round(3).should == Rational(157143, 500)
+ Rational(0, 1).round(1).should == Rational(0, 1)
+ Rational(2, 1).round(1).should == Rational(2, 1)
+ end
+
+ it "doesn't alter the value if the precision is too great" do
+ Rational(3, 2).round(10).should == Rational(3, 2).round(20)
+ end
+
+ # #6605
+ it "doesn't fail when rounding to an absurdly large positive precision" do
+ Rational(3, 2).round(2_097_171).should == Rational(3, 2)
+ end
+ end
+
+ ruby_version_is "2.4" do
+ describe "with half option" do
+ it "returns an Integer when precision is not passed" do
+ Rational(10, 4).round(half: :up).should == 3
+ Rational(10, 4).round(half: :down).should == 2
+ Rational(10, 4).round(half: :even).should == 2
+ Rational(-10, 4).round(half: :up).should == -3
+ Rational(-10, 4).round(half: :down).should == -2
+ Rational(-10, 4).round(half: :even).should == -2
+ end
+
+ it "returns a Rational when the precision is greater than 0" do
+ Rational(25, 100).round(1, half: :up).should == Rational(3, 10)
+ Rational(25, 100).round(1, half: :down).should == Rational(1, 5)
+ Rational(25, 100).round(1, half: :even).should == Rational(1, 5)
+ Rational(35, 100).round(1, half: :up).should == Rational(2, 5)
+ Rational(35, 100).round(1, half: :down).should == Rational(3, 10)
+ Rational(35, 100).round(1, half: :even).should == Rational(2, 5)
+ Rational(-25, 100).round(1, half: :up).should == Rational(-3, 10)
+ Rational(-25, 100).round(1, half: :down).should == Rational(-1, 5)
+ Rational(-25, 100).round(1, half: :even).should == Rational(-1, 5)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/shared/rational/to_f.rb b/spec/ruby/shared/rational/to_f.rb
new file mode 100644
index 0000000000..56e0b61d68
--- /dev/null
+++ b/spec/ruby/shared/rational/to_f.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+
+describe :rational_to_f, shared: true do
+ it "returns self converted to a Float" do
+ Rational(3, 4).to_f.should eql(0.75)
+ Rational(3, -4).to_f.should eql(-0.75)
+ Rational(-1, 4).to_f.should eql(-0.25)
+ Rational(-1, -4).to_f.should eql(0.25)
+ end
+end
diff --git a/spec/ruby/shared/rational/to_i.rb b/spec/ruby/shared/rational/to_i.rb
new file mode 100644
index 0000000000..9be1183aa4
--- /dev/null
+++ b/spec/ruby/shared/rational/to_i.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+
+describe :rational_to_i, shared: true do
+ it "converts self to an Integer by truncation" do
+ Rational(7, 4).to_i.should eql(1)
+ Rational(11, 4).to_i.should eql(2)
+ end
+
+ it "converts self to an Integer by truncation" do
+ Rational(-7, 4).to_i.should eql(-1)
+ end
+end
diff --git a/spec/ruby/shared/rational/to_r.rb b/spec/ruby/shared/rational/to_r.rb
new file mode 100644
index 0000000000..372c086850
--- /dev/null
+++ b/spec/ruby/shared/rational/to_r.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe :rational_to_r, shared: true do
+ it "returns self" do
+ a = Rational(3, 4)
+ a.to_r.should equal(a)
+
+ a = Rational(bignum_value, 4)
+ a.to_r.should equal(a)
+ end
+end
diff --git a/spec/ruby/shared/rational/to_s.rb b/spec/ruby/shared/rational/to_s.rb
new file mode 100644
index 0000000000..e90c6e5e39
--- /dev/null
+++ b/spec/ruby/shared/rational/to_s.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe :rational_to_s, shared: true do
+ it "returns a string representation of self" do
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ Rational(1, 1).to_s.should == "1/1"
+ Rational(2, 1).to_s.should == "2/1"
+ end
+ Rational(1, 2).to_s.should == "1/2"
+ Rational(-1, 3).to_s.should == "-1/3"
+ Rational(1, -3).to_s.should == "-1/3"
+ end
+end
diff --git a/spec/ruby/shared/rational/truncate.rb b/spec/ruby/shared/rational/truncate.rb
new file mode 100644
index 0000000000..761dd3113a
--- /dev/null
+++ b/spec/ruby/shared/rational/truncate.rb
@@ -0,0 +1,45 @@
+require_relative '../../spec_helper'
+
+describe :rational_truncate, shared: true do
+ before do
+ @rational = Rational(2200, 7)
+ end
+
+ describe "with no arguments (precision = 0)" do
+ it "returns an integer" do
+ @rational.truncate.should be_kind_of(Integer)
+ end
+
+ it "returns the truncated value toward 0" do
+ @rational.truncate.should == 314
+ Rational(1, 2).truncate.should == 0
+ Rational(-1, 2).truncate.should == 0
+ end
+ end
+
+ describe "with a precision < 0" do
+ it "returns an integer" do
+ @rational.truncate(-2).should be_kind_of(Integer)
+ @rational.truncate(-1).should be_kind_of(Integer)
+ end
+
+ it "moves the truncation point n decimal places left" do
+ @rational.truncate(-3).should == 0
+ @rational.truncate(-2).should == 300
+ @rational.truncate(-1).should == 310
+ end
+ end
+
+ describe "with a precision > 0" do
+ it "returns a Rational" do
+ @rational.truncate(1).should be_kind_of(Rational)
+ @rational.truncate(2).should be_kind_of(Rational)
+ end
+
+ it "moves the truncation point n decimal places right" do
+ @rational.truncate(1).should == Rational(1571, 5)
+ @rational.truncate(2).should == Rational(7857, 25)
+ @rational.truncate(3).should == Rational(62857, 200)
+ end
+ end
+end
diff --git a/spec/ruby/shared/sizedqueue/enque.rb b/spec/ruby/shared/sizedqueue/enque.rb
new file mode 100644
index 0000000000..b724101269
--- /dev/null
+++ b/spec/ruby/shared/sizedqueue/enque.rb
@@ -0,0 +1,50 @@
+describe :sizedqueue_enq, shared: true do
+ it "blocks if queued elements exceed size" do
+ q = @object.call(1)
+
+ q.size.should == 0
+ q.send(@method, :first_element)
+ q.size.should == 1
+
+ blocked_thread = Thread.new { q.send(@method, :second_element) }
+ sleep 0.01 until blocked_thread.stop?
+
+ q.size.should == 1
+ q.pop.should == :first_element
+
+ blocked_thread.join
+ q.size.should == 1
+ q.pop.should == :second_element
+ q.size.should == 0
+ end
+
+ it "raises a ThreadError if queued elements exceed size when not blocking" do
+ q = @object.call(2)
+
+ non_blocking = true
+ add_to_queue = lambda { q.send(@method, Object.new, non_blocking) }
+
+ q.size.should == 0
+ add_to_queue.call
+ q.size.should == 1
+ add_to_queue.call
+ q.size.should == 2
+ add_to_queue.should raise_error(ThreadError)
+ end
+
+ it "interrupts enqueuing threads with ClosedQueueError when the queue is closed" do
+ q = @object.call(1)
+ q << 1
+
+ t = Thread.new {
+ lambda { q.send(@method, 2) }.should raise_error(ClosedQueueError)
+ }
+
+ Thread.pass until q.num_waiting == 1
+
+ q.close
+
+ t.join
+ q.pop.should == 1
+ end
+end
diff --git a/spec/ruby/shared/sizedqueue/max.rb b/spec/ruby/shared/sizedqueue/max.rb
new file mode 100644
index 0000000000..cd4b47f1c1
--- /dev/null
+++ b/spec/ruby/shared/sizedqueue/max.rb
@@ -0,0 +1,47 @@
+describe :sizedqueue_max, shared: true do
+ it "returns the size of the queue" do
+ q = @object.call(5)
+ q.max.should == 5
+ end
+end
+
+describe :sizedqueue_max=, shared: true do
+ it "sets the size of the queue" do
+ q = @object.call(5)
+ q.max.should == 5
+ q.max = 10
+ q.max.should == 10
+ end
+
+ it "does not remove items already in the queue beyond the maximum" do
+ q = @object.call(5)
+ q.enq 1
+ q.enq 2
+ q.enq 3
+ q.max = 2
+ (q.size > q.max).should be_true
+ q.deq.should == 1
+ q.deq.should == 2
+ q.deq.should == 3
+ end
+
+ it "raises a TypeError when given a non-numeric value" do
+ q = @object.call(5)
+ lambda { q.max = "foo" }.should raise_error(TypeError)
+ lambda { q.max = Object.new }.should raise_error(TypeError)
+ end
+
+ it "raises an argument error when set to zero" do
+ q = @object.call(5)
+ q.max.should == 5
+ lambda { q.max = 0 }.should raise_error(ArgumentError)
+ q.max.should == 5
+ end
+
+ it "raises an argument error when set to a negative number" do
+ q = @object.call(5)
+ q.max.should == 5
+ lambda { q.max = -1 }.should raise_error(ArgumentError)
+ q.max.should == 5
+ end
+end
diff --git a/spec/ruby/shared/sizedqueue/new.rb b/spec/ruby/shared/sizedqueue/new.rb
new file mode 100644
index 0000000000..4439f2a9c6
--- /dev/null
+++ b/spec/ruby/shared/sizedqueue/new.rb
@@ -0,0 +1,18 @@
+describe :sizedqueue_new, shared: true do
+ it "raises a TypeError when the given argument is not Numeric" do
+ lambda { @object.call("foo") }.should raise_error(TypeError)
+ lambda { @object.call(Object.new) }.should raise_error(TypeError)
+ end
+
+ it "raises an argument error when no argument is given" do
+ lambda { @object.call }.should raise_error(ArgumentError)
+ end
+
+ it "raises an argument error when the given argument is zero" do
+ lambda { @object.call(0) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an argument error when the given argument is negative" do
+ lambda { @object.call(-1) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/shared/sizedqueue/num_waiting.rb b/spec/ruby/shared/sizedqueue/num_waiting.rb
new file mode 100644
index 0000000000..8c31e48ca5
--- /dev/null
+++ b/spec/ruby/shared/sizedqueue/num_waiting.rb
@@ -0,0 +1,12 @@
+describe :sizedqueue_num_waiting, shared: true do
+ it "reports the number of threads waiting to push" do
+ q = @object.call(1)
+ q.push(1)
+ t = Thread.new { q.push(2) }
+ sleep 0.05 until t.stop?
+ q.num_waiting.should == 1
+
+ q.pop
+ t.join
+ end
+end
diff --git a/spec/ruby/shared/string/times.rb b/spec/ruby/shared/string/times.rb
new file mode 100644
index 0000000000..c44a76c9b7
--- /dev/null
+++ b/spec/ruby/shared/string/times.rb
@@ -0,0 +1,64 @@
+describe :string_times, shared: true do
+ class MyString < String; end
+
+ it "returns a new string containing count copies of self" do
+ @object.call("cool", 0).should == ""
+ @object.call("cool", 1).should == "cool"
+ @object.call("cool", 3).should == "coolcoolcool"
+ end
+
+ it "tries to convert the given argument to an integer using to_int" do
+ @object.call("cool", 3.1).should == "coolcoolcool"
+ @object.call("a", 3.999).should == "aaa"
+
+ a = mock('4')
+ a.should_receive(:to_int).and_return(4)
+
+ @object.call("a", a).should == "aaaa"
+ end
+
+ it "raises an ArgumentError when given integer is negative" do
+ lambda { @object.call("cool", -3) }.should raise_error(ArgumentError)
+ lambda { @object.call("cool", -3.14) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a RangeError when given integer is a Bignum" do
+ lambda { @object.call("cool", 999999999999999999999) }.should raise_error(RangeError)
+ end
+
+ it "returns subclass instances" do
+ @object.call(MyString.new("cool"), 0).should be_an_instance_of(MyString)
+ @object.call(MyString.new("cool"), 1).should be_an_instance_of(MyString)
+ @object.call(MyString.new("cool"), 2).should be_an_instance_of(MyString)
+ end
+
+ it "always taints the result when self is tainted" do
+ ["", "OK", MyString.new(""), MyString.new("OK")].each do |str|
+ str.taint
+
+ [0, 1, 2].each do |arg|
+ @object.call(str, arg).tainted?.should == true
+ end
+ end
+ end
+
+ with_feature :encoding do
+ it "returns a String in the same encoding as self" do
+ str = "\xE3\x81\x82".force_encoding Encoding::UTF_8
+ result = @object.call(str, 2)
+ result.encoding.should equal(Encoding::UTF_8)
+ end
+ end
+
+ platform_is wordsize: 32 do
+ it "raises an ArgumentError if the length of the resulting string doesn't fit into a long" do
+ lambda { @object.call("abc", (2 ** 31) - 1) }.should raise_error(ArgumentError)
+ end
+ end
+
+ platform_is wordsize: 64 do
+ it "raises an ArgumentError if the length of the resulting string doesn't fit into a long" do
+ lambda { @object.call("abc", (2 ** 63) - 1) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/shared/time/strftime_for_date.rb b/spec/ruby/shared/time/strftime_for_date.rb
new file mode 100644
index 0000000000..f126c5a323
--- /dev/null
+++ b/spec/ruby/shared/time/strftime_for_date.rb
@@ -0,0 +1,275 @@
+# Shared for Time, Date and DateTime, testing only date components (smallest unit is day)
+
+describe :strftime_date, shared: true do
+ before :all do
+ @d200_4_6 = @new_date[200, 4, 6]
+ @d2000_4_6 = @new_date[2000, 4, 6]
+ @d2000_4_9 = @new_date[2000, 4, 9]
+ @d2000_4_10 = @new_date[2000, 4, 10]
+ @d2009_9_18 = @new_date[2009, 9, 18]
+ end
+
+ # Per conversion specifier, not combining
+ it "should be able to print the full day name" do
+ @d2000_4_6.strftime("%A").should == "Thursday"
+ @d2009_9_18.strftime('%A').should == 'Friday'
+ end
+
+ it "should be able to print the short day name" do
+ @d2000_4_6.strftime("%a").should == "Thu"
+ @d2009_9_18.strftime('%a').should == 'Fri'
+ end
+
+ it "should be able to print the full month name" do
+ @d2000_4_6.strftime("%B").should == "April"
+ @d2009_9_18.strftime('%B').should == 'September'
+ end
+
+ it "should be able to print the short month name" do
+ @d2000_4_6.strftime("%b").should == "Apr"
+ @d2000_4_6.strftime("%h").should == "Apr"
+ @d2000_4_6.strftime("%b").should == @d2000_4_6.strftime("%h")
+ @d2009_9_18.strftime('%b').should == 'Sep'
+ end
+
+ it "should be able to print the century" do
+ @d2000_4_6.strftime("%C").should == "20"
+ end
+
+ it "should be able to print the month day with leading zeroes" do
+ @d2000_4_6.strftime("%d").should == "06"
+ @d2009_9_18.strftime('%d').should == '18'
+ end
+
+ it "should be able to print the month day with leading spaces" do
+ @d2000_4_6.strftime("%e").should == " 6"
+ end
+
+ it "should be able to print the commercial year with leading zeroes" do
+ @d2000_4_6.strftime("%G").should == "2000"
+ @d200_4_6.strftime("%G").should == "0200"
+ end
+
+ it "should be able to print the commercial year with only two digits" do
+ @d2000_4_6.strftime("%g").should == "00"
+ @d200_4_6.strftime("%g").should == "00"
+ end
+
+ it "should be able to print the hour with leading zeroes (hour is always 00)" do
+ @d2000_4_6.strftime("%H").should == "00"
+ end
+
+ it "should be able to print the hour in 12 hour notation with leading zeroes" do
+ @d2000_4_6.strftime("%I").should == "12"
+ end
+
+ it "should be able to print the julian day with leading zeroes" do
+ @d2000_4_6.strftime("%j").should == "097"
+ @d2009_9_18.strftime('%j').should == '261'
+ end
+
+ it "should be able to print the hour in 24 hour notation with leading spaces" do
+ @d2000_4_6.strftime("%k").should == " 0"
+ end
+
+ it "should be able to print the hour in 12 hour notation with leading spaces" do
+ @d2000_4_6.strftime("%l").should == "12"
+ end
+
+ it "should be able to print the minutes with leading zeroes" do
+ @d2000_4_6.strftime("%M").should == "00"
+ end
+
+ it "should be able to print the month with leading zeroes" do
+ @d2000_4_6.strftime("%m").should == "04"
+ @d2009_9_18.strftime('%m').should == '09'
+ end
+
+ it "should be able to add a newline" do
+ @d2000_4_6.strftime("%n").should == "\n"
+ end
+
+ it "should be able to show AM/PM" do
+ @d2000_4_6.strftime("%P").should == "am"
+ end
+
+ it "should be able to show am/pm" do
+ @d2000_4_6.strftime("%p").should == "AM"
+ end
+
+ it "should be able to show the number of seconds with leading zeroes" do
+ @d2000_4_6.strftime("%S").should == "00"
+ end
+
+ it "should be able to show the number of seconds since the unix epoch for a date" do
+ @d2000_4_6.strftime("%s").should == "954979200"
+ end
+
+ it "should be able to add a tab" do
+ @d2000_4_6.strftime("%t").should == "\t"
+ end
+
+ it "should be able to show the week number with the week starting on Sunday (%U) and Monday (%W)" do
+ @d2000_4_6.strftime("%U").should == "14" # Thursday
+ @d2000_4_6.strftime("%W").should == "14"
+
+ @d2000_4_9.strftime("%U").should == "15" # Sunday
+ @d2000_4_9.strftime("%W").should == "14"
+
+ @d2000_4_10.strftime("%U").should == "15" # Monday
+ @d2000_4_10.strftime("%W").should == "15"
+
+ # start of the year
+ saturday_first = @new_date[2000,1,1]
+ saturday_first.strftime("%U").should == "00"
+ saturday_first.strftime("%W").should == "00"
+
+ sunday_second = @new_date[2000,1,2]
+ sunday_second.strftime("%U").should == "01"
+ sunday_second.strftime("%W").should == "00"
+
+ monday_third = @new_date[2000,1,3]
+ monday_third.strftime("%U").should == "01"
+ monday_third.strftime("%W").should == "01"
+
+ sunday_9th = @new_date[2000,1,9]
+ sunday_9th.strftime("%U").should == "02"
+ sunday_9th.strftime("%W").should == "01"
+
+ monday_10th = @new_date[2000,1,10]
+ monday_10th.strftime("%U").should == "02"
+ monday_10th.strftime("%W").should == "02"
+
+ # middle of the year
+ some_sunday = @new_date[2000,8,6]
+ some_sunday.strftime("%U").should == "32"
+ some_sunday.strftime("%W").should == "31"
+ some_monday = @new_date[2000,8,7]
+ some_monday.strftime("%U").should == "32"
+ some_monday.strftime("%W").should == "32"
+
+ # end of year, and start of next one
+ saturday_30th = @new_date[2000,12,30]
+ saturday_30th.strftime("%U").should == "52"
+ saturday_30th.strftime("%W").should == "52"
+
+ sunday_last = @new_date[2000,12,31]
+ sunday_last.strftime("%U").should == "53"
+ sunday_last.strftime("%W").should == "52"
+
+ monday_first = @new_date[2001,1,1]
+ monday_first.strftime("%U").should == "00"
+ monday_first.strftime("%W").should == "01"
+ end
+
+ it "should be able to show the commercial week day" do
+ @d2000_4_9.strftime("%u").should == "7"
+ @d2000_4_10.strftime("%u").should == "1"
+ end
+
+ it "should be able to show the commercial week with %V" do
+ @d2000_4_9.strftime("%V").should == "14"
+ @d2000_4_10.strftime("%V").should == "15"
+ end
+
+ # %W: see %U
+
+ it "should be able to show the week day" do
+ @d2000_4_9.strftime("%w").should == "0"
+ @d2000_4_10.strftime("%w").should == "1"
+ @d2009_9_18.strftime('%w').should == '5'
+ end
+
+ it "should be able to show the year in YYYY format" do
+ @d2000_4_9.strftime("%Y").should == "2000"
+ @d2009_9_18.strftime('%Y').should == '2009'
+ end
+
+ it "should be able to show the year in YY format" do
+ @d2000_4_9.strftime("%y").should == "00"
+ @d2009_9_18.strftime('%y').should == '09'
+ end
+
+ it "should be able to show the timezone of the date with a : separator" do
+ @d2000_4_9.strftime("%z").should == "+0000"
+ end
+
+ it "should be able to escape the % character" do
+ @d2000_4_9.strftime("%%").should == "%"
+ end
+
+ # Combining conversion specifiers
+ it "should be able to print the date in full" do
+ @d2000_4_6.strftime("%c").should == "Thu Apr 6 00:00:00 2000"
+ @d2000_4_6.strftime("%c").should == @d2000_4_6.strftime('%a %b %e %H:%M:%S %Y')
+ end
+
+ it "should be able to print the date with slashes" do
+ @d2000_4_6.strftime("%D").should == "04/06/00"
+ @d2000_4_6.strftime("%D").should == @d2000_4_6.strftime('%m/%d/%y')
+ end
+
+ it "should be able to print the date as YYYY-MM-DD" do
+ @d2000_4_6.strftime("%F").should == "2000-04-06"
+ @d2000_4_6.strftime("%F").should == @d2000_4_6.strftime('%Y-%m-%d')
+ end
+
+ it "should be able to show HH:MM for a date" do
+ @d2000_4_6.strftime("%R").should == "00:00"
+ @d2000_4_6.strftime("%R").should == @d2000_4_6.strftime('%H:%M')
+ end
+
+ it "should be able to show HH:MM:SS AM/PM for a date" do
+ @d2000_4_6.strftime("%r").should == "12:00:00 AM"
+ @d2000_4_6.strftime("%r").should == @d2000_4_6.strftime('%I:%M:%S %p')
+ end
+
+ it "should be able to show HH:MM:SS" do
+ @d2000_4_6.strftime("%T").should == "00:00:00"
+ @d2000_4_6.strftime("%T").should == @d2000_4_6.strftime('%H:%M:%S')
+ end
+
+ it "should be able to show HH:MM:SS" do
+ @d2000_4_6.strftime("%X").should == "00:00:00"
+ @d2000_4_6.strftime("%X").should == @d2000_4_6.strftime('%H:%M:%S')
+ end
+
+ it "should be able to show MM/DD/YY" do
+ @d2000_4_6.strftime("%x").should == "04/06/00"
+ @d2000_4_6.strftime("%x").should == @d2000_4_6.strftime('%m/%d/%y')
+ end
+
+ # GNU modificators
+ it "supports GNU modificators" do
+ time = @new_date[2001, 2, 3]
+
+ time.strftime('%^h').should == 'FEB'
+ time.strftime('%^_5h').should == ' FEB'
+ time.strftime('%0^5h').should == '00FEB'
+ time.strftime('%04m').should == '0002'
+ time.strftime('%0-^5h').should == 'FEB'
+ time.strftime('%_-^5h').should == 'FEB'
+ time.strftime('%^ha').should == 'FEBa'
+
+ time.strftime("%10h").should == ' Feb'
+ time.strftime("%^10h").should == ' FEB'
+ time.strftime("%_10h").should == ' Feb'
+ time.strftime("%_010h").should == '0000000Feb'
+ time.strftime("%0_10h").should == ' Feb'
+ time.strftime("%0_-10h").should == 'Feb'
+ time.strftime("%0-_10h").should == 'Feb'
+ end
+
+ it "supports the '-' modifier to drop leading zeros" do
+ @new_date[2001,3,22].strftime("%-m/%-d/%-y").should == "3/22/1"
+ end
+
+ with_feature :encoding do
+ it "passes the format string's encoding to the result string" do
+ result = @new_date[2010,3,8].strftime("%d. März %Y")
+
+ result.encoding.should == Encoding::UTF_8
+ result.should == "08. März 2010"
+ end
+ end
+end
diff --git a/spec/ruby/shared/time/strftime_for_time.rb b/spec/ruby/shared/time/strftime_for_time.rb
new file mode 100644
index 0000000000..49cd09051a
--- /dev/null
+++ b/spec/ruby/shared/time/strftime_for_time.rb
@@ -0,0 +1,173 @@
+# Shared for Time and DateTime, testing only time components (hours, minutes, seconds and smaller)
+
+describe :strftime_time, shared: true do
+ before :all do
+ @time = @new_time[2001, 2, 3, 4, 5, 6]
+ end
+
+ it "formats time according to the directives in the given format string" do
+ @new_time[1970, 1, 1].strftime("There is %M minutes in epoch").should == "There is 00 minutes in epoch"
+ end
+
+ # Per conversion specifier, not combining
+ it "returns the 24-based hour with %H" do
+ time = @new_time[2009, 9, 18, 18, 0, 0]
+ time.strftime('%H').should == '18'
+ end
+
+ it "returns the 12-based hour with %I" do
+ time = @new_time[2009, 9, 18, 18, 0, 0]
+ time.strftime('%I').should == '06'
+ end
+
+ it "supports 24-hr formatting with %l" do
+ time = @new_time[2004, 8, 26, 22, 38, 3]
+ time.strftime("%k").should == "22"
+ morning_time = @new_time[2004, 8, 26, 6, 38, 3]
+ morning_time.strftime("%k").should == " 6"
+ end
+
+ describe "with %L" do
+ it "formats the milliseconds of the second" do
+ @new_time[2009, 1, 1, 0, 0, Rational(100, 1000)].strftime("%L").should == "100"
+ @new_time[2009, 1, 1, 0, 0, Rational(10, 1000)].strftime("%L").should == "010"
+ @new_time[2009, 1, 1, 0, 0, Rational(1, 1000)].strftime("%L").should == "001"
+ @new_time[2009, 1, 1, 0, 0, Rational(1, 10000)].strftime("%L").should == "000"
+ end
+ end
+
+ it "supports 12-hr formatting with %l" do
+ time = @new_time[2004, 8, 26, 22, 38, 3]
+ time.strftime('%l').should == '10'
+ morning_time = @new_time[2004, 8, 26, 6, 38, 3]
+ morning_time.strftime('%l').should == ' 6'
+ end
+
+ it "returns the minute with %M" do
+ time = @new_time[2009, 9, 18, 12, 6, 0]
+ time.strftime('%M').should == '06'
+ end
+
+ describe "with %N" do
+ it "formats the nanoseconds of the second with %N" do
+ @new_time[2000, 4, 6, 0, 0, Rational(1234560, 1_000_000_000)].strftime("%N").should == "001234560"
+ end
+
+ it "formats the milliseconds of the second with %3N" do
+ @new_time[2000, 4, 6, 0, 0, Rational(50, 1000)].strftime("%3N").should == "050"
+ end
+
+ it "formats the microseconds of the second with %6N" do
+ @new_time[2000, 4, 6, 0, 0, Rational(42, 1000)].strftime("%6N").should == "042000"
+ end
+
+ it "formats the nanoseconds of the second with %9N" do
+ @new_time[2000, 4, 6, 0, 0, Rational(1234, 1_000_000)].strftime("%9N").should == "001234000"
+ end
+
+ it "formats the picoseconds of the second with %12N" do
+ @new_time[2000, 4, 6, 0, 0, Rational(999999999999, 1000_000_000_000)].strftime("%12N").should == "999999999999"
+ end
+ end
+
+ it "supports am/pm formatting with %P" do
+ time = @new_time[2004, 8, 26, 22, 38, 3]
+ time.strftime('%P').should == 'pm'
+ time = @new_time[2004, 8, 26, 11, 38, 3]
+ time.strftime('%P').should == 'am'
+ end
+
+ it "supports AM/PM formatting with %p" do
+ time = @new_time[2004, 8, 26, 22, 38, 3]
+ time.strftime('%p').should == 'PM'
+ time = @new_time[2004, 8, 26, 11, 38, 3]
+ time.strftime('%p').should == 'AM'
+ end
+
+ it "returns the second with %S" do
+ time = @new_time[2009, 9, 18, 12, 0, 6]
+ time.strftime('%S').should == '06'
+ end
+
+ it "should be able to show the number of seconds since the unix epoch" do
+ @new_time_in_zone["GMT", 0, 2005].strftime("%s").should == "1104537600"
+ end
+
+ it "returns the timezone with %Z" do
+ time = @new_time[2009, 9, 18, 12, 0, 0]
+ zone = time.zone
+ time.strftime("%Z").should == zone
+ end
+
+ describe "with %z" do
+ it "formats a UTC time offset as '+0000'" do
+ @new_time_in_zone["GMT", 0, 2005].strftime("%z").should == "+0000"
+ end
+
+ it "formats a local time with positive UTC offset as '+HHMM'" do
+ @new_time_in_zone["CET", 1, 2005].strftime("%z").should == "+0100"
+ end
+
+ it "formats a local time with negative UTC offset as '-HHMM'" do
+ @new_time_in_zone["PST", -8, 2005].strftime("%z").should == "-0800"
+ end
+
+ it "formats a time with fixed positive offset as '+HHMM'" do
+ @new_time_with_offset[2012, 1, 1, 0, 0, 0, 3660].strftime("%z").should == "+0101"
+ end
+
+ it "formats a time with fixed negative offset as '-HHMM'" do
+ @new_time_with_offset[2012, 1, 1, 0, 0, 0, -3660].strftime("%z").should == "-0101"
+ end
+
+ it "formats a time with fixed offset as '+/-HH:MM' with ':' specifier" do
+ @new_time_with_offset[2012, 1, 1, 0, 0, 0, 3660].strftime("%:z").should == "+01:01"
+ end
+
+ it "formats a time with fixed offset as '+/-HH:MM:SS' with '::' specifier" do
+ @new_time_with_offset[2012, 1, 1, 0, 0, 0, 3665].strftime("%::z").should == "+01:01:05"
+ end
+ end
+
+ # Combining conversion specifiers
+ it "should be able to print the time in full" do
+ @time.strftime("%c").should == "Sat Feb 3 04:05:06 2001"
+ @time.strftime("%c").should == @time.strftime('%a %b %e %H:%M:%S %Y')
+ end
+
+ it "should be able to show HH:MM" do
+ @time.strftime("%R").should == "04:05"
+ @time.strftime("%R").should == @time.strftime('%H:%M')
+ end
+
+ it "should be able to show HH:MM:SS AM/PM" do
+ @time.strftime("%r").should == "04:05:06 AM"
+ @time.strftime("%r").should == @time.strftime('%I:%M:%S %p')
+ end
+
+ it "supports HH:MM:SS formatting with %T" do
+ @time.strftime('%T').should == '04:05:06'
+ @time.strftime('%T').should == @time.strftime('%H:%M:%S')
+ end
+
+ it "supports HH:MM:SS formatting with %X" do
+ @time.strftime('%X').should == '04:05:06'
+ @time.strftime('%X').should == @time.strftime('%H:%M:%S')
+ end
+
+ # GNU modificators
+ it "supports the '-' modifier to drop leading zeros" do
+ time = @new_time[2001,1,1,14,01,42]
+ time.strftime("%-m/%-d/%-y %-I:%-M %p").should == "1/1/1 2:1 PM"
+
+ time = @new_time[2010,10,10,12,10,42]
+ time.strftime("%-m/%-d/%-y %-I:%-M %p").should == "10/10/10 12:10 PM"
+ end
+
+ it "supports the '-' modifier for padded format directives" do
+ time = @new_time[2010, 8, 8, 8, 10, 42]
+ time.strftime("%-e").should == "8"
+ time.strftime("%-k%p").should == "8AM"
+ time.strftime("%-l%p").should == "8AM"
+ end
+end
diff --git a/spec/ruby/spec_helper.rb b/spec/ruby/spec_helper.rb
new file mode 100644
index 0000000000..c38965d3c5
--- /dev/null
+++ b/spec/ruby/spec_helper.rb
@@ -0,0 +1,32 @@
+use_realpath = File.respond_to?(:realpath)
+root = File.dirname(__FILE__)
+dir = "fixtures/code"
+CODE_LOADING_DIR = use_realpath ? File.realpath(dir, root) : File.expand_path(dir, root)
+
+# Enable Thread.report_on_exception by default to catch thread errors earlier
+if Thread.respond_to? :report_on_exception=
+ Thread.report_on_exception = true
+else
+ class Thread
+ def report_on_exception=(value)
+ raise "shim Thread#report_on_exception used with true" if value
+ end
+ end
+end
+
+# Running directly with ruby some_spec.rb
+unless ENV['MSPEC_RUNNER']
+ mspec_lib = File.expand_path("../../mspec/lib", __FILE__)
+ $LOAD_PATH << mspec_lib if File.directory?(mspec_lib)
+
+ begin
+ require 'mspec'
+ require 'mspec/commands/mspec-run'
+ rescue LoadError
+ puts "Please add -Ipath/to/mspec/lib or clone mspec as a sibling to run the specs."
+ exit 1
+ end
+
+ ARGV.unshift $0
+ MSpecRun.main
+end
diff --git a/sprintf.c b/sprintf.c
index 5884926b3e..53abbddca7 100644
--- a/sprintf.c
+++ b/sprintf.c
@@ -11,8 +11,9 @@
**********************************************************************/
-#include "internal.h"
+#include "ruby/encoding.h"
#include "ruby/re.h"
+#include "internal.h"
#include "id.h"
#include <math.h>
#include <stdarg.h>
@@ -23,7 +24,8 @@
#define BIT_DIGITS(N) (((N)*146)/485 + 1) /* log2(10) =~ 146/485 */
-static void fmt_setup(char*,size_t,int,int,int,int);
+static char *fmt_setup(char*,size_t,int,int,int,int);
+static char *ruby_ultoa(unsigned long val, char *endp, int base, int octzero);
static char
sign_bits(int base, const char *p)
@@ -55,8 +57,9 @@ sign_bits(int base, const char *p)
#define CHECK(l) do {\
int cr = ENC_CODERANGE(result);\
- while (blen + (l) >= bsiz) {\
+ while ((l) >= bsiz - blen) {\
bsiz*=2;\
+ if (bsiz<0) rb_raise(rb_eArgError, "too big specifier");\
}\
rb_str_resize(result, bsiz);\
ENC_CODERANGE_SET(result, cr);\
@@ -74,6 +77,7 @@ sign_bits(int base, const char *p)
} while (0)
#define FILL(c, l) do { \
+ if ((l) <= 0) break;\
CHECK(l);\
FILL_(c, l);\
} while (0)
@@ -199,7 +203,7 @@ get_hash(volatile VALUE *hash, int argc, const VALUE *argv)
* any additional arguments. Within the format string, any characters
* other than format sequences are copied to the result.
*
- * The syntax of a format sequence is follows.
+ * The syntax of a format sequence is as follows.
*
* %[flags][width][.precision]type
*
@@ -475,6 +479,7 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
int tainted = 0;
VALUE nextvalue;
VALUE tmp;
+ VALUE orig;
VALUE str;
volatile VALUE hash = Qundef;
@@ -498,7 +503,8 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
if (OBJ_TAINTED(fmt)) tainted = 1;
StringValue(fmt);
enc = rb_enc_get(fmt);
- fmt = rb_str_new4(fmt);
+ orig = fmt;
+ fmt = rb_str_tmp_frozen_acquire(fmt);
p = RSTRING_PTR(fmt);
end = p + RSTRING_LEN(fmt);
blen = 0;
@@ -515,6 +521,9 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
VALUE sym = Qnil;
for (t = p; t < end && *t != '%'; t++) ;
+ if (t + 1 == end) {
+ rb_raise(rb_eArgError, "incomplete format specifier; use %%%% (double %%) instead");
+ }
PUSH(p, t - p);
if (coderange != ENC_CODERANGE_BROKEN && scanned < blen) {
scanned += rb_str_coderange_scan_restartable(buf+scanned, buf+blen, enc, &coderange);
@@ -625,7 +634,7 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
}
nextvalue = rb_hash_default_value(hash, sym);
if (NIL_P(nextvalue)) {
- rb_enc_raise(enc, rb_eKeyError, "key%.*s not found", len, start);
+ rb_key_err_raise(rb_enc_sprintf(enc, "key%.*s not found", len, start), hash, sym);
}
}
if (term == '}') goto format_s;
@@ -640,6 +649,7 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
if (width < 0) {
flags |= FMINUS;
width = -width;
+ if (width < 0) rb_raise(rb_eArgError, "width too big");
}
p++;
goto retry;
@@ -705,10 +715,10 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
CHECK(n);
rb_enc_mbcput(c, &buf[blen], enc);
blen += n;
- FILL(' ', width-1);
+ if (width > 1) FILL(' ', width-1);
}
else {
- FILL(' ', width-1);
+ if (width > 1) FILL(' ', width-1);
CHECK(n);
rb_enc_mbcput(c, &buf[blen], enc);
blen += n;
@@ -755,20 +765,15 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
if ((flags&FWIDTH) && (width > slen)) {
width -= (int)slen;
if (!(flags&FMINUS)) {
- CHECK(width);
- while (width--) {
- buf[blen++] = ' ';
- }
+ FILL(' ', width);
+ width = 0;
}
CHECK(len);
memcpy(&buf[blen], RSTRING_PTR(str), len);
RB_GC_GUARD(str);
blen += len;
if (flags&FMINUS) {
- CHECK(width);
- while (width--) {
- buf[blen++] = ' ';
- }
+ FILL(' ', width);
}
rb_enc_associate(result, enc);
break;
@@ -791,7 +796,7 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
{
volatile VALUE val = GETARG();
int valsign;
- char nbuf[64], *s;
+ char nbuf[BIT_DIGITS(SIZEOF_LONG*CHAR_BIT)+2], *s;
const char *prefix = 0;
int sign = 0, dots = 0;
char sc = 0;
@@ -938,9 +943,8 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
sc = ' ';
width--;
}
- snprintf(nbuf, sizeof(nbuf), "%ld", v);
- s = nbuf;
- len = (int)strlen(s);
+ s = ruby_ultoa((unsigned long)v, nbuf + sizeof(nbuf), 10, 0);
+ len = (int)(nbuf + sizeof(nbuf) - s);
}
else {
tmp = rb_big2str(val, 10);
@@ -1006,35 +1010,28 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
width -= prec;
}
if (!(flags&FMINUS)) {
- CHECK(width);
- while (width-- > 0) {
- buf[blen++] = ' ';
- }
+ FILL(' ', width);
+ width = 0;
}
if (sc) PUSH(&sc, 1);
if (prefix) {
int plen = (int)strlen(prefix);
PUSH(prefix, plen);
}
- CHECK(prec - len);
if (dots) PUSH("..", 2);
- if (!sign && valsign < 0) {
- char c = sign_bits(base, p);
- while (len < prec--) {
- buf[blen++] = c;
+ if (prec > len) {
+ CHECK(prec - len);
+ if (!sign && valsign < 0) {
+ char c = sign_bits(base, p);
+ FILL_(c, prec - len);
}
- }
- else if ((flags & (FMINUS|FPREC)) != FMINUS) {
- while (len < prec--) {
- buf[blen++] = '0';
+ else if ((flags & (FMINUS|FPREC)) != FMINUS) {
+ FILL_('0', prec - len);
}
}
PUSH(s, len);
RB_GC_GUARD(tmp);
- CHECK(width);
- while (width-- > 0) {
- buf[blen++] = ' ';
- }
+ FILL(' ', width);
}
break;
@@ -1080,15 +1077,15 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
if (prec >= len) len = prec + 1; /* integer part 0 */
if (sign || (flags&FSPACE)) ++len;
if (prec > 0) ++len; /* period */
- CHECK(len > width ? len : width);
fill = width > len ? width - len : 0;
- if (fill && !(flags&FMINUS) && !(flags&FZERO)) {
+ CHECK(fill + len);
+ if (fill && !(flags&(FMINUS|FZERO))) {
FILL_(' ', fill);
}
if (sign || (flags&FSPACE)) {
buf[blen++] = sign > 0 ? '+' : sign < 0 ? '-' : ' ';
}
- if (fill && !(flags&FMINUS) && (flags&FZERO)) {
+ if (fill && (flags&(FMINUS|FZERO)) == FZERO) {
FILL_('0', fill);
}
len = RSTRING_LEN(val) + zero;
@@ -1129,12 +1126,13 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
{
VALUE val = GETARG();
double fval;
- int i, need;
- char fbuf[32];
fval = RFLOAT_VALUE(rb_Float(val));
if (isnan(fval) || isinf(fval)) {
const char *expr;
+ int need;
+ int elen;
+ char sign = '\0';
if (isnan(fval)) {
expr = "NaN";
@@ -1143,52 +1141,39 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
expr = "Inf";
}
need = (int)strlen(expr);
- if ((!isnan(fval) && fval < 0.0) || (flags & FPLUS))
- need++;
+ elen = need;
+ if (!isnan(fval) && fval < 0.0)
+ sign = '-';
+ else if (flags & (FPLUS|FSPACE))
+ sign = (flags & FPLUS) ? '+' : ' ';
+ if (sign)
+ ++need;
if ((flags & FWIDTH) && need < width)
need = width;
- CHECK(need + 1);
- snprintf(&buf[blen], need + 1, "%*s", need, "");
+ FILL(' ', need);
if (flags & FMINUS) {
- if (!isnan(fval) && fval < 0.0)
- buf[blen++] = '-';
- else if (flags & FPLUS)
- buf[blen++] = '+';
- else if (flags & FSPACE)
- blen++;
- memcpy(&buf[blen], expr, strlen(expr));
+ if (sign)
+ buf[blen - need--] = sign;
+ memcpy(&buf[blen - need], expr, elen);
}
else {
- if (!isnan(fval) && fval < 0.0)
- buf[blen + need - strlen(expr) - 1] = '-';
- else if (flags & FPLUS)
- buf[blen + need - strlen(expr) - 1] = '+';
- else if ((flags & FSPACE) && need > width)
- blen++;
- memcpy(&buf[blen + need - strlen(expr)], expr,
- strlen(expr));
+ if (sign)
+ buf[blen - elen - 1] = sign;
+ memcpy(&buf[blen - elen], expr, elen);
}
- blen += strlen(&buf[blen]);
break;
}
-
- fmt_setup(fbuf, sizeof(fbuf), *p, flags, width, prec);
- need = 0;
- if (*p != 'e' && *p != 'E') {
- i = INT_MIN;
- frexp(fval, &i);
- if (i > 0)
- need = BIT_DIGITS(i);
+ else {
+ int cr = ENC_CODERANGE(result);
+ char fbuf[2*BIT_DIGITS(SIZEOF_INT*CHAR_BIT)+10];
+ char *fmt = fmt_setup(fbuf, sizeof(fbuf), *p, flags, width, prec);
+ rb_str_set_len(result, blen);
+ rb_str_catf(result, fmt, fval);
+ ENC_CODERANGE_SET(result, cr);
+ bsiz = rb_str_capacity(result);
+ RSTRING_GETMEM(result, buf, blen);
}
- need += (flags&FPREC) ? prec : default_float_precision;
- if ((flags&FWIDTH) && need < width)
- need = width;
- need += 20;
-
- CHECK(need);
- snprintf(&buf[blen], need, fbuf, fval);
- blen += strlen(&buf[blen]);
}
break;
}
@@ -1196,7 +1181,7 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
}
sprint_exit:
- RB_GC_GUARD(fmt);
+ rb_str_tmp_frozen_release(orig, fmt);
/* XXX - We cannot validate the number of arguments if (digit)$ style used.
*/
if (posarg >= 0 && nextarg < argc) {
@@ -1210,29 +1195,29 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
return result;
}
-static void
+static char *
fmt_setup(char *buf, size_t size, int c, int flags, int width, int prec)
{
- char *end = buf + size;
- *buf++ = '%';
- if (flags & FSHARP) *buf++ = '#';
- if (flags & FPLUS) *buf++ = '+';
- if (flags & FMINUS) *buf++ = '-';
- if (flags & FZERO) *buf++ = '0';
- if (flags & FSPACE) *buf++ = ' ';
+ buf += size;
+ *--buf = '\0';
+ *--buf = c;
- if (flags & FWIDTH) {
- snprintf(buf, end - buf, "%d", width);
- buf += strlen(buf);
+ if (flags & FPREC) {
+ buf = ruby_ultoa(prec, buf, 10, 0);
+ *--buf = '.';
}
- if (flags & FPREC) {
- snprintf(buf, end - buf, ".%d", prec);
- buf += strlen(buf);
+ if (flags & FWIDTH) {
+ buf = ruby_ultoa(width, buf, 10, 0);
}
- *buf++ = c;
- *buf = '\0';
+ if (flags & FSPACE) *--buf = ' ';
+ if (flags & FZERO) *--buf = '0';
+ if (flags & FMINUS) *--buf = '-';
+ if (flags & FPLUS) *--buf = '+';
+ if (flags & FSHARP) *--buf = '#';
+ *--buf = '%';
+ return buf;
}
#undef FILE
@@ -1263,24 +1248,47 @@ fmt_setup(char *buf, size_t size, int c, int flags, int width, int prec)
#endif
#define lower_hexdigits (ruby_hexdigits+0)
#define upper_hexdigits (ruby_hexdigits+16)
+#if defined RUBY_USE_SETJMPEX && RUBY_USE_SETJMPEX
+# undef MAYBE_UNUSED
+# define MAYBE_UNUSED(x) x = 0
+#endif
#include "vsnprintf.c"
+static char *
+ruby_ultoa(unsigned long val, char *endp, int base, int flags)
+{
+ const char *xdigs = lower_hexdigits;
+ int octzero = flags & FSHARP;
+ return BSD__ultoa(val, endp, base, octzero, xdigs);
+}
+
+static int ruby_do_vsnprintf(char *str, size_t n, const char *fmt, va_list ap);
+
int
ruby_vsnprintf(char *str, size_t n, const char *fmt, va_list ap)
{
- int ret;
+ if (str && (ssize_t)n < 1)
+ return (EOF);
+ return ruby_do_vsnprintf(str, n, fmt, ap);
+}
+
+static int
+ruby_do_vsnprintf(char *str, size_t n, const char *fmt, va_list ap)
+{
+ ssize_t ret;
rb_printf_buffer f;
- if ((int)n < 1)
- return (EOF);
f._flags = __SWR | __SSTR;
f._bf._base = f._p = (unsigned char *)str;
- f._bf._size = f._w = n - 1;
+ f._bf._size = f._w = str ? (n - 1) : 0;
f.vwrite = BSD__sfvwrite;
f.vextra = 0;
- ret = (int)BSD_vfprintf(&f, fmt, ap);
- *f._p = 0;
- return ret;
+ ret = BSD_vfprintf(&f, fmt, ap);
+ if (str) *f._p = 0;
+#if SIZEOF_SIZE_T > SIZEOF_INT
+ if (n > INT_MAX) return INT_MAX;
+#endif
+ return (int)ret;
}
int
@@ -1289,11 +1297,11 @@ ruby_snprintf(char *str, size_t n, char const *fmt, ...)
int ret;
va_list ap;
- if ((int)n < 1)
+ if (str && (ssize_t)n < 1)
return (EOF);
va_start(ap, fmt);
- ret = ruby_vsnprintf(str, n, fmt, ap);
+ ret = ruby_do_vsnprintf(str, n, fmt, ap);
va_end(ap);
return ret;
}
@@ -1309,14 +1317,19 @@ ruby__sfvwrite(register rb_printf_buffer *fp, register struct __suio *uio)
struct __siov *iov;
VALUE result = (VALUE)fp->_bf._base;
char *buf = (char*)fp->_p;
- size_t len, n;
- size_t blen = buf - RSTRING_PTR(result), bsiz = fp->_w;
+ long len, n;
+ long blen = buf - RSTRING_PTR(result), bsiz = fp->_w;
if (RBASIC(result)->klass) {
rb_raise(rb_eRuntimeError, "rb_vsprintf reentered");
}
- if ((len = uio->uio_resid) == 0)
+ if (uio->uio_resid == 0)
return 0;
+#if SIZE_MAX > LONG_MAX
+ if (uio->uio_resid >= LONG_MAX)
+ rb_raise(rb_eRuntimeError, "too big string");
+#endif
+ len = (long)uio->uio_resid;
CHECK(len);
buf += blen;
fp->_w = bsiz;
@@ -1365,6 +1378,12 @@ ruby__sfvextra(rb_printf_buffer *fp, size_t valsize, void *valp, long *sz, int s
}
value = rb_inspect(value);
}
+ else if (SYMBOL_P(value)) {
+ value = rb_sym2str(value);
+ if (sign == ' ' && !rb_str_symname_p(value)) {
+ value = rb_str_inspect(value);
+ }
+ }
else {
value = rb_obj_as_string(value);
if (sign == ' ') value = QUOTE(value);
diff --git a/st.c b/st.c
index 7bf5e13284..ed235c674e 100644
--- a/st.c
+++ b/st.c
@@ -90,6 +90,11 @@
o To save more memory we use 8-, 16-, 32- and 64- bit indexes in
bins depending on the current hash table size.
+ o The implementation takes into account that the table can be
+ rebuilt during hashing or comparison functions. It can happen if
+ the functions are implemented in Ruby and a thread switch occurs
+ during their execution.
+
This implementation speeds up the Ruby hash table benchmarks in
average by more 40% on Intel Haswell CPU.
@@ -120,7 +125,7 @@
#endif
#ifdef ST_DEBUG
-#define st_assert(cond) assert(cond)
+#define st_assert assert
#else
#define st_assert(cond) ((void)(0 && (cond)))
#endif
@@ -135,7 +140,7 @@ struct st_table_entry {
};
#define type_numhash st_hashtype_num
-const struct st_hash_type st_hashtype_num = {
+static const struct st_hash_type st_hashtype_num = {
st_numcmp,
st_numhash,
};
@@ -171,8 +176,17 @@ static const struct st_hash_type type_strcasehash = {
#endif
#define EQUAL(tab,x,y) ((x) == (y) || (*(tab)->type->compare)((x),(y)) == 0)
-#define PTR_EQUAL(tab, ptr, hash_val, key) \
- ((ptr)->hash == (hash_val) && EQUAL((tab), (key), (ptr)->key))
+#define PTR_EQUAL(tab, ptr, hash_val, key_) \
+ ((ptr)->hash == (hash_val) && EQUAL((tab), (key_), (ptr)->key))
+
+/* As PRT_EQUAL only its result is returned in RES. REBUILT_P is set
+ up to TRUE if the table is rebuilt during the comparison. */
+#define DO_PTR_EQUAL_CHECK(tab, ptr, hash_val, key, res, rebuilt_p) \
+ do { \
+ unsigned int _old_rebuilds_num = (tab)->rebuilds_num; \
+ res = PTR_EQUAL(tab, ptr, hash_val, key); \
+ rebuilt_p = _old_rebuilds_num != (tab)->rebuilds_num; \
+ } while (FALSE)
/* Features of a table. */
struct st_features {
@@ -192,7 +206,7 @@ struct st_features {
/* Features of all possible size tables. */
#if SIZEOF_ST_INDEX_T == 8
#define MAX_POWER2 62
-struct st_features features[] = {
+static const struct st_features features[] = {
{0, 1, 0, 0x0},
{1, 2, 0, 0x1},
{2, 3, 0, 0x1},
@@ -261,7 +275,7 @@ struct st_features features[] = {
#else
#define MAX_POWER2 30
-struct st_features features[] = {
+static const struct st_features features[] = {
{0, 1, 0, 0x1},
{1, 2, 0, 0x1},
{2, 3, 0, 0x2},
@@ -301,15 +315,14 @@ struct st_features features[] = {
#define RESERVED_HASH_VAL (~(st_hash_t) 0)
#define RESERVED_HASH_SUBSTITUTION_VAL ((st_hash_t) 0)
+const st_hash_t st_reserved_hash_val = RESERVED_HASH_VAL;
+const st_hash_t st_reserved_hash_substitution_val = RESERVED_HASH_SUBSTITUTION_VAL;
+
/* Return hash value of KEY for table TAB. */
static inline st_hash_t
-do_hash(st_data_t key, st_table *tab) {
- st_index_t h = (st_index_t)(tab->curr_hash)(key);
-#if SIZEOF_INT == SIZEOF_VOIDP
- st_hash_t hash = h;
-#else
- st_hash_t hash = h;
-#endif
+do_hash(st_data_t key, st_table *tab)
+{
+ st_hash_t hash = (st_hash_t)(tab->type->hash)(key);
/* RESERVED_HASH_VAL is used for a deleted entry. Map it into
another value. Such mapping should be extremely rare. */
@@ -329,7 +342,8 @@ do_hash(st_data_t key, st_table *tab) {
/* Return smallest n >= MINIMAL_POWER2 such 2^n > SIZE. */
static int
-get_power2(st_index_t size) {
+get_power2(st_index_t size)
+{
unsigned int n;
for (n = 0; size != 0; n++)
@@ -347,17 +361,19 @@ get_power2(st_index_t size) {
/* Return value of N-th bin in array BINS of table with bins size
index S. */
static inline st_index_t
-get_bin(st_index_t *bins, int s, st_index_t n) {
- return (s == 0 ? ((unsigned char *) bins)[n]
- : s == 1 ? ((unsigned short *) bins)[n]
- : s == 2 ? ((unsigned int *) bins)[n]
- : ((st_index_t *) bins)[n]);
+get_bin(st_index_t *bins, int s, st_index_t n)
+{
+ return (s == 0 ? ((unsigned char *) bins)[n]
+ : s == 1 ? ((unsigned short *) bins)[n]
+ : s == 2 ? ((unsigned int *) bins)[n]
+ : ((st_index_t *) bins)[n]);
}
/* Set up N-th bin in array BINS of table with bins size index S to
value V. */
static inline void
-set_bin(st_index_t *bins, int s, st_index_t n, st_index_t v) {
+set_bin(st_index_t *bins, int s, st_index_t n, st_index_t v)
+{
if (s == 0) ((unsigned char *) bins)[n] = (unsigned char) v;
else if (s == 1) ((unsigned short *) bins)[n] = (unsigned short) v;
else if (s == 2) ((unsigned int *) bins)[n] = (unsigned int) v;
@@ -381,6 +397,11 @@ set_bin(st_index_t *bins, int s, st_index_t n, st_index_t v) {
#define UNDEFINED_ENTRY_IND (~(st_index_t) 0)
#define UNDEFINED_BIN_IND (~(st_index_t) 0)
+/* Entry and bin values returned when we found a table rebuild during
+ the search. */
+#define REBUILT_TABLE_ENTRY_IND (~(st_index_t) 1)
+#define REBUILT_TABLE_BIN_IND (~(st_index_t) 1)
+
/* Mark I-th bin of table TAB as corresponding to a deleted table
entry. Update number of entries in the table and number of bins
corresponding to deleted entries. */
@@ -410,44 +431,51 @@ set_bin(st_index_t *bins, int s, st_index_t n, st_index_t v) {
/* Return bin size index of table TAB. */
static inline unsigned int
-get_size_ind(const st_table *tab) {
+get_size_ind(const st_table *tab)
+{
return tab->size_ind;
}
/* Return the number of allocated bins of table TAB. */
static inline st_index_t
-get_bins_num(const st_table *tab) {
- return 1<<tab->bin_power;
+get_bins_num(const st_table *tab)
+{
+ return ((st_index_t) 1)<<tab->bin_power;
}
/* Return mask for a bin index in table TAB. */
static inline st_index_t
-bins_mask(const st_table *tab) {
+bins_mask(const st_table *tab)
+{
return get_bins_num(tab) - 1;
}
/* Return the index of table TAB bin corresponding to
HASH_VALUE. */
static inline st_index_t
-hash_bin(st_hash_t hash_value, st_table *tab) {
+hash_bin(st_hash_t hash_value, st_table *tab)
+{
return hash_value & bins_mask(tab);
}
/* Return the number of allocated entries of table TAB. */
static inline st_index_t
-get_allocated_entries(const st_table *tab) {
- return 1<<tab->entry_power;
+get_allocated_entries(const st_table *tab)
+{
+ return ((st_index_t) 1)<<tab->entry_power;
}
/* Return size of the allocated bins of table TAB. */
static inline st_index_t
-bins_size(const st_table *tab) {
+bins_size(const st_table *tab)
+{
return features[tab->entry_power].bins_words * sizeof (st_index_t);
}
/* Mark all bins of table TAB as empty. */
static void
-initialize_bins(st_table *tab) {
+initialize_bins(st_table *tab)
+{
memset(tab->bins, 0, bins_size(tab));
}
@@ -455,7 +483,6 @@ initialize_bins(st_table *tab) {
static void
make_tab_empty(st_table *tab)
{
- tab->curr_hash = tab->type->hash;
tab->num_entries = 0;
tab->entries_start = tab->entries_bound = 0;
if (tab->bins != NULL)
@@ -463,35 +490,40 @@ make_tab_empty(st_table *tab)
}
#ifdef ST_DEBUG
+#define st_assert_notinitial(ent) \
+ do { \
+ st_assert(ent.hash != (st_hash_t) ST_INIT_VAL); \
+ st_assert(ent.key != ST_INIT_VAL); \
+ st_assert(ent.record != ST_INIT_VAL); \
+ } while (0)
/* Check the table T consistency. It can be extremely slow. So use
it only for debugging. */
static void
-st_check(st_table *tab) {
+st_check(st_table *tab)
+{
st_index_t d, e, i, n, p;
for (p = get_allocated_entries(tab), i = 0; p > 1; i++, p>>=1)
;
p = i;
- assert(p >= MINIMAL_POWER2);
- assert(tab->entries_bound <= get_allocated_entries(tab)
- && tab->entries_start <= tab->entries_bound);
+ st_assert(p >= MINIMAL_POWER2);
+ st_assert(tab->entries_bound <= get_allocated_entries(tab));
+ st_assert(tab->entries_start <= tab->entries_bound);
n = 0;
return;
if (tab->entries_bound != 0)
for (i = tab->entries_start; i < tab->entries_bound; i++) {
- assert(tab->entries[i].hash != (st_hash_t) ST_INIT_VAL
- && tab->entries[i].key != ST_INIT_VAL
- && tab->entries[i].record != ST_INIT_VAL);
+ st_assert_notinitial(tab->entries[i]);
if (! DELETED_ENTRY_P(&tab->entries[i]))
n++;
}
- assert(n == tab->num_entries);
+ st_assert(n == tab->num_entries);
if (tab->bins == NULL)
- assert(p <= MAX_POWER2_FOR_TABLES_WITHOUT_BINS);
+ st_assert(p <= MAX_POWER2_FOR_TABLES_WITHOUT_BINS);
else {
- assert(p > MAX_POWER2_FOR_TABLES_WITHOUT_BINS);
+ st_assert(p > MAX_POWER2_FOR_TABLES_WITHOUT_BINS);
for (n = d = i = 0; i < get_bins_num(tab); i++) {
- assert(get_bin(tab->bins, tab->size_ind, i) != ST_INIT_VAL);
+ st_assert(get_bin(tab->bins, tab->size_ind, i) != ST_INIT_VAL);
if (IND_DELETED_BIN_P(tab, i)) {
d++;
continue;
@@ -500,14 +532,12 @@ st_check(st_table *tab) {
continue;
n++;
e = get_bin(tab->bins, tab->size_ind, i) - ENTRY_BASE;
- assert(tab->entries_start <= e && e < tab->entries_bound);
- assert(! DELETED_ENTRY_P(&tab->entries[e]));
- assert(tab->entries[e].hash != (st_hash_t) ST_INIT_VAL
- && tab->entries[e].key != ST_INIT_VAL
- && tab->entries[e].record != ST_INIT_VAL);
+ st_assert(tab->entries_start <= e && e < tab->entries_bound);
+ st_assert(! DELETED_ENTRY_P(&tab->entries[e]));
+ st_assert_notinitial(tab->entries[e]);
}
- assert(n == tab->num_entries);
- assert(n + d < get_bins_num(tab));
+ st_assert(n == tab->num_entries);
+ st_assert(n + d < get_bins_num(tab));
}
}
#endif
@@ -544,7 +574,8 @@ stat_col(void)
entries. The real number of entries which the table can hold is
the nearest power of two for SIZE. */
st_table *
-st_init_table_with_size(const struct st_hash_type *type, st_index_t size) {
+st_init_table_with_size(const struct st_hash_type *type, st_index_t size)
+{
st_table *tab;
int n;
@@ -567,7 +598,6 @@ st_init_table_with_size(const struct st_hash_type *type, st_index_t size) {
tab->entry_power = n;
tab->bin_power = features[n].bin_power;
tab->size_ind = features[n].size_ind;
- tab->inside_rebuild_p = FALSE;
if (n <= MAX_POWER2_FOR_TABLES_WITHOUT_BINS)
tab->bins = NULL;
else
@@ -591,53 +621,61 @@ st_init_table_with_size(const struct st_hash_type *type, st_index_t size) {
/* Create and return table with TYPE which can hold a minimal number
of entries (see comments for get_power2). */
st_table *
-st_init_table(const struct st_hash_type *type) {
+st_init_table(const struct st_hash_type *type)
+{
return st_init_table_with_size(type, 0);
}
/* Create and return table which can hold a minimal number of
numbers. */
st_table *
-st_init_numtable(void) {
+st_init_numtable(void)
+{
return st_init_table(&type_numhash);
}
/* Create and return table which can hold SIZE numbers. */
st_table *
-st_init_numtable_with_size(st_index_t size) {
+st_init_numtable_with_size(st_index_t size)
+{
return st_init_table_with_size(&type_numhash, size);
}
/* Create and return table which can hold a minimal number of
strings. */
st_table *
-st_init_strtable(void) {
+st_init_strtable(void)
+{
return st_init_table(&type_strhash);
}
/* Create and return table which can hold SIZE strings. */
st_table *
-st_init_strtable_with_size(st_index_t size) {
+st_init_strtable_with_size(st_index_t size)
+{
return st_init_table_with_size(&type_strhash, size);
}
/* Create and return table which can hold a minimal number of strings
whose character case is ignored. */
st_table *
-st_init_strcasetable(void) {
+st_init_strcasetable(void)
+{
return st_init_table(&type_strcasehash);
}
/* Create and return table which can hold SIZE strings whose character
case is ignored. */
st_table *
-st_init_strcasetable_with_size(st_index_t size) {
+st_init_strcasetable_with_size(st_index_t size)
+{
return st_init_table_with_size(&type_strcasehash, size);
}
/* Make table TAB empty. */
void
-st_clear(st_table *tab) {
+st_clear(st_table *tab)
+{
make_tab_empty(tab);
tab->rebuilds_num++;
#ifdef ST_DEBUG
@@ -647,7 +685,8 @@ st_clear(st_table *tab) {
/* Free table TAB space. */
void
-st_free_table(st_table *tab) {
+st_free_table(st_table *tab)
+{
if (tab->bins != NULL)
free(tab->bins);
free(tab->entries);
@@ -656,7 +695,8 @@ st_free_table(st_table *tab) {
/* Return byte size of memory allocted for table TAB. */
size_t
-st_memsize(const st_table *tab) {
+st_memsize(const st_table *tab)
+{
return(sizeof(st_table)
+ (tab->bins == NULL ? 0 : bins_size(tab))
+ get_allocated_entries(tab) * sizeof(st_table_entry));
@@ -677,7 +717,8 @@ find_table_bin_ptr_and_reserve(st_table *tab, st_hash_t *hash_value,
#ifdef HASH_LOG
static void
-count_collision(const struct st_hash_type *type) {
+count_collision(const struct st_hash_type *type)
+{
collision.all++;
if (type == &type_numhash) {
collision.num++;
@@ -712,7 +753,8 @@ count_collision(const struct st_hash_type *type) {
Rebuilding is implemented by creation of a new table or by
compaction of the existing one. */
static void
-rebuild_table(st_table *tab) {
+rebuild_table(st_table *tab)
+{
st_index_t i, ni, bound;
unsigned int size_ind;
st_table *new_tab;
@@ -724,7 +766,6 @@ rebuild_table(st_table *tab) {
st_assert(tab != NULL);
bound = tab->entries_bound;
entries = tab->entries;
- tab->inside_rebuild_p = TRUE;
if ((2 * tab->num_entries <= get_allocated_entries(tab)
&& REBUILD_THRESHOLD * tab->num_entries > get_allocated_entries(tab))
|| tab->num_entries < (1 << MINIMAL_POWER2)) {
@@ -738,8 +779,6 @@ rebuild_table(st_table *tab) {
else {
new_tab = st_init_table_with_size(tab->type,
2 * tab->num_entries - 1);
- st_assert(new_tab->curr_hash == new_tab->type->hash);
- new_tab->curr_hash = tab->curr_hash;
new_entries = new_tab->entries;
}
ni = 0;
@@ -755,9 +794,9 @@ rebuild_table(st_table *tab) {
if (EXPECT(bins != NULL, 1)) {
bin_ind = find_table_bin_ind_direct(new_tab, curr_entry_ptr->hash,
curr_entry_ptr->key);
- st_assert(bin_ind != UNDEFINED_BIN_IND
- && (tab == new_tab || new_tab->rebuilds_num == 0)
- && IND_EMPTY_BIN_P(new_tab, bin_ind));
+ st_assert(bin_ind != UNDEFINED_BIN_IND);
+ st_assert(tab == new_tab || new_tab->rebuilds_num == 0);
+ st_assert(IND_EMPTY_BIN_P(new_tab, bin_ind));
set_bin(bins, size_ind, bin_ind, ni + ENTRY_BASE);
}
new_tab->num_entries++;
@@ -767,7 +806,8 @@ rebuild_table(st_table *tab) {
tab->entry_power = new_tab->entry_power;
tab->bin_power = new_tab->bin_power;
tab->size_ind = new_tab->size_ind;
- st_assert (tab->num_entries == ni && new_tab->num_entries == ni);
+ st_assert(tab->num_entries == ni);
+ st_assert(new_tab->num_entries == ni);
if (tab->bins != NULL)
free(tab->bins);
tab->bins = new_tab->bins;
@@ -778,7 +818,6 @@ rebuild_table(st_table *tab) {
tab->entries_start = 0;
tab->entries_bound = tab->num_entries;
tab->rebuilds_num++;
- tab->inside_rebuild_p = FALSE;
#ifdef ST_DEBUG
st_check(tab);
#endif
@@ -797,7 +836,8 @@ rebuild_table(st_table *tab) {
For our case a is 5, c is 1, and m is a power of two. */
static inline st_index_t
-secondary_hash(st_index_t ind, st_table *tab, st_index_t *perterb) {
+secondary_hash(st_index_t ind, st_table *tab, st_index_t *perterb)
+{
*perterb >>= 11;
ind = (ind << 2) + ind + *perterb + 1;
return hash_bin(ind, tab);
@@ -805,16 +845,22 @@ secondary_hash(st_index_t ind, st_table *tab, st_index_t *perterb) {
/* Find an entry with HASH_VALUE and KEY in TABLE using a linear
search. Return the index of the found entry in array `entries`.
- If it is not found, return UNDEFINED_ENTRY_IND. */
+ If it is not found, return UNDEFINED_ENTRY_IND. If the table was
+ rebuilt during the search, return REBUILT_TABLE_ENTRY_IND. */
static inline st_index_t
-find_entry(st_table *tab, st_hash_t hash_value, st_data_t key) {
- st_index_t i, bound;
+find_entry(st_table *tab, st_hash_t hash_value, st_data_t key)
+{
+ int eq_p, rebuilt_p;
+ st_index_t i, bound;
st_table_entry *entries;
bound = tab->entries_bound;
entries = tab->entries;
for (i = tab->entries_start; i < bound; i++) {
- if (PTR_EQUAL(tab, &entries[i], hash_value, key))
+ DO_PTR_EQUAL_CHECK(tab, &entries[i], hash_value, key, eq_p, rebuilt_p);
+ if (EXPECT(rebuilt_p, 0))
+ return REBUILT_TABLE_ENTRY_IND;
+ if (eq_p)
return i;
}
return UNDEFINED_ENTRY_IND;
@@ -826,9 +872,12 @@ find_entry(st_table *tab, st_hash_t hash_value, st_data_t key) {
/*#define QUADRATIC_PROBE*/
/* Return index of entry with HASH_VALUE and KEY in table TAB. If
- there is no such entry, return UNDEFINED_ENTRY_IND. */
+ there is no such entry, return UNDEFINED_ENTRY_IND. If the table
+ was rebuilt during the search, return REBUILT_TABLE_ENTRY_IND. */
static st_index_t
-find_table_entry_ind(st_table *tab, st_hash_t hash_value, st_data_t key) {
+find_table_entry_ind(st_table *tab, st_hash_t hash_value, st_data_t key)
+{
+ int eq_p, rebuilt_p;
st_index_t ind;
#ifdef QUADRATIC_PROBE
st_index_t d;
@@ -838,7 +887,8 @@ find_table_entry_ind(st_table *tab, st_hash_t hash_value, st_data_t key) {
st_index_t bin;
st_table_entry *entries = tab->entries;
- st_assert(tab != NULL && tab->bins != NULL);
+ st_assert(tab != NULL);
+ st_assert(tab->bins != NULL);
ind = hash_bin(hash_value, tab);
#ifdef QUADRATIC_PROBE
d = 1;
@@ -848,10 +898,13 @@ find_table_entry_ind(st_table *tab, st_hash_t hash_value, st_data_t key) {
FOUND_BIN;
for (;;) {
bin = get_bin(tab->bins, get_size_ind(tab), ind);
- if (! EMPTY_OR_DELETED_BIN_P(bin)
- && PTR_EQUAL(tab, &entries[bin - ENTRY_BASE], hash_value, key))
- break;
- else if (EMPTY_BIN_P(bin))
+ if (! EMPTY_OR_DELETED_BIN_P(bin)) {
+ DO_PTR_EQUAL_CHECK(tab, &entries[bin - ENTRY_BASE], hash_value, key, eq_p, rebuilt_p);
+ if (EXPECT(rebuilt_p, 0))
+ return REBUILT_TABLE_ENTRY_IND;
+ if (eq_p)
+ break;
+ } else if (EMPTY_BIN_P(bin))
return UNDEFINED_ENTRY_IND;
#ifdef QUADRATIC_PROBE
ind = hash_bin(ind + d, tab);
@@ -866,9 +919,12 @@ find_table_entry_ind(st_table *tab, st_hash_t hash_value, st_data_t key) {
/* Find and return index of table TAB bin corresponding to an entry
with HASH_VALUE and KEY. If there is no such bin, return
- UNDEFINED_BIN_IND. */
+ UNDEFINED_BIN_IND. If the table was rebuilt during the search,
+ return REBUILT_TABLE_BIN_IND. */
static st_index_t
-find_table_bin_ind(st_table *tab, st_hash_t hash_value, st_data_t key) {
+find_table_bin_ind(st_table *tab, st_hash_t hash_value, st_data_t key)
+{
+ int eq_p, rebuilt_p;
st_index_t ind;
#ifdef QUADRATIC_PROBE
st_index_t d;
@@ -878,7 +934,8 @@ find_table_bin_ind(st_table *tab, st_hash_t hash_value, st_data_t key) {
st_index_t bin;
st_table_entry *entries = tab->entries;
- st_assert(tab != NULL && tab->bins != NULL);
+ st_assert(tab != NULL);
+ st_assert(tab->bins != NULL);
ind = hash_bin(hash_value, tab);
#ifdef QUADRATIC_PROBE
d = 1;
@@ -888,10 +945,13 @@ find_table_bin_ind(st_table *tab, st_hash_t hash_value, st_data_t key) {
FOUND_BIN;
for (;;) {
bin = get_bin(tab->bins, get_size_ind(tab), ind);
- if (! EMPTY_OR_DELETED_BIN_P(bin)
- && PTR_EQUAL(tab, &entries[bin - ENTRY_BASE], hash_value, key))
- break;
- else if (EMPTY_BIN_P(bin))
+ if (! EMPTY_OR_DELETED_BIN_P(bin)) {
+ DO_PTR_EQUAL_CHECK(tab, &entries[bin - ENTRY_BASE], hash_value, key, eq_p, rebuilt_p);
+ if (EXPECT(rebuilt_p, 0))
+ return REBUILT_TABLE_BIN_IND;
+ if (eq_p)
+ break;
+ } else if (EMPTY_BIN_P(bin))
return UNDEFINED_BIN_IND;
#ifdef QUADRATIC_PROBE
ind = hash_bin(ind + d, tab);
@@ -908,7 +968,8 @@ find_table_bin_ind(st_table *tab, st_hash_t hash_value, st_data_t key) {
with HASH_VALUE and KEY. The entry should be in the table
already. */
static st_index_t
-find_table_bin_ind_direct(st_table *tab, st_hash_t hash_value, st_data_t key) {
+find_table_bin_ind_direct(st_table *tab, st_hash_t hash_value, st_data_t key)
+{
st_index_t ind;
#ifdef QUADRATIC_PROBE
st_index_t d;
@@ -918,7 +979,8 @@ find_table_bin_ind_direct(st_table *tab, st_hash_t hash_value, st_data_t key) {
st_index_t bin;
st_table_entry *entries = tab->entries;
- st_assert(tab != NULL && tab->bins != NULL);
+ st_assert(tab != NULL);
+ st_assert(tab->bins != NULL);
ind = hash_bin(hash_value, tab);
#ifdef QUADRATIC_PROBE
d = 1;
@@ -930,7 +992,7 @@ find_table_bin_ind_direct(st_table *tab, st_hash_t hash_value, st_data_t key) {
bin = get_bin(tab->bins, get_size_ind(tab), ind);
if (EMPTY_OR_DELETED_BIN_P(bin))
return ind;
- st_assert (! PTR_EQUAL(tab, &entries[bin - ENTRY_BASE], hash_value, key));
+ st_assert (entries[bin - ENTRY_BASE].hash != hash_value);
#ifdef QUADRATIC_PROBE
ind = hash_bin(ind + d, tab);
d++;
@@ -941,28 +1003,6 @@ find_table_bin_ind_direct(st_table *tab, st_hash_t hash_value, st_data_t key) {
}
}
-/* Recalculate hashes of entries in table TAB. */
-static void
-reset_entry_hashes (st_table *tab)
-{
- st_index_t i, bound;
- st_table_entry *entries, *curr_entry_ptr;
-
- bound = tab->entries_bound;
- entries = tab->entries;
- for (i = tab->entries_start; i < bound; i++) {
- curr_entry_ptr = &entries[i];
- if (! DELETED_ENTRY_P(curr_entry_ptr))
- curr_entry_ptr->hash = do_hash(curr_entry_ptr->key, tab);
- }
-}
-
-/* If we have the following number of collisions with different keys
- but with the same hash during finding a bin for new entry
- inclusions, possibly a denial attack is going on. Start to use a
- stronger hash. */
-#define HIT_THRESHOULD_FOR_STRONG_HASH 10
-
/* Return index of table TAB bin for HASH_VALUE and KEY through
BIN_IND and the pointed value as the function result. Reserve the
bin for inclusion of the corresponding entry into the table if it
@@ -970,10 +1010,13 @@ reset_entry_hashes (st_table *tab)
bigger entries array. Although we can reuse a deleted bin, the
result bin value is always empty if the table has no entry with
KEY. Return the entries array index of the found entry or
- UNDEFINED_ENTRY_IND if it is not found. */
+ UNDEFINED_ENTRY_IND if it is not found. If the table was rebuilt
+ during the search, return REBUILT_TABLE_ENTRY_IND. */
static st_index_t
find_table_bin_ptr_and_reserve(st_table *tab, st_hash_t *hash_value,
- st_data_t key, st_index_t *bin_ind) {
+ st_data_t key, st_index_t *bin_ind)
+{
+ int eq_p, rebuilt_p;
st_index_t ind;
st_hash_t curr_hash_value = *hash_value;
#ifdef QUADRATIC_PROBE
@@ -984,12 +1027,11 @@ find_table_bin_ptr_and_reserve(st_table *tab, st_hash_t *hash_value,
st_index_t entry_index;
st_index_t first_deleted_bin_ind;
st_table_entry *entries;
- int hit;
- st_assert(tab != NULL && tab->bins != NULL
- && tab->entries_bound <= get_allocated_entries(tab)
- && tab->entries_start <= tab->entries_bound);
- repeat:
+ st_assert(tab != NULL);
+ st_assert(tab->bins != NULL);
+ st_assert(tab->entries_bound <= get_allocated_entries(tab));
+ st_assert(tab->entries_start <= tab->entries_bound);
ind = hash_bin(curr_hash_value, tab);
#ifdef QUADRATIC_PROBE
d = 1;
@@ -999,7 +1041,6 @@ find_table_bin_ptr_and_reserve(st_table *tab, st_hash_t *hash_value,
FOUND_BIN;
first_deleted_bin_ind = UNDEFINED_BIN_IND;
entries = tab->entries;
- hit = 0;
for (;;) {
entry_index = get_bin(tab->bins, get_size_ind(tab), ind);
if (EMPTY_BIN_P(entry_index)) {
@@ -1011,23 +1052,15 @@ find_table_bin_ptr_and_reserve(st_table *tab, st_hash_t *hash_value,
MARK_BIN_EMPTY(tab, ind);
}
break;
- } else if (! DELETED_BIN_P(entry_index)) {
- if (PTR_EQUAL(tab, &entries[entry_index - ENTRY_BASE], curr_hash_value, key))
+ }
+ else if (! DELETED_BIN_P(entry_index)) {
+ DO_PTR_EQUAL_CHECK(tab, &entries[entry_index - ENTRY_BASE], curr_hash_value, key, eq_p, rebuilt_p);
+ if (EXPECT(rebuilt_p, 0))
+ return REBUILT_TABLE_ENTRY_IND;
+ if (eq_p)
break;
- if (curr_hash_value == entries[entry_index - ENTRY_BASE].hash) {
- hit++;
- if (hit > HIT_THRESHOULD_FOR_STRONG_HASH
- && tab->curr_hash != tab->type->strong_hash
- && tab->type->strong_hash != NULL
- && ! tab->inside_rebuild_p) {
- tab->curr_hash = tab->type->strong_hash;
- *hash_value = curr_hash_value = do_hash(key, tab);
- reset_entry_hashes(tab);
- rebuild_table(tab);
- goto repeat;
- }
- }
- } else if (first_deleted_bin_ind == UNDEFINED_BIN_IND)
+ }
+ else if (first_deleted_bin_ind == UNDEFINED_BIN_IND)
first_deleted_bin_ind = ind;
#ifdef QUADRATIC_PROBE
ind = hash_bin(ind + d, tab);
@@ -1044,16 +1077,23 @@ find_table_bin_ptr_and_reserve(st_table *tab, st_hash_t *hash_value,
/* Find an entry with KEY in table TAB. Return non-zero if we found
it. Set up *RECORD to the found entry record. */
int
-st_lookup(st_table *tab, st_data_t key, st_data_t *value) {
+st_lookup(st_table *tab, st_data_t key, st_data_t *value)
+{
st_index_t bin;
st_hash_t hash = do_hash(key, tab);
+ retry:
if (tab->bins == NULL) {
bin = find_entry(tab, hash, key);
+ if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0))
+ goto retry;
if (bin == UNDEFINED_ENTRY_IND)
return 0;
- } else {
+ }
+ else {
bin = find_table_entry_ind(tab, hash, key);
+ if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0))
+ goto retry;
if (bin == UNDEFINED_ENTRY_IND)
return 0;
bin -= ENTRY_BASE;
@@ -1066,16 +1106,23 @@ st_lookup(st_table *tab, st_data_t key, st_data_t *value) {
/* Find an entry with KEY in table TAB. Return non-zero if we found
it. Set up *RESULT to the found table entry key. */
int
-st_get_key(st_table *tab, st_data_t key, st_data_t *result) {
+st_get_key(st_table *tab, st_data_t key, st_data_t *result)
+{
st_index_t bin;
st_hash_t hash = do_hash(key, tab);
+ retry:
if (tab->bins == NULL) {
bin = find_entry(tab, hash, key);
+ if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0))
+ goto retry;
if (bin == UNDEFINED_ENTRY_IND)
return 0;
- } else {
+ }
+ else {
bin = find_table_entry_ind(tab, hash, key);
+ if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0))
+ goto retry;
if (bin == UNDEFINED_ENTRY_IND)
return 0;
bin -= ENTRY_BASE;
@@ -1087,7 +1134,8 @@ st_get_key(st_table *tab, st_data_t key, st_data_t *result) {
/* Check the table and rebuild it if it is necessary. */
static inline void
-rebuild_table_if_necessary (st_table *tab) {
+rebuild_table_if_necessary (st_table *tab)
+{
st_index_t bound = tab->entries_bound;
if (bound == get_allocated_entries(tab))
@@ -1099,7 +1147,8 @@ rebuild_table_if_necessary (st_table *tab) {
already entry with KEY in the table, return nonzero and and update
the value of the found entry. */
int
-st_insert(st_table *tab, st_data_t key, st_data_t value) {
+st_insert(st_table *tab, st_data_t key, st_data_t value)
+{
st_table_entry *entry;
st_index_t bin;
st_index_t ind;
@@ -1107,17 +1156,23 @@ st_insert(st_table *tab, st_data_t key, st_data_t value) {
st_index_t bin_ind;
int new_p;
- rebuild_table_if_necessary(tab);
hash_value = do_hash(key, tab);
+ retry:
+ rebuild_table_if_necessary(tab);
if (tab->bins == NULL) {
bin = find_entry(tab, hash_value, key);
+ if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0))
+ goto retry;
new_p = bin == UNDEFINED_ENTRY_IND;
if (new_p)
tab->num_entries++;
bin_ind = UNDEFINED_BIN_IND;
- } else {
+ }
+ else {
bin = find_table_bin_ptr_and_reserve(tab, &hash_value,
key, &bin_ind);
+ if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0))
+ goto retry;
new_p = bin == UNDEFINED_ENTRY_IND;
bin -= ENTRY_BASE;
}
@@ -1144,9 +1199,10 @@ st_insert(st_table *tab, st_data_t key, st_data_t value) {
/* Insert (KEY, VALUE, HASH) into table TAB. The table should not have
entry with KEY before the insertion. */
-static inline void
+void
st_add_direct_with_hash(st_table *tab,
- st_data_t key, st_data_t value, st_hash_t hash) {
+ st_data_t key, st_data_t value, st_hash_t hash)
+{
st_table_entry *entry;
st_index_t ind;
st_index_t bin_ind;
@@ -1171,7 +1227,8 @@ st_add_direct_with_hash(st_table *tab,
/* Insert (KEY, VALUE) into table TAB. The table should not have
entry with KEY before the insertion. */
void
-st_add_direct(st_table *tab, st_data_t key, st_data_t value) {
+st_add_direct(st_table *tab, st_data_t key, st_data_t value)
+{
st_hash_t hash_value;
hash_value = do_hash(key, tab);
@@ -1183,7 +1240,8 @@ st_add_direct(st_table *tab, st_data_t key, st_data_t value) {
and update the value of the found entry. */
int
st_insert2(st_table *tab, st_data_t key, st_data_t value,
- st_data_t (*func)(st_data_t)) {
+ st_data_t (*func)(st_data_t))
+{
st_table_entry *entry;
st_index_t bin;
st_index_t ind, check;
@@ -1191,15 +1249,23 @@ st_insert2(st_table *tab, st_data_t key, st_data_t value,
st_index_t bin_ind;
int new_p;
- rebuild_table_if_necessary (tab);
hash_value = do_hash(key, tab);
+ retry:
+ rebuild_table_if_necessary (tab);
if (tab->bins == NULL) {
bin = find_entry(tab, hash_value, key);
+ if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0))
+ goto retry;
new_p = bin == UNDEFINED_ENTRY_IND;
+ if (new_p)
+ tab->num_entries++;
bin_ind = UNDEFINED_BIN_IND;
- } else {
+ }
+ else {
bin = find_table_bin_ptr_and_reserve(tab, &hash_value,
key, &bin_ind);
+ if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0))
+ goto retry;
new_p = bin == UNDEFINED_ENTRY_IND;
bin -= ENTRY_BASE;
}
@@ -1207,8 +1273,7 @@ st_insert2(st_table *tab, st_data_t key, st_data_t value,
st_assert(tab->entries_bound < get_allocated_entries(tab));
check = tab->rebuilds_num;
key = (*func)(key);
- st_assert(check == tab->rebuilds_num
- && do_hash(key, tab) == hash_value);
+ st_assert(check == tab->rebuilds_num);
ind = tab->entries_bound++;
entry = &tab->entries[ind];
entry->hash = hash_value;
@@ -1216,6 +1281,7 @@ st_insert2(st_table *tab, st_data_t key, st_data_t value,
entry->record = value;
if (bin_ind != UNDEFINED_BIN_IND)
set_bin(tab->bins, get_size_ind(tab), bin_ind, ind + ENTRY_BASE);
+ st_assert(do_hash(key, tab) == hash_value);
#ifdef ST_DEBUG
st_check(tab);
#endif
@@ -1230,7 +1296,8 @@ st_insert2(st_table *tab, st_data_t key, st_data_t value,
/* Create and return a copy of table OLD_TAB. */
st_table *
-st_copy(st_table *old_tab) {
+st_copy(st_table *old_tab)
+{
st_table *new_tab;
new_tab = (st_table *) malloc(sizeof(st_table));
@@ -1254,7 +1321,8 @@ st_copy(st_table *old_tab) {
/* Update the entries start of table TAB after removing an entry
with index N in the array entries. */
static inline void
-update_range_for_deleted(st_table *tab, st_index_t n) {
+update_range_for_deleted(st_table *tab, st_index_t n)
+{
/* Do not update entries_bound here. Otherwise, we can fill all
bins by deleted entry value before rebuilding the table. */
if (tab->entries_start == n)
@@ -1275,14 +1343,20 @@ st_general_delete(st_table *tab, st_data_t *key, st_data_t *value)
st_assert(tab != NULL);
hash = do_hash(*key, tab);
+ retry:
if (tab->bins == NULL) {
bin = find_entry(tab, hash, *key);
+ if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0))
+ goto retry;
if (bin == UNDEFINED_ENTRY_IND) {
if (value != 0) *value = 0;
return 0;
}
- } else {
+ }
+ else {
bin_ind = find_table_bin_ind(tab, hash, *key);
+ if (EXPECT(bin_ind == REBUILT_TABLE_BIN_IND, 0))
+ goto retry;
if (bin_ind == UNDEFINED_BIN_IND) {
if (value != 0) *value = 0;
return 0;
@@ -1303,7 +1377,8 @@ st_general_delete(st_table *tab, st_data_t *key, st_data_t *value)
}
int
-st_delete(st_table *tab, st_data_t *key, st_data_t *value) {
+st_delete(st_table *tab, st_data_t *key, st_data_t *value)
+{
return st_general_delete(tab, key, value);
}
@@ -1314,7 +1389,8 @@ st_delete(st_table *tab, st_data_t *key, st_data_t *value) {
traversing without a specific way to do this. */
int
st_delete_safe(st_table *tab, st_data_t *key, st_data_t *value,
- st_data_t never ATTRIBUTE_UNUSED) {
+ st_data_t never ATTRIBUTE_UNUSED)
+{
return st_general_delete(tab, key, value);
}
@@ -1323,7 +1399,8 @@ st_delete_safe(st_table *tab, st_data_t *key, st_data_t *value,
Return its key through KEY and its record through VALUE (unless
VALUE is zero). */
int
-st_shift(st_table *tab, st_data_t *key, st_data_t *value) {
+st_shift(st_table *tab, st_data_t *key, st_data_t *value)
+{
st_index_t i, bound;
st_index_t bin;
st_table_entry *entries, *curr_entry_ptr;
@@ -1334,20 +1411,33 @@ st_shift(st_table *tab, st_data_t *key, st_data_t *value) {
for (i = tab->entries_start; i < bound; i++) {
curr_entry_ptr = &entries[i];
if (! DELETED_ENTRY_P(curr_entry_ptr)) {
+ st_hash_t entry_hash = curr_entry_ptr->hash;
+ st_data_t entry_key = curr_entry_ptr->key;
+
if (value != 0) *value = curr_entry_ptr->record;
- *key = curr_entry_ptr->key;
+ *key = entry_key;
+ retry:
if (tab->bins == NULL) {
- bin = find_entry(tab, curr_entry_ptr->hash, curr_entry_ptr->key);
- st_assert(bin != UNDEFINED_ENTRY_IND
- && &entries[bin] == curr_entry_ptr);
- } else {
- bin_ind = find_table_bin_ind(tab, curr_entry_ptr->hash,
- curr_entry_ptr->key);
- st_assert(bin_ind != UNDEFINED_BIN_IND
- && &entries[get_bin(tab->bins, get_size_ind(tab), bin_ind)
- - ENTRY_BASE] == curr_entry_ptr);
+ bin = find_entry(tab, entry_hash, entry_key);
+ if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0)) {
+ entries = tab->entries;
+ goto retry;
+ }
+ st_assert(bin != UNDEFINED_ENTRY_IND);
+ curr_entry_ptr = &entries[bin];
+ }
+ else {
+ bin_ind = find_table_bin_ind(tab, entry_hash, entry_key);
+ if (EXPECT(bin_ind == REBUILT_TABLE_BIN_IND, 0)) {
+ entries = tab->entries;
+ goto retry;
+ }
+ st_assert(bin_ind != UNDEFINED_BIN_IND);
+ curr_entry_ptr = &entries[get_bin(tab->bins, get_size_ind(tab), bin_ind)
+ - ENTRY_BASE];
MARK_BIN_DELETED(tab, bin_ind);
}
+ st_assert(entry_hash != curr_entry_ptr->hash && entry_key == curr_entry_ptr->key);
MARK_ENTRY_DELETED(curr_entry_ptr);
tab->num_entries--;
update_range_for_deleted(tab, i);
@@ -1366,7 +1456,8 @@ st_shift(st_table *tab, st_data_t *key, st_data_t *value) {
/* See comments for function st_delete_safe. */
void
st_cleanup_safe(st_table *tab ATTRIBUTE_UNUSED,
- st_data_t never ATTRIBUTE_UNUSED) {
+ st_data_t never ATTRIBUTE_UNUSED)
+{
}
/* Find entry with KEY in table TAB, call FUNC with the key and the
@@ -1379,7 +1470,8 @@ st_cleanup_safe(st_table *tab ATTRIBUTE_UNUSED,
in the table before the call. */
int
st_update(st_table *tab, st_data_t key,
- st_update_callback_func *func, st_data_t arg) {
+ st_update_callback_func *func, st_data_t arg)
+{
st_table_entry *entry = NULL; /* to avoid uninitialized value warning */
st_index_t bin = 0; /* Ditto */
st_table_entry *entries;
@@ -1389,14 +1481,20 @@ st_update(st_table *tab, st_data_t key,
int retval, existing;
st_hash_t hash = do_hash(key, tab);
+ retry:
entries = tab->entries;
if (tab->bins == NULL) {
bin = find_entry(tab, hash, key);
+ if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0))
+ goto retry;
existing = bin != UNDEFINED_ENTRY_IND;
entry = &entries[bin];
bin_ind = UNDEFINED_BIN_IND;
- } else {
+ }
+ else {
bin_ind = find_table_bin_ind(tab, hash, key);
+ if (EXPECT(bin_ind == REBUILT_TABLE_BIN_IND, 0))
+ goto retry;
existing = bin_ind != UNDEFINED_BIN_IND;
if (existing) {
bin = get_bin(tab->bins, get_size_ind(tab), bin_ind) - ENTRY_BASE;
@@ -1412,7 +1510,7 @@ st_update(st_table *tab, st_data_t key,
retval = (*func)(&key, &value, arg, existing);
st_assert(check == tab->rebuilds_num);
switch (retval) {
- case ST_CONTINUE:
+ case ST_CONTINUE:
if (! existing) {
st_add_direct_with_hash(tab, key, value, hash);
break;
@@ -1422,7 +1520,7 @@ st_update(st_table *tab, st_data_t key,
}
entry->record = value;
break;
- case ST_DELETE:
+ case ST_DELETE:
if (existing) {
if (bin_ind != UNDEFINED_BIN_IND)
MARK_BIN_DELETED(tab, bin_ind);
@@ -1451,7 +1549,8 @@ st_update(st_table *tab, st_data_t key,
during traversing. */
static inline int
st_general_foreach(st_table *tab, int (*func)(ANYARGS), st_data_t arg,
- int check_p) {
+ int check_p)
+{
st_index_t bin;
st_index_t bin_ind;
st_table_entry *entries, *curr_entry_ptr;
@@ -1474,13 +1573,19 @@ st_general_foreach(st_table *tab, int (*func)(ANYARGS), st_data_t arg,
hash = curr_entry_ptr->hash;
retval = (*func)(key, curr_entry_ptr->record, arg, 0);
if (rebuilds_num != tab->rebuilds_num) {
+ retry:
entries = tab->entries;
packed_p = tab->bins == NULL;
if (packed_p) {
i = find_entry(tab, hash, key);
+ if (EXPECT(i == REBUILT_TABLE_ENTRY_IND, 0))
+ goto retry;
error_p = i == UNDEFINED_ENTRY_IND;
- } else {
+ }
+ else {
i = find_table_entry_ind(tab, hash, key);
+ if (EXPECT(i == REBUILT_TABLE_ENTRY_IND, 0))
+ goto retry;
error_p = i == UNDEFINED_ENTRY_IND;
i -= ENTRY_BASE;
}
@@ -1495,36 +1600,45 @@ st_general_foreach(st_table *tab, int (*func)(ANYARGS), st_data_t arg,
curr_entry_ptr = &entries[i];
}
switch (retval) {
- case ST_CONTINUE:
- break;
- case ST_CHECK:
- if (check_p)
- break;
- case ST_STOP:
+ case ST_CONTINUE:
+ break;
+ case ST_CHECK:
+ if (check_p)
+ break;
+ case ST_STOP:
#ifdef ST_DEBUG
- st_check(tab);
-#endif
- return 0;
- case ST_DELETE:
- if (packed_p) {
- bin = find_entry(tab, hash, curr_entry_ptr->key);
- if (bin == UNDEFINED_ENTRY_IND)
- break;
- } else {
- bin_ind = find_table_bin_ind(tab, hash, curr_entry_ptr->key);
- if (bin_ind == UNDEFINED_BIN_IND)
- break;
- bin = get_bin(tab->bins, get_size_ind(tab), bin_ind) - ENTRY_BASE;
- MARK_BIN_DELETED(tab, bin_ind);
- }
- st_assert(&entries[bin] == curr_entry_ptr);
- MARK_ENTRY_DELETED(curr_entry_ptr);
- tab->num_entries--;
- update_range_for_deleted(tab, bin);
+ st_check(tab);
+#endif
+ return 0;
+ case ST_DELETE: {
+ st_data_t key = curr_entry_ptr->key;
+
+ again:
+ if (packed_p) {
+ bin = find_entry(tab, hash, key);
+ if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0))
+ goto again;
+ if (bin == UNDEFINED_ENTRY_IND)
+ break;
+ }
+ else {
+ bin_ind = find_table_bin_ind(tab, hash, key);
+ if (EXPECT(bin_ind == REBUILT_TABLE_BIN_IND, 0))
+ goto again;
+ if (bin_ind == UNDEFINED_BIN_IND)
+ break;
+ bin = get_bin(tab->bins, get_size_ind(tab), bin_ind) - ENTRY_BASE;
+ MARK_BIN_DELETED(tab, bin_ind);
+ }
+ curr_entry_ptr = &entries[bin];
+ MARK_ENTRY_DELETED(curr_entry_ptr);
+ tab->num_entries--;
+ update_range_for_deleted(tab, bin);
#ifdef ST_DEBUG
- st_check(tab);
+ st_check(tab);
#endif
- break;
+ break;
+ }
}
}
#ifdef ST_DEBUG
@@ -1534,21 +1648,24 @@ st_general_foreach(st_table *tab, int (*func)(ANYARGS), st_data_t arg,
}
int
-st_foreach(st_table *tab, int (*func)(ANYARGS), st_data_t arg) {
- return st_general_foreach(tab, func, arg, FALSE);
+st_foreach(st_table *tab, int (*func)(ANYARGS), st_data_t arg)
+{
+ return st_general_foreach(tab, func, arg, FALSE);
}
/* See comments for function st_delete_safe. */
int
st_foreach_check(st_table *tab, int (*func)(ANYARGS), st_data_t arg,
- st_data_t never ATTRIBUTE_UNUSED) {
- return st_general_foreach(tab, func, arg, TRUE);
+ st_data_t never ATTRIBUTE_UNUSED)
+{
+ return st_general_foreach(tab, func, arg, TRUE);
}
/* Set up array KEYS by at most SIZE keys of head table TAB entries.
Return the number of keys set up in array KEYS. */
static inline st_index_t
-st_general_keys(st_table *tab, st_data_t *keys, st_index_t size) {
+st_general_keys(st_table *tab, st_data_t *keys, st_index_t size)
+{
st_index_t i, bound;
st_data_t key, *keys_start, *keys_end;
st_table_entry *curr_entry_ptr, *entries = tab->entries;
@@ -1569,21 +1686,24 @@ st_general_keys(st_table *tab, st_data_t *keys, st_index_t size) {
}
st_index_t
-st_keys(st_table *tab, st_data_t *keys, st_index_t size) {
+st_keys(st_table *tab, st_data_t *keys, st_index_t size)
+{
return st_general_keys(tab, keys, size);
}
/* See comments for function st_delete_safe. */
st_index_t
st_keys_check(st_table *tab, st_data_t *keys, st_index_t size,
- st_data_t never ATTRIBUTE_UNUSED) {
+ st_data_t never ATTRIBUTE_UNUSED)
+{
return st_general_keys(tab, keys, size);
}
/* Set up array VALUES by at most SIZE values of head table TAB
entries. Return the number of values set up in array VALUES. */
static inline st_index_t
-st_general_values(st_table *tab, st_data_t *values, st_index_t size) {
+st_general_values(st_table *tab, st_data_t *values, st_index_t size)
+{
st_index_t i, bound;
st_data_t *values_start, *values_end;
st_table_entry *curr_entry_ptr, *entries = tab->entries;
@@ -1604,84 +1724,19 @@ st_general_values(st_table *tab, st_data_t *values, st_index_t size) {
}
st_index_t
-st_values(st_table *tab, st_data_t *values, st_index_t size) {
+st_values(st_table *tab, st_data_t *values, st_index_t size)
+{
return st_general_values(tab, values, size);
}
/* See comments for function st_delete_safe. */
st_index_t
st_values_check(st_table *tab, st_data_t *values, st_index_t size,
- st_data_t never ATTRIBUTE_UNUSED) {
+ st_data_t never ATTRIBUTE_UNUSED)
+{
return st_general_values(tab, values, size);
}
-/*
- * hash_32 - 32 bit Fowler/Noll/Vo FNV-1a hash code
- *
- * @(#) $Hash32: Revision: 1.1 $
- * @(#) $Hash32: Id: hash_32a.c,v 1.1 2003/10/03 20:38:53 chongo Exp $
- * @(#) $Hash32: Source: /usr/local/src/cmd/fnv/RCS/hash_32a.c,v $
- *
- ***
- *
- * Fowler/Noll/Vo hash
- *
- * The basis of this hash algorithm was taken from an idea sent
- * as reviewer comments to the IEEE POSIX P1003.2 committee by:
- *
- * Phong Vo (http://www.research.att.com/info/kpv/)
- * Glenn Fowler (http://www.research.att.com/~gsf/)
- *
- * In a subsequent ballot round:
- *
- * Landon Curt Noll (http://www.isthe.com/chongo/)
- *
- * improved on their algorithm. Some people tried this hash
- * and found that it worked rather well. In an EMail message
- * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.
- *
- * FNV hashes are designed to be fast while maintaining a low
- * collision rate. The FNV speed allows one to quickly hash lots
- * of data while maintaining a reasonable collision rate. See:
- *
- * http://www.isthe.com/chongo/tech/comp/fnv/index.html
- *
- * for more details as well as other forms of the FNV hash.
- ***
- *
- * To use the recommended 32 bit FNV-1a hash, pass FNV1_32A_INIT as the
- * Fnv32_t hashval argument to fnv_32a_buf() or fnv_32a_str().
- *
- ***
- *
- * Please do not copyright this code. This code is in the public domain.
- *
- * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
- * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
- * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
- * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- *
- * By:
- * chongo <Landon Curt Noll> /\oo/\
- * http://www.isthe.com/chongo/
- *
- * Share and Enjoy! :-)
- */
-/*
- * 32 bit FNV-1 and FNV-1a non-zero initial basis
- *
- * The FNV-1 initial basis is the FNV-0 hash of the following 32 octets:
- *
- * chongo <Landon Curt Noll> /\../\
- *
- * NOTE: The \'s above are not back-slashing escape characters.
- * They are literal ASCII backslash 0x5c characters.
- *
- * NOTE: The FNV-1a initial basis is the same value as FNV-1 by definition.
- */
#define FNV1_32A_INIT 0x811c9dc5
/*
@@ -1689,27 +1744,6 @@ st_values_check(st_table *tab, st_data_t *values, st_index_t size,
*/
#define FNV_32_PRIME 0x01000193
-#ifdef ST_USE_FNV1
-static st_index_t
-strhash(st_data_t arg)
-{
- register const char *string = (const char *)arg;
- register st_index_t hval = FNV1_32A_INIT;
-
- /*
- * FNV-1a hash each octet in the buffer
- */
- while (*string) {
- /* xor the bottom with the current octet */
- hval ^= (unsigned int)*string++;
-
- /* multiply by the 32 bit FNV magic prime mod 2^32 */
- hval *= FNV_32_PRIME;
- }
- return hval;
-}
-#else
-
#ifndef UNALIGNED_WORD_ACCESS
# if defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || \
@@ -1722,71 +1756,81 @@ strhash(st_data_t arg)
# define UNALIGNED_WORD_ACCESS 0
#endif
-/* MurmurHash described in http://murmurhash.googlepages.com/ */
-#ifndef MURMUR
-#define MURMUR 2
-#endif
+/* This hash function is quite simplified MurmurHash3
+ * Simplification is legal, cause most of magic still happens in finalizator.
+ * And finalizator is almost the same as in MurmurHash3 */
+#define BIG_CONSTANT(x,y) ((st_index_t)(x)<<32|(st_index_t)(y))
+#define ROTL(x,n) ((x)<<(n)|(x)>>(SIZEOF_ST_INDEX_T*CHAR_BIT-(n)))
-#define MurmurMagic_1 (st_index_t)0xc6a4a793
-#define MurmurMagic_2 (st_index_t)0x5bd1e995
-#if MURMUR == 1
-#define MurmurMagic MurmurMagic_1
-#elif MURMUR == 2
-#if SIZEOF_ST_INDEX_T > 4
-#define MurmurMagic ((MurmurMagic_1 << 32) | MurmurMagic_2)
+#if ST_INDEX_BITS <= 32
+#define C1 (st_index_t)0xcc9e2d51
+#define C2 (st_index_t)0x1b873593
#else
-#define MurmurMagic MurmurMagic_2
-#endif
+#define C1 BIG_CONSTANT(0x87c37b91,0x114253d5);
+#define C2 BIG_CONSTANT(0x4cf5ad43,0x2745937f);
#endif
+NO_SANITIZE("unsigned-integer-overflow", static inline st_index_t murmur_step(st_index_t h, st_index_t k));
+NO_SANITIZE("unsigned-integer-overflow", static inline st_index_t murmur_finish(st_index_t h));
+NO_SANITIZE("unsigned-integer-overflow", extern st_index_t st_hash(const void *ptr, size_t len, st_index_t h));
static inline st_index_t
-murmur(st_index_t h, st_index_t k, int r)
+murmur_step(st_index_t h, st_index_t k)
{
- const st_index_t m = MurmurMagic;
-#if MURMUR == 1
- h += k;
- h *= m;
- h ^= h >> r;
-#elif MURMUR == 2
- k *= m;
- k ^= k >> r;
- k *= m;
-
- h *= m;
- h ^= k;
+#if ST_INDEX_BITS <= 32
+#define r1 (17)
+#define r2 (11)
+#else
+#define r1 (33)
+#define r2 (24)
#endif
+ k *= C1;
+ h ^= ROTL(k, r1);
+ h *= C2;
+ h = ROTL(h, r2);
return h;
}
+#undef r1
+#undef r2
static inline st_index_t
murmur_finish(st_index_t h)
{
-#if MURMUR == 1
- h = murmur(h, 0, 10);
- h = murmur(h, 0, 17);
-#elif MURMUR == 2
- h ^= h >> 13;
- h *= MurmurMagic;
- h ^= h >> 15;
-#endif
+#if ST_INDEX_BITS <= 32
+#define r1 (16)
+#define r2 (13)
+#define r3 (16)
+ const st_index_t c1 = 0x85ebca6b;
+ const st_index_t c2 = 0xc2b2ae35;
+#else
+/* values are taken from Mix13 on http://zimbry.blogspot.ru/2011/09/better-bit-mixing-improving-on.html */
+#define r1 (30)
+#define r2 (27)
+#define r3 (31)
+ const st_index_t c1 = BIG_CONSTANT(0xbf58476d,0x1ce4e5b9);
+ const st_index_t c2 = BIG_CONSTANT(0x94d049bb,0x133111eb);
+#endif
+#if ST_INDEX_BITS > 64
+ h ^= h >> 64;
+ h *= c2;
+ h ^= h >> 65;
+#endif
+ h ^= h >> r1;
+ h *= c1;
+ h ^= h >> r2;
+ h *= c2;
+ h ^= h >> r3;
return h;
}
-
-#define murmur_step(h, k) murmur((h), (k), 16)
-
-#if MURMUR == 1
-#define murmur1(h) murmur_step((h), 16)
-#else
-#define murmur1(h) murmur_step((h), 24)
-#endif
+#undef r1
+#undef r2
+#undef r3
st_index_t
st_hash(const void *ptr, size_t len, st_index_t h)
{
const char *data = ptr;
st_index_t t = 0;
-
- h += 0xdeadbeef;
+ size_t l = len;
#define data_at(n) (st_index_t)((unsigned char)data[(n)])
#define UNALIGNED_ADD_4 UNALIGNED_ADD(2); UNALIGNED_ADD(1); UNALIGNED_ADD(0)
@@ -1801,6 +1845,7 @@ st_hash(const void *ptr, size_t len, st_index_t h)
#else
#define UNALIGNED_ADD_ALL UNALIGNED_ADD_4
#endif
+#undef SKIP_TAIL
if (len >= sizeof(st_index_t)) {
#if !UNALIGNED_WORD_ACCESS
int align = (int)((st_data_t)data % sizeof(st_index_t));
@@ -1864,18 +1909,22 @@ st_hash(const void *ptr, size_t len, st_index_t h)
t = (t >> sr) | (d << sl);
#endif
-#if MURMUR == 2
if (len < (size_t)align) goto skip_tail;
-#endif
+# define SKIP_TAIL 1
h = murmur_step(h, t);
data += pack;
len -= pack;
}
else
#endif
+#ifdef HAVE_BUILTIN___BUILTIN_ASSUME_ALIGNED
+#define aligned_data __builtin_assume_aligned(data, sizeof(st_index_t))
+#else
+#define aligned_data data
+#endif
{
do {
- h = murmur_step(h, *(st_index_t *)data);
+ h = murmur_step(h, *(st_index_t *)aligned_data);
data += sizeof(st_index_t);
len -= sizeof(st_index_t);
} while (len >= sizeof(st_index_t));
@@ -1884,6 +1933,21 @@ st_hash(const void *ptr, size_t len, st_index_t h)
t = 0;
switch (len) {
+#if UNALIGNED_WORD_ACCESS && SIZEOF_ST_INDEX_T <= 8 && CHAR_BIT == 8
+ /* in this case byteorder doesn't really matter */
+#if SIZEOF_ST_INDEX_T > 4
+ case 7: t |= data_at(6) << 48;
+ case 6: t |= data_at(5) << 40;
+ case 5: t |= data_at(4) << 32;
+ case 4:
+ t |= (st_index_t)*(uint32_t*)aligned_data;
+ goto skip_tail;
+# define SKIP_TAIL 1
+#endif
+ case 3: t |= data_at(2) << 16;
+ case 2: t |= data_at(1) << 8;
+ case 1: t |= data_at(0);
+#else
#ifdef WORDS_BIGENDIAN
# define UNALIGNED_ADD(n) case (n) + 1: \
t |= data_at(n) << CHAR_BIT*(SIZEOF_ST_INDEX_T - (n) - 1)
@@ -1893,16 +1957,15 @@ st_hash(const void *ptr, size_t len, st_index_t h)
#endif
UNALIGNED_ADD_ALL;
#undef UNALIGNED_ADD
-#if MURMUR == 1
- h = murmur_step(h, t);
-#elif MURMUR == 2
-# if !UNALIGNED_WORD_ACCESS
+#endif
+#ifdef SKIP_TAIL
skip_tail:
-# endif
- h ^= t;
- h *= MurmurMagic;
#endif
+ h ^= t; h -= ROTL(t, 7);
+ h *= C2;
}
+ h ^= l;
+#undef aligned_data
return murmur_finish(h);
}
@@ -1910,45 +1973,27 @@ st_hash(const void *ptr, size_t len, st_index_t h)
st_index_t
st_hash_uint32(st_index_t h, uint32_t i)
{
- return murmur_step(h + i, 16);
+ return murmur_step(h, i);
}
+NO_SANITIZE("unsigned-integer-overflow", extern st_index_t st_hash_uint(st_index_t h, st_index_t i));
st_index_t
st_hash_uint(st_index_t h, st_index_t i)
{
- st_index_t v = 0;
- h += i;
-#ifdef WORDS_BIGENDIAN
-#if SIZEOF_ST_INDEX_T*CHAR_BIT > 12*8
- v = murmur1(v + (h >> 12*8));
-#endif
-#if SIZEOF_ST_INDEX_T*CHAR_BIT > 8*8
- v = murmur1(v + (h >> 8*8));
-#endif
-#if SIZEOF_ST_INDEX_T*CHAR_BIT > 4*8
- v = murmur1(v + (h >> 4*8));
-#endif
-#endif
- v = murmur1(v + h);
-#ifndef WORDS_BIGENDIAN
-#if SIZEOF_ST_INDEX_T*CHAR_BIT > 4*8
- v = murmur1(v + (h >> 4*8));
-#endif
+ i += h;
+/* no matter if it is BigEndian or LittleEndian,
+ * we hash just integers */
#if SIZEOF_ST_INDEX_T*CHAR_BIT > 8*8
- v = murmur1(v + (h >> 8*8));
+ h = murmur_step(h, i >> 8*8);
#endif
-#if SIZEOF_ST_INDEX_T*CHAR_BIT > 12*8
- v = murmur1(v + (h >> 12*8));
-#endif
-#endif
- return v;
+ h = murmur_step(h, i);
+ return h;
}
st_index_t
st_hash_end(st_index_t h)
{
- h = murmur_step(h, 10);
- h = murmur_step(h, 17);
+ h = murmur_finish(h);
return h;
}
@@ -1965,23 +2010,22 @@ strhash(st_data_t arg)
register const char *string = (const char *)arg;
return st_hash(string, strlen(string), FNV1_32A_INIT);
}
-#endif
int
st_locale_insensitive_strcasecmp(const char *s1, const char *s2)
{
- unsigned int c1, c2;
+ char c1, c2;
while (1) {
- c1 = (unsigned char)*s1++;
- c2 = (unsigned char)*s2++;
+ c1 = *s1++;
+ c2 = *s2++;
if (c1 == '\0' || c2 == '\0') {
if (c1 != '\0') return 1;
if (c2 != '\0') return -1;
return 0;
}
- if ((unsigned int)(c1 - 'A') <= ('Z' - 'A')) c1 += 'a' - 'A';
- if ((unsigned int)(c2 - 'A') <= ('Z' - 'A')) c2 += 'a' - 'A';
+ if (('A' <= c1) && (c1 <= 'Z')) c1 += 'a' - 'A';
+ if (('A' <= c2) && (c2 <= 'Z')) c2 += 'a' - 'A';
if (c1 != c2) {
if (c1 > c2)
return 1;
@@ -1994,18 +2038,19 @@ st_locale_insensitive_strcasecmp(const char *s1, const char *s2)
int
st_locale_insensitive_strncasecmp(const char *s1, const char *s2, size_t n)
{
- unsigned int c1, c2;
+ char c1, c2;
+ size_t i;
- while (n--) {
- c1 = (unsigned char)*s1++;
- c2 = (unsigned char)*s2++;
+ for (i = 0; i < n; i++) {
+ c1 = *s1++;
+ c2 = *s2++;
if (c1 == '\0' || c2 == '\0') {
if (c1 != '\0') return 1;
if (c2 != '\0') return -1;
return 0;
}
- if ((unsigned int)(c1 - 'A') <= ('Z' - 'A')) c1 += 'a' - 'A';
- if ((unsigned int)(c2 - 'A') <= ('Z' - 'A')) c2 += 'a' - 'A';
+ if (('A' <= c1) && (c1 <= 'Z')) c1 += 'a' - 'A';
+ if (('A' <= c2) && (c2 <= 'Z')) c2 += 'a' - 'A';
if (c1 != c2) {
if (c1 > c2)
return 1;
@@ -2016,7 +2061,7 @@ st_locale_insensitive_strncasecmp(const char *s1, const char *s2, size_t n)
return 0;
}
-PUREFUNC(static st_index_t strcasehash(st_data_t));
+NO_SANITIZE("unsigned-integer-overflow", PUREFUNC(static st_index_t strcasehash(st_data_t)));
static st_index_t
strcasehash(st_data_t arg)
{
@@ -2049,3 +2094,220 @@ st_numhash(st_data_t n)
enum {s1 = 11, s2 = 3};
return (st_index_t)((n>>s1|(n<<s2)) ^ (n>>s2));
}
+
+/* Expand TAB to be suitable for holding SIZ entries in total.
+ Pre-existing entries remain not deleted inside of TAB, but its bins
+ are cleared to expect future reconstruction. See rehash below. */
+static void
+st_expand_table(st_table *tab, st_index_t siz)
+{
+ st_table *tmp;
+ st_index_t n;
+
+ if (siz <= get_allocated_entries(tab))
+ return; /* enough room already */
+
+ tmp = st_init_table_with_size(tab->type, siz);
+ n = get_allocated_entries(tab);
+ MEMCPY(tmp->entries, tab->entries, st_table_entry, n);
+ free(tab->entries);
+ if (tab->bins != NULL)
+ free(tab->bins);
+ if (tmp->bins != NULL)
+ free(tmp->bins);
+ tab->entry_power = tmp->entry_power;
+ tab->bin_power = tmp->bin_power;
+ tab->size_ind = tmp->size_ind;
+ tab->entries = tmp->entries;
+ tab->bins = NULL;
+ tab->rebuilds_num++;
+ free(tmp);
+}
+
+/* Rehash using linear search. Return TRUE if we found that the table
+ was rebuilt. */
+static int
+st_rehash_linear(st_table *tab)
+{
+ int eq_p, rebuilt_p;
+ st_index_t i, j;
+ st_table_entry *p, *q;
+ if (tab->bins) {
+ free(tab->bins);
+ tab->bins = NULL;
+ }
+ for (i = tab->entries_start; i < tab->entries_bound; i++) {
+ p = &tab->entries[i];
+ if (DELETED_ENTRY_P(p))
+ continue;
+ for (j = i + 1; j < tab->entries_bound; j++) {
+ q = &tab->entries[j];
+ if (DELETED_ENTRY_P(q))
+ continue;
+ DO_PTR_EQUAL_CHECK(tab, p, q->hash, q->key, eq_p, rebuilt_p);
+ if (EXPECT(rebuilt_p, 0))
+ return TRUE;
+ if (eq_p) {
+ st_assert(p < q);
+ *p = *q;
+ MARK_ENTRY_DELETED(q);
+ tab->num_entries--;
+ update_range_for_deleted(tab, j);
+ }
+ }
+ }
+ return FALSE;
+}
+
+/* Rehash using index. Return TRUE if we found that the table was
+ rebuilt. */
+static int
+st_rehash_indexed(st_table *tab)
+{
+ int eq_p, rebuilt_p;
+ st_index_t i;
+ st_index_t const n = bins_size(tab);
+ unsigned int const size_ind = get_size_ind(tab);
+ st_index_t *bins = realloc(tab->bins, n);
+ st_assert(bins != NULL);
+ tab->bins = bins;
+ initialize_bins(tab);
+ for (i = tab->entries_start; i < tab->entries_bound; i++) {
+ st_table_entry *p = &tab->entries[i];
+ st_index_t ind;
+#ifdef QUADRATIC_PROBE
+ st_index_t d = 1;
+#else
+ st_index_t peterb = p->hash;
+#endif
+
+ if (DELETED_ENTRY_P(p))
+ continue;
+
+ ind = hash_bin(p->hash, tab);
+ for(;;) {
+ st_index_t bin = get_bin(bins, size_ind, ind);
+ if (EMPTY_OR_DELETED_BIN_P(bin)) {
+ /* ok, new room */
+ set_bin(bins, size_ind, ind, i + ENTRY_BASE);
+ break;
+ }
+ else {
+ st_table_entry *q = &tab->entries[bin - ENTRY_BASE];
+ DO_PTR_EQUAL_CHECK(tab, q, p->hash, p->key, eq_p, rebuilt_p);
+ if (EXPECT(rebuilt_p, 0))
+ return TRUE;
+ if (eq_p) {
+ /* duplicated key; delete it */
+ st_assert(q < p);
+ q->record = p->record;
+ MARK_ENTRY_DELETED(p);
+ tab->num_entries--;
+ update_range_for_deleted(tab, bin);
+ break;
+ }
+ else {
+ /* hash collision; skip it */
+#ifdef QUADRATIC_PROBE
+ ind = hash_bin(ind + d, tab);
+ d++;
+#else
+ ind = secondary_hash(ind, tab, &peterb);
+#endif
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+/* Reconstruct TAB's bins according to TAB's entries. This function
+ permits conflicting keys inside of entries. No errors are reported
+ then. All but one of them are discarded silently. */
+static void
+st_rehash(st_table *tab)
+{
+ int rebuilt_p;
+
+ do {
+ if (tab->bin_power <= MAX_POWER2_FOR_TABLES_WITHOUT_BINS)
+ rebuilt_p = st_rehash_linear(tab);
+ else
+ rebuilt_p = st_rehash_indexed(tab);
+ } while (rebuilt_p);
+}
+
+#ifdef RUBY
+static st_data_t
+st_stringify(VALUE key)
+{
+ return (rb_obj_class(key) == rb_cString && !RB_OBJ_FROZEN(key)) ?
+ rb_hash_key_str(key) : key;
+}
+
+static void
+st_insert_single(st_table *tab, VALUE hash, VALUE key, VALUE val)
+{
+ st_data_t k = st_stringify(key);
+ st_table_entry e;
+ e.hash = do_hash(k, tab);
+ e.key = k;
+ e.record = val;
+
+ tab->entries[tab->entries_bound++] = e;
+ tab->num_entries++;
+ RB_OBJ_WRITTEN(hash, Qundef, k);
+ RB_OBJ_WRITTEN(hash, Qundef, val);
+}
+
+static void
+st_insert_linear(st_table *tab, long argc, const VALUE *argv, VALUE hash)
+{
+ long i;
+
+ for (i = 0; i < argc; /* */) {
+ st_data_t k = st_stringify(argv[i++]);
+ st_data_t v = argv[i++];
+ st_insert(tab, k, v);
+ RB_OBJ_WRITTEN(hash, Qundef, k);
+ RB_OBJ_WRITTEN(hash, Qundef, v);
+ }
+}
+
+static void
+st_insert_generic(st_table *tab, long argc, const VALUE *argv, VALUE hash)
+{
+ long i;
+
+ /* push elems */
+ for (i = 0; i < argc; /* */) {
+ VALUE key = argv[i++];
+ VALUE val = argv[i++];
+ st_insert_single(tab, hash, key, val);
+ }
+
+ /* reindex */
+ st_rehash(tab);
+}
+
+/* Mimics ruby's { foo => bar } syntax. This function is subpart
+ of rb_hash_bulk_insert. */
+void
+rb_hash_bulk_insert_into_st_table(long argc, const VALUE *argv, VALUE hash)
+{
+ st_index_t n, size = argc / 2;
+ st_table *tab = RHASH_ST_TABLE(hash);
+
+ tab = RHASH_TBL_RAW(hash);
+ n = tab->entries_bound + size;
+ st_expand_table(tab, n);
+ if (UNLIKELY(tab->num_entries))
+ st_insert_generic(tab, argc, argv, hash);
+ else if (argc <= 2)
+ st_insert_single(tab, hash, argv[0], argv[1]);
+ else if (tab->bin_power <= MAX_POWER2_FOR_TABLES_WITHOUT_BINS)
+ st_insert_linear(tab, argc, argv, hash);
+ else
+ st_insert_generic(tab, argc, argv, hash);
+}
+#endif
diff --git a/strftime.c b/strftime.c
index 42e733818c..446be7d338 100644
--- a/strftime.c
+++ b/strftime.c
@@ -226,8 +226,8 @@ format_value(VALUE val, int base)
*/
static VALUE
rb_strftime_with_timespec(VALUE ftime, const char *format, size_t format_len,
- rb_encoding *enc, const struct vtm *vtm, VALUE timev,
- struct timespec *ts, int gmt, size_t maxsize)
+ rb_encoding *enc, VALUE time, const struct vtm *vtm,
+ VALUE timev, struct timespec *ts, int gmt, size_t maxsize)
{
size_t len = RSTRING_LEN(ftime);
char *s = RSTRING_PTR(ftime);
@@ -246,6 +246,7 @@ rb_strftime_with_timespec(VALUE ftime, const char *format, size_t format_len,
#ifdef MAILHEADER_EXT
int sign;
#endif
+ VALUE zone = Qnil;
/* various tables, useful in North America */
static const char days_l[][10] = {
@@ -317,7 +318,7 @@ rb_strftime_with_timespec(VALUE ftime, const char *format, size_t format_len,
len = s - start; \
rb_str_set_len(ftime, len); \
if (!rb_strftime_with_timespec(ftime, (fmt), rb_strlen_lit(fmt), \
- enc, vtm, timev, ts, gmt, maxsize)) \
+ enc, time, vtm, timev, ts, gmt, maxsize)) \
return 0; \
s = RSTRING_PTR(ftime); \
i = RSTRING_LEN(ftime) - len; \
@@ -325,7 +326,9 @@ rb_strftime_with_timespec(VALUE ftime, const char *format, size_t format_len,
s += len; \
if (i > 0) case_conv(s, i, flags); \
if (precision > i) {\
+ s += i; \
NEEDS(precision); \
+ s -= i; \
memmove(s + precision - i, s, i);\
memset(s, padding ? padding : ' ', precision - i); \
s += precision; \
@@ -543,7 +546,8 @@ rb_strftime_with_timespec(VALUE ftime, const char *format, size_t format_len,
if (off < 0) {
off = -off;
sign = -1;
- } else {
+ }
+ else {
sign = +1;
}
switch (colons) {
@@ -582,7 +586,7 @@ rb_strftime_with_timespec(VALUE ftime, const char *format, size_t format_len,
goto unknown;
}
i = snprintf(s, endp - s, (padding == ' ' ? "%+*ld" : "%+.*ld"),
- precision + 1, sign * (off / 3600));
+ precision + (padding == ' '), sign * (off / 3600));
if (i < 0) goto err;
if (sign < 0 && off < 3600) {
*(padding == ' ' ? s + i - 2 : s) = '-';
@@ -618,11 +622,14 @@ rb_strftime_with_timespec(VALUE ftime, const char *format, size_t format_len,
tp = "UTC";
break;
}
- if (vtm->zone == NULL) {
+ if (NIL_P(vtm->zone)) {
i = 0;
}
else {
- tp = vtm->zone;
+ if (NIL_P(zone)) {
+ zone = rb_time_zone_abbreviation(vtm->zone, time);
+ }
+ tp = RSTRING_PTR(zone);
if (enc) {
for (i = 0; i < TBUFSIZE && tp[i]; i++) {
if ((unsigned char)tp[i] > 0x7F) {
@@ -820,7 +827,8 @@ rb_strftime_with_timespec(VALUE ftime, const char *format, size_t format_len,
VALUE args[2], result;
args[0] = INT2FIX(precision);
args[1] = subsec;
- result = rb_str_format(2, args, rb_str_new2("%0*d"));
+ result = rb_str_format(2, args,
+ rb_fstring_lit("%0*d"));
(void)strlcpy(s, StringValueCStr(result), endp-s);
s += precision;
}
@@ -910,34 +918,34 @@ strftime_size_limit(size_t format_len)
}
VALUE
-rb_strftime(const char *format, size_t format_len,
- rb_encoding *enc, const struct vtm *vtm, VALUE timev, int gmt)
+rb_strftime(const char *format, size_t format_len, rb_encoding *enc,
+ VALUE time, const struct vtm *vtm, VALUE timev, int gmt)
{
VALUE result = rb_enc_str_new(0, 0, enc);
return rb_strftime_with_timespec(result, format, format_len, enc,
- vtm, timev, NULL, gmt,
+ time, vtm, timev, NULL, gmt,
strftime_size_limit(format_len));
}
VALUE
-rb_strftime_timespec(const char *format, size_t format_len,
- rb_encoding *enc, const struct vtm *vtm, struct timespec *ts, int gmt)
+rb_strftime_timespec(const char *format, size_t format_len, rb_encoding *enc,
+ VALUE time, const struct vtm *vtm, struct timespec *ts, int gmt)
{
VALUE result = rb_enc_str_new(0, 0, enc);
return rb_strftime_with_timespec(result, format, format_len, enc,
- vtm, Qnil, ts, gmt,
+ time, vtm, Qnil, ts, gmt,
strftime_size_limit(format_len));
}
#if 0
VALUE
-rb_strftime_limit(const char *format, size_t format_len,
- rb_encoding *enc, const struct vtm *vtm, struct timespec *ts,
+rb_strftime_limit(const char *format, size_t format_len, rb_encoding *enc,
+ VALUE time, const struct vtm *vtm, struct timespec *ts,
int gmt, size_t maxsize)
{
VALUE result = rb_enc_str_new(0, 0, enc);
return rb_strftime_with_timespec(result, format, format_len, enc,
- vtm, Qnil, ts, gmt, maxsize);
+ time, vtm, Qnil, ts, gmt, maxsize);
}
#endif
diff --git a/string.c b/string.c
index 2ca835c74a..76a6ae0297 100644
--- a/string.c
+++ b/string.c
@@ -11,17 +11,21 @@
**********************************************************************/
-#include "internal.h"
+#include "ruby/encoding.h"
#include "ruby/re.h"
+#include "internal.h"
#include "encindex.h"
#include "probes.h"
#include "gc.h"
#include "ruby_assert.h"
#include "id.h"
+#include "debug_counter.h"
+#include "ruby/util.h"
#define BEG(no) (regs->beg[(no)])
#define END(no) (regs->end[(no)])
+#include <errno.h>
#include <math.h>
#include <ctype.h>
@@ -30,7 +34,9 @@
#endif
#if defined HAVE_CRYPT_R
+# if defined HAVE_CRYPT_H
# include <crypt.h>
+# endif
#elif !defined HAVE_CRYPT
# include "missing/crypt.h"
# define HAVE_CRYPT_R 1
@@ -68,6 +74,9 @@ VALUE rb_cSymbol;
* 1: RSTRING_NOEMBED
* 2: STR_SHARED (== ELTS_SHARED)
* 2-6: RSTRING_EMBED_LEN (5 bits == 32)
+ * 5: STR_SHARED_ROOT (RSTRING_NOEMBED==1 && STR_SHARED == 0, there may be
+ * other strings that rely on this string's buffer)
+ * 6: STR_IS_SHARED_M (shared, when RSTRING_NOEMBED==1 && klass==0)
* 7: STR_TMPLOCK
* 8-9: ENC_CODERANGE (2 bits)
* 10-16: ENCODING (7 bits == 128)
@@ -77,6 +86,8 @@ VALUE rb_cSymbol;
*/
#define RUBY_MAX_CHAR_LEN 16
+#define STR_SHARED_ROOT FL_USER5
+#define STR_IS_SHARED_M FL_USER6
#define STR_TMPLOCK FL_USER7
#define STR_NOFREE FL_USER18
#define STR_FAKESTR FL_USER19
@@ -128,7 +139,7 @@ VALUE rb_cSymbol;
#define RESIZE_CAPA_TERM(str,capacity,termlen) do {\
if (STR_EMBED_P(str)) {\
if (!STR_EMBEDDABLE_P(capacity, termlen)) {\
- char *const tmp = ALLOC_N(char, (capacity)+termlen);\
+ char *const tmp = ALLOC_N(char, (size_t)(capacity) + (termlen));\
const long tlen = RSTRING_LEN(str);\
memcpy(tmp, RSTRING_PTR(str), tlen);\
RSTRING(str)->as.heap.ptr = tmp;\
@@ -139,7 +150,8 @@ VALUE rb_cSymbol;
}\
else {\
assert(!FL_TEST((str), STR_SHARED)); \
- REALLOC_N(RSTRING(str)->as.heap.ptr, char, (size_t)(capacity) + (termlen));\
+ SIZED_REALLOC_N(RSTRING(str)->as.heap.ptr, char, \
+ (size_t)(capacity) + (termlen), STR_HEAP_SIZE(str)); \
RSTRING(str)->as.heap.aux.capa = (capacity);\
}\
} while (0)
@@ -148,6 +160,9 @@ VALUE rb_cSymbol;
if (!FL_TEST(str, STR_FAKESTR)) { \
RB_OBJ_WRITE((str), &RSTRING(str)->as.heap.aux.shared, (shared_str)); \
FL_SET((str), STR_SHARED); \
+ FL_SET((shared_str), STR_SHARED_ROOT); \
+ if (RBASIC_CLASS((shared_str)) == 0) /* for CoW-friendliness */ \
+ FL_SET_RAW((shared_str), STR_IS_SHARED_M); \
} \
} while (0)
@@ -174,6 +189,7 @@ static VALUE str_new_frozen(VALUE klass, VALUE orig);
static VALUE str_new_static(VALUE klass, const char *ptr, long len, int encindex);
static void str_make_independent_expand(VALUE str, long len, long expand, const int termlen);
static inline void str_modifiable(VALUE str);
+static VALUE rb_str_downcase(int argc, VALUE *argv, VALUE str);
static inline void
str_make_independent(VALUE str)
@@ -306,9 +322,15 @@ rb_fstring(VALUE str)
return str;
bare = BARE_STRING_P(str);
- if (STR_EMBED_P(str) && !bare) {
- OBJ_FREEZE_RAW(str);
- return str;
+ if (!bare) {
+ if (STR_EMBED_P(str)) {
+ OBJ_FREEZE_RAW(str);
+ return str;
+ }
+ if (FL_TEST_RAW(str, STR_NOEMBED|STR_SHARED_ROOT|STR_SHARED) == (STR_NOEMBED|STR_SHARED_ROOT)) {
+ assert(OBJ_FROZEN(str));
+ return str;
+ }
}
fstr = register_fstring(str);
@@ -356,13 +378,21 @@ setup_fake_str(struct RString *fake_str, const char *name, long len, int encidx)
return (VALUE)fake_str;
}
+/*
+ * set up a fake string which refers a static string literal.
+ */
VALUE
rb_setup_fake_str(struct RString *fake_str, const char *name, long len, rb_encoding *enc)
{
return setup_fake_str(fake_str, name, len, rb_enc_to_index(enc));
}
-VALUE
+/*
+ * rb_fstring_new and rb_fstring_cstr family create or lookup a frozen
+ * shared string which refers a static string literal. `ptr` must
+ * point a constant string.
+ */
+MJIT_FUNC_EXPORTED VALUE
rb_fstring_new(const char *ptr, long len)
{
struct RString fake_str;
@@ -431,10 +461,23 @@ static inline const char *
search_nonascii(const char *p, const char *e)
{
const uintptr_t *s, *t;
-#if SIZEOF_VOIDP == 8
-# define NONASCII_MASK 0x8080808080808080ULL
-#elif SIZEOF_VOIDP == 4
-# define NONASCII_MASK 0x80808080UL
+
+#if defined(__STDC_VERSION) && (__STDC_VERSION__ >= 199901L)
+# if SIZEOF_UINTPTR_T == 8
+# define NONASCII_MASK UINT64_C(0x8080808080808080)
+# elif SIZEOF_UINTPTR_T == 4
+# define NONASCII_MASK UINT32_C(0x80808080)
+# else
+# error "don't know what to do."
+# endif
+#else
+# if SIZEOF_UINTPTR_T == 8
+# define NONASCII_MASK ((uintptr_t)0x80808080UL << 32 | (uintptr_t)0x80808080UL)
+# elif SIZEOF_UINTPTR_T == 4
+# define NONASCII_MASK 0x80808080UL /* or...? */
+# else
+# error "don't know what to do."
+# endif
#endif
if (UNALIGNED_WORD_ACCESS || e - p >= SIZEOF_VOIDP) {
@@ -457,8 +500,15 @@ search_nonascii(const char *p, const char *e)
}
}
#endif
- s = (const uintptr_t *)p;
- t = (const uintptr_t *)(e - (SIZEOF_VOIDP-1));
+#if defined(HAVE_BUILTIN___BUILTIN_ASSUME_ALIGNED) &&! UNALIGNED_WORD_ACCESS
+#define aligned_ptr(value) \
+ __builtin_assume_aligned((value), sizeof(uintptr_t))
+#else
+#define aligned_ptr(value) (uintptr_t *)(value)
+#endif
+ s = aligned_ptr(p);
+ t = aligned_ptr(e - (SIZEOF_VOIDP-1));
+#undef aligned_ptr
for (;s < t; s++) {
if (*s & NONASCII_MASK) {
#ifdef WORDS_BIGENDIAN
@@ -617,12 +667,13 @@ rb_enc_str_coderange(VALUE str)
if (cr == ENC_CODERANGE_UNKNOWN) {
int encidx = ENCODING_GET(str);
rb_encoding *enc = rb_enc_from_index(encidx);
- if (rb_enc_mbminlen(enc) > 1 && rb_enc_dummy_p(enc)) {
+ if (rb_enc_mbminlen(enc) > 1 && rb_enc_dummy_p(enc) &&
+ rb_enc_mbminlen(enc = get_actual_encoding(encidx, str)) == 1) {
cr = ENC_CODERANGE_BROKEN;
}
else {
cr = coderange_scan(RSTRING_PTR(str), RSTRING_LEN(str),
- get_actual_encoding(encidx, str));
+ enc);
}
ENC_CODERANGE_SET(str, cr);
}
@@ -763,6 +814,11 @@ VALUE
rb_str_new_cstr(const char *ptr)
{
must_not_null(ptr);
+ /* rb_str_new_cstr() can take pointer from non-malloc-generated
+ * memory regions, and that cannot be detected by the MSAN. Just
+ * trust the programmer that the argument passed here is a sane C
+ * string. */
+ __msan_unpoison_string(ptr);
return rb_str_new(ptr, strlen(ptr));
}
@@ -910,7 +966,7 @@ rb_str_cat_conv_enc_opts(VALUE newstr, long ofs, const char *ptr, long len,
long olen;
olen = RSTRING_LEN(newstr);
- if (ofs < -olen || olen <= ofs)
+ if (ofs < -olen || olen < ofs)
rb_raise(rb_eIndexError, "index %ld out of string", ofs);
if (ofs < 0) ofs += olen;
if (!from) {
@@ -924,6 +980,15 @@ rb_str_cat_conv_enc_opts(VALUE newstr, long ofs, const char *ptr, long len,
ecflags, ecopts);
}
+VALUE
+rb_str_initialize(VALUE str, const char *ptr, long len, rb_encoding *enc)
+{
+ STR_SET_LEN(str, 0);
+ rb_enc_associate(str, enc);
+ rb_str_cat(str, ptr, len);
+ return str;
+}
+
static VALUE
str_cat_conv_enc_opts(VALUE newstr, long ofs, const char *ptr, long len,
rb_encoding *from, rb_encoding *to,
@@ -990,10 +1055,39 @@ rb_str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to)
VALUE
rb_external_str_new_with_enc(const char *ptr, long len, rb_encoding *eenc)
{
+ rb_encoding *ienc;
VALUE str;
+ const int eidx = rb_enc_to_index(eenc);
+
+ if (!ptr) {
+ return rb_tainted_str_new_with_enc(ptr, len, eenc);
+ }
- str = rb_tainted_str_new_with_enc(ptr, len, eenc);
- return rb_external_str_with_enc(str, eenc);
+ /* ASCII-8BIT case, no conversion */
+ if ((eidx == rb_ascii8bit_encindex()) ||
+ (eidx == rb_usascii_encindex() && search_nonascii(ptr, ptr + len))) {
+ return rb_tainted_str_new(ptr, len);
+ }
+ /* no default_internal or same encoding, no conversion */
+ ienc = rb_default_internal_encoding();
+ if (!ienc || eenc == ienc) {
+ return rb_tainted_str_new_with_enc(ptr, len, eenc);
+ }
+ /* ASCII compatible, and ASCII only string, no conversion in
+ * default_internal */
+ if ((eidx == rb_ascii8bit_encindex()) ||
+ (eidx == rb_usascii_encindex()) ||
+ (rb_enc_asciicompat(eenc) && !search_nonascii(ptr, ptr + len))) {
+ return rb_tainted_str_new_with_enc(ptr, len, ienc);
+ }
+ /* convert from the given encoding to default_internal */
+ str = rb_tainted_str_new_with_enc(NULL, 0, ienc);
+ /* when the conversion failed for some reason, just ignore the
+ * default_internal and result in the given encoding as-is. */
+ if (NIL_P(rb_str_cat_conv_enc_opts(str, 0, ptr, len, eenc, 0, Qnil))) {
+ rb_str_initialize(str, ptr, len, eenc);
+ }
+ return str;
}
VALUE
@@ -1079,12 +1173,28 @@ str_replace_shared_without_enc(VALUE str2, VALUE str)
TERM_FILL(ptr2+len, termlen);
}
else {
- str = rb_str_new_frozen(str);
+ VALUE root;
+ if (STR_SHARED_P(str)) {
+ root = RSTRING(str)->as.heap.aux.shared;
+ RSTRING_GETMEM(str, ptr, len);
+ }
+ else {
+ root = rb_str_new_frozen(str);
+ RSTRING_GETMEM(root, ptr, len);
+ }
+ if (!STR_EMBED_P(str2) && !FL_TEST_RAW(str2, STR_SHARED|STR_NOFREE)) {
+ if (FL_TEST_RAW(str2, STR_SHARED_ROOT)) {
+ rb_fatal("about to free a possible shared root");
+ }
+ char *ptr2 = STR_HEAP_PTR(str2);
+ if (ptr2 != ptr) {
+ ruby_sized_xfree(ptr2, STR_HEAP_SIZE(str2));
+ }
+ }
FL_SET(str2, STR_NOEMBED);
- RSTRING_GETMEM(str, ptr, len);
RSTRING(str2)->as.heap.len = len;
RSTRING(str2)->as.heap.ptr = ptr;
- STR_SET_SHARED(str2, str);
+ STR_SET_SHARED(str2, root);
}
return str2;
}
@@ -1124,6 +1234,45 @@ rb_str_new_frozen(VALUE orig)
return str;
}
+VALUE
+rb_str_tmp_frozen_acquire(VALUE orig)
+{
+ VALUE tmp;
+
+ if (OBJ_FROZEN_RAW(orig)) return orig;
+
+ tmp = str_new_frozen(0, orig);
+ OBJ_INFECT(tmp, orig);
+
+ return tmp;
+}
+
+void
+rb_str_tmp_frozen_release(VALUE orig, VALUE tmp)
+{
+ if (RBASIC_CLASS(tmp) != 0)
+ return;
+
+ if (STR_EMBED_P(tmp)) {
+ assert(OBJ_FROZEN_RAW(tmp));
+ rb_gc_force_recycle(tmp);
+ }
+ else if (FL_TEST_RAW(orig, STR_SHARED) &&
+ !FL_TEST_RAW(orig, STR_TMPLOCK|RUBY_FL_FREEZE)) {
+ VALUE shared = RSTRING(orig)->as.heap.aux.shared;
+
+ if (shared == tmp && !FL_TEST_RAW(tmp, STR_IS_SHARED_M)) {
+ FL_UNSET_RAW(orig, STR_SHARED);
+ assert(RSTRING(orig)->as.heap.ptr == RSTRING(tmp)->as.heap.ptr);
+ assert(RSTRING(orig)->as.heap.len == RSTRING(tmp)->as.heap.len);
+ RSTRING(orig)->as.heap.aux.capa = RSTRING(tmp)->as.heap.aux.capa;
+ RBASIC(orig)->flags |= RBASIC(tmp)->flags & STR_NOFREE;
+ assert(OBJ_FROZEN_RAW(tmp));
+ rb_gc_force_recycle(tmp);
+ }
+ }
+}
+
static VALUE
str_new_frozen(VALUE klass, VALUE orig)
{
@@ -1149,6 +1298,8 @@ str_new_frozen(VALUE klass, VALUE orig)
RSTRING(str)->as.heap.len -= ofs + rest;
}
else {
+ if (RBASIC_CLASS(shared) == 0)
+ FL_SET_RAW(shared, STR_IS_SHARED_M);
return shared;
}
}
@@ -1168,6 +1319,8 @@ str_new_frozen(VALUE klass, VALUE orig)
RBASIC(str)->flags |= RBASIC(orig)->flags & STR_NOFREE;
RBASIC(orig)->flags &= ~STR_NOFREE;
STR_SET_SHARED(orig, str);
+ if (klass == 0)
+ FL_UNSET_RAW(str, STR_IS_SHARED_M);
}
}
@@ -1192,6 +1345,7 @@ str_new_empty(VALUE str)
}
#define STR_BUF_MIN_SIZE 127
+STATIC_ASSERT(STR_BUF_MIN_SIZE, STR_BUF_MIN_SIZE > RSTRING_EMBED_LEN_MAX);
VALUE
rb_str_buf_new(long capa)
@@ -1233,9 +1387,18 @@ rb_str_free(VALUE str)
if (FL_TEST(str, RSTRING_FSTR)) {
st_data_t fstr = (st_data_t)str;
st_delete(rb_vm_fstring_table(), &fstr, NULL);
+ RB_DEBUG_COUNTER_INC(obj_str_fstr);
}
- if (!STR_EMBED_P(str) && !FL_TEST(str, STR_SHARED|STR_NOFREE)) {
+ if (STR_EMBED_P(str)) {
+ RB_DEBUG_COUNTER_INC(obj_str_embed);
+ }
+ else if (FL_TEST(str, STR_SHARED | STR_NOFREE)) {
+ (void)RB_DEBUG_COUNTER_INC_IF(obj_str_shared, FL_TEST(str, STR_SHARED));
+ (void)RB_DEBUG_COUNTER_INC_IF(obj_str_shared, FL_TEST(str, STR_NOFREE));
+ }
+ else {
+ RB_DEBUG_COUNTER_INC(obj_str_ptr);
ruby_sized_xfree(STR_HEAP_PTR(str), STR_HEAP_SIZE(str));
}
}
@@ -1254,7 +1417,7 @@ rb_str_memsize(VALUE str)
VALUE
rb_str_to_str(VALUE str)
{
- return rb_convert_type(str, T_STRING, "String", "to_str");
+ return rb_convert_type_with_id(str, T_STRING, "String", idTo_str);
}
static inline void str_discard(VALUE str);
@@ -1273,7 +1436,7 @@ str_shared_replace(VALUE str, VALUE str2)
int cr;
int termlen;
- ASSUME(str2 != str);
+ RUBY_ASSERT(str2 != str);
enc = STR_ENC_GET(str2);
cr = ENC_CODERANGE(str2);
str_discard(str);
@@ -1319,6 +1482,12 @@ rb_obj_as_string(VALUE obj)
return obj;
}
str = rb_funcall(obj, idTo_s, 0);
+ return rb_obj_as_string_result(str, obj);
+}
+
+MJIT_FUNC_EXPORTED VALUE
+rb_obj_as_string_result(VALUE str, VALUE obj)
+{
if (!RB_TYPE_P(str, T_STRING))
return rb_any_to_s(obj);
if (!FL_TEST_RAW(str, RSTRING_FSTR) && FL_ABLE(obj))
@@ -1364,10 +1533,13 @@ str_duplicate(VALUE klass, VALUE str)
MEMCPY(RSTRING(dup)->as.ary, RSTRING(str)->as.ary,
char, embed_size);
if (flags & STR_NOEMBED) {
- if (UNLIKELY(!(flags & FL_FREEZE))) {
- str = str_new_frozen(klass, str);
- FL_SET_RAW(str, flags & FL_TAINT);
- flags = FL_TEST_RAW(str, flag_mask);
+ if (FL_TEST_RAW(str, STR_SHARED)) {
+ str = RSTRING(str)->as.heap.aux.shared;
+ }
+ else if (UNLIKELY(!(flags & FL_FREEZE))) {
+ str = str_new_frozen(klass, str);
+ FL_SET_RAW(str, flags & FL_TAINT);
+ flags = FL_TEST_RAW(str, flag_mask);
}
if (flags & STR_NOEMBED) {
RB_OBJ_WRITE(dup, &RSTRING(dup)->as.heap.aux.shared, str);
@@ -1397,19 +1569,21 @@ rb_str_resurrect(VALUE str)
/*
* call-seq:
- * String.new(str="") -> new_str
- * String.new(str="", encoding: enc) -> new_str
- * String.new(str="", capacity: size) -> new_str
+ * String.new(str="") -> new_str
+ * String.new(str="", encoding: enc) -> new_str
+ * String.new(str="", capacity: size) -> new_str
*
* Returns a new string object containing a copy of <i>str</i>.
*
- * The optional <i>enc</i> argument specifies the encoding of the new string.
- * If not specified, the encoding of <i>str</i> (or ASCII-8BIT, if <i>str</i>
- * is not specified) is used.
+ * The optional <i>encoding</i> keyword argument specifies the encoding
+ * of the new string.
+ * If not specified, the encoding of <i>str</i> is used
+ * (or ASCII-8BIT, if <i>str</i> is not specified).
*
- * The optional <i>size</i> argument specifies the size of internal buffer.
+ * The optional <i>capacity</i> keyword argument specifies the size
+ * of the internal buffer.
* This may improve performance, when the string will be concatenated many
- * times (and call many realloc).
+ * times (causing many realloc calls).
*/
static VALUE
@@ -1452,10 +1626,22 @@ rb_str_init(int argc, VALUE *argv, VALUE str)
}
str_modifiable(str);
if (STR_EMBED_P(str)) { /* make noembed always */
- RSTRING(str)->as.heap.ptr = ALLOC_N(char, (size_t)capa + termlen);
+ char *new_ptr = ALLOC_N(char, (size_t)capa + termlen);
+ memcpy(new_ptr, RSTRING(str)->as.ary, RSTRING_EMBED_LEN_MAX + 1);
+ RSTRING(str)->as.heap.ptr = new_ptr;
+ }
+ else if (FL_TEST(str, STR_SHARED|STR_NOFREE)) {
+ const size_t size = (size_t)capa + termlen;
+ const char *const old_ptr = RSTRING_PTR(str);
+ const size_t osize = RSTRING(str)->as.heap.len + TERM_LEN(str);
+ char *new_ptr = ALLOC_N(char, (size_t)capa + termlen);
+ memcpy(new_ptr, old_ptr, osize < size ? osize : size);
+ FL_UNSET_RAW(str, STR_SHARED);
+ RSTRING(str)->as.heap.ptr = new_ptr;
}
else if (STR_HEAP_SIZE(str) != (size_t)capa + termlen) {
- REALLOC_N(RSTRING(str)->as.heap.ptr, char, (size_t)capa + termlen);
+ SIZED_REALLOC_N(RSTRING(str)->as.heap.ptr, char,
+ (size_t)capa + termlen, STR_HEAP_SIZE(str));
}
RSTRING(str)->as.heap.len = len;
TERM_FILL(&RSTRING(str)->as.heap.ptr[len], termlen);
@@ -1809,6 +1995,18 @@ rb_str_times(VALUE str, VALUE times)
if (len < 0) {
rb_raise(rb_eArgError, "negative argument");
}
+ if (RSTRING_LEN(str) == 1 && RSTRING_PTR(str)[0] == 0) {
+ str2 = str_alloc(rb_obj_class(str));
+ if (!STR_EMBEDDABLE_P(len, 1)) {
+ RSTRING(str2)->as.heap.aux.capa = len;
+ RSTRING(str2)->as.heap.ptr = ZALLOC_N(char, (size_t)len + 1);
+ STR_SET_NOEMBED(str2);
+ }
+ STR_SET_LEN(str2, len);
+ rb_enc_copy(str2, str);
+ OBJ_INFECT(str2, str);
+ return str2;
+ }
if (len && LONG_MAX/len < RSTRING_LEN(str)) {
rb_raise(rb_eArgError, "argument too big");
}
@@ -1845,7 +2043,7 @@ rb_str_times(VALUE str, VALUE times)
* details of the format string.
*
* "%05d" % 123 #=> "00123"
- * "%-5s: %08x" % [ "ID", self.object_id ] #=> "ID : 200e14d6"
+ * "%-5s: %016x" % [ "ID", self.object_id ] #=> "ID : 00002b054ec93168"
* "foo = %{foo}" % { :foo => 'bar' } #=> "foo = bar"
*/
@@ -1855,9 +2053,7 @@ rb_str_format_m(VALUE str, VALUE arg)
VALUE tmp = rb_check_array_type(arg);
if (!NIL_P(tmp)) {
- VALUE rv = rb_str_format(RARRAY_LENINT(tmp), RARRAY_CONST_PTR(tmp), str);
- RB_GC_GUARD(tmp);
- return rv;
+ return rb_str_format(RARRAY_LENINT(tmp), RARRAY_CONST_PTR(tmp), str);
}
return rb_str_format(1, &arg, str);
}
@@ -1899,7 +2095,7 @@ static void
str_make_independent_expand(VALUE str, long len, long expand, const int termlen)
{
char *ptr;
- const char *oldptr;
+ char *oldptr;
long capa = len + expand;
if (len > capa) len = capa;
@@ -1918,6 +2114,9 @@ str_make_independent_expand(VALUE str, long len, long expand, const int termlen)
if (oldptr) {
memcpy(ptr, oldptr, len);
}
+ if (FL_TEST_RAW(str, STR_NOEMBED|STR_NOFREE|STR_SHARED) == STR_NOEMBED) {
+ xfree(oldptr);
+ }
STR_SET_NOEMBED(str);
FL_UNSET(str, STR_SHARED|STR_NOFREE);
TERM_FILL(ptr + len, termlen);
@@ -2028,17 +2227,10 @@ str_null_char(const char *s, long len, const int minlen, rb_encoding *enc)
static char *
str_fill_term(VALUE str, char *s, long len, int termlen)
{
- long capa = str_capacity(str, termlen);
-
/* This function assumes that (capa + termlen) bytes of memory
* is allocated, like many other functions in this file.
*/
-
- if (capa < len) {
- rb_check_lockedtmp(str);
- str_make_independent_expand(str, len, 0L, termlen);
- }
- else if (str_dependent_p(str)) {
+ if (str_dependent_p(str)) {
if (!zero_filled(s + len, termlen))
str_make_independent_expand(str, len, 0L, termlen);
}
@@ -2052,11 +2244,11 @@ str_fill_term(VALUE str, char *s, long len, int termlen)
void
rb_str_change_terminator_length(VALUE str, const int oldtermlen, const int termlen)
{
- long capa = str_capacity(str, oldtermlen);
+ long capa = str_capacity(str, oldtermlen) + oldtermlen;
long len = RSTRING_LEN(str);
assert(capa >= len);
- if (capa - len < termlen - oldtermlen) {
+ if (capa - len < termlen) {
rb_check_lockedtmp(str);
str_make_independent_expand(str, len, 0L, termlen);
}
@@ -2068,7 +2260,7 @@ rb_str_change_terminator_length(VALUE str, const int oldtermlen, const int terml
if (!STR_EMBED_P(str)) {
/* modify capa instead of realloc */
assert(!FL_TEST((str), STR_SHARED));
- RSTRING(str)->as.heap.aux.capa = capa - (termlen - oldtermlen);
+ RSTRING(str)->as.heap.aux.capa = capa - termlen;
}
if (termlen > oldtermlen) {
TERM_FILL(RSTRING_PTR(str) + len, termlen);
@@ -2078,23 +2270,24 @@ rb_str_change_terminator_length(VALUE str, const int oldtermlen, const int terml
return;
}
-char *
-rb_string_value_cstr(volatile VALUE *ptr)
+static char *
+str_null_check(VALUE str, int *w)
{
- VALUE str = rb_string_value(ptr);
char *s = RSTRING_PTR(str);
long len = RSTRING_LEN(str);
rb_encoding *enc = rb_enc_get(str);
const int minlen = rb_enc_mbminlen(enc);
if (minlen > 1) {
+ *w = 1;
if (str_null_char(s, len, minlen, enc)) {
- rb_raise(rb_eArgError, "string contains null char");
+ return NULL;
}
return str_fill_term(str, s, len, minlen);
}
+ *w = 0;
if (!s || memchr(s, 0, len)) {
- rb_raise(rb_eArgError, "string contains null byte");
+ return NULL;
}
if (s[len]) {
s = str_fill_term(str, s, len, minlen);
@@ -2103,6 +2296,28 @@ rb_string_value_cstr(volatile VALUE *ptr)
}
char *
+rb_str_to_cstr(VALUE str)
+{
+ int w;
+ return str_null_check(str, &w);
+}
+
+char *
+rb_string_value_cstr(volatile VALUE *ptr)
+{
+ VALUE str = rb_string_value(ptr);
+ int w;
+ char *s = str_null_check(str, &w);
+ if (!s) {
+ if (w) {
+ rb_raise(rb_eArgError, "string contains null char");
+ }
+ rb_raise(rb_eArgError, "string contains null byte");
+ }
+ return s;
+}
+
+char *
rb_str_fill_terminator(VALUE str, const int newminlen)
{
char *s = RSTRING_PTR(str);
@@ -2113,7 +2328,7 @@ rb_str_fill_terminator(VALUE str, const int newminlen)
VALUE
rb_check_string_type(VALUE str)
{
- str = rb_check_convert_type(str, T_STRING, "String", "to_str");
+ str = rb_check_convert_type_with_id(str, T_STRING, "String", idTo_str);
return str;
}
@@ -2313,7 +2528,7 @@ rb_str_subpos(VALUE str, long beg, long *lenp)
beg += blen;
if (beg < 0) return 0;
}
- if (beg + len > blen)
+ if (len > blen - beg)
len = blen - beg;
if (len < 0) return 0;
p = s + beg;
@@ -2401,6 +2616,7 @@ str_substr(VALUE str, long beg, long len, int empty)
str2 = str_new_shared(rb_obj_class(str2), str2);
RSTRING(str2)->as.heap.ptr += ofs;
RSTRING(str2)->as.heap.len = len;
+ ENC_CODERANGE_CLEAR(str2);
}
else {
if (!len && !empty) return Qnil;
@@ -2445,20 +2661,15 @@ str_uplus(VALUE str)
* call-seq:
* -str -> str (frozen)
*
- * If the string is frozen, then return the string itself.
+ * Returns a frozen, possibly pre-existing copy of the string.
*
- * If the string is not frozen, then duplicate the string
- * freeze it and return it.
+ * The string will be deduplicated as long as it is not tainted,
+ * or has any instance variables set on it.
*/
static VALUE
str_uminus(VALUE str)
{
- if (OBJ_FROZEN(str)) {
- return str;
- }
- else {
- return rb_str_freeze(rb_str_dup(str));
- }
+ return rb_fstring(str);
}
RUBY_ALIAS_FUNCTION(rb_str_dup_frozen(VALUE str), rb_str_new_frozen, (str))
@@ -2501,7 +2712,7 @@ rb_str_set_len(VALUE str, long len)
if (STR_SHARED_P(str)) {
rb_raise(rb_eRuntimeError, "can't set length of shared string");
}
- if (len > (capa = (long)str_capacity(str, termlen))) {
+ if (len > (capa = (long)str_capacity(str, termlen)) || len < 0) {
rb_bug("probable buffer overflow: %ld for %ld", len, capa);
}
STR_SET_LEN(str, len);
@@ -2550,7 +2761,8 @@ rb_str_resize(VALUE str, long len)
}
else if ((capa = RSTRING(str)->as.heap.aux.capa) < len ||
(capa - len) > (len < 1024 ? len : 1024)) {
- REALLOC_N(RSTRING(str)->as.heap.ptr, char, (size_t)len + termlen);
+ SIZED_REALLOC_N(RSTRING(str)->as.heap.ptr, char,
+ (size_t)len + termlen, STR_HEAP_SIZE(str));
RSTRING(str)->as.heap.aux.capa = len;
}
else if (len == slen) return str;
@@ -2771,15 +2983,30 @@ rb_str_append(VALUE str, VALUE str2)
return rb_str_buf_append(str, str2);
}
-VALUE
+#define MIN_PRE_ALLOC_SIZE 48
+
+MJIT_FUNC_EXPORTED VALUE
rb_str_concat_literals(size_t num, const VALUE *strary)
{
VALUE str;
- size_t i;
+ size_t i, s;
+ long len = 1;
+
+ if (UNLIKELY(!num)) return rb_str_new(0, 0);
+ if (UNLIKELY(num == 1)) return rb_str_resurrect(strary[0]);
- if (!num) return rb_str_new(0, 0);
- str = rb_str_resurrect(strary[0]);
- for (i = 1; i < num; ++i) {
+ for (i = 0; i < num; ++i) { len += RSTRING_LEN(strary[i]); }
+ if (LIKELY(len < MIN_PRE_ALLOC_SIZE)) {
+ str = rb_str_resurrect(strary[0]);
+ s = 1;
+ }
+ else {
+ str = rb_str_buf_new(len);
+ rb_enc_copy(str, strary[0]);
+ s = 0;
+ }
+
+ for (i = s; i < num; ++i) {
const VALUE v = strary[i];
int encidx = ENCODING_GET(v);
@@ -2796,31 +3023,33 @@ rb_str_concat_literals(size_t num, const VALUE *strary)
/*
* call-seq:
- * str << integer -> str
- * str.concat(integer1, integer2,...) -> str
- * str << obj -> str
- * str.concat(obj1, obj2,...) -> str
+ * str.concat(obj1, obj2, ...) -> str
+ *
+ * Concatenates the given object(s) to <i>str</i>. If an object is an
+ * <code>Integer</code>, it is considered a codepoint and converted
+ * to a character before concatenation.
*
- * Append---Concatenates the given object to <i>str</i>. If the object is an
- * <code>Integer</code>, it is considered as a codepoint, and is converted
- * to a character before concatenation. Concat can take multiple arguments.
- * All the arguments are concatenated in order.
+ * +concat+ can take multiple arguments, and all the arguments are
+ * concatenated in order.
*
* a = "hello "
- * a << "world" #=> "hello world"
- * a.concat(33) #=> "hello world!"
- * a #=> "hollo world!"
+ * a.concat("world", 33) #=> "hello world!"
+ * a #=> "hello world!"
*
* b = "sn"
- * b.concat(b, b) #=> "snsnsn"
+ * b.concat("_", b, "_", b) #=> "sn_sn_sn"
+ *
+ * See also String#<<, which takes a single argument.
*/
-
static VALUE
rb_str_concat_multi(int argc, VALUE *argv, VALUE str)
{
str_modifiable(str);
- if (argc > 0) {
+ if (argc == 1) {
+ return rb_str_concat(str, argv[0]);
+ }
+ else if (argc > 1) {
int i;
VALUE arg_str = rb_str_tmp_new(0);
rb_enc_copy(arg_str, str);
@@ -2833,6 +3062,21 @@ rb_str_concat_multi(int argc, VALUE *argv, VALUE str)
return str;
}
+/*
+ * call-seq:
+ * str << obj -> str
+ * str << integer -> str
+ *
+ * Appends the given object to <i>str</i>. If the object is an
+ * <code>Integer</code>, it is considered a codepoint and converted
+ * to a character before being appended.
+ *
+ * a = "hello "
+ * a << "world" #=> "hello world"
+ * a << 33 #=> "hello world!"
+ *
+ * See also String#concat, which takes multiple arguments.
+ */
VALUE
rb_str_concat(VALUE str1, VALUE str2)
{
@@ -2899,7 +3143,7 @@ rb_str_concat(VALUE str1, VALUE str2)
/*
* call-seq:
- * str.prepend(other_str1, other_str2,...) -> str
+ * str.prepend(other_str1, other_str2, ...) -> str
*
* Prepend---Prepend the given strings to <i>str</i>.
*
@@ -2915,7 +3159,10 @@ rb_str_prepend_multi(int argc, VALUE *argv, VALUE str)
{
str_modifiable(str);
- if (argc > 0) {
+ if (argc == 1) {
+ rb_str_update(str, 0L, 0L, argv[0]);
+ }
+ else if (argc > 1) {
int i;
VALUE arg_str = rb_str_tmp_new(0);
rb_enc_copy(arg_str, str);
@@ -2954,7 +3201,7 @@ rb_str_hash_cmp(VALUE str1, VALUE str2)
* call-seq:
* str.hash -> integer
*
- * Return a hash based on the string's length, content and encoding.
+ * Returns a hash based on the string's length, content and encoding.
*
* See also Object#hash.
*/
@@ -3040,12 +3287,10 @@ str_eql(const VALUE str1, const VALUE str2)
* str == obj -> true or false
* str === obj -> true or false
*
- * === Equality
- *
- * Returns whether +str+ == +obj+, similar to Object#==.
+ * Equality---Returns whether +str+ == +obj+, similar to Object#==.
*
* If +obj+ is not an instance of String but responds to +to_str+, then the
- * two strings are compared using case equality Object#===.
+ * two strings are compared using <code>obj.==</code>.
*
* Otherwise, returns similarly to String#eql?, comparing length and content.
*/
@@ -3070,7 +3315,7 @@ rb_str_equal(VALUE str1, VALUE str2)
* Two strings are equal if they have the same length and content.
*/
-static VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_str_eql(VALUE str1, VALUE str2)
{
if (str1 == str2) return Qtrue;
@@ -3080,11 +3325,10 @@ rb_str_eql(VALUE str1, VALUE str2)
/*
* call-seq:
- * string <=> other_string -> -1, 0, +1 or nil
- *
+ * string <=> other_string -> -1, 0, +1, or nil
*
- * Comparison---Returns -1, 0, +1 or nil depending on whether +string+ is less
- * than, equal to, or greater than +other_string+.
+ * Comparison---Returns -1, 0, +1, or +nil+ depending on whether +string+ is
+ * less than, equal to, or greater than +other_string+.
*
* +nil+ is returned if the two values are incomparable.
*
@@ -3108,42 +3352,54 @@ static VALUE
rb_str_cmp_m(VALUE str1, VALUE str2)
{
int result;
-
- if (!RB_TYPE_P(str2, T_STRING)) {
- VALUE tmp = rb_check_funcall(str2, idTo_str, 0, 0);
- if (RB_TYPE_P(tmp, T_STRING)) {
- result = rb_str_cmp(str1, tmp);
- }
- else {
- return rb_invcmp(str1, str2);
- }
- }
- else {
- result = rb_str_cmp(str1, str2);
+ VALUE s = rb_check_string_type(str2);
+ if (NIL_P(s)) {
+ return rb_invcmp(str1, str2);
}
+ result = rb_str_cmp(str1, s);
return INT2FIX(result);
}
+static VALUE str_casecmp(VALUE str1, VALUE str2);
+static VALUE str_casecmp_p(VALUE str1, VALUE str2);
+
/*
* call-seq:
- * str.casecmp(other_str) -> -1, 0, +1 or nil
+ * str.casecmp(other_str) -> -1, 0, +1, or nil
*
* Case-insensitive version of <code>String#<=></code>.
+ * Currently, case-insensitivity only works on characters A-Z/a-z,
+ * not all of Unicode. This is different from String#casecmp?.
*
- * "abcdef".casecmp("abcde") #=> 1
+ * "aBcDeF".casecmp("abcde") #=> 1
* "aBcDeF".casecmp("abcdef") #=> 0
- * "abcdef".casecmp("abcdefg") #=> -1
+ * "aBcDeF".casecmp("abcdefg") #=> -1
* "abcdef".casecmp("ABCDEF") #=> 0
+ *
+ * +nil+ is returned if the two strings have incompatible encodings,
+ * or if +other_str+ is not a string.
+ *
+ * "foo".casecmp(2) #=> nil
+ * "\u{e4 f6 fc}".encode("ISO-8859-1").casecmp("\u{c4 d6 dc}") #=> nil
*/
static VALUE
rb_str_casecmp(VALUE str1, VALUE str2)
{
+ VALUE s = rb_check_string_type(str2);
+ if (NIL_P(s)) {
+ return Qnil;
+ }
+ return str_casecmp(str1, s);
+}
+
+static VALUE
+str_casecmp(VALUE str1, VALUE str2)
+{
long len;
rb_encoding *enc;
char *p1, *p1end, *p2, *p2end;
- StringValue(str2);
enc = rb_enc_compatible(str1, str2);
if (!enc) {
return Qnil;
@@ -3194,50 +3450,112 @@ rb_str_casecmp(VALUE str1, VALUE str2)
return INT2FIX(-1);
}
+/*
+ * call-seq:
+ * str.casecmp?(other_str) -> true, false, or nil
+ *
+ * Returns +true+ if +str+ and +other_str+ are equal after
+ * Unicode case folding, +false+ if they are not equal.
+ *
+ * "aBcDeF".casecmp?("abcde") #=> false
+ * "aBcDeF".casecmp?("abcdef") #=> true
+ * "aBcDeF".casecmp?("abcdefg") #=> false
+ * "abcdef".casecmp?("ABCDEF") #=> true
+ * "\u{e4 f6 fc}".casecmp?("\u{c4 d6 dc}") #=> true
+ *
+ * +nil+ is returned if the two strings have incompatible encodings,
+ * or if +other_str+ is not a string.
+ *
+ * "foo".casecmp?(2) #=> nil
+ * "\u{e4 f6 fc}".encode("ISO-8859-1").casecmp?("\u{c4 d6 dc}") #=> nil
+ */
+
+static VALUE
+rb_str_casecmp_p(VALUE str1, VALUE str2)
+{
+ VALUE s = rb_check_string_type(str2);
+ if (NIL_P(s)) {
+ return Qnil;
+ }
+ return str_casecmp_p(str1, s);
+}
+
+static VALUE
+str_casecmp_p(VALUE str1, VALUE str2)
+{
+ rb_encoding *enc;
+ VALUE folded_str1, folded_str2;
+ VALUE fold_opt = sym_fold;
+
+ enc = rb_enc_compatible(str1, str2);
+ if (!enc) {
+ return Qnil;
+ }
+
+ folded_str1 = rb_str_downcase(1, &fold_opt, str1);
+ folded_str2 = rb_str_downcase(1, &fold_opt, str2);
+
+ return rb_str_eql(folded_str1, folded_str2);
+}
+
+static long
+strseq_core(const char *str_ptr, const char *str_ptr_end, long str_len,
+ const char *sub_ptr, long sub_len, long offset, rb_encoding *enc)
+{
+ const char *search_start = str_ptr;
+ long pos, search_len = str_len - offset;
+
+ for (;;) {
+ const char *t;
+ pos = rb_memsearch(sub_ptr, sub_len, search_start, search_len, enc);
+ if (pos < 0) return pos;
+ t = rb_enc_right_char_head(search_start, search_start+pos, str_ptr_end, enc);
+ if (t == search_start + pos) break;
+ search_len -= t - search_start;
+ if (search_len <= 0) return -1;
+ offset += t - search_start;
+ search_start = t;
+ }
+ return pos + offset;
+}
+
#define rb_str_index(str, sub, offset) rb_strseq_index(str, sub, offset, 0)
static long
rb_strseq_index(VALUE str, VALUE sub, long offset, int in_byte)
{
- const char *s, *sptr, *e;
- long pos, len, slen;
+ const char *str_ptr, *str_ptr_end, *sub_ptr;
+ long str_len, sub_len;
int single_byte = single_byte_optimizable(str);
rb_encoding *enc;
enc = rb_enc_check(str, sub);
if (is_broken_string(sub)) return -1;
- len = (in_byte || single_byte) ? RSTRING_LEN(str) : str_strlen(str, enc); /* rb_enc_check */
- slen = in_byte ? RSTRING_LEN(sub) : str_strlen(sub, enc); /* rb_enc_check */
- if (offset < 0) {
- offset += len;
- if (offset < 0) return -1;
- }
- if (len - offset < slen) return -1;
+ str_ptr = RSTRING_PTR(str);
+ str_ptr_end = RSTRING_END(str);
+ str_len = RSTRING_LEN(str);
+ sub_ptr = RSTRING_PTR(sub);
+ sub_len = RSTRING_LEN(sub);
- s = RSTRING_PTR(str);
- e = RSTRING_END(str);
- if (offset) {
- if (!in_byte) offset = str_offset(s, e, offset, enc, single_byte);
- s += offset;
+ if (str_len < sub_len) return -1;
+
+ if (offset != 0) {
+ long str_len_char, sub_len_char;
+ str_len_char = (in_byte || single_byte) ? str_len : str_strlen(str, enc);
+ sub_len_char = in_byte ? sub_len : str_strlen(sub, enc);
+ if (offset < 0) {
+ offset += str_len_char;
+ if (offset < 0) return -1;
+ }
+ if (str_len_char - offset < sub_len_char) return -1;
+ if (!in_byte) offset = str_offset(str_ptr, str_ptr_end, offset, enc, single_byte);
+ str_ptr += offset;
}
- if (slen == 0) return offset;
+ if (sub_len == 0) return offset;
+
/* need proceed one character at a time */
- sptr = RSTRING_PTR(sub);
- slen = RSTRING_LEN(sub);
- len = RSTRING_LEN(str) - offset;
- for (;;) {
- const char *t;
- pos = rb_memsearch(sptr, slen, s, len, enc);
- if (pos < 0) return pos;
- t = rb_enc_right_char_head(s, s+pos, e, enc);
- if (t == s + pos) break;
- len -= t - s;
- if (len <= 0) return -1;
- offset += t - s;
- s = t;
- }
- return pos + offset;
+ return strseq_core(str_ptr, str_ptr_end, str_len, sub_ptr, sub_len, offset, enc);
}
@@ -3455,10 +3773,8 @@ rb_str_rindex_m(int argc, VALUE *argv, VALUE str)
pos = str_offset(RSTRING_PTR(str), RSTRING_END(str), pos,
enc, single_byte_optimizable(str));
- if (!RREGEXP_PTR(sub) || RREGEXP_SRC_LEN(sub)) {
- pos = rb_reg_search(sub, str, pos, 1);
- pos = rb_str_sublen(str, pos);
- }
+ pos = rb_reg_search(sub, str, pos, 1);
+ pos = rb_str_sublen(str, pos);
if (pos >= 0) return LONG2NUM(pos);
break;
@@ -3534,6 +3850,7 @@ static VALUE get_pat(VALUE);
* 'hello'.match('(.)\1') #=> #<MatchData "ll" 1:"l">
* 'hello'.match('(.)\1')[0] #=> "ll"
* 'hello'.match(/(.)\1/)[0] #=> "ll"
+ * 'hello'.match(/(.)\1/, 3) #=> nil
* 'hello'.match('xx') #=> nil
*
* If a block is given, invoke the block with MatchData if match succeed, so
@@ -3565,6 +3882,32 @@ rb_str_match_m(int argc, VALUE *argv, VALUE str)
return result;
}
+/*
+ * call-seq:
+ * str.match?(pattern) -> true or false
+ * str.match?(pattern, pos) -> true or false
+ *
+ * Converts _pattern_ to a +Regexp+ (if it isn't already one), then
+ * returns a +true+ or +false+ indicates whether the regexp is
+ * matched _str_ or not without updating <code>$~</code> and other
+ * related variables. If the second parameter is present, it
+ * specifies the position in the string to begin the search.
+ *
+ * "Ruby".match?(/R.../) #=> true
+ * "Ruby".match?(/R.../, 1) #=> false
+ * "Ruby".match?(/P.../) #=> false
+ * $& #=> nil
+ */
+
+static VALUE
+rb_str_match_m_p(int argc, VALUE *argv, VALUE str)
+{
+ VALUE re;
+ rb_check_arity(argc, 1, 2);
+ re = get_pat(argv[0]);
+ return rb_reg_match_p(re, str, argc > 1 ? NUM2LONG(argv[1]) : 0);
+}
+
enum neighbor_char {
NEIGHBOR_NOT_CHAR,
NEIGHBOR_FOUND,
@@ -3859,6 +4202,7 @@ str_succ(VALUE str)
}
carry_pos = s - sbeg;
}
+ ENC_CODERANGE_SET(str, ENC_CODERANGE_UNKNOWN);
}
RESIZE_CAPA(str, slen + carry_len);
sbeg = RSTRING_PTR(str);
@@ -3900,8 +4244,6 @@ all_digits_p(const char *s, long len)
return 1;
}
-static VALUE str_upto_each(VALUE beg, VALUE end, int excl, int (*each)(VALUE, VALUE), VALUE);
-
static int
str_upto_i(VALUE str, VALUE arg)
{
@@ -3948,11 +4290,11 @@ rb_str_upto(int argc, VALUE *argv, VALUE beg)
rb_scan_args(argc, argv, "11", &end, &exclusive);
RETURN_ENUMERATOR(beg, argc, argv);
- return str_upto_each(beg, end, RTEST(exclusive), str_upto_i, Qnil);
+ return rb_str_upto_each(beg, end, RTEST(exclusive), str_upto_i, Qnil);
}
-static VALUE
-str_upto_each(VALUE beg, VALUE end, int excl, int (*each)(VALUE, VALUE), VALUE arg)
+VALUE
+rb_str_upto_each(VALUE beg, VALUE end, int excl, int (*each)(VALUE, VALUE), VALUE arg)
{
VALUE current, after_end;
ID succ;
@@ -4000,7 +4342,7 @@ str_upto_each(VALUE beg, VALUE end, int excl, int (*each)(VALUE, VALUE), VALUE a
}
else {
ID op = excl ? '<' : idLE;
- VALUE args[2], fmt = rb_obj_freeze(rb_usascii_str_new_cstr("%.*d"));
+ VALUE args[2], fmt = rb_fstring_lit("%.*d");
args[0] = INT2FIX(width);
while (rb_funcall(b, op, 1, e)) {
@@ -4033,6 +4375,50 @@ str_upto_each(VALUE beg, VALUE end, int excl, int (*each)(VALUE, VALUE), VALUE a
return beg;
}
+VALUE
+rb_str_upto_endless_each(VALUE beg, int (*each)(VALUE, VALUE), VALUE arg)
+{
+ VALUE current;
+ ID succ;
+
+ CONST_ID(succ, "succ");
+ /* both edges are all digits */
+ if (is_ascii_string(beg) && ISDIGIT(RSTRING_PTR(beg)[0]) &&
+ all_digits_p(RSTRING_PTR(beg), RSTRING_LEN(beg))) {
+ VALUE b, args[2], fmt = rb_fstring_lit("%.*d");
+ int width = RSTRING_LENINT(beg);
+ b = rb_str_to_inum(beg, 10, FALSE);
+ if (FIXNUM_P(b)) {
+ long bi = FIX2LONG(b);
+ rb_encoding *usascii = rb_usascii_encoding();
+
+ while (FIXABLE(bi)) {
+ if ((*each)(rb_enc_sprintf(usascii, "%.*ld", width, bi), arg)) break;
+ bi++;
+ }
+ b = LONG2NUM(bi);
+ }
+ args[0] = INT2FIX(width);
+ while (1) {
+ args[1] = b;
+ if ((*each)(rb_str_format(numberof(args), args, fmt), arg)) break;
+ b = rb_funcallv(b, succ, 0, 0);
+ }
+ }
+ /* normal case */
+ current = rb_str_dup(beg);
+ while (1) {
+ VALUE next = rb_funcallv(current, succ, 0, 0);
+ if ((*each)(current, arg)) break;
+ current = next;
+ StringValue(current);
+ if (RSTRING_LEN(current) == 0)
+ break;
+ }
+
+ return beg;
+}
+
static int
include_range_i(VALUE str, VALUE arg)
{
@@ -4081,7 +4467,7 @@ rb_str_include_range_p(VALUE beg, VALUE end, VALUE val, VALUE exclusive)
}
#endif
}
- str_upto_each(beg, end, RTEST(exclusive), include_range_i, (VALUE)&val);
+ rb_str_upto_each(beg, end, RTEST(exclusive), include_range_i, (VALUE)&val);
return NIL_P(val) ? Qtrue : Qfalse;
}
@@ -4250,6 +4636,7 @@ rb_str_splice_0(VALUE str, long beg, long len, VALUE val)
{
char *sptr;
long slen, vlen = RSTRING_LEN(val);
+ int cr;
if (beg == 0 && vlen == 0) {
rb_str_drop_bytes(str, len);
@@ -4257,7 +4644,7 @@ rb_str_splice_0(VALUE str, long beg, long len, VALUE val)
return;
}
- rb_str_modify(str);
+ str_modify_keep_cr(str);
RSTRING_GETMEM(str, sptr, slen);
if (len < vlen) {
/* expand string */
@@ -4265,6 +4652,11 @@ rb_str_splice_0(VALUE str, long beg, long len, VALUE val)
sptr = RSTRING_PTR(str);
}
+ if (ENC_CODERANGE(str) == ENC_CODERANGE_7BIT)
+ cr = rb_enc_str_coderange(val);
+ else
+ cr = ENC_CODERANGE_UNKNOWN;
+
if (vlen != len) {
memmove(sptr + beg + vlen,
sptr + beg + len,
@@ -4280,6 +4672,7 @@ rb_str_splice_0(VALUE str, long beg, long len, VALUE val)
STR_SET_LEN(str, slen);
TERM_FILL(&sptr[slen], TERM_LEN(str));
OBJ_INFECT(str, val);
+ ENC_CODERANGE_SET(str, cr);
}
void
@@ -4302,12 +4695,14 @@ rb_str_update(VALUE str, long beg, long len, VALUE val)
rb_raise(rb_eIndexError, "index %ld out of string", beg);
}
if (beg < 0) {
- if (-beg > slen) {
+ if (beg + slen < 0) {
goto out_of_range;
}
beg += slen;
}
- if (slen < len || slen < beg + len) {
+ assert(beg >= 0);
+ assert(beg <= slen);
+ if (len > slen - beg) {
len = slen - beg;
}
str_modify_keep_cr(str);
@@ -4378,7 +4773,7 @@ rb_str_aset(VALUE str, VALUE indx, VALUE val)
}
if (SPECIAL_CONST_P(indx)) goto generic;
- switch (TYPE(indx)) {
+ switch (BUILTIN_TYPE(indx)) {
case T_REGEXP:
rb_str_subpat_set(str, indx, INT2FIX(0), val);
return val;
@@ -4698,7 +5093,7 @@ rb_str_sub_bang(int argc, VALUE *argv, VALUE str)
cr = cr2;
}
plen = end0 - beg0;
- rp = RSTRING_PTR(repl); rlen = RSTRING_LEN(repl);
+ rlen = RSTRING_LEN(repl);
len = RSTRING_LEN(str);
if (rlen > plen) {
RESIZE_CAPA(str, len + rlen - plen);
@@ -4707,7 +5102,8 @@ rb_str_sub_bang(int argc, VALUE *argv, VALUE str)
if (rlen != plen) {
memmove(p + beg0 + rlen, p + beg0 + plen, len - beg0 - plen);
}
- memcpy(p + beg0, rp, rlen);
+ rp = RSTRING_PTR(repl);
+ memmove(p + beg0, rp, rlen);
len += rlen - plen;
STR_SET_LEN(str, len);
TERM_FILL(&RSTRING_PTR(str)[len], TERM_LEN(str));
@@ -4927,7 +5323,7 @@ rb_str_gsub_bang(int argc, VALUE *argv, VALUE str)
* str.gsub(pattern) {|match| block } -> new_str
* str.gsub(pattern) -> enumerator
*
- * Returns a copy of <i>str</i> with the <em>all</em> occurrences of
+ * Returns a copy of <i>str</i> with <em>all</em> occurrences of
* <i>pattern</i> substituted for the second argument. The <i>pattern</i> is
* typically a <code>Regexp</code>; if given as a <code>String</code>, any
* regular expression metacharacters it contains will be interpreted
@@ -5061,9 +5457,9 @@ static VALUE
rb_str_setbyte(VALUE str, VALUE index, VALUE value)
{
long pos = NUM2LONG(index);
- int byte = NUM2INT(value);
long len = RSTRING_LEN(str);
- char *head, *ptr, *left = 0;
+ char *head, *left = 0;
+ unsigned char *ptr;
rb_encoding *enc;
int cr = ENC_CODERANGE_UNKNOWN, width, nlen;
@@ -5072,18 +5468,22 @@ rb_str_setbyte(VALUE str, VALUE index, VALUE value)
if (pos < 0)
pos += len;
+ VALUE v = rb_to_int(value);
+ VALUE w = rb_int_modulo(v, INT2FIX(256));
+ unsigned char byte = NUM2INT(w) & 0xFF;
+
if (!str_independent(str))
str_make_independent(str);
enc = STR_ENC_GET(str);
head = RSTRING_PTR(str);
- ptr = &head[pos];
- if (!STR_EMBEDDABLE_P(len, rb_enc_mbminlen(enc))) {
+ ptr = (unsigned char *)&head[pos];
+ if (!STR_EMBED_P(str)) {
cr = ENC_CODERANGE(str);
switch (cr) {
case ENC_CODERANGE_7BIT:
- left = ptr;
+ left = (char *)ptr;
*ptr = byte;
- if (ISASCII(byte)) break;
+ if (ISASCII(byte)) goto end;
nlen = rb_enc_precise_mbclen(left, head+len, enc);
if (!MBCLEN_CHARFOUND_P(nlen))
ENC_CODERANGE_SET(str, ENC_CODERANGE_BROKEN);
@@ -5121,7 +5521,7 @@ str_byte_substr(VALUE str, long beg, long len, int empty)
beg += n;
if (beg < 0) return Qnil;
}
- if (beg + len > n)
+ if (len > n - beg)
len = n - beg;
if (len <= 0) {
if (!empty) return Qnil;
@@ -5131,7 +5531,7 @@ str_byte_substr(VALUE str, long beg, long len, int empty)
else
p = s + beg;
- if (!STR_EMBEDDABLE_P(len, TERM_LEN(str) && SHARABLE_SUBSTRING_P(beg, len, n))) {
+ if (!STR_EMBEDDABLE_P(len, TERM_LEN(str)) && SHARABLE_SUBSTRING_P(beg, len, n)) {
str2 = rb_str_new_frozen(str);
str2 = str_new_shared(rb_obj_class(str2), str2);
RSTRING(str2)->as.heap.ptr += beg;
@@ -5369,16 +5769,9 @@ rb_str_include(VALUE str, VALUE arg)
static VALUE
rb_str_to_i(int argc, VALUE *argv, VALUE str)
{
- int base;
-
- if (argc == 0) base = 10;
- else {
- VALUE b;
+ int base = 10;
- rb_scan_args(argc, argv, "01", &b);
- base = NUM2INT(b);
- }
- if (base < 0) {
+ if (rb_check_arity(argc, 0, 1) && (base = NUM2INT(argv[0])) < 0) {
rb_raise(rb_eArgError, "invalid radix %d", base);
}
return rb_str_to_inum(str, base, FALSE);
@@ -5652,7 +6045,7 @@ rb_str_inspect(VALUE str)
* Produces a version of +str+ with all non-printing characters replaced by
* <code>\nnn</code> notation and all special characters escaped.
*
- * "hello \n ''".dump #=> "\"hello \\n ''\"
+ * "hello \n ''".dump #=> "\"hello \\n ''\""
*/
VALUE
@@ -5665,7 +6058,7 @@ rb_str_dump(VALUE str)
char *q, *qend;
VALUE result;
int u8 = (encidx == rb_utf8_encindex());
- static const char nonascii_suffix[] = ".force_encoding(\"%s\")";
+ static const char nonascii_suffix[] = ".dup.force_encoding(\"%s\")";
len = 2; /* "" */
if (!rb_enc_asciicompat(enc)) {
@@ -5803,6 +6196,239 @@ rb_str_dump(VALUE str)
return result;
}
+static int
+unescape_ascii(unsigned int c)
+{
+ switch (c) {
+ case 'n':
+ return '\n';
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+ case 'f':
+ return '\f';
+ case 'v':
+ return '\13';
+ case 'b':
+ return '\010';
+ case 'a':
+ return '\007';
+ case 'e':
+ return 033;
+ default:
+ UNREACHABLE;
+ }
+}
+
+static void
+undump_after_backslash(VALUE undumped, const char **ss, const char *s_end, rb_encoding **penc, bool *utf8, bool *binary)
+{
+ const char *s = *ss;
+ unsigned int c;
+ int codelen;
+ size_t hexlen;
+ unsigned char buf[6];
+ static rb_encoding *enc_utf8 = NULL;
+
+ switch (*s) {
+ case '\\':
+ case '"':
+ case '#':
+ rb_str_cat(undumped, s, 1); /* cat itself */
+ s++;
+ break;
+ case 'n':
+ case 'r':
+ case 't':
+ case 'f':
+ case 'v':
+ case 'b':
+ case 'a':
+ case 'e':
+ *buf = unescape_ascii(*s);
+ rb_str_cat(undumped, (char *)buf, 1);
+ s++;
+ break;
+ case 'u':
+ if (*binary) {
+ rb_raise(rb_eRuntimeError, "hex escape and Unicode escape are mixed");
+ }
+ *utf8 = true;
+ if (++s >= s_end) {
+ rb_raise(rb_eRuntimeError, "invalid Unicode escape");
+ }
+ if (enc_utf8 == NULL) enc_utf8 = rb_utf8_encoding();
+ if (*penc != enc_utf8) {
+ *penc = enc_utf8;
+ rb_enc_associate(undumped, enc_utf8);
+ }
+ if (*s == '{') { /* handle \u{...} form */
+ s++;
+ for (;;) {
+ if (s >= s_end) {
+ rb_raise(rb_eRuntimeError, "unterminated Unicode escape");
+ }
+ if (*s == '}') {
+ s++;
+ break;
+ }
+ if (ISSPACE(*s)) {
+ s++;
+ continue;
+ }
+ c = scan_hex(s, s_end-s, &hexlen);
+ if (hexlen == 0 || hexlen > 6) {
+ rb_raise(rb_eRuntimeError, "invalid Unicode escape");
+ }
+ if (c > 0x10ffff) {
+ rb_raise(rb_eRuntimeError, "invalid Unicode codepoint (too large)");
+ }
+ if (0xd800 <= c && c <= 0xdfff) {
+ rb_raise(rb_eRuntimeError, "invalid Unicode codepoint");
+ }
+ codelen = rb_enc_mbcput(c, (char *)buf, *penc);
+ rb_str_cat(undumped, (char *)buf, codelen);
+ s += hexlen;
+ }
+ }
+ else { /* handle \uXXXX form */
+ c = scan_hex(s, 4, &hexlen);
+ if (hexlen != 4) {
+ rb_raise(rb_eRuntimeError, "invalid Unicode escape");
+ }
+ if (0xd800 <= c && c <= 0xdfff) {
+ rb_raise(rb_eRuntimeError, "invalid Unicode codepoint");
+ }
+ codelen = rb_enc_mbcput(c, (char *)buf, *penc);
+ rb_str_cat(undumped, (char *)buf, codelen);
+ s += hexlen;
+ }
+ break;
+ case 'x':
+ if (*utf8) {
+ rb_raise(rb_eRuntimeError, "hex escape and Unicode escape are mixed");
+ }
+ *binary = true;
+ if (++s >= s_end) {
+ rb_raise(rb_eRuntimeError, "invalid hex escape");
+ }
+ *buf = scan_hex(s, 2, &hexlen);
+ if (hexlen != 2) {
+ rb_raise(rb_eRuntimeError, "invalid hex escape");
+ }
+ rb_str_cat(undumped, (char *)buf, 1);
+ s += hexlen;
+ break;
+ default:
+ rb_str_cat(undumped, s-1, 2);
+ s++;
+ }
+
+ *ss = s;
+}
+
+static VALUE rb_str_is_ascii_only_p(VALUE str);
+
+/*
+ * call-seq:
+ * str.undump -> new_str
+ *
+ * Produces unescaped version of +str+.
+ * See also String#dump because String#undump does inverse of String#dump.
+ *
+ * "\"hello \\n ''\"".undump #=> "hello \n ''"
+ */
+
+static VALUE
+str_undump(VALUE str)
+{
+ const char *s = RSTRING_PTR(str);
+ const char *s_end = RSTRING_END(str);
+ rb_encoding *enc = rb_enc_get(str);
+ VALUE undumped = rb_enc_str_new(s, 0L, enc);
+ bool utf8 = false;
+ bool binary = false;
+ int w;
+
+ rb_must_asciicompat(str);
+ if (rb_str_is_ascii_only_p(str) == Qfalse) {
+ rb_raise(rb_eRuntimeError, "non-ASCII character detected");
+ }
+ if (!str_null_check(str, &w)) {
+ rb_raise(rb_eRuntimeError, "string contains null byte");
+ }
+ if (RSTRING_LEN(str) < 2) goto invalid_format;
+ if (*s != '"') goto invalid_format;
+
+ /* strip '"' at the start */
+ s++;
+
+ for (;;) {
+ if (s >= s_end) {
+ rb_raise(rb_eRuntimeError, "unterminated dumped string");
+ }
+
+ if (*s == '"') {
+ /* epilogue */
+ s++;
+ if (s == s_end) {
+ /* ascii compatible dumped string */
+ break;
+ }
+ else {
+ static const char force_encoding_suffix[] = ".force_encoding(\""; /* "\")" */
+ static const char dup_suffix[] = ".dup";
+ const char *encname;
+ int encidx;
+ ptrdiff_t size;
+
+ /* check separately for strings dumped by older versions */
+ size = sizeof(dup_suffix) - 1;
+ if (s_end - s > size && memcmp(s, dup_suffix, size) == 0) s += size;
+
+ size = sizeof(force_encoding_suffix) - 1;
+ if (s_end - s <= size) goto invalid_format;
+ if (memcmp(s, force_encoding_suffix, size) != 0) goto invalid_format;
+ s += size;
+
+ if (utf8) {
+ rb_raise(rb_eRuntimeError, "dumped string contained Unicode escape but used force_encoding");
+ }
+
+ encname = s;
+ s = memchr(s, '"', s_end-s);
+ size = s - encname;
+ if (!s) goto invalid_format;
+ if (s_end - s != 2) goto invalid_format;
+ if (s[0] != '"' || s[1] != ')') goto invalid_format;
+
+ encidx = rb_enc_find_index2(encname, (long)size);
+ if (encidx < 0) {
+ rb_raise(rb_eRuntimeError, "dumped string has unknown encoding name");
+ }
+ rb_enc_associate_index(undumped, encidx);
+ }
+ break;
+ }
+
+ if (*s == '\\') {
+ s++;
+ if (s >= s_end) {
+ rb_raise(rb_eRuntimeError, "invalid escape");
+ }
+ undump_after_backslash(undumped, &s, s_end, &enc, &utf8, &binary);
+ }
+ else {
+ rb_str_cat(undumped, s++, 1);
+ }
+ }
+
+ OBJ_INFECT(undumped, str);
+ return undumped;
+invalid_format:
+ rb_raise(rb_eRuntimeError, "invalid dumped string; not wrapped with '\"' nor '\"...\".force_encoding(\"...\")' form");
+}
static void
rb_str_check_dummy_enc(rb_encoding *enc)
@@ -5864,9 +6490,26 @@ typedef struct mapping_buffer {
size_t capa;
size_t used;
struct mapping_buffer *next;
- OnigUChar space[0];
+ OnigUChar space[FLEX_ARY_LEN];
} mapping_buffer;
+static void
+mapping_buffer_free(void *p)
+{
+ mapping_buffer *previous_buffer;
+ mapping_buffer *current_buffer = p;
+ while (current_buffer) {
+ previous_buffer = current_buffer;
+ current_buffer = current_buffer->next;
+ ruby_sized_xfree(previous_buffer, previous_buffer->capa);
+ }
+}
+
+static const rb_data_type_t mapping_buffer_type = {
+ "mapping_buffer",
+ {0, mapping_buffer_free,}
+};
+
static VALUE
rb_str_casemap(VALUE source, OnigCaseFoldType *flags, rb_encoding *enc)
{
@@ -5874,8 +6517,9 @@ rb_str_casemap(VALUE source, OnigCaseFoldType *flags, rb_encoding *enc)
OnigUChar *source_current, *source_end;
int target_length = 0;
- mapping_buffer pre_buffer, /* only next pointer used */
- *current_buffer = &pre_buffer;
+ VALUE buffer_anchor;
+ mapping_buffer *current_buffer = 0;
+ mapping_buffer **pre_buffer;
size_t buffer_count = 0;
int buffer_length_or_invalid;
@@ -5884,14 +6528,17 @@ rb_str_casemap(VALUE source, OnigCaseFoldType *flags, rb_encoding *enc)
source_current = (OnigUChar*)RSTRING_PTR(source);
source_end = (OnigUChar*)RSTRING_END(source);
+ buffer_anchor = TypedData_Wrap_Struct(0, &mapping_buffer_type, 0);
+ pre_buffer = (mapping_buffer **)&DATA_PTR(buffer_anchor);
while (source_current < source_end) {
/* increase multiplier using buffer count to converge quickly */
size_t capa = (size_t)(source_end-source_current)*++buffer_count + CASE_MAPPING_ADDITIONAL_LENGTH;
if (CASEMAP_DEBUG) {
fprintf(stderr, "Buffer allocation, capa is %"PRIuSIZE"\n", capa); /* for tuning */
}
- current_buffer->next = (mapping_buffer*)ALLOC_N(char, sizeof(mapping_buffer)+capa);
- current_buffer = current_buffer->next;
+ current_buffer = xmalloc(offsetof(mapping_buffer, space) + capa);
+ *pre_buffer = current_buffer;
+ pre_buffer = &current_buffer->next;
current_buffer->next = NULL;
current_buffer->capa = capa;
buffer_length_or_invalid = enc->case_map(flags,
@@ -5900,14 +6547,9 @@ rb_str_casemap(VALUE source, OnigCaseFoldType *flags, rb_encoding *enc)
current_buffer->space+current_buffer->capa,
enc);
if (buffer_length_or_invalid < 0) {
- mapping_buffer *previous_buffer;
-
- current_buffer = pre_buffer.next;
- while (current_buffer) {
- previous_buffer = current_buffer;
- current_buffer = current_buffer->next;
- xfree(previous_buffer);
- }
+ current_buffer = DATA_PTR(buffer_anchor);
+ DATA_PTR(buffer_anchor) = 0;
+ mapping_buffer_free(current_buffer);
rb_raise(rb_eArgError, "input string invalid");
}
target_length += current_buffer->used = buffer_length_or_invalid;
@@ -5918,23 +6560,22 @@ rb_str_casemap(VALUE source, OnigCaseFoldType *flags, rb_encoding *enc)
if (buffer_count==1) {
target = rb_str_new_with_class(source, (const char*)current_buffer->space, target_length);
- xfree(current_buffer);
}
else {
char *target_current;
- mapping_buffer *previous_buffer;
target = rb_str_new_with_class(source, 0, target_length);
target_current = RSTRING_PTR(target);
- current_buffer=pre_buffer.next;
+ current_buffer = DATA_PTR(buffer_anchor);
while (current_buffer) {
memcpy(target_current, current_buffer->space, current_buffer->used);
target_current += current_buffer->used;
- previous_buffer = current_buffer;
current_buffer = current_buffer->next;
- xfree(previous_buffer);
}
}
+ current_buffer = DATA_PTR(buffer_anchor);
+ DATA_PTR(buffer_anchor) = 0;
+ mapping_buffer_free(current_buffer);
/* TODO: check about string terminator character */
OBJ_INFECT_RAW(target, source);
@@ -5990,7 +6631,7 @@ rb_str_upcase_bang(int argc, VALUE *argv, VALUE str)
str_modify_keep_cr(str);
enc = STR_ENC_GET(str);
rb_str_check_dummy_enc(enc);
- if ((flags&ONIGENC_CASE_ASCII_ONLY) && (enc==rb_utf8_encoding() || rb_enc_mbmaxlen(enc)==1)
+ if (((flags&ONIGENC_CASE_ASCII_ONLY) && (enc==rb_utf8_encoding() || rb_enc_mbmaxlen(enc)==1))
|| (!(flags&ONIGENC_CASE_FOLD_TURKISH_AZERI) && ENC_CODERANGE(str)==ENC_CODERANGE_7BIT)) {
char *s = RSTRING_PTR(str), *send = RSTRING_END(str);
@@ -6056,7 +6697,7 @@ rb_str_downcase_bang(int argc, VALUE *argv, VALUE str)
str_modify_keep_cr(str);
enc = STR_ENC_GET(str);
rb_str_check_dummy_enc(enc);
- if ((flags&ONIGENC_CASE_ASCII_ONLY) && (enc==rb_utf8_encoding() || rb_enc_mbmaxlen(enc)==1)
+ if (((flags&ONIGENC_CASE_ASCII_ONLY) && (enc==rb_utf8_encoding() || rb_enc_mbmaxlen(enc)==1))
|| (!(flags&ONIGENC_CASE_FOLD_TURKISH_AZERI) && ENC_CODERANGE(str)==ENC_CODERANGE_7BIT)) {
char *s = RSTRING_PTR(str), *send = RSTRING_END(str);
@@ -6103,7 +6744,7 @@ rb_str_downcase_bang(int argc, VALUE *argv, VALUE str)
* This option cannot be combined with any other option.
* :turkic ::
* Full Unicode case mapping, adapted for Turkic languages
- * (Turkish, Aserbaijani,...). This means that upper case I is mapped to
+ * (Turkish, Azerbaijani, ...). This means that upper case I is mapped to
* lower case dotless i, and so on.
* :lithuanian ::
* Currently, just full Unicode case mapping. In the future, full Unicode
@@ -6113,7 +6754,7 @@ rb_str_downcase_bang(int argc, VALUE *argv, VALUE str)
* Only available on +downcase+ and +downcase!+. Unicode case <b>folding</b>,
* which is more far-reaching than Unicode case mapping.
* This option currently cannot be combined with any other option
- * (i.e. there is currenty no variant for turkic languages).
+ * (i.e. there is currently no variant for turkic languages).
*
* Please note that several assumptions that are valid for ASCII-only case
* conversions do not hold for more general case conversions. For example,
@@ -6146,6 +6787,8 @@ rb_str_downcase(int argc, VALUE *argv, VALUE str)
*
* Modifies <i>str</i> by converting the first character to uppercase and the
* remainder to lowercase. Returns <code>nil</code> if no changes are made.
+ * There is an exception for modern Georgian (mkhedruli/MTAVRULI), where
+ * the result is the same as for String#downcase, to avoid mixed case.
*
* See String#downcase for meaning of +options+ and use with different encodings.
*
@@ -6327,7 +6970,7 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag)
int cflag = 0;
unsigned int c, c0, last = 0;
int modify = 0, i, l;
- char *s, *send;
+ unsigned char *s, *send;
VALUE hash = 0;
int singlebyte = single_byte_optimizable(str);
int termlen;
@@ -6408,21 +7051,21 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag)
}
}
- if (cr == ENC_CODERANGE_VALID)
+ if (cr == ENC_CODERANGE_VALID && rb_enc_asciicompat(e1))
cr = ENC_CODERANGE_7BIT;
str_modify_keep_cr(str);
- s = RSTRING_PTR(str); send = RSTRING_END(str);
+ s = (unsigned char *)RSTRING_PTR(str); send = (unsigned char *)RSTRING_END(str);
termlen = rb_enc_mbminlen(enc);
if (sflag) {
int clen, tlen;
long offset, max = RSTRING_LEN(str);
unsigned int save = -1;
- char *buf = ALLOC_N(char, max + termlen), *t = buf;
+ unsigned char *buf = ALLOC_N(unsigned char, max + termlen), *t = buf;
while (s < send) {
int may_modify = 0;
- c0 = c = rb_enc_codepoint_len(s, send, &clen, e1);
+ c0 = c = rb_enc_codepoint_len((char *)s, (char *)send, &clen, e1);
tlen = enc == e1 ? clen : rb_enc_codelen(c, enc);
s += clen;
@@ -6456,8 +7099,9 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag)
if (enc != e1) may_modify = 1;
}
if ((offset = t - buf) + tlen > max) {
+ size_t MAYBE_UNUSED(old) = max + termlen;
max = offset + tlen + (send - s);
- REALLOC_N(buf, char, max + termlen);
+ SIZED_REALLOC_N(buf, unsigned char, max + termlen, old);
t = buf + offset;
}
rb_enc_mbcput(c, t, enc);
@@ -6470,8 +7114,8 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag)
if (!STR_EMBED_P(str)) {
ruby_sized_xfree(STR_HEAP_PTR(str), STR_HEAP_SIZE(str));
}
- TERM_FILL(t, termlen);
- RSTRING(str)->as.heap.ptr = buf;
+ TERM_FILL((char *)t, termlen);
+ RSTRING(str)->as.heap.ptr = (char *)buf;
RSTRING(str)->as.heap.len = t - buf;
STR_SET_NOEMBED(str);
RSTRING(str)->as.heap.aux.capa = max;
@@ -6497,11 +7141,11 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag)
else {
int clen, tlen;
long offset, max = (long)((send - s) * 1.2);
- char *buf = ALLOC_N(char, max + termlen), *t = buf;
+ unsigned char *buf = ALLOC_N(unsigned char, max + termlen), *t = buf;
while (s < send) {
int may_modify = 0;
- c0 = c = rb_enc_codepoint_len(s, send, &clen, e1);
+ c0 = c = rb_enc_codepoint_len((char *)s, (char *)send, &clen, e1);
tlen = enc == e1 ? clen : rb_enc_codelen(c, enc);
if (c < 256) {
@@ -6528,8 +7172,9 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag)
if (enc != e1) may_modify = 1;
}
if ((offset = t - buf) + tlen > max) {
+ size_t MAYBE_UNUSED(old) = max + termlen;
max = offset + tlen + (long)((send - s) * 1.2);
- REALLOC_N(buf, char, max + termlen);
+ SIZED_REALLOC_N(buf, unsigned char, max + termlen, old);
t = buf + offset;
}
if (s != t) {
@@ -6545,8 +7190,8 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag)
if (!STR_EMBED_P(str)) {
ruby_sized_xfree(STR_HEAP_PTR(str), STR_HEAP_SIZE(str));
}
- TERM_FILL(t, termlen);
- RSTRING(str)->as.heap.ptr = buf;
+ TERM_FILL((char *)t, termlen);
+ RSTRING(str)->as.heap.ptr = (char *)buf;
RSTRING(str)->as.heap.len = t - buf;
STR_SET_NOEMBED(str);
RSTRING(str)->as.heap.aux.capa = max;
@@ -6815,7 +7460,7 @@ rb_str_squeeze_bang(int argc, VALUE *argv, VALUE str)
char squeez[TR_TABLE_SIZE];
rb_encoding *enc = 0;
VALUE del = 0, nodel = 0;
- char *s, *send, *t;
+ unsigned char *s, *send, *t;
int i, modify = 0;
int ascompat, singlebyte = single_byte_optimizable(str);
unsigned int save;
@@ -6836,15 +7481,15 @@ rb_str_squeeze_bang(int argc, VALUE *argv, VALUE str)
}
str_modify_keep_cr(str);
- s = t = RSTRING_PTR(str);
+ s = t = (unsigned char *)RSTRING_PTR(str);
if (!s || RSTRING_LEN(str) == 0) return Qnil;
- send = RSTRING_END(str);
+ send = (unsigned char *)RSTRING_END(str);
save = -1;
ascompat = rb_enc_asciicompat(enc);
if (singlebyte) {
while (s < send) {
- unsigned int c = *(unsigned char*)s++;
+ unsigned int c = *s++;
if (c != save || (argc > 0 && !squeez[c])) {
*t++ = save = c;
}
@@ -6855,14 +7500,14 @@ rb_str_squeeze_bang(int argc, VALUE *argv, VALUE str)
unsigned int c;
int clen;
- if (ascompat && (c = *(unsigned char*)s) < 0x80) {
+ if (ascompat && (c = *s) < 0x80) {
if (c != save || (argc > 0 && !squeez[c])) {
*t++ = save = c;
}
s++;
}
else {
- c = rb_enc_codepoint_len(s, send, &clen, enc);
+ c = rb_enc_codepoint_len((char *)s, (char *)send, &clen, enc);
if (c != save || (argc > 0 && !tr_find(c, squeez, del, nodel))) {
if (t != s) rb_enc_mbcput(c, t, enc);
@@ -6874,9 +7519,9 @@ rb_str_squeeze_bang(int argc, VALUE *argv, VALUE str)
}
}
- TERM_FILL(t, TERM_LEN(str));
- if (t - RSTRING_PTR(str) != RSTRING_LEN(str)) {
- STR_SET_LEN(str, t - RSTRING_PTR(str));
+ TERM_FILL((char *)t, TERM_LEN(str));
+ if ((char *)t - RSTRING_PTR(str) != RSTRING_LEN(str)) {
+ STR_SET_LEN(str, (char *)t - RSTRING_PTR(str));
modify = 1;
}
@@ -7074,17 +7719,47 @@ static const char isspacetable[256] = {
#define ascii_isspace(c) isspacetable[(unsigned char)(c)]
+static long
+split_string(VALUE result, VALUE str, long beg, long len, long empty_count)
+{
+ if (empty_count >= 0 && len == 0) {
+ return empty_count + 1;
+ }
+ if (empty_count > 0) {
+ /* make different substrings */
+ if (result) {
+ do {
+ rb_ary_push(result, str_new_empty(str));
+ } while (--empty_count > 0);
+ }
+ else {
+ do {
+ rb_yield(str_new_empty(str));
+ } while (--empty_count > 0);
+ }
+ }
+ str = rb_str_subseq(str, beg, len);
+ if (result) {
+ rb_ary_push(result, str);
+ }
+ else {
+ rb_yield(str);
+ }
+ return empty_count;
+}
+
/*
* call-seq:
- * str.split(pattern=nil, [limit]) -> anArray
+ * str.split(pattern=nil, [limit]) -> an_array
+ * str.split(pattern=nil, [limit]) {|sub| block } -> str
*
* Divides <i>str</i> into substrings based on a delimiter, returning an array
* of these substrings.
*
* If <i>pattern</i> is a <code>String</code>, then its contents are used as
* the delimiter when splitting <i>str</i>. If <i>pattern</i> is a single
- * space, <i>str</i> is split on whitespace, with leading whitespace and runs
- * of contiguous whitespace characters ignored.
+ * space, <i>str</i> is split on whitespace, with leading and trailing
+ * whitespace and runs of contiguous whitespace characters ignored.
*
* If <i>pattern</i> is a <code>Regexp</code>, <i>str</i> is divided where the
* pattern matches. Whenever the pattern matches a zero-length string,
@@ -7096,17 +7771,19 @@ static const char isspacetable[256] = {
* split on whitespace as if ' ' were specified.
*
* If the <i>limit</i> parameter is omitted, trailing null fields are
- * suppressed. If <i>limit</i> is a positive number, at most that number of
- * fields will be returned (if <i>limit</i> is <code>1</code>, the entire
- * string is returned as the only entry in an array). If negative, there is no
+ * suppressed. If <i>limit</i> is a positive number, at most that number
+ * of split substrings will be returned (captured groups will be returned
+ * as well, but are not counted towards the limit).
+ * If <i>limit</i> is <code>1</code>, the entire
+ * string is returned as the only entry in an array. If negative, there is no
* limit to the number of fields returned, and trailing null fields are not
* suppressed.
*
* When the input +str+ is empty an empty Array is returned as the string is
* considered to have no fields to split.
*
- * " now's the time".split #=> ["now's", "the", "time"]
- * " now's the time".split(' ') #=> ["now's", "the", "time"]
+ * " now's the time ".split #=> ["now's", "the", "time"]
+ * " now's the time ".split(' ') #=> ["now's", "the", "time"]
* " now's the time".split(/ /) #=> ["", "now's", "", "the", "time"]
* "1, 2.34,56, 7".split(%r{,\s*}) #=> ["1", "2.34", "56", "7"]
* "hello".split(//) #=> ["h", "e", "l", "l", "o"]
@@ -7118,7 +7795,12 @@ static const char isspacetable[256] = {
* "1,2,,3,4,,".split(',', 4) #=> ["1", "2", "", "3,4,,"]
* "1,2,,3,4,,".split(',', -4) #=> ["1", "2", "", "3", "4", "", ""]
*
+ * "1:2:3".split(/(:)()()/, 2) #=> ["1", ":", "", "", "2:3"]
+ *
* "".split(',', -1) #=> []
+ *
+ * If a block is given, invoke the block with each split substring.
+ *
*/
static VALUE
@@ -7128,20 +7810,27 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str)
VALUE spat;
VALUE limit;
enum {awk, string, regexp} split_type;
- long beg, end, i = 0;
+ long beg, end, i = 0, empty_count = -1;
int lim = 0;
VALUE result, tmp;
+ result = rb_block_given_p() ? Qfalse : Qnil;
if (rb_scan_args(argc, argv, "02", &spat, &limit) == 2) {
lim = NUM2INT(limit);
if (lim <= 0) limit = Qnil;
else if (lim == 1) {
if (RSTRING_LEN(str) == 0)
- return rb_ary_new2(0);
- return rb_ary_new3(1, str);
+ return result ? rb_ary_new2(0) : str;
+ tmp = rb_str_dup(str);
+ if (!result) {
+ rb_yield(tmp);
+ return str;
+ }
+ return rb_ary_new3(1, tmp);
}
i = 1;
}
+ if (NIL_P(limit) && !lim) empty_count = 0;
enc = STR_ENC_GET(str);
split_type = regexp;
@@ -7180,7 +7869,9 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str)
}
}
- result = rb_ary_new();
+#define SPLIT_STR(beg, len) (empty_count = split_string(result, str, beg, len, empty_count))
+
+ if (result) result = rb_ary_new();
beg = 0;
if (split_type == awk) {
char *ptr = RSTRING_PTR(str);
@@ -7204,7 +7895,7 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str)
}
}
else if (ascii_isspace(c)) {
- rb_ary_push(result, rb_str_subseq(str, beg, end-beg));
+ SPLIT_STR(beg, end-beg);
skip = 1;
beg = ptr - bptr;
if (!NIL_P(limit)) ++i;
@@ -7231,7 +7922,7 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str)
}
}
else if (rb_isspace(c)) {
- rb_ary_push(result, rb_str_subseq(str, beg, end-beg));
+ SPLIT_STR(beg, end-beg);
skip = 1;
beg = ptr - bptr;
if (!NIL_P(limit)) ++i;
@@ -7244,7 +7935,8 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str)
}
else if (split_type == string) {
char *ptr = RSTRING_PTR(str);
- char *temp = ptr;
+ char *str_start = ptr;
+ char *substr_start = ptr;
char *eptr = RSTRING_END(str);
char *sptr = RSTRING_PTR(spat);
long slen = RSTRING_LEN(spat);
@@ -7259,11 +7951,12 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str)
ptr = t;
continue;
}
- rb_ary_push(result, rb_str_subseq(str, ptr - temp, end));
+ SPLIT_STR(substr_start - str_start, (ptr+end) - substr_start);
ptr += end + slen;
+ substr_start = ptr;
if (!NIL_P(limit) && lim <= ++i) break;
}
- beg = ptr - temp;
+ beg = ptr - str_start;
}
else {
char *ptr = RSTRING_PTR(str);
@@ -7272,19 +7965,20 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str)
long idx;
int last_null = 0;
struct re_registers *regs;
+ VALUE match = 0;
- while ((end = rb_reg_search(spat, str, start, 0)) >= 0) {
- regs = RMATCH_REGS(rb_backref_get());
+ for (; (end = rb_reg_search(spat, str, start, 0)) >= 0;
+ (match ? (rb_match_unbusy(match), rb_backref_set(match)) : (void)0)) {
+ match = rb_backref_get();
+ if (!result) rb_match_busy(match);
+ regs = RMATCH_REGS(match);
if (start == end && BEG(0) == END(0)) {
if (!ptr) {
- rb_ary_push(result, str_new_empty(str));
+ SPLIT_STR(0, 0);
break;
}
else if (last_null == 1) {
- rb_ary_push(result, rb_str_subseq(str, beg,
- rb_enc_fast_mbclen(ptr+beg,
- ptr+len,
- enc)));
+ SPLIT_STR(beg, rb_enc_fast_mbclen(ptr+beg, ptr+len, enc));
beg = start;
}
else {
@@ -7297,37 +7991,24 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str)
}
}
else {
- rb_ary_push(result, rb_str_subseq(str, beg, end-beg));
+ SPLIT_STR(beg, end-beg);
beg = start = END(0);
}
last_null = 0;
for (idx=1; idx < regs->num_regs; idx++) {
if (BEG(idx) == -1) continue;
- if (BEG(idx) == END(idx))
- tmp = str_new_empty(str);
- else
- tmp = rb_str_subseq(str, BEG(idx), END(idx)-BEG(idx));
- rb_ary_push(result, tmp);
+ SPLIT_STR(BEG(idx), END(idx)-BEG(idx));
}
if (!NIL_P(limit) && lim <= ++i) break;
}
+ if (match) rb_match_unbusy(match);
}
if (RSTRING_LEN(str) > 0 && (!NIL_P(limit) || RSTRING_LEN(str) > beg || lim < 0)) {
- if (RSTRING_LEN(str) == beg)
- tmp = str_new_empty(str);
- else
- tmp = rb_str_subseq(str, beg, RSTRING_LEN(str)-beg);
- rb_ary_push(result, tmp);
- }
- if (NIL_P(limit) && lim == 0) {
- long len;
- while ((len = RARRAY_LEN(result)) > 0 &&
- (tmp = RARRAY_AREF(result, len-1), RSTRING_LEN(tmp) == 0))
- rb_ary_pop(result);
+ SPLIT_STR(beg, RSTRING_LEN(str)-beg);
}
- return result;
+ return result ? result : str;
}
VALUE
@@ -7340,52 +8021,81 @@ rb_str_split(VALUE str, const char *sep0)
return rb_str_split_m(1, &sep, str);
}
+static int
+enumerator_wantarray(const char *method)
+{
+ if (rb_block_given_p()) {
+#if STRING_ENUMERATORS_WANTARRAY
+ rb_warn("given block not used");
+#else
+ rb_warning("passing a block to String#%s is deprecated", method);
+ return 0;
+#endif
+ }
+ return 1;
+}
+
+#define WANTARRAY(m, size) \
+ (enumerator_wantarray(m) ? rb_ary_new_capa(size) : 0)
+
+static inline int
+enumerator_element(VALUE ary, VALUE e)
+{
+ if (ary) {
+ rb_ary_push(ary, e);
+ return 0;
+ }
+ else {
+ rb_yield(e);
+ return 1;
+ }
+}
+
+#define ENUM_ELEM(ary, e) enumerator_element(ary, e)
+
+static const char *
+chomp_newline(const char *p, const char *e, rb_encoding *enc)
+{
+ const char *prev = rb_enc_prev_char(p, e, e, enc);
+ if (rb_enc_is_newline(prev, e, enc)) {
+ e = prev;
+ prev = rb_enc_prev_char(p, e, e, enc);
+ if (prev && rb_enc_ascget(prev, e, NULL, enc) == '\r')
+ e = prev;
+ }
+ return e;
+}
static VALUE
-rb_str_enumerate_lines(int argc, VALUE *argv, VALUE str, int wantarray)
+rb_str_enumerate_lines(int argc, VALUE *argv, VALUE str, VALUE ary)
{
rb_encoding *enc;
- VALUE line, rs, orig = str;
+ VALUE line, rs, orig = str, opts = Qnil, chomp = Qfalse;
const char *ptr, *pend, *subptr, *subend, *rsptr, *hit, *adjusted;
long pos, len, rslen;
- int paragraph_mode = 0;
+ int rsnewline = 0;
- VALUE MAYBE_UNUSED(ary);
-
- if (argc == 0)
+ if (rb_scan_args(argc, argv, "01:", &rs, &opts) == 0)
rs = rb_rs;
- else
- rb_scan_args(argc, argv, "01", &rs);
-
- if (rb_block_given_p()) {
- if (wantarray) {
-#if STRING_ENUMERATORS_WANTARRAY
- rb_warn("given block not used");
- ary = rb_ary_new();
-#else
- rb_warning("passing a block to String#lines is deprecated");
- wantarray = 0;
-#endif
+ if (!NIL_P(opts)) {
+ static ID keywords[1];
+ if (!keywords[0]) {
+ keywords[0] = rb_intern_const("chomp");
}
- }
- else {
- if (wantarray)
- ary = rb_ary_new();
- else
- return SIZED_ENUMERATOR(str, argc, argv, 0);
+ rb_get_kwargs(opts, keywords, 0, 1, &chomp);
+ chomp = (chomp != Qundef && RTEST(chomp));
}
if (NIL_P(rs)) {
- if (wantarray) {
- rb_ary_push(ary, str);
+ if (!ENUM_ELEM(ary, str)) {
return ary;
}
else {
- rb_yield(str);
return orig;
}
}
+ if (!RSTRING_LEN(str)) goto end;
str = rb_str_new_frozen(str);
ptr = subptr = RSTRING_PTR(str);
pend = RSTRING_END(str);
@@ -7399,15 +8109,45 @@ rb_str_enumerate_lines(int argc, VALUE *argv, VALUE str, int wantarray)
enc = rb_enc_check(str, rs);
if (rslen == 0) {
- rsptr = "\n\n";
- rslen = 2;
- paragraph_mode = 1;
+ /* paragraph mode */
+ int n;
+ const char *eol = NULL;
+ subend = subptr;
+ while (subend < pend) {
+ do {
+ if (rb_enc_ascget(subend, pend, &n, enc) != '\r')
+ n = 0;
+ rslen = n + rb_enc_mbclen(subend + n, pend, enc);
+ if (rb_enc_is_newline(subend + n, pend, enc)) {
+ if (eol == subend) break;
+ subend += rslen;
+ if (subptr) eol = subend;
+ }
+ else {
+ if (!subptr) subptr = subend;
+ subend += rslen;
+ }
+ rslen = 0;
+ } while (subend < pend);
+ if (!subptr) break;
+ line = rb_str_subseq(str, subptr - ptr,
+ subend - subptr + (chomp ? 0 : rslen));
+ if (ENUM_ELEM(ary, line)) {
+ str_mod_check(str, ptr, len);
+ }
+ subptr = eol = NULL;
+ }
+ goto end;
}
else {
rsptr = RSTRING_PTR(rs);
+ if (RSTRING_LEN(rs) == rb_enc_mbminlen(enc) &&
+ rb_enc_is_newline(rsptr, rsptr + RSTRING_LEN(rs), enc)) {
+ rsnewline = 1;
+ }
}
- if ((rs == rb_default_rs || paragraph_mode) && !rb_enc_asciicompat(enc)) {
+ if ((rs == rb_default_rs) && !rb_enc_asciicompat(enc)) {
rs = rb_str_new(rsptr, rslen);
rs = rb_str_encode(rs, rb_enc_from_encoding(enc), 0, Qnil);
rsptr = RSTRING_PTR(rs);
@@ -7423,33 +8163,39 @@ rb_str_enumerate_lines(int argc, VALUE *argv, VALUE str, int wantarray)
subptr = adjusted;
continue;
}
- subend = hit + rslen;
- if (paragraph_mode) {
- while (subend < pend && rb_enc_is_newline(subend, pend, enc)) {
- subend += rb_enc_mbclen(subend, pend, enc);
+ subend = hit += rslen;
+ if (chomp) {
+ if (rsnewline) {
+ subend = chomp_newline(subptr, subend, enc);
+ }
+ else {
+ subend -= rslen;
}
}
line = rb_str_subseq(str, subptr - ptr, subend - subptr);
- if (wantarray) {
- rb_ary_push(ary, line);
- }
- else {
- rb_yield(line);
+ if (ENUM_ELEM(ary, line)) {
str_mod_check(str, ptr, len);
}
- subptr = subend;
+ subptr = hit;
}
if (subptr != pend) {
+ if (chomp) {
+ if (rsnewline) {
+ pend = chomp_newline(subptr, pend, enc);
+ }
+ else if (pend - subptr >= rslen &&
+ memcmp(pend - rslen, rsptr, rslen) == 0) {
+ pend -= rslen;
+ }
+ }
line = rb_str_subseq(str, subptr - ptr, pend - subptr);
- if (wantarray)
- rb_ary_push(ary, line);
- else
- rb_yield(line);
+ ENUM_ELEM(ary, line);
RB_GC_GUARD(str);
}
- if (wantarray)
+ end:
+ if (ary)
return ary;
else
return orig;
@@ -7457,8 +8203,8 @@ rb_str_enumerate_lines(int argc, VALUE *argv, VALUE str, int wantarray)
/*
* call-seq:
- * str.each_line(separator=$/) {|substr| block } -> str
- * str.each_line(separator=$/) -> an_enumerator
+ * str.each_line(separator=$/ [, getline_args]) {|substr| block } -> str
+ * str.each_line(separator=$/ [, getline_args]) -> an_enumerator
*
* Splits <i>str</i> using the supplied parameter as the record
* separator (<code>$/</code> by default), passing each substring in
@@ -7466,6 +8212,8 @@ rb_str_enumerate_lines(int argc, VALUE *argv, VALUE str, int wantarray)
* supplied, the string is split into paragraphs delimited by
* multiple successive newlines.
*
+ * See IO.readlines for details about getline_args.
+ *
* If no block is given, an enumerator is returned instead.
*
* print "Example one\n"
@@ -7486,23 +8234,30 @@ rb_str_enumerate_lines(int argc, VALUE *argv, VALUE str, int wantarray)
* "o\nworl"
* "d"
* Example three
- * "hello\n\n\n"
+ * "hello\n\n"
* "world"
*/
static VALUE
rb_str_each_line(int argc, VALUE *argv, VALUE str)
{
+ RETURN_SIZED_ENUMERATOR(str, argc, argv, 0);
return rb_str_enumerate_lines(argc, argv, str, 0);
}
/*
* call-seq:
- * str.lines(separator=$/) -> an_array
+ * str.lines(separator=$/ [, getline_args]) -> an_array
*
* Returns an array of lines in <i>str</i> split using the supplied
* record separator (<code>$/</code> by default). This is a
- * shorthand for <code>str.each_line(separator).to_a</code>.
+ * shorthand for <code>str.each_line(separator, getline_args).to_a</code>.
+ *
+ * See IO.readlines for details about getline_args.
+ *
+ * "hello\nworld\n".lines #=> ["hello\n", "world\n"]
+ * "hello world".lines(' ') #=> ["hello ", " ", "world"]
+ * "hello\nworld\n".lines(chomp: true) #=> ["hello", "world"]
*
* If a block is given, which is a deprecated form, works the same as
* <code>each_line</code>.
@@ -7511,7 +8266,8 @@ rb_str_each_line(int argc, VALUE *argv, VALUE str)
static VALUE
rb_str_lines(int argc, VALUE *argv, VALUE str)
{
- return rb_str_enumerate_lines(argc, argv, str, 1);
+ VALUE ary = WANTARRAY("lines", 0);
+ return rb_str_enumerate_lines(argc, argv, str, ary);
}
static VALUE
@@ -7521,36 +8277,14 @@ rb_str_each_byte_size(VALUE str, VALUE args, VALUE eobj)
}
static VALUE
-rb_str_enumerate_bytes(VALUE str, int wantarray)
+rb_str_enumerate_bytes(VALUE str, VALUE ary)
{
long i;
- VALUE MAYBE_UNUSED(ary);
-
- if (rb_block_given_p()) {
- if (wantarray) {
-#if STRING_ENUMERATORS_WANTARRAY
- rb_warn("given block not used");
- ary = rb_ary_new();
-#else
- rb_warning("passing a block to String#bytes is deprecated");
- wantarray = 0;
-#endif
- }
- }
- else {
- if (wantarray)
- ary = rb_ary_new2(RSTRING_LEN(str));
- else
- return SIZED_ENUMERATOR(str, 0, 0, rb_str_each_byte_size);
- }
for (i=0; i<RSTRING_LEN(str); i++) {
- if (wantarray)
- rb_ary_push(ary, INT2FIX(RSTRING_PTR(str)[i] & 0xff));
- else
- rb_yield(INT2FIX(RSTRING_PTR(str)[i] & 0xff));
+ ENUM_ELEM(ary, INT2FIX(RSTRING_PTR(str)[i] & 0xff));
}
- if (wantarray)
+ if (ary)
return ary;
else
return str;
@@ -7574,6 +8308,7 @@ rb_str_enumerate_bytes(VALUE str, int wantarray)
static VALUE
rb_str_each_byte(VALUE str)
{
+ RETURN_SIZED_ENUMERATOR(str, 0, 0, rb_str_each_byte_size);
return rb_str_enumerate_bytes(str, 0);
}
@@ -7591,7 +8326,8 @@ rb_str_each_byte(VALUE str)
static VALUE
rb_str_bytes(VALUE str)
{
- return rb_str_enumerate_bytes(str, 1);
+ VALUE ary = WANTARRAY("bytes", RSTRING_LEN(str));
+ return rb_str_enumerate_bytes(str, ary);
}
static VALUE
@@ -7601,60 +8337,32 @@ rb_str_each_char_size(VALUE str, VALUE args, VALUE eobj)
}
static VALUE
-rb_str_enumerate_chars(VALUE str, int wantarray)
+rb_str_enumerate_chars(VALUE str, VALUE ary)
{
VALUE orig = str;
- VALUE substr;
long i, len, n;
const char *ptr;
rb_encoding *enc;
- VALUE MAYBE_UNUSED(ary);
str = rb_str_new_frozen(str);
ptr = RSTRING_PTR(str);
len = RSTRING_LEN(str);
enc = rb_enc_get(str);
- if (rb_block_given_p()) {
- if (wantarray) {
-#if STRING_ENUMERATORS_WANTARRAY
- rb_warn("given block not used");
- ary = rb_ary_new_capa(str_strlen(str, enc)); /* str's enc*/
-#else
- rb_warning("passing a block to String#chars is deprecated");
- wantarray = 0;
-#endif
- }
- }
- else {
- if (wantarray)
- ary = rb_ary_new_capa(str_strlen(str, enc)); /* str's enc*/
- else
- return SIZED_ENUMERATOR(str, 0, 0, rb_str_each_char_size);
- }
-
if (ENC_CODERANGE_CLEAN_P(ENC_CODERANGE(str))) {
for (i = 0; i < len; i += n) {
n = rb_enc_fast_mbclen(ptr + i, ptr + len, enc);
- substr = rb_str_subseq(str, i, n);
- if (wantarray)
- rb_ary_push(ary, substr);
- else
- rb_yield(substr);
+ ENUM_ELEM(ary, rb_str_subseq(str, i, n));
}
}
else {
for (i = 0; i < len; i += n) {
n = rb_enc_mbclen(ptr + i, ptr + len, enc);
- substr = rb_str_subseq(str, i, n);
- if (wantarray)
- rb_ary_push(ary, substr);
- else
- rb_yield(substr);
+ ENUM_ELEM(ary, rb_str_subseq(str, i, n));
}
}
RB_GC_GUARD(str);
- if (wantarray)
+ if (ary)
return ary;
else
return orig;
@@ -7678,6 +8386,7 @@ rb_str_enumerate_chars(VALUE str, int wantarray)
static VALUE
rb_str_each_char(VALUE str)
{
+ RETURN_SIZED_ENUMERATOR(str, 0, 0, rb_str_each_char_size);
return rb_str_enumerate_chars(str, 0);
}
@@ -7695,56 +8404,34 @@ rb_str_each_char(VALUE str)
static VALUE
rb_str_chars(VALUE str)
{
- return rb_str_enumerate_chars(str, 1);
+ VALUE ary = WANTARRAY("chars", rb_str_strlen(str));
+ return rb_str_enumerate_chars(str, ary);
}
-
static VALUE
-rb_str_enumerate_codepoints(VALUE str, int wantarray)
+rb_str_enumerate_codepoints(VALUE str, VALUE ary)
{
VALUE orig = str;
int n;
unsigned int c;
const char *ptr, *end;
rb_encoding *enc;
- VALUE MAYBE_UNUSED(ary);
if (single_byte_optimizable(str))
- return rb_str_enumerate_bytes(str, wantarray);
+ return rb_str_enumerate_bytes(str, ary);
str = rb_str_new_frozen(str);
ptr = RSTRING_PTR(str);
end = RSTRING_END(str);
enc = STR_ENC_GET(str);
- if (rb_block_given_p()) {
- if (wantarray) {
-#if STRING_ENUMERATORS_WANTARRAY
- rb_warn("given block not used");
- ary = rb_ary_new_capa(str_strlen(str, enc)); /* str's enc*/
-#else
- rb_warning("passing a block to String#codepoints is deprecated");
- wantarray = 0;
-#endif
- }
- }
- else {
- if (wantarray)
- ary = rb_ary_new_capa(str_strlen(str, enc)); /* str's enc*/
- else
- return SIZED_ENUMERATOR(str, 0, 0, rb_str_each_char_size);
- }
-
while (ptr < end) {
c = rb_enc_codepoint_len(ptr, end, &n, enc);
- if (wantarray)
- rb_ary_push(ary, UINT2NUM(c));
- else
- rb_yield(UINT2NUM(c));
+ ENUM_ELEM(ary, UINT2NUM(c));
ptr += n;
}
RB_GC_GUARD(str);
- if (wantarray)
+ if (ary)
return ary;
else
return orig;
@@ -7757,7 +8444,9 @@ rb_str_enumerate_codepoints(VALUE str, int wantarray)
*
* Passes the <code>Integer</code> ordinal of each character in <i>str</i>,
* also known as a <i>codepoint</i> when applied to Unicode strings to the
- * given block.
+ * given block. For encodings other than UTF-8/UTF-16(BE|LE)/UTF-32(BE|LE),
+ * values are directly derived from the binary representation
+ * of each character.
*
* If no block is given, an enumerator is returned instead.
*
@@ -7771,6 +8460,7 @@ rb_str_enumerate_codepoints(VALUE str, int wantarray)
static VALUE
rb_str_each_codepoint(VALUE str)
{
+ RETURN_SIZED_ENUMERATOR(str, 0, 0, rb_str_each_char_size);
return rb_str_enumerate_codepoints(str, 0);
}
@@ -7789,9 +8479,158 @@ rb_str_each_codepoint(VALUE str)
static VALUE
rb_str_codepoints(VALUE str)
{
- return rb_str_enumerate_codepoints(str, 1);
+ VALUE ary = WANTARRAY("codepoints", rb_str_strlen(str));
+ return rb_str_enumerate_codepoints(str, ary);
+}
+
+static regex_t *
+get_reg_grapheme_cluster(rb_encoding *enc)
+{
+ int encidx = rb_enc_to_index(enc);
+ regex_t *reg_grapheme_cluster = NULL;
+ static regex_t *reg_grapheme_cluster_utf8 = NULL;
+
+ /* synchronize */
+ if (encidx == rb_utf8_encindex() && reg_grapheme_cluster_utf8) {
+ reg_grapheme_cluster = reg_grapheme_cluster_utf8;
+ }
+ if (!reg_grapheme_cluster) {
+ const OnigUChar source_ascii[] = "\\X";
+ OnigErrorInfo einfo;
+ const OnigUChar *source = source_ascii;
+ size_t source_len = sizeof(source_ascii) - 1;
+ switch (encidx) {
+#define CHARS_16BE(x) (OnigUChar)((x)>>8), (OnigUChar)(x)
+#define CHARS_16LE(x) (OnigUChar)(x), (OnigUChar)((x)>>8)
+#define CHARS_32BE(x) CHARS_16BE((x)>>16), CHARS_16BE(x)
+#define CHARS_32LE(x) CHARS_16LE(x), CHARS_16LE((x)>>16)
+#define CASE_UTF(e) \
+ case ENCINDEX_UTF_##e: { \
+ static const OnigUChar source_UTF_##e[] = {CHARS_##e('\\'), CHARS_##e('X')}; \
+ source = source_UTF_##e; \
+ source_len = sizeof(source_UTF_##e); \
+ break; \
+ }
+ CASE_UTF(16BE); CASE_UTF(16LE); CASE_UTF(32BE); CASE_UTF(32LE);
+#undef CASE_UTF
+#undef CHARS_16BE
+#undef CHARS_16LE
+#undef CHARS_32BE
+#undef CHARS_32LE
+ }
+ int r = onig_new(&reg_grapheme_cluster, source, source + source_len,
+ ONIG_OPTION_DEFAULT, enc, OnigDefaultSyntax, &einfo);
+ if (r) {
+ UChar message[ONIG_MAX_ERROR_MESSAGE_LEN];
+ onig_error_code_to_str(message, r, &einfo);
+ rb_fatal("cannot compile grapheme cluster regexp: %s", (char *)message);
+ }
+ if (encidx == rb_utf8_encindex()) {
+ reg_grapheme_cluster_utf8 = reg_grapheme_cluster;
+ }
+ }
+ return reg_grapheme_cluster;
+}
+
+static VALUE
+rb_str_each_grapheme_cluster_size(VALUE str, VALUE args, VALUE eobj)
+{
+ size_t grapheme_cluster_count = 0;
+ regex_t *reg_grapheme_cluster = NULL;
+ rb_encoding *enc = rb_enc_from_index(ENCODING_GET(str));
+ const char *ptr, *end;
+
+ if (!rb_enc_unicode_p(enc)) {
+ return rb_str_length(str);
+ }
+
+ reg_grapheme_cluster = get_reg_grapheme_cluster(enc);
+ ptr = RSTRING_PTR(str);
+ end = RSTRING_END(str);
+
+ while (ptr < end) {
+ OnigPosition len = onig_match(reg_grapheme_cluster,
+ (const OnigUChar *)ptr, (const OnigUChar *)end,
+ (const OnigUChar *)ptr, NULL, 0);
+ if (len <= 0) break;
+ grapheme_cluster_count++;
+ ptr += len;
+ }
+
+ return SIZET2NUM(grapheme_cluster_count);
}
+static VALUE
+rb_str_enumerate_grapheme_clusters(VALUE str, VALUE ary)
+{
+ VALUE orig = str;
+ regex_t *reg_grapheme_cluster = NULL;
+ rb_encoding *enc = rb_enc_from_index(ENCODING_GET(str));
+ const char *ptr0, *ptr, *end;
+
+ if (!rb_enc_unicode_p(enc)) {
+ return rb_str_enumerate_chars(str, ary);
+ }
+
+ if (!ary) str = rb_str_new_frozen(str);
+ reg_grapheme_cluster = get_reg_grapheme_cluster(enc);
+ ptr0 = ptr = RSTRING_PTR(str);
+ end = RSTRING_END(str);
+
+ while (ptr < end) {
+ OnigPosition len = onig_match(reg_grapheme_cluster,
+ (const OnigUChar *)ptr, (const OnigUChar *)end,
+ (const OnigUChar *)ptr, NULL, 0);
+ if (len <= 0) break;
+ ENUM_ELEM(ary, rb_str_subseq(str, ptr-ptr0, len));
+ ptr += len;
+ }
+ RB_GC_GUARD(str);
+ if (ary)
+ return ary;
+ else
+ return orig;
+}
+
+/*
+ * call-seq:
+ * str.each_grapheme_cluster {|cstr| block } -> str
+ * str.each_grapheme_cluster -> an_enumerator
+ *
+ * Passes each grapheme cluster in <i>str</i> to the given block, or returns
+ * an enumerator if no block is given.
+ * Unlike String#each_char, this enumerates by grapheme clusters defined by
+ * Unicode Standard Annex #29 http://unicode.org/reports/tr29/
+ *
+ * "a\u0300".each_char.to_a.size #=> 2
+ * "a\u0300".each_grapheme_cluster.to_a.size #=> 1
+ *
+ */
+
+static VALUE
+rb_str_each_grapheme_cluster(VALUE str)
+{
+ RETURN_SIZED_ENUMERATOR(str, 0, 0, rb_str_each_grapheme_cluster_size);
+ return rb_str_enumerate_grapheme_clusters(str, 0);
+}
+
+/*
+ * call-seq:
+ * str.grapheme_clusters -> an_array
+ *
+ * Returns an array of grapheme clusters in <i>str</i>. This is a shorthand
+ * for <code>str.each_grapheme_cluster.to_a</code>.
+ *
+ * If a block is given, which is a deprecated form, works the same as
+ * <code>each_grapheme_cluster</code>.
+ */
+
+static VALUE
+rb_str_grapheme_clusters(VALUE str)
+{
+ VALUE ary = WANTARRAY("grapheme_clusters", rb_str_strlen(str));
+ return rb_str_enumerate_grapheme_clusters(str, ary);
+}
static long
chopped_length(VALUE str)
@@ -7961,6 +8800,11 @@ chompped_length(VALUE str, VALUE rs)
return len;
}
+/*!
+ * Returns the separator for arguments of rb_str_chomp.
+ *
+ * @return returns rb_ps ($/) as default, the default value of rb_ps ($/) is "\n".
+ */
static VALUE
chomp_rs(int argc, const VALUE *argv)
{
@@ -7981,6 +8825,7 @@ rb_str_chomp_string(VALUE str, VALUE rs)
long olen = RSTRING_LEN(str);
long len = chompped_length(str, rs);
if (len >= olen) return Qnil;
+ str_modify_keep_cr(str);
STR_SET_LEN(str, len);
TERM_FILL(&RSTRING_PTR(str)[len], TERM_LEN(str));
if (ENC_CODERANGE(str) != ENC_CODERANGE_7BIT) {
@@ -8001,7 +8846,7 @@ static VALUE
rb_str_chomp_bang(int argc, VALUE *argv, VALUE str)
{
VALUE rs;
- str_modify_keep_cr(str);
+ str_modifiable(str);
if (RSTRING_LEN(str) == 0) return Qnil;
rs = chomp_rs(argc, argv);
if (NIL_P(rs)) return Qnil;
@@ -8066,11 +8911,11 @@ lstrip_offset(VALUE str, const char *s, const char *e, rb_encoding *enc)
* call-seq:
* str.lstrip! -> self or nil
*
- * Removes leading whitespace from <i>str</i>, returning <code>nil</code> if no
- * change was made. See also <code>String#rstrip!</code> and
- * <code>String#strip!</code>.
+ * Removes leading whitespace from the receiver.
+ * Returns the altered receiver, or +nil+ if no change was made.
+ * See also String#rstrip! and String#strip!.
*
- * Refer to <code>strip</code> for the definition of whitespace.
+ * Refer to String#strip for the definition of whitespace.
*
* " hello ".lstrip! #=> "hello "
* "hello ".lstrip! #=> nil
@@ -8106,10 +8951,10 @@ rb_str_lstrip_bang(VALUE str)
* call-seq:
* str.lstrip -> new_str
*
- * Returns a copy of <i>str</i> with leading whitespace removed. See also
- * <code>String#rstrip</code> and <code>String#strip</code>.
+ * Returns a copy of the receiver with leading whitespace removed.
+ * See also String#rstrip and String#strip.
*
- * Refer to <code>strip</code> for the definition of whitespace.
+ * Refer to String#strip for the definition of whitespace.
*
* " hello ".lstrip #=> "hello "
* "hello".lstrip #=> "hello"
@@ -8156,11 +9001,11 @@ rstrip_offset(VALUE str, const char *s, const char *e, rb_encoding *enc)
* call-seq:
* str.rstrip! -> self or nil
*
- * Removes trailing whitespace from <i>str</i>, returning <code>nil</code> if
- * no change was made. See also <code>String#lstrip!</code> and
- * <code>String#strip!</code>.
+ * Removes trailing whitespace from the receiver.
+ * Returns the altered receiver, or +nil+ if no change was made.
+ * See also String#lstrip! and String#strip!.
*
- * Refer to <code>strip</code> for the definition of whitespace.
+ * Refer to String#strip for the definition of whitespace.
*
* " hello ".rstrip! #=> " hello"
* " hello".rstrip! #=> nil
@@ -8195,10 +9040,10 @@ rb_str_rstrip_bang(VALUE str)
* call-seq:
* str.rstrip -> new_str
*
- * Returns a copy of <i>str</i> with trailing whitespace removed. See also
- * <code>String#lstrip</code> and <code>String#strip</code>.
+ * Returns a copy of the receiver with trailing whitespace removed.
+ * See also String#lstrip and String#strip.
*
- * Refer to <code>strip</code> for the definition of whitespace.
+ * Refer to String#strip for the definition of whitespace.
*
* " hello ".rstrip #=> " hello"
* "hello".rstrip #=> "hello"
@@ -8222,12 +9067,15 @@ rb_str_rstrip(VALUE str)
/*
* call-seq:
- * str.strip! -> str or nil
+ * str.strip! -> self or nil
+ *
+ * Removes leading and trailing whitespace from the receiver.
+ * Returns the altered receiver, or +nil+ if there was no change.
*
- * Removes leading and trailing whitespace from <i>str</i>. Returns
- * <code>nil</code> if <i>str</i> was not altered.
+ * Refer to String#strip for the definition of whitespace.
*
- * Refer to <code>strip</code> for the definition of whitespace.
+ * " hello ".strip! #=> "hello"
+ * "hello".strip! #=> nil
*/
static VALUE
@@ -8263,7 +9111,7 @@ rb_str_strip_bang(VALUE str)
* call-seq:
* str.strip -> new_str
*
- * Returns a copy of <i>str</i> with leading and trailing whitespace removed.
+ * Returns a copy of the receiver with leading and trailing whitespace removed.
*
* Whitespace is defined as any of the following characters:
* null, horizontal tab, line feed, vertical tab, form feed, carriage return, space.
@@ -8271,6 +9119,7 @@ rb_str_strip_bang(VALUE str)
* " hello ".strip #=> "hello"
* "\tgoodbye\r\n".strip #=> "goodbye"
* "\x00\t\n\v\f\r ".strip #=> ""
+ * "hello".strip #=> "hello"
*/
static VALUE
@@ -8289,35 +9138,50 @@ rb_str_strip(VALUE str)
}
static VALUE
-scan_once(VALUE str, VALUE pat, long *start)
+scan_once(VALUE str, VALUE pat, long *start, int set_backref_str)
{
VALUE result, match;
struct re_registers *regs;
int i;
-
- if (rb_pat_search(pat, str, *start, 1) >= 0) {
- match = rb_backref_get();
- regs = RMATCH_REGS(match);
- if (BEG(0) == END(0)) {
+ long end, pos = rb_pat_search(pat, str, *start, set_backref_str);
+ if (pos >= 0) {
+ if (BUILTIN_TYPE(pat) == T_STRING) {
+ regs = NULL;
+ end = pos + RSTRING_LEN(pat);
+ }
+ else {
+ match = rb_backref_get();
+ regs = RMATCH_REGS(match);
+ pos = BEG(0);
+ end = END(0);
+ }
+ if (pos == end) {
rb_encoding *enc = STR_ENC_GET(str);
/*
* Always consume at least one character of the input string
*/
- if (RSTRING_LEN(str) > END(0))
- *start = END(0)+rb_enc_fast_mbclen(RSTRING_PTR(str)+END(0),
- RSTRING_END(str), enc);
+ if (RSTRING_LEN(str) > end)
+ *start = end + rb_enc_fast_mbclen(RSTRING_PTR(str) + end,
+ RSTRING_END(str), enc);
else
- *start = END(0)+1;
+ *start = end + 1;
}
else {
- *start = END(0);
+ *start = end;
}
- if (regs->num_regs == 1) {
- return rb_reg_nth_match(0, match);
+ if (!regs || regs->num_regs == 1) {
+ result = rb_str_subseq(str, pos, end - pos);
+ OBJ_INFECT(result, pat);
+ return result;
}
result = rb_ary_new2(regs->num_regs);
for (i=1; i < regs->num_regs; i++) {
- rb_ary_push(result, rb_reg_nth_match(i, match));
+ VALUE s = Qnil;
+ if (BEG(i) >= 0) {
+ s = rb_str_subseq(str, BEG(i), END(i)-BEG(i));
+ OBJ_INFECT(s, pat);
+ }
+ rb_ary_push(result, s);
}
return result;
@@ -8370,16 +9234,17 @@ rb_str_scan(VALUE str, VALUE pat)
if (!rb_block_given_p()) {
VALUE ary = rb_ary_new();
- while (!NIL_P(result = scan_once(str, pat, &start))) {
+ while (!NIL_P(result = scan_once(str, pat, &start, 0))) {
last = prev;
prev = start;
rb_ary_push(ary, result);
}
if (last >= 0) rb_pat_search(pat, str, last, 1);
+ else rb_backref_set(Qnil);
return ary;
}
- while (!NIL_P(result = scan_once(str, pat, &start))) {
+ while (!NIL_P(result = scan_once(str, pat, &start, 1))) {
last = prev;
prev = start;
rb_yield(result);
@@ -8439,26 +9304,72 @@ rb_str_oct(VALUE str)
* call-seq:
* str.crypt(salt_str) -> new_str
*
- * Applies a one-way cryptographic hash to <i>str</i> by invoking the
- * standard library function <code>crypt(3)</code> with the given
- * salt string. While the format and the result are system and
- * implementation dependent, using a salt matching the regular
- * expression <code>\A[a-zA-Z0-9./]{2}</code> should be valid and
- * safe on any platform, in which only the first two characters are
- * significant.
- *
- * This method is for use in system specific scripts, so if you want
- * a cross-platform hash function consider using Digest or OpenSSL
- * instead.
+ * Returns the string generated by calling <code>crypt(3)</code>
+ * standard library function with <code>str</code> and
+ * <code>salt_str</code>, in this order, as its arguments. Please do
+ * not use this method any longer. It is legacy; provided only for
+ * backward compatibility with ruby scripts in earlier days. It is
+ * bad to use in contemporary programs for several reasons:
+ *
+ * * Behaviour of C's <code>crypt(3)</code> depends on the OS it is
+ * run. The generated string lacks data portability.
+ *
+ * * On some OSes such as Mac OS, <code>crypt(3)</code> never fails
+ * (i.e. silently ends up in unexpected results).
+ *
+ * * On some OSes such as Mac OS, <code>crypt(3)</code> is not
+ * thread safe.
+ *
+ * * So-called "traditional" usage of <code>crypt(3)</code> is very
+ * very very weak. According to its manpage, Linux's traditional
+ * <code>crypt(3)</code> output has only 2**56 variations; too
+ * easy to brute force today. And this is the default behaviour.
+ *
+ * * In order to make things robust some OSes implement so-called
+ * "modular" usage. To go through, you have to do a complex
+ * build-up of the <code>salt_str</code> parameter, by hand.
+ * Failure in generation of a proper salt string tends not to
+ * yield any errors; typos in parameters are normally not
+ * detectable.
+ *
+ * * For instance, in the following example, the second invocation
+ * of <code>String#crypt</code> is wrong; it has a typo in
+ * "round=" (lacks "s"). However the call does not fail and
+ * something unexpected is generated.
+ *
+ * "foo".crypt("$5$rounds=1000$salt$") # OK, proper usage
+ * "foo".crypt("$5$round=1000$salt$") # Typo not detected
+ *
+ * * Even in the "modular" mode, some hash functions are considered
+ * archaic and no longer recommended at all; for instance module
+ * <code>$1$</code> is officially abandoned by its author: see
+ * http://phk.freebsd.dk/sagas/md5crypt_eol.html . For another
+ * instance module <code>$3$</code> is considered completely
+ * broken: see the manpage of FreeBSD.
+ *
+ * * On some OS such as Mac OS, there is no modular mode. Yet, as
+ * written above, <code>crypt(3)</code> on Mac OS never fails.
+ * This means even if you build up a proper salt string it
+ * generates a traditional DES hash anyways, and there is no way
+ * for you to be aware of.
+ *
+ * "foo".crypt("$5$rounds=1000$salt$") # => "$5fNPQMxC5j6."
+ *
+ * If for some reason you cannot migrate to other secure contemporary
+ * password hashing algorithms, install the string-crypt gem and
+ * <code>require 'string/crypt'</code> to continue using it.
*/
static VALUE
rb_str_crypt(VALUE str, VALUE salt)
{
#ifdef HAVE_CRYPT_R
- struct crypt_data data;
+ VALUE databuf;
+ struct crypt_data *data;
+# define CRYPT_END() ALLOCV_END(databuf)
#else
extern char *crypt(const char *, const char *);
+# define CRYPT_END() (void)0
#endif
VALUE result;
const char *s, *saltp;
@@ -8487,17 +9398,21 @@ rb_str_crypt(VALUE str, VALUE salt)
}
#endif
#ifdef HAVE_CRYPT_R
+ data = ALLOCV(databuf, sizeof(struct crypt_data));
# ifdef HAVE_STRUCT_CRYPT_DATA_INITIALIZED
- data.initialized = 0;
+ data->initialized = 0;
# endif
- res = crypt_r(s, saltp, &data);
+ res = crypt_r(s, saltp, data);
#else
res = crypt(s, saltp);
#endif
if (!res) {
- rb_sys_fail("crypt");
+ int err = errno;
+ CRYPT_END();
+ rb_syserr_fail(err, "crypt");
}
result = rb_str_new_cstr(res);
+ CRYPT_END();
FL_SET_RAW(result, OBJ_TAINTED_RAW(str) | OBJ_TAINTED_RAW(salt));
return result;
}
@@ -8507,7 +9422,7 @@ rb_str_crypt(VALUE str, VALUE salt)
* call-seq:
* str.ord -> integer
*
- * Return the <code>Integer</code> ordinal of a one-character string.
+ * Returns the <code>Integer</code> ordinal of a one-character string.
*
* "a".ord #=> 97
*/
@@ -8534,21 +9449,14 @@ rb_str_ord(VALUE s)
static VALUE
rb_str_sum(int argc, VALUE *argv, VALUE str)
{
- VALUE vbits;
- int bits;
+ int bits = 16;
char *ptr, *p, *pend;
long len;
VALUE sum = INT2FIX(0);
unsigned long sum0 = 0;
- if (argc == 0) {
- bits = 16;
- }
- else {
- rb_scan_args(argc, argv, "01", &vbits);
- bits = NUM2INT(vbits);
- if (bits < 0)
- bits = 0;
+ if (rb_check_arity(argc, 0, 1) && (bits = NUM2INT(argv[0])) < 0) {
+ bits = 0;
}
ptr = p = RSTRING_PTR(str);
len = RSTRING_LEN(str);
@@ -8770,7 +9678,7 @@ rb_str_partition(VALUE str, VALUE sep)
pos = rb_reg_search(sep, str, 0, 0);
if (pos < 0) {
failed:
- return rb_ary_new3(3, str, str_new_empty(str), str_new_empty(str));
+ return rb_ary_new3(3, rb_str_dup(str), str_new_empty(str), str_new_empty(str));
}
sep = rb_str_subpat(str, sep, INT2FIX(0));
if (pos == 0 && RSTRING_LEN(sep) == 0) goto failed;
@@ -8823,7 +9731,7 @@ rb_str_rpartition(VALUE str, VALUE sep)
pos = rb_str_rindex(str, sep, pos);
}
if (pos < 0) {
- return rb_ary_new3(3, str_new_empty(str), str_new_empty(str), str);
+ return rb_ary_new3(3, str_new_empty(str), str_new_empty(str), rb_str_dup(str));
}
if (regex) {
sep = rb_reg_nth_match(0, rb_backref_get());
@@ -8842,8 +9750,10 @@ rb_str_rpartition(VALUE str, VALUE sep)
* str.start_with?([prefixes]+) -> true or false
*
* Returns true if +str+ starts with one of the +prefixes+ given.
+ * Each of the +prefixes+ should be a String or a Regexp.
*
* "hello".start_with?("hell") #=> true
+ * "hello".start_with?(/H/i) #=> true
*
* # returns true if one of the prefixes matches.
* "hello".start_with?("heaven", "hell") #=> true
@@ -8857,11 +9767,17 @@ rb_str_start_with(int argc, VALUE *argv, VALUE str)
for (i=0; i<argc; i++) {
VALUE tmp = argv[i];
- StringValue(tmp);
- rb_enc_check(str, tmp);
- if (RSTRING_LEN(str) < RSTRING_LEN(tmp)) continue;
- if (memcmp(RSTRING_PTR(str), RSTRING_PTR(tmp), RSTRING_LEN(tmp)) == 0)
- return Qtrue;
+ if (RB_TYPE_P(tmp, T_REGEXP)) {
+ if (rb_reg_start_with_p(tmp, str))
+ return Qtrue;
+ }
+ else {
+ StringValue(tmp);
+ rb_enc_check(str, tmp);
+ if (RSTRING_LEN(str) < RSTRING_LEN(tmp)) continue;
+ if (memcmp(RSTRING_PTR(str), RSTRING_PTR(tmp), RSTRING_LEN(tmp)) == 0)
+ return Qtrue;
+ }
}
return Qfalse;
}
@@ -8902,6 +9818,167 @@ rb_str_end_with(int argc, VALUE *argv, VALUE str)
return Qfalse;
}
+/*!
+ * Returns the length of the <i>prefix</i> to be deleted in the given <i>str</i>,
+ * returning 0 if <i>str</i> does not start with the <i>prefix</i>.
+ *
+ * @param str the target
+ * @param prefix the prefix
+ * @retval 0 if the given <i>str</i> does not start with the given <i>prefix</i>
+ * @retval Positive-Integer otherwise
+ */
+static long
+deleted_prefix_length(VALUE str, VALUE prefix)
+{
+ char *strptr, *prefixptr;
+ long olen, prefixlen;
+
+ StringValue(prefix);
+ if (is_broken_string(prefix)) return 0;
+ rb_enc_check(str, prefix);
+
+ /* return 0 if not start with prefix */
+ prefixlen = RSTRING_LEN(prefix);
+ if (prefixlen <= 0) return 0;
+ olen = RSTRING_LEN(str);
+ if (olen < prefixlen) return 0;
+ strptr = RSTRING_PTR(str);
+ prefixptr = RSTRING_PTR(prefix);
+ if (memcmp(strptr, prefixptr, prefixlen) != 0) return 0;
+
+ return prefixlen;
+}
+
+/*
+ * call-seq:
+ * str.delete_prefix!(prefix) -> self or nil
+ *
+ * Deletes leading <code>prefix</code> from <i>str</i>, returning
+ * <code>nil</code> if no change was made.
+ *
+ * "hello".delete_prefix!("hel") #=> "lo"
+ * "hello".delete_prefix!("llo") #=> nil
+ */
+
+static VALUE
+rb_str_delete_prefix_bang(VALUE str, VALUE prefix)
+{
+ long prefixlen;
+ str_modify_keep_cr(str);
+
+ prefixlen = deleted_prefix_length(str, prefix);
+ if (prefixlen <= 0) return Qnil;
+
+ return rb_str_drop_bytes(str, prefixlen);
+}
+
+/*
+ * call-seq:
+ * str.delete_prefix(prefix) -> new_str
+ *
+ * Returns a copy of <i>str</i> with leading <code>prefix</code> deleted.
+ *
+ * "hello".delete_prefix("hel") #=> "lo"
+ * "hello".delete_prefix("llo") #=> "hello"
+ */
+
+static VALUE
+rb_str_delete_prefix(VALUE str, VALUE prefix)
+{
+ long prefixlen;
+
+ prefixlen = deleted_prefix_length(str, prefix);
+ if (prefixlen <= 0) return rb_str_dup(str);
+
+ return rb_str_subseq(str, prefixlen, RSTRING_LEN(str) - prefixlen);
+}
+
+/*!
+ * Returns the length of the <i>suffix</i> to be deleted in the given <i>str</i>,
+ * returning 0 if <i>str</i> does not end with the <i>suffix</i>.
+ *
+ * @param str the target
+ * @param suffix the suffix
+ * @retval 0 if the given <i>str</i> does not end with the given <i>suffix</i>
+ * @retval Positive-Integer otherwise
+ */
+static long
+deleted_suffix_length(VALUE str, VALUE suffix)
+{
+ char *strptr, *suffixptr, *s;
+ long olen, suffixlen;
+ rb_encoding *enc;
+
+ StringValue(suffix);
+ if (is_broken_string(suffix)) return 0;
+ enc = rb_enc_check(str, suffix);
+
+ /* return 0 if not start with suffix */
+ suffixlen = RSTRING_LEN(suffix);
+ if (suffixlen <= 0) return 0;
+ olen = RSTRING_LEN(str);
+ if (olen < suffixlen) return 0;
+ strptr = RSTRING_PTR(str);
+ suffixptr = RSTRING_PTR(suffix);
+ s = strptr + olen - suffixlen;
+ if (memcmp(s, suffixptr, suffixlen) != 0) return 0;
+ if (rb_enc_left_char_head(strptr, s, strptr + olen, enc) != s) return 0;
+
+ return suffixlen;
+}
+
+/*
+ * call-seq:
+ * str.delete_suffix!(suffix) -> self or nil
+ *
+ * Deletes trailing <code>suffix</code> from <i>str</i>, returning
+ * <code>nil</code> if no change was made.
+ *
+ * "hello".delete_suffix!("llo") #=> "he"
+ * "hello".delete_suffix!("hel") #=> nil
+ */
+
+static VALUE
+rb_str_delete_suffix_bang(VALUE str, VALUE suffix)
+{
+ long olen, suffixlen, len;
+ str_modifiable(str);
+
+ suffixlen = deleted_suffix_length(str, suffix);
+ if (suffixlen <= 0) return Qnil;
+
+ olen = RSTRING_LEN(str);
+ str_modify_keep_cr(str);
+ len = olen - suffixlen;
+ STR_SET_LEN(str, len);
+ TERM_FILL(&RSTRING_PTR(str)[len], TERM_LEN(str));
+ if (ENC_CODERANGE(str) != ENC_CODERANGE_7BIT) {
+ ENC_CODERANGE_CLEAR(str);
+ }
+ return str;
+}
+
+/*
+ * call-seq:
+ * str.delete_suffix(suffix) -> new_str
+ *
+ * Returns a copy of <i>str</i> with trailing <code>suffix</code> deleted.
+ *
+ * "hello".delete_suffix("llo") #=> "he"
+ * "hello".delete_suffix("hel") #=> "hello"
+ */
+
+static VALUE
+rb_str_delete_suffix(VALUE str, VALUE suffix)
+{
+ long suffixlen;
+
+ suffixlen = deleted_suffix_length(str, suffix);
+ if (suffixlen <= 0) return rb_str_dup(str);
+
+ return rb_str_subseq(str, 0, RSTRING_LEN(str) - suffixlen);
+}
+
void
rb_str_setter(VALUE val, ID id, VALUE *var)
{
@@ -9064,6 +10141,8 @@ str_compat_and_valid(VALUE str, rb_encoding *enc)
return str;
}
+static VALUE enc_str_scrub(rb_encoding *enc, VALUE str, VALUE repl, int cr);
+
/**
* @param str the string to be scrubbed
* @param repl the replacement character
@@ -9072,19 +10151,37 @@ str_compat_and_valid(VALUE str, rb_encoding *enc)
VALUE
rb_str_scrub(VALUE str, VALUE repl)
{
- return rb_enc_str_scrub(STR_ENC_GET(str), str, repl);
+ rb_encoding *enc = STR_ENC_GET(str);
+ return enc_str_scrub(enc, str, repl, ENC_CODERANGE(str));
}
VALUE
rb_enc_str_scrub(rb_encoding *enc, VALUE str, VALUE repl)
{
- int cr = ENC_CODERANGE(str);
+ int cr = ENC_CODERANGE_UNKNOWN;
+ if (enc == STR_ENC_GET(str)) {
+ /* cached coderange makes sense only when enc equals the
+ * actual encoding of str */
+ cr = ENC_CODERANGE(str);
+ }
+ return enc_str_scrub(enc, str, repl, cr);
+}
+
+static VALUE
+enc_str_scrub(rb_encoding *enc, VALUE str, VALUE repl, int cr)
+{
int encidx;
VALUE buf = Qnil;
const char *rep;
- long replen;
+ long replen = -1;
int tainted = 0;
+ if (rb_block_given_p()) {
+ if (!NIL_P(repl))
+ rb_raise(rb_eArgError, "both of block and replacement given");
+ replen = 0;
+ }
+
if (ENC_CODERANGE_CLEAN_P(cr))
return Qnil;
@@ -9108,9 +10205,8 @@ rb_enc_str_scrub(rb_encoding *enc, VALUE str, VALUE repl)
const char *e = RSTRING_END(str);
const char *p1 = p;
int rep7bit_p;
- if (rb_block_given_p()) {
+ if (!replen) {
rep = NULL;
- replen = 0;
rep7bit_p = FALSE;
}
else if (!NIL_P(repl)) {
@@ -9221,7 +10317,10 @@ rb_enc_str_scrub(rb_encoding *enc, VALUE str, VALUE repl)
const char *e = RSTRING_END(str);
const char *p1 = p;
long mbminlen = rb_enc_mbminlen(enc);
- if (!NIL_P(repl)) {
+ if (!replen) {
+ rep = NULL;
+ }
+ else if (!NIL_P(repl)) {
rep = RSTRING_PTR(repl);
replen = RSTRING_LEN(repl);
}
@@ -9272,7 +10371,7 @@ rb_enc_str_scrub(rb_encoding *enc, VALUE str, VALUE repl)
rb_str_buf_cat(buf, rep, replen);
}
else {
- repl = rb_yield(rb_enc_str_new(p, e-p, enc));
+ repl = rb_yield(rb_enc_str_new(p, clen, enc));
repl = str_compat_and_valid(repl, enc);
tainted |= OBJ_TAINTED_RAW(repl);
rb_str_buf_cat(buf, RSTRING_PTR(repl), RSTRING_LEN(repl));
@@ -9357,6 +10456,89 @@ str_scrub_bang(int argc, VALUE *argv, VALUE str)
return str;
}
+static ID id_normalize;
+static ID id_normalized_p;
+static VALUE mUnicodeNormalize;
+
+static VALUE
+unicode_normalize_common(int argc, VALUE *argv, VALUE str, ID id)
+{
+ static int UnicodeNormalizeRequired = 0;
+ VALUE argv2[2];
+
+ if (!UnicodeNormalizeRequired) {
+ rb_require("unicode_normalize/normalize.rb");
+ UnicodeNormalizeRequired = 1;
+ }
+ argv2[0] = str;
+ if (rb_check_arity(argc, 0, 1)) argv2[1] = argv[0];
+ return rb_funcallv(mUnicodeNormalize, id, argc+1, argv2);
+}
+
+/*
+ * call-seq:
+ * str.unicode_normalize(form=:nfc)
+ *
+ * Unicode Normalization---Returns a normalized form of +str+,
+ * using Unicode normalizations NFC, NFD, NFKC, or NFKD.
+ * The normalization form used is determined by +form+, which can
+ * be any of the four values +:nfc+, +:nfd+, +:nfkc+, or +:nfkd+.
+ * The default is +:nfc+.
+ *
+ * If the string is not in a Unicode Encoding, then an Exception is raised.
+ * In this context, 'Unicode Encoding' means any of UTF-8, UTF-16BE/LE,
+ * and UTF-32BE/LE, as well as GB18030, UCS_2BE, and UCS_4BE.
+ * Anything other than UTF-8 is implemented by converting to UTF-8,
+ * which makes it slower than UTF-8.
+ *
+ * "a\u0300".unicode_normalize #=> "\u00E0"
+ * "a\u0300".unicode_normalize(:nfc) #=> "\u00E0"
+ * "\u00E0".unicode_normalize(:nfd) #=> "a\u0300"
+ * "\xE0".force_encoding('ISO-8859-1').unicode_normalize(:nfd)
+ * #=> Encoding::CompatibilityError raised
+ */
+static VALUE
+rb_str_unicode_normalize(int argc, VALUE *argv, VALUE str)
+{
+ return unicode_normalize_common(argc, argv, str, id_normalize);
+}
+
+/*
+ * call-seq:
+ * str.unicode_normalize!(form=:nfc)
+ *
+ * Destructive version of String#unicode_normalize, doing Unicode
+ * normalization in place.
+ */
+static VALUE
+rb_str_unicode_normalize_bang(int argc, VALUE *argv, VALUE str)
+{
+ return rb_str_replace(str, unicode_normalize_common(argc, argv, str, id_normalize));
+}
+
+/* call-seq:
+ * str.unicode_normalized?(form=:nfc)
+ *
+ * Checks whether +str+ is in Unicode normalization form +form+,
+ * which can be any of the four values +:nfc+, +:nfd+, +:nfkc+, or +:nfkd+.
+ * The default is +:nfc+.
+ *
+ * If the string is not in a Unicode Encoding, then an Exception is raised.
+ * For details, see String#unicode_normalize.
+ *
+ * "a\u0300".unicode_normalized? #=> false
+ * "a\u0300".unicode_normalized?(:nfd) #=> true
+ * "\u00E0".unicode_normalized? #=> true
+ * "\u00E0".unicode_normalized?(:nfd) #=> false
+ * "\xE0".force_encoding('ISO-8859-1').unicode_normalized?
+ * #=> Encoding::CompatibilityError raised
+ */
+static VALUE
+rb_str_unicode_normalized_p(int argc, VALUE *argv, VALUE str)
+{
+ return unicode_normalize_common(argc, argv, str, id_normalized_p);
+}
+
/**********************************************************************
* Document-class: Symbol
*
@@ -9430,7 +10612,7 @@ rb_str_symname_p(VALUE sym)
ptr = RSTRING_PTR(sym);
len = RSTRING_LEN(sym);
if ((resenc != enc && !rb_str_is_ascii_only_p(sym)) || len != (long)strlen(ptr) ||
- !rb_enc_symname_p(ptr, enc) || !sym_printable(ptr, ptr + len, enc)) {
+ !rb_enc_symname2_p(ptr, len, enc) || !sym_printable(ptr, ptr + len, enc)) {
return FALSE;
}
return TRUE;
@@ -9457,10 +10639,14 @@ rb_str_quote_unprintable(VALUE str)
return str;
}
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_id_quote_unprintable(ID id)
{
- return rb_str_quote_unprintable(rb_id2str(id));
+ VALUE str = rb_id2str(id);
+ if (!rb_str_symname_p(str)) {
+ return rb_str_inspect(str);
+ }
+ return str;
}
/*
@@ -9507,6 +10693,7 @@ sym_inspect(VALUE sym)
* Returns the name or string corresponding to <i>sym</i>.
*
* :fred.id2name #=> "fred"
+ * :ginger.to_s #=> "ginger"
*/
@@ -9533,7 +10720,7 @@ sym_to_sym(VALUE sym)
return sym;
}
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_sym_proc_call(ID mid, int argc, const VALUE *argv, VALUE passed_proc)
{
VALUE obj;
@@ -9550,7 +10737,7 @@ rb_sym_proc_call(ID mid, int argc, const VALUE *argv, VALUE passed_proc)
* call-seq:
* sym.to_proc
*
- * Returns a _Proc_ object which respond to the given method by _sym_.
+ * Returns a _Proc_ object which responds to the given method by _sym_.
*
* (1..3).collect(&:to_s) #=> ["1", "2", "3"]
*/
@@ -9578,13 +10765,13 @@ sym_succ(VALUE sym)
/*
* call-seq:
*
- * symbol <=> other_symbol -> -1, 0, +1 or nil
+ * symbol <=> other_symbol -> -1, 0, +1, or nil
*
* Compares +symbol+ with +other_symbol+ after calling #to_s on each of the
- * symbols. Returns -1, 0, +1 or nil depending on whether +symbol+ is less
- * than, equal to, or greater than +other_symbol+.
+ * symbols. Returns -1, 0, +1, or +nil+ depending on whether +symbol+ is
+ * less than, equal to, or greater than +other_symbol+.
*
- * +nil+ is returned if the two values are incomparable.
+ * +nil+ is returned if the two values are incomparable.
*
* See String#<=> for more information.
*/
@@ -9600,10 +10787,22 @@ sym_cmp(VALUE sym, VALUE other)
/*
* call-seq:
- *
- * sym.casecmp(other) -> -1, 0, +1 or nil
+ * sym.casecmp(other_symbol) -> -1, 0, +1, or nil
*
* Case-insensitive version of <code>Symbol#<=></code>.
+ * Currently, case-insensitivity only works on characters A-Z/a-z,
+ * not all of Unicode. This is different from Symbol#casecmp?.
+ *
+ * :aBcDeF.casecmp(:abcde) #=> 1
+ * :aBcDeF.casecmp(:abcdef) #=> 0
+ * :aBcDeF.casecmp(:abcdefg) #=> -1
+ * :abcdef.casecmp(:ABCDEF) #=> 0
+ *
+ * +nil+ is returned if the two symbols have incompatible encodings,
+ * or if +other_symbol+ is not a symbol.
+ *
+ * :foo.casecmp(2) #=> nil
+ * "\u{e4 f6 fc}".encode("ISO-8859-1").to_sym.casecmp(:"\u{c4 d6 dc}") #=> nil
*/
static VALUE
@@ -9612,7 +10811,36 @@ sym_casecmp(VALUE sym, VALUE other)
if (!SYMBOL_P(other)) {
return Qnil;
}
- return rb_str_casecmp(rb_sym2str(sym), rb_sym2str(other));
+ return str_casecmp(rb_sym2str(sym), rb_sym2str(other));
+}
+
+/*
+ * call-seq:
+ * sym.casecmp?(other_symbol) -> true, false, or nil
+ *
+ * Returns +true+ if +sym+ and +other_symbol+ are equal after
+ * Unicode case folding, +false+ if they are not equal.
+ *
+ * :aBcDeF.casecmp?(:abcde) #=> false
+ * :aBcDeF.casecmp?(:abcdef) #=> true
+ * :aBcDeF.casecmp?(:abcdefg) #=> false
+ * :abcdef.casecmp?(:ABCDEF) #=> true
+ * :"\u{e4 f6 fc}".casecmp?(:"\u{c4 d6 dc}") #=> true
+ *
+ * +nil+ is returned if the two symbols have incompatible encodings,
+ * or if +other_symbol+ is not a symbol.
+ *
+ * :foo.casecmp?(2) #=> nil
+ * "\u{e4 f6 fc}".encode("ISO-8859-1").to_sym.casecmp?(:"\u{c4 d6 dc}") #=> nil
+ */
+
+static VALUE
+sym_casecmp_p(VALUE sym, VALUE other)
+{
+ if (!SYMBOL_P(other)) {
+ return Qnil;
+ }
+ return str_casecmp_p(rb_sym2str(sym), rb_sym2str(other));
}
/*
@@ -9630,9 +10858,10 @@ sym_match(VALUE sym, VALUE other)
/*
* call-seq:
- * sym.match(obj) -> MatchData or nil
+ * sym.match(pattern) -> matchdata or nil
+ * sym.match(pattern, pos) -> matchdata or nil
*
- * Returns <code>sym.to_s.match(obj)</code>.
+ * Returns <code>sym.to_s.match</code>.
*/
static VALUE
@@ -9643,6 +10872,20 @@ sym_match_m(int argc, VALUE *argv, VALUE sym)
/*
* call-seq:
+ * sym.match?(pattern) -> true or false
+ * sym.match?(pattern, pos) -> true or false
+ *
+ * Returns <code>sym.to_s.match?</code>.
+ */
+
+static VALUE
+sym_match_m_p(int argc, VALUE *argv, VALUE sym)
+{
+ return rb_str_match_m_p(argc, argv, sym);
+}
+
+/*
+ * call-seq:
* sym[idx] -> char
* sym[b, n] -> string
* sym.slice(idx) -> char
@@ -9659,8 +10902,8 @@ sym_aref(int argc, VALUE *argv, VALUE sym)
/*
* call-seq:
- * sym.length -> integer
- * sym.size -> integer
+ * sym.length -> integer
+ * sym.size -> integer
*
* Same as <code>sym.to_s.length</code>.
*/
@@ -9675,7 +10918,7 @@ sym_length(VALUE sym)
* call-seq:
* sym.empty? -> true or false
*
- * Returns that _sym_ is :"" or not.
+ * Returns whether _sym_ is :"" or not.
*/
static VALUE
@@ -9686,7 +10929,8 @@ sym_empty(VALUE sym)
/*
* call-seq:
- * sym.upcase [options] -> symbol
+ * sym.upcase -> symbol
+ * sym.upcase([options]) -> symbol
*
* Same as <code>sym.to_s.upcase.intern</code>.
*/
@@ -9699,7 +10943,8 @@ sym_upcase(int argc, VALUE *argv, VALUE sym)
/*
* call-seq:
- * sym.downcase [options] -> symbol
+ * sym.downcase -> symbol
+ * sym.downcase([options]) -> symbol
*
* Same as <code>sym.to_s.downcase.intern</code>.
*/
@@ -9712,7 +10957,8 @@ sym_downcase(int argc, VALUE *argv, VALUE sym)
/*
* call-seq:
- * sym.capitalize [options] -> symbol
+ * sym.capitalize -> symbol
+ * sym.capitalize([options]) -> symbol
*
* Same as <code>sym.to_s.capitalize.intern</code>.
*/
@@ -9725,7 +10971,8 @@ sym_capitalize(int argc, VALUE *argv, VALUE sym)
/*
* call-seq:
- * sym.swapcase [options] -> symbol
+ * sym.swapcase -> symbol
+ * sym.swapcase([options]) -> symbol
*
* Same as <code>sym.to_s.swapcase.intern</code>.
*/
@@ -9803,6 +11050,8 @@ Init_String(void)
#define rb_intern(str) rb_intern_const(str)
rb_cString = rb_define_class("String", rb_cObject);
+ assert(rb_vm_fstring_table());
+ st_foreach(rb_vm_fstring_table(), fstring_set_class_i, rb_cString);
rb_include_module(rb_cString, rb_mComparable);
rb_define_alloc_func(rb_cString, empty_str_alloc);
rb_define_singleton_method(rb_cString, "try_convert", rb_str_s_try_convert, 1);
@@ -9814,6 +11063,7 @@ Init_String(void)
rb_define_method(rb_cString, "eql?", rb_str_eql, 1);
rb_define_method(rb_cString, "hash", rb_str_hash_m, 0);
rb_define_method(rb_cString, "casecmp", rb_str_casecmp, 1);
+ rb_define_method(rb_cString, "casecmp?", rb_str_casecmp_p, 1);
rb_define_method(rb_cString, "+", rb_str_plus, 1);
rb_define_method(rb_cString, "*", rb_str_times, 1);
rb_define_method(rb_cString, "%", rb_str_format_m, 1);
@@ -9826,6 +11076,7 @@ Init_String(void)
rb_define_method(rb_cString, "empty?", rb_str_empty, 0);
rb_define_method(rb_cString, "=~", rb_str_match, 1);
rb_define_method(rb_cString, "match", rb_str_match_m, -1);
+ rb_define_method(rb_cString, "match?", rb_str_match_m_p, -1);
rb_define_method(rb_cString, "succ", rb_str_succ, 0);
rb_define_method(rb_cString, "succ!", rb_str_succ_bang, 0);
rb_define_method(rb_cString, "next", rb_str_succ, 0);
@@ -9851,6 +11102,7 @@ Init_String(void)
rb_define_method(rb_cString, "to_str", rb_str_to_s, 0);
rb_define_method(rb_cString, "inspect", rb_str_inspect, 0);
rb_define_method(rb_cString, "dump", rb_str_dump, 0);
+ rb_define_method(rb_cString, "undump", str_undump, 0);
sym_ascii = ID2SYM(rb_intern("ascii"));
sym_turkic = ID2SYM(rb_intern("turkic"));
@@ -9874,6 +11126,7 @@ Init_String(void)
rb_define_method(rb_cString, "bytes", rb_str_bytes, 0);
rb_define_method(rb_cString, "chars", rb_str_chars, 0);
rb_define_method(rb_cString, "codepoints", rb_str_codepoints, 0);
+ rb_define_method(rb_cString, "grapheme_clusters", rb_str_grapheme_clusters, 0);
rb_define_method(rb_cString, "reverse", rb_str_reverse, 0);
rb_define_method(rb_cString, "reverse!", rb_str_reverse_bang, 0);
rb_define_method(rb_cString, "concat", rb_str_concat_multi, -1);
@@ -9901,6 +11154,8 @@ Init_String(void)
rb_define_method(rb_cString, "strip", rb_str_strip, 0);
rb_define_method(rb_cString, "lstrip", rb_str_lstrip, 0);
rb_define_method(rb_cString, "rstrip", rb_str_rstrip, 0);
+ rb_define_method(rb_cString, "delete_prefix", rb_str_delete_prefix, 1);
+ rb_define_method(rb_cString, "delete_suffix", rb_str_delete_suffix, 1);
rb_define_method(rb_cString, "sub!", rb_str_sub_bang, -1);
rb_define_method(rb_cString, "gsub!", rb_str_gsub_bang, -1);
@@ -9909,6 +11164,8 @@ Init_String(void)
rb_define_method(rb_cString, "strip!", rb_str_strip_bang, 0);
rb_define_method(rb_cString, "lstrip!", rb_str_lstrip_bang, 0);
rb_define_method(rb_cString, "rstrip!", rb_str_rstrip_bang, 0);
+ rb_define_method(rb_cString, "delete_prefix!", rb_str_delete_prefix_bang, 1);
+ rb_define_method(rb_cString, "delete_suffix!", rb_str_delete_suffix_bang, 1);
rb_define_method(rb_cString, "tr", rb_str_tr, 2);
rb_define_method(rb_cString, "tr_s", rb_str_tr_s, 2);
@@ -9925,6 +11182,7 @@ Init_String(void)
rb_define_method(rb_cString, "each_byte", rb_str_each_byte, 0);
rb_define_method(rb_cString, "each_char", rb_str_each_char, 0);
rb_define_method(rb_cString, "each_codepoint", rb_str_each_codepoint, 0);
+ rb_define_method(rb_cString, "each_grapheme_cluster", rb_str_each_grapheme_cluster, 0);
rb_define_method(rb_cString, "sum", rb_str_sum, -1);
@@ -9940,9 +11198,19 @@ Init_String(void)
rb_define_method(rb_cString, "valid_encoding?", rb_str_valid_encoding_p, 0);
rb_define_method(rb_cString, "ascii_only?", rb_str_is_ascii_only_p, 0);
+ /* define UnicodeNormalize module here so that we don't have to look it up */
+ mUnicodeNormalize = rb_define_module("UnicodeNormalize");
+ id_normalize = rb_intern("normalize");
+ id_normalized_p = rb_intern("normalized?");
+
+ rb_define_method(rb_cString, "unicode_normalize", rb_str_unicode_normalize, -1);
+ rb_define_method(rb_cString, "unicode_normalize!", rb_str_unicode_normalize_bang, -1);
+ rb_define_method(rb_cString, "unicode_normalized?", rb_str_unicode_normalized_p, -1);
+
rb_fs = Qnil;
rb_define_hooked_variable("$;", &rb_fs, 0, rb_fs_setter);
rb_define_hooked_variable("$-F", &rb_fs, 0, rb_fs_setter);
+ rb_gc_register_address(&rb_fs);
rb_cSymbol = rb_define_class("Symbol", rb_cObject);
rb_include_module(rb_cSymbol, rb_mComparable);
@@ -9963,6 +11231,7 @@ Init_String(void)
rb_define_method(rb_cSymbol, "<=>", sym_cmp, 1);
rb_define_method(rb_cSymbol, "casecmp", sym_casecmp, 1);
+ rb_define_method(rb_cSymbol, "casecmp?", sym_casecmp_p, 1);
rb_define_method(rb_cSymbol, "=~", sym_match, 1);
rb_define_method(rb_cSymbol, "[]", sym_aref, -1);
@@ -9971,6 +11240,7 @@ Init_String(void)
rb_define_method(rb_cSymbol, "size", sym_length, 0);
rb_define_method(rb_cSymbol, "empty?", sym_empty, 0);
rb_define_method(rb_cSymbol, "match", sym_match_m, -1);
+ rb_define_method(rb_cSymbol, "match?", sym_match_m_p, -1);
rb_define_method(rb_cSymbol, "upcase", sym_upcase, -1);
rb_define_method(rb_cSymbol, "downcase", sym_downcase, -1);
@@ -9978,7 +11248,4 @@ Init_String(void)
rb_define_method(rb_cSymbol, "swapcase", sym_swapcase, -1);
rb_define_method(rb_cSymbol, "encoding", sym_encoding, 0);
-
- assert(rb_vm_fstring_table());
- st_foreach(rb_vm_fstring_table(), fstring_set_class_i, rb_cString);
}
diff --git a/struct.c b/struct.c
index cd6d05a552..f34882e780 100644
--- a/struct.c
+++ b/struct.c
@@ -12,6 +12,7 @@
#include "internal.h"
#include "vm_core.h"
#include "id.h"
+#include "transient_heap.h"
/* only for struct[:field] access */
enum {
@@ -23,7 +24,7 @@ const rb_iseq_t *rb_method_for_self_aref(VALUE name, VALUE arg, rb_insn_func_t f
const rb_iseq_t *rb_method_for_self_aset(VALUE name, VALUE arg, rb_insn_func_t func);
VALUE rb_cStruct;
-static ID id_members, id_back_members;
+static ID id_members, id_back_members, id_keyword_init;
static VALUE struct_alloc(VALUE);
@@ -48,6 +49,12 @@ struct_ivar_get(VALUE c, ID id)
}
VALUE
+rb_struct_s_keyword_init(VALUE klass)
+{
+ return struct_ivar_get(klass, id_keyword_init);
+}
+
+VALUE
rb_struct_s_members(VALUE klass)
{
VALUE members = struct_ivar_get(klass, id_members);
@@ -132,7 +139,6 @@ static inline int
struct_member_pos(VALUE s, VALUE name)
{
VALUE back = struct_ivar_get(rb_obj_class(s), id_back_members);
- VALUE const * p;
long j, mask;
if (UNLIKELY(NIL_P(back))) {
@@ -142,7 +148,6 @@ struct_member_pos(VALUE s, VALUE name)
rb_raise(rb_eTypeError, "corrupted struct");
}
- p = RARRAY_CONST_PTR(back);
mask = RARRAY_LEN(back);
if (mask <= AREF_HASH_THRESHOLD) {
@@ -152,7 +157,7 @@ struct_member_pos(VALUE s, VALUE name)
mask, RSTRUCT_LEN(s));
}
for (j = 0; j < mask; j++) {
- if (p[j] == name)
+ if (RARRAY_AREF(back, j) == name)
return (int)j;
}
return -1;
@@ -167,9 +172,10 @@ struct_member_pos(VALUE s, VALUE name)
j = struct_member_pos_ideal(name, mask);
for (;;) {
- if (p[j] == name)
- return FIX2INT(p[j + 1]);
- if (!RTEST(p[j])) {
+ VALUE e = RARRAY_AREF(back, j);
+ if (e == name)
+ return FIX2INT(RARRAY_AREF(back, j + 1));
+ if (!RTEST(e)) {
return -1;
}
j = struct_member_pos_probe(j, mask);
@@ -211,7 +217,7 @@ rb_struct_getmember(VALUE obj, ID id)
}
rb_name_err_raise("`%1$s' is not a struct member", obj, ID2SYM(id));
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
static VALUE rb_struct_ref0(VALUE obj) {return RSTRUCT_GET(obj, 0);}
@@ -279,7 +285,7 @@ new_struct(VALUE name, VALUE super)
static void
define_aref_method(VALUE nstr, VALUE name, VALUE off)
{
- rb_control_frame_t *FUNC_FASTCALL(rb_vm_opt_struct_aref)(rb_thread_t *, rb_control_frame_t *);
+ rb_control_frame_t *FUNC_FASTCALL(rb_vm_opt_struct_aref)(rb_execution_context_t *, rb_control_frame_t *);
const rb_iseq_t *iseq = rb_method_for_self_aref(name, off, rb_vm_opt_struct_aref);
rb_add_method_iseq(nstr, SYM2ID(name), iseq, NULL, METHOD_VISI_PUBLIC);
@@ -288,16 +294,25 @@ define_aref_method(VALUE nstr, VALUE name, VALUE off)
static void
define_aset_method(VALUE nstr, VALUE name, VALUE off)
{
- rb_control_frame_t *FUNC_FASTCALL(rb_vm_opt_struct_aset)(rb_thread_t *, rb_control_frame_t *);
+ rb_control_frame_t *FUNC_FASTCALL(rb_vm_opt_struct_aset)(rb_execution_context_t *, rb_control_frame_t *);
const rb_iseq_t *iseq = rb_method_for_self_aset(name, off, rb_vm_opt_struct_aset);
rb_add_method_iseq(nstr, SYM2ID(name), iseq, NULL, METHOD_VISI_PUBLIC);
}
static VALUE
+rb_struct_s_inspect(VALUE klass)
+{
+ VALUE inspect = rb_class_name(klass);
+ if (RTEST(rb_struct_s_keyword_init(klass))) {
+ rb_str_cat_cstr(inspect, "(keyword_init: true)");
+ }
+ return inspect;
+}
+
+static VALUE
setup_struct(VALUE nstr, VALUE members)
{
- const VALUE *ptr_members;
long i, len;
members = struct_set_members(nstr, members);
@@ -306,17 +321,18 @@ setup_struct(VALUE nstr, VALUE members)
rb_define_singleton_method(nstr, "new", rb_class_new_instance, -1);
rb_define_singleton_method(nstr, "[]", rb_class_new_instance, -1);
rb_define_singleton_method(nstr, "members", rb_struct_s_members_m, 0);
- ptr_members = RARRAY_CONST_PTR(members);
+ rb_define_singleton_method(nstr, "inspect", rb_struct_s_inspect, 0);
len = RARRAY_LEN(members);
for (i=0; i< len; i++) {
- ID id = SYM2ID(ptr_members[i]);
+ VALUE sym = RARRAY_AREF(members, i);
+ ID id = SYM2ID(sym);
VALUE off = LONG2NUM(i);
if (i < N_REF_FUNC) {
rb_define_method_id(nstr, id, ref_func[i], 0);
}
else {
- define_aref_method(nstr, ptr_members[i], off);
+ define_aref_method(nstr, sym, off);
}
define_aset_method(nstr, ID2SYM(rb_id_attrset(id)), off);
}
@@ -436,10 +452,11 @@ rb_struct_define_under(VALUE outer, const char *name, ...)
/*
* call-seq:
- * Struct.new([class_name] [, member_name]+>) -> StructClass
- * Struct.new([class_name] [, member_name]+>) {|StructClass| block } -> StructClass
- * StructClass.new(value, ...) -> obj
- * StructClass[value, ...] -> obj
+ * Struct.new([class_name] [, member_name]+) -> StructClass
+ * Struct.new([class_name] [, member_name]+, keyword_init: true) -> StructClass
+ * Struct.new([class_name] [, member_name]+) {|StructClass| block } -> StructClass
+ * StructClass.new(value, ...) -> object
+ * StructClass[value, ...] -> object
*
* The first two forms are used to create a new Struct subclass +class_name+
* that can contain a value for each +member_name+. This subclass can be
@@ -457,6 +474,19 @@ rb_struct_define_under(VALUE outer, const char *name, ...)
* Struct::Customer.new("Dave", "123 Main")
* #=> #<struct Struct::Customer name="Dave", address="123 Main">
*
+ * # Create a structure named by its constant
+ * Customer = Struct.new(:name, :address)
+ * #=> Customer
+ * Customer.new("Dave", "123 Main")
+ * #=> #<struct Customer name="Dave", address="123 Main">
+ *
+ * If the optional +keyword_init+ keyword argument is set to +true+,
+ * .new takes keyword arguments instead of normal arguments.
+ *
+ * Customer = Struct.new(:name, :address, keyword_init: true)
+ * Customer.new(name: "Dave", address: "123 Main")
+ * #=> #<struct Customer name="Dave", address="123 Main">
+ *
* If a block is given it will be evaluated in the context of
* +StructClass+, passing the created class as a parameter:
*
@@ -465,7 +495,7 @@ rb_struct_define_under(VALUE outer, const char *name, ...)
* "Hello #{name}!"
* end
* end
- * Customer.new("Dave", "123 Main").greeting # => "Hello Dave!"
+ * Customer.new("Dave", "123 Main").greeting #=> "Hello Dave!"
*
* This is the recommended way to customize a struct. Subclassing an
* anonymous struct creates an extra anonymous class that will never be used.
@@ -476,17 +506,17 @@ rb_struct_define_under(VALUE outer, const char *name, ...)
* Passing more parameters than number of attributes will raise
* an ArgumentError.
*
- * # Create a structure named by its constant
* Customer = Struct.new(:name, :address)
- * #=> Customer
* Customer.new("Dave", "123 Main")
* #=> #<struct Customer name="Dave", address="123 Main">
+ * Customer["Dave"]
+ * #=> #<struct Customer name="Dave", address=nil>
*/
static VALUE
rb_struct_s_def(int argc, VALUE *argv, VALUE klass)
{
- VALUE name, rest;
+ VALUE name, rest, keyword_init = Qfalse;
long i;
VALUE st;
st_table *tbl;
@@ -500,6 +530,20 @@ rb_struct_s_def(int argc, VALUE *argv, VALUE klass)
--argc;
++argv;
}
+
+ if (RB_TYPE_P(argv[argc-1], T_HASH)) {
+ static ID keyword_ids[1];
+
+ if (!keyword_ids[0]) {
+ keyword_ids[0] = rb_intern("keyword_init");
+ }
+ rb_get_kwargs(argv[argc-1], keyword_ids, 0, 1, &keyword_init);
+ if (keyword_init == Qundef) {
+ keyword_init = Qfalse;
+ }
+ --argc;
+ }
+
rest = rb_ident_hash_new();
RBASIC_CLEAR_CLASS(rest);
tbl = RHASH_TBL(rest);
@@ -520,6 +564,7 @@ rb_struct_s_def(int argc, VALUE *argv, VALUE klass)
st = new_struct(name, klass);
}
setup_struct(st, rest);
+ rb_ivar_set(st, id_keyword_init, keyword_init);
if (rb_block_given_p()) {
rb_mod_module_eval(0, 0, st);
}
@@ -541,6 +586,31 @@ num_members(VALUE klass)
/*
*/
+struct struct_hash_set_arg {
+ VALUE self;
+ VALUE unknown_keywords;
+};
+
+static int rb_struct_pos(VALUE s, VALUE *name);
+
+static int
+struct_hash_set_i(VALUE key, VALUE val, VALUE arg)
+{
+ struct struct_hash_set_arg *args = (struct struct_hash_set_arg *)arg;
+ int i = rb_struct_pos(args->self, &key);
+ if (i < 0) {
+ if (args->unknown_keywords == Qnil) {
+ args->unknown_keywords = rb_ary_new();
+ }
+ rb_ary_push(args->unknown_keywords, key);
+ }
+ else {
+ rb_struct_modify(args->self);
+ RSTRUCT_SET(args->self, i, val);
+ }
+ return ST_CONTINUE;
+}
+
static VALUE
rb_struct_initialize_m(int argc, const VALUE *argv, VALUE self)
{
@@ -549,14 +619,30 @@ rb_struct_initialize_m(int argc, const VALUE *argv, VALUE self)
rb_struct_modify(self);
n = num_members(klass);
- if (n < argc) {
- rb_raise(rb_eArgError, "struct size differs");
- }
- for (i=0; i<argc; i++) {
- RSTRUCT_SET(self, i, argv[i]);
+ if (argc > 0 && RTEST(rb_struct_s_keyword_init(klass))) {
+ struct struct_hash_set_arg arg;
+ if (argc > 2 || !RB_TYPE_P(argv[0], T_HASH)) {
+ rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 0)", argc);
+ }
+ rb_mem_clear((VALUE *)RSTRUCT_CONST_PTR(self), n);
+ arg.self = self;
+ arg.unknown_keywords = Qnil;
+ rb_hash_foreach(argv[0], struct_hash_set_i, (VALUE)&arg);
+ if (arg.unknown_keywords != Qnil) {
+ rb_raise(rb_eArgError, "unknown keywords: %s",
+ RSTRING_PTR(rb_ary_join(arg.unknown_keywords, rb_str_new2(", "))));
+ }
}
- if (n > argc) {
- rb_mem_clear((VALUE *)RSTRUCT_CONST_PTR(self)+argc, n-argc);
+ else {
+ if (n < argc) {
+ rb_raise(rb_eArgError, "struct size differs");
+ }
+ for (i=0; i<argc; i++) {
+ RSTRUCT_SET(self, i, argv[i]);
+ }
+ if (n > argc) {
+ rb_mem_clear((VALUE *)RSTRUCT_CONST_PTR(self)+argc, n-argc);
+ }
}
return Qnil;
}
@@ -567,6 +653,43 @@ rb_struct_initialize(VALUE self, VALUE values)
return rb_struct_initialize_m(RARRAY_LENINT(values), RARRAY_CONST_PTR(values), self);
}
+static VALUE *
+struct_heap_alloc(VALUE st, size_t len)
+{
+ VALUE *ptr = rb_transient_heap_alloc((VALUE)st, sizeof(VALUE) * len);
+
+ if (ptr) {
+ RSTRUCT_TRANSIENT_SET(st);
+ return ptr;
+ }
+ else {
+ RSTRUCT_TRANSIENT_UNSET(st);
+ return ALLOC_N(VALUE, len);
+ }
+}
+
+#if USE_TRANSIENT_HEAP
+void
+rb_struct_transient_heap_evacuate(VALUE obj, int promote)
+{
+ if (RSTRUCT_TRANSIENT_P(obj)) {
+ const VALUE *old_ptr = rb_struct_const_heap_ptr(obj);
+ VALUE *new_ptr;
+ long len = RSTRUCT_LEN(obj);
+
+ if (promote) {
+ new_ptr = ALLOC_N(VALUE, len);
+ FL_UNSET_RAW(obj, RSTRUCT_TRANSIENT_FLAG);
+ }
+ else {
+ new_ptr = struct_heap_alloc(obj, len);
+ }
+ MEMCPY(new_ptr, old_ptr, VALUE, len);
+ RSTRUCT(obj)->as.heap.ptr = new_ptr;
+ }
+}
+#endif
+
static VALUE
struct_alloc(VALUE klass)
{
@@ -581,9 +704,9 @@ struct_alloc(VALUE klass)
rb_mem_clear((VALUE *)st->as.ary, n);
}
else {
- st->as.heap.ptr = ALLOC_N(VALUE, n);
- rb_mem_clear((VALUE *)st->as.heap.ptr, n);
- st->as.heap.len = n;
+ st->as.heap.ptr = struct_heap_alloc((VALUE)st, n);
+ rb_mem_clear((VALUE *)st->as.heap.ptr, n);
+ st->as.heap.len = n;
}
return (VALUE)st;
@@ -625,7 +748,7 @@ struct_enum_size(VALUE s, VALUE args, VALUE eobj)
/*
* call-seq:
* struct.each {|obj| block } -> struct
- * struct.each -> an_enumerator
+ * struct.each -> enumerator
*
* Yields the value of each struct member in order. If no block is given an
* enumerator is returned.
@@ -656,7 +779,7 @@ rb_struct_each(VALUE s)
/*
* call-seq:
* struct.each_pair {|sym, obj| block } -> struct
- * struct.each_pair -> an_enumerator
+ * struct.each_pair -> enumerator
*
* Yields the name and value of each struct member in order. If no block is
* given an enumerator is returned.
@@ -747,7 +870,7 @@ inspect_struct(VALUE s, VALUE dummy, int recur)
* struct.to_s -> string
* struct.inspect -> string
*
- * Describe the contents of this struct in a string.
+ * Returns a description of this struct as a string.
*/
static VALUE
@@ -776,24 +899,35 @@ rb_struct_to_a(VALUE s)
/*
* call-seq:
- * struct.to_h -> hash
+ * struct.to_h -> hash
+ * struct.to_h {|name, value| block } -> hash
*
* Returns a Hash containing the names and values for the struct's members.
*
+ * If a block is given, the results of the block on each pair of the receiver
+ * will be used as pairs.
+ *
* Customer = Struct.new(:name, :address, :zip)
* joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
* joe.to_h[:address] #=> "123 Maple, Anytown NC"
+ * joe.to_h{|name, value| [name.upcase, value.to_s.upcase]}[:ADDRESS]
+ * #=> "123 MAPLE, ANYTOWN NC"
*/
static VALUE
rb_struct_to_h(VALUE s)
{
- VALUE h = rb_hash_new();
+ VALUE h = rb_hash_new_with_size(RSTRUCT_LEN(s));
VALUE members = rb_struct_members(s);
long i;
+ int block_given = rb_block_given_p();
for (i=0; i<RSTRUCT_LEN(s); i++) {
- rb_hash_aset(h, rb_ary_entry(members, i), RSTRUCT_GET(s, i));
+ VALUE k = rb_ary_entry(members, i), v = RSTRUCT_GET(s, i);
+ if (block_given)
+ rb_hash_set_pair(h, rb_yield_values(2, k, v));
+ else
+ rb_hash_aset(h, k, v);
}
return h;
}
@@ -871,8 +1005,8 @@ invalid_struct_pos(VALUE s, VALUE idx)
/*
* call-seq:
- * struct[member] -> anObject
- * struct[index] -> anObject
+ * struct[member] -> object
+ * struct[index] -> object
*
* Attribute Reference---Returns the value of the given struct +member+ or
* the member at the given +index+. Raises NameError if the +member+ does
@@ -948,7 +1082,7 @@ struct_entry(VALUE s, long n)
/*
* call-seq:
- * struct.values_at(selector, ...) -> an_array
+ * struct.values_at(selector, ...) -> array
*
* Returns the struct member values for each +selector+ as an Array. A
* +selector+ may be either an Integer offset or a Range of offsets (as in
@@ -956,7 +1090,7 @@ struct_entry(VALUE s, long n)
*
* Customer = Struct.new(:name, :address, :zip)
* joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
- * joe.values_at 0, 2 #=> ["Joe Smith", 12345]
+ * joe.values_at(0, 2) #=> ["Joe Smith", 12345]
*
*/
@@ -968,8 +1102,10 @@ rb_struct_values_at(int argc, VALUE *argv, VALUE s)
/*
* call-seq:
- * struct.select {|i| block } -> array
- * struct.select -> an_enumerator
+ * struct.select {|obj| block } -> array
+ * struct.select -> enumerator
+ * struct.filter {|obj| block } -> array
+ * struct.filter -> enumerator
*
* Yields each member value from the struct to the block and returns an Array
* containing the member values from the +struct+ for which the given block
@@ -977,7 +1113,9 @@ rb_struct_values_at(int argc, VALUE *argv, VALUE s)
*
* Lots = Struct.new(:a, :b, :c, :d, :e, :f)
* l = Lots.new(11, 22, 33, 44, 55, 66)
- * l.select {|v| (v % 2).zero? } #=> [22, 44, 66]
+ * l.select {|v| v.even? } #=> [22, 44, 66]
+ *
+ * Struct#filter is an alias for Struct#select.
*/
static VALUE
@@ -1001,15 +1139,12 @@ rb_struct_select(int argc, VALUE *argv, VALUE s)
static VALUE
recursive_equal(VALUE s, VALUE s2, int recur)
{
- const VALUE *ptr, *ptr2;
long i, len;
if (recur) return Qtrue; /* Subtle! */
- ptr = RSTRUCT_CONST_PTR(s);
- ptr2 = RSTRUCT_CONST_PTR(s2);
len = RSTRUCT_LEN(s);
for (i=0; i<len; i++) {
- if (!rb_equal(ptr[i], ptr2[i])) return Qfalse;
+ if (!rb_equal(RSTRUCT_GET(s, i), RSTRUCT_GET(s2, i))) return Qfalse;
}
return Qtrue;
}
@@ -1046,7 +1181,7 @@ rb_struct_equal(VALUE s, VALUE s2)
* call-seq:
* struct.hash -> integer
*
- * Returns a hash value based on this struct's contents (see Object#hash).
+ * Returns a hash value based on this struct's contents.
*
* See also Object#hash.
*/
@@ -1057,13 +1192,11 @@ rb_struct_hash(VALUE s)
long i, len;
st_index_t h;
VALUE n;
- const VALUE *ptr;
h = rb_hash_start(rb_hash(rb_obj_class(s)));
- ptr = RSTRUCT_CONST_PTR(s);
len = RSTRUCT_LEN(s);
for (i = 0; i < len; i++) {
- n = rb_hash(ptr[i]);
+ n = rb_hash(RSTRUCT_GET(s, i));
h = rb_hash_uint(h, NUM2LONG(n));
}
h = rb_hash_end(h);
@@ -1073,15 +1206,12 @@ rb_struct_hash(VALUE s)
static VALUE
recursive_eql(VALUE s, VALUE s2, int recur)
{
- const VALUE *ptr, *ptr2;
long i, len;
if (recur) return Qtrue; /* Subtle! */
- ptr = RSTRUCT_CONST_PTR(s);
- ptr2 = RSTRUCT_CONST_PTR(s2);
len = RSTRUCT_LEN(s);
for (i=0; i<len; i++) {
- if (!rb_eql(ptr[i], ptr2[i])) return Qfalse;
+ if (!rb_eql(RSTRUCT_GET(s, i), RSTRUCT_GET(s2, i))) return Qfalse;
}
return Qtrue;
}
@@ -1126,25 +1256,20 @@ rb_struct_size(VALUE s)
return LONG2FIX(RSTRUCT_LEN(s));
}
-const VALUE*
-rb_struct_ptr(VALUE s)
-{
- return RSTRUCT_CONST_PTR(s);
-}
-
/*
* call-seq:
* struct.dig(key, ...) -> object
*
- * Extracts the nested value specified by the sequence of <i>idx</i>
+ * Extracts the nested value specified by the sequence of +key+
* objects by calling +dig+ at each step, returning +nil+ if any
* intermediate step is +nil+.
*
- * klass = Struct.new(:a)
- * o = klass.new(klass.new({b: [1, 2, 3]}))
+ * Foo = Struct.new(:a)
+ * f = Foo.new(Foo.new({b: [1, 2, 3]}))
*
- * o.dig(:a, :a, :b, 0) #=> 1
- * o.dig(:b, 0) #=> nil
+ * f.dig(:a, :a, :b, 0) # => 1
+ * f.dig(:b, 0) # => nil
+ * f.dig(:a, :a, :b, :c) # TypeError: no implicit conversion of Symbol into Integer
*/
static VALUE
@@ -1158,6 +1283,8 @@ rb_struct_dig(int argc, VALUE *argv, VALUE self)
}
/*
+ * Document-class: Struct
+ *
* A Struct is a convenient way to bundle a number of attributes together,
* using accessor methods, without having to write an explicit class.
*
@@ -1178,7 +1305,7 @@ rb_struct_dig(int argc, VALUE *argv, VALUE self)
* See Struct::new for further examples of creating struct subclasses and
* instances.
*
- * In the method descriptions that follow a "member" parameter refers to a
+ * In the method descriptions that follow, a "member" parameter refers to a
* struct member which is either a quoted string (<code>"name"</code>) or a
* Symbol (<code>:name</code>).
*/
@@ -1211,6 +1338,7 @@ InitVM_Struct(void)
rb_define_method(rb_cStruct, "[]", rb_struct_aref, 1);
rb_define_method(rb_cStruct, "[]=", rb_struct_aset, 2);
rb_define_method(rb_cStruct, "select", rb_struct_select, -1);
+ rb_define_method(rb_cStruct, "filter", rb_struct_select, -1);
rb_define_method(rb_cStruct, "values_at", rb_struct_values_at, -1);
rb_define_method(rb_cStruct, "members", rb_struct_members_m, 0);
@@ -1223,6 +1351,7 @@ Init_Struct(void)
{
id_members = rb_intern("__members__");
id_back_members = rb_intern("__members_back__");
+ id_keyword_init = rb_intern("__keyword_init__");
InitVM(Struct);
}
diff --git a/symbol.c b/symbol.c
index facdb3ce24..a408ee052e 100644
--- a/symbol.c
+++ b/symbol.c
@@ -9,8 +9,9 @@
**********************************************************************/
-#include "internal.h"
+#include "ruby/encoding.h"
#include "ruby/st.h"
+#include "internal.h"
#include "symbol.h"
#include "gc.h"
#include "probes.h"
@@ -18,6 +19,9 @@
#ifndef SYMBOL_DEBUG
# define SYMBOL_DEBUG 0
#endif
+#ifndef CHECK_ID_SERIAL
+# define CHECK_ID_SERIAL SYMBOL_DEBUG
+#endif
#define SYMBOL_PINNED_P(sym) (RSYMBOL(sym)->id&~ID_SCOPE_MASK)
@@ -51,7 +55,7 @@ Init_op_tbl(void)
}
}
-enum {ID_ENTRY_UNIT = 512};
+static const int ID_ENTRY_UNIT = 512;
enum id_entry_type {
ID_ENTRY_STR,
@@ -198,10 +202,46 @@ rb_enc_symname_p(const char *name, rb_encoding *enc)
return rb_enc_symname2_p(name, strlen(name), enc);
}
+static int
+rb_sym_constant_char_p(const char *name, long nlen, rb_encoding *enc)
+{
+ int c, len;
+ const char *end = name + nlen;
+
+ if (nlen < 1) return FALSE;
+ if (ISASCII(*name)) return ISUPPER(*name);
+ c = rb_enc_precise_mbclen(name, end, enc);
+ if (!MBCLEN_CHARFOUND_P(c)) return FALSE;
+ len = MBCLEN_CHARFOUND_LEN(c);
+ c = rb_enc_mbc_to_codepoint(name, end, enc);
+ if (ONIGENC_IS_UNICODE(enc)) {
+ static int ctype_titlecase = 0;
+ if (rb_enc_isupper(c, enc)) return TRUE;
+ if (rb_enc_islower(c, enc)) return FALSE;
+ if (!ctype_titlecase) {
+ static const UChar cname[] = "titlecaseletter";
+ static const UChar *const end = cname + sizeof(cname) - 1;
+ ctype_titlecase = ONIGENC_PROPERTY_NAME_TO_CTYPE(enc, cname, end);
+ }
+ if (rb_enc_isctype(c, ctype_titlecase, enc)) return TRUE;
+ }
+ else {
+ /* fallback to case-folding */
+ OnigUChar fold[ONIGENC_GET_CASE_FOLD_CODES_MAX_NUM];
+ const OnigUChar *beg = (const OnigUChar *)name;
+ int r = enc->mbc_case_fold(ONIGENC_CASE_FOLD,
+ &beg, (const OnigUChar *)end,
+ fold, enc);
+ if (r > 0 && (r != len || memcmp(fold, name, r)))
+ return TRUE;
+ }
+ return FALSE;
+}
+
#define IDSET_ATTRSET_FOR_SYNTAX ((1U<<ID_LOCAL)|(1U<<ID_CONST))
#define IDSET_ATTRSET_FOR_INTERN (~(~0U<<(1<<ID_SCOPE_SHIFT)) & ~(1U<<ID_ATTRSET))
-static int
+int
rb_enc_symname_type(const char *name, long len, rb_encoding *enc, unsigned int allowed_attrset)
{
const char *m = name;
@@ -278,7 +318,7 @@ rb_enc_symname_type(const char *name, long len, rb_encoding *enc, unsigned int a
break;
default:
- type = ISUPPER(*m) ? ID_CONST : ID_LOCAL;
+ type = rb_sym_constant_char_p(m, e-m, enc) ? ID_CONST : ID_LOCAL;
id:
if (m >= e || (*m != '_' && !ISALPHA(*m) && ISASCII(*m))) {
if (len > 1 && *(e-1) == '=') {
@@ -338,20 +378,41 @@ set_id_entry(rb_id_serial_t num, VALUE str, VALUE sym)
}
static VALUE
-get_id_entry(rb_id_serial_t num, const enum id_entry_type t)
+get_id_serial_entry(rb_id_serial_t num, ID id, const enum id_entry_type t)
{
if (num && num <= global_symbols.last_id) {
size_t idx = num / ID_ENTRY_UNIT;
VALUE ids = global_symbols.ids;
VALUE ary;
if (idx < (size_t)RARRAY_LEN(ids) && !NIL_P(ary = rb_ary_entry(ids, (long)idx))) {
- VALUE result = rb_ary_entry(ary, (long)(num % ID_ENTRY_UNIT) * ID_ENTRY_SIZE + t);
- if (!NIL_P(result)) return result;
+ long pos = (long)(num % ID_ENTRY_UNIT) * ID_ENTRY_SIZE;
+ VALUE result = rb_ary_entry(ary, pos + t);
+ if (NIL_P(result)) return 0;
+#if CHECK_ID_SERIAL
+ if (id) {
+ VALUE sym = result;
+ if (t != ID_ENTRY_SYM)
+ sym = rb_ary_entry(ary, pos + ID_ENTRY_SYM);
+ if (STATIC_SYM_P(sym)) {
+ if (STATIC_SYM2ID(sym) != id) return 0;
+ }
+ else {
+ if (RSYMBOL(sym)->id != id) return 0;
+ }
+ }
+#endif
+ return result;
}
}
return 0;
}
+static VALUE
+get_id_entry(ID id, const enum id_entry_type t)
+{
+ return get_id_serial_entry(rb_id_to_serial(id), id, t);
+}
+
static inline ID
#ifdef __GNUC__
__attribute__((unused))
@@ -359,7 +420,7 @@ __attribute__((unused))
rb_id_serial_to_id(rb_id_serial_t num)
{
if (is_notop_id((ID)num)) {
- VALUE sym = get_id_entry(num, ID_ENTRY_SYM);
+ VALUE sym = get_id_serial_entry(num, 0, ID_ENTRY_SYM);
return SYM2ID(sym);
}
else {
@@ -472,7 +533,7 @@ dsymbol_alloc(const VALUE klass, const VALUE str, rb_encoding * const enc, const
const VALUE dsym = rb_newobj_of(klass, T_SYMBOL | FL_WB_PROTECTED);
long hashval;
- rb_enc_associate(dsym, enc);
+ rb_enc_set_index(dsym, rb_enc_to_index(enc));
OBJ_FREEZE(dsym);
RB_OBJ_WRITE(dsym, &RSYMBOL(dsym)->fstr, str);
RSYMBOL(dsym)->id = type;
@@ -547,7 +608,7 @@ lookup_str_sym(const VALUE str)
static VALUE
lookup_id_str(ID id)
{
- return get_id_entry(rb_id_to_serial(id), ID_ENTRY_STR);
+ return get_id_entry(id, ID_ENTRY_STR);
}
ID
@@ -682,7 +743,8 @@ rb_str_intern(VALUE str)
enc = ascii;
}
else {
- str = rb_str_new_frozen(str);
+ str = rb_str_dup(str);
+ OBJ_FREEZE(str);
}
str = rb_fstring(str);
type = rb_str_symname_type(str, IDSET_ATTRSET_FOR_INTERN);
@@ -726,7 +788,7 @@ VALUE
rb_id2sym(ID x)
{
if (!DYNAMIC_ID_P(x)) return STATIC_ID2SYM(x);
- return get_id_entry(rb_id_to_serial(x), ID_ENTRY_SYM);
+ return get_id_entry(x, ID_ENTRY_SYM);
}
@@ -744,15 +806,7 @@ rb_sym2str(VALUE sym)
VALUE
rb_id2str(ID id)
{
- VALUE str;
-
- if ((str = lookup_id_str(id)) != 0) {
- if (RBASIC(str)->klass == 0)
- RBASIC_SET_CLASS_RAW(str, rb_cString);
- return str;
- }
-
- return 0;
+ return lookup_id_str(id);
}
const char *
@@ -1052,6 +1106,12 @@ rb_sym_intern_ascii_cstr(const char *ptr)
return rb_sym_intern_ascii(ptr, strlen(ptr));
}
+VALUE
+rb_to_symbol_type(VALUE obj)
+{
+ return rb_convert_type_with_id(obj, T_SYMBOL, "Symbol", idTo_sym);
+}
+
static ID
attrsetname_to_attr_id(VALUE name)
{
diff --git a/symbol.h b/symbol.h
index 1f0b139811..0d09b207e1 100644
--- a/symbol.h
+++ b/symbol.h
@@ -53,6 +53,10 @@ id_type(ID id)
}
typedef uint32_t rb_id_serial_t;
+static const uint32_t RB_ID_SERIAL_MAX = /* 256M on LP32 */
+ UINT32_MAX >>
+ ((sizeof(ID)-sizeof(rb_id_serial_t))*CHAR_BIT < RUBY_ID_SCOPE_SHIFT ?
+ RUBY_ID_SCOPE_SHIFT : 0);
static inline rb_id_serial_t
rb_id_to_serial(ID id)
@@ -98,7 +102,7 @@ is_global_name_punct(const int c)
return (ruby_global_name_punct_bits[(c - 0x20) / 32] >> (c % 32)) & 1;
}
-ID rb_intern_cstr_without_pindown(const char *, long, rb_encoding *);
+int rb_enc_symname_type(const char *name, long len, rb_encoding *enc, unsigned int allowed_attrset);
RUBY_SYMBOL_EXPORT_BEGIN
diff --git a/template/Doxyfile.tmpl b/template/Doxyfile.tmpl
index 2fb7588b02..0361c48bdd 100644
--- a/template/Doxyfile.tmpl
+++ b/template/Doxyfile.tmpl
@@ -31,11 +31,11 @@ CREATE_SUBDIRS = YES
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
-ABBREVIATE_BRIEF =
+ABBREVIATE_BRIEF =
ALWAYS_DETAILED_SEC = YES
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = YES
-STRIP_FROM_INC_PATH =
+STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = YES
QT_AUTOBRIEF = YES
@@ -43,7 +43,7 @@ MULTILINE_CPP_IS_BRIEF = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 8
-ALIASES =
+ALIASES =
OPTIMIZE_OUTPUT_FOR_C = YES
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
@@ -61,7 +61,7 @@ SYMBOL_CACHE_SIZE = 0
#---------------------------------------------------------------------------
EXTRACT_ALL = YES
EXTRACT_PRIVATE = NO
-EXTRACT_STATIC = YES
+EXTRACT_STATIC = NO
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
@@ -69,7 +69,7 @@ HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = YES
-INTERNAL_DOCS = YES
+INTERNAL_DOCS = NO
CASE_SENSE_NAMES = NO
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = YES
@@ -82,12 +82,12 @@ GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST= YES
-ENABLED_SECTIONS =
+ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
SHOW_FILES = YES
SHOW_NAMESPACES = YES
-LAYOUT_FILE =
+LAYOUT_FILE =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
@@ -97,22 +97,22 @@ WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_FORMAT = "$file:$line: $text"
-WARN_LOGFILE =
+WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.c *.h *.y *.def
RECURSIVE = YES
-EXCLUDE = ext/dl/callback ccan
+EXCLUDE = <%=srcdir%>/ext/dl/callback <%=srcdir%>/ccan <%=srcdir%>/ext/psych/yaml
EXCLUDE_SYMLINKS = YES
EXCLUDE_PATTERNS = *.src doc enc build */ext/-test-/* tmp test yarvtest lib bootstraptest spec .ext .git .svn extconf.h *prelude.c encdb.h transdb.h insns.def
-EXCLUDE_SYMBOLS =
-EXAMPLE_PATH =
-EXAMPLE_PATTERNS =
+EXCLUDE_SYMBOLS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS =
EXAMPLE_RECURSIVE = NO
IMAGE_PATH = <%=srcdir%>/doc/images
-FILTER_PATTERNS =
+FILTER_PATTERNS =
FILTER_SOURCE_FILES = YES
#---------------------------------------------------------------------------
# configuration options related to source browsing
@@ -129,32 +129,32 @@ VERBATIM_HEADERS = YES
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
COLS_IN_ALPHA_INDEX = 5
-IGNORE_PREFIX =
+IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
-HTML_HEADER =
-HTML_FOOTER =
-HTML_STYLESHEET =
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
HTML_DYNAMIC_SECTIONS = NO
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = org.doxygen.Project
GENERATE_HTMLHELP = NO
-CHM_FILE =
-HHC_LOCATION =
+CHM_FILE =
+HHC_LOCATION =
GENERATE_CHI = NO
-CHM_INDEX_ENCODING =
+CHM_INDEX_ENCODING =
BINARY_TOC = NO
TOC_EXPAND = NO
GENERATE_QHP = NO
-QCH_FILE =
+QCH_FILE =
QHP_NAMESPACE = org.doxygen.Project
QHP_VIRTUAL_FOLDER = doc
-QHG_LOCATION =
+QHG_LOCATION =
DISABLE_INDEX = NO
ENUM_VALUES_PER_LINE = 4
GENERATE_TREEVIEW = NO
@@ -169,8 +169,8 @@ LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4wide
-EXTRA_PACKAGES =
-LATEX_HEADER =
+EXTRA_PACKAGES =
+LATEX_HEADER =
PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
@@ -182,8 +182,8 @@ GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
-RTF_STYLESHEET_FILE =
-RTF_EXTENSIONS_FILE =
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
@@ -196,8 +196,8 @@ MAN_LINKS = NO
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
-XML_SCHEMA =
-XML_DTD =
+XML_SCHEMA =
+XML_DTD =
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
@@ -209,7 +209,7 @@ GENERATE_AUTOGEN_DEF = NO
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
-PERLMOD_MAKEVAR_PREFIX =
+PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
@@ -218,15 +218,15 @@ MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH = <%=srcdir%> <%=srcdir%>/include
-INCLUDE_FILE_PATTERNS =
-PREDEFINED =
-EXPAND_AS_DEFINED =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED =
+EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
-TAGFILES =
-GENERATE_TAGFILE =
+TAGFILES =
+GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
PERL_PATH = /usr/bin/perl
@@ -234,11 +234,11 @@ PERL_PATH = /usr/bin/perl
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = NO
-MSCGEN_PATH =
+MSCGEN_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = <%= have_dot %>
DOT_FONTNAME = FreeSans
-DOT_FONTPATH =
+DOT_FONTPATH =
CLASS_GRAPH = NO
COLLABORATION_GRAPH = NO
GROUP_GRAPHS = YES
@@ -252,7 +252,7 @@ GRAPHICAL_HIERARCHY = NO
DIRECTORY_GRAPH = NO
DOT_IMAGE_FORMAT = png
DOT_PATH =
-DOTFILE_DIRS =
+DOTFILE_DIRS =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
diff --git a/template/GNUmakefile.in b/template/GNUmakefile.in
index 8c6733a478..d0444d37e2 100644
--- a/template/GNUmakefile.in
+++ b/template/GNUmakefile.in
@@ -1,6 +1,10 @@
-override MFLAGS := $(filter-out -j%,$(MFLAGS))
-override MAKEFLAGS := $(filter-out -j%,$(MAKEFLAGS))
include Makefile
+
+ifeq ($(HAVE_BASERUBY),yes)
+override REVISION_FORCE := PHONY
+endif
+
+include $(srcdir)/defs/universal.mk
-include uncommon.mk
include $(srcdir)/defs/gmake.mk
diff --git a/template/configure-ext.mk.tmpl b/template/configure-ext.mk.tmpl
new file mode 100644
index 0000000000..438e109eba
--- /dev/null
+++ b/template/configure-ext.mk.tmpl
@@ -0,0 +1,44 @@
+V = 0
+Q1 = $(V:1=)
+Q = $(Q1:0=@)
+ECHO1 = $(V:1=@:)
+ECHO = $(ECHO1:0=@echo)
+
+<%
+srcdir = miniruby = script_args = nil
+opt = OptionParser.new do |o|
+ o.on('--srcdir=SRCDIR') {|v| srcdir = v}
+ o.on('--miniruby=MINIRUBY') {|v| miniruby = v}
+ o.on('--script-args=MINIRUBY') {|v| script_args = v}
+ o.order!(ARGV)
+end
+srcdir ||= File.dirname(File.dirname(__FILE__))
+exts = {}
+[
+ ["exts", "ext", "--extstatic $(EXTSTATIC)"],
+ ["gems", "gems", "--no-extstatic"],
+].each do |t, d, o|
+ exts[t] = [o, Dir.glob("#{srcdir}/#{d}/*/").map {|n| n[(srcdir.size+1)..-2]}]
+end
+%>
+MINIRUBY = <%=miniruby%>
+SCRIPT_ARGS = <%=script_args%>
+EXTMK_ARGS = $(SCRIPT_ARGS) --gnumake=$(gnumake) --extflags="$(EXTLDFLAGS)" \
+ --make-flags="MINIRUBY='$(MINIRUBY)'"
+
+all: exts gems
+exts:
+gems:
+
+% exts.each do |t, (o, dirs)|
+% dirs.each do |d|
+<%=t%>: <%=d%>/exts.mk
+<%=d%>/exts.mk: FORCE
+ $(Q)$(MINIRUBY) $(srcdir)/ext/extmk.rb --make='$(MAKE)' \
+ --command-output=$@ $(EXTMK_ARGS) <%=o%> \
+ -- configure $(@D)
+% end
+% end
+
+.PHONY: FORCE all exts gems
+FORCE:
diff --git a/template/encdb.h.tmpl b/template/encdb.h.tmpl
index 9cbb1f0083..06afb5dbe1 100644
--- a/template/encdb.h.tmpl
+++ b/template/encdb.h.tmpl
@@ -40,19 +40,28 @@ encdirs.each do |encdir|
files[fn] = true
open(File.join(encdir,fn)) do |f|
name = nil
+ skip_ifndef_ruby = false
+ encoding_def = false
f.each_line do |line|
- if (/^OnigEncodingDefine/ =~ line)..(/"(.*?)"/ =~ line)
- if $1
- if name
- lines << %[ENC_SET_BASE("#$1", "#{name}");]
- else
- name = $1
- end
- check_duplication(defs, $1, fn, $.)
- next if BUILTIN_ENCODINGS[name]
- encodings << $1
- count += 1
+ case line
+ when /^#ifndef RUBY/
+ skip_ifndef_ruby = true
+ when /^#endif/
+ skip_ifndef_ruby = false
+ end
+ next if skip_ifndef_ruby
+ encoding_def = true if /^OnigEncodingDefine/ =~ line
+ if encoding_def && /"(.*?)"/ =~ line
+ encoding_def = false
+ if name
+ lines << %[ENC_SET_BASE("#$1", "#{name}");]
+ else
+ name = $1
end
+ check_duplication(defs, $1, fn, $.)
+ next if BUILTIN_ENCODINGS[name]
+ encodings << $1
+ count += 1
else
case line
when /^\s*rb_enc_register\(\s*"([^"]+)"/
diff --git a/template/extinit.c.tmpl b/template/extinit.c.tmpl
new file mode 100644
index 0000000000..7a9c910633
--- /dev/null
+++ b/template/extinit.c.tmpl
@@ -0,0 +1,17 @@
+%# -*- C -*-
+% extinits = ARGV.map {|n| [n[%r[[^/.]+(?=\.[^/]*)?\z]], n]}
+#include "ruby/ruby.h"
+
+#define init(func, name) { \
+ extern void func(void); \
+ ruby_init_ext(name".so", func); \
+}
+
+void ruby_init_ext(const char *name, void (*init)(void));
+
+void Init_ext(void)
+{
+% extinits.each do |f, n|
+ init(Init_<%=f%>, <%=n.dump%>);
+% end
+}
diff --git a/template/exts.mk.tmpl b/template/exts.mk.tmpl
new file mode 100644
index 0000000000..ea1219805b
--- /dev/null
+++ b/template/exts.mk.tmpl
@@ -0,0 +1,159 @@
+# -*- makefile -*-
+V = 0
+Q1 = $(V:1=)
+Q = $(Q1:0=@)
+ECHO1 = $(V:1=@:)
+ECHO = $(ECHO1:0=@echo)
+<%
+require './rbconfig'
+macros = {}
+deps = []
+notes = {}
+rubies = []
+exeext = RbConfig::CONFIG['EXEEXT']
+gnumake = false
+opt = OptionParser.new do |o|
+ o.on('--gnumake=BOOL') {|v| gnumake = v == 'yes'}
+ o.order!(ARGV)
+end
+contpat = /(?>(?>[^\\\n]|\\.)*\\\n)*(?>[^\\\n]|\\.)*/
+Dir.glob("{ext,gems}/*/exts.mk") do |e|
+ gem = /\Agems(?=\/)/ =~ e
+ s = File.read(e)
+ s.scan(/^(extensions|SUBMAKEOPTS|EXT[A-Z]+|MFLAGS|NOTE_[A-Z]+)[ \t]*=[ \t]*(#{contpat})$/o) do |n, v|
+ v.gsub!(/\\\n[ \t]*/, ' ')
+ next if v.empty?
+ next if gem and n != "extensions"
+ v = v.split
+ m = macros[n] ||= []
+ case n
+ when "LIBS"
+ m.concat(v)
+ else
+ macros[n] = m | v
+ end
+ end
+ if gem
+ r = ""
+ else
+ r = s[/^all static: (.+)$/, 1]
+ deps << $&
+ rubies |= r.split if r
+ r = "(?:#{Regexp.new(r)})|"
+ end
+ s.scan(%r"^(ext/\S+)/[^/\s:]+:[ \t]*\1/static$|
+ ^(?:#{r}
+ all|static|install(?:-(?:so|rb))?|
+ (?:dist|real)?clean
+ ):.+$
+ "x) do
+ deps << $&.sub(/ +note$/, '')
+ end
+ s.scan(%r"^(note(?:-\w+)?):(:)?[ \t]*(#{contpat})\n((?:\t.+\n)*)"o) do |(t, m, d, n)|
+ note = (notes[t] ||= [[m||""], []])
+ note[0] |= d.split(/(?:\\\n|[ \t])[ \t]*/)
+ n = n.split(/^/)
+ if m
+ note[1].concat(n)
+ else
+ note[1] |= n
+ end
+ end
+end
+deps.uniq!
+if objs = macros["EXTOBJS"] and objs.any? {|e|e.start_with?("ext/extinit.")}
+ objs.delete_if {|e|e.start_with?("dmyext.")}
+end
+macros.default = [].freeze
+class Array
+ def fold(h, w = 70)
+ return "" if empty?
+ w -= h
+ ret = [s = String.new]
+ each do |e|
+ if s.size + e.size + 1 > w
+ ret << (s = String.new)
+ end
+ s << " " << e
+ end
+ ret.join(" \\\n" + "\t" * (h / 8) + " " * (h % 8))
+ end
+end
+@erbout = _erbout
+def self.column
+ w = 0
+ @erbout[/^.*\z/].scan(/\t|([^\t]+)/) {|s,| w += (s ? s.size : 8 - w % 8)}
+ w
+end
+targets = %w[all static install install-so install-rb clean distclean realclean]
+objext = RbConfig::CONFIG["OBJEXT"]
+if gnumake
+ submake = "$(MAKE) -C $(@D)"
+else
+ submake = "cd $(@D) && "
+ exec = RbConfig::CONFIG["exec"] and !exec.empty? and submake << exec << " "
+ submake << "$(MAKE)"
+ mflags = " $(MFLAGS)"
+end
+-%>
+% macros.each_pair do |k, v|
+<%=k%> =<%= v.fold(column) %>
+% end
+% RbConfig::MAKEFILE_CONFIG.keys.grep(/RM/) do |k|
+<%=k%> = <%=RbConfig::MAKEFILE_CONFIG[k]%>
+% end
+
+all:
+static:
+
+clean:
+ -$(Q)$(RM) ext/extinit.<%= objext %>
+distclean:
+ -$(Q)$(RM) ext/extinit.c
+
+% deps.each do |d|
+<%= d %>
+% end
+
+% rubies.each do |ruby|
+<%= ruby %>:
+ $(Q)$(MAKE)<%=mflags%> $(SUBMAKEOPTS) $@
+% end
+% if rubies.size > 1
+<%= rubies[1..-1].join(' ')%>: <%= rubies[0] %>
+% end
+
+libencs:
+ $(Q)$(MAKE)<%=mflags%> -f enc.mk V=$(V) $@
+ext/extinit.<%=objext%>:
+ $(Q)$(MAKE)<%=mflags%> V=$(V) EXTINITS="$(EXTINITS)" $@
+
+% exts = macros["extensions"].map {|e|e.chomp("/.")}.sort
+% targets.each do |tgt|
+% exts.each do |d|
+% t = "#{d}/#{tgt}"
+% if /^(dist|real)?clean$/ =~ tgt
+% deps = exts.select {|e|e.start_with?("#{d}/")}.map {|e|"#{e}/#{tgt}"}
+% pd = ' ' + deps.join(' ') unless deps.empty?
+% else
+% pext = File.dirname(d)
+% pd = " #{pext}/#{tgt}" if exts.include?(pext)
+% end
+<%=t%>:<%=pd%>
+% if /^(dist|real)clean$/ =~ tgt
+ $(ECHO) $(@F)ing $(@D)
+% end
+ $(Q)<%= submake %><%=mflags%> V=$(V) $(@F)
+% if /^(dist|real)clean$/ =~ tgt
+ $(Q)$(RMDIRS) $(@D)
+% end
+% end
+% end
+
+extso:
+ @echo EXTSO=$(EXTSO)
+
+% notes.each_pair do |k, (d, n)|
+<%= k %>:<%= d.join(' ') %>
+<%= n.join("") %>
+% end
diff --git a/template/fake.rb.in b/template/fake.rb.in
index 5e95530c38..5788af1781 100644
--- a/template/fake.rb.in
+++ b/template/fake.rb.in
@@ -7,12 +7,15 @@ while /\A(\w+)=(.*)/ =~ ARGV[0]
end
if inc = arg['i']
src = inc == '-' ? STDIN.read : File.read(inc)
+ def src.value(name)
+ eval(self[/\bruby_#{name}(?:\[\])?\s*=\s*((?:"(?:\\.|[^\"\\])*"\s*)*(?=;)|[^{};]+)/m, 1].gsub(/#/, '\\#'))
+ end
arg['versions'] = version = {}
File.read(File.join(arg['srcdir'], 'version.c')).
scan(/rb_define_global_const\("(RUBY_\w+)",[^;]*?\bMK(?:INT|STR)\(([^()]*)\)/m) do |n, v|
- version[n] =
- eval(src[/\bruby_#{v}(?:\[\])?\s*=\s*((?:"(?:\\.|[^\"\\])*"\s*)*(?=;)|[^{};]+)/m, 1].gsub(/#/, '\\#'))
+ version[n] = src.value(v)
end
+ arg['RUBY_DESCRIPTION_WITH_JIT'] = src.value('description_with_jit')
end
%>baseruby="<%=arg['BASERUBY']%>"
_\
@@ -29,12 +32,19 @@ class Object
CROSS_COMPILING = RUBY_PLATFORM
constants.grep(/^RUBY_/) {|n| remove_const n}
% arg['versions'].each {|n, v|
- <%=n%> = <%=v.inspect%>
+ <%=n%> = <%if n=='RUBY_DESCRIPTION' %>RubyVM.const_defined?(:MJIT) && RubyVM::MJIT.enabled? ?
+ <%=arg['RUBY_DESCRIPTION_WITH_JIT'].inspect%> :
+ <%end%><%=v.inspect%>
% }
end
builddir = File.dirname(File.expand_path(__FILE__))
srcdir = "<%=arg['srcdir']%>"
top_srcdir = File.realpath(srcdir, builddir)
fake = File.join(top_srcdir, "tool/fake.rb")
-eval(File.read(fake), nil, fake)
-ENV["RUBYOPT"] = ["-r#{__FILE__}", ENV["RUBYOPT"]].compact.join(" ")
+eval(File.binread(fake), nil, fake)
+ropt = "-r#{__FILE__}"
+["RUBYOPT"].each do |flag|
+ opt = ENV[flag]
+ opt = opt ? ([ropt] | opt.b.split).join(" ") : ropt
+ ENV[flag] = opt
+end
diff --git a/template/id.h.tmpl b/template/id.h.tmpl
index ab5c98b6c4..687cbbbe40 100644
--- a/template/id.h.tmpl
+++ b/template/id.h.tmpl
@@ -57,6 +57,11 @@ enum ruby_id_types {
% end
#define RUBY_TOKEN(t) RUBY_TOKEN_##t
+#define RUBY_TOKEN2ID_TYPE(tok, type) ((tok<<RUBY_ID_SCOPE_SHIFT)|type|RUBY_ID_STATIC_SYM)
+% types.each do |type|
+#define TOKEN2<%=type%>ID(tok) RUBY_TOKEN2ID_TYPE(tok, RUBY_ID_<%=type%>)
+% end
+
enum ruby_method_ids {
% ids[:token_op].uniq {|_, op| op}.each do |id, op, token|
id<%=id%> = <%=token ? "RUBY_TOKEN(#{token})" : "'#{op}'"%>,
@@ -66,16 +71,23 @@ enum ruby_method_ids {
id<%=token%>,
% end
tPRESERVED_ID_END,
-% ids.values_at(*types).flatten.each do |token|
+% prev = 'tPRESERVED_ID_END'
+% types.each do |type|
+% tokens = ids[type]
+ tTOKEN_<%=type%>_BEGIN = <%=prev%>-1,
+% tokens.each do |token|
t<%=token%>,
+% end
+% prev = "tTOKEN_#{type}_END"
+ <%=prev%>,
% end
- tNEXT_ID,
+ tNEXT_ID = <%=prev%>,
% types.each do |type|
-% types = ids[type] or next
-% types.empty? and next
-#define TOKEN2<%=type%>ID(n) id##n = ((t##n<<ID_SCOPE_SHIFT)|ID_<%=type%>|ID_STATIC_SYM)
-% types.each do |token|
- TOKEN2<%=type%>ID(<%=token%>),
+% tokens = ids[type]
+#define DEFINE_<%=type%>ID_FROM_TOKEN(n) id##n = TOKEN2<%=type%>ID(t##n)
+% tokens or next
+% tokens.each do |token|
+ DEFINE_<%=type%>ID_FROM_TOKEN(<%=token%>),
% end
% end
tLAST_OP_ID = tPRESERVED_ID_END-1,
diff --git a/template/insns.inc.tmpl b/template/insns.inc.tmpl
deleted file mode 100644
index 203273f92b..0000000000
--- a/template/insns.inc.tmpl
+++ /dev/null
@@ -1,20 +0,0 @@
-/** -*-c-*-
- This file contains YARV instructions list.
-
- ----
- This file is auto generated by insns2vm.rb
- DO NOT TOUCH!
-
- If you want to fix something, you must edit 'template/insns.inc.tmpl'
- or tool/insns2vm.rb
- */
-
-
-/* BIN : Basic Instruction Name */
-#define BIN(n) YARVINSN_##n
-
-enum ruby_vminsn_type {
-<%= insns %>
- VM_INSTRUCTION_SIZE = <%= @insns.size %>
-};
-
diff --git a/template/insns_info.inc.tmpl b/template/insns_info.inc.tmpl
deleted file mode 100644
index 14b4ef50ab..0000000000
--- a/template/insns_info.inc.tmpl
+++ /dev/null
@@ -1,83 +0,0 @@
-/** -*-c-*-
- This file contains instruction information for yarv instruction sequence.
-
- ----
- This file is auto generated by insns2vm.rb
- DO NOT TOUCH!
-
- If you want to fix something, you must edit 'template/insns_info.inc.tmpl'
- or tool/insns2vm.rb
- */
-
-<%= insn_type_chars %>
-
-static const char *const insn_name_info[] = {
-<%= insn_names %>
-};
-
-static const char *const insn_operand_info[] = {
-<%= operands_info %>
-};
-
-static const int insn_len_info[] = {
-<%= operands_num_info %>
-};
-
-#ifdef USE_INSN_RET_NUM
-static const int insn_stack_push_num_info[] = {
-<%= stack_num_info %>
-};
-#endif
-
-#ifdef USE_INSN_STACK_INCREASE
-static int
-insn_stack_increase(int depth, int insn, VALUE *opes)
-{
- switch(insn){
-<%= stack_increase %>
- default:
- rb_bug("insn_sp_increase: unreachable");
- }
- return 0;
-}
-#endif
-
-/* some utilities */
-
-static int
-insn_len(VALUE insn)
-{
- return insn_len_info[(int)insn];
-}
-
-static const char *
-insn_name(VALUE insn)
-{
- return insn_name_info[(int)insn];
-}
-
-static const char *
-insn_op_types(VALUE insn)
-{
- return insn_operand_info[(int)insn];
-}
-
-static int
-insn_op_type(VALUE insn, long pos)
-{
- int len = insn_len(insn) - 1;
- if(pos < len){
- return insn_operand_info[(int)insn][pos];
- }
- else{
- return 0;
- }
-}
-
-#ifdef USE_INSN_RET_NUM
-static int
-insn_ret_num(VALUE insn)
-{
- return insn_stack_push_num_info[(int)insn];
-}
-#endif
diff --git a/template/limits.c.tmpl b/template/limits.c.tmpl
new file mode 100644
index 0000000000..de35354829
--- /dev/null
+++ b/template/limits.c.tmpl
@@ -0,0 +1,97 @@
+%# -*- c -*-
+% limits = %w[
+% FIXNUM
+% CHAR SCHAR UCHAR WCHAR
+% SHRT USHRT
+% INT UINT
+% LONG ULONG
+% LLONG ULLONG
+% INT8 UINT8 INT_LEAST8 UINT_LEAST8 INT_FAST8 UINT_FAST8
+% INT16 UINT16 INT_LEAST16 UINT_LEAST16 INT_FAST16 UINT_FAST16
+% INT32 UINT32 INT_LEAST32 UINT_LEAST32 INT_FAST32 UINT_FAST32
+% INT64 UINT64 INT_LEAST64 UINT_LEAST64 INT_FAST64 UINT_FAST64
+% INT128 UINT128
+% INTMAX UINTMAX
+% INTPTR UINTPTR
+% SSZIE SIZE
+% PTRDIFF
+% ]
+%
+% verbatim_integers = %w[
+% FLT_RADIX
+% FLT_ROUNDS
+% FLT_EVAL_METHOD
+% FLT_MANT_DIG DBL_MANT_DIG LDBL_MANT_DIG
+% FLT_DIG DBL_DIG LDBL_DIG
+% FLT_MIN_EXP DBL_MIN_EXP LDBL_MIN_EXP
+% FLT_MIN_10_EXP DBL_MIN_10_EXP LDBL_MIN_10_EXP
+% FLT_MAX_EXP DBL_MAX_EXP LDBL_MAX_EXP
+% FLT_MAX_10_EXP DBL_MAX_10_EXP LDBL_MAX_10_EXP
+% FLT_DECIMAL_DIG DBL_DECIMAL_DIG LDBL_DECIMAL_DIG DECIMAL_DIG
+% FLT_HAS_SUBNORM DBL_HAS_SUBNORM LDBL_HAS_SUBNORM
+% ]
+%
+% # Beware; Ruby cannot handle LDBL_MAX.
+% verbatim_doubles = %w[
+% FLT_MAX DBL_MAX
+% FLT_EPSILON DBL_EPSILON
+% FLT_MIN DBL_MIN
+% FLT_TRUE_MIN DBL_TRUE_MIN
+% ]
+%
+#include <limits.h>
+#include "ruby/ruby.h"
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_FLOAT_H
+# include <float.h>
+#endif
+
+void
+Init_limits(void)
+{
+ VALUE h = rb_hash_new();
+ rb_define_const(rb_define_module("RbConfig"), "LIMITS", h);
+
+#ifdef HAVE_LONG_LONG
+#ifndef ULLONG_MAX
+#define ULLONG_MAX ((unsigned LONG_LONG)LLONG_MAX*2+1)
+#endif
+#define MAX2NUM(name) ULL2NUM(name ## _MAX)
+#define MIN2NUM(name) LL2NUM(name ## _MIN)
+#else
+#define MAX2NUM(name) ULONG2NUM(name ## _MAX)
+#define MIN2NUM(name) LONG2NUM(name ## _MIN)
+#endif
+#define DEFINE(k, v) rb_hash_aset(h, rb_str_new_cstr(#k), v)
+
+% limits.each do |type|
+#ifdef <%= type %>_MAX
+ DEFINE(<%= type %>_MAX, MAX2NUM(<%= type %>));
+#endif
+#ifdef <%= type %>_MIN
+ DEFINE(<%= type %>_MIN, MIN2NUM(<%= type %>));
+#endif
+% end
+
+% verbatim_integers.each do |name|
+#ifdef <%= name %>
+ DEFINE(<%= name %>, LONG2NUM(<%= name %>));
+#endif
+% end
+
+% verbatim_doubles.each do |name|
+#ifdef <%= name %>
+ DEFINE(<%= name %>, DBL2NUM(<%= name %>));
+#endif
+% end
+
+#undef DEFINE
+#undef MIN2NUM
+#undef MAX2NUM
+ OBJ_FREEZE(h);
+}
diff --git a/template/minsns.inc.tmpl b/template/minsns.inc.tmpl
deleted file mode 100644
index 54b192898f..0000000000
--- a/template/minsns.inc.tmpl
+++ /dev/null
@@ -1,14 +0,0 @@
-/** -*-c-*-
- This file contains YARV instructions list, to define YARVCore::Instructions.
-
- ----
- This file is auto generated by insns2vm.rb
- DO NOT TOUCH!
-
- If you want to fix something, you must edit 'template/minsns.inc.tmpl'
- or tool/insns2vm.rb
- */
-
-<%= defs %>
-
-
diff --git a/template/opt_sc.inc.tmpl b/template/opt_sc.inc.tmpl
deleted file mode 100644
index 41492b2bb6..0000000000
--- a/template/opt_sc.inc.tmpl
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*-c-*- *********************************************************/
-/*******************************************************************/
-/*******************************************************************/
-/**
- This file is for threaded code.
-
- ----
- This file is auto generated by insns2vm.rb
- DO NOT TOUCH!
-
- If you want to fix something, you must edit 'template/opt_sc.inc.tmpl'
- or tool/insns2vm.rb
- */
-
-#define SC_STATE_SIZE 6
-
-#define SCS_XX 1
-#define SCS_AX 2
-#define SCS_BX 3
-#define SCS_AB 4
-#define SCS_BA 5
-
-#define SC_ERROR 0xffffffff
-
-static const VALUE sc_insn_info[][SC_STATE_SIZE] = {
-<%= sc_insn_info %>
-};
-
-static const VALUE sc_insn_next[] = {
-<%= sc_insn_next %>
-};
-
diff --git a/template/optinsn.inc.tmpl b/template/optinsn.inc.tmpl
deleted file mode 100644
index 3c4f732290..0000000000
--- a/template/optinsn.inc.tmpl
+++ /dev/null
@@ -1,30 +0,0 @@
-/* -*-c-*- *********************************************************/
-/*******************************************************************/
-/*******************************************************************/
-/**
- This file is for threaded code.
-
- ----
- This file is auto generated by insns2vm.rb
- DO NOT TOUCH!
-
- If you want to fix something, you must edit 'template/optinsn.inc.tmpl'
- or tool/insns2vm.rb
- */
-
-static INSN *
-insn_operands_unification(INSN *insnobj){
-#ifdef OPT_OPERANDS_UNIFICATION
- /* optimize rule */
- switch(insnobj->insn_id){
-
-<%= rule %>
-
- default:
- /* do nothing */;
- break;
- }
-#endif
- return insnobj;
-}
-
diff --git a/template/optunifs.inc.tmpl b/template/optunifs.inc.tmpl
deleted file mode 100644
index af313a9b45..0000000000
--- a/template/optunifs.inc.tmpl
+++ /dev/null
@@ -1,35 +0,0 @@
-/* -*-c-*- *********************************************************/
-/*******************************************************************/
-/*******************************************************************/
-/**
- This file is for threaded code.
-
- ----
- This file is auto generated by insns2vm.rb
- DO NOT TOUCH!
-
- If you want to fix something, you must edit 'template/optunifs.inc.tmpl'
- or tool/insns2vm.rb
- */
-
-/*
- static const int UNIFIED_insn_name_1[] = {id, size, ...};
- static const int UNIFIED_insn_name_2[] = {id, size, ...};
- ...
-
- static const int *const UNIFIED_insn_name[] = {size,
- UNIFIED_insn_name_1,
- UNIFIED_insn_name_2, ...};
- ...
-
- static const int *const *const unified_insns_data[] = {
- UNIFIED_insn_nameA,
- UNIFIED_insn_nameB, ...};
- */
-
-<%= unif_insns_each %>
-<%= unif_insns %>
-<%= unif_insns_data %>
-
-#undef GET_INSN_NAME
-
diff --git a/template/prelude.c.tmpl b/template/prelude.c.tmpl
index 873146fc39..aa760dc054 100644
--- a/template/prelude.c.tmpl
+++ b/template/prelude.c.tmpl
@@ -6,7 +6,11 @@
# Ruby 1.9 feature should not be used.
class Prelude
+ LINE_LIMIT = 509 # by C89
+
C_ESC = {
+ "/*" => "/\\*",
+ "*/" => "*\\/",
"\\" => "\\\\",
'"' => '\"',
"\n" => '\n',
@@ -17,7 +21,7 @@ class Prelude
C_ESC_PAT = Regexp.union(*C_ESC.keys)
def c_esc(str)
- '"' + str.gsub(C_ESC_PAT) { C_ESC[$&] } + '"'
+ str.gsub(C_ESC_PAT) { C_ESC[$&] }
end
def prelude_base(filename)
filename.chomp(".rb")
@@ -42,7 +46,10 @@ class Prelude
result = [@preludes.size, @vpath.strip(filename), lines, sub]
@vpath.foreach(filename) do |line|
@preludes[filename] ||= result
- line.sub!(/(?:^|\s+)\#(?:$|[#\s].*)/, '')
+ comment = ($1 || '' if line.sub!(/(?:^|\s+)\#(?:$|[#\s](.*))/, ''))
+ if line.size > LINE_LIMIT
+ raise "#{filename}:#{lines.size+1}: too long line"
+ end
line.sub!(/require(_relative)?\s*\(?\s*(["'])(.*?)(?:\.rb)?\2\)?/) do
orig, rel, path = $&, $2, $3
if rel
@@ -57,7 +64,7 @@ class Prelude
orig
end
end
- lines << c_esc(line)
+ lines << [line, comment]
end
result
end
@@ -67,7 +74,7 @@ Prelude.new(output && output[/\w+(?=_prelude.c\b)/] || 'prelude', ARGV, vpath).i
/* -*-c-*-
THIS FILE WAS AUTOGENERATED BY template/prelude.c.tmpl. DO NOT EDIT.
- sources: <%= @preludes.map {|n,*| prelude_base(n)}.join(', ') %>
+ sources: <%= @preludes.map {|n,*| prelude_base(n)}.join(', ') %><%=%>
*/
%unless @preludes.empty?
#include "ruby/ruby.h"
@@ -78,12 +85,36 @@ Prelude.new(output && output[/\w+(?=_prelude.c\b)/] || 'prelude', ARGV, vpath).i
% preludes = @preludes.values.sort
% preludes.each {|i, prelude, lines, sub|
-static const char prelude_name<%=i%><%=%>[] = <%=c_esc(prelude_name(*prelude))%><%=%>;
-static const char prelude_code<%=i%><%=%>[] =
-% lines.each {|line|
-<%=line%><%=%>
+% name = prelude_name(*prelude)
+static const char prelude_name<%=i%><%=%>[] = "<%=c_esc(name)%>";
+static const struct {
+% size = beg = 0
+% lines.each_with_index {|(line, comment), n|
+% if size + line.size < Prelude::LINE_LIMIT
+% size += line.size
+% next
+% end
+ char L<%=beg%><%=%>[<%=size%><%=%>]; /* <%=beg+1%>..<%=n%> */
+% size = line.size
+% beg = n
% }
-;
+% if size > 0
+ char L<%=beg%><%=%>[<%=size%><%=%>]; /* <%=beg+1%>..<%=lines.size+1%> */
+% end
+} prelude_code<%=i%><%=%> = {
+% size = 0
+#line 1 "<%=c_esc(prelude)%>"
+% lines.each_with_index {|(line, comment), n|
+% if size + line.size >= Prelude::LINE_LIMIT
+% size = 0
+,
+#line <%=n+1%> "<%=c_esc(prelude)%>"
+% end
+% size += line.size
+"<%=c_esc(line)%>"<%if comment%>/* <%=c_esc(comment)%> */<%end%>
+% }
+#line <%=_erbout.count("\n")+2%> "<%=@init_name%>.c"
+};
% }
% if @have_sublib
@@ -105,27 +136,39 @@ prelude_prefix_path(VALUE self)
% end
% unless preludes.empty?
+#define PRELUDE_NAME(n) rb_usascii_str_new_static(prelude_name##n, sizeof(prelude_name##n)-1)
+#define PRELUDE_CODE(n) rb_usascii_str_new_static(prelude_code##n.L0, sizeof(prelude_code##n))
+COMPILER_WARNING_PUSH
+#if GCC_VERSION_SINCE(4, 2, 0)
+COMPILER_WARNING_ERROR(-Wmissing-field-initializers)
+#endif
static void
prelude_eval(VALUE code, VALUE name, int line)
{
static const rb_compile_option_t optimization = {
TRUE, /* int inline_const_cache; */
TRUE, /* int peephole_optimization; */
- TRUE, /* int tailcall_optimization; */
+ FALSE,/* int tailcall_optimization; */
TRUE, /* int specialized_instruction; */
TRUE, /* int operands_unification; */
TRUE, /* int instructions_unification; */
TRUE, /* int stack_caching; */
- FALSE, /* int trace_instruction; */
TRUE, /* int frozen_string_literal; */
FALSE, /* int debug_frozen_string_literal; */
+ FALSE, /* unsigned int coverage_enabled; */
+ 0, /* int debug_level; */
};
- NODE *node = rb_parser_compile_string_path(rb_parser_new(), name, code, line);
- if (!node) rb_exc_raise(rb_errinfo());
- rb_iseq_eval(rb_iseq_new_with_opt(node, name, name, Qnil, INT2FIX(line),
+ rb_ast_t *ast = rb_parser_compile_string_path(rb_parser_new(), name, code, line);
+ if (!ast->body.root) {
+ rb_ast_dispose(ast);
+ rb_exc_raise(rb_errinfo());
+ }
+ rb_iseq_eval(rb_iseq_new_with_opt(&ast->body, name, name, Qnil, INT2FIX(line),
NULL, ISEQ_TYPE_TOP, &optimization));
+ rb_ast_dispose(ast);
}
+COMPILER_WARNING_POP
% end
% if @have_sublib
@@ -143,8 +186,8 @@ prelude_require(VALUE self, VALUE nth)
% @preludes.each_value do |i, prelude, lines, sub|
% if sub
case <%=i%><%=%>:
- code = rb_usascii_str_new(prelude_code<%=i%><%=%>, sizeof(prelude_code<%=i%><%=%>) - 1);
- name = rb_usascii_str_new(prelude_name<%=i%><%=%>, sizeof(prelude_name<%=i%><%=%>) - 1);
+ code = PRELUDE_CODE(<%=i%><%=%>);
+ name = PRELUDE_NAME(<%=i%><%=%>);
break;
% end
% end
@@ -176,10 +219,7 @@ Init_<%=@init_name%><%=%>(void)
% end
% preludes.each do |i, prelude, lines, sub|
% next if sub
- prelude_eval(
- rb_usascii_str_new(prelude_code<%=i%><%=%>, sizeof(prelude_code<%=i%><%=%>) - 1),
- rb_usascii_str_new(prelude_name<%=i%><%=%>, sizeof(prelude_name<%=i%><%=%>) - 1),
- INT2FIX(1));
+ prelude_eval(PRELUDE_CODE(<%=i%><%=%>), PRELUDE_NAME(<%=i%><%=%>), 1);
% end
% if @have_sublib
rb_gc_force_recycle(prelude);
@@ -187,7 +227,7 @@ Init_<%=@init_name%><%=%>(void)
#if 0
% preludes.length.times {|i|
- puts(prelude_code<%=i%><%=%>);
+ printf("%.*s", (int)sizeof(prelude_code<%=i%><%=%>), prelude_code<%=i%><%=%>.L0);
% }
#endif
%end
diff --git a/template/ruby-runner.h.in b/template/ruby-runner.h.in
index b0647fd0e1..fdfe88dcb9 100644
--- a/template/ruby-runner.h.in
+++ b/template/ruby-runner.h.in
@@ -1,3 +1,9 @@
+#define ABS_SRCDIR "@abs_srcdir@"
#define BUILDDIR "@abs_top_builddir@"
#define LIBPATHENV "@LIBPATHENV@"
+#define PRELOADENV "@PRELOADENV@"
+#define PATH_SEPARATOR "@PATH_SEPARATOR@"
#define PATH_SEP '@PATH_SEPARATOR@'
+#define EXTOUT "@EXTOUT@"
+#define ARCH "@arch@"
+#define SOEXT "@SOEXT@"
diff --git a/template/ruby.pc.in b/template/ruby.pc.in
index 2b68f59776..7ce4461c05 100644
--- a/template/ruby.pc.in
+++ b/template/ruby.pc.in
@@ -9,6 +9,7 @@ MAJOR=@MAJOR@
MINOR=@MINOR@
TEENY=@TEENY@
ruby_version=@ruby_version@
+RUBY_API_VERSION=@RUBY_API_VERSION@
RUBY_PROGRAM_VERSION=@RUBY_PROGRAM_VERSION@
RUBY_BASE_NAME=@RUBY_BASE_NAME@
RUBY_VERSION_NAME=@RUBY_VERSION_NAME@
@@ -37,6 +38,8 @@ sitehdrdir=@sitehdrdir@
rubyarchhdrdir=@rubyarchhdrdir@
vendorarchhdrdir=@vendorarchhdrdir@
sitearchhdrdir=@sitearchhdrdir@
+MAINLIBS=@MAINLIBS@
+SOEXT=@SOEXT@
LIBPATH=@LIBPATH@
LIBRUBY_A=@LIBRUBY_A@
LIBRUBY_SO=@LIBRUBY_SO@
diff --git a/template/sizes.c.tmpl b/template/sizes.c.tmpl
index 2d23cacace..848a29f4be 100644
--- a/template/sizes.c.tmpl
+++ b/template/sizes.c.tmpl
@@ -25,6 +25,7 @@ conditions = {
#endif
% end
+extern void Init_limits(void);
void
Init_sizeof(void)
{
@@ -46,6 +47,8 @@ Init_sizeof(void)
DEFINE(<%= type %>, <%= type.tr_cpp %>);
#endif
% end
+ OBJ_FREEZE(s);
#undef DEFINE
+ Init_limits();
}
diff --git a/template/transdb.h.tmpl b/template/transdb.h.tmpl
index d0cf101344..16565dd638 100644
--- a/template/transdb.h.tmpl
+++ b/template/transdb.h.tmpl
@@ -36,17 +36,18 @@ transdirs.each do |transdir|
files[fn] = true
path = File.join(transdir,fn)
open(path) do |f|
+ transcoder_def = false
f.each_line do |line|
- if (/^static const rb_transcoder/ =~ line)..(/"(.*?)"\s*,\s*"(.*?)"/ =~ line)
- if $1 && $2
- from_to = "%s to %s" % [$1, $2]
- if converters[from_to]
- raise ArgumentError, '%s:%d: transcode "%s" is already registered at %s:%d' %
- [path, $., from_to, *converters[from_to].values_at(3, 4)]
- else
- converters[from_to] = [$1, $2, fn[0..-3], path, $.]
- converter_list << from_to
- end
+ transcoder_def = true if /^static const rb_transcoder/ =~ line
+ if transcoder_def && /"(.*?)"\s*,\s*"(.*?)"/ =~ line
+ transcoder_def = false
+ from_to = "%s to %s" % [$1, $2]
+ if converters[from_to]
+ raise ArgumentError, '%s:%d: transcode "%s" is already registered at %s:%d' %
+ [path, $., from_to, *converters[from_to].values_at(3, 4)]
+ else
+ converters[from_to] = [$1, $2, fn[0..-3], path, $.]
+ converter_list << from_to
end
end
end
diff --git a/template/unicode_norm_gen.tmpl b/template/unicode_norm_gen.tmpl
index c8d697cb34..c0044dddbc 100644
--- a/template/unicode_norm_gen.tmpl
+++ b/template/unicode_norm_gen.tmpl
@@ -5,9 +5,9 @@
# Script to generate Ruby data structures used in implementing
# String#unicode_normalize,...
-# Constants for input and ouput directory
+# Constants for input and output directory
InputDataDir = ARGV[0] || 'enc/unicode/data'
-unicode_version = InputDataDir[/[\d.]+\z/]
+unicode_version = InputDataDir[/.*\/(\d+\.\d+\.\d+)(?=\/|\z)/, 1]
# convenience methods
class Integer
@@ -42,7 +42,7 @@ end
class Array
def to_UTF8() collect {|c| c.to_UTF8}.join('') end
- def each_regexp_chars(n = 8) # converts an array of Integers to character ranges
+ def each_regexp_chars(n = 1) # converts an array of Integers to character ranges
sort.inject([]) do |ranges, value|
if ranges.last and ranges.last[1]+1>=value
ranges.last[1] = value
@@ -157,7 +157,7 @@ end
# automatically generated by template/unicode_norm_gen.tmpl
-module UnicodeNormalize
+module UnicodeNormalize # :nodoc:
accents = "" \
"[<% accent_array.each_regexp_chars do |rx|%><%=rx%>" \
"<% end%>]"
@@ -193,28 +193,28 @@ module UnicodeNormalize
"<%end%>]"
class_table = {
-% combining_class.each_slice(8) do |slice|
- <% slice.each do |key, value|%> "<%=key.to_UTF8%>"=><%=value%><%=%>,<% end%>
+% combining_class.each do |key, value|
+ "<%=key.to_UTF8%>"=><%=value%><%=%>,
% end
}
class_table.default = 0
CLASS_TABLE = class_table.freeze
DECOMPOSITION_TABLE = {
-% decomposition_table.each_slice(8) do |slice|
- <% slice.each do |key, value|%> "<%=key.to_UTF8%>"=>"<%=value.to_UTF8%>"<%=%>,<% end%>
+% decomposition_table.each do |key, value|
+ "<%=key.to_UTF8%>"=>"<%=value.to_UTF8%>"<%=%>,
% end
}.freeze
KOMPATIBLE_TABLE = {
-% kompatible_table.each_slice(8) do |slice|
- <% slice.each do |key, value|%> "<%=key.to_UTF8%>"=>"<%=value.to_UTF8%>"<%=%>,<% end%>
+% kompatible_table.each do |key, value|
+ "<%=key.to_UTF8%>"=>"<%=value.to_UTF8%>"<%=%>,
% end
}.freeze
COMPOSITION_TABLE = {
-% composition_table.each_slice(8) do |slice|
- <% slice.each do |key, value|%> "<%=key.to_UTF8%>"=>"<%=value.to_UTF8%>"<%=%>,<% end%>
+% composition_table.each do |key, value|
+ "<%=key.to_UTF8%>"=>"<%=value.to_UTF8%>"<%=%>,
% end
}.freeze
end
diff --git a/template/verconf.h.tmpl b/template/verconf.h.tmpl
index 9325aee0ff..9ba2bd6de5 100644
--- a/template/verconf.h.tmpl
+++ b/template/verconf.h.tmpl
@@ -4,7 +4,7 @@
% C = rbconfig::MAKEFILE_CONFIG.dup
% def C.[](name) str = super and (str unless str.empty?); end
#define RUBY_BASE_NAME "${RUBY_BASE_NAME}"
-#define RUBY_VERSION_NAME RUBY_BASE_NAME"-"RUBY_LIB_VERSION
+#define RUBY_VERSION_NAME "${RUBY_VERSION_NAME}"
% if C["RUBY_LIB_VERSION_STYLE"]
#define RUBY_LIB_VERSION_STYLE ${RUBY_LIB_VERSION_STYLE}
% elsif !C["RUBY_LIB_VERSION"]
@@ -21,13 +21,13 @@
#define RUBY_SITEARCH_PREFIX_FOR(arch) "${rubysitearchprefix}"
#define RUBY_LIB "${rubylibdir}"
#define RUBY_ARCH_LIB_FOR(arch) "${rubyarchdir}"
-% if C["sitedir"] == "no"
+% if !C["sitedir"] || C["sitedir"] == "no"
#define NO_RUBY_SITE_LIB 1
% else
#define RUBY_SITE_LIB "${sitedir}"
#define RUBY_SITE_ARCH_LIB_FOR(arch) "${sitearchdir}"
% end
-% if C["vendordir"] == "no"
+% if !C["vendordir"] || C["vendordir"] == "no"
#define NO_RUBY_VENDOR_LIB 1
% else
#define RUBY_VENDOR_LIB "${vendordir}"
diff --git a/template/vm.inc.tmpl b/template/vm.inc.tmpl
deleted file mode 100644
index 11c6d1bcf5..0000000000
--- a/template/vm.inc.tmpl
+++ /dev/null
@@ -1,29 +0,0 @@
-/* -*-c-*- *********************************************************/
-/*******************************************************************/
-/*******************************************************************/
-/**
- This file is VM main loop.
-
- ----
- This file is auto generated by insns2vm.rb
- DO NOT TOUCH!
-
- If you want to fix something, you must edit 'insns.def'
- */
-
-<%=
-line = 15
-vm_body.gsub(/\n|__CURRENT_LINE__|__CURRENT_FILE__/){
- e = $&
- case e
- when '__CURRENT_LINE__'
- line.to_s
- when '__CURRENT_FILE__'
- "vm.inc"
- else
- line += 1
- e
- end
-}
-%>
-
diff --git a/template/vmtc.inc.tmpl b/template/vmtc.inc.tmpl
deleted file mode 100644
index 5f1f51a08b..0000000000
--- a/template/vmtc.inc.tmpl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* -*-c-*- *********************************************************/
-/*******************************************************************/
-/*******************************************************************/
-/**
- This file is for threaded code.
-
- ----
- This file is auto generated by insns2vm.rb
- DO NOT TOUCH!
-
- If you want to fix something, you must edit 'template/vmtc.inc.tmpl'
- or insns2vm.rb
- */
-
-static const void *const insns_address_table[] = {
-<%= insns_table %>
-};
-
diff --git a/template/yasmdata.rb.tmpl b/template/yasmdata.rb.tmpl
deleted file mode 100644
index a11df0a712..0000000000
--- a/template/yasmdata.rb.tmpl
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*-ruby-*-
-#
-
-class VM
- class InstructionSequence
- class Instruction
- InsnID2NO = {
-<%= insn_id2no %>
- }
-
- def self.id2insn_no id
- if InsnID2NO.has_key? id
- InsnID2NO[id]
- end
- end
- end
- end
-end
-
-
diff --git a/test/-ext-/arith_seq/test_arith_seq_extract.rb b/test/-ext-/arith_seq/test_arith_seq_extract.rb
new file mode 100644
index 0000000000..d64fe6a7b5
--- /dev/null
+++ b/test/-ext-/arith_seq/test_arith_seq_extract.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: false
+require 'test/unit'
+
+class Test_ArithSeq < Test::Unit::TestCase
+ def test_extract_with_arith_seq
+ assert_separately([], <<-"end;") #do
+ require '-test-/arith_seq/extract'
+
+ b, e, s, f, r = Enumerator::ArithmeticSequence.__extract__(1.step(10, 2))
+ assert_equal([1, 10, 2, 0, 1], [b, e, s, f, r])
+
+ b, e, s, f, r = Enumerator::ArithmeticSequence.__extract__((1..10) % 2)
+ assert_equal([1, 10, 2, 0, 1], [b, e, s, f, r])
+
+ b, e, s, f, r = Enumerator::ArithmeticSequence.__extract__((1...10) % 2)
+ assert_equal([1, 10, 2, 1, 1], [b, e, s, f, r])
+ end;
+ end
+
+ def test_extract_with_range
+ assert_separately([], <<-"end;") #do
+ require '-test-/arith_seq/extract'
+
+ b, e, s, f, r = Enumerator::ArithmeticSequence.__extract__(1..10)
+ assert_equal([1, 10, 1, 0, 1], [b, e, s, f, r])
+
+ b, e, s, f, r = Enumerator::ArithmeticSequence.__extract__(1...10)
+ assert_equal([1, 10, 1, 1, 1], [b, e, s, f, r])
+ end;
+ end
+
+ def test_extract_with_others
+ assert_separately([], <<-"end;") #do
+ require '-test-/arith_seq/extract'
+
+ b, e, s, f, r = Enumerator::ArithmeticSequence.__extract__(nil)
+ assert_equal([nil, nil, nil, nil, 0], [b, e, s, f, r])
+ end;
+ end
+end
diff --git a/test/-ext-/array/test_resize.rb b/test/-ext-/array/test_resize.rb
index f6a368cb75..c7a57f3cf1 100644
--- a/test/-ext-/array/test_resize.rb
+++ b/test/-ext-/array/test_resize.rb
@@ -2,7 +2,7 @@
require 'test/unit'
require '-test-/array/resize'
-class TestArray < Test::Unit::TestCase
+class Test_Array < Test::Unit::TestCase
class TestResize < Test::Unit::TestCase
def test_expand
feature = '[ruby-dev:42912]'
diff --git a/test/-ext-/bignum/test_big2str.rb b/test/-ext-/bignum/test_big2str.rb
index 296a5a9c49..cc28d97ce5 100644
--- a/test/-ext-/bignum/test_big2str.rb
+++ b/test/-ext-/bignum/test_big2str.rb
@@ -2,7 +2,7 @@
require 'test/unit'
require "-test-/bignum"
-class TestBignum < Test::Unit::TestCase
+class Test_Bignum < Test::Unit::TestCase
class TestBig2str < Test::Unit::TestCase
SIZEOF_BDIGIT = Integer::SIZEOF_BDIGIT
diff --git a/test/-ext-/bignum/test_bigzero.rb b/test/-ext-/bignum/test_bigzero.rb
index 43a99b01a3..cf34964acd 100644
--- a/test/-ext-/bignum/test_bigzero.rb
+++ b/test/-ext-/bignum/test_bigzero.rb
@@ -2,7 +2,7 @@
require 'test/unit'
require "-test-/bignum"
-class TestBignum < Test::Unit::TestCase
+class Test_Bignum < Test::Unit::TestCase
class TestBigZero < Test::Unit::TestCase
def test_equal_0
bug8204 = '[ruby-core:53893] [Bug #8204]'
diff --git a/test/-ext-/bignum/test_div.rb b/test/-ext-/bignum/test_div.rb
index c53332630a..4e4bd3fa3e 100644
--- a/test/-ext-/bignum/test_div.rb
+++ b/test/-ext-/bignum/test_div.rb
@@ -2,7 +2,7 @@
require 'test/unit'
require "-test-/bignum"
-class TestBignum < Test::Unit::TestCase
+class Test_Bignum < Test::Unit::TestCase
class TestDiv < Test::Unit::TestCase
SIZEOF_BDIGIT = Integer::SIZEOF_BDIGIT
diff --git a/test/-ext-/bignum/test_mul.rb b/test/-ext-/bignum/test_mul.rb
index fa974ab55d..5de046a804 100644
--- a/test/-ext-/bignum/test_mul.rb
+++ b/test/-ext-/bignum/test_mul.rb
@@ -2,7 +2,7 @@
require 'test/unit'
require "-test-/bignum"
-class TestBignum < Test::Unit::TestCase
+class Test_Bignum < Test::Unit::TestCase
class TestMul < Test::Unit::TestCase
SIZEOF_BDIGIT = Integer::SIZEOF_BDIGIT
diff --git a/test/-ext-/bignum/test_pack.rb b/test/-ext-/bignum/test_pack.rb
index 04bf3e02de..5d553435fe 100644
--- a/test/-ext-/bignum/test_pack.rb
+++ b/test/-ext-/bignum/test_pack.rb
@@ -4,7 +4,7 @@
require 'test/unit'
require "-test-/bignum"
-class TestBignum < Test::Unit::TestCase
+class Test_Bignum < Test::Unit::TestCase
class TestPack < Test::Unit::TestCase
MSWORD_FIRST = Integer::INTEGER_PACK_MSWORD_FIRST
diff --git a/test/-ext-/bignum/test_str2big.rb b/test/-ext-/bignum/test_str2big.rb
index c14811d9c4..6886e4a968 100644
--- a/test/-ext-/bignum/test_str2big.rb
+++ b/test/-ext-/bignum/test_str2big.rb
@@ -2,7 +2,7 @@
require 'test/unit'
require "-test-/bignum"
-class TestBignum < Test::Unit::TestCase
+class Test_Bignum < Test::Unit::TestCase
class TestStr2big < Test::Unit::TestCase
SIZEOF_BDIGIT = Integer::SIZEOF_BDIGIT
diff --git a/test/-ext-/bug_reporter/test_bug_reporter.rb b/test/-ext-/bug_reporter/test_bug_reporter.rb
index 7bfb660faf..576628d63e 100644
--- a/test/-ext-/bug_reporter/test_bug_reporter.rb
+++ b/test/-ext-/bug_reporter/test_bug_reporter.rb
@@ -4,10 +4,12 @@ require 'tmpdir'
class TestBugReporter < Test::Unit::TestCase
def test_bug_reporter_add
+ description = RUBY_DESCRIPTION
+ description = description.sub(/\+JIT /, '') if RubyVM::MJIT.enabled?
expected_stderr = [
:*,
/\[BUG\]\sSegmentation\sfault.*\n/,
- /#{ Regexp.quote(RUBY_DESCRIPTION) }\n\n/,
+ /#{ Regexp.quote(description) }\n\n/,
:*,
/Sample bug reporter: 12345/,
:*
diff --git a/test/-ext-/debug/test_debug.rb b/test/-ext-/debug/test_debug.rb
index 3804714d0d..bc41c1bb79 100644
--- a/test/-ext-/debug/test_debug.rb
+++ b/test/-ext-/debug/test_debug.rb
@@ -56,4 +56,20 @@ class TestDebug < Test::Unit::TestCase
binds = inspector_in_eval
binds_check binds, bug7635
end
+
+ class MyRelation
+ include Enumerable
+
+ def each
+ yield :each_entry
+ end
+ end
+
+ def test_lazy_block
+ x = MyRelation.new.any? do
+ Bug::Debug.inspector
+ true
+ end
+ assert_equal true, x, '[Bug #15105]'
+ end
end
diff --git a/test/-ext-/debug/test_profile_frames.rb b/test/-ext-/debug/test_profile_frames.rb
index 5ea506046e..0335267ee9 100644
--- a/test/-ext-/debug/test_profile_frames.rb
+++ b/test/-ext-/debug/test_profile_frames.rb
@@ -3,6 +3,16 @@ require 'test/unit'
require '-test-/debug'
class SampleClassForTestProfileFrames
+ class << self
+ attr_accessor :sample4
+ end
+
+ self.sample4 = Module.new do
+ def self.corge(block)
+ Sample2.new.baz(block)
+ end
+ end
+
class Sample2
def baz(block)
instance_eval "def zab(block) block.call end"
@@ -10,8 +20,16 @@ class SampleClassForTestProfileFrames
end
end
+ module Sample3
+ class << self
+ def qux(block)
+ SampleClassForTestProfileFrames.sample4.corge(block)
+ end
+ end
+ end
+
def self.bar(block)
- Sample2.new.baz(block)
+ Sample3.qux(block)
end
def foo(block)
@@ -29,6 +47,8 @@ class TestProfileFrames < Test::Unit::TestCase
"test_profile_frames",
"zab",
"baz",
+ "corge",
+ "qux",
"bar",
"foo",
"test_profile_frames",
@@ -37,6 +57,8 @@ class TestProfileFrames < Test::Unit::TestCase
"test_profile_frames",
"zab",
"baz",
+ "corge",
+ "qux",
"bar",
"foo",
"test_profile_frames",
@@ -45,6 +67,8 @@ class TestProfileFrames < Test::Unit::TestCase
"TestProfileFrames#test_profile_frames",
"#{obj.inspect}.zab",
"SampleClassForTestProfileFrames::Sample2#baz",
+ "#{SampleClassForTestProfileFrames.sample4.inspect}.corge",
+ "SampleClassForTestProfileFrames::Sample3.qux",
"SampleClassForTestProfileFrames.bar",
"SampleClassForTestProfileFrames#foo",
"TestProfileFrames#test_profile_frames",
@@ -53,17 +77,21 @@ class TestProfileFrames < Test::Unit::TestCase
TestProfileFrames,
obj,
SampleClassForTestProfileFrames::Sample2,
+ SampleClassForTestProfileFrames.sample4,
+ SampleClassForTestProfileFrames::Sample3,
SampleClassForTestProfileFrames, # singleton method
SampleClassForTestProfileFrames,
TestProfileFrames,
]
singleton_method_p = [
- false, true, false, true, false, false, false,
+ false, true, false, true, true, true, false, false, false,
]
method_names = [
"test_profile_frames",
"zab",
"baz",
+ "corge",
+ "qux",
"bar",
"foo",
"test_profile_frames",
@@ -72,14 +100,14 @@ class TestProfileFrames < Test::Unit::TestCase
"TestProfileFrames#test_profile_frames",
"#{obj.inspect}.zab",
"SampleClassForTestProfileFrames::Sample2#baz",
+ "#{SampleClassForTestProfileFrames.sample4.inspect}.corge",
+ "SampleClassForTestProfileFrames::Sample3.qux",
"SampleClassForTestProfileFrames.bar",
"SampleClassForTestProfileFrames#foo",
"TestProfileFrames#test_profile_frames",
]
- paths = [ file=__FILE__, "(eval)", file, file, file, file ]
- absolute_paths = [ file, nil, file, file, file, file ]
-
- # pp frames
+ paths = [ file=__FILE__, "(eval)", file, file, file, file, file, file ]
+ absolute_paths = [ file, nil, file, file, file, file, file, file ]
assert_equal(labels.size, frames.size)
diff --git a/test/-ext-/exception/test_data_error.rb b/test/-ext-/exception/test_data_error.rb
index d33d8ca43f..0fec4ac1ec 100644
--- a/test/-ext-/exception/test_data_error.rb
+++ b/test/-ext-/exception/test_data_error.rb
@@ -2,7 +2,7 @@
require 'test/unit'
module Bug
- class TestException < Test::Unit::TestCase
+ class Test_ExceptionDE < Test::Unit::TestCase
def test_cleanup_data_error
bug9167 = '[ruby-core:58643] [Bug #9167]'
assert_normal_exit(<<-'end;', bug9167) # do
diff --git a/test/-ext-/exception/test_enc_raise.rb b/test/-ext-/exception/test_enc_raise.rb
index 2bc7f02413..706f0e2772 100644
--- a/test/-ext-/exception/test_enc_raise.rb
+++ b/test/-ext-/exception/test_enc_raise.rb
@@ -3,7 +3,7 @@ require 'test/unit'
require '-test-/exception'
module Bug
- class TestException < Test::Unit::TestCase
+ class Test_ExceptionER < Test::Unit::TestCase
def test_enc_raise
feature5650 = '[ruby-core:41160]'
Encoding.list.each do |enc|
diff --git a/test/-ext-/exception/test_ensured.rb b/test/-ext-/exception/test_ensured.rb
index 858245868b..c250e46bab 100644
--- a/test/-ext-/exception/test_ensured.rb
+++ b/test/-ext-/exception/test_ensured.rb
@@ -5,7 +5,7 @@ module Bug
class Bug7802 < RuntimeError
end
- class TestException < Test::Unit::TestCase
+ class Test_ExceptionE < Test::Unit::TestCase
def test_ensured
assert_separately([], <<-'end;') # do
diff --git a/test/-ext-/exception/test_exception_at_throwing.rb b/test/-ext-/exception/test_exception_at_throwing.rb
new file mode 100644
index 0000000000..4bce348a25
--- /dev/null
+++ b/test/-ext-/exception/test_exception_at_throwing.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+require 'test/unit'
+
+module Bug
+ class Test_ExceptionAT < Test::Unit::TestCase
+ def test_exception_at_throwing
+ assert_separately(%w[-r-test-/exception], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ e = RuntimeError.new("[Bug #13176]")
+ assert_raise_with_message(e.class, e.message) do
+ catch do |t|
+ Bug::Exception.ensure_raise(nil, e) {throw t}
+ end
+ end
+ end;
+ end
+ end
+end
diff --git a/test/-ext-/funcall/test_funcall.rb b/test/-ext-/funcall/test_funcall.rb
new file mode 100644
index 0000000000..01a03bf5ef
--- /dev/null
+++ b/test/-ext-/funcall/test_funcall.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+require 'test/unit'
+
+class TestFuncall < Test::Unit::TestCase
+ require '-test-/funcall'
+
+ def test_funcall_extra_args
+ assert_equal 'TestFuncall', TestFuncall.extra_args_name,
+ '[ruby-core:85266] [Bug #14425]'
+ end
+end
diff --git a/test/-ext-/gvl/test_last_thread.rb b/test/-ext-/gvl/test_last_thread.rb
index 3b297a5b31..6808e963bf 100644
--- a/test/-ext-/gvl/test_last_thread.rb
+++ b/test/-ext-/gvl/test_last_thread.rb
@@ -3,7 +3,6 @@ class TestLastThread < Test::Unit::TestCase
# [Bug #11237]
def test_last_thread
-
assert_separately([], <<-"end;") #do
require '-test-/gvl/call_without_gvl'
diff --git a/test/-ext-/hash/test_delete.rb b/test/-ext-/hash/test_delete.rb
index e2ad3cbdbc..91e6ff67cb 100644
--- a/test/-ext-/hash/test_delete.rb
+++ b/test/-ext-/hash/test_delete.rb
@@ -2,7 +2,7 @@
require 'test/unit'
require '-test-/hash'
-class TestHash < Test::Unit::TestCase
+class Test_Hash < Test::Unit::TestCase
class TestDelete < Test::Unit::TestCase
def test_delete
hash = Bug::Hash.new
diff --git a/test/-ext-/integer/test_integer.rb b/test/-ext-/integer/test_integer.rb
index 000cb78e4e..8d7785047b 100644
--- a/test/-ext-/integer/test_integer.rb
+++ b/test/-ext-/integer/test_integer.rb
@@ -2,9 +2,9 @@
require 'test/unit'
require '-test-/integer'
-class TestInteger < Test::Unit::TestCase
- FIXNUM_MIN = Integer::FIXNUM_MIN
- FIXNUM_MAX = Integer::FIXNUM_MAX
+class Test_Integer < Test::Unit::TestCase
+ FIXNUM_MIN = RbConfig::LIMITS['FIXNUM_MIN']
+ FIXNUM_MAX = RbConfig::LIMITS['FIXNUM_MAX']
def test_fixnum_range
assert_bignum(FIXNUM_MIN-1)
@@ -12,4 +12,15 @@ class TestInteger < Test::Unit::TestCase
assert_fixnum(FIXNUM_MAX)
assert_bignum(FIXNUM_MAX+1)
end
+
+ def test_positive_pow
+ assert_separately(%w[-r-test-/integer], "#{<<~"begin;"}\n#{<<~'end;'}", timeout: 3)
+ begin;
+ assert_equal(1, 1.positive_pow(1))
+ assert_equal(0, 0.positive_pow(1))
+ assert_equal(3, 3.positive_pow(1))
+ assert_equal(-3, -3.positive_pow(1))
+ assert_equal(9, -3.positive_pow(2))
+ end;
+ end
end
diff --git a/test/-ext-/integer/test_my_integer.rb b/test/-ext-/integer/test_my_integer.rb
index 260986d203..1b6f8489f8 100644
--- a/test/-ext-/integer/test_my_integer.rb
+++ b/test/-ext-/integer/test_my_integer.rb
@@ -2,7 +2,7 @@
require 'test/unit'
require "-test-/integer"
-class TestIntegerExt < Test::Unit::TestCase
+class Test_MyInteger < Test::Unit::TestCase
def test_my_integer_to_f
assert_raise(NotImplementedError) do
Bug::Integer::MyInteger.new.to_f
diff --git a/test/-ext-/iseq_load/test_iseq_load.rb b/test/-ext-/iseq_load/test_iseq_load.rb
index d193e37a4f..f96117b163 100644
--- a/test/-ext-/iseq_load/test_iseq_load.rb
+++ b/test/-ext-/iseq_load/test_iseq_load.rb
@@ -6,7 +6,8 @@ class TestIseqLoad < Test::Unit::TestCase
ISeq = RubyVM::InstructionSequence
def test_bug8543
- assert_iseq_roundtrip <<-'end;'
+ assert_iseq_roundtrip "#{<<~"begin;"}\n#{<<~'end;'}"
+ begin;
puts "tralivali"
def funct(a, b)
a**b
@@ -15,8 +16,35 @@ class TestIseqLoad < Test::Unit::TestCase
end;
end
+ def test_stressful_roundtrip
+ assert_separately(%w[-r-test-/iseq_load], "#{<<~"begin;"}\n#{<<~'end;;'}", timeout: 30)
+ begin;
+ ISeq = RubyVM::InstructionSequence
+ def assert_iseq_roundtrip(src, line=caller_locations(1,1)[0].lineno+1)
+ a = ISeq.compile(src, __FILE__, __FILE__, line).to_a
+ b = ISeq.iseq_load(a).to_a
+ assert_equal a, b, proc {diff(a, b)}
+ b = ISeq.iseq_load(b).to_a
+ assert_equal a, b, proc {diff(a, b)}
+ end
+ def test_bug8543
+ assert_iseq_roundtrip "#{<<~"begin;"}\n#{<<~'end;'}"
+ begin;
+ puts "tralivali"
+ def funct(a, b)
+ a**b
+ end
+ 3.times { |i| puts "Hello, world#{funct(2,i)}!" }
+ end;
+ end
+ GC.stress = true
+ test_bug8543
+ end;;
+ end
+
def test_case_when
- assert_iseq_roundtrip <<-'end;'
+ assert_iseq_roundtrip "#{<<~"begin;"}\n#{<<~'end;'}"
+ begin;
def user_mask(target)
target.each_char.inject(0) do |mask, chr|
case chr
@@ -37,27 +65,34 @@ class TestIseqLoad < Test::Unit::TestCase
end
def test_splatsplat
- assert_iseq_roundtrip('def splatsplat(**); end')
+ assert_iseq_roundtrip("#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ def splatsplat(**); end
+ end;
end
def test_hidden
- assert_iseq_roundtrip('def x(a, (b, *c), d: false); end')
+ assert_iseq_roundtrip("#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ def x(a, (b, *c), d: false); end
+ end;
end
- def assert_iseq_roundtrip(src)
- a = ISeq.compile(src).to_a
+ def assert_iseq_roundtrip(src, line=caller_locations(1,1)[0].lineno+1)
+ a = ISeq.compile(src, __FILE__, __FILE__, line).to_a
b = ISeq.iseq_load(a).to_a
- warn diff(a, b) if a != b
- assert_equal a, b
- assert_equal a, ISeq.iseq_load(b).to_a
+ assert_equal a, b, proc {diff(a, b)}
+ b = ISeq.iseq_load(b).to_a
+ assert_equal a, b, proc {diff(a, b)}
end
def test_next_in_block_in_block
@next_broke = false
- src = <<-'end;'
+ src, line = "#{<<~"begin;"}#{<<~'end;'}", __LINE__+2
+ begin;
3.times { 3.times { next; @next_broke = true } }
end;
- a = ISeq.compile(src).to_a
+ a = ISeq.compile(src, __FILE__, __FILE__, line).to_a
iseq = ISeq.iseq_load(a)
iseq.eval
assert_equal false, @next_broke
@@ -66,7 +101,8 @@ class TestIseqLoad < Test::Unit::TestCase
end
def test_break_ensure
- src = <<-'end;'
+ src, line = "#{<<~"begin;"}#{<<~'end;'}", __LINE__+2
+ begin;
def test_break_ensure_def_method
bad = true
while true
@@ -79,7 +115,7 @@ class TestIseqLoad < Test::Unit::TestCase
bad
end
end;
- a = ISeq.compile(src).to_a
+ a = ISeq.compile(src, __FILE__, __FILE__, line).to_a
iseq = ISeq.iseq_load(a)
iseq.eval
assert_equal false, test_break_ensure_def_method
@@ -88,7 +124,8 @@ class TestIseqLoad < Test::Unit::TestCase
end
def test_kwarg
- assert_iseq_roundtrip <<-'end;'
+ assert_iseq_roundtrip "#{<<~"begin;"}\n#{<<~'end;'}"
+ begin;
def foo(kwarg: :foo)
kwarg
end
@@ -102,16 +139,16 @@ class TestIseqLoad < Test::Unit::TestCase
f = File.expand_path(__FILE__)
# $(top_srcdir)/test/ruby/test_....rb
3.times { f = File.dirname(f) }
- Dir[File.join(f, 'ruby', '*.rb')].each do |f|
- iseq = ISeq.compile_file(f)
- orig = iseq.to_a.freeze
+ all_assertions do |all|
+ Dir[File.join(f, 'ruby', '*.rb')].each do |f|
+ all.for(f) do
+ iseq = ISeq.compile_file(f)
+ orig = iseq.to_a.freeze
- loaded = ISeq.iseq_load(orig).to_a
- if loaded != orig
- warn f
- warn diff(orig, loaded)
+ loaded = ISeq.iseq_load(orig).to_a
+ assert loaded == orig, proc {"ISeq unmatch:\n"+diff(orig, loaded)}
+ end
end
- #assert_equal orig, loaded
end
end
end
diff --git a/test/-ext-/iter/test_yield_block.rb b/test/-ext-/iter/test_yield_block.rb
index d4f1fa3c35..0036854fd4 100644
--- a/test/-ext-/iter/test_yield_block.rb
+++ b/test/-ext-/iter/test_yield_block.rb
@@ -12,6 +12,12 @@ class TestIter::YieldBlock < Test::Unit::TestCase
def test(arg, &block)
block.call(arg) {|blockarg| @blockarg = blockarg}
end
+ def call_proc(&block)
+ block.call {}
+ end
+ def call_lambda(&block)
+ block.call &->{}
+ end
end
def test_yield_block
@@ -19,4 +25,10 @@ class TestIter::YieldBlock < Test::Unit::TestCase
a.yield_block(:test, "foo") {|x, &b| assert_kind_of(Proc, b); b.call(x)}
assert_equal("foo", a.blockarg)
end
+
+ def test_yield_lambda
+ a = YieldTest.new
+ assert_not_predicate a.yield_block(:call_proc) {|&b| b}, :lambda?
+ assert_predicate a.yield_block(:call_lambda) {|&b| b}, :lambda?
+ end
end
diff --git a/test/-ext-/load/script.rb b/test/-ext-/load/script.rb
new file mode 100644
index 0000000000..4bc2480587
--- /dev/null
+++ b/test/-ext-/load/script.rb
@@ -0,0 +1,2 @@
+# frozen_string_literal: true
+raise "foo"
diff --git a/test/-ext-/load/test_protect.rb b/test/-ext-/load/test_protect.rb
new file mode 100644
index 0000000000..83b179b34c
--- /dev/null
+++ b/test/-ext-/load/test_protect.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+require 'test/unit'
+require '-test-/load/protect'
+
+class Test_Load_Protect < Test::Unit::TestCase
+ def test_load_protect
+ assert_raise(LoadError) {
+ Bug.load_protect(__dir__+"/nonexistent.rb")
+ }
+ assert_raise_with_message(RuntimeError, "foo") {
+ Bug.load_protect(__dir__+"/script.rb")
+ }
+ end
+end
diff --git a/test/-ext-/method/test_arity.rb b/test/-ext-/method/test_arity.rb
index 2ba73c8d4c..cb8549e7c9 100644
--- a/test/-ext-/method/test_arity.rb
+++ b/test/-ext-/method/test_arity.rb
@@ -2,7 +2,7 @@
require '-test-/method'
require 'test/unit'
-class TestMethod < Test::Unit::TestCase
+class Test_Method < Test::Unit::TestCase
class TestArity < Test::Unit::TestCase
class A
def foo0()
diff --git a/test/-ext-/num2int/test_num2int.rb b/test/-ext-/num2int/test_num2int.rb
index 8eedc50db1..5d310af40d 100644
--- a/test/-ext-/num2int/test_num2int.rb
+++ b/test/-ext-/num2int/test_num2int.rb
@@ -2,33 +2,29 @@
require 'test/unit'
require '-test-/num2int'
require '-test-/integer'
+require 'rbconfig/sizeof'
class TestNum2int < Test::Unit::TestCase
- SHRT_MIN = -32768
- SHRT_MAX = 32767
- USHRT_MAX = 65535
+ l = RbConfig::LIMITS
- INT_MIN = -2147483648
- INT_MAX = 2147483647
- UINT_MAX = 4294967295
+ SHRT_MIN = l["SHRT_MIN"]
+ SHRT_MAX = l["SHRT_MAX"]
+ USHRT_MAX = l["USHRT_MAX"]
- case [0].pack('L!').size
- when 4
- LONG_MAX = 2147483647
- LONG_MIN = -2147483648
- ULONG_MAX = 4294967295
- when 8
- LONG_MAX = 9223372036854775807
- LONG_MIN = -9223372036854775808
- ULONG_MAX = 18446744073709551615
- end
+ INT_MIN = l["INT_MIN"]
+ INT_MAX = l["INT_MAX"]
+ UINT_MAX = l["UINT_MAX"]
+
+ LONG_MAX = l["LONG_MAX"]
+ LONG_MIN = l["LONG_MIN"]
+ ULONG_MAX = l["ULONG_MAX"]
- LLONG_MAX = 9223372036854775807
- LLONG_MIN = -9223372036854775808
- ULLONG_MAX = 18446744073709551615
+ LLONG_MAX = l["LLONG_MAX"]
+ LLONG_MIN = l["LLONG_MIN"]
+ ULLONG_MAX = l["ULLONG_MAX"]
- FIXNUM_MAX = LONG_MAX/2
- FIXNUM_MIN = LONG_MIN/2
+ FIXNUM_MAX = l["FIXNUM_MAX"]
+ FIXNUM_MIN = l["FIXNUM_MIN"]
def fix2big(n)
10000000000000000000000000000.coerce(n)[0]
diff --git a/test/-ext-/postponed_job/test_postponed_job.rb b/test/-ext-/postponed_job/test_postponed_job.rb
index da3b579eba..978b728ef7 100644
--- a/test/-ext-/postponed_job/test_postponed_job.rb
+++ b/test/-ext-/postponed_job/test_postponed_job.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: false
require 'test/unit'
-require 'thread'
require '-test-/postponed_job'
module Bug
diff --git a/test/-ext-/proc/test_bmethod.rb b/test/-ext-/proc/test_bmethod.rb
index 344f975755..403d99bfd1 100644
--- a/test/-ext-/proc/test_bmethod.rb
+++ b/test/-ext-/proc/test_bmethod.rb
@@ -2,12 +2,12 @@
require 'test/unit'
require '-test-/proc'
-class TestProc < Test::Unit::TestCase
+class Test_Proc < Test::Unit::TestCase
class TestBMethod < Test::Unit::TestCase
end
end
-class TestProc::TestBMethod
+class Test_Proc::TestBMethod
class Base
def foo(*a)
a
diff --git a/test/-ext-/string/test_capacity.rb b/test/-ext-/string/test_capacity.rb
index 48d2deadee..65444123a7 100644
--- a/test/-ext-/string/test_capacity.rb
+++ b/test/-ext-/string/test_capacity.rb
@@ -8,7 +8,7 @@ class Test_StringCapacity < Test::Unit::TestCase
Bug::String.capacity(str)
end
- def test_capacity_embeded
+ def test_capacity_embedded
size = RbConfig::SIZEOF['void*'] * 3 - 1
assert_equal size, capa('foo')
end
@@ -29,4 +29,12 @@ class Test_StringCapacity < Test::Unit::TestCase
assert_equal("", String.new(capacity: -1000))
assert_equal(capa(String.new(capacity: -10000)), capa(String.new(capacity: -1000)))
end
+
+ def test_io_read
+ s = String.new(capacity: 1000)
+ open(__FILE__) {|f|f.read(1024*1024, s)}
+ assert_equal(1024*1024, capa(s))
+ open(__FILE__) {|f|s = f.read(1024*1024)}
+ assert_operator(capa(s), :<=, s.bytesize+4096)
+ end
end
diff --git a/test/-ext-/string/test_enc_associate.rb b/test/-ext-/string/test_enc_associate.rb
index 4fad8e1cc8..95d1f00cd2 100644
--- a/test/-ext-/string/test_enc_associate.rb
+++ b/test/-ext-/string/test_enc_associate.rb
@@ -7,8 +7,8 @@ class Test_StrEncAssociate < Test::Unit::TestCase
s = Bug::String.new("abc")
s.force_encoding(Encoding::US_ASCII)
s.freeze
- assert_raise(RuntimeError) {s.associate_encoding!(Encoding::US_ASCII)}
- assert_raise(RuntimeError) {s.associate_encoding!(Encoding::UTF_8)}
+ assert_raise(FrozenError) {s.associate_encoding!(Encoding::US_ASCII)}
+ assert_raise(FrozenError) {s.associate_encoding!(Encoding::UTF_8)}
end
Encoding.list.select(&:dummy?).each do |enc|
diff --git a/test/-ext-/string/test_external_new.rb b/test/-ext-/string/test_external_new.rb
new file mode 100644
index 0000000000..f8ee773b16
--- /dev/null
+++ b/test/-ext-/string/test_external_new.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: false
+require 'test/unit'
+require '-test-/string'
+
+class Test_StringExternalNew < Test::Unit::TestCase
+ def test_buf_new
+ assert_operator(0, :<=, Bug::String.capacity(Bug::String.buf_new(0)))
+ assert_operator(127, :<=, Bug::String.capacity(Bug::String.buf_new(127)))
+ assert_operator(128, :<=, Bug::String.capacity(Bug::String.buf_new(128)))
+ end
+
+ def test_external_new_with_enc
+ Encoding.list.each do |enc|
+ assert_equal(enc, Bug::String.external_new(0, enc).encoding)
+ end
+ end
+end
diff --git a/test/-ext-/string/test_fstring.rb b/test/-ext-/string/test_fstring.rb
index 1b3b15c922..8b9eca891d 100644
--- a/test/-ext-/string/test_fstring.rb
+++ b/test/-ext-/string/test_fstring.rb
@@ -71,4 +71,13 @@ class Test_String_Fstring < Test::Unit::TestCase
str.freeze
assert_fstring(str) {|s| assert_instance_of(S, s)}
end
+
+ def test_shared_string_safety
+ -('a' * 30).force_encoding(Encoding::ASCII)
+ str = ('a' * 30).force_encoding(Encoding::ASCII).taint
+ frozen_str = Bug::String.rb_str_new_frozen(str)
+ assert_fstring(frozen_str) {|s| assert_equal(str, s)}
+ GC.start
+ assert_equal('a' * 30, str, "[Bug #16151]")
+ end
end
diff --git a/test/-ext-/string/test_modify_expand.rb b/test/-ext-/string/test_modify_expand.rb
index c7093e3892..b7f96426fc 100644
--- a/test/-ext-/string/test_modify_expand.rb
+++ b/test/-ext-/string/test_modify_expand.rb
@@ -16,6 +16,7 @@ class Test_StringModifyExpand < Test::Unit::TestCase
end
def test_integer_overflow
+ return if RbConfig::SIZEOF['size_t'] > RbConfig::SIZEOF['long']
bug12390 = '[ruby-core:75592] [Bug #12390]'
s = Bug::String.new
long_max = (1 << (8 * RbConfig::SIZEOF['long'] - 1)) - 1
diff --git a/test/-ext-/string/test_rb_str_dup.rb b/test/-ext-/string/test_rb_str_dup.rb
new file mode 100644
index 0000000000..49b6af9598
--- /dev/null
+++ b/test/-ext-/string/test_rb_str_dup.rb
@@ -0,0 +1,16 @@
+require 'test/unit'
+require '-test-/string'
+
+class Test_RbStrDup < Test::Unit::TestCase
+ def test_nested_shared_non_frozen
+ str = Bug::String.rb_str_dup(Bug::String.rb_str_dup("a" * 50))
+ assert_send([Bug::String, :shared_string?, str])
+ assert_not_send([Bug::String, :sharing_with_shared?, str], '[Bug #15792]')
+ end
+
+ def test_nested_shared_frozen
+ str = Bug::String.rb_str_dup(Bug::String.rb_str_dup("a" * 50).freeze)
+ assert_send([Bug::String, :shared_string?, str])
+ assert_not_send([Bug::String, :sharing_with_shared?, str], '[Bug #15792]')
+ end
+end
diff --git a/test/-ext-/struct/test_len.rb b/test/-ext-/struct/test_len.rb
new file mode 100644
index 0000000000..2358e340e5
--- /dev/null
+++ b/test/-ext-/struct/test_len.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: false
+require 'test/unit'
+require "-test-/struct"
+
+class Bug::Struct::Test_Len < Test::Unit::TestCase
+ def test_rstruct_len
+ klass = Bug::Struct.new(:a, :b, :c)
+ assert_equal 3, klass.new.rstruct_len
+ end
+end
diff --git a/test/-ext-/test_bug-14834.rb b/test/-ext-/test_bug-14834.rb
new file mode 100644
index 0000000000..a3623b8adc
--- /dev/null
+++ b/test/-ext-/test_bug-14834.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class Test_BUG_14834 < Test::Unit::TestCase
+ def test
+ assert_ruby_status [], <<~'end;', '[ruby-core:87449] [Bug #14834]'
+ require '-test-/bug_14834'
+ Bug.bug_14834 do
+ [123].group_by {}
+ end
+ end;
+ end
+end
diff --git a/test/-ext-/test_notimplement.rb b/test/-ext-/test_notimplement.rb
index 0eba7bdaf8..92a2fd22b8 100644
--- a/test/-ext-/test_notimplement.rb
+++ b/test/-ext-/test_notimplement.rb
@@ -1,15 +1,37 @@
# frozen_string_literal: false
require '-test-/notimplement'
-class TestNotImplement < Test::Unit::TestCase
+class Test_NotImplement < Test::Unit::TestCase
def test_funcall_notimplement
bug3662 = '[ruby-dev:41953]'
assert_raise(NotImplementedError, bug3662) {
Bug.funcall(:notimplement)
}
+ assert_raise(NotImplementedError) {
+ Bug::NotImplement.new.notimplement
+ }
end
def test_respond_to
assert_not_respond_to(Bug, :notimplement)
+ assert_not_respond_to(Bug::NotImplement.new, :notimplement)
+ end
+
+ def test_not_method_defined
+ assert !Bug::NotImplement.method_defined?(:notimplement)
+ assert !Bug::NotImplement.method_defined?(:notimplement, true)
+ assert !Bug::NotImplement.method_defined?(:notimplement, false)
+ end
+
+ def test_not_private_method_defined
+ assert !Bug::NotImplement.private_method_defined?(:notimplement)
+ assert !Bug::NotImplement.private_method_defined?(:notimplement, true)
+ assert !Bug::NotImplement.private_method_defined?(:notimplement, false)
+ end
+
+ def test_not_protected_method_defined
+ assert !Bug::NotImplement.protected_method_defined?(:notimplement)
+ assert !Bug::NotImplement.protected_method_defined?(:notimplement, true)
+ assert !Bug::NotImplement.protected_method_defined?(:notimplement, false)
end
end
diff --git a/test/-ext-/test_printf.rb b/test/-ext-/test_printf.rb
index 4e4f5b4695..feaeadd975 100644
--- a/test/-ext-/test_printf.rb
+++ b/test/-ext-/test_printf.rb
@@ -24,6 +24,8 @@ class Test_SPrintf < Test::Unit::TestCase
assert_equal('["\n"]', Bug::Printf.q("\n"))
assert_equal('[aaa]', Bug::Printf.q('aaa'))
assert_equal('[a a]', Bug::Printf.q('a a'))
+ assert_equal('[]', Bug::Printf.q(''))
+ assert_equal('[""]', Bug::Printf.q(:''))
end
def test_encoding
@@ -184,4 +186,8 @@ class Test_SPrintf < Test::Unit::TestCase
assert_equal(" a", Bug::Printf.("s", "a", width: 3, prec: 3)[0])
assert_equal("a ", Bug::Printf.("s", "a", minus: true, width: 3, prec: 3)[0])
end
+
+ def test_snprintf_count
+ assert_equal(3, Bug::Printf.sncount("foo"))
+ end
end
diff --git a/test/-ext-/test_scan_args.rb b/test/-ext-/test_scan_args.rb
new file mode 100644
index 0000000000..cb2dab5760
--- /dev/null
+++ b/test/-ext-/test_scan_args.rb
@@ -0,0 +1,231 @@
+require 'test/unit'
+require '-test-/scan_args'
+
+class TestScanArgs < Test::Unit::TestCase
+ def test_lead
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead()}
+ assert_equal([1, "a"], Bug::ScanArgs.lead("a"))
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead("a", "b")}
+ end
+
+ def test_opt
+ assert_equal([0, nil], Bug::ScanArgs.opt())
+ assert_equal([1, "a"], Bug::ScanArgs.opt("a"))
+ assert_raise(ArgumentError) {Bug::ScanArgs.opt("a", "b")}
+ end
+
+ def test_lead_opt
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt()}
+ assert_equal([1, "a", nil], Bug::ScanArgs.lead_opt("a"))
+ assert_equal([2, "a", "b"], Bug::ScanArgs.lead_opt("a", "b"))
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt("a", "b", "c")}
+ end
+
+ def test_var
+ assert_equal([0, []], Bug::ScanArgs.var())
+ assert_equal([3, ["a", "b", "c"]], Bug::ScanArgs.var("a", "b", "c"))
+ end
+
+ def test_lead_var
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_var()}
+ assert_equal([3, "a", ["b", "c"]], Bug::ScanArgs.lead_var("a", "b", "c"))
+ end
+
+ def test_opt_var
+ assert_equal([0, nil, []], Bug::ScanArgs.opt_var())
+ assert_equal([3, "a", ["b", "c"]], Bug::ScanArgs.opt_var("a", "b", "c"))
+ end
+
+ def test_lead_opt_var
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_var()}
+ assert_equal([3, "a", "b", ["c"]], Bug::ScanArgs.lead_opt_var("a", "b", "c"))
+ end
+
+ def test_opt_trail
+ assert_raise(ArgumentError) {Bug::ScanArgs.opt_trail()}
+ assert_equal([2, "a", "b"], Bug::ScanArgs.opt_trail("a", "b"))
+ assert_equal([1, nil, "a"], Bug::ScanArgs.opt_trail("a"))
+ assert_raise(ArgumentError) {Bug::ScanArgs.opt_trail("a", "b", "c")}
+ end
+
+ def test_lead_opt_trail
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_trail()}
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_trail("a")}
+ assert_equal([2, "a", nil, "b"], Bug::ScanArgs.lead_opt_trail("a", "b"))
+ assert_equal([3, "a", "b", "c"], Bug::ScanArgs.lead_opt_trail("a", "b", "c"))
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_trail("a", "b", "c", "d")}
+ end
+
+ def test_var_trail
+ assert_raise(ArgumentError) {Bug::ScanArgs.var_trail()}
+ assert_equal([1, [], "a"], Bug::ScanArgs.var_trail("a"))
+ assert_equal([2, ["a"], "b"], Bug::ScanArgs.var_trail("a", "b"))
+ end
+
+ def test_lead_var_trail
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_var_trail("a")}
+ assert_equal([2, "a", [], "b"], Bug::ScanArgs.lead_var_trail("a", "b"))
+ assert_equal([3, "a", ["b"], "c"], Bug::ScanArgs.lead_var_trail("a", "b", "c"))
+ end
+
+ def test_opt_var_trail
+ assert_raise(ArgumentError) {Bug::ScanArgs.opt_var_trail()}
+ assert_equal([1, nil, [], "a"], Bug::ScanArgs.opt_var_trail("a"))
+ assert_equal([2, "a", [], "b"], Bug::ScanArgs.opt_var_trail("a", "b"))
+ assert_equal([3, "a", ["b"], "c"], Bug::ScanArgs.opt_var_trail("a", "b", "c"))
+ end
+
+ def test_lead_opt_var_trail
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_var_trail("a")}
+ assert_equal([2, "a", nil, [], "b"], Bug::ScanArgs.lead_opt_var_trail("a", "b"))
+ assert_equal([3, "a", "b", [], "c"], Bug::ScanArgs.lead_opt_var_trail("a", "b", "c"))
+ assert_equal([4, "a", "b", ["c"], "d"], Bug::ScanArgs.lead_opt_var_trail("a", "b", "c", "d"))
+ end
+
+ def test_hash
+ assert_equal([0, nil], Bug::ScanArgs.hash())
+ assert_raise(ArgumentError) {Bug::ScanArgs.hash("a")}
+ assert_equal([0, {a: 0}], Bug::ScanArgs.hash(a: 0))
+ end
+
+ def test_lead_hash
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_hash()}
+ assert_equal([1, "a", nil], Bug::ScanArgs.lead_hash("a"))
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_hash("a", "b")}
+ assert_equal([1, "a", {b: 1}], Bug::ScanArgs.lead_hash("a", b: 1))
+ assert_equal([1, {b: 1}, nil], Bug::ScanArgs.lead_hash(b: 1))
+ end
+
+ def test_opt_hash
+ assert_equal([0, nil, nil], Bug::ScanArgs.opt_hash())
+ assert_equal([1, "a", nil], Bug::ScanArgs.opt_hash("a"))
+ assert_equal([0, nil, {b: 1}], Bug::ScanArgs.opt_hash(b: 1))
+ assert_equal([1, "a", {b: 1}], Bug::ScanArgs.opt_hash("a", b: 1))
+ assert_raise(ArgumentError) {Bug::ScanArgs.opt_hash("a", "b")}
+ assert_equal([1, {"a"=>0}, {b: 1}], Bug::ScanArgs.opt_hash("a"=>0, b: 1))
+ end
+
+ def test_lead_opt_hash
+ assert_equal([1, "a", nil, nil], Bug::ScanArgs.lead_opt_hash("a"))
+ assert_equal([2, "a", "b", nil], Bug::ScanArgs.lead_opt_hash("a", "b"))
+ assert_equal([1, "a", nil, {c: 1}], Bug::ScanArgs.lead_opt_hash("a", c: 1))
+ assert_equal([2, "a", "b", {c: 1}], Bug::ScanArgs.lead_opt_hash("a", "b", c: 1))
+ assert_equal([1, {c: 1}, nil, nil], Bug::ScanArgs.lead_opt_hash(c: 1))
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_hash("a", "b", "c")}
+ assert_equal([2, "a", {"b"=>0}, {c: 1}], Bug::ScanArgs.lead_opt_hash("a", "b"=>0, c: 1))
+ end
+
+ def test_var_hash
+ assert_equal([0, [], nil], Bug::ScanArgs.var_hash())
+ assert_equal([1, ["a"], nil], Bug::ScanArgs.var_hash("a"))
+ assert_equal([1, ["a"], {b: 1}], Bug::ScanArgs.var_hash("a", b: 1))
+ assert_equal([0, [], {b: 1}], Bug::ScanArgs.var_hash(b: 1))
+ assert_equal([1, [{"a"=>0}], {b: 1}], Bug::ScanArgs.var_hash("a"=>0, b: 1))
+ end
+
+ def test_lead_var_hash
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_var_hash()}
+ assert_equal([1, "a", [], nil], Bug::ScanArgs.lead_var_hash("a"))
+ assert_equal([2, "a", ["b"], nil], Bug::ScanArgs.lead_var_hash("a", "b"))
+ assert_equal([2, "a", ["b"], {c: 1}], Bug::ScanArgs.lead_var_hash("a", "b", c: 1))
+ assert_equal([1, "a", [], {c: 1}], Bug::ScanArgs.lead_var_hash("a", c: 1))
+ assert_equal([1, {c: 1}, [], nil], Bug::ScanArgs.lead_var_hash(c: 1))
+ assert_equal([3, "a", ["b", "c"], nil], Bug::ScanArgs.lead_var_hash("a", "b", "c"))
+ assert_equal([2, "a", [{"b"=>0}], {c: 1}], Bug::ScanArgs.lead_var_hash("a", "b"=>0, c: 1))
+ end
+
+ def test_opt_var_hash
+ assert_equal([0, nil, [], nil], Bug::ScanArgs.opt_var_hash())
+ assert_equal([1, "a", [], nil], Bug::ScanArgs.opt_var_hash("a"))
+ assert_equal([2, "a", ["b"], nil], Bug::ScanArgs.opt_var_hash("a", "b"))
+ assert_equal([2, "a", ["b"], {c: 1}], Bug::ScanArgs.opt_var_hash("a", "b", c: 1))
+ assert_equal([1, "a", [], {c: 1}], Bug::ScanArgs.opt_var_hash("a", c: 1))
+ assert_equal([0, nil, [], {c: 1}], Bug::ScanArgs.opt_var_hash(c: 1))
+ assert_equal([3, "a", ["b", "c"], nil], Bug::ScanArgs.opt_var_hash("a", "b", "c"))
+ assert_equal([2, "a", [{"b"=>0}], {c: 1}], Bug::ScanArgs.opt_var_hash("a", "b"=>0, c: 1))
+ end
+
+ def test_lead_opt_var_hash
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_var_hash()}
+ assert_equal([1, "a", nil, [], nil], Bug::ScanArgs.lead_opt_var_hash("a"))
+ assert_equal([2, "a", "b", [], nil], Bug::ScanArgs.lead_opt_var_hash("a", "b"))
+ assert_equal([2, "a", "b", [], {c: 1}], Bug::ScanArgs.lead_opt_var_hash("a", "b", c: 1))
+ assert_equal([1, "a", nil, [], {c: 1}], Bug::ScanArgs.lead_opt_var_hash("a", c: 1))
+ assert_equal([1, {c: 1}, nil, [], nil], Bug::ScanArgs.lead_opt_var_hash(c: 1))
+ assert_equal([3, "a", "b", ["c"], nil], Bug::ScanArgs.lead_opt_var_hash("a", "b", "c"))
+ assert_equal([3, "a", "b", ["c"], {d: 1}], Bug::ScanArgs.lead_opt_var_hash("a", "b", "c", d: 1))
+ assert_equal([3, "a", "b", [{"c"=>0}], {d: 1}], Bug::ScanArgs.lead_opt_var_hash("a", "b", "c"=>0, d: 1))
+ end
+
+ def test_opt_trail_hash
+ assert_raise(ArgumentError) {Bug::ScanArgs.opt_trail_hash()}
+ assert_equal([1, nil, "a", nil], Bug::ScanArgs.opt_trail_hash("a"))
+ assert_equal([2, "a", "b", nil], Bug::ScanArgs.opt_trail_hash("a", "b"))
+ assert_equal([1, nil, "a", {c: 1}], Bug::ScanArgs.opt_trail_hash("a", c: 1))
+ assert_equal([2, "a", "b", {c: 1}], Bug::ScanArgs.opt_trail_hash("a", "b", c: 1))
+ assert_equal([1, nil, {c: 1}, nil], Bug::ScanArgs.opt_trail_hash(c: 1))
+ assert_raise(ArgumentError) {Bug::ScanArgs.opt_trail_hash("a", "b", "c")}
+ assert_equal([2, "a", {"b"=>0}, {c: 1}], Bug::ScanArgs.opt_trail_hash("a", "b"=>0, c: 1))
+ end
+
+ def test_lead_opt_trail_hash
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_trail_hash()}
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_trail_hash("a")}
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_trail_hash(c: 1)}
+ assert_equal([2, "a", nil, "b", nil], Bug::ScanArgs.lead_opt_trail_hash("a", "b"))
+ assert_equal([2, "a", nil, {c: 1}, nil], Bug::ScanArgs.lead_opt_trail_hash("a", c: 1))
+ assert_equal([2, "a", nil, "b", {c: 1}], Bug::ScanArgs.lead_opt_trail_hash("a", "b", c: 1))
+ assert_equal([3, "a", "b", "c", nil], Bug::ScanArgs.lead_opt_trail_hash("a", "b", "c"))
+ assert_equal([3, "a", "b", "c", {c: 1}], Bug::ScanArgs.lead_opt_trail_hash("a", "b", "c", c: 1))
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_trail_hash("a", "b", "c", "d")}
+ assert_equal([3, "a", "b", {"c"=>0}, {c: 1}], Bug::ScanArgs.lead_opt_trail_hash("a", "b", "c"=>0, c: 1))
+ end
+
+ def test_var_trail_hash
+ assert_raise(ArgumentError) {Bug::ScanArgs.var_trail_hash()}
+ assert_equal([1, [], "a", nil], Bug::ScanArgs.var_trail_hash("a"))
+ assert_equal([2, ["a"], "b", nil], Bug::ScanArgs.var_trail_hash("a", "b"))
+ assert_equal([1, [], "a", {c: 1}], Bug::ScanArgs.var_trail_hash("a", c: 1))
+ assert_equal([2, ["a"], "b", {c: 1}], Bug::ScanArgs.var_trail_hash("a", "b", c: 1))
+ assert_equal([1, [], {c: 1}, nil], Bug::ScanArgs.var_trail_hash(c: 1))
+ assert_equal([3, ["a", "b"], "c", nil], Bug::ScanArgs.var_trail_hash("a", "b", "c"))
+ assert_equal([3, ["a", "b"], "c", {c: 1}], Bug::ScanArgs.var_trail_hash("a", "b", "c", c: 1))
+ assert_equal([3, ["a", "b"], {"c"=>0}, {c: 1}], Bug::ScanArgs.var_trail_hash("a", "b", "c"=>0, c: 1))
+ end
+
+ def test_lead_var_trail_hash
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_var_trail_hash()}
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_var_trail_hash("a")}
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_var_trail_hash(c: 1)}
+ assert_equal([2, "a", [], {c: 1}, nil], Bug::ScanArgs.lead_var_trail_hash("a", c: 1))
+ assert_equal([2, "a", [], "b", nil], Bug::ScanArgs.lead_var_trail_hash("a", "b"))
+ assert_equal([2, "a", [], "b", {c: 1}], Bug::ScanArgs.lead_var_trail_hash("a", "b", c: 1))
+ assert_equal([3, "a", ["b"], "c", nil], Bug::ScanArgs.lead_var_trail_hash("a", "b", "c"))
+ assert_equal([3, "a", ["b"], "c", {c: 1}], Bug::ScanArgs.lead_var_trail_hash("a", "b", "c", c: 1))
+ assert_equal([3, "a", ["b"], {"c"=>0}, {c: 1}], Bug::ScanArgs.lead_var_trail_hash("a", "b", c: 1, "c"=>0))
+ end
+
+ def test_opt_var_trail_hash
+ assert_raise(ArgumentError) {Bug::ScanArgs.opt_var_trail_hash()}
+ assert_equal([1, nil, [], "a", nil], Bug::ScanArgs.opt_var_trail_hash("a"))
+ assert_equal([1, nil, [], {c: 1}, nil], Bug::ScanArgs.opt_var_trail_hash(c: 1))
+ assert_equal([1, nil, [], "a", {c: 1}], Bug::ScanArgs.opt_var_trail_hash("a", c: 1))
+ assert_equal([2, "a", [], "b", nil], Bug::ScanArgs.opt_var_trail_hash("a", "b"))
+ assert_equal([2, "a", [], "b", {c: 1}], Bug::ScanArgs.opt_var_trail_hash("a", "b", c: 1))
+ assert_equal([3, "a", ["b"], "c", nil], Bug::ScanArgs.opt_var_trail_hash("a", "b", "c"))
+ assert_equal([3, "a", ["b"], "c", {c: 1}], Bug::ScanArgs.opt_var_trail_hash("a", "b", "c", c: 1))
+ assert_equal([3, "a", ["b"], {"c"=>0}, {c: 1}], Bug::ScanArgs.opt_var_trail_hash("a", "b", "c"=>0, c: 1))
+ end
+
+ def test_lead_opt_var_trail_hash
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_var_trail_hash()}
+ assert_raise(ArgumentError) {Bug::ScanArgs.lead_opt_var_trail_hash("a")}
+ assert_equal([2, "a", nil, [], {b: 1}, nil], Bug::ScanArgs.lead_opt_var_trail_hash("a", b: 1))
+ assert_equal([2, "a", nil, [], "b", nil], Bug::ScanArgs.lead_opt_var_trail_hash("a", "b"))
+ assert_equal([2, "a", nil, [], "b", {c: 1}], Bug::ScanArgs.lead_opt_var_trail_hash("a", "b", c: 1))
+ assert_equal([3, "a", "b", [], "c", nil], Bug::ScanArgs.lead_opt_var_trail_hash("a", "b", "c"))
+ assert_equal([3, "a", "b", [], "c", {c: 1}], Bug::ScanArgs.lead_opt_var_trail_hash("a", "b", "c", c: 1))
+ assert_equal([4, "a", "b", ["c"], "d", nil], Bug::ScanArgs.lead_opt_var_trail_hash("a", "b", "c", "d"))
+ assert_equal([4, "a", "b", ["c"], {"d"=>0}, {c: 1}], Bug::ScanArgs.lead_opt_var_trail_hash("a", "b", "c", "d"=>0, c: 1))
+ end
+end
diff --git a/test/-ext-/thread_fd_close/test_thread_fd_close.rb b/test/-ext-/thread_fd_close/test_thread_fd_close.rb
new file mode 100644
index 0000000000..e6c3895da7
--- /dev/null
+++ b/test/-ext-/thread_fd_close/test_thread_fd_close.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+require 'test/unit'
+require '-test-/thread_fd_close'
+require 'io/wait'
+
+class TestThreadFdClose < Test::Unit::TestCase
+
+ def test_thread_fd_close
+ IO.pipe do |r, w|
+ th = Thread.new do
+ begin
+ assert_raise(IOError) {
+ r.read(4)
+ }
+ ensure
+ w.syswrite('done')
+ end
+ end
+ Thread.pass until th.stop?
+ IO.thread_fd_close(r.fileno)
+ assert_equal 'done', r.read(4)
+ th.join
+ end
+ end
+end
diff --git a/test/-ext-/typeddata/test_typeddata.rb b/test/-ext-/typeddata/test_typeddata.rb
index daeda7611a..e32b030a35 100644
--- a/test/-ext-/typeddata/test_typeddata.rb
+++ b/test/-ext-/typeddata/test_typeddata.rb
@@ -17,4 +17,13 @@ class Test_TypedData < Test::Unit::TestCase
obj = eval("class C\u{1f5ff}; self; end").new
assert_raise_with_message(TypeError, /C\u{1f5ff}/) {Bug::TypedData.check(obj)}
end
+
+ def test_deferred_free
+ assert_ruby_status([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ require "-test-/typeddata"
+ begin;
+ n = 1 << 20
+ Bug::TypedData.make(n)
+ end;
+ end
end
diff --git a/test/-ext-/wait_for_single_fd/test_wait_for_single_fd.rb b/test/-ext-/wait_for_single_fd/test_wait_for_single_fd.rb
index d7bc827a6e..777e9d14dd 100644
--- a/test/-ext-/wait_for_single_fd/test_wait_for_single_fd.rb
+++ b/test/-ext-/wait_for_single_fd/test_wait_for_single_fd.rb
@@ -4,28 +4,28 @@ require 'test/unit'
class TestWaitForSingleFD < Test::Unit::TestCase
require '-test-/wait_for_single_fd'
- def with_pipe
- r, w = IO.pipe
- begin
- yield r, w
- ensure
- r.close unless r.closed?
- w.close unless w.closed?
- end
- end
-
def test_wait_for_valid_fd
- with_pipe do |r,w|
+ IO.pipe do |r,w|
rc = IO.wait_for_single_fd(w.fileno, RB_WAITFD_OUT, nil)
assert_equal RB_WAITFD_OUT, rc
end
end
def test_wait_for_invalid_fd
+ # Negative FDs should not cause NoMemoryError or segfault when
+ # using select(). For now, match the poll() implementation
+ # used on Linux, which sleeps the given amount of time given
+ # when fd is negative (as documented in the Linux poll(2) manpage)
+ assert_equal 0, IO.wait_for_single_fd(-999, RB_WAITFD_IN, 0)
+ assert_equal 0, IO.wait_for_single_fd(-1, RB_WAITFD_OUT, 0)
+
# FreeBSD 8.2 or prior sticks this
# http://bugs.ruby-lang.org/issues/5524
- skip if /freebsd[1-8]/ =~ RUBY_PLATFORM
- with_pipe do |r,w|
+ if /freebsd([\d\.]+)/ =~ RUBY_PLATFORM
+ ver = $1.to_r
+ skip 'FreeBSD <= 8.2' if ver <= 8.2r
+ end
+ IO.pipe do |r,w|
wfd = w.fileno
w.close
assert_raise(Errno::EBADF) do
@@ -35,12 +35,15 @@ class TestWaitForSingleFD < Test::Unit::TestCase
end
def test_wait_for_closed_pipe
- with_pipe do |r,w|
+ IO.pipe do |r,w|
w.close
rc = IO.wait_for_single_fd(r.fileno, RB_WAITFD_IN, nil)
assert_equal RB_WAITFD_IN, rc
end
end
-
+ def test_wait_for_kqueue
+ skip 'no kqueue' unless IO.respond_to?(:kqueue_test_wait)
+ assert_equal RB_WAITFD_IN, IO.kqueue_test_wait
+ end
end
diff --git a/test/-ext-/win32/test_console_attr.rb b/test/-ext-/win32/test_console_attr.rb
index e596e13469..35d425ded7 100644
--- a/test/-ext-/win32/test_console_attr.rb
+++ b/test/-ext-/win32/test_console_attr.rb
@@ -5,6 +5,17 @@ if /mswin|mingw/ =~ RUBY_PLATFORM and STDOUT.tty?
require 'test/unit'
class Test_Win32Console < Test::Unit::TestCase
+ REVERSE_VIDEO = Bug::Win32::REVERSE_VIDEO
+
+ def reverse_video(fore, back = 0x0)
+ info = STDOUT.console_info
+ if (info.attr & REVERSE_VIDEO) == 0
+ (fore << 4) | back
+ else
+ (back << 4) | fore | REVERSE_VIDEO
+ end
+ end
+
def reset
STDOUT.console_attribute(7)
end
@@ -20,7 +31,7 @@ if /mswin|mingw/ =~ RUBY_PLATFORM and STDOUT.tty?
def test_reverse
print "\e[7m"
info = STDOUT.console_info
- assert_equal(0x70, info.attr);
+ assert_equal(reverse_video(0x7), info.attr);
end
def test_bold
@@ -32,13 +43,13 @@ if /mswin|mingw/ =~ RUBY_PLATFORM and STDOUT.tty?
def test_bold_reverse
print "\e[1;7m"
info = STDOUT.console_info
- assert_equal(0xf0, info.attr);
+ assert_equal(reverse_video(0xf), info.attr);
end
def test_reverse_bold
print "\e[7;1m"
info = STDOUT.console_info
- assert_equal(0xf0, info.attr);
+ assert_equal(reverse_video(0xf), info.attr);
end
end
end
diff --git a/test/-ext-/win32/test_dln.rb b/test/-ext-/win32/test_dln.rb
index 3624346a56..e8f68ac4d4 100644
--- a/test/-ext-/win32/test_dln.rb
+++ b/test/-ext-/win32/test_dln.rb
@@ -11,7 +11,7 @@ module Bug
so = ::File.expand_path("../ext/-test-/win32/dln/dlntest.dll", ::EnvUtil.rubybin)
assert_send([::File, :file?, so])
path = ::ENV['PATH']
- path = ::File.dirname(so) + ::RbConfig::CONFIG["PATH_SEPARATOR"] + path
+ path = ::File.dirname(so) + ::File::PATH_SEPARATOR + path
assert_in_out_err([{'PATH'=>path}, '-r-test-/win32/dln', '-eexit'], '', [], [], bug, timeout: 10)
end
diff --git a/test/base64/test_base64.rb b/test/base64/test_base64.rb
index 8e11bef9a6..ce716043a8 100644
--- a/test/base64/test_base64.rb
+++ b/test/base64/test_base64.rb
@@ -1,5 +1,5 @@
# coding: US-ASCII
-# frozen_string_literal: false
+# frozen_string_literal: true
require "test/unit"
require "base64"
diff --git a/test/benchmark/test_benchmark.rb b/test/benchmark/test_benchmark.rb
index 2db148304c..655c6946e7 100644
--- a/test/benchmark/test_benchmark.rb
+++ b/test/benchmark/test_benchmark.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'benchmark'
diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb
index 63a3e2747d..e4f14449a1 100644
--- a/test/bigdecimal/test_bigdecimal.rb
+++ b/test/bigdecimal/test_bigdecimal.rb
@@ -2,8 +2,6 @@
require_relative "testbase"
require 'bigdecimal/math'
-require 'thread'
-
class TestBigDecimal < Test::Unit::TestCase
include TestBigDecimalBase
@@ -46,52 +44,92 @@ class TestBigDecimal < Test::Unit::TestCase
end
def test_not_equal
- assert_not_equal BigDecimal("1"), BigDecimal.allocate
+ assert_not_equal BigDecimal("1"), BigDecimal("2")
end
- def test_global_new
+ def test_BigDecimal
assert_equal(1, BigDecimal("1"))
assert_equal(1, BigDecimal("1", 1))
+ assert_equal(1, BigDecimal(" 1 "))
+ assert_equal(111, BigDecimal("1_1_1_"))
+ assert_equal(10**(-1), BigDecimal("1E-1"), '#4825')
+ assert_equal(1234, BigDecimal(" \t\n\r \r1234 \t\n\r \r"))
+
assert_raise(ArgumentError) { BigDecimal("1", -1) }
- assert_raise(ArgumentError) { BigDecimal(4.2) }
- begin
- BigDecimal(4.2)
- rescue ArgumentError => error
- assert_match(/Float/, error.message)
+ assert_raise_with_message(ArgumentError, /"1__1_1"/) { BigDecimal("1__1_1") }
+ assert_raise_with_message(ArgumentError, /"_1_1_1"/) { BigDecimal("_1_1_1") }
+
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ assert_positive_infinite(BigDecimal("Infinity"))
+ assert_positive_infinite(BigDecimal("1E1111111111111111111"))
+ assert_positive_infinite(BigDecimal(" \t\n\r \rInfinity \t\n\r \r"))
+ assert_negative_infinite(BigDecimal("-Infinity"))
+ assert_negative_infinite(BigDecimal(" \t\n\r \r-Infinity \t\n\r \r"))
+ assert_nan(BigDecimal("NaN"))
+ assert_nan(BigDecimal(" \t\n\r \rNaN \t\n\r \r"))
end
- assert_raise(ArgumentError) { BigDecimal(42.quo(7)) }
- begin
- BigDecimal(42.quo(7))
- rescue ArgumentError => error
- assert_match(/Rational/, error.message)
+ end
+
+ def test_BigDecimal_with_invalid_string
+ [
+ '', '.', 'e1', 'd1', '.e', '.d', '1.e', '1.d', '.1e', '.1d',
+ '2,30', '19,000.0', '-2,30', '-19,000.0', '+2,30', '+19,000.0',
+ '2.3,0', '19.000,0', '-2.3,0', '-19.000,0', '+2.3,0', '+19.000,0',
+ '2.3.0', '19.000.0', '-2.3.0', '-19.000.0', '+2.3.0', '+19.000.0',
+ 'invlaid value', '123 xyz'
+ ].each do |invalid_string|
+ assert_raise_with_message(ArgumentError, %Q[invalid value for BigDecimal(): "#{invalid_string}"]) do
+ BigDecimal(invalid_string)
+ end
+ end
+
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ assert_raise_with_message(ArgumentError, /"Infinity_"/) { BigDecimal("Infinity_") }
+ assert_raise_with_message(ArgumentError, /"\+Infinity_"/) { BigDecimal("+Infinity_") }
+ assert_raise_with_message(ArgumentError, /"-Infinity_"/) { BigDecimal("-Infinity_") }
+ assert_raise_with_message(ArgumentError, /"NaN_"/) { BigDecimal("NaN_") }
end
end
- def test_global_new_with_integer
+ def test_BigDecimal_with_integer
assert_equal(BigDecimal("1"), BigDecimal(1))
assert_equal(BigDecimal("-1"), BigDecimal(-1))
assert_equal(BigDecimal((2**100).to_s), BigDecimal(2**100))
assert_equal(BigDecimal((-2**100).to_s), BigDecimal(-2**100))
end
- def test_global_new_with_rational
+ def test_BigDecimal_with_rational
assert_equal(BigDecimal("0.333333333333333333333"), BigDecimal(1.quo(3), 21))
assert_equal(BigDecimal("-0.333333333333333333333"), BigDecimal(-1.quo(3), 21))
- assert_raise(ArgumentError) { BigDecimal(1.quo(3)) }
+ assert_raise_with_message(ArgumentError, "can't omit precision for a Rational.") { BigDecimal(42.quo(7)) }
end
- def test_global_new_with_float
+ def test_BigDecimal_with_float
assert_equal(BigDecimal("0.1235"), BigDecimal(0.1234567, 4))
assert_equal(BigDecimal("-0.1235"), BigDecimal(-0.1234567, 4))
- assert_raise(ArgumentError) { BigDecimal(0.1) }
+ assert_raise_with_message(ArgumentError, "can't omit precision for a Float.") { BigDecimal(4.2) }
assert_raise(ArgumentError) { BigDecimal(0.1, Float::DIG + 2) }
assert_nothing_raised { BigDecimal(0.1, Float::DIG + 1) }
bug9214 = '[ruby-core:58858]'
assert_equal(BigDecimal(-0.0, Float::DIG).sign, -1, bug9214)
+
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ assert_nan(BigDecimal(Float::NAN))
+ end
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ assert_positive_infinite(BigDecimal(Float::INFINITY))
+ assert_negative_infinite(BigDecimal(-Float::INFINITY))
+ end
end
- def test_global_new_with_big_decimal
+ def test_BigDecimal_with_big_decimal
assert_equal(BigDecimal(1), BigDecimal(BigDecimal(1)))
assert_equal(BigDecimal('+0'), BigDecimal(BigDecimal('+0')))
assert_equal(BigDecimal('-0'), BigDecimal(BigDecimal('-0')))
@@ -104,68 +142,91 @@ class TestBigDecimal < Test::Unit::TestCase
end
end
- def test_global_new_with_tainted_string
+ def test_BigDecimal_with_tainted_string
Thread.new {
$SAFE = 1
BigDecimal('1'.taint)
}.join
+ ensure
+ $SAFE = 0
end
- def test_new
- assert_equal(1, BigDecimal.new("1"))
- assert_equal(1, BigDecimal.new("1", 1))
- assert_equal(1, BigDecimal.new(" 1 "))
- assert_equal(111, BigDecimal.new("1_1_1_"))
- assert_equal(0, BigDecimal.new("_1_1_1"))
- assert_equal(10**(-1), BigDecimal.new("1E-1"), '#4825')
-
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_equal( 1, BigDecimal.new("Infinity").infinite?)
- assert_equal(-1, BigDecimal.new("-Infinity").infinite?)
- assert_equal(true, BigDecimal.new("NaN").nan?)
- assert_equal( 1, BigDecimal.new("1E1111111111111111111").infinite?)
- end
-
- def test_new_with_integer
- assert_equal(BigDecimal("1"), BigDecimal.new(1))
- assert_equal(BigDecimal("-1"), BigDecimal.new(-1))
- assert_equal(BigDecimal((2**100).to_s), BigDecimal.new(2**100))
- assert_equal(BigDecimal((-2**100).to_s), BigDecimal.new(-2**100))
- end
-
- def test_new_with_rational
- assert_equal(BigDecimal("0.333333333333333333333"), BigDecimal.new(1.quo(3), 21))
- assert_equal(BigDecimal("-0.333333333333333333333"), BigDecimal.new(-1.quo(3), 21))
- assert_raise(ArgumentError) { BigDecimal.new(1.quo(3)) }
+ def test_BigDecimal_with_exception_keyword
+ assert_raise(ArgumentError) {
+ BigDecimal('.', exception: true)
+ }
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(nil, BigDecimal(".", exception: false))
+ }
+ assert_raise(ArgumentError) {
+ BigDecimal("1", -1, exception: true)
+ }
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(nil, BigDecimal("1", -1, exception: false))
+ }
+ assert_raise(ArgumentError) {
+ BigDecimal(42.quo(7), exception: true)
+ }
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(nil, BigDecimal(42.quo(7), exception: false))
+ }
+ assert_raise(ArgumentError) {
+ BigDecimal(4.2, exception: true)
+ }
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(nil, BigDecimal(4.2, exception: false))
+ }
+ # TODO: support conversion from complex
+ # assert_raise(RangeError) {
+ # BigDecimal(1i, exception: true)
+ # }
+ # assert_nothing_raised(RangeError) {
+ # assert_equal(nil, BigDecimal(1i, exception: false))
+ # }
+ assert_raise(TypeError) {
+ BigDecimal(nil, exception: true)
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, BigDecimal(nil, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, BigDecimal(:test, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, BigDecimal(Object.new, exception: false))
+ }
+ # TODO: support to_d
+ # assert_nothing_raised(TypeError) {
+ # o = Object.new
+ # def o.to_d; 3.14; end
+ # assert_equal(3.14, BigDecimal(o, exception: false))
+ # }
+ # assert_nothing_raised(RuntimeError) {
+ # o = Object.new
+ # def o.to_d; raise; end
+ # assert_equal(nil, BigDecimal(o, exception: false))
+ # }
end
- def test_new_with_float
- assert_equal(BigDecimal("0.1235"), BigDecimal(0.1234567, 4))
- assert_equal(BigDecimal("-0.1235"), BigDecimal(-0.1234567, 4))
- assert_raise(ArgumentError) { BigDecimal.new(0.1) }
- assert_raise(ArgumentError) { BigDecimal.new(0.1, Float::DIG + 2) }
- assert_nothing_raised { BigDecimal.new(0.1, Float::DIG + 1) }
+ def test_s_ver
+ assert_raise_with_message(NoMethodError, /undefined method `ver'/) { BigDecimal.ver }
end
- def test_new_with_big_decimal
- assert_equal(BigDecimal(1), BigDecimal.new(BigDecimal(1)))
- assert_equal(BigDecimal('+0'), BigDecimal.new(BigDecimal('+0')))
- assert_equal(BigDecimal('-0'), BigDecimal.new(BigDecimal('-0')))
- BigDecimal.save_exception_mode do
- BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_positive_infinite(BigDecimal.new(BigDecimal('Infinity')))
- assert_negative_infinite(BigDecimal.new(BigDecimal('-Infinity')))
- assert_nan(BigDecimal(BigDecimal.new('NaN')))
- end
+ def test_s_allocate
+ assert_raise_with_message(NoMethodError, /undefined method `allocate'/) { BigDecimal.allocate }
end
- def test_new_with_tainted_string
- Thread.new {
- $SAFE = 1
- BigDecimal.new('1'.taint)
- }.join
+ def test_s_new
+ # TODO: BigDecimal.new will be removed on 1.5
+ # assert_raise_with_message(NoMethodError, /undefined method `new'/) { BigDecimal.new("1") }
+ verbose, $VERBOSE = $VERBOSE, nil
+ assert_equal(BigDecimal(1), BigDecimal.new(1))
+ assert_raise(ArgumentError) { BigDecimal.new(',', exception: true) }
+ assert_nothing_raised { assert_equal(nil, BigDecimal.new(',', exception: false)) }
+ assert_raise(TypeError) { BigDecimal.new(nil, exception: true) }
+ assert_nothing_raised { assert_equal(nil, BigDecimal.new(nil, exception: false)) }
+ ensure
+ $VERBOSE = verbose
end
def _test_mode(type)
@@ -266,16 +327,16 @@ class TestBigDecimal < Test::Unit::TestCase
end
def test_exception_nan
- _test_mode(BigDecimal::EXCEPTION_NaN) { BigDecimal.new("NaN") }
+ _test_mode(BigDecimal::EXCEPTION_NaN) { BigDecimal("NaN") }
end
def test_exception_infinity
- _test_mode(BigDecimal::EXCEPTION_INFINITY) { BigDecimal.new("Infinity") }
+ _test_mode(BigDecimal::EXCEPTION_INFINITY) { BigDecimal("Infinity") }
end
def test_exception_underflow
_test_mode(BigDecimal::EXCEPTION_UNDERFLOW) do
- x = BigDecimal.new("0.1")
+ x = BigDecimal("0.1")
100.times do
x *= x
end
@@ -284,7 +345,7 @@ class TestBigDecimal < Test::Unit::TestCase
def test_exception_overflow
_test_mode(BigDecimal::EXCEPTION_OVERFLOW) do
- x = BigDecimal.new("10")
+ x = BigDecimal("10")
100.times do
x *= x
end
@@ -293,17 +354,17 @@ class TestBigDecimal < Test::Unit::TestCase
def test_exception_zerodivide
BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- _test_mode(BigDecimal::EXCEPTION_ZERODIVIDE) { 1 / BigDecimal.new("0") }
- _test_mode(BigDecimal::EXCEPTION_ZERODIVIDE) { -1 / BigDecimal.new("0") }
+ _test_mode(BigDecimal::EXCEPTION_ZERODIVIDE) { 1 / BigDecimal("0") }
+ _test_mode(BigDecimal::EXCEPTION_ZERODIVIDE) { -1 / BigDecimal("0") }
end
def test_round_up
- n4 = BigDecimal.new("4") # n4 / 9 = 0.44444...
- n5 = BigDecimal.new("5") # n5 / 9 = 0.55555...
- n6 = BigDecimal.new("6") # n6 / 9 = 0.66666...
+ n4 = BigDecimal("4") # n4 / 9 = 0.44444...
+ n5 = BigDecimal("5") # n5 / 9 = 0.55555...
+ n6 = BigDecimal("6") # n6 / 9 = 0.66666...
m4, m5, m6 = -n4, -n5, -n6
- n2h = BigDecimal.new("2.5")
- n3h = BigDecimal.new("3.5")
+ n2h = BigDecimal("2.5")
+ n3h = BigDecimal("3.5")
m2h, m3h = -n2h, -n3h
BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_UP)
@@ -392,14 +453,28 @@ class TestBigDecimal < Test::Unit::TestCase
end
def test_zero_p
- assert_equal(true, BigDecimal.new("0").zero?)
- assert_equal(false, BigDecimal.new("1").zero?)
- assert_equal(true, BigDecimal.new("0E200000000000000").zero?)
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+
+ assert_equal(true, BigDecimal("0").zero?)
+ assert_equal(true, BigDecimal("-0").zero?)
+ assert_equal(false, BigDecimal("1").zero?)
+ assert_equal(true, BigDecimal("0E200000000000000").zero?)
+ assert_equal(false, BigDecimal("Infinity").zero?)
+ assert_equal(false, BigDecimal("-Infinity").zero?)
+ assert_equal(false, BigDecimal("NaN").zero?)
end
def test_nonzero_p
- assert_equal(nil, BigDecimal.new("0").nonzero?)
- assert_equal(BigDecimal.new("1"), BigDecimal.new("1").nonzero?)
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+
+ assert_equal(nil, BigDecimal("0").nonzero?)
+ assert_equal(nil, BigDecimal("-0").nonzero?)
+ assert_equal(BigDecimal("1"), BigDecimal("1").nonzero?)
+ assert_positive_infinite(BigDecimal("Infinity").nonzero?)
+ assert_negative_infinite(BigDecimal("-Infinity").nonzero?)
+ assert_nan(BigDecimal("NaN").nonzero?)
end
def test_double_fig
@@ -407,8 +482,8 @@ class TestBigDecimal < Test::Unit::TestCase
end
def test_cmp
- n1 = BigDecimal.new("1")
- n2 = BigDecimal.new("2")
+ n1 = BigDecimal("1")
+ n2 = BigDecimal("2")
assert_equal( 0, n1 <=> n1)
assert_equal( 1, n2 <=> n1)
assert_equal(-1, n1 <=> n2)
@@ -421,16 +496,16 @@ class TestBigDecimal < Test::Unit::TestCase
assert_operator(n2, :>=, n1)
assert_operator(n1, :>=, n1)
- assert_operator(BigDecimal.new("-0"), :==, BigDecimal.new("0"))
- assert_operator(BigDecimal.new("0"), :<, BigDecimal.new("1"))
- assert_operator(BigDecimal.new("1"), :>, BigDecimal.new("0"))
- assert_operator(BigDecimal.new("1"), :>, BigDecimal.new("-1"))
- assert_operator(BigDecimal.new("-1"), :<, BigDecimal.new("1"))
- assert_operator(BigDecimal.new((2**100).to_s), :>, BigDecimal.new("1"))
- assert_operator(BigDecimal.new("1"), :<, BigDecimal.new((2**100).to_s))
+ assert_operator(BigDecimal("-0"), :==, BigDecimal("0"))
+ assert_operator(BigDecimal("0"), :<, BigDecimal("1"))
+ assert_operator(BigDecimal("1"), :>, BigDecimal("0"))
+ assert_operator(BigDecimal("1"), :>, BigDecimal("-1"))
+ assert_operator(BigDecimal("-1"), :<, BigDecimal("1"))
+ assert_operator(BigDecimal((2**100).to_s), :>, BigDecimal("1"))
+ assert_operator(BigDecimal("1"), :<, BigDecimal((2**100).to_s))
BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- inf = BigDecimal.new("Infinity")
+ inf = BigDecimal("Infinity")
assert_operator(inf, :>, 1)
assert_operator(1, :<, inf)
@@ -453,35 +528,25 @@ class TestBigDecimal < Test::Unit::TestCase
end
def test_cmp_nan
- n1 = BigDecimal.new("1")
+ n1 = BigDecimal("1")
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_equal(nil, BigDecimal.new("NaN") <=> n1)
- assert_equal(false, BigDecimal.new("NaN") > n1)
- end
-
- def test_cmp_float_nan
- assert_equal(nil, BigDecimal.new("1") <=> Float::NAN)
- end
-
- def test_cmp_float_pos_inf
- assert_equal(-1, BigDecimal.new("1") <=> Float::INFINITY)
- end
-
- def test_cmp_float_neg_inf
- assert_equal(+1, BigDecimal.new("1") <=> -Float::INFINITY)
+ assert_equal(nil, BigDecimal("NaN") <=> n1)
+ assert_equal(false, BigDecimal("NaN") > n1)
+ assert_equal(nil, BigDecimal("NaN") <=> BigDecimal("NaN"))
+ assert_equal(false, BigDecimal("NaN") == BigDecimal("NaN"))
end
def test_cmp_failing_coercion
- n1 = BigDecimal.new("1")
+ n1 = BigDecimal("1")
assert_equal(nil, n1 <=> nil)
assert_raise(ArgumentError){n1 > nil}
end
def test_cmp_coerce
- n1 = BigDecimal.new("1")
- n2 = BigDecimal.new("2")
- o1 = Object.new; def o1.coerce(x); [x, BigDecimal.new("1")]; end
- o2 = Object.new; def o2.coerce(x); [x, BigDecimal.new("2")]; end
+ n1 = BigDecimal("1")
+ n2 = BigDecimal("2")
+ o1 = Object.new; def o1.coerce(x); [x, BigDecimal("1")]; end
+ o2 = Object.new; def o2.coerce(x); [x, BigDecimal("2")]; end
assert_equal( 0, n1 <=> o1)
assert_equal( 1, n2 <=> o1)
assert_equal(-1, n1 <=> o2)
@@ -493,19 +558,24 @@ class TestBigDecimal < Test::Unit::TestCase
assert_operator(n2, :>, o1)
assert_operator(n2, :>=, o1)
assert_operator(n1, :>=, 1)
+
+ bug10109 = '[ruby-core:64190]'
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ assert_operator(BigDecimal(0), :<, Float::INFINITY, bug10109)
+ assert_operator(Float::INFINITY, :>, BigDecimal(0), bug10109)
end
def test_cmp_bignum
- assert_operator(BigDecimal.new((2**100).to_s), :==, 2**100)
+ assert_operator(BigDecimal((2**100).to_s), :==, 2**100)
end
def test_cmp_data
d = Time.now; def d.coerce(x); [x, x]; end
- assert_operator(BigDecimal.new((2**100).to_s), :==, d)
+ assert_operator(BigDecimal((2**100).to_s), :==, d)
end
def test_precs
- a = BigDecimal.new("1").precs
+ a = BigDecimal("1").precs
assert_instance_of(Array, a)
assert_equal(2, a.size)
assert_kind_of(Integer, a[0])
@@ -514,7 +584,7 @@ class TestBigDecimal < Test::Unit::TestCase
def test_hash
a = []
- b = BigDecimal.new("1")
+ b = BigDecimal("1")
10.times { a << b *= 10 }
h = {}
a.each_with_index {|x, i| h[x] = i }
@@ -536,7 +606,7 @@ class TestBigDecimal < Test::Unit::TestCase
BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, false)
- x = BigDecimal.new("0")
+ x = BigDecimal("0")
assert_equal(true, x.finite?)
assert_equal(nil, x.infinite?)
assert_equal(false, x.nan?)
@@ -560,15 +630,15 @@ class TestBigDecimal < Test::Unit::TestCase
BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- x = BigDecimal.new("0")
+ x = BigDecimal("0")
assert_kind_of(Integer, x.to_i)
assert_equal(0, x.to_i)
assert_raise(FloatDomainError){( 1 / x).to_i}
assert_raise(FloatDomainError){(-1 / x).to_i}
assert_raise(FloatDomainError) {( 0 / x).to_i}
- x = BigDecimal.new("1")
+ x = BigDecimal("1")
assert_equal(1, x.to_i)
- x = BigDecimal.new((2**100).to_s)
+ x = BigDecimal((2**100).to_s)
assert_equal(2**100, x.to_i)
end
@@ -577,18 +647,18 @@ class TestBigDecimal < Test::Unit::TestCase
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, false)
- x = BigDecimal.new("0")
+ x = BigDecimal("0")
assert_instance_of(Float, x.to_f)
assert_equal(0.0, x.to_f)
assert_equal( 1.0 / 0.0, ( 1 / x).to_f)
assert_equal(-1.0 / 0.0, (-1 / x).to_f)
- assert_equal(true, ( 0 / x).to_f.nan?)
- x = BigDecimal.new("1")
+ assert_nan(( 0 / x).to_f)
+ x = BigDecimal("1")
assert_equal(1.0, x.to_f)
- x = BigDecimal.new((2**100).to_s)
+ x = BigDecimal((2**100).to_s)
assert_equal((2**100).to_f, x.to_f)
- x = BigDecimal.new("1" + "0" * 10000)
- assert_equal(0, BigDecimal.new("-0").to_f)
+ x = BigDecimal("1" + "0" * 10000)
+ assert_equal(0, BigDecimal("-0").to_f)
BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, true)
assert_raise(FloatDomainError) { x.to_f }
@@ -632,11 +702,27 @@ class TestBigDecimal < Test::Unit::TestCase
assert_equal(-0.0, BigDecimal('-10e-325').to_f)
end
+ def test_to_r
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+
+ x = BigDecimal("0")
+ assert_kind_of(Rational, x.to_r)
+ assert_equal(0, x.to_r)
+ assert_raise(FloatDomainError) {( 1 / x).to_r}
+ assert_raise(FloatDomainError) {(-1 / x).to_r}
+ assert_raise(FloatDomainError) {( 0 / x).to_r}
+
+ assert_equal(1, BigDecimal("1").to_r)
+ assert_equal(Rational(3, 2), BigDecimal("1.5").to_r)
+ assert_equal((2**100).to_r, BigDecimal((2**100).to_s).to_r)
+ end
+
def test_coerce
- a, b = BigDecimal.new("1").coerce(1.0)
+ a, b = BigDecimal("1").coerce(1.0)
assert_instance_of(BigDecimal, a)
assert_instance_of(BigDecimal, b)
- assert_equal(2, 1 + BigDecimal.new("1"), '[ruby-core:25697]')
+ assert_equal(2, 1 + BigDecimal("1"), '[ruby-core:25697]')
a, b = BigDecimal("1").coerce(1.quo(10))
assert_equal(BigDecimal("0.1"), a, '[ruby-core:34318]')
@@ -645,65 +731,101 @@ class TestBigDecimal < Test::Unit::TestCase
assert_equal(BigDecimal("0." + "3"*a.precs[0]), a)
assert_nothing_raised(TypeError, '#7176') do
- BigDecimal.new('1') + Rational(1)
+ BigDecimal('1') + Rational(1)
end
end
def test_uplus
- x = BigDecimal.new("1")
+ x = BigDecimal("1")
assert_equal(x, x.send(:+@))
end
+ def test_neg
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+
+ assert_equal(BigDecimal("-1"), BigDecimal("1").send(:-@))
+ assert_equal(BigDecimal("-0"), BigDecimal("0").send(:-@))
+ assert_equal(BigDecimal("0"), BigDecimal("-0").send(:-@))
+ assert_equal(BigDecimal("-Infinity"), BigDecimal("Infinity").send(:-@))
+ assert_equal(BigDecimal("Infinity"), BigDecimal("-Infinity").send(:-@))
+ assert_equal(true, BigDecimal("NaN").send(:-@).nan?)
+ end
+
def test_add
- x = BigDecimal.new("1")
- assert_equal(BigDecimal.new("2"), x + x)
- assert_equal(1, BigDecimal.new("0") + 1)
+ x = BigDecimal("1")
+ assert_equal(BigDecimal("2"), x + x)
+ assert_equal(1, BigDecimal("0") + 1)
assert_equal(1, x + 0)
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal.new("0") + 0).sign)
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal.new("-0") + 0).sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (BigDecimal.new("-0") + BigDecimal.new("-0")).sign)
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal("0") + 0).sign)
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal("-0") + 0).sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (BigDecimal("-0") + BigDecimal("-0")).sign)
+
+ x = BigDecimal((2**100).to_s)
+ assert_equal(BigDecimal((2**100+1).to_s), x + 1)
- x = BigDecimal.new((2**100).to_s)
- assert_equal(BigDecimal.new((2**100+1).to_s), x + 1)
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ inf = BigDecimal("Infinity")
+ neginf = BigDecimal("-Infinity")
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true)
+ assert_raise_with_message(FloatDomainError, "Computation results to 'Infinity'") { inf + inf }
+ assert_raise_with_message(FloatDomainError, "Computation results to '-Infinity'") { neginf + neginf }
end
def test_sub
- x = BigDecimal.new("1")
- assert_equal(BigDecimal.new("0"), x - x)
- assert_equal(-1, BigDecimal.new("0") - 1)
+ x = BigDecimal("1")
+ assert_equal(BigDecimal("0"), x - x)
+ assert_equal(-1, BigDecimal("0") - 1)
assert_equal(1, x - 0)
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal.new("0") - 0).sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (BigDecimal.new("-0") - 0).sign)
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal.new("-0") - BigDecimal.new("-0")).sign)
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal("0") - 0).sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (BigDecimal("-0") - 0).sign)
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal("-0") - BigDecimal("-0")).sign)
+
+ x = BigDecimal((2**100).to_s)
+ assert_equal(BigDecimal((2**100-1).to_s), x - 1)
- x = BigDecimal.new((2**100).to_s)
- assert_equal(BigDecimal.new((2**100-1).to_s), x - 1)
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ inf = BigDecimal("Infinity")
+ neginf = BigDecimal("-Infinity")
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true)
+ assert_raise_with_message(FloatDomainError, "Computation results to 'Infinity'") { inf - neginf }
+ assert_raise_with_message(FloatDomainError, "Computation results to '-Infinity'") { neginf - inf }
end
def test_sub_with_float
- assert_kind_of(BigDecimal, BigDecimal.new("3") - 1.0)
+ assert_kind_of(BigDecimal, BigDecimal("3") - 1.0)
end
def test_sub_with_rational
- assert_kind_of(BigDecimal, BigDecimal.new("3") - 1.quo(3))
+ assert_kind_of(BigDecimal, BigDecimal("3") - 1.quo(3))
end
def test_mult
- x = BigDecimal.new((2**100).to_s)
- assert_equal(BigDecimal.new((2**100 * 3).to_s), (x * 3).to_i)
+ x = BigDecimal((2**100).to_s)
+ assert_equal(BigDecimal((2**100 * 3).to_s), (x * 3).to_i)
assert_equal(x, (x * 1).to_i)
assert_equal(x, (BigDecimal("1") * x).to_i)
- assert_equal(BigDecimal.new((2**200).to_s), (x * x).to_i)
+ assert_equal(BigDecimal((2**200).to_s), (x * x).to_i)
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ inf = BigDecimal("Infinity")
+ neginf = BigDecimal("-Infinity")
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true)
+ assert_raise_with_message(FloatDomainError, "Computation results to 'Infinity'") { inf * inf }
+ assert_raise_with_message(FloatDomainError, "Computation results to '-Infinity'") { neginf * inf }
end
def test_mult_with_float
- assert_kind_of(BigDecimal, BigDecimal.new("3") * 1.5)
+ assert_kind_of(BigDecimal, BigDecimal("3") * 1.5)
end
def test_mult_with_rational
- assert_kind_of(BigDecimal, BigDecimal.new("3") * 1.quo(3))
+ assert_kind_of(BigDecimal, BigDecimal("3") * 1.quo(3))
end
def test_mult_with_nil
@@ -713,28 +835,39 @@ class TestBigDecimal < Test::Unit::TestCase
end
def test_div
- x = BigDecimal.new((2**100).to_s)
- assert_equal(BigDecimal.new((2**100 / 3).to_s), (x / 3).to_i)
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal.new("0") / 1).sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (BigDecimal.new("-0") / 1).sign)
- assert_equal(2, BigDecimal.new("2") / 1)
- assert_equal(-2, BigDecimal.new("2") / -1)
+ x = BigDecimal((2**100).to_s)
+ assert_equal(BigDecimal((2**100 / 3).to_s), (x / 3).to_i)
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal("0") / 1).sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (BigDecimal("-0") / 1).sign)
+ assert_equal(2, BigDecimal("2") / 1)
+ assert_equal(-2, BigDecimal("2") / -1)
assert_equal(BigDecimal('1486.868686869'), BigDecimal('1472.0') / BigDecimal('0.99'), '[ruby-core:59365] [#9316]')
assert_equal(4.124045235, BigDecimal('0.9932') / (700 * BigDecimal('0.344045') / BigDecimal('1000.0')), '[#9305]')
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ assert_positive_zero(BigDecimal("1.0") / BigDecimal("Infinity"))
+ assert_negative_zero(BigDecimal("-1.0") / BigDecimal("Infinity"))
+ assert_negative_zero(BigDecimal("1.0") / BigDecimal("-Infinity"))
+ assert_positive_zero(BigDecimal("-1.0") / BigDecimal("-Infinity"))
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true)
+ BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, false)
+ assert_raise_with_message(FloatDomainError, "Computation results to 'Infinity'") { BigDecimal("1") / 0 }
+ assert_raise_with_message(FloatDomainError, "Computation results to '-Infinity'") { BigDecimal("-1") / 0 }
end
def test_div_with_float
- assert_kind_of(BigDecimal, BigDecimal.new("3") / 1.5)
+ assert_kind_of(BigDecimal, BigDecimal("3") / 1.5)
end
def test_div_with_rational
- assert_kind_of(BigDecimal, BigDecimal.new("3") / 1.quo(3))
+ assert_kind_of(BigDecimal, BigDecimal("3") / 1.quo(3))
end
def test_mod
- x = BigDecimal.new((2**100).to_s)
+ x = BigDecimal((2**100).to_s)
assert_equal(1, x % 3)
assert_equal(2, (-x) % 3)
assert_equal(-2, x % -3)
@@ -742,15 +875,15 @@ class TestBigDecimal < Test::Unit::TestCase
end
def test_mod_with_float
- assert_kind_of(BigDecimal, BigDecimal.new("3") % 1.5)
+ assert_kind_of(BigDecimal, BigDecimal("3") % 1.5)
end
def test_mod_with_rational
- assert_kind_of(BigDecimal, BigDecimal.new("3") % 1.quo(3))
+ assert_kind_of(BigDecimal, BigDecimal("3") % 1.quo(3))
end
def test_remainder
- x = BigDecimal.new((2**100).to_s)
+ x = BigDecimal((2**100).to_s)
assert_equal(1, x.remainder(3))
assert_equal(-1, (-x).remainder(3))
assert_equal(1, x.remainder(-3))
@@ -758,47 +891,47 @@ class TestBigDecimal < Test::Unit::TestCase
end
def test_remainder_with_float
- assert_kind_of(BigDecimal, BigDecimal.new("3").remainder(1.5))
+ assert_kind_of(BigDecimal, BigDecimal("3").remainder(1.5))
end
def test_remainder_with_rational
- assert_kind_of(BigDecimal, BigDecimal.new("3").remainder(1.quo(3)))
+ assert_kind_of(BigDecimal, BigDecimal("3").remainder(1.quo(3)))
end
def test_divmod
- x = BigDecimal.new((2**100).to_s)
+ x = BigDecimal((2**100).to_s)
assert_equal([(x / 3).floor, 1], x.divmod(3))
assert_equal([(-x / 3).floor, 2], (-x).divmod(3))
- assert_equal([0, 0], BigDecimal.new("0").divmod(2))
+ assert_equal([0, 0], BigDecimal("0").divmod(2))
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_raise(ZeroDivisionError){BigDecimal.new("0").divmod(0)}
+ assert_raise(ZeroDivisionError){BigDecimal("0").divmod(0)}
end
def test_add_bigdecimal
- x = BigDecimal.new((2**100).to_s)
+ x = BigDecimal((2**100).to_s)
assert_equal(3000000000000000000000000000000, x.add(x, 1))
assert_equal(2500000000000000000000000000000, x.add(x, 2))
assert_equal(2540000000000000000000000000000, x.add(x, 3))
end
def test_sub_bigdecimal
- x = BigDecimal.new((2**100).to_s)
+ x = BigDecimal((2**100).to_s)
assert_equal(1000000000000000000000000000000, x.sub(1, 1))
assert_equal(1300000000000000000000000000000, x.sub(1, 2))
assert_equal(1270000000000000000000000000000, x.sub(1, 3))
end
def test_mult_bigdecimal
- x = BigDecimal.new((2**100).to_s)
+ x = BigDecimal((2**100).to_s)
assert_equal(4000000000000000000000000000000, x.mult(3, 1))
assert_equal(3800000000000000000000000000000, x.mult(3, 2))
assert_equal(3800000000000000000000000000000, x.mult(3, 3))
end
def test_div_bigdecimal
- x = BigDecimal.new((2**100).to_s)
+ x = BigDecimal((2**100).to_s)
assert_equal(422550200076076467165567735125, x.div(3))
assert_equal(400000000000000000000000000000, x.div(3, 1))
assert_equal(420000000000000000000000000000, x.div(3, 2))
@@ -807,46 +940,52 @@ class TestBigDecimal < Test::Unit::TestCase
BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
assert_equal(0, BigDecimal("0").div(BigDecimal("Infinity")))
end
-
- x = BigDecimal.new("1")
- y = BigDecimal.new("0.22")
- (2..20).each do |i|
- assert_equal ("0."+"45"*(i/2)+"5"*(i%2)+"E1"), x.div(y, i).to_s, "#{i}"
- end
end
def test_abs_bigdecimal
- x = BigDecimal.new((2**100).to_s)
+ x = BigDecimal((2**100).to_s)
assert_equal(1267650600228229401496703205376, x.abs)
- x = BigDecimal.new("-" + (2**100).to_s)
+ x = BigDecimal("-" + (2**100).to_s)
assert_equal(1267650600228229401496703205376, x.abs)
- x = BigDecimal.new("0")
+ x = BigDecimal("0")
+ assert_equal(0, x.abs)
+ x = BigDecimal("-0")
assert_equal(0, x.abs)
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ x = BigDecimal("Infinity")
+ assert_equal(BigDecimal("Infinity"), x.abs)
+ x = BigDecimal("-Infinity")
+ assert_equal(BigDecimal("Infinity"), x.abs)
+
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- x = BigDecimal.new("NaN")
- assert_equal(true, x.abs.nan?)
+ x = BigDecimal("NaN")
+ assert_nan(x.abs)
end
def test_sqrt_bigdecimal
- x = BigDecimal.new("0.09")
+ x = BigDecimal("0.09")
assert_in_delta(0.3, x.sqrt(1), 0.001)
- x = BigDecimal.new((2**100).to_s)
+ x = BigDecimal((2**100).to_s)
y = BigDecimal("1125899906842624")
e = y.exponent
assert_equal(true, (x.sqrt(100) - y).abs < BigDecimal("1E#{e-100}"))
assert_equal(true, (x.sqrt(200) - y).abs < BigDecimal("1E#{e-200}"))
assert_equal(true, (x.sqrt(300) - y).abs < BigDecimal("1E#{e-300}"))
- x = BigDecimal.new("-" + (2**100).to_s)
- assert_raise(FloatDomainError) { x.sqrt(1) }
- x = BigDecimal.new((2**200).to_s)
+ x = BigDecimal("-" + (2**100).to_s)
+ assert_raise_with_message(FloatDomainError, "sqrt of negative value") { x.sqrt(1) }
+ x = BigDecimal((2**200).to_s)
assert_equal(2**100, x.sqrt(1))
BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_raise(FloatDomainError) { BigDecimal.new("NaN").sqrt(1) }
+ assert_raise_with_message(FloatDomainError, "sqrt of 'NaN'(Not a Number)") { BigDecimal("NaN").sqrt(1) }
+ assert_raise_with_message(FloatDomainError, "sqrt of negative value") { BigDecimal("-Infinity").sqrt(1) }
- assert_equal(0, BigDecimal.new("0").sqrt(1))
- assert_equal(1, BigDecimal.new("1").sqrt(1))
+ assert_equal(0, BigDecimal("0").sqrt(1))
+ assert_equal(0, BigDecimal("-0").sqrt(1))
+ assert_equal(1, BigDecimal("1").sqrt(1))
+ assert_positive_infinite(BigDecimal("Infinity").sqrt(1))
end
def test_sqrt_5266
@@ -864,25 +1003,26 @@ class TestBigDecimal < Test::Unit::TestCase
end
def test_fix
- x = BigDecimal.new("1.1")
+ x = BigDecimal("1.1")
assert_equal(1, x.fix)
+ assert_kind_of(BigDecimal, x.fix)
end
def test_frac
- x = BigDecimal.new("1.1")
+ x = BigDecimal("1.1")
assert_equal(0.1, x.frac)
- assert_equal(0.1, BigDecimal.new("0.1").frac)
+ assert_equal(0.1, BigDecimal("0.1").frac)
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_equal(true, BigDecimal.new("NaN").frac.nan?)
+ assert_nan(BigDecimal("NaN").frac)
end
def test_round
- assert_equal(3, BigDecimal.new("3.14159").round)
- assert_equal(9, BigDecimal.new("8.7").round)
- assert_equal(3.142, BigDecimal.new("3.14159").round(3))
- assert_equal(13300.0, BigDecimal.new("13345.234").round(-2))
+ assert_equal(3, BigDecimal("3.14159").round)
+ assert_equal(9, BigDecimal("8.7").round)
+ assert_equal(3.142, BigDecimal("3.14159").round(3))
+ assert_equal(13300.0, BigDecimal("13345.234").round(-2))
- x = BigDecimal.new("111.111")
+ x = BigDecimal("111.111")
assert_equal(111 , x.round)
assert_equal(111.1 , x.round(1))
assert_equal(111.11 , x.round(2))
@@ -893,7 +1033,7 @@ class TestBigDecimal < Test::Unit::TestCase
assert_equal( 0 , x.round(-3))
assert_equal( 0 , x.round(-4))
- x = BigDecimal.new("2.5")
+ x = BigDecimal("2.5")
assert_equal(3, x.round(0, BigDecimal::ROUND_UP))
assert_equal(2, x.round(0, BigDecimal::ROUND_DOWN))
assert_equal(3, x.round(0, BigDecimal::ROUND_HALF_UP))
@@ -903,80 +1043,201 @@ class TestBigDecimal < Test::Unit::TestCase
assert_equal(2, x.round(0, BigDecimal::ROUND_FLOOR))
assert_raise(ArgumentError) { x.round(0, 256) }
+ x = BigDecimal("-2.5")
+ assert_equal(-3, x.round(0, BigDecimal::ROUND_UP))
+ assert_equal(-2, x.round(0, BigDecimal::ROUND_DOWN))
+ assert_equal(-3, x.round(0, BigDecimal::ROUND_HALF_UP))
+ assert_equal(-2, x.round(0, BigDecimal::ROUND_HALF_DOWN))
+ assert_equal(-2, x.round(0, BigDecimal::ROUND_HALF_EVEN))
+ assert_equal(-2, x.round(0, BigDecimal::ROUND_CEILING))
+ assert_equal(-3, x.round(0, BigDecimal::ROUND_FLOOR))
+
ROUNDING_MODE_MAP.each do |const, sym|
assert_equal(x.round(0, const), x.round(0, sym))
end
bug3803 = '[ruby-core:32136]'
15.times do |n|
- x = BigDecimal.new("5#{'0'*n}1")
+ x = BigDecimal("5#{'0'*n}1")
assert_equal(10**(n+2), x.round(-(n+2), BigDecimal::ROUND_HALF_DOWN), bug3803)
assert_equal(10**(n+2), x.round(-(n+2), BigDecimal::ROUND_HALF_EVEN), bug3803)
- x = BigDecimal.new("0.5#{'0'*n}1")
+ x = BigDecimal("0.5#{'0'*n}1")
assert_equal(1, x.round(0, BigDecimal::ROUND_HALF_DOWN), bug3803)
assert_equal(1, x.round(0, BigDecimal::ROUND_HALF_EVEN), bug3803)
- x = BigDecimal.new("-0.5#{'0'*n}1")
+ x = BigDecimal("-0.5#{'0'*n}1")
assert_equal(-1, x.round(0, BigDecimal::ROUND_HALF_DOWN), bug3803)
assert_equal(-1, x.round(0, BigDecimal::ROUND_HALF_EVEN), bug3803)
end
end
+ def test_round_half_even
+ assert_equal(BigDecimal('12.0'), BigDecimal('12.5').round(half: :even))
+ assert_equal(BigDecimal('14.0'), BigDecimal('13.5').round(half: :even))
+
+ assert_equal(BigDecimal('2.2'), BigDecimal('2.15').round(1, half: :even))
+ assert_equal(BigDecimal('2.2'), BigDecimal('2.25').round(1, half: :even))
+ assert_equal(BigDecimal('2.4'), BigDecimal('2.35').round(1, half: :even))
+
+ assert_equal(BigDecimal('-2.2'), BigDecimal('-2.15').round(1, half: :even))
+ assert_equal(BigDecimal('-2.2'), BigDecimal('-2.25').round(1, half: :even))
+ assert_equal(BigDecimal('-2.4'), BigDecimal('-2.35').round(1, half: :even))
+
+ assert_equal(BigDecimal('7.1364'), BigDecimal('7.13645').round(4, half: :even))
+ assert_equal(BigDecimal('7.1365'), BigDecimal('7.1364501').round(4, half: :even))
+ assert_equal(BigDecimal('7.1364'), BigDecimal('7.1364499').round(4, half: :even))
+
+ assert_equal(BigDecimal('-7.1364'), BigDecimal('-7.13645').round(4, half: :even))
+ assert_equal(BigDecimal('-7.1365'), BigDecimal('-7.1364501').round(4, half: :even))
+ assert_equal(BigDecimal('-7.1364'), BigDecimal('-7.1364499').round(4, half: :even))
+ end
+
+ def test_round_half_up
+ assert_equal(BigDecimal('13.0'), BigDecimal('12.5').round(half: :up))
+ assert_equal(BigDecimal('14.0'), BigDecimal('13.5').round(half: :up))
+
+ assert_equal(BigDecimal('2.2'), BigDecimal('2.15').round(1, half: :up))
+ assert_equal(BigDecimal('2.3'), BigDecimal('2.25').round(1, half: :up))
+ assert_equal(BigDecimal('2.4'), BigDecimal('2.35').round(1, half: :up))
+
+ assert_equal(BigDecimal('-2.2'), BigDecimal('-2.15').round(1, half: :up))
+ assert_equal(BigDecimal('-2.3'), BigDecimal('-2.25').round(1, half: :up))
+ assert_equal(BigDecimal('-2.4'), BigDecimal('-2.35').round(1, half: :up))
+
+ assert_equal(BigDecimal('7.1365'), BigDecimal('7.13645').round(4, half: :up))
+ assert_equal(BigDecimal('7.1365'), BigDecimal('7.1364501').round(4, half: :up))
+ assert_equal(BigDecimal('7.1364'), BigDecimal('7.1364499').round(4, half: :up))
+
+ assert_equal(BigDecimal('-7.1365'), BigDecimal('-7.13645').round(4, half: :up))
+ assert_equal(BigDecimal('-7.1365'), BigDecimal('-7.1364501').round(4, half: :up))
+ assert_equal(BigDecimal('-7.1364'), BigDecimal('-7.1364499').round(4, half: :up))
+ end
+
+ def test_round_half_down
+ assert_equal(BigDecimal('12.0'), BigDecimal('12.5').round(half: :down))
+ assert_equal(BigDecimal('13.0'), BigDecimal('13.5').round(half: :down))
+
+ assert_equal(BigDecimal('2.1'), BigDecimal('2.15').round(1, half: :down))
+ assert_equal(BigDecimal('2.2'), BigDecimal('2.25').round(1, half: :down))
+ assert_equal(BigDecimal('2.3'), BigDecimal('2.35').round(1, half: :down))
+
+ assert_equal(BigDecimal('-2.1'), BigDecimal('-2.15').round(1, half: :down))
+ assert_equal(BigDecimal('-2.2'), BigDecimal('-2.25').round(1, half: :down))
+ assert_equal(BigDecimal('-2.3'), BigDecimal('-2.35').round(1, half: :down))
+
+ assert_equal(BigDecimal('7.1364'), BigDecimal('7.13645').round(4, half: :down))
+ assert_equal(BigDecimal('7.1365'), BigDecimal('7.1364501').round(4, half: :down))
+ assert_equal(BigDecimal('7.1364'), BigDecimal('7.1364499').round(4, half: :down))
+
+ assert_equal(BigDecimal('-7.1364'), BigDecimal('-7.13645').round(4, half: :down))
+ assert_equal(BigDecimal('-7.1365'), BigDecimal('-7.1364501').round(4, half: :down))
+ assert_equal(BigDecimal('-7.1364'), BigDecimal('-7.1364499').round(4, half: :down))
+ end
+
+ def test_round_half_nil
+ x = BigDecimal("2.5")
+
+ BigDecimal.save_rounding_mode do
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_UP)
+ assert_equal(3, x.round(0, half: nil))
+ end
+
+ BigDecimal.save_rounding_mode do
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_DOWN)
+ assert_equal(2, x.round(0, half: nil))
+ end
+
+ BigDecimal.save_rounding_mode do
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_UP)
+ assert_equal(3, x.round(0, half: nil))
+ end
+
+ BigDecimal.save_rounding_mode do
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_DOWN)
+ assert_equal(2, x.round(0, half: nil))
+ end
+
+ BigDecimal.save_rounding_mode do
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_EVEN)
+ assert_equal(2, x.round(0, half: nil))
+ end
+
+ BigDecimal.save_rounding_mode do
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_CEILING)
+ assert_equal(3, x.round(0, half: nil))
+ end
+
+ BigDecimal.save_rounding_mode do
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_FLOOR)
+ assert_equal(2, x.round(0, half: nil))
+ end
+ end
+
+ def test_round_half_invalid_option
+ assert_raise_with_message(ArgumentError, "invalid rounding mode: invalid") { BigDecimal('12.5').round(half: :invalid) }
+ assert_raise_with_message(ArgumentError, "invalid rounding mode: invalid") { BigDecimal('2.15').round(1, half: :invalid) }
+ end
+
def test_truncate
- assert_equal(3, BigDecimal.new("3.14159").truncate)
- assert_equal(8, BigDecimal.new("8.7").truncate)
- assert_equal(3.141, BigDecimal.new("3.14159").truncate(3))
- assert_equal(13300.0, BigDecimal.new("13345.234").truncate(-2))
+ assert_equal(3, BigDecimal("3.14159").truncate)
+ assert_equal(8, BigDecimal("8.7").truncate)
+ assert_equal(3.141, BigDecimal("3.14159").truncate(3))
+ assert_equal(13300.0, BigDecimal("13345.234").truncate(-2))
+
+ assert_equal(-3, BigDecimal("-3.14159").truncate)
+ assert_equal(-8, BigDecimal("-8.7").truncate)
+ assert_equal(-3.141, BigDecimal("-3.14159").truncate(3))
+ assert_equal(-13300.0, BigDecimal("-13345.234").truncate(-2))
end
def test_floor
- assert_equal(3, BigDecimal.new("3.14159").floor)
- assert_equal(-10, BigDecimal.new("-9.1").floor)
- assert_equal(3.141, BigDecimal.new("3.14159").floor(3))
- assert_equal(13300.0, BigDecimal.new("13345.234").floor(-2))
+ assert_equal(3, BigDecimal("3.14159").floor)
+ assert_equal(-10, BigDecimal("-9.1").floor)
+ assert_equal(3.141, BigDecimal("3.14159").floor(3))
+ assert_equal(13300.0, BigDecimal("13345.234").floor(-2))
end
def test_ceil
- assert_equal(4, BigDecimal.new("3.14159").ceil)
- assert_equal(-9, BigDecimal.new("-9.1").ceil)
- assert_equal(3.142, BigDecimal.new("3.14159").ceil(3))
- assert_equal(13400.0, BigDecimal.new("13345.234").ceil(-2))
+ assert_equal(4, BigDecimal("3.14159").ceil)
+ assert_equal(-9, BigDecimal("-9.1").ceil)
+ assert_equal(3.142, BigDecimal("3.14159").ceil(3))
+ assert_equal(13400.0, BigDecimal("13345.234").ceil(-2))
end
def test_to_s
- assert_equal('-123.45678 90123 45678 9', BigDecimal.new('-123.45678901234567890').to_s('5F'))
- assert_equal('+123.45678901 23456789', BigDecimal.new('123.45678901234567890').to_s('+8F'))
- assert_equal(' 123.4567890123456789', BigDecimal.new('123.45678901234567890').to_s(' F'))
- assert_equal('0.1234567890123456789E3', BigDecimal.new('123.45678901234567890').to_s)
- assert_equal('0.12345 67890 12345 6789E3', BigDecimal.new('123.45678901234567890').to_s(5))
+ assert_equal('-123.45678 90123 45678 9', BigDecimal('-123.45678901234567890').to_s('5F'))
+ assert_equal('+123.45678901 23456789', BigDecimal('123.45678901234567890').to_s('+8F'))
+ assert_equal(' 123.4567890123456789', BigDecimal('123.45678901234567890').to_s(' F'))
+ assert_equal('0.1234567890123456789e3', BigDecimal('123.45678901234567890').to_s)
+ assert_equal('0.12345 67890 12345 6789e3', BigDecimal('123.45678901234567890').to_s(5))
end
def test_split
- x = BigDecimal.new('-123.45678901234567890')
+ x = BigDecimal('-123.45678901234567890')
assert_equal([-1, "1234567890123456789", 10, 3], x.split)
- assert_equal([1, "0", 10, 0], BigDecimal.new("0").split)
- assert_equal([-1, "0", 10, 0], BigDecimal.new("-0").split)
+ assert_equal([1, "0", 10, 0], BigDecimal("0").split)
+ assert_equal([-1, "0", 10, 0], BigDecimal("-0").split)
BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert_equal([0, "NaN", 10, 0], BigDecimal.new("NaN").split)
- assert_equal([1, "Infinity", 10, 0], BigDecimal.new("Infinity").split)
- assert_equal([-1, "Infinity", 10, 0], BigDecimal.new("-Infinity").split)
+ assert_equal([0, "NaN", 10, 0], BigDecimal("NaN").split)
+ assert_equal([1, "Infinity", 10, 0], BigDecimal("Infinity").split)
+ assert_equal([-1, "Infinity", 10, 0], BigDecimal("-Infinity").split)
end
def test_exponent
- x = BigDecimal.new('-123.45678901234567890')
+ x = BigDecimal('-123.45678901234567890')
assert_equal(3, x.exponent)
end
def test_inspect
- x = BigDecimal.new("1234.5678")
- prec, maxprec = x.precs
- assert_match(/^#<BigDecimal:[0-9a-f]+,'0.12345678E4',#{prec}\(#{maxprec}\)>$/, x.inspect)
+ assert_equal("0.123456789012e0", BigDecimal("0.123456789012").inspect)
+ assert_equal("0.123456789012e4", BigDecimal("1234.56789012").inspect)
+ assert_equal("0.123456789012e-4", BigDecimal("0.0000123456789012").inspect)
end
def test_power
assert_nothing_raised(TypeError, '[ruby-core:47632]') do
- 1000.times { BigDecimal.new('1001.10')**0.75 }
+ 1000.times { BigDecimal('1001.10')**0.75 }
end
end
@@ -1175,7 +1436,7 @@ class TestBigDecimal < Test::Unit::TestCase
def test_limit
BigDecimal.limit(1)
- x = BigDecimal.new("3")
+ x = BigDecimal("3")
assert_equal(90, x ** 4) # OK? must it be 80?
# 3 * 3 * 3 * 3 = 10 * 3 * 3 = 30 * 3 = 90 ???
assert_raise(ArgumentError) { BigDecimal.limit(-1) }
@@ -1205,38 +1466,38 @@ class TestBigDecimal < Test::Unit::TestCase
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, false)
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, BigDecimal.new("0").sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, BigDecimal.new("-0").sign)
- assert_equal(BigDecimal::SIGN_POSITIVE_FINITE, BigDecimal.new("1").sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_FINITE, BigDecimal.new("-1").sign)
- assert_equal(BigDecimal::SIGN_POSITIVE_INFINITE, (BigDecimal.new("1") / 0).sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_INFINITE, (BigDecimal.new("-1") / 0).sign)
- assert_equal(BigDecimal::SIGN_NaN, (BigDecimal.new("0") / 0).sign)
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, BigDecimal("0").sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, BigDecimal("-0").sign)
+ assert_equal(BigDecimal::SIGN_POSITIVE_FINITE, BigDecimal("1").sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_FINITE, BigDecimal("-1").sign)
+ assert_equal(BigDecimal::SIGN_POSITIVE_INFINITE, (BigDecimal("1") / 0).sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_INFINITE, (BigDecimal("-1") / 0).sign)
+ assert_equal(BigDecimal::SIGN_NaN, (BigDecimal("0") / 0).sign)
end
def test_inf
BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- inf = BigDecimal.new("Infinity")
+ inf = BigDecimal("Infinity")
assert_equal(inf, inf + inf)
- assert_equal(true, (inf + (-inf)).nan?)
- assert_equal(true, (inf - inf).nan?)
+ assert_nan((inf + (-inf)))
+ assert_nan((inf - inf))
assert_equal(inf, inf - (-inf))
assert_equal(inf, inf * inf)
- assert_equal(true, (inf / inf).nan?)
+ assert_nan((inf / inf))
assert_equal(inf, inf + 1)
assert_equal(inf, inf - 1)
assert_equal(inf, inf * 1)
- assert_equal(true, (inf * 0).nan?)
+ assert_nan((inf * 0))
assert_equal(inf, inf / 1)
assert_equal(inf, 1 + inf)
assert_equal(-inf, 1 - inf)
assert_equal(inf, 1 * inf)
assert_equal(-inf, -1 * inf)
- assert_equal(true, (0 * inf).nan?)
+ assert_nan((0 * inf))
assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (1 / inf).sign)
assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (-1 / inf).sign)
end
@@ -1244,14 +1505,14 @@ class TestBigDecimal < Test::Unit::TestCase
def test_to_special_string
BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- nan = BigDecimal.new("NaN")
+ nan = BigDecimal("NaN")
assert_equal("NaN", nan.to_s)
- inf = BigDecimal.new("Infinity")
+ inf = BigDecimal("Infinity")
assert_equal("Infinity", inf.to_s)
assert_equal(" Infinity", inf.to_s(" "))
assert_equal("+Infinity", inf.to_s("+"))
assert_equal("-Infinity", (-inf).to_s)
- pzero = BigDecimal.new("0")
+ pzero = BigDecimal("0")
assert_equal("0.0", pzero.to_s)
assert_equal(" 0.0", pzero.to_s(" "))
assert_equal("+0.0", pzero.to_s("+"))
@@ -1267,15 +1528,15 @@ class TestBigDecimal < Test::Unit::TestCase
end
def test_ctov
- assert_equal(0.1, BigDecimal.new("1E-1"))
- assert_equal(10, BigDecimal.new("1E+1"))
- assert_equal(1, BigDecimal.new("+1"))
+ assert_equal(0.1, BigDecimal("1E-1"))
+ assert_equal(10, BigDecimal("1E+1"))
+ assert_equal(1, BigDecimal("+1"))
BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
- assert_equal(BigDecimal::SIGN_POSITIVE_INFINITE, BigDecimal.new("1E1" + "0" * 10000).sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_INFINITE, BigDecimal.new("-1E1" + "0" * 10000).sign)
- assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, BigDecimal.new("1E-1" + "0" * 10000).sign)
- assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, BigDecimal.new("-1E-1" + "0" * 10000).sign)
+ assert_equal(BigDecimal::SIGN_POSITIVE_INFINITE, BigDecimal("1E1" + "0" * 10000).sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_INFINITE, BigDecimal("-1E1" + "0" * 10000).sign)
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, BigDecimal("1E-1" + "0" * 10000).sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, BigDecimal("-1E-1" + "0" * 10000).sign)
end
def test_split_under_gc_stress
@@ -1284,7 +1545,7 @@ class TestBigDecimal < Test::Unit::TestCase
assert_in_out_err(%w[-rbigdecimal --disable-gems], <<-EOS, expect, [], bug3258)
GC.stress = true
10.upto(20) do |i|
- p BigDecimal.new("1"+"0"*i).split
+ p BigDecimal("1"+"0"*i).split
end
EOS
end
@@ -1292,7 +1553,7 @@ class TestBigDecimal < Test::Unit::TestCase
def test_coerce_under_gc_stress
assert_in_out_err(%w[-rbigdecimal --disable-gems], <<-EOS, [], [])
expect = ":too_long_to_embed_as_string can't be coerced into BigDecimal"
- b = BigDecimal.new("1")
+ b = BigDecimal("1")
GC.stress = true
10.times do
begin
@@ -1306,14 +1567,14 @@ class TestBigDecimal < Test::Unit::TestCase
end
def test_INFINITY
- assert(BigDecimal::INFINITY.infinite?, "BigDecimal::INFINITY is not a infinity")
+ assert_positive_infinite(BigDecimal::INFINITY)
end
def test_NAN
- assert(BigDecimal::NAN.nan?, "BigDecimal::NAN is not NaN")
+ assert_nan(BigDecimal::NAN)
end
- def test_exp_with_zerp_precision
+ def test_exp_with_zero_precision
assert_raise(ArgumentError) do
BigMath.exp(1, 0)
end
@@ -1342,14 +1603,14 @@ class TestBigDecimal < Test::Unit::TestCase
BigDecimal.save_exception_mode do
BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
assert(BigMath.exp(BigDecimal::INFINITY, 20) > 0)
- assert(BigMath.exp(BigDecimal::INFINITY, 20).infinite?)
+ assert_positive_infinite(BigMath.exp(BigDecimal::INFINITY, 20))
end
end
def test_exp_with_nan
BigDecimal.save_exception_mode do
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert(BigMath.exp(BigDecimal::NAN, 20).nan?)
+ assert_nan(BigMath.exp(BigDecimal::NAN, 20))
end
end
@@ -1470,21 +1731,21 @@ class TestBigDecimal < Test::Unit::TestCase
BigDecimal.save_exception_mode do
BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
assert(BigMath.log(BigDecimal::INFINITY, 20) > 0)
- assert(BigMath.log(BigDecimal::INFINITY, 20).infinite?)
+ assert_positive_infinite(BigMath.log(BigDecimal::INFINITY, 20))
end
end
def test_BigMath_log_with_nan
BigDecimal.save_exception_mode do
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert(BigMath.log(BigDecimal::NAN, 20).nan?)
+ assert_nan(BigMath.log(BigDecimal::NAN, 20))
end
end
def test_BigMath_log_with_float_nan
BigDecimal.save_exception_mode do
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
- assert(BigMath.log(Float::NAN, 20).nan?)
+ assert_nan(BigMath.log(Float::NAN, 20))
end
end
@@ -1545,26 +1806,47 @@ class TestBigDecimal < Test::Unit::TestCase
EOS
end
+ def test_frozen_p
+ x = BigDecimal(1)
+ assert(x.frozen?)
+ assert((x + x).frozen?)
+ end
+
+ def test_clone
+ assert_warning(/^$/) do
+ x = BigDecimal(0)
+ assert_same(x, x.clone)
+ end
+ end
+
def test_dup
- [1, -1, 2**100, -2**100].each do |i|
- x = BigDecimal(i)
- assert_equal(x, x.dup)
+ assert_warning(/^$/) do
+ [1, -1, 2**100, -2**100].each do |i|
+ x = BigDecimal(i)
+ assert_same(x, x.dup)
+ end
end
end
def test_dup_subclass
c = Class.new(BigDecimal)
- x = c.new(1)
- y = x.dup
- assert_equal(1, y)
- assert_kind_of(c, y)
+ # TODO: BigDecimal.new will be removed on 1.5
+ # assert_raise_with_message(NoMethodError, /undefined method `new'/) { c.new(1) }
+ verbose, $VERBOSE = $VERBOSE, nil
+ assert_equal(BigDecimal(1), c.new(1))
+ assert_raise(ArgumentError) { c.new(',', exception: true) }
+ assert_nothing_raised { assert_equal(nil, c.new(',', exception: false)) }
+ assert_raise(TypeError) { c.new(nil, exception: true) }
+ assert_nothing_raised { assert_equal(nil, c.new(nil, exception: false)) }
+ ensure
+ $VERBOSE = verbose
end
def test_to_d
bug6093 = '[ruby-core:42969]'
- code = "exit(BigDecimal.new('10.0') == 10.0.to_d)"
+ code = "exit(BigDecimal('10.0') == 10.0.to_d)"
assert_ruby_status(%w[-rbigdecimal -rbigdecimal/util -rmathn -], code, bug6093)
- end
+ end if RUBY_VERSION < '2.5' # mathn was removed from Ruby 2.5
def test_bug6406
assert_in_out_err(%w[-rbigdecimal --disable-gems], <<-EOS, [], [])
@@ -1586,10 +1868,10 @@ class TestBigDecimal < Test::Unit::TestCase
end
def test_no_memory_leak_initialize
- assert_no_memory_leak("BigDecimal.new")
+ assert_no_memory_leak("BigDecimal()")
end
- def test_no_memory_leak_global_new
+ def test_no_memory_leak_BigDecimal
assert_no_memory_leak("BigDecimal('10')")
assert_no_memory_leak("BigDecimal(b)")
end
diff --git a/test/bigdecimal/test_bigdecimal_util.rb b/test/bigdecimal/test_bigdecimal_util.rb
index c3a45b2eee..bb9ed83185 100644
--- a/test/bigdecimal/test_bigdecimal_util.rb
+++ b/test/bigdecimal/test_bigdecimal_util.rb
@@ -12,6 +12,8 @@ class TestBigDecimalUtil < Test::Unit::TestCase
def test_Integer_to_d
assert_equal(BigDecimal(1), 1.to_d)
assert_equal(BigDecimal(2<<100), (2<<100).to_d)
+
+ assert(1.to_d.frozen?)
end
def test_Float_to_d_without_precision
@@ -22,6 +24,11 @@ class TestBigDecimalUtil < Test::Unit::TestCase
bug9214 = '[ruby-core:58858]'
assert_equal((-0.0).to_d.sign, -1, bug9214)
+
+ assert_raise(TypeError) { 0.3.to_d(nil) }
+ assert_raise(TypeError) { 0.3.to_d(false) }
+
+ assert(1.1.to_d.frozen?)
end
def test_Float_to_d_with_precision
@@ -32,6 +39,8 @@ class TestBigDecimalUtil < Test::Unit::TestCase
bug9214 = '[ruby-core:58858]'
assert_equal((-0.0).to_d(digits).sign, -1, bug9214)
+
+ assert(1.1.to_d(digits).frozen?)
end
def test_Rational_to_d
@@ -39,13 +48,44 @@ class TestBigDecimalUtil < Test::Unit::TestCase
delta = 1.0/10**(digits)
assert_in_delta(BigDecimal(1.quo(2), digits), 1.quo(2).to_d(digits), delta)
assert_in_delta(BigDecimal(355.quo(113), digits), 355.quo(113).to_d(digits), delta)
+
+ assert(355.quo(113).to_d(digits).frozen?)
end
def test_Rational_to_d_with_zero_precision
- assert_raise(ArgumentError) { 355.quo(113).to_d(0) }
+ assert_equal(BigDecimal(355.quo(113), 0), 355.quo(113).to_d(0))
end
def test_Rational_to_d_with_negative_precision
assert_raise(ArgumentError) { 355.quo(113).to_d(-42) }
end
+
+ def test_String_to_d
+ assert_equal(BigDecimal('1'), "1__1_1".to_d)
+ assert_equal(BigDecimal('2.5'), "2.5".to_d)
+ assert_equal(BigDecimal('2.5'), "2.5 degrees".to_d)
+ assert_equal(BigDecimal('2.5e1'), "2.5e1 degrees".to_d)
+ assert_equal(BigDecimal('0'), "degrees 100.0".to_d)
+ assert_equal(BigDecimal('0.125'), "0.1_2_5".to_d)
+ assert_equal(BigDecimal('0.125'), "0.1_2_5__".to_d)
+ assert_equal(BigDecimal('1'), "1_.125".to_d)
+ assert_equal(BigDecimal('1'), "1._125".to_d)
+ assert_equal(BigDecimal('0.1'), "0.1__2_5".to_d)
+ assert_equal(BigDecimal('0.1'), "0.1_e10".to_d)
+ assert_equal(BigDecimal('0.1'), "0.1e_10".to_d)
+ assert_equal(BigDecimal('1'), "0.1e1__0".to_d)
+ assert_equal(BigDecimal('1.2'), "1.2.3".to_d)
+
+ assert("2.5".to_d.frozen?)
+ end
+
+ def test_invalid_String_to_d
+ assert_equal("invalid".to_d, BigDecimal('0.0'))
+ end
+
+ def test_Nil_to_d
+ assert_equal(nil.to_d, BigDecimal('0.0'))
+
+ assert(nil.to_d)
+ end
end
diff --git a/test/cgi/test_cgi_cookie.rb b/test/cgi/test_cgi_cookie.rb
index ca81e41133..985cc0d7a1 100644
--- a/test/cgi/test_cgi_cookie.rb
+++ b/test/cgi/test_cgi_cookie.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'cgi'
require 'stringio'
@@ -15,7 +15,7 @@ class CGICookieTest < Test::Unit::TestCase
'REQUEST_METHOD' => 'GET',
'SCRIPT_NAME' => nil,
)
- @str1="\xE3\x82\x86\xE3\x82\x93\xE3\x82\x86\xE3\x82\x93"
+ @str1="\xE3\x82\x86\xE3\x82\x93\xE3\x82\x86\xE3\x82\x93".dup
@str1.force_encoding("UTF-8") if defined?(::Encoding)
end
@@ -39,7 +39,7 @@ class CGICookieTest < Test::Unit::TestCase
def test_cgi_cookie_new_complex
t = Time.gm(2030, 12, 31, 23, 59, 59)
- value = ['val1', '&<>"', "\xA5\xE0\xA5\xB9\xA5\xAB"]
+ value = ['val1', '&<>"', "\xA5\xE0\xA5\xB9\xA5\xAB".dup]
value[2].force_encoding("EUC-JP") if defined?(::Encoding)
cookie = CGI::Cookie.new('name'=>'name1',
'value'=>value,
@@ -101,6 +101,11 @@ class CGICookieTest < Test::Unit::TestCase
end
end
+ def test_cgi_cookie_parse_not_decode_name
+ cookie_str = "%66oo=baz;foo=bar"
+ cookies = CGI::Cookie.parse(cookie_str)
+ assert_equal({"%66oo" => ["baz"], "foo" => ["bar"]}, cookies)
+ end
def test_cgi_cookie_arrayinterface
cookie = CGI::Cookie.new('name1', 'a', 'b', 'c')
diff --git a/test/cgi/test_cgi_core.rb b/test/cgi/test_cgi_core.rb
index 97308a0457..255203dc17 100644
--- a/test/cgi/test_cgi_core.rb
+++ b/test/cgi/test_cgi_core.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'cgi'
require 'stringio'
@@ -105,11 +105,11 @@ class CGICoreTest < Test::Unit::TestCase
hash={}
cgi = CGI.new(:accept_charset=>"UTF-8"){|key,val|hash[key]=val}
## cgi[]
- assert_equal("\xBE\xBE\xB9\xBE".force_encoding("UTF-8"), cgi['str'])
+ assert_equal("\xBE\xBE\xB9\xBE".dup.force_encoding("UTF-8"), cgi['str'])
## cgi.params
- assert_equal(["\xBE\xBE\xB9\xBE".force_encoding("UTF-8")], cgi.params['str'])
+ assert_equal(["\xBE\xBE\xB9\xBE".dup.force_encoding("UTF-8")], cgi.params['str'])
## accept-charset error
- assert_equal({"str"=>"\xBE\xBE\xB9\xBE".force_encoding("UTF-8")},hash)
+ assert_equal({"str"=>"\xBE\xBE\xB9\xBE".dup.force_encoding("UTF-8")},hash)
$stdin.rewind
assert_raise(CGI::InvalidEncoding) do
@@ -119,9 +119,9 @@ class CGICoreTest < Test::Unit::TestCase
$stdin.rewind
cgi = CGI.new(:accept_charset=>"EUC-JP")
## cgi[]
- assert_equal("\xBE\xBE\xB9\xBE".force_encoding("EUC-JP"), cgi['str'])
+ assert_equal("\xBE\xBE\xB9\xBE".dup.force_encoding("EUC-JP"), cgi['str'])
## cgi.params
- assert_equal(["\xBE\xBE\xB9\xBE".force_encoding("EUC-JP")], cgi.params['str'])
+ assert_equal(["\xBE\xBE\xB9\xBE".dup.force_encoding("EUC-JP")], cgi.params['str'])
else
assert(true)
end
diff --git a/test/cgi/test_cgi_header.rb b/test/cgi/test_cgi_header.rb
index c420e7978b..bab2d0348a 100644
--- a/test/cgi/test_cgi_header.rb
+++ b/test/cgi/test_cgi_header.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'cgi'
require 'time'
@@ -58,7 +58,7 @@ class CGIHeaderTest < Test::Unit::TestCase
'expires' => Time.gm(2000, 1, 23, 12, 34, 56),
'location' => 'http://www.ruby-lang.org/',
}
- expected = "Status: 302 Found\r\n"
+ expected = "Status: 302 Found\r\n".dup
expected << "Server: webrick\r\n"
expected << "Connection: close\r\n"
expected << "Content-Type: text/xhtml; charset=utf8\r\n"
@@ -116,7 +116,7 @@ class CGIHeaderTest < Test::Unit::TestCase
CGI::Cookie.new('name'=>'name2', 'value'=>'value2', 'secure'=>true),
]
cgi.instance_variable_set('@output_cookies', cookies)
- expected = "Content-Type: text/html; charset=utf8\r\n"
+ expected = "Content-Type: text/html; charset=utf8\r\n".dup
expected << "Set-Cookie: name1=abc&123; path=\r\n"
expected << "Set-Cookie: name2=value2; path=; secure\r\n"
expected << "\r\n"
@@ -151,7 +151,7 @@ class CGIHeaderTest < Test::Unit::TestCase
actual.sub!(date, "Date: DATE_IS_REMOVED\r\n")
end
## assertion
- expected = "HTTP/1.1 200 OK\r\n"
+ expected = "HTTP/1.1 200 OK\r\n".dup
expected << "Date: DATE_IS_REMOVED\r\n"
expected << "Server: Apache 2.2.0\r\n"
expected << "Connection: close\r\n"
@@ -163,10 +163,10 @@ class CGIHeaderTest < Test::Unit::TestCase
expected.sub!(/^HTTP\/1.1 200 OK\r\n/, "HTTP/1.1 302 Found\r\n")
expected.sub!(/\r\n\r\n/, "\r\nlocation: http://www.example.com/\r\n\r\n")
assert_equal(expected, actual3)
- expected = "Content-Type: text/html\r\n"
+ expected = "Content-Type: text/html\r\n".dup
expected << "\r\n"
assert_equal(expected, actual4)
- expected = "Status: 302 Found\r\n"
+ expected = "Status: 302 Found\r\n".dup
expected << "Content-Type: text/html\r\n"
expected << "location: http://www.example.com/\r\n"
expected << "\r\n"
diff --git a/test/cgi/test_cgi_modruby.rb b/test/cgi/test_cgi_modruby.rb
index 9813a95277..90132962b5 100644
--- a/test/cgi/test_cgi_modruby.rb
+++ b/test/cgi/test_cgi_modruby.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'cgi'
require_relative 'update_env'
diff --git a/test/cgi/test_cgi_multipart.rb b/test/cgi/test_cgi_multipart.rb
index f63c2e3778..d27b1cb8b6 100644
--- a/test/cgi/test_cgi_multipart.rb
+++ b/test/cgi/test_cgi_multipart.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'cgi'
require 'tempfile'
@@ -33,7 +33,7 @@ class MultiPart
def initialize(boundary=nil)
@boundary = boundary || create_boundary()
- @buf = ''
+ @buf = ''.dup
@buf.force_encoding(::Encoding::ASCII_8BIT) if defined?(::Encoding)
end
attr_reader :boundary
@@ -53,7 +53,7 @@ class MultiPart
def close
buf = @buf
- @buf = ''
+ @buf = ''.dup
return buf << "--#{boundary}--\r\n"
end
@@ -202,7 +202,7 @@ class CGIMultipartTest < Test::Unit::TestCase
@boundary = '----WebKitFormBoundaryAAfvAII+YL9102cX'
@data = [
{:name=>'hidden1', :value=>'foobar'},
- {:name=>'text1', :value=>"\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"},
+ {:name=>'text1', :value=>"\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A".dup},
{:name=>'file1', :value=>_read('file1.html'),
:filename=>'file1.html', :content_type=>'text/html'},
{:name=>'image1', :value=>_read('small.png'),
@@ -218,7 +218,7 @@ class CGIMultipartTest < Test::Unit::TestCase
@boundary = '----WebKitFormBoundaryAAfvAII+YL9102cX'
@data = [
{:name=>'hidden1', :value=>'foobar'},
- {:name=>'text1', :value=>"\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"},
+ {:name=>'text1', :value=>"\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A".dup},
{:name=>'file1', :value=>_read('file1.html'),
:filename=>'file1.html', :content_type=>'text/html'},
{:name=>'image1', :value=>_read('large.png'),
@@ -323,7 +323,7 @@ class CGIMultipartTest < Test::Unit::TestCase
@boundary = '(.|\n)*'
@data = [
{:name=>'hidden1', :value=>'foobar'},
- {:name=>'text1', :value=>"\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"},
+ {:name=>'text1', :value=>"\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A".dup},
{:name=>'file1', :value=>_read('file1.html'),
:filename=>'file1.html', :content_type=>'text/html'},
{:name=>'image1', :value=>_read('small.png'),
diff --git a/test/cgi/test_cgi_session.rb b/test/cgi/test_cgi_session.rb
index 59e8d4c15b..b16b69766e 100644
--- a/test/cgi/test_cgi_session.rb
+++ b/test/cgi/test_cgi_session.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'cgi'
require 'cgi/session'
@@ -30,7 +30,7 @@ class CGISessionTest < Test::Unit::TestCase
'SERVER_PROTOCOL' => 'HTTP/1.1',
)
value1="value1"
- value2="\x8F\xBC\x8D]"
+ value2="\x8F\xBC\x8D]".dup
value2.force_encoding("SJIS") if defined?(::Encoding)
cgi = CGI.new
session = CGI::Session.new(cgi,"tmpdir"=>@session_dir)
@@ -66,7 +66,7 @@ class CGISessionTest < Test::Unit::TestCase
'SERVER_PROTOCOL' => 'HTTP/1.1',
)
value1="value1"
- value2="\x8F\xBC\x8D]"
+ value2="\x8F\xBC\x8D]".dup
value2.force_encoding("SJIS") if defined?(::Encoding)
cgi = CGI.new
session = CGI::Session.new(cgi,"tmpdir"=>@session_dir,"database_manager"=>CGI::Session::PStore)
@@ -101,7 +101,7 @@ class CGISessionTest < Test::Unit::TestCase
'SERVER_PROTOCOL' => 'HTTP/1.1',
)
value1="value1"
- value2="\x8F\xBC\x8D]"
+ value2="\x8F\xBC\x8D]".dup
value2.force_encoding("SJIS") if defined?(::Encoding)
cgi = CGI.new
session = CGI::Session.new(cgi,"tmpdir"=>@session_dir,"session_id"=>"foo")
@@ -139,7 +139,7 @@ class CGISessionTest < Test::Unit::TestCase
'SERVER_PROTOCOL' => 'HTTP/1.1',
)
value1="value1"
- value2="\x8F\xBC\x8D]"
+ value2="\x8F\xBC\x8D]".dup
value2.force_encoding("SJIS") if defined?(::Encoding)
cgi = CGI.new
session = CGI::Session.new(cgi,"tmpdir"=>@session_dir,"session_key"=>"bar")
diff --git a/test/cgi/test_cgi_tag_helper.rb b/test/cgi/test_cgi_tag_helper.rb
index a48bafdaec..0b99dfc1bc 100644
--- a/test/cgi/test_cgi_tag_helper.rb
+++ b/test/cgi/test_cgi_tag_helper.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'cgi'
require 'stringio'
diff --git a/test/cgi/test_cgi_util.rb b/test/cgi/test_cgi_util.rb
index 4e4f7a45e7..f2f5575efb 100644
--- a/test/cgi/test_cgi_util.rb
+++ b/test/cgi/test_cgi_util.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'cgi'
require 'stringio'
@@ -15,7 +15,7 @@ class CGIUtilTest < Test::Unit::TestCase
'REQUEST_METHOD' => 'GET',
'SCRIPT_NAME' => nil,
)
- @str1="&<>\" \xE3\x82\x86\xE3\x82\x93\xE3\x82\x86\xE3\x82\x93"
+ @str1="&<>\" \xE3\x82\x86\xE3\x82\x93\xE3\x82\x86\xE3\x82\x93".dup
@str1.force_encoding("UTF-8") if defined?(::Encoding)
end
@@ -29,28 +29,47 @@ class CGIUtilTest < Test::Unit::TestCase
assert_equal('%26%3C%3E%22+%E3%82%86%E3%82%93%E3%82%86%E3%82%93'.ascii_only?, CGI::escape(@str1).ascii_only?) if defined?(::Encoding)
end
+ def test_cgi_escape_with_unreserved_characters
+ assert_equal("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~",
+ CGI::escape("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"),
+ "should not escape any unreserved characters, as per RFC3986 Section 2.3")
+ end
+
def test_cgi_escape_with_invalid_byte_sequence
assert_nothing_raised(ArgumentError) do
- assert_equal('%C0%3C%3C', CGI::escape("\xC0\<\<".force_encoding("UTF-8")))
+ assert_equal('%C0%3C%3C', CGI::escape("\xC0\<\<".dup.force_encoding("UTF-8")))
end
end
def test_cgi_escape_preserve_encoding
- assert_equal(Encoding::US_ASCII, CGI::escape("\xC0\<\<".force_encoding("US-ASCII")).encoding)
- assert_equal(Encoding::ASCII_8BIT, CGI::escape("\xC0\<\<".force_encoding("ASCII-8BIT")).encoding)
- assert_equal(Encoding::UTF_8, CGI::escape("\xC0\<\<".force_encoding("UTF-8")).encoding)
+ assert_equal(Encoding::US_ASCII, CGI::escape("\xC0\<\<".dup.force_encoding("US-ASCII")).encoding)
+ assert_equal(Encoding::ASCII_8BIT, CGI::escape("\xC0\<\<".dup.force_encoding("ASCII-8BIT")).encoding)
+ assert_equal(Encoding::UTF_8, CGI::escape("\xC0\<\<".dup.force_encoding("UTF-8")).encoding)
end
def test_cgi_unescape
- assert_equal(@str1, CGI::unescape('%26%3C%3E%22+%E3%82%86%E3%82%93%E3%82%86%E3%82%93'))
- assert_equal(@str1.encoding, CGI::unescape('%26%3C%3E%22+%E3%82%86%E3%82%93%E3%82%86%E3%82%93').encoding) if defined?(::Encoding)
+ str = CGI::unescape('%26%3C%3E%22+%E3%82%86%E3%82%93%E3%82%86%E3%82%93')
+ assert_equal(@str1, str)
+ return unless defined?(::Encoding)
+
+ assert_equal(@str1.encoding, str.encoding)
assert_equal("\u{30E1 30E2 30EA 691C 7D22}", CGI.unescape("\u{30E1 30E2 30EA}%E6%A4%9C%E7%B4%A2"))
end
def test_cgi_unescape_preserve_encoding
- assert_equal(Encoding::US_ASCII, CGI::unescape("%C0%3C%3C".force_encoding("US-ASCII")).encoding)
- assert_equal(Encoding::ASCII_8BIT, CGI::unescape("%C0%3C%3C".force_encoding("ASCII-8BIT")).encoding)
- assert_equal(Encoding::UTF_8, CGI::unescape("%C0%3C%3C".force_encoding("UTF-8")).encoding)
+ assert_equal(Encoding::US_ASCII, CGI::unescape("%C0%3C%3C".dup.force_encoding("US-ASCII")).encoding)
+ assert_equal(Encoding::ASCII_8BIT, CGI::unescape("%C0%3C%3C".dup.force_encoding("ASCII-8BIT")).encoding)
+ assert_equal(Encoding::UTF_8, CGI::unescape("%C0%3C%3C".dup.force_encoding("UTF-8")).encoding)
+ end
+
+ def test_cgi_unescape_accept_charset
+ return unless defined?(::Encoding)
+
+ assert_raise(TypeError) {CGI.unescape('', nil)}
+ assert_separately(%w[-rcgi/util], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ assert_equal("", CGI.unescape(''))
+ end;
end
def test_cgi_pretty
@@ -63,7 +82,7 @@ class CGIUtilTest < Test::Unit::TestCase
end
def test_cgi_escape_html_duplicated
- orig = "Ruby".force_encoding("US-ASCII")
+ orig = "Ruby".dup.force_encoding("US-ASCII")
str = CGI::escapeHTML(orig)
assert_equal(orig, str)
assert_not_same(orig, str)
@@ -81,10 +100,10 @@ class CGIUtilTest < Test::Unit::TestCase
end
def test_cgi_escape_html_preserve_tainted
- assert_not_predicate CGI::escapeHTML("'&\"><"), :tainted?
- assert_predicate CGI::escapeHTML("'&\"><".taint), :tainted?
- assert_not_predicate CGI::escapeHTML("Ruby"), :tainted?
- assert_predicate CGI::escapeHTML("Ruby".taint), :tainted?
+ assert_not_predicate CGI::escapeHTML("'&\"><"), :tainted?
+ assert_predicate CGI::escapeHTML("'&\"><".dup.taint), :tainted?
+ assert_not_predicate CGI::escapeHTML("Ruby"), :tainted?
+ assert_predicate CGI::escapeHTML("Ruby".dup.taint), :tainted?
end
def test_cgi_escape_html_dont_freeze
@@ -152,8 +171,11 @@ class CGIUtilTest < Test::Unit::TestCase
end
def test_cgi_include_unescape
- assert_equal(@str1, unescape('%26%3C%3E%22+%E3%82%86%E3%82%93%E3%82%86%E3%82%93'))
- assert_equal(@str1.encoding, unescape('%26%3C%3E%22+%E3%82%86%E3%82%93%E3%82%86%E3%82%93').encoding) if defined?(::Encoding)
+ str = unescape('%26%3C%3E%22+%E3%82%86%E3%82%93%E3%82%86%E3%82%93')
+ assert_equal(@str1, str)
+ return unless defined?(::Encoding)
+
+ assert_equal(@str1.encoding, str.encoding)
assert_equal("\u{30E1 30E2 30EA 691C 7D22}", unescape("\u{30E1 30E2 30EA}%E6%A4%9C%E7%B4%A2"))
end
diff --git a/test/coverage/test_coverage.rb b/test/coverage/test_coverage.rb
index 0753ccddc5..1625d48845 100644
--- a/test/coverage/test_coverage.rb
+++ b/test/coverage/test_coverage.rb
@@ -2,60 +2,75 @@
require "test/unit"
require "coverage"
require "tmpdir"
+require "envutil"
class TestCoverage < Test::Unit::TestCase
def test_result_without_start
- assert_raise(RuntimeError) {Coverage.result}
+ assert_in_out_err(%w[-rcoverage], <<-"end;", [], /coverage measurement is not enabled/)
+ Coverage.result
+ p :NG
+ end;
end
def test_peek_result_without_start
- assert_raise(RuntimeError) {Coverage.peek_result}
+ assert_in_out_err(%w[-rcoverage], <<-"end;", [], /coverage measurement is not enabled/)
+ Coverage.peek_result
+ p :NG
+ end;
end
def test_result_with_nothing
- Coverage.start
- result = Coverage.result
- assert_kind_of(Hash, result)
- result.each do |key, val|
- assert_kind_of(String, key)
- assert_kind_of(Array, val)
- end
+ assert_in_out_err(%w[-rcoverage], <<-"end;", ["{}"], [])
+ Coverage.start
+ p Coverage.result
+ end;
end
- def test_coverage_snapshot
- loaded_features = $".dup
+ def test_coverage_running?
+ assert_in_out_err(%w[-rcoverage], <<-"end;", ["false", "true", "true", "false"], [])
+ p Coverage.running?
+ Coverage.start
+ p Coverage.running?
+ Coverage.peek_result
+ p Coverage.running?
+ Coverage.result
+ p Coverage.running?
+ end;
+ end
+ def test_coverage_snapshot
Dir.mktmpdir {|tmp|
Dir.chdir(tmp) {
File.open("test.rb", "w") do |f|
f.puts <<-EOS
- def TestCoverage.coverage_test_snapshot
+ def coverage_test_snapshot
:ok
end
EOS
end
- Coverage.start
- require tmp + '/test.rb'
- cov = Coverage.peek_result[tmp + '/test.rb']
- TestCoverage.coverage_test_snapshot
- cov2 = Coverage.peek_result[tmp + '/test.rb']
- assert_equal cov[1] + 1, cov2[1]
- assert_equal cov2, Coverage.result[tmp + '/test.rb']
+ assert_in_out_err(%w[-rcoverage], <<-"end;", ["[1, 0, nil]", "[1, 1, nil]", "[1, 1, nil]"], [])
+ Coverage.start
+ tmp = Dir.pwd
+ require tmp + "/test.rb"
+ cov = Coverage.peek_result[tmp + "/test.rb"]
+ coverage_test_snapshot
+ cov2 = Coverage.peek_result[tmp + "/test.rb"]
+ p cov
+ p cov2
+ p Coverage.result[tmp + "/test.rb"]
+ end;
}
}
- ensure
- $".replace loaded_features
end
def test_restarting_coverage
- loaded_features = $".dup
-
Dir.mktmpdir {|tmp|
Dir.chdir(tmp) {
+ tmp = Dir.pwd
File.open("test.rb", "w") do |f|
f.puts <<-EOS
- def TestCoverage.coverage_test_restarting
+ def coverage_test_restarting
:ok
end
EOS
@@ -67,32 +82,32 @@ class TestCoverage < Test::Unit::TestCase
EOS
end
- Coverage.start
- require tmp + '/test.rb'
- cov = { "#{tmp}/test.rb" => [1, 0, nil] }
- assert_equal cov, Coverage.result
+ exp1 = { "#{tmp}/test.rb" => [1, 0, nil] }.inspect
+ exp2 = {}.inspect
+ exp3 = { "#{tmp}/test2.rb" => [1] }.inspect
+ assert_in_out_err(%w[-rcoverage], <<-"end;", [exp1, exp2, exp3], [])
+ Coverage.start
+ tmp = Dir.pwd
+ require tmp + "/test.rb"
+ p Coverage.result
- # Restart coverage but '/test.rb' is required before restart,
- # so coverage is not recorded.
- Coverage.start
- TestCoverage.coverage_test_restarting
- assert_equal({}, Coverage.result)
+ # Restart coverage but '/test.rb' is required before restart,
+ # so coverage is not recorded.
+ Coverage.start
+ coverage_test_restarting
+ p Coverage.result
- # Restart coverage and '/test2.rb' is required after restart,
- # so coverage is recorded.
- Coverage.start
- require tmp + '/test2.rb'
- cov = { "#{tmp}/test2.rb" => [1] }
- assert_equal cov, Coverage.result
+ # Restart coverage and '/test2.rb' is required after restart,
+ # so coverage is recorded.
+ Coverage.start
+ require tmp + "/test2.rb"
+ p Coverage.result
+ end;
}
}
- ensure
- $".replace loaded_features
end
def test_big_code
- loaded_features = $".dup
-
Dir.mktmpdir {|tmp|
Dir.chdir(tmp) {
File.open("test.rb", "w") do |f|
@@ -102,21 +117,595 @@ class TestCoverage < Test::Unit::TestCase
f.puts "])"
end
- Coverage.start
- require tmp + '/test.rb'
- assert_equal 10003, Coverage.result[tmp + '/test.rb'].size
+ assert_in_out_err(%w[-rcoverage], <<-"end;", ["10003"], [])
+ Coverage.start
+ tmp = Dir.pwd
+ require tmp + '/test.rb'
+ p Coverage.result[tmp + '/test.rb'].size
+ end;
+ }
+ }
+ end
+
+ def test_eval
+ bug13305 = '[ruby-core:80079] [Bug #13305]'
+
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ File.open("test.rb", "w") do |f|
+ f.puts 'REPEATS = 400'
+ f.puts 'def add_method(target)'
+ f.puts ' REPEATS.times do'
+ f.puts ' target.class_eval(<<~RUBY, __FILE__, __LINE__ + 1)'
+ f.puts ' def foo'
+ f.puts ' #{"\n" * rand(REPEATS)}'
+ f.puts ' end'
+ f.puts ' 1'
+ f.puts ' RUBY'
+ f.puts ' end'
+ f.puts 'end'
+ end
+
+ assert_in_out_err(%w[-W0 -rcoverage], <<-"end;", ["[1, 1, 1, 400, nil, nil, nil, nil, nil, nil, nil]"], [], bug13305)
+ Coverage.start
+ tmp = Dir.pwd
+ require tmp + '/test.rb'
+ add_method(Class.new)
+ p Coverage.result[tmp + "/test.rb"]
+ end;
+ }
+ }
+ end
+
+ def test_nocoverage_optimized_line
+ assert_ruby_status(%w[], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ def foo(x)
+ x # optimized away
+ nil
+ end
+ end;
+ end
+
+ def test_coverage_optimized_branch
+ result = {
+ :branches => {
+ [:"&.", 0, 1, 0, 1, 8] => {
+ [:then, 1, 1, 0, 1, 8] => 0,
+ [:else, 2, 1, 0, 1, 8] => 1,
+ },
+ },
+ }
+ assert_coverage(<<~"end;", { branches: true }, result) # Bug #15476
+ nil&.foo
+ end;
+ end
+
+ def assert_coverage(code, opt, stdout)
+ stdout = [stdout] unless stdout.is_a?(Array)
+ stdout = stdout.map {|s| s.to_s }
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ File.write("test.rb", code)
+
+ assert_in_out_err(%w[-W0 -rcoverage], <<-"end;", stdout, [])
+ Coverage.start(#{ opt })
+ tmp = Dir.pwd
+ require tmp + '/test.rb'
+ r = Coverage.result[tmp + "/test.rb"]
+ if r[:methods]
+ h = {}
+ r[:methods].keys.sort_by {|key| key.drop(1) }.each do |key|
+ h[key] = r[:methods][key]
+ end
+ r[:methods].replace h
+ end
+ p r
+ end;
+ }
+ }
+ end
+
+ def test_line_coverage_for_multiple_lines
+ result = {
+ :lines => [nil, 1, nil, nil, nil, 1, nil, nil, nil, 1, nil, 1, nil, nil, nil, nil, 1, 1, nil, 1, nil, nil, nil, nil, 1]
+ }
+ assert_coverage(<<~"end;", { lines: true }, result) # Bug #14191
+ FOO = [
+ { foo: 'bar' },
+ { bar: 'baz' }
+ ]
+
+ 'some string'.split
+ .map(&:length)
+
+ some =
+ 'value'
+
+ Struct.new(
+ :foo,
+ :bar
+ ).new
+
+ class Test
+ def foo(bar)
+ {
+ foo: bar
+ }
+ end
+ end
+
+ Test.new.foo(Object.new)
+ end;
+ end
+
+ def test_branch_coverage_for_if_statement
+ result = {
+ :branches => {
+ [:if , 0, 2, 2, 6, 5] => {[:then, 1, 3, 4, 3, 5]=>2, [:else, 2, 5, 4, 5, 5]=>1},
+ [:unless, 3, 8, 2, 12, 5] => {[:else, 4, 11, 4, 11, 5]=>2, [:then, 5, 9, 4, 9, 5]=>1},
+ [:if , 6, 14, 2, 16, 5] => {[:then, 7, 15, 4, 15, 5]=>2, [:else, 8, 14, 2, 16, 5]=>1},
+ [:unless, 9, 18, 2, 20, 5] => {[:else, 10, 18, 2, 20, 5]=>2, [:then, 11, 19, 4, 19, 5]=>1},
+ [:if , 12, 22, 2, 22, 13] => {[:then, 13, 22, 2, 22, 3]=>2, [:else, 14, 22, 2, 22, 13]=>1},
+ [:unless, 15, 23, 2, 23, 17] => {[:else, 16, 23, 2, 23, 17]=>2, [:then, 17, 23, 2, 23, 3]=>1},
+ [:if , 18, 25, 2, 25, 16] => {[:then, 19, 25, 11, 25, 12]=>2, [:else, 20, 25, 15, 25, 16]=>1},
+ }
+ }
+ assert_coverage(<<~"end;", { branches: true }, result)
+ def foo(x)
+ if x == 0
+ 0
+ else
+ 1
+ end
+
+ unless x == 0
+ 0
+ else
+ 1
+ end
+
+ if x == 0
+ 0
+ end
+
+ unless x == 0
+ 0
+ end
+
+ 0 if x == 0
+ 0 unless x == 0
+
+ x == 0 ? 0 : 1
+ end
+
+ foo(0)
+ foo(0)
+ foo(1)
+ end;
+ end
+
+ def test_branch_coverage_for_while_statement
+ result = {
+ :branches => {
+ [:while, 0, 2, 0, 4, 3] => {[:body, 1, 3, 2, 3, 8]=> 3},
+ [:until, 2, 5, 0, 7, 3] => {[:body, 3, 6, 2, 6, 8]=>10},
+ [:while, 4, 10, 0, 10, 18] => {[:body, 5, 10, 0, 10, 6]=> 3},
+ [:until, 6, 11, 0, 11, 20] => {[:body, 7, 11, 0, 11, 6]=>10},
+ }
+ }
+ assert_coverage(<<~"end;", { branches: true }, result)
+ x = 3
+ while x > 0
+ x -= 1
+ end
+ until x == 10
+ x += 1
+ end
+
+ y = 3
+ y -= 1 while y > 0
+ y += 1 until y == 10
+ end;
+ end
+
+ def test_branch_coverage_for_case_statement
+ result = {
+ :branches => {
+ [:case, 0, 2, 2, 7, 5] => {[:when, 1, 4, 4, 4, 5]=>2, [:when, 2, 6, 4, 6, 5]=>0, [:else, 3, 2, 2, 7, 5]=>1},
+ [:case, 4, 9, 2, 14, 5] => {[:when, 5, 11, 4, 11, 5]=>2, [:when, 6, 13, 4, 13, 5]=>0, [:else, 7, 9, 2, 14, 5]=>1},
+ [:case, 8, 16, 2, 23, 5] => {[:when, 9, 18, 4, 18, 5]=>2, [:when, 10, 20, 4, 20, 5]=>0, [:else, 11, 22, 4, 22, 10]=>1},
+ [:case, 12, 25, 2, 32, 5] => {[:when, 13, 27, 4, 27, 5]=>2, [:when, 14, 29, 4, 29, 5]=>0, [:else, 15, 31, 4, 31, 10]=>1},
+ }
+ }
+ assert_coverage(<<~"end;", { branches: true }, result)
+ def foo(x)
+ case x
+ when 0
+ 0
+ when 1
+ 1
+ end
+
+ case
+ when x == 0
+ 0
+ when x == 1
+ 1
+ end
+
+ case x
+ when 0
+ 0
+ when 1
+ 1
+ else
+ :other
+ end
+
+ case
+ when x == 0
+ 0
+ when x == 1
+ 1
+ else
+ :other
+ end
+ end
+
+ foo(0)
+ foo(0)
+ foo(2)
+ end;
+ end
+
+ def test_branch_coverage_for_safe_method_invocation
+ result = {
+ :branches=>{
+ [:"&.", 0, 6, 0, 6, 6] => {[:then, 1, 6, 0, 6, 6]=>1, [:else, 2, 6, 0, 6, 6]=>0},
+ [:"&.", 3, 7, 0, 7, 6] => {[:then, 4, 7, 0, 7, 6]=>0, [:else, 5, 7, 0, 7, 6]=>1},
+ [:"&.", 6, 8, 0, 8, 10] => {[:then, 7, 8, 0, 8, 10]=>1, [:else, 8, 8, 0, 8, 10]=>0},
+ [:"&.", 9, 9, 0, 9, 10] => {[:then, 10, 9, 0, 9, 10]=>0, [:else, 11, 9, 0, 9, 10]=>1},
+ }
+ }
+ assert_coverage(<<~"end;", { branches: true }, result)
+ class Dummy; def foo; end; def foo=(x); end; end
+ a = Dummy.new
+ b = nil
+ c = Dummy.new
+ d = nil
+ a&.foo
+ b&.foo
+ c&.foo = 1
+ d&.foo = 1
+ end;
+ end
+
+ def test_method_coverage
+ result = {
+ :methods => {
+ [Object, :bar, 2, 0, 3, 3] => 1,
+ [Object, :baz, 4, 1, 4, 13] => 0,
+ [Object, :foo, 1, 0, 1, 12] => 2,
+ }
+ }
+ assert_coverage(<<~"end;", { methods: true }, result)
+ def foo; end
+ def bar
+ end
+ def baz; end
+
+ foo
+ foo
+ bar
+ end;
+ end
+
+ def test_method_coverage_for_define_method
+ result = {
+ :methods => {
+ [Object, :a, 6, 18, 6, 25] => 2,
+ [Object, :b, 7, 18, 8, 3] => 0,
+ [Object, :bar, 2, 20, 3, 1] => 1,
+ [Object, :baz, 4, 9, 4, 11] => 0,
+ [Object, :foo, 1, 20, 1, 22] => 2,
}
}
- ensure
- $".replace loaded_features
+ assert_coverage(<<~"end;", { methods: true }, result)
+ define_method(:foo) {}
+ define_method(:bar) {
+ }
+ f = proc {}
+ define_method(:baz, &f)
+ define_method(:a) do; end
+ define_method(:b) do
+ end
+
+ foo
+ foo
+ bar
+ a
+ a
+ end;
end
- def test_nonpositive_linenumber
- bug12517 = '[ruby-core:76141] [Bug #12517]'
- Coverage.start
- assert_nothing_raised(ArgumentError, bug12517) do
- RubyVM::InstructionSequence.compile(":ok", nil, "<compiled>", 0)
+ class DummyConstant < String
+ def inspect
+ self
end
- assert_include Coverage.result, "<compiled>"
end
-end unless ENV['COVERAGE']
+
+ def test_method_coverage_for_alias
+ _C = DummyConstant.new("C")
+ _M = DummyConstant.new("M")
+ code = <<~"end;"
+ module M
+ def foo
+ end
+ alias bar foo
+ end
+ class C
+ include M
+ def baz
+ end
+ alias qux baz
+ end
+ end;
+
+ result = {
+ :methods => {
+ [_C, :baz, 8, 2, 9, 5] => 0,
+ [_M, :foo, 2, 2, 3, 5] => 0,
+ }
+ }
+ assert_coverage(code, { methods: true }, result)
+
+ result = {
+ :methods => {
+ [_C, :baz, 8, 2, 9, 5] => 12,
+ [_M, :foo, 2, 2, 3, 5] => 3,
+ }
+ }
+ assert_coverage(code + <<~"end;", { methods: true }, result)
+ obj = C.new
+ 1.times { obj.foo }
+ 2.times { obj.bar }
+ 4.times { obj.baz }
+ 8.times { obj.qux }
+ end;
+ end
+
+ def test_method_coverage_for_singleton_class
+ _singleton_Foo = DummyConstant.new("#<Class:Foo>")
+ _Foo = DummyConstant.new("Foo")
+ code = <<~"end;"
+ class Foo
+ def foo
+ end
+ alias bar foo
+ def self.baz
+ end
+ class << self
+ alias qux baz
+ end
+ end
+
+ 1.times { Foo.new.foo }
+ 2.times { Foo.new.bar }
+ 4.times { Foo.baz }
+ 8.times { Foo.qux }
+ end;
+
+ result = {
+ :methods => {
+ [_singleton_Foo, :baz, 5, 2, 6, 5] => 12,
+ [_Foo, :foo, 2, 2, 3, 5] => 3,
+ }
+ }
+ assert_coverage(code, { methods: true }, result)
+ end
+
+ def test_oneshot_line_coverage
+ result = {
+ :oneshot_lines => [2, 6, 10, 12, 17, 18, 25, 20]
+ }
+ assert_coverage(<<~"end;", { oneshot_lines: true }, result)
+ FOO = [
+ { foo: 'bar' }, # 2
+ { bar: 'baz' }
+ ]
+
+ 'some string'.split # 6
+ .map(&:length)
+
+ some =
+ 'value' # 10
+
+ Struct.new( # 12
+ :foo,
+ :bar
+ ).new
+
+ class Test # 17
+ def foo(bar) # 18
+ {
+ foo: bar # 20
+ }
+ end
+ end
+
+ Test.new.foo(Object.new) # 25
+ end;
+ end
+
+ def test_clear_with_lines
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ File.open("test.rb", "w") do |f|
+ f.puts "def foo(x)"
+ f.puts " if x > 0"
+ f.puts " :pos"
+ f.puts " else"
+ f.puts " :non_pos"
+ f.puts " end"
+ f.puts "end"
+ end
+
+ exp = [
+ "{:lines=>[1, 0, 0, nil, 0, nil, nil]}",
+ "{:lines=>[0, 1, 1, nil, 0, nil, nil]}",
+ "{:lines=>[0, 1, 0, nil, 1, nil, nil]}",
+ ]
+ assert_in_out_err(%w[-rcoverage], <<-"end;", exp, [])
+ Coverage.start(lines: true)
+ tmp = Dir.pwd
+ f = tmp + "/test.rb"
+ require f
+ p Coverage.result(stop: false, clear: true)[f]
+ foo(1)
+ p Coverage.result(stop: false, clear: true)[f]
+ foo(-1)
+ p Coverage.result[f]
+ end;
+ }
+ }
+ end
+
+ def test_clear_with_branches
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ File.open("test.rb", "w") do |f|
+ f.puts "def foo(x)"
+ f.puts " if x > 0"
+ f.puts " :pos"
+ f.puts " else"
+ f.puts " :non_pos"
+ f.puts " end"
+ f.puts "end"
+ end
+
+ exp = [
+ "{:branches=>{[:if, 0, 2, 2, 6, 5]=>{[:then, 1, 3, 4, 3, 8]=>0, [:else, 2, 5, 4, 5, 12]=>0}}}",
+ "{:branches=>{[:if, 0, 2, 2, 6, 5]=>{[:then, 1, 3, 4, 3, 8]=>1, [:else, 2, 5, 4, 5, 12]=>0}}}",
+ "{:branches=>{[:if, 0, 2, 2, 6, 5]=>{[:then, 1, 3, 4, 3, 8]=>0, [:else, 2, 5, 4, 5, 12]=>1}}}",
+ "{:branches=>{[:if, 0, 2, 2, 6, 5]=>{[:then, 1, 3, 4, 3, 8]=>0, [:else, 2, 5, 4, 5, 12]=>1}}}",
+ ]
+ assert_in_out_err(%w[-rcoverage], <<-"end;", exp, [])
+ Coverage.start(branches: true)
+ tmp = Dir.pwd
+ f = tmp + "/test.rb"
+ require f
+ p Coverage.result(stop: false, clear: true)[f]
+ foo(1)
+ p Coverage.result(stop: false, clear: true)[f]
+ foo(-1)
+ p Coverage.result(stop: false, clear: true)[f]
+ foo(-1)
+ p Coverage.result(stop: false, clear: true)[f]
+ end;
+ }
+ }
+ end
+
+ def test_clear_with_methods
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ File.open("test.rb", "w") do |f|
+ f.puts "def foo(x)"
+ f.puts " if x > 0"
+ f.puts " :pos"
+ f.puts " else"
+ f.puts " :non_pos"
+ f.puts " end"
+ f.puts "end"
+ end
+
+ exp = [
+ "{:methods=>{[Object, :foo, 1, 0, 7, 3]=>0}}",
+ "{:methods=>{[Object, :foo, 1, 0, 7, 3]=>1}}",
+ "{:methods=>{[Object, :foo, 1, 0, 7, 3]=>1}}",
+ "{:methods=>{[Object, :foo, 1, 0, 7, 3]=>1}}"
+ ]
+ assert_in_out_err(%w[-rcoverage], <<-"end;", exp, [])
+ Coverage.start(methods: true)
+ tmp = Dir.pwd
+ f = tmp + "/test.rb"
+ require f
+ p Coverage.result(stop: false, clear: true)[f]
+ foo(1)
+ p Coverage.result(stop: false, clear: true)[f]
+ foo(-1)
+ p Coverage.result(stop: false, clear: true)[f]
+ foo(-1)
+ p Coverage.result(stop: false, clear: true)[f]
+ end;
+ }
+ }
+ end
+
+ def test_clear_with_oneshot_lines
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ File.open("test.rb", "w") do |f|
+ f.puts "def foo(x)"
+ f.puts " if x > 0"
+ f.puts " :pos"
+ f.puts " else"
+ f.puts " :non_pos"
+ f.puts " end"
+ f.puts "end"
+ end
+
+ exp = [
+ "{:oneshot_lines=>[1]}",
+ "{:oneshot_lines=>[2, 3]}",
+ "{:oneshot_lines=>[5]}",
+ "{:oneshot_lines=>[]}",
+ ]
+ assert_in_out_err(%w[-rcoverage], <<-"end;", exp, [])
+ Coverage.start(oneshot_lines: true)
+ tmp = Dir.pwd
+ f = tmp + "/test.rb"
+ require f
+ p Coverage.result(stop: false, clear: true)[f]
+ foo(1)
+ p Coverage.result(stop: false, clear: true)[f]
+ foo(-1)
+ p Coverage.result(stop: false, clear: true)[f]
+ foo(-1)
+ p Coverage.result(stop: false, clear: true)[f]
+ end;
+ }
+ }
+ end
+
+ def test_line_stub
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ File.open("test.rb", "w") do |f|
+ f.puts "def foo(x)"
+ f.puts " if x > 0"
+ f.puts " :pos"
+ f.puts " else"
+ f.puts " :non_pos"
+ f.puts " end"
+ f.puts "end"
+ end
+
+ assert_equal([0, 0, 0, nil, 0, nil, nil], Coverage.line_stub("test.rb"))
+ }
+ }
+ end
+
+ def test_stop_wrong_peephole_optimization
+ result = {
+ :lines => [1, 1, 1, nil]
+ }
+ assert_coverage(<<~"end;", { lines: true }, result)
+ raise if 1 == 2
+ while true
+ break
+ end
+ end;
+ end
+end
diff --git a/test/csv/base.rb b/test/csv/base.rb
deleted file mode 100644
index a282c7afed..0000000000
--- a/test/csv/base.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: false
-require "test/unit"
-
-require "csv"
-
-require_relative "../lib/with_different_ofs.rb"
-
-class TestCSV < Test::Unit::TestCase
-end
diff --git a/test/csv/helper.rb b/test/csv/helper.rb
new file mode 100644
index 0000000000..2542cc9c97
--- /dev/null
+++ b/test/csv/helper.rb
@@ -0,0 +1,18 @@
+require "tempfile"
+require "test/unit"
+
+require "csv"
+
+require_relative "../lib/with_different_ofs.rb"
+
+module Helper
+ def with_chunk_size(chunk_size)
+ chunk_size_keep = ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"]
+ begin
+ ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"] = chunk_size
+ yield
+ ensure
+ ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"] = chunk_size_keep
+ end
+ end
+end
diff --git a/test/csv/interface/test_delegation.rb b/test/csv/interface/test_delegation.rb
new file mode 100644
index 0000000000..349257633b
--- /dev/null
+++ b/test/csv/interface/test_delegation.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVInterfaceDelegation < Test::Unit::TestCase
+ class TestStringIO < self
+ def setup
+ @csv = CSV.new("h1,h2")
+ end
+
+ def test_flock
+ assert_raise(NotImplementedError) do
+ @csv.flock(File::LOCK_EX)
+ end
+ end
+
+ def test_ioctl
+ assert_raise(NotImplementedError) do
+ @csv.ioctl(0)
+ end
+ end
+
+ def test_stat
+ assert_raise(NotImplementedError) do
+ @csv.stat
+ end
+ end
+
+ def test_to_i
+ assert_raise(NotImplementedError) do
+ @csv.to_i
+ end
+ end
+
+ def test_binmode?
+ assert_equal(false, @csv.binmode?)
+ end
+
+ def test_path
+ assert_equal(nil, @csv.path)
+ end
+
+ def test_to_io
+ assert_instance_of(StringIO, @csv.to_io)
+ end
+ end
+end
diff --git a/test/csv/interface/test_read.rb b/test/csv/interface/test_read.rb
new file mode 100644
index 0000000000..8fefbbb172
--- /dev/null
+++ b/test/csv/interface/test_read.rb
@@ -0,0 +1,277 @@
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVInterfaceRead < Test::Unit::TestCase
+ extend DifferentOFS
+
+ def setup
+ super
+ @data = ""
+ @data << "1\t2\t3\r\n"
+ @data << "4\t5\r\n"
+ @input = Tempfile.new(["interface-read", ".csv"], binmode: true)
+ @input << @data
+ @input.rewind
+ @rows = [
+ ["1", "2", "3"],
+ ["4", "5"],
+ ]
+ end
+
+ def teardown
+ @input.close(true)
+ super
+ end
+
+ def test_foreach
+ rows = []
+ CSV.foreach(@input.path, col_sep: "\t", row_sep: "\r\n").each do |row|
+ rows << row
+ end
+ assert_equal(@rows, rows)
+ end
+
+ def test_foreach_mode
+ rows = []
+ CSV.foreach(@input.path, "r", col_sep: "\t", row_sep: "\r\n").each do |row|
+ rows << row
+ end
+ assert_equal(@rows, rows)
+ end
+
+ def test_foreach_enumurator
+ rows = CSV.foreach(@input.path, col_sep: "\t", row_sep: "\r\n").to_a
+ assert_equal(@rows, rows)
+ end
+
+ def test_closed?
+ csv = CSV.open(@input.path, "r+", col_sep: "\t", row_sep: "\r\n")
+ assert_not_predicate(csv, :closed?)
+ csv.close
+ assert_predicate(csv, :closed?)
+ end
+
+ def test_open_auto_close
+ csv = nil
+ CSV.open(@input.path) do |_csv|
+ csv = _csv
+ end
+ assert_predicate(csv, :closed?)
+ end
+
+ def test_open_closed
+ csv = nil
+ CSV.open(@input.path) do |_csv|
+ csv = _csv
+ csv.close
+ end
+ assert_predicate(csv, :closed?)
+ end
+
+ def test_open_block_return_value
+ return_value = CSV.open(@input.path) do
+ "Return value."
+ end
+ assert_equal("Return value.", return_value)
+ end
+
+ def test_open_encoding_valid
+ # U+1F600 GRINNING FACE
+ # U+1F601 GRINNING FACE WITH SMILING EYES
+ File.open(@input.path, "w") do |file|
+ file << "\u{1F600},\u{1F601}"
+ end
+ CSV.open(@input.path, encoding: "utf-8") do |csv|
+ assert_equal([["\u{1F600}", "\u{1F601}"]],
+ csv.to_a)
+ end
+ end
+
+ def test_open_encoding_invalid
+ # U+1F600 GRINNING FACE
+ # U+1F601 GRINNING FACE WITH SMILING EYES
+ File.open(@input.path, "w") do |file|
+ file << "\u{1F600},\u{1F601}"
+ end
+ CSV.open(@input.path, encoding: "EUC-JP") do |csv|
+ error = assert_raise(CSV::MalformedCSVError) do
+ csv.shift
+ end
+ assert_equal("Invalid byte sequence in EUC-JP in line 1.",
+ error.message)
+ end
+ end
+
+ def test_open_encoding_nonexistent
+ _output, error = capture_io do
+ CSV.open(@input.path, encoding: "nonexistent") do
+ end
+ end
+ assert_equal("path:0: warning: Unsupported encoding nonexistent ignored\n",
+ error.gsub(/\A.+:\d+: /, "path:0: "))
+ end
+
+ def test_open_encoding_utf_8_with_bom
+ # U+FEFF ZERO WIDTH NO-BREAK SPACE, BOM
+ # U+1F600 GRINNING FACE
+ # U+1F601 GRINNING FACE WITH SMILING EYES
+ File.open(@input.path, "w") do |file|
+ file << "\u{FEFF}\u{1F600},\u{1F601}"
+ end
+ CSV.open(@input.path, encoding: "bom|utf-8") do |csv|
+ assert_equal([["\u{1F600}", "\u{1F601}"]],
+ csv.to_a)
+ end
+ end
+
+ def test_parse
+ assert_equal(@rows,
+ CSV.parse(@data, col_sep: "\t", row_sep: "\r\n"))
+ end
+
+ def test_parse_block
+ rows = []
+ CSV.parse(@data, col_sep: "\t", row_sep: "\r\n") do |row|
+ rows << row
+ end
+ assert_equal(@rows, rows)
+ end
+
+ def test_parse_enumerator
+ rows = CSV.parse(@data, col_sep: "\t", row_sep: "\r\n").to_a
+ assert_equal(@rows, rows)
+ end
+
+ def test_parse_headers_only
+ table = CSV.parse("a,b,c", headers: true)
+ assert_equal([
+ ["a", "b", "c"],
+ [],
+ ],
+ [
+ table.headers,
+ table.each.to_a,
+ ])
+ end
+
+ def test_parse_line
+ assert_equal(["1", "2", "3"],
+ CSV.parse_line("1;2;3", col_sep: ";"))
+ end
+
+ def test_parse_line_shortcut
+ assert_equal(["1", "2", "3"],
+ "1;2;3".parse_csv(col_sep: ";"))
+ end
+
+ def test_parse_line_empty
+ assert_equal(nil, CSV.parse_line("")) # to signal eof
+ end
+
+ def test_parse_line_empty_line
+ assert_equal([], CSV.parse_line("\n1,2,3"))
+ end
+
+ def test_read
+ assert_equal(@rows,
+ CSV.read(@input.path, col_sep: "\t", row_sep: "\r\n"))
+ end
+
+ def test_readlines
+ assert_equal(@rows,
+ CSV.readlines(@input.path, col_sep: "\t", row_sep: "\r\n"))
+ end
+
+ def test_open_read
+ rows = CSV.open(@input.path, col_sep: "\t", row_sep: "\r\n") do |csv|
+ csv.read
+ end
+ assert_equal(@rows, rows)
+ end
+
+ def test_open_readlines
+ rows = CSV.open(@input.path, col_sep: "\t", row_sep: "\r\n") do |csv|
+ csv.readlines
+ end
+ assert_equal(@rows, rows)
+ end
+
+ def test_table
+ table = CSV.table(@input.path, col_sep: "\t", row_sep: "\r\n")
+ assert_equal(CSV::Table.new([
+ CSV::Row.new([:"1", :"2", :"3"], [4, 5, nil]),
+ ]),
+ table)
+ end
+
+ def test_shift # aliased as gets() and readline()
+ CSV.open(@input.path, "rb+", col_sep: "\t", row_sep: "\r\n") do |csv|
+ rows = [
+ csv.shift,
+ csv.shift,
+ csv.shift,
+ ]
+ assert_equal(@rows + [nil],
+ rows)
+ end
+ end
+
+ def test_enumerator
+ CSV.open(@input.path, col_sep: "\t", row_sep: "\r\n") do |csv|
+ assert_equal(@rows, csv.each.to_a)
+ end
+ end
+
+ def test_shift_and_each
+ CSV.open(@input.path, col_sep: "\t", row_sep: "\r\n") do |csv|
+ rows = []
+ rows << csv.shift
+ rows.concat(csv.each.to_a)
+ assert_equal(@rows, rows)
+ end
+ end
+
+ def test_each_twice
+ CSV.open(@input.path, col_sep: "\t", row_sep: "\r\n") do |csv|
+ assert_equal([
+ @rows,
+ [],
+ ],
+ [
+ csv.each.to_a,
+ csv.each.to_a,
+ ])
+ end
+ end
+
+ def test_eof?
+ eofs = []
+ CSV.open(@input.path, col_sep: "\t", row_sep: "\r\n") do |csv|
+ eofs << csv.eof?
+ csv.shift
+ eofs << csv.eof?
+ csv.shift
+ eofs << csv.eof?
+ end
+ assert_equal([false, false, true],
+ eofs)
+ end
+
+ def test_new_nil
+ assert_raise_with_message ArgumentError, "Cannot parse nil as CSV" do
+ CSV.new(nil)
+ end
+ end
+
+ def test_options_not_modified
+ options = {}.freeze
+ CSV.foreach(@input.path, options)
+ CSV.open(@input.path, options) {}
+ CSV.parse("", options)
+ CSV.parse_line("", options)
+ CSV.read(@input.path, options)
+ CSV.readlines(@input.path, options)
+ CSV.table(@input.path, options)
+ end
+end
diff --git a/test/csv/interface/test_read_write.rb b/test/csv/interface/test_read_write.rb
new file mode 100644
index 0000000000..9ce3553d61
--- /dev/null
+++ b/test/csv/interface/test_read_write.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVInterfaceReadWrite < Test::Unit::TestCase
+ extend DifferentOFS
+
+ def test_filter
+ rows = [[1, 2, 3], [4, 5]]
+ input = <<-CSV
+1;2;3
+4;5
+ CSV
+ output = ""
+ CSV.filter(input, output,
+ in_col_sep: ";",
+ out_col_sep: ",",
+ converters: :all) do |row|
+ row.map! {|n| n * 2}
+ row << "Added\r"
+ end
+ assert_equal(<<-CSV, output)
+2,4,6,"Added\r"
+8,10,"Added\r"
+ CSV
+ end
+
+ def test_instance_same
+ data = ""
+ assert_equal(CSV.instance(data, col_sep: ";").object_id,
+ CSV.instance(data, col_sep: ";").object_id)
+ end
+
+ def test_instance_append
+ output = ""
+ CSV.instance(output, col_sep: ";") << ["a", "b", "c"]
+ assert_equal(<<-CSV, output)
+a;b;c
+ CSV
+ CSV.instance(output, col_sep: ";") << [1, 2, 3]
+ assert_equal(<<-CSV, output)
+a;b;c
+1;2;3
+ CSV
+ end
+
+ def test_instance_shortcut
+ assert_equal(CSV.instance,
+ CSV {|csv| csv})
+ end
+end
diff --git a/test/csv/interface/test_write.rb b/test/csv/interface/test_write.rb
new file mode 100644
index 0000000000..8511204ef0
--- /dev/null
+++ b/test/csv/interface/test_write.rb
@@ -0,0 +1,174 @@
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVInterfaceWrite < Test::Unit::TestCase
+ extend DifferentOFS
+
+ def setup
+ super
+ @output = Tempfile.new(["interface-write", ".csv"])
+ end
+
+ def teardown
+ @output.close(true)
+ super
+ end
+
+ def test_generate_default
+ csv_text = CSV.generate do |csv|
+ csv << [1, 2, 3] << [4, nil, 5]
+ end
+ assert_equal(<<-CSV, csv_text)
+1,2,3
+4,,5
+ CSV
+ end
+
+ def test_generate_append
+ csv_text = <<-CSV
+1,2,3
+4,,5
+ CSV
+ CSV.generate(csv_text) do |csv|
+ csv << ["last", %Q{"row"}]
+ end
+ assert_equal(<<-CSV, csv_text)
+1,2,3
+4,,5
+last,"""row"""
+ CSV
+ end
+
+ def test_generate_no_new_line
+ csv_text = CSV.generate("test") do |csv|
+ csv << ["row"]
+ end
+ assert_equal(<<-CSV, csv_text)
+testrow
+ CSV
+ end
+
+ def test_generate_line_col_sep
+ line = CSV.generate_line(["1", "2", "3"], col_sep: ";")
+ assert_equal(<<-LINE, line)
+1;2;3
+ LINE
+ end
+
+ def test_generate_line_row_sep
+ line = CSV.generate_line(["1", "2"], row_sep: nil)
+ assert_equal(<<-LINE.chomp, line)
+1,2
+ LINE
+ end
+
+ def test_generate_line_shortcut
+ line = ["1", "2", "3"].to_csv(col_sep: ";")
+ assert_equal(<<-LINE, line)
+1;2;3
+ LINE
+ end
+
+ def test_headers_detection
+ headers = ["a", "b", "c"]
+ CSV.open(@output.path, "w", headers: true) do |csv|
+ csv << headers
+ csv << ["1", "2", "3"]
+ assert_equal(headers, csv.headers)
+ end
+ end
+
+ def test_lineno
+ CSV.open(@output.path, "w") do |csv|
+ n_lines = 20
+ n_lines.times do
+ csv << ["a", "b", "c"]
+ end
+ assert_equal(n_lines, csv.lineno)
+ end
+ end
+
+ def test_append_row
+ CSV.open(@output.path, "wb") do |csv|
+ csv <<
+ CSV::Row.new([], ["1", "2", "3"]) <<
+ CSV::Row.new([], ["a", "b", "c"])
+ end
+ assert_equal(<<-CSV, File.read(@output.path, mode: "rb"))
+1,2,3
+a,b,c
+ CSV
+ end
+
+ def test_append_hash
+ CSV.open(@output.path, "wb", headers: true) do |csv|
+ csv << [:a, :b, :c]
+ csv << {a: 1, b: 2, c: 3}
+ csv << {a: 4, b: 5, c: 6}
+ end
+ assert_equal(<<-CSV, File.read(@output.path, mode: "rb"))
+a,b,c
+1,2,3
+4,5,6
+ CSV
+ end
+
+ def test_append_hash_headers_array
+ CSV.open(@output.path, "wb", headers: [:b, :a, :c]) do |csv|
+ csv << {a: 1, b: 2, c: 3}
+ csv << {a: 4, b: 5, c: 6}
+ end
+ assert_equal(<<-CSV, File.read(@output.path, mode: "rb"))
+2,1,3
+5,4,6
+ CSV
+ end
+
+ def test_append_hash_headers_string
+ CSV.open(@output.path, "wb", headers: "b|a|c", col_sep: "|") do |csv|
+ csv << {"a" => 1, "b" => 2, "c" => 3}
+ csv << {"a" => 4, "b" => 5, "c" => 6}
+ end
+ assert_equal(<<-CSV, File.read(@output.path, mode: "rb"))
+2|1|3
+5|4|6
+ CSV
+ end
+
+ def test_write_headers
+ CSV.open(@output.path,
+ "wb",
+ headers: "b|a|c",
+ write_headers: true,
+ col_sep: "|" ) do |csv|
+ csv << {"a" => 1, "b" => 2, "c" => 3}
+ csv << {"a" => 4, "b" => 5, "c" => 6}
+ end
+ assert_equal(<<-CSV, File.read(@output.path, mode: "rb"))
+b|a|c
+2|1|3
+5|4|6
+ CSV
+ end
+
+ def test_write_headers_empty
+ CSV.open(@output.path,
+ "wb",
+ headers: "b|a|c",
+ write_headers: true,
+ col_sep: "|" ) do |csv|
+ end
+ assert_equal(<<-CSV, File.read(@output.path, mode: "rb"))
+b|a|c
+ CSV
+ end
+
+ def test_options_not_modified
+ options = {}.freeze
+ CSV.generate(options) {}
+ CSV.generate_line([], options)
+ CSV.filter("", "", options)
+ CSV.instance("", options)
+ end
+end
diff --git a/test/csv/parse/test_column_separator.rb b/test/csv/parse/test_column_separator.rb
new file mode 100644
index 0000000000..d6eaa7b6de
--- /dev/null
+++ b/test/csv/parse/test_column_separator.rb
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVParseColumnSeparator < Test::Unit::TestCase
+ extend DifferentOFS
+
+ def test_comma
+ assert_equal([["a", "b", nil, "d"]],
+ CSV.parse("a,b,,d", col_sep: ","))
+ end
+
+ def test_space
+ assert_equal([["a", "b", nil, "d"]],
+ CSV.parse("a b d", col_sep: " "))
+ end
+
+ def test_tab
+ assert_equal([["a", "b", nil, "d"]],
+ CSV.parse("a\tb\t\td", col_sep: "\t"))
+ end
+
+ def test_multiple_characters_include_sub_separator
+ assert_equal([["a b", nil, "d"]],
+ CSV.parse("a b d", col_sep: " "))
+ end
+
+ def test_multiple_characters_leading_empty_fields
+ data = <<-CSV
+<=><=>A<=>B<=>C
+1<=>2<=>3
+ CSV
+ assert_equal([
+ [nil, nil, "A", "B", "C"],
+ ["1", "2", "3"],
+ ],
+ CSV.parse(data, col_sep: "<=>"))
+ end
+end
diff --git a/test/csv/parse/test_convert.rb b/test/csv/parse/test_convert.rb
new file mode 100644
index 0000000000..bfe6ddd527
--- /dev/null
+++ b/test/csv/parse/test_convert.rb
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 -*-
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVParseConvert < Test::Unit::TestCase
+ extend DifferentOFS
+
+ def setup
+ super
+ @data = "Numbers,:integer,1,:float,3.015"
+ @parser = CSV.new(@data)
+
+ @custom = lambda {|field| /\A:(\S.*?)\s*\Z/ =~ field ? $1.to_sym : field}
+
+ @time = Time.utc(2018, 12, 30, 6, 41, 29)
+ @windows_safe_time_data = @time.strftime("%a %b %d %H:%M:%S %Y")
+ end
+
+ def test_integer
+ @parser.convert(:integer)
+ assert_equal(["Numbers", ":integer", 1, ":float", "3.015"],
+ @parser.shift)
+ end
+
+ def test_float
+ @parser.convert(:float)
+ assert_equal(["Numbers", ":integer", 1.0, ":float", 3.015],
+ @parser.shift)
+ end
+
+ def test_float_integer
+ @parser.convert(:float)
+ @parser.convert(:integer)
+ assert_equal(["Numbers", ":integer", 1.0, ":float", 3.015],
+ @parser.shift)
+ end
+
+ def test_integer_float
+ @parser.convert(:integer)
+ @parser.convert(:float)
+ assert_equal(["Numbers", ":integer", 1, ":float", 3.015],
+ @parser.shift)
+ end
+
+ def test_numberic
+ @parser.convert(:numeric)
+ assert_equal(["Numbers", ":integer", 1, ":float", 3.015],
+ @parser.shift)
+ end
+
+ def test_all
+ @data << ",#{@windows_safe_time_data}"
+ @parser = CSV.new(@data)
+ @parser.convert(:all)
+ assert_equal(["Numbers", ":integer", 1, ":float", 3.015, @time.to_datetime],
+ @parser.shift)
+ end
+
+ def test_custom
+ @parser.convert do |field|
+ /\A:(\S.*?)\s*\Z/ =~ field ? $1.to_sym : field
+ end
+ assert_equal(["Numbers", :integer, "1", :float, "3.015"],
+ @parser.shift)
+ end
+
+ def test_builtin_custom
+ @parser.convert(:numeric)
+ @parser.convert(&@custom)
+ assert_equal(["Numbers", :integer, 1, :float, 3.015],
+ @parser.shift)
+ end
+
+ def test_custom_field_info_line
+ @parser.convert do |field, info|
+ assert_equal(1, info.line)
+ info.index == 4 ? Float(field).floor : field
+ end
+ assert_equal(["Numbers", ":integer", "1", ":float", 3],
+ @parser.shift)
+ end
+
+ def test_custom_field_info_header
+ headers = ["one", "two", "three", "four", "five"]
+ @parser = CSV.new(@data, headers: headers)
+ @parser.convert do |field, info|
+ info.header == "three" ? Integer(field) * 100 : field
+ end
+ assert_equal(CSV::Row.new(headers,
+ ["Numbers", ":integer", 100, ":float", "3.015"]),
+ @parser.shift)
+ end
+
+ def test_custom_blank_field
+ converter = lambda {|field| field.nil?}
+ row = CSV.parse_line('nil,', converters: converter)
+ assert_equal([false, true], row)
+ end
+
+ def test_nil_value
+ assert_equal(["nil", "", "a"],
+ CSV.parse_line(',"",a', nil_value: "nil"))
+ end
+
+ def test_empty_value
+ assert_equal([nil, "empty", "a"],
+ CSV.parse_line(',"",a', empty_value: "empty"))
+ end
+end
diff --git a/test/csv/parse/test_each.rb b/test/csv/parse/test_each.rb
new file mode 100644
index 0000000000..ce0b71d058
--- /dev/null
+++ b/test/csv/parse/test_each.rb
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVParseEach < Test::Unit::TestCase
+ extend DifferentOFS
+
+ def test_twice
+ data = <<-CSV
+Ruby,2.6.0,script
+ CSV
+ csv = CSV.new(data)
+ assert_equal([
+ [["Ruby", "2.6.0", "script"]],
+ [],
+ ],
+ [
+ csv.to_a,
+ csv.to_a,
+ ])
+ end
+end
diff --git a/test/csv/parse/test_general.rb b/test/csv/parse/test_general.rb
new file mode 100644
index 0000000000..49222c7159
--- /dev/null
+++ b/test/csv/parse/test_general.rb
@@ -0,0 +1,245 @@
+# -*- coding: utf-8 -*-
+# frozen_string_literal: false
+
+require "timeout"
+
+require_relative "../helper"
+
+#
+# Following tests are my interpretation of the
+# {CSV RCF}[http://www.ietf.org/rfc/rfc4180.txt]. I only deviate from that
+# document in one place (intentionally) and that is to make the default row
+# separator <tt>$/</tt>.
+#
+class TestCSVParseGeneral < Test::Unit::TestCase
+ extend DifferentOFS
+
+ BIG_DATA = "123456789\n" * 1024
+
+ def test_mastering_regex_example
+ ex = %Q{Ten Thousand,10000, 2710 ,,"10,000","It's ""10 Grand"", baby",10K}
+ assert_equal( [ "Ten Thousand", "10000", " 2710 ", nil, "10,000",
+ "It's \"10 Grand\", baby", "10K" ],
+ CSV.parse_line(ex) )
+ end
+
+ # Old Ruby 1.8 CSV library tests.
+ def test_std_lib_csv
+ [ ["\t", ["\t"]],
+ ["foo,\"\"\"\"\"\",baz", ["foo", "\"\"", "baz"]],
+ ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]],
+ ["\"\"\"\n\",\"\"\"\n\"", ["\"\n", "\"\n"]],
+ ["foo,\"\r\n\",baz", ["foo", "\r\n", "baz"]],
+ ["\"\"", [""]],
+ ["foo,\"\"\"\",baz", ["foo", "\"", "baz"]],
+ ["foo,\"\r.\n\",baz", ["foo", "\r.\n", "baz"]],
+ ["foo,\"\r\",baz", ["foo", "\r", "baz"]],
+ ["foo,\"\",baz", ["foo", "", "baz"]],
+ ["\",\"", [","]],
+ ["foo", ["foo"]],
+ [",,", [nil, nil, nil]],
+ [",", [nil, nil]],
+ ["foo,\"\n\",baz", ["foo", "\n", "baz"]],
+ ["foo,,baz", ["foo", nil, "baz"]],
+ ["\"\"\"\r\",\"\"\"\r\"", ["\"\r", "\"\r"]],
+ ["\",\",\",\"", [",", ","]],
+ ["foo,bar,", ["foo", "bar", nil]],
+ [",foo,bar", [nil, "foo", "bar"]],
+ ["foo,bar", ["foo", "bar"]],
+ [";", [";"]],
+ ["\t,\t", ["\t", "\t"]],
+ ["foo,\"\r\n\r\",baz", ["foo", "\r\n\r", "baz"]],
+ ["foo,\"\r\n\n\",baz", ["foo", "\r\n\n", "baz"]],
+ ["foo,\"foo,bar\",baz", ["foo", "foo,bar", "baz"]],
+ [";,;", [";", ";"]] ].each do |csv_test|
+ assert_equal(csv_test.last, CSV.parse_line(csv_test.first))
+ end
+
+ [ ["foo,\"\"\"\"\"\",baz", ["foo", "\"\"", "baz"]],
+ ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]],
+ ["foo,\"\r\n\",baz", ["foo", "\r\n", "baz"]],
+ ["\"\"", [""]],
+ ["foo,\"\"\"\",baz", ["foo", "\"", "baz"]],
+ ["foo,\"\r.\n\",baz", ["foo", "\r.\n", "baz"]],
+ ["foo,\"\r\",baz", ["foo", "\r", "baz"]],
+ ["foo,\"\",baz", ["foo", "", "baz"]],
+ ["foo", ["foo"]],
+ [",,", [nil, nil, nil]],
+ [",", [nil, nil]],
+ ["foo,\"\n\",baz", ["foo", "\n", "baz"]],
+ ["foo,,baz", ["foo", nil, "baz"]],
+ ["foo,bar", ["foo", "bar"]],
+ ["foo,\"\r\n\n\",baz", ["foo", "\r\n\n", "baz"]],
+ ["foo,\"foo,bar\",baz", ["foo", "foo,bar", "baz"]] ].each do |csv_test|
+ assert_equal(csv_test.last, CSV.parse_line(csv_test.first))
+ end
+ end
+
+ # From: http://ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-core/6496
+ def test_aras_edge_cases
+ [ [%Q{a,b}, ["a", "b"]],
+ [%Q{a,"""b"""}, ["a", "\"b\""]],
+ [%Q{a,"""b"}, ["a", "\"b"]],
+ [%Q{a,"b"""}, ["a", "b\""]],
+ [%Q{a,"\nb"""}, ["a", "\nb\""]],
+ [%Q{a,"""\nb"}, ["a", "\"\nb"]],
+ [%Q{a,"""\nb\n"""}, ["a", "\"\nb\n\""]],
+ [%Q{a,"""\nb\n""",\nc}, ["a", "\"\nb\n\"", nil]],
+ [%Q{a,,,}, ["a", nil, nil, nil]],
+ [%Q{,}, [nil, nil]],
+ [%Q{"",""}, ["", ""]],
+ [%Q{""""}, ["\""]],
+ [%Q{"""",""}, ["\"",""]],
+ [%Q{,""}, [nil,""]],
+ [%Q{,"\r"}, [nil,"\r"]],
+ [%Q{"\r\n,"}, ["\r\n,"]],
+ [%Q{"\r\n,",}, ["\r\n,", nil]] ].each do |edge_case|
+ assert_equal(edge_case.last, CSV.parse_line(edge_case.first))
+ end
+ end
+
+ def test_james_edge_cases
+ # A read at eof? should return nil.
+ assert_equal(nil, CSV.parse_line(""))
+ #
+ # With Ruby 1.8 CSV it's impossible to tell an empty line from a line
+ # containing a single +nil+ field. The old CSV library returns
+ # <tt>[nil]</tt> in these cases, but <tt>Array.new</tt> makes more sense to
+ # me.
+ #
+ assert_equal(Array.new, CSV.parse_line("\n1,2,3\n"))
+ end
+
+ def test_rob_edge_cases
+ [ [%Q{"a\nb"}, ["a\nb"]],
+ [%Q{"\n\n\n"}, ["\n\n\n"]],
+ [%Q{a,"b\n\nc"}, ['a', "b\n\nc"]],
+ [%Q{,"\r\n"}, [nil,"\r\n"]],
+ [%Q{,"\r\n."}, [nil,"\r\n."]],
+ [%Q{"a\na","one newline"}, ["a\na", 'one newline']],
+ [%Q{"a\n\na","two newlines"}, ["a\n\na", 'two newlines']],
+ [%Q{"a\r\na","one CRLF"}, ["a\r\na", 'one CRLF']],
+ [%Q{"a\r\n\r\na","two CRLFs"}, ["a\r\n\r\na", 'two CRLFs']],
+ [%Q{with blank,"start\n\nfinish"\n}, ['with blank', "start\n\nfinish"]],
+ ].each do |edge_case|
+ assert_equal(edge_case.last, CSV.parse_line(edge_case.first))
+ end
+ end
+
+ def test_non_regex_edge_cases
+ # An early version of the non-regex parser fails this test
+ [ [ "foo,\"foo,bar,baz,foo\",\"foo\"",
+ ["foo", "foo,bar,baz,foo", "foo"] ] ].each do |edge_case|
+ assert_equal(edge_case.last, CSV.parse_line(edge_case.first))
+ end
+
+ assert_raise(CSV::MalformedCSVError) do
+ CSV.parse_line("1,\"23\"4\"5\", 6")
+ end
+ end
+
+ def test_malformed_csv_cr_first_line
+ error = assert_raise(CSV::MalformedCSVError) do
+ CSV.parse_line("1,2\r,3", row_sep: "\n")
+ end
+ assert_equal("Unquoted fields do not allow new line <\"\\r\"> in line 1.",
+ error.message)
+ end
+
+ def test_malformed_csv_cr_middle_line
+ csv = <<-CSV
+line,1,abc
+line,2,"def\nghi"
+
+line,4,some\rjunk
+line,5,jkl
+ CSV
+
+ error = assert_raise(CSV::MalformedCSVError) do
+ CSV.parse(csv)
+ end
+ assert_equal("Unquoted fields do not allow new line <\"\\r\"> in line 4.",
+ error.message)
+ end
+
+ def test_malformed_csv_unclosed_quote
+ error = assert_raise(CSV::MalformedCSVError) do
+ CSV.parse_line('1,2,"3...')
+ end
+ assert_equal("Unclosed quoted field in line 1.",
+ error.message)
+ end
+
+ def test_malformed_csv_illegal_quote_middle_line
+ csv = <<-CSV
+line,1,abc
+line,2,"def\nghi"
+
+line,4,8'10"
+line,5,jkl
+ CSV
+
+ error = assert_raise(CSV::MalformedCSVError) do
+ CSV.parse(csv)
+ end
+ assert_equal("Illegal quoting in line 4.",
+ error.message)
+ end
+
+ def test_the_parse_fails_fast_when_it_can_for_unquoted_fields
+ assert_parse_errors_out('valid,fields,bad start"' + BIG_DATA)
+ end
+
+ def test_the_parse_fails_fast_when_it_can_for_unescaped_quotes
+ assert_parse_errors_out('valid,fields,"bad start"unescaped' + BIG_DATA)
+ end
+
+ def test_field_size_limit_controls_lookahead
+ assert_parse_errors_out( 'valid,fields,"' + BIG_DATA + '"',
+ field_size_limit: 2048 )
+ end
+
+ def test_field_size_limit_in_extended_column_not_exceeding
+ data = <<~DATA
+ "a","b"
+ "
+ 2
+ ",""
+ DATA
+ assert_nothing_raised(CSV::MalformedCSVError) do
+ CSV.parse(data, field_size_limit: 4)
+ end
+ end
+
+ def test_field_size_limit_in_extended_column_exceeding
+ data = <<~DATA
+ "a","b"
+ "
+ 2345
+ ",""
+ DATA
+ assert_parse_errors_out(data, field_size_limit: 5)
+ end
+
+ def test_row_sep_auto_cr
+ assert_equal([["a"]], CSV.parse("a\r"))
+ end
+
+ def test_row_sep_auto_lf
+ assert_equal([["a"]], CSV.parse("a\n"))
+ end
+
+ def test_row_sep_auto_cr_lf
+ assert_equal([["a"]], CSV.parse("a\r\n"))
+ end
+
+ private
+ def assert_parse_errors_out(*args)
+ assert_raise(CSV::MalformedCSVError) do
+ Timeout.timeout(0.2) do
+ CSV.parse(*args)
+ fail("Parse didn't error out")
+ end
+ end
+ end
+end
diff --git a/test/csv/parse/test_header.rb b/test/csv/parse/test_header.rb
new file mode 100644
index 0000000000..d92d823f61
--- /dev/null
+++ b/test/csv/parse/test_header.rb
@@ -0,0 +1,335 @@
+# -*- coding: utf-8 -*-
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVHeaders < Test::Unit::TestCase
+ extend DifferentOFS
+
+ def setup
+ super
+ @data = <<-CSV
+first,second,third
+A,B,C
+1,2,3
+ CSV
+ end
+
+ def test_first_row
+ [:first_row, true].each do |setting| # two names for the same setting
+ # activate headers
+ csv = nil
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse(@data, headers: setting)
+ end
+
+ # first data row - skipping headers
+ row = csv[0]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{first A}, %w{second B}, %w{third C}], row.to_a)
+
+ # second data row
+ row = csv[1]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{first 1}, %w{second 2}, %w{third 3}], row.to_a)
+
+ # empty
+ assert_nil(csv[2])
+ end
+ end
+
+ def test_array_of_headers
+ # activate headers
+ csv = nil
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse(@data, headers: [:my, :new, :headers])
+ end
+
+ # first data row - skipping headers
+ row = csv[0]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal( [[:my, "first"], [:new, "second"], [:headers, "third"]],
+ row.to_a )
+
+ # second data row
+ row = csv[1]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([[:my, "A"], [:new, "B"], [:headers, "C"]], row.to_a)
+
+ # third data row
+ row = csv[2]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([[:my, "1"], [:new, "2"], [:headers, "3"]], row.to_a)
+
+ # empty
+ assert_nil(csv[3])
+
+ # with return and convert
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse( @data, headers: [:my, :new, :headers],
+ return_headers: true,
+ header_converters: lambda { |h| h.to_s } )
+ end
+ row = csv[0]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([["my", :my], ["new", :new], ["headers", :headers]], row.to_a)
+ assert_predicate(row, :header_row?)
+ assert_not_predicate(row, :field_row?)
+ end
+
+ def test_csv_header_string
+ # activate headers
+ csv = nil
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse(@data, headers: "my,new,headers")
+ end
+
+ # first data row - skipping headers
+ row = csv[0]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{my first}, %w{new second}, %w{headers third}], row.to_a)
+
+ # second data row
+ row = csv[1]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{my A}, %w{new B}, %w{headers C}], row.to_a)
+
+ # third data row
+ row = csv[2]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{my 1}, %w{new 2}, %w{headers 3}], row.to_a)
+
+ # empty
+ assert_nil(csv[3])
+
+ # with return and convert
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse( @data, headers: "my,new,headers",
+ return_headers: true,
+ header_converters: :symbol )
+ end
+ row = csv[0]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([[:my, "my"], [:new, "new"], [:headers, "headers"]], row.to_a)
+ assert_predicate(row, :header_row?)
+ assert_not_predicate(row, :field_row?)
+ end
+
+ def test_csv_header_string_inherits_separators
+ # parse with custom col_sep
+ csv = nil
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse( @data.tr(",", "|"), col_sep: "|",
+ headers: "my|new|headers" )
+ end
+
+ # verify headers were recognized
+ row = csv[0]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{my first}, %w{new second}, %w{headers third}], row.to_a)
+ end
+
+ def test_return_headers
+ # activate headers and request they are returned
+ csv = nil
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse(@data, headers: true, return_headers: true)
+ end
+
+ # header row
+ row = csv[0]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal( [%w{first first}, %w{second second}, %w{third third}],
+ row.to_a )
+ assert_predicate(row, :header_row?)
+ assert_not_predicate(row, :field_row?)
+
+ # first data row - skipping headers
+ row = csv[1]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{first A}, %w{second B}, %w{third C}], row.to_a)
+ assert_not_predicate(row, :header_row?)
+ assert_predicate(row, :field_row?)
+
+ # second data row
+ row = csv[2]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{first 1}, %w{second 2}, %w{third 3}], row.to_a)
+ assert_not_predicate(row, :header_row?)
+ assert_predicate(row, :field_row?)
+
+ # empty
+ assert_nil(csv[3])
+ end
+
+ def test_converters
+ # create test data where headers and fields look alike
+ data = <<-CSV
+1,2,3
+1,2,3
+ CSV
+
+ # normal converters do not affect headers
+ csv = CSV.parse( data, headers: true,
+ return_headers: true,
+ converters: :numeric )
+ assert_equal([%w{1 1}, %w{2 2}, %w{3 3}], csv[0].to_a)
+ assert_equal([["1", 1], ["2", 2], ["3", 3]], csv[1].to_a)
+ assert_nil(csv[2])
+
+ # header converters do affect headers (only)
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse( data, headers: true,
+ return_headers: true,
+ converters: :numeric,
+ header_converters: :symbol )
+ end
+ assert_equal([[:"1", "1"], [:"2", "2"], [:"3", "3"]], csv[0].to_a)
+ assert_equal([[:"1", 1], [:"2", 2], [:"3", 3]], csv[1].to_a)
+ assert_nil(csv[2])
+ end
+
+ def test_builtin_downcase_converter
+ csv = CSV.parse( "One,TWO Three", headers: true,
+ return_headers: true,
+ header_converters: :downcase )
+ assert_equal(%w{one two\ three}, csv.headers)
+ end
+
+ def test_builtin_symbol_converter
+ # Note that the trailing space is intentional
+ csv = CSV.parse( "One,TWO Three ", headers: true,
+ return_headers: true,
+ header_converters: :symbol )
+ assert_equal([:one, :two_three], csv.headers)
+ end
+
+ def test_builtin_symbol_converter_with_punctuation
+ csv = CSV.parse( "One, Two & Three ($)", headers: true,
+ return_headers: true,
+ header_converters: :symbol )
+ assert_equal([:one, :two_three], csv.headers)
+ end
+
+ def test_builtin_converters_with_blank_header
+ csv = CSV.parse( "one,,three", headers: true,
+ return_headers: true,
+ header_converters: [:downcase, :symbol] )
+ assert_equal([:one, nil, :three], csv.headers)
+ end
+
+ def test_custom_converter
+ converter = lambda { |header| header.tr(" ", "_") }
+ csv = CSV.parse( "One,TWO Three",
+ headers: true,
+ return_headers: true,
+ header_converters: converter )
+ assert_equal(%w{One TWO_Three}, csv.headers)
+ end
+
+ def test_table_support
+ csv = nil
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse(@data, headers: true)
+ end
+
+ assert_instance_of(CSV::Table, csv)
+ end
+
+ def test_skip_blanks
+ @data = <<-CSV
+
+
+A,B,C
+
+1,2,3
+
+
+
+ CSV
+
+ expected = [%w[1 2 3]]
+ CSV.parse(@data, headers: true, skip_blanks: true) do |row|
+ assert_equal(expected.shift, row.fields)
+ end
+
+ expected = [%w[A B C], %w[1 2 3]]
+ CSV.parse( @data,
+ headers: true,
+ return_headers: true,
+ skip_blanks: true ) do |row|
+ assert_equal(expected.shift, row.fields)
+ end
+ end
+
+ def test_headers_reader
+ # no headers
+ assert_nil(CSV.new(@data).headers)
+
+ # headers
+ csv = CSV.new(@data, headers: true)
+ assert_equal(true, csv.headers) # before headers are read
+ csv.shift # set headers
+ assert_equal(%w[first second third], csv.headers) # after headers are read
+ end
+
+ def test_blank_row
+ @data += "\n#{@data}" # add a blank row
+
+ # ensure that everything returned is a Row object
+ CSV.parse(@data, headers: true) do |row|
+ assert_instance_of(CSV::Row, row)
+ end
+ end
+
+ def test_nil_row_header
+ @data = <<-CSV
+A
+
+1
+ CSV
+
+ csv = CSV.parse(@data, headers: true)
+
+ # ensure nil row creates Row object with headers
+ row = csv[0]
+ assert_equal([["A"], [nil]],
+ [row.headers, row.fields])
+ end
+
+ def test_parse_empty
+ assert_equal(CSV::Table.new([], {}),
+ CSV.parse("", headers: true))
+ end
+
+ def test_parse_empty_line
+ assert_equal(CSV::Table.new([], {}),
+ CSV.parse("\n", headers: true))
+ end
+
+ def test_specified_empty
+ assert_equal(CSV::Table.new([],
+ headers: ["header1"]),
+ CSV.parse("", headers: ["header1"]))
+ end
+
+ def test_specified_empty_line
+ assert_equal(CSV::Table.new([CSV::Row.new(["header1"], [])],
+ headers: ["header1"]),
+ CSV.parse("\n", headers: ["header1"]))
+ end
+end
diff --git a/test/csv/parse/test_invalid.rb b/test/csv/parse/test_invalid.rb
new file mode 100644
index 0000000000..b84707c2cc
--- /dev/null
+++ b/test/csv/parse/test_invalid.rb
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVParseInvalid < Test::Unit::TestCase
+ def test_no_column_mixed_new_lines
+ error = assert_raise(CSV::MalformedCSVError) do
+ CSV.parse("\n" +
+ "\r")
+ end
+ assert_equal("New line must be <\"\\n\"> not <\"\\r\"> in line 2.",
+ error.message)
+ end
+
+ def test_ignore_invalid_line
+ csv = CSV.new(<<-CSV, headers: true, return_headers: true)
+head1,head2,head3
+aaa,bbb,ccc
+ddd,ee"e.fff
+ggg,hhh,iii
+ CSV
+ headers = ["head1", "head2", "head3"]
+ assert_equal(CSV::Row.new(headers, headers),
+ csv.shift)
+ assert_equal(CSV::Row.new(headers, ["aaa", "bbb", "ccc"]),
+ csv.shift)
+ error = assert_raise(CSV::MalformedCSVError) do
+ csv.shift
+ end
+ assert_equal("Illegal quoting in line 3.",
+ error.message)
+ assert_equal(CSV::Row.new(headers, ["ggg", "hhh", "iii"]),
+ csv.shift)
+ end
+end
diff --git a/test/csv/parse/test_liberal_parsing.rb b/test/csv/parse/test_liberal_parsing.rb
new file mode 100644
index 0000000000..2f7b34689f
--- /dev/null
+++ b/test/csv/parse/test_liberal_parsing.rb
@@ -0,0 +1,160 @@
+# -*- coding: utf-8 -*-
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVParseLiberalParsing < Test::Unit::TestCase
+ extend DifferentOFS
+
+ def test_middle_quote_start
+ input = '"Johnson, Dwayne",Dwayne "The Rock" Johnson'
+ error = assert_raise(CSV::MalformedCSVError) do
+ CSV.parse_line(input)
+ end
+ assert_equal("Illegal quoting in line 1.",
+ error.message)
+ assert_equal(["Johnson, Dwayne", 'Dwayne "The Rock" Johnson'],
+ CSV.parse_line(input, liberal_parsing: true))
+ end
+
+ def test_middle_quote_end
+ input = '"quoted" field'
+ error = assert_raise(CSV::MalformedCSVError) do
+ CSV.parse_line(input)
+ end
+ assert_equal("Any value after quoted field isn't allowed in line 1.",
+ error.message)
+ assert_equal(['"quoted" field'],
+ CSV.parse_line(input, liberal_parsing: true))
+ end
+
+ def test_quote_after_column_separator
+ error = assert_raise(CSV::MalformedCSVError) do
+ CSV.parse_line('is,this "three," or four,fields', liberal_parsing: true)
+ end
+ assert_equal("Unclosed quoted field in line 1.",
+ error.message)
+ end
+
+ def test_quote_before_column_separator
+ assert_equal(["is", 'this "three', ' or four"', "fields"],
+ CSV.parse_line('is,this "three, or four",fields',
+ liberal_parsing: true))
+ end
+
+ def test_backslash_quote
+ assert_equal([
+ "1",
+ "\"Hamlet says, \\\"Seems",
+ "\\\" madam! Nay it is; I know not \\\"seems.\\\"\"",
+ ],
+ CSV.parse_line('1,' +
+ '"Hamlet says, \"Seems,' +
+ '\" madam! Nay it is; I know not \"seems.\""',
+ liberal_parsing: true))
+ end
+
+ def test_space_quote
+ input = <<~CSV
+ Los Angeles, 34°03'N, 118°15'W
+ New York City, 40°42'46"N, 74°00'21"W
+ Paris, 48°51'24"N, 2°21'03"E
+ CSV
+ assert_equal(
+ [
+ ["Los Angeles", " 34°03'N", " 118°15'W"],
+ ["New York City", " 40°42'46\"N", " 74°00'21\"W"],
+ ["Paris", " 48°51'24\"N", " 2°21'03\"E"],
+ ],
+ CSV.parse(input, liberal_parsing: true))
+ end
+
+ def test_double_quote_outside_quote
+ data = %Q{a,""b""}
+ error = assert_raise(CSV::MalformedCSVError) do
+ CSV.parse(data)
+ end
+ assert_equal("Any value after quoted field isn't allowed in line 1.",
+ error.message)
+ assert_equal([
+ [["a", %Q{""b""}]],
+ [["a", %Q{"b"}]],
+ ],
+ [
+ CSV.parse(data, liberal_parsing: true),
+ CSV.parse(data,
+ liberal_parsing: {
+ double_quote_outside_quote: true,
+ }),
+ ])
+ end
+
+ class TestBackslashQuote < Test::Unit::TestCase
+ extend ::DifferentOFS
+
+ def test_double_quote_outside_quote
+ data = %Q{a,""b""}
+ assert_equal([
+ [["a", %Q{""b""}]],
+ [["a", %Q{"b"}]],
+ ],
+ [
+ CSV.parse(data,
+ liberal_parsing: {
+ backslash_quote: true
+ }),
+ CSV.parse(data,
+ liberal_parsing: {
+ backslash_quote: true,
+ double_quote_outside_quote: true
+ }),
+ ])
+ end
+
+ def test_unquoted_value
+ data = %q{\"\"a\"\"}
+ assert_equal([
+ [[%q{\"\"a\"\"}]],
+ [[%q{""a""}]],
+ ],
+ [
+ CSV.parse(data, liberal_parsing: true),
+ CSV.parse(data,
+ liberal_parsing: {
+ backslash_quote: true
+ }),
+ ])
+ end
+
+ def test_unquoted_value_multiple_characters_col_sep
+ data = %q{a<\\"b<=>x}
+ assert_equal([[%Q{a<"b}, "x"]],
+ CSV.parse(data,
+ col_sep: "<=>",
+ liberal_parsing: {
+ backslash_quote: true
+ }))
+ end
+
+ def test_quoted_value
+ data = %q{"\"\"a\"\""}
+ assert_equal([
+ [[%q{"\"\"a\"\""}]],
+ [[%q{""a""}]],
+ [[%q{""a""}]],
+ ],
+ [
+ CSV.parse(data, liberal_parsing: true),
+ CSV.parse(data,
+ liberal_parsing: {
+ backslash_quote: true
+ }),
+ CSV.parse(data,
+ liberal_parsing: {
+ backslash_quote: true,
+ double_quote_outside_quote: true
+ }),
+ ])
+ end
+ end
+end
diff --git a/test/csv/parse/test_quote_char_nil.rb b/test/csv/parse/test_quote_char_nil.rb
new file mode 100644
index 0000000000..fc3b646759
--- /dev/null
+++ b/test/csv/parse/test_quote_char_nil.rb
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVParseQuoteCharNil < Test::Unit::TestCase
+ extend DifferentOFS
+
+ def test_full
+ assert_equal(["a", "b"], CSV.parse_line(%Q{a,b}, quote_char: nil))
+ end
+
+ def test_end_with_nil
+ assert_equal(["a", nil, nil, nil], CSV.parse_line(%Q{a,,,}, quote_char: nil))
+ end
+
+ def test_nil_nil
+ assert_equal([nil, nil], CSV.parse_line(%Q{,}, quote_char: nil))
+ end
+
+ def test_unquoted_value_multiple_characters_col_sep
+ data = %q{a<b<=>x}
+ assert_equal([[%Q{a<b}, "x"]], CSV.parse(data, col_sep: "<=>", quote_char: nil))
+ end
+
+ def test_csv_header_string
+ data = <<~DATA
+ first,second,third
+ A,B,C
+ 1,2,3
+ DATA
+ assert_equal(
+ CSV::Table.new([
+ CSV::Row.new(["my", "new", "headers"], ["first", "second", "third"]),
+ CSV::Row.new(["my", "new", "headers"], ["A", "B", "C"]),
+ CSV::Row.new(["my", "new", "headers"], ["1", "2", "3"])
+ ]),
+ CSV.parse(data, headers: "my,new,headers", quote_char: nil)
+ )
+ end
+
+ def test_comma
+ assert_equal([["a", "b", nil, "d"]],
+ CSV.parse("a,b,,d", col_sep: ",", quote_char: nil))
+ end
+
+ def test_space
+ assert_equal([["a", "b", nil, "d"]],
+ CSV.parse("a b d", col_sep: " ", quote_char: nil))
+ end
+
+ def encode_array(array, encoding)
+ array.collect do |element|
+ element ? element.encode(encoding) : element
+ end
+ end
+
+ def test_space_no_ascii
+ encoding = Encoding::UTF_16LE
+ assert_equal([encode_array(["a", "b", nil, "d"], encoding)],
+ CSV.parse("a b d".encode(encoding),
+ col_sep: " ".encode(encoding),
+ quote_char: nil))
+ end
+
+ def test_multiple_space
+ assert_equal([["a b", nil, "d"]],
+ CSV.parse("a b d", col_sep: " ", quote_char: nil))
+ end
+
+ def test_multiple_characters_leading_empty_fields
+ data = <<-CSV
+<=><=>A<=>B<=>C
+1<=>2<=>3
+ CSV
+ assert_equal([
+ [nil, nil, "A", "B", "C"],
+ ["1", "2", "3"],
+ ],
+ CSV.parse(data, col_sep: "<=>", quote_char: nil))
+ end
+
+ def test_line
+ lines = [
+ "abc,def\n",
+ ]
+ csv = CSV.new(lines.join(""), quote_char: nil)
+ lines.each do |line|
+ csv.shift
+ assert_equal(line, csv.line)
+ end
+ end
+end
diff --git a/test/csv/parse/test_rewind.rb b/test/csv/parse/test_rewind.rb
new file mode 100644
index 0000000000..73a69e9ccd
--- /dev/null
+++ b/test/csv/parse/test_rewind.rb
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVParseRewind < Test::Unit::TestCase
+ extend DifferentOFS
+
+ def parse(data, options={})
+ csv = CSV.new(data, options)
+ records = csv.to_a
+ csv.rewind
+ [records, csv.to_a]
+ end
+
+ def test_default
+ data = <<-CSV
+Ruby,2.6.0,script
+ CSV
+ assert_equal([
+ [["Ruby", "2.6.0", "script"]],
+ [["Ruby", "2.6.0", "script"]],
+ ],
+ parse(data))
+ end
+
+ def test_have_headers
+ data = <<-CSV
+Language,Version,Type
+Ruby,2.6.0,script
+ CSV
+ assert_equal([
+ [CSV::Row.new(["Language", "Version", "Type"],
+ ["Ruby", "2.6.0", "script"])],
+ [CSV::Row.new(["Language", "Version", "Type"],
+ ["Ruby", "2.6.0", "script"])],
+ ],
+ parse(data, headers: true))
+ end
+end
diff --git a/test/csv/parse/test_row_separator.rb b/test/csv/parse/test_row_separator.rb
new file mode 100644
index 0000000000..eaf6adc910
--- /dev/null
+++ b/test/csv/parse/test_row_separator.rb
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVParseRowSeparator < Test::Unit::TestCase
+ extend DifferentOFS
+ include Helper
+
+ def test_multiple_characters
+ with_chunk_size("1") do
+ assert_equal([["a"], ["b"]],
+ CSV.parse("a\r\nb\r\n", row_sep: "\r\n"))
+ end
+ end
+end
diff --git a/test/csv/parse/test_skip_lines.rb b/test/csv/parse/test_skip_lines.rb
new file mode 100644
index 0000000000..196858f1b0
--- /dev/null
+++ b/test/csv/parse/test_skip_lines.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVParseSkipLines < Test::Unit::TestCase
+ extend DifferentOFS
+ include Helper
+
+ def test_default
+ csv = CSV.new("a,b,c\n")
+ assert_nil(csv.skip_lines)
+ end
+
+ def test_regexp
+ csv = <<-CSV
+1
+#2
+ #3
+4
+ CSV
+ assert_equal([
+ ["1"],
+ ["4"],
+ ],
+ CSV.parse(csv, :skip_lines => /\A\s*#/))
+ end
+
+ def test_regexp_quoted
+ csv = <<-CSV
+1
+#2
+"#3"
+4
+ CSV
+ assert_equal([
+ ["1"],
+ ["#3"],
+ ["4"],
+ ],
+ CSV.parse(csv, :skip_lines => /\A\s*#/))
+ end
+
+ def test_string
+ csv = <<-CSV
+1
+.2
+3.
+4
+ CSV
+ assert_equal([
+ ["1"],
+ ["4"],
+ ],
+ CSV.parse(csv, :skip_lines => "."))
+ end
+
+ class RegexStub
+ end
+
+ def test_not_matchable
+ regex_stub = RegexStub.new
+ csv = CSV.new("1\n", :skip_lines => regex_stub)
+ error = assert_raise(ArgumentError) do
+ csv.shift
+ end
+ assert_equal(":skip_lines has to respond to #match: #{regex_stub.inspect}",
+ error.message)
+ end
+
+ class Matchable
+ def initialize(pattern)
+ @pattern = pattern
+ end
+
+ def match(line)
+ @pattern.match(line)
+ end
+ end
+
+ def test_matchable
+ csv = <<-CSV
+1
+# 2
+3
+# 4
+ CSV
+ assert_equal([
+ ["1"],
+ ["3"],
+ ],
+ CSV.parse(csv, :skip_lines => Matchable.new(/\A#/)))
+ end
+
+ def test_multibyte_data
+ # U+3042 HIRAGANA LETTER A
+ # U+3044 HIRAGANA LETTER I
+ # U+3046 HIRAGANA LETTER U
+ value = "\u3042\u3044\u3046"
+ with_chunk_size("5") do
+ assert_equal([[value], [value]],
+ CSV.parse("#{value}\n#{value}\n",
+ :skip_lines => /\A#/))
+ end
+ end
+end
diff --git a/test/csv/parse/test_strip.rb b/test/csv/parse/test_strip.rb
new file mode 100644
index 0000000000..160407bd94
--- /dev/null
+++ b/test/csv/parse/test_strip.rb
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVParseStrip < Test::Unit::TestCase
+ extend DifferentOFS
+
+ def test_both
+ assert_equal(["a", "b"],
+ CSV.parse_line(%Q{ a , b }, strip: true))
+ end
+
+ def test_left
+ assert_equal(["a", "b"],
+ CSV.parse_line(%Q{ a, b}, strip: true))
+ end
+
+ def test_right
+ assert_equal(["a", "b"],
+ CSV.parse_line(%Q{a ,b }, strip: true))
+ end
+
+ def test_quoted
+ assert_equal([" a ", " b "],
+ CSV.parse_line(%Q{" a "," b "}, strip: true))
+ end
+
+ def test_liberal_parsing
+ assert_equal([" a ", "b", " c ", " d "],
+ CSV.parse_line(%Q{" a ", b , " c "," d " },
+ strip: true,
+ liberal_parsing: true))
+ end
+
+ def test_string
+ assert_equal(["a", " b"],
+ CSV.parse_line(%Q{ a , " b" },
+ strip: " "))
+ end
+
+ def test_no_quote
+ assert_equal([" a ", " b "],
+ CSV.parse_line(%Q{" a ", b },
+ strip: %Q{"},
+ quote_char: nil))
+ end
+end
diff --git a/test/csv/parse/test_unconverted_fields.rb b/test/csv/parse/test_unconverted_fields.rb
new file mode 100644
index 0000000000..437124ebd3
--- /dev/null
+++ b/test/csv/parse/test_unconverted_fields.rb
@@ -0,0 +1,117 @@
+# -*- coding: utf-8 -*-
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+class TestCSVParseUnconvertedFields < Test::Unit::TestCase
+ extend DifferentOFS
+
+ def setup
+ super
+ @custom = lambda {|field| /\A:(\S.*?)\s*\Z/ =~ field ? $1.to_sym : field}
+
+ @headers = ["first", "second", "third"]
+ @data = <<-CSV
+first,second,third
+1,2,3
+ CSV
+ end
+
+
+ def test_custom
+ row = CSV.parse_line("Numbers,:integer,1,:float,3.015",
+ converters: [:numeric, @custom],
+ unconverted_fields: true)
+ assert_equal([
+ ["Numbers", :integer, 1, :float, 3.015],
+ ["Numbers", ":integer", "1", ":float", "3.015"],
+ ],
+ [
+ row,
+ row.unconverted_fields,
+ ])
+ end
+
+ def test_no_fields
+ row = CSV.parse_line("\n",
+ converters: [:numeric, @custom],
+ unconverted_fields: true)
+ assert_equal([
+ [],
+ [],
+ ],
+ [
+ row,
+ row.unconverted_fields,
+ ])
+ end
+
+ def test_parsed_header
+ row = CSV.parse_line(@data,
+ converters: :numeric,
+ unconverted_fields: true,
+ headers: :first_row)
+ assert_equal([
+ CSV::Row.new(@headers,
+ [1, 2, 3]),
+ ["1", "2", "3"],
+ ],
+ [
+ row,
+ row.unconverted_fields,
+ ])
+ end
+
+ def test_return_headers
+ row = CSV.parse_line(@data,
+ converters: :numeric,
+ unconverted_fields: true,
+ headers: :first_row,
+ return_headers: true)
+ assert_equal([
+ CSV::Row.new(@headers,
+ @headers),
+ @headers,
+ ],
+ [
+ row,
+ row.unconverted_fields,
+ ])
+ end
+
+ def test_header_converters
+ row = CSV.parse_line(@data,
+ converters: :numeric,
+ unconverted_fields: true,
+ headers: :first_row,
+ return_headers: true,
+ header_converters: :symbol)
+ assert_equal([
+ CSV::Row.new(@headers.collect(&:to_sym),
+ @headers),
+ @headers,
+ ],
+ [
+ row,
+ row.unconverted_fields,
+ ])
+ end
+
+ def test_specified_headers
+ row = CSV.parse_line("\n",
+ converters: :numeric,
+ unconverted_fields: true,
+ headers: %w{my new headers},
+ return_headers: true,
+ header_converters: :symbol)
+ assert_equal([
+ CSV::Row.new([:my, :new, :headers],
+ ["my", "new", "headers"]),
+ [],
+ ],
+ [
+ row,
+ row.unconverted_fields,
+ ])
+ end
+end
diff --git a/test/csv/test_csv_parsing.rb b/test/csv/test_csv_parsing.rb
deleted file mode 100755
index ff3d65fd24..0000000000
--- a/test/csv/test_csv_parsing.rb
+++ /dev/null
@@ -1,222 +0,0 @@
-#!/usr/bin/env ruby -w
-# encoding: UTF-8
-# frozen_string_literal: false
-
-# tc_csv_parsing.rb
-#
-# Created by James Edward Gray II on 2005-10-31.
-# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
-# under the terms of Ruby's license.
-
-require "timeout"
-
-require_relative "base"
-
-#
-# Following tests are my interpretation of the
-# {CSV RCF}[http://www.ietf.org/rfc/rfc4180.txt]. I only deviate from that
-# document in one place (intentionally) and that is to make the default row
-# separator <tt>$/</tt>.
-#
-class TestCSV::Parsing < TestCSV
- extend DifferentOFS
-
- BIG_DATA = "123456789\n" * 1024
-
- def test_mastering_regex_example
- ex = %Q{Ten Thousand,10000, 2710 ,,"10,000","It's ""10 Grand"", baby",10K}
- assert_equal( [ "Ten Thousand", "10000", " 2710 ", nil, "10,000",
- "It's \"10 Grand\", baby", "10K" ],
- CSV.parse_line(ex) )
- end
-
- # Old Ruby 1.8 CSV library tests.
- def test_std_lib_csv
- [ ["\t", ["\t"]],
- ["foo,\"\"\"\"\"\",baz", ["foo", "\"\"", "baz"]],
- ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]],
- ["\"\"\"\n\",\"\"\"\n\"", ["\"\n", "\"\n"]],
- ["foo,\"\r\n\",baz", ["foo", "\r\n", "baz"]],
- ["\"\"", [""]],
- ["foo,\"\"\"\",baz", ["foo", "\"", "baz"]],
- ["foo,\"\r.\n\",baz", ["foo", "\r.\n", "baz"]],
- ["foo,\"\r\",baz", ["foo", "\r", "baz"]],
- ["foo,\"\",baz", ["foo", "", "baz"]],
- ["\",\"", [","]],
- ["foo", ["foo"]],
- [",,", [nil, nil, nil]],
- [",", [nil, nil]],
- ["foo,\"\n\",baz", ["foo", "\n", "baz"]],
- ["foo,,baz", ["foo", nil, "baz"]],
- ["\"\"\"\r\",\"\"\"\r\"", ["\"\r", "\"\r"]],
- ["\",\",\",\"", [",", ","]],
- ["foo,bar,", ["foo", "bar", nil]],
- [",foo,bar", [nil, "foo", "bar"]],
- ["foo,bar", ["foo", "bar"]],
- [";", [";"]],
- ["\t,\t", ["\t", "\t"]],
- ["foo,\"\r\n\r\",baz", ["foo", "\r\n\r", "baz"]],
- ["foo,\"\r\n\n\",baz", ["foo", "\r\n\n", "baz"]],
- ["foo,\"foo,bar\",baz", ["foo", "foo,bar", "baz"]],
- [";,;", [";", ";"]] ].each do |csv_test|
- assert_equal(csv_test.last, CSV.parse_line(csv_test.first))
- end
-
- [ ["foo,\"\"\"\"\"\",baz", ["foo", "\"\"", "baz"]],
- ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]],
- ["foo,\"\r\n\",baz", ["foo", "\r\n", "baz"]],
- ["\"\"", [""]],
- ["foo,\"\"\"\",baz", ["foo", "\"", "baz"]],
- ["foo,\"\r.\n\",baz", ["foo", "\r.\n", "baz"]],
- ["foo,\"\r\",baz", ["foo", "\r", "baz"]],
- ["foo,\"\",baz", ["foo", "", "baz"]],
- ["foo", ["foo"]],
- [",,", [nil, nil, nil]],
- [",", [nil, nil]],
- ["foo,\"\n\",baz", ["foo", "\n", "baz"]],
- ["foo,,baz", ["foo", nil, "baz"]],
- ["foo,bar", ["foo", "bar"]],
- ["foo,\"\r\n\n\",baz", ["foo", "\r\n\n", "baz"]],
- ["foo,\"foo,bar\",baz", ["foo", "foo,bar", "baz"]] ].each do |csv_test|
- assert_equal(csv_test.last, CSV.parse_line(csv_test.first))
- end
- end
-
- # From: http://ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-core/6496
- def test_aras_edge_cases
- [ [%Q{a,b}, ["a", "b"]],
- [%Q{a,"""b"""}, ["a", "\"b\""]],
- [%Q{a,"""b"}, ["a", "\"b"]],
- [%Q{a,"b"""}, ["a", "b\""]],
- [%Q{a,"\nb"""}, ["a", "\nb\""]],
- [%Q{a,"""\nb"}, ["a", "\"\nb"]],
- [%Q{a,"""\nb\n"""}, ["a", "\"\nb\n\""]],
- [%Q{a,"""\nb\n""",\nc}, ["a", "\"\nb\n\"", nil]],
- [%Q{a,,,}, ["a", nil, nil, nil]],
- [%Q{,}, [nil, nil]],
- [%Q{"",""}, ["", ""]],
- [%Q{""""}, ["\""]],
- [%Q{"""",""}, ["\"",""]],
- [%Q{,""}, [nil,""]],
- [%Q{,"\r"}, [nil,"\r"]],
- [%Q{"\r\n,"}, ["\r\n,"]],
- [%Q{"\r\n,",}, ["\r\n,", nil]] ].each do |edge_case|
- assert_equal(edge_case.last, CSV.parse_line(edge_case.first))
- end
- end
-
- def test_james_edge_cases
- # A read at eof? should return nil.
- assert_equal(nil, CSV.parse_line(""))
- #
- # With Ruby 1.8 CSV it's impossible to tell an empty line from a line
- # containing a single +nil+ field. The old CSV library returns
- # <tt>[nil]</tt> in these cases, but <tt>Array.new</tt> makes more sense to
- # me.
- #
- assert_equal(Array.new, CSV.parse_line("\n1,2,3\n"))
- end
-
- def test_rob_edge_cases
- [ [%Q{"a\nb"}, ["a\nb"]],
- [%Q{"\n\n\n"}, ["\n\n\n"]],
- [%Q{a,"b\n\nc"}, ['a', "b\n\nc"]],
- [%Q{,"\r\n"}, [nil,"\r\n"]],
- [%Q{,"\r\n."}, [nil,"\r\n."]],
- [%Q{"a\na","one newline"}, ["a\na", 'one newline']],
- [%Q{"a\n\na","two newlines"}, ["a\n\na", 'two newlines']],
- [%Q{"a\r\na","one CRLF"}, ["a\r\na", 'one CRLF']],
- [%Q{"a\r\n\r\na","two CRLFs"}, ["a\r\n\r\na", 'two CRLFs']],
- [%Q{with blank,"start\n\nfinish"\n}, ['with blank', "start\n\nfinish"]],
- ].each do |edge_case|
- assert_equal(edge_case.last, CSV.parse_line(edge_case.first))
- end
- end
-
- def test_non_regex_edge_cases
- # An early version of the non-regex parser fails this test
- [ [ "foo,\"foo,bar,baz,foo\",\"foo\"",
- ["foo", "foo,bar,baz,foo", "foo"] ] ].each do |edge_case|
- assert_equal(edge_case.last, CSV.parse_line(edge_case.first))
- end
-
- assert_raise(CSV::MalformedCSVError) do
- CSV.parse_line("1,\"23\"4\"5\", 6")
- end
- end
-
- def test_malformed_csv
- assert_raise(CSV::MalformedCSVError) do
- CSV.parse_line("1,2\r,3", row_sep: "\n")
- end
-
- bad_data = <<-END_DATA.gsub(/^ +/, "")
- line,1,abc
- line,2,"def\nghi"
-
- line,4,some\rjunk
- line,5,jkl
- END_DATA
- lines = bad_data.lines.to_a
- assert_equal(6, lines.size)
- assert_match(/\Aline,4/, lines.find { |l| l =~ /some\rjunk/ })
-
- csv = CSV.new(bad_data)
- begin
- loop do
- assert_not_nil(csv.shift)
- assert_send([csv.lineno, :<, 4])
- end
- rescue CSV::MalformedCSVError
- assert_equal( "Unquoted fields do not allow \\r or \\n (line 4).",
- $!.message )
- end
-
- assert_raise(CSV::MalformedCSVError) { CSV.parse_line('1,2,"3...') }
-
- bad_data = <<-END_DATA.gsub(/^ +/, "")
- line,1,abc
- line,2,"def\nghi"
-
- line,4,8'10"
- line,5,jkl
- END_DATA
- lines = bad_data.lines.to_a
- assert_equal(6, lines.size)
- assert_match(/\Aline,4/, lines.find { |l| l =~ /8'10"/ })
-
- csv = CSV.new(bad_data)
- begin
- loop do
- assert_not_nil(csv.shift)
- assert_send([csv.lineno, :<, 4])
- end
- rescue CSV::MalformedCSVError
- assert_equal("Illegal quoting in line 4.", $!.message)
- end
- end
-
- def test_the_parse_fails_fast_when_it_can_for_unquoted_fields
- assert_parse_errors_out('valid,fields,bad start"' + BIG_DATA)
- end
-
- def test_the_parse_fails_fast_when_it_can_for_unescaped_quotes
- assert_parse_errors_out('valid,fields,"bad start"unescaped' + BIG_DATA)
- end
-
- def test_field_size_limit_controls_lookahead
- assert_parse_errors_out( 'valid,fields,"' + BIG_DATA + '"',
- field_size_limit: 2048 )
- end
-
- private
-
- def assert_parse_errors_out(*args)
- assert_raise(CSV::MalformedCSVError) do
- Timeout.timeout(0.2) do
- CSV.parse(*args)
- fail("Parse didn't error out")
- end
- end
- end
-end
diff --git a/test/csv/test_csv_writing.rb b/test/csv/test_csv_writing.rb
deleted file mode 100755
index de82dae244..0000000000
--- a/test/csv/test_csv_writing.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-#!/usr/bin/env ruby -w
-# encoding: UTF-8
-# frozen_string_literal: false
-
-# tc_csv_writing.rb
-#
-# Created by James Edward Gray II on 2005-10-31.
-# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
-# under the terms of Ruby's license.
-
-require_relative "base"
-
-class TestCSV::Writing < TestCSV
- extend DifferentOFS
-
- def test_writing
- [ ["\t", ["\t"]],
- ["foo,\"\"\"\"\"\",baz", ["foo", "\"\"", "baz"]],
- ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]],
- ["\"\"\"\n\",\"\"\"\n\"", ["\"\n", "\"\n"]],
- ["foo,\"\r\n\",baz", ["foo", "\r\n", "baz"]],
- ["\"\"", [""]],
- ["foo,\"\"\"\",baz", ["foo", "\"", "baz"]],
- ["foo,\"\r.\n\",baz", ["foo", "\r.\n", "baz"]],
- ["foo,\"\r\",baz", ["foo", "\r", "baz"]],
- ["foo,\"\",baz", ["foo", "", "baz"]],
- ["\",\"", [","]],
- ["foo", ["foo"]],
- [",,", [nil, nil, nil]],
- [",", [nil, nil]],
- ["foo,\"\n\",baz", ["foo", "\n", "baz"]],
- ["foo,,baz", ["foo", nil, "baz"]],
- ["\"\"\"\r\",\"\"\"\r\"", ["\"\r", "\"\r"]],
- ["\",\",\",\"", [",", ","]],
- ["foo,bar,", ["foo", "bar", nil]],
- [",foo,bar", [nil, "foo", "bar"]],
- ["foo,bar", ["foo", "bar"]],
- [";", [";"]],
- ["\t,\t", ["\t", "\t"]],
- ["foo,\"\r\n\r\",baz", ["foo", "\r\n\r", "baz"]],
- ["foo,\"\r\n\n\",baz", ["foo", "\r\n\n", "baz"]],
- ["foo,\"foo,bar\",baz", ["foo", "foo,bar", "baz"]],
- [";,;", [";", ";"]],
- ["foo,\"\"\"\"\"\",baz", ["foo", "\"\"", "baz"]],
- ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]],
- ["foo,\"\r\n\",baz", ["foo", "\r\n", "baz"]],
- ["\"\"", [""]],
- ["foo,\"\"\"\",baz", ["foo", "\"", "baz"]],
- ["foo,\"\r.\n\",baz", ["foo", "\r.\n", "baz"]],
- ["foo,\"\r\",baz", ["foo", "\r", "baz"]],
- ["foo,\"\",baz", ["foo", "", "baz"]],
- ["foo", ["foo"]],
- [",,", [nil, nil, nil]],
- [",", [nil, nil]],
- ["foo,\"\n\",baz", ["foo", "\n", "baz"]],
- ["foo,,baz", ["foo", nil, "baz"]],
- ["foo,bar", ["foo", "bar"]],
- ["foo,\"\r\n\n\",baz", ["foo", "\r\n\n", "baz"]],
- ["foo,\"foo,bar\",baz", ["foo", "foo,bar", "baz"]],
- [%Q{a,b}, ["a", "b"]],
- [%Q{a,"""b"""}, ["a", "\"b\""]],
- [%Q{a,"""b"}, ["a", "\"b"]],
- [%Q{a,"b"""}, ["a", "b\""]],
- [%Q{a,"\nb"""}, ["a", "\nb\""]],
- [%Q{a,"""\nb"}, ["a", "\"\nb"]],
- [%Q{a,"""\nb\n"""}, ["a", "\"\nb\n\""]],
- [%Q{a,"""\nb\n""",}, ["a", "\"\nb\n\"", nil]],
- [%Q{a,,,}, ["a", nil, nil, nil]],
- [%Q{,}, [nil, nil]],
- [%Q{"",""}, ["", ""]],
- [%Q{""""}, ["\""]],
- [%Q{"""",""}, ["\"",""]],
- [%Q{,""}, [nil,""]],
- [%Q{,"\r"}, [nil,"\r"]],
- [%Q{"\r\n,"}, ["\r\n,"]],
- [%Q{"\r\n,",}, ["\r\n,", nil]] ].each do |test_case|
- assert_equal(test_case.first + $/, CSV.generate_line(test_case.last))
- end
- end
-
- def test_col_sep
- assert_equal( "a;b;;c\n", CSV.generate_line( ["a", "b", nil, "c"],
- col_sep: ";" ) )
- assert_equal( "a\tb\t\tc\n", CSV.generate_line( ["a", "b", nil, "c"],
- col_sep: "\t" ) )
- end
-
- def test_row_sep
- assert_equal( "a,b,,c\r\n", CSV.generate_line( ["a", "b", nil, "c"],
- row_sep: "\r\n" ) )
- end
-
- def test_force_quotes
- assert_equal( %Q{"1","b","","already ""quoted"""\n},
- CSV.generate_line( [1, "b", nil, %Q{already "quoted"}],
- force_quotes: true ) )
- end
-end
diff --git a/test/csv/test_data_converters.rb b/test/csv/test_data_converters.rb
index e274b2b10b..1620e077be 100755
--- a/test/csv/test_data_converters.rb
+++ b/test/csv/test_data_converters.rb
@@ -1,25 +1,13 @@
-#!/usr/bin/env ruby -w
-# encoding: UTF-8
+# -*- coding: utf-8 -*-
# frozen_string_literal: false
-# tc_data_converters.rb
-#
-# Created by James Edward Gray II on 2005-10-31.
-# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
-# under the terms of Ruby's license.
+require_relative "helper"
-require_relative "base"
-
-class TestCSV::DataConverters < TestCSV
+class TestCSVDataConverters < Test::Unit::TestCase
extend DifferentOFS
def setup
super
- @data = "Numbers,:integer,1,:float,3.015"
- @parser = CSV.new(@data)
-
- @custom = lambda { |field| field =~ /\A:(\S.*?)\s*\Z/ ? $1.to_sym : field }
-
@win_safe_time_str = Time.now.strftime("%a %b %d %H:%M:%S %Y")
end
@@ -67,198 +55,52 @@ class TestCSV::DataConverters < TestCSV
assert_instance_of(String, CSV::Converters[:date_time]["junk"])
end
- def test_convert_with_builtin_integer
- # setup parser...
- assert_respond_to(@parser, :convert)
- assert_nothing_raised(Exception) { @parser.convert(:integer) }
-
- # and use
- assert_equal(["Numbers", ":integer", 1, ":float", "3.015"], @parser.shift)
- end
-
- def test_convert_with_builtin_float
- # setup parser...
- assert_respond_to(@parser, :convert)
- assert_nothing_raised(Exception) { @parser.convert(:float) }
-
- # and use
- assert_equal(["Numbers", ":integer", 1.0, ":float", 3.015], @parser.shift)
- end
-
- def test_convert_order_float_integer
- # floats first, then integers...
- assert_nothing_raised(Exception) do
- @parser.convert(:float)
- @parser.convert(:integer)
- end
-
- # gets us nothing but floats
- assert_equal( [String, String, Float, String, Float],
- @parser.shift.map { |field| field.class } )
- end
-
- def test_convert_order_integer_float
- # integers have precendance...
- assert_nothing_raised(Exception) do
- @parser.convert(:integer)
- @parser.convert(:float)
- end
-
- # gives us proper number conversion
- assert_equal( [String, String, Integer, String, Float],
- @parser.shift.map { |field| field.class } )
- end
-
- def test_builtin_numeric_combo_converter
- # setup parser...
- assert_nothing_raised(Exception) { @parser.convert(:numeric) }
-
- # and use
- assert_equal( [String, String, Integer, String, Float],
- @parser.shift.map { |field| field.class } )
- end
-
- def test_builtin_all_nested_combo_converter
- # setup parser...
- @data << ",#{@win_safe_time_str}" # add a DateTime field
- @parser = CSV.new(@data) # reset parser
- assert_nothing_raised(Exception) { @parser.convert(:all) }
-
- # and use
- assert_equal( [String, String, Integer, String, Float, DateTime],
- @parser.shift.map { |field| field.class } )
+ def test_builtin_date_time_converter_iso8601_date
+ iso8601_string = "2018-01-14"
+ datetime = DateTime.new(2018, 1, 14)
+ assert_equal(datetime,
+ CSV::Converters[:date_time][iso8601_string])
end
- def test_convert_with_custom_code
- # define custom converter...
- assert_nothing_raised(Exception) do
- @parser.convert { |field| field =~ /\A:(\S.*?)\s*\Z/ ? $1.to_sym : field }
- end
-
- # and use
- assert_equal(["Numbers", :integer, "1", :float, "3.015"], @parser.shift)
+ def test_builtin_date_time_converter_iso8601_minute
+ iso8601_string = "2018-01-14T22:25"
+ datetime = DateTime.new(2018, 1, 14, 22, 25)
+ assert_equal(datetime,
+ CSV::Converters[:date_time][iso8601_string])
end
- def test_convert_with_custom_code_mix
- # mix built-in and custom...
- assert_nothing_raised(Exception) { @parser.convert(:numeric) }
- assert_nothing_raised(Exception) { @parser.convert(&@custom) }
-
- # and use
- assert_equal(["Numbers", :integer, 1, :float, 3.015], @parser.shift)
+ def test_builtin_date_time_converter_iso8601_second
+ iso8601_string = "2018-01-14T22:25:19"
+ datetime = DateTime.new(2018, 1, 14, 22, 25, 19)
+ assert_equal(datetime,
+ CSV::Converters[:date_time][iso8601_string])
end
- def test_convert_with_custom_code_using_field_info
- # define custom converter that uses field information...
- assert_nothing_raised(Exception) do
- @parser.convert do |field, info|
- assert_equal(1, info.line)
- info.index == 4 ? Float(field).floor : field
- end
- end
-
- # and use
- assert_equal(["Numbers", ":integer", "1", ":float", 3], @parser.shift)
+ def test_builtin_date_time_converter_iso8601_under_second
+ iso8601_string = "2018-01-14T22:25:19.1"
+ datetime = DateTime.new(2018, 1, 14, 22, 25, 19.1)
+ assert_equal(datetime,
+ CSV::Converters[:date_time][iso8601_string])
end
- def test_convert_with_custom_code_using_field_info_header
- @parser = CSV.new(@data, headers: %w{one two three four five})
-
- # define custom converter that uses field header information...
- assert_nothing_raised(Exception) do
- @parser.convert do |field, info|
- info.header == "three" ? Integer(field) * 100 : field
- end
- end
-
- # and use
- assert_equal( ["Numbers", ":integer", 100, ":float", "3.015"],
- @parser.shift.fields )
+ def test_builtin_date_time_converter_iso8601_under_second_offset
+ iso8601_string = "2018-01-14T22:25:19.1+09:00"
+ datetime = DateTime.new(2018, 1, 14, 22, 25, 19.1, "+9")
+ assert_equal(datetime,
+ CSV::Converters[:date_time][iso8601_string])
end
- def test_shortcut_interface
- assert_equal( ["Numbers", ":integer", 1, ":float", 3.015],
- CSV.parse_line(@data, converters: :numeric) )
-
- assert_equal( ["Numbers", ":integer", 1, ":float", 3.015],
- CSV.parse_line(@data, converters: [:integer, :float]) )
-
- assert_equal( ["Numbers", :integer, 1, :float, 3.015],
- CSV.parse_line(@data, converters: [:numeric, @custom]) )
+ def test_builtin_date_time_converter_iso8601_offset
+ iso8601_string = "2018-01-14T22:25:19+09:00"
+ datetime = DateTime.new(2018, 1, 14, 22, 25, 19, "+9")
+ assert_equal(datetime,
+ CSV::Converters[:date_time][iso8601_string])
end
- def test_unconverted_fields
- [ [ @data,
- ["Numbers", :integer, 1, :float, 3.015],
- %w{Numbers :integer 1 :float 3.015} ],
- ["\n", Array.new, Array.new] ].each do |test, fields, unconverted|
- row = nil
- assert_nothing_raised(Exception) do
- row = CSV.parse_line( test,
- converters: [:numeric, @custom],
- unconverted_fields: true )
- end
- assert_not_nil(row)
- assert_equal(fields, row)
- assert_respond_to(row, :unconverted_fields)
- assert_equal(unconverted, row.unconverted_fields)
- end
-
- data = <<-END_CSV.gsub(/^\s+/, "")
- first,second,third
- 1,2,3
- END_CSV
- row = nil
- assert_nothing_raised(Exception) do
- row = CSV.parse_line( data,
- converters: :numeric,
- unconverted_fields: true,
- headers: :first_row )
- end
- assert_not_nil(row)
- assert_equal([["first", 1], ["second", 2], ["third", 3]], row.to_a)
- assert_respond_to(row, :unconverted_fields)
- assert_equal(%w{1 2 3}, row.unconverted_fields)
-
- assert_nothing_raised(Exception) do
- row = CSV.parse_line( data,
- converters: :numeric,
- unconverted_fields: true,
- headers: :first_row,
- return_headers: true )
- end
- assert_not_nil(row)
- assert_equal( [%w{first first}, %w{second second}, %w{third third}],
- row.to_a )
- assert_respond_to(row, :unconverted_fields)
- assert_equal(%w{first second third}, row.unconverted_fields)
-
- assert_nothing_raised(Exception) do
- row = CSV.parse_line( data,
- converters: :numeric,
- unconverted_fields: true,
- headers: :first_row,
- return_headers: true,
- header_converters: :symbol )
- end
- assert_not_nil(row)
- assert_equal( [[:first, "first"], [:second, "second"], [:third, "third"]],
- row.to_a )
- assert_respond_to(row, :unconverted_fields)
- assert_equal(%w{first second third}, row.unconverted_fields)
-
- assert_nothing_raised(Exception) do
- row = CSV.parse_line( data,
- converters: :numeric,
- unconverted_fields: true,
- headers: %w{my new headers},
- return_headers: true,
- header_converters: :symbol )
- end
- assert_not_nil(row)
- assert_equal( [[:my, "my"], [:new, "new"], [:headers, "headers"]],
- row.to_a )
- assert_respond_to(row, :unconverted_fields)
- assert_equal(Array.new, row.unconverted_fields)
+ def test_builtin_date_time_converter_iso8601_utc
+ iso8601_string = "2018-01-14T22:25:19Z"
+ datetime = DateTime.new(2018, 1, 14, 22, 25, 19)
+ assert_equal(datetime,
+ CSV::Converters[:date_time][iso8601_string])
end
end
diff --git a/test/csv/test_encodings.rb b/test/csv/test_encodings.rb
index 7460a3ff34..64ea36a9a4 100755
--- a/test/csv/test_encodings.rb
+++ b/test/csv/test_encodings.rb
@@ -1,16 +1,9 @@
-#!/usr/bin/env ruby -w
-# encoding: UTF-8
+# -*- coding: utf-8 -*-
# frozen_string_literal: false
-# tc_encodings.rb
-#
-# Created by James Edward Gray II on 2008-09-13.
-# Copyright 2008 James Edward Gray II. You can redistribute or modify this code
-# under the terms of Ruby's license.
+require_relative "helper"
-require_relative "base"
-
-class TestCSV::Encodings < TestCSV
+class TestCSVEncodings < Test::Unit::TestCase
extend DifferentOFS
def setup
@@ -256,14 +249,31 @@ class TestCSV::Encodings < TestCSV
assert_equal(["foo,\u3042\n".encode(Encoding::Windows_31J), Encoding::Windows_31J], [s, s.encoding], bug9766)
end
+ def test_row_separator_detection_with_invalid_encoding
+ csv = CSV.new("invalid,\xF8\r\nvalid,x\r\n".force_encoding("UTF-8"),
+ encoding: "UTF-8")
+ assert_equal("\r\n", csv.row_sep)
+ end
+
+ def test_invalid_encoding_row_error
+ csv = CSV.new("valid,x\rinvalid,\xF8\r".force_encoding("UTF-8"),
+ encoding: "UTF-8", row_sep: "\r")
+ error = assert_raise(CSV::MalformedCSVError) do
+ csv.shift
+ csv.shift
+ end
+ assert_equal("Invalid byte sequence in UTF-8 in line 2.",
+ error.message)
+ end
+
private
def assert_parses(fields, encoding, options = { })
encoding = Encoding.find(encoding) unless encoding.is_a? Encoding
orig_fields = fields
- fields = encode_ary(fields, encoding)
+ fields = encode_ary(fields, encoding)
data = ary_to_data(fields, options)
- parsed = CSV.parse(data, options)
+ parsed = CSV.parse(data, options)
assert_equal(fields, parsed)
parsed.flatten.each_with_index do |field, i|
assert_equal(encoding, field.encoding, "Field[#{i + 1}] was transcoded.")
diff --git a/test/csv/test_features.rb b/test/csv/test_features.rb
index a558875522..306b880f6f 100755
--- a/test/csv/test_features.rb
+++ b/test/csv/test_features.rb
@@ -1,22 +1,15 @@
-#!/usr/bin/env ruby -w
-# encoding: UTF-8
+# -*- coding: utf-8 -*-
# frozen_string_literal: false
-# tc_features.rb
-#
-# Created by James Edward Gray II on 2005-10-31.
-# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
-# under the terms of Ruby's license.
-
begin
require "zlib"
rescue LoadError
end
-require_relative "base"
+require_relative "helper"
require "tempfile"
-class TestCSV::Features < TestCSV
+class TestCSVFeatures < Test::Unit::TestCase
extend DifferentOFS
TEST_CASES = [ [%Q{a,b}, ["a", "b"]],
@@ -39,12 +32,12 @@ class TestCSV::Features < TestCSV
def setup
super
- @sample_data = <<-END_DATA.gsub(/^ +/, "")
- line,1,abc
- line,2,"def\nghi"
+ @sample_data = <<-CSV
+line,1,abc
+line,2,"def\nghi"
- line,4,jkl
- END_DATA
+line,4,jkl
+ CSV
@csv = CSV.new(@sample_data)
end
@@ -60,26 +53,37 @@ class TestCSV::Features < TestCSV
end
def test_row_sep
- assert_raise(CSV::MalformedCSVError) do
- CSV.parse_line("1,2,3\n,4,5\r\n", row_sep: "\r\n")
+ error = assert_raise(CSV::MalformedCSVError) do
+ CSV.parse_line("1,2,3\n,4,5\r\n", row_sep: "\r\n")
end
+ assert_equal("Unquoted fields do not allow new line <\"\\n\"> in line 1.",
+ error.message)
assert_equal( ["1", "2", "3\n", "4", "5"],
CSV.parse_line(%Q{1,2,"3\n",4,5\r\n}, row_sep: "\r\n"))
end
def test_quote_char
TEST_CASES.each do |test_case|
- assert_equal( test_case.last.map { |t| t.tr('"', "'") unless t.nil? },
- CSV.parse_line( test_case.first.tr('"', "'"),
- quote_char: "'" ) )
+ assert_equal(test_case.last.map {|t| t.tr('"', "'") unless t.nil?},
+ CSV.parse_line(test_case.first.tr('"', "'"),
+ quote_char: "'" ))
end
end
- def test_bug_8405
+ def test_quote_char_special_regexp_char
TEST_CASES.each do |test_case|
- assert_equal( test_case.last.map { |t| t.tr('"', "|") unless t.nil? },
- CSV.parse_line( test_case.first.tr('"', "|"),
- quote_char: "|" ) )
+ assert_equal(test_case.last.map {|t| t.tr('"', "|") unless t.nil?},
+ CSV.parse_line(test_case.first.tr('"', "|"),
+ quote_char: "|"))
+ end
+ end
+
+ def test_quote_char_special_regexp_char_liberal_parsing
+ TEST_CASES.each do |test_case|
+ assert_equal(test_case.last.map {|t| t.tr('"', "|") unless t.nil?},
+ CSV.parse_line(test_case.first.tr('"', "|"),
+ quote_char: "|",
+ liberal_parsing: true))
end
end
@@ -104,6 +108,20 @@ class TestCSV::Features < TestCSV
assert_equal($/, CSV.new(STDERR).row_sep)
end
+ def test_line
+ lines = [
+ %Q(abc,def\n),
+ %Q(abc,"d\nef"\n),
+ %Q(abc,"d\r\nef"\n),
+ %Q(abc,"d\ref")
+ ]
+ csv = CSV.new(lines.join(''))
+ lines.each do |line|
+ csv.shift
+ assert_equal(line, csv.line)
+ end
+ end
+
def test_lineno
assert_equal(5, @sample_data.lines.to_a.size)
@@ -124,9 +142,12 @@ class TestCSV::Features < TestCSV
end
def test_unknown_options
- assert_raise_with_message(ArgumentError, /unknown/) {
+ assert_raise_with_message(ArgumentError, /unknown keyword/) {
CSV.new(@sample_data, unknown: :error)
}
+ assert_raise_with_message(ArgumentError, /unknown keyword/) {
+ CSV.new(@sample_data, universal_newline: true)
+ }
end
def test_skip_blanks
@@ -142,29 +163,6 @@ class TestCSV::Features < TestCSV
assert_equal(3, count)
end
- def test_liberal_parsing
- input = '"Johnson, Dwayne",Dwayne "The Rock" Johnson'
- assert_raise(CSV::MalformedCSVError) do
- CSV.parse_line(input)
- end
- assert_equal(["Johnson, Dwayne", 'Dwayne "The Rock" Johnson'],
- CSV.parse_line(input, liberal_parsing: true))
-
- input = '"quoted" field'
- assert_raise(CSV::MalformedCSVError) do
- CSV.parse_line(input)
- end
- assert_equal(['"quoted" field'],
- CSV.parse_line(input, liberal_parsing: true))
-
- assert_raise(CSV::MalformedCSVError) do
- CSV.parse_line('is,this "three," or four,fields', liberal_parsing: true)
- end
-
- assert_equal(["is", 'this "three', ' or four"', "fields"],
- CSV.parse_line('is,this "three, or four",fields', liberal_parsing: true))
- end
-
def test_csv_behavior_readers
%w[ unconverted_fields return_headers write_headers
skip_blanks force_quotes ].each do |behavior|
@@ -210,29 +208,19 @@ class TestCSV::Features < TestCSV
end
# reported by Kev Jackson
- def test_failing_to_escape_col_sep_bug_fix
+ def test_failing_to_escape_col_sep
assert_nothing_raised(Exception) { CSV.new(String.new, col_sep: "|") }
end
# reported by Chris Roos
- def test_failing_to_reset_headers_in_rewind_bug_fix
+ def test_failing_to_reset_headers_in_rewind
csv = CSV.new("forename,surname", headers: true, return_headers: true)
csv.each {|row| assert_predicate row, :header_row?}
csv.rewind
csv.each {|row| assert_predicate row, :header_row?}
end
- # reported by Dave Burt
- def test_leading_empty_fields_with_multibyte_col_sep_bug_fix
- data = <<-END_DATA.gsub(/^\s+/, "")
- <=><=>A<=>B<=>C
- 1<=>2<=>3
- END_DATA
- parsed = CSV.parse(data, col_sep: "<=>")
- assert_equal([[nil, nil, "A", "B", "C"], ["1", "2", "3"]], parsed)
- end
-
- def test_gzip_reader_bug_fix
+ def test_gzip_reader
zipped = nil
assert_nothing_raised(NoMethodError) do
zipped = CSV.new(
@@ -246,7 +234,7 @@ class TestCSV::Features < TestCSV
zipped.close
end if defined?(Zlib::GzipReader)
- def test_gzip_writer_bug_fix
+ def test_gzip_writer
Tempfile.create(%w"temp .gz") {|tempfile|
tempfile.close
file = tempfile.path
@@ -287,65 +275,71 @@ class TestCSV::Features < TestCSV
end
def test_inspect_shows_headers_when_available
- CSV.new("one,two,three\n1,2,3\n", headers: true) do |csv|
- assert_include(csv.inspect, "headers:true", "Header hint not shown.")
- csv.shift # load headers
- assert_match(/headers:\[[^\]]+\]/, csv.inspect)
- end
+ csv = CSV.new("one,two,three\n1,2,3\n", headers: true)
+ assert_include(csv.inspect, "headers:true", "Header hint not shown.")
+ csv.shift # load headers
+ assert_match(/headers:\[[^\]]+\]/, csv.inspect)
end
def test_inspect_encoding_is_ascii_compatible
- CSV.new("one,two,three\n1,2,3\n".encode("UTF-16BE")) do |csv|
- assert_send([Encoding, :compatible?,
- Encoding.find("US-ASCII"), csv.inspect.encoding],
- "inspect() was not ASCII compatible.")
- end
+ csv = CSV.new("one,two,three\n1,2,3\n".encode("UTF-16BE"))
+ assert_send([Encoding, :compatible?,
+ Encoding.find("US-ASCII"), csv.inspect.encoding],
+ "inspect() was not ASCII compatible.")
end
def test_version
assert_not_nil(CSV::VERSION)
assert_instance_of(String, CSV::VERSION)
assert_predicate(CSV::VERSION, :frozen?)
- assert_match(/\A\d\.\d\.\d\Z/, CSV::VERSION)
+ assert_match(/\A\d\.\d\.\d\z/, CSV::VERSION)
end
- def test_accepts_comment_skip_lines_option
- assert_nothing_raised(ArgumentError) do
- CSV.new(@sample_data, :skip_lines => /\A\s*#/)
- end
+ def test_table_nil_equality
+ assert_nothing_raised(NoMethodError) { CSV.parse("test", headers: true) == nil }
end
- def test_accepts_comment_defaults_to_nil
- c = CSV.new(@sample_data)
- assert_nil(c.skip_lines)
+ # non-seekable input stream for testing https://github.com/ruby/csv/issues/44
+ class DummyIO
+ extend Forwardable
+ def_delegators :@io, :gets, :read, :pos, :eof? # no seek or rewind!
+ def initialize(data)
+ @io = StringIO.new(data)
+ end
end
- class RegexStub
+ def test_line_separator_autodetection_for_non_seekable_input_lf
+ c = CSV.new(DummyIO.new("one,two,three\nfoo,bar,baz\n"))
+ assert_equal [["one", "two", "three"], ["foo", "bar", "baz"]], c.each.to_a
end
- def test_requires_skip_lines_to_call_match
- regex_stub = RegexStub.new
- assert_raise_with_message(ArgumentError, /skip_lines/) do
- CSV.new(@sample_data, :skip_lines => regex_stub)
- end
+ def test_line_separator_autodetection_for_non_seekable_input_cr
+ c = CSV.new(DummyIO.new("one,two,three\rfoo,bar,baz\r"))
+ assert_equal [["one", "two", "three"], ["foo", "bar", "baz"]], c.each.to_a
end
- def test_comment_rows_are_ignored
- sample_data = "line,1,a\n#not,a,line\nline,2,b\n #also,no,line"
- c = CSV.new sample_data, :skip_lines => /\A\s*#/
- assert_equal [["line", "1", "a"], ["line", "2", "b"]], c.each.to_a
+ def test_line_separator_autodetection_for_non_seekable_input_cr_lf
+ c = CSV.new(DummyIO.new("one,two,three\r\nfoo,bar,baz\r\n"))
+ assert_equal [["one", "two", "three"], ["foo", "bar", "baz"]], c.each.to_a
end
- def test_quoted_skip_line_markers_are_ignored
- sample_data = "line,1,a\n\"#not\",a,line\nline,2,b"
- c = CSV.new sample_data, :skip_lines => /\A\s*#/
- assert_equal [["line", "1", "a"], ["#not", "a", "line"], ["line", "2", "b"]], c.each.to_a
+ def test_line_separator_autodetection_for_non_seekable_input_1024_over_lf
+ table = (1..10).map { |row| (1..200).map { |col| "row#{row}col#{col}" }.to_a }.to_a
+ input = table.map { |line| line.join(",") }.join("\n")
+ c = CSV.new(DummyIO.new(input))
+ assert_equal table, c.each.to_a
end
- def test_string_works_like_a_regexp
- sample_data = "line,1,a\n#(not,a,line\nline,2,b\n also,#no,line"
- c = CSV.new sample_data, :skip_lines => "#"
- assert_equal [["line", "1", "a"], ["line", "2", "b"]], c.each.to_a
+ def test_line_separator_autodetection_for_non_seekable_input_1024_over_cr_lf
+ table = (1..10).map { |row| (1..200).map { |col| "row#{row}col#{col}" }.to_a }.to_a
+ input = table.map { |line| line.join(",") }.join("\r\n")
+ c = CSV.new(DummyIO.new(input))
+ assert_equal table, c.each.to_a
end
+ def test_line_separator_autodetection_for_non_seekable_input_many_cr_only
+ # input with lots of CRs (to make sure no bytes are lost due to look-ahead)
+ c = CSV.new(DummyIO.new("foo\r" + "\r" * 9999 + "bar\r"))
+ assert_equal [["foo"]] + [[]] * 9999 + [["bar"]], c.each.to_a
+ end
end
diff --git a/test/csv/test_headers.rb b/test/csv/test_headers.rb
deleted file mode 100755
index d8a1c15836..0000000000
--- a/test/csv/test_headers.rb
+++ /dev/null
@@ -1,298 +0,0 @@
-#!/usr/bin/env ruby -w
-# encoding: UTF-8
-# frozen_string_literal: false
-
-# tc_headers.rb
-#
-# Created by James Edward Gray II on 2005-10-31.
-# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
-# under the terms of Ruby's license.
-
-require_relative "base"
-
-class TestCSV::Headers < TestCSV
- extend DifferentOFS
-
- def setup
- super
- @data = <<-END_CSV.gsub(/^\s+/, "")
- first,second,third
- A,B,C
- 1,2,3
- END_CSV
- end
-
- def test_first_row
- [:first_row, true].each do |setting| # two names for the same setting
- # activate headers
- csv = nil
- assert_nothing_raised(Exception) do
- csv = CSV.parse(@data, headers: setting)
- end
-
- # first data row - skipping headers
- row = csv[0]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{first A}, %w{second B}, %w{third C}], row.to_a)
-
- # second data row
- row = csv[1]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{first 1}, %w{second 2}, %w{third 3}], row.to_a)
-
- # empty
- assert_nil(csv[2])
- end
- end
-
- def test_array_of_headers
- # activate headers
- csv = nil
- assert_nothing_raised(Exception) do
- csv = CSV.parse(@data, headers: [:my, :new, :headers])
- end
-
- # first data row - skipping headers
- row = csv[0]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal( [[:my, "first"], [:new, "second"], [:headers, "third"]],
- row.to_a )
-
- # second data row
- row = csv[1]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([[:my, "A"], [:new, "B"], [:headers, "C"]], row.to_a)
-
- # third data row
- row = csv[2]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([[:my, "1"], [:new, "2"], [:headers, "3"]], row.to_a)
-
- # empty
- assert_nil(csv[3])
-
- # with return and convert
- assert_nothing_raised(Exception) do
- csv = CSV.parse( @data, headers: [:my, :new, :headers],
- return_headers: true,
- header_converters: lambda { |h| h.to_s } )
- end
- row = csv[0]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([["my", :my], ["new", :new], ["headers", :headers]], row.to_a)
- assert_predicate(row, :header_row?)
- assert_not_predicate(row, :field_row?)
- end
-
- def test_csv_header_string
- # activate headers
- csv = nil
- assert_nothing_raised(Exception) do
- csv = CSV.parse(@data, headers: "my,new,headers")
- end
-
- # first data row - skipping headers
- row = csv[0]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{my first}, %w{new second}, %w{headers third}], row.to_a)
-
- # second data row
- row = csv[1]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{my A}, %w{new B}, %w{headers C}], row.to_a)
-
- # third data row
- row = csv[2]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{my 1}, %w{new 2}, %w{headers 3}], row.to_a)
-
- # empty
- assert_nil(csv[3])
-
- # with return and convert
- assert_nothing_raised(Exception) do
- csv = CSV.parse( @data, headers: "my,new,headers",
- return_headers: true,
- header_converters: :symbol )
- end
- row = csv[0]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([[:my, "my"], [:new, "new"], [:headers, "headers"]], row.to_a)
- assert_predicate(row, :header_row?)
- assert_not_predicate(row, :field_row?)
- end
-
- def test_csv_header_string_inherits_separators
- # parse with custom col_sep
- csv = nil
- assert_nothing_raised(Exception) do
- csv = CSV.parse( @data.tr(",", "|"), col_sep: "|",
- headers: "my|new|headers" )
- end
-
- # verify headers were recognized
- row = csv[0]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{my first}, %w{new second}, %w{headers third}], row.to_a)
- end
-
- def test_return_headers
- # activate headers and request they are returned
- csv = nil
- assert_nothing_raised(Exception) do
- csv = CSV.parse(@data, headers: true, return_headers: true)
- end
-
- # header row
- row = csv[0]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal( [%w{first first}, %w{second second}, %w{third third}],
- row.to_a )
- assert_predicate(row, :header_row?)
- assert_not_predicate(row, :field_row?)
-
- # first data row - skipping headers
- row = csv[1]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{first A}, %w{second B}, %w{third C}], row.to_a)
- assert_not_predicate(row, :header_row?)
- assert_predicate(row, :field_row?)
-
- # second data row
- row = csv[2]
- assert_not_nil(row)
- assert_instance_of(CSV::Row, row)
- assert_equal([%w{first 1}, %w{second 2}, %w{third 3}], row.to_a)
- assert_not_predicate(row, :header_row?)
- assert_predicate(row, :field_row?)
-
- # empty
- assert_nil(csv[3])
- end
-
- def test_converters
- # create test data where headers and fields look alike
- data = <<-END_MATCHING_CSV.gsub(/^\s+/, "")
- 1,2,3
- 1,2,3
- END_MATCHING_CSV
-
- # normal converters do not affect headers
- csv = CSV.parse( data, headers: true,
- return_headers: true,
- converters: :numeric )
- assert_equal([%w{1 1}, %w{2 2}, %w{3 3}], csv[0].to_a)
- assert_equal([["1", 1], ["2", 2], ["3", 3]], csv[1].to_a)
- assert_nil(csv[2])
-
- # header converters do affect headers (only)
- assert_nothing_raised(Exception) do
- csv = CSV.parse( data, headers: true,
- return_headers: true,
- converters: :numeric,
- header_converters: :symbol )
- end
- assert_equal([[:"1", "1"], [:"2", "2"], [:"3", "3"]], csv[0].to_a)
- assert_equal([[:"1", 1], [:"2", 2], [:"3", 3]], csv[1].to_a)
- assert_nil(csv[2])
- end
-
- def test_builtin_downcase_converter
- csv = CSV.parse( "One,TWO Three", headers: true,
- return_headers: true,
- header_converters: :downcase )
- assert_equal(%w{one two\ three}, csv.headers)
- end
-
- def test_builtin_symbol_converter
- # Note that the trailing space is intentional
- csv = CSV.parse( "One,TWO Three ", headers: true,
- return_headers: true,
- header_converters: :symbol )
- assert_equal([:one, :two_three], csv.headers)
- end
-
- def test_builtin_converters_with_blank_header
- csv = CSV.parse( "one,,three", headers: true,
- return_headers: true,
- header_converters: [:downcase, :symbol] )
- assert_equal([:one, nil, :three], csv.headers)
- end
-
- def test_custom_converter
- converter = lambda { |header| header.tr(" ", "_") }
- csv = CSV.parse( "One,TWO Three",
- headers: true,
- return_headers: true,
- header_converters: converter )
- assert_equal(%w{One TWO_Three}, csv.headers)
- end
-
- def test_table_support
- csv = nil
- assert_nothing_raised(Exception) do
- csv = CSV.parse(@data, headers: true)
- end
-
- assert_instance_of(CSV::Table, csv)
- end
-
- def test_skip_blanks
- @data = <<-END_CSV.gsub(/^ +/, "")
-
-
- A,B,C
-
- 1,2,3
-
-
-
- END_CSV
-
- expected = [%w[1 2 3]]
- CSV.parse(@data, headers: true, skip_blanks: true) do |row|
- assert_equal(expected.shift, row.fields)
- end
-
- expected = [%w[A B C], %w[1 2 3]]
- CSV.parse( @data,
- headers: true,
- return_headers: true,
- skip_blanks: true ) do |row|
- assert_equal(expected.shift, row.fields)
- end
- end
-
- def test_headers_reader
- # no headers
- assert_nil(CSV.new(@data).headers)
-
- # headers
- csv = CSV.new(@data, headers: true)
- assert_equal(true, csv.headers) # before headers are read
- csv.shift # set headers
- assert_equal(%w[first second third], csv.headers) # after headers are read
- end
-
- def test_blank_row_bug_fix
- @data += "\n#{@data}" # add a blank row
-
- # ensure that everything returned is a Row object
- CSV.parse(@data, headers: true) do |row|
- assert_instance_of(CSV::Row, row)
- end
- end
-end
diff --git a/test/csv/test_interface.rb b/test/csv/test_interface.rb
deleted file mode 100755
index a12545c8b0..0000000000
--- a/test/csv/test_interface.rb
+++ /dev/null
@@ -1,369 +0,0 @@
-#!/usr/bin/env ruby -w
-# encoding: UTF-8
-# frozen_string_literal: false
-
-# tc_interface.rb
-#
-# Created by James Edward Gray II on 2005-10-31.
-# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
-# under the terms of Ruby's license.
-
-require_relative "base"
-require "tempfile"
-
-class TestCSV::Interface < TestCSV
- extend DifferentOFS
-
- def setup
- super
- @tempfile = Tempfile.new(%w"temp .csv")
- @tempfile.close
- @path = @tempfile.path
-
- File.open(@path, "wb") do |file|
- file << "1\t2\t3\r\n"
- file << "4\t5\r\n"
- end
-
- @expected = [%w{1 2 3}, %w{4 5}]
- end
-
- def teardown
- @tempfile.close(true)
- super
- end
-
- ### Test Read Interface ###
-
- def test_foreach
- CSV.foreach(@path, col_sep: "\t", row_sep: "\r\n") do |row|
- assert_equal(@expected.shift, row)
- end
- end
-
- def test_foreach_enum
- CSV.foreach(@path, col_sep: "\t", row_sep: "\r\n").zip(@expected) do |row, exp|
- assert_equal(exp, row)
- end
- end
-
- def test_open_and_close
- csv = CSV.open(@path, "r+", col_sep: "\t", row_sep: "\r\n")
- assert_not_nil(csv)
- assert_instance_of(CSV, csv)
- assert_not_predicate(csv, :closed?)
- csv.close
- assert_predicate(csv, :closed?)
-
- ret = CSV.open(@path) do |new_csv|
- csv = new_csv
- assert_instance_of(CSV, new_csv)
- "Return value."
- end
- assert_predicate(csv, :closed?)
- assert_equal("Return value.", ret)
- end
-
- def test_parse
- data = File.binread(@path)
- assert_equal( @expected,
- CSV.parse(data, col_sep: "\t", row_sep: "\r\n") )
-
- CSV.parse(data, col_sep: "\t", row_sep: "\r\n") do |row|
- assert_equal(@expected.shift, row)
- end
- end
-
- def test_parse_line
- row = CSV.parse_line("1;2;3", col_sep: ";")
- assert_not_nil(row)
- assert_instance_of(Array, row)
- assert_equal(%w{1 2 3}, row)
-
- # shortcut interface
- row = "1;2;3".parse_csv(col_sep: ";")
- assert_not_nil(row)
- assert_instance_of(Array, row)
- assert_equal(%w{1 2 3}, row)
- end
-
- def test_parse_line_with_empty_lines
- assert_equal(nil, CSV.parse_line("")) # to signal eof
- assert_equal(Array.new, CSV.parse_line("\n1,2,3"))
- end
-
- def test_read_and_readlines
- assert_equal( @expected,
- CSV.read(@path, col_sep: "\t", row_sep: "\r\n") )
- assert_equal( @expected,
- CSV.readlines(@path, col_sep: "\t", row_sep: "\r\n") )
-
-
- data = CSV.open(@path, col_sep: "\t", row_sep: "\r\n") do |csv|
- csv.read
- end
- assert_equal(@expected, data)
- data = CSV.open(@path, col_sep: "\t", row_sep: "\r\n") do |csv|
- csv.readlines
- end
- assert_equal(@expected, data)
- end
-
- def test_table
- table = CSV.table(@path, col_sep: "\t", row_sep: "\r\n")
- assert_instance_of(CSV::Table, table)
- assert_equal([[:"1", :"2", :"3"], [4, 5, nil]], table.to_a)
- end
-
- def test_shift # aliased as gets() and readline()
- CSV.open(@path, "rb+", col_sep: "\t", row_sep: "\r\n") do |csv|
- assert_equal(@expected.shift, csv.shift)
- assert_equal(@expected.shift, csv.shift)
- assert_equal(nil, csv.shift)
- end
- end
-
- def test_enumerators_are_supported
- CSV.open(@path, col_sep: "\t", row_sep: "\r\n") do |csv|
- enum = csv.each
- assert_instance_of(Enumerator, enum)
- assert_equal(@expected.shift, enum.next)
- end
- end
-
- def test_nil_is_not_acceptable
- assert_raise_with_message ArgumentError, "Cannot parse nil as CSV" do
- CSV.new(nil)
- end
- end
-
- ### Test Write Interface ###
-
- def test_generate
- str = CSV.generate do |csv| # default empty String
- assert_instance_of(CSV, csv)
- assert_equal(csv, csv << [1, 2, 3])
- assert_equal(csv, csv << [4, nil, 5])
- end
- assert_not_nil(str)
- assert_instance_of(String, str)
- assert_equal("1,2,3\n4,,5\n", str)
-
- CSV.generate(str) do |csv| # appending to a String
- assert_equal(csv, csv << ["last", %Q{"row"}])
- end
- assert_equal(%Q{1,2,3\n4,,5\nlast,"""row"""\n}, str)
- end
-
- def test_generate_line
- line = CSV.generate_line(%w{1 2 3}, col_sep: ";")
- assert_not_nil(line)
- assert_instance_of(String, line)
- assert_equal("1;2;3\n", line)
-
- # shortcut interface
- line = %w{1 2 3}.to_csv(col_sep: ";")
- assert_not_nil(line)
- assert_instance_of(String, line)
- assert_equal("1;2;3\n", line)
- end
-
- def test_write_header_detection
- File.unlink(@path)
-
- headers = %w{a b c}
- CSV.open(@path, "w", headers: true) do |csv|
- csv << headers
- csv << %w{1 2 3}
- assert_equal(headers, csv.instance_variable_get(:@headers))
- end
- end
-
- def test_write_lineno
- File.unlink(@path)
-
- CSV.open(@path, "w") do |csv|
- lines = 20
- lines.times { csv << %w{a b c} }
- assert_equal(lines, csv.lineno)
- end
- end
-
- def test_write_hash
- File.unlink(@path)
-
- lines = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}]
- CSV.open( @path, "wb", headers: true,
- header_converters: :symbol ) do |csv|
- csv << lines.first.keys
- lines.each { |line| csv << line }
- end
- CSV.open( @path, "rb", headers: true,
- converters: :all,
- header_converters: :symbol ) do |csv|
- csv.each { |line| assert_equal(lines.shift, line.to_hash) }
- end
- end
-
- def test_write_hash_with_string_keys
- File.unlink(@path)
-
- lines = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}]
- CSV.open( @path, "wb", headers: true ) do |csv|
- csv << lines.first.keys
- lines.each { |line| csv << line }
- end
- CSV.open( @path, "rb", headers: true ) do |csv|
- csv.each do |line|
- csv.headers.each_with_index do |header, h|
- keys = line.to_hash.keys
- assert_instance_of(String, keys[h])
- assert_same(header, keys[h])
- end
- end
- end
- end
-
- def test_write_hash_with_headers_array
- File.unlink(@path)
-
- lines = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}]
- CSV.open(@path, "wb", headers: [:b, :a, :c]) do |csv|
- lines.each { |line| csv << line }
- end
-
- # test writing fields in the correct order
- File.open(@path, "rb") do |f|
- assert_equal("2,1,3", f.gets.strip)
- assert_equal("5,4,6", f.gets.strip)
- end
-
- # test reading CSV with headers
- CSV.open( @path, "rb", headers: [:b, :a, :c],
- converters: :all ) do |csv|
- csv.each { |line| assert_equal(lines.shift, line.to_hash) }
- end
- end
-
- def test_write_hash_with_headers_string
- File.unlink(@path)
-
- lines = [{"a" => 1, "b" => 2, "c" => 3}, {"a" => 4, "b" => 5, "c" => 6}]
- CSV.open(@path, "wb", headers: "b|a|c", col_sep: "|") do |csv|
- lines.each { |line| csv << line }
- end
-
- # test writing fields in the correct order
- File.open(@path, "rb") do |f|
- assert_equal("2|1|3", f.gets.strip)
- assert_equal("5|4|6", f.gets.strip)
- end
-
- # test reading CSV with headers
- CSV.open( @path, "rb", headers: "b|a|c",
- col_sep: "|",
- converters: :all ) do |csv|
- csv.each { |line| assert_equal(lines.shift, line.to_hash) }
- end
- end
-
- def test_write_headers
- File.unlink(@path)
-
- lines = [{"a" => 1, "b" => 2, "c" => 3}, {"a" => 4, "b" => 5, "c" => 6}]
- CSV.open( @path, "wb", headers: "b|a|c",
- write_headers: true,
- col_sep: "|" ) do |csv|
- lines.each { |line| csv << line }
- end
-
- # test writing fields in the correct order
- File.open(@path, "rb") do |f|
- assert_equal("b|a|c", f.gets.strip)
- assert_equal("2|1|3", f.gets.strip)
- assert_equal("5|4|6", f.gets.strip)
- end
-
- # test reading CSV with headers
- CSV.open( @path, "rb", headers: true,
- col_sep: "|",
- converters: :all ) do |csv|
- csv.each { |line| assert_equal(lines.shift, line.to_hash) }
- end
- end
-
- def test_append # aliased add_row() and puts()
- File.unlink(@path)
-
- CSV.open(@path, "wb", col_sep: "\t", row_sep: "\r\n") do |csv|
- @expected.each { |row| csv << row }
- end
-
- test_shift
-
- # same thing using CSV::Row objects
- File.unlink(@path)
-
- CSV.open(@path, "wb", col_sep: "\t", row_sep: "\r\n") do |csv|
- @expected.each { |row| csv << CSV::Row.new(Array.new, row) }
- end
-
- test_shift
- end
-
- ### Test Read and Write Interface ###
-
- def test_filter
- assert_respond_to(CSV, :filter)
-
- expected = [[1, 2, 3], [4, 5]]
- CSV.filter( "1;2;3\n4;5\n", (result = String.new),
- in_col_sep: ";", out_col_sep: ",",
- converters: :all ) do |row|
- assert_equal(row, expected.shift)
- row.map! { |n| n * 2 }
- row << "Added\r"
- end
- assert_equal("2,4,6,\"Added\r\"\n8,10,\"Added\r\"\n", result)
- end
-
- def test_instance
- csv = String.new
-
- first = nil
- assert_nothing_raised(Exception) do
- first = CSV.instance(csv, col_sep: ";")
- first << %w{a b c}
- end
-
- assert_equal("a;b;c\n", csv)
-
- second = nil
- assert_nothing_raised(Exception) do
- second = CSV.instance(csv, col_sep: ";")
- second << [1, 2, 3]
- end
-
- assert_equal(first.object_id, second.object_id)
- assert_equal("a;b;c\n1;2;3\n", csv)
-
- # shortcuts
- assert_equal(STDOUT, CSV.instance.instance_eval { @io })
- assert_equal(STDOUT, CSV { |new_csv| new_csv.instance_eval { @io } })
- end
-
- def test_options_are_not_modified
- opt = {}.freeze
- assert_nothing_raised { CSV.foreach(@path, opt) }
- assert_nothing_raised { CSV.open(@path, opt){} }
- assert_nothing_raised { CSV.parse("", opt) }
- assert_nothing_raised { CSV.parse_line("", opt) }
- assert_nothing_raised { CSV.read(@path, opt) }
- assert_nothing_raised { CSV.readlines(@path, opt) }
- assert_nothing_raised { CSV.table(@path, opt) }
- assert_nothing_raised { CSV.generate(opt){} }
- assert_nothing_raised { CSV.generate_line([], opt) }
- assert_nothing_raised { CSV.filter("", "", opt){} }
- assert_nothing_raised { CSV.instance("", opt) }
- end
-end
diff --git a/test/csv/test_row.rb b/test/csv/test_row.rb
index 1cb851b027..f709dd3f13 100755
--- a/test/csv/test_row.rb
+++ b/test/csv/test_row.rb
@@ -1,16 +1,9 @@
-#!/usr/bin/env ruby -w
-# encoding: UTF-8
+# -*- coding: utf-8 -*-
# frozen_string_literal: false
-# tc_row.rb
-#
-# Created by James Edward Gray II on 2005-10-31.
-# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
-# under the terms of Ruby's license.
+require_relative "helper"
-require_relative "base"
-
-class TestCSV::Row < TestCSV
+class TestCSVRow < Test::Unit::TestCase
extend DifferentOFS
def setup
@@ -107,6 +100,19 @@ class TestCSV::Row < TestCSV
def test_has_key?
assert_equal(true, @row.has_key?('B'))
assert_equal(false, @row.has_key?('foo'))
+
+ # aliases
+ assert_equal(true, @row.header?('B'))
+ assert_equal(false, @row.header?('foo'))
+
+ assert_equal(true, @row.include?('B'))
+ assert_equal(false, @row.include?('foo'))
+
+ assert_equal(true, @row.member?('B'))
+ assert_equal(false, @row.member?('foo'))
+
+ assert_equal(true, @row.key?('B'))
+ assert_equal(false, @row.key?('foo'))
end
def test_set_field
@@ -263,12 +269,6 @@ class TestCSV::Row < TestCSV
end
def test_queries
- # headers
- assert_send([@row, :header?, "A"])
- assert_send([@row, :header?, "C"])
- assert_not_send([@row, :header?, "Z"])
- assert_send([@row, :include?, "A"]) # alias
-
# fields
assert(@row.field?(4))
assert(@row.field?(nil))
@@ -304,6 +304,17 @@ class TestCSV::Row < TestCSV
end
end
+ def test_each_pair
+ assert_equal([
+ ["A", 1],
+ ["B", 2],
+ ["C", 3],
+ ["A", 4],
+ ["A", nil],
+ ],
+ @row.each_pair.to_a)
+ end
+
def test_enumerable
assert_equal( [["A", 1], ["A", 4], ["A", nil]],
@row.select { |pair| pair.first == "A" } )
@@ -323,7 +334,7 @@ class TestCSV::Row < TestCSV
def test_to_hash
hash = @row.to_hash
- assert_equal({"A" => nil, "B" => 2, "C" => 3}, hash)
+ assert_equal({"A" => @row["A"], "B" => @row["B"], "C" => @row["C"]}, hash)
hash.keys.each_with_index do |string_key, h|
assert_predicate(string_key, :frozen?)
assert_same(string_key, @row.headers[h])
@@ -377,4 +388,45 @@ class TestCSV::Row < TestCSV
r = @row == []
assert_equal false, r
end
+
+ def test_dig_by_index
+ assert_equal(2, @row.dig(1))
+
+ assert_nil(@row.dig(100))
+ end
+
+ def test_dig_by_header
+ assert_equal(2, @row.dig("B"))
+
+ assert_nil(@row.dig("Missing"))
+ end
+
+ def test_dig_cell
+ row = CSV::Row.new(%w{A}, [["foo", ["bar", ["baz"]]]])
+
+ assert_equal("foo", row.dig(0, 0))
+ assert_equal("bar", row.dig(0, 1, 0))
+
+ assert_equal("foo", row.dig("A", 0))
+ assert_equal("bar", row.dig("A", 1, 0))
+ end
+
+ def test_dig_cell_no_dig
+ row = CSV::Row.new(%w{A}, ["foo"])
+
+ assert_raise(TypeError) do
+ row.dig(0, 0)
+ end
+ assert_raise(TypeError) do
+ row.dig("A", 0)
+ end
+ end
+
+ def test_dup
+ row = CSV::Row.new(["A"], ["foo"])
+ dupped_row = row.dup
+ dupped_row.delete("A")
+ assert_equal(["foo", nil],
+ [row["A"], dupped_row["A"]])
+ end
end
diff --git a/test/csv/test_table.rb b/test/csv/test_table.rb
index 25ef11a772..50edc77e40 100755
--- a/test/csv/test_table.rb
+++ b/test/csv/test_table.rb
@@ -1,16 +1,9 @@
-#!/usr/bin/env ruby -w
-# encoding: UTF-8
+# -*- coding: utf-8 -*-
# frozen_string_literal: false
-# tc_table.rb
-#
-# Created by James Edward Gray II on 2005-10-31.
-# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
-# under the terms of Ruby's license.
+require_relative "helper"
-require_relative "base"
-
-class TestCSV::Table < TestCSV
+class TestCSVTable < Test::Unit::TestCase
extend DifferentOFS
def setup
@@ -23,6 +16,8 @@ class TestCSV::Table < TestCSV
@header_table = CSV::Table.new(
[CSV::Row.new(%w{A B C}, %w{A B C}, true)] + @rows
)
+
+ @header_only_table = CSV::Table.new([], headers: %w{A B C})
end
def test_initialze
@@ -44,6 +39,11 @@ class TestCSV::Table < TestCSV
assert_equal(:row, rows.mode)
assert_equal(@table, rows)
+ col_or_row = rows.by_col_or_row
+ assert_equal(:row, rows.mode)
+ assert_equal(:col_or_row, col_or_row.mode)
+ assert_equal(@table, col_or_row)
+
# destructive mode changing calls
assert_equal(@table, @table.by_row!)
assert_equal(:row, @table.mode)
@@ -60,6 +60,17 @@ class TestCSV::Table < TestCSV
assert_equal Array.new, t.headers
end
+ def test_headers_only
+ assert_equal(%w[A B C], @header_only_table.headers)
+ end
+
+ def test_headers_modified_by_row
+ table = CSV::Table.new([], headers: ["A", "B"])
+ table << ["a", "b"]
+ table.first << {"C" => "c"}
+ assert_equal(["A", "B", "C"], table.headers)
+ end
+
def test_index
##################
### Mixed Mode ###
@@ -150,13 +161,13 @@ class TestCSV::Table < TestCSV
@table.to_a )
# verify resulting table
- assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
- A,B,C,Type,Index
- 1,100,3,data,1
- 4,200,6,data,2
- 10,,12,data,3
- 13,,15,data,
- END_RESULT
+ assert_equal(<<-CSV, @table.to_csv)
+A,B,C,Type,Index
+1,100,3,data,1
+4,200,6,data,2
+10,,12,data,3
+13,,15,data,
+ CSV
# with headers
@header_table["Type"] = "data"
@@ -263,6 +274,15 @@ class TestCSV::Table < TestCSV
@table.each { |row| assert_instance_of(CSV::Row, row) }
end
+ def test_each_split
+ yielded_values = []
+ @table.each do |column1, column2, column3|
+ yielded_values << [column1, column2, column3]
+ end
+ assert_equal(@rows.collect(&:to_a),
+ yielded_values)
+ end
+
def test_enumerable
assert_equal( @rows.values_at(0, 2),
@table.select { |row| (row["B"] % 2).zero? } )
@@ -279,12 +299,12 @@ class TestCSV::Table < TestCSV
end
def test_to_csv
- csv = <<-END_CSV.gsub(/^\s+/, "")
- A,B,C
- 1,2,3
- 4,5,6
- 7,8,9
- END_CSV
+ csv = <<-CSV
+A,B,C
+1,2,3
+4,5,6
+7,8,9
+ CSV
# normal conversion
assert_equal(csv, @table.to_csv)
@@ -312,7 +332,7 @@ class TestCSV::Table < TestCSV
assert_equal(CSV::Row.new(%w[A B C], [13, 14, 15]), @table[-1])
end
- def test_delete_mixed
+ def test_delete_mixed_one
##################
### Mixed Mode ###
##################
@@ -323,11 +343,33 @@ class TestCSV::Table < TestCSV
assert_equal(@rows.map { |row| row["A"] }, @table.delete("A"))
# verify resulting table
- assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
- B,C
- 2,3
- 8,9
- END_RESULT
+ assert_equal(<<-CSV, @table.to_csv)
+B,C
+2,3
+8,9
+ CSV
+ end
+
+ def test_delete_mixed_multiple
+ ##################
+ ### Mixed Mode ###
+ ##################
+ # delete row and col
+ second_row = @rows[1]
+ a_col = @rows.map { |row| row["A"] }
+ a_col_without_second_row = a_col[0..0] + a_col[2..-1]
+ assert_equal([
+ second_row,
+ a_col_without_second_row,
+ ],
+ @table.delete(1, "A"))
+
+ # verify resulting table
+ assert_equal(<<-CSV, @table.to_csv)
+B,C
+2,3
+8,9
+ CSV
end
def test_delete_column
@@ -340,12 +382,12 @@ class TestCSV::Table < TestCSV
assert_equal(@rows.map { |row| row["C"] }, @table.delete("C"))
# verify resulting table
- assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
- B
- 2
- 5
- 8
- END_RESULT
+ assert_equal(<<-CSV, @table.to_csv)
+B
+2
+5
+8
+ CSV
end
def test_delete_row
@@ -358,11 +400,11 @@ class TestCSV::Table < TestCSV
assert_raise(TypeError) { @table.delete("C") }
# verify resulting table
- assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
- A,B,C
- 1,2,3
- 7,8,9
- END_RESULT
+ assert_equal(<<-CSV, @table.to_csv)
+A,B,C
+1,2,3
+7,8,9
+ CSV
end
def test_delete_with_blank_rows
@@ -379,10 +421,10 @@ class TestCSV::Table < TestCSV
assert_equal(@table, @table.delete_if { |row| (row["B"] % 2).zero? })
# verify resulting table
- assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
- A,B,C
- 4,5,6
- END_RESULT
+ assert_equal(<<-CSV, @table.to_csv)
+A,B,C
+4,5,6
+ CSV
end
def test_delete_if_row_without_block
@@ -397,10 +439,10 @@ class TestCSV::Table < TestCSV
assert_equal(@table, enum.each { |row| (row["B"] % 2).zero? })
# verify resulting table
- assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
- A,B,C
- 4,5,6
- END_RESULT
+ assert_equal(<<-CSV, @table.to_csv)
+A,B,C
+4,5,6
+ CSV
end
def test_delete_if_column
@@ -410,12 +452,12 @@ class TestCSV::Table < TestCSV
@table.by_col!
assert_equal(@table, @table.delete_if { |h, v| h > "A" })
- assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
- A
- 1
- 4
- 7
- END_RESULT
+ assert_equal(<<-CSV, @table.to_csv)
+A
+1
+4
+7
+ CSV
end
def test_delete_if_column_without_block
@@ -429,12 +471,27 @@ class TestCSV::Table < TestCSV
assert_equal(@table.headers.size, enum.size)
assert_equal(@table, enum.each { |h, v| h > "A" })
- assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
- A
- 1
- 4
- 7
- END_RESULT
+ assert_equal(<<-CSV, @table.to_csv)
+A
+1
+4
+7
+ CSV
+ end
+
+ def test_delete_headers_only
+ ###################
+ ### Column Mode ###
+ ###################
+ @header_only_table.by_col!
+
+ # delete by index
+ assert_equal([], @header_only_table.delete(0))
+ assert_equal(%w[B C], @header_only_table.headers)
+
+ # delete by header
+ assert_equal([], @header_only_table.delete("C"))
+ assert_equal(%w[B], @header_only_table.headers)
end
def test_values_at
@@ -494,4 +551,70 @@ class TestCSV::Table < TestCSV
@table.inspect.encoding],
"inspect() was not ASCII compatible." )
end
+
+ def test_dig_mixed
+ # by row
+ assert_equal(@rows[0], @table.dig(0))
+ assert_nil(@table.dig(100)) # empty row
+
+ # by col
+ assert_equal([2, 5, 8], @table.dig("B"))
+ assert_equal([nil] * @rows.size, @table.dig("Z")) # empty col
+
+ # by row then col
+ assert_equal(2, @table.dig(0, 1))
+ assert_equal(6, @table.dig(1, "C"))
+
+ # by col then row
+ assert_equal(5, @table.dig("B", 1))
+ assert_equal(9, @table.dig("C", 2))
+ end
+
+ def test_dig_by_column
+ @table.by_col!
+
+ assert_equal([2, 5, 8], @table.dig(1))
+ assert_equal([2, 5, 8], @table.dig("B"))
+
+ # by col then row
+ assert_equal(5, @table.dig("B", 1))
+ assert_equal(9, @table.dig("C", 2))
+ end
+
+ def test_dig_by_row
+ @table.by_row!
+
+ assert_equal(@rows[1], @table.dig(1))
+ assert_raise(TypeError) { @table.dig("B") }
+
+ # by row then col
+ assert_equal(2, @table.dig(0, 1))
+ assert_equal(6, @table.dig(1, "C"))
+ end
+
+ def test_dig_cell
+ table = CSV::Table.new([CSV::Row.new(["A"], [["foo", ["bar", ["baz"]]]])])
+
+ # by row, col then cell
+ assert_equal("foo", table.dig(0, "A", 0))
+ assert_equal(["baz"], table.dig(0, "A", 1, 1))
+
+ # by col, row then cell
+ assert_equal("foo", table.dig("A", 0, 0))
+ assert_equal(["baz"], table.dig("A", 0, 1, 1))
+ end
+
+ def test_dig_cell_no_dig
+ table = CSV::Table.new([CSV::Row.new(["A"], ["foo"])])
+
+ # by row, col then cell
+ assert_raise(TypeError) do
+ table.dig(0, "A", 0)
+ end
+
+ # by col, row then cell
+ assert_raise(TypeError) do
+ table.dig("A", 0, 0)
+ end
+ end
end
diff --git a/test/csv/ts_all.rb b/test/csv/ts_all.rb
deleted file mode 100644
index 9eadf12918..0000000000
--- a/test/csv/ts_all.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env ruby -w
-# encoding: UTF-8
-# frozen_string_literal: false
-
-# ts_all.rb
-#
-# Created by James Edward Gray II on 2005-10-31.
-# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
-# under the terms of Ruby's license.
-
-require "test/unit"
-
-require "test_csv_parsing"
-require "test_features"
-require "test_interface"
-require "test_csv_writing"
-require "test_data_converters"
-require "test_row"
-require "test_table"
-require "test_headers"
-require "test_encodings"
diff --git a/test/csv/write/test_converters.rb b/test/csv/write/test_converters.rb
new file mode 100644
index 0000000000..a93b1040ac
--- /dev/null
+++ b/test/csv/write/test_converters.rb
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+module TestCSVWriteConverters
+ def test_one
+ assert_equal(%Q[=a,=b,=c\n],
+ generate_line(["a", "b", "c"],
+ write_converters: ->(value) {"=" + value}))
+ end
+
+ def test_multiple
+ assert_equal(%Q[=a_,=b_,=c_\n],
+ generate_line(["a", "b", "c"],
+ write_converters: [
+ ->(value) {"=" + value},
+ ->(value) {value + "_"},
+ ]))
+ end
+
+ def test_nil_value
+ assert_equal(%Q[a,NaN,c\n],
+ generate_line(["a", nil, "c"],
+ write_nil_value: "NaN"))
+ end
+
+ def test_empty_value
+ assert_equal(%Q[a,,c\n],
+ generate_line(["a", "", "c"],
+ write_empty_value: nil))
+ end
+end
+
+class TestCSVWriteConvertersGenerateLine < Test::Unit::TestCase
+ include TestCSVWriteConverters
+ extend DifferentOFS
+
+ def generate_line(row, **kwargs)
+ CSV.generate_line(row, **kwargs)
+ end
+end
+
+class TestCSVWriteConvertersGenerate < Test::Unit::TestCase
+ include TestCSVWriteConverters
+ extend DifferentOFS
+
+ def generate_line(row, **kwargs)
+ CSV.generate(**kwargs) do |csv|
+ csv << row
+ end
+ end
+end
diff --git a/test/csv/write/test_general.rb b/test/csv/write/test_general.rb
new file mode 100644
index 0000000000..c879f54e74
--- /dev/null
+++ b/test/csv/write/test_general.rb
@@ -0,0 +1,228 @@
+# -*- coding: utf-8 -*-
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+module TestCSVWriteGeneral
+ def test_tab
+ assert_equal("\t#{$INPUT_RECORD_SEPARATOR}",
+ generate_line(["\t"]))
+ end
+
+ def test_quote_character
+ assert_equal(%Q[foo,"""",baz#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", %Q["], "baz"]))
+ end
+
+ def test_quote_character_double
+ assert_equal(%Q[foo,"""""",baz#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", %Q[""], "baz"]))
+ end
+
+ def test_quote
+ assert_equal(%Q[foo,"""bar""",baz#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", %Q["bar"], "baz"]))
+ end
+
+ def test_quote_lf
+ assert_equal(%Q["""\n","""\n"#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([%Q["\n], %Q["\n]]))
+ end
+
+ def test_quote_cr
+ assert_equal(%Q["""\r","""\r"#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([%Q["\r], %Q["\r]]))
+ end
+
+ def test_quote_last
+ assert_equal(%Q[foo,"bar"""#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", %Q[bar"]]))
+ end
+
+ def test_quote_lf_last
+ assert_equal(%Q[foo,"\nbar"""#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", %Q[\nbar"]]))
+ end
+
+ def test_quote_lf_value_lf
+ assert_equal(%Q[foo,"""\nbar\n"""#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", %Q["\nbar\n"]]))
+ end
+
+ def test_quote_lf_value_lf_nil
+ assert_equal(%Q[foo,"""\nbar\n""",#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", %Q["\nbar\n"], nil]))
+ end
+
+ def test_cr
+ assert_equal(%Q[foo,"\r",baz#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", "\r", "baz"]))
+ end
+
+ def test_lf
+ assert_equal(%Q[foo,"\n",baz#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", "\n", "baz"]))
+ end
+
+ def test_cr_lf
+ assert_equal(%Q[foo,"\r\n",baz#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", "\r\n", "baz"]))
+ end
+
+ def test_cr_dot_lf
+ assert_equal(%Q[foo,"\r.\n",baz#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", "\r.\n", "baz"]))
+ end
+
+ def test_cr_lf_cr
+ assert_equal(%Q[foo,"\r\n\r",baz#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", "\r\n\r", "baz"]))
+ end
+
+ def test_cr_lf_lf
+ assert_equal(%Q[foo,"\r\n\n",baz#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", "\r\n\n", "baz"]))
+ end
+
+ def test_cr_lf_comma
+ assert_equal(%Q["\r\n,"#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["\r\n,"]))
+ end
+
+ def test_cr_lf_comma_nil
+ assert_equal(%Q["\r\n,",#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["\r\n,", nil]))
+ end
+
+ def test_comma
+ assert_equal(%Q[","#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([","]))
+ end
+
+ def test_comma_double
+ assert_equal(%Q[",",","#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([",", ","]))
+ end
+
+ def test_comma_and_value
+ assert_equal(%Q[foo,"foo,bar",baz#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", "foo,bar", "baz"]))
+ end
+
+ def test_one_element
+ assert_equal(%Q[foo#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo"]))
+ end
+
+ def test_nil_values_only
+ assert_equal(%Q[,,#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([nil, nil, nil]))
+ end
+
+ def test_nil_double_only
+ assert_equal(%Q[,#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([nil, nil]))
+ end
+
+ def test_nil_values
+ assert_equal(%Q[foo,,,#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", nil, nil, nil]))
+ end
+
+ def test_nil_value_first
+ assert_equal(%Q[,foo,baz#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([nil, "foo", "baz"]))
+ end
+
+ def test_nil_value_middle
+ assert_equal(%Q[foo,,baz#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", nil, "baz"]))
+ end
+
+ def test_nil_value_last
+ assert_equal(%Q[foo,baz,#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", "baz", nil]))
+ end
+
+ def test_nil_empty
+ assert_equal(%Q[,""#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([nil, ""]))
+ end
+
+ def test_nil_cr
+ assert_equal(%Q[,"\r"#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([nil, "\r"]))
+ end
+
+ def test_values
+ assert_equal(%Q[foo,bar#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", "bar"]))
+ end
+
+ def test_semi_colon
+ assert_equal(%Q[;#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([";"]))
+ end
+
+ def test_semi_colon_values
+ assert_equal(%Q[;,;#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([";", ";"]))
+ end
+
+ def test_tab_values
+ assert_equal(%Q[\t,\t#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["\t", "\t"]))
+ end
+
+ def test_col_sep
+ assert_equal(%Q[a;b;;c#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["a", "b", nil, "c"],
+ col_sep: ";"))
+ assert_equal(%Q[a\tb\t\tc#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["a", "b", nil, "c"],
+ col_sep: "\t"))
+ end
+
+ def test_row_sep
+ assert_equal(%Q[a,b,,c\r\n],
+ generate_line(["a", "b", nil, "c"],
+ row_sep: "\r\n"))
+ end
+
+ def test_force_quotes
+ assert_equal(%Q["1","b","","already ""quoted"""#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([1, "b", nil, %Q{already "quoted"}],
+ force_quotes: true))
+ end
+
+ def test_encoding_utf8
+ assert_equal(%Q[ã‚,ã„,ã†#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["ã‚" , "ã„", "ã†"]))
+ end
+
+ def test_encoding_euc_jp
+ row = ["ã‚", "ã„", "ã†"].collect {|field| field.encode("EUC-JP")}
+ assert_equal(%Q[ã‚,ã„,ã†#{$INPUT_RECORD_SEPARATOR}].encode("EUC-JP"),
+ generate_line(row))
+ end
+end
+
+class TestCSVWriteGeneralGenerateLine < Test::Unit::TestCase
+ include TestCSVWriteGeneral
+ extend DifferentOFS
+
+ def generate_line(row, **kwargs)
+ CSV.generate_line(row, **kwargs)
+ end
+end
+
+class TestCSVWriteGeneralGenerate < Test::Unit::TestCase
+ include TestCSVWriteGeneral
+ extend DifferentOFS
+
+ def generate_line(row, **kwargs)
+ CSV.generate(**kwargs) do |csv|
+ csv << row
+ end
+ end
+end
diff --git a/test/csv/write/test_quote_empty.rb b/test/csv/write/test_quote_empty.rb
new file mode 100644
index 0000000000..70f73dad4a
--- /dev/null
+++ b/test/csv/write/test_quote_empty.rb
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+# frozen_string_literal: false
+
+require_relative "../helper"
+
+module TestCSVWriteQuoteEmpty
+ def test_quote_empty_default
+ assert_equal(%Q["""",""#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([%Q["], ""]))
+ end
+
+ def test_quote_empty_false
+ assert_equal(%Q["""",#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([%Q["], ""],
+ quote_empty: false))
+ end
+
+ def test_empty_default
+ assert_equal(%Q[foo,"",baz#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", "", "baz"]))
+ end
+
+ def test_empty_false
+ assert_equal(%Q[foo,,baz#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["foo", "", "baz"],
+ quote_empty: false))
+ end
+
+ def test_empty_only_default
+ assert_equal(%Q[""#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([""]))
+ end
+
+ def test_empty_only_false
+ assert_equal(%Q[#{$INPUT_RECORD_SEPARATOR}],
+ generate_line([""],
+ quote_empty: false))
+ end
+
+ def test_empty_double_default
+ assert_equal(%Q["",""#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["", ""]))
+ end
+
+ def test_empty_double_false
+ assert_equal(%Q[,#{$INPUT_RECORD_SEPARATOR}],
+ generate_line(["", ""],
+ quote_empty: false))
+ end
+end
+
+class TestCSVWriteQuoteEmptyGenerateLine < Test::Unit::TestCase
+ include TestCSVWriteQuoteEmpty
+ extend DifferentOFS
+
+ def generate_line(row, **kwargs)
+ CSV.generate_line(row, **kwargs)
+ end
+end
+
+class TestCSVWriteQuoteEmptyGenerate < Test::Unit::TestCase
+ include TestCSVWriteQuoteEmpty
+ extend DifferentOFS
+
+ def generate_line(row, **kwargs)
+ CSV.generate(**kwargs) do |csv|
+ csv << row
+ end
+ end
+end
diff --git a/test/date/test_date.rb b/test/date/test_date.rb
index 247cce1eaf..03e935e299 100644
--- a/test/date/test_date.rb
+++ b/test/date/test_date.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'date'
@@ -129,6 +129,8 @@ class TestDate < Test::Unit::TestCase
assert_equal(3, h.size)
assert_equal(9, h[Date.new(1999,5,25)])
assert_equal(9, h[DateTime.new(1999,5,25)])
+
+ assert_instance_of(String, Date.new(1999,5,25).hash.to_s)
end
def test_freeze
diff --git a/test/date/test_date_arith.rb b/test/date/test_date_arith.rb
index f5ac5bf30b..e1f29dbf22 100644
--- a/test/date/test_date_arith.rb
+++ b/test/date/test_date_arith.rb
@@ -1,13 +1,20 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'date'
class TestDateArith < Test::Unit::TestCase
+ class Rat < Numeric
+ def to_r; self; end
+ end
- def new_offset
+ def test_new_offset
d = DateTime.new(2002, 3, 14)
assert_equal(Rational(9, 24), d.new_offset(Rational(9, 24)).offset)
assert_equal(Rational(9, 24), d.new_offset('+0900').offset)
+ n = Rat.new
+ assert_raise(TypeError) do
+ Timeout.timeout(1) {d.new_offset(n)}
+ end
end
def test__plus
@@ -37,6 +44,13 @@ class TestDateArith < Test::Unit::TestCase
assert_raise(e) do
DateTime.new(2000,2,29) + Time.mktime(2000,2,29)
end
+ n = Rat.new
+ assert_raise(e) do
+ Timeout.timeout(1) {Date.new(2000,2,29) + n}
+ end
+ assert_raise(e) do
+ Timeout.timeout(1) {DateTime.new(2000,2,29) + n}
+ end
end
def test__minus
@@ -262,4 +276,17 @@ class TestDateArith < Test::Unit::TestCase
assert_equal(8, e.to_a.size)
end
+ def test_step__compare
+ o = Object.new
+ def o.<=>(*);end
+ assert_raise(ArgumentError) {
+ Date.new(2000, 1, 1).step(3, o).to_a
+ }
+
+ o = Object.new
+ def o.<=>(*);2;end
+ a = []
+ Date.new(2000, 1, 1).step(3, o) {|d| a << d}
+ assert_empty(a)
+ end
end
diff --git a/test/date/test_date_attr.rb b/test/date/test_date_attr.rb
index 3d1b0a2e6e..1e4d1bfd7a 100644
--- a/test/date/test_date_attr.rb
+++ b/test/date/test_date_attr.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'date'
@@ -88,17 +88,16 @@ class TestDateAttr < Test::Unit::TestCase
end
def test_nth_kday
- skip unless Date.new.respond_to?(:nth_kday?, true)
- assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, 1,0))
- assert_equal(true, Date.new(2001,1,14).__send__(:nth_kday?, 2,0))
- assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, 3,0))
- assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, 4,0))
- assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, 5,0))
- assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, -1,0))
- assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, -2,0))
- assert_equal(true, Date.new(2001,1,14).__send__(:nth_kday?, -3,0))
- assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, -4,0))
- assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, -5,0))
- end
+ assert_equal(false, Date.new(2001,1,14).nth_kday?(1,0))
+ assert_equal(true, Date.new(2001,1,14).nth_kday?(2,0))
+ assert_equal(false, Date.new(2001,1,14).nth_kday?(3,0))
+ assert_equal(false, Date.new(2001,1,14).nth_kday?(4,0))
+ assert_equal(false, Date.new(2001,1,14).nth_kday?(5,0))
+ assert_equal(false, Date.new(2001,1,14).nth_kday?(-1,0))
+ assert_equal(false, Date.new(2001,1,14).nth_kday?(-2,0))
+ assert_equal(true, Date.new(2001,1,14).nth_kday?(-3,0))
+ assert_equal(false, Date.new(2001,1,14).nth_kday?(-4,0))
+ assert_equal(false, Date.new(2001,1,14).nth_kday?(-5,0))
+ end if Date.new.respond_to?(:nth_kday?, true)
end
diff --git a/test/date/test_date_base.rb b/test/date/test_date_base.rb
deleted file mode 100644
index 06fec19e52..0000000000
--- a/test/date/test_date_base.rb
+++ /dev/null
@@ -1,443 +0,0 @@
-# frozen_string_literal: false
-require 'test/unit'
-require 'date'
-
-begin
- require 'calendar'
- include Calendar
-rescue LoadError
-end
-
-class TestDateBase < Test::Unit::TestCase
-
- def setup
- if defined?(Calendar)
- @from ||= julian_day_number_from_absolute(absolute_from_julian(1, 1, 1601))
- @to ||= julian_day_number_from_absolute(absolute_from_julian(12, 31, 2400))
- @from4t ||= julian_day_number_from_absolute(absolute_from_julian(1, 1, 1970))
- @to4t ||= julian_day_number_from_absolute(absolute_from_julian(12, 31, 2037))
- end
- end
-
- def test__inf
- assert_equal(0, Date::Infinity.new(-1) <=> Date::Infinity.new(-1))
- assert_equal(-1, Date::Infinity.new(-1) <=> Date::Infinity.new(+1))
- assert_equal(-1, Date::Infinity.new(-1) <=> 0)
-
- assert_equal(1, Date::Infinity.new(+1) <=> Date::Infinity.new(-1))
- assert_equal(0, Date::Infinity.new(+1) <=> Date::Infinity.new(+1))
- assert_equal(1, Date::Infinity.new(+1) <=> 0)
-
- assert_equal(1, 0 <=> Date::Infinity.new(-1))
- assert_equal(-1, 0 <=> Date::Infinity.new(+1))
- assert_equal(0, 0 <=> 0)
-
- assert_equal(0, Date::ITALY <=> Date::ITALY)
- assert_equal(-1, Date::ITALY <=> Date::ENGLAND)
- assert_equal(-1, Date::ITALY <=> Date::JULIAN)
- assert_equal(1, Date::ITALY <=> Date::GREGORIAN)
-
- assert_equal(1, Date::ENGLAND <=> Date::ITALY)
- assert_equal(0, Date::ENGLAND <=> Date::ENGLAND)
- assert_equal(-1, Date::ENGLAND <=> Date::JULIAN)
- assert_equal(1, Date::ENGLAND <=> Date::GREGORIAN)
-
- assert_equal(1, Date::JULIAN <=> Date::ITALY)
- assert_equal(1, Date::JULIAN <=> Date::ENGLAND)
- assert_equal(0, Date::JULIAN <=> Date::JULIAN)
- assert_equal(1, Date::JULIAN <=> Date::GREGORIAN)
-
- assert_equal(-1, Date::GREGORIAN <=> Date::ITALY)
- assert_equal(-1, Date::GREGORIAN <=> Date::ENGLAND)
- assert_equal(-1, Date::GREGORIAN <=> Date::JULIAN)
- assert_equal(0, Date::GREGORIAN <=> Date::GREGORIAN)
- end
-
- def test_ordinal__julian
- skip unless defined?(Calendar)
- for j in @from..@to
- m, d, y = julian_from_absolute(absolute_from_julian_day_number(j))
- j0 = julian_day_number_from_absolute(absolute_from_julian(12, 31, y - 1))
- j2 = julian_day_number_from_absolute(absolute_from_julian(m, d, y))
- assert_equal(j, j2)
- oy, od = Date.__send__(:jd_to_ordinal, j, Date::JULIAN)
- assert_equal(y, oy)
- assert_equal(j2 - j0, od)
- oj = Date.__send__(:ordinal_to_jd, oy, od, Date::JULIAN)
- assert_equal(j, oj)
- end
- end
-
- def test_ordinal__gregorian
- skip unless defined?(Calendar)
- for j in @from..@to
- m, d, y = gregorian_from_absolute(absolute_from_julian_day_number(j))
- j0 =
- julian_day_number_from_absolute(absolute_from_gregorian(12, 31, y - 1))
- j2 = julian_day_number_from_absolute(absolute_from_gregorian(m, d, y))
- assert_equal(j, j2)
- oy, od = Date.__send__(:jd_to_ordinal, j, Date::GREGORIAN)
- assert_equal(y, oy)
- assert_equal(j2 - j0, od)
- oj = Date.__send__(:ordinal_to_jd, oy, od, Date::GREGORIAN)
- assert_equal(j, oj)
- end
- end
-
- def test_civil__julian
- skip unless defined?(Calendar)
- for j in @from..@to
- m, d, y = julian_from_absolute(absolute_from_julian_day_number(j))
- j2 = julian_day_number_from_absolute(absolute_from_julian(m, d, y))
- assert_equal(j2, j)
- cy, cm, cd = Date.__send__(:jd_to_civil, j, Date::JULIAN)
- assert_equal(y, cy)
- assert_equal(m, cm)
- assert_equal(d, cd)
- cj = Date.__send__(:civil_to_jd, cy, cm, cd, Date::JULIAN)
- assert_equal(j, cj)
- end
- end
-
- def test_civil__gregorian
- skip unless defined?(Calendar)
- for j in @from..@to
- m, d, y = gregorian_from_absolute(absolute_from_julian_day_number(j))
- j2 = julian_day_number_from_absolute(absolute_from_gregorian(m, d, y))
- assert_equal(j2, j)
- cy, cm, cd = Date.__send__(:jd_to_civil, j, Date::GREGORIAN)
- assert_equal(y, cy)
- assert_equal(m, cm)
- assert_equal(d, cd)
- cj = Date.__send__(:civil_to_jd, cy, cm, cd, Date::GREGORIAN)
- assert_equal(j, cj)
- end
- end
-
- def test_commercial__gregorian
- skip unless defined?(Calendar)
- for j in @from..@to
- w, d, y = iso_from_absolute(absolute_from_julian_day_number(j))
- j2 = julian_day_number_from_absolute(absolute_from_iso(w, d, y))
- assert_equal(j2, j)
- cy, cw, cd = Date.__send__(:jd_to_commercial, j, Date::GREGORIAN)
- assert_equal(y, cy)
- assert_equal(w, cw)
- assert_equal(d, cd)
- cj = Date.__send__(:commercial_to_jd, cy, cw, cd, Date::GREGORIAN)
- assert_equal(j, cj)
- end
- end
-
- def test_weeknum
- skip unless defined?(Calendar)
- for j in @from..@to
- for k in 0..1
- wy, ww, wd = Date.__send__(:jd_to_weeknum, j, k, Date::GREGORIAN)
- wj = Date.__send__(:weeknum_to_jd, wy, ww, wd, k, Date::GREGORIAN)
- assert_equal(j, wj)
- end
- end
- end
-
- def test_weeknum__2
- skip unless defined?(Calendar)
- for j in @from4t..@to4t
- d = Date.jd(j)
- t = Time.mktime(d.year, d.mon, d.mday)
- [
- '%Y %U %w',
- '%Y %U %u',
- '%Y %W %w',
- '%Y %W %u'
- ].each do |fmt|
- s = t.strftime(fmt)
- d2 = Date.strptime(s, fmt)
- assert_equal(j, d2.jd)
- end
- end
- end
-
- def test_nth_kday
- skip unless defined?(Calendar)
- skip unless (Date.respond_to?(:nth_kday_to_jd, true) &&
- Date.respond_to?(:jd_to_nth_kday, true))
- for y in 1601..2401
- for m in 1..12
- for n in -5..5
- next if n == 0
- for k in 0..6
- j = julian_day_number_from_absolute(Nth_Kday(n, k, m, y))
- j2 = Date.__send__(:nth_kday_to_jd, y, m, n, k, Date::GREGORIAN)
- assert_equal(j, j2)
-
- d1 = Date.__send__(:jd_to_nth_kday, j2, Date::GREGORIAN)
- j3 = Date.__send__(:nth_kday_to_jd, *d1)
- assert_equal(j, j3)
- end
- end
- end
- end
- end
-
- def test_jd
- assert_equal(1 << 33, Date.jd(1 << 33).jd)
- end
-
- def test_mjd
- skip unless Date.respond_to?(:mjd_to_jd, true)
- jd = Date.__send__(:mjd_to_jd, 51321)
- mjd = Date.__send__(:jd_to_mjd, jd)
- assert_equal(51321, mjd)
- end
-
- def test_ld
- skip unless Date.respond_to?(:ld_to_jd, true)
- jd = Date.__send__(:ld_to_jd, 152162)
- ld = Date.__send__(:jd_to_ld, jd)
- assert_equal(152162, ld)
- end
-
- def test_wday
- skip unless Date.respond_to?(:jd_to_wday, true)
- assert_equal(4, Date.__send__(:jd_to_wday, 3))
- assert_equal(3, Date.__send__(:jd_to_wday, 2))
- assert_equal(2, Date.__send__(:jd_to_wday, 1))
- assert_equal(1, Date.__send__(:jd_to_wday, 0))
- assert_equal(0, Date.__send__(:jd_to_wday, -1))
- assert_equal(6, Date.__send__(:jd_to_wday, -2))
- assert_equal(5, Date.__send__(:jd_to_wday, -3))
- end
-
- def test_leap?
- assert_equal(true, Date.julian_leap?(1900))
- assert_equal(false, Date.julian_leap?(1999))
- assert_equal(true, Date.julian_leap?(2000))
-
- assert_equal(false, Date.gregorian_leap?(1900))
- assert_equal(false, Date.gregorian_leap?(1999))
- assert_equal(true, Date.gregorian_leap?(2000))
-
- assert_equal(Date.leap?(1990), Date.gregorian_leap?(1900))
- assert_equal(Date.leap?(1999), Date.gregorian_leap?(1999))
- assert_equal(Date.leap?(2000), Date.gregorian_leap?(2000))
- end
-
- def test_valid_jd
- valid_jd_p = :_valid_jd?
- skip unless Date.respond_to?(valid_jd_p, true)
- assert_equal(-1, Date.__send__(valid_jd_p, -1))
- assert_equal(0, Date.__send__(valid_jd_p, 0))
- assert_equal(1, Date.__send__(valid_jd_p, 1))
- assert_equal(2452348, Date.__send__(valid_jd_p, 2452348))
- end
-
- def test_valid_ordinal
- valid_ordinal_p = :_valid_ordinal?
- skip unless Date.respond_to?(valid_ordinal_p, true)
- assert_nil(Date.__send__(valid_ordinal_p, 1999,366))
- assert_equal(2451910, Date.__send__(valid_ordinal_p, 2000,366))
- assert_nil(Date.__send__(valid_ordinal_p, 1999,-366))
- assert_equal(2451545, Date.__send__(valid_ordinal_p, 2000,-366))
- assert_equal(2452275, Date.__send__(valid_ordinal_p, 2001,365))
- assert_nil(Date.__send__(valid_ordinal_p, 2001,366))
- assert_equal(Date.__send__(valid_ordinal_p, 2001,1),
- Date.__send__(valid_ordinal_p, 2001,-365))
- assert_nil(Date.__send__(valid_ordinal_p, 2001,-366))
- assert_equal(2452348, Date.__send__(valid_ordinal_p, 2002,73))
- end
-
- def test_valid_ordinal__edge
- valid_ordinal_p = :_valid_ordinal?
- skip unless Date.respond_to?(valid_ordinal_p, true)
- (1601..2400).each do |y|
- d = if Date.leap?(y) then 366 else 365 end
- assert_not_nil(Date.__send__(valid_ordinal_p, y,d))
- assert_nil(Date.__send__(valid_ordinal_p, y,d + 1))
- assert_not_nil(Date.__send__(valid_ordinal_p, y,-d))
- assert_nil(Date.__send__(valid_ordinal_p, y,-(d + 1)))
- end
- end
-
- # October 1582
- # S M Tu W Th F S
- # 274 275 276 277 288 289
- # 290 291 292 293 294 295 296
- # 297 298 299 300 301 302 303
- # 304
-
- # October 1582
- # S M Tu W Th F S
- # -92 -91 -90 -89 -78 -77
- # -76 -75 -74 -73 -72 -71 -70
- # -69 -68 -67 -66 -65 -64 -63
- # -62
-
- def test_valid_ordinal__italy
- valid_ordinal_p = :_valid_ordinal?
- skip unless Date.respond_to?(valid_ordinal_p, true)
- (1..355).each do |d|
- assert_not_nil(Date.__send__(valid_ordinal_p, 1582,d,Date::ITALY))
- end
- (356..365).each do |d|
- assert_nil(Date.__send__(valid_ordinal_p, 1582,d,Date::ITALY))
- end
- end
-
- # September 1752
- # S M Tu W Th F S
- # 245 246 258 259 260
- # 261 262 263 264 265 266 267
- # 268 269 270 271 272 273 274
-
- def test_valid_ordinal__england
- valid_ordinal_p = :_valid_ordinal?
- skip unless Date.respond_to?(valid_ordinal_p, true)
- (1..355).each do |d|
- assert_not_nil(Date.__send__(valid_ordinal_p, 1752,d,Date::ENGLAND))
- end
- (356..366).each do |d|
- assert_nil(Date.__send__(valid_ordinal_p, 1752,d,Date::ENGLAND))
- end
- end
-
- def test_valid_civil
- valid_civil_p = :_valid_civil?
- skip unless Date.respond_to?(valid_civil_p, true)
- assert_nil(Date.__send__(valid_civil_p, 1999,2,29))
- assert_equal(2451604, Date.__send__(valid_civil_p, 2000,2,29))
- assert_nil(Date.__send__(valid_civil_p, 1999,2,-29))
- assert_equal(2451576, Date.__send__(valid_civil_p, 2000,2,-29))
- assert_equal(2451941, Date.__send__(valid_civil_p, 2001,1,31))
- assert_nil(Date.__send__(valid_civil_p, 2001,1,32))
- assert_equal(Date.__send__(valid_civil_p, 2001,1,1),
- Date.__send__(valid_civil_p, 2001,1,-31))
- assert_nil(Date.__send__(valid_civil_p, 2001,1,-32))
- assert_equal(2452348, Date.__send__(valid_civil_p, 2002,3,14))
- assert_nil(Date.__send__(valid_civil_p, 2010,-13,-1))
- end
-
- def test_valid_civil__edge
- valid_civil_p = :_valid_civil?
- skip unless Date.respond_to?(valid_civil_p, true)
- (1601..2400).each do |y|
- d = if Date.leap?(y) then 29 else 28 end
- assert_not_nil(Date.__send__(valid_civil_p, y,2,d))
- assert_nil(Date.__send__(valid_civil_p, y,2,d + 1))
- assert_not_nil(Date.__send__(valid_civil_p, y,2,-d))
- assert_nil(Date.__send__(valid_civil_p, y,2,-(d + 1)))
- end
- end
-
- # 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
-
- def test_valid_civil__italy
- valid_civil_p = :_valid_civil?
- skip unless Date.respond_to?(valid_civil_p, true)
- (1..4).each do |d|
- assert_not_nil(Date.__send__(valid_civil_p, 1582,10,d,Date::ITALY))
- end
- (5..14).each do |d|
- assert_nil(Date.__send__(valid_civil_p, 1582,10,d,Date::ITALY))
- end
- (15..31).each do |d|
- assert_not_nil(Date.__send__(valid_civil_p, 1582,10,d,Date::ITALY))
- end
- (32..100).each do |d|
- assert_nil(Date.__send__(valid_civil_p, 1582,10,d,Date::ITALY))
- end
- (-31..-22).each do |d|
- assert_nil(Date.__send__(valid_civil_p, 1582,10,d,Date::ITALY))
- end
- (-21..-1).each do |d|
- assert_not_nil(Date.__send__(valid_civil_p, 1582,10,d,Date::ITALY))
- end
- end
-
- # 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
-
- def test_valid_civil__england
- valid_civil_p = :_valid_civil?
- skip unless Date.respond_to?(valid_civil_p, true)
- (1..2).each do |d|
- assert_not_nil(Date.__send__(valid_civil_p, 1752,9,d,Date::ENGLAND))
- end
- (3..13).each do |d|
- assert_nil(Date.__send__(valid_civil_p, 1752,9,d,Date::ENGLAND))
- end
- (14..30).each do |d|
- assert_not_nil(Date.__send__(valid_civil_p, 1752,9,d,Date::ENGLAND))
- end
- (31..100).each do |d|
- assert_nil(Date.__send__(valid_civil_p, 1752,9,d,Date::ENGLAND))
- end
- (-31..-20).each do |d|
- assert_nil(Date.__send__(valid_civil_p, 1752,9,d,Date::ENGLAND))
- end
- (-19..-1).each do |d|
- assert_not_nil(Date.__send__(valid_civil_p, 1752,9,d,Date::ENGLAND))
- end
- end
-
- def test_valid_commercial
- valid_commercial_p = :_valid_commercial?
- skip unless Date.respond_to?(valid_commercial_p, true)
- assert_nil(Date.__send__(valid_commercial_p, 1999,53,1))
- assert_equal(2453367, Date.__send__(valid_commercial_p, 2004,53,1))
- assert_nil(Date.__send__(valid_commercial_p, 1999,-53,-1))
- assert_equal(2453009, Date.__send__(valid_commercial_p, 2004,-53,-1))
- assert_equal(2452348, Date.__send__(valid_commercial_p, 2002,11,4))
- end
-
- def test_valid_weeknum
- valid_weeknum_p = :_valid_weeknum?
- skip unless Date.respond_to?(valid_weeknum_p, true)
- assert_nil(Date.__send__(valid_weeknum_p, 1999,53,0, 0))
- assert_equal(2454101, Date.__send__(valid_weeknum_p, 2006,53,0, 0))
- assert_nil(Date.__send__(valid_weeknum_p, 1999,-53,-1, 0))
- assert_equal(2453743, Date.__send__(valid_weeknum_p, 2006,-53,-1, 0))
- assert_equal(2452355, Date.__send__(valid_weeknum_p, 2002,11,4, 0))
- assert_nil(Date.__send__(valid_weeknum_p, 1999,53,0, 1))
- assert_equal(2454101, Date.__send__(valid_weeknum_p, 2006,52,6, 1))
- assert_nil(Date.__send__(valid_weeknum_p, 1999,-53,-1, 1))
- assert_equal(2453743, Date.__send__(valid_weeknum_p, 2006,-52,-2, 1))
- assert_equal(2452355, Date.__send__(valid_weeknum_p, 2002,11,3, 1))
- end
-
- def test_valid_nth_kday
- valid_nth_kday_p = :_valid_nth_kday?
- skip unless Date.respond_to?(valid_nth_kday_p, true)
- assert_nil(Date.__send__(valid_nth_kday_p, 1992,2, 5,0))
- assert_equal(2448682, Date.__send__(valid_nth_kday_p, 1992,2, 5,6))
- assert_equal(2448682, Date.__send__(valid_nth_kday_p, 1992,2, 5,-1))
- assert_equal(2448682, Date.__send__(valid_nth_kday_p, 1992,2, -1,6))
- assert_equal(2448682, Date.__send__(valid_nth_kday_p, 1992,2, -1,-1))
- end
-
- def test_valid_time
- valid_time_p = :_valid_time?
- skip unless Date.respond_to?(valid_time_p, true)
- assert_equal(Rational(0), DateTime.__send__(valid_time_p, 0,0,0))
- assert_nil(DateTime.__send__(valid_time_p, 25,59,59))
- assert_nil(DateTime.__send__(valid_time_p, 23,60,59))
- assert_nil(DateTime.__send__(valid_time_p, 23,59,60))
- assert_equal(Rational(86399, 86400),
- DateTime.__send__(valid_time_p, 23,59,59))
- assert_equal(Rational(86399, 86400),
- DateTime.__send__(valid_time_p, -1,-1,-1))
- assert_equal(Rational(1), DateTime.__send__(valid_time_p, 24,0,0))
- assert_nil(DateTime.__send__(valid_time_p, 24,0,1))
- assert_nil(DateTime.__send__(valid_time_p, 24,1,0))
- assert_nil(DateTime.__send__(valid_time_p, 24,1,1))
- end
-
-end
diff --git a/test/date/test_date_compat.rb b/test/date/test_date_compat.rb
index 1bd30b5f80..316f8c0298 100644
--- a/test/date/test_date_compat.rb
+++ b/test/date/test_date_compat.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'date'
diff --git a/test/date/test_date_conv.rb b/test/date/test_date_conv.rb
index 3729476314..5c295daba6 100644
--- a/test/date/test_date_conv.rb
+++ b/test/date/test_date_conv.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'date'
diff --git a/test/date/test_date_marshal.rb b/test/date/test_date_marshal.rb
index 27ae1e9004..c7ce737aac 100644
--- a/test/date/test_date_marshal.rb
+++ b/test/date/test_date_marshal.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'date'
@@ -30,13 +30,23 @@ class TestDateMarshal < Test::Unit::TestCase
a = d.marshal_dump
d.freeze
assert(d.frozen?)
- assert_raise(RuntimeError){d.marshal_load(a)}
+ expected_error = defined?(FrozenError) ? FrozenError : RuntimeError
+ assert_raise(expected_error){d.marshal_load(a)}
d = DateTime.now
a = d.marshal_dump
d.freeze
assert(d.frozen?)
- assert_raise(RuntimeError){d.marshal_load(a)}
+ expected_error = defined?(FrozenError) ? FrozenError : RuntimeError
+ assert_raise(expected_error){d.marshal_load(a)}
end
+ def test_memsize
+ require 'objspace'
+ t = DateTime.new(2018, 11, 13)
+ size = ObjectSpace.memsize_of(t)
+ t2 = Marshal.load(Marshal.dump(t))
+ assert_equal(t, t2)
+ assert_equal(size, ObjectSpace.memsize_of(t2), "not reallocated but memsize changed")
+ end
end
diff --git a/test/date/test_date_new.rb b/test/date/test_date_new.rb
index 80ecbc204e..d27116b198 100644
--- a/test/date/test_date_new.rb
+++ b/test/date/test_date_new.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'date'
@@ -203,50 +203,48 @@ class TestDateNew < Test::Unit::TestCase
end
def test_weeknum
- skip unless Date.respond_to?(:weeknum, true)
- d = Date.__send__(:weeknum)
- dt = DateTime.__send__(:weeknum)
+ d = Date.weeknum
+ dt = DateTime.weeknum
assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
assert_equal([-4712, 1, 1], [dt.year, dt.mon, dt.mday])
assert_equal([0, 0, 0], [dt.hour, dt.min, dt.sec])
- d = Date.__send__(:weeknum, 2002,11,4, 0)
+ d = Date.weeknum(2002,11,4, 0)
assert_equal(2452355, d.jd)
- d = DateTime.__send__(:weeknum, 2002,11,4, 0, 11,22,33)
+ d = DateTime.weeknum(2002,11,4, 0, 11,22,33)
assert_equal(2452355, d.jd)
assert_equal([11,22,33], [d.hour, d.min, d.sec])
assert_raise(ArgumentError) do
- Date.__send__(:weeknum, 1999,53,0, 0)
+ Date.weeknum(1999,53,0, 0)
end
assert_raise(ArgumentError) do
- Date.__send__(:weeknum, 1999,-53,-1, 0)
+ Date.weeknum(1999,-53,-1, 0)
end
- end
+ end if Date.respond_to?(:weeknum, true)
def test_nth_kday
- skip unless Date.respond_to?(:nth_kday, true)
- d = Date.__send__(:nth_kday)
- dt = DateTime.__send__(:nth_kday)
+ d = Date.nth_kday
+ dt = DateTime.nth_kday
assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
assert_equal([-4712, 1, 1], [dt.year, dt.mon, dt.mday])
assert_equal([0, 0, 0], [dt.hour, dt.min, dt.sec])
- d = Date.__send__(:nth_kday, 1992,2, 5,6)
+ d = Date.nth_kday(1992,2, 5,6)
assert_equal(2448682, d.jd)
- d = DateTime.__send__(:nth_kday, 1992,2, 5,6, 11,22,33)
+ d = DateTime.nth_kday(1992,2, 5,6, 11,22,33)
assert_equal(2448682, d.jd)
assert_equal([11,22,33], [d.hour, d.min, d.sec])
assert_raise(ArgumentError) do
- Date.__send__(:nth_kday, 2006,5, 5,0)
+ Date.nth_kday(2006,5, 5,0)
end
assert_raise(ArgumentError) do
- Date.__send__(:nth_kday, 2006,5, -5,0)
+ Date.nth_kday(2006,5, -5,0)
end
- end
+ end if Date.respond_to?(:nth_kday, true)
def test_today
z = Time.now
@@ -269,4 +267,12 @@ class TestDateNew < Test::Unit::TestCase
assert_in_delta(t, t2, t - z + 2)
end
+ def test_memsize
+ require 'objspace'
+ t = DateTime.now
+ size = ObjectSpace.memsize_of(t)
+ t.__send__(:initialize_copy, Date.today)
+ assert_instance_of(DateTime, t)
+ assert_equal(size, ObjectSpace.memsize_of(t), "not reallocated but memsize changed")
+ end
end
diff --git a/test/date/test_date_parse.rb b/test/date/test_date_parse.rb
index d980674591..d1163b29cb 100644
--- a/test/date/test_date_parse.rb
+++ b/test/date/test_date_parse.rb
@@ -1,6 +1,7 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'date'
+require 'timeout'
class TestDateParse < Test::Unit::TestCase
@@ -823,6 +824,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._iso8601('')
assert_equal({}, h)
+
+ h = Date._iso8601(nil)
+ assert_equal({}, h)
+
+ h = Date._iso8601('01-02-03T04:05:06Z'.to_sym)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test__rfc3339
@@ -838,6 +846,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._rfc3339('')
assert_equal({}, h)
+
+ h = Date._rfc3339(nil)
+ assert_equal({}, h)
+
+ h = Date._rfc3339('2001-02-03T04:05:06Z'.to_sym)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test__xmlschema
@@ -920,6 +935,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._xmlschema('')
assert_equal({}, h)
+
+ h = Date._xmlschema(nil)
+ assert_equal({}, h)
+
+ h = Date._xmlschema('2001-02-03'.to_sym)
+ assert_equal([2001, 2, 3, nil, nil, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test__rfc2822
@@ -952,6 +974,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._rfc2822('')
assert_equal({}, h)
+
+ h = Date._rfc2822(nil)
+ assert_equal({}, h)
+
+ h = Date._rfc2822('Sat, 3 Feb 2001 04:05:06 UT'.to_sym)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test__httpdate
@@ -972,6 +1001,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._httpdate('')
assert_equal({}, h)
+
+ h = Date._httpdate(nil)
+ assert_equal({}, h)
+
+ h = Date._httpdate('Sat, 03 Feb 2001 04:05:06 GMT'.to_sym)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test__jisx0301
@@ -984,6 +1020,15 @@ class TestDateParse < Test::Unit::TestCase
h = Date._jisx0301('S63.02.03')
assert_equal([1988, 2, 3, nil, nil, nil, nil],
h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('H31.04.30')
+ assert_equal([2019, 4, 30, nil, nil, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('H31.05.01')
+ assert_equal([2019, 5, 1, nil, nil, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('R01.05.01')
+ assert_equal([2019, 5, 1, nil, nil, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
h = Date._jisx0301('H13.02.03T04:05:06')
assert_equal([2001, 2, 3, 4, 5, 6, nil],
@@ -998,8 +1043,54 @@ class TestDateParse < Test::Unit::TestCase
assert_equal([2001, 2, 3, 4, 5, 6, 3600],
h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('H31.04.30T04:05:06')
+ assert_equal([2019, 4, 30, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('H31.04.30T04:05:06,07')
+ assert_equal([2019, 4, 30, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('H31.04.30T04:05:06Z')
+ assert_equal([2019, 4, 30, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('H31.04.30T04:05:06.07+0100')
+ assert_equal([2019, 4, 30, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._jisx0301('H31.05.01T04:05:06')
+ assert_equal([2019, 5, 1, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('H31.05.01T04:05:06,07')
+ assert_equal([2019, 5, 1, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('H31.05.01T04:05:06Z')
+ assert_equal([2019, 5, 1, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('H31.05.01T04:05:06.07+0100')
+ assert_equal([2019, 5, 1, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._jisx0301('R01.05.01T04:05:06')
+ assert_equal([2019, 5, 1, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('R01.05.01T04:05:06,07')
+ assert_equal([2019, 5, 1, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('R01.05.01T04:05:06Z')
+ assert_equal([2019, 5, 1, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('R01.05.01T04:05:06.07+0100')
+ assert_equal([2019, 5, 1, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
h = Date._jisx0301('')
assert_equal({}, h)
+
+ h = Date._jisx0301(nil)
+ assert_equal({}, h)
+
+ h = Date._jisx0301('H13.02.03T04:05:06.07+0100'.to_sym)
+ assert_equal([2001, 2, 3, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test_iso8601
@@ -1083,9 +1174,33 @@ class TestDateParse < Test::Unit::TestCase
assert_equal(Date.new(2001,2,3), d)
assert_equal(Date::ITALY + 10, d.start)
+ d = Date.jisx0301('H31.04.30', Date::ITALY + 10)
+ assert_equal(Date.new(2019,4,30), d)
+ assert_equal(Date::ITALY + 10, d.start)
+
+ d = Date.jisx0301('H31.05.01', Date::ITALY + 10)
+ assert_equal(Date.new(2019,5,1), d)
+ assert_equal(Date::ITALY + 10, d.start)
+
+ d = Date.jisx0301('R01.05.01', Date::ITALY + 10)
+ assert_equal(Date.new(2019,5,1), d)
+ assert_equal(Date::ITALY + 10, d.start)
+
d = DateTime.jisx0301('H13.02.03T04:05:06+07:00', Date::ITALY + 10)
assert_equal(DateTime.new(2001,2,3,4,5,6,'+07:00'), d)
assert_equal(Date::ITALY + 10, d.start)
+
+ d = DateTime.jisx0301('H31.04.30T04:05:06+07:00', Date::ITALY + 10)
+ assert_equal(DateTime.new(2019,4,30,4,5,6,'+07:00'), d)
+ assert_equal(Date::ITALY + 10, d.start)
+
+ d = DateTime.jisx0301('H31.05.01T04:05:06+07:00', Date::ITALY + 10)
+ assert_equal(DateTime.new(2019,5,1,4,5,6,'+07:00'), d)
+ assert_equal(Date::ITALY + 10, d.start)
+
+ d = DateTime.jisx0301('R01.05.01T04:05:06+07:00', Date::ITALY + 10)
+ assert_equal(DateTime.new(2019,5,1,4,5,6,'+07:00'), d)
+ assert_equal(Date::ITALY + 10, d.start)
end
def test_given_string
@@ -1122,4 +1237,32 @@ class TestDateParse < Test::Unit::TestCase
assert_equal(s0, s)
end
+ def test_length_limit
+ assert_raise(ArgumentError) { Date._parse("1" * 1000) }
+ assert_raise(ArgumentError) { Date._iso8601("1" * 1000) }
+ assert_raise(ArgumentError) { Date._rfc3339("1" * 1000) }
+ assert_raise(ArgumentError) { Date._xmlschema("1" * 1000) }
+ assert_raise(ArgumentError) { Date._rfc2822("1" * 1000) }
+ assert_raise(ArgumentError) { Date._rfc822("1" * 1000) }
+ assert_raise(ArgumentError) { Date._jisx0301("1" * 1000) }
+
+ assert_raise(ArgumentError) { Date.parse("1" * 1000) }
+ assert_raise(ArgumentError) { Date.iso8601("1" * 1000) }
+ assert_raise(ArgumentError) { Date.rfc3339("1" * 1000) }
+ assert_raise(ArgumentError) { Date.xmlschema("1" * 1000) }
+ assert_raise(ArgumentError) { Date.rfc2822("1" * 1000) }
+ assert_raise(ArgumentError) { Date.rfc822("1" * 1000) }
+ assert_raise(ArgumentError) { Date.jisx0301("1" * 1000) }
+
+ assert_raise(ArgumentError) { DateTime.parse("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.iso8601("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.rfc3339("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.xmlschema("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.rfc2822("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.rfc822("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.jisx0301("1" * 1000) }
+
+ assert_raise(ArgumentError) { Date._parse("Jan " + "9" * 1000000) }
+ assert_raise(Timeout::Error) { Timeout.timeout(1) { Date._parse("Jan " + "9" * 1000000, limit: nil) } }
+ end
end
diff --git a/test/date/test_date_strftime.rb b/test/date/test_date_strftime.rb
index 1c0f9b11b4..dc237a909d 100644
--- a/test/date/test_date_strftime.rb
+++ b/test/date/test_date_strftime.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'date'
@@ -406,6 +406,8 @@ class TestDateStrftime < Test::Unit::TestCase
assert_equal('S64.01.07', Date.parse('1989-01-07').jisx0301)
assert_equal('H01.01.08', Date.parse('1989-01-08').jisx0301)
assert_equal('H18.09.01', Date.parse('2006-09-01').jisx0301)
+ assert_equal('H31.04.30', Date.parse('2019-04-30').jisx0301)
+ assert_equal('R01.05.01', Date.parse('2019-05-01').jisx0301)
%w(M06.01.01
M45.07.29
@@ -414,7 +416,10 @@ class TestDateStrftime < Test::Unit::TestCase
S01.12.25
S64.01.07
H01.01.08
- H18.09.01).each do |s|
+ H18.09.01
+ H31.04.30
+ R01.05.01
+ ).each do |s|
assert_equal(s, Date.parse(s).jisx0301)
end
diff --git a/test/date/test_date_strptime.rb b/test/date/test_date_strptime.rb
index 15fdc949f5..bf3002a24a 100644
--- a/test/date/test_date_strptime.rb
+++ b/test/date/test_date_strptime.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'date'
diff --git a/test/date/test_switch_hitter.rb b/test/date/test_switch_hitter.rb
index 931d736edf..4693cbd466 100644
--- a/test/date/test_switch_hitter.rb
+++ b/test/date/test_switch_hitter.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'date'
@@ -187,18 +187,18 @@ class TestSH < Test::Unit::TestCase
d = Date.jd(Rational(2451944))
assert_equal(2451944, d.jd)
d = Date.jd(2451944.5)
- assert_equal([2451944, 12], [d.jd, d.send('hour')])
+ assert_equal(2451944, d.jd)
d = Date.jd(Rational('2451944.5'))
- assert_equal([2451944, 12], [d.jd, d.send('hour')])
+ assert_equal(2451944, d.jd)
d = Date.civil(2001, 2, 3.0)
assert_equal([2001, 2, 3], [d.year, d.mon, d.mday])
d = Date.civil(2001, 2, Rational(3))
assert_equal([2001, 2, 3], [d.year, d.mon, d.mday])
d = Date.civil(2001, 2, 3.5)
- assert_equal([2001, 2, 3, 12], [d.year, d.mon, d.mday, d.send('hour')])
+ assert_equal([2001, 2, 3], [d.year, d.mon, d.mday])
d = Date.civil(2001, 2, Rational('3.5'))
- assert_equal([2001, 2, 3, 12], [d.year, d.mon, d.mday, d.send('hour')])
+ assert_equal([2001, 2, 3], [d.year, d.mon, d.mday])
d = Date.ordinal(2001, 2.0)
assert_equal([2001, 2], [d.year, d.yday])
@@ -266,10 +266,8 @@ class TestSH < Test::Unit::TestCase
end
def test_zone
- d = Date.new(2001, 2, 3)
- assert_equal(Encoding::US_ASCII, d.send(:zone).encoding)
d = DateTime.new(2001, 2, 3)
- assert_equal(Encoding::US_ASCII, d.send(:zone).encoding)
+ assert_equal(Encoding::US_ASCII, d.zone.encoding)
end
def test_to_s
@@ -534,7 +532,6 @@ class TestSH < Test::Unit::TestCase
s = "\x04\x03u:\x01\x04Date\x01\v\x04\x03[\x01\x02i\x03\xE8i%T"
d = Marshal.load(s)
assert_equal(Rational(4903887,2), d.ajd)
- assert_equal(0, d.send(:offset))
assert_equal(Date::GREGORIAN, d.start)
end
@@ -542,7 +539,6 @@ class TestSH < Test::Unit::TestCase
s = "\x04\x06u:\tDate\x0F\x04\x06[\ai\x03\xE8i%T"
d = Marshal.load(s)
assert_equal(Rational(4903887,2), d.ajd)
- assert_equal(0, d.send(:offset))
assert_equal(Date::GREGORIAN, d.start)
end
@@ -550,7 +546,6 @@ class TestSH < Test::Unit::TestCase
s = "\x04\bu:\tDateP\x04\b[\bo:\rRational\a:\x0F@numeratori\x03\xCF\xD3J:\x11@denominatori\ai\x00o:\x13Date::Infinity\x06:\a@di\xFA"
d = Marshal.load(s)
assert_equal(Rational(4903887,2), d.ajd)
- assert_equal(0, d.send(:offset))
assert_equal(Date::GREGORIAN, d.start)
s = "\x04\bu:\rDateTime`\x04\b[\bo:\rRational\a:\x0F@numeratorl+\b\xC9\xB0\x81\xBD\x02\x00:\x11@denominatori\x02\xC0\x12o;\x00\a;\x06i\b;\ai\ro:\x13Date::Infinity\x06:\a@di\xFA"
@@ -564,7 +559,6 @@ class TestSH < Test::Unit::TestCase
s = "\x04\bU:\tDate[\bU:\rRational[\ai\x03\xCF\xD3Ji\ai\x00o:\x13Date::Infinity\x06:\a@di\xFA"
d = Marshal.load(s)
assert_equal(Rational(4903887,2), d.ajd)
- assert_equal(Rational(0,24), d.send(:offset))
assert_equal(Date::GREGORIAN, d.start)
s = "\x04\bU:\rDateTime[\bU:\rRational[\al+\b\xC9\xB0\x81\xBD\x02\x00i\x02\xC0\x12U;\x06[\ai\bi\ro:\x13Date::Infinity\x06:\a@di\xFA"
@@ -577,29 +571,29 @@ class TestSH < Test::Unit::TestCase
def test_taint
h = Date._strptime('15:43+09:00', '%R%z')
assert_equal(false, h[:zone].tainted?)
- h = Date._strptime('15:43+09:00'.taint, '%R%z')
+ h = Date._strptime('15:43+09:00'.dup.taint, '%R%z')
assert_equal(true, h[:zone].tainted?)
h = Date._strptime('1;1/0', '%d')
assert_equal(false, h[:leftover].tainted?)
- h = Date._strptime('1;1/0'.taint, '%d')
+ h = Date._strptime('1;1/0'.dup.taint, '%d')
assert_equal(true, h[:leftover].tainted?)
h = Date._parse('15:43+09:00')
assert_equal(false, h[:zone].tainted?)
- h = Date._parse('15:43+09:00'.taint)
+ h = Date._parse('15:43+09:00'.dup.taint)
assert_equal(true, h[:zone].tainted?)
s = Date.today.strftime('new 105')
assert_equal(false, s.tainted?)
- s = Date.today.strftime('new 105'.taint)
+ s = Date.today.strftime('new 105'.dup.taint)
assert_equal(true, s.tainted?)
- s = Date.today.strftime("new \000 105".taint)
+ s = Date.today.strftime("new \000 105".dup.taint)
assert_equal(true, s.tainted?)
s = DateTime.now.strftime('super $record')
assert_equal(false, s.tainted?)
- s = DateTime.now.strftime('super $record'.taint)
+ s = DateTime.now.strftime('super $record'.dup.taint)
assert_equal(true, s.tainted?)
end
@@ -617,29 +611,29 @@ class TestSH < Test::Unit::TestCase
assert_equal(Encoding::US_ASCII, s.encoding) if s
end
- h = Date._strptime('15:43+09:00'.force_encoding('euc-jp'), '%R%z')
+ h = Date._strptime('15:43+09:00'.dup.force_encoding('euc-jp'), '%R%z')
assert_equal(Encoding::EUC_JP, h[:zone].encoding)
- h = Date._strptime('15:43+09:00'.force_encoding('ascii-8bit'), '%R%z')
+ h = Date._strptime('15:43+09:00'.dup.force_encoding('ascii-8bit'), '%R%z')
assert_equal(Encoding::ASCII_8BIT, h[:zone].encoding)
- h = Date._strptime('1;1/0'.force_encoding('euc-jp'), '%d')
+ h = Date._strptime('1;1/0'.dup.force_encoding('euc-jp'), '%d')
assert_equal(Encoding::EUC_JP, h[:leftover].encoding)
- h = Date._strptime('1;1/0'.force_encoding('ascii-8bit'), '%d')
+ h = Date._strptime('1;1/0'.dup.force_encoding('ascii-8bit'), '%d')
assert_equal(Encoding::ASCII_8BIT, h[:leftover].encoding)
- h = Date._parse('15:43+09:00'.force_encoding('euc-jp'))
+ h = Date._parse('15:43+09:00'.dup.force_encoding('euc-jp'))
assert_equal(Encoding::EUC_JP, h[:zone].encoding)
- h = Date._parse('15:43+09:00'.force_encoding('ascii-8bit'))
+ h = Date._parse('15:43+09:00'.dup.force_encoding('ascii-8bit'))
assert_equal(Encoding::ASCII_8BIT, h[:zone].encoding)
- s = Date.today.strftime('new 105'.force_encoding('euc-jp'))
+ s = Date.today.strftime('new 105'.dup.force_encoding('euc-jp'))
assert_equal(Encoding::EUC_JP, s.encoding)
- s = Date.today.strftime('new 105'.force_encoding('ascii-8bit'))
+ s = Date.today.strftime('new 105'.dup.force_encoding('ascii-8bit'))
assert_equal(Encoding::ASCII_8BIT, s.encoding)
- s = DateTime.now.strftime('super $record'.force_encoding('euc-jp'))
+ s = DateTime.now.strftime('super $record'.dup.force_encoding('euc-jp'))
assert_equal(Encoding::EUC_JP, s.encoding)
- s = DateTime.now.strftime('super $record'.force_encoding('ascii-8bit'))
+ s = DateTime.now.strftime('super $record'.dup.force_encoding('ascii-8bit'))
assert_equal(Encoding::ASCII_8BIT, s.encoding)
end
@@ -658,8 +652,7 @@ class TestSH < Test::Unit::TestCase
end
def test_base
- skip unless defined?(Date.test_all)
assert_equal(true, Date.test_all)
- end
+ end if defined?(Date.test_all)
end
diff --git a/test/dbm/test_dbm.rb b/test/dbm/test_dbm.rb
index d6211fcb31..d7cb5497fc 100644
--- a/test/dbm/test_dbm.rb
+++ b/test/dbm/test_dbm.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'tmpdir'
@@ -47,6 +47,8 @@ if defined? DBM
end
def test_delete_rdonly
+ skip("skipped because root can read anything") if Process.uid == 0
+
if /^CYGWIN_9/ !~ SYSTEM
assert_raise(DBMError) {
@dbm_rdonly.delete("foo")
@@ -405,7 +407,7 @@ if defined? DBM
assert_equal('foo', @dbm.delete(key) {|k| k.replace 'called block'; :blockval})
assert_equal(0, @dbm.size)
- key = 'no called block'
+ key = 'no called block'.dup
assert_equal(:blockval, @dbm.delete(key) {|k| k.replace 'called block'; :blockval})
assert_equal(0, @dbm.size)
end
@@ -623,9 +625,10 @@ if defined? DBM
end
def test_freeze
+ expected_error = defined?(FrozenError) ? FrozenError : RuntimeError
DBM.open("#{@tmproot}/a") {|d|
d.freeze
- assert_raise(RuntimeError) { d["k"] = "v" }
+ assert_raise(expected_error) { d["k"] = "v" }
}
end
end
diff --git a/test/digest/test_digest.rb b/test/digest/test_digest.rb
index abb6352e5a..4cae455076 100644
--- a/test/digest/test_digest.rb
+++ b/test/digest/test_digest.rb
@@ -264,6 +264,7 @@ module TestDigest
assert_nothing_raised {
t = Thread.start {
sleep 0.1
+ Thread.current.report_on_exception = false
Digest::Foo.new
}
Digest(:Foo).new
diff --git a/test/drb/drbtest.rb b/test/drb/drbtest.rb
index 40ef35ef2e..b3e857877e 100644
--- a/test/drb/drbtest.rb
+++ b/test/drb/drbtest.rb
@@ -7,32 +7,44 @@ require 'timeout'
module DRbTests
class DRbService
- @@manager = DRb::ExtServManager.new
@@ruby = [EnvUtil.rubybin]
@@ruby << "-d" if $DEBUG
def self.add_service_command(nm)
dir = File.dirname(File.expand_path(__FILE__))
+ if /ssl/ =~ nm && RUBY_PLATFORM =~ /solaris/i
+ @@ruby[1..-1] = "-dv"
+ end
DRb::ExtServManager.command[nm] = @@ruby + ["#{dir}/#{nm}"]
end
- %w(ut_drb.rb ut_array.rb ut_port.rb ut_large.rb ut_safe1.rb ut_eval.rb ut_eq.rb).each do |nm|
+ %w(ut_drb.rb ut_array.rb ut_port.rb ut_large.rb ut_safe1.rb ut_eq.rb).each do |nm|
add_service_command(nm)
end
- @server = @@server = DRb::DRbServer.new('druby://localhost:0', @@manager, {})
- @@manager.uri = @@server.uri
- def self.manager
- @@manager
+
+ def initialize
+ @manager = DRb::ExtServManager.new
+ start
+ @manager.uri = server.uri
end
- def self.server
- @server || @@server
+
+ def start
+ @server = DRb::DRbServer.new('druby://localhost:0', manager, {})
end
- def self.ext_service(name)
+
+ attr_reader :manager
+ attr_reader :server
+
+ def ext_service(name)
Timeout.timeout(100, RuntimeError) do
manager.service(name)
end
end
- def self.finish
- @server.instance_variable_get(:@grp).list.each {|th| th.join }
+
+ def finish
+ server.instance_variable_get(:@grp).list.each {|th| th.join }
+ server.stop_service
+ manager.instance_variable_get(:@queue)&.push(nil)
+ manager.instance_variable_get(:@thread)&.join
end
end
@@ -68,35 +80,42 @@ class XArray < Array
end
module DRbBase
+ def setup
+ @drb_service ||= DRbService.new
+ end
+
def setup_service(service_name)
@service_name = service_name
- @ext = DRbService.ext_service(@service_name)
+ @ext = @drb_service.ext_service(@service_name)
@there = @ext.front
end
def teardown
@ext.stop_service if defined?(@ext) && @ext
- DRbService.manager.unregist(@service_name)
- while (@there&&@there.to_s rescue nil)
- # nop
- end
- signal = /mswin|mingw/ =~ RUBY_PLATFORM ? :KILL : :TERM
- Thread.list.each {|th|
- if th.respond_to?(:pid) && th[:drb_service] == @service_name
- 10.times do
- begin
- Process.kill signal, th.pid
- break
- rescue Errno::ESRCH
- break
- rescue Errno::EPERM # on Windows
- sleep 0.1
- retry
+ if defined?(@service_name) && @service_name
+ @drb_service.manager.unregist(@service_name)
+ while (@there&&@there.to_s rescue nil)
+ # nop
+ end
+ signal = /mswin|mingw/ =~ RUBY_PLATFORM ? :KILL : :TERM
+ Thread.list.each {|th|
+ if th.respond_to?(:pid) && th[:drb_service] == @service_name
+ 10.times do
+ begin
+ Process.kill signal, th.pid
+ break
+ rescue Errno::ESRCH
+ break
+ rescue Errno::EPERM # on Windows
+ sleep 0.1
+ retry
+ end
end
+ th.join
end
- th.join
- end
- }
+ }
+ end
+ @drb_service&.finish
end
end
@@ -124,10 +143,10 @@ module DRbCore
ary = @there.to_a
assert_kind_of(DRb::DRbObject, ary)
- assert(@there.respond_to?(:to_a, true))
- assert(@there.respond_to?(:eval, true))
- assert(! @there.respond_to?(:eval, false))
- assert(! @there.respond_to?(:eval))
+ assert_respond_to(@there, [:to_a, true])
+ assert_respond_to(@there, [:eval, true])
+ assert_not_respond_to(@there, [:eval, false])
+ assert_not_respond_to(@there, :eval)
end
def test_01_02_loop
@@ -171,23 +190,23 @@ module DRbCore
def test_04
assert_respond_to(@there, 'sum')
- assert(!(@there.respond_to? "foobar"))
+ assert_not_respond_to(@there, "foobar")
end
def test_05_eq
a = @there.to_a[0]
b = @there.to_a[0]
- assert(a.object_id != b.object_id)
- assert(a == b)
+ assert_not_same(a, b)
assert_equal(a, b)
- assert(a == @there)
+ assert_equal(a, @there)
assert_equal(a.hash, b.hash)
assert_equal(a.hash, @there.hash)
- assert(a.eql?(b))
- assert(a.eql?(@there))
+ assert_operator(a, :eql?, b)
+ assert_operator(a, :eql?, @there)
end
def test_06_timeout
+ skip if RUBY_PLATFORM.include?("armv7l-linux")
ten = Onecky.new(10)
assert_raise(Timeout::Error) do
@there.do_timeout(ten)
diff --git a/test/drb/test_acl.rb b/test/drb/test_acl.rb
index c886ae2396..ea7b32e76f 100644
--- a/test/drb/test_acl.rb
+++ b/test/drb/test_acl.rb
@@ -48,58 +48,65 @@ class ACLEntryTest < Test::Unit::TestCase
a = ACL::ACLEntry.new("*")
b = ACL::ACLEntry.new("all")
@hostlist.each do |h|
- assert(a.match(h))
- assert(b.match(h))
+ assert_operator(a, :match, h)
+ assert_operator(b, :match, h)
end
end
def test_ip_v6
a = ACL::ACLEntry.new('::ffff:192.0.0.0/104')
- assert(! a.match(@hosts['localhost']))
- assert(a.match(@hosts['yum']))
- assert(a.match(@hosts['ipv6']))
- assert(! a.match(@hosts['too']))
+ assert_not_operator(a, :match, @hosts['localhost'])
+ assert_operator(a, :match, @hosts['yum'])
+ assert_operator(a, :match, @hosts['ipv6'])
+ assert_not_operator(a, :match, @hosts['too'])
end
def test_ip
a = ACL::ACLEntry.new('192.0.0.0/8')
- assert(! a.match(@hosts['localhost']))
- assert(a.match(@hosts['yum']))
+ assert_not_operator(a, :match, @hosts['localhost'])
+ assert_operator(a, :match, @hosts['yum'])
- a = ACL::ACLEntry.new('192.168.0.1/255.255.0.255')
- assert(! a.match(@hosts['localhost']))
- assert(! a.match(@hosts['yum']))
- assert(a.match(@hosts['x68k']))
+ a = ACL::ACLEntry.new('192.168.1.0/255.255.255.0')
+ assert_not_operator(a, :match, @hosts['localhost'])
+ assert_operator(a, :match, @hosts['yum'])
+ assert_operator(a, :match, @hosts['x68k'])
a = ACL::ACLEntry.new('192.168.1.0/24')
- assert(! a.match(@hosts['localhost']))
- assert(a.match(@hosts['yum']))
- assert(a.match(@hosts['x68k']))
+ assert_not_operator(a, :match, @hosts['localhost'])
+ assert_operator(a, :match, @hosts['yum'])
+ assert_operator(a, :match, @hosts['x68k'])
a = ACL::ACLEntry.new('92.0.0.0/8')
- assert(! a.match(@hosts['localhost']))
- assert(! a.match(@hosts['yum']))
- assert(! a.match(@hosts['x68k']))
+ assert_not_operator(a, :match, @hosts['localhost'])
+ assert_not_operator(a, :match, @hosts['yum'])
+ assert_not_operator(a, :match, @hosts['x68k'])
- a = ACL::ACLEntry.new('127.0.0.1/255.0.0.255')
- assert(a.match(@hosts['localhost']))
- assert(! a.match(@hosts['yum']))
- assert(! a.match(@hosts['x68k']))
+ a = ACL::ACLEntry.new('127.0.0.0/255.0.0.0')
+ assert_operator(a, :match, @hosts['localhost'])
+ assert_not_operator(a, :match, @hosts['yum'])
+ assert_not_operator(a, :match, @hosts['x68k'])
+
+ assert_raise(IPAddr::InvalidPrefixError) {
+ ACL::ACLEntry.new('192.168.0.0/33')
+ }
+ assert_raise(IPAddr::InvalidPrefixError) {
+ ACL::ACLEntry.new('192.168.0.0/255.255.0.255')
+ }
end
def test_name
a = ACL::ACLEntry.new('*.jp')
- assert(! a.match(@hosts['localhost']))
- assert(a.match(@hosts['yum']))
+ assert_not_operator(a, :match, @hosts['localhost'])
+ assert_operator(a, :match, @hosts['yum'])
a = ACL::ACLEntry.new('yum.*.jp')
- assert(a.match(@hosts['yum']))
- assert(! a.match(@hosts['lc630']))
+ assert_operator(a, :match, @hosts['yum'])
+ assert_not_operator(a, :match, @hosts['lc630'])
a = ACL::ACLEntry.new('*.macos.or.jp')
- assert(a.match(@hosts['yum']))
- assert(a.match(@hosts['lc630']))
- assert(! a.match(@hosts['lib30']))
+ assert_operator(a, :match, @hosts['yum'])
+ assert_operator(a, :match, @hosts['lc630'])
+ assert_not_operator(a, :match, @hosts['lib30'])
end
end
@@ -124,29 +131,31 @@ class ACLListTest < Test::Unit::TestCase
def test_all_1
a = build(%w(all))
@hostlist.each do |h|
- assert(a.match(h))
+ assert_operator(a, :match, h)
end
end
def test_all_2
a = build(%w(localhost 127.0.0.0/8 yum.* *))
@hostlist.each do |h|
- assert(a.match(h))
+ assert_operator(a, :match, h)
end
end
def test_1
- a = build(%w(192.0.0.1/255.0.0.255 yum.*.jp))
- assert(a.match(@hosts['yum']))
- assert(a.match(@hosts['x68k']))
- assert(! a.match(@hosts['lc630']))
+ a = build(%w(192.168.1.0/255.255.255.252 yum.*.jp))
+ assert_operator(a, :match, @hosts['x68k'])
+ assert_operator(a, :match, @hosts['lc630'])
+ assert_operator(a, :match, @hosts['lib30'])
+ assert_not_operator(a, :match, @hosts['ns00'])
+ assert_operator(a, :match, @hosts['yum'])
end
def test_2
a = build(%w(*.linux.or.jp))
- assert(!a.match(@hosts['yum']))
- assert(a.match(@hosts['x68k']))
- assert(!a.match(@hosts['lc630']))
+ assert_not_operator(a, :match, @hosts['yum'])
+ assert_operator(a, :match, @hosts['x68k'])
+ assert_not_operator(a, :match, @hosts['lc630'])
end
end
@@ -161,14 +170,14 @@ class ACLTest < Test::Unit::TestCase
def test_0
a = ACL.new
@hostlist.each do |h|
- assert(a.allow_addr?(h))
+ assert_operator(a, :allow_addr?, h)
end
end
def test_not_0
a = ACL.new([], ACL::ALLOW_DENY)
@hostlist.each do |h|
- assert(! a.allow_addr?(h))
+ assert_not_operator(a, :allow_addr?, h)
end
end
@@ -178,9 +187,9 @@ class ACLTest < Test::Unit::TestCase
allow x68k.*)
a = ACL.new(data)
- assert(a.allow_addr?(@hosts['x68k']))
- assert(a.allow_addr?(@hosts['localhost']))
- assert(! a.allow_addr?(@hosts['lc630']))
+ assert_operator(a, :allow_addr?, @hosts['x68k'])
+ assert_operator(a, :allow_addr?, @hosts['localhost'])
+ assert_not_operator(a, :allow_addr?, @hosts['lc630'])
end
def test_not_1
@@ -189,9 +198,9 @@ class ACLTest < Test::Unit::TestCase
allow x68k.*)
a = ACL.new(data, ACL::ALLOW_DENY)
- assert(!a.allow_addr?(@hosts['x68k']))
- assert(a.allow_addr?(@hosts['localhost']))
- assert(! a.allow_addr?(@hosts['lc630']))
+ assert_not_operator(a, :allow_addr?, @hosts['x68k'])
+ assert_operator(a, :allow_addr?, @hosts['localhost'])
+ assert_not_operator(a, :allow_addr?, @hosts['lc630'])
end
end
diff --git a/test/drb/test_drb.rb b/test/drb/test_drb.rb
index 681a26b606..4e8f5cb0ec 100644
--- a/test/drb/test_drb.rb
+++ b/test/drb/test_drb.rb
@@ -7,29 +7,14 @@ class TestDRbCore < Test::Unit::TestCase
include DRbCore
def setup
- setup_service 'ut_drb.rb'
- super
- end
-
- def teardown
super
- DRbService.finish
+ setup_service 'ut_drb.rb'
end
end
-class TestDRbYield < Test::Unit::TestCase
+module DRbYield
include DRbBase
- def setup
- setup_service 'ut_drb.rb'
- super
- end
-
- def teardown
- super
- DRbService.finish
- end
-
def test_01_one
@there.echo_yield_1([]) {|one|
assert_equal([], one)
@@ -121,15 +106,31 @@ class TestDRbYield < Test::Unit::TestCase
def test_06_taint
x = proc {}
- assert(! x.tainted?)
+ assert_not_predicate(x, :tainted?)
@there.echo_yield(x) {|o|
assert_equal(x, o)
- assert(! x.tainted?)
+ assert_not_predicate(x, :tainted?)
}
end
end
-class TestDRbRubyYield < TestDRbYield
+class TestDRbYield < Test::Unit::TestCase
+ include DRbYield
+
+ def setup
+ super
+ setup_service 'ut_drb.rb'
+ end
+end
+
+class TestDRbRubyYield < Test::Unit::TestCase
+ include DRbYield
+
+ def setup
+ @there = self
+ super
+ end
+
def echo_yield(*arg)
yield(*arg)
end
@@ -153,15 +154,11 @@ class TestDRbRubyYield < TestDRbYield
end
end
- def setup
- @there = self
- end
-
- def teardown
- end
end
-class TestDRbRuby18Yield < TestDRbRubyYield
+class TestDRbRuby18Yield < Test::Unit::TestCase
+ include DRbYield
+
class YieldTest18
def echo_yield(*arg, &proc)
proc.call(*arg)
@@ -188,6 +185,7 @@ class TestDRbRuby18Yield < TestDRbRubyYield
def setup
@there = YieldTest18.new
+ super
end
end
@@ -195,13 +193,8 @@ class TestDRbAry < Test::Unit::TestCase
include DRbAry
def setup
- setup_service 'ut_array.rb'
super
- end
-
- def teardown
- super
- DRbService.finish
+ setup_service 'ut_array.rb'
end
end
@@ -209,8 +202,8 @@ class TestDRbMServer < Test::Unit::TestCase
include DRbBase
def setup
- setup_service 'ut_drb.rb'
super
+ setup_service 'ut_drb.rb'
@server = (1..3).collect do |n|
DRb::DRbServer.new("druby://localhost:0", Onecky.new(n.to_s))
end
@@ -221,7 +214,6 @@ class TestDRbMServer < Test::Unit::TestCase
s.stop_service
end
super
- DRbService.finish
end
def test_01
@@ -229,63 +221,11 @@ class TestDRbMServer < Test::Unit::TestCase
end
end
-class TestDRbSafe1 < TestDRbAry
- def setup
- setup_service 'ut_safe1.rb'
- end
-
- def teardown
- super
- DRbService.finish
- end
-end
-
-class TestDRbEval # < Test::Unit::TestCase
+class TestDRbSafe1 < Test::Unit::TestCase
+ include DRbAry
def setup
super
- @ext = DRbService.ext_service('ut_eval.rb')
- @there = @ext.front
- end
-
- def teardown
- @ext.stop_service if @ext
- end
-
- def test_01_safe1_safe4_eval
- assert_raise(SecurityError) do
- @there.method_missing(:instance_eval, 'ENV.inspect')
- end
-
- assert_raise(SecurityError) do
- @there.method_missing(:send, :eval, 'ENV.inspect')
- end
-
- remote_class = @there.remote_class
-
- assert_raise(SecurityError) do
- remote_class.class_eval('ENV.inspect')
- end
-
- assert_raise(SecurityError) do
- remote_class.module_eval('ENV.inspect')
- end
-
- four = @there.four
- assert_equal(1, four.method_missing(:send, :eval, '1'))
-
- remote_class = four.remote_class
-
- assert_equal(1, remote_class.class_eval('1'))
-
- assert_equal(1, remote_class.module_eval('1'))
-
- assert_raise(SecurityError) do
- remote_class.class_eval('ENV = {}')
- end
-
- assert_raise(SecurityError) do
- remote_class.module_eval('ENV = {}')
- end
+ setup_service 'ut_safe1.rb'
end
end
@@ -293,13 +233,8 @@ class TestDRbLarge < Test::Unit::TestCase
include DRbBase
def setup
- setup_service 'ut_large.rb'
super
- end
-
- def teardown
- super
- DRbService.finish
+ setup_service 'ut_large.rb'
end
def test_01_large_ary
@@ -315,9 +250,9 @@ class TestDRbLarge < Test::Unit::TestCase
ary = ["Hello, World"] * 10240
assert_equal(10240, @there.size(ary))
assert_equal(ary[0..ary.length].inject(:+), @there.sum(ary))
- assert_raise (TypeError) {@there.multiply(ary)}
- assert_raise (TypeError) {@there.avg(ary)}
- assert_raise (TypeError) {@there.median(ary)}
+ assert_raise(TypeError) {@there.multiply(ary)}
+ assert_raise(TypeError) {@there.avg(ary)}
+ assert_raise(TypeError) {@there.median(ary)}
end
def test_03_large_ary
@@ -382,18 +317,13 @@ class TestBug4409 < Test::Unit::TestCase
include DRbBase
def setup
- setup_service 'ut_eq.rb'
super
- end
-
- def teardown
- super
- DRbService.finish
+ setup_service 'ut_eq.rb'
end
def test_bug4409
foo = @there.foo
- assert(@there.foo?(foo))
+ assert_operator(@there, :foo?, foo)
end
end
diff --git a/test/drb/test_drbssl.rb b/test/drb/test_drbssl.rb
index 1f1495356e..bf30ba5513 100644
--- a/test/drb/test_drbssl.rb
+++ b/test/drb/test_drbssl.rb
@@ -15,37 +15,36 @@ class DRbSSLService < DRbService
%w(ut_drb_drbssl.rb ut_array_drbssl.rb).each do |nm|
add_service_command(nm)
end
- config = Hash.new
-
- config[:SSLVerifyMode] = OpenSSL::SSL::VERIFY_PEER
- config[:SSLVerifyCallback] = lambda{ |ok,x509_store|
- true
- }
- begin
- data = open("sample.key"){|io| io.read }
- config[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(data)
- data = open("sample.crt"){|io| io.read }
- config[:SSLCertificate] = OpenSSL::X509::Certificate.new(data)
- rescue
- # $stderr.puts "Switching to use self-signed certificate"
- config[:SSLCertName] =
- [ ["C","JP"], ["O","Foo.DRuby.Org"], ["CN", "Sample"] ]
- end
- uri = ARGV.shift if $0 == __FILE__
- @server = DRb::DRbServer.new(uri || 'drbssl://:0', self.manager, config)
+ def start
+ config = Hash.new
+
+ config[:SSLVerifyMode] = OpenSSL::SSL::VERIFY_PEER
+ config[:SSLVerifyCallback] = lambda{ |ok,x509_store|
+ true
+ }
+ begin
+ data = open("sample.key"){|io| io.read }
+ config[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(data)
+ data = open("sample.crt"){|io| io.read }
+ config[:SSLCertificate] = OpenSSL::X509::Certificate.new(data)
+ rescue
+ # $stderr.puts "Switching to use self-signed certificate"
+ config[:SSLCertName] =
+ [ ["C","JP"], ["O","Foo.DRuby.Org"], ["CN", "Sample"] ]
+ end
+
+ @server = DRb::DRbServer.new('drbssl://:0', manager, config)
+ end
end
class TestDRbSSLCore < Test::Unit::TestCase
include DRbCore
def setup
- setup_service 'ut_drb_drbssl.rb'
- super
- end
-
- def teardown
+ skip 'FIXME: BUG in OpenSSL on Solaris' if RUBY_PLATFORM =~ /solaris/i
+ @drb_service = DRbSSLService.new
super
- DRbService.finish
+ setup_service 'ut_drb_drbssl.rb'
end
def test_02_unknown
@@ -61,13 +60,10 @@ end
class TestDRbSSLAry < Test::Unit::TestCase
include DRbAry
def setup
- setup_service 'ut_array_drbssl.rb'
- super
- end
-
- def teardown
+ skip 'FIXME: BUG in OpenSSL on Solaris' if RUBY_PLATFORM =~ /solaris/i
+ @drb_service = DRbSSLService.new
super
- DRbService.finish
+ setup_service 'ut_array_drbssl.rb'
end
end
diff --git a/test/drb/test_drbunix.rb b/test/drb/test_drbunix.rb
index 7ece2453a9..95b3c3ca91 100644
--- a/test/drb/test_drbunix.rb
+++ b/test/drb/test_drbunix.rb
@@ -16,20 +16,17 @@ class DRbUNIXService < DRbService
add_service_command(nm)
end
- uri = ARGV.shift if $0 == __FILE__
- @server = DRb::DRbServer.new(uri || 'drbunix:', self.manager, {})
+ def start
+ @server = DRb::DRbServer.new('drbunix:', manager, {})
+ end
end
class TestDRbUNIXCore < Test::Unit::TestCase
include DRbCore
def setup
- setup_service 'ut_drb_drbunix.rb'
- super
- end
-
- def teardown
+ @drb_service = DRbUNIXService.new
super
- DRbService.finish
+ setup_service 'ut_drb_drbunix.rb'
end
def test_02_unknown
@@ -40,17 +37,20 @@ class TestDRbUNIXCore < Test::Unit::TestCase
def test_05_eq
end
+
+ def test_bad_uri
+ assert_raise(DRb::DRbBadURI) do
+ DRb::DRbServer.new("badfile\n""drbunix:")
+ end
+ end
end
class TestDRbUNIXAry < Test::Unit::TestCase
include DRbAry
def setup
- setup_service 'ut_array_drbunix.rb'
+ @drb_service = DRbUNIXService.new
super
- end
- def teardown
- super
- DRbService.finish
+ setup_service 'ut_array_drbunix.rb'
end
end
diff --git a/test/drb/ut_eval.rb b/test/drb/ut_eval.rb
deleted file mode 100644
index 0a81b40b0e..0000000000
--- a/test/drb/ut_eval.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: false
-require 'drb/drb'
-require 'drb/extserv'
-
-module DRbTests
-
-class EvalAttack
- def initialize
- @four = DRb::DRbServer.new('druby://localhost:0', self, {:safe_level => 4})
- end
-
- def four
- DRbObject.new_with_uri(@four.uri)
- end
-
- def remote_class
- DRbObject.new(self.class)
- end
-end
-
-end
-
-
-if __FILE__ == $0
- def ARGV.shift
- it = super()
- raise "usage: #{$0} <uri> <name>" unless it
- it
- end
-
- $SAFE = 1
-
- DRb.start_service('druby://localhost:0', DRbTests::EvalAttack.new, {:safe_level => 2})
- es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
- DRb.thread.join
- es.stop_service if es.alive?
-end
diff --git a/test/dtrace/helper.rb b/test/dtrace/helper.rb
index 539cce9d6f..023d1165b4 100644
--- a/test/dtrace/helper.rb
+++ b/test/dtrace/helper.rb
@@ -10,6 +10,22 @@ elsif (sudo = ENV["SUDO"]) and !sudo.empty? and (`#{sudo} echo ok` rescue false)
else
ok = false
end
+
+impl = :dtrace
+
+# GNU/Linux distros with Systemtap support allows unprivileged users
+# in the stapusr and statdev groups to work.
+if RUBY_PLATFORM =~ /linux/
+ impl = :stap
+ begin
+ require 'etc'
+ login = Etc.getlogin
+ ok = Etc.getgrnam('stapusr').mem.include?(login) &&
+ Etc.getgrnam('stapdev').mem.include?(login)
+ rescue LoadError, ArgumentError
+ end unless ok
+end
+
if ok
case RUBY_PLATFORM
when /darwin/i
@@ -20,7 +36,24 @@ if ok
end
end
end
-ok &= (`dtrace -V` rescue false)
+
+# use miniruby to reduce the amount of trace data we don't care about
+rubybin = "miniruby#{RbConfig::CONFIG["EXEEXT"]}"
+rubybin = File.join(File.dirname(EnvUtil.rubybin), rubybin)
+rubybin = EnvUtil.rubybin unless File.executable?(rubybin)
+
+# make sure ruby was built with --enable-dtrace and we can run
+# dtrace(1) or stap(1):
+cmd = "#{rubybin} --disable=gems -eexit"
+case impl
+when :dtrace; cmd = %W(dtrace -l -n ruby$target:::gc-sweep-end -c #{cmd})
+when :stap; cmd = %W(stap -l process.mark("gc__sweep__end") -c #{cmd})
+else
+ warn "don't know how to check if built with #{impl} support"
+ cmd = false
+end
+ok &= system(*cmd, err: IO::NULL, out: IO::NULL) if cmd
+
module DTrace
class TestCase < Test::Unit::TestCase
INCLUDE = File.expand_path('..', File.dirname(__FILE__))
@@ -40,16 +73,52 @@ module DTrace
end
end
+ # only handles simple cases, use a Hash for d_program
+ # if there are more complex cases
+ def dtrace2systemtap(d_program)
+ translate = lambda do |str|
+ # dtrace starts args with '0', systemtap with '1' and prefixes '$'
+ str = str.gsub(/\barg(\d+)/) { "$arg#{$1.to_i + 1}" }
+ # simple function mappings:
+ str.gsub!(/\bcopyinstr\b/, 'user_string')
+ str.gsub!(/\bstrstr\b/, 'isinstr')
+ str
+ end
+ out = ''
+ cond = nil
+ d_program.split(/^/).each do |l|
+ case l
+ when /\bruby\$target:::([a-z-]+)/
+ name = $1.gsub(/-/, '__')
+ out << %Q{probe process.mark("#{name}")\n}
+ when %r{/(.+)/}
+ cond = translate.call($1)
+ when "{\n"
+ out << l
+ out << "if (#{cond}) {\n" if cond
+ when "}\n"
+ out << "}\n" if cond
+ out << l
+ else
+ out << translate.call(l)
+ end
+ end
+ out
+ end
+
DTRACE_CMD ||= %w[dtrace]
READ_PROBES ||= proc do |cmd|
IO.popen(cmd, err: [:child, :out], &:readlines)
end
- exeext = Regexp.quote(RbConfig::CONFIG["EXEEXT"])
- RUBYBIN = EnvUtil.rubybin.sub(/\/ruby-runner(?=#{exeext}\z)/, '/miniruby')
-
def trap_probe d_program, ruby_program
+ if Hash === d_program
+ d_program = d_program[IMPL] or
+ skip "#{d_program} not implemented for #{IMPL}"
+ elsif String === d_program && IMPL == :stap
+ d_program = dtrace2systemtap(d_program)
+ end
d = Tempfile.new(%w'probe .d')
d.write d_program
d.flush
@@ -60,8 +129,12 @@ module DTrace
d_path = d.path
rb_path = rb.path
-
- cmd = [*DTRACE_CMD, "-q", "-s", d_path, "-c", "#{RUBYBIN} -I#{INCLUDE} #{rb_path}"]
+ cmd = "#{RUBYBIN} --disable=gems -I#{INCLUDE} #{rb_path}"
+ if IMPL == :stap
+ cmd = %W(stap #{d_path} -c #{cmd})
+ else
+ cmd = [*DTRACE_CMD, "-q", "-s", d_path, "-c", cmd ]
+ end
if sudo = @@sudo
[RbConfig::CONFIG["LIBPATHENV"], "RUBY", "RUBYOPT"].each do |name|
if name and val = ENV[name]
@@ -80,4 +153,6 @@ end if ok
if ok
DTrace::TestCase.class_variable_set(:@@sudo, sudo)
+ DTrace::TestCase.const_set(:IMPL, impl)
+ DTrace::TestCase.const_set(:RUBYBIN, rubybin)
end
diff --git a/test/dtrace/test_array_create.rb b/test/dtrace/test_array_create.rb
index 44d4657b61..1bf20085ba 100644
--- a/test/dtrace/test_array_create.rb
+++ b/test/dtrace/test_array_create.rb
@@ -14,11 +14,11 @@ module DTrace
end
def test_many_lit
- trap_probe(probe, '[1,2,3,4]') { |_,rbfile,saw|
- saw = saw.map(&:split).find_all { |num, file, line|
+ trap_probe(probe, '[1,2,3,4]') { |_,rbfile,orig|
+ saw = orig.map(&:split).find_all { |num, file, line|
file == rbfile && num == '4' && line == '1'
}
- assert_operator saw.length, :>, 0
+ assert_operator saw.length, :>, 0, orig
}
end
@@ -26,7 +26,7 @@ module DTrace
def probe type = 'array'
<<-eoprobe
ruby$target:::#{type}-create
-/arg1/
+/arg1 && arg2/
{
printf("%d %s %d\\n", arg0, copyinstr(arg1), arg2);
}
diff --git a/test/dtrace/test_function_entry.rb b/test/dtrace/test_function_entry.rb
index fc07ccc455..8050f91402 100644
--- a/test/dtrace/test_function_entry.rb
+++ b/test/dtrace/test_function_entry.rb
@@ -17,8 +17,8 @@ ruby$target:::method-entry
row.first == 'Foo' && row[1] == 'foo'
}
- assert_equal 10, foo_calls.length
- line = '2'
+ assert_equal 10, foo_calls.length, probes
+ line = '3'
foo_calls.each { |f| assert_equal line, f[3] }
foo_calls.each { |f| assert_equal rb_file, f[2] }
}
@@ -38,8 +38,8 @@ ruby$target:::method-return
row.first == 'Foo' && row[1] == 'foo'
}
- assert_equal 10, foo_calls.length
- line = '2'
+ assert_equal 10, foo_calls.length, probes.inspect
+ line = '3'
foo_calls.each { |f| assert_equal line, f[3] }
foo_calls.each { |f| assert_equal rb_file, f[2] }
}
@@ -77,6 +77,7 @@ ruby$target:::method-return
private
def ruby_program
<<-eoruby
+ TracePoint.new{}.__enable(nil, nil)
class Foo
def foo; end
end
diff --git a/test/dtrace/test_hash_create.rb b/test/dtrace/test_hash_create.rb
index 83a4d0062c..603ee21872 100644
--- a/test/dtrace/test_hash_create.rb
+++ b/test/dtrace/test_hash_create.rb
@@ -22,11 +22,11 @@ module DTrace
end
def test_hash_lit_elements
- trap_probe(probe, '{ :foo => :bar }') { |_,rbfile,saw|
- saw = saw.map(&:split).find_all { |num, file, line|
+ trap_probe(probe, '{ :foo => :bar }') { |_,rbfile,orig|
+ saw = orig.map(&:split).find_all { |num, file, line|
file == rbfile && num == '2'
}
- assert_operator saw.length, :>, 0
+ assert_operator saw.length, :>, 0, orig
}
end
diff --git a/test/dtrace/test_method_cache.rb b/test/dtrace/test_method_cache.rb
index a101b5ddec..88b927464a 100644
--- a/test/dtrace/test_method_cache.rb
+++ b/test/dtrace/test_method_cache.rb
@@ -19,7 +19,7 @@ module DTrace
def probe
<<-eoprobe
ruby$target:::method-cache-clear
-/arg1/
+/arg1 && arg2/
{
printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
}
diff --git a/test/dtrace/test_singleton_function.rb b/test/dtrace/test_singleton_function.rb
index 3698a02c93..11fe80599d 100644
--- a/test/dtrace/test_singleton_function.rb
+++ b/test/dtrace/test_singleton_function.rb
@@ -17,8 +17,8 @@ ruby$target:::method-entry
row.first == 'Foo' && row[1] == 'foo'
}
- assert_equal 10, foo_calls.length
- line = '2'
+ assert_equal 10, foo_calls.length, probes.inspect
+ line = '3'
foo_calls.each { |f| assert_equal line, f[3] }
foo_calls.each { |f| assert_equal rb_file, f[2] }
}
@@ -37,8 +37,8 @@ ruby$target:::method-return
row.first == 'Foo' && row[1] == 'foo'
}
- assert_equal 10, foo_calls.length
- line = '2'
+ assert_equal 10, foo_calls.length, probes.inspect
+ line = '3'
foo_calls.each { |f| assert_equal line, f[3] }
foo_calls.each { |f| assert_equal rb_file, f[2] }
}
@@ -46,6 +46,7 @@ ruby$target:::method-return
def ruby_program
<<-eoruby
+ TracePoint.new{}.__enable(nil, nil)
class Foo
def self.foo; end
end
diff --git a/test/dtrace/test_string.rb b/test/dtrace/test_string.rb
index 407280b1fc..a72f989f63 100644
--- a/test/dtrace/test_string.rb
+++ b/test/dtrace/test_string.rb
@@ -4,11 +4,11 @@ require_relative 'helper'
module DTrace
class TestStringProbes < TestCase
def test_object_create_start_string_lit
- trap_probe(probe, '"omglolwutbbq"') { |_,rbfile,saw|
- saw = saw.map(&:split).find_all { |klass, file, line, len|
+ trap_probe(probe, '"omglolwutbbq"') { |_,rbfile,orig|
+ saw = orig.map(&:split).find_all { |klass, file, line, len|
file == rbfile && len == '12' && line == '1'
}
- assert_equal(%w{ String }, saw.map(&:first))
+ assert_equal(%w{ String }, saw.map(&:first), orig.inspect)
assert_equal([rbfile], saw.map { |line| line[1] })
assert_equal(['1'], saw.map { |line| line[2] })
}
diff --git a/test/erb/test_erb.rb b/test/erb/test_erb.rb
index 6a07f9c5cb..1fec46d66c 100644
--- a/test/erb/test_erb.rb
+++ b/test/erb/test_erb.rb
@@ -2,6 +2,7 @@
# frozen_string_literal: false
require 'test/unit'
require 'erb'
+require 'stringio'
class TestERB < Test::Unit::TestCase
class MyError < RuntimeError ; end
@@ -23,16 +24,22 @@ class TestERB < Test::Unit::TestCase
assert_match(/\Atest filename:1\b/, e.backtrace[0])
end
+ # [deprecated] This will be removed at Ruby 2.7
def test_without_filename_with_safe_level
- erb = ERB.new("<% raise ::TestERB::MyError %>", 1)
+ erb = EnvUtil.suppress_warning do
+ ERB.new("<% raise ::TestERB::MyError %>", 1)
+ end
e = assert_raise(MyError) {
erb.result
}
assert_match(/\A\(erb\):1\b/, e.backtrace[0])
end
+ # [deprecated] This will be removed at Ruby 2.7
def test_with_filename_and_safe_level
- erb = ERB.new("<% raise ::TestERB::MyError %>", 1)
+ erb = EnvUtil.suppress_warning do
+ ERB.new("<% raise ::TestERB::MyError %>", 1)
+ end
erb.filename = "test filename"
e = assert_raise(MyError) {
erb.result
@@ -86,10 +93,17 @@ class TestERBCore < Test::Unit::TestCase
@erb = ERB
end
+ def test_version
+ assert_equal(String, @erb.version.class)
+ end
+
def test_core
- _test_core(nil)
- _test_core(0)
- _test_core(1)
+ # [deprecated] Fix initializer at Ruby 2.7
+ EnvUtil.suppress_warning do
+ _test_core(nil)
+ _test_core(0)
+ _test_core(1)
+ end
end
def _test_core(safe)
@@ -197,6 +211,65 @@ EOS
assert_equal(ans, erb.result)
end
+ def test_trim_line1_with_carriage_return
+ erb = @erb.new("<% 3.times do %>\r\nline\r\n<% end %>\r\n", trim_mode: '>')
+ assert_equal("line\r\n" * 3, erb.result)
+
+ erb = @erb.new("<% 3.times do %>\r\nline\r\n<% end %>\r\n", trim_mode: '%>')
+ assert_equal("line\r\n" * 3, erb.result)
+ end
+
+ def test_trim_line2_with_carriage_return
+ erb = @erb.new("<% 3.times do %>\r\nline\r\n<% end %>\r\n", trim_mode: '<>')
+ assert_equal("line\r\n" * 3, erb.result)
+
+ erb = @erb.new("<% 3.times do %>\r\nline\r\n<% end %>\r\n", trim_mode: '%<>')
+ assert_equal("line\r\n" * 3, erb.result)
+ end
+
+ def test_explicit_trim_line_with_carriage_return
+ erb = @erb.new("<%- 3.times do -%>\r\nline\r\n<%- end -%>\r\n", trim_mode: '-')
+ assert_equal("line\r\n" * 3, erb.result)
+
+ erb = @erb.new("<%- 3.times do -%>\r\nline\r\n<%- end -%>\r\n", trim_mode: '%-')
+ assert_equal("line\r\n" * 3, erb.result)
+ end
+
+ def test_invalid_trim_mode
+ assert_warning(/#{__FILE__}:#{__LINE__ + 1}/) do
+ @erb.new("", trim_mode: 'abc-def')
+ end
+
+ assert_warning(/Invalid ERB trim mode/) do
+ @erb.new("", trim_mode: 'abc-def')
+ end
+
+ assert_warning(/Invalid ERB trim mode/) do
+ @erb.new("", trim_mode: '%<')
+ end
+
+ assert_warning(/Invalid ERB trim mode/) do
+ @erb.new("", trim_mode: '%<>-')
+ end
+
+ assert_warning(/Invalid ERB trim mode/) do
+ @erb.new("", trim_mode: 3)
+ end
+ end
+
+ def test_run
+ out = StringIO.new
+ orig, $stdout = $stdout, out
+
+ num = 3
+ @erb.new('<%= num * 3 %>').run(binding)
+
+ $stdout = orig
+ out.rewind
+ assert_equal('9', out.read)
+ return unless num # to remove warning
+ end
+
class Foo; end
def test_def_class
@@ -217,26 +290,26 @@ EOS
%n = 1
<%= n%>
EOS
- assert_equal("1\n", ERB.new(src, nil, '%').result(binding))
+ assert_equal("1\n", ERB.new(src, trim_mode: '%').result(binding))
src = <<EOS
<%
%>
EOS
ans = "\n"
- assert_equal(ans, ERB.new(src, nil, '%').result(binding))
+ assert_equal(ans, ERB.new(src, trim_mode: '%').result(binding))
src = "<%\n%>"
# ans = "\n"
ans = ""
- assert_equal(ans, ERB.new(src, nil, '%').result(binding))
+ assert_equal(ans, ERB.new(src, trim_mode: '%').result(binding))
src = <<EOS
<%
n = 1
%><%= n%>
EOS
- assert_equal("1\n", ERB.new(src, nil, '%').result(binding))
+ assert_equal("1\n", ERB.new(src, trim_mode: '%').result(binding))
src = <<EOS
%n = 1
@@ -252,7 +325,7 @@ EOS
% %%><%1
%%
EOS
- assert_equal(ans, ERB.new(src, nil, '%').result(binding))
+ assert_equal(ans, ERB.new(src, trim_mode: '%').result(binding))
end
def test_def_erb_method
@@ -296,6 +369,12 @@ EOS
assert_match(/\Atest fname:1\b/, e.backtrace[0])
end
+ def test_def_module
+ klass = Class.new
+ klass.include ERB.new('<%= val %>').def_module('render(val)')
+ assert_equal('1', klass.new.render(1))
+ end
+
def test_escape
src = <<EOS
1.<%% : <%="<%%"%>
@@ -330,7 +409,7 @@ foo
%% print 'foo'
EOS
- assert_equal(ans, ERB.new(src, nil, '%').result)
+ assert_equal(ans, ERB.new(src, trim_mode: '%').result)
end
def test_keep_lineno
@@ -341,7 +420,7 @@ Hello,\s
% raise("lineno")
EOS
- erb = ERB.new(src, nil, '%')
+ erb = ERB.new(src, trim_mode: '%')
e = assert_raise(RuntimeError) {
erb.result
}
@@ -359,7 +438,7 @@ EOS
%>Hello,\s
World%>
EOS
- assert_equal(ans, ERB.new(src, nil, '>').result)
+ assert_equal(ans, ERB.new(src, trim_mode: '>').result)
ans = <<EOS
%>
@@ -367,7 +446,7 @@ Hello,\s
World%>
EOS
- assert_equal(ans, ERB.new(src, nil, '<>').result)
+ assert_equal(ans, ERB.new(src, trim_mode: '<>').result)
ans = <<EOS
%>
@@ -392,13 +471,13 @@ EOS
}
assert_match(/\A\(erb\):5\b/, e.backtrace[0].to_s)
- erb = ERB.new(src, nil, '>')
+ erb = ERB.new(src, trim_mode: '>')
e = assert_raise(RuntimeError) {
erb.result
}
assert_match(/\A\(erb\):5\b/, e.backtrace[0].to_s)
- erb = ERB.new(src, nil, '<>')
+ erb = ERB.new(src, trim_mode: '<>')
e = assert_raise(RuntimeError) {
erb.result
}
@@ -412,13 +491,13 @@ EOS
<% raise("lineno") %>
EOS
- erb = ERB.new(src, nil, '-')
+ erb = ERB.new(src, trim_mode: '-')
e = assert_raise(RuntimeError) {
erb.result
}
assert_match(/\A\(erb\):5\b/, e.backtrace[0].to_s)
- erb = ERB.new(src, nil, '%-')
+ erb = ERB.new(src, trim_mode: '%-')
e = assert_raise(RuntimeError) {
erb.result
}
@@ -454,8 +533,8 @@ NotSkip NotSkip
* WORLD
KeepNewLine \s
EOS
- assert_equal(ans, ERB.new(src, nil, '-').result)
- assert_equal(ans, ERB.new(src, nil, '-%').result)
+ assert_equal(ans, ERB.new(src, trim_mode: '-').result)
+ assert_equal(ans, ERB.new(src, trim_mode: '-%').result)
end
def test_url_encode
@@ -464,10 +543,14 @@ EOS
assert_equal("%A5%B5%A5%F3%A5%D7%A5%EB",
ERB::Util.url_encode("\xA5\xB5\xA5\xF3\xA5\xD7\xA5\xEB".force_encoding("EUC-JP")))
+
+ assert_equal("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~",
+ ERB::Util.url_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"),
+ "should not escape any unreserved characters, as per RFC3986 Section 2.3")
end
def test_percent_after_etag
- assert_equal("1%", @erb.new("<%= 1 %>%", nil, "%").result)
+ assert_equal("1%", @erb.new("<%= 1 %>%", trim_mode: "%").result)
end
def test_token_extension
@@ -536,6 +619,68 @@ EOS
assert_equal(flag, erb.result)
end
end
+
+ def test_result_with_hash
+ erb = @erb.new("<%= foo %>")
+ assert_equal("1", erb.result_with_hash(foo: "1"))
+ end
+
+ def test_result_with_hash_does_not_use_caller_local_variables
+ erb = @erb.new("<%= foo %>")
+ foo = 1
+ assert_raise(NameError) { erb.result_with_hash({}) }
+ assert_equal("1", erb.result_with_hash(foo: foo))
+ end
+
+ def test_result_with_hash_does_not_modify_caller_binding
+ erb = @erb.new("<%= foo %>")
+ erb.result_with_hash(foo: "1")
+ assert_equal(false, binding.local_variable_defined?(:foo))
+ end
+
+ def test_result_with_hash_does_not_modify_toplevel_binding
+ erb = @erb.new("<%= foo %>")
+ erb.result_with_hash(foo: "1")
+ assert_equal(false, TOPLEVEL_BINDING.local_variable_defined?(:foo))
+ TOPLEVEL_BINDING.eval 'template2 = "two"'
+ erb = @erb.new("<%= template2 %>")
+ erb.result_with_hash(template2: "TWO")
+ assert_equal "two", TOPLEVEL_BINDING.local_variable_get("template2")
+ end
+
+ # This depends on the behavior that #local_variable_set raises TypeError by invalid key.
+ def test_result_with_hash_with_invalid_keys_raises_type_error
+ erb = @erb.new("<%= 1 %>")
+ assert_raise(TypeError) { erb.result_with_hash({ 1 => "1" }) }
+ end
+
+ # Bug#14243
+ def test_half_working_comment_backward_compatibility
+ assert_nothing_raised do
+ @erb.new("<% # comment %>\n").result
+ end
+ end
+
+ # These interfaces will be removed at Ruby 2.7.
+ def test_deprecated_interface_warnings
+ [nil, 0, 1, 2].each do |safe|
+ assert_warning(/2nd argument of ERB.new is deprecated/) do
+ ERB.new('', safe)
+ end
+ end
+
+ [nil, '', '%', '%<>'].each do |trim|
+ assert_warning(/3rd argument of ERB.new is deprecated/) do
+ ERB.new('', nil, trim)
+ end
+ end
+
+ [nil, '_erbout', '_hamlout'].each do |eoutvar|
+ assert_warning(/4th argument of ERB.new is deprecated/) do
+ ERB.new('', nil, nil, eoutvar)
+ end
+ end
+ end
end
class TestERBCoreWOStrScan < TestERBCore
diff --git a/test/erb/test_erb_command.rb b/test/erb/test_erb_command.rb
index 406b14cdb3..ed13c29c96 100644
--- a/test/erb/test_erb_command.rb
+++ b/test/erb/test_erb_command.rb
@@ -9,4 +9,22 @@ class TestErbCommand < Test::Unit::TestCase
"var=hoge"],
"<%=var%>", ["hoge"])
end
+
+ def test_template_file_encoding
+ assert_in_out_err(["-w",
+ File.expand_path("../../../bin/erb", __FILE__)],
+ "<%=''.encoding.to_s%>", ["UTF-8"])
+ end
+
+ # These interfaces will be removed at Ruby 2.7.
+ def test_deprecated_option
+ warnings = [
+ "warning: -S option of erb command is deprecated. Please do not use this.",
+ /\n.+\/bin\/erb:\d+: warning: Passing safe_level with the 2nd argument of ERB\.new is deprecated\. Do not use it, and specify other arguments as keyword arguments\.\n/,
+ ]
+ assert_in_out_err(["-w",
+ File.expand_path("../../../bin/erb", __FILE__),
+ "-S", "0"],
+ "hoge", ["hoge"], warnings)
+ end
end
diff --git a/test/erb/test_erb_m17n.rb b/test/erb/test_erb_m17n.rb
index a7840c9605..2fd9e700f3 100644
--- a/test/erb/test_erb_m17n.rb
+++ b/test/erb/test_erb_m17n.rb
@@ -3,7 +3,7 @@
require 'test/unit'
require 'erb'
-class TestERB < Test::Unit::TestCase
+class TestERBEncoding < Test::Unit::TestCase
def test_result_encoding
erb = ERB.new("hello")
assert_equal __ENCODING__, erb.result.encoding
diff --git a/test/etc/test_etc.rb b/test/etc/test_etc.rb
index ec9083eb16..365c27021c 100644
--- a/test/etc/test_etc.rb
+++ b/test/etc/test_etc.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require "test/unit"
require "etc"
@@ -97,12 +97,12 @@ class TestEtc < Test::Unit::TestCase
end
def test_getgrnam
- groups = {}
+ groups = Hash.new {[]}
Etc.group do |s|
- groups[s.name] ||= s.gid unless /\A\+/ =~ s.name
+ groups[s.name] |= [s.gid] unless /\A\+/ =~ s.name
end
groups.each_pair do |n, s|
- assert_equal(s, Etc.getgrnam(n).gid)
+ assert_include(s, Etc.getgrnam(n).gid)
end
end
diff --git a/test/excludes/_appveyor/TestArray.rb b/test/excludes/_appveyor/TestArray.rb
new file mode 100644
index 0000000000..7d03833f07
--- /dev/null
+++ b/test/excludes/_appveyor/TestArray.rb
@@ -0,0 +1,7 @@
+# https://ci.appveyor.com/project/ruby/ruby/builds/20339189/job/ltdpffep976xtj85
+# `test_push_over_ary_max': failed to allocate memory (NoMemoryError)
+exclude(:test_push_over_ary_max, 'Sometimes AppVeyor has insufficient memory to run this test')
+# https://ci.appveyor.com/project/ruby/ruby/builds/20728419/job/o73q9fy1ojfibg5v
+exclude(:test_unshift_over_ary_max, 'Sometimes AppVeyor has insufficient memory to run this test')
+# https://ci.appveyor.com/project/ruby/ruby/builds/20427662/job/prq9i2lkfxv2j0uy
+exclude(:test_splice_over_ary_max, 'Sometimes AppVeyor has insufficient memory to run this test')
diff --git a/test/excludes/_travis/osx/IMAPTest.rb b/test/excludes/_travis/osx/IMAPTest.rb
new file mode 100644
index 0000000000..11687702a1
--- /dev/null
+++ b/test/excludes/_travis/osx/IMAPTest.rb
@@ -0,0 +1,3 @@
+# https://travis-ci.org/ruby/ruby/jobs/444232675
+# randomly raises: Errno::EPROTOTYPE "Protocol wrong type for socket"
+exclude(:test_imaps_post_connection_check, 'This test randomly fails with OpenSSL 1.1.1 on Travis osx build')
diff --git a/test/excludes/_travis/osx/TestGemRemoteFetcher.rb b/test/excludes/_travis/osx/TestGemRemoteFetcher.rb
new file mode 100644
index 0000000000..e10c0ff45c
--- /dev/null
+++ b/test/excludes/_travis/osx/TestGemRemoteFetcher.rb
@@ -0,0 +1,4 @@
+# https://travis-ci.org/ruby/ruby/jobs/444240249
+# raises: OpenSSL::SSL::SSLError "SSL_read: tlsv1 alert decrypt error"
+exclude(:test_do_not_allow_invalid_client_cert_auth_connection,
+ 'This test is failing with OpenSSL 1.1.1 on Travis osx build')
diff --git a/test/excludes/_travis/osx/TestWEBrickUtils.rb b/test/excludes/_travis/osx/TestWEBrickUtils.rb
new file mode 100644
index 0000000000..1b1dd877f5
--- /dev/null
+++ b/test/excludes/_travis/osx/TestWEBrickUtils.rb
@@ -0,0 +1,3 @@
+# https://travis-ci.org/ruby/ruby/jobs/454707326
+# maybe this test's timeout should be re-arranged for Travis osx
+exclude(:test_nested_timeout_outer, 'This test randomly fails on Travis osx')
diff --git a/test/excludes/_wercker/jit-wait/TestDelegateClass.rb b/test/excludes/_wercker/jit-wait/TestDelegateClass.rb
new file mode 100644
index 0000000000..384bb03b4c
--- /dev/null
+++ b/test/excludes/_wercker/jit-wait/TestDelegateClass.rb
@@ -0,0 +1,2 @@
+# https://app.wercker.com/ruby/ruby/runs/mjit-test2/5bda979a191eda000655a8d2?step=5bda9fe4591ca80007653f64
+exclude(:test_frozen, 'somehow FrozenError is not raised with --jit-wait')
diff --git a/test/excludes/_wercker/jit-wait/TestGemRemoteFetcher.rb b/test/excludes/_wercker/jit-wait/TestGemRemoteFetcher.rb
new file mode 100644
index 0000000000..30ece81992
--- /dev/null
+++ b/test/excludes/_wercker/jit-wait/TestGemRemoteFetcher.rb
@@ -0,0 +1,4 @@
+# https://app.wercker.com/ruby/ruby/runs/mjit-test2/5c18fd67c0c3d7001190a14d?step=5c18fda8cfa0fc0007fcc633
+# https://app.wercker.com/ruby/ruby/runs/mjit-test2/5c1a3db51eea2b000777144a?step=5c1a3def78c72000078df9cf
+# https://app.wercker.com/ruby/ruby/runs/mjit-test2/5c1b3a71c0c3d7001191f66e?step=5c1b5ed978c7200007961577
+exclude(/./, 'most of tests are too fragile with --jit-wait and this remote fetcher cannot configure timeout')
diff --git a/test/excludes/_wercker/jit-wait/TestParallel/TestParallel.rb b/test/excludes/_wercker/jit-wait/TestParallel/TestParallel.rb
new file mode 100644
index 0000000000..e241a78b47
--- /dev/null
+++ b/test/excludes/_wercker/jit-wait/TestParallel/TestParallel.rb
@@ -0,0 +1,2 @@
+# https://app.wercker.com/ruby/ruby/runs/test-mjit-wait/5bd040d19b1e440006d52516?step=5bd0410c591ca8000721e3e4
+exclude(:test_separate, 'this test randomly fails with --jit-wait')
diff --git a/test/excludes/_wercker/jit-wait/TestThreadQueue.rb b/test/excludes/_wercker/jit-wait/TestThreadQueue.rb
new file mode 100644
index 0000000000..5d0ba88cf4
--- /dev/null
+++ b/test/excludes/_wercker/jit-wait/TestThreadQueue.rb
@@ -0,0 +1,2 @@
+# https://app.wercker.com/ruby/ruby/runs/test-mjit-wait/5bcfd19aa9806e000655c598?step=5bcfd1d5acc4510006e00f77
+exclude(:test_queue_with_trap, 'this test randomly fails with --jit-wait')
diff --git a/test/excludes/_wercker/jit/TestThreadQueue.rb b/test/excludes/_wercker/jit/TestThreadQueue.rb
new file mode 100644
index 0000000000..2a968ab04c
--- /dev/null
+++ b/test/excludes/_wercker/jit/TestThreadQueue.rb
@@ -0,0 +1,2 @@
+# https://app.wercker.com/ruby/ruby/runs/test-mjit/5bcf51409b1e440006d47831?step=5bcf517d87436a000649326d
+exclude(:test_queue_with_trap, 'this test fails with --jit')
diff --git a/test/fiddle/helper.rb b/test/fiddle/helper.rb
index 1da3d93819..4aaa55ea78 100644
--- a/test/fiddle/helper.rb
+++ b/test/fiddle/helper.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'minitest/autorun'
+# frozen_string_literal: true
+require 'test/unit'
require 'fiddle'
# FIXME: this is stolen from DL and needs to be refactored.
@@ -10,9 +10,6 @@ case RUBY_PLATFORM
when /cygwin/
libc_so = "cygwin1.dll"
libm_so = "cygwin1.dll"
-when /x86_64-linux/
- libc_so = "/lib64/libc.so.6"
- libm_so = "/lib64/libm.so.6"
when /linux/
libdir = '/lib'
case [0].pack('L!').size
@@ -111,7 +108,7 @@ Fiddle::LIBC_SO = libc_so
Fiddle::LIBM_SO = libm_so
module Fiddle
- class TestCase < MiniTest::Unit::TestCase
+ class TestCase < Test::Unit::TestCase
def setup
@libc = Fiddle.dlopen(LIBC_SO)
@libm = Fiddle.dlopen(LIBM_SO)
diff --git a/test/fiddle/test_c_struct_entry.rb b/test/fiddle/test_c_struct_entry.rb
index 39b7aeff99..8ece438f54 100644
--- a/test/fiddle/test_c_struct_entry.rb
+++ b/test/fiddle/test_c_struct_entry.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require_relative 'helper'
require 'fiddle/struct'
diff --git a/test/fiddle/test_c_union_entity.rb b/test/fiddle/test_c_union_entity.rb
index 3c4d538ff1..5727a20e3b 100644
--- a/test/fiddle/test_c_union_entity.rb
+++ b/test/fiddle/test_c_union_entity.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require_relative 'helper'
require 'fiddle/struct'
diff --git a/test/fiddle/test_closure.rb b/test/fiddle/test_closure.rb
index 0e3574ff84..2de0660725 100644
--- a/test/fiddle/test_closure.rb
+++ b/test/fiddle/test_closure.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require_relative 'helper'
rescue LoadError
@@ -7,15 +7,15 @@ end
module Fiddle
class TestClosure < Fiddle::TestCase
def test_argument_errors
- assert_raises(TypeError) do
+ assert_raise(TypeError) do
Closure.new(TYPE_INT, TYPE_INT)
end
- assert_raises(TypeError) do
+ assert_raise(TypeError) do
Closure.new('foo', [TYPE_INT])
end
- assert_raises(TypeError) do
+ assert_raise(TypeError) do
Closure.new(TYPE_INT, ['meow!'])
end
end
diff --git a/test/fiddle/test_cparser.rb b/test/fiddle/test_cparser.rb
index 16134f5aa7..5d9ac3c815 100644
--- a/test/fiddle/test_cparser.rb
+++ b/test/fiddle/test_cparser.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require_relative 'helper'
require 'fiddle/cparser'
@@ -61,7 +61,7 @@ module Fiddle
end
def test_undefined_ctype
- assert_raises(DLError) { parse_ctype('DWORD') }
+ assert_raise(DLError) { parse_ctype('DWORD') }
end
def test_undefined_ctype_with_type_alias
@@ -93,7 +93,7 @@ module Fiddle
end
def test_struct_undefined
- assert_raises(DLError) { parse_struct_signature(['int i', 'DWORD cb']) }
+ assert_raise(DLError) { parse_struct_signature(['int i', 'DWORD cb']) }
end
def test_struct_undefined_with_type_alias
@@ -127,10 +127,13 @@ module Fiddle
'short', 'unsigned short',
'int', 'unsigned int',
'long', 'unsigned long',
+ defined?(TYPE_LONG_LONG) && \
+ [
'long long', 'unsigned long long',
+ ],
'float', 'double',
'const char*', 'void*',
- ]
+ ].flatten.compact
func, ret, args = parse_signature("void func(#{types.join(',')})")
assert_equal 'func', func
assert_equal TYPE_VOID, ret
@@ -139,10 +142,13 @@ module Fiddle
TYPE_SHORT, -TYPE_SHORT,
TYPE_INT, -TYPE_INT,
TYPE_LONG, -TYPE_LONG,
+ defined?(TYPE_LONG_LONG) && \
+ [
TYPE_LONG_LONG, -TYPE_LONG_LONG,
+ ],
TYPE_FLOAT, TYPE_DOUBLE,
TYPE_VOIDP, TYPE_VOIDP,
- ], args
+ ].flatten.compact, args
end
def test_signature_single_variable
diff --git a/test/fiddle/test_fiddle.rb b/test/fiddle/test_fiddle.rb
index cf4839af86..8751d96920 100644
--- a/test/fiddle/test_fiddle.rb
+++ b/test/fiddle/test_fiddle.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require_relative 'helper'
rescue LoadError
diff --git a/test/fiddle/test_func.rb b/test/fiddle/test_func.rb
index 5cdf90f567..d170c59a75 100644
--- a/test/fiddle/test_func.rb
+++ b/test/fiddle/test_func.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require_relative 'helper'
rescue LoadError
@@ -13,12 +13,14 @@ module Fiddle
def test_syscall_with_tainted_string
f = Function.new(@libc['system'], [TYPE_VOIDP], TYPE_INT)
- assert_raises(SecurityError) do
- Thread.new {
- $SAFE = 1
- f.call("uname -rs".taint)
- }.join
- end
+ Thread.new {
+ $SAFE = 1
+ assert_raise(SecurityError) do
+ f.call("uname -rs".dup.taint)
+ end
+ }.join
+ ensure
+ $SAFE = 0
end
def test_sinf
@@ -38,7 +40,7 @@ module Fiddle
def test_string
stress, GC.stress = GC.stress, true
f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
- buff = "000"
+ buff = +"000"
str = f.call(buff, "123")
assert_equal("123", buff)
assert_equal("123", str.to_s)
diff --git a/test/fiddle/test_function.rb b/test/fiddle/test_function.rb
index 06d0eb2b50..eb8ef232e8 100644
--- a/test/fiddle/test_function.rb
+++ b/test/fiddle/test_function.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require_relative 'helper'
rescue LoadError
@@ -6,8 +6,6 @@ end
module Fiddle
class TestFunction < Fiddle::TestCase
- include Test::Unit::Assertions
-
def setup
super
Fiddle.last_error = nil
@@ -62,19 +60,25 @@ module Fiddle
func = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
assert_nil Fiddle.last_error
- func.call("000", "123")
+ func.call(+"000", "123")
refute_nil Fiddle.last_error
end
def test_strcpy
f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
- buff = "000"
+ buff = +"000"
str = f.call(buff, "123")
assert_equal("123", buff)
assert_equal("123", str.to_s)
end
def test_nogvl_poll
+ # XXX hack to quiet down CI errors on EINTR from r64353
+ # [ruby-core:88360] [Misc #14937]
+ # Making pipes (and sockets) non-blocking by default would allow
+ # us to get rid of POSIX timers / timer pthread
+ # https://bugs.ruby-lang.org/issues/14968
+ IO.pipe { |r,w| IO.select([r], [w]) }
begin
poll = @libc['poll']
rescue Fiddle::DLError
@@ -88,9 +92,9 @@ module Fiddle
n1 = f.call(nil, 0, msec)
n2 = th.value
t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
- assert_in_delta(msec, t1 - t0, 100, 'slept correct amount of time')
- assert_equal(0, n1, 'poll(2) called correctly main-thread')
- assert_equal(0, n2, 'poll(2) called correctly in sub-thread')
+ assert_in_delta(msec, t1 - t0, 180, 'slept amount of time')
+ assert_equal(0, n1, perror("poll(2) in main-thread"))
+ assert_equal(0, n2, perror("poll(2) in sub-thread"))
end
def test_no_memory_leak
@@ -98,5 +102,16 @@ module Fiddle
code = 'begin r.call(a); rescue TypeError; end'
assert_no_memory_leak(%w[-W0 -rfiddle], "#{prep}\n1000.times{#{code}}", "10_000.times {#{code}}", limit: 1.2)
end
+
+ private
+
+ def perror(m)
+ proc do
+ if e = Fiddle.last_error
+ m = "#{m}: #{SystemCallError.new(e).message}"
+ end
+ m
+ end
+ end
end
end if defined?(Fiddle)
diff --git a/test/fiddle/test_handle.rb b/test/fiddle/test_handle.rb
index edbfbb3e6e..c0fac39908 100644
--- a/test/fiddle/test_handle.rb
+++ b/test/fiddle/test_handle.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require_relative 'helper'
rescue LoadError
@@ -8,23 +8,27 @@ module Fiddle
class TestHandle < TestCase
include Fiddle
- include Test::Unit::Assertions
-
def test_safe_handle_open
- t = Thread.new do
+ Thread.new do
$SAFE = 1
- Fiddle::Handle.new(LIBC_SO.taint)
- end
- assert_raise(SecurityError) { t.value }
+ assert_raise(SecurityError) {
+ Fiddle::Handle.new(LIBC_SO.dup.taint)
+ }
+ end.join
+ ensure
+ $SAFE = 0
end
def test_safe_function_lookup
- t = Thread.new do
+ Thread.new do
h = Fiddle::Handle.new(LIBC_SO)
$SAFE = 1
- h["qsort".taint]
- end
- assert_raise(SecurityError) { t.value }
+ assert_raise(SecurityError) {
+ h["qsort".dup.taint]
+ }
+ end.join
+ ensure
+ $SAFE = 0
end
def test_to_i
diff --git a/test/fiddle/test_import.rb b/test/fiddle/test_import.rb
index ba68296fd0..99294ea161 100644
--- a/test/fiddle/test_import.rb
+++ b/test/fiddle/test_import.rb
@@ -1,5 +1,5 @@
# coding: US-ASCII
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require_relative 'helper'
require 'fiddle/import'
@@ -19,7 +19,7 @@ module Fiddle
extern "double atof(string)"
extern "unsigned long strtoul(char*, char **, int)"
extern "int qsort(void*, unsigned long, unsigned long, void*)"
- extern "int fprintf(FILE*, char*)"
+ extern "int fprintf(FILE*, char*)" rescue nil
extern "int gettimeofday(timeval*, timezone*)" rescue nil
BoundQsortCallback = bind("void *bound_qsort_callback(void*, void*)"){|ptr1,ptr2| ptr1[0] <=> ptr2[0]}
@@ -45,7 +45,7 @@ module Fiddle
class TestImport < TestCase
def test_ensure_call_dlload
- err = assert_raises(RuntimeError) do
+ err = assert_raise(RuntimeError) do
Class.new do
extend Importer
extern "void *strcpy(char*, char*)"
@@ -64,7 +64,7 @@ module Fiddle
assert_equal(SIZEOF_VOIDP, LIBC.sizeof("FILE*"))
assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct))
assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct.malloc()))
- assert_equal(SIZEOF_LONG_LONG, LIBC.sizeof("long long"))
+ assert_equal(SIZEOF_LONG_LONG, LIBC.sizeof("long long")) if defined?(SIZEOF_LONG_LONG)
end
Fiddle.constants.grep(/\ATYPE_(?!VOID\z)(.*)/) do
@@ -84,7 +84,7 @@ module Fiddle
end
def test_io()
- if( RUBY_PLATFORM != BUILD_RUBY_PLATFORM )
+ if( RUBY_PLATFORM != BUILD_RUBY_PLATFORM ) || !defined?(LIBC.fprintf)
return
end
io_in,io_out = IO.pipe()
@@ -128,7 +128,7 @@ module Fiddle
end
def test_strcpy()
- buff = "000"
+ buff = +"000"
str = LIBC.strcpy(buff, "123")
assert_equal("123", buff)
assert_equal("123", str.to_s)
@@ -147,5 +147,9 @@ module Fiddle
r = LIBC.atof("12.34")
assert_includes(12.00..13.00, r)
end
+
+ def test_no_message_with_debug
+ assert_in_out_err(%w[--debug --disable=gems -rfiddle/import], 'p Fiddle::Importer', ['Fiddle::Importer'])
+ end
end
end if defined?(Fiddle)
diff --git a/test/fiddle/test_pointer.rb b/test/fiddle/test_pointer.rb
index aa77c619ef..b1122aa9c5 100644
--- a/test/fiddle/test_pointer.rb
+++ b/test/fiddle/test_pointer.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require_relative 'helper'
rescue LoadError
@@ -10,8 +10,6 @@ module Fiddle
Fiddle.dlwrap arg
end
- include Test::Unit::Assertions
-
def test_cptr_to_int
null = Fiddle::NULL
assert_equal(null.to_i, null.to_int)
@@ -154,11 +152,7 @@ module Fiddle
def test_free=
assert_normal_exit(<<-"End", '[ruby-dev:39269]')
require 'fiddle'
- Fiddle::LIBC_SO = #{Fiddle::LIBC_SO.dump}
- Fiddle::LIBM_SO = #{Fiddle::LIBM_SO.dump}
include Fiddle
- @libc = dlopen(LIBC_SO)
- @libm = dlopen(LIBM_SO)
free = Fiddle::Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID)
ptr = Fiddle::Pointer.malloc(4)
ptr.free = free
diff --git a/test/fileutils/clobber.rb b/test/fileutils/clobber.rb
index ae416e9fa5..fdcecd5e08 100644
--- a/test/fileutils/clobber.rb
+++ b/test/fileutils/clobber.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'fileutils'
require 'test/unit'
require 'tmpdir'
diff --git a/test/fileutils/fileasserts.rb b/test/fileutils/fileasserts.rb
index 512d31e95f..8ab30b032c 100644
--- a/test/fileutils/fileasserts.rb
+++ b/test/fileutils/fileasserts.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# $Id$
module Test
diff --git a/test/fileutils/test_dryrun.rb b/test/fileutils/test_dryrun.rb
index bc59f8d339..fd8a7805ec 100644
--- a/test/fileutils/test_dryrun.rb
+++ b/test/fileutils/test_dryrun.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# $Id$
require 'fileutils'
@@ -8,7 +8,7 @@ require_relative 'visibility_tests'
class TestFileUtilsDryRun < Test::Unit::TestCase
include FileUtils::DryRun
- include TestFileUtils::Visibility
+ include TestFileUtilsInc::Visibility
def setup
super
diff --git a/test/fileutils/test_fileutils.rb b/test/fileutils/test_fileutils.rb
index 0ff0aadca2..c81f5b40dc 100644
--- a/test/fileutils/test_fileutils.rb
+++ b/test/fileutils/test_fileutils.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# $Id$
require 'fileutils'
@@ -12,13 +12,16 @@ class TestFileUtils < Test::Unit::TestCase
include Test::Unit::FileAssertions
def assert_output_lines(expected, fu = self, message=nil)
- old = fu.instance_variable_get(:@fileutils_output)
+ old = fu.instance_variables.include?(:@fileutils_output) && fu.instance_variable_get(:@fileutils_output)
IO.pipe {|read, write|
fu.instance_variable_set(:@fileutils_output, write)
th = Thread.new { read.read }
th2 = Thread.new {
- yield
- write.close
+ begin
+ yield
+ ensure
+ write.close
+ end
}
th_value, _ = assert_join_threads([th, th2])
lines = th_value.lines.map {|l| l.chomp }
@@ -71,7 +74,20 @@ class TestFileUtils < Test::Unit::TestCase
return true
end
+ @@no_broken_symlink = false
+ if /cygwin/ =~ RUBY_PLATFORM and /\bwinsymlinks:native(?:strict)?\b/ =~ ENV["CYGWIN"]
+ @@no_broken_symlink = true
+ end
+
+ def no_broken_symlink?
+ @@no_broken_symlink
+ end
+
def root_in_posix?
+ if /cygwin/ =~ RUBY_PLATFORM
+ # FIXME: privilege if groups include root user?
+ return Process.groups.include?(0)
+ end
if Process.respond_to?('uid')
return Process.uid == 0
else
@@ -119,6 +135,8 @@ class TestFileUtils < Test::Unit::TestCase
include m
extend m
+ UID_1, UID_2 = distinct_uids(2)
+
include FileUtils
def check_singleton(name)
@@ -142,7 +160,7 @@ class TestFileUtils < Test::Unit::TestCase
def setup
@prevdir = Dir.pwd
- @groups = Process.groups if have_file_perm?
+ @groups = [Process.gid] | Process.groups if have_file_perm?
tmproot = @tmproot = Dir.mktmpdir "fileutils"
Dir.chdir tmproot
my_rm_rf 'data'; mymkdir 'data'
@@ -213,6 +231,17 @@ class TestFileUtils < Test::Unit::TestCase
# Test Cases
#
+ def test_assert_output_lines
+ assert_raise(MiniTest::Assertion) {
+ Timeout.timeout(0.5) {
+ assert_output_lines([]) {
+ Thread.current.report_on_exception = false
+ raise "ok"
+ }
+ }
+ }
+ end
+
def test_pwd
check_singleton :pwd
@@ -280,7 +309,8 @@ class TestFileUtils < Test::Unit::TestCase
touch 'tmp/cptmp'
chmod 0755, 'tmp/cptmp'
cp 'tmp/cptmp', 'tmp/cptmp2'
- assert_equal_filemode('tmp/cptmp', 'tmp/cptmp2', bug4507)
+
+ assert_equal_filemode('tmp/cptmp', 'tmp/cptmp2', bug4507, mask: ~File.umask)
end
def test_cp_preserve_permissions_dir
@@ -305,6 +335,7 @@ class TestFileUtils < Test::Unit::TestCase
assert_raise(ArgumentError) {
cp 'tmp/cptmp_symlink', 'tmp/cptmp'
}
+ return if no_broken_symlink?
# src==dest (3) looped symlink
File.symlink 'symlink', 'tmp/symlink'
assert_raise(Errno::ELOOP) {
@@ -371,6 +402,7 @@ class TestFileUtils < Test::Unit::TestCase
def test_cp_r_symlink
# symlink in a directory
mkdir 'tmp/cpr_src'
+ touch 'tmp/cpr_src/SLdest'
ln_s 'SLdest', 'tmp/cpr_src/symlink'
cp_r 'tmp/cpr_src', 'tmp/cpr_dest'
assert_symlink 'tmp/cpr_dest/symlink'
@@ -396,7 +428,7 @@ class TestFileUtils < Test::Unit::TestCase
assert_nothing_raised {
cp_r 'tmp/cross', 'tmp/cross2', :preserve => true
}
- end if have_symlink?
+ end if have_symlink? and !no_broken_symlink?
def test_cp_r_pathname
# pathname
@@ -408,6 +440,54 @@ class TestFileUtils < Test::Unit::TestCase
}
end
+ def test_cp_r_symlink_remove_destination
+ Dir.mkdir 'tmp/src'
+ Dir.mkdir 'tmp/dest'
+ Dir.mkdir 'tmp/src/dir'
+ File.symlink 'tmp/src/dir', 'tmp/src/a'
+ cp_r 'tmp/src', 'tmp/dest/', remove_destination: true
+ cp_r 'tmp/src', 'tmp/dest/', remove_destination: true
+ end if have_symlink?
+
+ def test_cp_lr
+ check_singleton :cp_lr
+
+ cp_lr 'data', 'tmp'
+ TARGETS.each do |fname|
+ assert_same_file fname, "tmp/#{fname}"
+ end
+
+ # a/* -> b/*
+ mkdir 'tmp/cpr_src'
+ mkdir 'tmp/cpr_dest'
+ File.open('tmp/cpr_src/a', 'w') {|f| f.puts 'a' }
+ File.open('tmp/cpr_src/b', 'w') {|f| f.puts 'b' }
+ File.open('tmp/cpr_src/c', 'w') {|f| f.puts 'c' }
+ mkdir 'tmp/cpr_src/d'
+ cp_lr 'tmp/cpr_src/.', 'tmp/cpr_dest'
+ assert_same_file 'tmp/cpr_src/a', 'tmp/cpr_dest/a'
+ assert_same_file 'tmp/cpr_src/b', 'tmp/cpr_dest/b'
+ assert_same_file 'tmp/cpr_src/c', 'tmp/cpr_dest/c'
+ assert_directory 'tmp/cpr_dest/d'
+ my_rm_rf 'tmp/cpr_src'
+ my_rm_rf 'tmp/cpr_dest'
+
+ bug3588 = '[ruby-core:31360]'
+ mkdir 'tmp2'
+ assert_nothing_raised(ArgumentError, bug3588) do
+ cp_lr 'tmp', 'tmp2'
+ end
+ assert_directory 'tmp2/tmp'
+ assert_raise(ArgumentError, bug3588) do
+ cp_lr 'tmp2', 'tmp2/new_tmp2'
+ end
+
+ bug12892 = '[ruby-core:77885] [Bug #12892]'
+ assert_raise(Errno::ENOENT, bug12892) do
+ cp_lr 'non/existent', 'tmp'
+ end
+ end if have_hardlink?
+
def test_mv
check_singleton :mv
@@ -452,6 +532,9 @@ class TestFileUtils < Test::Unit::TestCase
assert_raise(ArgumentError) {
mv 'tmp/cptmp_symlink', 'tmp/cptmp'
}
+ end if have_symlink?
+
+ def test_mv_broken_symlink
# src==dest (3) looped symlink
File.symlink 'symlink', 'tmp/symlink'
assert_raise(Errno::ELOOP) {
@@ -463,7 +546,7 @@ class TestFileUtils < Test::Unit::TestCase
mv 'tmp/src', 'tmp/dest'
}
assert_equal true, File.symlink?('tmp/dest')
- end if have_symlink?
+ end if have_symlink? and !no_broken_symlink?
def test_mv_pathname
# pathname
@@ -511,7 +594,7 @@ class TestFileUtils < Test::Unit::TestCase
def test_rm_symlink
File.open('tmp/lnf_symlink_src', 'w') {|f| f.puts 'dummy' }
- File.symlink 'tmp/lnf_symlink_src', 'tmp/lnf_symlink_dest'
+ File.symlink 'lnf_symlink_src', 'tmp/lnf_symlink_dest'
rm_f 'tmp/lnf_symlink_dest'
assert_file_not_exist 'tmp/lnf_symlink_dest'
assert_file_exist 'tmp/lnf_symlink_src'
@@ -657,6 +740,17 @@ class TestFileUtils < Test::Unit::TestCase
remove_entry_secure 'tmp/tmpdir/c', true
assert_file_not_exist 'tmp/tmpdir/a'
assert_file_not_exist 'tmp/tmpdir/c'
+
+ unless root_in_posix?
+ File.chmod(01777, 'tmp/tmpdir')
+ if File.sticky?('tmp/tmpdir')
+ Dir.mkdir 'tmp/tmpdir/d', 0
+ assert_raise(Errno::EACCES) {remove_entry_secure 'tmp/tmpdir/d'}
+ File.chmod 0777, 'tmp/tmpdir/d'
+ Dir.rmdir 'tmp/tmpdir/d'
+ end
+ end
+
Dir.rmdir 'tmp/tmpdir'
end
@@ -725,6 +819,9 @@ class TestFileUtils < Test::Unit::TestCase
assert_raise(Errno::EEXIST) {
ln 'tmp/symlink', 'tmp/cptmp' # symlink -> normal file
}
+ end if have_symlink?
+
+ def test_ln_broken_symlink
# src==dest (3) looped symlink
File.symlink 'cptmp_symlink', 'tmp/cptmp_symlink'
begin
@@ -732,7 +829,7 @@ class TestFileUtils < Test::Unit::TestCase
rescue => err
assert_kind_of SystemCallError, err
end
- end if have_symlink?
+ end if have_symlink? and !no_broken_symlink?
def test_ln_pathname
# pathname
@@ -748,16 +845,26 @@ class TestFileUtils < Test::Unit::TestCase
check_singleton :ln_s
TARGETS.each do |fname|
- ln_s fname, 'tmp/lnsdest'
- assert FileTest.symlink?('tmp/lnsdest'), 'not symlink'
- assert_equal fname, File.readlink('tmp/lnsdest')
- rm_f 'tmp/lnsdest'
+ begin
+ fname = "../#{fname}"
+ lnfname = 'tmp/lnsdest'
+ ln_s fname, lnfname
+ assert FileTest.symlink?(lnfname), 'not symlink'
+ assert_equal fname, File.readlink(lnfname)
+ ensure
+ rm_f lnfname
+ end
end
+ end if have_symlink? and !no_broken_symlink?
+
+ def test_ln_s_broken_symlink
assert_nothing_raised {
ln_s 'symlink', 'tmp/symlink'
}
assert_symlink 'tmp/symlink'
+ end if have_symlink? and !no_broken_symlink?
+ def test_ln_s_pathname
# pathname
touch 'tmp/lnsdest'
assert_nothing_raised {
@@ -771,16 +878,22 @@ class TestFileUtils < Test::Unit::TestCase
check_singleton :ln_sf
TARGETS.each do |fname|
+ fname = "../#{fname}"
ln_sf fname, 'tmp/lnsdest'
assert FileTest.symlink?('tmp/lnsdest'), 'not symlink'
assert_equal fname, File.readlink('tmp/lnsdest')
ln_sf fname, 'tmp/lnsdest'
ln_sf fname, 'tmp/lnsdest'
end
+ end if have_symlink?
+
+ def test_ln_sf_broken_symlink
assert_nothing_raised {
ln_sf 'symlink', 'tmp/symlink'
}
+ end if have_symlink? and !no_broken_symlink?
+ def test_ln_sf_pathname
# pathname
touch 'tmp/lns_dest'
assert_nothing_raised {
@@ -892,6 +1005,24 @@ class TestFileUtils < Test::Unit::TestCase
mkdir_p '/'
end
+ if /mswin|mingw|cygwin/ =~ RUBY_PLATFORM
+ def test_mkdir_p_root
+ if /cygwin/ =~ RUBY_PLATFORM
+ tmpdir = `cygpath -ma .`.chomp
+ else
+ tmpdir = Dir.pwd
+ end
+ skip "No drive letter" unless /\A[a-z]:/i =~ tmpdir
+ drive = "./#{$&}"
+ assert_file_not_exist drive
+ mkdir_p "#{tmpdir}/none/dir"
+ assert_directory "none/dir"
+ assert_file_not_exist drive
+ ensure
+ Dir.rmdir(drive) if drive and File.directory?(drive)
+ end
+ end
+
def test_mkdir_p_file_perm
mkdir_p 'tmp/tmp/tmp', :mode => 07777
assert_directory 'tmp/tmp/tmp'
@@ -942,13 +1073,16 @@ class TestFileUtils < Test::Unit::TestCase
assert_raise(ArgumentError) {
install 'tmp/cptmp_symlink', 'tmp/cptmp'
}
+ end if have_symlink?
+
+ def test_install_broken_symlink
# src==dest (3) looped symlink
File.symlink 'symlink', 'tmp/symlink'
assert_raise(Errno::ELOOP) {
# File#install invokes open(2), always ELOOP must be raised
install 'tmp/symlink', 'tmp/symlink'
}
- end if have_symlink?
+ end if have_symlink? and !no_broken_symlink?
def test_install_pathname
# pathname
@@ -1158,7 +1292,7 @@ class TestFileUtils < Test::Unit::TestCase
if have_file_perm?
def test_chown_error
- uid, = distinct_uids(1)
+ uid = UID_1
return unless uid
touch 'tmp/a'
@@ -1270,14 +1404,11 @@ class TestFileUtils < Test::Unit::TestCase
if root_in_posix?
def test_chown_with_root
- uid_1, uid_2 = distinct_uids(2)
- return unless uid_1 and uid_2
-
gid = @groups[0] # Most of the time, root only has one group
files = ['tmp/a1', 'tmp/a2']
files.each {|file| touch file}
- [uid_1, uid_2].each {|uid|
+ [UID_1, UID_2].each {|uid|
assert_output_lines(["chown #{uid}:#{gid} tmp/a1 tmp/a2"]) {
chown uid, gid, files, verbose: true
files.each {|file|
@@ -1289,69 +1420,57 @@ class TestFileUtils < Test::Unit::TestCase
end
def test_chown_dir_user_ownership_not_recursive_with_root
- uid_1, uid_2 = distinct_uids(2)
- return unless uid_1 and uid_2
-
assert_output_lines([]) {
mkdir 'tmp/dir'
touch 'tmp/dir/a'
- chown uid_1, nil, ['tmp/dir', 'tmp/dir/a']
- assert_ownership_user uid_1, 'tmp/dir'
- assert_ownership_user uid_1, 'tmp/dir/a'
- chown uid_2, nil, 'tmp/dir'
- assert_ownership_user uid_2, 'tmp/dir'
+ chown UID_1, nil, ['tmp/dir', 'tmp/dir/a']
+ assert_ownership_user UID_1, 'tmp/dir'
+ assert_ownership_user UID_1, 'tmp/dir/a'
+ chown UID_2, nil, 'tmp/dir'
+ assert_ownership_user UID_2, 'tmp/dir'
# Make sure FileUtils.chown does not chown recursively
- assert_ownership_user uid_1, 'tmp/dir/a'
+ assert_ownership_user UID_1, 'tmp/dir/a'
}
end
def test_chown_R_with_root
- uid_1, uid_2 = distinct_uids(2)
- return unless uid_1 and uid_2
-
assert_output_lines([]) {
list = ['tmp/dir', 'tmp/dir/a', 'tmp/dir/a/b', 'tmp/dir/a/b/c']
mkdir_p 'tmp/dir/a/b/c'
touch 'tmp/d'
# string input
- chown_R uid_1, nil, 'tmp/dir'
+ chown_R UID_1, nil, 'tmp/dir'
list.each {|dir|
- assert_ownership_user uid_1, dir
+ assert_ownership_user UID_1, dir
}
- chown_R uid_1, nil, 'tmp/d'
- assert_ownership_user uid_1, 'tmp/d'
+ chown_R UID_1, nil, 'tmp/d'
+ assert_ownership_user UID_1, 'tmp/d'
# list input
- chown_R uid_2, nil, ['tmp/dir', 'tmp/d']
+ chown_R UID_2, nil, ['tmp/dir', 'tmp/d']
list += ['tmp/d']
list.each {|dir|
- assert_ownership_user uid_2, dir
+ assert_ownership_user UID_2, dir
}
}
end
else
def test_chown_without_permission
- uid_1, uid_2 = distinct_uids(2)
- return unless uid_1 and uid_2
-
touch 'tmp/a'
assert_raise(Errno::EPERM) {
- chown uid_1, nil, 'tmp/a'
- chown uid_2, nil, 'tmp/a'
+ chown UID_1, nil, 'tmp/a'
+ chown UID_2, nil, 'tmp/a'
}
end
def test_chown_R_without_permission
- uid_1, uid_2 = distinct_uids(2)
- return unless uid_1 and uid_2
-
touch 'tmp/a'
- exception = assert_raise(Errno::EPERM) {
- chown_R uid_1, nil, 'tmp/a'
- chown_R uid_2, nil, 'tmp/a'
+ assert_raise(Errno::EPERM) {
+ chown_R UID_1, nil, 'tmp/a'
+ chown_R UID_2, nil, 'tmp/a'
}
end
end
- end
+ end if UID_1 and UID_2
def test_copy_entry
check_singleton :copy_entry
@@ -1365,6 +1484,7 @@ class TestFileUtils < Test::Unit::TestCase
def test_copy_entry_symlink
# root is a symlink
+ touch 'tmp/somewhere'
File.symlink 'somewhere', 'tmp/symsrc'
copy_entry 'tmp/symsrc', 'tmp/symdest'
assert_symlink 'tmp/symdest'
@@ -1372,6 +1492,7 @@ class TestFileUtils < Test::Unit::TestCase
# content is a symlink
mkdir 'tmp/dir'
+ touch 'tmp/dir/somewhere'
File.symlink 'somewhere', 'tmp/dir/sym'
copy_entry 'tmp/dir', 'tmp/dirdest'
assert_directory 'tmp/dirdest'
@@ -1380,6 +1501,14 @@ class TestFileUtils < Test::Unit::TestCase
assert_equal 'somewhere', File.readlink('tmp/dirdest/sym')
end if have_symlink?
+ def test_copy_entry_symlink_remove_destination
+ Dir.mkdir 'tmp/dir'
+ File.symlink 'tmp/dir', 'tmp/dest'
+ touch 'tmp/src'
+ copy_entry 'tmp/src', 'tmp/dest', false, false, true
+ assert_file_exist 'tmp/dest'
+ end if have_symlink?
+
def test_copy_file
check_singleton :copy_file
@@ -1497,6 +1626,10 @@ class TestFileUtils < Test::Unit::TestCase
check_singleton :cd
end
+ def test_cd_result
+ assert_equal 42, cd('.') { 42 }
+ end
+
def test_chdir
check_singleton :chdir
end
@@ -1554,6 +1687,21 @@ class TestFileUtils < Test::Unit::TestCase
subdir = 'data/sub/dir'
mkdir_p(subdir)
+ File.write("#{subdir}/file", '')
+ msg = "should fail to remove non-empty directory"
+ assert_raise(Errno::ENOTEMPTY, Errno::EEXIST, msg) {
+ rmdir(subdir)
+ }
+ assert_raise(Errno::ENOTEMPTY, Errno::EEXIST, msg) {
+ rmdir(subdir, parents: true)
+ }
+ File.unlink("#{subdir}/file")
+ assert_raise(Errno::ENOENT) {
+ rmdir("#{subdir}/nonexistent")
+ }
+ assert_raise(Errno::ENOENT) {
+ rmdir("#{subdir}/nonexistent", parents: true)
+ }
assert_nothing_raised(Errno::ENOENT) {
rmdir(subdir, parents: true)
}
diff --git a/test/fileutils/test_nowrite.rb b/test/fileutils/test_nowrite.rb
index f331d51e46..543fa39f5a 100644
--- a/test/fileutils/test_nowrite.rb
+++ b/test/fileutils/test_nowrite.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# $Id$
require 'fileutils'
@@ -8,7 +8,7 @@ require_relative 'visibility_tests'
class TestFileUtilsNoWrite < Test::Unit::TestCase
include FileUtils::NoWrite
- include TestFileUtils::Visibility
+ include TestFileUtilsInc::Visibility
def setup
super
diff --git a/test/fileutils/test_verbose.rb b/test/fileutils/test_verbose.rb
index 1daf9f0531..cf65be8e03 100644
--- a/test/fileutils/test_verbose.rb
+++ b/test/fileutils/test_verbose.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
# $Id$
require 'test/unit'
@@ -8,7 +8,7 @@ require_relative 'visibility_tests'
class TestFileUtilsVerbose < Test::Unit::TestCase
include FileUtils::Verbose
- include TestFileUtils::Visibility
+ include TestFileUtilsInc::Visibility
def setup
super
diff --git a/test/fileutils/visibility_tests.rb b/test/fileutils/visibility_tests.rb
index 6bb5a9fc8f..4c02c9d207 100644
--- a/test/fileutils/visibility_tests.rb
+++ b/test/fileutils/visibility_tests.rb
@@ -1,15 +1,15 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'fileutils'
-class TestFileUtils < Test::Unit::TestCase
+class TestFileUtilsInc < Test::Unit::TestCase
end
##
# These tests are reused in the FileUtils::Verbose, FileUtils::NoWrite and
# FileUtils::DryRun tests
-module TestFileUtils::Visibility
+module TestFileUtilsInc::Visibility
FileUtils::METHODS.each do |m|
define_method "test_singleton_visibility_#{m}" do
diff --git a/test/gdbm/test_gdbm.rb b/test/gdbm/test_gdbm.rb
index aaf0bf7459..64692a4465 100644
--- a/test/gdbm/test_gdbm.rb
+++ b/test/gdbm/test_gdbm.rb
@@ -6,6 +6,7 @@ end
if defined? GDBM
require 'test/unit'
+ require 'envutil' unless defined?(EnvUtil)
require 'tmpdir'
require 'fileutils'
@@ -42,6 +43,8 @@ if defined? GDBM
end
def test_delete_rdonly
+ skip("skipped because root can open anything") if Process.uid == 0
+
if /^CYGWIN_9/ !~ SYSTEM
assert_raise(GDBMError) {
@gdbm_rdonly.delete("foo")
@@ -161,7 +164,7 @@ if defined? GDBM
open_db_child(dbname) do
assert_raise(Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EACCES) {
GDBM.open(dbname, 0644) {|gdbm|
- assert_instance_of(GDBM, gdbm)
+ assert(false)
}
}
end
@@ -210,6 +213,8 @@ if defined? GDBM
end if defined? GDBM::NOLOCK # gdbm 1.8.0 specific
def test_s_open_error
+ skip "because root can open anything" if Process.uid == 0
+
assert_instance_of(GDBM, gdbm = GDBM.open("#{@tmpdir}/#{@prefix}", 0))
assert_raise(Errno::EACCES, Errno::EWOULDBLOCK) {
GDBM.open("#{@tmpdir}/#{@prefix}", 0)
@@ -721,7 +726,8 @@ if defined? GDBM
def test_freeze
GDBM.open("#{@tmproot}/a.dbm") {|d|
d.freeze
- assert_raise(RuntimeError) { d["k"] = "v" }
+ expected_error = defined?(FrozenError) ? FrozenError : RuntimeError
+ assert_raise(expected_error) { d["k"] = "v" }
}
end
end
diff --git a/test/io/console/test_io_console.rb b/test/io/console/test_io_console.rb
index ff705f7d7c..db3814d51c 100644
--- a/test/io/console/test_io_console.rb
+++ b/test/io/console/test_io_console.rb
@@ -7,6 +7,15 @@ rescue LoadError
end
class TestIO_Console < Test::Unit::TestCase
+ # FreeBSD seems to hang on TTOU when running parallel tests
+ # tested on FreeBSD 11.x
+ def set_winsize_setup
+ @old_ttou = trap(:TTOU, 'IGNORE') if RUBY_PLATFORM =~ /freebsd/i
+ end
+
+ def set_winsize_teardown
+ trap(:TTOU, @old_ttou) if defined?(@old_ttou) and @old_ttou
+ end
end
defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do
@@ -127,22 +136,22 @@ defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do
sleep 0.1
s.print "b\n"
sleep 0.1
- assert_equal("a\r\nb\r\n", m.readpartial(10))
- assert_equal("a\n", s.readpartial(10))
+ assert_equal("a\r\nb\r\n", m.gets + m.gets)
+ assert_equal("a\n", s.gets)
s.noecho {
assert_not_send([s, :echo?])
m.print "a\n"
s.print "b\n"
- assert_equal("b\r\n", m.readpartial(10))
- assert_equal("a\n", s.readpartial(10))
+ assert_equal("b\r\n", m.gets)
+ assert_equal("a\n", s.gets)
}
assert_send([s, :echo?])
m.print "a\n"
sleep 0.1
s.print "b\n"
sleep 0.1
- assert_equal("a\r\nb\r\n", m.readpartial(10))
- assert_equal("a\n", s.readpartial(10))
+ assert_equal("a\r\nb\r\n", m.gets + m.gets)
+ assert_equal("a\n", s.gets)
}
end
@@ -165,22 +174,22 @@ defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do
sleep 0.1
s.print "b\n"
sleep 0.1
- assert_equal("a\r\nb\r\n", m.readpartial(10))
- assert_equal("a\n", s.readpartial(10))
+ assert_equal("a\r\nb\r\n", m.gets + m.gets)
+ assert_equal("a\n", s.gets)
s.echo = false
assert_not_send([s, :echo?])
m.print "a\n"
s.print "b\n"
- assert_equal("b\r\n", m.readpartial(10))
- assert_equal("a\n", s.readpartial(10))
+ assert_equal("b\r\n", m.gets)
+ assert_equal("a\n", s.gets)
s.echo = true
assert_send([s, :echo?])
m.print "a\n"
sleep 0.1
s.print "b\n"
sleep 0.1
- assert_equal("a\r\nb\r\n", m.readpartial(10))
- assert_equal("a\n", s.readpartial(10))
+ assert_equal("a\r\nb\r\n", m.gets + m.gets)
+ assert_equal("a\n", s.gets)
}
end
@@ -188,8 +197,9 @@ defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do
skip unless IO.method_defined?("getpass")
run_pty("p IO.console.getpass('> ')") do |r, w|
assert_equal("> ", r.readpartial(10))
+ sleep 0.1
w.print "asdf\n"
- sleep 1
+ sleep 0.1
assert_equal("\r\n", r.gets)
assert_equal("\"asdf\"", r.gets.chomp)
end
@@ -200,7 +210,8 @@ defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do
m.print "a"
s.iflush
m.print "b\n"
- assert_equal("b\n", s.readpartial(10))
+ m.flush
+ assert_equal("b\n", s.gets)
}
end
@@ -209,6 +220,8 @@ defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do
s.print "a"
s.oflush # oflush may be issued after "a" is already sent.
s.print "b"
+ s.flush
+ sleep 0.1
assert_include(["b", "ab"], m.readpartial(10))
}
end
@@ -218,7 +231,8 @@ defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do
m.print "a"
s.ioflush
m.print "b\n"
- assert_equal("b\n", s.readpartial(10))
+ m.flush
+ assert_equal("b\n", s.gets)
}
end
@@ -227,6 +241,8 @@ defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do
s.print "a"
s.ioflush # ioflush may be issued after "a" is already sent.
s.print "b"
+ s.flush
+ sleep 0.1
assert_include(["b", "ab"], m.readpartial(10))
}
end
@@ -236,28 +252,36 @@ defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do
begin
assert_equal([0, 0], s.winsize)
rescue Errno::EINVAL # OpenSolaris 2009.06 TIOCGWINSZ causes Errno::EINVAL before TIOCSWINSZ.
+ else
+ assert_equal([80, 25], s.winsize = [80, 25])
+ assert_equal([80, 25], s.winsize)
+ #assert_equal([80, 25], m.winsize)
+ assert_equal([100, 40], m.winsize = [100, 40])
+ #assert_equal([100, 40], s.winsize)
+ assert_equal([100, 40], m.winsize)
end
}
end
- if IO.console
- def test_close
- IO.console.close
- assert_kind_of(IO, IO.console)
- assert_nothing_raised(IOError) {IO.console.fileno}
-
- IO.console(:close)
- assert(IO.console(:tty?))
- ensure
- IO.console(:close)
+ def test_set_winsize_invalid_dev
+ set_winsize_setup
+ [IO::NULL, __FILE__].each do |path|
+ open(path) do |io|
+ begin
+ s = io.winsize
+ rescue SystemCallError => e
+ assert_raise(e.class) {io.winsize = [0, 0]}
+ else
+ assert(false, "winsize on #{path} succeed: #{s.inspect}")
+ end
+ assert_raise(ArgumentError) {io.winsize = [0, 0, 0]}
+ end
end
+ ensure
+ set_winsize_teardown
+ end
- def test_sync
- assert(IO.console.sync, "console should be unbuffered")
- ensure
- IO.console(:close)
- end
- else
+ unless IO.console
def test_close
assert_equal(["true"], run_pty("IO.console.close; p IO.console.fileno >= 0"))
assert_equal(["true"], run_pty("IO.console(:close); p IO.console(:tty?)"))
@@ -300,6 +324,48 @@ defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do
end
defined?(IO.console) and TestIO_Console.class_eval do
+ if IO.console
+ def test_get_winsize_console
+ s = IO.console.winsize
+ assert_kind_of(Array, s)
+ assert_equal(2, s.size)
+ assert_kind_of(Integer, s[0])
+ assert_kind_of(Integer, s[1])
+ end
+
+ def test_set_winsize_console
+ set_winsize_setup
+ s = IO.console.winsize
+ assert_nothing_raised(TypeError) {IO.console.winsize = s}
+ bug = '[ruby-core:82741] [Bug #13888]'
+ IO.console.winsize = [s[0], s[1]+1]
+ assert_equal([s[0], s[1]+1], IO.console.winsize, bug)
+ IO.console.winsize = s
+ assert_equal(s, IO.console.winsize, bug)
+ ensure
+ set_winsize_teardown
+ end
+
+ def test_close
+ IO.console.close
+ assert_kind_of(IO, IO.console)
+ assert_nothing_raised(IOError) {IO.console.fileno}
+
+ IO.console(:close)
+ assert(IO.console(:tty?))
+ ensure
+ IO.console(:close)
+ end
+
+ def test_sync
+ assert(IO.console.sync, "console should be unbuffered")
+ ensure
+ IO.console(:close)
+ end
+ end
+end
+
+defined?(IO.console) and TestIO_Console.class_eval do
case
when Process.respond_to?(:daemon)
noctty = [EnvUtil.rubybin, "-e", "Process.daemon(true)"]
diff --git a/test/io/nonblock/test_flush.rb b/test/io/nonblock/test_flush.rb
index bc60cab8bf..08d129de3f 100644
--- a/test/io/nonblock/test_flush.rb
+++ b/test/io/nonblock/test_flush.rb
@@ -50,4 +50,24 @@ class TestIONonblock < Test::Unit::TestCase
assert_equal(4097, result.size)
true
end
+
+ def test_nonblock
+ IO.pipe {|r, w|
+ w.nonblock = false
+ assert_equal(false, w.nonblock?)
+ w.nonblock do
+ assert_equal(true, w.nonblock?)
+ w.nonblock(false) do
+ assert_equal(false, w.nonblock?)
+ w.nonblock(false) do
+ assert_equal(false, w.nonblock?)
+ end
+ assert_equal(false, w.nonblock?)
+ end
+ assert_equal(true, w.nonblock?)
+ end
+ assert_equal(false, w.nonblock?)
+ }
+ rescue NotImplementedError
+ end
end if IO.method_defined?(:nonblock)
diff --git a/test/io/wait/test_io_wait_uncommon.rb b/test/io/wait/test_io_wait_uncommon.rb
new file mode 100644
index 0000000000..e7f222c578
--- /dev/null
+++ b/test/io/wait/test_io_wait_uncommon.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+require 'test/unit'
+require 'io/wait'
+
+# test uncommon device types to check portability problems
+# We may optimize IO#wait_*able for non-Linux kernels in the future
+class TestIOWaitUncommon < Test::Unit::TestCase
+ def test_tty_wait
+ begin
+ tty = File.open('/dev/tty', 'w+')
+ rescue Errno::ENOENT, Errno::ENXIO => e
+ skip "/dev/tty: #{e.message} (#{e.class})"
+ end
+ assert_include [ nil, tty ], tty.wait_readable(0)
+ assert_equal tty, tty.wait_writable(1), 'portability test'
+ ensure
+ tty&.close
+ end
+
+ def test_fifo_wait
+ skip 'no mkfifo' unless File.respond_to?(:mkfifo) && IO.const_defined?(:NONBLOCK)
+ Dir.mktmpdir('rubytest-fifo') do |dir|
+ fifo = "#{dir}/fifo"
+ assert_equal 0, File.mkfifo(fifo)
+ rd = Thread.new { File.open(fifo, IO::RDONLY|IO::NONBLOCK) }
+ begin
+ wr = File.open(fifo, IO::WRONLY|IO::NONBLOCK)
+ rescue Errno::ENXIO
+ Thread.pass
+ end until wr
+ assert_instance_of File, rd.value
+ assert_instance_of File, wr
+ rd = rd.value
+ assert_nil rd.wait_readable(0)
+ assert_same wr, wr.wait_writable(0)
+ wr.syswrite 'hi'
+ assert_same rd, rd.wait_readable(1)
+ wr.close
+ assert_equal 'hi', rd.gets
+ rd.close
+ end
+ end
+
+ # used to find portability problems because some ppoll implementations
+ # are incomplete and do not work for certain "file" types
+ def check_dev(dev, m = :wait_readable)
+ begin
+ fp = File.open("/dev/#{dev}", m == :wait_readable ? 'r' : 'w')
+ rescue SystemCallError => e
+ skip "#{dev} could not be opened #{e.message} (#{e.class})"
+ end
+ assert_same fp, fp.__send__(m)
+ ensure
+ fp&.close
+ end
+
+ def test_wait_readable_urandom
+ check_dev 'urandom'
+ end
+
+ def test_wait_readable_random
+ File.open('/dev/random') do |fp|
+ assert_nothing_raised do
+ fp.wait_readable(0)
+ end
+ end
+ rescue SystemCallError => e
+ skip "/dev/random could not be opened #{e.message} (#{e.class})"
+ end
+
+ def test_wait_readable_zero
+ check_dev 'zero'
+ end
+
+ def test_wait_writable_null
+ check_dev 'null', :wait_writable
+ end
+end
diff --git a/test/irb/test_context.rb b/test/irb/test_context.rb
new file mode 100644
index 0000000000..fa2432b3f3
--- /dev/null
+++ b/test/irb/test_context.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'tempfile'
+require 'irb'
+require 'rubygems' if defined?(Gem)
+
+module TestIRB
+ class TestContext < Test::Unit::TestCase
+ class TestInputMethod < ::IRB::InputMethod
+ attr_reader :list, :line_no
+
+ def initialize(list = [])
+ super("test")
+ @line_no = 0
+ @list = list
+ end
+
+ def gets
+ @list[@line_no]&.tap {@line_no += 1}
+ end
+
+ def eof?
+ @line_no >= @list.size
+ end
+
+ def encoding
+ Encoding.default_external
+ end
+ end
+
+ def setup
+ IRB.init_config(nil)
+ IRB.conf[:USE_READLINE] = false
+ IRB.conf[:VERBOSE] = false
+ workspace = IRB::WorkSpace.new(Object.new)
+ @context = IRB::Context.new(nil, workspace, TestInputMethod.new)
+ end
+
+ def test_last_value
+ assert_nil(@context.last_value)
+ assert_nil(@context.evaluate('_', 1))
+ obj = Object.new
+ @context.set_last_value(obj)
+ assert_same(obj, @context.last_value)
+ assert_same(obj, @context.evaluate('_', 1))
+ end
+
+ def test_evaluate_with_exception
+ assert_nil(@context.evaluate("$!", 1))
+ e = assert_raise_with_message(RuntimeError, 'foo') {
+ @context.evaluate("raise 'foo'", 1)
+ }
+ assert_equal('foo', e.message)
+ assert_same(e, @context.evaluate('$!', 1, exception: e))
+ end
+
+ def test_eval_input
+ input = TestInputMethod.new([
+ "raise 'Foo'\n",
+ "_\n",
+ "0\n",
+ "_\n",
+ ])
+ irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
+ out, err = capture_io do
+ irb.eval_input
+ end
+ assert_empty err
+ assert_pattern_list([:*, /RuntimeError \(.*Foo.*\).*\n/,
+ :*, /#<RuntimeError: Foo>\n/,
+ :*, /0$/,
+ :*, /0$/,
+ /\s*/], out)
+ end
+ end
+end
diff --git a/test/irb/test_init.rb b/test/irb/test_init.rb
new file mode 100644
index 0000000000..11e293ad18
--- /dev/null
+++ b/test/irb/test_init.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: false
+require "test/unit"
+require "irb"
+
+module TestIRB
+ class TestInit < Test::Unit::TestCase
+ def test_setup_with_argv_preserves_global_argv
+ argv = ["foo", "bar"]
+ with_argv(argv) do
+ IRB.setup(eval("__FILE__"), argv: %w[-f])
+ assert_equal argv, ARGV
+ end
+ end
+
+ def test_setup_with_minimum_argv_does_not_change_dollar0
+ orig = $0.dup
+ IRB.setup(eval("__FILE__"), argv: %w[-f])
+ assert_equal orig, $0
+ end
+
+ private
+
+ def with_argv(argv)
+ orig = ARGV.dup
+ ARGV.replace(argv)
+ yield
+ ensure
+ ARGV.replace(orig)
+ end
+ end
+end
diff --git a/test/irb/test_ruby-lex.rb b/test/irb/test_ruby-lex.rb
new file mode 100644
index 0000000000..b07b4a2eb6
--- /dev/null
+++ b/test/irb/test_ruby-lex.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'irb/ruby-lex'
+require 'stringio'
+
+module TestIRB
+ class TestRubyLex < Test::Unit::TestCase
+ def setup
+ @scanner = RubyLex.new
+ end
+
+ def teardown
+ RubyLex.debug_level = 0
+ end
+
+ def test_set_input_proc
+ called = false
+ @scanner.set_input(nil) {called = true; nil}
+ @scanner.each_top_level_statement {}
+ assert(called)
+ end
+
+ def test_comment
+ assert_equal([["#\n", 1]], top_level_statement("#\n"))
+ end
+
+ def test_top_level_statement
+ result = top_level_statement("#{<<-"begin;"}#{<<~"end;"}")
+ begin;
+ begin
+ end
+ begin
+ end
+ end;
+ assert_equal([
+ ["begin\n""end\n", 1],
+ ["begin\n""end\n", 3],
+ ],
+ result)
+ end
+
+ def test_immature_statement
+ src = "if false\n"
+ assert_equal([[src, 1]], top_level_statement(src))
+ end
+
+ def test_prompt
+ prompts = []
+ @scanner.set_prompt {|*a|
+ a << @scanner.instance_variable_get(:@lex_state)
+ unless prompts.last == a
+ prompts << a
+ end
+ }
+ src, lineno = "#{<<-"begin;"}#{<<~'end;'}", __LINE__+1
+ begin;
+ # #;# LTYPE:INDENT:CONTINUE
+ x #;# -:0:-
+ x( #;# -:0:-
+ ) #;# -:1:*
+ a \ #;# -:0:-
+ #;# -:0:*
+ a; #;# -:0:-
+ a #;# -:0:-
+ #;# -:0:-
+ a #;# -:0:-
+ a = #;# -:0:-
+ ' #;# -:0:*
+ ' #;# ':0:*
+ if false or #;# -:0:-
+ true #;# -:1:*
+ a #;# -:1:-
+ " #;# -:1:-
+ " #;# ":1:-
+ begin #;# -:1:-
+ a #;# -:2:-
+ a #;# -:2:-
+ end #;# -:2:-
+ else #;# -:1:-
+ nil #;# -:1:-
+ end #;# -:1:-
+ end;
+ top_level_statement(src.gsub(/[ \t]*#;#.*/, ''))
+ src.each_line.with_index(1) do |line, i|
+ p = prompts.shift
+ next unless /#;#\s*(?:-|(?<ltype>\S)):(?<indent>\d+):(?:(?<cont>\*)|-)(?:.*FIXME:(?<fixme>.*))?/ =~ line
+ indent = indent.to_i
+ cont = (fixme && /`continue'/.match?(fixme)) ^ cont
+ assert_equal([ltype, indent, cont, i], p[0..3], "#{lineno+i}:#{p[4]}: #{line}")
+ end
+ end
+
+ def top_level_statement(lines)
+ input = InputLines.new(lines, "r")
+ scanned = []
+ @scanner.set_input(input)
+ @scanner.each_top_level_statement {|*e|
+ scanned << e
+ yield(*e) if defined?(yield)
+ }
+ scanned
+ end
+
+ class InputLines < StringIO
+ alias encoding external_encoding
+ end
+ end
+end
diff --git a/test/irb/test_workspace.rb b/test/irb/test_workspace.rb
new file mode 100644
index 0000000000..0795b17e09
--- /dev/null
+++ b/test/irb/test_workspace.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'tempfile'
+require 'irb/workspace'
+
+module TestIRB
+ class TestWorkSpace < Test::Unit::TestCase
+ def test_code_around_binding
+ Tempfile.create do |f|
+ code = <<~RUBY
+ # 1
+ # 2
+ IRB::WorkSpace.new(binding) # 3
+ # 4
+ # 5
+ RUBY
+ f.print(code)
+ f.close
+
+ workspace = eval(code, binding, f.path)
+ assert_equal(<<~EOS, workspace.code_around_binding)
+
+ From: #{f.path} @ line 3 :
+
+ 1: # 1
+ 2: # 2
+ => 3: IRB::WorkSpace.new(binding) # 3
+ 4: # 4
+ 5: # 5
+
+ EOS
+ end
+ end
+
+ def test_code_around_binding_with_existing_unreadable_file
+ skip 'chmod cannot make file unreadable on windows' if windows?
+ skip 'skipped in root privilege' if Process.uid == 0
+
+ Tempfile.create do |f|
+ code = "IRB::WorkSpace.new(binding)\n"
+ f.print(code)
+ f.close
+
+ File.chmod(0, f.path)
+
+ workspace = eval(code, binding, f.path)
+ assert_equal(nil, workspace.code_around_binding)
+ end
+ end
+
+ def test_code_around_binding_with_script_lines__
+ with_script_lines do |script_lines|
+ Tempfile.create do |f|
+ code = "IRB::WorkSpace.new(binding)\n"
+ script_lines[f.path] = code.split(/^/)
+
+ workspace = eval(code, binding, f.path)
+ assert_equal(<<~EOS, workspace.code_around_binding)
+
+ From: #{f.path} @ line 1 :
+
+ => 1: IRB::WorkSpace.new(binding)
+
+ EOS
+ end
+ end
+ end
+
+ def test_code_around_binding_on_irb
+ workspace = eval("IRB::WorkSpace.new(binding)", binding, "(irb)")
+ assert_equal(nil, workspace.code_around_binding)
+ end
+
+ private
+
+ def with_script_lines
+ script_lines = nil
+ debug_lines = {}
+ Object.class_eval do
+ if defined?(SCRIPT_LINES__)
+ script_lines = SCRIPT_LINES__
+ remove_const :SCRIPT_LINES__
+ end
+ const_set(:SCRIPT_LINES__, debug_lines)
+ end
+ yield debug_lines
+ ensure
+ Object.class_eval do
+ remove_const :SCRIPT_LINES__
+ const_set(:SCRIPT_LINES__, script_lines) if script_lines
+ end
+ end
+ end
+end
diff --git a/test/json/json_encoding_test.rb b/test/json/json_encoding_test.rb
index 29ae02e563..cc7b71553a 100644
--- a/test/json/json_encoding_test.rb
+++ b/test/json/json_encoding_test.rb
@@ -79,6 +79,8 @@ class JSONEncodingTest < Test::Unit::TestCase
json = '["\ud840\udc01"]'
assert_equal json, generate(utf8, :ascii_only => true)
assert_equal utf8, parse(json)
+ assert_raise(JSON::ParserError) { parse('"\u"') }
+ assert_raise(JSON::ParserError) { parse('"\ud800"') }
end
def test_chars
diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb
index 86be398f46..86be398f46 100755..100644
--- a/test/json/json_generator_test.rb
+++ b/test/json/json_generator_test.rb
diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb
index 40ad500cb6..68aeb572bb 100644
--- a/test/json/json_parser_test.rb
+++ b/test/json/json_parser_test.rb
@@ -4,6 +4,7 @@ require 'test_helper'
require 'stringio'
require 'tempfile'
require 'ostruct'
+require 'bigdecimal'
class JSONParserTest < Test::Unit::TestCase
include JSON
@@ -108,6 +109,11 @@ class JSONParserTest < Test::Unit::TestCase
assert_equal -1.0/0, parse('-Infinity', :allow_nan => true)
end
+ def test_parse_bigdecimals
+ assert_equal(BigDecimal, JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"].class)
+ assert_equal(BigDecimal("0.901234567890123456789E1"),JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"] )
+ end
+
if Array.method_defined?(:permutation)
def test_parse_more_complex_arrays
a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
diff --git a/test/lib/envutil.rb b/test/lib/envutil.rb
index 93b6b8e98b..5d3bce99ec 100644
--- a/test/lib/envutil.rb
+++ b/test/lib/envutil.rb
@@ -1,13 +1,15 @@
# -*- coding: us-ascii -*-
-# frozen_string_literal: false
+# frozen_string_literal: true
require "open3"
require "timeout"
require_relative "find_executable"
-require "rbconfig/sizeof"
-
-class Integer
- FIXNUM_MIN = -(1 << (8 * RbConfig::SIZEOF['long'] - 2))
- FIXNUM_MAX = (1 << (8 * RbConfig::SIZEOF['long'] - 2)) - 1
+begin
+ require 'rbconfig'
+rescue LoadError
+end
+begin
+ require "rbconfig/sizeof"
+rescue LoadError
end
module EnvUtil
@@ -40,12 +42,38 @@ module EnvUtil
DEFAULT_SIGNALS = Signal.list
DEFAULT_SIGNALS.delete("TERM") if /mswin|mingw/ =~ RUBY_PLATFORM
+ RUBYLIB = ENV["RUBYLIB"]
+
+ class << self
+ attr_accessor :subprocess_timeout_scale
+ attr_reader :original_internal_encoding, :original_external_encoding,
+ :original_verbose
+
+ def capture_global_values
+ @original_internal_encoding = Encoding.default_internal
+ @original_external_encoding = Encoding.default_external
+ @original_verbose = $VERBOSE
+ end
+ end
+
+ def apply_timeout_scale(t)
+ if scale = EnvUtil.subprocess_timeout_scale
+ t * scale
+ else
+ t
+ end
+ end
+ module_function :apply_timeout_scale
+
def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr = false,
encoding: nil, timeout: 10, reprieve: 1, timeout_error: Timeout::Error,
stdout_filter: nil, stderr_filter: nil,
signal: :TERM,
- rubybin: EnvUtil.rubybin,
+ rubybin: EnvUtil.rubybin, precommand: nil,
**opt)
+ timeout = apply_timeout_scale(timeout)
+ reprieve = apply_timeout_scale(reprieve) if reprieve
+
in_c, in_p = IO.pipe
out_p, out_c = IO.pipe if capture_stdout
err_p, err_c = IO.pipe if capture_stderr && capture_stderr != :merge_to_stdout
@@ -62,8 +90,11 @@ module EnvUtil
if Array === args and Hash === args.first
child_env.update(args.shift)
end
+ if RUBYLIB and lib = child_env["RUBYLIB"]
+ child_env["RUBYLIB"] = [lib, RUBYLIB].join(File::PATH_SEPARATOR)
+ end
args = [args] if args.kind_of?(String)
- pid = spawn(child_env, rubybin, *args, **opt)
+ pid = spawn(child_env, *precommand, rubybin, *args, **opt)
in_c.close
out_c.close if capture_stdout
err_c.close if capture_stderr && capture_stderr != :merge_to_stdout
@@ -116,7 +147,7 @@ module EnvUtil
stderr = stderr_filter.call(stderr) if stderr_filter
if timeout_error
bt = caller_locations
- msg = "execution of #{bt.shift.label} expired"
+ msg = "execution of #{bt.shift.label} expired timeout (#{timeout} sec)"
msg = Test::Unit::Assertions::FailDesc[status, msg, [stdout, stderr].join("\n")].()
raise timeout_error, msg, bt.map(&:to_s)
end
@@ -127,7 +158,7 @@ module EnvUtil
th.kill if th
end
[in_c, in_p, out_c, out_p, err_c, err_p].each do |io|
- io.close if io && !io.closed?
+ io&.close
end
[th_stdout, th_stderr].each do |th|
th.join if th
@@ -141,30 +172,33 @@ module EnvUtil
end
def verbose_warning
- class << (stderr = "")
- alias write <<
+ class << (stderr = "".dup)
+ alias write concat
+ def flush; end
end
- stderr, $stderr, verbose, $VERBOSE = $stderr, stderr, $VERBOSE, true
+ stderr, $stderr = $stderr, stderr
+ $VERBOSE = true
yield stderr
return $stderr
ensure
- stderr, $stderr, $VERBOSE = $stderr, stderr, verbose
+ stderr, $stderr = $stderr, stderr
+ $VERBOSE = EnvUtil.original_verbose
end
module_function :verbose_warning
def default_warning
- verbose, $VERBOSE = $VERBOSE, false
+ $VERBOSE = false
yield
ensure
- $VERBOSE = verbose
+ $VERBOSE = EnvUtil.original_verbose
end
module_function :default_warning
def suppress_warning
- verbose, $VERBOSE = $VERBOSE, nil
+ $VERBOSE = nil
yield
ensure
- $VERBOSE = verbose
+ $VERBOSE = EnvUtil.original_verbose
end
module_function :suppress_warning
@@ -177,26 +211,18 @@ module EnvUtil
module_function :under_gc_stress
def with_default_external(enc)
- verbose, $VERBOSE = $VERBOSE, nil
- origenc, Encoding.default_external = Encoding.default_external, enc
- $VERBOSE = verbose
+ suppress_warning { Encoding.default_external = enc }
yield
ensure
- verbose, $VERBOSE = $VERBOSE, nil
- Encoding.default_external = origenc
- $VERBOSE = verbose
+ suppress_warning { Encoding.default_external = EnvUtil.original_external_encoding }
end
module_function :with_default_external
def with_default_internal(enc)
- verbose, $VERBOSE = $VERBOSE, nil
- origenc, Encoding.default_internal = Encoding.default_internal, enc
- $VERBOSE = verbose
+ suppress_warning { Encoding.default_internal = enc }
yield
ensure
- verbose, $VERBOSE = $VERBOSE, nil
- Encoding.default_internal = origenc
- $VERBOSE = verbose
+ suppress_warning { Encoding.default_internal = EnvUtil.original_internal_encoding }
end
module_function :with_default_internal
@@ -219,9 +245,12 @@ module EnvUtil
if /darwin/ =~ RUBY_PLATFORM
DIAGNOSTIC_REPORTS_PATH = File.expand_path("~/Library/Logs/DiagnosticReports")
DIAGNOSTIC_REPORTS_TIMEFORMAT = '%Y-%m-%d-%H%M%S'
- def self.diagnostic_reports(signame, cmd, pid, now)
+ @ruby_install_name = RbConfig::CONFIG['RUBY_INSTALL_NAME']
+
+ def self.diagnostic_reports(signame, pid, now)
return unless %w[ABRT QUIT SEGV ILL TRAP].include?(signame)
- cmd = File.basename(cmd)
+ cmd = File.basename(rubybin)
+ cmd = @ruby_install_name if "ruby-runner#{RbConfig::CONFIG["EXEEXT"]}" == cmd
path = DIAGNOSTIC_REPORTS_PATH
timeformat = DIAGNOSTIC_REPORTS_TIMEFORMAT
pat = "#{path}/#{cmd}_#{now.strftime(timeformat)}[-_]*.crash"
@@ -240,7 +269,7 @@ module EnvUtil
nil
end
else
- def self.diagnostic_reports(signame, cmd, pid, now)
+ def self.diagnostic_reports(signame, pid, now)
end
end
@@ -253,10 +282,7 @@ module EnvUtil
end
end
-begin
- require 'rbconfig'
-rescue LoadError
-else
+if defined?(RbConfig)
module RbConfig
@ruby = EnvUtil.rubybin
class << self
@@ -264,10 +290,9 @@ else
attr_reader :ruby
end
dir = File.dirname(ruby)
- name = File.basename(ruby, CONFIG['EXEEXT'])
CONFIG['bindir'] = dir
- CONFIG['ruby_install_name'] = name
- CONFIG['RUBY_INSTALL_NAME'] = name
Gem::ConfigMap[:bindir] = dir if defined?(Gem::ConfigMap)
end
end
+
+EnvUtil.capture_global_values
diff --git a/test/lib/find_executable.rb b/test/lib/find_executable.rb
index 0ddd307f8c..89c6fb8f3b 100644
--- a/test/lib/find_executable.rb
+++ b/test/lib/find_executable.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require "rbconfig"
module EnvUtil
diff --git a/test/lib/iseq_loader_checker.rb b/test/lib/iseq_loader_checker.rb
index f368551883..1a1a694834 100644
--- a/test/lib/iseq_loader_checker.rb
+++ b/test/lib/iseq_loader_checker.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require '-test-/iseq_load/iseq_load'
diff --git a/test/lib/jit_support.rb b/test/lib/jit_support.rb
new file mode 100644
index 0000000000..8d17ed9552
--- /dev/null
+++ b/test/lib/jit_support.rb
@@ -0,0 +1,74 @@
+require 'rbconfig'
+
+module JITSupport
+ JIT_TIMEOUT = 600 # 10min for each...
+ JIT_SUCCESS_PREFIX = 'JIT success \(\d+\.\dms\)'
+ UNSUPPORTED_COMPILERS = [
+ %r[\A.*/bin/intel64/icc\b],
+ %r[\A/opt/developerstudio\d+\.\d+/bin/cc\z],
+ ]
+ # freebsd12: cc1 internal failure https://rubyci.org/logs/rubyci.s3.amazonaws.com/freebsd12/ruby-master/log/20200306T103003Z.fail.html.gz
+ # rhel8: one or more PCH files were found, but they were invalid https://rubyci.org/logs/rubyci.s3.amazonaws.com/rhel8/ruby-master/log/20200306T153003Z.fail.html.gz
+ PENDING_RUBYCI_NICKNAMES = %w[
+ freebsd12
+ rhel8
+ ]
+
+ module_function
+ # Run Ruby script with --jit-wait (Synchronous JIT compilation).
+ # Returns [stdout, stderr]
+ def eval_with_jit(env = nil, script, **opts)
+ stdout, stderr = nil, nil
+ # retry 3 times while cc1 error happens.
+ 3.times do |i|
+ stdout, stderr, status = eval_with_jit_without_retry(env, script, **opts)
+ assert_equal(true, status.success?, "Failed to run script with JIT:\n#{code_block(script)}\nstdout:\n#{code_block(stdout)}\nstderr:\n#{code_block(stderr)}")
+ break unless retried_stderr?(stderr)
+ end
+ [stdout, stderr]
+ end
+
+ def eval_with_jit_without_retry(env = nil, script, verbose: 0, min_calls: 5, save_temps: false, max_cache: 1000, wait: true, timeout: JIT_TIMEOUT)
+ args = [
+ '--disable-gems', "--jit-verbose=#{verbose}",
+ "--jit-min-calls=#{min_calls}", "--jit-max-cache=#{max_cache}",
+ ]
+ args << '--jit-wait' if wait
+ args << '--jit-save-temps' if save_temps
+ args << '-e' << script
+ base_env = { 'MJIT_SEARCH_BUILD_DIR' => 'true' } # workaround to skip requiring `make install` for `make test-all`
+ if preloadenv = RbConfig::CONFIG['PRELOADENV'] and !preloadenv.empty?
+ so = "mjit_build_dir.#{RbConfig::CONFIG['SOEXT']}"
+ base_env[preloadenv] = File.realpath(so) rescue nil
+ end
+ args.unshift(env ? base_env.merge!(env) : base_env)
+ EnvUtil.invoke_ruby(args,
+ '', true, true, timeout: timeout,
+ )
+ end
+
+ def supported?
+ return @supported if defined?(@supported)
+ @supported = UNSUPPORTED_COMPILERS.all? do |regexp|
+ !regexp.match?(RbConfig::CONFIG['MJIT_CC'])
+ end && RbConfig::CONFIG["MJIT_SUPPORT"] != 'no' && !PENDING_RUBYCI_NICKNAMES.include?(ENV['RUBYCI_NICKNAME'])
+ end
+
+ def remove_mjit_logs(stderr)
+ if RubyVM::MJIT.enabled? # utility for -DFORCE_MJIT_ENABLE
+ stderr.gsub(/^MJIT warning: Skipped to compile unsupported instruction: \w+\n/m, '')
+ else
+ stderr
+ end
+ end
+
+ def code_block(code)
+ %Q["""\n#{code}\n"""\n\n]
+ end
+
+ # We're retrying cc1 not found error on gcc, which should be solved in the future but ignored for now.
+ def retried_stderr?(stderr)
+ RbConfig::CONFIG['CC'].start_with?('gcc') &&
+ stderr.include?("error trying to exec 'cc1': execvp: No such file or directory")
+ end
+end
diff --git a/test/lib/leakchecker.rb b/test/lib/leakchecker.rb
index 355f93dc79..af9200bf77 100644
--- a/test/lib/leakchecker.rb
+++ b/test/lib/leakchecker.rb
@@ -1,10 +1,12 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
class LeakChecker
def initialize
@fd_info = find_fds
@tempfile_info = find_tempfiles
@thread_info = find_threads
@env_info = find_env
+ @encoding_info = find_encodings
+ @old_verbose = $VERBOSE
end
def check(test_name)
@@ -12,11 +14,22 @@ class LeakChecker
check_fd_leak(test_name),
check_thread_leak(test_name),
check_tempfile_leak(test_name),
- check_env(test_name)
+ check_env(test_name),
+ check_encodings(test_name),
+ check_safe(test_name),
+ check_verbose(test_name),
]
GC.start if leaks.any?
end
+ def check_safe test_name
+ puts "#{test_name}: $SAFE == #{$SAFE}" unless $SAFE == 0
+ end
+
+ def check_verbose test_name
+ puts "#{test_name}: $VERBOSE == #{$VERBOSE}" unless @old_verbose == $VERBOSE
+ end
+
def find_fds
if IO.respond_to?(:console) and (m = IO.method(:console)).arity.nonzero?
m[:close]
@@ -61,7 +74,7 @@ class LeakChecker
(h[fd] ||= []) << [io, autoclose, inspect]
}
fd_leaked.each {|fd|
- str = ''
+ str = ''.dup
if h[fd]
str << ' :'
h[fd].map {|io, autoclose, inspect|
@@ -114,7 +127,9 @@ class LeakChecker
if prev_count == count
[prev_count, []]
else
- tempfiles = ObjectSpace.each_object(Tempfile).find_all {|t| t.path }
+ tempfiles = ObjectSpace.each_object(Tempfile).find_all {|t|
+ t.instance_variable_defined?(:@tmpfile) and t.path
+ }
[count, tempfiles]
end
end
@@ -195,7 +210,31 @@ class LeakChecker
return true
end
+ def find_encodings
+ [Encoding.default_internal, Encoding.default_external]
+ end
+
+ def check_encodings(test_name)
+ old_internal, old_external = @encoding_info
+ new_internal, new_external = find_encodings
+ leaked = false
+ if new_internal != old_internal
+ leaked = true
+ puts "Encoding.default_internal changed: #{test_name} : #{old_internal.inspect} to #{new_internal.inspect}"
+ end
+ if new_external != old_external
+ leaked = true
+ puts "Encoding.default_external changed: #{test_name} : #{old_external.inspect} to #{new_external.inspect}"
+ end
+ @encoding_info = [new_internal, new_external]
+ return leaked
+ end
+
def puts(*a)
- MiniTest::Unit.output.puts(*a)
+ output = MiniTest::Unit.output
+ if defined?(output.set_encoding)
+ output.set_encoding(nil, nil)
+ end
+ output.puts(*a)
end
end
diff --git a/test/lib/memory_status.rb b/test/lib/memory_status.rb
index 0f6d4db91b..ad002b2dda 100644
--- a/test/lib/memory_status.rb
+++ b/test/lib/memory_status.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require '-test-/memory_status.so'
rescue LoadError
diff --git a/test/lib/minitest/autorun.rb b/test/lib/minitest/autorun.rb
index 40640b2580..844096623c 100644
--- a/test/lib/minitest/autorun.rb
+++ b/test/lib/minitest/autorun.rb
@@ -1,5 +1,5 @@
# encoding: utf-8
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require 'rubygems'
diff --git a/test/lib/minitest/benchmark.rb b/test/lib/minitest/benchmark.rb
index 21f0b29d50..b3f2bc28b3 100644
--- a/test/lib/minitest/benchmark.rb
+++ b/test/lib/minitest/benchmark.rb
@@ -1,5 +1,5 @@
# encoding: utf-8
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'minitest/unit'
diff --git a/test/lib/minitest/mock.rb b/test/lib/minitest/mock.rb
index 07e88113ea..224b06cb89 100644
--- a/test/lib/minitest/mock.rb
+++ b/test/lib/minitest/mock.rb
@@ -1,5 +1,5 @@
# encoding: utf-8
-# frozen_string_literal: false
+# frozen_string_literal: true
class MockExpectationError < StandardError; end # :nodoc:
diff --git a/test/lib/minitest/unit.rb b/test/lib/minitest/unit.rb
index b71e8b85b0..25fd15d093 100644
--- a/test/lib/minitest/unit.rb
+++ b/test/lib/minitest/unit.rb
@@ -1,5 +1,5 @@
# encoding: utf-8
-# frozen_string_literal: false
+# frozen_string_literal: true
require "optparse"
require "rbconfig"
@@ -872,35 +872,46 @@ module MiniTest
suites = TestCase.send "#{type}_suites"
return if suites.empty?
- start = Time.now
-
puts
puts "# Running #{type}s:"
puts
@test_count, @assertion_count = 0, 0
+ test_count = assertion_count = 0
sync = output.respond_to? :"sync=" # stupid emacs
old_sync, output.sync = output.sync, true if sync
- results = _run_suites suites, type
-
- @test_count = results.inject(0) { |sum, (tc, _)| sum + tc }
- @assertion_count = results.inject(0) { |sum, (_, ac)| sum + ac }
+ count = 0
+ begin
+ start = Time.now
+
+ results = _run_suites suites, type
+
+ @test_count = results.inject(0) { |sum, (tc, _)| sum + tc }
+ @assertion_count = results.inject(0) { |sum, (_, ac)| sum + ac }
+ test_count += @test_count
+ assertion_count += @assertion_count
+ t = Time.now - start
+ count += 1
+ unless @repeat_count
+ puts
+ puts
+ end
+ puts "Finished%s %ss in %.6fs, %.4f tests/s, %.4f assertions/s.\n" %
+ [(@repeat_count ? "(#{count}/#{@repeat_count}) " : ""), type,
+ t, @test_count.fdiv(t), @assertion_count.fdiv(t)]
+ end while @repeat_count && count < @repeat_count &&
+ report.empty? && failures.zero? && errors.zero?
output.sync = old_sync if sync
- t = Time.now - start
-
- puts
- puts
- puts "Finished #{type}s in %.6fs, %.4f tests/s, %.4f assertions/s." %
- [t, test_count / t, assertion_count / t]
-
report.each_with_index do |msg, i|
puts "\n%3d) %s" % [i + 1, msg]
end
puts
+ @test_count = test_count
+ @assertion_count = assertion_count
status
end
@@ -945,7 +956,9 @@ module MiniTest
puts if @verbose
$stdout.flush
- leakchecker.check("#{inst.class}\##{inst.__name__}")
+ unless defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # compiler process is wrongly considered as leak
+ leakchecker.check("#{inst.class}\##{inst.__name__}")
+ end
inst._assertions
}
@@ -1010,6 +1023,7 @@ module MiniTest
@verbose = false
@mutex = Thread::Mutex.new
@info_signal = Signal.list['INFO']
+ @repeat_count = nil
end
def synchronize # :nodoc:
@@ -1157,6 +1171,14 @@ module MiniTest
def windows? platform = RUBY_PLATFORM
/mswin|mingw/ =~ platform
end
+
+ ##
+ # Is this running on mingw?
+
+ def mingw? platform = RUBY_PLATFORM
+ /mingw/ =~ platform
+ end
+
end
##
diff --git a/test/lib/profile_test_all.rb b/test/lib/profile_test_all.rb
index 4496d3f780..4771b72afb 100644
--- a/test/lib/profile_test_all.rb
+++ b/test/lib/profile_test_all.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# purpose:
# Profile memory usage of each tests.
diff --git a/test/lib/test/unit.rb b/test/lib/test/unit.rb
index 2c4a052b6f..35751208a9 100644
--- a/test/lib/test/unit.rb
+++ b/test/lib/test/unit.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
gem 'minitest', '< 5.0.0' if defined? Gem
rescue Gem::LoadError
@@ -106,10 +106,10 @@ module Test
elsif negative.empty? and positive.size == 1 and pos_pat !~ positive[0]
filter = positive[0]
else
- filter = Regexp.union(*positive.map! {|s| s[pos_pat, 1] || "\\A#{Regexp.quote(s)}\\z"})
+ filter = Regexp.union(*positive.map! {|s| Regexp.new(s[pos_pat, 1] || "\\A#{Regexp.quote(s)}\\z")})
end
unless negative.empty?
- negative = Regexp.union(*negative.map! {|s| s[neg_pat, 1]})
+ negative = Regexp.union(*negative.map! {|s| Regexp.new(s[neg_pat, 1])})
filter = /\A(?=.*#{filter})(?!.*#{negative})/
end
if Regexp === filter
@@ -134,6 +134,27 @@ module Test
options
end
+ def non_options(files, options)
+ @jobserver = nil
+ makeflags = ENV.delete("MAKEFLAGS")
+ if !options[:parallel] and
+ /(?:\A|\s)--jobserver-(?:auth|fds)=(\d+),(\d+)/ =~ makeflags
+ begin
+ r = IO.for_fd($1.to_i(10), "rb", autoclose: false)
+ w = IO.for_fd($2.to_i(10), "wb", autoclose: false)
+ rescue
+ r.close if r
+ nil
+ else
+ r.close_on_exec = true
+ w.close_on_exec = true
+ @jobserver = [r, w]
+ options[:parallel] ||= 1
+ end
+ end
+ super
+ end
+
def status(*args)
result = super
raise @interrupt if @interrupt
@@ -148,13 +169,9 @@ module Test
options[:retry] = true
- opts.on '-j N', '--jobs N', "Allow run tests with N jobs at once" do |a|
- if /^t/ =~ a
- options[:testing] = true # For testing
- options[:parallel] = a[1..-1].to_i
- else
- options[:parallel] = a.to_i
- end
+ opts.on '-j N', '--jobs N', /\A(t)?(\d+)\z/, "Allow run tests with N jobs at once" do |_, t, a|
+ options[:testing] = true & t # For testing
+ options[:parallel] = a.to_i
end
opts.on '--separate', "Restart job process after one testcase has done" do
@@ -170,15 +187,17 @@ module Test
options[:retry] = false
end
- opts.on '--ruby VAL', "Path to ruby; It'll have used at -j option" do |a|
+ opts.on '--ruby VAL', "Path to ruby which is used at -j option" do |a|
options[:ruby] = a.split(/ /).reject(&:empty?)
end
end
class Worker
def self.launch(ruby,args=[])
+ scale = EnvUtil.subprocess_timeout_scale
io = IO.popen([*ruby, "-W1",
"#{File.dirname(__FILE__)}/unit/parallel.rb",
+ *("--subprocess-timeout-scale=#{scale}" if scale),
*args], "rb+")
new(io, io.pid, :waiting)
end
@@ -211,7 +230,7 @@ module Test
rescue Errno::EPIPE
died
rescue IOError
- raise unless ["stream closed","closed stream"].include? $!.message
+ raise unless /stream closed|closed stream/ =~ $!.message
died
end
end
@@ -237,7 +256,6 @@ module Test
return if @io.closed?
@quit_called = true
@io.puts "quit"
- @io.close
end
def kill
@@ -277,9 +295,20 @@ module Test
end
+ def flush_job_tokens
+ if @jobserver
+ r, w = @jobserver.shift(2)
+ @jobserver = nil
+ w << @job_tokens.slice!(0..-1)
+ r.close
+ w.close
+ end
+ end
+
def after_worker_down(worker, e=nil, c=false)
return unless @options[:parallel]
return if @interrupt
+ flush_job_tokens
warn e if e
real_file = worker.real_file and warn "running file: #{real_file}"
@need_quit = true
@@ -295,6 +324,10 @@ module Test
def after_worker_quit(worker)
return unless @options[:parallel]
return if @interrupt
+ worker.close
+ if @jobserver and (token = @job_tokens.slice!(0))
+ @jobserver[1] << token
+ end
@workers.delete(worker)
@dead_workers << worker
@ios = @workers.map(&:io)
@@ -348,6 +381,11 @@ module Test
end
end
+ FakeClass = Struct.new(:name)
+ def fake_class(name)
+ (@fake_classes ||= {})[name] ||= FakeClass.new(name)
+ end
+
def deal(io, type, result, rep, shutting_down = false)
worker = @workers_hash[io]
cmd = worker.read
@@ -361,10 +399,13 @@ module Test
bang = $1
worker.status = :ready
- return nil unless task = @tasks.shift
+ unless task = @tasks.shift
+ worker.quit
+ return nil
+ end
if @options[:separate] and not bang
worker.quit
- worker = add_worker
+ worker = launch_worker
end
worker.run(task, type)
@test_count += 1
@@ -382,6 +423,14 @@ module Test
$:.push(*r[4]).uniq!
jobs_status(worker) if @options[:job_status] == :replace
return true
+ when /^record (.+?)$/
+ begin
+ r = Marshal.load($1.unpack("m")[0])
+ rescue => e
+ print "unknown record: #{e.message} #{$1.unpack("m")[0].dump}"
+ return true
+ end
+ record(fake_class(r[0]), *r[1..-1])
when /^p (.+?)$/
del_jobs_status
print $1.unpack("m")[0]
@@ -408,8 +457,7 @@ module Test
return
end
- # Require needed things for parallel running
- require 'thread'
+ # Require needed thing for parallel running
require 'timeout'
@tasks = @files.dup # Array of filenames.
@need_quit = false
@@ -421,8 +469,9 @@ module Test
@workers = [] # Array of workers.
@workers_hash = {} # out-IO => worker
@ios = [] # Array of worker IOs
+ @job_tokens = String.new(encoding: Encoding::ASCII_8BIT) if @jobserver
begin
- @options[:parallel].times {launch_worker}
+ [@tasks.size, @options[:parallel]].min.times {launch_worker}
while _io = IO.select(@ios)[0]
break if _io.any? do |io|
@@ -430,6 +479,13 @@ module Test
(deal(io, type, result, rep).nil? and
!@workers.any? {|x| [:running, :prepare].include? x.status})
end
+ if @jobserver and @job_tokens and !@tasks.empty? and !@workers.any? {|x| x.status == :ready}
+ t = @jobserver[0].read_nonblock([@tasks.size, @options[:parallel]].min, exception: false)
+ if String === t
+ @job_tokens << t
+ t.size.times {launch_worker}
+ end
+ end
end
rescue Interrupt => ex
@interrupt = ex
@@ -443,8 +499,10 @@ module Test
end
quit_workers
+ flush_job_tokens
unless @interrupt || !@options[:retry] || @need_quit
+ parallel = @options[:parallel]
@options[:parallel] = false
suites, rep = rep.partition {|r| r[:testcase] && r[:file] && r[:report].any? {|e| !e[2].is_a?(MiniTest::Skip)}}
suites.map {|r| r[:file]}.uniq.each {|file| require file}
@@ -454,6 +512,7 @@ module Test
puts "\n""Retrying..."
_run_suites(suites, type)
end
+ @options[:parallel] = parallel
end
unless @options[:retry]
del_status_line or puts
@@ -525,7 +584,6 @@ module Test
end
end
- private
def _run_suites(suites, type)
result = super
report.reject!{|r| r.start_with? "Skipped:" } if @options[:hide_skip]
@@ -536,6 +594,57 @@ module Test
end
end
+ module Statistics
+ def update_list(list, rec, max)
+ if i = list.empty? ? 0 : list.bsearch_index {|*a| yield(*a)}
+ list[i, 0] = [rec]
+ list[max..-1] = [] if list.size >= max
+ end
+ end
+
+ def record(suite, method, assertions, time, error)
+ if @options.values_at(:longest, :most_asserted).any?
+ @tops ||= {}
+ rec = [suite.name, method, assertions, time, error]
+ if max = @options[:longest]
+ update_list(@tops[:longest] ||= [], rec, max) {|_,_,_,t,_|t<time}
+ end
+ if max = @options[:most_asserted]
+ update_list(@tops[:most_asserted] ||= [], rec, max) {|_,_,a,_,_|a<assertions}
+ end
+ end
+ # (((@record ||= {})[suite] ||= {})[method]) = [assertions, time, error]
+ super
+ end
+
+ def run(*args)
+ result = super
+ if @tops ||= nil
+ @tops.each do |t, list|
+ if list
+ puts "#{t.to_s.tr('_', ' ')} tests:"
+ list.each {|suite, method, assertions, time, error|
+ printf "%5.2fsec(%d): %s#%s\n", time, assertions, suite, method
+ }
+ end
+ end
+ end
+ result
+ end
+
+ private
+ def setup_options(opts, options)
+ super
+ opts.separator "statistics options:"
+ opts.on '--longest=N', Integer, 'Show longest N tests' do |n|
+ options[:longest] = n
+ end
+ opts.on '--most-asserted=N', Integer, 'Show most asserted N tests' do |n|
+ options[:most_asserted] = n
+ end
+ end
+ end
+
module StatusLine # :nodoc: all
def terminal_width
unless @terminal_width ||= nil
@@ -737,7 +846,7 @@ module Test
begin
require "rbconfig"
rescue LoadError
- warn "#{caller(1)[0]}: warning: Parallel running disabled because can't get path to ruby; run specify with --ruby argument"
+ warn "#{caller(1, 1)[0]}: warning: Parallel running disabled because can't get path to ruby; run specify with --ruby argument"
options[:parallel] = nil
else
options[:ruby] ||= [RbConfig.ruby]
@@ -784,10 +893,15 @@ module Test
next if f.empty?
path = f
end
- if !(match = (Dir["#{path}/**/#{@@testfile_prefix}_*.rb"] + Dir["#{path}/**/*_#{@@testfile_suffix}.rb"]).uniq).empty?
+ if f.end_with?(File::SEPARATOR) or !f.include?(File::SEPARATOR) or File.directory?(path)
+ match = (Dir["#{path}/**/#{@@testfile_prefix}_*.rb"] + Dir["#{path}/**/*_#{@@testfile_suffix}.rb"]).uniq
+ else
+ match = Dir[path]
+ end
+ if !match.empty?
if reject
match.reject! {|n|
- n[(prefix.length+1)..-1] if prefix
+ n = n[(prefix.length+1)..-1] if prefix
reject_pat =~ n
}
end
@@ -853,6 +967,22 @@ module Test
end
end
+ module RepeatOption # :nodoc: all
+ def setup_options(parser, options)
+ super
+ options[:repeat_count] = nil
+ parser.separator "repeat options:"
+ parser.on '--repeat-count=NUM', "Number of times to repeat", Integer do |n|
+ options[:repeat_count] = n
+ end
+ end
+
+ def _run_anything(type)
+ @repeat_count = @options[:repeat_count]
+ super
+ end
+ end
+
module ExcludesOption # :nodoc: all
class ExcludedMethods < Struct.new(:excludes)
def exclude(name, reason)
@@ -902,6 +1032,7 @@ module Test
excludes = excludes.split(File::PATH_SEPARATOR)
end
options[:excludes] = excludes || []
+ parser.separator "excludes options:"
parser.on '-X', '--excludes-dir DIRECTORY', "Directory name of exclude files" do |d|
options[:excludes].concat d.split(File::PATH_SEPARATOR)
end
@@ -915,15 +1046,37 @@ module Test
end
end
+ module SubprocessOption
+ def setup_options(parser, options)
+ super
+ parser.separator "subprocess options:"
+ parser.on '--subprocess-timeout-scale NUM', "Scale subprocess timeout", Float do |scale|
+ raise OptionParser::InvalidArgument, "timeout scale must be positive" unless scale > 0
+ options[:timeout_scale] = scale
+ end
+ end
+
+ def non_options(files, options)
+ if scale = options[:timeout_scale] or
+ (scale = ENV["RUBY_TEST_SUBPROCESS_TIMEOUT_SCALE"] and (scale = scale.to_f) > 0)
+ EnvUtil.subprocess_timeout_scale = scale
+ end
+ super
+ end
+ end
+
class Runner < MiniTest::Unit # :nodoc: all
include Test::Unit::Options
include Test::Unit::StatusLine
include Test::Unit::Parallel
+ include Test::Unit::Statistics
include Test::Unit::Skipping
include Test::Unit::GlobOption
+ include Test::Unit::RepeatOption
include Test::Unit::LoadPathOption
include Test::Unit::GCStressOption
include Test::Unit::ExcludesOption
+ include Test::Unit::SubprocessOption
include Test::Unit::RunCount
class << self; undef autorun; end
diff --git a/test/lib/test/unit/assertions.rb b/test/lib/test/unit/assertions.rb
index 9277fbc6b6..dfa929a136 100644
--- a/test/lib/test/unit/assertions.rb
+++ b/test/lib/test/unit/assertions.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'minitest/unit'
require 'pp'
@@ -89,11 +89,13 @@ module Test
}
return e
- end
-
- exp = exp.first if exp.size == 1
+ ensure
+ unless e
+ exp = exp.first if exp.size == 1
- flunk(message(msg) {"#{mu_pp(exp)} expected but nothing was raised"})
+ flunk(message(msg) {"#{mu_pp(exp)} expected but nothing was raised"})
+ end
+ end
end
def assert_raises(*exp, &b)
@@ -361,15 +363,41 @@ EOT
#
# assert_respond_to("hello", :reverse) #Succeeds
# assert_respond_to("hello", :does_not_exist) #Fails
- def assert_respond_to obj, (meth, priv), msg = nil
- if priv
+ def assert_respond_to(obj, (meth, *priv), msg = nil)
+ unless priv.empty?
msg = message(msg) {
- "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}#{" privately" if priv}"
+ "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}#{" privately" if priv[0]}"
}
- return assert obj.respond_to?(meth, priv), msg
+ return assert obj.respond_to?(meth, *priv), msg
end
#get rid of overcounting
- super if !caller[0].rindex(MINI_DIR, 0) || !obj.respond_to?(meth)
+ if caller_locations(1, 1)[0].path.start_with?(MINI_DIR)
+ return if obj.respond_to?(meth)
+ end
+ super(obj, meth, msg)
+ end
+
+ # :call-seq:
+ # assert_not_respond_to( object, method, failure_message = nil )
+ #
+ #Tests if the given Object does not respond to +method+.
+ #
+ #An optional failure message may be provided as the final argument.
+ #
+ # assert_not_respond_to("hello", :reverse) #Fails
+ # assert_not_respond_to("hello", :does_not_exist) #Succeeds
+ def assert_not_respond_to(obj, (meth, *priv), msg = nil)
+ unless priv.empty?
+ msg = message(msg) {
+ "Expected #{mu_pp(obj)} (#{obj.class}) to not respond to ##{meth}#{" privately" if priv[0]}"
+ }
+ return assert !obj.respond_to?(meth, *priv), msg
+ end
+ #get rid of overcounting
+ if caller_locations(1, 1)[0].path.start_with?(MINI_DIR)
+ return unless obj.respond_to?(meth)
+ end
+ refute_respond_to(obj, meth, msg)
end
# :call-seq:
@@ -426,7 +454,7 @@ EOT
ms = instance_methods(true).map {|sym| sym.to_s }
ms.grep(/\Arefute_/) do |m|
- mname = ('assert_not_' << m.to_s[/.*?_(.*)/, 1])
+ mname = ('assert_not_'.dup << m.to_s[/.*?_(.*)/, 1])
alias_method(mname, m) unless ms.include? mname
end
alias assert_include assert_includes
@@ -446,61 +474,65 @@ EOT
failed = []
obj.each do |*a, &b|
if blk.call(*a, &b)
- failed << a.size > 1 ? a : a[0]
+ failed << (a.size > 1 ? a : a[0])
end
end
assert(failed.empty?, message(m) {failed.pretty_inspect})
end
- # compatiblity with test-unit
+ # compatibility with test-unit
alias pend skip
- def assert_valid_syntax(code, fname = caller_locations(1, 1)[0], mesg = fname.to_s, verbose: nil)
- code = code.b
- code.sub!(/\A(?:\xef\xbb\xbf)?(\s*\#.*$)*(\n)?/n) {
- "#$&#{"\n" if $1 && !$2}BEGIN{throw tag, :ok}\n"
- }
- code.force_encoding(Encoding::UTF_8)
+ if defined?(RubyVM::InstructionSequence)
+ def syntax_check(code, fname, line)
+ code = code.dup.force_encoding(Encoding::UTF_8)
+ RubyVM::InstructionSequence.compile(code, fname, fname, line)
+ :ok
+ end
+ else
+ def syntax_check(code, fname, line)
+ code = code.b
+ code.sub!(/\A(?:\xef\xbb\xbf)?(\s*\#.*$)*(\n)?/n) {
+ "#$&#{"\n" if $1 && !$2}BEGIN{throw tag, :ok}\n"
+ }
+ code = code.force_encoding(Encoding::UTF_8)
+ catch {|tag| eval(code, binding, fname, line - 1)}
+ end
+ end
+
+ def prepare_syntax_check(code, fname = caller_locations(2, 1)[0], mesg = fname.to_s, verbose: nil)
verbose, $VERBOSE = $VERBOSE, verbose
- yield if defined?(yield)
case
when Array === fname
fname, line = *fname
when defined?(fname.path) && defined?(fname.lineno)
fname, line = fname.path, fname.lineno
else
- line = 0
- end
- assert_nothing_raised(SyntaxError, mesg) do
- assert_equal(:ok, catch {|tag| eval(code, binding, fname, line)}, mesg)
+ line = 1
end
+ yield(code, fname, line, mesg)
ensure
$VERBOSE = verbose
end
- def assert_syntax_error(code, error, fname = caller_locations(1, 1)[0], mesg = fname.to_s)
- code = code.b
- code.sub!(/\A(?:\xef\xbb\xbf)?(\s*\#.*$)*(\n)?/n) {
- "#$&#{"\n" if $1 && !$2}BEGIN{throw tag, :ng}\n"
- }
- code.force_encoding(Encoding::US_ASCII)
- verbose, $VERBOSE = $VERBOSE, nil
- yield if defined?(yield)
- case
- when Array === fname
- fname, line = *fname
- when defined?(fname.path) && defined?(fname.lineno)
- fname, line = fname.path, fname.lineno
- else
- line = 0
+ def assert_valid_syntax(code, *args, **opt)
+ prepare_syntax_check(code, *args, **opt) do |src, fname, line, mesg|
+ yield if defined?(yield)
+ assert_nothing_raised(SyntaxError, mesg) do
+ assert_equal(:ok, syntax_check(src, fname, line), mesg)
+ end
end
- e = assert_raise(SyntaxError, mesg) do
- catch {|tag| eval(code, binding, fname, line)}
+ end
+
+ def assert_syntax_error(code, error, *args)
+ prepare_syntax_check(code, *args) do |src, fname, line, mesg|
+ yield if defined?(yield)
+ e = assert_raise(SyntaxError, mesg) do
+ syntax_check(src, fname, line)
+ end
+ assert_match(error, e.message, mesg)
+ e
end
- assert_match(error, e.message, mesg)
- e
- ensure
- $VERBOSE = verbose
end
def assert_normal_exit(testsrc, message = '', child_env: nil, **opt)
@@ -522,14 +554,15 @@ EOT
signame = Signal.signame(signo)
sigdesc = "signal #{signo}"
end
- log = EnvUtil.diagnostic_reports(signame, EnvUtil.rubybin, pid, now)
+ log = EnvUtil.diagnostic_reports(signame, pid, now)
if signame
sigdesc = "SIG#{signame} (#{sigdesc})"
end
if status.coredump?
- sigdesc << " (core dumped)"
+ sigdesc = "#{sigdesc} (core dumped)"
end
- full_message = ''
+ full_message = ''.dup
+ message = message.call if Proc === message
if message and !message.empty?
full_message << message << "\n"
end
@@ -537,11 +570,11 @@ EOT
full_message << " exit #{status.exitstatus}" if status.exited?
full_message << " killed by #{sigdesc}" if sigdesc
if out and !out.empty?
- full_message << "\n#{out.b.gsub(/^/, '| ')}"
- full_message << "\n" if /\n\z/ !~ full_message
+ full_message << "\n" << out.b.gsub(/^/, '| ')
+ full_message.sub!(/(?<!\n)\z/, "\n")
end
if log
- full_message << "\n#{log.b.gsub(/^/, '| ')}"
+ full_message << "Diagnostic reports:\n" << log.b.gsub(/^/, '| ')
end
full_message
end
@@ -550,9 +583,11 @@ EOT
def assert_in_out_err(args, test_stdin = "", test_stdout = [], test_stderr = [], message = nil,
success: nil, **opt)
+ args = Array(args).dup
+ args.insert((Hash === args[0] ? 1 : 0), '--disable=gems')
stdout, stderr, status = EnvUtil.invoke_ruby(args, test_stdin, true, true, **opt)
if signo = status.termsig
- EnvUtil.diagnostic_reports(Signal.signame(signo), EnvUtil.rubybin, status.pid, Time.now)
+ EnvUtil.diagnostic_reports(Signal.signame(signo), status.pid, Time.now)
end
if block_given?
raise "test_stdout ignored, use block only or without block" if test_stdout != []
@@ -601,14 +636,13 @@ EOT
file ||= loc.path
line ||= loc.lineno
end
- line -= 5 # lines until src
src = <<eom
-# -*- coding: #{src.encoding}; -*-
+# -*- coding: #{line += __LINE__; src.encoding}; -*-
require #{__dir__.dump};include Test::Unit::Assertions
END {
puts [Marshal.dump($!)].pack('m'), "assertions=\#{self._assertions}"
}
-#{src}
+#{line -= __LINE__; src}
class Test::Unit::Runner
@@stop_auto_run = true
end
@@ -633,7 +667,7 @@ eom
else
res.set_backtrace(caller)
end
- raise res
+ raise res unless SystemExit === res
end
# really is it succeed?
@@ -646,8 +680,8 @@ eom
end
def assert_warning(pat, msg = nil)
- stderr = EnvUtil.verbose_warning {
- EnvUtil.with_default_internal(pat.encoding) {
+ stderr = EnvUtil.with_default_internal(pat.encoding) {
+ EnvUtil.verbose_warning {
yield
}
}
@@ -659,7 +693,20 @@ eom
assert_warning(*args) {$VERBOSE = false; yield}
end
+ def assert_no_warning(pat, msg = nil)
+ stderr = EnvUtil.verbose_warning {
+ EnvUtil.with_default_internal(pat.encoding) {
+ yield
+ }
+ }
+ msg = message(msg) {diff pat, stderr}
+ refute(pat === stderr, msg)
+ end
+
def assert_no_memory_leak(args, prepare, code, message=nil, limit: 2.0, rss: false, **opt)
+ # TODO: consider choosing some appropriate limit for MJIT and stop skipping this once it does not randomly fail
+ skip 'assert_no_memory_leak may consider MJIT memory usage as leak' if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
+
require_relative '../../memory_status'
raise MiniTest::Skip, "unsupported platform" unless defined?(Memory::Status)
@@ -701,6 +748,27 @@ eom
skip
end
+ def assert_cpu_usage_low(msg = nil, pct: 0.01)
+ require 'benchmark'
+
+ tms = Benchmark.measure(msg || '') { yield }
+ max = pct * tms.real
+ if tms.real < 0.1 # TIME_QUANTUM_USEC in thread_pthread.c
+ warn "test #{msg || 'assert_cpu_usage_low'} too short to be accurate"
+ end
+
+ # kernel resolution can limit the minimum time we can measure
+ # [ruby-core:81540]
+ min_hz = windows? ? 67 : 100
+ min_measurable = 1.0 / min_hz
+ min_measurable *= 1.10 # add a little (10%) to account for misc. overheads
+ if max < min_measurable
+ max = min_measurable
+ end
+
+ assert_operator tms.total, :<=, max, msg
+ end
+
def assert_is_minus_zero(f)
assert(1.0/f == -Float::INFINITY, "#{f} is not -0.0")
end
@@ -730,13 +798,13 @@ eom
msg = message(msg) {
expect_msg = "Expected #{mu_pp pattern}\n"
if /\n[^\n]/ =~ rest
- actual_mesg = "to match\n"
+ actual_mesg = +"to match\n"
rest.scan(/.*\n+/) {
actual_mesg << ' ' << $&.inspect << "+\n"
}
actual_mesg.sub!(/\+\n\z/, '')
else
- actual_mesg = "to match #{mu_pp rest}"
+ actual_mesg = "to match " + mu_pp(rest)
end
actual_mesg << "\nafter #{i} patterns with #{actual.length - rest.length} characters"
expect_msg + actual_mesg
@@ -793,7 +861,7 @@ eom
end
result = File.__send__(predicate, *args)
result = !result if neg
- mesg = "Expected file " << args.shift.inspect
+ mesg = "Expected file ".dup << args.shift.inspect
mesg << "#{neg} to be #{predicate}"
mesg << mu_pp(args).sub(/\A\[(.*)\]\z/m, '(\1)') unless args.empty?
mesg << " #{failure_message}" if failure_message
@@ -821,12 +889,24 @@ eom
@failures[key] = [@count, e]
end
+ def foreach(*keys)
+ keys.each do |key|
+ @count += 1
+ begin
+ yield key
+ rescue Exception => e
+ @failures[key] = [@count, e]
+ end
+ end
+ end
+
def message
i = 0
total = @count.to_s
fmt = "%#{total.size}d"
@failures.map {|k, (n, v)|
- "\n#{i+=1}. [#{fmt%n}/#{total}] Assertion for #{k.inspect}\n#{v.message.b.gsub(/^/, ' | ')}"
+ v = v.message
+ "\n#{i+=1}. [#{fmt%n}/#{total}] Assertion for #{k.inspect}\n#{v.b.gsub(/^/, ' | ').force_encoding(v.encoding)}"
}.join("\n")
end
@@ -843,6 +923,14 @@ eom
end
alias all_assertions assert_all_assertions
+ def assert_all_assertions_foreach(msg = nil, *keys, &block)
+ all = AllFailures.new
+ all.foreach(*keys, &block)
+ ensure
+ assert(all.pass?, message(msg) {all.message.chomp(".")})
+ end
+ alias all_assertions_foreach assert_all_assertions_foreach
+
def build_message(head, template=nil, *arguments) #:nodoc:
template &&= template.chomp
template.gsub(/\G((?:[^\\]|\\.)*?)(\\)?\?/) { $1 + ($2 ? "?" : mu_pp(arguments.shift)) }
diff --git a/test/lib/test/unit/parallel.rb b/test/lib/test/unit/parallel.rb
index 1e12d18457..d851326aca 100644
--- a/test/lib/test/unit/parallel.rb
+++ b/test/lib/test/unit/parallel.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../.."
require 'test/unit'
@@ -61,7 +61,7 @@ module Test
begin
th.join
rescue IOError
- raise unless ["stream closed","closed stream"].include? $!.message
+ raise unless /stream closed|closed stream/ =~ $!.message
end
i.close
@@ -152,8 +152,7 @@ module Test
end
def _report(res, *args) # :nodoc:
- res = "#{res} #{args.pack("m0")}" unless args.empty?
- @stdout.puts(res)
+ @stdout.write(args.empty? ? "#{res}\n" : "#{res} #{args.pack("m0")}\n")
end
def puke(klass, meth, e) # :nodoc:
@@ -165,6 +164,24 @@ module Test
@partial_report << [klass.name, meth, e.is_a?(MiniTest::Assertion) ? e : ProxyError.new(e)]
super
end
+
+ def record(suite, method, assertions, time, error) # :nodoc:
+ case error
+ when nil
+ when MiniTest::Assertion, MiniTest::Skip
+ case error.cause
+ when nil, MiniTest::Assertion, MiniTest::Skip
+ else
+ bt = error.backtrace
+ error = error.class.new(error.message)
+ error.set_backtrace(bt)
+ end
+ else
+ error = ProxyError.new(error)
+ end
+ _report "record", Marshal.dump([suite.name, method, assertions, time, error])
+ super
+ end
end
end
end
diff --git a/test/lib/test/unit/testcase.rb b/test/lib/test/unit/testcase.rb
index 10348b5c9b..58cfbcab99 100644
--- a/test/lib/test/unit/testcase.rb
+++ b/test/lib/test/unit/testcase.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit/assertions'
module Test
diff --git a/test/lib/tracepointchecker.rb b/test/lib/tracepointchecker.rb
index 73631d415e..47822ecef5 100644
--- a/test/lib/tracepointchecker.rb
+++ b/test/lib/tracepointchecker.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module TracePointChecker
STATE = {
count: 0,
@@ -6,9 +6,16 @@ module TracePointChecker
}
module ZombieTraceHunter
- def before_setup
- @tracepoint_captured_stat = TracePoint.stat.map{|k, (activated, _deleted)| [k, activated]}
+ def tracepoint_capture_stat_get
+ TracePoint.stat.map{|k, (activated, deleted)|
+ deleted = 0 unless @tracepoint_captured_singlethread
+ [k, activated, deleted]
+ }
+ end
+ def before_setup
+ @tracepoint_captured_singlethread = (Thread.list.size == 1)
+ @tracepoint_captured_stat = tracepoint_capture_stat_get()
super
end
@@ -18,8 +25,8 @@ module TracePointChecker
# detect zombie traces.
assert_equal(
@tracepoint_captured_stat,
- TracePoint.stat.map{|k, (activated, _deleted)| [k, activated]},
- "The number of active trace events was changed"
+ tracepoint_capture_stat_get(),
+ "The number of active/deleted trace events was changed"
)
# puts "TracePoint - deleted: #{deleted}" if deleted > 0
diff --git a/test/lib/with_different_ofs.rb b/test/lib/with_different_ofs.rb
index 164914f1d6..b7ac646f8f 100644
--- a/test/lib/with_different_ofs.rb
+++ b/test/lib/with_different_ofs.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module DifferentOFS
module WithDifferentOFS
def setup
diff --git a/test/lib/zombie_hunter.rb b/test/lib/zombie_hunter.rb
index 8a8fba649c..33bc467941 100644
--- a/test/lib/zombie_hunter.rb
+++ b/test/lib/zombie_hunter.rb
@@ -1,4 +1,5 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
+
module ZombieHunter
def after_teardown
super
diff --git a/test/logger/test_logdevice.rb b/test/logger/test_logdevice.rb
index edb3ff57d8..7d5bf9ac81 100644
--- a/test/logger/test_logdevice.rb
+++ b/test/logger/test_logdevice.rb
@@ -75,7 +75,7 @@ class TestLogDevice < Test::Unit::TestCase
#
logdev = d(LogExcnRaiser.new)
class << (stderr = '')
- alias write <<
+ alias write concat
end
$stderr, stderr = stderr, $stderr
begin
@@ -334,12 +334,13 @@ class TestLogDevice < Test::Unit::TestCase
File.unlink(logfile1) if File.exist?(logfile1)
File.unlink(logfile2) if File.exist?(logfile2)
begin
- stderr = run_children(2, [logfile], <<-'END')
+ stderr = run_children(2, [logfile], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
logger = Logger.new(ARGV[0], 4, 10)
10.times do
logger.info '0' * 15
end
- END
+ end;
assert_no_match(/log shifting failed/, stderr)
assert_no_match(/log writing failed/, stderr)
assert_no_match(/log rotation inter-process lock failed/, stderr)
@@ -354,12 +355,13 @@ class TestLogDevice < Test::Unit::TestCase
def test_shifting_age_in_multiprocess
yyyymmdd = Time.now.strftime("%Y%m%d")
begin
- stderr = run_children(2, [@filename], <<-'END')
+ stderr = run_children(2, [@filename], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
logger = Logger.new(ARGV[0], 'now')
10.times do
logger.info '0' * 15
end
- END
+ end;
assert_no_match(/log shifting failed/, stderr)
assert_no_match(/log writing failed/, stderr)
assert_no_match(/log rotation inter-process lock failed/, stderr)
@@ -376,11 +378,12 @@ class TestLogDevice < Test::Unit::TestCase
tmpfile.close(true)
begin
20.times do
- run_children(2, [logfile], <<-'END')
+ run_children(2, [logfile], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
logfile = ARGV[0]
logdev = Logger::LogDevice.new(logfile)
logdev.send(:open_logfile, logfile)
- END
+ end;
assert_equal(1, File.readlines(logfile).grep(/# Logfile created on/).size)
File.unlink(logfile)
end
@@ -432,7 +435,8 @@ class TestLogDevice < Test::Unit::TestCase
def test_shifting_midnight
Dir.mktmpdir do |tmpdir|
- assert_in_out_err([*%W"--disable=gems -rlogger -C#{tmpdir} -"], <<-'end;')
+ assert_in_out_err([*%W"--disable=gems -rlogger -C#{tmpdir} -"], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
begin
module FakeTime
attr_accessor :now
@@ -444,14 +448,16 @@ class TestLogDevice < Test::Unit::TestCase
log = "log"
File.open(log, "w") {}
- File.utime(*[Time.mktime(2014, 1, 1, 23, 59, 59)]*2, log)
+ File.utime(*[Time.mktime(2014, 1, 2, 0, 0, 0)]*2, log)
Time.now = Time.mktime(2014, 1, 2, 23, 59, 59, 999000)
dev = Logger::LogDevice.new(log, shift_age: 'daily')
dev.write("#{Time.now} hello-1\n")
+ File.utime(Time.now, Time.now, log)
Time.now = Time.mktime(2014, 1, 3, 1, 1, 1)
dev.write("#{Time.now} hello-2\n")
+ File.utime(Time.now, Time.now, log)
ensure
dev.close
end
@@ -471,7 +477,8 @@ class TestLogDevice < Test::Unit::TestCase
def test_shifting_weekly
Dir.mktmpdir do |tmpdir|
- assert_in_out_err([{"TZ"=>"UTC"}, *%W"-rlogger -C#{tmpdir} -"], <<-'end;')
+ assert_in_out_err([{"TZ"=>"UTC"}, *%W"-rlogger -C#{tmpdir} -"], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
begin
module FakeTime
attr_accessor :now
@@ -483,8 +490,9 @@ class TestLogDevice < Test::Unit::TestCase
log = "log"
File.open(log, "w") {}
-
Time.now = Time.utc(2015, 12, 14, 0, 1, 1)
+ File.utime(Time.now, Time.now, log)
+
dev = Logger::LogDevice.new("log", shift_age: 'weekly')
Time.now = Time.utc(2015, 12, 19, 12, 34, 56)
@@ -492,8 +500,8 @@ class TestLogDevice < Test::Unit::TestCase
File.utime(Time.now, Time.now, log)
Time.now = Time.utc(2015, 12, 20, 0, 1, 1)
- File.utime(Time.now, Time.now, log)
dev.write("#{Time.now} hello-2\n")
+ File.utime(Time.now, Time.now, log)
ensure
dev.close if dev
end
@@ -514,7 +522,8 @@ class TestLogDevice < Test::Unit::TestCase
def test_shifting_monthly
Dir.mktmpdir do |tmpdir|
- assert_in_out_err([{"TZ"=>"UTC"}, *%W"-rlogger -C#{tmpdir} -"], <<-'end;')
+ assert_in_out_err([{"TZ"=>"UTC"}, *%W"-rlogger -C#{tmpdir} -"], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
begin
module FakeTime
attr_accessor :now
@@ -526,8 +535,9 @@ class TestLogDevice < Test::Unit::TestCase
log = "log"
File.open(log, "w") {}
-
Time.now = Time.utc(2015, 12, 14, 0, 1, 1)
+ File.utime(Time.now, Time.now, log)
+
dev = Logger::LogDevice.new("log", shift_age: 'monthly')
Time.now = Time.utc(2015, 12, 31, 12, 34, 56)
@@ -535,8 +545,8 @@ class TestLogDevice < Test::Unit::TestCase
File.utime(Time.now, Time.now, log)
Time.now = Time.utc(2016, 1, 1, 0, 1, 1)
- File.utime(Time.now, Time.now, log)
dev.write("#{Time.now} hello-2\n")
+ File.utime(Time.now, Time.now, log)
ensure
dev.close if dev
end
@@ -557,7 +567,8 @@ class TestLogDevice < Test::Unit::TestCase
def test_shifting_dst_change
Dir.mktmpdir do |tmpdir|
- assert_in_out_err([{"TZ"=>"Europe/London"}, *%W"--disable=gems -rlogger -C#{tmpdir} -"], <<-'end;')
+ assert_in_out_err([{"TZ"=>"Europe/London"}, *%W"--disable=gems -rlogger -C#{tmpdir} -"], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
begin
module FakeTime
attr_accessor :now
@@ -578,8 +589,8 @@ class TestLogDevice < Test::Unit::TestCase
File.utime(*[Time.mktime(2014, 3, 30, 0, 2, 3)]*2, log)
Time.now = Time.mktime(2014, 3, 31, 0, 1, 1)
- File.utime(Time.now, Time.now, log)
dev.write("#{Time.now} hello-2\n")
+ File.utime(Time.now, Time.now, log)
ensure
dev.close
end
@@ -595,7 +606,8 @@ class TestLogDevice < Test::Unit::TestCase
def test_shifting_weekly_dst_change
Dir.mktmpdir do |tmpdir|
- assert_separately([{"TZ"=>"Europe/London"}, *%W"-rlogger -C#{tmpdir} -"], <<-'end;')
+ assert_separately([{"TZ"=>"Europe/London"}, *%W"-rlogger -C#{tmpdir} -"], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
begin
module FakeTime
attr_accessor :now
@@ -607,10 +619,12 @@ class TestLogDevice < Test::Unit::TestCase
log = "log"
File.open(log, "w") {}
-
Time.now = Time.mktime(2015, 10, 25, 0, 1, 1)
+ File.utime(Time.now, Time.now, log)
+
dev = Logger::LogDevice.new("log", shift_age: 'weekly')
dev.write("#{Time.now} hello-1\n")
+ File.utime(Time.now, Time.now, log)
ensure
dev.close if dev
end
@@ -623,7 +637,8 @@ class TestLogDevice < Test::Unit::TestCase
def test_shifting_monthly_dst_change
Dir.mktmpdir do |tmpdir|
- assert_separately([{"TZ"=>"Europe/London"}, *%W"-rlogger -C#{tmpdir} -"], <<-'end;')
+ assert_separately([{"TZ"=>"Europe/London"}, *%W"-rlogger -C#{tmpdir} -"], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
begin
module FakeTime
attr_accessor :now
@@ -635,8 +650,9 @@ class TestLogDevice < Test::Unit::TestCase
log = "log"
File.open(log, "w") {}
-
Time.now = Time.utc(2016, 9, 1, 0, 1, 1)
+ File.utime(Time.now, Time.now, log)
+
dev = Logger::LogDevice.new("log", shift_age: 'monthly')
Time.now = Time.utc(2016, 9, 8, 7, 6, 5)
@@ -644,12 +660,12 @@ class TestLogDevice < Test::Unit::TestCase
File.utime(Time.now, Time.now, log)
Time.now = Time.utc(2016, 10, 9, 8, 7, 6)
- File.utime(Time.now, Time.now, log)
dev.write("#{Time.now} hello-2\n")
+ File.utime(Time.now, Time.now, log)
Time.now = Time.utc(2016, 10, 9, 8, 7, 7)
- File.utime(Time.now, Time.now, log)
dev.write("#{Time.now} hello-3\n")
+ File.utime(Time.now, Time.now, log)
ensure
dev.close if dev
end
@@ -668,6 +684,144 @@ class TestLogDevice < Test::Unit::TestCase
end
end if env_tz_works
+ def test_shifting_midnight_exist_file
+ Dir.mktmpdir do |tmpdir|
+ assert_in_out_err([*%W"--disable=gems -rlogger -C#{tmpdir} -"], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ begin
+ module FakeTime
+ attr_accessor :now
+ end
+
+ class << Time
+ prepend FakeTime
+ end
+
+ log = "log"
+ File.open(log, "w") {}
+ File.utime(*[Time.mktime(2014, 1, 2, 0, 0, 0)]*2, log)
+
+ Time.now = Time.mktime(2014, 1, 2, 23, 59, 59, 999000)
+ dev = Logger::LogDevice.new(log, shift_age: 'daily')
+ dev.write("#{Time.now} hello-1\n")
+ dev.close
+ File.utime(Time.now, Time.now, log)
+
+ Time.now = Time.mktime(2014, 1, 3, 1, 1, 1)
+ dev = Logger::LogDevice.new(log, shift_age: 'daily')
+ dev.write("#{Time.now} hello-2\n")
+ File.utime(Time.now, Time.now, log)
+ ensure
+ dev.close
+ end
+ end;
+
+ bug = '[GH-539]'
+ log = File.join(tmpdir, "log")
+ cont = File.read(log)
+ assert_match(/hello-2/, cont)
+ assert_not_match(/hello-1/, cont)
+ assert_file.for(bug).exist?(log+".20140102")
+ assert_match(/hello-1/, File.read(log+".20140102"), bug)
+ end
+ end
+
+ env_tz_works = /linux|darwin|freebsd/ =~ RUBY_PLATFORM # borrow from test/ruby/test_time_tz.rb
+
+ def test_shifting_weekly_exist_file
+ Dir.mktmpdir do |tmpdir|
+ assert_in_out_err([{"TZ"=>"UTC"}, *%W"-rlogger -C#{tmpdir} -"], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ begin
+ module FakeTime
+ attr_accessor :now
+ end
+
+ class << Time
+ prepend FakeTime
+ end
+
+ log = "log"
+ File.open(log, "w") {}
+ Time.now = Time.utc(2015, 12, 14, 0, 1, 1)
+ File.utime(Time.now, Time.now, log)
+
+ dev = Logger::LogDevice.new("log", shift_age: 'weekly')
+
+ Time.now = Time.utc(2015, 12, 19, 12, 34, 56)
+ dev.write("#{Time.now} hello-1\n")
+ dev.close
+ File.utime(Time.now, Time.now, log)
+
+ Time.now = Time.utc(2015, 12, 20, 0, 1, 1)
+ dev = Logger::LogDevice.new("log", shift_age: 'weekly')
+ dev.write("#{Time.now} hello-2\n")
+ File.utime(Time.now, Time.now, log)
+ ensure
+ dev.close if dev
+ end
+ end;
+ log = File.join(tmpdir, "log")
+ cont = File.read(log)
+ assert_match(/hello-2/, cont)
+ assert_not_match(/hello-1/, cont)
+ log = Dir.glob(log+".*")
+ assert_equal(1, log.size)
+ log, = *log
+ cont = File.read(log)
+ assert_match(/hello-1/, cont)
+ assert_equal("2015-12-19", cont[/^[-\d]+/])
+ assert_equal("20151219", log[/\d+\z/])
+ end
+ end if env_tz_works
+
+ def test_shifting_monthly_exist_file
+ Dir.mktmpdir do |tmpdir|
+ assert_in_out_err([{"TZ"=>"UTC"}, *%W"-rlogger -C#{tmpdir} -"], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ begin
+ module FakeTime
+ attr_accessor :now
+ end
+
+ class << Time
+ prepend FakeTime
+ end
+
+ log = "log"
+ File.open(log, "w") {}
+ Time.now = Time.utc(2015, 12, 14, 0, 1, 1)
+ File.utime(Time.now, Time.now, log)
+
+ dev = Logger::LogDevice.new("log", shift_age: 'monthly')
+
+ Time.now = Time.utc(2015, 12, 31, 12, 34, 56)
+ dev.write("#{Time.now} hello-1\n")
+ dev.close
+ File.utime(Time.now, Time.now, log)
+
+ Time.now = Time.utc(2016, 1, 1, 0, 1, 1)
+ dev = Logger::LogDevice.new("log", shift_age: 'monthly')
+ dev.write("#{Time.now} hello-2\n")
+ File.utime(Time.now, Time.now, log)
+ ensure
+ dev.close if dev
+ end
+ end;
+ log = File.join(tmpdir, "log")
+ cont = File.read(log)
+ assert_match(/hello-2/, cont)
+ assert_not_match(/hello-1/, cont)
+ log = Dir.glob(log+".*")
+ assert_equal(1, log.size)
+ log, = *log
+ cont = File.read(log)
+ assert_match(/hello-1/, cont)
+ assert_equal("2015-12-31", cont[/^[-\d]+/])
+ assert_equal("20151231", log[/\d+\z/])
+ end
+ end if env_tz_works
+
private
def run_children(n, args, src)
diff --git a/test/logger/test_logger.rb b/test/logger/test_logger.rb
index 628da99447..3f2319b961 100644
--- a/test/logger/test_logger.rb
+++ b/test/logger/test_logger.rb
@@ -235,6 +235,10 @@ class TestLogger < Test::Unit::TestCase
log = log_add(logger, WARN, nil, "progname?")
assert_equal("progname?\n", log.msg)
assert_equal("my_progname", log.progname)
+ #
+ logger = Logger.new(nil)
+ log = log_add(logger, INFO, nil, false)
+ assert_equal("false\n", log.msg)
end
def test_level_log
@@ -322,7 +326,7 @@ class TestLogger < Test::Unit::TestCase
r, w = IO.pipe
logger = Logger.new(w)
logger << "msg"
- read_ready, = IO.select([r], nil, nil, 0.1)
+ IO.select([r], nil, nil, 0.1)
w.close
msg = r.read
r.close
@@ -331,7 +335,7 @@ class TestLogger < Test::Unit::TestCase
r, w = IO.pipe
logger = Logger.new(w)
logger << "msg2\n\n"
- read_ready, = IO.select([r], nil, nil, 0.1)
+ IO.select([r], nil, nil, 0.1)
w.close
msg = r.read
r.close
diff --git a/test/matrix/test_matrix.rb b/test/matrix/test_matrix.rb
index 425ec25a52..3ecb9d2f8b 100644
--- a/test/matrix/test_matrix.rb
+++ b/test/matrix/test_matrix.rb
@@ -218,6 +218,10 @@ class TestMatrix < Test::Unit::TestCase
assert_equal([[1], [1]], m2.to_a)
end
+ def test_to_matrix
+ assert @m1.equal? @m1.to_matrix
+ end
+
def test_columns
assert_equal(@m1, Matrix.columns([[1, 4], [2, 5], [3, 6]]))
end
@@ -279,7 +283,18 @@ class TestMatrix < Test::Unit::TestCase
end
def test_collect
- assert_equal(Matrix[[1, 4, 9], [16, 25, 36]], @m1.collect {|x| x ** 2 })
+ m1 = Matrix.zero(2,2)
+ m2 = Matrix.build(3,4){|row, col| 1}
+
+ assert_equal(Matrix[[5, 5, 5, 5], [5, 5, 5, 5], [5, 5, 5, 5]], m2.collect{|e| e * 5})
+ assert_equal(Matrix[[7, 0],[0, 7]], m1.collect(:diagonal){|e| e + 7})
+ assert_equal(Matrix[[0, 5],[5, 0]], m1.collect(:off_diagonal){|e| e + 5})
+ assert_equal(Matrix[[8, 1, 1, 1], [8, 8, 1, 1], [8, 8, 8, 1]], m2.collect(:lower){|e| e + 7})
+ assert_equal(Matrix[[1, 1, 1, 1], [-11, 1, 1, 1], [-11, -11, 1, 1]], m2.collect(:strict_lower){|e| e - 12})
+ assert_equal(Matrix[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]], m2.collect(:strict_upper){|e| e ** 2})
+ assert_equal(Matrix[[-1, -1, -1, -1], [1, -1, -1, -1], [1, 1, -1, -1]], m2.collect(:upper){|e| -e})
+ assert_raise(ArgumentError) {m1.collect(:test){|e| e + 7}}
+ assert_not_equal(m2, m2.collect {|e| e * 2 })
end
def test_minor
@@ -414,6 +429,20 @@ class TestMatrix < Test::Unit::TestCase
assert_equal(Matrix[[1,1],[1,1]], Matrix[[2,2],[2,2]] / o)
end
+ def test_hadamard_product
+ assert_equal(Matrix[[1,4], [9,16]], Matrix[[1,2], [3,4]].hadamard_product(Matrix[[1,2], [3,4]]))
+ assert_equal(Matrix[[2, 6, 12], [20, 30, 42]], @m1.hadamard_product(@n1))
+ o = Object.new
+ def o.to_matrix
+ Matrix[[1, 2, 3], [-1, 0, 1]]
+ end
+ assert_equal(Matrix[[1, 4, 9], [-4, 0, 6]], @m1.hadamard_product(o))
+ e = Matrix.empty(3, 0)
+ assert_equal(e, e.hadamard_product(e))
+ e = Matrix.empty(0, 3)
+ assert_equal(e, e.hadamard_product(e))
+ end
+
def test_exp
assert_equal(Matrix[[67,96],[48,99]], Matrix[[7,6],[3,9]] ** 2)
assert_equal(Matrix.I(5), Matrix.I(5) ** -1)
@@ -567,6 +596,8 @@ class TestMatrix < Test::Unit::TestCase
assert_equal @e1, @e1.hstack(@e1)
assert_equal Matrix.empty(0,6), @e2.hstack(@e2)
assert_equal SubMatrix, SubMatrix.hstack(@e1).class
+ # From Vectors:
+ assert_equal Matrix[[1, 3],[2, 4]], Matrix.hstack(Vector[1,2], Vector[3, 4])
end
def test_vstack
@@ -582,6 +613,153 @@ class TestMatrix < Test::Unit::TestCase
assert_equal Matrix.empty(4,0), @e1.vstack(@e1)
assert_equal @e2, @e2.vstack(@e2)
assert_equal SubMatrix, SubMatrix.vstack(@e1).class
+ # From Vectors:
+ assert_equal Matrix[[1],[2],[3]], Matrix.vstack(Vector[1,2], Vector[3])
+ end
+
+ def test_combine
+ x = Matrix[[6, 6], [4, 4]]
+ y = Matrix[[1, 2], [3, 4]]
+ assert_equal Matrix[[5, 4], [1, 0]], Matrix.combine(x, y) {|a, b| a - b}
+ assert_equal Matrix[[5, 4], [1, 0]], x.combine(y) {|a, b| a - b}
+ # Without block
+ assert_equal Matrix[[5, 4], [1, 0]], Matrix.combine(x, y).each {|a, b| a - b}
+ # With vectors
+ assert_equal Matrix[[111], [222]], Matrix.combine(Matrix[[1], [2]], Vector[10,20], Vector[100,200], &:sum)
+ # Basic checks
+ assert_raise(Matrix::ErrDimensionMismatch) { @m1.combine(x) { raise } }
+ # Edge cases
+ assert_equal Matrix.empty, Matrix.combine{ raise }
+ assert_equal Matrix.empty(3,0), Matrix.combine(Matrix.empty(3,0), Matrix.empty(3,0)) { raise }
+ assert_equal Matrix.empty(0,3), Matrix.combine(Matrix.empty(0,3), Matrix.empty(0,3)) { raise }
+ end
+
+ def test_set_element
+ src = Matrix[
+ [1, 2, 3, 4],
+ [5, 6, 7, 8],
+ [9, 10, 11, 12],
+ ]
+ rows = {
+ range: [1..2, 1...3, 1..-1, -2..2, 1.., 1..., -2.., -2...],
+ int: [2, -1],
+ invalid: [-4, 4, -4..2, 2..-4, 0...0, 2..0, -4..],
+ }
+ columns = {
+ range: [2..3, 2...4, 2..-1, -2..3, 2.., 2..., -2..., -2..],
+ int: [3, -1],
+ invalid: [-5, 5, -5..2, 2..-5, 0...0, -5..],
+ }
+ values = {
+ element: 42,
+ matrix: Matrix[[20, 21], [22, 23]],
+ vector: Vector[30, 31],
+ row: Matrix[[60, 61]],
+ column: Matrix[[50], [51]],
+ mismatched_matrix: Matrix.identity(3),
+ mismatched_vector: Vector[0, 1, 2, 3],
+ }
+ solutions = {
+ [:int, :int] => {
+ element: Matrix[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 42]],
+ },
+ [:range , :int] => {
+ element: Matrix[[1, 2, 3, 4], [5, 6, 7, 42], [9, 10, 11, 42]],
+ column: Matrix[[1, 2, 3, 4], [5, 6, 7, 50], [9, 10, 11, 51]],
+ vector: Matrix[[1, 2, 3, 4], [5, 6, 7, 30], [9, 10, 11, 31]],
+ },
+ [:int, :range] => {
+ element: Matrix[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 42, 42]],
+ row: Matrix[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 60, 61]],
+ vector: Matrix[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 30, 31]],
+ },
+ [:range , :range] => {
+ element: Matrix[[1, 2, 3, 4], [5, 6, 42, 42], [9, 10, 42, 42]],
+ matrix: Matrix[[1, 2, 3, 4], [5, 6, 20, 21], [9, 10, 22, 23]],
+ },
+ }
+ solutions.default = Hash.new(IndexError)
+
+ rows.each do |row_style, row_arguments|
+ row_arguments.each do |row_argument|
+ columns.each do |column_style, column_arguments|
+ column_arguments.each do |column_argument|
+ values.each do |value_type, value|
+ expected = solutions[[row_style, column_style]][value_type] || Matrix::ErrDimensionMismatch
+
+ result = src.clone
+ begin
+ result[row_argument, column_argument] = value
+ assert_equal expected, result,
+ "m[#{row_argument.inspect}][#{column_argument.inspect}] = #{value.inspect} failed"
+ rescue Exception => e
+ raise if e.class != expected
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def test_map!
+ m1 = Matrix.zero(2,2)
+ m2 = Matrix.build(3,4){|row, col| 1}
+ m3 = Matrix.zero(3,5).freeze
+ m4 = Matrix.empty.freeze
+
+ assert_equal Matrix[[5, 5, 5, 5], [5, 5, 5, 5], [5, 5, 5, 5]], m2.map!{|e| e * 5}
+ assert_equal Matrix[[7, 0],[0, 7]], m1.map!(:diagonal){|e| e + 7}
+ assert_equal Matrix[[7, 5],[5, 7]], m1.map!(:off_diagonal){|e| e + 5}
+ assert_equal Matrix[[12, 5, 5, 5], [12, 12, 5, 5], [12, 12, 12, 5]], m2.map!(:lower){|e| e + 7}
+ assert_equal Matrix[[12, 5, 5, 5], [0, 12, 5, 5], [0, 0, 12, 5]], m2.map!(:strict_lower){|e| e - 12}
+ assert_equal Matrix[[12, 25, 25, 25], [0, 12, 25, 25], [0, 0, 12, 25]], m2.map!(:strict_upper){|e| e ** 2}
+ assert_equal Matrix[[-12, -25, -25, -25], [0, -12, -25, -25], [0, 0, -12, -25]], m2.map!(:upper){|e| -e}
+ assert_equal m1, m1.map!{|e| e ** 2 }
+ assert_equal m2, m2.map!(:lower){ |e| e - 3 }
+ assert_raise(ArgumentError) {m1.map!(:test){|e| e + 7}}
+ assert_raise(FrozenError) { m3.map!{|e| e * 2} }
+ assert_raise(FrozenError) { m4.map!{} }
+ end
+
+ def test_freeze
+ m = Matrix[[1, 2, 3],[4, 5, 6]]
+ f = m.freeze
+ assert_equal true, f.frozen?
+ assert m.equal?(f)
+ assert m.equal?(f.freeze)
+ assert_raise(FrozenError){ m[0, 1] = 56 }
+ assert_equal m.dup, m
+ end
+
+ def test_clone
+ a = Matrix[[4]]
+ def a.foo
+ 42
+ end
+
+ m = a.clone
+ m[0, 0] = 2
+ assert_equal a, m * 2
+ assert_equal 42, m.foo
+
+ a.freeze
+ m = a.clone
+ assert m.frozen?
+ assert_equal 42, m.foo
+ end
+
+ def test_dup
+ a = Matrix[[4]]
+ def a.foo
+ 42
+ end
+ a.freeze
+
+ m = a.dup
+ m[0, 0] = 2
+ assert_equal a, m * 2
+ assert !m.respond_to?(:foo)
end
def test_eigenvalues_and_eigenvectors_symmetric
diff --git a/test/matrix/test_vector.rb b/test/matrix/test_vector.rb
index 72082be84c..8b771efee3 100644
--- a/test/matrix/test_vector.rb
+++ b/test/matrix/test_vector.rb
@@ -11,6 +11,13 @@ class TestVector < Test::Unit::TestCase
@w1 = Vector[2,3,4]
end
+ def test_zero
+ assert_equal Vector[0, 0, 0, 0], Vector.zero(4)
+ assert_equal Vector[], Vector.zero(0)
+ assert_raise(ArgumentError) { Vector.zero(-1) }
+ assert Vector[0, 0, 0, 0].zero?
+ end
+
def test_basis
assert_equal(Vector[1, 0, 0], Vector.basis(size: 3, index: 0))
assert_raise(ArgumentError) { Vector.basis(size: -1, index: 2) }
@@ -20,6 +27,108 @@ class TestVector < Test::Unit::TestCase
assert_raise(ArgumentError) { Vector.basis(index: 3) }
end
+ def test_get_element
+ assert_equal(@v1[0..], [1, 2, 3])
+ assert_equal(@v1[0..1], [1, 2])
+ assert_equal(@v1[2], 3)
+ assert_equal(@v1[4], nil)
+ end
+
+ def test_set_element
+
+ assert_block do
+ v = Vector[5, 6, 7, 8, 9]
+ v[1..2] = Vector[1, 2]
+ v == Vector[5, 1, 2, 8, 9]
+ end
+
+ assert_block do
+ v = Vector[6, 7, 8]
+ v[1..2] = Matrix[[1, 3]]
+ v == Vector[6, 1, 3]
+ end
+
+ assert_block do
+ v = Vector[1, 2, 3, 4, 5, 6]
+ v[0..2] = 8
+ v == Vector[8, 8, 8, 4, 5, 6]
+ end
+
+ assert_block do
+ v = Vector[1, 3, 4, 5]
+ v[2] = 5
+ v == Vector[1, 3, 5, 5]
+ end
+
+ assert_block do
+ v = Vector[2, 3, 5]
+ v[-2] = 13
+ v == Vector[2, 13, 5]
+ end
+
+ assert_block do
+ v = Vector[4, 8, 9, 11, 30]
+ v[1..-2] = Vector[1, 2, 3]
+ v == Vector[4, 1, 2, 3, 30]
+ end
+
+ assert_raise(IndexError) {Vector[1, 3, 4, 5][5..6] = 17}
+ assert_raise(IndexError) {Vector[1, 3, 4, 5][6] = 17}
+ assert_raise(Matrix::ErrDimensionMismatch) {Vector[1, 3, 4, 5][0..2] = Matrix[[1], [2], [3]]}
+ assert_raise(ArgumentError) {Vector[1, 2, 3, 4, 5, 6][0..2] = Vector[1, 2, 3, 4, 5, 6]}
+ assert_raise(FrozenError) { Vector[7, 8, 9].freeze[0..1] = 5}
+ end
+
+ def test_map!
+ v1 = Vector[1, 2, 3]
+ v2 = Vector[1, 3, 5].freeze
+ v3 = Vector[].freeze
+ assert_equal Vector[1, 4, 9], v1.map!{|e| e ** 2}
+ assert_equal v1, v1.map!{|e| e - 8}
+ assert_raise(FrozenError) { v2.map!{|e| e + 2 }}
+ assert_raise(FrozenError){ v3.map!{} }
+ end
+
+ def test_freeze
+ v = Vector[1,2,3]
+ f = v.freeze
+ assert_equal true, f.frozen?
+ assert v.equal?(f)
+ assert v.equal?(f.freeze)
+ assert_raise(FrozenError){ v[1] = 56 }
+ assert_equal v.dup, v
+ end
+
+ def test_clone
+ a = Vector[4]
+ def a.foo
+ 42
+ end
+
+ v = a.clone
+ v[0] = 2
+ assert_equal a, v * 2
+ assert_equal 42, v.foo
+
+ a.freeze
+ v = a.clone
+ assert v.frozen?
+ assert_equal 42, v.foo
+ end
+
+ def test_dup
+ a = Vector[4]
+ def a.foo
+ 42
+ end
+ a.freeze
+
+ v = a.dup
+ v[0] = 2
+ assert_equal a, v * 2
+ assert !v.respond_to?(:foo)
+ end
+
def test_identity
assert_same @v1, @v1
assert_not_same @v1, @v2
@@ -170,6 +279,10 @@ class TestVector < Test::Unit::TestCase
assert_equal("Vector[1, 2, 3]", @v1.to_s)
end
+ def test_to_matrix
+ assert_equal Matrix[[1], [2], [3]], @v1.to_matrix
+ end
+
def test_inspect
assert_equal("Vector[1, 2, 3]", @v1.inspect)
end
@@ -212,6 +325,8 @@ class TestVector < Test::Unit::TestCase
assert_in_epsilon(Math::PI/2, Vector[1, 0].angle_with(Vector[0, -1]))
assert_in_epsilon(Math::PI/4, Vector[2, 2].angle_with(Vector[0, 1]))
assert_in_delta(0.0, Vector[1, 1].angle_with(Vector[1, 1]), 0.00001)
+ assert_equal(Vector[6, 6].angle_with(Vector[7, 7]), 0.0)
+ assert_equal(Vector[6, 6].angle_with(Vector[-7, -7]), Math::PI)
assert_raise(Vector::ZeroVectorError) { Vector[1, 1].angle_with(Vector[0, 0]) }
assert_raise(Vector::ZeroVectorError) { Vector[0, 0].angle_with(Vector[1, 1]) }
diff --git a/test/minitest/test_minitest_unit.rb b/test/minitest/test_minitest_unit.rb
index b70c099d01..3d92b865c3 100644
--- a/test/minitest/test_minitest_unit.rb
+++ b/test/minitest/test_minitest_unit.rb
@@ -1122,7 +1122,7 @@ class TestMiniTestUnitTestCase < MiniTest::Unit::TestCase
# *sigh* This is quite an odd scenario, but it is from real (albeit
# ugly) test code in ruby-core:
#
- # http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=29259
+ # https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=29259
def test_assert_raises_skip
@assertion_count = 0
@@ -1528,7 +1528,9 @@ class TestMiniTestUnitTestCase < MiniTest::Unit::TestCase
def test_refute_match_matcher_object
@assertion_count = 2
- @tc.refute_match Object.new, 5 # default #=~ returns false
+ non_verbose do
+ @tc.refute_match Object.new, 5 # default #=~ returns false
+ end
end
def test_refute_match_object_triggered
@@ -1645,7 +1647,7 @@ class TestMiniTestUnitTestCase < MiniTest::Unit::TestCase
assert_equal expected, sample_test_case.test_methods
end
- def util_assert_triggered expected, klass = MiniTest::Assertion
+ def assert_triggered expected, klass = MiniTest::Assertion
e = assert_raises klass do
yield
end
@@ -1656,6 +1658,7 @@ class TestMiniTestUnitTestCase < MiniTest::Unit::TestCase
assert_equal expected, msg
end
+ alias util_assert_triggered assert_triggered
def util_msg exp, act, msg = nil
s = "Expected: #{exp.inspect}\n Actual: #{act.inspect}"
diff --git a/test/mkmf/base.rb b/test/mkmf/base.rb
index 86bdd29286..80dec1421a 100644
--- a/test/mkmf/base.rb
+++ b/test/mkmf/base.rb
@@ -1,5 +1,13 @@
# frozen_string_literal: false
$extmk = true
+require 'rbconfig'
+RbConfig.fire_update!("top_srcdir", File.expand_path("../..", __dir__))
+File.foreach(RbConfig::CONFIG["topdir"]+"/Makefile") do |line|
+ if /^CC_WRAPPER\s*=\s*/ =~ line
+ RbConfig.fire_update!('CC_WRAPPER', $'.strip)
+ break
+ end
+end
require 'test/unit'
require 'mkmf'
@@ -49,11 +57,11 @@ module TestMkmf::Base
def filter(&block)
@filter = block
end
- def write(s)
+ def write(*s)
if @out
- @buffer << s
+ @buffer.concat(*s)
elsif @origin
- @origin << s
+ @origin.write(*s)
end
end
end
diff --git a/test/mkmf/test_framework.rb b/test/mkmf/test_framework.rb
index ae05b0cffc..ba15299ae0 100644
--- a/test/mkmf/test_framework.rb
+++ b/test/mkmf/test_framework.rb
@@ -10,7 +10,7 @@ class TestMkmf
FileUtils.mkdir_p(hdrdir)
File.write("#{hdrdir}/#{hdrname}", "")
src = "#{fwdir}/main.c"
- File.write(src, "void #{fw}(void) {}")
+ File.write(src, "void #{fw}(void) {}\n")
cmd = LINK_SO.dup
RbConfig.expand(cmd, RbConfig::CONFIG.merge("OBJS"=>src))
cmd.gsub!("$@", "#{fwdir}/#{fw}")
@@ -22,11 +22,11 @@ class TestMkmf
end
def test_single_framework
- assert(have_framework("Ruby"), mkmflog("try as Objective-C"))
+ assert(have_framework(%w"Ruby ruby.h"), mkmflog("try as Objective-C"))
end
def test_multi_frameworks
- assert(have_framework("Ruby"), mkmflog("try as Objective-C"))
+ assert(have_framework(%w"Ruby ruby.h"), mkmflog("try as Objective-C"))
create_framework("MkmfTest") do |fw|
assert(have_framework(fw), MKMFLOG)
end
diff --git a/test/monitor/test_monitor.rb b/test/monitor/test_monitor.rb
index a3861735b3..8854b59ccc 100644
--- a/test/monitor/test_monitor.rb
+++ b/test/monitor/test_monitor.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: false
require "monitor"
-require "thread"
require "test/unit"
@@ -146,6 +145,35 @@ class TestMonitor < Test::Unit::TestCase
assert_join_threads([th, th2])
end
+ def test_mon_locked_and_owned
+ queue1 = Queue.new
+ queue2 = Queue.new
+ th = Thread.start {
+ @monitor.enter
+ queue1.enq(nil)
+ queue2.deq
+ @monitor.exit
+ queue1.enq(nil)
+ }
+ queue1.deq
+ assert(@monitor.mon_locked?)
+ assert(!@monitor.mon_owned?)
+
+ queue2.enq(nil)
+ queue1.deq
+ assert(!@monitor.mon_locked?)
+
+ @monitor.enter
+ assert @monitor.mon_locked?
+ assert @monitor.mon_owned?
+ @monitor.exit
+
+ @monitor.synchronize do
+ assert @monitor.mon_locked?
+ assert @monitor.mon_owned?
+ end
+ end
+
def test_cond
cond = @monitor.new_cond
@@ -241,4 +269,26 @@ class TestMonitor < Test::Unit::TestCase
# end
# cumber_thread.kill
end
+
+ def test_wait_interruption
+ queue = Queue.new
+ cond = @monitor.new_cond
+ @monitor.define_singleton_method(:mon_enter_for_cond) do |*args|
+ queue.deq
+ super(*args)
+ end
+ th = Thread.start {
+ @monitor.synchronize do
+ begin
+ cond.wait(0.1)
+ rescue Interrupt
+ @monitor.instance_variable_get(:@mon_owner)
+ end
+ end
+ }
+ sleep(0.1)
+ th.raise(Interrupt)
+ queue.enq(nil)
+ assert_equal th, th.value
+ end
end
diff --git a/test/net/fixtures/cacert.pem b/test/net/fixtures/cacert.pem
new file mode 100644
index 0000000000..f623bd62ed
--- /dev/null
+++ b/test/net/fixtures/cacert.pem
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIID7TCCAtWgAwIBAgIJAIltvxrFAuSnMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYD
+VQQGEwJKUDEQMA4GA1UECAwHU2hpbWFuZTEUMBIGA1UEBwwLTWF0ei1lIGNpdHkx
+FzAVBgNVBAoMDlJ1YnkgQ29yZSBUZWFtMRUwEwYDVQQDDAxSdWJ5IFRlc3QgQ0Ex
+JTAjBgkqhkiG9w0BCQEWFnNlY3VyaXR5QHJ1YnktbGFuZy5vcmcwHhcNMTkwMTAy
+MDI1ODI4WhcNMjQwMTAxMDI1ODI4WjCBjDELMAkGA1UEBhMCSlAxEDAOBgNVBAgM
+B1NoaW1hbmUxFDASBgNVBAcMC01hdHotZSBjaXR5MRcwFQYDVQQKDA5SdWJ5IENv
+cmUgVGVhbTEVMBMGA1UEAwwMUnVieSBUZXN0IENBMSUwIwYJKoZIhvcNAQkBFhZz
+ZWN1cml0eUBydWJ5LWxhbmcub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAznlbjRVhz1NlutHVrhcGnK8W0qug2ujKXv1njSC4U6nJF6py7I9EeehV
+SaKePyv+I9z3K1LnfUHOtUbdwdKC77yN66A6q2aqzu5q09/NSykcZGOIF0GuItYI
+3nvW3IqBddff2ffsyR+9pBjfb5AIPP08WowF9q4s1eGULwZc4w2B8PFhtxYANd7d
+BvGLXFlcufv9tDtzyRi4t7eqxCRJkZQIZNZ6DHHIJrNxejOILfHLarI12yk8VK6L
+2LG4WgGqyeePiRyd1o1MbuiAFYqAwpXNUbRKg5NaZGwBHZk8UZ+uFKt1QMBURO5R
+WFy1c349jbWszTqFyL4Lnbg9HhAowQIDAQABo1AwTjAdBgNVHQ4EFgQU9tEiKdU9
+I9derQyc5nWPnc34nVMwHwYDVR0jBBgwFoAU9tEiKdU9I9derQyc5nWPnc34nVMw
+DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAxj7F/u3C3fgq24N7hGRA
+of7ClFQxGmo/IGT0AISzW3HiVYiFaikKhbO1NwD9aBpD8Zwe62sCqMh8jGV/b0+q
+aOORnWYNy2R6r9FkASAglmdF6xn3bhgGD5ls4pCvcG9FynGnGc24g6MrjFNrBYUS
+2iIZsg36i0IJswo/Dy6HLphCms2BMCD3DeWtfjePUiTmQHJo6HsQIKP/u4N4Fvee
+uMBInei2M4VU74fLXbmKl1F9AEX7JDP3BKSZG19Ch5pnUo4uXM1uNTGsi07P4Y0s
+K44+SKBC0bYEFbDK0eQWMrX3kIhkPxyIWhxdq9/NqPYjShuSEAhA6CSpmRg0pqc+
+mA==
+-----END CERTIFICATE-----
diff --git a/test/net/fixtures/dhparams.pem b/test/net/fixtures/dhparams.pem
new file mode 100644
index 0000000000..389285bf59
--- /dev/null
+++ b/test/net/fixtures/dhparams.pem
@@ -0,0 +1,29 @@
+ DH Parameters: (2048 bit)
+ prime:
+ 00:ec:4e:a4:06:b6:22:ca:f9:8a:00:cc:d0:ee:2f:
+ 16:bf:05:64:f5:8f:fe:7f:c4:bb:b0:24:cd:ef:5d:
+ 8a:90:ad:dc:a9:dd:63:84:90:d8:25:ba:d8:78:d5:
+ 77:91:42:0a:84:fc:56:1e:13:9b:1c:aa:43:d5:1f:
+ 38:52:92:fe:b3:66:f9:e7:e8:8c:77:a1:a6:2f:b3:
+ 98:98:d2:13:fc:57:1c:2a:14:dc:bd:e6:9b:54:19:
+ 99:4f:ce:81:64:a6:32:7f:8e:61:50:5f:45:3a:e5:
+ 0c:f7:13:f3:b8:ad:d5:77:ca:09:42:f7:d8:30:27:
+ 7b:2c:f0:b4:b5:a0:04:96:34:0b:47:81:1d:7f:c1:
+ 3a:62:86:8e:7d:f8:13:7f:9a:b1:8b:09:23:9e:55:
+ 59:41:cd:f0:86:09:c4:b7:d1:69:54:cb:d0:f5:e9:
+ 27:c9:e1:81:e4:a1:df:6b:20:1c:df:e8:54:02:f2:
+ 37:fc:2a:f7:d5:b3:6f:79:7e:70:22:78:79:18:3c:
+ 75:14:68:4a:05:9f:ac:d4:7f:9a:79:db:9d:0a:6e:
+ ec:0a:04:70:bf:c9:4a:59:81:a2:1f:33:9b:4a:66:
+ bc:03:ce:8a:1b:e3:03:ec:ba:39:26:ab:90:dc:39:
+ 41:a1:d8:f7:20:3c:8f:af:12:2f:f7:a9:6f:44:f1:
+ 6d:03
+ generator: 2 (0x2)
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY
+JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab
+VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6
+YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
+1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD
+7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg==
+-----END DH PARAMETERS-----
diff --git a/test/net/fixtures/server.crt b/test/net/fixtures/server.crt
new file mode 100644
index 0000000000..5ca78a6d14
--- /dev/null
+++ b/test/net/fixtures/server.crt
@@ -0,0 +1,82 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=JP, ST=Shimane, L=Matz-e city, O=Ruby Core Team, CN=Ruby Test CA/emailAddress=security@ruby-lang.org
+ Validity
+ Not Before: Jan 2 03:27:13 2019 GMT
+ Not After : Jan 1 03:27:13 2024 GMT
+ Subject: C=JP, ST=Shimane, O=Ruby Core Team, OU=Ruby Test, CN=localhost
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:e8:da:9c:01:2e:2b:10:ec:49:cd:5e:07:13:07:
+ 9c:70:9e:c6:74:bc:13:c2:e1:6f:c6:82:fd:e3:48:
+ e0:2c:a5:68:c7:9e:42:de:60:54:65:e6:6a:14:57:
+ 7a:30:d0:cc:b5:b6:d9:c3:d2:df:c9:25:97:54:67:
+ cf:f6:be:5e:cb:8b:ee:03:c5:e1:e2:f9:e7:f7:d1:
+ 0c:47:f0:b8:da:33:5a:ad:41:ad:e7:b5:a2:7b:b7:
+ bf:30:da:60:f8:e3:54:a2:bc:3a:fd:1b:74:d9:dc:
+ 74:42:e9:29:be:df:ac:b4:4f:eb:32:f4:06:f1:e1:
+ 8c:4b:a8:8b:fb:29:e7:b1:bf:1d:01:ee:73:0f:f9:
+ 40:dc:d5:15:79:d9:c6:73:d0:c0:dd:cb:e4:da:19:
+ 47:80:c6:14:04:72:fd:9a:7c:8f:11:82:76:49:04:
+ 79:cc:f2:5c:31:22:95:13:3e:5d:40:a6:4d:e0:a3:
+ 02:26:7d:52:3b:bb:ed:65:a1:0f:ed:6b:b0:3c:d4:
+ de:61:15:5e:d3:dd:68:09:9f:4a:57:a5:c2:a9:6d:
+ 86:92:c5:f4:a4:d4:b7:13:3b:52:63:24:05:e2:cc:
+ e3:8a:3c:d4:35:34:2b:10:bb:58:72:e7:e1:8d:1d:
+ 74:8c:61:16:20:3d:d0:1c:4e:8f:6e:fd:fe:64:10:
+ 4f:41
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ ED:28:C2:7E:AB:4B:C8:E8:FE:55:6D:66:95:31:1C:2D:60:F9:02:36
+ X509v3 Authority Key Identifier:
+ keyid:F6:D1:22:29:D5:3D:23:D7:5E:AD:0C:9C:E6:75:8F:9D:CD:F8:9D:53
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 1d:b8:c5:8b:72:41:20:65:ad:27:6f:15:63:06:26:12:8d:9c:
+ ad:ca:f4:db:97:b4:90:cb:ff:35:94:bb:2a:a7:a1:ab:1e:35:
+ 2d:a5:3f:c9:24:b0:1a:58:89:75:3e:81:0a:2c:4f:98:f9:51:
+ fb:c0:a3:09:d0:0a:9b:e7:a2:b7:c3:60:40:c8:f4:6d:b2:6a:
+ 56:12:17:4c:00:24:31:df:9c:60:ae:b1:68:54:a9:e6:b5:4a:
+ 04:e6:92:05:86:d9:5a:dc:96:30:a5:58:de:14:99:0f:e5:15:
+ 89:3e:9b:eb:80:e3:bd:83:c3:ea:33:35:4b:3e:2f:d3:0d:64:
+ 93:67:7f:8d:f5:3f:0c:27:bc:37:5a:cc:d6:47:16:af:5a:62:
+ d2:da:51:f8:74:06:6b:24:ad:28:68:08:98:37:7d:ed:0e:ab:
+ 1e:82:61:05:d0:ba:75:a0:ab:21:b0:9a:fd:2b:54:86:1d:0d:
+ 1f:c2:d4:77:1f:72:26:5e:ad:8a:9f:09:36:6d:44:be:74:c2:
+ 5a:3e:ff:5c:9d:75:d6:38:7b:c5:39:f9:44:6e:a1:d1:8e:ff:
+ 63:db:c4:bb:c6:91:92:ca:5c:60:9b:1d:eb:0a:de:08:ee:bf:
+ da:76:03:65:62:29:8b:f8:7f:c7:86:73:1e:f6:1f:2d:89:69:
+ fd:be:bd:6e
+-----BEGIN CERTIFICATE-----
+MIID4zCCAsugAwIBAgIBAjANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCSlAx
+EDAOBgNVBAgMB1NoaW1hbmUxFDASBgNVBAcMC01hdHotZSBjaXR5MRcwFQYDVQQK
+DA5SdWJ5IENvcmUgVGVhbTEVMBMGA1UEAwwMUnVieSBUZXN0IENBMSUwIwYJKoZI
+hvcNAQkBFhZzZWN1cml0eUBydWJ5LWxhbmcub3JnMB4XDTE5MDEwMjAzMjcxM1oX
+DTI0MDEwMTAzMjcxM1owYDELMAkGA1UEBhMCSlAxEDAOBgNVBAgMB1NoaW1hbmUx
+FzAVBgNVBAoMDlJ1YnkgQ29yZSBUZWFtMRIwEAYDVQQLDAlSdWJ5IFRlc3QxEjAQ
+BgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AOjanAEuKxDsSc1eBxMHnHCexnS8E8Lhb8aC/eNI4CylaMeeQt5gVGXmahRXejDQ
+zLW22cPS38kll1Rnz/a+XsuL7gPF4eL55/fRDEfwuNozWq1Bree1onu3vzDaYPjj
+VKK8Ov0bdNncdELpKb7frLRP6zL0BvHhjEuoi/sp57G/HQHucw/5QNzVFXnZxnPQ
+wN3L5NoZR4DGFARy/Zp8jxGCdkkEeczyXDEilRM+XUCmTeCjAiZ9Uju77WWhD+1r
+sDzU3mEVXtPdaAmfSlelwqlthpLF9KTUtxM7UmMkBeLM44o81DU0KxC7WHLn4Y0d
+dIxhFiA90BxOj279/mQQT0ECAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhC
+AQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFO0o
+wn6rS8jo/lVtZpUxHC1g+QI2MB8GA1UdIwQYMBaAFPbRIinVPSPXXq0MnOZ1j53N
++J1TMA0GCSqGSIb3DQEBCwUAA4IBAQAduMWLckEgZa0nbxVjBiYSjZytyvTbl7SQ
+y/81lLsqp6GrHjUtpT/JJLAaWIl1PoEKLE+Y+VH7wKMJ0Aqb56K3w2BAyPRtsmpW
+EhdMACQx35xgrrFoVKnmtUoE5pIFhtla3JYwpVjeFJkP5RWJPpvrgOO9g8PqMzVL
+Pi/TDWSTZ3+N9T8MJ7w3WszWRxavWmLS2lH4dAZrJK0oaAiYN33tDqsegmEF0Lp1
+oKshsJr9K1SGHQ0fwtR3H3ImXq2Knwk2bUS+dMJaPv9cnXXWOHvFOflEbqHRjv9j
+28S7xpGSylxgmx3rCt4I7r/adgNlYimL+H/HhnMe9h8tiWn9vr1u
+-----END CERTIFICATE-----
diff --git a/test/net/fixtures/server.key b/test/net/fixtures/server.key
new file mode 100644
index 0000000000..7f2380e71e
--- /dev/null
+++ b/test/net/fixtures/server.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDo2pwBLisQ7EnN
+XgcTB5xwnsZ0vBPC4W/Ggv3jSOAspWjHnkLeYFRl5moUV3ow0My1ttnD0t/JJZdU
+Z8/2vl7Li+4DxeHi+ef30QxH8LjaM1qtQa3ntaJ7t78w2mD441SivDr9G3TZ3HRC
+6Sm+36y0T+sy9Abx4YxLqIv7Keexvx0B7nMP+UDc1RV52cZz0MDdy+TaGUeAxhQE
+cv2afI8RgnZJBHnM8lwxIpUTPl1Apk3gowImfVI7u+1loQ/ta7A81N5hFV7T3WgJ
+n0pXpcKpbYaSxfSk1LcTO1JjJAXizOOKPNQ1NCsQu1hy5+GNHXSMYRYgPdAcTo9u
+/f5kEE9BAgMBAAECggEBAOHkwhc7DLh8IhTDNSW26oMu5OP2WU1jmiYAigDmf+OQ
+DBgrZj+JQBci8qINQxL8XLukSZn5hvQCLc7Kbyu1/wyEEUFDxSGGwwzclodr9kho
+LX2LDASPZrOSzD2+fPi2wTKmXKuS6Uc44OjQfZkYMNkz9r4Vkm8xGgOD3VipjIYX
+QXlhhdqkXZcNABsihCV52GKkDFSVm8jv95YJc5xhoYCy/3a4/qPdF0aT2R7oYUej
+hKrxVDskyooe8Zg/JTydZNV5GQEDmW01/K3r6XGT26oPi1AqMU1gtv/jkW56CRQQ
+1got8smnqM+AV7Slf9R6DauIPdQJ2S8wsr/o8ISBsOECgYEA9YrqEP2gAYSGFXRt
+liw0WI2Ant8BqXS6yvq1jLo/qWhLw/ph4Di73OQ2mpycVTpgfGr2wFPQR1XJ+0Fd
+U+Ir/C3Q7FK4VIGHK7B0zNvZr5tEjlFfeRezo2JMVw5YWeSagIFcSwK+KqCTH9qc
+pw/Eb8nB/4XNcpTZu7Fg0Wc+ooUCgYEA8sVaicn1Wxkpb45a4qfrA6wOr5xdJ4cC
+A5qs7vjX2OdPIQOmoQhdI7bCWFXZzF33wA4YCws6j5wRaySLIJqdms8Gl9QnODy1
+ZlA5gwKToBC/jqPmWAXSKb8EH7cHilaxU9OKnQ7CfwlGLHqjMtjrhR7KHlt3CVRs
+oRmvsjZVXI0CgYAmPedslAO6mMhFSSfULrhMXmV82OCqYrrA6EEkVNGbcdnzAOkD
+gfKIWabDd8bFY10po4Mguy0CHzNhBXIioWQWV5BlbhC1YKMLw+S9DzSdLAKGY9gJ
+xQ4+UQ3wtRQ/k+IYR413RUsW2oFvgZ3KSyNeAb9MK6uuv84VdG/OzVSs/QKBgQDn
+kap//l2EbObiWyaERunckdVcW0lcN+KK75J/TGwPoOwQsLvTpPe65kxRGGrtDsEQ
+uCDk/+v3KkZPLgdrrTAih9FhJ+PVN8tMcb+6IM4SA4fFFr/UPJEwct0LJ3oQ0grJ
+y+HPWFHb/Uurh7t99/4H98uR02sjQh1wOeEmm78mzQKBgQDm+LzGH0se6CXQ6cdZ
+g1JRZeXkDEsrW3hfAsW62xJQmXcWxBoblP9OamMY+A06rM5og3JbDk5Zm6JsOaA8
+wS2gw4ilp46jors4eQey8ux7kB9LzdBoDBBElnsbjLO8oBNZlVcYXg+6BOl/CUi7
+2whRF0FEjKA8ehrNhAq+VFfFNw==
+-----END PRIVATE KEY-----
diff --git a/test/net/ftp/test_buffered_socket.rb b/test/net/ftp/test_buffered_socket.rb
index 3cc46fa555..875c53f4e0 100644
--- a/test/net/ftp/test_buffered_socket.rb
+++ b/test/net/ftp/test_buffered_socket.rb
@@ -33,6 +33,12 @@ class BufferedSocketTest < Test::Unit::TestCase
assert_equal("bar", sock.gets)
end
+ def test_read_nil
+ sock = create_buffered_socket("foo\nbar")
+ assert_equal("foo\nbar", sock.read)
+ assert_equal("", sock.read)
+ end
+
private
def create_buffered_socket(s)
diff --git a/test/net/ftp/test_ftp.rb b/test/net/ftp/test_ftp.rb
index ca71a918e0..b3fe7774ed 100644
--- a/test/net/ftp/test_ftp.rb
+++ b/test/net/ftp/test_ftp.rb
@@ -5,9 +5,19 @@ require "test/unit"
require "ostruct"
require "stringio"
require "tempfile"
+require "tmpdir"
class FTPTest < Test::Unit::TestCase
- SERVER_ADDR = "127.0.0.1"
+ SERVER_NAME = "localhost"
+ SERVER_ADDR =
+ begin
+ Addrinfo.getaddrinfo(SERVER_NAME, 0, nil, :STREAM)[0].ip_address
+ rescue SocketError
+ "127.0.0.1"
+ end
+ CA_FILE = File.expand_path("../fixtures/cacert.pem", __dir__)
+ SERVER_KEY = File.expand_path("../fixtures/server.key", __dir__)
+ SERVER_CERT = File.expand_path("../fixtures/server.crt", __dir__)
def setup
@thread = nil
@@ -51,7 +61,7 @@ class FTPTest < Test::Unit::TestCase
end
def test_parse227
- ftp = Net::FTP.new
+ ftp = Net::FTP.new(nil, use_pasv_ip: true)
host, port = ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34)")
assert_equal("192.168.0.1", host)
assert_equal(3106, port)
@@ -70,6 +80,14 @@ class FTPTest < Test::Unit::TestCase
assert_raise(Net::FTPProtoError) do
ftp.send(:parse227, "227 ) foo bar (")
end
+
+ ftp = Net::FTP.new
+ sock = OpenStruct.new
+ sock.remote_address = OpenStruct.new
+ sock.remote_address.ip_address = "10.0.0.1"
+ ftp.instance_variable_set(:@bare_sock, sock)
+ host, port = ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34)")
+ assert_equal("10.0.0.1", host)
end
def test_parse228
@@ -109,8 +127,9 @@ class FTPTest < Test::Unit::TestCase
def test_parse229
ftp = Net::FTP.new
sock = OpenStruct.new
- sock.peeraddr = [nil, nil, nil, "1080:0000:0000:0000:0008:0800:200c:417a"]
- ftp.instance_variable_set(:@sock, sock)
+ sock.remote_address = OpenStruct.new
+ sock.remote_address.ip_address = "1080:0000:0000:0000:0008:0800:200c:417a"
+ ftp.instance_variable_set(:@bare_sock, sock)
host, port = ftp.send(:parse229, "229 Entering Passive Mode (|||3106|)")
assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host)
assert_equal(3106, port)
@@ -218,6 +237,72 @@ class FTPTest < Test::Unit::TestCase
end
end
+ def test_implicit_login
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("332 Need account for login.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new(SERVER_ADDR,
+ port: server.port,
+ username: "foo",
+ password: "bar",
+ account: "baz")
+ assert_equal("USER foo\r\n", commands.shift)
+ assert_equal("PASS bar\r\n", commands.shift)
+ assert_equal("ACCT baz\r\n", commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_s_open
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ Net::FTP.open(SERVER_ADDR, port: server.port, username: "anonymous") do
+ end
+ assert_equal("USER anonymous\r\n", commands.shift)
+ assert_equal("PASS anonymous@\r\n", commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ server.close
+ end
+ end
+
+ def test_s_new_timeout_options
+ ftp = Net::FTP.new
+ assert_equal(nil, ftp.open_timeout)
+ assert_equal(60, ftp.read_timeout)
+
+ ftp = Net::FTP.new(nil, open_timeout: 123, read_timeout: 234)
+ assert_equal(123, ftp.open_timeout)
+ assert_equal(234, ftp.read_timeout)
+ end
+
# TODO: How can we test open_timeout? sleep before accept cannot delay
# connections.
def _test_open_timeout_exceeded
@@ -258,7 +343,7 @@ class FTPTest < Test::Unit::TestCase
sleep(0.1)
sock.print("331 Please specify the password.\r\n")
commands.push(sock.gets)
- sleep(0.3)
+ sleep(2.0) # Net::ReadTimeout
sock.print("230 Login successful.\r\n")
commands.push(sock.gets)
sleep(0.1)
@@ -267,7 +352,7 @@ class FTPTest < Test::Unit::TestCase
begin
begin
ftp = Net::FTP.new
- ftp.read_timeout = 0.2
+ ftp.read_timeout = 0.4
ftp.connect(SERVER_ADDR, server.port)
assert_raise(Net::ReadTimeout) do
ftp.login
@@ -300,7 +385,7 @@ class FTPTest < Test::Unit::TestCase
begin
begin
ftp = Net::FTP.new
- ftp.read_timeout = 0.2
+ ftp.read_timeout = 1.0
ftp.connect(SERVER_ADDR, server.port)
ftp.login
assert_match(/\AUSER /, commands.shift)
@@ -309,7 +394,7 @@ class FTPTest < Test::Unit::TestCase
assert_equal(nil, commands.shift)
ensure
ftp.close
- assert_equal(0.2, ftp.read_timeout)
+ assert_equal(1.0, ftp.read_timeout)
end
ensure
server.close
@@ -335,10 +420,7 @@ class FTPTest < Test::Unit::TestCase
sock.print("200 Switching to ASCII mode.\r\n")
line = sock.gets
commands.push(line)
- port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
- host = port_args[0, 4].join(".")
- port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("150 Here comes the directory listing.\r\n")
begin
@@ -371,7 +453,7 @@ class FTPTest < Test::Unit::TestCase
ftp.list
end
assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("LIST\r\n", commands.shift)
assert_equal(nil, commands.shift)
ensure
@@ -401,10 +483,7 @@ class FTPTest < Test::Unit::TestCase
sock.print("200 Switching to ASCII mode.\r\n")
line = sock.gets
commands.push(line)
- port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
- host = port_args[0, 4].join(".")
- port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("150 Here comes the directory listing.\r\n")
conn = TCPSocket.new(host, port)
@@ -420,7 +499,7 @@ class FTPTest < Test::Unit::TestCase
begin
begin
ftp = Net::FTP.new
- ftp.read_timeout = 0.2
+ ftp.read_timeout = 1.0
ftp.connect(SERVER_ADDR, server.port)
ftp.login
assert_match(/\AUSER /, commands.shift)
@@ -428,7 +507,7 @@ class FTPTest < Test::Unit::TestCase
assert_equal("TYPE I\r\n", commands.shift)
assert_equal(list_lines, ftp.list)
assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("LIST\r\n", commands.shift)
assert_equal("TYPE I\r\n", commands.shift)
assert_equal(nil, commands.shift)
@@ -454,7 +533,7 @@ class FTPTest < Test::Unit::TestCase
sock.print("200 Switching to ASCII mode.\r\n")
line = sock.gets
commands.push(line)
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("553 Requested action not taken.\r\n")
commands.push(sock.gets)
@@ -463,7 +542,6 @@ class FTPTest < Test::Unit::TestCase
begin
begin
ftp = Net::FTP.new
- ftp.read_timeout = 0.2
ftp.connect(SERVER_ADDR, server.port)
ftp.login
assert_match(/\AUSER /, commands.shift)
@@ -471,7 +549,7 @@ class FTPTest < Test::Unit::TestCase
assert_equal("TYPE I\r\n", commands.shift)
assert_raise(Net::FTPPermError){ ftp.list }
assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("LIST\r\n", commands.shift)
assert_equal("TYPE I\r\n", commands.shift)
assert_equal(nil, commands.shift)
@@ -504,7 +582,6 @@ class FTPTest < Test::Unit::TestCase
begin
begin
ftp = Net::FTP.new
- ftp.read_timeout = 0.2
ftp.connect(SERVER_ADDR, server.port)
ftp.login
assert_match(/\AUSER /, commands.shift)
@@ -512,7 +589,7 @@ class FTPTest < Test::Unit::TestCase
assert_equal("TYPE I\r\n", commands.shift)
assert_raise(Net::FTPTempError){ ftp.list }
assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("TYPE I\r\n", commands.shift)
assert_equal(nil, commands.shift)
ensure
@@ -536,16 +613,13 @@ class FTPTest < Test::Unit::TestCase
sock.print("200 Switching to Binary mode.\r\n")
line = sock.gets
commands.push(line)
- port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
- host = port_args[0, 4].join(".")
- port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
conn = TCPSocket.new(host, port)
sleep(0.1)
conn.print(binary_data[0,1024])
- sleep(0.5)
+ sleep(1.0)
conn.print(binary_data[1024, 1024]) rescue nil # may raise EPIPE or something
conn.close
sock.print("226 Transfer complete.\r\n")
@@ -553,7 +627,7 @@ class FTPTest < Test::Unit::TestCase
begin
begin
ftp = Net::FTP.new
- ftp.read_timeout = 0.2
+ ftp.read_timeout = 0.5
ftp.connect(SERVER_ADDR, server.port)
ftp.login
assert_match(/\AUSER /, commands.shift)
@@ -567,7 +641,7 @@ class FTPTest < Test::Unit::TestCase
end
assert_equal(1024, buf.bytesize)
assert_equal(binary_data[0, 1024], buf)
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("RETR foo\r\n", commands.shift)
assert_equal(nil, commands.shift)
ensure
@@ -591,15 +665,12 @@ class FTPTest < Test::Unit::TestCase
sock.print("200 Switching to Binary mode.\r\n")
line = sock.gets
commands.push(line)
- port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
- host = port_args[0, 4].join(".")
- port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
conn = TCPSocket.new(host, port)
binary_data.scan(/.{1,1024}/nm) do |s|
- sleep(0.1)
+ sleep(0.2)
conn.print(s)
end
conn.shutdown(Socket::SHUT_WR)
@@ -610,7 +681,7 @@ class FTPTest < Test::Unit::TestCase
begin
begin
ftp = Net::FTP.new
- ftp.read_timeout = 0.2
+ ftp.read_timeout = 1.0
ftp.connect(SERVER_ADDR, server.port)
ftp.login
assert_match(/\AUSER /, commands.shift)
@@ -622,7 +693,7 @@ class FTPTest < Test::Unit::TestCase
end
assert_equal(binary_data.bytesize, buf.bytesize)
assert_equal(binary_data, buf)
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("RETR foo\r\n", commands.shift)
assert_equal(nil, commands.shift)
ensure
@@ -645,21 +716,20 @@ class FTPTest < Test::Unit::TestCase
sock.print("200 Switching to Binary mode.\r\n")
line = sock.gets
commands.push(line)
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("550 Requested action not taken.\r\n")
}
begin
begin
ftp = Net::FTP.new
- ftp.read_timeout = 0.2
ftp.connect(SERVER_ADDR, server.port)
ftp.login
assert_match(/\AUSER /, commands.shift)
assert_match(/\APASS /, commands.shift)
assert_equal("TYPE I\r\n", commands.shift)
assert_raise(Net::FTPPermError){ ftp.retrbinary("RETR foo", 1024) }
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("RETR foo\r\n", commands.shift)
assert_equal(nil, commands.shift)
ensure
@@ -683,10 +753,7 @@ class FTPTest < Test::Unit::TestCase
sock.print("200 Switching to Binary mode.\r\n")
line = sock.gets
commands.push(line)
- port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
- host = port_args[0, 4].join(".")
- port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
conn = TCPSocket.new(host, port)
@@ -701,7 +768,6 @@ class FTPTest < Test::Unit::TestCase
begin
begin
ftp = Net::FTP.new
- ftp.read_timeout = 0.2
ftp.connect(SERVER_ADDR, server.port)
ftp.login
assert_match(/\AUSER /, commands.shift)
@@ -710,7 +776,7 @@ class FTPTest < Test::Unit::TestCase
buf = ftp.getbinaryfile("foo", nil)
assert_equal(binary_data, buf)
assert_equal(Encoding::ASCII_8BIT, buf.encoding)
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("RETR foo\r\n", commands.shift)
assert_equal(nil, commands.shift)
ensure
@@ -734,10 +800,7 @@ class FTPTest < Test::Unit::TestCase
sock.print("200 Switching to Binary mode.\r\n")
line = sock.gets
commands.push(line)
- port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
- host = port_args[0, 4].join(".")
- port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
conn = TCPSocket.new(host, port)
@@ -749,7 +812,6 @@ class FTPTest < Test::Unit::TestCase
begin
begin
ftp = Net::FTP.new
- ftp.read_timeout = 0.2
ftp.connect(SERVER_ADDR, server.port)
ftp.login
assert_match(/\AUSER /, commands.shift)
@@ -758,7 +820,7 @@ class FTPTest < Test::Unit::TestCase
buf = ftp.getbinaryfile("foo", nil)
assert_equal(binary_data, buf)
assert_equal(Encoding::ASCII_8BIT, buf.encoding)
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("RETR foo\r\n", commands.shift)
assert_equal(nil, commands.shift)
ensure
@@ -782,10 +844,7 @@ class FTPTest < Test::Unit::TestCase
sock.print("200 Switching to Binary mode.\r\n")
line = sock.gets
commands.push(line)
- port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
- host = port_args[0, 4].join(".")
- port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
conn = TCPSocket.new(host, port)
@@ -800,7 +859,6 @@ class FTPTest < Test::Unit::TestCase
begin
begin
ftp = Net::FTP.new
- ftp.read_timeout = 0.2
ftp.connect(SERVER_ADDR, server.port)
ftp.login
assert_match(/\AUSER /, commands.shift)
@@ -817,7 +875,7 @@ class FTPTest < Test::Unit::TestCase
assert_equal(Encoding::ASCII_8BIT, buf.encoding)
assert_equal(binary_data, f.read)
end
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("RETR foo\r\n", commands.shift)
assert_equal(nil, commands.shift)
ensure
@@ -842,10 +900,7 @@ class FTPTest < Test::Unit::TestCase
sock.print("200 Switching to Binary mode.\r\n")
line = sock.gets
commands.push(line)
- port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
- host = port_args[0, 4].join(".")
- port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("150 Opening BINARY mode data connection for foo\r\n")
conn = TCPSocket.new(host, port)
@@ -856,7 +911,6 @@ class FTPTest < Test::Unit::TestCase
begin
begin
ftp = Net::FTP.new
- ftp.read_timeout = 0.2
ftp.connect(SERVER_ADDR, server.port)
ftp.login
assert_match(/\AUSER /, commands.shift)
@@ -864,7 +918,7 @@ class FTPTest < Test::Unit::TestCase
assert_equal("TYPE I\r\n", commands.shift)
ftp.storbinary("STOR foo", StringIO.new(binary_data), 1024)
assert_equal(binary_data, stored_data)
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("STOR foo\r\n", commands.shift)
assert_equal(nil, commands.shift)
ensure
@@ -888,21 +942,20 @@ class FTPTest < Test::Unit::TestCase
sock.print("200 Switching to Binary mode.\r\n")
line = sock.gets
commands.push(line)
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("452 Requested file action aborted.\r\n")
}
begin
begin
ftp = Net::FTP.new
- ftp.read_timeout = 0.2
ftp.connect(SERVER_ADDR, server.port)
ftp.login
assert_match(/\AUSER /, commands.shift)
assert_match(/\APASS /, commands.shift)
assert_equal("TYPE I\r\n", commands.shift)
assert_raise(Net::FTPTempError){ ftp.storbinary("STOR foo", StringIO.new(binary_data), 1024) }
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("STOR foo\r\n", commands.shift)
assert_equal(nil, commands.shift)
ensure
@@ -932,15 +985,12 @@ EOF
sock.print("200 Switching to ASCII mode.\r\n")
line = sock.gets
commands.push(line)
- port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
- host = port_args[0, 4].join(".")
- port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("150 Opening TEXT mode data connection for foo (#{text_data.size} bytes)\r\n")
conn = TCPSocket.new(host, port)
- text_data.each_line do |line|
- conn.print(line)
+ text_data.each_line do |l|
+ conn.print(l)
end
conn.shutdown(Socket::SHUT_WR)
conn.read
@@ -964,7 +1014,7 @@ EOF
assert_equal(text_data.bytesize, buf.bytesize)
assert_equal(text_data, buf)
assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("RETR foo\r\n", commands.shift)
assert_equal("TYPE I\r\n", commands.shift)
assert_equal(nil, commands.shift)
@@ -995,15 +1045,12 @@ EOF
sock.print("200 Switching to ASCII mode.\r\n")
line = sock.gets
commands.push(line)
- port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
- host = port_args[0, 4].join(".")
- port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("150 Opening TEXT mode data connection for foo (#{text_data.size} bytes)\r\n")
conn = TCPSocket.new(host, port)
- text_data.each_line do |line|
- conn.print(line)
+ text_data.each_line do |l|
+ conn.print(l)
end
conn.shutdown(Socket::SHUT_WR)
conn.read
@@ -1024,7 +1071,7 @@ EOF
assert_equal(text_data.gsub(/\r\n/, "\n"), buf)
assert_equal(Encoding::ASCII_8BIT, buf.encoding)
assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("RETR foo\r\n", commands.shift)
assert_equal("TYPE I\r\n", commands.shift)
assert_equal(nil, commands.shift)
@@ -1055,15 +1102,12 @@ EOF
sock.print("200 Switching to ASCII mode.\r\n")
line = sock.gets
commands.push(line)
- port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
- host = port_args[0, 4].join(".")
- port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("150 Opening TEXT mode data connection for foo (#{text_data.size} bytes)\r\n")
conn = TCPSocket.new(host, port)
- text_data.each_line do |line|
- conn.print(line)
+ text_data.each_line do |l|
+ conn.print(l)
end
conn.shutdown(Socket::SHUT_WR)
conn.read
@@ -1091,7 +1135,7 @@ EOF
assert_equal(buf, f.read)
end
assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("RETR foo\r\n", commands.shift)
assert_equal("TYPE I\r\n", commands.shift)
assert_equal(nil, commands.shift)
@@ -1123,10 +1167,7 @@ EOF
sock.print("200 Switching to ASCII mode.\r\n")
line = sock.gets
commands.push(line)
- port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
- host = port_args[0, 4].join(".")
- port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("150 Here comes the directory listing.\r\n")
conn = TCPSocket.new(host, port)
@@ -1139,10 +1180,7 @@ EOF
sock.print("200 Switching to Binary mode.\r\n")
line = sock.gets
commands.push(line)
- port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
- host = port_args[0, 4].join(".")
- port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
conn = TCPSocket.new(host, port)
@@ -1166,10 +1204,10 @@ EOF
end
end
assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("LIST\r\n", commands.shift)
assert_equal("TYPE I\r\n", commands.shift)
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_equal("RETR baz.bin\r\n", commands.shift)
assert_equal(nil, commands.shift)
ensure
@@ -1196,7 +1234,6 @@ EOF
begin
begin
ftp = Net::FTP.new
- #ftp.read_timeout = 0.2
ftp.connect(SERVER_ADDR, server.port)
ftp.login
assert_match(/\AUSER /, commands.shift)
@@ -1229,7 +1266,6 @@ EOF
begin
begin
ftp = Net::FTP.new
- ftp.read_timeout = 0.2
ftp.connect(SERVER_ADDR, server.port)
ftp.login
assert_match(/\AUSER /, commands.shift)
@@ -1246,6 +1282,38 @@ EOF
end
end
+ def test_status_path
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.gets)
+ sock.print("213 End of status\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ ftp.status "/"
+ assert_equal("STAT /\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
def test_pathnames
require 'pathname'
@@ -1555,16 +1623,13 @@ EOF
sock.print("200 Switching to ASCII mode.\r\n")
line = sock.gets
commands.push(line)
- port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
- host = port_args[0, 4].join(".")
- port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
- sock.print("200 PORT command successful.\r\n")
+ host, port = process_port_or_eprt(sock, line)
commands.push(sock.gets)
sock.print("150 Here comes the directory listing.\r\n")
begin
conn = TCPSocket.new(host, port)
- entry_lines.each do |line|
- conn.print(line, "\r\n")
+ entry_lines.each do |l|
+ conn.print(l, "\r\n")
end
rescue Errno::EPIPE
ensure
@@ -1602,7 +1667,7 @@ EOF
assert_equal(123456, modify.usec)
assert_equal(true, modify.utc?)
assert_equal("TYPE A\r\n", commands.shift)
- assert_match(/\APORT /, commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
assert_match("MLSD /\r\n", commands.shift)
assert_equal("TYPE I\r\n", commands.shift)
assert_equal(nil, commands.shift)
@@ -1643,10 +1708,815 @@ EOF
end
end
+ if defined?(OpenSSL::SSL)
+ def test_tls_unknown_ca
+ assert_raise(OpenSSL::SSL::SSLError) do
+ tls_test do |port|
+ begin
+ Net::FTP.new(SERVER_NAME,
+ :port => port,
+ :ssl => true)
+ rescue SystemCallError
+ skip $!
+ end
+ end
+ end
+ end
+
+ def test_tls_with_ca_file
+ assert_nothing_raised do
+ tls_test do |port|
+ begin
+ Net::FTP.new(SERVER_NAME,
+ :port => port,
+ :ssl => { :ca_file => CA_FILE })
+ rescue SystemCallError
+ skip $!
+ end
+ end
+ end
+ end
+
+ def test_tls_verify_none
+ assert_nothing_raised do
+ tls_test do |port|
+ Net::FTP.new(SERVER_ADDR,
+ :port => port,
+ :ssl => { :verify_mode => OpenSSL::SSL::VERIFY_NONE })
+ end
+ end
+ end
+
+ def test_tls_post_connection_check
+ assert_raise(OpenSSL::SSL::SSLError) do
+ tls_test do |port|
+ # SERVER_ADDR is different from the hostname in the certificate,
+ # so the following code should raise a SSLError.
+ Net::FTP.new(SERVER_ADDR,
+ :port => port,
+ :ssl => { :ca_file => CA_FILE })
+ end
+ end
+ end
+
+ def test_active_private_data_connection
+ server = TCPServer.new(SERVER_ADDR, 0)
+ port = server.addr[1]
+ commands = []
+ session_reused_for_data_connection = nil
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ @thread = Thread.start do
+ sock = server.accept
+ begin
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("234 AUTH success.\r\n")
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ca_file = CA_FILE
+ ctx.key = File.open(SERVER_KEY) { |f|
+ OpenSSL::PKey::RSA.new(f)
+ }
+ ctx.cert = File.open(SERVER_CERT) { |f|
+ OpenSSL::X509::Certificate.new(f)
+ }
+ sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ sock.sync_close = true
+ begin
+ sock.accept
+ commands.push(sock.gets)
+ sock.print("200 PSBZ success.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 PROT success.\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ host, port = process_port_or_eprt(sock, line)
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
+ conn = TCPSocket.new(host, port)
+ conn = OpenSSL::SSL::SSLSocket.new(conn, ctx)
+ conn.sync_close = true
+ conn.accept
+ session_reused_for_data_connection = conn.session_reused?
+ binary_data.scan(/.{1,1024}/nm) do |s|
+ conn.print(s)
+ end
+ conn.close
+ sock.print("226 Transfer complete.\r\n")
+ rescue OpenSSL::SSL::SSLError
+ end
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ ftp = Net::FTP.new(SERVER_NAME,
+ port: port,
+ ssl: { ca_file: CA_FILE },
+ passive: false)
+ begin
+ assert_equal("AUTH TLS\r\n", commands.shift)
+ assert_equal("PBSZ 0\r\n", commands.shift)
+ assert_equal("PROT P\r\n", commands.shift)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ buf = ftp.getbinaryfile("foo", nil)
+ assert_equal(binary_data, buf)
+ assert_equal(Encoding::ASCII_8BIT, buf.encoding)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
+ assert_equal("RETR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ # FIXME: The new_session_cb is known broken for clients in OpenSSL 1.1.0h.
+ # See https://github.com/openssl/openssl/pull/5967 for details.
+ if OpenSSL::OPENSSL_LIBRARY_VERSION !~ /OpenSSL 1.1.0h/
+ assert_equal(true, session_reused_for_data_connection)
+ end
+ ensure
+ ftp.close
+ end
+ end
+
+ def test_passive_private_data_connection
+ server = TCPServer.new(SERVER_ADDR, 0)
+ port = server.addr[1]
+ commands = []
+ session_reused_for_data_connection = nil
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ @thread = Thread.start do
+ sock = server.accept
+ begin
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("234 AUTH success.\r\n")
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ca_file = CA_FILE
+ ctx.key = File.open(SERVER_KEY) { |f|
+ OpenSSL::PKey::RSA.new(f)
+ }
+ ctx.cert = File.open(SERVER_CERT) { |f|
+ OpenSSL::X509::Certificate.new(f)
+ }
+ sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ sock.sync_close = true
+ begin
+ sock.accept
+ commands.push(sock.gets)
+ sock.print("200 PSBZ success.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 PROT success.\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.gets)
+ data_server = create_data_connection_server(sock)
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
+ conn = data_server.accept
+ conn = OpenSSL::SSL::SSLSocket.new(conn, ctx)
+ conn.sync_close = true
+ conn.accept
+ session_reused_for_data_connection = conn.session_reused?
+ binary_data.scan(/.{1,1024}/nm) do |s|
+ conn.print(s)
+ end
+ conn.close
+ data_server.close
+ sock.print("226 Transfer complete.\r\n")
+ rescue OpenSSL::SSL::SSLError
+ end
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ ftp = Net::FTP.new(SERVER_NAME,
+ port: port,
+ ssl: { ca_file: CA_FILE },
+ passive: true)
+ begin
+ assert_equal("AUTH TLS\r\n", commands.shift)
+ assert_equal("PBSZ 0\r\n", commands.shift)
+ assert_equal("PROT P\r\n", commands.shift)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ buf = ftp.getbinaryfile("foo", nil)
+ assert_equal(binary_data, buf)
+ assert_equal(Encoding::ASCII_8BIT, buf.encoding)
+ assert_match(/\A(PASV|EPSV)\r\n/, commands.shift)
+ assert_equal("RETR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ # FIXME: The new_session_cb is known broken for clients in OpenSSL 1.1.0h.
+ if OpenSSL::OPENSSL_LIBRARY_VERSION !~ /OpenSSL 1.1.0h/
+ assert_equal(true, session_reused_for_data_connection)
+ end
+ ensure
+ ftp.close
+ end
+ end
+
+ def test_active_clear_data_connection
+ server = TCPServer.new(SERVER_ADDR, 0)
+ port = server.addr[1]
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ @thread = Thread.start do
+ sock = server.accept
+ begin
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("234 AUTH success.\r\n")
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ca_file = CA_FILE
+ ctx.key = File.open(SERVER_KEY) { |f|
+ OpenSSL::PKey::RSA.new(f)
+ }
+ ctx.cert = File.open(SERVER_CERT) { |f|
+ OpenSSL::X509::Certificate.new(f)
+ }
+ sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ sock.sync_close = true
+ begin
+ sock.accept
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ host, port = process_port_or_eprt(sock, line)
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
+ conn = TCPSocket.new(host, port)
+ binary_data.scan(/.{1,1024}/nm) do |s|
+ conn.print(s)
+ end
+ conn.close
+ sock.print("226 Transfer complete.\r\n")
+ rescue OpenSSL::SSL::SSLError
+ end
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ ftp = Net::FTP.new(SERVER_NAME,
+ port: port,
+ ssl: { ca_file: CA_FILE },
+ private_data_connection: false,
+ passive: false)
+ begin
+ assert_equal("AUTH TLS\r\n", commands.shift)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ buf = ftp.getbinaryfile("foo", nil)
+ assert_equal(binary_data, buf)
+ assert_equal(Encoding::ASCII_8BIT, buf.encoding)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
+ assert_equal("RETR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close
+ end
+ end
+
+ def test_passive_clear_data_connection
+ server = TCPServer.new(SERVER_ADDR, 0)
+ port = server.addr[1]
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ @thread = Thread.start do
+ sock = server.accept
+ begin
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("234 AUTH success.\r\n")
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ca_file = CA_FILE
+ ctx.key = File.open(SERVER_KEY) { |f|
+ OpenSSL::PKey::RSA.new(f)
+ }
+ ctx.cert = File.open(SERVER_CERT) { |f|
+ OpenSSL::X509::Certificate.new(f)
+ }
+ sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ sock.sync_close = true
+ begin
+ sock.accept
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.gets)
+ data_server = create_data_connection_server(sock)
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
+ conn = data_server.accept
+ binary_data.scan(/.{1,1024}/nm) do |s|
+ conn.print(s)
+ end
+ conn.close
+ data_server.close
+ sock.print("226 Transfer complete.\r\n")
+ rescue OpenSSL::SSL::SSLError
+ end
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ ftp = Net::FTP.new(SERVER_NAME,
+ port: port,
+ ssl: { ca_file: CA_FILE },
+ private_data_connection: false,
+ passive: true)
+ begin
+ assert_equal("AUTH TLS\r\n", commands.shift)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ buf = ftp.getbinaryfile("foo", nil)
+ assert_equal(binary_data, buf)
+ assert_equal(Encoding::ASCII_8BIT, buf.encoding)
+ assert_match(/\A(PASV|EPSV)\r\n/, commands.shift)
+ assert_equal("RETR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close
+ end
+ end
+
+ def test_tls_connect_timeout
+ server = TCPServer.new(SERVER_ADDR, 0)
+ port = server.addr[1]
+ commands = []
+ sock = nil
+ @thread = Thread.start do
+ sock = server.accept
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("234 AUTH success.\r\n")
+ end
+ begin
+ assert_raise(Net::OpenTimeout) do
+ Net::FTP.new(SERVER_NAME,
+ port: port,
+ ssl: { ca_file: CA_FILE },
+ ssl_handshake_timeout: 0.1)
+ end
+ @thread.join
+ ensure
+ sock.close if sock
+ server.close
+ end
+ end
+ end
+
+ def test_abort_tls
+ return unless defined?(OpenSSL)
+
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("234 AUTH success.\r\n")
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ca_file = CA_FILE
+ ctx.key = File.open(SERVER_KEY) { |f|
+ OpenSSL::PKey::RSA.new(f)
+ }
+ ctx.cert = File.open(SERVER_CERT) { |f|
+ OpenSSL::X509::Certificate.new(f)
+ }
+ sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ sock.sync_close = true
+ sock.accept
+ commands.push(sock.gets)
+ sock.print("200 PSBZ success.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 PROT success.\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.gets)
+ sock.print("225 No transfer to ABOR.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new(SERVER_NAME,
+ port: server.port,
+ ssl: { ca_file: CA_FILE })
+ assert_equal("AUTH TLS\r\n", commands.shift)
+ assert_equal("PBSZ 0\r\n", commands.shift)
+ assert_equal("PROT P\r\n", commands.shift)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ ftp.abort
+ assert_equal("ABOR\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ rescue RuntimeError, LoadError
+ # skip (require openssl)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_getbinaryfile_command_injection
+ skip "| is not allowed in filename on Windows" if windows?
+ [false, true].each do |resume|
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ host, port = process_port_or_eprt(sock, line)
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for |echo hello (#{binary_data.size} bytes)\r\n")
+ conn = TCPSocket.new(host, port)
+ binary_data.scan(/.{1,1024}/nm) do |s|
+ conn.print(s)
+ end
+ conn.shutdown(Socket::SHUT_WR)
+ conn.read
+ conn.close
+ sock.print("226 Transfer complete.\r\n")
+ }
+ begin
+ chdir_to_tmpdir do
+ begin
+ ftp = Net::FTP.new
+ ftp.resume = resume
+ ftp.read_timeout = RubyVM::MJIT.enabled? ? 5 : 0.2 # use large timeout for --jit-wait
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ ftp.getbinaryfile("|echo hello")
+ assert_equal(binary_data, File.binread("./|echo hello"))
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
+ assert_equal("RETR |echo hello\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ end
+ ensure
+ server.close
+ end
+ end
+ end
+
+ def test_gettextfile_command_injection
+ skip "| is not allowed in filename on Windows" if windows?
+ commands = []
+ text_data = <<EOF.gsub(/\n/, "\r\n")
+foo
+bar
+baz
+EOF
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to ASCII mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ host, port = process_port_or_eprt(sock, line)
+ commands.push(sock.gets)
+ sock.print("150 Opening TEXT mode data connection for |echo hello (#{text_data.size} bytes)\r\n")
+ conn = TCPSocket.new(host, port)
+ text_data.each_line do |l|
+ conn.print(l)
+ end
+ conn.shutdown(Socket::SHUT_WR)
+ conn.read
+ conn.close
+ sock.print("226 Transfer complete.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ chdir_to_tmpdir do
+ begin
+ ftp = Net::FTP.new
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ ftp.gettextfile("|echo hello")
+ assert_equal(text_data.gsub(/\r\n/, "\n"),
+ File.binread("./|echo hello"))
+ assert_equal("TYPE A\r\n", commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
+ assert_equal("RETR |echo hello\r\n", commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_putbinaryfile_command_injection
+ skip "| is not allowed in filename on Windows" if windows?
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ received_data = nil
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ host, port = process_port_or_eprt(sock, line)
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for |echo hello (#{binary_data.size} bytes)\r\n")
+ conn = TCPSocket.new(host, port)
+ received_data = conn.read
+ conn.close
+ sock.print("226 Transfer complete.\r\n")
+ }
+ begin
+ chdir_to_tmpdir do
+ File.binwrite("./|echo hello", binary_data)
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ ftp.putbinaryfile("|echo hello")
+ assert_equal(binary_data, received_data)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
+ assert_equal("STOR |echo hello\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_puttextfile_command_injection
+ skip "| is not allowed in filename on Windows" if windows?
+ commands = []
+ received_data = nil
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to ASCII mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ host, port = process_port_or_eprt(sock, line)
+ commands.push(sock.gets)
+ sock.print("150 Opening TEXT mode data connection for |echo hello\r\n")
+ conn = TCPSocket.new(host, port)
+ received_data = conn.read
+ conn.close
+ sock.print("226 Transfer complete.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ chdir_to_tmpdir do
+ File.open("|echo hello", "w") do |f|
+ f.puts("foo")
+ f.puts("bar")
+ f.puts("baz")
+ end
+ begin
+ ftp = Net::FTP.new
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ ftp.puttextfile("|echo hello")
+ assert_equal(<<EOF.gsub(/\n/, "\r\n"), received_data)
+foo
+bar
+baz
+EOF
+ assert_equal("TYPE A\r\n", commands.shift)
+ assert_match(/\A(PORT|EPRT) /, commands.shift)
+ assert_equal("STOR |echo hello\r\n", commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_ignore_pasv_ip
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server(nil, "127.0.0.1") { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ data_server = TCPServer.new("127.0.0.1", 0)
+ port = data_server.local_address.ip_port
+ sock.printf("227 Entering Passive Mode (999,0,0,1,%s).\r\n",
+ port.divmod(256).join(","))
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
+ conn = data_server.accept
+ binary_data.scan(/.{1,1024}/nm) do |s|
+ conn.print(s)
+ end
+ conn.shutdown(Socket::SHUT_WR)
+ conn.read
+ conn.close
+ data_server.close
+ sock.print("226 Transfer complete.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.passive = true
+ ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
+ ftp.connect("127.0.0.1", server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ buf = ftp.getbinaryfile("foo", nil)
+ assert_equal(binary_data, buf)
+ assert_equal(Encoding::ASCII_8BIT, buf.encoding)
+ assert_equal("PASV\r\n", commands.shift)
+ assert_equal("RETR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_use_pasv_ip
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server(nil, "127.0.0.1") { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ data_server = TCPServer.new("127.0.0.1", 0)
+ port = data_server.local_address.ip_port
+ sock.printf("227 Entering Passive Mode (127,0,0,1,%s).\r\n",
+ port.divmod(256).join(","))
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
+ conn = data_server.accept
+ binary_data.scan(/.{1,1024}/nm) do |s|
+ conn.print(s)
+ end
+ conn.shutdown(Socket::SHUT_WR)
+ conn.read
+ conn.close
+ data_server.close
+ sock.print("226 Transfer complete.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.passive = true
+ ftp.use_pasv_ip = true
+ ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
+ ftp.connect("127.0.0.1", server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ buf = ftp.getbinaryfile("foo", nil)
+ assert_equal(binary_data, buf)
+ assert_equal(Encoding::ASCII_8BIT, buf.encoding)
+ assert_equal("PASV\r\n", commands.shift)
+ assert_equal("RETR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_use_pasv_invalid_ip
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server(nil, "127.0.0.1") { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ sock.print("227 Entering Passive Mode (999,0,0,1,48,57).\r\n")
+ commands.push(sock.gets)
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.passive = true
+ ftp.use_pasv_ip = true
+ ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
+ ftp.connect("127.0.0.1", server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_raise(SocketError) do
+ ftp.getbinaryfile("foo", nil)
+ end
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
private
- def create_ftp_server(sleep_time = nil)
- server = TCPServer.new(SERVER_ADDR, 0)
+ def create_ftp_server(sleep_time = nil, addr = SERVER_ADDR)
+ server = TCPServer.new(addr, 0)
@thread = Thread.start do
if sleep_time
sleep(sleep_time)
@@ -1666,4 +2536,89 @@ EOF
end
return server
end
+
+ def tls_test
+ server = TCPServer.new(SERVER_ADDR, 0)
+ port = server.addr[1]
+ commands = []
+ @thread = Thread.start do
+ sock = server.accept
+ begin
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("234 AUTH success.\r\n")
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ca_file = CA_FILE
+ ctx.key = File.open(SERVER_KEY) { |f|
+ OpenSSL::PKey::RSA.new(f)
+ }
+ ctx.cert = File.open(SERVER_CERT) { |f|
+ OpenSSL::X509::Certificate.new(f)
+ }
+ sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ sock.sync_close = true
+ begin
+ sock.accept
+ commands.push(sock.gets)
+ sock.print("200 PSBZ success.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 PROT success.\r\n")
+ rescue OpenSSL::SSL::SSLError, SystemCallError
+ end
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ ftp = yield(port)
+ ftp.close
+
+ assert_equal("AUTH TLS\r\n", commands.shift)
+ assert_equal("PBSZ 0\r\n", commands.shift)
+ assert_equal("PROT P\r\n", commands.shift)
+ end
+
+ def process_port_or_eprt(sock, line)
+ case line
+ when /\APORT (.*)/
+ port_args = $1.split(/,/)
+ host = port_args[0, 4].join(".")
+ port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
+ sock.print("200 PORT command successful.\r\n")
+ return host, port
+ when /\AEPRT \|2\|(.*?)\|(.*?)\|/
+ host = $1
+ port = $2.to_i
+ sock.print("200 EPRT command successful.\r\n")
+ return host, port
+ else
+ flunk "PORT or EPRT expected"
+ end
+ end
+
+ def create_data_connection_server(sock)
+ data_server = TCPServer.new(SERVER_ADDR, 0)
+ port = data_server.local_address.ip_port
+ if data_server.local_address.ipv4?
+ sock.printf("227 Entering Passive Mode (127,0,0,1,%s).\r\n",
+ port.divmod(256).join(","))
+ elsif data_server.local_address.ipv6?
+ sock.printf("229 Entering Extended Passive Mode (|||%d|)\r\n", port)
+ else
+ flunk "Invalid local address"
+ end
+ return data_server
+ end
+
+ def chdir_to_tmpdir
+ Dir.mktmpdir do |dir|
+ pwd = Dir.pwd
+ Dir.chdir(dir)
+ begin
+ yield
+ ensure
+ Dir.chdir(pwd)
+ end
+ end
+ end
end
diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb
index f2c03f2915..389b6fa42e 100644
--- a/test/net/http/test_http.rb
+++ b/test/net/http/test_http.rb
@@ -59,6 +59,33 @@ class TestNetHTTP < Test::Unit::TestCase
end
end
+ def test_addr_port
+ http = Net::HTTP.new 'hostname.example', nil, nil
+ addr_port = http.__send__ :addr_port
+ assert_equal 'hostname.example', addr_port
+
+ http.use_ssl = true
+ addr_port = http.__send__ :addr_port
+ assert_equal 'hostname.example:80', addr_port
+
+ http = Net::HTTP.new '203.0.113.1', nil, nil
+ addr_port = http.__send__ :addr_port
+ assert_equal '203.0.113.1', addr_port
+
+ http.use_ssl = true
+ addr_port = http.__send__ :addr_port
+ assert_equal '203.0.113.1:80', addr_port
+
+ http = Net::HTTP.new '2001:db8::1', nil, nil
+ addr_port = http.__send__ :addr_port
+ assert_equal '[2001:db8::1]', addr_port
+
+ http.use_ssl = true
+ addr_port = http.__send__ :addr_port
+ assert_equal '[2001:db8::1]:80', addr_port
+
+ end
+
def test_edit_path
http = Net::HTTP.new 'hostname.example', nil, nil
@@ -97,6 +124,16 @@ class TestNetHTTP < Test::Unit::TestCase
end
end
+ def test_proxy_address_no_proxy
+ clean_http_proxy_env do
+ http = Net::HTTP.new 'hostname.example', nil, 'proxy.example', nil, nil, nil, 'example'
+ assert_nil http.proxy_address
+
+ http = Net::HTTP.new '10.224.1.1', nil, 'proxy.example', nil, nil, nil, 'example,10.224.0.0/22'
+ assert_nil http.proxy_address
+ end
+ end
+
def test_proxy_from_env_ENV
clean_http_proxy_env do
ENV['http_proxy'] = 'http://proxy.example:8000'
@@ -134,6 +171,23 @@ class TestNetHTTP < Test::Unit::TestCase
end
end
+ def test_proxy_eh_ENV_with_user
+ clean_http_proxy_env do
+ ENV['http_proxy'] = 'http://foo:bar@proxy.example:8000'
+
+ http = Net::HTTP.new 'hostname.example'
+
+ assert_equal true, http.proxy?
+ if Net::HTTP::ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE
+ assert_equal 'foo', http.proxy_user
+ assert_equal 'bar', http.proxy_pass
+ else
+ assert_nil http.proxy_user
+ assert_nil http.proxy_pass
+ end
+ end
+ end
+
def test_proxy_eh_ENV_none_set
clean_http_proxy_env do
assert_equal false, Net::HTTP.new('hostname.example').proxy?
@@ -208,7 +262,7 @@ class TestNetHTTP < Test::Unit::TestCase
def host.to_str; raise SocketError, "open failure"; end
uri = Struct.new(:scheme, :hostname, :port).new("http", host, port)
assert_raise_with_message(SocketError, /#{host}:#{port}/) do
- Net::HTTP.get(uri)
+ clean_http_proxy_env{ Net::HTTP.get(uri) }
end
end
@@ -216,6 +270,35 @@ end
module TestNetHTTP_version_1_1_methods
+ def test_s_start
+ begin
+ h = Net::HTTP.start(config('host'), config('port'))
+ ensure
+ h&.finish
+ end
+ assert_equal config('host'), h.address
+ assert_equal config('port'), h.port
+ assert_equal true, h.instance_variable_get(:@proxy_from_env)
+
+ begin
+ h = Net::HTTP.start(config('host'), config('port'), :ENV)
+ ensure
+ h&.finish
+ end
+ assert_equal config('host'), h.address
+ assert_equal config('port'), h.port
+ assert_equal true, h.instance_variable_get(:@proxy_from_env)
+
+ begin
+ h = Net::HTTP.start(config('host'), config('port'), nil)
+ ensure
+ h&.finish
+ end
+ assert_equal config('host'), h.address
+ assert_equal config('port'), h.port
+ assert_equal false, h.instance_variable_get(:@proxy_from_env)
+ end
+
def test_s_get
assert_equal $test_net_http_data,
Net::HTTP.get(config('host'), '/', config('port'))
@@ -385,12 +468,13 @@ module TestNetHTTP_version_1_1_methods
end
def test_s_post
- url = "http://#{config('host')}:#{config('port')}/"
+ url = "http://#{config('host')}:#{config('port')}/?q=a"
res = Net::HTTP.post(
URI.parse(url),
"a=x")
assert_equal "application/x-www-form-urlencoded", res["Content-Type"]
assert_equal "a=x", res.body
+ assert_equal url, res["X-request-uri"]
res = Net::HTTP.post(
URI.parse(url),
@@ -445,6 +529,28 @@ module TestNetHTTP_version_1_1_methods
assert_equal data, res.entity
end
+ def test_timeout_during_HTTP_session_write
+ th = nil
+ # listen for connections... but deliberately do not read
+ TCPServer.open('localhost', 0) {|server|
+ port = server.addr[1]
+
+ conn = Net::HTTP.new('localhost', port)
+ conn.write_timeout = 0.01
+ conn.read_timeout = 0.01 if windows?
+ conn.open_timeout = 0.1
+
+ th = Thread.new do
+ err = !windows? ? Net::WriteTimeout : Net::ReadTimeout
+ assert_raise(err) { conn.post('/', "a"*50_000_000) }
+ end
+ assert th.join(10)
+ }
+ ensure
+ th&.kill
+ th&.join
+ end
+
def test_timeout_during_HTTP_session
bug4246 = "expected the HTTP session to have timed out but have not. c.f. [ruby-core:34203]"
@@ -998,6 +1104,58 @@ class TestNetHTTPKeepAlive < Test::Unit::TestCase
}
end
+ class MockSocket
+ attr_reader :count
+ def initialize(success_after: nil)
+ @success_after = success_after
+ @count = 0
+ end
+ def close
+ end
+ def closed?
+ end
+ def write(_)
+ end
+ def readline
+ @count += 1
+ if @success_after && @success_after <= @count
+ "HTTP/1.1 200 OK"
+ else
+ raise Errno::ECONNRESET
+ end
+ end
+ def readuntil(*_)
+ ""
+ end
+ def read_all(_)
+ end
+ end
+
+ def test_http_retry_success
+ start {|http|
+ socket = MockSocket.new(success_after: 10)
+ http.instance_variable_get(:@socket).close
+ http.instance_variable_set(:@socket, socket)
+ assert_equal 0, socket.count
+ http.max_retries = 10
+ res = http.get('/')
+ assert_equal 10, socket.count
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ }
+ end
+
+ def test_http_retry_failed
+ start {|http|
+ socket = MockSocket.new
+ http.instance_variable_get(:@socket).close
+ http.instance_variable_set(:@socket, socket)
+ http.max_retries = 10
+ assert_raise(Errno::ECONNRESET){ http.get('/') }
+ assert_equal 11, socket.count
+ }
+ end
+
def test_keep_alive_server_close
def @server.run(sock)
sock.close
diff --git a/test/net/http/test_http_request.rb b/test/net/http/test_http_request.rb
index c2144d86c7..b7515b7e98 100644
--- a/test/net/http/test_http_request.rb
+++ b/test/net/http/test_http_request.rb
@@ -65,6 +65,18 @@ class HTTPRequestTest < Test::Unit::TestCase
'Bug #7381 - do not decode content if the user overrides'
end if Net::HTTP::HAVE_ZLIB
+ def test_initialize_GET_uri
+ req = Net::HTTP::Get.new(URI("http://example.com/foo"))
+ assert_equal "/foo", req.path
+ assert_equal "example.com", req['Host']
+
+ req = Net::HTTP::Get.new(URI("https://example.com/foo"))
+ assert_equal "/foo", req.path
+ assert_equal "example.com", req['Host']
+
+ assert_raise(ArgumentError){ Net::HTTP::Get.new(URI("urn:ietf:rfc:7231")) }
+ end
+
def test_header_set
req = Net::HTTP::Get.new '/'
diff --git a/test/net/http/test_httpheader.rb b/test/net/http/test_httpheader.rb
index 99c47cac93..cb4678a805 100644
--- a/test/net/http/test_httpheader.rb
+++ b/test/net/http/test_httpheader.rb
@@ -16,6 +16,26 @@ class HTTPHeaderTest < Test::Unit::TestCase
@c = C.new
end
+ def test_initialize
+ @c.initialize_http_header("foo"=>"abc")
+ assert_equal "abc", @c["foo"]
+ @c.initialize_http_header("foo"=>"abc", "bar"=>"xyz")
+ assert_equal "xyz", @c["bar"]
+ @c.initialize_http_header([["foo", "abc"]])
+ assert_equal "abc", @c["foo"]
+ @c.initialize_http_header([["foo", "abc"], ["bar","xyz"]])
+ assert_equal "xyz", @c["bar"]
+ assert_raise(NoMethodError){ @c.initialize_http_header("foo"=>[]) }
+ assert_raise(ArgumentError){ @c.initialize_http_header("foo"=>"a\nb") }
+ assert_raise(ArgumentError){ @c.initialize_http_header("foo"=>"a\rb") }
+ assert_raise(ArgumentError){ @c.initialize_http_header("foo"=>"a\xff") }
+ end
+
+ def test_initialize_with_symbol
+ @c.initialize_http_header(foo: "abc")
+ assert_equal "abc", @c["foo"]
+ end
+
def test_size
assert_equal 0, @c.size
@c['a'] = 'a'
@@ -40,6 +60,16 @@ class HTTPHeaderTest < Test::Unit::TestCase
@c['aaA'] = 'aaa'
@c['AAa'] = 'aaa'
assert_equal 2, @c.length
+
+ @c['aaa'] = ['aaa', ['bbb', [3]]]
+ assert_equal 2, @c.length
+ assert_equal ['aaa', 'bbb', '3'], @c.get_fields('aaa')
+
+ @c['aaa'] = "aaa\xff"
+ assert_equal 2, @c.length
+
+ assert_raise(ArgumentError){ @c['foo'] = "a\nb" }
+ assert_raise(ArgumentError){ @c['foo'] = ["a\nb"] }
end
def test_AREF
@@ -65,6 +95,10 @@ class HTTPHeaderTest < Test::Unit::TestCase
@c.add_field 'My-Header', 'd, d'
assert_equal 'a, b, c, d, d', @c['My-Header']
assert_equal ['a', 'b', 'c', 'd, d'], @c.get_fields('My-Header')
+ assert_raise(ArgumentError){ @c.add_field 'My-Header', "d\nd" }
+ @c.add_field 'My-Header', ['e', ["\xff", 7]]
+ assert_equal "a, b, c, d, d, e, \xff, 7", @c['My-Header']
+ assert_equal ['a', 'b', 'c', 'd, d', 'e', "\xff", '7'], @c.get_fields('My-Header')
end
def test_get_fields
diff --git a/test/net/http/test_httpresponse.rb b/test/net/http/test_httpresponse.rb
index a67add7c88..a03bb2e152 100644
--- a/test/net/http/test_httpresponse.rb
+++ b/test/net/http/test_httpresponse.rb
@@ -76,6 +76,32 @@ EOS
assert_equal 'hello', body
end
+ def test_read_body_block_mod
+ IO.pipe do |r, w|
+ buf = 'x' * 1024
+ buf.freeze
+ n = 1024
+ len = n * buf.size
+ th = Thread.new do
+ w.write("HTTP/1.1 200 OK\r\nContent-Length: #{len}\r\n\r\n")
+ n.times { w.write(buf) }
+ :ok
+ end
+ io = Net::BufferedIO.new(r)
+ res = Net::HTTPResponse.read_new(io)
+ nr = 0
+ res.reading_body io, true do
+ # should be allowed to modify the chunk given to them:
+ res.read_body do |chunk|
+ nr += chunk.size
+ chunk.clear
+ end
+ end
+ assert_equal len, nr
+ assert_equal :ok, th.value
+ end
+ end
+
def test_read_body_content_encoding_deflate
io = dummy_io(<<EOS)
HTTP/1.1 200 OK
@@ -396,11 +422,41 @@ EOS
res = Net::HTTPResponse.read_new(io)
assert_equal(nil, res.message)
- assert_raise Net::HTTPServerException do
+ assert_raise Net::HTTPClientException do
res.error!
end
end
+ def test_read_code_type
+ res = Net::HTTPUnknownResponse.new('1.0', '???', 'test response')
+ assert_equal Net::HTTPUnknownResponse, res.code_type
+
+ res = Net::HTTPInformation.new('1.0', '1xx', 'test response')
+ assert_equal Net::HTTPInformation, res.code_type
+
+ res = Net::HTTPSuccess.new('1.0', '2xx', 'test response')
+ assert_equal Net::HTTPSuccess, res.code_type
+
+ res = Net::HTTPRedirection.new('1.0', '3xx', 'test response')
+ assert_equal Net::HTTPRedirection, res.code_type
+
+ res = Net::HTTPClientError.new('1.0', '4xx', 'test response')
+ assert_equal Net::HTTPClientError, res.code_type
+
+ res = Net::HTTPServerError.new('1.0', '5xx', 'test response')
+ assert_equal Net::HTTPServerError, res.code_type
+ end
+
+ def test_inspect_response
+ res = Net::HTTPUnknownResponse.new('1.0', '???', 'test response')
+ assert_equal '#<Net::HTTPUnknownResponse ??? test response readbody=false>', res.inspect
+
+ res = Net::HTTPUnknownResponse.new('1.0', '???', 'test response')
+ socket = Net::BufferedIO.new(StringIO.new('test body'))
+ res.reading_body(socket, true) {}
+ assert_equal '#<Net::HTTPUnknownResponse ??? test response readbody=true>', res.inspect
+ end
+
private
def dummy_io(str)
diff --git a/test/net/http/test_https.rb b/test/net/http/test_https.rb
index dfdb221d7e..784f002c22 100644
--- a/test/net/http/test_https.rb
+++ b/test/net/http/test_https.rb
@@ -4,7 +4,6 @@ begin
require 'net/https'
require 'stringio'
require 'timeout'
- require File.expand_path("../../openssl/utils", File.dirname(__FILE__))
require File.expand_path("utils", File.dirname(__FILE__))
rescue LoadError
# should skip this test
@@ -13,34 +12,42 @@ end
class TestNetHTTPS < Test::Unit::TestCase
include TestNetHTTPUtils
- subject = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
- exts = [
- ["keyUsage", "keyEncipherment,digitalSignature", true],
- ]
- key = OpenSSL::TestUtils::TEST_KEY_RSA1024
- cert = OpenSSL::TestUtils.issue_cert(
- subject, key, 1, Time.now, Time.now + 3600, exts,
- nil, nil, OpenSSL::Digest::SHA1.new
- )
+ def self.fixture(key)
+ File.read(File.expand_path("../fixtures/#{key}", __dir__))
+ end
+
+ CA_CERT = OpenSSL::X509::Certificate.new(fixture("cacert.pem"))
+ SERVER_KEY = OpenSSL::PKey.read(fixture("server.key"))
+ SERVER_CERT = OpenSSL::X509::Certificate.new(fixture("server.crt"))
+ DHPARAMS = OpenSSL::PKey::DH.new(fixture("dhparams.pem"))
+ TEST_STORE = OpenSSL::X509::Store.new.tap {|s| s.add_cert(CA_CERT) }
CONFIG = {
'host' => '127.0.0.1',
'proxy_host' => nil,
'proxy_port' => nil,
'ssl_enable' => true,
- 'ssl_certificate' => cert,
- 'ssl_private_key' => key,
+ 'ssl_certificate' => SERVER_CERT,
+ 'ssl_private_key' => SERVER_KEY,
+ 'ssl_tmp_dh_callback' => proc { DHPARAMS },
}
def test_get
http = Net::HTTP.new("localhost", config("port"))
http.use_ssl = true
+ http.cert_store = TEST_STORE
+ certs = []
http.verify_callback = Proc.new do |preverify_ok, store_ctx|
- store_ctx.current_cert.to_der == config('ssl_certificate').to_der
+ certs << store_ctx.current_cert
+ preverify_ok
end
http.request_get("/") {|res|
assert_equal($test_net_http_data, res.body)
}
+ # TODO: OpenSSL 1.1.1h seems to yield only SERVER_CERT; need to check the incompatibility
+ certs.zip([CA_CERT, SERVER_CERT]) do |actual, expected|
+ assert_equal(expected.to_der, actual.to_der)
+ end
rescue SystemCallError
skip $!
end
@@ -48,9 +55,7 @@ class TestNetHTTPS < Test::Unit::TestCase
def test_post
http = Net::HTTP.new("localhost", config("port"))
http.use_ssl = true
- http.verify_callback = Proc.new do |preverify_ok, store_ctx|
- store_ctx.current_cert.to_der == config('ssl_certificate').to_der
- end
+ http.cert_store = TEST_STORE
data = config('ssl_private_key').to_der
http.request_post("/", data, {'content-type' => 'application/x-www-form-urlencoded'}) {|res|
assert_equal(data, res.body)
@@ -60,11 +65,13 @@ class TestNetHTTPS < Test::Unit::TestCase
end
def test_session_reuse
+ # FIXME: The new_session_cb is known broken for clients in OpenSSL 1.1.0h.
+ # See https://github.com/openssl/openssl/pull/5967 for details.
+ skip if OpenSSL::OPENSSL_LIBRARY_VERSION =~ /OpenSSL 1.1.0h/
+
http = Net::HTTP.new("localhost", config("port"))
http.use_ssl = true
- http.verify_callback = Proc.new do |preverify_ok, store_ctx|
- store_ctx.current_cert.to_der == config('ssl_certificate').to_der
- end
+ http.cert_store = TEST_STORE
http.start
http.get("/")
@@ -72,18 +79,9 @@ class TestNetHTTPS < Test::Unit::TestCase
http.start
http.get("/")
- http.finish # three times due to possible bug in OpenSSL 0.9.8
-
- sid = http.instance_variable_get(:@ssl_session).id
-
- http.start
- http.get("/")
socket = http.instance_variable_get(:@socket).io
-
- assert socket.session_reused?
-
- assert_equal sid, http.instance_variable_get(:@ssl_session).id
+ assert_equal true, socket.session_reused?
http.finish
rescue SystemCallError
@@ -91,27 +89,24 @@ class TestNetHTTPS < Test::Unit::TestCase
end
def test_session_reuse_but_expire
+ # FIXME: The new_session_cb is known broken for clients in OpenSSL 1.1.0h.
+ skip if OpenSSL::OPENSSL_LIBRARY_VERSION =~ /OpenSSL 1.1.0h/
+
http = Net::HTTP.new("localhost", config("port"))
http.use_ssl = true
- http.verify_callback = Proc.new do |preverify_ok, store_ctx|
- store_ctx.current_cert.to_der == config('ssl_certificate').to_der
- end
+ http.cert_store = TEST_STORE
http.ssl_timeout = -1
http.start
http.get("/")
http.finish
- sid = http.instance_variable_get(:@ssl_session).id
-
http.start
http.get("/")
socket = http.instance_variable_get(:@socket).io
assert_equal false, socket.session_reused?
- assert_not_equal sid, http.instance_variable_get(:@ssl_session).id
-
http.finish
rescue SystemCallError
skip $!
@@ -161,15 +156,16 @@ class TestNetHTTPS < Test::Unit::TestCase
end
def test_identity_verify_failure
+ # the certificate's subject has CN=localhost
http = Net::HTTP.new("127.0.0.1", config("port"))
http.use_ssl = true
- http.verify_callback = Proc.new do |preverify_ok, store_ctx|
- store_ctx.current_cert.to_der == config('ssl_certificate').to_der
- end
+ http.cert_store = TEST_STORE
+ @log_tester = lambda {|_| }
ex = assert_raise(OpenSSL::SSL::SSLError){
http.request_get("/") {|res| }
}
- assert_match(/hostname \"127.0.0.1\" does not match/, ex.message)
+ re_msg = /certificate verify failed|hostname \"127.0.0.1\" does not match/
+ assert_match(re_msg, ex.message)
end
def test_timeout_during_SSL_handshake
@@ -192,4 +188,30 @@ class TestNetHTTPS < Test::Unit::TestCase
assert th.join(10), bug4246
}
end
-end if defined?(OpenSSL::TestUtils)
+
+ def test_min_version
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ http.min_version = :TLS1
+ http.cert_store = TEST_STORE
+ http.request_get("/") {|res|
+ assert_equal($test_net_http_data, res.body)
+ }
+ end
+
+ def test_max_version
+ http = Net::HTTP.new("127.0.0.1", config("port"))
+ http.use_ssl = true
+ http.max_version = :SSL2
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ true
+ end
+ @log_tester = lambda {|_| }
+ ex = assert_raise(OpenSSL::SSL::SSLError){
+ http.request_get("/") {|res| }
+ }
+ re_msg = /\ASSL_connect returned=1 errno=0 |SSL_CTX_set_max_proto_version/
+ assert_match(re_msg, ex.message)
+ end
+
+end if defined?(OpenSSL::SSL)
diff --git a/test/net/http/utils.rb b/test/net/http/utils.rb
index 8a00615fbe..dbfd112f31 100644
--- a/test/net/http/utils.rb
+++ b/test/net/http/utils.rb
@@ -61,7 +61,7 @@ module TestNetHTTPUtils
:SSLEnable => true,
:SSLCertificate => config('ssl_certificate'),
:SSLPrivateKey => config('ssl_private_key'),
- :SSLTmpDhCallback => proc { OpenSSL::TestUtils::TEST_KEY_DH1024 },
+ :SSLTmpDhCallback => config('ssl_tmp_dh_callback'),
})
end
@server = WEBrick::HTTPServer.new(server_config)
diff --git a/test/net/imap/cacert.pem b/test/net/imap/cacert.pem
deleted file mode 100644
index 7073387877..0000000000
--- a/test/net/imap/cacert.pem
+++ /dev/null
@@ -1,66 +0,0 @@
-Certificate:
- Data:
- Version: 3 (0x2)
- Serial Number:
- b9:90:a2:bf:62:69:17:9c
- Signature Algorithm: sha1WithRSAEncryption
- Issuer: C=JP, ST=Shimane, L=Matz-e city, O=Ruby Core Team, CN=Ruby Test CA/emailAddress=security@ruby-lang.org
- Validity
- Not Before: Jan 3 01:34:17 2014 GMT
- Not After : Jan 2 01:34:17 2019 GMT
- Subject: C=JP, ST=Shimane, L=Matz-e city, O=Ruby Core Team, CN=Ruby Test CA/emailAddress=security@ruby-lang.org
- Subject Public Key Info:
- Public Key Algorithm: rsaEncryption
- RSA Public Key: (1024 bit)
- Modulus (1024 bit):
- 00:db:75:d0:45:de:b1:df:bf:71:a0:0e:b0:a5:e6:
- bc:f4:1c:9d:e5:25:67:64:c5:7b:cb:f1:af:c6:be:
- 9a:aa:ea:7e:0f:cc:05:af:ef:40:69:06:b2:c9:13:
- 9d:7e:eb:a2:06:e2:ea:7d:07:c7:c7:99:c7:fb:d5:
- b8:eb:63:77:62:2b:18:12:c3:53:58:d0:f5:c7:40:
- 0c:01:d1:26:82:34:16:09:e3:dc:65:f4:dc:bb:5d:
- a5:41:60:e7:a9:74:ba:d7:4c:b6:a3:9c:c5:8c:89:
- af:cb:e8:9f:05:fe:ea:fe:64:24:bf:e7:ed:e3:f6:
- d0:fc:d6:eb:fc:06:82:10:fb
- Exponent: 65537 (0x10001)
- X509v3 extensions:
- X509v3 Subject Key Identifier:
- E8:7E:58:AC:13:7B:03:22:8D:9E:AF:32:0B:84:89:80:80:0C:1E:C2
- X509v3 Authority Key Identifier:
- keyid:E8:7E:58:AC:13:7B:03:22:8D:9E:AF:32:0B:84:89:80:80:0C:1E:C2
- DirName:/C=JP/ST=Shimane/L=Matz-e city/O=Ruby Core Team/CN=Ruby Test CA/emailAddress=security@ruby-lang.org
- serial:B9:90:A2:BF:62:69:17:9C
-
- X509v3 Basic Constraints:
- CA:TRUE
- Signature Algorithm: sha1WithRSAEncryption
- 8f:77:06:4e:31:72:12:ee:68:09:70:27:d4:31:85:ef:10:95:
- f9:0f:2b:66:63:08:37:88:6e:b7:9b:40:3e:18:77:33:86:e8:
- 61:6a:b7:3c:cb:c7:a6:d6:d5:92:6a:1f:56:d0:9f:5c:32:56:
- d3:37:52:fe:0e:20:c2:7a:0d:fe:2d:3c:81:da:b8:7f:4d:6a:
- 08:01:d9:be:7a:a2:15:be:a6:ce:49:64:90:8c:9a:ca:6e:2e:
- 84:48:1d:94:19:56:94:46:aa:25:9b:68:c2:80:60:bf:cb:2e:
- 35:03:ea:0a:65:5a:33:38:c6:cc:81:46:c0:bc:36:86:96:39:
- 10:7d
------BEGIN CERTIFICATE-----
-MIIDjTCCAvagAwIBAgIJALmQor9iaRecMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYD
-VQQGEwJKUDEQMA4GA1UECBMHU2hpbWFuZTEUMBIGA1UEBxMLTWF0ei1lIGNpdHkx
-FzAVBgNVBAoTDlJ1YnkgQ29yZSBUZWFtMRUwEwYDVQQDEwxSdWJ5IFRlc3QgQ0Ex
-JTAjBgkqhkiG9w0BCQEWFnNlY3VyaXR5QHJ1YnktbGFuZy5vcmcwHhcNMTQwMTAz
-MDEzNDE3WhcNMTkwMTAyMDEzNDE3WjCBjDELMAkGA1UEBhMCSlAxEDAOBgNVBAgT
-B1NoaW1hbmUxFDASBgNVBAcTC01hdHotZSBjaXR5MRcwFQYDVQQKEw5SdWJ5IENv
-cmUgVGVhbTEVMBMGA1UEAxMMUnVieSBUZXN0IENBMSUwIwYJKoZIhvcNAQkBFhZz
-ZWN1cml0eUBydWJ5LWxhbmcub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
-gQDbddBF3rHfv3GgDrCl5rz0HJ3lJWdkxXvL8a/Gvpqq6n4PzAWv70BpBrLJE51+
-66IG4up9B8fHmcf71bjrY3diKxgSw1NY0PXHQAwB0SaCNBYJ49xl9Ny7XaVBYOep
-dLrXTLajnMWMia/L6J8F/ur+ZCS/5+3j9tD81uv8BoIQ+wIDAQABo4H0MIHxMB0G
-A1UdDgQWBBToflisE3sDIo2erzILhImAgAwewjCBwQYDVR0jBIG5MIG2gBToflis
-E3sDIo2erzILhImAgAwewqGBkqSBjzCBjDELMAkGA1UEBhMCSlAxEDAOBgNVBAgT
-B1NoaW1hbmUxFDASBgNVBAcTC01hdHotZSBjaXR5MRcwFQYDVQQKEw5SdWJ5IENv
-cmUgVGVhbTEVMBMGA1UEAxMMUnVieSBUZXN0IENBMSUwIwYJKoZIhvcNAQkBFhZz
-ZWN1cml0eUBydWJ5LWxhbmcub3JnggkAuZCiv2JpF5wwDAYDVR0TBAUwAwEB/zAN
-BgkqhkiG9w0BAQUFAAOBgQCPdwZOMXIS7mgJcCfUMYXvEJX5DytmYwg3iG63m0A+
-GHczhuhharc8y8em1tWSah9W0J9cMlbTN1L+DiDCeg3+LTyB2rh/TWoIAdm+eqIV
-vqbOSWSQjJrKbi6ESB2UGVaURqolm2jCgGC/yy41A+oKZVozOMbMgUbAvDaGljkQ
-fQ==
------END CERTIFICATE-----
diff --git a/test/net/imap/server.crt b/test/net/imap/server.crt
deleted file mode 100644
index fa4f99493a..0000000000
--- a/test/net/imap/server.crt
+++ /dev/null
@@ -1,48 +0,0 @@
-Certificate:
- Data:
- Version: 1 (0x0)
- Serial Number: 0 (0x0)
- Signature Algorithm: sha1WithRSAEncryption
- Issuer: C=JP, ST=Shimane, L=Matz-e city, O=Ruby Core Team, CN=Ruby Test CA/emailAddress=security@ruby-lang.org
- Validity
- Not Before: Jan 3 01:34:17 2014 GMT
- Not After : Jan 2 01:34:17 2019 GMT
- Subject: C=JP, ST=Shimane, O=Ruby Core Team, OU=Ruby Test, CN=localhost
- Subject Public Key Info:
- Public Key Algorithm: rsaEncryption
- RSA Public Key: (1024 bit)
- Modulus (1024 bit):
- 00:db:75:d0:45:de:b1:df:bf:71:a0:0e:b0:a5:e6:
- bc:f4:1c:9d:e5:25:67:64:c5:7b:cb:f1:af:c6:be:
- 9a:aa:ea:7e:0f:cc:05:af:ef:40:69:06:b2:c9:13:
- 9d:7e:eb:a2:06:e2:ea:7d:07:c7:c7:99:c7:fb:d5:
- b8:eb:63:77:62:2b:18:12:c3:53:58:d0:f5:c7:40:
- 0c:01:d1:26:82:34:16:09:e3:dc:65:f4:dc:bb:5d:
- a5:41:60:e7:a9:74:ba:d7:4c:b6:a3:9c:c5:8c:89:
- af:cb:e8:9f:05:fe:ea:fe:64:24:bf:e7:ed:e3:f6:
- d0:fc:d6:eb:fc:06:82:10:fb
- Exponent: 65537 (0x10001)
- Signature Algorithm: sha1WithRSAEncryption
- 85:f5:d3:05:8b:8c:f4:43:1c:88:f2:8f:b2:f2:93:77:b7:3d:
- 95:c6:a0:34:bc:33:6a:d8:85:5f:3e:86:08:10:c5:5c:c1:76:
- a3:53:3c:dc:38:98:23:97:e7:da:21:ac:e8:4d:3c:96:70:29:
- ff:ff:1e:4a:9a:17:2b:db:04:62:b9:ef:ab:ea:a7:a5:e8:7c:
- b1:d5:ed:30:a8:6c:78:de:51:7e:e3:8a:c2:a4:64:a8:63:a2:
- bc:fd:43:9c:f3:55:7d:54:c9:6a:d8:53:1c:4b:6b:03:aa:b6:
- 19:e6:a4:4f:47:00:96:c5:42:59:85:4e:c3:4e:cd:41:82:53:
- 10:f8
------BEGIN CERTIFICATE-----
-MIICXDCCAcUCAQAwDQYJKoZIhvcNAQEFBQAwgYwxCzAJBgNVBAYTAkpQMRAwDgYD
-VQQIEwdTaGltYW5lMRQwEgYDVQQHEwtNYXR6LWUgY2l0eTEXMBUGA1UEChMOUnVi
-eSBDb3JlIFRlYW0xFTATBgNVBAMTDFJ1YnkgVGVzdCBDQTElMCMGCSqGSIb3DQEJ
-ARYWc2VjdXJpdHlAcnVieS1sYW5nLm9yZzAeFw0xNDAxMDMwMTM0MTdaFw0xOTAx
-MDIwMTM0MTdaMGAxCzAJBgNVBAYTAkpQMRAwDgYDVQQIEwdTaGltYW5lMRcwFQYD
-VQQKEw5SdWJ5IENvcmUgVGVhbTESMBAGA1UECxMJUnVieSBUZXN0MRIwEAYDVQQD
-Ewlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANt10EXesd+/
-caAOsKXmvPQcneUlZ2TFe8vxr8a+mqrqfg/MBa/vQGkGsskTnX7rogbi6n0Hx8eZ
-x/vVuOtjd2IrGBLDU1jQ9cdADAHRJoI0Fgnj3GX03LtdpUFg56l0utdMtqOcxYyJ
-r8vonwX+6v5kJL/n7eP20PzW6/wGghD7AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEA
-hfXTBYuM9EMciPKPsvKTd7c9lcagNLwzatiFXz6GCBDFXMF2o1M83DiYI5fn2iGs
-6E08lnAp//8eSpoXK9sEYrnvq+qnpeh8sdXtMKhseN5RfuOKwqRkqGOivP1DnPNV
-fVTJathTHEtrA6q2GeakT0cAlsVCWYVOw07NQYJTEPg=
------END CERTIFICATE-----
diff --git a/test/net/imap/server.key b/test/net/imap/server.key
deleted file mode 100644
index 7c57546ece..0000000000
--- a/test/net/imap/server.key
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXQIBAAKBgQDbddBF3rHfv3GgDrCl5rz0HJ3lJWdkxXvL8a/Gvpqq6n4PzAWv
-70BpBrLJE51+66IG4up9B8fHmcf71bjrY3diKxgSw1NY0PXHQAwB0SaCNBYJ49xl
-9Ny7XaVBYOepdLrXTLajnMWMia/L6J8F/ur+ZCS/5+3j9tD81uv8BoIQ+wIDAQAB
-AoGAGtYHR+P5gFDaxiXFuCPFC1zMeg7e29XCU6gURIteQnQ2QhxCvcbV64HkLu51
-HeYWhB0Pa4aeCWxmpgb2e+JH4MEoIjeJSGyZQeqwkQLgWJDdvkgWx5am58QzA60I
-ipkZ9QHcPffSs5RiGx4yfr58KqAmwFphGCY8W7v4LqaENdECQQD9H5VTW9g4gj1c
-j3uNYvSI/D7a9P7gfI+ziczuwMm5xsBx3D/t5TAr3SJKNne3sl1E6ZERCUbzxf+C
-k58EiHx1AkEA3fRLGqDOq7EcQhbjTcA/v/t5MwlGEUsS9+XrqOWn50YuoIwRZJ3v
-qHRQzfQfFNklGtfBvwQ4md3irXjMeGVprwJBAMEAuwiDiHuV+xm/ofKtmE13IKot
-ksYy1BOOp/8IawhHXueyi+BmF/PqOkIiA+jCjNGF0oIN89beizPSQbbgJx0CQG/K
-qL1bu1ys0y/SeWBi8XkP/0aeaCUzq/UiYCTsrzoEll2UzvnftqMhGsXxLGqCyHaR
-r2s3hA6zvIVlL4+AfM8CQQClq+WDrC5VKciLYakZNWJjV1m+H2Ut/0fXdUjKHajE
-FWLcsrOhADf6bkTb71GwPxnKRkkRmud5upP0ZYYTqM4X
------END RSA PRIVATE KEY-----
diff --git a/test/net/imap/test_imap.rb b/test/net/imap/test_imap.rb
index cdd9323c10..81928cb8fe 100644
--- a/test/net/imap/test_imap.rb
+++ b/test/net/imap/test_imap.rb
@@ -4,11 +4,9 @@ require "net/imap"
require "test/unit"
class IMAPTest < Test::Unit::TestCase
- CA_FILE = File.expand_path("cacert.pem", File.dirname(__FILE__))
- SERVER_KEY = File.expand_path("server.key", File.dirname(__FILE__))
- SERVER_CERT = File.expand_path("server.crt", File.dirname(__FILE__))
-
- SERVER_ADDR = "127.0.0.1"
+ CA_FILE = File.expand_path("../fixtures/cacert.pem", __dir__)
+ SERVER_KEY = File.expand_path("../fixtures/server.key", __dir__)
+ SERVER_CERT = File.expand_path("../fixtures/server.crt", __dir__)
def setup
@do_not_reverse_lookup = Socket.do_not_reverse_lookup
@@ -94,7 +92,7 @@ class IMAPTest < Test::Unit::TestCase
def test_imaps_verify_none
assert_nothing_raised do
imaps_test do |port|
- Net::IMAP.new(SERVER_ADDR,
+ Net::IMAP.new(server_addr,
:port => port,
:ssl => { :verify_mode => OpenSSL::SSL::VERIFY_NONE })
end
@@ -104,9 +102,9 @@ class IMAPTest < Test::Unit::TestCase
def test_imaps_post_connection_check
assert_raise(OpenSSL::SSL::SSLError) do
imaps_test do |port|
- # SERVER_ADDR is different from the hostname in the certificate,
+ # server_addr is different from the hostname in the certificate,
# so the following code should raise a SSLError.
- Net::IMAP.new(SERVER_ADDR,
+ Net::IMAP.new(server_addr,
:port => port,
:ssl => { :ca_file => CA_FILE })
end
@@ -129,6 +127,24 @@ class IMAPTest < Test::Unit::TestCase
imap.disconnect
end
end
+
+ def test_starttls_stripping
+ starttls_stripping_test do |port|
+ imap = Net::IMAP.new("localhost", :port => port)
+ assert_raise(Net::IMAP::UnknownResponseError) do
+ imap.starttls(:ca_file => CA_FILE)
+ end
+ imap
+ end
+ end
+ end
+
+ def start_server
+ th = Thread.new do
+ yield
+ end
+ @threads << th
+ sleep 0.1 until th.stop?
end
def test_unexpected_eof
@@ -147,7 +163,7 @@ class IMAPTest < Test::Unit::TestCase
end
end
begin
- imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ imap = Net::IMAP.new(server_addr, :port => port)
assert_raise(EOFError) do
imap.logout
end
@@ -180,7 +196,7 @@ class IMAPTest < Test::Unit::TestCase
end
begin
- imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ imap = Net::IMAP.new(server_addr, :port => port)
responses = []
imap.idle do |res|
responses.push(res)
@@ -226,22 +242,26 @@ class IMAPTest < Test::Unit::TestCase
end
end
begin
- imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ imap = Net::IMAP.new(server_addr, :port => port)
begin
th = Thread.current
m = Monitor.new
in_idle = false
exception_raised = false
c = m.new_cond
- @threads << Thread.start do
+ raiser = Thread.start do
m.synchronize do
until in_idle
c.wait(0.1)
end
end
th.raise(Interrupt)
- exception_raised = true
+ m.synchronize do
+ exception_raised = true
+ c.signal
+ end
end
+ @threads << raiser
imap.idle do |res|
m.synchronize do
in_idle = true
@@ -259,6 +279,7 @@ class IMAPTest < Test::Unit::TestCase
imap.logout
ensure
imap.disconnect if imap
+ raiser.kill unless in_idle
end
end
@@ -275,7 +296,7 @@ class IMAPTest < Test::Unit::TestCase
end
end
begin
- imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ imap = Net::IMAP.new(server_addr, :port => port)
assert_raise(Net::IMAP::Error) do
imap.idle_done
end
@@ -308,13 +329,13 @@ class IMAPTest < Test::Unit::TestCase
end
begin
- imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ imap = Net::IMAP.new(server_addr, :port => port)
responses = []
Thread.pass
imap.idle(0.2) do |res|
responses.push(res)
end
- # There is no gurantee that this thread has received all the responses,
+ # There is no guarantee that this thread has received all the responses,
# so check the response length.
if responses.length > 0
assert_instance_of(Net::IMAP::ContinuationRequest, responses[0])
@@ -327,7 +348,7 @@ class IMAPTest < Test::Unit::TestCase
end
end
end
- # Also, there is no gurantee that the server thread has stored
+ # Also, there is no guarantee that the server thread has stored
# all the requests into the array, so check the length.
if requests.length > 0
assert_equal("RUBY0001 IDLE\r\n", requests[0])
@@ -356,7 +377,7 @@ class IMAPTest < Test::Unit::TestCase
end
end
begin
- imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ imap = Net::IMAP.new(server_addr, :port => port)
assert_raise(Net::IMAP::ByeResponseError) do
imap.login("user", "password")
end
@@ -379,7 +400,7 @@ class IMAPTest < Test::Unit::TestCase
end
end
begin
- imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ imap = Net::IMAP.new(server_addr, :port => port)
imap.instance_eval do
def @sock.shutdown(*args)
super
@@ -413,11 +434,11 @@ class IMAPTest < Test::Unit::TestCase
end
end
threads << Thread.start do
- imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ imap = Net::IMAP.new(server_addr, :port => port)
begin
m = Monitor.new
in_idle = false
- exception_raised = false
+ closed = false
c = m.new_cond
threads << Thread.start do
m.synchronize do
@@ -426,14 +447,17 @@ class IMAPTest < Test::Unit::TestCase
end
end
sock.close
- exception_raised = true
+ m.synchronize do
+ closed = true
+ c.signal
+ end
end
- assert_raise(Net::IMAP::Error) do
+ assert_raise(EOFError) do
imap.idle do |res|
m.synchronize do
in_idle = true
c.signal
- until exception_raised
+ until closed
c.wait(0.1)
end
end
@@ -464,7 +488,7 @@ class IMAPTest < Test::Unit::TestCase
end
end
assert_raise(Net::IMAP::Error) do
- Net::IMAP.new(SERVER_ADDR, :port => port)
+ Net::IMAP.new(server_addr, :port => port)
end
end
@@ -500,7 +524,7 @@ class IMAPTest < Test::Unit::TestCase
end
end
begin
- imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ imap = Net::IMAP.new(server_addr, :port => port)
assert_raise(Net::IMAP::DataFormatError) do
imap.send(:send_command, "TEST", -1)
end
@@ -526,6 +550,158 @@ class IMAPTest < Test::Unit::TestCase
end
end
+ def test_send_literal
+ server = create_tcp_server
+ port = server.addr[1]
+ requests = []
+ literal = nil
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ line = sock.gets
+ requests.push(line)
+ size = line.slice(/{(\d+)}\r\n/, 1).to_i
+ sock.print("+ Ready for literal data\r\n")
+ literal = sock.read(size)
+ requests.push(sock.gets)
+ sock.print("RUBY0001 OK TEST completed\r\n")
+ sock.gets
+ sock.print("* BYE terminating connection\r\n")
+ sock.print("RUBY0002 OK LOGOUT completed\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = Net::IMAP.new(server_addr, :port => port)
+ imap.send(:send_command, "TEST", ["\xDE\xAD\xBE\xEF".b])
+ assert_equal(2, requests.length)
+ assert_equal("RUBY0001 TEST ({4}\r\n", requests[0])
+ assert_equal("\xDE\xAD\xBE\xEF".b, literal)
+ assert_equal(")\r\n", requests[1])
+ imap.logout
+ ensure
+ imap.disconnect
+ end
+ end
+
+ def test_disconnect
+ server = create_tcp_server
+ port = server.addr[1]
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ sock.gets
+ sock.print("* BYE terminating connection\r\n")
+ sock.print("RUBY0001 OK LOGOUT completed\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = Net::IMAP.new(server_addr, :port => port)
+ imap.logout
+ imap.disconnect
+ assert_equal(true, imap.disconnected?)
+ imap.disconnect
+ assert_equal(true, imap.disconnected?)
+ ensure
+ imap.disconnect if imap && !imap.disconnected?
+ end
+ end
+
+ def test_append
+ server = create_tcp_server
+ port = server.addr[1]
+ mail = <<EOF.gsub(/\n/, "\r\n")
+From: shugo@example.com
+To: matz@example.com
+Subject: hello
+
+hello world
+EOF
+ requests = []
+ received_mail = nil
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ line = sock.gets
+ requests.push(line)
+ size = line.slice(/{(\d+)}\r\n/, 1).to_i
+ sock.print("+ Ready for literal data\r\n")
+ received_mail = sock.read(size)
+ sock.gets
+ sock.print("RUBY0001 OK APPEND completed\r\n")
+ requests.push(sock.gets)
+ sock.print("* BYE terminating connection\r\n")
+ sock.print("RUBY0002 OK LOGOUT completed\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+
+ begin
+ imap = Net::IMAP.new(server_addr, :port => port)
+ resp = imap.append("INBOX", mail)
+ assert_equal(1, requests.length)
+ assert_equal("RUBY0001 APPEND INBOX {#{mail.size}}\r\n", requests[0])
+ assert_equal(mail, received_mail)
+ imap.logout
+ assert_equal(2, requests.length)
+ assert_equal("RUBY0002 LOGOUT\r\n", requests[1])
+ ensure
+ imap.disconnect if imap
+ end
+ end
+
+ def test_append_fail
+ server = create_tcp_server
+ port = server.addr[1]
+ mail = <<EOF.gsub(/\n/, "\r\n")
+From: shugo@example.com
+To: matz@example.com
+Subject: hello
+
+hello world
+EOF
+ requests = []
+ received_mail = nil
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ requests.push(sock.gets)
+ sock.print("RUBY0001 NO Mailbox doesn't exist\r\n")
+ requests.push(sock.gets)
+ sock.print("* BYE terminating connection\r\n")
+ sock.print("RUBY0002 OK LOGOUT completed\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+
+ begin
+ imap = Net::IMAP.new(server_addr, :port => port)
+ assert_raise(Net::IMAP::NoResponseError) do
+ imap.append("INBOX", mail)
+ end
+ assert_equal(1, requests.length)
+ assert_equal("RUBY0001 APPEND INBOX {#{mail.size}}\r\n", requests[0])
+ imap.logout
+ assert_equal(2, requests.length)
+ assert_equal("RUBY0002 LOGOUT\r\n", requests[1])
+ ensure
+ imap.disconnect if imap
+ end
+ end
+
private
def imaps_test
@@ -541,6 +717,7 @@ class IMAPTest < Test::Unit::TestCase
}
ssl_server = OpenSSL::SSL::SSLServer.new(server, ctx)
ths = Thread.start do
+ Thread.current.report_on_exception = false # always join-ed
begin
sock = ssl_server.accept
begin
@@ -603,7 +780,32 @@ class IMAPTest < Test::Unit::TestCase
end
end
+ def starttls_stripping_test
+ server = create_tcp_server
+ port = server.addr[1]
+ start_server do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ sock.gets
+ sock.print("RUBY0001 BUG unhandled command\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = yield(port)
+ ensure
+ imap.disconnect if imap && !imap.disconnected?
+ end
+ end
+
def create_tcp_server
- return TCPServer.new(SERVER_ADDR, 0)
+ return TCPServer.new(server_addr, 0)
+ end
+
+ def server_addr
+ Addrinfo.tcp("localhost", 0).ip_address
end
end
diff --git a/test/net/imap/test_imap_response_parser.rb b/test/net/imap/test_imap_response_parser.rb
index db07f9b022..ed31a03f5a 100644
--- a/test/net/imap/test_imap_response_parser.rb
+++ b/test/net/imap/test_imap_response_parser.rb
@@ -29,6 +29,8 @@ class IMAPResponseParserTest < Test::Unit::TestCase
EOF
}.call
assert_equal [:Haschildren], response.data.attr
+ ensure
+ $SAFE = 0
end
def test_flag_list_too_many_flags
@@ -60,7 +62,7 @@ EOF
def test_flag_xlist_inbox
parser = Net::IMAP::ResponseParser.new
- response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
* XLIST (\\Inbox) "." "INBOX"
EOF
assert_equal [:Inbox], response.data.attr
@@ -247,7 +249,7 @@ EOF
assert_equal("AUTH=PLAIN", response.data.last)
end
- def test_mixed_boundry
+ def test_mixed_boundary
parser = Net::IMAP::ResponseParser.new
response = parser.parse("* 2688 FETCH (UID 179161 BODYSTRUCTURE (" \
"(\"TEXT\" \"PLAIN\" (\"CHARSET\" \"iso-8859-1\") NIL NIL \"QUOTED-PRINTABLE\" 200 4 NIL NIL NIL)" \
@@ -291,4 +293,32 @@ EOF
assert_equal("test.xml", body.parts[1].disposition.param["FILENAME"])
assert_equal(nil, body.parts[1].language)
end
+
+ # [Bug #13649]
+ def test_status
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse("* STATUS INBOX (UIDNEXT 1 UIDVALIDITY 1234)\r\n")
+ assert_equal("STATUS", response.name)
+ assert_equal("INBOX", response.data.mailbox)
+ assert_equal(1234, response.data.attr["UIDVALIDITY"])
+ response = parser.parse("* STATUS INBOX (UIDNEXT 1 UIDVALIDITY 1234) \r\n")
+ assert_equal("STATUS", response.name)
+ assert_equal("INBOX", response.data.mailbox)
+ assert_equal(1234, response.data.attr["UIDVALIDITY"])
+ end
+
+ # [Bug #10119]
+ def test_msg_att_modseq_data
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse("* 1 FETCH (FLAGS (\Seen) MODSEQ (12345) UID 5)\r\n")
+ assert_equal(12345, response.data.attr["MODSEQ"])
+ end
+
+ def test_continuation_request_without_response_text
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse("+\r\n")
+ assert_instance_of(Net::IMAP::ContinuationRequest, response)
+ assert_equal(nil, response.data.code)
+ assert_equal("", response.data.text)
+ end
end
diff --git a/test/net/pop/test_pop.rb b/test/net/pop/test_pop.rb
index f06ccb4452..f4c807a7a8 100644
--- a/test/net/pop/test_pop.rb
+++ b/test/net/pop/test_pop.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'net/pop'
require 'test/unit'
require 'digest/md5'
@@ -64,6 +64,35 @@ class TestPOP < Test::Unit::TestCase
end
end
+ def test_popmail
+ # totally not representative of real messages, but
+ # enough to test frozen bugs
+ lines = [ "[ruby-core:85210]" , "[Bug #14416]" ].freeze
+ command = Object.new
+ command.instance_variable_set(:@lines, lines)
+
+ def command.retr(n)
+ @lines.each { |l| yield "#{l}\r\n" }
+ end
+
+ def command.top(number, nl)
+ @lines.each do |l|
+ yield "#{l}\r\n"
+ break if (nl -= 1) <= 0
+ end
+ end
+
+ net_pop = :unused
+ popmail = Net::POPMail.new(1, 123, net_pop, command)
+ res = popmail.pop
+ assert_equal "[ruby-core:85210]\r\n[Bug #14416]\r\n", res
+ assert_not_predicate res, :frozen?
+
+ res = popmail.top(1)
+ assert_equal "[ruby-core:85210]\r\n", res
+ assert_not_predicate res, :frozen?
+ end
+
def pop_test(apop=false)
host = 'localhost'
server = TCPServer.new(host, 0)
diff --git a/test/net/protocol/test_protocol.rb b/test/net/protocol/test_protocol.rb
index 0161fbc3a0..2ad3709ac5 100644
--- a/test/net/protocol/test_protocol.rb
+++ b/test/net/protocol/test_protocol.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require "test/unit"
require "net/protocol"
require "stringio"
@@ -6,7 +6,7 @@ require "stringio"
class TestProtocol < Test::Unit::TestCase
def test_should_properly_dot_stuff_period_with_no_endline
bug9627 = '[ruby-core:61441] [Bug #9627]'
- sio = StringIO.new("")
+ sio = StringIO.new("".dup)
imio = Net::InternetMessageIO.new(sio)
email = "To: bob@aol.com\nlook, a period with no endline\n."
imio.write_message(email)
@@ -15,15 +15,110 @@ class TestProtocol < Test::Unit::TestCase
def test_each_crlf_line
assert_output('', '') do
- sio = StringIO.new("")
+ sio = StringIO.new("".dup)
imio = Net::InternetMessageIO.new(sio)
assert_equal(23, imio.write_message("\u3042\r\u3044\n\u3046\r\n\u3048"))
assert_equal("\u3042\r\n\u3044\r\n\u3046\r\n\u3048\r\n.\r\n", sio.string)
- sio = StringIO.new("")
+ sio = StringIO.new("".dup)
imio = Net::InternetMessageIO.new(sio)
assert_equal(8, imio.write_message("\u3042\r"))
assert_equal("\u3042\r\n.\r\n", sio.string)
end
end
+
+ def create_mockio(capacity: 100)
+ mockio = Object.new
+ mockio.instance_variable_set(:@str, +'')
+ mockio.instance_variable_set(:@capacity, capacity)
+ def mockio.string; @str; end
+ def mockio.to_io; self; end
+ def mockio.wait_writable(sec); sleep sec; false; end
+ def mockio.write_nonblock(*strs, exception: true)
+ if @capacity <= @str.bytesize
+ if exception
+ raise Net::WaitWritable
+ else
+ return :wait_writable
+ end
+ end
+ len = 0
+ strs.each do |str|
+ len1 = @str.bytesize
+ break if @capacity <= len1
+ @str << str.byteslice(0, @capacity - @str.bytesize)
+ len2 = @str.bytesize
+ len += len2 - len1
+ end
+ len
+ end
+ mockio
+ end
+
+ def test_write0_multibyte
+ mockio = create_mockio(capacity: 1)
+ def mockio.write_nonblock(str, *strs, **kw)
+ @str << str.byteslice(0, 1)
+ 1
+ end
+ io = Net::BufferedIO.new(mockio)
+ assert_equal(3, io.write("\u3042"))
+ end
+
+ def test_write0_timeout
+ mockio = create_mockio
+ io = Net::BufferedIO.new(mockio)
+ io.write_timeout = 0.1
+ assert_raise(Net::WriteTimeout){ io.write("a"*1000) }
+ end
+
+ def test_write0_success
+ mockio = create_mockio
+ io = Net::BufferedIO.new(mockio)
+ io.write_timeout = 0.1
+ len = io.write("a"*10)
+ assert_equal "a"*10, mockio.string
+ assert_equal 10, len
+ end
+
+ def test_write0_success2
+ mockio = create_mockio
+ io = Net::BufferedIO.new(mockio)
+ io.write_timeout = 0.1
+ len = io.write("a"*100)
+ assert_equal "a"*100, mockio.string
+ assert_equal 100, len
+ end
+
+ def test_write0_success_multi1
+ mockio = create_mockio
+ io = Net::BufferedIO.new(mockio)
+ io.write_timeout = 0.1
+ len = io.write("a"*50, "a"*49)
+ assert_equal "a"*99, mockio.string
+ assert_equal 99, len
+ end
+
+ def test_write0_success_multi2
+ mockio = create_mockio
+ io = Net::BufferedIO.new(mockio)
+ io.write_timeout = 0.1
+ len = io.write("a"*50, "a"*50)
+ assert_equal "a"*100, mockio.string
+ assert_equal 100, len
+ end
+
+ def test_write0_timeout_multi1
+ mockio = create_mockio
+ io = Net::BufferedIO.new(mockio)
+ io.write_timeout = 0.1
+ assert_raise(Net::WriteTimeout){ io.write("a"*50,"a"*51) }
+ end
+
+ def test_write0_timeout_multi2
+ mockio = create_mockio
+ io = Net::BufferedIO.new(mockio)
+ io.write_timeout = 0.1
+ assert_raise(Net::WriteTimeout){ io.write("a"*50,"a"*50,"a") }
+ end
end
diff --git a/test/net/smtp/test_response.rb b/test/net/smtp/test_response.rb
index 79ac1d2d32..3cf909a762 100644
--- a/test/net/smtp/test_response.rb
+++ b/test/net/smtp/test_response.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'net/smtp'
require 'test/unit'
diff --git a/test/net/smtp/test_smtp.rb b/test/net/smtp/test_smtp.rb
index bf25af5b58..23e1542d8f 100644
--- a/test/net/smtp/test_smtp.rb
+++ b/test/net/smtp/test_smtp.rb
@@ -1,13 +1,13 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'net/smtp'
require 'stringio'
require 'test/unit'
module Net
class TestSMTP < Test::Unit::TestCase
- CA_FILE = File.expand_path("../imap/cacert.pem", __dir__)
- SERVER_KEY = File.expand_path("../imap/server.key", __dir__)
- SERVER_CERT = File.expand_path("../imap/server.crt", __dir__)
+ CA_FILE = File.expand_path("../fixtures/cacert.pem", __dir__)
+ SERVER_KEY = File.expand_path("../fixtures/server.key", __dir__)
+ SERVER_CERT = File.expand_path("../fixtures/server.crt", __dir__)
class FakeSocket
attr_reader :write_io
@@ -135,6 +135,8 @@ module Net
sock.close if sock
servers.each(&:close)
end
+ rescue LoadError
+ # skip (require openssl)
end
def test_tls_connect_timeout
@@ -151,12 +153,37 @@ module Net
smtp.start do
end
end
+ rescue LoadError
+ # skip (require openssl)
ensure
sock.close if sock
servers.each(&:close)
end
end
+ def test_eof_error_backtrace
+ bug13018 = '[ruby-core:78550] [Bug #13018]'
+ servers = Socket.tcp_server_sockets("localhost", 0)
+ begin
+ sock = nil
+ t = Thread.start do
+ sock = accept(servers)
+ sock.close
+ end
+ smtp = Net::SMTP.new("localhost", servers[0].local_address.ip_port)
+ e = assert_raise(EOFError, bug13018) do
+ smtp.start do
+ end
+ end
+ assert_equal(EOFError, e.class, bug13018)
+ assert(e.backtrace.grep(%r"\bnet/smtp\.rb:").size > 0, bug13018)
+ ensure
+ sock.close if sock
+ servers.each(&:close)
+ t.join
+ end
+ end
+
private
def accept(servers)
diff --git a/test/net/smtp/test_ssl_socket.rb b/test/net/smtp/test_ssl_socket.rb
index 354f413040..342391f159 100644
--- a/test/net/smtp/test_ssl_socket.rb
+++ b/test/net/smtp/test_ssl_socket.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'net/smtp'
require 'test/unit'
diff --git a/test/nkf/test_kconv.rb b/test/nkf/test_kconv.rb
index de09bcd4ae..3968cc47dc 100644
--- a/test/nkf/test_kconv.rb
+++ b/test/nkf/test_kconv.rb
@@ -6,21 +6,21 @@ class TestKconv < Test::Unit::TestCase
def setup
@euc_str = "\
\xa5\xaa\xa5\xd6\xa5\xb8\xa5\xa7\xa5\xaf\xa5\xc8\xbb\xd8\xb8\xfe\
-\xa5\xd7\xa5\xed\xa5\xb0\xa5\xe9\xa5\xdf\xa5\xf3\xa5\xb0\xb8\xc0\xb8\xec
+\xa5\xd7\xa5\xed\xa5\xb0\xa5\xe9\xa5\xdf\xa5\xf3\xa5\xb0\xb8\xc0\xb8\xec\
\x52\x75\x62\x79".force_encoding('EUC-JP')
@utf8_str = "\
\xe3\x82\xaa\xe3\x83\x96\xe3\x82\xb8\xe3\x82\xa7\
\xe3\x82\xaf\xe3\x83\x88\xe6\x8c\x87\xe5\x90\x91\
\xe3\x83\x97\xe3\x83\xad\xe3\x82\xb0\xe3\x83\xa9\xe3\x83\x9f\
-\xe3\x83\xb3\xe3\x82\xb0\xe8\xa8\x80\xe8\xaa\x9e
+\xe3\x83\xb3\xe3\x82\xb0\xe8\xa8\x80\xe8\xaa\x9e\
\x52\x75\x62\x79".force_encoding('UTF-8')
@sjis_str = "\
\x83\x49\x83\x75\x83\x57\x83\x46\x83\x4e\x83\x67\x8e\x77\x8c\xfc\
-\x83\x76\x83\x8d\x83\x4f\x83\x89\x83\x7e\x83\x93\x83\x4f\x8c\xbe\x8c\xea
+\x83\x76\x83\x8d\x83\x4f\x83\x89\x83\x7e\x83\x93\x83\x4f\x8c\xbe\x8c\xea\
\x52\x75\x62\x79".force_encoding('Shift_JIS')
@jis_str = "\
\x1b\x24\x42\x25\x2a\x25\x56\x25\x38\x25\x27\x25\x2f\x25\x48\x3b\x58\x38\x7e\
-\x25\x57\x25\x6d\x25\x30\x25\x69\x25\x5f\x25\x73\x25\x30\x38\x40\x38\x6c\x1b\x28\x42
+\x25\x57\x25\x6d\x25\x30\x25\x69\x25\x5f\x25\x73\x25\x30\x38\x40\x38\x6c\x1b\x28\x42\
\x52\x75\x62\x79".force_encoding('ISO-2022-JP')
end
diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb
index 52558418bf..f139b90efe 100644
--- a/test/objspace/test_objspace.rb
+++ b/test/objspace/test_objspace.rb
@@ -45,7 +45,7 @@ class TestObjSpace < Test::Unit::TestCase
argf.inplace_mode = nil
size = ObjectSpace.memsize_of(argf)
argf.inplace_mode = "inplace_mode_suffix"
- assert_equal(size + 20, ObjectSpace.memsize_of(argf))
+ assert_equal(size, ObjectSpace.memsize_of(argf))
end
def test_memsize_of_all
@@ -61,9 +61,19 @@ class TestObjSpace < Test::Unit::TestCase
res = ObjectSpace.count_objects_size
assert_not_empty(res)
assert_operator(res[:TOTAL], :>, 0)
+ end
+
+ def test_count_objects_size_with_hash
arg = {}
ObjectSpace.count_objects_size(arg)
assert_not_empty(arg)
+ arg = {:TOTAL => 1 }
+ ObjectSpace.count_objects_size(arg)
+ assert_not_empty(arg)
+ end
+
+ def test_count_objects_size_with_wrong_type
+ assert_raise(TypeError) { ObjectSpace.count_objects_size(0) }
end
def test_count_nodes
@@ -88,6 +98,8 @@ class TestObjSpace < Test::Unit::TestCase
res = ObjectSpace.count_imemo_objects
assert_not_empty(res)
assert_not_nil(res[:imemo_cref])
+ assert_not_empty res.inspect
+
arg = {}
res = ObjectSpace.count_imemo_objects(arg)
assert_not_empty(res)
@@ -100,29 +112,30 @@ class TestObjSpace < Test::Unit::TestCase
end
def test_reachable_objects_from
- assert_separately %w[--disable-gem -robjspace], __FILE__, __LINE__, <<-'eom'
- assert_equal(nil, ObjectSpace.reachable_objects_from(nil))
- assert_equal([Array, 'a', 'b', 'c'], ObjectSpace.reachable_objects_from(['a', 'b', 'c']))
-
- assert_equal([Array, 'a', 'a', 'a'], ObjectSpace.reachable_objects_from(['a', 'a', 'a']))
- assert_equal([Array, 'a', 'a'], ObjectSpace.reachable_objects_from(['a', v = 'a', v]))
- assert_equal([Array, 'a'], ObjectSpace.reachable_objects_from([v = 'a', v, v]))
-
- long_ary = Array.new(1_000){''}
- max = 0
-
- ObjectSpace.each_object{|o|
- refs = ObjectSpace.reachable_objects_from(o)
- max = [refs.size, max].max
-
- unless refs.nil?
- refs.each_with_index {|ro, i|
- assert_not_nil(ro, "#{i}: this referenced object is internal object")
- }
- end
- }
- assert_operator(max, :>=, long_ary.size+1, "1000 elems + Array class")
- eom
+ assert_separately %w[--disable-gem -robjspace], "#{<<-"begin;"}\n#{<<-'end;'}"
+ begin;
+ assert_equal(nil, ObjectSpace.reachable_objects_from(nil))
+ assert_equal([Array, 'a', 'b', 'c'], ObjectSpace.reachable_objects_from(['a', 'b', 'c']))
+
+ assert_equal([Array, 'a', 'a', 'a'], ObjectSpace.reachable_objects_from(['a', 'a', 'a']))
+ assert_equal([Array, 'a', 'a'], ObjectSpace.reachable_objects_from(['a', v = 'a', v]))
+ assert_equal([Array, 'a'], ObjectSpace.reachable_objects_from([v = 'a', v, v]))
+
+ long_ary = Array.new(1_000){''}
+ max = 0
+
+ ObjectSpace.each_object{|o|
+ refs = ObjectSpace.reachable_objects_from(o)
+ max = [refs.size, max].max
+
+ unless refs.nil?
+ refs.each_with_index {|ro, i|
+ assert_not_nil(ro, "#{i}: this referenced object is internal object")
+ }
+ end
+ }
+ assert_operator(max, :>=, long_ary.size+1, "1000 elems + Array class")
+ end;
end
def test_reachable_objects_from_root
@@ -138,15 +151,16 @@ class TestObjSpace < Test::Unit::TestCase
end
def test_reachable_objects_size
- assert_separately %w[--disable-gem -robjspace], __FILE__, __LINE__, <<-'eom'
- ObjectSpace.each_object{|o|
- ObjectSpace.reachable_objects_from(o).each{|reached_obj|
- size = ObjectSpace.memsize_of(reached_obj)
- assert_kind_of(Integer, size)
- assert_operator(size, :>=, 0)
+ assert_separately %w[--disable-gem -robjspace], "#{<<~"begin;"}\n#{<<~'end;'}"
+ begin;
+ ObjectSpace.each_object{|o|
+ ObjectSpace.reachable_objects_from(o).each{|reached_obj|
+ size = ObjectSpace.memsize_of(reached_obj)
+ assert_kind_of(Integer, size)
+ assert_operator(size, :>=, 0)
+ }
}
- }
- eom
+ end;
end
def test_trace_object_allocations
@@ -275,32 +289,121 @@ class TestObjSpace < Test::Unit::TestCase
assert_match /"value":"foobar\h+"/, dump
end
+ def test_dump_includes_imemo_type
+ assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
+ begin;
+ def dump_my_heap_please
+ ObjectSpace.dump_all(output: :stdout)
+ end
+
+ dump_my_heap_please
+ end;
+ heap = output.find_all { |l|
+ obj = JSON.parse(l)
+ obj['type'] == "IMEMO" && obj['imemo_type']
+ }
+ assert_operator heap.length, :>, 0
+ end
+ end
+
+ def test_dump_all_full
+ assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
+ begin;
+ def dump_my_heap_please
+ ObjectSpace.dump_all(output: :stdout, full: true)
+ end
+
+ dump_my_heap_please
+ end;
+ heap = output.find_all { |l| JSON.parse(l)['type'] == "NONE" }
+ assert_operator heap.length, :>, 0
+ end
+ end
+
+ def test_dump_addresses_match_dump_all_addresses
+ assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
+ begin;
+ def dump_my_heap_please
+ obj = Object.new
+ puts ObjectSpace.dump(obj)
+ ObjectSpace.dump_all(output: $stdout)
+ end
+
+ dump_my_heap_please
+ end;
+ needle = JSON.parse(output.first)
+ addr = needle['address']
+ found = output.drop(1).find { |l| JSON.parse(l)['address'] == addr }
+ assert found, "object #{addr} should be findable in full heap dump"
+ end
+ end
+
+ def test_dump_class_addresses_match_dump_all_addresses
+ assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
+ begin;
+ def dump_my_heap_please
+ obj = Object.new
+ puts ObjectSpace.dump(obj)
+ ObjectSpace.dump_all(output: $stdout)
+ end
+
+ dump_my_heap_please
+ end;
+ needle = JSON.parse(output.first)
+ addr = needle['class']
+ found = output.drop(1).find { |l| JSON.parse(l)['address'] == addr }
+ assert found, "object #{addr} should be findable in full heap dump"
+ end
+ end
+
+ def test_dump_reference_addresses_match_dump_all_addresses
+ assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
+ begin;
+ def dump_my_heap_please
+ obj = Object.new
+ obj2 = Object.new
+ obj2.instance_variable_set(:@ref, obj)
+ puts ObjectSpace.dump(obj)
+ ObjectSpace.dump_all(output: $stdout)
+ end
+
+ dump_my_heap_please
+ end;
+ needle = JSON.parse(output.first)
+ addr = needle['address']
+ found = output.drop(1).find { |l| (JSON.parse(l)['references'] || []).include? addr }
+ assert found, "object #{addr} should be findable in full heap dump"
+ end
+ end
+
def test_dump_all
entry = /"bytesize":11, "value":"TEST STRING", "encoding":"UTF-8", "file":"-", "line":4, "method":"dump_my_heap_please", "generation":/
- assert_in_out_err(%w[-robjspace], <<-'end;') do |output, error|
- def dump_my_heap_please
- ObjectSpace.trace_object_allocations_start
- GC.start
- str = "TEST STRING".force_encoding("UTF-8")
- ObjectSpace.dump_all(output: :stdout)
- end
+ assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}#{<<-'end;'}") do |output, error|
+ begin;
+ def dump_my_heap_please
+ ObjectSpace.trace_object_allocations_start
+ GC.start
+ str = "TEST STRING".force_encoding("UTF-8")
+ ObjectSpace.dump_all(output: :stdout)
+ end
- dump_my_heap_please
- end;
+ dump_my_heap_please
+ end;
assert_match(entry, output.grep(/TEST STRING/).join("\n"))
end
- assert_in_out_err(%w[-robjspace], <<-'end;') do |(output), (error)|
- def dump_my_heap_please
- ObjectSpace.trace_object_allocations_start
- GC.start
- str = "TEST STRING".force_encoding("UTF-8")
- ObjectSpace.dump_all().path
- end
+ assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}#{<<-'end;'}") do |(output), (error)|
+ begin;
+ def dump_my_heap_please
+ ObjectSpace.trace_object_allocations_start
+ GC.start
+ str = "TEST STRING".force_encoding("UTF-8")
+ ObjectSpace.dump_all().path
+ end
- puts dump_my_heap_please
- end;
+ puts dump_my_heap_please
+ end;
skip if /is not supported/ =~ error
skip error unless output
assert_match(entry, File.readlines(output).grep(/TEST STRING/).join("\n"))
@@ -384,13 +487,18 @@ class TestObjSpace < Test::Unit::TestCase
end
def test_count_symbols
- syms = (1..128).map{|i| ("xyzzy#{i}" * 128).to_sym}
- c = Class.new{define_method(syms[-1]){}}
+ assert_separately(%w[-robjspace], "#{<<~';;;'}")
+ h0 = ObjectSpace.count_symbols
+
+ syms = (1..128).map{|i| ("xyzzy#{i}_#{Process.pid}_#{rand(1_000_000)}_" * 128).to_sym}
+ syms << Class.new{define_method(syms[-1]){}}
h = ObjectSpace.count_symbols
- assert_operator h[:mortal_dynamic_symbol], :>=, 128, h.inspect
- assert_operator h[:immortal_dynamic_symbol], :>=, 1, h.inspect
- assert_operator h[:immortal_static_symbol], :>=, Object.methods.size, h.inspect
- assert_equal h[:immortal_symbol], h[:immortal_dynamic_symbol] + h[:immortal_static_symbol], h.inspect
+ m = proc {h0.inspect + "\n" + h.inspect}
+ assert_equal 127, h[:mortal_dynamic_symbol] - h0[:mortal_dynamic_symbol], m
+ assert_equal 1, h[:immortal_dynamic_symbol] - h0[:immortal_dynamic_symbol], m
+ assert_operator h[:immortal_static_symbol], :>=, Object.methods.size, m
+ assert_equal h[:immortal_symbol], h[:immortal_dynamic_symbol] + h[:immortal_static_symbol], m
+ ;;;
end
end
diff --git a/test/open-uri/test_open-uri.rb b/test/open-uri/test_open-uri.rb
index 49b3e3da06..0301483902 100644
--- a/test/open-uri/test_open-uri.rb
+++ b/test/open-uri/test_open-uri.rb
@@ -68,6 +68,16 @@ class TestOpenURI < Test::Unit::TestCase
@proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] }
end
+ def test_200_uri_open
+ with_http {|srv, dr, url|
+ srv.mount_proc("/urifoo200", lambda { |req, res| res.body = "urifoo200" } )
+ URI.open("#{url}/urifoo200") {|f|
+ assert_equal("200", f.status[0])
+ assert_equal("urifoo200", f.read)
+ }
+ }
+ end
+
def test_200
with_http {|srv, dr, url|
srv.mount_proc("/foo200", lambda { |req, res| res.body = "foo200" } )
@@ -604,6 +614,24 @@ class TestOpenURI < Test::Unit::TestCase
assert_equal(content_ej, f.read)
assert_equal("text/plain", f.content_type)
assert_equal("euc-jp", f.charset)
+ assert_equal(Encoding::EUC_JP, f.read.encoding)
+ }
+ open("#{url}/ej/", 'r:utf-8') {|f|
+ # override charset with encoding option
+ assert_equal(content_ej.dup.force_encoding('utf-8'), f.read)
+ assert_equal("text/plain", f.content_type)
+ assert_equal("euc-jp", f.charset)
+ assert_equal(Encoding::UTF_8, f.read.encoding)
+ }
+ open("#{url}/ej/", :encoding=>'utf-8') {|f|
+ # override charset with encoding option
+ assert_equal(content_ej.dup.force_encoding('utf-8'), f.read)
+ assert_equal("text/plain", f.content_type)
+ assert_equal("euc-jp", f.charset)
+ assert_equal(Encoding::UTF_8, f.read.encoding)
+ }
+ assert_raise(ArgumentError) {
+ open("#{url}/ej/", 'r:utf-8', :encoding=>'utf-8') {|f| }
}
open("#{url}/nc/") {|f|
assert_equal("aa", f.read)
diff --git a/test/open-uri/test_ssl.rb b/test/open-uri/test_ssl.rb
index 1618541849..337139604f 100644
--- a/test/open-uri/test_ssl.rb
+++ b/test/open-uri/test_ssl.rb
@@ -6,7 +6,6 @@ require 'webrick'
begin
require 'openssl'
require 'webrick/https'
- require_relative '../openssl/utils'
rescue LoadError
end
require 'webrick/httpproxy'
@@ -31,7 +30,7 @@ class TestOpenURISSL
:SSLEnable => true,
:SSLCertificate => OpenSSL::X509::Certificate.new(SERVER_CERT),
:SSLPrivateKey => OpenSSL::PKey::RSA.new(SERVER_KEY),
- :SSLTmpDhCallback => proc { OpenSSL::TestUtils::TEST_KEY_DH1024 },
+ :SSLTmpDhCallback => proc { OpenSSL::PKey::DH.new(DHPARAMS) },
:BindAddress => '127.0.0.1',
:Port => 0})
_, port, _, host = srv.listeners[0].addr
@@ -173,21 +172,21 @@ class TestOpenURISSL
}
end
-end if defined?(OpenSSL::TestUtils)
+end if defined?(OpenSSL::SSL)
-if defined?(OpenSSL::TestUtils)
-# cp /etc/ssl/openssl.cnf . # I copied from OpenSSL 1.0.2h source
+if defined?(OpenSSL::SSL)
+# cp /etc/ssl/openssl.cnf . # I copied from OpenSSL 1.1.1b source
# mkdir demoCA demoCA/private demoCA/newcerts
# touch demoCA/index.txt
# echo 00 > demoCA/serial
-# openssl genrsa -des3 -out demoCA/private/cakey.pem 1024
+# openssl genrsa -des3 -out demoCA/private/cakey.pem 2048
# openssl req -new -key demoCA/private/cakey.pem -out demoCA/careq.pem -subj "/C=JP/ST=Tokyo/O=RubyTest/CN=Ruby Test CA"
# # basicConstraints=CA:TRUE is required; the default openssl.cnf has it in [v3_ca]
# openssl ca -config openssl.cnf -extensions v3_ca -out demoCA/cacert.pem -startdate 090101000000Z -enddate 491231235959Z -batch -keyfile demoCA/private/cakey.pem -selfsign -infiles demoCA/careq.pem
# mkdir server
-# openssl genrsa -des3 -out server/server.key 1024
+# openssl genrsa -des3 -out server/server.key 2048
# openssl req -new -key server/server.key -out server/csr.pem -subj "/C=JP/ST=Tokyo/O=RubyTest/CN=127.0.0.1"
# openssl ca -config openssl.cnf -startdate 090101000000Z -enddate 491231235959Z -in server/csr.pem -keyfile demoCA/private/cakey.pem -cert demoCA/cacert.pem -out server/cert.pem
@@ -200,7 +199,7 @@ Certificate:
Data:
Version: 3 (0x2)
Serial Number: 0 (0x0)
- Signature Algorithm: sha256WithRSAEncryption
+ Signature Algorithm: sha256WithRSAEncryption
Issuer: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA
Validity
Not Before: Jan 1 00:00:00 2009 GMT
@@ -208,49 +207,70 @@ Certificate:
Subject: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
- Public-Key: (1024 bit)
+ RSA Public-Key: (2048 bit)
Modulus:
- 00:be:74:41:33:c9:1b:e1:12:78:6b:b4:52:2e:ae:
- b6:e2:1e:58:65:57:2d:cb:07:3f:91:c9:53:7a:e7:
- 2e:68:2c:0c:5d:8b:16:a7:42:4a:5c:6f:c7:aa:44:
- ff:6d:c6:d7:49:0e:b1:5d:03:5b:51:ce:d5:cc:cd:
- ab:69:cc:c2:43:76:b1:b2:30:3b:e7:f6:1f:3e:35:
- 1d:21:75:41:96:eb:84:a0:34:6f:a4:5d:70:a2:b2:
- d5:fe:b9:45:47:a1:e8:ca:e3:b7:bb:4d:37:1c:f3:
- 96:d4:2d:80:85:cd:8e:31:96:53:92:a0:fe:e4:4c:
- 16:47:5e:c8:27:32:70:a8:6b
+ 00:ad:f3:4d:5b:0b:01:54:cc:86:36:d1:93:6b:33:
+ 56:25:90:61:d6:9a:a0:f4:24:20:ee:c8:14:ab:0f:
+ 4b:89:d8:7c:bb:c0:f8:7f:fb:e9:a2:d5:1c:6b:6f:
+ dc:5c:23:b1:49:aa:2c:e8:ca:43:48:64:69:4b:8a:
+ bd:44:57:9b:14:d9:7a:b2:49:00:d6:c2:74:67:62:
+ 52:1d:a9:32:df:fe:7a:22:20:49:83:e1:cb:3d:dc:
+ 1a:2a:f0:36:20:c1:e8:c8:89:d4:51:1a:68:91:20:
+ e0:ba:67:0a:b2:6b:f8:e3:8c:f5:ee:a1:36:b1:89:
+ ec:23:b6:f2:39:a9:b9:2e:ea:de:d9:86:e5:42:11:
+ 46:ed:10:9a:90:76:44:4e:4d:49:2d:49:e8:e3:cb:
+ ff:7a:7d:80:cb:bf:c4:c3:69:ba:9c:60:4a:de:af:
+ bf:26:78:b8:fb:46:d1:37:d0:89:ba:78:93:6a:37:
+ a5:e9:58:e7:e2:e3:7d:7c:95:20:79:41:56:15:cd:
+ b2:c6:3b:e1:b7:e7:ba:47:60:9a:05:b1:07:f3:26:
+ 72:9d:3b:1b:02:18:3d:d5:de:e6:e9:30:a9:b5:8f:
+ 15:1b:40:f9:64:61:54:d3:53:e8:c4:29:4a:89:f3:
+ e5:0d:fd:16:61:ee:f2:6d:8a:45:a8:34:7e:53:46:
+ 8e:87
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
- 71:DB:DC:BA:F6:7F:75:31:7A:ED:AB:8B:48:93:86:94:1A:FF:30:58
+ A0:7E:0B:AD:A3:AD:37:D7:21:0B:75:6F:8A:90:5F:8C:C9:69:DF:98
X509v3 Authority Key Identifier:
- keyid:71:DB:DC:BA:F6:7F:75:31:7A:ED:AB:8B:48:93:86:94:1A:FF:30:58
+ keyid:A0:7E:0B:AD:A3:AD:37:D7:21:0B:75:6F:8A:90:5F:8C:C9:69:DF:98
- X509v3 Basic Constraints:
+ X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
- 91:1c:45:a5:c0:4e:fc:54:39:62:33:80:7d:03:c1:b8:51:f7:
- 56:83:6c:a3:15:50:cf:92:a0:77:a3:34:16:b5:30:f0:33:5a:
- be:6a:ac:17:87:70:f8:4e:4d:49:ac:8b:84:fd:e5:0f:15:d7:
- 9a:29:cc:a9:f5:97:f5:13:2a:86:3b:2d:f4:b7:b4:a2:7c:e1:
- 0e:2a:ff:91:64:31:8f:12:cc:99:bf:e1:de:8f:6f:7c:1b:e4:
- cc:56:c8:bb:85:c9:ba:df:7f:07:7a:cd:03:22:2c:b6:f8:06:
- 35:72:72:b8:52:eb:62:15:85:2b:8f:8c:bc:27:3c:8b:de:32:
- db:95
+ 06:ea:06:02:19:9a:cb:94:a2:7e:c0:86:71:66:e7:a5:71:46:
+ a2:25:55:f5:e5:58:df:d1:91:58:e6:8a:0e:91:b3:22:4c:88:
+ 4d:5f:02:af:0f:73:65:0d:af:9a:f2:e4:36:f3:1f:e8:28:1d:
+ 9c:74:72:5b:f7:12:e8:fa:45:d6:df:e5:f1:d3:91:f4:0e:db:
+ e2:56:63:ee:82:57:6f:12:ad:d7:0d:de:5a:8c:3d:76:d2:87:
+ c9:48:1c:c4:f3:89:63:3c:c2:25:e0:dd:63:a6:4c:6c:5a:07:
+ 7b:86:78:62:86:02:a1:ef:0e:41:75:c5:d4:61:ab:c3:3b:9b:
+ 51:0b:e6:34:6d:0b:14:5a:2d:aa:d3:58:26:43:8f:4c:d7:45:
+ 73:1e:67:66:5e:f3:0c:69:70:27:a1:d5:70:f3:5a:10:98:c8:
+ 4f:8a:3b:9f:ad:8e:8d:49:8f:fb:f6:36:5d:4f:70:f9:4f:54:
+ 33:cf:a2:a6:1d:8c:61:b9:30:42:f2:49:d1:3d:a1:f1:eb:1e:
+ 78:a6:30:f8:8a:48:89:c7:3e:bd:0d:d8:72:04:a6:00:e5:62:
+ a4:13:3f:9e:b6:86:25:dc:d1:ff:3a:fc:f5:0e:e4:0e:f7:b8:
+ 66:90:fe:4f:c2:54:2a:7f:61:6e:e7:4b:bf:40:7e:75:30:02:
+ 5b:bb:91:1b
-----BEGIN CERTIFICATE-----
-MIICVDCCAb2gAwIBAgIBADANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJKUDEO
+MIIDXDCCAkSgAwIBAgIBADANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJKUDEO
MAwGA1UECAwFVG9reW8xETAPBgNVBAoMCFJ1YnlUZXN0MRUwEwYDVQQDDAxSdWJ5
IFRlc3QgQ0EwHhcNMDkwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjBHMQswCQYD
VQQGEwJKUDEOMAwGA1UECAwFVG9reW8xETAPBgNVBAoMCFJ1YnlUZXN0MRUwEwYD
-VQQDDAxSdWJ5IFRlc3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAL50
-QTPJG+ESeGu0Ui6utuIeWGVXLcsHP5HJU3rnLmgsDF2LFqdCSlxvx6pE/23G10kO
-sV0DW1HO1czNq2nMwkN2sbIwO+f2Hz41HSF1QZbrhKA0b6RdcKKy1f65RUeh6Mrj
-t7tNNxzzltQtgIXNjjGWU5Kg/uRMFkdeyCcycKhrAgMBAAGjUDBOMB0GA1UdDgQW
-BBRx29y69n91MXrtq4tIk4aUGv8wWDAfBgNVHSMEGDAWgBRx29y69n91MXrtq4tI
-k4aUGv8wWDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBAJEcRaXATvxU
-OWIzgH0DwbhR91aDbKMVUM+SoHejNBa1MPAzWr5qrBeHcPhOTUmsi4T95Q8V15op
-zKn1l/UTKoY7LfS3tKJ84Q4q/5FkMY8SzJm/4d6Pb3wb5MxWyLuFybrffwd6zQMi
-LLb4BjVycrhS62IVhSuPjLwnPIveMtuV
+VQQDDAxSdWJ5IFRlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQCt801bCwFUzIY20ZNrM1YlkGHWmqD0JCDuyBSrD0uJ2Hy7wPh/++mi1Rxrb9xc
+I7FJqizoykNIZGlLir1EV5sU2XqySQDWwnRnYlIdqTLf/noiIEmD4cs93Boq8DYg
+wejIidRRGmiRIOC6Zwqya/jjjPXuoTaxiewjtvI5qbku6t7ZhuVCEUbtEJqQdkRO
+TUktSejjy/96fYDLv8TDabqcYErer78meLj7RtE30Im6eJNqN6XpWOfi4318lSB5
+QVYVzbLGO+G357pHYJoFsQfzJnKdOxsCGD3V3ubpMKm1jxUbQPlkYVTTU+jEKUqJ
+8+UN/RZh7vJtikWoNH5TRo6HAgMBAAGjUzBRMB0GA1UdDgQWBBSgfguto6031yEL
+dW+KkF+MyWnfmDAfBgNVHSMEGDAWgBSgfguto6031yELdW+KkF+MyWnfmDAPBgNV
+HRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAG6gYCGZrLlKJ+wIZxZuel
+cUaiJVX15Vjf0ZFY5ooOkbMiTIhNXwKvD3NlDa+a8uQ28x/oKB2cdHJb9xLo+kXW
+3+Xx05H0DtviVmPugldvEq3XDd5ajD120ofJSBzE84ljPMIl4N1jpkxsWgd7hnhi
+hgKh7w5BdcXUYavDO5tRC+Y0bQsUWi2q01gmQ49M10VzHmdmXvMMaXAnodVw81oQ
+mMhPijufrY6NSY/79jZdT3D5T1Qzz6KmHYxhuTBC8knRPaHx6x54pjD4ikiJxz69
+DdhyBKYA5WKkEz+etoYl3NH/Ovz1DuQO97hmkP5PwlQqf2Fu50u/QH51MAJbu5Eb
-----END CERTIFICATE-----
End
@@ -259,7 +279,7 @@ Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1 (0x1)
- Signature Algorithm: sha256WithRSAEncryption
+ Signature Algorithm: sha256WithRSAEncryption
Issuer: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA
Validity
Not Before: Jan 1 00:00:00 2009 GMT
@@ -267,17 +287,26 @@ Certificate:
Subject: C=JP, ST=Tokyo, O=RubyTest, CN=127.0.0.1
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
- Public-Key: (1024 bit)
+ RSA Public-Key: (2048 bit)
Modulus:
- 00:bb:bd:74:69:53:58:50:24:79:f2:eb:db:8b:97:
- e4:69:a4:dd:48:0c:40:35:62:42:b3:35:8c:96:2a:
- 62:76:98:b5:2a:e0:f8:78:33:b6:ff:f8:55:bf:44:
- 69:21:d7:b5:0e:bd:8a:dd:31:1b:88:d5:b4:5e:7a:
- 82:e0:ba:99:6c:04:76:e9:ff:e6:f8:f5:06:8e:7e:
- a4:db:db:eb:43:44:12:a7:ca:ca:2b:aa:5f:83:10:
- e2:9e:35:55:e8:e8:af:be:c8:7d:bb:c2:d4:aa:c1:
- 1c:57:0b:c0:0c:3a:1d:6e:23:a9:03:26:7c:ea:8c:
- f0:86:61:ce:f1:ff:42:c7:23
+ 00:cb:b3:71:95:12:70:fc:db:d4:a9:a7:66:d6:d3:
+ 09:dd:06:80:19:e1:f2:d6:1e:31:b6:6b:20:75:51:
+ dc:a7:37:a9:ac:5b:57:5d:69:36:b6:de:1d:2c:f6:
+ 44:64:f8:e8:d6:f0:da:38:6a:ba:c2:b1:9e:dc:bb:
+ 79:94:e0:25:0c:ce:76:87:17:5d:79:9e:14:9e:bd:
+ 4c:0d:aa:74:10:3a:96:ef:76:82:d5:72:16:b5:c1:
+ ac:17:2d:90:83:73:5c:d7:a6:f5:36:0f:4c:55:f3:
+ 30:5d:19:dc:01:0e:f8:e6:fe:a5:ad:52:88:59:dc:
+ 4a:07:ed:a2:eb:a1:01:63:c4:8a:92:ba:06:80:9b:
+ 0d:85:f2:9f:f9:70:ac:d7:ad:f0:7a:3f:b8:92:2a:
+ 33:ca:69:d0:01:65:5d:31:38:1d:f6:1f:b2:17:07:
+ 7e:ac:88:67:a6:c4:5f:3e:93:94:61:e6:e4:49:9d:
+ ba:d4:d2:e8:e3:93:d1:66:79:c5:e3:1d:f8:5a:50:
+ 54:58:3d:04:b0:fd:65:d1:b3:8a:b5:8a:30:5f:b2:
+ dc:34:1a:14:f7:74:4c:03:29:97:63:5a:d7:de:bb:
+ eb:7f:4a:2a:90:59:c0:2b:47:09:82:8f:75:de:14:
+ 3f:bc:78:9a:69:25:80:5b:6c:a0:65:12:0d:29:61:
+ ac:f9
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
@@ -285,105 +314,200 @@ Certificate:
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
- 7F:17:5A:58:88:96:E1:1F:44:EA:FF:AD:C6:2E:90:E2:95:32:DD:F0
+ EC:6B:7C:79:B8:3B:11:1D:42:F3:9A:2A:CF:9A:15:59:D7:F9:D8:C6
X509v3 Authority Key Identifier:
- keyid:71:DB:DC:BA:F6:7F:75:31:7A:ED:AB:8B:48:93:86:94:1A:FF:30:58
+ keyid:A0:7E:0B:AD:A3:AD:37:D7:21:0B:75:6F:8A:90:5F:8C:C9:69:DF:98
Signature Algorithm: sha256WithRSAEncryption
- 1c:80:02:67:f0:4e:a8:5a:6a:73:9c:de:75:ad:7d:2e:e9:ce:
- c3:2e:cd:70:b4:21:d9:42:0d:7c:0e:77:9e:97:91:13:02:77:
- 4a:cd:f6:fc:26:3d:42:2e:08:85:05:10:df:3a:5f:f0:77:85:
- 44:29:41:dd:03:6b:eb:e7:c8:89:8e:d1:57:a8:ac:43:c8:85:
- c3:95:64:9f:a5:6e:e9:2e:6e:06:45:21:36:ec:d5:79:f5:0e:
- a8:53:b5:f7:02:b0:59:12:e3:ae:73:25:fd:18:ab:23:b2:fc:
- a9:f9:60:e5:a7:d8:ba:0f:db:be:17:81:25:90:fd:7a:21:cb:
- fa:8b
+ 29:14:db:71:e9:a0:86:f8:cc:4d:e4:8a:76:78:a7:ff:4e:94:
+ b4:4d:92:dc:57:9a:52:64:46:27:15:8b:4f:2a:18:a7:0d:fc:
+ d2:75:ce:4e:49:97:0b:46:71:57:23:e3:a5:c0:c5:71:94:fc:
+ f2:1d:3b:06:93:82:03:59:56:d4:fb:09:06:08:b4:97:50:33:
+ cf:58:89:dd:91:31:07:26:9a:7e:7f:8d:71:de:09:dc:4f:e5:
+ 6b:a3:10:71:d4:50:24:43:a0:1c:f5:2a:d9:1a:fb:e3:d6:f1:
+ bc:6b:42:67:16:b4:3b:31:f4:ec:03:7d:78:e2:64:16:57:6d:
+ ba:7c:0c:e1:14:b2:7c:75:4e:2b:09:3e:86:e4:aa:cc:7e:5c:
+ 2b:bd:8d:26:4d:49:36:74:86:fe:c5:a6:15:4a:af:e8:b4:4e:
+ d5:f2:e1:59:c2:fb:7e:c3:c4:f1:63:d8:c2:b0:9a:ae:31:96:
+ 90:c3:09:d0:ce:2e:31:90:d7:83:dd:ac:31:cc:f7:87:41:08:
+ 92:33:28:52:fa:2d:9e:ad:ae:6a:9f:c3:be:ce:c1:a6:e4:16:
+ 2f:69:34:40:86:b6:10:21:0e:31:69:81:9e:fc:fd:c3:06:25:
+ 65:37:d3:d9:4a:20:84:aa:e7:0e:60:7c:bf:3f:88:67:ac:e5:
+ 8c:e0:61:d6
-----BEGIN CERTIFICATE-----
-MIICfDCCAeWgAwIBAgIBATANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJKUDEO
+MIIDgTCCAmmgAwIBAgIBATANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJKUDEO
MAwGA1UECAwFVG9reW8xETAPBgNVBAoMCFJ1YnlUZXN0MRUwEwYDVQQDDAxSdWJ5
IFRlc3QgQ0EwHhcNMDkwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjBEMQswCQYD
VQQGEwJKUDEOMAwGA1UECAwFVG9reW8xETAPBgNVBAoMCFJ1YnlUZXN0MRIwEAYD
-VQQDDAkxMjcuMC4wLjEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALu9dGlT
-WFAkefLr24uX5Gmk3UgMQDViQrM1jJYqYnaYtSrg+Hgztv/4Vb9EaSHXtQ69it0x
-G4jVtF56guC6mWwEdun/5vj1Bo5+pNvb60NEEqfKyiuqX4MQ4p41Vejor77IfbvC
-1KrBHFcLwAw6HW4jqQMmfOqM8IZhzvH/QscjAgMBAAGjezB5MAkGA1UdEwQCMAAw
-LAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0G
-A1UdDgQWBBR/F1pYiJbhH0Tq/63GLpDilTLd8DAfBgNVHSMEGDAWgBRx29y69n91
-MXrtq4tIk4aUGv8wWDANBgkqhkiG9w0BAQsFAAOBgQAcgAJn8E6oWmpznN51rX0u
-6c7DLs1wtCHZQg18Dneel5ETAndKzfb8Jj1CLgiFBRDfOl/wd4VEKUHdA2vr58iJ
-jtFXqKxDyIXDlWSfpW7pLm4GRSE27NV59Q6oU7X3ArBZEuOucyX9GKsjsvyp+WDl
-p9i6D9u+F4ElkP16Icv6iw==
+VQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDL
+s3GVEnD829Spp2bW0wndBoAZ4fLWHjG2ayB1UdynN6msW1ddaTa23h0s9kRk+OjW
+8No4arrCsZ7cu3mU4CUMznaHF115nhSevUwNqnQQOpbvdoLVcha1wawXLZCDc1zX
+pvU2D0xV8zBdGdwBDvjm/qWtUohZ3EoH7aLroQFjxIqSugaAmw2F8p/5cKzXrfB6
+P7iSKjPKadABZV0xOB32H7IXB36siGemxF8+k5Rh5uRJnbrU0ujjk9FmecXjHfha
+UFRYPQSw/WXRs4q1ijBfstw0GhT3dEwDKZdjWtfeu+t/SiqQWcArRwmCj3XeFD+8
+eJppJYBbbKBlEg0pYaz5AgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgEN
+BB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBTsa3x5
+uDsRHULzmirPmhVZ1/nYxjAfBgNVHSMEGDAWgBSgfguto6031yELdW+KkF+MyWnf
+mDANBgkqhkiG9w0BAQsFAAOCAQEAKRTbcemghvjMTeSKdnin/06UtE2S3FeaUmRG
+JxWLTyoYpw380nXOTkmXC0ZxVyPjpcDFcZT88h07BpOCA1lW1PsJBgi0l1Azz1iJ
+3ZExByaafn+Ncd4J3E/la6MQcdRQJEOgHPUq2Rr749bxvGtCZxa0OzH07AN9eOJk
+FldtunwM4RSyfHVOKwk+huSqzH5cK72NJk1JNnSG/sWmFUqv6LRO1fLhWcL7fsPE
+8WPYwrCarjGWkMMJ0M4uMZDXg92sMcz3h0EIkjMoUvotnq2uap/Dvs7BpuQWL2k0
+QIa2ECEOMWmBnvz9wwYlZTfT2UoghKrnDmB8vz+IZ6zljOBh1g==
-----END CERTIFICATE-----
End
TestOpenURISSL::SERVER_KEY = <<'End'
-Private-Key: (1024 bit)
+RSA Private-Key: (2048 bit, 2 primes)
modulus:
- 00:bb:bd:74:69:53:58:50:24:79:f2:eb:db:8b:97:
- e4:69:a4:dd:48:0c:40:35:62:42:b3:35:8c:96:2a:
- 62:76:98:b5:2a:e0:f8:78:33:b6:ff:f8:55:bf:44:
- 69:21:d7:b5:0e:bd:8a:dd:31:1b:88:d5:b4:5e:7a:
- 82:e0:ba:99:6c:04:76:e9:ff:e6:f8:f5:06:8e:7e:
- a4:db:db:eb:43:44:12:a7:ca:ca:2b:aa:5f:83:10:
- e2:9e:35:55:e8:e8:af:be:c8:7d:bb:c2:d4:aa:c1:
- 1c:57:0b:c0:0c:3a:1d:6e:23:a9:03:26:7c:ea:8c:
- f0:86:61:ce:f1:ff:42:c7:23
+ 00:cb:b3:71:95:12:70:fc:db:d4:a9:a7:66:d6:d3:
+ 09:dd:06:80:19:e1:f2:d6:1e:31:b6:6b:20:75:51:
+ dc:a7:37:a9:ac:5b:57:5d:69:36:b6:de:1d:2c:f6:
+ 44:64:f8:e8:d6:f0:da:38:6a:ba:c2:b1:9e:dc:bb:
+ 79:94:e0:25:0c:ce:76:87:17:5d:79:9e:14:9e:bd:
+ 4c:0d:aa:74:10:3a:96:ef:76:82:d5:72:16:b5:c1:
+ ac:17:2d:90:83:73:5c:d7:a6:f5:36:0f:4c:55:f3:
+ 30:5d:19:dc:01:0e:f8:e6:fe:a5:ad:52:88:59:dc:
+ 4a:07:ed:a2:eb:a1:01:63:c4:8a:92:ba:06:80:9b:
+ 0d:85:f2:9f:f9:70:ac:d7:ad:f0:7a:3f:b8:92:2a:
+ 33:ca:69:d0:01:65:5d:31:38:1d:f6:1f:b2:17:07:
+ 7e:ac:88:67:a6:c4:5f:3e:93:94:61:e6:e4:49:9d:
+ ba:d4:d2:e8:e3:93:d1:66:79:c5:e3:1d:f8:5a:50:
+ 54:58:3d:04:b0:fd:65:d1:b3:8a:b5:8a:30:5f:b2:
+ dc:34:1a:14:f7:74:4c:03:29:97:63:5a:d7:de:bb:
+ eb:7f:4a:2a:90:59:c0:2b:47:09:82:8f:75:de:14:
+ 3f:bc:78:9a:69:25:80:5b:6c:a0:65:12:0d:29:61:
+ ac:f9
publicExponent: 65537 (0x10001)
privateExponent:
- 00:af:3a:ec:17:0a:f5:d9:07:d2:d3:4c:15:c5:3b:
- 66:b4:bc:6e:d5:ba:a9:8b:aa:45:3b:63:f5:ee:8b:
- 6d:0f:e9:04:e0:1a:cf:8f:d2:25:32:d1:a5:a7:3a:
- c1:2e:17:5a:25:82:00:c4:e7:fb:1d:42:ea:71:6c:
- c4:0f:e1:db:23:ff:1e:d6:c8:d6:60:ca:2d:06:fc:
- 54:3c:03:d4:09:96:bb:38:7a:22:a1:61:2c:f7:d0:
- d0:90:6c:9f:61:ba:61:30:5a:aa:64:ad:43:3a:53:
- 38:e8:ba:cc:8c:51:3e:68:3e:3a:6a:0f:5d:5d:e0:
- d6:df:f2:54:93:d3:14:22:a1
+ 12:be:d5:b2:01:3b:72:99:8c:4d:7c:81:43:3d:b2:
+ 87:ab:84:78:5d:49:aa:98:a6:bc:81:c9:3f:e2:a3:
+ aa:a3:bd:b2:85:c9:59:68:48:47:b5:d2:fb:83:42:
+ 32:04:91:f0:cd:c3:57:33:c3:32:0d:84:70:0d:b4:
+ 97:95:b4:f3:23:c0:d6:97:b8:db:6b:47:bc:7f:f1:
+ 12:c4:df:df:6a:74:df:5e:89:95:b8:e5:0c:1e:e1:
+ 86:54:84:1b:04:af:c3:8c:b2:be:21:d4:45:88:96:
+ a7:ca:ac:6b:50:84:69:45:7f:db:9e:5f:bb:dd:40:
+ d6:cf:f0:91:3c:84:d3:38:65:c9:15:f7:9e:37:aa:
+ 1a:2e:bc:16:b6:95:be:bc:af:45:76:ba:ad:99:f6:
+ ef:6a:e8:fd:f0:31:89:19:c4:04:67:a1:ec:c4:79:
+ 59:08:77:ab:0b:65:88:88:02:b1:38:5c:80:4e:27:
+ 78:b2:a5:bd:b5:ad:d5:9c:4c:ea:ad:db:05:56:25:
+ 70:28:da:22:fb:d8:de:8c:3b:78:fe:3e:cf:ed:1b:
+ f9:97:c6:b6:4a:bf:60:08:8f:dc:85:5e:b1:49:ab:
+ 87:8b:68:72:f4:6a:3f:bc:db:a3:6c:f7:e8:b0:15:
+ bb:4b:ba:37:49:a2:d1:7c:f8:4f:1b:05:11:22:d9:
+ 81
prime1:
- 00:e8:ec:11:fe:e6:2b:23:21:29:d5:40:a6:11:ec:
- 4c:ae:4d:08:2a:71:18:ac:d1:3e:40:2f:12:41:59:
- 12:09:e2:f7:c2:d7:6b:0a:96:0a:06:e3:90:6a:4e:
- b2:eb:25:b7:09:68:e9:13:ab:d0:5a:29:7a:e4:72:
- 1a:ee:46:a0:8b
+ 00:fb:d2:cb:14:61:00:c1:7a:83:ba:fe:79:97:a2:
+ 4d:5a:ea:40:78:96:6e:d2:be:71:5b:c6:2c:1f:c9:
+ 18:48:6b:ae:20:86:87:b5:08:0b:17:69:ca:93:cd:
+ 00:36:22:51:7b:d5:2d:8c:0c:0e:de:bc:86:a8:07:
+ 0e:c5:57:e4:df:be:ed:7d:cc:b1:a4:d6:a8:2b:00:
+ 65:2a:69:30:5e:dc:6d:6d:c4:c8:7e:20:34:eb:6f:
+ 5e:cf:b3:b8:2e:8d:56:31:44:a8:17:ea:be:65:19:
+ ff:da:14:e0:0c:73:56:14:08:47:4c:5b:79:51:74:
+ 5d:bc:e7:fe:01:2f:55:27:69
prime2:
- 00:ce:57:5e:31:e9:c9:a8:5b:1f:55:af:67:e2:49:
- 2a:af:90:b6:02:c0:32:2f:ca:ae:1e:de:47:81:73:
- a8:f8:37:53:70:93:24:62:77:d4:b8:80:30:9f:65:
- 26:20:46:ae:5a:65:6e:6d:af:68:4c:8d:e8:3c:f3:
- d1:d1:d9:6e:c9
+ 00:cf:14:54:47:bb:5f:5d:d6:2b:2d:ed:a6:8a:6f:
+ 36:fc:47:5e:9f:84:ae:aa:1f:f8:44:50:91:15:f5:
+ ed:9d:29:d9:2b:2a:19:66:56:2e:96:15:b5:8e:a9:
+ 7f:89:27:21:b5:57:55:7e:2a:c5:8c:93:fe:f6:0a:
+ a5:17:15:91:91:b3:7d:35:1a:d5:9a:2e:b8:0d:ad:
+ e6:97:6d:83:a3:27:29:ee:00:74:ef:57:34:f3:07:
+ ad:12:43:37:0c:5c:b7:26:34:bc:4e:3a:43:65:6b:
+ 0c:b8:23:ac:77:fd:b2:23:eb:7b:65:70:f6:96:c4:
+ 17:2c:aa:24:b8:a5:5e:b7:11
exponent1:
- 03:f1:02:b8:f2:82:26:5d:08:4d:30:83:de:e7:c5:
- c0:69:53:4b:0c:90:e3:53:c3:1e:e8:ed:01:28:15:
- b3:0f:21:2c:2d:e3:04:d1:d7:27:98:b0:37:ec:4f:
- 00:c5:a9:9c:42:27:37:8a:ff:c2:96:d3:1a:8c:87:
- c2:22:75:d3
+ 00:92:32:ae:f4:05:dd:0a:76:b6:43:b9:b9:9d:ee:
+ fc:39:ec:05:c1:fc:94:1a:85:b6:0a:31:e3:2c:10:
+ f3:a8:17:db:df:c6:3a:c3:3f:08:31:6f:99:cc:75:
+ 17:ca:55:e2:38:a2:6a:ef:03:91:1e:7f:15:2e:37:
+ ea:bb:67:6b:d8:fa:5f:a6:c9:4f:d9:03:46:5e:b0:
+ bc:0b:03:46:b1:cc:07:3b:d3:23:13:16:5f:a2:cf:
+ e5:9b:70:1b:5d:eb:70:3e:ea:3d:2c:a5:7c:23:f6:
+ 14:33:e8:2a:ab:0f:ca:c9:96:84:ce:2f:cd:1f:1d:
+ 0f:ce:bc:61:1b:0e:ff:c1:01
exponent2:
- 6f:17:32:ab:84:c7:01:51:2d:e9:9f:ea:3a:36:52:
- 38:fb:9c:42:96:df:6e:43:9c:c3:19:c1:3d:bc:db:
- 77:e7:b1:90:a6:67:ac:6b:ff:a6:e5:bd:47:d3:d9:
- 56:ff:36:d7:8c:4c:8b:d9:28:3a:2f:1c:9d:d4:57:
- 5e:b7:c5:a1
+ 00:9e:0b:f3:03:48:73:d1:e7:9a:cf:13:f9:ae:e0:
+ 91:03:dc:e8:d0:30:f1:2a:30:fa:48:11:81:9a:54:
+ 37:c5:62:e2:37:fa:8a:a6:3b:92:94:c3:fe:ec:e2:
+ 5a:cf:70:09:5f:21:47:c3:e2:9b:21:de:f6:92:0c:
+ af:d1:bd:89:7b:bd:95:0b:49:ee:cb:1d:6b:26:2d:
+ 9a:b7:ea:42:b4:ec:38:29:49:39:f6:4e:05:c0:93:
+ 14:39:c3:09:29:ab:3d:b1:b0:40:24:28:7d:b5:d3:
+ 0d:43:21:1f:09:f9:9b:d3:a4:6f:6a:8d:db:f6:57:
+ b5:24:46:bb:7e:1d:e0:fb:31
coefficient:
- 45:50:47:66:56:e9:21:d9:40:0e:af:3f:f2:05:77:
- ab:e7:08:40:97:88:2a:51:b3:7e:86:b0:b2:03:2e:
- 6d:36:3f:46:42:97:7d:5a:a2:93:6c:05:c2:8b:8b:
- 2d:af:d5:7d:75:e9:70:f0:2d:21:e3:b9:cf:4d:9a:
- c4:97:e2:79
+ 10:93:1d:c8:33:a5:c1:d3:84:6a:22:68:e5:60:cc:
+ 9c:27:0a:52:0b:58:a3:0c:83:f4:f4:46:09:0c:a1:
+ 41:a6:ea:bf:80:9d:0e:5d:d8:3d:25:00:c5:a1:35:
+ 7a:8c:ea:95:16:94:c3:7c:8f:2b:e0:53:ea:66:ae:
+ 19:be:55:04:3d:ee:e2:4b:a8:69:1b:7e:d8:09:7f:
+ ed:7c:ee:95:88:10:dc:4b:5b:bf:81:a4:e8:dc:7e:
+ 4f:e5:c3:90:c4:e5:5a:90:10:32:d6:08:b5:1f:5d:
+ 09:18:d8:44:28:e4:c4:c7:07:75:9b:9b:b3:80:86:
+ 68:9d:fe:68:f3:4d:db:66
+writing RSA key
-----BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQC7vXRpU1hQJHny69uLl+RppN1IDEA1YkKzNYyWKmJ2mLUq4Ph4
-M7b/+FW/RGkh17UOvYrdMRuI1bReeoLguplsBHbp/+b49QaOfqTb2+tDRBKnysor
-ql+DEOKeNVXo6K++yH27wtSqwRxXC8AMOh1uI6kDJnzqjPCGYc7x/0LHIwIDAQAB
-AoGBAK867BcK9dkH0tNMFcU7ZrS8btW6qYuqRTtj9e6LbQ/pBOAaz4/SJTLRpac6
-wS4XWiWCAMTn+x1C6nFsxA/h2yP/HtbI1mDKLQb8VDwD1AmWuzh6IqFhLPfQ0JBs
-n2G6YTBaqmStQzpTOOi6zIxRPmg+OmoPXV3g1t/yVJPTFCKhAkEA6OwR/uYrIyEp
-1UCmEexMrk0IKnEYrNE+QC8SQVkSCeL3wtdrCpYKBuOQak6y6yW3CWjpE6vQWil6
-5HIa7kagiwJBAM5XXjHpyahbH1WvZ+JJKq+QtgLAMi/Krh7eR4FzqPg3U3CTJGJ3
-1LiAMJ9lJiBGrlplbm2vaEyN6Dzz0dHZbskCQAPxArjygiZdCE0wg97nxcBpU0sM
-kONTwx7o7QEoFbMPISwt4wTR1yeYsDfsTwDFqZxCJzeK/8KW0xqMh8IiddMCQG8X
-MquExwFRLemf6jo2Ujj7nEKW325DnMMZwT2823fnsZCmZ6xr/6blvUfT2Vb/NteM
-TIvZKDovHJ3UV163xaECQEVQR2ZW6SHZQA6vP/IFd6vnCECXiCpRs36GsLIDLm02
-P0ZCl31aopNsBcKLiy2v1X116XDwLSHjuc9NmsSX4nk=
+MIIEpAIBAAKCAQEAy7NxlRJw/NvUqadm1tMJ3QaAGeHy1h4xtmsgdVHcpzeprFtX
+XWk2tt4dLPZEZPjo1vDaOGq6wrGe3Lt5lOAlDM52hxddeZ4Unr1MDap0EDqW73aC
+1XIWtcGsFy2Qg3Nc16b1Ng9MVfMwXRncAQ745v6lrVKIWdxKB+2i66EBY8SKkroG
+gJsNhfKf+XCs163wej+4kiozymnQAWVdMTgd9h+yFwd+rIhnpsRfPpOUYebkSZ26
+1NLo45PRZnnF4x34WlBUWD0EsP1l0bOKtYowX7LcNBoU93RMAymXY1rX3rvrf0oq
+kFnAK0cJgo913hQ/vHiaaSWAW2ygZRINKWGs+QIDAQABAoIBABK+1bIBO3KZjE18
+gUM9soerhHhdSaqYpryByT/io6qjvbKFyVloSEe10vuDQjIEkfDNw1czwzINhHAN
+tJeVtPMjwNaXuNtrR7x/8RLE399qdN9eiZW45Qwe4YZUhBsEr8OMsr4h1EWIlqfK
+rGtQhGlFf9ueX7vdQNbP8JE8hNM4ZckV9543qhouvBa2lb68r0V2uq2Z9u9q6P3w
+MYkZxARnoezEeVkId6sLZYiIArE4XIBOJ3iypb21rdWcTOqt2wVWJXAo2iL72N6M
+O3j+Ps/tG/mXxrZKv2AIj9yFXrFJq4eLaHL0aj+826Ns9+iwFbtLujdJotF8+E8b
+BREi2YECgYEA+9LLFGEAwXqDuv55l6JNWupAeJZu0r5xW8YsH8kYSGuuIIaHtQgL
+F2nKk80ANiJRe9UtjAwO3ryGqAcOxVfk377tfcyxpNaoKwBlKmkwXtxtbcTIfiA0
+629ez7O4Lo1WMUSoF+q+ZRn/2hTgDHNWFAhHTFt5UXRdvOf+AS9VJ2kCgYEAzxRU
+R7tfXdYrLe2mim82/Eden4Suqh/4RFCRFfXtnSnZKyoZZlYulhW1jql/iSchtVdV
+firFjJP+9gqlFxWRkbN9NRrVmi64Da3ml22Doycp7gB071c08wetEkM3DFy3JjS8
+TjpDZWsMuCOsd/2yI+t7ZXD2lsQXLKokuKVetxECgYEAkjKu9AXdCna2Q7m5ne78
+OewFwfyUGoW2CjHjLBDzqBfb38Y6wz8IMW+ZzHUXylXiOKJq7wORHn8VLjfqu2dr
+2PpfpslP2QNGXrC8CwNGscwHO9MjExZfos/lm3AbXetwPuo9LKV8I/YUM+gqqw/K
+yZaEzi/NHx0PzrxhGw7/wQECgYEAngvzA0hz0eeazxP5ruCRA9zo0DDxKjD6SBGB
+mlQ3xWLiN/qKpjuSlMP+7OJaz3AJXyFHw+KbId72kgyv0b2Je72VC0nuyx1rJi2a
+t+pCtOw4KUk59k4FwJMUOcMJKas9sbBAJCh9tdMNQyEfCfmb06Rvao3b9le1JEa7
+fh3g+zECgYAQkx3IM6XB04RqImjlYMycJwpSC1ijDIP09EYJDKFBpuq/gJ0OXdg9
+JQDFoTV6jOqVFpTDfI8r4FPqZq4ZvlUEPe7iS6hpG37YCX/tfO6ViBDcS1u/gaTo
+3H5P5cOQxOVakBAy1gi1H10JGNhEKOTExwd1m5uzgIZonf5o803bZg==
-----END RSA PRIVATE KEY-----
End
+TestOpenURISSL::DHPARAMS = <<'End'
+ DH Parameters: (2048 bit)
+ prime:
+ 00:ec:4e:a4:06:b6:22:ca:f9:8a:00:cc:d0:ee:2f:
+ 16:bf:05:64:f5:8f:fe:7f:c4:bb:b0:24:cd:ef:5d:
+ 8a:90:ad:dc:a9:dd:63:84:90:d8:25:ba:d8:78:d5:
+ 77:91:42:0a:84:fc:56:1e:13:9b:1c:aa:43:d5:1f:
+ 38:52:92:fe:b3:66:f9:e7:e8:8c:77:a1:a6:2f:b3:
+ 98:98:d2:13:fc:57:1c:2a:14:dc:bd:e6:9b:54:19:
+ 99:4f:ce:81:64:a6:32:7f:8e:61:50:5f:45:3a:e5:
+ 0c:f7:13:f3:b8:ad:d5:77:ca:09:42:f7:d8:30:27:
+ 7b:2c:f0:b4:b5:a0:04:96:34:0b:47:81:1d:7f:c1:
+ 3a:62:86:8e:7d:f8:13:7f:9a:b1:8b:09:23:9e:55:
+ 59:41:cd:f0:86:09:c4:b7:d1:69:54:cb:d0:f5:e9:
+ 27:c9:e1:81:e4:a1:df:6b:20:1c:df:e8:54:02:f2:
+ 37:fc:2a:f7:d5:b3:6f:79:7e:70:22:78:79:18:3c:
+ 75:14:68:4a:05:9f:ac:d4:7f:9a:79:db:9d:0a:6e:
+ ec:0a:04:70:bf:c9:4a:59:81:a2:1f:33:9b:4a:66:
+ bc:03:ce:8a:1b:e3:03:ec:ba:39:26:ab:90:dc:39:
+ 41:a1:d8:f7:20:3c:8f:af:12:2f:f7:a9:6f:44:f1:
+ 6d:03
+ generator: 2 (0x2)
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY
+JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab
+VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6
+YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
+1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD
+7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg==
+-----END DH PARAMETERS-----
+End
+
end
diff --git a/test/openssl/fixtures/pkey/dh-1.pem b/test/openssl/fixtures/pkey/dh-1.pem
new file mode 100644
index 0000000000..3340a6a188
--- /dev/null
+++ b/test/openssl/fixtures/pkey/dh-1.pem
@@ -0,0 +1,13 @@
+-----BEGIN DH PARAMETERS-----
+MIICCAKCAgEAvRzXYxY6L2DjeYmm1eowtMDu1it3j+VwFr6s6PRWzc1apMtztr9G
+xZ2mYndUAJLgNLO3n2fUDCYVMB6ZkcekW8Siocof3xWiMA6wqZ6uw0dsE3q7ZX+6
+TLjgSjaXeGvjutvuEwVrFeaUi83bMgfXN8ToxIQVprIF35sYFt6fpbFATKfW7qqi
+P1pQkjmCskU4tztaWvlLh0qg85wuQGnpJaQT3gS30378i0IGbA0EBvJcSpTHYbLa
+nsdI9bfN/ZVgeolVMNMU9/n8R8vRhNPcHuciFwaqS656q+HavCIyxw/LfjSwwFvR
+TngCn0wytRErkzFIXnRKckh8/BpI4S+0+l1NkOwG4WJ55KJ/9OOdZW5o/QCp2bDi
+E0JN1EP/gkSom/prq8JR/yEqtsy99uc5nUxPmzv0IgdcFHZEfiQU7iRggEbx7qfQ
+Ve55XksmmJInmpCy1bSabAEgIKp8Ckt5KLYZ0RgTXUhcEpsxEo6cuAwoSJT5o4Rp
+yG3xow2ozPcqZkvb+d2CHj1sc54w9BVFAjVANEKmRil/9WKz14bu3wxEhOPqC54n
+QojjLcoXSoT66ZUOQnYxTSiLtzoKGPy8cAVPbkBrXz2u2sj5gcvr1JjoGjdHm9/3
+qnqC8fsTz8UndKNIQC337o4K0833bQMzRGl1/qjbAPit2B7E3b6xTZMCAQI=
+-----END DH PARAMETERS-----
diff --git a/test/openssl/fixtures/pkey/dh1024.pem b/test/openssl/fixtures/pkey/dh1024.pem
new file mode 100644
index 0000000000..f99c757f21
--- /dev/null
+++ b/test/openssl/fixtures/pkey/dh1024.pem
@@ -0,0 +1,5 @@
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0
+pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG
+AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
+-----END DH PARAMETERS-----
diff --git a/test/openssl/fixtures/pkey/dsa1024.pem b/test/openssl/fixtures/pkey/dsa1024.pem
new file mode 100644
index 0000000000..1bf498895e
--- /dev/null
+++ b/test/openssl/fixtures/pkey/dsa1024.pem
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBugIBAAKBgQCH9aAoXvWWThIjkA6D+nI1F9ksF9iDq594rkiGNOT9sPDOdB+n
+D+qeeeeloRlj19ymCSADPI0ZLRgkchkAEnY2RnqnhHOjVf/roGgRbW+iQDMbQ9wa
+/pvc6/fAbsu1goE1hBYjm98/sZEeXavj8tR56IXnjF1b6Nx0+sgeUKFKEQIVAMiz
+4BJUFeTtddyM4uadBM7HKLPRAoGAZdLBSYNGiij7vAjesF5mGUKTIgPd+JKuBEDx
+OaBclsgfdoyoF/TMOkIty+PVlYD+//Vl2xnoUEIRaMXHwHfm0r2xUX++oeRaSScg
+YizJdUxe5jvBuBszGPRc/mGpb9YvP0sB+FL1KmuxYmdODfCe51zl8uM/CVhouJ3w
+DjmRGscCgYAuFlfC7p+e8huCKydfcv/beftqjewiOPpQ3u5uI6KPCtCJPpDhs3+4
+IihH2cPsAlqwGF4tlibW1+/z/OZ1AZinPK3y7b2jSJASEaPeEltVzB92hcd1khk2
+jTYcmSsV4VddplOPK9czytR/GbbibxsrhhgZUbd8LPbvIgaiadJ1PgIUBnJ/5vN2
+CVArsEzlPUCbohPvZnE=
+-----END DSA PRIVATE KEY-----
diff --git a/test/openssl/fixtures/pkey/dsa256.pem b/test/openssl/fixtures/pkey/dsa256.pem
new file mode 100644
index 0000000000..d9a407f736
--- /dev/null
+++ b/test/openssl/fixtures/pkey/dsa256.pem
@@ -0,0 +1,8 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIH3AgEAAkEAhk2libbY2a8y2Pt21+YPYGZeW6wzaW2yfj5oiClXro9XMR7XWLkE
+9B7XxLNFCS2gmCCdMsMW1HulaHtLFQmB2wIVAM43JZrcgpu6ajZ01VkLc93gu/Ed
+AkAOhujZrrKV5CzBKutKLb0GVyVWmdC7InoNSMZEeGU72rT96IjM59YzoqmD0pGM
+3I1o4cGqg1D1DfM1rQlnN1eSAkBq6xXfEDwJ1mLNxF6q8Zm/ugFYWR5xcX/3wFiT
+b4+EjHP/DbNh9Vm5wcfnDBJ1zKvrMEf2xqngYdrV/3CiGJeKAhRvL57QvJZcQGvn
+ISNX5cMzFHRW3Q==
+-----END DSA PRIVATE KEY-----
diff --git a/test/openssl/fixtures/pkey/dsa512.pem b/test/openssl/fixtures/pkey/dsa512.pem
new file mode 100644
index 0000000000..962c41cc67
--- /dev/null
+++ b/test/openssl/fixtures/pkey/dsa512.pem
@@ -0,0 +1,8 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIH4AgEAAkEA5lB4GvEwjrsMlGDqGsxrbqeFRh6o9OWt6FgTYiEEHaOYhkIxv0Ok
+RZPDNwOG997mDjBnvDJ1i56OmS3MbTnovwIVAJgub/aDrSDB4DZGH7UyarcaGy6D
+AkB9HdFw/3td8K4l1FZHv7TCZeJ3ZLb7dF3TWoGUP003RCqoji3/lHdKoVdTQNuR
+S/m6DlCwhjRjiQ/lBRgCLCcaAkEAjN891JBjzpMj4bWgsACmMggFf57DS0Ti+5++
+Q1VB8qkJN7rA7/2HrCR3gTsWNb1YhAsnFsoeRscC+LxXoXi9OAIUBG98h4tilg6S
+55jreJD3Se3slps=
+-----END DSA PRIVATE KEY-----
diff --git a/test/openssl/fixtures/pkey/p256.pem b/test/openssl/fixtures/pkey/p256.pem
new file mode 100644
index 0000000000..97c97d9f9d
--- /dev/null
+++ b/test/openssl/fixtures/pkey/p256.pem
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIID49FDqcf1O1eO8saTgG70UbXQw9Fqwseliit2aWhH1oAoGCCqGSM49
+AwEHoUQDQgAEFglk2c+oVUIKQ64eZG9bhLNPWB7lSZ/ArK41eGy5wAzU/0G51Xtt
+CeBUl+MahZtn9fO1JKdF4qJmS39dXnpENg==
+-----END EC PRIVATE KEY-----
diff --git a/test/openssl/fixtures/pkey/rsa-1.pem b/test/openssl/fixtures/pkey/rsa-1.pem
new file mode 100644
index 0000000000..bd5a624f6b
--- /dev/null
+++ b/test/openssl/fixtures/pkey/rsa-1.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJJwIBAAKCAgEArIEJUYZrXhMfUXXdl2gLcXrRB4ciWNEeXt5UVLG0nPhygZwJ
+xis8tOrjXOJEpUXUsfgF35pQiJLD4T9/Vp3zLFtMOOQjOR3AxjIelbH9KPyGFEr9
+TcPtsJ24zhcG7RbwOGXR4iIcDaTx+bCLSAd7BjG3XHQtyeepGGRZkGyGUvXjPorH
+XP+dQjQnMd09wv0GMZSqQ06PedUUKQ4PJRfMCP+mwjFP+rB3NZuThF0CsNmpoixg
+GdoQ591Yrf5rf2Bs848JrYdqJlKlBL6rTFf2glHiC+mE5YRny7RZtv/qIkyUNotV
+ce1cE0GFrRmCpw9bqulDDcgKjFkhihTg4Voq0UYdJ6Alg7Ur4JerKTfyCaRGF27V
+fh/g2A2/6Vu8xKYYwTAwLn+Tvkx9OTVZ1t15wM7Ma8hHowNoO0g/lWkeltgHLMji
+rmeuIYQ20BQmdx2RRgWKl57D0wO/N0HIR+Bm4vcBoNPgMlk9g5WHA6idHR8TLxOr
+dMMmTiWfefB0/FzGXBv7DuuzHN3+urdCvG1QIMFQ06kHXhr4rC28KbWIxg+PJGM8
+oGNEGtGWAOvi4Ov+BVsIdbD5Sfyb4nY3L9qqPl6TxRxMWTKsYCYx11jC8civCzOu
+yL1z+wgIICJ6iGzrfYf6C2BiNV3BC1YCtp2XsG+AooIxCwjL2CP/54MuRnUCAwEA
+AQKCAgAP4+8M0HoRd2d6JIZeDRqIwIyCygLy9Yh7qrVP+/KsRwKdR9dqps73x29c
+Pgeexdj67+Lynw9uFT7v/95mBzTAUESsNO+9sizw1OsWVQgB/4kGU4YT5Ml/bHf6
+nApqSqOkPlTgJM46v4f+vTGHWBEQGAJRBO62250q/wt1D1osSDQ/rZ8BxRYiZBV8
+NWocDRzF8nDgtFrpGSS7R21DuHZ2Gb6twscgS6MfkA49sieuTM6gfr/3gavu/+fM
+V1Rlrmc65GE61++CSjijQEEdTjkJ9isBd+hjEBhTnnBpOBfEQxOgFqOvU/MYXv/G
+W0Q6yWJjUwt3OIcoOImrY5L3j0vERneA1Alweqsbws3fXXMjA+jhLxlJqjPvSAKc
+POi7xu7QCJjSSLAzHSDPdmGmfzlrbdWS1h0mrC5YZYOyToLajfnmAlXNNrytnePg
+JV9/1136ZFrJyEi1JVN3kyrC+1iVd1E+lWK0U1UQ6/25tJvKFc1I+xToaUbK10UN
+ycXib7p2Zsc/+ZMlPRgCxWmpIHmKhnwbO7vtRunnnc6wzhvlQQNHWlIvkyQukV50
+6k/bzWw0M6A98B4oCICIcxcpS3njDlHyL7NlkCD+/OfZp6X3RZF/m4grmA2doebz
+glsaNMyGHFrpHkHq19Y63Y4jtBdW/XuBv06Cnr4r3BXdjEzzwQKCAQEA5bj737Nk
+ZLA0UgzVVvY67MTserTOECIt4i37nULjRQwsSFiz0AWFOBwUCBJ5N2qDEelbf0Fa
+t4VzrphryEgzLz/95ZXi+oxw1liqCHi8iHeU2wSclDtx2jKv2q7bFvFSaH4CKC4N
+zBJNfP92kdXuAjXkbK/jWwr64fLNh/2KFWUAmrYmtGfnOjjyL+yZhPxBatztE58q
+/T61pkvP9NiLfrr7Xq8fnzrwqGERhXKueyoK6ig9ZJPZ2VTykMUUvNYJJ7OYQZru
+EYA3zkuEZifqmjgF57Bgg7dkkIh285TzH3CNf3MCMTmjlWVyHjlyeSPYgISB9Mys
+VKKQth+SvYcChQKCAQEAwDyCcolA7+bQBfECs6GXi7RYy2YSlx562S5vhjSlY9Ko
+WiwVJWviF7uSBdZRnGUKoPv4K4LV34o2lJpSSTi5Xgp7FH986VdGePe3p4hcXSIZ
+NtsKImLVLnEjrmkZExfQl7p0MkcU/LheCf/eEZVp0Z84O54WCs6GRm9wHYIUyrag
+9FREqqxTRVNhQQ2EDVGq1slREdwB+aygE76axK/qosk0RaoLzGZiMn4Sb8bpJxXO
+mee+ftq5bayVltfR0DhC8eHkcPPFeQMll1g+ML7HbINwHTr01ONm3cFUO4zOLBOO
+ws/+vtNfiv6S/lO1RQSRoiApbENBLdSc3V8Cy70PMQKCAQBOcZN4uP5gL5c+KWm0
+T1KhxUDnSdRPyAwY/xC7i7qlullovvlv4GK0XUot03kXBkUJmcEHvF5o6qYtCZlM
+g/MOgHCHtF4Upl5lo1M0n13pz8PB4lpBd+cR1lscdrcTp4Y3bkf4RnmppNpXA7kO
+ZZnnoVWGE620ShSPkWTDuj0rvxisu+SNmClqRUXWPZnSwnzoK9a86443efF3fs3d
+UxCXTuxFUdGfgvXo2XStOBMCtcGSYflM3fv27b4C13mUXhY0O2yTgn8m9LyZsknc
+xGalENpbWmwqrjYl8KOF2+gFZV68FZ67Bm6otkJ4ta80VJw6joT9/eIe6IA34KIw
+G+ktAoIBAFRuPxzvC4ZSaasyX21l25mQbC9pdWDKEkqxCmp3VOyy6R4xnlgBOhwS
+VeAacV2vQyvRfv4dSLIVkkNSRDHEqCWVlNk75TDXFCytIAyE54xAHbLqIVlY7yim
+qHVB07F/FC6PxdkPPziAAU2DA5XVedSHibslg6jbbD4jU6qiJ1+hNrAZEs+jQC+C
+n4Ri20y+Qbp0URb2+icemnARlwgr+3HjzQGL3gK4NQjYNmDBjEWOXl9aWWB90FNL
+KahGwfAhxcVW4W56opCzwR7nsujV4eDXGba83itidRuQfd5pyWOyc1E86TYGwD/b
+79OkEElv6Ea8uXTDVS075GmWATRapQECggEAd9ZAbyT+KouTfi2e6yLOosxSZfns
+eF06QAJi5n9GOtdfK5fqdmHJqJI7wbubCnd0oxPeL71lRjrOAMXufaQRdZtfXSMn
+B1TljteNrh1en5xF451rCPR/Y6tNKBvIKnhy1waO27/vA+ovXrm17iR9rRuGZ29i
+IurlKA6z/96UdrSdpqITTCyTjSOBYg34f49ueGjlpL4+8HJq2wor4Cb1Sbv8ErqA
+bsQ/Jz+KIGUiuFCfNa6d6McPRXIrGgzpprXgfimkV3nj49QyrnuCF/Pc4psGgIaN
+l3EiGXzRt/55K7DQVadtbcjo9zREac8QnDD6dS/gOfJ82L7frQfMpNWgQA==
+-----END RSA PRIVATE KEY-----
diff --git a/test/openssl/fixtures/pkey/rsa-2.pem b/test/openssl/fixtures/pkey/rsa-2.pem
new file mode 100644
index 0000000000..e4fd4f4370
--- /dev/null
+++ b/test/openssl/fixtures/pkey/rsa-2.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEA1HUbx825tG7+/ulC5DpDogzXqM2/KmeCwGXZY4XjiWa+Zj7b
+ECkZwQh7zxFUsPixGqQKJSyFwCogdaPzYTRNtqKKaw/IWS0um1PTn4C4/9atbIsf
+HVKu/fWg4VrZL+ixFIZxa8Z6pvTB2omMcx+uEzbXPsO01i1pHf7MaWBxUDGFyC9P
+lASJBfFZAf2Ar1H99OTS4SP+gxM9Kk5tcc22r8uFiqqbhJmQNSDApdHvT1zSZxAc
+T1BFEZqfmR0B0UegPyJc/9hW0dYpB9JjR29UaZRSta3LUMpqltoOF5bzaKVgMuBm
+Qy79xJ71LjGp8bKhgRaWXyPsDzAC0MQlOW6En0v8LK8fntivJEvw9PNOMcZ8oMTn
+no0NeVt32HiQJW8LIVo7dOLVFtguSBMWUVe8mdKbuIIULD6JlSYke9Ob6andUhzO
+U79m/aRWs2yjD6o5QAktjFBARdPgcpTdWfppc8xpJUkQgRmVhINoIMT9W6Wl898E
+P4aPx6mRV/k05ellN3zRgd9tx5dyNuj3RBaNmR47cAVvGYRQgtH9bQYs6jtf0oer
+A5yIYEKspNRlZZJKKrQdLflQFOEwjQJyZnTk7Mp0y21wOuEGgZBexew55/hUJDC2
+mQ8CqjV4ki/Mm3z6Cw3jXIMNBJkH7oveBGSX0S9bF8A/73oOCU3W/LkORxECAwEA
+AQKCAgBLK7RMmYmfQbaPUtEMF2FesNSNMV72DfHBSUgFYpYDQ4sSeiLgMOqf1fSY
+azVf+F4RYwED7iDUwRMDDKNMPUlR2WjIQKlOhCH9a0dxJAZQ3xA1W3QC2AJ6cLIf
+ihlWTip5bKgszekPsYH1ZL2A7jCVM84ssuoE7cRHjKOelTUCfsMq9TJe2MvyglZP
+0fX6EjSctWm3pxiiH+iAU4d9wJ9my8fQLFUiMYNIiPIguYrGtbzsIlMh7PDDLcZS
+UmUWOxWDwRDOpSjyzadu0Q23dLiVMpmhFoDdcQENptFdn1c4K2tCFQuZscKwEt4F
+HiVXEzD5j5hcyUT4irA0VXImQ+hAH3oSDmn7wyHvyOg0bDZpUZXEHXb83Vvo54/d
+Fb4AOUva1dwhjci8CTEMxCENMy/CLilRv46AeHbOX8KMPM7BnRSJPptvTTh/qB9C
+HI5hxfkO+EOYnu0kUlxhJfrqG86H4IS+zA8HWiSEGxQteMjUQfgJoBzJ94YChpzo
+ePpKSpjxxl1PNNWKxWM3yUvlKmI2lNl6YNC8JpF2wVg4VvYkG7iVjleeRg21ay89
+NCVMF98n3MI5jdzfDKACnuYxg7sw+gjMy8PSoFvQ5pvHuBBOpa8tho6vk7bLJixT
+QY5uXMNQaO6OwpkBssKpnuXhIJzDhO48nSjJ5nUEuadPH1nGwQKCAQEA7twrUIMi
+Vqze/X6VyfEBnX+n3ZyQHLGqUv/ww1ZOOHmSW5ceC4GxHa8EPDjoh9NEjYffwGq9
+bfQh9Gntjk5gFipT/SfPrIhbPt59HthUqVvOGgSErCmn0vhsa0+ROpVi4K2WHS7O
+7SEwnoCWd6p1omon2olVY0ODlMH4neCx/ZuKV8SRMREubABlL8/MLp37AkgKarTY
+tewd0lpaZMvsjOhr1zVCGUUBxy87Fc7OKAcoQY8//0r8VMH7Jlga7F2PKVPzqRKf
+tjeW5jMAuRxTqtEdIeclJZwvUMxvb23BbBE+mtvKpXv69TB3DK8T1YIkhW2CidZW
+lad4MESC+QFNbQKCAQEA47PtULM/0ZFdE+PDDHOa2kJ2arm94sVIqF2168ZLXR69
+NkvCWfjkUPDeejINCx7XQgk0d/+5BCvrJpcM7lE4XfnYVNtPpct1el6eTfaOcPU8
+wAMsnq5n9Mxt02U+XRPtEqGk+lt0KLPDDSG88Z7jPmfftigLyPH6i/ZJyRUETlGk
+rGnWSx/LFUxQU5aBa2jUCjKOKa+OOk2jGg50A5Cmk26v9sA/ksOHisMjfdIpZc9P
+r4R0IteDDD5awlkWTF++5u1GpgU2yav4uan0wzY8OWYFzVyceA6+wffEcoplLm82
+CPd/qJOB5HHkjoM+CJgfumFxlNtdowKvKNUxpoQNtQKCAQEAh3ugofFPp+Q0M4r6
+gWnPZbuDxsLIR05K8vszYEjy4zup1YO4ygQNJ24fM91/n5Mo/jJEqwqgWd6w58ax
+tRclj00BCMXtGMrbHqTqSXWhR9LH66AGdPTHuXWpYZDnKliTlic/z1u+iWhbAHyl
+XEj2omIeKunc4gnod5cyYrKRouz3omLfi/pX33C19FGkWgjH2HpuViowBbhhDfCr
+9yJoEWC/0njl/hlTMdzLYcpEyxWMMuuC/FZXG+hPgWdWFh3XVzTEL3Fd3+hWEkp5
+rYWwu2ITaSiHvHaDrAvZZVXW8WoynXnvzr+tECgmTq57zI4eEwSTl4VY5VfxZ0dl
+FsIzXQKCAQBC07GYd6MJPGJWzgeWhe8yk0Lxu6WRAll6oFYd5kqD/9uELePSSAup
+/actsbbGRrziMpVlinWgVctjvf0bjFbArezhqqPLgtTtnwtS0kOnvzGfIM9dms4D
+uGObISGWa5yuVSZ4G5MRxwA9wGMVfo4u6Iltin868FmZ7iRlkXd8DNYJi95KmgAe
+NhF1FrzQ6ykf/QpgDZfuYI63vPorea6JonieMHn39s622OJ3sNBZguheGL+E4j8h
+vsMgOskijQ8X8xdC7lDQC1qqEsk06ZvvNJQLW1zIl3tArhjHjPp5EEaJhym+Ldx3
+UT3E3Zu9JfhZ2PNevqrShp0lnLw/pI3pAoIBAAUMz5Lj6V9ftsl1pTa8WDFeBJW0
+Wa5AT1BZg/ip2uq2NLPnA5JWcD+v682fRSvIj1pU0DRi6VsXlzhs+1q3+sgqiXGz
+u2ArFylh8TvC1gXUctXKZz/M3Rqr6aSNoejUGLmvHre+ja/k6Zwmu6ePtB7dL50d
+6+xMTYquS4gLbrbSLcEu3iBAAnvRLreXK4KguPxaBdICB7v7epdpAKe3Z7hp/sst
+eJj1+6KRdlcmt8fh5MPkBBXa6I/9XGmX5UEo7q4wAxeM9nuFWY3watz/EO9LiO6P
+LmqUSWL65m4cX0VZPvhYEsHppKi1eoWGlHqS4Af5+aIXi2alu2iljQFeA+Q=
+-----END RSA PRIVATE KEY-----
diff --git a/test/openssl/fixtures/pkey/rsa-3.pem b/test/openssl/fixtures/pkey/rsa-3.pem
new file mode 100644
index 0000000000..6c9c9cedd2
--- /dev/null
+++ b/test/openssl/fixtures/pkey/rsa-3.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAzn+YCcOh7BIRzrb7TEuhQLD545+/Fx/zCYO3l+y/8ogUxMTg
+LG5HrcXlX3JP796ie90/GHIf8/lwczVhP1jk/keYjkwoTYDt477R7KRcJPyGqHRr
+qLp7AnZxtz3JLNboTgO3bAYzlvtsSKU/R3oehBbGHzEWCP2UEYj/Kky0zpcjkhZU
+jiErr9ARPq8+dOGqBf+CE2NLKYC1bu8hZe9AddvvN2SvfMN6uhJtEGZO1k8tScwf
+AyvPJ1Po/6z08pzMAgfBUCE95waAVeYJWIOlnNB4eEievzlXdPB9vEt8OOwtWfQX
+V8xyMsoKeAW05s413E0eTYx1aulFXdWwG2mWEBRtNzKF1iBudlg1a3x1zThWi1pY
+jW5vROvoWZMCbl9bYQ/LxOCVqDoUl86+NPEGeuESMzm5NvOQA2e0Ty5wphnt9M19
+Wcc8neBhb6iCGqYzxWNvUYXZWUv1+/MrPHKyJuv7MSivwtctfp8SacUGxkd6T+u6
+V6ntHf3qtN/5pAmni6nzUTgjC65MS0LEhi/RTzwafkIfifeJH7/LqFtjrursuwua
++p9lkACck/J5TpzaAfLroFQuepP8qgeq1cpD5Iii56IJ+FPSnkvesHuRUmZIkhtR
+VVsVqMaNPv/Uzc02bOaRXWP4auUY91mDKx/FDmORa9YCDQxMkKke05SWQ90CAwEA
+AQKCAgA0+B/c6VTgxGXS+7cMhB3yBTOkgva2jNh/6Uyv6Of345ZIPyQt4X/7gFbt
+G9qLcjWFxmQH9kZiA+snclrmr/vVijIE1l5EOz1KfUlGBYcpaal1DqALIQKqyA01
+buDq4pmmYWesiw6yvP2yyMipohav1VOu7p1zYvCXaufhRtneYICcWaQI7VNSfvHd
+fYBs5PIDJd6M8Jx4Ie7obOjJSAzl7qu3LtmhDFev4Ugeu8+fQ6IfWv/dhWBW+zw6
+UXhnv3bJUonw7wX8+/rxjdd54BMcXZF5cU9fR+s6MPJf2ZEc3OBpQaa3O9dTVeZH
+kVctGVpRj2qlg9EewoWro0PQVE5Mjah+mdFhPAHWoGl1xht6xJmg0uHYxMCzbUSz
+7NSS3knR0qieFvsp5ESY72i7DnQsbhbn6mTuYdVtm9bphxifAWCP3jFdft/bjtSF
+4yuPI7Qga+3m0B8QhtbWhEzPVon6NyiY7qfa6qllp0opEbw2hE22uGFFNJo2mpPa
+pe9VwARtD0IyfeklE7KrBEwV8NjTaAipZTZODw0w/dt4K3dOiePDl3pPWjmERpVg
+Lkw7XSCMtu5X87I1BbfOYbQhOXksPY+W9Asf6ETBeIZ8bD6Iypuk2ssool1lukqv
+yq1Y8gbR9B2x91ftYwXgzqBSvd8PFNsaXWLD3nrai2G1vb81lQKCAQEA6W02eZcN
+7wJfkqNokcuqhc5OKXH14gVIRV+KocG6f3vg88wrCg5J2GqNhBFuwVrafJjRenm6
+C8zWdneeyrl6cztgbaySw7kXnqFdTBiuOT8bhiG5NTPjDQ109EucaTbZU9KUXk6k
+ChPlr4G6IPrONpvi/9BvDDZLZkwR6uIg1kFWBy9kZaxFUEIug02hrbkTpPtnEUrO
+r3nG0QL/D0vf+bm4YHIVRMH2O2ZTTWexMw9XlfCe1+WjbJ+PS35QRCRDcRdWHXDb
+HnIFIAajtH5LtaJLgWUYq3B25WkQYtbHmFkm94sp/G4trb8JIJGzVO8cj9t6KeAT
+LG+tk8OqplqsYwKCAQEA4ne81KXx8VNwsKVFqwmiDIoi1q3beNa2hoXdzAMrnYdj
+iLxbfCVgrKPav9hdfXPBncHaNlGsd2G5W1a1UsOr128lTdfBsgm1RVPhVMKvo3fl
+yUnWajtAR1q3tVEUhuFlbJ/RHEtxJaGrzudYCPWQiYhydpDgSckbxD8PuElEgFBX
+O91vnWZEjMsxrABWiZNBxmtBUEv+fjUU/9USYzO4sN79UeD1+ZuBxPFwscsRcjLr
+bPgZWOwiywH6UmQ+DJTzeu0wJ6jgPoy/pgEujsbPDz1wNos6NhA/RQv31QeX33/B
+7/F5XKNmbJ2AFb/B+xTaTQPg0pjT5Exm+HrNU5OivwKCAQEAsLLVi9FG4OiBBHXi
+UItFuChljoYPxVqOTMV4Id6OmLZjoOmqouASElsGaTTxDDkEL1FXMUk4Bnq21dLT
+R06EXPpTknISX0qbkJ9CCrqcGAWnhi+9DYMLmvPW1p7t9c9pUESVv5X0IxTQx7yB
+8zkoJLp4aYGUrj/jb7qhzZYDmWy3/JRpgXWYupp+rzJy8xiowDj22mYwczDRyaJl
+BWVAVL+7zHZPl07kYC6jXHLj9mzktkIBXBkfTriyNkmV5R82VkN+Eqc9l5xkOMwN
+3DHGieYjFf47YHuv5RVVLBy91puWHckgrU+SEHYOKLNidybSDivsHArdOMQJN1Pk
+uCznVQKCAQAYY7DQbfa6eLQAMixomSb8lrvdxueGAgmyPyR93jGKS5Rqm2521ket
+EBB07MZUxmyposDvbKhYSwv9TD9G5I/TKcMouP3BQM5m4vu3dygXQMhcfzk6Q5tO
+k/SI8Gx3gjq8EhIhK/bJiLnKFJwkit3AEhPRtRSSnbgB0JDO1gUslHpwlg55MxRa
+3V9CGN84/cTtq4tjLGwCB5F1Y+sRB/byBXHeqY2UDi1Rmnb6jtYYKGe2WpnQO84b
+cuEUknskO75lFLpE6ykLU3koVaQ/+CVAjOtS1He2btWBiCJurNysU0P9pVHeqjJT
+rDqpHPe1JK/F74783zyir5+/Tuph/9pdAoIBAANPdFRQkJVH8K6iuhxQk6vFqiYB
+MUxpIVeLonD0p9TgMdezVNESht/AIutc0+5wabM45XuDWFRTuonvcE8lckv2Ux3a
+AvSsamjuesxw2YmkEtzZouVqDU0+oxppQJiwBG3MiaHX9F5IfnK6YmQ6xPwZ6MXi
+9feq1jR4KOc1ZrHtRMNgjnBWEFWroGe3FHgV7O133hpMSshRFmwcbE0nAaDr82U9
+sl8dclDjEKBxaqjAeNajOr+BU0w0AAwWXL7dt/ctG2QClcj9wqbEfsXnOR10h4AI
+rqkcvQrOLbTwcrOD/6R1rQfQXtEHKf1maThxosootAQZXdf6jxU3oonx3tU=
+-----END RSA PRIVATE KEY-----
diff --git a/test/openssl/fixtures/pkey/rsa1024.pem b/test/openssl/fixtures/pkey/rsa1024.pem
new file mode 100644
index 0000000000..464de074be
--- /dev/null
+++ b/test/openssl/fixtures/pkey/rsa1024.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7Cx
+aKPERYHsk4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/
+Q3geLv8ZD9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQAB
+AoGBAKSl/MQarye1yOysqX6P8fDFQt68VvtXkNmlSiKOGuzyho0M+UVSFcs6k1L0
+maDE25AMZUiGzuWHyaU55d7RXDgeskDMakD1v6ZejYtxJkSXbETOTLDwUWTn618T
+gnb17tU1jktUtU67xK/08i/XodlgnQhs6VoHTuCh3Hu77O6RAkEA7+gxqBuZR572
+74/akiW/SuXm0SXPEviyO1MuSRwtI87B02D0qgV8D1UHRm4AhMnJ8MCs1809kMQE
+JiQUCrp9mQJBANlt2ngBO14us6NnhuAseFDTBzCHXwUUu1YKHpMMmxpnGqaldGgX
+sOZB3lgJsT9VlGf3YGYdkLTNVbogQKlKpB8CQQDiSwkb4vyQfDe8/NpU5Not0fII
+8jsDUCb+opWUTMmfbxWRR3FBNu8wnym/m19N4fFj8LqYzHX4KY0oVPu6qvJxAkEA
+wa5snNekFcqONLIE4G5cosrIrb74sqL8GbGb+KuTAprzj5z1K8Bm0UW9lTjVDjDi
+qRYgZfZSL+x1P/54+xTFSwJAY1FxA/N3QPCXCjPh5YqFxAMQs2VVYTfg+t0MEcJD
+dPMQD5JX6g5HKnHFg2mZtoXQrWmJSn7p8GJK8yNTopEErA==
+-----END RSA PRIVATE KEY-----
diff --git a/test/openssl/fixtures/pkey/rsa2048.pem b/test/openssl/fixtures/pkey/rsa2048.pem
new file mode 100644
index 0000000000..ac89cd88ed
--- /dev/null
+++ b/test/openssl/fixtures/pkey/rsa2048.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAuV9ht9J7k4NBs38jOXvvTKY9gW8nLICSno5EETR1cuF7i4pN
+s9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enenfzq/t/e/1IRW0wkJUJUFQign
+4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWmqbjs07JbuS4QQGGXLc+Su96D
+kYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v68JkRFIhdGlb6JL8fllf/A/bl
+NwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX9KZYcU00mOX+fdxOSnGqS/8J
+DRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wIDAQABAoIBAAzsamqfYQAqwXTb
+I0CJtGg6msUgU7HVkOM+9d3hM2L791oGHV6xBAdpXW2H8LgvZHJ8eOeSghR8+dgq
+PIqAffo4x1Oma+FOg3A0fb0evyiACyrOk+EcBdbBeLo/LcvahBtqnDfiUMQTpy6V
+seSoFCwuN91TSCeGIsDpRjbG1vxZgtx+uI+oH5+ytqJOmfCksRDCkMglGkzyfcl0
+Xc5CUhIJ0my53xijEUQl19rtWdMnNnnkdbG8PT3LZlOta5Do86BElzUYka0C6dUc
+VsBDQ0Nup0P6rEQgy7tephHoRlUGTYamsajGJaAo1F3IQVIrRSuagi7+YpSpCqsW
+wORqorkCgYEA7RdX6MDVrbw7LePnhyuaqTiMK+055/R1TqhB1JvvxJ1CXk2rDL6G
+0TLHQ7oGofd5LYiemg4ZVtWdJe43BPZlVgT6lvL/iGo8JnrncB9Da6L7nrq/+Rvj
+XGjf1qODCK+LmreZWEsaLPURIoR/Ewwxb9J2zd0CaMjeTwafJo1CZvcCgYEAyCgb
+aqoWvUecX8VvARfuA593Lsi50t4MEArnOXXcd1RnXoZWhbx5rgO8/ATKfXr0BK/n
+h2GF9PfKzHFm/4V6e82OL7gu/kLy2u9bXN74vOvWFL5NOrOKPM7Kg+9I131kNYOw
+Ivnr/VtHE5s0dY7JChYWE1F3vArrOw3T00a4CXUCgYEA0SqY+dS2LvIzW4cHCe9k
+IQqsT0yYm5TFsUEr4sA3xcPfe4cV8sZb9k/QEGYb1+SWWZ+AHPV3UW5fl8kTbSNb
+v4ng8i8rVVQ0ANbJO9e5CUrepein2MPL0AkOATR8M7t7dGGpvYV0cFk8ZrFx0oId
+U0PgYDotF/iueBWlbsOM430CgYEAqYI95dFyPI5/AiSkY5queeb8+mQH62sdcCCr
+vd/w/CZA/K5sbAo4SoTj8dLk4evU6HtIa0DOP63y071eaxvRpTNqLUOgmLh+D6gS
+Cc7TfLuFrD+WDBatBd5jZ+SoHccVrLR/4L8jeodo5FPW05A+9gnKXEXsTxY4LOUC
+9bS4e1kCgYAqVXZh63JsMwoaxCYmQ66eJojKa47VNrOeIZDZvd2BPVf30glBOT41
+gBoDG3WMPZoQj9pb7uMcrnvs4APj2FIhMU8U15LcPAj59cD6S6rWnAxO8NFK7HQG
+4Jxg3JNNf8ErQoCHb1B3oVdXJkmbJkARoDpBKmTCgKtP8ADYLmVPQw==
+-----END RSA PRIVATE KEY-----
diff --git a/test/openssl/test_asn1.rb b/test/openssl/test_asn1.rb
index f226da5c66..cc11301804 100644
--- a/test/openssl/test_asn1.rb
+++ b/test/openssl/test_asn1.rb
@@ -1,10 +1,12 @@
# frozen_string_literal: false
require_relative 'utils'
+if defined?(OpenSSL)
+
class OpenSSL::TestASN1 < OpenSSL::TestCase
- def test_decode
+ def test_decode_x509_certificate
subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA")
- key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ key = Fixtures.pkey("rsa1024")
now = Time.at(Time.now.to_i) # suppress usec
s = 0xdeadbeafdeadbeafdeadbeafdeadbeaf
exts = [
@@ -14,7 +16,7 @@ class OpenSSL::TestASN1 < OpenSSL::TestCase
]
dgst = OpenSSL::Digest::SHA1.new
cert = OpenSSL::TestUtils.issue_cert(
- subj, key, s, now, now+3600, exts, nil, nil, dgst)
+ subj, key, s, exts, nil, nil, digest: dgst, not_before: now, not_after: now+3600)
asn1 = OpenSSL::ASN1.decode(cert)
@@ -128,9 +130,9 @@ class OpenSSL::TestASN1 < OpenSSL::TestCase
assert_equal(OpenSSL::ASN1::Sequence, spkey.class)
assert_equal(2, spkey.value.size)
assert_equal(OpenSSL::ASN1::Integer, spkey.value[0].class)
- assert_equal(143085709396403084580358323862163416700436550432664688288860593156058579474547937626086626045206357324274536445865308750491138538454154232826011964045825759324933943290377903384882276841880081931690695505836279972214003660451338124170055999155993192881685495391496854691199517389593073052473319331505702779271, spkey.value[0].value)
+ assert_equal(cert.public_key.n, spkey.value[0].value)
assert_equal(OpenSSL::ASN1::Integer, spkey.value[1].class)
- assert_equal(65537, spkey.value[1].value)
+ assert_equal(cert.public_key.e, spkey.value[1].value)
extensions = tbs_cert.value[7]
assert_equal(:CONTEXT_SPECIFIC, extensions.tag_class)
@@ -191,71 +193,8 @@ class OpenSSL::TestASN1 < OpenSSL::TestCase
assert_equal(cululated_sig, sig_val.value)
end
- def test_encode_boolean
- encode_decode_test(OpenSSL::ASN1::Boolean, [true, false])
- end
-
- def test_encode_integer
- encode_decode_test(OpenSSL::ASN1::Integer, [72, -127, -128, 128, -1, 0, 1, -(2**12345), 2**12345])
- end
-
- def test_encode_nil
- m = OpenSSL::ASN1
- [
- m::Boolean, m::Integer, m::BitString, m::OctetString,
- m::ObjectId, m::Enumerated, m::UTF8String, m::UTCTime,
- m::GeneralizedTime, m::Sequence, m::Set
- ].each do |klass|
- #Primitives raise TypeError, Constructives NoMethodError
- assert_raise(TypeError, NoMethodError) { klass.send(:new, nil).to_der }
- end
- end
-
- def encode_decode_test(type, values)
- values.each do |v|
- assert_equal(v, OpenSSL::ASN1.decode(type.new(v).to_der).value)
- end
- end
-
- def test_decode_pem #should fail gracefully (cf. [ruby-dev:44542])
- pem = <<-_EOS_
------BEGIN CERTIFICATE-----
-MIIC8zCCAdugAwIBAgIBATANBgkqhkiG9w0BAQUFADA9MRMwEQYKCZImiZPyLGQB
-GRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMQswCQYDVQQDDAJDQTAe
-Fw0xMTA5MjUxMzQ4MjZaFw0xMTA5MjUxNDQ4MjZaMD0xEzARBgoJkiaJk/IsZAEZ
-FgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxCzAJBgNVBAMMAkNBMIIB
-IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuV9ht9J7k4NBs38jOXvvTKY9
-gW8nLICSno5EETR1cuF7i4pNs9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enen
-fzq/t/e/1IRW0wkJUJUFQign4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWm
-qbjs07JbuS4QQGGXLc+Su96DkYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v6
-8JkRFIhdGlb6JL8fllf/A/blNwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX
-9KZYcU00mOX+fdxOSnGqS/8JDRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wID
-AQABMA0GCSqGSIb3DQEBBQUAA4IBAQAiAtrIr1pLX4GYN5klviWKb8HC9ICYuAFI
-NfE3FwqzErEVXotuMe3yPVyB3Bv6rjYY/x5EtS5+WPTbHlvHZTkfcsnTpizcn4mW
-dJ6dDRaFCHt1YKKjUxqBt9lvvrc3nReYZN/P+s1mrDhWzGf8iPZgf8sFUHgnaK7W
-CXRVXmPFgCDRNpDDVQ0MQkr509yYfTH+dujNzqTCwSvkyZFyQ7Oe8Yj0VR6kquG3
-rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm
-/93PnPG1IvPjYNd5VlV+sXSnaxQn974HRCsMv7jA8BD6IgSaX6WK
------END CERTIFICATE-----
- _EOS_
- assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1.decode(pem) }
- assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1.decode_all(pem) }
- end
-
- def test_primitive_cannot_set_infinite_length
- begin
- prim = OpenSSL::ASN1::Integer.new(50)
- assert_equal(false, prim.infinite_length)
- prim.infinite_length = true
- flunk('Could set infinite length on primitive value')
- rescue NoMethodError
- #ok
- end
- end
-
def test_decode_all
- expected = %w{ 02 01 01 02 01 02 02 01 03 }
- raw = [expected.join('')].pack('H*')
+ raw = B(%w{ 02 01 01 02 01 02 02 01 03 })
ary = OpenSSL::ASN1.decode_all(raw)
assert_equal(3, ary.size)
ary.each_with_index do |asn1, i|
@@ -264,354 +203,485 @@ rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm
end
end
- def test_decode_utctime
- expected = Time.at 1374535380
- assert_equal expected, OpenSSL::ASN1.decode("\x17\v1307222323Z").value
-
- expected += 17
- assert_equal expected, OpenSSL::ASN1.decode("\x17\r130722232317Z").value
- end
-
- def test_encode_utctime_2k38
- encoded = OpenSSL::ASN1::UTCTime(2 ** 31 - 1).to_der
- assert_equal 2 ** 31 - 1, OpenSSL::ASN1.decode(encoded).value.to_i
-
- encoded = OpenSSL::ASN1::UTCTime(2 ** 31).to_der
- assert_equal 2 ** 31, OpenSSL::ASN1.decode(encoded).value.to_i
- end
-
- def test_decode_enumerated
- encoded = OpenSSL::ASN1.Enumerated(0).to_der
- assert_equal "\x0a\x01\x00".b, encoded
- assert_equal encoded, OpenSSL::ASN1.decode(encoded).to_der
- end
-
- def test_create_inf_length_primitive
- expected = %w{ 24 80 04 01 61 00 00 }
- raw = [expected.join('')].pack('H*')
- val = OpenSSL::ASN1::OctetString.new('a')
- cons = OpenSSL::ASN1::Constructive.new([val,
- OpenSSL::ASN1::EndOfContent.new],
- OpenSSL::ASN1::OCTET_STRING,
- nil,
- :UNIVERSAL)
- cons.infinite_length = true
- assert_equal(nil, cons.tagging)
- assert_equal(raw, cons.to_der)
- asn1 = OpenSSL::ASN1.decode(raw)
- assert(asn1.infinite_length)
- assert_equal(raw, asn1.to_der)
- end
-
- def test_cons_without_inf_length_forbidden
- assert_raise(OpenSSL::ASN1::ASN1Error) do
- val = OpenSSL::ASN1::OctetString.new('a')
- cons = OpenSSL::ASN1::Constructive.new([val],
- OpenSSL::ASN1::OCTET_STRING,
- nil,
- :UNIVERSAL)
- cons.to_der
- end
- end
-
- def test_cons_without_array_forbidden
- assert_raise(OpenSSL::ASN1::ASN1Error) do
- val = OpenSSL::ASN1::OctetString.new('a')
- cons = OpenSSL::ASN1::Constructive.new(val,
- OpenSSL::ASN1::OCTET_STRING,
- nil,
- :UNIVERSAL)
- cons.infinite_length = true
- cons.to_der
- end
- end
-
- def test_parse_empty_sequence
- expected = %w{ A0 07 30 02 30 00 02 01 00 }
- raw = [expected.join('')].pack('H*')
- asn1 = OpenSSL::ASN1.decode(raw)
- assert_equal(raw, asn1.to_der)
- assert_equal(2, asn1.value.size)
- seq = asn1.value[0]
- assert_equal(1, seq.value.size)
- inner_seq = seq.value[0]
- assert_equal(0, inner_seq.value.size)
- end
-
- def test_parse_tagged_0_infinite
- expected = %w{ 30 80 02 01 01 80 01 02 00 00 }
- raw = [expected.join('')].pack('H*')
- asn1 = OpenSSL::ASN1.decode(raw)
- assert_equal(3, asn1.value.size)
- int = asn1.value[0]
- assert_universal(OpenSSL::ASN1::INTEGER, int)
- tagged = asn1.value[1]
- assert_equal(0, tagged.tag)
- assert_universal(OpenSSL::ASN1::EOC, asn1.value[2])
- assert_equal(raw, asn1.to_der)
- end
-
- def test_seq_infinite_length
- begin
- content = [ OpenSSL::ASN1::Null.new(nil),
- OpenSSL::ASN1::EndOfContent.new ]
- cons = OpenSSL::ASN1::Sequence.new(content)
- cons.infinite_length = true
- expected = %w{ 30 80 05 00 00 00 }
- raw = [expected.join('')].pack('H*')
- assert_equal(raw, cons.to_der)
- assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
- end
- end
+ def test_object_id_register
+ oid = "1.2.34.56789"
+ pend "OID 1.2.34.56789 is already registered" if OpenSSL::ASN1::ObjectId(oid).sn
+ assert_equal true, OpenSSL::ASN1::ObjectId.register(oid, "ossl-test-sn", "ossl-test-ln")
+ obj = OpenSSL::ASN1::ObjectId(oid)
+ assert_equal oid, obj.oid
+ assert_equal "ossl-test-sn", obj.sn
+ assert_equal "ossl-test-ln", obj.ln
+ obj = encode_decode_test B(%w{ 06 05 2A 22 83 BB 55 }), OpenSSL::ASN1::ObjectId("ossl-test-ln")
+ assert_equal "ossl-test-sn", obj.value
+ end
+
+ def test_end_of_content
+ encode_decode_test B(%w{ 00 00 }), OpenSSL::ASN1::EndOfContent.new
+ assert_raise(OpenSSL::ASN1::ASN1Error) {
+ OpenSSL::ASN1.decode(B(%w{ 00 01 00 }))
+ }
+ end
+
+ def test_boolean
+ encode_decode_test B(%w{ 01 01 00 }), OpenSSL::ASN1::Boolean.new(false)
+ encode_decode_test B(%w{ 01 01 FF }), OpenSSL::ASN1::Boolean.new(true)
+ decode_test B(%w{ 01 01 01 }), OpenSSL::ASN1::Boolean.new(true)
+ assert_raise(OpenSSL::ASN1::ASN1Error) {
+ OpenSSL::ASN1.decode(B(%w{ 01 02 00 00 }))
+ }
+ end
+
+ def test_integer
+ encode_decode_test B(%w{ 02 01 00 }), OpenSSL::ASN1::Integer.new(0)
+ encode_decode_test B(%w{ 02 01 48 }), OpenSSL::ASN1::Integer.new(72)
+ encode_decode_test B(%w{ 02 02 00 80 }), OpenSSL::ASN1::Integer.new(128)
+ encode_decode_test B(%w{ 02 01 81 }), OpenSSL::ASN1::Integer.new(-127)
+ encode_decode_test B(%w{ 02 01 80 }), OpenSSL::ASN1::Integer.new(-128)
+ encode_decode_test B(%w{ 02 01 FF }), OpenSSL::ASN1::Integer.new(-1)
+ encode_decode_test B(%w{ 02 09 01 00 00 00 00 00 00 00 00 }), OpenSSL::ASN1::Integer.new(2 ** 64)
+ encode_decode_test B(%w{ 02 09 FF 00 00 00 00 00 00 00 00 }), OpenSSL::ASN1::Integer.new(-(2 ** 64))
+ # FIXME: OpenSSL < 1.1.0 does not fail
+ # assert_raise(OpenSSL::ASN1::ASN1Error) {
+ # OpenSSL::ASN1.decode(B(%w{ 02 02 00 7F }))
+ # }
+ # assert_raise(OpenSSL::ASN1::ASN1Error) {
+ # OpenSSL::ASN1.decode(B(%w{ 02 02 FF 80 }))
+ # }
+ end
+
+ def test_enumerated
+ encode_decode_test B(%w{ 0A 01 00 }), OpenSSL::ASN1::Enumerated.new(0)
+ encode_decode_test B(%w{ 0A 01 48 }), OpenSSL::ASN1::Enumerated.new(72)
+ encode_decode_test B(%w{ 0A 02 00 80 }), OpenSSL::ASN1::Enumerated.new(128)
+ encode_decode_test B(%w{ 0A 09 01 00 00 00 00 00 00 00 00 }), OpenSSL::ASN1::Enumerated.new(2 ** 64)
+ end
+
+ def test_bitstring
+ encode_decode_test B(%w{ 03 01 00 }), OpenSSL::ASN1::BitString.new(B(%w{}))
+ encode_decode_test B(%w{ 03 02 00 01 }), OpenSSL::ASN1::BitString.new(B(%w{ 01 }))
+ obj = OpenSSL::ASN1::BitString.new(B(%w{ F0 }))
+ obj.unused_bits = 4
+ encode_decode_test B(%w{ 03 02 04 F0 }), obj
+ assert_raise(OpenSSL::ASN1::ASN1Error) {
+ OpenSSL::ASN1.decode(B(%w{ 03 00 }))
+ }
+ # OpenSSL < OpenSSL_1_0_1k and LibreSSL ignore the error
+ # assert_raise(OpenSSL::ASN1::ASN1Error) {
+ # OpenSSL::ASN1.decode(B(%w{ 03 03 08 FF 00 }))
+ # }
+ # OpenSSL does not seem to prohibit this, though X.690 8.6.2.3 (15/08) does
+ # assert_raise(OpenSSL::ASN1::ASN1Error) {
+ # OpenSSL::ASN1.decode(B(%w{ 03 01 04 }))
+ # }
+ assert_raise(OpenSSL::ASN1::ASN1Error) {
+ obj = OpenSSL::ASN1::BitString.new(B(%w{ FF FF }))
+ obj.unused_bits = 8
+ obj.to_der
+ }
+ end
+
+ def test_string_basic
+ test = -> (tag, klass) {
+ encode_decode_test tag.chr + B(%w{ 00 }), klass.new(B(%w{}))
+ encode_decode_test tag.chr + B(%w{ 02 00 01 }), klass.new(B(%w{ 00 01 }))
+ }
+ test.(4, OpenSSL::ASN1::OctetString)
+ test.(12, OpenSSL::ASN1::UTF8String)
+ test.(18, OpenSSL::ASN1::NumericString)
+ test.(19, OpenSSL::ASN1::PrintableString)
+ test.(20, OpenSSL::ASN1::T61String)
+ test.(21, OpenSSL::ASN1::VideotexString)
+ test.(22, OpenSSL::ASN1::IA5String)
+ test.(25, OpenSSL::ASN1::GraphicString)
+ test.(26, OpenSSL::ASN1::ISO64String)
+ test.(27, OpenSSL::ASN1::GeneralString)
+ test.(28, OpenSSL::ASN1::UniversalString)
+ test.(30, OpenSSL::ASN1::BMPString)
+ end
+
+ def test_null
+ encode_decode_test B(%w{ 05 00 }), OpenSSL::ASN1::Null.new(nil)
+ assert_raise(OpenSSL::ASN1::ASN1Error) {
+ OpenSSL::ASN1.decode(B(%w{ 05 01 00 }))
+ }
+ end
+
+ def test_object_identifier
+ encode_decode_test B(%w{ 06 01 00 }), OpenSSL::ASN1::ObjectId.new("0.0".b)
+ encode_decode_test B(%w{ 06 01 28 }), OpenSSL::ASN1::ObjectId.new("1.0".b)
+ encode_decode_test B(%w{ 06 03 88 37 03 }), OpenSSL::ASN1::ObjectId.new("2.999.3".b)
+ encode_decode_test B(%w{ 06 05 2A 22 83 BB 55 }), OpenSSL::ASN1::ObjectId.new("1.2.34.56789".b)
+ obj = encode_decode_test B(%w{ 06 09 60 86 48 01 65 03 04 02 01 }), OpenSSL::ASN1::ObjectId.new("sha256")
+ assert_equal "2.16.840.1.101.3.4.2.1", obj.oid
+ assert_equal "SHA256", obj.sn
+ assert_equal "sha256", obj.ln
+ assert_raise(OpenSSL::ASN1::ASN1Error) {
+ OpenSSL::ASN1.decode(B(%w{ 06 00 }))
+ }
+ assert_raise(OpenSSL::ASN1::ASN1Error) {
+ OpenSSL::ASN1.decode(B(%w{ 06 01 80 }))
+ }
+ assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1::ObjectId.new("3.0".b).to_der }
+ assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1::ObjectId.new("0.40".b).to_der }
- def test_set_infinite_length
begin
- content = [ OpenSSL::ASN1::Null.new(nil),
- OpenSSL::ASN1::EndOfContent.new() ]
- cons = OpenSSL::ASN1::Set.new(content)
- cons.infinite_length = true
- expected = %w{ 31 80 05 00 00 00 }
- raw = [expected.join('')].pack('H*')
- assert_equal(raw, cons.to_der)
- assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+ oid = (0...100).to_a.join(".").b
+ obj = OpenSSL::ASN1::ObjectId.new(oid)
+ assert_equal oid, obj.oid
+ rescue OpenSSL::ASN1::ASN1Error
+ pend "OBJ_obj2txt() not working (LibreSSL?)" if $!.message =~ /OBJ_obj2txt/
+ raise
end
end
- def test_octet_string_infinite_length
+ def test_sequence
+ encode_decode_test B(%w{ 30 00 }), OpenSSL::ASN1::Sequence.new([])
+ encode_decode_test B(%w{ 30 07 05 00 30 00 04 01 00 }), OpenSSL::ASN1::Sequence.new([
+ OpenSSL::ASN1::Null.new(nil),
+ OpenSSL::ASN1::Sequence.new([]),
+ OpenSSL::ASN1::OctetString.new(B(%w{ 00 }))
+ ])
+
+ expected = OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::OctetString.new(B(%w{ 00 }))])
+ expected.indefinite_length = true
+ encode_decode_test B(%w{ 30 80 04 01 00 00 00 }), expected
+
+ # OpenSSL::ASN1::EndOfContent can only be at the end
+ obj = OpenSSL::ASN1::Sequence.new([
+ OpenSSL::ASN1::EndOfContent.new,
+ OpenSSL::ASN1::OctetString.new(B(%w{ 00 })),
+ OpenSSL::ASN1::EndOfContent.new,
+ ])
+ obj.indefinite_length = true
+ assert_raise(OpenSSL::ASN1::ASN1Error) { obj.to_der }
+
+ # The last EOC in value is ignored if indefinite length form is used
+ expected = OpenSSL::ASN1::Sequence.new([
+ OpenSSL::ASN1::OctetString.new(B(%w{ 00 })),
+ OpenSSL::ASN1::EndOfContent.new
+ ])
+ expected.indefinite_length = true
+ encode_test B(%w{ 30 80 04 01 00 00 00 }), expected
+ end
+
+ def test_set
+ encode_decode_test B(%w{ 31 00 }), OpenSSL::ASN1::Set.new([])
+ encode_decode_test B(%w{ 31 07 05 00 30 00 04 01 00 }), OpenSSL::ASN1::Set.new([
+ OpenSSL::ASN1::Null.new(nil),
+ OpenSSL::ASN1::Sequence.new([]),
+ OpenSSL::ASN1::OctetString.new(B(%w{ 00 }))
+ ])
+ expected = OpenSSL::ASN1::Set.new([OpenSSL::ASN1::OctetString.new(B(%w{ 00 }))])
+ expected.indefinite_length = true
+ encode_decode_test B(%w{ 31 80 04 01 00 00 00 }), expected
+ end
+
+ def test_utctime
+ encode_decode_test B(%w{ 17 0D }) + "160908234339Z".b,
+ OpenSSL::ASN1::UTCTime.new(Time.utc(2016, 9, 8, 23, 43, 39))
+ # Seconds is omitted
+ decode_test B(%w{ 17 0B }) + "1609082343Z".b,
+ OpenSSL::ASN1::UTCTime.new(Time.utc(2016, 9, 8, 23, 43, 0))
begin
- octets = [ OpenSSL::ASN1::OctetString.new('aaa'),
- OpenSSL::ASN1::EndOfContent.new() ]
- cons = OpenSSL::ASN1::Constructive.new(
- octets,
- OpenSSL::ASN1::OCTET_STRING,
- nil,
- :UNIVERSAL)
- cons.infinite_length = true
- expected = %w{ 24 80 04 03 61 61 61 00 00 }
- raw = [expected.join('')].pack('H*')
- assert_equal(raw, cons.to_der)
- assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+ # possible range of UTCTime is 1969-2068 currently
+ encode_decode_test B(%w{ 17 0D }) + "690908234339Z".b,
+ OpenSSL::ASN1::UTCTime.new(Time.utc(1969, 9, 8, 23, 43, 39))
+ rescue OpenSSL::ASN1::ASN1Error
+ pend "No negative time_t support?"
end
+ # not implemented
+ # decode_test B(%w{ 17 11 }) + "500908234339+0930".b,
+ # OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 39, "+09:30"))
+ # decode_test B(%w{ 17 0F }) + "5009082343-0930".b,
+ # OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 0, "-09:30"))
+ # assert_raise(OpenSSL::ASN1::ASN1Error) {
+ # OpenSSL::ASN1.decode(B(%w{ 17 0C }) + "500908234339".b)
+ # }
+ # assert_raise(OpenSSL::ASN1::ASN1Error) {
+ # OpenSSL::ASN1.decode(B(%w{ 17 0D }) + "500908234339Y".b)
+ # }
+ end
+
+ def test_generalizedtime
+ encode_decode_test B(%w{ 18 0F }) + "20161208193429Z".b,
+ OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 29))
+ encode_decode_test B(%w{ 18 0F }) + "99990908234339Z".b,
+ OpenSSL::ASN1::GeneralizedTime.new(Time.utc(9999, 9, 8, 23, 43, 39))
+ decode_test B(%w{ 18 0D }) + "201612081934Z".b,
+ OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 0))
+ # not implemented
+ # decode_test B(%w{ 18 13 }) + "20161208193439+0930".b,
+ # OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 39, "+09:30"))
+ # decode_test B(%w{ 18 11 }) + "201612081934-0930".b,
+ # OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:30"))
+ # decode_test B(%w{ 18 11 }) + "201612081934-09".b,
+ # OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:00"))
+ # decode_test B(%w{ 18 0D }) + "2016120819.5Z".b,
+ # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0))
+ # decode_test B(%w{ 18 0D }) + "2016120819,5Z".b,
+ # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0))
+ # decode_test B(%w{ 18 0F }) + "201612081934.5Z".b,
+ # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 30))
+ # decode_test B(%w{ 18 11 }) + "20161208193439.5Z".b,
+ # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 39.5))
+ # assert_raise(OpenSSL::ASN1::ASN1Error) {
+ # OpenSSL::ASN1.decode(B(%w{ 18 0D }) + "201612081934Y".b)
+ # }
+ end
+
+ def test_basic_asn1data
+ encode_test B(%w{ 00 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 0, :UNIVERSAL)
+ encode_test B(%w{ 01 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 1, :UNIVERSAL)
+ encode_decode_test B(%w{ 41 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 1, :APPLICATION)
+ encode_decode_test B(%w{ 81 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 1, :CONTEXT_SPECIFIC)
+ encode_decode_test B(%w{ C1 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 1, :PRIVATE)
+ encode_decode_test B(%w{ 1F 20 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 32, :UNIVERSAL)
+ encode_decode_test B(%w{ 1F C0 20 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 8224, :UNIVERSAL)
+ encode_decode_test B(%w{ 41 02 AB CD }), OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD }), 1, :APPLICATION)
+ encode_decode_test B(%w{ 41 81 80 } + %w{ AB CD } * 64), OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD } * 64), 1, :APPLICATION)
+ encode_decode_test B(%w{ 41 82 01 00 } + %w{ AB CD } * 128), OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD } * 128), 1, :APPLICATION)
+ encode_decode_test B(%w{ 61 00 }), OpenSSL::ASN1::ASN1Data.new([], 1, :APPLICATION)
+ obj = OpenSSL::ASN1::ASN1Data.new([OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD }), 2, :PRIVATE)], 1, :APPLICATION)
+ obj.indefinite_length = true
+ encode_decode_test B(%w{ 61 80 C2 02 AB CD 00 00 }), obj
+ obj = OpenSSL::ASN1::ASN1Data.new([
+ OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD }), 2, :PRIVATE),
+ OpenSSL::ASN1::EndOfContent.new
+ ], 1, :APPLICATION)
+ obj.indefinite_length = true
+ encode_test B(%w{ 61 80 C2 02 AB CD 00 00 }), obj
+ obj = OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD }), 1, :UNIVERSAL)
+ obj.indefinite_length = true
+ assert_raise(OpenSSL::ASN1::ASN1Error) { obj.to_der }
+ end
+
+ def test_basic_primitive
+ encode_test B(%w{ 00 00 }), OpenSSL::ASN1::Primitive.new(B(%w{}), 0)
+ encode_test B(%w{ 01 00 }), OpenSSL::ASN1::Primitive.new(B(%w{}), 1, nil, :UNIVERSAL)
+ encode_test B(%w{ 81 00 }), OpenSSL::ASN1::Primitive.new(B(%w{}), 1, nil, :CONTEXT_SPECIFIC)
+ encode_test B(%w{ 01 02 AB CD }), OpenSSL::ASN1::Primitive.new(B(%w{ AB CD }), 1)
+ assert_raise(TypeError) { OpenSSL::ASN1::Primitive.new([], 1).to_der }
+
+ prim = OpenSSL::ASN1::Integer.new(50)
+ assert_equal false, prim.indefinite_length
+ assert_not_respond_to prim, :indefinite_length=
+ end
+
+ def test_basic_constructed
+ octet_string = OpenSSL::ASN1::OctetString.new(B(%w{ AB CD }))
+ encode_test B(%w{ 20 00 }), OpenSSL::ASN1::Constructive.new([], 0)
+ encode_test B(%w{ 21 00 }), OpenSSL::ASN1::Constructive.new([], 1, nil, :UNIVERSAL)
+ encode_test B(%w{ A1 00 }), OpenSSL::ASN1::Constructive.new([], 1, nil, :CONTEXT_SPECIFIC)
+ encode_test B(%w{ 21 04 04 02 AB CD }), OpenSSL::ASN1::Constructive.new([octet_string], 1)
+ obj = OpenSSL::ASN1::Constructive.new([octet_string], 1)
+ obj.indefinite_length = true
+ encode_decode_test B(%w{ 21 80 04 02 AB CD 00 00 }), obj
+ obj = OpenSSL::ASN1::Constructive.new([octet_string, OpenSSL::ASN1::EndOfContent.new], 1)
+ obj.indefinite_length = true
+ encode_test B(%w{ 21 80 04 02 AB CD 00 00 }), obj
end
def test_prim_explicit_tagging
- begin
- oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT)
- expected = %w{ A0 03 04 01 61 }
- raw = [expected.join('')].pack('H*')
- assert_equal(raw, oct_str.to_der)
- assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
- end
- end
+ oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT)
+ encode_test B(%w{ A0 03 04 01 61 }), oct_str
+ oct_str2 = OpenSSL::ASN1::OctetString.new("a", 1, :EXPLICIT, :APPLICATION)
+ encode_test B(%w{ 61 03 04 01 61 }), oct_str2
- def test_prim_explicit_tagging_tag_class
- begin
- oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT)
- oct_str2 = OpenSSL::ASN1::OctetString.new(
- "a",
- 0,
- :EXPLICIT,
- :CONTEXT_SPECIFIC)
- assert_equal(oct_str.to_der, oct_str2.to_der)
- end
+ decoded = OpenSSL::ASN1.decode(oct_str2.to_der)
+ assert_equal :APPLICATION, decoded.tag_class
+ assert_equal 1, decoded.tag
+ assert_equal 1, decoded.value.size
+ inner = decoded.value[0]
+ assert_equal OpenSSL::ASN1::OctetString, inner.class
+ assert_equal B(%w{ 61 }), inner.value
end
def test_prim_implicit_tagging
- begin
- int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT)
- expected = %w{ 80 01 01 }
- raw = [expected.join('')].pack('H*')
- assert_equal(raw, int.to_der)
- assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
- end
- end
+ int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT)
+ encode_test B(%w{ 80 01 01 }), int
+ int2 = OpenSSL::ASN1::Integer.new(1, 1, :IMPLICIT, :APPLICATION)
+ encode_test B(%w{ 41 01 01 }), int2
+ decoded = OpenSSL::ASN1.decode(int2.to_der)
+ assert_equal :APPLICATION, decoded.tag_class
+ assert_equal 1, decoded.tag
+ assert_equal B(%w{ 01 }), decoded.value
- def test_prim_implicit_tagging_tag_class
- begin
- int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT)
- int2 = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT, :CONTEXT_SPECIFIC);
- assert_equal(int.to_der, int2.to_der)
- end
+ # Special behavior: Encoding universal types with non-default 'tag'
+ # attribute and nil tagging method.
+ int3 = OpenSSL::ASN1::Integer.new(1, 1)
+ encode_test B(%w{ 01 01 01 }), int3
end
def test_cons_explicit_tagging
- begin
- content = [ OpenSSL::ASN1::PrintableString.new('abc') ]
- seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT)
- expected = %w{ A2 07 30 05 13 03 61 62 63 }
- raw = [expected.join('')].pack('H*')
- assert_equal(raw, seq.to_der)
- assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
- end
- end
-
- def test_cons_explicit_tagging_inf_length
- begin
- content = [ OpenSSL::ASN1::PrintableString.new('abc') ,
- OpenSSL::ASN1::EndOfContent.new() ]
- seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT)
- seq.infinite_length = true
- expected = %w{ A2 80 30 80 13 03 61 62 63 00 00 00 00 }
- raw = [expected.join('')].pack('H*')
- assert_equal(raw, seq.to_der)
- assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
- end
- end
-
- def test_cons_implicit_tagging
- begin
- content = [ OpenSSL::ASN1::Null.new(nil) ]
- seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT)
- expected = %w{ A1 02 05 00 }
- raw = [expected.join('')].pack('H*')
- assert_equal(raw, seq.to_der)
- assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
- end
- end
-
- def test_cons_implicit_tagging_inf_length
- begin
- content = [ OpenSSL::ASN1::Null.new(nil),
- OpenSSL::ASN1::EndOfContent.new() ]
- seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT)
- seq.infinite_length = true
- expected = %w{ A1 80 05 00 00 00 }
- raw = [expected.join('')].pack('H*')
- assert_equal(raw, seq.to_der)
- assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
- end
- end
+ content = [ OpenSSL::ASN1::PrintableString.new('abc') ]
+ seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT)
+ encode_test B(%w{ A2 07 30 05 13 03 61 62 63 }), seq
+ seq2 = OpenSSL::ASN1::Sequence.new(content, 3, :EXPLICIT, :APPLICATION)
+ encode_test B(%w{ 63 07 30 05 13 03 61 62 63 }), seq2
- def test_octet_string_infinite_length_explicit_tagging
- begin
- octets = [ OpenSSL::ASN1::OctetString.new('aaa'),
+ content3 = [ OpenSSL::ASN1::PrintableString.new('abc'),
OpenSSL::ASN1::EndOfContent.new() ]
- cons = OpenSSL::ASN1::Constructive.new(
- octets,
- 1,
- :EXPLICIT)
- cons.infinite_length = true
- expected = %w{ A1 80 24 80 04 03 61 61 61 00 00 00 00 }
- raw = [expected.join('')].pack('H*')
- assert_equal(raw, cons.to_der)
- assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
- end
+ seq3 = OpenSSL::ASN1::Sequence.new(content3, 2, :EXPLICIT)
+ seq3.indefinite_length = true
+ encode_test B(%w{ A2 80 30 80 13 03 61 62 63 00 00 00 00 }), seq3
end
- def test_octet_string_infinite_length_implicit_tagging
- begin
- octets = [ OpenSSL::ASN1::OctetString.new('aaa'),
- OpenSSL::ASN1::EndOfContent.new() ]
- cons = OpenSSL::ASN1::Constructive.new(
- octets,
- 0,
- :IMPLICIT)
- cons.infinite_length = true
- expected = %w{ A0 80 04 03 61 61 61 00 00 }
- raw = [expected.join('')].pack('H*')
- assert_equal(raw, cons.to_der)
- assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
- end
- end
+ def test_cons_implicit_tagging
+ content = [ OpenSSL::ASN1::Null.new(nil) ]
+ seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT)
+ encode_test B(%w{ A1 02 05 00 }), seq
+ seq2 = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT, :APPLICATION)
+ encode_test B(%w{ 61 02 05 00 }), seq2
- def test_recursive_octet_string_infinite_length
- begin
- octets_sub1 = [ OpenSSL::ASN1::OctetString.new("\x01"),
- OpenSSL::ASN1::EndOfContent.new() ]
- octets_sub2 = [ OpenSSL::ASN1::OctetString.new("\x02"),
- OpenSSL::ASN1::EndOfContent.new() ]
- container1 = OpenSSL::ASN1::Constructive.new(
- octets_sub1,
- OpenSSL::ASN1::OCTET_STRING,
- nil,
- :UNIVERSAL)
- container1.infinite_length = true
- container2 = OpenSSL::ASN1::Constructive.new(
- octets_sub2,
- OpenSSL::ASN1::OCTET_STRING,
- nil,
- :UNIVERSAL)
- container2.infinite_length = true
- octets3 = OpenSSL::ASN1::OctetString.new("\x03")
-
- octets = [ container1, container2, octets3,
+ content3 = [ OpenSSL::ASN1::Null.new(nil),
OpenSSL::ASN1::EndOfContent.new() ]
- cons = OpenSSL::ASN1::Constructive.new(
- octets,
- OpenSSL::ASN1::OCTET_STRING,
- nil,
- :UNIVERSAL)
- cons.infinite_length = true
- expected = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 }
- raw = [expected.join('')].pack('H*')
- assert_equal(raw, cons.to_der)
- assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
- end
- end
-
- def test_bit_string_infinite_length
- begin
- content = [ OpenSSL::ASN1::BitString.new("\x01"),
- OpenSSL::ASN1::EndOfContent.new() ]
- cons = OpenSSL::ASN1::Constructive.new(
- content,
- OpenSSL::ASN1::BIT_STRING,
- nil,
- :UNIVERSAL)
- cons.infinite_length = true
- expected = %w{ 23 80 03 02 00 01 00 00 }
- raw = [expected.join('')].pack('H*')
- assert_equal(raw, cons.to_der)
- assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
- end
- end
-
- def test_primitive_inf_length
- assert_raise(OpenSSL::ASN1::ASN1Error) do
- spec = %w{ 02 80 02 01 01 00 00 }
- raw = [spec.join('')].pack('H*')
- OpenSSL::ASN1.decode(raw)
- OpenSSL::ASN1.decode_all(raw)
- end
+ seq3 = OpenSSL::ASN1::Sequence.new(content3, 1, :IMPLICIT)
+ seq3.indefinite_length = true
+ encode_test B(%w{ A1 80 05 00 00 00 }), seq3
+
+ # Special behavior: Encoding universal types with non-default 'tag'
+ # attribute and nil tagging method.
+ seq4 = OpenSSL::ASN1::Sequence.new([], 1)
+ encode_test B(%w{ 21 00 }), seq4
+ end
+
+ def test_octet_string_constructed_tagging
+ octets = [ OpenSSL::ASN1::OctetString.new('aaa') ]
+ cons = OpenSSL::ASN1::Constructive.new(octets, 0, :IMPLICIT)
+ encode_test B(%w{ A0 05 04 03 61 61 61 }), cons
+
+ octets = [ OpenSSL::ASN1::OctetString.new('aaa'),
+ OpenSSL::ASN1::EndOfContent.new() ]
+ cons = OpenSSL::ASN1::Constructive.new(octets, 0, :IMPLICIT)
+ cons.indefinite_length = true
+ encode_test B(%w{ A0 80 04 03 61 61 61 00 00 }), cons
+ end
+
+ def test_recursive_octet_string_indefinite_length
+ octets_sub1 = [ OpenSSL::ASN1::OctetString.new("\x01"),
+ OpenSSL::ASN1::EndOfContent.new() ]
+ octets_sub2 = [ OpenSSL::ASN1::OctetString.new("\x02"),
+ OpenSSL::ASN1::EndOfContent.new() ]
+ container1 = OpenSSL::ASN1::Constructive.new(octets_sub1, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL)
+ container1.indefinite_length = true
+ container2 = OpenSSL::ASN1::Constructive.new(octets_sub2, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL)
+ container2.indefinite_length = true
+ octets3 = OpenSSL::ASN1::OctetString.new("\x03")
+
+ octets = [ container1, container2, octets3,
+ OpenSSL::ASN1::EndOfContent.new() ]
+ cons = OpenSSL::ASN1::Constructive.new(octets, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL)
+ cons.indefinite_length = true
+ raw = B(%w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 })
+ assert_equal(raw, cons.to_der)
+ assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
end
def test_recursive_octet_string_parse
- test = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 }
- raw = [test.join('')].pack('H*')
+ raw = B(%w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 })
asn1 = OpenSSL::ASN1.decode(raw)
assert_equal(OpenSSL::ASN1::Constructive, asn1.class)
assert_universal(OpenSSL::ASN1::OCTET_STRING, asn1)
- assert_equal(true, asn1.infinite_length)
- assert_equal(4, asn1.value.size)
+ assert_equal(true, asn1.indefinite_length)
+ assert_equal(3, asn1.value.size)
nested1 = asn1.value[0]
assert_equal(OpenSSL::ASN1::Constructive, nested1.class)
assert_universal(OpenSSL::ASN1::OCTET_STRING, nested1)
- assert_equal(true, nested1.infinite_length)
- assert_equal(2, nested1.value.size)
+ assert_equal(true, nested1.indefinite_length)
+ assert_equal(1, nested1.value.size)
oct1 = nested1.value[0]
assert_universal(OpenSSL::ASN1::OCTET_STRING, oct1)
- assert_equal(false, oct1.infinite_length)
- assert_universal(OpenSSL::ASN1::EOC, nested1.value[1])
- assert_equal(false, nested1.value[1].infinite_length)
+ assert_equal(false, oct1.indefinite_length)
nested2 = asn1.value[1]
assert_equal(OpenSSL::ASN1::Constructive, nested2.class)
assert_universal(OpenSSL::ASN1::OCTET_STRING, nested2)
- assert_equal(true, nested2.infinite_length)
- assert_equal(2, nested2.value.size)
+ assert_equal(true, nested2.indefinite_length)
+ assert_equal(1, nested2.value.size)
oct2 = nested2.value[0]
assert_universal(OpenSSL::ASN1::OCTET_STRING, oct2)
- assert_equal(false, oct2.infinite_length)
- assert_universal(OpenSSL::ASN1::EOC, nested2.value[1])
- assert_equal(false, nested2.value[1].infinite_length)
+ assert_equal(false, oct2.indefinite_length)
oct3 = asn1.value[2]
assert_universal(OpenSSL::ASN1::OCTET_STRING, oct3)
- assert_equal(false, oct3.infinite_length)
- assert_universal(OpenSSL::ASN1::EOC, asn1.value[3])
- assert_equal(false, asn1.value[3].infinite_length)
+ assert_equal(false, oct3.indefinite_length)
+ end
+
+ def test_decode_constructed_overread
+ test = %w{ 31 06 31 02 30 02 05 00 }
+ # ^ <- invalid
+ raw = [test.join].pack("H*")
+ ret = []
+ assert_raise(OpenSSL::ASN1::ASN1Error) {
+ OpenSSL::ASN1.traverse(raw) { |x| ret << x }
+ }
+ assert_equal 2, ret.size
+ assert_equal 17, ret[0][6]
+ assert_equal 17, ret[1][6]
+
+ test = %w{ 31 80 30 03 00 00 }
+ # ^ <- invalid
+ raw = [test.join].pack("H*")
+ ret = []
+ assert_raise(OpenSSL::ASN1::ASN1Error) {
+ OpenSSL::ASN1.traverse(raw) { |x| ret << x }
+ }
+ assert_equal 1, ret.size
+ assert_equal 17, ret[0][6]
+ end
+
+ def test_constructive_each
+ data = [OpenSSL::ASN1::Integer.new(0), OpenSSL::ASN1::Integer.new(1)]
+ seq = OpenSSL::ASN1::Sequence.new data
+
+ assert_equal data, seq.entries
+ end
+
+ def test_gc_stress
+ skip "very time consuming test"
+ assert_ruby_status(['--disable-gems', '-eGC.stress=true', '-erequire "openssl.so"'])
end
private
+ def B(ary)
+ [ary.join].pack("H*")
+ end
+
+ def assert_asn1_equal(a, b)
+ assert_equal a.class, b.class
+ assert_equal a.tag, b.tag
+ assert_equal a.tag_class, b.tag_class
+ assert_equal a.indefinite_length, b.indefinite_length
+ assert_equal a.unused_bits, b.unused_bits if a.respond_to?(:unused_bits)
+ case a.value
+ when Array
+ a.value.each_with_index { |ai, i|
+ assert_asn1_equal ai, b.value[i]
+ }
+ else
+ if OpenSSL::ASN1::ObjectId === a
+ assert_equal a.oid, b.oid
+ else
+ assert_equal a.value, b.value
+ end
+ end
+ assert_equal a.to_der, b.to_der
+ end
+
+ def encode_test(der, obj)
+ assert_equal der, obj.to_der
+ end
+
+ def decode_test(der, obj)
+ decoded = OpenSSL::ASN1.decode(der)
+ assert_asn1_equal obj, decoded
+ decoded
+ end
+
+ def encode_decode_test(der, obj)
+ encode_test(der, obj)
+ decode_test(der, obj)
+ end
+
def assert_universal(tag, asn1)
assert_equal(tag, asn1.tag)
if asn1.respond_to?(:tagging)
@@ -619,6 +689,6 @@ rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm
end
assert_equal(:UNIVERSAL, asn1.tag_class)
end
+end
-end if defined?(OpenSSL::TestUtils)
-
+end
diff --git a/test/openssl/test_bn.rb b/test/openssl/test_bn.rb
index 37ba5e5595..0b5cd84241 100644
--- a/test/openssl/test_bn.rb
+++ b/test/openssl/test_bn.rb
@@ -1,60 +1,281 @@
+# coding: us-ascii
# frozen_string_literal: false
require_relative 'utils'
+require "prime"
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestBN < OpenSSL::TestCase
- def test_new_str
- e1 = OpenSSL::BN.new(999.to_s(16), 16) # OpenSSL::BN.new(str, 16) must be most stable
- e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16)
- assert_equal(e1, OpenSSL::BN.new("999"))
- assert_equal(e2, OpenSSL::BN.new((2**107-1).to_s))
- assert_equal(e1, OpenSSL::BN.new("999", 10))
- assert_equal(e2, OpenSSL::BN.new((2**107-1).to_s, 10))
- assert_equal(e1, OpenSSL::BN.new("\x03\xE7", 2))
- assert_equal(e2, OpenSSL::BN.new("\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 2))
- assert_equal(e1, OpenSSL::BN.new("\x00\x00\x00\x02\x03\xE7", 0))
- assert_equal(e2, OpenSSL::BN.new("\x00\x00\x00\x0E\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0))
- end
-
- def test_new_bn
- e1 = OpenSSL::BN.new(999.to_s(16), 16)
- e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16)
- assert_equal(e1, OpenSSL::BN.new(e1))
- assert_equal(e2, OpenSSL::BN.new(e2))
- end
-
- def test_new_integer
- assert_equal(999.to_bn, OpenSSL::BN.new(999))
- assert_equal((2 ** 107 - 1).to_bn, OpenSSL::BN.new(2 ** 107 - 1))
- assert_equal(-999.to_bn, OpenSSL::BN.new(-999))
- assert_equal((-(2 ** 107 - 1)).to_bn, OpenSSL::BN.new(-(2 ** 107 - 1)))
- end
-
- def test_to_bn
- e1 = OpenSSL::BN.new(999.to_s(16), 16)
- e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16)
- assert_equal(e1, 999.to_bn)
- assert_equal(e2, (2**107-1).to_bn)
- end
-
- def test_prime_p
- assert_equal(true, OpenSSL::BN.new((2 ** 107 - 1).to_s(16), 16).prime?)
- assert_equal(true, OpenSSL::BN.new((2 ** 127 - 1).to_s(16), 16).prime?(1))
- end
-
- def test_cmp
- bn1 = OpenSSL::BN.new('1')
- bn2 = OpenSSL::BN.new('1')
- bn3 = OpenSSL::BN.new('2')
- assert_equal(false, bn1 == nil)
- assert_equal(true, bn1 != nil)
- assert_equal(true, bn1 == bn2)
- assert_equal(false, bn1 == bn3)
- assert_equal(true, bn1.eql?(bn2))
- assert_equal(false, bn1.eql?(bn3))
- assert_equal(bn1.hash, bn2.hash)
- assert_not_equal(bn3.hash, bn1.hash)
+ def setup
+ super
+ @e1 = OpenSSL::BN.new(999.to_s(16), 16) # OpenSSL::BN.new(str, 16) must be most stable
+ @e2 = OpenSSL::BN.new("-" + 999.to_s(16), 16)
+ @e3 = OpenSSL::BN.new((2**107-1).to_s(16), 16)
+ @e4 = OpenSSL::BN.new("-" + (2**107-1).to_s(16), 16)
+ end
+
+ def test_new
+ assert_equal(@e1, OpenSSL::BN.new("999"))
+ assert_equal(@e1, OpenSSL::BN.new("999", 10))
+ assert_equal(@e1, OpenSSL::BN.new("\x03\xE7", 2))
+ assert_equal(@e1, OpenSSL::BN.new("\x00\x00\x00\x02\x03\xE7", 0))
+ assert_equal(@e2, OpenSSL::BN.new("-999"))
+ assert_equal(@e2, OpenSSL::BN.new("-999", 10))
+ assert_equal(@e2, OpenSSL::BN.new("\x00\x00\x00\x02\x83\xE7", 0))
+ assert_equal(@e3, OpenSSL::BN.new((2**107-1).to_s))
+ assert_equal(@e3, OpenSSL::BN.new((2**107-1).to_s, 10))
+ assert_equal(@e3, OpenSSL::BN.new("\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 2))
+ assert_equal(@e3, OpenSSL::BN.new("\x00\x00\x00\x0E\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0))
+ assert_equal(@e4, OpenSSL::BN.new("-" + (2**107-1).to_s))
+ assert_equal(@e4, OpenSSL::BN.new("-" + (2**107-1).to_s, 10))
+ assert_equal(@e4, OpenSSL::BN.new("\x00\x00\x00\x0E\x87\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0))
+
+ e1copy = OpenSSL::BN.new(@e1)
+ assert_equal(@e1, e1copy)
+ e1copy.clear_bit!(0) #=> 998
+ assert_not_equal(@e1, e1copy)
+
+ assert_equal(@e1, OpenSSL::BN.new(999))
+ assert_equal(@e2, OpenSSL::BN.new(-999))
+ assert_equal(@e3, OpenSSL::BN.new(2**107-1))
+ assert_equal(@e4, OpenSSL::BN.new(-(2**107-1)))
+
+ assert_equal(@e1, 999.to_bn)
+ assert_equal(@e2, -999.to_bn)
+ assert_equal(@e3, (2**107-1).to_bn)
+ assert_equal(@e4, (-(2**107-1)).to_bn)
+ end
+
+ def test_to_str
+ assert_equal("999", @e1.to_s(10))
+ assert_equal("-999", @e2.to_s(10))
+ assert_equal((2**107-1).to_s, @e3.to_s(10))
+ assert_equal((-(2**107-1)).to_s, @e4.to_s(10))
+ assert_equal("999", @e1.to_s)
+
+ assert_equal("03E7", @e1.to_s(16))
+ assert_equal("-03E7", @e2.to_s(16))
+ assert_equal("07FFFFFFFFFFFFFFFFFFFFFFFFFF", @e3.to_s(16))
+ assert_equal("-07FFFFFFFFFFFFFFFFFFFFFFFFFF", @e4.to_s(16))
+
+ assert_equal("\x03\xe7", @e1.to_s(2))
+ assert_equal("\x03\xe7", @e2.to_s(2))
+ assert_equal("\x07\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", @e3.to_s(2))
+ assert_equal("\x07\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", @e4.to_s(2))
+
+ assert_equal("\x00\x00\x00\x02\x03\xe7", @e1.to_s(0))
+ assert_equal("\x00\x00\x00\x02\x83\xe7", @e2.to_s(0))
+ assert_equal("\x00\x00\x00\x0e\x07\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", @e3.to_s(0))
+ assert_equal("\x00\x00\x00\x0e\x87\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", @e4.to_s(0))
+ end
+
+ def test_to_int
+ assert_equal(999, @e1.to_i)
+ assert_equal(-999, @e2.to_i)
+ assert_equal(2**107-1, @e3.to_i)
+ assert_equal(-(2**107-1), @e4.to_i)
+
+ assert_equal(999, @e1.to_int)
+ end
+
+ def test_coerce
+ assert_equal(["", "-999"], @e2.coerce(""))
+ assert_equal([1000, -999], @e2.coerce(1000))
+ assert_raise(TypeError) { @e2.coerce(Class.new.new) }
+ end
+
+ def test_zero_p
+ assert_equal(true, 0.to_bn.zero?)
+ assert_equal(false, 1.to_bn.zero?)
+ end
+
+ def test_one_p
+ assert_equal(true, 1.to_bn.one?)
+ assert_equal(false, 2.to_bn.one?)
+ end
+
+ def test_odd_p
+ assert_equal(true, 1.to_bn.odd?)
+ assert_equal(false, 2.to_bn.odd?)
+ end
+
+ def test_negative_p
+ assert_equal(false, 0.to_bn.negative?)
+ assert_equal(false, @e1.negative?)
+ assert_equal(true, @e2.negative?)
+ end
+
+ def test_sqr
+ assert_equal(1, 1.to_bn.sqr)
+ assert_equal(100, 10.to_bn.sqr)
+ end
+
+ def test_four_ops
+ assert_equal(3, 1.to_bn + 2)
+ assert_equal(-1, 1.to_bn + -2)
+ assert_equal(-1, 1.to_bn - 2)
+ assert_equal(3, 1.to_bn - -2)
+ assert_equal(2, 1.to_bn * 2)
+ assert_equal(-2, 1.to_bn * -2)
+ assert_equal([0, 1], 1.to_bn / 2)
+ assert_equal([2, 0], 2.to_bn / 1)
+ assert_raise(OpenSSL::BNError) { 1.to_bn / 0 }
+ end
+
+ def test_unary_plus_minus
+ assert_equal(999, +@e1)
+ assert_equal(-999, +@e2)
+ assert_equal(-999, -@e1)
+ assert_equal(+999, -@e2)
+ end
+
+ def test_mod
+ assert_equal(1, 1.to_bn % 2)
+ assert_equal(0, 2.to_bn % 1)
+ assert_equal(-2, -2.to_bn % 7)
+ end
+
+ def test_exp
+ assert_equal(1, 1.to_bn ** 5)
+ assert_equal(32, 2.to_bn ** 5)
+ end
+
+ def test_gcd
+ assert_equal(1, 7.to_bn.gcd(5))
+ assert_equal(8, 24.to_bn.gcd(16))
+ end
+
+ def test_mod_sqr
+ assert_equal(4, 3.to_bn.mod_sqr(5))
+ assert_equal(0, 59.to_bn.mod_sqr(59))
+ end
+
+ def test_mod_inverse
+ assert_equal(2, 3.to_bn.mod_inverse(5))
+ assert_raise(OpenSSL::BNError) { 3.to_bn.mod_inverse(6) }
+ end
+
+ def test_mod_add
+ assert_equal(1, 3.to_bn.mod_add(5, 7))
+ assert_equal(2, 3.to_bn.mod_add(5, 3))
+ assert_equal(5, 3.to_bn.mod_add(-5, 7))
+ end
+
+ def test_mod_sub
+ assert_equal(1, 11.to_bn.mod_sub(3, 7))
+ assert_equal(2, 11.to_bn.mod_sub(3, 3))
+ assert_equal(5, 3.to_bn.mod_sub(5, 7))
+ end
+
+ def test_mod_mul
+ assert_equal(1, 2.to_bn.mod_mul(4, 7))
+ assert_equal(5, 2.to_bn.mod_mul(-1, 7))
+ end
+
+ def test_mod_exp
+ assert_equal(1, 3.to_bn.mod_exp(2, 8))
+ assert_equal(4, 2.to_bn.mod_exp(5, 7))
+ end
+
+ def test_bit_operations
+ e = 0b10010010.to_bn
+ assert_equal(0b10010011, e.set_bit!(0))
+ assert_equal(0b10010011, e.set_bit!(1))
+ assert_equal(0b1010010011, e.set_bit!(9))
+
+ e = 0b10010010.to_bn
+ assert_equal(0b10010010, e.clear_bit!(0))
+ assert_equal(0b10010000, e.clear_bit!(1))
+
+ e = 0b10010010.to_bn
+ assert_equal(0b10010010, e.mask_bits!(8))
+ assert_equal(0b10, e.mask_bits!(3))
+
+ e = 0b10010010.to_bn
+ assert_equal(false, e.bit_set?(0))
+ assert_equal(true, e.bit_set?(1))
+ assert_equal(false, e.bit_set?(1000))
+
+ e = 0b10010010.to_bn
+ assert_equal(0b1001001000, e << 2)
+ assert_equal(0b10010010, e)
+ assert_equal(0b1001001000, e.lshift!(2))
+ assert_equal(0b1001001000, e)
+
+ e = 0b10010010.to_bn
+ assert_equal(0b100100, e >> 2)
+ assert_equal(0b10010010, e)
+ assert_equal(0b100100, e.rshift!(2))
+ assert_equal(0b100100, e)
+ end
+
+ def test_random
+ 10.times {
+ r1 = OpenSSL::BN.rand(8)
+ assert_include(128..255, r1)
+ r2 = OpenSSL::BN.rand(8, -1)
+ assert_include(0..255, r2)
+ r3 = OpenSSL::BN.rand(8, 1)
+ assert_include(192..255, r3)
+ r4 = OpenSSL::BN.rand(8, 1, true)
+ assert_include(192..255, r4)
+ assert_equal(true, r4.odd?)
+
+ r5 = OpenSSL::BN.rand_range(256)
+ assert_include(0..255, r5)
+ }
+ end
+
+ def test_prime
+ p1 = OpenSSL::BN.generate_prime(32)
+ assert_include(0...2**32, p1)
+ assert_equal(true, Prime.prime?(p1.to_i))
+ p2 = OpenSSL::BN.generate_prime(32, true)
+ assert_equal(true, Prime.prime?((p2.to_i - 1) / 2))
+ p3 = OpenSSL::BN.generate_prime(32, false, 4)
+ assert_equal(1, p3 % 4)
+ p4 = OpenSSL::BN.generate_prime(32, false, 4, 3)
+ assert_equal(3, p4 % 4)
+
+ assert_equal(true, p1.prime?)
+ assert_equal(true, p2.prime?)
+ assert_equal(true, p3.prime?)
+ assert_equal(true, p4.prime?)
+ assert_equal(true, @e3.prime?)
+ assert_equal(true, @e3.prime_fasttest?)
+ end
+
+ def test_num_bits_bytes
+ assert_equal(10, @e1.num_bits)
+ assert_equal(2, @e1.num_bytes)
+ assert_equal(107, @e3.num_bits)
+ assert_equal(14, @e3.num_bytes)
+ assert_equal(0, 0.to_bn.num_bits)
+ assert_equal(0, 0.to_bn.num_bytes)
+ assert_equal(9, -256.to_bn.num_bits)
+ assert_equal(2, -256.to_bn.num_bytes)
+ end
+
+ def test_comparison
+ assert_equal(false, @e1 == nil)
+ assert_equal(false, @e1 == -999)
+ assert_equal(true, @e1 == 999)
+ assert_equal(true, @e1 == 999.to_bn)
+ assert_equal(false, @e1.eql?(nil))
+ assert_equal(false, @e1.eql?(999))
+ assert_equal(true, @e1.eql?(999.to_bn))
+ assert_equal(@e1.hash, 999.to_bn.hash)
+ assert_not_equal(@e1.hash, @e3.hash)
+ assert_equal(0, @e1.cmp(999))
+ assert_equal(1, @e1.cmp(-999))
+ assert_equal(0, @e1.ucmp(999))
+ assert_equal(0, @e1.ucmp(-999))
+ assert_instance_of(String, @e1.hash.to_s)
+ end
+
+ def test_type_error
+ bug15760 = '[ruby-core:92231] [Bug #15760]'
+ assert_raise(TypeError, bug15760) { OpenSSL::BN.new(nil, 2) }
end
end
diff --git a/test/openssl/test_buffering.rb b/test/openssl/test_buffering.rb
index 1f42cd3c31..c85a6f020b 100644
--- a/test/openssl/test_buffering.rb
+++ b/test/openssl/test_buffering.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: false
require_relative 'utils'
-require 'stringio'
-class OpenSSL::TestBuffering < OpenSSL::TestCase
+if defined?(OpenSSL)
+class OpenSSL::TestBuffering < OpenSSL::TestCase
class IO
include OpenSSL::Buffering
@@ -37,6 +37,7 @@ class OpenSSL::TestBuffering < OpenSSL::TestCase
end
def setup
+ super
@io = IO.new
end
@@ -84,5 +85,6 @@ class OpenSSL::TestBuffering < OpenSSL::TestCase
end
assert_equal([97, 98, 99], res)
end
+end
-end if defined?(OpenSSL::TestUtils)
+end
diff --git a/test/openssl/test_cipher.rb b/test/openssl/test_cipher.rb
index 74c5394f5f..d83fa4ec3d 100644
--- a/test/openssl/test_cipher.rb
+++ b/test/openssl/test_cipher.rb
@@ -1,114 +1,138 @@
# frozen_string_literal: false
require_relative 'utils'
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestCipher < OpenSSL::TestCase
-
- @ciphers = OpenSSL::Cipher.ciphers
-
- class << self
-
+ module Helper
def has_cipher?(name)
+ @ciphers ||= OpenSSL::Cipher.ciphers
@ciphers.include?(name)
end
-
- def has_ciphers?(list)
- list.all? { |name| has_cipher?(name) }
- end
-
- end
-
- def setup
- @c1 = OpenSSL::Cipher.new("DES-EDE3-CBC")
- @c2 = OpenSSL::Cipher::DES.new(:EDE3, "CBC")
- @key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- @iv = "\0\0\0\0\0\0\0\0"
- @hexkey = "0000000000000000000000000000000000000000000000"
- @hexiv = "0000000000000000"
- @data = "DATA"
end
-
- def teardown
- super
- @c1 = @c2 = nil
+ include Helper
+ extend Helper
+
+ def test_encrypt_decrypt
+ # NIST SP 800-38A F.2.1
+ key = ["2b7e151628aed2a6abf7158809cf4f3c"].pack("H*")
+ iv = ["000102030405060708090a0b0c0d0e0f"].pack("H*")
+ pt = ["6bc1bee22e409f96e93d7e117393172a" \
+ "ae2d8a571e03ac9c9eb76fac45af8e51"].pack("H*")
+ ct = ["7649abac8119b246cee98e9b12e9197d" \
+ "5086cb9b507219ee95db113a917678b2"].pack("H*")
+ cipher = new_encryptor("aes-128-cbc", key: key, iv: iv, padding: 0)
+ assert_equal ct, cipher.update(pt) << cipher.final
+ cipher = new_decryptor("aes-128-cbc", key: key, iv: iv, padding: 0)
+ assert_equal pt, cipher.update(ct) << cipher.final
end
- def test_crypt
- @c1.encrypt.pkcs5_keyivgen(@key, @iv)
- @c2.encrypt.pkcs5_keyivgen(@key, @iv)
- s1 = @c1.update(@data) + @c1.final
- s2 = @c2.update(@data) + @c2.final
- assert_equal(s1, s2, "encrypt")
-
- @c1.decrypt.pkcs5_keyivgen(@key, @iv)
- @c2.decrypt.pkcs5_keyivgen(@key, @iv)
- assert_equal(@data, @c1.update(s1)+@c1.final, "decrypt")
- assert_equal(@data, @c2.update(s2)+@c2.final, "decrypt")
+ def test_pkcs5_keyivgen
+ pass = "\x00" * 8
+ salt = "\x01" * 8
+ num = 2048
+ pt = "data to be encrypted"
+ cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt
+ cipher.pkcs5_keyivgen(pass, salt, num, "MD5")
+ s1 = cipher.update(pt) << cipher.final
+
+ d1 = num.times.inject(pass + salt) {|out, _| OpenSSL::Digest::MD5.digest(out) }
+ d2 = num.times.inject(d1 + pass + salt) {|out, _| OpenSSL::Digest::MD5.digest(out) }
+ key = (d1 + d2)[0, 24]
+ iv = (d1 + d2)[24, 8]
+ cipher = new_encryptor("DES-EDE3-CBC", key: key, iv: iv)
+ s2 = cipher.update(pt) << cipher.final
+
+ assert_equal s1, s2
+
+ cipher2 = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt
+ assert_raise(ArgumentError) { cipher2.pkcs5_keyivgen(pass, salt, -1, "MD5") }
end
def test_info
- assert_equal("DES-EDE3-CBC", @c1.name, "name")
- assert_equal("DES-EDE3-CBC", @c2.name, "name")
- assert_kind_of(Integer, @c1.key_len, "key_len")
- assert_kind_of(Integer, @c1.iv_len, "iv_len")
+ cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt
+ assert_equal "DES-EDE3-CBC", cipher.name
+ assert_equal 24, cipher.key_len
+ assert_equal 8, cipher.iv_len
end
def test_dup
- assert_equal(@c1.name, @c1.dup.name, "dup")
- assert_equal(@c1.name, @c1.clone.name, "clone")
- @c1.encrypt
- @c1.key = @key
- @c1.iv = @iv
- tmpc = @c1.dup
- s1 = @c1.update(@data) + @c1.final
- s2 = tmpc.update(@data) + tmpc.final
+ cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt
+ assert_equal cipher.name, cipher.dup.name
+ cipher.encrypt
+ cipher.random_key
+ cipher.random_iv
+ tmpc = cipher.dup
+ s1 = cipher.update("data") + cipher.final
+ s2 = tmpc.update("data") + tmpc.final
assert_equal(s1, s2, "encrypt dup")
end
def test_reset
- @c1.encrypt
- @c1.key = @key
- @c1.iv = @iv
- s1 = @c1.update(@data) + @c1.final
- @c1.reset
- s2 = @c1.update(@data) + @c1.final
+ cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt
+ cipher.encrypt
+ cipher.random_key
+ cipher.random_iv
+ s1 = cipher.update("data") + cipher.final
+ cipher.reset
+ s2 = cipher.update("data") + cipher.final
assert_equal(s1, s2, "encrypt reset")
end
def test_key_iv_set
- # default value for DES-EDE3-CBC
- assert_equal(24, @c1.key_len)
- assert_equal(8, @c1.iv_len)
- assert_raise(ArgumentError) { @c1.key = "\x01" * 23 }
- @c1.key = "\x01" * 24
- assert_raise(ArgumentError) { @c1.key = "\x01" * 25 }
- assert_raise(ArgumentError) { @c1.iv = "\x01" * 7 }
- @c1.iv = "\x01" * 8
- assert_raise(ArgumentError) { @c1.iv = "\x01" * 9 }
+ cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt
+ assert_raise(ArgumentError) { cipher.key = "\x01" * 23 }
+ assert_nothing_raised { cipher.key = "\x01" * 24 }
+ assert_raise(ArgumentError) { cipher.key = "\x01" * 25 }
+ assert_raise(ArgumentError) { cipher.iv = "\x01" * 7 }
+ assert_nothing_raised { cipher.iv = "\x01" * 8 }
+ assert_raise(ArgumentError) { cipher.iv = "\x01" * 9 }
+ end
+
+ def test_random_key_iv
+ data = "data"
+ s1, s2 = 2.times.map do
+ cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt
+ cipher.random_key
+ cipher.iv = "\x01" * 16
+ cipher.update(data) << cipher.final
+ end
+ assert_not_equal s1, s2
+
+ s1, s2 = 2.times.map do
+ cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt
+ cipher.key = "\x01" * 16
+ cipher.random_iv
+ cipher.update(data) << cipher.final
+ end
+ assert_not_equal s1, s2
end
def test_empty_data
- @c1.encrypt
- assert_raise(ArgumentError){ @c1.update("") }
+ cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt
+ cipher.random_key
+ assert_raise(ArgumentError) { cipher.update("") }
end
def test_initialize
- assert_raise(RuntimeError) {@c1.__send__(:initialize, "DES-EDE3-CBC")}
- assert_raise(RuntimeError) {OpenSSL::Cipher.allocate.final}
+ cipher = OpenSSL::Cipher.new("DES-EDE3-CBC")
+ assert_raise(RuntimeError) { cipher.__send__(:initialize, "DES-EDE3-CBC") }
+ assert_raise(RuntimeError) { OpenSSL::Cipher.allocate.final }
end
def test_ctr_if_exists
- begin
- cipher = OpenSSL::Cipher.new('aes-128-ctr')
- cipher.encrypt
- cipher.pkcs5_keyivgen('password')
- c = cipher.update('hello,world') + cipher.final
- cipher.decrypt
- cipher.pkcs5_keyivgen('password')
- assert_equal('hello,world', cipher.update(c) + cipher.final)
- end
- end if has_cipher?('aes-128-ctr')
+ # NIST SP 800-38A F.5.1
+ key = ["2b7e151628aed2a6abf7158809cf4f3c"].pack("H*")
+ iv = ["f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"].pack("H*")
+ pt = ["6bc1bee22e409f96e93d7e117393172a" \
+ "ae2d8a571e03ac9c9eb76fac45af8e51"].pack("H*")
+ ct = ["874d6191b620e3261bef6864990db6ce" \
+ "9806f66b7970fdff8617187bb9fffdff"].pack("H*")
+ cipher = new_encryptor("aes-128-ctr", key: key, iv: iv, padding: 0)
+ assert_equal ct, cipher.update(pt) << cipher.final
+ cipher = new_decryptor("aes-128-ctr", key: key, iv: iv, padding: 0)
+ assert_equal pt, cipher.update(ct) << cipher.final
+ end
def test_ciphers
OpenSSL::Cipher.ciphers.each{|name|
@@ -136,205 +160,166 @@ class OpenSSL::TestCipher < OpenSSL::TestCase
}
end
- def test_AES_crush
- 500.times do
- assert_nothing_raised("[Bug #2768]") do
- # it caused OpenSSL SEGV by uninitialized key
- OpenSSL::Cipher::AES128.new("ECB").update "." * 17
- end
+ def test_update_raise_if_key_not_set
+ assert_raise(OpenSSL::Cipher::CipherError) do
+ # it caused OpenSSL SEGV by uninitialized key [Bug #2768]
+ OpenSSL::Cipher::AES128.new("ECB").update "." * 17
end
end
- if has_ciphers?(['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'])
-
- def test_authenticated
- cipher = OpenSSL::Cipher.new('aes-128-gcm')
- assert_predicate(cipher, :authenticated?)
- cipher = OpenSSL::Cipher.new('aes-128-cbc')
- assert_not_predicate(cipher, :authenticated?)
- end
-
- def test_aes_gcm
- ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'].each do |algo|
- pt = "You should all use Authenticated Encryption!"
- cipher, key, iv = new_encryptor(algo)
-
- cipher.auth_data = "aad"
- ct = cipher.update(pt) + cipher.final
- tag = cipher.auth_tag
- assert_equal(16, tag.size)
-
- decipher = new_decryptor(algo, key, iv)
- decipher.auth_tag = tag
- decipher.auth_data = "aad"
-
- assert_equal(pt, decipher.update(ct) + decipher.final)
- end
- end
-
- def test_aes_gcm_short_tag
- ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'].each do |algo|
- pt = "You should all use Authenticated Encryption!"
- cipher, key, iv = new_encryptor(algo)
-
- cipher.auth_data = "aad"
- ct = cipher.update(pt) + cipher.final
- tag = cipher.auth_tag(8)
- assert_equal(8, tag.size)
-
- decipher = new_decryptor(algo, key, iv)
- decipher.auth_tag = tag
- decipher.auth_data = "aad"
-
- assert_equal(pt, decipher.update(ct) + decipher.final)
- end
- end
-
- def test_aes_gcm_wrong_tag
- pt = "You should all use Authenticated Encryption!"
- cipher, key, iv = new_encryptor('aes-128-gcm')
-
- cipher.auth_data = "aad"
- ct = cipher.update(pt) + cipher.final
- tag = cipher.auth_tag
-
- decipher = new_decryptor('aes-128-gcm', key, iv)
- tag.setbyte(-1, (tag.getbyte(-1) + 1) & 0xff)
- decipher.auth_tag = tag
- decipher.auth_data = "aad"
-
- assert_raise OpenSSL::Cipher::CipherError do
- decipher.update(ct) + decipher.final
- end
- end
-
- def test_aes_gcm_wrong_auth_data
- pt = "You should all use Authenticated Encryption!"
- cipher, key, iv = new_encryptor('aes-128-gcm')
-
- cipher.auth_data = "aad"
- ct = cipher.update(pt) + cipher.final
- tag = cipher.auth_tag
-
- decipher = new_decryptor('aes-128-gcm', key, iv)
- decipher.auth_tag = tag
- decipher.auth_data = "daa"
-
- assert_raise OpenSSL::Cipher::CipherError do
- decipher.update(ct) + decipher.final
- end
- end
-
- def test_aes_gcm_wrong_ciphertext
- pt = "You should all use Authenticated Encryption!"
- cipher, key, iv = new_encryptor('aes-128-gcm')
+ def test_authenticated
+ cipher = OpenSSL::Cipher.new('aes-128-gcm')
+ assert_predicate(cipher, :authenticated?)
+ cipher = OpenSSL::Cipher.new('aes-128-cbc')
+ assert_not_predicate(cipher, :authenticated?)
+ end
- cipher.auth_data = "aad"
- ct = cipher.update(pt) + cipher.final
- tag = cipher.auth_tag
+ def test_aes_gcm
+ # GCM spec Appendix B Test Case 4
+ key = ["feffe9928665731c6d6a8f9467308308"].pack("H*")
+ iv = ["cafebabefacedbaddecaf888"].pack("H*")
+ aad = ["feedfacedeadbeeffeedfacedeadbeef" \
+ "abaddad2"].pack("H*")
+ pt = ["d9313225f88406e5a55909c5aff5269a" \
+ "86a7a9531534f7da2e4c303d8a318a72" \
+ "1c3c0c95956809532fcf0e2449a6b525" \
+ "b16aedf5aa0de657ba637b39"].pack("H*")
+ ct = ["42831ec2217774244b7221b784d0d49c" \
+ "e3aa212f2c02a4e035c17e2329aca12e" \
+ "21d514b25466931c7d8f6a5aac84aa05" \
+ "1ba30b396a0aac973d58e091"].pack("H*")
+ tag = ["5bc94fbc3221a5db94fae95ae7121a47"].pack("H*")
+
+ cipher = new_encryptor("aes-128-gcm", key: key, iv: iv, auth_data: aad)
+ assert_equal ct, cipher.update(pt) << cipher.final
+ assert_equal tag, cipher.auth_tag
+ cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag, auth_data: aad)
+ assert_equal pt, cipher.update(ct) << cipher.final
+
+ # truncated tag is accepted
+ cipher = new_encryptor("aes-128-gcm", key: key, iv: iv, auth_data: aad)
+ assert_equal ct, cipher.update(pt) << cipher.final
+ assert_equal tag[0, 8], cipher.auth_tag(8)
+ cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag[0, 8], auth_data: aad)
+ assert_equal pt, cipher.update(ct) << cipher.final
+
+ # wrong tag is rejected
+ tag2 = tag.dup
+ tag2.setbyte(-1, (tag2.getbyte(-1) + 1) & 0xff)
+ cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag2, auth_data: aad)
+ cipher.update(ct)
+ assert_raise(OpenSSL::Cipher::CipherError) { cipher.final }
+
+ # wrong aad is rejected
+ aad2 = aad[0..-2] << aad[-1].succ
+ cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag, auth_data: aad2)
+ cipher.update(ct)
+ assert_raise(OpenSSL::Cipher::CipherError) { cipher.final }
+
+ # wrong ciphertext is rejected
+ ct2 = ct[0..-2] << ct[-1].succ
+ cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag, auth_data: aad)
+ cipher.update(ct2)
+ assert_raise(OpenSSL::Cipher::CipherError) { cipher.final }
+ end
- decipher = new_decryptor('aes-128-gcm', key, iv)
- decipher.auth_tag = tag
- decipher.auth_data = "aad"
+ def test_aes_gcm_variable_iv_len
+ # GCM spec Appendix B Test Case 5
+ key = ["feffe9928665731c6d6a8f9467308308"].pack("H*")
+ iv = ["cafebabefacedbad"].pack("H*")
+ aad = ["feedfacedeadbeeffeedfacedeadbeef" \
+ "abaddad2"].pack("H*")
+ pt = ["d9313225f88406e5a55909c5aff5269a" \
+ "86a7a9531534f7da2e4c303d8a318a72" \
+ "1c3c0c95956809532fcf0e2449a6b525" \
+ "b16aedf5aa0de657ba637b39"].pack("H*")
+ ct = ["61353b4c2806934a777ff51fa22a4755" \
+ "699b2a714fcdc6f83766e5f97b6c7423" \
+ "73806900e49f24b22b097544d4896b42" \
+ "4989b5e1ebac0f07c23f4598"].pack("H*")
+ tag = ["3612d2e79e3b0785561be14aaca2fccb"].pack("H*")
+
+ cipher = new_encryptor("aes-128-gcm", key: key, iv_len: 8, iv: iv, auth_data: aad)
+ assert_equal ct, cipher.update(pt) << cipher.final
+ assert_equal tag, cipher.auth_tag
+ cipher = new_decryptor("aes-128-gcm", key: key, iv_len: 8, iv: iv, auth_tag: tag, auth_data: aad)
+ assert_equal pt, cipher.update(ct) << cipher.final
+ end
- assert_raise OpenSSL::Cipher::CipherError do
- decipher.update(ct[0..-2] << ct[-1].succ) + decipher.final
- end
- end
+ def test_aes_ocb_tag_len
+ # RFC 7253 Appendix A; the second sample
+ key = ["000102030405060708090A0B0C0D0E0F"].pack("H*")
+ iv = ["BBAA99887766554433221101"].pack("H*")
+ aad = ["0001020304050607"].pack("H*")
+ pt = ["0001020304050607"].pack("H*")
+ ct = ["6820B3657B6F615A"].pack("H*")
+ tag = ["5725BDA0D3B4EB3A257C9AF1F8F03009"].pack("H*")
+
+ cipher = new_encryptor("aes-128-ocb", key: key, iv: iv, auth_data: aad)
+ assert_equal ct, cipher.update(pt) << cipher.final
+ assert_equal tag, cipher.auth_tag
+ cipher = new_decryptor("aes-128-ocb", key: key, iv: iv, auth_tag: tag, auth_data: aad)
+ assert_equal pt, cipher.update(ct) << cipher.final
+
+ # RFC 7253 Appendix A; with 96 bits tag length
+ key = ["0F0E0D0C0B0A09080706050403020100"].pack("H*")
+ iv = ["BBAA9988776655443322110D"].pack("H*")
+ aad = ["000102030405060708090A0B0C0D0E0F1011121314151617" \
+ "18191A1B1C1D1E1F2021222324252627"].pack("H*")
+ pt = ["000102030405060708090A0B0C0D0E0F1011121314151617" \
+ "18191A1B1C1D1E1F2021222324252627"].pack("H*")
+ ct = ["1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1" \
+ "A0124B0A55BAE884ED93481529C76B6A"].pack("H*")
+ tag = ["D0C515F4D1CDD4FDAC4F02AA"].pack("H*")
+
+ cipher = new_encryptor("aes-128-ocb", auth_tag_len: 12, key: key, iv: iv, auth_data: aad)
+ assert_equal ct, cipher.update(pt) << cipher.final
+ assert_equal tag, cipher.auth_tag
+ cipher = new_decryptor("aes-128-ocb", auth_tag_len: 12, key: key, iv: iv, auth_tag: tag, auth_data: aad)
+ assert_equal pt, cipher.update(ct) << cipher.final
- def test_aes_gcm_variable_iv_len
- pt = "You should all use Authenticated Encryption!"
- cipher = OpenSSL::Cipher.new("aes-128-gcm").encrypt
- cipher.key = "x" * 16
- assert_equal(12, cipher.iv_len)
- cipher.iv = "a" * 12
- ct1 = cipher.update(pt) << cipher.final
- tag1 = cipher.auth_tag
-
- cipher = OpenSSL::Cipher.new("aes-128-gcm").encrypt
- cipher.key = "x" * 16
- cipher.iv_len = 10
- assert_equal(10, cipher.iv_len)
- cipher.iv = "a" * 10
- ct2 = cipher.update(pt) << cipher.final
- tag2 = cipher.auth_tag
-
- assert_not_equal ct1, ct2
- assert_not_equal tag1, tag2
-
- decipher = OpenSSL::Cipher.new("aes-128-gcm").decrypt
- decipher.auth_tag = tag1
- decipher.key = "x" * 16
- decipher.iv_len = 12
- decipher.iv = "a" * 12
- assert_equal(pt, decipher.update(ct1) << decipher.final)
-
- decipher.reset
- decipher.auth_tag = tag2
- assert_raise(OpenSSL::Cipher::CipherError) {
- decipher.update(ct2) << decipher.final
- }
-
- decipher.reset
- decipher.auth_tag = tag2
- decipher.iv_len = 10
- decipher.iv = "a" * 10
- assert_equal(pt, decipher.update(ct2) << decipher.final)
- end
+ end if has_cipher?("aes-128-ocb")
+ def test_aes_gcm_key_iv_order_issue
+ pt = "[ruby/openssl#49]"
+ cipher = OpenSSL::Cipher.new("aes-128-gcm").encrypt
+ cipher.key = "x" * 16
+ cipher.iv = "a" * 12
+ ct1 = cipher.update(pt) << cipher.final
+ tag1 = cipher.auth_tag
+
+ cipher = OpenSSL::Cipher.new("aes-128-gcm").encrypt
+ cipher.iv = "a" * 12
+ cipher.key = "x" * 16
+ ct2 = cipher.update(pt) << cipher.final
+ tag2 = cipher.auth_tag
+
+ assert_equal ct1, ct2
+ assert_equal tag1, tag2
end
- def test_aes_ocb_tag_len
- pt = "You should all use Authenticated Encryption!"
- cipher = OpenSSL::Cipher.new("aes-128-ocb").encrypt
- cipher.auth_tag_len = 14
- cipher.iv_len = 8
- key = cipher.random_key
- iv = cipher.random_iv
- cipher.auth_data = "aad"
- ct = cipher.update(pt) + cipher.final
- tag = cipher.auth_tag
- assert_equal(14, tag.size)
-
- decipher = OpenSSL::Cipher.new("aes-128-ocb").decrypt
- decipher.auth_tag_len = 14
- decipher.auth_tag = tag
- decipher.iv_len = 8
- decipher.key = key
- decipher.iv = iv
- decipher.auth_data = "aad"
- assert_equal(pt, decipher.update(ct) + decipher.final)
-
- decipher = OpenSSL::Cipher.new("aes-128-ocb").decrypt
- decipher.auth_tag_len = 9
- decipher.auth_tag = tag[0, 9]
- decipher.iv_len = 8
- decipher.key = key
- decipher.iv = iv
- decipher.auth_data = "aad"
+ def test_non_aead_cipher_set_auth_data
assert_raise(OpenSSL::Cipher::CipherError) {
- decipher.update(ct) + decipher.final
+ cipher = OpenSSL::Cipher.new("aes-128-cfb").encrypt
+ cipher.auth_data = "123"
}
- end if has_cipher?("aes-128-ocb")
+ end
private
- def new_encryptor(algo)
- cipher = OpenSSL::Cipher.new(algo)
- cipher.encrypt
- key = cipher.random_key
- iv = cipher.random_iv
- [cipher, key, iv]
+ def new_encryptor(algo, **kwargs)
+ OpenSSL::Cipher.new(algo).tap do |cipher|
+ cipher.encrypt
+ kwargs.each {|k, v| cipher.send(:"#{k}=", v) }
+ end
end
- def new_decryptor(algo, key, iv)
+ def new_decryptor(algo, **kwargs)
OpenSSL::Cipher.new(algo).tap do |cipher|
cipher.decrypt
- cipher.key = key
- cipher.iv = iv
+ kwargs.each {|k, v| cipher.send(:"#{k}=", v) }
end
end
-
end
end
diff --git a/test/openssl/test_config.rb b/test/openssl/test_config.rb
index 3e2e1273fb..8096375c07 100644
--- a/test/openssl/test_config.rb
+++ b/test/openssl/test_config.rb
@@ -1,8 +1,11 @@
# frozen_string_literal: false
require_relative 'utils'
+if defined?(OpenSSL)
+
class OpenSSL::TestConfig < OpenSSL::TestCase
def setup
+ super
file = Tempfile.open("openssl.cnf")
file << <<__EOD__
HOME = .
@@ -170,7 +173,7 @@ __EOC__
def test_value
# suppress deprecation warnings
- OpenSSL::TestUtils.silent do
+ EnvUtil.suppress_warning do
assert_equal('CA_default', @it.value('ca', 'default_ca'))
assert_equal(nil, @it.value('ca', 'no such key'))
assert_equal(nil, @it.value('no such section', 'no such key'))
@@ -183,7 +186,7 @@ __EOC__
end
def test_value_ENV
- OpenSSL::TestUtils.silent do
+ EnvUtil.suppress_warning do
key = ENV.keys.first
assert_not_nil(key) # make sure we have at least one ENV var.
assert_equal(ENV[key], @it.value('ENV', key))
@@ -198,7 +201,7 @@ __EOC__
end
def test_section
- OpenSSL::TestUtils.silent do
+ EnvUtil.suppress_warning do
assert_equal({'HOME' => '.'}, @it.section('default'))
assert_equal({'dir' => './demoCA', 'certs' => './certs'}, @it.section('CA_default'))
assert_equal({}, @it.section('no_such_section'))
@@ -296,4 +299,6 @@ __EOC__
@it['newsection'] = {'a' => 'b'}
assert_not_equal(@it.sections.sort, c.sections.sort)
end
-end if defined?(OpenSSL::TestUtils)
+end
+
+end
diff --git a/test/openssl/test_digest.rb b/test/openssl/test_digest.rb
index 51fc03bb87..2cb878b6fa 100644
--- a/test/openssl/test_digest.rb
+++ b/test/openssl/test_digest.rb
@@ -1,33 +1,28 @@
# frozen_string_literal: false
require_relative 'utils'
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestDigest < OpenSSL::TestCase
def setup
+ super
@d1 = OpenSSL::Digest.new("MD5")
@d2 = OpenSSL::Digest::MD5.new
- @md = Digest::MD5.new
- @data = "DATA"
- end
-
- def teardown
- super
- @d1 = @d2 = @md = nil
end
def test_digest
- assert_equal(@md.digest, @d1.digest)
- assert_equal(@md.hexdigest, @d1.hexdigest)
- @d1 << @data
- @d2 << @data
- @md << @data
- assert_equal(@md.digest, @d1.digest)
- assert_equal(@md.hexdigest, @d1.hexdigest)
- assert_equal(@d1.digest, @d2.digest)
- assert_equal(@d1.hexdigest, @d2.hexdigest)
- assert_equal(@md.digest, OpenSSL::Digest::MD5.digest(@data))
- assert_equal(@md.hexdigest, OpenSSL::Digest::MD5.hexdigest(@data))
+ null_hex = "d41d8cd98f00b204e9800998ecf8427e"
+ null_bin = [null_hex].pack("H*")
+ data = "DATA"
+ hex = "e44f9e348e41cb272efa87387728571b"
+ bin = [hex].pack("H*")
+ assert_equal(null_bin, @d1.digest)
+ assert_equal(null_hex, @d1.hexdigest)
+ @d1 << data
+ assert_equal(bin, @d1.digest)
+ assert_equal(hex, @d1.hexdigest)
+ assert_equal(bin, OpenSSL::Digest::MD5.digest(data))
+ assert_equal(hex, OpenSSL::Digest::MD5.hexdigest(data))
end
def test_eql
@@ -43,29 +38,26 @@ class OpenSSL::TestDigest < OpenSSL::TestCase
end
def test_dup
- @d1.update(@data)
+ @d1.update("DATA")
assert_equal(@d1.name, @d1.dup.name, "dup")
assert_equal(@d1.name, @d1.clone.name, "clone")
assert_equal(@d1.digest, @d1.clone.digest, "clone .digest")
end
def test_reset
- @d1.update(@data)
+ @d1.update("DATA")
dig1 = @d1.digest
@d1.reset
- @d1.update(@data)
+ @d1.update("DATA")
dig2 = @d1.digest
assert_equal(dig1, dig2, "reset")
end
def test_digest_constants
- algs = %w(MD4 MD5 RIPEMD160 SHA1)
- if OpenSSL::OPENSSL_VERSION_NUMBER < 0x10100000
+ algs = %w(MD4 MD5 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512)
+ if !libressl? && !openssl?(1, 1, 0)
algs += %w(DSS1 SHA)
end
- if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00908000
- algs += %w(SHA224 SHA256 SHA384 SHA512)
- end
algs.each do |alg|
assert_not_nil(OpenSSL::Digest.new(alg))
klass = OpenSSL::Digest.const_get(alg)
@@ -78,34 +70,32 @@ class OpenSSL::TestDigest < OpenSSL::TestCase
check_digest(OpenSSL::ASN1::ObjectId.new("SHA1"))
end
- if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00908000
- def encode16(str)
- str.unpack("H*").first
- end
+ def encode16(str)
+ str.unpack("H*").first
+ end
- def test_098_features
- sha224_a = "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5"
- sha256_a = "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
- sha384_a = "54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31"
- sha512_a = "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75"
-
- assert_equal(sha224_a, OpenSSL::Digest::SHA224.hexdigest("a"))
- assert_equal(sha256_a, OpenSSL::Digest::SHA256.hexdigest("a"))
- assert_equal(sha384_a, OpenSSL::Digest::SHA384.hexdigest("a"))
- assert_equal(sha512_a, OpenSSL::Digest::SHA512.hexdigest("a"))
-
- assert_equal(sha224_a, encode16(OpenSSL::Digest::SHA224.digest("a")))
- assert_equal(sha256_a, encode16(OpenSSL::Digest::SHA256.digest("a")))
- assert_equal(sha384_a, encode16(OpenSSL::Digest::SHA384.digest("a")))
- assert_equal(sha512_a, encode16(OpenSSL::Digest::SHA512.digest("a")))
- end
+ def test_sha2
+ sha224_a = "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5"
+ sha256_a = "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
+ sha384_a = "54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31"
+ sha512_a = "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75"
+
+ assert_equal(sha224_a, OpenSSL::Digest::SHA224.hexdigest("a"))
+ assert_equal(sha256_a, OpenSSL::Digest::SHA256.hexdigest("a"))
+ assert_equal(sha384_a, OpenSSL::Digest::SHA384.hexdigest("a"))
+ assert_equal(sha512_a, OpenSSL::Digest::SHA512.hexdigest("a"))
+
+ assert_equal(sha224_a, encode16(OpenSSL::Digest::SHA224.digest("a")))
+ assert_equal(sha256_a, encode16(OpenSSL::Digest::SHA256.digest("a")))
+ assert_equal(sha384_a, encode16(OpenSSL::Digest::SHA384.digest("a")))
+ assert_equal(sha512_a, encode16(OpenSSL::Digest::SHA512.digest("a")))
+ end
- def test_digest_by_oid_and_name_sha2
- check_digest(OpenSSL::ASN1::ObjectId.new("SHA224"))
- check_digest(OpenSSL::ASN1::ObjectId.new("SHA256"))
- check_digest(OpenSSL::ASN1::ObjectId.new("SHA384"))
- check_digest(OpenSSL::ASN1::ObjectId.new("SHA512"))
- end
+ def test_digest_by_oid_and_name_sha2
+ check_digest(OpenSSL::ASN1::ObjectId.new("SHA224"))
+ check_digest(OpenSSL::ASN1::ObjectId.new("SHA256"))
+ check_digest(OpenSSL::ASN1::ObjectId.new("SHA384"))
+ check_digest(OpenSSL::ASN1::ObjectId.new("SHA512"))
end
def test_openssl_digest
@@ -126,14 +116,6 @@ class OpenSSL::TestDigest < OpenSSL::TestCase
d = OpenSSL::Digest.new(oid.oid)
assert_not_nil(d)
end
-
- def libressl?
- OpenSSL::OPENSSL_VERSION.include?('LibreSSL')
- end
-
- def version_since(verary)
- (OpenSSL::OPENSSL_LIBRARY_VERSION.scan(/\d+/).map(&:to_i) <=> verary) != -1
- end
end
end
diff --git a/test/openssl/test_engine.rb b/test/openssl/test_engine.rb
index 2d394cf797..bb1123d516 100644
--- a/test/openssl/test_engine.rb
+++ b/test/openssl/test_engine.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: false
require_relative 'utils'
-class OpenSSL::TestEngine < OpenSSL::TestCase
+if defined?(OpenSSL) && defined?(OpenSSL::Engine)
+class OpenSSL::TestEngine < OpenSSL::TestCase
def test_engines_free # [ruby-dev:44173]
with_openssl <<-'end;'
OpenSSL::Engine.load("openssl")
@@ -51,39 +52,28 @@ class OpenSSL::TestEngine < OpenSSL::TestCase
end
def test_openssl_engine_cipher_rc4
- with_openssl <<-'end;'
- begin
- engine = get_engine
- algo = "RC4" #AES is not supported by openssl Engine (<=1.0.0e)
- data = "a" * 1000
- key = OpenSSL::Random.random_bytes(16)
- # suppress message from openssl Engine's RC4 cipher [ruby-core:41026]
- err_back = $stderr.dup
- $stderr.reopen(IO::NULL)
- encrypted = crypt_data(data, key, :encrypt) { engine.cipher(algo) }
- decrypted = crypt_data(encrypted, key, :decrypt) { OpenSSL::Cipher.new(algo) }
- assert_equal(data, decrypted)
- ensure
- if err_back
- $stderr.reopen(err_back)
- err_back.close
- end
- end
- end;
- end
+ begin
+ OpenSSL::Cipher.new("rc4")
+ rescue OpenSSL::Cipher::CipherError
+ pend "RC4 is not supported"
+ end
- def test_dup
- with_openssl <<-'end;'
+ with_openssl(<<-'end;', ignore_stderr: true)
engine = get_engine
- assert_raise(NoMethodError) { engine.dup }
+ algo = "RC4"
+ data = "a" * 1000
+ key = OpenSSL::Random.random_bytes(16)
+ encrypted = crypt_data(data, key, :encrypt) { engine.cipher(algo) }
+ decrypted = crypt_data(encrypted, key, :decrypt) { OpenSSL::Cipher.new(algo) }
+ assert_equal(data, decrypted)
end;
end
private
# this is required because OpenSSL::Engine methods change global state
- def with_openssl(code)
- assert_separately([{ "OSSL_MDEBUG" => nil }, "-ropenssl"], <<~"end;")
+ def with_openssl(code, **opts)
+ assert_separately([{ "OSSL_MDEBUG" => nil }, "-ropenssl"], <<~"end;", **opts)
require #{__FILE__.dump}
include OpenSSL::TestEngine::Utils
#{code}
@@ -102,5 +92,6 @@ class OpenSSL::TestEngine < OpenSSL::TestCase
cipher.update(data) + cipher.final
end
end
+end
-end if defined?(OpenSSL::TestUtils) && defined?(OpenSSL::Engine)
+end
diff --git a/test/openssl/test_fips.rb b/test/openssl/test_fips.rb
index 534dade02b..a577d7891e 100644
--- a/test/openssl/test_fips.rb
+++ b/test/openssl/test_fips.rb
@@ -1,15 +1,30 @@
# frozen_string_literal: false
require_relative 'utils'
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestFIPS < OpenSSL::TestCase
-
def test_fips_mode_is_reentrant
OpenSSL.fips_mode = false
OpenSSL.fips_mode = false
end
+ def test_fips_mode_get
+ return unless OpenSSL::OPENSSL_FIPS
+ assert_separately([{ "OSSL_MDEBUG" => nil }, "-ropenssl"], <<~"end;")
+ require #{__FILE__.dump}
+
+ begin
+ OpenSSL.fips_mode = true
+ assert OpenSSL.fips_mode == true, ".fips_mode returns true when .fips_mode=true"
+
+ OpenSSL.fips_mode = false
+ assert OpenSSL.fips_mode == false, ".fips_mode returns false when .fips_mode=false"
+ rescue OpenSSL::OpenSSLError
+ pend "Could not set FIPS mode (OpenSSL::OpenSSLError: \#$!); skipping"
+ end
+ end;
+ end
end
end
diff --git a/test/openssl/test_hmac.rb b/test/openssl/test_hmac.rb
index dd58e4ac98..831a5b6b37 100644
--- a/test/openssl/test_hmac.rb
+++ b/test/openssl/test_hmac.rb
@@ -1,33 +1,28 @@
-# coding: UTF-8
# frozen_string_literal: false
-
require_relative 'utils'
-class OpenSSL::TestHMAC < OpenSSL::TestCase
- def setup
- @digest = OpenSSL::Digest::MD5
- @key = "KEY"
- @data = "DATA"
- @h1 = OpenSSL::HMAC.new(@key, @digest.new)
- @h2 = OpenSSL::HMAC.new(@key, "MD5")
- end
+if defined?(OpenSSL)
+class OpenSSL::TestHMAC < OpenSSL::TestCase
def test_hmac
- @h1.update(@data)
- @h2.update(@data)
- assert_equal(@h1.digest, @h2.digest)
-
- assert_equal(OpenSSL::HMAC.digest(@digest.new, @key, @data), @h1.digest, "digest")
- assert_equal(OpenSSL::HMAC.hexdigest(@digest.new, @key, @data), @h1.hexdigest, "hexdigest")
-
- assert_equal(OpenSSL::HMAC.digest("MD5", @key, @data), @h2.digest, "digest")
- assert_equal(OpenSSL::HMAC.hexdigest("MD5", @key, @data), @h2.hexdigest, "hexdigest")
+ # RFC 2202 2. Test Cases for HMAC-MD5
+ hmac = OpenSSL::HMAC.new(["0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"].pack("H*"), "MD5")
+ hmac.update("Hi There")
+ assert_equal ["9294727a3638bb1c13f48ef8158bfc9d"].pack("H*"), hmac.digest
+ assert_equal "9294727a3638bb1c13f48ef8158bfc9d", hmac.hexdigest
+
+ # RFC 4231 4.2. Test Case 1
+ hmac = OpenSSL::HMAC.new(["0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"].pack("H*"), "SHA224")
+ hmac.update("Hi There")
+ assert_equal ["896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22"].pack("H*"), hmac.digest
+ assert_equal "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22", hmac.hexdigest
end
def test_dup
- @h1.update(@data)
- h = @h1.dup
- assert_equal(@h1.digest, h.digest, "dup digest")
+ h1 = OpenSSL::HMAC.new("KEY", "MD5")
+ h1.update("DATA")
+ h = h1.dup
+ assert_equal(h1.digest, h.digest, "dup digest")
end
def test_binary_update
@@ -38,9 +33,12 @@ class OpenSSL::TestHMAC < OpenSSL::TestCase
end
def test_reset_keep_key
- first = @h1.update("test").hexdigest
- @h2.reset
- second = @h2.update("test").hexdigest
+ h1 = OpenSSL::HMAC.new("KEY", "MD5")
+ first = h1.update("test").hexdigest
+ h1.reset
+ second = h1.update("test").hexdigest
assert_equal first, second
end
-end if defined?(OpenSSL::TestUtils)
+end
+
+end
diff --git a/test/openssl/test_kdf.rb b/test/openssl/test_kdf.rb
new file mode 100644
index 0000000000..5e1db80c5f
--- /dev/null
+++ b/test/openssl/test_kdf.rb
@@ -0,0 +1,183 @@
+# frozen_string_literal: false
+require_relative 'utils'
+
+if defined?(OpenSSL)
+
+class OpenSSL::TestKDF < OpenSSL::TestCase
+ def test_pkcs5_pbkdf2_hmac_compatibility
+ expected = OpenSSL::KDF.pbkdf2_hmac("password", salt: "salt", iterations: 1, length: 20, hash: "sha1")
+ assert_equal(expected, OpenSSL::PKCS5.pbkdf2_hmac("password", "salt", 1, 20, "sha1"))
+ assert_equal(expected, OpenSSL::PKCS5.pbkdf2_hmac_sha1("password", "salt", 1, 20))
+ end
+
+ def test_pbkdf2_hmac_sha1_rfc6070_c_1_len_20
+ p ="password"
+ s = "salt"
+ c = 1
+ dk_len = 20
+ raw = %w{ 0c 60 c8 0f 96 1f 0e 71
+ f3 a9 b5 24 af 60 12 06
+ 2f e0 37 a6 }
+ expected = [raw.join('')].pack('H*')
+ value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1")
+ assert_equal(expected, value)
+ end
+
+ def test_pbkdf2_hmac_sha1_rfc6070_c_2_len_20
+ p ="password"
+ s = "salt"
+ c = 2
+ dk_len = 20
+ raw = %w{ ea 6c 01 4d c7 2d 6f 8c
+ cd 1e d9 2a ce 1d 41 f0
+ d8 de 89 57 }
+ expected = [raw.join('')].pack('H*')
+ value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1")
+ assert_equal(expected, value)
+ end
+
+ def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_20
+ p ="password"
+ s = "salt"
+ c = 4096
+ dk_len = 20
+ raw = %w{ 4b 00 79 01 b7 65 48 9a
+ be ad 49 d9 26 f7 21 d0
+ 65 a4 29 c1 }
+ expected = [raw.join('')].pack('H*')
+ value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1")
+ assert_equal(expected, value)
+ end
+
+# takes too long!
+# def test_pbkdf2_hmac_sha1_rfc6070_c_16777216_len_20
+# p ="password"
+# s = "salt"
+# c = 16777216
+# dk_len = 20
+# raw = %w{ ee fe 3d 61 cd 4d a4 e4
+# e9 94 5b 3d 6b a2 15 8c
+# 26 34 e9 84 }
+# expected = [raw.join('')].pack('H*')
+# value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1")
+# assert_equal(expected, value)
+# end
+
+ def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_25
+ p ="passwordPASSWORDpassword"
+ s = "saltSALTsaltSALTsaltSALTsaltSALTsalt"
+ c = 4096
+ dk_len = 25
+
+ raw = %w{ 3d 2e ec 4f e4 1c 84 9b
+ 80 c8 d8 36 62 c0 e4 4a
+ 8b 29 1a 96 4c f2 f0 70
+ 38 }
+ expected = [raw.join('')].pack('H*')
+ value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1")
+ assert_equal(expected, value)
+ end
+
+ def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_16
+ p ="pass\0word"
+ s = "sa\0lt"
+ c = 4096
+ dk_len = 16
+ raw = %w{ 56 fa 6a a7 55 48 09 9d
+ cc 37 d7 f0 34 25 e0 c3 }
+ expected = [raw.join('')].pack('H*')
+ value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1")
+ assert_equal(expected, value)
+ end
+
+ def test_pbkdf2_hmac_sha256_c_20000_len_32
+ #unfortunately no official test vectors available yet for SHA-2
+ p ="password"
+ s = OpenSSL::Random.random_bytes(16)
+ c = 20000
+ dk_len = 32
+ value1 = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha256")
+ value2 = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha256")
+ assert_equal(value1, value2)
+ end
+
+ def test_scrypt_rfc7914_first
+ pend "scrypt is not implemented" unless OpenSSL::KDF.respond_to?(:scrypt) # OpenSSL >= 1.1.0
+ pass = ""
+ salt = ""
+ n = 16
+ r = 1
+ p = 1
+ dklen = 64
+ expected = B(%w{ 77 d6 57 62 38 65 7b 20 3b 19 ca 42 c1 8a 04 97
+ f1 6b 48 44 e3 07 4a e8 df df fa 3f ed e2 14 42
+ fc d0 06 9d ed 09 48 f8 32 6a 75 3a 0f c8 1f 17
+ e8 d3 e0 fb 2e 0d 36 28 cf 35 e2 0c 38 d1 89 06 })
+ assert_equal(expected, OpenSSL::KDF.scrypt(pass, salt: salt, N: n, r: r, p: p, length: dklen))
+ end
+
+ def test_scrypt_rfc7914_second
+ pend "scrypt is not implemented" unless OpenSSL::KDF.respond_to?(:scrypt) # OpenSSL >= 1.1.0
+ pass = "password"
+ salt = "NaCl"
+ n = 1024
+ r = 8
+ p = 16
+ dklen = 64
+ expected = B(%w{ fd ba be 1c 9d 34 72 00 78 56 e7 19 0d 01 e9 fe
+ 7c 6a d7 cb c8 23 78 30 e7 73 76 63 4b 37 31 62
+ 2e af 30 d9 2e 22 a3 88 6f f1 09 27 9d 98 30 da
+ c7 27 af b9 4a 83 ee 6d 83 60 cb df a2 cc 06 40 })
+ assert_equal(expected, OpenSSL::KDF.scrypt(pass, salt: salt, N: n, r: r, p: p, length: dklen))
+ end
+
+ def test_hkdf_rfc5869_test_case_1
+ pend "HKDF is not implemented" unless OpenSSL::KDF.respond_to?(:hkdf) # OpenSSL >= 1.1.0
+ hash = "sha256"
+ ikm = B("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")
+ salt = B("000102030405060708090a0b0c")
+ info = B("f0f1f2f3f4f5f6f7f8f9")
+ l = 42
+
+ okm = B("3cb25f25faacd57a90434f64d0362f2a" \
+ "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" \
+ "34007208d5b887185865")
+ assert_equal(okm, OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: l, hash: hash))
+ end
+
+ def test_hkdf_rfc5869_test_case_3
+ pend "HKDF is not implemented" unless OpenSSL::KDF.respond_to?(:hkdf) # OpenSSL >= 1.1.0
+ hash = "sha256"
+ ikm = B("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")
+ salt = B("")
+ info = B("")
+ l = 42
+
+ okm = B("8da4e775a563c18f715f802a063c5a31" \
+ "b8a11f5c5ee1879ec3454e5f3c738d2d" \
+ "9d201395faa4b61a96c8")
+ assert_equal(okm, OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: l, hash: hash))
+ end
+
+ def test_hkdf_rfc5869_test_case_4
+ pend "HKDF is not implemented" unless OpenSSL::KDF.respond_to?(:hkdf) # OpenSSL >= 1.1.0
+ hash = "sha1"
+ ikm = B("0b0b0b0b0b0b0b0b0b0b0b")
+ salt = B("000102030405060708090a0b0c")
+ info = B("f0f1f2f3f4f5f6f7f8f9")
+ l = 42
+
+ okm = B("085a01ea1b10f36933068b56efa5ad81" \
+ "a4f14b822f5b091568a9cdd4f155fda2" \
+ "c22e422478d305f3f896")
+ assert_equal(okm, OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: l, hash: hash))
+ end
+
+ private
+
+ def B(ary)
+ [Array(ary).join].pack("H*")
+ end
+end
+
+end
diff --git a/test/openssl/test_ns_spki.rb b/test/openssl/test_ns_spki.rb
index 4740c0b29e..aa1e61824f 100644
--- a/test/openssl/test_ns_spki.rb
+++ b/test/openssl/test_ns_spki.rb
@@ -1,10 +1,11 @@
# frozen_string_literal: false
require_relative 'utils'
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestNSSPI < OpenSSL::TestCase
def setup
+ super
# This request data is adopt from the specification of
# "Netscape Extensions for User Key Generation".
# -- http://wp.netscape.com/eng/security/comm4-keygen.html
@@ -16,8 +17,8 @@ class OpenSSL::TestNSSPI < OpenSSL::TestCase
end
def test_build_data
- key1 = OpenSSL::TestUtils::TEST_KEY_RSA1024
- key2 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+ key1 = Fixtures.pkey("rsa1024")
+ key2 = Fixtures.pkey("rsa2048")
spki = OpenSSL::Netscape::SPKI.new
spki.challenge = "RandomString"
spki.public_key = key1.public_key
diff --git a/test/openssl/test_ocsp.rb b/test/openssl/test_ocsp.rb
index a69fd60fda..50ad6c31f5 100644
--- a/test/openssl/test_ocsp.rb
+++ b/test/openssl/test_ocsp.rb
@@ -1,13 +1,11 @@
# frozen_string_literal: false
require_relative "utils"
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestOCSP < OpenSSL::TestCase
def setup
- now = Time.at(Time.now.to_i) # suppress usec
- dgst = OpenSSL::Digest::SHA1.new
-
+ super
# @ca_cert
# |
# @cert
@@ -15,37 +13,37 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase
# @cert2 @ocsp_cert
ca_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA")
- @ca_key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ @ca_key = Fixtures.pkey("rsa1024")
ca_exts = [
["basicConstraints", "CA:TRUE", true],
["keyUsage", "cRLSign,keyCertSign", true],
]
@ca_cert = OpenSSL::TestUtils.issue_cert(
- ca_subj, @ca_key, 1, now, now+3600, ca_exts, nil, nil, dgst)
+ ca_subj, @ca_key, 1, ca_exts, nil, nil)
cert_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA2")
- @cert_key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ @cert_key = Fixtures.pkey("rsa1024")
cert_exts = [
["basicConstraints", "CA:TRUE", true],
["keyUsage", "cRLSign,keyCertSign", true],
]
@cert = OpenSSL::TestUtils.issue_cert(
- cert_subj, @cert_key, 5, now, now+3600, cert_exts, @ca_cert, @ca_key, dgst)
+ cert_subj, @cert_key, 5, cert_exts, @ca_cert, @ca_key)
cert2_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCert")
- @cert2_key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ @cert2_key = Fixtures.pkey("rsa1024")
cert2_exts = [
]
@cert2 = OpenSSL::TestUtils.issue_cert(
- cert2_subj, @cert2_key, 10, now, now+3600, cert2_exts, @cert, @cert_key, dgst)
+ cert2_subj, @cert2_key, 10, cert2_exts, @cert, @cert_key)
ocsp_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCAOCSP")
- @ocsp_key = OpenSSL::TestUtils::TEST_KEY_RSA2048
+ @ocsp_key = Fixtures.pkey("rsa2048")
ocsp_exts = [
["extendedKeyUsage", "OCSPSigning", true],
]
@ocsp_cert = OpenSSL::TestUtils.issue_cert(
- ocsp_subj, @ocsp_key, 100, now, now+3600, ocsp_exts, @cert, @cert_key, "SHA256")
+ ocsp_subj, @ocsp_key, 100, ocsp_exts, @cert, @cert_key)
end
def test_new_certificate_id
@@ -124,14 +122,29 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase
assert_equal true, req.verify([@cert], store, OpenSSL::OCSP::NOINTERN)
ret = req.verify([@cert], store)
- if ret || OpenSSL::OPENSSL_VERSION =~ /OpenSSL/ && OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10002000
+ if ret || openssl?(1, 0, 2)
assert_equal true, ret
else
# RT2560; OCSP_request_verify() does not find signer cert from 'certs' when
# OCSP_NOINTERN is not specified.
- # fixed by OpenSSL 1.0.1j, 1.0.2 and LibreSSL 2.4.2
+ # fixed by OpenSSL 1.0.1j, 1.0.2
pend "RT2560: ocsp_req_find_signer"
end
+
+ # not signed
+ req = OpenSSL::OCSP::Request.new.add_certid(cid)
+ assert_equal false, req.verify([], store)
+ end
+
+ def test_request_is_signed
+ cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert)
+ req = OpenSSL::OCSP::Request.new
+ req.add_certid(cid)
+ assert_equal false, req.signed?
+ assert_equal false, OpenSSL::OCSP::Request.new(req.to_der).signed?
+ req.sign(@cert, @cert_key, [])
+ assert_equal true, req.signed?
+ assert_equal true, OpenSSL::OCSP::Request.new(req.to_der).signed?
end
def test_request_nonce
@@ -249,11 +262,6 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase
bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, -400, -300, nil, [])
bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, nil, Time.now + 100, nil, nil)
- if bres.responses[2].check_validity # thisUpdate is in future; must fail
- # LibreSSL bug; skip for now
- pend "OCSP_check_validity() is broken"
- end
-
single1 = bres.responses[0]
assert_equal false, single1.check_validity
assert_equal false, single1.check_validity(30)
@@ -262,6 +270,8 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase
assert_equal true, single2.check_validity
assert_equal true, single2.check_validity(0, 500)
assert_equal false, single2.check_validity(0, 200)
+ single3 = bres.responses[2]
+ assert_equal false, single3.check_validity
end
def test_response
diff --git a/test/openssl/test_pair.rb b/test/openssl/test_pair.rb
index 721e8e370e..08c15a0f75 100644
--- a/test/openssl/test_pair.rb
+++ b/test/openssl/test_pair.rb
@@ -1,61 +1,46 @@
# frozen_string_literal: false
require_relative 'utils'
-
-if defined?(OpenSSL::TestUtils)
-
-require 'socket'
require_relative 'ut_eof'
+if defined?(OpenSSL)
+
module OpenSSL::SSLPairM
- def server
- host = "127.0.0.1"
- port = 0
- ctx = OpenSSL::SSL::SSLContext.new()
- ctx.ciphers = "ADH"
- ctx.security_level = 0
- ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 }
- tcps = create_tcp_server(host, port)
- ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
- return ssls
+ def setup
+ svr_dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
+ ee_exts = [
+ ["keyUsage", "keyEncipherment,digitalSignature", true],
+ ]
+ @svr_key = OpenSSL::TestUtils::Fixtures.pkey("rsa-1")
+ @svr_cert = issue_cert(svr_dn, @svr_key, 1, ee_exts, nil, nil)
end
- def client(port)
+ def ssl_pair
host = "127.0.0.1"
- ctx = OpenSSL::SSL::SSLContext.new()
- ctx.ciphers = "ADH"
- ctx.security_level = 0
- s = create_tcp_client(host, port)
- ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
- ssl.connect
- ssl.sync_close = true
- ssl
- end
+ tcps = create_tcp_server(host, 0)
+ port = tcps.connect_address.ip_port
- def ssl_pair
- ssls = server
th = Thread.new {
+ sctx = OpenSSL::SSL::SSLContext.new
+ sctx.cert = @svr_cert
+ sctx.key = @svr_key
+ sctx.tmp_dh_callback = proc { OpenSSL::TestUtils::Fixtures.pkey("dh-1") }
+ sctx.options |= OpenSSL::SSL::OP_NO_COMPRESSION
+ ssls = OpenSSL::SSL::SSLServer.new(tcps, sctx)
ns = ssls.accept
ssls.close
ns
}
- port = ssls.to_io.local_address.ip_port
- c = client(port)
+
+ tcpc = create_tcp_client(host, port)
+ c = OpenSSL::SSL::SSLSocket.new(tcpc)
+ c.connect
s = th.value
- if block_given?
- begin
- yield c, s
- ensure
- c.close unless c.closed?
- s.close unless s.closed?
- end
- else
- return c, s
- end
+
+ yield c, s
ensure
- if th&.alive?
- th.kill
- th.join
- end
+ tcpc&.close
+ tcps&.close
+ s&.close
end
end
@@ -85,23 +70,27 @@ end
module OpenSSL::TestEOF1M
def open_file(content)
- s1, s2 = ssl_pair
- th = Thread.new { s2 << content; s2.close }
- yield s1
- ensure
- th.join if th
- s1.close
+ ssl_pair { |s1, s2|
+ begin
+ th = Thread.new { s2 << content; s2.close }
+ yield s1
+ ensure
+ th&.join
+ end
+ }
end
end
module OpenSSL::TestEOF2M
def open_file(content)
- s1, s2 = ssl_pair
- th = Thread.new { s1 << content; s1.close }
- yield s2
- ensure
- th.join if th
- s2.close
+ ssl_pair { |s1, s2|
+ begin
+ th = Thread.new { s1 << content; s1.close }
+ yield s2
+ ensure
+ th&.join
+ end
+ }
end
end
@@ -189,6 +178,27 @@ module OpenSSL::TestPairM
}
end
+ def test_multibyte_read_write
+ # German a umlaut
+ auml = [%w{ C3 A4 }.join('')].pack('H*')
+ auml.force_encoding(Encoding::UTF_8)
+ bsize = auml.bytesize
+
+ ssl_pair { |s1, s2|
+ assert_equal bsize, s1.write(auml)
+ read = s2.read(bsize)
+ assert_equal Encoding::ASCII_8BIT, read.encoding
+ assert_equal bsize, read.bytesize
+ assert_equal auml, read.force_encoding(Encoding::UTF_8)
+
+ s1.puts(auml)
+ read = s2.gets
+ assert_equal Encoding::ASCII_8BIT, read.encoding
+ assert_equal bsize + 1, read.bytesize
+ assert_equal auml + "\n", read.force_encoding(Encoding::UTF_8)
+ }
+ end
+
def test_read_nonblock
ssl_pair {|s1, s2|
err = nil
@@ -208,7 +218,7 @@ module OpenSSL::TestPairM
assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10) }
assert_equal("def\n", ret)
s1.close
- sleep 0.1
+ IO.select([s2])
assert_raise(EOFError) { s2.read_nonblock(10) }
}
end
@@ -224,55 +234,71 @@ module OpenSSL::TestPairM
assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10, exception: false) }
assert_equal("def\n", ret)
s1.close
- sleep 0.1
+ IO.select([s2])
assert_equal(nil, s2.read_nonblock(10, exception: false))
}
end
- def write_nonblock(socket, meth, str)
- ret = socket.send(meth, str)
- ret.is_a?(Symbol) ? 0 : ret
- end
+ def test_read_with_outbuf
+ ssl_pair { |s1, s2|
+ s1.write("abc\n")
+ buf = ""
+ ret = s2.read(2, buf)
+ assert_same ret, buf
+ assert_equal "ab", ret
+
+ buf = "garbage"
+ ret = s2.read(2, buf)
+ assert_same ret, buf
+ assert_equal "c\n", ret
- def write_nonblock_no_ex(socket, str)
- ret = socket.write_nonblock str, exception: false
- ret.is_a?(Symbol) ? 0 : ret
+ buf = "garbage"
+ assert_equal :wait_readable, s2.read_nonblock(100, buf, exception: false)
+ assert_equal "", buf
+
+ s1.close
+ buf = "garbage"
+ assert_equal nil, s2.read(100, buf)
+ assert_equal "", buf
+ }
end
def test_write_nonblock
ssl_pair {|s1, s2|
- n = 0
- begin
- n += write_nonblock s1, :write_nonblock, "a" * 100000
- n += write_nonblock s1, :write_nonblock, "b" * 100000
- n += write_nonblock s1, :write_nonblock, "c" * 100000
- n += write_nonblock s1, :write_nonblock, "d" * 100000
- n += write_nonblock s1, :write_nonblock, "e" * 100000
- n += write_nonblock s1, :write_nonblock, "f" * 100000
- rescue IO::WaitWritable
+ assert_equal 3, s1.write_nonblock("foo")
+ assert_equal "foo", s2.read(3)
+
+ data = "x" * 16384
+ written = 0
+ while true
+ begin
+ written += s1.write_nonblock(data)
+ rescue IO::WaitWritable, IO::WaitReadable
+ break
+ end
end
- s1.close
- assert_equal(n, s2.read.length)
+ assert written > 0
+ assert_equal written, s2.read(written).bytesize
}
end
def test_write_nonblock_no_exceptions
ssl_pair {|s1, s2|
- n = 0
- begin
- n += write_nonblock_no_ex s1, "a" * 100000
- n += write_nonblock_no_ex s1, "b" * 100000
- n += write_nonblock_no_ex s1, "c" * 100000
- n += write_nonblock_no_ex s1, "d" * 100000
- n += write_nonblock_no_ex s1, "e" * 100000
- n += write_nonblock_no_ex s1, "f" * 100000
- rescue OpenSSL::SSL::SSLError => e
- # on some platforms (maybe depend on OpenSSL version), writing to
- # SSLSocket after SSL_ERROR_WANT_WRITE causes this error.
- raise e if n == 0
+ assert_equal 3, s1.write_nonblock("foo", exception: false)
+ assert_equal "foo", s2.read(3)
+
+ data = "x" * 16384
+ written = 0
+ while true
+ case ret = s1.write_nonblock(data, exception: false)
+ when :wait_readable, :wait_writable
+ break
+ else
+ written += ret
+ end
end
- s1.close
- assert_equal(n, s2.read.length)
+ assert written > 0
+ assert_equal written, s2.read(written).bytesize
}
end
@@ -301,7 +327,7 @@ module OpenSSL::TestPairM
# fill up a socket so we hit EAGAIN
written = String.new
n = 0
- buf = 'a' * 11
+ buf = 'a' * 4099
case ret = s1.write_nonblock(buf, exception: false)
when :wait_readable then break
when :wait_writable then break
@@ -336,6 +362,15 @@ module OpenSSL::TestPairM
}
end
+ def test_write_multiple_arguments
+ ssl_pair {|s1, s2|
+ str1 = "foo"; str2 = "bar"
+ assert_equal 6, s1.write(str1, str2)
+ s1.close
+ assert_equal "foobar", s2.read
+ }
+ end
+
def test_partial_tls_record_read_nonblock
ssl_pair { |s1, s2|
# the beginning of a TLS record
@@ -360,9 +395,9 @@ module OpenSSL::TestPairM
def test_connect_accept_nonblock_no_exception
ctx2 = OpenSSL::SSL::SSLContext.new
- ctx2.ciphers = "ADH"
- ctx2.security_level = 0
- ctx2.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 }
+ ctx2.cert = @svr_cert
+ ctx2.key = @svr_key
+ ctx2.tmp_dh_callback = proc { OpenSSL::TestUtils::Fixtures.pkey("dh-1") }
sock1, sock2 = tcp_pair
@@ -371,8 +406,6 @@ module OpenSSL::TestPairM
assert_equal :wait_readable, accepted
ctx1 = OpenSSL::SSL::SSLContext.new
- ctx1.ciphers = "ADH"
- ctx1.security_level = 0
s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1)
th = Thread.new do
rets = []
@@ -409,56 +442,47 @@ module OpenSSL::TestPairM
end
def test_connect_accept_nonblock
- ctx = OpenSSL::SSL::SSLContext.new()
- ctx.ciphers = "ADH"
- ctx.security_level = 0
- ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 }
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.cert = @svr_cert
+ ctx.key = @svr_key
+ ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::Fixtures.pkey("dh-1") }
sock1, sock2 = tcp_pair
th = Thread.new {
s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx)
- s2.sync_close = true
- begin
+ 5.times {
+ begin
+ break s2.accept_nonblock
+ rescue IO::WaitReadable
+ IO.select([s2], nil, nil, 1)
+ rescue IO::WaitWritable
+ IO.select(nil, [s2], nil, 1)
+ end
sleep 0.2
- s2.accept_nonblock
+ }
+ }
+
+ s1 = OpenSSL::SSL::SSLSocket.new(sock1)
+ 5.times {
+ begin
+ break s1.connect_nonblock
rescue IO::WaitReadable
- IO.select([s2])
- retry
+ IO.select([s1], nil, nil, 1)
rescue IO::WaitWritable
- IO.select(nil, [s2])
- retry
+ IO.select(nil, [s1], nil, 1)
end
- s2
- }
-
- sleep 0.1
- ctx = OpenSSL::SSL::SSLContext.new()
- ctx.ciphers = "ADH"
- ctx.security_level = 0
- s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx)
- begin
sleep 0.2
- s1.connect_nonblock
- rescue IO::WaitReadable
- IO.select([s1])
- retry
- rescue IO::WaitWritable
- IO.select(nil, [s1])
- retry
- end
- s1.sync_close = true
+ }
s2 = th.value
s1.print "a\ndef"
assert_equal("a\n", s2.gets)
ensure
- th.join if th
- s1.close if s1 && !s1.closed?
- s2.close if s2 && !s2.closed?
- sock1.close if sock1 && !sock1.closed?
- sock2.close if sock2 && !sock2.closed?
+ sock1&.close
+ sock2&.close
+ th&.join
end
end
diff --git a/test/openssl/test_pkcs12.rb b/test/openssl/test_pkcs12.rb
index 4f2544dfee..de8e35ed79 100644
--- a/test/openssl/test_pkcs12.rb
+++ b/test/openssl/test_pkcs12.rb
@@ -1,25 +1,20 @@
# frozen_string_literal: false
require_relative "utils"
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
module OpenSSL
class TestPKCS12 < OpenSSL::TestCase
- include OpenSSL::TestUtils
-
def setup
+ super
ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
-
- now = Time.now
ca_exts = [
["basicConstraints","CA:TRUE",true],
["keyUsage","keyCertSign, cRLSign",true],
["subjectKeyIdentifier","hash",false],
["authorityKeyIdentifier","keyid:always",false],
]
-
- @cacert = issue_cert(ca, TEST_KEY_RSA2048, 1, now, now+3600, ca_exts,
- nil, nil, OpenSSL::Digest::SHA1.new)
+ @cacert = issue_cert(ca, Fixtures.pkey("rsa2048"), 1, ca_exts, nil, nil)
inter_ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=Intermediate CA")
inter_ca_key = OpenSSL::PKey.read <<-_EOS_
@@ -39,28 +34,26 @@ FJx7d3f29gkzynCLJDkCQGQZlEZJC4vWmWJGRKJ24P6MyQn3VsPfErSKOg4lvyM3
Li8JsX5yIiuVYaBg/6ha3tOg4TCa5K/3r3tVliRZ2Es=
-----END RSA PRIVATE KEY-----
_EOS_
-
- @inter_cacert = issue_cert(inter_ca, inter_ca_key, 2, now, now+3600, ca_exts,
- @cacert, TEST_KEY_RSA2048, OpenSSL::Digest::SHA1.new)
+ @inter_cacert = issue_cert(inter_ca, inter_ca_key, 2, ca_exts, @cacert, Fixtures.pkey("rsa2048"))
exts = [
["keyUsage","digitalSignature",true],
["subjectKeyIdentifier","hash",false],
]
ee = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=Ruby PKCS12 Test Certificate")
- @mycert = issue_cert(ee, TEST_KEY_RSA1024, 3, now, now+3600, exts,
- @inter_cacert, inter_ca_key, OpenSSL::Digest::SHA1.new)
+ @mykey = Fixtures.pkey("rsa1024")
+ @mycert = issue_cert(ee, @mykey, 3, exts, @inter_cacert, inter_ca_key)
end
def test_create
pkcs12 = OpenSSL::PKCS12.create(
"omg",
"hello",
- TEST_KEY_RSA1024,
+ @mykey,
@mycert
)
- assert_equal @mycert, pkcs12.certificate
- assert_equal TEST_KEY_RSA1024, pkcs12.key
+ assert_equal @mycert.to_der, pkcs12.certificate.to_der
+ assert_equal @mykey.to_der, pkcs12.key.to_der
assert_nil pkcs12.ca_certs
end
@@ -68,11 +61,11 @@ Li8JsX5yIiuVYaBg/6ha3tOg4TCa5K/3r3tVliRZ2Es=
pkcs12 = OpenSSL::PKCS12.create(
nil,
"hello",
- TEST_KEY_RSA1024,
+ @mykey,
@mycert
)
- assert_equal @mycert, pkcs12.certificate
- assert_equal TEST_KEY_RSA1024, pkcs12.key
+ assert_equal @mycert.to_der, pkcs12.certificate.to_der
+ assert_equal @mykey.to_der, pkcs12.key.to_der
assert_nil pkcs12.ca_certs
decoded = OpenSSL::PKCS12.new(pkcs12.to_der)
@@ -85,7 +78,7 @@ Li8JsX5yIiuVYaBg/6ha3tOg4TCa5K/3r3tVliRZ2Es=
pkcs12 = OpenSSL::PKCS12.create(
"omg",
"hello",
- TEST_KEY_RSA1024,
+ @mykey,
@mycert,
chain
)
@@ -100,7 +93,7 @@ Li8JsX5yIiuVYaBg/6ha3tOg4TCa5K/3r3tVliRZ2Es=
pkcs12 = OpenSSL::PKCS12.create(
passwd,
"hello",
- TEST_KEY_RSA1024,
+ @mykey,
@mycert,
chain
)
@@ -110,7 +103,7 @@ Li8JsX5yIiuVYaBg/6ha3tOg4TCa5K/3r3tVliRZ2Es=
assert_include_cert @cacert, decoded.ca_certs
assert_include_cert @inter_cacert, decoded.ca_certs
assert_cert @mycert, decoded.certificate
- assert_equal TEST_KEY_RSA1024.to_der, decoded.key.to_der
+ assert_equal @mykey.to_der, decoded.key.to_der
end
def test_create_with_bad_nid
@@ -118,7 +111,7 @@ Li8JsX5yIiuVYaBg/6ha3tOg4TCa5K/3r3tVliRZ2Es=
OpenSSL::PKCS12.create(
"omg",
"hello",
- TEST_KEY_RSA1024,
+ @mykey,
@mycert,
[],
"foo"
@@ -130,7 +123,7 @@ Li8JsX5yIiuVYaBg/6ha3tOg4TCa5K/3r3tVliRZ2Es=
OpenSSL::PKCS12.create(
"omg",
"hello",
- TEST_KEY_RSA1024,
+ @mykey,
@mycert,
[],
nil,
@@ -142,7 +135,7 @@ Li8JsX5yIiuVYaBg/6ha3tOg4TCa5K/3r3tVliRZ2Es=
OpenSSL::PKCS12.create(
"omg",
"hello",
- TEST_KEY_RSA1024,
+ @mykey,
@mycert,
[],
nil,
@@ -156,7 +149,7 @@ Li8JsX5yIiuVYaBg/6ha3tOg4TCa5K/3r3tVliRZ2Es=
OpenSSL::PKCS12.create(
"omg",
"hello",
- TEST_KEY_RSA1024,
+ @mykey,
@mycert,
[],
nil,
@@ -169,7 +162,7 @@ Li8JsX5yIiuVYaBg/6ha3tOg4TCa5K/3r3tVliRZ2Es=
OpenSSL::PKCS12.create(
"omg",
"hello",
- TEST_KEY_RSA1024,
+ @mykey,
@mycert,
[],
nil,
@@ -222,7 +215,7 @@ vyl2WuMdEwQIMWFFphPkIUICAggA
EOF
p12 = OpenSSL::PKCS12.new(str, "abc123")
- assert_equal TEST_KEY_RSA1024.to_der, p12.key.to_der
+ assert_equal @mykey.to_der, p12.key.to_der
assert_equal @mycert.subject.to_der, p12.certificate.subject.to_der
assert_equal [], Array(p12.ca_certs)
end
@@ -281,13 +274,13 @@ Kw4DAhoFAAQUYAuwVtGD1TdgbFK4Yal2XBgwUR4ECEawsN3rNaa6AgIIAA==
EOF
p12 = OpenSSL::PKCS12.new(str, "abc123")
- assert_equal TEST_KEY_RSA1024.to_der, p12.key.to_der
+ assert_equal @mykey.to_der, p12.key.to_der
assert_equal nil, p12.certificate
assert_equal [], Array(p12.ca_certs)
end
def test_dup
- p12 = OpenSSL::PKCS12.create("pass", "name", TEST_KEY_RSA1024, @mycert)
+ p12 = OpenSSL::PKCS12.create("pass", "name", @mykey, @mycert)
assert_equal p12.to_der, p12.dup.to_der
end
@@ -314,7 +307,6 @@ Kw4DAhoFAAQUYAuwVtGD1TdgbFK4Yal2XBgwUR4ECEawsN3rNaa6AgIIAA==
end
false
end
-
end
end
diff --git a/test/openssl/test_pkcs5.rb b/test/openssl/test_pkcs5.rb
deleted file mode 100644
index ad8132c263..0000000000
--- a/test/openssl/test_pkcs5.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-# frozen_string_literal: false
-require_relative 'utils'
-
-class OpenSSL::TestPKCS5 < OpenSSL::TestCase
-
- def test_pbkdf2_hmac_sha1_rfc6070_c_1_len_20
- p ="password"
- s = "salt"
- c = 1
- dk_len = 20
- raw = %w{ 0c 60 c8 0f 96 1f 0e 71
- f3 a9 b5 24 af 60 12 06
- 2f e0 37 a6 }
- expected = [raw.join('')].pack('H*')
- value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
- assert_equal(expected, value)
- end
-
- def test_pbkdf2_hmac_sha1_rfc6070_c_2_len_20
- p ="password"
- s = "salt"
- c = 2
- dk_len = 20
- raw = %w{ ea 6c 01 4d c7 2d 6f 8c
- cd 1e d9 2a ce 1d 41 f0
- d8 de 89 57 }
- expected = [raw.join('')].pack('H*')
- value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
- assert_equal(expected, value)
- end
-
- def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_20
- p ="password"
- s = "salt"
- c = 4096
- dk_len = 20
- raw = %w{ 4b 00 79 01 b7 65 48 9a
- be ad 49 d9 26 f7 21 d0
- 65 a4 29 c1 }
- expected = [raw.join('')].pack('H*')
- value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
- assert_equal(expected, value)
- end
-
-# takes too long!
-# def test_pbkdf2_hmac_sha1_rfc6070_c_16777216_len_20
-# p ="password"
-# s = "salt"
-# c = 16777216
-# dk_len = 20
-# raw = %w{ ee fe 3d 61 cd 4d a4 e4
-# e9 94 5b 3d 6b a2 15 8c
-# 26 34 e9 84 }
-# expected = [raw.join('')].pack('H*')
-# value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
-# assert_equal(expected, value)
-# end
-
- def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_25
- p ="passwordPASSWORDpassword"
- s = "saltSALTsaltSALTsaltSALTsaltSALTsalt"
- c = 4096
- dk_len = 25
-
- raw = %w{ 3d 2e ec 4f e4 1c 84 9b
- 80 c8 d8 36 62 c0 e4 4a
- 8b 29 1a 96 4c f2 f0 70
- 38 }
- expected = [raw.join('')].pack('H*')
- value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
- assert_equal(expected, value)
- end
-
- def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_16
- p ="pass\0word"
- s = "sa\0lt"
- c = 4096
- dk_len = 16
- raw = %w{ 56 fa 6a a7 55 48 09 9d
- cc 37 d7 f0 34 25 e0 c3 }
- expected = [raw.join('')].pack('H*')
- value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
- assert_equal(expected, value)
- end
-
- def test_pbkdf2_hmac_sha256_c_20000_len_32
- #unfortunately no official test vectors available yet for SHA-2
- p ="password"
- s = OpenSSL::Random.random_bytes(16)
- c = 20000
- dk_len = 32
- digest = OpenSSL::Digest::SHA256.new
- value1 = OpenSSL::PKCS5.pbkdf2_hmac(p, s, c, dk_len, digest)
- value2 = OpenSSL::PKCS5.pbkdf2_hmac(p, s, c, dk_len, digest)
- assert_equal(value1, value2)
- end if OpenSSL::PKCS5.respond_to?(:pbkdf2_hmac)
-
-end if defined?(OpenSSL::TestUtils)
diff --git a/test/openssl/test_pkcs7.rb b/test/openssl/test_pkcs7.rb
index def4910ccd..6437112b74 100644
--- a/test/openssl/test_pkcs7.rb
+++ b/test/openssl/test_pkcs7.rb
@@ -1,38 +1,31 @@
# frozen_string_literal: false
require_relative 'utils'
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestPKCS7 < OpenSSL::TestCase
def setup
- @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
- @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+ super
+ @rsa1024 = Fixtures.pkey("rsa1024")
+ @rsa2048 = Fixtures.pkey("rsa2048")
ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
- now = Time.now
ca_exts = [
["basicConstraints","CA:TRUE",true],
["keyUsage","keyCertSign, cRLSign",true],
["subjectKeyIdentifier","hash",false],
["authorityKeyIdentifier","keyid:always",false],
]
- @ca_cert = issue_cert(ca, @rsa2048, 1, now, now+3600, ca_exts,
- nil, nil, OpenSSL::Digest::SHA1.new)
+ @ca_cert = issue_cert(ca, @rsa2048, 1, ca_exts, nil, nil)
ee_exts = [
["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true],
["authorityKeyIdentifier","keyid:always",false],
["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
]
- @ee1_cert = issue_cert(ee1, @rsa1024, 2, now, now+1800, ee_exts,
- @ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
- @ee2_cert = issue_cert(ee2, @rsa1024, 3, now, now+1800, ee_exts,
- @ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
- end
-
- def issue_cert(*args)
- OpenSSL::TestUtils.issue_cert(*args)
+ @ee1_cert = issue_cert(ee1, @rsa1024, 2, ee_exts, @ca_cert, @rsa2048)
+ @ee2_cert = issue_cert(ee2, @rsa1024, 3, ee_exts, @ca_cert, @rsa2048)
end
def test_signed
@@ -54,7 +47,7 @@ class OpenSSL::TestPKCS7 < OpenSSL::TestCase
assert_equal(@ee1_cert.serial, signers[0].serial)
assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
- # Normaly OpenSSL tries to translate the supplied content into canonical
+ # Normally OpenSSL tries to translate the supplied content into canonical
# MIME format (e.g. a newline character is converted into CR+LF).
# If the content is a binary, PKCS7::BINARY flag should be used.
@@ -140,6 +133,8 @@ class OpenSSL::TestPKCS7 < OpenSSL::TestCase
assert_equal(@ca_cert.subject.to_s, recip[1].issuer.to_s)
assert_equal(3, recip[1].serial)
assert_equal(data, p7.decrypt(@rsa1024, @ee2_cert))
+
+ assert_equal(data, p7.decrypt(@rsa1024))
end
def test_graceful_parsing_failure #[ruby-core:43250]
diff --git a/test/openssl/test_pkey.rb b/test/openssl/test_pkey.rb
deleted file mode 100644
index 79647c8f1d..0000000000
--- a/test/openssl/test_pkey.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# frozen_string_literal: false
-require_relative "utils"
-
-if defined?(OpenSSL::TestUtils)
-
-class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
- PKEYS = {
- OpenSSL::PKey::RSA => {
- key: OpenSSL::TestUtils::TEST_KEY_RSA1024,
- digest: OpenSSL::Digest::SHA1,
- },
- OpenSSL::PKey::DSA => {
- key: OpenSSL::TestUtils::TEST_KEY_DSA512,
- digest: OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST,
- },
- }
- if defined?(OpenSSL::PKey::EC)
- PKEYS[OpenSSL::PKey::EC] = {
- key: OpenSSL::TestUtils::TEST_KEY_EC_P256V1,
- digest: OpenSSL::Digest::SHA1,
- }
- end
-
- def test_sign_verify
- data = "Sign me!"
- invalid_data = "Sign me?"
- PKEYS.each do |klass, prop|
- key = prop[:key]
- pub_key = dup_public(prop[:key])
- digest = prop[:digest].new
- signature = key.sign(digest, data)
- assert_equal(true, pub_key.verify(digest, signature, data))
- assert_equal(false, pub_key.verify(digest, signature, invalid_data))
- # digest state is irrelevant
- digest << "unya"
- assert_equal(true, pub_key.verify(digest, signature, data))
- assert_equal(false, pub_key.verify(digest, signature, invalid_data))
-
- if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10000000
- digest = OpenSSL::Digest::SHA256.new
- signature = key.sign(digest, data)
- assert_equal(true, pub_key.verify(digest, signature, data))
- assert_equal(false, pub_key.verify(digest, signature, invalid_data))
- end
- end
- end
-end
-
-end
diff --git a/test/openssl/test_pkey_dh.rb b/test/openssl/test_pkey_dh.rb
index 470c952e21..8c8fbaeefb 100644
--- a/test/openssl/test_pkey_dh.rb
+++ b/test/openssl/test_pkey_dh.rb
@@ -1,29 +1,11 @@
# frozen_string_literal: false
require_relative 'utils'
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL) && defined?(OpenSSL::PKey::DH)
class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase
- DH1024 = OpenSSL::TestUtils::TEST_KEY_DH1024
-
NEW_KEYLEN = 256
- def test_DEFAULT_parameters
- list = {
- 1024 => OpenSSL::PKey::DH::DEFAULT_1024,
- 2048 => OpenSSL::PKey::DH::DEFAULT_2048,
- }
-
- list.each do |expected_size, dh|
- assert_equal expected_size, dh.p.num_bits
- assert_predicate dh.p, :prime?
- result, remainder = (dh.p - 1) / 2
- assert_predicate result, :prime?
- assert_equal 0, remainder
- assert_no_key dh
- end
- end
-
def test_new
dh = OpenSSL::PKey::DH.new(NEW_KEYLEN)
assert_key(dh)
@@ -37,12 +19,13 @@ class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase
end
def test_DHparams
+ dh1024 = Fixtures.pkey("dh1024")
asn1 = OpenSSL::ASN1::Sequence([
- OpenSSL::ASN1::Integer(DH1024.p),
- OpenSSL::ASN1::Integer(DH1024.g)
+ OpenSSL::ASN1::Integer(dh1024.p),
+ OpenSSL::ASN1::Integer(dh1024.g)
])
key = OpenSSL::PKey::DH.new(asn1.to_der)
- assert_same_dh dup_public(DH1024), key
+ assert_same_dh dup_public(dh1024), key
pem = <<~EOF
-----BEGIN DH PARAMETERS-----
@@ -52,14 +35,14 @@ class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase
-----END DH PARAMETERS-----
EOF
key = OpenSSL::PKey::DH.new(pem)
- assert_same_dh dup_public(DH1024), key
+ assert_same_dh dup_public(dh1024), key
- assert_equal asn1.to_der, DH1024.to_der
- assert_equal pem, DH1024.export
+ assert_equal asn1.to_der, dh1024.to_der
+ assert_equal pem, dh1024.export
end
def test_public_key
- dh = OpenSSL::TestUtils::TEST_KEY_DH1024
+ dh = Fixtures.pkey("dh1024")
public_key = dh.public_key
assert_no_key(public_key) #implies public_key.public? is false!
assert_equal(dh.to_der, public_key.to_der)
@@ -67,14 +50,14 @@ class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase
end
def test_generate_key
- dh = OpenSSL::TestUtils::TEST_KEY_DH1024.public_key # creates a copy
+ dh = Fixtures.pkey("dh1024").public_key # creates a copy
assert_no_key(dh)
dh.generate_key!
assert_key(dh)
end
def test_key_exchange
- dh = OpenSSL::TestUtils::TEST_KEY_DH1024
+ dh = Fixtures.pkey("dh1024")
dh2 = dh.public_key
dh.generate_key!
dh2.generate_key!
diff --git a/test/openssl/test_pkey_dsa.rb b/test/openssl/test_pkey_dsa.rb
index d0ba8ec0f0..d651949842 100644
--- a/test/openssl/test_pkey_dsa.rb
+++ b/test/openssl/test_pkey_dsa.rb
@@ -1,12 +1,9 @@
# frozen_string_literal: false
require_relative 'utils'
-require 'base64'
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL) && defined?(OpenSSL::PKey::DSA)
class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase
- DSA512 = OpenSSL::TestUtils::TEST_KEY_DSA512
-
def test_private
key = OpenSSL::PKey::DSA.new(256)
assert(key.private?)
@@ -36,8 +33,28 @@ class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase
end
end
+ def test_sign_verify
+ dsa512 = Fixtures.pkey("dsa512")
+ data = "Sign me!"
+ if defined?(OpenSSL::Digest::DSS1)
+ signature = dsa512.sign(OpenSSL::Digest::DSS1.new, data)
+ assert_equal true, dsa512.verify(OpenSSL::Digest::DSS1.new, signature, data)
+ end
+
+ signature = dsa512.sign("SHA1", data)
+ assert_equal true, dsa512.verify("SHA1", signature, data)
+
+ signature0 = (<<~'end;').unpack("m")[0]
+ MCwCFH5h40plgU5Fh0Z4wvEEpz0eE9SnAhRPbkRB8ggsN/vsSEYMXvJwjGg/
+ 6g==
+ end;
+ assert_equal true, dsa512.verify("SHA256", signature0, data)
+ signature1 = signature0.succ
+ assert_equal false, dsa512.verify("SHA256", signature1, data)
+ end
+
def test_sys_sign_verify
- key = OpenSSL::TestUtils::TEST_KEY_DSA256
+ key = Fixtures.pkey("dsa256")
data = 'Sign me!'
digest = OpenSSL::Digest::SHA1.digest(data)
sig = key.syssign(digest)
@@ -46,17 +63,18 @@ class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase
def test_DSAPrivateKey
# OpenSSL DSAPrivateKey format; similar to RSAPrivateKey
+ dsa512 = Fixtures.pkey("dsa512")
asn1 = OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Integer(0),
- OpenSSL::ASN1::Integer(DSA512.p),
- OpenSSL::ASN1::Integer(DSA512.q),
- OpenSSL::ASN1::Integer(DSA512.g),
- OpenSSL::ASN1::Integer(DSA512.pub_key),
- OpenSSL::ASN1::Integer(DSA512.priv_key)
+ OpenSSL::ASN1::Integer(dsa512.p),
+ OpenSSL::ASN1::Integer(dsa512.q),
+ OpenSSL::ASN1::Integer(dsa512.g),
+ OpenSSL::ASN1::Integer(dsa512.pub_key),
+ OpenSSL::ASN1::Integer(dsa512.priv_key)
])
key = OpenSSL::PKey::DSA.new(asn1.to_der)
assert_predicate key, :private?
- assert_same_dsa DSA512, key
+ assert_same_dsa dsa512, key
pem = <<~EOF
-----BEGIN DSA PRIVATE KEY-----
@@ -69,14 +87,15 @@ class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase
-----END DSA PRIVATE KEY-----
EOF
key = OpenSSL::PKey::DSA.new(pem)
- assert_same_dsa DSA512, key
+ assert_same_dsa dsa512, key
- assert_equal asn1.to_der, DSA512.to_der
- assert_equal pem, DSA512.export
+ assert_equal asn1.to_der, dsa512.to_der
+ assert_equal pem, dsa512.export
end
def test_DSAPrivateKey_encrypted
# key = abcdef
+ dsa512 = Fixtures.pkey("dsa512")
pem = <<~EOF
-----BEGIN DSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
@@ -91,35 +110,36 @@ class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase
-----END DSA PRIVATE KEY-----
EOF
key = OpenSSL::PKey::DSA.new(pem, "abcdef")
- assert_same_dsa DSA512, key
+ assert_same_dsa dsa512, key
key = OpenSSL::PKey::DSA.new(pem) { "abcdef" }
- assert_same_dsa DSA512, key
+ assert_same_dsa dsa512, key
cipher = OpenSSL::Cipher.new("aes-128-cbc")
- exported = DSA512.to_pem(cipher, "abcdef\0\1")
- assert_same_dsa DSA512, OpenSSL::PKey::DSA.new(exported, "abcdef\0\1")
+ exported = dsa512.to_pem(cipher, "abcdef\0\1")
+ assert_same_dsa dsa512, OpenSSL::PKey::DSA.new(exported, "abcdef\0\1")
assert_raise(OpenSSL::PKey::DSAError) {
OpenSSL::PKey::DSA.new(exported, "abcdef")
}
end
def test_PUBKEY
+ dsa512 = Fixtures.pkey("dsa512")
asn1 = OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::ObjectId("DSA"),
OpenSSL::ASN1::Sequence([
- OpenSSL::ASN1::Integer(DSA512.p),
- OpenSSL::ASN1::Integer(DSA512.q),
- OpenSSL::ASN1::Integer(DSA512.g)
+ OpenSSL::ASN1::Integer(dsa512.p),
+ OpenSSL::ASN1::Integer(dsa512.q),
+ OpenSSL::ASN1::Integer(dsa512.g)
])
]),
OpenSSL::ASN1::BitString(
- OpenSSL::ASN1::Integer(DSA512.pub_key).to_der
+ OpenSSL::ASN1::Integer(dsa512.pub_key).to_der
)
])
key = OpenSSL::PKey::DSA.new(asn1.to_der)
assert_not_predicate key, :private?
- assert_same_dsa dup_public(DSA512), key
+ assert_same_dsa dup_public(dsa512), key
pem = <<~EOF
-----BEGIN PUBLIC KEY-----
@@ -132,10 +152,10 @@ class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase
-----END PUBLIC KEY-----
EOF
key = OpenSSL::PKey::DSA.new(pem)
- assert_same_dsa dup_public(DSA512), key
+ assert_same_dsa dup_public(dsa512), key
- assert_equal asn1.to_der, dup_public(DSA512).to_der
- assert_equal pem, dup_public(DSA512).export
+ assert_equal asn1.to_der, dup_public(dsa512).to_der
+ assert_equal pem, dup_public(dsa512).export
end
def test_read_DSAPublicKey_pem
diff --git a/test/openssl/test_pkey_ec.rb b/test/openssl/test_pkey_ec.rb
index 53aa5a10f6..dc5a14e6bf 100644
--- a/test/openssl/test_pkey_ec.rb
+++ b/test/openssl/test_pkey_ec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: false
require_relative 'utils'
-if defined?(OpenSSL::TestUtils) && defined?(OpenSSL::PKey::EC)
+if defined?(OpenSSL) && defined?(OpenSSL::PKey::EC)
class OpenSSL::TestEC < OpenSSL::PKeyTestCase
- P256 = OpenSSL::TestUtils::TEST_KEY_EC_P256V1
-
def test_ec_key
builtin_curves = OpenSSL::PKey::EC.builtin_curves
assert_not_empty builtin_curves
@@ -73,6 +71,21 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
assert_raise(OpenSSL::PKey::ECError) { key2.check_key }
end
+ def test_sign_verify
+ p256 = Fixtures.pkey("p256")
+ data = "Sign me!"
+ signature = p256.sign("SHA1", data)
+ assert_equal true, p256.verify("SHA1", signature, data)
+
+ signature0 = (<<~'end;').unpack("m")[0]
+ MEQCIEOTY/hD7eI8a0qlzxkIt8LLZ8uwiaSfVbjX2dPAvN11AiAQdCYx56Fq
+ QdBp1B4sxJoA8jvODMMklMyBKVmudboA6A==
+ end;
+ assert_equal true, p256.verify("SHA256", signature0, data)
+ signature1 = signature0.succ
+ assert_equal false, p256.verify("SHA256", signature1, data)
+ end
+
def test_dsa_sign_verify
data1 = "foo"
data2 = "bar"
@@ -86,16 +99,9 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
key = OpenSSL::PKey::EC.new("prime256v1").generate_key!
size = key.group.order.num_bits / 8 + 1
dgst = (1..size).to_a.pack('C*')
- begin
- sig = key.dsa_sign_asn1(dgst)
- # dgst is auto-truncated according to FIPS186-3 after openssl-0.9.8m
- assert(key.dsa_verify_asn1(dgst + "garbage", sig))
- rescue OpenSSL::PKey::ECError => e
- # just an exception for longer dgst before openssl-0.9.8m
- assert_equal('ECDSA_sign: data too large for key size', e.message)
- # no need to do following tests
- return
- end
+ sig = key.dsa_sign_asn1(dgst)
+ # dgst is auto-truncated according to FIPS186-3 after openssl-0.9.8m
+ assert(key.dsa_verify_asn1(dgst + "garbage", sig))
end
def test_dh_compute_key
@@ -110,21 +116,17 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
end
def test_ECPrivateKey
+ p256 = Fixtures.pkey("p256")
asn1 = OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Integer(1),
- OpenSSL::ASN1::OctetString(P256.private_key.to_s(2)),
- OpenSSL::ASN1::ASN1Data.new(
- [OpenSSL::ASN1::ObjectId("prime256v1")],
- 0, :CONTEXT_SPECIFIC
- ),
- OpenSSL::ASN1::ASN1Data.new(
- [OpenSSL::ASN1::BitString(P256.public_key.to_bn.to_s(2))],
- 1, :CONTEXT_SPECIFIC
- )
+ OpenSSL::ASN1::OctetString(p256.private_key.to_s(2)),
+ OpenSSL::ASN1::ObjectId("prime256v1", 0, :EXPLICIT),
+ OpenSSL::ASN1::BitString(p256.public_key.to_octet_string(:uncompressed),
+ 1, :EXPLICIT)
])
key = OpenSSL::PKey::EC.new(asn1.to_der)
assert_predicate key, :private?
- assert_same_ec P256, key
+ assert_same_ec p256, key
pem = <<~EOF
-----BEGIN EC PRIVATE KEY-----
@@ -134,13 +136,14 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
-----END EC PRIVATE KEY-----
EOF
key = OpenSSL::PKey::EC.new(pem)
- assert_same_ec P256, key
+ assert_same_ec p256, key
- assert_equal asn1.to_der, P256.to_der
- assert_equal pem, P256.export
+ assert_equal asn1.to_der, p256.to_der
+ assert_equal pem, p256.export
end
def test_ECPrivateKey_encrypted
+ p256 = Fixtures.pkey("p256")
# key = abcdef
pem = <<~EOF
-----BEGIN EC PRIVATE KEY-----
@@ -153,31 +156,32 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
-----END EC PRIVATE KEY-----
EOF
key = OpenSSL::PKey::EC.new(pem, "abcdef")
- assert_same_ec P256, key
+ assert_same_ec p256, key
key = OpenSSL::PKey::EC.new(pem) { "abcdef" }
- assert_same_ec P256, key
+ assert_same_ec p256, key
cipher = OpenSSL::Cipher.new("aes-128-cbc")
- exported = P256.to_pem(cipher, "abcdef\0\1")
- assert_same_ec P256, OpenSSL::PKey::EC.new(exported, "abcdef\0\1")
+ exported = p256.to_pem(cipher, "abcdef\0\1")
+ assert_same_ec p256, OpenSSL::PKey::EC.new(exported, "abcdef\0\1")
assert_raise(OpenSSL::PKey::ECError) {
OpenSSL::PKey::EC.new(exported, "abcdef")
}
end
def test_PUBKEY
+ p256 = Fixtures.pkey("p256")
asn1 = OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::ObjectId("id-ecPublicKey"),
OpenSSL::ASN1::ObjectId("prime256v1")
]),
OpenSSL::ASN1::BitString(
- P256.public_key.to_bn.to_s(2)
+ p256.public_key.to_octet_string(:uncompressed)
)
])
key = OpenSSL::PKey::EC.new(asn1.to_der)
assert_not_predicate key, :private?
- assert_same_ec dup_public(P256), key
+ assert_same_ec dup_public(p256), key
pem = <<~EOF
-----BEGIN PUBLIC KEY-----
@@ -186,10 +190,10 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
-----END PUBLIC KEY-----
EOF
key = OpenSSL::PKey::EC.new(pem)
- assert_same_ec dup_public(P256), key
+ assert_same_ec dup_public(p256), key
- assert_equal asn1.to_der, dup_public(P256).to_der
- assert_equal pem, dup_public(P256).export
+ assert_equal asn1.to_der, dup_public(p256).to_der
+ assert_equal pem, dup_public(p256).export
end
def test_ec_group
@@ -215,7 +219,8 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
assert_equal :uncompressed, group4.point_conversion_form
assert_equal group1, group4
assert_equal group1.curve_name, group4.curve_name
- assert_equal group1.generator.to_bn, group4.generator.to_bn
+ assert_equal group1.generator.to_octet_string(:uncompressed),
+ group4.generator.to_octet_string(:uncompressed)
assert_equal group1.order, group4.order
assert_equal group1.cofactor, group4.cofactor
assert_equal group1.seed, group4.seed
@@ -230,30 +235,57 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
point2 = OpenSSL::PKey::EC::Point.new(group, point.to_bn)
assert_equal point, point2
assert_equal point.to_bn, point2.to_bn
+ assert_equal point.to_octet_string(:uncompressed),
+ point2.to_octet_string(:uncompressed)
+
+ point3 = OpenSSL::PKey::EC::Point.new(group,
+ point.to_octet_string(:uncompressed))
+ assert_equal point, point3
+ assert_equal point.to_bn, point3.to_bn
+ assert_equal point.to_octet_string(:uncompressed),
+ point3.to_octet_string(:uncompressed)
+
point2.invert!
- assert_not_equal point.to_bn, point2.to_bn
+ point3.invert!
+ assert_not_equal point.to_octet_string(:uncompressed),
+ point2.to_octet_string(:uncompressed)
+ assert_equal point2.to_octet_string(:uncompressed),
+ point3.to_octet_string(:uncompressed)
begin
group = OpenSSL::PKey::EC::Group.new(:GFp, 17, 2, 2)
group.point_conversion_form = :uncompressed
- generator = OpenSSL::PKey::EC::Point.new(group, 0x040501.to_bn)
+ generator = OpenSSL::PKey::EC::Point.new(group, B(%w{ 04 05 01 }))
group.set_generator(generator, 19, 1)
- point = OpenSSL::PKey::EC::Point.new(group, 0x040603.to_bn)
+ point = OpenSSL::PKey::EC::Point.new(group, B(%w{ 04 06 03 }))
rescue OpenSSL::PKey::EC::Group::Error
pend "Patched OpenSSL rejected curve" if /unsupported field/ =~ $!.message
raise
end
assert_equal 0x040603.to_bn, point.to_bn
+ assert_equal 0x040603.to_bn, point.to_bn(:uncompressed)
+ assert_equal 0x0306.to_bn, point.to_bn(:compressed)
+ assert_equal 0x070603.to_bn, point.to_bn(:hybrid)
+
+ group2 = group.dup; group2.point_conversion_form = :compressed
+ point2 = OpenSSL::PKey::EC::Point.new(group2, B(%w{ 04 06 03 }))
+ assert_equal 0x0306.to_bn, point2.to_bn
+
+ assert_equal B(%w{ 04 06 03 }), point.to_octet_string(:uncompressed)
+ assert_equal B(%w{ 03 06 }), point.to_octet_string(:compressed)
+ assert_equal B(%w{ 07 06 03 }), point.to_octet_string(:hybrid)
+
assert_equal true, point.on_curve?
point.invert! # 8.5
- assert_equal 0x04060E.to_bn, point.to_bn
+ assert_equal B(%w{ 04 06 0E }), point.to_octet_string(:uncompressed)
assert_equal true, point.on_curve?
assert_equal false, point.infinity?
point.set_to_infinity!
assert_equal true, point.infinity?
assert_equal 0.to_bn, point.to_bn
+ assert_equal B(%w{ 00 }), point.to_octet_string(:uncompressed)
assert_equal true, point.on_curve?
end
@@ -263,31 +295,31 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
# generator is (5, 1)
group = OpenSSL::PKey::EC::Group.new(:GFp, 17, 2, 2)
group.point_conversion_form = :uncompressed
- gen = OpenSSL::PKey::EC::Point.new(group, OpenSSL::BN.new("040501", 16))
- group.set_generator(gen, 0, 0)
+ gen = OpenSSL::PKey::EC::Point.new(group, B(%w{ 04 05 01 }))
+ group.set_generator(gen, 19, 1)
# 3 * (6, 3) = (16, 13)
- point_a = OpenSSL::PKey::EC::Point.new(group, OpenSSL::BN.new("040603", 16))
+ point_a = OpenSSL::PKey::EC::Point.new(group, B(%w{ 04 06 03 }))
result_a1 = point_a.mul(3)
- assert_equal("04100D", result_a1.to_bn.to_s(16))
+ assert_equal B(%w{ 04 10 0D }), result_a1.to_octet_string(:uncompressed)
# 3 * (6, 3) + 3 * (5, 1) = (7, 6)
result_a2 = point_a.mul(3, 3)
- assert_equal("040706", result_a2.to_bn.to_s(16))
+ assert_equal B(%w{ 04 07 06 }), result_a2.to_octet_string(:uncompressed)
# 3 * point_a = 3 * (6, 3) = (16, 13)
result_b1 = point_a.mul([3], [])
- assert_equal("04100D", result_b1.to_bn.to_s(16))
+ assert_equal B(%w{ 04 10 0D }), result_b1.to_octet_string(:uncompressed)
# 3 * point_a + 2 * point_a = 3 * (6, 3) + 2 * (6, 3) = (7, 11)
result_b1 = point_a.mul([3, 2], [point_a])
- assert_equal("04070B", result_b1.to_bn.to_s(16))
+ assert_equal B(%w{ 04 07 0B }), result_b1.to_octet_string(:uncompressed)
# 3 * point_a + 5 * point_a.group.generator = 3 * (6, 3) + 5 * (5, 1) = (13, 10)
result_b1 = point_a.mul([3], [], 5)
- assert_equal("040D0A", result_b1.to_bn.to_s(16))
+ assert_equal B(%w{ 04 0D 0A }), result_b1.to_octet_string(:uncompressed)
rescue OpenSSL::PKey::EC::Group::Error
# CentOS patches OpenSSL to reject curves defined over Fp where p < 256 bits
raise if $!.message !~ /unsupported field/
end
- p256_key = P256
+ p256_key = Fixtures.pkey("p256")
p256_g = p256_key.group
assert_equal(p256_key.public_key, p256_g.generator.mul(p256_key.private_key))
@@ -303,6 +335,10 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
private
+ def B(ary)
+ [Array(ary).join].pack("H*")
+ end
+
def assert_same_ec(expected, key)
check_component(expected, key, [:group, :public_key, :private_key])
end
diff --git a/test/openssl/test_pkey_rsa.rb b/test/openssl/test_pkey_rsa.rb
index e211faa63b..ef02717d8b 100644
--- a/test/openssl/test_pkey_rsa.rb
+++ b/test/openssl/test_pkey_rsa.rb
@@ -1,12 +1,9 @@
# frozen_string_literal: false
-require_relative 'utils'
-require 'base64'
+require_relative "utils"
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
- RSA1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
-
def test_padding
key = OpenSSL::PKey::RSA.new(512, 3)
@@ -63,6 +60,13 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
end
end
+ def test_generate
+ key = OpenSSL::PKey::RSA.generate(512, 17)
+ assert_equal 512, key.n.num_bits
+ assert_equal 17, key.e
+ assert_not_nil key.d
+ end
+
def test_new_break
assert_nil(OpenSSL::PKey::RSA.new(1024) { break })
assert_raise(RuntimeError) do
@@ -70,8 +74,24 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
end
end
+ def test_sign_verify
+ rsa1024 = Fixtures.pkey("rsa1024")
+ data = "Sign me!"
+ signature = rsa1024.sign("SHA1", data)
+ assert_equal true, rsa1024.verify("SHA1", signature, data)
+
+ signature0 = (<<~'end;').unpack("m")[0]
+ oLCgbprPvfhM4pjFQiDTFeWI9Sk+Og7Nh9TmIZ/xSxf2CGXQrptlwo7NQ28+
+ WA6YQo8jPH4hSuyWIM4Gz4qRYiYRkl5TDMUYob94zm8Si1HxEiS9354tzvqS
+ zS8MLW2BtNPuTubMxTItHGTnOzo9sUg0LAHVFt8kHG2NfKAw/gQ=
+ end;
+ assert_equal true, rsa1024.verify("SHA256", signature0, data)
+ signature1 = signature0.succ
+ assert_equal false, rsa1024.verify("SHA256", signature1, data)
+ end
+
def test_digest_state_irrelevant_sign
- key = RSA1024
+ key = Fixtures.pkey("rsa1024")
digest1 = OpenSSL::Digest::SHA1.new
digest2 = OpenSSL::Digest::SHA1.new
data = 'Sign me!'
@@ -82,7 +102,7 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
end
def test_digest_state_irrelevant_verify
- key = RSA1024
+ key = Fixtures.pkey("rsa1024")
digest1 = OpenSSL::Digest::SHA1.new
digest2 = OpenSSL::Digest::SHA1.new
data = 'Sign me!'
@@ -93,21 +113,62 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
assert(key.verify(digest2, sig, data))
end
+ def test_verify_empty_rsa
+ rsa = OpenSSL::PKey::RSA.new
+ assert_raise(OpenSSL::PKey::PKeyError, "[Bug #12783]") {
+ rsa.verify("SHA1", "a", "b")
+ }
+ end
+
+ def test_sign_verify_pss
+ key = Fixtures.pkey("rsa1024")
+ data = "Sign me!"
+ invalid_data = "Sign me?"
+
+ signature = key.sign_pss("SHA256", data, salt_length: 20, mgf1_hash: "SHA1")
+ assert_equal 128, signature.bytesize
+ assert_equal true,
+ key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA1")
+ assert_equal true,
+ key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA1")
+ assert_equal false,
+ key.verify_pss("SHA256", signature, invalid_data, salt_length: 20, mgf1_hash: "SHA1")
+
+ signature = key.sign_pss("SHA256", data, salt_length: :digest, mgf1_hash: "SHA1")
+ assert_equal true,
+ key.verify_pss("SHA256", signature, data, salt_length: 32, mgf1_hash: "SHA1")
+ assert_equal true,
+ key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA1")
+ assert_equal false,
+ key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA1")
+
+ signature = key.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA1")
+ assert_equal true,
+ key.verify_pss("SHA256", signature, data, salt_length: 94, mgf1_hash: "SHA1")
+ assert_equal true,
+ key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA1")
+
+ assert_raise(OpenSSL::PKey::RSAError) {
+ key.sign_pss("SHA256", data, salt_length: 95, mgf1_hash: "SHA1")
+ }
+ end
+
def test_RSAPrivateKey
+ rsa1024 = Fixtures.pkey("rsa1024")
asn1 = OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Integer(0),
- OpenSSL::ASN1::Integer(RSA1024.n),
- OpenSSL::ASN1::Integer(RSA1024.e),
- OpenSSL::ASN1::Integer(RSA1024.d),
- OpenSSL::ASN1::Integer(RSA1024.p),
- OpenSSL::ASN1::Integer(RSA1024.q),
- OpenSSL::ASN1::Integer(RSA1024.dmp1),
- OpenSSL::ASN1::Integer(RSA1024.dmq1),
- OpenSSL::ASN1::Integer(RSA1024.iqmp)
+ OpenSSL::ASN1::Integer(rsa1024.n),
+ OpenSSL::ASN1::Integer(rsa1024.e),
+ OpenSSL::ASN1::Integer(rsa1024.d),
+ OpenSSL::ASN1::Integer(rsa1024.p),
+ OpenSSL::ASN1::Integer(rsa1024.q),
+ OpenSSL::ASN1::Integer(rsa1024.dmp1),
+ OpenSSL::ASN1::Integer(rsa1024.dmq1),
+ OpenSSL::ASN1::Integer(rsa1024.iqmp)
])
key = OpenSSL::PKey::RSA.new(asn1.to_der)
assert_predicate key, :private?
- assert_same_rsa RSA1024, key
+ assert_same_rsa rsa1024, key
pem = <<~EOF
-----BEGIN RSA PRIVATE KEY-----
@@ -127,13 +188,14 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
-----END RSA PRIVATE KEY-----
EOF
key = OpenSSL::PKey::RSA.new(pem)
- assert_same_rsa RSA1024, key
+ assert_same_rsa rsa1024, key
- assert_equal asn1.to_der, RSA1024.to_der
- assert_equal pem, RSA1024.export
+ assert_equal asn1.to_der, rsa1024.to_der
+ assert_equal pem, rsa1024.export
end
def test_RSAPrivateKey_encrypted
+ rsa1024 = Fixtures.pkey("rsa1024")
# key = abcdef
pem = <<~EOF
-----BEGIN RSA PRIVATE KEY-----
@@ -156,26 +218,27 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
-----END RSA PRIVATE KEY-----
EOF
key = OpenSSL::PKey::RSA.new(pem, "abcdef")
- assert_same_rsa RSA1024, key
+ assert_same_rsa rsa1024, key
key = OpenSSL::PKey::RSA.new(pem) { "abcdef" }
- assert_same_rsa RSA1024, key
+ assert_same_rsa rsa1024, key
cipher = OpenSSL::Cipher.new("aes-128-cbc")
- exported = RSA1024.to_pem(cipher, "abcdef\0\1")
- assert_same_rsa RSA1024, OpenSSL::PKey::RSA.new(exported, "abcdef\0\1")
+ exported = rsa1024.to_pem(cipher, "abcdef\0\1")
+ assert_same_rsa rsa1024, OpenSSL::PKey::RSA.new(exported, "abcdef\0\1")
assert_raise(OpenSSL::PKey::RSAError) {
OpenSSL::PKey::RSA.new(exported, "abcdef")
}
end
def test_RSAPublicKey
+ rsa1024 = Fixtures.pkey("rsa1024")
asn1 = OpenSSL::ASN1::Sequence([
- OpenSSL::ASN1::Integer(RSA1024.n),
- OpenSSL::ASN1::Integer(RSA1024.e)
+ OpenSSL::ASN1::Integer(rsa1024.n),
+ OpenSSL::ASN1::Integer(rsa1024.e)
])
key = OpenSSL::PKey::RSA.new(asn1.to_der)
assert_not_predicate key, :private?
- assert_same_rsa dup_public(RSA1024), key
+ assert_same_rsa dup_public(rsa1024), key
pem = <<~EOF
-----BEGIN RSA PUBLIC KEY-----
@@ -185,10 +248,11 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
-----END RSA PUBLIC KEY-----
EOF
key = OpenSSL::PKey::RSA.new(pem)
- assert_same_rsa dup_public(RSA1024), key
+ assert_same_rsa dup_public(rsa1024), key
end
def test_PUBKEY
+ rsa1024 = Fixtures.pkey("rsa1024")
asn1 = OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::ObjectId("rsaEncryption"),
@@ -196,14 +260,14 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
]),
OpenSSL::ASN1::BitString(
OpenSSL::ASN1::Sequence([
- OpenSSL::ASN1::Integer(RSA1024.n),
- OpenSSL::ASN1::Integer(RSA1024.e)
+ OpenSSL::ASN1::Integer(rsa1024.n),
+ OpenSSL::ASN1::Integer(rsa1024.e)
]).to_der
)
])
key = OpenSSL::PKey::RSA.new(asn1.to_der)
assert_not_predicate key, :private?
- assert_same_rsa dup_public(RSA1024), key
+ assert_same_rsa dup_public(rsa1024), key
pem = <<~EOF
-----BEGIN PUBLIC KEY-----
@@ -214,14 +278,25 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
-----END PUBLIC KEY-----
EOF
key = OpenSSL::PKey::RSA.new(pem)
- assert_same_rsa dup_public(RSA1024), key
+ assert_same_rsa dup_public(rsa1024), key
- assert_equal asn1.to_der, dup_public(RSA1024).to_der
- assert_equal pem, dup_public(RSA1024).export
+ assert_equal asn1.to_der, dup_public(rsa1024).to_der
+ assert_equal pem, dup_public(rsa1024).export
+ end
+
+ def test_pem_passwd
+ key = Fixtures.pkey("rsa1024")
+ pem3c = key.to_pem("aes-128-cbc", "key")
+ assert_match (/ENCRYPTED/), pem3c
+ assert_equal key.to_der, OpenSSL::PKey.read(pem3c, "key").to_der
+ assert_equal key.to_der, OpenSSL::PKey.read(pem3c) { "key" }.to_der
+ assert_raise(OpenSSL::PKey::PKeyError) {
+ OpenSSL::PKey.read(pem3c) { nil }
+ }
end
def test_dup
- key = OpenSSL::PKey::RSA.generate(256, 17)
+ key = Fixtures.pkey("rsa1024")
key2 = key.dup
assert_equal key.params, key2.params
key2.set_key(key2.n, 3, key2.d)
diff --git a/test/openssl/test_random.rb b/test/openssl/test_random.rb
index 6079461920..d5a374540d 100644
--- a/test/openssl/test_random.rb
+++ b/test/openssl/test_random.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: false
require_relative "utils"
+if defined?(OpenSSL)
+
class OpenSSL::TestRandom < OpenSSL::TestCase
def test_random_bytes
assert_equal("", OpenSSL::Random.random_bytes(0))
@@ -12,4 +14,6 @@ class OpenSSL::TestRandom < OpenSSL::TestCase
assert_equal("", OpenSSL::Random.pseudo_bytes(0))
assert_equal(12, OpenSSL::Random.pseudo_bytes(12).bytesize)
end if OpenSSL::Random.methods.include?(:pseudo_bytes)
-end if defined?(OpenSSL::TestCase)
+end
+
+end
diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb
index 8b4c090b5c..dad9a43779 100644
--- a/test/openssl/test_ssl.rb
+++ b/test/openssl/test_ssl.rb
@@ -1,10 +1,9 @@
# frozen_string_literal: false
require_relative "utils"
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestSSL < OpenSSL::SSLTestCase
-
def test_ctx_options
ctx = OpenSSL::SSL::SSLContext.new
@@ -35,7 +34,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
readwrite_loop(ctx, ssl)
}
- start_server(ctx_proc: ctx_proc, server_proc: server_proc) { |server, port|
+ start_server(ctx_proc: ctx_proc, server_proc: server_proc) { |port|
begin
sock = TCPSocket.new("127.0.0.1", port)
ctx = OpenSSL::SSL::SSLContext.new
@@ -48,6 +47,8 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
assert_equal 2, ssl.peer_cert_chain.size
assert_equal @svr_cert.to_der, ssl.peer_cert_chain[0].to_der
assert_equal @ca_cert.to_der, ssl.peer_cert_chain[1].to_der
+
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
ensure
ssl&.close
sock&.close
@@ -55,8 +56,91 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
}
end
+ def test_add_certificate
+ ctx_proc = -> ctx {
+ # Unset values set by start_server
+ ctx.cert = ctx.key = ctx.extra_chain_cert = nil
+ ctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) # RSA
+ }
+ start_server(ctx_proc: ctx_proc) do |port|
+ server_connect(port) { |ssl|
+ assert_equal @svr_cert.subject, ssl.peer_cert.subject
+ assert_equal [@svr_cert.subject, @ca_cert.subject],
+ ssl.peer_cert_chain.map(&:subject)
+
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ }
+ end
+ end
+
+ def test_add_certificate_multiple_certs
+ pend "EC is not supported" unless defined?(OpenSSL::PKey::EC)
+ pend "TLS 1.2 is not supported" unless tls12_supported?
+
+ # SSL_CTX_set0_chain() is needed for setting multiple certificate chains
+ add0_chain_supported = openssl?(1, 0, 2)
+
+ if add0_chain_supported
+ ca2_key = Fixtures.pkey("rsa2048")
+ ca2_exts = [
+ ["basicConstraints", "CA:TRUE", true],
+ ["keyUsage", "cRLSign, keyCertSign", true],
+ ]
+ ca2_dn = OpenSSL::X509::Name.parse_rfc2253("CN=CA2")
+ ca2_cert = issue_cert(ca2_dn, ca2_key, 123, ca2_exts, nil, nil)
+ else
+ # Use the same CA as @svr_cert
+ ca2_key = @ca_key; ca2_cert = @ca_cert
+ end
+
+ ecdsa_key = Fixtures.pkey("p256")
+ exts = [
+ ["keyUsage", "digitalSignature", false],
+ ]
+ ecdsa_dn = OpenSSL::X509::Name.parse_rfc2253("CN=localhost2")
+ ecdsa_cert = issue_cert(ecdsa_dn, ecdsa_key, 456, exts, ca2_cert, ca2_key)
+
+ if !add0_chain_supported
+ # Testing the warning emitted when 'extra' chain is replaced
+ tctx = OpenSSL::SSL::SSLContext.new
+ tctx.add_certificate(@svr_cert, @svr_key, [@ca_cert])
+ assert_warning(/set0_chain/) {
+ tctx.add_certificate(ecdsa_cert, ecdsa_key, [ca2_cert])
+ }
+ end
+
+ ctx_proc = -> ctx {
+ # Unset values set by start_server
+ ctx.cert = ctx.key = ctx.extra_chain_cert = nil
+ ctx.ecdh_curves = "P-256" unless openssl?(1, 0, 2)
+ ctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) # RSA
+ EnvUtil.suppress_warning do # !add0_chain_supported
+ ctx.add_certificate(ecdsa_cert, ecdsa_key, [ca2_cert])
+ end
+ }
+ start_server(ctx_proc: ctx_proc) do |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.max_version = :TLS1_2 # TODO: We need this to force certificate type
+ ctx.ciphers = "aECDSA"
+ server_connect(port, ctx) { |ssl|
+ assert_equal ecdsa_cert.subject, ssl.peer_cert.subject
+ assert_equal [ecdsa_cert.subject, ca2_cert.subject],
+ ssl.peer_cert_chain.map(&:subject)
+ }
+
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.max_version = :TLS1_2
+ ctx.ciphers = "aRSA"
+ server_connect(port, ctx) { |ssl|
+ assert_equal @svr_cert.subject, ssl.peer_cert.subject
+ assert_equal [@svr_cert.subject, @ca_cert.subject],
+ ssl.peer_cert_chain.map(&:subject)
+ }
+ end
+ end
+
def test_sysread_and_syswrite
- start_server { |server, port|
+ start_server { |port|
server_connect(port) { |ssl|
str = "x" * 100 + "\n"
ssl.syswrite(str)
@@ -66,17 +150,18 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
buf = ""
ssl.syswrite(str)
assert_same buf, ssl.sysread(str.size, buf)
- assert_equal(str, newstr)
+ assert_equal(str, buf)
}
}
end
def test_sync_close
- start_server { |server, port|
+ start_server { |port|
begin
sock = TCPSocket.new("127.0.0.1", port)
ssl = OpenSSL::SSL::SSLSocket.new(sock)
ssl.connect
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
ssl.close
assert_not_predicate sock, :closed?
ensure
@@ -88,6 +173,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
ssl = OpenSSL::SSL::SSLSocket.new(sock)
ssl.sync_close = true # !!
ssl.connect
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
ssl.close
assert_predicate sock, :closed?
ensure
@@ -97,7 +183,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def test_copy_stream
- start_server do |server, port|
+ start_server do |port|
server_connect(port) do |ssl|
IO.pipe do |r, w|
str = "hello world\n"
@@ -112,21 +198,16 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
def test_client_auth_failure
vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
- start_server(verify_mode: vflag, ignore_listener_error: true) { |server, port|
- sock = TCPSocket.new("127.0.0.1", port)
- ssl = OpenSSL::SSL::SSLSocket.new(sock)
- ssl.sync_close = true
- begin
- assert_handshake_error { ssl.connect }
- ensure
- ssl.close
- end
+ start_server(verify_mode: vflag, ignore_listener_error: true) { |port|
+ assert_handshake_error {
+ server_connect(port) { |ssl| ssl.puts("abc"); ssl.gets }
+ }
}
end
def test_client_auth_success
vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
- start_server(verify_mode: vflag) { |server, port|
+ start_server(verify_mode: vflag) { |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.key = @cli_key
ctx.cert = @cli_cert
@@ -153,19 +234,21 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
def test_client_auth_public_key
vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
- start_server(verify_mode: vflag, ignore_listener_error: true) do |server, port|
+ start_server(verify_mode: vflag, ignore_listener_error: true) do |port|
assert_raise(ArgumentError) {
ctx = OpenSSL::SSL::SSLContext.new
ctx.key = @cli_key.public_key
ctx.cert = @cli_cert
- server_connect(port, ctx) { }
+ server_connect(port, ctx) { |ssl| ssl.puts("abc"); ssl.gets }
}
ctx = OpenSSL::SSL::SSLContext.new
ctx.client_cert_cb = Proc.new{ |ssl|
[@cli_cert, @cli_key.public_key]
}
- assert_handshake_error { server_connect(port, ctx) }
+ assert_handshake_error {
+ server_connect(port, ctx) { |ssl| ssl.puts("abc"); ssl.gets }
+ }
end
end
@@ -175,20 +258,23 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
- start_server(verify_mode: vflag, ctx_proc: ctx_proc) { |server, port|
+ start_server(verify_mode: vflag, ctx_proc: ctx_proc) { |port|
ctx = OpenSSL::SSL::SSLContext.new
client_ca_from_server = nil
ctx.client_cert_cb = Proc.new do |sslconn|
client_ca_from_server = sslconn.client_ca
[@cli_cert, @cli_key]
end
- server_connect(port, ctx) { |ssl| assert_equal([@ca], client_ca_from_server) }
+ server_connect(port, ctx) { |ssl|
+ assert_equal([@ca], client_ca_from_server)
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ }
}
end
def test_read_nonblock_without_session
- OpenSSL::TestUtils.silent do
- start_server(start_immediately: false) { |server, port|
+ EnvUtil.suppress_warning do
+ start_server(start_immediately: false) { |port|
sock = TCPSocket.new("127.0.0.1", port)
ssl = OpenSSL::SSL::SSLSocket.new(sock)
ssl.sync_close = true
@@ -206,26 +292,21 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
def test_starttls
server_proc = -> (ctx, ssl) {
- begin
- while line = ssl.gets
- if line =~ /^STARTTLS$/
- ssl.write("x")
- ssl.flush
- ssl.accept
- next
- end
- ssl.write(line)
+ while line = ssl.gets
+ if line =~ /^STARTTLS$/
+ ssl.write("x")
+ ssl.flush
+ ssl.accept
+ break
end
- rescue OpenSSL::SSL::SSLError
- rescue IOError
- ensure
- ssl.close rescue nil
+ ssl.write(line)
end
+ readwrite_loop(ctx, ssl)
}
EnvUtil.suppress_warning do # read/write on not started session
start_server(start_immediately: false,
- server_proc: server_proc) { |server, port|
+ server_proc: server_proc) { |port|
begin
sock = TCPSocket.new("127.0.0.1", port)
ssl = OpenSSL::SSL::SSLSocket.new(sock)
@@ -248,7 +329,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def test_parallel
- start_server { |server, port|
+ start_server { |port|
ssls = []
10.times{
sock = TCPSocket.new("127.0.0.1", port)
@@ -269,7 +350,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def test_verify_result
- start_server(ignore_listener_error: true) { |server, port|
+ start_server(ignore_listener_error: true) { |port|
sock = TCPSocket.new("127.0.0.1", port)
ctx = OpenSSL::SSL::SSLContext.new
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
@@ -283,25 +364,20 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
}
- start_server { |server, port|
- sock = TCPSocket.new("127.0.0.1", port)
+ start_server { |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
ctx.verify_callback = Proc.new do |preverify_ok, store_ctx|
store_ctx.error = OpenSSL::X509::V_OK
true
end
- ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
- ssl.sync_close = true
- begin
- ssl.connect
+ server_connect(port, ctx) { |ssl|
assert_equal(OpenSSL::X509::V_OK, ssl.verify_result)
- ensure
- ssl.close
- end
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ }
}
- start_server(ignore_listener_error: true) { |server, port|
+ start_server(ignore_listener_error: true) { |port|
sock = TCPSocket.new("127.0.0.1", port)
ctx = OpenSSL::SSL::SSLContext.new
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
@@ -321,7 +397,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def test_exception_in_verify_callback_is_ignored
- start_server(ignore_listener_error: true) { |server, port|
+ start_server(ignore_listener_error: true) { |port|
sock = TCPSocket.new("127.0.0.1", port)
ctx = OpenSSL::SSL::SSLContext.new
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
@@ -332,7 +408,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
ssl.sync_close = true
begin
- OpenSSL::TestUtils.silent do
+ EnvUtil.suppress_warning do
# SSLError, not RuntimeError
assert_raise(OpenSSL::SSL::SSLError) { ssl.connect }
end
@@ -350,22 +426,24 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
assert_equal OpenSSL::SSL::VERIFY_PEER, ctx.verify_mode
ciphers_names = ctx.ciphers.collect{|v, _, _, _| v }
assert ciphers_names.all?{|v| /A(EC)?DH/ !~ v }, "anon ciphers are disabled"
- assert ciphers_names.all?{|v| /(RC4|MD5|EXP|DES)/ !~ v }, "weak ciphers are disabled"
+ assert ciphers_names.all?{|v| /(RC4|MD5|EXP|DES(?!-EDE|-CBC3))/ !~ v }, "weak ciphers are disabled"
assert_equal 0, ctx.options & OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS
- if defined?(OpenSSL::SSL::OP_NO_COMPRESSION) # >= 1.0.0
- assert_equal OpenSSL::SSL::OP_NO_COMPRESSION,
- ctx.options & OpenSSL::SSL::OP_NO_COMPRESSION
- end
+ assert_equal OpenSSL::SSL::OP_NO_COMPRESSION,
+ ctx.options & OpenSSL::SSL::OP_NO_COMPRESSION
end
def test_post_connect_check_with_anon_ciphers
+ pend "TLS 1.2 is not supported" unless tls12_supported?
+
ctx_proc = -> ctx {
+ ctx.ssl_version = :TLSv1_2
ctx.ciphers = "aNULL"
ctx.security_level = 0
}
- start_server(ctx_proc: ctx_proc) { |server, port|
+ start_server(ctx_proc: ctx_proc) { |port|
ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ssl_version = :TLSv1_2
ctx.ciphers = "aNULL"
ctx.security_level = 0
server_connect(port, ctx) { |ssl|
@@ -379,8 +457,10 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
def test_post_connection_check
sslerr = OpenSSL::SSL::SSLError
- start_server { |server, port|
+ start_server { |port|
server_connect(port) { |ssl|
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+
assert_raise(sslerr){ssl.post_connection_check("localhost.localdomain")}
assert_raise(sslerr){ssl.post_connection_check("127.0.0.1")}
assert(ssl.post_connection_check("localhost"))
@@ -394,16 +474,16 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
}
}
- now = Time.now
exts = [
["keyUsage","keyEncipherment,digitalSignature",true],
["subjectAltName","DNS:localhost.localdomain",false],
["subjectAltName","IP:127.0.0.1",false],
]
- @svr_cert = issue_cert(@svr, @svr_key, 4, now, now+1800, exts,
- @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
- start_server { |server, port|
+ @svr_cert = issue_cert(@svr, @svr_key, 4, exts, @ca_cert, @ca_key)
+ start_server { |port|
server_connect(port) { |ssl|
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+
assert(ssl.post_connection_check("localhost.localdomain"))
assert(ssl.post_connection_check("127.0.0.1"))
assert_raise(sslerr){ssl.post_connection_check("localhost")}
@@ -417,15 +497,15 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
}
}
- now = Time.now
exts = [
["keyUsage","keyEncipherment,digitalSignature",true],
["subjectAltName","DNS:*.localdomain",false],
]
- @svr_cert = issue_cert(@svr, @svr_key, 5, now, now+1800, exts,
- @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
- start_server { |server, port|
+ @svr_cert = issue_cert(@svr, @svr_key, 5, exts, @ca_cert, @ca_key)
+ start_server { |port|
server_connect(port) { |ssl|
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+
assert(ssl.post_connection_check("localhost.localdomain"))
assert_raise(sslerr){ssl.post_connection_check("127.0.0.1")}
assert_raise(sslerr){ssl.post_connection_check("localhost")}
@@ -446,8 +526,12 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, "www.example.com\0.evil.com"))
assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '192.168.7.255'))
assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, '192.168.7.1'))
- assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '13::17'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, '13::17'))
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '13::18'))
assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, '13:0:0:0:0:0:0:17'))
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '44:0:0:0:0:0:0:17'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, '0013:0000:0000:0000:0000:0000:0000:0017'))
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '1313:0000:0000:0000:0000:0000:0000:0017'))
end
end
@@ -627,48 +711,48 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def test_tlsext_hostname
- ctx3 = OpenSSL::SSL::SSLContext.new
- ctx3.ciphers = "ADH"
- ctx3.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 }
- ctx3.security_level = 0
- assert_not_predicate ctx3, :frozen?
+ fooctx = OpenSSL::SSL::SSLContext.new
+ fooctx.tmp_dh_callback = proc { Fixtures.pkey("dh-1") }
+ fooctx.cert = @cli_cert
+ fooctx.key = @cli_key
- ctx_proc = -> ctx {
- ctx.ciphers = "ALL:!aNULL"
+ ctx_proc = proc { |ctx|
ctx.servername_cb = proc { |ssl, servername|
case servername
when "foo.example.com"
- ctx3
+ fooctx
when "bar.example.com"
nil
else
- raise "unknown hostname"
+ raise "unreachable"
end
}
}
- start_server(ctx_proc: ctx_proc) do |server, port|
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ciphers = "ALL"
- ctx.security_level = 0
-
+ start_server(ctx_proc: ctx_proc) do |port|
sock = TCPSocket.new("127.0.0.1", port)
begin
- ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl = OpenSSL::SSL::SSLSocket.new(sock)
ssl.hostname = "foo.example.com"
ssl.connect
- assert_match /^ADH-/, ssl.cipher[0], "the context returned by servername_cb is used"
- assert_predicate ctx3, :frozen?
+ assert_equal @cli_cert.serial, ssl.peer_cert.serial
+ assert_predicate fooctx, :frozen?
+
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
ensure
+ ssl&.close
sock.close
end
sock = TCPSocket.new("127.0.0.1", port)
begin
- ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl = OpenSSL::SSL::SSLSocket.new(sock)
ssl.hostname = "bar.example.com"
ssl.connect
- assert_not_match /^A(EC)?DH-/, ssl.cipher[0], "the original context is used"
+ assert_equal @svr_cert.serial, ssl.peer_cert.serial
+
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
ensure
+ ssl&.close
sock.close
end
end
@@ -678,9 +762,9 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
hostname = 'example.org'
ctx2 = OpenSSL::SSL::SSLContext.new
- ctx2.ciphers = "aNULL"
- ctx2.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 }
- ctx2.security_level = 0
+ ctx2.cert = @svr_cert
+ ctx2.key = @svr_key
+ ctx2.tmp_dh_callback = proc { Fixtures.pkey("dh-1") }
ctx2.servername_cb = lambda { |args| Object.new }
sock1, sock2 = socketpair
@@ -688,8 +772,6 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2)
ctx1 = OpenSSL::SSL::SSLContext.new
- ctx1.ciphers = "aNULL"
- ctx1.security_level = 0
s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1)
s1.hostname = hostname
@@ -711,18 +793,16 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
def test_verify_hostname_on_connect
ctx_proc = proc { |ctx|
- now = Time.now
exts = [
["keyUsage", "keyEncipherment,digitalSignature", true],
["subjectAltName", "DNS:a.example.com,DNS:*.b.example.com," \
"DNS:c*.example.com,DNS:d.*.example.com"],
]
- ctx.cert = issue_cert(@svr, @svr_key, 4, now, now+1800, exts,
- @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
+ ctx.cert = issue_cert(@svr, @svr_key, 4, exts, @ca_cert, @ca_key)
ctx.key = @svr_key
}
- start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |server, port|
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.verify_hostname = true
ctx.cert_store = OpenSSL::X509::Store.new
@@ -743,7 +823,8 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
ssl.hostname = name
if expected_ok
- assert_nothing_raised { ssl.connect }
+ ssl.connect
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
else
assert_handshake_error { ssl.connect }
end
@@ -755,27 +836,26 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
end
- def test_multibyte_read_write
- #German a umlaut
- auml = [%w{ C3 A4 }.join('')].pack('H*')
- auml.force_encoding(Encoding::UTF_8)
-
- [10, 1000, 100000].each {|i|
- str = nil
- num_written = nil
- server_proc = Proc.new {|ctx, ssl|
- cmp = ssl.read
- raw_size = cmp.size
- cmp.force_encoding(Encoding::UTF_8)
- assert_equal(str, cmp)
- assert_equal(num_written, raw_size)
- ssl.close
+ def test_connect_certificate_verify_failed_exception_message
+ start_server(ignore_listener_error: true) { |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.set_params
+ assert_raise_with_message(OpenSSL::SSL::SSLError, /self signed/) {
+ server_connect(port, ctx)
}
- start_server(server_proc: server_proc) { |server, port|
- server_connect(port) { |ssl|
- str = auml * i
- num_written = ssl.write(str)
- }
+ }
+
+ ctx_proc = proc { |ctx|
+ ctx.cert = issue_cert(@svr, @svr_key, 30, [], @ca_cert, @ca_key,
+ not_before: Time.now-100, not_after: Time.now-10)
+ }
+ start_server(ignore_listener_error: true, ctx_proc: ctx_proc) { |port|
+ store = OpenSSL::X509::Store.new
+ store.add_cert(@ca_cert)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.set_params(cert_store: store)
+ assert_raise_with_message(OpenSSL::SSL::SSLError, /expired/) {
+ server_connect(port, ctx)
}
}
end
@@ -787,7 +867,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
# But it also degrades gracefully, so keep it
ctx.options = OpenSSL::SSL::OP_ALL
}
- start_server(ctx_proc: ctx_proc) { |server, port|
+ start_server(ctx_proc: ctx_proc) { |port|
server_connect(port) { |ssl|
ssl.puts('hello')
assert_equal("hello\n", ssl.gets)
@@ -795,112 +875,251 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
}
end
-if OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1) && OpenSSL::SSL::SSLContext::METHODS.include?(:SSLv3)
+ def check_supported_protocol_versions
+ possible_versions = [
+ OpenSSL::SSL::SSL3_VERSION,
+ OpenSSL::SSL::TLS1_VERSION,
+ OpenSSL::SSL::TLS1_1_VERSION,
+ OpenSSL::SSL::TLS1_2_VERSION,
+ # OpenSSL 1.1.1
+ defined?(OpenSSL::SSL::TLS1_3_VERSION) && OpenSSL::SSL::TLS1_3_VERSION,
+ ].compact
+
+ # Prepare for testing & do sanity check
+ supported = []
+ possible_versions.each do |ver|
+ catch(:unsupported) {
+ ctx_proc = proc { |ctx|
+ begin
+ ctx.min_version = ctx.max_version = ver
+ rescue ArgumentError, OpenSSL::SSL::SSLError
+ throw :unsupported
+ end
+ }
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
+ begin
+ server_connect(port) { |ssl|
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ }
+ rescue OpenSSL::SSL::SSLError, Errno::ECONNRESET
+ else
+ supported << ver
+ end
+ end
+ }
+ end
+ assert_not_empty supported
- def test_forbid_ssl_v3_for_client
- ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv3 }
- start_server_version(:SSLv23, ctx_proc) { |server, port|
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ssl_version = :SSLv3
- assert_handshake_error { server_connect(port, ctx) }
- }
+ supported
end
- def test_forbid_ssl_v3_from_server
- start_server_version(:SSLv3) { |server, port|
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv3
- assert_handshake_error { server_connect(port, ctx) }
- }
+ def test_set_params_min_version
+ supported = check_supported_protocol_versions
+ store = OpenSSL::X509::Store.new
+ store.add_cert(@ca_cert)
+
+ if supported.include?(OpenSSL::SSL::SSL3_VERSION)
+ # SSLContext#set_params properly disables SSL 3.0 by default
+ ctx_proc = proc { |ctx|
+ ctx.min_version = ctx.max_version = OpenSSL::SSL::SSL3_VERSION
+ }
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.set_params(cert_store: store, verify_hostname: false)
+ assert_handshake_error { server_connect(port, ctx) { } }
+ }
+ end
end
-end
+ def test_minmax_version
+ supported = check_supported_protocol_versions
+
+ # name: The string that would be returned by SSL_get_version()
+ # method: The version-specific method name (if any)
+ vmap = {
+ OpenSSL::SSL::SSL3_VERSION => { name: "SSLv3", method: "SSLv3" },
+ OpenSSL::SSL::SSL3_VERSION => { name: "SSLv3", method: "SSLv3" },
+ OpenSSL::SSL::TLS1_VERSION => { name: "TLSv1", method: "TLSv1" },
+ OpenSSL::SSL::TLS1_1_VERSION => { name: "TLSv1.1", method: "TLSv1_1" },
+ OpenSSL::SSL::TLS1_2_VERSION => { name: "TLSv1.2", method: "TLSv1_2" },
+ # OpenSSL 1.1.1
+ defined?(OpenSSL::SSL::TLS1_3_VERSION) && OpenSSL::SSL::TLS1_3_VERSION =>
+ { name: "TLSv1.3", method: nil },
+ }
-if OpenSSL::SSL::SSLContext::METHODS.include? :TLSv1_1
+ # Server enables a single version
+ supported.each do |ver|
+ ctx_proc = proc { |ctx| ctx.min_version = ctx.max_version = ver }
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
+ supported.each do |cver|
+ # Client enables a single version
+ ctx1 = OpenSSL::SSL::SSLContext.new
+ ctx1.min_version = ctx1.max_version = cver
+ if ver == cver
+ server_connect(port, ctx1) { |ssl|
+ assert_equal vmap[cver][:name], ssl.ssl_version
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ }
+ else
+ assert_handshake_error { server_connect(port, ctx1) { } }
+ end
- def test_tls_v1_1
- start_server_version(:TLSv1_1) { |server, port|
- server_connect(port) { |ssl| assert_equal("TLSv1.1", ssl.ssl_version) }
- }
- end
+ # There is no version-specific SSL methods for TLS 1.3
+ if cver <= OpenSSL::SSL::TLS1_2_VERSION
+ # Client enables a single version using #ssl_version=
+ ctx2 = OpenSSL::SSL::SSLContext.new
+ ctx2.ssl_version = vmap[cver][:method]
+ if ver == cver
+ server_connect(port, ctx2) { |ssl|
+ assert_equal vmap[cver][:name], ssl.ssl_version
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ }
+ else
+ assert_handshake_error { server_connect(port, ctx2) { } }
+ end
+ end
+ end
- def test_forbid_tls_v1_for_client
- ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1 }
- start_server_version(:SSLv23, ctx_proc) { |server, port|
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ssl_version = :TLSv1
- assert_handshake_error { server_connect(port, ctx) }
+ # Client enables all supported versions
+ ctx3 = OpenSSL::SSL::SSLContext.new
+ ctx3.min_version = ctx3.max_version = nil
+ server_connect(port, ctx3) { |ssl|
+ assert_equal vmap[ver][:name], ssl.ssl_version
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ }
+ }
+ end
+
+ if supported.size == 1
+ pend "More than one protocol version must be supported"
+ end
+
+ # Server sets min_version (earliest is disabled)
+ sver = supported[1]
+ ctx_proc = proc { |ctx| ctx.min_version = sver }
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
+ supported.each do |cver|
+ # Client sets min_version
+ ctx1 = OpenSSL::SSL::SSLContext.new
+ ctx1.min_version = cver
+ server_connect(port, ctx1) { |ssl|
+ assert_equal vmap[supported.last][:name], ssl.ssl_version
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ }
+
+ # Client sets max_version
+ ctx2 = OpenSSL::SSL::SSLContext.new
+ ctx2.max_version = cver
+ if cver >= sver
+ server_connect(port, ctx2) { |ssl|
+ assert_equal vmap[cver][:name], ssl.ssl_version
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ }
+ else
+ assert_handshake_error { server_connect(port, ctx2) { } }
+ end
+ end
}
- end
- def test_forbid_tls_v1_from_server
- start_server_version(:TLSv1) { |server, port|
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1
- assert_handshake_error { server_connect(port, ctx) }
+ # Server sets max_version (latest is disabled)
+ sver = supported[-2]
+ ctx_proc = proc { |ctx| ctx.max_version = sver }
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
+ supported.each do |cver|
+ # Client sets min_version
+ ctx1 = OpenSSL::SSL::SSLContext.new
+ ctx1.min_version = cver
+ if cver <= sver
+ server_connect(port, ctx1) { |ssl|
+ assert_equal vmap[sver][:name], ssl.ssl_version
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ }
+ else
+ assert_handshake_error { server_connect(port, ctx1) { } }
+ end
+
+ # Client sets max_version
+ ctx2 = OpenSSL::SSL::SSLContext.new
+ ctx2.max_version = cver
+ server_connect(port, ctx2) { |ssl|
+ if cver >= sver
+ assert_equal vmap[sver][:name], ssl.ssl_version
+ else
+ assert_equal vmap[cver][:name], ssl.ssl_version
+ end
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ }
+ end
}
end
-end
-
-if OpenSSL::SSL::SSLContext::METHODS.include? :TLSv1_2
+ def test_options_disable_versions
+ # Note: Use of these OP_* flags has been deprecated since OpenSSL 1.1.0.
+ supported = check_supported_protocol_versions
- def test_tls_v1_2
- start_server_version(:TLSv1_2) { |server, port|
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ssl_version = :TLSv1_2_client
- server_connect(port, ctx) { |ssl| assert_equal("TLSv1.2", ssl.ssl_version) }
- }
- end if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000
+ if supported.include?(OpenSSL::SSL::TLS1_1_VERSION) &&
+ supported.include?(OpenSSL::SSL::TLS1_2_VERSION)
+ # Server disables ~ TLS 1.1
+ ctx_proc = proc { |ctx|
+ ctx.options |= OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3 |
+ OpenSSL::SSL::OP_NO_TLSv1 | OpenSSL::SSL::OP_NO_TLSv1_1
+ }
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
+ # Client only supports TLS 1.1
+ ctx1 = OpenSSL::SSL::SSLContext.new
+ ctx1.min_version = ctx1.max_version = OpenSSL::SSL::TLS1_1_VERSION
+ assert_handshake_error { server_connect(port, ctx1) { } }
- def test_forbid_tls_v1_1_for_client
- ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_1 }
- start_server_version(:SSLv23, ctx_proc) { |server, port|
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ssl_version = :TLSv1_1
- assert_handshake_error { server_connect(port, ctx) }
- }
- end if defined?(OpenSSL::SSL::OP_NO_TLSv1_1)
+ # Client only supports TLS 1.2
+ ctx2 = OpenSSL::SSL::SSLContext.new
+ ctx2.min_version = ctx2.max_version = OpenSSL::SSL::TLS1_2_VERSION
+ assert_nothing_raised { server_connect(port, ctx2) { } }
+ }
- def test_forbid_tls_v1_1_from_server
- start_server_version(:TLSv1_1) { |server, port|
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_1
- assert_handshake_error { server_connect(port, ctx) }
- }
- end if defined?(OpenSSL::SSL::OP_NO_TLSv1_1)
+ # Server only supports TLS 1.1
+ ctx_proc = proc { |ctx|
+ ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION
+ }
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
+ # Client disables TLS 1.1
+ ctx1 = OpenSSL::SSL::SSLContext.new
+ ctx1.options |= OpenSSL::SSL::OP_NO_TLSv1_1
+ assert_handshake_error { server_connect(port, ctx1) { } }
- def test_forbid_tls_v1_2_for_client
- ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_2 }
- start_server_version(:SSLv23, ctx_proc) { |server, port|
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ssl_version = :TLSv1_2
- assert_handshake_error { server_connect(port, ctx) }
- }
- end if defined?(OpenSSL::SSL::OP_NO_TLSv1_2)
+ # Client disables TLS 1.2
+ ctx2 = OpenSSL::SSL::SSLContext.new
+ ctx2.options |= OpenSSL::SSL::OP_NO_TLSv1_2
+ assert_nothing_raised { server_connect(port, ctx2) { } }
+ }
+ else
+ pend "TLS 1.1 and TLS 1.2 must be supported; skipping"
+ end
+ end
- def test_forbid_tls_v1_2_from_server
- start_server_version(:TLSv1_2) { |server, port|
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_2
- assert_handshake_error { server_connect(port, ctx) }
+ def test_ssl_methods_constant
+ EnvUtil.suppress_warning { # Deprecated in v2.1.0
+ base = [:TLSv1_2, :TLSv1_1, :TLSv1, :SSLv3, :SSLv2, :SSLv23]
+ base.each do |name|
+ assert_include OpenSSL::SSL::SSLContext::METHODS, name
+ assert_include OpenSSL::SSL::SSLContext::METHODS, :"#{name}_client"
+ assert_include OpenSSL::SSL::SSLContext::METHODS, :"#{name}_server"
+ end
}
- end if defined?(OpenSSL::SSL::OP_NO_TLSv1_2)
-
-end
+ end
def test_renegotiation_cb
num_handshakes = 0
renegotiation_cb = Proc.new { |ssl| num_handshakes += 1 }
ctx_proc = Proc.new { |ctx| ctx.renegotiation_cb = renegotiation_cb }
- start_server_version(:SSLv23, ctx_proc) { |server, port|
+ start_server_version(:SSLv23, ctx_proc) { |port|
server_connect(port) { |ssl|
assert_equal(1, num_handshakes)
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
}
}
end
-if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10002000
+if openssl?(1, 0, 2) || libressl?
def test_alpn_protocol_selection_ary
advertised = ["http/1.1", "spdy/2"]
ctx_proc = Proc.new { |ctx|
@@ -909,11 +1128,12 @@ if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10002000
}
ctx.alpn_protocols = advertised
}
- start_server_version(:SSLv23, ctx_proc) { |server, port|
+ start_server_version(:SSLv23, ctx_proc) { |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.alpn_protocols = advertised
server_connect(port, ctx) { |ssl|
assert_equal(advertised.first, ssl.alpn_protocol)
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
}
}
end
@@ -922,14 +1142,13 @@ if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10002000
sock1, sock2 = socketpair
ctx1 = OpenSSL::SSL::SSLContext.new
- ctx1.ciphers = "aNULL"
- ctx1.security_level = 0
+ ctx1.cert = @svr_cert
+ ctx1.key = @svr_key
+ ctx1.tmp_dh_callback = proc { Fixtures.pkey("dh-1") }
ctx1.alpn_select_cb = -> (protocols) { nil }
ssl1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1)
ctx2 = OpenSSL::SSL::SSLContext.new
- ctx2.ciphers = "aNULL"
- ctx2.security_level = 0
ctx2.alpn_protocols = ["http/1.1"]
ssl2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2)
@@ -948,14 +1167,15 @@ if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10002000
end
end
-if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000 &&
- OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
- # NPN may be disabled by OpenSSL configure option
-
def test_npn_protocol_selection_ary
+ pend "TLS 1.2 is not supported" unless tls12_supported?
+ pend "NPN is not supported" unless \
+ OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
+ pend "LibreSSL 2.6 has broken NPN functions" if libressl?(2, 6, 1)
+
advertised = ["http/1.1", "spdy/2"]
- ctx_proc = Proc.new { |ctx| ctx.npn_protocols = advertised }
- start_server_version(:SSLv23, ctx_proc) { |server, port|
+ ctx_proc = proc { |ctx| ctx.npn_protocols = advertised }
+ start_server_version(:TLSv1_2, ctx_proc) { |port|
selector = lambda { |which|
ctx = OpenSSL::SSL::SSLContext.new
ctx.npn_select_cb = -> (protocols) { protocols.send(which) }
@@ -969,13 +1189,18 @@ if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000 &&
end
def test_npn_protocol_selection_enum
+ pend "TLS 1.2 is not supported" unless tls12_supported?
+ pend "NPN is not supported" unless \
+ OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
+ pend "LibreSSL 2.6 has broken NPN functions" if libressl?(2, 6, 1)
+
advertised = Object.new
def advertised.each
yield "http/1.1"
yield "spdy/2"
end
ctx_proc = Proc.new { |ctx| ctx.npn_protocols = advertised }
- start_server_version(:SSLv23, ctx_proc) { |server, port|
+ start_server_version(:TLSv1_2, ctx_proc) { |port|
selector = lambda { |selected, which|
ctx = OpenSSL::SSL::SSLContext.new
ctx.npn_select_cb = -> (protocols) { protocols.to_a.send(which) }
@@ -989,8 +1214,13 @@ if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000 &&
end
def test_npn_protocol_selection_cancel
+ pend "TLS 1.2 is not supported" unless tls12_supported?
+ pend "NPN is not supported" unless \
+ OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
+ pend "LibreSSL 2.6 has broken NPN functions" if libressl?(2, 6, 1)
+
ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] }
- start_server_version(:SSLv23, ctx_proc) { |server, port|
+ start_server_version(:TLSv1_2, ctx_proc) { |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.npn_select_cb = -> (protocols) { raise RuntimeError.new }
assert_raise(RuntimeError) { server_connect(port, ctx) }
@@ -998,8 +1228,13 @@ if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000 &&
end
def test_npn_advertised_protocol_too_long
+ pend "TLS 1.2 is not supported" unless tls12_supported?
+ pend "NPN is not supported" unless \
+ OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
+ pend "LibreSSL 2.6 has broken NPN functions" if libressl?(2, 6, 1)
+
ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["a" * 256] }
- start_server_version(:SSLv23, ctx_proc) { |server, port|
+ start_server_version(:TLSv1_2, ctx_proc) { |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.npn_select_cb = -> (protocols) { protocols.first }
assert_handshake_error { server_connect(port, ctx) }
@@ -1007,36 +1242,25 @@ if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000 &&
end
def test_npn_selected_protocol_too_long
+ pend "TLS 1.2 is not supported" unless tls12_supported?
+ pend "NPN is not supported" unless \
+ OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
+ pend "LibreSSL 2.6 has broken NPN functions" if libressl?(2, 6, 1)
+
ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] }
- start_server_version(:SSLv23, ctx_proc) { |server, port|
+ start_server_version(:TLSv1_2, ctx_proc) { |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.npn_select_cb = -> (protocols) { "a" * 256 }
assert_handshake_error { server_connect(port, ctx) }
}
end
-end
-
- def test_invalid_shutdown_by_gc
- assert_nothing_raised {
- start_server { |server, port|
- 10.times {
- sock = TCPSocket.new("127.0.0.1", port)
- ssl = OpenSSL::SSL::SSLSocket.new(sock)
- GC.start
- ssl.connect
- sock.close
- }
- }
- }
- end
-
def test_close_after_socket_close
- start_server { |server, port|
+ start_server { |port|
sock = TCPSocket.new("127.0.0.1", port)
ssl = OpenSSL::SSL::SSLSocket.new(sock)
- ssl.sync_close = true
ssl.connect
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
sock.close
assert_nothing_raised do
ssl.close
@@ -1053,84 +1277,149 @@ end
}
end
- def test_close_and_socket_close_while_connecting
- # test it doesn't cause a segmentation fault
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ciphers = "aNULL"
- ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 }
- ctx.security_level = 0
+ def test_get_ephemeral_key
+ # OpenSSL >= 1.0.2
+ unless OpenSSL::SSL::SSLSocket.method_defined?(:tmp_key)
+ pend "SSL_get_server_tmp_key() is not supported"
+ end
- sock1, sock2 = socketpair
- ssl1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx)
- ssl2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx)
+ if tls12_supported?
+ # kRSA
+ ctx_proc1 = proc { |ctx|
+ ctx.ssl_version = :TLSv1_2
+ ctx.ciphers = "kRSA"
+ }
+ start_server(ctx_proc: ctx_proc1) do |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ssl_version = :TLSv1_2
+ ctx.ciphers = "kRSA"
+ server_connect(port, ctx) { |ssl| assert_nil ssl.tmp_key }
+ end
+ end
- t = Thread.new { ssl1.connect }
- ssl2.accept
+ if defined?(OpenSSL::PKey::DH) && tls12_supported?
+ # DHE
+ # TODO: How to test this with TLS 1.3?
+ ctx_proc2 = proc { |ctx|
+ ctx.ssl_version = :TLSv1_2
+ ctx.ciphers = "EDH"
+ }
+ start_server(ctx_proc: ctx_proc2) do |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ssl_version = :TLSv1_2
+ ctx.ciphers = "EDH"
+ server_connect(port, ctx) { |ssl|
+ assert_instance_of OpenSSL::PKey::DH, ssl.tmp_key
+ }
+ end
+ end
- ssl1.close
- sock1.close
- t.value rescue nil
- ensure
- ssl1.close if ssl1
- ssl2.close if ssl2
- sock1.close if sock1
- sock2.close if sock2
+ if defined?(OpenSSL::PKey::EC)
+ # ECDHE
+ ctx_proc3 = proc { |ctx|
+ ctx.ciphers = "DEFAULT:!kRSA:!kEDH"
+ ctx.ecdh_curves = "P-256"
+ }
+ start_server(ctx_proc: ctx_proc3) do |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ciphers = "DEFAULT:!kRSA:!kEDH"
+ server_connect(port, ctx) { |ssl|
+ assert_instance_of OpenSSL::PKey::EC, ssl.tmp_key
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ }
+ end
+ end
end
- def test_get_ephemeral_key
- return unless OpenSSL::SSL::SSLSocket.method_defined?(:tmp_key)
- pkey = OpenSSL::PKey
- ciphers = {
- 'ECDHE-RSA-AES128-SHA' => (pkey::EC if defined?(pkey::EC)),
- 'DHE-RSA-AES128-SHA' => (pkey::DH if defined?(pkey::DH)),
- 'AES128-SHA' => nil
+ def test_fallback_scsv
+ pend "Fallback SCSV is not supported" unless \
+ OpenSSL::SSL::SSLContext.method_defined?(:enable_fallback_scsv)
+
+ start_server do |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
+ # Here is OK
+ # TLS1.2 supported and this is what we ask the first time
+ server_connect(port, ctx)
+ end
+
+ ctx_proc = proc { |ctx|
+ ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION
}
- conf_proc = Proc.new { |ctx| ctx.ciphers = 'ALL' }
- start_server(ctx_proc: conf_proc) do |server, port|
- ciphers.each do |cipher, ephemeral|
- ctx = OpenSSL::SSL::SSLContext.new
- begin
- ctx.ciphers = cipher
- rescue OpenSSL::SSL::SSLError => e
- next if /no cipher match/ =~ e.message
- raise
- end
- server_connect(port, ctx) do |ssl|
- if ephemeral
- assert_instance_of(ephemeral, ssl.tmp_key)
- else
- assert_nil(ssl.tmp_key)
- end
- end
- end
+ start_server(ctx_proc: ctx_proc) do |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.enable_fallback_scsv
+ ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION
+ # Here is OK too
+ # TLS1.2 not supported, fallback to TLS1.1 and signaling the fallback
+ # Server doesn't support better, so connection OK
+ server_connect(port, ctx)
+ end
+
+ # Here is not OK
+ # TLS1.2 is supported, fallback to TLS1.1 (downgrade attack) and signaling the fallback
+ # Server support better, so refuse the connection
+ sock1, sock2 = socketpair
+ begin
+ # This test is for the downgrade protection mechanism of TLS1.2.
+ # This is why ctx1 bounds max_version == TLS1.2.
+ # Otherwise, this test fails when using openssl 1.1.1 (or later) that supports TLS1.3.
+ # TODO: We may need another test for TLS1.3 because it seems to have a different mechanism.
+ ctx1 = OpenSSL::SSL::SSLContext.new
+ ctx1.max_version = OpenSSL::SSL::TLS1_2_VERSION
+ s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1)
+
+ ctx2 = OpenSSL::SSL::SSLContext.new
+ ctx2.enable_fallback_scsv
+ ctx2.max_version = OpenSSL::SSL::TLS1_1_VERSION
+ s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2)
+ t = Thread.new {
+ assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate fallback/) {
+ s2.connect
+ }
+ }
+ assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate fallback/) {
+ s1.accept
+ }
+ t.join
+ ensure
+ sock1.close
+ sock2.close
end
end
def test_dh_callback
+ pend "TLS 1.2 is not supported" unless tls12_supported?
+
+ dh = Fixtures.pkey("dh-1")
called = false
ctx_proc = -> ctx {
+ ctx.ssl_version = :TLSv1_2
ctx.ciphers = "DH:!NULL"
ctx.tmp_dh_callback = ->(*args) {
called = true
- OpenSSL::TestUtils::TEST_KEY_DH1024
+ dh
}
}
- start_server(ctx_proc: ctx_proc) do |server, port|
+ start_server(ctx_proc: ctx_proc) do |port|
server_connect(port) { |ssl|
assert called, "dh callback should be called"
if ssl.respond_to?(:tmp_key)
- assert_equal OpenSSL::TestUtils::TEST_KEY_DH1024.to_der, ssl.tmp_key.to_der
+ assert_equal dh.to_der, ssl.tmp_key.to_der
end
}
end
end
def test_connect_works_when_setting_dh_callback_to_nil
+ pend "TLS 1.2 is not supported" unless tls12_supported?
+
ctx_proc = -> ctx {
+ ctx.ssl_version = :TLSv1_2
ctx.ciphers = "DH:!NULL" # use DH
ctx.tmp_dh_callback = nil
}
- start_server(ctx_proc: ctx_proc) do |server, port|
+ start_server(ctx_proc: ctx_proc) do |port|
EnvUtil.suppress_warning { # uses default callback
assert_nothing_raised {
server_connect(port) { }
@@ -1139,73 +1428,56 @@ end
end
end
- def test_ecdh_callback
- return unless OpenSSL::SSL::SSLContext.instance_methods.include?(:tmp_ecdh_callback)
+ def test_tmp_ecdh_callback
+ pend "EC is disabled" unless defined?(OpenSSL::PKey::EC)
+ pend "tmp_ecdh_callback is not supported" unless \
+ OpenSSL::SSL::SSLContext.method_defined?(:tmp_ecdh_callback)
+ pend "LibreSSL 2.6 has broken SSL_CTX_set_tmp_ecdh_callback()" \
+ if libressl?(2, 6, 1)
+
EnvUtil.suppress_warning do # tmp_ecdh_callback is deprecated (2016-05)
- begin
- called = false
- ctx2 = OpenSSL::SSL::SSLContext.new
- ctx2.ciphers = "ECDH"
- # OpenSSL 1.1.0 doesn't have tmp_ecdh_callback so this shouldn't be required
- ctx2.security_level = 0
- ctx2.tmp_ecdh_callback = ->(*args) {
+ called = false
+ ctx_proc = -> ctx {
+ ctx.ciphers = "DEFAULT:!kRSA:!kEDH"
+ ctx.tmp_ecdh_callback = -> (*args) {
called = true
OpenSSL::PKey::EC.new "prime256v1"
}
-
- sock1, sock2 = socketpair
-
- s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2)
- ctx1 = OpenSSL::SSL::SSLContext.new
- ctx1.ciphers = "ECDH"
- ctx1.security_level = 0
-
- s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1)
- th = Thread.new do
- s1.connect
- end
-
- s2.accept
- assert called, 'ecdh callback should be called'
- rescue OpenSSL::SSL::SSLError => e
- if e.message =~ /no cipher match/
- pend "ECDH cipher not supported."
- else
- raise e
- end
- ensure
- th.join if th
- s1.close if s1
- s2.close if s2
- sock1.close if sock1
- sock2.close if sock2
+ }
+ start_server(ctx_proc: ctx_proc) do |port|
+ server_connect(port) { |s|
+ assert called, "tmp_ecdh_callback should be called"
+ }
end
end
end
def test_ecdh_curves
+ pend "EC is disabled" unless defined?(OpenSSL::PKey::EC)
+
ctx_proc = -> ctx {
- begin
- ctx.ciphers = "ECDH:!NULL"
- rescue OpenSSL::SSL::SSLError
- pend "ECDH is not enabled in this OpenSSL" if $!.message =~ /no cipher match/
- raise
- end
+ # Enable both ECDHE (~ TLS 1.2) cipher suites and TLS 1.3
+ ctx.ciphers = "DEFAULT:!kRSA:!kEDH"
ctx.ecdh_curves = "P-384:P-521"
}
- start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |server, port|
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.ecdh_curves = "P-256:P-384" # disable P-521 for OpenSSL >= 1.0.2
server_connect(port, ctx) { |ssl|
- assert ssl.cipher[0].start_with?("ECDH"), "ECDH should be used"
- if ssl.respond_to?(:tmp_key)
+ cs = ssl.cipher[0]
+ if /\ATLS/ =~ cs # Is TLS 1.3 is used?
assert_equal "secp384r1", ssl.tmp_key.group.curve_name
+ else
+ assert_match (/\AECDH/), cs
+ if ssl.respond_to?(:tmp_key)
+ assert_equal "secp384r1", ssl.tmp_key.group.curve_name
+ end
end
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
}
- if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10002000 &&
- !OpenSSL::OPENSSL_VERSION.include?("LibreSSL")
+ if openssl?(1, 0, 2) || libressl?(2, 5, 1)
ctx = OpenSSL::SSL::SSLContext.new
ctx.ecdh_curves = "P-256"
@@ -1218,6 +1490,7 @@ end
server_connect(port, ctx) { |ssl|
assert_equal "secp521r1", ssl.tmp_key.group.curve_name
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
}
end
end
@@ -1232,11 +1505,24 @@ end
return
end
assert_equal(1, ctx.security_level)
- # assert_raise(OpenSSL::SSL::SSLError) { ctx.key = OpenSSL::TestUtils::TEST_KEY_DSA512 }
- # ctx.key = OpenSSL::TestUtils::TEST_KEY_RSA1024
- # ctx.security_level = 2
- # assert_raise(OpenSSL::SSL::SSLError) { ctx.key = OpenSSL::TestUtils::TEST_KEY_RSA1024 }
- pend "FIXME: SSLContext#key= currently does not raise because SSL_CTX_use_certificate() is delayed"
+
+ dsa512 = Fixtures.pkey("dsa512")
+ dsa512_cert = issue_cert(@svr, dsa512, 50, [], @ca_cert, @ca_key)
+ rsa1024 = Fixtures.pkey("rsa1024")
+ rsa1024_cert = issue_cert(@svr, rsa1024, 51, [], @ca_cert, @ca_key)
+
+ assert_raise(OpenSSL::SSL::SSLError) {
+ # 512 bit DSA key is rejected because it offers < 80 bits of security
+ ctx.add_certificate(dsa512_cert, dsa512)
+ }
+ assert_nothing_raised {
+ ctx.add_certificate(rsa1024_cert, rsa1024)
+ }
+ ctx.security_level = 2
+ assert_raise(OpenSSL::SSL::SSLError) {
+ # < 112 bits of security
+ ctx.add_certificate(rsa1024_cert, rsa1024)
+ }
end
def test_dup
@@ -1252,6 +1538,18 @@ end
sock2.close
end
+ def test_freeze_calls_setup
+ bug = "[ruby/openssl#85]"
+ start_server(ignore_listener_error: true) { |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ ctx.freeze
+ assert_raise(OpenSSL::SSL::SSLError, bug) {
+ server_connect(port, ctx)
+ }
+ }
+ end
+
private
def start_server_version(version, ctx_proc = nil,
@@ -1268,7 +1566,7 @@ end
)
end
- def server_connect(port, ctx=nil)
+ def server_connect(port, ctx = nil)
sock = TCPSocket.new("127.0.0.1", port)
ssl = ctx ? OpenSSL::SSL::SSLSocket.new(sock, ctx) : OpenSSL::SSL::SSLSocket.new(sock)
ssl.sync_close = true
diff --git a/test/openssl/test_ssl_session.rb b/test/openssl/test_ssl_session.rb
index b2643edd8c..e199f86d2b 100644
--- a/test/openssl/test_ssl_session.rb
+++ b/test/openssl/test_ssl_session.rb
@@ -1,57 +1,15 @@
# frozen_string_literal: false
require_relative "utils"
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestSSLSession < OpenSSL::SSLTestCase
- def test_session_equals
- session = OpenSSL::SSL::Session.new <<-SESSION
------BEGIN SSL SESSION PARAMETERS-----
-MIIDFgIBAQICAwEEAgA5BCCY3pW6iTkPoD5SENuztz/gZjhvey6XnHbsxd22k0Ol
-dgQw8uaN3hCRnlhoIKPWInCFzrp/tQsDRFs9jDjc9pwpy/oKHmJdQQMQA1g8FYnO
-gpdVoQYCBE52ikKiBAICASyjggKOMIICijCCAXKgAwIBAgIBAjANBgkqhkiG9w0B
-AQUFADA9MRMwEQYKCZImiZPyLGQBGRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVi
-eS1sYW5nMQswCQYDVQQDDAJDQTAeFw0xMTA5MTkwMDE4MTBaFw0xMTA5MTkwMDQ4
-MTBaMEQxEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5
-LWxhbmcxEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
-gYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8Atv5y/OVQRp0ag8Tqo1YewsWijxEWB
-7JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9JWsjah8ssEBFSxZqdXKSLf0N4Hi7/
-GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/peeBOpRfyXxRFOYcCAwEAAaMSMBAw
-DgYDVR0PAQH/BAQDAgWgMA0GCSqGSIb3DQEBBQUAA4IBAQARC7GP7InX1t7VEXz2
-I8RI57S0/HSJL4fDIYP3zFpitHX1PZeo+7XuzMilvPjjBo/ky9Jzo8TYiY+N+JEz
-mY/A/zPA4ZsJ7KYj6/FEdIc/vRlS0CvsbClbNjw1jl/PoB2FLr2b3uuBcZEsyZeP
-yq154ijq37Ajf8K5Mi5FgshoP41BPtRPj+VVf61rv1IcEnNWdDCS6DR4XsaNC+zt
-G6AqCqkytIXWRuDw6n6vYLF3A/tn2sldLo7/scY0PMDNbo63O/LTxkDHmPhSkD68
-8m9SsMeTR+RCiDEZWFPVcAH/8mDfi+5k8uN3qS+gOU/PPrmHGgl5ykiSFgqs4v61
-tddwpBAEDjcwMzA5NTYzMTU1MzAwpQMCARM=
------END SSL SESSION PARAMETERS-----
- SESSION
-
- start_server(ignore_listener_error: true) { |_, port|
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
- ctx.session_id_context = self.object_id.to_s
-
- sock = TCPSocket.new '127.0.0.1', port
- begin
- ssl = OpenSSL::SSL::SSLSocket.new sock, ctx
- ssl.session = session
-
- assert_equal session, ssl.session
- ensure
- sock.close
- end
- }
- end
-
def test_session
- Timeout.timeout(5) do
- start_server do |server, port|
- sock = TCPSocket.new("127.0.0.1", port)
- ctx = OpenSSL::SSL::SSLContext.new("TLSv1")
- ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
- ssl.sync_close = true
- ssl.connect
+ pend "TLS 1.2 is not supported" unless tls12_supported?
+
+ ctx_proc = proc { |ctx| ctx.ssl_version = :TLSv1_2 }
+ start_server(ctx_proc: ctx_proc) do |port|
+ server_connect_with_session(port, nil, nil) { |ssl|
session = ssl.session
assert(session == OpenSSL::SSL::Session.new(session.to_pem))
assert(session == OpenSSL::SSL::Session.new(ssl))
@@ -68,8 +26,7 @@ tddwpBAEDjcwMzA5NTYzMTU1MzAwpQMCARM=
pem.gsub!(/-----(BEGIN|END) SSL SESSION PARAMETERS-----/, '').gsub!(/[\r\n]+/m, '')
assert_equal(session.to_der, pem.unpack('m*')[0])
assert_not_nil(session.to_text)
- ssl.close
- end
+ }
end
end
@@ -150,224 +107,272 @@ __EOS__
def test_session_exts_read
assert(OpenSSL::SSL::Session.new(DUMMY_SESSION))
- end if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x009080bf
-
- def test_client_session
- last_session = nil
- start_server do |server, port|
- 2.times do
- sock = TCPSocket.new("127.0.0.1", port)
- # Debian's openssl 0.9.8g-13 failed at assert(ssl.session_reused?),
- # when use default SSLContext. [ruby-dev:36167]
- ctx = OpenSSL::SSL::SSLContext.new("TLSv1")
- ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
- ssl.sync_close = true
- ssl.session = last_session if last_session
- ssl.connect
+ end
- session = ssl.session
- if last_session
- assert(ssl.session_reused?)
- assert_equal(session.id, last_session.id)
- assert_equal(session.to_pem, last_session.to_pem)
- assert_equal(session.to_der, last_session.to_der)
- # Older version of OpenSSL may not be consistent. Look up which versions later.
- assert_equal(session.to_text, last_session.to_text)
- else
- assert(!ssl.session_reused?)
- end
- last_session = session
+ def test_resumption
+ non_resumable = nil
+ start_server { |port|
+ server_connect_with_session(port, nil, nil) { |ssl|
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ non_resumable = ssl.session
+ }
+ }
- str = "x" * 100 + "\n"
- ssl.puts(str)
- assert_equal(str, ssl.gets)
+ ctx_proc = proc { |ctx|
+ ctx.options &= ~OpenSSL::SSL::OP_NO_TICKET
+ # Disable server-side session cache which is enabled by default
+ ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_OFF
+ }
+ start_server(ctx_proc: ctx_proc) do |port|
+ sess1 = server_connect_with_session(port, nil, nil) { |ssl|
+ ssl.puts("abc"); assert_equal "abc\n", ssl.gets
+ assert_equal false, ssl.session_reused?
+ ssl.session
+ }
- ssl.close
- end
+ server_connect_with_session(port, nil, non_resumable) { |ssl|
+ ssl.puts("abc"); assert_equal "abc\n", ssl.gets
+ assert_equal false, ssl.session_reused?
+ }
+
+ server_connect_with_session(port, nil, sess1) { |ssl|
+ ssl.puts("abc"); assert_equal "abc\n", ssl.gets
+ assert_equal true, ssl.session_reused?
+ }
end
end
- def test_server_session
- connections = 0
- saved_session = nil
+ def test_server_session_cache
+ pend "TLS 1.2 is not supported" unless tls12_supported?
- ctx_proc = Proc.new do |ctx, ssl|
-# add test for session callbacks here
+ ctx_proc = Proc.new do |ctx|
+ ctx.ssl_version = :TLSv1_2
+ ctx.options |= OpenSSL::SSL::OP_NO_TICKET
end
+ connections = nil
+ saved_session = nil
server_proc = Proc.new do |ctx, ssl|
- session = ssl.session
stats = ctx.session_cache_stats
case connections
when 0
- assert_equal(stats[:cache_num], 1)
- assert_equal(stats[:cache_hits], 0)
- assert_equal(stats[:cache_misses], 0)
- assert(!ssl.session_reused?)
+ assert_equal false, ssl.session_reused?
+ assert_equal 1, stats[:cache_num]
+ assert_equal 0, stats[:cache_hits]
+ assert_equal 0, stats[:cache_misses]
when 1
- assert_equal(stats[:cache_num], 1)
- assert_equal(stats[:cache_hits], 1)
- assert_equal(stats[:cache_misses], 0)
- assert(ssl.session_reused?)
- ctx.session_remove(session)
- saved_session = session.to_der
+ assert_equal true, ssl.session_reused?
+ assert_equal 1, stats[:cache_num]
+ assert_equal 1, stats[:cache_hits]
+ assert_equal 0, stats[:cache_misses]
+
+ saved_session = ssl.session
+ assert_equal true, ctx.session_remove(ssl.session)
when 2
- assert_equal(stats[:cache_num], 1)
- assert_equal(stats[:cache_hits], 1)
- assert_equal(stats[:cache_misses], 1)
- assert(!ssl.session_reused?)
- ctx.session_add(OpenSSL::SSL::Session.new(saved_session))
+ assert_equal false, ssl.session_reused?
+ assert_equal 1, stats[:cache_num]
+ assert_equal 1, stats[:cache_hits]
+ assert_equal 1, stats[:cache_misses]
+
+ assert_equal true, ctx.session_add(saved_session.dup)
when 3
- assert_equal(stats[:cache_num], 2)
- assert_equal(stats[:cache_hits], 2)
- assert_equal(stats[:cache_misses], 1)
- assert(ssl.session_reused?)
+ assert_equal true, ssl.session_reused?
+ assert_equal 2, stats[:cache_num]
+ assert_equal 2, stats[:cache_hits]
+ assert_equal 1, stats[:cache_misses]
+
ctx.flush_sessions(Time.now + 10000)
when 4
- assert_equal(stats[:cache_num], 1)
- assert_equal(stats[:cache_hits], 2)
- assert_equal(stats[:cache_misses], 2)
- assert(!ssl.session_reused?)
- ctx.session_add(OpenSSL::SSL::Session.new(saved_session))
+ assert_equal false, ssl.session_reused?
+ assert_equal 1, stats[:cache_num]
+ assert_equal 2, stats[:cache_hits]
+ assert_equal 2, stats[:cache_misses]
+
+ assert_equal true, ctx.session_add(saved_session.dup)
end
- connections += 1
readwrite_loop(ctx, ssl)
end
- first_session = nil
- start_server(ctx_proc: ctx_proc, server_proc: server_proc) do |server, port|
+ start_server(ctx_proc: ctx_proc, server_proc: server_proc) do |port|
+ first_session = nil
10.times do |i|
- sock = TCPSocket.new("127.0.0.1", port)
- ctx = OpenSSL::SSL::SSLContext.new
- # disable RFC4507 support
- ctx.options = OpenSSL::SSL::OP_NO_TICKET
- ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
- ssl.sync_close = true
- ssl.session = first_session if first_session
- ssl.connect
-
- session = ssl.session
- if first_session
- case i
- when 1; assert(ssl.session_reused?)
- when 2; assert(!ssl.session_reused?)
- when 3; assert(ssl.session_reused?)
- when 4; assert(!ssl.session_reused?)
- when 5..10; assert(ssl.session_reused?)
+ connections = i
+ cctx = OpenSSL::SSL::SSLContext.new
+ cctx.ssl_version = :TLSv1_2
+ server_connect_with_session(port, cctx, first_session) { |ssl|
+ ssl.puts("abc"); assert_equal "abc\n", ssl.gets
+ first_session ||= ssl.session
+
+ case connections
+ when 0;
+ when 1; assert_equal true, ssl.session_reused?
+ when 2; assert_equal false, ssl.session_reused?
+ when 3; assert_equal true, ssl.session_reused?
+ when 4; assert_equal false, ssl.session_reused?
+ when 5..9; assert_equal true, ssl.session_reused?
end
- end
- first_session ||= session
-
- str = "x" * 100 + "\n"
- ssl.puts(str)
- assert_equal(str, ssl.gets)
-
- ssl.close
+ }
end
end
end
- def test_ctx_client_session_cb
- called = {}
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
+ # Skipping tests that use session_remove_cb by default because it may cause
+ # deadlock.
+ TEST_SESSION_REMOVE_CB = ENV["OSSL_TEST_ALL"] == "1"
- ctx.session_new_cb = lambda { |ary|
- sock, sess = ary
- called[:new] = [sock, sess]
- }
+ def test_ctx_client_session_cb
+ pend "TLS 1.2 is not supported" unless tls12_supported?
- ctx.session_remove_cb = lambda { |ary|
- ctx, sess = ary
- called[:remove] = [ctx, sess]
- # any resulting value is OK (ignored)
- }
+ ctx_proc = proc { |ctx| ctx.ssl_version = :TLSv1_2 }
+ start_server(ctx_proc: ctx_proc) do |port|
+ called = {}
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
+ ctx.session_new_cb = lambda { |ary|
+ sock, sess = ary
+ called[:new] = [sock, sess]
+ }
+ if TEST_SESSION_REMOVE_CB
+ ctx.session_remove_cb = lambda { |ary|
+ ctx, sess = ary
+ called[:remove] = [ctx, sess]
+ # any resulting value is OK (ignored)
+ }
+ end
- start_server do |server, port|
- sock = TCPSocket.new("127.0.0.1", port)
- begin
- ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
- ssl.sync_close = true
- ssl.connect
+ server_connect_with_session(port, ctx, nil) { |ssl|
assert_equal(1, ctx.session_cache_stats[:cache_num])
assert_equal(1, ctx.session_cache_stats[:connect_good])
assert_equal([ssl, ssl.session], called[:new])
assert(ctx.session_remove(ssl.session))
assert(!ctx.session_remove(ssl.session))
- assert_equal([ctx, ssl.session], called[:remove])
- ssl.close
- ensure
- sock.close if !sock.closed?
- end
+ if TEST_SESSION_REMOVE_CB
+ assert_equal([ctx, ssl.session], called[:remove])
+ end
+ }
end
end
def test_ctx_server_session_cb
- called = {}
+ pend "TLS 1.2 is not supported" unless tls12_supported?
- ctx_proc = Proc.new { |ctx, ssl|
- ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_SERVER
- ctx.options = OpenSSL::SSL::OP_NO_TICKET
- last_server_session = nil
+ connections = nil
+ called = {}
+ cctx = OpenSSL::SSL::SSLContext.new
+ cctx.ssl_version = :TLSv1_2
+ sctx = nil
+ ctx_proc = Proc.new { |ctx|
+ sctx = ctx
+ ctx.ssl_version = :TLSv1_2
+ ctx.options |= OpenSSL::SSL::OP_NO_TICKET
# get_cb is called whenever a client proposed to resume a session but
# the session could not be found in the internal session cache.
+ last_server_session = nil
ctx.session_get_cb = lambda { |ary|
- sess, data = ary
- if last_server_session
- called[:get2] = [sess, data]
- last_server_session
+ _sess, data = ary
+ called[:get] = data
+
+ if connections == 2
+ last_server_session.dup
else
- called[:get1] = [sess, data]
- last_server_session = sess
nil
end
}
ctx.session_new_cb = lambda { |ary|
- sock, sess = ary
- called[:new] = [sock, sess]
- # SSL server doesn't cache sessions so get_cb is called next time.
- ctx.session_remove(sess)
+ _sock, sess = ary
+ called[:new] = sess
+ last_server_session = sess
}
- ctx.session_remove_cb = lambda { |ary|
- ctx, sess = ary
- called[:remove] = [ctx, sess]
- }
+ if TEST_SESSION_REMOVE_CB
+ ctx.session_remove_cb = lambda { |ary|
+ _ctx, sess = ary
+ called[:remove] = sess
+ }
+ end
}
+ start_server(ctx_proc: ctx_proc) do |port|
+ connections = 0
+ sess0 = server_connect_with_session(port, cctx, nil) { |ssl|
+ ssl.puts("abc"); assert_equal "abc\n", ssl.gets
+ assert_equal false, ssl.session_reused?
+ ssl.session
+ }
+ assert_nil called[:get]
+ assert_not_nil called[:new]
+ assert_equal sess0.id, called[:new].id
+ if TEST_SESSION_REMOVE_CB
+ assert_nil called[:remove]
+ end
+ called.clear
+
+ # Internal cache hit
+ connections = 1
+ server_connect_with_session(port, cctx, sess0.dup) { |ssl|
+ ssl.puts("abc"); assert_equal "abc\n", ssl.gets
+ assert_equal true, ssl.session_reused?
+ ssl.session
+ }
+ assert_nil called[:get]
+ assert_nil called[:new]
+ if TEST_SESSION_REMOVE_CB
+ assert_nil called[:remove]
+ end
+ called.clear
- server_proc = Proc.new { |c, ssl|
- ssl.session
- c.session_cache_stats
- readwrite_loop(c, ssl)
- }
- start_server(ctx_proc: ctx_proc, server_proc: server_proc) do |server, port|
- last_client_session = nil
- 3.times do
- sock = TCPSocket.new("127.0.0.1", port)
- begin
- ssl = OpenSSL::SSL::SSLSocket.new(sock, OpenSSL::SSL::SSLContext.new())
- ssl.sync_close = true
- ssl.session = last_client_session if last_client_session
- ssl.connect
- last_client_session = ssl.session
- ssl.close
- Timeout.timeout(5) do
- Thread.pass until called.key?(:new)
- assert(called.delete(:new))
- Thread.pass until called.key?(:remove)
- assert(called.delete(:remove))
- end
- ensure
- sock.close if !sock.closed?
+ sctx.flush_sessions(Time.now + 10000)
+ if TEST_SESSION_REMOVE_CB
+ assert_not_nil called[:remove]
+ assert_equal sess0.id, called[:remove].id
+ end
+ called.clear
+
+ # External cache hit
+ connections = 2
+ sess2 = server_connect_with_session(port, cctx, sess0.dup) { |ssl|
+ ssl.puts("abc"); assert_equal "abc\n", ssl.gets
+ if !ssl.session_reused? && openssl?(1, 1, 0) && !openssl?(1, 1, 0, 7)
+ # OpenSSL >= 1.1.0, < 1.1.0g
+ pend "External session cache is not working; " \
+ "see https://github.com/openssl/openssl/pull/4014"
end
+ assert_equal true, ssl.session_reused?
+ ssl.session
+ }
+ assert_equal sess0.id, sess2.id
+ assert_equal sess0.id, called[:get]
+ assert_nil called[:new]
+ if TEST_SESSION_REMOVE_CB
+ assert_nil called[:remove]
+ end
+ called.clear
+
+ sctx.flush_sessions(Time.now + 10000)
+ if TEST_SESSION_REMOVE_CB
+ assert_not_nil called[:remove]
+ assert_equal sess0.id, called[:remove].id
+ end
+ called.clear
+
+ # Cache miss
+ connections = 3
+ sess3 = server_connect_with_session(port, cctx, sess0.dup) { |ssl|
+ ssl.puts("abc"); assert_equal "abc\n", ssl.gets
+ assert_equal false, ssl.session_reused?
+ ssl.session
+ }
+ assert_not_equal sess0.id, sess3.id
+ assert_equal sess0.id, called[:get]
+ assert_not_nil called[:new]
+ assert_equal sess3.id, called[:new].id
+ if TEST_SESSION_REMOVE_CB
+ assert_nil called[:remove]
end
end
- assert(called[:get1])
- assert(called[:get2])
end
def test_dup
@@ -375,6 +380,21 @@ __EOS__
sess_dup = sess_orig.dup
assert_equal(sess_orig.to_der, sess_dup.to_der)
end
+
+ private
+
+ def server_connect_with_session(port, ctx = nil, sess = nil)
+ sock = TCPSocket.new("127.0.0.1", port)
+ ctx ||= OpenSSL::SSL::SSLContext.new
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.session = sess if sess
+ ssl.sync_close = true
+ ssl.connect
+ yield ssl if block_given?
+ ensure
+ ssl&.close
+ sock&.close
+ end
end
end
diff --git a/test/openssl/test_x509attr.rb b/test/openssl/test_x509attr.rb
index d7473f1a29..c6c48e86ab 100644
--- a/test/openssl/test_x509attr.rb
+++ b/test/openssl/test_x509attr.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
require_relative "utils"
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestX509Attribute < OpenSSL::TestCase
def test_new
@@ -62,6 +62,23 @@ class OpenSSL::TestX509Attribute < OpenSSL::TestCase
attr = OpenSSL::X509::Attribute.new("challengePassword", val)
assert_equal(attr.to_der, attr.dup.to_der)
end
+
+ def test_eq
+ val1 = OpenSSL::ASN1::Set([
+ OpenSSL::ASN1::UTF8String("abc123")
+ ])
+ attr1 = OpenSSL::X509::Attribute.new("challengePassword", val1)
+ attr2 = OpenSSL::X509::Attribute.new("challengePassword", val1)
+ ef = OpenSSL::X509::ExtensionFactory.new
+ val2 = OpenSSL::ASN1::Set.new([OpenSSL::ASN1::Sequence.new([
+ ef.create_extension("keyUsage", "keyCertSign", true)
+ ])])
+ attr3 = OpenSSL::X509::Attribute.new("extReq", val2)
+
+ assert_equal false, attr1 == 12345
+ assert_equal true, attr1 == attr2
+ assert_equal false, attr1 == attr3
+ end
end
end
diff --git a/test/openssl/test_x509cert.rb b/test/openssl/test_x509cert.rb
index 269d017298..40a5b0ad74 100644
--- a/test/openssl/test_x509cert.rb
+++ b/test/openssl/test_x509cert.rb
@@ -1,27 +1,22 @@
# frozen_string_literal: false
require_relative "utils"
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestX509Certificate < OpenSSL::TestCase
def setup
- @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
- @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
- @dsa256 = OpenSSL::TestUtils::TEST_KEY_DSA256
- @dsa512 = OpenSSL::TestUtils::TEST_KEY_DSA512
+ super
+ @rsa1024 = Fixtures.pkey("rsa1024")
+ @rsa2048 = Fixtures.pkey("rsa2048")
+ @dsa256 = Fixtures.pkey("dsa256")
+ @dsa512 = Fixtures.pkey("dsa512")
@ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
@ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
- @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
- end
-
- def issue_cert(*args)
- OpenSSL::TestUtils.issue_cert(*args)
end
def test_serial
[1, 2**32, 2**100].each{|s|
- cert = issue_cert(@ca, @rsa2048, s, Time.now, Time.now+3600, [],
- nil, nil, OpenSSL::Digest::SHA1.new)
+ cert = issue_cert(@ca, @rsa2048, s, [], nil, nil)
assert_equal(s, cert.serial)
cert = OpenSSL::X509::Certificate.new(cert.to_der)
assert_equal(s, cert.serial)
@@ -35,14 +30,10 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase
["authorityKeyIdentifier","keyid:always",false],
]
- sha1 = OpenSSL::Digest::SHA1.new
- dsa_digest = OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new
-
[
- [@rsa1024, sha1], [@rsa2048, sha1], [@dsa256, dsa_digest], [@dsa512, dsa_digest]
- ].each{|pk, digest|
- cert = issue_cert(@ca, pk, 1, Time.now, Time.now+3600, exts,
- nil, nil, digest)
+ @rsa1024, @rsa2048, @dsa256, @dsa512,
+ ].each{|pk|
+ cert = issue_cert(@ca, pk, 1, exts, nil, nil)
assert_equal(cert.extensions.sort_by(&:to_s)[2].value,
OpenSSL::TestUtils.get_subject_key_id(cert))
cert = OpenSSL::X509::Certificate.new(cert.to_der)
@@ -52,27 +43,27 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase
end
def test_validity
- now = Time.now until now && now.usec != 0
- cert = issue_cert(@ca, @rsa2048, 1, now, now+3600, [],
- nil, nil, OpenSSL::Digest::SHA1.new)
- assert_not_equal(now, cert.not_before)
- assert_not_equal(now+3600, cert.not_after)
+ now = Time.at(Time.now.to_i + 0.9)
+ cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil,
+ not_before: now, not_after: now+3600)
+ assert_equal(Time.at(now.to_i), cert.not_before)
+ assert_equal(Time.at(now.to_i+3600), cert.not_after)
now = Time.at(now.to_i)
- cert = issue_cert(@ca, @rsa2048, 1, now, now+3600, [],
- nil, nil, OpenSSL::Digest::SHA1.new)
+ cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil,
+ not_before: now, not_after: now+3600)
assert_equal(now.getutc, cert.not_before)
assert_equal((now+3600).getutc, cert.not_after)
now = Time.at(0)
- cert = issue_cert(@ca, @rsa2048, 1, now, now, [],
- nil, nil, OpenSSL::Digest::SHA1.new)
+ cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil,
+ not_before: now, not_after: now)
assert_equal(now.getutc, cert.not_before)
assert_equal(now.getutc, cert.not_after)
now = Time.at(0x7fffffff)
- cert = issue_cert(@ca, @rsa2048, 1, now, now, [],
- nil, nil, OpenSSL::Digest::SHA1.new)
+ cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil,
+ not_before: now, not_after: now)
assert_equal(now.getutc, cert.not_before)
assert_equal(now.getutc, cert.not_after)
end
@@ -84,8 +75,7 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase
["subjectKeyIdentifier","hash",false],
["authorityKeyIdentifier","keyid:always",false],
]
- ca_cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, ca_exts,
- nil, nil, OpenSSL::Digest::SHA1.new)
+ ca_cert = issue_cert(@ca, @rsa2048, 1, ca_exts, nil, nil)
ca_cert.extensions.each_with_index{|ext, i|
assert_equal(ca_exts[i].first, ext.oid)
assert_equal(ca_exts[i].last, ext.critical?)
@@ -98,34 +88,16 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase
["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
["subjectAltName","email:ee1@ruby-lang.org",false],
]
- ee1_cert = issue_cert(@ee1, @rsa1024, 2, Time.now, Time.now+1800, ee1_exts,
- ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ ee1_cert = issue_cert(@ee1, @rsa1024, 2, ee1_exts, ca_cert, @rsa2048)
assert_equal(ca_cert.subject.to_der, ee1_cert.issuer.to_der)
ee1_cert.extensions.each_with_index{|ext, i|
assert_equal(ee1_exts[i].first, ext.oid)
assert_equal(ee1_exts[i].last, ext.critical?)
}
-
- ee2_exts = [
- ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true],
- ["subjectKeyIdentifier","hash",false],
- ["authorityKeyIdentifier","issuer:always",false],
- ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
- ["subjectAltName","email:ee2@ruby-lang.org",false],
- ]
- ee2_cert = issue_cert(@ee2, @rsa1024, 3, Time.now, Time.now+1800, ee2_exts,
- ca_cert, @rsa2048, OpenSSL::Digest::MD5.new)
- assert_equal(ca_cert.subject.to_der, ee2_cert.issuer.to_der)
- ee2_cert.extensions.each_with_index{|ext, i|
- assert_equal(ee2_exts[i].first, ext.oid)
- assert_equal(ee2_exts[i].last, ext.critical?)
- }
-
end
def test_sign_and_verify_rsa_sha1
- cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
- nil, nil, OpenSSL::Digest::SHA1.new)
+ cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil, digest: "sha1")
assert_equal(false, cert.verify(@rsa1024))
assert_equal(true, cert.verify(@rsa2048))
assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) })
@@ -135,8 +107,7 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase
end
def test_sign_and_verify_rsa_md5
- cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
- nil, nil, OpenSSL::Digest::MD5.new)
+ cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil, digest: "md5")
assert_equal(false, cert.verify(@rsa1024))
assert_equal(true, cert.verify(@rsa2048))
@@ -148,8 +119,7 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase
end
def test_sign_and_verify_dsa
- cert = issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
- nil, nil, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new)
+ cert = issue_cert(@ca, @dsa512, 1, [], nil, nil)
assert_equal(false, certificate_error_returns_false { cert.verify(@rsa1024) })
assert_equal(false, certificate_error_returns_false { cert.verify(@rsa2048) })
assert_equal(false, cert.verify(@dsa256))
@@ -159,8 +129,7 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase
end
def test_sign_and_verify_rsa_dss1
- cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
- nil, nil, OpenSSL::Digest::DSS1.new)
+ cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil, digest: OpenSSL::Digest::DSS1.new)
assert_equal(false, cert.verify(@rsa1024))
assert_equal(true, cert.verify(@rsa2048))
assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) })
@@ -172,46 +141,54 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase
def test_sign_and_verify_dsa_md5
assert_raise(OpenSSL::X509::CertificateError){
- issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
- nil, nil, OpenSSL::Digest::MD5.new)
+ issue_cert(@ca, @dsa512, 1, [], nil, nil, digest: "md5")
}
end
- def test_dsig_algorithm_mismatch
- assert_raise(OpenSSL::X509::CertificateError) do
- issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
- nil, nil, OpenSSL::Digest::DSS1.new)
- end if OpenSSL::OPENSSL_VERSION_NUMBER < 0x10001000 # [ruby-core:42949]
-
- assert_raise(OpenSSL::X509::CertificateError) do
- issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
- nil, nil, OpenSSL::Digest::MD5.new)
- end
- end
-
def test_dsa_with_sha2
- begin
- cert = issue_cert(@ca, @dsa256, 1, Time.now, Time.now+3600, [],
- nil, nil, OpenSSL::Digest::SHA256.new)
- assert_equal("dsa_with_SHA256", cert.signature_algorithm)
- rescue OpenSSL::X509::CertificateError
- # dsa_with_sha2 not supported. skip following test.
- return
- end
+ cert = issue_cert(@ca, @dsa256, 1, [], nil, nil, digest: "sha256")
+ assert_equal("dsa_with_SHA256", cert.signature_algorithm)
# TODO: need more tests for dsa + sha2
# SHA1 is allowed from OpenSSL 1.0.0 (0.9.8 requires DSS1)
- cert = issue_cert(@ca, @dsa256, 1, Time.now, Time.now+3600, [],
- nil, nil, OpenSSL::Digest::SHA1.new)
+ cert = issue_cert(@ca, @dsa256, 1, [], nil, nil, digest: "sha1")
assert_equal("dsaWithSHA1", cert.signature_algorithm)
- end if defined?(OpenSSL::Digest::SHA256)
+ end
def test_check_private_key
- cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
- nil, nil, OpenSSL::Digest::SHA1.new)
+ cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil)
assert_equal(true, cert.check_private_key(@rsa2048))
end
+ def test_read_from_file
+ cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil)
+ Tempfile.create("cert") { |f|
+ f << cert.to_pem
+ f.rewind
+ assert_equal cert.to_der, OpenSSL::X509::Certificate.new(f).to_der
+ }
+ end
+
+ def test_eq
+ now = Time.now
+ cacert = issue_cert(@ca, @rsa1024, 1, [], nil, nil,
+ not_before: now, not_after: now + 3600)
+ cert1 = issue_cert(@ee1, @rsa2048, 2, [], cacert, @rsa1024,
+ not_before: now, not_after: now + 3600)
+ cert2 = issue_cert(@ee1, @rsa2048, 2, [], cacert, @rsa1024,
+ not_before: now, not_after: now + 3600)
+ cert3 = issue_cert(@ee1, @rsa2048, 3, [], cacert, @rsa1024,
+ not_before: now, not_after: now + 3600)
+ cert4 = issue_cert(@ee1, @rsa2048, 2, [], cacert, @rsa1024,
+ digest: "sha512", not_before: now, not_after: now + 3600)
+
+ assert_equal false, cert1 == 12345
+ assert_equal true, cert1 == cert2
+ assert_equal false, cert1 == cert3
+ assert_equal false, cert1 == cert4
+ assert_equal false, cert3 == cert4
+ end
+
private
def certificate_error_returns_false
diff --git a/test/openssl/test_x509crl.rb b/test/openssl/test_x509crl.rb
index cd1ccc98ab..03fdf64dd4 100644
--- a/test/openssl/test_x509crl.rb
+++ b/test/openssl/test_x509crl.rb
@@ -1,32 +1,24 @@
# frozen_string_literal: false
require_relative "utils"
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestX509CRL < OpenSSL::TestCase
def setup
- @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
- @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
- @dsa256 = OpenSSL::TestUtils::TEST_KEY_DSA256
- @dsa512 = OpenSSL::TestUtils::TEST_KEY_DSA512
+ super
+ @rsa1024 = Fixtures.pkey("rsa1024")
+ @rsa2048 = Fixtures.pkey("rsa2048")
+ @dsa256 = Fixtures.pkey("dsa256")
+ @dsa512 = Fixtures.pkey("dsa512")
@ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
@ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
@ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
end
- def issue_crl(*args)
- OpenSSL::TestUtils.issue_crl(*args)
- end
-
- def issue_cert(*args)
- OpenSSL::TestUtils.issue_cert(*args)
- end
-
def test_basic
now = Time.at(Time.now.to_i)
- cert = issue_cert(@ca, @rsa2048, 1, now, now+3600, [],
- nil, nil, OpenSSL::Digest::SHA1.new)
+ cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil)
crl = issue_crl([], 1, now, now+1600, [],
cert, @rsa2048, OpenSSL::Digest::SHA1.new)
assert_equal(1, crl.version)
@@ -63,8 +55,7 @@ class OpenSSL::TestX509CRL < OpenSSL::TestCase
[4, now, 4],
[5, now, 5],
]
- cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
- nil, nil, OpenSSL::Digest::SHA1.new)
+ cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil)
crl = issue_crl(revoke_info, 1, Time.now, Time.now+1600, [],
cert, @rsa2048, OpenSSL::Digest::SHA1.new)
revoked = crl.revoked
@@ -131,8 +122,7 @@ class OpenSSL::TestX509CRL < OpenSSL::TestCase
["issuerAltName", "issuer:copy", false],
]
- cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, cert_exts,
- nil, nil, OpenSSL::Digest::SHA1.new)
+ cert = issue_cert(@ca, @rsa2048, 1, cert_exts, nil, nil)
crl = issue_crl([], 1, Time.now, Time.now+1600, crl_exts,
cert, @rsa2048, OpenSSL::Digest::SHA1.new)
exts = crl.extensions
@@ -168,8 +158,7 @@ class OpenSSL::TestX509CRL < OpenSSL::TestCase
end
def test_crlnumber
- cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
- nil, nil, OpenSSL::Digest::SHA1.new)
+ cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil)
crl = issue_crl([], 1, Time.now, Time.now+1600, [],
cert, @rsa2048, OpenSSL::Digest::SHA1.new)
assert_match(1.to_s, crl.extensions[0].value)
@@ -187,8 +176,7 @@ class OpenSSL::TestX509CRL < OpenSSL::TestCase
end
def test_sign_and_verify
- cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
- nil, nil, OpenSSL::Digest::SHA1.new)
+ cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil)
crl = issue_crl([], 1, Time.now, Time.now+1600, [],
cert, @rsa2048, OpenSSL::Digest::SHA1.new)
assert_equal(false, crl.verify(@rsa1024))
@@ -198,10 +186,9 @@ class OpenSSL::TestX509CRL < OpenSSL::TestCase
crl.version = 0
assert_equal(false, crl.verify(@rsa2048))
- cert = issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
- nil, nil, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new)
+ cert = issue_cert(@ca, @dsa512, 1, [], nil, nil)
crl = issue_crl([], 1, Time.now, Time.now+1600, [],
- cert, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new)
+ cert, @dsa512, OpenSSL::Digest::SHA1.new)
assert_equal(false, crl_error_returns_false { crl.verify(@rsa1024) })
assert_equal(false, crl_error_returns_false { crl.verify(@rsa2048) })
assert_equal(false, crl.verify(@dsa256))
@@ -210,6 +197,58 @@ class OpenSSL::TestX509CRL < OpenSSL::TestCase
assert_equal(false, crl.verify(@dsa512))
end
+ def test_revoked_to_der
+ # revokedCertificates SEQUENCE OF SEQUENCE {
+ # userCertificate CertificateSerialNumber,
+ # revocationDate Time,
+ # crlEntryExtensions Extensions OPTIONAL
+ # -- if present, version MUST be v2
+ # } OPTIONAL,
+
+ now = Time.utc(2000, 1, 1)
+ rev1 = OpenSSL::X509::Revoked.new
+ rev1.serial = 123
+ rev1.time = now
+ ext = OpenSSL::X509::Extension.new("CRLReason", OpenSSL::ASN1::Enumerated(1))
+ rev1.extensions = [ext]
+ asn1 = OpenSSL::ASN1::Sequence([
+ OpenSSL::ASN1::Integer(123),
+ OpenSSL::ASN1::UTCTime(now),
+ OpenSSL::ASN1::Sequence([ext.to_der])
+ ])
+
+ assert_equal asn1.to_der, rev1.to_der
+ end
+
+ def test_eq
+ now = Time.now
+
+ cacert = issue_cert(@ca, @rsa1024, 1, [], nil, nil)
+ crl1 = issue_crl([], 1, now, now + 3600, [], cacert, @rsa1024, "sha256")
+ rev1 = OpenSSL::X509::Revoked.new.tap { |rev|
+ rev.serial = 1
+ rev.time = now
+ }
+ crl1.add_revoked(rev1)
+ crl2 = OpenSSL::X509::CRL.new(crl1.to_der)
+
+ # CRL
+ assert_equal false, crl1 == 12345
+ assert_equal true, crl1 == crl2
+ rev2 = OpenSSL::X509::Revoked.new.tap { |rev|
+ rev.serial = 2
+ rev.time = now
+ }
+ crl2.add_revoked(rev2)
+ assert_equal false, crl1 == crl2
+
+ # Revoked
+ assert_equal false, rev1 == 12345
+ assert_equal true, rev1 == crl2.revoked[0]
+ assert_equal false, rev1 == crl2.revoked[1]
+ assert_equal true, rev2 == crl2.revoked[1]
+ end
+
private
def crl_error_returns_false
diff --git a/test/openssl/test_x509ext.rb b/test/openssl/test_x509ext.rb
index 79713c0fad..91ce202fec 100644
--- a/test/openssl/test_x509ext.rb
+++ b/test/openssl/test_x509ext.rb
@@ -1,10 +1,11 @@
# frozen_string_literal: false
require_relative 'utils'
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestX509Extension < OpenSSL::TestCase
def setup
+ super
@basic_constraints_value = OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Boolean(true), # CA
OpenSSL::ASN1::Integer(2) # pathlen
@@ -74,6 +75,17 @@ class OpenSSL::TestX509Extension < OpenSSL::TestCase
assert_equal(@basic_constraints.to_der, ext.to_der)
assert_equal(ext.to_der, ext.dup.to_der)
end
+
+ def test_eq
+ ext1 = OpenSSL::X509::Extension.new(@basic_constraints.to_der)
+ ef = OpenSSL::X509::ExtensionFactory.new
+ ext2 = ef.create_extension("basicConstraints", "critical, CA:TRUE, pathlen:2")
+ ext3 = ef.create_extension("basicConstraints", "critical, CA:TRUE")
+
+ assert_equal false, ext1 == 12345
+ assert_equal true, ext1 == ext2
+ assert_equal false, ext1 == ext3
+ end
end
end
diff --git a/test/openssl/test_x509name.rb b/test/openssl/test_x509name.rb
index 250f1d0949..e31b5e29fb 100644
--- a/test/openssl/test_x509name.rb
+++ b/test/openssl/test_x509name.rb
@@ -1,11 +1,12 @@
-# coding: US-ASCII
+# coding: ASCII-8BIT
# frozen_string_literal: false
require_relative 'utils'
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestX509Name < OpenSSL::TestCase
def setup
+ super
@obj_type_tmpl = Hash.new(OpenSSL::ASN1::PRINTABLESTRING)
@obj_type_tmpl.update(OpenSSL::X509::Name::OBJECT_TYPE_TEMPLATE)
end
@@ -147,33 +148,28 @@ class OpenSSL::TestX509Name < OpenSSL::TestCase
end
def test_s_parse
- dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org"
+ dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org/1.2.3.4.5.6=A=BCD"
name = OpenSSL::X509::Name.parse(dn)
assert_equal(dn, name.to_s)
ary = name.to_a
- assert_equal("DC", ary[0][0])
- assert_equal("DC", ary[1][0])
- assert_equal("CN", ary[2][0])
- assert_equal("org", ary[0][1])
- assert_equal("ruby-lang", ary[1][1])
- assert_equal("www.ruby-lang.org", ary[2][1])
- assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
- assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
- assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+ assert_equal [
+ ["DC", "org", OpenSSL::ASN1::IA5STRING],
+ ["DC", "ruby-lang", OpenSSL::ASN1::IA5STRING],
+ ["CN", "www.ruby-lang.org", OpenSSL::ASN1::UTF8STRING],
+ ["1.2.3.4.5.6", "A=BCD", OpenSSL::ASN1::UTF8STRING],
+ ], ary
- dn2 = "DC=org, DC=ruby-lang, CN=www.ruby-lang.org"
+ dn2 = "DC=org, DC=ruby-lang, CN=www.ruby-lang.org, 1.2.3.4.5.6=A=BCD"
name = OpenSSL::X509::Name.parse(dn2)
- ary = name.to_a
assert_equal(dn, name.to_s)
- assert_equal("org", ary[0][1])
- assert_equal("ruby-lang", ary[1][1])
- assert_equal("www.ruby-lang.org", ary[2][1])
+ assert_equal ary, name.to_a
name = OpenSSL::X509::Name.parse(dn2, @obj_type_tmpl)
ary = name.to_a
assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2])
+ assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[3][2])
end
def test_s_parse_rfc2253
@@ -305,7 +301,6 @@ class OpenSSL::TestX509Name < OpenSSL::TestCase
end
def test_add_entry_street
- return if OpenSSL::OPENSSL_VERSION_NUMBER < 0x009080df # 0.9.8m
# openssl/crypto/objects/obj_mac.h 1.83
dn = [
["DC", "org"],
@@ -322,18 +317,104 @@ class OpenSSL::TestX509Name < OpenSSL::TestCase
assert_equal("Namiki", ary[5][1])
end
+ def test_add_entry_placing
+ der = %w{ 30 2A
+ 31 12
+ 30 10 06 03 55 04 0A 0C 09 72 75 62 79 2D 6C 61 6E 67
+ 31 14
+ 30 08 06 03 55 04 0B 0C 01 61
+ 30 08 06 03 55 04 0B 0C 01 62 }
+ orig = OpenSSL::X509::Name.new([der.join].pack("H*"))
+ assert_equal("OU=b+OU=a,O=ruby-lang", orig.to_s(OpenSSL::X509::Name::RFC2253))
+ # Skip for now; they do not work
+ #
+ # dn = orig.dup
+ # dn.add_entry("CN", "unya", loc: 0, set: 0)
+ # assert_equal("OU=b+OU=a,O=ruby-lang,CN=unya", dn.dup.to_s(OpenSSL::X509::Name::RFC2253))
+ # dn = orig.dup
+ # dn.add_entry("CN", "unya", loc: 0, set: 1)
+ # assert_equal("OU=b+OU=a,O=ruby-lang+CN=unya", dn.dup.to_s(OpenSSL::X509::Name::RFC2253))
+ dn = orig.dup
+ dn.add_entry("CN", "unya", loc: 1, set: -1)
+ assert_equal("OU=b+OU=a,O=ruby-lang+CN=unya", dn.dup.to_s(OpenSSL::X509::Name::RFC2253))
+ # dn = orig.dup
+ # dn.add_entry("CN", "unya", loc: 1, set: 0)
+ # assert_equal("OU=b+OU=a,CN=unya,O=ruby-lang", dn.dup.to_s(OpenSSL::X509::Name::RFC2253))
+ dn = orig.dup
+ dn.add_entry("CN", "unya", loc: 1, set: 1)
+ assert_equal("CN=unya+OU=b+OU=a,O=ruby-lang", dn.dup.to_s(OpenSSL::X509::Name::RFC2253))
+ dn = orig.dup
+ dn.add_entry("CN", "unya", loc: -1, set: -1)
+ assert_equal("CN=unya+OU=b+OU=a,O=ruby-lang", dn.dup.to_s(OpenSSL::X509::Name::RFC2253))
+ dn = orig.dup
+ dn.add_entry("CN", "unya", loc: -1, set: 0)
+ assert_equal("CN=unya,OU=b+OU=a,O=ruby-lang", dn.dup.to_s(OpenSSL::X509::Name::RFC2253))
+ end
+
+ def test_to_s
+ dn = [
+ ["DC", "org"],
+ ["DC", "ruby-lang"],
+ ["CN", "フー, ãƒãƒ¼"],
+ ]
+ name = OpenSSL::X509::Name.new
+ dn.each { |x| name.add_entry(*x) }
+
+ assert_equal "/DC=org/DC=ruby-lang/" \
+ "CN=\\xE3\\x83\\x95\\xE3\\x83\\xBC, \\xE3\\x83\\x90\\xE3\\x83\\xBC",
+ name.to_s
+ # OpenSSL escapes characters with MSB by default
+ assert_equal \
+ "CN=\\E3\\83\\95\\E3\\83\\BC\\, \\E3\\83\\90\\E3\\83\\BC," \
+ "DC=ruby-lang,DC=org",
+ name.to_s(OpenSSL::X509::Name::RFC2253)
+ assert_equal "DC = org, DC = ruby-lang, " \
+ "CN = \"\\E3\\83\\95\\E3\\83\\BC, \\E3\\83\\90\\E3\\83\\BC\"",
+ name.to_s(OpenSSL::X509::Name::ONELINE)
+
+ empty = OpenSSL::X509::Name.new
+ assert_equal "", empty.to_s
+ assert_equal "", empty.to_s(OpenSSL::X509::Name::COMPAT)
+ assert_equal "", empty.to_s(OpenSSL::X509::Name::RFC2253)
+ assert_equal "", empty.to_s(OpenSSL::X509::Name::ONELINE)
+ end
+
+ def test_to_utf8
+ dn = [
+ ["DC", "org"],
+ ["DC", "ruby-lang"],
+ ["CN", "フー, ãƒãƒ¼"],
+ ]
+ name = OpenSSL::X509::Name.new
+ dn.each { |x| name.add_entry(*x) }
+
+ str = name.to_utf8
+ expected = "CN=フー\\, ãƒãƒ¼,DC=ruby-lang,DC=org".force_encoding("UTF-8")
+ assert_equal expected, str
+ assert_equal Encoding.find("UTF-8"), str.encoding
+
+ empty = OpenSSL::X509::Name.new
+ assert_equal "", empty.to_utf8
+ end
+
def test_equals2
- n1 = OpenSSL::X509::Name.parse 'CN=a'
- n2 = OpenSSL::X509::Name.parse 'CN=a'
+ n1 = OpenSSL::X509::Name.parse_rfc2253 'CN=a'
+ n2 = OpenSSL::X509::Name.parse_rfc2253 'CN=a'
assert_equal n1, n2
end
def test_spaceship
- n1 = OpenSSL::X509::Name.parse 'CN=a'
- n2 = OpenSSL::X509::Name.parse 'CN=b'
+ n1 = OpenSSL::X509::Name.new([["CN", "a"]])
+ n2 = OpenSSL::X509::Name.new([["CN", "a"]])
+ n3 = OpenSSL::X509::Name.new([["CN", "ab"]])
- assert_equal(-1, n1 <=> n2)
+ assert_equal 0, n1 <=> n2
+ assert_equal -1, n1 <=> n3
+ assert_equal 0, n2 <=> n1
+ assert_equal -1, n2 <=> n3
+ assert_equal 1, n3 <=> n1
+ assert_equal 1, n3 <=> n2
end
def name_hash(name)
@@ -345,17 +426,27 @@ class OpenSSL::TestX509Name < OpenSSL::TestCase
def test_hash
dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org"
name = OpenSSL::X509::Name.parse(dn)
- d = Digest::MD5.digest(name.to_der)
+ d = OpenSSL::Digest::MD5.digest(name.to_der)
expected = (d[0].ord & 0xff) | (d[1].ord & 0xff) << 8 | (d[2].ord & 0xff) << 16 | (d[3].ord & 0xff) << 24
assert_equal(expected, name_hash(name))
#
dn = "/DC=org/DC=ruby-lang/CN=baz.ruby-lang.org"
name = OpenSSL::X509::Name.parse(dn)
- d = Digest::MD5.digest(name.to_der)
+ d = OpenSSL::Digest::MD5.digest(name.to_der)
expected = (d[0].ord & 0xff) | (d[1].ord & 0xff) << 8 | (d[2].ord & 0xff) << 16 | (d[3].ord & 0xff) << 24
assert_equal(expected, name_hash(name))
end
+ def test_equality
+ name0 = OpenSSL::X509::Name.new([["DC", "org"], ["DC", "ruby-lang"], ["CN", "bar.ruby-lang.org"]])
+ name1 = OpenSSL::X509::Name.new([["DC", "org"], ["DC", "ruby-lang"], ["CN", "bar.ruby-lang.org"]])
+ name2 = OpenSSL::X509::Name.new([["DC", "org"], ["DC", "ruby-lang"], ["CN", "baz.ruby-lang.org"]])
+ assert_equal true, name0 == name1
+ assert_equal true, name0.eql?(name1)
+ assert_equal false, name0 == name2
+ assert_equal false, name0.eql?(name2)
+ end
+
def test_dup
name = OpenSSL::X509::Name.parse("/CN=ruby-lang.org")
assert_equal(name.to_der, name.dup.to_der)
diff --git a/test/openssl/test_x509req.rb b/test/openssl/test_x509req.rb
index 086ccfbdb1..2c447ccdd5 100644
--- a/test/openssl/test_x509req.rb
+++ b/test/openssl/test_x509req.rb
@@ -1,14 +1,15 @@
# frozen_string_literal: false
require_relative "utils"
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestX509Request < OpenSSL::TestCase
def setup
- @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
- @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
- @dsa256 = OpenSSL::TestUtils::TEST_KEY_DSA256
- @dsa512 = OpenSSL::TestUtils::TEST_KEY_DSA512
+ super
+ @rsa1024 = Fixtures.pkey("rsa1024")
+ @rsa2048 = Fixtures.pkey("rsa2048")
+ @dsa256 = Fixtures.pkey("dsa256")
+ @dsa512 = Fixtures.pkey("dsa512")
@dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou")
end
@@ -27,7 +28,7 @@ class OpenSSL::TestX509Request < OpenSSL::TestCase
req = OpenSSL::X509::Request.new(req.to_der)
assert_equal(@rsa1024.public_key.to_der, req.public_key.to_der)
- req = issue_csr(0, @dn, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new)
+ req = issue_csr(0, @dn, @dsa512, OpenSSL::Digest::SHA1.new)
assert_equal(@dsa512.public_key.to_der, req.public_key.to_der)
req = OpenSSL::X509::Request.new(req.to_der)
assert_equal(@dsa512.public_key.to_der, req.public_key.to_der)
@@ -121,7 +122,7 @@ class OpenSSL::TestX509Request < OpenSSL::TestCase
end
def test_sign_and_verify_dsa
- req = issue_csr(0, @dn, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new)
+ req = issue_csr(0, @dn, @dsa512, OpenSSL::Digest::SHA1.new)
assert_equal(false, request_error_returns_false { req.verify(@rsa1024) })
assert_equal(false, request_error_returns_false { req.verify(@rsa2048) })
assert_equal(false, req.verify(@dsa256))
@@ -130,18 +131,6 @@ class OpenSSL::TestX509Request < OpenSSL::TestCase
assert_equal(false, req.verify(@dsa512))
end
- def test_sign_and_verify_rsa_dss1
- req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::DSS1.new)
- assert_equal(true, req.verify(@rsa1024))
- assert_equal(false, req.verify(@rsa2048))
- assert_equal(false, request_error_returns_false { req.verify(@dsa256) })
- assert_equal(false, request_error_returns_false { req.verify(@dsa512) })
- req.version = 1
- assert_equal(false, req.verify(@rsa1024))
- rescue OpenSSL::X509::RequestError
- pend
- end if defined?(OpenSSL::Digest::DSS1)
-
def test_sign_and_verify_dsa_md5
assert_raise(OpenSSL::X509::RequestError){
issue_csr(0, @dn, @dsa512, OpenSSL::Digest::MD5.new) }
@@ -152,6 +141,16 @@ class OpenSSL::TestX509Request < OpenSSL::TestCase
assert_equal(req.to_der, req.dup.to_der)
end
+ def test_eq
+ req1 = issue_csr(0, @dn, @rsa1024, "sha1")
+ req2 = issue_csr(0, @dn, @rsa1024, "sha1")
+ req3 = issue_csr(0, @dn, @rsa1024, "sha256")
+
+ assert_equal false, req1 == 12345
+ assert_equal true, req1 == req2
+ assert_equal false, req1 == req3
+ end
+
private
def request_error_returns_false
diff --git a/test/openssl/test_x509store.rb b/test/openssl/test_x509store.rb
index e0fa07ac16..6412249b93 100644
--- a/test/openssl/test_x509store.rb
+++ b/test/openssl/test_x509store.rb
@@ -1,14 +1,15 @@
# frozen_string_literal: false
require_relative "utils"
-if defined?(OpenSSL::TestUtils)
+if defined?(OpenSSL)
class OpenSSL::TestX509Store < OpenSSL::TestCase
def setup
- @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
- @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
- @dsa256 = OpenSSL::TestUtils::TEST_KEY_DSA256
- @dsa512 = OpenSSL::TestUtils::TEST_KEY_DSA512
+ super
+ @rsa1024 = Fixtures.pkey("rsa1024")
+ @rsa2048 = Fixtures.pkey("rsa2048")
+ @dsa256 = Fixtures.pkey("dsa256")
+ @dsa512 = Fixtures.pkey("dsa512")
@ca1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA1")
@ca2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA2")
@ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
@@ -25,16 +26,33 @@ class OpenSSL::TestX509Store < OpenSSL::TestCase
ctx.verify
end
- def issue_cert(*args)
- OpenSSL::TestUtils.issue_cert(*args)
- end
+ def test_add_file
+ ca_exts = [
+ ["basicConstraints", "CA:TRUE", true],
+ ["keyUsage", "cRLSign,keyCertSign", true],
+ ]
+ cert1 = issue_cert(@ca1, @rsa1024, 1, ca_exts, nil, nil)
+ cert2 = issue_cert(@ca2, @rsa2048, 1, ca_exts, nil, nil)
+ tmpfile = Tempfile.open { |f| f << cert1.to_pem << cert2.to_pem; f }
- def issue_crl(*args)
- OpenSSL::TestUtils.issue_crl(*args)
+ store = OpenSSL::X509::Store.new
+ assert_equal false, store.verify(cert1)
+ assert_equal false, store.verify(cert2)
+ store.add_file(tmpfile.path)
+ assert_equal true, store.verify(cert1)
+ assert_equal true, store.verify(cert2)
+
+ # OpenSSL < 1.1.1 leaks an error on a duplicate certificate
+ assert_nothing_raised { store.add_file(tmpfile.path) }
+ assert_equal [], OpenSSL.errors
+ ensure
+ tmpfile and tmpfile.close!
end
def test_verify
- now = Time.at(Time.now.to_i)
+ # OpenSSL uses time(2) while Time.now uses clock_gettime(CLOCK_REALTIME),
+ # and there may be difference.
+ now = Time.now - 3
ca_exts = [
["basicConstraints","CA:TRUE",true],
["keyUsage","cRLSign,keyCertSign",true],
@@ -42,18 +60,15 @@ class OpenSSL::TestX509Store < OpenSSL::TestCase
ee_exts = [
["keyUsage","keyEncipherment,digitalSignature",true],
]
- ca1_cert = issue_cert(@ca1, @rsa2048, 1, now, now+3600, ca_exts,
- nil, nil, OpenSSL::Digest::SHA1.new)
- ca2_cert = issue_cert(@ca2, @rsa1024, 2, now, now+1800, ca_exts,
- ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
- ee1_cert = issue_cert(@ee1, @dsa256, 10, now, now+1800, ee_exts,
- ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
- ee2_cert = issue_cert(@ee2, @dsa512, 20, now, now+1800, ee_exts,
- ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
- ee3_cert = issue_cert(@ee2, @dsa512, 30, now-100, now-1, ee_exts,
- ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
- ee4_cert = issue_cert(@ee2, @dsa512, 40, now+1000, now+2000, ee_exts,
- ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+ ca1_cert = issue_cert(@ca1, @rsa2048, 1, ca_exts, nil, nil)
+ ca2_cert = issue_cert(@ca2, @rsa1024, 2, ca_exts, ca1_cert, @rsa2048,
+ not_after: now+1800)
+ ee1_cert = issue_cert(@ee1, @dsa256, 10, ee_exts, ca2_cert, @rsa1024)
+ ee2_cert = issue_cert(@ee2, @dsa512, 20, ee_exts, ca2_cert, @rsa1024)
+ ee3_cert = issue_cert(@ee2, @dsa512, 30, ee_exts, ca2_cert, @rsa1024,
+ not_before: now-100, not_after: now-1)
+ ee4_cert = issue_cert(@ee2, @dsa512, 40, ee_exts, ca2_cert, @rsa1024,
+ not_before: now+1000, not_after: now+2000,)
revoke_info = []
crl1 = issue_crl(revoke_info, 1, now, now+1800, [],
@@ -194,9 +209,9 @@ class OpenSSL::TestX509Store < OpenSSL::TestCase
end
def test_set_errors
+ return if openssl?(1, 1, 0) || libressl?
now = Time.now
- ca1_cert = issue_cert(@ca1, @rsa2048, 1, now, now+3600, [],
- nil, nil, OpenSSL::Digest::SHA1.new)
+ ca1_cert = issue_cert(@ca1, @rsa2048, 1, [], nil, nil)
store = OpenSSL::X509::Store.new
store.add_cert(ca1_cert)
assert_raise(OpenSSL::X509::StoreError){
@@ -210,17 +225,9 @@ class OpenSSL::TestX509Store < OpenSSL::TestCase
crl2 = issue_crl(revoke_info, 2, now+1800, now+3600, [],
ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
store.add_crl(crl1)
- if /0\.9\.8.*-rhel/ =~ OpenSSL::OPENSSL_VERSION
- # RedHat is distributing a patched version of OpenSSL that allows
- # multiple CRL for a key (multi-crl.patch)
- assert_nothing_raised do
- store.add_crl(crl2) # add CRL issued by same CA twice.
- end
- else
- assert_raise(OpenSSL::X509::StoreError){
- store.add_crl(crl2) # add CRL issued by same CA twice.
- }
- end
+ assert_raise(OpenSSL::X509::StoreError){
+ store.add_crl(crl2) # add CRL issued by same CA twice.
+ }
end
def test_dup
diff --git a/test/openssl/ut_eof.rb b/test/openssl/ut_eof.rb
index 6de41c4a70..bd62fd50f9 100644
--- a/test/openssl/ut_eof.rb
+++ b/test/openssl/ut_eof.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: false
require 'test/unit'
+if defined?(OpenSSL)
+
module OpenSSL::TestEOF
def test_eof_0
open_file("") {|f|
@@ -127,3 +129,5 @@ module OpenSSL::TestEOF
end
end
end
+
+end
diff --git a/test/openssl/utils.rb b/test/openssl/utils.rb
index 6f3a3c6d1c..1da3d8f797 100644
--- a/test/openssl/utils.rb
+++ b/test/openssl/utils.rb
@@ -9,130 +9,55 @@ begin
rescue LoadError
end
+# Compile OpenSSL with crypto-mdebug and run this test suite with OSSL_MDEBUG=1
+# environment variable to enable memory leak check.
+if ENV["OSSL_MDEBUG"] == "1"
+ if OpenSSL.respond_to?(:print_mem_leaks)
+ OpenSSL.mem_check_start
+
+ END {
+ GC.start
+ case OpenSSL.print_mem_leaks
+ when nil
+ warn "mdebug: check what is printed"
+ when true
+ raise "mdebug: memory leaks detected"
+ end
+ }
+ else
+ warn "OSSL_MDEBUG=1 is specified but OpenSSL is not built with crypto-mdebug"
+ end
+end
+
require "test/unit"
-require "digest/md5"
-require 'tempfile'
-require "rbconfig"
+require "tempfile"
require "socket"
require "envutil"
-module OpenSSL::TestUtils
- TEST_KEY_RSA1024 = OpenSSL::PKey::RSA.new <<-_end_of_pem_
------BEGIN RSA PRIVATE KEY-----
-MIICXgIBAAKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7Cx
-aKPERYHsk4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/
-Q3geLv8ZD9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQAB
-AoGBAKSl/MQarye1yOysqX6P8fDFQt68VvtXkNmlSiKOGuzyho0M+UVSFcs6k1L0
-maDE25AMZUiGzuWHyaU55d7RXDgeskDMakD1v6ZejYtxJkSXbETOTLDwUWTn618T
-gnb17tU1jktUtU67xK/08i/XodlgnQhs6VoHTuCh3Hu77O6RAkEA7+gxqBuZR572
-74/akiW/SuXm0SXPEviyO1MuSRwtI87B02D0qgV8D1UHRm4AhMnJ8MCs1809kMQE
-JiQUCrp9mQJBANlt2ngBO14us6NnhuAseFDTBzCHXwUUu1YKHpMMmxpnGqaldGgX
-sOZB3lgJsT9VlGf3YGYdkLTNVbogQKlKpB8CQQDiSwkb4vyQfDe8/NpU5Not0fII
-8jsDUCb+opWUTMmfbxWRR3FBNu8wnym/m19N4fFj8LqYzHX4KY0oVPu6qvJxAkEA
-wa5snNekFcqONLIE4G5cosrIrb74sqL8GbGb+KuTAprzj5z1K8Bm0UW9lTjVDjDi
-qRYgZfZSL+x1P/54+xTFSwJAY1FxA/N3QPCXCjPh5YqFxAMQs2VVYTfg+t0MEcJD
-dPMQD5JX6g5HKnHFg2mZtoXQrWmJSn7p8GJK8yNTopEErA==
------END RSA PRIVATE KEY-----
- _end_of_pem_
-
- TEST_KEY_RSA2048 = OpenSSL::PKey::RSA.new <<-_end_of_pem_
------BEGIN RSA PRIVATE KEY-----
-MIIEpAIBAAKCAQEAuV9ht9J7k4NBs38jOXvvTKY9gW8nLICSno5EETR1cuF7i4pN
-s9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enenfzq/t/e/1IRW0wkJUJUFQign
-4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWmqbjs07JbuS4QQGGXLc+Su96D
-kYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v68JkRFIhdGlb6JL8fllf/A/bl
-NwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX9KZYcU00mOX+fdxOSnGqS/8J
-DRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wIDAQABAoIBAAzsamqfYQAqwXTb
-I0CJtGg6msUgU7HVkOM+9d3hM2L791oGHV6xBAdpXW2H8LgvZHJ8eOeSghR8+dgq
-PIqAffo4x1Oma+FOg3A0fb0evyiACyrOk+EcBdbBeLo/LcvahBtqnDfiUMQTpy6V
-seSoFCwuN91TSCeGIsDpRjbG1vxZgtx+uI+oH5+ytqJOmfCksRDCkMglGkzyfcl0
-Xc5CUhIJ0my53xijEUQl19rtWdMnNnnkdbG8PT3LZlOta5Do86BElzUYka0C6dUc
-VsBDQ0Nup0P6rEQgy7tephHoRlUGTYamsajGJaAo1F3IQVIrRSuagi7+YpSpCqsW
-wORqorkCgYEA7RdX6MDVrbw7LePnhyuaqTiMK+055/R1TqhB1JvvxJ1CXk2rDL6G
-0TLHQ7oGofd5LYiemg4ZVtWdJe43BPZlVgT6lvL/iGo8JnrncB9Da6L7nrq/+Rvj
-XGjf1qODCK+LmreZWEsaLPURIoR/Ewwxb9J2zd0CaMjeTwafJo1CZvcCgYEAyCgb
-aqoWvUecX8VvARfuA593Lsi50t4MEArnOXXcd1RnXoZWhbx5rgO8/ATKfXr0BK/n
-h2GF9PfKzHFm/4V6e82OL7gu/kLy2u9bXN74vOvWFL5NOrOKPM7Kg+9I131kNYOw
-Ivnr/VtHE5s0dY7JChYWE1F3vArrOw3T00a4CXUCgYEA0SqY+dS2LvIzW4cHCe9k
-IQqsT0yYm5TFsUEr4sA3xcPfe4cV8sZb9k/QEGYb1+SWWZ+AHPV3UW5fl8kTbSNb
-v4ng8i8rVVQ0ANbJO9e5CUrepein2MPL0AkOATR8M7t7dGGpvYV0cFk8ZrFx0oId
-U0PgYDotF/iueBWlbsOM430CgYEAqYI95dFyPI5/AiSkY5queeb8+mQH62sdcCCr
-vd/w/CZA/K5sbAo4SoTj8dLk4evU6HtIa0DOP63y071eaxvRpTNqLUOgmLh+D6gS
-Cc7TfLuFrD+WDBatBd5jZ+SoHccVrLR/4L8jeodo5FPW05A+9gnKXEXsTxY4LOUC
-9bS4e1kCgYAqVXZh63JsMwoaxCYmQ66eJojKa47VNrOeIZDZvd2BPVf30glBOT41
-gBoDG3WMPZoQj9pb7uMcrnvs4APj2FIhMU8U15LcPAj59cD6S6rWnAxO8NFK7HQG
-4Jxg3JNNf8ErQoCHb1B3oVdXJkmbJkARoDpBKmTCgKtP8ADYLmVPQw==
------END RSA PRIVATE KEY-----
- _end_of_pem_
-
- TEST_KEY_DSA256 = OpenSSL::PKey::DSA.new <<-_end_of_pem_
------BEGIN DSA PRIVATE KEY-----
-MIH3AgEAAkEAhk2libbY2a8y2Pt21+YPYGZeW6wzaW2yfj5oiClXro9XMR7XWLkE
-9B7XxLNFCS2gmCCdMsMW1HulaHtLFQmB2wIVAM43JZrcgpu6ajZ01VkLc93gu/Ed
-AkAOhujZrrKV5CzBKutKLb0GVyVWmdC7InoNSMZEeGU72rT96IjM59YzoqmD0pGM
-3I1o4cGqg1D1DfM1rQlnN1eSAkBq6xXfEDwJ1mLNxF6q8Zm/ugFYWR5xcX/3wFiT
-b4+EjHP/DbNh9Vm5wcfnDBJ1zKvrMEf2xqngYdrV/3CiGJeKAhRvL57QvJZcQGvn
-ISNX5cMzFHRW3Q==
------END DSA PRIVATE KEY-----
- _end_of_pem_
-
- TEST_KEY_DSA512 = OpenSSL::PKey::DSA.new <<-_end_of_pem_
------BEGIN DSA PRIVATE KEY-----
-MIH4AgEAAkEA5lB4GvEwjrsMlGDqGsxrbqeFRh6o9OWt6FgTYiEEHaOYhkIxv0Ok
-RZPDNwOG997mDjBnvDJ1i56OmS3MbTnovwIVAJgub/aDrSDB4DZGH7UyarcaGy6D
-AkB9HdFw/3td8K4l1FZHv7TCZeJ3ZLb7dF3TWoGUP003RCqoji3/lHdKoVdTQNuR
-S/m6DlCwhjRjiQ/lBRgCLCcaAkEAjN891JBjzpMj4bWgsACmMggFf57DS0Ti+5++
-Q1VB8qkJN7rA7/2HrCR3gTsWNb1YhAsnFsoeRscC+LxXoXi9OAIUBG98h4tilg6S
-55jreJD3Se3slps=
------END DSA PRIVATE KEY-----
- _end_of_pem_
-
- TEST_KEY_DSA1024 = OpenSSL::PKey::DSA.new <<-_end_of_pem_
------BEGIN DSA PRIVATE KEY-----
-MIIBugIBAAKBgQCH9aAoXvWWThIjkA6D+nI1F9ksF9iDq594rkiGNOT9sPDOdB+n
-D+qeeeeloRlj19ymCSADPI0ZLRgkchkAEnY2RnqnhHOjVf/roGgRbW+iQDMbQ9wa
-/pvc6/fAbsu1goE1hBYjm98/sZEeXavj8tR56IXnjF1b6Nx0+sgeUKFKEQIVAMiz
-4BJUFeTtddyM4uadBM7HKLPRAoGAZdLBSYNGiij7vAjesF5mGUKTIgPd+JKuBEDx
-OaBclsgfdoyoF/TMOkIty+PVlYD+//Vl2xnoUEIRaMXHwHfm0r2xUX++oeRaSScg
-YizJdUxe5jvBuBszGPRc/mGpb9YvP0sB+FL1KmuxYmdODfCe51zl8uM/CVhouJ3w
-DjmRGscCgYAuFlfC7p+e8huCKydfcv/beftqjewiOPpQ3u5uI6KPCtCJPpDhs3+4
-IihH2cPsAlqwGF4tlibW1+/z/OZ1AZinPK3y7b2jSJASEaPeEltVzB92hcd1khk2
-jTYcmSsV4VddplOPK9czytR/GbbibxsrhhgZUbd8LPbvIgaiadJ1PgIUBnJ/5vN2
-CVArsEzlPUCbohPvZnE=
------END DSA PRIVATE KEY-----
- _end_of_pem_
-
-if defined?(OpenSSL::PKey::EC)
-
- TEST_KEY_EC_P256V1 = OpenSSL::PKey::EC.new <<-_end_of_pem_
------BEGIN EC PRIVATE KEY-----
-MHcCAQEEIID49FDqcf1O1eO8saTgG70UbXQw9Fqwseliit2aWhH1oAoGCCqGSM49
-AwEHoUQDQgAEFglk2c+oVUIKQ64eZG9bhLNPWB7lSZ/ArK41eGy5wAzU/0G51Xtt
-CeBUl+MahZtn9fO1JKdF4qJmS39dXnpENg==
------END EC PRIVATE KEY-----
- _end_of_pem_
-
-end
-
- TEST_KEY_DH1024 = OpenSSL::PKey::DH.new <<-_end_of_pem_
------BEGIN DH PARAMETERS-----
-MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0
-pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG
-AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
------END DH PARAMETERS-----
- _end_of_pem_
+if defined?(OpenSSL)
- TEST_KEY_DH1024.set_key(OpenSSL::BN.new("556AF1598AE69899867CEBA9F29CE4862B884C2B43C9019EA0231908F6EFA785E3C462A6ECB16DF676866E997FFB72B487DC7967C58C3CA38CE974473BF19B2AA5DCBF102735572EBA6F353F6F0BBE7FF1DE1B07FE1381A355C275C33405004317F9491B5955F191F6615A63B30E55A027FB88A1A4B25608E09EEE68A7DF32D", 16),
- OpenSSL::BN.new("48561834C67E65FFD2A9B47F41E5E78FDC95C387428FDB1E4B0188B64D1643C3A8D3455B945B7E8C4D166010C7C2CE23BFB9BEF43D0348FE7FA5284B0225E7FE1537546D114E3D8A4411B9B9351AB451E1A358F50ED61B1F00DA29336EEBBD649980AC86D76AF8BBB065298C2052672EEF3EF13AB47A15275FC2836F3AC74CEA", 16))
+module OpenSSL::TestUtils
+ module Fixtures
+ module_function
+
+ def pkey(name)
+ OpenSSL::PKey.read(read_file("pkey", name))
+ rescue OpenSSL::PKey::PKeyError
+ # TODO: DH parameters can be read by OpenSSL::PKey.read atm
+ OpenSSL::PKey::DH.new(read_file("pkey", name))
+ end
- DSA_SIGNATURE_DIGEST = OpenSSL::OPENSSL_VERSION_NUMBER > 0x10000000 ?
- OpenSSL::Digest::SHA1 :
- OpenSSL::Digest::DSS1
+ def read_file(category, name)
+ @file_cache ||= {}
+ @file_cache[[category, name]] ||=
+ File.read(File.join(__dir__, "fixtures", category, name + ".pem"))
+ end
+ end
module_function
- def issue_cert(dn, key, serial, not_before, not_after, extensions,
- issuer, issuer_key, digest)
+ def issue_cert(dn, key, serial, extensions, issuer, issuer_key,
+ not_before: nil, not_after: nil, digest: "sha256")
cert = OpenSSL::X509::Certificate.new
issuer = cert unless issuer
issuer_key = key unless issuer_key
@@ -140,9 +65,10 @@ AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
cert.serial = serial
cert.subject = dn
cert.issuer = issuer.subject
- cert.public_key = key.public_key
- cert.not_before = not_before
- cert.not_after = not_after
+ cert.public_key = key
+ now = Time.now
+ cert.not_before = not_before || now - 3600
+ cert.not_after = not_after || now + 3600
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate = issuer
@@ -190,181 +116,203 @@ AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
OpenSSL::Digest::SHA1.hexdigest(pkvalue).scan(/../).join(":").upcase
end
- def silent
- begin
- back, $VERBOSE = $VERBOSE, nil
- yield
- ensure
- $VERBOSE = back
- end
+ def openssl?(major = nil, minor = nil, fix = nil, patch = 0)
+ return false if OpenSSL::OPENSSL_VERSION.include?("LibreSSL")
+ return true unless major
+ OpenSSL::OPENSSL_VERSION_NUMBER >=
+ major * 0x10000000 + minor * 0x100000 + fix * 0x1000 + patch * 0x10
end
- class OpenSSL::TestCase < Test::Unit::TestCase
- def teardown
- # OpenSSL error stack must be empty
- assert_equal([], OpenSSL.errors)
- end
+ def libressl?(major = nil, minor = nil, fix = nil)
+ version = OpenSSL::OPENSSL_VERSION.scan(/LibreSSL (\d+)\.(\d+)\.(\d+).*/)[0]
+ return false unless version
+ !major || (version.map(&:to_i) <=> [major, minor, fix]) >= 0
end
+end
- class OpenSSL::SSLTestCase < OpenSSL::TestCase
- RUBY = EnvUtil.rubybin
- ITERATIONS = ($0 == __FILE__) ? 100 : 10
-
- def setup
- @ca_key = OpenSSL::TestUtils::TEST_KEY_RSA2048
- @svr_key = OpenSSL::TestUtils::TEST_KEY_RSA1024
- @cli_key = OpenSSL::TestUtils::TEST_KEY_DSA1024
- @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
- @svr = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
- @cli = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
- now = Time.at(Time.now.to_i)
- ca_exts = [
- ["basicConstraints","CA:TRUE",true],
- ["keyUsage","cRLSign,keyCertSign",true],
- ]
- ee_exts = [
- ["keyUsage","keyEncipherment,digitalSignature",true],
- ]
- @ca_cert = issue_cert(@ca, @ca_key, 1, now, now+3600, ca_exts, nil, nil, OpenSSL::Digest::SHA1.new)
- @svr_cert = issue_cert(@svr, @svr_key, 2, now, now+1800, ee_exts, @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
- @cli_cert = issue_cert(@cli, @cli_key, 3, now, now+1800, ee_exts, @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
- @server = nil
- end
+class OpenSSL::TestCase < Test::Unit::TestCase
+ include OpenSSL::TestUtils
+ extend OpenSSL::TestUtils
- def issue_cert(*arg)
- OpenSSL::TestUtils.issue_cert(*arg)
+ def setup
+ if ENV["OSSL_GC_STRESS"] == "1"
+ GC.stress = true
end
+ end
- def issue_crl(*arg)
- OpenSSL::TestUtils.issue_crl(*arg)
+ def teardown
+ if ENV["OSSL_GC_STRESS"] == "1"
+ GC.stress = false
end
+ # OpenSSL error stack must be empty
+ assert_equal([], OpenSSL.errors)
+ end
+end
- def readwrite_loop(ctx, ssl)
- while line = ssl.gets
- ssl.write(line)
- end
- rescue OpenSSL::SSL::SSLError
- rescue IOError
- ensure
- ssl.close rescue nil
- end
+class OpenSSL::SSLTestCase < OpenSSL::TestCase
+ RUBY = EnvUtil.rubybin
+ ITERATIONS = ($0 == __FILE__) ? 100 : 10
+
+ def setup
+ super
+ @ca_key = Fixtures.pkey("rsa-1")
+ @svr_key = Fixtures.pkey("rsa-2")
+ @cli_key = Fixtures.pkey("rsa-3")
+ @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
+ @svr = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
+ @cli = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
+ ca_exts = [
+ ["basicConstraints","CA:TRUE",true],
+ ["keyUsage","cRLSign,keyCertSign",true],
+ ]
+ ee_exts = [
+ ["keyUsage","keyEncipherment,digitalSignature",true],
+ ]
+ @ca_cert = issue_cert(@ca, @ca_key, 1, ca_exts, nil, nil)
+ @svr_cert = issue_cert(@svr, @svr_key, 2, ee_exts, @ca_cert, @ca_key)
+ @cli_cert = issue_cert(@cli, @cli_key, 3, ee_exts, @ca_cert, @ca_key)
+ @server = nil
+ end
- def server_loop(ctx, ssls, stop_pipe_r, ignore_listener_error, server_proc, threads)
- loop do
- ssl = nil
- begin
- readable, = IO.select([ssls, stop_pipe_r])
- if readable.include? stop_pipe_r
- return
- end
- ssl = ssls.accept
- rescue OpenSSL::SSL::SSLError, Errno::ECONNRESET
- if ignore_listener_error
- retry
- else
- raise
- end
- end
+ def tls12_supported?
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
+ true
+ rescue
+ end
- th = Thread.start do
- server_proc.call(ctx, ssl)
- end
- threads << th
- end
- rescue Errno::EBADF, IOError, Errno::EINVAL, Errno::ECONNABORTED, Errno::ENOTSOCK, Errno::ECONNRESET
- if !ignore_listener_error
- raise
- end
+ def readwrite_loop(ctx, ssl)
+ while line = ssl.gets
+ ssl.write(line)
end
+ end
- def start_server(verify_mode: OpenSSL::SSL::VERIFY_NONE, start_immediately: true,
- ctx_proc: nil, server_proc: method(:readwrite_loop),
- ignore_listener_error: false, &block)
- IO.pipe {|stop_pipe_r, stop_pipe_w|
- store = OpenSSL::X509::Store.new
- store.add_cert(@ca_cert)
- store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.cert_store = store
- ctx.cert = @svr_cert
- ctx.key = @svr_key
- ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 }
- begin
- ctx.ecdh_curves = "P-256"
- rescue NotImplementedError
- end
- ctx.verify_mode = verify_mode
- ctx_proc.call(ctx) if ctx_proc
-
- Socket.do_not_reverse_lookup = true
- tcps = nil
- tcps = TCPServer.new("127.0.0.1", 0)
- port = tcps.connect_address.ip_port
-
- ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
- ssls.start_immediately = start_immediately
-
- threads = []
- begin
- server = Thread.new do
- begin
- server_loop(ctx, ssls, stop_pipe_r, ignore_listener_error, server_proc, threads)
- ensure
- tcps.close
+ def start_server(verify_mode: OpenSSL::SSL::VERIFY_NONE, start_immediately: true,
+ ctx_proc: nil, server_proc: method(:readwrite_loop),
+ ignore_listener_error: false, &block)
+ IO.pipe {|stop_pipe_r, stop_pipe_w|
+ store = OpenSSL::X509::Store.new
+ store.add_cert(@ca_cert)
+ store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.cert_store = store
+ ctx.cert = @svr_cert
+ ctx.key = @svr_key
+ ctx.tmp_dh_callback = proc { Fixtures.pkey("dh-1") }
+ ctx.verify_mode = verify_mode
+ ctx_proc.call(ctx) if ctx_proc
+
+ Socket.do_not_reverse_lookup = true
+ tcps = TCPServer.new("127.0.0.1", 0)
+ port = tcps.connect_address.ip_port
+
+ ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
+ ssls.start_immediately = start_immediately
+
+ threads = []
+ begin
+ server_thread = Thread.new do
+ if Thread.method_defined?(:report_on_exception=) # Ruby >= 2.4
+ Thread.current.report_on_exception = false
+ end
+
+ begin
+ loop do
+ begin
+ readable, = IO.select([ssls, stop_pipe_r])
+ break if readable.include? stop_pipe_r
+ ssl = ssls.accept
+ rescue OpenSSL::SSL::SSLError, IOError, Errno::EBADF, Errno::EINVAL,
+ Errno::ECONNABORTED, Errno::ENOTSOCK, Errno::ECONNRESET
+ retry if ignore_listener_error
+ raise
+ end
+
+ th = Thread.new do
+ if Thread.method_defined?(:report_on_exception=)
+ Thread.current.report_on_exception = false
+ end
+
+ begin
+ server_proc.call(ctx, ssl)
+ ensure
+ ssl.close
+ end
+ true
+ end
+ threads << th
end
+ ensure
+ tcps.close
end
- threads.unshift server
+ end
- $stderr.printf("SSL server started: pid=%d port=%d\n", $$, port) if $DEBUG
+ client_thread = Thread.new do
+ if Thread.method_defined?(:report_on_exception=)
+ Thread.current.report_on_exception = false
+ end
- client = Thread.new do
- begin
- block.call(server, port.to_i)
- ensure
- stop_pipe_w.close
- end
+ begin
+ block.call(port)
+ ensure
+ # Stop accepting new connection
+ stop_pipe_w.close
+ server_thread.join
end
- threads.unshift client
- ensure
- assert_join_threads(threads)
end
- }
- end
+ threads.unshift client_thread
+ ensure
+ # Terminate existing connections. If a thread did 'pend', re-raise it.
+ pend = nil
+ threads.each { |th|
+ begin
+ th.join(10) or
+ th.raise(RuntimeError, "[start_server] thread did not exit in 10 secs")
+ rescue (defined?(MiniTest::Skip) ? MiniTest::Skip : Test::Unit::PendedError)
+ # MiniTest::Skip is for the Ruby tree
+ pend = $!
+ rescue Exception
+ end
+ }
+ raise pend if pend
+ assert_join_threads(threads)
+ end
+ }
end
+end
- class OpenSSL::PKeyTestCase < OpenSSL::TestCase
- def check_component(base, test, keys)
- keys.each { |comp|
- assert_equal base.send(comp), test.send(comp)
- }
- end
+class OpenSSL::PKeyTestCase < OpenSSL::TestCase
+ def check_component(base, test, keys)
+ keys.each { |comp|
+ assert_equal base.send(comp), test.send(comp)
+ }
+ end
- def dup_public(key)
- case key
- when OpenSSL::PKey::RSA
- rsa = OpenSSL::PKey::RSA.new
- rsa.set_key(key.n, key.e, nil)
- rsa
- when OpenSSL::PKey::DSA
- dsa = OpenSSL::PKey::DSA.new
- dsa.set_pqg(key.p, key.q, key.g)
- dsa.set_key(key.pub_key, nil)
- dsa
- when OpenSSL::PKey::DH
- dh = OpenSSL::PKey::DH.new
- dh.set_pqg(key.p, nil, key.g)
- dh
+ def dup_public(key)
+ case key
+ when OpenSSL::PKey::RSA
+ rsa = OpenSSL::PKey::RSA.new
+ rsa.set_key(key.n, key.e, nil)
+ rsa
+ when OpenSSL::PKey::DSA
+ dsa = OpenSSL::PKey::DSA.new
+ dsa.set_pqg(key.p, key.q, key.g)
+ dsa.set_key(key.pub_key, nil)
+ dsa
+ when OpenSSL::PKey::DH
+ dh = OpenSSL::PKey::DH.new
+ dh.set_pqg(key.p, nil, key.g)
+ dh
+ else
+ if defined?(OpenSSL::PKey::EC) && OpenSSL::PKey::EC === key
+ ec = OpenSSL::PKey::EC.new(key.group)
+ ec.public_key = key.public_key
+ ec
else
- if defined?(OpenSSL::PKey::EC) && OpenSSL::PKey::EC === key
- ec = OpenSSL::PKey::EC.new(key.group)
- ec.public_key = key.public_key
- ec
- else
- raise "unknown key type"
- end
+ raise "unknown key type"
end
end
end
+end
-end if defined?(OpenSSL::OPENSSL_LIBRARY_VERSION) and
- /\AOpenSSL +0\./ !~ OpenSSL::OPENSSL_LIBRARY_VERSION
+end
diff --git a/test/optparse/test_acceptable.rb b/test/optparse/test_acceptable.rb
index 0c7590bae3..5c3fbdb10c 100644
--- a/test/optparse/test_acceptable.rb
+++ b/test/optparse/test_acceptable.rb
@@ -85,6 +85,9 @@ class TestOptionParser::Acceptable < TestOptionParser
assert_equal(%w"", no_error {@opt.parse!(%w"--numeric 1/2")})
assert_equal(Rational(1, 2), @numeric)
+ assert_equal(%w"", no_error {@opt.parse!(%w"--numeric 010")})
+ assert_equal(8, @numeric)
+
assert_equal(%w"", no_error {@opt.parse!(%w"--numeric 1.2/2.3")})
assert_equal(Rational(12, 23), @numeric)
@@ -108,16 +111,16 @@ class TestOptionParser::Acceptable < TestOptionParser
assert_equal(%w"", no_error {@opt.parse!(%w"--decimal-integer 10")})
assert_equal(10, @decimal_integer)
+ assert_equal(%w"", no_error {@opt.parse!(%w"--decimal-integer 010")})
+ assert_equal(10, @decimal_integer)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--decimal-integer 09")})
+ assert_equal(9, @decimal_integer)
+
assert_raise(OptionParser::InvalidArgument) do
@opt.parse!(%w"--decimal-integer 0b1")
end
- e = assert_raise(OptionParser::InvalidArgument) do
- @opt.parse!(%w"--decimal-integer 09")
- end
-
- assert_equal("invalid argument: --decimal-integer 09", e.message)
-
assert_raise(OptionParser::InvalidArgument) do
@opt.parse!(%w"--decimal-integer x")
end
diff --git a/test/optparse/test_autoconf.rb b/test/optparse/test_autoconf.rb
index 4b3616f816..3be4a4c598 100644
--- a/test/optparse/test_autoconf.rb
+++ b/test/optparse/test_autoconf.rb
@@ -14,7 +14,7 @@ class TestOptionParser::AutoConf < Test::Unit::TestCase
end
class DummyOutput < String
- alias write <<
+ alias write concat
end
def no_error(*args)
$stderr, stderr = DummyOutput.new, $stderr
diff --git a/test/optparse/test_bash_completion.rb b/test/optparse/test_bash_completion.rb
index 714fd35cff..513e986f66 100644
--- a/test/optparse/test_bash_completion.rb
+++ b/test/optparse/test_bash_completion.rb
@@ -40,4 +40,9 @@ class TestOptionParser::BashCompletion < Test::Unit::TestCase
def test_long_for_option_complete
assert_equal(%w[hello help], @opt.candidate("--for=h"))
end
+
+ def test_case_sensitive
+ @opt.define("-Z") {}
+ assert_equal(%w[-z], @opt.candidate("-z"))
+ end
end
diff --git a/test/optparse/test_kwargs.rb b/test/optparse/test_kwargs.rb
index 68fe207a2c..78d7e2ee9c 100644
--- a/test/optparse/test_kwargs.rb
+++ b/test/optparse/test_kwargs.rb
@@ -14,7 +14,7 @@ class TestOptionParser::KwArg < Test::Unit::TestCase
end
class DummyOutput < String
- alias write <<
+ alias write concat
end
def assert_no_error(*args)
$stderr, stderr = DummyOutput.new, $stderr
diff --git a/test/optparse/test_optparse.rb b/test/optparse/test_optparse.rb
index a2540db241..e4aeb07aac 100644
--- a/test/optparse/test_optparse.rb
+++ b/test/optparse/test_optparse.rb
@@ -9,7 +9,7 @@ class TestOptionParser < Test::Unit::TestCase
end
class DummyOutput < String
- alias write <<
+ alias write concat
end
def assert_no_error(*args)
$stderr, stderr = DummyOutput.new, $stderr
diff --git a/test/optparse/test_summary.rb b/test/optparse/test_summary.rb
index b743aa00c1..67b05672d4 100644
--- a/test/optparse/test_summary.rb
+++ b/test/optparse/test_summary.rb
@@ -44,4 +44,15 @@ class TestOptionParser::SummaryTest < TestOptionParser
assert_equal("foo bar\n", o.to_s, bug6348)
assert_equal(["foo bar"], o.to_a, bug6348)
end
+
+ def test_ver
+ o = OptionParser.new("foo bar")
+ o.program_name = "foo"
+ assert_warning('') {assert_nil(o.version)}
+ assert_warning('') {assert_nil(o.release)}
+ o.version = [0, 1]
+ assert_equal "foo 0.1", o.ver
+ o.release = "rel"
+ assert_equal "foo 0.1 (rel)", o.ver
+ end
end
diff --git a/test/ostruct/test_ostruct.rb b/test/ostruct/test_ostruct.rb
index aeccc9209a..61a4822810 100644
--- a/test/ostruct/test_ostruct.rb
+++ b/test/ostruct/test_ostruct.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'ostruct'
@@ -52,27 +52,30 @@ class TC_OpenStruct < Test::Unit::TestCase
foo.bar = 1
foo.baz = 2
assert_equal("#<OpenStruct bar=1, baz=2>", foo.inspect)
+ assert_equal(false, foo.inspect.frozen?)
foo = OpenStruct.new
foo.bar = OpenStruct.new
assert_equal('#<OpenStruct bar=#<OpenStruct>>', foo.inspect)
foo.bar.foo = foo
assert_equal('#<OpenStruct bar=#<OpenStruct foo=#<OpenStruct ...>>>', foo.inspect)
+ assert_equal(false, foo.inspect.frozen?)
end
def test_frozen
o = OpenStruct.new(foo: 42)
o.a = 'a'
o.freeze
- assert_raise(RuntimeError) {o.b = 'b'}
+ expected_error = defined?(FrozenError) ? FrozenError : RuntimeError
+ assert_raise(expected_error) {o.b = 'b'}
assert_not_respond_to(o, :b)
- assert_raise(RuntimeError) {o.a = 'z'}
+ assert_raise(expected_error) {o.a = 'z'}
assert_equal('a', o.a)
assert_equal(42, o.foo)
o = OpenStruct.new :a => 42
def o.frozen?; nil end
o.freeze
- assert_raise(RuntimeError, '[ruby-core:22559]') {o.a = 1764}
+ assert_raise(expected_error, '[ruby-core:22559]') {o.a = 1764}
end
def test_delete_field
@@ -138,9 +141,17 @@ class TC_OpenStruct < Test::Unit::TestCase
assert_equal(h, OpenStruct.new("name" => "John Smith", "age" => 70, pension: 300).to_h)
end
+ def test_to_h_with_block
+ os = OpenStruct.new("country" => "Australia", :capital => "Canberra")
+ assert_equal({"country" => "AUSTRALIA", "capital" => "CANBERRA" },
+ os.to_h {|name, value| [name.to_s, value.upcase]})
+ assert_equal("Australia", os.country)
+ end
+
def test_each_pair
h = {name: "John Smith", age: 70, pension: 300}
os = OpenStruct.new(h)
+ assert_same os, os.each_pair{ }
assert_equal '#<Enumerator: #<OpenStruct name="John Smith", age=70, pension=300>:each_pair>', os.each_pair.inspect
assert_equal [[:name, "John Smith"], [:age, 70], [:pension, 300]], os.each_pair.to_a
assert_equal 3, os.each_pair.size
@@ -182,4 +193,37 @@ class TC_OpenStruct < Test::Unit::TestCase
os.foo = 44
assert_equal(43, os.foo)
end
+
+ def test_allocate_subclass
+ bug = '[ruby-core:80292] [Bug #13358] allocate should not call initialize'
+ c = Class.new(OpenStruct) {
+ def initialize(x,y={})super(y);end
+ }
+ os = assert_nothing_raised(ArgumentError, bug) {c.allocate}
+ assert_instance_of(c, os)
+ end
+
+ def test_private_method
+ os = OpenStruct.new
+ class << os
+ private
+ def foo
+ end
+ end
+ assert_raise_with_message(NoMethodError, /private method/) do
+ os.foo true, true
+ end
+ end
+
+ def test_protected_method
+ os = OpenStruct.new
+ class << os
+ protected
+ def foo
+ end
+ end
+ assert_raise_with_message(NoMethodError, /protected method/) do
+ os.foo true, true
+ end
+ end
end
diff --git a/test/pathname/test_pathname.rb b/test/pathname/test_pathname.rb
index 848d4e4190..750fabf039 100644
--- a/test/pathname/test_pathname.rb
+++ b/test/pathname/test_pathname.rb
@@ -14,8 +14,8 @@ class TestPathname < Test::Unit::TestCase
end
def self.get_linenum
- if /:(\d+):/ =~ caller[1]
- $1.to_i
+ if loc = caller_locations(2, 1)
+ loc[0].lineno
else
nil
end
@@ -221,6 +221,8 @@ class TestPathname < Test::Unit::TestCase
defassert(:plus, 'a//b/d//e', 'a//b/c', '../d//e')
+ defassert(:plus, '//foo/var/bar', '//foo/var', 'bar')
+
def test_slash
assert_kind_of(Pathname, Pathname("a") / Pathname("b"))
end
@@ -289,9 +291,10 @@ class TestPathname < Test::Unit::TestCase
end
def relative_path_from(dest_directory, base_directory)
- Pathname.new(dest_directory).relative_path_from(Pathname.new(base_directory)).to_s
+ Pathname.new(dest_directory).relative_path_from(base_directory).to_s
end
+ defassert(:relative_path_from, "../a", Pathname.new("a"), "b")
defassert(:relative_path_from, "../a", "a", "b")
defassert(:relative_path_from, "../a", "a", "b/")
defassert(:relative_path_from, "../a", "a/", "b")
@@ -542,7 +545,7 @@ class TestPathname < Test::Unit::TestCase
defassert(:pathsubext, 'lex.yy.o', 'lex.yy.c', '.o')
defassert(:pathsubext, 'fooaa.o', 'fooaa', '.o')
defassert(:pathsubext, 'd.e/aa.o', 'd.e/aa', '.o')
- defassert(:pathsubext, 'long_enough.bug-3664', 'long_enough.not_to_be_embeded[ruby-core:31640]', '.bug-3664')
+ defassert(:pathsubext, 'long_enough.bug-3664', 'long_enough.not_to_be_embedded[ruby-core:31640]', '.bug-3664')
def test_sub_matchdata
result = Pathname("abc.gif").sub(/\..*/) {
@@ -633,7 +636,7 @@ class TestPathname < Test::Unit::TestCase
obj = Pathname.new("a")
obj.freeze
assert_equal(false, obj.tainted?)
- assert_raise(RuntimeError) { obj.taint }
+ assert_raise(FrozenError) { obj.taint }
obj = Pathname.new("a")
obj.taint
@@ -821,7 +824,7 @@ class TestPathname < Test::Unit::TestCase
old = path.lstat.mode
begin
path.lchmod(0444)
- rescue NotImplementedError
+ rescue NotImplementedError, Errno::EOPNOTSUPP
next
end
assert_equal(0444, path.lstat.mode & 0777)
@@ -910,11 +913,11 @@ class TestPathname < Test::Unit::TestCase
}
Pathname("b").open("w", 0444) {|f| f.write "def" }
- assert_equal(0444, File.stat("b").mode & 0777)
+ assert_equal(0444 & ~File.umask, File.stat("b").mode & 0777)
assert_equal("def", File.read("b"))
Pathname("c").open("w", 0444, {}) {|f| f.write "ghi" }
- assert_equal(0444, File.stat("c").mode & 0777)
+ assert_equal(0444 & ~File.umask, File.stat("c").mode & 0777)
assert_equal("ghi", File.read("c"))
g = path.open
@@ -1248,6 +1251,19 @@ class TestPathname < Test::Unit::TestCase
assert_kind_of(Pathname, wd)
end
+ def test_glob
+ with_tmpchdir('rubytest-pathname') {|dir|
+ Dir.mkdir("d")
+ open("d/f", "w") {|f| f.write "abc" }
+ Dir.mkdir("d/e")
+ assert_equal([Pathname("d/e"), Pathname("d/f")], Pathname("d").glob("*").sort)
+ a = []
+ Pathname("d").glob("*") {|path| a << path }
+ a.sort!
+ assert_equal([Pathname("d/e"), Pathname("d/f")], a)
+ }
+ end
+
def test_entries
with_tmpchdir('rubytest-pathname') {|dir|
open("a", "w") {}
@@ -1321,6 +1337,7 @@ class TestPathname < Test::Unit::TestCase
assert_equal([Pathname("d"), Pathname("d/x")], a)
skip "no meaning test on Windows" if /mswin|mingw/ =~ RUBY_PLATFORM
+ skip 'skipped in root privilege' if Process.uid == 0
a = [];
assert_raise_with_message(Errno::EACCES, %r{d/x}) do
Pathname(".").find(ignore_error: false) {|v| a << v }
@@ -1396,6 +1413,8 @@ class TestPathname < Test::Unit::TestCase
$SAFE = 1
assert_equal("foo/bar", File.join(Pathname.new("foo"), Pathname.new("bar").taint))
}.call
+ ensure
+ $SAFE = 0
end
def test_relative_path_from_casefold
@@ -1410,4 +1429,19 @@ class TestPathname < Test::Unit::TestCase
assert_instance_of(Pathname, foo.relative_path_from(bar))
end;
end
+
+ def test_relative_path_from_mock
+ assert_equal(
+ Pathname.new("../bar"),
+ Pathname.new("/foo/bar").relative_path_from(Pathname.new("/foo/baz")))
+ assert_equal(
+ Pathname.new("../bar"),
+ Pathname.new("/foo/bar").relative_path_from("/foo/baz"))
+ obj = Object.new
+ def obj.cleanpath() Pathname.new("/foo/baz") end
+ def obj.is_a?(m) m == Pathname end
+ assert_equal(
+ Pathname.new("../bar"),
+ Pathname.new("/foo/bar").relative_path_from(obj))
+ end
end
diff --git a/test/psych/handlers/test_recorder.rb b/test/psych/handlers/test_recorder.rb
index 17c7c6d0a0..d9d379ea9a 100644
--- a/test/psych/handlers/test_recorder.rb
+++ b/test/psych/handlers/test_recorder.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/helper'
require 'psych/handlers/recorder'
diff --git a/test/psych/helper.rb b/test/psych/helper.rb
index 498cdf8b09..9348457958 100644
--- a/test/psych/helper.rb
+++ b/test/psych/helper.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'minitest/autorun'
require 'stringio'
require 'tempfile'
@@ -50,10 +50,10 @@ module Psych
def assert_to_yaml( obj, yaml )
assert_equal( obj, Psych::load( yaml ) )
assert_equal( obj, Psych::parse( yaml ).transform )
- assert_equal( obj, Psych::load( obj.psych_to_yaml ) )
- assert_equal( obj, Psych::parse( obj.psych_to_yaml ).transform )
+ assert_equal( obj, Psych::load( obj.to_yaml ) )
+ assert_equal( obj, Psych::parse( obj.to_yaml ).transform )
assert_equal( obj, Psych::load(
- obj.psych_to_yaml(
+ obj.to_yaml(
:UseVersion => true, :UseHeader => true, :SortKeys => true
)
))
@@ -70,9 +70,15 @@ module Psych
def assert_cycle( obj )
v = Visitors::YAMLTree.create
v << obj
- assert_equal(obj, Psych.load(v.tree.yaml))
- assert_equal( obj, Psych::load(Psych.dump(obj)))
- assert_equal( obj, Psych::load( obj.psych_to_yaml ) )
+ if obj.nil?
+ assert_nil Psych.load(v.tree.yaml)
+ assert_nil Psych::load(Psych.dump(obj))
+ assert_nil Psych::load(obj.to_yaml)
+ else
+ assert_equal(obj, Psych.load(v.tree.yaml))
+ assert_equal(obj, Psych::load(Psych.dump(obj)))
+ assert_equal(obj, Psych::load(obj.to_yaml))
+ end
end
#
diff --git a/test/psych/json/test_stream.rb b/test/psych/json/test_stream.rb
index 519c114b29..90a770c1b7 100644
--- a/test/psych/json/test_stream.rb
+++ b/test/psych/json/test_stream.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/helper'
module Psych
diff --git a/test/psych/nodes/test_enumerable.rb b/test/psych/nodes/test_enumerable.rb
index 2f4e1f3bd0..76b36856f1 100644
--- a/test/psych/nodes/test_enumerable.rb
+++ b/test/psych/nodes/test_enumerable.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/helper'
module Psych
diff --git a/test/psych/test_alias_and_anchor.rb b/test/psych/test_alias_and_anchor.rb
index ed009605ca..91c09dfdfa 100644
--- a/test/psych/test_alias_and_anchor.rb
+++ b/test/psych/test_alias_and_anchor.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
class ObjectWithInstanceVariables
diff --git a/test/psych/test_array.rb b/test/psych/test_array.rb
index f1e71fb16c..f2bbdcab88 100644
--- a/test/psych/test_array.rb
+++ b/test/psych/test_array.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
@@ -15,8 +15,14 @@ module Psych
@list = [{ :a => 'b' }, 'foo']
end
+ def test_enumerator
+ x = [1, 2, 3, 4]
+ y = Psych.load Psych.dump x.to_enum
+ assert_equal x, y
+ end
+
def test_another_subclass_with_attributes
- y = Y.new.tap {|y| y.val = 1}
+ y = Y.new.tap {|o| o.val = 1}
y << "foo" << "bar"
y = Psych.load Psych.dump y
@@ -36,7 +42,7 @@ module Psych
end
def test_subclass_with_attributes
- y = Psych.load Psych.dump Y.new.tap {|y| y.val = 1}
+ y = Psych.load Psych.dump Y.new.tap {|o| o.val = 1}
assert_equal Y, y.class
assert_equal 1, y.val
end
diff --git a/test/psych/test_boolean.rb b/test/psych/test_boolean.rb
index b2803a6550..a4b80fc13d 100644
--- a/test/psych/test_boolean.rb
+++ b/test/psych/test_boolean.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
diff --git a/test/psych/test_class.rb b/test/psych/test_class.rb
index 4e1fd4a1d6..71f7ec31fd 100644
--- a/test/psych/test_class.rb
+++ b/test/psych/test_class.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
diff --git a/test/psych/test_coder.rb b/test/psych/test_coder.rb
index e578d55f2d..5ea8cab966 100644
--- a/test/psych/test_coder.rb
+++ b/test/psych/test_coder.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
diff --git a/test/psych/test_date_time.rb b/test/psych/test_date_time.rb
index 433fbf3d26..f73f34628f 100644
--- a/test/psych/test_date_time.rb
+++ b/test/psych/test_date_time.rb
@@ -1,18 +1,49 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
require 'date'
module Psych
class TestDateTime < TestCase
def test_negative_year
- time = Time.utc -1, 12, 16
+ time = Time.utc(-1, 12, 16)
assert_cycle time
end
+ def test_usec
+ time = Time.utc(2017, 4, 13, 12, 0, 0, 5)
+ assert_cycle time
+ end
+
+ def test_non_utc
+ time = Time.new(2017, 4, 13, 12, 0, 0.5, "+09:00")
+ assert_cycle time
+ end
+
+ def test_timezone_offset
+ times = [Time.new(2017, 4, 13, 12, 0, 0, "+09:00"),
+ Time.new(2017, 4, 13, 12, 0, 0, "-05:00")]
+ cycled = Psych::load(Psych.dump times)
+ assert_match(/12:00:00 \+0900/, cycled.first.to_s)
+ assert_match(/12:00:00 -0500/, cycled.last.to_s)
+ end
+
def test_new_datetime
assert_cycle DateTime.new
end
+ def test_datetime_non_utc
+ dt = DateTime.new(2017, 4, 13, 12, 0, 0.5, "+09:00")
+ assert_cycle dt
+ end
+
+ def test_datetime_timezone_offset
+ times = [DateTime.new(2017, 4, 13, 12, 0, 0, "+09:00"),
+ DateTime.new(2017, 4, 13, 12, 0, 0, "-05:00")]
+ cycled = Psych::load(Psych.dump times)
+ assert_match(/12:00:00\+09:00/, cycled.first.to_s)
+ assert_match(/12:00:00-05:00/, cycled.last.to_s)
+ end
+
def test_invalid_date
assert_cycle "2013-10-31T10:40:07-000000000000033"
end
diff --git a/test/psych/test_deprecated.rb b/test/psych/test_deprecated.rb
index a806f6b972..624f4379a6 100644
--- a/test/psych/test_deprecated.rb
+++ b/test/psych/test_deprecated.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
@@ -8,47 +8,12 @@ module Psych
Psych.domain_types.clear
end
- class QuickEmitter
- attr_reader :name
- attr_reader :value
-
- def initialize
- @name = 'hello!!'
- @value = 'Friday!'
- end
-
- def to_yaml opts = {}
- Psych.quick_emit object_id, opts do |out|
- out.map taguri, to_yaml_style do |map|
- map.add 'name', @name
- map.add 'value', nil
- end
- end
- end
- end
+ class QuickEmitter; end
def setup
- @qe = QuickEmitter.new
@orig_verbose, $VERBOSE = $VERBOSE, false
end
- def test_quick_emit
- qe2 = Psych.load @qe.to_yaml
- assert_equal @qe.name, qe2.name
- assert_instance_of QuickEmitter, qe2
- assert_nil qe2.value
- end
-
- def test_recursive_quick_emit
- hash = { :qe => @qe }
- hash2 = Psych.load Psych.dump hash
- qe = hash2[:qe]
-
- assert_equal @qe.name, qe.name
- assert_instance_of QuickEmitter, qe
- assert_nil qe.value
- end
-
class QuickEmitterEncodeWith
attr_reader :name
attr_reader :value
@@ -84,30 +49,6 @@ module Psych
assert_nil qe.value
end
- class YamlInit
- attr_reader :name
- attr_reader :value
-
- def initialize
- @name = 'hello!!'
- @value = 'Friday!'
- end
-
- def yaml_initialize tag, vals
- vals.each { |ivar, val| instance_variable_set "@#{ivar}", 'TGIF!' }
- end
- end
-
- def test_yaml_initialize
- hash = { :yi => YamlInit.new }
- hash2 = Psych.load Psych.dump hash
- yi = hash2[:yi]
-
- assert_equal 'TGIF!', yi.name
- assert_equal 'TGIF!', yi.value
- assert_instance_of YamlInit, yi
- end
-
class YamlInitAndInitWith
attr_reader :name
attr_reader :value
@@ -146,70 +87,5 @@ module Psych
assert_equal 'some string', coder.scalar
assert_equal :scalar, coder.type
end
-
- class YamlAs
- TestCase.suppress_warning do
- psych_yaml_as 'helloworld' # this should be yaml_as but to avoid syck
- end
- end
-
- def test_yaml_as
- assert_match(/helloworld/, Psych.dump(YamlAs.new))
- end
-
- def test_ruby_type
- types = []
- appender = lambda { |*args| types << args }
-
- Psych.add_ruby_type('foo', &appender)
- Psych.load <<-eoyml
-- !ruby.yaml.org,2002/foo bar
- eoyml
-
- assert_equal [["tag:ruby.yaml.org,2002:foo", "bar"]], types
- end
-
- def test_detect_implicit
- assert_equal '', Psych.detect_implicit(nil)
- assert_equal '', Psych.detect_implicit(Object.new)
- assert_equal '', Psych.detect_implicit(1.2)
- assert_equal 'null', Psych.detect_implicit('')
- assert_equal 'string', Psych.detect_implicit('foo')
- end
-
- def test_private_type
- types = []
- Psych.add_private_type('foo') { |*args| types << args }
- Psych.load <<-eoyml
-- !x-private:foo bar
- eoyml
-
- assert_equal [["x-private:foo", "bar"]], types
- end
-
- def test_tagurize
- assert_nil Psych.tagurize nil
- assert_equal Psych, Psych.tagurize(Psych)
- assert_equal 'tag:yaml.org,2002:foo', Psych.tagurize('foo')
- end
-
- def test_read_type_class
- things = Psych.read_type_class 'tag:yaml.org,2002:int:Psych::TestDeprecated::QuickEmitter', Object
- assert_equal 'int', things.first
- assert_equal Psych::TestDeprecated::QuickEmitter, things.last
- end
-
- def test_read_type_class_no_class
- things = Psych.read_type_class 'tag:yaml.org,2002:int', Object
- assert_equal 'int', things.first
- assert_equal Object, things.last
- end
-
- def test_object_maker
- thing = Psych.object_maker(Object, { 'a' => 'b', 'c' => 'd' })
- assert_instance_of(Object, thing)
- assert_equal 'b', thing.instance_variable_get(:@a)
- assert_equal 'd', thing.instance_variable_get(:@c)
- end
end
end
diff --git a/test/psych/test_document.rb b/test/psych/test_document.rb
index 97de2e15ec..a88dd32f0d 100644
--- a/test/psych/test_document.rb
+++ b/test/psych/test_document.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
diff --git a/test/psych/test_emitter.rb b/test/psych/test_emitter.rb
index 23e68b324e..52d5e9d1c1 100644
--- a/test/psych/test_emitter.rb
+++ b/test/psych/test_emitter.rb
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
@@ -7,7 +7,7 @@ module Psych
class TestEmitter < TestCase
def setup
super
- @out = StringIO.new('')
+ @out = StringIO.new(''.dup)
@emitter = Psych::Emitter.new @out
end
diff --git a/test/psych/test_encoding.rb b/test/psych/test_encoding.rb
index 13ca5dbc74..ef6653142f 100644
--- a/test/psych/test_encoding.rb
+++ b/test/psych/test_encoding.rb
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
@@ -106,6 +106,18 @@ module Psych
}
end
+ def test_io_utf8_read_as_binary
+ Tempfile.create(['utf8', 'yml']) {|t|
+ t.binmode
+ t.write '--- ã“ã‚“ã«ã¡ã¯ï¼'.encode('UTF-8')
+ t.close
+
+ File.open(t.path, 'rb', :encoding => 'ascii-8bit') do |f|
+ assert_equal "ã“ã‚“ã«ã¡ã¯ï¼", Psych.load(f)
+ end
+ }
+ end
+
def test_emit_alias
@emitter.start_stream Psych::Parser::UTF8
@emitter.start_document [], [], true
diff --git a/test/psych/test_exception.rb b/test/psych/test_exception.rb
index 85fa78f5df..df7fd73740 100644
--- a/test/psych/test_exception.rb
+++ b/test/psych/test_exception.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
@@ -15,6 +15,12 @@ module Psych
def setup
super
@wups = Wups.new
+
+ @orig_verbose, $VERBOSE = $VERBOSE, nil
+ end
+
+ def teardown
+ $VERBOSE = @orig_verbose
end
def test_naming_exception
@@ -30,9 +36,15 @@ module Psych
assert_nil ex.file
ex = assert_raises(Psych::SyntaxError) do
- Psych.load '--- `', 'meow'
+ Psych.load '--- `', filename: 'meow'
end
assert_equal 'meow', ex.file
+
+ # deprecated interface
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.load '--- `', 'deprecated'
+ end
+ assert_equal 'deprecated', ex.file
end
def test_psych_parse_stream_takes_file
@@ -43,7 +55,7 @@ module Psych
assert_match '(<unknown>)', ex.message
ex = assert_raises(Psych::SyntaxError) do
- Psych.parse_stream '--- `', 'omg!'
+ Psych.parse_stream '--- `', filename: 'omg!'
end
assert_equal 'omg!', ex.file
assert_match 'omg!', ex.message
@@ -57,9 +69,15 @@ module Psych
assert_match '(<unknown>)', ex.message
ex = assert_raises(Psych::SyntaxError) do
- Psych.load_stream '--- `', 'omg!'
+ Psych.load_stream '--- `', filename: 'omg!'
end
assert_equal 'omg!', ex.file
+
+ # deprecated interface
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.load_stream '--- `', 'deprecated'
+ end
+ assert_equal 'deprecated', ex.file
end
def test_parse_file_exception
@@ -94,9 +112,15 @@ module Psych
assert_nil ex.file
ex = assert_raises(Psych::SyntaxError) do
- Psych.parse '--- `', 'omg!'
+ Psych.parse '--- `', filename: 'omg!'
end
assert_match 'omg!', ex.message
+
+ # deprecated interface
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.parse '--- `', 'deprecated'
+ end
+ assert_match 'deprecated', ex.message
end
def test_attributes
@@ -121,19 +145,6 @@ module Psych
assert_equal 2, w.bar
end
- def test_to_yaml_properties
- class << @wups
- def to_yaml_properties
- [:@foo]
- end
- end
-
- w = Psych.load(Psych.dump(@wups))
- assert_equal @wups, w
- assert_equal 1, w.foo
- assert_nil w.bar
- end
-
def test_psych_syntax_error
Tempfile.create(['parsefile', 'yml']) do |t|
t.binmode
diff --git a/test/psych/test_hash.rb b/test/psych/test_hash.rb
index b449ce49e3..e93aa73249 100644
--- a/test/psych/test_hash.rb
+++ b/test/psych/test_hash.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
diff --git a/test/psych/test_json_tree.rb b/test/psych/test_json_tree.rb
index 8bb850b138..3c59a8dbda 100644
--- a/test/psych/test_json_tree.rb
+++ b/test/psych/test_json_tree.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
diff --git a/test/psych/test_marshalable.rb b/test/psych/test_marshalable.rb
index 544947a521..b1f4a837f5 100644
--- a/test/psych/test_marshalable.rb
+++ b/test/psych/test_marshalable.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
require 'delegate'
diff --git a/test/psych/test_merge_keys.rb b/test/psych/test_merge_keys.rb
index 76245ca7b5..1bc3dd1cb6 100644
--- a/test/psych/test_merge_keys.rb
+++ b/test/psych/test_merge_keys.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
diff --git a/test/psych/test_nil.rb b/test/psych/test_nil.rb
index 3d4fa88eaa..bcbbcb9c93 100644
--- a/test/psych/test_nil.rb
+++ b/test/psych/test_nil.rb
@@ -1,17 +1,17 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
class TestNil < TestCase
def test_nil
yml = Psych.dump nil
- assert_match(/--- \n(?:\.\.\.\n)?/, yml)
+ assert_match(/---[ ]?\n(?:\.\.\.\n)?/, yml)
assert_nil Psych.load(yml)
end
def test_array_nil
yml = Psych.dump [nil]
- assert_equal "---\n- \n", yml
+ assert_match(/---\n-[ ]?\n/, yml)
assert_equal [nil], Psych.load(yml)
end
diff --git a/test/psych/test_null.rb b/test/psych/test_null.rb
index d8ccb7056f..9d92d74150 100644
--- a/test/psych/test_null.rb
+++ b/test/psych/test_null.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
diff --git a/test/psych/test_numeric.rb b/test/psych/test_numeric.rb
index 9d8b74f7ca..db99a2a0d6 100644
--- a/test/psych/test_numeric.rb
+++ b/test/psych/test_numeric.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
require 'bigdecimal'
diff --git a/test/psych/test_object.rb b/test/psych/test_object.rb
index ed1ccd9bf8..f1c61451d0 100644
--- a/test/psych/test_object.rb
+++ b/test/psych/test_object.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
diff --git a/test/psych/test_object_references.rb b/test/psych/test_object_references.rb
index 1300bfc1db..ca69c7d288 100644
--- a/test/psych/test_object_references.rb
+++ b/test/psych/test_object_references.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
diff --git a/test/psych/test_omap.rb b/test/psych/test_omap.rb
index 80791aa918..98636ded97 100644
--- a/test/psych/test_omap.rb
+++ b/test/psych/test_omap.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
diff --git a/test/psych/test_parser.rb b/test/psych/test_parser.rb
index 26aba0543b..e8225dabb6 100644
--- a/test/psych/test_parser.rb
+++ b/test/psych/test_parser.rb
@@ -1,5 +1,5 @@
# coding: utf-8
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
@@ -87,13 +87,22 @@ module Psych
assert_equal 0, @parser.mark.line
@parser.parse "---\n- hello\n- world"
line_calls = @handler.marks.map(&:line).zip(@handler.calls.map(&:first))
- assert_equal [[0, :start_stream],
+ assert_equal [
+ [0, :event_location],
+ [0, :start_stream],
+ [0, :event_location],
[0, :start_document],
+ [1, :event_location],
[1, :start_sequence],
+ [2, :event_location],
[2, :scalar],
+ [3, :event_location],
[3, :scalar],
+ [3, :event_location],
[3, :end_sequence],
+ [3, :event_location],
[3, :end_document],
+ [3, :event_location],
[3, :end_stream]], line_calls
assert_equal 3, @parser.mark.line
@@ -103,13 +112,22 @@ module Psych
assert_equal 0, @parser.mark.column
@parser.parse "---\n- hello\n- world"
col_calls = @handler.marks.map(&:column).zip(@handler.calls.map(&:first))
- assert_equal [[0, :start_stream],
+ assert_equal [
+ [0, :event_location],
+ [0, :start_stream],
+ [3, :event_location],
[3, :start_document],
+ [1, :event_location],
[1, :start_sequence],
+ [0, :event_location],
[0, :scalar],
+ [0, :event_location],
[0, :scalar],
+ [0, :event_location],
[0, :end_sequence],
+ [0, :event_location],
[0, :end_document],
+ [0, :event_location],
[0, :end_stream]], col_calls
assert_equal 0, @parser.mark.column
@@ -119,13 +137,22 @@ module Psych
assert_equal 0, @parser.mark.index
@parser.parse "---\n- hello\n- world"
idx_calls = @handler.marks.map(&:index).zip(@handler.calls.map(&:first))
- assert_equal [[0, :start_stream],
+ assert_equal [
+ [0, :event_location],
+ [0, :start_stream],
+ [3, :event_location],
[3, :start_document],
+ [5, :event_location],
[5, :start_sequence],
+ [12, :event_location],
[12, :scalar],
+ [19, :event_location],
[19, :scalar],
+ [19, :event_location],
[19, :end_sequence],
+ [19, :event_location],
[19, :end_document],
+ [19, :event_location],
[19, :end_stream]], idx_calls
assert_equal 19, @parser.mark.index
@@ -137,7 +164,7 @@ module Psych
# BOM + text
yml = "\uFEFF#{tadpole}".encode('UTF-16LE')
@parser.parse yml
- assert_equal tadpole, @parser.handler.calls[2][1].first
+ assert_equal tadpole, @parser.handler.calls.find { |method, args| method == :scalar }[1].first
end
def test_external_encoding
@@ -145,7 +172,7 @@ module Psych
@parser.external_encoding = Psych::Parser::UTF16LE
@parser.parse tadpole.encode 'UTF-16LE'
- assert_equal tadpole, @parser.handler.calls[2][1].first
+ assert_equal tadpole, @parser.handler.calls.find { |method, args| method == :scalar }[1].first
end
def test_bogus_io
@@ -324,6 +351,31 @@ module Psych
assert_called :start_document, [[], [['!yaml!', 'tag:yaml.org,2002']], false]
end
+ def test_event_location
+ @parser.parse "foo:\n" \
+ " barbaz: [1, 2]"
+
+ events = @handler.calls.each_slice(2).map do |location, event|
+ [event[0], location[1]]
+ end
+
+ assert_equal [
+ [:start_stream, [0, 0, 0, 0]],
+ [:start_document, [0, 0, 0, 0]],
+ [:start_mapping, [0, 0, 0, 0]],
+ [:scalar, [0, 0, 0, 3]],
+ [:start_mapping, [1, 2, 1, 2]],
+ [:scalar, [1, 2, 1, 8]],
+ [:start_sequence, [1, 10, 1, 11]],
+ [:scalar, [1, 11, 1, 12]],
+ [:scalar, [1, 14, 1, 15]],
+ [:end_sequence, [1, 15, 1, 16]],
+ [:end_mapping, [2, 0, 2, 0]],
+ [:end_mapping, [2, 0, 2, 0]],
+ [:end_document, [2, 0, 2, 0]],
+ [:end_stream, [2, 0, 2, 0]]], events
+ end
+
def assert_called call, with = nil, parser = @parser
if with
call = parser.handler.calls.find { |x|
diff --git a/test/psych/test_psych.rb b/test/psych/test_psych.rb
index 0e769274fd..e557feffb7 100644
--- a/test/psych/test_psych.rb
+++ b/test/psych/test_psych.rb
@@ -1,12 +1,18 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
require 'stringio'
require 'tempfile'
class TestPsych < Psych::TestCase
+
+ def setup
+ @orig_verbose, $VERBOSE = $VERBOSE, nil
+ end
+
def teardown
Psych.domain_types.clear
+ $VERBOSE = @orig_verbose
end
def test_line_width_invalid
@@ -60,6 +66,22 @@ class TestPsych < Psych::TestCase
end
end
+ def test_parse
+ assert_equal %w[a b], Psych.parse("- a\n- b").to_ruby
+ end
+
+ def test_parse_default_fallback
+ assert_equal false, Psych.parse("")
+ end
+
+ def test_parse_raises_on_bad_input
+ assert_raises(Psych::SyntaxError) { Psych.parse("--- `") }
+ end
+
+ def test_parse_with_fallback
+ assert_equal 42, Psych.parse("", fallback: 42)
+ end
+
def test_non_existing_class_on_deserialize
e = assert_raises(ArgumentError) do
Psych.load("--- !ruby/object:NonExistent\nfoo: 1")
@@ -84,7 +106,7 @@ class TestPsych < Psych::TestCase
def test_dump_io
hash = {'hello' => 'TGIF!'}
- stringio = StringIO.new ''
+ stringio = StringIO.new ''.dup
assert_equal stringio, Psych.dump(hash, stringio)
assert_equal Psych.dump(hash), stringio.string
end
@@ -98,14 +120,49 @@ class TestPsych < Psych::TestCase
assert_equal Psych.libyaml_version.join('.'), Psych::LIBYAML_VERSION
end
- def test_load_documents
- docs = Psych.load_documents("--- foo\n...\n--- bar\n...")
+ def test_load_stream
+ docs = Psych.load_stream("--- foo\n...\n--- bar\n...")
assert_equal %w{ foo bar }, docs
end
+ def test_load_stream_default_fallback
+ assert_equal [], Psych.load_stream("")
+ end
+
+ def test_load_stream_raises_on_bad_input
+ assert_raises(Psych::SyntaxError) { Psych.load_stream("--- `") }
+ end
+
def test_parse_stream
docs = Psych.parse_stream("--- foo\n...\n--- bar\n...")
- assert_equal %w{ foo bar }, docs.children.map { |x| x.transform }
+ assert_equal(%w[foo bar], docs.children.map(&:transform))
+ end
+
+ def test_parse_stream_with_block
+ docs = []
+ Psych.parse_stream("--- foo\n...\n--- bar\n...") do |node|
+ docs << node
+ end
+
+ assert_equal %w[foo bar], docs.map(&:to_ruby)
+ end
+
+ def test_parse_stream_default_fallback
+ docs = Psych.parse_stream("")
+ assert_equal [], docs.children.map(&:to_ruby)
+ end
+
+ def test_parse_stream_with_block_default_fallback
+ docs = []
+ Psych.parse_stream("") do |node|
+ docs << node
+ end
+
+ assert_equal [], docs.map(&:to_ruby)
+ end
+
+ def test_parse_stream_raises_on_bad_input
+ assert_raises(Psych::SyntaxError) { Psych.parse_stream("--- `") }
end
def test_add_builtin_type
@@ -121,20 +178,45 @@ class TestPsych < Psych::TestCase
def test_domain_types
got = nil
- Psych.add_domain_type 'foo.bar,2002', 'foo' do |type, val|
+ Psych.add_domain_type 'foo.bar/2002', 'foo' do |type, val|
got = val
end
- Psych.load('--- !foo.bar,2002/foo hello')
+ Psych.load('--- !foo.bar/2002:foo hello')
assert_equal 'hello', got
- Psych.load("--- !foo.bar,2002/foo\n- hello\n- world")
+ Psych.load("--- !foo.bar/2002:foo\n- hello\n- world")
assert_equal %w{ hello world }, got
- Psych.load("--- !foo.bar,2002/foo\nhello: world")
+ Psych.load("--- !foo.bar/2002:foo\nhello: world")
assert_equal({ 'hello' => 'world' }, got)
end
+ def test_load_default_fallback
+ assert_equal false, Psych.load("")
+ end
+
+ def test_load_with_fallback
+ assert_equal 42, Psych.load("", "file", fallback: 42)
+ end
+
+ def test_load_with_fallback_nil_or_false
+ assert_nil Psych.load("", "file", fallback: nil)
+ assert_equal false, Psych.load("", "file", fallback: false)
+ end
+
+ def test_load_with_fallback_hash
+ assert_equal Hash.new, Psych.load("", "file", fallback: Hash.new)
+ end
+
+ def test_load_with_fallback_for_nil
+ assert_nil Psych.load("--- null", "file", fallback: 42)
+ end
+
+ def test_load_with_fallback_for_false
+ assert_equal false, Psych.load("--- false", "file", fallback: 42)
+ end
+
def test_load_file
Tempfile.create(['yikes', 'yml']) {|t|
t.binmode
@@ -144,9 +226,46 @@ class TestPsych < Psych::TestCase
}
end
+ def test_load_file_default_fallback
+ Tempfile.create(['empty', 'yml']) {|t|
+ assert_equal false, Psych.load_file(t.path)
+ }
+ end
+
def test_load_file_with_fallback
Tempfile.create(['empty', 'yml']) {|t|
- assert_equal Hash.new, Psych.load_file(t.path, Hash.new)
+ assert_equal 42, Psych.load_file(t.path, fallback: 42)
+ }
+ end
+
+ def test_load_file_with_fallback_nil_or_false
+ Tempfile.create(['empty', 'yml']) {|t|
+ assert_nil Psych.load_file(t.path, fallback: nil)
+ assert_equal false, Psych.load_file(t.path, fallback: false)
+ }
+ end
+
+ def test_load_file_with_fallback_hash
+ Tempfile.create(['empty', 'yml']) {|t|
+ assert_equal Hash.new, Psych.load_file(t.path, fallback: Hash.new)
+ }
+ end
+
+ def test_load_file_with_fallback_for_nil
+ Tempfile.create(['nil', 'yml']) {|t|
+ t.binmode
+ t.write('--- null')
+ t.close
+ assert_nil Psych.load_file(t.path, fallback: 42)
+ }
+ end
+
+ def test_load_file_with_fallback_for_false
+ Tempfile.create(['false', 'yml']) {|t|
+ t.binmode
+ t.write('--- false')
+ t.close
+ assert_equal false, Psych.load_file(t.path, fallback: 42)
}
end
@@ -159,6 +278,12 @@ class TestPsych < Psych::TestCase
}
end
+ def test_parse_file_default_fallback
+ Tempfile.create(['empty', 'yml']) do |t|
+ assert_equal false, Psych.parse_file(t.path)
+ end
+ end
+
def test_degenerate_strings
assert_equal false, Psych.load(' ')
assert_equal false, Psych.parse(' ')
@@ -170,16 +295,31 @@ class TestPsych < Psych::TestCase
types = []
appender = lambda { |*args| types << args }
- Psych.add_builtin_type('foo', &appender)
- Psych.add_domain_type('example.com,2002', 'foo', &appender)
+ Psych.add_domain_type('example.com:2002', 'foo', &appender)
Psych.load <<-eoyml
-- !tag:yaml.org,2002:foo bar
-- !tag:example.com,2002:foo bar
+- !tag:example.com:2002:foo bar
eoyml
assert_equal [
- ["tag:yaml.org,2002:foo", "bar"],
- ["tag:example.com,2002:foo", "bar"]
+ ["tag:example.com:2002:foo", "bar"]
], types
end
+
+ def test_symbolize_names
+ yaml = <<-eoyml
+foo:
+ bar: baz
+hoge:
+ - fuga: piyo
+ eoyml
+
+ result = Psych.load(yaml)
+ assert_equal result, { "foo" => { "bar" => "baz"}, "hoge" => [{ "fuga" => "piyo" }] }
+
+ result = Psych.load(yaml, symbolize_names: true)
+ assert_equal result, { foo: { bar: "baz" }, hoge: [{ fuga: "piyo" }] }
+
+ result = Psych.safe_load(yaml, symbolize_names: true)
+ assert_equal result, { foo: { bar: "baz" }, hoge: [{ fuga: "piyo" }] }
+ end
end
diff --git a/test/psych/test_safe_load.rb b/test/psych/test_safe_load.rb
index b69c54c199..e3972712fc 100644
--- a/test/psych/test_safe_load.rb
+++ b/test/psych/test_safe_load.rb
@@ -1,8 +1,16 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/helper'
module Psych
class TestSafeLoad < TestCase
+ def setup
+ @orig_verbose, $VERBOSE = $VERBOSE, nil
+ end
+
+ def teardown
+ $VERBOSE = @orig_verbose
+ end
+
class Foo; end
[1, 2.2, {}, [], "foo"].each do |obj|
@@ -22,14 +30,26 @@ module Psych
def test_explicit_recursion
x = []
x << x
+ assert_equal(x, Psych.safe_load(Psych.dump(x), permitted_classes: [], permitted_symbols: [], aliases: true))
+ # deprecated interface
assert_equal(x, Psych.safe_load(Psych.dump(x), [], [], true))
end
- def test_symbol_whitelist
+ def test_permitted_symbol
yml = Psych.dump :foo
assert_raises(Psych::DisallowedClass) do
Psych.safe_load yml
end
+ assert_equal(
+ :foo,
+ Psych.safe_load(
+ yml,
+ permitted_classes: [Symbol],
+ permitted_symbols: [:foo]
+ )
+ )
+
+ # deprecated interface
assert_equal(:foo, Psych.safe_load(yml, [Symbol], [:foo]))
end
@@ -38,32 +58,71 @@ module Psych
assert_safe_cycle :foo
end
assert_raises(Psych::DisallowedClass) do
+ Psych.safe_load '--- !ruby/symbol foo', permitted_classes: []
+ end
+
+ # deprecated interface
+ assert_raises(Psych::DisallowedClass) do
Psych.safe_load '--- !ruby/symbol foo', []
end
- assert_safe_cycle :foo, [Symbol]
- assert_safe_cycle :foo, %w{ Symbol }
+
+ assert_safe_cycle :foo, permitted_classes: [Symbol]
+ assert_safe_cycle :foo, permitted_classes: %w{ Symbol }
+ assert_equal :foo, Psych.safe_load('--- !ruby/symbol foo', permitted_classes: [Symbol])
+
+ # deprecated interface
assert_equal :foo, Psych.safe_load('--- !ruby/symbol foo', [Symbol])
end
def test_foo
assert_raises(Psych::DisallowedClass) do
+ Psych.safe_load '--- !ruby/object:Foo {}', permitted_classes: [Foo]
+ end
+
+ # deprecated interface
+ assert_raises(Psych::DisallowedClass) do
Psych.safe_load '--- !ruby/object:Foo {}', [Foo]
end
+
assert_raises(Psych::DisallowedClass) do
assert_safe_cycle Foo.new
end
+ assert_kind_of(Foo, Psych.safe_load(Psych.dump(Foo.new), permitted_classes: [Foo]))
+
+ # deprecated interface
assert_kind_of(Foo, Psych.safe_load(Psych.dump(Foo.new), [Foo]))
end
X = Struct.new(:x)
def test_struct_depends_on_sym
- assert_safe_cycle(X.new, [X, Symbol])
+ assert_safe_cycle(X.new, permitted_classes: [X, Symbol])
assert_raises(Psych::DisallowedClass) do
- cycle X.new, [X]
+ cycle X.new, permitted_classes: [X]
end
end
def test_anon_struct
+ assert Psych.safe_load(<<-eoyml, permitted_classes: [Struct, Symbol])
+--- !ruby/struct
+ foo: bar
+ eoyml
+
+ assert_raises(Psych::DisallowedClass) do
+ Psych.safe_load(<<-eoyml, permitted_classes: [Struct])
+--- !ruby/struct
+ foo: bar
+ eoyml
+ end
+
+ assert_raises(Psych::DisallowedClass) do
+ Psych.safe_load(<<-eoyml, permitted_classes: [Symbol])
+--- !ruby/struct
+ foo: bar
+ eoyml
+ end
+ end
+
+ def test_deprecated_anon_struct
assert Psych.safe_load(<<-eoyml, [Struct, Symbol])
--- !ruby/struct
foo: bar
@@ -84,14 +143,28 @@ module Psych
end
end
+ def test_safe_load_default_fallback
+ assert_nil Psych.safe_load("")
+ end
+
+ def test_safe_load
+ assert_equal %w[a b], Psych.safe_load("- a\n- b")
+ end
+
+ def test_safe_load_raises_on_bad_input
+ assert_raises(Psych::SyntaxError) { Psych.safe_load("--- `") }
+ end
+
private
- def cycle object, whitelist = []
- Psych.safe_load(Psych.dump(object), whitelist)
+ def cycle object, permitted_classes: []
+ Psych.safe_load(Psych.dump(object), permitted_classes: permitted_classes)
+ # deprecated interface test
+ Psych.safe_load(Psych.dump(object), permitted_classes)
end
- def assert_safe_cycle object, whitelist = []
- other = cycle object, whitelist
+ def assert_safe_cycle object, permitted_classes: []
+ other = cycle object, permitted_classes: permitted_classes
assert_equal object, other
end
end
diff --git a/test/psych/test_scalar.rb b/test/psych/test_scalar.rb
index 4353ec33fa..e2f9ec791d 100644
--- a/test/psych/test_scalar.rb
+++ b/test/psych/test_scalar.rb
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
@@ -8,5 +8,10 @@ module Psych
def test_utf_8
assert_equal "日本語", Psych.load("--- 日本語")
end
+
+ def test_some_bytes # Ticket #278
+ x = "\xEF\xBF\xBD\x1F"
+ assert_cycle x
+ end
end
end
diff --git a/test/psych/test_scalar_scanner.rb b/test/psych/test_scalar_scanner.rb
index 25999892bc..ebe8daf672 100644
--- a/test/psych/test_scalar_scanner.rb
+++ b/test/psych/test_scalar_scanner.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
require 'date'
@@ -79,15 +79,21 @@ module Psych
end
def test_scan_null
- assert_equal nil, ss.tokenize('null')
- assert_equal nil, ss.tokenize('~')
- assert_equal nil, ss.tokenize('')
+ assert_nil ss.tokenize('null')
+ assert_nil ss.tokenize('~')
+ assert_nil ss.tokenize('')
end
def test_scan_symbol
assert_equal :foo, ss.tokenize(':foo')
end
+ def test_scan_not_sexagesimal
+ assert_equal '00:00:00:00:0f', ss.tokenize('00:00:00:00:0f')
+ assert_equal '00:00:00:00:00', ss.tokenize('00:00:00:00:00')
+ assert_equal '00:00:00:00:00.0', ss.tokenize('00:00:00:00:00.0')
+ end
+
def test_scan_sexagesimal_float
assert_equal 685230.15, ss.tokenize('190:20:30.15')
end
diff --git a/test/psych/test_serialize_subclasses.rb b/test/psych/test_serialize_subclasses.rb
index be209edf2f..8e1d0d354d 100644
--- a/test/psych/test_serialize_subclasses.rb
+++ b/test/psych/test_serialize_subclasses.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
diff --git a/test/psych/test_set.rb b/test/psych/test_set.rb
index 0eb999cb2f..5690957eff 100644
--- a/test/psych/test_set.rb
+++ b/test/psych/test_set.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
diff --git a/test/psych/test_stream.rb b/test/psych/test_stream.rb
index 1b054289ea..9b71c6d996 100644
--- a/test/psych/test_stream.rb
+++ b/test/psych/test_stream.rb
@@ -1,8 +1,24 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
class TestStream < TestCase
+ [
+ [Psych::Nodes::Alias, :alias?],
+ [Psych::Nodes::Document, :document?],
+ [Psych::Nodes::Mapping, :mapping?],
+ [Psych::Nodes::Scalar, :scalar?],
+ [Psych::Nodes::Sequence, :sequence?],
+ [Psych::Nodes::Stream, :stream?],
+ ].each do |klass, block|
+ define_method :"test_predicate_#{block}" do
+ rb = Psych.parse_stream("---\n- foo: bar\n- &a !!str Anchored\n- *a")
+ nodes = rb.grep(klass)
+ assert_operator nodes.length, :>, 0
+ assert_equal nodes, rb.find_all(&block)
+ end
+ end
+
def test_parse_partial
rb = Psych.parse("--- foo\n...\n--- `").to_ruby
assert_equal 'foo', rb
diff --git a/test/psych/test_string.rb b/test/psych/test_string.rb
index 25c6353cee..973f38b9c2 100644
--- a/test/psych/test_string.rb
+++ b/test/psych/test_string.rb
@@ -1,5 +1,5 @@
# encoding: UTF-8
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
@@ -33,28 +33,28 @@ module Psych
def test_doublequotes_when_there_is_a_single
str = "@123'abc"
yaml = Psych.dump str
- assert_match /---\s*"/, yaml
+ assert_match(/---\s*"/, yaml)
assert_equal str, Psych.load(yaml)
end
def test_plain_when_shorten_than_line_width_and_no_final_line_break
str = "Lorem ipsum"
yaml = Psych.dump str, line_width: 12
- assert_match /---\s*[^>|]+\n/, yaml
+ assert_match(/---\s*[^>|]+\n/, yaml)
assert_equal str, Psych.load(yaml)
end
def test_plain_when_shorten_than_line_width_and_with_final_line_break
str = "Lorem ipsum\n"
yaml = Psych.dump str, line_width: 12
- assert_match /---\s*[^>|]+\n/, yaml
+ assert_match(/---\s*[^>|]+\n/, yaml)
assert_equal str, Psych.load(yaml)
end
def test_folded_when_longer_than_line_width_and_with_final_line_break
str = "Lorem ipsum dolor sit\n"
yaml = Psych.dump str, line_width: 12
- assert_match /---\s*>\n(.*\n){2}\Z/, yaml
+ assert_match(/---\s*>\n(.*\n){2}\Z/, yaml)
assert_equal str, Psych.load(yaml)
end
@@ -62,7 +62,7 @@ module Psych
def test_folded_strip_when_longer_than_line_width_and_no_newlines
str = "Lorem ipsum dolor sit amet, consectetur"
yaml = Psych.dump str, line_width: 12
- assert_match /---\s*>-\n(.*\n){3}\Z/, yaml
+ assert_match(/---\s*>-\n(.*\n){3}\Z/, yaml)
assert_equal str, Psych.load(yaml)
end
@@ -72,7 +72,7 @@ module Psych
"Lorem ipsum\nZolor\n",
].each do |str|
yaml = Psych.dump str, line_width: 12
- assert_match /---\s*\|\n(.*\n){2}\Z/, yaml
+ assert_match(/---\s*\|\n(.*\n){2}\Z/, yaml)
assert_equal str, Psych.load(yaml)
end
end
@@ -84,7 +84,7 @@ module Psych
"Lorem ipsum\nZolor",
].each do |str|
yaml = Psych.dump str, line_width: 12
- assert_match /---\s*\|-\n(.*\n){2}\Z/, yaml
+ assert_match(/---\s*\|-\n(.*\n){2}\Z/, yaml)
assert_equal str, Psych.load(yaml)
end
end
@@ -129,7 +129,7 @@ string: &70121654388580 !ruby/string
end
def test_another_subclass_with_attributes
- y = Psych.load Psych.dump Y.new("foo").tap {|y| y.val = 1}
+ y = Psych.load Psych.dump Y.new("foo").tap {|o| o.val = 1}
assert_equal "foo", y
assert_equal Y, y.class
assert_equal 1, y.val
@@ -154,7 +154,7 @@ string: &70121654388580 !ruby/string
end
def test_subclass_with_attributes
- y = Psych.load Psych.dump Y.new.tap {|y| y.val = 1}
+ y = Psych.load Psych.dump Y.new.tap {|o| o.val = 1}
assert_equal Y, y.class
assert_equal 1, y.val
end
@@ -166,7 +166,7 @@ string: &70121654388580 !ruby/string
end
def test_nonascii_string_as_binary
- string = "hello \x80 world!"
+ string = "hello \x80 world!".dup
string.force_encoding 'ascii-8bit'
yml = Psych.dump string
assert_match(/binary/, yml)
@@ -174,7 +174,7 @@ string: &70121654388580 !ruby/string
end
def test_binary_string_null
- string = "\x00"
+ string = "\x00\x92".b
yml = Psych.dump string
assert_match(/binary/, yml)
assert_equal string, Psych.load(yml)
@@ -187,8 +187,8 @@ string: &70121654388580 !ruby/string
assert_equal string, Psych.load(yml)
end
- def test_non_binary_string
- string = binary_string(0.29)
+ def test_ascii_only_binary_string
+ string = "non bnry string".b
yml = Psych.dump string
refute_match(/binary/, yml)
assert_equal string, Psych.load(yml)
@@ -202,7 +202,7 @@ string: &70121654388580 !ruby/string
end
def test_string_with_ivars
- food = "is delicious"
+ food = "is delicious".dup
ivar = "on rock and roll"
food.instance_variable_set(:@we_built_this_city, ivar)
@@ -220,9 +220,9 @@ string: &70121654388580 !ruby/string
end
def binary_string percentage = 0.31, length = 100
- string = ''
+ string = ''.b
(percentage * length).to_i.times do |i|
- string << "\b"
+ string << "\x92".b
end
string << 'a' * (length - string.length)
string
diff --git a/test/psych/test_struct.rb b/test/psych/test_struct.rb
index b7968d3189..721df44216 100644
--- a/test/psych/test_struct.rb
+++ b/test/psych/test_struct.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
class PsychStructWithIvar < Struct.new(:foo)
diff --git a/test/psych/test_symbol.rb b/test/psych/test_symbol.rb
index a98881cf4b..36416ffe29 100644
--- a/test/psych/test_symbol.rb
+++ b/test/psych/test_symbol.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
diff --git a/test/psych/test_tainted.rb b/test/psych/test_tainted.rb
index 870583323d..dcf150b138 100644
--- a/test/psych/test_tainted.rb
+++ b/test/psych/test_tainted.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
@@ -90,7 +90,7 @@ module Psych
end
def assert_taintedness string
- @parser.parse string.taint
+ @parser.parse string.dup.taint
end
end
diff --git a/test/psych/test_to_yaml_properties.rb b/test/psych/test_to_yaml_properties.rb
deleted file mode 100644
index 8a29b6a9b9..0000000000
--- a/test/psych/test_to_yaml_properties.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-# frozen_string_literal: false
-require_relative 'helper'
-
-module Psych
- class TestToYamlProperties < Psych::TestCase
- class Foo
- attr_accessor :a, :b, :c
- def initialize
- @a = 1
- @b = 2
- @c = 3
- end
-
- def to_yaml_properties
- [:@a, :@b]
- end
- end
-
- def test_object_dump_yaml_properties
- foo = Psych.load(Psych.dump(Foo.new))
- assert_equal 1, foo.a
- assert_equal 2, foo.b
- assert_nil foo.c
- end
-
- class Bar < Struct.new(:foo, :bar)
- attr_reader :baz
- def initialize *args
- super
- @baz = 'hello'
- end
-
- def to_yaml_properties
- []
- end
- end
-
- def test_struct_dump_yaml_properties
- bar = Psych.load(Psych.dump(Bar.new('a', 'b')))
- assert_equal 'a', bar.foo
- assert_equal 'b', bar.bar
- assert_nil bar.baz
- end
-
- def test_string_dump
- string = "okonomiyaki"
- class << string
- def to_yaml_properties
- [:@tastes]
- end
- end
-
- string.instance_variable_set(:@tastes, 'delicious')
- v = Psych.load Psych.dump string
- assert_equal 'delicious', v.instance_variable_get(:@tastes)
- end
-
- def test_string_load_syck
- str = Psych.load("--- !str \nstr: okonomiyaki\n:@tastes: delicious\n")
- assert_equal 'okonomiyaki', str
- assert_equal 'delicious', str.instance_variable_get(:@tastes)
- end
- end
-end
diff --git a/test/psych/test_tree_builder.rb b/test/psych/test_tree_builder.rb
index 09f1ee30c9..dfb5da9892 100644
--- a/test/psych/test_tree_builder.rb
+++ b/test/psych/test_tree_builder.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
module Psych
@@ -21,6 +21,7 @@ module Psych
def test_stream
assert_instance_of Nodes::Stream, @tree
+ assert_location 0, 0, 8, 0, @tree
end
def test_documents
@@ -31,6 +32,7 @@ module Psych
assert_equal [1,1], doc.version
assert_equal [], doc.tag_directives
assert_equal false, doc.implicit
+ assert_location 0, 0, 8, 0, doc
end
def test_sequence
@@ -43,6 +45,7 @@ module Psych
assert_nil seq.tag
assert_equal true, seq.implicit
assert_equal Nodes::Sequence::BLOCK, seq.style
+ assert_location 2, 0, 8, 0, seq
end
def test_scalar
@@ -58,6 +61,7 @@ module Psych
assert_equal true, scalar.plain
assert_equal false, scalar.quoted
assert_equal Nodes::Scalar::PLAIN, scalar.style
+ assert_location 2, 2, 2, 5, scalar
end
def test_mapping
@@ -66,6 +70,7 @@ module Psych
map = seq.children[1]
assert_instance_of Nodes::Mapping, map
+ assert_location 3, 2, 6, 1, map
end
def test_alias
@@ -75,6 +80,15 @@ module Psych
al = seq.children[2]
assert_instance_of Nodes::Alias, al
assert_equal 'A', al.anchor
+ assert_location 7, 2, 7, 4, al
+ end
+
+ private
+ def assert_location(start_line, start_column, end_line, end_column, node)
+ assert_equal start_line, node.start_line
+ assert_equal start_column, node.start_column
+ assert_equal end_line, node.end_line
+ assert_equal end_column, node.end_column
end
end
end
diff --git a/test/psych/test_yaml.rb b/test/psych/test_yaml.rb
index f8e9e2f955..0dfd60f894 100644
--- a/test/psych/test_yaml.rb
+++ b/test/psych/test_yaml.rb
@@ -1,5 +1,5 @@
# -*- coding: us-ascii; mode: ruby; ruby-indent-level: 4; tab-width: 4 -*-
-# frozen_string_literal: false
+# frozen_string_literal: true
# vim:sw=4:ts=4
# $Id$
#
@@ -512,7 +512,7 @@ EOY
def test_spec_log_file
doc_ct = 0
- Psych::load_documents( <<EOY
+ Psych::load_stream( <<EOY
---
Time: 2001-11-23 15:01:42 -05:00
User: ed
@@ -585,7 +585,7 @@ EOY
def test_spec_oneline_docs
doc_ct = 0
- Psych::load_documents( <<EOY
+ Psych::load_stream( <<EOY
# The following is a sequence of three documents.
# The first contains an empty mapping, the second
# an empty sequence, and the last an empty string.
@@ -617,11 +617,11 @@ EOY
raise ArgumentError, "Not a Hash in domain.tld,2002/invoice: " + val.inspect
end
}
- Psych.add_domain_type( "domain.tld,2002", 'invoice', &customer_proc )
- Psych.add_domain_type( "domain.tld,2002", 'customer', &customer_proc )
+ Psych.add_domain_type( "domain.tld/2002", 'invoice', &customer_proc )
+ Psych.add_domain_type( "domain.tld/2002", 'customer', &customer_proc )
assert_parse_only( { "invoice"=> { "customers"=> [ { "given"=>"Chris", "type"=>"domain customer", "family"=>"Dumars" } ], "type"=>"domain invoice" } }, <<EOY
# 'http://domain.tld,2002/invoice' is some type family.
-invoice: !domain.tld,2002/invoice
+invoice: !domain.tld/2002:invoice
# 'seq' is shorthand for 'http://yaml.org/seq'.
# This does not effect '^customer' below
# because it is does not specify a prefix.
@@ -705,7 +705,7 @@ EOY
end
def test_spec_explicit_families
- Psych.add_domain_type( "somewhere.com,2002", 'type' ) { |type, val|
+ Psych.add_domain_type( "somewhere.com/2002", 'type' ) { |type, val|
"SOMEWHERE: #{val}"
}
assert_parse_only(
@@ -717,7 +717,7 @@ picture: !binary |
Pz7Y6OjuDg4J+fn5OTk6enp
56enmleECcgggoBADs=
-hmm: !somewhere.com,2002/type |
+hmm: !somewhere.com/2002:type |
family above is short for
http://somewhere.com/type
EOY
@@ -726,7 +726,7 @@ EOY
def test_spec_application_family
# Testing the clarkevans.com graphs
- Psych.add_domain_type( "clarkevans.com,2002", 'graph/shape' ) { |type, val|
+ Psych.add_domain_type( "clarkevans.com/2002", 'graph/shape' ) { |type, val|
if Array === val
val << "Shape Container"
val
@@ -743,13 +743,13 @@ EOY
raise ArgumentError, "Invalid graph of type #{val.class}: " + val.inspect
end
}
- Psych.add_domain_type( "clarkevans.com,2002", 'graph/circle', &one_shape_proc )
- Psych.add_domain_type( "clarkevans.com,2002", 'graph/line', &one_shape_proc )
- Psych.add_domain_type( "clarkevans.com,2002", 'graph/text', &one_shape_proc )
+ Psych.add_domain_type( "clarkevans.com/2002", 'graph/circle', &one_shape_proc )
+ Psych.add_domain_type( "clarkevans.com/2002", 'graph/line', &one_shape_proc )
+ Psych.add_domain_type( "clarkevans.com/2002", 'graph/text', &one_shape_proc )
# MODIFIED to remove invalid Psych
assert_parse_only(
[[{"radius"=>7, "center"=>{"x"=>73, "y"=>129}, "TYPE"=>"Shape: graph/circle"}, {"finish"=>{"x"=>89, "y"=>102}, "TYPE"=>"Shape: graph/line", "start"=>{"x"=>73, "y"=>129}}, {"TYPE"=>"Shape: graph/text", "value"=>"Pretty vector drawing.", "start"=>{"x"=>73, "y"=>129}, "color"=>16772795}, "Shape Container"]], <<EOY
-- !clarkevans.com,2002/graph/shape
+- !clarkevans.com/2002:graph/shape
- !/graph/circle
center: &ORIGIN {x: 73, y: 129}
radius: 7
@@ -771,8 +771,8 @@ EOY
# have the same type and value.
- 10.0
- !float 10
-- !yaml.org,2002/float '10'
-- !yaml.org,2002/float "\\
+- !yaml.org/2002/float '10'
+- !yaml.org/2002/float "\\
1\\
0"
EOY
diff --git a/test/psych/test_yaml_special_cases.rb b/test/psych/test_yaml_special_cases.rb
new file mode 100644
index 0000000000..4501704030
--- /dev/null
+++ b/test/psych/test_yaml_special_cases.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+require_relative 'helper'
+
+require 'stringio'
+require 'tempfile'
+
+module Psych
+ class TestYamlSpecialCases < TestCase
+ def setup
+ super
+ end
+
+ def test_empty_string
+ s = ""
+ assert_equal false, Psych.load(s)
+ assert_equal [], Psych.load_stream(s)
+ assert_equal false, Psych.parse(s)
+ assert_equal [], Psych.parse_stream(s).transform
+ assert_nil Psych.safe_load(s)
+ end
+
+ def test_false
+ s = "false"
+ assert_equal false, Psych.load(s)
+ assert_equal [false], Psych.load_stream(s)
+ assert_equal false, Psych.parse(s).transform
+ assert_equal [false], Psych.parse_stream(s).transform
+ assert_equal false, Psych.safe_load(s)
+ end
+
+ def test_n
+ s = "n"
+ assert_equal "n", Psych.load(s)
+ assert_equal ["n"], Psych.load_stream(s)
+ assert_equal "n", Psych.parse(s).transform
+ assert_equal ["n"], Psych.parse_stream(s).transform
+ assert_equal "n", Psych.safe_load(s)
+ end
+
+ def test_off
+ s = "off"
+ assert_equal false, Psych.load(s)
+ assert_equal [false], Psych.load_stream(s)
+ assert_equal false, Psych.parse(s).transform
+ assert_equal [false], Psych.parse_stream(s).transform
+ assert_equal false, Psych.safe_load(s)
+ end
+
+ def test_inf
+ s = "-.inf"
+ assert_equal(-Float::INFINITY, Psych.load(s))
+ assert_equal([-Float::INFINITY], Psych.load_stream(s))
+ assert_equal(-Float::INFINITY, Psych.parse(s).transform)
+ assert_equal([-Float::INFINITY], Psych.parse_stream(s).transform)
+ assert_equal(-Float::INFINITY, Psych.safe_load(s))
+ end
+
+ def test_NaN
+ s = ".NaN"
+ assert Float::NAN, Psych.load(s).nan?
+ assert [Float::NAN], Psych.load_stream(s).first.nan?
+ assert Psych.parse(s).transform.nan?
+ assert Psych.parse_stream(s).transform.first.nan?
+ assert Psych.safe_load(s).nan?
+ end
+
+ def test_0xC
+ s = "0xC"
+ assert_equal 12, Psych.load(s)
+ assert_equal [12], Psych.load_stream(s)
+ assert_equal 12, Psych.parse(s).transform
+ assert_equal [12], Psych.parse_stream(s).transform
+ assert_equal 12, Psych.safe_load(s)
+ end
+
+ def test_arrows
+ s = "<<"
+ assert_equal "<<", Psych.load(s)
+ assert_equal ["<<"], Psych.load_stream(s)
+ assert_equal "<<", Psych.parse(s).transform
+ assert_equal ["<<"], Psych.parse_stream(s).transform
+ assert_equal "<<", Psych.safe_load(s)
+ end
+
+ def test_arrows_hash
+ s = "<<: {}"
+ assert_equal({}, Psych.load(s))
+ assert_equal [{}], Psych.load_stream(s)
+ assert_equal({}, Psych.parse(s).transform)
+ assert_equal [{}], Psych.parse_stream(s).transform
+ assert_equal({}, Psych.safe_load(s))
+ end
+
+ def test_thousand
+ s = "- 1000\n- +1000\n- 1_000"
+ assert_equal [1000, 1000, 1000], Psych.load(s)
+ assert_equal [[1000, 1000, 1000]], Psych.load_stream(s)
+ assert_equal [1000, 1000, 1000], Psych.parse(s).transform
+ assert_equal [[1000, 1000, 1000]], Psych.parse_stream(s).transform
+ assert_equal [1000, 1000, 1000], Psych.safe_load(s)
+ end
+
+ def test_8
+ s = "[8, 08, 0o10, 010]"
+ assert_equal [8, "08", "0o10", 8], Psych.load(s)
+ assert_equal [[8, "08", "0o10", 8]], Psych.load_stream(s)
+ assert_equal [8, "08", "0o10", 8], Psych.parse(s).transform
+ assert_equal [[8, "08", "0o10", 8]], Psych.parse_stream(s).transform
+ assert_equal [8, "08", "0o10", 8], Psych.safe_load(s)
+ end
+
+ def test_null
+ s = "null"
+ assert_nil Psych.load(s)
+ assert_equal [nil], Psych.load_stream(s)
+ assert_nil Psych.parse(s).transform
+ assert_equal [nil], Psych.parse_stream(s).transform
+ assert_nil Psych.safe_load(s)
+ end
+
+ private
+
+ def special_case_cycle(object)
+ %w[load load_stream parse parse_stream safe_load].map do |m|
+ Psych.public_send(m, object)
+ end
+ end
+ end
+end
diff --git a/test/psych/test_yamldbm.rb b/test/psych/test_yamldbm.rb
index 71ee04f863..1f9ba15cde 100644
--- a/test/psych/test_yamldbm.rb
+++ b/test/psych/test_yamldbm.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
require 'tmpdir'
diff --git a/test/psych/test_yamlstore.rb b/test/psych/test_yamlstore.rb
index a9ce652ced..d1e927cefe 100644
--- a/test/psych/test_yamlstore.rb
+++ b/test/psych/test_yamlstore.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require_relative 'helper'
require 'yaml/store'
require 'tmpdir'
diff --git a/test/psych/visitors/test_depth_first.rb b/test/psych/visitors/test_depth_first.rb
index 8072c26b57..f8305e15ec 100644
--- a/test/psych/visitors/test_depth_first.rb
+++ b/test/psych/visitors/test_depth_first.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/helper'
module Psych
diff --git a/test/psych/visitors/test_emitter.rb b/test/psych/visitors/test_emitter.rb
index 9317855bbb..70adbb9ca0 100644
--- a/test/psych/visitors/test_emitter.rb
+++ b/test/psych/visitors/test_emitter.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/helper'
module Psych
diff --git a/test/psych/visitors/test_to_ruby.rb b/test/psych/visitors/test_to_ruby.rb
index 8878ea22b2..f342bf0a1a 100644
--- a/test/psych/visitors/test_to_ruby.rb
+++ b/test/psych/visitors/test_to_ruby.rb
@@ -1,5 +1,5 @@
# coding: US-ASCII
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/helper'
module Psych
diff --git a/test/psych/visitors/test_yaml_tree.rb b/test/psych/visitors/test_yaml_tree.rb
index 5755f5863e..01f1aecd08 100644
--- a/test/psych/visitors/test_yaml_tree.rb
+++ b/test/psych/visitors/test_yaml_tree.rb
@@ -1,9 +1,18 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'psych/helper'
module Psych
module Visitors
class TestYAMLTree < TestCase
+ class TestDelegatorClass < Delegator
+ def initialize(obj); super; @obj = obj; end
+ def __setobj__(obj); @obj = obj; end
+ def __getobj__; @obj; end
+ end
+
+ class TestSimpleDelegatorClass < SimpleDelegator
+ end
+
def setup
super
@v = Visitors::YAMLTree.create
@@ -28,7 +37,7 @@ module Psych
end
def test_binary_formatting
- gif = "GIF89a\f\x00\f\x00\x84\x00\x00\xFF\xFF\xF7\xF5\xF5\xEE\xE9\xE9\xE5fff\x00\x00\x00\xE7\xE7\xE7^^^\xF3\xF3\xED\x8E\x8E\x8E\xE0\xE0\xE0\x9F\x9F\x9F\x93\x93\x93\xA7\xA7\xA7\x9E\x9E\x9Eiiiccc\xA3\xA3\xA3\x84\x84\x84\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9!\xFE\x0EMade with GIMP\x00,\x00\x00\x00\x00\f\x00\f\x00\x00\x05, \x8E\x810\x9E\xE3@\x14\xE8i\x10\xC4\xD1\x8A\b\x1C\xCF\x80M$z\xEF\xFF0\x85p\xB8\xB01f\r\e\xCE\x01\xC3\x01\x1E\x10' \x82\n\x01\x00;"
+ gif = "GIF89a\f\x00\f\x00\x84\x00\x00\xFF\xFF\xF7\xF5\xF5\xEE\xE9\xE9\xE5fff\x00\x00\x00\xE7\xE7\xE7^^^\xF3\xF3\xED\x8E\x8E\x8E\xE0\xE0\xE0\x9F\x9F\x9F\x93\x93\x93\xA7\xA7\xA7\x9E\x9E\x9Eiiiccc\xA3\xA3\xA3\x84\x84\x84\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9!\xFE\x0EMade with GIMP\x00,\x00\x00\x00\x00\f\x00\f\x00\x00\x05, \x8E\x810\x9E\xE3@\x14\xE8i\x10\xC4\xD1\x8A\b\x1C\xCF\x80M$z\xEF\xFF0\x85p\xB8\xB01f\r\e\xCE\x01\xC3\x01\x1E\x10' \x82\n\x01\x00;".b
@v << gif
scalar = @v.tree.children.first.children.first
assert_equal Psych::Nodes::Scalar::LITERAL, scalar.style
@@ -165,16 +174,24 @@ module Psych
# http://yaml.org/type/null.html
def test_nil
assert_cycle nil
- assert_equal nil, Psych.load('null')
- assert_equal nil, Psych.load('Null')
- assert_equal nil, Psych.load('NULL')
- assert_equal nil, Psych.load('~')
+ assert_nil Psych.load('null')
+ assert_nil Psych.load('Null')
+ assert_nil Psych.load('NULL')
+ assert_nil Psych.load('~')
assert_equal({'foo' => nil}, Psych.load('foo: '))
assert_cycle 'null'
assert_cycle 'nUll'
assert_cycle '~'
end
+
+ def test_delegator
+ assert_cycle(TestDelegatorClass.new([1, 2, 3]))
+ end
+
+ def test_simple_delegator
+ assert_cycle(TestSimpleDelegatorClass.new([1, 2, 3]))
+ end
end
end
end
diff --git a/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Basics.text b/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Basics.text
index 486055ca7f..6c5a6fdb4b 100644
--- a/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Basics.text
+++ b/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Basics.text
@@ -123,7 +123,7 @@ Output:
Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`,
`+`, and `-`) as list markers. These three markers are
-interchangable; this:
+interchangeable; this:
* Candy.
* Gum.
diff --git a/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Syntax.text b/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Syntax.text
index 57360a16c8..184018a5ac 100644
--- a/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Syntax.text
+++ b/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Syntax.text
@@ -298,7 +298,7 @@ Quote Level from the Text menu.
Markdown supports ordered (numbered) and unordered (bulleted) lists.
-Unordered lists use asterisks, pluses, and hyphens -- interchangably
+Unordered lists use asterisks, pluses, and hyphens -- interchangeably
-- as list markers:
* Red
diff --git a/test/rdoc/minitest_helper.rb b/test/rdoc/minitest_helper.rb
new file mode 100644
index 0000000000..9fb0cd676b
--- /dev/null
+++ b/test/rdoc/minitest_helper.rb
@@ -0,0 +1,204 @@
+# frozen_string_literal: true
+require 'bundler/errors'
+begin
+ gem 'minitest', '~> 5.0'
+rescue NoMethodError, Gem::LoadError, Bundler::GemfileNotFound
+ # for ruby tests
+end
+
+require 'minitest/autorun'
+require 'minitest/benchmark' unless ENV['NOBENCHMARK']
+
+require 'fileutils'
+require 'pp'
+require 'tempfile'
+require 'tmpdir'
+require 'stringio'
+
+require 'rdoc'
+
+##
+# RDoc::TestCase is an abstract TestCase to provide common setup and teardown
+# across all RDoc tests. The test case uses minitest, so all the assertions
+# of minitest may be used.
+#
+# The testcase provides the following:
+#
+# * A reset code-object tree
+# * A reset markup preprocessor (RDoc::Markup::PreProcess)
+# * The <code>@RM</code> alias of RDoc::Markup (for less typing)
+# * <code>@pwd</code> containing the current working directory
+# * FileUtils, pp, Tempfile, Dir.tmpdir and StringIO
+
+class RDoc::TestCase < (defined?(Minitest::Test) ? Minitest::Test : MiniTest::Unit::TestCase)
+
+ ##
+ # Abstract test-case setup
+
+ def setup
+ super
+
+ @top_level = nil
+
+ @RM = RDoc::Markup
+
+ @pwd = Dir.pwd
+
+ @store = RDoc::Store.new
+
+ @rdoc = RDoc::RDoc.new
+ @rdoc.store = @store
+ @rdoc.options = RDoc::Options.new
+
+ g = Object.new
+ def g.class_dir() end
+ def g.file_dir() end
+ @rdoc.generator = g
+
+ RDoc::Markup::PreProcess.reset
+ end
+
+ ##
+ # Asserts +path+ is a file
+
+ def assert_file path
+ assert File.file?(path), "#{path} is not a file"
+ end
+
+ ##
+ # Asserts +path+ is a directory
+
+ def assert_directory path
+ assert File.directory?(path), "#{path} is not a directory"
+ end
+
+ ##
+ # Refutes +path+ exists
+
+ def refute_file path
+ refute File.exist?(path), "#{path} exists"
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::BlankLine.new
+
+ def blank_line
+ @RM::BlankLine.new
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::BlockQuote.new with +contents+
+
+ def block *contents
+ @RM::BlockQuote.new(*contents)
+ end
+
+ ##
+ # Creates an RDoc::Comment with +text+ which was defined on +top_level+.
+ # By default the comment has the 'rdoc' format.
+
+ def comment text, top_level = @top_level
+ RDoc::Comment.new text, top_level
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::Document.new with +contents+
+
+ def doc *contents
+ @RM::Document.new(*contents)
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::HardBreak.new
+
+ def hard_break
+ @RM::HardBreak.new
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::Heading.new with +level+ and +text+
+
+ def head level, text
+ @RM::Heading.new level, text
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::ListItem.new with +label+ and +parts+
+
+ def item label = nil, *parts
+ @RM::ListItem.new label, *parts
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::List.new with +type+ and +items+
+
+ def list type = nil, *items
+ @RM::List.new type, *items
+ end
+
+ ##
+ # Enables pretty-print output
+
+ def mu_pp obj # :nodoc:
+ s = obj.pretty_inspect
+ s = RDoc::Encoding.change_encoding s, Encoding.default_external
+ s.chomp
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::Paragraph.new with +contents+
+
+ def para *a
+ @RM::Paragraph.new(*a)
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::Rule.new with +weight+
+
+ def rule weight
+ @RM::Rule.new weight
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::Raw.new with +contents+
+
+ def raw *contents
+ @RM::Raw.new(*contents)
+ end
+
+ ##
+ # Creates a temporary directory changes the current directory to it for the
+ # duration of the block.
+ #
+ # Depends upon Dir.mktmpdir
+
+ def temp_dir
+ Dir.mktmpdir do |temp_dir|
+ Dir.chdir temp_dir do
+ yield temp_dir
+ end
+ end
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::Verbatim.new with +parts+
+
+ def verb *parts
+ @RM::Verbatim.new(*parts)
+ end
+
+ ##
+ # run capture_io with setting $VERBOSE = true
+
+ def verbose_capture_io
+ capture_io do
+ begin
+ orig_verbose = $VERBOSE
+ $VERBOSE = true
+ yield
+ ensure
+ $VERBOSE = orig_verbose
+ end
+ end
+ end
+end
diff --git a/test/rdoc/test_rdoc_alias.rb b/test/rdoc/test_rdoc_alias.rb
index fa0ab0f366..89ae2d5a56 100644
--- a/test/rdoc/test_rdoc_alias.rb
+++ b/test/rdoc/test_rdoc_alias.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
class TestRDocAlias < XrefTestCase
diff --git a/test/rdoc/test_rdoc_any_method.rb b/test/rdoc/test_rdoc_any_method.rb
index fdbb62efa6..6dd46b0b46 100644
--- a/test/rdoc/test_rdoc_any_method.rb
+++ b/test/rdoc/test_rdoc_any_method.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
class TestRDocAnyMethod < XrefTestCase
@@ -74,7 +74,7 @@ method(a, b) { |c, d| ... }
def test_markup_code
tokens = [
- RDoc::RubyToken::TkCONSTANT. new(0, 0, 0, 'CONSTANT'),
+ { :line_no => 0, :char_no => 0, :kind => :on_const, :text => 'CONSTANT' },
]
@c2_a.collect_tokens
@@ -85,10 +85,46 @@ method(a, b) { |c, d| ... }
assert_equal expected, @c2_a.markup_code
end
+ def test_markup_code_with_line_numbers
+ position_comment = "# File #{@file_name}, line 1"
+ tokens = [
+ { :line_no => 1, :char_no => 0, :kind => :on_comment, :text => position_comment },
+ { :line_no => 1, :char_no => position_comment.size, :kind => :on_nl, :text => "\n" },
+ { :line_no => 2, :char_no => 0, :kind => :on_const, :text => 'A' },
+ { :line_no => 2, :char_no => 1, :kind => :on_nl, :text => "\n" },
+ { :line_no => 3, :char_no => 0, :kind => :on_const, :text => 'B' }
+ ]
+
+ @c2_a.collect_tokens
+ @c2_a.add_tokens(*tokens)
+
+ assert_equal <<-EXPECTED.chomp, @c2_a.markup_code
+<span class="ruby-comment"># File xref_data.rb, line 1</span>
+<span class="ruby-constant">A</span>
+<span class="ruby-constant">B</span>
+ EXPECTED
+
+ @options.line_numbers = true
+ assert_equal <<-EXPECTED.chomp, @c2_a.markup_code
+ <span class="ruby-comment"># File xref_data.rb</span>
+<span class="line-num">1</span> <span class="ruby-constant">A</span>
+<span class="line-num">2</span> <span class="ruby-constant">B</span>
+ EXPECTED
+ end
+
def test_markup_code_empty
assert_equal '', @c2_a.markup_code
end
+ def test_markup_code_with_variable_expansion
+ m = RDoc::AnyMethod.new nil, 'method'
+ m.parent = @c1
+ m.block_params = '"Hello, #{world}", yield_arg'
+ m.params = 'a'
+
+ assert_equal '(a) { |"Hello, #{world}", yield_arg| ... }', m.param_seq
+ end
+
def test_marshal_dump
@store.path = Dir.tmpdir
top_level = @store.add_file 'file.rb'
@@ -124,7 +160,7 @@ method(a, b) { |c, d| ... }
assert_equal 'Klass#method', loaded.full_name
assert_equal 'method', loaded.name
assert_equal 'param', loaded.params
- assert_equal nil, loaded.singleton # defaults to nil
+ assert_nil loaded.singleton # defaults to nil
assert_equal :public, loaded.visibility
assert_equal cm, loaded.parent
assert_equal section, loaded.section
@@ -142,8 +178,21 @@ method(a, b) { |c, d| ... }
assert aliased_method.display?
end
+ def test_marshal_load_aliased_method_with_nil_singleton
+ aliased_method = Marshal.load Marshal.dump(@c2_a)
+
+ aliased_method.store = @store
+ aliased_method.is_alias_for = ["C2", nil, "b"]
+
+ assert_equal 'C2#a', aliased_method.full_name
+ assert_equal 'C2', aliased_method.parent_name
+ assert_equal '()', aliased_method.params
+ assert_equal @c2_b, aliased_method.is_alias_for, 'is_alias_for'
+ assert aliased_method.display?
+ end
+
def test_marshal_load_class_method
- class_method = Marshal.load Marshal.dump(@c1.method_list.first)
+ class_method = Marshal.load Marshal.dump(@c1.find_class_method_named 'm')
assert_equal 'C1::m', class_method.full_name
assert_equal 'C1', class_method.parent_name
@@ -152,7 +201,7 @@ method(a, b) { |c, d| ... }
end
def test_marshal_load_instance_method
- instance_method = Marshal.load Marshal.dump(@c1.method_list.last)
+ instance_method = Marshal.load Marshal.dump(@c1.find_instance_method_named 'm')
assert_equal 'C1#m', instance_method.full_name
assert_equal 'C1', instance_method.parent_name
@@ -198,9 +247,9 @@ method(a, b) { |c, d| ... }
assert_equal 'Klass#method', loaded.full_name
assert_equal 'method', loaded.name
assert_equal 'param', loaded.params
- assert_equal nil, loaded.singleton # defaults to nil
+ assert_nil loaded.singleton # defaults to nil
assert_equal :public, loaded.visibility
- assert_equal nil, loaded.file
+ assert_nil loaded.file
assert_equal cm, loaded.parent
assert_equal section, loaded.section
assert_nil loaded.is_alias_for
@@ -255,7 +304,7 @@ method(a, b) { |c, d| ... }
assert_equal 'Klass#method', loaded.full_name
assert_equal 'method', loaded.name
assert_equal 'param', loaded.params
- assert_equal nil, loaded.singleton # defaults to nil
+ assert_nil loaded.singleton # defaults to nil
assert_equal :public, loaded.visibility
assert_equal cm, loaded.parent
assert_equal section, loaded.section
@@ -458,4 +507,3 @@ method(a, b) { |c, d| ... }
end
end
-
diff --git a/test/rdoc/test_rdoc_attr.rb b/test/rdoc/test_rdoc_attr.rb
index e4062d1807..5910c0fad3 100644
--- a/test/rdoc/test_rdoc_attr.rb
+++ b/test/rdoc/test_rdoc_attr.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocAttr < RDoc::TestCase
@@ -139,7 +139,7 @@ class TestRDocAttr < RDoc::TestCase
assert_equal cm, loaded.parent
assert_equal section, loaded.section
- assert loaded.display?
+ assert loaded.display?
end
def test_marshal_load_version_2
@@ -188,4 +188,3 @@ class TestRDocAttr < RDoc::TestCase
end
end
-
diff --git a/test/rdoc/test_rdoc_class_module.rb b/test/rdoc/test_rdoc_class_module.rb
index 7cbbcbcc52..cc53a13528 100644
--- a/test/rdoc/test_rdoc_class_module.rb
+++ b/test/rdoc/test_rdoc_class_module.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
class TestRDocClassModule < XrefTestCase
@@ -42,7 +42,8 @@ class TestRDocClassModule < XrefTestCase
cm.add_comment '# comment 1', tl1
cm.add_comment '# comment 2', tl1
- assert_equal [['comment 2', tl1]], cm.comment_location
+ assert_equal [['comment 1', tl1],
+ ['comment 2', tl1]], cm.comment_location
end
def test_add_comment_stopdoc
@@ -90,6 +91,7 @@ class TestRDocClassModule < XrefTestCase
assert @c1.document_self_or_methods
+ @c1_plus.document_self = false
@c1_m.document_self = false
assert @c1.document_self_or_methods
@@ -1276,7 +1278,8 @@ class TestRDocClassModule < XrefTestCase
n1 = @xref_data.add_module RDoc::NormalClass, 'N1'
n1_k2 = n1.add_module RDoc::NormalClass, 'N2'
- n1.add_module_alias n1_k2, 'A1', @xref_data
+ a1 = RDoc::Constant.new 'A1', '', ''
+ n1.add_module_alias n1_k2, n1_k2.name, a1, @xref_data
n1_a1_c = n1.constants.find { |c| c.name == 'A1' }
refute_nil n1_a1_c
@@ -1300,7 +1303,8 @@ class TestRDocClassModule < XrefTestCase
n1 = @xref_data.add_module RDoc::NormalModule, 'N1'
n1_n2 = n1.add_module RDoc::NormalModule, 'N2'
- n1.add_module_alias n1_n2, 'A1', @xref_data
+ a1 = RDoc::Constant.new 'A1', '', ''
+ n1.add_module_alias n1_n2, n1_n2.name, a1, @xref_data
n1_a1_c = n1.constants.find { |c| c.name == 'A1' }
refute_nil n1_a1_c
@@ -1325,7 +1329,8 @@ class TestRDocClassModule < XrefTestCase
l1_l2 = l1.add_module RDoc::NormalModule, 'L2'
o1 = @xref_data.add_module RDoc::NormalModule, 'O1'
- o1.add_module_alias l1_l2, 'A1', @xref_data
+ a1 = RDoc::Constant.new 'A1', '', ''
+ o1.add_module_alias l1_l2, l1_l2.name, a1, @xref_data
o1_a1_c = o1.constants.find { |c| c.name == 'A1' }
refute_nil o1_a1_c
@@ -1357,7 +1362,8 @@ class TestRDocClassModule < XrefTestCase
const.record_location top_level
const.is_alias_for = klass
- top_level.add_module_alias klass, 'A', top_level
+ a = RDoc::Constant.new 'A', '', ''
+ top_level.add_module_alias klass, klass.name, a, top_level
object.add_constant const
diff --git a/test/rdoc/test_rdoc_code_object.rb b/test/rdoc/test_rdoc_code_object.rb
index e0d89e4e67..fad182722a 100644
--- a/test/rdoc/test_rdoc_code_object.rb
+++ b/test/rdoc/test_rdoc_code_object.rb
@@ -1,5 +1,5 @@
# coding: US-ASCII
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
@@ -53,7 +53,7 @@ class TestRDocCodeObject < XrefTestCase
refute_equal Encoding::UTF_8, ''.encoding, 'Encoding sanity check'
input = 'text'
- input.force_encoding Encoding::UTF_8
+ input = RDoc::Encoding.change_encoding input, Encoding::UTF_8
@co.comment = input
@@ -65,7 +65,7 @@ class TestRDocCodeObject < XrefTestCase
refute_equal Encoding::UTF_8, ''.encoding, 'Encoding sanity check'
input = ''
- input.force_encoding Encoding::UTF_8
+ input = RDoc::Encoding.change_encoding input, Encoding::UTF_8
@co.comment = input
@@ -213,7 +213,7 @@ class TestRDocCodeObject < XrefTestCase
end
def test_file_name
- assert_equal nil, @co.file_name
+ assert_nil @co.file_name
@co.record_location @store.add_file 'lib/file.rb'
@@ -276,12 +276,6 @@ class TestRDocCodeObject < XrefTestCase
assert_equal 'not_rdoc', @co.metadata['markup']
end
- def test_offset
- @c1_m.offset = 5
-
- assert_equal 5, @c1_m.offset
- end
-
def test_options
assert_kind_of RDoc::Options, @co.options
diff --git a/test/rdoc/test_rdoc_comment.rb b/test/rdoc/test_rdoc_comment.rb
index 3b0927212f..9b3c105bb0 100644
--- a/test/rdoc/test_rdoc_comment.rb
+++ b/test/rdoc/test_rdoc_comment.rb
@@ -1,7 +1,7 @@
# coding: us-ascii
-# frozen_string_literal: false
+# frozen_string_literal: true
-require 'rdoc/test_case'
+require 'minitest_helper'
class TestRDocComment < RDoc::TestCase
@@ -77,7 +77,7 @@ call-seq:
comment.extract_call_seq m
- assert_equal nil, m.call_seq
+ assert_nil m.call_seq
end
def test_extract_call_seq_no_blank
@@ -207,7 +207,7 @@ lines, one line per element. Lines are assumed to be separated by _sep_.
end
def test_force_encoding
- @comment.force_encoding Encoding::UTF_8
+ @comment = RDoc::Encoding.change_encoding @comment, Encoding::UTF_8
assert_equal Encoding::UTF_8, @comment.text.encoding
end
@@ -347,7 +347,7 @@ lines, one line per element. Lines are assumed to be separated by _sep_.
# this is private
EOS
- comment.force_encoding Encoding::IBM437
+ comment = RDoc::Encoding.change_encoding comment, Encoding::IBM437
comment.remove_private
@@ -471,7 +471,7 @@ lines, one line per element. Lines are assumed to be separated by _sep_.
# This is text again.
EOS
- comment.force_encoding Encoding::IBM437
+ comment = RDoc::Encoding.change_encoding comment, Encoding::IBM437
comment.remove_private
@@ -486,7 +486,7 @@ lines, one line per element. Lines are assumed to be separated by _sep_.
# This is text again.
EOS
- comment.force_encoding Encoding::IBM437
+ comment = RDoc::Encoding.change_encoding comment, Encoding::IBM437
comment.remove_private
diff --git a/test/rdoc/test_rdoc_constant.rb b/test/rdoc/test_rdoc_constant.rb
index c43aa7dc2b..79dcdad57e 100644
--- a/test/rdoc/test_rdoc_constant.rb
+++ b/test/rdoc/test_rdoc_constant.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
class TestRDocConstant < XrefTestCase
@@ -86,7 +86,7 @@ class TestRDocConstant < XrefTestCase
assert_equal top_level, loaded.file
assert_equal 'Klass::CONST', loaded.full_name
assert_equal 'CONST', loaded.name
- assert_nil loaded.visibility
+ assert_equal :public, loaded.visibility
assert_equal cm, loaded.parent
assert_equal section, loaded.section
end
@@ -114,11 +114,11 @@ class TestRDocConstant < XrefTestCase
assert_equal top_level, loaded.file
assert_equal 'Klass::CONST', loaded.full_name
assert_equal 'CONST', loaded.name
- assert_nil loaded.visibility
+ assert_equal :public, loaded.visibility
assert_equal cm, loaded.parent
assert_equal section, loaded.section
- assert loaded.display?
+ assert loaded.display?
end
def test_marshal_load_version_0
@@ -146,7 +146,7 @@ class TestRDocConstant < XrefTestCase
assert_equal top_level, loaded.file
assert_equal 'Klass::CONST', loaded.full_name
assert_equal 'CONST', loaded.name
- assert_nil loaded.visibility
+ assert_equal :public, loaded.visibility
assert_equal cm, loaded.parent
assert_equal section, loaded.section
diff --git a/test/rdoc/test_rdoc_context.rb b/test/rdoc/test_rdoc_context.rb
index cc9ab3650f..be17496f40 100644
--- a/test/rdoc/test_rdoc_context.rb
+++ b/test/rdoc/test_rdoc_context.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
class TestRDocContext < XrefTestCase
@@ -14,10 +14,10 @@ class TestRDocContext < XrefTestCase
assert_empty @context.in_files
assert_equal 'unknown', @context.name
assert_equal '', @context.comment
- assert_equal nil, @context.parent
+ assert_nil @context.parent
assert_equal :public, @context.visibility
assert_equal 1, @context.sections.length
- assert_equal nil, @context.temporary_section
+ assert_nil @context.temporary_section
assert_empty @context.classes_hash
assert_empty @context.modules_hash
@@ -280,7 +280,8 @@ class TestRDocContext < XrefTestCase
def test_add_module_alias
tl = @store.add_file 'file.rb'
- c3_c4 = @c2.add_module_alias @c2_c3, 'C4', tl
+ c4 = RDoc::Constant.new 'C4', '', ''
+ c3_c4 = @c2.add_module_alias @c2_c3, @c2_c3.name, c4, tl
alias_constant = @c2.constants.first
@@ -298,7 +299,8 @@ class TestRDocContext < XrefTestCase
object = top_level.add_class RDoc::NormalClass, 'Object'
- top_level.add_module_alias klass, 'A', top_level
+ a = RDoc::Constant.new 'A', '', ''
+ top_level.add_module_alias klass, klass.name, a, top_level
refute_empty object.constants
@@ -481,12 +483,38 @@ class TestRDocContext < XrefTestCase
assert_equal expected_attrs, attrs
end
+ def test_each_section_only_display
+ sects = []
+ consts = []
+ attrs = []
+
+ @c7.each_section do |section, constants, attributes|
+ sects << section
+ consts << constants
+ attrs << attributes
+ end
+
+ assert_equal [nil], sects.map { |section| section.title }
+
+ expected_consts = [
+ @c7.constants.select(&:display?).sort
+ ]
+
+ assert_equal expected_consts, consts
+
+ expected_attrs = [
+ @c7.attributes.select(&:display?).sort
+ ]
+
+ assert_equal expected_attrs, attrs
+ end
+
def test_each_section_enumerator
assert_kind_of Enumerator, @c1.each_section
end
def test_find_attribute_named
- assert_equal nil, @c1.find_attribute_named('none')
+ assert_nil @c1.find_attribute_named('none')
assert_equal 'R', @c1.find_attribute_named('attr').rw
assert_equal 'R', @c1.find_attribute_named('attr_reader').rw
assert_equal 'W', @c1.find_attribute_named('attr_writer').rw
@@ -494,7 +522,7 @@ class TestRDocContext < XrefTestCase
end
def test_find_class_method_named
- assert_equal nil, @c1.find_class_method_named('none')
+ assert_nil @c1.find_class_method_named('none')
m = @c1.find_class_method_named('m')
assert_instance_of RDoc::AnyMethod, m
@@ -502,23 +530,23 @@ class TestRDocContext < XrefTestCase
end
def test_find_constant_named
- assert_equal nil, @c1.find_constant_named('NONE')
+ assert_nil @c1.find_constant_named('NONE')
assert_equal ':const', @c1.find_constant_named('CONST').value
end
def test_find_enclosing_module_named
- assert_equal nil, @c2_c3.find_enclosing_module_named('NONE')
+ assert_nil @c2_c3.find_enclosing_module_named('NONE')
assert_equal @c1, @c2_c3.find_enclosing_module_named('C1')
assert_equal @c2, @c2_c3.find_enclosing_module_named('C2')
end
def test_find_file_named
- assert_equal nil, @c1.find_file_named('nonexistent.rb')
+ assert_nil @c1.find_file_named('nonexistent.rb')
assert_equal @xref_data, @c1.find_file_named(@file_name)
end
def test_find_instance_method_named
- assert_equal nil, @c1.find_instance_method_named('none')
+ assert_nil @c1.find_instance_method_named('none')
m = @c1.find_instance_method_named('m')
assert_instance_of RDoc::AnyMethod, m
@@ -533,6 +561,14 @@ class TestRDocContext < XrefTestCase
assert_equal @c2_c3, @c2.find_local_symbol('C3')
end
+ def test_find_method
+ loaded_c2 = Marshal.load Marshal.dump @c2
+ assert_equal @c2_a, loaded_c2.find_method('a', false)
+ assert_equal @c2_b, loaded_c2.find_method('b', false)
+ assert_equal @c2_a, loaded_c2.find_method('a', nil)
+ assert_equal @c2_b, loaded_c2.find_method('b', nil)
+ end
+
def test_find_method_named
assert_equal true, @c1.find_method_named('m').singleton
end
@@ -624,7 +660,7 @@ class TestRDocContext < XrefTestCase
'instance' => {
:private => [],
:protected => [],
- :public => [@c1_m],
+ :public => [@c1_plus, @c1_m],
},
'class' => {
:private => [],
@@ -693,6 +729,7 @@ class TestRDocContext < XrefTestCase
assert_equal [@pub, @prot, @priv], @vis.method_list
assert_equal [@apub, @aprot, @apriv], @vis.attributes
+ assert_equal [@cpub, @cpriv], @vis.constants
end
def test_remove_invisible_nodoc
@@ -702,6 +739,7 @@ class TestRDocContext < XrefTestCase
assert_equal [@pub, @prot, @priv], @vis.method_list
assert_equal [@apub, @aprot, @apriv], @vis.attributes
+ assert_equal [@cpub, @cpriv], @vis.constants
end
def test_remove_invisible_protected
@@ -711,6 +749,7 @@ class TestRDocContext < XrefTestCase
assert_equal [@pub, @prot], @vis.method_list
assert_equal [@apub, @aprot], @vis.attributes
+ assert_equal [@cpub], @vis.constants
end
def test_remove_invisible_public
@@ -720,6 +759,7 @@ class TestRDocContext < XrefTestCase
assert_equal [@pub], @vis.method_list
assert_equal [@apub], @vis.attributes
+ assert_equal [@cpub], @vis.constants
end
def test_remove_invisible_public_force
@@ -729,11 +769,13 @@ class TestRDocContext < XrefTestCase
@prot.force_documentation = true
@apriv.force_documentation = true
@aprot.force_documentation = true
+ @cpriv.force_documentation = true
@vis.remove_invisible :public
assert_equal [@pub, @prot, @priv], @vis.method_list
assert_equal [@apub, @aprot, @apriv], @vis.attributes
+ assert_equal [@cpub, @cpriv], @vis.constants
end
def test_remove_invisible_in_protected
@@ -866,6 +908,27 @@ class TestRDocContext < XrefTestCase
assert_equal [nil, 'Public', 'Internal'], titles
end
+ def test_visibility_def
+ assert_equal :private, @c6.find_method_named('priv1').visibility
+ assert_equal :protected, @c6.find_method_named('prot1').visibility
+ assert_equal :public, @c6.find_method_named('pub1').visibility
+ assert_equal :private, @c6.find_method_named('priv2').visibility
+ assert_equal :protected, @c6.find_method_named('prot2').visibility
+ assert_equal :public, @c6.find_method_named('pub2').visibility
+ assert_equal :private, @c6.find_method_named('priv3').visibility
+ assert_equal :protected, @c6.find_method_named('prot3').visibility
+ assert_equal :public, @c6.find_method_named('pub3').visibility
+ assert_equal :private, @c6.find_method_named('priv4').visibility
+ assert_equal :protected, @c6.find_method_named('prot4').visibility
+ assert_equal :public, @c6.find_method_named('pub4').visibility
+ assert_equal :private, @c6.find_method_named('priv5').visibility
+ assert_equal :protected, @c6.find_method_named('prot5').visibility
+ assert_equal :public, @c6.find_method_named('pub5').visibility
+ assert_equal :private, @c6.find_method_named('priv6').visibility
+ assert_equal :protected, @c6.find_method_named('prot6').visibility
+ assert_equal :public, @c6.find_method_named('pub6').visibility
+ end
+
def util_visibilities
@pub = RDoc::AnyMethod.new nil, 'pub'
@prot = RDoc::AnyMethod.new nil, 'prot'
@@ -875,6 +938,9 @@ class TestRDocContext < XrefTestCase
@aprot = RDoc::Attr.new nil, 'prot', 'RW', nil
@apriv = RDoc::Attr.new nil, 'priv', 'RW', nil
+ @cpub = RDoc::Constant.new 'CONST_PUBLIC', nil, nil
+ @cpriv = RDoc::Constant.new 'CONST_PRIVATE', nil, nil
+
@vis = RDoc::NormalClass.new 'Vis'
@vis.add_method @pub
@vis.add_method @prot
@@ -884,11 +950,16 @@ class TestRDocContext < XrefTestCase
@vis.add_attribute @aprot
@vis.add_attribute @apriv
+ @vis.add_constant @cpub
+ @vis.add_constant @cpriv
+
@prot.visibility = :protected
@priv.visibility = :private
@aprot.visibility = :protected
@apriv.visibility = :private
+
+ @cpriv.visibility = :private
end
end
diff --git a/test/rdoc/test_rdoc_context_section.rb b/test/rdoc/test_rdoc_context_section.rb
index b008c60c57..f1cf493d3d 100644
--- a/test/rdoc/test_rdoc_context_section.rb
+++ b/test/rdoc/test_rdoc_context_section.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocContextSection < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_cross_reference.rb b/test/rdoc/test_rdoc_cross_reference.rb
index c39d6ad356..183de0930d 100644
--- a/test/rdoc/test_rdoc_cross_reference.rb
+++ b/test/rdoc/test_rdoc_cross_reference.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
class TestRDocCrossReference < XrefTestCase
@@ -107,18 +107,24 @@ class TestRDocCrossReference < XrefTestCase
end
def test_resolve_method
- assert_ref @c1__m, 'm'
- assert_ref @c1_m, '#m'
- assert_ref @c1__m, '::m'
-
- assert_ref @c1_m, 'C1#m'
- assert_ref @c1__m, 'C1.m'
- assert_ref @c1__m, 'C1::m'
+ assert_ref @c1__m, 'm'
+ assert_ref @c1__m, '::m'
+ assert_ref @c1_m, '#m'
+ assert_ref @c1_plus, '#+'
+
+ assert_ref @c1_m, 'C1#m'
+ assert_ref @c1_plus, 'C1#+'
+ assert_ref @c1__m, 'C1.m'
+ assert_ref @c1__m, 'C1::m'
assert_ref @c1_m, 'C1#m'
assert_ref @c1_m, 'C1#m()'
assert_ref @c1_m, 'C1#m(*)'
+ assert_ref @c1_plus, 'C1#+'
+ assert_ref @c1_plus, 'C1#+()'
+ assert_ref @c1_plus, 'C1#+(*)'
+
assert_ref @c1__m, 'C1.m'
assert_ref @c1__m, 'C1.m()'
assert_ref @c1__m, 'C1.m(*)'
@@ -139,6 +145,15 @@ class TestRDocCrossReference < XrefTestCase
assert_ref @c2_c3_m, '::C2::C3#m(*)'
end
+ def test_resolve_the_same_name_in_instance_and_class_method
+ assert_ref @c9_a_i_foo, 'C9::A#foo'
+ assert_ref @c9_a_c_bar, 'C9::A::bar'
+ assert_ref @c9_b_c_foo, 'C9::B::foo'
+ assert_ref @c9_b_i_bar, 'C9::B#bar'
+ assert_ref @c9_b_c_foo, 'C9::B.foo'
+ assert_ref @c9_a_c_bar, 'C9::B.bar'
+ end
+
def test_resolve_method_equals3
m = RDoc::AnyMethod.new '', '==='
@c1.add_method m
@@ -147,8 +162,7 @@ class TestRDocCrossReference < XrefTestCase
end
def test_resolve_page
- page = @store.add_file 'README.txt'
- page.parser = RDoc::Parser::Simple
+ page = @store.add_file 'README.txt', parser: RDoc::Parser::Simple
assert_ref page, 'README'
end
diff --git a/test/rdoc/test_rdoc_encoding.rb b/test/rdoc/test_rdoc_encoding.rb
index 915f3ce5ec..5b2de47aa2 100644
--- a/test/rdoc/test_rdoc_encoding.rb
+++ b/test/rdoc/test_rdoc_encoding.rb
@@ -1,7 +1,7 @@
# coding: US-ASCII
-# frozen_string_literal: false
+# frozen_string_literal: true
-require 'rdoc/test_case'
+require 'minitest_helper'
class TestRDocEncoding < RDoc::TestCase
@@ -31,13 +31,13 @@ class TestRDocEncoding < RDoc::TestCase
@tempfile.flush
contents = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
- assert_equal "hi everybody", contents
+ assert_equal "# coding: utf-8\nhi everybody", contents
assert_equal Encoding::UTF_8, contents.encoding
end
def test_class_read_file_encoding_convert
content = ""
- content.encode! 'ISO-8859-1'
+ content = RDoc::Encoding.change_encoding content, 'ISO-8859-1'
content << "# coding: ISO-8859-1\nhi \xE9verybody"
@tempfile.write content
@@ -45,7 +45,7 @@ class TestRDocEncoding < RDoc::TestCase
contents = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
assert_equal Encoding::UTF_8, contents.encoding
- assert_equal "hi \u00e9verybody", contents.sub("\r", '')
+ assert_equal "# coding: ISO-8859-1\nhi \u00e9verybody", contents.sub("\r", '')
end
def test_class_read_file_encoding_fail
@@ -65,13 +65,13 @@ class TestRDocEncoding < RDoc::TestCase
def test_class_read_file_encoding_fancy
expected = "# -*- coding: utf-8; fill-column: 74 -*-\nhi everybody"
- expected.encode! Encoding::UTF_8
+ expected = RDoc::Encoding.change_encoding expected, Encoding::UTF_8
@tempfile.write expected
@tempfile.flush
contents = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
- assert_equal "hi everybody", contents
+ assert_equal "# -*- coding: utf-8; fill-column: 74 -*-\nhi everybody", contents
assert_equal Encoding::UTF_8, contents.encoding
end
@@ -81,7 +81,7 @@ class TestRDocEncoding < RDoc::TestCase
contents = RDoc::Encoding.read_file @tempfile.path, Encoding::US_ASCII, true
- assert_equal '?', contents
+ assert_equal "# coding: utf-8\n?", contents
assert_equal Encoding::US_ASCII, contents.encoding
end
@@ -124,110 +124,58 @@ class TestRDocEncoding < RDoc::TestCase
contents = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
- expected = ":\xe3\x82\xb3\xe3\x83\x9e\xe3\x83\xb3\xe3\x83\x89:"
- expected.force_encoding Encoding::UTF_8
+ expected = "# coding: ISO-2022-JP\n:\xe3\x82\xb3\xe3\x83\x9e\xe3\x83\xb3\xe3\x83\x89:"
+ expected = RDoc::Encoding.change_encoding expected, Encoding::UTF_8
assert_equal expected, contents
assert_equal Encoding::UTF_8, contents.encoding
end
- def test_class_set_encoding
+ def test_class_detect_encoding
s = "# coding: UTF-8\n"
- RDoc::Encoding.set_encoding s
+ encoding = RDoc::Encoding.detect_encoding s
# sanity check for 1.8
- assert_equal Encoding::UTF_8, s.encoding
+ assert_equal Encoding::UTF_8, encoding
s = "#!/bin/ruby\n# coding: UTF-8\n"
- RDoc::Encoding.set_encoding s
+ encoding = RDoc::Encoding.detect_encoding s
- assert_equal Encoding::UTF_8, s.encoding
+ assert_equal Encoding::UTF_8, encoding
s = "<?xml version='1.0' encoding='UTF-8'?>\n"
- expected = s.encoding
- RDoc::Encoding.set_encoding s
+ encoding = RDoc::Encoding.detect_encoding s
- assert_equal Encoding::UTF_8, s.encoding
+ assert_equal Encoding::UTF_8, encoding
s = "<?xml version='1.0' encoding=\"UTF-8\"?>\n"
- expected = s.encoding
- RDoc::Encoding.set_encoding s
+ encoding = RDoc::Encoding.detect_encoding s
- assert_equal Encoding::UTF_8, s.encoding
- end
-
- def test_class_set_encoding_strip
- s = "# coding: UTF-8\n# more comments"
-
- RDoc::Encoding.set_encoding s
-
- assert_equal "# more comments", s
-
- s = "#!/bin/ruby\n# coding: UTF-8\n# more comments"
-
- RDoc::Encoding.set_encoding s
-
- assert_equal "#!/bin/ruby\n# more comments", s
+ assert_equal Encoding::UTF_8, encoding
end
def test_class_set_encoding_bad
s = ""
- expected = s.encoding
- RDoc::Encoding.set_encoding s
+ encoding = RDoc::Encoding.detect_encoding s
- assert_equal expected, s.encoding
+ assert_nil encoding
s = "# vim:set fileencoding=utf-8:\n"
- expected = s.encoding
- RDoc::Encoding.set_encoding s
+ encoding = RDoc::Encoding.detect_encoding s
- assert_equal expected, s.encoding
+ assert_nil encoding
s = "# vim:set fileencoding=utf-8:\n"
- expected = s.encoding
- RDoc::Encoding.set_encoding s
+ encoding = RDoc::Encoding.detect_encoding s
- assert_equal expected, s.encoding
+ assert_nil encoding
assert_raises ArgumentError do
- RDoc::Encoding.set_encoding "# -*- encoding: undecided -*-\n"
+ s = RDoc::Encoding.detect_encoding "# -*- encoding: undecided -*-\n"
end
end
- def test_skip_frozen_string_literal
- expected = "# frozen_string_literal: false\nhi everybody"
-
- @tempfile.write expected
- @tempfile.flush
-
- contents = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
- assert_equal "hi everybody", contents
- assert_equal Encoding::UTF_8, contents.encoding
- end
-
- def test_skip_frozen_string_literal_after_coding
- expected = "# coding: utf-8\n# frozen-string-literal: false\nhi everybody"
-
- @tempfile.write expected
- @tempfile.flush
-
- contents = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
- assert_equal "hi everybody", contents
- assert_equal Encoding::UTF_8, contents.encoding
- end
-
- def test_skip_frozen_string_literal_before_coding
- expected = "# frozen_string_literal: false\n# coding: utf-8\nhi everybody"
-
- @tempfile.write expected
- @tempfile.flush
-
- contents = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
- assert_equal "hi everybody", contents
- assert_equal Encoding::UTF_8, contents.encoding
- end
-
def test_sanity
assert_equal Encoding::US_ASCII, ''.encoding,
'If this file is not ASCII tests may incorrectly pass'
diff --git a/test/rdoc/test_rdoc_extend.rb b/test/rdoc/test_rdoc_extend.rb
index bc50a657ac..f4c8425864 100644
--- a/test/rdoc/test_rdoc_extend.rb
+++ b/test/rdoc/test_rdoc_extend.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
class TestRDocExtend < XrefTestCase
diff --git a/test/rdoc/test_rdoc_generator_darkfish.rb b/test/rdoc/test_rdoc_generator_darkfish.rb
index 6a3e7038e1..edabe4fad4 100644
--- a/test/rdoc/test_rdoc_generator_darkfish.rb
+++ b/test/rdoc/test_rdoc_generator_darkfish.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocGeneratorDarkfish < RDoc::TestCase
@@ -39,14 +39,17 @@ class TestRDocGeneratorDarkfish < RDoc::TestCase
@top_level.add_constant @alias_constant
- @klass.add_module_alias @klass, 'A', @top_level
+ @klass.add_module_alias @klass, @klass.name, @alias_constant, @top_level
@meth = RDoc::AnyMethod.new nil, 'method'
@meth_bang = RDoc::AnyMethod.new nil, 'method!'
+ @meth_with_html_tag_yield = RDoc::AnyMethod.new nil, 'method_with_html_tag_yield'
+ @meth_with_html_tag_yield.block_params = '%<<script>alert("atui")</script>>, yield_arg'
@attr = RDoc::Attr.new nil, 'attr', 'RW', ''
@klass.add_method @meth
@klass.add_method @meth_bang
+ @klass.add_method @meth_with_html_tag_yield
@klass.add_attribute @attr
@ignored = @top_level.add_class RDoc::NormalClass, 'Ignored'
@@ -132,7 +135,7 @@ class TestRDocGeneratorDarkfish < RDoc::TestCase
end
def test_install_rdoc_static_file
- src = Pathname(__FILE__)
+ src = Pathname File.expand_path(__FILE__, @pwd)
dst = File.join @tmpdir, File.basename(src)
options = {}
@@ -167,7 +170,7 @@ class TestRDocGeneratorDarkfish < RDoc::TestCase
assert_equal [@klass_alias, @ignored, @klass, @object],
@g.classes.sort_by { |klass| klass.full_name }
assert_equal [@top_level], @g.files
- assert_equal [@meth, @meth, @meth_bang, @meth_bang], @g.methods
+ assert_equal [@meth, @meth, @meth_bang, @meth_bang, @meth_with_html_tag_yield, @meth_with_html_tag_yield], @g.methods
assert_equal [@klass_alias, @klass, @object], @g.modsort
end
@@ -199,6 +202,24 @@ class TestRDocGeneratorDarkfish < RDoc::TestCase
assert_same template, @g.send(:template_for, partial)
end
+ def test_generated_method_with_html_tag_yield
+ top_level = @store.add_file 'file.rb'
+ top_level.add_class @klass.class, @klass.name
+
+ @g.generate
+
+ path = File.join @tmpdir, 'A.html'
+
+ f = open(path)
+ internal_file = f.read
+ method_name_index = internal_file.index('<span class="method-name">method_with_html_tag_yield</span>')
+ last_of_method_name_index = method_name_index + internal_file[method_name_index..-1].index('<div class="method-description">') - 1
+ method_name = internal_file[method_name_index..last_of_method_name_index]
+ f.close
+
+ assert_includes method_name, '{ |%&lt;&lt;script&gt;alert(&quot;atui&quot;)&lt;/script&gt;&gt;, yield_arg| ... }'
+ end
+
##
# Asserts that +filename+ has a link count greater than 1 if hard links to
# @tmpdir are supported.
diff --git a/test/rdoc/test_rdoc_generator_json_index.rb b/test/rdoc/test_rdoc_generator_json_index.rb
index 52c35a564d..ab8f6f1521 100644
--- a/test/rdoc/test_rdoc_generator_json_index.rb
+++ b/test/rdoc/test_rdoc_generator_json_index.rb
@@ -1,14 +1,14 @@
# coding: US-ASCII
-# frozen_string_literal: false
+# frozen_string_literal: true
-require 'rdoc/test_case'
+require 'minitest_helper'
class TestRDocGeneratorJsonIndex < RDoc::TestCase
def setup
super
- @tmpdir = File.join Dir.tmpdir, "test_rdoc_generator_darkfish_#{$$}"
+ @tmpdir = Dir.mktmpdir "test_rdoc_generator_darkfish_#{$$}_"
FileUtils.mkdir_p @tmpdir
@options = RDoc::Options.new
@@ -95,6 +95,19 @@ class TestRDocGeneratorJsonIndex < RDoc::TestCase
assert_file 'js/navigation.js'
assert_file 'js/search_index.js'
+ srcdir = File.expand_path('lib/rdoc', @pwd)
+ if !File.directory? srcdir
+ # for Ruby core repository
+ srcdir = File.expand_path("../../../lib/rdoc", __FILE__)
+ end
+
+ orig_file = Pathname(File.join srcdir, 'generator/template/json_index/js/navigation.js')
+ generated_file = Pathname(File.join @tmpdir, 'js/navigation.js')
+
+ # This is dirty hack on JRuby for MiniTest 4
+ assert orig_file.mtime.inspect == generated_file.mtime.inspect,
+ '.js files should be tha same timestamp of original'
+
json = File.read 'js/search_index.js'
json =~ /\Avar search_data = /
@@ -137,6 +150,20 @@ class TestRDocGeneratorJsonIndex < RDoc::TestCase
assert_equal expected, index
end
+ def test_generate_search_index_with_reproducible_builds
+ backup_epoch = ENV['SOURCE_DATE_EPOCH']
+ ruby_birthday = Time.parse 'Wed, 24 Feb 1993 21:00:00 +0900'
+ ENV['SOURCE_DATE_EPOCH'] = ruby_birthday.to_i.to_s
+
+ @g.generate
+
+ assert_file 'js/search_index.js'
+ generated_search_index = Pathname(File.join @tmpdir, 'js/search_index.js')
+ assert_equal ruby_birthday, generated_search_index.mtime
+
+ ENV['SOURCE_DATE_EPOCH'] = backup_epoch
+ end
+
def test_generate_gzipped
begin
require 'zlib'
@@ -199,7 +226,7 @@ class TestRDocGeneratorJsonIndex < RDoc::TestCase
def test_generate_utf_8
text = "5\xB0"
- text.force_encoding Encoding::ISO_8859_1
+ text = RDoc::Encoding.change_encoding text, Encoding::ISO_8859_1
@klass.add_comment comment(text), @top_level
@g.generate
@@ -215,7 +242,7 @@ class TestRDocGeneratorJsonIndex < RDoc::TestCase
klass_record = @klass.search_record[2..-1]
klass_record[-1] = "<p>5\xc2\xb0\n"
- klass_record.last.force_encoding Encoding::UTF_8
+ klass_record[-1] = RDoc::Encoding.change_encoding klass_record[-1], Encoding::UTF_8
info = [
klass_record,
diff --git a/test/rdoc/test_rdoc_generator_markup.rb b/test/rdoc/test_rdoc_generator_markup.rb
index d546c2f87f..5588d9c5f0 100644
--- a/test/rdoc/test_rdoc_generator_markup.rb
+++ b/test/rdoc/test_rdoc_generator_markup.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocGeneratorMarkup < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_generator_pot.rb b/test/rdoc/test_rdoc_generator_pot.rb
index 3f1bee70c9..26641345ef 100644
--- a/test/rdoc/test_rdoc_generator_pot.rb
+++ b/test/rdoc/test_rdoc_generator_pot.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocGeneratorPOT < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_generator_pot_po.rb b/test/rdoc/test_rdoc_generator_pot_po.rb
index ae60ff004e..c50899d446 100644
--- a/test/rdoc/test_rdoc_generator_pot_po.rb
+++ b/test/rdoc/test_rdoc_generator_pot_po.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocGeneratorPOTPO < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_generator_pot_po_entry.rb b/test/rdoc/test_rdoc_generator_pot_po_entry.rb
index 36b85df864..f3bf329348 100644
--- a/test/rdoc/test_rdoc_generator_pot_po_entry.rb
+++ b/test/rdoc/test_rdoc_generator_pot_po_entry.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocGeneratorPOTPOEntry < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_generator_ri.rb b/test/rdoc/test_rdoc_generator_ri.rb
index d9f7b7ebc6..a33f5ca2ac 100644
--- a/test/rdoc/test_rdoc_generator_ri.rb
+++ b/test/rdoc/test_rdoc_generator_ri.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocGeneratorRI < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_i18n_locale.rb b/test/rdoc/test_rdoc_i18n_locale.rb
index e9dce78472..c936a7219f 100644
--- a/test/rdoc/test_rdoc_i18n_locale.rb
+++ b/test/rdoc/test_rdoc_i18n_locale.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocI18nLocale < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_i18n_text.rb b/test/rdoc/test_rdoc_i18n_text.rb
index 61df193969..beb9f307a7 100644
--- a/test/rdoc/test_rdoc_i18n_text.rb
+++ b/test/rdoc/test_rdoc_i18n_text.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocI18nText < RDoc::TestCase
@@ -59,7 +59,7 @@ Paragraphe 2.
assert_equal expected, translate(raw)
end
- def test_translate_not_transalted_message
+ def test_translate_not_translated_message
nonexistent_paragraph = <<-PARAGRAPH.strip
Nonexistent paragraph.
PARAGRAPH
diff --git a/test/rdoc/test_rdoc_include.rb b/test/rdoc/test_rdoc_include.rb
index 6a34f5d4b8..67d3dfd88e 100644
--- a/test/rdoc/test_rdoc_include.rb
+++ b/test/rdoc/test_rdoc_include.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
class TestRDocInclude < XrefTestCase
diff --git a/test/rdoc/test_rdoc_markdown.rb b/test/rdoc/test_rdoc_markdown.rb
index 6c7bf4ae32..8b58150f8a 100644
--- a/test/rdoc/test_rdoc_markdown.rb
+++ b/test/rdoc/test_rdoc_markdown.rb
@@ -1,7 +1,7 @@
# coding: UTF-8
-# frozen_string_literal: false
+# frozen_string_literal: true
-require 'rdoc/test_case'
+require 'minitest_helper'
require 'rdoc/markup/block_quote'
require 'rdoc/markdown'
@@ -717,7 +717,7 @@ Some text. ^[With a footnote]
def test_parse_note_no_notes
@parser.notes = false
- assert_raises RuntimeError do # TODO use a real error
+ assert_raises RDoc::Markdown::ParseError do
parse "Some text.[^1]"
end
end
@@ -930,6 +930,35 @@ and an extra note.[^2]
assert_equal expected, doc
end
+ def test_parse_strike_tilde
+ doc = parse "it ~~works~~\n"
+
+ expected = @RM::Document.new(
+ @RM::Paragraph.new("it ~works~"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_strike_words_tilde
+ doc = parse "it ~~works fine~~\n"
+
+ expected = @RM::Document.new(
+ @RM::Paragraph.new("it <s>works fine</s>"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_strike_tilde_no
+ @parser.strike = false
+
+ doc = parse "it ~~works fine~~\n"
+
+ expected = @RM::Document.new(
+ @RM::Paragraph.new("it ~~works fine~~"))
+
+ assert_equal expected, doc
+ end
+
def test_parse_style
@parser.css = true
@@ -973,6 +1002,14 @@ and an extra note.[^2]
assert_equal '<b>_emphasis_</b>', @parser.strong('_emphasis_')
end
+ def test_code_fence_with_unintended_array
+ doc = parse '```<ruby>```'
+
+ expected = doc(verb('<ruby>'))
+
+ assert_equal expected, doc
+ end
+
def parse text
@parser.parse text
end
diff --git a/test/rdoc/test_rdoc_markdown_test.rb b/test/rdoc/test_rdoc_markdown_test.rb
index b80838131c..6445fb5e65 100644
--- a/test/rdoc/test_rdoc_markdown_test.rb
+++ b/test/rdoc/test_rdoc_markdown_test.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'minitest/autorun'
+# frozen_string_literal: true
+require 'minitest_helper'
require 'pp'
require 'rdoc'
@@ -601,7 +601,7 @@ foo
para("Unordered (bulleted) lists use asterisks, pluses, and hyphens (<code>*</code>,\n" +
"<code>+</code>, and <code>-</code>) as list markers. These three markers are\n" +
- "interchangable; this:"),
+ "interchangeable; this:"),
verb("* Candy.\n",
"* Gum.\n",
@@ -1090,7 +1090,7 @@ foo
para("Markdown supports ordered (numbered) and unordered (bulleted) lists."),
- para("Unordered lists use asterisks, pluses, and hyphens -- interchangably\n" +
+ para("Unordered lists use asterisks, pluses, and hyphens -- interchangeably\n" +
"-- as list markers:"),
verb("* Red\n",
diff --git a/test/rdoc/test_rdoc_markup.rb b/test/rdoc/test_rdoc_markup.rb
index ad13211f7b..2812a2994f 100644
--- a/test/rdoc/test_rdoc_markup.rb
+++ b/test/rdoc/test_rdoc_markup.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkup < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_markup_attribute_manager.rb b/test/rdoc/test_rdoc_markup_attribute_manager.rb
index c0f7666a01..d6eccdf76f 100644
--- a/test/rdoc/test_rdoc_markup_attribute_manager.rb
+++ b/test/rdoc/test_rdoc_markup_attribute_manager.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupAttributeManager < RDoc::TestCase
@@ -36,12 +36,12 @@ class TestRDocMarkupAttributeManager < RDoc::TestCase
end
def crossref(text)
- crossref_bitmap = @am.attributes.bitmap_for(:_SPECIAL_) |
+ crossref_bitmap = @am.attributes.bitmap_for(:_REGEXP_HANDLING_) |
@am.attributes.bitmap_for(:CROSSREF)
- [ @am.changed_attribute_by_name([], [:CROSSREF, :_SPECIAL_]),
- RDoc::Markup::Special.new(crossref_bitmap, text),
- @am.changed_attribute_by_name([:CROSSREF, :_SPECIAL_], [])
+ [ @am.changed_attribute_by_name([], [:CROSSREF, :_REGEXP_HANDLING_]),
+ RDoc::Markup::RegexpHandling.new(crossref_bitmap, text),
+ @am.changed_attribute_by_name([:CROSSREF, :_REGEXP_HANDLING_], [])
]
end
@@ -58,12 +58,12 @@ class TestRDocMarkupAttributeManager < RDoc::TestCase
assert(tags.has_key?("test"))
end
- def test_add_special
- @am.add_special "WikiWord", :WIKIWORD
- specials = @am.special
+ def test_add_regexp_handling
+ @am.add_regexp_handling "WikiWord", :WIKIWORD
+ regexp_handlings = @am.regexp_handlings
- assert_equal 1, specials.size
- assert specials.assoc "WikiWord"
+ assert_equal 1, regexp_handlings.size
+ assert regexp_handlings.assoc "WikiWord"
end
def test_add_word_pair
@@ -171,21 +171,21 @@ class TestRDocMarkupAttributeManager < RDoc::TestCase
end
def test_convert_attrs
- str = '+foo+'
+ str = '+foo+'.dup
attrs = RDoc::Markup::AttrSpan.new str.length
@am.convert_attrs str, attrs
assert_equal "\000foo\000", str
- str = '+:foo:+'
+ str = '+:foo:+'.dup
attrs = RDoc::Markup::AttrSpan.new str.length
@am.convert_attrs str, attrs
assert_equal "\000:foo:\000", str
- str = '+x-y+'
+ str = '+x-y+'.dup
attrs = RDoc::Markup::AttrSpan.new str.length
@am.convert_attrs str, attrs
@@ -299,17 +299,17 @@ class TestRDocMarkupAttributeManager < RDoc::TestCase
def @am.str() @str end
def @am.str=(str) @str = str end
- @am.str = '<code>foo</code>'
+ @am.str = '<code>foo</code>'.dup
@am.mask_protected_sequences
assert_equal "<code>foo</code>", @am.str
- @am.str = '<code>foo\\</code>'
+ @am.str = '<code>foo\\</code>'.dup
@am.mask_protected_sequences
assert_equal "<code>foo<\x04/code>", @am.str, 'escaped close'
- @am.str = '<code>foo\\\\</code>'
+ @am.str = '<code>foo\\\\</code>'.dup
@am.mask_protected_sequences
assert_equal "<code>foo\\</code>", @am.str, 'escaped backslash'
@@ -332,8 +332,16 @@ class TestRDocMarkupAttributeManager < RDoc::TestCase
@am.flow("\\_cat_<i>dog</i>"))
end
- def test_special
- @am.add_special(RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF)
+ def test_lost_tag_for_the_second_time
+ str = "cat <tt>dog</tt>"
+ assert_equal(["cat ", @tt_on, "dog", @tt_off],
+ @am.flow(str))
+ assert_equal(["cat ", @tt_on, "dog", @tt_off],
+ @am.flow(str))
+ end
+
+ def test_regexp_handling
+ @am.add_regexp_handling(RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF)
#
# The apostrophes in "cats'" and "dogs'" suppress the flagging of these
diff --git a/test/rdoc/test_rdoc_markup_attributes.rb b/test/rdoc/test_rdoc_markup_attributes.rb
index 077b6a5474..e592fa7145 100644
--- a/test/rdoc/test_rdoc_markup_attributes.rb
+++ b/test/rdoc/test_rdoc_markup_attributes.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupAttributes < RDoc::TestCase
@@ -19,10 +19,10 @@ class TestRDocMarkupAttributes < RDoc::TestCase
@as.bitmap_for 'two'
@as.bitmap_for 'three'
- assert_equal 'none', @as.as_string(0)
- assert_equal '_SPECIAL_', @as.as_string(1)
- assert_equal 'two', @as.as_string(2)
- assert_equal '_SPECIAL_,two', @as.as_string(3)
+ assert_equal 'none', @as.as_string(0)
+ assert_equal '_REGEXP_HANDLING_', @as.as_string(1)
+ assert_equal 'two', @as.as_string(2)
+ assert_equal '_REGEXP_HANDLING_,two', @as.as_string(3)
end
def test_each_name_of
diff --git a/test/rdoc/test_rdoc_markup_document.rb b/test/rdoc/test_rdoc_markup_document.rb
index e5a61daea5..8202916238 100644
--- a/test/rdoc/test_rdoc_markup_document.rb
+++ b/test/rdoc/test_rdoc_markup_document.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupDocument < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_markup_formatter.rb b/test/rdoc/test_rdoc_markup_formatter.rb
index 02a6844a3a..cdc5944cdf 100644
--- a/test/rdoc/test_rdoc_markup_formatter.rb
+++ b/test/rdoc/test_rdoc_markup_formatter.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupFormatter < RDoc::TestCase
@@ -12,15 +12,15 @@ class TestRDocMarkupFormatter < RDoc::TestCase
end
def accept_paragraph paragraph
- @res << attributes(paragraph.text)
+ @res += attributes(paragraph.text)
end
def attributes text
convert_flow @am.flow text.dup
end
- def handle_special_CAPS special
- "handled #{special.text}"
+ def handle_regexp_CAPS target
+ "handled #{target.text}"
end
def start_accepting
@@ -37,16 +37,16 @@ class TestRDocMarkupFormatter < RDoc::TestCase
super
@markup = @RM.new
- @markup.add_special(/[A-Z]+/, :CAPS)
+ @markup.add_regexp_handling(/[A-Z]+/, :CAPS)
@attribute_manager = @markup.attribute_manager
@attributes = @attribute_manager.attributes
@to = ToTest.new @markup
- @caps = @attributes.bitmap_for :CAPS
- @special = @attributes.bitmap_for :_SPECIAL_
- @tt = @attributes.bitmap_for :TT
+ @caps = @attributes.bitmap_for :CAPS
+ @regexp_handling = @attributes.bitmap_for :_REGEXP_HANDLING_
+ @tt = @attributes.bitmap_for :TT
end
def test_class_gen_relative_url
@@ -62,19 +62,19 @@ class TestRDocMarkupFormatter < RDoc::TestCase
assert_equal 'a/c.html', gen('a.html', 'a/c.html')
end
- def special_names
- @attribute_manager.special.map do |_, mask|
+ def regexp_handling_names
+ @attribute_manager.regexp_handlings.map do |_, mask|
@attributes.as_string mask
end
end
- def test_add_special_RDOCLINK
- @to.add_special_RDOCLINK
+ def test_add_regexp_handling_RDOCLINK
+ @to.add_regexp_handling_RDOCLINK
- assert_includes special_names, 'RDOCLINK'
+ assert_includes regexp_handling_names, 'RDOCLINK'
- def @to.handle_special_RDOCLINK special
- "<#{special.text}>"
+ def @to.handle_regexp_RDOCLINK target
+ "<#{target.text}>"
end
document = doc(para('{foo}[rdoc-label:bar].'))
@@ -84,13 +84,13 @@ class TestRDocMarkupFormatter < RDoc::TestCase
assert_equal '{foo}[<rdoc-label:bar>].', formatted
end
- def test_add_special_TIDYLINK
- @to.add_special_TIDYLINK
+ def test_add_regexp_handling_TIDYLINK
+ @to.add_regexp_handling_TIDYLINK
- assert_includes special_names, 'TIDYLINK'
+ assert_includes regexp_handling_names, 'TIDYLINK'
- def @to.handle_special_TIDYLINK special
- "<#{special.text}>"
+ def @to.handle_regexp_TIDYLINK target
+ "<#{target.text}>"
end
document = doc(para('foo[rdoc-label:bar].'))
@@ -111,15 +111,15 @@ class TestRDocMarkupFormatter < RDoc::TestCase
assert_equal 'http', scheme
assert_equal 'example/foo', url
- assert_equal nil, id
+ assert_nil id
end
def test_parse_url_anchor
scheme, url, id = @to.parse_url '#foottext-1'
- assert_equal nil, scheme
+ assert_nil scheme
assert_equal '#foottext-1', url
- assert_equal nil, id
+ assert_nil id
end
def test_parse_url_link
@@ -127,7 +127,7 @@ class TestRDocMarkupFormatter < RDoc::TestCase
assert_equal 'link', scheme
assert_equal 'README.txt', url
- assert_equal nil, id
+ assert_nil id
end
def test_parse_url_link_id
@@ -135,7 +135,7 @@ class TestRDocMarkupFormatter < RDoc::TestCase
assert_equal 'link', scheme
assert_equal 'README.txt#label-foo', url
- assert_equal nil, id
+ assert_nil id
end
def test_parse_url_rdoc_label
@@ -143,7 +143,7 @@ class TestRDocMarkupFormatter < RDoc::TestCase
assert_equal 'link', scheme
assert_equal '#foo', url
- assert_equal nil, id
+ assert_nil id
scheme, url, id = @to.parse_url 'rdoc-label:foo:bar'
@@ -157,20 +157,19 @@ class TestRDocMarkupFormatter < RDoc::TestCase
assert_equal 'http', scheme
assert_equal 'http://example/foo', url
- assert_equal nil, id
+ assert_nil id
scheme, url, id = @to.parse_url 'https://example/foo'
assert_equal 'https', scheme
assert_equal 'https://example/foo', url
- assert_equal nil, id
+ assert_nil id
end
- def test_convert_tt_special
+ def test_convert_tt_regexp_handling
converted = @to.convert '<code>AAA</code>'
assert_equal '<code>AAA</code>', converted
end
end
-
diff --git a/test/rdoc/test_rdoc_markup_hard_break.rb b/test/rdoc/test_rdoc_markup_hard_break.rb
index 5d2d359646..1d6c8927b5 100644
--- a/test/rdoc/test_rdoc_markup_hard_break.rb
+++ b/test/rdoc/test_rdoc_markup_hard_break.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupHardBreak < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_markup_heading.rb b/test/rdoc/test_rdoc_markup_heading.rb
index 463df7ef43..4508561a79 100644
--- a/test/rdoc/test_rdoc_markup_heading.rb
+++ b/test/rdoc/test_rdoc_markup_heading.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupHeading < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_markup_include.rb b/test/rdoc/test_rdoc_markup_include.rb
index e72a0ac1bb..6b2d570286 100644
--- a/test/rdoc/test_rdoc_markup_include.rb
+++ b/test/rdoc/test_rdoc_markup_include.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupInclude < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_markup_indented_paragraph.rb b/test/rdoc/test_rdoc_markup_indented_paragraph.rb
index efcd840453..06dcb25357 100644
--- a/test/rdoc/test_rdoc_markup_indented_paragraph.rb
+++ b/test/rdoc/test_rdoc_markup_indented_paragraph.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupIndentedParagraph < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_markup_paragraph.rb b/test/rdoc/test_rdoc_markup_paragraph.rb
index 6da6658c6f..00e4320119 100644
--- a/test/rdoc/test_rdoc_markup_paragraph.rb
+++ b/test/rdoc/test_rdoc_markup_paragraph.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupParagraph < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_markup_parser.rb b/test/rdoc/test_rdoc_markup_parser.rb
index 849d19f8c7..344d67df39 100644
--- a/test/rdoc/test_rdoc_markup_parser.rb
+++ b/test/rdoc/test_rdoc_markup_parser.rb
@@ -1,7 +1,7 @@
# coding: utf-8
-# frozen_string_literal: false
+# frozen_string_literal: true
-require 'rdoc/test_case'
+require 'minitest_helper'
class TestRDocMarkupParser < RDoc::TestCase
@@ -1045,6 +1045,41 @@ the time
assert_equal expected, @RMP.parse(" 1\n 2\n\n 3").parts
end
+ def test_parse_block_quote
+ expected = [
+ @RM::BlockQuote.new(@RM::Paragraph.new("foo"))
+ ]
+ assert_equal expected, @RMP.parse(<<-DOC).parts
+>>>
+ foo
+ DOC
+
+ expected = [
+ @RM::BlockQuote.new(@RM::Paragraph.new("foo"),
+ @RM::Verbatim.new("code\n"),
+ @RM::Paragraph.new("bar"))
+ ]
+ assert_equal expected, @RMP.parse(<<-DOC).parts
+>>>
+ foo
+ code
+ bar
+ DOC
+
+ expected = [
+ @RM::BlockQuote.new(@RM::Paragraph.new("foo"),
+ @RM::BlockQuote.new(@RM::Paragraph.new("bar")),
+ @RM::Paragraph.new("zot"))
+ ]
+ assert_equal expected, @RMP.parse(<<-DOC).parts
+>>>
+ foo
+ >>>
+ bar
+ zot
+ DOC
+ end
+
def test_peek_token
parser = util_parser
@@ -1068,7 +1103,7 @@ the time
assert_equal [:NEWLINE, "\n", 9, 0], parser.peek_token
- assert_equal nil, parser.skip(:NONE, false)
+ assert_nil parser.skip(:NONE, false)
assert_equal [:NEWLINE, "\n", 9, 0], parser.peek_token
end
diff --git a/test/rdoc/test_rdoc_markup_pre_process.rb b/test/rdoc/test_rdoc_markup_pre_process.rb
index ceb411c745..60ec75c19e 100644
--- a/test/rdoc/test_rdoc_markup_pre_process.rb
+++ b/test/rdoc/test_rdoc_markup_pre_process.rb
@@ -1,7 +1,6 @@
-# coding: utf-8
-# frozen_string_literal: false
+# frozen_string_literal: true
-require 'rdoc/test_case'
+require 'minitest_helper'
class TestRDocMarkupPreProcess < RDoc::TestCase
@@ -30,7 +29,8 @@ class TestRDocMarkupPreProcess < RDoc::TestCase
def test_class_post_process
RDoc::Markup::PreProcess.post_process do end
- assert_equal 1, RDoc::Markup::PreProcess.post_processors.length
+ assert_equal 1, RDoc::Markup::PreProcess.post_processors.length,
+ proc{RDoc::Markup::PreProcess.post_processors.inspect}
end
def test_include_file
@@ -87,8 +87,7 @@ contents of a string.
text = "# :main: M\n"
out = @pp.handle text
- assert_same out, text
- assert_equal "#\n", text
+ assert_equal "#\n", out
end
def test_handle_comment
@@ -97,8 +96,7 @@ contents of a string.
out = @pp.handle c
- assert_same out, text
- assert_equal "#\n", text
+ assert_equal "#\n", out
end
def test_handle_markup
@@ -130,8 +128,7 @@ contents of a string.
out = @pp.handle text, cd
- assert_same out, text
- assert_equal "# a b c\n", text
+ assert_equal "# a b c\n", out
assert_equal "# a b c\n", cd.metadata[:stuff]
end
@@ -139,8 +136,7 @@ contents of a string.
text = "# :x: y\n"
out = @pp.handle text
- assert_same out, text
- assert_equal "# :x: y\n", text
+ assert_equal text, out
end
def test_handle_directive_blankline
diff --git a/test/rdoc/test_rdoc_markup_raw.rb b/test/rdoc/test_rdoc_markup_raw.rb
index 1453333b9e..95de62208a 100644
--- a/test/rdoc/test_rdoc_markup_raw.rb
+++ b/test/rdoc/test_rdoc_markup_raw.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupRaw < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_markup_to_ansi.rb b/test/rdoc/test_rdoc_markup_to_ansi.rb
index 8bd49271c1..4c312e7972 100644
--- a/test/rdoc/test_rdoc_markup_to_ansi.rb
+++ b/test/rdoc/test_rdoc_markup_to_ansi.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupToAnsi < RDoc::Markup::TextFormatterTestCase
diff --git a/test/rdoc/test_rdoc_markup_to_bs.rb b/test/rdoc/test_rdoc_markup_to_bs.rb
index 153a121d78..f269790357 100644
--- a/test/rdoc/test_rdoc_markup_to_bs.rb
+++ b/test/rdoc/test_rdoc_markup_to_bs.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupToBs < RDoc::Markup::TextFormatterTestCase
diff --git a/test/rdoc/test_rdoc_markup_to_html.rb b/test/rdoc/test_rdoc_markup_to_html.rb
index 2e5b4b9300..c30c89a7e3 100644
--- a/test/rdoc/test_rdoc_markup_to_html.rb
+++ b/test/rdoc/test_rdoc_markup_to_html.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase
@@ -395,7 +395,7 @@ class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase
@to.accept_paragraph para("hello\n", "world\n")
- assert_equal "\n<p>hello world</p>\n", @to.res.join
+ assert_equal "\n<p>hello world </p>\n", @to.res.join
end
def test_accept_heading_output_decoration
@@ -451,6 +451,22 @@ class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase
assert_equal expected, @to.res.join
end
+ def test_accept_verbatim_nl_after_backslash
+ verb = @RM::Verbatim.new("a = 1 if first_flag_var and \\\n", " this_is_flag_var\n")
+
+ @to.start_accepting
+ @to.accept_verbatim verb
+
+ expected = <<-EXPECTED
+
+<pre class="ruby"><span class="ruby-identifier">a</span> = <span class="ruby-value">1</span> <span class="ruby-keyword">if</span> <span class="ruby-identifier">first_flag_var</span> <span class="ruby-keyword">and</span> \\
+ <span class="ruby-identifier">this_is_flag_var</span>
+</pre>
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ end
+
def test_accept_verbatim_pipe
@options.pipe = true
@@ -469,6 +485,106 @@ class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase
assert_equal expected, @to.res.join
end
+ def test_accept_verbatim_escape_in_string
+ code = <<-'RUBY'
+def foo
+ [
+ '\\',
+ '\'',
+ "'",
+ "\'\"\`",
+ "\#",
+ "\#{}",
+ "#",
+ "#{}",
+ /'"/,
+ /\'\"/,
+ /\//,
+ /\\/,
+ /\#/,
+ /\#{}/,
+ /#/,
+ /#{}/
+ ]
+end
+def bar
+end
+ RUBY
+ verb = @RM::Verbatim.new(*code.split(/(?<=\n)/))
+
+ @to.start_accepting
+ @to.accept_verbatim verb
+
+ expected = <<-'EXPECTED'
+
+<pre class="ruby"><span class="ruby-keyword">def</span> <span class="ruby-identifier ruby-title">foo</span>
+ [
+ <span class="ruby-string">&#39;\\&#39;</span>,
+ <span class="ruby-string">&#39;\&#39;&#39;</span>,
+ <span class="ruby-string">&quot;&#39;&quot;</span>,
+ <span class="ruby-string">&quot;\&#39;\&quot;\`&quot;</span>,
+ <span class="ruby-string">&quot;\#&quot;</span>,
+ <span class="ruby-string">&quot;\#{}&quot;</span>,
+ <span class="ruby-string">&quot;#&quot;</span>,
+ <span class="ruby-node">&quot;#{}&quot;</span>,
+ <span class="ruby-regexp">/&#39;&quot;/</span>,
+ <span class="ruby-regexp">/\&#39;\&quot;/</span>,
+ <span class="ruby-regexp">/\//</span>,
+ <span class="ruby-regexp">/\\/</span>,
+ <span class="ruby-regexp">/\#/</span>,
+ <span class="ruby-regexp">/\#{}/</span>,
+ <span class="ruby-regexp">/#/</span>,
+ <span class="ruby-regexp">/#{}/</span>
+ ]
+<span class="ruby-keyword">end</span>
+<span class="ruby-keyword">def</span> <span class="ruby-identifier ruby-title">bar</span>
+<span class="ruby-keyword">end</span>
+</pre>
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ end
+
+ def test_accept_verbatim_escape_in_backtick
+ code = <<-'RUBY'
+def foo
+ [
+ `\\`,
+ `\'\"\``,
+ `\#`,
+ `\#{}`,
+ `#`,
+ `#{}`
+ ]
+end
+def bar
+end
+ RUBY
+ verb = @RM::Verbatim.new(*code.split(/(?<=\n)/))
+
+ @to.start_accepting
+ @to.accept_verbatim verb
+
+ expected = <<-'EXPECTED'
+
+<pre class="ruby"><span class="ruby-keyword">def</span> <span class="ruby-identifier ruby-title">foo</span>
+ [
+ <span class="ruby-string">`\\`</span>,
+ <span class="ruby-string">`\&#39;\&quot;\``</span>,
+ <span class="ruby-string">`\#`</span>,
+ <span class="ruby-string">`\#{}`</span>,
+ <span class="ruby-string">`#`</span>,
+ <span class="ruby-node">`#{}`</span>
+ ]
+<span class="ruby-keyword">end</span>
+<span class="ruby-keyword">def</span> <span class="ruby-identifier ruby-title">bar</span>
+<span class="ruby-keyword">end</span>
+</pre>
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ end
+
def test_accept_verbatim_ruby
verb = @RM::Verbatim.new("1 + 1\n")
verb.format = :ruby
@@ -485,6 +601,36 @@ class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase
assert_equal expected, @to.res.join
end
+ def test_accept_verbatim_redefinable_operators
+ functions = %w[| ^ & <=> == === =~ > >= < <= << >> + - * / % ** ~ +@ -@ [] []= ` ! != !~].map { |redefinable_op|
+ ["def #{redefinable_op}\n", "end\n"]
+ }.flatten
+
+ verb = @RM::Verbatim.new(*functions)
+
+ @to.start_accepting
+ @to.accept_verbatim verb
+
+ expected = <<-EXPECTED
+
+<pre class="ruby">
+ EXPECTED
+ expected = expected.rstrip
+
+ %w[| ^ &amp; &lt;=&gt; == === =~ &gt; &gt;= &lt; &lt;= &lt;&lt; &gt;&gt; + - * / % ** ~ +@ -@ [] []= ` ! != !~].each do |html_escaped_op|
+ expected += <<-EXPECTED
+<span class="ruby-keyword">def</span> <span class="ruby-identifier ruby-title">#{html_escaped_op}</span>
+<span class="ruby-keyword">end</span>
+ EXPECTED
+ end
+
+ expected += <<-EXPECTED
+</pre>
+EXPECTED
+
+ assert_equal expected, @to.res.join
+ end
+
def test_convert_string
assert_equal '&lt;&gt;', @to.convert_string('<>')
end
@@ -581,18 +727,18 @@ class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase
assert_equal '<img src="https://example.com/image.png" />', @to.gen_url('https://example.com/image.png', 'ignored')
end
- def test_handle_special_HYPERLINK_link
- special = RDoc::Markup::Special.new 0, 'link:README.txt'
+ def test_handle_regexp_HYPERLINK_link
+ target = RDoc::Markup::RegexpHandling.new 0, 'link:README.txt'
- link = @to.handle_special_HYPERLINK special
+ link = @to.handle_regexp_HYPERLINK target
assert_equal '<a href="README.txt">README.txt</a>', link
end
- def test_handle_special_HYPERLINK_irc
- special = RDoc::Markup::Special.new 0, 'irc://irc.freenode.net/#ruby-lang'
+ def test_handle_regexp_HYPERLINK_irc
+ target = RDoc::Markup::RegexpHandling.new 0, 'irc://irc.freenode.net/#ruby-lang'
- link = @to.handle_special_HYPERLINK special
+ link = @to.handle_regexp_HYPERLINK target
assert_equal '<a href="irc://irc.freenode.net/#ruby-lang">irc.freenode.net/#ruby-lang</a>', link
end
diff --git a/test/rdoc/test_rdoc_markup_to_html_crossref.rb b/test/rdoc/test_rdoc_markup_to_html_crossref.rb
index b9b5629554..598bae3d3f 100644
--- a/test/rdoc/test_rdoc_markup_to_html_crossref.rb
+++ b/test/rdoc/test_rdoc_markup_to_html_crossref.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
class TestRDocMarkupToHtmlCrossref < XrefTestCase
@@ -14,26 +14,26 @@ class TestRDocMarkupToHtmlCrossref < XrefTestCase
def test_convert_CROSSREF
result = @to.convert 'C1'
- assert_equal para("<a href=\"C1.html\">C1</a>"), result
+ assert_equal para("<a href=\"C1.html\"><code>C1</code></a>"), result
end
def test_convert_CROSSREF_label
result = @to.convert 'C1@foo'
- assert_equal para("<a href=\"C1.html#label-foo\">foo at C1</a>"), result
+ assert_equal para("<a href=\"C1.html#label-foo\">foo at <code>C1</code></a>"), result
result = @to.convert 'C1#m@foo'
- assert_equal para("<a href=\"C1.html#method-i-m-label-foo\">foo at C1#m</a>"),
+ assert_equal para("<a href=\"C1.html#method-i-m-label-foo\">foo at <code>C1#m</code></a>"),
result
end
def test_convert_CROSSREF_label_period
result = @to.convert 'C1@foo.'
- assert_equal para("<a href=\"C1.html#label-foo\">foo at C1</a>."), result
+ assert_equal para("<a href=\"C1.html#label-foo\">foo at <code>C1</code></a>."), result
end
def test_convert_CROSSREF_label_space
result = @to.convert 'C1@foo+bar'
- assert_equal para("<a href=\"C1.html#label-foo+bar\">foo bar at C1</a>"),
+ assert_equal para("<a href=\"C1.html#label-foo+bar\">foo bar at <code>C1</code></a>"),
result
end
@@ -41,31 +41,31 @@ class TestRDocMarkupToHtmlCrossref < XrefTestCase
@c1.add_section 'Section'
result = @to.convert 'C1@Section'
- assert_equal para("<a href=\"C1.html#Section\">Section at C1</a>"), result
+ assert_equal para("<a href=\"C1.html#Section\">Section at <code>C1</code></a>"), result
end
def test_convert_CROSSREF_constant
result = @to.convert 'C1::CONST'
- assert_equal para("<a href=\"C1.html#CONST\">C1::CONST</a>"), result
+ assert_equal para("<a href=\"C1.html#CONST\"><code>C1::CONST</code></a>"), result
end
def test_convert_RDOCLINK_rdoc_ref
result = @to.convert 'rdoc-ref:C1'
- assert_equal para("<a href=\"C1.html\">C1</a>"), result
+ assert_equal para("<a href=\"C1.html\"><code>C1</code></a>"), result
end
def test_convert_RDOCLINK_rdoc_ref_method
result = @to.convert 'rdoc-ref:C1#m'
- assert_equal para("<a href=\"C1.html#method-i-m\">#m</a>"), result
+ assert_equal para("<a href=\"C1.html#method-i-m\"><code>C1#m</code></a>"), result
end
def test_convert_RDOCLINK_rdoc_ref_method_label
result = @to.convert 'rdoc-ref:C1#m@foo'
- assert_equal para("<a href=\"C1.html#method-i-m-label-foo\">foo at C1#m</a>"),
+ assert_equal para("<a href=\"C1.html#method-i-m-label-foo\">foo at <code>C1#m</code></a>"),
result, 'rdoc-ref:C1#m@foo'
end
@@ -75,13 +75,13 @@ class TestRDocMarkupToHtmlCrossref < XrefTestCase
result = @to.convert 'rdoc-ref:C1#%'
- assert_equal para("<a href=\"C1.html#method-i-25\">#%</a>"), result
+ assert_equal para("<a href=\"C1.html#method-i-25\"><code>C1#%</code></a>"), result
m.singleton = true
result = @to.convert 'rdoc-ref:C1::%'
- assert_equal para("<a href=\"C1.html#method-c-25\">::%</a>"), result
+ assert_equal para("<a href=\"C1.html#method-c-25\"><code>C1::%</code></a>"), result
end
def test_convert_RDOCLINK_rdoc_ref_method_percent_label
@@ -90,21 +90,21 @@ class TestRDocMarkupToHtmlCrossref < XrefTestCase
result = @to.convert 'rdoc-ref:C1#%@f'
- assert_equal para("<a href=\"C1.html#method-i-25-label-f\">f at C1#%</a>"),
+ assert_equal para("<a href=\"C1.html#method-i-25-label-f\">f at <code>C1#%</code></a>"),
result
m.singleton = true
result = @to.convert 'rdoc-ref:C1::%@f'
- assert_equal para("<a href=\"C1.html#method-c-25-label-f\">f at C1::%</a>"),
+ assert_equal para("<a href=\"C1.html#method-c-25-label-f\">f at <code>C1::%</code></a>"),
result
end
def test_convert_RDOCLINK_rdoc_ref_label
result = @to.convert 'rdoc-ref:C1@foo'
- assert_equal para("<a href=\"C1.html#label-foo\">foo at C1</a>"), result,
+ assert_equal para("<a href=\"C1.html#label-foo\">foo at <code>C1</code></a>"), result,
'rdoc-ref:C1@foo'
end
@@ -116,66 +116,66 @@ class TestRDocMarkupToHtmlCrossref < XrefTestCase
@to.gen_url('http://example', 'HTTP example')
end
- def test_handle_special_CROSSREF
- assert_equal "<a href=\"C2/C3.html\">C2::C3</a>", SPECIAL('C2::C3')
+ def test_handle_regexp_CROSSREF
+ assert_equal "<a href=\"C2/C3.html\"><code>C2::C3</code></a>", REGEXP_HANDLING('C2::C3')
end
- def test_handle_special_CROSSREF_label
- assert_equal "<a href=\"C1.html#method-i-m-label-foo\">foo at C1#m</a>",
- SPECIAL('C1#m@foo')
+ def test_handle_regexp_CROSSREF_label
+ assert_equal "<a href=\"C1.html#method-i-m-label-foo\">foo at <code>C1#m</code></a>",
+ REGEXP_HANDLING('C1#m@foo')
end
- def test_handle_special_CROSSREF_show_hash_false
+ def test_handle_regexp_CROSSREF_show_hash_false
@to.show_hash = false
- assert_equal "<a href=\"C1.html#method-i-m\">m</a>",
- SPECIAL('#m')
+ assert_equal "<a href=\"C1.html#method-i-m\"><code>m</code></a>",
+ REGEXP_HANDLING('#m')
end
- def test_handle_special_HYPERLINK_rdoc
+ def test_handle_regexp_HYPERLINK_rdoc
readme = @store.add_file 'README.txt'
readme.parser = RDoc::Parser::Simple
@to = RDoc::Markup::ToHtmlCrossref.new @options, 'C2.html', @c2
- link = @to.handle_special_HYPERLINK hyper 'C2::C3'
+ link = @to.handle_regexp_HYPERLINK hyper 'C2::C3'
- assert_equal '<a href="C2/C3.html">C2::C3</a>', link
+ assert_equal '<a href="C2/C3.html"><code>C2::C3</code></a>', link
- link = @to.handle_special_HYPERLINK hyper 'C4'
+ link = @to.handle_regexp_HYPERLINK hyper 'C4'
- assert_equal '<a href="C4.html">C4</a>', link
+ assert_equal '<a href="C4.html"><code>C4</code></a>', link
- link = @to.handle_special_HYPERLINK hyper 'README.txt'
+ link = @to.handle_regexp_HYPERLINK hyper 'README.txt'
assert_equal '<a href="README_txt.html">README.txt</a>', link
end
- def test_handle_special_TIDYLINK_rdoc
+ def test_handle_regexp_TIDYLINK_rdoc
readme = @store.add_file 'README.txt'
readme.parser = RDoc::Parser::Simple
@to = RDoc::Markup::ToHtmlCrossref.new @options, 'C2.html', @c2
- link = @to.handle_special_TIDYLINK tidy 'C2::C3'
+ link = @to.handle_regexp_TIDYLINK tidy 'C2::C3'
assert_equal '<a href="C2/C3.html">tidy</a>', link
- link = @to.handle_special_TIDYLINK tidy 'C4'
+ link = @to.handle_regexp_TIDYLINK tidy 'C4'
assert_equal '<a href="C4.html">tidy</a>', link
- link = @to.handle_special_TIDYLINK tidy 'C1#m'
+ link = @to.handle_regexp_TIDYLINK tidy 'C1#m'
assert_equal '<a href="C1.html#method-i-m">tidy</a>', link
- link = @to.handle_special_TIDYLINK tidy 'README.txt'
+ link = @to.handle_regexp_TIDYLINK tidy 'README.txt'
assert_equal '<a href="README_txt.html">tidy</a>', link
end
- def test_handle_special_TIDYLINK_label
- link = @to.handle_special_TIDYLINK tidy 'C1#m@foo'
+ def test_handle_regexp_TIDYLINK_label
+ link = @to.handle_regexp_TIDYLINK tidy 'C1#m@foo'
assert_equal "<a href=\"C1.html#method-i-m-label-foo\">tidy</a>",
link, 'C1#m@foo'
@@ -200,11 +200,16 @@ class TestRDocMarkupToHtmlCrossref < XrefTestCase
def test_link
assert_equal 'n', @to.link('n', 'n')
- assert_equal '<a href="C1.html#method-c-m">::m</a>', @to.link('m', 'm')
+ assert_equal '<a href="C1.html#method-c-m"><code>m</code></a>', @to.link('m', 'm')
+ end
+
+ def test_link_for_method_traverse
+ @to = RDoc::Markup::ToHtmlCrossref.new @options, 'C2.html', @c9
+ assert_equal '<a href="C9/A.html#method-i-foo"><code>C9::B#foo</code></a>', @to.link('C9::B#foo', 'C9::B#foo')
end
def test_link_class_method_full
- assert_equal '<a href="Parent.html#method-c-m">Parent.m</a>',
+ assert_equal '<a href="Parent.html#method-c-m"><code>Parent::m</code></a>',
@to.link('Parent::m', 'Parent::m')
end
@@ -212,20 +217,20 @@ class TestRDocMarkupToHtmlCrossref < XrefTestCase
"\n<p>#{text}</p>\n"
end
- def SPECIAL text
- @to.handle_special_CROSSREF special text
+ def REGEXP_HANDLING text
+ @to.handle_regexp_CROSSREF regexp_handling text
end
def hyper reference
- RDoc::Markup::Special.new 0, "rdoc-ref:#{reference}"
+ RDoc::Markup::RegexpHandling.new 0, "rdoc-ref:#{reference}"
end
- def special text
- RDoc::Markup::Special.new 0, text
+ def regexp_handling text
+ RDoc::Markup::RegexpHandling.new 0, text
end
def tidy reference
- RDoc::Markup::Special.new 0, "{tidy}[rdoc-ref:#{reference}]"
+ RDoc::Markup::RegexpHandling.new 0, "{tidy}[rdoc-ref:#{reference}]"
end
end
diff --git a/test/rdoc/test_rdoc_markup_to_html_snippet.rb b/test/rdoc/test_rdoc_markup_to_html_snippet.rb
index d180f551c9..7e01413dda 100644
--- a/test/rdoc/test_rdoc_markup_to_html_snippet.rb
+++ b/test/rdoc/test_rdoc_markup_to_html_snippet.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupToHtmlSnippet < RDoc::Markup::FormatterTestCase
@@ -458,8 +458,7 @@ So there you have it
expected = <<-EXPECTED
<p>Hello
-<p>This is some text, it <strong>will</strong> be cut off after 100 characters
-and an ellipsis must follow
+<p>This is some text, it <strong>will</strong> be cut off after 100 characters and an ellipsis must follow
<p>So there you #{@ellipsis}
EXPECTED
@@ -494,9 +493,9 @@ be guessed, raises an error if +name+ couldn't be guessed.
rdoc = <<-RDOC
= \RDoc - Ruby Documentation System
-* {RDoc Project Page}[https://github.com/rdoc/rdoc/]
-* {RDoc Documentation}[https://rdoc.github.io/rdoc]
-* {RDoc Bug Tracker}[https://github.com/rdoc/rdoc/issues]
+* {RDoc Project Page}[https://github.com/ruby/rdoc]
+* {RDoc Documentation}[https://ruby.github.io/rdoc]
+* {RDoc Bug Tracker}[https://github.com/ruby/rdoc/issues]
== DESCRIPTION:
@@ -563,8 +562,7 @@ NOTE: Given Foo::Bar, Bar is considered a class even though it may be a
RDOC
expected = <<-EXPECTED
-<p>Extracts the class, selector and method name parts from <code>name</code>
-like Foo::Bar#baz.
+<p>Extracts the class, selector and method name parts from <code>name</code> like Foo::Bar#baz.
<p>NOTE: Given Foo::Bar, #{@ellipsis}
EXPECTED
@@ -604,7 +602,7 @@ This routine modifies its +comment+ parameter.
rdoc = "* text\n" * 2
expected = "<p>text\n"
- expected.chomp!
+ expected = expected.chomp
expected << " #{@ellipsis}\n"
actual = @to.convert rdoc
@@ -652,10 +650,10 @@ This routine modifies its +comment+ parameter.
assert_equal 3, @to.characters
end
- def test_handle_special_HYPERLINK_link
- special = RDoc::Markup::Special.new 0, 'link:README.txt'
+ def test_handle_regexp_HYPERLINK_link
+ target = RDoc::Markup::RegexpHandling.new 0, 'link:README.txt'
- link = @to.handle_special_HYPERLINK special
+ link = @to.handle_regexp_HYPERLINK target
assert_equal 'README.txt', link
end
diff --git a/test/rdoc/test_rdoc_markup_to_joined_paragraph.rb b/test/rdoc/test_rdoc_markup_to_joined_paragraph.rb
index 414b7d3732..b4eed5f601 100644
--- a/test/rdoc/test_rdoc_markup_to_joined_paragraph.rb
+++ b/test/rdoc/test_rdoc_markup_to_joined_paragraph.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupToJoinedParagraph < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_markup_to_label.rb b/test/rdoc/test_rdoc_markup_to_label.rb
index 35df69c2ac..b869745528 100644
--- a/test/rdoc/test_rdoc_markup_to_label.rb
+++ b/test/rdoc/test_rdoc_markup_to_label.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupToLabel < RDoc::Markup::FormatterTestCase
diff --git a/test/rdoc/test_rdoc_markup_to_markdown.rb b/test/rdoc/test_rdoc_markup_to_markdown.rb
index a72f30ee01..9c5b6d3e57 100644
--- a/test/rdoc/test_rdoc_markup_to_markdown.rb
+++ b/test/rdoc/test_rdoc_markup_to_markdown.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupToMarkdown < RDoc::Markup::TextFormatterTestCase
diff --git a/test/rdoc/test_rdoc_markup_to_rdoc.rb b/test/rdoc/test_rdoc_markup_to_rdoc.rb
index 0dce0a0d5a..e81bc2f1ad 100644
--- a/test/rdoc/test_rdoc_markup_to_rdoc.rb
+++ b/test/rdoc/test_rdoc_markup_to_rdoc.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupToRDoc < RDoc::Markup::TextFormatterTestCase
diff --git a/test/rdoc/test_rdoc_markup_to_table_of_contents.rb b/test/rdoc/test_rdoc_markup_to_table_of_contents.rb
index acfa807948..bfeb2d6f58 100644
--- a/test/rdoc/test_rdoc_markup_to_table_of_contents.rb
+++ b/test/rdoc/test_rdoc_markup_to_table_of_contents.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupToTableOfContents < RDoc::Markup::FormatterTestCase
diff --git a/test/rdoc/test_rdoc_markup_to_tt_only.rb b/test/rdoc/test_rdoc_markup_to_tt_only.rb
index a7918c90ab..856eb592c5 100644
--- a/test/rdoc/test_rdoc_markup_to_tt_only.rb
+++ b/test/rdoc/test_rdoc_markup_to_tt_only.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupToTtOnly < RDoc::Markup::FormatterTestCase
diff --git a/test/rdoc/test_rdoc_markup_verbatim.rb b/test/rdoc/test_rdoc_markup_verbatim.rb
index 6fdf8fde28..88ca4906bb 100644
--- a/test/rdoc/test_rdoc_markup_verbatim.rb
+++ b/test/rdoc/test_rdoc_markup_verbatim.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocMarkupVerbatim < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_method_attr.rb b/test/rdoc/test_rdoc_method_attr.rb
index 70f129ef32..68a9d6cc21 100644
--- a/test/rdoc/test_rdoc_method_attr.rb
+++ b/test/rdoc/test_rdoc_method_attr.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
class TestRDocMethodAttr < XrefTestCase
diff --git a/test/rdoc/test_rdoc_normal_class.rb b/test/rdoc/test_rdoc_normal_class.rb
index 9153bdc839..874eaaa88c 100644
--- a/test/rdoc/test_rdoc_normal_class.rb
+++ b/test/rdoc/test_rdoc_normal_class.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
class TestRDocNormalClass < XrefTestCase
diff --git a/test/rdoc/test_rdoc_normal_module.rb b/test/rdoc/test_rdoc_normal_module.rb
index d92a72e7bf..68e776c41f 100644
--- a/test/rdoc/test_rdoc_normal_module.rb
+++ b/test/rdoc/test_rdoc_normal_module.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
class TestRDocNormalModule < XrefTestCase
diff --git a/test/rdoc/test_rdoc_options.rb b/test/rdoc/test_rdoc_options.rb
index eef37b44b4..baef2d860e 100644
--- a/test/rdoc/test_rdoc_options.rb
+++ b/test/rdoc/test_rdoc_options.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocOptions < RDoc::TestCase
@@ -18,6 +18,7 @@ class TestRDocOptions < RDoc::TestCase
def test_check_files
skip "assumes UNIX permission model" if /mswin|mingw/ =~ RUBY_PLATFORM
+ skip "assumes that euid is not root" if Process.euid == 0
out, err = capture_io do
temp_dir do
diff --git a/test/rdoc/test_rdoc_parser.rb b/test/rdoc/test_rdoc_parser.rb
index b71d89b064..2638adcd15 100644
--- a/test/rdoc/test_rdoc_parser.rb
+++ b/test/rdoc/test_rdoc_parser.rb
@@ -1,7 +1,7 @@
# -*- coding: us-ascii -*-
-# frozen_string_literal: false
+# frozen_string_literal: true
-require 'rdoc/test_case'
+require 'minitest_helper'
class TestRDocParser < RDoc::TestCase
@@ -19,7 +19,7 @@ class TestRDocParser < RDoc::TestCase
def test_class_binary_eh_ISO_2022_JP
iso_2022_jp = File.join Dir.tmpdir, "test_rdoc_parser_#{$$}.rd"
- open iso_2022_jp, 'wb' do |io|
+ File.open iso_2022_jp, 'wb' do |io|
io.write "# coding: ISO-2022-JP\n"
io.write ":\e$B%3%^%s%I\e(B:\n"
end
@@ -31,7 +31,7 @@ class TestRDocParser < RDoc::TestCase
def test_class_binary_eh_marshal
marshal = File.join Dir.tmpdir, "test_rdoc_parser_#{$$}.marshal"
- open marshal, 'wb' do |io|
+ File.open marshal, 'wb' do |io|
io.write Marshal.dump('')
io.write 'lots of text ' * 500
end
@@ -92,7 +92,7 @@ class TestRDocParser < RDoc::TestCase
def test_class_for_executable
temp_dir do
content = "#!/usr/bin/env ruby -w\n"
- open 'app', 'w' do |io| io.write content end
+ File.open 'app', 'w' do |io| io.write content end
app = @store.add_file 'app'
parser = @RP.for app, 'app', content, @options, :stats
@@ -126,7 +126,7 @@ class TestRDocParser < RDoc::TestCase
temp_dir do
content = "# -*- rdoc -*-\n= NEWS\n"
- open 'NEWS', 'w' do |io| io.write content end
+ File.open 'NEWS', 'w' do |io| io.write content end
app = @store.add_file 'NEWS'
parser = @RP.for app, 'NEWS', content, @options, :stats
@@ -140,7 +140,7 @@ class TestRDocParser < RDoc::TestCase
def test_can_parse_modeline
readme_ext = File.join Dir.tmpdir, "README.EXT.#{$$}"
- open readme_ext, 'w' do |io|
+ File.open readme_ext, 'w' do |io|
io.puts "# README.EXT - -*- rdoc -*- created at: Mon Aug 7 16:45:54 JST 1995"
io.puts
io.puts "This document explains how to make extension libraries for Ruby."
@@ -162,7 +162,7 @@ class TestRDocParser < RDoc::TestCase
def test_check_modeline
readme_ext = File.join Dir.tmpdir, "README.EXT.#{$$}"
- open readme_ext, 'w' do |io|
+ File.open readme_ext, 'w' do |io|
io.puts "# README.EXT - -*- RDoc -*- created at: Mon Aug 7 16:45:54 JST 1995"
io.puts
io.puts "This document explains how to make extension libraries for Ruby."
@@ -176,7 +176,7 @@ class TestRDocParser < RDoc::TestCase
def test_check_modeline_coding
readme_ext = File.join Dir.tmpdir, "README.EXT.#{$$}"
- open readme_ext, 'w' do |io|
+ File.open readme_ext, 'w' do |io|
io.puts "# -*- coding: utf-8 -*-"
end
@@ -188,7 +188,7 @@ class TestRDocParser < RDoc::TestCase
def test_check_modeline_with_other
readme_ext = File.join Dir.tmpdir, "README.EXT.#{$$}"
- open readme_ext, 'w' do |io|
+ File.open readme_ext, 'w' do |io|
io.puts "# README.EXT - -*- mode: RDoc; indent-tabs-mode: nil -*-"
io.puts
io.puts "This document explains how to make extension libraries for Ruby."
@@ -202,7 +202,7 @@ class TestRDocParser < RDoc::TestCase
def test_check_modeline_no_modeline
readme_ext = File.join Dir.tmpdir, "README.EXT.#{$$}"
- open readme_ext, 'w' do |io|
+ File.open readme_ext, 'w' do |io|
io.puts "This document explains how to make extension libraries for Ruby."
end
diff --git a/test/rdoc/test_rdoc_parser_c.rb b/test/rdoc/test_rdoc_parser_c.rb
index 09d7c4b16d..3d30d767df 100644
--- a/test/rdoc/test_rdoc_parser_c.rb
+++ b/test/rdoc/test_rdoc_parser_c.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
=begin
TODO: test call-seq parsing
@@ -327,7 +327,7 @@ VALUE cFoo = boot_defclass("Foo", 0);
klass = util_get_class content, 'cFoo'
assert_equal "this is the Foo boot class", klass.comment.text
- assert_equal nil, klass.superclass
+ assert_nil klass.superclass
end
def test_do_aliases_missing_class
@@ -357,6 +357,25 @@ VALUE cFoo = rb_define_class("Foo", rb_cObject);
assert_equal "this is the Foo class", klass.comment.text
end
+ def test_do_classes_duplicate_class
+ content = <<-EOF
+/* Document-class: Foo
+ * first
+ */
+VALUE cFoo = rb_define_class("Foo", rb_cObject);
+/* Document-class: Foo
+ * second
+ */
+VALUE cFoo = rb_define_class("Foo", rb_cObject);
+ EOF
+
+ klass = util_get_class content, 'cFoo'
+ assert_equal 1, klass.comment_location.size
+ first = klass.comment_location.first
+ first_comment = first[0]
+ assert_equal 'first', first_comment.text
+ end
+
def test_do_classes_struct
content = <<-EOF
/* Document-class: Foo
@@ -642,6 +661,7 @@ void Init_Blah(void) {
klass = util_get_class content, 'cDate'
end
+ assert_equal 'Date', klass.full_name
assert_match ' blah.c ', err
end
@@ -664,6 +684,7 @@ void Init_Blah(void) {
klass = util_get_class content, 'cDate'
end
+ assert_equal 'Date', klass.full_name
assert_match ' blah.cpp ', err
end
@@ -686,6 +707,7 @@ void Init_Blah(void) {
klass = util_get_class content, 'cDate'
end
+ assert_equal 'Date', klass.full_name
assert_match ' blah.y ', err
end
@@ -1037,7 +1059,7 @@ Init_Foo(void) {
other_function.comment.text
assert_equal '()', other_function.params
- code = other_function.token_stream.first.text
+ code = other_function.token_stream.first[:text]
assert_equal "VALUE\nother_function() {\n}", code
end
@@ -1107,7 +1129,7 @@ Init_Foo(void) {
other_function.comment.text
assert_equal '()', other_function.params
- code = other_function.token_stream.first.text
+ code = other_function.token_stream.first[:text]
assert_equal "VALUE\nother_function() {\n}", code
end
@@ -1139,10 +1161,9 @@ Init_Foo(void) {
assert_equal 'my_method', other_function.name
assert_equal 'a comment for rb_other_function', other_function.comment.text
assert_equal '()', other_function.params
- assert_equal 118, other_function.offset
assert_equal 8, other_function.line
- code = other_function.token_stream.first.text
+ code = other_function.token_stream.first[:text]
assert_equal "VALUE\nrb_other_function() {\n}", code
end
@@ -1173,10 +1194,9 @@ Init_Foo(void) {
assert_equal 'my_method', other_function.name
assert_equal 'a comment for other_function', other_function.comment.text
assert_equal '()', other_function.params
- assert_equal 39, other_function.offset
assert_equal 4, other_function.line
- code = other_function.token_stream.first.text
+ code = other_function.token_stream.first[:text]
assert_equal "#define other_function rb_other_function", code
end
@@ -1316,7 +1336,7 @@ Init_Foo(void) {
other_function.comment.text
assert_equal '()', other_function.params
- code = other_function.token_stream.first.text
+ code = other_function.token_stream.first[:text]
assert_equal "DLL_LOCAL VALUE\nother_function() {\n}", code
end
@@ -1357,7 +1377,7 @@ commercial() -> Date <br />
parser.find_modifiers comment, method_obj
- assert_equal nil, method_obj.document_self
+ assert_nil method_obj.document_self
end
def test_find_modifiers_yields
@@ -1402,7 +1422,6 @@ rb_m(int argc, VALUE *argv, VALUE obj) {
assert_equal 'm', m.name
assert_equal @top_level, m.file
- assert_equal 115, m.offset
assert_equal 7, m.line
assert_equal '(p1)', m.params
@@ -1611,6 +1630,19 @@ Init_IO(void) {
assert read_method.singleton
end
+ def test_define_method_dynamically
+ content = <<-EOF
+void
+Init_foo(void)
+{
+ rb_define_singleton_method(obj, "foo", foo, -1);
+}
+ EOF
+
+ klass = util_get_class content, 'obj'
+ assert_nil klass
+ end
+
def test_define_method_with_prototype
content = <<-EOF
static VALUE rb_io_s_read(int, VALUE*, VALUE);
diff --git a/test/rdoc/test_rdoc_parser_changelog.rb b/test/rdoc/test_rdoc_parser_changelog.rb
index 0135d26d87..18ea81b34e 100644
--- a/test/rdoc/test_rdoc_parser_changelog.rb
+++ b/test/rdoc/test_rdoc_parser_changelog.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocParserChangeLog < RDoc::TestCase
@@ -33,7 +33,7 @@ class TestRDocParserChangeLog < RDoc::TestCase
def test_continue_entry_body
parser = util_parser
- entry_body = ['a']
+ entry_body = ['a'.dup]
parser.continue_entry_body entry_body, 'b'
@@ -53,7 +53,7 @@ class TestRDocParserChangeLog < RDoc::TestCase
def test_continue_entry_body_function
parser = util_parser
- entry_body = ['file: (func1)']
+ entry_body = ['file: (func1)'.dup]
parser.continue_entry_body entry_body, '(func2): blah'
diff --git a/test/rdoc/test_rdoc_parser_markdown.rb b/test/rdoc/test_rdoc_parser_markdown.rb
index a8a8402946..30e66e2833 100644
--- a/test/rdoc/test_rdoc_parser_markdown.rb
+++ b/test/rdoc/test_rdoc_parser_markdown.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocParserMarkdown < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_parser_rd.rb b/test/rdoc/test_rdoc_parser_rd.rb
index 481d190ffe..15112fdb0e 100644
--- a/test/rdoc/test_rdoc_parser_rd.rb
+++ b/test/rdoc/test_rdoc_parser_rd.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocParserRd < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_parser_ruby.rb b/test/rdoc/test_rdoc_parser_ruby.rb
index 44b38c28aa..8188318769 100644
--- a/test/rdoc/test_rdoc_parser_ruby.rb
+++ b/test/rdoc/test_rdoc_parser_ruby.rb
@@ -1,7 +1,6 @@
-# coding: utf-8
-# frozen_string_literal: false
+# frozen_string_literal: true
-require 'rdoc/test_case'
+require 'minitest_helper'
class TestRDocParserRuby < RDoc::TestCase
@@ -74,7 +73,7 @@ class C; end
comment = parser.collect_first_comment
- assert_equal RDoc::Comment.new("first\n\n", @top_level), comment
+ assert_equal RDoc::Comment.new("first\n", @top_level), comment
end
def test_get_class_or_module
@@ -84,7 +83,7 @@ class C; end
cont, name_t, given_name = util_parser('A') .get_class_or_module ctxt
assert_equal ctxt, cont
- assert_equal 'A', name_t.text
+ assert_equal 'A', name_t[:text]
assert_equal 'A', given_name
cont, name_t, given_name = util_parser('B::C') .get_class_or_module ctxt
@@ -92,16 +91,16 @@ class C; end
b = @store.find_module_named('B')
assert_equal b, cont
assert_equal [@top_level], b.in_files
- assert_equal 'C', name_t.text
+ assert_equal 'C', name_t[:text]
assert_equal 'B::C', given_name
cont, name_t, given_name = util_parser('D:: E').get_class_or_module ctxt
assert_equal @store.find_module_named('D'), cont
- assert_equal 'E', name_t.text
+ assert_equal 'E', name_t[:text]
assert_equal 'D::E', given_name
- assert_raises NoMethodError do
+ assert_raises RDoc::Error do
util_parser("A::\nB").get_class_or_module ctxt
end
end
@@ -232,8 +231,8 @@ class C; end
@parser.look_for_directives_in @top_level, comment
section = @top_level.current_section
- assert_equal nil, section.title
- assert_equal nil, section.comment
+ assert_nil section.title
+ assert_nil section.comment
assert_equal "# how to make a section:\n# # :section: new section\n",
comment.text
@@ -282,6 +281,92 @@ class C; end
assert_equal 'blah', @top_level.metadata['unhandled']
end
+ def test_parse_for_in
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new '', @top_level
+
+ util_parser <<ruby
+def sum(n)
+ result = 0
+ for i in 1..n do
+ result += i
+ end
+ result
+end
+ruby
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ sum = klass.method_list.first
+ assert_equal 'sum', sum.name
+ assert_equal @top_level, sum.file
+ end
+
+ def test_parse_on_ignored_nl_with_nil_text
+ util_parser <<ruby
+class Foo
+ def meth
+ variable # comment
+ .chain
+ end
+end
+ruby
+
+ expected = <<EXPECTED
+<span class="ruby-keyword">def</span> <span class="ruby-identifier ruby-title">meth</span>
+ <span class="ruby-identifier">variable</span> <span class="ruby-comment"># comment</span>
+ .<span class="ruby-identifier">chain</span>
+<span class="ruby-keyword">end</span>
+EXPECTED
+ expected = expected.rstrip
+
+ @parser.scan
+
+ foo = @store.find_class_named 'Foo'
+ meth = foo.method_list.first
+
+ assert_equal 'meth', meth.name
+ assert_equal @top_level, meth.file
+
+ markup_code = meth.markup_code.sub(/^.*\n/, '')
+ assert_equal expected, markup_code
+ end
+
+ def test_parse_redefined_op_with_constant
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new '', @top_level
+
+ util_parser <<ruby
+def meth
+ Integer::**()
+ return Integer::**()
+ break Integer::**()
+ case Integer::**()
+ when Integer::**()
+ end
+ while Integer::**()
+ end
+ yield Integer::**()
+ defined? Integer::**()
+ if Integer::**()
+ end
+end
+ruby
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ meth = klass.method_list.first
+ assert_equal 'meth', meth.name
+ end
+
def test_parse_alias
klass = RDoc::NormalClass.new 'Foo'
klass.parent = @top_level
@@ -297,7 +382,6 @@ class C; end
assert_equal klass, alas.parent
assert_equal 'comment', alas.comment
assert_equal @top_level, alas.file
- assert_equal 0, alas.offset
assert_equal 1, alas.line
end
@@ -365,7 +449,6 @@ class C; end
assert_equal 'foo', foo.name
assert_equal 'my attr', foo.comment.text
assert_equal @top_level, foo.file
- assert_equal 0, foo.offset
assert_equal 1, foo.line
end
@@ -404,7 +487,6 @@ class C; end
assert_equal 'RW', foo.rw
assert_equal 'my attr', foo.comment.text
assert_equal @top_level, foo.file
- assert_equal 0, foo.offset
assert_equal 1, foo.line
bar = klass.attributes.last
@@ -413,6 +495,43 @@ class C; end
assert_equal 'my attr', bar.comment.text
end
+ def test_parse_attr_accessor_with_newline
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "##\n# my attr\n", @top_level
+
+ util_parser "attr_accessor :foo, :bar,\n :baz,\n :qux"
+
+ tk = @parser.get_tk
+
+ @parser.parse_attr_accessor klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_equal 4, klass.attributes.length
+
+ foo = klass.attributes[0]
+ assert_equal 'foo', foo.name
+ assert_equal 'RW', foo.rw
+ assert_equal 'my attr', foo.comment.text
+ assert_equal @top_level, foo.file
+ assert_equal 1, foo.line
+
+ bar = klass.attributes[1]
+ assert_equal 'bar', bar.name
+ assert_equal 'RW', bar.rw
+ assert_equal 'my attr', bar.comment.text
+
+ bar = klass.attributes[2]
+ assert_equal 'baz', bar.name
+ assert_equal 'RW', bar.rw
+ assert_equal 'my attr', bar.comment.text
+
+ bar = klass.attributes[3]
+ assert_equal 'qux', bar.name
+ assert_equal 'RW', bar.rw
+ assert_equal 'my attr', bar.comment.text
+ end
+
def test_parse_attr_accessor_nodoc
klass = RDoc::NormalClass.new 'Foo'
klass.parent = @top_level
@@ -617,7 +736,6 @@ class C; end
assert_equal 'Foo', foo.full_name
assert_equal 'my class', foo.comment.text
assert_equal [@top_level], foo.in_files
- assert_equal 0, foo.offset
assert_equal 1, foo.line
end
@@ -637,7 +755,6 @@ end
c = @top_level.classes.first
assert_equal 'C', c.full_name
- assert_equal 0, c.offset
assert_equal 1, c.line
end
@@ -662,6 +779,32 @@ end
assert_equal @top_level, blah.file
end
+ def test_parse_class_in_a_file_repeatedly
+ @filename = 'a.rb'
+ comment_a = RDoc::Comment.new "# aaa\n", @top_level
+ util_parser "class Foo\nend"
+ tk = @parser.get_tk
+ @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, comment_a
+ comment_b = RDoc::Comment.new "# bbb\n", @top_level
+ util_parser "class Foo\nend"
+ tk = @parser.get_tk
+ @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, comment_b
+
+ @filename = 'b.rb'
+ comment_c = RDoc::Comment.new "# ccc\n", @top_level
+ util_parser "class Foo\nend"
+ tk = @parser.get_tk
+ @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, comment_c
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+ assert_equal [[comment_a, @top_level],
+ [comment_b, @top_level],
+ [comment_c, @top_level]], foo.comment_location
+ assert_equal [@top_level], foo.in_files
+ assert_equal 1, foo.line
+ end
+
def test_parse_class_ghost_method_yields
util_parser <<-CLASS
class Foo
@@ -727,7 +870,6 @@ end
assert_equal 'Foo', foo.full_name
assert_empty foo.comment
assert_equal [@top_level], foo.in_files
- assert_equal 0, foo.offset
assert_equal 1, foo.line
end
@@ -758,6 +900,42 @@ end
assert_empty @top_level.classes.first.comment
end
+ def test_parse_class_lower_name_warning
+ @options.verbosity = 2
+ stds = capture_io do
+ util_parser "class foo\nend"
+ tk = @parser.get_tk
+ @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, @comment
+ end
+ err = stds[1]
+ assert_match(/Expected class name or '<<'\. Got/, err)
+ end
+
+ def test_parse_syntax_error_code
+ @options.verbosity = 2
+ stds = capture_io do
+ begin
+ util_parser <<INVALID_CODE
+# invalid class name
+class Invalid::@@Code
+end
+INVALID_CODE
+ @parser.scan
+ rescue
+ end
+ end
+ err = stds[1]
+
+ expected = <<EXPECTED
+RDoc::Parser::Ruby failure around line 2 of
+#{@filename}
+
+class Invalid::@@Code
+EXPECTED
+
+ assert_match(expected, err)
+ end
+
def test_parse_multi_ghost_methods
util_parser <<-'CLASS'
class Foo
@@ -920,7 +1098,6 @@ end
assert_equal %w[A::B A::d], modules.map { |c| c.full_name }
b = modules.first
- assert_equal 10, b.offset
assert_equal 2, b.line
# make sure method/alias was not added to enclosing class/module
@@ -1083,10 +1260,9 @@ EOF
assert_equal 'RW', foo.rw
assert_equal 'my attr', foo.comment.text
assert_equal @top_level, foo.file
- assert_equal 0, foo.offset
assert_equal 1, foo.line
- assert_equal nil, foo.viewer
+ assert_nil foo.viewer
assert_equal true, foo.document_children
assert_equal true, foo.document_self
assert_equal false, foo.done_documenting
@@ -1147,34 +1323,43 @@ EOF
assert_equal 'foo', foo.name
assert_equal 'my method', foo.comment.text
assert_equal @top_level, foo.file
- assert_equal 0, foo.offset
assert_equal 1, foo.line
- assert_equal [], foo.aliases
- assert_equal nil, foo.block_params
- assert_equal nil, foo.call_seq
- assert_equal nil, foo.is_alias_for
- assert_equal nil, foo.viewer
- assert_equal true, foo.document_children
- assert_equal true, foo.document_self
- assert_equal '', foo.params
- assert_equal false, foo.done_documenting
- assert_equal false, foo.dont_rename_initialize
- assert_equal false, foo.force_documentation
- assert_equal klass, foo.parent
- assert_equal false, foo.singleton
- assert_equal :public, foo.visibility
- assert_equal "\n", foo.text
+ assert_equal [], foo.aliases
+ assert_nil foo.block_params
+ assert_nil foo.call_seq
+ assert_nil foo.is_alias_for
+ assert_nil foo.viewer
+ assert_equal true, foo.document_children
+ assert_equal true, foo.document_self
+ assert_equal '', foo.params
+ assert_equal false, foo.done_documenting
+ assert_equal false, foo.dont_rename_initialize
+ assert_equal false, foo.force_documentation
+ assert_equal klass, foo.parent
+ assert_equal false, foo.singleton
+ assert_equal :public, foo.visibility
+ assert_equal "\n", foo.text
assert_equal klass.current_section, foo.section
stream = [
- tk(:COMMENT, 0, 1, 1, nil,
- "# File #{@top_level.relative_name}, line 1"),
- RDoc::Parser::Ruby::NEWLINE_TOKEN,
- tk(:SPACE, 0, 1, 1, nil, ''),
+ {
+ :line_no => 1, :char_no => 1, :kind => :on_comment,
+ :text => "# File #{@top_level.relative_name}, line 1"
+ },
+ { :line_no => 0, :char_no => 0, :kind => :on_nl, :text => "\n" },
+ { :line_no => 1, :char_no => 1, :kind => :on_sp, :text => '' }
]
+ parsed_stream = foo.token_stream.map { |t|
+ {
+ :line_no => t[:line_no],
+ :char_no => t[:char_no],
+ :kind => t[:kind],
+ :text => t[:text]
+ }
+ }
- assert_equal stream, foo.token_stream
+ assert_equal stream, parsed_stream
end
def test_parse_comment_method_args
@@ -1223,7 +1408,6 @@ EOF
assert_equal 'A', foo.name
assert_equal @top_level, foo.file
- assert_equal 0, foo.offset
assert_equal 1, foo.line
end
@@ -1249,6 +1433,9 @@ EOF
@parser.parse_constant klass, tk, @comment
+ assert_equal [], klass.modules.map(&:full_name)
+ assert_equal ['Foo::B', 'Foo::A'], klass.classes.map(&:full_name)
+ assert_equal ['Foo::A'], klass.constants.map(&:full_name)
assert_equal 'Foo::A', klass.find_module_named('A').full_name
end
@@ -1333,6 +1520,77 @@ A::B::C = 1
assert_equal 'comment', c.comment
end
+ def test_parse_class_the_same_of_outside
+ util_parser <<-RUBY
+module A
+ class A::B
+ end
+end
+ RUBY
+
+ @parser.scan
+
+ assert_includes @store.modules_hash, 'A'
+ module_a = @store.find_module_named 'A'
+ refute_empty module_a.classes_hash
+ assert_includes module_a.classes_hash, 'B'
+ refute_includes module_a.classes_hash, 'A'
+ end
+
+ def test_parse_constant_the_same_of_outside
+ util_parser <<-RUBY
+module A
+ class B
+ class C
+ end
+ end
+
+ def self.foo
+ A::B::C
+ end
+end
+ RUBY
+
+ expected = <<EXPECTED
+<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier ruby-title">foo</span>
+ <span class="ruby-constant">A</span><span class="ruby-operator">::</span><span class="ruby-constant">B</span><span class="ruby-operator">::</span><span class="ruby-constant">C</span>
+<span class="ruby-keyword">end</span>
+EXPECTED
+ expected = expected.rstrip
+
+ @parser.scan
+
+ module_a = @store.find_module_named 'A'
+ foo = module_a.method_list.first
+ markup_code = foo.markup_code.sub(/^.*\n/, '')
+ assert_equal expected, markup_code
+ end
+
+ def test_parse_constant_with_bracket
+ util_parser <<-RUBY
+class Klass
+end
+
+class Klass2
+ CONSTANT = Klass
+end
+
+class Klass3
+ CONSTANT_2 = {}
+ CONSTANT_2[1] = Klass
+end
+ RUBY
+
+ @parser.scan
+
+ klass = @store.find_class_named 'Klass'
+ klass2 = @store.find_class_named 'Klass2'
+ klass3 = @store.find_class_named 'Klass3'
+ assert_equal klass, klass2.constants.first.is_alias_for
+ refute_equal klass, klass3.constants.first.is_alias_for
+ assert_nil klass3.find_module_named 'CONSTANT_2'
+ end
+
def test_parse_extend_or_include_extend
klass = RDoc::NormalClass.new 'C'
klass.parent = @top_level
@@ -1389,41 +1647,50 @@ A::B::C = 1
assert_equal 'foo', foo.name
assert_equal 'my method', foo.comment.text
assert_equal @top_level, foo.file
- assert_equal 0, foo.offset
assert_equal 1, foo.line
assert_equal [], foo.aliases
- assert_equal nil, foo.block_params
- assert_equal nil, foo.call_seq
+ assert_nil foo.block_params
+ assert_nil foo.call_seq
assert_equal true, foo.document_children
assert_equal true, foo.document_self
assert_equal false, foo.done_documenting
assert_equal false, foo.dont_rename_initialize
assert_equal false, foo.force_documentation
- assert_equal nil, foo.is_alias_for
+ assert_nil foo.is_alias_for
assert_equal '', foo.params
assert_equal klass, foo.parent
assert_equal false, foo.singleton
assert_equal 'add_my_method :foo', foo.text
- assert_equal nil, foo.viewer
+ assert_nil foo.viewer
assert_equal :public, foo.visibility
assert_equal klass.current_section, foo.section
stream = [
- tk(:COMMENT, 0, 1, 1, nil,
- "# File #{@top_level.relative_name}, line 1"),
- RDoc::Parser::Ruby::NEWLINE_TOKEN,
- tk(:SPACE, 0, 1, 1, nil, ''),
- tk(:IDENTIFIER, 0, 1, 0, 'add_my_method', 'add_my_method'),
- tk(:SPACE, 0, 1, 13, nil, ' '),
- tk(:SYMBOL, 0, 1, 14, nil, ':foo'),
- tk(:COMMA, 0, 1, 18, nil, ','),
- tk(:SPACE, 0, 1, 19, nil, ' '),
- tk(:SYMBOL, 0, 1, 20, nil, ':bar'),
- tk(:NL, 0, 1, 24, nil, "\n"),
+ {
+ :line_no => 1, :char_no => 1, :kind => :on_comment,
+ :text => "# File #{@top_level.relative_name}, line 1"
+ },
+ { :line_no => 0, :char_no => 0, :kind => :on_nl, :text => "\n" },
+ { :line_no => 1, :char_no => 1, :kind => :on_sp, :text => '' },
+ { :line_no => 1, :char_no => 0, :kind => :on_ident, :text => 'add_my_method' },
+ { :line_no => 1, :char_no => 13, :kind => :on_sp, :text => ' ' },
+ { :line_no => 1, :char_no => 14, :kind => :on_symbol, :text => ':foo' },
+ { :line_no => 1, :char_no => 18, :kind => :on_comma, :text => ',' },
+ { :line_no => 1, :char_no => 19, :kind => :on_sp, :text => ' ' },
+ { :line_no => 1, :char_no => 20, :kind => :on_symbol, :text => ':bar' },
+ { :line_no => 1, :char_no => 24, :kind => :on_nl, :text => "\n" }
]
+ parsed_stream = foo.token_stream.map { |t|
+ {
+ :line_no => t[:line_no],
+ :char_no => t[:char_no],
+ :kind => t[:kind],
+ :text => t[:text]
+ }
+ }
- assert_equal stream, foo.token_stream
+ assert_equal stream, parsed_stream
end
def test_parse_meta_method_block
@@ -1444,7 +1711,10 @@ end
@parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
- assert_equal tk(:NL, 0, 3, 3, 3, "\n"), @parser.get_tk
+ rest = { :line_no => 3, :char_no => 3, :kind => :on_nl, :text => "\n" }
+ tk = @parser.get_tk
+ tk = { :line_no => tk[:line_no], :char_no => tk[:char_no], :kind => tk[:kind], :text => tk[:text] }
+ assert_equal rest, tk
end
def test_parse_meta_method_define_method
@@ -1587,14 +1857,13 @@ end
assert_equal 'foo', foo.name
assert_equal 'my method', foo.comment.text
assert_equal @top_level, foo.file
- assert_equal 0, foo.offset
assert_equal 1, foo.line
assert_equal [], foo.aliases
- assert_equal nil, foo.block_params
- assert_equal nil, foo.call_seq
- assert_equal nil, foo.is_alias_for
- assert_equal nil, foo.viewer
+ assert_nil foo.block_params
+ assert_nil foo.call_seq
+ assert_nil foo.is_alias_for
+ assert_nil foo.viewer
assert_equal true, foo.document_children
assert_equal true, foo.document_self
assert_equal '()', foo.params
@@ -1608,23 +1877,93 @@ end
assert_equal klass.current_section, foo.section
stream = [
- tk(:COMMENT, 0, 1, 1, nil,
- "# File #{@top_level.relative_name}, line 1"),
- RDoc::Parser::Ruby::NEWLINE_TOKEN,
- tk(:SPACE, 0, 1, 1, nil, ''),
- tk(:DEF, 0, 1, 0, 'def', 'def'),
- tk(:SPACE, 3, 1, 3, nil, ' '),
- tk(:IDENTIFIER, 4, 1, 4, 'foo', 'foo'),
- tk(:LPAREN, 7, 1, 7, nil, '('),
- tk(:RPAREN, 8, 1, 8, nil, ')'),
- tk(:SPACE, 9, 1, 9, nil, ' '),
- tk(:COLON, 10, 1, 10, nil, ':'),
- tk(:IDENTIFIER, 11, 1, 11, 'bar', 'bar'),
- tk(:SPACE, 14, 1, 14, nil, ' '),
- tk(:END, 15, 1, 15, 'end', 'end'),
+ {
+ :line_no => 1, :char_no => 1, :kind => :on_comment,
+ :text => "# File #{@top_level.relative_name}, line 1" },
+ { :line_no => 0, :char_no => 0, :kind => :on_nl, :text => "\n" },
+ { :line_no => 1, :char_no => 1, :kind => :on_sp, :text => '' },
+ { :line_no => 1, :char_no => 0, :kind => :on_kw, :text => 'def' },
+ { :line_no => 1, :char_no => 3, :kind => :on_sp, :text => ' ' },
+ { :line_no => 1, :char_no => 4, :kind => :on_ident, :text => 'foo' },
+ { :line_no => 1, :char_no => 7, :kind => :on_lparen, :text => '(' },
+ { :line_no => 1, :char_no => 8, :kind => :on_rparen, :text => ')' },
+ { :line_no => 1, :char_no => 9, :kind => :on_sp, :text => ' ' },
+ { :line_no => 1, :char_no => 10, :kind => :on_symbol, :text => ':bar' },
+ { :line_no => 1, :char_no => 14, :kind => :on_sp, :text => ' ' },
+ { :line_no => 1, :char_no => 15, :kind => :on_kw, :text => 'end' }
]
+ parsed_stream = foo.token_stream.map { |t|
+ {
+ :line_no => t[:line_no],
+ :char_no => t[:char_no],
+ :kind => t[:kind],
+ :text => t[:text]
+ }
+ }
+ assert_equal stream, parsed_stream
+ end
+
+ def test_parse_redefinable_methods
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "", @top_level
- assert_equal stream, foo.token_stream
+ redefinable_ops = %w[| ^ & <=> == === =~ > >= < <= << >> + - * / % ** ~ +@ -@ [] []= ` ! != !~]
+ redefinable_ops.each do |redefinable_op|
+ util_parser "def #{redefinable_op}\nend\n"
+ tk = @parser.get_tk
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+ end
+
+ klass.method_list.each do |method|
+ assert_equal :on_ident, method.token_stream[5][:kind]
+ assert_includes redefinable_ops, method.token_stream[5][:text]
+ end
+ end
+
+ def test_parse_method_with_args_directive
+ util_parser <<-RUBY
+class C
+ def meth_with_args_after # :args: a, b, c
+ end
+
+ ##
+ # :args: d, e, f
+ def meth_with_args_before
+end
+ RUBY
+
+ @parser.scan
+
+ c = @store.find_class_named 'C'
+
+ assert_equal 'C#meth_with_args_after', c.method_list[0].full_name
+ assert_equal 'a, b, c', c.method_list[0].params
+ assert_equal 'C#meth_with_args_before', c.method_list[1].full_name
+ assert_equal 'd, e, f', c.method_list[1].params
+ end
+
+ def test_parse_method_bracket
+ util_parser <<-RUBY
+class C
+ def [] end
+ def self.[] end
+ def []= end
+ def self.[]= end
+end
+ RUBY
+
+ @parser.scan
+
+ c = @store.find_class_named 'C'
+
+ assert_equal 4, c.method_list.size
+ assert_equal 'C#[]', c.method_list[0].full_name
+ assert_equal 'C::[]', c.method_list[1].full_name
+ assert_equal 'C#[]=', c.method_list[2].full_name
+ assert_equal 'C::[]=', c.method_list[3].full_name
+ assert c.aliases.empty?
end
def test_parse_method_alias
@@ -1845,6 +2184,20 @@ end
assert_equal '(arg1, arg2, arg3)', foo.params
end
+ def test_parse_method_parameters_with_paren_comment_continue
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "def foo(arg1, arg2, # some useful comment\narg3)\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ foo = klass.method_list.first
+ assert_equal '(arg1, arg2, arg3)', foo.params
+ end
+
def test_parse_method_star
klass = RDoc::NormalClass.new 'Foo'
klass.parent = @top_level
@@ -1978,6 +2331,55 @@ end
assert_equal 'Foo#blah', methods.first.full_name
end
+ def test_parse_statements_postfix_if_unless
+ util_parser <<-CODE
+class C
+ def foo
+ 1 if nil
+ end
+
+ def bar
+ 2 unless nil
+ end
+end
+ CODE
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil
+
+ c = @top_level.classes.first
+ assert_equal 'C', c.full_name, 'class C'
+
+ methods = c.method_list
+ assert_equal 2, methods.length
+ assert_equal 'C#foo', methods[0].full_name
+ assert_equal 'C#bar', methods[1].full_name
+ end
+
+ def test_parse_statements_postfix_if_unless_with_expr_mid
+ util_parser <<-CODE
+class A
+ class B
+ def foo
+ return if nil
+ end
+ end
+
+ class C
+ end
+end
+ CODE
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil
+
+ a = @top_level.classes.first
+ assert_equal 'A', a.full_name, 'class A'
+ assert_equal 2, a.classes.length
+ b = a.classes[0]
+ assert_equal 'A::B', b.full_name, 'class A::B'
+ c = a.classes[1]
+ assert_equal 'A::C', c.full_name, 'class A::C'
+ end
+
def test_parse_statements_class_nested
comment = RDoc::Comment.new "##\n# my method\n", @top_level
@@ -1994,7 +2396,7 @@ end
end
def test_parse_statements_def_percent_string_pound
- util_parser "class C\ndef a\n%r{#}\nend\ndef b() end\nend"
+ util_parser "class C\ndef a\n%r{#}\n%r{\#{}}\nend\ndef b() end\nend"
@parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL
@@ -2003,20 +2405,34 @@ end
assert_equal 2, x.method_list.length
a = x.method_list.first
+
expected = [
- tk(:COMMENT, 0, 2, 1, nil, "# File #{@filename}, line 2"),
- tk(:NL, 0, 0, 0, nil, "\n"),
- tk(:SPACE, 0, 1, 1, nil, ''),
- tk(:DEF, 8, 2, 0, 'def', 'def'),
- tk(:SPACE, 11, 2, 3, nil, ' '),
- tk(:IDENTIFIER, 12, 2, 4, 'a', 'a'),
- tk(:NL, 13, 2, 5, nil, "\n"),
- tk(:DREGEXP, 14, 3, 0, nil, '%r{#}'),
- tk(:NL, 19, 3, 5, nil, "\n"),
- tk(:END, 20, 4, 0, 'end', 'end'),
+ {
+ :line_no => 2, :char_no => 1, :kind => :on_comment,
+ :text => "# File #{@filename}, line 2"
+ },
+ { :line_no => 0, :char_no => 0, :kind => :on_nl, :text => "\n" },
+ { :line_no => 1, :char_no => 1, :kind => :on_sp, :text => '' },
+ { :line_no => 2, :char_no => 0, :kind => :on_kw, :text => 'def' },
+ { :line_no => 2, :char_no => 3, :kind => :on_sp, :text => ' ' },
+ { :line_no => 2, :char_no => 4, :kind => :on_ident, :text => 'a' },
+ { :line_no => 2, :char_no => 5, :kind => :on_nl, :text => "\n" },
+ { :line_no => 3, :char_no => 0, :kind => :on_regexp, :text => '%r{#}' },
+ { :line_no => 3, :char_no => 5, :kind => :on_nl, :text => "\n" },
+ { :line_no => 4, :char_no => 0, :kind => :on_regexp, :text => '%r{#{}}' },
+ { :line_no => 4, :char_no => 7, :kind => :on_nl, :text => "\n" },
+ { :line_no => 5, :char_no => 0, :kind => :on_kw, :text => 'end' }
]
+ parsed_stream = a.token_stream.map { |tk|
+ {
+ :line_no => tk[:line_no],
+ :char_no => tk[:char_no],
+ :kind => tk[:kind],
+ :text => tk[:text]
+ }
+ }
- assert_equal expected, a.token_stream
+ assert_equal expected, parsed_stream
end
def test_parse_statements_encoding
@@ -2196,6 +2612,9 @@ class Foo
SIXTH_CONSTANT = #{sixth_constant}
SEVENTH_CONSTANT = proc { |i| begin i end }
+
+ EIGHTH_CONSTANT = "a" \\
+ "b"
end
EOF
@@ -2241,6 +2660,11 @@ EOF
assert_equal 'SEVENTH_CONSTANT', constant.name
assert_equal "proc { |i| begin i end }", constant.value
assert_equal @top_level, constant.file
+
+ constant = constants[7]
+ assert_equal 'EIGHTH_CONSTANT', constant.name
+ assert_equal "\"a\" \\\n\"b\"", constant.value
+ assert_equal @top_level, constant.file
end
def test_parse_statements_identifier_attr
@@ -2377,6 +2801,214 @@ end
assert_equal :private, date_time_now.visibility, date_time_now.full_name
end
+ def test_parse_statements_complex_condition_in_for
+ util_parser <<RUBY
+class Foo
+ def blah()
+ for i in (k)...n do
+ end
+ for i in (k)...n
+ end
+ end
+end
+RUBY
+
+ expected = <<EXPECTED
+<span class="ruby-keyword">def</span> <span class="ruby-identifier ruby-title">blah</span>()
+ <span class="ruby-keyword">for</span> <span class="ruby-identifier">i</span> <span class="ruby-keyword">in</span> (<span class="ruby-identifier">k</span>)<span class="ruby-operator">...</span><span class="ruby-identifier">n</span> <span class="ruby-keyword">do</span>
+ <span class="ruby-keyword">end</span>
+ <span class="ruby-keyword">for</span> <span class="ruby-identifier">i</span> <span class="ruby-keyword">in</span> (<span class="ruby-identifier">k</span>)<span class="ruby-operator">...</span><span class="ruby-identifier">n</span>
+ <span class="ruby-keyword">end</span>
+<span class="ruby-keyword">end</span>
+EXPECTED
+ expected = expected.rstrip
+
+ @parser.scan
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+
+ blah = foo.method_list.first
+ markup_code = blah.markup_code.sub(/^.*\n/, '')
+ assert_equal expected, markup_code
+ end
+
+ def test_parse_instance_operation_method
+ util_parser <<-RUBY
+class Foo
+ def self.& end
+end
+ RUBY
+
+ expected = <<EXPECTED
+ <span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier ruby-title">&amp;</span> <span class="ruby-keyword">end</span>
+<span class="ruby-keyword">end</span>
+EXPECTED
+ expected = expected.rstrip
+
+ @parser.scan
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+
+ blah = foo.method_list.first
+ markup_code = blah.markup_code.sub(/^.*\n/, '')
+ assert_equal expected, markup_code
+ end
+
+ def test_parse_statements_postfix_if_after_heredocbeg
+ @filename = 'file.rb'
+ util_parser <<RUBY
+class Foo
+ def blah()
+ <<-EOM if true
+ EOM
+ end
+end
+RUBY
+
+ expected = <<EXPECTED
+ <span class="ruby-keyword">def</span> <span class="ruby-identifier ruby-title">blah</span>()
+ <span class="ruby-identifier">&lt;&lt;-EOM</span> <span class="ruby-keyword">if</span> <span class="ruby-keyword">true</span>
+<span class="ruby-value"></span><span class="ruby-identifier"> EOM</span>
+ <span class="ruby-keyword">end</span>
+EXPECTED
+ expected = expected.rstrip
+
+ @parser.scan
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+
+ blah = foo.method_list.first
+ markup_code = blah.markup_code.sub(/^.*\n/, '')
+ assert_equal expected, markup_code
+ end
+
+ def test_parse_mutable_heredocbeg
+ @filename = 'file.rb'
+ util_parser <<RUBY
+class Foo
+ def blah()
+ @str = -<<-EOM
+ EOM
+ end
+end
+RUBY
+
+ expected = <<EXPECTED
+ <span class="ruby-keyword">def</span> <span class="ruby-identifier ruby-title">blah</span>()
+ <span class="ruby-ivar">@str</span> = <span class="ruby-identifier">-&lt;&lt;-EOM</span>
+<span class="ruby-value"></span><span class="ruby-identifier"> EOM</span>
+ <span class="ruby-keyword">end</span>
+EXPECTED
+ expected = expected.rstrip
+
+ @parser.scan
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+
+ blah = foo.method_list.first
+ markup_code = blah.markup_code.sub(/^.*\n/, '')
+ assert_equal expected, markup_code
+ end
+
+ def test_parse_statements_method_oneliner_with_regexp
+ util_parser <<RUBY
+class Foo
+ def blah() /bar/ end
+end
+RUBY
+
+ expected = <<EXPECTED
+<span class="ruby-keyword">def</span> <span class="ruby-identifier ruby-title">blah</span>() <span class="ruby-regexp">/bar/</span> <span class="ruby-keyword">end</span>
+EXPECTED
+ expected = expected.rstrip
+
+ @parser.scan
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+
+ blah = foo.method_list.first
+ markup_code = blah.markup_code.sub(/^.*\n/, '')
+ assert_equal expected, markup_code
+ end
+
+ def test_parse_statements_embdoc_in_document
+ @filename = 'file.rb'
+ util_parser <<RUBY
+class Foo
+ # doc
+ #
+ # =begin
+ # test embdoc
+ # =end
+ #
+ def blah
+ end
+end
+RUBY
+
+ expected = <<EXPECTED
+<p>doc
+
+<pre class="ruby"><span class="ruby-comment">=begin
+test embdoc
+=end</span>
+</pre>
+EXPECTED
+
+ @parser.scan
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+
+ blah = foo.method_list.first
+ markup_comment = blah.search_record[6]
+ assert_equal expected, markup_comment
+ end
+
+ def test_parse_require_dynamic_string
+ content = <<-RUBY
+prefix = 'path'
+require "\#{prefix}/a_library"
+require 'test'
+RUBY
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ assert_equal 1, @top_level.requires.length
+ end
+
+ def test_parse_postfix_nodoc
+ util_parser <<-RUBY
+class A
+end # :nodoc:
+
+class B
+ def a
+ end # :nodoc:
+
+ def b
+ end
+end
+RUBY
+
+ @parser.parse_statements @top_level
+
+ c_a = @top_level.classes.select(&:document_self).first
+ assert_equal 'B', c_a.full_name
+
+ assert_equal 2, @top_level.classes.length
+ assert_equal 1, @top_level.classes.count(&:document_self)
+ assert_equal 1, c_a.method_list.length
+ assert_equal 'B#b', c_a.method_list.first.full_name
+ end
+
def test_parse_statements_identifier_require
content = "require 'bar'"
@@ -2405,7 +3037,7 @@ end
assert_nil m.params, 'Module parameter not removed'
end
- def test_parse_statements_stopdoc_TkALIAS
+ def test_parse_statements_stopdoc_alias
klass = @top_level.add_class RDoc::NormalClass, 'Foo'
util_parser "\n# :stopdoc:\nalias old new"
@@ -2416,7 +3048,7 @@ end
assert_empty klass.unmatched_alias_lists
end
- def test_parse_statements_stopdoc_TkIDENTIFIER_alias_method
+ def test_parse_statements_stopdoc_identifier_alias_method
klass = @top_level.add_class RDoc::NormalClass, 'Foo'
util_parser "\n# :stopdoc:\nalias_method :old :new"
@@ -2427,7 +3059,7 @@ end
assert_empty klass.unmatched_alias_lists
end
- def test_parse_statements_stopdoc_TkIDENTIFIER_metaprogrammed
+ def test_parse_statements_stopdoc_identifier_metaprogrammed
klass = @top_level.add_class RDoc::NormalClass, 'Foo'
util_parser "\n# :stopdoc:\n# attr :meta"
@@ -2438,7 +3070,7 @@ end
assert_empty klass.attributes
end
- def test_parse_statements_stopdoc_TkCONSTANT
+ def test_parse_statements_stopdoc_constant
klass = @top_level.add_class RDoc::NormalClass, 'Foo'
util_parser "\n# :stopdoc:\nA = v"
@@ -2448,7 +3080,7 @@ end
assert_empty klass.constants
end
- def test_parse_statements_stopdoc_TkDEF
+ def test_parse_statements_stopdoc_def
klass = @top_level.add_class RDoc::NormalClass, 'Foo'
util_parser "\n# :stopdoc:\ndef m\n end"
@@ -2504,22 +3136,45 @@ end
assert_equal 'A#b', m_b.full_name
end
+
+ def test_parse_symbol_in_paren_arg
+ util_parser <<RUBY
+class Foo
+ def blah
+ end
+ private(:blah)
+end
+RUBY
+
+ @parser.scan
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+
+ blah = foo.method_list.first
+ assert_equal :private, blah.visibility
+ end
+
def test_parse_symbol_in_arg
- util_parser ':blah "blah" "#{blah}" blah'
+ util_parser '[:blah, "blah", "#{blah}", blah]'
+ @parser.get_tk # skip '['
assert_equal 'blah', @parser.parse_symbol_in_arg
+ @parser.get_tk # skip ','
@parser.skip_tkspace
assert_equal 'blah', @parser.parse_symbol_in_arg
+ @parser.get_tk # skip ','
@parser.skip_tkspace
- assert_equal nil, @parser.parse_symbol_in_arg
+ assert_nil @parser.parse_symbol_in_arg
+ @parser.get_tk # skip ','
@parser.skip_tkspace
- assert_equal nil, @parser.parse_symbol_in_arg
+ assert_nil @parser.parse_symbol_in_arg
end
def test_parse_statements_alias_method
@@ -2623,7 +3278,7 @@ end
assert_equal 'category', directive
assert_equal 'test', value
- assert_kind_of RDoc::RubyToken::TkNL, parser.get_tk
+ assert_nil parser.get_tk
end
def test_read_directive_allow
@@ -2633,7 +3288,7 @@ end
assert_nil directive
- assert_kind_of RDoc::RubyToken::TkNL, parser.get_tk
+ assert_nil parser.get_tk
end
def test_read_directive_empty
@@ -2643,7 +3298,7 @@ end
assert_nil directive
- assert_kind_of RDoc::RubyToken::TkNL, parser.get_tk
+ assert_nil parser.get_tk
end
def test_read_directive_no_comment
@@ -2653,18 +3308,18 @@ end
assert_nil directive
- assert_kind_of RDoc::RubyToken::TkNL, parser.get_tk
+ assert_nil parser.get_tk
end
def test_read_directive_one_liner
- parser = util_parser '; end # :category: test'
+ parser = util_parser 'AAA = 1 # :category: test'
directive, value = parser.read_directive %w[category]
assert_equal 'category', directive
assert_equal 'test', value
- assert_kind_of RDoc::RubyToken::TkSEMICOLON, parser.get_tk
+ assert_equal :on_const, parser.get_tk[:kind]
end
def test_read_documentation_modifiers
@@ -2709,10 +3364,10 @@ end
def test_sanity_integer
util_parser '1'
- assert_equal '1', @parser.get_tk.text
+ assert_equal '1', @parser.get_tk[:text]
util_parser '1.0'
- assert_equal '1.0', @parser.get_tk.text
+ assert_equal '1.0', @parser.get_tk[:text]
end
def test_sanity_interpolation
@@ -2721,7 +3376,7 @@ end
while tk = @parser.get_tk do last_tk = tk end
- assert_equal "\n", last_tk.text
+ assert_equal 'end', last_tk[:text]
end
# If you're writing code like this you're doing it wrong
@@ -2729,15 +3384,15 @@ end
def test_sanity_interpolation_crazy
util_parser '"#{"#{"a")}" if b}"'
- assert_equal '"#{"#{"a")}" if b}"', @parser.get_tk.text
- assert_equal RDoc::RubyToken::TkNL, @parser.get_tk.class
+ assert_equal '"#{"#{"a")}" if b}"', @parser.get_tk[:text]
+ assert_nil @parser.get_tk
end
def test_sanity_interpolation_curly
util_parser '%{ #{} }'
- assert_equal '%Q{ #{} }', @parser.get_tk.text
- assert_equal RDoc::RubyToken::TkNL, @parser.get_tk.class
+ assert_equal '%{ #{} }', @parser.get_tk[:text]
+ assert_nil @parser.get_tk
end
def test_sanity_interpolation_format
@@ -2830,9 +3485,10 @@ end
def test_scan_block_comment_notflush
##
#
- # The previous test assumes that between the =begin/=end blocs that there is
- # only one line, or minima formatting directives. This test tests for those
- # who use the =begin bloc with longer / more advanced formatting within.
+ # The previous test assumes that between the =begin/=end blocks that there
+ # is only one line, or minima formatting directives. This test tests for
+ # those who use the =begin bloc with longer / more advanced formatting
+ # within.
#
##
content = <<-CONTENT
@@ -2997,6 +3653,7 @@ end
foo = @top_level.modules.first
expected = [
+ RDoc::Comment.new('comment a', @top_level),
RDoc::Comment.new('comment b', @top_level)
]
@@ -3245,6 +3902,80 @@ end
assert c_b.singleton
end
+ def test_scan_visibility_count
+ util_parser <<-RUBY
+class C < Original::Base
+ class C2 < Original::Base
+ def m0() end
+ def m1() end
+
+ private
+
+ def m2() end
+ def m3() end
+ def m4() end
+ end
+end
+ RUBY
+
+ @parser.scan
+
+ c = @store.find_class_named 'C::C2'
+
+ private_method_count = c.method_list.count { |m| :private == m.visibility }
+ assert_equal 3, private_method_count
+
+ public_method_count = c.method_list.count { |m| :public == m.visibility }
+ assert_equal 2, public_method_count
+ end
+
+ def test_scan_constant_visibility
+ util_parser <<-RUBY
+class C
+ CONST_A = 123
+
+ CONST_B = 234
+ private_constant :CONST_B
+
+ CONST_C = 345
+ public_constant :CONST_C
+end
+ RUBY
+
+ @parser.scan
+
+ c = @store.find_class_named 'C'
+ const_a, const_b, const_c = c.constants.sort_by(&:name)
+
+ assert_equal 'CONST_A', const_a.name
+ assert_equal :public, const_a.visibility
+
+ assert_equal 'CONST_B', const_b.name
+ assert_equal :private, const_b.visibility
+
+ assert_equal 'CONST_C', const_c.name
+ assert_equal :public, const_c.visibility
+ end
+
+ def test_document_after_rescue_inside_paren
+ util_parser <<-RUBY
+class C
+ attr_accessor :sample if (1.inexistent_method rescue false)
+ # first
+ # second
+ def a
+ end
+end
+ RUBY
+
+ @parser.scan
+
+ c = @store.find_class_named 'C'
+
+ c_a = c.find_method_named 'a'
+ assert_equal "first\nsecond", c_a.comment.text
+ end
+
def test_singleton_method_via_eigenclass
util_parser <<-RUBY
class C
@@ -3288,21 +4019,6 @@ end
assert_equal 'there', baz.comment.text
end
- def tk klass, scan, line, char, name, text
- klass = RDoc::RubyToken.const_get "Tk#{klass.to_s.upcase}"
-
- token = if klass.instance_method(:initialize).arity == 3 then
- raise ArgumentError, "name not used for #{klass}" if name
- klass.new scan, line, char
- else
- klass.new scan, line, char, name
- end
-
- token.set_text text
-
- token
- end
-
def util_parser(content)
@parser = RDoc::Parser::Ruby.new @top_level, @filename, content, @options,
@stats
@@ -3315,4 +4031,242 @@ end
second_file_content, @options, @stats
end
+ def test_parse_const_third_party
+ util_parser <<-CLASS
+class A
+ true if B
+ true if B::C
+ true if B::C::D
+
+ module B
+ end
+end
+ CLASS
+
+ tk = @parser.get_tk
+
+ @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ a = @top_level.classes.first
+ assert_equal 'A', a.full_name
+
+ visible = @store.all_modules.reject { |mod| mod.suppressed? }
+ visible = visible.map { |mod| mod.full_name }
+
+ assert_equal ['A::B'], visible
+ end
+
+ def test_parse_const_alias_defined_elsewhere
+ util_parser <<-CLASS
+module A
+ Aliased = Defined
+end
+
+module A
+ class Defined
+ end
+end
+ CLASS
+
+ @parser.scan
+
+ a = @top_level.modules.first
+ assert_equal 'A', a.full_name
+ aliased = a.constants.first
+ assert_equal 'A::Aliased', aliased.full_name
+ assert_equal [], a.modules.map(&:full_name)
+ assert_equal ['A::Defined', 'A::Aliased'], a.classes.map(&:full_name)
+ assert_equal ['A::Aliased'], a.constants.map(&:full_name)
+
+ visible = @store.all_modules.reject { |mod| mod.suppressed? }
+ visible = visible.map { |mod| mod.full_name }
+
+ assert_equal ['A'], visible
+ end
+
+ def test_parse_const_alias_defined_far_away
+ util_parser <<-CLASS
+module A
+ Aliased = ::B::C::Defined
+end
+
+module B
+ module C
+ class Defined
+ end
+ end
+end
+ CLASS
+
+ @parser.scan
+
+ a = @top_level.modules.first
+ assert_equal 'A', a.full_name
+ assert_empty a.classes
+ assert_empty a.modules
+ assert_equal ['A::Aliased'], a.constants.map(&:full_name)
+
+ defined = @store.find_class_named 'B::C::Defined'
+ assert_equal 'B::C::Defined', defined.full_name
+
+ aliased = @store.find_class_named 'B::C::Aliased'
+ assert_equal 'B::C::Aliased', aliased.full_name
+
+ visible = @store.all_modules.reject { |mod| mod.suppressed? }
+ visible = visible.map { |mod| mod.full_name }
+
+ assert_equal ['A', 'B', 'B::C'], visible
+ end
+
+ def test_parse_include_by_dynamic_definition
+ util_parser <<-CLASS
+module A
+ class B
+ include(Module.new do
+ def e(m)
+ end
+ end)
+ end
+
+ class C
+ end
+
+ class D
+ end
+end
+ CLASS
+
+ @parser.scan
+
+ a = @store.find_module_named 'A'
+ assert_equal 'A', a.full_name
+ a_b = a.find_class_named 'B'
+ assert_equal 'A::B', a_b.full_name
+ a_c = a.find_class_named 'C'
+ assert_equal 'A::C', a_c.full_name
+ a_d = a.find_class_named 'D'
+ assert_equal 'A::D', a_d.full_name
+ end
+
+ def test_parse_include_by_dynamic_definition_without_paren
+ util_parser <<-CLASS
+module A
+ class B
+ include(Module.new do
+ def e m
+ end
+ end)
+ end
+
+ class C
+ end
+
+ class D
+ end
+end
+ CLASS
+
+ @parser.scan
+
+ a = @store.find_module_named 'A'
+ assert_equal 'A', a.full_name
+ a_b = a.find_class_named 'B'
+ assert_equal 'A::B', a_b.full_name
+ a_c = a.find_class_named 'C'
+ assert_equal 'A::C', a_c.full_name
+ a_d = a.find_class_named 'D'
+ assert_equal 'A::D', a_d.full_name
+ end
+
+ def test_parse_include_by_dynamic_definition_via_variable
+ util_parser <<-CLASS
+module A
+ class B
+ m = Module.new do
+ def e(m)
+ end
+ end
+ include m
+ end
+
+ class C
+ end
+
+ class D
+ end
+end
+ CLASS
+
+ @parser.scan
+
+ a = @store.find_module_named 'A'
+ assert_equal 'A', a.full_name
+ a_b = a.find_class_named 'B'
+ assert_equal 'A::B', a_b.full_name
+ a_c = a.find_class_named 'C'
+ assert_equal 'A::C', a_c.full_name
+ a_d = a.find_class_named 'D'
+ assert_equal 'A::D', a_d.full_name
+ end
+
+ def test_parse_include_by_dynamic_definition_with_brace
+ util_parser <<-CLASS
+module A
+ class B
+ extend(e {
+ def f(g)
+ end
+ })
+ end
+
+ class C
+ end
+
+ class D
+ end
+end
+ CLASS
+
+ @parser.scan
+
+ a = @store.find_module_named 'A'
+ assert_equal 'A', a.full_name
+ a_b = a.find_class_named 'B'
+ assert_equal 'A::B', a_b.full_name
+ a_c = a.find_class_named 'C'
+ assert_equal 'A::C', a_c.full_name
+ a_d = a.find_class_named 'D'
+ assert_equal 'A::D', a_d.full_name
+ end
+
+ def test_parse_include_by_dynamic_definition_directly
+ util_parser <<-CLASS
+module A
+ class B
+ include Module.new do
+ def e m
+ end
+ end
+ end
+
+ class C
+ end
+
+ class D
+ end
+end
+ CLASS
+
+ @parser.scan
+
+ a = @store.find_module_named 'A'
+ assert_equal 'A', a.full_name
+ a_b = a.find_class_named 'B'
+ assert_equal 'A::B', a_b.full_name
+ a_c = a.find_class_named 'C'
+ assert_equal 'A::C', a_c.full_name
+ a_d = a.find_class_named 'D'
+ assert_equal 'A::D', a_d.full_name
+ end
+
end
diff --git a/test/rdoc/test_rdoc_parser_simple.rb b/test/rdoc/test_rdoc_parser_simple.rb
index 0e2cf0d9a9..5bde34b746 100644
--- a/test/rdoc/test_rdoc_parser_simple.rb
+++ b/test/rdoc/test_rdoc_parser_simple.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocParserSimple < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_rd.rb b/test/rdoc/test_rdoc_rd.rb
index 938c5569f9..70bb94d24b 100644
--- a/test/rdoc/test_rdoc_rd.rb
+++ b/test/rdoc/test_rdoc_rd.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocRd < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_rd_block_parser.rb b/test/rdoc/test_rdoc_rd_block_parser.rb
index 18ec30db0f..0f0bc4f891 100644
--- a/test/rdoc/test_rdoc_rd_block_parser.rb
+++ b/test/rdoc/test_rdoc_rd_block_parser.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocRdBlockParser < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_rd_inline.rb b/test/rdoc/test_rdoc_rd_inline.rb
index d4448e4397..dbba164865 100644
--- a/test/rdoc/test_rdoc_rd_inline.rb
+++ b/test/rdoc/test_rdoc_rd_inline.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocRdInline < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_rd_inline_parser.rb b/test/rdoc/test_rdoc_rd_inline_parser.rb
index eb022b240f..3673de48e1 100644
--- a/test/rdoc/test_rdoc_rd_inline_parser.rb
+++ b/test/rdoc/test_rdoc_rd_inline_parser.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocRdInlineParser < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_rdoc.rb b/test/rdoc/test_rdoc_rdoc.rb
index 39f54555b5..123b1a4f87 100644
--- a/test/rdoc/test_rdoc_rdoc.rb
+++ b/test/rdoc/test_rdoc_rdoc.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocRDoc < RDoc::TestCase
@@ -40,6 +40,34 @@ class TestRDocRDoc < RDoc::TestCase
assert_equal 'title', store.title
end
+ def test_document_with_dry_run # functional test
+ options = RDoc::Options.new
+ options.files = [File.expand_path('../xref_data.rb', __FILE__)]
+ options.setup_generator 'darkfish'
+ options.main_page = 'MAIN_PAGE.rdoc'
+ options.root = Pathname File.expand_path('..', __FILE__)
+ options.title = 'title'
+ options.dry_run = true
+
+ rdoc = RDoc::RDoc.new
+
+ out = nil
+ temp_dir do
+ out, = capture_io do
+ rdoc.document options
+ end
+
+ refute File.directory? 'doc'
+ assert_equal rdoc, rdoc.store.rdoc
+ end
+ assert_includes out, '100%'
+
+ store = rdoc.store
+
+ assert_equal 'MAIN_PAGE.rdoc', store.main
+ assert_equal 'title', store.title
+ end
+
def test_gather_files
a = File.expand_path __FILE__
b = File.expand_path '../test_rdoc_text.rb', __FILE__
@@ -87,7 +115,7 @@ class TestRDocRDoc < RDoc::TestCase
def test_load_options_invalid
temp_dir do
- open '.rdoc_options', 'w' do |io|
+ File.open '.rdoc_options', 'w' do |io|
io.write "a: !ruby.yaml.org,2002:str |\nfoo"
end
@@ -109,17 +137,18 @@ class TestRDocRDoc < RDoc::TestCase
end
def test_normalized_file_list
+ test_path = File.expand_path(__FILE__)
files = temp_dir do |dir|
flag_file = @rdoc.output_flag_file dir
FileUtils.touch flag_file
- @rdoc.normalized_file_list [__FILE__, flag_file]
+ @rdoc.normalized_file_list [test_path, flag_file]
end
files = files.map { |file| File.expand_path file }
- assert_equal [File.expand_path(__FILE__)], files
+ assert_equal [test_path], files
end
def test_normalized_file_list_not_modified
@@ -152,13 +181,67 @@ class TestRDocRDoc < RDoc::TestCase
assert_match %r"#{dev}$", err
end
+ def test_normalized_file_list_with_dot_doc
+ expected_files = []
+ files = temp_dir do |dir|
+ a = File.expand_path('a.rb')
+ b = File.expand_path('b.rb')
+ c = File.expand_path('c.rb')
+ FileUtils.touch a
+ FileUtils.touch b
+ FileUtils.touch c
+
+ dot_doc = File.expand_path('.document')
+ FileUtils.touch dot_doc
+ open(dot_doc, 'w') do |f|
+ f.puts 'a.rb'
+ f.puts 'b.rb'
+ end
+ expected_files << a
+ expected_files << b
+
+ @rdoc.normalized_file_list [File.realpath(dir)]
+ end
+
+ files = files.map { |file| File.expand_path file }
+
+ assert_equal expected_files, files
+ end
+
+ def test_normalized_file_list_with_dot_doc_overridden_by_exclude_option
+ expected_files = []
+ files = temp_dir do |dir|
+ a = File.expand_path('a.rb')
+ b = File.expand_path('b.rb')
+ c = File.expand_path('c.rb')
+ FileUtils.touch a
+ FileUtils.touch b
+ FileUtils.touch c
+
+ dot_doc = File.expand_path('.document')
+ FileUtils.touch dot_doc
+ open(dot_doc, 'w') do |f|
+ f.puts 'a.rb'
+ f.puts 'b.rb'
+ end
+ expected_files << a
+
+ @rdoc.options.exclude = Regexp.new(['b.rb'].join('|'))
+ @rdoc.normalized_file_list [File.realpath(dir)]
+ end
+
+ files = files.map { |file| File.expand_path file }
+
+ assert_equal expected_files, files
+ end
+
def test_parse_file
@rdoc.store = RDoc::Store.new
temp_dir do |dir|
@rdoc.options.root = Pathname(Dir.pwd)
- open 'test.txt', 'w' do |io|
+ File.open 'test.txt', 'w' do |io|
io.puts 'hi'
end
@@ -189,11 +272,12 @@ class TestRDocRDoc < RDoc::TestCase
def test_parse_file_include_root
@rdoc.store = RDoc::Store.new
+ test_path = File.expand_path('..', __FILE__)
top_level = nil
temp_dir do |dir|
- @rdoc.options.parse %W[--root #{File.dirname(__FILE__)}]
+ @rdoc.options.parse %W[--root #{test_path}]
- open 'include.txt', 'w' do |io|
+ File.open 'include.txt', 'w' do |io|
io.puts ':include: test.txt'
end
@@ -214,7 +298,7 @@ class TestRDocRDoc < RDoc::TestCase
@rdoc.options.page_dir = Pathname('pages')
@rdoc.options.root = Pathname(Dir.pwd)
- open 'pages/test.txt', 'w' do |io|
+ File.open 'pages/test.txt', 'w' do |io|
io.puts 'hi'
end
@@ -233,7 +317,7 @@ class TestRDocRDoc < RDoc::TestCase
temp_dir do |dir|
@rdoc.options.root = Pathname(dir)
- open 'test.txt', 'w' do |io|
+ File.open 'test.txt', 'w' do |io|
io.puts 'hi'
end
@@ -266,6 +350,7 @@ class TestRDocRDoc < RDoc::TestCase
def test_parse_file_forbidden
skip 'chmod not supported' if Gem.win_platform?
+ skip "assumes that euid is not root" if Process.euid == 0
@rdoc.store = RDoc::Store.new
@@ -309,7 +394,7 @@ class TestRDocRDoc < RDoc::TestCase
def test_remove_unparseable_tags_emacs
temp_dir do
- open 'TAGS', 'wb' do |io| # emacs
+ File.open 'TAGS', 'wb' do |io| # emacs
io.write "\f\nlib/foo.rb,43\n"
end
@@ -323,7 +408,7 @@ class TestRDocRDoc < RDoc::TestCase
def test_remove_unparseable_tags_vim
temp_dir do
- open 'TAGS', 'w' do |io| # emacs
+ File.open 'TAGS', 'w' do |io| # emacs
io.write "!_TAG_"
end
@@ -335,6 +420,18 @@ class TestRDocRDoc < RDoc::TestCase
end
end
+ def test_remove_unparseable_CVE_2021_31799
+ skip 'for Un*x platforms' if Gem.win_platform?
+ temp_dir do
+ file_list = ['| touch evil.txt && echo tags']
+ file_list.each do |f|
+ FileUtils.touch f
+ end
+ assert_equal file_list, @rdoc.remove_unparseable(file_list)
+ assert_equal file_list, Dir.children('.')
+ end
+ end
+
def test_setup_output_dir
Dir.mktmpdir {|d|
path = File.join d, 'testdir'
@@ -362,7 +459,7 @@ class TestRDocRDoc < RDoc::TestCase
def test_setup_output_dir_exists
Dir.mktmpdir {|path|
- open @rdoc.output_flag_file(path), 'w' do |io|
+ File.open @rdoc.output_flag_file(path), 'w' do |io|
io.puts Time.at 0
io.puts "./lib/rdoc.rb\t#{Time.at 86400}"
end
@@ -376,7 +473,7 @@ class TestRDocRDoc < RDoc::TestCase
def test_setup_output_dir_exists_empty_created_rid
Dir.mktmpdir {|path|
- open @rdoc.output_flag_file(path), 'w' do end
+ File.open @rdoc.output_flag_file(path), 'w' do end
e = assert_raises RDoc::Error do
@rdoc.setup_output_dir path, false
@@ -437,6 +534,25 @@ class TestRDocRDoc < RDoc::TestCase
end
end
+ def test_update_output_dir_with_reproducible_time
+ Dir.mktmpdir do |d|
+ backup_epoch = ENV['SOURCE_DATE_EPOCH']
+ ruby_birthday = Time.parse 'Wed, 24 Feb 1993 21:00:00 +0900'
+ ENV['SOURCE_DATE_EPOCH'] = ruby_birthday.to_i.to_s
+
+ @rdoc.update_output_dir d, Time.now, {}
+
+ assert File.exist? "#{d}/created.rid"
+
+ f = File.open("#{d}/created.rid", 'r')
+ head_timestamp = Time.parse f.gets.chomp
+ f.close
+ assert_equal ruby_birthday, head_timestamp
+
+ ENV['SOURCE_DATE_EPOCH'] = backup_epoch
+ end
+ end
+
def test_normalized_file_list_removes_created_rid_dir
temp_dir do |d|
FileUtils.mkdir "doc"
diff --git a/test/rdoc/test_rdoc_require.rb b/test/rdoc/test_rdoc_require.rb
index cbedf5c8a4..46c225299a 100644
--- a/test/rdoc/test_rdoc_require.rb
+++ b/test/rdoc/test_rdoc_require.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
class TestRDocRequire < XrefTestCase
@@ -12,13 +12,13 @@ class TestRDocRequire < XrefTestCase
def test_initialize
assert_equal 'foo', @req.name
- req = RDoc::Require.new '"foo"', ''
+ RDoc::Require.new '"foo"', ''
assert_equal 'foo', @req.name
- req = RDoc::Require.new '\'foo\'', ''
+ RDoc::Require.new '\'foo\'', ''
assert_equal 'foo', @req.name
- req = RDoc::Require.new '|foo|', ''
+ RDoc::Require.new '|foo|', ''
assert_equal 'foo', @req.name, 'for fortran?'
end
diff --git a/test/rdoc/test_rdoc_ri_driver.rb b/test/rdoc/test_rdoc_ri_driver.rb
index 144e74f825..18dcb5b68a 100644
--- a/test/rdoc/test_rdoc_ri_driver.rb
+++ b/test/rdoc/test_rdoc_ri_driver.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocRIDriver < RDoc::TestCase
@@ -243,6 +243,32 @@ class TestRDocRIDriver < RDoc::TestCase
assert_equal expected, out
end
+ def test_add_method_that_is_alias_for_original
+ util_store
+
+ out = doc
+
+ @driver.add_method out, 'Qux#aliased'
+
+ expected =
+ doc(
+ head(1, 'Qux#aliased'),
+ blank_line,
+ para('(from ~/.rdoc)'),
+ rule(1),
+ blank_line,
+ para('alias comment'),
+ blank_line,
+ blank_line,
+ para('(This method is an alias for Qux#original.)'),
+ blank_line,
+ para('original comment'),
+ blank_line,
+ blank_line)
+
+ assert_equal expected, out
+ end
+
def test_add_method_attribute
util_store
@@ -282,7 +308,7 @@ class TestRDocRIDriver < RDoc::TestCase
assert_equal expected, out
end
- def test_add_method_overriden
+ def test_add_method_overridden
util_multi_store
out = doc
@@ -348,6 +374,22 @@ class TestRDocRIDriver < RDoc::TestCase
assert_equal expected, out
end
+ def test_output_width
+ @options[:width] = 10
+ driver = RDoc::RI::Driver.new @options
+
+ doc = @RM::Document.new
+ doc << @RM::IndentedParagraph.new(0, 'new, parse, foo, bar, baz')
+
+ out, = capture_io do
+ driver.display doc
+ end
+
+ expected = "new, parse, foo,\nbar, baz\n"
+
+ assert_equal expected, out
+ end
+
def test_add_method_list_interative
@options[:interactive] = true
driver = RDoc::RI::Driver.new @options
@@ -390,6 +432,7 @@ class TestRDocRIDriver < RDoc::TestCase
'Foo::Bar' => [@store1],
'Foo::Baz' => [@store1, @store2],
'Inc' => [@store1],
+ 'Qux' => [@store1],
}
classes = @driver.classes
@@ -646,7 +689,7 @@ class TestRDocRIDriver < RDoc::TestCase
assert_match %r%^=== Implementation from Foo%, out
end
- def test_display_method_overriden
+ def test_display_method_overridden
util_multi_store
out, = capture_io do
@@ -656,6 +699,21 @@ class TestRDocRIDriver < RDoc::TestCase
refute_match %r%must not be displayed%, out
end
+ def test_display_name
+ util_store
+
+ out, = capture_io do
+ assert_equal true, @driver.display_name('home:README.rdoc')
+ end
+
+ expected = <<-EXPECTED
+= README
+This is a README
+ EXPECTED
+
+ assert_equal expected, out
+ end
+
def test_display_name_not_found_class
util_store
@@ -923,6 +981,7 @@ Foo::Bar#bother
[@store1, 'Foo::Bar', 'Foo::Bar', :both, 'blah'],
[@store1, 'Foo::Baz', 'Foo::Baz', :both, 'blah'],
[@store1, 'Inc', 'Inc', :both, 'blah'],
+ [@store1, 'Qux', 'Qux', :both, 'blah'],
]
assert_equal expected, items
@@ -969,6 +1028,27 @@ Foo::Bar#bother
assert_equal 'nonexistent', e.name
end
+ def test_did_you_mean
+ skip 'skip test with did_you_men' unless defined? DidYouMean::SpellChecker
+
+ util_ancestors_store
+
+ e = assert_raises RDoc::RI::Driver::NotFoundError do
+ @driver.lookup_method 'Foo.i_methdo'
+ end
+ assert_equal "Nothing known about Foo.i_methdo\nDid you mean? i_method", e.message
+
+ e = assert_raises RDoc::RI::Driver::NotFoundError do
+ @driver.lookup_method 'Foo#i_methdo'
+ end
+ assert_equal "Nothing known about Foo#i_methdo\nDid you mean? i_method", e.message
+
+ e = assert_raises RDoc::RI::Driver::NotFoundError do
+ @driver.lookup_method 'Foo::i_methdo'
+ end
+ assert_equal "Nothing known about Foo::i_methdo\nDid you mean? c_method", e.message
+ end
+
def test_formatter
tty = Object.new
def tty.tty?() true; end
@@ -1035,7 +1115,7 @@ Foo::Bar#bother
@driver.list_known_classes
end
- assert_equal "Ambiguous\nExt\nFoo\nFoo::Bar\nFoo::Baz\nInc\n", out
+ assert_equal "Ambiguous\nExt\nFoo\nFoo::Bar\nFoo::Baz\nInc\nQux\n", out
end
def test_list_known_classes_name
@@ -1106,7 +1186,7 @@ Foo::Bar#bother
method = @driver.load_method(@store2, :instance_methods, 'Bar', '#',
'inherit')
- assert_equal nil, method
+ assert_nil method
end
def test_load_methods_matching
@@ -1211,7 +1291,7 @@ Foo::Bar#bother
assert_equal 'ruby', klass, 'ruby project'
assert_equal ':', type, 'ruby type'
- assert_equal nil, meth, 'ruby page'
+ assert_nil meth, 'ruby page'
end
def test_parse_name_page_extenson
@@ -1226,26 +1306,26 @@ Foo::Bar#bother
klass, type, meth = @driver.parse_name 'Foo'
assert_equal 'Foo', klass, 'Foo class'
- assert_equal nil, type, 'Foo type'
- assert_equal nil, meth, 'Foo method'
+ assert_nil type, 'Foo type'
+ assert_nil meth, 'Foo method'
klass, type, meth = @driver.parse_name 'Foo#'
assert_equal 'Foo', klass, 'Foo# class'
assert_equal '#', type, 'Foo# type'
- assert_equal nil, meth, 'Foo# method'
+ assert_nil meth, 'Foo# method'
klass, type, meth = @driver.parse_name 'Foo::'
assert_equal 'Foo', klass, 'Foo:: class'
assert_equal '::', type, 'Foo:: type'
- assert_equal nil, meth, 'Foo:: method'
+ assert_nil meth, 'Foo:: method'
klass, type, meth = @driver.parse_name 'Foo.'
assert_equal 'Foo', klass, 'Foo. class'
assert_equal '.', type, 'Foo. type'
- assert_equal nil, meth, 'Foo. method'
+ assert_nil meth, 'Foo. method'
klass, type, meth = @driver.parse_name 'Foo#Bar'
@@ -1270,14 +1350,14 @@ Foo::Bar#bother
klass, type, meth = @driver.parse_name 'Foo::Bar'
assert_equal 'Foo::Bar', klass, 'Foo::Bar class'
- assert_equal nil, type, 'Foo::Bar type'
- assert_equal nil, meth, 'Foo::Bar method'
+ assert_nil type, 'Foo::Bar type'
+ assert_nil meth, 'Foo::Bar method'
klass, type, meth = @driver.parse_name 'Foo::Bar#'
assert_equal 'Foo::Bar', klass, 'Foo::Bar# class'
assert_equal '#', type, 'Foo::Bar# type'
- assert_equal nil, meth, 'Foo::Bar# method'
+ assert_nil meth, 'Foo::Bar# method'
klass, type, meth = @driver.parse_name 'Foo::Bar#baz'
@@ -1455,10 +1535,19 @@ Foo::Bar#bother
@inherit = @cFoo.add_method RDoc::AnyMethod.new(nil, 'inherit')
@inherit.record_location @top_level
- # overriden by Bar in multi_store
- @overriden = @cFoo.add_method RDoc::AnyMethod.new(nil, 'override')
- @overriden.comment = 'must not be displayed in Bar#override'
- @overriden.record_location @top_level
+ # overridden by Bar in multi_store
+ @overridden = @cFoo.add_method RDoc::AnyMethod.new(nil, 'override')
+ @overridden.comment = 'must not be displayed in Bar#override'
+ @overridden.record_location @top_level
+
+ @cQux = @top_level.add_class RDoc::NormalClass, 'Qux'
+
+ @original = @cQux.add_method RDoc::AnyMethod.new(nil, 'original')
+ @original.comment = 'original comment'
+ @original.record_location @top_level
+
+ @aliased = @original.add_alias RDoc::Alias.new(nil, 'original', 'aliased', 'alias comment'), @cQux
+ @aliased.record_location @top_level
@store1.save
diff --git a/test/rdoc/test_rdoc_ri_paths.rb b/test/rdoc/test_rdoc_ri_paths.rb
index 06968b5510..eb9b7fc93c 100644
--- a/test/rdoc/test_rdoc_ri_paths.rb
+++ b/test/rdoc/test_rdoc_ri_paths.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocRIPaths < RDoc::TestCase
@@ -22,7 +22,7 @@ class TestRDocRIPaths < RDoc::TestCase
specs.each do |spec|
spec.loaded_from = spec.spec_file
- open spec.spec_file, 'w' do |file|
+ File.open spec.spec_file, 'w' do |file|
file.write spec.to_ruby_for_cache
end
diff --git a/test/rdoc/test_rdoc_ruby_lex.rb b/test/rdoc/test_rdoc_ruby_lex.rb
deleted file mode 100644
index 6356fa2894..0000000000
--- a/test/rdoc/test_rdoc_ruby_lex.rb
+++ /dev/null
@@ -1,422 +0,0 @@
-# coding: UTF-8
-# frozen_string_literal: false
-
-require 'rdoc/test_case'
-
-class TestRDocRubyLex < RDoc::TestCase
-
- def setup
- @TK = RDoc::RubyToken
- end
-
- def test_class_tokenize
- tokens = RDoc::RubyLex.tokenize "def x() end", nil
-
- expected = [
- @TK::TkDEF .new( 0, 1, 0, "def"),
- @TK::TkSPACE .new( 3, 1, 3, " "),
- @TK::TkIDENTIFIER.new( 4, 1, 4, "x"),
- @TK::TkLPAREN .new( 5, 1, 5, "("),
- @TK::TkRPAREN .new( 6, 1, 6, ")"),
- @TK::TkSPACE .new( 7, 1, 7, " "),
- @TK::TkEND .new( 8, 1, 8, "end"),
- @TK::TkNL .new(11, 1, 11, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize___END__
- tokens = RDoc::RubyLex.tokenize '__END__', nil
-
- expected = [
- @TK::TkEND_OF_SCRIPT.new(0, 1, 0, '__END__'),
- @TK::TkNL .new(7, 1, 7, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_character_literal
- tokens = RDoc::RubyLex.tokenize "?\\", nil
-
- expected = [
- @TK::TkCHAR.new( 0, 1, 0, "?\\"),
- @TK::TkNL .new( 2, 1, 2, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_def_heredoc
- tokens = RDoc::RubyLex.tokenize <<-'RUBY', nil
-def x
- <<E
-Line 1
-Line 2
-E
-end
- RUBY
-
- expected = [
- @TK::TkDEF .new( 0, 1, 0, 'def'),
- @TK::TkSPACE .new( 3, 1, 3, ' '),
- @TK::TkIDENTIFIER.new( 4, 1, 4, 'x'),
- @TK::TkNL .new( 5, 1, 5, "\n"),
- @TK::TkSPACE .new( 6, 2, 0, ' '),
- @TK::TkHEREDOC .new( 8, 2, 2,
- %Q{<<E\nLine 1\nLine 2\nE}),
- @TK::TkNL .new(27, 5, 28, "\n"),
- @TK::TkEND .new(28, 6, 0, 'end'),
- @TK::TkNL .new(31, 6, 28, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_hash_symbol
- tokens = RDoc::RubyLex.tokenize '{ class:"foo" }', nil
-
- expected = [
- @TK::TkLBRACE .new( 0, 1, 0, '{'),
- @TK::TkSPACE .new( 1, 1, 1, ' '),
- @TK::TkIDENTIFIER.new( 2, 1, 2, 'class'),
- @TK::TkSYMBEG .new( 7, 1, 7, ':'),
- @TK::TkSTRING .new( 8, 1, 8, '"foo"'),
- @TK::TkSPACE .new(13, 1, 13, ' '),
- @TK::TkRBRACE .new(14, 1, 14, '}'),
- @TK::TkNL .new(15, 1, 15, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_heredoc_CR_NL
- tokens = RDoc::RubyLex.tokenize <<-RUBY, nil
-string = <<-STRING\r
-Line 1\r
-Line 2\r
- STRING\r
- RUBY
-
- expected = [
- @TK::TkIDENTIFIER.new( 0, 1, 0, 'string'),
- @TK::TkSPACE .new( 6, 1, 6, ' '),
- @TK::TkASSIGN .new( 7, 1, 7, '='),
- @TK::TkSPACE .new( 8, 1, 8, ' '),
- @TK::TkHEREDOC .new( 9, 1, 9,
- %Q{<<-STRING\nLine 1\nLine 2\n STRING}),
- @TK::TkSPACE .new(44, 4, 45, "\r"),
- @TK::TkNL .new(45, 4, 46, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_heredoc_call
- tokens = RDoc::RubyLex.tokenize <<-'RUBY', nil
-string = <<-STRING.chomp
-Line 1
-Line 2
- STRING
- RUBY
-
- expected = [
- @TK::TkIDENTIFIER.new( 0, 1, 0, 'string'),
- @TK::TkSPACE .new( 6, 1, 6, ' '),
- @TK::TkASSIGN .new( 7, 1, 7, '='),
- @TK::TkSPACE .new( 8, 1, 8, ' '),
- @TK::TkSTRING .new( 9, 1, 9, %Q{"Line 1\nLine 2\n"}),
- @TK::TkDOT .new(41, 4, 42, '.'),
- @TK::TkIDENTIFIER.new(42, 4, 43, 'chomp'),
- @TK::TkNL .new(47, 4, 48, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_heredoc_indent
- tokens = RDoc::RubyLex.tokenize <<-'RUBY', nil
-string = <<-STRING
-Line 1
-Line 2
- STRING
- RUBY
-
- expected = [
- @TK::TkIDENTIFIER.new( 0, 1, 0, 'string'),
- @TK::TkSPACE .new( 6, 1, 6, ' '),
- @TK::TkASSIGN .new( 7, 1, 7, '='),
- @TK::TkSPACE .new( 8, 1, 8, ' '),
- @TK::TkHEREDOC .new( 9, 1, 9,
- %Q{<<-STRING\nLine 1\nLine 2\n STRING}),
- @TK::TkNL .new(41, 4, 42, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_heredoc_missing_end
- e = assert_raises RDoc::RubyLex::Error do
- RDoc::RubyLex.tokenize <<-'RUBY', nil
->> string1 = <<-TXT
->" That's swell
->" TXT
- RUBY
- end
-
- assert_equal 'Missing terminating TXT for string', e.message
- end
-
- def test_class_tokenize_heredoc_percent_N
- tokens = RDoc::RubyLex.tokenize <<-'RUBY', nil
-a b <<-U
-%N
-U
- RUBY
-
- expected = [
- @TK::TkIDENTIFIER.new( 0, 1, 0, 'a'),
- @TK::TkSPACE .new( 1, 1, 1, ' '),
- @TK::TkIDENTIFIER.new( 2, 1, 2, 'b'),
- @TK::TkSPACE .new( 3, 1, 3, ' '),
- @TK::TkHEREDOC .new( 4, 1, 4, %Q{<<-U\n%N\nU}),
- @TK::TkNL .new(13, 3, 14, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_identifier_high_unicode
- tokens = RDoc::RubyLex.tokenize 'ð–’', nil
-
- expected = @TK::TkIDENTIFIER.new(0, 1, 0, 'ð–’')
-
- assert_equal expected, tokens.first
- end
-
- def test_class_tokenize_percent_1
- tokens = RDoc::RubyLex.tokenize 'v%10==10', nil
-
- expected = [
- @TK::TkIDENTIFIER.new(0, 1, 0, 'v'),
- @TK::TkMOD.new( 1, 1, 1, '%'),
- @TK::TkINTEGER.new( 2, 1, 2, '10'),
- @TK::TkEQ.new( 4, 1, 4, '=='),
- @TK::TkINTEGER.new( 6, 1, 6, '10'),
- @TK::TkNL.new( 8, 1, 8, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_percent_r
- tokens = RDoc::RubyLex.tokenize '%r[hi]', nil
-
- expected = [
- @TK::TkREGEXP.new( 0, 1, 0, '%r[hi]'),
- @TK::TkNL .new( 6, 1, 6, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_percent_w
- tokens = RDoc::RubyLex.tokenize '%w[hi]', nil
-
- expected = [
- @TK::TkDSTRING.new( 0, 1, 0, '%w[hi]'),
- @TK::TkNL .new( 6, 1, 6, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_percent_w_quote
- tokens = RDoc::RubyLex.tokenize '%w"hi"', nil
-
- expected = [
- @TK::TkDSTRING.new( 0, 1, 0, '%w"hi"'),
- @TK::TkNL .new( 6, 1, 6, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_regexp
- tokens = RDoc::RubyLex.tokenize "/hay/", nil
-
- expected = [
- @TK::TkREGEXP.new( 0, 1, 0, "/hay/"),
- @TK::TkNL .new( 5, 1, 5, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_regexp_options
- tokens = RDoc::RubyLex.tokenize "/hAY/i", nil
-
- expected = [
- @TK::TkREGEXP.new( 0, 1, 0, "/hAY/i"),
- @TK::TkNL .new( 6, 1, 6, "\n"),
- ]
-
- assert_equal expected, tokens
-
- tokens = RDoc::RubyLex.tokenize "/hAY/ix", nil
-
- expected = [
- @TK::TkREGEXP.new( 0, 1, 0, "/hAY/ix"),
- @TK::TkNL .new( 7, 1, 7, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_regexp_backref
- tokens = RDoc::RubyLex.tokenize "/[csh](..) [csh]\\1 in/", nil
-
- expected = [
- @TK::TkREGEXP.new( 0, 1, 0, "/[csh](..) [csh]\\1 in/"),
- @TK::TkNL .new(22, 1, 22, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_regexp_escape
- tokens = RDoc::RubyLex.tokenize "/\\//", nil
-
- expected = [
- @TK::TkREGEXP.new( 0, 1, 0, "/\\//"),
- @TK::TkNL .new( 4, 1, 4, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_string
- tokens = RDoc::RubyLex.tokenize "'hi'", nil
-
- expected = [
- @TK::TkSTRING.new( 0, 1, 0, "'hi'"),
- @TK::TkNL .new( 4, 1, 4, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_class_tokenize_string_escape
- tokens = RDoc::RubyLex.tokenize '"\\n"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\n\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\r"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\r\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\f"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\f\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\\\"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\\\\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\t"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\t\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\v"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\v\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\a"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\a\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\e"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\e\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\b"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\b\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\s"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\s\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\d"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\d\""), tokens.first
-
- end
-
- def test_class_tokenize_string_escape_control
- tokens = RDoc::RubyLex.tokenize '"\\C-a"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\C-a\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\c\\a"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\c\\a\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\C-\\M-a"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\C-\\M-a\""), tokens.first
- end
-
- def test_class_tokenize_string_escape_meta
- tokens = RDoc::RubyLex.tokenize '"\\M-a"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\M-a\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\M-\\C-a"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\M-\\C-a\""), tokens.first
- end
-
- def test_class_tokenize_string_escape_hexadecimal
- tokens = RDoc::RubyLex.tokenize '"\\x0"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\x0\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\x00"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\x00\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\x000"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\x000\""), tokens.first
- end
-
- def test_class_tokenize_string_escape_octal
- tokens = RDoc::RubyLex.tokenize '"\\0"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\0\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\00"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\00\""), tokens.first
-
- tokens = RDoc::RubyLex.tokenize '"\\000"', nil
- assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\000\""), tokens.first
- end
-
- def test_class_tokenize_symbol
- tokens = RDoc::RubyLex.tokenize 'scope module: :v1', nil
-
- expected = [
- @TK::TkIDENTIFIER.new( 0, 1, 0, 'scope'),
- @TK::TkSPACE .new( 5, 1, 5, ' '),
- @TK::TkIDENTIFIER.new( 6, 1, 6, 'module'),
- @TK::TkCOLON .new(12, 1, 12, ':'),
- @TK::TkSPACE .new(13, 1, 13, ' '),
- @TK::TkSYMBEG .new(14, 1, 14, ':'),
- @TK::TkIDENTIFIER.new(15, 1, 15, 'v1'),
- @TK::TkNL .new(17, 1, 17, "\n"),
- ]
-
- assert_equal expected, tokens
- end
-
- def test_unary_minus
- ruby_lex = RDoc::RubyLex.new("-1", nil)
- assert_equal("-1", ruby_lex.token.value)
-
- ruby_lex = RDoc::RubyLex.new("a[-2]", nil)
- 2.times { ruby_lex.token } # skip "a" and "["
- assert_equal("-2", ruby_lex.token.value)
-
- ruby_lex = RDoc::RubyLex.new("a[0..-12]", nil)
- 4.times { ruby_lex.token } # skip "a", "[", "0", and ".."
- assert_equal("-12", ruby_lex.token.value)
-
- ruby_lex = RDoc::RubyLex.new("0+-0.1", nil)
- 2.times { ruby_lex.token } # skip "0" and "+"
- assert_equal("-0.1", ruby_lex.token.value)
- end
-
-end
-
diff --git a/test/rdoc/test_rdoc_ruby_token.rb b/test/rdoc/test_rdoc_ruby_token.rb
deleted file mode 100644
index 4638606ef3..0000000000
--- a/test/rdoc/test_rdoc_ruby_token.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
-
-class TestRDocRubyToken < RDoc::TestCase
-
- def test_Token_text
- token = RDoc::RubyToken::Token.new 0, 0, 0, 'text'
-
- assert_equal 'text', token.text
- end
-
- def test_TkOp_name
- token = RDoc::RubyToken::TkOp.new 0, 0, 0, '&'
-
- assert_equal '&', token.text
- assert_equal '&', token.name
- end
-
-end
-
diff --git a/test/rdoc/test_rdoc_rubygems_hook.rb b/test/rdoc/test_rdoc_rubygems_hook.rb
index ee16f3d95c..ff0c29ae1b 100644
--- a/test/rdoc/test_rdoc_rubygems_hook.rb
+++ b/test/rdoc/test_rdoc_rubygems_hook.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'rubygems/test_case'
require 'rdoc/rubygems_hook'
@@ -200,6 +200,8 @@ class TestRDocRubygemsHook < Gem::TestCase
def test_remove_unwritable
skip 'chmod not supported' if Gem.win_platform?
+ skip "assumes that euid is not root" if Process.euid == 0
+
FileUtils.mkdir_p @a.base_dir
FileUtils.chmod 0, @a.base_dir
@@ -228,6 +230,8 @@ class TestRDocRubygemsHook < Gem::TestCase
def test_setup_unwritable
skip 'chmod not supported' if Gem.win_platform?
+ skip "assumes that euid is not root" if Process.euid == 0
+
FileUtils.mkdir_p @a.doc_dir
FileUtils.chmod 0, @a.doc_dir
diff --git a/test/rdoc/test_rdoc_servlet.rb b/test/rdoc/test_rdoc_servlet.rb
index 947d8c896c..0a197a31b6 100644
--- a/test/rdoc/test_rdoc_servlet.rb
+++ b/test/rdoc/test_rdoc_servlet.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocServlet < RDoc::TestCase
@@ -69,7 +69,7 @@ class TestRDocServlet < RDoc::TestCase
FileUtils.mkdir 'css'
now = Time.now
- open 'css/rdoc.css', 'w' do |io| io.write 'h1 { color: red }' end
+ File.open 'css/rdoc.css', 'w' do |io| io.write 'h1 { color: red }' end
File.utime now, now, 'css/rdoc.css'
@s.asset_dirs[:darkfish] = '.'
@@ -143,7 +143,7 @@ class TestRDocServlet < RDoc::TestCase
@s.asset_dirs[:darkfish] = '.'
- @req.path = '/mount/path/css/rdoc.css'
+ @req.path = '/mount/path/css/rdoc.css'.dup
@s.do_GET @req, @res
@@ -224,8 +224,7 @@ class TestRDocServlet < RDoc::TestCase
generator = @s.generator_for store
- readme = store.add_file 'README.rdoc'
- readme.parser = RDoc::Parser::Simple
+ readme = store.add_file 'README.rdoc', parser: RDoc::Parser::Simple
@s.documentation_page store, generator, 'README_rdoc.html', @req, @res
diff --git a/test/rdoc/test_rdoc_single_class.rb b/test/rdoc/test_rdoc_single_class.rb
index 85a99d2229..e4123cb9bc 100644
--- a/test/rdoc/test_rdoc_single_class.rb
+++ b/test/rdoc/test_rdoc_single_class.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocSingleClass < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_stats.rb b/test/rdoc/test_rdoc_stats.rb
index 34e1417783..cac30d15aa 100644
--- a/test/rdoc/test_rdoc_stats.rb
+++ b/test/rdoc/test_rdoc_stats.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocStats < RDoc::TestCase
diff --git a/test/rdoc/test_rdoc_store.rb b/test/rdoc/test_rdoc_store.rb
index 4082df71ea..5a884c0b5f 100644
--- a/test/rdoc/test_rdoc_store.rb
+++ b/test/rdoc/test_rdoc_store.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
class TestRDocStore < XrefTestCase
@@ -14,8 +14,7 @@ class TestRDocStore < XrefTestCase
@top_level = @s.add_file 'file.rb'
- @page = @s.add_file 'README.txt'
- @page.parser = RDoc::Parser::Simple
+ @page = @s.add_file 'README.txt', parser: RDoc::Parser::Simple
@page.comment = RDoc::Comment.new 'This is a page', @page
@klass = @top_level.add_class RDoc::NormalClass, 'Object'
@@ -146,7 +145,7 @@ class TestRDocStore < XrefTestCase
end
def test_add_file_relative
- top_level = @store.add_file 'path/file.rb', 'file.rb'
+ top_level = @store.add_file 'path/file.rb', relative_name: 'file.rb'
assert_kind_of RDoc::TopLevel, top_level
assert_equal @store, top_level.store
@@ -162,7 +161,7 @@ class TestRDocStore < XrefTestCase
def test_all_classes_and_modules
expected = %w[
- C1 C2 C2::C3 C2::C3::H1 C3 C3::H1 C3::H2 C4 C4::C4 C5 C5::C1
+ C1 C2 C2::C3 C2::C3::H1 C3 C3::H1 C3::H2 C4 C4::C4 C5 C5::C1 C6 C7 C8 C8::S1 C9 C9::A C9::B
Child
M1 M1::M2
Parent
@@ -213,7 +212,7 @@ class TestRDocStore < XrefTestCase
def test_classes
expected = %w[
- C1 C2 C2::C3 C2::C3::H1 C3 C3::H1 C3::H2 C4 C4::C4 C5 C5::C1
+ C1 C2 C2::C3 C2::C3::H1 C3 C3::H1 C3::H2 C4 C4::C4 C5 C5::C1 C6 C7 C8 C8::S1 C9 C9::A C9::B
Child
Parent
]
@@ -222,7 +221,8 @@ class TestRDocStore < XrefTestCase
end
def test_complete
- @c2.add_module_alias @c2_c3, 'A1', @top_level
+ a1 = RDoc::Constant.new 'A1', '', ''
+ @c2.add_module_alias @c2_c3, @c2_c3.name, a1, @top_level
@store.complete :public
@@ -309,8 +309,7 @@ class TestRDocStore < XrefTestCase
end
def test_find_text_page
- page = @store.add_file 'PAGE.txt'
- page.parser = RDoc::Parser::Simple
+ page = @store.add_file 'PAGE.txt', parser: RDoc::Parser::Simple
assert_nil @store.find_text_page 'no such page'
@@ -407,7 +406,7 @@ class TestRDocStore < XrefTestCase
Dir.mkdir @tmpdir
- open File.join(@tmpdir, 'cache.ri'), 'wb' do |io|
+ File.open File.join(@tmpdir, 'cache.ri'), 'wb' do |io|
Marshal.dump cache, io
end
@@ -441,7 +440,7 @@ class TestRDocStore < XrefTestCase
Dir.mkdir @tmpdir
- open File.join(@tmpdir, 'cache.ri'), 'wb' do |io|
+ File.open File.join(@tmpdir, 'cache.ri'), 'wb' do |io|
Marshal.dump cache, io
end
@@ -490,7 +489,7 @@ class TestRDocStore < XrefTestCase
Dir.mkdir @tmpdir
- open File.join(@tmpdir, 'cache.ri'), 'wb' do |io|
+ File.open File.join(@tmpdir, 'cache.ri'), 'wb' do |io|
Marshal.dump cache, io
end
@@ -524,6 +523,15 @@ class TestRDocStore < XrefTestCase
assert_includes @s.classes_hash, 'Object'
end
+ def test_load_single_class
+ @s.save_class @c8_s1
+ @s.classes_hash.clear
+
+ assert_equal @c8_s1, @s.load_class('C8::S1')
+
+ assert_includes @s.classes_hash, 'C8::S1'
+ end
+
def test_load_method
@s.save_method @klass, @meth_bang
@@ -538,7 +546,7 @@ class TestRDocStore < XrefTestCase
file = @s.method_file @klass.full_name, @meth.full_name
- open file, 'wb' do |io|
+ File.open file, 'wb' do |io|
io.write "\x04\bU:\x14RDoc::AnyMethod[\x0Fi\x00I" +
"\"\vmethod\x06:\x06EF\"\x11Klass#method0:\vpublic" +
"o:\eRDoc::Markup::Document\x06:\v@parts[\x06" +
@@ -563,7 +571,7 @@ class TestRDocStore < XrefTestCase
end
def test_main
- assert_equal nil, @s.main
+ assert_nil @s.main
@s.main = 'README.txt'
@@ -591,8 +599,7 @@ class TestRDocStore < XrefTestCase
end
def test_page
- page = @store.add_file 'PAGE.txt'
- page.parser = RDoc::Parser::Simple
+ page = @store.add_file 'PAGE.txt', parser: RDoc::Parser::Simple
assert_nil @store.page 'no such page'
@@ -633,7 +640,7 @@ class TestRDocStore < XrefTestCase
expected[:ancestors]['Object'] = %w[BasicObject]
- open File.join(@tmpdir, 'cache.ri'), 'rb' do |io|
+ File.open File.join(@tmpdir, 'cache.ri'), 'rb' do |io|
cache = Marshal.load io.read
assert_equal expected, cache
@@ -701,7 +708,7 @@ class TestRDocStore < XrefTestCase
expected[:ancestors]['Object'] = %w[BasicObject]
- open File.join(@tmpdir, 'cache.ri'), 'rb' do |io|
+ File.open File.join(@tmpdir, 'cache.ri'), 'rb' do |io|
cache = Marshal.load io.read
assert_equal expected, cache
@@ -981,7 +988,7 @@ class TestRDocStore < XrefTestCase
end
def test_title
- assert_equal nil, @s.title
+ assert_nil @s.title
@s.title = 'rdoc'
diff --git a/test/rdoc/test_rdoc_task.rb b/test/rdoc/test_rdoc_task.rb
index bb5d2ae134..3ce8529aef 100644
--- a/test/rdoc/test_rdoc_task.rb
+++ b/test/rdoc/test_rdoc_task.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
begin
require 'rake'
rescue LoadError
diff --git a/test/rdoc/test_rdoc_text.rb b/test/rdoc/test_rdoc_text.rb
index 2eb5ad69c7..2669766e71 100644
--- a/test/rdoc/test_rdoc_text.rb
+++ b/test/rdoc/test_rdoc_text.rb
@@ -1,7 +1,7 @@
-# coding: utf-8
-# frozen_string_literal: false
+# frozen_string_literal: true
-require 'rdoc/test_case'
+require 'minitest_helper'
+require 'timeout'
class TestRDocText < RDoc::TestCase
@@ -62,7 +62,7 @@ class TestRDocText < RDoc::TestCase
def test_expand_tabs_encoding
inn = "hello\ns\tdave"
- inn.force_encoding Encoding::BINARY
+ inn = RDoc::Encoding.change_encoding inn, Encoding::BINARY
out = expand_tabs inn
@@ -96,7 +96,7 @@ The comments associated with
The comments associated with
TEXT
- text.force_encoding Encoding::US_ASCII
+ text = RDoc::Encoding.change_encoding text, Encoding::US_ASCII
expected = <<-EXPECTED
@@ -259,8 +259,7 @@ paragraph will be cut off some point after the one-hundredth character.
TEXT
expected = <<-EXPECTED
-<p>This is one-hundred characters or more of text in a single paragraph. This
-paragraph will be cut off …
+<p>This is one-hundred characters or more of text in a single paragraph. This paragraph will be cut off …
EXPECTED
assert_equal expected, snippet(text)
@@ -304,7 +303,7 @@ paragraph will be cut off …
# The comments associated with
TEXT
- text.force_encoding Encoding::CP852
+ text = RDoc::Encoding.change_encoding text, Encoding::CP852
expected = <<-EXPECTED
@@ -333,7 +332,7 @@ paragraph will be cut off …
assert_equal Encoding::UTF_8, ''.encoding, 'Encoding sanity check'
text = " \n"
- text.force_encoding Encoding::US_ASCII
+ text = RDoc::Encoding.change_encoding text, Encoding::US_ASCII
stripped = strip_newlines text
@@ -378,6 +377,32 @@ paragraph will be cut off …
assert_equal expected, strip_stars(text)
end
+ def test_strip_stars_document_method_special
+ text = <<-TEXT
+/*
+ * Document-method: Zlib::GzipFile#mtime=
+ * Document-method: []
+ * Document-method: `
+ * Document-method: |
+ * Document-method: &
+ * Document-method: <=>
+ * Document-method: =~
+ * Document-method: +
+ * Document-method: -
+ * Document-method: +@
+ *
+ * A comment
+ */
+ TEXT
+
+ expected = <<-EXPECTED
+
+ A comment
+ EXPECTED
+
+ assert_equal expected, strip_stars(text)
+ end
+
def test_strip_stars_encoding
text = <<-TEXT
/*
@@ -387,7 +412,7 @@ paragraph will be cut off …
*/
TEXT
- text.force_encoding Encoding::CP852
+ text = RDoc::Encoding.change_encoding text, Encoding::CP852
expected = <<-EXPECTED
@@ -411,7 +436,7 @@ paragraph will be cut off …
*/
TEXT
- text.force_encoding Encoding::BINARY
+ text = RDoc::Encoding.change_encoding text, Encoding::BINARY
expected = <<-EXPECTED
diff --git a/test/rdoc/test_rdoc_token_stream.rb b/test/rdoc/test_rdoc_token_stream.rb
index 5ed7b1d1a9..c946d0f1ef 100644
--- a/test/rdoc/test_rdoc_token_stream.rb
+++ b/test/rdoc/test_rdoc_token_stream.rb
@@ -1,21 +1,21 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocTokenStream < RDoc::TestCase
def test_class_to_html
tokens = [
- RDoc::RubyToken::TkCONSTANT. new(0, 0, 0, 'CONSTANT'),
- RDoc::RubyToken::TkDEF. new(0, 0, 0, 'KW'),
- RDoc::RubyToken::TkIVAR. new(0, 0, 0, 'IVAR'),
- RDoc::RubyToken::TkOp. new(0, 0, 0, 'Op'),
- RDoc::RubyToken::TkId. new(0, 0, 0, 'Id'),
- RDoc::RubyToken::TkNode. new(0, 0, 0, 'Node'),
- RDoc::RubyToken::TkCOMMENT. new(0, 0, 0, 'COMMENT'),
- RDoc::RubyToken::TkREGEXP. new(0, 0, 0, 'REGEXP'),
- RDoc::RubyToken::TkSTRING. new(0, 0, 0, 'STRING'),
- RDoc::RubyToken::TkVal. new(0, 0, 0, 'Val'),
- RDoc::RubyToken::TkBACKSLASH.new(0, 0, 0, '\\'),
+ { :line_no => 0, :char_no => 0, :kind => :on_const, :text => 'CONSTANT' },
+ { :line_no => 0, :char_no => 0, :kind => :on_kw, :text => 'KW' },
+ { :line_no => 0, :char_no => 0, :kind => :on_ivar, :text => 'IVAR' },
+ { :line_no => 0, :char_no => 0, :kind => :on_op, :text => 'Op' },
+ { :line_no => 0, :char_no => 0, :kind => :on_ident, :text => 'Id' },
+ { :line_no => 0, :char_no => 0, :kind => :on_backref, :text => 'Node' },
+ { :line_no => 0, :char_no => 0, :kind => :on_comment, :text => 'COMMENT' },
+ { :line_no => 0, :char_no => 0, :kind => :on_regexp, :text => 'REGEXP' },
+ { :line_no => 0, :char_no => 0, :kind => :on_tstring, :text => 'STRING' },
+ { :line_no => 0, :char_no => 0, :kind => :on_int, :text => 'Val' },
+ { :line_no => 0, :char_no => 0, :kind => :on_unknown, :text => '\\' }
]
expected = [
@@ -39,5 +39,20 @@ class TestRDocTokenStream < RDoc::TestCase
assert_equal '', RDoc::TokenStream.to_html([])
end
+ def test_tokens_to_s
+ foo = Class.new do
+ include RDoc::TokenStream
+
+ def initialize
+ @token_stream = [
+ { line_no: 0, char_no: 0, kind: :on_ident, text: "foo" },
+ { line_no: 0, char_no: 0, kind: :on_sp, text: " " },
+ { line_no: 0, char_no: 0, kind: :on_tstring, text: "'bar'" },
+ ]
+ end
+ end.new
+
+ assert_equal "foo 'bar'", foo.tokens_to_s
+ end
end
diff --git a/test/rdoc/test_rdoc_tom_doc.rb b/test/rdoc/test_rdoc_tom_doc.rb
index 7076edfa72..27a3e6f178 100644
--- a/test/rdoc/test_rdoc_tom_doc.rb
+++ b/test/rdoc/test_rdoc_tom_doc.rb
@@ -1,5 +1,5 @@
-# frozen_string_literal: false
-require 'rdoc/test_case'
+# frozen_string_literal: true
+require 'minitest_helper'
class TestRDocTomDoc < RDoc::TestCase
@@ -131,7 +131,7 @@ here - something
def test_parse_multiline_paragraph
text = "Public: Do some stuff\n"
- text << "On a new line\n"
+ text += "On a new line\n"
expected =
doc(
@@ -301,6 +301,44 @@ Returns another thing
assert_equal expected, @TD.parse(text)
end
+ def test_parse_returns_with_raises
+ text = <<-TEXT
+Do some stuff
+
+Returns a thing
+Raises ArgumentError when stuff
+Raises StandardError when stuff
+ TEXT
+ expected =
+ doc(
+ para('Do some stuff'),
+ blank_line,
+ head(3, 'Returns'),
+ blank_line,
+ para('Returns a thing'),
+ para('Raises ArgumentError when stuff'),
+ para('Raises StandardError when stuff'))
+
+ assert_equal expected, @TD.parse(text)
+ end
+
+ def test_parse_raises_without_returns
+ text = <<-TEXT
+Do some stuff
+
+Raises ArgumentError when stuff
+ TEXT
+ expected =
+ doc(
+ para('Do some stuff'),
+ blank_line,
+ head(3, 'Returns'),
+ blank_line,
+ para('Raises ArgumentError when stuff'))
+
+ assert_equal expected, @TD.parse(text)
+ end
+
def test_parse_returns_multiline
text = <<-TEXT
Do some stuff
@@ -320,6 +358,27 @@ Returns a thing
assert_equal expected, @TD.parse(text)
end
+ def test_parse_returns_multiline_and_raises
+ text = <<-TEXT
+Do some stuff
+
+Returns a thing
+ that is multiline
+Raises ArgumentError
+ TEXT
+
+ expected =
+ doc(
+ para('Do some stuff'),
+ blank_line,
+ head(3, 'Returns'),
+ blank_line,
+ para('Returns a thing', ' ', 'that is multiline'),
+ para('Raises ArgumentError'))
+
+ assert_equal expected, @TD.parse(text)
+ end
+
def test_parse_signature
text = <<-TEXT
Do some stuff
@@ -353,7 +412,7 @@ Signature
def test_tokenize_multiline_paragraph
text = "Public: Do some stuff\n"
- text << "On a new line\n"
+ text += "On a new line\n"
@td.tokenize text
@@ -518,4 +577,3 @@ Returns a thing
end
end
-
diff --git a/test/rdoc/test_rdoc_top_level.rb b/test/rdoc/test_rdoc_top_level.rb
index b8145e3f9d..e396791ab8 100644
--- a/test/rdoc/test_rdoc_top_level.rb
+++ b/test/rdoc/test_rdoc_top_level.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require File.expand_path '../xref_test_case', __FILE__
class TestRDocTopLevel < XrefTestCase
@@ -160,7 +160,7 @@ class TestRDocTopLevel < XrefTestCase
end
def test_last_modified
- assert_equal nil, @top_level.last_modified
+ assert_nil @top_level.last_modified
stat = Object.new
def stat.mtime() 0 end
@top_level.file_stat = stat
diff --git a/test/rdoc/xref_data.rb b/test/rdoc/xref_data.rb
index 5d3f6a9a55..aa9faaecd9 100644
--- a/test/rdoc/xref_data.rb
+++ b/test/rdoc/xref_data.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
XREF_DATA = <<-XREF_DATA
class C1
@@ -20,6 +20,8 @@ class C1
def m foo
end
+ def +
+ end
end
class C2
@@ -57,6 +59,62 @@ class C5
end
end
+class C6
+ private def priv1() end
+ def pub1() end
+ protected def prot1() end
+ def pub2() end
+ public def pub3() end
+ def pub4() end
+
+ private
+ private def priv2() end
+ def priv3() end
+ protected def prot2() end
+ def priv4() end
+ public def pub5() end
+ def priv5() end
+
+ protected
+ private def priv6() end
+ def prot3() end
+ protected def prot4() end
+ def prot5() end
+ public def pub6() end
+ def prot6() end
+end
+
+class C7
+ attr_reader :attr_reader
+ attr_reader :attr_reader_nodoc # :nodoc:
+ attr_writer :attr_writer
+ attr_writer :attr_writer_nodoc # :nodoc:
+ attr_accessor :attr_accessor
+ attr_accessor :attr_accessor_nodoc # :nodoc:
+
+ CONST = :const
+ CONST_NODOC = :const_nodoc # :nodoc:
+end
+
+class C8
+ class << self
+ class S1
+ end
+ end
+end
+
+class C9
+ class A
+ def foo() end
+ def self.bar() end
+ end
+
+ class B < A
+ def self.foo() end
+ def bar() end
+ end
+end
+
module M1
def m
end
diff --git a/test/rdoc/xref_test_case.rb b/test/rdoc/xref_test_case.rb
index c0b3ae5abd..d42cf398e7 100644
--- a/test/rdoc/xref_test_case.rb
+++ b/test/rdoc/xref_test_case.rb
@@ -1,7 +1,7 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
ENV['RDOC_TEST'] = 'yes'
-require 'rdoc'
+require 'minitest_helper'
require File.expand_path '../xref_data', __FILE__
class XrefTestCase < RDoc::TestCase
@@ -31,9 +31,10 @@ class XrefTestCase < RDoc::TestCase
@rdoc.options = @options
@rdoc.generator = generator
- @c1 = @xref_data.find_module_named 'C1'
- @c1_m = @c1.method_list.last # C1#m
- @c1__m = @c1.method_list.first # C1::m
+ @c1 = @xref_data.find_module_named 'C1'
+ @c1__m = @c1.find_class_method_named 'm' # C1::m
+ @c1_m = @c1.find_instance_method_named 'm' # C1#m
+ @c1_plus = @c1.find_instance_method_named '+'
@c2 = @xref_data.find_module_named 'C2'
@c2_a = @c2.method_list.last
@@ -51,6 +52,18 @@ class XrefTestCase < RDoc::TestCase
@c5_c1 = @xref_data.find_module_named 'C5::C1'
@c3_h1 = @xref_data.find_module_named 'C3::H1'
@c3_h2 = @xref_data.find_module_named 'C3::H2'
+ @c6 = @xref_data.find_module_named 'C6'
+ @c7 = @xref_data.find_module_named 'C7'
+ @c8 = @xref_data.find_module_named 'C8'
+ @c8_s1 = @xref_data.find_module_named 'C8::S1'
+
+ @c9 = @xref_data.find_module_named 'C9'
+ @c9_a = @xref_data.find_module_named 'C9::A'
+ @c9_a_i_foo = @c9_a.method_list.first
+ @c9_a_c_bar = @c9_a.method_list.last
+ @c9_b = @xref_data.find_module_named 'C9::B'
+ @c9_b_c_foo = @c9_b.method_list.first
+ @c9_b_i_bar = @c9_b.method_list.last
@m1 = @xref_data.find_module_named 'M1'
@m1_m = @m1.method_list.first
diff --git a/test/readline/test_readline.rb b/test/readline/test_readline.rb
index 8b98b62414..5adeca251a 100644
--- a/test/readline/test_readline.rb
+++ b/test/readline/test_readline.rb
@@ -45,14 +45,16 @@ class TestReadline < Test::Unit::TestCase
assert_equal("> ", stdout.read(2))
assert_equal(1, Readline::HISTORY.length)
assert_equal("hello", Readline::HISTORY[0])
- assert_raise(SecurityError) do
- Thread.start {
- $SAFE = 1
+ Thread.start {
+ $SAFE = 1
+ assert_raise(SecurityError) do
replace_stdio(stdin.path, stdout.path) do
Readline.readline("> ".taint)
end
- }.join
- end
+ end
+ }.join
+ ensure
+ $SAFE = 0
end
end
@@ -83,7 +85,7 @@ class TestReadline < Test::Unit::TestCase
stdin.write("first second\t")
stdin.flush
Readline.completion_append_character = " "
- line = replace_stdio(stdin.path, stdout.path) {
+ replace_stdio(stdin.path, stdout.path) {
Readline.readline("> ", false)
}
assert_equal("second", actual_text)
@@ -100,7 +102,7 @@ class TestReadline < Test::Unit::TestCase
stdin.write("first second\t")
stdin.flush
Readline.completion_append_character = nil
- line = replace_stdio(stdin.path, stdout.path) {
+ replace_stdio(stdin.path, stdout.path) {
Readline.readline("> ", false)
}
assert_equal("second", actual_text)
@@ -410,6 +412,7 @@ class TestReadline < Test::Unit::TestCase
end if !/EditLine|\A4\.3\z/n.match(Readline::VERSION)
def test_input_metachar
+ skip("Won't pass on mingw w/readline 7.0.005 [ruby-core:45682]") if mingw?
bug6601 = '[ruby-core:45682]'
Readline::HISTORY << "hello"
wo = nil
@@ -419,7 +422,7 @@ class TestReadline < Test::Unit::TestCase
end
assert_equal("hello", line, bug6601)
ensure
- wo.close
+ wo&.close
Readline.delete_text
Readline::HISTORY.clear
end if !/EditLine/n.match(Readline::VERSION)
@@ -486,13 +489,13 @@ class TestReadline < Test::Unit::TestCase
with_temp_stdio do |stdin, stdout|
replace_stdio(stdin.path, stdout.path) do
- Readline.completion_proc = -> (text) do
+ Readline.completion_proc = ->(text) do
passed_text = text
['completion']
end
Readline.completer_quote_characters = '\'"'
Readline.completer_word_break_characters = ' '
- Readline.quoting_detection_proc = -> (text, index) do
+ Readline.quoting_detection_proc = ->(text, index) do
index > 0 && text[index-1] == '\\'
end
@@ -524,13 +527,13 @@ class TestReadline < Test::Unit::TestCase
with_temp_stdio do |stdin, stdout|
replace_stdio(stdin.path, stdout.path) do
- Readline.completion_proc = -> (text) do
+ Readline.completion_proc = ->(text) do
passed_text = text
['completion']
end
Readline.completer_quote_characters = '\'"'
Readline.completer_word_break_characters = ' '
- Readline.quoting_detection_proc = -> (text, index) do
+ Readline.quoting_detection_proc = ->(text, index) do
escaped = index > 0 && text[index-1] == '\\'
escaped_char_indexes << index if escaped
escaped
@@ -550,6 +553,65 @@ class TestReadline < Test::Unit::TestCase
Readline.completer_word_break_characters = saved_completer_word_break_characters
end
+ def test_completion_quote_character_completing_unquoted_argument
+ return unless Readline.respond_to?(:completion_quote_character)
+
+ quote_character = "original value"
+ Readline.completion_proc = -> (_) do
+ quote_character = Readline.completion_quote_character
+ []
+ end
+ Readline.completer_quote_characters = "'\""
+
+ with_temp_stdio do |stdin, stdout|
+ replace_stdio(stdin.path, stdout.path) do
+ stdin.write("input\t")
+ stdin.flush
+ Readline.readline("> ", false)
+ end
+ end
+
+ assert_nil(quote_character)
+ end
+
+ def test_completion_quote_character_completing_quoted_argument
+ return unless Readline.respond_to?(:completion_quote_character)
+
+ quote_character = "original value"
+ Readline.completion_proc = -> (_) do
+ quote_character = Readline.completion_quote_character
+ []
+ end
+ Readline.completer_quote_characters = "'\""
+
+ with_temp_stdio do |stdin, stdout|
+ replace_stdio(stdin.path, stdout.path) do
+ stdin.write("'input\t")
+ stdin.flush
+ Readline.readline("> ", false)
+ end
+ end
+
+ assert_equal("'", quote_character)
+ end
+
+ def test_completion_quote_character_after_completion
+ return unless Readline.respond_to?(:completion_quote_character)
+
+ Readline.completion_proc = -> (_) { [] }
+ Readline.completer_quote_characters = "'\""
+
+ with_temp_stdio do |stdin, stdout|
+ replace_stdio(stdin.path, stdout.path) do
+ stdin.write("'input\t")
+ stdin.flush
+ Readline.readline("> ", false)
+ end
+ end
+
+ assert_nil(Readline.completion_quote_character)
+ end
+
private
def replace_stdio(stdin_path, stdout_path)
@@ -581,6 +643,11 @@ class TestReadline < Test::Unit::TestCase
Tempfile.create("test_readline_stdin") {|stdin|
Tempfile.create("test_readline_stdout") {|stdout|
yield stdin, stdout
+ if windows?
+ # needed since readline holds refs to tempfiles, can't delete on Windows
+ Readline.input = STDIN
+ Readline.output = STDOUT
+ end
}
}
end
diff --git a/test/resolv/test_addr.rb b/test/resolv/test_addr.rb
index 4a2df5bfca..14ec2651ab 100644
--- a/test/resolv/test_addr.rb
+++ b/test/resolv/test_addr.rb
@@ -27,4 +27,15 @@ class TestResolvAddr < Test::Unit::TestCase
end
end
end
+
+ def test_hosts_by_command
+ Dir.mktmpdir do |dir|
+ Dir.chdir(dir) do
+ hosts = Resolv::Hosts.new("|echo error")
+ assert_raise(Errno::ENOENT, Errno::EINVAL) do
+ hosts.each_name("") {}
+ end
+ end
+ end
+ end
end
diff --git a/test/resolv/test_dns.rb b/test/resolv/test_dns.rb
index d1431c1427..c49c152208 100644
--- a/test/resolv/test_dns.rb
+++ b/test/resolv/test_dns.rb
@@ -3,6 +3,7 @@ require 'test/unit'
require 'resolv'
require 'socket'
require 'tempfile'
+require 'minitest/mock'
class TestResolvDNS < Test::Unit::TestCase
def setup
@@ -127,6 +128,119 @@ class TestResolvDNS < Test::Unit::TestCase
}
end
+ def test_query_ipv4_duplicate_responses
+ begin
+ OpenSSL
+ rescue LoadError
+ skip 'autoload problem. see [ruby-dev:45021][Bug #5786]'
+ end if defined?(OpenSSL)
+
+ with_udp('127.0.0.1', 0) {|u|
+ _, server_port, _, server_address = u.addr
+ begin
+ client_thread = Thread.new {
+ Resolv::DNS.open(:nameserver_port => [[server_address, server_port]], :search => ['bad1.com', 'bad2.com', 'good.com'], ndots: 5) {|dns|
+ dns.getaddress("example")
+ }
+ }
+ server_thread = Thread.new {
+ 3.times do
+ msg, (_, client_port, _, client_address) = Timeout.timeout(5) {u.recvfrom(4096)}
+ id, flags, qdcount, ancount, nscount, arcount = msg.unpack("nnnnnn")
+
+ qr = (flags & 0x8000) >> 15
+ opcode = (flags & 0x7800) >> 11
+ aa = (flags & 0x0400) >> 10
+ tc = (flags & 0x0200) >> 9
+ rd = (flags & 0x0100) >> 8
+ ra = (flags & 0x0080) >> 7
+ z = (flags & 0x0070) >> 4
+ rcode = flags & 0x000f
+ rest = msg[12..-1]
+
+ questions = msg.bytes[12..-1]
+ labels = []
+ idx = 0
+ while idx < questions.length-5
+ size = questions[idx]
+ labels << questions[idx+1..idx+size].pack('c*')
+ idx += size+1
+ end
+ hostname = labels.join('.')
+
+ if hostname == "example.good.com"
+ id = id
+ qr = 1
+ opcode = opcode
+ aa = 0
+ tc = 0
+ rd = rd
+ ra = 1
+ z = 0
+ rcode = 0
+ qdcount = 1
+ ancount = 1
+ nscount = 0
+ arcount = 0
+ word2 = (qr << 15) |
+ (opcode << 11) |
+ (aa << 10) |
+ (tc << 9) |
+ (rd << 8) |
+ (ra << 7) |
+ (z << 4) |
+ rcode
+ msg = [id, word2, qdcount, ancount, nscount, arcount].pack("nnnnnn")
+ msg << questions.pack('c*')
+ type = 1
+ klass = 1
+ ttl = 3600
+ rdlength = 4
+ rdata = [52,0,2,1].pack("CCCC")
+ rr = [0xc00c, type, klass, ttl, rdlength, rdata].pack("nnnNna*")
+ msg << rr
+ rdata = [52,0,2,2].pack("CCCC")
+ rr = [0xc00c, type, klass, ttl, rdlength, rdata].pack("nnnNna*")
+ msg << rr
+
+ u.send(msg, 0, client_address, client_port)
+ else
+ id = id
+ qr = 1
+ opcode = opcode
+ aa = 0
+ tc = 0
+ rd = rd
+ ra = 1
+ z = 0
+ rcode = 3
+ qdcount = 1
+ ancount = 0
+ nscount = 0
+ arcount = 0
+ word2 = (qr << 15) |
+ (opcode << 11) |
+ (aa << 10) |
+ (tc << 9) |
+ (rd << 8) |
+ (ra << 7) |
+ (z << 4) |
+ rcode
+ msg = [id, word2, qdcount, ancount, nscount, arcount].pack("nnnnnn")
+ msg << questions.pack('c*')
+
+ u.send(msg, 0, client_address, client_port)
+ u.send(msg, 0, client_address, client_port)
+ end
+ end
+ }
+ result, _ = assert_join_threads([client_thread, server_thread])
+ assert_instance_of(Resolv::IPv4, result)
+ assert_equal("52.0.2.1", result.to_s)
+ end
+ }
+ end
+
def test_query_ipv4_address_timeout
with_udp('127.0.0.1', 0) {|u|
_, port , _, host = u.addr
@@ -158,7 +272,7 @@ class TestResolvDNS < Test::Unit::TestCase
u.bind("127.0.0.1", 0)
_, port, _, host = u.addr
u.close
- # A rase condition here.
+ # A race condition here.
# Another program may use the port.
# But no way to prevent it.
Timeout.timeout(5) do
@@ -179,6 +293,16 @@ class TestResolvDNS < Test::Unit::TestCase
end
end
+ def test_resolv_conf_by_command
+ Dir.mktmpdir do |dir|
+ Dir.chdir(dir) do
+ assert_raise(Errno::ENOENT, Errno::EINVAL) do
+ Resolv::DNS::Config.parse_resolv_conf("|echo foo")
+ end
+ end
+ end
+ end
+
def test_dots_diffences
name1 = Resolv::DNS::Name.create("example.org")
name2 = Resolv::DNS::Name.create("ex.ampl.eo.rg")
@@ -201,8 +325,23 @@ class TestResolvDNS < Test::Unit::TestCase
def test_ipv6_create
ref = '[Bug #11910] [ruby-core:72559]'
- assert_instance_of Resolv::IPv6, Resolv::IPv6.create('::1')
- assert_instance_of Resolv::IPv6, Resolv::IPv6.create('::1:127.0.0.1')
+ assert_instance_of Resolv::IPv6, Resolv::IPv6.create('::1'), ref
+ assert_instance_of Resolv::IPv6, Resolv::IPv6.create('::1:127.0.0.1'), ref
+ end
+
+ def test_ipv6_should_be_16
+ ref = '[rubygems:1626]'
+
+ broken_message =
+ "\0\0\0\0\0\0\0\0\0\0\0\1" \
+ "\x03ns2\bdnsimple\x03com\x00" \
+ "\x00\x1C\x00\x01\x00\x02OD" \
+ "\x00\x10$\x00\xCB\x00 I\x00\x01\x00\x00\x00\x00"
+
+ e = assert_raise_with_message(Resolv::DNS::DecodeError, /IPv6 address must be 16 bytes/, ref) do
+ Resolv::DNS::Message.decode broken_message
+ end
+ assert_kind_of(ArgumentError, e.cause)
end
def test_too_big_label_address
@@ -221,4 +360,22 @@ class TestResolvDNS < Test::Unit::TestCase
}
assert_operator(2**14, :<, m.to_s.length)
end
+
+ def assert_no_fd_leak
+ socket = assert_throw(self) do |tag|
+ Resolv::DNS.stub(:bind_random_port, ->(s, *) {throw(tag, s)}) do
+ yield.getname("8.8.8.8")
+ end
+ end
+
+ assert_predicate(socket, :closed?, "file descriptor leaked")
+ end
+
+ def test_no_fd_leak_connected
+ assert_no_fd_leak {Resolv::DNS.new(nameserver_port: [['127.0.0.1', 53]])}
+ end
+
+ def test_no_fd_leak_unconnected
+ assert_no_fd_leak {Resolv::DNS.new}
+ end
end
diff --git a/test/resolv/test_mdns.rb b/test/resolv/test_mdns.rb
new file mode 100644
index 0000000000..ff66276f9a
--- /dev/null
+++ b/test/resolv/test_mdns.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'resolv'
+
+class TestResolvMDNS < Test::Unit::TestCase
+ def test_mdns_each_address
+ mdns = Resolv::MDNS.new
+ def mdns.each_resource(name, typeclass)
+ if typeclass == Resolv::DNS::Resource::IN::A
+ yield typeclass.new("127.0.0.1")
+ else
+ yield typeclass.new("::1")
+ end
+ end
+ addrs = mdns.__send__(:use_ipv6?) ? ["127.0.0.1", "::1"] : ["127.0.0.1"]
+ [
+ ["example.com", []],
+ ["foo.local", addrs],
+ ].each do |name, expect|
+ results = []
+ mdns.each_address(name) do |result|
+ results << result.to_s
+ end
+ assert_equal expect, results.sort, "GH-1484"
+ end
+ end
+end
diff --git a/test/rexml/data/t75.xml b/test/rexml/data/t75.xml
index 0911fb1b1a..eb3cccee4b 100644
--- a/test/rexml/data/t75.xml
+++ b/test/rexml/data/t75.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?><?pos="3"?>
+<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- generated by hnb 1.9.17 (http://hnb.sourceforge.net) -->
<!DOCTYPE tree[
diff --git a/test/rexml/formatter/test_default.rb b/test/rexml/formatter/test_default.rb
new file mode 100644
index 0000000000..b5b131724b
--- /dev/null
+++ b/test/rexml/formatter/test_default.rb
@@ -0,0 +1,19 @@
+require_relative "../rexml_test_utils"
+
+module REXMLTests
+ class DefaultFormatterTest < Test::Unit::TestCase
+ def format(node)
+ formatter = REXML::Formatters::Default.new
+ output = ""
+ formatter.write(node, output)
+ output
+ end
+
+ class InstructionTest < self
+ def test_content_nil
+ instruction = REXML::Instruction.new("target")
+ assert_equal("<?target?>", format(instruction))
+ end
+ end
+ end
+end
diff --git a/test/rexml/parse/test_document_type_declaration.rb b/test/rexml/parse/test_document_type_declaration.rb
index 80f70888fb..55713909e7 100644
--- a/test/rexml/parse/test_document_type_declaration.rb
+++ b/test/rexml/parse/test_document_type_declaration.rb
@@ -5,17 +5,187 @@ require "rexml/document"
module REXMLTests
class TestParseDocumentTypeDeclaration < Test::Unit::TestCase
private
- def xml(internal_subset)
- <<-XML
-<!DOCTYPE r SYSTEM "urn:x-rexml:test" [
-#{internal_subset}
-]>
+ def parse(doctype)
+ REXML::Document.new(<<-XML).doctype
+#{doctype}
<r/>
XML
end
- def parse(internal_subset)
- REXML::Document.new(xml(internal_subset)).doctype
+ class TestName < self
+ def test_valid
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r>
+ DOCTYPE
+ assert_equal("r", doctype.name)
+ end
+
+ def test_garbage_plus_before_name_at_line_start
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-DOCTYPE)
+<!DOCTYPE +
+r SYSTEM "urn:x-rexml:test" [
+]>
+ DOCTYPE
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed DOCTYPE: invalid name
+Line: 5
+Position: 51
+Last 80 unconsumed characters:
++ r SYSTEM "urn:x-rexml:test" [ ]> <r/>
+ DETAIL
+ end
+ end
+
+ class TestExternalID < self
+ class TestSystem < self
+ def test_left_bracket_in_system_literal
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r SYSTEM "urn:x-rexml:[test" [
+]>
+ DOCTYPE
+ assert_equal([
+ "r",
+ "SYSTEM",
+ nil,
+ "urn:x-rexml:[test",
+ ],
+ [
+ doctype.name,
+ doctype.external_id,
+ doctype.public,
+ doctype.system,
+ ])
+ end
+
+ def test_greater_than_in_system_literal
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r SYSTEM "urn:x-rexml:>test" [
+]>
+ DOCTYPE
+ assert_equal([
+ "r",
+ "SYSTEM",
+ nil,
+ "urn:x-rexml:>test",
+ ],
+ [
+ doctype.name,
+ doctype.external_id,
+ doctype.public,
+ doctype.system,
+ ])
+ end
+
+ def test_no_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-DOCTYPE)
+<!DOCTYPE r SYSTEM>
+ DOCTYPE
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed DOCTYPE: system literal is missing
+Line: 3
+Position: 26
+Last 80 unconsumed characters:
+ SYSTEM> <r/>
+ DETAIL
+ end
+
+ def test_garbage_after_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-DOCTYPE)
+<!DOCTYPE r SYSTEM 'r.dtd'x'>
+ DOCTYPE
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed DOCTYPE: garbage after external ID
+Line: 3
+Position: 36
+Last 80 unconsumed characters:
+x'> <r/>
+ DETAIL
+ end
+
+ def test_single_quote
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r SYSTEM 'r".dtd'>
+ DOCTYPE
+ assert_equal("r\".dtd", doctype.system)
+ end
+
+ def test_double_quote
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r SYSTEM "r'.dtd">
+ DOCTYPE
+ assert_equal("r'.dtd", doctype.system)
+ end
+ end
+
+ class TestPublic < self
+ class TestPublicIDLiteral < self
+ def test_content_double_quote
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-DOCTYPE)
+<!DOCTYPE r PUBLIC 'double quote " is invalid' "r.dtd">
+ DOCTYPE
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed DOCTYPE: invalid public ID literal
+Line: 3
+Position: 62
+Last 80 unconsumed characters:
+ PUBLIC 'double quote " is invalid' "r.dtd"> <r/>
+ DETAIL
+ end
+
+ def test_single_quote
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r PUBLIC 'public-id-literal' "r.dtd">
+ DOCTYPE
+ assert_equal("public-id-literal", doctype.public)
+ end
+
+ def test_double_quote
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r PUBLIC "public'-id-literal" "r.dtd">
+ DOCTYPE
+ assert_equal("public'-id-literal", doctype.public)
+ end
+ end
+
+ class TestSystemLiteral < self
+ def test_garbage_after_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-DOCTYPE)
+<!DOCTYPE r PUBLIC 'public-id-literal' 'system-literal'x'>
+ DOCTYPE
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed DOCTYPE: garbage after external ID
+Line: 3
+Position: 65
+Last 80 unconsumed characters:
+x'> <r/>
+ DETAIL
+ end
+
+ def test_single_quote
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r PUBLIC "public-id-literal" 'system"-literal'>
+ DOCTYPE
+ assert_equal("system\"-literal", doctype.system)
+ end
+
+ def test_double_quote
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r PUBLIC "public-id-literal" "system'-literal">
+ DOCTYPE
+ assert_equal("system'-literal", doctype.system)
+ end
+ end
+ end
end
class TestMixed < self
@@ -45,6 +215,15 @@ module REXMLTests
assert_equal([REXML::NotationDecl, REXML::AttlistDecl],
doctype.children.collect(&:class))
end
+
+ private
+ def parse(internal_subset)
+ super(<<-DOCTYPE)
+<!DOCTYPE r SYSTEM "urn:x-rexml:test" [
+#{internal_subset}
+]>
+ DOCTYPE
+ end
end
end
end
diff --git a/test/rexml/parse/test_element.rb b/test/rexml/parse/test_element.rb
new file mode 100644
index 0000000000..7206fe595a
--- /dev/null
+++ b/test/rexml/parse/test_element.rb
@@ -0,0 +1,64 @@
+require "test/unit"
+require "rexml/document"
+
+module REXMLTests
+ class TestParseElement < Test::Unit::TestCase
+ def parse(xml)
+ REXML::Document.new(xml)
+ end
+
+ class TestInvalid < self
+ def test_no_end_tag
+ exception = assert_raise(REXML::ParseException) do
+ parse("<a></")
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Missing end tag for 'a'
+Line: 1
+Position: 5
+Last 80 unconsumed characters:
+</
+ DETAIL
+ end
+
+ def test_empty_namespace_attribute_name
+ exception = assert_raise(REXML::ParseException) do
+ parse("<x :a=\"\"></x>")
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Invalid attribute name: <:a="">
+Line: 1
+Position: 9
+Last 80 unconsumed characters:
+
+ DETAIL
+ end
+
+ def test_garbage_less_than_before_root_element_at_line_start
+ exception = assert_raise(REXML::ParseException) do
+ parse("<\n<x/>")
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+malformed XML: missing tag start
+Line: 2
+Position: 6
+Last 80 unconsumed characters:
+< <x/>
+ DETAIL
+ end
+
+ def test_garbage_less_than_slash_before_end_tag_at_line_start
+ exception = assert_raise(REXML::ParseException) do
+ parse("<x></\n</x>")
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Missing end tag for 'x'
+Line: 2
+Position: 10
+Last 80 unconsumed characters:
+</ </x>
+ DETAIL
+ end
+ end
+ end
+end
diff --git a/test/rexml/parse/test_notation_declaration.rb b/test/rexml/parse/test_notation_declaration.rb
index 0d29f0d81f..19a0536d0a 100644
--- a/test/rexml/parse/test_notation_declaration.rb
+++ b/test/rexml/parse/test_notation_declaration.rb
@@ -23,10 +23,100 @@ module REXMLTests
doctype = parse("<!NOTATION name PUBLIC 'urn:public-id'>")
assert_equal("name", doctype.notation("name").name)
end
+
+ def test_no_name
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: name is missing
+Line: 5
+Position: 72
+Last 80 unconsumed characters:
+ <!NOTATION> ]> <r/>
+ DETAIL
+ end
+
+ def test_invalid_name
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION '>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: invalid name
+Line: 5
+Position: 74
+Last 80 unconsumed characters:
+'> ]> <r/>
+ DETAIL
+ end
+
+ def test_no_id_type
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: invalid ID type
+Line: 5
+Position: 77
+Last 80 unconsumed characters:
+> ]> <r/>
+ DETAIL
+ end
+
+ def test_invalid_id_type
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name INVALID>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: invalid ID type
+Line: 5
+Position: 85
+Last 80 unconsumed characters:
+ INVALID> ]> <r/>
+ DETAIL
+ end
end
class TestExternalID < self
class TestSystem < self
+ def test_no_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name SYSTEM>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: system literal is missing
+Line: 5
+Position: 84
+Last 80 unconsumed characters:
+ SYSTEM> ]> <r/>
+ DETAIL
+ end
+
+ def test_garbage_after_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name SYSTEM 'system-literal'x'>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: garbage before end >
+Line: 5
+Position: 103
+Last 80 unconsumed characters:
+x'> ]> <r/>
+ DETAIL
+ end
+
def test_single_quote
doctype = parse(<<-INTERNAL_SUBSET)
<!NOTATION name SYSTEM 'system-literal'>
@@ -44,6 +134,21 @@ module REXMLTests
class TestPublic < self
class TestPublicIDLiteral < self
+ def test_content_double_quote
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC 'double quote " is invalid' "system-literal">
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: invalid public ID literal
+Line: 5
+Position: 129
+Last 80 unconsumed characters:
+ PUBLIC 'double quote " is invalid' "system-literal"> ]> <r/>
+ DETAIL
+ end
+
def test_single_quote
doctype = parse(<<-INTERNAL_SUBSET)
<!NOTATION name PUBLIC 'public-id-literal' "system-literal">
@@ -60,6 +165,21 @@ module REXMLTests
end
class TestSystemLiteral < self
+ def test_garbage_after_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC 'public-id-literal' 'system-literal'x'>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: garbage before end >
+Line: 5
+Position: 123
+Last 80 unconsumed characters:
+x'> ]> <r/>
+ DETAIL
+ end
+
def test_single_quote
doctype = parse(<<-INTERNAL_SUBSET)
<!NOTATION name PUBLIC "public-id-literal" 'system-literal'>
@@ -96,5 +216,66 @@ module REXMLTests
end
end
end
+
+ class TestPublicID < self
+ def test_no_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: public ID literal is missing
+Line: 5
+Position: 84
+Last 80 unconsumed characters:
+ PUBLIC> ]> <r/>
+ DETAIL
+ end
+
+ def test_literal_content_double_quote
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC 'double quote " is invalid in PubidLiteral'>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: invalid public ID literal
+Line: 5
+Position: 128
+Last 80 unconsumed characters:
+ PUBLIC 'double quote \" is invalid in PubidLiteral'> ]> <r/>
+ DETAIL
+ end
+
+ def test_garbage_after_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC 'public-id-literal'x'>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: garbage before end >
+Line: 5
+Position: 106
+Last 80 unconsumed characters:
+x'> ]> <r/>
+ DETAIL
+ end
+
+ def test_literal_single_quote
+ doctype = parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC 'public-id-literal'>
+ INTERNAL_SUBSET
+ assert_equal("public-id-literal", doctype.notation("name").public)
+ end
+
+ def test_literal_double_quote
+ doctype = parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC "public-id-literal">
+ INTERNAL_SUBSET
+ assert_equal("public-id-literal", doctype.notation("name").public)
+ end
+ end
end
end
diff --git a/test/rexml/parse/test_processing_instruction.rb b/test/rexml/parse/test_processing_instruction.rb
new file mode 100644
index 0000000000..f0c0c24e67
--- /dev/null
+++ b/test/rexml/parse/test_processing_instruction.rb
@@ -0,0 +1,44 @@
+require "test/unit"
+require "rexml/document"
+
+module REXMLTests
+ class TestParseProcessinInstruction < Test::Unit::TestCase
+ def parse(xml)
+ REXML::Document.new(xml)
+ end
+
+ class TestInvalid < self
+ def test_no_name
+ exception = assert_raise(REXML::ParseException) do
+ parse("<??>")
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Invalid processing instruction node
+Line: 1
+Position: 4
+Last 80 unconsumed characters:
+<??>
+ DETAIL
+ end
+
+ def test_garbage_text
+ # TODO: This should be parse error.
+ # Create test/parse/test_document.rb or something and move this to it.
+ doc = parse(<<-XML)
+x<?x y
+<!--?><?x -->?>
+<r/>
+ XML
+ pi = doc.children[1]
+ assert_equal([
+ "x",
+ "y\n<!--",
+ ],
+ [
+ pi.target,
+ pi.content,
+ ])
+ end
+ end
+ end
+end
diff --git a/test/rexml/parser/test_stream.rb b/test/rexml/parser/test_stream.rb
new file mode 100644
index 0000000000..c315833e4b
--- /dev/null
+++ b/test/rexml/parser/test_stream.rb
@@ -0,0 +1,32 @@
+require "test/unit"
+require "rexml/document"
+require "rexml/streamlistener"
+
+module REXMLTests
+ class TestStreamParser < Test::Unit::TestCase
+ class NullListener
+ include REXML::StreamListener
+ end
+
+ class TestInvalid < self
+ def test_no_end_tag
+ xml = "<root><sub>"
+ exception = assert_raise(REXML::ParseException) do
+ parse(xml)
+ end
+ assert_equal(<<-MESSAGE, exception.to_s)
+Missing end tag for '/root/sub'
+Line: 1
+Position: #{xml.bytesize}
+Last 80 unconsumed characters:
+ MESSAGE
+ end
+
+ private
+ def parse(xml, listener=nil)
+ listener ||= NullListener.new
+ REXML::Document.parse_stream(xml, listener)
+ end
+ end
+ end
+end
diff --git a/test/rexml/parser/test_tree.rb b/test/rexml/parser/test_tree.rb
index 7ab0addca1..8a5d9d1223 100644
--- a/test/rexml/parser/test_tree.rb
+++ b/test/rexml/parser/test_tree.rb
@@ -12,7 +12,7 @@ class TestTreeParser < Test::Unit::TestCase
parse(xml)
end
assert_equal(<<-MESSAGE, exception.to_s)
-Missing end tag for 'root' (got "not-root")
+Missing end tag for 'root' (got 'not-root')
Line: 1
Position: #{xml.bytesize}
Last 80 unconsumed characters:
diff --git a/test/rexml/parser/test_ultra_light.rb b/test/rexml/parser/test_ultra_light.rb
index c48a13d311..44fd1d1ec0 100644
--- a/test/rexml/parser/test_ultra_light.rb
+++ b/test/rexml/parser/test_ultra_light.rb
@@ -16,7 +16,6 @@ class TestUltraLightParser < Test::Unit::TestCase
nil,
[:entitydecl, "name", "value"]
],
- [:text, "\n"],
[:start_element, :parent, "root", {}],
[:text, "\n"],
],
@@ -55,7 +54,7 @@ class TestUltraLightParser < Test::Unit::TestCase
normalized_doctype[1] = normalized_parent
normalized_doctype
when :start_element
- tag, parent, name, attributes, *children = child
+ tag, _parent, name, attributes, *children = child
normalized_parent = :parent
normalized_children = children.collect do |sub_child|
normalize_child(sub_child)
diff --git a/test/rexml/rexml_test_utils.rb b/test/rexml/rexml_test_utils.rb
index 7c59629e53..8bb002cee2 100644
--- a/test/rexml/rexml_test_utils.rb
+++ b/test/rexml/rexml_test_utils.rb
@@ -1,5 +1,8 @@
# frozen_string_literal: false
-require 'test/unit'
+
+require "test/unit"
+require "rexml/document"
+
module REXMLTestUtils
def fixture_path(*components)
File.join(File.dirname(__FILE__), "data", *components)
diff --git a/test/rexml/test_attribute.rb b/test/rexml/test_attribute.rb
new file mode 100644
index 0000000000..5175bd4454
--- /dev/null
+++ b/test/rexml/test_attribute.rb
@@ -0,0 +1,14 @@
+require_relative "rexml_test_utils"
+
+module REXMLTests
+ class AttributeTest < Test::Unit::TestCase
+ def test_empty_prefix
+ error = assert_raise(ArgumentError) do
+ REXML::Attribute.new(":x")
+ end
+ assert_equal("name must be " +
+ "\#{PREFIX}:\#{LOCAL_NAME} or \#{LOCAL_NAME}: <\":x\">",
+ error.message)
+ end
+ end
+end
diff --git a/test/rexml/test_core.rb b/test/rexml/test_core.rb
index 0071063128..ae528d75d3 100644
--- a/test/rexml/test_core.rb
+++ b/test/rexml/test_core.rb
@@ -1,4 +1,4 @@
-# coding: binary
+# coding: utf-8
# frozen_string_literal: false
require_relative "rexml_test_utils"
@@ -877,18 +877,18 @@ EOL
EOL
# The most common case. People not caring about the namespaces much.
- assert_equal( "XY", XPath.match( doc, "/test/a/text()" ).join )
- assert_equal( "XY", XPath.match( doc, "/test/x:a/text()" ).join )
+ assert_equal( "XY", XPath.match( doc, "/*:test/*:a/text()" ).join )
+ assert_equal( "XY", XPath.match( doc, "/*:test/x:a/text()" ).join )
# Surprising? I don't think so, if you believe my definition of the "common case"
- assert_equal( "XYZ", XPath.match( doc, "//a/text()" ).join )
+ assert_equal( "XYZ", XPath.match( doc, "//*:a/text()" ).join )
# These are the uncommon cases. Namespaces are actually important, so we define our own
# mappings, and pass them in.
assert_equal( "XY", XPath.match( doc, "/f:test/f:a/text()", { "f" => "1" } ).join )
# The namespaces are defined, and override the original mappings
- assert_equal( "", XPath.match( doc, "/test/a/text()", { "f" => "1" } ).join )
+ assert_equal( "XY", XPath.match( doc, "/*:test/*:a/text()", { "f" => "1" } ).join )
assert_equal( "", XPath.match( doc, "/x:test/x:a/text()", { "f" => "1" } ).join )
- assert_equal( "", XPath.match( doc, "//a/text()", { "f" => "1" } ).join )
+ assert_equal( "XYZ", XPath.match( doc, "//*:a/text()", { "f" => "1" } ).join )
end
def test_processing_instruction
@@ -995,7 +995,7 @@ EOL
document.write(s)
## XML Doctype
- str = '<!DOCTYPE foo "bar">'
+ str = '<!DOCTYPE foo SYSTEM "bar">'
source = REXML::Source.new(str)
doctype = REXML::DocType.new(source)
document.add(doctype)
@@ -1274,14 +1274,15 @@ EOL
def test_ticket_21
src = "<foo bar=value/>"
- assert_raise( ParseException, "invalid XML should be caught" ) {
+ exception = assert_raise(ParseException) do
Document.new(src)
- }
- begin
- Document.new(src)
- rescue
- assert_match( /missing attribute quote/, $!.message )
end
+ assert_equal(<<-DETAIL, exception.to_s)
+Missing attribute value start quote: <bar>
+Line: 1
+Position: 16
+Last 80 unconsumed characters:
+ DETAIL
end
def test_ticket_63
@@ -1390,8 +1391,8 @@ ENDXML
def test_ticket_102
doc = REXML::Document.new '<doc xmlns="ns"><item name="foo"/></doc>'
- assert_equal( "foo", doc.root.elements["item"].attribute("name","ns").to_s )
- assert_equal( "item", doc.root.elements["item[@name='foo']"].name )
+ assert_equal( "foo", doc.root.elements["*:item"].attribute("name","ns").to_s )
+ assert_equal( "item", doc.root.elements["*:item[@name='foo']"].name )
end
def test_ticket_14
@@ -1420,11 +1421,11 @@ ENDXML
doc = REXML::Document.new(
'<doc xmlns="ns" xmlns:phantom="ns"><item name="foo">text</item></doc>'
)
- assert_equal 'text', doc.text( "/doc/item[@name='foo']" )
+ assert_equal 'text', doc.text( "/*:doc/*:item[@name='foo']" )
assert_equal "name='foo'",
- doc.root.elements["item"].attribute("name", "ns").inspect
+ doc.root.elements["*:item"].attribute("name", "ns").inspect
assert_equal "<item name='foo'>text</item>",
- doc.root.elements["item[@name='foo']"].to_s
+ doc.root.elements["*:item[@name='foo']"].to_s
end
def test_ticket_135
diff --git a/test/rexml/test_doctype.rb b/test/rexml/test_doctype.rb
index 91de05b05f..915717de8e 100644
--- a/test/rexml/test_doctype.rb
+++ b/test/rexml/test_doctype.rb
@@ -1,68 +1,82 @@
# frozen_string_literal: false
-require 'test/unit'
-require 'rexml/document'
+
+require_relative "rexml_test_utils"
module REXMLTests
class TestDocTypeAccessor < Test::Unit::TestCase
-
def setup
@sysid = "urn:x-test:sysid1"
- @notid1 = "urn:x-test:notation1"
- @notid2 = "urn:x-test:notation2"
- document_string1 = <<-"XMLEND"
- <!DOCTYPE r SYSTEM "#{@sysid}" [
- <!NOTATION n1 SYSTEM "#{@notid1}">
- <!NOTATION n2 SYSTEM "#{@notid2}">
+ @notation_id1 = "urn:x-test:notation1"
+ @notation_id2 = "urn:x-test:notation2"
+ xml_system = <<-XML
+ <!DOCTYPE root SYSTEM "#{@sysid}" [
+ <!NOTATION n1 SYSTEM "#{@notation_id1}">
+ <!NOTATION n2 SYSTEM "#{@notation_id2}">
]>
- <r/>
- XMLEND
- @doctype1 = REXML::Document.new(document_string1).doctype
+ <root/>
+ XML
+ @doc_type_system = REXML::Document.new(xml_system).doctype
@pubid = "TEST_ID"
- document_string2 = <<-"XMLEND"
- <!DOCTYPE r PUBLIC "#{@pubid}">
- <r/>
- XMLEND
- @doctype2 = REXML::Document.new(document_string2).doctype
+ xml_public_system = <<-XML
+ <!DOCTYPE root PUBLIC "#{@pubid}" "#{@sysid}">
+ <root/>
+ XML
+ @doc_type_public_system = REXML::Document.new(xml_public_system).doctype
+ end
- document_string3 = <<-"XMLEND"
- <!DOCTYPE r PUBLIC "#{@pubid}" "#{@sysid}">
- <r/>
- XMLEND
- @doctype3 = REXML::Document.new(document_string3).doctype
+ def test_public
+ assert_equal([
+ nil,
+ @pubid,
+ ],
+ [
+ @doc_type_system.public,
+ @doc_type_public_system.public,
+ ])
+ end
+ def test_to_s
+ assert_equal("<!DOCTYPE root PUBLIC \"#{@pubid}\" \"#{@sysid}\">",
+ @doc_type_public_system.to_s)
end
- def test_public
- assert_equal(nil, @doctype1.public)
- assert_equal(@pubid, @doctype2.public)
- assert_equal(@pubid, @doctype3.public)
+ def test_to_s_apostrophe
+ @doc_type_public_system.parent.context[:prologue_quote] = :apostrophe
+ assert_equal("<!DOCTYPE root PUBLIC '#{@pubid}' '#{@sysid}'>",
+ @doc_type_public_system.to_s)
end
def test_system
- assert_equal(@sysid, @doctype1.system)
- assert_equal(nil, @doctype2.system)
- assert_equal(@sysid, @doctype3.system)
+ assert_equal([
+ @sysid,
+ @sysid,
+ ],
+ [
+ @doc_type_system.system,
+ @doc_type_public_system.system,
+ ])
end
def test_notation
- assert_equal(@notid1, @doctype1.notation("n1").system)
- assert_equal(@notid2, @doctype1.notation("n2").system)
+ assert_equal([
+ @notation_id1,
+ @notation_id2,
+ ],
+ [
+ @doc_type_system.notation("n1").system,
+ @doc_type_system.notation("n2").system,
+ ])
end
def test_notations
- notations = @doctype1.notations
- assert_equal(2, notations.length)
- assert_equal(@notid1, find_notation(notations, "n1").system)
- assert_equal(@notid2, find_notation(notations, "n2").system)
- end
-
- def find_notation(notations, name)
- notations.find { |notation|
- name == notation.name
- }
+ notations = @doc_type_system.notations
+ assert_equal([
+ @notation_id1,
+ @notation_id2,
+ ],
+ notations.collect(&:system))
end
-
end
class TestNotationDeclPublic < Test::Unit::TestCase
@@ -82,6 +96,19 @@ module REXMLTests
decl(@id, @uri).to_s)
end
+ def test_to_s_apostrophe
+ document = REXML::Document.new(<<-XML)
+ <!DOCTYPE root SYSTEM "urn:x-test:sysid" [
+ #{decl(@id, @uri).to_s}
+ ]>
+ <root/>
+ XML
+ document.context[:prologue_quote] = :apostrophe
+ notation = document.doctype.notations[0]
+ assert_equal("<!NOTATION #{@name} PUBLIC '#{@id}' '#{@uri}'>",
+ notation.to_s)
+ end
+
private
def decl(id, uri)
REXML::NotationDecl.new(@name, "PUBLIC", id, uri)
@@ -99,6 +126,19 @@ module REXMLTests
decl(@id).to_s)
end
+ def test_to_s_apostrophe
+ document = REXML::Document.new(<<-XML)
+ <!DOCTYPE root SYSTEM "urn:x-test:sysid" [
+ #{decl(@id).to_s}
+ ]>
+ <root/>
+ XML
+ document.context[:prologue_quote] = :apostrophe
+ notation = document.doctype.notations[0]
+ assert_equal("<!NOTATION #{@name} SYSTEM '#{@id}'>",
+ notation.to_s)
+ end
+
private
def decl(id)
REXML::NotationDecl.new(@name, "SYSTEM", id, nil)
diff --git a/test/rexml/test_element.rb b/test/rexml/test_element.rb
new file mode 100644
index 0000000000..82830b44e6
--- /dev/null
+++ b/test/rexml/test_element.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: false
+
+require "test/unit/testcase"
+require "rexml/document"
+
+module REXMLTests
+ class ElementTester < Test::Unit::TestCase
+ def test_array_reference_string
+ doc = REXML::Document.new("<language name='Ruby'/>")
+ assert_equal("Ruby", doc.root["name"])
+ end
+
+ def test_array_reference_symbol
+ doc = REXML::Document.new("<language name='Ruby'/>")
+ assert_equal("Ruby", doc.root[:name])
+ end
+ end
+end
diff --git a/test/rexml/test_functions.rb b/test/rexml/test_functions.rb
index 5ac823dd8f..a77be38cc1 100644
--- a/test/rexml/test_functions.rb
+++ b/test/rexml/test_functions.rb
@@ -221,5 +221,18 @@ module REXMLTests
m = REXML::XPath.match(doc, "//comment()[#{predicate}]")
assert_equal( [REXML::Comment.new("COMMENT A")], m )
end
+
+ def test_unregistered_method
+ doc = Document.new("<root/>")
+ assert_nil(XPath::first(doc.root, "to_s()"))
+ end
+
+ def test_nonexistent_function
+ doc = Document.new("<root><nonexistent/></root>")
+ # TODO: Maybe, this is not XPath spec behavior.
+ # This behavior must be reconsidered.
+ assert_equal(doc.root.elements[1],
+ XPath::first(doc.root, "nonexistent()"))
+ end
end
end
diff --git a/test/rexml/test_instruction.rb b/test/rexml/test_instruction.rb
new file mode 100644
index 0000000000..96fa909e17
--- /dev/null
+++ b/test/rexml/test_instruction.rb
@@ -0,0 +1,14 @@
+require_relative "rexml_test_utils"
+
+module REXMLTests
+ class InstructionTest < Test::Unit::TestCase
+ def test_target_nil
+ error = assert_raise(ArgumentError) do
+ REXML::Instruction.new(nil)
+ end
+ assert_equal("processing instruction target must be String or " +
+ "REXML::Instruction: <nil>",
+ error.message)
+ end
+ end
+end
diff --git a/test/rexml/test_jaxen.rb b/test/rexml/test_jaxen.rb
index 9cd7bee8c2..9640b8290b 100644
--- a/test/rexml/test_jaxen.rb
+++ b/test/rexml/test_jaxen.rb
@@ -12,119 +12,120 @@ module REXMLTests
include REXMLTestUtils
include REXML
- def test_axis ; test("axis") ; end
- def test_basic ; test("basic") ; end
- def test_basicupdate ; test("basicupdate") ; end
- def test_contents ; test("contents") ; end
- def test_defaultNamespace ; test("defaultNamespace") ; end
- def test_fibo ; test("fibo") ; end
- def test_id ; test("id") ; end
- def test_jaxen24 ; test("jaxen24") ; end
- def test_lang ; test("lang") ; end
- def test_message ; test("message") ; end
- def test_moreover ; test("moreover") ; end
- def test_much_ado ; test("much_ado") ; end
- def test_namespaces ; test("namespaces") ; end
- def test_nitf ; test("nitf") ; end
- def test_numbers ; test("numbers") ; end
- def test_pi ; test("pi") ; end
- def test_pi2 ; test("pi2") ; end
- def test_simple ; test("simple") ; end
- def test_testNamespaces ; test("testNamespaces") ; end
- def test_text ; test("text") ; end
- def test_underscore ; test("underscore") ; end
- def test_web ; test("web") ; end
- def test_web2 ; test("web2") ; end
+ def test_axis ; process_test_case("axis") ; end
+ def test_basic ; process_test_case("basic") ; end
+ def test_basicupdate ; process_test_case("basicupdate") ; end
+ def test_contents ; process_test_case("contents") ; end
+ def test_defaultNamespace ; process_test_case("defaultNamespace") ; end
+ def test_fibo ; process_test_case("fibo") ; end
+ def test_id ; process_test_case("id") ; end
+ def test_jaxen24 ; process_test_case("jaxen24") ; end
+ def test_lang ; process_test_case("lang") ; end
+ # document() function for XSLT isn't supported
+ def _test_message ; process_test_case("message") ; end
+ def test_moreover ; process_test_case("moreover") ; end
+ def test_much_ado ; process_test_case("much_ado") ; end
+ def test_namespaces ; process_test_case("namespaces") ; end
+ def test_nitf ; process_test_case("nitf") ; end
+ # Exception should be considered
+ def _test_numbers ; process_test_case("numbers") ; end
+ def test_pi ; process_test_case("pi") ; end
+ def test_pi2 ; process_test_case("pi2") ; end
+ def test_simple ; process_test_case("simple") ; end
+ # TODO: namespace node is needed
+ def _test_testNamespaces ; process_test_case("testNamespaces") ; end
+ # document() function for XSLT isn't supported
+ def _test_text ; process_test_case("text") ; end
+ def test_underscore ; process_test_case("underscore") ; end
+ def _test_web ; process_test_case("web") ; end
+ def test_web2 ; process_test_case("web2") ; end
private
- def test( fname )
-# Dir.entries( xml_dir ).each { |fname|
-# if fname =~ /\.xml$/
- doc = File.open(fixture_path(fname+".xml")) do |file|
- Document.new(file)
- end
- XPath.each( doc, "/tests/document" ) {|e| handleDocument(e)}
-# end
-# }
+ def process_test_case(name)
+ xml_path = "#{name}.xml"
+ doc = File.open(fixture_path(xml_path)) do |file|
+ Document.new(file)
+ end
+ test_doc = File.open(fixture_path("test/tests.xml")) do |file|
+ Document.new(file)
+ end
+ XPath.each(test_doc,
+ "/tests/document[@url='xml/#{xml_path}']/context") do |context|
+ process_context(doc, context)
+ end
end
# processes a tests/document/context node
- def handleContext( testDoc, ctxElement)
- testCtx = XPath.match( testDoc, ctxElement.attributes["select"] )[0]
- namespaces = {}
- if testCtx.class == Element
- testCtx.prefixes.each { |pre| handleNamespace( testCtx, pre, namespaces ) }
- end
+ def process_context(doc, context)
+ test_context = XPath.match(doc, context.attributes["select"])
+ namespaces = context.namespaces
+ namespaces.delete("var")
+ namespaces = nil if namespaces.empty?
variables = {}
- XPath.each( ctxElement, "@*[namespace-uri() = 'http://jaxen.org/test-harness/var']") { |attrib| handleVariable(testCtx, variables, attrib) }
- XPath.each( ctxElement, "valueOf") { |e| handleValueOf(testCtx, variables, namespaces, e) }
- XPath.each( ctxElement, "test[not(@exception) or (@exception != 'true') ]") { |e| handleNominalTest(testCtx,variables, namespaces, e) }
- XPath.each( ctxElement, "test[@exception = 'true']") { |e| handleExceptionalTest(testCtx,variables, namespaces, e) }
+ var_namespace = "http://jaxen.org/test-harness/var"
+ XPath.each(context,
+ "@*[namespace-uri() = '#{var_namespace}']") do |attribute|
+ variables[attribute.name] = attribute.value
+ end
+ XPath.each(context, "valueOf") do |value|
+ process_value_of(test_context, variables, namespaces, value)
+ end
+ XPath.each(context,
+ "test[not(@exception) or (@exception != 'true')]") do |test|
+ process_nominal_test(test_context, variables, namespaces, test)
+ end
+ XPath.each(context,
+ "test[@exception = 'true']") do |test|
+ process_exceptional_test(test_context, variables, namespaces, test)
+ end
end
# processes a tests/document/context/valueOf or tests/document/context/test/valueOf node
- def handleValueOf(ctx,variables, namespaces, valueOfElement)
- expected = valueOfElement.text
- got = XPath.match( ctx, valueOfElement.attributes["select"], namespaces, variables )[0]
- assert_true( (got.nil? && expected.nil?) || !got.nil? )
- case got.class
- when Element
- assert_equal( got.class, Element )
- when Attribute, Text, Comment, TrueClass, FalseClass
- assert_equal( expected, got.to_s )
- when Instruction
- assert_equal( expected, got.content )
- when Integer
- assert_equal( exected.to_f, got )
- when String
- # normalize values for comparison
- got = "" if got == nil or got == ""
- expected = "" if expected == nil or expected == ""
- assert_equal( expected, got )
- else
- assert_fail( "Wassup?" )
- end
- end
+ def process_value_of(context, variables, namespaces, value_of)
+ expected = value_of.text
+ xpath = value_of.attributes["select"]
+ matched = XPath.match(context, xpath, namespaces, variables, strict: true)
+ message = user_message(context, xpath, matched)
+ assert_equal(expected || "",
+ REXML::Functions.string(matched),
+ message)
+ end
# processes a tests/document/context/test node ( where @exception is false or doesn't exist )
- def handleNominalTest(ctx, variables, namespaces, testElement)
- expected = testElement.attributes["count"]
- got = XPath.match( ctx, testElement.attributes["select"], namespaces, variables )
+ def process_nominal_test(context, variables, namespaces, test)
+ xpath = test.attributes["select"]
+ matched = XPath.match(context, xpath, namespaces, variables, strict: true)
# might be a test with no count attribute, but nested valueOf elements
- assert( expected == got.size.to_s ) if !expected.nil?
+ expected = test.attributes["count"]
+ if expected
+ assert_equal(Integer(expected, 10),
+ matched.size,
+ user_message(context, xpath, matched))
+ end
- XPath.each( testElement, "valueOf") { |e|
- handleValueOf(got, variables, namespaces, e)
- }
+ XPath.each(test, "valueOf") do |value_of|
+ process_value_of(matched, variables, namespaces, value_of)
+ end
end
# processes a tests/document/context/test node ( where @exception is true )
- def handleExceptionalTest(ctx, variables, namespaces, testElement)
- assert_raise( Exception ) {
- XPath.match( ctx, testElement.attributes["select"], namespaces, variables )
- }
- end
-
- # processes a tests/document node
- def handleDocument(docElement)
- puts "- Processing document: #{docElement.attributes['url']}"
- testFile = File.new( docElement.attributes["url"] )
- testDoc = Document.new testFile
- XPath.each( docElement, "context") { |e| handleContext(testDoc, e) }
- end
-
- # processes a variable definition in a namespace like <test var:foo="bar">
- def handleVariable( ctx, variables, attrib )
- puts "--- Found attribute: #{attrib.name}"
- variables[attrib.name] = attrib.value
+ def process_exceptional_test(context, variables, namespaces, test)
+ xpath = test.attributes["select"]
+ assert_raise(REXML::ParseException) do
+ XPath.match(context, xpath, namespaces, variables, strict: true)
+ end
end
- # processes a namespace definition like <test xmlns:foo="fiz:bang:bam">
- def handleNamespace( ctx, prefix, namespaces )
- puts "--- Found namespace: #{prefix}"
- namespaces[prefix] = ctx.namespaces[prefix]
+ def user_message(context, xpath, matched)
+ message = ""
+ context.each_with_index do |node, i|
+ message << "Node#{i}:\n"
+ message << "#{node}\n"
+ end
+ message << "XPath: <#{xpath}>\n"
+ message << "Matched <#{matched}>"
+ message
end
-
end
end
diff --git a/test/rexml/test_martin_fowler.rb b/test/rexml/test_martin_fowler.rb
index da685a80ec..add3c82723 100644
--- a/test/rexml/test_martin_fowler.rb
+++ b/test/rexml/test_martin_fowler.rb
@@ -3,7 +3,7 @@ require 'test/unit'
require 'rexml/document'
module REXMLTests
- class OrderTester < Test::Unit::TestCase
+ class OrderTesterMF < Test::Unit::TestCase
DOC = <<END
<paper>
<title>Remove this element and figs order differently</title>
diff --git a/test/rexml/test_stream.rb b/test/rexml/test_stream.rb
index d7ceedc70e..08d4462ef9 100644
--- a/test/rexml/test_stream.rb
+++ b/test/rexml/test_stream.rb
@@ -15,8 +15,8 @@ module REXMLTests
def test_listener
data = %Q{<session1 user="han" password="rootWeiler" />\n<session2 user="han" password="rootWeiler" />}
- b = RequestReader.new( data )
- b = RequestReader.new( data )
+ RequestReader.new( data )
+ RequestReader.new( data )
end
def test_ticket_49
diff --git a/test/rexml/test_text.rb b/test/rexml/test_text.rb
index 3f8036eee3..e9a246e27f 100644
--- a/test/rexml/test_text.rb
+++ b/test/rexml/test_text.rb
@@ -1,10 +1,57 @@
# frozen_string_literal: false
-require "rexml/text"
+
+require_relative "rexml_test_utils"
module REXMLTests
class TextTester < Test::Unit::TestCase
include REXML
+ def test_new_text_response_whitespace_default
+ text = Text.new("a b\t\tc", true)
+ assert_equal("a b\tc", Text.new(text).to_s)
+ end
+
+ def test_new_text_response_whitespace_true
+ text = Text.new("a b\t\tc", true)
+ assert_equal("a b\t\tc", Text.new(text, true).to_s)
+ end
+
+ def test_new_text_raw_default
+ text = Text.new("&amp;lt;", false, nil, true)
+ assert_equal("&amp;lt;", Text.new(text).to_s)
+ end
+
+ def test_new_text_raw_false
+ text = Text.new("&amp;lt;", false, nil, true)
+ assert_equal("&amp;amp;lt;", Text.new(text, false, nil, false).to_s)
+ end
+
+ def test_new_text_entity_filter_default
+ document = REXML::Document.new(<<-XML)
+<!DOCTYPE root [
+ <!ENTITY a "aaa">
+ <!ENTITY b "bbb">
+]>
+<root/>
+ XML
+ text = Text.new("aaa bbb", false, document.root, nil, ["a"])
+ assert_equal("aaa &b;",
+ Text.new(text, false, document.root).to_s)
+ end
+
+ def test_new_text_entity_filter_custom
+ document = REXML::Document.new(<<-XML)
+<!DOCTYPE root [
+ <!ENTITY a "aaa">
+ <!ENTITY b "bbb">
+]>
+<root/>
+ XML
+ text = Text.new("aaa bbb", false, document.root, nil, ["a"])
+ assert_equal("&a; bbb",
+ Text.new(text, false, document.root, nil, ["b"]).to_s)
+ end
+
def test_shift_operator_chain
text = Text.new("original\r\n")
text << "append1\r\n" << "append2\r\n"
@@ -18,5 +65,11 @@ module REXMLTests
text << "append3\r\n" << "append4\r\n"
assert_equal("original\nappend1\nappend2\nappend3\nappend4\n", text.to_s)
end
+
+ def test_clone
+ text = Text.new("&amp;lt; <")
+ assert_equal(text.to_s,
+ text.clone.to_s)
+ end
end
end
diff --git a/test/rexml/test_xml_declaration.rb b/test/rexml/test_xml_declaration.rb
index a4d97c41d0..1d5a6d312f 100644
--- a/test/rexml/test_xml_declaration.rb
+++ b/test/rexml/test_xml_declaration.rb
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
# frozen_string_literal: false
#
# Created by Henrik MÃ¥rtensson on 2007-02-18.
@@ -10,11 +9,11 @@ require "test/unit"
module REXMLTests
class TestXmlDeclaration < Test::Unit::TestCase
def setup
- xml = <<-'END_XML'
+ xml = <<-XML
<?xml encoding= 'UTF-8' standalone='yes'?>
<root>
</root>
- END_XML
+ XML
@doc = REXML::Document.new xml
@root = @doc.root
@xml_declaration = @doc.children[0]
@@ -32,5 +31,12 @@ module REXMLTests
assert_kind_of(REXML::XMLDecl, @root.previous_sibling.previous_sibling)
assert_kind_of(REXML::Element, @xml_declaration.next_sibling.next_sibling)
end
+
+ def test_write_prologue_quote
+ @doc.context[:prologue_quote] = :quote
+ assert_equal("<?xml version=\"1.0\" " +
+ "encoding=\"UTF-8\" standalone=\"yes\"?>",
+ @xml_declaration.to_s)
+ end
end
end
diff --git a/test/rexml/xpath/test_base.rb b/test/rexml/xpath/test_base.rb
index 5079fdd75a..5a03087ca6 100644
--- a/test/rexml/xpath/test_base.rb
+++ b/test/rexml/xpath/test_base.rb
@@ -632,29 +632,36 @@ module REXMLTests
<c id='a'/>
</b>
<c id='b'/>
+ <c id='c'/>
+ <c/>
</a>")
- assert_equal( 1, REXML::XPath.match(doc,
- "//*[local-name()='c' and @id='b']").size )
- assert_equal( 1, REXML::XPath.match(doc,
- "//*[ local-name()='c' and @id='b' ]").size )
- assert_equal( 1, REXML::XPath.match(doc,
- "//*[ local-name() = 'c' and @id = 'b' ]").size )
- assert_equal( 1,
- REXML::XPath.match(doc, '/a/c[@id]').size )
- assert_equal( 1,
- REXML::XPath.match(doc, '/a/c[(@id)]').size )
- assert_equal( 1,
- REXML::XPath.match(doc, '/a/c[ @id ]').size )
- assert_equal( 1,
- REXML::XPath.match(doc, '/a/c[ (@id) ]').size )
- assert_equal( 1,
- REXML::XPath.match(doc, '/a/c[( @id )]').size )
- assert_equal( 1, REXML::XPath.match(doc.root,
- '/a/c[ ( @id ) ]').size )
- assert_equal( 1, REXML::XPath.match(doc,
- '/a/c [ ( @id ) ] ').size )
- assert_equal( 1, REXML::XPath.match(doc,
- ' / a / c [ ( @id ) ] ').size )
+ match = lambda do |xpath|
+ REXML::XPath.match(doc, xpath).collect(&:to_s)
+ end
+ assert_equal(["<c id='b'/>"],
+ match.call("//*[local-name()='c' and @id='b']"))
+ assert_equal(["<c id='b'/>"],
+ match.call("//*[ local-name()='c' and @id='b' ]"))
+ assert_equal(["<c id='b'/>"],
+ match.call("//*[ local-name() = 'c' and @id = 'b' ]"))
+ assert_equal(["<c id='b'/>", "<c id='c'/>"],
+ match.call('/a/c[@id]'))
+ assert_equal(["<c id='b'/>", "<c id='c'/>"],
+ match.call('/a/c[(@id)]'))
+ assert_equal(["<c id='b'/>", "<c id='c'/>"],
+ match.call('/a/c[ @id ]'))
+ assert_equal(["<c id='b'/>", "<c id='c'/>"],
+ match.call('/a/c[ (@id) ]'))
+ assert_equal(["<c id='b'/>", "<c id='c'/>"],
+ match.call('/a/c[( @id )]'))
+ assert_equal(["<c id='b'/>", "<c id='c'/>"],
+ match.call('/a/c[ ( @id ) ]'))
+ assert_equal(["<c id='b'/>", "<c id='c'/>"],
+ match.call('/a/c [ ( @id ) ] '))
+ assert_equal(["<c id='b'/>", "<c id='c'/>"],
+ match.call(' / a / c [ ( @id ) ] '))
+ assert_equal(["<c id='b'/>", "<c id='c'/>"],
+ match.call('/ a / child:: c [( @id )] /'))
end
def test_text_nodes
@@ -692,11 +699,22 @@ module REXMLTests
end
def test_ordering
- source = "<a><b><c id='1'/><c id='2'/></b><b><d id='1'/><d id='2'/></b></a>"
+ source = <<-XML
+<a>
+ <b>
+ <c id='1'/>
+ <c id='2'/>
+ </b>
+ <b>
+ <d id='3'/>
+ <d id='4'/>
+ </b>
+</a>
+ XML
d = REXML::Document.new( source )
r = REXML::XPath.match( d, %q{/a/*/*[1]} )
- assert_equal( 1, r.size )
- r.each { |el| assert_equal( '1', el.attribute('id').value ) }
+ assert_equal(["1", "3"],
+ r.collect {|element| element.attribute("id").value})
end
def test_descendant_or_self_ordering
@@ -830,31 +848,44 @@ module REXMLTests
</a>
EOL
d = REXML::Document.new( string )
- c1 = XPath.match( d, '/a/*/*[1]' )
- assert_equal( 1, c1.length )
- assert_equal( 'c1', c1[0].name )
+ cs = XPath.match( d, '/a/*/*[1]' )
+ assert_equal(["c1", "c2"], cs.collect(&:name))
end
def test_sum
- d = Document.new("<a>"+
- "<b>1</b><b>2</b><b>3</b>"+
- "<c><d>1</d><d>2</d></c>"+
- "<e att='1'/><e att='2'/>"+
- "</a>")
-
- for v,p in [[6, "sum(/a/b)"],
- [9, "sum(//b | //d)"],
- [3, "sum(/a/e/@*)"] ]
- assert_equal( v, XPath::match( d, p ).first )
- end
+ d = Document.new(<<-XML)
+<a>
+ <b>1</b>
+ <b>2</b>
+ <b>3</b>
+ <c>
+ <d>1</d>
+ <d>2</d>
+ </c>
+ <e att='1'/>
+ <e att='2'/>
+</a>
+ XML
+
+ assert_equal([6], XPath::match(d, "sum(/a/b)"))
+ assert_equal([9], XPath::match(d, "sum(//b | //d)"))
+ assert_equal([3], XPath::match(d, "sum(/a/e/@*)"))
end
def test_xpath_namespace
- d = REXML::Document.new("<tag1 xmlns='ns1'><tag2 xmlns='ns2'/><tada>xa</tada></tag1>")
- x = d.root
- num = 0
- x.each_element('tada') { num += 1 }
- assert_equal(1, num)
+ d = REXML::Document.new(<<-XML)
+<tag1 xmlns='ns1'>
+ <tag2 xmlns='ns2'/>
+ <tada>xa</tada>
+ <tada xmlns=''>xb</tada>
+</tag1>
+ XML
+ actual = []
+ d.root.each_element('tada') do |element|
+ actual << element.to_s
+ end
+ assert_equal(["<tada>xa</tada>", "<tada xmlns=''>xb</tada>"],
+ actual)
end
def test_ticket_39
@@ -990,7 +1021,7 @@ EOF
</a>"
d = Document.new(data)
res = d.elements.to_a( "//c" ).collect {|e| e.attributes['id'].to_i}
- assert_equal( res, res.sort )
+ assert_equal((1..12).to_a, res)
end
def ticket_61_fixture(doc, xpath)
diff --git a/test/rinda/test_rinda.rb b/test/rinda/test_rinda.rb
index a07eac427c..9a25661bd9 100644
--- a/test/rinda/test_rinda.rb
+++ b/test/rinda/test_rinda.rb
@@ -17,16 +17,36 @@ class MockClock
def keeper_thread
nil
end
+
+ def stop_keeper
+ if @keeper
+ @keeper.kill
+ @keeper.join
+ @keeper = nil
+ end
+ end
end
def initialize
@now = 2
@reso = 1
+ @ts = nil
+ @inf = 2**31 - 1
+ end
+
+ def start_keeper
+ @now = 2
+ @reso = 1
+ @ts&.stop_keeper
@ts = MyTS.new
@ts.write([2, :now])
@inf = 2**31 - 1
end
+ def stop_keeper
+ @ts.stop_keeper
+ end
+
def now
@now.to_f
end
@@ -100,6 +120,14 @@ class TupleSpace
end
module TupleSpaceTestModule
+ def setup
+ MockClock.instance.start_keeper
+ end
+
+ def teardown
+ MockClock.instance.stop_keeper
+ end
+
def sleep(n)
if Thread.current == Thread.main
Time.forward(n)
@@ -241,17 +269,21 @@ module TupleSpaceTestModule
end
def test_ruby_talk_264062
- th = Thread.new { @ts.take([:empty], 1) }
+ th = Thread.new {
+ assert_raise(Rinda::RequestExpiredError) do
+ @ts.take([:empty], 1)
+ end
+ }
sleep(10)
- assert_raise(Rinda::RequestExpiredError) do
- thread_join(th)
- end
+ thread_join(th)
- th = Thread.new { @ts.read([:empty], 1) }
+ th = Thread.new {
+ assert_raise(Rinda::RequestExpiredError) do
+ @ts.read([:empty], 1)
+ end
+ }
sleep(10)
- assert_raise(Rinda::RequestExpiredError) do
- thread_join(th)
- end
+ thread_join(th)
end
def test_symbol_tuple
@@ -348,19 +380,18 @@ module TupleSpaceTestModule
template = nil
taker = Thread.new do
- @ts.take([:take, nil], 10) do |t|
- template = t
- Thread.new do
- template.cancel
- end
+ assert_raise(Rinda::RequestCanceledError) do
+ @ts.take([:take, nil], 10) do |t|
+ template = t
+ Thread.new do
+ template.cancel
+ end
+ end
end
end
sleep(2)
-
- assert_raise(Rinda::RequestCanceledError) do
- assert_nil(thread_join(taker))
- end
+ thread_join(taker)
assert(template.canceled?)
@@ -377,19 +408,18 @@ module TupleSpaceTestModule
template = nil
reader = Thread.new do
- @ts.read([:take, nil], 10) do |t|
- template = t
- Thread.new do
- template.cancel
- end
+ assert_raise(Rinda::RequestCanceledError) do
+ @ts.read([:take, nil], 10) do |t|
+ template = t
+ Thread.new do
+ template.cancel
+ end
+ end
end
end
sleep(2)
-
- assert_raise(Rinda::RequestCanceledError) do
- assert_nil(thread_join(reader))
- end
+ thread_join(reader)
assert(template.canceled?)
@@ -444,6 +474,7 @@ class TupleSpaceTest < Test::Unit::TestCase
include TupleSpaceTestModule
def setup
+ super
ThreadGroup.new.add(Thread.current)
@ts = Rinda::TupleSpace.new(1)
end
@@ -455,6 +486,7 @@ class TupleSpaceTest < Test::Unit::TestCase
th.join
end
}
+ super
end
end
@@ -462,9 +494,11 @@ class TupleSpaceProxyTest < Test::Unit::TestCase
include TupleSpaceTestModule
def setup
+ super
ThreadGroup.new.add(Thread.current)
@ts_base = Rinda::TupleSpace.new(1)
@ts = Rinda::TupleSpaceProxy.new(@ts_base)
+ @server = DRb.start_service("druby://localhost:0")
end
def teardown
# implementation-dependent
@@ -474,6 +508,8 @@ class TupleSpaceProxyTest < Test::Unit::TestCase
th.join
end
}
+ @server.stop_service
+ super
end
def test_remote_array_and_hash
@@ -502,6 +538,8 @@ class TupleSpaceProxyTest < Test::Unit::TestCase
ts = Rinda::TupleSpaceProxy.new(ro)
th = Thread.new do
ts.take([:test_take, nil])
+ rescue Interrupt
+ # Expected
end
Kernel.sleep(0.1)
th.raise(Interrupt) # causes loss of the taken tuple
@@ -531,8 +569,6 @@ class TupleSpaceProxyTest < Test::Unit::TestCase
Process.wait(write) if write && status.nil?
Process.wait(take) if take
end
-
- @server = DRb.primary_server || DRb.start_service("druby://localhost:0")
end
module RingIPv6
@@ -552,6 +588,28 @@ module RingIPv6
end
skip 'IPv6 not available'
end
+
+ def ipv6_mc(rf, hops = nil)
+ ifaddr = prepare_ipv6(rf)
+ rf.multicast_hops = hops if hops
+ begin
+ v6mc = rf.make_socket("ff02::1")
+ rescue Errno::EINVAL
+ # somehow Debian 6.0.7 needs ifname
+ v6mc = rf.make_socket("ff02::1%#{ifaddr.name}")
+ rescue Errno::EADDRNOTAVAIL
+ return # IPv6 address for multicast not available
+ rescue Errno::ENETDOWN
+ return # Network is down
+ rescue Errno::EHOSTUNREACH
+ return # Unreachable for some reason
+ end
+ begin
+ yield v6mc
+ ensure
+ v6mc.close
+ end
+ end
end
class TestRingServer < Test::Unit::TestCase
@@ -623,7 +681,11 @@ class TestRingServer < Test::Unit::TestCase
end
def test_make_socket_ipv4_multicast
- v4mc = @rs.make_socket('239.0.0.1')
+ begin
+ v4mc = @rs.make_socket('239.0.0.1')
+ rescue Errno::ENOBUFS => e
+ skip "Missing multicast support in OS: #{e.message}"
+ end
begin
if Socket.const_defined?(:SO_REUSEPORT) then
@@ -650,6 +712,8 @@ class TestRingServer < Test::Unit::TestCase
v6mc = @rs.make_socket('ff02::1')
rescue Errno::EADDRNOTAVAIL
return # IPv6 address for multicast not available
+ rescue Errno::ENOBUFS => e
+ skip "Missing multicast support in OS: #{e.message}"
end
if Socket.const_defined?(:SO_REUSEPORT) then
@@ -664,7 +728,12 @@ class TestRingServer < Test::Unit::TestCase
def test_ring_server_ipv4_multicast
@rs.shutdown
- @rs = Rinda::RingServer.new(@ts, [['239.0.0.1', '0.0.0.0']], @port)
+ begin
+ @rs = Rinda::RingServer.new(@ts, [['239.0.0.1', '0.0.0.0']], @port)
+ rescue Errno::ENOBUFS => e
+ skip "Missing multicast support in OS: #{e.message}"
+ end
+
v4mc = @rs.instance_variable_get('@sockets').first
begin
@@ -788,20 +857,10 @@ class TestRingFinger < Test::Unit::TestCase
end
def test_make_socket_ipv6_multicast
- ifaddr = prepare_ipv6(@rf)
- begin
- v6mc = @rf.make_socket("ff02::1")
- rescue Errno::EINVAL
- # somehow Debian 6.0.7 needs ifname
- v6mc = @rf.make_socket("ff02::1%#{ifaddr.name}")
- rescue Errno::EADDRNOTAVAIL
- return # IPv6 address for multicast not available
+ ipv6_mc(@rf) do |v6mc|
+ assert_equal(1, v6mc.getsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_LOOP).int)
+ assert_equal(1, v6mc.getsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_HOPS).int)
end
-
- assert_equal(1, v6mc.getsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_LOOP).int)
- assert_equal(1, v6mc.getsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_HOPS).int)
- ensure
- v6mc.close if v6mc
end
def test_make_socket_ipv4_multicast_hops
@@ -813,22 +872,11 @@ class TestRingFinger < Test::Unit::TestCase
end
def test_make_socket_ipv6_multicast_hops
- ifaddr = prepare_ipv6(@rf)
- @rf.multicast_hops = 2
- begin
- v6mc = @rf.make_socket("ff02::1")
- rescue Errno::EINVAL
- # somehow Debian 6.0.7 needs ifname
- v6mc = @rf.make_socket("ff02::1%#{ifaddr.name}")
- rescue Errno::EADDRNOTAVAIL
- return # IPv6 address for multicast not available
+ ipv6_mc(@rf, 2) do |v6mc|
+ assert_equal(2, v6mc.getsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_HOPS).int)
end
- assert_equal(2, v6mc.getsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_HOPS).int)
- ensure
- v6mc.close if v6mc
end
end
end
-
diff --git a/test/ripper/dummyparser.rb b/test/ripper/dummyparser.rb
index 3c196d9a23..ca36985893 100644
--- a/test/ripper/dummyparser.rb
+++ b/test/ripper/dummyparser.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# dummyparser.rb
#
@@ -24,6 +24,16 @@ class Node
end
list
end
+
+ class Sym < self
+ def initialize(name)
+ @name = name
+ end
+
+ def to_s
+ ":#{@name}"
+ end
+ end
end
class NodeList
@@ -38,6 +48,11 @@ class NodeList
self
end
+ def concat(item)
+ @list.concat item
+ self
+ end
+
def prepend(items)
@list.unshift items
end
@@ -148,6 +163,10 @@ class DummyParser < Ripper
"*#{var}"
end
+ def on_kwrest_param(var)
+ "**#{var}"
+ end
+
def on_blockarg(var)
"&#{var}"
end
@@ -186,7 +205,7 @@ class DummyParser < Ripper
end
def on_word_new
- ""
+ "".dup
end
def on_word_add(word, w)
@@ -209,6 +228,50 @@ class DummyParser < Ripper
words.push word
end
+ def on_symbols_new
+ NodeList.new
+ end
+
+ def on_symbols_add(symbols, symbol)
+ symbols.push Node::Sym.new(symbol)
+ end
+
+ def on_qsymbols_new
+ NodeList.new
+ end
+
+ def on_qsymbols_add(symbols, symbol)
+ symbols.push Node::Sym.new(symbol)
+ end
+
+ def on_mlhs_new
+ NodeList.new
+ end
+
+ def on_mlhs_paren(list)
+ Node.new(:mlhs, list)
+ end
+
+ def on_mlhs_add(list, node)
+ list.push node
+ end
+
+ def on_mlhs_add_block(list, blk)
+ if blk
+ list.push('&' + blk.to_s)
+ else
+ list
+ end
+ end
+
+ def on_mlhs_add_star(list, arg)
+ list.push('*' + arg.to_s)
+ end
+
+ def on_mlhs_add_post(list, post)
+ list.concat(post.list)
+ end
+
def on_rescue(exc, *rest)
Node.new('rescue', (exc && NodeList.new(exc)), *rest)
end
diff --git a/test/ripper/test_files.rb b/test/ripper/test_files.rb
index ac24727e9b..d90cd6479e 100644
--- a/test/ripper/test_files.rb
+++ b/test/ripper/test_files.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
module TestRipper; end
diff --git a/test/ripper/test_filter.rb b/test/ripper/test_filter.rb
index 0f9d38f726..c39820c321 100644
--- a/test/ripper/test_filter.rb
+++ b/test/ripper/test_filter.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require 'ripper'
require 'test/unit'
@@ -15,6 +15,7 @@ class TestRipper::Filter < Test::Unit::TestCase
data[:filename] = filename rescue nil
data[:lineno] = lineno
data[:column] = column
+ data[:state] = state
data[:token] = token
end
data
@@ -75,6 +76,16 @@ class TestRipper::Filter < Test::Unit::TestCase
assert_equal(last_columns, filter.column)
end
+ def test_filter_state
+ data = {}
+ src = File.read(filename)
+ filter = Filter.new(src)
+ assert_equal(nil, filter.state)
+ filter.parse(data)
+ assert_not_nil(data[:state])
+ assert_not_nil(filter.state)
+ end
+
def test_filter_token
data = {}
filter = Filter.new("begin; puts 1; end")
diff --git a/test/ripper/test_lexer.rb b/test/ripper/test_lexer.rb
new file mode 100644
index 0000000000..48110857f8
--- /dev/null
+++ b/test/ripper/test_lexer.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+begin
+ require 'ripper'
+ require 'test/unit'
+ ripper_test = true
+ module TestRipper; end
+rescue LoadError
+end
+
+class TestRipper::Lexer < Test::Unit::TestCase
+ def test_nested_dedent_heredoc
+ bug = '[ruby-core:80977] [Bug #13536]'
+ str = <<~'E'
+ <<~"D"
+ #{
+ <<~"B"
+ this must be a valid ruby
+ B
+ }
+ D
+ E
+ assert_equal(str, Ripper.tokenize(str).join(""), bug)
+
+ str = <<~'E'
+ <<~"D"
+ #{
+ <<~"B"
+ this must be a valid ruby
+ B
+ }
+ D
+ E
+ assert_equal(str, Ripper.tokenize(str).join(""), bug)
+ end
+
+ def test_embedded_expr_in_heredoc
+ src = <<~'E'
+ <<~B
+ #{1}
+ B
+ E
+ expect = %I[
+ on_heredoc_beg
+ on_nl
+ on_ignored_sp
+ on_embexpr_beg
+ on_int
+ on_embexpr_end
+ on_tstring_content
+ on_heredoc_end
+ ]
+ assert_equal expect, Ripper.lex(src).map {|e| e[1]}
+ end
+
+ def test_space_after_expr_in_heredoc
+ src = <<~'E'
+ <<~B
+ #{1} a
+ B
+ E
+ expect = %I[
+ on_heredoc_beg
+ on_nl
+ on_ignored_sp
+ on_embexpr_beg
+ on_int
+ on_embexpr_end
+ on_tstring_content
+ on_heredoc_end
+ ]
+ assert_equal expect, Ripper.lex(src).map {|e| e[1]}
+ end
+
+ def test_expr_at_beginning_in_heredoc
+ src = <<~'E'
+ <<~B
+ a
+ #{1}
+ B
+ E
+ expect = %I[
+ on_heredoc_beg
+ on_nl
+ on_tstring_content
+ on_embexpr_beg
+ on_int
+ on_embexpr_end
+ on_tstring_content
+ on_heredoc_end
+ ]
+ assert_equal expect, Ripper.lex(src).map {|e| e[1]}
+ end
+
+ def test_slice
+ assert_equal "string\#{nil}\n",
+ Ripper.slice(%(<<HERE\nstring\#{nil}\nHERE), "heredoc_beg .*? nl $(.*?) heredoc_end", 1)
+ end
+end
diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb
index 540d36e4d9..1f2f960020 100644
--- a/test/ripper/test_parser_events.rb
+++ b/test/ripper/test_parser_events.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
begin
require_relative 'dummyparser'
require 'test/unit'
@@ -172,8 +173,14 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
def test_operator_ambiguous
thru_operator_ambiguous = false
- parse('a=1; a %[]', :on_operator_ambiguous) {thru_operator_ambiguous = true}
+ token = syntax = nil
+ parse('a=1; a %[]', :on_operator_ambiguous) {|*a|
+ thru_operator_ambiguous = true
+ _, token, syntax = *a
+ }
assert_equal true, thru_operator_ambiguous
+ assert_equal :%, token
+ assert_equal "string literal", syntax
end
def test_array # array literal
@@ -187,38 +194,64 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
end
def test_assign_error
- # for test_coverage
+ thru_assign_error = false
+ result = parse('self = 1', :on_assign_error) {thru_assign_error = true}
+ assert_equal true, thru_assign_error
+ assert_equal '[assign(assign_error(var_field(self)),1)]', result
end
def test_assign_error_backref
thru_assign_error = false
- parse('$` = 1', :on_assign_error) {thru_assign_error = true}
+ result =
+ parse('$` = 1', :on_assign_error) {thru_assign_error = true}
assert_equal true, thru_assign_error
+ assert_equal '[assign(assign_error(var_field($`)),1)]', result
+
thru_assign_error = false
- parse('$`, _ = 1', :on_assign_error) {thru_assign_error = true}
+ result =
+ parse('$`, _ = 1', :on_assign_error) {thru_assign_error = true}
assert_equal true, thru_assign_error
+ assert_equal '[massign([assign_error(var_field($`)),var_field(_)],1)]', result
end
def test_assign_error_const_qualified
thru_assign_error = false
- parse('self::X = 1', :on_assign_error) {thru_assign_error = true}
+ result =
+ parse('self::X = 1', :on_assign_error) {thru_assign_error = true}
assert_equal false, thru_assign_error
- parse("def m\n self::X = 1\nend", :on_assign_error) {thru_assign_error = true}
+ assert_equal "[assign(const_path_field(ref(self),X),1)]", result
+
+ thru_assign_error = false
+ result =
+ parse("def m\n self::X = 1\nend", :on_assign_error) {thru_assign_error = true}
assert_equal true, thru_assign_error
+ assert_include result, "assign_error(const_path_field(ref(self),X))"
+
thru_assign_error = false
- parse("def m\n self::X, a = 1, 2\nend", :on_assign_error) {thru_assign_error = true}
+ result =
+ parse("def m\n self::X, a = 1, 2\nend", :on_assign_error) {thru_assign_error = true}
assert_equal true, thru_assign_error
+ assert_include result, "assign_error(const_path_field(ref(self),X))"
end
def test_assign_error_const
thru_assign_error = false
- parse('X = 1', :on_assign_error) {thru_assign_error = true}
+ result = parse('X = 1', :on_assign_error) {thru_assign_error = true}
+ assert_equal false, thru_assign_error
+ assert_equal "[assign(var_field(X),1)]", result
+
+ thru_assign_error = false
+ result = parse('X, a = 1, 2', :on_assign_error) {thru_assign_error = true}
assert_equal false, thru_assign_error
- parse("def m\n X = 1\nend", :on_assign_error) {thru_assign_error = true}
+ assert_include result, "massign([var_field(X),var_field(a)],"
+
+ result = parse("def m\n X = 1\nend", :on_assign_error) {thru_assign_error = true}
assert_equal true, thru_assign_error
+ assert_include result, "assign_error(var_field(X))"
thru_assign_error = false
- parse("def m\n X, a = 1, 2\nend", :on_assign_error) {thru_assign_error = true}
+ result = parse("def m\n X, a = 1, 2\nend", :on_assign_error) {thru_assign_error = true}
assert_equal true, thru_assign_error
+ assert_include result, "assign_error(var_field(X))"
end
def test_assign_error_const_toplevel
@@ -463,23 +496,46 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
thru_mlhs_add_star = false
tree = parse("a, *b = 1, 2", :on_mlhs_add_star) {thru_mlhs_add_star = true}
assert_equal true, thru_mlhs_add_star
- assert_match(/mlhs_add_star\(mlhs_add\(mlhs_new\(\),a\),b\)/, tree)
+ assert_include(tree, "massign([var_field(a),*var_field(b)]")
thru_mlhs_add_star = false
tree = parse("a, *b, c = 1, 2", :on_mlhs_add_star) {thru_mlhs_add_star = true}
assert_equal true, thru_mlhs_add_star
- assert_match(/mlhs_add\(mlhs_add_star\(mlhs_add\(mlhs_new\(\),a\),b\),mlhs_add\(mlhs_new\(\),c\)\)/, tree, bug2232)
+ assert_include(tree, "massign([var_field(a),*var_field(b),var_field(c)]", bug2232)
thru_mlhs_add_star = false
tree = parse("a, *, c = 1, 2", :on_mlhs_add_star) {thru_mlhs_add_star = true}
assert_equal true, thru_mlhs_add_star
- assert_match(/mlhs_add\(mlhs_add_star\(mlhs_add\(mlhs_new\(\),a\)\),mlhs_add\(mlhs_new\(\),c\)\)/, tree, bug4364)
+ assert_include(tree, "massign([var_field(a),*,var_field(c)]", bug4364)
thru_mlhs_add_star = false
tree = parse("*b, c = 1, 2", :on_mlhs_add_star) {thru_mlhs_add_star = true}
assert_equal true, thru_mlhs_add_star
- assert_match(/mlhs_add\(mlhs_add_star\(mlhs_new\(\),b\),mlhs_add\(mlhs_new\(\),c\)\)/, tree, bug4364)
+ assert_include(tree, "massign([*var_field(b),var_field(c)]", bug4364)
thru_mlhs_add_star = false
tree = parse("*, c = 1, 2", :on_mlhs_add_star) {thru_mlhs_add_star = true}
assert_equal true, thru_mlhs_add_star
- assert_match(/mlhs_add\(mlhs_add_star\(mlhs_new\(\)\),mlhs_add\(mlhs_new\(\),c\)\)/, tree, bug4364)
+ assert_include(tree, "massign([*,var_field(c)],", bug4364)
+ end
+
+ def test_mlhs_add_post
+ thru_mlhs_add_post = false
+ tree = parse("a, *b = 1, 2", :on_mlhs_add_post) {thru_mlhs_add_post = true}
+ assert_equal false, thru_mlhs_add_post
+ assert_include(tree, "massign([var_field(a),*var_field(b)],")
+ thru_massign_add_post = false
+ tree = parse("a, *b, c = 1, 2", :on_mlhs_add_post) {thru_mlhs_add_post = true}
+ assert_equal true, thru_mlhs_add_post
+ assert_include(tree, "massign([var_field(a),*var_field(b),var_field(c)],")
+ thru_mlhs_add_post = false
+ tree = parse("a, *, c = 1, 2", :on_mlhs_add_post) {thru_mlhs_add_post = true}
+ assert_equal true, thru_mlhs_add_post
+ assert_include(tree, "massign([var_field(a),*,var_field(c)],")
+ thru_mlhs_add_post = false
+ tree = parse("*b, c = 1, 2", :on_mlhs_add_post) {thru_mlhs_add_post = true}
+ assert_equal true, thru_mlhs_add_post
+ assert_include(tree, "massign([*var_field(b),var_field(c)],")
+ thru_mlhs_add_post = false
+ tree = parse("*, c = 1, 2", :on_mlhs_add_post) {thru_mlhs_add_post = true}
+ assert_equal true, thru_mlhs_add_post
+ assert_include(tree, "massign([*,var_field(c)],")
end
def test_mlhs_new
@@ -863,7 +919,85 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
thru_params = false
parse('a {|**x|}', :on_params) {|_, *v| thru_params = true; arg = v}
assert_equal true, thru_params
- assert_equal [nil, nil, nil, nil, nil, "x", nil], arg
+ assert_equal [nil, nil, nil, nil, nil, "**x", nil], arg
+ end
+
+ def test_params_mlhs
+ thru_mlhs = false
+ tree = parse("proc {|(a, b)|}", :on_mlhs_paren) {thru_mlhs = true}
+ assert_equal true, thru_mlhs
+ assert_include(tree, "[mlhs([a,b])]")
+ end
+
+ def test_params_mlhs_add
+ thru_mlhs_add = false
+ tree = parse("proc {|(a, b)|}", :on_mlhs_add) {thru_mlhs_add = true}
+ assert_equal true, thru_mlhs_add
+ assert_include(tree, "[mlhs([a,b])]")
+ end
+
+ def test_params_mlhs_add_star
+ thru_mlhs_add_star = false
+ tree = parse("proc {|(a, *b)|}", :on_mlhs_add_star) {thru_mlhs_add_star = true}
+ assert_equal true, thru_mlhs_add_star
+ assert_include(tree, "[mlhs([a,*b])]")
+ thru_mlhs_add_star = false
+ tree = parse("proc {|(a, *b, c)|}", :on_mlhs_add_star) {thru_mlhs_add_star = true}
+ assert_equal true, thru_mlhs_add_star
+ assert_include(tree, "[mlhs([a,*b,c])]")
+ thru_mlhs_add_star = false
+ tree = parse("proc {|(a, *, c)|}", :on_mlhs_add_star) {thru_mlhs_add_star = true}
+ assert_equal true, thru_mlhs_add_star
+ assert_include(tree, "[mlhs([a,*,c])]")
+ thru_mlhs_add_star = false
+ tree = parse("proc {|(*b, c)|}", :on_mlhs_add_star) {thru_mlhs_add_star = true}
+ assert_equal true, thru_mlhs_add_star
+ assert_include(tree, "[mlhs([*b,c])]")
+ thru_mlhs_add_star = false
+ tree = parse("proc {|(*b)|}", :on_mlhs_add_star) {thru_mlhs_add_star = true}
+ assert_equal true, thru_mlhs_add_star
+ assert_include(tree, "[mlhs([*b])]")
+ end
+
+ def test_params_mlhs_add_post
+ thru_mlhs_add_post = false
+ tree = parse("proc {|(a, *b)|}", :on_mlhs_add_post) {thru_mlhs_add_post = true}
+ assert_equal false, thru_mlhs_add_post
+ assert_include(tree, "mlhs([a,*b])")
+ thru_mlhs_add_post = false
+ tree = parse("proc {|(a, *b, c)|}", :on_mlhs_add_post) {thru_mlhs_add_post = true}
+ assert_equal true, thru_mlhs_add_post
+ assert_include(tree, "mlhs([a,*b,c])")
+ thru_mlhs_add_post = false
+ tree = parse("proc {|(a, *, c)|}", :on_mlhs_add_post) {thru_mlhs_add_post = true}
+ assert_equal true, thru_mlhs_add_post
+ assert_include(tree, "mlhs([a,*,c])")
+ thru_mlhs_add_post = false
+ tree = parse("proc {|(*b, c)|}", :on_mlhs_add_post) {thru_mlhs_add_post = true}
+ assert_equal true, thru_mlhs_add_post
+ assert_include(tree, "mlhs([*b,c])")
+ thru_mlhs_add_post = false
+ tree = parse("proc {|(*, c)|}", :on_mlhs_add_post) {thru_mlhs_add_post = true}
+ assert_equal true, thru_mlhs_add_post
+ assert_include(tree, "mlhs([*,c])")
+ end
+
+ def test_params_mlhs_new
+ thru_mlhs_new = false
+ tree = parse("proc {|(a, b)|}", :on_mlhs_new) {thru_mlhs_new = true}
+ assert_equal true, thru_mlhs_new
+ assert_include(tree, "[mlhs([a,b])]")
+ end
+
+ def test_params_mlhs_paren
+ thru_mlhs_paren = 0
+ tree = parse("proc {|(a, b)|}", :on_mlhs_paren) {thru_mlhs_paren += 1}
+ assert_equal 1, thru_mlhs_paren
+ assert_include(tree, "[mlhs([a,b])]")
+ thru_mlhs_paren = 0
+ tree = parse("proc {|((a, b))|}", :on_mlhs_paren) {thru_mlhs_paren += 1}
+ assert_equal 2, thru_mlhs_paren
+ assert_include(tree, "[mlhs([a,b])]")
end
def test_paren
@@ -880,20 +1014,35 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
def test_qwords_add
thru_qwords_add = false
- parse('%w[a]', :on_qwords_add) {thru_qwords_add = true}
+ tree = parse('%w[a]', :on_qwords_add) {thru_qwords_add = true}
assert_equal true, thru_qwords_add
+ assert_equal '[array([a])]', tree
+ thru_qwords_add = false
+ tree = parse('%w[ a ]', :on_qwords_add) {thru_qwords_add = true}
+ assert_equal true, thru_qwords_add
+ assert_equal '[array([a])]', tree
end
def test_qsymbols_add
thru_qsymbols_add = false
- parse('%i[a]', :on_qsymbols_add) {thru_qsymbols_add = true}
+ tree = parse('%i[a]', :on_qsymbols_add) {thru_qsymbols_add = true}
assert_equal true, thru_qsymbols_add
+ assert_equal '[array([:a])]', tree
+ thru_qsymbols_add = false
+ tree = parse('%i[ a ]', :on_qsymbols_add) {thru_qsymbols_add = true}
+ assert_equal true, thru_qsymbols_add
+ assert_equal '[array([:a])]', tree
end
def test_symbols_add
thru_symbols_add = false
- parse('%I[a]', :on_symbols_add) {thru_symbols_add = true}
+ tree = parse('%I[a]', :on_symbols_add) {thru_symbols_add = true}
+ assert_equal true, thru_symbols_add
+ assert_equal '[array([:a])]', tree
+ thru_symbols_add = false
+ tree = parse('%I[ a ]', :on_symbols_add) {thru_symbols_add = true}
assert_equal true, thru_symbols_add
+ assert_equal '[array([:a])]', tree
end
def test_qwords_new
@@ -971,6 +1120,15 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
assert_equal true, thru_rest_param
end
+ def test_kwrest_param
+ thru_kwrest = false
+ parse('def a(**) end', :on_kwrest_param) {|n, val| thru_kwrest = val}
+ assert_equal nil, thru_kwrest
+ thru_kwrest = false
+ parse('def a(**x) end', :on_kwrest_param) {|n, val| thru_kwrest = val}
+ assert_equal "x", thru_kwrest
+ end
+
def test_retry
thru_retry = false
parse('retry', :on_retry) {thru_retry = true}
@@ -1234,8 +1392,13 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
def test_words_add
thru_words_add = false
- parse('%W[a]', :on_words_add) {thru_words_add = true}
+ tree = parse('%W[a]', :on_words_add) {thru_words_add = true}
assert_equal true, thru_words_add
+ assert_equal '[array([a])]', tree
+ thru_words_add = false
+ tree = parse('%W[ a ]', :on_words_add) {thru_words_add = true}
+ assert_equal true, thru_words_add
+ assert_equal '[array([a])]', tree
end
def test_words_new
@@ -1294,8 +1457,11 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
def test_block_variables
assert_equal("[fcall(proc,[],&block([],[void()]))]", parse("proc{|;y|}"))
if defined?(Process::RLIMIT_AS)
- assert_in_out_err(["-I#{File.dirname(__FILE__)}", "-rdummyparser"],
- 'Process.setrlimit(Process::RLIMIT_AS,100*1024*1024); puts DummyParser.new("proc{|;y|!y}").parse',
+ dir = File.dirname(__FILE__)
+ as = (RubyVM::MJIT.enabled? ? 150 : 100) * 1024 * 1024
+ assert_in_out_err(%W(-I#{dir} -rdummyparser),
+ "Process.setrlimit(Process::RLIMIT_AS,#{as}); "\
+ "puts DummyParser.new('proc{|;y|!y}').parse",
["[fcall(proc,[],&block([],[unary(!,ref(y))]))]"], [], '[ruby-dev:39423]')
end
end
@@ -1321,13 +1487,6 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
assert_equal("`$' without identifiers is not allowed as a global variable name", compile_error('$'))
end
- def test_warning_shadowing
- fmt, *args = warning("x = 1; tap {|;x|}")
- assert_match(/shadowing outer local variable/, fmt)
- assert_equal("x", args[0])
- assert_match(/x/, fmt % args)
- end
-
def test_warning_ignored_magic_comment
fmt, *args = warning("1; #-*- frozen-string-literal: true -*-")
assert_match(/ignored after any tokens/, fmt)
@@ -1339,4 +1498,10 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
assert_warn("") {fmt, = warn("\r;")}
assert_match(/encountered/, fmt)
end
+
+ def test_warn_mismatched_indentations
+ fmt, tokend, tokbeg, line = assert_warning("") {break warn("if true\n end\n")}
+ assert_match(/mismatched indentations/, fmt)
+ assert_equal(["if", "end", 1], [tokbeg, tokend, line])
+ end
end if ripper_test
diff --git a/test/ripper/test_ripper.rb b/test/ripper/test_ripper.rb
index c2084d8b01..e7d20a66a0 100644
--- a/test/ripper/test_ripper.rb
+++ b/test/ripper/test_ripper.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require 'ripper'
require 'test/unit'
@@ -17,6 +17,10 @@ class TestRipper::Ripper < Test::Unit::TestCase
assert_nil @ripper.column
end
+ def test_state
+ assert_nil @ripper.state
+ end
+
def test_encoding
assert_equal Encoding::UTF_8, @ripper.encoding
ripper = Ripper.new('# coding: iso-8859-15')
@@ -37,7 +41,7 @@ class TestRipper::Ripper < Test::Unit::TestCase
def test_filename
assert_equal '(ripper)', @ripper.filename
- filename = "ripper"
+ filename = "ripper".dup
ripper = Ripper.new("", filename)
filename.clear
assert_equal "ripper", ripper.filename
@@ -63,11 +67,11 @@ class TestRipper::Ripper < Test::Unit::TestCase
def test_regexp_with_option
bug11932 = '[ruby-core:72638] [Bug #11932]'
- src = '/[\xC0-\xF0]/u'.force_encoding(Encoding::UTF_8)
+ src = '/[\xC0-\xF0]/u'.dup.force_encoding(Encoding::UTF_8)
ripper = Ripper.new(src)
ripper.parse
assert_predicate(ripper, :error?)
- src = '/[\xC0-\xF0]/n'.force_encoding(Encoding::UTF_8)
+ src = '/[\xC0-\xF0]/n'.dup.force_encoding(Encoding::UTF_8)
ripper = Ripper.new(src)
ripper.parse
assert_not_predicate(ripper, :error?, bug11932)
@@ -101,11 +105,11 @@ class TestRipper::Ripper < Test::Unit::TestCase
# https://bugs.jruby.org/4176
def test_dedent_string
- col = Ripper.dedent_string ' hello', 0
+ col = Ripper.dedent_string ' hello'.dup, 0
assert_equal 0, col
- col = Ripper.dedent_string ' hello', 2
+ col = Ripper.dedent_string ' hello'.dup, 2
assert_equal 2, col
- col = Ripper.dedent_string ' hello', 4
+ col = Ripper.dedent_string ' hello'.dup, 4
assert_equal 2, col
# lexing a squiggly heredoc triggers Ripper#dedent_string use
@@ -117,4 +121,22 @@ end
assert_nothing_raised { Ripper.lex src }
end
+
+ class TestInput < self
+ Input = Struct.new(:lines) do
+ def gets
+ lines.shift
+ end
+ end
+
+ def setup
+ @ripper = Ripper.new(Input.new(["1 + 1"]))
+ end
+
+ def test_invalid_gets
+ ripper = assert_nothing_raised {Ripper.new(Input.new([0]))}
+ assert_raise(TypeError) {ripper.parse}
+ end
+ end
+
end if ripper_test
diff --git a/test/ripper/test_scanner_events.rb b/test/ripper/test_scanner_events.rb
index 1b7b56ebfe..1a3413a0f0 100644
--- a/test/ripper/test_scanner_events.rb
+++ b/test/ripper/test_scanner_events.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# test_scanner_events.rb
#
@@ -48,67 +48,71 @@ class TestRipper::ScannerEvents < Test::Unit::TestCase
def test_lex
assert_equal [],
Ripper.lex('')
- assert_equal [[[1,0], :on_ident, "a"]],
+ assert_equal [[[1,0], :on_ident, "a", Ripper::EXPR_CMDARG]],
Ripper.lex('a')
- assert_equal [[[1, 0], :on_kw, "nil"]],
+ assert_equal [[[1, 0], :on_kw, "nil", Ripper::EXPR_END]],
Ripper.lex("nil")
- assert_equal [[[1, 0], :on_kw, "def"],
- [[1, 3], :on_sp, " "],
- [[1, 4], :on_ident, "m"],
- [[1, 5], :on_lparen, "("],
- [[1, 6], :on_ident, "a"],
- [[1, 7], :on_rparen, ")"],
- [[1, 8], :on_kw, "end"]],
+ assert_equal [[[1, 0], :on_kw, "def", Ripper::EXPR_FNAME],
+ [[1, 3], :on_sp, " ", Ripper::EXPR_FNAME],
+ [[1, 4], :on_ident, "m", Ripper::EXPR_ENDFN],
+ [[1, 5], :on_lparen, "(", Ripper::EXPR_BEG | Ripper::EXPR_LABEL],
+ [[1, 6], :on_ident, "a", Ripper::EXPR_ARG],
+ [[1, 7], :on_rparen, ")", Ripper::EXPR_ENDFN],
+ [[1, 8], :on_kw, "end", Ripper::EXPR_END]],
Ripper.lex("def m(a)end")
- assert_equal [[[1, 0], :on_int, "1"],
- [[1, 1], :on_nl, "\n"],
- [[2, 0], :on_int, "2"],
- [[2, 1], :on_nl, "\n"],
- [[3, 0], :on_int, "3"]],
+ assert_equal [[[1, 0], :on_int, "1", Ripper::EXPR_END],
+ [[1, 1], :on_nl, "\n", Ripper::EXPR_BEG],
+ [[2, 0], :on_int, "2", Ripper::EXPR_END],
+ [[2, 1], :on_nl, "\n", Ripper::EXPR_BEG],
+ [[3, 0], :on_int, "3", Ripper::EXPR_END]],
Ripper.lex("1\n2\n3")
- assert_equal [[[1, 0], :on_heredoc_beg, "<<""EOS"],
- [[1, 5], :on_nl, "\n"],
- [[2, 0], :on_tstring_content, "heredoc\n"],
- [[3, 0], :on_heredoc_end, "EOS"]],
+ assert_equal [[[1, 0], :on_heredoc_beg, "<<""EOS", Ripper::EXPR_BEG],
+ [[1, 5], :on_nl, "\n", Ripper::EXPR_BEG],
+ [[2, 0], :on_tstring_content, "heredoc\n", Ripper::EXPR_BEG],
+ [[3, 0], :on_heredoc_end, "EOS", Ripper::EXPR_BEG]],
Ripper.lex("<<""EOS\nheredoc\nEOS")
- assert_equal [[[1, 0], :on_heredoc_beg, "<<""EOS"],
- [[1, 5], :on_nl, "\n"],
- [[2, 0], :on_heredoc_end, "EOS"]],
+ assert_equal [[[1, 0], :on_heredoc_beg, "<<""EOS", Ripper::EXPR_BEG],
+ [[1, 5], :on_nl, "\n", Ripper::EXPR_BEG],
+ [[2, 0], :on_heredoc_end, "EOS", Ripper::EXPR_BEG]],
Ripper.lex("<<""EOS\nEOS"),
"bug#4543"
- assert_equal [[[1, 0], :on_regexp_beg, "/"],
- [[1, 1], :on_tstring_content, "foo\nbar"],
- [[2, 3], :on_regexp_end, "/"]],
+ assert_equal [[[1, 0], :on_regexp_beg, "/", Ripper::EXPR_BEG],
+ [[1, 1], :on_tstring_content, "foo\nbar", Ripper::EXPR_BEG],
+ [[2, 3], :on_regexp_end, "/", Ripper::EXPR_BEG]],
Ripper.lex("/foo\nbar/")
- assert_equal [[[1, 0], :on_regexp_beg, "/"],
- [[1, 1], :on_tstring_content, "foo\n\u3020"],
- [[2, 3], :on_regexp_end, "/"]],
+ assert_equal [[[1, 0], :on_regexp_beg, "/", Ripper::EXPR_BEG],
+ [[1, 1], :on_tstring_content, "foo\n\u3020", Ripper::EXPR_BEG],
+ [[2, 3], :on_regexp_end, "/", Ripper::EXPR_BEG]],
Ripper.lex("/foo\n\u3020/")
- assert_equal [[[1, 0], :on_tstring_beg, "'"],
- [[1, 1], :on_tstring_content, "foo\n\xe3\x80\xa0"],
- [[2, 3], :on_tstring_end, "'"]],
+ assert_equal [[[1, 0], :on_tstring_beg, "'", Ripper::EXPR_BEG],
+ [[1, 1], :on_tstring_content, "foo\n\xe3\x80\xa0", Ripper::EXPR_BEG],
+ [[2, 3], :on_tstring_end, "'", Ripper::EXPR_END]],
Ripper.lex("'foo\n\xe3\x80\xa0'")
- assert_equal [[[1, 0], :on_tstring_beg, "'"],
- [[1, 1], :on_tstring_content, "\u3042\n\u3044"],
- [[2, 3], :on_tstring_end, "'"]],
+ assert_equal [[[1, 0], :on_tstring_beg, "'", Ripper::EXPR_BEG],
+ [[1, 1], :on_tstring_content, "\u3042\n\u3044", Ripper::EXPR_BEG],
+ [[2, 3], :on_tstring_end, "'", Ripper::EXPR_END]],
Ripper.lex("'\u3042\n\u3044'")
- assert_equal [[[1, 0], :on_rational, "1r"],
- [[1, 2], :on_nl, "\n"],
- [[2, 0], :on_imaginary, "2i"],
- [[2, 2], :on_nl, "\n"],
- [[3, 0], :on_imaginary, "3ri"],
- [[3, 3], :on_nl, "\n"],
- [[4, 0], :on_rational, "4.2r"],
- [[4, 4], :on_nl, "\n"],
- [[5, 0], :on_imaginary, "5.6ri"],
+ assert_equal [[[1, 0], :on_rational, "1r", Ripper::EXPR_END],
+ [[1, 2], :on_nl, "\n", Ripper::EXPR_BEG],
+ [[2, 0], :on_imaginary, "2i", Ripper::EXPR_END],
+ [[2, 2], :on_nl, "\n", Ripper::EXPR_BEG],
+ [[3, 0], :on_imaginary, "3ri", Ripper::EXPR_END],
+ [[3, 3], :on_nl, "\n", Ripper::EXPR_BEG],
+ [[4, 0], :on_rational, "4.2r", Ripper::EXPR_END],
+ [[4, 4], :on_nl, "\n", Ripper::EXPR_BEG],
+ [[5, 0], :on_imaginary, "5.6ri", Ripper::EXPR_END],
],
Ripper.lex("1r\n2i\n3ri\n4.2r\n5.6ri")
- assert_equal [[[1, 0], :on_heredoc_beg, "<<~EOS"],
- [[1, 6], :on_nl, "\n"],
- [[2, 2], :on_tstring_content, "heredoc\n"],
- [[3, 0], :on_heredoc_end, "EOS"]
+ assert_equal [[[1, 0], :on_heredoc_beg, "<<~EOS", Ripper::EXPR_BEG],
+ [[1, 6], :on_nl, "\n", Ripper::EXPR_BEG],
+ [[2, 0], :on_ignored_sp, " ", Ripper::EXPR_BEG],
+ [[2, 2], :on_tstring_content, "heredoc\n", Ripper::EXPR_BEG],
+ [[3, 0], :on_heredoc_end, "EOS", Ripper::EXPR_BEG]
],
Ripper.lex("<<~EOS\n heredoc\nEOS")
+ assert_equal [[[1, 0], :on_tstring_beg, "'", Ripper::EXPR_BEG],
+ [[1, 1], :on_tstring_content, "foo", Ripper::EXPR_BEG]],
+ Ripper.lex("'foo")
end
def test_location
@@ -131,10 +135,12 @@ class TestRipper::ScannerEvents < Test::Unit::TestCase
assert_location %Q["a\nb\r\nc"]
assert_location "print(<<""EOS)\nheredoc\nEOS\n"
assert_location "print(<<-\"EOS\")\nheredoc\n EOS\n"
+ assert_location "'foo'"
+ assert_location "'foo"
end
def assert_location(src)
- buf = ''
+ buf = ''.dup
Ripper.lex(src).each do |pos, type, tok|
line, col = *pos
assert_equal buf.count("\n") + 1, line,
@@ -631,8 +637,10 @@ class TestRipper::ScannerEvents < Test::Unit::TestCase
scan('words_beg', '%W()')
assert_equal ['%W('],
scan('words_beg', '%W(w w w)')
- assert_equal ['%W( '],
+ assert_equal ['%W('],
scan('words_beg', '%W( w w w )')
+ assert_equal ['%W('],
+ scan('words_beg', "%W(\nw)")
end
def test_qwords_beg
@@ -642,8 +650,10 @@ class TestRipper::ScannerEvents < Test::Unit::TestCase
scan('qwords_beg', '%w()')
assert_equal ['%w('],
scan('qwords_beg', '%w(w w w)')
- assert_equal ['%w( '],
+ assert_equal ['%w('],
scan('qwords_beg', '%w( w w w )')
+ assert_equal ['%w('],
+ scan('qwords_beg', "%w(\nw)")
end
def test_qsymbols_beg
@@ -653,8 +663,10 @@ class TestRipper::ScannerEvents < Test::Unit::TestCase
scan('qsymbols_beg', '%i()')
assert_equal ['%i('],
scan('qsymbols_beg', '%i(w w w)')
- assert_equal ['%i( '],
+ assert_equal ['%i('],
scan('qsymbols_beg', '%i( w w w )')
+ assert_equal ['%i('],
+ scan('qsymbols_beg', "%i(\nw)")
end
def test_symbols_beg
@@ -664,22 +676,25 @@ class TestRipper::ScannerEvents < Test::Unit::TestCase
scan('symbols_beg', '%I()')
assert_equal ['%I('],
scan('symbols_beg', '%I(w w w)')
- assert_equal ['%I( '],
+ assert_equal ['%I('],
scan('symbols_beg', '%I( w w w )')
+ assert_equal ['%I('],
+ scan('symbols_beg', "%I(\nw)")
end
- # FIXME: Close paren must not present (`words_end' scanner event?).
def test_words_sep
assert_equal [],
scan('words_sep', '')
- assert_equal [')'],
+ assert_equal [],
scan('words_sep', '%w()')
- assert_equal [' ', ' ', ')'],
+ assert_equal [' ', ' '],
scan('words_sep', '%w(w w w)')
- assert_equal [' ', ' ', ' )'],
+ assert_equal [' ', ' ', ' ', ' '],
scan('words_sep', '%w( w w w )')
- assert_equal ["\n", ' ', ' )'],
+ assert_equal [' ', "\n", ' ', ' '],
scan('words_sep', "%w( w\nw w )")
+ assert_equal ["\n\n", "\n ", ' ', ' '],
+ scan('words_sep', "%w(\n\nw\n w w )")
end
def test_heredoc_beg
@@ -825,6 +840,11 @@ class TestRipper::ScannerEvents < Test::Unit::TestCase
scan('sp', "%w( w )")
assert_equal [],
scan('sp', "p(/ /)")
+
+ assert_equal ["\\\n"],
+ scan('sp', "\\\n")
+ assert_equal ['\ '],
+ scan('sp', '\ ')
end
# `nl' event always means End-Of-Statement.
@@ -866,6 +886,13 @@ class TestRipper::ScannerEvents < Test::Unit::TestCase
scan('ignored_nl', "1;\r\n")
end
+ def test_ignored_sp
+ assert_equal [],
+ scan('ignored_sp', "<<~EOS\nheredoc\nEOS")
+ assert_equal [" "],
+ scan('ignored_sp', "<<~EOS\n heredoc\nEOS")
+ end
+
def test___end__
assert_equal [],
scan('__end__', "")
diff --git a/test/ripper/test_sexp.rb b/test/ripper/test_sexp.rb
index 46fb7f292a..d63464d5a7 100644
--- a/test/ripper/test_sexp.rb
+++ b/test/ripper/test_sexp.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
begin
require 'ripper'
require 'test/unit'
@@ -60,6 +60,56 @@ eot
assert_equal clear_pos(sexp1), clear_pos(sexp2)
end
+ def test_params_mlhs
+ sexp = Ripper.sexp("proc {|(w, *x, y), z|}")
+ _, ((mlhs, w, (rest, x), y), z) = search_sexp(:params, sexp)
+ assert_equal(:mlhs, mlhs)
+ assert_equal(:@ident, w[0])
+ assert_equal("w", w[1])
+ assert_equal(:rest_param, rest)
+ assert_equal(:@ident, x[0])
+ assert_equal("x", x[1])
+ assert_equal(:@ident, y[0])
+ assert_equal("y", y[1])
+ assert_equal(:@ident, z[0])
+ assert_equal("z", z[1])
+ end
+
+ def test_def_fname
+ sexp = Ripper.sexp("def t; end")
+ _, (type, fname,) = search_sexp(:def, sexp)
+ assert_equal(:@ident, type)
+ assert_equal("t", fname)
+
+ sexp = Ripper.sexp("def <<; end")
+ _, (type, fname,) = search_sexp(:def, sexp)
+ assert_equal(:@op, type)
+ assert_equal("<<", fname)
+ end
+
+ def test_defs_fname
+ sexp = Ripper.sexp("def self.t; end")
+ _, recv, _, (type, fname) = search_sexp(:defs, sexp)
+ assert_equal(:var_ref, recv[0], recv)
+ assert_equal([:@kw, "self", [1, 4]], recv[1], recv)
+ assert_equal(:@ident, type)
+ assert_equal("t", fname)
+ end
+
+ def test_named_with_default
+ sexp = Ripper.sexp("def hello(bln: true, int: 1, str: 'str', sym: :sym) end")
+ named = String.new
+ search_sexp(:params, sexp)[5].each { |i| named << "#{i}\n" } # join flattens
+ exp = "#{<<-"{#"}#{<<~'};'}"
+ {#
+ [[:@label, "bln:", [1, 10]], [:var_ref, [:@kw, "true", [1, 15]]]]
+ [[:@label, "int:", [1, 21]], [:@int, "1", [1, 26]]]
+ [[:@label, "str:", [1, 29]], [:string_literal, [:string_content, [:@tstring_content, "str", [1, 35]]]]]
+ [[:@label, "sym:", [1, 41]], [:symbol_literal, [:symbol, [:@ident, "sym", [1, 47]]]]]
+ };
+ assert_equal(exp, named)
+ end
+
def search_sexp(sym, sexp)
return sexp if !sexp or sexp[0] == sym
sexp.find do |e|
@@ -82,4 +132,12 @@ eot
end
end
end
+
+ def test_dsym
+ bug15670 = '[ruby-core:91852]'
+ _, (_, _, s) = Ripper.sexp_raw(%q{:"sym"})
+ assert_equal([:dyna_symbol, [:string_add, [:string_content], [:@tstring_content, "sym", [1, 2]]]],
+ s,
+ bug15670)
+ end
end if ripper_test
diff --git a/test/rss/test_itunes.rb b/test/rss/test_itunes.rb
index 7be001062b..456e0afb20 100644
--- a/test/rss/test_itunes.rb
+++ b/test/rss/test_itunes.rb
@@ -224,9 +224,13 @@ module RSS
def assert_itunes_explicit(readers, &rss20_maker)
_wrap_assertion do
+ _assert_itunes_explicit(true, "explicit", readers, &rss20_maker)
_assert_itunes_explicit(true, "yes", readers, &rss20_maker)
+ _assert_itunes_explicit(true, "true", readers, &rss20_maker)
_assert_itunes_explicit(false, "clean", readers, &rss20_maker)
- _assert_itunes_explicit(nil, "no", readers, &rss20_maker)
+ _assert_itunes_explicit(false, "no", readers, &rss20_maker)
+ _assert_itunes_explicit(false, "false", readers, &rss20_maker)
+ _assert_itunes_explicit(nil, "invalid", readers, &rss20_maker)
end
end
diff --git a/test/rss/test_maker_itunes.rb b/test/rss/test_maker_itunes.rb
index 03092b1c41..5b435f5aec 100644
--- a/test/rss/test_maker_itunes.rb
+++ b/test/rss/test_maker_itunes.rb
@@ -279,10 +279,20 @@ module RSS
def assert_maker_itunes_explicit(maker_readers, feed_readers=nil)
_wrap_assertion do
feed_readers ||= maker_readers
- _assert_maker_itunes_explicit(true, "yes", maker_readers, feed_readers)
+ _assert_maker_itunes_explicit(true, "explicit",
+ maker_readers, feed_readers)
+ _assert_maker_itunes_explicit(true, "yes",
+ maker_readers, feed_readers)
+ _assert_maker_itunes_explicit(true, "true",
+ maker_readers, feed_readers)
_assert_maker_itunes_explicit(false, "clean",
maker_readers, feed_readers)
- _assert_maker_itunes_explicit(nil, "no", maker_readers, feed_readers)
+ _assert_maker_itunes_explicit(false, "no",
+ maker_readers, feed_readers)
+ _assert_maker_itunes_explicit(false, "false",
+ maker_readers, feed_readers)
+ _assert_maker_itunes_explicit(nil, "invalid",
+ maker_readers, feed_readers)
end
end
diff --git a/test/rss/test_parser.rb b/test/rss/test_parser.rb
index 7d64657d57..4e7cc1b963 100644
--- a/test/rss/test_parser.rb
+++ b/test/rss/test_parser.rb
@@ -61,5 +61,61 @@ EOR
EOR
end
end
+
+ def test_parse_option_validate_nil
+ assert_raise(RSS::MissingTagError) do
+ RSS::Parser.parse(make_RDF(<<-RDF), :validate => nil)
+ RDF
+ end
+ end
+
+ def test_parse_option_validate_true
+ assert_raise(RSS::MissingTagError) do
+ RSS::Parser.parse(make_RDF(<<-RDF), :validate => true)
+ RDF
+ end
+ end
+
+ def test_parse_option_validate_false
+ rdf = RSS::Parser.parse(make_RDF(<<-RDF), :validate => false)
+ RDF
+ assert_nil(rdf.channel)
+ end
+
+ def test_parse_option_ignore_unknown_element_nil
+ assert_nothing_raised do
+ RSS::Parser.parse(make_RDF(<<-RDF), :ignore_unknown_element => nil)
+<unknown/>
+#{make_channel}
+#{make_item}
+#{make_textinput}
+#{make_image}
+ RDF
+ end
+ end
+
+ def test_parse_option_ignore_unknown_element_true
+ assert_nothing_raised do
+ RSS::Parser.parse(make_RDF(<<-RDF), :ignore_unknown_element => true)
+<unknown/>
+#{make_channel}
+#{make_item}
+#{make_textinput}
+#{make_image}
+ RDF
+ end
+ end
+
+ def test_parse_option_ignore_unknown_element_false
+ assert_raise(RSS::NotExpectedTagError) do
+ RSS::Parser.parse(make_RDF(<<-RDF), :ignore_unknown_element => false)
+<unknown/>
+#{make_channel}
+#{make_item}
+#{make_textinput}
+#{make_image}
+ RDF
+ end
+ end
end
end
diff --git a/test/rss/test_to_s.rb b/test/rss/test_to_s.rb
index b8ef0d8da4..bbdd74ef0b 100644
--- a/test/rss/test_to_s.rb
+++ b/test/rss/test_to_s.rb
@@ -109,6 +109,30 @@ module RSS
'[ruby-core:70667] [Bug #11509]')
end
+ def test_20_empty_text
+ title = "Blog entries"
+ link = "http://blog.example.com/"
+ description = ""
+ rss = RSS::Maker.make("2.0") do |maker|
+ maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+ end
+
+ parsed_rss = RSS::Parser.parse(rss.to_s)
+ assert_equal({
+ title: title,
+ link: link,
+ description: description,
+ },
+ {
+ title: parsed_rss.channel.title,
+ link: parsed_rss.channel.link,
+ description: parsed_rss.channel.description,
+ },
+ "[ruby-core:80965] [Bug #13531]")
+ end
+
private
def setup_xml_declaration_info
@version = "1.0"
diff --git a/test/ruby/bug-13526.rb b/test/ruby/bug-13526.rb
new file mode 100644
index 0000000000..50c6c67a7d
--- /dev/null
+++ b/test/ruby/bug-13526.rb
@@ -0,0 +1,22 @@
+# From https://bugs.ruby-lang.org/issues/13526#note-1
+
+Thread.report_on_exception = true
+
+sleep if $load
+$load = true
+
+n = 10
+threads = Array.new(n) do
+ Thread.new do
+ begin
+ autoload :Foo, File.expand_path(__FILE__)
+ Thread.pass
+ Foo
+ ensure
+ Thread.pass
+ end
+ end
+end
+
+Thread.pass until threads.all?(&:stop?)
+1000.times { Thread.pass }
diff --git a/test/ruby/enc/test_case_comprehensive.rb b/test/ruby/enc/test_case_comprehensive.rb
index 13639f35e8..bde47017a2 100644
--- a/test/ruby/enc/test_case_comprehensive.rb
+++ b/test/ruby/enc/test_case_comprehensive.rb
@@ -3,9 +3,10 @@
require "test/unit"
-class TestComprehensiveCaseFold < Test::Unit::TestCase
+class TestComprehensiveCaseMapping < Test::Unit::TestCase
UNICODE_VERSION = RbConfig::CONFIG['UNICODE_VERSION']
- UNICODE_DATA_PATH = "../../../enc/unicode/data/#{UNICODE_VERSION}"
+ path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__)
+ UNICODE_DATA_PATH = File.directory?("#{path}/ucd") ? "#{path}/ucd" : path
def self.hex2utf8(s)
s.split(' ').map { |c| c.to_i(16) }.pack('U*')
@@ -22,20 +23,20 @@ class TestComprehensiveCaseFold < Test::Unit::TestCase
end
def test_data_files_available
- unless TestComprehensiveCaseFold.data_files_available?
+ unless TestComprehensiveCaseMapping.data_files_available?
skip "Unicode data files not available in #{UNICODE_DATA_PATH}."
end
end
end
-TestComprehensiveCaseFold.data_files_available? and class TestComprehensiveCaseFold
+TestComprehensiveCaseMapping.data_files_available? and class TestComprehensiveCaseMapping
(CaseTest = Struct.new(:method_name, :attributes, :first_data, :follow_data)).class_eval do
def initialize(method_name, attributes, first_data, follow_data=first_data)
super
end
end
- def self.read_data_file (filename)
+ def self.read_data_file(filename)
IO.foreach(expand_filename(filename), encoding: Encoding::ASCII_8BIT) do |line|
if $. == 1
if filename == 'UnicodeData'
@@ -72,7 +73,11 @@ TestComprehensiveCaseFold.data_files_available? and class TestComprehensiveCase
@@codepoints << code
upcase[code] = hex2utf8 data[12] unless data[12].empty?
downcase[code] = hex2utf8 data[13] unless data[13].empty?
- titlecase[code] = hex2utf8 data[14] unless data[14].empty?
+ if code>="\u1C90" and code<="\u1CBF" # exception for Georgian: use lowercase for titlecase
+ titlecase[code] = hex2utf8(data[13]) unless data[13].empty?
+ else
+ titlecase[code] = hex2utf8 data[14] unless data[14].empty?
+ end
end
read_data_file('CaseFolding') do |code, data|
casefold[code] = hex2utf8(data[2]) if data[1] =~ /^[CF]$/
@@ -140,7 +145,7 @@ TestComprehensiveCaseFold.data_files_available? and class TestComprehensiveCase
@@tests ||= []
end
- def self.generate_unicode_case_mapping_tests (encoding)
+ def self.generate_unicode_case_mapping_tests(encoding)
all_tests.each do |test|
attributes = test.attributes.map(&:to_s).join '-'
attributes.prepend '_' unless attributes.empty?
@@ -149,14 +154,14 @@ TestComprehensiveCaseFold.data_files_available? and class TestComprehensiveCase
source = code.encode(encoding) * 5
target = "#{test.first_data[code]}#{test.follow_data[code]*4}".encode(encoding)
result = source.__send__(test.method_name, *test.attributes)
- assert_equal target, target,
+ assert_equal target, result,
proc{"from #{code*5} (#{source.dump}) expected #{target.dump} but was #{result.dump}"}
end
end
end
end
- def self.generate_case_mapping_tests (encoding)
+ def self.generate_case_mapping_tests(encoding)
all_tests
# preselect codepoints to speed up testing for small encodings
codepoints = @@codepoints.select do |code|
@@ -198,7 +203,7 @@ TestComprehensiveCaseFold.data_files_available? and class TestComprehensiveCase
end
# test for encodings that don't yet (or will never) deal with non-ASCII characters
- def self.generate_ascii_only_case_mapping_tests (encoding)
+ def self.generate_ascii_only_case_mapping_tests(encoding)
all_tests
# preselect codepoints to speed up testing for small encodings
codepoints = @@codepoints.select do |code|
diff --git a/test/ruby/enc/test_case_mapping.rb b/test/ruby/enc/test_case_mapping.rb
index dc14d8cb4f..aa20531783 100644
--- a/test/ruby/enc/test_case_mapping.rb
+++ b/test/ruby/enc/test_case_mapping.rb
@@ -162,6 +162,54 @@ class TestCaseMappingPreliminary < Test::Unit::TestCase
check_upcase_properties 'ΑΒΓΔΕΖΗΘΙΚΛΜÎΞΟΠΡΣΤΥΦΧΨΩ', 'αβγδεζηθικλμνξοπÏστυφχψω'
end
+ # This test checks against problems when changing the order of mapping results
+ # in some of the entries of the unfolding table (related to
+ # https://bugs.ruby-lang.org/issues/12990).
+ def test_reorder_unfold
+ # GREEK SMALL LETTER IOTA
+ assert_equal 0, "\u03B9" =~ /\u0345/i
+ assert_equal 0, "\u0345" =~ /\u03B9/i
+ assert_equal 0, "\u03B9" =~ /\u0399/i
+ assert_equal 0, "\u0399" =~ /\u03B9/i
+ assert_equal 0, "\u03B9" =~ /\u1fbe/i
+ assert_equal 0, "\u1fbe" =~ /\u03B9/i
+
+ # GREEK SMALL LETTER MU
+ assert_equal 0, "\u03BC" =~ /\u00B5/i
+ assert_equal 0, "\u00B5" =~ /\u03BC/i
+ assert_equal 0, "\u03BC" =~ /\u039C/i
+ assert_equal 0, "\u039C" =~ /\u03BC/i
+
+ # CYRILLIC SMALL LETTER MONOGRAPH UK
+ assert_equal 0, "\uA64B" =~ /\u1c88/i
+ assert_equal 0, "\u1c88" =~ /\uA64B/i
+ assert_equal 0, "\uA64B" =~ /\ua64A/i
+ assert_equal 0, "\ua64A" =~ /\uA64B/i
+ end
+
+ def test_georgian_canary
+ message = "Reexamine implementation of Georgian in String#capitalize"
+ assert_equal false, "\u1CBB".match?(/\p{assigned}/), message
+ assert_equal false, "\u1CBC".match?(/\p{assigned}/), message
+ end
+
+ def test_georgian_unassigned
+ message = "Unassigned codepoints should not be converted"
+ assert_equal "\u1CBB", "\u1CBB".capitalize, message
+ assert_equal "\u1CBC", "\u1CBC".capitalize, message
+ end
+
+ def test_georgian_capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u1C90\u1C91\u1C92".capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u1C90\u1C91\u10D2".capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u1C90\u10D1\u1C92".capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u1C90\u10D1\u10D2".capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u10D0\u1C91\u1C92".capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u10D0\u1C91\u10D2".capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u10D0\u10D1\u1C92".capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u10D0\u10D1\u10D2".capitalize
+ end
+
def no_longer_a_test_buffer_allocations
assert_equal 'TURKISH*ı'*10, ('I'*10).downcase(:turkic)
assert_equal 'TURKISH*ı'*100, ('I'*100).downcase(:turkic)
diff --git a/test/ruby/enc/test_case_options.rb b/test/ruby/enc/test_case_options.rb
index 761c92d51d..e9bf50fcfc 100644
--- a/test/ruby/enc/test_case_options.rb
+++ b/test/ruby/enc/test_case_options.rb
@@ -3,21 +3,21 @@
require "test/unit"
class TestCaseOptions < Test::Unit::TestCase
- def assert_raise_functional_operations (arg, *options)
+ def assert_raise_functional_operations(arg, *options)
assert_raise(ArgumentError) { arg.upcase(*options) }
assert_raise(ArgumentError) { arg.downcase(*options) }
assert_raise(ArgumentError) { arg.capitalize(*options) }
assert_raise(ArgumentError) { arg.swapcase(*options) }
end
- def assert_raise_bang_operations (arg, *options)
+ def assert_raise_bang_operations(arg, *options)
assert_raise(ArgumentError) { arg.upcase!(*options) }
assert_raise(ArgumentError) { arg.downcase!(*options) }
assert_raise(ArgumentError) { arg.capitalize!(*options) }
assert_raise(ArgumentError) { arg.swapcase!(*options) }
end
- def assert_raise_both_types (*options)
+ def assert_raise_both_types(*options)
assert_raise_functional_operations 'a', *options
assert_raise_bang_operations 'a', *options
assert_raise_functional_operations :a, *options
@@ -35,21 +35,21 @@ class TestCaseOptions < Test::Unit::TestCase
assert_raise_both_types :lithuanian, :ascii
end
- def assert_okay_functional_operations (arg, *options)
+ def assert_okay_functional_operations(arg, *options)
assert_nothing_raised { arg.upcase(*options) }
assert_nothing_raised { arg.downcase(*options) }
assert_nothing_raised { arg.capitalize(*options) }
assert_nothing_raised { arg.swapcase(*options) }
end
- def assert_okay_bang_operations (arg, *options)
+ def assert_okay_bang_operations(arg, *options)
assert_nothing_raised { arg.upcase!(*options) }
assert_nothing_raised { arg.downcase!(*options) }
assert_nothing_raised { arg.capitalize!(*options) }
assert_nothing_raised { arg.swapcase!(*options) }
end
- def assert_okay_both_types (*options)
+ def assert_okay_both_types(*options)
assert_okay_functional_operations 'a', *options
assert_okay_bang_operations 'a', *options
assert_okay_functional_operations :a, *options
diff --git a/test/ruby/enc/test_emoji_breaks.rb b/test/ruby/enc/test_emoji_breaks.rb
new file mode 100644
index 0000000000..37a29640d8
--- /dev/null
+++ b/test/ruby/enc/test_emoji_breaks.rb
@@ -0,0 +1,119 @@
+# frozen_string_literal: true
+# Copyright © 2018 Martin J. Dürst (duerst@it.aoyama.ac.jp)
+
+require "test/unit"
+
+class BreakTest
+ attr_reader :string, :comment, :filename, :line_number, :type, :shortname
+
+ def initialize (filename, line_number, data, comment='')
+ @filename = filename
+ @line_number = line_number
+ @comment = comment.gsub(/\s+/, ' ').strip
+ if filename=='emoji-test'
+ codes, @type = data.split(/\s*;\s*/)
+ @shortname = ''
+ else
+ codes, @type, @shortname = data.split(/\s*;\s*/)
+ end
+ @type = @type.gsub(/\s+/, ' ').strip
+ @shortname = @shortname.gsub(/\s+/, ' ').strip
+ @string = codes.split(/\s+/)
+ .map do |ch|
+ c = ch.to_i(16)
+ # eliminate cases with surrogates
+ # raise ArgumentError if 0xD800 <= c and c <= 0xDFFF
+ c.chr('UTF-8')
+ end.join
+ end
+end
+
+class TestEmojiBreaks < Test::Unit::TestCase
+ EMOJI_DATA_FILES = %w[emoji-sequences emoji-test emoji-variation-sequences emoji-zwj-sequences]
+ EMOJI_VERSION = RbConfig::CONFIG['UNICODE_EMOJI_VERSION']
+ EMOJI_DATA_PATH = File.expand_path("../../../enc/unicode/data/emoji/#{EMOJI_VERSION}", __dir__)
+
+ def self.expand_filename(basename)
+ File.expand_path("#{EMOJI_DATA_PATH}/#{basename}.txt", __dir__)
+ end
+
+ def self.data_files_available?
+ EMOJI_DATA_FILES.all? do |f|
+ File.exist?(expand_filename(f))
+ end
+ end
+
+ def test_data_files_available
+ unless TestEmojiBreaks.data_files_available?
+ skip "Emoji data files not available in #{EMOJI_DATA_PATH}."
+ end
+ end
+end
+
+TestEmojiBreaks.data_files_available? and class TestEmojiBreaks
+ def read_data
+ tests = []
+ EMOJI_DATA_FILES.each do |filename|
+ version_mismatch = true
+ file_tests = []
+ IO.foreach(TestEmojiBreaks.expand_filename(filename), encoding: Encoding::UTF_8) do |line|
+ line.chomp!
+ raise "File Name Mismatch" if $.==1 and not line=="# #{filename}.txt"
+ version_mismatch = false if line=="# Version: #{EMOJI_VERSION}"
+ next if /\A(#|\z)/.match? line
+ file_tests << BreakTest.new(filename, $., *line.split('#')) rescue 'whatever'
+ end
+ raise "File Version Mismatch" if version_mismatch
+ tests += file_tests
+ end
+ tests
+ end
+
+ def all_tests
+ @@tests ||= read_data
+ rescue Errno::ENOENT
+ @@tests ||= []
+ end
+
+ def test_single_emoji
+ all_tests.each do |test|
+ expected = [test.string]
+ actual = test.string.each_grapheme_cluster.to_a
+ assert_equal expected, actual,
+ "file: #{test.filename}, line #{test.line_number}, " +
+ "type: #{test.type}, shortname: #{test.shortname}, comment: #{test.comment}"
+ end
+ end
+
+ def test_embedded_emoji
+ all_tests.each do |test|
+ expected = ["\t", test.string, "\t"]
+ actual = "\t#{test.string}\t".each_grapheme_cluster.to_a
+ assert_equal expected, actual,
+ "file: #{test.filename}, line #{test.line_number}, " +
+ "type: #{test.type}, shortname: #{test.shortname}, comment: #{test.comment}"
+ end
+ end
+
+ # test some pseodorandom combinations of emoji
+ def test_mixed_emoji
+ srand 0
+ length = all_tests.length
+ step = 503 # use a prime number
+ all_tests.each do |test1|
+ start = rand step
+ start.step(by: step, to: length-1) do |t2|
+ test2 = all_tests[t2]
+ # exclude skin tones, because they glue to previous grapheme clusters
+ next if (0x1F3FB..0x1F3FF).include? test2.string.ord
+ expected = [test1.string, test2.string]
+ actual = (test1.string+test2.string).each_grapheme_cluster.to_a
+ assert_equal expected, actual,
+ "file1: #{test1.filename}, line1 #{test1.line_number}, " +
+ "file2: #{test2.filename}, line2 #{test2.line_number},\n" +
+ "type1: #{test1.type}, shortname1: #{test1.shortname}, comment1: #{test1.comment},\n" +
+ "type2: #{test2.type}, shortname2: #{test2.shortname}, comment2: #{test2.comment}"
+ end
+ end
+ end
+end
diff --git a/test/ruby/enc/test_grapheme_breaks.rb b/test/ruby/enc/test_grapheme_breaks.rb
new file mode 100644
index 0000000000..7f6c776113
--- /dev/null
+++ b/test/ruby/enc/test_grapheme_breaks.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+# Copyright © 2018 Martin J. Dürst (duerst@it.aoyama.ac.jp)
+
+require "test/unit"
+
+class BreakTest
+ attr_reader :clusters, :string, :comment, :line_number
+
+ def initialize (line_number, data, comment)
+ @line_number = line_number
+ @comment = comment
+ @clusters = data.sub(/\A\s*÷\s*/, '')
+ .sub(/\s*÷\s*\z/, '')
+ .split(/\s*÷\s*/)
+ .map do |cl|
+ cl.split(/\s*×\s*/)
+ .map do |ch|
+ c = ch.to_i(16)
+ # eliminate cases with surrogates
+ raise ArgumentError if 0xD800 <= c and c <= 0xDFFF
+ c.chr('UTF-8')
+ end.join
+ end
+ @string = @clusters.join
+ end
+end
+
+class TestGraphemeBreaksFromFile < Test::Unit::TestCase
+ UNICODE_VERSION = RbConfig::CONFIG['UNICODE_VERSION']
+ path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__)
+ UNICODE_DATA_PATH = File.directory?("#{path}/ucd/auxiliary") ? "#{path}/ucd/auxiliary" : path
+ GRAPHEME_BREAK_TEST_FILE = File.expand_path("#{UNICODE_DATA_PATH}/GraphemeBreakTest.txt", __dir__)
+
+ def self.file_available?
+ File.exist? GRAPHEME_BREAK_TEST_FILE
+ end
+
+ def test_data_files_available
+ unless TestGraphemeBreaksFromFile.file_available?
+ skip "Unicode data file GraphemeBreakTest not available in #{UNICODE_DATA_PATH}."
+ end
+ end
+end
+
+TestGraphemeBreaksFromFile.file_available? and class TestGraphemeBreaksFromFile
+ def read_data
+ tests = []
+ IO.foreach(GRAPHEME_BREAK_TEST_FILE, encoding: Encoding::UTF_8) do |line|
+ if $. == 1 and not line.start_with?("# GraphemeBreakTest-#{UNICODE_VERSION}.txt")
+ raise "File Version Mismatch"
+ end
+ next if /\A#/.match? line
+ tests << BreakTest.new($., *line.chomp.split('#')) rescue 'whatever'
+ end
+ tests
+ end
+
+ def all_tests
+ @@tests ||= read_data
+ rescue Errno::ENOENT
+ @@tests ||= []
+ end
+
+ def test_each_grapheme_cluster
+ all_tests.each do |test|
+ expected = test.clusters
+ actual = test.string.each_grapheme_cluster.to_a
+ assert_equal expected, actual,
+ "line #{test.line_number}, expected '#{expected}', " +
+ "but got '#{actual}', comment: #{test.comment}"
+ end
+ end
+
+ def test_backslash_X
+ all_tests.each do |test|
+ clusters = test.clusters.dup
+ string = test.string.dup
+ removals = 0
+ while string.sub!(/\A\X/, '')
+ removals += 1
+ clusters.shift
+ expected = clusters.join
+ assert_equal expected, string,
+ "line #{test.line_number}, removals: #{removals}, expected '#{expected}', " +
+ "but got '#{string}', comment: #{test.comment}"
+ end
+ assert_equal expected, string,
+ "line #{test.line_number}, after last removal, expected '#{expected}', " +
+ "but got '#{string}', comment: #{test.comment}"
+ end
+ end
+end
diff --git a/test/ruby/enc/test_regex_casefold.rb b/test/ruby/enc/test_regex_casefold.rb
index cc525c268e..2b252bd441 100644
--- a/test/ruby/enc/test_regex_casefold.rb
+++ b/test/ruby/enc/test_regex_casefold.rb
@@ -5,6 +5,8 @@ require "test/unit"
class TestCaseFold < Test::Unit::TestCase
UNICODE_VERSION = RbConfig::CONFIG['UNICODE_VERSION']
+ path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__)
+ UNICODE_DATA_PATH = File.directory?("#{path}/ucd") ? "#{path}/ucd" : path
CaseTest = Struct.new :source, :target, :kind, :line
def check_downcase_properties(expected, start, *flags)
@@ -17,7 +19,7 @@ class TestCaseFold < Test::Unit::TestCase
end
def read_tests
- IO.readlines(File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}/CaseFolding.txt", __dir__), encoding: Encoding::ASCII_8BIT)
+ IO.readlines("#{UNICODE_DATA_PATH}/CaseFolding.txt", encoding: Encoding::ASCII_8BIT)
.collect.with_index { |linedata, linenumber| [linenumber.to_i+1, linedata.chomp] }
.reject { |number, data| data =~ /^(#|$)/ }
.collect do |linenumber, linedata|
diff --git a/test/ruby/enc/test_utf16.rb b/test/ruby/enc/test_utf16.rb
index 99b48c2982..e08f2ea14e 100644
--- a/test/ruby/enc/test_utf16.rb
+++ b/test/ruby/enc/test_utf16.rb
@@ -56,59 +56,71 @@ EOT
# tests start
def test_utf16be_valid_encoding
- [
- "\x00\x00",
- "\xd7\xff",
- "\xd8\x00\xdc\x00",
- "\xdb\xff\xdf\xff",
- "\xe0\x00",
- "\xff\xff",
- ].each {|s|
- s.force_encoding("utf-16be")
- assert_equal(true, s.valid_encoding?, "#{encdump s}.valid_encoding?")
- }
- [
- "\x00",
- "\xd7",
- "\xd8\x00",
- "\xd8\x00\xd8\x00",
- "\xdc\x00",
- "\xdc\x00\xd8\x00",
- "\xdc\x00\xdc\x00",
- "\xe0",
- "\xff",
- ].each {|s|
- s.force_encoding("utf-16be")
- assert_equal(false, s.valid_encoding?, "#{encdump s}.valid_encoding?")
- }
+ all_assertions do |a|
+ [
+ "\x00\x00",
+ "\xd7\xff",
+ "\xd8\x00\xdc\x00",
+ "\xdb\xff\xdf\xff",
+ "\xe0\x00",
+ "\xff\xff",
+ ].each {|s|
+ s.force_encoding("utf-16be")
+ a.for(s) {
+ assert_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ [
+ "\x00",
+ "\xd7",
+ "\xd8\x00",
+ "\xd8\x00\xd8\x00",
+ "\xdc\x00",
+ "\xdc\x00\xd8\x00",
+ "\xdc\x00\xdc\x00",
+ "\xe0",
+ "\xff",
+ ].each {|s|
+ s.force_encoding("utf-16be")
+ a.for(s) {
+ assert_not_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ end
end
def test_utf16le_valid_encoding
- [
- "\x00\x00",
- "\xff\xd7",
- "\x00\xd8\x00\xdc",
- "\xff\xdb\xff\xdf",
- "\x00\xe0",
- "\xff\xff",
- ].each {|s|
- s.force_encoding("utf-16le")
- assert_equal(true, s.valid_encoding?, "#{encdump s}.valid_encoding?")
- }
- [
- "\x00",
- "\xd7",
- "\x00\xd8",
- "\x00\xd8\x00\xd8",
- "\x00\xdc",
- "\x00\xdc\x00\xd8",
- "\x00\xdc\x00\xdc",
- "\xe0",
- "\xff",
- ].each {|s|
- s.force_encoding("utf-16le")
- assert_equal(false, s.valid_encoding?, "#{encdump s}.valid_encoding?")
- }
+ all_assertions do |a|
+ [
+ "\x00\x00",
+ "\xff\xd7",
+ "\x00\xd8\x00\xdc",
+ "\xff\xdb\xff\xdf",
+ "\x00\xe0",
+ "\xff\xff",
+ ].each {|s|
+ s.force_encoding("utf-16le")
+ a.for(s) {
+ assert_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ [
+ "\x00",
+ "\xd7",
+ "\x00\xd8",
+ "\x00\xd8\x00\xd8",
+ "\x00\xdc",
+ "\x00\xdc\x00\xd8",
+ "\x00\xdc\x00\xdc",
+ "\xe0",
+ "\xff",
+ ].each {|s|
+ s.force_encoding("utf-16le")
+ a.for(s) {
+ assert_not_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ end
end
def test_strftime
diff --git a/test/ruby/enc/test_utf32.rb b/test/ruby/enc/test_utf32.rb
index 23e8aeb315..76379abca0 100644
--- a/test/ruby/enc/test_utf32.rb
+++ b/test/ruby/enc/test_utf32.rb
@@ -90,5 +90,73 @@ EOT
assert_equal(sl, "a".ord.chr("utf-32le"))
assert_equal(sb, "a".ord.chr("utf-32be"))
end
+
+ def test_utf32be_valid_encoding
+ all_assertions do |a|
+ [
+ "\x00\x00\x00\x00",
+ "\x00\x00\x00a",
+ "\x00\x00\x30\x40",
+ "\x00\x00\xd7\xff",
+ "\x00\x00\xe0\x00",
+ "\x00\x00\xff\xff",
+ "\x00\x10\xff\xff",
+ ].each {|s|
+ s.force_encoding("utf-32be")
+ a.for(s) {
+ assert_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ [
+ "a",
+ "\x00a",
+ "\x00\x00a",
+ "\x00\x00\xd8\x00",
+ "\x00\x00\xdb\xff",
+ "\x00\x00\xdc\x00",
+ "\x00\x00\xdf\xff",
+ "\x00\x11\x00\x00",
+ ].each {|s|
+ s.force_encoding("utf-32be")
+ a.for(s) {
+ assert_not_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ end
+ end
+
+ def test_utf32le_valid_encoding
+ all_assertions do |a|
+ [
+ "\x00\x00\x00\x00",
+ "a\x00\x00\x00",
+ "\x40\x30\x00\x00",
+ "\xff\xd7\x00\x00",
+ "\x00\xe0\x00\x00",
+ "\xff\xff\x00\x00",
+ "\xff\xff\x10\x00",
+ ].each {|s|
+ s.force_encoding("utf-32le")
+ a.for(s) {
+ assert_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ [
+ "a",
+ "a\x00",
+ "a\x00\x00",
+ "\x00\xd8\x00\x00",
+ "\xff\xdb\x00\x00",
+ "\x00\xdc\x00\x00",
+ "\xff\xdf\x00\x00",
+ "\x00\x00\x11\x00",
+ ].each {|s|
+ s.force_encoding("utf-32le")
+ a.for(s) {
+ assert_not_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ end
+ end
end
diff --git a/test/ruby/lbtest.rb b/test/ruby/lbtest.rb
index 208c8b26ec..c7822c9e9a 100644
--- a/test/ruby/lbtest.rb
+++ b/test/ruby/lbtest.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: false
-require 'thread'
class LocalBarrier
def initialize(n)
diff --git a/test/ruby/marshaltestlib.rb b/test/ruby/marshaltestlib.rb
index 358d3c5133..5c48a8d853 100644
--- a/test/ruby/marshaltestlib.rb
+++ b/test/ruby/marshaltestlib.rb
@@ -110,7 +110,9 @@ module MarshalTestLib
class MyException < Exception; def initialize(v, *args) super(*args); @v = v; end; attr_reader :v; end
def test_exception
marshal_equal(Exception.new('foo')) {|o| o.message}
- marshal_equal(assert_raise(NoMethodError) {no_such_method()}) {|o| o.message}
+ obj = Object.new
+ e = assert_raise(NoMethodError) {obj.no_such_method()}
+ marshal_equal(e) {|o| o.message}
end
def test_exception_subclass
diff --git a/test/ruby/test_alias.rb b/test/ruby/test_alias.rb
index 3fc1bb4000..e81636fa43 100644
--- a/test/ruby/test_alias.rb
+++ b/test/ruby/test_alias.rb
@@ -122,7 +122,8 @@ class TestAlias < Test::Unit::TestCase
end
def test_alias_wb_miss
- assert_normal_exit %q{
+ assert_normal_exit "#{<<-"begin;"}\n#{<<-'end;'}"
+ begin;
require 'stringio'
GC.verify_internal_consistency
GC.start
@@ -130,7 +131,7 @@ class TestAlias < Test::Unit::TestCase
alias_method :read_nonblock, :sysread
end
GC.verify_internal_consistency
- }
+ end;
end
def test_cyclic_zsuper
@@ -183,7 +184,8 @@ class TestAlias < Test::Unit::TestCase
def test_alias_in_module
bug9663 = '[ruby-core:61635] [Bug #9663]'
- assert_separately(['-', bug9663], <<-'end;')
+ assert_separately(['-', bug9663], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
bug = ARGV[0]
m = Module.new do
diff --git a/test/ruby/test_argf.rb b/test/ruby/test_argf.rb
index d4be87e954..a76bdccf45 100644
--- a/test/ruby/test_argf.rb
+++ b/test/ruby/test_argf.rb
@@ -35,8 +35,8 @@ class TestArgf < Test::Unit::TestCase
open("#{@tmpdir}/#{basename}-#{@tmp_count}", "w")
end
- def make_tempfile
- t = make_tempfile0("argf-qux")
+ def make_tempfile(basename = "argf-qux")
+ t = make_tempfile0(basename)
t.puts "foo"
t.puts "bar"
t.puts "baz"
@@ -57,7 +57,7 @@ class TestArgf < Test::Unit::TestCase
/cygwin|mswin|mingw|bccwin/ =~ RUBY_PLATFORM
end
- def assert_src_expected(line, src, args = nil)
+ def assert_src_expected(src, args = nil, line: caller_locations(1, 1)[0].lineno+1)
args ||= [@t1.path, @t2.path, @t3.path]
expected = src.split(/^/)
ruby('-e', src, *args) do |f|
@@ -71,79 +71,108 @@ class TestArgf < Test::Unit::TestCase
end
def test_argf
- assert_src_expected(__LINE__+1, <<-'SRC')
+ assert_src_expected("#{<<~"{#"}\n#{<<~'};'}")
+ {#
a = ARGF
b = a.dup
p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["1", 1, "1", 1]
p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["2", 2, "2", 2]
a.rewind
b.rewind
- p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["1", 1, "1", 3]
- p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["2", 2, "2", 4]
- p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["3", 3, "3", 5]
- p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["4", 4, "4", 6]
- p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["5", 5, "5", 7]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["1", 1, "1", 1]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["2", 2, "2", 2]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["3", 3, "3", 3]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["4", 4, "4", 4]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["5", 5, "5", 5]
a.rewind
b.rewind
- p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["5", 5, "5", 8]
- p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["6", 6, "6", 9]
- SRC
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["5", 5, "5", 5]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["6", 6, "6", 6]
+ };
end
def test_lineno
- assert_src_expected(__LINE__+1, <<-'SRC')
+ assert_src_expected("#{<<~"{#"}\n#{<<~'};'}")
+ {#
a = ARGF
- a.gets; p $. #=> 1
- a.gets; p $. #=> 2
- a.gets; p $. #=> 3
- a.rewind; p $. #=> 3
- a.gets; p $. #=> 3
- a.gets; p $. #=> 4
- a.rewind; p $. #=> 4
- a.gets; p $. #=> 3
- a.lineno = 1000; p $. #=> 1000
- a.gets; p $. #=> 1001
- a.gets; p $. #=> 1002
+ a.gets; p($.) #=> 1
+ a.gets; p($.) #=> 2
+ a.gets; p($.) #=> 3
+ a.rewind; p($.) #=> 3
+ a.gets; p($.) #=> 3
+ a.gets; p($.) #=> 4
+ a.rewind; p($.) #=> 4
+ a.gets; p($.) #=> 3
+ a.lineno = 1000; p($.) #=> 1000
+ a.gets; p($.) #=> 1001
+ a.gets; p($.) #=> 1002
$. = 2000
- a.gets; p $. #=> 2001
- a.gets; p $. #=> 2001
- SRC
+ a.gets; p($.) #=> 2001
+ a.gets; p($.) #=> 2001
+ };
end
def test_lineno2
- assert_src_expected(__LINE__+1, <<-'SRC')
+ assert_src_expected("#{<<~"{#"}\n#{<<~'};'}")
+ {#
a = ARGF.dup
- a.gets; p $. #=> 1
- a.gets; p $. #=> 2
- a.gets; p $. #=> 1
- a.rewind; p $. #=> 1
- a.gets; p $. #=> 1
- a.gets; p $. #=> 2
- a.gets; p $. #=> 1
- a.lineno = 1000; p $. #=> 1
- a.gets; p $. #=> 2
- a.gets; p $. #=> 2
+ a.gets; p($.) #=> 1
+ a.gets; p($.) #=> 2
+ a.gets; p($.) #=> 1
+ a.rewind; p($.) #=> 1
+ a.gets; p($.) #=> 1
+ a.gets; p($.) #=> 2
+ a.gets; p($.) #=> 1
+ a.lineno = 1000; p($.) #=> 1
+ a.gets; p($.) #=> 2
+ a.gets; p($.) #=> 2
$. = 2000
- a.gets; p $. #=> 2000
- a.gets; p $. #=> 2000
- SRC
+ a.gets; p($.) #=> 2000
+ a.gets; p($.) #=> 2000
+ };
end
def test_lineno3
- assert_in_out_err(["-", @t1.path, @t2.path], <<-INPUT, %w"1 1 1 2 2 2 3 3 1 4 4 2", [], "[ruby-core:25205]")
+ expected = %w"1 1 1 2 2 2 3 3 1 4 4 2"
+ assert_in_out_err(["-", @t1.path, @t2.path],
+ "#{<<~"{#"}\n#{<<~'};'}", expected, [], "[ruby-core:25205]")
+ {#
ARGF.each do |line|
puts [$., ARGF.lineno, ARGF.file.lineno]
end
- INPUT
+ };
+ end
+
+ def test_new_lineno_each
+ f = ARGF.class.new(@t1.path, @t2.path, @t3.path)
+ result = []
+ f.each {|line| result << [f.lineno, line]; break if result.size == 3}
+ assert_equal(3, f.lineno)
+ assert_equal((1..3).map {|i| [i, "#{i}\n"]}, result)
+
+ f.rewind
+ assert_equal(2, f.lineno)
+ ensure
+ f.close
+ end
+
+ def test_new_lineno_each_char
+ f = ARGF.class.new(@t1.path, @t2.path, @t3.path)
+ f.each_char.to_a
+ assert_equal(0, f.lineno)
+ ensure
+ f.close
end
def test_inplace
- assert_in_out_err(["-", @t1.path, @t2.path, @t3.path], <<-INPUT, [], [])
+ assert_in_out_err(["-", @t1.path, @t2.path, @t3.path],
+ "#{<<~"{#"}\n#{<<~'};'}")
+ {#
ARGF.inplace_mode = '.bak'
while line = ARGF.gets
puts line.chomp + '.new'
end
- INPUT
+ };
assert_equal("1.new\n2.new\n", File.read(@t1.path))
assert_equal("3.new\n4.new\n", File.read(@t2.path))
assert_equal("5.new\n6.new\n", File.read(@t3.path))
@@ -153,7 +182,9 @@ class TestArgf < Test::Unit::TestCase
end
def test_inplace2
- assert_in_out_err(["-", @t1.path, @t2.path, @t3.path], <<-INPUT, [], [])
+ assert_in_out_err(["-", @t1.path, @t2.path, @t3.path],
+ "#{<<~"{#"}\n#{<<~'};'}")
+ {#
ARGF.inplace_mode = '.bak'
puts ARGF.gets.chomp + '.new'
puts ARGF.gets.chomp + '.new'
@@ -167,7 +198,7 @@ class TestArgf < Test::Unit::TestCase
p ARGF.inplace_mode
ARGF.inplace_mode = nil
puts ARGF.gets.chomp + '.new'
- INPUT
+ };
assert_equal("1.new\n2.new\n\".bak\"\n3.new\n4.new\nnil\n", File.read(@t1.path))
assert_equal("3\n4\n", File.read(@t2.path))
assert_equal("5.new\n\".bak\"\n6.new\n", File.read(@t3.path))
@@ -177,7 +208,9 @@ class TestArgf < Test::Unit::TestCase
end
def test_inplace3
- assert_in_out_err(["-i.bak", "-", @t1.path, @t2.path, @t3.path], <<-INPUT, [], [])
+ assert_in_out_err(["-i.bak", "-", @t1.path, @t2.path, @t3.path],
+ "#{<<~"{#"}\n#{<<~'};'}")
+ {#
puts ARGF.gets.chomp + '.new'
puts ARGF.gets.chomp + '.new'
p $-i
@@ -190,7 +223,7 @@ class TestArgf < Test::Unit::TestCase
p $-i
$-i = nil
puts ARGF.gets.chomp + '.new'
- INPUT
+ };
assert_equal("1.new\n2.new\n\".bak\"\n3.new\n4.new\nnil\n", File.read(@t1.path))
assert_equal("3\n4\n", File.read(@t2.path))
assert_equal("5.new\n\".bak\"\n6.new\n", File.read(@t3.path))
@@ -202,12 +235,13 @@ class TestArgf < Test::Unit::TestCase
def test_inplace_rename_impossible
t = make_tempfile
- assert_in_out_err(["-", t.path], <<-INPUT) do |r, e|
- ARGF.inplace_mode = '/\\\\:'
- while line = ARGF.gets
- puts line.chomp + '.new'
- end
- INPUT
+ assert_in_out_err(["-", t.path], "#{<<~"{#"}\n#{<<~'};'}") do |r, e|
+ {#
+ ARGF.inplace_mode = '/\\\\:'
+ while line = ARGF.gets
+ puts line.chomp + '.new'
+ end
+ };
assert_match(/Can't rename .* to .*: .*. skipping file/, e.first) #'
assert_equal([], r)
assert_equal("foo\nbar\nbaz\n", File.read(t.path))
@@ -221,15 +255,36 @@ class TestArgf < Test::Unit::TestCase
assert_warning(/#{base}/) {argf.gets}
end
+ def test_inplace_nonascii
+ ext = Encoding.default_external or
+ skip "no default external encoding"
+ t = nil
+ ["\u{3042}", "\u{e9}"].any? do |n|
+ t = make_tempfile(n.encode(ext))
+ rescue Encoding::UndefinedConversionError
+ end
+ t or skip "no name to test"
+ assert_in_out_err(["-i.bak", "-", t.path],
+ "#{<<~"{#"}\n#{<<~'};'}")
+ {#
+ puts ARGF.gets.chomp + '.new'
+ puts ARGF.gets.chomp + '.new'
+ puts ARGF.gets.chomp + '.new'
+ };
+ assert_equal("foo.new\n""bar.new\n""baz.new\n", File.read(t.path))
+ assert_equal("foo\n""bar\n""baz\n", File.read(t.path + ".bak"))
+ end
+
def test_inplace_no_backup
t = make_tempfile
- assert_in_out_err(["-", t.path], <<-INPUT) do |r, e|
- ARGF.inplace_mode = ''
- while line = ARGF.gets
- puts line.chomp + '.new'
- end
- INPUT
+ assert_in_out_err(["-", t.path], "#{<<~"{#"}\n#{<<~'};'}") do |r, e|
+ {#
+ ARGF.inplace_mode = ''
+ while line = ARGF.gets
+ puts line.chomp + '.new'
+ end
+ };
if no_safe_rename
assert_match(/Can't do inplace edit without backup/, e.join) #'
else
@@ -243,63 +298,125 @@ class TestArgf < Test::Unit::TestCase
def test_inplace_dup
t = make_tempfile
- assert_in_out_err(["-", t.path], <<-INPUT, [], [])
+ assert_in_out_err(["-", t.path], "#{<<~"{#"}\n#{<<~'};'}", [], [])
+ {#
ARGF.inplace_mode = '.bak'
f = ARGF.dup
while line = f.gets
puts line.chomp + '.new'
end
- INPUT
+ };
assert_equal("foo.new\nbar.new\nbaz.new\n", File.read(t.path))
end
def test_inplace_stdin
- assert_in_out_err(["-", "-"], <<-INPUT, [], /Can't do inplace edit for stdio; skipping/)
+ assert_in_out_err(["-", "-"], "#{<<~"{#"}\n#{<<~'};'}", [], /Can't do inplace edit for stdio; skipping/)
+ {#
ARGF.inplace_mode = '.bak'
f = ARGF.dup
while line = f.gets
puts line.chomp + '.new'
end
- INPUT
+ };
end
def test_inplace_stdin2
- assert_in_out_err(["-"], <<-INPUT, [], /Can't do inplace edit for stdio/)
+ assert_in_out_err(["-"], "#{<<~"{#"}\n#{<<~'};'}", [], /Can't do inplace edit for stdio/)
+ {#
ARGF.inplace_mode = '.bak'
while line = ARGF.gets
puts line.chomp + '.new'
end
- INPUT
+ };
+ end
+
+ def test_inplace_invalid_backup
+ assert_raise(ArgumentError, '[ruby-dev:50272] [Bug #13960]') {
+ ARGF.inplace_mode = "a\0"
+ }
+ end
+
+ def test_inplace_to_path
+ base = "argf-test"
+ name = "#{@tmpdir}/#{base}"
+ File.write(name, "foo")
+ stdout = $stdout
+ argf = ARGF.class.new(Struct.new(:to_path).new(name))
+ begin
+ result = argf.gets
+ ensure
+ $stdout = stdout
+ argf.close
+ end
+ assert_equal("foo", result)
+ end
+
+ def test_inplace_ascii_incompatible_path
+ base = "argf-\u{30c6 30b9 30c8}"
+ name = "#{@tmpdir}/#{base}"
+ File.write(name, "foo")
+ stdout = $stdout
+ argf = ARGF.class.new(name.encode(Encoding::UTF_16LE))
+ assert_raise(Encoding::CompatibilityError) do
+ argf.gets
+ end
+ ensure
+ $stdout = stdout
+ end
+
+ def test_inplace_suffix_encoding
+ base = "argf-\u{30c6 30b9 30c8}"
+ name = "#{@tmpdir}/#{base}"
+ suffix = "-bak"
+ File.write(name, "foo")
+ stdout = $stdout
+ argf = ARGF.class.new(name)
+ argf.inplace_mode = suffix.encode(Encoding::UTF_16LE)
+ begin
+ argf.each do |s|
+ puts "+"+s
+ end
+ ensure
+ $stdout.close unless $stdout == stdout
+ $stdout = stdout
+ end
+ assert_file.exist?(name)
+ assert_equal("+foo\n", File.read(name))
+ assert_file.not_exist?(name+"-")
+ assert_file.exist?(name+suffix)
+ assert_equal("foo", File.read(name+suffix))
end
def test_encoding
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- p ARGF.external_encoding.is_a?(Encoding)
- p ARGF.internal_encoding.is_a?(Encoding)
- ARGF.gets
- p ARGF.external_encoding.is_a?(Encoding)
- p ARGF.internal_encoding
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ p ARGF.external_encoding.is_a?(Encoding)
+ p ARGF.internal_encoding.is_a?(Encoding)
+ ARGF.gets
+ p ARGF.external_encoding.is_a?(Encoding)
+ p ARGF.internal_encoding
+ };
assert_equal("true\ntrue\ntrue\nnil\n", f.read)
end
end
def test_tell
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- begin
- ARGF.binmode
- loop do
- p ARGF.tell
- p ARGF.gets
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ begin
+ ARGF.binmode
+ loop do
+ p ARGF.tell
+ p ARGF.gets
+ end
+ rescue ArgumentError
+ puts "end"
end
- rescue ArgumentError
- puts "end"
- end
- SRC
+ };
a = f.read.split("\n")
[0, 2, 4, 2, 4, 2, 4].map {|i| i.to_s }.
- zip((1..6).map {|i| '"' + i.to_s + '\n"' } + ["nil"]).flatten.
- each do |x|
+ zip((1..6).map {|i| '"' + i.to_s + '\n"' } + ["nil"]).flatten.
+ each do |x|
assert_equal(x, a.shift)
end
assert_equal('end', a.shift)
@@ -307,7 +424,8 @@ class TestArgf < Test::Unit::TestCase
end
def test_seek
- assert_src_expected(__LINE__+1, <<-'SRC')
+ assert_src_expected("#{<<~"{#"}\n#{<<~'};'}")
+ {#
ARGF.seek(4)
p ARGF.gets #=> "3\n"
ARGF.seek(0, IO::SEEK_END)
@@ -319,11 +437,12 @@ class TestArgf < Test::Unit::TestCase
rescue
puts "end" #=> end
end
- SRC
+ };
end
def test_set_pos
- assert_src_expected(__LINE__+1, <<-'SRC')
+ assert_src_expected("#{<<~"{#"}\n#{<<~'};'}")
+ {#
ARGF.pos = 4
p ARGF.gets #=> "3\n"
ARGF.pos = 4
@@ -335,11 +454,12 @@ class TestArgf < Test::Unit::TestCase
rescue
puts "end" #=> end
end
- SRC
+ };
end
def test_rewind
- assert_src_expected(__LINE__+1, <<-'SRC')
+ assert_src_expected("#{<<~"{#"}\n#{<<~'};'}")
+ {#
ARGF.pos = 4
ARGF.rewind
p ARGF.gets #=> "1\n"
@@ -354,28 +474,29 @@ class TestArgf < Test::Unit::TestCase
rescue
puts "end" #=> end
end
- SRC
+ };
end
def test_fileno
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- p ARGF.fileno
- ARGF.gets
- ARGF.gets
- p ARGF.fileno
- ARGF.gets
- ARGF.gets
- p ARGF.fileno
- ARGF.gets
- ARGF.gets
- p ARGF.fileno
- ARGF.gets
- begin
- ARGF.fileno
- rescue
- puts "end"
- end
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ p ARGF.fileno
+ ARGF.gets
+ ARGF.gets
+ p ARGF.fileno
+ ARGF.gets
+ ARGF.gets
+ p ARGF.fileno
+ ARGF.gets
+ ARGF.gets
+ p ARGF.fileno
+ ARGF.gets
+ begin
+ ARGF.fileno
+ rescue
+ puts "end"
+ end
+ };
a = f.read.split("\n")
fd1, fd2, fd3, fd4, tag = a
assert_match(/^\d+$/, fd1)
@@ -387,12 +508,13 @@ class TestArgf < Test::Unit::TestCase
end
def test_to_io
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- 8.times do
- p ARGF.to_io
- ARGF.gets
- end
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ 8.times do
+ p ARGF.to_io
+ ARGF.gets
+ end
+ };
a = f.read.split("\n")
f11, f12, f13, f21, f22, f31, f32, f4 = a
assert_equal(f11, f12)
@@ -406,16 +528,17 @@ class TestArgf < Test::Unit::TestCase
end
def test_eof
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- begin
- 8.times do
- p ARGF.eof?
- ARGF.gets
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ begin
+ 8.times do
+ p ARGF.eof?
+ ARGF.gets
+ end
+ rescue IOError
+ puts "end"
end
- rescue IOError
- puts "end"
- end
- SRC
+ };
a = f.read.split("\n")
(%w(false) + (%w(false true) * 3) + %w(end)).each do |x|
assert_equal(x, a.shift)
@@ -442,66 +565,71 @@ class TestArgf < Test::Unit::TestCase
end
def test_read2
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = ""
- ARGF.read(8, s)
- p s
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = ""
+ ARGF.read(8, s)
+ p s
+ };
assert_equal("\"1\\n2\\n3\\n4\\n\"\n", f.read)
end
end
def test_read2_with_not_empty_buffer
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = "0123456789"
- ARGF.read(8, s)
- p s
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = "0123456789"
+ ARGF.read(8, s)
+ p s
+ };
assert_equal("\"1\\n2\\n3\\n4\\n\"\n", f.read)
end
end
def test_read3
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- nil while ARGF.gets
- p ARGF.read
- p ARGF.read(0, "")
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ nil while ARGF.gets
+ p ARGF.read
+ p ARGF.read(0, "")
+ };
assert_equal("nil\n\"\"\n", f.read)
end
end
def test_readpartial
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = ""
- begin
- loop do
- s << ARGF.readpartial(1)
- t = ""; ARGF.readpartial(1, t); s << t
- # not empty buffer
- u = "abcdef"; ARGF.readpartial(1, u); s << u
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = ""
+ begin
+ loop do
+ s << ARGF.readpartial(1)
+ t = ""; ARGF.readpartial(1, t); s << t
+ # not empty buffer
+ u = "abcdef"; ARGF.readpartial(1, u); s << u
+ end
+ rescue EOFError
+ puts s
end
- rescue EOFError
- puts s
- end
- SRC
+ };
assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
end
end
def test_readpartial2
- ruby('-e', <<-SRC) do |f|
- s = ""
- begin
- loop do
- s << ARGF.readpartial(1)
- t = ""; ARGF.readpartial(1, t); s << t
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}") do |f|
+ {#
+ s = ""
+ begin
+ loop do
+ s << ARGF.readpartial(1)
+ t = ""; ARGF.readpartial(1, t); s << t
+ end
+ rescue EOFError
+ $stdout.binmode
+ puts s
end
- rescue EOFError
- $stdout.binmode
- puts s
- end
- SRC
+ };
f.binmode
f.puts("foo")
f.puts("bar")
@@ -512,76 +640,82 @@ class TestArgf < Test::Unit::TestCase
end
def test_readpartial_eof_twice
- ruby('-W1', '-e', <<-SRC, @t1.path) do |f|
- $stderr = $stdout
- print ARGF.readpartial(256)
- ARGF.readpartial(256) rescue p($!.class)
- ARGF.readpartial(256) rescue p($!.class)
- SRC
+ ruby('-W1', '-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path) do |f|
+ {#
+ $stderr = $stdout
+ print ARGF.readpartial(256)
+ ARGF.readpartial(256) rescue p($!.class)
+ ARGF.readpartial(256) rescue p($!.class)
+ };
assert_equal("1\n2\nEOFError\nEOFError\n", f.read)
end
end
def test_getc
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = ""
- while c = ARGF.getc
- s << c
- end
- puts s
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = ""
+ while c = ARGF.getc
+ s << c
+ end
+ puts s
+ };
assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
end
end
def test_getbyte
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = []
- while c = ARGF.getbyte
- s << c
- end
- p s
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = []
+ while c = ARGF.getbyte
+ s << c
+ end
+ p s
+ };
assert_equal("[49, 10, 50, 10, 51, 10, 52, 10, 53, 10, 54, 10]\n", f.read)
end
end
def test_readchar
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = ""
- begin
- while c = ARGF.readchar
- s << c
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = ""
+ begin
+ while c = ARGF.readchar
+ s << c
+ end
+ rescue EOFError
+ puts s
end
- rescue EOFError
- puts s
- end
- SRC
+ };
assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
end
end
def test_readbyte
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- begin
- s = []
- while c = ARGF.readbyte
- s << c
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ begin
+ s = []
+ while c = ARGF.readbyte
+ s << c
+ end
+ rescue EOFError
+ p s
end
- rescue EOFError
- p s
- end
- SRC
+ };
assert_equal("[49, 10, 50, 10, 51, 10, 52, 10, 53, 10, 54, 10]\n", f.read)
end
end
def test_each_line
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = []
- ARGF.each_line {|l| s << l }
- p s
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = []
+ ARGF.each_line {|l| s << l }
+ p s
+ };
assert_equal("[\"1\\n\", \"2\\n\", \"3\\n\", \"4\\n\", \"5\\n\", \"6\\n\"]\n", f.read)
end
end
@@ -592,32 +726,35 @@ class TestArgf < Test::Unit::TestCase
end
def test_each_byte
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = []
- ARGF.each_byte {|c| s << c }
- p s
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = []
+ ARGF.each_byte {|c| s << c }
+ p s
+ };
assert_equal("[49, 10, 50, 10, 51, 10, 52, 10, 53, 10, 54, 10]\n", f.read)
end
end
def test_each_char
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = ""
- ARGF.each_char {|c| s << c }
- puts s
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = ""
+ ARGF.each_char {|c| s << c }
+ puts s
+ };
assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
end
end
def test_filename
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- begin
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ begin
+ puts ARGF.filename.dump
+ end while ARGF.gets
puts ARGF.filename.dump
- end while ARGF.gets
- puts ARGF.filename.dump
- SRC
+ };
a = f.read.split("\n")
assert_equal(@t1.path.dump, a.shift)
assert_equal(@t1.path.dump, a.shift)
@@ -631,12 +768,13 @@ class TestArgf < Test::Unit::TestCase
end
def test_filename2
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- begin
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ begin
+ puts $FILENAME.dump
+ end while ARGF.gets
puts $FILENAME.dump
- end while ARGF.gets
- puts $FILENAME.dump
- SRC
+ };
a = f.read.split("\n")
assert_equal(@t1.path.dump, a.shift)
assert_equal(@t1.path.dump, a.shift)
@@ -650,12 +788,13 @@ class TestArgf < Test::Unit::TestCase
end
def test_file
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- begin
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ begin
+ puts ARGF.file.path.dump
+ end while ARGF.gets
puts ARGF.file.path.dump
- end while ARGF.gets
- puts ARGF.file.path.dump
- SRC
+ };
a = f.read.split("\n")
assert_equal(@t1.path.dump, a.shift)
assert_equal(@t1.path.dump, a.shift)
@@ -687,33 +826,37 @@ class TestArgf < Test::Unit::TestCase
end unless IO::BINARY.zero?
def test_skip
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- ARGF.skip
- puts ARGF.gets
- ARGF.skip
- puts ARGF.read
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ ARGF.skip
+ puts ARGF.gets
+ ARGF.skip
+ puts ARGF.read
+ };
assert_equal("1\n3\n4\n5\n6\n", f.read)
end
end
def test_skip_in_each_line
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- ARGF.each_line {|l| print l; ARGF.skip}
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ ARGF.each_line {|l| print l; ARGF.skip}
+ };
assert_equal("1\n3\n5\n", f.read, '[ruby-list:49185]')
end
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- ARGF.each_line {|l| ARGF.skip; puts [l, ARGF.gets].map {|s| s ? s.chomp : s.inspect}.join("+")}
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ ARGF.each_line {|l| ARGF.skip; puts [l, ARGF.gets].map {|s| s ? s.chomp : s.inspect}.join("+")}
+ };
assert_equal("1+3\n4+5\n6+nil\n", f.read, '[ruby-list:49185]')
end
end
def test_skip_in_each_byte
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- ARGF.each_byte {|l| print l; ARGF.skip}
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ ARGF.each_byte {|l| print l; ARGF.skip}
+ };
assert_equal("135".unpack("C*").join(""), f.read, '[ruby-list:49185]')
end
end
@@ -722,9 +865,10 @@ class TestArgf < Test::Unit::TestCase
[[@t1, "\u{3042}"], [@t2, "\u{3044}"], [@t3, "\u{3046}"]].each do |f, s|
File.write(f.path, s, mode: "w:utf-8")
end
- ruby('-Eutf-8', '-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- ARGF.each_char {|l| print l; ARGF.skip}
- SRC
+ ruby('-Eutf-8', '-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ ARGF.each_char {|l| print l; ARGF.skip}
+ };
assert_equal("\u{3042 3044 3046}", f.read, '[ruby-list:49185]')
end
end
@@ -733,43 +877,48 @@ class TestArgf < Test::Unit::TestCase
[[@t1, "\u{3042}"], [@t2, "\u{3044}"], [@t3, "\u{3046}"]].each do |f, s|
File.write(f.path, s, mode: "w:utf-8")
end
- ruby('-Eutf-8', '-Eutf-8', '-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- ARGF.each_codepoint {|l| printf "%x:", l; ARGF.skip}
- SRC
+ ruby('-Eutf-8', '-Eutf-8', '-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ ARGF.each_codepoint {|l| printf "%x:", l; ARGF.skip}
+ };
assert_equal("3042:3044:3046:", f.read, '[ruby-list:49185]')
end
end
def test_close
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- ARGF.close
- puts ARGF.read
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ ARGF.close
+ puts ARGF.read
+ };
assert_equal("3\n4\n5\n6\n", f.read)
end
end
def test_close_replace
- ruby('-e', <<-SRC) do |f|
- ARGF.close
- ARGV.replace ['#{@t1.path}', '#{@t2.path}', '#{@t3.path}']
- puts ARGF.read
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}") do |f|
+ paths = ['#{@t1.path}', '#{@t2.path}', '#{@t3.path}']
+ {#
+ ARGF.close
+ ARGV.replace paths
+ puts ARGF.read
+ };
assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
end
end
def test_closed
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- 3.times do
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ 3.times do
+ p ARGF.closed?
+ ARGF.gets
+ ARGF.gets
+ end
p ARGF.closed?
ARGF.gets
- ARGF.gets
- end
- p ARGF.closed?
- ARGF.gets
- p ARGF.closed?
- SRC
+ p ARGF.closed?
+ };
assert_equal("false\nfalse\nfalse\nfalse\ntrue\n", f.read)
end
end
@@ -828,81 +977,86 @@ class TestArgf < Test::Unit::TestCase
end
def test_lines
- ruby('-W1', '-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- $stderr = $stdout
- s = []
- ARGF.lines {|l| s << l }
- p s
- SRC
+ ruby('-W1', '-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ $stderr = $stdout
+ s = []
+ ARGF.lines {|l| s << l }
+ p s
+ };
assert_match(/deprecated/, f.gets)
assert_equal("[\"1\\n\", \"2\\n\", \"3\\n\", \"4\\n\", \"5\\n\", \"6\\n\"]\n", f.read)
end
end
def test_bytes
- ruby('-W1', '-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- $stderr = $stdout
- print Marshal.dump(ARGF.bytes.to_a)
- SRC
+ ruby('-W1', '-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ $stderr = $stdout
+ print Marshal.dump(ARGF.bytes.to_a)
+ };
assert_match(/deprecated/, f.gets)
assert_equal([49, 10, 50, 10, 51, 10, 52, 10, 53, 10, 54, 10], Marshal.load(f.read))
end
end
def test_chars
- ruby('-W1', '-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- $stderr = $stdout
- print [Marshal.dump(ARGF.chars.to_a)].pack('m')
- SRC
- assert_match(/deprecated/, f.gets)
- assert_equal(["1", "\n", "2", "\n", "3", "\n", "4", "\n", "5", "\n", "6", "\n"], Marshal.load(f.read.unpack('m').first))
+ ruby('-W1', '-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ $stderr = $stdout
+ print [Marshal.dump(ARGF.chars.to_a)].pack('m')
+ };
+ assert_match(/deprecated/, f.gets)
+ assert_equal(["1", "\n", "2", "\n", "3", "\n", "4", "\n", "5", "\n", "6", "\n"], Marshal.load(f.read.unpack('m').first))
end
end
def test_codepoints
- ruby('-W1', '-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- $stderr = $stdout
- print Marshal.dump(ARGF.codepoints.to_a)
- SRC
+ ruby('-W1', '-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ $stderr = $stdout
+ print Marshal.dump(ARGF.codepoints.to_a)
+ };
assert_match(/deprecated/, f.gets)
assert_equal([49, 10, 50, 10, 51, 10, 52, 10, 53, 10, 54, 10], Marshal.load(f.read))
end
end
def test_read_nonblock
- ruby('-e', <<-SRC) do |f|
- $stdout.sync = true
- :wait_readable == ARGF.read_nonblock(1, "", exception: false) or
- abort "did not return :wait_readable"
-
- begin
- ARGF.read_nonblock(1)
- abort 'fail to raise IO::WaitReadable'
- rescue IO::WaitReadable
- end
- puts 'starting select'
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}") do |f|
+ {#
+ $stdout.sync = true
+ :wait_readable == ARGF.read_nonblock(1, "", exception: false) or
+ abort "did not return :wait_readable"
+
+ begin
+ ARGF.read_nonblock(1)
+ abort 'fail to raise IO::WaitReadable'
+ rescue IO::WaitReadable
+ end
+ puts 'starting select'
- IO.select([ARGF]) == [[ARGF], [], []] or
- abort 'did not awaken for readability (before byte)'
+ IO.select([ARGF]) == [[ARGF], [], []] or
+ abort 'did not awaken for readability (before byte)'
- buf = ''
- buf.object_id == ARGF.read_nonblock(1, buf).object_id or
- abort "read destination buffer failed"
- print buf
+ buf = ''
+ buf.object_id == ARGF.read_nonblock(1, buf).object_id or
+ abort "read destination buffer failed"
+ print buf
- IO.select([ARGF]) == [[ARGF], [], []] or
- abort 'did not awaken for readability (before EOF)'
+ IO.select([ARGF]) == [[ARGF], [], []] or
+ abort 'did not awaken for readability (before EOF)'
- ARGF.read_nonblock(1, buf, exception: false) == nil or
- abort "EOF should return nil if exception: false"
+ ARGF.read_nonblock(1, buf, exception: false) == nil or
+ abort "EOF should return nil if exception: false"
- begin
- ARGF.read_nonblock(1, buf)
- abort 'fail to raise IO::WaitReadable'
- rescue EOFError
- puts 'done with eof'
- end
- SRC
+ begin
+ ARGF.read_nonblock(1, buf)
+ abort 'fail to raise IO::WaitReadable'
+ rescue EOFError
+ puts 'done with eof'
+ end
+ };
f.sync = true
assert_equal "starting select\n", f.gets
f.write('.') # wake up from IO.select
@@ -913,10 +1067,11 @@ class TestArgf < Test::Unit::TestCase
end
def test_wrong_type
- assert_separately([], <<-'end;')
+ assert_separately([], "#{<<~"{#"}\n#{<<~'};'}")
+ {#
bug11610 = '[ruby-core:71140] [Bug #11610]'
ARGV[0] = nil
assert_raise(TypeError, bug11610) {gets}
- end;
+ };
end
end
diff --git a/test/ruby/test_arithmetic_sequence.rb b/test/ruby/test_arithmetic_sequence.rb
new file mode 100644
index 0000000000..143906f4e2
--- /dev/null
+++ b/test/ruby/test_arithmetic_sequence.rb
@@ -0,0 +1,464 @@
+# frozen_string_literal: false
+require 'test/unit'
+
+class TestArithmeticSequence < Test::Unit::TestCase
+ def test_new
+ assert_raise(NoMethodError) { Enumerator::ArithmeticSequence.new }
+ end
+
+ def test_allocate
+ assert_raise(TypeError) { Enumerator::ArithmeticSequence.allocate }
+ end
+
+ def test_begin
+ assert_equal(1, 1.step.begin)
+ assert_equal(1, 1.step(10).begin)
+ assert_equal(1, 1.step(to: 10).begin)
+ assert_equal(1, 1.step(nil).begin)
+ assert_equal(1, 1.step(to: nil).begin)
+ assert_equal(1, 1.step(by: 2).begin)
+ assert_equal(1, 1.step(by: -1).begin)
+ assert_equal(1, 1.step(by: nil).begin)
+ assert_equal(1, 1.step(10, 2).begin)
+ assert_equal(1, 1.step(10, by: 2).begin)
+ assert_equal(1, 1.step(to: 10, by: 2).begin)
+ assert_equal(10, 10.step(to: 1, by: -1).begin)
+ assert_equal(10, 10.step(to: 1, by: -2).begin)
+ assert_equal(10, 10.step(to: -1, by: -2).begin)
+ assert_equal(10.0, 10.0.step(to: -1.0, by: -2.0).begin)
+ end
+
+ def test_end
+ assert_equal(nil, 1.step.end)
+ assert_equal(10, 1.step(10).end)
+ assert_equal(10, 1.step(to: 10).end)
+ assert_equal(nil, 1.step(nil).end)
+ assert_equal(nil, 1.step(to: nil).end)
+ assert_equal(nil, 1.step(by: 2).end)
+ assert_equal(nil, 1.step(by: -1).end)
+ assert_equal(nil, 1.step(by: nil).end)
+ assert_equal(10, 1.step(10, 2).end)
+ assert_equal(10, 1.step(10, by: 2).end)
+ assert_equal(10, 1.step(to: 10, by: 2).end)
+ assert_equal(1, 10.step(to: 1, by: -1).end)
+ assert_equal(1, 10.step(to: 1, by: -2).end)
+ assert_equal(-1, 10.step(to: -1, by: -2).end)
+ assert_equal(-1.0, 10.0.step(to: -1.0, by: -2.0).end)
+ end
+
+ def test_exclude_end_p
+ assert_equal(false, 1.step.exclude_end?)
+ assert_equal(false, 1.step(10).exclude_end?)
+ assert_equal(false, 1.step(to: 10).exclude_end?)
+ assert_equal(false, 1.step(nil).exclude_end?)
+ assert_equal(false, 1.step(to: nil).exclude_end?)
+ assert_equal(false, 1.step(by: 2).exclude_end?)
+ assert_equal(false, 1.step(by: -1).exclude_end?)
+ assert_equal(false, 1.step(by: nil).exclude_end?)
+ assert_equal(false, 1.step(10, 2).exclude_end?)
+ assert_equal(false, 1.step(10, by: 2).exclude_end?)
+ assert_equal(false, 1.step(to: 10, by: 2).exclude_end?)
+ assert_equal(false, 10.step(to: 1, by: -1).exclude_end?)
+ assert_equal(false, 10.step(to: 1, by: -2).exclude_end?)
+ assert_equal(false, 10.step(to: -1, by: -2).exclude_end?)
+ end
+
+ def test_step
+ assert_equal(1, 1.step.step)
+ assert_equal(1, 1.step(10).step)
+ assert_equal(1, 1.step(to: 10).step)
+ assert_equal(1, 1.step(nil).step)
+ assert_equal(1, 1.step(to: nil).step)
+ assert_equal(2, 1.step(by: 2).step)
+ assert_equal(-1, 1.step(by: -1).step)
+ assert_equal(1, 1.step(by: nil).step)
+ assert_equal(2, 1.step(10, 2).step)
+ assert_equal(2, 1.step(10, by: 2).step)
+ assert_equal(2, 1.step(to: 10, by: 2).step)
+ assert_equal(-1, 10.step(to: 1, by: -1).step)
+ assert_equal(-2, 10.step(to: 1, by: -2).step)
+ assert_equal(-2, 10.step(to: -1, by: -2).step)
+ assert_equal(-2.0, 10.0.step(to: -1.0, by: -2.0).step)
+ end
+
+ def test_eq
+ seq = 1.step
+ assert_equal(seq, seq)
+ assert_equal(seq, 1.step)
+ assert_equal(seq, 1.step(nil))
+ end
+
+ def test_eqq
+ seq = 1.step
+ assert_operator(seq, :===, seq)
+ assert_operator(seq, :===, 1.step)
+ assert_operator(seq, :===, 1.step(nil))
+ end
+
+ def test_eql_p
+ seq = 1.step
+ assert_operator(seq, :eql?, seq)
+ assert_operator(seq, :eql?, 1.step)
+ assert_operator(seq, :eql?, 1.step(nil))
+ end
+
+ def test_hash
+ seq = 1.step
+ assert_equal(seq.hash, seq.hash)
+ assert_equal(seq.hash, 1.step.hash)
+ assert_equal(seq.hash, 1.step(nil).hash)
+ assert_kind_of(String, seq.hash.to_s)
+ end
+
+ def test_first
+ seq = 1.step
+ assert_equal(1, seq.first)
+ assert_equal([1], seq.first(1))
+ assert_equal([1, 2, 3], seq.first(3))
+
+ seq = 1.step(by: 2)
+ assert_equal(1, seq.first)
+ assert_equal([1], seq.first(1))
+ assert_equal([1, 3, 5], seq.first(3))
+
+ seq = 10.step(by: -2)
+ assert_equal(10, seq.first)
+ assert_equal([10], seq.first(1))
+ assert_equal([10, 8, 6], seq.first(3))
+
+ seq = 1.step(by: 4)
+ assert_equal([1, 5, 9], seq.first(3))
+
+ seq = 1.step(10, by: 4)
+ assert_equal([1, 5, 9], seq.first(5))
+
+ seq = 1.step(0)
+ assert_equal(nil, seq.first)
+ assert_equal([], seq.first(1))
+ assert_equal([], seq.first(3))
+
+ seq = 1.step(10, by: -1)
+ assert_equal(nil, seq.first)
+ assert_equal([], seq.first(1))
+ assert_equal([], seq.first(3))
+
+ seq = 1.step(10, by: 0)
+ assert_equal(1, seq.first)
+ assert_equal([1], seq.first(1))
+ assert_equal([1, 1, 1], seq.first(3))
+
+ seq = 10.0.step(-1.0, by: -2.0)
+ assert_equal(10.0, seq.first)
+ assert_equal([10.0], seq.first(1))
+ assert_equal([10.0, 8.0, 6.0], seq.first(3))
+ end
+
+ def test_first_bug15518
+ bug15518 = '[Bug #15518]'
+ seq = (1 .. 10.0).step(1)
+ five_float_classes = Array.new(5) { Float }
+ assert_equal(five_float_classes, seq.first(5).map(&:class), bug15518)
+ assert_equal([1.0, 2.0, 3.0, 4.0, 5.0], seq.first(5), bug15518)
+ seq = (1 .. Float::INFINITY).step(1)
+ assert_equal(five_float_classes, seq.first(5).map(&:class), bug15518)
+ assert_equal([1.0, 2.0, 3.0, 4.0, 5.0], seq.first(5), bug15518)
+ seq = (1 .. Float::INFINITY).step(1r)
+ assert_equal(five_float_classes, seq.first(5).map(&:class), bug15518)
+ assert_equal([1.0, 2.0, 3.0, 4.0, 5.0], seq.first(5), bug15518)
+ end
+
+ def test_last
+ seq = 1.step(10)
+ assert_equal(10, seq.last)
+ assert_equal([10], seq.last(1))
+ assert_equal([8, 9, 10], seq.last(3))
+
+ seq = 1.step(10, 2)
+ assert_equal(9, seq.last)
+ assert_equal([9], seq.last(1))
+ assert_equal([5, 7, 9], seq.last(3))
+
+ seq = 10.step(1, -2)
+ assert_equal(2, seq.last)
+ assert_equal([2], seq.last(1))
+ assert_equal([6, 4, 2], seq.last(3))
+
+ seq = 10.step(-1, -2)
+ assert_equal(0, seq.last)
+
+ seq = 1.step(10, 4)
+ assert_equal([1, 5, 9], seq.last(5))
+
+ seq = 10.step(1)
+ assert_equal(nil, seq.last)
+ assert_equal([], seq.last(1))
+ assert_equal([], seq.last(5))
+
+ seq = 1.step(10, -1)
+ assert_equal(nil, seq.last)
+ assert_equal([], seq.last(1))
+ assert_equal([], seq.last(5))
+
+ seq = (1..10).step
+ assert_equal(10, seq.last)
+ assert_equal([10], seq.last(1))
+ assert_equal([8, 9, 10], seq.last(3))
+
+ seq = (1...10).step
+ assert_equal(9, seq.last)
+ assert_equal([9], seq.last(1))
+ assert_equal([7, 8, 9], seq.last(3))
+
+ seq = 10.0.step(-3.0, by: -2.0)
+ assert_equal(-2.0, seq.last)
+ assert_equal([-2.0], seq.last(1))
+ assert_equal([2.0, 0.0, -2.0], seq.last(3))
+ end
+
+ def test_last_with_float
+ res = (1..3).step(2).last(2.0)
+ assert_equal([1, 3], res)
+ assert_instance_of Integer, res[0]
+ assert_instance_of Integer, res[1]
+
+ res = (1..3).step(2).last(5.0)
+ assert_equal([1, 3], res)
+ assert_instance_of Integer, res[0]
+ assert_instance_of Integer, res[1]
+ end
+
+ def test_last_with_rational
+ res = (1..3).step(2).last(2r)
+ assert_equal([1, 3], res)
+ assert_instance_of Integer, res[0]
+ assert_instance_of Integer, res[1]
+
+ res = (1..3).step(2).last(10/2r)
+ assert_equal([1, 3], res)
+ assert_instance_of Integer, res[0]
+ assert_instance_of Integer, res[1]
+ end
+
+ def test_to_a
+ assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 1.step(10).to_a)
+ assert_equal([1, 3, 5, 7, 9], 1.step(10, 2).to_a)
+ assert_equal([1, 3, 5, 7, 9], (1..10).step(2).to_a)
+ assert_equal([10, 8, 6, 4, 2], 10.step(1, by: -2).to_a)
+ assert_equal([10, 8, 6, 4, 2], (10..1).step(-2).to_a)
+ assert_equal([10.0, 8.0, 6.0, 4.0, 2.0], (10.0..1.0).step(-2.0).to_a)
+ end
+
+ def test_to_a_bug15444
+ seq = ((1/10r)..(1/2r)).step(1/10r)
+ assert_num_equal_type([1/10r, 1/5r, 3/10r, 2/5r, 1/2r], seq.to_a,
+ '[ruby-core:90648] [Bug #15444]')
+ end
+
+ def test_last_bug17218
+ seq = (1.0997r .. 1.1r).step(0.0001r)
+ assert_equal([1.0997r, 1.0998r, 1.0999r, 1.1r], seq.to_a, '[ruby-core:100312] [Bug #17218]')
+ end
+
+ def test_slice
+ seq = 1.step(10, 2)
+ assert_equal([[1, 3, 5], [7, 9]], seq.each_slice(3).to_a)
+
+ seq = 10.step(1, -2)
+ assert_equal([[10, 8, 6], [4, 2]], seq.each_slice(3).to_a)
+ end
+
+ def test_cons
+ seq = 1.step(10, 2)
+ assert_equal([[1, 3, 5], [3, 5, 7], [5, 7, 9]], seq.each_cons(3).to_a)
+
+ seq = 10.step(1, -2)
+ assert_equal([[10, 8, 6], [8, 6, 4], [6, 4, 2]], seq.each_cons(3).to_a)
+ end
+
+ def test_with_index
+ seq = 1.step(6, 2)
+ assert_equal([[1, 0], [3, 1], [5, 2]], seq.with_index.to_a)
+ assert_equal([[1, 10], [3, 11], [5, 12]], seq.with_index(10).to_a)
+
+ seq = 10.step(5, -2)
+ assert_equal([[10, 0], [8, 1], [6, 2]], seq.with_index.to_a)
+ assert_equal([[10, 10], [8, 11], [6, 12]], seq.with_index(10).to_a)
+ end
+
+ def test_with_object
+ obj = [0, 1]
+ seq = 1.step(10, 2)
+ ret = seq.each_with_object(obj) do |i, memo|
+ memo[0] += i
+ memo[1] *= i
+ end
+ assert_same(obj, ret)
+ assert_equal([25, 945], ret)
+
+ obj = [0, 1]
+ seq = 10.step(1, -2)
+ ret = seq.each_with_object(obj) do |i, memo|
+ memo[0] += i
+ memo[1] *= i
+ end
+ assert_same(obj, ret)
+ assert_equal([30, 3840], ret)
+ end
+
+ def test_next
+ seq = 1.step(10, 2)
+ [1, 3, 5, 7, 9].each do |i|
+ assert_equal(i, seq.next)
+ end
+
+ seq = 10.step(1, -2)
+ [10, 8, 6, 4, 2].each do |i|
+ assert_equal(i, seq.next)
+ end
+
+ seq = ((1/10r)..(1/2r)).step(0)
+ assert_equal(1/10r, seq.next)
+ end
+
+ def test_next_bug15444
+ seq = ((1/10r)..(1/2r)).step(1/10r)
+ assert_equal(1/10r, seq.next, '[ruby-core:90648] [Bug #15444]')
+ end
+
+ def test_next_rewind
+ seq = 1.step(6, 2)
+ assert_equal(1, seq.next)
+ assert_equal(3, seq.next)
+ seq.rewind
+ assert_equal(1, seq.next)
+ assert_equal(3, seq.next)
+ assert_equal(5, seq.next)
+ assert_raise(StopIteration) { seq.next }
+
+ seq = 10.step(5, -2)
+ assert_equal(10, seq.next)
+ assert_equal(8, seq.next)
+ seq.rewind
+ assert_equal(10, seq.next)
+ assert_equal(8, seq.next)
+ assert_equal(6, seq.next)
+ assert_raise(StopIteration) { seq.next }
+ end
+
+ def test_next_after_stopiteration
+ seq = 1.step(2, 2)
+ assert_equal(1, seq.next)
+ assert_raise(StopIteration) { seq.next }
+ assert_raise(StopIteration) { seq.next }
+ seq.rewind
+ assert_equal(1, seq.next)
+ assert_raise(StopIteration) { seq.next }
+ assert_raise(StopIteration) { seq.next }
+ end
+
+ def test_stop_result
+ seq = 1.step(2, 2)
+ res = seq.each {}
+ assert_equal(1, seq.next)
+ exc = assert_raise(StopIteration) { seq.next }
+ assert_equal(res, exc.result)
+ end
+
+ def test_peek
+ seq = 1.step(2, 2)
+ assert_equal(1, seq.peek)
+ assert_equal(1, seq.peek)
+ assert_equal(1, seq.next)
+ assert_raise(StopIteration) { seq.peek }
+ assert_raise(StopIteration) { seq.peek }
+
+ seq = 10.step(9, -2)
+ assert_equal(10, seq.peek)
+ assert_equal(10, seq.peek)
+ assert_equal(10, seq.next)
+ assert_raise(StopIteration) { seq.peek }
+ assert_raise(StopIteration) { seq.peek }
+ end
+
+ def test_next_values
+ seq = 1.step(2, 2)
+ assert_equal([1], seq.next_values)
+ end
+
+ def test_peek_values
+ seq = 1.step(2, 2)
+ assert_equal([1], seq.peek_values)
+ end
+
+ def test_num_step_inspect
+ assert_equal('(1.step)', 1.step.inspect)
+ assert_equal('(1.step(10))', 1.step(10).inspect)
+ assert_equal('(1.step(10, 2))', 1.step(10, 2).inspect)
+ assert_equal('(1.step(10, by: 2))', 1.step(10, by: 2).inspect)
+ assert_equal('(1.step(by: 2))', 1.step(by: 2).inspect)
+ end
+
+ def test_range_step_inspect
+ assert_equal('((1..).step)', (1..).step.inspect)
+ assert_equal('((1..10).step)', (1..10).step.inspect)
+ assert_equal('((1..10).step(2))', (1..10).step(2).inspect)
+ end
+
+ def test_num_step_size
+ assert_equal(10, 1.step(10).size)
+ assert_equal(5, 1.step(10, 2).size)
+ assert_equal(4, 1.step(10, 3).size)
+ assert_equal(1, 1.step(10, 10).size)
+ assert_equal(0, 1.step(0).size)
+ assert_equal(Float::INFINITY, 1.step.size)
+
+ assert_equal(10, 10.step(1, -1).size)
+ assert_equal(5, 10.step(1, -2).size)
+ assert_equal(4, 10.step(1, -3).size)
+ assert_equal(1, 10.step(1, -10).size)
+ assert_equal(0, 1.step(2, -1).size)
+ assert_equal(Float::INFINITY, 1.step(by: -1).size)
+ end
+
+ def test_range_step_size
+ assert_equal(10, (1..10).step.size)
+ assert_equal(9, (1...10).step.size)
+ assert_equal(5, (1..10).step(2).size)
+ assert_equal(5, (1...10).step(2).size)
+ assert_equal(4, (1...9).step(2).size)
+ assert_equal(Float::INFINITY, (1..).step.size)
+
+ assert_equal(10, (10..1).step(-1).size)
+ assert_equal(9, (10...1).step(-1).size)
+ assert_equal(5, (10..1).step(-2).size)
+ assert_equal(5, (10...1).step(-2).size)
+ assert_equal(4, (10...2).step(-2).size)
+ assert_equal(Float::INFINITY, (1..).step(-1).size)
+ end
+
+ def assert_num_equal_type(ary1, ary2, message=nil)
+ assert_equal(ary1.length, ary2.length, message)
+ ary1.zip(ary2) do |e1, e2|
+ assert_equal(e1.class, e2.class, message)
+ if e1.is_a? Complex
+ assert_equal(e1.real, e2.real, message)
+ assert_equal(e1.imag, e2.imag, message)
+ else
+ assert_equal(e1, e2, message)
+ end
+ end
+ end
+
+ def test_complex
+ assert_num_equal_type([1, 1+1i, 1+2i], (1..).step(1i).take(3))
+ assert_num_equal_type([1, 1+1.0i, 1+2.0i], (1..).step(1.0i).take(3))
+ assert_num_equal_type([0.0, 0.0+1.0i, 0.0+2.0i], (0.0..).step(1.0i).take(3))
+ assert_num_equal_type([0.0+0.0i, 0.0+1.0i, 0.0+2.0i], (0.0i..).step(1.0i).take(3))
+ end
+
+ def test_sum
+ assert_equal([1, 3, 5, 7, 9].sum, (1..10).step(2).sum)
+ assert_equal([1.0, 2.5, 4.0, 5.5, 7.0, 8.5, 10.0].sum, (1.0..10.0).step(1.5).sum)
+ assert_equal([1/2r, 1r, 3/2r, 2, 5/2r, 3, 7/2r, 4].sum, ((1/2r)...(9/2r)).step(1/2r).sum)
+ end
+end
diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb
index bdb603bec5..8d48b86ea9 100644
--- a/test/ruby/test_array.rb
+++ b/test/ruby/test_array.rb
@@ -41,6 +41,7 @@ class TestArray < Test::Unit::TestCase
assert_equal(2, x[2])
assert_equal([1, 2, 3], x[1..3])
assert_equal([1, 2, 3], x[1,3])
+ assert_equal([3, 4, 5], x[3..])
x[0, 2] = 10
assert_equal([10, 2, 3, 4, 5], x)
@@ -145,14 +146,17 @@ class TestArray < Test::Unit::TestCase
assert_equal(1, x.first)
assert_equal([1], x.first(1))
assert_equal([1, 2, 3], x.first(3))
+ assert_raise_with_message(ArgumentError, /0\.\.1/) {x.first(1, 2)}
assert_equal(5, x.last)
assert_equal([5], x.last(1))
assert_equal([3, 4, 5], x.last(3))
+ assert_raise_with_message(ArgumentError, /0\.\.1/) {x.last(1, 2)}
assert_equal(1, x.shift)
assert_equal([2, 3, 4], x.shift(3))
assert_equal([5], x)
+ assert_raise_with_message(ArgumentError, /0\.\.1/) {x.first(1, 2)}
assert_equal([2, 3, 4, 5], x.unshift(2, 3, 4))
assert_equal([1, 2, 3, 4, 5], x.unshift(1))
@@ -161,6 +165,7 @@ class TestArray < Test::Unit::TestCase
assert_equal(5, x.pop)
assert_equal([3, 4], x.pop(2))
assert_equal([1, 2], x)
+ assert_raise_with_message(ArgumentError, /0\.\.1/) {x.pop(1, 2)}
assert_equal([1, 2, 3, 4], x.push(3, 4))
assert_equal([1, 2, 3, 4, 5], x.push(5))
@@ -170,6 +175,7 @@ class TestArray < Test::Unit::TestCase
def test_find_all_0
assert_respond_to([], :find_all)
assert_respond_to([], :select) # Alias
+ assert_respond_to([], :filter) # Alias
assert_equal([], [].find_all{ |obj| obj == "foo"})
x = ["foo", "bar", "baz", "baz", 1, 2, 3, 3, 4]
@@ -198,6 +204,8 @@ class TestArray < Test::Unit::TestCase
assert_equal([0, 1, 2, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(3...4){|i| i+10})
assert_equal([0, 1, 12, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(2..-2){|i| i+10})
assert_equal([0, 1, 12, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(2...-2){|i| i+10})
+ assert_equal([0, 1, 2, 13, 14, 15], [0, 1, 2, 3, 4, 5].fill(3..){|i| i+10})
+ assert_equal([0, 1, 2, 13, 14, 15], [0, 1, 2, 3, 4, 5].fill(3...){|i| i+10})
end
# From rubicon
@@ -224,6 +232,13 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[], @cls[ 1, 2, 3 ] & @cls[ 4, 5, 6 ])
end
+ def test_AND_big_array # '&'
+ assert_equal(@cls[1, 3], @cls[ 1, 1, 3, 5 ]*64 & @cls[ 1, 2, 3 ]*64)
+ assert_equal(@cls[], @cls[ 1, 1, 3, 5 ]*64 & @cls[ ])
+ assert_equal(@cls[], @cls[ ] & @cls[ 1, 2, 3 ]*64)
+ assert_equal(@cls[], @cls[ 1, 2, 3 ]*64 & @cls[ 4, 5, 6 ]*64)
+ end
+
def test_MUL # '*'
assert_equal(@cls[], @cls[]*3)
assert_equal(@cls[1, 1, 1], @cls[1]*3)
@@ -247,19 +262,45 @@ class TestArray < Test::Unit::TestCase
def test_MINUS # '-'
assert_equal(@cls[], @cls[1] - @cls[1])
assert_equal(@cls[1], @cls[1, 2, 3, 4, 5] - @cls[2, 3, 4, 5])
- # Ruby 1.8 feature change
- #assert_equal(@cls[1], @cls[1, 2, 1, 3, 1, 4, 1, 5] - @cls[2, 3, 4, 5])
assert_equal(@cls[1, 1, 1, 1], @cls[1, 2, 1, 3, 1, 4, 1, 5] - @cls[2, 3, 4, 5])
a = @cls[]
1000.times { a << 1 }
assert_equal(1000, a.length)
- #assert_equal(@cls[1], a - @cls[2])
assert_equal(@cls[1] * 1000, a - @cls[2])
- #assert_equal(@cls[1], @cls[1, 2, 1] - @cls[2])
assert_equal(@cls[1, 1], @cls[1, 2, 1] - @cls[2])
assert_equal(@cls[1, 2, 3], @cls[1, 2, 3] - @cls[4, 5, 6])
end
+ def test_MINUS_big_array # '-'
+ assert_equal(@cls[1]*64, @cls[1, 2, 3, 4, 5]*64 - @cls[2, 3, 4, 5]*64)
+ assert_equal(@cls[1, 1, 1, 1]*64, @cls[1, 2, 1, 3, 1, 4, 1, 5]*64 - @cls[2, 3, 4, 5]*64)
+ a = @cls[]
+ 1000.times { a << 1 }
+ assert_equal(1000, a.length)
+ assert_equal(@cls[1] * 1000, a - @cls[2])
+ end
+
+ def test_difference
+ assert_equal(@cls[], @cls[1].difference(@cls[1]))
+ assert_equal(@cls[1], @cls[1, 2, 3, 4, 5].difference(@cls[2, 3, 4, 5]))
+ assert_equal(@cls[1, 1], @cls[1, 2, 1].difference(@cls[2]))
+ assert_equal(@cls[1, 1, 1, 1], @cls[1, 2, 1, 3, 1, 4, 1, 5].difference(@cls[2, 3, 4, 5]))
+ assert_equal(@cls[], @cls[1, 2, 3, 4].difference(@cls[1], @cls[2], @cls[3], @cls[4]))
+ a = [1]
+ assert_equal(@cls[1], a.difference(@cls[2], @cls[2]))
+ assert_equal(@cls[], a.difference(@cls[1]))
+ assert_equal(@cls[1], a)
+ end
+
+ def test_difference_big_array
+ assert_equal(@cls[1]*64, (@cls[1, 2, 3, 4, 5] * 64).difference(@cls[2, 3, 4] * 64, @cls[3, 5] * 64))
+ assert_equal(@cls[1, 1, 1, 1]*64, (@cls[1, 2, 1, 3, 1, 4, 1, 5] * 64).difference(@cls[2, 3, 4, 5] * 64))
+ a = @cls[1] * 1000
+ assert_equal(@cls[1] * 1000, a.difference(@cls[2], @cls[2]))
+ assert_equal(@cls[], a.difference(@cls[1]))
+ assert_equal(@cls[1] * 1000, a)
+ end
+
def test_LSHIFT # '<<'
a = @cls[]
a << 1
@@ -333,12 +374,11 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[99], a[-2..-2])
assert_equal(@cls[10, 11, 12], a[9..11])
+ assert_equal(@cls[98, 99, 100], a[97..])
assert_equal(@cls[10, 11, 12], a[-91..-89])
+ assert_equal(@cls[98, 99, 100], a[-3..])
assert_nil(a[10, -3])
- # Ruby 1.8 feature change:
- # Array#[size..x] returns [] instead of nil.
- #assert_nil(a[10..7])
assert_equal [], a[10..7]
assert_raise(TypeError) {a['cat']}
@@ -394,33 +434,33 @@ class TestArray < Test::Unit::TestCase
assert_equal(b, a[10..19] = b)
assert_equal(@cls[*(0..9).to_a] + b + @cls[*(20..99).to_a], a)
- # Ruby 1.8 feature change:
- # assigning nil does not remove elements.
-=begin
a = @cls[*(0..99).to_a]
assert_equal(nil, a[0,1] = nil)
- assert_equal(@cls[*(1..99).to_a], a)
+ assert_equal(@cls[nil] + @cls[*(1..99).to_a], a)
a = @cls[*(0..99).to_a]
assert_equal(nil, a[10,10] = nil)
- assert_equal(@cls[*(0..9).to_a] + @cls[*(20..99).to_a], a)
+ assert_equal(@cls[*(0..9).to_a] + @cls[nil] + @cls[*(20..99).to_a], a)
a = @cls[*(0..99).to_a]
assert_equal(nil, a[-1, 1] = nil)
- assert_equal(@cls[*(0..98).to_a], a)
+ assert_equal(@cls[*(0..98).to_a] + @cls[nil], a)
a = @cls[*(0..99).to_a]
assert_equal(nil, a[-10, 10] = nil)
- assert_equal(@cls[*(0..89).to_a], a)
+ assert_equal(@cls[*(0..89).to_a] + @cls[nil], a)
a = @cls[*(0..99).to_a]
assert_equal(nil, a[0,1000] = nil)
- assert_equal(@cls[] , a)
+ assert_equal(@cls[nil] , a)
a = @cls[*(0..99).to_a]
assert_equal(nil, a[10..19] = nil)
- assert_equal(@cls[*(0..9).to_a] + @cls[*(20..99).to_a], a)
-=end
+ assert_equal(@cls[*(0..9).to_a] + @cls[nil] + @cls[*(20..99).to_a], a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(nil, a[10..] = nil)
+ assert_equal(@cls[*(0..9).to_a] + @cls[nil], a)
a = @cls[1, 2, 3]
a[1, 0] = a
@@ -443,6 +483,17 @@ class TestArray < Test::Unit::TestCase
assert_equal([1, 2], a)
end
+ def test_append
+ a = @cls[1, 2, 3]
+ assert_equal(@cls[1, 2, 3, 4, 5], a.append(4, 5))
+ assert_equal(@cls[1, 2, 3, 4, 5, nil], a.append(nil))
+
+ a.append
+ assert_equal @cls[1, 2, 3, 4, 5, nil], a
+ a.append 6, 7
+ assert_equal @cls[1, 2, 3, 4, 5, nil, 6, 7], a
+ end
+
def test_assoc
a1 = @cls[*%w( cat feline )]
a2 = @cls[*%w( dog canine )]
@@ -500,10 +551,11 @@ class TestArray < Test::Unit::TestCase
assert_equal([], @cls[].collect { 99 })
- # Ruby 1.9 feature change:
- # Enumerable#collect without block returns an Enumerator.
- #assert_equal([1, 2, 3], @cls[1, 2, 3].collect)
assert_kind_of Enumerator, @cls[1, 2, 3].collect
+
+ assert_raise(ArgumentError) {
+ assert_equal([[1, 2, 3]], [[1, 2, 3]].collect(&->(a, b, c) {[a, b, c]}))
+ }
end
# also update map!
@@ -571,7 +623,7 @@ class TestArray < Test::Unit::TestCase
assert_equal([4, 5, 4, 5, 4, 5], b)
assert_raise(TypeError) { [0].concat(:foo) }
- assert_raise(RuntimeError) { [0].freeze.concat(:foo) }
+ assert_raise(FrozenError) { [0].freeze.concat(:foo) }
end
def test_count
@@ -702,7 +754,7 @@ class TestArray < Test::Unit::TestCase
a = @cls[]
i = 0
a.each { |e|
- assert_equal(a[i], e)
+ assert(false, "Never get here")
i += 1
}
assert_equal(0, i)
@@ -722,7 +774,7 @@ class TestArray < Test::Unit::TestCase
a = @cls[]
i = 0
a.each_index { |ind|
- assert_equal(i, ind)
+ assert(false, "Never get here")
i += 1
}
assert_equal(0, i)
@@ -1074,6 +1126,7 @@ class TestArray < Test::Unit::TestCase
assert_equal(Encoding::US_ASCII, [1, [u]].join.encoding)
assert_equal(Encoding::UTF_8, [u, [e]].join.encoding)
assert_equal(Encoding::UTF_8, [u, [1]].join.encoding)
+ assert_equal(Encoding::UTF_8, [Struct.new(:to_str).new(u)].join.encoding)
bug5379 = '[ruby-core:39776]'
assert_equal(Encoding::US_ASCII, [[], u, nil].join.encoding, bug5379)
assert_equal(Encoding::UTF_8, [[], "\u3042", nil].join.encoding, bug5379)
@@ -1204,13 +1257,18 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[], a)
end
+ def test_prepend
+ a = @cls[]
+ assert_equal(@cls['cat'], a.prepend('cat'))
+ assert_equal(@cls['dog', 'cat'], a.prepend('dog'))
+ assert_equal(@cls[nil, 'dog', 'cat'], a.prepend(nil))
+ assert_equal(@cls[@cls[1,2], nil, 'dog', 'cat'], a.prepend(@cls[1, 2]))
+ end
+
def test_push
a = @cls[1, 2, 3]
assert_equal(@cls[1, 2, 3, 4, 5], a.push(4, 5))
assert_equal(@cls[1, 2, 3, 4, 5, nil], a.push(nil))
- # Ruby 1.8 feature:
- # Array#push accepts any number of arguments.
- #assert_raise(ArgumentError, "a.push()") { a.push() }
a.push
assert_equal @cls[1, 2, 3, 4, 5, nil], a
a.push 6, 7
@@ -1250,6 +1308,65 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[7, 8, 9, 10], a, bug2545)
end
+ def test_shared_array_reject!
+ c = []
+ b = [1, 2, 3, 4]
+ 3.times do
+ a = b.dup
+ c << a.dup
+
+ begin
+ a.reject! do |x|
+ case x
+ when 2 then true
+ when 3 then raise StandardError, 'Oops'
+ else false
+ end
+ end
+ rescue StandardError
+ end
+
+ c << a.dup
+ end
+
+ bug90781 = '[ruby-core:90781]'
+ assert_equal [[1, 2, 3, 4],
+ [1, 3, 4],
+ [1, 2, 3, 4],
+ [1, 3, 4],
+ [1, 2, 3, 4],
+ [1, 3, 4]], c, bug90781
+ end
+
+ def test_iseq_shared_array_reject!
+ c = []
+ 3.times do
+ a = [1, 2, 3, 4]
+ c << a.dup
+
+ begin
+ a.reject! do |x|
+ case x
+ when 2 then true
+ when 3 then raise StandardError, 'Oops'
+ else false
+ end
+ end
+ rescue StandardError
+ end
+
+ c << a.dup
+ end
+
+ bug90781 = '[ruby-core:90781]'
+ assert_equal [[1, 2, 3, 4],
+ [1, 3, 4],
+ [1, 2, 3, 4],
+ [1, 3, 4],
+ [1, 2, 3, 4],
+ [1, 3, 4]], c, bug90781
+ end
+
def test_replace
a = @cls[ 1, 2, 3]
a_id = a.__id__
@@ -1260,10 +1377,10 @@ class TestArray < Test::Unit::TestCase
fa = a.dup.freeze
assert_nothing_raised(RuntimeError) { a.replace(a) }
- assert_raise(RuntimeError) { fa.replace(fa) }
+ assert_raise(FrozenError) { fa.replace(fa) }
assert_raise(ArgumentError) { fa.replace() }
assert_raise(TypeError) { a.replace(42) }
- assert_raise(RuntimeError) { fa.replace(42) }
+ assert_raise(FrozenError) { fa.replace(42) }
end
def test_reverse
@@ -1277,9 +1394,6 @@ class TestArray < Test::Unit::TestCase
a = @cls[*%w( dog cat bee ant )]
assert_equal(@cls[*%w(ant bee cat dog)], a.reverse!)
assert_equal(@cls[*%w(ant bee cat dog)], a)
- # Ruby 1.8 feature change:
- # Array#reverse always returns self.
- #assert_nil(@cls[].reverse!)
assert_equal @cls[], @cls[].reverse!
end
@@ -1359,14 +1473,14 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[99], a.slice(-2..-2))
assert_equal(@cls[10, 11, 12], a.slice(9..11))
+ assert_equal(@cls[98, 99, 100], a.slice(97..))
+ assert_equal(@cls[10, 11, 12], a.slice(-91..-89))
assert_equal(@cls[10, 11, 12], a.slice(-91..-89))
assert_nil(a.slice(-101..-1))
+ assert_nil(a.slice(-101..))
assert_nil(a.slice(10, -3))
- # Ruby 1.8 feature change:
- # Array#slice[size..x] always returns [].
- #assert_nil(a.slice(10..7))
assert_equal @cls[], a.slice(10..7)
end
@@ -1407,6 +1521,8 @@ class TestArray < Test::Unit::TestCase
assert_equal(nil, a.slice!(-6,2))
assert_equal(@cls[1, 2, 3, 4, 5], a)
+ assert_equal("[2, 3]", [1,2,3].slice!(1,10000).inspect, "moved from btest/knownbug")
+
assert_raise(ArgumentError) { @cls[1].slice! }
assert_raise(ArgumentError) { @cls[1].slice!(0, 0, 0) }
end
@@ -1491,7 +1607,7 @@ class TestArray < Test::Unit::TestCase
o2 = o1.clone
ary << o1 << o2
orig = ary.dup
- assert_raise(RuntimeError, "frozen during comparison") {ary.sort!}
+ assert_raise(FrozenError, "frozen during comparison") {ary.sort!}
assert_equal(orig, ary, "must not be modified once frozen")
end
@@ -1532,7 +1648,7 @@ class TestArray < Test::Unit::TestCase
def o.to_ary
foo_bar()
end
- assert_match(/foo_bar/, assert_raise(NoMethodError) {a.concat(o)}.message)
+ assert_raise_with_message(NoMethodError, /foo_bar/) {a.concat(o)}
end
def test_to_s
@@ -1555,15 +1671,17 @@ class TestArray < Test::Unit::TestCase
$, = nil
end
+ StubToH = [
+ [:key, :value],
+ Object.new.tap do |kvp|
+ def kvp.to_ary
+ [:obtained, :via_to_ary]
+ end
+ end,
+ ]
+
def test_to_h
- kvp = Object.new
- def kvp.to_ary
- [:obtained, :via_to_ary]
- end
- array = [
- [:key, :value],
- kvp,
- ]
+ array = StubToH
assert_equal({key: :value, obtained: :via_to_ary}, array.to_h)
e = assert_raise(TypeError) {
@@ -1578,6 +1696,27 @@ class TestArray < Test::Unit::TestCase
assert_equal "wrong array length at 2 (expected 2, was 1)", e.message
end
+ def test_to_h_block
+ array = StubToH
+ assert_equal({"key" => "value", "obtained" => "via_to_ary"},
+ array.to_h {|k, v| [k.to_s, v.to_s]})
+
+ assert_equal({first_one: :ok, not_ok: :ng},
+ [[:first_one, :ok], :not_ok].to_h {|k, v| [k, v || :ng]})
+
+ e = assert_raise(TypeError) {
+ [[:first_one, :ok], :not_ok].to_h {|k, v| v ? [k, v] : k}
+ }
+ assert_equal "wrong element type Symbol at 1 (expected array)", e.message
+ array = [1]
+ k = eval("class C\u{1f5ff}; self; end").new
+ assert_raise_with_message(TypeError, /C\u{1f5ff}/) {array.to_h {k}}
+ e = assert_raise(ArgumentError) {
+ [[:first_one, :ok], [1, 2], [:not_ok]].to_h {|kv| kv}
+ }
+ assert_equal "wrong array length at 2 (expected 2, was 1)", e.message
+ end
+
def test_min
assert_equal(1, [1, 2, 3, 1, 2].min)
assert_equal(3, [1, 2, 3, 1, 2].min {|a,b| b <=> a })
@@ -1592,6 +1731,12 @@ class TestArray < Test::Unit::TestCase
ary.min(2) {|a,b| a.length <=> b.length })
assert_equal([13, 14], [20, 32, 32, 21, 30, 25, 29, 13, 14].min(2))
assert_equal([2, 4, 6, 7], [2, 4, 8, 6, 7].min(4))
+
+ class << (obj = Object.new)
+ def <=>(x) 1 <=> x end
+ def coerce(x) [x, 1] end
+ end
+ assert_same(obj, [obj, 1.0].min)
end
def test_max
@@ -1607,6 +1752,12 @@ class TestArray < Test::Unit::TestCase
assert_equal(%w[albatross horse],
ary.max(2) {|a,b| a.length <=> b.length })
assert_equal([3, 2], [0, 0, 0, 0, 0, 0, 1, 3, 2].max(2))
+
+ class << (obj = Object.new)
+ def <=>(x) 1 <=> x end
+ def coerce(x) [x, 1] end
+ end
+ assert_same(obj, [obj, 1.0].max)
end
def test_uniq
@@ -1723,7 +1874,7 @@ class TestArray < Test::Unit::TestCase
f = a.dup.freeze
assert_raise(ArgumentError) { a.uniq!(1) }
assert_raise(ArgumentError) { f.uniq!(1) }
- assert_raise(RuntimeError) { f.uniq! }
+ assert_raise(FrozenError) { f.uniq! }
assert_nothing_raised do
a = [ {c: "b"}, {c: "r"}, {c: "w"}, {c: "g"}, {c: "g"} ]
@@ -1769,7 +1920,7 @@ class TestArray < Test::Unit::TestCase
def test_uniq_bang_with_freeze
ary = [1,2]
orig = ary.dup
- assert_raise(RuntimeError, "frozen during comparison") {
+ assert_raise(FrozenError, "frozen during comparison") {
ary.uniq! {|v| ary.freeze; 1}
}
assert_equal(orig, ary, "must not be modified once frozen")
@@ -1783,6 +1934,17 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[@cls[1,2], nil, 'dog', 'cat'], a.unshift(@cls[1, 2]))
end
+ def test_unshift_frozen
+ bug15952 = '[Bug #15952]'
+ assert_raise(FrozenError, bug15952) do
+ a = [1] * 100
+ b = a[4..-1]
+ a.replace([1])
+ b.freeze
+ b.unshift("a")
+ end
+ end
+
def test_OR # '|'
assert_equal(@cls[], @cls[] | @cls[])
assert_equal(@cls[1], @cls[1] | @cls[])
@@ -1817,13 +1979,86 @@ class TestArray < Test::Unit::TestCase
assert_equal([obj1], [obj1]|[obj2])
end
+ def test_OR_big_in_order
+ obj1, obj2 = Class.new do
+ attr_reader :name
+ def initialize(name) @name = name; end
+ def inspect; "test_OR_in_order(#{@name})"; end
+ def hash; 0; end
+ def eql?(a) true; end
+ break [new("1"), new("2")]
+ end
+ assert_equal([obj1], [obj1]*64|[obj2]*64)
+ end
+
+ def test_OR_big_array # '|'
+ assert_equal(@cls[1,2], @cls[1]*64 | @cls[2]*64)
+ assert_equal(@cls[1,2], @cls[1, 2]*64 | @cls[1, 2]*64)
+
+ a = (1..64).to_a
+ b = (1..128).to_a
+ c = a | b
+ assert_equal(c, b)
+ assert_not_same(c, b)
+ assert_equal((1..64).to_a, a)
+ assert_equal((1..128).to_a, b)
+ end
+
+ def test_union
+ assert_equal(@cls[], @cls[].union(@cls[]))
+ assert_equal(@cls[1], @cls[1].union(@cls[]))
+ assert_equal(@cls[1], @cls[].union(@cls[1]))
+ assert_equal(@cls[1], @cls[].union(@cls[], @cls[1]))
+ assert_equal(@cls[1], @cls[1].union(@cls[1]))
+ assert_equal(@cls[1], @cls[1].union(@cls[1], @cls[1], @cls[1]))
+
+ assert_equal(@cls[1,2], @cls[1].union(@cls[2]))
+ assert_equal(@cls[1,2], @cls[1, 1].union(@cls[2, 2]))
+ assert_equal(@cls[1,2], @cls[1, 2].union(@cls[1, 2]))
+ assert_equal(@cls[1,2], @cls[1, 1].union(@cls[1, 1], @cls[1, 2], @cls[2, 1], @cls[2, 2, 2]))
+
+ a = %w(a b c)
+ b = %w(a b c d e)
+ c = a.union(b)
+ assert_equal(c, b)
+ assert_not_same(c, b)
+ assert_equal(%w(a b c), a)
+ assert_equal(%w(a b c d e), b)
+ assert(a.none?(&:frozen?))
+ assert(b.none?(&:frozen?))
+ assert(c.none?(&:frozen?))
+ end
+
+ def test_union_big_array
+ assert_equal(@cls[1,2], (@cls[1]*64).union(@cls[2]*64))
+ assert_equal(@cls[1,2,3], (@cls[1, 2]*64).union(@cls[1, 2]*64, @cls[3]*60))
+
+ a = (1..64).to_a
+ b = (1..128).to_a
+ c = a | b
+ assert_equal(c, b)
+ assert_not_same(c, b)
+ assert_equal((1..64).to_a, a)
+ assert_equal((1..128).to_a, b)
+ end
+
def test_combination
- assert_equal(@cls[[]], @cls[1,2,3,4].combination(0).to_a)
- assert_equal(@cls[[1],[2],[3],[4]], @cls[1,2,3,4].combination(1).to_a)
- assert_equal(@cls[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]], @cls[1,2,3,4].combination(2).to_a)
- assert_equal(@cls[[1,2,3],[1,2,4],[1,3,4],[2,3,4]], @cls[1,2,3,4].combination(3).to_a)
- assert_equal(@cls[[1,2,3,4]], @cls[1,2,3,4].combination(4).to_a)
- assert_equal(@cls[], @cls[1,2,3,4].combination(5).to_a)
+ a = @cls[]
+ assert_equal(1, a.combination(0).size)
+ assert_equal(0, a.combination(1).size)
+ a = @cls[1,2,3,4]
+ assert_equal(1, a.combination(0).size)
+ assert_equal(4, a.combination(1).size)
+ assert_equal(6, a.combination(2).size)
+ assert_equal(4, a.combination(3).size)
+ assert_equal(1, a.combination(4).size)
+ assert_equal(0, a.combination(5).size)
+ assert_equal(@cls[[]], a.combination(0).to_a)
+ assert_equal(@cls[[1],[2],[3],[4]], a.combination(1).to_a)
+ assert_equal(@cls[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]], a.combination(2).to_a)
+ assert_equal(@cls[[1,2,3],[1,2,4],[1,3,4],[2,3,4]], a.combination(3).to_a)
+ assert_equal(@cls[[1,2,3,4]], a.combination(4).to_a)
+ assert_equal(@cls[], a.combination(5).to_a)
end
def test_product
@@ -1855,7 +2090,16 @@ class TestArray < Test::Unit::TestCase
end
def test_permutation
+ a = @cls[]
+ assert_equal(1, a.permutation(0).size)
+ assert_equal(0, a.permutation(1).size)
a = @cls[1,2,3]
+ assert_equal(1, a.permutation(0).size)
+ assert_equal(3, a.permutation(1).size)
+ assert_equal(6, a.permutation(2).size)
+ assert_equal(6, a.permutation(3).size)
+ assert_equal(0, a.permutation(4).size)
+ assert_equal(6, a.permutation.size)
assert_equal(@cls[[]], a.permutation(0).to_a)
assert_equal(@cls[[1],[2],[3]], a.permutation(1).to_a.sort)
assert_equal(@cls[[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]],
@@ -1880,15 +2124,24 @@ class TestArray < Test::Unit::TestCase
def test_permutation_stack_error
bug9932 = '[ruby-core:63103] [Bug #9932]'
- assert_separately([], <<-"end;", timeout: 30) # do
- assert_nothing_raised(SystemStackError, "#{bug9932}") do
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}", timeout: 30)
+ bug = "#{bug9932}"
+ begin;
+ assert_nothing_raised(SystemStackError, bug) do
assert_equal(:ok, Array.new(100_000, nil).permutation {break :ok})
end
end;
end
def test_repeated_permutation
+ a = @cls[]
+ assert_equal(1, a.repeated_permutation(0).size)
+ assert_equal(0, a.repeated_permutation(1).size)
a = @cls[1,2]
+ assert_equal(1, a.repeated_permutation(0).size)
+ assert_equal(2, a.repeated_permutation(1).size)
+ assert_equal(4, a.repeated_permutation(2).size)
+ assert_equal(8, a.repeated_permutation(3).size)
assert_equal(@cls[[]], a.repeated_permutation(0).to_a)
assert_equal(@cls[[1],[2]], a.repeated_permutation(1).to_a.sort)
assert_equal(@cls[[1,1],[1,2],[2,1],[2,2]],
@@ -1913,7 +2166,8 @@ class TestArray < Test::Unit::TestCase
end
def test_repeated_permutation_stack_error
- assert_separately([], <<-"end;", timeout: 30) # do
+ assert_separately([], "#{<<-"begin;"}\n#{<<~'end;'}", timeout: 30)
+ begin;
assert_nothing_raised(SystemStackError) do
assert_equal(:ok, Array.new(100_000, nil).repeated_permutation(500_000) {break :ok})
end
@@ -1921,7 +2175,15 @@ class TestArray < Test::Unit::TestCase
end
def test_repeated_combination
+ a = @cls[]
+ assert_equal(1, a.repeated_combination(0).size)
+ assert_equal(0, a.repeated_combination(1).size)
a = @cls[1,2,3]
+ assert_equal(1, a.repeated_combination(0).size)
+ assert_equal(3, a.repeated_combination(1).size)
+ assert_equal(6, a.repeated_combination(2).size)
+ assert_equal(10, a.repeated_combination(3).size)
+ assert_equal(15, a.repeated_combination(4).size)
assert_equal(@cls[[]], a.repeated_combination(0).to_a)
assert_equal(@cls[[1],[2],[3]], a.repeated_combination(1).to_a.sort)
assert_equal(@cls[[1,1],[1,2],[1,3],[2,2],[2,3],[3,3]],
@@ -1950,7 +2212,8 @@ class TestArray < Test::Unit::TestCase
end
def test_repeated_combination_stack_error
- assert_separately([], <<-"end;", timeout: 20) # do
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}", timeout: 20)
+ begin;
assert_nothing_raised(SystemStackError) do
assert_equal(:ok, Array.new(100_000, nil).repeated_combination(500_000) {break :ok})
end
@@ -2013,13 +2276,14 @@ class TestArray < Test::Unit::TestCase
assert_raise(IndexError) { [0][LONGP] = 2 }
assert_raise(IndexError) { [0][(LONGP + 1) / 2 - 1] = 2 }
assert_raise(IndexError) { [0][LONGP..-1] = 2 }
+ assert_raise(IndexError) { [0][LONGP..] = 2 }
a = [0]
a[2] = 4
assert_equal([0, nil, 4], a)
assert_raise(ArgumentError) { [0][0, 0, 0] = 0 }
assert_raise(ArgumentError) { [0].freeze[0, 0, 0] = 0 }
assert_raise(TypeError) { [0][:foo] = 0 }
- assert_raise(RuntimeError) { [0].freeze[:foo] = 0 }
+ assert_raise(FrozenError) { [0].freeze[:foo] = 0 }
end
def test_first2
@@ -2044,12 +2308,13 @@ class TestArray < Test::Unit::TestCase
end
def test_unshift_error
- assert_raise(RuntimeError) { [].freeze.unshift('cat') }
- assert_raise(RuntimeError) { [].freeze.unshift() }
+ assert_raise(FrozenError) { [].freeze.unshift('cat') }
+ assert_raise(FrozenError) { [].freeze.unshift() }
end
def test_aref
assert_raise(ArgumentError) { [][0, 0, 0] }
+ assert_raise(TypeError) { [][(1..10).step(2)] }
end
def test_fetch
@@ -2102,9 +2367,11 @@ class TestArray < Test::Unit::TestCase
assert_equal([0], a.insert(1))
assert_equal([0, 1], a.insert(1, 1))
assert_raise(ArgumentError) { a.insert }
+ assert_raise(TypeError) { a.insert(Object.new) }
assert_equal([0, 1, 2], a.insert(-1, 2))
assert_equal([0, 1, 3, 2], a.insert(-2, 3))
- assert_raise(RuntimeError) { [0].freeze.insert(0)}
+ assert_raise_with_message(IndexError, /-6/) { a.insert(-6, 4) }
+ assert_raise(FrozenError) { [0].freeze.insert(0)}
assert_raise(ArgumentError) { [0].freeze.insert }
end
@@ -2163,6 +2430,11 @@ class TestArray < Test::Unit::TestCase
}
assert_equal(9, r)
assert_equal(@cls[7, 8, 9, 10], a, bug10722)
+
+ bug13053 = '[ruby-core:78739] [Bug #13053] Array#select! can resize to negative size'
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ a.select! {|i| a.clear if i == 5; false }
+ assert_equal(0, a.size, bug13053)
end
# also select!
@@ -2180,6 +2452,25 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[4, 5], a)
end
+ def test_filter
+ assert_equal([0, 2], [0, 1, 2, 3].filter {|x| x % 2 == 0 })
+ end
+
+ # alias for select!
+ def test_filter!
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(nil, a.filter! { true })
+ assert_equal(@cls[1, 2, 3, 4, 5], a)
+
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(a, a.filter! { false })
+ assert_equal(@cls[], a)
+
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(a, a.filter! { |i| i > 3 })
+ assert_equal(@cls[4, 5], a)
+ end
+
def test_delete2
a = [0] * 1024 + [1] + [0] * 1024
a.delete(0)
@@ -2280,8 +2571,8 @@ class TestArray < Test::Unit::TestCase
assert_raise(ArgumentError) { a.flatten!(1, 2) }
assert_raise(TypeError) { a.flatten!(:foo) }
assert_raise(ArgumentError) { f.flatten!(1, 2) }
- assert_raise(RuntimeError) { f.flatten! }
- assert_raise(RuntimeError) { f.flatten!(:foo) }
+ assert_raise(FrozenError) { f.flatten! }
+ assert_raise(FrozenError) { f.flatten!(:foo) }
end
def test_shuffle
@@ -2337,6 +2628,13 @@ class TestArray < Test::Unit::TestCase
end
ary = (0...10000).to_a
assert_equal(ary.rotate, ary.shuffle(random: gen_to_int))
+
+ assert_raise(NoMethodError) {
+ ary.shuffle(random: Object.new)
+ }
+ assert_raise(NoMethodError) {
+ ary.shuffle!(random: Object.new)
+ }
end
def test_sample
@@ -2437,6 +2735,10 @@ class TestArray < Test::Unit::TestCase
end
ary = (0...10000).to_a
assert_equal(5000, ary.sample(random: gen_to_int))
+
+ assert_raise(NoMethodError) {
+ ary.sample(random: Object.new)
+ }
end
def test_cycle
@@ -2453,6 +2755,9 @@ class TestArray < Test::Unit::TestCase
a = []
[0, 1, 2].cycle(3) {|i| a << i }
assert_equal([0, 1, 2, 0, 1, 2, 0, 1, 2], a)
+
+ assert_equal(Float::INFINITY, a.cycle.size)
+ assert_equal(27, a.cycle(3).size)
end
def test_reverse_each2
@@ -2472,11 +2777,18 @@ class TestArray < Test::Unit::TestCase
def test_combination_clear
bug9939 = '[ruby-core:63149] [Bug #9939]'
- assert_ruby_status([], <<-'end;', bug9939)
- 100_000.times {Array.new(1000)}
+ assert_nothing_raised(bug9939) {
a = [*0..100]
a.combination(3) {|*,x| a.clear}
- end;
+ }
+
+ bug13052 = '[ruby-core:78738] [Bug #13052] Array#combination segfaults if the Array is modified during iteration'
+ assert_nothing_raised(bug13052) {
+ a = [*0..100]
+ a.combination(1) { a.clear }
+ a = [*0..100]
+ a.repeated_combination(1) { a.clear }
+ }
end
def test_product2
@@ -2494,8 +2806,8 @@ class TestArray < Test::Unit::TestCase
def test_array_subclass
assert_equal(Array2, Array2[1,2,3].uniq.class, "[ruby-dev:34581]")
- assert_equal(Array2, Array2[1,2][0,1].class) # embeded
- assert_equal(Array2, Array2[*(1..100)][1..99].class) #not embeded
+ assert_equal(Array2, Array2[1,2][0,1].class) # embedded
+ assert_equal(Array2, Array2[*(1..100)][1..99].class) #not embedded
end
def test_inspect
@@ -2598,7 +2910,7 @@ class TestArray < Test::Unit::TestCase
assert_equal([], a.rotate!(13))
assert_equal([], a.rotate!(-13))
a = [].freeze
- assert_raise_with_message(RuntimeError, /can\'t modify frozen/) {a.rotate!}
+ assert_raise_with_message(FrozenError, /can\'t modify frozen/) {a.rotate!}
a = [1,2,3]
assert_raise(ArgumentError) { a.rotate!(1, 1) }
end
@@ -2708,21 +3020,24 @@ class TestArray < Test::Unit::TestCase
Bug11235 = '[ruby-dev:49043] [Bug #11235]'
def test_push_over_ary_max
- assert_separately(['-', ARY_MAX.to_s, Bug11235], <<-"end;", timeout: 20)
+ assert_separately(['-', ARY_MAX.to_s, Bug11235], "#{<<~"begin;"}\n#{<<~'end;'}", timeout: 30)
+ begin;
a = Array.new(ARGV[0].to_i)
assert_raise(IndexError, ARGV[1]) {0x1000.times {a.push(1)}}
end;
end
def test_unshift_over_ary_max
- assert_separately(['-', ARY_MAX.to_s, Bug11235], <<-"end;")
+ assert_separately(['-', ARY_MAX.to_s, Bug11235], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
a = Array.new(ARGV[0].to_i)
assert_raise(IndexError, ARGV[1]) {0x1000.times {a.unshift(1)}}
end;
end
def test_splice_over_ary_max
- assert_separately(['-', ARY_MAX.to_s, Bug11235], <<-"end;")
+ assert_separately(['-', ARY_MAX.to_s, Bug11235], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
a = Array.new(ARGV[0].to_i)
assert_raise(IndexError, ARGV[1]) {a[0, 0] = Array.new(0x1000)}
end;
@@ -2736,8 +3051,8 @@ class TestArray < Test::Unit::TestCase
assert_raise(TypeError) {h.dig(1, 0)}
end
- FIXNUM_MIN = -(1 << (8 * RbConfig::SIZEOF['long'] - 2))
- FIXNUM_MAX = (1 << (8 * RbConfig::SIZEOF['long'] - 2)) - 1
+ FIXNUM_MIN = RbConfig::LIMITS['FIXNUM_MIN']
+ FIXNUM_MAX = RbConfig::LIMITS['FIXNUM_MAX']
def assert_typed_equal(e, v, cls, msg=nil)
assert_kind_of(cls, v, msg)
@@ -2810,13 +3125,20 @@ class TestArray < Test::Unit::TestCase
assert_float_equal(large_number+(small_number*10), [large_number, *[small_number]*10].sum)
assert_float_equal(large_number+(small_number*10), [large_number/1r, *[small_number]*10].sum)
assert_float_equal(large_number+(small_number*11), [small_number, large_number/1r, *[small_number]*10].sum)
+ assert_float_equal(small_number, [large_number, small_number, -large_number].sum)
+ assert_equal(+Float::INFINITY, [+Float::INFINITY].sum)
+ assert_equal(+Float::INFINITY, [0.0, +Float::INFINITY].sum)
+ assert_equal(+Float::INFINITY, [+Float::INFINITY, 0.0].sum)
+ assert_equal(-Float::INFINITY, [-Float::INFINITY].sum)
+ assert_equal(-Float::INFINITY, [0.0, -Float::INFINITY].sum)
+ assert_equal(-Float::INFINITY, [-Float::INFINITY, 0.0].sum)
+ assert_predicate([-Float::INFINITY, Float::INFINITY].sum, :nan?)
assert_equal("abc", ["a", "b", "c"].sum(""))
assert_equal([1, [2], 3], [[1], [[2]], [3]].sum([]))
- assert_separately(%w[-rmathn], <<-EOS, ignore_stderr: true)
- assert_equal(6, [1r, 2, 3r].sum)
- EOS
+ assert_raise(TypeError) {[0].sum("")}
+ assert_raise(TypeError) {[1].sum("")}
end
private
diff --git a/test/ruby/test_assignment.rb b/test/ruby/test_assignment.rb
index 45c4d6e058..c1f8c46890 100644
--- a/test/ruby/test_assignment.rb
+++ b/test/ruby/test_assignment.rb
@@ -129,17 +129,17 @@ class TestAssignment < Test::Unit::TestCase
}
assert_raise(NoMethodError, bug11096) {
- assert_equal(43, o.instance_eval {self.foo += 1})
+ o.instance_eval {self.foo += 1}
}
assert_raise(NoMethodError, bug11096) {
- assert_equal(1, o.instance_eval {self.foo &&= 1})
+ o.instance_eval {self.foo &&= 1}
}
assert_raise(NoMethodError, bug11096) {
- assert_equal(43, o.instance_eval {self[0] += 1})
+ o.instance_eval {self[0] += 1}
}
assert_raise(NoMethodError, bug11096) {
- assert_equal(1, o.instance_eval {self[0] &&= 1})
+ o.instance_eval {self[0] &&= 1}
}
end
@@ -480,11 +480,10 @@ class TestAssignment < Test::Unit::TestCase
assert_equal 1, a
assert_equal [2, 3], b
- # not supported yet
- #a, *b, c = 1, 2, 3, 4
- #assert_equal 1, a
- #assert_equal [2,3], b
- #assert_equal 4, c
+ a, *b, c = 1, 2, 3, 4
+ assert_equal 1, a
+ assert_equal [2,3], b
+ assert_equal 4, c
a = 1, 2
assert_equal [1, 2], a
diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb
new file mode 100644
index 0000000000..3ba67e69a2
--- /dev/null
+++ b/test/ruby/test_ast.rb
@@ -0,0 +1,283 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'tempfile'
+
+class RubyVM
+ module AbstractSyntaxTree
+ class Node
+ class CodePosition
+ include Comparable
+ attr_reader :lineno, :column
+ def initialize(lineno, column)
+ @lineno = lineno
+ @column = column
+ end
+
+ def <=>(other)
+ case
+ when lineno < other.lineno
+ -1
+ when lineno == other.lineno
+ column <=> other.column
+ when lineno > other.lineno
+ 1
+ end
+ end
+ end
+
+ def beg_pos
+ CodePosition.new(first_lineno, first_column)
+ end
+
+ def end_pos
+ CodePosition.new(last_lineno, last_column)
+ end
+
+ alias to_s inspect
+ end
+ end
+end
+
+class TestAst < Test::Unit::TestCase
+ class Helper
+ attr_reader :errors
+
+ def initialize(path)
+ @path = path
+ @errors = []
+ @debug = false
+ end
+
+ def validate_range
+ @errors = []
+ validate_range0(ast)
+
+ @errors.empty?
+ end
+
+ def validate_not_cared
+ @errors = []
+ validate_not_cared0(ast)
+
+ @errors.empty?
+ end
+
+ def ast
+ return @ast if defined?(@ast)
+ @ast = RubyVM::AbstractSyntaxTree.parse_file(@path)
+ end
+
+ private
+
+ def validate_range0(node)
+ beg_pos, end_pos = node.beg_pos, node.end_pos
+ children = node.children.grep(RubyVM::AbstractSyntaxTree::Node)
+
+ return true if children.empty?
+ # These NODE_D* has NODE_ARRAY as nd_next->nd_next whose last locations
+ # we can not update when item is appended.
+ return true if [:DSTR, :DXSTR, :DREGX, :DSYM].include? node.type
+
+ min = children.map(&:beg_pos).min
+ max = children.map(&:end_pos).max
+
+ unless beg_pos <= min
+ @errors << { type: :min_validation_error, min: min, beg_pos: beg_pos, node: node }
+ end
+
+ unless max <= end_pos
+ @errors << { type: :max_validation_error, max: max, end_pos: end_pos, node: node }
+ end
+
+ p "#{node} => #{children}" if @debug
+
+ children.each do |child|
+ p child if @debug
+ validate_range0(child)
+ end
+ end
+
+ def validate_not_cared0(node)
+ beg_pos, end_pos = node.beg_pos, node.end_pos
+ children = node.children.grep(RubyVM::AbstractSyntaxTree::Node)
+
+ @errors << { type: :first_lineno, node: node } if beg_pos.lineno == 0
+ @errors << { type: :first_column, node: node } if beg_pos.column == -1
+ @errors << { type: :last_lineno, node: node } if end_pos.lineno == 0
+ @errors << { type: :last_column, node: node } if end_pos.column == -1
+
+ children.each {|c| validate_not_cared0(c) }
+ end
+ end
+
+ SRCDIR = File.expand_path("../../..", __FILE__)
+
+ Dir.glob("test/**/*.rb", base: SRCDIR).each do |path|
+ define_method("test_ranges:#{path}") do
+ helper = Helper.new("#{SRCDIR}/#{path}")
+ helper.validate_range
+
+ assert_equal([], helper.errors)
+ end
+ end
+
+ Dir.glob("test/**/*.rb", base: SRCDIR).each do |path|
+ define_method("test_not_cared:#{path}") do
+ helper = Helper.new("#{SRCDIR}/#{path}")
+ helper.validate_not_cared
+
+ assert_equal([], helper.errors)
+ end
+ end
+
+ def test_allocate
+ assert_raise(TypeError) {RubyVM::AbstractSyntaxTree::Node.allocate}
+ end
+
+ def test_parse_argument_error
+ assert_raise(TypeError) {RubyVM::AbstractSyntaxTree.parse(0)}
+ assert_raise(TypeError) {RubyVM::AbstractSyntaxTree.parse(nil)}
+ assert_raise(TypeError) {RubyVM::AbstractSyntaxTree.parse(false)}
+ assert_raise(TypeError) {RubyVM::AbstractSyntaxTree.parse(true)}
+ assert_raise(TypeError) {RubyVM::AbstractSyntaxTree.parse(:foo)}
+ end
+
+ def test_column_with_long_heredoc_identifier
+ term = "A"*257
+ ast = RubyVM::AbstractSyntaxTree.parse("<<-#{term}\n""ddddddd\n#{term}\n")
+ node = ast.children[2]
+ assert_equal(:STR, node.type)
+ assert_equal(0, node.first_column)
+ end
+
+ def test_column_of_heredoc
+ node = RubyVM::AbstractSyntaxTree.parse("<<-SRC\nddddddd\nSRC\n").children[2]
+ assert_equal(:STR, node.type)
+ assert_equal(0, node.first_column)
+ assert_equal(6, node.last_column)
+
+ node = RubyVM::AbstractSyntaxTree.parse("<<SRC\nddddddd\nSRC\n").children[2]
+ assert_equal(:STR, node.type)
+ assert_equal(0, node.first_column)
+ assert_equal(5, node.last_column)
+ end
+
+ def test_parse_raises_syntax_error
+ assert_raise_with_message(SyntaxError, /\bend\b/) do
+ RubyVM::AbstractSyntaxTree.parse("end")
+ end
+ end
+
+ def test_parse_file_raises_syntax_error
+ Tempfile.create(%w"test_ast .rb") do |f|
+ f.puts "end"
+ f.close
+ assert_raise_with_message(SyntaxError, /\bend\b/) do
+ RubyVM::AbstractSyntaxTree.parse_file(f.path)
+ end
+ end
+ end
+
+ def test_of
+ proc = Proc.new { 1 + 2 }
+ method = self.method(__method__)
+
+ node_proc = RubyVM::AbstractSyntaxTree.of(proc)
+ node_method = RubyVM::AbstractSyntaxTree.of(method)
+
+ assert_instance_of(RubyVM::AbstractSyntaxTree::Node, node_proc)
+ assert_instance_of(RubyVM::AbstractSyntaxTree::Node, node_method)
+ assert_raise(TypeError) { RubyVM::AbstractSyntaxTree.of("1 + 2") }
+
+ Tempfile.create(%w"test_of .rb") do |tmp|
+ tmp.print "#{<<-"begin;"}\n#{<<-'end;'}"
+ begin;
+ SCRIPT_LINES__ = {}
+ assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(proc {|x| x}))
+ end;
+ tmp.close
+ assert_separately(["-", tmp.path], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ load ARGV[0]
+ assert_empty(SCRIPT_LINES__)
+ end;
+ end
+ end
+
+ def test_scope_local_variables
+ node = RubyVM::AbstractSyntaxTree.parse("x = 0")
+ lv, _, body = *node.children
+ assert_equal([:x], lv)
+ assert_equal(:LASGN, body.type)
+ end
+
+ def test_call
+ node = RubyVM::AbstractSyntaxTree.parse("nil.foo")
+ _, _, body = *node.children
+ assert_equal(:CALL, body.type)
+ recv, mid, args = body.children
+ assert_equal(:NIL, recv.type)
+ assert_equal(:foo, mid)
+ assert_nil(args)
+ end
+
+ def test_fcall
+ node = RubyVM::AbstractSyntaxTree.parse("foo()")
+ _, _, body = *node.children
+ assert_equal(:FCALL, body.type)
+ mid, args = body.children
+ assert_equal(:foo, mid)
+ assert_nil(args)
+ end
+
+ def test_vcall
+ node = RubyVM::AbstractSyntaxTree.parse("foo")
+ _, _, body = *node.children
+ assert_equal(:VCALL, body.type)
+ mid, args = body.children
+ assert_equal(:foo, mid)
+ assert_nil(args)
+ end
+
+ def test_defn
+ node = RubyVM::AbstractSyntaxTree.parse("def a; end")
+ _, _, body = *node.children
+ assert_equal(:DEFN, body.type)
+ mid, defn = body.children
+ assert_equal(:a, mid)
+ assert_equal(:SCOPE, defn.type)
+ end
+
+ def test_defs
+ node = RubyVM::AbstractSyntaxTree.parse("def a.b; end")
+ _, _, body = *node.children
+ assert_equal(:DEFS, body.type)
+ recv, mid, defn = body.children
+ assert_equal(:VCALL, recv.type)
+ assert_equal(:b, mid)
+ assert_equal(:SCOPE, defn.type)
+ end
+
+ def test_dstr
+ node = RubyVM::AbstractSyntaxTree.parse('"foo#{1}bar"')
+ _, _, body = *node.children
+ assert_equal(:DSTR, body.type)
+ head, body = body.children
+ assert_equal("foo", head)
+ assert_equal(:EVSTR, body.type)
+ body, = body.children
+ assert_equal(:LIT, body.type)
+ assert_equal([1], body.children)
+ end
+
+ def test_op_asgn2
+ node = RubyVM::AbstractSyntaxTree.parse("struct.field += foo")
+ _, _, body = *node.children
+ assert_equal(:OP_ASGN2, body.type)
+ recv, _, mid, op, value = body.children
+ assert_equal(:VCALL, recv.type)
+ assert_equal(:field, mid)
+ assert_equal(:+, op)
+ assert_equal(:VCALL, value.type)
+ end
+end
diff --git a/test/ruby/test_autoload.rb b/test/ruby/test_autoload.rb
index b2d8d660fe..1d2d1af00c 100644
--- a/test/ruby/test_autoload.rb
+++ b/test/ruby/test_autoload.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: false
require 'test/unit'
require 'tempfile'
-require 'thread'
class TestAutoload < Test::Unit::TestCase
def test_autoload_so
@@ -43,8 +42,10 @@ p Foo::Bar
require 'tmpdir'
Dir.mktmpdir('autoload') {|tmpdir|
tmpfile = tmpdir + '/foo.rb'
+ tmpfile2 = tmpdir + '/bar.rb'
a = Module.new do
autoload :X, tmpfile
+ autoload :Y, tmpfile2
end
b = Module.new do
include a
@@ -53,6 +54,10 @@ p Foo::Bar
assert_equal(true, b.const_defined?(:X))
assert_equal(tmpfile, a.autoload?(:X), bug4565)
assert_equal(tmpfile, b.autoload?(:X), bug4565)
+ assert_equal(true, a.const_defined?("Y"))
+ assert_equal(true, b.const_defined?("Y"))
+ assert_equal(tmpfile2, a.autoload?("Y"))
+ assert_equal(tmpfile2, b.autoload?("Y"))
}
end
@@ -241,6 +246,113 @@ p Foo::Bar
end
end
+ def test_bug_13526
+ script = File.join(__dir__, 'bug-13526.rb')
+ assert_ruby_status([script], '', '[ruby-core:81016] [Bug #13526]')
+ end
+
+ def test_autoload_private_constant
+ Dir.mktmpdir('autoload') do |tmpdir|
+ File.write(tmpdir+"/zzz.rb", "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class AutoloadTest
+ ZZZ = :ZZZ
+ private_constant :ZZZ
+ end
+ end;
+ assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-'end;'}")
+ bug = '[ruby-core:85516] [Bug #14469]'
+ begin;
+ class AutoloadTest
+ autoload :ZZZ, "zzz.rb"
+ end
+ assert_raise(NameError, bug) {AutoloadTest::ZZZ}
+ end;
+ end
+ end
+
+ def test_autoload_deprecate_constant
+ Dir.mktmpdir('autoload') do |tmpdir|
+ File.write(tmpdir+"/zzz.rb", "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class AutoloadTest
+ ZZZ = :ZZZ
+ deprecate_constant :ZZZ
+ end
+ end;
+ assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-'end;'}")
+ bug = '[ruby-core:85516] [Bug #14469]'
+ begin;
+ class AutoloadTest
+ autoload :ZZZ, "zzz.rb"
+ end
+ assert_warning(/ZZZ is deprecated/, bug) {AutoloadTest::ZZZ}
+ end;
+ end
+ end
+
+ def test_autoload_fork
+ EnvUtil.default_warning do
+ Tempfile.create(['autoload', '.rb']) {|file|
+ file.puts 'sleep 0.3; class AutoloadTest; end'
+ file.close
+ add_autoload(file.path)
+ begin
+ thrs = []
+ 3.times do
+ thrs << Thread.new { AutoloadTest; nil }
+ thrs << Thread.new { fork { AutoloadTest } }
+ end
+ thrs.each(&:join)
+ thrs.each do |th|
+ pid = th.value or next
+ _, status = Process.waitpid2(pid)
+ assert_predicate status, :success?
+ end
+ ensure
+ remove_autoload_constant
+ assert_nil $!, '[ruby-core:86410] [Bug #14634]'
+ end
+ }
+ end
+ end if Process.respond_to?(:fork)
+
+ def test_autoload_same_file
+ Dir.mktmpdir('autoload') do |tmpdir|
+ File.write("#{tmpdir}/b.rb", "#{<<~'begin;'}\n#{<<~'end;'}")
+ begin;
+ module Foo; end
+ module Bar; end
+ end;
+ 3.times do # timing-dependent, needs a few times to hit [Bug #14742]
+ assert_separately(%W[-I #{tmpdir}], "#{<<-'begin;'}\n#{<<-'end;'}")
+ begin;
+ autoload :Foo, 'b'
+ autoload :Bar, 'b'
+ t1 = Thread.new do Foo end
+ t2 = Thread.new do Bar end
+ t1.join
+ t2.join
+ bug = '[ruby-core:86935] [Bug #14742]'
+ assert_instance_of Module, t1.value, bug
+ assert_instance_of Module, t2.value, bug
+ end;
+ end
+ end
+ end
+
+ def test_no_leak
+ assert_no_memory_leak([], '', <<~'end;', 'many autoloads', timeout: 30)
+ 200000.times do |i|
+ m = Module.new
+ m.instance_eval do
+ autoload :Foo, 'x'
+ autoload :Bar, i.to_s
+ end
+ end
+ end;
+ end
+
def add_autoload(path)
(@autoload_paths ||= []) << path
::Object.class_eval {autoload(:AutoloadTest, path)}
diff --git a/test/ruby/test_backtrace.rb b/test/ruby/test_backtrace.rb
index ec6e0586d4..0730b5d1c5 100644
--- a/test/ruby/test_backtrace.rb
+++ b/test/ruby/test_backtrace.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: false
require 'test/unit'
-require 'thread'
require 'tempfile'
class TestBacktrace < Test::Unit::TestCase
@@ -298,4 +297,36 @@ class TestBacktrace < Test::Unit::TestCase
end
assert_not_match(/\Acore#/, e.backtrace_locations[0].base_label)
end
+
+ def test_notty_backtrace
+ err = ["-:1:in `<main>': unhandled exception"]
+ assert_in_out_err([], "raise", [], err)
+
+ err = ["-:2:in `foo': foo! (RuntimeError)",
+ "\tfrom -:4:in `<main>'"]
+ assert_in_out_err([], <<-"end;", [], err)
+ def foo
+ raise "foo!"
+ end
+ foo
+ end;
+
+ err = ["-:7:in `rescue in bar': bar! (RuntimeError)",
+ "\tfrom -:4:in `bar'",
+ "\tfrom -:9:in `<main>'",
+ "-:2:in `foo': foo! (RuntimeError)",
+ "\tfrom -:5:in `bar'",
+ "\tfrom -:9:in `<main>'"]
+ assert_in_out_err([], <<-"end;", [], err)
+ def foo
+ raise "foo!"
+ end
+ def bar
+ foo
+ rescue
+ raise "bar!"
+ end
+ bar
+ end;
+ end
end
diff --git a/test/ruby/test_basicinstructions.rb b/test/ruby/test_basicinstructions.rb
index dd3ca4dd22..ab32ee54e2 100644
--- a/test/ruby/test_basicinstructions.rb
+++ b/test/ruby/test_basicinstructions.rb
@@ -117,7 +117,6 @@ class TestBasicInstructions < Test::Unit::TestCase
assert_equal({1=>2}, {1=>2})
assert_equal({1=>2, 3=>4}, {1=>2, 3=>4})
assert_equal({1=>2, 3=>4}, {3=>4, 1=>2})
- # assert_equal({1=>2, 3=>4}, {1,2, 3,4}) # 1.9 doesn't support
assert_equal({"key"=>"val"}, {"key"=>"val"})
end
diff --git a/test/ruby/test_beginendblock.rb b/test/ruby/test_beginendblock.rb
index a09c963b3c..eb8394864f 100644
--- a/test/ruby/test_beginendblock.rb
+++ b/test/ruby/test_beginendblock.rb
@@ -31,7 +31,8 @@ class TestBeginEndBlock < Test::Unit::TestCase
end
def test_endblockwarn
- assert_in_out_err([], <<-'end;', [], ['-:2: warning: END in method; use at_exit'])
+ assert_in_out_err([], "#{<<~"begin;"}#{<<~'end;'}", [], ['-:2: warning: END in method; use at_exit'])
+ begin;
def end1
END {}
end
@@ -39,7 +40,8 @@ class TestBeginEndBlock < Test::Unit::TestCase
end
def test_endblockwarn_in_eval
- assert_in_out_err([], <<-'end;', [], ['(eval):2: warning: END in method; use at_exit'])
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", [], ['(eval):2: warning: END in method; use at_exit'])
+ begin;
eval <<-EOE
def end2
END {}
@@ -72,7 +74,8 @@ class TestBeginEndBlock < Test::Unit::TestCase
end
def test_propagate_signaled
- status = assert_in_out_err([], <<-'end;', [], /Interrupt$/)
+ status = assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", [], /Interrupt$/)
+ begin;
trap(:INT, "DEFAULT")
at_exit{Process.kill(:INT, $$)}
end;
@@ -82,7 +85,8 @@ class TestBeginEndBlock < Test::Unit::TestCase
end
def test_endblock_raise
- assert_in_out_err([], <<-'end;', %w(e6 e4 e2), [:*, /e5/, :*, /e3/, :*, /e1/, :*])
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", %w(e6 e4 e2), [:*, /e5/, :*, /e3/, :*, /e1/, :*])
+ begin;
END {raise "e1"}; END {puts "e2"}
END {raise "e3"}; END {puts "e4"}
END {raise "e5"}; END {puts "e6"}
@@ -99,7 +103,8 @@ class TestBeginEndBlock < Test::Unit::TestCase
"inner1",
"outer0" ]
- assert_in_out_err([], <<-'end;', expected, [], "[ruby-core:35237]")
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", expected, [], "[ruby-core:35237]")
+ begin;
at_exit { puts :outer0 }
at_exit { puts :outer1_begin; at_exit { puts :inner1 }; puts :outer1_end }
at_exit { puts :outer2_begin; at_exit { puts :inner2 }; puts :outer2_end }
@@ -122,18 +127,19 @@ class TestBeginEndBlock < Test::Unit::TestCase
def test_callcc_at_exit
bug9110 = '[ruby-core:58329][Bug #9110]'
- script = <<EOS
-require "continuation"
-c = nil
-at_exit { c.call }
-at_exit { callcc {|_c| c = _c } }
-EOS
- assert_normal_exit(script, bug9110)
+ assert_ruby_status([], "#{<<~"begin;"}\n#{<<~'end;'}", bug9110)
+ begin;
+ require "continuation"
+ c = nil
+ at_exit { c.call }
+ at_exit { callcc {|_c| c = _c } }
+ end;
end
def test_errinfo_at_exit
bug12302 = '[ruby-core:75038] [Bug #12302]'
- assert_in_out_err([], <<-'end;', %w[2:exit 1:exit], [], bug12302)
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", %w[2:exit 1:exit], [], bug12302)
+ begin;
at_exit do
puts "1:#{$!}"
end
@@ -148,4 +154,26 @@ EOS
end
end;
end
+
+ if defined?(fork)
+ def test_internal_errinfo_at_exit
+ # TODO: use other than break-in-fork to throw an internal
+ # error info.
+ error, pid, status = IO.pipe do |r, w|
+ pid = fork do
+ r.close
+ STDERR.reopen(w)
+ at_exit do
+ $!.class
+ end
+ break
+ end
+ w.close
+ [r.read, *Process.wait2(pid)]
+ end
+ assert_not_predicate(status, :success?)
+ assert_not_predicate(status, :signaled?)
+ assert_match(/unexpected break/, error)
+ end
+ end
end
diff --git a/test/ruby/test_bignum.rb b/test/ruby/test_bignum.rb
index 15811d5d43..58d63a7c29 100644
--- a/test/ruby/test_bignum.rb
+++ b/test/ruby/test_bignum.rb
@@ -6,8 +6,8 @@ rescue LoadError
else
class TestBignum < Test::Unit::TestCase
- FIXNUM_MIN = Integer::FIXNUM_MIN
- FIXNUM_MAX = Integer::FIXNUM_MAX
+ FIXNUM_MIN = RbConfig::LIMITS['FIXNUM_MIN']
+ FIXNUM_MAX = RbConfig::LIMITS['FIXNUM_MAX']
BIGNUM_MIN = FIXNUM_MAX + 1
b = BIGNUM_MIN
@@ -615,14 +615,17 @@ class TestBignum < Test::Unit::TestCase
start_flag = false
end_flag = false
num = (65536 ** 65536)
+ q = Queue.new
thread = Thread.new do
- start_flag = true
- num.to_s
- end_flag = true
+ assert_raise(RuntimeError) {
+ q << true
+ num.to_s
+ end_flag = true
+ }
end
- sleep 0.001 until start_flag
+ q.pop # sync
thread.raise
- thread.join rescue nil
+ thread.join
time = Time.now - time
skip "too fast cpu" if end_flag
assert_operator(time, :<, 10)
@@ -693,6 +696,10 @@ class TestBignum < Test::Unit::TestCase
o = Object.new
def o.coerce(x); [x, 2**100]; end
assert_equal((2**200).to_f, (2**300).fdiv(o))
+ o = Object.new
+ def o.coerce(x); [self, x]; end
+ def o.fdiv(x); 1; end
+ assert_equal(1.0, (2**300).fdiv(o))
end
def test_singleton_method
diff --git a/test/ruby/test_call.rb b/test/ruby/test_call.rb
index 4496d78210..2a1b671cac 100644
--- a/test/ruby/test_call.rb
+++ b/test/ruby/test_call.rb
@@ -50,8 +50,9 @@ class TestCall < Test::Unit::TestCase
o = nil
assert_nil(o&.x)
- assert_nothing_raised(NoMethodError) {o&.x = 6}
- assert_nothing_raised(NoMethodError) {o&.x *= 7}
+ assert_nothing_raised(NoMethodError) {o&.x = raise}
+ assert_nothing_raised(NoMethodError) {o&.x *= raise}
+ assert_nothing_raised(NoMethodError) {o&.x *= raise; nil}
end
def test_safe_call_evaluate_arguments_only_method_call_is_made
diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb
index 072729d4e5..2ab1e7f266 100644
--- a/test/ruby/test_class.rb
+++ b/test/ruby/test_class.rb
@@ -89,7 +89,7 @@ class TestClass < Test::Unit::TestCase
end
end
- def test_instanciate_singleton_class
+ def test_instantiate_singleton_class
c = class << Object.new; self; end
assert_raise(TypeError) { c.new }
end
@@ -232,6 +232,11 @@ class TestClass < Test::Unit::TestCase
assert_raise(TypeError) { Class.allocate.superclass }
bug6863 = '[ruby-core:47148]'
assert_raise(TypeError, bug6863) { Class.new(Class.allocate) }
+
+ allocator = Class.instance_method(:allocate)
+ assert_raise_with_message(TypeError, /prohibited/) {
+ allocator.bind(Rational).call
+ }
end
def test_nonascii_name
@@ -241,13 +246,28 @@ class TestClass < Test::Unit::TestCase
assert_equal("TestClass::C\u{df}", c.name, '[ruby-core:24600]')
end
- def test_invalid_jump_from_class_definition
+ def test_invalid_next_from_class_definition
assert_raise(SyntaxError) { eval("class C; next; end") }
+ end
+
+ def test_invalid_break_from_class_definition
assert_raise(SyntaxError) { eval("class C; break; end") }
+ end
+
+ def test_invalid_redo_from_class_definition
assert_raise(SyntaxError) { eval("class C; redo; end") }
+ end
+
+ def test_invalid_retry_from_class_definition
assert_raise(SyntaxError) { eval("class C; retry; end") }
+ end
+
+ def test_invalid_return_from_class_definition
assert_raise(SyntaxError) { eval("class C; return; end") }
- assert_raise(SyntaxError) { eval("class C; yield; end") }
+ end
+
+ def test_invalid_yield_from_class_definition
+ assert_raise(LocalJumpError) { eval("class C; yield; end") }
end
def test_clone
@@ -297,7 +317,8 @@ class TestClass < Test::Unit::TestCase
end
def test_cannot_reinitialize_class_with_initialize_copy # [ruby-core:50869]
- assert_in_out_err([], <<-'end;', ["Object"], [])
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", ["Object"], [])
+ begin;
class Class
def initialize_copy(*); super; end
end
@@ -411,6 +432,53 @@ class TestClass < Test::Unit::TestCase
assert_equal(:foo, d.foo)
end
+ def test_clone_singleton_class_exists
+ klass = Class.new do
+ def self.bar; :bar; end
+ end
+
+ o = klass.new
+ o.singleton_class
+ clone = o.clone
+
+ assert_empty(o.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.instance_methods(false))
+ assert_empty(o.singleton_class.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.singleton_class.instance_methods(false))
+ end
+
+ def test_clone_when_singleton_class_of_singleton_class_exists
+ klass = Class.new do
+ def self.bar; :bar; end
+ end
+
+ o = klass.new
+ o.singleton_class.singleton_class
+ clone = o.clone
+
+ assert_empty(o.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.instance_methods(false))
+ assert_empty(o.singleton_class.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.singleton_class.instance_methods(false))
+ end
+
+ def test_clone_when_method_exists_on_singleton_class_of_singleton_class
+ klass = Class.new do
+ def self.bar; :bar; end
+ end
+
+ o = klass.new
+ o.singleton_class.singleton_class.define_method(:s2_method) { :s2 }
+ clone = o.clone
+
+ assert_empty(o.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.instance_methods(false))
+ assert_equal(:s2, o.singleton_class.s2_method)
+ assert_equal(:s2, clone.singleton_class.s2_method)
+ assert_equal([:s2_method], o.singleton_class.singleton_class.instance_methods(false))
+ assert_equal([:s2_method], clone.singleton_class.singleton_class.instance_methods(false))
+ end
+
def test_singleton_class_p
feature7609 = '[ruby-core:51087] [Feature #7609]'
assert_predicate(self.singleton_class, :singleton_class?, feature7609)
@@ -427,14 +495,14 @@ class TestClass < Test::Unit::TestCase
obj = Object.new
c = obj.singleton_class
obj.freeze
- assert_raise_with_message(RuntimeError, /frozen object/) {
+ assert_raise_with_message(FrozenError, /frozen object/) {
c.class_eval {def f; end}
}
end
def test_singleton_class_message
c = Class.new.freeze
- assert_raise_with_message(RuntimeError, /frozen Class/) {
+ assert_raise_with_message(FrozenError, /frozen Class/) {
def c.f; end
}
end
@@ -579,7 +647,8 @@ class TestClass < Test::Unit::TestCase
m.module_eval "class #{n}; end"
}
- assert_separately([], <<-"end;")
+ assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}")
+ begin;
Date = (class C\u{1f5ff}; self; end).new
assert_raise_with_message(TypeError, /C\u{1f5ff}/) {
require 'date'
@@ -588,22 +657,24 @@ class TestClass < Test::Unit::TestCase
end
def test_should_not_expose_singleton_class_without_metaclass
- assert_normal_exit %q{
+ assert_normal_exit "#{<<~"begin;"}\n#{<<~'end;'}", '[Bug #11740]'
+ begin;
klass = Class.new(Array)
# The metaclass of +klass+ should handle #bla since it should inherit methods from meta:meta:Array
def (Array.singleton_class).bla; :bla; end
hidden = ObjectSpace.each_object(Class).find { |c| klass.is_a? c and c.inspect.include? klass.inspect }
raise unless hidden.nil?
- }, '[Bug #11740]'
+ end;
- assert_normal_exit %q{
+ assert_normal_exit "#{<<~"begin;"}\n#{<<~'end;'}", '[Bug #11740]'
+ begin;
klass = Class.new(Array)
klass.singleton_class
# The metaclass of +klass+ should handle #bla since it should inherit methods from meta:meta:Array
def (Array.singleton_class).bla; :bla; end
hidden = ObjectSpace.each_object(Class).find { |c| klass.is_a? c and c.inspect.include? klass.inspect }
raise if hidden.nil?
- }, '[Bug #11740]'
+ end;
end
end
diff --git a/test/ruby/test_clone.rb b/test/ruby/test_clone.rb
index 93ef438461..321feb07c7 100644
--- a/test/ruby/test_clone.rb
+++ b/test/ruby/test_clone.rb
@@ -26,4 +26,39 @@ class TestClone < Test::Unit::TestCase
assert_equal([M003, M002, M001], M003.ancestors)
end
+
+ def test_user_flags
+ assert_separately([], <<-EOS)
+ #
+ class Array
+ undef initialize_copy
+ def initialize_copy(*); end
+ end
+ x = [1, 2, 3].clone
+ assert_equal [], x, '[Bug #14847]'
+ EOS
+
+ assert_separately([], <<-EOS)
+ #
+ class Array
+ undef initialize_copy
+ def initialize_copy(*); end
+ end
+ x = [1,2,3,4,5,6,7][1..-2].clone
+ x.push(1,1,1,1,1)
+ assert_equal [1, 1, 1, 1, 1], x, '[Bug #14847]'
+ EOS
+
+ assert_separately([], <<-EOS)
+ #
+ class Hash
+ undef initialize_copy
+ def initialize_copy(*); end
+ end
+ h = {}
+ h.default_proc = proc { raise }
+ h = h.clone
+ assert_equal nil, h[:not_exist], '[Bug #14847]'
+ EOS
+ end
end
diff --git a/test/ruby/test_complex.rb b/test/ruby/test_complex.rb
index a510a07dfa..0161ce8ffe 100644
--- a/test/ruby/test_complex.rb
+++ b/test/ruby/test_complex.rb
@@ -48,8 +48,12 @@ class Complex_Test < Test::Unit::TestCase
end
def test_hash
- assert_kind_of(Integer, Complex(1,2).hash)
- assert_kind_of(Integer, Complex(1.0,2.0).hash)
+ h = Complex(1,2).hash
+ assert_kind_of(Integer, h)
+ assert_nothing_raised {h.to_s}
+ h = Complex(1.0,2.0).hash
+ assert_kind_of(Integer, h)
+ assert_nothing_raised {h.to_s}
h = {}
h[Complex(0)] = 0
@@ -75,7 +79,6 @@ class Complex_Test < Test::Unit::TestCase
def test_freeze
c = Complex(1)
- c.freeze
assert_predicate(c, :frozen?)
assert_instance_of(String, c.to_s)
end
@@ -266,6 +269,39 @@ class Complex_Test < Test::Unit::TestCase
assert_equal(Complex(Rational(5,3),Rational(2)), c + Rational(2,3))
end
+ def test_add_with_redefining_int_plus
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Integer
+ remove_method :+
+ def +(other); 42; end
+ end
+ a = Complex(1, 2) + Complex(0, 1)
+ puts a == Complex(42, 42)
+ end;
+ end
+
+ def test_add_with_redefining_float_plus
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Float
+ remove_method :+
+ def +(other); 42.0; end
+ end
+ a = Complex(1.0, 2.0) + Complex(0, 1)
+ puts a == Complex(42.0, 42.0)
+ end;
+ end
+
+ def test_add_with_redefining_rational_plus
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Rational
+ remove_method :+
+ def +(other); 355/113r; end
+ end
+ a = Complex(1r, 2r) + Complex(0, 1)
+ puts a == Complex(355/113r, 355/113r)
+ end;
+ end
+
def test_sub
c = Complex(1,2)
c2 = Complex(2,3)
@@ -279,6 +315,39 @@ class Complex_Test < Test::Unit::TestCase
assert_equal(Complex(Rational(1,3),Rational(2)), c - Rational(2,3))
end
+ def test_sub_with_redefining_int_minus
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Integer
+ remove_method :-
+ def -(other); 42; end
+ end
+ a = Complex(1, 2) - Complex(0, 1)
+ puts a == Complex(42, 42)
+ end;
+ end
+
+ def test_sub_with_redefining_float_minus
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Float
+ remove_method :-
+ def -(other); 42.0; end
+ end
+ a = Complex(1.0, 2.0) - Complex(0, 1)
+ puts a == Complex(42.0, 42.0)
+ end;
+ end
+
+ def test_sub_with_redefining_rational_minus
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Rational
+ remove_method :-
+ def -(other); 355/113r; end
+ end
+ a = Complex(1r, 2r) - Complex(0, 1)
+ puts a == Complex(355/113r, 355/113r)
+ end;
+ end
+
def test_mul
c = Complex(1,2)
c2 = Complex(2,3)
@@ -297,6 +366,42 @@ class Complex_Test < Test::Unit::TestCase
c = Complex(0, Float::INFINITY)
assert_equal(Complex(0, Float::INFINITY), c * Complex(1, 0))
assert_equal(Complex(-Float::INFINITY, 0), c * Complex(0, 1))
+
+ assert_equal(Complex(-0.0, -0.0), Complex(-0.0, 0) * Complex(0, 0))
+ end
+
+ def test_mul_with_redefining_int_mult
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Integer
+ remove_method :*
+ def *(other); 42; end
+ end
+ a = Complex(2, 0) * Complex(1, 2)
+ puts a == Complex(0, 84)
+ end;
+ end
+
+ def test_mul_with_redefining_float_mult
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Float
+ remove_method :*
+ def *(other); 42.0; end
+ end
+ a = Complex(2.0, 0.0) * Complex(1, 2)
+ puts a == Complex(0.0, 84.0)
+ end;
+ end
+
+
+ def test_mul_with_redefining_rational_mult
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Rational
+ remove_method :*
+ def *(other); 355/113r; end
+ end
+ a = Complex(2r, 0r) * Complex(1, 2)
+ puts a == Complex(0r, 2*355/113r)
+ end;
end
def test_div
@@ -320,6 +425,13 @@ class Complex_Test < Test::Unit::TestCase
assert_equal(Complex(Rational(1,2),Rational(1)), c / Rational(2))
assert_equal(Complex(Rational(3,2),Rational(3)), c / Rational(2,3))
+
+ c = Complex(1)
+ r = c / c
+ assert_instance_of(Complex, r)
+ assert_equal(1, r)
+ assert_predicate(r.real, :integer?)
+ assert_predicate(r.imag, :integer?)
end
def test_quo
@@ -397,6 +509,10 @@ class Complex_Test < Test::Unit::TestCase
r = c ** Rational(-2,3)
assert_in_delta(0.432, r.real, 0.001)
assert_in_delta(-0.393, r.imag, 0.001)
+
+ c = Complex(0.0, -888888888888888.0)**8888
+ assert_not_predicate(c.real, :nan?)
+ assert_not_predicate(c.imag, :nan?)
end
def test_cmp
@@ -434,7 +550,7 @@ class Complex_Test < Test::Unit::TestCase
end
class ObjectX
- def + (x) Rational(1) end
+ def +(x) Rational(1) end
alias - +
alias * +
alias / +
@@ -534,12 +650,10 @@ class Complex_Test < Test::Unit::TestCase
def test_marshal
c = Complex(1,2)
- c.instance_eval{@ivar = 9}
s = Marshal.dump(c)
c2 = Marshal.load(s)
assert_equal(c, c2)
- assert_equal(9, c2.instance_variable_get(:@ivar))
assert_instance_of(Complex, c2)
c = Complex(Rational(1,2),Rational(2,3))
@@ -551,7 +665,6 @@ class Complex_Test < Test::Unit::TestCase
bug3656 = '[ruby-core:31622]'
c = Complex(1,2)
- c.freeze
assert_predicate(c, :frozen?)
result = c.marshal_load([2,3]) rescue :fail
assert_equal(:fail, result, bug3656)
@@ -746,6 +859,33 @@ class Complex_Test < Test::Unit::TestCase
end
+ def test_Complex_without_exception
+ assert_nothing_raised(ArgumentError){
+ assert_equal(nil, Complex('5x', exception: false))
+ }
+ assert_nothing_raised(ArgumentError){
+ assert_equal(nil, Complex(nil, exception: false))
+ }
+ assert_nothing_raised(ArgumentError){
+ assert_equal(nil, Complex(Object.new, exception: false))
+ }
+ assert_nothing_raised(ArgumentError){
+ assert_equal(nil, Complex(1, nil, exception: false))
+ }
+ assert_nothing_raised(ArgumentError){
+ assert_equal(nil, Complex(1, Object.new, exception: false))
+ }
+
+ o = Object.new
+ def o.to_c; raise; end
+ assert_nothing_raised(ArgumentError){
+ assert_equal(nil, Complex(o, exception: false))
+ }
+ assert_nothing_raised(ArgumentError){
+ assert_equal(nil, Complex(1, o, exception: false))
+ }
+ end
+
def test_respond
c = Complex(1,1)
assert_not_respond_to(c, :%)
@@ -832,6 +972,12 @@ class Complex_Test < Test::Unit::TestCase
assert_predicate(-1-1i, :finite?)
assert_not_predicate(Float::INFINITY + 1i, :finite?)
assert_not_predicate(Complex(1, Float::INFINITY), :finite?)
+ assert_predicate(Complex(Float::MAX, 0.0), :finite?)
+ assert_predicate(Complex(0.0, Float::MAX), :finite?)
+ assert_predicate(Complex(Float::MAX, Float::MAX), :finite?)
+ assert_not_predicate(Complex(Float::NAN, 0), :finite?)
+ assert_not_predicate(Complex(0, Float::NAN), :finite?)
+ assert_not_predicate(Complex(Float::NAN, Float::NAN), :finite?)
end
def test_infinite_p
@@ -847,6 +993,12 @@ class Complex_Test < Test::Unit::TestCase
assert_equal(1, Complex(-1, Float::INFINITY).infinite?)
assert_equal(1, Complex(1, -Float::INFINITY).infinite?)
assert_equal(1, Complex(-1, -Float::INFINITY).infinite?)
+ assert_nil(Complex(Float::MAX, 0.0).infinite?)
+ assert_nil(Complex(0.0, Float::MAX).infinite?)
+ assert_nil(Complex(Float::MAX, Float::MAX).infinite?)
+ assert_nil(Complex(Float::NAN, 0).infinite?)
+ assert_nil(Complex(0, Float::NAN).infinite?)
+ assert_nil(Complex(Float::NAN, Float::NAN).infinite?)
end
def test_supp
@@ -946,4 +1098,30 @@ class Complex_Test < Test::Unit::TestCase
def test_known_bug
end
+ def test_canonicalize_internal
+ obj = Class.new(Numeric) do
+ attr_accessor :real
+ alias real? real
+ end.new
+ obj.real = true
+ c = Complex.rect(obj, 1);
+ obj.real = false
+ c = c.conj
+ assert_equal(obj, c.real)
+ assert_equal(-1, c.imag)
+ end
+
+ def test_canonicalize_polar
+ obj = Class.new(Numeric) do
+ def initialize
+ @x = 2
+ end
+ def real?
+ (@x -= 1) > 0
+ end
+ end.new
+ assert_raise(TypeError) do
+ Complex.polar(1, obj)
+ end
+ end
end
diff --git a/test/ruby/test_complexrational.rb b/test/ruby/test_complexrational.rb
index 1e7622a351..0360f5ee42 100644
--- a/test/ruby/test_complexrational.rb
+++ b/test/ruby/test_complexrational.rb
@@ -214,10 +214,10 @@ class SimpleRat < Numeric
def numerator() @num end
def denominator() @den end
- def +@ () self end
- def -@ () self.class.new(-@num, @den) end
+ def +@() self end
+ def -@() self.class.new(-@num, @den) end
- def + (o)
+ def +(o)
case o
when SimpleRat, Rational
a = @num * o.denominator
@@ -233,7 +233,7 @@ class SimpleRat < Numeric
end
end
- def - (o)
+ def -(o)
case o
when SimpleRat, Rational
a = @num * o.denominator
@@ -249,7 +249,7 @@ class SimpleRat < Numeric
end
end
- def * (o)
+ def *(o)
case o
when SimpleRat, Rational
a = @num * o.numerator
@@ -334,7 +334,7 @@ class SimpleRat < Numeric
def divmod(o) [div(o), modulo(o)] end
def quotrem(o) [quot(o), remainder(o)] end
- def ** (o)
+ def **(o)
case o
when SimpleRat, Rational
Float(self) ** o
@@ -357,7 +357,7 @@ class SimpleRat < Numeric
end
end
- def <=> (o)
+ def <=>(o)
case o
when SimpleRat, Rational
a = @num * o.denominator
@@ -373,7 +373,7 @@ class SimpleRat < Numeric
end
end
- def == (o)
+ def ==(o)
begin
(self <=> o) == 0
rescue
diff --git a/test/ruby/test_const.rb b/test/ruby/test_const.rb
index ef5c295b07..8784e0e988 100644
--- a/test/ruby/test_const.rb
+++ b/test/ruby/test_const.rb
@@ -65,4 +65,8 @@ WARNING
PRE
assert_no_memory_leak(%w[-W0 -], '', code, 'redefined constant', timeout: 30)
end
+
+ def test_toplevel_lookup
+ assert_raise(NameError, '[Feature #11547]') {TestConst::Object}
+ end
end
diff --git a/test/ruby/test_continuation.rb b/test/ruby/test_continuation.rb
index efc549b67a..8c62d20840 100644
--- a/test/ruby/test_continuation.rb
+++ b/test/ruby/test_continuation.rb
@@ -37,9 +37,11 @@ class TestContinuation < Test::Unit::TestCase
def test_error
cont = callcc{|c| c}
- assert_raise(RuntimeError){
- Thread.new{cont.call}.join
- }
+ Thread.new{
+ assert_raise(RuntimeError){
+ cont.call
+ }
+ }.join
assert_raise(LocalJumpError){
callcc
}
@@ -86,11 +88,16 @@ class TestContinuation < Test::Unit::TestCase
@memo += 1
c = cont
cont = nil
- c.call(nil)
+ begin
+ c.call(nil)
+ rescue RuntimeError
+ set_trace_func(nil)
+ end
end
end
end
cont = callcc { |cc| cc }
+
if cont
set_trace_func(func)
else
@@ -98,12 +105,12 @@ class TestContinuation < Test::Unit::TestCase
end
end
- def test_tracing_with_set_trace_func
+ def _test_tracing_with_set_trace_func
@memo = 0
tracing_with_set_trace_func
tracing_with_set_trace_func
tracing_with_set_trace_func
- assert_equal 3, @memo
+ assert_equal 0, @memo
end
def tracing_with_thread_set_trace_func
@@ -113,7 +120,11 @@ class TestContinuation < Test::Unit::TestCase
@memo += 1
c = cont
cont = nil
- c.call(nil)
+ begin
+ c.call(nil)
+ rescue RuntimeError
+ Thread.current.set_trace_func(nil)
+ end
end
end
cont = callcc { |cc| cc }
@@ -132,4 +143,3 @@ class TestContinuation < Test::Unit::TestCase
assert_equal 3, @memo
end
end
-
diff --git a/test/ruby/test_default_gems.rb b/test/ruby/test_default_gems.rb
new file mode 100644
index 0000000000..837f7571ea
--- /dev/null
+++ b/test/ruby/test_default_gems.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: false
+require 'rubygems'
+
+class TestDefaultGems < Test::Unit::TestCase
+
+ def test_validate_gemspec
+ srcdir = File.expand_path('../../..', __FILE__)
+ Dir.glob("#{srcdir}/{lib,ext}/**/*.gemspec").map do |src|
+ assert_nothing_raised do
+ raise("invalid spec in #{src}") unless Gem::Specification.load(src)
+ end
+ end
+ end
+
+end
diff --git a/test/ruby/test_defined.rb b/test/ruby/test_defined.rb
index de7ba9cc14..e1571d5714 100644
--- a/test/ruby/test_defined.rb
+++ b/test/ruby/test_defined.rb
@@ -23,40 +23,80 @@ class TestDefined < Test::Unit::TestCase
return !defined?(yield)
end
- def test_defined
+ def test_defined_global_variable
$x = nil
assert(defined?($x)) # global variable
assert_equal('global-variable', defined?($x))# returns description
+ end
+ def test_defined_local_variable
assert_nil(defined?(foo)) # undefined
foo=5
assert(defined?(foo)) # local variable
+ end
+ def test_defined_constant
assert(defined?(Array)) # constant
assert(defined?(::Array)) # toplevel constant
assert(defined?(File::Constants)) # nested constant
+ end
+
+ def test_defined_public_method
assert(defined?(Object.new)) # method
assert(defined?(Object::new)) # method
+ end
+
+ def test_defined_private_method
assert(!defined?(Object.print)) # private method
+ end
+
+ def test_defined_operator
assert(defined?(1 == 2)) # operator expression
+ end
+ def test_defined_protected_method
f = Foo.new
assert_nil(defined?(f.foo)) # protected method
f.bar(f) { |v| assert(v) }
+ f.bar(Class.new(Foo).new) { |v| assert(v, "inherited protected method") }
+ end
+
+ def test_defined_undefined_method
+ f = Foo.new
assert_nil(defined?(f.quux)) # undefined method
+ end
+
+ def test_defined_undefined_argument
+ f = Foo.new
assert_nil(defined?(f.baz(x))) # undefined argument
x = 0
assert(defined?(f.baz(x)))
assert_nil(defined?(f.quux(x)))
assert(defined?(print(x)))
assert_nil(defined?(quux(x)))
+ end
+
+ def test_defined_attrasgn
+ f = Foo.new
assert(defined?(f.attr = 1))
f.attrasgn_test { |v| assert(v) }
+ end
+
+ def test_defined_undef
+ x = Object.new
+ def x.foo; end
+ assert(defined?(x.foo))
+ x.instance_eval {undef :foo}
+ assert(!defined?(x.foo), "undefed method should not be defined?")
+ end
+ def test_defined_yield
assert(defined_test) # not iterator
assert(!defined_test{}) # called as iterator
+ end
+ def test_defined_matchdata
/a/ =~ ''
assert_equal nil, defined?($&)
assert_equal nil, defined?($`)
@@ -85,12 +125,16 @@ class TestDefined < Test::Unit::TestCase
assert_equal 'global-variable', defined?($+)
assert_equal 'global-variable', defined?($1)
assert_equal nil, defined?($2)
+ end
+ def test_defined_literal
assert_equal("nil", defined?(nil))
assert_equal("true", defined?(true))
assert_equal("false", defined?(false))
assert_equal("expression", defined?(1))
+ end
+ def test_defined_empty_paren_expr
bug8224 = '[ruby-core:54024] [Bug #8224]'
(1..3).each do |level|
expr = "("*level+")"*level
@@ -99,6 +143,10 @@ class TestDefined < Test::Unit::TestCase
end
end
+ def test_defined_empty_paren_arg
+ assert_nil(defined?(p () + 1))
+ end
+
def test_defined_impl_specific
feature7035 = '[ruby-core:47558]' # not spec
assert_predicate(defined?(Foo), :frozen?, feature7035)
@@ -249,4 +297,8 @@ class TestDefined < Test::Unit::TestCase
assert_equal(nil, obj.func_defined_non_existing_func, bug_11212)
assert_equal(true, obj.called, bug_11212)
end
+
+ def test_top_level_constant_not_defined
+ assert_nil(defined?(TestDefined::Object))
+ end
end
diff --git a/test/ruby/test_dir.rb b/test/ruby/test_dir.rb
index bb778e754d..5196c08f81 100644
--- a/test/ruby/test_dir.rb
+++ b/test/ruby/test_dir.rb
@@ -11,11 +11,13 @@ class TestDir < Test::Unit::TestCase
$VERBOSE = nil
@root = File.realpath(Dir.mktmpdir('__test_dir__'))
@nodir = File.join(@root, "dummy")
+ @dirs = []
for i in "a".."z"
if i.ord % 2 == 0
FileUtils.touch(File.join(@root, i))
else
FileUtils.mkdir(File.join(@root, i))
+ @dirs << File.join(i, "")
end
end
end
@@ -136,8 +138,10 @@ class TestDir < Test::Unit::TestCase
Dir.glob(File.join(@root, "*"), File::FNM_DOTMATCH).sort)
assert_equal([@root] + ("a".."z").map {|f| File.join(@root, f) }.sort,
Dir.glob([@root, File.join(@root, "*")]).sort)
- assert_equal([@root] + ("a".."z").map {|f| File.join(@root, f) }.sort,
- Dir.glob(@root + "\0\0\0" + File.join(@root, "*")).sort)
+ assert_warning(/nul-separated patterns/) do
+ assert_equal([@root] + ("a".."z").map {|f| File.join(@root, f) }.sort,
+ Dir.glob(@root + "\0\0\0" + File.join(@root, "*")).sort)
+ end
assert_equal(("a".."z").step(2).map {|f| File.join(File.join(@root, f), "") }.sort,
Dir.glob(File.join(@root, "*/")).sort)
@@ -156,6 +160,9 @@ class TestDir < Test::Unit::TestCase
open(File.join(@root, "}}a"), "wb") {}
assert_equal(%w(}}{} }}a).map {|f| File.join(@root, f)}, Dir.glob(File.join(@root, '}}{\{\},a}')))
assert_equal(%w(}}{} }}a b c).map {|f| File.join(@root, f)}, Dir.glob(File.join(@root, '{\}\}{\{\},a},b,c}')))
+ assert_raise(ArgumentError) {
+ Dir.glob([[@root, File.join(@root, "*")].join("\0")])
+ }
end
def test_glob_recursive
@@ -184,17 +191,112 @@ class TestDir < Test::Unit::TestCase
end
end
- def assert_entries(entries)
+ def test_glob_recursive_directory
+ Dir.chdir(@root) do
+ ['d', 'e'].each do |path|
+ FileUtils.mkdir_p("c/#{path}/a/b/c")
+ FileUtils.touch("c/#{path}/a/a.file")
+ FileUtils.touch("c/#{path}/a/b/b.file")
+ FileUtils.touch("c/#{path}/a/b/c/c.file")
+ end
+ bug15540 = '[ruby-core:91110] [Bug #15540]'
+ assert_equal(["c/d/a/", "c/d/a/b/", "c/d/a/b/c/", "c/e/a/", "c/e/a/b/", "c/e/a/b/c/"],
+ Dir.glob('c/{d,e}/a/**/'), bug15540)
+ end
+ end
+
+ def test_glob_starts_with_brace
+ Dir.chdir(@root) do
+ bug15649 = '[ruby-core:91728] [Bug #15649]'
+ assert_equal(["#{@root}/a", "#{@root}/b"],
+ Dir.glob("{#{@root}/a,#{@root}/b}"), bug15649)
+ end
+ end
+
+ if Process.const_defined?(:RLIMIT_NOFILE)
+ def test_glob_too_may_open_files
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}", chdir: @root)
+ begin;
+ n = 16
+ Process.setrlimit(Process::RLIMIT_NOFILE, n)
+ files = []
+ begin
+ n.times {files << File.open('b')}
+ rescue Errno::EMFILE, Errno::ENFILE => e
+ end
+ assert_raise(e.class) {
+ Dir.glob('*')
+ }
+ end;
+ end
+ end
+
+ def test_glob_base
+ files = %w[a/foo.c c/bar.c]
+ files.each {|n| File.write(File.join(@root, n), "")}
+ Dir.mkdir(File.join(@root, "a/dir"))
+ dirs = @dirs + %w[a/dir/]
+ dirs.sort!
+ assert_equal(files, Dir.glob("*/*.c", base: @root).sort)
+ assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: ".").sort})
+ assert_equal(%w[foo.c], Dir.chdir(@root) {Dir.glob("*.c", base: "a").sort})
+ assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: "").sort})
+ assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: nil).sort})
+ assert_equal(@dirs, Dir.glob("*/", base: @root).sort)
+ assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: ".").sort})
+ assert_equal(%w[dir/], Dir.chdir(@root) {Dir.glob("*/", base: "a").sort})
+ assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: "").sort})
+ assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: nil).sort})
+ assert_equal(dirs, Dir.glob("**/*/", base: @root).sort)
+ assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: ".").sort})
+ assert_equal(%w[dir/], Dir.chdir(@root) {Dir.glob("**/*/", base: "a").sort})
+ assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: "").sort})
+ assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: nil).sort})
+ end
+
+ def test_glob_base_dir
+ files = %w[a/foo.c c/bar.c]
+ files.each {|n| File.write(File.join(@root, n), "")}
+ Dir.mkdir(File.join(@root, "a/dir"))
+ dirs = @dirs + %w[a/dir/]
+ dirs.sort!
+ assert_equal(files, Dir.open(@root) {|d| Dir.glob("*/*.c", base: d)}.sort)
+ assert_equal(%w[foo.c], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("*.c", base: d)}})
+ assert_equal(@dirs, Dir.open(@root) {|d| Dir.glob("*/", base: d).sort})
+ assert_equal(%w[dir/], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("*/", base: d).sort}})
+ assert_equal(dirs, Dir.open(@root) {|d| Dir.glob("**/*/", base: d).sort})
+ assert_equal(%w[dir/], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("**/*/", base: d).sort}})
+ end
+
+ def assert_entries(entries, children_only = false)
entries.sort!
- assert_equal(%w(. ..) + ("a".."z").to_a, entries)
+ expected = ("a".."z").to_a
+ expected = %w(. ..) + expected unless children_only
+ assert_equal(expected, entries)
end
def test_entries
assert_entries(Dir.open(@root) {|dir| dir.entries})
+ assert_entries(Dir.entries(@root).to_a)
+ assert_raise(ArgumentError) {Dir.entries(@root+"\0")}
end
def test_foreach
+ assert_entries(Dir.open(@root) {|dir| dir.each.to_a})
assert_entries(Dir.foreach(@root).to_a)
+ assert_raise(ArgumentError) {Dir.foreach(@root+"\0").to_a}
+ end
+
+ def test_children
+ assert_entries(Dir.open(@root) {|dir| dir.children}, true)
+ assert_entries(Dir.children(@root), true)
+ assert_raise(ArgumentError) {Dir.children(@root+"\0")}
+ end
+
+ def test_each_child
+ assert_entries(Dir.open(@root) {|dir| dir.each_child.to_a}, true)
+ assert_entries(Dir.each_child(@root).to_a, true)
+ assert_raise(ArgumentError) {Dir.each_child(@root+"\0").to_a}
end
def test_dir_enc
@@ -289,17 +391,19 @@ class TestDir < Test::Unit::TestCase
ENV.delete("HOME")
ENV.delete("LOGDIR")
- assert_raise(ArgumentError) { Dir.home }
- assert_raise(ArgumentError) { Dir.home("") }
ENV["HOME"] = @nodir
- assert_nothing_raised(ArgumentError) {
+ assert_nothing_raised(ArgumentError) do
assert_equal(@nodir, Dir.home)
+ end
+ assert_nothing_raised(ArgumentError) do
assert_equal(@nodir, Dir.home(""))
- if user = ENV["USER"]
- ENV["HOME"] = env_home
- assert_equal(File.expand_path(env_home), Dir.home(user))
+ end
+ if user = ENV["USER"]
+ tilde = windows? ? "~" : "~#{user}"
+ assert_nothing_raised(ArgumentError) do
+ assert_equal(File.expand_path(tilde), Dir.home(user))
end
- }
+ end
%W[no:such:user \u{7559 5b88}:\u{756a}].each do |user|
assert_raise_with_message(ArgumentError, /#{user}/) {Dir.home(user)}
end
@@ -353,6 +457,7 @@ class TestDir < Test::Unit::TestCase
end
assert_raise(Errno::ENOENT) {Dir.empty?(@nodir)}
assert_not_send([Dir, :empty?, File.join(@root, "b")])
+ assert_raise(ArgumentError) {Dir.empty?(@root+"\0")}
end
def test_glob_gc_for_fd
diff --git a/test/ruby/test_dir_m17n.rb b/test/ruby/test_dir_m17n.rb
index 754c035ccb..7584074c7e 100644
--- a/test/ruby/test_dir_m17n.rb
+++ b/test/ruby/test_dir_m17n.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: false
require 'test/unit'
require 'tmpdir'
+require '-test-/file'
class TestDir_M17N < Test::Unit::TestCase
def with_tmpdir
@@ -59,6 +60,8 @@ class TestDir_M17N < Test::Unit::TestCase
def test_filename_extutf8_invalid
return if /cygwin/ =~ RUBY_PLATFORM
+ # High Sierra's APFS cannot use invalid filenames
+ return if Bug::File::Fs.fsname(Dir.tmpdir) == "apfs"
with_tmpdir {|d|
assert_separately(%w[-EASCII-8BIT], <<-'EOS', :chdir=>d)
filename = "\xff".force_encoding("ASCII-8BIT") # invalid byte sequence as UTF-8
@@ -375,13 +378,20 @@ class TestDir_M17N < Test::Unit::TestCase
a = "file_one*".force_encoding Encoding::IBM437
b = "file_two*".force_encoding Encoding::EUC_JP
assert_equal([a, b].map(&:encoding), Dir[a, b].map(&:encoding))
- dir = "\u{76EE 5F551}"
+ if Bug::File::Fs.fsname(Dir.pwd) == "apfs"
+ # High Sierra's APFS cannot use filenames with undefined character
+ dir = "\u{76EE}"
+ else
+ dir = "\u{76EE 5F551}"
+ end
Dir.mkdir(dir)
list << dir
bug12081 = '[ruby-core:73868] [Bug #12081]'
a = "*".force_encoding("us-ascii")
result = Dir[a].map {|n|
- if n.encoding == Encoding::ASCII_8BIT
+ if n.encoding == Encoding::ASCII_8BIT ||
+ n.encoding == Encoding::ISO_8859_1 ||
+ !n.valid_encoding?
n.force_encoding(Encoding::UTF_8)
else
n.encode(Encoding::UTF_8)
@@ -402,12 +412,12 @@ class TestDir_M17N < Test::Unit::TestCase
with_tmpdir {|d|
orig = %W"d\u{e9}tente x\u{304c 304e 3050 3052 3054}"
orig.each {|n| open(n, "w") {}}
+ enc = Encoding.find("filesystem")
+ enc = Encoding::ASCII_8BIT if enc == Encoding::US_ASCII
if /mswin|mingw/ =~ RUBY_PLATFORM
- opts = {:encoding => Encoding.default_external}
+ opts = {:encoding => enc}
orig.map! {|o| o.encode("filesystem") rescue o.tr("^a-z", "?")}
else
- enc = Encoding.find("filesystem")
- enc = Encoding::ASCII_8BIT if enc == Encoding::US_ASCII
orig.each {|o| o.force_encoding(enc) }
end
ents = Dir.entries(".", opts).reject {|n| /\A\./ =~ n}
diff --git a/test/ruby/test_encoding.rb b/test/ruby/test_encoding.rb
index 06559651c5..8f73a8fce1 100644
--- a/test/ruby/test_encoding.rb
+++ b/test/ruby/test_encoding.rb
@@ -117,9 +117,10 @@ class TestEncoding < Test::Unit::TestCase
end
def test_errinfo_after_autoload
+ assert_separately(%w[--disable=gems], "#{<<~"begin;"}\n#{<<~'end;'}")
bug9038 = '[ruby-core:57949] [Bug #9038]'
- assert_separately(%w[--disable=gems], <<-"end;")
- assert_raise_with_message(SyntaxError, /unknown regexp option - Q/, #{bug9038.dump}) {
+ begin;
+ assert_raise_with_message(SyntaxError, /unknown regexp option - Q/, bug9038) {
eval("/regexp/sQ")
}
end;
diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb
index 4ff4f88ca9..568fa0ea8d 100644
--- a/test/ruby/test_enum.rb
+++ b/test/ruby/test_enum.rb
@@ -144,8 +144,7 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal([], inf.to_a)
end
- def test_to_h
- obj = Object.new
+ StubToH = Object.new.tap do |obj|
def obj.each(*args)
yield(*args)
yield [:key, :value]
@@ -157,6 +156,12 @@ class TestEnumerable < Test::Unit::TestCase
yield kvp
end
obj.extend Enumerable
+ obj.freeze
+ end
+
+ def test_to_h
+ obj = StubToH
+
assert_equal({
:hello => :world,
:key => :value,
@@ -175,6 +180,27 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal "element has wrong array length (expected 2, was 1)", e.message
end
+ def test_to_h_block
+ obj = StubToH
+
+ assert_equal({
+ "hello" => "world",
+ "key" => "value",
+ "other_key" => "other_value",
+ "obtained" => "via_to_ary",
+ }, obj.to_h(:hello, :world) {|k, v| [k.to_s, v.to_s]})
+
+ e = assert_raise(TypeError) {
+ obj.to_h {:not_an_array}
+ }
+ assert_equal "wrong element type Symbol (expected array)", e.message
+
+ e = assert_raise(ArgumentError) {
+ obj.to_h {[1]}
+ }
+ assert_equal "element has wrong array length (expected 2, was 1)", e.message
+ end
+
def test_inject
assert_equal(12, @obj.inject {|z, x| z * x })
assert_equal(48, @obj.inject {|z, x| z * 2 + x })
@@ -184,8 +210,8 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal(nil, @empty.inject() {9})
end
- FIXNUM_MIN = Integer::FIXNUM_MIN
- FIXNUM_MAX = Integer::FIXNUM_MAX
+ FIXNUM_MIN = RbConfig::LIMITS['FIXNUM_MIN']
+ FIXNUM_MAX = RbConfig::LIMITS['FIXNUM_MAX']
def test_inject_array_mul
assert_equal(nil, [].inject(:*))
@@ -196,11 +222,6 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal(105, [5, 7].inject(3, :*))
end
- def assert_float_equal(e, v, msg=nil)
- assert_equal(Float, v.class, msg)
- assert_equal(e, v, msg)
- end
-
def test_inject_array_plus
assert_equal(3, [3].inject(:+))
assert_equal(8, [3, 5].inject(:+))
@@ -218,15 +239,43 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal(2.0+3.0i, [2.0, 3.0i].inject(:+))
end
- def test_inject_array_plus_redefined
- assert_separately([], <<-"end;")
- class Integer
- undef :+
- def +(x)
- 0
+ def test_inject_array_op_redefined
+ assert_separately([], "#{<<~"end;"}\n""end")
+ all_assertions_foreach("", *%i[+ * / - %]) do |op|
+ bug = '[ruby-dev:49510] [Bug#12178] should respect redefinition'
+ begin
+ Integer.class_eval do
+ alias_method :orig, op
+ define_method(op) do |x|
+ 0
+ end
+ end
+ assert_equal(0, [1,2,3].inject(op), bug)
+ ensure
+ Integer.class_eval do
+ undef_method op
+ alias_method op, :orig
+ end
+ end
+ end;
+ end
+
+ def test_inject_array_op_private
+ assert_separately([], "#{<<~"end;"}\n""end")
+ all_assertions_foreach("", *%i[+ * / - %]) do |op|
+ bug = '[ruby-core:81349] [Bug #13592] should respect visibility'
+ assert_raise_with_message(NoMethodError, /private method/, bug) do
+ begin
+ Integer.class_eval do
+ private op
+ end
+ [1,2,3].inject(op)
+ ensure
+ Integer.class_eval do
+ public op
+ end
end
end
- assert_equal(0, [1,2,3].inject(:+), "[ruby-dev:49510] [Bug#12178]")
end;
end
@@ -287,6 +336,23 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal(false, [true, true, false].all?)
assert_equal(true, [].all?)
assert_equal(true, @empty.all?)
+ assert_equal(true, @obj.all?(Fixnum))
+ assert_equal(false, @obj.all?(1..2))
+ end
+
+ def test_all_with_unused_block
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ [1, 2].all?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ (1..2).all?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ 3.times.all?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ {a: 1, b: 2}.all?([:b, 2]) {|x| x == 4 }
+ EOS
end
def test_any
@@ -296,27 +362,73 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal(false, [false, false, false].any?)
assert_equal(false, [].any?)
assert_equal(false, @empty.any?)
+ assert_equal(true, @obj.any?(1..2))
+ assert_equal(false, @obj.any?(Float))
+ assert_equal(false, [1, 42].any?(Float))
+ assert_equal(true, [1, 4.2].any?(Float))
+ assert_equal(false, {a: 1, b: 2}.any?(->(kv) { kv == [:foo, 42] }))
+ assert_equal(true, {a: 1, b: 2}.any?(->(kv) { kv == [:b, 2] }))
+ end
+
+ def test_any_with_unused_block
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ [1, 23].any?(1) {|x| x == 1 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ (1..2).any?(34) {|x| x == 2 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ 3.times.any?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ {a: 1, b: 2}.any?([:b, 2]) {|x| x == 4 }
+ EOS
end
def test_one
assert(@obj.one? {|x| x == 3 })
assert(!(@obj.one? {|x| x == 1 }))
assert(!(@obj.one? {|x| x == 4 }))
+ assert(@obj.one?(3..4))
+ assert(!(@obj.one?(1..2)))
+ assert(!(@obj.one?(4..5)))
assert(%w{ant bear cat}.one? {|word| word.length == 4})
assert(!(%w{ant bear cat}.one? {|word| word.length > 4}))
assert(!(%w{ant bear cat}.one? {|word| word.length < 4}))
+ assert(%w{ant bear cat}.one?(/b/))
+ assert(!(%w{ant bear cat}.one?(/t/)))
assert(!([ nil, true, 99 ].one?))
assert([ nil, true, false ].one?)
assert(![].one?)
assert(!@empty.one?)
+ assert([ nil, true, 99 ].one?(Integer))
+ end
+
+ def test_one_with_unused_block
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ [1, 2].one?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ (1..2).one?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ 3.times.one?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ {a: 1, b: 2}.one?([:b, 2]) {|x| x == 4 }
+ EOS
end
def test_none
assert(@obj.none? {|x| x == 4 })
assert(!(@obj.none? {|x| x == 1 }))
assert(!(@obj.none? {|x| x == 3 }))
+ assert(@obj.none?(4..5))
+ assert(!(@obj.none?(1..3)))
assert(%w{ant bear cat}.none? {|word| word.length == 5})
assert(!(%w{ant bear cat}.none? {|word| word.length >= 4}))
+ assert(%w{ant bear cat}.none?(/d/))
+ assert(!(%w{ant bear cat}.none?(/b/)))
assert([].none?)
assert([nil].none?)
assert([nil,false].none?)
@@ -324,6 +436,21 @@ class TestEnumerable < Test::Unit::TestCase
assert(@empty.none?)
end
+ def test_none_with_unused_block
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ [1, 2].none?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ (1..2).none?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ 3.times.none?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ {a: 1, b: 2}.none?([:b, 2]) {|x| x == 4 }
+ EOS
+ end
+
def test_min
assert_equal(1, @obj.min)
assert_equal(3, @obj.min {|a,b| b <=> a })
@@ -581,12 +708,41 @@ class TestEnumerable < Test::Unit::TestCase
[o, o, o].sort_by {|x| x }
c.call
end
+
+ assert_raise_with_message(RuntimeError, /reentered/) do
+ i = 0
+ c = nil
+ o = Object.new
+ class << o; self; end.class_eval do
+ define_method(:<=>) do |x|
+ callcc {|c2| c ||= c2 }
+ i += 1
+ 0
+ end
+ end
+ [o, o].min(1)
+ assert_operator(i, :<=, 5, "infinite loop")
+ c.call
+ end
end
def test_reverse_each
assert_equal([2,1,3,2,1], @obj.reverse_each.to_a)
end
+ def test_reverse_each_memory_corruption
+ bug16354 = '[ruby-dev:50867]'
+ assert_normal_exit %q{
+ size = 1000
+ (0...size).reverse_each do |i|
+ i.inspect
+ ObjectSpace.each_object(Array) do |a|
+ a.clear if a.length == size
+ end
+ end
+ }, bug16354
+ end
+
def test_chunk
e = [].chunk {|elt| true }
assert_equal([], e.to_a)
@@ -798,6 +954,10 @@ class TestEnumerable < Test::Unit::TestCase
lambda2 = ->(x, i) { [x.upcase, i] }
assert_equal([['A',0], ['B',1], ['C',2], ['D',3], ['E',4]],
@obj.each_with_index.map(&lambda2))
+
+ hash = { a: 'hoge', b: 'fuga' }
+ lambda = -> (k, v) { "#{k}:#{v}" }
+ assert_equal ["a:hoge", "b:fuga"], hash.map(&lambda)
end
def test_flat_map
@@ -903,13 +1063,25 @@ class TestEnumerable < Test::Unit::TestCase
assert_float_equal(large_number+(small_number*10), [large_number, *[small_number]*10].each.sum)
assert_float_equal(large_number+(small_number*10), [large_number/1r, *[small_number]*10].each.sum)
assert_float_equal(large_number+(small_number*11), [small_number, large_number/1r, *[small_number]*10].each.sum)
+ assert_float_equal(small_number, [large_number, small_number, -large_number].each.sum)
+
+ k = Class.new do
+ include Enumerable
+ def initialize(*values)
+ @values = values
+ end
+ def each(&block)
+ @values.each(&block)
+ end
+ end
+ assert_equal(+Float::INFINITY, k.new(0.0, +Float::INFINITY).sum)
+ assert_equal(+Float::INFINITY, k.new(+Float::INFINITY, 0.0).sum)
+ assert_equal(-Float::INFINITY, k.new(0.0, -Float::INFINITY).sum)
+ assert_equal(-Float::INFINITY, k.new(-Float::INFINITY, 0.0).sum)
+ assert_predicate(k.new(-Float::INFINITY, Float::INFINITY).sum, :nan?)
assert_equal("abc", ["a", "b", "c"].each.sum(""))
assert_equal([1, [2], 3], [[1], [[2]], [3]].each.sum([]))
-
- assert_separately(%w[-rmathn], <<-EOS, ignore_stderr: true)
- assert_equal(6, [1r, 2, 3r].each.sum)
- EOS
end
def test_hash_sum
@@ -926,6 +1098,19 @@ class TestEnumerable < Test::Unit::TestCase
assert_int_equal(5, (2..0).sum(5))
assert_int_equal(2, (2..2).sum)
assert_int_equal(42, (2...2).sum(42))
+
+ not_a_range = Class.new do
+ include Enumerable # Defines the `#sum` method
+ def each
+ yield 2
+ yield 4
+ yield 6
+ end
+
+ def begin; end
+ def end; end
+ end
+ assert_equal(12, not_a_range.new.sum)
end
def test_uniq
@@ -941,5 +1126,21 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal([[1896, "Athens"], [1900, "Paris"], [1904, "Chicago"], [1908, "Rome"]],
olympics.uniq{|k,v| v})
assert_equal([1, 2, 3, 4, 5, 10], (1..100).uniq{|x| (x**2) % 10 }.first(6))
+ assert_equal([1, [1, 2]], Foo.new.to_enum.uniq)
+ end
+
+ def test_transient_heap_sort_by
+ klass = Class.new do
+ include Comparable
+ attr_reader :i
+ def initialize e
+ @i = e
+ end
+ def <=> other
+ GC.start
+ i <=> other.i
+ end
+ end
+ assert_equal [1, 2, 3, 4, 5], (1..5).sort_by{|e| klass.new e}
end
end
diff --git a/test/ruby/test_enumerator.rb b/test/ruby/test_enumerator.rb
index bd6b7fa669..efcfef580f 100644
--- a/test/ruby/test_enumerator.rb
+++ b/test/ruby/test_enumerator.rb
@@ -79,7 +79,7 @@ class TestEnumerator < Test::Unit::TestCase
enum = @obj.to_enum
assert_raise(NoMethodError) { enum.each {} }
enum.freeze
- assert_raise(RuntimeError) {
+ assert_raise(FrozenError) {
capture_io do
# warning: Enumerator.new without a block is deprecated; use Object#to_enum
enum.__send__(:initialize, @obj, :foo)
@@ -301,8 +301,11 @@ class TestEnumerator < Test::Unit::TestCase
yield
end
ary = []
- e = o.to_enum.each(ary)
- e.next
+ e = o.to_enum { 1 }
+ assert_equal(1, e.size)
+ e_arg = e.each(ary)
+ assert_equal(nil, e_arg.size)
+ e_arg.next
assert_equal([1], ary)
end
@@ -404,6 +407,12 @@ class TestEnumerator < Test::Unit::TestCase
e = (0..10).each_cons(2)
assert_equal("#<Enumerator: 0..10:each_cons(2)>", e.inspect)
+ e = (0..10).each_with_object({})
+ assert_equal("#<Enumerator: 0..10:each_with_object({})>", e.inspect)
+
+ e = (0..10).each_with_object(a: 1)
+ assert_equal("#<Enumerator: 0..10:each_with_object(a: 1)>", e.inspect)
+
e = Enumerator.new {|y| y.yield; 10 }
assert_match(/\A#<Enumerator: .*:each>/, e.inspect)
@@ -440,7 +449,7 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal([1, 2, 3], a)
g.freeze
- assert_raise(RuntimeError) {
+ assert_raise(FrozenError) {
g.__send__ :initialize, proc { |y| y << 4 << 5 }
}
@@ -476,6 +485,12 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal([1], y.yield(1))
assert_equal([1, 2], y.yield(2))
assert_equal([1, 2, 3], y.yield(3))
+ assert_equal([1, 2, 3, 4], y.yield(4, 5))
+
+ a = []
+ y = Enumerator::Yielder.new {|*x| a.concat(x) }
+ assert_equal([1], y.yield(1))
+ assert_equal([1, 2, 3], y.yield(2, 3))
assert_raise(LocalJumpError) { Enumerator::Yielder.new }
end
@@ -505,7 +520,7 @@ class TestEnumerator < Test::Unit::TestCase
def test_size_for_enum_created_from_array
arr = %w[hello world]
%i[each each_with_index reverse_each sort_by! sort_by map map!
- keep_if reject! reject select! select delete_if].each do |method|
+ keep_if reject! reject select! select filter! filter delete_if].each do |method|
assert_equal arr.size, arr.send(method).size
end
end
@@ -522,7 +537,7 @@ class TestEnumerator < Test::Unit::TestCase
def test_size_for_enum_created_from_hash
h = {a: 1, b: 2, c: 3}
- methods = %i[delete_if reject reject! select select! keep_if each each_key each_pair]
+ methods = %i[delete_if reject reject! select select! filter filter! keep_if each each_key each_pair]
enums = methods.map {|method| h.send(method)}
s = enums.group_by(&:size)
assert_equal([3], s.keys, ->{s.reject!{|k| k==3}.inspect})
@@ -532,7 +547,7 @@ class TestEnumerator < Test::Unit::TestCase
end
def test_size_for_enum_created_from_env
- %i[each_pair reject! delete_if select select! keep_if].each do |method|
+ %i[each_pair reject! delete_if select select! filter filter! keep_if].each do |method|
assert_equal ENV.size, ENV.send(method).size
end
end
@@ -576,13 +591,22 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal Float::INFINITY, [:foo].cycle.size
assert_equal 10, [:foo, :bar].cycle(5).size
assert_equal 0, [:foo, :bar].cycle(-10).size
+ assert_equal Float::INFINITY, {foo: 1}.cycle.size
+ assert_equal 10, {foo: 1, bar: 2}.cycle(5).size
+ assert_equal 0, {foo: 1, bar: 2}.cycle(-10).size
assert_equal 0, [].cycle.size
assert_equal 0, [].cycle(5).size
+ assert_equal 0, {}.cycle.size
+ assert_equal 0, {}.cycle(5).size
assert_equal nil, @obj.cycle.size
assert_equal nil, @obj.cycle(5).size
assert_equal Float::INFINITY, @sized.cycle.size
assert_equal 126, @sized.cycle(3).size
+ assert_equal Float::INFINITY, [].to_enum { 42 }.cycle.size
+ assert_equal 0, [].to_enum { 0 }.cycle.size
+
+ assert_raise(TypeError) {[].to_enum { 0 }.cycle("").size}
end
def test_size_for_loops
@@ -623,7 +647,7 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal 4, (1..10).step(3).size
assert_equal 3, (1...10).step(3).size
assert_equal Float::INFINITY, (42..Float::INFINITY).step(2).size
- assert_raise(ArgumentError){ (1..10).step(-2).size }
+ assert_equal 0, (1..10).step(-2).size
end
def test_size_for_downup_to
@@ -648,8 +672,123 @@ class TestEnumerator < Test::Unit::TestCase
end
def test_uniq
- assert_equal([1, 2, 3, 4, 5, 10],
- (1..Float::INFINITY).lazy.uniq{|x| (x**2) % 10 }.first(6))
+ u = [0, 1, 0, 1].to_enum.lazy.uniq
+ assert_equal([0, 1], u.force)
+ assert_equal([0, 1], u.force)
end
-end
+ def test_enum_chain_and_plus
+ r = 1..5
+
+ e1 = r.chain()
+ assert_kind_of(Enumerator::Chain, e1)
+ assert_equal(5, e1.size)
+ ary = []
+ e1.each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5], ary)
+
+ e2 = r.chain([6, 7, 8])
+ assert_kind_of(Enumerator::Chain, e2)
+ assert_equal(8, e2.size)
+ ary = []
+ e2.each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5, 6, 7, 8], ary)
+
+ e3 = r.chain([6, 7], 8.step)
+ assert_kind_of(Enumerator::Chain, e3)
+ assert_equal(Float::INFINITY, e3.size)
+ ary = []
+ e3.take(10).each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ary)
+
+ # `a + b + c` should not return `Enumerator::Chain.new(a, b, c)`
+ # because it is expected that `(a + b).each` be called.
+ e4 = e2.dup
+ class << e4
+ attr_reader :each_is_called
+ def each
+ super
+ @each_is_called = true
+ end
+ end
+ e5 = e4 + 9.step
+ assert_kind_of(Enumerator::Chain, e5)
+ assert_equal(Float::INFINITY, e5.size)
+ ary = []
+ e5.take(10).each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ary)
+ assert_equal(true, e4.each_is_called)
+ end
+
+ def test_chained_enums
+ a = (1..5).each
+
+ e0 = Enumerator::Chain.new()
+ assert_kind_of(Enumerator::Chain, e0)
+ assert_equal(0, e0.size)
+ ary = []
+ e0.each { |x| ary << x }
+ assert_equal([], ary)
+
+ e1 = Enumerator::Chain.new(a)
+ assert_kind_of(Enumerator::Chain, e1)
+ assert_equal(5, e1.size)
+ ary = []
+ e1.each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5], ary)
+
+ e2 = Enumerator::Chain.new(a, [6, 7, 8])
+ assert_kind_of(Enumerator::Chain, e2)
+ assert_equal(8, e2.size)
+ ary = []
+ e2.each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5, 6, 7, 8], ary)
+
+ e3 = Enumerator::Chain.new(a, [6, 7], 8.step)
+ assert_kind_of(Enumerator::Chain, e3)
+ assert_equal(Float::INFINITY, e3.size)
+ ary = []
+ e3.take(10).each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ary)
+
+ e4 = Enumerator::Chain.new(a, Enumerator.new { |y| y << 6 << 7 << 8 })
+ assert_kind_of(Enumerator::Chain, e4)
+ assert_equal(nil, e4.size)
+ ary = []
+ e4.each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5, 6, 7, 8], ary)
+
+ e5 = Enumerator::Chain.new(e1, e2)
+ assert_kind_of(Enumerator::Chain, e5)
+ assert_equal(13, e5.size)
+ ary = []
+ e5.each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 7, 8], ary)
+
+ rewound = []
+ e1.define_singleton_method(:rewind) { rewound << object_id }
+ e2.define_singleton_method(:rewind) { rewound << object_id }
+ e5.rewind
+ assert_equal(rewound, [e2.object_id, e1.object_id])
+
+ rewound = []
+ a = [1]
+ e6 = Enumerator::Chain.new(a)
+ a.define_singleton_method(:rewind) { rewound << object_id }
+ e6.rewind
+ assert_equal(rewound, [])
+
+ assert_equal(
+ '#<Enumerator::Chain: [' +
+ '#<Enumerator::Chain: [' +
+ '#<Enumerator: 1..5:each>' +
+ ']>, ' +
+ '#<Enumerator::Chain: [' +
+ '#<Enumerator: 1..5:each>, ' +
+ '[6, 7, 8]' +
+ ']>' +
+ ']>',
+ e5.inspect
+ )
+ end
+end
diff --git a/test/ruby/test_env.rb b/test/ruby/test_env.rb
index 29c058339f..e9fb2524f6 100644
--- a/test/ruby/test_env.rb
+++ b/test/ruby/test_env.rb
@@ -122,12 +122,16 @@ class TestEnv < Test::Unit::TestCase
assert_equal("foo", ENV.fetch("test"))
ENV.delete("test")
feature8649 = '[ruby-core:56062] [Feature #8649]'
- assert_raise_with_message(KeyError, 'key not found: "test"', feature8649) do
+ e = assert_raise_with_message(KeyError, 'key not found: "test"', feature8649) do
ENV.fetch("test")
end
+ assert_same(ENV, e.receiver)
+ assert_equal("test", e.key)
assert_equal("foo", ENV.fetch("test", "foo"))
assert_equal("bar", ENV.fetch("test") { "bar" })
- assert_equal("bar", ENV.fetch("test", "foo") { "bar" })
+ EnvUtil.suppress_warning do
+ assert_equal("bar", ENV.fetch("test", "foo") { "bar" })
+ end
assert_invalid_env {|v| ENV.fetch(v)}
assert_nothing_raised { ENV.fetch(PATH_ENV, "foo") }
ENV[PATH_ENV] = ""
@@ -218,6 +222,18 @@ class TestEnv < Test::Unit::TestCase
assert_nil(ENV.select! {|k, v| IGNORE_CASE ? k.upcase != "TEST" : k != "test" })
end
+ def test_filter_bang
+ h1 = {}
+ ENV.each_pair {|k, v| h1[k] = v }
+ ENV["test"] = "foo"
+ ENV.filter! {|k, v| IGNORE_CASE ? k.upcase != "TEST" : k != "test" }
+ h2 = {}
+ ENV.each_pair {|k, v| h2[k] = v }
+ assert_equal(h1, h2)
+
+ assert_nil(ENV.filter! {|k, v| IGNORE_CASE ? k.upcase != "TEST" : k != "test" })
+ end
+
def test_keep_if
h1 = {}
ENV.each_pair {|k, v| h1[k] = v }
@@ -250,6 +266,32 @@ class TestEnv < Test::Unit::TestCase
end
end
+ def test_filter
+ ENV["test"] = "foo"
+ h = ENV.filter {|k| IGNORE_CASE ? k.upcase == "TEST" : k == "test" }
+ assert_equal(1, h.size)
+ k = h.keys.first
+ v = h.values.first
+ if IGNORE_CASE
+ assert_equal("TEST", k.upcase)
+ assert_equal("FOO", v.upcase)
+ else
+ assert_equal("test", k)
+ assert_equal("foo", v)
+ end
+ end
+
+ def test_slice
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ ENV["bar"] = "rab"
+ assert_equal({}, ENV.slice())
+ assert_equal({}, ENV.slice(""))
+ assert_equal({}, ENV.slice("unknown"))
+ assert_equal({"foo"=>"bar", "baz"=>"qux"}, ENV.slice("foo", "baz"))
+ end
+
def test_clear
ENV.clear
assert_equal(0, ENV.size)
@@ -357,6 +399,8 @@ class TestEnv < Test::Unit::TestCase
def test_to_h
assert_equal(ENV.to_hash, ENV.to_h)
+ assert_equal(ENV.map {|k, v| ["$#{k}", v.size]}.to_h,
+ ENV.to_h {|k, v| ["$#{k}", v.size]})
end
def test_reject
@@ -396,6 +440,8 @@ class TestEnv < Test::Unit::TestCase
ENV["foo"] = "xxx"
ENV.replace({"foo"=>"bar", "baz"=>"qux"})
check(ENV.to_hash.to_a, [%w(foo bar), %w(baz qux)])
+ ENV.replace({"Foo"=>"Bar", "Baz"=>"Qux"})
+ check(ENV.to_hash.to_a, [%w(Foo Bar), %w(Baz Qux)])
end
def test_update
@@ -415,7 +461,7 @@ class TestEnv < Test::Unit::TestCase
def test_huge_value
huge_value = "bar" * 40960
ENV["foo"] = "bar"
- if /mswin|mingw/ =~ RUBY_PLATFORM
+ if /mswin/ =~ RUBY_PLATFORM
assert_raise(Errno::EINVAL) { ENV["foo"] = huge_value }
assert_equal("bar", ENV["foo"])
else
@@ -425,18 +471,28 @@ class TestEnv < Test::Unit::TestCase
end
if /mswin|mingw/ =~ RUBY_PLATFORM
+ def windows_version
+ @windows_version ||= %x[ver][/Version (\d+)/, 1].to_i
+ end
+
def test_win32_blocksize
keys = []
- len = 32767 - ENV.to_a.flatten.inject(0) {|r,e| r + e.bytesize + 1}
+ len = 32767 - ENV.to_a.flatten.inject(1) {|r,e| r + e.bytesize + 1}
val = "bar" * 1000
key = nil
while (len -= val.size + (key="foo#{len}").size + 2) > 0
keys << key
ENV[key] = val
end
- 1.upto(12) {|i|
- assert_raise(Errno::EINVAL) { ENV[key] = val }
- }
+ if windows_version < 6
+ 1.upto(12) {|i|
+ assert_raise(Errno::EINVAL) { ENV[key] = val }
+ }
+ else
+ 1.upto(12) {|i|
+ assert_nothing_raised(Errno::EINVAL) { ENV[key] = val }
+ }
+ end
ensure
keys.each {|k| ENV.delete(k)}
end
diff --git a/test/ruby/test_eval.rb b/test/ruby/test_eval.rb
index dba54773cb..9cb69ddc37 100644
--- a/test/ruby/test_eval.rb
+++ b/test/ruby/test_eval.rb
@@ -320,20 +320,6 @@ class TestEval < Test::Unit::TestCase
end
assert(!bad)
- if false
- # Ruby 2.0 doesn't see Proc as Binding
- x = proc{}
- eval "i4 = 1", x
- assert_equal(1, eval("i4", x))
- x = proc{proc{}}.call
- eval "i4 = 22", x
- assert_equal(22, eval("i4", x))
- t = []
- x = proc{proc{}}.call
- eval "(0..9).each{|i5| t[i5] = proc{i5*2}}", x
- assert_equal(8, t[4].call)
- end
-
x = binding
eval "i = 1", x
assert_equal(1, eval("i", x))
@@ -360,27 +346,6 @@ class TestEval < Test::Unit::TestCase
assert_equal(eval("foo22"), eval("foo22", p))
assert_equal(55, eval("foo22"))
}.call
-
- if false
- # Ruby 2.0 doesn't see Proc as Binding
- p1 = proc{i7 = 0; proc{i7}}.call
- assert_equal(0, p1.call)
- eval "i7=5", p1
- assert_equal(5, p1.call)
- assert(!defined?(i7))
- end
-
- if false
- # Ruby 2.0 doesn't see Proc as Binding
- p1 = proc{i7 = 0; proc{i7}}.call
- i7 = nil
- assert_equal(0, p1.call)
- eval "i7=1", p1
- assert_equal(1, p1.call)
- eval "i7=5", p1
- assert_equal(5, p1.call)
- assert_nil(i7)
- end
end
def test_nil_instance_eval_cvar
@@ -503,6 +468,20 @@ class TestEval < Test::Unit::TestCase
assert_same a, b
end
+ def test_eval_location_binding
+ assert_warning(/__FILE__ in eval/) do
+ assert_equal(__FILE__, eval("__FILE__", binding))
+ end
+ end
+
+ def test_fstring_instance_eval
+ bug = "[ruby-core:78116] [Bug #12930]".freeze
+ assert_same bug, (bug.instance_eval {self})
+ assert_raise(FrozenError) {
+ bug.instance_eval {@ivar = true}
+ }
+ end
+
def test_gced_binding_block
assert_normal_exit %q{
def m
@@ -517,4 +496,33 @@ class TestEval < Test::Unit::TestCase
b.eval('yield')
}, '[Bug #10368]'
end
+
+ def test_gced_eval_location
+ Dir.mktmpdir do |d|
+ File.write("#{d}/2.rb", "")
+ File.write("#{d}/1.rb", "require_relative '2'\n""__FILE__\n")
+ file = "1.rb"
+ path = File.expand_path(file, d)
+ assert_equal(path, eval(File.read(path), nil, File.expand_path(file, d)))
+ assert_equal(path, eval(File.read(path), nil, File.expand_path(file, d)))
+ end
+ end
+
+ def orphan_proc
+ proc {eval("return :ng")}
+ end
+
+ def orphan_lambda
+ lambda {eval("return :ok")}
+ end
+
+ def test_return_in_eval_proc
+ x = orphan_proc
+ assert_raise(LocalJumpError) {x.call}
+ end
+
+ def test_return_in_eval_lambda
+ x = orphan_lambda
+ assert_equal(:ok, x.call)
+ end
end
diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb
index 91d3ac1e93..de249c9b7c 100644
--- a/test/ruby/test_exception.rb
+++ b/test/ruby/test_exception.rb
@@ -183,12 +183,12 @@ class TestException < Test::Unit::TestCase
def test_throw_false
bug12743 = '[ruby-core:77229] [Bug #12743]'
- e = assert_raise_with_message(UncaughtThrowError, /false/, bug12743) {
- Thread.start {
+ Thread.start {
+ e = assert_raise_with_message(UncaughtThrowError, /false/, bug12743) {
throw false
- }.join
- }
- assert_same(false, e.tag, bug12743)
+ }
+ assert_same(false, e.tag, bug12743)
+ }.join
end
def test_else_no_exception
@@ -352,8 +352,10 @@ class TestException < Test::Unit::TestCase
end
def test_thread_signal_location
+ skip
_, stderr, _ = EnvUtil.invoke_ruby(%w"--disable-gems -d", <<-RUBY, false, true)
Thread.start do
+ Thread.current.report_on_exception = false
begin
Process.kill(:INT, $$)
ensure
@@ -604,6 +606,30 @@ end.join
rescue SystemStackError
end
+ def test_machine_stackoverflow_by_trace
+ assert_normal_exit("#{<<-"begin;"}\n#{<<~"end;"}", timeout: 60)
+ begin;
+ require 'timeout'
+ require 'tracer'
+ class HogeError < StandardError
+ def to_s
+ message.upcase # disable tailcall optimization
+ end
+ end
+ Tracer.stdout = open(IO::NULL, "w")
+ begin
+ Timeout.timeout(5) do
+ Tracer.on
+ HogeError.new.to_s
+ end
+ rescue Timeout::Error
+ # ok. there are no SEGV or critical error
+ rescue SystemStackError => e
+ # ok.
+ end
+ end;
+ end
+
def test_cause
msg = "[Feature #8257]"
cause = nil
@@ -673,6 +699,16 @@ end.join
assert_same(a, e.cause.cause)
end
+ def test_cause_at_end
+ errs = [
+ /-: unexpected return\n/,
+ /.*undefined local variable or method `n'.*\n/,
+ ]
+ assert_in_out_err([], <<-'end;', [], errs)
+ END{n}; END{return}
+ end;
+ end
+
def test_raise_with_cause
msg = "[Feature #8257]"
cause = ArgumentError.new("foobar")
@@ -727,6 +763,8 @@ end.join
end
end
assert_nil(e.cause)
+ ensure
+ y.join
end
def test_cause_thread_with_cause
@@ -770,13 +808,60 @@ end.join
e = assert_raise(exc, bug) {raise exc, "foo" => "bar", foo: "bar"}
assert_equal({"foo" => "bar", foo: "bar"}, e.arg, bug)
- e = assert_raise(exc, bug) {raise exc, "foo" => "bar", foo: "bar", cause: "zzz"}
+ e = assert_raise(exc, bug) {raise exc, "foo" => "bar", foo: "bar", cause: RuntimeError.new("zzz")}
assert_equal({"foo" => "bar", foo: "bar"}, e.arg, bug)
e = assert_raise(exc, bug) {raise exc, {}}
assert_equal({}, e.arg, bug)
end
+ def test_circular_cause
+ bug13043 = '[ruby-core:78688] [Bug #13043]'
+ begin
+ begin
+ raise "error 1"
+ ensure
+ orig_error = $!
+ begin
+ raise "error 2"
+ rescue => err
+ raise orig_error
+ end
+ end
+ rescue => x
+ end
+ assert_equal(orig_error, x)
+ assert_equal(orig_error, err.cause)
+ assert_nil(orig_error.cause, bug13043)
+ end
+
+ def test_cause_with_frozen_exception
+ exc = ArgumentError.new("foo").freeze
+ assert_raise_with_message(ArgumentError, exc.message) {
+ raise exc, cause: RuntimeError.new("bar")
+ }
+ end
+
+ def test_cause_exception_in_cause_message
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}") do |outs, errs, status|
+ begin;
+ exc = Class.new(StandardError) do
+ def initialize(obj, cnt)
+ super(obj)
+ @errcnt = cnt
+ end
+ def to_s
+ return super if @errcnt <= 0
+ @errcnt -= 1
+ raise "xxx"
+ end
+ end.new("ok", 10)
+ raise "[Bug #17033]", cause: exc
+ end;
+ assert_equal(1, errs.count {|m| m.include?("[Bug #17033]")}, proc {errs.pretty_inspect})
+ end
+ end
+
def test_anonymous_message
assert_in_out_err([], "raise Class.new(RuntimeError), 'foo'", [], /foo\n/)
end
@@ -788,6 +873,113 @@ end.join
alias inspect pretty_inspect
end
+ def test_name_error_new_default
+ error = NameError.new
+ assert_equal("NameError", error.message)
+ end
+
+ def test_name_error_new_message
+ error = NameError.new("Message")
+ assert_equal("Message", error.message)
+ end
+
+ def test_name_error_new_name
+ error = NameError.new("Message")
+ assert_nil(error.name)
+
+ error = NameError.new("Message", :foo)
+ assert_equal(:foo, error.name)
+ end
+
+ def test_name_error_new_receiver
+ receiver = Object.new
+
+ error = NameError.new
+ assert_raise(ArgumentError) {error.receiver}
+ assert_equal("NameError", error.message)
+
+ error = NameError.new(receiver: receiver)
+ assert_equal(["NameError", receiver],
+ [error.message, error.receiver])
+
+ error = NameError.new("Message", :foo, receiver: receiver)
+ assert_equal(["Message", receiver, :foo],
+ [error.message, error.receiver, error.name])
+ end
+
+ def test_nomethod_error_new_default
+ error = NoMethodError.new
+ assert_equal("NoMethodError", error.message)
+ end
+
+ def test_nomethod_error_new_message
+ error = NoMethodError.new("Message")
+ assert_equal("Message", error.message)
+ end
+
+ def test_nomethod_error_new_name
+ error = NoMethodError.new("Message")
+ assert_nil(error.name)
+
+ error = NoMethodError.new("Message", :foo)
+ assert_equal(:foo, error.name)
+ end
+
+ def test_nomethod_error_new_name_args
+ error = NoMethodError.new("Message", :foo)
+ assert_nil(error.args)
+
+ error = NoMethodError.new("Message", :foo, [1, 2])
+ assert_equal([:foo, [1, 2]], [error.name, error.args])
+ end
+
+ def test_nomethod_error_new_name_args_priv
+ error = NoMethodError.new("Message", :foo, [1, 2])
+ assert_not_predicate(error, :private_call?)
+
+ error = NoMethodError.new("Message", :foo, [1, 2], true)
+ assert_equal([:foo, [1, 2], true],
+ [error.name, error.args, error.private_call?])
+ end
+
+ def test_nomethod_error_new_receiver
+ receiver = Object.new
+
+ error = NoMethodError.new
+ assert_raise(ArgumentError) {error.receiver}
+
+ error = NoMethodError.new(receiver: receiver)
+ assert_equal(receiver, error.receiver)
+
+ error = NoMethodError.new("Message")
+ assert_raise(ArgumentError) {error.receiver}
+
+ error = NoMethodError.new("Message", receiver: receiver)
+ assert_equal(["Message", receiver],
+ [error.message, error.receiver])
+
+ error = NoMethodError.new("Message", :foo)
+ assert_raise(ArgumentError) {error.receiver}
+
+ error = NoMethodError.new("Message", :foo, receiver: receiver)
+ assert_equal(["Message", :foo, receiver],
+ [error.message, error.name, error.receiver])
+
+ error = NoMethodError.new("Message", :foo, [1, 2])
+ assert_raise(ArgumentError) {error.receiver}
+
+ error = NoMethodError.new("Message", :foo, [1, 2], receiver: receiver)
+ assert_equal(["Message", :foo, [1, 2], receiver],
+ [error.message, error.name, error.args, error.receiver])
+
+ error = NoMethodError.new("Message", :foo, [1, 2], true)
+ assert_raise(ArgumentError) {error.receiver}
+
+ error = NoMethodError.new("Message", :foo, [1, 2], true, receiver: receiver)
+ assert_equal([:foo, [1, 2], true, receiver],
+ [error.name, error.args, error.private_call?, error.receiver])
+ end
+
def test_name_error_info_const
obj = PrettyObject.new
@@ -914,23 +1106,23 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end
end
- def test_warning_warn
+ def capture_warning_warn
verbose = $VERBOSE
- warning = nil
+ warning = []
::Warning.class_eval do
alias_method :warn2, :warn
remove_method :warn
define_method(:warn) do |str|
- warning = str
+ warning << str
end
end
$VERBOSE = true
- a = @a
+ yield
- assert_match(/instance variable @a not initialized/, warning)
+ return warning
ensure
$VERBOSE = verbose
@@ -941,6 +1133,27 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end
end
+ def test_warning_warn
+ warning = capture_warning_warn {@a}
+ assert_match(/instance variable @a not initialized/, warning[0])
+
+ assert_equal(["a\nz\n"], capture_warning_warn {warn "a\n", "z"})
+ assert_equal([], capture_warning_warn {warn})
+ assert_equal(["\n"], capture_warning_warn {warn ""})
+ end
+
+ def test_kernel_warn_uplevel
+ warning = capture_warning_warn {warn("test warning", uplevel: 0)}
+ assert_equal("#{__FILE__}:#{__LINE__-1}: warning: test warning\n", warning[0])
+ def (obj = Object.new).w(n) warn("test warning", uplevel: n) end
+ warning = capture_warning_warn {obj.w(0)}
+ assert_equal("#{__FILE__}:#{__LINE__-2}: warning: test warning\n", warning[0])
+ warning = capture_warning_warn {obj.w(1)}
+ assert_equal("#{__FILE__}:#{__LINE__-1}: warning: test warning\n", warning[0])
+ assert_raise(ArgumentError) {warn("test warning", uplevel: -1)}
+ assert_in_out_err(["-e", "warn 'ok', uplevel: 1"], '', [], /warning:/)
+ end
+
def test_warning_warn_invalid_argument
assert_raise(TypeError) do
::Warning.warn nil
@@ -952,4 +1165,236 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
::Warning.warn "\x00a\x00b\x00c".force_encoding("utf-16be")
end
end
+
+ def test_warning_warn_circular_require_backtrace
+ warning = nil
+ path = nil
+ Tempfile.create(%w[circular .rb]) do |t|
+ path = File.realpath(t.path)
+ basename = File.basename(path)
+ t.puts "require '#{basename}'"
+ t.close
+ $LOAD_PATH.push(File.dirname(t))
+ warning = capture_warning_warn {require basename}
+ ensure
+ $LOAD_PATH.pop
+ $LOADED_FEATURES.delete(t)
+ end
+ assert_equal(1, warning.size)
+ assert_match(/circular require/, warning.first)
+ assert_match(/^\tfrom #{Regexp.escape(path)}:1:/, warning.first)
+ end
+
+ def test_warning_warn_super
+ assert_in_out_err(%[-W0], "#{<<~"{#"}\n#{<<~'};'}", [], /instance variable @a not initialized/)
+ {#
+ module Warning
+ def warn(message)
+ super
+ end
+ end
+
+ $VERBOSE = true
+ @a
+ };
+ end
+
+ def test_undefined_backtrace
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ class Exception
+ undef backtrace
+ end
+
+ assert_raise(RuntimeError) {
+ raise RuntimeError, "hello"
+ }
+ end;
+ end
+
+ def test_redefined_backtrace
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ $exc = nil
+
+ class Exception
+ undef backtrace
+ def backtrace
+ $exc = self
+ end
+ end
+
+ e = assert_raise(RuntimeError) {
+ raise RuntimeError, "hello"
+ }
+ assert_same(e, $exc)
+ end;
+ end
+
+ def test_blocking_backtrace
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class Bug < RuntimeError
+ def backtrace
+ IO.readlines(IO::NULL)
+ end
+ end
+ bug = Bug.new '[ruby-core:85939] [Bug #14577]'
+ n = 10000
+ i = 0
+ n.times do
+ begin
+ raise bug
+ rescue Bug
+ i += 1
+ end
+ end
+ assert_equal(n, i)
+ end;
+ end
+
+ def test_wrong_backtrace
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ class Exception
+ undef backtrace
+ def backtrace(a)
+ end
+ end
+
+ assert_raise(RuntimeError) {
+ raise RuntimeError, "hello"
+ }
+ end;
+
+ error_class = Class.new(StandardError) do
+ def backtrace; :backtrace; end
+ end
+ begin
+ raise error_class
+ rescue error_class => e
+ assert_raise(TypeError) {$@}
+ assert_raise(TypeError) {e.full_message}
+ end
+ end
+
+ def test_backtrace_in_eval
+ bug = '[ruby-core:84434] [Bug #14229]'
+ assert_in_out_err(['-e', 'eval("raise")'], "", [], /^\(eval\):1:/, bug)
+ end
+
+ def test_full_message
+ message = RuntimeError.new("testerror").full_message
+ assert_operator(message, :end_with?, "\n")
+
+ test_method = "def foo; raise 'testerror'; end"
+
+ out1, err1, status1 = EnvUtil.invoke_ruby(['-e', "#{test_method}; begin; foo; rescue => e; puts e.full_message; end"], '', true, true)
+ assert_predicate(status1, :success?)
+ assert_empty(err1, "expected nothing wrote to $stdout by #full_message")
+
+ _, err2, status1 = EnvUtil.invoke_ruby(['-e', "#{test_method}; begin; foo; end"], '', true, true)
+ assert_equal(err2, out1)
+
+ e = RuntimeError.new("a\n")
+ message = assert_nothing_raised(ArgumentError, proc {e.pretty_inspect}) do
+ e.full_message
+ end
+ assert_operator(message, :end_with?, "\n")
+ message = message.gsub(/\e\[[\d;]*m/, '')
+ assert_not_operator(message, :end_with?, "\n\n")
+ e = RuntimeError.new("a\n\nb\n\nc")
+ message = assert_nothing_raised(ArgumentError, proc {e.pretty_inspect}) do
+ e.full_message
+ end
+ assert_all?(message.lines) do |m|
+ /\e\[\d[;\d]*m[^\e]*\n/ !~ m
+ end
+
+ e = RuntimeError.new("testerror")
+ message = e.full_message(highlight: false)
+ assert_not_match(/\e/, message)
+
+ bt = ["test:100", "test:99", "test:98", "test:1"]
+ e = assert_raise(RuntimeError) {raise RuntimeError, "testerror", bt}
+
+ bottom = "test:100: testerror (RuntimeError)\n"
+ top = "test:1\n"
+ remark = "Traceback (most recent call last):"
+
+ message = e.full_message(highlight: false, order: :top)
+ assert_not_match(/\e/, message)
+ assert_operator(message.count("\n"), :>, 2)
+ assert_operator(message, :start_with?, bottom)
+ assert_operator(message, :end_with?, top)
+
+ message = e.full_message(highlight: false, order: :bottom)
+ assert_not_match(/\e/, message)
+ assert_operator(message.count("\n"), :>, 2)
+ assert_operator(message, :start_with?, remark)
+ assert_operator(message, :end_with?, bottom)
+
+ assert_raise_with_message(ArgumentError, /:top or :bottom/) {
+ e.full_message(highlight: false, order: :middle)
+ }
+
+ message = e.full_message(highlight: true)
+ assert_match(/\e/, message)
+
+ message = e.full_message
+ if Exception.to_tty?
+ assert_match(/\e/, message)
+ message = message.gsub(/\e\[[\d;]*m/, '')
+ assert_operator(message, :start_with?, remark)
+ assert_operator(message, :end_with?, bottom)
+ else
+ assert_not_match(/\e/, message)
+ assert_operator(message, :start_with?, bottom)
+ assert_operator(message, :end_with?, top)
+ end
+ end
+
+ def test_exception_in_message
+ code = "#{<<~"begin;"}\n#{<<~'end;'}"
+ begin;
+ class Bug14566 < StandardError
+ def message; raise self.class; end
+ end
+ raise Bug14566
+ end;
+ assert_in_out_err([], code, [], /Bug14566/, success: false, timeout: 2)
+ end
+
+ def test_non_exception_cause
+ assert_raise_with_message(TypeError, /exception/) do
+ raise "foo", cause: 1
+ end;
+ end
+
+ def test_circular_cause_handle
+ assert_raise_with_message(ArgumentError, /circular cause/) do
+ begin
+ raise "error 1"
+ rescue => e1
+ raise "error 2" rescue raise e1, cause: $!
+ end
+ end;
+ end
+
+ def test_super_in_method_missing
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ $VERBOSE = nil
+ class Object
+ def method_missing(name, *args, &block)
+ super
+ end
+ end
+
+ bug14670 = '[ruby-dev:50522] [Bug #14670]'
+ assert_raise_with_message(NoMethodError, /`foo'/, bug14670) do
+ Object.new.foo
+ end
+ end;
+ end
end
diff --git a/test/ruby/test_extlibs.rb b/test/ruby/test_extlibs.rb
deleted file mode 100644
index 9b82b4f8cd..0000000000
--- a/test/ruby/test_extlibs.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-# frozen_string_literal: false
-require "envutil"
-require "shellwords"
-
-class TestExtLibs < Test::Unit::TestCase
- @extdir = $".grep(/\/rbconfig\.rb\z/) {break "#$`/ext"}
-
- def self.check_existence(ext, add_msg = nil)
- return if @excluded.any? {|i| File.fnmatch?(i, ext, File::FNM_CASEFOLD)}
- add_msg = ". #{add_msg}" if add_msg
- log = "#{@extdir}/#{ext}/mkmf.log"
- define_method("test_existence_of_#{ext}") do
- assert_separately([], <<-"end;", ignore_stderr: true) # do
- log = #{log.dump}
- msg = proc {
- "extension library `#{ext}' is not found#{add_msg}\n" <<
- (File.exist?(log) ? File.binread(log) : "\#{log} not found")
- }
- assert_nothing_raised(msg) do
- require "#{ext}"
- end
- end;
- end
- end
-
- def windows?
- /mswin|mingw/ =~ RUBY_PLATFORM
- end
-
- excluded = [RbConfig::CONFIG, ENV].map do |conf|
- if args = conf['configure_args']
- args.shellsplit.grep(/\A--without-ext=/) {$'.split(/,/)}
- end
- end.flatten.compact
- excluded << '+' if excluded.empty?
- if windows?
- excluded.map! {|i| i == '+' ? ['pty', 'syslog'] : i}
- excluded.flatten!
- else
- excluded.map! {|i| i == '+' ? '*win32*' : i}
- end
- @excluded = excluded
-
- check_existence "bigdecimal"
- check_existence "continuation"
- check_existence "coverage"
- check_existence "date"
- #check_existence "dbm" # depend on libdbm
- check_existence "digest"
- check_existence "digest/bubblebabble"
- check_existence "digest/md5"
- check_existence "digest/rmd160"
- check_existence "digest/sha1"
- check_existence "digest/sha2"
- check_existence "etc"
- check_existence "fcntl"
- check_existence "fiber"
- check_existence "fiddle"
- #check_existence "gdbm" # depend on libgdbm
- check_existence "io/console"
- check_existence "io/nonblock"
- check_existence "io/wait"
- check_existence "json"
- check_existence "mathn/complex"
- check_existence "mathn/rational"
- check_existence "nkf"
- check_existence "objspace"
- check_existence "openssl", "this may be false positive, but should assert because rubygems requires this"
- check_existence "pathname"
- check_existence "psych"
- check_existence "pty"
- check_existence "racc/cparse"
- check_existence "rbconfig/sizeof"
- #check_existence "readline" # depend on libreadline
- check_existence "ripper"
- check_existence "sdbm"
- check_existence "socket"
- check_existence "stringio"
- check_existence "strscan"
- check_existence "syslog"
- check_existence "thread"
- check_existence "Win32API"
- check_existence "win32ole"
- check_existence "zlib", "this may be false positive, but should assert because rubygems requires this"
-end
diff --git a/test/ruby/test_fiber.rb b/test/ruby/test_fiber.rb
index d1d15828fc..e6b89ffa45 100644
--- a/test/ruby/test_fiber.rb
+++ b/test/ruby/test_fiber.rb
@@ -70,10 +70,12 @@ class TestFiber < Test::Unit::TestCase
assert_raise(ArgumentError){
Fiber.new # Fiber without block
}
- assert_raise(FiberError){
- f = Fiber.new{}
- Thread.new{f.resume}.join # Fiber yielding across thread
- }
+ f = Fiber.new{}
+ Thread.new{
+ assert_raise(FiberError){ # Fiber yielding across thread
+ f.resume
+ }
+ }.join
assert_raise(FiberError){
f = Fiber.new{}
f.resume
@@ -199,11 +201,11 @@ class TestFiber < Test::Unit::TestCase
end
def test_resume_root_fiber
- assert_raise(FiberError) do
- Thread.new do
+ Thread.new do
+ assert_raise(FiberError) do
Fiber.current.resume
- end.join
- end
+ end
+ end.join
end
def test_gc_root_fiber
@@ -217,13 +219,24 @@ class TestFiber < Test::Unit::TestCase
}, bug4612
end
+ def test_mark_fiber
+ bug13875 = '[ruby-core:82681]'
+
+ assert_normal_exit %q{
+ GC.stress = true
+ up = 1.upto(10)
+ down = 10.downto(1)
+ up.zip(down) {|a, b| a + b == 11 or fail 'oops'}
+ }, bug13875
+ end
+
def test_no_valid_cfp
bug5083 = '[ruby-dev:44208]'
assert_equal([], Fiber.new(&Module.method(:nesting)).resume, bug5083)
assert_instance_of(Class, Fiber.new(&Class.new.method(:undef_method)).resume(:to_s), bug5083)
end
- def test_prohibit_resume_transfered_fiber
+ def test_prohibit_resume_transferred_fiber
assert_raise(FiberError){
root_fiber = Fiber.current
f = Fiber.new{
@@ -247,16 +260,29 @@ class TestFiber < Test::Unit::TestCase
end
def test_fork_from_fiber
- begin
- pid = Process.fork{}
- rescue NotImplementedError
- return
- else
- Process.wait(pid)
- end
+ skip 'fork not supported' unless Process.respond_to?(:fork)
+ pid = nil
bug5700 = '[ruby-core:41456]'
assert_nothing_raised(bug5700) do
- Fiber.new{ pid = fork {} }.resume
+ Fiber.new do
+ pid = fork do
+ xpid = nil
+ Fiber.new {
+ xpid = fork do
+ # enough to trigger GC on old root fiber
+ count = 10000
+ count = 1000 if /openbsd/i =~ RUBY_PLATFORM
+ count.times do
+ Fiber.new {}.transfer
+ Fiber.new { Fiber.yield }
+ end
+ exit!(0)
+ end
+ }.transfer
+ _, status = Process.waitpid2(xpid)
+ exit!(status.success?)
+ end
+ end.resume
end
pid, status = Process.waitpid2(pid)
assert_equal(0, status.exitstatus, bug5700)
@@ -344,5 +370,44 @@ class TestFiber < Test::Unit::TestCase
assert_equal("inner", s2)
assert_equal(s1, $_, bug7678)
end
-end
+ def test_new_symbol_proc
+ bug = '[ruby-core:80147] [Bug #13313]'
+ assert_ruby_status([], "#{<<-"begin;"}\n#{<<-'end;'}", bug)
+ begin;
+ exit("1" == Fiber.new(&:to_s).resume(1))
+ end;
+ end
+
+ def test_to_s
+ f = Fiber.new do
+ assert_match(/resumed/, f.to_s)
+ Fiber.yield
+ end
+ assert_match(/created/, f.to_s)
+ f.resume
+ assert_match(/suspended/, f.to_s)
+ f.resume
+ assert_match(/terminated/, f.to_s)
+ assert_match(/resumed/, Fiber.current.to_s)
+ end
+
+ def test_create_fiber_in_new_thread
+ ret = Thread.new{
+ Thread.new{
+ Fiber.new{Fiber.yield :ok}.resume
+ }.value
+ }.value
+ assert_equal :ok, ret, '[Bug #14642]'
+ end
+
+ def test_machine_stack_gc
+ assert_normal_exit <<-RUBY, '[Bug #14561]', timeout: 10
+ enum = Enumerator.new { |y| y << 1 }
+ thread = Thread.new { enum.peek }
+ thread.join
+ sleep 5 # pause until thread cache wait time runs out. Native thread exits.
+ GC.start
+ RUBY
+ end
+end
diff --git a/test/ruby/test_file.rb b/test/ruby/test_file.rb
index cdbc19c0d4..3deab76e93 100644
--- a/test/ruby/test_file.rb
+++ b/test/ruby/test_file.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: false
require 'test/unit'
require 'tempfile'
-require "thread"
require "-test-/file"
require_relative 'ut_eof'
@@ -88,7 +87,7 @@ class TestFile < Test::Unit::TestCase
end
def test_bom_32le
- assert_bom(["\xFF\xFE\0", "\0"], __method__)
+ assert_bom(["\xFF", "\xFE\0\0"], __method__)
end
def test_truncate_wbuf
@@ -271,7 +270,7 @@ class TestFile < Test::Unit::TestCase
a = File.join(tmpdir, "x")
begin
File.symlink(tst, a)
- rescue Errno::EACCES
+ rescue Errno::EACCES, Errno::EPERM
skip "need privilege"
end
assert_equal(File.join(realdir, tst), File.realpath(a))
@@ -284,6 +283,34 @@ class TestFile < Test::Unit::TestCase
}
end
+ def test_realpath_taintedness
+ Dir.mktmpdir('rubytest-realpath') {|tmpdir|
+ dir = File.realpath(tmpdir).untaint
+ File.write(File.join(dir, base = "test.file"), '')
+ base.taint
+ dir.taint
+ assert_predicate(File.realpath(base, dir), :tainted?)
+ base.untaint
+ dir.taint
+ assert_predicate(File.realpath(base, dir), :tainted?)
+ base.taint
+ dir.untaint
+ assert_predicate(File.realpath(base, dir), :tainted?)
+ base.untaint
+ dir.untaint
+ assert_predicate(File.realpath(base, dir), :tainted?)
+ assert_predicate(Dir.chdir(dir) {File.realpath(base)}, :tainted?)
+ }
+ end
+
+ def test_realpath_special_symlink
+ IO.pipe do |r, w|
+ if File.pipe?(path = "/dev/fd/#{r.fileno}")
+ assert_file.identical?(File.realpath(path), path)
+ end
+ end
+ end
+
def test_realdirpath
Dir.mktmpdir('rubytest-realdirpath') {|tmpdir|
realdir = File.realpath(tmpdir)
@@ -446,6 +473,8 @@ class TestFile < Test::Unit::TestCase
(0..1).each do |level|
assert_nothing_raised(SecurityError, bug5374) {in_safe[level]}
end
+ ensure
+ $SAFE = 0
end
if /(bcc|ms|cyg)win|mingw|emx/ =~ RUBY_PLATFORM
@@ -468,4 +497,26 @@ class TestFile < Test::Unit::TestCase
assert_file.not_exist?(path)
end
end
+
+ def test_open_tempfile_path
+ Dir.mktmpdir(__method__.to_s) do |tmpdir|
+ begin
+ io = File.open(tmpdir, File::RDWR | File::TMPFILE)
+ rescue Errno::EINVAL
+ skip 'O_TMPFILE not supported (EINVAL)'
+ rescue Errno::EISDIR
+ skip 'O_TMPFILE not supported (EISDIR)'
+ rescue Errno::EOPNOTSUPP
+ skip 'O_TMPFILE not supported (EOPNOTSUPP)'
+ end
+
+ io.write "foo"
+ io.flush
+ assert_equal 3, io.size
+ assert_raise(IOError) { io.path }
+ ensure
+ io&.close
+ end
+ end if File::Constants.const_defined?(:TMPFILE)
+
end
diff --git a/test/ruby/test_file_exhaustive.rb b/test/ruby/test_file_exhaustive.rb
index f316d3fcf3..7d4eeb6d59 100644
--- a/test/ruby/test_file_exhaustive.rb
+++ b/test/ruby/test_file_exhaustive.rb
@@ -3,11 +3,12 @@ require "test/unit"
require "fileutils"
require "tmpdir"
require "socket"
+require '-test-/file'
class TestFileExhaustive < Test::Unit::TestCase
DRIVE = Dir.pwd[%r'\A(?:[a-z]:|//[^/]+/[^/]+)'i]
POSIX = /cygwin|mswin|bccwin|mingw|emx/ !~ RUBY_PLATFORM
- NTFS = !(/cygwin|mingw|mswin|bccwin/ !~ RUBY_PLATFORM)
+ NTFS = !(/mingw|mswin|bccwin/ !~ RUBY_PLATFORM)
def assert_incompatible_encoding
d = "\u{3042}\u{3044}".encode("utf-16le")
@@ -118,7 +119,7 @@ class TestFileExhaustive < Test::Unit::TestCase
@symlinkfile = make_tmp_filename("symlinkfile")
begin
File.symlink(regular_file, @symlinkfile)
- rescue NotImplementedError, Errno::EACCES
+ rescue NotImplementedError, Errno::EACCES, Errno::EPERM
@symlinkfile = nil
end
@symlinkfile
@@ -494,22 +495,39 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_file.grpowned?(utf8_file)
end if POSIX
+ def io_open(file_name)
+ # avoid File.open since we do not want #to_path
+ io = IO.for_fd(IO.sysopen(file_name))
+ yield io
+ ensure
+ io&.close
+ end
+
def test_suid
assert_file.not_setuid?(regular_file)
assert_file.not_setuid?(utf8_file)
- assert_file.setuid?(suidfile) if suidfile
+ if suidfile
+ assert_file.setuid?(suidfile)
+ io_open(suidfile) { |io| assert_file.setuid?(io) }
+ end
end
def test_sgid
assert_file.not_setgid?(regular_file)
assert_file.not_setgid?(utf8_file)
- assert_file.setgid?(sgidfile) if sgidfile
+ if sgidfile
+ assert_file.setgid?(sgidfile)
+ io_open(sgidfile) { |io| assert_file.setgid?(io) }
+ end
end
def test_sticky
assert_file.not_sticky?(regular_file)
assert_file.not_sticky?(utf8_file)
- assert_file.sticky?(stickyfile) if stickyfile
+ if stickyfile
+ assert_file.sticky?(stickyfile)
+ io_open(stickyfile) { |io| assert_file.sticky?(io) }
+ end
end
def test_path_identical_p
@@ -572,7 +590,13 @@ class TestFileExhaustive < Test::Unit::TestCase
t2 = File.open(file) {|f| f.atime}
assert_kind_of(Time, t1)
assert_kind_of(Time, t2)
- assert_equal(t1, t2)
+ # High Sierra's APFS can handle nano-sec precise.
+ # t1 value is difference from t2 on APFS.
+ if Bug::File::Fs.fsname(Dir.tmpdir) == "apfs"
+ assert_equal(t1.to_i, t2.to_i)
+ else
+ assert_equal(t1, t2)
+ end
end
assert_raise(Errno::ENOENT) { File.atime(nofile) }
end
@@ -640,6 +664,34 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_equal(t + 2, File.mtime(zerofile))
end
+ def test_utime_symlinkfile
+ return unless symlinkfile
+ t = Time.local(2000)
+ stat = File.lstat(symlinkfile)
+ assert_equal(1, File.utime(t, t, symlinkfile))
+ assert_equal(t, File.stat(regular_file).atime)
+ assert_equal(t, File.stat(regular_file).mtime)
+ end
+
+ def test_lutime
+ return unless File.respond_to?(:lutime)
+ return unless symlinkfile
+
+ r = File.stat(regular_file)
+ t = Time.local(2000)
+ File.lutime(t + 1, t + 2, symlinkfile)
+ rescue NotImplementedError => e
+ skip(e.message)
+ else
+ stat = File.stat(regular_file)
+ assert_equal(r.atime, stat.atime)
+ assert_equal(r.mtime, stat.mtime)
+
+ stat = File.lstat(symlinkfile)
+ assert_equal(t + 1, stat.atime)
+ assert_equal(t + 2, stat.mtime)
+ end
+
def test_hardlink
return unless hardlinkfile
assert_equal("file", File.ftype(hardlinkfile))
@@ -729,7 +781,10 @@ class TestFileExhaustive < Test::Unit::TestCase
def test_expand_path
assert_equal(regular_file, File.expand_path(File.basename(regular_file), File.dirname(regular_file)))
assert_equal(utf8_file, File.expand_path(File.basename(utf8_file), File.dirname(utf8_file)))
- if NTFS
+ end
+
+ if NTFS
+ def test_expand_path_ntfs
[regular_file, utf8_file].each do |file|
assert_equal(file, File.expand_path(file + " "))
assert_equal(file, File.expand_path(file + "."))
@@ -740,24 +795,36 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_match(/\Ae:\//i, File.expand_path('e:foo', 'd:/bar'))
assert_match(%r'\Ac:/bar/foo\z'i, File.expand_path('c:foo', 'c:/bar'))
end
- case RUBY_PLATFORM
- when /darwin/
+ end
+
+ case RUBY_PLATFORM
+ when /darwin/
+ def test_expand_path_hfs
["\u{feff}", *"\u{2000}"..."\u{2100}"].each do |c|
file = regular_file + c
+ full_path = File.expand_path(file)
+ mesg = proc {File.basename(full_path).dump}
begin
open(file) {}
rescue
- assert_equal(file, File.expand_path(file), c.dump)
+ # High Sierra's APFS cannot use filenames with undefined character
+ next if Bug::File::Fs.fsname(Dir.tmpdir) == "apfs"
+ assert_equal(file, full_path, mesg)
else
- assert_equal(regular_file, File.expand_path(file), c.dump)
+ assert_equal(regular_file, full_path, mesg)
end
end
end
- if DRIVE
+ end
+
+ if DRIVE
+ def test_expand_path_absolute
assert_match(%r"\Az:/foo\z"i, File.expand_path('/foo', "z:/bar"))
assert_match(%r"\A//host/share/foo\z"i, File.expand_path('/foo', "//host/share/bar"))
assert_match(%r"\A#{DRIVE}/foo\z"i, File.expand_path('/foo'))
- else
+ end
+ else
+ def test_expand_path_absolute
assert_equal("/foo", File.expand_path('/foo'))
end
end
@@ -782,6 +849,8 @@ class TestFileExhaustive < Test::Unit::TestCase
a = "#{drive}/\225\\\\"
if File::ALT_SEPARATOR == '\\'
[%W"cp437 #{drive}/\225", %W"cp932 #{drive}/\225\\"]
+ elsif File.directory?("#{@dir}/\\")
+ [%W"cp437 /\225", %W"cp932 /\225\\"]
else
[["cp437", a], ["cp932", a]]
end.each do |cp, expected|
@@ -827,7 +896,6 @@ class TestFileExhaustive < Test::Unit::TestCase
ENV["HOMEDRIVE"] = nil
ENV["HOMEPATH"] = nil
ENV["USERPROFILE"] = nil
- assert_raise(ArgumentError) { File.expand_path("~") }
ENV["HOME"] = "~"
assert_raise(ArgumentError, bug3630) { File.expand_path("~") }
ENV["HOME"] = "."
@@ -851,6 +919,8 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_raise(ArgumentError) { File.expand_path(".", UnknownUserHome) }
assert_nothing_raised(ArgumentError) { File.expand_path("#{DRIVE}/", UnknownUserHome) }
+ ENV["HOME"] = "#{DRIVE}UserHome"
+ assert_raise(ArgumentError) { File.expand_path("~") }
ensure
ENV["HOME"] = home
end
@@ -1124,7 +1194,10 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_equal("foo", File.basename("foo", ".ext"))
assert_equal("foo", File.basename("foo.ext", ".ext"))
assert_equal("foo", File.basename("foo.ext", ".*"))
- if NTFS
+ end
+
+ if NTFS
+ def test_basename_strip
[regular_file, utf8_file].each do |file|
basename = File.basename(file)
assert_equal(basename, File.basename(file + " "))
@@ -1139,13 +1212,34 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_equal(basename, File.basename(file + "::$DATA", ".*"))
end
end
- if File::ALT_SEPARATOR == '\\'
+ else
+ def test_basename_strip
+ [regular_file, utf8_file].each do |file|
+ basename = File.basename(file)
+ assert_equal(basename + " ", File.basename(file + " "))
+ assert_equal(basename + ".", File.basename(file + "."))
+ assert_equal(basename + "::$DATA", File.basename(file + "::$DATA"))
+ assert_equal(basename + " ", File.basename(file + " ", ".test"))
+ assert_equal(basename + ".", File.basename(file + ".", ".test"))
+ assert_equal(basename + "::$DATA", File.basename(file + "::$DATA", ".test"))
+ assert_equal(basename, File.basename(file + ".", ".*"))
+ basename.chomp!(".test")
+ assert_equal(basename, File.basename(file + " ", ".*"))
+ assert_equal(basename, File.basename(file + "::$DATA", ".*"))
+ end
+ end
+ end
+
+ if File::ALT_SEPARATOR == '\\'
+ def test_basename_backslash
a = "foo/\225\\\\"
[%W"cp437 \225", %W"cp932 \225\\"].each do |cp, expected|
assert_equal(expected.force_encoding(cp), File.basename(a.dup.force_encoding(cp)), cp)
end
end
+ end
+ def test_basename_encoding
assert_incompatible_encoding {|d| File.basename(d)}
assert_incompatible_encoding {|d| File.basename(d, ".*")}
assert_raise(Encoding::CompatibilityError) {File.basename("foo.ext", ".*".encode("utf-16le"))}
@@ -1161,8 +1255,14 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_equal(@dir, File.dirname(regular_file))
assert_equal(@dir, File.dirname(utf8_file))
assert_equal(".", File.dirname(""))
+ end
+
+ def test_dirname_encoding
assert_incompatible_encoding {|d| File.dirname(d)}
- if File::ALT_SEPARATOR == '\\'
+ end
+
+ if File::ALT_SEPARATOR == '\\'
+ def test_dirname_backslash
a = "\225\\\\foo"
[%W"cp437 \225", %W"cp932 \225\\"].each do |cp, expected|
assert_equal(expected.force_encoding(cp), File.dirname(a.dup.force_encoding(cp)), cp)
@@ -1245,6 +1345,19 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_raise(Encoding::CompatibilityError, bug7168) {File.join(names)}
end
+ def test_join_with_changed_separator
+ assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}")
+ bug = '[ruby-core:79579] [Bug #13223]'
+ begin;
+ class File
+ remove_const :Separator
+ remove_const :SEPARATOR
+ end
+ GC.start
+ assert_equal("hello/world", File.join("hello", "world"), bug)
+ end;
+ end
+
def test_truncate
[regular_file, utf8_file].each do |file|
assert_equal(0, File.truncate(file, 1))
@@ -1408,11 +1521,7 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_integer_or_nil(fs1.rdev_minor)
assert_integer(fs1.ino)
assert_integer(fs1.mode)
- unless /emx|mswin|mingw/ =~ RUBY_PLATFORM
- # on Windows, nlink is always 1. but this behavior will be changed
- # in the future.
- assert_equal(hardlinkfile ? 2 : 1, fs1.nlink)
- end
+ assert_equal(hardlinkfile ? 2 : 1, fs1.nlink)
assert_integer(fs1.uid)
assert_integer(fs1.gid)
assert_equal(3, fs1.size)
diff --git a/test/ruby/test_flip.rb b/test/ruby/test_flip.rb
index 810fd5d3ae..8ea6416bdb 100644
--- a/test/ruby/test_flip.rb
+++ b/test/ruby/test_flip.rb
@@ -2,7 +2,16 @@
require 'test/unit'
class TestFlip < Test::Unit::TestCase
+ def setup
+ @verbose_bak, $VERBOSE = $VERBOSE, nil
+ end
+
+ def teardown
+ $VERBOSE = @verbose_bak
+ end
+
def test_flip_flop
+ eval <<-END
assert_equal [4,5], (1..9).select {|n| true if (n==4)..(n==5)}
assert_equal [4,5], (1..9).select {|n| true if (n==4)...(n==5)}
assert_equal [2], (1..9).select {|n| true if (n==2)..(n%2).zero?}
@@ -10,6 +19,7 @@ class TestFlip < Test::Unit::TestCase
assert_equal [4,5,7,8], (1..9).select {|n| true if (n==4)...(n==5) or (n==7)...(n==8)}
assert_equal [nil, 2, 3, 4, nil], (1..5).map {|x| x if (x==2..x==4)}
assert_equal [1, nil, nil, nil, 5], (1..5).map {|x| x if !(x==2..x==4)}
+ END
end
def test_hidden_key
@@ -25,13 +35,13 @@ class TestFlip < Test::Unit::TestCase
def test_shared_eval
bug7671 = '[ruby-core:51296]'
vs = (1..9).to_a
- vs.select {|n| if n==2..n==16 then 1 end}
+ eval("vs.select {|n| if n==2..n==16 then 1 end}")
v = eval("vs.select {|n| if n==3..n==6 then 1 end}")
assert_equal([*3..6], v, bug7671)
end
def test_shared_thread
- ff = proc {|n| true if n==3..n==5}
+ ff = eval("proc {|n| true if n==3..n==5}")
v = 1..9
a = true
th = Thread.new {
@@ -50,4 +60,25 @@ class TestFlip < Test::Unit::TestCase
assert_equal(expected, v1, mesg)
assert_equal(expected, v2, mesg)
end
+
+ def test_input_line_number_range
+ bug12947 = '[ruby-core:78162] [Bug #12947]'
+ ary = b1 = b2 = nil
+ EnvUtil.suppress_warning do
+ b1 = eval("proc {|i| i if 2..4}")
+ b2 = eval("proc {|i| if 2..4; i; end}")
+ end
+ IO.pipe {|r, w|
+ th = Thread.start {(1..5).each {|i| w.puts i};w.close}
+ ary = r.map {|i| b1.call(i.chomp)}
+ th.join
+ }
+ assert_equal([nil, "2", "3", "4", nil], ary, bug12947)
+ IO.pipe {|r, w|
+ th = Thread.start {(1..5).each {|i| w.puts i};w.close}
+ ary = r.map {|i| b2.call(i.chomp)}
+ th.join
+ }
+ assert_equal([nil, "2", "3", "4", nil], ary, bug12947)
+ end
end
diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb
index f2989e4f5b..0b2e4df05b 100644
--- a/test/ruby/test_float.rb
+++ b/test/ruby/test_float.rb
@@ -16,6 +16,8 @@ class TestFloat < Test::Unit::TestCase
assert_in_delta(13.4 % 1, 0.4, 0.0001)
assert_equal(36893488147419111424,
36893488147419107329.0.to_i)
+ assert_equal(1185151044158398820374743613440,
+ 1.1851510441583988e+30.to_i)
end
def nan_test(x,y)
@@ -161,6 +163,32 @@ class TestFloat < Test::Unit::TestCase
assert_equal(-31.0*2**-1027, Float("-0x1f"+("0"*268)+".0p-2099"))
assert_equal(-31.0*2**-1027, Float("-0x1f"+("0"*600)+".0p-3427"))
end
+
+ assert_equal(1.0e10, Float("1.0_"+"00000"*Float::DIG+"e10"))
+
+ z = "0" * (Float::DIG * 4 + 10)
+ all_assertions_foreach("long invalid string", "1.0", "1.0e", "1.0e-", "1.0e+") do |n|
+ assert_raise(ArgumentError, n += z + "A") {Float(n)}
+ assert_raise(ArgumentError, n += z + ".0") {Float(n)}
+ end
+
+ x = nil
+ 2000.times do
+ x = Float("0x"+"0"*30)
+ break unless x == 0.0
+ end
+ assert_equal(0.0, x, ->{"%a" % x})
+ x = nil
+ 2000.times do
+ begin
+ x = Float("0x1."+"0"*270)
+ rescue ArgumentError => e
+ raise unless /"0x1\.0{270}"/ =~ e.message
+ else
+ break
+ end
+ end
+ assert_nil(x, ->{"%a" % x})
end
def test_divmod
@@ -455,6 +483,8 @@ class TestFloat < Test::Unit::TestCase
end
def test_floor_with_precision
+ assert_equal(+0.0, +0.001.floor(1))
+ assert_equal(-0.1, -0.001.floor(1))
assert_equal(1.100, 1.111.floor(1))
assert_equal(1.110, 1.111.floor(2))
assert_equal(11110, 11119.9.floor(-1))
@@ -482,6 +512,8 @@ class TestFloat < Test::Unit::TestCase
end
def test_ceil_with_precision
+ assert_equal(+0.1, +0.001.ceil(1))
+ assert_equal(-0.0, -0.001.ceil(1))
assert_equal(1.200, 1.111.ceil(1))
assert_equal(1.120, 1.111.ceil(2))
assert_equal(11120, 11111.1.ceil(-1))
@@ -701,9 +733,66 @@ class TestFloat < Test::Unit::TestCase
assert_equal(-7.1364, -7.1364499.round(4, half: :up))
end
+ def test_round_half_down
+ assert_equal(12.0, 12.5.round(half: :down))
+ assert_equal(13.0, 13.5.round(half: :down))
+
+ assert_equal(2.1, 2.15.round(1, half: :down))
+ assert_equal(2.2, 2.25.round(1, half: :down))
+ assert_equal(2.3, 2.35.round(1, half: :down))
+
+ assert_equal(-2.1, -2.15.round(1, half: :down))
+ assert_equal(-2.2, -2.25.round(1, half: :down))
+ assert_equal(-2.3, -2.35.round(1, half: :down))
+
+ assert_equal(7.1364, 7.13645.round(4, half: :down))
+ assert_equal(7.1365, 7.1364501.round(4, half: :down))
+ assert_equal(7.1364, 7.1364499.round(4, half: :down))
+
+ assert_equal(-7.1364, -7.13645.round(4, half: :down))
+ assert_equal(-7.1365, -7.1364501.round(4, half: :down))
+ assert_equal(-7.1364, -7.1364499.round(4, half: :down))
+ end
+
+ def test_round_half_nil
+ assert_equal(13.0, 12.5.round(half: nil))
+ assert_equal(14.0, 13.5.round(half: nil))
+
+ assert_equal(2.2, 2.15.round(1, half: nil))
+ assert_equal(2.3, 2.25.round(1, half: nil))
+ assert_equal(2.4, 2.35.round(1, half: nil))
+
+ assert_equal(-2.2, -2.15.round(1, half: nil))
+ assert_equal(-2.3, -2.25.round(1, half: nil))
+ assert_equal(-2.4, -2.35.round(1, half: nil))
+
+ assert_equal(7.1365, 7.13645.round(4, half: nil))
+ assert_equal(7.1365, 7.1364501.round(4, half: nil))
+ assert_equal(7.1364, 7.1364499.round(4, half: nil))
+
+ assert_equal(-7.1365, -7.13645.round(4, half: nil))
+ assert_equal(-7.1365, -7.1364501.round(4, half: nil))
+ assert_equal(-7.1364, -7.1364499.round(4, half: nil))
+ end
+
+ def test_round_half_invalid
+ assert_raise_with_message(ArgumentError, /Object/) {
+ 1.0.round(half: Object)
+ }
+ assert_raise_with_message(ArgumentError, /xxx/) {
+ 1.0.round(half: "\0xxx")
+ }
+ end
+
def test_Float
assert_in_delta(0.125, Float("0.1_2_5"), 0.00001)
assert_in_delta(0.125, "0.1_2_5__".to_f, 0.00001)
+ assert_in_delta(0.0, "0_.125".to_f, 0.00001)
+ assert_in_delta(0.0, "0._125".to_f, 0.00001)
+ assert_in_delta(0.1, "0.1__2_5".to_f, 0.00001)
+ assert_in_delta(0.1, "0.1_e10".to_f, 0.00001)
+ assert_in_delta(0.1, "0.1e_10".to_f, 0.00001)
+ assert_in_delta(1.0, "0.1e1__0".to_f, 0.00001)
assert_equal(1, suppress_warning {Float(([1] * 10000).join)}.infinite?)
assert_not_predicate(Float(([1] * 10000).join("_")), :infinite?) # is it really OK?
assert_raise(ArgumentError) { Float("1.0\x001") }
@@ -720,6 +809,7 @@ class TestFloat < Test::Unit::TestCase
assert_equal(Float::INFINITY, Float('0xf.fp1000000000000000'))
assert_equal(1, suppress_warning {Float("1e10_00")}.infinite?)
assert_raise(TypeError) { Float(nil) }
+ assert_raise(TypeError) { Float(:test) }
o = Object.new
def o.to_f; inf = Float::INFINITY; inf/inf; end
assert_predicate(Float(o), :nan?)
@@ -730,8 +820,45 @@ class TestFloat < Test::Unit::TestCase
assert_raise(ArgumentError, bug4310) {under_gc_stress {Float('a'*10000)}}
end
+ def test_Float_with_exception_keyword
+ assert_raise(ArgumentError) {
+ Float(".", exception: true)
+ }
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(nil, Float(".", exception: false))
+ }
+ assert_raise(RangeError) {
+ Float(1i, exception: true)
+ }
+ assert_nothing_raised(RangeError) {
+ assert_equal(nil, Float(1i, exception: false))
+ }
+ assert_raise(TypeError) {
+ Float(nil, exception: true)
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Float(nil, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Float(:test, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Float(Object.new, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ o = Object.new
+ def o.to_f; 3.14; end
+ assert_equal(3.14, Float(o, exception: false))
+ }
+ assert_nothing_raised(RuntimeError) {
+ o = Object.new
+ def o.to_f; raise; end
+ assert_equal(nil, Float(o, exception: false))
+ }
+ end
+
def test_num2dbl
- assert_raise(TypeError) do
+ assert_raise(ArgumentError, "comparison of String with 0 failed") do
1.0.step(2.0, "0.5") {}
end
assert_raise(TypeError) do
diff --git a/test/ruby/test_fnmatch.rb b/test/ruby/test_fnmatch.rb
index ca01a28698..16f1076e48 100644
--- a/test/ruby/test_fnmatch.rb
+++ b/test/ruby/test_fnmatch.rb
@@ -10,6 +10,7 @@ class TestFnmatch < Test::Unit::TestCase
assert_equal(t.include?(i.chr), !File.fnmatch("[!#{s}]", i.chr, File::FNM_DOTMATCH))
end
end
+
def test_fnmatch
assert_file.for("[ruby-dev:22819]").fnmatch('\[1\]' , '[1]')
assert_file.for("[ruby-dev:22815]").fnmatch('*?', 'a')
@@ -17,10 +18,16 @@ class TestFnmatch < Test::Unit::TestCase
assert_file.fnmatch('\[1\]' , '[1]', File::FNM_PATHNAME)
assert_file.fnmatch('*?', 'a', File::FNM_PATHNAME)
assert_file.fnmatch('*/', 'a/', File::FNM_PATHNAME)
+ end
+
+ def test_text
# text
assert_file.fnmatch('cat', 'cat')
assert_file.not_fnmatch('cat', 'category')
assert_file.not_fnmatch('cat', 'wildcat')
+ end
+
+ def test_any_one
# '?' matches any one character
assert_file.fnmatch('?at', 'cat')
assert_file.fnmatch('c?t', 'cat')
@@ -29,6 +36,9 @@ class TestFnmatch < Test::Unit::TestCase
assert_file.not_fnmatch('c??t', 'cat')
assert_file.not_fnmatch('??at', 'cat')
assert_file.not_fnmatch('ca??', 'cat')
+ end
+
+ def test_any_chars
# '*' matches any number (including 0) of any characters
assert_file.fnmatch('c*', 'cats')
assert_file.fnmatch('c*ts', 'cats')
@@ -40,9 +50,15 @@ class TestFnmatch < Test::Unit::TestCase
assert_file.not_fnmatch('a*abc', 'abc')
assert_file.fnmatch('a*bc', 'abc')
assert_file.not_fnmatch('a*bc', 'abcd')
+ end
+
+ def test_char_class
# [seq] : matches any character listed between bracket
# [!seq] or [^seq] : matches any character except those listed between bracket
bracket_test("bd-gikl-mosv-x", "bdefgiklmosvwx")
+ end
+
+ def test_escape
# escaping character
assert_file.fnmatch('\?', '?')
assert_file.not_fnmatch('\?', '\?')
@@ -59,6 +75,9 @@ class TestFnmatch < Test::Unit::TestCase
assert_file.fnmatch('[a\-c]', 'c')
assert_file.not_fnmatch('[a\-c]', 'b')
assert_file.not_fnmatch('[a\-c]', '\\')
+ end
+
+ def test_fnm_escape
# escaping character loses its meaning if FNM_NOESCAPE is set
assert_file.not_fnmatch('\?', '?', File::FNM_NOESCAPE)
assert_file.fnmatch('\?', '\?', File::FNM_NOESCAPE)
@@ -75,6 +94,9 @@ class TestFnmatch < Test::Unit::TestCase
assert_file.fnmatch('[a\-c]', 'c', File::FNM_NOESCAPE)
assert_file.fnmatch('[a\-c]', 'b', File::FNM_NOESCAPE) # '\\' < 'b' < 'c'
assert_file.fnmatch('[a\-c]', '\\', File::FNM_NOESCAPE)
+ end
+
+ def test_fnm_casefold
# case is ignored if FNM_CASEFOLD is set
assert_file.not_fnmatch('cat', 'CAT')
assert_file.fnmatch('cat', 'CAT', File::FNM_CASEFOLD)
@@ -82,11 +104,17 @@ class TestFnmatch < Test::Unit::TestCase
assert_file.fnmatch('[a-z]', 'D', File::FNM_CASEFOLD)
assert_file.not_fnmatch('[abc]', 'B')
assert_file.fnmatch('[abc]', 'B', File::FNM_CASEFOLD)
+ end
+
+ def test_fnm_pathname
# wildcard doesn't match '/' if FNM_PATHNAME is set
assert_file.fnmatch('foo?boo', 'foo/boo')
assert_file.fnmatch('foo*', 'foo/boo')
assert_file.not_fnmatch('foo?boo', 'foo/boo', File::FNM_PATHNAME)
assert_file.not_fnmatch('foo*', 'foo/boo', File::FNM_PATHNAME)
+ end
+
+ def test_fnm_dotmatch
# wildcard matches leading period if FNM_DOTMATCH is set
assert_file.not_fnmatch('*', '.profile')
assert_file.fnmatch('*', '.profile', File::FNM_DOTMATCH)
@@ -95,6 +123,9 @@ class TestFnmatch < Test::Unit::TestCase
assert_file.fnmatch('*/*', 'dave/.profile')
assert_file.not_fnmatch('*/*', 'dave/.profile', File::FNM_PATHNAME)
assert_file.fnmatch('*/*', 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH)
+ end
+
+ def test_recursive
# recursive matching
assert_file.fnmatch('**/foo', 'a/b/c/foo', File::FNM_PATHNAME)
assert_file.fnmatch('**/foo', '/foo', File::FNM_PATHNAME)
@@ -129,4 +160,10 @@ class TestFnmatch < Test::Unit::TestCase
assert_file.fnmatch("[a-\u3042]*", "\u3042")
assert_file.not_fnmatch("[a-\u3042]*", "\u3043")
end
+
+ def test_nullchar
+ assert_raise(ArgumentError) {
+ File.fnmatch("a\0z", "a")
+ }
+ end
end
diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb
index a5fd2897a2..7a6309b6a3 100644
--- a/test/ruby/test_gc.rb
+++ b/test/ruby/test_gc.rb
@@ -290,7 +290,10 @@ class TestGc < Test::Unit::TestCase
base_length = GC.stat[:heap_eden_pages]
(base_length * 500).times{ 'a' }
GC.start
- assert_in_delta base_length, (v = GC.stat[:heap_eden_pages]), 2,
+ base_length = GC.stat[:heap_eden_pages]
+ (base_length * 500).times{ 'a' }
+ GC.start
+ assert_in_epsilon base_length, (v = GC.stat[:heap_eden_pages]), 1/8r,
"invalid heap expanding (base_length: #{base_length}, GC.stat[:heap_eden_pages]: #{v})"
a = []
@@ -397,4 +400,50 @@ class TestGc < Test::Unit::TestCase
ObjectSpace.each_object{|o| case o when Module then o.instance_methods end}
end
end
+
+ def test_exception_in_finalizer_procs
+ result = []
+ c1 = proc do
+ result << :c1
+ raise
+ end
+ c2 = proc do
+ result << :c2
+ raise
+ end
+ tap {
+ tap {
+ obj = Object.new
+ ObjectSpace.define_finalizer(obj, c1)
+ ObjectSpace.define_finalizer(obj, c2)
+ obj = nil
+ }
+ }
+ GC.start
+ skip "finalizers did not get run" if result.empty?
+ assert_equal([:c1, :c2], result)
+ end
+
+ def test_exception_in_finalizer_method
+ @result = []
+ def self.c1(x)
+ @result << :c1
+ raise
+ end
+ def self.c2(x)
+ @result << :c2
+ raise
+ end
+ tap {
+ tap {
+ obj = Object.new
+ ObjectSpace.define_finalizer(obj, method(:c1))
+ ObjectSpace.define_finalizer(obj, method(:c2))
+ obj = nil
+ }
+ }
+ GC.start
+ skip "finalizers did not get run" if @result.empty?
+ assert_equal([:c1, :c2], @result)
+ end
end
diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb
index fdb65ae6ca..e1b6e7257e 100644
--- a/test/ruby/test_hash.rb
+++ b/test/ruby/test_hash.rb
@@ -133,6 +133,50 @@ class TestHash < Test::Unit::TestCase
assert_equal(100, h['a'])
assert_equal(200, h['b'])
assert_nil(h['c'])
+
+ h = @cls["a", 100, "b", 200]
+ assert_equal(100, h['a'])
+ assert_equal(200, h['b'])
+ assert_nil(h['c'])
+
+ h = @cls[[["a", 100], ["b", 200]]]
+ assert_equal(100, h['a'])
+ assert_equal(200, h['b'])
+ assert_nil(h['c'])
+
+ h = @cls[[["a", 100], ["b"], ["c", 300]]]
+ assert_equal(100, h['a'])
+ assert_equal(nil, h['b'])
+ assert_equal(300, h['c'])
+
+ h = @cls[[["a", 100], "b", ["c", 300]]]
+ assert_equal(100, h['a'])
+ assert_equal(nil, h['b'])
+ assert_equal(300, h['c'])
+ end
+
+ def test_s_AREF_duplicated_key
+ alist = [["a", 100], ["b", 200], ["a", 300], ["a", 400]]
+ h = @cls[alist]
+ assert_equal(2, h.size)
+ assert_equal(400, h['a'])
+ assert_equal(200, h['b'])
+ assert_nil(h['c'])
+ assert_equal(nil, h.key('300'))
+ end
+
+ def test_s_AREF_frozen_key_id
+ key = "a".freeze
+ h = @cls[key, 100]
+ assert_equal(100, h['a'])
+ assert_same(key, *h.keys)
+ end
+
+ def test_s_AREF_key_tampering
+ key = "a".dup
+ h = @cls[key, 100]
+ key.upcase!
+ assert_equal(100, h['a'])
end
def test_s_new
@@ -145,7 +189,6 @@ class TestHash < Test::Unit::TestCase
assert_instance_of(@cls, h)
assert_equal('default', h.default)
assert_equal('default', h['spurious'])
-
end
def test_try_convert
@@ -237,11 +280,56 @@ class TestHash < Test::Unit::TestCase
assert_same a.keys[0], b.keys[0]
end
+ def test_ASET_fstring_non_literal_key
+ underscore = "_"
+ non_literal_strings = Proc.new{ ["abc#{underscore}def", "abc" * 5, "abc" + "def", "" << "ghi" << "jkl"] }
+
+ a, b = {}, {}
+ non_literal_strings.call.each do |string|
+ assert_equal 1, a[string] = 1
+ end
+
+ non_literal_strings.call.each do |string|
+ assert_equal 1, b[string] = 1
+ end
+
+ [a.keys, b.keys].transpose.each do |key_a, key_b|
+ assert_same key_a, key_b
+ end
+ end
+
+ def test_hash_aset_fstring_identity
+ h = {}.compare_by_identity
+ h['abc'] = 1
+ h['abc'] = 2
+ assert_equal 2, h.size, '[ruby-core:78783] [Bug #12855]'
+ end
+
+ def test_hash_aref_fstring_identity
+ h = {}.compare_by_identity
+ h['abc'] = 1
+ assert_nil h['abc'], '[ruby-core:78783] [Bug #12855]'
+ end
+
def test_NEWHASH_fstring_key
a = {"ABC" => :t}
b = {"ABC" => :t}
assert_same a.keys[0], b.keys[0]
assert_same "ABC".freeze, a.keys[0]
+ var = +'ABC'
+ c = { var => :t }
+ assert_same "ABC".freeze, c.keys[0]
+ end
+
+ def test_tainted_string_key
+ str = 'str'.taint
+ h = {}
+ h[str] = nil
+ key = h.keys.first
+ assert_predicate str, :tainted?
+ assert_not_predicate str, :frozen?
+ assert_predicate key, :tainted?
+ assert_predicate key, :frozen?
end
def test_EQUAL # '=='
@@ -464,6 +552,8 @@ class TestHash < Test::Unit::TestCase
e = assert_raise(KeyError) { @h.fetch('gumby'*20) }
assert_match(/key not found: "gumbygumby/, e.message)
assert_match(/\.\.\.\z/, e.message)
+ assert_same(@h, e.receiver)
+ assert_equal('gumby'*20, e.key)
end
def test_key2?
@@ -524,9 +614,11 @@ class TestHash < Test::Unit::TestCase
assert_equal(4, res.length)
assert_equal %w( three two one nil ), res
- assert_raise KeyError do
+ e = assert_raise KeyError do
@h.fetch_values(3, 'invalid')
end
+ assert_same(@h, e.receiver)
+ assert_equal('invalid', e.key)
res = @h.fetch_values(3, 'invalid') { |k| k.upcase }
assert_equal %w( three INVALID ), res
@@ -663,6 +755,14 @@ class TestHash < Test::Unit::TestCase
assert_predicate(h, :compare_by_identity?)
end
+ def test_replace_bug15358
+ h1 = {}
+ h2 = {a:1,b:2,c:3,d:4,e:5}
+ h2.replace(h1)
+ GC.start
+ assert(true)
+ end
+
def test_shift
h = @h.dup
@@ -773,6 +873,16 @@ class TestHash < Test::Unit::TestCase
assert_equal("nope42", h[42])
end
+ def test_to_h_block
+ h = @h.to_h {|k, v| [k.to_s, v.to_s]}
+ assert_equal({
+ "1"=>"one", "2"=>"two", "3"=>"three", to_s=>"self",
+ "true"=>"true", ""=>"nil", "nil"=>""
+ },
+ h)
+ assert_instance_of(Hash, h)
+ end
+
def test_nil_to_h
h = nil.to_h
assert_equal({}, h)
@@ -935,6 +1045,52 @@ class TestHash < Test::Unit::TestCase
assert_equal(nil, h.select!{true})
end
+ def test_slice
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_equal({1=>2, 3=>4}, h.slice(1, 3))
+ assert_equal({}, h.slice(7))
+ assert_equal({}, h.slice)
+ assert_equal({}, {}.slice)
+ end
+
+ def test_filter
+ assert_equal({3=>4,5=>6}, @cls[1=>2,3=>4,5=>6].filter {|k, v| k + v >= 7 })
+
+ base = @cls[ 1 => 'one', '2' => false, true => 'true', 'cat' => 99 ]
+ h1 = @cls[ '2' => false, 'cat' => 99 ]
+ h2 = @cls[ 1 => 'one', true => 'true' ]
+ h3 = @cls[ 1 => 'one', true => 'true', 'cat' => 99 ]
+
+ h = base.dup
+ assert_equal(h, h.filter { true })
+ assert_equal(@cls[], h.filter { false })
+
+ h = base.dup
+ assert_equal(h1, h.filter {|k,v| k.instance_of?(String) })
+
+ assert_equal(h2, h.filter {|k,v| v.instance_of?(String) })
+
+ assert_equal(h3, h.filter {|k,v| v })
+ assert_equal(base, h)
+
+ h.instance_variable_set(:@foo, :foo)
+ h.default = 42
+ h.taint
+ h = h.filter {true}
+ assert_instance_of(Hash, h)
+ assert_not_predicate(h, :tainted?)
+ assert_nil(h.default)
+ assert_not_send([h, :instance_variable_defined?, :@foo])
+ end
+
+ def test_filter!
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_equal(h, h.filter! {|k, v| k + v >= 7 })
+ assert_equal({3=>4,5=>6}, h)
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_equal(nil, h.filter!{true})
+ end
+
def test_clear2
assert_equal({}, @cls[1=>2,3=>4,5=>6].clear)
h = @cls[1=>2,3=>4,5=>6]
@@ -952,8 +1108,8 @@ class TestHash < Test::Unit::TestCase
assert_raise(TypeError) { h2.replace(1) }
h2.freeze
assert_raise(ArgumentError) { h2.replace() }
- assert_raise(RuntimeError) { h2.replace(h1) }
- assert_raise(RuntimeError) { h2.replace(42) }
+ assert_raise(FrozenError) { h2.replace(h1) }
+ assert_raise(FrozenError) { h2.replace(42) }
end
def test_size2
@@ -1003,11 +1159,35 @@ class TestHash < Test::Unit::TestCase
assert_equal({1=>6, 3=>4, 5=>7}, h1)
end
+ def test_update3
+ h1 = @cls[1=>2, 3=>4]
+ h1.update()
+ assert_equal({1=>2, 3=>4}, h1)
+ h2 = {1=>3, 5=>7}
+ h3 = {1=>1, 2=>4}
+ h1.update(h2, h3)
+ assert_equal({1=>1, 2=>4, 3=>4, 5=>7}, h1)
+ end
+
+ def test_update4
+ h1 = @cls[1=>2, 3=>4]
+ h1.update(){|k, v1, v2| k + v1 + v2 }
+ assert_equal({1=>2, 3=>4}, h1)
+ h2 = {1=>3, 5=>7}
+ h3 = {1=>1, 2=>4}
+ h1.update(h2, h3){|k, v1, v2| k + v1 + v2 }
+ assert_equal({1=>8, 2=>4, 3=>4, 5=>7}, h1)
+ end
+
def test_merge
h1 = @cls[1=>2, 3=>4]
h2 = {1=>3, 5=>7}
+ h3 = {1=>1, 2=>4}
+ assert_equal({1=>2, 3=>4}, h1.merge())
assert_equal({1=>3, 3=>4, 5=>7}, h1.merge(h2))
assert_equal({1=>6, 3=>4, 5=>7}, h1.merge(h2) {|k, v1, v2| k + v1 + v2 })
+ assert_equal({1=>1, 2=>4, 3=>4, 5=>7}, h1.merge(h2, h3))
+ assert_equal({1=>8, 2=>4, 3=>4, 5=>7}, h1.merge(h2, h3) {|k, v1, v2| k + v1 + v2 })
end
def test_assoc
@@ -1039,7 +1219,12 @@ class TestHash < Test::Unit::TestCase
assert_equal([1, "one", 2, 2, "two", 3, 3, ["three"]], a.flatten(2))
assert_equal([1, "one", 2, 2, "two", 3, 3, "three"], a.flatten(3))
assert_equal([1, "one", 2, 2, "two", 3, 3, "three"], a.flatten(-1))
- assert_raise(TypeError){ a.flatten(Object) }
+ assert_raise(TypeError){ a.flatten(nil) }
+ end
+
+ def test_flatten_arity
+ a = @cls[1=> "one", 2 => [2,"two"], 3 => [3, ["three"]]]
+ assert_raise(ArgumentError){ a.flatten(1, 2) }
end
def test_callcc
@@ -1183,7 +1368,7 @@ class TestHash < Test::Unit::TestCase
assert_equal({o=>1}.hash, @cls[o=>1].hash)
end
- def test_hash_poped
+ def test_hash_popped
assert_nothing_raised { eval("a = 1; @cls[a => a]; a") }
end
@@ -1302,7 +1487,7 @@ class TestHash < Test::Unit::TestCase
assert_no_memory_leak([], prepare, code, bug9187)
end
- def test_wrapper_of_special_const
+ def test_wrapper
bug9381 = '[ruby-core:59638] [Bug #9381]'
wrapper = Class.new do
@@ -1323,6 +1508,7 @@ class TestHash < Test::Unit::TestCase
5, true, false, nil,
0.0, 1.72723e-77,
:foo, "dsym_#{self.object_id.to_s(16)}_#{Time.now.to_i.to_s(16)}".to_sym,
+ "str",
].select do |x|
hash = {x => bug9381}
hash[wrapper.new(x)] != bug9381
@@ -1330,6 +1516,44 @@ class TestHash < Test::Unit::TestCase
assert_empty(bad, bug9381)
end
+ def assert_hash_random(obj, dump = obj.inspect)
+ a = [obj.hash.to_s]
+ 3.times {
+ assert_in_out_err(["-e", "print (#{dump}).hash"], "") do |r, e|
+ a += r
+ assert_equal([], e)
+ end
+ }
+ assert_not_equal([obj.hash.to_s], a.uniq)
+ assert_operator(a.uniq.size, :>, 2, proc {a.inspect})
+ end
+
+ def test_string_hash_random
+ assert_hash_random('abc')
+ end
+
+ def test_symbol_hash_random
+ assert_hash_random(:-)
+ assert_hash_random(:foo)
+ assert_hash_random("dsym_#{self.object_id.to_s(16)}_#{Time.now.to_i.to_s(16)}".to_sym)
+ end
+
+ def test_integer_hash_random
+ assert_hash_random(0)
+ assert_hash_random(+1)
+ assert_hash_random(-1)
+ assert_hash_random(+(1<<100))
+ assert_hash_random(-(1<<100))
+ end
+
+ def test_float_hash_random
+ assert_hash_random(0.0)
+ assert_hash_random(+1.0)
+ assert_hash_random(-1.0)
+ assert_hash_random(1.72723e-77)
+ assert_hash_random(Float::INFINITY, "Float::INFINITY")
+ end
+
def test_label_syntax
return unless @cls == Hash
@@ -1424,6 +1648,42 @@ class TestHash < Test::Unit::TestCase
assert_equal([10, 20, 30], [1, 2, 3].map(&h))
end
+ def test_transform_keys
+ x = @cls[a: 1, b: 2, c: 3]
+ y = x.transform_keys {|k| :"#{k}!" }
+ assert_equal({a: 1, b: 2, c: 3}, x)
+ assert_equal({a!: 1, b!: 2, c!: 3}, y)
+
+ enum = x.transform_keys
+ assert_equal(x.size, enum.size)
+ assert_instance_of(Enumerator, enum)
+
+ y = x.transform_keys.with_index {|k, i| "#{k}.#{i}" }
+ assert_equal(%w(a.0 b.1 c.2), y.keys)
+ end
+
+ def test_transform_keys_bang
+ x = @cls[a: 1, b: 2, c: 3]
+ y = x.transform_keys! {|k| :"#{k}!" }
+ assert_equal({a!: 1, b!: 2, c!: 3}, x)
+ assert_same(x, y)
+
+ enum = x.transform_keys!
+ assert_equal(x.size, enum.size)
+ assert_instance_of(Enumerator, enum)
+
+ x.transform_keys!.with_index {|k, i| "#{k}.#{i}" }
+ assert_equal(%w(a!.0 b!.1 c!.2), x.keys)
+
+ x = @cls[1 => :a, -1 => :b]
+ x.transform_keys! {|k| -k }
+ assert_equal([-1, :a, 1, :b], x.flatten)
+
+ x = @cls[true => :a, false => :b]
+ x.transform_keys! {|k| !k }
+ assert_equal([false, :a, true, :b], x.flatten)
+ end
+
def test_transform_values
x = @cls[a: 1, b: 2, c: 3]
y = x.transform_values {|v| v ** 2 }
@@ -1445,6 +1705,22 @@ class TestHash < Test::Unit::TestCase
assert_equal(%w(1.0 2.1 3.2), y.values_at(:a, :b, :c))
end
+ def test_broken_hash_value
+ bug14218 = '[ruby-core:84395] [Bug #14218]'
+
+ assert_equal(0, 1_000_000.times.count{a=Object.new.hash; b=Object.new.hash; a < 0 && b < 0 && a + b > 0}, bug14218)
+ assert_equal(0, 1_000_000.times.count{a=Object.new.hash; b=Object.new.hash; 0 + a + b != 0 + b + a}, bug14218)
+ end
+
+ def test_reserved_hash_val
+ s = Struct.new(:hash)
+ h = {}
+ keys = [*0..8]
+ keys.each {|i| h[s.new(i)]=true}
+ msg = proc {h.inspect}
+ assert_equal(keys, h.keys.map(&:hash), msg)
+ end
+
class TestSubHash < TestHash
class SubHash < Hash
def reject(*)
@@ -1457,4 +1733,62 @@ class TestHash < Test::Unit::TestCase
super
end
end
+
+ def test_ar2st
+ # insert
+ obj = Object.new
+ obj.instance_variable_set(:@h, h = {})
+ def obj.hash
+ 10.times{|i| @h[i] = i}
+ 0
+ end
+ def obj.inspect
+ 'test'
+ end
+ h[obj] = true
+ assert_equal '{0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9, test=>true}', h.inspect
+
+ # delete
+ obj = Object.new
+ obj.instance_variable_set(:@h, h = {})
+ def obj.hash
+ 10.times{|i| @h[i] = i}
+ 0
+ end
+ def obj.inspect
+ 'test'
+ end
+ def obj.eql? other
+ other.class == Object
+ end
+ obj2 = Object.new
+ def obj2.hash
+ 0
+ end
+
+ h[obj2] = true
+ h.delete obj
+ assert_equal '{0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9}', h.inspect
+
+ # lookup
+ obj = Object.new
+ obj.instance_variable_set(:@h, h = {})
+ def obj.hash
+ 10.times{|i| @h[i] = i}
+ 0
+ end
+ def obj.inspect
+ 'test'
+ end
+ def obj.eql? other
+ other.class == Object
+ end
+ obj2 = Object.new
+ def obj2.hash
+ 0
+ end
+
+ h[obj2] = true
+ assert_equal true, h[obj]
+ end
end
diff --git a/test/ruby/test_ifunless.rb b/test/ruby/test_ifunless.rb
index d533e155bc..f68e5154a2 100644
--- a/test/ruby/test_ifunless.rb
+++ b/test/ruby/test_ifunless.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
require 'test/unit'
-class TestIfunless < Test::Unit::TestCase
+class TestIfUnless < Test::Unit::TestCase
def test_if_unless
x = 'test';
assert(if x == x then true else false end)
diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb
index d9dd754ca6..0f289d8ed9 100644
--- a/test/ruby/test_integer.rb
+++ b/test/ruby/test_integer.rb
@@ -24,6 +24,32 @@ class TestInteger < Test::Unit::TestCase
rescue
nil
end, "[ruby-dev:32084] [ruby-dev:34547]")
+
+ x = EnvUtil.suppress_warning {2 ** -0x4000000000000000}
+ assert_in_delta(0.0, (x / 2), Float::EPSILON)
+
+ <<~EXPRS.each_line.with_index(__LINE__+1) do |expr, line|
+ crash01: 111r+11**-11111161111111
+ crash02: 1118111111111**-1111111111111111**1+1==11111
+ crash03: -1111111**-1111*11 - -1111111** -111111111
+ crash04: 1118111111111** -1111111111111111**1+11111111111**1 ===111
+ crash05: 11** -111155555555555555 -55 !=5-555
+ crash07: 1 + 111111111**-1111811111
+ crash08: 18111111111**-1111111111111111**1 + 1111111111**-1111**1
+ crash10: -7 - -1111111** -1111**11
+ crash12: 1118111111111** -1111111111111111**1 + 1111 - -1111111** -1111*111111111119
+ crash13: 1.0i - -1111111** -111111111
+ crash14: 11111**111111111**111111 * -11111111111111111111**-111111111111
+ crash15: ~1**1111 + -~1**~1**111
+ crash17: 11** -1111111**1111 /11i
+ crash18: 5555i**-5155 - -9111111**-1111**11
+ crash19: 111111*-11111111111111111111**-1111111111111111
+ crash20: 1111**111-11**-11111**11
+ crash21: 11**-10111111119-1i -1r
+ EXPRS
+ name, expr = expr.split(':', 2)
+ assert_ruby_status(%w"-W0", expr, name)
+ end
end
def test_lshift
@@ -92,6 +118,17 @@ class TestInteger < Test::Unit::TestCase
assert_equal(2 ** 50, Integer(2.0 ** 50))
assert_raise(TypeError) { Integer(nil) }
+ bug14552 = '[ruby-core:85813]'
+ obj = Object.new
+ def obj.to_int; "str"; end
+ assert_raise(TypeError, bug14552) { Integer(obj) }
+ def obj.to_i; 42; end
+ assert_equal(42, Integer(obj), bug14552)
+
+ obj = Object.new
+ def obj.to_i; "str"; end
+ assert_raise(TypeError) { Integer(obj) }
+
bug6192 = '[ruby-core:43566]'
assert_raise(Encoding::CompatibilityError, bug6192) {Integer("0".encode("utf-16be"))}
assert_raise(Encoding::CompatibilityError, bug6192) {Integer("0".encode("utf-16le"))}
@@ -116,6 +153,55 @@ class TestInteger < Test::Unit::TestCase
end;
end
+ def test_Integer_with_exception_keyword
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(nil, Integer("1z", exception: false))
+ }
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(nil, Integer(Object.new, exception: false))
+ }
+ assert_nothing_raised(ArgumentError) {
+ o = Object.new
+ def o.to_i; 42.5; end
+ assert_equal(nil, Integer(o, exception: false))
+ }
+ assert_nothing_raised(ArgumentError) {
+ o = Object.new
+ def o.to_i; raise; end
+ assert_equal(nil, Integer(o, exception: false))
+ }
+ assert_nothing_raised(ArgumentError) {
+ o = Object.new
+ def o.to_int; raise; end
+ assert_equal(nil, Integer(o, exception: false))
+ }
+ assert_nothing_raised(FloatDomainError) {
+ assert_equal(nil, Integer(Float::INFINITY, exception: false))
+ }
+ assert_nothing_raised(FloatDomainError) {
+ assert_equal(nil, Integer(-Float::INFINITY, exception: false))
+ }
+ assert_nothing_raised(FloatDomainError) {
+ assert_equal(nil, Integer(Float::NAN, exception: false))
+ }
+
+ assert_raise(ArgumentError) {
+ Integer("1z", exception: true)
+ }
+ assert_raise(TypeError) {
+ Integer(nil, exception: true)
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Integer(nil, exception: false))
+ }
+
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class Integer;def method_missing(*);"";end;end
+ assert_equal(0, Integer("0", 2))
+ end;
+ end
+
def test_int_p
assert_not_predicate(1.0, :integer?)
assert_predicate(1, :integer?)
@@ -181,13 +267,13 @@ class TestInteger < Test::Unit::TestCase
assert_int_equal(11111, 11111.round)
assert_int_equal(11111, 11111.round(0))
- assert_float_equal(11111.0, 11111.round(1))
- assert_float_equal(11111.0, 11111.round(2))
+ assert_int_equal(11111, 11111.round(1))
+ assert_int_equal(11111, 11111.round(2))
assert_int_equal(11110, 11111.round(-1))
assert_int_equal(11100, 11111.round(-2))
assert_int_equal(+200, +249.round(-2))
- assert_int_equal(+200, +250.round(-2))
+ assert_int_equal(+300, +250.round(-2))
assert_int_equal(-200, -249.round(-2))
assert_int_equal(+200, +249.round(-2, half: :even))
assert_int_equal(+200, +250.round(-2, half: :even))
@@ -197,7 +283,11 @@ class TestInteger < Test::Unit::TestCase
assert_int_equal(+300, +250.round(-2, half: :up))
assert_int_equal(+300, +349.round(-2, half: :up))
assert_int_equal(+400, +350.round(-2, half: :up))
- assert_int_equal(-200, -250.round(-2))
+ assert_int_equal(+200, +249.round(-2, half: :down))
+ assert_int_equal(+200, +250.round(-2, half: :down))
+ assert_int_equal(+300, +349.round(-2, half: :down))
+ assert_int_equal(+300, +350.round(-2, half: :down))
+ assert_int_equal(-300, -250.round(-2))
assert_int_equal(-200, -249.round(-2, half: :even))
assert_int_equal(-200, -250.round(-2, half: :even))
assert_int_equal(-300, -349.round(-2, half: :even))
@@ -206,8 +296,12 @@ class TestInteger < Test::Unit::TestCase
assert_int_equal(-300, -250.round(-2, half: :up))
assert_int_equal(-300, -349.round(-2, half: :up))
assert_int_equal(-400, -350.round(-2, half: :up))
- assert_int_equal(+20 * 10**70, (+25 * 10**70).round(-71))
- assert_int_equal(-20 * 10**70, (-25 * 10**70).round(-71))
+ assert_int_equal(-200, -249.round(-2, half: :down))
+ assert_int_equal(-200, -250.round(-2, half: :down))
+ assert_int_equal(-300, -349.round(-2, half: :down))
+ assert_int_equal(-300, -350.round(-2, half: :down))
+ assert_int_equal(+30 * 10**70, (+25 * 10**70).round(-71))
+ assert_int_equal(-30 * 10**70, (-25 * 10**70).round(-71))
assert_int_equal(+20 * 10**70, (+25 * 10**70 - 1).round(-71))
assert_int_equal(-20 * 10**70, (-25 * 10**70 + 1).round(-71))
assert_int_equal(+40 * 10**70, (+35 * 10**70).round(-71))
@@ -230,17 +324,28 @@ class TestInteger < Test::Unit::TestCase
assert_int_equal(-40 * 10**70, (-35 * 10**70).round(-71, half: :up))
assert_int_equal(+30 * 10**70, (+35 * 10**70 - 1).round(-71, half: :up))
assert_int_equal(-30 * 10**70, (-35 * 10**70 + 1).round(-71, half: :up))
+ assert_int_equal(+20 * 10**70, (+25 * 10**70).round(-71, half: :down))
+ assert_int_equal(-20 * 10**70, (-25 * 10**70).round(-71, half: :down))
+ assert_int_equal(+20 * 10**70, (+25 * 10**70 - 1).round(-71, half: :down))
+ assert_int_equal(-20 * 10**70, (-25 * 10**70 + 1).round(-71, half: :down))
+ assert_int_equal(+30 * 10**70, (+35 * 10**70).round(-71, half: :down))
+ assert_int_equal(-30 * 10**70, (-35 * 10**70).round(-71, half: :down))
+ assert_int_equal(+30 * 10**70, (+35 * 10**70 - 1).round(-71, half: :down))
+ assert_int_equal(-30 * 10**70, (-35 * 10**70 + 1).round(-71, half: :down))
assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1110, 1111_1111_1111_1111_1111_1111_1111_1111.round(-1))
assert_int_equal(-1111_1111_1111_1111_1111_1111_1111_1110, (-1111_1111_1111_1111_1111_1111_1111_1111).round(-1))
+
+ assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1111, 1111_1111_1111_1111_1111_1111_1111_1111.round(1))
+ assert_int_equal(10**400, (10**400).round(1))
end
def test_floor
assert_int_equal(11111, 11111.floor)
assert_int_equal(11111, 11111.floor(0))
- assert_float_equal(11111.0, 11111.floor(1))
- assert_float_equal(11111.0, 11111.floor(2))
+ assert_int_equal(11111, 11111.floor(1))
+ assert_int_equal(11111, 11111.floor(2))
assert_int_equal(11110, 11110.floor(-1))
assert_int_equal(11110, 11119.floor(-1))
@@ -258,14 +363,17 @@ class TestInteger < Test::Unit::TestCase
assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1110, 1111_1111_1111_1111_1111_1111_1111_1111.floor(-1))
assert_int_equal(-1111_1111_1111_1111_1111_1111_1111_1120, (-1111_1111_1111_1111_1111_1111_1111_1111).floor(-1))
+
+ assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1111, 1111_1111_1111_1111_1111_1111_1111_1111.floor(1))
+ assert_int_equal(10**400, (10**400).floor(1))
end
def test_ceil
assert_int_equal(11111, 11111.ceil)
assert_int_equal(11111, 11111.ceil(0))
- assert_float_equal(11111.0, 11111.ceil(1))
- assert_float_equal(11111.0, 11111.ceil(2))
+ assert_int_equal(11111, 11111.ceil(1))
+ assert_int_equal(11111, 11111.ceil(2))
assert_int_equal(11110, 11110.ceil(-1))
assert_int_equal(11120, 11119.ceil(-1))
@@ -283,14 +391,17 @@ class TestInteger < Test::Unit::TestCase
assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1120, 1111_1111_1111_1111_1111_1111_1111_1111.ceil(-1))
assert_int_equal(-1111_1111_1111_1111_1111_1111_1111_1110, (-1111_1111_1111_1111_1111_1111_1111_1111).ceil(-1))
+
+ assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1111, 1111_1111_1111_1111_1111_1111_1111_1111.ceil(1))
+ assert_int_equal(10**400, (10**400).ceil(1))
end
def test_truncate
assert_int_equal(11111, 11111.truncate)
assert_int_equal(11111, 11111.truncate(0))
- assert_float_equal(11111.0, 11111.truncate(1))
- assert_float_equal(11111.0, 11111.truncate(2))
+ assert_int_equal(11111, 11111.truncate(1))
+ assert_int_equal(11111, 11111.truncate(2))
assert_int_equal(11110, 11110.truncate(-1))
assert_int_equal(11110, 11119.truncate(-1))
@@ -308,6 +419,9 @@ class TestInteger < Test::Unit::TestCase
assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1110, 1111_1111_1111_1111_1111_1111_1111_1111.truncate(-1))
assert_int_equal(-1111_1111_1111_1111_1111_1111_1111_1110, (-1111_1111_1111_1111_1111_1111_1111_1111).truncate(-1))
+
+ assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1111, 1111_1111_1111_1111_1111_1111_1111_1111.truncate(1))
+ assert_int_equal(10**400, (10**400).truncate(1))
end
MimicInteger = Struct.new(:to_int)
@@ -448,4 +562,56 @@ class TestInteger < Test::Unit::TestCase
end
assert_equal([0, 1], 10.digits(o))
end
+
+ def test_square_root
+ assert_raise(TypeError) {Integer.sqrt("x")}
+ assert_raise(Math::DomainError) {Integer.sqrt(-1)}
+ assert_equal(0, Integer.sqrt(0))
+ (1...4).each {|i| assert_equal(1, Integer.sqrt(i))}
+ (4...9).each {|i| assert_equal(2, Integer.sqrt(i))}
+ (9...16).each {|i| assert_equal(3, Integer.sqrt(i))}
+ (1..40).each do |i|
+ mesg = "10**#{i}"
+ s = Integer.sqrt(n = 10**i)
+ if i.even?
+ assert_equal(10**(i/2), Integer.sqrt(n), mesg)
+ else
+ assert_include((s**2)...(s+1)**2, n, mesg)
+ end
+ end
+ 50.step(400, 10) do |i|
+ exact = 10**(i/2)
+ x = 10**i
+ assert_equal(exact, Integer.sqrt(x), "10**#{i}")
+ assert_equal(exact, Integer.sqrt(x+1), "10**#{i}+1")
+ assert_equal(exact-1, Integer.sqrt(x-1), "10**#{i}-1")
+ end
+
+ bug13440 = '[ruby-core:80696] [Bug #13440]'
+ failures = []
+ 0.step(to: 50, by: 0.05) do |i|
+ n = (10**i).to_i
+ root = Integer.sqrt(n)
+ failures << n unless root*root <= n && (root+1)*(root+1) > n
+ end
+ assert_empty(failures, bug13440)
+
+ x = 0xffff_ffff_ffff_ffff
+ assert_equal(x, Integer.sqrt(x ** 2), "[ruby-core:95453]")
+ end
+
+ def test_fdiv
+ assert_equal(1.0, 1.fdiv(1))
+ assert_equal(0.5, 1.fdiv(2))
+ end
+
+ def test_obj_fdiv
+ o = Object.new
+ def o.coerce(x); [x, 0.5]; end
+ assert_equal(2.0, 1.fdiv(o))
+ o = Object.new
+ def o.coerce(x); [self, x]; end
+ def o.fdiv(x); 1; end
+ assert_equal(1.0, 1.fdiv(o))
+ end
end
diff --git a/test/ruby/test_integer_comb.rb b/test/ruby/test_integer_comb.rb
index 80d08cac04..1ad13dd31b 100644
--- a/test/ruby/test_integer_comb.rb
+++ b/test/ruby/test_integer_comb.rb
@@ -457,6 +457,30 @@ class TestIntegerComb < Test::Unit::TestCase
}
end
+ def test_allbits_p
+ VS.each {|a|
+ VS.each {|b|
+ assert_equal((a & b) == b, a.allbits?(b), "(#{a}).allbits?(#{b}")
+ }
+ }
+ end
+
+ def test_anybits_p
+ VS.each {|a|
+ VS.each {|b|
+ assert_equal((a & b) != 0, a.anybits?(b), "(#{a}).anybits?(#{b}")
+ }
+ }
+ end
+
+ def test_nobits_p
+ VS.each {|a|
+ VS.each {|b|
+ assert_equal((a & b) == 0, a.nobits?(b), "(#{a}).nobits?(#{b}")
+ }
+ }
+ end
+
def test_to_s
2.upto(36) {|radix|
VS.each {|a|
diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb
index a5cdc7b19f..92fba2c04e 100644
--- a/test/ruby/test_io.rb
+++ b/test/ruby/test_io.rb
@@ -49,8 +49,8 @@ class TestIO < Test::Unit::TestCase
end
flunk("timeout") unless wt.join(10) && rt.join(10)
ensure
- w.close unless !w || w.closed?
- r.close unless !r || r.closed?
+ w&.close
+ r&.close
(wt.kill; wt.join) if wt
(rt.kill; rt.join) if rt
raise we if we
@@ -62,8 +62,8 @@ class TestIO < Test::Unit::TestCase
begin
yield r, w
ensure
- r.close unless r.closed?
- w.close unless w.closed?
+ r.close
+ w.close
end
end
@@ -84,12 +84,17 @@ class TestIO < Test::Unit::TestCase
}
end
- def trapping_usr1
- @usr1_rcvd = 0
- trap(:USR1) { @usr1_rcvd += 1 }
- yield
+ def trapping_usr2
+ @usr2_rcvd = 0
+ r, w = IO.pipe
+ trap(:USR2) do
+ w.write([@usr2_rcvd += 1].pack('L'))
+ end
+ yield r
ensure
- trap(:USR1, "DEFAULT")
+ trap(:USR2, "DEFAULT")
+ w&.close
+ r&.close
end
def test_pipe
@@ -228,6 +233,19 @@ class TestIO < Test::Unit::TestCase
assert_nil r.gets
r.close
end)
+
+ (0..3).each do |i|
+ pipe(proc do |w|
+ w.write("a" * ((4096 << i) - 4), "\r\n" "a\r\n")
+ w.close
+ end,
+ proc do |r|
+ r.gets
+ assert_equal "a", r.gets(chomp: true)
+ assert_nil r.gets
+ r.close
+ end)
+ end
end
def test_gets_chomp_rs_nil
@@ -366,6 +384,16 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_copy_stream_append
+ with_srccontent("foobar") {|src, content|
+ File.open('dst', 'ab') do |dst|
+ ret = IO.copy_stream(src, dst)
+ assert_equal(content.bytesize, ret)
+ assert_equal(content, File.read("dst"))
+ end
+ }
+ end
+
def test_copy_stream_smaller
with_srccontent {|src, content|
@@ -532,6 +560,22 @@ class TestIO < Test::Unit::TestCase
end
if have_nonblock?
+ def test_copy_stream_no_busy_wait
+ skip "MJIT has busy wait on GC. This sometimes fails with --jit." if RubyVM::MJIT.enabled?
+ skip "multiple threads already active" if Thread.list.size > 1
+
+ msg = 'r58534 [ruby-core:80969] [Backport #13533]'
+ IO.pipe do |r,w|
+ r.nonblock = true
+ assert_cpu_usage_low(msg) do
+ th = Thread.new { IO.copy_stream(r, IO::NULL) }
+ sleep 0.1
+ w.close
+ th.join
+ end
+ end
+ end
+
def test_copy_stream_pipe_nonblock
mkcdtmpdir {
with_read_pipe("abc") {|r1|
@@ -541,7 +585,6 @@ class TestIO < Test::Unit::TestCase
w2.nonblock = true
rescue Errno::EBADF
skip "nonblocking IO for pipe is not implemented"
- break
end
s = w2.syswrite("a" * 100000)
t = Thread.new { sleep 0.1; r2.read }
@@ -607,7 +650,7 @@ class TestIO < Test::Unit::TestCase
assert_equal(bigcontent[30, 40], File.read("bigdst"))
assert_equal(0, f.pos)
rescue NotImplementedError
- #skip "pread(2) is not implemtented."
+ #skip "pread(2) is not implemented."
end
}
}
@@ -839,20 +882,21 @@ class TestIO < Test::Unit::TestCase
rescue Errno::EBADF
skip "nonblocking IO for pipe is not implemented"
end
- trapping_usr1 do
+ trapping_usr2 do |rd|
nr = 30
begin
pid = fork do
s1.close
IO.select([s2])
- Process.kill(:USR1, Process.ppid)
- s2.read
+ Process.kill(:USR2, Process.ppid)
+ buf = String.new(capacity: 16384)
+ nil while s2.read(16384, buf)
end
s2.close
nr.times do
assert_equal megacontent.bytesize, IO.copy_stream("megasrc", s1)
end
- assert_equal(1, @usr1_rcvd)
+ assert_equal(1, rd.read(4).unpack1('L'))
ensure
s1.close
_, status = Process.waitpid2(pid) if pid
@@ -1156,10 +1200,11 @@ class TestIO < Test::Unit::TestCase
def test_copy_stream_to_duplex_io
result = IO.pipe {|a,w|
- Thread.start {w.puts "yes"; w.close}
+ th = Thread.start {w.puts "yes"; w.close}
IO.popen([EnvUtil.rubybin, '-pe$_="#$.:#$_"'], "r+") {|b|
IO.copy_stream(a, b)
b.close_write
+ assert_join_threads([th])
b.read
}
}
@@ -1172,7 +1217,7 @@ class TestIO < Test::Unit::TestCase
opts = {}
if defined?(Process::RLIMIT_NPROC)
lim = Process.getrlimit(Process::RLIMIT_NPROC)[1]
- opts[:rlimit_nproc] = [lim, 1024].min
+ opts[:rlimit_nproc] = [lim, 2048].min
end
f = IO.popen([ruby] + args, 'r+', opts)
pid = f.pid
@@ -1203,6 +1248,70 @@ class TestIO < Test::Unit::TestCase
end)
end
+ def test_write_with_multiple_arguments
+ pipe(proc do |w|
+ w.write("foo", "bar")
+ w.close
+ end, proc do |r|
+ assert_equal("foobar", r.read)
+ end)
+ end
+
+ def test_write_with_multiple_arguments_and_buffer
+ mkcdtmpdir do
+ line = "x"*9+"\n"
+ file = "test.out"
+ open(file, "wb") do |w|
+ w.write(line)
+ assert_equal(11, w.write(line, "\n"))
+ end
+ open(file, "rb") do |r|
+ assert_equal([line, line, "\n"], r.readlines)
+ end
+
+ line = "x"*99+"\n"
+ open(file, "wb") do |w|
+ w.write(line*81) # 8100 bytes
+ assert_equal(100, w.write("a"*99, "\n"))
+ end
+ open(file, "rb") do |r|
+ 81.times {assert_equal(line, r.gets)}
+ assert_equal("a"*99+"\n", r.gets)
+ end
+ end
+ end
+
+ def test_write_with_many_arguments
+ [1023, 1024].each do |n|
+ pipe(proc do |w|
+ w.write(*(["a"] * n))
+ w.close
+ end, proc do |r|
+ assert_equal("a" * n, r.read)
+ end)
+ end
+ end
+
+ def test_write_with_multiple_nonstring_arguments
+ assert_in_out_err([], "STDOUT.write(:foo, :bar)", ["foobar"])
+ end
+
+ def test_write_buffered_with_multiple_arguments
+ out, err, (_, status) = EnvUtil.invoke_ruby(["-e", "sleep 0.1;puts 'foo'"], "", true, true) do |_, o, e, i|
+ [o.read, e.read, Process.waitpid2(i)]
+ end
+ assert_predicate(status, :success?)
+ assert_equal("foo\n", out)
+ assert_empty(err)
+ end
+
+ def test_write_no_args
+ IO.pipe do |r, w|
+ assert_equal 0, w.write, '[ruby-core:86285] [Bug #14338]'
+ assert_equal :wait_readable, r.read_nonblock(1, exception: false)
+ end
+ end
+
def test_write_non_writable
with_pipe do |r, w|
assert_raise(IOError) do
@@ -1768,7 +1877,6 @@ class TestIO < Test::Unit::TestCase
def test_pos
make_tempfile {|t|
-
open(t.path, IO::RDWR|IO::CREAT|IO::TRUNC, 0600) do |f|
f.write "Hello"
assert_equal(5, f.pos)
@@ -2051,6 +2159,10 @@ class TestIO < Test::Unit::TestCase
end
def test_autoclose_true_closed_by_finalizer
+ # http://ci.rvm.jp/results/trunk-mjit@silicon-docker/1465760
+ # http://ci.rvm.jp/results/trunk-mjit@silicon-docker/1469765
+ skip 'this randomly fails with MJIT' if RubyVM::MJIT.enabled?
+
feature2250 = '[ruby-core:26222]'
pre = 'ft2250'
t = Tempfile.new(pre)
@@ -2066,7 +2178,7 @@ class TestIO < Test::Unit::TestCase
assert_raise(Errno::EBADF, feature2250) {t.close}
end
ensure
- t.close!
+ t&.close!
end
def test_autoclose_false_closed_by_finalizer
@@ -2104,6 +2216,22 @@ class TestIO < Test::Unit::TestCase
end
end
+ def test_read_command
+ assert_equal("foo\n", IO.read("|echo foo"))
+ assert_raise(Errno::ENOENT, Errno::EINVAL) do
+ File.read("|#{EnvUtil.rubybin} -e puts")
+ end
+ assert_raise(Errno::ENOENT, Errno::EINVAL) do
+ File.binread("|#{EnvUtil.rubybin} -e puts")
+ end
+ assert_raise(Errno::ENOENT, Errno::EINVAL) do
+ Class.new(IO).read("|#{EnvUtil.rubybin} -e puts")
+ end
+ assert_raise(Errno::ENOENT, Errno::EINVAL) do
+ Class.new(IO).binread("|#{EnvUtil.rubybin} -e puts")
+ end
+ end
+
def test_reopen
make_tempfile {|t|
open(__FILE__) do |f|
@@ -2145,13 +2273,13 @@ class TestIO < Test::Unit::TestCase
def test_reopen_inherit
mkcdtmpdir {
- system(EnvUtil.rubybin, '-e', <<"End")
+ system(EnvUtil.rubybin, '-e', <<-"End")
f = open("out", "w")
STDOUT.reopen(f)
STDERR.reopen(f)
system(#{EnvUtil.rubybin.dump}, '-e', 'STDOUT.print "out"')
system(#{EnvUtil.rubybin.dump}, '-e', 'STDERR.print "err"')
-End
+ End
assert_equal("outerr", File.read("out"))
}
end
@@ -2212,7 +2340,7 @@ End
t
end
ensure
- t.close(true) if t and block_given?
+ t&.close(true) if block_given?
end
def test_reopen_encoding
@@ -2277,6 +2405,10 @@ End
IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :foo; puts :bar; puts :baz'") {|x| a << x }
assert_equal(["foo\n", "bar\n", "baz\n"], a)
+ a = []
+ IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :zot'", :open_args => ["r"]) {|x| a << x }
+ assert_equal(["zot\n"], a)
+
make_tempfile {|t|
a = []
IO.foreach(t.path) {|x| a << x }
@@ -2388,6 +2520,36 @@ End
end)
end
+ def test_puts_parallel
+ skip "not portable"
+ pipe(proc do |w|
+ threads = []
+ 100.times do
+ threads << Thread.new { w.puts "hey" }
+ end
+ threads.each(&:join)
+ w.close
+ end, proc do |r|
+ assert_equal("hey\n" * 100, r.read)
+ end)
+ end
+
+ def test_puts_old_write
+ capture = String.new
+ def capture.write(str)
+ self << str
+ end
+
+ capture.clear
+ assert_warning(/[.#]write is outdated/) do
+ stdout, $stdout = $stdout, capture
+ puts "hey"
+ ensure
+ $stdout = stdout
+ end
+ assert_equal("hey\n", capture)
+ end
+
def test_display
pipe(proc do |w|
"foo".display(w)
@@ -2404,7 +2566,8 @@ End
assert_in_out_err([], "$> = $stderr\nputs 'foo'", [], %w(foo))
- assert_separately(%w[-Eutf-8], <<-"end;") # do
+ assert_separately(%w[-Eutf-8], "#{<<~"begin;"}\n#{<<~"end;"}")
+ begin;
alias $\u{6a19 6e96 51fa 529b} $stdout
x = eval("class X\u{307b 3052}; self; end".encode("euc-jp"))
assert_raise_with_message(TypeError, /\\$\u{6a19 6e96 51fa 529b} must.*, X\u{307b 3052} given/) do
@@ -2417,7 +2580,6 @@ End
return unless defined?(Fcntl::F_GETFL)
make_tempfile {|t|
-
fd = IO.sysopen(t.path, "w")
assert_kind_of(Integer, fd)
%w[r r+ w+ a+].each do |mode|
@@ -2527,14 +2689,15 @@ __END__
def test_threaded_flush
bug3585 = '[ruby-core:31348]'
- src = %q{\
+ src = "#{<<~"begin;"}\n#{<<~'end;'}"
+ begin;
t = Thread.new { sleep 3 }
Thread.new {sleep 1; t.kill; p 'hi!'}
t.join
- }.gsub(/^\s+/, '')
+ end;
10.times.map do
Thread.start do
- assert_in_out_err([], src) {|stdout, stderr|
+ assert_in_out_err([], src, timeout: 20) {|stdout, stderr|
assert_no_match(/hi.*hi/, stderr.join, bug3585)
}
end
@@ -2542,7 +2705,6 @@ __END__
end
def test_flush_in_finalizer1
- require 'tempfile'
bug3910 = '[ruby-dev:42341]'
tmp = Tempfile.open("bug3910") {|t|
path = t.path
@@ -2568,7 +2730,6 @@ __END__
end
def test_flush_in_finalizer2
- require 'tempfile'
bug3910 = '[ruby-dev:42341]'
Tempfile.open("bug3910") {|t|
path = t.path
@@ -2702,7 +2863,7 @@ __END__
end
def test_fcntl_lock_linux
- pad=0
+ pad = 0
Tempfile.create(self.class.name) do |f|
r, w = IO.pipe
pid = fork do
@@ -2800,15 +2961,41 @@ __END__
$stdin.reopen(r)
r.close
read_thread = Thread.new do
- $stdin.read(1)
+ begin
+ $stdin.read(1)
+ rescue IOError => e
+ e
+ end
end
sleep(0.1) until read_thread.stop?
$stdin.close
- assert_raise(IOError) {read_thread.join}
+ assert_kind_of(IOError, read_thread.value)
end
end;
end
+ def test_single_exception_on_close
+ a = []
+ t = []
+ 10.times do
+ r, w = IO.pipe
+ a << [r, w]
+ t << Thread.new do
+ while r.gets
+ end rescue IOError
+ Thread.current.pending_interrupt?
+ end
+ end
+ a.each do |r, w|
+ w.write(-"\n")
+ w.close
+ r.close
+ end
+ t.each do |th|
+ assert_equal false, th.value, '[ruby-core:81581] [Bug #13632]'
+ end
+ end
+
def test_open_mode
feature4742 = "[ruby-core:36338]"
bug6055 = '[ruby-dev:45268]'
@@ -2985,7 +3172,7 @@ __END__
f.ioctl(tiocgwinsz, winsize)
}
ensure
- f.close if f
+ f&.close
end
end if /^(?:i.?86|x86_64)-linux/ =~ RUBY_PLATFORM
@@ -3081,17 +3268,12 @@ __END__
assert_equal 100, buf.bytesize
- begin
+ msg = /can't modify string; temporarily locked/
+ assert_raise_with_message(RuntimeError, msg) do
buf.replace("")
- rescue RuntimeError => e
- assert_match(/can't modify string; temporarily locked/, e.message)
- Thread.pass
- end until buf.empty?
-
- assert_empty(buf, bug6099)
+ end
assert_predicate(th, :alive?)
w.write(data)
- Thread.pass while th.alive?
th.join
end
assert_equal(data, buf, bug6099)
@@ -3192,7 +3374,7 @@ __END__
}
IO.select(tempfiles)
- }, bug8080, timeout: 30
+ }, bug8080, timeout: 50
end if defined?(Process::RLIMIT_NOFILE)
def test_read_32bit_boundary
@@ -3255,12 +3437,16 @@ __END__
str = ""
IO.pipe {|r,|
- t = Thread.new { r.read(nil, str) }
+ t = Thread.new {
+ assert_raise(RuntimeError) {
+ r.read(nil, str)
+ }
+ }
sleep 0.1 until t.stop?
t.raise
sleep 0.1 while t.alive?
assert_nothing_raised(RuntimeError, bug8669) { str.clear }
- assert_raise(RuntimeError) { t.join }
+ t.join
}
end if /cygwin/ !~ RUBY_PLATFORM
@@ -3269,12 +3455,16 @@ __END__
str = ""
IO.pipe {|r, w|
- t = Thread.new { r.readpartial(4096, str) }
+ t = Thread.new {
+ assert_raise(RuntimeError) {
+ r.readpartial(4096, str)
+ }
+ }
sleep 0.1 until t.stop?
t.raise
sleep 0.1 while t.alive?
assert_nothing_raised(RuntimeError, bug8669) { str.clear }
- assert_raise(RuntimeError) { t.join }
+ t.join
}
end if /cygwin/ !~ RUBY_PLATFORM
@@ -3294,12 +3484,16 @@ __END__
str = ""
IO.pipe {|r, w|
- t = Thread.new { r.sysread(4096, str) }
+ t = Thread.new {
+ assert_raise(RuntimeError) {
+ r.sysread(4096, str)
+ }
+ }
sleep 0.1 until t.stop?
t.raise
sleep 0.1 while t.alive?
assert_nothing_raised(RuntimeError, bug8669) { str.clear }
- assert_raise(RuntimeError) { t.join }
+ t.join
}
end if /cygwin/ !~ RUBY_PLATFORM
@@ -3366,6 +3560,72 @@ __END__
end
end if File::BINARY != 0
+ def test_exclusive_mode
+ make_tempfile do |t|
+ assert_raise(Errno::EEXIST){ open(t.path, 'wx'){} }
+ assert_raise(ArgumentError){ open(t.path, 'rx'){} }
+ assert_raise(ArgumentError){ open(t.path, 'ax'){} }
+ end
+ end
+
+ def test_race_gets_and_close
+ opt = { signal: :ABRT, timeout: 200 }
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}", opt)
+ bug13076 = '[ruby-core:78845] [Bug #13076]'
+ begin;
+ 10.times do |i|
+ a = []
+ t = []
+ 10.times do
+ r,w = IO.pipe
+ a << [r,w]
+ t << Thread.new do
+ begin
+ while r.gets
+ end
+ rescue IOError
+ end
+ end
+ end
+ a.each do |r,w|
+ w.puts "hoge"
+ w.close
+ r.close
+ end
+ t.each do |th|
+ assert_same(th, th.join(2), bug13076)
+ end
+ end
+ end;
+ end
+
+ def test_race_closed_stream
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ bug13158 = '[ruby-core:79262] [Bug #13158]'
+ closed = nil
+ q = Queue.new
+ IO.pipe do |r, w|
+ thread = Thread.new do
+ begin
+ q << true
+ assert_raise_with_message(IOError, /stream closed/) do
+ while r.gets
+ end
+ end
+ ensure
+ closed = r.closed?
+ end
+ end
+ q.pop
+ sleep 0.01 until thread.stop?
+ r.close
+ thread.join
+ assert_equal(true, closed, bug13158 + ': stream should be closed')
+ end
+ end;
+ end
+
if RUBY_ENGINE == "ruby" # implementation details
def test_foreach_rs_conversion
make_tempfile {|t|
@@ -3432,5 +3692,167 @@ __END__
assert_equal(["foo\n", "bar\n", "baz\n"], IO.readlines(t.path))
}
end
+
+ def test_closed_stream_in_rescue
+ assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
+ 10.times do
+ assert_nothing_raised(RuntimeError, /frozen IOError/) do
+ IO.pipe do |r, w|
+ th = Thread.start {r.close}
+ r.gets
+ rescue IOError
+ # swallow pending exceptions
+ begin
+ sleep 0.001
+ rescue IOError
+ retry
+ end
+ ensure
+ th.kill.join
+ end
+ end
+ end
+ end;
+ end
+
+ def test_write_no_garbage
+ skip "multiple threads already active" if Thread.list.size > 1
+ res = {}
+ ObjectSpace.count_objects(res) # creates strings on first call
+ [ 'foo'.b, '*' * 24 ].each do |buf|
+ with_pipe do |r, w|
+ GC.disable
+ begin
+ before = ObjectSpace.count_objects(res)[:T_STRING]
+ n = w.write(buf)
+ s = w.syswrite(buf)
+ after = ObjectSpace.count_objects(res)[:T_STRING]
+ ensure
+ GC.enable
+ end
+ assert_equal before, after,
+ "no strings left over after write [ruby-core:78898] [Bug #13085]: #{ before } strings before write -> #{ after } strings after write"
+ assert_not_predicate buf, :frozen?, 'no inadvertent freeze'
+ assert_equal buf.bytesize, n, 'IO#write wrote expected size'
+ assert_equal s, n, 'IO#syswrite wrote expected size'
+ end
+ end
+ end
+
+ def test_pread
+ make_tempfile { |t|
+ open(t.path) do |f|
+ assert_equal("bar", f.pread(3, 4))
+ buf = "asdf"
+ assert_equal("bar", f.pread(3, 4, buf))
+ assert_equal("bar", buf)
+ assert_raise(EOFError) { f.pread(1, f.size) }
+ end
+ }
+ end if IO.method_defined?(:pread)
+
+ def test_pwrite
+ make_tempfile { |t|
+ open(t.path, IO::RDWR) do |f|
+ assert_equal(3, f.pwrite("ooo", 4))
+ assert_equal("ooo", f.pread(3, 4))
+ end
+ }
+ end if IO.method_defined?(:pread) and IO.method_defined?(:pwrite)
+ end
+
+ def test_select_exceptfds
+ if Etc.uname[:sysname] == 'SunOS' && Etc.uname[:release] == '5.11'
+ skip "Solaris 11 fails this"
+ end
+
+ TCPServer.open('localhost', 0) do |svr|
+ con = TCPSocket.new('localhost', svr.addr[1])
+ acc = svr.accept
+ assert_equal 5, con.send('hello', Socket::MSG_OOB)
+ set = IO.select(nil, nil, [acc], 30)
+ assert_equal([[], [], [acc]], set, 'IO#select exceptions array OK')
+ acc.close
+ con.close
+ end
+ end if Socket.const_defined?(:MSG_OOB)
+
+ def test_recycled_fd_close
+ dot = -'.'
+ IO.pipe do |sig_rd, sig_wr|
+ noex = Thread.new do # everything right and never see exceptions :)
+ until sig_rd.wait_readable(0)
+ IO.pipe do |r, w|
+ th = Thread.new { r.read(1) }
+ w.write(dot)
+
+ assert_same th, th.join(15), '"good" reader timeout'
+ assert_equal(dot, th.value)
+ end
+ end
+ sig_rd.read(4)
+ end
+ 1000.times do |i| # stupid things and make exceptions:
+ IO.pipe do |r,w|
+ th = Thread.new do
+ begin
+ while r.gets
+ end
+ rescue IOError => e
+ e
+ end
+ end
+ Thread.pass until th.stop?
+
+ r.close
+ assert_same th, th.join(30), '"bad" reader timeout'
+ assert_match(/stream closed/, th.value.message)
+ end
+ end
+ sig_wr.write 'done'
+ assert_same noex, noex.join(20), '"good" writer timeout'
+ assert_equal 'done', noex.value ,'r63216'
+ end
+ end
+
+ def test_select_leak
+ # avoid malloc arena explosion from glibc and jemalloc:
+ env = {
+ 'MALLOC_ARENA_MAX' => '1',
+ 'MALLOC_ARENA_TEST' => '1',
+ 'MALLOC_CONF' => 'narenas:1',
+ }
+ assert_no_memory_leak([env], <<-"end;", <<-"end;", rss: true, timeout: 60)
+ r, w = IO.pipe
+ rset = [r]
+ wset = [w]
+ exc = StandardError.new(-"select used to leak on exception")
+ exc.set_backtrace([])
+ Thread.new { IO.select(rset, wset, nil, 0) }.join
+ end;
+ th = Thread.new do
+ Thread.handle_interrupt(StandardError => :on_blocking) do
+ begin
+ IO.select(rset, wset)
+ rescue
+ retry
+ end while true
+ end
+ end
+ 50_000.times do
+ Thread.pass until th.stop?
+ th.raise(exc)
+ end
+ th.kill
+ th.join
+ end;
+ end
+
+ def test_external_encoding_index
+ IO.pipe {|r, w|
+ assert_raise(TypeError) {Marshal.dump(r)}
+ assert_raise(TypeError) {Marshal.dump(w)}
+ }
end
end
diff --git a/test/ruby/test_io_m17n.rb b/test/ruby/test_io_m17n.rb
index cd45329170..416a24ba6b 100644
--- a/test/ruby/test_io_m17n.rb
+++ b/test/ruby/test_io_m17n.rb
@@ -1608,6 +1608,44 @@ EOT
}
end
+
+ def test_binmode_decode_universal_newline
+ with_tmpdir {
+ generate_file("t.txt", "a\n")
+ assert_raise(ArgumentError) {
+ open("t.txt", "rb", newline: :universal) {}
+ }
+ }
+ end
+
+ def test_default_mode_decode_universal_newline_gets
+ with_tmpdir {
+ generate_file("t.crlf", "a\r\nb\r\nc\r\n")
+ open("t.crlf", "r", newline: :universal) {|f|
+ assert_equal("a\n", f.gets)
+ assert_equal("b\n", f.gets)
+ assert_equal("c\n", f.gets)
+ assert_equal(nil, f.gets)
+ }
+
+ generate_file("t.cr", "a\rb\rc\r")
+ open("t.cr", "r", newline: :universal) {|f|
+ assert_equal("a\n", f.gets)
+ assert_equal("b\n", f.gets)
+ assert_equal("c\n", f.gets)
+ assert_equal(nil, f.gets)
+ }
+
+ generate_file("t.lf", "a\nb\nc\n")
+ open("t.lf", "r", newline: :universal) {|f|
+ assert_equal("a\n", f.gets)
+ assert_equal("b\n", f.gets)
+ assert_equal("c\n", f.gets)
+ assert_equal(nil, f.gets)
+ }
+ }
+ end
+
def test_read_newline_conversion_with_encoding_conversion
with_tmpdir {
generate_file("t.utf8.crlf", "a\r\nb\r\n")
@@ -2044,14 +2082,14 @@ EOT
def test_strip_bom
with_tmpdir {
- text = "\uFEFFa"
- stripped = "a"
+ text = "\uFEFF\u0100a"
+ stripped = "\u0100a"
%w/UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE/.each do |name|
path = '%s-bom.txt' % name
content = text.encode(name)
generate_file(path, content)
result = File.read(path, mode: 'rb:BOM|UTF-8')
- assert_equal(content[1].force_encoding("ascii-8bit"),
+ assert_equal(content[1..-1].force_encoding("ascii-8bit"),
result.force_encoding("ascii-8bit"))
result = File.read(path, mode: 'rb:BOM|UTF-8:UTF-8')
assert_equal(Encoding::UTF_8, result.encoding)
@@ -2061,10 +2099,10 @@ EOT
bug3407 = '[ruby-core:30641]'
path = 'UTF-8-bom.txt'
result = File.read(path, encoding: 'BOM|UTF-8')
- assert_equal("a", result.force_encoding("ascii-8bit"), bug3407)
+ assert_equal(stripped.b, result.force_encoding("ascii-8bit"), bug3407)
bug8323 = '[ruby-core:54563] [Bug #8323]'
- expected = "a\xff".force_encoding("utf-8")
+ expected = (stripped.b + "\xff").force_encoding("utf-8")
open(path, 'ab') {|f| f.write("\xff")}
result = File.read(path, encoding: 'BOM|UTF-8')
assert_not_predicate(result, :valid_encoding?, bug8323)
@@ -2083,12 +2121,14 @@ EOT
end
def test_bom_too_long_utfname
- assert_separately([], <<-'end;') # do
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
assert_warn(/Unsupported encoding/) {
open(IO::NULL, "r:bom|utf-" + "x" * 10000) {}
}
end;
- assert_separately([], <<-'end;') # do
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
assert_warn(/Unsupported encoding/) {
open(IO::NULL, encoding: "bom|utf-" + "x" * 10000) {}
}
@@ -2130,6 +2170,20 @@ EOT
assert_nil(enc)
end
+ def test_bom_non_reading
+ with_tmpdir {
+ enc = nil
+ assert_nothing_raised(IOError) {
+ open("test", "w:bom|utf-8") {|f|
+ enc = f.external_encoding
+ f.print("abc")
+ }
+ }
+ assert_equal(Encoding::UTF_8, enc)
+ assert_equal("abc", File.binread("test"))
+ }
+ end
+
def test_cbuf
with_tmpdir {
fn = "tst"
@@ -2237,7 +2291,7 @@ EOT
w.binmode
w.puts(0x010a.chr(Encoding::UTF_32BE))
w.puts(0x010a.chr(Encoding::UTF_16BE))
- w.puts(0x0a010000.chr(Encoding::UTF_32LE))
+ w.puts(0x0a01.chr(Encoding::UTF_32LE))
w.puts(0x0a01.chr(Encoding::UTF_16LE))
w.close
end,
@@ -2245,7 +2299,7 @@ EOT
r.binmode
assert_equal("\x00\x00\x01\x0a\n", r.read(5), bug)
assert_equal("\x01\x0a\n", r.read(3), bug)
- assert_equal("\x00\x00\x01\x0a\n", r.read(5), bug)
+ assert_equal("\x01\x0a\x00\x00\n", r.read(5), bug)
assert_equal("\x01\x0a\n", r.read(3), bug)
assert_equal("", r.read, bug)
r.close
diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb
index df71e1821e..9de241c485 100644
--- a/test/ruby/test_iseq.rb
+++ b/test/ruby/test_iseq.rb
@@ -1,6 +1,8 @@
require 'test/unit'
require 'tempfile'
+return
+
class TestISeq < Test::Unit::TestCase
ISeq = RubyVM::InstructionSequence
@@ -91,6 +93,11 @@ class TestISeq < Test::Unit::TestCase
asm = compile(src).disasm
assert_equal(src.encoding, asm.encoding)
assert_predicate(asm, :valid_encoding?)
+
+ obj = Object.new
+ name = "\u{2603 26a1}"
+ obj.instance_eval("def #{name}; tap {}; end")
+ assert_include(RubyVM::InstructionSequence.of(obj.method(name)).disasm, name)
end
LINE_BEFORE_METHOD = __LINE__
@@ -186,12 +193,14 @@ class TestISeq < Test::Unit::TestCase
assert_predicate(s4, :frozen?)
end
+ # Safe call chain is not optimized when Coverage is running.
+ # So we can test it only when Coverage is not running.
def test_safe_call_chain
src = "a&.a&.a&.a&.a&.a"
body = compile(src, __LINE__, {peephole_optimization: true}).to_a[13]
labels = body.select {|op, arg| op == :branchnil}.map {|op, arg| arg}
assert_equal(1, labels.uniq.size)
- end
+ end if (!defined?(Coverage) || !Coverage.running?)
def test_parent_iseq_mark
assert_separately([], <<-'end;', timeout: 20)
@@ -249,7 +258,7 @@ class TestISeq < Test::Unit::TestCase
f.puts "end"
f.close
path = f.path
- assert_in_out_err(%W[- #{path}], "#{<<-"begin;"}\n#{<<-"end;"}", /keyword_end/, [], success: true)
+ assert_in_out_err(%W[- #{path}], "#{<<-"begin;"}\n#{<<-"end;"}", /unexpected end/, [], success: true)
begin;
path = ARGV[0]
begin
@@ -270,4 +279,261 @@ class TestISeq < Test::Unit::TestCase
assert_equal(0, eval("0"))
end;
end
+
+ def test_inspect
+ %W[foo \u{30d1 30b9}].each do |name|
+ assert_match /@#{name}/, ISeq.compile("", name).inspect, name
+ m = ISeq.compile("class TestISeq::Inspect; def #{name}; end; instance_method(:#{name}); end").eval
+ assert_match /:#{name}@/, ISeq.of(m).inspect, name
+ end
+ end
+
+ def strip_lineno(source)
+ source.gsub(/^.*?: /, "")
+ end
+
+ def sample_iseq
+ ISeq.compile(strip_lineno(<<-EOS))
+ 1: class C
+ 2: def foo
+ 3: begin
+ 4: rescue
+ 5: p :rescue
+ 6: ensure
+ 7: p :ensure
+ 8: end
+ 9: end
+ 10: def bar
+ 11: 1.times{
+ 12: 2.times{
+ 13: }
+ 14: }
+ 15: end
+ 16: end
+ 17: class D < C
+ 18: end
+ EOS
+ end
+
+ def test_each_child
+ iseq = sample_iseq
+
+ collect_iseq = lambda{|iseq|
+ iseqs = []
+ iseq.each_child{|child_iseq|
+ iseqs << collect_iseq.call(child_iseq)
+ }
+ ["#{iseq.label}@#{iseq.first_lineno}", *iseqs.sort_by{|k, *| k}]
+ }
+
+ expected = ["<compiled>@1",
+ ["<class:C>@1",
+ ["bar@10", ["block in bar@11",
+ ["block (2 levels) in bar@12"]]],
+ ["foo@2", ["ensure in foo@2"],
+ ["rescue in foo@4"]]],
+ ["<class:D>@17"]]
+
+ assert_equal expected, collect_iseq.call(iseq)
+ end
+
+ def test_trace_points
+ collect_iseq = lambda{|iseq|
+ iseqs = []
+ iseq.each_child{|child_iseq|
+ iseqs << collect_iseq.call(child_iseq)
+ }
+ [["#{iseq.label}@#{iseq.first_lineno}", iseq.trace_points], *iseqs.sort_by{|k, *| k}]
+ }
+ assert_equal [["<compiled>@1", [[1, :line],
+ [17, :line]]],
+ [["<class:C>@1", [[1, :class],
+ [2, :line],
+ [10, :line],
+ [16, :end]]],
+ [["bar@10", [[10, :call],
+ [11, :line],
+ [15, :return]]],
+ [["block in bar@11", [[11, :b_call],
+ [12, :line],
+ [14, :b_return]]],
+ [["block (2 levels) in bar@12", [[12, :b_call],
+ [13, :b_return]]]]]],
+ [["foo@2", [[2, :call],
+ [4, :line],
+ [7, :line],
+ [9, :return]]],
+ [["ensure in foo@2", [[7, :line]]]],
+ [["rescue in foo@4", [[5, :line]]]]]],
+ [["<class:D>@17", [[17, :class],
+ [18, :end]]]]], collect_iseq.call(sample_iseq)
+ end
+
+ def test_empty_iseq_lineno
+ iseq = ISeq.compile(<<-EOS)
+ # 1
+ # 2
+ def foo # line 3 empty method
+ end # line 4
+ 1.time do # line 5 empty block
+ end # line 6
+ class C # line 7 empty class
+ end
+ EOS
+
+ iseq.each_child{|ci|
+ ary = ci.to_a
+ type = ary[9]
+ name = ary[5]
+ line = ary[13].first
+ case ary[9]
+ when :method
+ assert_equal "foo", name
+ assert_equal 3, line
+ when :class
+ assert_equal '<class:C>', name
+ assert_equal 7, line
+ when :block
+ assert_equal 'block in <compiled>', name
+ assert_equal 5, line
+ else
+ raise "unknown ary: " + ary.inspect
+ end
+ }
+ end
+
+ def hexdump(bin)
+ bin.unpack1("H*").gsub(/.{1,32}/) {|s|
+ "#{'%04x:' % $~.begin(0)}#{s.gsub(/../, " \\&").tap{|_|_[24]&&="-"}}\n"
+ }
+ end
+
+ def assert_iseq_to_binary(code, mesg = nil)
+ iseq = RubyVM::InstructionSequence.compile(code)
+ bin = assert_nothing_raised(mesg) do
+ iseq.to_binary
+ rescue RuntimeError => e
+ skip e.message if /compile with coverage/ =~ e.message
+ raise
+ end
+ 10.times do
+ bin2 = iseq.to_binary
+ assert_equal(bin, bin2, message(mesg) {diff hexdump(bin), hexdump(bin2)})
+ end
+ iseq2 = RubyVM::InstructionSequence.load_from_binary(bin)
+ a1 = iseq.to_a
+ a2 = iseq2.to_a
+ assert_equal(a1, a2, message(mesg) {diff iseq.disassemble, iseq2.disassemble})
+ iseq2
+ end
+
+ def test_to_binary_with_objects
+ assert_iseq_to_binary("[]"+100.times.map{|i|"<</#{i}/"}.join)
+ assert_iseq_to_binary("@x ||= (1..2)")
+ end
+
+ def test_to_binary_line_info
+ assert_iseq_to_binary("#{<<~"begin;"}\n#{<<~'end;'}", '[Bug #14660]').eval
+ begin;
+ class P
+ def p; end
+ def q; end
+ E = ""
+ N = "#{E}"
+ attr_reader :i
+ end
+ end;
+ end
+
+ def collect_from_binary_tracepoint_lines(tracepoint_type, filename)
+ iseq = RubyVM::InstructionSequence.compile(strip_lineno(<<-RUBY), filename)
+ class A
+ class B
+ 2.times {
+ def self.foo
+ a = 'good day'
+ raise
+ rescue
+ 'dear reader'
+ end
+ }
+ end
+ B.foo
+ end
+ RUBY
+
+ iseq_bin = iseq.to_binary
+ lines = []
+ TracePoint.new(tracepoint_type){|tp|
+ next unless tp.path == filename
+ lines << tp.lineno
+ }.enable{
+ ISeq.load_from_binary(iseq_bin).eval
+ }
+
+ lines
+ end
+
+ def test_to_binary_line_tracepoint
+ filename = "#{File.basename(__FILE__)}_#{__LINE__}"
+ lines = collect_from_binary_tracepoint_lines(:line, filename)
+
+ assert_equal [1, 2, 3, 4, 4, 12, 5, 6, 8], lines, '[Bug #14702]'
+ end
+
+ def test_to_binary_class_tracepoint
+ filename = "#{File.basename(__FILE__)}_#{__LINE__}"
+ lines = collect_from_binary_tracepoint_lines(:class, filename)
+
+ assert_equal [1, 2], lines, '[Bug #14702]'
+ end
+
+ def test_to_binary_end_tracepoint
+ filename = "#{File.basename(__FILE__)}_#{__LINE__}"
+ lines = collect_from_binary_tracepoint_lines(:end, filename)
+
+ assert_equal [11, 13], lines, '[Bug #14702]'
+ end
+
+ def test_to_binary_return_tracepoint
+ filename = "#{File.basename(__FILE__)}_#{__LINE__}"
+ lines = collect_from_binary_tracepoint_lines(:return, filename)
+
+ assert_equal [9], lines, '[Bug #14702]'
+ end
+
+ def test_to_binary_b_call_tracepoint
+ filename = "#{File.basename(__FILE__)}_#{__LINE__}"
+ lines = collect_from_binary_tracepoint_lines(:b_call, filename)
+
+ assert_equal [3, 3], lines, '[Bug #14702]'
+ end
+
+ def test_to_binary_b_return_tracepoint
+ filename = "#{File.basename(__FILE__)}_#{__LINE__}"
+ lines = collect_from_binary_tracepoint_lines(:b_return, filename)
+
+ assert_equal [10, 10], lines, '[Bug #14702]'
+ end
+
+ def test_iseq_of
+ [proc{},
+ method(:test_iseq_of),
+ RubyVM::InstructionSequence.compile("p 1", __FILE__)].each{|src|
+ iseq = RubyVM::InstructionSequence.of(src)
+ assert_equal __FILE__, iseq.path
+ }
+ end
+
+ def test_iseq_of_twice_for_same_code
+ [proc{},
+ method(:test_iseq_of_twice_for_same_code),
+ RubyVM::InstructionSequence.compile("p 1")].each{|src|
+ iseq1 = RubyVM::InstructionSequence.of(src)
+ iseq2 = RubyVM::InstructionSequence.of(src)
+
+ # ISeq objects should be same for same src
+ assert_equal iseq1.object_id, iseq2.object_id
+ }
+ end
end
diff --git a/test/ruby/test_iterator.rb b/test/ruby/test_iterator.rb
index 4a9cfe46f0..ebfb60354a 100644
--- a/test/ruby/test_iterator.rb
+++ b/test/ruby/test_iterator.rb
@@ -107,6 +107,16 @@ class TestIterator < Test::Unit::TestCase
assert_equal([1, 2, 3, 4, 5, 6, 7], x)
end
+ def test_array_for_masgn
+ a = [Struct.new(:to_ary).new([1,2])]
+ x = []
+ a.each {|i,j|x << [i,j]}
+ assert_equal([[1,2]], x)
+ x = []
+ for i,j in a; x << [i,j]; end
+ assert_equal([[1,2]], x)
+ end
+
def test_append_method_to_built_in_class
x = [[1,2],[3,4],[5,6]]
assert_equal(x.iter_test1{|e|e}, x.iter_test2{|e|e})
@@ -279,6 +289,9 @@ class TestIterator < Test::Unit::TestCase
def proc_call(&b)
b.call
end
+ def proc_call2(b)
+ b.call
+ end
def proc_yield()
yield
end
@@ -300,6 +313,7 @@ class TestIterator < Test::Unit::TestCase
def test_ljump
assert_raise(LocalJumpError) {get_block{break}.call}
+ assert_raise(LocalJumpError) {proc_call2(get_block{break}){}}
# cannot use assert_nothing_raised due to passing block.
begin
diff --git a/test/ruby/test_jit.rb b/test/ruby/test_jit.rb
new file mode 100644
index 0000000000..fb63d4087c
--- /dev/null
+++ b/test/ruby/test_jit.rb
@@ -0,0 +1,1047 @@
+# frozen_string_literal: true
+require 'test/unit'
+require 'tmpdir'
+require_relative '../lib/jit_support'
+
+# Test for --jit option
+class TestJIT < Test::Unit::TestCase
+ include JITSupport
+
+ IGNORABLE_PATTERNS = [
+ /\ASuccessful MJIT finish\n\z/,
+ ]
+ MAX_CACHE_PATTERNS = [
+ /\AJIT compaction \([^)]+\): .+\n\z/,
+ /\ANo units can be unloaded -- .+\n\z/,
+ ]
+
+ # trace_* insns are not compiled for now...
+ TEST_PENDING_INSNS = RubyVM::INSTRUCTION_NAMES.select { |n| n.start_with?('trace_') }.map(&:to_sym) + [
+ # not supported yet
+ :getblockparamproxy,
+ :defineclass,
+ :opt_call_c_function,
+
+ # joke
+ :bitblt,
+ :answer,
+
+ # TODO: write tests for them
+ :reput,
+ :tracecoverage,
+ ]
+
+ def self.untested_insns
+ @untested_insns ||= (RubyVM::INSTRUCTION_NAMES.map(&:to_sym) - TEST_PENDING_INSNS)
+ end
+
+ def setup
+ unless JITSupport.supported?
+ skip 'JIT seems not supported on this platform'
+ end
+
+ # ruby -w -Itest/lib test/ruby/test_jit.rb
+ if $VERBOSE && !defined?(@@at_exit_hooked)
+ at_exit do
+ unless TestJIT.untested_insns.empty?
+ warn "untested insns are found!: #{TestJIT.untested_insns.join(' ')}"
+ end
+ end
+ @@at_exit_hooked = true
+ end
+ end
+
+ def test_compile_insn_nop
+ assert_compile_once('nil rescue true', result_inspect: 'nil', insns: %i[nop])
+ end
+
+ def test_compile_insn_local
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1', insns: %i[setlocal_WC_0 getlocal_WC_0])
+ begin;
+ foo = 1
+ foo
+ end;
+
+ insns = %i[setlocal getlocal setlocal_WC_0 getlocal_WC_0 setlocal_WC_1 getlocal_WC_1]
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", success_count: 3, stdout: '168', insns: insns)
+ begin;
+ def foo
+ a = 0
+ [1, 2].each do |i|
+ a += i
+ [3, 4].each do |j|
+ a *= j
+ end
+ end
+ a
+ end
+
+ print foo
+ end;
+ end
+
+ def test_compile_insn_blockparam
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '3', success_count: 2, insns: %i[getblockparam setblockparam])
+ begin;
+ def foo(&b)
+ a = b
+ b = 2
+ a.call + 2
+ end
+
+ print foo { 1 }
+ end;
+ end
+
+ def test_compile_insn_getblockparamproxy
+ skip "support this in mjit_compile"
+ end
+
+ def test_compile_insn_getspecial
+ assert_compile_once('$1', result_inspect: 'nil', insns: %i[getspecial])
+ end
+
+ def test_compile_insn_setspecial
+ verbose_bak, $VERBOSE = $VERBOSE, nil
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: 'true', insns: %i[setspecial])
+ begin;
+ true if nil.nil?..nil.nil?
+ end;
+ ensure
+ $VERBOSE = verbose_bak
+ end
+
+ def test_compile_insn_instancevariable
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1', insns: %i[getinstancevariable setinstancevariable])
+ begin;
+ @foo = 1
+ @foo
+ end;
+
+ # optimized getinstancevariable call
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '33', success_count: 1, min_calls: 2)
+ begin;
+ class A
+ def initialize
+ @a = 1
+ @b = 2
+ end
+
+ def three
+ @a + @b
+ end
+ end
+
+ a = A.new
+ print(a.three) # set ic
+ print(a.three) # inlined ic
+ end;
+ end
+
+ def test_compile_insn_classvariable
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '1', success_count: 1, insns: %i[getclassvariable setclassvariable])
+ begin;
+ class Foo
+ def self.foo
+ @@foo = 1
+ @@foo
+ end
+ end
+
+ print Foo.foo
+ end;
+ end
+
+ def test_compile_insn_constant
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1', insns: %i[getconstant setconstant])
+ begin;
+ FOO = 1
+ FOO
+ end;
+ end
+
+ def test_compile_insn_global
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1', insns: %i[getglobal setglobal])
+ begin;
+ $foo = 1
+ $foo
+ end;
+ end
+
+ def test_compile_insn_putnil
+ assert_compile_once('nil', result_inspect: 'nil', insns: %i[putnil])
+ end
+
+ def test_compile_insn_putself
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'hello', success_count: 1, insns: %i[putself])
+ begin;
+ proc { print "hello" }.call
+ end;
+ end
+
+ def test_compile_insn_putobject
+ assert_compile_once('0', result_inspect: '0', insns: %i[putobject_INT2FIX_0_])
+ assert_compile_once('1', result_inspect: '1', insns: %i[putobject_INT2FIX_1_])
+ assert_compile_once('2', result_inspect: '2', insns: %i[putobject])
+ end
+
+ def test_compile_insn_putspecialobject_putiseq
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'hellohello', success_count: 2, insns: %i[putspecialobject putiseq])
+ begin;
+ print 2.times.map {
+ def method_definition
+ 'hello'
+ end
+ method_definition
+ }.join
+ end;
+ end
+
+ def test_compile_insn_putstring_concatstrings_tostring
+ assert_compile_once('"a#{}b" + "c"', result_inspect: '"abc"', insns: %i[putstring concatstrings tostring])
+ end
+
+ def test_compile_insn_freezestring
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~'end;'}", stdout: 'true', success_count: 1, insns: %i[freezestring])
+ begin;
+ # frozen_string_literal: true
+ print proc { "#{true}".frozen? }.call
+ end;
+ end
+
+ def test_compile_insn_toregexp
+ assert_compile_once('/#{true}/ =~ "true"', result_inspect: '0', insns: %i[toregexp])
+ end
+
+ def test_compile_insn_newarray
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '[1, 2, 3]', insns: %i[newarray])
+ begin;
+ a, b, c = 1, 2, 3
+ [a, b, c]
+ end;
+ end
+
+ def test_compile_insn_intern_duparray
+ assert_compile_once('[:"#{0}"] + [1,2,3]', result_inspect: '[:"0", 1, 2, 3]', insns: %i[intern duparray])
+ end
+
+ def test_compile_insn_expandarray
+ assert_compile_once('y = [ true, false, nil ]; x, = y; x', result_inspect: 'true', insns: %i[expandarray])
+ end
+
+ def test_compile_insn_concatarray
+ assert_compile_once('["t", "r", *x = "u", "e"].join', result_inspect: '"true"', insns: %i[concatarray])
+ end
+
+ def test_compile_insn_splatarray
+ assert_compile_once('[*(1..2)]', result_inspect: '[1, 2]', insns: %i[splatarray])
+ end
+
+ def test_compile_insn_newhash
+ assert_compile_once('a = 1; { a: a }', result_inspect: '{:a=>1}', insns: %i[newhash])
+ end
+
+ def test_compile_insn_duphash
+ assert_compile_once('{ a: 1 }', result_inspect: '{:a=>1}', insns: %i[duphash])
+ end
+
+ def test_compile_insn_newrange
+ assert_compile_once('a = 1; 0..a', result_inspect: '0..1', insns: %i[newrange])
+ end
+
+ def test_compile_insn_pop
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1', insns: %i[pop])
+ begin;
+ a = false
+ b = 1
+ a || b
+ end;
+ end
+
+ def test_compile_insn_dup
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '3', insns: %i[dup])
+ begin;
+ a = 1
+ a&.+(2)
+ end;
+ end
+
+ def test_compile_insn_dupn
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: 'true', insns: %i[dupn])
+ begin;
+ klass = Class.new
+ klass::X ||= true
+ end;
+ end
+
+ def test_compile_insn_swap_topn
+ assert_compile_once('{}["true"] = true', result_inspect: 'true', insns: %i[swap topn])
+ end
+
+ def test_compile_insn_reverse
+ assert_compile_once('q, (w, e), r = 1, [2, 3], 4; [q, w, e, r]', result_inspect: '[1, 2, 3, 4]', insns: %i[reverse])
+ end
+
+ def test_compile_insn_reput
+ skip "write test"
+ end
+
+ def test_compile_insn_setn
+ assert_compile_once('[nil][0] = 1', result_inspect: '1', insns: %i[setn])
+ end
+
+ def test_compile_insn_adjuststack
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: 'true', insns: %i[adjuststack])
+ begin;
+ x = [true]
+ x[0] ||= nil
+ x[0]
+ end;
+ end
+
+ def test_compile_insn_defined
+ assert_compile_once('defined?(a)', result_inspect: 'nil', insns: %i[defined])
+ end
+
+ def test_compile_insn_checkkeyword
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'true', success_count: 1, insns: %i[checkkeyword])
+ begin;
+ def test(x: rand)
+ x
+ end
+ print test(x: true)
+ end;
+ end
+
+ def test_compile_insn_tracecoverage
+ skip "write test"
+ end
+
+ def test_compile_insn_defineclass
+ skip "support this in mjit_compile (low priority)"
+ end
+
+ def test_compile_insn_send
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '1', success_count: 2, insns: %i[send])
+ begin;
+ print proc { yield_self { 1 } }.call
+ end;
+ end
+
+ def test_compile_insn_opt_str_freeze
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '"foo"', insns: %i[opt_str_freeze])
+ begin;
+ 'foo'.freeze
+ end;
+ end
+
+ def test_compile_insn_opt_str_uminus
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '"bar"', insns: %i[opt_str_uminus])
+ begin;
+ -'bar'
+ end;
+ end
+
+ def test_compile_insn_opt_newarray_max
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '2', insns: %i[opt_newarray_max])
+ begin;
+ a = 1
+ b = 2
+ [a, b].max
+ end;
+ end
+
+ def test_compile_insn_opt_newarray_min
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1', insns: %i[opt_newarray_min])
+ begin;
+ a = 1
+ b = 2
+ [a, b].min
+ end;
+ end
+
+ def test_compile_insn_opt_send_without_block
+ assert_compile_once('print', result_inspect: 'nil', insns: %i[opt_send_without_block])
+ end
+
+ def test_compile_insn_invokesuper
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '3', success_count: 4, insns: %i[invokesuper])
+ begin;
+ mod = Module.new {
+ def test
+ super + 2
+ end
+ }
+ klass = Class.new {
+ prepend mod
+ def test
+ 1
+ end
+ }
+ print klass.new.test
+ end;
+ end
+
+ def test_compile_insn_invokeblock_leave
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '2', success_count: 2, insns: %i[invokeblock leave])
+ begin;
+ def foo
+ yield
+ end
+ print foo { 2 }
+ end;
+ end
+
+ def test_compile_insn_throw
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '4', success_count: 2, insns: %i[throw])
+ begin;
+ def test
+ proc do
+ if 1+1 == 1
+ return 3
+ else
+ return 4
+ end
+ 5
+ end.call
+ end
+ print test
+ end;
+ end
+
+ def test_compile_insn_jump_branchif
+ assert_compile_once("#{<<~"begin;"}\n#{<<~'end;'}", result_inspect: 'nil', insns: %i[jump branchif])
+ begin;
+ a = false
+ 1 + 1 while a
+ end;
+ end
+
+ def test_compile_insn_branchunless
+ assert_compile_once("#{<<~"begin;"}\n#{<<~'end;'}", result_inspect: '1', insns: %i[branchunless])
+ begin;
+ a = true
+ if a
+ 1
+ else
+ 2
+ end
+ end;
+ end
+
+ def test_compile_insn_branchnil
+ assert_compile_once("#{<<~"begin;"}\n#{<<~'end;'}", result_inspect: '3', insns: %i[branchnil])
+ begin;
+ a = 2
+ a&.+(1)
+ end;
+ end
+
+ def test_compile_insn_checktype
+ assert_compile_once("#{<<~"begin;"}\n#{<<~'end;'}", result_inspect: '"42"', insns: %i[checktype])
+ begin;
+ a = '2'
+ "4#{a}"
+ end;
+ end
+
+ def test_compile_insn_inlinecache
+ assert_compile_once('Struct', result_inspect: 'Struct', insns: %i[opt_getinlinecache opt_setinlinecache])
+ end
+
+ def test_compile_insn_once
+ assert_compile_once('/#{true}/o =~ "true" && $~.to_a', result_inspect: '["true"]', insns: %i[once])
+ end
+
+ def test_compile_insn_checkmatch_opt_case_dispatch
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '"world"', insns: %i[checkmatch opt_case_dispatch])
+ begin;
+ case 'hello'
+ when 'hello'
+ 'world'
+ end
+ end;
+ end
+
+ def test_compile_insn_opt_calc
+ assert_compile_once('4 + 2 - ((2 * 3 / 2) % 2)', result_inspect: '5', insns: %i[opt_plus opt_minus opt_mult opt_div opt_mod])
+ assert_compile_once('4.0 + 2.0 - ((2.0 * 3.0 / 2.0) % 2.0)', result_inspect: '5.0', insns: %i[opt_plus opt_minus opt_mult opt_div opt_mod])
+ assert_compile_once('4 + 2', result_inspect: '6')
+ end
+
+ def test_compile_insn_opt_cmp
+ assert_compile_once('(1 == 1) && (1 != 2)', result_inspect: 'true', insns: %i[opt_eq opt_neq])
+ end
+
+ def test_compile_insn_opt_rel
+ assert_compile_once('1 < 2 && 1 <= 1 && 2 > 1 && 1 >= 1', result_inspect: 'true', insns: %i[opt_lt opt_le opt_gt opt_ge])
+ end
+
+ def test_compile_insn_opt_ltlt
+ assert_compile_once('[1] << 2', result_inspect: '[1, 2]', insns: %i[opt_ltlt])
+ end
+
+ def test_compile_insn_opt_and
+ assert_compile_once('1 & 3', result_inspect: '1', insns: %i[opt_and])
+ end
+
+ def test_compile_insn_opt_or
+ assert_compile_once('1 | 3', result_inspect: '3', insns: %i[opt_or])
+ end
+
+ def test_compile_insn_opt_aref
+ # optimized call (optimized JIT) -> send call
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '21', success_count: 2, min_calls: 1, insns: %i[opt_aref])
+ begin;
+ obj = Object.new
+ def obj.[](h)
+ h
+ end
+
+ block = proc { |h| h[1] }
+ print block.call({ 1 => 2 })
+ print block.call(obj)
+ end;
+
+ # send call -> optimized call (send JIT) -> optimized call
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '122', success_count: 1, min_calls: 2)
+ begin;
+ obj = Object.new
+ def obj.[](h)
+ h
+ end
+
+ block = proc { |h| h[1] }
+ print block.call(obj)
+ print block.call({ 1 => 2 })
+ print block.call({ 1 => 2 })
+ end;
+ end
+
+ def test_compile_insn_opt_aref_with
+ assert_compile_once("{ '1' => 2 }['1']", result_inspect: '2', insns: %i[opt_aref_with])
+ end
+
+ def test_compile_insn_opt_aset
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '5', insns: %i[opt_aset opt_aset_with])
+ begin;
+ hash = { '1' => 2 }
+ (hash['2'] = 2) + (hash[1.to_s] = 3)
+ end;
+ end
+
+ def test_compile_insn_opt_length_size
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '4', insns: %i[opt_length opt_size])
+ begin;
+ array = [1, 2]
+ array.length + array.size
+ end;
+ end
+
+ def test_compile_insn_opt_empty_p
+ assert_compile_once('[].empty?', result_inspect: 'true', insns: %i[opt_empty_p])
+ end
+
+ def test_compile_insn_opt_succ
+ assert_compile_once('1.succ', result_inspect: '2', insns: %i[opt_succ])
+ end
+
+ def test_compile_insn_opt_not
+ assert_compile_once('!!true', result_inspect: 'true', insns: %i[opt_not])
+ end
+
+ def test_compile_insn_opt_regexpmatch1
+ assert_compile_once("/true/ =~ 'true'", result_inspect: '0', insns: %i[opt_regexpmatch1])
+ end
+
+ def test_compile_insn_opt_regexpmatch2
+ assert_compile_once("'true' =~ /true/", result_inspect: '0', insns: %i[opt_regexpmatch2])
+ end
+
+ def test_compile_insn_opt_call_c_function
+ skip "support this in opt_call_c_function (low priority)"
+ end
+
+ def test_jit_output
+ out, err = eval_with_jit('5.times { puts "MJIT" }', verbose: 1, min_calls: 5)
+ assert_equal("MJIT\n" * 5, out)
+ assert_match(/^#{JIT_SUCCESS_PREFIX}: block in <main>@-e:1 -> .+_ruby_mjit_p\d+u\d+\.c$/, err)
+ assert_match(/^Successful MJIT finish$/, err)
+ end
+
+ def test_nothing_to_unload_with_jit_wait
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'hello', success_count: 11, max_cache: 10, ignorable_patterns: MAX_CACHE_PATTERNS)
+ begin;
+ def a1() a2() end
+ def a2() a3() end
+ def a3() a4() end
+ def a4() a5() end
+ def a5() a6() end
+ def a6() a7() end
+ def a7() a8() end
+ def a8() a9() end
+ def a9() a10() end
+ def a10() a11() end
+ def a11() print('hello') end
+ a1
+ end;
+ end
+
+ def test_unload_units_on_fiber
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'hello', success_count: 12, max_cache: 10, ignorable_patterns: MAX_CACHE_PATTERNS)
+ begin;
+ def a1() a2(false); a2(true) end
+ def a2(a) a3(a) end
+ def a3(a) a4(a) end
+ def a4(a) a5(a) end
+ def a5(a) a6(a) end
+ def a6(a) a7(a) end
+ def a7(a) a8(a) end
+ def a8(a) a9(a) end
+ def a9(a) a10(a) end
+ def a10(a)
+ if a
+ Fiber.new { a11 }.resume
+ end
+ end
+ def a11() print('hello') end
+ a1
+ end;
+ end
+
+ def test_unload_units_and_compaction
+ Dir.mktmpdir("jit_test_unload_units_") do |dir|
+ # MIN_CACHE_SIZE is 10
+ out, err = eval_with_jit({"TMPDIR"=>dir}, "#{<<~"begin;"}\n#{<<~'end;'}", verbose: 1, min_calls: 1, max_cache: 10)
+ begin;
+ i = 0
+ while i < 11
+ eval(<<-EOS)
+ def mjit#{i}
+ print #{i}
+ end
+ mjit#{i}
+ EOS
+ i += 1
+ end
+
+ if defined?(fork)
+ # test the child does not try to delete files which are deleted by parent,
+ # and test possible deadlock on fork during MJIT unload and JIT compaction on child
+ Process.waitpid(Process.fork {})
+ end
+ end;
+
+ debug_info = %Q[stdout:\n"""\n#{out}\n"""\n\nstderr:\n"""\n#{err}"""\n]
+ assert_equal('012345678910', out, debug_info)
+ compactions, errs = err.lines.partition do |l|
+ l.match?(/\AJIT compaction \(\d+\.\dms\): Compacted \d+ methods ->/)
+ end
+ 10.times do |i|
+ assert_match(/\A#{JIT_SUCCESS_PREFIX}: mjit#{i}@\(eval\):/, errs[i], debug_info)
+ end
+ assert_equal("Too many JIT code -- 1 units unloaded\n", errs[10], debug_info)
+ assert_match(/\A#{JIT_SUCCESS_PREFIX}: mjit10@\(eval\):/, errs[11], debug_info)
+
+ # On --jit-wait, when the number of JIT-ed code reaches --jit-max-cache,
+ # it should trigger compaction.
+ unless RUBY_PLATFORM.match?(/mswin|mingw/) # compaction is not supported on Windows yet
+ assert_equal(3, compactions.size, debug_info)
+ end
+
+ if appveyor_mswin?
+ # "Permission Denied" error is preventing to remove so file on AppVeyor.
+ warn 'skipped to test directory emptiness in TestJIT#test_unload_units on AppVeyor mswin'
+ else
+ # verify .o files are deleted on unload_units
+ assert_send([Dir, :empty?, dir], debug_info)
+ end
+ end
+ end
+
+ def test_local_stack_on_exception
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '3', success_count: 2)
+ begin;
+ def b
+ raise
+ rescue
+ 2
+ end
+
+ def a
+ # Calling #b should be vm_exec, not direct mjit_exec.
+ # Otherwise `1` on local variable would be purged.
+ 1 + b
+ end
+
+ print a
+ end;
+ end
+
+ def test_local_stack_with_sp_motion_by_blockargs
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '1', success_count: 2)
+ begin;
+ def b(base)
+ 1
+ end
+
+ # This method is simple enough to have false in catch_except_p.
+ # So local_stack_p would be true in JIT compiler.
+ def a
+ m = method(:b)
+
+ # ci->flag has VM_CALL_ARGS_BLOCKARG and cfp->sp is moved in vm_caller_setup_arg_block.
+ # So, for this send insn, JIT-ed code should use cfp->sp instead of local variables for stack.
+ Module.module_eval(&m)
+ end
+
+ print a
+ end;
+ end
+
+ def test_catching_deep_exception
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '1', success_count: 4)
+ begin;
+ def catch_true(paths, prefixes) # catch_except_p: TRUE
+ prefixes.each do |prefix| # catch_except_p: TRUE
+ paths.each do |path| # catch_except_p: FALSE
+ return path
+ end
+ end
+ end
+
+ def wrapper(paths, prefixes)
+ catch_true(paths, prefixes)
+ end
+
+ print wrapper(['1'], ['2'])
+ end;
+ end
+
+ def test_inlined_undefined_ivar
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "bbb", success_count: 2, min_calls: 3)
+ begin;
+ class Foo
+ def initialize
+ @a = :a
+ end
+
+ def bar
+ if @b.nil?
+ @b = :b
+ end
+ end
+ end
+
+ print(Foo.new.bar)
+ print(Foo.new.bar)
+ print(Foo.new.bar)
+ end;
+ end
+
+ def test_inlined_setivar_frozen
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "FrozenError\n", success_count: 1, min_calls: 3)
+ begin;
+ class A
+ def a
+ @a = 1
+ end
+ end
+
+ a = A.new
+ a.a
+ a.a
+ a.a
+ a.freeze
+ begin
+ a.a
+ rescue FrozenError => e
+ p e.class
+ end
+ end;
+ end
+
+ def test_attr_reader
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "4nil\nnil\n6", success_count: 2, min_calls: 2)
+ begin;
+ class A
+ attr_reader :a, :b
+
+ def initialize
+ @a = 2
+ end
+
+ def test
+ a
+ end
+
+ def undefined
+ b
+ end
+ end
+
+ a = A.new
+ print(a.test * a.test)
+ p(a.undefined)
+ p(a.undefined)
+
+ # redefinition
+ class A
+ def test
+ 3
+ end
+ end
+
+ print(2 * a.test)
+ end;
+
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "true", success_count: 1, min_calls: 2)
+ begin;
+ class Hoge
+ attr_reader :foo
+
+ def initialize
+ @foo = []
+ @bar = nil
+ end
+ end
+
+ class Fuga < Hoge
+ def initialize
+ @bar = nil
+ @foo = []
+ end
+ end
+
+ def test(recv)
+ recv.foo.empty?
+ end
+
+ hoge = Hoge.new
+ fuga = Fuga.new
+
+ test(hoge) # VM: cc set index=1
+ test(hoge) # JIT: compile with index=1
+ test(fuga) # JIT -> VM: cc set index=2
+ print test(hoge) # JIT: should use index=1, not index=2 in cc
+ end;
+ end
+
+ def test_jump_to_precompiled_branch
+ assert_eval_with_jit("#{<<~'begin;'}\n#{<<~'end;'}", stdout: ".0", success_count: 1, min_calls: 1)
+ begin;
+ def test(foo)
+ ".#{foo unless foo == 1}" if true
+ end
+ print test(0)
+ end;
+ end
+
+ def test_clean_so
+ if appveyor_mswin?
+ skip 'Removing so file is failing on AppVeyor mswin due to Permission Denied.'
+ end
+ Dir.mktmpdir("jit_test_clean_so_") do |dir|
+ code = "x = 0; 10.times {|i|x+=i}"
+ eval_with_jit({"TMPDIR"=>dir}, code)
+ assert_send([Dir, :empty?, dir])
+ eval_with_jit({"TMPDIR"=>dir}, code, save_temps: true)
+ assert_not_send([Dir, :empty?, dir])
+ end
+ end
+
+ def test_clean_objects_on_exec
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ # TODO: check call stack and close handle of code which is not on stack, and remove objects on best-effort basis
+ skip 'Removing so file being used does not work on Windows'
+ end
+ Dir.mktmpdir("jit_test_clean_objects_on_exec_") do |dir|
+ eval_with_jit({"TMPDIR"=>dir}, "#{<<~"begin;"}\n#{<<~"end;"}", min_calls: 1)
+ begin;
+ def a; end; a
+ exec "true"
+ end;
+ error_message = "Undeleted files:\n #{Dir.glob("#{dir}/*").join("\n ")}\n"
+ assert_send([Dir, :empty?, dir], error_message)
+ end
+ end
+
+ def test_lambda_longjmp
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '5', success_count: 1)
+ begin;
+ fib = lambda do |x|
+ return x if x == 0 || x == 1
+ fib.call(x-1) + fib.call(x-2)
+ end
+ print fib.call(5)
+ end;
+ end
+
+ def test_stack_pointer_with_assignment
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "nil\nnil\n", success_count: 1)
+ begin;
+ 2.times do
+ a, _ = nil
+ p a
+ end
+ end;
+ end
+
+ def test_program_counter_with_regexpmatch
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aa", success_count: 1)
+ begin;
+ 2.times do
+ break if /a/ =~ "ab" && !$~[0]
+ print $~[0]
+ end
+ end;
+ end
+
+ def test_pushed_values_with_opt_aset_with
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "{}{}", success_count: 1)
+ begin;
+ 2.times do
+ print(Thread.current["a"] = {})
+ end
+ end;
+ end
+
+ def test_pushed_values_with_opt_aref_with
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "nil\nnil\n", success_count: 1)
+ begin;
+ 2.times do
+ p(Thread.current["a"])
+ end
+ end;
+ end
+
+ def test_caller_locations_without_catch_table
+ out, _ = eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", min_calls: 1)
+ begin;
+ def b # 2
+ caller_locations.first # 3
+ end # 4
+ # 5
+ def a # 6
+ print # <-- don't leave PC here # 7
+ b # 8
+ end
+ puts a
+ puts a
+ end;
+ lines = out.lines
+ assert_equal("-e:8:in `a'\n", lines[0])
+ assert_equal("-e:8:in `a'\n", lines[1])
+ end
+
+ def test_fork_with_mjit_worker_thread
+ Dir.mktmpdir("jit_test_fork_with_mjit_worker_thread_") do |dir|
+ # min_calls: 2 to skip fork block
+ out, err = eval_with_jit({ "TMPDIR" => dir }, "#{<<~"begin;"}\n#{<<~"end;"}", min_calls: 2, verbose: 1)
+ begin;
+ def before_fork; end
+ def after_fork; end
+
+ before_fork; before_fork # the child should not delete this .o file
+ pid = Process.fork do # this child should not delete shared .pch file
+ after_fork; after_fork # this child does not share JIT-ed after_fork with parent
+ end
+ after_fork; after_fork # this parent does not share JIT-ed after_fork with child
+
+ Process.waitpid(pid)
+ end;
+ success_count = err.scan(/^#{JIT_SUCCESS_PREFIX}:/).size
+ debug_info = "stdout:\n```\n#{out}\n```\n\nstderr:\n```\n#{err}```\n"
+ assert_equal(3, success_count, debug_info)
+
+ # assert no remove error
+ assert_equal("Successful MJIT finish\n" * 2, err.gsub(/^#{JIT_SUCCESS_PREFIX}:[^\n]+\n/, ''), debug_info)
+
+ # ensure objects are deleted
+ assert_send([Dir, :empty?, dir], debug_info)
+ end
+ end if defined?(fork)
+
+ private
+
+ def appveyor_mswin?
+ ENV['APPVEYOR'] == 'True' && RUBY_PLATFORM.match?(/mswin/)
+ end
+
+ # The shortest way to test one proc
+ def assert_compile_once(script, result_inspect:, insns: [], uplevel: 1)
+ if script.match?(/\A\n.+\n\z/m)
+ script = script.gsub(/^/, ' ')
+ else
+ script = " #{script} "
+ end
+ assert_eval_with_jit("p proc {#{script}}.call", stdout: "#{result_inspect}\n", success_count: 1, insns: insns, uplevel: uplevel + 1)
+ end
+
+ # Shorthand for normal test cases
+ def assert_eval_with_jit(script, stdout: nil, success_count:, min_calls: 1, max_cache: 1000, insns: [], uplevel: 1, ignorable_patterns: [])
+ out, err = eval_with_jit(script, verbose: 1, min_calls: min_calls, max_cache: max_cache)
+ actual = err.scan(/^#{JIT_SUCCESS_PREFIX}:/).size
+ # Add --jit-verbose=2 logs for cl.exe because compiler's error message is suppressed
+ # for cl.exe with --jit-verbose=1. See `start_process` in mjit_worker.c.
+ if RUBY_PLATFORM.match?(/mswin/) && success_count != actual
+ out2, err2 = eval_with_jit(script, verbose: 2, min_calls: min_calls, max_cache: max_cache)
+ end
+
+ # Make sure that the script has insns expected to be tested
+ used_insns = method_insns(script)
+ insns.each do |insn|
+ unless used_insns.include?(insn)
+ $stderr.puts
+ warn "'#{insn}' insn is not included in the script. Actual insns are: #{used_insns.join(' ')}\n", uplevel: uplevel+2
+ end
+ TestJIT.untested_insns.delete(insn)
+ end
+
+ assert_equal(
+ success_count, actual,
+ "Expected #{success_count} times of JIT success, but succeeded #{actual} times.\n\n"\
+ "script:\n#{code_block(script)}\nstderr:\n#{code_block(err)}#{(
+ "\nstdout(verbose=2 retry):\n#{code_block(out2)}\nstderr(verbose=2 retry):\n#{code_block(err2)}" if out2 || err2
+ )}",
+ )
+ if stdout
+ assert_equal(stdout, out, "Expected stdout #{out.inspect} to match #{stdout.inspect} with script:\n#{code_block(script)}")
+ end
+ err_lines = err.lines.reject! do |l|
+ l.chomp.empty? || l.match?(/\A#{JIT_SUCCESS_PREFIX}/) || (IGNORABLE_PATTERNS + ignorable_patterns).any? { |pat| pat.match?(l) }
+ end
+ unless err_lines.empty?
+ warn err_lines.join(''), uplevel: uplevel
+ end
+ end
+
+ # Collect block's insns or defined method's insns, which are expected to be JIT-ed.
+ # Note that this intentionally excludes insns in script's toplevel because they are not JIT-ed.
+ def method_insns(script)
+ insns = []
+ RubyVM::InstructionSequence.compile(script).to_a.last.each do |(insn, *args)|
+ case insn
+ when :putiseq, :send
+ insns += collect_insns(args.last)
+ when :defineclass
+ insns += collect_insns(args[1])
+ end
+ end
+ insns.uniq
+ end
+
+ # Recursively collect insns in iseq_array
+ def collect_insns(iseq_array)
+ return [] if iseq_array.nil?
+
+ insns = iseq_array.last.select { |x| x.is_a?(Array) }.map(&:first)
+ iseq_array.last.each do |(insn, *args)|
+ case insn
+ when :putiseq, :send
+ insns += collect_insns(args.last)
+ end
+ end
+ insns
+ end
+end
diff --git a/test/ruby/test_key_error.rb b/test/ruby/test_key_error.rb
new file mode 100644
index 0000000000..fe1d5bb5ab
--- /dev/null
+++ b/test/ruby/test_key_error.rb
@@ -0,0 +1,42 @@
+require 'test/unit'
+
+class TestKeyError < Test::Unit::TestCase
+ def test_default
+ error = KeyError.new
+ assert_equal("KeyError", error.message)
+ end
+
+ def test_message
+ error = KeyError.new("Message")
+ assert_equal("Message", error.message)
+ end
+
+ def test_receiver
+ receiver = Object.new
+ error = KeyError.new(receiver: receiver)
+ assert_equal(receiver, error.receiver)
+ error = KeyError.new
+ assert_raise(ArgumentError) {error.receiver}
+ end
+
+ def test_key
+ error = KeyError.new(key: :key)
+ assert_equal(:key, error.key)
+ error = KeyError.new
+ assert_raise(ArgumentError) {error.key}
+ end
+
+ def test_receiver_and_key
+ receiver = Object.new
+ error = KeyError.new(receiver: receiver, key: :key)
+ assert_equal([receiver, :key],
+ [error.receiver, error.key])
+ end
+
+ def test_all
+ receiver = Object.new
+ error = KeyError.new("Message", receiver: receiver, key: :key)
+ assert_equal(["Message", receiver, :key],
+ [error.message, error.receiver, error.key])
+ end
+end
diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb
index b8ca84c27e..8e2da53bdf 100644
--- a/test/ruby/test_keyword.rb
+++ b/test/ruby/test_keyword.rb
@@ -371,12 +371,8 @@ class TestKeywordArguments < Test::Unit::TestCase
break eval("proc {|a:| a}", nil, 'xyzzy', __LINE__)
end
assert_raise_with_message(ArgumentError, /missing keyword/, feature7701) {b.call}
- assert_raise_with_message(ArgumentError, /unknown keyword/, feature7701) {b.call(a:0, b:1)}
- begin
- b.call(a: 0, b: 1)
- rescue => e
- assert_equal('xyzzy', e.backtrace_locations[0].path)
- end
+ e = assert_raise_with_message(ArgumentError, /unknown keyword/, feature7701) {b.call(a:0, b:1)}
+ assert_equal('xyzzy', e.backtrace_locations[0].path)
assert_equal(42, b.call(a: 42), feature7701)
assert_equal([[:keyreq, :a]], b.parameters, feature7701)
@@ -390,6 +386,15 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([[:keyreq, :a], [:keyrest, :bl]], b.parameters, feature7701)
assert_raise_with_message(ArgumentError, /missing keyword/, bug8139) {b.call(c: bug8139)}
assert_raise_with_message(ArgumentError, /missing keyword/, bug8139) {b.call}
+
+ b = assert_nothing_raised(SyntaxError, feature7701) do
+ break eval("proc {|m, a:| [m, a]}", nil, 'xyzzy', __LINE__)
+ end
+ assert_raise_with_message(ArgumentError, /missing keyword/) {b.call}
+ assert_equal([:ok, 42], b.call(:ok, a: 42))
+ e = assert_raise_with_message(ArgumentError, /unknown keyword/) {b.call(42, a:0, b:1)}
+ assert_equal('xyzzy', e.backtrace_locations[0].path)
+ assert_equal([[:opt, :m], [:keyreq, :a]], b.parameters)
end
def test_super_with_keyword
@@ -498,6 +503,39 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, 9], m1(1, o, &->(a, k: 0) {break [a, k]}), bug10016)
end
+ def test_splat_hash
+ m = Object.new
+ def m.f() :ok; end
+ def m.f1(a) a; end
+ def m.f2(a = nil) a; end
+ def m.f3(**a) a; end
+ def m.f4(*a) a; end
+ o = {a: 1}
+ assert_raise_with_message(ArgumentError, /unknown keyword: a/) {
+ m.f(**o)
+ }
+ o = {}
+ assert_equal(:ok, m.f(**o), '[ruby-core:68124] [Bug #10856]')
+ a = []
+ assert_equal(:ok, m.f(*a, **o), '[ruby-core:83638] [Bug #10856]')
+
+ o = {a: 42}
+ assert_warning('', 'splat to kwrest') do
+ assert_equal({a: 42}, m.f3(**o))
+ end
+ assert_warning('', 'splat to rest') do
+ assert_equal([{a: 42}], m.f4(**o))
+ end
+
+ assert_warning('') do
+ assert_equal({a: 42}, m.f2("a".to_sym => 42), '[ruby-core:82291] [Bug #13793]')
+ end
+
+ o = {}
+ a = [:ok]
+ assert_equal(:ok, m.f2(*a, **o), '[ruby-core:83638] [Bug #10856]')
+ end
+
def test_gced_object_in_stack
bug8964 = '[ruby-dev:47729] [Bug #8964]'
assert_normal_exit %q{
@@ -519,7 +557,8 @@ class TestKeywordArguments < Test::Unit::TestCase
def test_dynamic_symbol_keyword
bug10266 = '[ruby-dev:48564] [Bug #10266]'
- assert_separately(['-', bug10266], <<-'end;') # do
+ assert_separately(['-', bug10266], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
bug = ARGV.shift
"hoge".to_sym
assert_nothing_raised(bug) {eval("def a(hoge:); end")}
@@ -541,6 +580,13 @@ class TestKeywordArguments < Test::Unit::TestCase
}
end
+ def test_unknown_keyword
+ bug13004 = '[ruby-dev:49893] [Bug #13004]'
+ assert_raise_with_message(ArgumentError, /unknown keyword: invalid-argument/, bug13004) {
+ [].sample(random: nil, "invalid-argument": nil)
+ }
+ end
+
def test_super_with_anon_restkeywords
bug10659 = '[ruby-core:67157] [Bug #10659]'
@@ -597,4 +643,106 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal({x: 1, y: 2, **h}, obj.foo)
}
end
+
+ def test_kwrest_overwritten
+ bug13015 = '[ruby-core:78536] [Bug #13015]'
+
+ klass = EnvUtil.labeled_class("Parent") do
+ def initialize(d:)
+ end
+ end
+
+ klass = EnvUtil.labeled_class("Child", klass) do
+ def initialize(d:, **h)
+ h = [2, 3]
+ super
+ end
+ end
+
+ assert_raise_with_message(TypeError, /expected Hash/, bug13015) do
+ klass.new(d: 4)
+ end
+ end
+
+ def test_non_keyword_hash_subclass
+ bug12884 = '[ruby-core:77813] [Bug #12884]'
+ klass = EnvUtil.labeled_class("Child", Hash)
+ obj = Object.new
+ def obj.t(params = klass.new, d: nil); params; end
+ x = klass.new
+ x["foo"] = "bar"
+ result = obj.t(x)
+ assert_equal(x, result)
+ assert_kind_of(klass, result, bug12884)
+ end
+
+ def test_arity_error_message
+ obj = Object.new
+ def obj.t(x:) end
+ assert_raise_with_message(ArgumentError, /required keyword: x\)/) do
+ obj.t(42)
+ end
+ obj = Object.new
+ def obj.t(x:, y:, z: nil) end
+ assert_raise_with_message(ArgumentError, /required keywords: x, y\)/) do
+ obj.t(42)
+ end
+ end
+
+ def many_kwargs(a0: '', a1: '', a2: '', a3: '', a4: '', a5: '', a6: '', a7: '',
+ b0: '', b1: '', b2: '', b3: '', b4: '', b5: '', b6: '', b7: '',
+ c0: '', c1: '', c2: '', c3: '', c4: '', c5: '', c6: '', c7: '',
+ d0: '', d1: '', d2: '', d3: '', d4: '', d5: '', d6: '', d7: '',
+ e0: '')
+ [a0, a1, a2, a3, a4, a5, a6, a7,
+ b0, b1, b2, b3, b4, b5, b6, b7,
+ c0, c1, c2, c3, c4, c5, c6, c7,
+ d0, d1, d2, d3, d4, d5, d6, d7,
+ e0]
+ end
+
+ def test_many_kwargs
+ i = 0
+ assert_equal(:ok, many_kwargs(a0: :ok)[i], "#{i}: a0"); i+=1
+ assert_equal(:ok, many_kwargs(a1: :ok)[i], "#{i}: a1"); i+=1
+ assert_equal(:ok, many_kwargs(a2: :ok)[i], "#{i}: a2"); i+=1
+ assert_equal(:ok, many_kwargs(a3: :ok)[i], "#{i}: a3"); i+=1
+ assert_equal(:ok, many_kwargs(a4: :ok)[i], "#{i}: a4"); i+=1
+ assert_equal(:ok, many_kwargs(a5: :ok)[i], "#{i}: a5"); i+=1
+ assert_equal(:ok, many_kwargs(a6: :ok)[i], "#{i}: a6"); i+=1
+ assert_equal(:ok, many_kwargs(a7: :ok)[i], "#{i}: a7"); i+=1
+
+ assert_equal(:ok, many_kwargs(b0: :ok)[i], "#{i}: b0"); i+=1
+ assert_equal(:ok, many_kwargs(b1: :ok)[i], "#{i}: b1"); i+=1
+ assert_equal(:ok, many_kwargs(b2: :ok)[i], "#{i}: b2"); i+=1
+ assert_equal(:ok, many_kwargs(b3: :ok)[i], "#{i}: b3"); i+=1
+ assert_equal(:ok, many_kwargs(b4: :ok)[i], "#{i}: b4"); i+=1
+ assert_equal(:ok, many_kwargs(b5: :ok)[i], "#{i}: b5"); i+=1
+ assert_equal(:ok, many_kwargs(b6: :ok)[i], "#{i}: b6"); i+=1
+ assert_equal(:ok, many_kwargs(b7: :ok)[i], "#{i}: b7"); i+=1
+
+ assert_equal(:ok, many_kwargs(c0: :ok)[i], "#{i}: c0"); i+=1
+ assert_equal(:ok, many_kwargs(c1: :ok)[i], "#{i}: c1"); i+=1
+ assert_equal(:ok, many_kwargs(c2: :ok)[i], "#{i}: c2"); i+=1
+ assert_equal(:ok, many_kwargs(c3: :ok)[i], "#{i}: c3"); i+=1
+ assert_equal(:ok, many_kwargs(c4: :ok)[i], "#{i}: c4"); i+=1
+ assert_equal(:ok, many_kwargs(c5: :ok)[i], "#{i}: c5"); i+=1
+ assert_equal(:ok, many_kwargs(c6: :ok)[i], "#{i}: c6"); i+=1
+ assert_equal(:ok, many_kwargs(c7: :ok)[i], "#{i}: c7"); i+=1
+
+ assert_equal(:ok, many_kwargs(d0: :ok)[i], "#{i}: d0"); i+=1
+ assert_equal(:ok, many_kwargs(d1: :ok)[i], "#{i}: d1"); i+=1
+ assert_equal(:ok, many_kwargs(d2: :ok)[i], "#{i}: d2"); i+=1
+ assert_equal(:ok, many_kwargs(d3: :ok)[i], "#{i}: d3"); i+=1
+ assert_equal(:ok, many_kwargs(d4: :ok)[i], "#{i}: d4"); i+=1
+ assert_equal(:ok, many_kwargs(d5: :ok)[i], "#{i}: d5"); i+=1
+ assert_equal(:ok, many_kwargs(d6: :ok)[i], "#{i}: d6"); i+=1
+ assert_equal(:ok, many_kwargs(d7: :ok)[i], "#{i}: d7"); i+=1
+
+ assert_equal(:ok, many_kwargs(e0: :ok)[i], "#{i}: e0"); i+=1
+ end
+
+ def test_splat_empty_hash_with_block_passing
+ assert_valid_syntax("bug15087(**{}, &nil)")
+ end
end
diff --git a/test/ruby/test_lambda.rb b/test/ruby/test_lambda.rb
index c65169ac27..3ac2e4cb98 100644
--- a/test/ruby/test_lambda.rb
+++ b/test/ruby/test_lambda.rb
@@ -31,8 +31,10 @@ class TestLambdaParameters < Test::Unit::TestCase
bug9605 = '[ruby-core:61468] [Bug #9605]'
assert_nothing_raised(ArgumentError, bug9605) {1.times(&->(n){ a += 1 })}
assert_equal(3, a, bug9605)
- assert_nothing_raised(ArgumentError, bug9605) {a = [[1, 2]].map(&->(x, y) {x+y})}
- assert_equal([3], a, bug9605)
+ assert_nothing_raised(ArgumentError, bug9605) {
+ a = %w(Hi there how are you).each_with_index.detect(&->(w, i) {w.length == 3})
+ }
+ assert_equal(["how", 2], a, bug9605)
end
def test_call_rest_args
@@ -79,29 +81,29 @@ class TestLambdaParameters < Test::Unit::TestCase
end
end
- def yield_1(arg)
- yield arg
- end
-
- tap do |;bug9605, expected, result|
- bug9605 = '[ruby-core:65887] [Bug #9605] arity check should be relaxed'
- expected = [1,2,3]
-
- [
- ["array", expected],
- ["to_ary", Struct.new(:to_ary).new(expected)],
- ].product \
- [
- ["proc", proc {|a, b, c| [a, b, c]}],
- ["lambda", lambda {|a, b, c| [a, b, c]}],
- ] do
- |(vtype, val), (btype, block)|
- define_method("test_yield_relaxed(#{vtype},&#{btype})") do
- result = assert_nothing_raised(ArgumentError, bug9605) {
- break yield_1(val, &block)
- }
- assert_equal(expected, result, bug9605)
- end
+ def test_instance_eval_return
+ bug13090 = '[ruby-core:78917] [Bug #13090] cannot return in lambdas'
+ x = :ng
+ assert_nothing_raised(LocalJumpError) do
+ x = instance_eval(&->(_){return :ok})
+ end
+ ensure
+ assert_equal(:ok, x, bug13090)
+ end
+
+ def test_instance_exec_return
+ bug13090 = '[ruby-core:78917] [Bug #13090] cannot return in lambdas'
+ x = :ng
+ assert_nothing_raised(LocalJumpError) do
+ x = instance_exec(&->(){return :ok})
+ end
+ ensure
+ assert_equal(:ok, x, bug13090)
+ end
+
+ def test_arity_error
+ assert_raise(ArgumentError, '[Bug #12705]') do
+ [1, 2].tap(&lambda {|a, b|})
end
end
diff --git a/test/ruby/test_lazy_enumerator.rb b/test/ruby/test_lazy_enumerator.rb
index 792a95bb7e..03371c912a 100644
--- a/test/ruby/test_lazy_enumerator.rb
+++ b/test/ruby/test_lazy_enumerator.rb
@@ -14,7 +14,14 @@ class TestLazyEnumerator < Test::Unit::TestCase
def each(*args)
@args = args
- @enum.each {|i| @current = i; yield i}
+ @enum.each do |v|
+ @current = v
+ if v.is_a? Enumerable
+ yield *v
+ else
+ yield v
+ end
+ end
end
end
@@ -25,7 +32,7 @@ class TestLazyEnumerator < Test::Unit::TestCase
a = [1, 2, 3].lazy
a.freeze
- assert_raise(RuntimeError) {
+ assert_raise(FrozenError) {
a.__send__ :initialize, [4, 5], &->(y, *v) { y << yield(*v) }
}
end
@@ -100,6 +107,15 @@ class TestLazyEnumerator < Test::Unit::TestCase
assert_equal(1, a.current)
end
+ def test_map_packed_nested
+ bug = '[ruby-core:81638] [Bug#13648]'
+
+ a = Step.new([[1, 2]])
+ expected = [[[1, 2]]]
+ assert_equal(expected, a.map {|*args| args}.map {|*args| args}.to_a)
+ assert_equal(expected, a.lazy.map {|*args| args}.map {|*args| args}.to_a, bug)
+ end
+
def test_flat_map
a = Step.new(1..3)
assert_equal(2, a.flat_map {|x| [x * 2]}.first)
@@ -485,6 +501,15 @@ EOS
assert_equal Float::INFINITY, loop.lazy.cycle.size
assert_equal nil, lazy.select{}.cycle(4).size
assert_equal nil, lazy.select{}.cycle.size
+
+ class << (obj = Object.new)
+ def each; end
+ def size; 0; end
+ include Enumerable
+ end
+ lazy = obj.lazy
+ assert_equal 0, lazy.cycle.size
+ assert_raise(TypeError) {lazy.cycle("").size}
end
def test_map_zip
@@ -544,4 +569,13 @@ EOS
[1, 2, 3].lazy.map(&:undefined).map(&:to_s).force
end
end
+
+ def test_uniq
+ u = (1..Float::INFINITY).lazy.uniq do |x|
+ raise "too big" if x > 10000
+ (x**2) % 10
+ end
+ assert_equal([1, 2, 3, 4, 5, 10], u.first(6))
+ assert_equal([1, 2, 3, 4, 5, 10], u.first(6))
+ end
end
diff --git a/test/ruby/test_literal.rb b/test/ruby/test_literal.rb
index 05b81f944d..ff40474bf7 100644
--- a/test/ruby/test_literal.rb
+++ b/test/ruby/test_literal.rb
@@ -90,6 +90,9 @@ class TestRubyLiteral < Test::Unit::TestCase
assert_equal "\u201c", eval(%[?\\\u{201c}]), bug6069
assert_equal "\u201c".encode("euc-jp"), eval(%[?\\\u{201c}].encode("euc-jp")), bug6069
assert_equal "\u201c".encode("iso-8859-13"), eval(%[?\\\u{201c}].encode("iso-8859-13")), bug6069
+
+ assert_equal "ab", eval("?a 'b'")
+ assert_equal "a\nb", eval("<<A 'b'\na\nA")
end
def test_dstring
@@ -116,6 +119,21 @@ class TestRubyLiteral < Test::Unit::TestCase
assert_equal :a3c, :"a#{1+2}c"
end
+ def test_dsymbol_redefined_intern
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ class String
+ alias _intern intern
+ def intern
+ "<#{upcase}>"
+ end
+ end
+ mesg = "literal symbol should not be affected by method redefinition"
+ str = "foo"
+ assert_equal(:foo, :"#{str}", mesg)
+ end;
+ end
+
def test_xstring
assert_equal "foo\n", `echo foo`
s = 'foo'
@@ -159,6 +177,37 @@ class TestRubyLiteral < Test::Unit::TestCase
end
end
+ def test_frozen_string_in_array_literal
+ list = eval("# frozen-string-literal: true\n""['foo', 'bar']")
+ assert_equal 2, list.length
+ list.each { |str| assert_predicate str, :frozen? }
+ end
+
+ if defined?(RubyVM::InstructionSequence.compile_option) and
+ RubyVM::InstructionSequence.compile_option.key?(:debug_frozen_string_literal)
+ def test_debug_frozen_string
+ src = 'n = 1; "foo#{n ? "-#{n}" : ""}"'; f = "test.rb"; n = 1
+ opt = {frozen_string_literal: true, debug_frozen_string_literal: true}
+ str = RubyVM::InstructionSequence.compile(src, f, f, n, opt).eval
+ assert_equal("foo-1", str)
+ assert_predicate(str, :frozen?)
+ assert_raise_with_message(FrozenError, /created at #{Regexp.quote(f)}:#{n}/) {
+ str << "x"
+ }
+ end
+
+ def test_debug_frozen_string_in_array_literal
+ src = '["foo"]'; f = "test.rb"; n = 1
+ opt = {frozen_string_literal: true, debug_frozen_string_literal: true}
+ ary = RubyVM::InstructionSequence.compile(src, f, f, n, opt).eval
+ assert_equal("foo", ary.first)
+ assert_predicate(ary.first, :frozen?)
+ assert_raise_with_message(FrozenError, /created at #{Regexp.quote(f)}:#{n}/) {
+ ary.first << "x"
+ } unless ENV['RUBY_ISEQ_DUMP_DEBUG']
+ end
+ end
+
def test_regexp
assert_instance_of Regexp, //
assert_match(//, 'a')
@@ -234,6 +283,24 @@ class TestRubyLiteral < Test::Unit::TestCase
assert_equal "literal", h["string"]
end
+ def test_hash_literal_frozen
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ def frozen_hash_literal_arg
+ {0=>1,1=>4,2=>17}
+ end
+
+ ObjectSpace.each_object(Hash) do |a|
+ if a.class == Hash and !a.default_proc and a.size == 3 &&
+ a[0] == 1 && a[1] == 4 && a[2] == 17
+ # should not be found.
+ raise
+ end
+ end
+ assert_not_include frozen_hash_literal_arg, 3
+ end;
+ end
+
def test_big_array_and_hash_literal
assert_normal_exit %q{GC.disable=true; x = nil; raise if eval("[#{(1..1_000_000).map{'x'}.join(", ")}]").size != 1_000_000}, "", timeout: 300, child_env: %[--disable-gems]
assert_normal_exit %q{GC.disable=true; x = nil; raise if eval("[#{(1..1_000_000).to_a.join(", ")}]").size != 1_000_000}, "", timeout: 300, child_env: %[--disable-gems]
@@ -391,6 +458,35 @@ class TestRubyLiteral < Test::Unit::TestCase
end;
end
+ def test_hash_duplicated_key
+ h = EnvUtil.suppress_warning do
+ eval <<~end
+ # This is a syntax that renders warning at very early stage.
+ # eval used to delay warning, to be suppressible by EnvUtil.
+ {"a" => 100, "b" => 200, "a" => 300, "a" => 400}
+ end
+ end
+ assert_equal(2, h.size)
+ assert_equal(400, h['a'])
+ assert_equal(200, h['b'])
+ assert_nil(h['c'])
+ assert_equal(nil, h.key('300'))
+ end
+
+ def test_hash_frozen_key_id
+ key = "a".freeze
+ h = {key => 100}
+ assert_equal(100, h['a'])
+ assert_same(key, *h.keys)
+ end
+
+ def test_hash_key_tampering
+ key = "a"
+ h = {key => 100}
+ key.upcase!
+ assert_equal(100, h['a'])
+ end
+
def test_range
assert_instance_of Range, (1..2)
assert_equal(1..2, 1..2)
@@ -455,15 +551,12 @@ class TestRubyLiteral < Test::Unit::TestCase
}
}
bug2407 = '[ruby-dev:39798]'
- head.each {|h|
- if /^0/ =~ h
- begin
- eval("#{h}_")
- rescue SyntaxError => e
- assert_match(/numeric literal without digits\Z/, e.message, bug2407)
- end
+ head.grep_v(/^0/) do |s|
+ head.grep(/^0/) do |h|
+ h = "#{s}#{h}_"
+ assert_syntax_error(h, /numeric literal without digits\Z/, "#{bug2407}: #{h.inspect}")
end
- }
+ end
end
def test_float
diff --git a/test/ruby/test_m17n.rb b/test/ruby/test_m17n.rb
index 950d205ae5..75daf61376 100644
--- a/test/ruby/test_m17n.rb
+++ b/test/ruby/test_m17n.rb
@@ -269,6 +269,13 @@ class TestM17N < Test::Unit::TestCase
assert_empty(encs, bug10598)
end
+ def test_utf_without_bom_valid
+ encs = [Encoding::UTF_16, Encoding::UTF_32].find_all {|enc|
+ !(+"abcd").encode!(enc).force_encoding(enc).valid_encoding?
+ }
+ assert_empty(encs)
+ end
+
def test_object_utf16_32_inspect
EnvUtil.suppress_warning do
begin
@@ -358,7 +365,10 @@ class TestM17N < Test::Unit::TestCase
"\u3042".encode("UTF-16LE"),
"\u3042".encode("UTF-16BE"),
].each do |str|
- assert_equal(str, eval(str.dump), "[ruby-dev:33142]")
+ dump = str.dump
+ assert_equal(str, eval(dump), "[ruby-dev:33142]")
+ assert_equal(str, dump.undump)
+ assert_equal(str, eval("# frozen-string-literal: true\n#{dump}"), '[Bug #14687]')
end
end
@@ -465,7 +475,7 @@ class TestM17N < Test::Unit::TestCase
def test_regexp_ascii_none
r = /a/n
- assert_warning(%r{regexp match /.../n against to}) {
+ assert_warning(%r{historical binary regexp match /\.\.\./n against}) {
assert_regexp_generic_ascii(r)
}
@@ -474,13 +484,13 @@ class TestM17N < Test::Unit::TestCase
assert_equal(0, r =~ s("a"))
assert_equal(0, r =~ u("a"))
assert_equal(nil, r =~ a("\xc2\xa1"))
- assert_warning(%r{regexp match /.../n against to EUC-JP string}) {
+ assert_warning(%r{historical binary regexp match /\.\.\./n against EUC-JP string}) {
assert_equal(nil, r =~ e("\xc2\xa1"))
}
- assert_warning(%r{regexp match /.../n against to Windows-31J string}) {
+ assert_warning(%r{historical binary regexp match /\.\.\./n against Windows-31J string}) {
assert_equal(nil, r =~ s("\xc2\xa1"))
}
- assert_warning(%r{regexp match /.../n against to UTF-8 string}) {
+ assert_warning(%r{historical binary regexp match /\.\.\./n against UTF-8 string}) {
assert_equal(nil, r =~ u("\xc2\xa1"))
}
@@ -725,7 +735,7 @@ class TestM17N < Test::Unit::TestCase
def test_union_1_regexp
assert_regexp_generic_ascii(Regexp.union(//))
- assert_warning(%r{regexp match /.../n against to}) {
+ assert_warning(%r{historical binary regexp match /.../n against}) {
assert_regexp_generic_ascii(Regexp.union(//n))
}
assert_regexp_fixed_eucjp(Regexp.union(//e))
@@ -768,7 +778,7 @@ class TestM17N < Test::Unit::TestCase
end
def test_dynamic_ascii_regexp
- assert_warning(%r{regexp match /.../n against to}) {
+ assert_warning(%r{historical binary regexp match /.../n against}) {
assert_regexp_generic_ascii(/#{ }/n)
}
assert_regexp_fixed_ascii8bit(/#{ }\xc2\xa1/n)
@@ -1521,6 +1531,17 @@ class TestM17N < Test::Unit::TestCase
}
end
+ def test_setbyte_range
+ s = u("\xE3\x81\x82\xE3\x81\x84")
+ assert_nothing_raised { s.setbyte(0, -1) }
+ assert_nothing_raised { s.setbyte(0, 0x00) }
+ assert_nothing_raised { s.setbyte(0, 0x7F) }
+ assert_nothing_raised { s.setbyte(0, 0x80) }
+ assert_nothing_raised { s.setbyte(0, 0xff) }
+ assert_nothing_raised { s.setbyte(0, 0x100) }
+ assert_nothing_raised { s.setbyte(0, 0x4f7574206f6620636861722072616e6765) }
+ end
+
def test_compatible
assert_nil Encoding.compatible?("",0)
assert_equal(Encoding::UTF_8, Encoding.compatible?(u(""), ua("abc")))
@@ -1623,8 +1644,9 @@ class TestM17N < Test::Unit::TestCase
assert_raise(ArgumentError){ u("\xE3\x81\x82\xE3\x81\x82\xE3\x81").scrub{u("\x81")} }
assert_equal(e("\xA4\xA2\xA2\xAE"), e("\xA4\xA2\xA4").scrub{e("\xA2\xAE")})
- assert_equal(u("\x81"), u("a\x81").scrub {|c| break c})
+ assert_equal(u("\x81"), u("a\x81c").scrub {|c| break c})
assert_raise(ArgumentError) {u("a\x81").scrub {|c| c}}
+ assert_raise(ArgumentError) {u("a").scrub("?") {|c| c}}
end
def test_scrub_widechar
@@ -1640,6 +1662,12 @@ class TestM17N < Test::Unit::TestCase
assert_equal("\uFFFD".encode("UTF-32LE"),
"\xff".force_encoding(Encoding::UTF_32LE).
scrub)
+ c = nil
+ assert_equal("?\u3042".encode(Encoding::UTF_16LE),
+ "\x00\xD8\x42\x30".force_encoding(Encoding::UTF_16LE).
+ scrub {|e| c = e; "?".encode(Encoding::UTF_16LE)})
+ assert_equal("\x00\xD8".force_encoding(Encoding::UTF_16LE), c)
+ assert_raise(ArgumentError) {"\uFFFD\u3042".encode("UTF-16BE").scrub("") {}}
end
def test_scrub_dummy_encoding
diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb
index d21ab66ffd..f6d84d181a 100644
--- a/test/ruby/test_marshal.rb
+++ b/test/ruby/test_marshal.rb
@@ -568,7 +568,7 @@ class TestMarshal < Test::Unit::TestCase
s.instance_variable_set(:@t, 42)
t = Bug8276.new(s)
s = Marshal.dump(t)
- assert_raise(RuntimeError) {Marshal.load(s)}
+ assert_raise(FrozenError) {Marshal.load(s)}
end
def test_marshal_load_ivar
@@ -622,7 +622,7 @@ class TestMarshal < Test::Unit::TestCase
def test_untainted_numeric
bug8945 = '[ruby-core:57346] [Bug #8945] Numerics never be tainted'
- b = Integer::FIXNUM_MAX + 1
+ b = RbConfig::LIMITS['FIXNUM_MAX'] + 1
tainted = [0, 1.0, 1.72723e-77, b].select do |x|
Marshal.load(Marshal.dump(x).taint).tainted?
end
@@ -644,6 +644,9 @@ class TestMarshal < Test::Unit::TestCase
c = Bug9523.new
assert_raise_with_message(RuntimeError, /Marshal\.dump reentered at marshal_dump/) do
Marshal.dump(c)
+ GC.start
+ 1000.times {"x"*1000}
+ GC.start
c.cc.call
end
end
@@ -757,4 +760,67 @@ class TestMarshal < Test::Unit::TestCase
end
assert_equal(obj, Marshal.load(dump))
end
+
+ class Bug12974
+ def marshal_dump
+ dup
+ end
+ end
+
+ def test_marshal_dump_recursion
+ assert_raise_with_message(RuntimeError, /same class instance/) do
+ Marshal.dump(Bug12974.new)
+ end
+ end
+
+ Bug14314 = Struct.new(:foo, keyword_init: true)
+
+ def test_marshal_keyword_init_struct
+ obj = Bug14314.new(foo: 42)
+ assert_equal obj, Marshal.load(Marshal.dump(obj))
+ end
+
+ class Bug15968
+ attr_accessor :bar, :baz
+
+ def initialize
+ self.bar = Bar.new(self)
+ end
+
+ class Bar
+ attr_accessor :foo
+
+ def initialize(foo)
+ self.foo = foo
+ end
+
+ def marshal_dump
+ if self.foo.baz
+ self.foo.remove_instance_variable(:@baz)
+ else
+ self.foo.baz = :problem
+ end
+ {foo: self.foo}
+ end
+
+ def marshal_load(data)
+ self.foo = data[:foo]
+ end
+ end
+ end
+
+ def test_marshal_dump_adding_instance_variable
+ obj = Bug15968.new
+ assert_raise_with_message(RuntimeError, /instance variable added/) do
+ Marshal.dump(obj)
+ end
+ end
+
+ def test_marshal_dump_removing_instance_variable
+ obj = Bug15968.new
+ obj.baz = :Bug15968
+ assert_raise_with_message(RuntimeError, /instance variable removed/) do
+ Marshal.dump(obj)
+ end
+ end
end
diff --git a/test/ruby/test_math.rb b/test/ruby/test_math.rb
index f226287442..5cc12bcfeb 100644
--- a/test/ruby/test_math.rb
+++ b/test/ruby/test_math.rb
@@ -202,6 +202,7 @@ class TestMath < Test::Unit::TestCase
check(3, Math.cbrt(27))
check(-0.1, Math.cbrt(-0.001))
assert_nothing_raised { assert_infinity(Math.cbrt(1.0/0)) }
+ assert_operator(Math.cbrt(1.0 - Float::EPSILON), :<=, 1.0)
end
def test_frexp
diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb
index 83ad484238..3bf25928c9 100644
--- a/test/ruby/test_method.rb
+++ b/test/ruby/test_method.rb
@@ -197,6 +197,7 @@ class TestMethod < Test::Unit::TestCase
def o.foo; end
assert_kind_of(Integer, o.method(:foo).hash)
assert_equal(Array.instance_method(:map).hash, Array.instance_method(:collect).hash)
+ assert_kind_of(String, o.method(:foo).hash.to_s)
end
def test_owner
@@ -454,6 +455,17 @@ class TestMethod < Test::Unit::TestCase
c3.class_eval { alias bar foo }
m3 = c3.new.method(:bar)
assert_equal("#<Method: #{c3.inspect}(#{c.inspect})#bar(foo)>", m3.inspect, bug7806)
+
+ m.taint
+ assert_predicate(m.inspect, :tainted?, "inspect result should be infected")
+
+ bug15608 = '[ruby-core:91570] [Bug #15608]'
+ c4 = Class.new(c)
+ c4.class_eval { alias bar foo }
+ o = c4.new
+ o.singleton_class
+ m4 = o.method(:bar)
+ assert_equal("#<Method: #{c4.inspect}(#{c.inspect})#bar(foo)>", m4.inspect, bug15608)
end
def test_callee_top_level
@@ -595,6 +607,11 @@ class TestMethod < Test::Unit::TestCase
assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], self.class.instance_method(:pmk7).parameters)
end
+ def test_hidden_parameters
+ instance_eval("def m((_)"+",(_)"*256+");end")
+ assert_empty(method(:m).parameters.map{|_,n|n}.compact)
+ end
+
def test_public_method_with_zsuper_method
c = Class.new
c.class_eval do
@@ -779,6 +796,19 @@ class TestMethod < Test::Unit::TestCase
assert_equal(:bar, m.call, feature8391)
end
+ def test_singleton_method_prepend
+ bug14658 = '[Bug #14658]'
+ c1 = Class.new
+ o = c1.new
+ def o.bar; :bar; end
+ class << o; prepend Module.new; end
+ m = assert_nothing_raised(NameError, bug14658) {o.singleton_method(:bar)}
+ assert_equal(:bar, m.call, bug14658)
+
+ o = Object.new
+ assert_raise(NameError, bug14658) {o.singleton_method(:bar)}
+ end
+
Feature9783 = '[ruby-core:62212] [Feature #9783]'
def assert_curry_three_args(m)
@@ -873,6 +903,16 @@ class TestMethod < Test::Unit::TestCase
m = m.super_method
assert_equal(c1, m.owner, Feature9781)
assert_same(o, m.receiver, Feature9781)
+
+ c1 = Class.new {def foo; end}
+ c2 = Class.new(c1) {include m1; include m2}
+ m = c2.instance_method(:foo)
+ assert_equal(m2, m.owner)
+ m = m.super_method
+ assert_equal(m1, m.owner)
+ m = m.super_method
+ assert_equal(c1, m.owner)
+ assert_nil(m.super_method)
end
def test_super_method_removed
@@ -901,6 +941,116 @@ class TestMethod < Test::Unit::TestCase
assert_nil(m)
end
+ def test_super_method_with_prepended_module
+ bug = '[ruby-core:81666] [Bug #13656] should be the method of the parent'
+ c1 = EnvUtil.labeled_class("C1") {def m; end}
+ c2 = EnvUtil.labeled_class("C2", c1) {def m; end}
+ c2.prepend(EnvUtil.labeled_module("M"))
+ m1 = c1.instance_method(:m)
+ m2 = c2.instance_method(:m).super_method
+ assert_equal(m1, m2, bug)
+ assert_equal(c1, m2.owner, bug)
+ assert_equal(m1.source_location, m2.source_location, bug)
+ end
+
+ def test_super_method_after_bind
+ assert_nil String.instance_method(:length).bind(String.new).super_method,
+ '[ruby-core:85231] [Bug #14421]'
+ end
+
+ def test_super_method_alias
+ c0 = Class.new do
+ def m1
+ [:C0_m1]
+ end
+ def m2
+ [:C0_m2]
+ end
+ end
+
+ c1 = Class.new(c0) do
+ def m1
+ [:C1_m1] + super
+ end
+ alias m2 m1
+ end
+
+ c2 = Class.new(c1) do
+ def m2
+ [:C2_m2] + super
+ end
+ end
+ o1 = c2.new
+ assert_equal([:C2_m2, :C1_m1, :C0_m1], o1.m2)
+
+ m = o1.method(:m2)
+ assert_equal([:C2_m2, :C1_m1, :C0_m1], m.call)
+
+ m = m.super_method
+ assert_equal([:C1_m1, :C0_m1], m.call)
+
+ m = m.super_method
+ assert_equal([:C0_m1], m.call)
+
+ assert_nil(m.super_method)
+ end
+
+ def test_super_method_alias_to_prepended_module
+ m = Module.new do
+ def m1
+ [:P_m1] + super
+ end
+
+ def m2
+ [:P_m2] + super
+ end
+ end
+
+ c0 = Class.new do
+ def m1
+ [:C0_m1]
+ end
+ end
+
+ c1 = Class.new(c0) do
+ def m1
+ [:C1_m1] + super
+ end
+ prepend m
+ alias m2 m1
+ end
+
+ o1 = c1.new
+ assert_equal([:P_m2, :P_m1, :C1_m1, :C0_m1], o1.m2)
+
+ m = o1.method(:m2)
+ assert_equal([:P_m2, :P_m1, :C1_m1, :C0_m1], m.call)
+
+ m = m.super_method
+ assert_equal([:P_m1, :C1_m1, :C0_m1], m.call)
+
+ m = m.super_method
+ assert_equal([:C1_m1, :C0_m1], m.call)
+
+ m = m.super_method
+ assert_equal([:C0_m1], m.call)
+
+ assert_nil(m.super_method)
+ end
+
+ # Bug 17780
+ def test_super_method_module_alias
+ m = Module.new do
+ def foo
+ end
+ alias :f :foo
+ end
+
+ method = m.instance_method(:f)
+ super_method = method.super_method
+ assert_nil(super_method)
+ end
+
def rest_parameter(*rest)
rest
end
@@ -965,4 +1115,87 @@ class TestMethod < Test::Unit::TestCase
assert_equal('1', obj.foo(1))
assert_equal('1', obj.bar(1))
end
+
+ def test_argument_error_location
+ body = <<-'END_OF_BODY'
+ eval <<-'EOS'
+ $line_lambda = __LINE__; $f = lambda do
+ _x = 1
+ end
+ $line_method = __LINE__; def foo
+ _x = 1
+ end
+ begin
+ $f.call(1)
+ rescue ArgumentError => e
+ assert_equal "(eval):#{$line_lambda.to_s}:in `block in <main>'", e.backtrace.first
+ end
+ begin
+ foo(1)
+ rescue ArgumentError => e
+ assert_equal "(eval):#{$line_method}:in `foo'", e.backtrace.first
+ end
+ EOS
+ END_OF_BODY
+
+ assert_separately [], body
+ # without trace insn
+ assert_separately [], "RubyVM::InstructionSequence.compile_option = {trace_instruction: false}\n" + body
+ end
+
+ def test_eqq
+ assert_operator(0.method(:<), :===, 5)
+ assert_not_operator(0.method(:<), :===, -5)
+ end
+
+ def test_compose_with_method
+ c = Class.new {
+ def f(x) x * 2 end
+ def g(x) x + 1 end
+ }
+ f = c.new.method(:f)
+ g = c.new.method(:g)
+
+ assert_equal(6, (f << g).call(2))
+ assert_equal(6, (g >> f).call(2))
+ end
+
+ def test_compose_with_proc
+ c = Class.new {
+ def f(x) x * 2 end
+ }
+ f = c.new.method(:f)
+ g = proc {|x| x + 1}
+
+ assert_equal(6, (f << g).call(2))
+ assert_equal(6, (g >> f).call(2))
+ end
+
+ def test_compose_with_callable
+ c = Class.new {
+ def f(x) x * 2 end
+ }
+ c2 = Class.new {
+ def call(x) x + 1 end
+ }
+ f = c.new.method(:f)
+ g = c2.new
+
+ assert_equal(6, (f << g).call(2))
+ assert_equal(5, (f >> g).call(2))
+ end
+
+ def test_compose_with_noncallable
+ c = Class.new {
+ def f(x) x * 2 end
+ }
+ f = c.new.method(:f)
+
+ assert_raise(NoMethodError) {
+ (f << 5).call(2)
+ }
+ assert_raise(NoMethodError) {
+ (f >> 5).call(2)
+ }
+ end
end
diff --git a/test/ruby/test_mixed_unicode_escapes.rb b/test/ruby/test_mixed_unicode_escapes.rb
index 09240d8ab2..f0b4cc691f 100644
--- a/test/ruby/test_mixed_unicode_escapes.rb
+++ b/test/ruby/test_mixed_unicode_escapes.rb
@@ -13,14 +13,18 @@ class TestMixedUnicodeEscape < Test::Unit::TestCase
# 8-bit character escapes are okay.
assert_equal("B\xFF", "\u0042\xFF")
- # sjis mb chars mixed with Unicode shound not work
+ # sjis mb chars mixed with Unicode should not work
assert_raise(SyntaxError) { eval %q("é\u1234")}
assert_raise(SyntaxError) { eval %q("\u{1234}é")}
+ # also should not work for Regexp
+ assert_raise(SyntaxError) { eval %q(/#{"\u1234"}#{"é"}/)}
+ assert_raise(RegexpError) { eval %q(/\u{1234}#{nil}é/)}
+ assert_raise(RegexpError) { eval %q(/é#{nil}\u1234/)}
+
# String interpolation turns into an expression and we get
# a different kind of error, but we still can't mix these
assert_raise(Encoding::CompatibilityError) { eval %q("\u{1234}#{nil}é")}
assert_raise(Encoding::CompatibilityError) { eval %q("é#{nil}\u1234")}
-
end
end
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index 0a11be46e3..ef78475424 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -294,8 +294,11 @@ class TestModule < Test::Unit::TestCase
end
def test_nested_get
- assert_equal Other, Object.const_get([self.class, Other].join('::'))
+ assert_equal Other, Object.const_get([self.class, 'Other'].join('::'))
assert_equal User::USER, self.class.const_get([User, 'USER'].join('::'))
+ assert_raise(NameError) {
+ Object.const_get([self.class.name, 'String'].join('::'))
+ }
end
def test_nested_get_symbol
@@ -328,6 +331,7 @@ class TestModule < Test::Unit::TestCase
assert_send([Object, :const_defined?, [self.class.name, 'Other'].join('::')])
assert_send([self.class, :const_defined?, 'User::USER'])
assert_not_send([self.class, :const_defined?, 'User::Foo'])
+ assert_not_send([Object, :const_defined?, [self.class.name, 'String'].join('::')])
end
def test_nested_defined_symbol
@@ -338,6 +342,17 @@ class TestModule < Test::Unit::TestCase
assert_raise(NameError) {self.class.const_defined?(const)}
end
+ def test_nested_defined_inheritance
+ assert_send([Object, :const_defined?, [self.class.name, 'User', 'MIXIN'].join('::')])
+ assert_send([self.class, :const_defined?, 'User::MIXIN'])
+ assert_send([Object, :const_defined?, 'File::SEEK_SET'])
+
+ # const_defined? with `false`
+ assert_not_send([Object, :const_defined?, [self.class.name, 'User', 'MIXIN'].join('::'), false])
+ assert_not_send([self.class, :const_defined?, 'User::MIXIN', false])
+ assert_not_send([Object, :const_defined?, 'File::SEEK_SET', false])
+ end
+
def test_nested_defined_bad_class
assert_raise(TypeError) do
self.class.const_defined?('User::USER::Foo')
@@ -455,33 +470,55 @@ class TestModule < Test::Unit::TestCase
end
def test_instance_methods
- assert_equal([:user, :user2], User.instance_methods(false))
+ assert_equal([:user, :user2], User.instance_methods(false).sort)
assert_equal([:user, :user2, :mixin].sort, User.instance_methods(true).sort)
assert_equal([:mixin], Mixin.instance_methods)
assert_equal([:mixin], Mixin.instance_methods(true))
assert_equal([:cClass], (class << CClass; self; end).instance_methods(false))
assert_equal([], (class << BClass; self; end).instance_methods(false))
assert_equal([:cm2], (class << AClass; self; end).instance_methods(false))
- # Ruby 1.8 feature change:
- # #instance_methods includes protected methods.
- #assert_equal([:aClass], AClass.instance_methods(false))
assert_equal([:aClass, :aClass2], AClass.instance_methods(false).sort)
assert_equal([:aClass, :aClass2],
(AClass.instance_methods(true) - Object.instance_methods(true)).sort)
end
def test_method_defined?
- assert !User.method_defined?(:wombat)
- assert User.method_defined?(:mixin)
- assert User.method_defined?(:user)
- assert User.method_defined?(:user2)
- assert !User.method_defined?(:user3)
+ [User, Class.new{include User}, Class.new{prepend User}].each do |klass|
+ [[], [true]].each do |args|
+ assert !klass.method_defined?(:wombat, *args)
+ assert klass.method_defined?(:mixin, *args)
+ assert klass.method_defined?(:user, *args)
+ assert klass.method_defined?(:user2, *args)
+ assert !klass.method_defined?(:user3, *args)
+
+ assert !klass.method_defined?("wombat", *args)
+ assert klass.method_defined?("mixin", *args)
+ assert klass.method_defined?("user", *args)
+ assert klass.method_defined?("user2", *args)
+ assert !klass.method_defined?("user3", *args)
+ end
+ end
+ end
- assert !User.method_defined?("wombat")
- assert User.method_defined?("mixin")
- assert User.method_defined?("user")
- assert User.method_defined?("user2")
- assert !User.method_defined?("user3")
+ def test_method_defined_without_include_super
+ assert User.method_defined?(:user, false)
+ assert !User.method_defined?(:mixin, false)
+ assert Mixin.method_defined?(:mixin, false)
+
+ User.const_set(:FOO, c = Class.new)
+
+ c.prepend(User)
+ assert !c.method_defined?(:user, false)
+ c.define_method(:user){}
+ assert c.method_defined?(:user, false)
+
+ assert !c.method_defined?(:mixin, false)
+ c.define_method(:mixin){}
+ assert c.method_defined?(:mixin, false)
+
+ assert !c.method_defined?(:userx, false)
+ c.define_method(:userx){}
+ assert c.method_defined?(:userx, false)
end
def module_exec_aux
@@ -636,15 +673,15 @@ class TestModule < Test::Unit::TestCase
def bar; end
end
m.freeze
- assert_raise(RuntimeError) do
+ assert_raise(FrozenError) do
m.module_eval do
def foo; end
end
end
- assert_raise(RuntimeError) do
+ assert_raise(FrozenError) do
m.__send__ :private, :bar
end
- assert_raise(RuntimeError) do
+ assert_raise(FrozenError) do
m.private_class_method :baz
end
end
@@ -949,7 +986,7 @@ class TestModule < Test::Unit::TestCase
def test_frozen_module
m = Module.new
m.freeze
- assert_raise(RuntimeError) do
+ assert_raise(FrozenError) do
m.instance_eval { undef_method(:foo) }
end
end
@@ -957,7 +994,7 @@ class TestModule < Test::Unit::TestCase
def test_frozen_class
c = Class.new
c.freeze
- assert_raise(RuntimeError) do
+ assert_raise(FrozenError) do
c.instance_eval { undef_method(:foo) }
end
end
@@ -967,7 +1004,7 @@ class TestModule < Test::Unit::TestCase
o = klass.new
c = class << o; self; end
c.freeze
- assert_raise_with_message(RuntimeError, /frozen/) do
+ assert_raise_with_message(FrozenError, /frozen/) do
c.instance_eval { undef_method(:foo) }
end
klass.class_eval do
@@ -977,8 +1014,8 @@ class TestModule < Test::Unit::TestCase
end
def test_method_defined
- c = Class.new
- c.class_eval do
+ cl = Class.new
+ def_methods = proc do
def foo; end
def bar; end
def baz; end
@@ -986,33 +1023,47 @@ class TestModule < Test::Unit::TestCase
protected :bar
private :baz
end
+ cl.class_eval(&def_methods)
+ sc = Class.new(cl)
+ mod = Module.new(&def_methods)
+ only_prepend = Class.new{prepend(mod)}
+ empty_prepend = cl.clone
+ empty_prepend.prepend(Module.new)
+ overlap_prepend = cl.clone
+ overlap_prepend.prepend(mod)
+
+ [[], [true], [false]].each do |args|
+ [cl, sc, only_prepend, empty_prepend, overlap_prepend].each do |c|
+ always_false = [sc, only_prepend].include?(c) && args == [false]
- assert_equal(true, c.public_method_defined?(:foo))
- assert_equal(false, c.public_method_defined?(:bar))
- assert_equal(false, c.public_method_defined?(:baz))
+ assert_equal(always_false ? false : true, c.public_method_defined?(:foo, *args))
+ assert_equal(always_false ? false : false, c.public_method_defined?(:bar, *args))
+ assert_equal(always_false ? false : false, c.public_method_defined?(:baz, *args))
- # Test if string arguments are converted to symbols
- assert_equal(true, c.public_method_defined?("foo"))
- assert_equal(false, c.public_method_defined?("bar"))
- assert_equal(false, c.public_method_defined?("baz"))
+ # Test if string arguments are converted to symbols
+ assert_equal(always_false ? false : true, c.public_method_defined?("foo", *args))
+ assert_equal(always_false ? false : false, c.public_method_defined?("bar", *args))
+ assert_equal(always_false ? false : false, c.public_method_defined?("baz", *args))
- assert_equal(false, c.protected_method_defined?(:foo))
- assert_equal(true, c.protected_method_defined?(:bar))
- assert_equal(false, c.protected_method_defined?(:baz))
+ assert_equal(always_false ? false : false, c.protected_method_defined?(:foo, *args))
+ assert_equal(always_false ? false : true, c.protected_method_defined?(:bar, *args))
+ assert_equal(always_false ? false : false, c.protected_method_defined?(:baz, *args))
- # Test if string arguments are converted to symbols
- assert_equal(false, c.protected_method_defined?("foo"))
- assert_equal(true, c.protected_method_defined?("bar"))
- assert_equal(false, c.protected_method_defined?("baz"))
+ # Test if string arguments are converted to symbols
+ assert_equal(always_false ? false : false, c.protected_method_defined?("foo", *args))
+ assert_equal(always_false ? false : true, c.protected_method_defined?("bar", *args))
+ assert_equal(always_false ? false : false, c.protected_method_defined?("baz", *args))
- assert_equal(false, c.private_method_defined?(:foo))
- assert_equal(false, c.private_method_defined?(:bar))
- assert_equal(true, c.private_method_defined?(:baz))
+ assert_equal(always_false ? false : false, c.private_method_defined?(:foo, *args))
+ assert_equal(always_false ? false : false, c.private_method_defined?(:bar, *args))
+ assert_equal(always_false ? false : true, c.private_method_defined?(:baz, *args))
- # Test if string arguments are converted to symbols
- assert_equal(false, c.private_method_defined?("foo"))
- assert_equal(false, c.private_method_defined?("bar"))
- assert_equal(true, c.private_method_defined?("baz"))
+ # Test if string arguments are converted to symbols
+ assert_equal(always_false ? false : false, c.private_method_defined?("foo", *args))
+ assert_equal(always_false ? false : false, c.private_method_defined?("bar", *args))
+ assert_equal(always_false ? false : true, c.private_method_defined?("baz", *args))
+ end
+ end
end
def test_top_public_private
@@ -1352,21 +1403,55 @@ class TestModule < Test::Unit::TestCase
assert_raise(ArgumentError, bug8540) { c.new.send :foo= }
end
- def test_private_constant
+ def test_private_constant_in_class
c = Class.new
c.const_set(:FOO, "foo")
assert_equal("foo", c::FOO)
c.private_constant(:FOO)
- assert_raise(NameError) { c::FOO }
+ e = assert_raise(NameError) {c::FOO}
+ assert_equal(c, e.receiver)
+ assert_equal(:FOO, e.name)
assert_equal("foo", c.class_eval("FOO"))
assert_equal("foo", c.const_get("FOO"))
$VERBOSE, verbose = nil, $VERBOSE
c.const_set(:FOO, "foo")
$VERBOSE = verbose
- assert_raise(NameError) { c::FOO }
- assert_raise_with_message(NameError, /#{c}::FOO/) do
+ e = assert_raise(NameError) {c::FOO}
+ assert_equal(c, e.receiver)
+ assert_equal(:FOO, e.name)
+ e = assert_raise_with_message(NameError, /#{c}::FOO/) do
Class.new(c)::FOO
end
+ assert_equal(c, e.receiver)
+ assert_equal(:FOO, e.name)
+ end
+
+ def test_private_constant_in_module
+ m = Module.new
+ m.const_set(:FOO, "foo")
+ assert_equal("foo", m::FOO)
+ m.private_constant(:FOO)
+ e = assert_raise(NameError) {m::FOO}
+ assert_equal(m, e.receiver)
+ assert_equal(:FOO, e.name)
+ assert_equal("foo", m.class_eval("FOO"))
+ assert_equal("foo", m.const_get("FOO"))
+ $VERBOSE, verbose = nil, $VERBOSE
+ m.const_set(:FOO, "foo")
+ $VERBOSE = verbose
+ e = assert_raise(NameError) {m::FOO}
+ assert_equal(m, e.receiver)
+ assert_equal(:FOO, e.name)
+ e = assert_raise(NameError, /#{m}::FOO/) do
+ Module.new {include m}::FOO
+ end
+ assert_equal(m, e.receiver)
+ assert_equal(:FOO, e.name)
+ e = assert_raise(NameError, /#{m}::FOO/) do
+ Class.new {include m}::FOO
+ end
+ assert_equal(m, e.receiver)
+ assert_equal(:FOO, e.name)
end
def test_private_constant2
@@ -1391,6 +1476,21 @@ class TestModule < Test::Unit::TestCase
RUBY
end
+ def test_private_constant_const_missing
+ c = Class.new
+ c.const_set(:FOO, "foo")
+ c.private_constant(:FOO)
+ class << c
+ attr_reader :const_missing_arg
+ def const_missing(name)
+ @const_missing_arg = name
+ name == :FOO ? const_get(:FOO) : super
+ end
+ end
+ assert_equal("foo", c::FOO)
+ assert_equal(:FOO, c.const_missing_arg)
+ end
+
class PrivateClass
end
private_constant :PrivateClass
@@ -1429,6 +1529,17 @@ class TestModule < Test::Unit::TestCase
assert_warn(/deprecated/, bug12382) {c.class_eval "FOO"}
end
+ NIL = nil
+ FALSE = false
+ deprecate_constant(:NIL, :FALSE)
+
+ def test_deprecate_nil_constant
+ w = EnvUtil.verbose_warning {2.times {FALSE}}
+ assert_equal(1, w.scan("::FALSE").size, w)
+ w = EnvUtil.verbose_warning {2.times {NIL}}
+ assert_equal(1, w.scan("::NIL").size, w)
+ end
+
def test_constants_with_private_constant
assert_not_include(::TestModule.constants, :PrivateClass)
assert_not_include(::TestModule.constants(true), :PrivateClass)
@@ -1876,6 +1987,25 @@ class TestModule < Test::Unit::TestCase
assert_raise(ArgumentError) { Module.new { prepend } }
end
+ def test_prepend_private_super
+ wrapper = Module.new do
+ def wrapped
+ super + 1
+ end
+ end
+
+ klass = Class.new do
+ prepend wrapper
+
+ def wrapped
+ 1
+ end
+ private :wrapped
+ end
+
+ assert_equal(2, klass.new.wrapped)
+ end
+
def test_class_variables
m = Module.new
m.class_variable_set(:@@foo, 1)
@@ -2030,6 +2160,22 @@ class TestModule < Test::Unit::TestCase
assert_raise(NameError){ m.instance_eval { remove_const(:__FOO__) } }
end
+ def test_public_methods
+ public_methods = %i[
+ include
+ prepend
+ attr
+ attr_accessor
+ attr_reader
+ attr_writer
+ define_method
+ alias_method
+ undef_method
+ remove_method
+ ]
+ assert_equal public_methods.sort, (Module.public_methods & public_methods).sort
+ end
+
def test_private_top_methods
assert_top_method_is_private(:include)
assert_top_method_is_private(:public)
@@ -2061,17 +2207,17 @@ class TestModule < Test::Unit::TestCase
bug11532 = '[ruby-core:70828] [Bug #11532]'
c = Class.new {const_set(:A, 1)}.freeze
- assert_raise_with_message(RuntimeError, /frozen class/, bug11532) {
+ assert_raise_with_message(FrozenError, /frozen class/, bug11532) {
c.class_eval {private_constant :A}
}
c = Class.new {const_set(:A, 1); private_constant :A}.freeze
- assert_raise_with_message(RuntimeError, /frozen class/, bug11532) {
+ assert_raise_with_message(FrozenError, /frozen class/, bug11532) {
c.class_eval {public_constant :A}
}
c = Class.new {const_set(:A, 1)}.freeze
- assert_raise_with_message(RuntimeError, /frozen class/, bug11532) {
+ assert_raise_with_message(FrozenError, /frozen class/, bug11532) {
c.class_eval {deprecate_constant :A}
}
end
@@ -2092,9 +2238,9 @@ class TestModule < Test::Unit::TestCase
def test_visibility_by_public_class_method
bug8284 = '[ruby-core:54404] [Bug #8284]'
- assert_raise(NoMethodError) {Object.define_method}
- Module.new.public_class_method(:define_method)
- assert_raise(NoMethodError, bug8284) {Object.define_method}
+ assert_raise(NoMethodError) {Object.remove_const}
+ Module.new.public_class_method(:remove_const)
+ assert_raise(NoMethodError, bug8284) {Object.remove_const}
end
def test_include_module_with_constants_does_not_invalidate_method_cache
@@ -2221,6 +2367,24 @@ class TestModule < Test::Unit::TestCase
end;
end
+ def test_private_extended_module
+ assert_separately [], %q{
+ class Object
+ def bar; "Object#bar"; end
+ end
+ module M1
+ def bar; super; end
+ end
+ module M2
+ include M1
+ private(:bar)
+ def foo; bar; end
+ end
+ extend M2
+ assert_equal 'Object#bar', foo
+ }
+ end
+
private
def assert_top_method_is_private(method)
diff --git a/test/ruby/test_not.rb b/test/ruby/test_not.rb
index 721f868a5a..12e4c4b696 100644
--- a/test/ruby/test_not.rb
+++ b/test/ruby/test_not.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
require 'test/unit'
-class TestIfunless < Test::Unit::TestCase
+class TestNot < Test::Unit::TestCase
def test_not_with_grouped_expression
assert_equal(false, (not (true)))
assert_equal(true, (not (false)))
diff --git a/test/ruby/test_notimp.rb b/test/ruby/test_notimp.rb
index a9ab8f328f..daa5a82d7b 100644
--- a/test/ruby/test_notimp.rb
+++ b/test/ruby/test_notimp.rb
@@ -13,11 +13,11 @@ class TestNotImplement < Test::Unit::TestCase
def test_respond_to_lchmod
assert_include(File.methods, :lchmod)
- if /linux/ =~ RUBY_PLATFORM
- assert_equal(false, File.respond_to?(:lchmod))
- end
- if /freebsd/ =~ RUBY_PLATFORM
+ case RUBY_PLATFORM
+ when /freebsd/, /linux-musl/
assert_equal(true, File.respond_to?(:lchmod))
+ when /linux/
+ assert_equal(false, File.respond_to?(:lchmod))
end
end
@@ -36,7 +36,7 @@ class TestNotImplement < Test::Unit::TestCase
proc {`ps -l #{pid}`}
end
assert_nothing_raised(Timeout::Error, ps) do
- Timeout.timeout(5) {
+ Timeout.timeout(EnvUtil.apply_timeout_scale(5)) {
pid = fork {}
Process.wait pid
pid = nil
@@ -57,9 +57,14 @@ class TestNotImplement < Test::Unit::TestCase
File.open(f, "w") {}
File.symlink f, g
newmode = 0444
- File.lchmod newmode, "#{d}/g"
- snew = File.lstat(g)
- assert_equal(newmode, snew.mode & 0777)
+ begin
+ File.lchmod newmode, "#{d}/g"
+ rescue Errno::EOPNOTSUPP
+ skip $!
+ else
+ snew = File.lstat(g)
+ assert_equal(newmode, snew.mode & 0777)
+ end
}
end
end
diff --git a/test/ruby/test_numeric.rb b/test/ruby/test_numeric.rb
index eb056f61c3..e48c3448e8 100644
--- a/test/ruby/test_numeric.rb
+++ b/test/ruby/test_numeric.rb
@@ -54,18 +54,16 @@ class TestNumeric < Test::Unit::TestCase
bug7688 = '[ruby-core:51389] [Bug #7688]'
a = Class.new(Numeric) do
- def coerce(x); raise StandardError; end
+ def coerce(x); raise StandardError, "my error"; end
end.new
- assert_raise_with_message(TypeError, /can't be coerced into /) { 1 + a }
- warn = /will no more rescue exceptions of #coerce.+ in the next release/m
- assert_warn(warn, bug7688) { assert_raise(ArgumentError) { 1 < a } }
+ assert_raise_with_message(StandardError, "my error") { 1 + a }
+ assert_raise_with_message(StandardError, "my error") { 1 < a }
a = Class.new(Numeric) do
def coerce(x); :bad_return_value; end
end.new
assert_raise_with_message(TypeError, "coerce must return [x, y]") { 1 + a }
- warn = /Bad return value for #coerce.+next release will raise an error/m
- assert_warn(warn, bug7688) { assert_raise(ArgumentError) { 1 < a } }
+ assert_raise_with_message(TypeError, "coerce must return [x, y]") { 1 < a }
end
def test_singleton_method
@@ -76,12 +74,18 @@ class TestNumeric < Test::Unit::TestCase
def test_dup
a = Numeric.new
- assert_raise(TypeError) { a.dup }
+ assert_same a, a.dup
+ end
+
+ def test_clone
+ a = Numeric.new
+ assert_same a, a.clone
+ assert_raise(ArgumentError) {a.clone(freeze: false)}
- c = Module.new do
- break eval("class C\u{3042} < Numeric; self; end")
+ c = EnvUtil.labeled_class("\u{1f4a9}", Numeric)
+ assert_raise_with_message(ArgumentError, /\u{1f4a9}/) do
+ c.new.clone(freeze: false)
end
- assert_raise_with_message(TypeError, /C\u3042/) {c.new.dup}
end
def test_quo
@@ -252,15 +256,15 @@ class TestNumeric < Test::Unit::TestCase
end
def test_step
- bignum = Integer::FIXNUM_MAX + 1
+ bignum = RbConfig::LIMITS['FIXNUM_MAX'] + 1
assert_raise(ArgumentError) { 1.step(10, 1, 0) { } }
assert_raise(ArgumentError) { 1.step(10, 1, 0).size }
assert_raise(ArgumentError) { 1.step(10, 0) { } }
- assert_raise(ArgumentError) { 1.step(10, 0).size }
- assert_raise(TypeError) { 1.step(10, "1") { } }
- assert_raise(TypeError) { 1.step(10, "1").size }
+ assert_raise(ArgumentError) { 1.step(10, "1") { } }
+ assert_raise(ArgumentError) { 1.step(10, "1").size }
assert_raise(TypeError) { 1.step(10, nil) { } }
- assert_raise(TypeError) { 1.step(10, nil).size }
+ assert_nothing_raised { 1.step(10, 0).size }
+ assert_nothing_raised { 1.step(10, nil).size }
assert_nothing_raised { 1.step(by: 0, to: nil) }
assert_nothing_raised { 1.step(by: 0, to: nil).size }
assert_nothing_raised { 1.step(by: 0) }
@@ -268,6 +272,14 @@ class TestNumeric < Test::Unit::TestCase
assert_nothing_raised { 1.step(by: nil) }
assert_nothing_raised { 1.step(by: nil).size }
+ assert_kind_of(Enumerator::ArithmeticSequence, 1.step(10))
+ assert_kind_of(Enumerator::ArithmeticSequence, 1.step(10, 2))
+ assert_kind_of(Enumerator::ArithmeticSequence, 1.step(10, by: 2))
+ assert_kind_of(Enumerator::ArithmeticSequence, 1.step(by: 2))
+ assert_kind_of(Enumerator::ArithmeticSequence, 1.step(by: 2, to: nil))
+ assert_kind_of(Enumerator::ArithmeticSequence, 1.step(by: 2, to: 10))
+ assert_kind_of(Enumerator::ArithmeticSequence, 1.step(by: -1))
+
bug9811 = '[ruby-dev:48177] [Bug #9811]'
assert_raise(ArgumentError, bug9811) { 1.step(10, foo: nil) {} }
assert_raise(ArgumentError, bug9811) { 1.step(10, foo: nil).size }
@@ -285,7 +297,6 @@ class TestNumeric < Test::Unit::TestCase
i <<= 1 until (bigflo - i).to_i < bignum
bigflo -= i >> 1
assert_equal(bigflo.to_i, (0.0).step(bigflo-1.0, 1.0).size)
- assert_operator((0.0).step(bignum.to_f, 1.0).size, :>=, bignum) # may loose precision
assert_step [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 10]
assert_step [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, to: 10]
@@ -380,4 +391,27 @@ class TestNumeric < Test::Unit::TestCase
end
end
end
+
+ def test_pow
+ assert_equal(2**3, 2.pow(3))
+ assert_equal(2**-1, 2.pow(-1))
+ assert_equal(2**0.5, 2.pow(0.5))
+ assert_equal((-1)**0.5, -1.pow(0.5))
+ assert_equal(3**3 % 8, 3.pow(3, 8))
+ assert_equal(3**3 % -8, 3.pow(3,-8))
+ assert_equal(3**2 % -2, 3.pow(2,-2))
+ assert_equal((-3)**3 % 8, -3.pow(3,8))
+ assert_equal((-3)**3 % -8, -3.pow(3,-8))
+ assert_equal(5**2 % -8, 5.pow(2,-8))
+ assert_equal(4481650795473624846969600733813414725093,
+ 2120078484650058507891187874713297895455.
+ pow(5478118174010360425845660566650432540723,
+ 5263488859030795548286226023720904036518))
+
+ assert_equal(12, 12.pow(1, 10000000000), '[Bug #14259]')
+ assert_equal(12, 12.pow(1, 10000000001), '[Bug #14259]')
+ assert_equal(12, 12.pow(1, 10000000002), '[Bug #14259]')
+ assert_equal(17298641040, 12.pow(72387894339363242, 243682743764), '[Bug #14259]')
+ end
+
end
diff --git a/test/ruby/test_object.rb b/test/ruby/test_object.rb
index 7b3defa011..28e07162d7 100644
--- a/test/ruby/test_object.rb
+++ b/test/ruby/test_object.rb
@@ -18,10 +18,25 @@ class TestObject < Test::Unit::TestCase
assert_same(object, object.itself, feature6373)
end
+ def test_yield_self
+ feature = '[ruby-core:46320] [Feature #6721]'
+ object = Object.new
+ assert_same(self, object.yield_self {self}, feature)
+ assert_same(object, object.yield_self {|x| break x}, feature)
+ enum = object.yield_self
+ assert_instance_of(Enumerator, enum)
+ assert_equal(1, enum.size)
+ end
+
def test_dup
- assert_raise(TypeError) { 1.dup }
- assert_raise(TypeError) { true.dup }
- assert_raise(TypeError) { nil.dup }
+ assert_equal 1, 1.dup
+ assert_equal true, true.dup
+ assert_equal nil, nil.dup
+ assert_equal false, false.dup
+ x = :x; assert_equal x, x.dup
+ x = "bug13145".intern; assert_equal x, x.dup
+ x = 1 << 64; assert_equal x, x.dup
+ x = 1.72723e-77; assert_equal x, x.dup
assert_raise(TypeError) do
Object.new.instance_eval { initialize_copy(1) }
@@ -37,11 +52,29 @@ class TestObject < Test::Unit::TestCase
assert_equal(true, c.frozen?)
assert_equal(2, c.b)
+ assert_raise(ArgumentError) {a.clone(freeze: [])}
d = a.clone(freeze: false)
def d.e; 3; end
assert_equal(false, d.frozen?)
assert_equal(2, d.b)
assert_equal(3, d.e)
+
+ assert_equal 1, 1.clone
+ assert_equal true, true.clone
+ assert_equal nil, nil.clone
+ assert_equal false, false.clone
+ x = :x; assert_equal x, x.dup
+ x = "bug13145".intern; assert_equal x, x.dup
+ x = 1 << 64; assert_equal x, x.clone
+ x = 1.72723e-77; assert_equal x, x.clone
+ assert_raise(ArgumentError) {1.clone(freeze: false)}
+ assert_raise(ArgumentError) {true.clone(freeze: false)}
+ assert_raise(ArgumentError) {nil.clone(freeze: false)}
+ assert_raise(ArgumentError) {false.clone(freeze: false)}
+ x = EnvUtil.labeled_class("\u{1f4a9}").new
+ assert_raise_with_message(ArgumentError, /\u{1f4a9}/) do
+ Object.new.clone(freeze: x)
+ end
end
def test_init_dupclone
@@ -66,12 +99,12 @@ class TestObject < Test::Unit::TestCase
def test_taint_frozen_obj
o = Object.new
o.freeze
- assert_raise(RuntimeError) { o.taint }
+ assert_raise(FrozenError) { o.taint }
o = Object.new
o.taint
o.freeze
- assert_raise(RuntimeError) { o.untaint }
+ assert_raise(FrozenError) { o.untaint }
end
def test_freeze_immediate
@@ -90,7 +123,7 @@ class TestObject < Test::Unit::TestCase
attr_accessor :foo
}
obj = klass.new.freeze
- assert_raise_with_message(RuntimeError, /#{name}/) {
+ assert_raise_with_message(FrozenError, /#{name}/) {
obj.foo = 1
}
end
@@ -366,7 +399,7 @@ class TestObject < Test::Unit::TestCase
def test_remove_method
c = Class.new
c.freeze
- assert_raise(RuntimeError) do
+ assert_raise(FrozenError) do
c.instance_eval { remove_method(:foo) }
end
@@ -774,7 +807,7 @@ class TestObject < Test::Unit::TestCase
class<<x;self;end.class_eval {define_method(:to_s) {name}}
assert_same(name, x.to_s)
assert_not_predicate(name, :tainted?)
- assert_raise(RuntimeError) {name.taint}
+ assert_raise(FrozenError) {name.taint}
assert_equal("X", [x].join(""))
assert_not_predicate(name, :tainted?)
assert_not_predicate(eval('"X".freeze'), :tainted?)
@@ -850,6 +883,7 @@ class TestObject < Test::Unit::TestCase
['ArgumentError.new("bug5473")', 'ArgumentError, "bug5473"', '"bug5473"'].each do |code|
exc = code[/\A[A-Z]\w+/] || 'RuntimeError'
assert_separately([], <<-SRC)
+ $VERBOSE = nil
class ::Object
def method_missing(m, *a, &b)
raise #{code}
@@ -866,7 +900,7 @@ class TestObject < Test::Unit::TestCase
b = yield
assert_nothing_raised("copy") {a.instance_eval {initialize_copy(b)}}
c = a.dup.freeze
- assert_raise(RuntimeError, "frozen") {c.instance_eval {initialize_copy(b)}}
+ assert_raise(FrozenError, "frozen") {c.instance_eval {initialize_copy(b)}}
d = a.dup.trust
[a, b, c, d]
end
@@ -890,6 +924,7 @@ class TestObject < Test::Unit::TestCase
_issue = "Bug #7539"
assert_raise_with_message(TypeError, "can't convert Array into Integer") {Integer([42])}
assert_raise_with_message(TypeError, 'no implicit conversion of Array into Integer') {[].first([42])}
+ assert_raise_with_message(TypeError, "can't convert Array into Rational") {Rational([42])}
end
def test_copied_ivar_memory_leak
@@ -901,4 +936,14 @@ class TestObject < Test::Unit::TestCase
num.times {a.clone.set}
end;
end
+
+ def test_clone_object_should_not_be_old
+ assert_normal_exit <<-EOS, '[Bug #13775]'
+ b = proc { }
+ 10.times do |i|
+ b.clone
+ GC.start
+ end
+ EOS
+ end
end
diff --git a/test/ruby/test_optimization.rb b/test/ruby/test_optimization.rb
index 502d12389e..bc3eacce52 100644
--- a/test/ruby/test_optimization.rb
+++ b/test/ruby/test_optimization.rb
@@ -4,7 +4,8 @@ require 'objspace'
class TestRubyOptimization < Test::Unit::TestCase
def assert_redefine_method(klass, method, code, msg = nil)
- assert_separately([], <<-"end;")# do
+ assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
class #{klass}
undef #{method}
def #{method}(*args)
@@ -44,6 +45,26 @@ class TestRubyOptimization < Test::Unit::TestCase
assert_redefine_method('Integer', '%', 'assert_equal 7, 8 % 7')
end
+ def test_fixnum_lt
+ assert_equal true, 1 < 2
+ assert_redefine_method('Integer', '<', 'assert_equal 2, 1 < 2')
+ end
+
+ def test_fixnum_le
+ assert_equal true, 1 <= 2
+ assert_redefine_method('Integer', '<=', 'assert_equal 2, 1 <= 2')
+ end
+
+ def test_fixnum_gt
+ assert_equal false, 1 > 2
+ assert_redefine_method('Integer', '>', 'assert_equal 2, 1 > 2')
+ end
+
+ def test_fixnum_ge
+ assert_equal false, 1 >= 2
+ assert_redefine_method('Integer', '>=', 'assert_equal 2, 1 >= 2')
+ end
+
def test_float_plus
assert_equal 4.0, 2.0 + 2.0
assert_redefine_method('Float', '+', 'assert_equal 2.0, 2.0 + 2.0')
@@ -64,6 +85,26 @@ class TestRubyOptimization < Test::Unit::TestCase
assert_redefine_method('Float', '/', 'assert_equal 6.66, 4.2 / 6.66', "[Bug #9238]")
end
+ def test_float_lt
+ assert_equal true, 1.1 < 2.2
+ assert_redefine_method('Float', '<', 'assert_equal 2.2, 1.1 < 2.2')
+ end
+
+ def test_float_le
+ assert_equal true, 1.1 <= 2.2
+ assert_redefine_method('Float', '<=', 'assert_equal 2.2, 1.1 <= 2.2')
+ end
+
+ def test_float_gt
+ assert_equal false, 1.1 > 2.2
+ assert_redefine_method('Float', '>', 'assert_equal 2.2, 1.1 > 2.2')
+ end
+
+ def test_float_ge
+ assert_equal false, 1.1 >= 2.2
+ assert_redefine_method('Float', '>=', 'assert_equal 2.2, 1.1 >= 2.2')
+ end
+
def test_string_length
assert_equal 6, "string".length
assert_redefine_method('String', 'length', 'assert_nil "string".length')
@@ -104,6 +145,11 @@ class TestRubyOptimization < Test::Unit::TestCase
assert_redefine_method('String', 'freeze', 'assert_nil "foo".freeze')
end
+ def test_string_uminus
+ assert_same "foo".freeze, -"foo"
+ assert_redefine_method('String', '-@', 'assert_nil(-"foo")')
+ end
+
def test_string_freeze_saves_memory
n = 16384
data = '.'.freeze
@@ -141,6 +187,16 @@ class TestRubyOptimization < Test::Unit::TestCase
assert_redefine_method('String', '<<', 'assert_equal "b", "a" << "b"')
end
+ def test_fixnum_and
+ assert_equal 1, 1&3
+ assert_redefine_method('Integer', '&', 'assert_equal 3, 1&3')
+ end
+
+ def test_fixnum_or
+ assert_equal 3, 1|3
+ assert_redefine_method('Integer', '|', 'assert_equal 1, 3|1')
+ end
+
def test_array_plus
assert_equal [1,2], [1]+[2]
assert_redefine_method('Array', '+', 'assert_equal [2], [1]+[2]')
@@ -178,20 +234,22 @@ class TestRubyOptimization < Test::Unit::TestCase
def test_hash_aref_with
h = { "foo" => 1 }
assert_equal 1, h["foo"]
- assert_redefine_method('Hash', '[]', <<-end)
+ assert_redefine_method('Hash', '[]', "#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
h = { "foo" => 1 }
assert_equal "foo", h["foo"]
- end
+ end;
end
def test_hash_aset_with
h = {}
assert_equal 1, h["foo"] = 1
- assert_redefine_method('Hash', '[]=', <<-end)
+ assert_redefine_method('Hash', '[]=', "#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
h = {}
assert_equal 1, h["foo"] = 1, "assignment always returns value set"
assert_nil h["foo"]
- end
+ end;
end
class MyObj
@@ -230,7 +288,8 @@ class TestRubyOptimization < Test::Unit::TestCase
def test_tailcall
bug4082 = '[ruby-core:33289]'
- tailcall(<<-EOF)
+ tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
def fact_helper(n, res)
if n == 1
res
@@ -241,14 +300,15 @@ class TestRubyOptimization < Test::Unit::TestCase
def fact(n)
fact_helper(n, 1)
end
- EOF
+ end;
assert_equal(9131, fact(3000).to_s.size, message(bug4082) {disasm(:fact_helper)})
end
def test_tailcall_with_block
bug6901 = '[ruby-dev:46065]'
- tailcall(<<-EOF)
+ tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
def identity(val)
val
end
@@ -258,7 +318,7 @@ class TestRubyOptimization < Test::Unit::TestCase
identity(yield)
}
end
- EOF
+ end;
assert_equal(123, delay { 123 }.call, message(bug6901) {disasm(:delay)})
end
@@ -267,11 +327,12 @@ class TestRubyOptimization < Test::Unit::TestCase
end
def test_tailcall_inhibited_by_block
- tailcall(<<-EOF)
+ tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
def yield_result
just_yield {:ok}
end
- EOF
+ end;
assert_equal(:ok, yield_result, message {disasm(:yield_result)})
end
@@ -286,7 +347,8 @@ class TestRubyOptimization < Test::Unit::TestCase
def test_tailcall_inhibited_by_rescue
bug12082 = '[ruby-core:73871] [Bug #12082]'
- tailcall(<<-'end;')
+ tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
def to_be_rescued
return do_raise
1 + 2
@@ -303,7 +365,8 @@ class TestRubyOptimization < Test::Unit::TestCase
def test_tailcall_symbol_block_arg
bug12565 = '[ruby-core:46065]'
- tailcall(<<-EOF)
+ tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
def apply_one_and_two(&block)
yield(1, 2)
end
@@ -311,29 +374,31 @@ class TestRubyOptimization < Test::Unit::TestCase
def add_one_and_two
apply_one_and_two(&:+)
end
- EOF
+ end;
assert_equal(3, add_one_and_two,
message(bug12565) {disasm(:add_one_and_two)})
end
def test_tailcall_interrupted_by_sigint
bug12576 = 'ruby-core:76327'
- script = <<EOS
-RubyVM::InstructionSequence.compile_option = {
- :tailcall_optimization => true,
- :trace_instruction => false
-}
-
-eval <<EOF
-def foo
- foo
-end
-puts("start")
-STDOUT.flush
-foo
-EOF
-EOS
- status, err = EnvUtil.invoke_ruby([], "", true, true, {}) {
+ script = "#{<<-"begin;"}\n#{<<~'end;'}"
+ begin;
+ RubyVM::InstructionSequence.compile_option = {
+ :tailcall_optimization => true,
+ :trace_instruction => false
+ }
+
+ eval "#{<<~"begin;"}\n#{<<~'end;1'}"
+ begin;
+ def foo
+ foo
+ end
+ puts("start")
+ STDOUT.flush
+ foo
+ end;1
+ end;
+ status, _err = EnvUtil.invoke_ruby([], "", true, true, {}) {
|in_p, out_p, err_p, pid|
in_p.write(script)
in_p.close
@@ -360,7 +425,7 @@ EOS
def test_tailcall_condition_block
bug = '[ruby-core:78015] [Bug #12905]'
- src = "#{<<-"begin;"}\n#{<<-"end;"}"
+ src = "#{<<-"begin;"}\n#{<<~"end;"}"
begin;
def run(current, final)
if current < final
@@ -409,7 +474,8 @@ EOS
end
def test_string_freeze_block
- assert_separately([], <<-"end;")# do
+ assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
class String
undef freeze
def freeze
@@ -422,7 +488,8 @@ EOS
end
def test_opt_case_dispatch
- code = <<-EOF
+ code = "#{<<-"begin;"}\n#{<<~"end;"}"
+ begin;
case foo
when "foo" then :foo
when true then true
@@ -435,7 +502,7 @@ EOS
else
:nomatch
end
- EOF
+ end;
check = {
'foo' => :foo,
true => true,
@@ -454,7 +521,8 @@ EOS
assert_equal :nomatch, eval("foo = :blah\n#{code}")
check.each do |foo, _|
klass = foo.class.to_s
- assert_separately([], <<-"end;") # do
+ assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}")
+ begin;
class #{klass}
undef ===
def ===(*args)
@@ -490,4 +558,270 @@ EOS
bug11816 = '[ruby-core:74993] [Bug #11816]'
assert_ruby_status([], 'nil&.foo &&= false', bug11816)
end
+
+ def test_peephole_string_literal_range
+ code = "#{<<~"begin;"}\n#{<<~"end;"}"
+ begin;
+ case ver
+ when "2.0.0".."2.3.2" then :foo
+ when "1.8.0"..."1.8.8" then :bar
+ end
+ end;
+ [ true, false ].each do |opt|
+ iseq = RubyVM::InstructionSequence.compile(code,
+ frozen_string_literal: opt)
+ insn = iseq.disasm
+ assert_match %r{putobject\s+#{Regexp.quote('"1.8.0"..."1.8.8"')}}, insn
+ assert_match %r{putobject\s+#{Regexp.quote('"2.0.0".."2.3.2"')}}, insn
+ assert_no_match(/putstring/, insn)
+ assert_no_match(/newrange/, insn)
+ end
+ end
+
+ def test_peephole_dstr
+ code = "#{<<~'begin;'}\n#{<<~'end;'}"
+ begin;
+ exp = -'a'
+ z = 'a'
+ [exp, -"#{z}"]
+ end;
+ [ false, true ].each do |fsl|
+ iseq = RubyVM::InstructionSequence.compile(code,
+ frozen_string_literal: fsl)
+ assert_same(*iseq.eval,
+ "[ruby-core:85542] [Bug #14475] fsl: #{fsl}")
+ end
+ end
+
+ def test_branch_condition_backquote
+ bug = '[ruby-core:80740] [Bug #13444] redefined backquote should be called'
+ class << self
+ def `(s)
+ @q = s
+ @r
+ end
+ end
+
+ @q = nil
+ @r = nil
+ assert_equal("bar", ("bar" unless `foo`), bug)
+ assert_equal("foo", @q, bug)
+
+ @q = nil
+ @r = true
+ assert_equal("bar", ("bar" if `foo`), bug)
+ assert_equal("foo", @q, bug)
+
+ @q = nil
+ @r = "z"
+ assert_equal("bar", ("bar" if `foo#{@r}`))
+ assert_equal("fooz", @q, bug)
+ end
+
+ def test_branch_condition_def
+ bug = '[ruby-core:80740] [Bug #13444] method should be defined'
+ c = Class.new do
+ raise "bug" unless def t;:ok;end
+ end
+ assert_nothing_raised(NoMethodError, bug) do
+ assert_equal(:ok, c.new.t)
+ end
+ end
+
+ def test_branch_condition_defs
+ bug = '[ruby-core:80740] [Bug #13444] singleton method should be defined'
+ raise "bug" unless def self.t;:ok;end
+ assert_nothing_raised(NameError, bug) do
+ assert_equal(:ok, t)
+ end
+ end
+
+ def test_retry_label_in_unreachable_chunk
+ bug = '[ruby-core:81272] [Bug #13578]'
+ assert_valid_syntax("#{<<-"begin;"}\n#{<<-"end;"}", bug)
+ begin;
+ def t; if false; case 42; when s {}; end; end; end
+ end;
+ end
+
+ def bptest_yield &b
+ yield
+ end
+
+ def bptest_yield_pass &b
+ bptest_yield(&b)
+ end
+
+ def bptest_bp_value &b
+ b
+ end
+
+ def bptest_bp_pass_bp_value &b
+ bptest_bp_value(&b)
+ end
+
+ def bptest_binding &b
+ binding
+ end
+
+ def bptest_set &b
+ b = Proc.new{2}
+ end
+
+ def test_block_parameter
+ assert_equal(1, bptest_yield{1})
+ assert_equal(1, bptest_yield_pass{1})
+ assert_equal(1, send(:bptest_yield){1})
+
+ assert_equal(Proc, bptest_bp_value{}.class)
+ assert_equal nil, bptest_bp_value
+ assert_equal(Proc, bptest_bp_pass_bp_value{}.class)
+ assert_equal nil, bptest_bp_pass_bp_value
+
+ assert_equal Proc, bptest_binding{}.local_variable_get(:b).class
+
+ assert_equal 2, bptest_set{1}.call
+ end
+
+ def test_block_parameter_should_not_create_objects
+ assert_separately [], <<-END
+ #
+ def foo &b
+ end
+ h1 = {}; h2 = {}
+ ObjectSpace.count_objects(h1) # reharsal
+ ObjectSpace.count_objects(h1)
+ foo{}
+ ObjectSpace.count_objects(h2)
+
+ assert_equal 0, h2[:TOTAL] - h1[:TOTAL]
+ END
+ end
+
+ def test_block_parameter_should_restore_safe_level
+ assert_separately [], <<-END
+ #
+ def foo &b
+ $SAFE = 1
+ b.call
+ end
+ assert_equal 1, foo{$SAFE}
+ END
+ end
+
+ def test_peephole_optimization_without_trace
+ assert_separately [], <<-END
+ RubyVM::InstructionSequence.compile_option = {trace_instruction: false}
+ eval "def foo; 1.times{|(a), &b| nil && a}; end"
+ END
+ end
+
+ def test_clear_unreachable_keyword_args
+ assert_separately [], <<-END, timeout: 30
+ script = <<-EOS
+ if true
+ else
+ foo(k1:1)
+ end
+ EOS
+ GC.stress = true
+ 30.times{
+ RubyVM::InstructionSequence.compile(script)
+ }
+ END
+ end
+
+ def test_callinfo_unreachable_path
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ iseq = RubyVM::InstructionSequence.compile("if false; foo(bar: :baz); else :ok end")
+ bin = iseq.to_binary
+ iseq = RubyVM::InstructionSequence.load_from_binary(bin)
+ assert_instance_of(RubyVM::InstructionSequence, iseq)
+ assert_equal(:ok, iseq.eval)
+ end;
+ end
+
+ def test_side_effect_in_popped_splat
+ bug = '[ruby-core:84340] [Bug #14201]'
+ eval("{**(bug = nil; {})};42")
+ assert_nil(bug)
+
+ bug = '[ruby-core:85486] [Bug #14459]'
+ h = {}
+ assert_equal(bug, eval('{ok: 42, **h}; bug'))
+ assert_equal(:ok, eval('{ok: bug = :ok, **h}; bug'))
+ end
+
+ def test_overwritten_blockparam
+ obj = Object.new
+ def obj.a(&block)
+ block = 1
+ return :ok if block
+ :ng
+ end
+ assert_equal(:ok, obj.a())
+ end
+
+ def test_blockparam_in_rescue
+ obj = Object.new
+ def obj.foo(&b)
+ raise
+ rescue
+ b.call
+ end
+ result = nil
+ assert_equal(42, obj.foo {result = 42})
+ assert_equal(42, result)
+ end
+
+ def test_unconditional_branch_to_leave_block
+ assert_valid_syntax("#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ tap {true || tap {}}
+ end;
+ end
+
+ def test_jump_elimination_with_optimized_out_block
+ x = Object.new
+ def x.bug(obj)
+ if obj || obj
+ obj = obj
+ else
+ raise "[ruby-core:87830] [Bug #14897]"
+ end
+ obj
+ end
+ assert_equal(:ok, x.bug(:ok))
+ end
+
+ def test_jump_elimination_with_optimized_out_block_2
+ x = Object.new
+ def x.bug
+ a = "aaa"
+ ok = :NG
+ if a == "bbb" || a == "ccc" then
+ a = a
+ else
+ ok = :ok
+ end
+ ok
+ end
+ assert_equal(:ok, x.bug)
+ end
+
+ def test_peephole_jump_after_newarray
+ i = 0
+ %w(1) || 2 while (i += 1) < 100
+ assert_equal(100, i)
+ end
+
+ def test_optimized_empty_ensure
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}", timeout: 1)
+ begin;
+ assert_raise(RuntimeError) {
+ begin raise ensure nil if nil end
+ }
+ end;
+ end
end
diff --git a/test/ruby/test_pack.rb b/test/ruby/test_pack.rb
index c5af6f2152..658208d9df 100644
--- a/test/ruby/test_pack.rb
+++ b/test/ruby/test_pack.rb
@@ -428,6 +428,7 @@ class TestPack < Test::Unit::TestCase
assert_operator(4, :<=, [1].pack("L!").bytesize)
end
+ require 'rbconfig'
def test_pack_unpack_qQ
s1 = [578437695752307201, -506097522914230529].pack("q*")
s2 = [578437695752307201, 17940646550795321087].pack("Q*")
@@ -437,6 +438,7 @@ class TestPack < Test::Unit::TestCase
# Note: q! and Q! should not work on platform which has no long long type.
# Is there a such platform now?
+ # @shyouhei: Yes. gcc -ansi is one of such platform.
s1 = [578437695752307201, -506097522914230529].pack("q!*")
s2 = [578437695752307201, 17940646550795321087].pack("Q!*")
assert_equal([578437695752307201, -506097522914230529], s2.unpack("q!*"))
@@ -446,7 +448,7 @@ class TestPack < Test::Unit::TestCase
assert_equal(8, [1].pack("Q").bytesize)
assert_operator(8, :<=, [1].pack("q!").bytesize)
assert_operator(8, :<=, [1].pack("Q!").bytesize)
- end
+ end if RbConfig::CONFIG['HAVE_LONG_LONG']
def test_pack_unpack_jJ
# Note: we assume that the size of intptr_t and uintptr_t equals to the size
@@ -548,6 +550,9 @@ class TestPack < Test::Unit::TestCase
assert_equal([1, 2], "\x01\x00\x00\x02".unpack("C@3C"))
assert_equal([nil], "\x00".unpack("@1C")) # is it OK?
assert_raise(ArgumentError) { "\x00".unpack("@2C") }
+
+ pos = RbConfig::LIMITS["UINTPTR_MAX"] - 99 # -100
+ assert_raise(RangeError) {"0123456789".unpack("@#{pos}C10")}
end
def test_pack_unpack_percent
@@ -686,6 +691,11 @@ EXPECTED
assert_equal(["pre=hoge"], "pre=hoge".unpack("M"))
assert_equal(["pre==31after"], "pre==31after".unpack("M"))
assert_equal(["pre===31after"], "pre===31after".unpack("M"))
+
+ bug = '[ruby-core:83055] [Bug #13949]'
+ s = "abcdef".unpack1("M")
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_predicate(s, :ascii_only?, bug)
end
def test_pack_unpack_P2
@@ -792,6 +802,13 @@ EXPECTED
assert_warning(/\A(.* in '\u{3042}'\n)+\z/) {
[].pack("\u{3042}")
}
+
+ assert_warning(/\A.* in '.*U'\Z/) {
+ assert_equal "\000", [0].pack("\0U")
+ }
+ assert_warning(/\A.* in '.*U'\Z/) {
+ "\000".unpack("\0U")
+ }
end
def test_pack_resize
@@ -812,4 +829,60 @@ EXPECTED
assert_raise_with_message(ArgumentError, /too few/) {ary.pack("AA")}
end;
end
+
+ def test_pack_with_buffer
+ buf = String.new(capacity: 100)
+
+ assert_raise_with_message(FrozenError, /frozen/) {
+ [0xDEAD_BEEF].pack('N', buffer: 'foo'.freeze)
+ }
+ assert_raise_with_message(TypeError, /must be String/) {
+ [0xDEAD_BEEF].pack('N', buffer: Object.new)
+ }
+
+ addr = [buf].pack('p')
+
+ [0xDEAD_BEEF].pack('N', buffer: buf)
+ assert_equal "\xDE\xAD\xBE\xEF", buf
+
+ [0xBABE_F00D].pack('@4N', buffer: buf)
+ assert_equal "\xDE\xAD\xBE\xEF\xBA\xBE\xF0\x0D", buf
+ assert_equal addr, [buf].pack('p')
+
+ [0xBAAD_FACE].pack('@10N', buffer: buf)
+ assert_equal "\xDE\xAD\xBE\xEF\xBA\xBE\xF0\x0D\0\0\xBA\xAD\xFA\xCE", buf
+
+ assert_equal addr, [buf].pack('p')
+ end
+
+ def test_unpack_with_block
+ ret = []; "ABCD".unpack("CCCC") {|v| ret << v }
+ assert_equal [65, 66, 67, 68], ret
+ ret = []; "A".unpack("B*") {|v| ret << v.dup }
+ assert_equal ["01000001"], ret
+ end
+
+ def test_unpack1
+ assert_equal 65, "A".unpack1("C")
+ assert_equal 68, "ABCD".unpack1("x3C")
+ assert_equal 0x3042, "\u{3042 3044 3046}".unpack1("U*")
+ assert_equal "hogefuga", "aG9nZWZ1Z2E=".unpack1("m")
+ assert_equal "01000001", "A".unpack1("B*")
+ end
+
+ def test_pack_infection
+ tainted_array_string = ["123456"]
+ tainted_array_string.first.taint
+ ['a', 'A', 'Z', 'B', 'b', 'H', 'h', 'u', 'M', 'm', 'P', 'p'].each do |f|
+ assert_predicate(tainted_array_string.pack(f), :tainted?)
+ end
+ end
+
+ def test_unpack_infection
+ tainted_string = "123456"
+ tainted_string.taint
+ ['a', 'A', 'Z', 'B', 'b', 'H', 'h', 'u', 'M', 'm'].each do |f|
+ assert_predicate(tainted_string.unpack(f).first, :tainted?)
+ end
+ end
end
diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb
index 434533fcab..24ca62a3f6 100644
--- a/test/ruby/test_parse.rb
+++ b/test/ruby/test_parse.rb
@@ -14,13 +14,12 @@ class TestParse < Test::Unit::TestCase
end
def test_else_without_rescue
- x = eval <<-END, nil, __FILE__, __LINE__+1
+ assert_syntax_error(<<-END, %r":#{__LINE__+2}: else without rescue"o, [__FILE__, __LINE__+1])
begin
else
42
end
END
- assert_equal(42, x)
end
def test_alias_backref
@@ -174,6 +173,7 @@ class TestParse < Test::Unit::TestCase
end
c = Class.new
+ c.freeze
assert_nothing_raised(SyntaxError) do
eval <<-END, nil, __FILE__, __LINE__+1
if false
@@ -183,7 +183,6 @@ class TestParse < Test::Unit::TestCase
END
end
- c = Class.new
assert_raise(SyntaxError) do
eval <<-END, nil, __FILE__, __LINE__+1
$1 &= 1
@@ -457,6 +456,30 @@ class TestParse < Test::Unit::TestCase
end
end
+ def test_op_asgn1_with_block
+ t = Object.new
+ a = []
+ blk = proc {|x| a << x }
+ def t.[](_)
+ yield(:aref)
+ nil
+ end
+ def t.[]=(_, _)
+ yield(:aset)
+ end
+ def t.dummy(_)
+ end
+ eval <<-END, nil, __FILE__, __LINE__+1
+ t[42, &blk] ||= 42
+ END
+ assert_equal([:aref, :aset], a)
+ a.clear
+ eval <<-END, nil, __FILE__, __LINE__+1
+ t[42, &blk] ||= t.dummy 42 # command_asgn test
+ END
+ assert_equal([:aref, :aset], a)
+ end
+
def test_backquote
t = Object.new
@@ -484,21 +507,41 @@ class TestParse < Test::Unit::TestCase
end
def test_string
- assert_raise(SyntaxError) do
- eval '"\xg1"'
- end
+ mesg = 'from the backslash through the invalid char'
- assert_raise(SyntaxError) do
- eval '"\u{1234"'
- end
+ e = assert_syntax_error('"\xg1"', /hex escape/)
+ assert_equal(' ^', e.message.lines.last, mesg)
- assert_raise(SyntaxError) do
- eval '"\M1"'
- end
+ e = assert_syntax_error('"\u{1234"', 'unterminated Unicode escape')
+ assert_equal(' ^', e.message.lines.last, mesg)
- assert_raise(SyntaxError) do
- eval '"\C1"'
- end
+ e = assert_syntax_error('"\u{xxxx}"', 'invalid Unicode escape')
+ assert_equal(' ^', e.message.lines.last, mesg)
+
+ e = assert_syntax_error('"\u{xxxx', 'Unicode escape')
+ assert_pattern_list([
+ /.*: invalid Unicode escape\n.*\n/,
+ / \^/,
+ /\n/,
+ /.*: unterminated Unicode escape\n.*\n/,
+ / \^/,
+ /\n/,
+ /.*: unterminated string.*\n.*\n/,
+ / \^/,
+ ], e.message)
+
+ e = assert_syntax_error('"\M1"', /escape character syntax/)
+ assert_equal(' ^~~', e.message.lines.last, mesg)
+
+ e = assert_syntax_error('"\C1"', /escape character syntax/)
+ assert_equal(' ^~~', e.message.lines.last, mesg)
+
+ src = '"\xD0\u{90'"\n""000000000000000000000000"
+ assert_syntax_error(src, /:#{__LINE__}: unterminated/o)
+
+ assert_syntax_error('"\u{100000000}"', /invalid Unicode escape/)
+ assert_equal("", eval('"\u{}"'))
+ assert_equal("", eval('"\u{ }"'))
assert_equal("\x81", eval('"\C-\M-a"'))
assert_equal("\177", eval('"\c?"'))
@@ -512,6 +555,8 @@ class TestParse < Test::Unit::TestCase
assert_raise(SyntaxError) { eval("?\v") }
assert_raise(SyntaxError) { eval("?\r") }
assert_raise(SyntaxError) { eval("?\f") }
+ assert_raise(SyntaxError) { eval("?\f") }
+ assert_raise(SyntaxError) { eval(" ?a\x8a".force_encoding("utf-8")) }
assert_equal("\u{1234}", eval("?\u{1234}"))
assert_equal("\u{1234}", eval('?\u{1234}'))
end
@@ -535,8 +580,9 @@ class TestParse < Test::Unit::TestCase
assert_nothing_raised(SyntaxError, bug) do
assert_equal(sym, eval(':"foo\u{0}bar"'))
end
- assert_raise(SyntaxError) do
- eval ':"foo\u{}bar"'
+ assert_nothing_raised(SyntaxError) do
+ assert_equal(:foobar, eval(':"foo\u{}bar"'))
+ assert_equal(:foobar, eval(':"foo\u{ }bar"'))
end
end
@@ -681,15 +727,17 @@ x = __ENCODING__
def test_invalid_char
bug10117 = '[ruby-core:64243] [Bug #10117]'
invalid_char = /Invalid char `\\x01'/
- x = x = 1
+ x = 1
assert_in_out_err(%W"-e \x01x", "", [], invalid_char, bug10117)
assert_syntax_error("\x01x", invalid_char, bug10117)
assert_equal(nil, eval("\x04x"))
+ assert_equal 1, x
end
def test_literal_concat
- x = x = "baz"
+ x = "baz"
assert_equal("foobarbaz", eval('"foo" "bar#{x}"'))
+ assert_equal("baz", x)
end
def test_unassignable
@@ -721,6 +769,12 @@ x = __ENCODING__
end
END
end
+ assert_raise(SyntaxError) do
+ eval "#{<<~"begin;"}\n#{<<~'end;'}", nil, __FILE__, __LINE__+1
+ begin;
+ x, true
+ end;
+ end
end
def test_block_dup
@@ -759,9 +813,10 @@ x = __ENCODING__
$VERBOSE = true
stderr = $stderr
$stderr = StringIO.new("")
- x = x = 1
+ x = 1
assert_nil eval("x; nil")
assert_nil eval("1+1; nil")
+ assert_nil eval("1.+(1); nil")
assert_nil eval("TestParse; nil")
assert_nil eval("::TestParse; nil")
assert_nil eval("x..x; nil")
@@ -771,6 +826,7 @@ x = __ENCODING__
assert_nil eval("true; nil")
assert_nil eval("false; nil")
assert_nil eval("defined?(1); nil")
+ assert_equal 1, x
assert_raise(SyntaxError) do
eval %q(1; next; 2)
@@ -825,10 +881,11 @@ x = __ENCODING__
end
assert_nothing_raised do
- x = x = "bar"
+ x = "bar"
eval <<-END, nil, __FILE__, __LINE__+1
:"foo#{"x"}baz" ? 1 : 2
END
+ assert_equal "bar", x
end
end
@@ -854,17 +911,19 @@ x = __ENCODING__
assert_equal(expected, actual, bug5614)
end
- def test_shadowing_variable
- assert_warning(/shadowing outer local variable/) {eval("a=1; tap {|a|}")}
- a = "\u{3042}"
- assert_warning(/#{a}/o) {eval("#{a}=1; tap {|#{a}|}")}
+ def test_no_shadowing_variable_warning
+ assert_no_warning(/shadowing outer local variable/) {eval("a=1; tap {|a|}")}
end
def test_unused_variable
o = Object.new
assert_warning(/assigned but unused variable/) {o.instance_eval("def foo; a=1; nil; end")}
+ assert_warning(/assigned but unused variable/) {o.instance_eval("def bar; a=1; a(); end")}
a = "\u{3042}"
assert_warning(/#{a}/) {o.instance_eval("def foo; #{a}=1; nil; end")}
+ o = Object.new
+ assert_warning(/assigned but unused variable/) {o.instance_eval("def foo; tap {a=1; a()}; end")}
+ assert_warning('') {o.instance_eval("def bar; a=a=1; nil; end")}
end
def test_named_capture_conflict
@@ -956,6 +1015,195 @@ x = __ENCODING__
end
end
+ def test_yyerror_at_eol
+ assert_syntax_error(" 0b", /\^/)
+ assert_syntax_error(" 0b\n", /\^/)
+ end
+
+ def test_error_def_in_argument
+ assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
+ assert_syntax_error("def f r:def d; def f 0end", /unexpected/)
+ end;
+
+ assert_syntax_error("def\nf(000)end", /^ \^~~/)
+ assert_syntax_error("def\nf(&)end", /^ \^/)
+ end
+
+ def test_method_location_in_rescue
+ bug = '[ruby-core:79388] [Bug #13181]'
+ obj, line = Object.new, __LINE__+1
+ def obj.location
+ #
+ raise
+ rescue
+ caller_locations(1, 1)[0]
+ end
+
+ assert_equal(line, obj.location.lineno, bug)
+ end
+
+ def test_negative_line_number
+ bug = '[ruby-core:80920] [Bug #13523]'
+ obj = Object.new
+ obj.instance_eval("def t(e = false);raise if e; __LINE__;end", "test", -100)
+ assert_equal(-100, obj.t, bug)
+ assert_equal(-100, obj.method(:t).source_location[1], bug)
+ e = assert_raise(RuntimeError) {obj.t(true)}
+ assert_equal(-100, e.backtrace_locations.first.lineno, bug)
+ end
+
+ def test_file_in_indented_heredoc
+ name = '[ruby-core:80987] [Bug #13540]' # long enough to be shared
+ assert_equal(name+"\n", eval("#{<<-"begin;"}\n#{<<-'end;'}", nil, name))
+ begin;
+ <<~HEREDOC
+ #{__FILE__}
+ HEREDOC
+ end;
+ end
+
+ def test_unexpected_token_error
+ assert_raise(SyntaxError) do
+ eval('"x"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
+ end
+ end
+
+ def test_unexpected_token_after_numeric
+ assert_raise_with_message(SyntaxError, /^ \^~~\z/) do
+ eval('0000xyz')
+ end
+ assert_raise_with_message(SyntaxError, /^ \^~~\z/) do
+ eval('1.2i1.1')
+ end
+ end
+
+ def test_truncated_source_line
+ e = assert_raise_with_message(SyntaxError, /unexpected tIDENTIFIER/) do
+ eval("'0123456789012345678901234567890123456789' abcdefghijklmnopqrstuvwxyz0123456789 0123456789012345678901234567890123456789")
+ end
+ line = e.message.lines[1]
+ assert_operator(line, :start_with?, "...")
+ assert_operator(line, :end_with?, "...\n")
+ end
+
+ def test_unterminated_regexp_error
+ e = assert_raise(SyntaxError) do
+ eval("/x")
+ end.message
+ assert_match(/unterminated regexp meets end of file/, e)
+ assert_not_match(/unexpected tSTRING_END/, e)
+ end
+
+ def test_lparenarg
+ o = Struct.new(:x).new
+ def o.i(x)
+ self.x = x
+ end
+ o.instance_eval {i (-1.3).abs}
+ assert_equal(1.3, o.x)
+ o.instance_eval {i = 0; i (-1.3).abs}
+ assert_equal(1.3, o.x)
+ end
+
+ def test_serial_comparison
+ assert_warning(/comparison '<' after/) do
+ $VERBOSE = true
+ x = 1
+ eval("if false; 0 < x < 2; end")
+ end
+ end
+
+ def test_eof_in_def
+ assert_raise(SyntaxError) { eval("def m\n\0""end") }
+ assert_raise(SyntaxError) { eval("def m\n\C-d""end") }
+ assert_raise(SyntaxError) { eval("def m\n\C-z""end") }
+ end
+
+ def test_location_of_invalid_token
+ assert_raise_with_message(SyntaxError, /^ \^~~\z/) do
+ eval('class xxx end')
+ end
+ end
+
+ def test_whitespace_warning
+ assert_raise_with_message(SyntaxError, /backslash/) do
+ eval("\\foo")
+ end
+ assert_raise_with_message(SyntaxError, /escaped space/) do
+ eval("\\ ")
+ end
+ assert_raise_with_message(SyntaxError, /escaped horizontal tab/) do
+ eval("\\\t")
+ end
+ assert_raise_with_message(SyntaxError, /escaped form feed/) do
+ eval("\\\f")
+ end
+ assert_raise_with_message(SyntaxError, /escaped carriage return/) do
+ assert_warn(/middle of line/) {eval("\\\r")}
+ end
+ assert_raise_with_message(SyntaxError, /escaped vertical tab/) do
+ eval("\\\v")
+ end
+ end
+
+ def test_command_def_cmdarg
+ assert_valid_syntax("\n#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ m def x(); end
+ 1.tap do end
+ end;
+ end
+
+ NONASCII_CONSTANTS = [
+ *%W"\u{00de} \u{00C0}".flat_map {|c| [c, c.encode("iso-8859-15")]},
+ "\u{1c4}", "\u{1f2}", "\u{1f88}", "\u{370}",
+ *%W"\u{391} \u{ff21}".flat_map {|c| [c, c.encode("cp932"), c.encode("euc-jp")]},
+ ]
+
+ def assert_nonascii_const
+ assert_all_assertions_foreach("NONASCII_CONSTANTS", *NONASCII_CONSTANTS) do |n|
+ m = Module.new
+ assert_not_operator(m, :const_defined?, n)
+ assert_raise_with_message(NameError, /uninitialized/) do
+ m.const_get(n)
+ end
+ assert_nil(eval("defined?(m::#{n})"))
+
+ v = yield m, n
+
+ assert_operator(m, :const_defined?, n)
+ assert_equal("constant", eval("defined?(m::#{n})"))
+ assert_same(v, m.const_get(n))
+
+ m.__send__(:remove_const, n)
+ assert_not_operator(m, :const_defined?, n)
+ assert_nil(eval("defined?(m::#{n})"))
+ end
+ end
+
+ def test_nonascii_const_set
+ assert_nonascii_const do |m, n|
+ m.const_set(n, 42)
+ end
+ end
+
+ def test_nonascii_constant
+ assert_nonascii_const do |m, n|
+ m.module_eval("class #{n}; self; end")
+ end
+ end
+
+ def test_cdmarg_after_command_args_and_tlbrace_arg
+ assert_valid_syntax('let () { m(a) do; end }')
+ end
+
+ def test_void_value_in_command_rhs
+ w = "void value expression"
+ ex = assert_syntax_error("x = return 1", w)
+ assert_equal(1, ex.message.scan(w).size, "same #{w.inspect} warning should be just once")
+ end
+
=begin
def test_past_scope_variable
assert_warning(/past scope/) {catch {|tag| eval("BEGIN{throw tag}; tap {a = 1}; a")}}
diff --git a/test/ruby/test_pipe.rb b/test/ruby/test_pipe.rb
index efca8f28c1..9fa42fd375 100644
--- a/test/ruby/test_pipe.rb
+++ b/test/ruby/test_pipe.rb
@@ -27,4 +27,23 @@ class TestPipe < Test::Unit::TestCase
end
end
end
+
+ def test_stdout_epipe
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ io = STDOUT
+ begin
+ save = io.dup
+ IO.popen("echo", "w", out: IO::NULL) do |f|
+ io.reopen(f)
+ Process.wait(f.pid)
+ assert_raise(Errno::EPIPE) do
+ io.print "foo\n"
+ end
+ end
+ ensure
+ io.reopen(save)
+ end
+ end;
+ end
end
diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb
index b46c0a901b..9ae1de62ff 100644
--- a/test/ruby/test_proc.rb
+++ b/test/ruby/test_proc.rb
@@ -160,26 +160,34 @@ class TestProc < Test::Unit::TestCase
$SAFE += 1
proc {$SAFE}
}.call
- assert_equal(safe, $SAFE)
+
+ assert_equal(safe + 1, $SAFE)
assert_equal(safe + 1, p.call)
- assert_equal(safe, $SAFE)
+ assert_equal(safe + 1, $SAFE)
+ $SAFE = 0
c.class_eval {define_method(:safe, p)}
assert_equal(safe, x.safe)
- assert_equal(safe, x.method(:safe).call)
- assert_equal(safe, x.method(:safe).to_proc.call)
+ $SAFE = 0
p = proc {$SAFE += 1}
assert_equal(safe + 1, p.call)
- assert_equal(safe, $SAFE)
+ assert_equal(safe + 1, $SAFE)
+ $SAFE = 0
c.class_eval {define_method(:inc, p)}
assert_equal(safe + 1, proc {x.inc; $SAFE}.call)
- assert_equal(safe, $SAFE)
+ assert_equal(safe + 1, $SAFE)
+
+ $SAFE = 0
assert_equal(safe + 1, proc {x.method(:inc).call; $SAFE}.call)
- assert_equal(safe, $SAFE)
+ assert_equal(safe + 1, $SAFE)
+
+ $SAFE = 0
assert_equal(safe + 1, proc {x.method(:inc).to_proc.call; $SAFE}.call)
- assert_equal(safe, $SAFE)
+ assert_equal(safe + 1, $SAFE)
+ ensure
+ $SAFE = 0
end
def m2
@@ -397,6 +405,15 @@ class TestProc < Test::Unit::TestCase
assert_equal([1, 2, 3], b.eval("[x, y, z]"))
end
+ def test_binding_source_location
+ b, expected_location = binding, [__FILE__, __LINE__]
+ assert_equal(expected_location, b.source_location)
+
+ file, lineno = method(:source_location_test).to_proc.binding.source_location
+ assert_match(/^#{ Regexp.quote(__FILE__) }$/, file)
+ assert_equal(@@line_of_source_location_test, lineno, 'Bug #2427')
+ end
+
def test_proc_lambda
assert_raise(ArgumentError) { proc }
assert_raise(ArgumentError) { lambda }
@@ -1120,6 +1137,9 @@ class TestProc < Test::Unit::TestCase
assert_equal([[:req]], method(:putc).parameters)
assert_equal([[:rest]], method(:p).parameters)
+
+ pr = eval("proc{|"+"(_),"*30+"|}")
+ assert_empty(pr.parameters.map{|_,n|n}.compact)
end
def pm0() end
@@ -1174,6 +1194,8 @@ class TestProc < Test::Unit::TestCase
x = proc {}
x.taint
assert_predicate(x.to_s, :tainted?)
+ name = "Proc\u{1f37b}"
+ assert_include(EnvUtil.labeled_class(name, Proc).new {}.to_s, name)
end
@@line_of_source_location_test = __LINE__ + 1
@@ -1322,6 +1344,20 @@ class TestProc < Test::Unit::TestCase
assert_equal(20, b.eval("b"))
end
+ def test_local_variable_set_wb
+ assert_ruby_status([], <<-'end;', '[Bug #13605]', timeout: 30)
+ b = binding
+ n = 20_000
+
+ n.times do |i|
+ v = rand(2_000)
+ name = "n#{v}"
+ value = Object.new
+ b.local_variable_set name, value
+ end
+ end;
+ end
+
def test_local_variable_defined?
b = get_binding
assert_equal(true, b.local_variable_defined?(:a))
@@ -1357,4 +1393,95 @@ class TestProc < Test::Unit::TestCase
e.each {}
EOS
end
+
+ def test_prepended_call
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", ["call"])
+ begin;
+ Proc.prepend Module.new {def call() puts "call"; super; end}
+ def m(&blk) blk.call; end
+ m {}
+ end;
+ end
+
+ def test_refined_call
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", ["call"])
+ begin;
+ using Module.new {refine(Proc) {def call() puts "call"; super; end}}
+ def m(&blk) blk.call; end
+ m {}
+ end;
+ end
+
+ def method_for_test_proc_without_block_for_symbol
+ binding.eval('proc')
+ end
+
+ def test_proc_without_block_for_symbol
+ assert_equal('1', method_for_test_proc_without_block_for_symbol(&:to_s).call(1), '[Bug #14782]')
+ end
+
+ def test_compose
+ f = proc {|x| x * 2}
+ g = proc {|x| x + 1}
+
+ assert_equal(6, (f << g).call(2))
+ assert_equal(6, (g >> f).call(2))
+ end
+
+ def test_compose_with_multiple_args
+ f = proc {|x| x * 2}
+ g = proc {|x, y| x + y}
+
+ assert_equal(6, (f << g).call(1, 2))
+ assert_equal(6, (g >> f).call(1, 2))
+ end
+
+ def test_compose_with_block
+ f = proc {|x| x * 2}
+ g = proc {|&blk| blk.call(1) }
+
+ assert_equal(8, (f << g).call { |x| x + 3 })
+ assert_equal(8, (g >> f).call { |x| x + 3 })
+ end
+
+ def test_compose_with_lambda
+ f = lambda {|x| x * 2}
+ g = lambda {|x| x}
+
+ assert_predicate((f << g), :lambda?)
+ assert_predicate((g >> f), :lambda?)
+ end
+
+ def test_compose_with_method
+ f = proc {|x| x * 2}
+ c = Class.new {
+ def g(x) x + 1 end
+ }
+ g = c.new.method(:g)
+
+ assert_equal(6, (f << g).call(2))
+ assert_equal(5, (f >> g).call(2))
+ end
+
+ def test_compose_with_callable
+ f = proc {|x| x * 2}
+ c = Class.new {
+ def call(x) x + 1 end
+ }
+ g = c.new
+
+ assert_equal(6, (f << g).call(2))
+ assert_equal(5, (f >> g).call(2))
+ end
+
+ def test_compose_with_noncallable
+ f = proc {|x| x * 2}
+
+ assert_raise(NoMethodError) {
+ (f << 5).call(2)
+ }
+ assert_raise(NoMethodError) {
+ (f >> 5).call(2)
+ }
+ end
end
diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb
index 8ad53ac78e..0b43c6bc48 100644
--- a/test/ruby/test_process.rb
+++ b/test/ruby/test_process.rb
@@ -242,9 +242,28 @@ class TestProcess < Test::Unit::TestCase
:rlimit_core=>n, :rlimit_cpu=>3600]) {|io|
assert_equal("[#{n}, #{n}]\n[3600, 3600]", io.read.chomp)
}
+
+ assert_raise(ArgumentError) do
+ system(RUBY, '-e', 'exit', 'rlimit_bogus'.to_sym => 123)
+ end
+ assert_separately([],"#{<<-"begin;"}\n#{<<~'end;'}")
+ BUG = "[ruby-core:82033] [Bug #13744]"
+ RUBY = "#{RUBY}"
+ begin;
+ assert(system("#{RUBY}", "-e",
+ "exit([3600,3600] == Process.getrlimit(:CPU))",
+ 'rlimit_cpu'.to_sym => 3600), BUG)
+ assert_raise(ArgumentError, BUG) do
+ system("#{RUBY}", '-e', 'exit', :rlimit_bogus => 123)
+ end
+ end;
+
+ assert_raise(ArgumentError, /rlimit_cpu/) {
+ system(RUBY, '-e', 'exit', "rlimit_cpu\0".to_sym => 3600)
+ }
end
- MANDATORY_ENVS = %w[RUBYLIB]
+ MANDATORY_ENVS = %w[RUBYLIB MJIT_SEARCH_BUILD_DIR]
case RbConfig::CONFIG['target_os']
when /linux/
MANDATORY_ENVS << 'LD_PRELOAD'
@@ -256,6 +275,9 @@ class TestProcess < Test::Unit::TestCase
if e = RbConfig::CONFIG['LIBPATHENV']
MANDATORY_ENVS << e
end
+ if e = RbConfig::CONFIG['PRELOADENV'] and !e.empty?
+ MANDATORY_ENVS << e
+ end
PREENVARG = ['-e', "%w[#{MANDATORY_ENVS.join(' ')}].each{|e|ENV.delete(e)}"]
ENVARG = ['-e', 'ENV.each {|k,v| puts "#{k}=#{v}" }']
ENVCOMMAND = [RUBY].concat(PREENVARG).concat(ENVARG)
@@ -443,10 +465,11 @@ class TestProcess < Test::Unit::TestCase
def test_execopts_open_chdir_m17n_path
with_tmpchdir {|d|
Dir.mkdir "テスト"
- system(*PWD, :chdir => "テスト", :out => "open_chdir_テスト")
+ (pwd = PWD.dup).insert(1, '-EUTF-8:UTF-8')
+ system(*pwd, :chdir => "テスト", :out => "open_chdir_テスト")
assert_file.exist?("open_chdir_テスト")
assert_file.not_exist?("テスト/open_chdir_テスト")
- assert_equal("#{d}/テスト", File.read("open_chdir_テスト").chomp.encode(__ENCODING__))
+ assert_equal("#{d}/テスト", File.read("open_chdir_テスト", encoding: "UTF-8").chomp)
}
end if windows? || Encoding.find('locale') == Encoding::UTF_8
@@ -636,11 +659,14 @@ class TestProcess < Test::Unit::TestCase
class E < StandardError; end
trap(:USR1) { raise E }
begin
+ puts "start"
+ STDOUT.flush
system("cat", :in => "fifo")
rescue E
puts "ok"
end
EOS
+ assert_equal("start\n", io.gets)
sleep 0.5
Process.kill(:USR1, io.pid)
assert_equal("ok\n", io.read)
@@ -656,14 +682,17 @@ class TestProcess < Test::Unit::TestCase
return
end
IO.popen([RUBY, '-e', <<-'EOS']) {|io|
+ STDOUT.sync = true
trap(:USR1) { print "trap\n" }
+ puts "start"
system("cat", :in => "fifo")
EOS
- sleep 1
+ assert_equal("start\n", io.gets)
+ sleep 0.2 # wait for the child to stop at opening "fifo"
Process.kill(:USR1, io.pid)
- sleep 1
+ assert_equal("trap\n", io.readpartial(8))
File.write("fifo", "ok\n")
- assert_equal("trap\nok\n", io.read)
+ assert_equal("ok\n", io.read)
}
}
end unless windows? # does not support fifo
@@ -736,6 +765,15 @@ class TestProcess < Test::Unit::TestCase
Process.wait pid
end
}
+
+ # ensure standard FDs we redirect to are blocking for compatibility
+ with_pipes(3) do |pipes|
+ src = 'p [STDIN,STDOUT,STDERR].map(&:nonblock?)'
+ rdr = { 0 => pipes[0][0], 1 => pipes[1][1], 2 => pipes[2][1] }
+ pid = spawn(RUBY, '-rio/nonblock', '-e', src, rdr)
+ assert_equal("[false, false, false]\n", pipes[1][0].gets)
+ Process.wait pid
+ end
end
end
@@ -983,6 +1021,15 @@ class TestProcess < Test::Unit::TestCase
}
end
+ def test_close_others_default_false
+ IO.pipe do |r,w|
+ w.close_on_exec = false
+ src = "IO.new(#{w.fileno}).puts(:hi)"
+ assert_equal true, system(*%W(#{RUBY} --disable=gems -e #{src}))
+ assert_equal "hi\n", r.gets
+ end
+ end unless windows? # passing non-stdio fds is not supported on Windows
+
def test_execopts_redirect_self
begin
with_pipe {|r, w|
@@ -1353,6 +1400,14 @@ class TestProcess < Test::Unit::TestCase
}
end
+ def test_argv0_keep_alive
+ assert_in_out_err([], <<~REPRO, ['-'], [], "[Bug #15887]")
+ $0 = "diverge"
+ 4.times { GC.start }
+ puts Process.argv0
+ REPRO
+ end
+
def test_status
with_tmpchdir do
s = run_in_child("exit 1")
@@ -1430,7 +1485,9 @@ class TestProcess < Test::Unit::TestCase
def test_wait_exception
bug11340 = '[ruby-dev:49176] [Bug #11340]'
t0 = t1 = nil
- IO.popen([RUBY, '-e', 'puts;STDOUT.flush;Thread.start{gets;exit};sleep(3)'], 'r+') do |f|
+ sec = 3
+ code = "puts;STDOUT.flush;Thread.start{gets;exit};sleep(#{sec})"
+ IO.popen([RUBY, '-e', code], 'r+') do |f|
pid = f.pid
f.gets
t0 = Time.now
@@ -1444,10 +1501,11 @@ class TestProcess < Test::Unit::TestCase
th.kill.join
end
t1 = Time.now
+ diff = t1 - t0
+ assert_operator(diff, :<, sec,
+ ->{"#{bug11340}: #{diff} seconds to interrupt Process.wait"})
f.puts
end
- assert_operator(t1 - t0, :<, 3,
- ->{"#{bug11340}: #{t1-t0} seconds to interrupt Process.wait"})
end
def test_abort
@@ -1459,6 +1517,9 @@ class TestProcess < Test::Unit::TestCase
def test_sleep
assert_raise(ArgumentError) { sleep(1, 1) }
+ [-1, -1.0, -1r].each do |sec|
+ assert_raise_with_message(ArgumentError, /not.*negative/) { sleep(sec) }
+ end
end
def test_getpgid
@@ -1492,8 +1553,16 @@ class TestProcess < Test::Unit::TestCase
end
def test_maxgroups
- assert_kind_of(Integer, Process.maxgroups)
+ max = Process.maxgroups
rescue NotImplementedError
+ else
+ assert_kind_of(Integer, max)
+ assert_predicate(max, :positive?)
+ skip "not limited to NGROUPS_MAX" if /darwin/ =~ RUBY_PLATFORM
+ gs = Process.groups
+ assert_operator(gs.size, :<=, max)
+ gs[0] ||= 0
+ assert_raise(ArgumentError) {Process.groups = gs * (max / gs.size + 1)}
end
def test_geteuid
@@ -1506,7 +1575,7 @@ class TestProcess < Test::Unit::TestCase
end
def test_seteuid_name
- user = ENV["USER"] or return
+ user = (Etc.getpwuid(Process.euid).name rescue ENV["USER"]) or return
assert_nothing_raised(TypeError) {Process.euid = user}
rescue NotImplementedError
end
@@ -1551,19 +1620,28 @@ class TestProcess < Test::Unit::TestCase
skip "this fails on FreeBSD and OpenBSD on multithreaded environment"
end
signal_received = []
- Signal.trap(:CHLD) { signal_received << true }
- pid = nil
- IO.pipe do |r, w|
- pid = fork { r.read(1); exit }
- Thread.start { raise }
- w.puts
+ IO.pipe do |sig_r, sig_w|
+ Signal.trap(:CHLD) do
+ signal_received << true
+ sig_w.write('?')
+ end
+ pid = nil
+ IO.pipe do |r, w|
+ pid = fork { r.read(1); exit }
+ Thread.start {
+ Thread.current.report_on_exception = false
+ raise
+ }
+ w.puts
+ end
+ Process.wait pid
+ assert sig_r.wait_readable(5), 'self-pipe not readable'
end
- Process.wait pid
- 10.times do
- break unless signal_received.empty?
- sleep 0.01
+ if RubyVM::MJIT.enabled? # checking -DMJIT_FORCE_ENABLE. It may trigger extra SIGCHLD.
+ assert_equal [true], signal_received.uniq, "[ruby-core:19744]"
+ else
+ assert_equal [true], signal_received, "[ruby-core:19744]"
end
- assert_equal [true], signal_received, " [ruby-core:19744]"
rescue NotImplementedError, ArgumentError
ensure
begin
@@ -1612,6 +1690,9 @@ class TestProcess < Test::Unit::TestCase
end
def test_aspawn_too_long_path
+ if /solaris/i =~ RUBY_PLATFORM && !defined?(Process::RLIMIT_NPROC)
+ skip "Too exhaustive test on platforms without Process::RLIMIT_NPROC such as Solaris 10"
+ end
bug4315 = '[ruby-core:34833] #7904 [ruby-core:52628] #11613'
assert_fail_too_long_path(%w"echo |", bug4315)
end
@@ -1718,7 +1799,7 @@ class TestProcess < Test::Unit::TestCase
puts Dir.entries("/proc/self/task") - %W[. ..]
end
bug4920 = '[ruby-dev:43873]'
- assert_equal(2, data.size, bug4920)
+ assert_include(1..2, data.size, bug4920)
assert_not_include(data.map(&:to_i), pid)
end
else # darwin
@@ -1772,6 +1853,16 @@ class TestProcess < Test::Unit::TestCase
end
end
+ def test_popen_reopen
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ io = File.open(IO::NULL)
+ io2 = io.dup
+ IO.popen("echo") {|f| io.reopen(f)}
+ io.reopen(io2)
+ end;
+ end
+
def test_execopts_new_pgroup
return unless windows?
@@ -1815,7 +1906,12 @@ class TestProcess < Test::Unit::TestCase
skip "Process.groups not implemented on Windows platform" if windows?
feature6975 = '[ruby-core:47414]'
- [30000, *Process.groups.map {|g| g = Etc.getgrgid(g); [g.name, g.gid]}].each do |group, gid|
+ groups = Process.groups.map do |g|
+ g = Etc.getgrgid(g) rescue next
+ [g.name, g.gid]
+ end
+ groups.compact!
+ [30000, *groups].each do |group, gid|
assert_nothing_raised(feature6975) do
begin
system(*TRUECOMMAND, gid: group)
@@ -1991,7 +2087,11 @@ EOS
def test_clock_gettime_GETTIMEOFDAY_BASED_CLOCK_REALTIME
n = :GETTIMEOFDAY_BASED_CLOCK_REALTIME
- t = Process.clock_gettime(n)
+ begin
+ t = Process.clock_gettime(n)
+ rescue Errno::EINVAL
+ return
+ end
assert_kind_of(Float, t, "Process.clock_gettime(:#{n})")
end
@@ -2069,7 +2169,11 @@ EOS
def test_clock_getres_GETTIMEOFDAY_BASED_CLOCK_REALTIME
n = :GETTIMEOFDAY_BASED_CLOCK_REALTIME
- t = Process.clock_getres(n)
+ begin
+ t = Process.clock_getres(n)
+ rescue Errno::EINVAL
+ return
+ end
assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
assert_equal(1000, Process.clock_getres(n, :nanosecond))
end
@@ -2135,7 +2239,7 @@ EOS
end
def test_deadlock_by_signal_at_forking
- assert_separately(["-", RUBY], <<-INPUT, timeout: 60)
+ assert_separately(%W(--disable=gems - #{RUBY}), <<-INPUT, timeout: 100)
ruby = ARGV.shift
GC.start # reduce garbage
GC.disable # avoid triggering CoW after forks
@@ -2143,7 +2247,7 @@ EOS
parent = $$
100.times do |i|
pid = fork {Process.kill(:QUIT, parent)}
- IO.popen(ruby, 'r+'){}
+ IO.popen([ruby, -'--disable=gems'], -'r+'){}
Process.wait(pid)
$stdout.puts
$stdout.flush
@@ -2249,7 +2353,7 @@ EOS
def test_threading_works_after_exec_fail
r, w = IO.pipe
pid = status = nil
- Timeout.timeout(30) do
+ Timeout.timeout(90) do
pid = fork do
r.close
begin
@@ -2257,11 +2361,12 @@ EOS
rescue SystemCallError
w.syswrite("exec failed\n")
end
+ q = Queue.new
run = true
- th1 = Thread.new { i = 0; i += 1 while run; i }
- th2 = Thread.new { j = 0; j += 1 while run && Thread.pass.nil?; j }
+ th1 = Thread.new { i = 0; i += 1 while q.empty?; i }
+ th2 = Thread.new { j = 0; j += 1 while q.empty? && Thread.pass.nil?; j }
sleep 0.5
- run = false
+ q << true
w.syswrite "#{th1.value} #{th2.value}\n"
end
w.close
@@ -2282,6 +2387,15 @@ EOS
r.close if r
end if defined?(fork)
+ def test_rescue_exec_fail
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_raise(Errno::ENOENT) do
+ exec("", in: "")
+ end
+ end;
+ end
+
def test_many_args
bug11418 = '[ruby-core:70251] [Bug #11418]'
assert_in_out_err([], <<-"end;", ["x"]*256, [], bug11418, timeout: 60)
@@ -2309,4 +2423,27 @@ EOS
end
end
end
+
+ def test_forked_child_handles_signal
+ skip "fork not supported" unless Process.respond_to?(:fork)
+ assert_normal_exit(<<-"end;", '[ruby-core:82883] [Bug #13916]')
+ require 'timeout'
+ pid = fork { sleep }
+ Process.kill(:TERM, pid)
+ assert_equal pid, Timeout.timeout(30) { Process.wait(pid) }
+ end;
+ end
+
+ if Process.respond_to?(:initgroups)
+ def test_initgroups
+ assert_raise(ArgumentError) do
+ Process.initgroups("\0", 0)
+ end
+ end
+ end
+
+ def test_last_status
+ Process.wait spawn(RUBY, "-e", "exit 13")
+ assert_same(Process.last_status, $?)
+ end
end
diff --git a/test/ruby/test_rand.rb b/test/ruby/test_rand.rb
index 46d10f8386..ab9a1837f6 100644
--- a/test/ruby/test_rand.rb
+++ b/test/ruby/test_rand.rb
@@ -347,10 +347,15 @@ END
end
def assert_random_bytes(r)
+ srand(0)
assert_equal("", r.bytes(0))
- assert_equal("\xAC".force_encoding("ASCII-8BIT"), r.bytes(1))
- assert_equal("/\xAA\xC4\x97u\xA6\x16\xB7\xC0\xCC".force_encoding("ASCII-8BIT"),
- r.bytes(10))
+ assert_equal("", Random.bytes(0))
+ x = "\xAC".force_encoding("ASCII-8BIT")
+ assert_equal(x, r.bytes(1))
+ assert_equal(x, Random.bytes(1))
+ x = "/\xAA\xC4\x97u\xA6\x16\xB7\xC0\xCC".force_encoding("ASCII-8BIT")
+ assert_equal(x, r.bytes(10))
+ assert_equal(x, Random.bytes(10))
end
def test_random_range
@@ -394,6 +399,7 @@ END
assert_raise(Errno::EDOM, Errno::ERANGE) { r.rand(1.0 / 0.0) }
assert_raise(Errno::EDOM, Errno::ERANGE) { r.rand(0.0 / 0.0) }
+ assert_raise(Errno::EDOM) {r.rand(1..)}
r = Random.new(0)
assert_in_delta(1.5488135039273248, r.rand(1.0...2.0), 0.0001, '[ruby-core:24655]')
@@ -429,9 +435,20 @@ END
def assert_fork_status(n, mesg, &block)
IO.pipe do |r, w|
(1..n).map do
- p1 = fork {w.puts(block.call.to_s)}
- _, st = Process.waitpid2(p1)
- assert_send([st, :success?], mesg)
+ st = desc = nil
+ IO.pipe do |re, we|
+ p1 = fork {
+ re.close
+ STDERR.reopen(we)
+ w.puts(block.call.to_s)
+ }
+ we.close
+ err = Thread.start {re.read}
+ _, st = Process.waitpid2(p1)
+ desc = FailDesc[st, mesg, err.value]
+ end
+ assert(!st.signaled?, desc)
+ assert(st.success?, mesg)
r.gets.strip
end
end
@@ -453,6 +470,10 @@ END
assert_fork_status(1, bug5661) {stable.rand(4)}
r1, r2 = *assert_fork_status(2, bug5661) {stable.rand}
assert_equal(r1, r2, bug5661)
+
+ assert_fork_status(1, '[ruby-core:82100] [Bug #13753]') do
+ Random::DEFAULT.rand(4)
+ end
rescue NotImplementedError
end
@@ -492,7 +513,7 @@ END
def test_initialize_frozen
r = Random.new(0)
r.freeze
- assert_raise(RuntimeError, '[Bug #6540]') do
+ assert_raise(FrozenError, '[Bug #6540]') do
r.__send__(:initialize, r)
end
end
@@ -501,7 +522,7 @@ END
r = Random.new(0)
d = r.__send__(:marshal_dump)
r.freeze
- assert_raise(RuntimeError, '[Bug #6540]') do
+ assert_raise(FrozenError, '[Bug #6540]') do
r.__send__(:marshal_load, d)
end
end
@@ -550,9 +571,9 @@ END
End
end
- def test_raw_seed
+ def test_urandom
[0, 1, 100].each do |size|
- v = Random.raw_seed(size)
+ v = Random.urandom(size)
assert_kind_of(String, v)
assert_equal(size, v.bytesize)
end
diff --git a/test/ruby/test_range.rb b/test/ruby/test_range.rb
index 1ce3f0663a..a7d34655b3 100644
--- a/test/ruby/test_range.rb
+++ b/test/ruby/test_range.rb
@@ -3,18 +3,30 @@ require 'test/unit'
require 'delegate'
require 'timeout'
require 'bigdecimal'
+require 'rbconfig/sizeof'
class TestRange < Test::Unit::TestCase
def test_new
assert_equal((0..2), Range.new(0, 2))
assert_equal((0..2), Range.new(0, 2, false))
assert_equal((0...2), Range.new(0, 2, true))
+
+ assert_raise(ArgumentError) { (1.."3") }
+
+ assert_equal((0..nil), Range.new(0, nil, false))
+ assert_equal((0...nil), Range.new(0, nil, true))
+
+ obj = Object.new
+ def obj.<=>(other)
+ raise RuntimeError, "cmp"
+ end
+ assert_raise_with_message(RuntimeError, "cmp") { (obj..3) }
end
def test_frozen_initialize
r = Range.allocate
r.freeze
- assert_raise(RuntimeError){r.__send__(:initialize, 1, 2)}
+ assert_raise(FrozenError){r.__send__(:initialize, 1, 2)}
end
def test_range_string
@@ -23,14 +35,17 @@ class TestRange < Test::Unit::TestCase
assert_equal(["a"], ("a" .. "a").to_a)
assert_equal(["a"], ("a" ... "b").to_a)
assert_equal(["a", "b"], ("a" .. "b").to_a)
+ assert_equal([*"a".."z", "aa"], ("a"..).take(27))
end
def test_range_numeric_string
assert_equal(["6", "7", "8"], ("6".."8").to_a, "[ruby-talk:343187]")
assert_equal(["6", "7"], ("6"..."8").to_a)
assert_equal(["9", "10"], ("9".."10").to_a)
+ assert_equal(["9", "10"], ("9"..).take(2))
assert_equal(["09", "10"], ("09".."10").to_a, "[ruby-dev:39361]")
assert_equal(["9", "10"], (SimpleDelegator.new("9").."10").to_a)
+ assert_equal(["9", "10"], (SimpleDelegator.new("9")..).take(2))
assert_equal(["9", "10"], ("9"..SimpleDelegator.new("10")).to_a)
end
@@ -65,22 +80,29 @@ class TestRange < Test::Unit::TestCase
assert_equal(1, (1..2).min)
assert_equal(nil, (2..1).min)
assert_equal(1, (1...2).min)
+ assert_equal(1, (1..).min)
assert_equal(1.0, (1.0..2.0).min)
assert_equal(nil, (2.0..1.0).min)
assert_equal(1, (1.0...2.0).min)
+ assert_equal(1, (1.0..).min)
assert_equal(0, (0..0).min)
assert_equal(nil, (0...0).min)
assert_equal([0,1,2], (0..10).min(3))
assert_equal([0,1], (0..1).min(3))
+ assert_equal([0,1,2], (0..).min(3))
+
+ assert_raise(RangeError) { (0..).min {|a, b| a <=> b } }
end
def test_max
assert_equal(2, (1..2).max)
assert_equal(nil, (2..1).max)
assert_equal(1, (1...2).max)
+ assert_raise(RangeError) { (1..).max }
+ assert_raise(RangeError) { (1...).max }
assert_equal(2.0, (1.0..2.0).max)
assert_equal(nil, (2.0..1.0).max)
@@ -95,6 +117,8 @@ class TestRange < Test::Unit::TestCase
assert_equal([10,9,8], (0..10).max(3))
assert_equal([9,8,7], (0...10).max(3))
+ assert_raise(RangeError) { (1..).max(3) }
+ assert_raise(RangeError) { (1...).max(3) }
end
def test_initialize_twice
@@ -115,9 +139,10 @@ class TestRange < Test::Unit::TestCase
assert_equal(r, Marshal.load(Marshal.dump(r)))
r = 1...2
assert_equal(r, Marshal.load(Marshal.dump(r)))
- s = Marshal.dump(r)
- s.sub!(/endi./n, 'end0')
- assert_raise(ArgumentError) {Marshal.load(s)}
+ r = (1..)
+ assert_equal(r, Marshal.load(Marshal.dump(r)))
+ r = (1...)
+ assert_equal(r, Marshal.load(Marshal.dump(r)))
end
def test_bad_value
@@ -127,6 +152,8 @@ class TestRange < Test::Unit::TestCase
def test_exclude_end
assert_not_predicate(0..1, :exclude_end?)
assert_predicate(0...1, :exclude_end?)
+ assert_not_predicate(0.., :exclude_end?)
+ assert_predicate(0..., :exclude_end?)
end
def test_eq
@@ -137,8 +164,17 @@ class TestRange < Test::Unit::TestCase
assert_not_equal(r, (1..2))
assert_not_equal(r, (0..2))
assert_not_equal(r, (0...1))
+ assert_not_equal(r, (0..nil))
subclass = Class.new(Range)
assert_equal(r, subclass.new(0,1))
+
+ r = (0..nil)
+ assert_equal(r, r)
+ assert_equal(r, (0..nil))
+ assert_not_equal(r, 0)
+ assert_not_equal(r, (0...nil))
+ subclass = Class.new(Range)
+ assert_equal(r, subclass.new(0,nil))
end
def test_eql
@@ -151,12 +187,23 @@ class TestRange < Test::Unit::TestCase
assert_not_operator(r, :eql?, 0...1)
subclass = Class.new(Range)
assert_operator(r, :eql?, subclass.new(0,1))
+
+ r = (0..nil)
+ assert_operator(r, :eql?, r)
+ assert_operator(r, :eql?, 0..nil)
+ assert_not_operator(r, :eql?, 0)
+ assert_not_operator(r, :eql?, 0...nil)
+ subclass = Class.new(Range)
+ assert_operator(r, :eql?, subclass.new(0,nil))
end
def test_hash
assert_kind_of(Integer, (0..1).hash)
assert_equal((0..1).hash, (0..1).hash)
assert_not_equal((0..1).hash, (0...1).hash)
+ assert_equal((0..nil).hash, (0..nil).hash)
+ assert_not_equal((0..nil).hash, (0...nil).hash)
+ assert_kind_of(String, (0..1).hash.to_s)
end
def test_step
@@ -165,31 +212,73 @@ class TestRange < Test::Unit::TestCase
assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], a)
a = []
+ (0..).step {|x| a << x; break if a.size == 10 }
+ assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], a)
+
+ a = []
(0..10).step(2) {|x| a << x }
assert_equal([0, 2, 4, 6, 8, 10], a)
- assert_raise(ArgumentError) { (0..10).step(-1) { } }
+ a = []
+ (0..).step(2) {|x| a << x; break if a.size == 10 }
+ assert_equal([0, 2, 4, 6, 8, 10, 12, 14, 16, 18], a)
+
+ assert_kind_of(Enumerator::ArithmeticSequence, (0..10).step)
+ assert_kind_of(Enumerator::ArithmeticSequence, (0..10).step(2))
+ assert_kind_of(Enumerator::ArithmeticSequence, (0..10).step(0.5))
+ assert_kind_of(Enumerator::ArithmeticSequence, (10..0).step(-1))
+
assert_raise(ArgumentError) { (0..10).step(0) { } }
+ assert_raise(ArgumentError) { (0..).step(-1) { } }
+ assert_raise(ArgumentError) { (0..).step(0) { } }
a = []
("a" .. "z").step(2) {|x| a << x }
assert_equal(%w(a c e g i k m o q s u w y), a)
a = []
+ ("a" .. ).step(2) {|x| a << x; break if a.size == 13 }
+ assert_equal(%w(a c e g i k m o q s u w y), a)
+
+ a = []
("a" .. "z").step(2**32) {|x| a << x }
assert_equal(["a"], a)
a = []
+ (:a .. :z).step(2) {|x| a << x }
+ assert_equal(%i(a c e g i k m o q s u w y), a)
+
+ a = []
+ (:a .. ).step(2) {|x| a << x; break if a.size == 13 }
+ assert_equal(%i(a c e g i k m o q s u w y), a)
+
+ a = []
+ (:a .. :z).step(2**32) {|x| a << x }
+ assert_equal([:a], a)
+
+ a = []
(2**32-1 .. 2**32+1).step(2) {|x| a << x }
assert_equal([4294967295, 4294967297], a)
zero = (2**32).coerce(0).first
assert_raise(ArgumentError) { (2**32-1 .. 2**32+1).step(zero) { } }
+ a = []
+ (2**32-1 .. ).step(2) {|x| a << x; break if a.size == 2 }
+ assert_equal([4294967295, 4294967297], a)
+
+ max = RbConfig::LIMITS["FIXNUM_MAX"]
+ a = []
+ (max..).step {|x| a << x; break if a.size == 2 }
+ assert_equal([max, max+1], a)
+ a = []
+ (max..).step(max) {|x| a << x; break if a.size == 4 }
+ assert_equal([max, 2*max, 3*max, 4*max], a)
o1 = Object.new
o2 = Object.new
def o1.<=>(x); -1; end
def o2.<=>(x); 0; end
assert_raise(TypeError) { (o1..o2).step(1) { } }
+ assert_raise(TypeError) { (o1..).step(1) { } }
class << o1; self; end.class_eval do
define_method(:succ) { o2 }
@@ -209,12 +298,38 @@ class TestRange < Test::Unit::TestCase
assert_equal([0, 0.5, 1.0, 1.5, 2.0], a)
a = []
+ (0..).step(0.5) {|x| a << x; break if a.size == 5 }
+ assert_equal([0, 0.5, 1.0, 1.5, 2.0], a)
+
+ a = []
(0x40000000..0x40000002).step(0.5) {|x| a << x }
assert_equal([1073741824, 1073741824.5, 1073741825.0, 1073741825.5, 1073741826], a)
o = Object.new
def o.to_int() 1 end
assert_nothing_raised("[ruby-dev:34558]") { (0..2).step(o) {|x| } }
+
+ o = Object.new
+ class << o
+ def to_str() "a" end
+ def <=>(other) to_str <=> other end
+ end
+
+ a = []
+ (o.."c").step(1) {|x| a << x}
+ assert_equal(["a", "b", "c"], a)
+ a = []
+ (o..).step(1) {|x| a << x; break if a.size >= 3}
+ assert_equal(["a", "b", "c"], a)
+ end
+
+ def test_percent_step
+ aseq = (1..10) % 2
+ assert_equal(Enumerator::ArithmeticSequence, aseq.class)
+ assert_equal(1, aseq.begin)
+ assert_equal(10, aseq.end)
+ assert_equal(2, aseq.step)
+ assert_equal([1, 3, 5, 7, 9], aseq.to_a)
end
def test_step_ruby_core_35753
@@ -231,6 +346,10 @@ class TestRange < Test::Unit::TestCase
(0..10).each {|x| a << x }
assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], a)
+ a = []
+ (0..).each {|x| a << x; break if a.size == 10 }
+ assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], a)
+
o1 = Object.new
o2 = Object.new
def o1.setcmp(v) @cmpresult = v end
@@ -271,12 +390,28 @@ class TestRange < Test::Unit::TestCase
a = []
r2.each {|x| a << x }
assert_equal([], a)
+
+ o = Object.new
+ class << o
+ def to_str() "a" end
+ def <=>(other) to_str <=> other end
+ end
+
+ a = []
+ (o.."c").each {|x| a << x}
+ assert_equal(["a", "b", "c"], a)
+ a = []
+ (o..).each {|x| a << x; break if a.size >= 3}
+ assert_equal(["a", "b", "c"], a)
end
def test_begin_end
assert_equal(0, (0..1).begin)
assert_equal(1, (0..1).end)
assert_equal(1, (0...1).end)
+ assert_equal(0, (0..nil).begin)
+ assert_equal(nil, (0..nil).end)
+ assert_equal(nil, (0...nil).end)
end
def test_first_last
@@ -295,11 +430,19 @@ class TestRange < Test::Unit::TestCase
assert_equal("a", ("a"..."c").first)
assert_equal("c", ("a"..."c").last)
assert_equal(0, (2...0).last)
+
+ assert_equal([0, 1, 2], (0..nil).first(3))
+ assert_equal(0, (0..nil).first)
+ assert_equal("a", ("a"..nil).first)
+ assert_raise(RangeError) { (0..nil).last }
+ assert_raise(RangeError) { (0..nil).last(3) }
end
def test_to_s
assert_equal("0..1", (0..1).to_s)
assert_equal("0...1", (0...1).to_s)
+ assert_equal("0..", (0..nil).to_s)
+ assert_equal("0...", (0...nil).to_s)
bug11767 = '[ruby-core:71811] [Bug #11767]'
assert_predicate(("0".taint.."1").to_s, :tainted?, bug11767)
@@ -310,6 +453,8 @@ class TestRange < Test::Unit::TestCase
def test_inspect
assert_equal("0..1", (0..1).inspect)
assert_equal("0...1", (0...1).inspect)
+ assert_equal("0..", (0..nil).inspect)
+ assert_equal("0...", (0...nil).inspect)
bug11767 = '[ruby-core:71811] [Bug #11767]'
assert_predicate(("0".taint.."1").inspect, :tainted?, bug11767)
@@ -320,6 +465,8 @@ class TestRange < Test::Unit::TestCase
def test_eqq
assert_operator(0..10, :===, 5)
assert_not_operator(0..10, :===, 11)
+ assert_operator(5..nil, :===, 11)
+ assert_not_operator(5..nil, :===, 0)
end
def test_eqq_time
@@ -327,6 +474,8 @@ class TestRange < Test::Unit::TestCase
t = Time.now
assert_nothing_raised(TypeError, bug11113) {
assert_operator(t..(t+10), :===, t+5)
+ assert_operator(t.., :===, t+5)
+ assert_not_operator(t.., :===, t-5)
}
end
@@ -354,13 +503,27 @@ class TestRange < Test::Unit::TestCase
assert_operator(c.new(0)..c.new(10), :===, c.new(5), bug12003)
end
+ def test_eqq_non_iteratable
+ k = Class.new do
+ include Comparable
+ attr_reader :i
+ def initialize(i) @i = i; end
+ def <=>(o); i <=> o.i; end
+ end
+ assert_operator(k.new(0)..k.new(2), :===, k.new(1))
+ end
+
def test_include
assert_include("a".."z", "c")
assert_not_include("a".."z", "5")
assert_include("a"..."z", "y")
assert_not_include("a"..."z", "z")
assert_not_include("a".."z", "cc")
+ assert_include("a".., "c")
+ assert_not_include("a".., "5")
assert_include(0...10, 5)
+ assert_include(5..., 10)
+ assert_not_include(5..., 0)
end
def test_cover
@@ -369,6 +532,55 @@ class TestRange < Test::Unit::TestCase
assert_operator("a"..."z", :cover?, "y")
assert_not_operator("a"..."z", :cover?, "z")
assert_operator("a".."z", :cover?, "cc")
+ assert_not_operator(5..., :cover?, 0)
+ assert_not_operator(5..., :cover?, "a")
+ assert_operator(5.., :cover?, 10)
+
+ assert_operator(2..5, :cover?, 2..5)
+ assert_operator(2...6, :cover?, 2...6)
+ assert_operator(2...6, :cover?, 2..5)
+ assert_operator(2..5, :cover?, 2...6)
+ assert_operator(2..5, :cover?, 2..4)
+ assert_operator(2..5, :cover?, 2...4)
+ assert_operator(2..5, :cover?, 2...5)
+ assert_operator(2..5, :cover?, 3..5)
+ assert_operator(2..5, :cover?, 3..4)
+ assert_operator(2..5, :cover?, 3...6)
+ assert_operator(2...6, :cover?, 2...5)
+ assert_operator(2...6, :cover?, 2..5)
+ assert_operator(2..6, :cover?, 2...6)
+ assert_operator(2.., :cover?, 2..)
+ assert_operator(2.., :cover?, 3..)
+ assert_operator(1.., :cover?, 1..10)
+ assert_operator(2.0..5.0, :cover?, 2..3)
+ assert_operator(2..5, :cover?, 2.0..3.0)
+ assert_operator(2..5, :cover?, 2.0...3.0)
+ assert_operator(2..5, :cover?, 2.0...5.0)
+ assert_operator(2.0..5.0, :cover?, 2.0...3.0)
+ assert_operator(2.0..5.0, :cover?, 2.0...5.0)
+ assert_operator('aa'..'zz', :cover?, 'aa'...'bb')
+
+ assert_not_operator(2..5, :cover?, 1..5)
+ assert_not_operator(2...6, :cover?, 1..5)
+ assert_not_operator(2..5, :cover?, 1...6)
+ assert_not_operator(1..3, :cover?, 1...6)
+ assert_not_operator(2..5, :cover?, 2..6)
+ assert_not_operator(2...6, :cover?, 2..6)
+ assert_not_operator(2...6, :cover?, 2...7)
+ assert_not_operator(2..3, :cover?, 1..4)
+ assert_not_operator(1..2, :cover?, 1.0..3.0)
+ assert_not_operator(1.0..2.9, :cover?, 1.0..3.0)
+ assert_not_operator(1..2, :cover?, 4..3)
+ assert_not_operator(2..1, :cover?, 1..2)
+ assert_not_operator(1...2, :cover?, 1...3)
+ assert_not_operator(2.., :cover?, 1..)
+ assert_not_operator(2.., :cover?, 1..10)
+ assert_not_operator(1..10, :cover?, 1..)
+ assert_not_operator(1..5, :cover?, 3..2)
+ assert_not_operator(1..10, :cover?, 3...2)
+ assert_not_operator(1..10, :cover?, 3...3)
+ assert_not_operator('aa'..'zz', :cover?, 'aa'...'zzz')
+ assert_not_operator(1..10, :cover?, 1...10.1)
end
def test_beg_len
@@ -379,12 +591,15 @@ class TestRange < Test::Unit::TestCase
assert_raise(TypeError) { [][o] }
class << o; attr_accessor :end end
o.end = 0
- assert_raise(NoMethodError) { [][o] }
+ assert_raise(TypeError) { [][o] }
def o.exclude_end=(v) @exclude_end = v end
def o.exclude_end?() @exclude_end end
o.exclude_end = false
assert_nil([0][o])
assert_raise(RangeError) { [0][o] = 1 }
+ class << o
+ private :begin, :end
+ end
o.begin = 10
o.end = 10
assert_nil([0][o])
@@ -443,6 +658,11 @@ class TestRange < Test::Unit::TestCase
assert_equal 6, (1...6.3).size
assert_equal 5, (1.1...6).size
assert_equal 42, (1..42).each.size
+ assert_nil ("a"..."z").size
+
+ assert_equal Float::INFINITY, (1...).size
+ assert_equal Float::INFINITY, (1.0...).size
+ assert_nil ("a"...).size
end
def test_bsearch_typechecks_return_values
@@ -484,6 +704,8 @@ class TestRange < Test::Unit::TestCase
ary = [0, 100, 100, 100, 200]
assert_equal(1, (0...ary.size).bsearch {|i| ary[i] >= 100 })
+
+ assert_equal(1_000_001, (0...).bsearch {|i| i > 1_000_000 })
end
def test_bsearch_for_float
@@ -535,6 +757,8 @@ class TestRange < Test::Unit::TestCase
assert_in_delta(1.0, (0.0..inf).bsearch {|x| Math.log(x) >= 0 })
assert_in_delta(7.0, (0.0..10).bsearch {|x| 7.0 - x })
+
+ assert_equal(1_000_000.0.next_float, (0.0..).bsearch {|x| x > 1_000_000 })
end
def check_bsearch_values(range, search, a)
@@ -636,21 +860,11 @@ class TestRange < Test::Unit::TestCase
assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 100 })
assert_equal(bignum + 0, (bignum...bignum+ary.size).bsearch {|i| true })
assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| false })
+ assert_equal(bignum * 2 + 1, (bignum...).bsearch {|i| i > bignum * 2 })
assert_raise(TypeError) { ("a".."z").bsearch {} }
end
- def test_bsearch_with_mathn
- assert_separately ['-r', 'mathn'], %q{
- msg = '[ruby-core:25740]'
- answer = (1..(1 << 100)).bsearch{|x|
- assert_predicate(x, :integer?, msg)
- x >= 42
- }
- assert_equal(42, answer, msg)
- }, ignore_stderr: true
- end
-
def test_each_no_blockarg
a = "a"
def a.upto(x, e, &b)
@@ -658,4 +872,10 @@ class TestRange < Test::Unit::TestCase
end
(a.."c").each {|x, &b| assert_nil(b)}
end
+
+ def test_to_a
+ assert_equal([1,2,3,4,5], (1..5).to_a)
+ assert_equal([1,2,3,4], (1...5).to_a)
+ assert_raise(RangeError) { (1..).to_a }
+ end
end
diff --git a/test/ruby/test_rational.rb b/test/ruby/test_rational.rb
index 834188e4d8..2957e1bfc8 100644
--- a/test/ruby/test_rational.rb
+++ b/test/ruby/test_rational.rb
@@ -42,7 +42,9 @@ class Rational_Test < Test::Unit::TestCase
end
def test_hash
- assert_kind_of(Integer, Rational(1,2).hash)
+ h = Rational(1,2).hash
+ assert_kind_of(Integer, h)
+ assert_nothing_raised {h.to_s}
h = {}
h[Rational(0)] = 0
@@ -59,7 +61,6 @@ class Rational_Test < Test::Unit::TestCase
def test_freeze
c = Rational(1)
- c.freeze
assert_predicate(c, :frozen?)
assert_instance_of(String, c.to_s)
end
@@ -109,12 +110,45 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(Rational(3),Rational('3'))
assert_equal(Rational(1),Rational('3.0','3.0'))
assert_equal(Rational(1),Rational('3/3','3/3'))
+ assert_equal(Rational(111, 1), Rational('1.11e+2'))
+ assert_equal(Rational(111, 10), Rational('1.11e+1'))
+ assert_equal(Rational(111, 10), Rational('1.11e1'))
+ assert_equal(Rational(111, 100), Rational('1.11e0'))
+ assert_equal(Rational(111, 1000), Rational('1.11e-1'))
assert_raise(TypeError){Rational(nil)}
assert_raise(ArgumentError){Rational('')}
assert_raise_with_message(ArgumentError, /\u{221a 2668}/) {
Rational("\u{221a 2668}")
}
+ assert_warning('') {
+ assert_predicate(Rational('1e-99999999999999999999'), :zero?)
+ }
+
assert_raise(TypeError){Rational(Object.new)}
+ assert_raise(TypeError){Rational(Object.new, Object.new)}
+ assert_raise(TypeError){Rational(1, Object.new)}
+
+ o = Object.new
+ def o.to_r; 1/42r; end
+ assert_equal(1/42r, Rational(o))
+ assert_equal(1/84r, Rational(o, 2))
+ assert_equal(42, Rational(1, o))
+ assert_equal(1, Rational(o, o))
+
+ o = Object.new
+ def o.to_r; nil; end
+ assert_raise(TypeError) { Rational(o) }
+ assert_raise(TypeError) { Rational(o, 2) }
+ assert_raise(TypeError) { Rational(1, o) }
+ assert_raise(TypeError) { Rational(o, o) }
+
+ o = Object.new
+ def o.to_r; raise; end
+ assert_raise(RuntimeError) { Rational(o) }
+ assert_raise(RuntimeError) { Rational(o, 2) }
+ assert_raise(RuntimeError) { Rational(1, o) }
+ assert_raise(RuntimeError) { Rational(o, o) }
+
assert_raise(ArgumentError){Rational()}
assert_raise(ArgumentError){Rational(1,2,3)}
@@ -561,13 +595,20 @@ class Rational_Test < Test::Unit::TestCase
assert_equal([Rational(2.2),Rational(1)], Rational(1).coerce(2.2))
assert_equal([Rational(2),Rational(1)], Rational(1).coerce(Rational(2)))
- assert_nothing_raised(TypeError, '[Bug #5020] [ruby-devl:44088]') do
+ assert_nothing_raised(TypeError, '[Bug #5020] [ruby-dev:44088]') do
Rational(1,2).coerce(Complex(1,1))
end
+
+ assert_raise(ZeroDivisionError) do
+ 1 / 0r.coerce(0+0i)[0]
+ end
+ assert_raise(ZeroDivisionError) do
+ 1 / 0r.coerce(0.0+0i)[0]
+ end
end
class ObjectX
- def + (x) Rational(1) end
+ def +(x) Rational(1) end
alias - +
alias * +
alias / +
@@ -597,12 +638,12 @@ class Rational_Test < Test::Unit::TestCase
end
def test_trunc
- [[Rational(13, 5), [ 2, 3, 2, 3, 3, 3]], # 2.6
- [Rational(5, 2), [ 2, 3, 2, 2, 2, 3]], # 2.5
- [Rational(12, 5), [ 2, 3, 2, 2, 2, 2]], # 2.4
- [Rational(-12,5), [-3, -2, -2, -2, -2, -2]], # -2.4
- [Rational(-5, 2), [-3, -2, -2, -2, -2, -3]], # -2.5
- [Rational(-13, 5), [-3, -2, -2, -3, -3, -3]], # -2.6
+ [[Rational(13, 5), [ 2, 3, 2, 3, 3, 3, 3]], # 2.6
+ [Rational(5, 2), [ 2, 3, 2, 3, 2, 3, 2]], # 2.5
+ [Rational(12, 5), [ 2, 3, 2, 2, 2, 2, 2]], # 2.4
+ [Rational(-12,5), [-3, -2, -2, -2, -2, -2, -2]], # -2.4
+ [Rational(-5, 2), [-3, -2, -2, -3, -2, -3, -2]], # -2.5
+ [Rational(-13, 5), [-3, -2, -2, -3, -3, -3, -3]], # -2.6
].each do |i, a|
s = proc {i.inspect}
assert_equal(a[0], i.floor, s)
@@ -611,6 +652,7 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(a[3], i.round, s)
assert_equal(a[4], i.round(half: :even), s)
assert_equal(a[5], i.round(half: :up), s)
+ assert_equal(a[6], i.round(half: :down), s)
end
end
@@ -638,12 +680,10 @@ class Rational_Test < Test::Unit::TestCase
def test_marshal
c = Rational(1,2)
- c.instance_eval{@ivar = 9}
s = Marshal.dump(c)
c2 = Marshal.load(s)
assert_equal(c, c2)
- assert_equal(9, c2.instance_variable_get(:@ivar))
assert_instance_of(Rational, c2)
assert_raise(TypeError){
@@ -656,7 +696,6 @@ class Rational_Test < Test::Unit::TestCase
bug3656 = '[ruby-core:31622]'
c = Rational(1,2)
- c.freeze
assert_predicate(c, :frozen?)
result = c.marshal_load([2,3]) rescue :fail
assert_equal(:fail, result, bug3656)
@@ -674,100 +713,136 @@ class Rational_Test < Test::Unit::TestCase
end
end
+ def assert_valid_rational(n, d, r)
+ x = Rational(n, d)
+ assert_equal(x, r.to_r, "#{r.dump}.to_r")
+ assert_equal(x, Rational(r), "Rational(#{r.dump})")
+ end
+
+ def assert_invalid_rational(n, d, r)
+ x = Rational(n, d)
+ assert_equal(x, r.to_r, "#{r.dump}.to_r")
+ assert_raise(ArgumentError, "Rational(#{r.dump})") {Rational(r)}
+ end
+
def test_parse
- assert_equal(Rational(5), '5'.to_r)
- assert_equal(Rational(-5), '-5'.to_r)
- assert_equal(Rational(5,3), '5/3'.to_r)
- assert_equal(Rational(-5,3), '-5/3'.to_r)
-
- assert_equal(Rational(5), '5.0'.to_r)
- assert_equal(Rational(-5), '-5.0'.to_r)
- assert_equal(Rational(5,3), '5.0/3'.to_r)
- assert_equal(Rational(-5,3), '-5.0/3'.to_r)
-
- assert_equal(Rational(5), '5e0'.to_r)
- assert_equal(Rational(-5), '-5e0'.to_r)
- assert_equal(Rational(5,3), '5e0/3'.to_r)
- assert_equal(Rational(-5,3), '-5e0/3'.to_r)
-
- assert_equal(Rational(5e1), '5e1'.to_r)
- assert_equal(Rational(-5e2), '-5e2'.to_r)
- assert_equal(Rational(5e3,3), '5e003/3'.to_r)
- assert_equal(Rational(-5e4,3), '-5e004/3'.to_r)
-
- assert_equal(Rational(33,100), '.33'.to_r)
- assert_equal(Rational(33,100), '0.33'.to_r)
- assert_equal(Rational(-33,100), '-.33'.to_r)
- assert_equal(Rational(-33,100), '-0.33'.to_r)
- assert_equal(Rational(-33,100), '-0.3_3'.to_r)
-
- assert_equal(Rational(1,2), '5e-1'.to_r)
- assert_equal(Rational(50), '5e+1'.to_r)
- assert_equal(Rational(1,2), '5.0e-1'.to_r)
- assert_equal(Rational(50), '5.0e+1'.to_r)
- assert_equal(Rational(50), '5e1'.to_r)
- assert_equal(Rational(50), '5E1'.to_r)
- assert_equal(Rational(500), '5e2'.to_r)
- assert_equal(Rational(5000), '5e3'.to_r)
- assert_equal(Rational(500000000000), '5e1_1'.to_r)
-
- assert_equal(Rational(5), Rational('5'))
- assert_equal(Rational(-5), Rational('-5'))
- assert_equal(Rational(5,3), Rational('5/3'))
- assert_equal(Rational(-5,3), Rational('-5/3'))
-
- assert_equal(Rational(5), Rational('5.0'))
- assert_equal(Rational(-5), Rational('-5.0'))
- assert_equal(Rational(5,3), Rational('5.0/3'))
- assert_equal(Rational(-5,3), Rational('-5.0/3'))
-
- assert_equal(Rational(5), Rational('5e0'))
- assert_equal(Rational(-5), Rational('-5e0'))
- assert_equal(Rational(5,3), Rational('5e0/3'))
- assert_equal(Rational(-5,3), Rational('-5e0/3'))
-
- assert_equal(Rational(5e1), Rational('5e1'))
- assert_equal(Rational(-5e2), Rational('-5e2'))
- assert_equal(Rational(5e3,3), Rational('5e003/3'))
- assert_equal(Rational(-5e4,3), Rational('-5e004/3'))
-
- assert_equal(Rational(33,100), Rational('.33'))
- assert_equal(Rational(33,100), Rational('0.33'))
- assert_equal(Rational(-33,100), Rational('-.33'))
- assert_equal(Rational(-33,100), Rational('-0.33'))
- assert_equal(Rational(-33,100), Rational('-0.3_3'))
-
- assert_equal(Rational(1,2), Rational('5e-1'))
- assert_equal(Rational(50), Rational('5e+1'))
- assert_equal(Rational(1,2), Rational('5.0e-1'))
- assert_equal(Rational(50), Rational('5.0e+1'))
- assert_equal(Rational(50), Rational('5e1'))
- assert_equal(Rational(50), Rational('5E1'))
- assert_equal(Rational(500), Rational('5e2'))
- assert_equal(Rational(5000), Rational('5e3'))
- assert_equal(Rational(500000000000), Rational('5e1_1'))
-
- assert_equal(Rational(0), ''.to_r)
- assert_equal(Rational(0), ' '.to_r)
- assert_equal(Rational(5), "\f\n\r\t\v5\0".to_r)
- assert_equal(Rational(0), '_'.to_r)
- assert_equal(Rational(0), '_5'.to_r)
- assert_equal(Rational(5), '5_'.to_r)
- assert_equal(Rational(5), '5x'.to_r)
- assert_equal(Rational(5), '5/_3'.to_r)
- assert_equal(Rational(5,3), '5/3_'.to_r)
- assert_equal(Rational(5,3), '5/3.3'.to_r)
- assert_equal(Rational(5,3), '5/3x'.to_r)
- assert_raise(ArgumentError){ Rational('')}
- assert_raise(ArgumentError){ Rational('_')}
- assert_raise(ArgumentError){ Rational("\f\n\r\t\v5\0")}
- assert_raise(ArgumentError){ Rational('_5')}
- assert_raise(ArgumentError){ Rational('5_')}
- assert_raise(ArgumentError){ Rational('5x')}
- assert_raise(ArgumentError){ Rational('5/_3')}
- assert_raise(ArgumentError){ Rational('5/3_')}
- assert_raise(ArgumentError){ Rational('5/3.3')}
- assert_raise(ArgumentError){ Rational('5/3x')}
+ ok = method(:assert_valid_rational)
+ ng = method(:assert_invalid_rational)
+
+ ok[ 5, 1, '5']
+ ok[-5, 1, '-5']
+ ok[ 5, 3, '5/3']
+ ok[-5, 3, '-5/3']
+ ok[ 5, 3, '5_5/33']
+ ok[ 5,33, '5/3_3']
+ ng[ 5, 1, '5__5/33']
+ ng[ 5, 3, '5/3__3']
+
+ ok[ 5, 1, '5.0']
+ ok[-5, 1, '-5.0']
+ ok[ 5, 3, '5.0/3']
+ ok[-5, 3, '-5.0/3']
+ ok[ 501,100, '5.0_1']
+ ok[ 501,300, '5.0_1/3']
+ ok[ 5,33, '5.0/3_3']
+ ng[ 5, 1, '5.0__1/3']
+ ng[ 5, 3, '5.0/3__3']
+
+ ok[ 5, 1, '5e0']
+ ok[-5, 1, '-5e0']
+ ok[ 5, 3, '5e0/3']
+ ok[-5, 3, '-5e0/3']
+ ok[550, 1, '5_5e1']
+ ng[ 5, 1, '5_e1']
+
+ ok[ 5e1, 1, '5e1']
+ ok[-5e2, 1, '-5e2']
+ ok[ 5e3, 3, '5e003/3']
+ ok[-5e4, 3, '-5e004/3']
+ ok[ 5e3, 1, '5e0_3']
+ ok[ 5e1,33, '5e1/3_3']
+ ng[ 5e0, 1, '5e0__3/3']
+ ng[ 5e1, 3, '5e1/3__3']
+
+ ok[ 33, 100, '.33']
+ ok[ 33, 100, '0.33']
+ ok[-33, 100, '-.33']
+ ok[-33, 100, '-0.33']
+ ok[-33, 100, '-0.3_3']
+ ng[ -3, 10, '-0.3__3']
+
+ ok[ 1, 2, '5e-1']
+ ok[50, 1, '5e+1']
+ ok[ 1, 2, '5.0e-1']
+ ok[50, 1, '5.0e+1']
+ ok[50, 1, '5e1']
+ ok[50, 1, '5E1']
+ ok[500, 1, '5e2']
+ ok[5000, 1, '5e3']
+ ok[500000000000, 1, '5e1_1']
+ ng[ 5, 1, '5e']
+ ng[ 5, 1, '5e_']
+ ng[ 5, 1, '5e_1']
+ ng[50, 1, '5e1_']
+
+ ok[ 50, 33, '5/3.3']
+ ok[ 5, 3, '5/3e0']
+ ok[ 5, 30, '5/3e1']
+ ng[ 5, 3, '5/3._3']
+ ng[ 50, 33, '5/3.3_']
+ ok[500,333, '5/3.3_3']
+ ng[ 5, 3, '5/3e']
+ ng[ 5, 3, '5/3_e']
+ ng[ 5, 3, '5/3e_']
+ ng[ 5, 3, '5/3e_1']
+ ng[ 5, 30, '5/3e1_']
+ ok[ 5, 300000000000, '5/3e1_1']
+
+ ng[0, 1, '']
+ ng[0, 1, ' ']
+ ng[5, 1, "\f\n\r\t\v5\0"]
+ ng[0, 1, '_']
+ ng[0, 1, '_5']
+ ng[5, 1, '5_']
+ ng[5, 1, '5x']
+ ng[5, 1, '5/_3']
+ ng[5, 3, '5/3_']
+ ng[5, 3, '5/3x']
+ end
+
+ def test_parse_zero_denominator
+ assert_raise(ZeroDivisionError) {"1/0".to_r}
+ assert_raise(ZeroDivisionError) {Rational("1/0")}
+ end
+
+ def test_Rational_without_exception
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(nil, Rational("5/3x", exception: false))
+ }
+ assert_nothing_raised(ZeroDivisionError) {
+ assert_equal(nil, Rational("1/0", exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Rational(nil, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Rational(Object.new, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Rational(1, nil, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Rational(1, Object.new, exception: false))
+ }
+
+ o = Object.new;
+ def o.to_r; raise; end
+ assert_nothing_raised(RuntimeError) {
+ assert_equal(nil, Rational(o, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Rational(1, o, exception: false))
+ }
end
def test_to_i
@@ -778,6 +853,7 @@ class Rational_Test < Test::Unit::TestCase
def test_to_f
assert_equal(1.5, Rational(3,2).to_f)
assert_equal(1.5, Float(Rational(3,2)))
+ assert_equal(1e-23, Rational(1, 10**23).to_f, "Bug #14637")
end
def test_to_c
@@ -879,6 +955,16 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(1152921470247108503, 1073741789.lcm(1073741827))
end
+ def test_gcd_no_memory_leak
+ assert_no_memory_leak([], "#{<<-"begin;"}", "#{<<-"end;"}", limit: 1.2, rss: true)
+ x = (1<<121) + 1
+ y = (1<<99) + 1
+ 1000.times{x.gcd(y)}
+ begin;
+ 100.times {1000.times{x.gcd(y)}}
+ end;
+ end
+
def test_supp
assert_predicate(1, :real?)
assert_predicate(1.1, :real?)
@@ -904,6 +990,13 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(5000000000.0, 10000000000.fdiv(2))
assert_equal(0.5, 1.0.fdiv(2))
assert_equal(0.25, Rational(1,2).fdiv(2))
+
+ a = 0xa42fcabf_c51ce400_00001000_00000000_00000000_00000000_00000000_00000000
+ b = 1<<1074
+ assert_equal(Rational(a, b).to_f, a.fdiv(b))
+ a = 3
+ b = 0x20_0000_0000_0001
+ assert_equal(Rational(a, b).to_f, a.fdiv(b))
end
def test_ruby19
@@ -937,6 +1030,14 @@ class Rational_Test < Test::Unit::TestCase
assert_raise(ZeroDivisionError, bug5713) { Rational(0, 1) ** Rational(-2,3) }
end
+ def test_power_overflow
+ bug = '[ruby-core:79686] [Bug #13242]: Infinity due to overflow'
+ x = EnvUtil.suppress_warning {4r**40000000}
+ assert_predicate x, :infinite?, bug
+ x = EnvUtil.suppress_warning {(1/4r)**40000000}
+ assert_equal 0, x, bug
+ end
+
def test_positive_p
assert_predicate(1/2r, :positive?)
assert_not_predicate(-1/2r, :positive?)
diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb
index 6c32be5e61..38d0adbd36 100644
--- a/test/ruby/test_refinement.rb
+++ b/test/ruby/test_refinement.rb
@@ -19,6 +19,10 @@ class TestRefinement < Test::Unit::TestCase
return "Foo#a"
end
+ def b
+ return "Foo#b"
+ end
+
def call_x
return x
end
@@ -41,6 +45,10 @@ class TestRefinement < Test::Unit::TestCase
def a
return "FooExt#a"
end
+
+ private def b
+ return "FooExt#b"
+ end
end
end
@@ -94,6 +102,18 @@ class TestRefinement < Test::Unit::TestCase
return foo.send(:z)
end
+ def self.send_b_on(foo)
+ return foo.send(:b)
+ end
+
+ def self.public_send_z_on(foo)
+ return foo.public_send(:z)
+ end
+
+ def self.public_send_b_on(foo)
+ return foo.public_send(:b)
+ end
+
def self.method_z(foo)
return foo.method(:z)
end
@@ -179,11 +199,20 @@ class TestRefinement < Test::Unit::TestCase
foo = Foo.new
assert_raise(NoMethodError) { foo.send(:z) }
assert_equal("FooExt#z", FooExtClient.send_z_on(foo))
+ assert_equal("FooExt#b", FooExtClient.send_b_on(foo))
assert_raise(NoMethodError) { foo.send(:z) }
assert_equal(true, RespondTo::Sub.new.respond_to?(:foo))
end
+ def test_public_send_should_use_refinements
+ foo = Foo.new
+ assert_raise(NoMethodError) { foo.public_send(:z) }
+ assert_equal("FooExt#z", FooExtClient.public_send_z_on(foo))
+ assert_equal("Foo#b", foo.public_send(:b))
+ assert_raise(NoMethodError) { FooExtClient.public_send_b_on(foo) }
+ end
+
def test_method_should_not_use_refinements
foo = Foo.new
assert_raise(NameError) { foo.method(:z) }
@@ -297,9 +326,9 @@ class TestRefinement < Test::Unit::TestCase
end
end
- def test_respond_to_should_not_use_refinements
+ def test_respond_to_should_use_refinements
assert_equal(false, 1.respond_to?(:foo))
- assert_equal(false, eval_using(IntegerFooExt, "1.respond_to?(:foo)"))
+ assert_equal(true, eval_using(IntegerFooExt, "1.respond_to?(:foo)"))
end
module StringCmpExt
@@ -907,6 +936,10 @@ class TestRefinement < Test::Unit::TestCase
return foo.send(:z)
end
+ def self.public_send_z_on(foo)
+ return foo.public_send(:z)
+ end
+
def self.method_z(foo)
return foo.method(:z)
end
@@ -1474,6 +1507,25 @@ class TestRefinement < Test::Unit::TestCase
INPUT
end
+ def test_undef_prepended_method
+ bug13096 = '[ruby-core:78944] [Bug #13096]'
+ klass = EnvUtil.labeled_class("X") do
+ def foo; end
+ end
+ klass.prepend(Module.new)
+ ext = EnvUtil.labeled_module("Ext") do
+ refine klass do
+ def foo
+ end
+ end
+ end
+ assert_nothing_raised(NameError, bug13096) do
+ klass.class_eval do
+ undef :foo
+ end
+ end
+ end
+
def test_call_refined_method_in_duplicate_module
bug10885 = '[ruby-dev:48878]'
assert_in_out_err([], <<-INPUT, [], [], bug10885)
@@ -1750,6 +1802,31 @@ class TestRefinement < Test::Unit::TestCase
assert_equal("Foo#x", FooExtClient.return_proc(&:x).(Foo.new))
end
+ def test_symbol_proc_with_block
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ bug = '[ruby-core:80219] [Bug #13325]'
+ begin;
+ module M
+ refine Class.new do
+ end
+ end
+ class C
+ def call(a, x, &b)
+ b.call(a, &x)
+ end
+ end
+ o = C.new
+ r = nil
+ x = ->(z){r = z}
+ assert_equal(42, o.call(42, x, &:tap))
+ assert_equal(42, r)
+ using M
+ r = nil
+ assert_equal(42, o.call(42, x, &:tap), bug)
+ assert_equal(42, r, bug)
+ end;
+ end
+
module AliasInSubclass
class C
def foo
@@ -1789,6 +1866,395 @@ class TestRefinement < Test::Unit::TestCase
end;
end
+ def test_public_in_refine
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ bug12729 = '[ruby-core:77161] [Bug #12729]'
+
+ class Cow
+ private
+ def moo() "Moo"; end
+ end
+
+ module PublicCows
+ refine(Cow) {
+ public :moo
+ }
+ end
+
+ using PublicCows
+ assert_equal("Moo", Cow.new.moo, bug12729)
+ end;
+ end
+
+ module SuperToModule
+ class Parent
+ end
+
+ class Child < Parent
+ end
+
+ module FooBar
+ refine Parent do
+ def to_s
+ "Parent"
+ end
+ end
+
+ refine Child do
+ def to_s
+ super + " -> Child"
+ end
+ end
+ end
+
+ using FooBar
+ def Child.test
+ new.to_s
+ end
+ end
+
+ def test_super_to_module
+ bug = '[ruby-core:79588] [Bug #13227]'
+ assert_equal("Parent -> Child", SuperToModule::Child.test, bug)
+ end
+
+ def test_include_refinement
+ bug = '[ruby-core:79632] [Bug #13236] cannot include refinement module'
+ r = nil
+ m = Module.new do
+ r = refine(String) {def test;:ok end}
+ end
+ assert_raise_with_message(ArgumentError, /refinement/, bug) do
+ m.module_eval {include r}
+ end
+ assert_raise_with_message(ArgumentError, /refinement/, bug) do
+ m.module_eval {prepend r}
+ end
+ end
+
+ class ParentDefiningPrivateMethod
+ private
+ def some_inherited_method
+ end
+ end
+
+ module MixinDefiningPrivateMethod
+ private
+ def some_included_method
+ end
+ end
+
+ class SomeChildClassToRefine < ParentDefiningPrivateMethod
+ include MixinDefiningPrivateMethod
+
+ private
+ def some_method
+ end
+ end
+
+ def test_refine_inherited_method_with_visibility_changes
+ Module.new do
+ refine(SomeChildClassToRefine) do
+ def some_inherited_method; end
+ def some_included_method; end
+ def some_method; end
+ end
+ end
+
+ obj = SomeChildClassToRefine.new
+
+ assert_raise_with_message(NoMethodError, /private/) do
+ obj.some_inherited_method
+ end
+
+ assert_raise_with_message(NoMethodError, /private/) do
+ obj.some_included_method
+ end
+
+ assert_raise_with_message(NoMethodError, /private/) do
+ obj.some_method
+ end
+ end
+
+ def test_refined_method_alias_warning
+ c = Class.new do
+ def t; :t end
+ def f; :f end
+ end
+ Module.new do
+ refine(c) do
+ alias foo t
+ end
+ end
+ assert_warning('', '[ruby-core:82385] [Bug #13817] refined method is not redefined') do
+ c.class_eval do
+ alias foo f
+ end
+ end
+ end
+
+ def test_using_wrong_argument
+ bug = '[ruby-dev:50270] [Bug #13956]'
+ pattern = /expected Module/
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ bug = ""#{bug.dump}
+ pattern = /#{pattern}/
+ begin;
+ assert_raise_with_message(TypeError, pattern, bug) {
+ using(1) do end
+ }
+ end;
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ bug = ""#{bug.dump}
+ pattern = /#{pattern}/
+ begin;
+ assert_raise_with_message(TypeError, pattern, bug) {
+ Module.new {using(1) {}}
+ }
+ end;
+ end
+
+ class ToString
+ c = self
+ using Module.new {refine(c) {def to_s; "ok"; end}}
+ def string
+ "#{self}"
+ end
+ end
+
+ def test_tostring
+ assert_equal("ok", ToString.new.string)
+ assert_predicate(ToString.new.taint.string, :tainted?)
+ end
+
+ class ToSymbol
+ c = self
+ using Module.new {refine(c) {def intern; "<#{upcase}>"; end}}
+ def symbol
+ :"#{@string}"
+ end
+ def initialize(string)
+ @string = string
+ end
+ end
+
+ def test_dsym_literal
+ assert_equal(:foo, ToSymbol.new("foo").symbol)
+ end
+
+ module ToProc
+ def self.call &block
+ block.call
+ end
+
+ class ReturnProc
+ c = self
+ using Module.new {
+ refine c do
+ def to_proc
+ proc { "to_proc" }
+ end
+ end
+ }
+ def call
+ ToProc.call &self
+ end
+ end
+
+ class ReturnNoProc
+ c = self
+ using Module.new {
+ refine c do
+ def to_proc
+ true
+ end
+ end
+ }
+
+ def call
+ ToProc.call &self
+ end
+ end
+
+ class PrivateToProc
+ c = self
+ using Module.new {
+ refine c do
+ private
+ def to_proc
+ proc { "private_to_proc" }
+ end
+ end
+ }
+
+ def call
+ ToProc.call &self
+ end
+ end
+
+
+ class NonProc
+ def call
+ ToProc.call &self
+ end
+ end
+
+ class MethodMissing
+ def method_missing *args
+ proc { "method_missing" }
+ end
+
+ def call
+ ToProc.call &self
+ end
+ end
+
+ class ToProcAndMethodMissing
+ def method_missing *args
+ proc { "method_missing" }
+ end
+
+ c = self
+ using Module.new {
+ refine c do
+ def to_proc
+ proc { "to_proc" }
+ end
+ end
+ }
+
+ def call
+ ToProc.call &self
+ end
+ end
+
+ class ToProcAndRefinements
+ def to_proc
+ proc { "to_proc" }
+ end
+
+ c = self
+ using Module.new {
+ refine c do
+ def to_proc
+ proc { "refinements_to_proc" }
+ end
+ end
+ }
+
+ def call
+ ToProc.call &self
+ end
+ end
+ end
+
+ def test_to_proc
+ assert_equal("to_proc", ToProc::ReturnProc.new.call)
+ assert_equal("private_to_proc", ToProc::PrivateToProc.new.call)
+ assert_raise(TypeError){ ToProc::ReturnNoProc.new.call }
+ assert_raise(TypeError){ ToProc::NonProc.new.call }
+ assert_equal("method_missing", ToProc::MethodMissing.new.call)
+ assert_equal("to_proc", ToProc::ToProcAndMethodMissing.new.call)
+ assert_equal("refinements_to_proc", ToProc::ToProcAndRefinements.new.call)
+ end
+
+ def test_unused_refinement_for_module
+ bug14068 = '[ruby-core:83613] [Bug #14068]'
+ assert_in_out_err([], <<-INPUT, ["M1#foo"], [], bug14068)
+ module M1
+ def foo
+ puts "M1#foo"
+ end
+ end
+
+ module M2
+ end
+
+ module UnusedRefinement
+ refine(M2) do
+ def foo
+ puts "M2#foo"
+ end
+ end
+ end
+
+ include M1
+ include M2
+ foo()
+ INPUT
+ end
+
+ def test_refining_module_repeatedly
+ bug14070 = '[ruby-core:83617] [Bug #14070]'
+ assert_in_out_err([], <<-INPUT, ["ok"], [], bug14070)
+ 1000.times do
+ Class.new do
+ include Enumerable
+ end
+
+ Module.new do
+ refine Enumerable do
+ def foo
+ end
+ end
+ end
+ end
+ puts "ok"
+ INPUT
+ end
+
+ def test_call_method_in_unused_refinement
+ bug15720 = '[ruby-core:91916] [Bug #15720]'
+ assert_in_out_err([], <<-INPUT, ["ok"], [], bug15720)
+ module M1
+ refine Kernel do
+ def foo
+ 'foo called!'
+ end
+ end
+ end
+
+ module M2
+ refine Kernel do
+ def bar
+ 'bar called!'
+ end
+ end
+ end
+
+ using M1
+
+ foo
+
+ begin
+ bar
+ rescue NameError
+ end
+
+ puts "ok"
+ INPUT
+ end
+
+ def test_super_from_refined_module
+ a = EnvUtil.labeled_module("A") do
+ def foo;"[A#{super}]";end
+ end
+ b = EnvUtil.labeled_class("B") do
+ def foo;"[B]";end
+ end
+ c = EnvUtil.labeled_class("C", b) do
+ include a
+ def foo;"[C#{super}]";end
+ end
+ d = EnvUtil.labeled_module("D") do
+ refine(a) do
+ def foo;end
+ end
+ end
+ assert_equal("[C[A[B]]]", c.new.foo, '[ruby-dev:50390] [Bug #14232]')
+ end
+
private
def eval_using(mod, s)
diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb
index 4853c41b86..038c1c830f 100644
--- a/test/ruby/test_regexp.rb
+++ b/test/ruby/test_regexp.rb
@@ -74,6 +74,12 @@ class TestRegexp < Test::Unit::TestCase
end
end
+ def test_to_s_extended_subexp
+ re = /#\g#{"\n"}/x
+ re = /#{re}/
+ assert_warn('', '[ruby-core:82328] [Bug #13798]') {re.to_s}
+ end
+
def test_union
assert_equal :ok, begin
Regexp.union(
@@ -84,6 +90,11 @@ class TestRegexp < Test::Unit::TestCase
rescue ArgumentError
:ok
end
+ re = Regexp.union(/\//, "")
+ re2 = eval(re.inspect)
+ assert_equal(re.to_s, re2.to_s)
+ assert_equal(re.source, re2.source)
+ assert_equal(re, re2)
end
def test_word_boundary
@@ -150,6 +161,10 @@ class TestRegexp < Test::Unit::TestCase
s = "foo"
s[/(?<bar>o)/, "bar"] = "baz"
assert_equal("fbazo", s)
+
+ /.*/ =~ "abc"
+ "a".sub("a", "")
+ assert_raise(IndexError) {Regexp.last_match(:_id)}
end
def test_named_capture_with_nul
@@ -203,6 +218,32 @@ class TestRegexp < Test::Unit::TestCase
def test_assign_named_capture_to_reserved_word
/(?<nil>.)/ =~ "a"
assert_not_include(local_variables, :nil, "[ruby-dev:32675]")
+
+ def (obj = Object.new).test(s, nil: :ng)
+ /(?<nil>.)/ =~ s
+ binding.local_variable_get(:nil)
+ end
+ assert_equal("b", obj.test("b"))
+
+ tap do |nil: :ng|
+ /(?<nil>.)/ =~ "c"
+ assert_equal("c", binding.local_variable_get(:nil))
+ end
+ end
+
+ def test_assign_named_capture_to_const
+ %W[C \u{1d402}].each do |name|
+ assert_equal(:ok, Class.new.class_eval("#{name} = :ok; /(?<#{name}>.*)/ =~ 'ng'; #{name}"))
+ end
+ end
+
+ def test_assign_named_capture_trace
+ bug = '[ruby-core:79940] [Bug #13287]'
+ assert_normal_exit("#{<<-"begin;"}\n#{<<-"end;"}", bug)
+ begin;
+ / (?<foo>.*)/ =~ "bar" &&
+ true
+ end;
end
def test_match_regexp
@@ -500,6 +541,8 @@ class TestRegexp < Test::Unit::TestCase
s = ".........."
5.times { s.sub!(".", "") }
assert_equal(".....", s)
+
+ assert_equal("\\\u{3042}", Regexp.new("\\\u{3042}").source)
end
def test_equal
@@ -543,7 +586,8 @@ class TestRegexp < Test::Unit::TestCase
assert_equal(true, /../.match?('abc', -2))
assert_equal(false, /../.match?("abc", -4))
assert_equal(false, /../.match?("abc", 4))
- assert_equal(true, /../n.match?("\u3042" + '\x', 1))
+ assert_equal(true, /../.match?("\u3042xx", 1))
+ assert_equal(false, /../.match?("\u3042x", 1))
assert_equal(true, /\z/.match?(""))
assert_equal(true, /\z/.match?("abc"))
assert_equal(true, /R.../.match?("Ruby"))
@@ -566,6 +610,10 @@ class TestRegexp < Test::Unit::TestCase
assert_equal("\\v", Regexp.quote("\v"))
assert_equal("\u3042\\t", Regexp.quote("\u3042\t"))
assert_equal("\\t\xff", Regexp.quote("\t" + [0xff].pack("C")))
+
+ bug13034 = '[ruby-core:78646] [Bug #13034]'
+ str = "\x00".force_encoding("UTF-16BE")
+ assert_equal(str, Regexp.quote(str), bug13034)
end
def test_try_convert
@@ -639,14 +687,30 @@ class TestRegexp < Test::Unit::TestCase
end
def test_match_without_regexp
+ # create a MatchData for each assertion because the internal state may change
+ test = proc {|&blk| "abc".sub("a", ""); blk.call($~) }
+
bug10877 = '[ruby-core:68209] [Bug #10877]'
- "abc".sub("a", "")
- assert_raise_with_message(IndexError, /foo/, bug10877) {$~["foo"]}
+ test.call {|m| assert_raise_with_message(IndexError, /foo/, bug10877) {m["foo"]} }
key = "\u{3042}"
[Encoding::UTF_8, Encoding::Shift_JIS, Encoding::EUC_JP].each do |enc|
idx = key.encode(enc)
- assert_raise_with_message(IndexError, /#{idx}/, bug10877) {$~[idx]}
+ test.call {|m| assert_raise_with_message(IndexError, /#{idx}/, bug10877) {m[idx]} }
end
+ test.call {|m| assert_equal(/a/, m.regexp) }
+ test.call {|m| assert_equal("abc", m.string) }
+ test.call {|m| assert_equal(1, m.size) }
+ test.call {|m| assert_equal(0, m.begin(0)) }
+ test.call {|m| assert_equal(1, m.end(0)) }
+ test.call {|m| assert_equal([0, 1], m.offset(0)) }
+ test.call {|m| assert_equal([], m.captures) }
+ test.call {|m| assert_equal([], m.names) }
+ test.call {|m| assert_equal({}, m.named_captures) }
+ test.call {|m| assert_equal(/a/.match("abc"), m) }
+ test.call {|m| assert_equal(/a/.match("abc").hash, m.hash) }
+ test.call {|m| assert_equal("bc", m.post_match) }
+ test.call {|m| assert_equal("", m.pre_match) }
+ test.call {|m| assert_equal(["a", nil], m.values_at(0, 1)) }
end
def test_last_match
@@ -900,6 +964,29 @@ class TestRegexp < Test::Unit::TestCase
assert_no_match(/[[:ascii:]]/, "\x80\xFF")
end
+ def test_cclass_R
+ assert_match /\A\R\z/, "\r"
+ assert_match /\A\R\z/, "\n"
+ assert_match /\A\R\z/, "\r\n"
+ end
+
+ def test_cclass_X
+ assert_match /\A\X\z/, "\u{20 200d}"
+ assert_match /\A\X\z/, "\u{600 600}"
+ assert_match /\A\X\z/, "\u{600 20}"
+ assert_match /\A\X\z/, "\u{261d 1F3FB}"
+ assert_match /\A\X\z/, "\u{1f600}"
+ assert_match /\A\X\z/, "\u{20 324}"
+ assert_match /\A\X\X\z/, "\u{a 324}"
+ assert_match /\A\X\X\z/, "\u{d 324}"
+ assert_match /\A\X\z/, "\u{1F477 1F3FF 200D 2640 FE0F}"
+ assert_match /\A\X\z/, "\u{1F468 200D 1F393}"
+ assert_match /\A\X\z/, "\u{1F46F 200D 2642 FE0F}"
+ assert_match /\A\X\z/, "\u{1f469 200d 2764 fe0f 200d 1f469}"
+
+ assert_warning('') {/\X/ =~ "\u{a0}"}
+ end
+
def test_backward
assert_equal(3, "foobar".rindex(/b.r/i))
assert_equal(nil, "foovar".rindex(/b.r/i))
@@ -925,6 +1012,7 @@ class TestRegexp < Test::Unit::TestCase
assert_raise(TypeError) { Regexp.allocate.names }
assert_raise(TypeError) { Regexp.allocate.named_captures }
+ assert_raise(TypeError) { MatchData.allocate.hash }
assert_raise(TypeError) { MatchData.allocate.regexp }
assert_raise(TypeError) { MatchData.allocate.names }
assert_raise(TypeError) { MatchData.allocate.size }
@@ -1002,6 +1090,9 @@ class TestRegexp < Test::Unit::TestCase
assert_no_match(/^\p{age=3.0}$/u, "\u2754")
assert_no_match(/^\p{age=2.0}$/u, "\u2754")
assert_no_match(/^\p{age=1.1}$/u, "\u2754")
+
+ assert_no_match(/^\p{age=12.0}$/u, "\u32FF")
+ assert_match(/^\p{age=12.1}$/u, "\u32FF")
end
MatchData_A = eval("class MatchData_\u{3042} < MatchData; self; end")
@@ -1019,7 +1110,7 @@ class TestRegexp < Test::Unit::TestCase
assert_equal("hoge fuga", h["body"])
end
- def test_regexp_poped
+ def test_regexp_popped
assert_nothing_raised { eval("a = 1; /\#{ a }/; a") }
assert_nothing_raised { eval("a = 1; /\#{ a }/o; a") }
end
@@ -1155,6 +1246,36 @@ class TestRegexp < Test::Unit::TestCase
RUBY
end
+ def test_invalid_free_at_parse_depth_limit_over
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ begin
+ require '-test-/regexp'
+ rescue LoadError
+ else
+ bug = '[ruby-core:79624] [Bug #13234]'
+ Bug::Regexp.parse_depth_limit = 10
+ src = "[" * 100
+ 3.times do
+ assert_raise_with_message(RegexpError, /parse depth limit over/, bug) do
+ Regexp.new(src)
+ end
+ end
+ end
+ end;
+ end
+
+ def test_absent
+ assert_equal(0, /(?~(a|c)c)/ =~ "abb")
+ assert_equal("abb", $&)
+
+ assert_equal(0, /\/\*((?~\*\/))\*\// =~ "/*abc*def/xyz*/ /* */")
+ assert_equal("abc*def/xyz", $1)
+
+ assert_equal(0, /(?~(a)c)/ =~ "abb")
+ assert_nil($1)
+ end
+
# This assertion is for porting x2() tests in testpy.py of Onigmo.
def assert_match_at(re, str, positions, msg = nil)
re = Regexp.new(re) unless re.is_a?(Regexp)
diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb
index 2e33499b41..af8e6e30fa 100644
--- a/test/ruby/test_require.rb
+++ b/test/ruby/test_require.rb
@@ -18,22 +18,24 @@ class TestRequire < Test::Unit::TestCase
t.puts "dummy"
t.close
- assert_separately([], <<-INPUT)
+ assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}")
+ begin;
$:.replace([IO::NULL])
assert_raise(LoadError) do
require \"#{ t.path }\"
end
- INPUT
+ end;
}
end
def test_require_too_long_filename
- assert_separately(["RUBYOPT"=>nil], <<-INPUT)
+ assert_separately(["RUBYOPT"=>nil], "#{<<~"begin;"}\n#{<<~"end;"}")
+ begin;
$:.replace([IO::NULL])
assert_raise(LoadError) do
require '#{ "foo/" * 10000 }foo'
end
- INPUT
+ end;
begin
assert_in_out_err(["-S", "-w", "foo/" * 1024 + "foo"], "") do |r, e|
@@ -60,6 +62,12 @@ class TestRequire < Test::Unit::TestCase
assert_require_nonascii_path(encoding, bug8165)
end
+ def test_require_insecure_path
+ assert_require_insecure_path("foo")
+ encoding = 'filesystem'
+ assert_require_insecure_path(nil, encoding)
+ end
+
def test_require_nonascii_path_utf8
bug8676 = '[ruby-core:56136] [Bug #8676]'
encoding = Encoding::UTF_8
@@ -67,6 +75,12 @@ class TestRequire < Test::Unit::TestCase
assert_require_nonascii_path(encoding, bug8676)
end
+ def test_require_insecure_path_utf8
+ encoding = Encoding::UTF_8
+ return if Encoding.find('filesystem') == encoding
+ assert_require_insecure_path(nil, encoding)
+ end
+
def test_require_nonascii_path_shift_jis
bug8676 = '[ruby-core:56136] [Bug #8676]'
encoding = Encoding::Shift_JIS
@@ -74,6 +88,12 @@ class TestRequire < Test::Unit::TestCase
assert_require_nonascii_path(encoding, bug8676)
end
+ def test_require_insecure_path_shift_jis
+ encoding = Encoding::Shift_JIS
+ return if Encoding.find('filesystem') == encoding
+ assert_require_insecure_path(nil, encoding)
+ end
+
case RUBY_PLATFORM
when /cygwin/, /mswin/, /mingw/, /darwin/
def self.ospath_encoding(path)
@@ -85,9 +105,20 @@ class TestRequire < Test::Unit::TestCase
end
end
- def assert_require_nonascii_path(encoding, bug)
+ SECURITY_WARNING =
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ nil
+ else
+ proc do |require_path|
+ $SAFE = 1
+ require(require_path)
+ ensure
+ $SAFE = 0
+ end
+ end
+
+ def prepare_require_path(dir, encoding)
Dir.mktmpdir {|tmp|
- dir = "\u3042" * 5
begin
require_path = File.join(tmp, dir, 'foo.rb').encode(encoding)
rescue
@@ -98,6 +129,17 @@ class TestRequire < Test::Unit::TestCase
begin
load_path = $:.dup
features = $".dup
+ yield require_path
+ ensure
+ $:.replace(load_path)
+ $".replace(features)
+ end
+ }
+ end
+
+ def assert_require_nonascii_path(encoding, bug)
+ prepare_require_path("\u3042" * 5, encoding) {|require_path|
+ begin
# leave paths for require encoding objects
bug = "#{bug} require #{encoding} path"
require_path = "#{require_path}"
@@ -107,13 +149,35 @@ class TestRequire < Test::Unit::TestCase
assert_equal(self.class.ospath_encoding(require_path), $:.last.encoding, '[Bug #8753]')
assert(!require(require_path), bug)
}
- ensure
- $:.replace(load_path)
- $".replace(features)
end
}
end
+ def assert_require_insecure_path(dirname, encoding = nil)
+ return unless SECURITY_WARNING
+ dirname ||= "\u3042" * 5
+ encoding ||= dirname.encoding
+ prepare_require_path(dirname, encoding) {|require_path|
+ require_path.untaint
+ require(require_path)
+ $".pop
+ File.chmod(0777, File.dirname(require_path))
+ ospath = (require_path.encode('filesystem') rescue
+ require_path.encode(self.class.ospath_encoding(require_path)))
+ e = nil
+ stderr = EnvUtil.verbose_warning do
+ e = assert_raise(SecurityError) do
+ SECURITY_WARNING.call(require_path)
+ end
+ end
+ assert_include(e.message, "loading from unsafe path")
+ assert_include(stderr, "Insecure world writable dir")
+ require_path = require_path.encode(self.class.ospath_encoding(require_path))
+ assert_include(e.message, require_path)
+ assert_include(stderr, File.dirname(require_path))
+ }
+ end
+
def test_require_path_home_1
env_rubypath, env_home = ENV["RUBYPATH"], ENV["HOME"]
pathname_too_long = /pathname too long \(ignored\).*\(LoadError\)/m
@@ -160,13 +224,20 @@ class TestRequire < Test::Unit::TestCase
end
def test_require_with_unc
- ruby = File.expand_path(EnvUtil.rubybin).sub(/\A(\w):/, '//127.0.0.1/\1$/')
- skip "local drive #$1: is not shared" unless File.exist?(ruby)
- pid = nil
- assert_nothing_raised {pid = spawn(ruby, "-rabbrev", "-e0")}
- ret, status = Process.wait2(pid)
- assert_equal(pid, ret)
- assert_predicate(status, :success?)
+ Tempfile.create(["test_ruby_test_require", ".rb"]) {|t|
+ t.puts "puts __FILE__"
+ t.close
+
+ path = File.expand_path(t.path).sub(/\A(\w):/, '//127.0.0.1/\1$')
+ skip "local drive #$1: is not shared" unless File.exist?(path)
+ args = ['--disable-gems', "-I#{File.dirname(path)}"]
+ assert_in_out_err(args, "#{<<~"END;"}", [path], [])
+ begin
+ require '#{File.basename(path)}'
+ rescue Errno::EPERM
+ end
+ END;
+ }
end if /mswin|mingw/ =~ RUBY_PLATFORM
def test_require_twice
@@ -376,13 +447,6 @@ class TestRequire < Test::Unit::TestCase
assert_separately([], <<-INPUT)
abs_dir = "#{ abs_dir }"
- $: << abs_dir.taint
- $SAFE = 1
- assert_raise(SecurityError) {require "#{ file }"}
- INPUT
-
- assert_separately([], <<-INPUT)
- abs_dir = "#{ abs_dir }"
$: << abs_dir << 'elsewhere'.taint
assert_nothing_raised {require "#{ file }"}
INPUT
@@ -445,7 +509,8 @@ class TestRequire < Test::Unit::TestCase
verbose = $VERBOSE
Tempfile.create(%w"bug5754 .rb") {|tmp|
path = tmp.path
- tmp.print %{\
+ tmp.print "#{<<~"begin;"}\n#{<<~"end;"}"
+ begin;
th = Thread.current
t = th[:t]
scratch = th[:scratch]
@@ -457,7 +522,7 @@ class TestRequire < Test::Unit::TestCase
else
scratch << :post
end
- }
+ end;
tmp.close
class << (output = "")
@@ -537,7 +602,8 @@ class TestRequire < Test::Unit::TestCase
open(File.join("b", "bar.rb"), "w") {|f|
f.puts "p :ok"
}
- assert_in_out_err([], <<-INPUT, %w(:ok), [], bug7158)
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
+ begin;
$:.replace([IO::NULL])
$: << "."
Dir.chdir("a")
@@ -546,7 +612,7 @@ class TestRequire < Test::Unit::TestCase
p :ng unless require "bar"
Dir.chdir("..")
p :ng if require "b/bar"
- INPUT
+ end;
}
}
end
@@ -556,7 +622,8 @@ class TestRequire < Test::Unit::TestCase
Dir.mktmpdir {|tmp|
Dir.chdir(tmp) {
open("foo.rb", "w") {}
- assert_in_out_err([], <<-INPUT, %w(:ok), [], bug7158)
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
+ begin;
$:.replace([IO::NULL])
a = Object.new
def a.to_str
@@ -566,7 +633,7 @@ class TestRequire < Test::Unit::TestCase
require "foo"
last_path = $:.pop
p :ok if last_path == a && last_path.class == Object
- INPUT
+ end;
}
}
end
@@ -578,14 +645,15 @@ class TestRequire < Test::Unit::TestCase
open("foo.rb", "w") {}
Dir.mkdir("a")
open(File.join("a", "bar.rb"), "w") {}
- assert_in_out_err([], <<-INPUT, %w(:ok), [], bug7158)
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
+ begin;
$:.replace([IO::NULL])
$: << '~'
ENV['HOME'] = "#{tmp}"
require "foo"
ENV['HOME'] = "#{tmp}/a"
p :ok if require "bar"
- INPUT
+ end;
}
}
end
@@ -595,7 +663,8 @@ class TestRequire < Test::Unit::TestCase
Dir.mktmpdir {|tmp|
Dir.chdir(tmp) {
open("foo.rb", "w") {}
- assert_in_out_err([{"RUBYOPT"=>nil}, '--disable-gems'], <<-INPUT, %w(:ok), [], bug7158)
+ assert_in_out_err([{"RUBYOPT"=>nil}, '--disable-gems'], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
+ begin;
$:.replace([IO::NULL])
a = Object.new
def a.to_path
@@ -612,7 +681,7 @@ class TestRequire < Test::Unit::TestCase
"#{tmp}"
end
p :ok if require "foo"
- INPUT
+ end;
}
}
end
@@ -622,7 +691,8 @@ class TestRequire < Test::Unit::TestCase
Dir.mktmpdir {|tmp|
Dir.chdir(tmp) {
open("foo.rb", "w") {}
- assert_in_out_err([{"RUBYOPT"=>nil}, '--disable-gems'], <<-INPUT, %w(:ok), [], bug7158)
+ assert_in_out_err([{"RUBYOPT"=>nil}, '--disable-gems'], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
+ begin;
$:.replace([IO::NULL])
a = Object.new
def a.to_str
@@ -639,7 +709,7 @@ class TestRequire < Test::Unit::TestCase
"#{tmp}"
end
p :ok if require "foo"
- INPUT
+ end;
}
}
end
@@ -651,7 +721,8 @@ class TestRequire < Test::Unit::TestCase
open("foo.rb", "w") {}
Dir.mkdir("a")
open(File.join("a", "bar.rb"), "w") {}
- assert_in_out_err(['--disable-gems'], <<-INPUT, %w(:ok), [], bug7383)
+ assert_in_out_err(['--disable-gems'], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7383)
+ begin;
$:.replace([IO::NULL])
$:.#{add} "#{tmp}"
$:.#{add} "#{tmp}/a"
@@ -667,7 +738,7 @@ class TestRequire < Test::Unit::TestCase
raise
end
end
- INPUT
+ end;
}
}
end
@@ -685,10 +756,11 @@ class TestRequire < Test::Unit::TestCase
Dir.mktmpdir {|tmp|
Dir.chdir(tmp) {
open("bar.rb", "w") {|f| f.puts 'TOPLEVEL_BINDING.eval("lib = 2")' }
- assert_in_out_err(%w[-r./bar.rb], <<-INPUT, %w([:lib] 2), [], bug7536)
+ assert_in_out_err(%w[-r./bar.rb], "#{<<~"begin;"}\n#{<<~"end;"}", %w([:lib] 2), [], bug7536)
+ begin;
puts TOPLEVEL_BINDING.eval("local_variables").inspect
puts TOPLEVEL_BINDING.eval("lib").inspect
- INPUT
+ end;
}
}
end
@@ -697,9 +769,10 @@ class TestRequire < Test::Unit::TestCase
bug7530 = '[ruby-core:50645]'
Tempfile.create(%w'bug-7530- .rb') {|script|
script.close
- assert_in_out_err([{"RUBYOPT" => nil}, "-", script.path], <<-INPUT, %w(:ok), [], bug7530)
+ assert_in_out_err([{"RUBYOPT" => nil}, "-", script.path], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7530, timeout: 60)
+ begin;
PATH = ARGV.shift
- THREADS = 2
+ THREADS = 4
ITERATIONS_PER_THREAD = 1000
THREADS.times.map {
@@ -711,7 +784,7 @@ class TestRequire < Test::Unit::TestCase
end
}.each(&:join)
p :ok
- INPUT
+ end;
}
end
@@ -720,7 +793,7 @@ class TestRequire < Test::Unit::TestCase
f.close
File.unlink(f.path)
File.mkfifo(f.path)
- assert_separately(["-", f.path], "#{<<-"begin;"}\n#{<<-"end;"}", timeout: 3)
+ assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 3)
begin;
th = Thread.current
Thread.start {begin sleep(0.001) end until th.stop?; th.raise(IOError)}
@@ -737,7 +810,7 @@ class TestRequire < Test::Unit::TestCase
File.unlink(f.path)
File.mkfifo(f.path)
- assert_separately(["-", f.path], "#{<<-"begin;"}\n#{<<-"end;"}", timeout: 3)
+ assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 3)
begin;
path = ARGV[0]
th = Thread.current
@@ -762,7 +835,7 @@ class TestRequire < Test::Unit::TestCase
f.close
File.unlink(f.path)
File.mkfifo(f.path)
- assert_separately(["-", f.path], "#{<<-"begin;"}\n#{<<-"end;"}", timeout: 3)
+ assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 3)
begin;
Process.setrlimit(Process::RLIMIT_NOFILE, 50)
th = Thread.current
@@ -786,7 +859,8 @@ class TestRequire < Test::Unit::TestCase
f.puts 'sleep'
f.close
- assert_separately(["-", f.path], <<-'end;')
+ assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
path = ARGV[0]
class Error < RuntimeError
def exception(*)
@@ -809,4 +883,18 @@ class TestRequire < Test::Unit::TestCase
end;
end
end
+
+ def test_symlink_load_path
+ Dir.mktmpdir {|tmp|
+ Dir.mkdir(File.join(tmp, "real"))
+ begin
+ File.symlink "real", File.join(tmp, "symlink")
+ rescue NotImplementedError, Errno::EACCES
+ skip "File.symlink is not implemented"
+ end
+ File.write(File.join(tmp, "real/a.rb"), "print __FILE__")
+ result = IO.popen([EnvUtil.rubybin, "-I#{tmp}/symlink", "-e", "require 'a.rb'"], &:read)
+ assert_operator(result, :end_with?, "/real/a.rb")
+ }
+ end
end
diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb
index 2e5f76d692..69521b1d23 100644
--- a/test/ruby/test_rubyoptions.rb
+++ b/test/ruby/test_rubyoptions.rb
@@ -1,10 +1,19 @@
# -*- coding: us-ascii -*-
require 'test/unit'
+require 'timeout'
require 'tmpdir'
require 'tempfile'
+require_relative '../lib/jit_support'
class TestRubyOptions < Test::Unit::TestCase
+ NO_JIT_DESCRIPTION =
+ if RubyVM::MJIT.enabled? # checking -DMJIT_FORCE_ENABLE
+ RUBY_DESCRIPTION.sub(/\+JIT /, '')
+ else
+ RUBY_DESCRIPTION
+ end
+
def write_file(filename, content)
File.open(filename, "w") {|f|
f << content
@@ -26,7 +35,7 @@ class TestRubyOptions < Test::Unit::TestCase
def test_usage
assert_in_out_err(%w(-h)) do |r, e|
- assert_operator(r.size, :<=, 24)
+ assert_operator(r.size, :<=, 25)
longer = r[1..-1].select {|x| x.size > 80}
assert_equal([], longer)
assert_equal([], e)
@@ -48,7 +57,7 @@ class TestRubyOptions < Test::Unit::TestCase
end
assert_in_out_err(%w(-p -l -a -e) + ['p [$-p, $-l, $-a]'],
- "foo\nbar\nbaz\n") do |r, e|
+ "foo\nbar\nbaz") do |r, e|
assert_equal(
[ '[true, true, true]', 'foo',
'[true, true, true]', 'bar',
@@ -57,6 +66,7 @@ class TestRubyOptions < Test::Unit::TestCase
end
end
+
def test_warning
save_rubyopt = ENV['RUBYOPT']
ENV['RUBYOPT'] = nil
@@ -95,10 +105,23 @@ class TestRubyOptions < Test::Unit::TestCase
end
private_constant :VERSION_PATTERN
+ VERSION_PATTERN_WITH_JIT =
+ case RUBY_ENGINE
+ when 'ruby'
+ /^ruby #{q[RUBY_VERSION]}(?:[p ]|dev|rc).*? \+JIT \[#{q[RUBY_PLATFORM]}\]$/
+ else
+ VERSION_PATTERN
+ end
+ private_constant :VERSION_PATTERN_WITH_JIT
+
def test_verbose
assert_in_out_err(["-vve", ""]) do |r, e|
assert_match(VERSION_PATTERN, r[0])
- assert_equal(RUBY_DESCRIPTION, r[0])
+ if RubyVM::MJIT.enabled? && !mjit_force_enabled? # checking -DMJIT_FORCE_ENABLE
+ assert_equal(NO_JIT_DESCRIPTION, r[0])
+ else
+ assert_equal(RUBY_DESCRIPTION, r[0])
+ end
assert_equal([], e)
end
@@ -115,9 +138,11 @@ class TestRubyOptions < Test::Unit::TestCase
end
def test_enable
- assert_in_out_err(%w(--enable all -e) + [""], "", [], [])
- assert_in_out_err(%w(--enable-all -e) + [""], "", [], [])
- assert_in_out_err(%w(--enable=all -e) + [""], "", [], [])
+ if JITSupport.supported?
+ assert_in_out_err(%w(--enable all -e) + [""], "", [], [])
+ assert_in_out_err(%w(--enable-all -e) + [""], "", [], [])
+ assert_in_out_err(%w(--enable=all -e) + [""], "", [], [])
+ end
assert_in_out_err(%w(--enable foobarbazqux -e) + [""], "", [],
/unknown argument for --enable: `foobarbazqux'/)
assert_in_out_err(%w(--enable), "", [], /missing argument for --enable/)
@@ -132,6 +157,7 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err(%w(--disable), "", [], /missing argument for --disable/)
assert_in_out_err(%w(--disable-gems -e) + ['p defined? Gem'], "", ["nil"], [])
assert_in_out_err(%w(--disable-did_you_mean -e) + ['p defined? DidYouMean'], "", ["nil"], [])
+ assert_in_out_err(%w(--disable-gems -e) + ['p defined? DidYouMean'], "", ["nil"], [])
end
def test_kanji
@@ -154,9 +180,45 @@ class TestRubyOptions < Test::Unit::TestCase
def test_version
assert_in_out_err(%w(--version)) do |r, e|
assert_match(VERSION_PATTERN, r[0])
- assert_equal(RUBY_DESCRIPTION, r[0])
+ if RubyVM::MJIT.enabled? # checking -DMJIT_FORCE_ENABLE
+ assert_equal(EnvUtil.invoke_ruby(['-e', 'print RUBY_DESCRIPTION'], '', true).first, r[0])
+ else
+ assert_equal(RUBY_DESCRIPTION, r[0])
+ end
assert_equal([], e)
end
+
+ return if RbConfig::CONFIG["MJIT_SUPPORT"] == 'no'
+
+ [
+ %w(--version --jit --disable=jit),
+ %w(--version --enable=jit --disable=jit),
+ %w(--version --enable-jit --disable-jit),
+ ].each do |args|
+ assert_in_out_err(args) do |r, e|
+ assert_match(VERSION_PATTERN, r[0])
+ assert_match(NO_JIT_DESCRIPTION, r[0])
+ assert_equal([], e)
+ end
+ end
+
+ if JITSupport.supported?
+ [
+ %w(--version --jit),
+ %w(--version --enable=jit),
+ %w(--version --enable-jit),
+ ].each do |args|
+ assert_in_out_err(args) do |r, e|
+ assert_match(VERSION_PATTERN_WITH_JIT, r[0])
+ if RubyVM::MJIT.enabled? # checking -DMJIT_FORCE_ENABLE
+ assert_equal(RUBY_DESCRIPTION, r[0])
+ else
+ assert_equal(EnvUtil.invoke_ruby(['--jit', '-e', 'print RUBY_DESCRIPTION'], '', true).first, r[0])
+ end
+ assert_equal([], e)
+ end
+ end
+ end
end
def test_eval
@@ -184,6 +246,10 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err(%w(-0141 -e) + ["print gets"], "foo\nbar\0baz", %w(foo ba), [])
assert_in_out_err(%w(-0e) + ["print gets"], "foo\nbar\0baz", %W(foo bar\0), [])
+
+ assert_in_out_err(%w(-00 -e) + ["p gets, gets"], "foo\nbar\n\nbaz\nzot\n\n\n", %w("foo\nbar\n\n" "baz\nzot\n\n"), [])
+
+ assert_in_out_err(%w(-00 -e) + ["p gets, gets"], "foo\nbar\n\n\n\nbaz\n", %w("foo\nbar\n\n" "baz\n"), [])
end
def test_autosplit
@@ -295,13 +361,15 @@ class TestRubyOptions < Test::Unit::TestCase
@verbose = $VERBOSE
$VERBOSE = nil
- ENV['PATH'] = File.dirname(t.path)
+ path, name = File.split(t.path)
- assert_in_out_err(%w(-S) + [File.basename(t.path)], "", %w(1), [])
-
- ENV['RUBYPATH'] = File.dirname(t.path)
+ ENV['PATH'] = (path_orig && RbConfig::CONFIG['LIBPATHENV'] == 'PATH') ?
+ [path, path_orig].join(File::PATH_SEPARATOR) : path
+ assert_in_out_err(%w(-S) + [name], "", %w(1), [])
+ ENV['PATH'] = path_orig
- assert_in_out_err(%w(-S) + [File.basename(t.path)], "", %w(1), [])
+ ENV['RUBYPATH'] = path
+ assert_in_out_err(%w(-S) + [name], "", %w(1), [])
}
ensure
@@ -325,7 +393,7 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err([], "#! /test_r_u_b_y_test_r_u_b_y_options_foobarbazqux -foo -bar\r\np 1\r\n",
[], /: no Ruby script found in input/)
- warning = /mswin|mingw/ =~ RUBY_PLATFORM ? [] : /shebang line ends with \\r/
+ warning = /mswin|mingw/ =~ RUBY_PLATFORM ? [] : /shebang line ending with \\r/
assert_in_out_err([{'RUBYOPT' => nil}], "#!ruby -KU -Eutf-8\r\np \"\u3042\"\r\n",
["\"\u3042\""], warning,
encoding: Encoding::UTF_8)
@@ -335,6 +403,26 @@ class TestRubyOptions < Test::Unit::TestCase
%w[4], [], bug4118)
assert_in_out_err(%w[-x], "#!/bin/sh\n""#!shebang\n""#!ruby\n""puts __LINE__\n",
%w[4], [], bug4118)
+
+ assert_ruby_status(%w[], "#! ruby -- /", '[ruby-core:82267] [Bug #13786]')
+
+ assert_ruby_status(%w[], "#!")
+ assert_in_out_err(%w[-c], "#!", ["Syntax OK"])
+ end
+
+ def test_flag_in_shebang
+ Tempfile.create(%w"pflag .rb") do |script|
+ code = "#!ruby -p"
+ script.puts(code)
+ script.close
+ assert_in_out_err([script.path, script.path], '', [code])
+ end
+ Tempfile.create(%w"sflag .rb") do |script|
+ script.puts("#!ruby -s")
+ script.puts("p $abc")
+ script.close
+ assert_in_out_err([script.path, "-abc=foo"], '', ['"foo"'])
+ end
end
def test_sflag
@@ -359,7 +447,7 @@ class TestRubyOptions < Test::Unit::TestCase
t.puts " end"
t.puts "end"
t.flush
- warning = ' warning: found = in conditional, should be =='
+ warning = ' warning: found `= literal\' in conditional, should be =='
err = ["#{t.path}:1:#{warning}",
"#{t.path}:4:#{warning}",
]
@@ -396,10 +484,18 @@ class TestRubyOptions < Test::Unit::TestCase
"begin", "if false", "for _ in []", "while false",
"def foo", "class X", "module M",
["-> do", "end"], ["-> {", "}"],
+ ["if false;", "else ; end"],
+ ["if false;", "elsif false ; end"],
+ ["begin", "rescue ; end"],
+ ["begin rescue", "else ; end"],
+ ["begin", "ensure ; end"],
+ [" case nil", "when true; end"],
+ ["case nil; when true", "end"],
].each do
|b, e = 'end'|
src = ["#{b}\n", " #{e}\n"]
- k = b[/\A\S+/]
+ k = b[/\A\s*(\S+)/, 1]
+ e = e[/\A\s*(\S+)/, 1]
a.for("no directives with #{b}") do
err = ["#{t.path}:2: warning: mismatched indentations at '#{e}' with '#{k}' at 1"]
@@ -441,6 +537,17 @@ class TestRubyOptions < Test::Unit::TestCase
t.flush
assert_in_out_err(["-w", t.path], "", [], [], '[ruby-core:25442]')
end
+
+ a.for("BOM with #{b}") do
+ err = ["#{t.path}:2: warning: mismatched indentations at '#{e}' with '#{k}' at 1"]
+ t.rewind
+ t.truncate(0)
+ t.print "\u{feff}"
+ t.puts src
+ t.flush
+ assert_in_out_err(["-w", t.path], "", [], err)
+ assert_in_out_err(["-wr", t.path, "-e", ""], "", [], err)
+ end
end
end
end
@@ -448,8 +555,9 @@ class TestRubyOptions < Test::Unit::TestCase
def test_notfound
notexist = "./notexist.rb"
- rubybin = EnvUtil.rubybin.dup
- rubybin.gsub!(%r(/), '\\') if /mswin|mingw/ =~ RUBY_PLATFORM
+ dir, *rubybin = RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME', 'EXEEXT')
+ rubybin = "#{dir}/#{rubybin.join('')}"
+ rubybin.tr!('/', '\\') if /mswin|mingw/ =~ RUBY_PLATFORM
rubybin = Regexp.quote(rubybin)
pat = Regexp.quote(notexist)
bug1573 = '[ruby-core:23717]'
@@ -503,7 +611,7 @@ class TestRubyOptions < Test::Unit::TestCase
if /linux|freebsd|netbsd|openbsd|darwin/ =~ RUBY_PLATFORM
PSCMD = EnvUtil.find_executable("ps", "-o", "command", "-p", $$.to_s) {|out| /ruby/=~out}
- PSCMD.pop if PSCMD
+ PSCMD&.pop
end
def test_set_program_name
@@ -514,33 +622,48 @@ class TestRubyOptions < Test::Unit::TestCase
pid = spawn(EnvUtil.rubybin, "test-script")
ps = nil
- 10.times do
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ stop = now + 30
+ begin
sleep 0.1
ps = `#{PSCMD.join(' ')} #{pid}`
break if /hello world/ =~ ps
- end
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ end until Process.wait(pid, Process::WNOHANG) || now > stop
assert_match(/hello world/, ps)
+ assert_operator now, :<, stop
Process.kill :KILL, pid
- Process.wait(pid)
+ Timeout.timeout(5) { Process.wait(pid) }
end
end
def test_setproctitle
skip "platform dependent feature" unless defined?(PSCMD) and PSCMD
+ assert_separately([], "#{<<-"{#"}\n#{<<-'};'}")
+ {#
+ assert_raise(ArgumentError) do
+ Process.setproctitle("hello\0")
+ end
+ };
+
with_tmpchdir do
write_file("test-script", "$_0 = $0.dup; Process.setproctitle('hello world'); $0 == $_0 or Process.setproctitle('$0 changed!'); sleep 60")
pid = spawn(EnvUtil.rubybin, "test-script")
ps = nil
- 10.times do
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ stop = now + 30
+ begin
sleep 0.1
ps = `#{PSCMD.join(' ')} #{pid}`
break if /hello world/ =~ ps
- end
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ end until Process.wait(pid, Process::WNOHANG) || now > stop
assert_match(/hello world/, ps)
+ assert_operator now, :<, stop
Process.kill :KILL, pid
- Process.wait(pid)
+ Timeout.timeout(5) { Process.wait(pid) }
end
end
@@ -559,7 +682,7 @@ class TestRubyOptions < Test::Unit::TestCase
-e:(?:1:)?\s\[BUG\]\sSegmentation\sfault.*\n
)x,
%r(
- #{ Regexp.quote(RUBY_DESCRIPTION) }\n\n
+ #{ Regexp.quote(NO_JIT_DESCRIPTION) }\n\n
)x,
%r(
(?:--\s(?:.+\n)*\n)?
@@ -585,8 +708,13 @@ class TestRubyOptions < Test::Unit::TestCase
You\smay\shave\sencountered\sa\sbug\sin\sthe\sRuby\sinterpreter\sor\sextension\slibraries.\n
Bug\sreports\sare\swelcome.\n
(?:.*\n)?
- For\sdetails:\shttp:\/\/.*\.ruby-lang\.org/.*\n
+ For\sdetails:\shttps:\/\/.*\.ruby-lang\.org/.*\n
\n
+ (?:
+ \[IMPORTANT\]\n
+ (?:.+\n)+
+ \n
+ )?
)x,
]
ExpectedStderrList << additional if additional
@@ -654,20 +782,6 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err(["-w", "-"], "eval('a=1')", [], [], feature7730)
end
- def test_shadowing_variable
- bug4130 = '[ruby-dev:42718]'
- assert_in_out_err(["-we", "def foo\n"" a=1\n"" 1.times do |a| end\n"" a\n""end"],
- "", [], ["-e:3: warning: shadowing outer local variable - a"], bug4130)
- assert_in_out_err(["-we", "def foo\n"" a=1\n"" 1.times do |a| end\n""end"],
- "", [],
- ["-e:3: warning: shadowing outer local variable - a",
- "-e:2: warning: assigned but unused variable - a",
- ], bug4130)
- feature6693 = '[ruby-core:46160]'
- assert_in_out_err(["-we", "def foo\n"" _a=1\n"" 1.times do |_a| end\n""end"],
- "", [], [], feature6693)
- end
-
def test_script_from_stdin
begin
require 'pty'
@@ -880,7 +994,7 @@ class TestRubyOptions < Test::Unit::TestCase
def test_frozen_string_literal_debug
with_debug_pat = /created at/
- wo_debug_pat = /can\'t modify frozen String \(RuntimeError\)\n\z/
+ wo_debug_pat = /can\'t modify frozen String \(FrozenError\)\n\z/
frozen = [
["--enable-frozen-string-literal", true],
["--disable-frozen-string-literal", false],
@@ -896,10 +1010,65 @@ class TestRubyOptions < Test::Unit::TestCase
frozen.product(debugs) do |(opt1, freeze), (opt2, debug)|
opt = opts + [opt1, opt2].compact
err = !freeze ? [] : debug ? with_debug_pat : wo_debug_pat
- assert_in_out_err(opt, '"foo" << "bar"', [], err)
- if freeze
- assert_in_out_err(opt, '"foo#{123}bar" << "bar"', [], err)
+ [
+ ['"foo" << "bar"', err],
+ ['"foo#{123}bar" << "bar"', err],
+ ['+"foo#{123}bar" << "bar"', []],
+ ['-"foo#{123}bar" << "bar"', freeze && debug ? with_debug_pat : wo_debug_pat],
+ ].each do |code, expected|
+ assert_in_out_err(opt, code, [], expected, [opt, code])
+ end
+ end
+ end
+
+ def test___dir__encoding
+ lang = {"LC_ALL"=>ENV["LC_ALL"]||ENV["LANG"]}
+ with_tmpchdir do
+ testdir = "\u30c6\u30b9\u30c8"
+ Dir.mkdir(testdir)
+ Dir.chdir(testdir) do
+ open("test.rb", "w") do |f|
+ f.puts <<-END
+ if __FILE__.encoding == __dir__.encoding
+ p true
+ else
+ puts "__FILE__: \#{__FILE__.encoding}, __dir__: \#{__dir__.encoding}"
+ end
+ END
+ end
+ r, = EnvUtil.invoke_ruby([lang, "test.rb"], "", true)
+ assert_equal "true", r.chomp, "the encoding of __FILE__ and __dir__ should be same"
end
end
end
+
+ def test_cwd_encoding
+ with_tmpchdir do
+ testdir = "\u30c6\u30b9\u30c8"
+ Dir.mkdir(testdir)
+ Dir.chdir(testdir) do
+ File.write("a.rb", "require './b'")
+ File.write("b.rb", "puts 'ok'")
+ assert_ruby_status([{"RUBYLIB"=>"."}, *%w[-E cp932:utf-8 a.rb]])
+ end
+ end
+ end
+
+ def test_null_script
+ skip "#{IO::NULL} is not a character device" unless File.chardev?(IO::NULL)
+ assert_in_out_err([IO::NULL], success: true)
+ end
+
+ def test_argv_tainted
+ assert_separately(%w[- arg], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_predicate(ARGV[0], :tainted?, '[ruby-dev:50596] [Bug #14941]')
+ end;
+ end
+
+ private
+
+ def mjit_force_enabled?
+ "#{RbConfig::CONFIG['CFLAGS']} #{RbConfig::CONFIG['CPPFLAGS']}".match?(/(\A|\s)-D ?MJIT_FORCE_ENABLE\b/)
+ end
end
diff --git a/test/ruby/test_rubyvm_mjit.rb b/test/ruby/test_rubyvm_mjit.rb
new file mode 100644
index 0000000000..12772320f5
--- /dev/null
+++ b/test/ruby/test_rubyvm_mjit.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+require 'test/unit'
+require_relative '../lib/jit_support'
+
+return if RbConfig::CONFIG["MJIT_SUPPORT"] == 'no'
+
+class TestRubyVMMJIT < Test::Unit::TestCase
+ include JITSupport
+
+ def setup
+ unless JITSupport.supported?
+ skip 'JIT seems not supported on this platform'
+ end
+ end
+
+ def test_pause
+ out, err = eval_with_jit(<<~'EOS', verbose: 1, min_calls: 1, wait: false)
+ i = 0
+ while i < 5
+ eval("def mjit#{i}; end; mjit#{i}")
+ i += 1
+ end
+ print RubyVM::MJIT.pause
+ print RubyVM::MJIT.pause
+ while i < 10
+ eval("def mjit#{i}; end; mjit#{i}")
+ i += 1
+ end
+ print RubyVM::MJIT.pause # no JIT here
+ EOS
+ assert_equal('truefalsefalse', out)
+ assert_equal(
+ 5, err.scan(/#{JITSupport::JIT_SUCCESS_PREFIX}/).size,
+ "unexpected stdout:\n```\n#{out}```\n\nstderr:\n```\n#{err}```",
+ )
+ end
+
+ def test_pause_does_not_hang_on_full_units
+ out, _ = eval_with_jit(<<~'EOS', verbose: 1, min_calls: 1, max_cache: 10, wait: false)
+ i = 0
+ while i < 11
+ eval("def mjit#{i}; end; mjit#{i}")
+ i += 1
+ end
+ print RubyVM::MJIT.pause
+ EOS
+ assert_equal('true', out)
+ end
+
+ def test_pause_wait_false
+ out, err = eval_with_jit(<<~'EOS', verbose: 1, min_calls: 1, wait: false)
+ i = 0
+ while i < 10
+ eval("def mjit#{i}; end; mjit#{i}")
+ i += 1
+ end
+ print RubyVM::MJIT.pause(wait: false)
+ print RubyVM::MJIT.pause(wait: false)
+ EOS
+ assert_equal('truefalse', out)
+ assert_equal(true, err.scan(/#{JITSupport::JIT_SUCCESS_PREFIX}/).size < 10)
+ end
+
+ def test_resume
+ out, err = eval_with_jit(<<~'EOS', verbose: 1, min_calls: 1, wait: false)
+ print RubyVM::MJIT.resume
+ print RubyVM::MJIT.pause
+ print RubyVM::MJIT.resume
+ print RubyVM::MJIT.resume
+ print RubyVM::MJIT.pause
+ EOS
+ assert_equal('falsetruetruefalsetrue', out)
+ assert_equal(0, err.scan(/#{JITSupport::JIT_SUCCESS_PREFIX}/).size)
+ end
+end
diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb
index 0930a2232d..29b69b7ce3 100644
--- a/test/ruby/test_settracefunc.rb
+++ b/test/ruby/test_settracefunc.rb
@@ -239,8 +239,6 @@ class TestSetTraceFunc < Test::Unit::TestCase
EOF
assert_equal(["c-return", 1, :set_trace_func, Kernel],
events.shift)
- assert_equal(["line", 4, __method__, self.class],
- events.shift)
assert_equal(["line", 5, __method__, self.class],
events.shift)
assert_equal(["c-call", 5, :raise, Kernel],
@@ -289,8 +287,8 @@ class TestSetTraceFunc < Test::Unit::TestCase
["line", 4, __method__, self.class],
["c-return", 4, :any?, Array],
["line", 5, __method__, self.class],
- ["c-call", 5, :set_trace_func, Kernel]].each{|e|
- assert_equal(e, events.shift)
+ ["c-call", 5, :set_trace_func, Kernel]].each.with_index{|e, i|
+ assert_equal(e, events.shift, "mismatch on #{i}th trace")
}
end
@@ -639,16 +637,19 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_tracepoint_enable
ary = []
+ args = nil
trace = TracePoint.new(:call){|tp|
next if !target_thread?
ary << tp.method_id
}
foo
- trace.enable{
+ trace.enable{|*a|
+ args = a
foo
}
foo
assert_equal([:foo], ary)
+ assert_equal([], args)
trace = TracePoint.new{}
begin
@@ -663,17 +664,20 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_tracepoint_disable
ary = []
+ args = nil
trace = TracePoint.trace(:call){|tp|
next if !target_thread?
ary << tp.method_id
}
foo
- trace.disable{
+ trace.disable{|*a|
+ args = a
foo
}
foo
trace.disable
assert_equal([:foo, :foo], ary)
+ assert_equal([], args)
trace = TracePoint.new{}
trace.enable{
@@ -699,6 +703,45 @@ class TestSetTraceFunc < Test::Unit::TestCase
assert_equal(false, trace.enabled?)
end
+ def parameter_test(a, b, c)
+ yield
+ end
+
+ def test_tracepoint_parameters
+ trace = TracePoint.new(:line, :class, :end, :call, :return, :b_call, :b_return, :c_call, :c_return, :raise){|tp|
+ next if !target_thread?
+ next if tp.path != __FILE__
+ case tp.event
+ when :call, :return
+ assert_equal([[:req, :a], [:req, :b], [:req, :c]], tp.parameters)
+ when :b_call, :b_return
+ next if tp.parameters == []
+ if tp.parameters.first == [:opt, :x]
+ assert_equal([[:opt, :x], [:opt, :y], [:opt, :z]], tp.parameters)
+ else
+ assert_equal([[:req, :p], [:req, :q], [:req, :r]], tp.parameters)
+ end
+ when :c_call, :c_return
+ assert_equal([[:req]], tp.parameters) if tp.method_id == :getbyte
+ when :line, :class, :end, :raise
+ assert_raise(RuntimeError) { tp.parameters }
+ end
+ }
+ obj = Object.new
+ trace.enable{
+ parameter_test(1, 2, 3) {|x, y, z|
+ }
+ lambda {|p, q, r| }.call(4, 5, 6)
+ "".getbyte(0)
+ class << obj
+ end
+ begin
+ raise
+ rescue
+ end
+ }
+ end
+
def method_test_tracepoint_return_value obj
obj
end
@@ -725,13 +768,13 @@ class TestSetTraceFunc < Test::Unit::TestCase
end
def test_tracepoint_raised_exception
- trace = TracePoint.new(:call, :return){|tp|
+ trace = TracePoint.new(:call, :return, :raise){|tp|
next if !target_thread?
case tp.event
when :call, :return
assert_raise(RuntimeError) { tp.raised_exception }
when :raise
- assert_equal(XYZZYError, tp.raised_exception)
+ assert_kind_of(XYZZYException, tp.raised_exception)
end
}
trace.enable{
@@ -808,14 +851,16 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_tracepoint_inspect
events = []
+ th = nil
trace = TracePoint.new{|tp|
- next if !target_thread?
+ next if !target_thread? && th != Thread.current
events << [tp.event, tp.inspect]
}
assert_equal("#<TracePoint:disabled>", trace.inspect)
trace.enable{
assert_equal("#<TracePoint:enabled>", trace.inspect)
- Thread.new{}.join
+ th = Thread.new{}
+ th.join
}
assert_equal("#<TracePoint:disabled>", trace.inspect)
events.each{|(ev, str)|
@@ -851,6 +896,21 @@ class TestSetTraceFunc < Test::Unit::TestCase
end
end
+ def test_tracepoint_exception_at_c_return
+ assert_nothing_raised(Timeout::Error, 'infinite trace') do
+ assert_normal_exit %q{
+ begin
+ TracePoint.new(:c_return){|tp|
+ raise
+ }.enable{
+ tap{ itself }
+ }
+ rescue
+ end
+ }, '', timeout: 3
+ end
+ end
+
def test_tracepoint_with_multithreads
assert_nothing_raised do
TracePoint.new{
@@ -1429,7 +1489,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
evs = []
TracePoint.new(:call, :return){|tp|
- return if Thread.current != target_th
+ next unless target_thread?
evs << tp.event
}.enable{
Bug10724.new
@@ -1492,6 +1552,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_tracepoint_callee_id
events = []
capture_events = Proc.new{|tp|
+ next unless target_thread?
events << [tp.event, tp.method_id, tp.callee_id]
}
@@ -1599,4 +1660,488 @@ class TestSetTraceFunc < Test::Unit::TestCase
assert_equal [[:c_call, :itself, :alias_itself], [:c_return, :itself, :alias_itself]], events
events.clear
end
+
+ # tests for `return_value` with non-local exit [Bug #13369]
+
+ def tp_return_value mid
+ ary = []
+ TracePoint.new(:return, :b_return){|tp| next if !target_thread?; ary << [tp.event, tp.method_id, tp.return_value]}.enable{
+ send mid
+ }
+ ary.pop # last b_return event is not required.
+ ary
+ end
+
+ def f_raise
+ raise
+ rescue
+ return :f_raise_return
+ end
+
+ def f_iter1
+ yield
+ return :f_iter1_return
+ end
+
+ def f_iter2
+ yield
+ return :f_iter2_return
+ end
+
+ def f_return_in_iter
+ f_iter1 do
+ f_iter2 do
+ return :f_return_in_iter_return
+ end
+ end
+ 2
+ end
+
+ def f_break_in_iter
+ f_iter1 do
+ f_iter2 do
+ break :f_break_in_iter_break
+ end
+ :f_iter1_block_value
+ end
+ :f_break_in_iter_return
+ end
+
+ def test_return_value_with_rescue
+ assert_equal [[:return, :f_raise, :f_raise_return]],
+ tp_return_value(:f_raise),
+ '[Bug #13369]'
+
+ assert_equal [[:b_return, :f_return_in_iter, nil],
+ [:return, :f_iter2, nil],
+ [:b_return, :f_return_in_iter, nil],
+ [:return, :f_iter1, nil],
+ [:return, :f_return_in_iter, :f_return_in_iter_return]],
+ tp_return_value(:f_return_in_iter),
+ '[Bug #13369]'
+
+ assert_equal [[:b_return, :f_break_in_iter, :f_break_in_iter_break],
+ [:return, :f_iter2, nil],
+ [:b_return, :f_break_in_iter, :f_iter1_block_value],
+ [:return, :f_iter1, :f_iter1_return],
+ [:return, :f_break_in_iter, :f_break_in_iter_return]],
+ tp_return_value(:f_break_in_iter),
+ '[Bug #13369]'
+ end
+
+ define_method(:f_last_defined) do
+ :f_last_defined
+ end
+
+ define_method(:f_return_defined) do
+ return :f_return_defined
+ end
+
+ define_method(:f_break_defined) do
+ return :f_break_defined
+ end
+
+ define_method(:f_raise_defined) do
+ raise
+ rescue
+ return :f_raise_defined
+ end
+
+ define_method(:f_break_in_rescue_defined) do
+ raise
+ rescue
+ break :f_break_in_rescue_defined
+ end
+
+ def test_return_value_with_rescue_and_defined_methods
+ assert_equal [[:b_return, :f_last_defined, :f_last_defined],
+ [:return, :f_last_defined, :f_last_defined]],
+ tp_return_value(:f_last_defined),
+ '[Bug #13369]'
+
+ assert_equal [[:b_return, :f_return_defined, nil], # current limitation
+ [:return, :f_return_defined, :f_return_defined]],
+ tp_return_value(:f_return_defined),
+ '[Bug #13369]'
+
+ assert_equal [[:b_return, :f_break_defined, nil],
+ [:return, :f_break_defined, :f_break_defined]],
+ tp_return_value(:f_break_defined),
+ '[Bug #13369]'
+
+ assert_equal [[:b_return, :f_raise_defined, nil],
+ [:return, :f_raise_defined, f_raise_defined]],
+ tp_return_value(:f_raise_defined),
+ '[Bug #13369]'
+
+ assert_equal [[:b_return, :f_break_in_rescue_defined, nil],
+ [:return, :f_break_in_rescue_defined, f_break_in_rescue_defined]],
+ tp_return_value(:f_break_in_rescue_defined),
+ '[Bug #13369]'
+ end
+
+ def f_iter
+ yield
+ end
+
+ def f_break_in_rescue
+ f_iter do
+ begin
+ raise
+ rescue
+ break :b
+ end
+ end
+ :f_break_in_rescue_return_value
+ end
+
+ def test_break_with_rescue
+ assert_equal [[:b_return, :f_break_in_rescue, :b],
+ [:return, :f_iter, nil],
+ [:return, :f_break_in_rescue, :f_break_in_rescue_return_value]],
+ tp_return_value(:f_break_in_rescue),
+ '[Bug #13369]'
+ end
+
+ def test_trace_point_raising_exception_in_bmethod_call
+ bug13705 = '[ruby-dev:50162]'
+ assert_normal_exit %q{
+ define_method(:m) {}
+
+ tp = TracePoint.new(:call) do
+ raise ''
+ end
+
+ tap do
+ tap do
+ begin
+ tp.enable
+ m
+ rescue
+ end
+ end
+ end
+ }, bug13705
+ end
+
+ def test_trace_point_require_block
+ assert_raise(ArgumentError) { TracePoint.new(:return) }
+ end
+
+ def method_for_test_thread_add_trace_func
+
+ end
+
+ def test_thread_add_trace_func
+ events = []
+ base_line = __LINE__
+ q = Queue.new
+ t = Thread.new{
+ Thread.current.add_trace_func proc{|ev, file, line, *args|
+ events << [ev, line]
+ } # do not stop trace. They will be stopped at Thread termination.
+ q.push 1
+ _x = 1
+ method_for_test_thread_add_trace_func
+ _y = 2
+ }
+ q.pop
+ method_for_test_thread_add_trace_func
+ t.join
+ assert_equal ["c-return", base_line + 3], events[0]
+ assert_equal ["line", base_line + 6], events[1]
+ assert_equal ["c-call", base_line + 6], events[2]
+ assert_equal ["c-return", base_line + 6], events[3]
+ assert_equal ["line", base_line + 7], events[4]
+ assert_equal ["line", base_line + 8], events[5]
+ assert_equal ["call", base_line + -6], events[6]
+ assert_equal ["return", base_line + -4], events[7]
+ assert_equal ["line", base_line + 9], events[8]
+ assert_equal nil, events[9]
+
+ # other thread
+ events = []
+ m2t_q = Queue.new
+
+ t = Thread.new{
+ Thread.current.abort_on_exception = true
+ assert_equal 1, m2t_q.pop
+ _x = 1
+ method_for_test_thread_add_trace_func
+ _y = 2
+ Thread.current.set_trace_func(nil)
+ method_for_test_thread_add_trace_func
+ }
+ # it is dirty hack. usually we shouldn't use such technique
+ Thread.pass until t.status == 'sleep'
+ # When MJIT thread exists, t.status becomes 'sleep' even if it does not reach m2t_q.pop.
+ # This sleep forces it to reach m2t_q.pop for --jit-wait.
+ sleep 1 if RubyVM::MJIT.enabled?
+
+ t.add_trace_func proc{|ev, file, line, *args|
+ if file == __FILE__
+ events << [ev, line]
+ end
+ }
+
+ method_for_test_thread_add_trace_func
+
+ m2t_q.push 1
+ t.join
+
+ assert_equal ["c-return", base_line + 31], events[0]
+ assert_equal ["line", base_line + 32], events[1]
+ assert_equal ["line", base_line + 33], events[2]
+ assert_equal ["call", base_line + -6], events[3]
+ assert_equal ["return", base_line + -4], events[4]
+ assert_equal ["line", base_line + 34], events[5]
+ assert_equal ["line", base_line + 35], events[6]
+ assert_equal ["c-call", base_line + 35], events[7] # Thread.current
+ assert_equal ["c-return", base_line + 35], events[8] # Thread.current
+ assert_equal ["c-call", base_line + 35], events[9] # Thread#set_trace_func
+ assert_equal nil, events[10]
+ end
+
+ def test_lineno_in_optimized_insn
+ actual, _, _ = EnvUtil.invoke_ruby [], <<-EOF.gsub(/^.*?: */, ""), true
+ 1: class String
+ 2: def -@
+ 3: puts caller_locations(1, 1)[0].lineno
+ 4: end
+ 5: end
+ 6:
+ 7: -""
+ EOF
+ assert_equal "7\n", actual, '[Bug #14809]'
+ end
+
+ def method_for_enable_target1
+ a = 1
+ b = 2
+ 1.times{|i|
+ x = i
+ }
+ c = a + b
+ end
+
+ def method_for_enable_target2
+ a = 1
+ b = 2
+ 1.times{|i|
+ x = i
+ }
+ c = a + b
+ end
+
+ def check_with_events *trace_events
+ all_events = [[:call, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:b_call, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:b_return, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:return, :method_for_enable_target1],
+ # repeat
+ [:call, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:b_call, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:b_return, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:return, :method_for_enable_target1],
+ ]
+ events = []
+ TracePoint.new(*trace_events) do |tp|
+ next unless target_thread?
+ events << [tp.event, tp.method_id]
+ end.enable(target: method(:method_for_enable_target1)) do
+ method_for_enable_target1
+ method_for_enable_target2
+ method_for_enable_target1
+ end
+ assert_equal all_events.find_all{|(ev, m)| trace_events.include? ev}, events
+ end
+
+ def test_tracepoint_enable_target
+ check_with_events :line
+ check_with_events :call, :return
+ check_with_events :line, :call, :return
+ check_with_events :call, :return, :b_call, :b_return
+ check_with_events :line, :call, :return, :b_call, :b_return
+ end
+
+ def test_tracepoint_nested_enabled_with_target
+ code1 = proc{
+ a = 1
+ }
+ code2 = proc{
+ b = 2
+ }
+
+ ## error
+
+ # targetted TP and targetted TP
+ ex = assert_raise(ArgumentError) do
+ tp = TracePoint.new(:line){}
+ tp.enable(target: code1){
+ tp.enable(target: code2){}
+ }
+ end
+ assert_equal "can't nest-enable a targetting TracePoint", ex.message
+
+ # global TP and targetted TP
+ ex = assert_raise(ArgumentError) do
+ tp = TracePoint.new(:line){}
+ tp.enable{
+ tp.enable(target: code2){}
+ }
+ end
+ assert_equal "can't nest-enable a targetting TracePoint", ex.message
+
+ # targetted TP and global TP
+ ex = assert_raise(ArgumentError) do
+ tp = TracePoint.new(:line){}
+ tp.enable(target: code1){
+ tp.enable{}
+ }
+ end
+ assert_equal "can't nest-enable a targetting TracePoint", ex.message
+
+ # targetted TP and disable
+ ex = assert_raise(ArgumentError) do
+ tp = TracePoint.new(:line){}
+ tp.enable(target: code1){
+ tp.disable{}
+ }
+ end
+ assert_equal "can't disable a targetting TracePoint in a block", ex.message
+
+ ## success with two nesting targetting tracepoints
+ events = []
+ tp1 = TracePoint.new(:line){|tp| events << :tp1}
+ tp2 = TracePoint.new(:line){|tp| events << :tp2}
+ tp1.enable(target: code1) do
+ tp2.enable(target: code1) do
+ code1.call
+ events << :___
+ end
+ end
+ assert_equal [:tp2, :tp1, :___], events
+
+ # succss with two tracepoints (global/targetting)
+ events = []
+ tp1 = TracePoint.new(:line){|tp| events << :tp1}
+ tp2 = TracePoint.new(:line){|tp| events << :tp2}
+ tp1.enable do
+ tp2.enable(target: code1) do
+ code1.call
+ events << :___
+ end
+ end
+ assert_equal [:tp1, :tp1, :tp1, :tp1, :tp2, :tp1, :___], events
+
+ # succss with two tracepoints (targetting/global)
+ events = []
+ tp1 = TracePoint.new(:line){|tp| events << :tp1}
+ tp2 = TracePoint.new(:line){|tp| events << :tp2}
+ tp1.enable(target: code1) do
+ tp2.enable do
+ code1.call
+ events << :___
+ end
+ end
+ assert_equal [:tp2, :tp2, :tp1, :tp2, :___], events
+ end
+
+ def test_tracepoint_enable_with_target_line
+ events = []
+ line_0 = __LINE__
+ code1 = proc{
+ events << 1
+ events << 2
+ events << 3
+ }
+ tp = TracePoint.new(:line) do |tp|
+ events << :tp
+ end
+ tp.enable(target: code1, target_line: line_0 + 3) do
+ code1.call
+ end
+ assert_equal [1, :tp, 2, 3], events
+
+
+ e = assert_raise(ArgumentError) do
+ TracePoint.new(:line){}.enable(target_line: 10){}
+ end
+ assert_equal 'only target_line is specified', e.message
+
+ e = assert_raise(ArgumentError) do
+ TracePoint.new(:call){}.enable(target: code1, target_line: 10){}
+ end
+ assert_equal 'target_line is specified, but line event is not specified', e.message
+ end
+
+ def test_script_compiled
+ events = []
+ tp = TracePoint.new(:script_compiled){|tp|
+ next unless target_thread?
+ events << [tp.instruction_sequence.path,
+ tp.eval_script]
+ }
+
+ eval_script = 'a = 1'
+ tp.enable{
+ eval(eval_script, nil, __FILE__+"/eval")
+ nil.instance_eval(eval_script, __FILE__+"/instance_eval")
+ Object.class_eval(eval_script, __FILE__+"/class_eval")
+ }
+ assert_equal [[__FILE__+"/eval", eval_script],
+ [__FILE__+"/instance_eval", eval_script],
+ [__FILE__+"/class_eval", eval_script],
+ ], events
+ events.clear
+
+ # TODO: test for requires
+ return
+
+ tp.enable{
+ require ''
+ require_relative ''
+ load ''
+ }
+ assert_equal [], events
+ end
+
+ def test_return_event_with_rescue
+ obj = Object.new
+ def obj.example
+ 1 if 1 == 1
+ rescue
+ end
+ ok = false
+ tp = TracePoint.new(:return) {ok = true}
+ tp.enable {obj.example}
+ assert ok, "return event should be emitted"
+ end
+
+ def test_disable_local_tracepoint_in_trace
+ assert_normal_exit <<-EOS
+ def foo
+ trace = TracePoint.new(:b_return){|tp|
+ tp.disable
+ }
+ trace.enable(target: method(:bar))
+ end
+ def bar
+ 100.times{|i|
+ foo; foo
+ }
+ end
+ bar
+ EOS
+ end
end
diff --git a/test/ruby/test_signal.rb b/test/ruby/test_signal.rb
index c48a4ad400..425dc26574 100644
--- a/test/ruby/test_signal.rb
+++ b/test/ruby/test_signal.rb
@@ -28,7 +28,8 @@ class TestSignal < Test::Unit::TestCase
def test_signal_process_group
bug4362 = '[ruby-dev:43169]'
assert_nothing_raised(bug4362) do
- pid = Process.spawn(EnvUtil.rubybin, '-e', 'sleep 10', :pgroup => true)
+ cmd = [ EnvUtil.rubybin, '--disable=gems' '-e', 'sleep 10' ]
+ pid = Process.spawn(*cmd, :pgroup => true)
Process.kill(:"-TERM", pid)
Process.waitpid(pid)
assert_equal(true, $?.signaled?)
@@ -44,7 +45,7 @@ class TestSignal < Test::Unit::TestCase
sig = "INT"
term = :KILL
end
- IO.popen([EnvUtil.rubybin, '-e', <<-"End"], 'r+') do |io|
+ IO.popen([EnvUtil.rubybin, '--disable=gems', '-e', <<-"End"], 'r+') do |io|
Signal.trap(:#{sig}, "EXIT")
STDOUT.syswrite("a")
Thread.start { sleep(2) }
@@ -83,10 +84,12 @@ class TestSignal < Test::Unit::TestCase
assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { SignalException.new("\u{30eb 30d3 30fc}") }
Signal.list.each do |signm, signo|
next if signm == "EXIT"
- assert_equal(SignalException.new(signm).signo, signo)
- assert_equal(SignalException.new(signm.to_sym).signo, signo)
- assert_equal(SignalException.new(signo).signo, signo)
+ assert_equal(signo, SignalException.new(signm).signo, signm)
+ assert_equal(signo, SignalException.new(signm.to_sym).signo, signm)
+ assert_equal(signo, SignalException.new(signo).signo, signo)
end
+ e = assert_raise(ArgumentError) {SignalException.new("-SIGEXIT")}
+ assert_not_match(/SIG-SIG/, e.message)
end
def test_interrupt
@@ -164,6 +167,8 @@ class TestSignal < Test::Unit::TestCase
assert_raise(ArgumentError) { Signal.trap("XXXXXXXXXX", "SIG_DFL") }
assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Signal.trap("\u{30eb 30d3 30fc}", "SIG_DFL") }
+
+ assert_raise(ArgumentError) { Signal.trap("EXIT\0") {} }
ensure
Signal.trap(:INT, oldtrap) if oldtrap
end
@@ -228,20 +233,20 @@ class TestSignal < Test::Unit::TestCase
end
def test_signame_delivered
- 10.times do
- IO.popen([EnvUtil.rubybin, "-e", <<EOS, :err => File::NULL]) do |child|
- Signal.trap("INT") do |signo|
- signame = Signal.signame(signo)
- Marshal.dump(signame, STDOUT)
- STDOUT.flush
- exit 0
- end
- Process.kill("INT", $$)
- sleep 1 # wait signal deliver
-EOS
+ args = [EnvUtil.rubybin, "--disable=gems", "-e", <<"", :err => File::NULL]
+ Signal.trap("INT") do |signo|
+ signame = Signal.signame(signo)
+ Marshal.dump(signame, STDOUT)
+ STDOUT.flush
+ exit 0
+ end
+ Process.kill("INT", $$)
+ sleep 1 # wait signal deliver
+ 10.times do
+ IO.popen(args) do |child|
signame = Marshal.load(child)
- assert_equal(signame, "INT")
+ assert_equal("INT", signame)
end
end
end if Process.respond_to?(:kill)
@@ -307,4 +312,81 @@ EOS
b = Signal.list.keys.map(&:object_id).sort
assert_equal a, b
end
+
+ def test_self_stop
+ assert_ruby_status([], <<-'end;')
+ begin
+ fork{
+ sleep 1
+ Process.kill(:CONT, Process.ppid)
+ }
+ Process.kill(:STOP, Process.pid)
+ rescue NotImplementedError
+ # ok
+ end
+ end;
+ end
+
+ def test_sigchld_ignore
+ skip 'no SIGCHLD' unless Signal.list['CHLD']
+ old = trap(:CHLD, 'IGNORE')
+ cmd = [ EnvUtil.rubybin, '--disable=gems', '-e' ]
+ assert(system(*cmd, 'exit!(0)'), 'no ECHILD')
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ IO.pipe do |r, w|
+ pid = spawn(*cmd, "STDIN.read", in: r)
+ nb = Process.wait(pid, Process::WNOHANG)
+ th = Thread.new(Thread.current) do |parent|
+ Thread.pass until parent.stop? # wait for parent to Process.wait
+ w.close
+ end
+ assert_raise(Errno::ECHILD) { Process.wait(pid) }
+ assert_nil nb
+ end
+
+ IO.pipe do |r, w|
+ pids = 3.times.map { spawn(*cmd, 'exit!', out: w) }
+ w.close
+ zombies = pids.dup
+ assert_nil r.read(1), 'children dead'
+
+ Timeout.timeout(10) do
+ zombies.delete_if do |pid|
+ begin
+ Process.kill(0, pid)
+ false
+ rescue Errno::ESRCH
+ true
+ end
+ end while zombies[0]
+ end
+ assert_predicate zombies, :empty?, 'zombies leftover'
+
+ pids.each do |pid|
+ assert_raise(Errno::ECHILD) { Process.waitpid(pid) }
+ end
+ end
+ ensure
+ trap(:CHLD, old) if Signal.list['CHLD']
+ end
+
+ def test_sigwait_fd_unused
+ t = EnvUtil.apply_timeout_scale(0.1)
+ assert_separately([], <<-End)
+ tgt = $$
+ trap(:TERM) { exit(0) }
+ e = "Process.daemon; sleep #{t * 2}; Process.kill(:TERM,\#{tgt})"
+ term = [ '#{EnvUtil.rubybin}', '--disable=gems', '-e', e ]
+ t2 = Thread.new { sleep } # grab sigwait_fd
+ Thread.pass until t2.stop?
+ Thread.new do
+ sleep #{t}
+ t2.kill
+ t2.join
+ end
+ Process.spawn(*term)
+ # last thread remaining, ensure it can react to SIGTERM
+ loop { sleep }
+ End
+ end if Process.respond_to?(:kill) && Process.respond_to?(:daemon)
end
diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb
index df9b19b064..7986e9d141 100644
--- a/test/ruby/test_sprintf.rb
+++ b/test/ruby/test_sprintf.rb
@@ -84,6 +84,18 @@ class TestSprintf < Test::Unit::TestCase
assert_equal("NaN", sprintf("%-f", nan))
assert_equal("+NaN", sprintf("%+f", nan))
+ assert_equal("NaN", sprintf("%3f", nan))
+ assert_equal("NaN", sprintf("%-3f", nan))
+ assert_equal("+NaN", sprintf("%+3f", nan))
+
+ assert_equal(" NaN", sprintf("% 3f", nan))
+ assert_equal(" NaN", sprintf("%- 3f", nan))
+ assert_equal("+NaN", sprintf("%+ 3f", nan))
+
+ assert_equal(" NaN", sprintf("% 03f", nan))
+ assert_equal(" NaN", sprintf("%- 03f", nan))
+ assert_equal("+NaN", sprintf("%+ 03f", nan))
+
assert_equal(" NaN", sprintf("%8f", nan))
assert_equal("NaN ", sprintf("%-8f", nan))
assert_equal(" +NaN", sprintf("%+8f", nan))
@@ -107,6 +119,26 @@ class TestSprintf < Test::Unit::TestCase
assert_equal("Inf", sprintf("%-f", inf))
assert_equal("+Inf", sprintf("%+f", inf))
+ assert_equal(" Inf", sprintf("% f", inf))
+ assert_equal(" Inf", sprintf("%- f", inf))
+ assert_equal("+Inf", sprintf("%+ f", inf))
+
+ assert_equal(" Inf", sprintf("% 0f", inf))
+ assert_equal(" Inf", sprintf("%- 0f", inf))
+ assert_equal("+Inf", sprintf("%+ 0f", inf))
+
+ assert_equal("Inf", sprintf("%3f", inf))
+ assert_equal("Inf", sprintf("%-3f", inf))
+ assert_equal("+Inf", sprintf("%+3f", inf))
+
+ assert_equal(" Inf", sprintf("% 3f", inf))
+ assert_equal(" Inf", sprintf("%- 3f", inf))
+ assert_equal("+Inf", sprintf("%+ 3f", inf))
+
+ assert_equal(" Inf", sprintf("% 03f", inf))
+ assert_equal(" Inf", sprintf("%- 03f", inf))
+ assert_equal("+Inf", sprintf("%+ 03f", inf))
+
assert_equal(" Inf", sprintf("%8f", inf))
assert_equal("Inf ", sprintf("%-8f", inf))
assert_equal(" +Inf", sprintf("%+8f", inf))
@@ -127,6 +159,26 @@ class TestSprintf < Test::Unit::TestCase
assert_equal("-Inf", sprintf("%-f", -inf))
assert_equal("-Inf", sprintf("%+f", -inf))
+ assert_equal("-Inf", sprintf("% f", -inf))
+ assert_equal("-Inf", sprintf("%- f", -inf))
+ assert_equal("-Inf", sprintf("%+ f", -inf))
+
+ assert_equal("-Inf", sprintf("% 0f", -inf))
+ assert_equal("-Inf", sprintf("%- 0f", -inf))
+ assert_equal("-Inf", sprintf("%+ 0f", -inf))
+
+ assert_equal("-Inf", sprintf("%4f", -inf))
+ assert_equal("-Inf", sprintf("%-4f", -inf))
+ assert_equal("-Inf", sprintf("%+4f", -inf))
+
+ assert_equal("-Inf", sprintf("% 4f", -inf))
+ assert_equal("-Inf", sprintf("%- 4f", -inf))
+ assert_equal("-Inf", sprintf("%+ 4f", -inf))
+
+ assert_equal("-Inf", sprintf("% 04f", -inf))
+ assert_equal("-Inf", sprintf("%- 04f", -inf))
+ assert_equal("-Inf", sprintf("%+ 04f", -inf))
+
assert_equal(" -Inf", sprintf("%8f", -inf))
assert_equal("-Inf ", sprintf("%-8f", -inf))
assert_equal(" -Inf", sprintf("%+8f", -inf))
@@ -186,6 +238,12 @@ class TestSprintf < Test::Unit::TestCase
assert_equal("with options {:capture=>/\\d+/}", sprintf("with options %p" % options))
end
+ def test_inspect
+ obj = Object.new
+ def obj.inspect; "TEST"; end
+ assert_equal("<TEST>", sprintf("<%p>", obj))
+ end
+
def test_invalid
# Star precision before star width:
assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%.**d", 5, 10, 1)}
@@ -372,6 +430,16 @@ class TestSprintf < Test::Unit::TestCase
assert_equal("%" * BSIZ, sprintf("%%" * BSIZ))
end
+ def test_percent_sign_at_end
+ assert_raise_with_message(ArgumentError, "incomplete format specifier; use %% (double %) instead") do
+ sprintf("%")
+ end
+
+ assert_raise_with_message(ArgumentError, "incomplete format specifier; use %% (double %) instead") do
+ sprintf("abc%")
+ end
+ end
+
def test_rb_sprintf
assert_match(/^#<TestSprintf::T012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789:0x[0-9a-f]+>$/,
T012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.new.inspect)
@@ -390,7 +458,10 @@ class TestSprintf < Test::Unit::TestCase
assert_raise_with_message(ArgumentError, "named<key2> after numbered") {sprintf("%1$<key2>s", :key => "value")}
assert_raise_with_message(ArgumentError, "named<key2> after unnumbered(2)") {sprintf("%s%s%<key2>s", "foo", "bar", :key => "value")}
assert_raise_with_message(ArgumentError, "named<key2> after <key>") {sprintf("%<key><key2>s", :key => "value")}
- assert_raise_with_message(KeyError, "key<key> not found") {sprintf("%<key>s", {})}
+ h = {}
+ e = assert_raise_with_message(KeyError, "key<key> not found") {sprintf("%<key>s", h)}
+ assert_same(h, e.receiver)
+ assert_equal(:key, e.key)
end
def test_named_untyped_enc
@@ -446,4 +517,30 @@ class TestSprintf < Test::Unit::TestCase
h = { key: nil, key2: "key2_val" }
assert_equal("key is , key2 is key2_val", "key is %{key}, key2 is %{key2}" % h)
end
+
+ def test_width_underflow
+ bug = 'https://github.com/mruby/mruby/issues/3347'
+ assert_equal("!", sprintf("%*c", 0, ?!.ord), bug)
+ end
+
+ def test_negative_width_overflow
+ assert_raise_with_message(ArgumentError, /too big/) do
+ sprintf("%*s", RbConfig::LIMITS["INT_MIN"], "")
+ end
+ end
+
+ def test_no_hidden_garbage
+ skip unless Thread.list.size == 1
+
+ fmt = [4, 2, 2].map { |x| "%0#{x}d" }.join('-') # defeats optimization
+ ObjectSpace.count_objects(res = {}) # creates strings on first call
+ GC.disable
+ before = ObjectSpace.count_objects(res)[:T_STRING]
+ val = sprintf(fmt, 1970, 1, 1)
+ after = ObjectSpace.count_objects(res)[:T_STRING]
+ assert_equal before + 1, after, 'only new string is the created one'
+ assert_equal '1970-01-01', val
+ ensure
+ GC.enable
+ end
end
diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb
index b1d795e183..23da909592 100644
--- a/test/ruby/test_string.rb
+++ b/test/ruby/test_string.rb
@@ -4,6 +4,11 @@ require 'test/unit'
class TestString < Test::Unit::TestCase
ENUMERATOR_WANTARRAY = RUBY_VERSION >= "3.0.0"
+ WIDE_ENCODINGS = [
+ Encoding::UTF_16BE, Encoding::UTF_16LE,
+ Encoding::UTF_32BE, Encoding::UTF_32LE,
+ ]
+
def initialize(*args)
@cls = String
@aref_re_nth = true
@@ -62,12 +67,26 @@ class TestString < Test::Unit::TestCase
def test_initialize
str = S("").freeze
assert_equal("", str.__send__(:initialize))
- assert_raise(RuntimeError){ str.__send__(:initialize, 'abc') }
- assert_raise(RuntimeError){ str.__send__(:initialize, capacity: 1000) }
- assert_raise(RuntimeError){ str.__send__(:initialize, 'abc', capacity: 1000) }
- assert_raise(RuntimeError){ str.__send__(:initialize, encoding: 'euc-jp') }
- assert_raise(RuntimeError){ str.__send__(:initialize, 'abc', encoding: 'euc-jp') }
- assert_raise(RuntimeError){ str.__send__(:initialize, 'abc', capacity: 1000, encoding: 'euc-jp') }
+ assert_raise(FrozenError){ str.__send__(:initialize, 'abc') }
+ assert_raise(FrozenError){ str.__send__(:initialize, capacity: 1000) }
+ assert_raise(FrozenError){ str.__send__(:initialize, 'abc', capacity: 1000) }
+ assert_raise(FrozenError){ str.__send__(:initialize, encoding: 'euc-jp') }
+ assert_raise(FrozenError){ str.__send__(:initialize, 'abc', encoding: 'euc-jp') }
+ assert_raise(FrozenError){ str.__send__(:initialize, 'abc', capacity: 1000, encoding: 'euc-jp') }
+
+ str = S("")
+ assert_equal("mystring", str.__send__(:initialize, "mystring"))
+ str = S("mystring")
+ assert_equal("mystring", str.__send__(:initialize, str))
+ str = S("")
+ assert_equal("mystring", str.__send__(:initialize, "mystring", capacity: 1000))
+ str = S("mystring")
+ assert_equal("mystring", str.__send__(:initialize, str, capacity: 1000))
+ end
+
+ def test_initialize_shared
+ String.new(str = "mystring" * 10).__send__(:initialize, capacity: str.bytesize)
+ assert_equal("mystring", str[0, 8])
end
def test_initialize_nonstring
@@ -202,6 +221,8 @@ CODE
assert_equal("fobar", s)
assert_raise(ArgumentError) { "foo"[1, 2, 3] = "" }
+
+ assert_raise(IndexError) {"foo"[RbConfig::LIMITS["LONG_MIN"]] = "l"}
end
def test_CMP # '<=>'
@@ -381,6 +402,56 @@ CODE
$/ = save
assert_equal(S("a").hash, S("a\u0101").chomp(S("\u0101")).hash, '[ruby-core:22414]')
+
+ s = S("hello")
+ assert_equal("hel", s.chomp('lo'))
+ assert_equal("hello", s)
+
+ s = S("hello")
+ assert_equal("hello", s.chomp('he'))
+ assert_equal("hello", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{3053 3093 306b}", s.chomp("\u{3061 306f}"))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{3053 3093 306b 3061 306f}", s.chomp('lo'))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("hello")
+ assert_equal("hello", s.chomp("\u{3061 306f}"))
+ assert_equal("hello", s)
+
+ # skip if argument is a broken string
+ s = S("\xe3\x81\x82")
+ assert_equal("\xe3\x81\x82", s.chomp("\x82"))
+ assert_equal("\xe3\x81\x82", s)
+
+ s = S("\x95\x5c").force_encoding("Shift_JIS")
+ assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s.chomp("\x5c"))
+ assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s)
+
+ # clear coderange
+ s = S("hello\u{3053 3093}")
+ assert_not_predicate(s, :ascii_only?)
+ assert_predicate(s.chomp("\u{3053 3093}"), :ascii_only?)
+
+ # argument should be converted to String
+ klass = Class.new { def to_str; 'a'; end }
+ s = S("abba")
+ assert_equal("abb", s.chomp(klass.new))
+ assert_equal("abba", s)
+
+ # chomp removes any of "\n", "\r\n", "\r" when "\n" is specified
+ s = "foo\n"
+ assert_equal("foo", s.chomp("\n"))
+ s = "foo\r\n"
+ assert_equal("foo", s.chomp("\n"))
+ s = "foo\r"
+ assert_equal("foo", s.chomp("\n"))
+ ensure
+ $/ = save
end
def test_chomp!
@@ -437,6 +508,67 @@ CODE
assert_equal("foo\r", s)
assert_equal(S("a").hash, S("a\u0101").chomp!(S("\u0101")).hash, '[ruby-core:22414]')
+
+ s = S("").freeze
+ assert_raise_with_message(FrozenError, /frozen/) {s.chomp!}
+
+ s = S("ax")
+ o = Struct.new(:s).new(s)
+ def o.to_str
+ s.freeze
+ "x"
+ end
+ assert_raise_with_message(FrozenError, /frozen/) {s.chomp!(o)}
+
+ s = S("hello")
+ assert_equal("hel", s.chomp!('lo'))
+ assert_equal("hel", s)
+
+ s = S("hello")
+ assert_equal(nil, s.chomp!('he'))
+ assert_equal("hello", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{3053 3093 306b}", s.chomp!("\u{3061 306f}"))
+ assert_equal("\u{3053 3093 306b}", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal(nil, s.chomp!('lo'))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("hello")
+ assert_equal(nil, s.chomp!("\u{3061 306f}"))
+ assert_equal("hello", s)
+
+ # skip if argument is a broken string
+ s = S("\xe3\x81\x82")
+ assert_equal(nil, s.chomp!("\x82"))
+ assert_equal("\xe3\x81\x82", s)
+
+ s = S("\x95\x5c").force_encoding("Shift_JIS")
+ assert_equal(nil, s.chomp!("\x5c"))
+ assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s)
+
+ # clear coderange
+ s = S("hello\u{3053 3093}")
+ assert_not_predicate(s, :ascii_only?)
+ assert_predicate(s.chomp!("\u{3053 3093}"), :ascii_only?)
+
+ # argument should be converted to String
+ klass = Class.new { def to_str; 'a'; end }
+ s = S("abba")
+ assert_equal("abb", s.chomp!(klass.new))
+ assert_equal("abb", s)
+
+ # chomp removes any of "\n", "\r\n", "\r" when "\n" is specified
+ s = "foo\n"
+ assert_equal("foo", s.chomp!("\n"))
+ s = "foo\r\n"
+ assert_equal("foo", s.chomp!("\n"))
+ s = "foo\r"
+ assert_equal("foo", s.chomp!("\n"))
+ ensure
+ $/ = save
end
def test_chop
@@ -504,7 +636,12 @@ CODE
expected = S("\u0300".encode(Encoding::UTF_16LE))
assert_equal(expected, result, bug7090)
assert_raise(TypeError) { 'foo' << :foo }
- assert_raise(RuntimeError) { 'foo'.freeze.concat('bar') }
+ assert_raise(FrozenError) { 'foo'.freeze.concat('bar') }
+ end
+
+ def test_concat_literals
+ s="." * 50
+ assert_equal(Encoding::UTF_8, "#{s}x".encoding)
end
def test_count
@@ -535,11 +672,16 @@ CODE
assert_raise(ArgumentError) {S("mypassword").crypt(S("\0a"))}
assert_raise(ArgumentError) {S("mypassword").crypt(S("a\0"))}
assert_raise(ArgumentError) {S("poison\u0000null").crypt(S("aa"))}
- [Encoding::UTF_16BE, Encoding::UTF_16LE,
- Encoding::UTF_32BE, Encoding::UTF_32LE].each do |enc|
+ WIDE_ENCODINGS.each do |enc|
assert_raise(ArgumentError) {S("mypassword").crypt(S("aa".encode(enc)))}
assert_raise(ArgumentError) {S("mypassword".encode(enc)).crypt(S("aa"))}
end
+
+ @cls == String and
+ assert_no_memory_leak([], 's = ""', "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ 1000.times { s.crypt(-"..").clear }
+ end;
end
def test_delete
@@ -551,7 +693,7 @@ CODE
assert_equal("a".hash, "a\u0101".delete("\u0101").hash, '[ruby-talk:329267]')
assert_equal(true, "a\u0101".delete("\u0101").ascii_only?)
assert_equal(true, "a\u3041".delete("\u3041").ascii_only?)
- assert_equal(false, "a\u3041\u3042".tr("\u3041", "a").ascii_only?)
+ assert_equal(false, "a\u3041\u3042".delete("\u3041").ascii_only?)
assert_equal("a", "abc\u{3042 3044 3046}".delete("^a"))
assert_equal("bc\u{3042 3044 3046}", "abc\u{3042 3044 3046}".delete("a"))
@@ -631,6 +773,70 @@ CODE
assert_equal(S('"\\u{10ABCD}"'), b.dump)
end
+ def test_undump
+ a = S("Test") << 1 << 2 << 3 << 9 << 13 << 10
+ assert_equal(a, S('"Test\\x01\\x02\\x03\\t\\r\\n"').undump)
+ assert_equal(S("\\ca"), S('"\\ca"').undump)
+ assert_equal(S("\u{7F}"), S('"\\x7F"').undump)
+ assert_equal(S("\u{7F}A"), S('"\\x7FA"').undump)
+ assert_equal(S("\u{AB}"), S('"\\u00AB"').undump)
+ assert_equal(S("\u{ABC}"), S('"\\u0ABC"').undump)
+ assert_equal(S("\uABCD"), S('"\\uABCD"').undump)
+ assert_equal(S("\uABCD"), S('"\\uABCD"').undump)
+ assert_equal(S("\u{ABCDE}"), S('"\\u{ABCDE}"').undump)
+ assert_equal(S("\u{10ABCD}"), S('"\\u{10ABCD}"').undump)
+ assert_equal(S("\u{ABCDE 10ABCD}"), S('"\\u{ABCDE 10ABCD}"').undump)
+ assert_equal(S(""), S('"\\u{}"').undump)
+ assert_equal(S(""), S('"\\u{ }"').undump)
+
+ assert_equal(S("\u3042".encode("sjis")), S('"\x82\xA0"'.force_encoding("sjis")).undump)
+ assert_equal(S("\u8868".encode("sjis")), S("\"\\x95\\\\\"".force_encoding("sjis")).undump)
+
+ assert_equal(S("äöü"), S('"\u00E4\u00F6\u00FC"').undump)
+ assert_equal(S("äöü"), S('"\xC3\xA4\xC3\xB6\xC3\xBC"').undump)
+
+ assert_equal(Encoding::UTF_8, S('"\\u3042"').encode(Encoding::EUC_JP).undump.encoding)
+
+ assert_equal("abc".encode(Encoding::UTF_16LE),
+ '"a\x00b\x00c\x00".force_encoding("UTF-16LE")'.undump)
+
+ assert_equal('\#', '"\\\\#"'.undump)
+ assert_equal('\#{', '"\\\\\#{"'.undump)
+
+ assert_raise(RuntimeError) { S('\u3042').undump }
+ assert_raise(RuntimeError) { S('"\x82\xA0\u3042"'.force_encoding("SJIS")).undump }
+ assert_raise(RuntimeError) { S('"\u3042\x82\xA0"'.force_encoding("SJIS")).undump }
+ assert_raise(RuntimeError) { S('"".force_encoding()').undump }
+ assert_raise(RuntimeError) { S('"".force_encoding("').undump }
+ assert_raise(RuntimeError) { S('"".force_encoding("UNKNOWN")').undump }
+ assert_raise(RuntimeError) { S('"\u3042".force_encoding("UTF-16LE")').undump }
+ assert_raise(RuntimeError) { S('"\x00\x00".force_encoding("UTF-16LE")"').undump }
+ assert_raise(RuntimeError) { S('"\x00\x00".force_encoding("'+("a"*9999999)+'")"').undump }
+ assert_raise(RuntimeError) { S(%("\u00E4")).undump }
+ assert_raise(RuntimeError) { S('"').undump }
+ assert_raise(RuntimeError) { S('"""').undump }
+ assert_raise(RuntimeError) { S('""""').undump }
+
+ assert_raise(RuntimeError) { S('"a').undump }
+ assert_raise(RuntimeError) { S('"\u"').undump }
+ assert_raise(RuntimeError) { S('"\u{"').undump }
+ assert_raise(RuntimeError) { S('"\u304"').undump }
+ assert_raise(RuntimeError) { S('"\u304Z"').undump }
+ assert_raise(RuntimeError) { S('"\udfff"').undump }
+ assert_raise(RuntimeError) { S('"\u{dfff}"').undump }
+ assert_raise(RuntimeError) { S('"\u{3042"').undump }
+ assert_raise(RuntimeError) { S('"\u{3042 "').undump }
+ assert_raise(RuntimeError) { S('"\u{110000}"').undump }
+ assert_raise(RuntimeError) { S('"\u{1234567}"').undump }
+ assert_raise(RuntimeError) { S('"\x"').undump }
+ assert_raise(RuntimeError) { S('"\xA"').undump }
+ assert_raise(RuntimeError) { S('"\\"').undump }
+ assert_raise(RuntimeError) { S(%("\0")).undump }
+ assert_raise_with_message(RuntimeError, /invalid/) {
+ '"\\u{007F}".xxxxxx'.undump
+ }
+ end
+
def test_dup
for taint in [ false, true ]
for frozen in [ false, true ]
@@ -657,14 +863,15 @@ CODE
res=[]
S("hello\n\n\nworld").lines(S('')).each {|x| res << x}
- assert_equal(S("hello\n\n\n"), res[0])
- assert_equal(S("world"), res[1])
+ assert_equal(S("hello\n\n"), res[0])
+ assert_equal(S("world"), res[1])
$/ = "!"
res=[]
S("hello!world").lines.each {|x| res << x}
assert_equal(S("hello!"), res[0])
assert_equal(S("world"), res[1])
+ ensure
$/ = save
end
@@ -689,13 +896,20 @@ CODE
assert_equal [65, 66, 67], s.bytes {}
}
else
- assert_warning(/deprecated/) {
+ warning = /passing a block to String#bytes is deprecated/
+ assert_warning(warning) {
res = []
assert_equal s.object_id, s.bytes {|x| res << x }.object_id
assert_equal(65, res[0])
assert_equal(66, res[1])
assert_equal(67, res[2])
}
+ assert_warning(warning) {
+ s = S("ABC")
+ res = []
+ assert_same s, s.bytes {|x| res << x }
+ assert_equal [65, 66, 67], res
+ }
end
end
@@ -726,13 +940,20 @@ CODE
assert_equal [0x3042, 0x3044, 0x3046], s.codepoints {}
}
else
- assert_warning(/deprecated/) {
+ warning = /passing a block to String#codepoints is deprecated/
+ assert_warning(warning) {
res = []
assert_equal s.object_id, s.codepoints {|x| res << x }.object_id
assert_equal(0x3042, res[0])
assert_equal(0x3044, res[1])
assert_equal(0x3046, res[2])
}
+ assert_warning(warning) {
+ s = S("ABC")
+ res = []
+ assert_same s, s.codepoints {|x| res << x }
+ assert_equal [65, 66, 67], res
+ }
end
end
@@ -757,7 +978,8 @@ CODE
assert_equal ["A", "B", "C"], s.chars {}
}
else
- assert_warning(/deprecated/) {
+ warning = /passing a block to String#chars is deprecated/
+ assert_warning(warning) {
res = []
assert_equal s.object_id, s.chars {|x| res << x }.object_id
assert_equal("A", res[0])
@@ -767,6 +989,88 @@ CODE
end
end
+ def test_each_grapheme_cluster
+ [
+ "\u{0D 0A}",
+ "\u{20 200d}",
+ "\u{600 600}",
+ "\u{600 20}",
+ "\u{261d 1F3FB}",
+ "\u{1f600}",
+ "\u{20 308}",
+ "\u{1F477 1F3FF 200D 2640 FE0F}",
+ "\u{1F468 200D 1F393}",
+ "\u{1F46F 200D 2642 FE0F}",
+ "\u{1f469 200d 2764 fe0f 200d 1f469}",
+ ].each do |g|
+ assert_equal [g], g.each_grapheme_cluster.to_a
+ assert_equal 1, g.each_grapheme_cluster.size
+ assert_predicate g.dup.taint.each_grapheme_cluster.to_a[0], :tainted?
+ end
+
+ [
+ ["\u{a 324}", ["\u000A", "\u0324"]],
+ ["\u{d 324}", ["\u000D", "\u0324"]],
+ ["abc", ["a", "b", "c"]],
+ ].each do |str, grapheme_clusters|
+ assert_equal grapheme_clusters, str.each_grapheme_cluster.to_a
+ assert_equal grapheme_clusters.size, str.each_grapheme_cluster.size
+ str.dup.taint.each_grapheme_cluster do |g|
+ assert_predicate g, :tainted?
+ end
+ end
+
+ s = ("x"+"\u{10ABCD}"*250000)
+ assert_empty(s.each_grapheme_cluster {s.clear})
+ end
+
+ def test_grapheme_clusters
+ [
+ "\u{20 200d}",
+ "\u{600 600}",
+ "\u{600 20}",
+ "\u{261d 1F3FB}",
+ "\u{1f600}",
+ "\u{20 308}",
+ "\u{1F477 1F3FF 200D 2640 FE0F}",
+ "\u{1F468 200D 1F393}",
+ "\u{1F46F 200D 2642 FE0F}",
+ "\u{1f469 200d 2764 fe0f 200d 1f469}",
+ ].product([Encoding::UTF_8, *WIDE_ENCODINGS]) do |g, enc|
+ g = g.encode(enc)
+ assert_equal [g], g.grapheme_clusters
+ assert_predicate g.taint.grapheme_clusters[0], :tainted?
+ end
+
+ [
+ "\u{a 324}",
+ "\u{d 324}",
+ "abc",
+ ].product([Encoding::UTF_8, *WIDE_ENCODINGS]) do |g, enc|
+ g = g.encode(enc)
+ assert_equal g.chars, g.grapheme_clusters
+ end
+ assert_equal ["a", "b", "c"], "abc".b.grapheme_clusters
+
+ if ENUMERATOR_WANTARRAY
+ assert_warn(/block not used/) {
+ assert_equal ["A", "B", "C"], "ABC".grapheme_clusters {}
+ }
+ else
+ warning = /passing a block to String#grapheme_clusters is deprecated/
+ assert_warning(warning) {
+ s = "ABC".b.taint
+ res = []
+ assert_same s, s.grapheme_clusters {|x| res << x }
+ assert_equal(3, res.size)
+ assert_equal("A", res[0])
+ assert_equal("B", res[1])
+ assert_equal("C", res[2])
+ res.each {|g| assert_predicate(g, :tainted?)}
+ }
+ end
+ end
+
def test_each_line
save = $/
$/ = "\n"
@@ -777,8 +1081,13 @@ CODE
res=[]
S("hello\n\n\nworld").each_line(S('')) {|x| res << x}
- assert_equal(S("hello\n\n\n"), res[0])
- assert_equal(S("world"), res[1])
+ assert_equal(S("hello\n\n"), res[0])
+ assert_equal(S("world"), res[1])
+
+ res=[]
+ S("hello\r\n\r\nworld").each_line(S('')) {|x| res << x}
+ assert_equal(S("hello\r\n\r\n"), res[0])
+ assert_equal(S("world"), res[1])
$/ = "!"
@@ -807,6 +1116,58 @@ CODE
assert_nothing_raised(bug7646) do
"\n\u0100".each_line("\n") {}
end
+ ensure
+ $/ = save
+ end
+
+ def test_each_line_chomp
+ res = []
+ S("hello\nworld").each_line("\n", chomp: true) {|x| res << x}
+ assert_equal(S("hello"), res[0])
+ assert_equal(S("world"), res[1])
+
+ res = []
+ S("hello\n\n\nworld").each_line(S(''), chomp: true) {|x| res << x}
+ assert_equal(S("hello\n"), res[0])
+ assert_equal(S("world"), res[1])
+
+ res = []
+ S("hello\r\n\r\nworld").each_line(S(''), chomp: true) {|x| res << x}
+ assert_equal(S("hello\r\n"), res[0])
+ assert_equal(S("world"), res[1])
+
+ res = []
+ S("hello!world").each_line(S('!'), chomp: true) {|x| res << x}
+ assert_equal(S("hello"), res[0])
+ assert_equal(S("world"), res[1])
+
+ res = []
+ S("a").each_line(S('ab'), chomp: true).each {|x| res << x}
+ assert_equal(1, res.size)
+ assert_equal(S("a"), res[0])
+
+ s = nil
+ "foo\nbar".each_line(nil, chomp: true) {|s2| s = s2 }
+ assert_equal("foo\nbar", s)
+
+ assert_equal "hello", S("hello\nworld").each_line(chomp: true).next
+ assert_equal "hello\nworld", S("hello\nworld").each_line(nil, chomp: true).next
+
+ res = []
+ S("").each_line(chomp: true) {|x| res << x}
+ assert_equal([], res)
+
+ res = []
+ S("\n").each_line(chomp: true) {|x| res << x}
+ assert_equal([S("")], res)
+
+ res = []
+ S("\r\n").each_line(chomp: true) {|x| res << x}
+ assert_equal([S("")], res)
+
+ res = []
+ S("a\n b\n").each_line(" ", chomp: true) {|x| res << x}
+ assert_equal([S("a\n"), S("b\n")], res)
end
def test_lines
@@ -819,7 +1180,7 @@ CODE
assert_equal ["hello\n", "world"], s.lines {}
}
else
- assert_warning(/deprecated/) {
+ assert_warning(/passing a block to String#lines is deprecated/) {
res = []
assert_equal s.object_id, s.lines {|x| res << x }.object_id
assert_equal(S("hello\n"), res[0])
@@ -943,18 +1304,6 @@ CODE
assert_not_equal(S("sub-setter").hash, S("discover").hash, bug9172)
end
- def test_hash_random
- str = 'abc'
- a = [str.hash.to_s]
- 3.times {
- assert_in_out_err(["-e", "print #{str.dump}.hash"], "") do |r, e|
- a += r
- assert_equal([], e)
- end
- }
- assert_not_equal([str.hash.to_s], a.uniq)
- end
-
def test_hex
assert_equal(255, S("0xff").hex)
assert_equal(-255, S("-0xff").hex)
@@ -1128,10 +1477,10 @@ CODE
assert_equal(s2, s)
fs = "".freeze
- assert_raise(RuntimeError) { fs.replace("a") }
- assert_raise(RuntimeError) { fs.replace(fs) }
+ assert_raise(FrozenError) { fs.replace("a") }
+ assert_raise(FrozenError) { fs.replace(fs) }
assert_raise(ArgumentError) { fs.replace() }
- assert_raise(RuntimeError) { fs.replace(42) }
+ assert_raise(FrozenError) { fs.replace(42) }
end
def test_reverse
@@ -1181,6 +1530,9 @@ CODE
assert_nil("foo".rindex(//, -100))
assert_nil($~)
+
+ assert_equal(3, "foo".rindex(//))
+ assert_equal([3, 3], $~.offset(0))
end
def test_rjust
@@ -1223,6 +1575,8 @@ CODE
assert_nil($~)
assert_equal(3, S("hello hello hello").scan("hello".taint).count(&:tainted?))
+
+ assert_equal(%w[1 2 3], S("a1 a2 a3").scan(/a\K./))
end
def test_size
@@ -1390,8 +1744,49 @@ CODE
assert_equal([S("a"), S(""), S("b"), S("c"), S("")], S("a||b|c|").split(S('|'), -1))
assert_equal([], "".split(//, 1))
+ ensure
+ $; = fs
+ end
- assert_equal("[2, 3]", [1,2,3].slice!(1,10000).inspect, "moved from btest/knownbug")
+ def test_split_with_block
+ fs, $; = $;, nil
+ result = []; S(" a b\t c ").split {|s| result << s}
+ assert_equal([S("a"), S("b"), S("c")], result)
+ result = []; S(" a b\t c ").split(S(" ")) {|s| result << s}
+ assert_equal([S("a"), S("b"), S("c")], result)
+
+ result = []; S(" a | b | c ").split(S("|")) {|s| result << s}
+ assert_equal([S(" a "), S(" b "), S(" c ")], result)
+
+ result = []; S("aXXbXXcXX").split(/X./) {|s| result << s}
+ assert_equal([S("a"), S("b"), S("c")], result)
+
+ result = []; S("abc").split(//) {|s| result << s}
+ assert_equal([S("a"), S("b"), S("c")], result)
+
+ result = []; S("a|b|c").split(S('|'), 1) {|s| result << s}
+ assert_equal([S("a|b|c")], result)
+
+ result = []; S("a|b|c").split(S('|'), 2) {|s| result << s}
+ assert_equal([S("a"), S("b|c")], result)
+ result = []; S("a|b|c").split(S('|'), 3) {|s| result << s}
+ assert_equal([S("a"), S("b"), S("c")], result)
+
+ result = []; S("a|b|c|").split(S('|'), -1) {|s| result << s}
+ assert_equal([S("a"), S("b"), S("c"), S("")], result)
+ result = []; S("a|b|c||").split(S('|'), -1) {|s| result << s}
+ assert_equal([S("a"), S("b"), S("c"), S(""), S("")], result)
+
+ result = []; S("a||b|c|").split(S('|')) {|s| result << s}
+ assert_equal([S("a"), S(""), S("b"), S("c")], result)
+ result = []; S("a||b|c|").split(S('|'), -1) {|s| result << s}
+ assert_equal([S("a"), S(""), S("b"), S("c"), S("")], result)
+
+ result = []; "".split(//, 1) {|s| result << s}
+ assert_equal([], result)
+
+ result = []; "aaa,bbb,ccc,ddd".split(/,/) {|s| result << s.gsub(/./, "A")}
+ assert_equal(["AAA"]*4, result)
ensure
$; = fs
end
@@ -1400,6 +1795,17 @@ CODE
assert_raise_with_message(TypeError, /\$;/) {
$; = []
}
+
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ bug = '[ruby-core:79582] $; must not be GCed'
+ begin;
+ $; = " "
+ $a = nil
+ alias $; $a
+ alias $-F $a
+ GC.start
+ assert_equal([], "".split, bug)
+ end;
end
def test_split_encoding
@@ -1413,10 +1819,7 @@ CODE
def test_split_wchar
bug8642 = '[ruby-core:56036] [Bug #8642]'
- [
- Encoding::UTF_16BE, Encoding::UTF_16LE,
- Encoding::UTF_32BE, Encoding::UTF_32LE,
- ].each do |enc|
+ WIDE_ENCODINGS.each do |enc|
s = S("abc,def".encode(enc))
assert_equal(["abc", "def"].map {|c| c.encode(enc)},
s.split(",".encode(enc)),
@@ -1438,6 +1841,12 @@ CODE
}
end
+ def test_split_dupped
+ s = "abc"
+ s.split("b", 1).map(&:upcase!)
+ assert_equal("abc", s)
+ end
+
def test_squeeze
assert_equal(S("abc"), S("aaabbbbccc").squeeze)
assert_equal(S("aa bb cc"), S("aa bb cc").squeeze(S(" ")))
@@ -1470,6 +1879,11 @@ CODE
bug5536 = '[ruby-core:40623]'
assert_raise(TypeError, bug5536) {S("str").start_with? :not_convertible_to_string}
+
+ assert_equal(true, "hello".start_with?(/hel/))
+ assert_equal("hel", $&)
+ assert_equal(false, "hello".start_with?(/el/))
+ assert_nil($&)
end
def test_strip
@@ -1562,6 +1976,10 @@ CODE
assert_equal(S("Abc"), S("abc").sub("a") {m = $~; "A"})
assert_equal(S("a"), m[0])
assert_equal(/a/, m.regexp)
+ bug = '[ruby-core:78686] [Bug #13042] other than regexp has no name references'
+ assert_raise_with_message(IndexError, /oops/, bug) {
+ 'hello'.gsub('hello', '\k<oops>')
+ }
end
def test_sub!
@@ -1590,6 +2008,12 @@ CODE
r.taint
a.sub!(/./, r)
assert_predicate(a, :tainted?)
+
+ bug16105 = '[Bug #16105] heap-use-after-free'
+ a = S("ABCDEFGHIJKLMNOPQRSTUVWXYZ012345678")
+ b = a.dup
+ c = a.slice(1, 100)
+ assert_equal("AABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", b.sub!(c, b), bug16105)
end
def test_succ
@@ -1615,6 +2039,11 @@ CODE
assert_equal("!", " ".succ)
assert_equal("", "".succ)
+
+ bug = '[ruby-core:83062] [Bug #13952]'
+ s = "\xff".b
+ assert_not_predicate(s, :ascii_only?)
+ assert_predicate(s.succ, :ascii_only?, bug)
end
def test_succ!
@@ -1806,8 +2235,13 @@ CODE
assert_equal(false, "\u3041\u3042".tr("\u3041", "a").ascii_only?)
bug6156 = '[ruby-core:43335]'
+ bug13950 = '[ruby-core:83056] [Bug #13950]'
str, range, star = %w[b a-z *].map{|s|s.encode("utf-16le")}
- assert_equal(star, str.tr(range, star), bug6156)
+ result = str.tr(range, star)
+ assert_equal(star, result, bug6156)
+ assert_not_predicate(str, :ascii_only?)
+ assert_not_predicate(star, :ascii_only?)
+ assert_not_predicate(result, :ascii_only?, bug13950)
end
def test_tr!
@@ -1995,7 +2429,7 @@ CODE
end
def test_frozen_check
- assert_raise(RuntimeError) {
+ assert_raise(FrozenError) {
s = ""
s.sub!(/\A/) { s.freeze; "zzz" }
}
@@ -2087,6 +2521,50 @@ CODE
assert_raise(ArgumentError) { "foo".match }
end
+ def test_match_p_regexp
+ /backref/ =~ 'backref'
+ # must match here, but not in a separate method, e.g., assert_send,
+ # to check if $~ is affected or not.
+ assert_equal(true, "".match?(//))
+ assert_equal(true, :abc.match?(/.../))
+ assert_equal(true, 'abc'.match?(/b/))
+ assert_equal(true, 'abc'.match?(/b/, 1))
+ assert_equal(true, 'abc'.match?(/../, 1))
+ assert_equal(true, 'abc'.match?(/../, -2))
+ assert_equal(false, 'abc'.match?(/../, -4))
+ assert_equal(false, 'abc'.match?(/../, 4))
+ assert_equal(true, "\u3042xx".match?(/../, 1))
+ assert_equal(false, "\u3042x".match?(/../, 1))
+ assert_equal(true, ''.match?(/\z/))
+ assert_equal(true, 'abc'.match?(/\z/))
+ assert_equal(true, 'Ruby'.match?(/R.../))
+ assert_equal(false, 'Ruby'.match?(/R.../, 1))
+ assert_equal(false, 'Ruby'.match?(/P.../))
+ assert_equal('backref', $&)
+ end
+
+ def test_match_p_string
+ /backref/ =~ 'backref'
+ # must match here, but not in a separate method, e.g., assert_send,
+ # to check if $~ is affected or not.
+ assert_equal(true, "".match?(''))
+ assert_equal(true, :abc.match?('...'))
+ assert_equal(true, 'abc'.match?('b'))
+ assert_equal(true, 'abc'.match?('b', 1))
+ assert_equal(true, 'abc'.match?('..', 1))
+ assert_equal(true, 'abc'.match?('..', -2))
+ assert_equal(false, 'abc'.match?('..', -4))
+ assert_equal(false, 'abc'.match?('..', 4))
+ assert_equal(true, "\u3042xx".match?('..', 1))
+ assert_equal(false, "\u3042x".match?('..', 1))
+ assert_equal(true, ''.match?('\z'))
+ assert_equal(true, 'abc'.match?('\z'))
+ assert_equal(true, 'Ruby'.match?('R...'))
+ assert_equal(false, 'Ruby'.match?('R...', 1))
+ assert_equal(false, 'Ruby'.match?('P...'))
+ assert_equal('backref', $&)
+ end
+
def test_clear
s = "foo" * 100
s.clear
@@ -2124,7 +2602,12 @@ CODE
end
assert_equal(["\u30E6\u30FC\u30B6", "@", "\u30C9\u30E1.\u30A4\u30F3"],
- "\u30E6\u30FC\u30B6@\u30C9\u30E1.\u30A4\u30F3".partition(/[@.]/))
+ "\u30E6\u30FC\u30B6@\u30C9\u30E1.\u30A4\u30F3".partition(/[@.]/))
+
+ bug = '[ruby-core:82911]'
+ hello = "hello"
+ hello.partition("hi").map(&:upcase!)
+ assert_equal("hello", hello, bug)
end
def test_rpartition
@@ -2144,6 +2627,11 @@ CODE
bug8138 = '[ruby-dev:47183]'
assert_equal(["\u30E6\u30FC\u30B6@\u30C9\u30E1", ".", "\u30A4\u30F3"],
"\u30E6\u30FC\u30B6@\u30C9\u30E1.\u30A4\u30F3".rpartition(/[@.]/), bug8138)
+
+ bug = '[ruby-core:82911]'
+ hello = "hello"
+ hello.rpartition("hi").map(&:upcase!)
+ assert_equal("hello", hello, bug)
end
def test_setter
@@ -2207,7 +2695,31 @@ CODE
=end
def test_casecmp
+ assert_equal(0, "FoO".casecmp("fOO"))
+ assert_equal(1, "FoO".casecmp("BaR"))
+ assert_equal(-1, "baR".casecmp("FoO"))
assert_equal(1, "\u3042B".casecmp("\u3042a"))
+
+ assert_nil("foo".casecmp(:foo))
+ assert_nil("foo".casecmp(Object.new))
+
+ o = Object.new
+ def o.to_str; "fOO"; end
+ assert_equal(0, "FoO".casecmp(o))
+ end
+
+ def test_casecmp?
+ assert_equal(true, 'FoO'.casecmp?('fOO'))
+ assert_equal(false, 'FoO'.casecmp?('BaR'))
+ assert_equal(false, 'baR'.casecmp?('FoO'))
+ assert_equal(true, 'äöü'.casecmp?('ÄÖÜ'))
+
+ assert_nil("foo".casecmp?(:foo))
+ assert_nil("foo".casecmp?(Object.new))
+
+ o = Object.new
+ def o.to_str; "fOO"; end
+ assert_equal(true, "FoO".casecmp?(o))
end
def test_upcase2
@@ -2267,6 +2779,222 @@ CODE
assert_equal("\u3042", s4)
end
+ def test_delete_prefix
+ assert_raise(TypeError) { 'hello'.delete_prefix(nil) }
+ assert_raise(TypeError) { 'hello'.delete_prefix(1) }
+ assert_raise(TypeError) { 'hello'.delete_prefix(/hel/) }
+
+ s = S("hello")
+ assert_equal("lo", s.delete_prefix('hel'))
+ assert_equal("hello", s)
+
+ s = S("hello")
+ assert_equal("hello", s.delete_prefix('lo'))
+ assert_equal("hello", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{306b 3061 306f}", s.delete_prefix("\u{3053 3093}"))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{3053 3093 306b 3061 306f}", s.delete_prefix('hel'))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("hello")
+ assert_equal("hello", s.delete_prefix("\u{3053 3093}"))
+ assert_equal("hello", s)
+
+ # skip if argument is a broken string
+ s = S("\xe3\x81\x82")
+ assert_equal("\xe3\x81\x82", s.delete_prefix("\xe3"))
+ assert_equal("\xe3\x81\x82", s)
+
+ s = S("\x95\x5c").force_encoding("Shift_JIS")
+ assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s.delete_prefix("\x95"))
+ assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s)
+
+ # clear coderange
+ s = S("\u{3053 3093}hello")
+ assert_not_predicate(s, :ascii_only?)
+ assert_predicate(s.delete_prefix("\u{3053 3093}"), :ascii_only?)
+
+ # argument should be converted to String
+ klass = Class.new { def to_str; 'a'; end }
+ s = S("abba")
+ assert_equal("bba", s.delete_prefix(klass.new))
+ assert_equal("abba", s)
+ end
+
+ def test_delete_prefix_bang
+ assert_raise(TypeError) { 'hello'.delete_prefix!(nil) }
+ assert_raise(TypeError) { 'hello'.delete_prefix!(1) }
+ assert_raise(TypeError) { 'hello'.delete_prefix!(/hel/) }
+
+ s = S("hello")
+ assert_equal("lo", s.delete_prefix!('hel'))
+ assert_equal("lo", s)
+
+ s = S("hello")
+ assert_equal(nil, s.delete_prefix!('lo'))
+ assert_equal("hello", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{306b 3061 306f}", s.delete_prefix!("\u{3053 3093}"))
+ assert_equal("\u{306b 3061 306f}", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal(nil, s.delete_prefix!('hel'))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("hello")
+ assert_equal(nil, s.delete_prefix!("\u{3053 3093}"))
+ assert_equal("hello", s)
+
+ # skip if argument is a broken string
+ s = S("\xe3\x81\x82")
+ assert_equal(nil, s.delete_prefix!("\xe3"))
+ assert_equal("\xe3\x81\x82", s)
+
+ # clear coderange
+ s = S("\u{3053 3093}hello")
+ assert_not_predicate(s, :ascii_only?)
+ assert_predicate(s.delete_prefix!("\u{3053 3093}"), :ascii_only?)
+
+ # argument should be converted to String
+ klass = Class.new { def to_str; 'a'; end }
+ s = S("abba")
+ assert_equal("bba", s.delete_prefix!(klass.new))
+ assert_equal("bba", s)
+
+ s = S("ax").freeze
+ assert_raise_with_message(FrozenError, /frozen/) {s.delete_prefix!("a")}
+
+ s = S("ax")
+ o = Struct.new(:s).new(s)
+ def o.to_str
+ s.freeze
+ "a"
+ end
+ assert_raise_with_message(FrozenError, /frozen/) {s.delete_prefix!(o)}
+ end
+
+ def test_delete_suffix
+ assert_raise(TypeError) { 'hello'.delete_suffix(nil) }
+ assert_raise(TypeError) { 'hello'.delete_suffix(1) }
+ assert_raise(TypeError) { 'hello'.delete_suffix(/hel/) }
+
+ s = S("hello")
+ assert_equal("hel", s.delete_suffix('lo'))
+ assert_equal("hello", s)
+
+ s = S("hello")
+ assert_equal("hello", s.delete_suffix('he'))
+ assert_equal("hello", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{3053 3093 306b}", s.delete_suffix("\u{3061 306f}"))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{3053 3093 306b 3061 306f}", s.delete_suffix('lo'))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("hello")
+ assert_equal("hello", s.delete_suffix("\u{3061 306f}"))
+ assert_equal("hello", s)
+
+ # skip if argument is a broken string
+ s = S("\xe3\x81\x82")
+ assert_equal("\xe3\x81\x82", s.delete_suffix("\x82"))
+ assert_equal("\xe3\x81\x82", s)
+
+ # clear coderange
+ s = S("hello\u{3053 3093}")
+ assert_not_predicate(s, :ascii_only?)
+ assert_predicate(s.delete_suffix("\u{3053 3093}"), :ascii_only?)
+
+ # argument should be converted to String
+ klass = Class.new { def to_str; 'a'; end }
+ s = S("abba")
+ assert_equal("abb", s.delete_suffix(klass.new))
+ assert_equal("abba", s)
+
+ # chomp removes any of "\n", "\r\n", "\r" when "\n" is specified,
+ # but delete_suffix does not
+ s = "foo\n"
+ assert_equal("foo", s.delete_suffix("\n"))
+ s = "foo\r\n"
+ assert_equal("foo\r", s.delete_suffix("\n"))
+ s = "foo\r"
+ assert_equal("foo\r", s.delete_suffix("\n"))
+ end
+
+ def test_delete_suffix_bang
+ assert_raise(TypeError) { 'hello'.delete_suffix!(nil) }
+ assert_raise(TypeError) { 'hello'.delete_suffix!(1) }
+ assert_raise(TypeError) { 'hello'.delete_suffix!(/hel/) }
+
+ s = S("hello").freeze
+ assert_raise_with_message(FrozenError, /frozen/) {s.delete_suffix!('lo')}
+
+ s = S("ax")
+ o = Struct.new(:s).new(s)
+ def o.to_str
+ s.freeze
+ "x"
+ end
+ assert_raise_with_message(FrozenError, /frozen/) {s.delete_suffix!(o)}
+
+ s = S("hello")
+ assert_equal("hel", s.delete_suffix!('lo'))
+ assert_equal("hel", s)
+
+ s = S("hello")
+ assert_equal(nil, s.delete_suffix!('he'))
+ assert_equal("hello", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{3053 3093 306b}", s.delete_suffix!("\u{3061 306f}"))
+ assert_equal("\u{3053 3093 306b}", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal(nil, s.delete_suffix!('lo'))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("hello")
+ assert_equal(nil, s.delete_suffix!("\u{3061 306f}"))
+ assert_equal("hello", s)
+
+ # skip if argument is a broken string
+ s = S("\xe3\x81\x82")
+ assert_equal(nil, s.delete_suffix!("\x82"))
+ assert_equal("\xe3\x81\x82", s)
+
+ s = S("\x95\x5c").force_encoding("Shift_JIS")
+ assert_equal(nil, s.delete_suffix!("\x5c"))
+ assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s)
+
+ # clear coderange
+ s = S("hello\u{3053 3093}")
+ assert_not_predicate(s, :ascii_only?)
+ assert_predicate(s.delete_suffix!("\u{3053 3093}"), :ascii_only?)
+
+ # argument should be converted to String
+ klass = Class.new { def to_str; 'a'; end }
+ s = S("abba")
+ assert_equal("abb", s.delete_suffix!(klass.new))
+ assert_equal("abb", s)
+
+ # chomp removes any of "\n", "\r\n", "\r" when "\n" is specified,
+ # but delete_suffix does not
+ s = "foo\n"
+ assert_equal("foo", s.delete_suffix!("\n"))
+ s = "foo\r\n"
+ assert_equal("foo\r", s.delete_suffix!("\n"))
+ s = "foo\r"
+ assert_equal(nil, s.delete_suffix!("\n"))
+ end
+
=begin
def test_symbol_table_overflow
assert_in_out_err([], <<-INPUT, [], /symbol table overflow \(symbol [a-z]{8}\) \(RuntimeError\)/)
@@ -2275,6 +3003,23 @@ CODE
end
=end
+ def test_nesting_shared
+ a = ('a' * 24).encode(Encoding::ASCII).gsub('x', '')
+ hash = {}
+ hash[a] = true
+ assert_equal(('a' * 24), a)
+ 4.times { GC.start }
+ assert_equal(('a' * 24), a, '[Bug #15792]')
+ end
+
+ def test_nesting_shared_b
+ a = ('j' * 24).b.b
+ eval('', binding, a)
+ assert_equal(('j' * 24), a)
+ 4.times { GC.start }
+ assert_equal(('j' * 24), a, '[Bug #15934]')
+ end
+
def test_shared_force_encoding
s = "\u{3066}\u{3059}\u{3068}".gsub(//, '')
h = {}
@@ -2290,8 +3035,7 @@ CODE
def test_ascii_incomat_inspect
bug4081 = '[ruby-core:33283]'
- [Encoding::UTF_16LE, Encoding::UTF_16BE,
- Encoding::UTF_32LE, Encoding::UTF_32BE].each do |e|
+ WIDE_ENCODINGS.each do |e|
assert_equal('"abc"', "abc".encode(e).inspect)
assert_equal('"\\u3042\\u3044\\u3046"', "\u3042\u3044\u3046".encode(e).inspect)
assert_equal('"ab\\"c"', "ab\"c".encode(e).inspect, bug4081)
@@ -2419,20 +3163,23 @@ CODE
def test_uplus_minus
str = "foo"
- assert_equal(false, str.frozen?)
- assert_equal(false, (+str).frozen?)
- assert_equal(true, (-str).frozen?)
+ assert_not_predicate(str, :frozen?)
+ assert_not_predicate(+str, :frozen?)
+ assert_predicate(-str, :frozen?)
- assert_equal(str.object_id, (+str).object_id)
- assert_not_equal(str.object_id, (-str).object_id)
+ assert_same(str, +str)
+ assert_not_same(str, -str)
str = "bar".freeze
- assert_equal(true, str.frozen?)
- assert_equal(false, (+str).frozen?)
- assert_equal(true, (-str).frozen?)
+ assert_predicate(str, :frozen?)
+ assert_not_predicate(+str, :frozen?)
+ assert_predicate(-str, :frozen?)
+
+ assert_not_same(str, +str)
+ assert_same(str, -str)
- assert_not_equal(str.object_id, (+str).object_id)
- assert_equal(str.object_id, (-str).object_id)
+ bar = %w(b a r).join('')
+ assert_same(str, -bar, "uminus deduplicates [Feature #13077]")
end
def test_ord
@@ -2448,6 +3195,12 @@ CODE
assert_equal("\u3042", "\u3042\u3043".chr)
assert_equal('', ''.chr)
end
+
+ def test_substr_code_range
+ data = "\xff" + "a"*200
+ assert_not_predicate(data, :valid_encoding?)
+ assert_predicate(data[100..-1], :valid_encoding?)
+ end
end
class TestString2 < TestString
diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb
index 9047e81bbe..0046e9bd04 100644
--- a/test/ruby/test_struct.rb
+++ b/test/ruby/test_struct.rb
@@ -92,6 +92,29 @@ module TestStruct
assert_equal([:utime, :stime, :cutime, :cstime], Process.times.members)
end
+ def test_struct_new_with_empty_hash
+ assert_equal({:a=>1}, Struct.new(:a, {}).new({:a=>1}).a)
+ end
+
+ def test_struct_new_with_keyword_init
+ @Struct.new("KeywordInitTrue", :a, :b, keyword_init: true)
+ @Struct.new("KeywordInitFalse", :a, :b, keyword_init: false)
+
+ assert_raise(ArgumentError) { @Struct::KeywordInitTrue.new(1, 2) }
+ assert_nothing_raised { @Struct::KeywordInitFalse.new(1, 2) }
+ assert_nothing_raised { @Struct::KeywordInitTrue.new(a: 1, b: 2) }
+ assert_raise(ArgumentError) { @Struct::KeywordInitTrue.new(1, b: 2) }
+ assert_raise(ArgumentError) { @Struct::KeywordInitTrue.new(a: 1, b: 2, c: 3) }
+ assert_equal @Struct::KeywordInitTrue.new(a: 1, b: 2).values, @Struct::KeywordInitFalse.new(1, 2).values
+ assert_equal "#{@Struct}::KeywordInitFalse", @Struct::KeywordInitFalse.inspect
+ assert_equal "#{@Struct}::KeywordInitTrue(keyword_init: true)", @Struct::KeywordInitTrue.inspect
+
+ @Struct.instance_eval do
+ remove_const(:KeywordInitTrue)
+ remove_const(:KeywordInitFalse)
+ end
+ end
+
def test_initialize
klass = @Struct.new(:a)
assert_raise(ArgumentError) { klass.new(1, 2) }
@@ -147,7 +170,7 @@ module TestStruct
assert_equal("#<struct :@a=3>", o.inspect)
methods = klass.instance_methods(false)
- assert_equal([:@a, :"@a="].inspect, methods.inspect, '[Bug #8756]')
+ assert_equal([:@a, :"@a="].sort.inspect, methods.sort.inspect, '[Bug #8756]')
assert_include(methods, :@a)
assert_include(methods, :"@a=")
end
@@ -193,6 +216,13 @@ module TestStruct
assert_raise(ArgumentError) { o.select(1) }
end
+ def test_filter
+ klass = @Struct.new(:a, :b, :c, :d, :e, :f)
+ o = klass.new(1, 2, 3, 4, 5, 6)
+ assert_equal([1, 3, 5], o.filter {|v| v % 2 != 0 })
+ assert_raise(ArgumentError) { o.filter(1) }
+ end
+
def test_big_struct
klass1 = @Struct.new(*('a'..'z').map(&:to_sym))
o = klass1.new
@@ -336,6 +366,13 @@ module TestStruct
assert_equal({a:1, b:2, c:3, d:4, e:5, f:6}, o.to_h)
end
+ def test_to_h_block
+ klass = @Struct.new(:a, :b, :c, :d, :e, :f)
+ o = klass.new(1, 2, 3, 4, 5, 6)
+ assert_equal({"a" => 1, "b" => 4, "c" => 9, "d" => 16, "e" => 25, "f" => 36},
+ o.to_h {|k, v| [k.to_s, v*v]})
+ end
+
def test_question_mark_in_member
klass = @Struct.new(:a, :b?)
x = Object.new
diff --git a/test/ruby/test_super.rb b/test/ruby/test_super.rb
index 9691116fb4..bb78ab516f 100644
--- a/test/ruby/test_super.rb
+++ b/test/ruby/test_super.rb
@@ -102,11 +102,11 @@ class TestSuper < Test::Unit::TestCase
def test_optional2
assert_raise(ArgumentError) do
# call Base#optional with 2 arguments; the 2nd arg is supplied
- assert_equal(9, Optional2.new.optional(9))
+ Optional2.new.optional(9)
end
assert_raise(ArgumentError) do
# call Base#optional with 2 arguments
- assert_equal(9, Optional2.new.optional(9, 2))
+ Optional2.new.optional(9, 2)
end
end
def test_optional3
@@ -544,4 +544,20 @@ class TestSuper < Test::Unit::TestCase
c.new
}
end
+
+ class TestFor_super_with_modified_rest_parameter_base
+ def foo *args
+ args
+ end
+ end
+
+ class TestFor_super_with_modified_rest_parameter < TestFor_super_with_modified_rest_parameter_base
+ def foo *args
+ args = 13
+ super
+ end
+ end
+ def test_super_with_modified_rest_parameter
+ assert_equal [13], TestFor_super_with_modified_rest_parameter.new.foo
+ end
end
diff --git a/test/ruby/test_symbol.rb b/test/ruby/test_symbol.rb
index d8c91c1eea..13d7cc9d57 100644
--- a/test/ruby/test_symbol.rb
+++ b/test/ruby/test_symbol.rb
@@ -121,21 +121,24 @@ class TestSymbol < Test::Unit::TestCase
end
def test_to_proc_yield
- assert_ruby_status([], <<-"end;", timeout: 5.0)
+ assert_ruby_status([], "#{<<-"begin;"}\n#{<<-"end;"}", timeout: 5.0)
+ begin;
GC.stress = true
true.tap(&:itself)
end;
end
def test_to_proc_new_proc
- assert_ruby_status([], <<-"end;", timeout: 5.0)
+ assert_ruby_status([], "#{<<-"begin;"}\n#{<<-"end;"}", timeout: 5.0)
+ begin;
GC.stress = true
2.times {Proc.new(&:itself)}
end;
end
def test_to_proc_no_method
- assert_separately([], <<-"end;", timeout: 5.0)
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}", timeout: 5.0)
+ begin;
bug11566 = '[ruby-core:70980] [Bug #11566]'
assert_raise(NoMethodError, bug11566) {Proc.new(&:foo).(1)}
assert_raise(NoMethodError, bug11566) {:foo.to_proc.(1)}
@@ -143,7 +146,8 @@ class TestSymbol < Test::Unit::TestCase
end
def test_to_proc_arg
- assert_separately([], <<-"end;", timeout: 5.0)
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}", timeout: 5.0)
+ begin;
def (obj = Object.new).proc(&b) b; end
assert_same(:itself.to_proc, obj.proc(&:itself))
end;
@@ -157,6 +161,42 @@ class TestSymbol < Test::Unit::TestCase
assert_equal(1, first, bug11594)
end
+ class TestToPRocArgWithRefinements; end
+ def _test_to_proc_arg_with_refinements_call(&block)
+ block.call TestToPRocArgWithRefinements.new
+ end
+ using Module.new {
+ refine TestToPRocArgWithRefinements do
+ def hoge
+ :hoge
+ end
+ end
+ }
+ def test_to_proc_arg_with_refinements
+ assert_equal(:hoge, _test_to_proc_arg_with_refinements_call(&:hoge))
+ end
+
+ def self._test_to_proc_arg_with_refinements_call(&block)
+ block.call TestToPRocArgWithRefinements.new
+ end
+ _test_to_proc_arg_with_refinements_call(&:hoge)
+ using Module.new {
+ refine TestToPRocArgWithRefinements do
+ def hoge
+ :hogehoge
+ end
+ end
+ }
+ def test_to_proc_arg_with_refinements_override
+ assert_equal(:hogehoge, _test_to_proc_arg_with_refinements_call(&:hoge))
+ end
+
+ def test_to_proc_arg_with_refinements_undefined
+ assert_raise(NoMethodError) do
+ _test_to_proc_arg_with_refinements_call(&:foo)
+ end
+ end
+
private def return_from_proc
Proc.new { return 1 }.tap(&:call)
end
@@ -168,13 +208,15 @@ class TestSymbol < Test::Unit::TestCase
def test_to_proc_for_hash_each
bug11830 = '[ruby-core:72205] [Bug #11830]'
- assert_normal_exit(<<-'end;', bug11830) # do
+ assert_normal_exit("#{<<-"begin;"}\n#{<<-'end;'}", bug11830)
+ begin;
{}.each(&:destroy)
end;
end
def test_to_proc_iseq
- assert_separately([], <<~"end;", timeout: 1) # do
+ assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}", timeout: 5)
+ begin;
bug11845 = '[ruby-core:72381] [Bug #11845]'
assert_nil(:class.to_proc.source_location, bug11845)
assert_equal([[:rest]], :class.to_proc.parameters, bug11845)
@@ -186,7 +228,8 @@ class TestSymbol < Test::Unit::TestCase
end
def test_to_proc_binding
- assert_separately([], <<~"end;", timeout: 1) # do
+ assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}", timeout: 5)
+ begin;
bug12137 = '[ruby-core:74100] [Bug #12137]'
assert_raise(ArgumentError, bug12137) {
:succ.to_proc.binding
@@ -194,6 +237,12 @@ class TestSymbol < Test::Unit::TestCase
end;
end
+ def test_to_proc_instance_exec
+ bug = '[ruby-core:78839] [Bug #13074] should evaluate on the argument'
+ assert_equal(2, BasicObject.new.instance_exec(1, &:succ), bug)
+ assert_equal(3, BasicObject.new.instance_exec(1, 2, &:+), bug)
+ end
+
def test_call
o = Object.new
def o.foo(x, y); x + y; end
@@ -273,7 +322,19 @@ class TestSymbol < Test::Unit::TestCase
assert_equal(0, :FoO.casecmp(:fOO))
assert_equal(1, :FoO.casecmp(:BaR))
assert_equal(-1, :baR.casecmp(:FoO))
+
assert_nil(:foo.casecmp("foo"))
+ assert_nil(:foo.casecmp(Object.new))
+ end
+
+ def test_casecmp?
+ assert_equal(true, :FoO.casecmp?(:fOO))
+ assert_equal(false, :FoO.casecmp?(:BaR))
+ assert_equal(false, :baR.casecmp?(:FoO))
+ assert_equal(true, :äöü.casecmp?(:ÄÖÜ))
+
+ assert_nil(:foo.casecmp?("foo"))
+ assert_nil(:foo.casecmp?(Object.new))
end
def test_length
@@ -317,7 +378,49 @@ class TestSymbol < Test::Unit::TestCase
assert_raise(ArgumentError) { :"foo".match }
end
- def test_symbol_poped
+ def test_match_p_regexp
+ /backref/ =~ 'backref'
+ # must match here, but not in a separate method, e.g., assert_send,
+ # to check if $~ is affected or not.
+ assert_equal(true, "".match?(//))
+ assert_equal(true, :abc.match?(/.../))
+ assert_equal(true, 'abc'.match?(/b/))
+ assert_equal(true, 'abc'.match?(/b/, 1))
+ assert_equal(true, 'abc'.match?(/../, 1))
+ assert_equal(true, 'abc'.match?(/../, -2))
+ assert_equal(false, 'abc'.match?(/../, -4))
+ assert_equal(false, 'abc'.match?(/../, 4))
+ assert_equal(true, ("\u3042" + '\x').match?(/../, 1))
+ assert_equal(true, ''.match?(/\z/))
+ assert_equal(true, 'abc'.match?(/\z/))
+ assert_equal(true, 'Ruby'.match?(/R.../))
+ assert_equal(false, 'Ruby'.match?(/R.../, 1))
+ assert_equal(false, 'Ruby'.match?(/P.../))
+ assert_equal('backref', $&)
+ end
+
+ def test_match_p_string
+ /backref/ =~ 'backref'
+ # must match here, but not in a separate method, e.g., assert_send,
+ # to check if $~ is affected or not.
+ assert_equal(true, "".match?(''))
+ assert_equal(true, :abc.match?('...'))
+ assert_equal(true, 'abc'.match?('b'))
+ assert_equal(true, 'abc'.match?('b', 1))
+ assert_equal(true, 'abc'.match?('..', 1))
+ assert_equal(true, 'abc'.match?('..', -2))
+ assert_equal(false, 'abc'.match?('..', -4))
+ assert_equal(false, 'abc'.match?('..', 4))
+ assert_equal(true, ("\u3042" + '\x').match?('..', 1))
+ assert_equal(true, ''.match?('\z'))
+ assert_equal(true, 'abc'.match?('\z'))
+ assert_equal(true, 'Ruby'.match?('R...'))
+ assert_equal(false, 'Ruby'.match?('R...', 1))
+ assert_equal(false, 'Ruby'.match?('P...'))
+ assert_equal('backref', $&)
+ end
+
+ def test_symbol_popped
assert_nothing_raised { eval('a = 1; :"#{ a }"; 1') }
end
@@ -404,13 +507,15 @@ class TestSymbol < Test::Unit::TestCase
def test_symbol_fstr_leak
bug10686 = '[ruby-core:67268] [Bug #10686]'
x = x = 0
- assert_no_memory_leak([], '200_000.times { |i| i.to_s.to_sym }; GC.start', <<-"end;", bug10686, limit: 1.71, rss: true)
+ assert_no_memory_leak([], '200_000.times { |i| i.to_s.to_sym }; GC.start', "#{<<-"begin;"}\n#{<<-"end;"}", bug10686, limit: 1.71, rss: true, timeout: 20)
+ begin;
200_000.times { |i| (i + 200_000).to_s.to_sym }
end;
end
def test_hash_redefinition
- assert_separately([], <<-'end;')
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
bug11035 = '[ruby-core:68767] [Bug #11035]'
class Symbol
def hash
@@ -435,4 +540,27 @@ class TestSymbol < Test::Unit::TestCase
assert_equal str, str.to_sym.to_s
assert_not_predicate(str, :frozen?, bug11721)
end
+
+ def test_hash_nondeterministic
+ ruby = EnvUtil.rubybin
+ assert_not_equal :foo.hash, `#{ruby} -e 'puts :foo.hash'`.to_i,
+ '[ruby-core:80430] [Bug #13376]'
+
+ sym = "dynsym_#{Random.rand(10000)}_#{Time.now}"
+ assert_not_equal sym.to_sym.hash,
+ `#{ruby} -e 'puts #{sym.inspect}.to_sym.hash'`.to_i
+ end
+
+ def test_eq_can_be_redefined
+ assert_in_out_err([], <<-RUBY, ["foo"], [])
+ class Symbol
+ remove_method :==
+ def ==(obj)
+ "foo"
+ end
+ end
+
+ puts :a == :a
+ RUBY
+ end
end
diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb
index ad1f0d8981..ab462bddf4 100644
--- a/test/ruby/test_syntax.rb
+++ b/test/ruby/test_syntax.rb
@@ -46,7 +46,7 @@ class TestSyntax < Test::Unit::TestCase
assert_raise(ArgumentError, enc.name) {load(f.path)}
end
ensure
- f.close! if f
+ f&.close!
end
def test_script_lines
@@ -63,7 +63,7 @@ class TestSyntax < Test::Unit::TestCase
end
end
ensure
- f.close! if f
+ f&.close!
end
def test_newline_in_block_parameters
@@ -169,10 +169,21 @@ class TestSyntax < Test::Unit::TestCase
end
def test_keyword_empty_splat
- assert_separately([], <<-'end;')
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
bug10719 = '[ruby-core:67446] [Bug #10719]'
assert_valid_syntax("foo(a: 1, **{})", bug10719)
end;
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ bug13756 = '[ruby-core:82113] [Bug #13756]'
+ assert_valid_syntax("defined? foo(**{})", bug13756)
+ end;
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ bug15271 = '[ruby-core:89648] [Bug #15271]'
+ assert_valid_syntax("a **{}", bug15271)
+ end;
end
def test_keyword_self_reference
@@ -395,21 +406,30 @@ WARN
def test_duplicated_arg
assert_syntax_error("def foo(a, a) end", /duplicated argument name/)
assert_valid_syntax("def foo(_, _) end")
+ (obj = Object.new).instance_eval("def foo(_, x, _) x end")
+ assert_equal(2, obj.foo(1, 2, 3))
end
def test_duplicated_rest
assert_syntax_error("def foo(a, *a) end", /duplicated argument name/)
assert_valid_syntax("def foo(_, *_) end")
+ (obj = Object.new).instance_eval("def foo(_, x, *_) x end")
+ assert_equal(2, obj.foo(1, 2, 3))
end
def test_duplicated_opt
assert_syntax_error("def foo(a, a=1) end", /duplicated argument name/)
assert_valid_syntax("def foo(_, _=1) end")
+ (obj = Object.new).instance_eval("def foo(_, x, _=42) x end")
+ assert_equal(2, obj.foo(1, 2))
end
def test_duplicated_opt_rest
assert_syntax_error("def foo(a=1, *a) end", /duplicated argument name/)
assert_valid_syntax("def foo(_=1, *_) end")
+ (obj = Object.new).instance_eval("def foo(_, x=42, *_) x end")
+ assert_equal(42, obj.foo(1))
+ assert_equal(2, obj.foo(1, 2))
end
def test_duplicated_rest_opt
@@ -418,41 +438,80 @@ WARN
def test_duplicated_rest_post
assert_syntax_error("def foo(*a, a) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(*_, _) end")
+ (obj = Object.new).instance_eval("def foo(*_, x, _) x end")
+ assert_equal(2, obj.foo(1, 2, 3))
+ assert_equal(2, obj.foo(2, 3))
+ (obj = Object.new).instance_eval("def foo(*_, _, x) x end")
+ assert_equal(3, obj.foo(1, 2, 3))
+ assert_equal(3, obj.foo(2, 3))
end
def test_duplicated_opt_post
assert_syntax_error("def foo(a=1, a) end", /duplicated argument name/)
assert_valid_syntax("def foo(_=1, _) end")
+ (obj = Object.new).instance_eval("def foo(_=1, x, _) x end")
+ assert_equal(2, obj.foo(1, 2, 3))
+ assert_equal(2, obj.foo(2, 3))
+ (obj = Object.new).instance_eval("def foo(_=1, _, x) x end")
+ assert_equal(3, obj.foo(1, 2, 3))
+ assert_equal(3, obj.foo(2, 3))
end
def test_duplicated_kw
assert_syntax_error("def foo(a, a: 1) end", /duplicated argument name/)
assert_valid_syntax("def foo(_, _: 1) end")
+ (obj = Object.new).instance_eval("def foo(_, x, _: 1) x end")
+ assert_equal(3, obj.foo(2, 3))
+ assert_equal(3, obj.foo(2, 3, _: 42))
+ (obj = Object.new).instance_eval("def foo(x, _, _: 1) x end")
+ assert_equal(2, obj.foo(2, 3))
+ assert_equal(2, obj.foo(2, 3, _: 42))
end
def test_duplicated_rest_kw
assert_syntax_error("def foo(*a, a: 1) end", /duplicated argument name/)
assert_nothing_raised {def foo(*_, _: 1) end}
+ (obj = Object.new).instance_eval("def foo(*_, x: 42, _: 1) x end")
+ assert_equal(42, obj.foo(42))
+ assert_equal(42, obj.foo(2, _: 0))
+ assert_equal(2, obj.foo(x: 2, _: 0))
end
def test_duplicated_opt_kw
assert_syntax_error("def foo(a=1, a: 1) end", /duplicated argument name/)
assert_valid_syntax("def foo(_=1, _: 1) end")
+ (obj = Object.new).instance_eval("def foo(_=42, x, _: 1) x end")
+ assert_equal(0, obj.foo(0))
+ assert_equal(0, obj.foo(0, _: 3))
end
def test_duplicated_kw_kwrest
assert_syntax_error("def foo(a: 1, **a) end", /duplicated argument name/)
assert_valid_syntax("def foo(_: 1, **_) end")
+ (obj = Object.new).instance_eval("def foo(_: 1, x: 42, **_) x end")
+ assert_equal(42, obj.foo())
+ assert_equal(42, obj.foo(a: 0))
+ assert_equal(42, obj.foo(_: 0, a: 0))
+ assert_equal(1, obj.foo(_: 0, x: 1, a: 0))
end
def test_duplicated_rest_kwrest
assert_syntax_error("def foo(*a, **a) end", /duplicated argument name/)
assert_valid_syntax("def foo(*_, **_) end")
+ (obj = Object.new).instance_eval("def foo(*_, x, **_) x end")
+ assert_equal(1, obj.foo(1))
+ assert_equal(1, obj.foo(1, a: 0))
+ assert_equal(2, obj.foo(1, 2, a: 0))
end
def test_duplicated_opt_kwrest
assert_syntax_error("def foo(a=1, **a) end", /duplicated argument name/)
assert_valid_syntax("def foo(_=1, **_) end")
+ (obj = Object.new).instance_eval("def foo(_=42, x, **_) x end")
+ assert_equal(1, obj.foo(1))
+ assert_equal(1, obj.foo(1, a: 0))
+ assert_equal(1, obj.foo(0, 1, a: 0))
end
def test_duplicated_when
@@ -478,8 +537,16 @@ WARN
}
end
+ def test_invalid_break
+ assert_syntax_error("def m; break; end", /Invalid break/)
+ assert_syntax_error('/#{break}/', /Invalid break/)
+ assert_syntax_error('/#{break}/o', /Invalid break/)
+ end
+
def test_invalid_next
assert_syntax_error("def m; next; end", /Invalid next/)
+ assert_syntax_error('/#{next}/', /Invalid next/)
+ assert_syntax_error('/#{next}/o', /Invalid next/)
end
def test_lambda_with_space
@@ -645,6 +712,38 @@ e"
end
end
+ def test_dedented_heredoc_expr_at_beginning
+ result = " a\n" \
+ '#{1}'"\n"
+ expected = " a\n" \
+ '#{1}'"\n"
+ assert_dedented_heredoc(expected, result)
+ end
+
+ def test_dedented_heredoc_expr_string
+ result = ' one#{" two "}'"\n"
+ expected = 'one#{" two "}'"\n"
+ assert_dedented_heredoc(expected, result)
+ end
+
+ def test_dedented_heredoc_continued_line
+ result = " 1\\\n" " 2\n"
+ expected = "1\\\n" "2\n"
+ assert_dedented_heredoc(expected, result)
+ assert_syntax_error("#{<<~"begin;"}\n#{<<~'end;'}", /can't find string "TEXT"/)
+ begin;
+ <<-TEXT
+ \
+ TEXT
+ end;
+ assert_syntax_error("#{<<~"begin;"}\n#{<<~'end;'}", /can't find string "TEXT"/)
+ begin;
+ <<~TEXT
+ \
+ TEXT
+ end;
+ end
+
def test_lineno_after_heredoc
bug7559 = '[ruby-dev:46737]'
expected, _, actual = __LINE__, <<eom, __LINE__
@@ -656,6 +755,43 @@ eom
assert_equal(expected, actual, bug7559)
end
+ def test_dedented_heredoc_invalid_identifer
+ assert_syntax_error('<<~ "#{}"', /unexpected <</)
+ end
+
+ def test_dedented_heredoc_concatenation
+ assert_equal("\n0\n1", eval("<<~0 '1'\n \n0\#{}\n0"))
+ end
+
+ def test_heredoc_mixed_encoding
+ assert_syntax_error(<<-'HEREDOC', 'UTF-8 mixed within Windows-31J source')
+ #encoding: cp932
+ <<-TEXT
+ \xe9\x9d\u1234
+ TEXT
+ HEREDOC
+ assert_syntax_error(<<-'HEREDOC', 'UTF-8 mixed within Windows-31J source')
+ #encoding: cp932
+ <<-TEXT
+ \xe9\x9d
+ \u1234
+ TEXT
+ HEREDOC
+ assert_syntax_error(<<-'HEREDOC', 'UTF-8 mixed within Windows-31J source')
+ #encoding: cp932
+ <<-TEXT
+ \u1234\xe9\x9d
+ TEXT
+ HEREDOC
+ assert_syntax_error(<<-'HEREDOC', 'UTF-8 mixed within Windows-31J source')
+ #encoding: cp932
+ <<-TEXT
+ \u1234
+ \xe9\x9d
+ TEXT
+ HEREDOC
+ end
+
def test_lineno_operation_brace_block
expected = __LINE__ + 1
actual = caller_lineno\
@@ -745,6 +881,12 @@ eom
assert_syntax_error("puts <<""EOS\n""ng\n""EOS\r""NO\n", /can't find string "EOS" anywhere before EOF/)
end
+ def test_heredoc_newline
+ assert_warn(/ends with a newline/) do
+ eval("<<\"EOS\n\"\nEOS\n")
+ end
+ end
+
def test__END___cr
assert_syntax_error("__END__\r<<<<<\n", /unexpected <</)
end
@@ -874,7 +1016,7 @@ eom
end
def test_parenthesised_statement_argument
- assert_syntax_error("foo(bar rescue nil)", /unexpected modifier_rescue/)
+ assert_syntax_error("foo(bar rescue nil)", /unexpected rescue \(modifier\)/)
assert_valid_syntax("foo (bar rescue nil)")
end
@@ -888,6 +1030,10 @@ eom
assert_valid_syntax %q{a b(c d), 1 do end}, bug11873
assert_valid_syntax %q{a b{c(d)}, 1 do end}, bug11873
assert_valid_syntax %q{a b(c(d)), 1 do end}, bug11873
+ assert_valid_syntax %q{a b{c d}, "x" do end}, bug11873
+ assert_valid_syntax %q{a b(c d), "x" do end}, bug11873
+ assert_valid_syntax %q{a b{c(d)}, "x" do end}, bug11873
+ assert_valid_syntax %q{a b(c(d)), "x" do end}, bug11873
end
def test_block_after_cmdarg_in_paren
@@ -911,6 +1057,15 @@ eom
end
end
+ def test_do_block_in_hash_brace
+ bug13073 = '[ruby-core:78837] [Bug #13073]'
+ assert_valid_syntax 'p :foo, {a: proc do end, b: proc do end}', bug13073
+ assert_valid_syntax 'p :foo, {:a => proc do end, b: proc do end}', bug13073
+ assert_valid_syntax 'p :foo, {"a": proc do end, b: proc do end}', bug13073
+ assert_valid_syntax 'p :foo, {** proc do end, b: proc do end}', bug13073
+ assert_valid_syntax 'p :foo, {proc do end => proc do end, b: proc do end}', bug13073
+ end
+
def test_do_after_local_variable
obj = Object.new
def obj.m; yield; end
@@ -920,6 +1075,252 @@ eom
assert_equal(:ok, result)
end
+ def test_brace_after_local_variable
+ obj = Object.new
+ def obj.m; yield; end
+ result = assert_nothing_raised(SyntaxError) do
+ obj.instance_eval("m = 1; m {:ok}")
+ end
+ assert_equal(:ok, result)
+ end
+
+ def test_brace_after_literal_argument
+ bug = '[ruby-core:81037] [Bug #13547]'
+ error = /unexpected '{'/
+ assert_syntax_error('m "x" {}', error)
+ assert_syntax_error('m 1 {}', error, bug)
+ assert_syntax_error('m 1.0 {}', error, bug)
+ assert_syntax_error('m :m {}', error, bug)
+ assert_syntax_error('m :"#{m}" {}', error, bug)
+ assert_syntax_error('m ?x {}', error, bug)
+ assert_syntax_error('m %[] {}', error, bug)
+ assert_syntax_error('m 0..1 {}', error, bug)
+ assert_syntax_error('m [] {}', error, bug)
+ end
+
+ def test_return_toplevel
+ feature4840 = '[ruby-core:36785] [Feature #4840]'
+ line = __LINE__+2
+ code = "#{<<~"begin;"}#{<<~'end;'}"
+ begin;
+ return; raise
+ begin return; rescue SystemExit; exit false; end
+ begin return; ensure puts "ensured"; end #=> ensured
+ begin ensure return; end
+ begin raise; ensure; return; end
+ begin raise; rescue; return; end
+ return false; raise
+ return 1; raise
+ "#{return}"
+ raise((return; "should not raise"))
+ begin raise; ensure return; end; self
+ begin raise; ensure return; end and self
+ nil&defined?0--begin e=no_method_error(); return; 0;end
+ return puts('ignored') #=> ignored
+ end;
+ .split(/\n/).map {|s|[(line+=1), *s.split(/#=> /, 2)]}
+ failed = proc do |n, s|
+ RubyVM::InstructionSequence.compile(s, __FILE__, nil, n).disasm
+ end
+ Tempfile.create(%w"test_return_ .rb") do |lib|
+ lib.close
+ args = %W[-W0 -r#{lib.path}]
+ all_assertions_foreach(feature4840, *[:main, :lib].product([:class, :top], code)) do |main, klass, (n, s, *ex)|
+ if klass == :class
+ s = "class X; #{s}; end"
+ if main == :main
+ assert_in_out_err(%[-W0], s, [], /return/, proc {failed[n, s]}, success: false)
+ else
+ File.write(lib, s)
+ assert_in_out_err(args, "", [], /return/, proc {failed[n, s]}, success: false)
+ end
+ else
+ if main == :main
+ assert_in_out_err(%[-W0], s, ex, [], proc {failed[n, s]}, success: true)
+ else
+ File.write(lib, s)
+ assert_in_out_err(args, "", ex, [], proc {failed[n, s]}, success: true)
+ end
+ end
+ end
+ end
+ end
+
+ def test_syntax_error_in_rescue
+ bug12613 = '[ruby-core:76531] [Bug #12613]'
+ assert_syntax_error("#{<<-"begin;"}\n#{<<-"end;"}", /Invalid retry/, bug12613)
+ begin;
+ while true
+ begin
+ p
+ rescue
+ retry
+ else
+ retry
+ end
+ break
+ end
+ end;
+ end
+
+ def test_invalid_jump
+ assert_in_out_err(%w[-e redo], "", [], /^-e:1: /)
+ end
+
+ def test_keyword_not_parens
+ assert_valid_syntax("not()")
+ end
+
+ def test_rescue_do_end_raised
+ result = []
+ assert_raise(RuntimeError) do
+ eval("#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ tap do
+ result << :begin
+ raise "An exception occurred!"
+ ensure
+ result << :ensure
+ end
+ end;
+ end
+ assert_equal([:begin, :ensure], result)
+ end
+
+ def test_rescue_do_end_rescued
+ result = []
+ assert_nothing_raised(RuntimeError) do
+ eval("#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ tap do
+ result << :begin
+ raise "An exception occurred!"
+ rescue
+ result << :rescue
+ else
+ result << :else
+ ensure
+ result << :ensure
+ end
+ end;
+ end
+ assert_equal([:begin, :rescue, :ensure], result)
+ end
+
+ def test_rescue_do_end_no_raise
+ result = []
+ assert_nothing_raised(RuntimeError) do
+ eval("#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ tap do
+ result << :begin
+ rescue
+ result << :rescue
+ else
+ result << :else
+ ensure
+ result << :ensure
+ end
+ end;
+ end
+ assert_equal([:begin, :else, :ensure], result)
+ end
+
+ def test_rescue_do_end_ensure_result
+ result = eval("#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ proc do
+ :begin
+ ensure
+ :ensure
+ end.call
+ end;
+ assert_equal(:begin, result)
+ end
+
+ def test_rescue_do_end_ensure_in_lambda
+ result = []
+ eval("#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ -> do
+ result << :begin
+ raise "An exception occurred!"
+ rescue
+ result << :rescue
+ else
+ result << :else
+ ensure
+ result << :ensure
+ end.call
+ end;
+ assert_equal([:begin, :rescue, :ensure], result)
+ end
+
+ def test_return_in_loop
+ obj = Object.new
+ def obj.test
+ x = nil
+ return until x unless x
+ end
+ assert_nil obj.test
+ end
+
+ def test_assignment_return_in_loop
+ obj = Object.new
+ def obj.test
+ x = nil
+ _y = (return until x unless x)
+ end
+ assert_nil obj.test, "[Bug #16695]"
+ end
+
+ def test_method_call_location
+ line = __LINE__+5
+ e = assert_raise(NoMethodError) do
+ 1.upto(0) do
+ end
+ .
+ foo(
+ 1,
+ 2,
+ )
+ end
+ assert_equal(line, e.backtrace_locations[0].lineno)
+
+ line = __LINE__+5
+ e = assert_raise(NoMethodError) do
+ 1.upto 0 do
+ end
+ .
+ foo(
+ 1,
+ 2,
+ )
+ end
+ assert_equal(line, e.backtrace_locations[0].lineno)
+ end
+
+ def test_methoddef_in_cond
+ assert_valid_syntax('while def foo; tap do end; end; break; end')
+ assert_valid_syntax('while def foo a = tap do end; end; break; end')
+ end
+
+ def test_classdef_in_cond
+ assert_valid_syntax('while class Foo; tap do end; end; break; end')
+ assert_valid_syntax('while class Foo a = tap do end; end; break; end')
+ end
+
+ def test_command_with_cmd_brace_block
+ assert_valid_syntax('obj.foo (1) {}')
+ assert_valid_syntax('obj::foo (1) {}')
+ end
+
+ def test_value_expr_in_condition
+ mesg = /void value expression/
+ assert_syntax_error("tap {a = (true ? next : break)}", mesg)
+ assert_valid_syntax("tap {a = (true ? true : break)}")
+ end
+
private
def not_label(x) @result = x; @not_label ||= nil end
diff --git a/test/ruby/test_system.rb b/test/ruby/test_system.rb
index 60037ab044..477767905b 100644
--- a/test/ruby/test_system.rb
+++ b/test/ruby/test_system.rb
@@ -160,4 +160,45 @@ class TestSystem < Test::Unit::TestCase
assert_equal(true, system(tmpfilename), '[ruby-core:32745]')
}
end if File.executable?("/bin/sh")
+
+ def test_system_exception
+ ruby = EnvUtil.rubybin
+ assert_nothing_raised do
+ system('feature_14235', exception: false)
+ end
+ assert_nothing_raised do
+ system(ruby, "-e", "abort", exception: false)
+ end
+ assert_nothing_raised do
+ system("'#{ruby}' -e abort", exception: false)
+ end
+ assert_raise(Errno::ENOENT) do
+ system('feature_14235', exception: true)
+ end
+ assert_raise_with_message(RuntimeError, /\ACommand failed with exit /) do
+ system(ruby, "-e", "abort", exception: true)
+ end
+ assert_raise_with_message(RuntimeError, /\ACommand failed with exit /) do
+ system("'#{ruby}' -e abort", exception: true)
+ end
+ end
+
+ def test_system_exception_nonascii
+ Dir.mktmpdir("ruby_script_tmp") do |tmpdir|
+ name = "\u{30c6 30b9 30c8}"
+ tmpfilename = "#{tmpdir}/#{name}.cmd"
+ message = /#{name}\.cmd/
+ e = assert_raise_with_message(Errno::ENOENT, message) do
+ system(tmpfilename, exception: true)
+ end
+ open(tmpfilename, "w") {|f|
+ f.print "@" if /mingw|mswin/ =~ RUBY_PLATFORM
+ f.puts "exit 127"
+ f.chmod(0755)
+ }
+ e = assert_raise_with_message(RuntimeError, message) do
+ system(tmpfilename, exception: true)
+ end
+ end
+ end
end
diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb
index f60df8392e..51c0338595 100644
--- a/test/ruby/test_thread.rb
+++ b/test/ruby/test_thread.rb
@@ -1,7 +1,8 @@
# -*- coding: us-ascii -*-
# frozen_string_literal: false
require 'test/unit'
-require 'thread'
+require "rbconfig/sizeof"
+require "timeout"
class TestThread < Test::Unit::TestCase
class Thread < ::Thread
@@ -34,6 +35,19 @@ class TestThread < Test::Unit::TestCase
th.join
end
+ def test_inspect_with_fiber
+ inspect1 = inspect2 = nil
+
+ Thread.new{
+ inspect1 = Thread.current.inspect
+ Fiber.new{
+ inspect2 = Thread.current.inspect
+ }.resume
+ }.join
+
+ assert_equal inspect1, inspect2, '[Bug #13689]'
+ end
+
def test_main_thread_variable_in_enumerator
assert_equal Thread.main, Thread.current
@@ -90,7 +104,7 @@ class TestThread < Test::Unit::TestCase
def test_thread_variable_frozen
t = Thread.new { }.join
t.freeze
- assert_raise(RuntimeError) do
+ assert_raise(FrozenError) do
t.thread_variable_set(:foo, "bar")
end
end
@@ -154,6 +168,8 @@ class TestThread < Test::Unit::TestCase
t1.kill
t2.kill
assert_operator(c1, :>, c2, "[ruby-dev:33124]") # not guaranteed
+ t1.join
+ t2.join
end
def test_new
@@ -172,8 +188,16 @@ class TestThread < Test::Unit::TestCase
end
ensure
- t1.kill if t1
- t2.kill if t2
+ t1&.kill&.join
+ t2&.kill&.join
+ end
+
+ def test_new_symbol_proc
+ bug = '[ruby-core:80147] [Bug #13313]'
+ assert_ruby_status([], "#{<<-"begin;"}\n#{<<-'end;'}", bug)
+ begin;
+ exit("1" == Thread.start(1, &:to_s).value)
+ end;
end
def test_join
@@ -181,7 +205,7 @@ class TestThread < Test::Unit::TestCase
assert_nil(t.join(0.05))
ensure
- t.kill if t
+ t&.kill&.join
end
def test_join2
@@ -202,9 +226,39 @@ class TestThread < Test::Unit::TestCase
assert_equal(t1, t3.value)
ensure
- t1.kill if t1
- t2.kill if t2
- t3.kill if t3
+ t1&.kill
+ t2&.kill
+ t3&.kill
+ end
+
+ { 'FIXNUM_MAX' => RbConfig::LIMITS['FIXNUM_MAX'],
+ 'UINT64_MAX' => RbConfig::LIMITS['UINT64_MAX'],
+ 'INFINITY' => Float::INFINITY
+ }.each do |name, limit|
+ define_method("test_join_limit_#{name}") do
+ t = Thread.new {}
+ assert_same t, t.join(limit), "limit=#{limit.inspect}"
+ end
+ end
+
+ { 'minus_1' => -1,
+ 'minus_0_1' => -0.1,
+ 'FIXNUM_MIN' => RbConfig::LIMITS['FIXNUM_MIN'],
+ 'INT64_MIN' => RbConfig::LIMITS['INT64_MIN'],
+ 'minus_INFINITY' => -Float::INFINITY
+ }.each do |name, limit|
+ define_method("test_join_limit_negative_#{name}") do
+ t = Thread.new { sleep }
+ begin
+ assert_nothing_raised(Timeout::Error) do
+ Timeout.timeout(30) do
+ assert_nil t.join(limit), "limit=#{limit.inspect}"
+ end
+ end
+ ensure
+ t.kill
+ end
+ end
end
def test_kill_main_thread
@@ -251,14 +305,14 @@ class TestThread < Test::Unit::TestCase
s += 1
end
Thread.pass until t.stop?
+ sleep 1 if RubyVM::MJIT.enabled? # t.stop? behaves unexpectedly with --jit-wait
assert_equal(1, s)
t.wakeup
Thread.pass while t.alive?
assert_equal(2, s)
assert_raise(ThreadError) { t.wakeup }
-
ensure
- t.kill if t
+ t&.kill&.join
end
def test_stop
@@ -297,7 +351,10 @@ class TestThread < Test::Unit::TestCase
assert_in_out_err([], <<-INPUT, %w(false 1), [])
p Thread.abort_on_exception
begin
- t = Thread.new { raise }
+ t = Thread.new {
+ Thread.current.report_on_exception = false
+ raise
+ }
Thread.pass until t.stop?
p 1
rescue
@@ -309,7 +366,10 @@ class TestThread < Test::Unit::TestCase
Thread.abort_on_exception = true
p Thread.abort_on_exception
begin
- Thread.new { raise }
+ Thread.new {
+ Thread.current.report_on_exception = false
+ raise
+ }
sleep 0.5
p 1
rescue
@@ -332,7 +392,11 @@ class TestThread < Test::Unit::TestCase
p Thread.abort_on_exception
begin
ok = false
- t = Thread.new { Thread.pass until ok; raise }
+ t = Thread.new {
+ Thread.current.report_on_exception = false
+ Thread.pass until ok
+ raise
+ }
t.abort_on_exception = true
p t.abort_on_exception
ok = 1
@@ -345,39 +409,45 @@ class TestThread < Test::Unit::TestCase
end
def test_report_on_exception
- assert_separately([], <<~"end;") #do
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
q1 = Thread::Queue.new
q2 = Thread::Queue.new
- assert_equal(false, Thread.report_on_exception,
- "global flags is false by default")
- assert_equal(false, Thread.current.report_on_exception)
+ assert_equal(true, Thread.report_on_exception,
+ "global flag is true by default")
+ assert_equal(true, Thread.current.report_on_exception,
+ "the main thread has report_on_exception=true")
- Thread.current.report_on_exception = true
- assert_equal(false,
+ Thread.report_on_exception = true
+ Thread.current.report_on_exception = false
+ assert_equal(true,
Thread.start {Thread.current.report_on_exception}.value,
- "should not inherit from the parent thread")
+ "should not inherit from the parent thread but from the global flag")
- assert_warn("", "exception should be ignored silently") {
+ assert_warn("", "exception should be ignored silently when false") {
th = Thread.start {
+ Thread.current.report_on_exception = false
q1.push(Thread.current.report_on_exception)
raise "report 1"
}
assert_equal(false, q1.pop)
Thread.pass while th.alive?
+ assert_raise(RuntimeError) { th.join }
}
- assert_warn(/report 2/, "exception should be reported") {
+ assert_warn(/report 2/, "exception should be reported when true") {
th = Thread.start {
q1.push(Thread.current.report_on_exception = true)
raise "report 2"
}
assert_equal(true, q1.pop)
Thread.pass while th.alive?
+ assert_raise(RuntimeError) { th.join }
}
- assert_equal(false, Thread.report_on_exception)
assert_warn("", "the global flag should not affect already started threads") {
+ Thread.report_on_exception = false
th = Thread.start {
q2.pop
q1.push(Thread.current.report_on_exception)
@@ -386,22 +456,41 @@ class TestThread < Test::Unit::TestCase
q2.push(Thread.report_on_exception = true)
assert_equal(false, q1.pop)
Thread.pass while th.alive?
+ assert_raise(RuntimeError) { th.join }
}
- assert_equal(true, Thread.report_on_exception)
assert_warn(/report 4/, "should defaults to the global flag at the start") {
+ Thread.report_on_exception = true
th = Thread.start {
q1.push(Thread.current.report_on_exception)
raise "report 4"
}
assert_equal(true, q1.pop)
Thread.pass while th.alive?
+ assert_raise(RuntimeError) { th.join }
+ }
+
+ assert_warn(/report 5/, "should first report and then raise with report_on_exception + abort_on_exception") {
+ th = Thread.start {
+ Thread.current.report_on_exception = true
+ Thread.current.abort_on_exception = true
+ q2.pop
+ raise "report 5"
+ }
+ assert_raise_with_message(RuntimeError, "report 5") {
+ q2.push(true)
+ Thread.pass while th.alive?
+ }
+ assert_raise(RuntimeError) { th.join }
}
end;
end
def test_status_and_stop_p
- a = ::Thread.new { raise("die now") }
+ a = ::Thread.new {
+ Thread.current.report_on_exception = false
+ raise("die now")
+ }
b = Thread.new { Thread.stop }
c = Thread.new { Thread.exit }
e = Thread.current
@@ -420,11 +509,10 @@ class TestThread < Test::Unit::TestCase
es1 = e.status
es2 = e.stop?
assert_equal(["run", false], [es1, es2])
-
+ assert_raise(RuntimeError) { a.join }
ensure
- a.kill if a
- b.kill if b
- c.kill if c
+ b&.kill&.join
+ c&.join
end
def test_switch_while_busy_loop
@@ -442,7 +530,7 @@ class TestThread < Test::Unit::TestCase
end
assert(!flag, bug1402)
ensure
- waiter.kill.join
+ waiter&.kill&.join
end
def test_safe_level
@@ -455,11 +543,11 @@ class TestThread < Test::Unit::TestCase
sleep
end
Thread.pass until ok
- assert_equal(0, Thread.current.safe_level)
- assert_equal(1, t.safe_level)
-
+ assert_equal($SAFE, Thread.current.safe_level)
+ assert_equal($SAFE, t.safe_level)
ensure
- t.kill if t
+ $SAFE = 0
+ t&.kill&.join
end
def test_thread_local
@@ -476,20 +564,52 @@ class TestThread < Test::Unit::TestCase
assert_equal(false, t.key?(:qux))
assert_equal(false, t.key?("qux"))
- assert_equal([:foo, :bar, :baz], t.keys)
+ assert_equal([:foo, :bar, :baz].sort, t.keys.sort)
+
+ ensure
+ t&.kill&.join
+ end
+
+ def test_thread_local_fetch
+ t = Thread.new { sleep }
+
+ assert_equal(false, t.key?(:foo))
+
+ t["foo"] = "foo"
+ t["bar"] = "bar"
+ t["baz"] = "baz"
+ x = nil
+ assert_equal("foo", t.fetch(:foo, 0))
+ assert_equal("foo", t.fetch(:foo) {x = true})
+ assert_nil(x)
+ assert_equal("foo", t.fetch("foo", 0))
+ assert_equal("foo", t.fetch("foo") {x = true})
+ assert_nil(x)
+
+ x = nil
+ assert_equal(0, t.fetch(:qux, 0))
+ assert_equal(1, t.fetch(:qux) {x = 1})
+ assert_equal(1, x)
+ assert_equal(2, t.fetch("qux", 2))
+ assert_equal(3, t.fetch("qux") {x = 3})
+ assert_equal(3, x)
+
+ e = assert_raise(KeyError) {t.fetch(:qux)}
+ assert_equal(:qux, e.key)
+ assert_equal(t, e.receiver)
ensure
- t.kill if t
+ t&.kill&.join
end
def test_thread_local_security
- assert_raise(RuntimeError) do
- Thread.new do
- Thread.current[:foo] = :bar
- Thread.current.freeze
+ Thread.new do
+ Thread.current[:foo] = :bar
+ Thread.current.freeze
+ assert_raise(FrozenError) do
Thread.current[:foo] = :baz
- end.join
- end
+ end
+ end.join
end
def test_thread_local_dynamic_symbol
@@ -509,7 +629,8 @@ class TestThread < Test::Unit::TestCase
end
Thread.pass until t.stop?
assert_predicate(t, :alive?)
- t.kill
+ ensure
+ t&.kill
end
def test_mutex_deadlock
@@ -538,11 +659,11 @@ class TestThread < Test::Unit::TestCase
def test_mutex_illegal_unlock
m = Thread::Mutex.new
m.lock
- assert_raise(ThreadError) do
- Thread.new do
+ Thread.new do
+ assert_raise(ThreadError) do
m.unlock
- end.join
- end
+ end
+ end.join
end
def test_mutex_fifo_like_lock
@@ -610,14 +731,14 @@ class TestThread < Test::Unit::TestCase
def make_handle_interrupt_test_thread1 flag
r = []
- ready_p = false
- done = false
+ ready_q = Queue.new
+ done_q = Queue.new
th = Thread.new{
begin
Thread.handle_interrupt(RuntimeError => flag){
begin
- ready_p = true
- sleep 0.01 until done
+ ready_q << true
+ done_q.pop
rescue
r << :c1
end
@@ -626,10 +747,10 @@ class TestThread < Test::Unit::TestCase
r << :c2
end
}
- Thread.pass until ready_p
+ ready_q.pop
th.raise
begin
- done = true
+ done_q << true
th.join
rescue
r << :c3
@@ -690,12 +811,12 @@ class TestThread < Test::Unit::TestCase
r=:ng
e=Class.new(Exception)
th_s = Thread.current
- begin
- th = Thread.start{
+ th = Thread.start{
+ assert_raise(RuntimeError) {
Thread.handle_interrupt(Object => :on_blocking){
begin
Thread.pass until r == :wait
- Thread.current.raise RuntimeError
+ Thread.current.raise RuntimeError, "will raise in sleep"
r = :ok
sleep
ensure
@@ -703,11 +824,9 @@ class TestThread < Test::Unit::TestCase
end
}
}
- assert_raise(e) {r = :wait; sleep 0.2}
- assert_raise(RuntimeError) {th.join(0.2)}
- ensure
- th.kill
- end
+ }
+ assert_raise(e) {r = :wait; sleep 0.2}
+ th.join
assert_equal(:ok,r)
end
@@ -716,6 +835,7 @@ class TestThread < Test::Unit::TestCase
th_waiting = true
t = Thread.new {
+ Thread.current.report_on_exception = false
Thread.handle_interrupt(RuntimeError => :on_blocking) {
nil while th_waiting
# async interrupt should be raised _before_ writing puts arguments
@@ -736,6 +856,7 @@ class TestThread < Test::Unit::TestCase
th_waiting = false
t = Thread.new {
+ Thread.current.report_on_exception = false
Thread.handle_interrupt(RuntimeError => :on_blocking) {
th_waiting = true
nil while th_waiting
@@ -837,15 +958,16 @@ _eom
def test_thread_timer_and_interrupt
bug5757 = '[ruby-dev:44985]'
pid = nil
- cmd = 'Signal.trap(:INT, "DEFAULT"); r,=IO.pipe; Thread.start {Thread.pass until Thread.main.stop?; puts; STDOUT.flush}; r.read'
+ cmd = 'Signal.trap(:INT, "DEFAULT"); pipe=IO.pipe; Thread.start {Thread.pass until Thread.main.stop?; puts; STDOUT.flush}; pipe[0].read'
opt = {}
opt[:new_pgroup] = true if /mswin|mingw/ =~ RUBY_PLATFORM
s, t, _err = EnvUtil.invoke_ruby(['-e', cmd], "", true, true, opt) do |in_p, out_p, err_p, cpid|
+ assert IO.select([out_p], nil, nil, 10), 'subprocess not ready'
out_p.gets
pid = cpid
t0 = Time.now.to_f
Process.kill(:SIGINT, pid)
- Process.wait(pid)
+ Timeout.timeout(10) { Process.wait(pid) }
t1 = Time.now.to_f
[$?, t1 - t0, err_p.read]
end
@@ -892,16 +1014,15 @@ _eom
end
def test_thread_join_main_thread
- assert_raise(ThreadError) do
- Thread.new(Thread.current) {|t|
+ Thread.new(Thread.current) {|t|
+ assert_raise(ThreadError) do
t.join
- }.join
- end
+ end
+ }.join
end
def test_main_thread_status_at_exit
assert_in_out_err([], <<-'INPUT', ["false false aborting"], [])
-require 'thread'
q = Thread::Queue.new
Thread.new(Thread.current) {|mth|
begin
@@ -941,31 +1062,30 @@ q.pop
ary = []
t = Thread.new {
- begin
- ary << Thread.current.status
- sleep #1
- ensure
+ assert_raise(RuntimeError) do
begin
ary << Thread.current.status
- sleep #2
+ sleep #1
ensure
- ary << Thread.current.status
+ begin
+ ary << Thread.current.status
+ sleep #2
+ ensure
+ ary << Thread.current.status
+ end
end
end
}
- begin
- Thread.pass until ary.size >= 1
- Thread.pass until t.stop?
- t.kill # wake up sleep #1
- Thread.pass until ary.size >= 2
- Thread.pass until t.stop?
- t.raise "wakeup" # wake up sleep #2
- Thread.pass while t.alive?
- assert_equal(ary, ["run", "aborting", "aborting"])
- ensure
- t.join rescue nil
- end
+ Thread.pass until ary.size >= 1
+ Thread.pass until t.stop?
+ t.kill # wake up sleep #1
+ Thread.pass until ary.size >= 2
+ Thread.pass until t.stop?
+ t.raise "wakeup" # wake up sleep #2
+ Thread.pass while t.alive?
+ assert_equal(ary, ["run", "aborting", "aborting"])
+ t.join
end
def test_mutex_owned
@@ -992,7 +1112,7 @@ q.pop
Thread.pass until mutex.locked?
assert_equal(mutex.owned?, false)
ensure
- th.kill if th
+ th&.kill
end
end
@@ -1097,9 +1217,9 @@ q.pop
end
Process.wait2(f.pid)
end
- unless th.join(3)
+ unless th.join(EnvUtil.apply_timeout_scale(30))
Process.kill(:QUIT, f.pid)
- Process.kill(:KILL, f.pid) unless th.join(1)
+ Process.kill(:KILL, f.pid) unless th.join(EnvUtil.apply_timeout_scale(1))
end
_, status = th.value
output = f.read
@@ -1108,6 +1228,53 @@ q.pop
assert_predicate(status, :success?, bug9751)
end if Process.respond_to?(:fork)
+ def test_fork_while_locked
+ m = Mutex.new
+ thrs = []
+ 3.times do |i|
+ thrs << Thread.new { m.synchronize { Process.waitpid2(fork{})[1] } }
+ end
+ thrs.each do |t|
+ assert_predicate t.value, :success?, '[ruby-core:85940] [Bug #14578]'
+ end
+ end if Process.respond_to?(:fork)
+
+ def test_fork_while_parent_locked
+ skip 'needs fork' unless Process.respond_to?(:fork)
+ m = Thread::Mutex.new
+ nr = 1
+ thrs = []
+ m.synchronize do
+ thrs = nr.times.map { Thread.new { m.synchronize {} } }
+ thrs.each { Thread.pass }
+ pid = fork do
+ m.locked? or exit!(2)
+ thrs = nr.times.map { Thread.new { m.synchronize {} } }
+ m.unlock
+ thrs.each { |t| t.join(1) == t or exit!(1) }
+ exit!(0)
+ end
+ _, st = Process.waitpid2(pid)
+ assert_predicate st, :success?, '[ruby-core:90312] [Bug #15383]'
+ end
+ thrs.each { |t| assert_same t, t.join(1) }
+ end
+
+ def test_fork_while_mutex_locked_by_forker
+ skip 'needs fork' unless Process.respond_to?(:fork)
+ m = Mutex.new
+ m.synchronize do
+ pid = fork do
+ exit!(2) unless m.locked?
+ m.unlock rescue exit!(3)
+ m.synchronize {} rescue exit!(4)
+ exit!(0)
+ end
+ _, st = Timeout.timeout(30) { Process.waitpid2(pid) }
+ assert_predicate st, :success?, '[ruby-core:90595] [Bug #15430]'
+ end
+ end
+
def test_subclass_no_initialize
t = Module.new do
break eval("class C\u{30b9 30ec 30c3 30c9} < Thread; self; end")
@@ -1160,4 +1327,73 @@ q.pop
c = Class.new(Thread) {def initialize() self.name = "foo"; super; end}
assert_equal("foo", c.new {Thread.current.name}.value, bug12290)
end
+
+ def test_thread_interrupt_for_killed_thread
+ opts = { timeout: 5, timeout_error: nil }
+
+ # prevent SIGABRT from slow shutdown with MJIT
+ opts[:reprieve] = 3 if RubyVM::MJIT.enabled?
+
+ assert_normal_exit(<<-_end, '[Bug #8996]', opts)
+ Thread.report_on_exception = false
+ trap(:TERM){exit}
+ while true
+ t = Thread.new{sleep 0}
+ t.raise Interrupt
+ Thread.pass # allow t to finish
+ end
+ _end
+ end
+
+ def test_signal_at_join
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ skip "can't trap a signal from another process on Windows"
+ # opt = {new_pgroup: true}
+ end
+ assert_separately([], "#{<<~"{#"}\n#{<<~'};'}", timeout: 120)
+ {#
+ n = 1000
+ sig = :INT
+ trap(sig) {}
+ IO.popen([EnvUtil.rubybin, "-e", "#{<<~"{#1"}\n#{<<~'};#1'}"], "r+") do |f|
+ tpid = #{$$}
+ sig = :#{sig}
+ {#1
+ STDOUT.sync = true
+ while gets
+ puts
+ Process.kill(sig, tpid)
+ end
+ };#1
+ assert_nothing_raised do
+ n.times do
+ w = Thread.start do
+ sleep 30
+ end
+ begin
+ f.puts
+ f.gets
+ ensure
+ w.kill
+ w.join
+ end
+ end
+ end
+ n.times do
+ w = Thread.start { sleep 30 }
+ begin
+ f.puts
+ f.gets
+ ensure
+ w.kill
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ w.join(30)
+ t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ diff = t1 - t0
+ assert_operator diff, :<=, 2
+ end
+ end
+ end
+ };
+ end
end
diff --git a/test/ruby/test_thread_cv.rb b/test/ruby/test_thread_cv.rb
new file mode 100644
index 0000000000..38bcc3b8fa
--- /dev/null
+++ b/test/ruby/test_thread_cv.rb
@@ -0,0 +1,245 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'tmpdir'
+
+class TestThreadConditionVariable < Test::Unit::TestCase
+ ConditionVariable = Thread::ConditionVariable
+ Mutex = Thread::Mutex
+
+ def test_initialized
+ assert_raise(TypeError) {
+ ConditionVariable.allocate.wait(nil)
+ }
+ end
+
+ def test_condvar_signal_and_wait
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+ result = []
+ mutex.synchronize do
+ t = Thread.new do
+ mutex.synchronize do
+ result << 1
+ condvar.signal
+ end
+ end
+
+ result << 0
+ condvar.wait(mutex)
+ result << 2
+ t.join
+ end
+ assert_equal([0, 1, 2], result)
+ end
+
+ def test_condvar_wait_exception_handling
+ # Calling wait in the only thread running should raise a ThreadError of
+ # 'stopping only thread'
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ locked = false
+ thread = Thread.new do
+ Thread.current.abort_on_exception = false
+ mutex.synchronize do
+ assert_raise(Interrupt) {
+ condvar.wait(mutex)
+ }
+ locked = mutex.locked?
+ end
+ end
+
+ until thread.stop?
+ sleep(0.1)
+ end
+
+ thread.raise Interrupt, "interrupt a dead condition variable"
+ thread.join
+ assert(locked)
+ end
+
+ def test_condvar_wait_and_broadcast
+ nr_threads = 3
+ threads = Array.new
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+ result = []
+
+ nr_threads.times do |i|
+ threads[i] = Thread.new do
+ mutex.synchronize do
+ result << "C1"
+ condvar.wait mutex
+ result << "C2"
+ end
+ end
+ end
+ sleep 0.1
+ mutex.synchronize do
+ result << "P1"
+ condvar.broadcast
+ result << "P2"
+ end
+ Timeout.timeout(5) do
+ nr_threads.times do |i|
+ threads[i].join
+ end
+ end
+
+ assert_equal ["C1", "C1", "C1", "P1", "P2", "C2", "C2", "C2"], result
+ end
+
+ def test_condvar_wait_deadlock
+ assert_in_out_err([], <<-INPUT, /\Afatal\nNo live threads left\. Deadlock/, [])
+ mutex = Mutex.new
+ cv = ConditionVariable.new
+
+ klass = nil
+ mesg = nil
+ begin
+ mutex.lock
+ cv.wait mutex
+ mutex.unlock
+ rescue Exception => e
+ klass = e.class
+ mesg = e.message
+ end
+ puts klass
+ print mesg
+INPUT
+ end
+
+ def test_condvar_wait_deadlock_2
+ nr_threads = 3
+ threads = Array.new
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ nr_threads.times do |i|
+ if (i != 0)
+ mutex.unlock
+ end
+ threads[i] = Thread.new do
+ mutex.synchronize do
+ condvar.wait mutex
+ end
+ end
+ mutex.lock
+ end
+
+ assert_raise(Timeout::Error) do
+ Timeout.timeout(0.1) { condvar.wait mutex }
+ end
+ mutex.unlock
+ threads.each(&:kill)
+ threads.each(&:join)
+ end
+
+ def test_condvar_timed_wait
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+ timeout = 0.3
+ locked = false
+
+ t0 = Time.now
+ mutex.synchronize do
+ begin
+ condvar.wait(mutex, timeout)
+ ensure
+ locked = mutex.locked?
+ end
+ end
+ t1 = Time.now
+ t = t1-t0
+
+ assert_operator(timeout*0.9, :<, t)
+ assert(locked)
+ end
+
+ def test_condvar_nolock
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ assert_raise(ThreadError) {condvar.wait(mutex)}
+ end
+
+ def test_condvar_nolock_2
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ Thread.new do
+ assert_raise(ThreadError) {condvar.wait(mutex)}
+ end.join
+ end
+
+ def test_condvar_nolock_3
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ Thread.new do
+ assert_raise(ThreadError) {condvar.wait(mutex, 0.1)}
+ end.join
+ end
+
+ def test_condvar_empty_signal
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ assert_nothing_raised(Exception) { mutex.synchronize {condvar.signal} }
+ end
+
+ def test_condvar_empty_broadcast
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ assert_nothing_raised(Exception) { mutex.synchronize {condvar.broadcast} }
+ end
+
+ def test_dup
+ bug9440 = '[ruby-core:59961] [Bug #9440]'
+ condvar = ConditionVariable.new
+ assert_raise(NoMethodError, bug9440) do
+ condvar.dup
+ end
+ end
+
+ (DumpableCV = ConditionVariable.dup).class_eval {remove_method :marshal_dump}
+
+ def test_dump
+ bug9674 = '[ruby-core:61677] [Bug #9674]'
+ condvar = ConditionVariable.new
+ assert_raise_with_message(TypeError, /#{ConditionVariable}/, bug9674) do
+ Marshal.dump(condvar)
+ end
+
+ condvar = DumpableCV.new
+ assert_raise(TypeError, bug9674) do
+ Marshal.dump(condvar)
+ end
+ end
+
+ def test_condvar_fork
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+ thrs = (1..10).map do
+ Thread.new { mutex.synchronize { condvar.wait(mutex) } }
+ end
+ thrs.each { 3.times { Thread.pass } }
+ pid = fork do
+ th = Thread.new do
+ mutex.synchronize { condvar.wait(mutex) }
+ :ok
+ end
+ until th.join(0.01)
+ mutex.synchronize { condvar.broadcast }
+ end
+ exit!(th.value == :ok ? 0 : 1)
+ end
+ _, s = Process.waitpid2(pid)
+ assert_predicate s, :success?, 'no segfault [ruby-core:86316] [Bug #14634]'
+ until thrs.empty?
+ mutex.synchronize { condvar.broadcast }
+ thrs.delete_if { |t| t.join(0.01) }
+ end
+ end if Process.respond_to?(:fork)
+end
diff --git a/test/ruby/test_thread_queue.rb b/test/ruby/test_thread_queue.rb
new file mode 100644
index 0000000000..8cebbbecb4
--- /dev/null
+++ b/test/ruby/test_thread_queue.rb
@@ -0,0 +1,619 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'tmpdir'
+require 'timeout'
+
+class TestThreadQueue < Test::Unit::TestCase
+ Queue = Thread::Queue
+ SizedQueue = Thread::SizedQueue
+
+ def test_queue_initialized
+ assert_raise(TypeError) {
+ Queue.allocate.push(nil)
+ }
+ end
+
+ def test_sized_queue_initialized
+ assert_raise(TypeError) {
+ SizedQueue.allocate.push(nil)
+ }
+ end
+
+ def test_queue
+ grind(5, 1000, 15, Queue)
+ end
+
+ def test_sized_queue
+ grind(5, 1000, 15, SizedQueue, 1000)
+ end
+
+ def grind(num_threads, num_objects, num_iterations, klass, *args)
+ from_workers = klass.new(*args)
+ to_workers = klass.new(*args)
+
+ workers = (1..num_threads).map {
+ Thread.new {
+ while object = to_workers.pop
+ from_workers.push object
+ end
+ }
+ }
+
+ Thread.new {
+ num_iterations.times {
+ num_objects.times { to_workers.push 99 }
+ num_objects.times { from_workers.pop }
+ }
+ }.join
+
+ # close the queue the old way to test for backwards-compatibility
+ num_threads.times { to_workers.push nil }
+ workers.each { |t| t.join }
+
+ assert_equal 0, from_workers.size
+ assert_equal 0, to_workers.size
+ end
+
+ def test_sized_queue_initialize
+ q = SizedQueue.new(1)
+ assert_equal 1, q.max
+ assert_raise(ArgumentError) { SizedQueue.new(0) }
+ assert_raise(ArgumentError) { SizedQueue.new(-1) }
+ end
+
+ def test_sized_queue_assign_max
+ q = SizedQueue.new(2)
+ assert_equal(2, q.max)
+ q.max = 1
+ assert_equal(1, q.max)
+ assert_raise(ArgumentError) { q.max = 0 }
+ assert_equal(1, q.max)
+ assert_raise(ArgumentError) { q.max = -1 }
+ assert_equal(1, q.max)
+
+ before = q.max
+ q.max.times { q << 1 }
+ t1 = Thread.new { q << 1 }
+ sleep 0.01 until t1.stop?
+ q.max = q.max + 1
+ assert_equal before + 1, q.max
+ ensure
+ t1.join if t1
+ end
+
+ def test_queue_pop_interrupt
+ q = Queue.new
+ t1 = Thread.new { q.pop }
+ sleep 0.01 until t1.stop?
+ t1.kill.join
+ assert_equal(0, q.num_waiting)
+ end
+
+ def test_queue_pop_non_block
+ q = Queue.new
+ assert_raise_with_message(ThreadError, /empty/) do
+ q.pop(true)
+ end
+ end
+
+ def test_sized_queue_pop_interrupt
+ q = SizedQueue.new(1)
+ t1 = Thread.new { q.pop }
+ sleep 0.01 until t1.stop?
+ t1.kill.join
+ assert_equal(0, q.num_waiting)
+ end
+
+ def test_sized_queue_pop_non_block
+ q = SizedQueue.new(1)
+ assert_raise_with_message(ThreadError, /empty/) do
+ q.pop(true)
+ end
+ end
+
+ def test_sized_queue_push_interrupt
+ q = SizedQueue.new(1)
+ q.push(1)
+ assert_raise_with_message(ThreadError, /full/) do
+ q.push(2, true)
+ end
+ end
+
+ def test_sized_queue_push_non_block
+ q = SizedQueue.new(1)
+ q.push(1)
+ t1 = Thread.new { q.push(2) }
+ sleep 0.01 until t1.stop?
+ t1.kill.join
+ assert_equal(0, q.num_waiting)
+ end
+
+ def test_thr_kill
+ bug5343 = '[ruby-core:39634]'
+ Dir.mktmpdir {|d|
+ timeout = 60
+ total_count = 250
+ begin
+ assert_normal_exit(<<-"_eom", bug5343, {:timeout => timeout, :chdir=>d})
+ #{total_count}.times do |i|
+ open("test_thr_kill_count", "w") {|f| f.puts i }
+ queue = Queue.new
+ r, w = IO.pipe
+ th = Thread.start {
+ queue.push(nil)
+ r.read 1
+ }
+ queue.pop
+ th.kill
+ th.join
+ end
+ _eom
+ rescue Timeout::Error
+ count = File.read("#{d}/test_thr_kill_count").to_i
+ flunk "only #{count}/#{total_count} done in #{timeout} seconds."
+ end
+ }
+ end
+
+ def test_queue_push_return_value
+ q = Queue.new
+ retval = q.push(1)
+ assert_same q, retval
+ end
+
+ def test_queue_clear_return_value
+ q = Queue.new
+ retval = q.clear
+ assert_same q, retval
+ end
+
+ def test_sized_queue_clear
+ # Fill queue, then test that SizedQueue#clear wakes up all waiting threads
+ sq = SizedQueue.new(2)
+ 2.times { sq << 1 }
+
+ t1 = Thread.new do
+ sq << 1
+ end
+
+ t2 = Thread.new do
+ sq << 1
+ end
+
+ t3 = Thread.new do
+ Thread.pass
+ sq.clear
+ end
+
+ [t3, t2, t1].each(&:join)
+ assert_equal sq.length, 2
+ end
+
+ def test_sized_queue_push_return_value
+ q = SizedQueue.new(1)
+ retval = q.push(1)
+ assert_same q, retval
+ end
+
+ def test_sized_queue_clear_return_value
+ q = SizedQueue.new(1)
+ retval = q.clear
+ assert_same q, retval
+ end
+
+ def test_sized_queue_throttle
+ q = SizedQueue.new(1)
+ i = 0
+ consumer = Thread.new do
+ while q.pop
+ i += 1
+ Thread.pass
+ end
+ end
+ nprod = 4
+ npush = 100
+
+ producer = nprod.times.map do
+ Thread.new do
+ npush.times { q.push(true) }
+ end
+ end
+ producer.each(&:join)
+ q.push(nil)
+ consumer.join
+ assert_equal(nprod * npush, i)
+ end
+
+ def test_queue_thread_raise
+ q = Queue.new
+ th1 = Thread.new do
+ begin
+ q.pop
+ rescue RuntimeError
+ sleep
+ end
+ end
+ th2 = Thread.new do
+ sleep 0.1
+ q.pop
+ end
+ sleep 0.1
+ th1.raise
+ sleep 0.1
+ q << :s
+ assert_nothing_raised(Timeout::Error) do
+ Timeout.timeout(1) { th2.join }
+ end
+ ensure
+ [th1, th2].each do |th|
+ if th and th.alive?
+ th.wakeup
+ th.join
+ end
+ end
+ end
+
+ def test_dup
+ bug9440 = '[ruby-core:59961] [Bug #9440]'
+ q = Queue.new
+ assert_raise(NoMethodError, bug9440) do
+ q.dup
+ end
+ end
+
+ (DumpableQueue = Queue.dup).class_eval {remove_method :marshal_dump}
+
+ def test_dump
+ bug9674 = '[ruby-core:61677] [Bug #9674]'
+ q = Queue.new
+ assert_raise_with_message(TypeError, /#{Queue}/, bug9674) do
+ Marshal.dump(q)
+ end
+
+ sq = SizedQueue.new(1)
+ assert_raise_with_message(TypeError, /#{SizedQueue}/, bug9674) do
+ Marshal.dump(sq)
+ end
+
+ q = DumpableQueue.new
+ assert_raise(TypeError, bug9674) do
+ Marshal.dump(q)
+ end
+ end
+
+ def test_close
+ [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
+ q = qcreate.call
+ assert_equal false, q.closed?
+ q << :something
+ assert_equal q, q.close
+ assert q.closed?
+ assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
+ assert_equal q.pop, :something
+ assert_nil q.pop
+ assert_nil q.pop
+ # non-blocking
+ assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
+ end
+ end
+
+ # test that waiting producers are woken up on close
+ def close_wakeup( num_items, num_threads, &qcreate )
+ raise "This test won't work with num_items(#{num_items}) >= num_threads(#{num_threads})" if num_items >= num_threads
+
+ # create the Queue
+ q = yield
+ threads = num_threads.times.map{Thread.new{q.pop}}
+ num_items.times{|i| q << i}
+
+ # wait until queue empty
+ (Thread.pass; sleep 0.01) until q.size == 0
+
+ # close the queue so remaining threads will wake up
+ q.close
+
+ # wait for them to go away
+ Thread.pass until threads.all?{|thr| thr.status == false}
+
+ # check that they've gone away. Convert nil to -1 so we can sort and do the comparison
+ expected_values = [-1] * (num_threads - num_items) + num_items.times.to_a
+ assert_equal expected_values, threads.map{|thr| thr.value || -1 }.sort
+ end
+
+ def test_queue_close_wakeup
+ close_wakeup(15, 18){Queue.new}
+ end
+
+ def test_size_queue_close_wakeup
+ close_wakeup(5, 8){SizedQueue.new 9}
+ end
+
+ def test_sized_queue_one_closed_interrupt
+ q = SizedQueue.new 1
+ q << :one
+ t1 = Thread.new { q << :two }
+ sleep 0.01 until t1.stop?
+ q.close
+
+ t1.kill.join
+ assert_equal 1, q.size
+ assert_equal :one, q.pop
+ assert q.empty?, "queue not empty"
+ end
+
+ # make sure that shutdown state is handled properly by empty? for the non-blocking case
+ def test_empty_non_blocking
+ return
+ q = SizedQueue.new 3
+ 3.times{|i| q << i}
+
+ # these all block cos the queue is full
+ prod_threads = 4.times.map{|i| Thread.new{q << 3+i}}
+ sleep 0.01 until prod_threads.all?{|thr| thr.status == 'sleep'}
+ q.close
+
+ items = []
+ # sometimes empty? is false but pop will raise ThreadError('empty'),
+ # meaning a value is not immediately available but will be soon.
+ until q.empty?
+ items << q.pop(non_block=true) rescue nil
+ end
+ items.compact!
+
+ assert_equal 7.times.to_a, items.sort
+ assert q.empty?
+ end
+
+ def test_sized_queue_closed_push_non_blocking
+ q = SizedQueue.new 7
+ q.close
+ assert_raise_with_message(ClosedQueueError, /queue closed/){q.push(non_block=true)}
+ end
+
+ def test_blocked_pushers
+ q = SizedQueue.new 3
+ prod_threads = 6.times.map do |i|
+ thr = Thread.new{
+ Thread.current.report_on_exception = false
+ q << i
+ }
+ thr[:pc] = i
+ thr
+ end
+
+ # wait until some producer threads have finished, and the other 3 are blocked
+ sleep 0.01 while prod_threads.reject{|t| t.status}.count < 3
+ # this would ensure that all producer threads call push before close
+ # sleep 0.01 while prod_threads.select{|t| t.status == 'sleep'}.count < 3
+ q.close
+
+ # more than prod_threads
+ cons_threads = 10.times.map do |i|
+ thr = Thread.new{q.pop}; thr[:pc] = i; thr
+ end
+
+ # values that came from the queue
+ popped_values = cons_threads.map &:value
+
+ # wait untl all threads have finished
+ sleep 0.01 until prod_threads.find_all{|t| t.status}.count == 0
+
+ # pick only the producer threads that got in before close
+ successful_prod_threads = prod_threads.reject{|thr| thr.status == nil}
+ assert_nothing_raised{ successful_prod_threads.map(&:value) }
+
+ # the producer threads that tried to push after q.close should all fail
+ unsuccessful_prod_threads = prod_threads - successful_prod_threads
+ unsuccessful_prod_threads.each do |thr|
+ assert_raise(ClosedQueueError){ thr.value }
+ end
+
+ assert_equal cons_threads.size, popped_values.size
+ assert_equal 0, q.size
+
+ # check that consumer threads with values match producers that called push before close
+ assert_equal successful_prod_threads.map{|thr| thr[:pc]}, popped_values.compact.sort
+ assert_nil q.pop
+ end
+
+ def test_deny_pushers
+ [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
+ q = qcreate[]
+ synq = Queue.new
+ prod_threads = 20.times.map do |i|
+ Thread.new {
+ synq.pop
+ assert_raise(ClosedQueueError) {
+ q << i
+ }
+ }
+ end
+ q.close
+ synq.close # start producer threads
+
+ prod_threads.each(&:join)
+ end
+ end
+
+ # size should account for waiting pushers during shutdown
+ def sized_queue_size_close
+ q = SizedQueue.new 4
+ 4.times{|i| q << i}
+ Thread.new{ q << 5 }
+ Thread.new{ q << 6 }
+ assert_equal 4, q.size
+ assert_equal 4, q.items
+ q.close
+ assert_equal 6, q.size
+ assert_equal 4, q.items
+ end
+
+ def test_blocked_pushers_empty
+ q = SizedQueue.new 3
+ prod_threads = 6.times.map do |i|
+ Thread.new{
+ Thread.current.report_on_exception = false
+ q << i
+ }
+ end
+
+ # this ensures that all producer threads call push before close
+ sleep 0.01 while prod_threads.select{|t| t.status == 'sleep'}.count < 3
+ q.close
+
+ ary = []
+ until q.empty?
+ ary << q.pop
+ end
+ assert_equal 0, q.size
+
+ assert_equal 3, ary.size
+ ary.each{|e| assert [0,1,2,3,4,5].include?(e)}
+ assert_nil q.pop
+
+ prod_threads.each{|t|
+ begin
+ t.join
+ rescue => e
+ end
+ }
+ end
+
+ # test thread wakeup on one-element SizedQueue with close
+ def test_one_element_sized_queue
+ q = SizedQueue.new 1
+ t = Thread.new{ q.pop }
+ q.close
+ assert_nil t.value
+ end
+
+ def test_close_twice
+ [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
+ q = qcreate[]
+ q.close
+ assert_nothing_raised(ClosedQueueError){q.close}
+ end
+ end
+
+ def test_queue_close_multi_multi
+ q = SizedQueue.new rand(800..1200)
+
+ count_items = rand(3000..5000)
+ count_producers = rand(10..20)
+
+ producers = count_producers.times.map do
+ Thread.new do
+ sleep(rand / 100)
+ count_items.times{|i| q << [i,"#{i} for #{Thread.current.inspect}"]}
+ end
+ end
+
+ consumers = rand(7..12).times.map do
+ Thread.new do
+ count = 0
+ while e = q.pop
+ i, st = e
+ count += 1 if i.is_a?(Integer) && st.is_a?(String)
+ end
+ count
+ end
+ end
+
+ # No dead or finished threads, give up to 10 seconds to start running
+ t = Time.now
+ Thread.pass until Time.now - t > 10 || (consumers + producers).all?{|thr| thr.status =~ /\A(?:run|sleep)\z/}
+
+ assert (consumers + producers).all?{|thr| thr.status =~ /\A(?:run|sleep)\z/}, 'no threads running'
+
+ # just exercising the concurrency of the support methods.
+ counter = Thread.new do
+ until q.closed? && q.empty?
+ raise if q.size > q.max
+ # otherwise this exercise causes too much contention on the lock
+ sleep 0.01
+ end
+ end
+
+ producers.each &:join
+ q.close
+
+ # results not randomly distributed. Not sure why.
+ # consumers.map{|thr| thr.value}.each do |x|
+ # assert_not_equal 0, x
+ # end
+
+ all_items_count = consumers.map{|thr| thr.value}.inject(:+)
+ assert_equal count_items * count_producers, all_items_count
+
+ # don't leak this thread
+ assert_nothing_raised{counter.join}
+ end
+
+ def test_queue_with_trap
+ if ENV['APPVEYOR'] == 'True' && RUBY_PLATFORM.match?(/mswin/)
+ skip 'This test fails too often on AppVeyor vs140'
+ end
+ assert_in_out_err([], <<-INPUT, %w(INT INT exit), [])
+ q = Queue.new
+ trap(:INT){
+ q.push 'INT'
+ }
+ Thread.new{
+ loop{
+ Process.kill :INT, $$
+ }
+ }
+ puts q.pop
+ puts q.pop
+ puts 'exit'
+ INPUT
+ end
+
+ def test_fork_while_queue_waiting
+ q = Queue.new
+ sq = SizedQueue.new(1)
+ thq = Thread.new { q.pop }
+ thsq = Thread.new { sq.pop }
+ Thread.pass until thq.stop? && thsq.stop?
+
+ pid = fork do
+ exit!(1) if q.num_waiting != 0
+ exit!(2) if sq.num_waiting != 0
+ exit!(6) unless q.empty?
+ exit!(7) unless sq.empty?
+ q.push :child_q
+ sq.push :child_sq
+ exit!(3) if q.pop != :child_q
+ exit!(4) if sq.pop != :child_sq
+ exit!(0)
+ end
+ _, s = Process.waitpid2(pid)
+ assert_predicate s, :success?, 'no segfault [ruby-core:86316] [Bug #14634]'
+
+ q.push :thq
+ sq.push :thsq
+ assert_equal :thq, thq.value
+ assert_equal :thsq, thsq.value
+
+ sq.push(1)
+ th = Thread.new { q.pop; sq.pop }
+ thsq = Thread.new { sq.push(2) }
+ Thread.pass until th.stop? && thsq.stop?
+ pid = fork do
+ exit!(1) if q.num_waiting != 0
+ exit!(2) if sq.num_waiting != 0
+ exit!(3) unless q.empty?
+ exit!(4) if sq.empty?
+ exit!(5) if sq.pop != 1
+ exit!(0)
+ end
+ _, s = Process.waitpid2(pid)
+ assert_predicate s, :success?, 'no segfault [ruby-core:86316] [Bug #14634]'
+
+ assert_predicate thsq, :stop?
+ assert_equal 1, sq.pop
+ assert_same sq, thsq.value
+ q.push('restart th')
+ assert_equal 2, th.value
+ end if Process.respond_to?(:fork)
+end
diff --git a/test/ruby/test_threadgroup.rb b/test/ruby/test_threadgroup.rb
index 80b0c15338..ec95bd6419 100644
--- a/test/ruby/test_threadgroup.rb
+++ b/test/ruby/test_threadgroup.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: false
require 'test/unit'
-require 'thread'
class TestThreadGroup < Test::Unit::TestCase
def test_thread_init
diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb
index 60bed87c9c..3b3711b805 100644
--- a/test/ruby/test_time.rb
+++ b/test/ruby/test_time.rb
@@ -236,6 +236,17 @@ class TestTime < Test::Unit::TestCase
assert_equal(1, Time.at(0, 0.001).nsec)
end
+ def test_at_with_unit
+ assert_equal(123456789, Time.at(0, 123456789, :nanosecond).nsec)
+ assert_equal(123456789, Time.at(0, 123456789, :nsec).nsec)
+ assert_equal(123456000, Time.at(0, 123456, :microsecond).nsec)
+ assert_equal(123456000, Time.at(0, 123456, :usec).nsec)
+ assert_equal(123000000, Time.at(0, 123, :millisecond).nsec)
+ assert_raise(ArgumentError){ Time.at(0, 1, 2) }
+ assert_raise(ArgumentError){ Time.at(0, 1, :invalid) }
+ assert_raise(ArgumentError){ Time.at(0, 1, nil) }
+ end
+
def test_at_rational
assert_equal(1, Time.at(Rational(1,1) / 1000000000).nsec)
assert_equal(1, Time.at(1167609600 + Rational(1,1) / 1000000000).nsec)
@@ -308,7 +319,9 @@ class TestTime < Test::Unit::TestCase
in_timezone('JST-9') do
t = Time.local(2013, 2, 24)
assert_equal('JST', Time.local(2013, 2, 24).zone)
- assert_equal('JST', Marshal.load(Marshal.dump(t)).zone)
+ t = Marshal.load(Marshal.dump(t))
+ assert_equal('JST', t.zone)
+ assert_equal('JST', (t+1).zone, '[ruby-core:81892] [Bug #13710]')
end
end
@@ -543,6 +556,10 @@ class TestTime < Test::Unit::TestCase
def test_zone
assert_zone_encoding Time.now
+ t = Time.now.utc
+ assert_equal("UTC", t.zone)
+ assert_nil(t.getlocal(0).zone)
+ assert_nil(t.getlocal("+02:00").zone)
end
def test_plus_minus_succ
@@ -834,6 +851,13 @@ class TestTime < Test::Unit::TestCase
assert_equal(8192, Time.now.strftime('%8192z').size)
end
+ def test_strftime_wide_precision
+ t2000 = get_t2000
+ s = t2000.strftime("%28c")
+ assert_equal(28, s.size)
+ assert_equal(t2000.strftime("%c"), s.strip)
+ end
+
def test_strfimte_zoneoffset
t2000 = get_t2000
t = t2000.getlocal("+09:00:00")
@@ -1043,6 +1067,29 @@ class TestTime < Test::Unit::TestCase
assert_equal(min, t.min)
assert_equal(sec, t.sec)
}
+ assert_equal(Time.local(2038,3,1), Time.local(2038,2,29))
+ assert_equal(Time.local(2038,3,2), Time.local(2038,2,30))
+ assert_equal(Time.local(2038,3,3), Time.local(2038,2,31))
+ assert_equal(Time.local(2040,2,29), Time.local(2040,2,29))
+ assert_equal(Time.local(2040,3,1), Time.local(2040,2,30))
+ assert_equal(Time.local(2040,3,2), Time.local(2040,2,31))
+ n = 2 ** 64
+ n += 400 - n % 400 # n is over 2^64 and multiple of 400
+ assert_equal(Time.local(n,2,29),Time.local(n,2,29))
+ assert_equal(Time.local(n,3,1), Time.local(n,2,30))
+ assert_equal(Time.local(n,3,2), Time.local(n,2,31))
+ n += 100
+ assert_equal(Time.local(n,3,1), Time.local(n,2,29))
+ assert_equal(Time.local(n,3,2), Time.local(n,2,30))
+ assert_equal(Time.local(n,3,3), Time.local(n,2,31))
+ n += 4
+ assert_equal(Time.local(n,2,29),Time.local(n,2,29))
+ assert_equal(Time.local(n,3,1), Time.local(n,2,30))
+ assert_equal(Time.local(n,3,2), Time.local(n,2,31))
+ n += 1
+ assert_equal(Time.local(n,3,1), Time.local(n,2,29))
+ assert_equal(Time.local(n,3,2), Time.local(n,2,30))
+ assert_equal(Time.local(n,3,3), Time.local(n,2,31))
end
def test_future
@@ -1061,6 +1108,14 @@ class TestTime < Test::Unit::TestCase
}
end
+ def test_getlocal_utc_offset
+ t = Time.gm(2000)
+ assert_equal [00, 30, 21, 31, 12, 1999], t.getlocal("-02:30").to_a[0, 6]
+ assert_equal [00, 00, 9, 1, 1, 2000], t.getlocal("+09:00").to_a[0, 6]
+ assert_equal [20, 29, 21, 31, 12, 1999], t.getlocal("-02:30:40").to_a[0, 6]
+ assert_equal [35, 10, 9, 1, 1, 2000], t.getlocal("+09:10:35").to_a[0, 6]
+ end
+
def test_getlocal_nil
now = Time.now
now2 = nil
@@ -1089,4 +1144,48 @@ class TestTime < Test::Unit::TestCase
t = Time.utc(2017, 1, 1, 1, 0, 0).getlocal("-05:00")
assert_equal("366", t.strftime("%j"))
end
+
+ def test_strftime_no_hidden_garbage
+ skip unless Thread.list.size == 1
+
+ fmt = %w(Y m d).map { |x| "%#{x}" }.join('-') # defeats optimization
+ t = Time.at(0).getutc
+ ObjectSpace.count_objects(res = {}) # creates strings on first call
+ GC.disable
+ before = ObjectSpace.count_objects(res)[:T_STRING]
+ val = t.strftime(fmt)
+ after = ObjectSpace.count_objects(res)[:T_STRING]
+ assert_equal before + 1, after, 'only new string is the created one'
+ assert_equal '1970-01-01', val
+ ensure
+ GC.enable
+ end
+
+ def test_num_exact_error
+ bad = EnvUtil.labeled_class("BadValue").new
+ x = EnvUtil.labeled_class("Inexact") do
+ def to_s; "Inexact"; end
+ define_method(:to_int) {bad}
+ define_method(:to_r) {bad}
+ end.new
+ assert_raise_with_message(TypeError, /Inexact/) {Time.at(x)}
+ end
+
+ def test_memsize
+ # Time objects are common in some code, try to keep them small
+ skip "Time object size test" if /^(?:i.?86|x86_64)-linux/ !~ RUBY_PLATFORM
+ require 'objspace'
+ t = Time.at(0)
+ size = GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
+ case size
+ when 20 then expect = 50
+ when 40 then expect = 86
+ when 48 then expect = 94
+ else
+ flunk "Unsupported RVALUE_SIZE=#{size}, update test_memsize"
+ end
+ assert_equal expect, ObjectSpace.memsize_of(t)
+ rescue LoadError => e
+ skip "failed to load objspace: #{e.message}"
+ end
end
diff --git a/test/ruby/test_time_tz.rb b/test/ruby/test_time_tz.rb
index 20a57fe7dd..9bba30e577 100644
--- a/test/ruby/test_time_tz.rb
+++ b/test/ruby/test_time_tz.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: false
require 'test/unit'
+require '-test-/time'
class TestTimeTZ < Test::Unit::TestCase
has_right_tz = true
@@ -84,6 +85,16 @@ class TestTimeTZ < Test::Unit::TestCase
has_right_tz &&= have_tz_offset?("right/America/Los_Angeles")
has_lisbon_tz &&= have_tz_offset?("Europe/Lisbon")
+ CORRECT_TOKYO_DST_1951 = with_tz("Asia/Tokyo") {
+ if Time.local(1951, 5, 6, 12, 0, 0).dst? # noon, DST
+ if Time.local(1951, 5, 6, 1, 0, 0).dst? # DST with fixed tzdata
+ Time.local(1951, 9, 8, 23, 0, 0).dst? ? "2018f" : "2018e"
+ end
+ end
+ }
+ CORRECT_KIRITIMATI_SKIP_1994 = with_tz("Pacific/Kiritimati") {
+ Time.local(1994, 12, 31, 0, 0, 0).year == 1995
+ }
def time_to_s(t)
t.to_s
@@ -97,6 +108,18 @@ class TestTimeTZ < Test::Unit::TestCase
assert_equal(expected, real, m)
end
+ def test_localtime_zone
+ t = with_tz("America/Los_Angeles") {
+ Time.local(2000, 1, 1)
+ }
+ skip "force_tz_test is false on this environment" unless t
+ z1 = t.zone
+ z2 = with_tz(tz="Asia/Singapore") {
+ t.localtime.zone
+ }
+ assert_equal(z2, z1)
+ end
+
def test_america_los_angeles
with_tz(tz="America/Los_Angeles") {
assert_time_constructor(tz, "2007-03-11 03:00:00 -0700", :local, [2007,3,11,2,0,0])
@@ -126,12 +149,19 @@ class TestTimeTZ < Test::Unit::TestCase
def test_asia_tokyo
with_tz(tz="Asia/Tokyo") {
- assert_time_constructor(tz, "1951-05-06 03:00:00 +1000", :local, [1951,5,6,2,0,0])
- assert_time_constructor(tz, "1951-05-06 03:59:59 +1000", :local, [1951,5,6,2,59,59])
+ h = CORRECT_TOKYO_DST_1951 ? 0 : 2
+ assert_time_constructor(tz, "1951-05-06 0#{h+1}:00:00 +1000", :local, [1951,5,6,h,0,0])
+ assert_time_constructor(tz, "1951-05-06 0#{h+1}:59:59 +1000", :local, [1951,5,6,h,59,59])
assert_time_constructor(tz, "2010-06-10 06:13:28 +0900", :local, [2010,6,10,6,13,28])
}
end
+ def test_asia_kuala_lumpur
+ with_tz(tz="Asia/Kuala_Lumpur") {
+ assert_time_constructor(tz, "1933-01-01 00:20:00 +0720", :local, [1933])
+ }
+ end
+
def test_canada_newfoundland
with_tz(tz="America/St_Johns") {
assert_time_constructor(tz, "2007-11-03 23:00:59 -0230", :new, [2007,11,3,23,0,59,:dst])
@@ -172,15 +202,24 @@ class TestTimeTZ < Test::Unit::TestCase
def test_pacific_kiritimati
with_tz(tz="Pacific/Kiritimati") {
- assert_time_constructor(tz, "1994-12-31 23:59:59 -1000", :local, [1994,12,31,23,59,59])
- assert_time_constructor(tz, "1995-01-02 00:00:00 +1400", :local, [1995,1,1,0,0,0])
- assert_time_constructor(tz, "1995-01-02 23:59:59 +1400", :local, [1995,1,1,23,59,59])
+ assert_time_constructor(tz, "1994-12-30 00:00:00 -1000", :local, [1994,12,30,0,0,0])
+ assert_time_constructor(tz, "1994-12-30 23:59:59 -1000", :local, [1994,12,30,23,59,59])
+ if CORRECT_KIRITIMATI_SKIP_1994
+ assert_time_constructor(tz, "1995-01-01 00:00:00 +1400", :local, [1994,12,31,0,0,0])
+ assert_time_constructor(tz, "1995-01-01 23:59:59 +1400", :local, [1994,12,31,23,59,59])
+ assert_time_constructor(tz, "1995-01-01 00:00:00 +1400", :local, [1995,1,1,0,0,0])
+ else
+ assert_time_constructor(tz, "1994-12-31 23:59:59 -1000", :local, [1994,12,31,23,59,59])
+ assert_time_constructor(tz, "1995-01-02 00:00:00 +1400", :local, [1995,1,1,0,0,0])
+ assert_time_constructor(tz, "1995-01-02 23:59:59 +1400", :local, [1995,1,1,23,59,59])
+ end
assert_time_constructor(tz, "1995-01-02 00:00:00 +1400", :local, [1995,1,2,0,0,0])
}
end
def test_right_utc
with_tz(tz="right/UTC") {
+ ::Bug::Time.reset_leap_second_info
assert_time_constructor(tz, "2008-12-31 23:59:59 UTC", :utc, [2008,12,31,23,59,59])
assert_time_constructor(tz, "2008-12-31 23:59:60 UTC", :utc, [2008,12,31,23,59,60])
assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,24,0,0])
@@ -188,6 +227,31 @@ class TestTimeTZ < Test::Unit::TestCase
}
end if has_right_tz
+ def test_right_utc_switching
+ with_tz("UTC") { # ensure no leap second timezone
+ ::Bug::Time.reset_leap_second_info
+ assert_equal(4102444800, Time.utc(2100,1,1,0,0,0).to_i)
+ with_tz(tz="right/UTC") {
+ assert_time_constructor(tz, "2008-12-31 23:59:59 UTC", :utc, [2008,12,31,23,59,59])
+ assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,23,59,60])
+ assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,24,0,0])
+ assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2009,1,1,0,0,0])
+ assert_equal(4102444800, Time.utc(2100,1,1,0,0,0).to_i)
+ }
+ }
+ with_tz("right/UTC") {
+ ::Bug::Time.reset_leap_second_info
+ assert_not_equal(4102444800, Time.utc(2100,1,1,0,0,0).to_i)
+ with_tz(tz="UTC") {
+ assert_time_constructor(tz, "2008-12-31 23:59:59 UTC", :utc, [2008,12,31,23,59,59])
+ assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,23,59,60])
+ assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,24,0,0])
+ assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2009,1,1,0,0,0])
+ assert_not_equal(4102444800, Time.utc(2100,1,1,0,0,0).to_i)
+ }
+ }
+ end if has_right_tz
+
def test_right_america_los_angeles
with_tz(tz="right/America/Los_Angeles") {
assert_time_constructor(tz, "2008-12-31 15:59:59 -0800", :local, [2008,12,31,15,59,59])
@@ -252,6 +316,7 @@ class TestTimeTZ < Test::Unit::TestCase
mesg = "#{mesg_utc}.localtime"
define_method(gen_test_name(tz)) {
with_tz(tz) {
+ ::Bug::Time.reset_leap_second_info
t = nil
assert_nothing_raised(mesg) { t = Time.utc(*u) }
assert_equal(expected_utc, time_to_s(t), mesg_utc)
@@ -329,10 +394,23 @@ America/Managua Wed Jan 1 05:00:00 1997 UTC = Tue Dec 31 23:00:00 1996 CST isd
Asia/Singapore Sun Aug 8 16:30:00 1965 UTC = Mon Aug 9 00:00:00 1965 SGT isdst=0 gmtoff=27000
Asia/Singapore Thu Dec 31 16:29:59 1981 UTC = Thu Dec 31 23:59:59 1981 SGT isdst=0 gmtoff=27000
Asia/Singapore Thu Dec 31 16:30:00 1981 UTC = Fri Jan 1 00:30:00 1982 SGT isdst=0 gmtoff=28800
+End
+ gen_zdump_test CORRECT_TOKYO_DST_1951 ? <<'End' + (CORRECT_TOKYO_DST_1951 < "2018f" ? <<'2018e' : <<'2018f') : <<'End'
+Asia/Tokyo Sat May 5 14:59:59 1951 UTC = Sat May 5 23:59:59 1951 JST isdst=0 gmtoff=32400
+Asia/Tokyo Sat May 5 15:00:00 1951 UTC = Sun May 6 01:00:00 1951 JDT isdst=1 gmtoff=36000
+End
+Asia/Tokyo Sat Sep 8 13:59:59 1951 UTC = Sat Sep 8 23:59:59 1951 JDT isdst=1 gmtoff=36000
+Asia/Tokyo Sat Sep 8 14:00:00 1951 UTC = Sat Sep 8 23:00:00 1951 JST isdst=0 gmtoff=32400
+2018e
+Asia/Tokyo Sat Sep 8 14:59:59 1951 UTC = Sun Sep 9 00:59:59 1951 JDT isdst=1 gmtoff=36000
+Asia/Tokyo Sat Sep 8 15:00:00 1951 UTC = Sun Sep 9 00:00:00 1951 JST isdst=0 gmtoff=32400
+2018f
Asia/Tokyo Sat May 5 16:59:59 1951 UTC = Sun May 6 01:59:59 1951 JST isdst=0 gmtoff=32400
Asia/Tokyo Sat May 5 17:00:00 1951 UTC = Sun May 6 03:00:00 1951 JDT isdst=1 gmtoff=36000
Asia/Tokyo Fri Sep 7 15:59:59 1951 UTC = Sat Sep 8 01:59:59 1951 JDT isdst=1 gmtoff=36000
Asia/Tokyo Fri Sep 7 16:00:00 1951 UTC = Sat Sep 8 01:00:00 1951 JST isdst=0 gmtoff=32400
+End
+ gen_zdump_test <<'End'
America/St_Johns Sun Mar 11 03:30:59 2007 UTC = Sun Mar 11 00:00:59 2007 NST isdst=0 gmtoff=-12600
America/St_Johns Sun Mar 11 03:31:00 2007 UTC = Sun Mar 11 01:01:00 2007 NDT isdst=1 gmtoff=-9000
America/St_Johns Sun Nov 4 02:30:59 2007 UTC = Sun Nov 4 00:00:59 2007 NDT isdst=1 gmtoff=-9000
@@ -349,9 +427,18 @@ Europe/London Sun Aug 10 00:59:59 1947 UTC = Sun Aug 10 02:59:59 1947 BDST isds
Europe/London Sun Aug 10 01:00:00 1947 UTC = Sun Aug 10 02:00:00 1947 BST isdst=1 gmtoff=3600
Europe/London Sun Nov 2 01:59:59 1947 UTC = Sun Nov 2 02:59:59 1947 BST isdst=1 gmtoff=3600
Europe/London Sun Nov 2 02:00:00 1947 UTC = Sun Nov 2 02:00:00 1947 GMT isdst=0 gmtoff=0
+End
+ if CORRECT_KIRITIMATI_SKIP_1994
+ gen_zdump_test <<'End'
+Pacific/Kiritimati Sat Dec 31 09:59:59 1994 UTC = Fri Dec 30 23:59:59 1994 LINT isdst=0 gmtoff=-36000
+Pacific/Kiritimati Sat Dec 31 10:00:00 1994 UTC = Sun Jan 1 00:00:00 1995 LINT isdst=0 gmtoff=50400
+End
+ else
+ gen_zdump_test <<'End'
Pacific/Kiritimati Sun Jan 1 09:59:59 1995 UTC = Sat Dec 31 23:59:59 1994 LINT isdst=0 gmtoff=-36000
Pacific/Kiritimati Sun Jan 1 10:00:00 1995 UTC = Mon Jan 2 00:00:00 1995 LINT isdst=0 gmtoff=50400
End
+ end
gen_zdump_test <<'End' if has_right_tz
right/America/Los_Angeles Fri Jun 30 23:59:60 1972 UTC = Fri Jun 30 16:59:60 1972 PDT isdst=1 gmtoff=-25200
right/America/Los_Angeles Wed Dec 31 23:59:60 2008 UTC = Wed Dec 31 15:59:60 2008 PST isdst=0 gmtoff=-28800
@@ -399,5 +486,206 @@ End
gen_variational_zdump_test "lisbon", <<'End' if has_lisbon_tz
Europe/Lisbon Mon Jan 1 00:36:31 1912 UTC = Sun Dec 31 23:59:59 1911 LMT isdst=0 gmtoff=-2192
Europe/Lisbon Mon Jan 1 00:36:44 1912 UT = Sun Dec 31 23:59:59 1911 LMT isdst=0 gmtoff=-2205
+Europe/Lisbon Sun Dec 31 23:59:59 1911 UT = Sun Dec 31 23:23:14 1911 LMT isdst=0 gmtoff=-2205
End
+
+ class TZ
+ attr_reader :name, :abbr, :offset
+
+ def initialize(name, abbr, offset)
+ @name = name
+ @abbr = abbr
+ @offset = offset
+ end
+
+ def local_to_utc(t)
+ t - @offset
+ end
+
+ def utc_to_local(t)
+ t + @offset
+ end
+
+ def abbr(t)
+ @abbr
+ end
+
+ def ==(other)
+ @name == other.name and @abbr == other.abbr(0) and @offset == other.offset
+ end
+
+ def inspect
+ "#<TZ: #@name #@abbr #@offset>"
+ end
+ end
+end
+
+module TestTimeTZ::WithTZ
+ def subtest_new(time_class, tz, tzarg, tzname, abbr, utc_offset)
+ t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg)
+ assert_equal([2018, 9, 1, 12, 0, 0, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone])
+ h, m = (-utc_offset / 60).divmod(60)
+ assert_equal(time_class.utc(2018, 9, 1, 12+h, m, 0).to_i, t.to_i)
+ end
+
+ def subtest_getlocal(time_class, tz, tzarg, tzname, abbr, utc_offset)
+ t = time_class.utc(2018, 9, 1, 12, 0, 0).getlocal(tzarg)
+ h, m = (utc_offset / 60).divmod(60)
+ assert_equal([2018, 9, 1, 12+h, m, 0, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone])
+ assert_equal(time_class.utc(2018, 9, 1, 12, 0, 0), t)
+ end
+
+ def subtest_strftime(time_class, tz, tzarg, tzname, abbr, utc_offset)
+ t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg)
+ h, m = (utc_offset.abs / 60).divmod(60)
+ h = -h if utc_offset < 0
+ assert_equal("%+.2d%.2d %s" % [h, m, abbr], t.strftime("%z %Z"))
+ end
+
+ def subtest_plus(time_class, tz, tzarg, tzname, abbr, utc_offset)
+ t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg) + 4000
+ assert_equal([2018, 9, 1, 13, 6, 40, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone])
+ m, s = (4000-utc_offset).divmod(60)
+ h, m = m.divmod(60)
+ assert_equal(time_class.utc(2018, 9, 1, 12+h, m, s), t)
+ assert_equal(6, t.wday)
+ assert_equal(244, t.yday)
+ end
+
+ def subtest_at(time_class, tz, tzarg, tzname, abbr, utc_offset)
+ h, m = (utc_offset / 60).divmod(60)
+ utc = time_class.utc(2018, 9, 1, 12, 0, 0)
+ t = time_class.at(utc, in: tzarg)
+ assert_equal([2018, 9, 1, 12+h, m, 0, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone])
+ assert_equal(utc.to_i, t.to_i)
+ utc = utc.to_i
+ t = time_class.at(utc, in: tzarg)
+ assert_equal([2018, 9, 1, 12+h, m, 0, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone])
+ assert_equal(utc, t.to_i)
+ end
+
+ def subtest_marshal(time_class, tz, tzarg, tzname, abbr, utc_offset)
+ t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg)
+ t2 = Marshal.load(Marshal.dump(t))
+ assert_equal(t, t2)
+ assert_equal(t.utc_offset, t2.utc_offset)
+ assert_equal(t.utc_offset, (t2+1).utc_offset)
+ assert_instance_of(t.zone.class, t2.zone)
+ end
+
+ def test_invalid_zone
+ make_timezone("INVALID", "INV", 0)
+ rescue => e
+ assert_kind_of(StandardError, e)
+ else
+ assert false, "ArgumentError expected but nothing was raised."
+ end
+
+ def nametest_marshal_compatibility(time_class, tzname, abbr, utc_offset)
+ data = [
+ "\x04\x08Iu:".b, Marshal.dump(time_class)[3..-1],
+ "\x0d""\xEF\xA7\x1D\x80\x00\x00\x00\x00".b,
+ Marshal.dump({offset: utc_offset, zone: abbr})[3..-1],
+ ].join('')
+ t = Marshal.load(data)
+ assert_equal(utc_offset, t.utc_offset)
+ assert_equal(utc_offset, (t+1).utc_offset)
+ # t.zone may be a mere String or timezone object.
+ end
+
+ ZONES = {
+ "Asia/Tokyo" => ["JST", +9*3600],
+ "America/Los_Angeles" => ["PDT", -7*3600],
+ "Africa/Ndjamena" => ["WAT", +1*3600],
+ }
+
+ def make_timezone(tzname, abbr, utc_offset)
+ self.class::TIME_CLASS.find_timezone(tzname)
+ end
+
+ instance_methods(false).grep(/\Asub(?=test_)/) do |subtest|
+ test = $'
+ ZONES.each_pair do |tzname, (abbr, utc_offset)|
+ define_method("#{test}@#{tzname}") do
+ tz = make_timezone(tzname, abbr, utc_offset)
+ time_class = self.class::TIME_CLASS
+ __send__(subtest, time_class, tz, tz, tzname, abbr, utc_offset)
+ __send__(subtest, time_class, tz, tzname, tzname, abbr, utc_offset)
+ end
+ end
+ end
+
+ instance_methods(false).grep(/\Aname(?=test_)/) do |subtest|
+ test = $'
+ ZONES.each_pair do |tzname, (abbr, utc_offset)|
+ define_method("#{test}@#{tzname}") do
+ time_class = self.class::TIME_CLASS
+ __send__(subtest, time_class, tzname, abbr, utc_offset)
+ end
+ end
+ end
+end
+
+class TestTimeTZ::DummyTZ < Test::Unit::TestCase
+ include TestTimeTZ::WithTZ
+
+ class TIME_CLASS < ::Time
+ ZONES = TestTimeTZ::WithTZ::ZONES
+ def self.find_timezone(tzname)
+ tz = ZONES[tzname] or raise ArgumentError, "Unknown timezone: #{name}"
+ TestTimeTZ::TZ.new(tzname, *tz)
+ end
+ end
+
+ def self.make_timezone(tzname, abbr, utc_offset)
+ TestTimeTZ::TZ.new(tzname, abbr, utc_offset)
+ end
+end
+
+begin
+ require "tzinfo"
+rescue LoadError
+else
+ class TestTimeTZ::GemTZInfo < Test::Unit::TestCase
+ include TestTimeTZ::WithTZ
+
+ class TIME_CLASS < ::Time
+ def self.find_timezone(tzname)
+ TZInfo::Timezone.get(tzname)
+ end
+ end
+
+ def tz
+ @tz ||= TZInfo::Timezone.get(tzname)
+ end
+ end
+
+ def test_fractional_second
+ x = Object.new
+ def x.local_to_utc(t); t + 8*3600; end
+ def x.utc_to_local(t); t - 8*3600; end
+
+ t1 = Time.new(2020,11,11,12,13,14.124r, '-08:00')
+ t2 = Time.new(2020,11,11,12,13,14.124r, x)
+ assert_equal(t1, t2)
+ end
+end
+
+begin
+ require "timezone"
+rescue LoadError
+else
+ class TestTimeTZ::GemTimezone < Test::Unit::TestCase
+ include TestTimeTZ::WithTZ
+
+ class TIME_CLASS < ::Time
+ def self.find_timezone(name)
+ Timezone.fetch(name)
+ end
+ end
+
+ def tz
+ @tz ||= Timezone[tzname]
+ end
+ end
end
diff --git a/test/ruby/test_transcode.rb b/test/ruby/test_transcode.rb
index b65a8c2b98..7f81cbf424 100644
--- a/test/ruby/test_transcode.rb
+++ b/test/ruby/test_transcode.rb
@@ -13,8 +13,8 @@ class TestTranscode < Test::Unit::TestCase
assert_raise(Encoding::UndefinedConversionError) { "\x80".encode('utf-8','ASCII-8BIT') }
assert_raise(Encoding::InvalidByteSequenceError) { "\x80".encode('utf-8','US-ASCII') }
assert_raise(Encoding::UndefinedConversionError) { "\xA5".encode('utf-8','iso-8859-3') }
- assert_raise(RuntimeError) { 'hello'.freeze.encode!('iso-8859-1') }
- assert_raise(RuntimeError) { '\u3053\u3093\u306b\u3061\u306f'.freeze.encode!('iso-8859-1') } # ã“ã‚“ã«ã¡ã¯
+ assert_raise(FrozenError) { 'hello'.freeze.encode!('iso-8859-1') }
+ assert_raise(FrozenError) { '\u3053\u3093\u306b\u3061\u306f'.freeze.encode!('iso-8859-1') } # ã“ã‚“ã«ã¡ã¯
end
def test_arguments
@@ -484,7 +484,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u2580", "\xDF", 'IBM775') # â–€
check_both_ways("\u00D3", "\xE0", 'IBM775') # Ó
check_both_ways("\u2019", "\xEF", 'IBM775') # ’
- check_both_ways("\u00AD", "\xF0", 'IBM775') # osft hyphen
+ check_both_ways("\u00AD", "\xF0", 'IBM775') # soft hyphen
check_both_ways("\u00A0", "\xFF", 'IBM775') # non-breaking space
end
@@ -503,7 +503,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u2580", "\xDF", 'IBM852') # â–€
check_both_ways("\u00D3", "\xE0", 'IBM852') # Ó
check_both_ways("\u00B4", "\xEF", 'IBM852') # ´
- check_both_ways("\u00AD", "\xF0", 'IBM852') # osft hyphen
+ check_both_ways("\u00AD", "\xF0", 'IBM852') # soft hyphen
check_both_ways("\u00A0", "\xFF", 'IBM852') # non-breaking space
end
@@ -522,7 +522,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u2580", "\xDF", 'IBM855') # â–€
check_both_ways("\u042F", "\xE0", 'IBM855') # Я
check_both_ways("\u2116", "\xEF", 'IBM855') # â„–
- check_both_ways("\u00AD", "\xF0", 'IBM855') # osft hyphen
+ check_both_ways("\u00AD", "\xF0", 'IBM855') # soft hyphen
check_both_ways("\u00A0", "\xFF", 'IBM855') # non-breaking space
end
@@ -998,6 +998,92 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u00A0", "\xFF", 'CP855') # non-breaking space
end
+ def test_ill_formed_utf_8_replace
+ fffd1 = "\uFFFD".encode 'UTF-16BE'
+ fffd2 = "\uFFFD\uFFFD".encode 'UTF-16BE'
+ fffd3 = "\uFFFD\uFFFD\uFFFD".encode 'UTF-16BE'
+ fffd4 = "\uFFFD\uFFFD\uFFFD\uFFFD".encode 'UTF-16BE'
+ fffd5 = "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD".encode 'UTF-16BE'
+ fffd6 = "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD".encode 'UTF-16BE'
+
+ assert_equal fffd1, "\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xC3".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xDF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xE0".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xE0\xA0".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xE0\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xE1".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xEC".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xE1\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xEC\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+
+ assert_equal fffd2, "\xC0\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xC0\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xC1\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xC1\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xE0\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xE0\x9F".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xE0\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xE0\x9F\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xED\xA0".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xED\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xED\xA0\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xED\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xF0\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xF0\x8F".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xF0\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xF0\x8F\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xF0\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xF0\x8F\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xF4\x90".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xF4\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xF4\x90\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xF4\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xF4\x90\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xF4\xBF\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xF5\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xF7\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xF5\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xF7\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xF5\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xF7\xBF\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xF8".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xFB".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xF8\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xFB\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xF8\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xFB\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xF8\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xFB\xBF\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd5, "\xF8\x80\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd5, "\xFB\xBF\xBF\xBF\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xFC".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xFD".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xFC\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xFD\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xFC\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xFD\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xFC\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xFD\xBF\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd5, "\xFC\x80\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd5, "\xFD\xBF\xBF\xBF\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd6, "\xFC\x80\x80\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd6, "\xFD\xBF\xBF\xBF\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xFE".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xFF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xFE\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xFF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xFE\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xFF\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xFE\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xFF\xBF\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd5, "\xFE\x80\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd5, "\xFF\xBF\xBF\xBF\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd6, "\xFE\x80\x80\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd6, "\xFF\xBF\xBF\xBF\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ end
+
def check_utf_16_both_ways(utf8, raw)
copy = raw.dup
0.step(copy.length-1, 2) { |i| copy[i+1], copy[i] = copy[i], copy[i+1] }
@@ -2075,6 +2161,14 @@ class TestTranscode < Test::Unit::TestCase
assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback.method(:escape)))
end
+ def test_fallback_aref
+ fallback = Object.new
+ def fallback.[](x)
+ "U+%.4X" % x.unpack("U")
+ end
+ assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback))
+ end
+
bug8940 = '[ruby-core:57318] [Bug #8940]'
%w[UTF-32 UTF-16].each do |enc|
define_method("test_pseudo_encoding_inspect(#{enc})") do
@@ -2094,17 +2188,19 @@ class TestTranscode < Test::Unit::TestCase
def test_valid_dummy_encoding
bug9314 = '[ruby-core:59354] [Bug #9314]'
- assert_separately(%W[- -- #{bug9314}], <<-'end;')
- bug = ARGV.shift
- result = assert_nothing_raised(TypeError, bug) {break "test".encode(Encoding::UTF_16)}
- assert_equal("\xFE\xFF\x00t\x00e\x00s\x00t", result.b, bug)
- result = assert_nothing_raised(TypeError, bug) {break "test".encode(Encoding::UTF_32)}
- assert_equal("\x00\x00\xFE\xFF\x00\x00\x00t\x00\x00\x00e\x00\x00\x00s\x00\x00\x00t", result.b, bug)
+ assert_separately(%W[- -- #{bug9314}], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ bug = ARGV.shift
+ result = assert_nothing_raised(TypeError, bug) {break "test".encode(Encoding::UTF_16)}
+ assert_equal("\xFE\xFF\x00t\x00e\x00s\x00t", result.b, bug)
+ result = assert_nothing_raised(TypeError, bug) {break "test".encode(Encoding::UTF_32)}
+ assert_equal("\x00\x00\xFE\xFF\x00\x00\x00t\x00\x00\x00e\x00\x00\x00s\x00\x00\x00t", result.b, bug)
end;
end
def test_loading_race
- assert_separately([], <<-'end;') #do
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
bug11277 = '[ruby-dev:49106] [Bug #11277]'
num = 2
th = (0...num).map do |i|
@@ -2121,6 +2217,17 @@ class TestTranscode < Test::Unit::TestCase
end;
end
+ def test_scrub_encode_with_coderange
+ bug = '[ruby-core:82674] [Bug #13874]'
+ s = "\xe5".b
+ u = Encoding::UTF_8
+ assert_equal("?", s.encode(u, u, invalid: :replace, replace: "?"),
+ "should replace invalid byte")
+ assert_predicate(s, :valid_encoding?, "any char is valid in binary")
+ assert_equal("?", s.encode(u, u, invalid: :replace, replace: "?"),
+ "#{bug} coderange should not have side effects")
+ end
+
def test_universal_newline
bug11324 = '[ruby-core:69841] [Bug #11324]'
usascii = Encoding::US_ASCII
diff --git a/test/ruby/test_undef.rb b/test/ruby/test_undef.rb
index 6d513a238f..e0add7c3ab 100644
--- a/test/ruby/test_undef.rb
+++ b/test/ruby/test_undef.rb
@@ -25,7 +25,7 @@ class TestUndef < Test::Unit::TestCase
y = Undef1.new
assert_equal "bar", y.bar
z = Undef2.new
- assert_raise(NoMethodError) { z.foo }
+ assert_raise(NoMethodError) { z.bar }
end
def test_special_const_undef
diff --git a/test/ruby/test_unicode_escape.rb b/test/ruby/test_unicode_escape.rb
index e0ca430a4a..5913bb0130 100644
--- a/test/ruby/test_unicode_escape.rb
+++ b/test/ruby/test_unicode_escape.rb
@@ -256,16 +256,17 @@ EOS
assert_raise(SyntaxError) { eval %q("\ughij") } # bad hex digits
assert_raise(SyntaxError) { eval %q("\u{ghij}") } # bad hex digits
- assert_raise(SyntaxError) { eval %q("\u{123 456 }")} # extra space
- assert_raise(SyntaxError) { eval %q("\u{ 123 456}")} # extra space
- assert_raise(SyntaxError) { eval %q("\u{123 456}")} # extra space
+ assert_raise_with_message(SyntaxError, /invalid/) {
+ eval %q("\u{123.}") # bad char
+ }
-# The utf-8 encoding object currently does not object to codepoints
-# in the surrogate blocks, so these do not raise an error.
-# assert_raise(SyntaxError) { "\uD800" } # surrogate block
-# assert_raise(SyntaxError) { "\uDCBA" } # surrogate block
-# assert_raise(SyntaxError) { "\uDFFF" } # surrogate block
-# assert_raise(SyntaxError) { "\uD847\uDD9A" } # surrogate pair
+ # assert_raise(SyntaxError) { eval %q("\u{123 456 }")} # extra space
+ # assert_raise(SyntaxError) { eval %q("\u{ 123 456}")} # extra space
+ # assert_raise(SyntaxError) { eval %q("\u{123 456}")} # extra space
+ assert_raise(SyntaxError) { eval %q("\uD800") } # surrogate block
+ assert_raise(SyntaxError) { eval %q("\uDCBA") } # surrogate block
+ assert_raise(SyntaxError) { eval %q("\uDFFF") } # surrogate block
+ assert_raise(SyntaxError) { eval %q("\uD847\uDD9A") } # surrogate pair
end
end
diff --git a/test/ruby/test_variable.rb b/test/ruby/test_variable.rb
index 4606f725c3..a9b1fd50ff 100644
--- a/test/ruby/test_variable.rb
+++ b/test/ruby/test_variable.rb
@@ -116,7 +116,7 @@ class TestVariable < Test::Unit::TestCase
assert_in_out_err(["-e", "$0='t'*1000;print $0"], "", /\At+\z/, [])
end
- def test_global_variable_poped
+ def test_global_variable_popped
assert_nothing_raised {
EnvUtil.suppress_warning {
eval("$foo; 1")
@@ -124,7 +124,7 @@ class TestVariable < Test::Unit::TestCase
}
end
- def test_constant_poped
+ def test_constant_popped
assert_nothing_raised {
EnvUtil.suppress_warning {
eval("TestVariable::Gods; 1")
@@ -137,14 +137,14 @@ class TestVariable < Test::Unit::TestCase
assert_empty v.instance_variables
msg = "can't modify frozen #{v.class}"
- assert_raise_with_message(RuntimeError, msg) do
+ assert_raise_with_message(FrozenError, msg) do
v.instance_variable_set(:@foo, :bar)
end
assert_nil EnvUtil.suppress_warning {v.instance_variable_get(:@foo)}
assert_not_send([v, :instance_variable_defined?, :@foo])
- assert_raise_with_message(RuntimeError, msg) do
+ assert_raise_with_message(FrozenError, msg) do
v.remove_instance_variable(:@foo)
end
end
diff --git a/test/ruby/test_vm_dump.rb b/test/ruby/test_vm_dump.rb
new file mode 100644
index 0000000000..68f0fa7f27
--- /dev/null
+++ b/test/ruby/test_vm_dump.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+require 'test/unit'
+
+class TestVMDump < Test::Unit::TestCase
+ def assert_darwin_vm_dump_works(args)
+ skip if RUBY_PLATFORM !~ /darwin/
+ assert_in_out_err(args, "", [], /^\[IMPORTANT\]/)
+ end
+
+ def test_darwin_invalid_call
+ assert_darwin_vm_dump_works(['-rfiddle', '-eFiddle::Function.new(Fiddle::Pointer.new(1), [], Fiddle::TYPE_VOID).call'])
+ end
+
+ def test_darwin_segv_in_syscall
+ assert_darwin_vm_dump_works('-e1.times{Process.kill :SEGV,$$}')
+ end
+
+ def test_darwin_invalid_access
+ assert_darwin_vm_dump_works(['-rfiddle', '-eFiddle.dlunwrap(100).class'])
+ end
+end
diff --git a/test/ruby/test_weakmap.rb b/test/ruby/test_weakmap.rb
index 15463bb030..fb57903c98 100644
--- a/test/ruby/test_weakmap.rb
+++ b/test/ruby/test_weakmap.rb
@@ -28,17 +28,27 @@ class TestWeakMap < Test::Unit::TestCase
assert_raise(ArgumentError) {@wm[x] = :foo}
end
- def test_include?
- m = __callee__[/test_(.*)/, 1]
- k = "foo"
+ def assert_weak_include(m, k, n = 100)
+ if n > 0
+ return assert_weak_include(m, k, n-1)
+ end
1.times do
x = Object.new
@wm[k] = x
assert_send([@wm, m, k])
assert_not_send([@wm, m, "FOO".downcase])
- x = nil
+ x = Object.new
+ end
+ end
+
+ def test_include?
+ m = __callee__[/test_(.*)/, 1]
+ k = "foo"
+ 1.times do
+ assert_weak_include(m, k)
end
GC.start
+ skip('TODO: failure introduced from r60440')
assert_not_send([@wm, m, k])
end
alias test_member? test_include?
diff --git a/test/ruby/test_yield.rb b/test/ruby/test_yield.rb
index 0690d3cdf4..9b2b2f37e0 100644
--- a/test/ruby/test_yield.rb
+++ b/test/ruby/test_yield.rb
@@ -245,7 +245,7 @@ class TestRubyYieldGen < Test::Unit::TestCase
throw :emuerror, ArgumentError
end
else
- if args.length != params.length and !(args.length == 1 and Array === args[0] and args[0].length == params.length)
+ if args.length != params.length
throw :emuerror, ArgumentError
end
end
diff --git a/test/rubygems/ca_cert.pem b/test/rubygems/ca_cert.pem
index 5207531bc2..b3977e26ad 100644
--- a/test/rubygems/ca_cert.pem
+++ b/test/rubygems/ca_cert.pem
@@ -1,68 +1,77 @@
------BEGIN CERTIFICATE-----
-MIID0DCCArigAwIBAgIBADANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGDAJKUDES
-MBAGA1UECgwJSklOLkdSLkpQMQwwCgYDVQQLDANSUlIxCzAJBgNVBAMMAkNBMB4X
-DTA0MDEzMDAwNDIzMloXDTM2MDEyMjAwNDIzMlowPDELMAkGA1UEBgwCSlAxEjAQ
-BgNVBAoMCUpJTi5HUi5KUDEMMAoGA1UECwwDUlJSMQswCQYDVQQDDAJDQTCCASIw
-DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANbv0x42BTKFEQOE+KJ2XmiSdZpR
-wjzQLAkPLRnLB98tlzs4xo+y4RyY/rd5TT9UzBJTIhP8CJi5GbS1oXEerQXB3P0d
-L5oSSMwGGyuIzgZe5+vZ1kgzQxMEKMMKlzA73rbMd4Jx3u5+jdbP0EDrPYfXSvLY
-bS04n2aX7zrN3x5KdDrNBfwBio2/qeaaj4+9OxnwRvYP3WOvqdW0h329eMfHw0pi
-JI0drIVdsEqClUV4pebT/F+CPUPkEh/weySgo9wANockkYu5ujw2GbLFcO5LXxxm
-dEfcVr3r6t6zOA4bJwL0W/e6LBcrwiG/qPDFErhwtgTLYf6Er67SzLyA66UCAwEA
-AaOB3DCB2TAPBgNVHRMBAf8EBTADAQH/MDEGCWCGSAGG+EIBDQQkFiJSdWJ5L09w
-ZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBRJ7Xd380KzBV7f
-USKIQ+O/vKbhDzAOBgNVHQ8BAf8EBAMCAQYwZAYDVR0jBF0wW4AUSe13d/NCswVe
-31EiiEPjv7ym4Q+hQKQ+MDwxCzAJBgNVBAYMAkpQMRIwEAYDVQQKDAlKSU4uR1Iu
-SlAxDDAKBgNVBAsMA1JSUjELMAkGA1UEAwwCQ0GCAQAwDQYJKoZIhvcNAQEFBQAD
-ggEBAIu/mfiez5XN5tn2jScgShPgHEFJBR0BTJBZF6xCk0jyqNx/g9HMj2ELCuK+
-r/Y7KFW5c5M3AQ+xWW0ZSc4kvzyTcV7yTVIwj2jZ9ddYMN3nupZFgBK1GB4Y05GY
-MJJFRkSu6d/Ph5ypzBVw2YMT/nsOo5VwMUGLgS7YVjU+u/HNWz80J3oO17mNZllj
-PvORJcnjwlroDnS58KoJ7GDgejv3ESWADvX1OHLE4cRkiQGeLoEU4pxdCxXRqX0U
-PbwIkZN9mXVcrmPHq8MWi4eC/V7hnbZETMHuWhUoiNdOEfsAXr3iP4KjyyRdwc7a
-d/xgcK06UVQRL/HbEYGiQL056mc=
------END CERTIFICATE-----
-
------BEGIN CERTIFICATE-----
-MIIDaDCCAlCgAwIBAgIBATANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGDAJKUDES
-MBAGA1UECgwJSklOLkdSLkpQMQwwCgYDVQQLDANSUlIxCzAJBgNVBAMMAkNBMB4X
-DTA0MDEzMDAwNDMyN1oXDTM1MDEyMjAwNDMyN1owPzELMAkGA1UEBgwCSlAxEjAQ
-BgNVBAoMCUpJTi5HUi5KUDEMMAoGA1UECwwDUlJSMQ4wDAYDVQQDDAVTdWJDQTCC
-ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ0Ou7AyRcRXnB/kVHv/6kwe
-ANzgg/DyJfsAUqW90m7Lu1nqyug8gK0RBd77yU0w5HOAMHTVSdpjZK0g2sgx4Mb1
-d/213eL9TTl5MRVEChTvQr8q5DVG/8fxPPE7fMI8eOAzd98/NOAChk+80r4Sx7fC
-kGVEE1bKwY1MrUsUNjOY2d6t3M4HHV3HX1V8ShuKfsHxgCmLzdI8U+5CnQedFgkm
-3e+8tr8IX5RR1wA1Ifw9VadF7OdI/bGMzog/Q8XCLf+WPFjnK7Gcx6JFtzF6Gi4x
-4dp1Xl45JYiVvi9zQ132wu8A1pDHhiNgQviyzbP+UjcB/tsOpzBQF8abYzgEkWEC
-AwEAAaNyMHAwDwYDVR0TAQH/BAUwAwEB/zAxBglghkgBhvhCAQ0EJBYiUnVieS9P
-cGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUlCjXWLsReYzH
-LzsxwVnCXmKoB/owCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQCJ/OyN
-rT8Cq2Y+G2yA/L1EMRvvxwFBqxavqaqHl/6rwsIBFlB3zbqGA/0oec6MAVnYynq4
-c4AcHTjx3bQ/S4r2sNTZq0DH4SYbQzIobx/YW8PjQUJt8KQdKMcwwi7arHP7A/Ha
-LKu8eIC2nsUBnP4NhkYSGhbmpJK+PFD0FVtD0ZIRlY/wsnaZNjWWcnWF1/FNuQ4H
-ySjIblqVQkPuzebv3Ror6ZnVDukn96Mg7kP4u6zgxOeqlJGRe1M949SS9Vudjl8X
-SF4aZUUB9pQGhsqQJVqaz2OlhGOp9D0q54xko/rekjAIcuDjl1mdX4F2WRrzpUmZ
-uY/bPeOBYiVsOYVe
------END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 0 (0x0)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=JP, ST=Tokyo, O=RubyGemsTest, CN=CA
+ Validity
+ Not Before: Jan 1 00:00:00 2009 GMT
+ Not After : Dec 31 23:59:59 2049 GMT
+ Subject: C=JP, ST=Tokyo, O=RubyGemsTest, CN=CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:c8:19:2c:5a:1d:4d:2a:65:1d:9a:0b:d6:3a:5c:
+ 5f:54:90:ac:17:6f:58:18:8f:e6:0f:33:36:ca:a0:
+ 92:02:b8:49:85:96:e9:74:16:14:40:67:98:4a:1f:
+ 4d:1c:d8:0b:c4:4e:f8:78:0a:68:70:39:d8:66:64:
+ c6:d5:ca:49:e9:02:c7:1a:1c:03:ba:a1:85:68:0a:
+ 03:05:27:b5:7f:97:21:94:20:f3:fe:ea:2e:f5:2e:
+ 99:34:6b:e0:e7:96:ca:51:4e:4d:40:48:09:d6:5f:
+ 64:7b:e5:df:eb:3d:44:bf:42:25:f7:84:c7:2d:22:
+ e0:7f:00:37:c6:c3:16:75:75:37:6a:e5:56:da:1c:
+ 77:37:3c:00:d3:1f:f4:9d:3b:27:08:ff:cd:cf:1e:
+ 60:74:65:90:c2:59:b4:12:3e:a0:7f:22:47:87:ff:
+ 52:f3:47:39:d1:91:02:1c:bb:8c:c9:20:1f:00:db:
+ d1:3a:b0:e0:ba:ee:55:05:8f:1a:f8:1e:dd:6d:83:
+ 1c:1d:18:01:44:92:27:22:f1:2a:07:fe:43:83:08:
+ 82:d3:2b:f1:ec:b1:68:b3:f8:94:1b:81:29:54:01:
+ 56:12:54:66:ba:60:e7:5c:27:04:4d:a3:61:e3:f9:
+ 8f:86:53:0b:83:eb:1f:1d:89:0c:83:66:88:c8:50:
+ 8d:c5
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ D6:DB:87:AD:D3:45:FC:D4:8D:6B:2B:97:F4:CF:95:08:B6:FA:62:A4
+ X509v3 Authority Key Identifier:
+ keyid:D6:DB:87:AD:D3:45:FC:D4:8D:6B:2B:97:F4:CF:95:08:B6:FA:62:A4
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ Signature Algorithm: sha256WithRSAEncryption
+ 06:92:f7:9a:0f:40:da:1a:7f:9f:0c:9e:04:37:4d:be:a4:1e:
+ 86:65:b3:4a:be:87:13:a1:e4:6b:3b:d6:58:9d:ca:f8:ba:6d:
+ e4:dd:de:c5:e3:a2:ec:ef:32:2d:c0:06:01:3a:d5:81:5a:e1:
+ e4:f7:5f:68:67:ea:cd:28:90:b1:9c:82:d3:4e:00:51:b6:eb:
+ d5:8d:ec:ab:c3:18:b2:8b:8d:5b:63:6d:f8:f5:40:c6:c6:7e:
+ 72:7b:ed:98:c5:5e:24:b9:ad:4f:5b:8f:1d:53:a3:d7:6a:4f:
+ 07:2e:6a:b6:63:5c:dc:05:22:ac:77:af:b0:72:9d:39:6f:77:
+ 9c:45:8b:ad:de:e8:bf:6a:b5:87:0b:58:47:af:11:1a:9e:84:
+ 25:21:68:48:2a:b3:3c:5a:97:54:20:03:bd:87:34:dd:db:24:
+ a6:c7:50:e9:6c:87:55:f2:e5:33:9c:83:8f:8c:9e:f3:3a:38:
+ a0:92:a1:a7:c4:89:31:bd:33:83:11:dd:ad:bb:e0:47:19:bb:
+ 62:6c:49:58:b3:13:12:c3:d0:dd:02:5f:6f:4f:13:07:6d:aa:
+ 7b:2c:46:5a:74:52:6d:13:10:9c:f7:3d:5d:84:5b:b8:5b:a9:
+ c5:ae:56:4b:9a:8c:e2:fd:7f:55:80:cb:b0:2d:56:d7:a4:3c:
+ cf:3c:b2:ff
-----BEGIN CERTIFICATE-----
-MIIDtTCCAp2gAwIBAgIJANz6ehBcVuuiMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
-BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
-aWRnaXRzIFB0eSBMdGQwHhcNMTMwNTAxMTQ0NTQxWhcNMjMwMzEwMTQ0NTQxWjBF
-MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
-ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEAzlpZwhEYoALOEKU4lmMw5l3YI/gadzDOoELtdcidvVvovKK8IIOTDwbA
-3XcjwV0UPGEPOK4Uk1aD0EKkOQVg8ivSre2a3FFGffs2kXck+doJMzAA+pf8tvFk
-QsETVOurOp74GN+er2xbbRSDVxQKq6d+QTe1E60btyXQS5M1Nt5SvLn8dazZJgvv
-3yzJQ1IOQl+xeEO0WVVhPIx5Mx3VtjjcDyl8aewPkYkzia6UOrAyQZnl5sIzWGOb
-kYKCNeKjTPepzlbMx0dN6jBupPYGNB+4FYY9GezInjGbRP5np5382wd3EWwsVzic
-Nau8kXHTL2r7GzNvoy0p//iPCqx9FQIDAQABo4GnMIGkMB0GA1UdDgQWBBS7B027
-H/ZIkW3ngm1SrR0X/aTCwDB1BgNVHSMEbjBsgBS7B027H/ZIkW3ngm1SrR0X/aTC
-wKFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV
-BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJANz6ehBcVuuiMAwGA1UdEwQF
-MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAC0glUrUiylTfuOWlwkQvi74oiYC5CzW
-Jfusg6o/Gg1XEuJhaHiYMsK/do16gSc6Za3934rHQbYu3mesyFkCWF9kD4J6/hEO
-OQL8xmmgN7wS6GXy6oIODpny0MgnFrV4gd1aEx69NIfL/wXaM8Gw2sj1TnuGLs8+
-HFmWLRRH3WSR7ZLnqYzPVJwhHu8vtZBL9HZk1J6xyq00Nwi2Cz5WdiHamgaza3TS
-OgBdWwDeSClwhrTJni4d30dbq+eNMByIZ7QNGBQivpFzDxeNV/2UBrTU0CilKG5Q
-j7ZwknfKeA4xUTd8TMK3vKab5JJCfjbXOTHZQsYUcEEGSjOMS8/YVQs=
+MIIDUDCCAjigAwIBAgIBADANBgkqhkiG9w0BAQsFADBBMQswCQYDVQQGEwJKUDEO
+MAwGA1UECAwFVG9reW8xFTATBgNVBAoMDFJ1YnlHZW1zVGVzdDELMAkGA1UEAwwC
+Q0EwHhcNMDkwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjBBMQswCQYDVQQGEwJK
+UDEOMAwGA1UECAwFVG9reW8xFTATBgNVBAoMDFJ1YnlHZW1zVGVzdDELMAkGA1UE
+AwwCQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIGSxaHU0qZR2a
+C9Y6XF9UkKwXb1gYj+YPMzbKoJICuEmFlul0FhRAZ5hKH00c2AvETvh4CmhwOdhm
+ZMbVyknpAscaHAO6oYVoCgMFJ7V/lyGUIPP+6i71Lpk0a+DnlspRTk1ASAnWX2R7
+5d/rPUS/QiX3hMctIuB/ADfGwxZ1dTdq5VbaHHc3PADTH/SdOycI/83PHmB0ZZDC
+WbQSPqB/IkeH/1LzRznRkQIcu4zJIB8A29E6sOC67lUFjxr4Ht1tgxwdGAFEkici
+8SoH/kODCILTK/HssWiz+JQbgSlUAVYSVGa6YOdcJwRNo2Hj+Y+GUwuD6x8diQyD
+ZojIUI3FAgMBAAGjUzBRMB0GA1UdDgQWBBTW24et00X81I1rK5f0z5UItvpipDAf
+BgNVHSMEGDAWgBTW24et00X81I1rK5f0z5UItvpipDAPBgNVHRMBAf8EBTADAQH/
+MA0GCSqGSIb3DQEBCwUAA4IBAQAGkveaD0DaGn+fDJ4EN02+pB6GZbNKvocToeRr
+O9ZYncr4um3k3d7F46Ls7zItwAYBOtWBWuHk919oZ+rNKJCxnILTTgBRtuvVjeyr
+wxiyi41bY2349UDGxn5ye+2YxV4kua1PW48dU6PXak8HLmq2Y1zcBSKsd6+wcp05
+b3ecRYut3ui/arWHC1hHrxEanoQlIWhIKrM8WpdUIAO9hzTd2ySmx1DpbIdV8uUz
+nIOPjJ7zOjigkqGnxIkxvTODEd2tu+BHGbtibElYsxMSw9DdAl9vTxMHbap7LEZa
+dFJtExCc9z1dhFu4W6nFrlZLmozi/X9VgMuwLVbXpDzPPLL/
-----END CERTIFICATE-----
diff --git a/test/rubygems/client.pem b/test/rubygems/client.pem
index 63a52c574a..9824d9cd4a 100644
--- a/test/rubygems/client.pem
+++ b/test/rubygems/client.pem
@@ -1,49 +1,107 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=JP, ST=Tokyo, O=RubyGemsTest, CN=CA
+ Validity
+ Not Before: Jan 1 00:00:00 2009 GMT
+ Not After : Dec 31 23:59:59 2049 GMT
+ Subject: C=JP, ST=Tokyo, O=RubyGemsTest, CN=client
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:c7:87:4a:f4:77:32:54:0e:56:0a:80:04:ac:b5:
+ 71:b6:29:9f:c6:aa:f5:9b:3b:75:0a:c2:55:2c:cf:
+ 34:c8:78:b7:38:ed:af:48:7d:7e:6c:4c:39:d8:0f:
+ cf:fc:ba:f5:e0:50:47:a0:76:72:cf:c7:de:91:a6:
+ 1a:99:8b:5f:6f:0c:06:fc:f1:78:6b:0f:c5:bc:91:
+ cc:91:f0:85:05:5d:66:d3:cb:ac:54:a1:bc:9b:6c:
+ e8:17:f2:17:20:b8:b0:b2:03:cc:9d:a6:8e:c0:33:
+ 6c:8b:5f:ef:1a:f6:38:6d:80:3f:4d:b5:e3:a5:a4:
+ f1:86:15:76:62:8b:6c:9d:fa:24:59:32:8f:60:b0:
+ 80:f4:22:a2:68:57:13:aa:60:e4:cd:01:34:87:76:
+ 2a:15:ca:86:9b:b7:aa:b5:66:fd:72:d8:35:86:7e:
+ c8:1d:a1:71:71:85:ac:65:64:c2:ea:19:52:7b:34:
+ 1e:12:c4:87:8f:75:d7:65:35:85:dd:5a:33:5d:2c:
+ 31:f8:2f:b4:84:a7:b6:56:56:2b:e1:9c:c9:c8:f9:
+ 41:18:40:19:d9:bb:d4:3c:0d:c4:93:dc:b8:d1:99:
+ 44:d0:3d:a2:de:de:29:7f:d6:0c:a8:07:df:bc:ed:
+ 66:5b:aa:cc:64:44:b8:79:49:ed:48:77:88:e2:d1:
+ 94:b9
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ EF:5A:B6:46:3A:44:5F:0F:E4:F9:E0:6B:B2:C8:45:8D:07:0D:70:15
+ X509v3 Authority Key Identifier:
+ keyid:D6:DB:87:AD:D3:45:FC:D4:8D:6B:2B:97:F4:CF:95:08:B6:FA:62:A4
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 55:b5:5a:51:cd:0b:2a:81:10:e6:d1:d9:d6:6c:78:99:9b:01:
+ 18:e7:91:32:89:6c:fd:3b:eb:c0:03:82:f8:5c:e2:19:c1:04:
+ 5d:37:10:5b:97:0b:be:76:8b:98:71:d4:63:68:8c:0e:61:c9:
+ ec:3d:cf:ed:01:57:9f:9b:53:07:27:1a:7e:20:f3:8a:13:8c:
+ 4f:30:bd:e6:a0:eb:d7:2c:a1:95:35:3f:a6:53:c9:00:11:f9:
+ f6:b3:9d:53:e4:b5:71:33:f1:dc:86:47:94:6f:a9:64:01:d4:
+ c5:1b:7c:95:0a:02:0e:6f:d1:70:94:5a:5f:7b:ac:77:f7:56:
+ 35:6b:ad:a2:e2:fc:74:91:1e:c3:46:fc:32:01:19:a1:a5:27:
+ f3:31:14:79:86:7c:4d:9a:83:7c:28:03:9f:ac:3c:8c:e4:d9:
+ c8:b0:4e:a3:fe:75:cd:a9:8e:34:57:3b:6b:14:d6:df:35:42:
+ 7b:c9:3d:88:0d:ea:5f:1e:c6:5f:80:0e:a5:b9:bf:25:06:ac:
+ ac:38:7f:cc:f9:a5:9e:68:cc:08:77:1e:de:45:0d:91:e5:38:
+ d2:b3:62:ea:03:ec:3c:18:9f:16:ec:43:21:30:7a:a7:8b:42:
+ c6:cb:e4:a8:ac:0b:15:82:a5:9c:93:b2:2c:20:1f:d2:de:e1:
+ cf:c0:74:bc
-----BEGIN CERTIFICATE-----
-MIIDgTCCAmmgAwIBAgICEAIwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQVUx
-EzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMg
-UHR5IEx0ZDAeFw0xMzA1MDExNTAxMzFaFw0yMzAzMTAxNTAxMzFaMEUxCzAJBgNV
-BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
-aWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCw
-+lcrpdWcloQCgAlxcM3GjvBxZ3yjzi6SgXKRBSi54i0J1LXxznWKcJ5P/O1+j+7i
-LjHK+OWqsa0+EbKTwSu+0tx20h0z++YJF9GWEoCwT5aH1kor/0+EQLgYnxBaF8GC
-2xAbkRkWmbSu2aLDIey3lg7lqAazYqdS2wH0UjSDjFKDLxz9LwpfFm0yGL3DgwLW
-+dobYkgt1A6F/8Pz6D2FjwYKcM8JE6w7KJSJDUvXcv2E18wmhZ/qF/MtFAF4coB1
-f5ALnz8YqY6eyDF5aY/VfaHZvXdirLlMH6/miie9GBVMnJWF0ah5ssbsMvcpmnDJ
-qkiYju2e1oLFEE7zztU/AgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgEN
-BB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBTcOELj
-hSUdiLrdRF3CFZDZkWaGzDAfBgNVHSMEGDAWgBS7B027H/ZIkW3ngm1SrR0X/aTC
-wDANBgkqhkiG9w0BAQUFAAOCAQEAlQMzHlnT6L1qqA4hL6tABPbiMsVwXyKCcfNB
-zBn82Wkxgbg7Mp31fbR6/qvGeXOtaX6IdPdgtVf8nh1NURk0MmFBP+gfnwfNBD+m
-Q1cldDt9kY2LGIrPii40xbugF1/xqEYcZMgXU08aEvQ2IHX46J8wZoqMa2KhrU8/
-mzY0F+UEFOGWtKDgUzz3dyBPsdzVrX+SXULwH0lqZX8Nsw5LyfrlVt3xQvS5Ogm4
-kYlt8kqhF8lUS3WTbuADrIs3NaDPRWSs1iLRRFgosgUtHN7tkrkrVaHeBo0KbAJG
-mMqtxSY0XZI9WBxffP9UtoY3EiTWNVWLtuCN3OSvryP6NDe4BA==
+MIIDfDCCAmSgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBBMQswCQYDVQQGEwJKUDEO
+MAwGA1UECAwFVG9reW8xFTATBgNVBAoMDFJ1YnlHZW1zVGVzdDELMAkGA1UEAwwC
+Q0EwHhcNMDkwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjBFMQswCQYDVQQGEwJK
+UDEOMAwGA1UECAwFVG9reW8xFTATBgNVBAoMDFJ1YnlHZW1zVGVzdDEPMA0GA1UE
+AwwGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx4dK9Hcy
+VA5WCoAErLVxtimfxqr1mzt1CsJVLM80yHi3OO2vSH1+bEw52A/P/Lr14FBHoHZy
+z8fekaYamYtfbwwG/PF4aw/FvJHMkfCFBV1m08usVKG8m2zoF/IXILiwsgPMnaaO
+wDNsi1/vGvY4bYA/TbXjpaTxhhV2YotsnfokWTKPYLCA9CKiaFcTqmDkzQE0h3Yq
+FcqGm7eqtWb9ctg1hn7IHaFxcYWsZWTC6hlSezQeEsSHj3XXZTWF3VozXSwx+C+0
+hKe2VlYr4ZzJyPlBGEAZ2bvUPA3Ek9y40ZlE0D2i3t4pf9YMqAffvO1mW6rMZES4
+eUntSHeI4tGUuQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1P
+cGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU71q2RjpEXw/k
++eBrsshFjQcNcBUwHwYDVR0jBBgwFoAU1tuHrdNF/NSNayuX9M+VCLb6YqQwDQYJ
+KoZIhvcNAQELBQADggEBAFW1WlHNCyqBEObR2dZseJmbARjnkTKJbP0768ADgvhc
+4hnBBF03EFuXC752i5hx1GNojA5hyew9z+0BV5+bUwcnGn4g84oTjE8wveag69cs
+oZU1P6ZTyQAR+faznVPktXEz8dyGR5RvqWQB1MUbfJUKAg5v0XCUWl97rHf3VjVr
+raLi/HSRHsNG/DIBGaGlJ/MxFHmGfE2ag3woA5+sPIzk2ciwTqP+dc2pjjRXO2sU
+1t81QnvJPYgN6l8exl+ADqW5vyUGrKw4f8z5pZ5ozAh3Ht5FDZHlONKzYuoD7DwY
+nxbsQyEweqeLQsbL5KisCxWCpZyTsiwgH9Le4c/AdLw=
-----END CERTIFICATE-----
-
-----BEGIN RSA PRIVATE KEY-----
-MIIEowIBAAKCAQEAsPpXK6XVnJaEAoAJcXDNxo7wcWd8o84ukoFykQUoueItCdS1
-8c51inCeT/ztfo/u4i4xyvjlqrGtPhGyk8ErvtLcdtIdM/vmCRfRlhKAsE+Wh9ZK
-K/9PhEC4GJ8QWhfBgtsQG5EZFpm0rtmiwyHst5YO5agGs2KnUtsB9FI0g4xSgy8c
-/S8KXxZtMhi9w4MC1vnaG2JILdQOhf/D8+g9hY8GCnDPCROsOyiUiQ1L13L9hNfM
-JoWf6hfzLRQBeHKAdX+QC58/GKmOnsgxeWmP1X2h2b13Yqy5TB+v5oonvRgVTJyV
-hdGoebLG7DL3KZpwyapImI7tntaCxRBO887VPwIDAQABAoIBAFOpdG3gzlNg3/Ti
-nBQxdEVqKwYhGs3A2UlOwl8F5lPBNPNRx9UQeYZBaMV9VrQezJnFpqpB8Sg5KCGQ
-ci/hAJIL0kalW0LI0Nz5ko10H7u5U/rQ9W1JG0j041JYV32Pf14husKdXBPQA5co
-sQW30tSSrmYogUpp15mWiJz8A3EvqiCTlQv5JwwMFGnjVl8+HNfuLghK/vqY/Eb9
-YmwTKxPFejqN7E0Mud2ylNiuPTSLwBy8UvV9uxOlDc6lMyZjVRO0woiEzrjw5dKF
-yf5tUkICRcPkekcx+XtpGrCMlRLl770bZBZX+YNmbYXVWhFp09cNR+U0KZqPNcDp
-jg73vXECgYEA3huOKzfHGt3qUdMlEHd1FvQhW9fYIrmUSnuVYQJOnY8lFfKfmrOH
-gpwOIHDNiVHYlhAJaNocCLYx4hWHgZXarY7NKxmlY2+Vp8mcCIf2Cw3Kr/sFklUJ
-KpiRxqEPGR7U4C/E31kkH/C+w7m9Zh3ndhltU2Pki9/Eq0lk8YClMMkCgYEAy/vU
-jxzviIk8bll5uCIuXJyCfao7ywaZABbL6a20kdVGKrHj57O/OJ2WZVwBihhB7OS+
-QsKC/J8LrUJkobOFtQvQ8O23uep5rB6kqCkXsXCG4SCl2L5xZySBp/qhiqbuMwvp
-EAWPSIA6UNoR0J2rDYVmq6jtY526wQf5ivE8IccCgYEAphfzJAyNH2FOZixArmS2
-shiUjasG3UjsRRrP5YClK5wtPpF2m2if8KMkyUux2HvVPLr3XmqkxjsBaLFy6QwY
-QOvmL9H45Tg/sP7KaXLLIw8IQLu2OezPcwQvF1u//6gXxyLR1bhClIQjFBjlMuUv
-/xgasl6kPZlz6Cd1jkgGwEkCgYAI1IT2EQWZfn9cM4leXDRvk+LeN8FQ35897r6z
-Be78JSRdcsfv3ssXU1MQXjQ+2x/3dkt6LltnPidOP8KFcXUHSlSoKVI7vRe5SLZO
-BUFeUAW2tygWwt+73Eu0jtfxXZqQISLcq7DxLYPYvifpRPoDotO3+J8WIdzUwFig
-GCNHPwKBgHqXOyRef7ykVUCptMf61/BvNU8NP1f9PkKQBMYQZC39UwqEQ675QBUh
-hSG9t/kyc44zUVmBeKIlWHVyLQ83Dv+ennz/D9t7tstet0VMKvALNdiVT0sjFKN7
-1VINygCeFkqrlTXlOwFcRSo1gHn3/JIrhSgRuYKHSf0GZOcN6d9l
+MIIEogIBAAKCAQEAx4dK9HcyVA5WCoAErLVxtimfxqr1mzt1CsJVLM80yHi3OO2v
+SH1+bEw52A/P/Lr14FBHoHZyz8fekaYamYtfbwwG/PF4aw/FvJHMkfCFBV1m08us
+VKG8m2zoF/IXILiwsgPMnaaOwDNsi1/vGvY4bYA/TbXjpaTxhhV2YotsnfokWTKP
+YLCA9CKiaFcTqmDkzQE0h3YqFcqGm7eqtWb9ctg1hn7IHaFxcYWsZWTC6hlSezQe
+EsSHj3XXZTWF3VozXSwx+C+0hKe2VlYr4ZzJyPlBGEAZ2bvUPA3Ek9y40ZlE0D2i
+3t4pf9YMqAffvO1mW6rMZES4eUntSHeI4tGUuQIDAQABAoIBAHbfhuuQ3D4x8Fb/
+IEnZK+8Qa22MSxl52ehYETDKHjNVoCtdiDGS+rAA1fGAsjRrGrPSgGn8R7i85kA2
+CuDxpSDetIccQdbfJbqLzqof9tBUbj++t2QQm/KpdrlVdSv8fOEB3HUMVz3xJTkA
+Jc8VZFbwskZVGFSGqZJt1QMu975By8mrNBiQ92dpWUwH6bcJ2rL+GgpW8LkosAtW
++bqAH86je2utErCStHTBMq459JIcef+dZxQ1iNALny+Q54MIsFbh21TbUf+mPsBK
+B5Pe+RnlEw9uFmFH0gQybmZBIB/IDnsDj/+L0gRrSp6nYaQBD2Gw2jPJQL0PEu0s
+nS1B4tECgYEA+E1z73eA35jBvDg/CI4mcL/f17xRCW9YsaeXTqEDMSi27b5OSgdP
+0ETl3xreVgKGeDHygyaaJ6MR7uoPRPJhZR1ifrhWKoyPLuD55rwGIvKIbAWXVj7/
+AvcY+qligVcK2dTJjczh6Tv8/XVYEPrfg4QdDlg9rodAlNJGofaX0WUCgYEAzbbD
+8Yw/KbX1bSN6TtHoAAIVd/y2a4TEgHxOOOM5LAQ98fgP4L7njse8CBFUCH9RXYIS
+lWyT7aHxykK/32wsd/6CfP1IlNcOSrBe5nA0b1m9piT6K9nZd7NMv1DpznaZ/roO
+6jPxXrtQAgAC4jPCehH23t8SqP+abBIWvTlZisUCgYB4Jvqf+UL6b+/nxYvy9t5x
+FtgZi/3mw+O2a/OSz+U8h4gleT5nIiykCoL1uAm4sxYg2YKRpj9YSNentclXwrYQ
+eOyth0Pi6QtsUt96oKeTh6suInJ+AJPj+nAy835AOj988zPpEyiKdUXR3FOWO9+m
+w2pQA7EtYDOHEE2vmCUU5QKBgCSP10OXKaLANF6xb4uSwyk9NZOd1s5FSqeLcFus
+Bv1Lw7a94BSR2ZYG6eSFL+pStqNn+uWT5rbVkaPhOTj8gOrS0V5lpgDOODwOHM/Y
+IXmo+YwOBmjEz2H2/C0EtIl9iuE7MGtvz/aGVDIGznxltqr7hmUWQLrIsymCDYiU
+KNYBAoGAeZd1hDEK3dmzNAzNUWaIVWg2yq+B1RF7k1yzk7XcAc13vGEyZc2gILji
+y+0IMS60/uKVZ7zYBvxuaDJImi1woEzAVSM2LUo2vHgFClrhHCF+tGKVEa1hbhhO
+ScifJC8f/HoKI+Ddn0hrFF0ndBJ7g4mB9sm7RBHfm+1steGCV3Q=
-----END RSA PRIVATE KEY-----
diff --git a/test/rubygems/fix_openssl_warnings.rb b/test/rubygems/fix_openssl_warnings.rb
deleted file mode 100644
index 1f5b8b5e0a..0000000000
--- a/test/rubygems/fix_openssl_warnings.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-##
-# HACK: this drives me BONKERS
-
-if defined? OpenSSL then
- class OpenSSL::X509::ExtensionFactory
- alias :old_create_ext :create_ext
- def create_ext(*args)
- @config ||= nil
- old_create_ext(*args)
- end
- end
-end if RUBY_VERSION < "1.9"
diff --git a/test/rubygems/private3072_key.pem b/test/rubygems/private3072_key.pem
new file mode 100644
index 0000000000..e99abce69c
--- /dev/null
+++ b/test/rubygems/private3072_key.pem
@@ -0,0 +1,40 @@
+-----BEGIN PRIVATE KEY-----
+MIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQDIl6Ne1eCreYAe
+j+Qkbygv06KqtcarwjNpSPJFq+G/R6xzGfbiuJDGePnlzqyAN6sti8r/tDD8orMS
+l3EUSQTvFAVJKxbBDnklHUozdaX+giw+fi7onRJ3sQE2j7GhjahXZB0vmx20wU9v
+sazZ0IU/x7k8X7WbVRCYig99UVn28zsq586LN5u/w7qXo50156bfmeDS8tt+TKSK
+PkQQYuO+AulWDVdwHycKSzCE7SPWiZ/scFUirN4Stfw0bJZbr8VQ6QBAhHcHgd6B
+tcZ1vBDC656zsWgpGWNTCU9mSBWV3UMk8mdwkO/jz8mXYSBfbyLESkhc8slf47zd
+dqs1iZZOmVVlz/TXYc9N9d8DTl3OR/YYy04mdrysf4ijB6HV9U7NrRbjcKqKwTjW
+xMWsFDdc1d5WCTsXzKjvB582LspGYxv9zgs0KkhSChGTA0qCqGdl0ZVx1Q0suW5O
+7XlcEWAp9gSww0RW3E1zkrUARcB4mQgyC89kvE4y9RW4/KzZDcsCAwEAAQKCAYEA
+uhQJBkGLgCZe1nsO3UmMUwmVPZ0QpmapgIKd1EnFScb4T3IHl3w1ORgiYa9eUDbU
+AZVLg/co9kMLsTRxPqsZ+5pr6Nsi6YY+lVJdce0yRi2FU5eEdl63MfcuM+oKkt4x
+CpihhnbzkKk+wlNlEE2iPm9NA5eZhXXcxlRUWCEuPqqV+ZA+BuFYBwVPw7mQbd/t
+6kD50VZejQQWIvPt+fFyaOKUiDIqKaWMdr0XTkgZ1bunchMmttr7ywms4wjUVktv
+LWMmI8wEMXfxR+xOtigwolSuFn+djWgh02KEc4gSQD3KGKshps5cMrLxxkwIoaC8
+rDxKfdUtixBx6JzrN0Wmq7f/oLpvzxTXoQTu30BvpUOr9evZjE/3ZYXiGCxoSDEL
+cvdZHvd+4r0TEkuxNBebq5106bJCBTuq6awwiwAodrsH5DCnqkkrv9tnkVi71NnV
+UfnF/C4i5clpJK+Rx4bub9SiqJuyEfQBDlEHdCDwKVASk6B3gXD2mgkJu+EdShkp
+AoHBAPx27s3iBN1gRGzD9GU0x2z7uMrgXzXh71kCvzzkfOli5fpIvCyoOR8CIfok
+Ph8Kth4c+Bz4bmp6c859yxm5aBdQb3y34LDvCkqEeFCs9XX31QTMjJ1Y3R6Iyun5
+nJzzZ5BtJbLFTHw9/p/pCUmJscUP8nQLwBMLIk4HzmrVXVLKJyaakK4LbsCChBc5
+XwCqzWFKbMRZM3X8PaZp3DCo9iA5/TyzkQMTucLSYaxDDG+YNtYQPB8J5wkFWTD8
+5//WZQKBwQDLZr9CnPR8HcrG46+rDzoZO1nXqaaEQMZhmm25lpL5oSHLrm4elGDp
+vH5SRcCPz986InRszMROI3xvYpGtFsAzff3UVY0M8uDQcEKucHtd4wmFHefzYmKo
+cvR9PuGmotk6yYpb+cMITT6sCzC4OHPz3DQAclmswRZchHYd2Zkv7tjgE0dXS9nw
+XKaH5vQC4wjWBc857scHz0WYJLlj8s8UM9Fludz7kuagXczrsFIONxvrFKNJXeYl
+muFCQl8hOG8CgcEA4ZZkTBNpxWX/vjBacRR4HinPNXjHmp4IAMEzoHWKKAD2/m1/
+t2eZotuFAL7hw1sO4FmCWmCiSQKh+CDvGk1RdYOqGwcy/uaZi3xTBcOGkaKh9WfR
+PcfpzR7uMaOZDaVxJNxikxs4/MtoefsBEXS4JB3bx1W4i0unm5HeIBgHC7MWyKfU
+H7CXhe0Zmqbo/O+iFQ0ro0cRdJuvesOcvN49Dw7B+Tt6mAVIN41FOWev9QdN+HkJ
+P7LZfnYI/Hz/0NsBAoHADm2+eZI6wac3YD58kqzk2S9doy/UsSMLL5dN21F0IaMt
+i45XH3I1Ib+OUnXCQDFly3DwQ1uPPV/FDv22CcpIXh6859gdxmJgUkj0Yf12suVN
+IpVJg/lhuENXVp8kULbSpBnx565jCG66WGf+z8Kpbw4a3kE+XUPhOzTmUB3EgSL3
+XYXglK+7yRI5egCHJMFIOi51Uc2/bq1kaXOJdy6dQ/idDRNPOsVj+NJOnBWI7Js6
+LsXrA2RW1CoVeqbMqsWfAoHAAabWXkQ74BuTo+P7lbfnFa6qLfTRnkWBkKKpWSAZ
+ZBLfGsw2F5ZzrfwXtLriPcQJNA+3q3u1WPiGZkjV4QnFMt+Kgcg8ahW94vDEgfex
+OSQyrlioT2m9DLJNXXHxo3+0ePNQkmoMQbNRUwdYjBuK7dqNDmFQ8Oo8SxtlV2sL
+ntLc47NvFaxDlOvnj9ftQv6ZhdzXQmKGiuZWxtrrjFgHJm8KhMS8IF9xHM3d0uYb
+sbykscVdmz3lOmUZrxCeIJvk
+-----END PRIVATE KEY-----
diff --git a/test/rubygems/public3072_cert.pem b/test/rubygems/public3072_cert.pem
new file mode 100644
index 0000000000..8f769aa8b9
--- /dev/null
+++ b/test/rubygems/public3072_cert.pem
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIENDCCApygAwIBAgIBATANBgkqhkiG9w0BAQsFADAqMQ8wDQYDVQQDDAZub2Jv
+ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMCAXDTE2MDEwMTAwMDAwMFoYDzk5
+OTkxMjMxMjM1OTU5WjAqMQ8wDQYDVQQDDAZub2JvZHkxFzAVBgoJkiaJk/IsZAEZ
+FgdleGFtcGxlMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyJejXtXg
+q3mAHo/kJG8oL9OiqrXGq8IzaUjyRavhv0escxn24riQxnj55c6sgDerLYvK/7Qw
+/KKzEpdxFEkE7xQFSSsWwQ55JR1KM3Wl/oIsPn4u6J0Sd7EBNo+xoY2oV2QdL5sd
+tMFPb7Gs2dCFP8e5PF+1m1UQmIoPfVFZ9vM7KufOizebv8O6l6OdNeem35ng0vLb
+fkykij5EEGLjvgLpVg1XcB8nCkswhO0j1omf7HBVIqzeErX8NGyWW6/FUOkAQIR3
+B4HegbXGdbwQwuues7FoKRljUwlPZkgVld1DJPJncJDv48/Jl2EgX28ixEpIXPLJ
+X+O83XarNYmWTplVZc/012HPTfXfA05dzkf2GMtOJna8rH+Ioweh1fVOza0W43Cq
+isE41sTFrBQ3XNXeVgk7F8yo7wefNi7KRmMb/c4LNCpIUgoRkwNKgqhnZdGVcdUN
+LLluTu15XBFgKfYEsMNEVtxNc5K1AEXAeJkIMgvPZLxOMvUVuPys2Q3LAgMBAAGj
+YzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBSf
+hxDG5kMk4VhQNMcuLhULi3gMpTAfBgNVHSMEGDAWgBSfhxDG5kMk4VhQNMcuLhUL
+i3gMpTANBgkqhkiG9w0BAQsFAAOCAYEAuvY1Nc8lkjCWEnVlAB0yfx85+xa/6zoQ
+9w4cG/Nk+M2XNXRmp0c6FQgy7Y/aRxIobJnJfo2S1yJIPfzBuxb/oOy4ikYGmrYV
+JUJFs4KaMPz8nM13YVI+KtskNEs0Pb8kcb0ZO640f0ptkgFDN/rvezu2uxqTlaD+
+NSy+O+2Xr5T1Qq2eT2ui3mint26sA2g2cZqkqIdeEWHz/wf5ECMANvgCvE4efduI
+oSwFbdb32UKKzppGW+usUbCgEH++EVNWN7VG8F7bvsnPDmuW2J2p2jjvg76H5eK2
+OtnI180JV2Qb80d2lKOS24Mq9edhCzh9AUFsTAfaQ1iBUE4P353G67RF88ZNvV1A
+n9DIgbMBf97bByUmp+5MWMXWJ9AcqyXQFsQutEQMudor8P9UqwpCVUkxijpfFxvM
+HOBVArYRsdhbjNRGpVAVHdzpJ2AQNTQVeSS7YdzHAzGIVksKHL3K5QJuUJCgNa52
+9H5201wSTxSAhlhoPTT06OHmIGiTvXZS
+-----END CERTIFICATE-----
diff --git a/test/rubygems/rubygems_plugin.rb b/test/rubygems/rubygems_plugin.rb
index 30a67789c6..7fac2ebec6 100644
--- a/test/rubygems/rubygems_plugin.rb
+++ b/test/rubygems/rubygems_plugin.rb
@@ -23,4 +23,3 @@ class Gem::Commands::InterruptCommand < Gem::Command
end
Gem::CommandManager.instance.register_command :interrupt
-
diff --git a/test/rubygems/simple_gem.rb b/test/rubygems/simple_gem.rb
index 1650910aaf..0f2ea48198 100644
--- a/test/rubygems/simple_gem.rb
+++ b/test/rubygems/simple_gem.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-SIMPLE_GEM = <<-GEMDATA
+SIMPLE_GEM = <<-GEMDATA.freeze
MD5SUM = "989bf34a1cbecd52e0ea66b662b3a405"
if $0 == __FILE__
require 'optparse'
diff --git a/test/rubygems/ssl_cert.pem b/test/rubygems/ssl_cert.pem
index 998ccc5892..b99938e15b 100644
--- a/test/rubygems/ssl_cert.pem
+++ b/test/rubygems/ssl_cert.pem
@@ -1,19 +1,80 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=JP, ST=Tokyo, O=RubyGemsTest, CN=CA
+ Validity
+ Not Before: Jan 1 00:00:00 2009 GMT
+ Not After : Dec 31 23:59:59 2049 GMT
+ Subject: C=JP, ST=Tokyo, O=RubyGemsTest, CN=localhost
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:c1:9b:13:e1:65:2a:87:a9:3c:84:2e:c3:21:e1:
+ 63:a2:8c:7c:6e:63:9a:ac:3e:45:f5:fa:37:08:aa:
+ c0:a7:6b:ff:42:27:1a:66:13:b9:ad:ca:d0:35:62:
+ 52:00:56:56:71:cd:dc:74:04:fe:2a:a6:7d:00:61:
+ e2:b8:9a:0f:d1:2c:56:b7:50:c4:23:f1:52:68:f3:
+ fd:cf:6c:6b:86:93:91:f0:d8:7a:67:d8:55:fc:0d:
+ d1:30:f8:aa:a4:79:f2:17:ca:11:b3:8c:e5:01:34:
+ ad:21:bc:a8:4d:ea:18:bc:13:9c:0a:94:bf:fb:46:
+ cf:29:d2:52:03:e4:97:4e:92:ae:b7:9d:b0:d9:19:
+ 49:5f:7a:5b:20:80:87:05:db:f4:73:df:04:69:12:
+ e8:14:1e:d2:c8:dd:d0:d3:81:72:04:f5:34:d7:9c:
+ 61:b5:b6:d3:4b:61:ee:a9:04:36:60:79:c8:77:74:
+ 24:70:89:a8:16:f8:6e:21:51:e9:30:61:fb:21:f6:
+ b3:6b:c1:b1:09:fa:26:ed:9f:3d:d2:2a:b6:34:d9:
+ b8:e8:46:d7:08:c3:3c:5d:0e:96:7a:e5:a1:ff:0a:
+ e2:36:bc:b4:06:3e:32:5a:9e:a5:4b:38:9e:0a:cc:
+ 08:3a:9f:07:4e:74:d0:16:4b:0f:51:e4:d5:24:f3:
+ 76:5d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ 1E:0A:57:4F:0E:BC:B5:26:26:B9:01:80:E8:9A:25:98:4A:C0:D6:05
+ X509v3 Authority Key Identifier:
+ keyid:D6:DB:87:AD:D3:45:FC:D4:8D:6B:2B:97:F4:CF:95:08:B6:FA:62:A4
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 85:13:fa:00:1d:65:c4:f3:82:12:94:67:e3:34:a1:ac:0c:7b:
+ f1:2c:e7:20:db:ea:b8:d1:54:52:3e:34:00:bf:d3:1f:04:be:
+ df:c3:7d:96:20:b6:e5:cf:d3:67:b4:27:95:57:41:e9:51:9e:
+ 90:88:a9:0e:97:4f:37:42:35:21:b5:e3:6e:82:c9:4c:66:1c:
+ 61:df:84:28:00:6a:93:d4:dd:25:96:18:55:89:cc:3d:70:a5:
+ 50:a4:e0:b9:db:c1:8e:aa:b1:aa:cc:89:dc:c1:1d:2d:c8:49:
+ ad:5b:96:eb:62:57:2e:0e:c0:5d:de:0a:86:27:b9:3c:92:bd:
+ 2d:db:0a:3c:ed:ef:1a:cf:0e:33:c9:61:a2:44:c1:ad:53:e6:
+ ca:28:ee:4c:19:6f:dd:75:a2:cc:d8:9a:36:e7:8c:64:35:da:
+ 1b:cb:9b:31:53:ca:a0:7b:d8:ac:ff:ee:a3:e8:9b:32:8f:5d:
+ 0d:ce:0c:eb:b5:ed:82:d6:70:0e:c3:ca:9e:8b:e4:c1:fc:c0:
+ 1e:ed:81:7e:5a:0a:a7:34:26:f7:0e:28:a0:7b:ba:21:42:14:
+ 84:48:12:df:e8:9e:21:91:fc:c8:c0:f5:f8:6a:9f:c7:27:d4:
+ 73:c5:9d:1a:5c:c8:62:24:71:d5:ae:4c:f2:c4:ad:14:c3:6d:
+ db:d6:56:bb
-----BEGIN CERTIFICATE-----
-MIIC/zCCAeegAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQGDAJKUDES
-MBAGA1UECgwJSklOLkdSLkpQMQwwCgYDVQQLDANSUlIxDjAMBgNVBAMMBVN1YkNB
-MB4XDTA0MDEzMTAzMTMxNloXDTMzMDEyMzAzMTMxNlowQzELMAkGA1UEBgwCSlAx
-EjAQBgNVBAoMCUpJTi5HUi5KUDEMMAoGA1UECwwDUlJSMRIwEAYDVQQDDAlsb2Nh
-bGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANFJTxWqup3nV9dsJAku
-p+WaXnPNIzcpAA3qMGZDJTJsfa8Du7ZxTP0XJK5mETttBrn711cJxAuP3KjqnW9S
-vtZ9lY2sXJ6Zj62sN5LwG3VVe25dI28yR1EsbHjJ5Zjf9tmggMC6am52dxuHbt5/
-vHo4ngJuKE/U+eeGRivMn6gFAgMBAAGjgYUwgYIwDAYDVR0TAQH/BAIwADAxBglg
-hkgBhvhCAQ0EJBYiUnVieS9PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAd
-BgNVHQ4EFgQUpZIyygD9JxFYHHOTEuWOLbCKfckwCwYDVR0PBAQDAgWgMBMGA1Ud
-JQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBBQUAA4IBAQBwAIj5SaBHaA5X31IP
-CFCJiep96awfp7RANO0cuUj+ZpGoFn9d6FXY0g+Eg5wAkCNIzZU5NHN9xsdOpnUo
-zIBbyTfQEPrge1CMWMvL6uGaoEXytq84VTitF/xBTky4KtTn6+es4/e7jrrzeUXQ
-RC46gkHObmDT91RkOEGjHLyld2328jo3DIN/VTHIryDeVHDWjY5dENwpwdkhhm60
-DR9IrNBbXWEe9emtguNXeN0iu1ux0lG1Hc6pWGQxMlRKNvGh0yZB9u5EVe38tOV0
-jQaoNyL7qzcQoXD3Dmbi1p0iRmg/+HngISsz8K7k7MBNVsSclztwgCzTZOBiVtkM
-rRlQ
+MIIDfzCCAmegAwIBAgIBATANBgkqhkiG9w0BAQsFADBBMQswCQYDVQQGEwJKUDEO
+MAwGA1UECAwFVG9reW8xFTATBgNVBAoMDFJ1YnlHZW1zVGVzdDELMAkGA1UEAwwC
+Q0EwHhcNMDkwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjBIMQswCQYDVQQGEwJK
+UDEOMAwGA1UECAwFVG9reW8xFTATBgNVBAoMDFJ1YnlHZW1zVGVzdDESMBAGA1UE
+AwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwZsT
+4WUqh6k8hC7DIeFjoox8bmOarD5F9fo3CKrAp2v/QicaZhO5rcrQNWJSAFZWcc3c
+dAT+KqZ9AGHiuJoP0SxWt1DEI/FSaPP9z2xrhpOR8Nh6Z9hV/A3RMPiqpHnyF8oR
+s4zlATStIbyoTeoYvBOcCpS/+0bPKdJSA+SXTpKut52w2RlJX3pbIICHBdv0c98E
+aRLoFB7SyN3Q04FyBPU015xhtbbTS2HuqQQ2YHnId3QkcImoFvhuIVHpMGH7Ifaz
+a8GxCfom7Z890iq2NNm46EbXCMM8XQ6WeuWh/wriNry0Bj4yWp6lSzieCswIOp8H
+TnTQFksPUeTVJPN2XQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQf
+Fh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUHgpXTw68
+tSYmuQGA6JolmErA1gUwHwYDVR0jBBgwFoAU1tuHrdNF/NSNayuX9M+VCLb6YqQw
+DQYJKoZIhvcNAQELBQADggEBAIUT+gAdZcTzghKUZ+M0oawMe/Es5yDb6rjRVFI+
+NAC/0x8Evt/DfZYgtuXP02e0J5VXQelRnpCIqQ6XTzdCNSG1426CyUxmHGHfhCgA
+apPU3SWWGFWJzD1wpVCk4LnbwY6qsarMidzBHS3ISa1blutiVy4OwF3eCoYnuTyS
+vS3bCjzt7xrPDjPJYaJEwa1T5soo7kwZb911oszYmjbnjGQ12hvLmzFTyqB72Kz/
+7qPomzKPXQ3ODOu17YLWcA7Dyp6L5MH8wB7tgX5aCqc0JvcOKKB7uiFCFIRIEt/o
+niGR/MjA9fhqn8cn1HPFnRpcyGIkcdWuTPLErRTDbdvWVrs=
-----END CERTIFICATE-----
diff --git a/test/rubygems/ssl_key.pem b/test/rubygems/ssl_key.pem
index 9ba2218a03..4883043b77 100644
--- a/test/rubygems/ssl_key.pem
+++ b/test/rubygems/ssl_key.pem
@@ -1,15 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
-MIICXQIBAAKBgQDRSU8Vqrqd51fXbCQJLqflml5zzSM3KQAN6jBmQyUybH2vA7u2
-cUz9FySuZhE7bQa5+9dXCcQLj9yo6p1vUr7WfZWNrFyemY+trDeS8Bt1VXtuXSNv
-MkdRLGx4yeWY3/bZoIDAumpudncbh27ef7x6OJ4CbihP1PnnhkYrzJ+oBQIDAQAB
-AoGBAIf4CstW2ltQO7+XYGoex7Hh8s9lTSW/G2vu5Hbr1LTHy3fzAvdq8MvVR12O
-rk9fa+lU9vhzPc0NMB0GIDZ9GcHuhW5hD1Wg9OSCbTOkZDoH3CAFqonjh4Qfwv5W
-IPAFn9KHukdqGXkwEMdErsUaPTy9A1V/aROVEaAY+HJgq/eZAkEA/BP1QMV04WEZ
-Oynzz7/lLizJGGxp2AOvEVtqMoycA/Qk+zdKP8ufE0wbmCE3Qd6GoynavsHb6aGK
-gQobb8zDZwJBANSK6MrXlrZTtEaeZuyOB4mAmRzGzOUVkUyULUjEx2GDT93ujAma
-qm/2d3E+wXAkNSeRpjUmlQXy/2oSqnGvYbMCQQDRM+cYyEcGPUVpWpnj0shrF/QU
-9vSot/X1G775EMTyaw6+BtbyNxVgOIu2J+rqGbn3c+b85XqTXOPL0A2RLYkFAkAm
-syhSDtE9X55aoWsCNZY/vi+i4rvaFoQ/WleogVQAeGVpdo7/DK9t9YWoFBIqth0L
-mGSYFu9ZhvZkvQNV8eYrAkBJ+rOIaLDsmbrgkeDruH+B/9yrm4McDtQ/rgnOGYnH
-LjLpLLOrgUxqpzLWe++EwSLwK2//dHO+SPsQJ4xsyQJy
+MIIEpAIBAAKCAQEAwZsT4WUqh6k8hC7DIeFjoox8bmOarD5F9fo3CKrAp2v/Qica
+ZhO5rcrQNWJSAFZWcc3cdAT+KqZ9AGHiuJoP0SxWt1DEI/FSaPP9z2xrhpOR8Nh6
+Z9hV/A3RMPiqpHnyF8oRs4zlATStIbyoTeoYvBOcCpS/+0bPKdJSA+SXTpKut52w
+2RlJX3pbIICHBdv0c98EaRLoFB7SyN3Q04FyBPU015xhtbbTS2HuqQQ2YHnId3Qk
+cImoFvhuIVHpMGH7Ifaza8GxCfom7Z890iq2NNm46EbXCMM8XQ6WeuWh/wriNry0
+Bj4yWp6lSzieCswIOp8HTnTQFksPUeTVJPN2XQIDAQABAoIBAG0KWZ0VrGlxsKrF
+55L6bXJMa3yEzsV54U9TmJFiElV5Ju/sNBsPuwEd/DxE3uhfuBoqlqIleb0tvbNs
+bhQIeSYXMdPXQlD4BrEj0mxzpO/Lx2N9mRtJpcvrQEmzk/BE2kv5vBSDIyuVrnkU
+1zniwgGTjcwL0UDFtcHZOeYFZg6S5Co7vbwBItIAcZKdZUXTHj7gt5j1uGD9a0FL
+fOFYiysrr9Nh8lVJgTv5W/gdYxE4vmyG6nfIUIPSyZwTgzItk4RZU3ygGTA1bP3D
+O0U3bW4avXKtJTdLUpy7MHT5edErZ0UX7LKYByfvinGMH9FpsBcp2XAcBKZmZwrG
+e00XR+UCgYEA+g3Gj2GUwGz/LUmPUb2fT7vqbuoOJpNQUxsx1csbX6NetH7eJU4y
+P/iZKSmAeVbf+RdfySf4/Z1brrwtKz700FMY7CYhfXiHH3vL+TCZgLq/wuU/hJYQ
+YHV5P1/71YQg3ArCP4ukkoJH4Zm8ACN5MUx8YbRrwvdVYycf9nwVGWcCgYEAxjWr
+b8kTCPhzbZIJiDWq5fDlnt5PwU+bXGDQMK5LnagxfcQg7LLlJm5c7+fxBX1Q0n6U
+dtAdswaTiL5VotV9zQhF/Kmehjl7GAMxpcSfMYpwxSQEY5jDswTVCRej4B4GLDQD
+7jO6Ih+o6fvxhxh6yP9kO5IMn/ATGqnxipwsI5sCgYB+pIt0As/7xThYD3FuzMLq
+9xl8oz4Xe3SrIpLS3NM4VN2aPMb1zezVSWsI1eQjkERPO+9fScdpa1EL5nJZ/MCw
+4eU1dPpxOaH16BUeV+bms657XvAUjowszlJiLpK1SP37c9d9p5PqP+F3+QymZVD0
+DvDP6zjIvcuZNC+T/rQQ8QKBgQCUzZwMCye4N1Xo75cqZCgOAqFQeNPla89WNfR2
+Z5OrI6csJP8W1GNGS9qH3VhL572ymYb5/9ocGhbOtR3zZlXQhKKOQgUZ/bNU3qj6
+KzfcaHOq/RQydXCOSFVrQw0rZz75Fn/Q55KYpa4ZI6U65/zeQyXLtyS1OfYNsYH9
+rqtQewKBgQDCXBOUc/wSzc77EFC6A3j4nETS1D4WLpz/sKljTDRES7zsIGqgur7N
+P0Lbijz3HiW8RCS4Kt0VPXApsXEa6D8aYordzk2rp3Uk1pdWhcMoOr4FaXyRYgl7
++XYlxe0kzAFY7ZR6M0p9cXjh9XZ1Is15xJQg3q0/ru/J3QS0BTSrFg==
-----END RSA PRIVATE KEY-----
diff --git a/test/rubygems/test_bundled_ca.rb b/test/rubygems/test_bundled_ca.rb
index 97a64af323..3f543d4acf 100644
--- a/test/rubygems/test_bundled_ca.rb
+++ b/test/rubygems/test_bundled_ca.rb
@@ -51,13 +51,16 @@ if ENV["CI"] || ENV["TEST_SSL"]
assert_https('rubygems.org')
end
- def test_accessing_fastly
- assert_https('rubygems.global.ssl.fastly.net')
+ def test_accessing_www_rubygems
+ assert_https('www.rubygems.org')
end
- def test_accessing_new_index
- assert_https('fastly.rubygems.org')
+ def test_accessing_staging
+ assert_https('staging.rubygems.org')
end
+ def test_accessing_new_index
+ assert_https('index.rubygems.org')
+ end
end
end
diff --git a/test/rubygems/test_config.rb b/test/rubygems/test_config.rb
index 79f300fd9c..70fc4e23f0 100644
--- a/test/rubygems/test_config.rb
+++ b/test/rubygems/test_config.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'rubygems/test_case'
require 'rubygems'
+require 'shellwords'
class TestConfig < Gem::TestCase
@@ -8,17 +9,21 @@ class TestConfig < Gem::TestCase
util_make_gems
spec = Gem::Specification.find_by_name("a")
spec.activate
- assert_equal "#{spec.full_gem_path}/data/a", Gem.datadir('a')
+ assert_equal "#{spec.full_gem_path}/data/a", spec.datadir
end
def test_good_rake_path_is_escaped
path = Gem::TestCase.class_eval('@@good_rake')
- assert_match(/#{Gem.ruby} "[^"]*good_rake.rb"/, path)
+ ruby, rake = path.shellsplit
+ assert_equal(Gem.ruby, ruby)
+ assert_match(/\/good_rake.rb\z/, rake)
end
def test_bad_rake_path_is_escaped
path = Gem::TestCase.class_eval('@@bad_rake')
- assert_match(/#{Gem.ruby} "[^"]*bad_rake.rb"/, path)
+ ruby, rake = path.shellsplit
+ assert_equal(Gem.ruby, ruby)
+ assert_match(/\/bad_rake.rb\z/, rake)
end
end
diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb
index 787350727f..e740a5ab94 100644
--- a/test/rubygems/test_gem.rb
+++ b/test/rubygems/test_gem.rb
@@ -5,16 +5,18 @@ require 'rubygems/command'
require 'rubygems/installer'
require 'pathname'
require 'tmpdir'
+require 'rbconfig'
# TODO: push this up to test_case.rb once battle tested
-$SAFE=1
+
$LOAD_PATH.map! do |path|
path.dup.untaint
end
class TestGem < Gem::TestCase
+ RUBY_INSTALL_NAME = RbConfig::CONFIG['RUBY_INSTALL_NAME']
- PLUGINS_LOADED = []
+ PLUGINS_LOADED = [] # rubocop:disable Style/MutableConstant
def setup
super
@@ -31,11 +33,11 @@ class TestGem < Gem::TestCase
def test_self_finish_resolve
save_loaded_features do
- a1 = new_spec "a", "1", "b" => "> 0"
- b1 = new_spec "b", "1", "c" => ">= 1"
- b2 = new_spec "b", "2", "c" => ">= 2"
- c1 = new_spec "c", "1"
- c2 = new_spec "c", "2"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 1"
+ b2 = util_spec "b", "2", "c" => ">= 2"
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2"
install_specs c1, c2, b1, b2, a1
@@ -53,13 +55,13 @@ class TestGem < Gem::TestCase
def test_self_finish_resolve_wtf
save_loaded_features do
- a1 = new_spec "a", "1", "b" => "> 0", "d" => "> 0" # this
- b1 = new_spec "b", "1", { "c" => ">= 1" }, "lib/b.rb" # this
- b2 = new_spec "b", "2", { "c" => ">= 2" }, "lib/b.rb"
- c1 = new_spec "c", "1" # this
- c2 = new_spec "c", "2"
- d1 = new_spec "d", "1", { "c" => "< 2" }, "lib/d.rb"
- d2 = new_spec "d", "2", { "c" => "< 2" }, "lib/d.rb" # this
+ a1 = util_spec "a", "1", "b" => "> 0", "d" => "> 0" # this
+ b1 = util_spec "b", "1", { "c" => ">= 1" }, "lib/b.rb" # this
+ b2 = util_spec "b", "2", { "c" => ">= 2" }, "lib/b.rb"
+ c1 = util_spec "c", "1" # this
+ c2 = util_spec "c", "2"
+ d1 = util_spec "d", "1", { "c" => "< 2" }, "lib/d.rb"
+ d2 = util_spec "d", "2", { "c" => "< 2" }, "lib/d.rb" # this
install_specs c1, c2, b1, b2, d1, d2, a1
@@ -75,6 +77,29 @@ class TestGem < Gem::TestCase
end
end
+ def test_self_finish_resolve_respects_loaded_specs
+ save_loaded_features do
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 1"
+ b2 = util_spec "b", "2", "c" => ">= 2"
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2"
+
+ install_specs c1, c2, b1, b2, a1
+
+ a1.activate
+ c1.activate
+
+ assert_equal %w(a-1 c-1), loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
+
+ Gem.finish_resolve
+
+ assert_equal %w(a-1 b-1 c-1), loaded_spec_names
+ assert_equal [], unresolved_names
+ end
+ end
+
def test_self_install
spec_fetcher do |f|
f.gem 'a', 1
@@ -107,6 +132,83 @@ class TestGem < Gem::TestCase
assert_equal %w[a-1], installed.map { |spec| spec.full_name }
end
+ def test_self_install_permissions
+ assert_self_install_permissions
+ end
+
+ def test_self_install_permissions_umask_0
+ umask = File.umask(0)
+ assert_self_install_permissions
+ ensure
+ File.umask(umask)
+ end
+
+ def test_self_install_permissions_umask_077
+ umask = File.umask(077)
+ assert_self_install_permissions
+ ensure
+ File.umask(umask)
+ end
+
+ def test_self_install_permissions_with_format_executable
+ @format_executable = true
+ assert_self_install_permissions
+ end
+
+ def assert_self_install_permissions
+ mask = /mingw|mswin/ =~ RUBY_PLATFORM ? 0700 : 0777
+ options = {
+ :dir_mode => 0500,
+ :prog_mode => 0510,
+ :data_mode => 0640,
+ :wrappers => true,
+ :format_executable => !!(@format_executable if defined?(@format_executable))
+ }
+ Dir.chdir @tempdir do
+ Dir.mkdir 'bin'
+ File.open 'bin/foo.cmd', 'w' do |fp|
+ fp.chmod(0755)
+ fp.puts 'p'
+ end
+
+ Dir.mkdir 'data'
+ File.open 'data/foo.txt', 'w' do |fp|
+ fp.puts 'blah'
+ end
+
+ spec_fetcher do |f|
+ f.gem 'foo', 1 do |s|
+ s.executables = ['foo.cmd']
+ s.files = %w[bin/foo.cmd data/foo.txt]
+ end
+ end
+ Gem.install 'foo', Gem::Requirement.default, options
+ end
+
+ prog_mode = (options[:prog_mode] & mask).to_s(8)
+ dir_mode = (options[:dir_mode] & mask).to_s(8)
+ data_mode = (options[:data_mode] & mask).to_s(8)
+ prog_name = 'foo.cmd'
+ prog_name = RUBY_INSTALL_NAME.sub('ruby', 'foo.cmd') if options[:format_executable]
+ expected = {
+ "bin/#{prog_name}" => prog_mode,
+ 'gems/foo-1' => dir_mode,
+ 'gems/foo-1/bin' => dir_mode,
+ 'gems/foo-1/data' => dir_mode,
+ 'gems/foo-1/bin/foo.cmd' => prog_mode,
+ 'gems/foo-1/data/foo.txt' => data_mode,
+ }
+ result = {}
+ Dir.chdir @gemhome do
+ expected.each_key do |n|
+ result[n] = (File.stat(n).mode & mask).to_s(8)
+ end
+ end
+ assert_equal(expected, result)
+ ensure
+ File.chmod(0755, *Dir.glob(@gemhome+'/gems/**/').map {|path| path.untaint})
+ end
+
def test_require_missing
save_loaded_features do
assert_raises ::LoadError do
@@ -117,7 +219,7 @@ class TestGem < Gem::TestCase
def test_require_does_not_glob
save_loaded_features do
- a1 = new_spec "a", "1", nil, "lib/a1.rb"
+ a1 = util_spec "a", "1", nil, "lib/a1.rb"
install_specs a1
@@ -157,6 +259,70 @@ class TestGem < Gem::TestCase
assert_match 'a-2/bin/exec', Gem.bin_path('a', 'exec', '>= 0')
end
+ def test_activate_bin_path_resolves_eagerly
+ a1 = util_spec 'a', '1' do |s|
+ s.executables = ['exec']
+ s.add_dependency 'b'
+ end
+
+ b1 = util_spec 'b', '1' do |s|
+ s.add_dependency 'c', '2'
+ end
+
+ b2 = util_spec 'b', '2' do |s|
+ s.add_dependency 'c', '1'
+ end
+
+ c1 = util_spec 'c', '1'
+ c2 = util_spec 'c', '2'
+
+ install_specs c1, c2, b1, b2, a1
+
+ Gem.activate_bin_path("a", "exec", ">= 0")
+
+ # If we didn't eagerly resolve, this would activate c-2 and then the
+ # finish_resolve would cause a conflict
+ gem 'c'
+ Gem.finish_resolve
+
+ assert_equal %w(a-1 b-2 c-1), loaded_spec_names
+ end
+
+ def test_activate_bin_path_gives_proper_error_for_bundler
+ bundler = util_spec 'bundler', '2' do |s|
+ s.executables = ['bundle']
+ end
+
+ install_specs bundler
+
+ File.open("Gemfile.lock", "w") do |f|
+ f.write <<-L.gsub(/ {8}/, "")
+ GEM
+ remote: https://rubygems.org/
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+
+ BUNDLED WITH
+ 9999
+ L
+ end
+
+ File.open("Gemfile", "w") { |f| f.puts('source "https://rubygems.org"') }
+
+ e = assert_raises Gem::GemNotFoundException do
+ load Gem.activate_bin_path("bundler", "bundle", ">= 0.a")
+ end
+
+ assert_includes e.message, "Could not find 'bundler' (9999) required by your #{File.expand_path("Gemfile.lock")}."
+ assert_includes e.message, "To update to the latest version installed on your system, run `bundle update --bundler`."
+ assert_includes e.message, "To install the missing version, run `gem install bundler:9999`"
+ refute_includes e.message, "can't find gem bundler (>= 0.a) with executable bundle"
+ end
+
def test_self_bin_path_no_exec_name
e = assert_raises ArgumentError do
Gem.bin_path 'a'
@@ -252,11 +418,13 @@ class TestGem < Gem::TestCase
expected = File.join @gemhome, 'gems', foo.full_name, 'data', 'foo'
- assert_equal expected, Gem.datadir('foo')
+ assert_equal expected, Gem::Specification.find_by_name("foo").datadir
end
def test_self_datadir_nonexistent_package
- assert_nil Gem.datadir('xyzzy')
+ assert_raises(Gem::MissingSpecError) do
+ Gem::Specification.find_by_name("xyzzy").datadir
+ end
end
def test_self_default_exec_format
@@ -333,8 +501,7 @@ class TestGem < Gem::TestCase
assert_equal %w[https://rubygems.org/], Gem.default_sources
end
- def test_self_detect_gemdeps
- skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7"
+ def test_self_use_gemdeps
rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], '-'
FileUtils.mkdir_p 'detect/a/b'
@@ -345,7 +512,7 @@ class TestGem < Gem::TestCase
begin
Dir.chdir 'detect/a/b'
- assert_empty Gem.detect_gemdeps
+ assert_equal add_bundler_full_name([]), Gem.use_gemdeps.map(&:full_name)
ensure
Dir.chdir @tempdir
end
@@ -409,7 +576,7 @@ class TestGem < Gem::TestCase
assert File.directory?(util_cache_dir)
end
- unless win_platform? then # only for FS that support write protection
+ unless win_platform? || Process.uid.zero? # only for FS that support write protection
def test_self_ensure_gem_directories_write_protected
gemdir = File.join @tempdir, "egd"
FileUtils.rm_r gemdir rescue nil
@@ -488,11 +655,8 @@ class TestGem < Gem::TestCase
end
def test_self_find_files_with_gemfile
- # write_file(File.join Dir.pwd, 'Gemfile') fails on travis 1.8.7 with $SAFE=1
- skip if RUBY_VERSION <= "1.8.7"
-
cwd = File.expand_path("test/rubygems", @@project_dir)
- $LOAD_PATH.unshift cwd
+ actual_load_path = $LOAD_PATH.unshift(cwd).dup
discover_path = File.join 'lib', 'sff', 'discover.rb'
@@ -518,12 +682,12 @@ class TestGem < Gem::TestCase
expected = [
File.expand_path('test/rubygems/sff/discover.rb', @@project_dir),
File.join(foo1.full_gem_path, discover_path)
- ]
+ ].sort
- assert_equal expected, Gem.find_files('sff/discover')
- assert_equal expected, Gem.find_files('sff/**.rb'), '[ruby-core:31730]'
+ assert_equal expected, Gem.find_files('sff/discover').sort
+ assert_equal expected, Gem.find_files('sff/**.rb').sort, '[ruby-core:31730]'
ensure
- assert_equal cwd, $LOAD_PATH.shift unless RUBY_VERSION <= "1.8.7"
+ assert_equal cwd, actual_load_path.shift
end
def test_self_find_latest_files
@@ -721,7 +885,7 @@ class TestGem < Gem::TestCase
end
def test_self_read_binary
- open 'test', 'w' do |io|
+ File.open 'test', 'w' do |io|
io.write "\xCF\x80"
end
@@ -739,7 +903,6 @@ class TestGem < Gem::TestCase
end
def test_self_refresh
- skip 'Insecure operation - mkdir' if RUBY_VERSION <= "1.8.7"
util_make_gems
a1_spec = @a1.spec_file
@@ -759,7 +922,6 @@ class TestGem < Gem::TestCase
end
def test_self_refresh_keeps_loaded_specs_activated
- skip 'Insecure operation - mkdir' if RUBY_VERSION <= "1.8.7"
util_make_gems
a1_spec = @a1.spec_file
@@ -841,7 +1003,7 @@ class TestGem < Gem::TestCase
assert_equal Gem::Requirement.default, Gem.env_requirement('qux')
end
- def test_self_ruby_version_1_8_5
+ def test_self_ruby_version_with_patchlevel_less_ancient_rubies
util_set_RUBY_VERSION '1.8.5'
assert_equal Gem::Version.new('1.8.5'), Gem.ruby_version
@@ -849,7 +1011,7 @@ class TestGem < Gem::TestCase
util_restore_RUBY_VERSION
end
- def test_self_ruby_version_1_8_6p287
+ def test_self_ruby_version_with_release
util_set_RUBY_VERSION '1.8.6', 287
assert_equal Gem::Version.new('1.8.6.287'), Gem.ruby_version
@@ -857,10 +1019,34 @@ class TestGem < Gem::TestCase
util_restore_RUBY_VERSION
end
- def test_self_ruby_version_1_9_2dev_r23493
- util_set_RUBY_VERSION '1.9.2', -1, 23493
+ def test_self_ruby_version_with_non_mri_implementations
+ util_set_RUBY_VERSION '2.5.0', 0, 60928, 'jruby 9.2.0.0 (2.5.0) 2018-05-24 81156a8 OpenJDK 64-Bit Server VM 25.171-b11 on 1.8.0_171-8u171-b11-0ubuntu0.16.04.1-b11 [linux-x86_64]'
+
+ assert_equal Gem::Version.new('2.5.0'), Gem.ruby_version
+ ensure
+ util_restore_RUBY_VERSION
+ end
+
+ def test_self_ruby_version_with_prerelease
+ util_set_RUBY_VERSION '2.6.0', -1, 63539, 'ruby 2.6.0preview2 (2018-05-31 trunk 63539) [x86_64-linux]'
+
+ assert_equal Gem::Version.new('2.6.0.preview2'), Gem.ruby_version
+ ensure
+ util_restore_RUBY_VERSION
+ end
+
+ def test_self_ruby_version_with_non_mri_implementations_with_mri_prerelase_compatibility
+ util_set_RUBY_VERSION '2.6.0', -1, 63539, 'weirdjruby 9.2.0.0 (2.6.0preview2) 2018-05-24 81156a8 OpenJDK 64-Bit Server VM 25.171-b11 on 1.8.0_171-8u171-b11-0ubuntu0.16.04.1-b11 [linux-x86_64]', 'weirdjruby', '9.2.0.0'
+
+ assert_equal Gem::Version.new('2.6.0.preview2'), Gem.ruby_version
+ ensure
+ util_restore_RUBY_VERSION
+ end
+
+ def test_self_ruby_version_with_trunk
+ util_set_RUBY_VERSION '1.9.2', -1, 23493, 'ruby 1.9.2dev (2009-05-20 trunk 23493) [x86_64-linux]'
- assert_equal Gem::Version.new('1.9.2.dev.23493'), Gem.ruby_version
+ assert_equal Gem::Version.new('1.9.2.dev'), Gem.ruby_version
ensure
util_restore_RUBY_VERSION
end
@@ -1045,7 +1231,7 @@ class TestGem < Gem::TestCase
refute Gem.try_activate 'nonexistent'
end
- expected = "Ignoring ext-1 because its extensions are not built. " +
+ expected = "Ignoring ext-1 because its extensions are not built. " +
"Try: gem pristine ext --version 1\n"
assert_equal expected, err
@@ -1056,7 +1242,8 @@ class TestGem < Gem::TestCase
orig_path = ENV.delete 'GEM_PATH'
Gem.use_paths nil, nil
assert_equal Gem.default_dir, Gem.paths.home
- assert_equal (Gem.default_path + [Gem.paths.home]).uniq, Gem.paths.path
+ path = (Gem.default_path + [Gem.paths.home]).uniq
+ assert_equal path, Gem.paths.path
ensure
ENV['GEM_HOME'] = orig_home
ENV['GEM_PATH'] = orig_path
@@ -1107,7 +1294,7 @@ class TestGem < Gem::TestCase
end
def test_self_user_home
- if ENV['HOME'] then
+ if ENV['HOME']
assert_equal ENV['HOME'], Gem.user_home
else
assert true, 'count this test'
@@ -1133,13 +1320,12 @@ class TestGem < Gem::TestCase
end
def test_self_needs_picks_up_unresolved_deps
- skip 'loading from unsafe file' if RUBY_VERSION <= "1.8.7"
save_loaded_features do
util_clear_gems
a = util_spec "a", "1"
b = util_spec "b", "1", "c" => nil
c = util_spec "c", "2"
- d = new_spec "d", "1", {'e' => '= 1'}, "lib/d.rb"
+ d = util_spec "d", "1", {'e' => '= 1'}, "lib/d.rb"
e = util_spec "e", "1"
install_specs a, c, b, e, d
@@ -1159,72 +1345,23 @@ class TestGem < Gem::TestCase
input = "\x1F\x8B\b\0\xED\xA3\x1AQ\0\x03\xCBH" +
"\xCD\xC9\xC9\a\0\x86\xA6\x106\x05\0\0\0"
- output = Gem.gunzip input
+ output = Gem::Util.gunzip input
assert_equal 'hello', output
-
- return unless Object.const_defined? :Encoding
-
assert_equal Encoding::BINARY, output.encoding
end
def test_self_gzip
input = 'hello'
- output = Gem.gzip input
+ output = Gem::Util.gzip input
zipped = StringIO.new output
assert_equal 'hello', Zlib::GzipReader.new(zipped).read
-
- return unless Object.const_defined? :Encoding
-
assert_equal Encoding::BINARY, output.encoding
end
- if Gem.win_platform? && '1.9' > RUBY_VERSION
- # Ruby 1.9 properly handles ~ path expansion, so no need to run such tests.
- def test_self_user_home_userprofile
-
- Gem.clear_paths
-
- # safe-keep env variables
- orig_home, orig_user_profile = ENV['HOME'], ENV['USERPROFILE']
-
- # prepare for the test
- ENV.delete('HOME')
- ENV['USERPROFILE'] = "W:\\Users\\RubyUser"
-
- assert_equal 'W:/Users/RubyUser', Gem.user_home
-
- ensure
- ENV['HOME'] = orig_home
- ENV['USERPROFILE'] = orig_user_profile
- end
-
- def test_self_user_home_user_drive_and_path
- Gem.clear_paths
-
- # safe-keep env variables
- orig_home, orig_user_profile = ENV['HOME'], ENV['USERPROFILE']
- orig_home_drive, orig_home_path = ENV['HOMEDRIVE'], ENV['HOMEPATH']
-
- # prepare the environment
- ENV.delete('HOME')
- ENV.delete('USERPROFILE')
- ENV['HOMEDRIVE'] = 'Z:'
- ENV['HOMEPATH'] = "\\Users\\RubyUser"
-
- assert_equal 'Z:/Users/RubyUser', Gem.user_home
-
- ensure
- ENV['HOME'] = orig_home
- ENV['USERPROFILE'] = orig_user_profile
- ENV['HOMEDRIVE'] = orig_home_drive
- ENV['HOMEPATH'] = orig_home_path
- end
- end
-
def test_self_vendor_dir
expected =
File.join RbConfig::CONFIG['vendordir'], 'gems',
@@ -1250,7 +1387,6 @@ class TestGem < Gem::TestCase
end
def test_load_plugins
- skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7"
plugin_path = File.join "lib", "rubygems_plugin.rb"
Dir.chdir @tempdir do
@@ -1305,8 +1441,8 @@ class TestGem < Gem::TestCase
write_file File.join(@tempdir, 'lib', "g.rb") { |fp| fp.puts "" }
write_file File.join(@tempdir, 'lib', 'm.rb') { |fp| fp.puts "" }
- g = new_spec 'g', '1', nil, "lib/g.rb"
- m = new_spec 'm', '1', nil, "lib/m.rb"
+ g = util_spec 'g', '1', nil, "lib/g.rb"
+ m = util_spec 'm', '1', nil, "lib/m.rb"
install_gem g, :install_dir => Gem.dir
m0 = install_gem m, :install_dir => Gem.dir
@@ -1361,8 +1497,8 @@ class TestGem < Gem::TestCase
write_file File.join(@tempdir, 'lib', "g.rb") { |fp| fp.puts "" }
write_file File.join(@tempdir, 'lib', 'm.rb') { |fp| fp.puts "" }
- g = new_spec 'g', '1', nil, "lib/g.rb"
- m = new_spec 'm', '1', nil, "lib/m.rb"
+ g = util_spec 'g', '1', nil, "lib/g.rb"
+ m = util_spec 'm', '1', nil, "lib/m.rb"
install_gem g, :install_dir => Gem.dir
install_gem m, :install_dir => Gem.dir
@@ -1379,9 +1515,9 @@ class TestGem < Gem::TestCase
def test_auto_activation_of_specific_gemdeps_file
util_clear_gems
- a = new_spec "a", "1", nil, "lib/a.rb"
- b = new_spec "b", "1", nil, "lib/b.rb"
- c = new_spec "c", "1", nil, "lib/c.rb"
+ a = util_spec "a", "1", nil, "lib/a.rb"
+ b = util_spec "b", "1", nil, "lib/b.rb"
+ c = util_spec "c", "1", nil, "lib/c.rb"
install_specs a, b, c
@@ -1395,18 +1531,17 @@ class TestGem < Gem::TestCase
ENV['RUBYGEMS_GEMDEPS'] = path
- Gem.detect_gemdeps
+ Gem.use_gemdeps
- assert_equal %w!a-1 b-1 c-1!, loaded_spec_names
+ assert_equal add_bundler_full_name(%W(a-1 b-1 c-1)), loaded_spec_names
end
- def test_auto_activation_of_detected_gemdeps_file
- skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7"
+ def test_auto_activation_of_used_gemdeps_file
util_clear_gems
- a = new_spec "a", "1", nil, "lib/a.rb"
- b = new_spec "b", "1", nil, "lib/b.rb"
- c = new_spec "c", "1", nil, "lib/c.rb"
+ a = util_spec "a", "1", nil, "lib/a.rb"
+ b = util_spec "b", "1", nil, "lib/b.rb"
+ c = util_spec "c", "1", nil, "lib/c.rb"
install_specs a, b, c
@@ -1420,28 +1555,33 @@ class TestGem < Gem::TestCase
ENV['RUBYGEMS_GEMDEPS'] = "-"
- assert_equal [a,b,c], Gem.detect_gemdeps.sort_by { |s| s.name }
+ expected_specs = [a, b, (Gem::USE_BUNDLER_FOR_GEMDEPS || nil) && util_spec("bundler", Bundler::VERSION), c].compact
+ assert_equal expected_specs, Gem.use_gemdeps.sort_by { |s| s.name }
end
LIB_PATH = File.expand_path "../../../lib".dup.untaint, __FILE__.dup.untaint
+ if Gem::USE_BUNDLER_FOR_GEMDEPS
+ BUNDLER_LIB_PATH = File.expand_path $LOAD_PATH.find {|lp| File.file?(File.join(lp, "bundler.rb")) }.dup.untaint
+ BUNDLER_FULL_NAME = "bundler-#{Bundler::VERSION}".freeze
+ end
+
+ def add_bundler_full_name(names)
+ return names unless Gem::USE_BUNDLER_FOR_GEMDEPS
+ names << BUNDLER_FULL_NAME
+ names.sort!
+ names
+ end
+
def test_looks_for_gemdeps_files_automatically_on_start
util_clear_gems
- a = new_spec "a", "1", nil, "lib/a.rb"
- b = new_spec "b", "1", nil, "lib/b.rb"
- c = new_spec "c", "1", nil, "lib/c.rb"
+ a = util_spec "a", "1", nil, "lib/a.rb"
+ b = util_spec "b", "1", nil, "lib/b.rb"
+ c = util_spec "c", "1", nil, "lib/c.rb"
install_specs a, b, c
- path = File.join @tempdir, "gem.deps.rb"
-
- File.open path, "w" do |f|
- f.puts "gem 'a'"
- f.puts "gem 'b'"
- f.puts "gem 'c'"
- end
-
path = File.join(@tempdir, "gd-tmp")
install_gem a, :install_dir => path
install_gem b, :install_dir => path
@@ -1450,27 +1590,33 @@ class TestGem < Gem::TestCase
ENV['GEM_PATH'] = path
ENV['RUBYGEMS_GEMDEPS'] = "-"
- out = `#{Gem.ruby.dup.untaint} -I "#{LIB_PATH.untaint}" -rubygems -e "p Gem.loaded_specs.values.map(&:full_name).sort"`
-
- assert_equal '["a-1", "b-1", "c-1"]', out.strip
- end
-
- def test_looks_for_gemdeps_files_automatically_on_start_in_parent_dir
- util_clear_gems
-
- a = new_spec "a", "1", nil, "lib/a.rb"
- b = new_spec "b", "1", nil, "lib/b.rb"
- c = new_spec "c", "1", nil, "lib/c.rb"
-
- install_specs a, b, c
-
path = File.join @tempdir, "gem.deps.rb"
+ cmd = [Gem.ruby.dup.untaint, "-I#{LIB_PATH.untaint}",
+ "-I#{BUNDLER_LIB_PATH.untaint}", "-rrubygems"]
+ cmd << "-eputs Gem.loaded_specs.values.map(&:full_name).sort"
File.open path, "w" do |f|
f.puts "gem 'a'"
+ end
+ out0 = IO.popen(cmd, &:read).split(/\n/)
+
+ File.open path, "a" do |f|
f.puts "gem 'b'"
f.puts "gem 'c'"
end
+ out = IO.popen(cmd, &:read).split(/\n/)
+
+ assert_equal ["b-1", "c-1"], out - out0
+ end if Gem::USE_BUNDLER_FOR_GEMDEPS
+
+ def test_looks_for_gemdeps_files_automatically_on_start_in_parent_dir
+ util_clear_gems
+
+ a = util_spec "a", "1", nil, "lib/a.rb"
+ b = util_spec "b", "1", nil, "lib/b.rb"
+ c = util_spec "c", "1", nil, "lib/c.rb"
+
+ install_specs a, b, c
path = File.join(@tempdir, "gd-tmp")
install_gem a, :install_dir => path
@@ -1481,14 +1627,27 @@ class TestGem < Gem::TestCase
ENV['RUBYGEMS_GEMDEPS'] = "-"
Dir.mkdir "sub1"
- out = Dir.chdir "sub1" do
- `#{Gem.ruby.dup.untaint} -I "#{LIB_PATH.untaint}" -rubygems -e "p Gem.loaded_specs.values.map(&:full_name).sort"`
+
+ path = File.join @tempdir, "gem.deps.rb"
+ cmd = [Gem.ruby.dup.untaint, "-Csub1", "-I#{LIB_PATH.untaint}",
+ "-I#{BUNDLER_LIB_PATH.untaint}", "-rrubygems"]
+ cmd << "-eputs Gem.loaded_specs.values.map(&:full_name).sort"
+
+ File.open path, "w" do |f|
+ f.puts "gem 'a'"
end
+ out0 = IO.popen(cmd, &:read).split(/\n/)
+
+ File.open path, "a" do |f|
+ f.puts "gem 'b'"
+ f.puts "gem 'c'"
+ end
+ out = IO.popen(cmd, &:read).split(/\n/)
Dir.rmdir "sub1"
- assert_equal '["a-1", "b-1", "c-1"]', out.strip
- end
+ assert_equal ["b-1", "c-1"], out - out0
+ end if Gem::USE_BUNDLER_FOR_GEMDEPS
def test_register_default_spec
Gem.clear_default_specs
@@ -1501,7 +1660,7 @@ class TestGem < Gem::TestCase
assert_equal old_style, Gem.find_unresolved_default_spec("foo.rb")
assert_equal old_style, Gem.find_unresolved_default_spec("bar.rb")
- assert_equal nil, Gem.find_unresolved_default_spec("baz.rb")
+ assert_nil Gem.find_unresolved_default_spec("baz.rb")
Gem.clear_default_specs
@@ -1514,13 +1673,13 @@ class TestGem < Gem::TestCase
assert_equal new_style, Gem.find_unresolved_default_spec("foo.rb")
assert_equal new_style, Gem.find_unresolved_default_spec("bar.rb")
- assert_equal nil, Gem.find_unresolved_default_spec("exec")
- assert_equal nil, Gem.find_unresolved_default_spec("README")
+ assert_nil Gem.find_unresolved_default_spec("exec")
+ assert_nil Gem.find_unresolved_default_spec("README")
end
def test_default_gems_use_full_paths
begin
- if defined?(RUBY_ENGINE) then
+ if defined?(RUBY_ENGINE)
engine = RUBY_ENGINE
Object.send :remove_const, :RUBY_ENGINE
end
@@ -1533,7 +1692,7 @@ class TestGem < Gem::TestCase
end
begin
- if defined?(RUBY_ENGINE) then
+ if defined?(RUBY_ENGINE)
engine = RUBY_ENGINE
Object.send :remove_const, :RUBY_ENGINE
end
@@ -1553,7 +1712,7 @@ class TestGem < Gem::TestCase
spec = Gem::Specification.find { |s| s == spec }
refute spec.activated?
- open gem_deps_file, 'w' do |io|
+ File.open gem_deps_file, 'w' do |io|
io.write 'gem "a"'
end
@@ -1561,7 +1720,7 @@ class TestGem < Gem::TestCase
Gem.use_gemdeps gem_deps_file
- assert spec.activated?
+ assert_equal add_bundler_full_name(%W(a-1)), loaded_spec_names
refute_nil Gem.gemdeps
end
@@ -1572,7 +1731,7 @@ class TestGem < Gem::TestCase
refute spec.activated?
- open 'gem.deps.rb', 'w' do |io|
+ File.open 'gem.deps.rb', 'w' do |io|
io.write 'gem "a"'
end
@@ -1607,7 +1766,6 @@ class TestGem < Gem::TestCase
end
def test_use_gemdeps_automatic
- skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7"
rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], '-'
spec = util_spec 'a', 1
@@ -1616,19 +1774,18 @@ class TestGem < Gem::TestCase
refute spec.activated?
- open 'Gemfile', 'w' do |io|
+ File.open 'Gemfile', 'w' do |io|
io.write 'gem "a"'
end
Gem.use_gemdeps
- assert spec.activated?
+ assert_equal add_bundler_full_name(%W(a-1)), loaded_spec_names
ensure
ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
end
def test_use_gemdeps_automatic_missing
- skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7"
rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], '-'
Gem.use_gemdeps
@@ -1645,7 +1802,7 @@ class TestGem < Gem::TestCase
refute spec.activated?
- open 'gem.deps.rb', 'w' do |io|
+ File.open 'gem.deps.rb', 'w' do |io|
io.write 'gem "a"'
end
@@ -1657,28 +1814,41 @@ class TestGem < Gem::TestCase
end
def test_use_gemdeps_missing_gem
- skip 'Insecure operation - read' if RUBY_VERSION <= "1.8.7"
rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], 'x'
- open 'x', 'w' do |io|
+ File.open 'x', 'w' do |io|
io.write 'gem "a"'
end
- expected = <<-EXPECTED
+ platform = Bundler::GemHelpers.generic_local_platform
+ if platform == Gem::Platform::RUBY
+ platform = ''
+ else
+ platform = " #{platform}"
+ end
+ expected =
+ if Gem::USE_BUNDLER_FOR_GEMDEPS
+ <<-EXPECTED
+Could not find gem 'a#{platform}' in any of the gem sources listed in your Gemfile.
+You may need to `gem install -g` to install missing gems
+
+ EXPECTED
+ else
+ <<-EXPECTED
Unable to resolve dependency: user requested 'a (>= 0)'
You may need to `gem install -g` to install missing gems
- EXPECTED
+ EXPECTED
+ end
assert_output nil, expected do
Gem.use_gemdeps
end
ensure
ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
- end
+ end if Gem::USE_BUNDLER_FOR_GEMDEPS
def test_use_gemdeps_specific
- skip 'Insecure operation - read' if RUBY_VERSION <= "1.8.7"
rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], 'x'
spec = util_spec 'a', 1
@@ -1687,17 +1857,24 @@ You may need to `gem install -g` to install missing gems
spec = Gem::Specification.find { |s| s == spec }
refute spec.activated?
- open 'x', 'w' do |io|
+ File.open 'x', 'w' do |io|
io.write 'gem "a"'
end
Gem.use_gemdeps
- assert spec.activated?
+ assert_equal add_bundler_full_name(%W(a-1)), loaded_spec_names
ensure
ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
end
+ def test_operating_system_defaults
+ operating_system_defaults = Gem.operating_system_defaults
+
+ assert operating_system_defaults != nil
+ assert operating_system_defaults.is_a? Hash
+ end
+
def test_platform_defaults
platform_defaults = Gem.platform_defaults
@@ -1705,13 +1882,13 @@ You may need to `gem install -g` to install missing gems
assert platform_defaults.is_a? Hash
end
- def ruby_install_name name
+ def ruby_install_name(name)
orig_RUBY_INSTALL_NAME = RbConfig::CONFIG['ruby_install_name']
RbConfig::CONFIG['ruby_install_name'] = name
yield
ensure
- if orig_RUBY_INSTALL_NAME then
+ if orig_RUBY_INSTALL_NAME
RbConfig::CONFIG['ruby_install_name'] = orig_RUBY_INSTALL_NAME
else
RbConfig::CONFIG.delete 'ruby_install_name'
diff --git a/test/rubygems/test_gem_bundler_version_finder.rb b/test/rubygems/test_gem_bundler_version_finder.rb
new file mode 100644
index 0000000000..3b63b89423
--- /dev/null
+++ b/test/rubygems/test_gem_bundler_version_finder.rb
@@ -0,0 +1,126 @@
+# frozen_string_literal: true
+require 'rubygems/test_case'
+
+class TestGemBundlerVersionFinder < Gem::TestCase
+ def setup
+ @argv = ARGV.dup
+ @env = ENV.to_hash.clone
+ ENV.delete("BUNDLER_VERSION")
+ @dollar_0 = $0
+ end
+
+ def teardown
+ ARGV.replace @argv
+ ENV.replace @env
+ $0 = @dollar_0
+ end
+
+ def bvf
+ Gem::BundlerVersionFinder
+ end
+
+ def test_bundler_version_defaults_to_nil
+ assert_nil bvf.bundler_version
+ end
+
+ def test_bundler_version_with_env_var
+ ENV["BUNDLER_VERSION"] = "1.1.1.1"
+ assert_equal v("1.1.1.1"), bvf.bundler_version
+ end
+
+ def test_bundler_version_with_bundle_update_bundler
+ ARGV.replace %w[update --bundler]
+ assert_nil bvf.bundler_version
+ $0 = "/foo/bar/bundle"
+ assert_nil bvf.bundler_version
+ ARGV.replace %w[update --bundler=1.1.1.1 gem_name]
+ assert_equal v("1.1.1.1"), bvf.bundler_version
+ ARGV.replace %w[update --bundler 1.1.1.1 gem_name]
+ assert_equal v("1.1.1.1"), bvf.bundler_version
+ ARGV.replace %w[update --bundler\ 1.1.1.1 gem_name]
+ assert_equal v("1.1.1.1"), bvf.bundler_version
+ ARGV.replace %w[update --bundler\ 1.1.1.2 --bundler --bundler 1.1.1.1 gem_name]
+ assert_equal v("1.1.1.1"), bvf.bundler_version
+ $0 = "other"
+ assert_nil bvf.bundler_version
+ end
+
+ def test_bundler_version_with_lockfile
+ bvf.stub(:lockfile_contents, [nil, ""]) do
+ assert_nil bvf.bundler_version
+ end
+ bvf.stub(:lockfile_contents, [nil, "\n\nBUNDLED WITH\n 1.1.1.1\n"]) do
+ assert_equal v("1.1.1.1"), bvf.bundler_version
+ end
+ bvf.stub(:lockfile_contents, [nil, "\n\nBUNDLED WITH\n fjdkslfjdkslfjsldk\n"]) do
+ assert_nil bvf.bundler_version
+ end
+ end
+
+ def test_bundler_version_with_reason
+ assert_nil bvf.bundler_version_with_reason
+ bvf.stub(:lockfile_contents, [nil, "\n\nBUNDLED WITH\n 1.1.1.1\n"]) do
+ assert_equal ["1.1.1.1", "your lockfile"], bvf.bundler_version_with_reason
+
+ $0 = "bundle"
+ ARGV.replace %w[update --bundler]
+ assert_nil bvf.bundler_version_with_reason
+ ARGV.replace %w[update --bundler=1.1.1.2]
+ assert_equal ["1.1.1.2", "`bundle update --bundler`"], bvf.bundler_version_with_reason
+
+ ENV["BUNDLER_VERSION"] = "1.1.1.3"
+ assert_equal ["1.1.1.3", "`$BUNDLER_VERSION`"], bvf.bundler_version_with_reason
+ end
+ end
+
+ def test_compatible
+ assert bvf.compatible?(util_spec("foo"))
+ assert bvf.compatible?(util_spec("bundler", 1.1))
+
+ bvf.stub(:bundler_version, v("1.1.1.1")) do
+ assert bvf.compatible?(util_spec("foo"))
+ assert bvf.compatible?(util_spec("bundler", "1.1.1.1"))
+ assert bvf.compatible?(util_spec("bundler", "1.1.1.a"))
+ assert bvf.compatible?(util_spec("bundler", "1.999"))
+ refute bvf.compatible?(util_spec("bundler", "2.999"))
+ end
+
+ bvf.stub(:bundler_version, v("2.1.1.1")) do
+ assert bvf.compatible?(util_spec("foo"))
+ assert bvf.compatible?(util_spec("bundler", "2.1.1.1"))
+ assert bvf.compatible?(util_spec("bundler", "2.1.1.a"))
+ assert bvf.compatible?(util_spec("bundler", "2.999"))
+ refute bvf.compatible?(util_spec("bundler", "1.999"))
+ refute bvf.compatible?(util_spec("bundler", "3.0.0"))
+ end
+ end
+
+ def test_filter
+ versions = %w[1 1.0 1.0.1.1 2 2.a 2.0 2.1.1 3 3.a 3.0 3.1.1]
+ specs = versions.map { |v| util_spec("bundler", v) }
+
+ assert_equal %w[1 1.0 1.0.1.1 2 2.a 2.0 2.1.1 3 3.a 3.0 3.1.1], util_filter_specs(specs).map(&:version).map(&:to_s)
+
+ bvf.stub(:bundler_version, v("2.1.1.1")) do
+ assert_equal %w[2 2.a 2.0 2.1.1], util_filter_specs(specs).map(&:version).map(&:to_s)
+ end
+ bvf.stub(:bundler_version, v("1.1.1.1")) do
+ assert_equal %w[1 1.0 1.0.1.1], util_filter_specs(specs).map(&:version).map(&:to_s)
+ end
+ bvf.stub(:bundler_version, v("1")) do
+ assert_equal %w[1 1.0 1.0.1.1], util_filter_specs(specs).map(&:version).map(&:to_s)
+ end
+ bvf.stub(:bundler_version, v("2.a")) do
+ assert_equal %w[2 2.a 2.0 2.1.1], util_filter_specs(specs).map(&:version).map(&:to_s)
+ end
+ bvf.stub(:bundler_version, v("3")) do
+ assert_equal %w[3 3.a 3.0 3.1.1], util_filter_specs(specs).map(&:version).map(&:to_s)
+ end
+ end
+
+ def util_filter_specs(specs)
+ specs = specs.dup
+ bvf.filter!(specs)
+ specs
+ end
+end
diff --git a/test/rubygems/test_gem_command.rb b/test/rubygems/test_gem_command.rb
index 8100a34c25..8caa9c6e2e 100644
--- a/test/rubygems/test_gem_command.rb
+++ b/test/rubygems/test_gem_command.rb
@@ -13,6 +13,7 @@ class TestGemCommand < Gem::TestCase
@xopt = nil
+ @common_options = Gem::Command.common_options.dup
Gem::Command.common_options.clear
Gem::Command.common_options << [
['-x', '--exe', 'Execute'], lambda do |*a|
@@ -24,6 +25,11 @@ class TestGemCommand < Gem::TestCase
@cmd = Gem::Command.new @cmd_name, 'summary'
end
+ def teardown
+ super
+ Gem::Command.common_options.replace @common_options
+ end
+
def test_self_add_specific_extra_args
added_args = %w[--all]
@cmd.add_option '--all' do |v,o| end
@@ -170,7 +176,7 @@ class TestGemCommand < Gem::TestCase
@cmd.add_option('-f', '--file FILE', 'File option') do |value, options|
options[:help] = true
end
- @cmd.add_option('--silent', 'Silence rubygems output') do |value, options|
+ @cmd.add_option('--silent', 'Silence RubyGems output') do |value, options|
options[:silent] = true
end
assert @cmd.handles?(['-x'])
@@ -245,4 +251,3 @@ ERROR: Possible alternatives: non_existent_with_hint
end
end
-
diff --git a/test/rubygems/test_gem_command_manager.rb b/test/rubygems/test_gem_command_manager.rb
index 2d2fc7d7a0..6ada96f1c1 100644
--- a/test/rubygems/test_gem_command_manager.rb
+++ b/test/rubygems/test_gem_command_manager.rb
@@ -29,6 +29,12 @@ class TestGemCommandManager < Gem::TestCase
e.message
end
+ def test_find_alias_command
+ command = @command_manager.find_command 'i'
+
+ assert_kind_of Gem::Commands::InstallCommand, command
+ end
+
def test_find_command_ambiguous_exact
ins_command = Class.new
Gem::Commands.send :const_set, :InsCommand, ins_command
@@ -114,13 +120,13 @@ class TestGemCommandManager < Gem::TestCase
assert_equal :both, check_options[:domain]
assert_equal true, check_options[:wrappers]
assert_equal Gem::Requirement.default, check_options[:version]
- assert_equal nil, check_options[:install_dir]
- assert_equal nil, check_options[:bin_dir]
+ assert_nil check_options[:install_dir]
+ assert_nil check_options[:bin_dir]
#check settings
check_options = nil
@command_manager.process_args %w[
- install --force --local --rdoc --install-dir .
+ install --force --local --document=ri,rdoc --install-dir .
--version 3.0 --no-wrapper --bindir .
]
assert_equal %w[rdoc ri], check_options[:document].sort
@@ -254,11 +260,10 @@ class TestGemCommandManager < Gem::TestCase
#check settings
check_options = nil
- @command_manager.process_args %w[update --force --rdoc --install-dir .]
+ @command_manager.process_args %w[update --force --document=ri --install-dir .]
assert_includes check_options[:document], 'ri'
assert_equal true, check_options[:force]
assert_equal Dir.pwd, check_options[:install_dir]
end
end
-
diff --git a/test/rubygems/test_gem_commands_build_command.rb b/test/rubygems/test_gem_commands_build_command.rb
index 703512ecba..ac82a408c7 100644
--- a/test/rubygems/test_gem_commands_build_command.rb
+++ b/test/rubygems/test_gem_commands_build_command.rb
@@ -1,3 +1,4 @@
+
# frozen_string_literal: true
require 'rubygems/test_case'
require 'rubygems/commands/build_command'
@@ -5,16 +6,71 @@ require 'rubygems/package'
class TestGemCommandsBuildCommand < Gem::TestCase
+ CERT_FILE = cert_path 'public3072'
+ SIGNING_KEY = key_path 'private3072'
+
+ EXPIRED_CERT_FILE = cert_path 'expired'
+ PRIVATE_KEY_FILE = key_path 'private'
+
def setup
super
+ readme_file = File.join(@tempdir, 'README.md')
+
+ File.open readme_file, 'w' do |f|
+ f.write 'My awesome gem'
+ end
+
@gem = util_spec 'some_gem' do |s|
- s.rubyforge_project = 'example'
+ s.license = 'AGPL-3.0'
+ s.files = ['README.md']
end
@cmd = Gem::Commands::BuildCommand.new
end
+ def test_handle_options
+ @cmd.handle_options %w[--force --strict]
+
+ assert @cmd.options[:force]
+ assert @cmd.options[:strict]
+ end
+
+ def test_options_filename
+ gemspec_file = File.join(@tempdir, @gem.spec_name)
+
+ File.open gemspec_file, 'w' do |gs|
+ gs.write @gem.to_ruby
+ end
+
+ @cmd.options[:args] = [gemspec_file]
+ @cmd.options[:output] = "test.gem"
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ file = File.join(@tempdir, File::SEPARATOR, "test.gem")
+ assert File.exist?(file)
+
+ output = @ui.output.split "\n"
+ assert_equal " Successfully built RubyGem", output.shift
+ assert_equal " Name: some_gem", output.shift
+ assert_equal " Version: 2", output.shift
+ assert_equal " File: test.gem", output.shift
+ assert_equal [], output
+ end
+
+ def test_handle_options_defaults
+ @cmd.handle_options []
+
+ refute @cmd.options[:force]
+ refute @cmd.options[:strict]
+ assert_nil @cmd.options[:output]
+ end
+
def test_execute
gemspec_file = File.join(@tempdir, @gem.spec_name)
@@ -22,7 +78,80 @@ class TestGemCommandsBuildCommand < Gem::TestCase
gs.write @gem.to_ruby
end
- util_test_build_gem @gem, gemspec_file
+ @cmd.options[:args] = [gemspec_file]
+
+ util_test_build_gem @gem
+ end
+
+ def test_execute_bad_name
+ [".", "-", "_"].each do |special_char|
+ gem = util_spec 'some_gem_with_bad_name' do |s|
+ s.name = "#{special_char}bad_gem_name"
+ s.license = 'AGPL-3.0'
+ s.files = ['README.md']
+ end
+
+ gemspec_file = File.join(@tempdir, gem.spec_name)
+
+ File.open gemspec_file, 'w' do |gs|
+ gs.write gem.to_ruby
+ end
+
+ @cmd.options[:args] = [gemspec_file]
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ assert_raises Gem::InvalidSpecificationException do
+ @cmd.execute
+ end
+ end
+ end
+ end
+ end
+
+ def test_execute_strict_without_warnings
+ gemspec_file = File.join(@tempdir, @gem.spec_name)
+
+ File.open gemspec_file, 'w' do |gs|
+ gs.write @gem.to_ruby
+ end
+
+ @cmd.options[:strict] = true
+ @cmd.options[:args] = [gemspec_file]
+
+ util_test_build_gem @gem
+ end
+
+ def test_execute_strict_with_warnings
+ bad_gem = util_spec 'some_bad_gem' do |s|
+ s.files = ['README.md']
+ end
+
+ gemspec_file = File.join(@tempdir, bad_gem.spec_name)
+
+ File.open gemspec_file, 'w' do |gs|
+ gs.write bad_gem.to_ruby
+ end
+
+ @cmd.options[:args] = [gemspec_file]
+ @cmd.options[:strict] = true
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ assert_raises Gem::InvalidSpecificationException do
+ @cmd.execute
+ end
+ end
+ end
+
+ error = @ui.error.split "\n"
+ assert_equal "WARNING: licenses is empty, but is recommended. Use a license identifier from", error.shift
+ assert_equal "http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.", error.shift
+ assert_equal "WARNING: See http://guides.rubygems.org/specification-reference/ for help", error.shift
+ assert_equal [], error
+
+ gem_file = File.join @tempdir, File.basename(@gem.cache_file)
+ refute File.exist?(gem_file)
end
def test_execute_bad_spec
@@ -63,6 +192,43 @@ class TestGemCommandsBuildCommand < Gem::TestCase
assert_equal "ERROR: Gemspec file not found: some_gem\n", @ui.error
end
+ def test_execute_outside_dir
+ gemspec_dir = File.join @tempdir, 'build_command_gem'
+ gemspec_file = File.join gemspec_dir, @gem.spec_name
+ readme_file = File.join gemspec_dir, 'README.md'
+
+ FileUtils.mkdir_p gemspec_dir
+
+ File.open readme_file, 'w' do |f|
+ f.write "My awesome gem"
+ end
+
+ File.open gemspec_file, 'w' do |gs|
+ gs.write @gem.to_ruby
+ end
+
+ @cmd.options[:args] = [gemspec_file]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ output = @ui.output.split "\n"
+ assert_equal " Successfully built RubyGem", output.shift
+ assert_equal " Name: some_gem", output.shift
+ assert_equal " Version: 2", output.shift
+ assert_equal " File: some_gem-2.gem", output.shift
+ assert_equal [], output
+
+ gem_file = File.join gemspec_dir, File.basename(@gem.cache_file)
+ assert File.exist?(gem_file)
+
+ spec = Gem::Package.new(gem_file).spec
+
+ assert_equal "some_gem", spec.name
+ assert_equal "this is a summary", spec.summary
+ end
+
def test_can_find_gemspecs_without_dot_gemspec
gemspec_file = File.join(@tempdir, @gem.spec_name)
@@ -70,12 +236,12 @@ class TestGemCommandsBuildCommand < Gem::TestCase
gs.write @gem.to_ruby
end
- util_test_build_gem @gem, gemspec_file
- end
-
- def util_test_build_gem(gem, gemspec_file, check_licenses=true)
@cmd.options[:args] = [gemspec_file]
+ util_test_build_gem @gem
+ end
+
+ def util_test_build_gem(gem)
use_ui @ui do
Dir.chdir @tempdir do
@cmd.execute
@@ -89,10 +255,6 @@ class TestGemCommandsBuildCommand < Gem::TestCase
assert_equal " File: some_gem-2.gem", output.shift
assert_equal [], output
- if check_licenses
- assert_match "WARNING: licenses is empty", @ui.error
- end
-
gem_file = File.join @tempdir, File.basename(gem.cache_file)
assert File.exist?(gem_file)
@@ -114,8 +276,78 @@ class TestGemCommandsBuildCommand < Gem::TestCase
@cmd.options[:args] = [gemspec_file]
@cmd.options[:force] = true
- util_test_build_gem @gem, gemspec_file, false
+ util_test_build_gem @gem
end
-end
+ def test_build_signed_gem
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ trust_dir = Gem::Security.trust_dir
+
+ spec = util_spec 'some_gem' do |s|
+ s.signing_key = SIGNING_KEY
+ s.cert_chain = [CERT_FILE]
+ end
+
+ gemspec_file = File.join(@tempdir, spec.spec_name)
+
+ File.open gemspec_file, 'w' do |gs|
+ gs.write spec.to_ruby
+ end
+
+ @cmd.options[:args] = [gemspec_file]
+
+ util_test_build_gem spec
+
+ trust_dir.trust_cert OpenSSL::X509::Certificate.new(File.read(CERT_FILE))
+
+ gem = Gem::Package.new(File.join(@tempdir, spec.file_name),
+ Gem::Security::HighSecurity)
+ assert gem.verify
+ end
+
+ def test_build_signed_gem_with_cert_expiration_length_days
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ gem_path = File.join Gem.user_home, ".gem"
+ Dir.mkdir gem_path
+
+ Gem::Security.trust_dir
+
+ tmp_expired_cert_file = File.join gem_path, "gem-public_cert.pem"
+ File.write(tmp_expired_cert_file, File.read(EXPIRED_CERT_FILE))
+
+ tmp_private_key_file = File.join gem_path, "gem-private_key.pem"
+ File.write(tmp_private_key_file, File.read(PRIVATE_KEY_FILE))
+
+ spec = util_spec 'some_gem' do |s|
+ s.signing_key = tmp_private_key_file
+ s.cert_chain = [tmp_expired_cert_file]
+ end
+
+ gemspec_file = File.join(@tempdir, spec.spec_name)
+ File.open gemspec_file, 'w' do |gs|
+ gs.write spec.to_ruby
+ end
+
+ @cmd.options[:args] = [gemspec_file]
+
+ Gem.configuration.cert_expiration_length_days = 28
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ re_signed_cert = OpenSSL::X509::Certificate.new(File.read(tmp_expired_cert_file))
+ cert_days_to_expire = (re_signed_cert.not_after - re_signed_cert.not_before).to_i / (24 * 60 * 60)
+
+ gem_file = File.join @tempdir, File.basename(spec.cache_file)
+
+ assert File.exist?(gem_file)
+ assert_equal(28, cert_days_to_expire)
+ end
+
+end
diff --git a/test/rubygems/test_gem_commands_cert_command.rb b/test/rubygems/test_gem_commands_cert_command.rb
index a9a78336aa..d7381f14d0 100644
--- a/test/rubygems/test_gem_commands_cert_command.rb
+++ b/test/rubygems/test_gem_commands_cert_command.rb
@@ -1,23 +1,24 @@
# frozen_string_literal: true
require 'rubygems/test_case'
require 'rubygems/commands/cert_command'
-require 'rubygems/fix_openssl_warnings' if RUBY_VERSION < "1.9"
-unless defined?(OpenSSL::SSL) then
+unless defined?(OpenSSL::SSL)
warn 'Skipping `gem cert` tests. openssl not found.'
end
class TestGemCommandsCertCommand < Gem::TestCase
ALTERNATE_CERT = load_cert 'alternate'
+ EXPIRED_PUBLIC_CERT = load_cert 'expired'
ALTERNATE_KEY_FILE = key_path 'alternate'
PRIVATE_KEY_FILE = key_path 'private'
PUBLIC_KEY_FILE = key_path 'public'
- ALTERNATE_CERT_FILE = cert_path 'alternate'
- CHILD_CERT_FILE = cert_path 'child'
- PUBLIC_CERT_FILE = cert_path 'public'
+ ALTERNATE_CERT_FILE = cert_path 'alternate'
+ CHILD_CERT_FILE = cert_path 'child'
+ PUBLIC_CERT_FILE = cert_path 'public'
+ EXPIRED_PUBLIC_CERT_FILE = cert_path 'expired'
def setup
super
@@ -130,6 +131,69 @@ Added '/CN=alternate/DC=example'
assert_path_exists File.join(@tempdir, 'gem-public_cert.pem')
end
+ def test_execute_build_bad_email_address
+ passphrase = 'Foo bar'
+ email = "nobody@"
+
+ @cmd.handle_options %W[--build #{email}]
+
+ @build_ui = Gem::MockGemUi.new "#{passphrase}\n#{passphrase}"
+
+ use_ui @build_ui do
+
+ e = assert_raises Gem::CommandLineError do
+ @cmd.execute
+ end
+
+ assert_equal "Invalid email address #{email}",
+ e.message
+
+ refute_path_exists File.join(@tempdir, 'gem-private_key.pem')
+ refute_path_exists File.join(@tempdir, 'gem-public_cert.pem')
+ end
+ end
+
+ def test_execute_build_expiration_days
+ passphrase = 'Foo bar'
+
+ @cmd.handle_options %W[
+ --build nobody@example.com
+ --days 26
+ ]
+
+ @build_ui = Gem::MockGemUi.new "#{passphrase}\n#{passphrase}"
+
+ use_ui @build_ui do
+ @cmd.execute
+ end
+
+ output = @build_ui.output.squeeze("\n").split "\n"
+
+ assert_equal "Passphrase for your Private Key: ",
+ output.shift
+ assert_equal "Please repeat the passphrase for your Private Key: ",
+ output.shift
+ assert_equal "Certificate: #{File.join @tempdir, 'gem-public_cert.pem'}",
+ output.shift
+ assert_equal "Private Key: #{File.join @tempdir, 'gem-private_key.pem'}",
+ output.shift
+
+ assert_equal "Don't forget to move the key file to somewhere private!",
+ output.shift
+
+ assert_empty output
+ assert_empty @build_ui.error
+
+ assert_path_exists File.join(@tempdir, 'gem-private_key.pem')
+ assert_path_exists File.join(@tempdir, 'gem-public_cert.pem')
+
+ pem = File.read("#{@tempdir}/gem-public_cert.pem")
+ cert = OpenSSL::X509::Certificate.new(pem)
+
+ test = (cert.not_after - cert.not_before).to_i / (24 * 60 * 60)
+ assert_equal(test, 26)
+ end
+
def test_execute_build_bad_passphrase_confirmation
passphrase = 'Foo bar'
passphrase_confirmation = 'Fu bar'
@@ -519,6 +583,68 @@ ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exis
assert_equal expected, @ui.error
end
+ def test_execute_re_sign
+ gem_path = File.join Gem.user_home, ".gem"
+ Dir.mkdir gem_path
+
+ path = File.join @tempdir, 'cert.pem'
+ Gem::Security.write EXPIRED_PUBLIC_CERT, path, 0600
+
+ assert_equal '/CN=nobody/DC=example', EXPIRED_PUBLIC_CERT.issuer.to_s
+
+ tmp_expired_cert_file = File.join(Dir.tmpdir, File.basename(EXPIRED_PUBLIC_CERT_FILE))
+ File.write(tmp_expired_cert_file, File.read(EXPIRED_PUBLIC_CERT_FILE))
+
+ @cmd.handle_options %W[
+ --private-key #{PRIVATE_KEY_FILE}
+ --certificate #{tmp_expired_cert_file}
+ --re-sign
+ ]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected_path = File.join(gem_path, "#{File.basename(tmp_expired_cert_file)}.expired")
+
+ assert_match(
+ /INFO: Your certificate #{tmp_expired_cert_file} has been re-signed\nINFO: Your expired certificate will be located at: #{expected_path}\.[0-9]+/,
+ @ui.output
+ )
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_re_sign_with_cert_expiration_length_days
+ gem_path = File.join Gem.user_home, ".gem"
+ Dir.mkdir gem_path
+
+ path = File.join @tempdir, 'cert.pem'
+ Gem::Security.write EXPIRED_PUBLIC_CERT, path, 0600
+
+ assert_equal '/CN=nobody/DC=example', EXPIRED_PUBLIC_CERT.issuer.to_s
+
+ tmp_expired_cert_file = File.join(Dir.tmpdir, File.basename(EXPIRED_PUBLIC_CERT_FILE))
+ File.write(tmp_expired_cert_file, File.read(EXPIRED_PUBLIC_CERT_FILE))
+
+ @cmd.handle_options %W[
+ --private-key #{PRIVATE_KEY_FILE}
+ --certificate #{tmp_expired_cert_file}
+ --re-sign
+ ]
+
+ Gem.configuration.cert_expiration_length_days = 28
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ re_signed_cert = OpenSSL::X509::Certificate.new(File.read(tmp_expired_cert_file))
+ cert_days_to_expire = (re_signed_cert.not_after - re_signed_cert.not_before).to_i / (24 * 60 * 60)
+
+ assert_equal(28, cert_days_to_expire)
+ assert_equal '', @ui.error
+ end
+
def test_handle_options
@cmd.handle_options %W[
--add #{PUBLIC_CERT_FILE}
@@ -668,4 +794,3 @@ ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exis
end
end if defined?(OpenSSL::SSL)
-
diff --git a/test/rubygems/test_gem_commands_check_command.rb b/test/rubygems/test_gem_commands_check_command.rb
index b220b4d36c..6a6033d35d 100644
--- a/test/rubygems/test_gem_commands_check_command.rb
+++ b/test/rubygems/test_gem_commands_check_command.rb
@@ -10,7 +10,7 @@ class TestGemCommandsCheckCommand < Gem::TestCase
@cmd = Gem::Commands::CheckCommand.new
end
- def gem name
+ def gem(name)
spec = quick_gem name do |gem|
gem.files = %W[lib/#{name}.rb Rakefile]
end
diff --git a/test/rubygems/test_gem_commands_cleanup_command.rb b/test/rubygems/test_gem_commands_cleanup_command.rb
index 8354160dbf..fdcd71ed8a 100644
--- a/test/rubygems/test_gem_commands_cleanup_command.rb
+++ b/test/rubygems/test_gem_commands_cleanup_command.rb
@@ -32,6 +32,21 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
assert @cmd.options[:dryrun]
end
+ def test_handle_options_check_development
+ @cmd.handle_options []
+ assert @cmd.options[:check_dev]
+
+ %w[-D --check-development].each do |options|
+ @cmd.handle_options [options]
+ assert @cmd.options[:check_dev]
+ end
+
+ %w[--no-check-development].each do |options|
+ @cmd.handle_options [options]
+ refute @cmd.options[:check_dev]
+ end
+ end
+
def test_execute
@cmd.options[:args] = %w[a]
@@ -55,6 +70,34 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
refute_path_exists @b_1.gem_dir
end
+ def test_execute_dev_dependencies
+ @b_1 = util_spec 'b', 1 do |s| s.add_development_dependency 'a', '1' end
+ @c_1 = util_spec 'c', 1 do |s| s.add_development_dependency 'a', '2' end
+
+ install_gem @b_1
+ install_gem @c_1
+
+ @cmd.handle_options %w[--check-development]
+
+ @cmd.execute
+
+ assert_path_exists @a_1.gem_dir
+ end
+
+ def test_execute_without_dev_dependencies
+ @b_1 = util_spec 'b', 1 do |s| s.add_development_dependency 'a', '1' end
+ @c_1 = util_spec 'c', 1 do |s| s.add_development_dependency 'a', '2' end
+
+ install_gem @b_1
+ install_gem @c_1
+
+ @cmd.handle_options %w[--no-check-development]
+
+ @cmd.execute
+
+ refute_path_exists @a_1.gem_dir
+ end
+
def test_execute_all
gemhome2 = File.join @tempdir, 'gemhome2'
@@ -115,7 +158,7 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
assert_path_exists @a_1_1.gem_dir
ensure
FileUtils.chmod 0755, @gemhome
- end unless win_platform?
+ end unless win_platform? || Process.uid.zero?
def test_execute_dry_run
@cmd.options[:args] = %w[a]
@@ -193,5 +236,31 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
refute_path_exists d_1.gem_dir
refute_path_exists e_1.gem_dir
end
-end
+ def test_execute_user_install
+ c_1, = util_gem 'c', '1.0'
+ c_2, = util_gem 'c', '1.1'
+
+ d_1, = util_gem 'd', '1.0'
+ d_2, = util_gem 'd', '1.1'
+
+ c_1 = install_gem c_1, :user_install => true # pick up user install path
+ c_2 = install_gem c_2, :user_install => true # pick up user install path
+
+ d_1 = install_gem d_1
+ d_2 = install_gem d_2
+
+ Gem::Specification.dirs = [Gem.dir, Gem.user_dir]
+
+ @cmd.handle_options %w[--user-install]
+ @cmd.options[:args] = []
+
+ @cmd.execute
+
+ refute_path_exists c_1.gem_dir
+ assert_path_exists c_2.gem_dir
+
+ assert_path_exists d_1.gem_dir
+ assert_path_exists d_2.gem_dir
+ end
+end
diff --git a/test/rubygems/test_gem_commands_contents_command.rb b/test/rubygems/test_gem_commands_contents_command.rb
index 65644f476b..a8d6efe794 100644
--- a/test/rubygems/test_gem_commands_contents_command.rb
+++ b/test/rubygems/test_gem_commands_contents_command.rb
@@ -10,7 +10,7 @@ class TestGemCommandsContentsCommand < Gem::TestCase
@cmd = Gem::Commands::ContentsCommand.new
end
- def gem name, version = 2
+ def gem(name, version = 2)
spec = quick_gem name, version do |gem|
gem.files = %W[lib/#{name}.rb Rakefile]
end
@@ -237,4 +237,3 @@ lib/foo.rb
end
end
-
diff --git a/test/rubygems/test_gem_commands_dependency_command.rb b/test/rubygems/test_gem_commands_dependency_command.rb
index d0389ccf55..25b759dd85 100644
--- a/test/rubygems/test_gem_commands_dependency_command.rb
+++ b/test/rubygems/test_gem_commands_dependency_command.rb
@@ -6,7 +6,7 @@ class TestGemCommandsDependencyCommand < Gem::TestCase
def setup
super
-
+ @stub_ui = Gem::MockGemUi.new
@cmd = Gem::Commands::DependencyCommand.new
@cmd.options[:domain] = :local
end
@@ -19,17 +19,17 @@ class TestGemCommandsDependencyCommand < Gem::TestCase
@cmd.options[:args] = %w[foo]
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
assert_equal "Gem foo-2\n bar (> 1)\n baz (> 1)\n\n",
- @ui.output
- assert_equal '', @ui.error
+ @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_no_args
- install_specs new_spec 'x', '2'
+ install_specs util_spec 'x', '2'
spec_fetcher do |fetcher|
fetcher.spec 'a', 1
@@ -40,7 +40,7 @@ class TestGemCommandsDependencyCommand < Gem::TestCase
@cmd.options[:args] = []
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -58,21 +58,21 @@ Gem x-2
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_no_match
@cmd.options[:args] = %w[foo]
assert_raises Gem::MockGemUi::TermError do
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
end
- assert_equal "No gems found matching foo (>= 0)\n", @ui.output
- assert_equal '', @ui.error
+ assert_equal "No gems found matching foo (>= 0)\n", @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_pipe_format
@@ -85,12 +85,12 @@ Gem x-2
@cmd.options[:args] = %w[foo]
@cmd.options[:pipe_format] = true
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
- assert_equal "bar --version '> 1'\n", @ui.output
- assert_equal '', @ui.error
+ assert_equal "bar --version '> 1'\n", @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_regexp
@@ -103,7 +103,7 @@ Gem x-2
@cmd.options[:args] = %w[/[ab]/]
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -118,8 +118,8 @@ Gem b-2
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_reverse
@@ -135,7 +135,7 @@ Gem b-2
@cmd.options[:args] = %w[foo]
@cmd.options[:reverse_dependencies] = true
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -147,8 +147,8 @@ Gem foo-2
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_reverse_remote
@@ -157,7 +157,7 @@ Gem foo-2
@cmd.options[:domain] = :remote
assert_raises Gem::MockGemUi::TermError do
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
end
@@ -166,12 +166,12 @@ Gem foo-2
ERROR: Only reverse dependencies for local gems are supported.
EOF
- assert_equal '', @ui.output
- assert_equal expected, @ui.error
+ assert_equal '', @stub_ui.output
+ assert_equal expected, @stub_ui.error
end
def test_execute_remote
- install_specs new_spec 'bar', '2'
+ install_specs util_spec 'bar', '2'
spec_fetcher do |fetcher|
fetcher.spec 'foo', 2, 'bar' => '> 1'
@@ -180,12 +180,12 @@ ERROR: Only reverse dependencies for local gems are supported.
@cmd.options[:args] = %w[foo]
@cmd.options[:domain] = :remote
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
- assert_equal "Gem foo-2\n bar (> 1)\n\n", @ui.output
- assert_equal '', @ui.error
+ assert_equal "Gem foo-2\n bar (> 1)\n\n", @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_remote_version
@@ -201,12 +201,12 @@ ERROR: Only reverse dependencies for local gems are supported.
@cmd.options[:domain] = :remote
@cmd.options[:version] = req '= 1'
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
- assert_equal "Gem a-1\n\n", @ui.output
- assert_equal '', @ui.error
+ assert_equal "Gem a-1\n\n", @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_prerelease
@@ -218,13 +218,12 @@ ERROR: Only reverse dependencies for local gems are supported.
@cmd.options[:domain] = :remote
@cmd.options[:prerelease] = true
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
- assert_equal "Gem a-2.a\n\n", @ui.output
- assert_equal '', @ui.error
+ assert_equal "Gem a-2.a\n\n", @stub_ui.output
+ assert_equal '', @stub_ui.error
end
end
-
diff --git a/test/rubygems/test_gem_commands_environment_command.rb b/test/rubygems/test_gem_commands_environment_command.rb
index 040e5c4c39..1451f03982 100644
--- a/test/rubygems/test_gem_commands_environment_command.rb
+++ b/test/rubygems/test_gem_commands_environment_command.rb
@@ -29,6 +29,7 @@ class TestGemCommandsEnvironmentCommand < Gem::TestCase
assert_match %r|RUBYGEMS PREFIX: |, @ui.output
assert_match %r|RUBY EXECUTABLE:.*#{RbConfig::CONFIG['ruby_install_name']}|,
@ui.output
+ assert_match %r|GIT EXECUTABLE: #{@cmd.send(:git_path)}|, @ui.output
assert_match %r|SYSTEM CONFIGURATION DIRECTORY:|, @ui.output
assert_match %r|EXECUTABLE DIRECTORY:|, @ui.output
assert_match %r|RUBYGEMS PLATFORMS:|, @ui.output
diff --git a/test/rubygems/test_gem_commands_fetch_command.rb b/test/rubygems/test_gem_commands_fetch_command.rb
index 6858327ed3..9989f57bd7 100644
--- a/test/rubygems/test_gem_commands_fetch_command.rb
+++ b/test/rubygems/test_gem_commands_fetch_command.rb
@@ -124,4 +124,3 @@ class TestGemCommandsFetchCommand < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_commands_generate_index_command.rb b/test/rubygems/test_gem_commands_generate_index_command.rb
index 7e30a6dccf..b4276702f4 100644
--- a/test/rubygems/test_gem_commands_generate_index_command.rb
+++ b/test/rubygems/test_gem_commands_generate_index_command.rb
@@ -48,4 +48,3 @@ class TestGemCommandsGenerateIndexCommand < Gem::TestCase
end
end if ''.respond_to? :to_xs
-
diff --git a/test/rubygems/test_gem_commands_help_command.rb b/test/rubygems/test_gem_commands_help_command.rb
index 986df1a9a5..55bc797b28 100644
--- a/test/rubygems/test_gem_commands_help_command.rb
+++ b/test/rubygems/test_gem_commands_help_command.rb
@@ -7,13 +7,16 @@ require "rubygems/command_manager"
require File.expand_path('../rubygems_plugin', __FILE__)
class TestGemCommandsHelpCommand < Gem::TestCase
+ # previously this was calc'd in setup, but 1.8.7 had
+ # intermittent failures, but no issues with above require
+ PLUGIN = File.expand_path('../rubygems_plugin.rb', __FILE__)
+
def setup
super
@cmd = Gem::Commands::HelpCommand.new
- load File.expand_path('../rubygems_plugin.rb', __FILE__) unless
- Gem::Commands.const_defined? :InterruptCommand
+ load PLUGIN unless Gem::Commands.const_defined? :InterruptCommand
end
def test_gem_help_bad
@@ -45,7 +48,7 @@ class TestGemCommandsHelpCommand < Gem::TestCase
assert_match(/\s+#{cmd}\s+\S+/, out)
end
- if defined?(OpenSSL::SSL) then
+ if defined?(OpenSSL::SSL)
assert_empty err
refute_match 'No command found for ', out
@@ -61,7 +64,7 @@ class TestGemCommandsHelpCommand < Gem::TestCase
end
end
- def util_gem *args
+ def util_gem(*args)
@cmd.options[:args] = args
use_ui @ui do
diff --git a/test/rubygems/test_gem_commands_info_command.rb b/test/rubygems/test_gem_commands_info_command.rb
new file mode 100644
index 0000000000..83b18c5036
--- /dev/null
+++ b/test/rubygems/test_gem_commands_info_command.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+require 'rubygems/test_case'
+require 'rubygems/commands/info_command'
+
+class TestGemCommandsInfoCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::InfoCommand.new
+ end
+
+ def gem(name, version = "1.0")
+ spec = quick_gem name do |gem|
+ gem.summary = "test gem"
+ gem.homepage = "https://github.com/rubygems/rubygems"
+ gem.files = %W[lib/#{name}.rb Rakefile]
+ gem.authors = ["Colby", "Jack"]
+ gem.license = "MIT"
+ gem.version = version
+ end
+ write_file File.join(*%W[gems #{spec.full_name} lib #{name}.rb])
+ write_file File.join(*%W[gems #{spec.full_name} Rakefile])
+ spec
+ end
+
+ def test_execute
+ @gem = gem "foo", "1.0.0"
+
+ @cmd.handle_options %w[foo]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r%#{@gem.name} \(#{@gem.version}\)\n%, @ui.output
+ assert_match %r%Authors: #{@gem.authors.join(', ')}\n%, @ui.output
+ assert_match %r%Homepage: #{@gem.homepage}\n%, @ui.output
+ assert_match %r%License: #{@gem.license}\n%, @ui.output
+ assert_match %r%Installed at: #{@gem.base_dir}\n%, @ui.output
+ assert_match %r%#{@gem.summary}\n%, @ui.output
+ assert_match "", @ui.error
+ end
+end
diff --git a/test/rubygems/test_gem_commands_install_command.rb b/test/rubygems/test_gem_commands_install_command.rb
index 13b9a7bbad..0976a31b50 100644
--- a/test/rubygems/test_gem_commands_install_command.rb
+++ b/test/rubygems/test_gem_commands_install_command.rb
@@ -55,7 +55,7 @@ class TestGemCommandsInstallCommand < Gem::TestCase
a2_pre = specs['a-2.a']
@cmd.handle_options [a2_pre.name, '--version', a2_pre.version.to_s,
- "--no-ri", "--no-rdoc"]
+ "--no-document"]
assert @cmd.options[:prerelease]
assert @cmd.options[:version].satisfied_by?(a2_pre.version)
@@ -96,8 +96,42 @@ class TestGemCommandsInstallCommand < Gem::TestCase
assert_match "1 gem installed", @ui.output
end
+ def test_execute_local_transitive_prerelease
+ specs = spec_fetcher do |fetcher|
+ fetcher.download 'a', 2, 'b' => "2.a", 'c' => '3'
+ fetcher.download 'b', '2.a'
+ fetcher.download 'c', '3'
+ end
+
+ @cmd.options[:domain] = :local
+
+ FileUtils.mv specs['a-2'].cache_file, @tempdir
+ FileUtils.mv specs['b-2.a'].cache_file, @tempdir
+ FileUtils.mv specs['c-3'].cache_file, @tempdir
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ orig_dir = Dir.pwd
+ begin
+ Dir.chdir @tempdir
+ FileUtils.rm_r [@gemhome, "gems"]
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ ensure
+ Dir.chdir orig_dir
+ end
+ end
+
+ assert_equal %w[a-2 b-2.a c-3], @cmd.installed_specs.map { |spec| spec.full_name }.sort
+
+ assert_match "3 gems installed", @ui.output
+ end
+
def test_execute_no_user_install
skip 'skipped on MS Windows (chmod has no effect)' if win_platform?
+ skip 'skipped in root privilege' if Process.uid.zero?
specs = spec_fetcher do |fetcher|
fetcher.gem 'a', 2
@@ -167,6 +201,38 @@ class TestGemCommandsInstallCommand < Gem::TestCase
assert_match(/ould not find a valid gem 'nonexistent'/, @ui.error)
end
+ def test_execute_dependency_nonexistent
+ spec_fetcher do |fetcher|
+ fetcher.spec 'foo', 2, 'bar' => '0.5'
+ end
+
+ @cmd.options[:args] = ['foo']
+
+ use_ui @ui do
+ e = assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+
+ assert_equal 2, e.exit_code
+ end
+
+ expected = <<-EXPECTED
+ERROR: Could not find a valid gem 'bar' (= 0.5) (required by 'foo' (>= 0)) in any repository
+ EXPECTED
+
+ assert_equal expected, @ui.error
+ end
+
+ def test_execute_http_proxy
+ use_ui @ui do
+ e = assert_raises ArgumentError, @ui.error do
+ @cmd.handle_options %w[-p=foo.bar.com]
+ end
+
+ assert_match "Invalid uri scheme for =foo.bar.com\nPreface URLs with one of [\"http://\", \"https://\", \"file://\", \"s3://\"]", e.message
+ end
+ end
+
def test_execute_bad_source
spec_fetcher
@@ -327,6 +393,23 @@ ERROR: Possible alternatives: non_existent_with_hint
assert_equal %w[a-2.a], @cmd.installed_specs.map { |spec| spec.full_name }
end
+ def test_execute_with_version_specified_by_colon
+ spec_fetcher do |fetcher|
+ fetcher.download 'a', 1
+ fetcher.download 'a', 2
+ end
+
+ @cmd.options[:args] = %w[a:1]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ assert_equal %w[a-1], @cmd.installed_specs.map { |spec| spec.full_name }
+ end
+
def test_execute_prerelease_skipped_when_non_pre_available
spec_fetcher do |fetcher|
fetcher.gem 'a', '2.pre'
@@ -346,7 +429,6 @@ ERROR: Possible alternatives: non_existent_with_hint
end
def test_execute_rdoc
- skip if RUBY_VERSION <= "1.8.7"
specs = spec_fetcher do |fetcher|
fetcher.gem 'a', 2
end
@@ -382,6 +464,42 @@ ERROR: Possible alternatives: non_existent_with_hint
assert_path_exists File.join(a2.doc_dir, 'rdoc')
end
+ def test_execute_rdoc_with_path
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ Gem.done_installing(&Gem::RDoc.method(:generation_hook))
+
+ @cmd.options[:document] = %w[rdoc ri]
+ @cmd.options[:domain] = :local
+ @cmd.options[:install_dir] = 'whatever'
+
+ a2 = specs['a-2']
+ FileUtils.mv a2.cache_file, @tempdir
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ # Don't use Dir.chdir with a block, it warnings a lot because
+ # of a downstream Dir.chdir with a block
+ old = Dir.getwd
+
+ begin
+ Dir.chdir @tempdir
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ ensure
+ Dir.chdir old
+ end
+ end
+
+ wait_for_child_process_to_exit
+
+ assert_path_exists 'whatever/doc/a-2', 'documentation not installed'
+ end
+
def test_execute_saves_build_args
specs = spec_fetcher do |fetcher|
fetcher.gem 'a', 2
@@ -547,12 +665,32 @@ ERROR: Possible alternatives: non_existent_with_hint
assert_empty @cmd.installed_specs
- msg = "ERROR: Can't use --version w/ multiple gems. Use name:ver instead."
+ msg = "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'`"
assert_empty @ui.output
assert_equal msg, @ui.error.chomp
end
+ def test_execute_two_version_specified_by_colon
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ fetcher.gem 'a', 2
+ fetcher.gem 'b', 1
+ fetcher.gem 'b', 2
+ end
+
+ @cmd.options[:args] = %w[a:1 b:1]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ assert_equal %w[a-1 b-1], @cmd.installed_specs.map { |spec| spec.full_name }
+ end
+
def test_execute_conservative
spec_fetcher do |fetcher|
fetcher.download 'b', 2
@@ -588,7 +726,7 @@ ERROR: Possible alternatives: non_existent_with_hint
done_installing = true
end
- spec = quick_spec 'a', 2
+ spec = util_spec 'a', 2
util_build_gem spec
@@ -616,7 +754,7 @@ ERROR: Possible alternatives: non_existent_with_hint
end
def test_install_gem_ignore_dependencies_specific_file
- spec = quick_spec 'a', 2
+ spec = util_spec 'a', 2
util_build_gem spec
diff --git a/test/rubygems/test_gem_commands_lock_command.rb b/test/rubygems/test_gem_commands_lock_command.rb
index af1fce999c..a35ed081cb 100644
--- a/test/rubygems/test_gem_commands_lock_command.rb
+++ b/test/rubygems/test_gem_commands_lock_command.rb
@@ -66,4 +66,3 @@ gem 'd', '= 1'
end
end
-
diff --git a/test/rubygems/test_gem_commands_open_command.rb b/test/rubygems/test_gem_commands_open_command.rb
index 3ec38972e6..e73a138204 100644
--- a/test/rubygems/test_gem_commands_open_command.rb
+++ b/test/rubygems/test_gem_commands_open_command.rb
@@ -24,7 +24,8 @@ class TestGemCommandsOpenCommand < Gem::TestCase
@cmd.options[:args] = %w[foo]
@cmd.options[:editor] = "#{Gem.ruby} -e0 --"
- spec = gem 'foo'
+ gem 'foo', '1.0.0'
+ spec = gem 'foo', '1.0.1'
mock = MiniTest::Mock.new
mock.expect(:call, true, [spec.full_gem_path])
@@ -67,4 +68,33 @@ class TestGemCommandsOpenCommand < Gem::TestCase
assert_equal "", @ui.error
end
+ def test_default_gem
+ @cmd.options[:version] = "1.0"
+ @cmd.options[:args] = %w[foo]
+
+ version = @cmd.options[:version]
+ @cmd.define_singleton_method(:spec_for) do |name|
+ spec = Gem::Specification.find_all_by_name(name, version).first
+
+ spec.define_singleton_method(:default_gem?) do
+ true
+ end
+
+ return spec if spec
+
+ say "Unable to find gem '#{name}'"
+ end
+
+ gem("foo", "1.0")
+
+ assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_match %r|'foo' is a default gem and can't be opened\.| , @ui.output
+ assert_equal "", @ui.error
+ end
+
end
diff --git a/test/rubygems/test_gem_commands_outdated_command.rb b/test/rubygems/test_gem_commands_outdated_command.rb
index 626b29057d..3b0220e84b 100644
--- a/test/rubygems/test_gem_commands_outdated_command.rb
+++ b/test/rubygems/test_gem_commands_outdated_command.rb
@@ -30,4 +30,3 @@ class TestGemCommandsOutdatedCommand < Gem::TestCase
assert_equal "", @ui.error
end
end
-
diff --git a/test/rubygems/test_gem_commands_owner_command.rb b/test/rubygems/test_gem_commands_owner_command.rb
index 44652c1093..071079c0dd 100644
--- a/test/rubygems/test_gem_commands_owner_command.rb
+++ b/test/rubygems/test_gem_commands_owner_command.rb
@@ -8,8 +8,10 @@ class TestGemCommandsOwnerCommand < Gem::TestCase
super
ENV["RUBYGEMS_HOST"] = nil
- @fetcher = Gem::FakeFetcher.new
- Gem::RemoteFetcher.fetcher = @fetcher
+ @stub_ui = Gem::MockGemUi.new
+ @stub_fetcher = Gem::FakeFetcher.new
+ Gem::RemoteFetcher.fetcher = @stub_fetcher
+ Gem.configuration = nil
Gem.configuration.rubygems_api_key = "ed244fbf2b1a52e012da8616c512fa47f9aa5250"
@cmd = Gem::Commands::OwnerCommand.new
@@ -27,35 +29,59 @@ class TestGemCommandsOwnerCommand < Gem::TestCase
- id: 4
EOF
- @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
+ @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.show_owners("freewill")
end
- assert_equal Net::HTTP::Get, @fetcher.last_request.class
- assert_equal Gem.configuration.rubygems_api_key, @fetcher.last_request["Authorization"]
+ assert_equal Net::HTTP::Get, @stub_fetcher.last_request.class
+ assert_equal Gem.configuration.rubygems_api_key, @stub_fetcher.last_request["Authorization"]
- assert_match %r{Owners for gem: freewill}, @ui.output
- assert_match %r{- user1@example.com}, @ui.output
- assert_match %r{- user2@example.com}, @ui.output
- assert_match %r{- user3}, @ui.output
- assert_match %r{- 4}, @ui.output
+ assert_match %r{Owners for gem: freewill}, @stub_ui.output
+ assert_match %r{- user1@example.com}, @stub_ui.output
+ assert_match %r{- user2@example.com}, @stub_ui.output
+ assert_match %r{- user3}, @stub_ui.output
+ assert_match %r{- 4}, @stub_ui.output
end
+ def test_show_owners_dont_load_objects
+ skip "testing a psych-only API" unless defined?(::Psych::DisallowedClass)
+
+ response = <<EOF
+---
+- email: !ruby/object:Object {}
+ id: 1
+ handle: user1
+- email: user2@example.com
+- id: 3
+ handle: user3
+- id: 4
+EOF
+
+ @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
+
+ assert_raises Psych::DisallowedClass do
+ use_ui @ui do
+ @cmd.show_owners("freewill")
+ end
+ end
+ end
+
+
def test_show_owners_setting_up_host_through_env_var
response = "- email: user1@example.com\n"
host = "http://rubygems.example"
ENV["RUBYGEMS_HOST"] = host
- @fetcher.data["#{host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
+ @stub_fetcher.data["#{host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.show_owners("freewill")
end
- assert_match %r{Owners for gem: freewill}, @ui.output
- assert_match %r{- user1@example.com}, @ui.output
+ assert_match %r{Owners for gem: freewill}, @stub_ui.output
+ assert_match %r{- user1@example.com}, @stub_ui.output
end
def test_show_owners_setting_up_host
@@ -63,32 +89,32 @@ EOF
host = "http://rubygems.example"
@cmd.host = host
- @fetcher.data["#{host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
+ @stub_fetcher.data["#{host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.show_owners("freewill")
end
- assert_match %r{Owners for gem: freewill}, @ui.output
- assert_match %r{- user1@example.com}, @ui.output
+ assert_match %r{Owners for gem: freewill}, @stub_ui.output
+ assert_match %r{- user1@example.com}, @stub_ui.output
end
def test_show_owners_denied
response = "You don't have permission to push to this gem"
- @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners.yaml"] = [response, 403, 'Forbidden']
+ @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners.yaml"] = [response, 403, 'Forbidden']
assert_raises Gem::MockGemUi::TermError do
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.show_owners("freewill")
end
end
- assert_match response, @ui.output
+ assert_match response, @stub_ui.output
end
def test_show_owners_key
response = "- email: user1@example.com\n"
- @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
+ @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
File.open Gem.configuration.credentials_path, 'a' do |f|
f.write ':other: 701229f217cdf23b1344c7b4b54ca97'
end
@@ -97,56 +123,56 @@ EOF
@cmd.handle_options %w(-k other)
@cmd.show_owners('freewill')
- assert_equal '701229f217cdf23b1344c7b4b54ca97', @fetcher.last_request['Authorization']
+ assert_equal '701229f217cdf23b1344c7b4b54ca97', @stub_fetcher.last_request['Authorization']
end
def test_add_owners
response = "Owner added successfully."
- @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 200, 'OK']
+ @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 200, 'OK']
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.add_owners("freewill", ["user-new1@example.com"])
end
- assert_equal Net::HTTP::Post, @fetcher.last_request.class
- assert_equal Gem.configuration.rubygems_api_key, @fetcher.last_request["Authorization"]
- assert_equal "email=user-new1%40example.com", @fetcher.last_request.body
+ assert_equal Net::HTTP::Post, @stub_fetcher.last_request.class
+ assert_equal Gem.configuration.rubygems_api_key, @stub_fetcher.last_request["Authorization"]
+ assert_equal "email=user-new1%40example.com", @stub_fetcher.last_request.body
- assert_match response, @ui.output
+ assert_match response, @stub_ui.output
end
def test_add_owners_denied
response = "You don't have permission to push to this gem"
- @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 403, 'Forbidden']
+ @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 403, 'Forbidden']
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.add_owners("freewill", ["user-new1@example.com"])
end
- assert_match response, @ui.output
+ assert_match response, @stub_ui.output
end
def test_add_owner_with_host_option_through_execute
host = "http://rubygems.example"
add_owner_response = "Owner added successfully."
show_owners_response = "- email: user1@example.com\n"
- @fetcher.data["#{host}/api/v1/gems/freewill/owners"] = [add_owner_response, 200, 'OK']
- @fetcher.data["#{host}/api/v1/gems/freewill/owners.yaml"] = [show_owners_response, 200, 'OK']
+ @stub_fetcher.data["#{host}/api/v1/gems/freewill/owners"] = [add_owner_response, 200, 'OK']
+ @stub_fetcher.data["#{host}/api/v1/gems/freewill/owners.yaml"] = [show_owners_response, 200, 'OK']
@cmd.handle_options %W[--host #{host} --add user-new1@example.com freewill]
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
- assert_match add_owner_response, @ui.output
- assert_match %r{Owners for gem: freewill}, @ui.output
- assert_match %r{- user1@example.com}, @ui.output
+ assert_match add_owner_response, @stub_ui.output
+ assert_match %r{Owners for gem: freewill}, @stub_ui.output
+ assert_match %r{- user1@example.com}, @stub_ui.output
end
def test_add_owners_key
response = "Owner added successfully."
- @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 200, 'OK']
+ @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 200, 'OK']
File.open Gem.configuration.credentials_path, 'a' do |f|
f.write ':other: 701229f217cdf23b1344c7b4b54ca97'
end
@@ -155,38 +181,38 @@ EOF
@cmd.handle_options %w(-k other)
@cmd.add_owners('freewill', ['user-new1@example.com'])
- assert_equal '701229f217cdf23b1344c7b4b54ca97', @fetcher.last_request['Authorization']
+ assert_equal '701229f217cdf23b1344c7b4b54ca97', @stub_fetcher.last_request['Authorization']
end
def test_remove_owners
response = "Owner removed successfully."
- @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 200, 'OK']
+ @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 200, 'OK']
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.remove_owners("freewill", ["user-remove1@example.com"])
end
- assert_equal Net::HTTP::Delete, @fetcher.last_request.class
- assert_equal Gem.configuration.rubygems_api_key, @fetcher.last_request["Authorization"]
- assert_equal "email=user-remove1%40example.com", @fetcher.last_request.body
+ assert_equal Net::HTTP::Delete, @stub_fetcher.last_request.class
+ assert_equal Gem.configuration.rubygems_api_key, @stub_fetcher.last_request["Authorization"]
+ assert_equal "email=user-remove1%40example.com", @stub_fetcher.last_request.body
- assert_match response, @ui.output
+ assert_match response, @stub_ui.output
end
def test_remove_owners_denied
response = "You don't have permission to push to this gem"
- @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 403, 'Forbidden']
+ @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 403, 'Forbidden']
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.remove_owners("freewill", ["user-remove1@example.com"])
end
- assert_match response, @ui.output
+ assert_match response, @stub_ui.output
end
def test_remove_owners_key
response = "Owner removed successfully."
- @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 200, 'OK']
+ @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 200, 'OK']
File.open Gem.configuration.credentials_path, 'a' do |f|
f.write ':other: 701229f217cdf23b1344c7b4b54ca97'
end
@@ -195,18 +221,53 @@ EOF
@cmd.handle_options %w(-k other)
@cmd.remove_owners('freewill', ['user-remove1@example.com'])
- assert_equal '701229f217cdf23b1344c7b4b54ca97', @fetcher.last_request['Authorization']
+ assert_equal '701229f217cdf23b1344c7b4b54ca97', @stub_fetcher.last_request['Authorization']
end
def test_remove_owners_missing
response = 'Owner could not be found.'
- @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 404, 'Not Found']
+ @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 404, 'Not Found']
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.remove_owners("freewill", ["missing@example"])
end
- assert_equal "Removing missing@example: #{response}\n", @ui.output
+ assert_equal "Removing missing@example: #{response}\n", @stub_ui.output
+ end
+
+ def test_otp_verified_success
+ response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
+ response_success = "Owner added successfully."
+
+ @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = proc do
+ @call_count ||= 0
+ (@call_count += 1).odd? ? [response_fail, 401, 'Unauthorized'] : [response_success, 200, 'OK']
+ end
+
+ @otp_ui = Gem::MockGemUi.new "111111\n"
+ use_ui @otp_ui do
+ @cmd.add_owners("freewill", ["user-new1@example.com"])
+ end
+
+ assert_match 'You have enabled multi-factor authentication. Please enter OTP code.', @otp_ui.output
+ assert_match 'Code: ', @otp_ui.output
+ assert_match response_success, @otp_ui.output
+ assert_equal '111111', @stub_fetcher.last_request['OTP']
+ end
+
+ def test_otp_verified_failure
+ response = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
+ @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 401, 'Unauthorized']
+
+ @otp_ui = Gem::MockGemUi.new "111111\n"
+ use_ui @otp_ui do
+ @cmd.add_owners("freewill", ["user-new1@example.com"])
+ end
+
+ assert_match response, @otp_ui.output
+ assert_match 'You have enabled multi-factor authentication. Please enter OTP code.', @otp_ui.output
+ assert_match 'Code: ', @otp_ui.output
+ assert_equal '111111', @stub_fetcher.last_request['OTP']
end
end
diff --git a/test/rubygems/test_gem_commands_pristine_command.rb b/test/rubygems/test_gem_commands_pristine_command.rb
index dfec78b848..e4aa8d8656 100644
--- a/test/rubygems/test_gem_commands_pristine_command.rb
+++ b/test/rubygems/test_gem_commands_pristine_command.rb
@@ -145,7 +145,7 @@ class TestGemCommandsPristineCommand < Gem::TestCase
out = @ui.output.split "\n"
assert_equal 'Restoring gems to pristine condition...', out.shift
- assert_equal 'Building native extensions. This could take a while...',
+ assert_equal 'Building native extensions. This could take a while...',
out.shift
assert_equal "Restored #{a.full_name}", out.shift
assert_empty out, out.inspect
@@ -253,6 +253,31 @@ class TestGemCommandsPristineCommand < Gem::TestCase
assert_empty out, out.inspect
end
+ def test_skip_many_gems
+ a = util_spec 'a'
+ b = util_spec 'b'
+ c = util_spec 'c'
+
+ install_gem a
+ install_gem b
+ install_gem c
+
+ @cmd.options[:args] = %w[a b c]
+ @cmd.options[:skip] = ['a', 'c']
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+
+ assert_equal "Restoring gems to pristine condition...", out.shift
+ assert_equal "Skipped #{a.full_name}, it was given through options", out.shift
+ assert_equal "Restored #{b.full_name}", out.shift
+ assert_equal "Skipped #{c.full_name}, it was given through options", out.shift
+ assert_empty out, out.inspect
+ end
+
def test_execute_many_multi_repo
a = util_spec 'a'
install_gem a
@@ -408,6 +433,39 @@ class TestGemCommandsPristineCommand < Gem::TestCase
refute File.exist? gem_lib
end
+ def test_execute_bindir
+ a = util_spec 'a' do |s|
+ s.name = "test_gem"
+ s.executables = %w[foo]
+ s.files = %w[bin/foo]
+ end
+
+ write_file File.join(@tempdir, 'bin', 'foo') do |fp|
+ fp.puts "#!/usr/bin/ruby"
+ end
+
+ write_file File.join(@tempdir, 'test_bin', 'foo') do |fp|
+ fp.puts "#!/usr/bin/ruby"
+ end
+
+ install_gem a
+
+ gem_exec = File.join @gemhome, 'bin', 'foo'
+ gem_bindir = File.join @tempdir, 'test_bin', 'foo'
+
+ FileUtils.rm gem_exec
+ FileUtils.rm gem_bindir
+
+ @cmd.handle_options ["--all", "--only-executables", "--bindir", "#{gem_bindir}"]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ refute File.exist? gem_exec
+ assert File.exist? gem_bindir
+ end
+
def test_execute_unknown_gem_at_remote_source
install_specs util_spec 'a'
@@ -437,38 +495,16 @@ class TestGemCommandsPristineCommand < Gem::TestCase
@cmd.execute
end
- assert_equal([
- "Restoring gems to pristine condition...",
- "Skipped default-2.0.0.0, it is a default gem",
- ],
- @ui.output.split("\n"))
+ assert_equal(
+ [
+ "Restoring gems to pristine condition...",
+ "Skipped default-2.0.0.0, it is a default gem",
+ ],
+ @ui.output.split("\n")
+ )
assert_empty(@ui.error)
end
- def test_execute_bundled_gem_on_old_rubies
- util_set_RUBY_VERSION '1.9.3', 551
-
- spec = util_spec 'bigdecimal', '1.1.0' do |s|
- s.summary = "This bigdecimal is bundled with Ruby"
- end
- install_specs spec
-
- @cmd.options[:args] = %w[bigdecimal]
-
- use_ui @ui do
- @cmd.execute
- end
-
- assert_equal([
- "Restoring gems to pristine condition...",
- "Skipped bigdecimal-1.1.0, it is bundled with old Ruby"
- ], @ui.output.split("\n"))
-
- assert_empty @ui.error
- ensure
- util_restore_RUBY_VERSION
- end
-
def test_handle_options
@cmd.handle_options %w[]
@@ -488,4 +524,3 @@ class TestGemCommandsPristineCommand < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_commands_push_command.rb b/test/rubygems/test_gem_commands_push_command.rb
index b888a741f0..9d2185dcd9 100644
--- a/test/rubygems/test_gem_commands_push_command.rb
+++ b/test/rubygems/test_gem_commands_push_command.rb
@@ -95,6 +95,26 @@ class TestGemCommandsPushCommand < Gem::TestCase
@fetcher.last_request["Content-Type"]
end
+ def test_execute_allowed_push_host
+ @spec, @path = util_gem "freebird", "1.0.1" do |spec|
+ spec.metadata['allowed_push_host'] = "https://privategemserver.example"
+ end
+
+ @response = "Successfully registered gem: freewill (1.0.0)"
+ @fetcher.data["#{@spec.metadata['allowed_push_host']}/api/v1/gems"] = [@response, 200, 'OK']
+ @fetcher.data["#{Gem.host}/api/v1/gems"] =
+ ['fail', 500, 'Internal Server Error']
+
+ @cmd.options[:args] = [@path]
+
+ @cmd.execute
+
+ assert_equal Net::HTTP::Post, @fetcher.last_request.class
+ assert_equal Gem.read_binary(@path), @fetcher.last_request.body
+ assert_equal "application/octet-stream",
+ @fetcher.last_request["Content-Type"]
+ end
+
def test_sending_when_default_host_disabled
Gem.configuration.disable_default_gem_server = true
response = "You must specify a gem server"
@@ -132,7 +152,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
}
FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
- open Gem.configuration.credentials_path, 'w' do |f|
+ File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
end
Gem.configuration.load_api_keys
@@ -141,6 +161,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
@response = "Successfully registered gem: freebird (1.0.1)"
@fetcher.data["#{@host}/api/v1/gems"] = [@response, 200, 'OK']
+
send_battery
end
@@ -166,7 +187,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
}
FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
- open Gem.configuration.credentials_path, 'w' do |f|
+ File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
end
Gem.configuration.load_api_keys
@@ -193,7 +214,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
}
FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
- open Gem.configuration.credentials_path, 'w' do |f|
+ File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
end
Gem.configuration.load_api_keys
@@ -210,6 +231,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
spec.metadata['allowed_push_host'] = "https://privategemserver.example"
end
+
response = %{ERROR: "#{@host}" is not allowed by the gemspec, which only allows "https://privategemserver.example"}
assert_raises Gem::MockGemUi::TermError do
@@ -235,7 +257,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
}
FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
- open Gem.configuration.credentials_path, 'w' do |f|
+ File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
end
Gem.configuration.load_api_keys
@@ -266,7 +288,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
}
FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
- open Gem.configuration.credentials_path, 'w' do |f|
+ File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
end
Gem.configuration.load_api_keys
@@ -327,4 +349,41 @@ class TestGemCommandsPushCommand < Gem::TestCase
@fetcher.last_request["Authorization"]
end
+ def test_otp_verified_success
+ response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
+ response_success = 'Successfully registered gem: freewill (1.0.0)'
+
+ @fetcher.data["#{Gem.host}/api/v1/gems"] = proc do
+ @call_count ||= 0
+ (@call_count += 1).odd? ? [response_fail, 401, 'Unauthorized'] : [response_success, 200, 'OK']
+ end
+
+ @otp_ui = Gem::MockGemUi.new "111111\n"
+ use_ui @otp_ui do
+ @cmd.send_gem(@path)
+ end
+
+ assert_match 'You have enabled multi-factor authentication. Please enter OTP code.', @otp_ui.output
+ assert_match 'Code: ', @otp_ui.output
+ assert_match response_success, @otp_ui.output
+ assert_equal '111111', @fetcher.last_request['OTP']
+ end
+
+ def test_otp_verified_failure
+ response = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
+ @fetcher.data["#{Gem.host}/api/v1/gems"] = [response, 401, 'Unauthorized']
+
+ @otp_ui = Gem::MockGemUi.new "111111\n"
+ assert_raises Gem::MockGemUi::TermError do
+ use_ui @otp_ui do
+ @cmd.send_gem(@path)
+ end
+ end
+
+ assert_match response, @otp_ui.output
+ assert_match 'You have enabled multi-factor authentication. Please enter OTP code.', @otp_ui.output
+ assert_match 'Code: ', @otp_ui.output
+ assert_equal '111111', @fetcher.last_request['OTP']
+ end
+
end
diff --git a/test/rubygems/test_gem_commands_query_command.rb b/test/rubygems/test_gem_commands_query_command.rb
index 223f205b2d..7957689db4 100644
--- a/test/rubygems/test_gem_commands_query_command.rb
+++ b/test/rubygems/test_gem_commands_query_command.rb
@@ -9,8 +9,10 @@ module TestGemCommandsQueryCommandSetup
@cmd = Gem::Commands::QueryCommand.new
@specs = add_gems_to_fetcher
+ @stub_ui = Gem::MockGemUi.new
+ @stub_fetcher = Gem::FakeFetcher.new
- @fetcher.data["#{@gem_repo}Marshal.#{Gem.marshal_version}"] = proc do
+ @stub_fetcher.data["#{@gem_repo}Marshal.#{Gem.marshal_version}"] = proc do
raise Gem::RemoteFetcher::FetchError
end
end
@@ -26,7 +28,7 @@ class TestGemCommandsQueryCommandWithInstalledGems < Gem::TestCase
@cmd.handle_options %w[-r]
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -38,8 +40,8 @@ a (2)
pl (1 i386-linux)
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_all
@@ -49,7 +51,7 @@ pl (1 i386-linux)
@cmd.handle_options %w[-r --all]
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -61,8 +63,8 @@ a (2, 1)
pl (1 i386-linux)
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_all_prerelease
@@ -72,7 +74,7 @@ pl (1 i386-linux)
@cmd.handle_options %w[-r --all --prerelease]
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -84,8 +86,8 @@ a (3.a, 2, 1)
pl (1 i386-linux)
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_details
@@ -101,7 +103,7 @@ pl (1 i386-linux)
@cmd.handle_options %w[-r -d]
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -124,34 +126,114 @@ pl (1)
this is a summary
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
+ end
+
+ def test_execute_details_cleans_text
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2 do |s|
+ s.summary = 'This is a lot of text. ' * 4
+ s.authors = ["Abraham Lincoln \x01", "\x02 Hirohito"]
+ s.homepage = "http://a.example.com/\x03"
+ end
+
+ fetcher.legacy_platform
+ end
+
+ @cmd.handle_options %w[-r -d]
+
+ use_ui @stub_ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** REMOTE GEMS ***
+
+a (2)
+ Authors: Abraham Lincoln ., . Hirohito
+ Homepage: http://a.example.com/.
+
+ This is a lot of text. This is a lot of text. This is a lot of text.
+ This is a lot of text.
+
+pl (1)
+ Platform: i386-linux
+ Author: A User
+ Homepage: http://example.com
+
+ this is a summary
+ EOF
+
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
+ end
+
+ def test_execute_details_truncates_summary
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2 do |s|
+ s.summary = 'This is a lot of text. ' * 10_000
+ s.authors = ["Abraham Lincoln \x01", "\x02 Hirohito"]
+ s.homepage = "http://a.example.com/\x03"
+ end
+
+ fetcher.legacy_platform
+ end
+
+ @cmd.handle_options %w[-r -d]
+
+ use_ui @stub_ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** REMOTE GEMS ***
+
+a (2)
+ Authors: Abraham Lincoln ., . Hirohito
+ Homepage: http://a.example.com/.
+
+ Truncating the summary for a-2 to 100,000 characters:
+#{" This is a lot of text. This is a lot of text. This is a lot of text.\n" * 1449} This is a lot of te
+
+pl (1)
+ Platform: i386-linux
+ Author: A User
+ Homepage: http://example.com
+
+ this is a summary
+ EOF
+
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_installed
@cmd.handle_options %w[-n a --installed]
assert_raises Gem::MockGemUi::SystemExitException do
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
end
- assert_equal "true\n", @ui.output
- assert_equal '', @ui.error
+ assert_equal "true\n", @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_installed_inverse
@cmd.handle_options %w[-n a --no-installed]
e = assert_raises Gem::MockGemUi::TermError do
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
end
- assert_equal "false\n", @ui.output
- assert_equal '', @ui.error
+ assert_equal "false\n", @stub_ui.output
+ assert_equal '', @stub_ui.error
assert_equal 1, e.exit_code
end
@@ -160,26 +242,26 @@ pl (1)
@cmd.handle_options %w[-n not_installed --no-installed]
assert_raises Gem::MockGemUi::SystemExitException do
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
end
- assert_equal "true\n", @ui.output
- assert_equal '', @ui.error
+ assert_equal "true\n", @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_installed_no_name
@cmd.handle_options %w[--installed]
e = assert_raises Gem::MockGemUi::TermError do
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
end
- assert_equal '', @ui.output
- assert_equal "ERROR: You must specify a gem name\n", @ui.error
+ assert_equal '', @stub_ui.output
+ assert_equal "ERROR: You must specify a gem name\n", @stub_ui.error
assert_equal 4, e.exit_code
end
@@ -188,13 +270,13 @@ pl (1)
@cmd.handle_options %w[-n not_installed --installed]
e = assert_raises Gem::MockGemUi::TermError do
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
end
- assert_equal "false\n", @ui.output
- assert_equal '', @ui.error
+ assert_equal "false\n", @stub_ui.output
+ assert_equal '', @stub_ui.error
assert_equal 1, e.exit_code
end
@@ -203,26 +285,26 @@ pl (1)
@cmd.handle_options %w[-n a --installed --version 2]
assert_raises Gem::MockGemUi::SystemExitException do
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
end
- assert_equal "true\n", @ui.output
- assert_equal '', @ui.error
+ assert_equal "true\n", @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_installed_version_not_installed
@cmd.handle_options %w[-n c --installed --version 2]
e = assert_raises Gem::MockGemUi::TermError do
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
end
- assert_equal "false\n", @ui.output
- assert_equal '', @ui.error
+ assert_equal "false\n", @stub_ui.output
+ assert_equal '', @stub_ui.error
assert_equal 1, e.exit_code
end
@@ -234,7 +316,7 @@ pl (1)
@cmd.options[:domain] = :local
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -246,8 +328,8 @@ a (3.a, 2, 1)
pl (1 i386-linux)
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_local_notty
@@ -257,9 +339,9 @@ pl (1 i386-linux)
@cmd.handle_options %w[]
- @ui.outs.tty = false
+ @stub_ui.outs.tty = false
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -268,8 +350,8 @@ a (3.a, 2, 1)
pl (1 i386-linux)
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_local_quiet
@@ -280,7 +362,7 @@ pl (1 i386-linux)
@cmd.options[:domain] = :local
Gem.configuration.verbose = false
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -289,8 +371,8 @@ a (3.a, 2, 1)
pl (1 i386-linux)
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_no_versions
@@ -300,7 +382,7 @@ pl (1 i386-linux)
@cmd.handle_options %w[-r --no-versions]
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -312,8 +394,8 @@ a
pl
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_notty
@@ -323,9 +405,9 @@ pl
@cmd.handle_options %w[-r]
- @ui.outs.tty = false
+ @stub_ui.outs.tty = false
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -334,14 +416,14 @@ a (2)
pl (1 i386-linux)
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_prerelease
@cmd.handle_options %w[-r --prerelease]
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -352,8 +434,8 @@ pl (1 i386-linux)
a (3.a)
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_prerelease_local
@@ -363,7 +445,7 @@ a (3.a)
@cmd.handle_options %w[-l --prerelease]
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -375,8 +457,8 @@ a (3.a, 2, 1)
pl (1 i386-linux)
EOF
- assert_equal expected, @ui.output
- assert_equal "WARNING: prereleases are always shown locally\n", @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal "WARNING: prereleases are always shown locally\n", @stub_ui.error
end
def test_execute_remote
@@ -386,7 +468,7 @@ pl (1 i386-linux)
@cmd.options[:domain] = :remote
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -398,8 +480,8 @@ a (2)
pl (1 i386-linux)
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_remote_notty
@@ -409,9 +491,9 @@ pl (1 i386-linux)
@cmd.handle_options %w[]
- @ui.outs.tty = false
+ @stub_ui.outs.tty = false
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -420,8 +502,8 @@ a (3.a, 2, 1)
pl (1 i386-linux)
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_remote_quiet
@@ -432,7 +514,7 @@ pl (1 i386-linux)
@cmd.options[:domain] = :remote
Gem.configuration.verbose = false
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -441,14 +523,14 @@ a (2)
pl (1 i386-linux)
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_make_entry
a_2_name = @specs['a-2'].original_name
- @fetcher.data.delete \
+ @stub_fetcher.data.delete \
"#{@gem_repo}quick/Marshal.#{Gem.marshal_version}/#{a_2_name}.gemspec.rz"
a2 = @specs['a-2']
@@ -472,26 +554,26 @@ pl (1 i386-linux)
@cmd.handle_options %w[a pl]
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
- assert_match %r%^a %, @ui.output
- assert_match %r%^pl %, @ui.output
- assert_equal '', @ui.error
+ assert_match %r%^a %, @stub_ui.output
+ assert_match %r%^pl %, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_show_gems
@cmd.options[:name] = //
@cmd.options[:domain] = :remote
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.send :show_gems, /a/i, false
end
- assert_match %r%^a %, @ui.output
- refute_match %r%^pl %, @ui.output
- assert_empty @ui.error
+ assert_match %r%^a %, @stub_ui.output
+ refute_match %r%^pl %, @stub_ui.output
+ assert_empty @stub_ui.error
end
private
@@ -522,7 +604,7 @@ class TestGemCommandsQueryCommandWithoutInstalledGems < Gem::TestCase
@cmd.handle_options %w[-r -a]
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -533,8 +615,8 @@ class TestGemCommandsQueryCommandWithoutInstalledGems < Gem::TestCase
a (2 universal-darwin, 1 ruby x86-linux)
EOF
- assert_equal expected, @ui.output
- assert_equal '', @ui.error
+ assert_equal expected, @stub_ui.output
+ assert_equal '', @stub_ui.error
end
def test_execute_show_default_gems
@@ -543,7 +625,7 @@ a (2 universal-darwin, 1 ruby x86-linux)
a1 = new_default_spec 'a', 1
install_default_specs a1
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -554,7 +636,26 @@ a (2 universal-darwin, 1 ruby x86-linux)
a (2, default: 1)
EOF
- assert_equal expected, @ui.output
+ assert_equal expected, @stub_ui.output
+ end
+
+ def test_execute_show_default_gems_with_platform
+ a1 = new_default_spec 'a', 1
+ a1.platform = 'java'
+ install_default_specs a1
+
+ use_ui @stub_ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** LOCAL GEMS ***
+
+a (default: 1 java)
+EOF
+
+ assert_equal expected, @stub_ui.output
end
def test_execute_default_details
@@ -567,7 +668,7 @@ EOF
@cmd.handle_options %w[-l -d]
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -584,7 +685,7 @@ a (2, 1)
this is a summary
EOF
- assert_equal expected, @ui.output
+ assert_equal expected, @stub_ui.output
end
def test_execute_local_details
@@ -605,11 +706,11 @@ a (2, 1)
@cmd.handle_options %w[-l -d]
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
- str = @ui.output
+ str = @stub_ui.output
str.gsub!(/\(\d\): [^\n]*/, "-")
str.gsub!(/at: [^\n]*/, "at: -")
@@ -639,10 +740,10 @@ pl (1)
this is a summary
EOF
- assert_equal expected, @ui.output
+ assert_equal expected, @stub_ui.output
end
- def test_execute_exact
+ def test_execute_exact_remote
spec_fetcher do |fetcher|
fetcher.spec 'coolgem-omg', 3
fetcher.spec 'coolgem', '4.2.1'
@@ -651,7 +752,7 @@ pl (1)
@cmd.handle_options %w[--remote --exact coolgem]
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
@@ -662,7 +763,61 @@ pl (1)
coolgem (4.2.1)
EOF
- assert_equal expected, @ui.output
+ assert_equal expected, @stub_ui.output
+ end
+
+ def test_execute_exact_local
+ spec_fetcher do |fetcher|
+ fetcher.spec 'coolgem-omg', 3
+ fetcher.spec 'coolgem', '4.2.1'
+ fetcher.spec 'wow_coolgem', 1
+ end
+
+ @cmd.handle_options %w[--exact coolgem]
+
+ use_ui @stub_ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** LOCAL GEMS ***
+
+coolgem (4.2.1)
+ EOF
+
+ assert_equal expected, @stub_ui.output
+ end
+
+ def test_execute_exact_multiple
+ spec_fetcher do |fetcher|
+ fetcher.spec 'coolgem-omg', 3
+ fetcher.spec 'coolgem', '4.2.1'
+ fetcher.spec 'wow_coolgem', 1
+
+ fetcher.spec 'othergem-omg', 3
+ fetcher.spec 'othergem', '1.2.3'
+ fetcher.spec 'wow_othergem', 1
+ end
+
+ @cmd.handle_options %w[--exact coolgem othergem]
+
+ use_ui @stub_ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** LOCAL GEMS ***
+
+coolgem (4.2.1)
+
+*** LOCAL GEMS ***
+
+othergem (1.2.3)
+ EOF
+
+ assert_equal expected, @stub_ui.output
end
private
diff --git a/test/rubygems/test_gem_commands_search_command.rb b/test/rubygems/test_gem_commands_search_command.rb
index 61caff1fc9..9187050c30 100644
--- a/test/rubygems/test_gem_commands_search_command.rb
+++ b/test/rubygems/test_gem_commands_search_command.rb
@@ -15,4 +15,3 @@ class TestGemCommandsSearchCommand < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_commands_server_command.rb b/test/rubygems/test_gem_commands_server_command.rb
index b61fc30e9b..d511ce0b7b 100644
--- a/test/rubygems/test_gem_commands_server_command.rb
+++ b/test/rubygems/test_gem_commands_server_command.rb
@@ -57,4 +57,3 @@ class TestGemCommandsServerCommand < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_commands_setup_command.rb b/test/rubygems/test_gem_commands_setup_command.rb
index ae3d0500dc..f1598f3fc2 100644
--- a/test/rubygems/test_gem_commands_setup_command.rb
+++ b/test/rubygems/test_gem_commands_setup_command.rb
@@ -6,6 +6,13 @@ require 'rubygems/commands/setup_command'
class TestGemCommandsSetupCommand < Gem::TestCase
+ bundler_gemspec = File.expand_path("../../../bundler/lib/bundler/version.rb", __FILE__)
+ if File.exist?(bundler_gemspec)
+ BUNDLER_VERS = File.read(bundler_gemspec).match(/VERSION = "(#{Gem::Version::VERSION_PATTERN})"/)[1]
+ else
+ BUNDLER_VERS = "1.16.2".freeze
+ end
+
def setup
super
@@ -16,10 +23,118 @@ class TestGemCommandsSetupCommand < Gem::TestCase
FileUtils.mkdir_p 'bin'
FileUtils.mkdir_p 'lib/rubygems/ssl_certs/rubygems.org'
- open 'bin/gem', 'w' do |io| io.puts '# gem' end
- open 'lib/rubygems.rb', 'w' do |io| io.puts '# rubygems.rb' end
- open 'lib/rubygems/test_case.rb', 'w' do |io| io.puts '# test_case.rb' end
- open 'lib/rubygems/ssl_certs/rubygems.org/foo.pem', 'w' do |io| io.puts 'PEM' end
+ File.open 'bin/gem', 'w' do |io| io.puts '# gem' end
+ File.open 'lib/rubygems.rb', 'w' do |io| io.puts '# rubygems.rb' end
+ File.open 'lib/rubygems/test_case.rb', 'w' do |io| io.puts '# test_case.rb' end
+ File.open 'lib/rubygems/ssl_certs/rubygems.org/foo.pem', 'w' do |io| io.puts 'PEM' end
+
+ FileUtils.mkdir_p 'bundler/exe'
+ FileUtils.mkdir_p 'bundler/lib/bundler'
+
+ File.open 'bundler/exe/bundle', 'w' do |io| io.puts '# bundle' end
+ File.open 'bundler/lib/bundler.rb', 'w' do |io| io.puts '# bundler.rb' end
+ File.open 'bundler/lib/bundler/b.rb', 'w' do |io| io.puts '# b.rb' end
+
+ FileUtils.mkdir_p 'default/gems'
+
+ gemspec = Gem::Specification.new
+ gemspec.name = "bundler"
+ gemspec.version = BUNDLER_VERS
+ gemspec.bindir = "exe"
+ gemspec.executables = ["bundle"]
+
+ File.open 'bundler/bundler.gemspec', 'w' do |io|
+ io.puts gemspec.to_ruby
+ end
+
+ open(File.join(Gem::Specification.default_specifications_dir, "bundler-1.15.4.gemspec"), 'w') do |io|
+ gemspec.version = "1.15.4"
+ io.puts gemspec.to_ruby
+ end
+
+ FileUtils.mkdir_p File.join(Gem.default_dir, "specifications")
+
+ open(File.join(Gem.default_dir, "specifications", "bundler-#{BUNDLER_VERS}.gemspec"), 'w') do |io|
+ io.puts "# bundler-#{BUNDLER_VERS}"
+ end
+
+ open(File.join(Gem.default_dir, "specifications", "bundler-audit-1.0.0.gemspec"), 'w') do |io|
+ io.puts '# bundler-audit'
+ end
+
+ FileUtils.mkdir_p 'default/gems/bundler-1.15.4'
+ FileUtils.mkdir_p 'default/gems/bundler-audit-1.0.0'
+ end
+
+ def gem_install(name)
+ gem = util_spec name do |s|
+ s.executables = [name]
+ s.files = %W[bin/#{name}]
+ end
+ write_file File.join @tempdir, 'bin', name do |f|
+ f.puts '#!/usr/bin/ruby'
+ end
+ install_gem gem
+ File.join @gemhome, 'bin', name
+ end
+
+ def test_execute_regenerate_binstubs
+ gem_bin_path = gem_install 'a'
+ write_file gem_bin_path do |io|
+ io.puts 'I changed it!'
+ end
+
+ @cmd.options[:document] = []
+ @cmd.execute
+
+ assert_match %r{\A#!}, File.read(gem_bin_path)
+ end
+
+ def test_execute_no_regenerate_binstubs
+ gem_bin_path = gem_install 'a'
+ write_file gem_bin_path do |io|
+ io.puts 'I changed it!'
+ end
+
+ @cmd.options[:document] = []
+ @cmd.options[:regenerate_binstubs] = false
+ @cmd.execute
+
+ assert_equal "I changed it!\n", File.read(gem_bin_path)
+ end
+
+ def test_env_shebang_flag
+ gem_bin_path = gem_install 'a'
+ write_file gem_bin_path do |io|
+ io.puts 'I changed it!'
+ end
+
+ @cmd.options[:document] = []
+ @cmd.options[:env_shebang] = true
+ @cmd.execute
+
+ gem_exec = sprintf Gem.default_exec_format, 'gem'
+ default_gem_bin_path = File.join @install_dir, 'bin', gem_exec
+ if Gem::USE_BUNDLER_FOR_GEMDEPS
+ bundle_exec = sprintf Gem.default_exec_format, 'bundle'
+ default_bundle_bin_path = File.join @install_dir, 'bin', bundle_exec
+ end
+
+ ruby_exec = sprintf Gem.default_exec_format, 'ruby'
+
+ if Gem.win_platform?
+ assert_match %r%\A#!\s*#{ruby_exec}%, File.read(default_gem_bin_path)
+ if Gem::USE_BUNDLER_FOR_GEMDEPS
+ assert_match %r%\A#!\s*#{ruby_exec}%, File.read(default_bundle_bin_path)
+ end
+ assert_match %r%\A#!\s*#{ruby_exec}%, File.read(gem_bin_path)
+ else
+ assert_match %r%\A#!/usr/bin/env #{ruby_exec}%, File.read(default_gem_bin_path)
+ if Gem::USE_BUNDLER_FOR_GEMDEPS
+ assert_match %r%\A#!/usr/bin/env #{ruby_exec}%, File.read(default_bundle_bin_path)
+ end
+ assert_match %r%\A#!/usr/bin/env #{ruby_exec}%, File.read(gem_bin_path)
+ end
end
def test_pem_files_in
@@ -40,12 +155,57 @@ class TestGemCommandsSetupCommand < Gem::TestCase
assert_path_exists File.join(dir, 'rubygems.rb')
assert_path_exists File.join(dir, 'rubygems/ssl_certs/rubygems.org/foo.pem')
+
+ if Gem::USE_BUNDLER_FOR_GEMDEPS
+ assert_path_exists File.join(dir, 'bundler.rb')
+ assert_path_exists File.join(dir, 'bundler/b.rb')
+ end
end
end
+ def test_install_default_bundler_gem
+ @cmd.extend FileUtils
+
+ @cmd.install_default_bundler_gem
+
+ if Gem.win_platform?
+ bundler_spec = Gem::Specification.load("bundler/bundler.gemspec")
+ default_spec_path = File.join(Gem::Specification.default_specifications_dir, "#{bundler_spec.full_name}.gemspec")
+ spec = Gem::Specification.load(default_spec_path)
+
+ spec.executables.each do |e|
+ assert_path_exists File.join(spec.bin_dir, "#{e}.bat")
+ end
+ end
+
+ default_dir = Gem::Specification.default_specifications_dir
+
+ # expect to remove other versions of bundler gemspecs on default specification directory.
+ refute_path_exists File.join(default_dir, "bundler-1.15.4.gemspec")
+ assert_path_exists File.join(default_dir, "bundler-#{BUNDLER_VERS}.gemspec")
+
+ # expect to not remove bundler-* gemspecs.
+ assert_path_exists File.join(Gem.default_dir, "specifications", "bundler-audit-1.0.0.gemspec")
+
+ # expect to remove normal gem that was same version. because it's promoted default gems.
+ refute_path_exists File.join(Gem.default_dir, "specifications", "bundler-#{BUNDLER_VERS}.gemspec")
+
+ # expect to install default gems. It location was `site_ruby` directory on real world.
+ assert_path_exists "default/gems/bundler-#{BUNDLER_VERS}"
+
+ # expect to not remove other versions of bundler on `site_ruby`
+ assert_path_exists 'default/gems/bundler-1.15.4'
+
+ # TODO: We need to assert to remove same version of bundler on gem_dir directory(It's not site_ruby dir)
+
+ # expect to not remove bundler-* direcotyr.
+ assert_path_exists 'default/gems/bundler-audit-1.0.0'
+ end if Gem::USE_BUNDLER_FOR_GEMDEPS
+
def test_remove_old_lib_files
lib = File.join @install_dir, 'lib'
lib_rubygems = File.join lib, 'rubygems'
+ lib_bundler = File.join lib, 'bundler'
lib_rubygems_defaults = File.join lib_rubygems, 'defaults'
securerandom_rb = File.join lib, 'securerandom.rb'
@@ -55,21 +215,25 @@ class TestGemCommandsSetupCommand < Gem::TestCase
old_builder_rb = File.join lib_rubygems, 'builder.rb'
old_format_rb = File.join lib_rubygems, 'format.rb'
+ old_bundler_c_rb = File.join lib_bundler, 'c.rb'
FileUtils.mkdir_p lib_rubygems_defaults
+ FileUtils.mkdir_p lib_bundler
- open securerandom_rb, 'w' do |io| io.puts '# securerandom.rb' end
+ File.open securerandom_rb, 'w' do |io| io.puts '# securerandom.rb' end
- open old_builder_rb, 'w' do |io| io.puts '# builder.rb' end
- open old_format_rb, 'w' do |io| io.puts '# format.rb' end
+ File.open old_builder_rb, 'w' do |io| io.puts '# builder.rb' end
+ File.open old_format_rb, 'w' do |io| io.puts '# format.rb' end
+ File.open old_bundler_c_rb, 'w' do |io| io.puts '# c.rb' end
- open engine_defaults_rb, 'w' do |io| io.puts '# jruby.rb' end
- open os_defaults_rb, 'w' do |io| io.puts '# operating_system.rb' end
+ File.open engine_defaults_rb, 'w' do |io| io.puts '# jruby.rb' end
+ File.open os_defaults_rb, 'w' do |io| io.puts '# operating_system.rb' end
@cmd.remove_old_lib_files lib
refute_path_exists old_builder_rb
refute_path_exists old_format_rb
+ refute_path_exists old_bundler_c_rb if Gem::USE_BUNDLER_FOR_GEMDEPS
assert_path_exists securerandom_rb
assert_path_exists engine_defaults_rb
@@ -77,15 +241,12 @@ class TestGemCommandsSetupCommand < Gem::TestCase
end
def test_show_release_notes
- @default_external = nil
- if Object.const_defined? :Encoding
- @default_external = @ui.outs.external_encoding
- @ui.outs.set_encoding Encoding::US_ASCII
- end
+ @default_external = @ui.outs.external_encoding
+ @ui.outs.set_encoding Encoding::US_ASCII
@cmd.options[:previous_version] = Gem::Version.new '2.0.2'
- open 'History.txt', 'w' do |io|
+ File.open 'History.txt', 'w' do |io|
io.puts <<-History_txt
# coding: UTF-8
@@ -126,7 +287,7 @@ class TestGemCommandsSetupCommand < Gem::TestCase
EXPECTED
output = @ui.output
- output.force_encoding Encoding::UTF_8 if Object.const_defined? :Encoding
+ output.force_encoding Encoding::UTF_8
assert_equal expected, output
ensure
diff --git a/test/rubygems/test_gem_commands_signin_command.rb b/test/rubygems/test_gem_commands_signin_command.rb
new file mode 100644
index 0000000000..afcb8d6d99
--- /dev/null
+++ b/test/rubygems/test_gem_commands_signin_command.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+require 'rubygems/test_case'
+require 'rubygems/commands/signin_command'
+require 'rubygems/installer'
+
+class TestGemCommandsSigninCommand < Gem::TestCase
+
+ def setup
+ super
+
+ Gem.configuration.rubygems_api_key = nil
+ Gem.configuration.api_keys.clear
+
+ @cmd = Gem::Commands::SigninCommand.new
+ end
+
+ def teardown
+ credentials_path = Gem.configuration.credentials_path
+ File.delete(credentials_path) if File.exist?(credentials_path)
+ super
+ end
+
+ def test_execute_when_not_already_signed_in
+ sign_in_ui = util_capture() { @cmd.execute }
+ assert_match %r{Signed in.}, sign_in_ui.output
+ end
+
+ def test_execute_when_already_signed_in_with_same_host
+ host = 'http://some-gemcutter-compatible-host.org'
+
+ util_capture(nil, host) { @cmd.execute }
+ old_credentials = YAML.load_file Gem.configuration.credentials_path
+
+ util_capture(nil, host) { @cmd.execute }
+ new_credentials = YAML.load_file Gem.configuration.credentials_path
+
+ assert_equal old_credentials[host], new_credentials[host]
+ end
+
+ def test_execute_when_already_signed_in_with_different_host
+ api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf04045xxxx'
+
+ util_capture(nil, nil, api_key) { @cmd.execute }
+ host = 'http://some-gemcutter-compatible-host.org'
+
+ util_capture(nil, host, api_key) { @cmd.execute }
+ credentials = YAML.load_file Gem.configuration.credentials_path
+
+ assert_equal credentials[:rubygems_api_key], api_key
+
+ assert_nil credentials[host]
+ end
+
+ def test_execute_with_host_supplied
+ host = 'http://some-gemcutter-compatible-host.org'
+
+ sign_in_ui = util_capture(nil, host) { @cmd.execute }
+ assert_match %r{Enter your #{host} credentials.}, sign_in_ui.output
+ assert_match %r{Signed in.}, sign_in_ui.output
+
+ api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
+ credentials = YAML.load_file Gem.configuration.credentials_path
+ assert_equal api_key, credentials[host]
+ end
+
+ def test_execute_with_valid_creds_set_for_default_host
+ util_capture {@cmd.execute}
+
+ api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
+ credentials = YAML.load_file Gem.configuration.credentials_path
+
+ assert_equal api_key, credentials[:rubygems_api_key]
+ end
+
+ # Utility method to capture IO/UI within the block passed
+
+ def util_capture(ui_stub = nil, host = nil, api_key = nil)
+ api_key ||= 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
+ response = [api_key, 200, 'OK']
+ email = 'you@example.com'
+ password = 'secret'
+ fetcher = Gem::FakeFetcher.new
+
+ # Set the expected response for the Web-API supplied
+ ENV['RUBYGEMS_HOST'] = host || Gem::DEFAULT_HOST
+ data_key = "#{ENV['RUBYGEMS_HOST']}/api/v1/api_key"
+ fetcher.data[data_key] = response
+ Gem::RemoteFetcher.fetcher = fetcher
+
+ sign_in_ui = ui_stub || Gem::MockGemUi.new("#{email}\n#{password}\n")
+
+ use_ui sign_in_ui do
+ yield
+ end
+
+ sign_in_ui
+ end
+end
diff --git a/test/rubygems/test_gem_commands_signout_command.rb b/test/rubygems/test_gem_commands_signout_command.rb
new file mode 100644
index 0000000000..814b55cf4f
--- /dev/null
+++ b/test/rubygems/test_gem_commands_signout_command.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'rubygems/test_case'
+require 'rubygems/commands/signout_command'
+require 'rubygems/installer'
+
+class TestGemCommandsSignoutCommand < Gem::TestCase
+
+ def setup
+ super
+ @cmd = Gem::Commands::SignoutCommand.new
+ end
+
+ def teardown
+ super
+ File.delete Gem.configuration.credentials_path if File.exist?(Gem.configuration.credentials_path)
+ end
+
+ def test_execute_when_user_is_signed_in
+ FileUtils.mkdir_p File.dirname(Gem.configuration.credentials_path)
+ FileUtils::touch Gem.configuration.credentials_path
+
+ @sign_out_ui = Gem::MockGemUi.new
+ use_ui(@sign_out_ui) { @cmd.execute }
+
+ assert_match %r{You have successfully signed out}, @sign_out_ui.output
+ assert_equal false, File.exist?(Gem.configuration.credentials_path)
+ end
+
+ def test_execute_when_not_signed_in # i.e. no credential file created
+ @sign_out_ui = Gem::MockGemUi.new
+ use_ui(@sign_out_ui) { @cmd.execute }
+
+ assert_match %r{You are not currently signed in}, @sign_out_ui.error
+ end
+
+end
diff --git a/test/rubygems/test_gem_commands_sources_command.rb b/test/rubygems/test_gem_commands_sources_command.rb
index 014b4b4c12..ad92c26073 100644
--- a/test/rubygems/test_gem_commands_sources_command.rb
+++ b/test/rubygems/test_gem_commands_sources_command.rb
@@ -108,6 +108,58 @@ source #{@gem_repo} already present in the cache
assert_equal '', @ui.error
end
+ def test_execute_add_redundant_source_trailing_slash
+ # Remove pre-existing gem source (w/ slash)
+ repo_with_slash = "http://gems.example.com/"
+ @cmd.handle_options %W[--remove #{repo_with_slash}]
+ use_ui @ui do
+ @cmd.execute
+ end
+ source = Gem::Source.new repo_with_slash
+ assert_equal false, Gem.sources.include?(source)
+
+ expected = <<-EOF
+#{repo_with_slash} removed from sources
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+
+ # Re-add pre-existing gem source (w/o slash)
+ repo_without_slash = "http://gems.example.com"
+ @cmd.handle_options %W[--add #{repo_without_slash}]
+ use_ui @ui do
+ @cmd.execute
+ end
+ source = Gem::Source.new repo_without_slash
+ assert_equal true, Gem.sources.include?(source)
+
+ expected = <<-EOF
+http://gems.example.com/ removed from sources
+http://gems.example.com added to sources
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+
+ # Re-add original gem source (w/ slash)
+ @cmd.handle_options %W[--add #{repo_with_slash}]
+ use_ui @ui do
+ @cmd.execute
+ end
+ source = Gem::Source.new repo_with_slash
+ assert_equal true, Gem.sources.include?(source)
+
+ expected = <<-EOF
+http://gems.example.com/ removed from sources
+http://gems.example.com added to sources
+source http://gems.example.com/ already present in the cache
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
def test_execute_add_http_rubygems_org
http_rubygems_org = 'http://rubygems.org'
@@ -246,4 +298,3 @@ beta-gems.example.com is not a URI
end
end
-
diff --git a/test/rubygems/test_gem_commands_specification_command.rb b/test/rubygems/test_gem_commands_specification_command.rb
index c3650649ae..f56aa9777a 100644
--- a/test/rubygems/test_gem_commands_specification_command.rb
+++ b/test/rubygems/test_gem_commands_specification_command.rb
@@ -105,7 +105,7 @@ class TestGemCommandsSpecificationCommand < Gem::TestCase
end
def test_execute_field
- foo = new_spec 'foo', '2'
+ foo = util_spec 'foo', '2'
install_specs foo
@@ -137,7 +137,7 @@ class TestGemCommandsSpecificationCommand < Gem::TestCase
end
def test_execute_marshal
- foo = new_spec 'foo', '2'
+ foo = util_spec 'foo', '2'
install_specs foo
@@ -248,4 +248,3 @@ class TestGemCommandsSpecificationCommand < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_commands_stale_command.rb b/test/rubygems/test_gem_commands_stale_command.rb
index 4d2f5349f0..a8909c6d13 100644
--- a/test/rubygems/test_gem_commands_stale_command.rb
+++ b/test/rubygems/test_gem_commands_stale_command.rb
@@ -6,6 +6,7 @@ class TestGemCommandsStaleCommand < Gem::TestCase
def setup
super
+ @stub_ui = Gem::MockGemUi.new
@cmd = Gem::Commands::StaleCommand.new
end
@@ -31,11 +32,11 @@ class TestGemCommandsStaleCommand < Gem::TestCase
FileUtils.touch(filename, :mtime => Time.now - 86400)
end
- use_ui @ui do
+ use_ui @stub_ui do
@cmd.execute
end
- lines = @ui.output.split("\n")
+ lines = @stub_ui.output.split("\n")
assert_equal("#{foo_bar.name}-#{foo_bar.version}", lines[0].split.first)
assert_equal("#{bar_baz.name}-#{bar_baz.version}", lines[1].split.first)
end
diff --git a/test/rubygems/test_gem_commands_uninstall_command.rb b/test/rubygems/test_gem_commands_uninstall_command.rb
index e64bc77123..407d2451a6 100644
--- a/test/rubygems/test_gem_commands_uninstall_command.rb
+++ b/test/rubygems/test_gem_commands_uninstall_command.rb
@@ -83,7 +83,7 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
end
end
- if win_platform? then
+ if win_platform?
assert File.exist?(@executable)
else
assert File.symlink?(@executable)
@@ -92,7 +92,7 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
# Evil hack to prevent false removal success
FileUtils.rm_f @executable
- open @executable, "wb+" do |f| f.puts "binary" end
+ File.open @executable, "wb+" do |f| f.puts "binary" end
@cmd.options[:executables] = true
@cmd.options[:args] = [@spec.name]
@@ -151,12 +151,14 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
assert_match(/Successfully uninstalled/, output)
end
- def test_execute_with_force_leaves_executable
+ def test_execute_with_version_leaves_non_matching_versions
ui = Gem::MockGemUi.new
util_make_gems
util_setup_gem ui
+ assert_equal 3, Gem::Specification.find_all_by_name('a').length
+
@cmd.options[:version] = '1'
@cmd.options[:force] = true
@cmd.options[:args] = ['a']
@@ -165,17 +167,43 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
@cmd.execute
end
- assert !Gem::Specification.all_names.include?('a')
+ assert_equal 2, Gem::Specification.find_all_by_name('a').length
+
assert File.exist? File.join(@gemhome, 'bin', 'executable')
end
- def test_execute_with_force_uninstalls_all_versions
+ def test_execute_with_version_specified_as_colon
ui = Gem::MockGemUi.new "y\n"
util_make_gems
util_setup_gem ui
- assert Gem::Specification.find_all_by_name('a').length > 1
+ assert_equal 3, Gem::Specification.find_all_by_name('a').length
+
+ @cmd.options[:force] = true
+ @cmd.options[:args] = ['a:1']
+
+ use_ui ui do
+ @cmd.execute
+ end
+
+ assert_equal 2, Gem::Specification.find_all_by_name('a').length
+
+ assert File.exist? File.join(@gemhome, 'bin', 'executable')
+ end
+
+ def test_execute_with_force_and_without_version_uninstalls_everything
+ ui = Gem::MockGemUi.new "y\n"
+
+ a_1, = util_gem 'a', 1
+ install_gem a_1
+
+ a_3a, = util_gem 'a', '3.a'
+ install_gem a_3a
+
+ util_setup_gem ui
+
+ assert_equal 3, Gem::Specification.find_all_by_name('a').length
@cmd.options[:force] = true
@cmd.options[:args] = ['a']
@@ -184,7 +212,9 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
@cmd.execute
end
- refute_includes Gem::Specification.all_names, 'a'
+ assert_empty Gem::Specification.find_all_by_name('a')
+ assert_match "Removing executable", ui.output
+ refute File.exist? @executable
end
def test_execute_with_force_ignores_dependencies
@@ -204,7 +234,7 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
end
assert Gem::Specification.find_all_by_name('dep_x').length > 0
- assert Gem::Specification.find_all_by_name('x').length == 0
+ assert Gem::Specification.find_all_by_name('x').length.zero?
end
def test_execute_all
@@ -238,7 +268,7 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
@cmd.handle_options %w[]
assert_equal false, @cmd.options[:check_dev]
- assert_equal nil, @cmd.options[:install_dir]
+ assert_nil @cmd.options[:install_dir]
assert_equal true, @cmd.options[:user_install]
assert_equal Gem::Requirement.default, @cmd.options[:version]
assert_equal false, @cmd.options[:vendor]
@@ -261,6 +291,25 @@ WARNING: Use your OS package manager to uninstall vendor gems
assert_match expected, @ui.error
end
+ def test_execute_two_version
+ @cmd.options[:args] = %w[a b]
+ @cmd.options[:version] = Gem::Requirement.new("> 1")
+
+ use_ui @ui do
+ e = assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+
+ assert_equal 1, e.exit_code
+ end
+
+ msg = "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'`"
+
+ assert_empty @ui.output
+ assert_equal msg, @ui.error.lines.last.chomp
+ end
+
def test_handle_options_vendor_missing
orig_vendordir = RbConfig::CONFIG['vendordir']
RbConfig::CONFIG.delete 'vendordir'
@@ -279,5 +328,42 @@ WARNING: Use your OS package manager to uninstall vendor gems
RbConfig::CONFIG['vendordir'] = orig_vendordir
end
-end
+ def test_execute_with_gem_not_installed
+ @cmd.options[:args] = ['d']
+
+ use_ui ui do
+ @cmd.execute
+ end
+
+ output = ui.output.split "\n"
+
+ assert_equal output.first, "Gem 'd' is not installed"
+ end
+
+ def test_execute_with_gem_uninstall_error
+ util_make_gems
+ @cmd.options[:args] = %w[a]
+
+ uninstall_exception = lambda do |_a|
+ ex = Gem::UninstallError.new
+ ex.spec = @spec
+
+ raise ex
+ end
+
+ e = nil
+ @cmd.stub :uninstall, uninstall_exception do
+ use_ui @ui do
+ e = assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ end
+
+ assert_equal 1, e.exit_code
+ end
+
+ assert_empty @ui.output
+ assert_match %r!Error: unable to successfully uninstall '#{@spec.name}'!, @ui.error
+ end
+end
diff --git a/test/rubygems/test_gem_commands_unpack_command.rb b/test/rubygems/test_gem_commands_unpack_command.rb
index 61f671da7d..7d96caaf57 100644
--- a/test/rubygems/test_gem_commands_unpack_command.rb
+++ b/test/rubygems/test_gem_commands_unpack_command.rb
@@ -134,6 +134,23 @@ class TestGemCommandsUnpackCommand < Gem::TestCase
assert File.exist?(File.join(@tempdir, 'b-2.gemspec'))
end
+ def test_execute_spec_target
+ util_make_gems
+
+ @cmd.options[:args] = %w[a b]
+ @cmd.options[:target] = 'specs'
+ @cmd.options[:spec] = true
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ assert File.exist?(File.join(@tempdir, 'specs/a-3.a.gemspec'))
+ assert File.exist?(File.join(@tempdir, 'specs/b-2.gemspec'))
+ end
+
def test_execute_sudo
skip 'Cannot perform this test on windows (chmod)' if win_platform?
@@ -206,4 +223,3 @@ class TestGemCommandsUnpackCommand < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_commands_update_command.rb b/test/rubygems/test_gem_commands_update_command.rb
index 4a9f496cbe..549e34c218 100644
--- a/test/rubygems/test_gem_commands_update_command.rb
+++ b/test/rubygems/test_gem_commands_update_command.rb
@@ -2,12 +2,6 @@
require 'rubygems/test_case'
require 'rubygems/commands/update_command'
-begin
- gem "rdoc"
-rescue Gem::LoadError
- # ignore
-end
-
class TestGemCommandsUpdateCommand < Gem::TestCase
def setup
@@ -107,7 +101,7 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
end
out = @ui.output.split "\n"
- assert_equal "Latest version currently installed. Aborting.", out.shift
+ assert_equal "Latest version already installed. Done.", out.shift
assert_empty out
end
@@ -223,7 +217,6 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
end
def test_execute_rdoc
- skip if RUBY_VERSION <= "1.8.7"
spec_fetcher do |fetcher|
fetcher.download 'a', 2
fetcher.spec 'a', 1
@@ -510,5 +503,23 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
assert_empty arguments
end
-end
+ def test_explain
+ spec_fetcher do |fetcher|
+ fetcher.download 'a', 2
+ fetcher.spec 'a', 1
+ end
+
+ @cmd.options[:explain] = true
+ @cmd.options[:args] = %w[a]
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+
+ assert_equal "Gems to update:", out.shift
+ assert_equal " a-2", out.shift
+ assert_empty out
+ end
+end
diff --git a/test/rubygems/test_gem_commands_which_command.rb b/test/rubygems/test_gem_commands_which_command.rb
index c55bb370cd..0d63bb9b37 100644
--- a/test/rubygems/test_gem_commands_which_command.rb
+++ b/test/rubygems/test_gem_commands_which_command.rb
@@ -33,7 +33,7 @@ class TestGemCommandsWhichCommand < Gem::TestCase
end
assert_equal '', @ui.output
- assert_match %r%Can.t find ruby library file or shared library directory\n%,
+ assert_match %r%Can.t find Ruby library file or shared library directory\n%,
@ui.error
end
@@ -51,7 +51,7 @@ class TestGemCommandsWhichCommand < Gem::TestCase
end
assert_equal "#{@foo_bar.full_gem_path}/lib/foo_bar.rb\n", @ui.output
- assert_match %r%Can.t find ruby library file or shared library missinglib\n%,
+ assert_match %r%Can.t find Ruby library file or shared library missinglib\n%,
@ui.error
end
@@ -65,7 +65,7 @@ class TestGemCommandsWhichCommand < Gem::TestCase
end
assert_equal '', @ui.output
- assert_match %r%Can.t find ruby library file or shared library missinglib\n%,
+ assert_match %r%Can.t find Ruby library file or shared library missinglib\n%,
@ui.error
end
@@ -84,4 +84,3 @@ class TestGemCommandsWhichCommand < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_commands_yank_command.rb b/test/rubygems/test_gem_commands_yank_command.rb
index 70aa2263a6..d30c386aa6 100644
--- a/test/rubygems/test_gem_commands_yank_command.rb
+++ b/test/rubygems/test_gem_commands_yank_command.rb
@@ -97,4 +97,3 @@ class TestGemCommandsYankCommand < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_config_file.rb b/test/rubygems/test_gem_config_file.rb
index e09a76ef98..192699aaba 100644
--- a/test/rubygems/test_gem_config_file.rb
+++ b/test/rubygems/test_gem_config_file.rb
@@ -44,6 +44,7 @@ class TestGemConfigFile < Gem::TestCase
assert_equal Gem::ConfigFile::DEFAULT_BULK_THRESHOLD, @cfg.bulk_threshold
assert_equal true, @cfg.verbose
assert_equal [@gem_repo], Gem.sources
+ assert_equal 365, @cfg.cert_expiration_length_days
File.open @temp_conf, 'w' do |fp|
fp.puts ":backtrace: true"
@@ -58,6 +59,7 @@ class TestGemConfigFile < Gem::TestCase
fp.puts "- /var/ruby/1.8/gem_home"
fp.puts ":ssl_verify_mode: 0"
fp.puts ":ssl_ca_cert: /etc/ssl/certs"
+ fp.puts ":cert_expiration_length_days: 28"
end
util_config_file
@@ -71,6 +73,7 @@ class TestGemConfigFile < Gem::TestCase
@cfg.path)
assert_equal 0, @cfg.ssl_verify_mode
assert_equal '/etc/ssl/certs', @cfg.ssl_ca_cert
+ assert_equal 28, @cfg.cert_expiration_length_days
end
def test_initialize_handle_arguments_config_file
@@ -341,7 +344,7 @@ if you believe they were disclosed to a third party.
assert_equal expected, YAML.load_file(@cfg.credentials_path)
- unless win_platform? then
+ unless win_platform?
stat = File.stat @cfg.credentials_path
assert_equal 0600, stat.mode & 0600
@@ -487,4 +490,3 @@ if you believe they were disclosed to a third party.
assert_equal(true, @cfg.disable_default_gem_server)
end
end
-
diff --git a/test/rubygems/test_gem_dependency.rb b/test/rubygems/test_gem_dependency.rb
index 0d501f567f..daeb8f625c 100644
--- a/test/rubygems/test_gem_dependency.rb
+++ b/test/rubygems/test_gem_dependency.rb
@@ -338,6 +338,34 @@ class TestGemDependency < Gem::TestCase
assert_match "Could not find 'a' (= 2.0) - did find: [a-1.0]", e.message
end
+ def test_to_specs_respects_bundler_version
+ b = util_spec 'bundler', '2.0.0.pre.1'
+ b_1 = util_spec 'bundler', '1'
+ install_specs b, b_1
+
+ b_file = File.join b.gem_dir, 'lib', 'bundler', 'setup.rb'
+
+ write_file b_file do |io|
+ io.puts '# setup.rb'
+ end
+
+ dep = Gem::Dependency.new "bundler", ">= 0.a"
+
+ assert_equal [b, b_1], dep.to_specs
+
+ Gem::BundlerVersionFinder.stub(:bundler_version_with_reason, ["3.5", "reason"]) do
+ e = assert_raises Gem::MissingSpecVersionError do
+ dep.to_specs
+ end
+
+ assert_match "Could not find 'bundler' (3.5) required by reason.\nTo update to the latest version installed on your system, run `bundle update --bundler`.\nTo install the missing version, run `gem install bundler:3.5`\n", e.message
+ end
+
+ Gem::BundlerVersionFinder.stub(:bundler_version_with_reason, ["2.0.0.pre.1", "reason"]) do
+ assert_equal [b], dep.to_specs
+ end
+ end
+
def test_to_specs_indicates_total_gem_set_size
a = util_spec 'a', '1.0'
install_specs a
@@ -359,4 +387,3 @@ class TestGemDependency < Gem::TestCase
end
-
diff --git a/test/rubygems/test_gem_dependency_installer.rb b/test/rubygems/test_gem_dependency_installer.rb
index e55cc75682..74b7a6a83e 100644
--- a/test/rubygems/test_gem_dependency_installer.rb
+++ b/test/rubygems/test_gem_dependency_installer.rb
@@ -424,14 +424,14 @@ class TestGemDependencyInstaller < Gem::TestCase
extconf_rb = File.join @gemhome, 'gems', 'e-1', 'extconf.rb'
FileUtils.mkdir_p File.dirname extconf_rb
- open extconf_rb, 'w' do |io|
+ File.open extconf_rb, 'w' do |io|
io.write <<-EXTCONF_RB
require 'mkmf'
create_makefile 'e'
EXTCONF_RB
end
- e1 = new_spec 'e', '1', nil, 'extconf.rb' do |s|
+ e1 = util_spec 'e', '1', nil, 'extconf.rb' do |s|
s.extensions << 'extconf.rb'
end
e1_gem = File.join @tempdir, 'gems', "#{e1.full_name}.gem"
@@ -445,9 +445,13 @@ class TestGemDependencyInstaller < Gem::TestCase
FileUtils.mv f1_gem, @tempdir
inst = nil
- Dir.chdir @tempdir do
+ pwd = Dir.getwd
+ Dir.chdir @tempdir
+ begin
inst = Gem::DependencyInstaller.new
inst.install 'f'
+ ensure
+ Dir.chdir pwd
end
assert_equal %w[f-1], inst.installed_gems.map { |s| s.full_name }
@@ -893,7 +897,7 @@ class TestGemDependencyInstaller < Gem::TestCase
assert_equal %w[a-1-cpu-other_platform-1], inst.installed_gems.map { |s| s.full_name }
end
- if defined? OpenSSL then
+ if defined? OpenSSL
def test_install_security_policy
util_setup_gems
@@ -918,7 +922,7 @@ class TestGemDependencyInstaller < Gem::TestCase
end
# Wrappers don't work on mswin
- unless win_platform? then
+ unless win_platform?
def test_install_no_wrappers
util_setup_gems
@@ -1069,7 +1073,6 @@ class TestGemDependencyInstaller < Gem::TestCase
assert_equal 'a-1', remote.spec.full_name, 'remote spec'
assert_equal Gem::Source.new(@gem_repo), remote.source, 'remote path'
-
end
def test_find_gems_with_sources_prerelease
diff --git a/test/rubygems/test_gem_dependency_list.rb b/test/rubygems/test_gem_dependency_list.rb
index d03a8fdfea..9bd6ceb6f3 100644
--- a/test/rubygems/test_gem_dependency_list.rb
+++ b/test/rubygems/test_gem_dependency_list.rb
@@ -11,7 +11,7 @@ class TestGemDependencyList < Gem::TestCase
@deplist = Gem::DependencyList.new
- # TODO: switch to new_spec
+ # TODO: switch to util_spec
@a1 = util_spec 'a', '1'
@a2 = util_spec 'a', '2'
@a3 = util_spec 'a', '3'
@@ -136,21 +136,21 @@ class TestGemDependencyList < Gem::TestCase
exp = {
"b" => [
- Gem::Dependency.new("a", ">= 1")
- ]
+ Gem::Dependency.new("a", ">= 1")
+ ]
}
assert_equal exp, @deplist.why_not_ok?
end
def test_why_not_ok_eh_old_dependency
- a = new_spec 'a', '1',
+ a = util_spec 'a', '1',
'b' => '~> 1.0'
- b0 = new_spec 'b', '1.0',
+ b0 = util_spec 'b', '1.0',
'd' => '>= 0'
- b1 = new_spec 'b', '1.1'
+ b1 = util_spec 'b', '1.1'
util_clear_gems
@@ -257,4 +257,3 @@ class TestGemDependencyList < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_dependency_resolution_error.rb b/test/rubygems/test_gem_dependency_resolution_error.rb
index b9625a3c02..cf4663650e 100644
--- a/test/rubygems/test_gem_dependency_resolution_error.rb
+++ b/test/rubygems/test_gem_dependency_resolution_error.rb
@@ -26,4 +26,3 @@ class TestGemDependencyResolutionError < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_doctor.rb b/test/rubygems/test_gem_doctor.rb
index 39b8a11692..a0e3a18d7f 100644
--- a/test/rubygems/test_gem_doctor.rb
+++ b/test/rubygems/test_gem_doctor.rb
@@ -4,7 +4,7 @@ require 'rubygems/doctor'
class TestGemDoctor < Gem::TestCase
- def gem name
+ def gem(name)
spec = quick_gem name do |gem|
gem.files = %W[lib/#{name}.rb Rakefile]
end
@@ -24,7 +24,7 @@ class TestGemDoctor < Gem::TestCase
FileUtils.rm b.spec_file
- open c.spec_file, 'w' do |io|
+ File.open c.spec_file, 'w' do |io|
io.write 'this will raise an exception when evaluated.'
end
@@ -77,7 +77,7 @@ Removed directory gems/c-2
FileUtils.rm b.spec_file
- open c.spec_file, 'w' do |io|
+ File.open c.spec_file, 'w' do |io|
io.write 'this will raise an exception when evaluated.'
end
@@ -166,4 +166,3 @@ This directory does not appear to be a RubyGems repository, skipping
end
end
-
diff --git a/test/rubygems/test_gem_ext_builder.rb b/test/rubygems/test_gem_ext_builder.rb
index 370ebf8d14..ce04762e84 100644
--- a/test/rubygems/test_gem_ext_builder.rb
+++ b/test/rubygems/test_gem_ext_builder.rb
@@ -32,7 +32,7 @@ class TestGemExtBuilder < Gem::TestCase
results = []
Dir.chdir @ext do
- open 'Makefile', 'w' do |io|
+ File.open 'Makefile', 'w' do |io|
io.puts <<-MAKEFILE
all:
\t@#{Gem.ruby} -e "puts %Q{all: \#{ENV['DESTDIR']}}"
@@ -50,15 +50,9 @@ install:
results = results.join "\n"
- if RUBY_VERSION > '2.0' then
- assert_match %r%"DESTDIR=#{ENV['DESTDIR']}" clean$%, results
- assert_match %r%"DESTDIR=#{ENV['DESTDIR']}"$%, results
- assert_match %r%"DESTDIR=#{ENV['DESTDIR']}" install$%, results
- else
- refute_match %r%"DESTDIR=#{ENV['DESTDIR']}" clean$%, results
- refute_match %r%"DESTDIR=#{ENV['DESTDIR']}"$%, results
- refute_match %r%"DESTDIR=#{ENV['DESTDIR']}" install$%, results
- end
+ assert_match %r%"DESTDIR=#{ENV['DESTDIR']}" clean$%, results
+ assert_match %r%"DESTDIR=#{ENV['DESTDIR']}"$%, results
+ assert_match %r%"DESTDIR=#{ENV['DESTDIR']}" install$%, results
if /nmake/ !~ results
assert_match %r%^clean: destination$%, results
@@ -72,7 +66,7 @@ install:
results = []
Dir.chdir @ext do
- open 'Makefile', 'w' do |io|
+ File.open 'Makefile', 'w' do |io|
io.puts <<-MAKEFILE
all:
\t@#{Gem.ruby} -e "puts %Q{all: \#{ENV['DESTDIR']}}"
@@ -87,15 +81,9 @@ install:
results = results.join "\n"
- if RUBY_VERSION > '2.0' then
- assert_match %r%"DESTDIR=#{ENV['DESTDIR']}" clean$%, results
- assert_match %r%"DESTDIR=#{ENV['DESTDIR']}"$%, results
- assert_match %r%"DESTDIR=#{ENV['DESTDIR']}" install$%, results
- else
- refute_match %r%"DESTDIR=#{ENV['DESTDIR']}" clean$%, results
- refute_match %r%"DESTDIR=#{ENV['DESTDIR']}"$%, results
- refute_match %r%"DESTDIR=#{ENV['DESTDIR']}" install$%, results
- end
+ assert_match %r%"DESTDIR=#{ENV['DESTDIR']}" clean$%, results
+ assert_match %r%"DESTDIR=#{ENV['DESTDIR']}"$%, results
+ assert_match %r%"DESTDIR=#{ENV['DESTDIR']}" install$%, results
end
def test_build_extensions
@@ -107,7 +95,7 @@ install:
extconf_rb = File.join ext_dir, 'extconf.rb'
- open extconf_rb, 'w' do |f|
+ File.open extconf_rb, 'w' do |f|
f.write <<-'RUBY'
require 'mkmf'
@@ -134,11 +122,6 @@ install:
end
def test_build_extensions_with_gemhome_with_space
- # Details: https://github.com/rubygems/rubygems/issues/977#issuecomment-171544940
- if win_platform? && RUBY_VERSION <= '2.0'
- skip 'gemhome with spaces does not work with Ruby 1.9.x on Windows'
- end
-
new_gemhome = File.join @tempdir, 'gem home'
File.rename(@gemhome, new_gemhome)
@gemhome = new_gemhome
@@ -168,7 +151,7 @@ install:
extconf_rb = File.join ext_dir, 'extconf.rb'
- open extconf_rb, 'w' do |f|
+ File.open extconf_rb, 'w' do |f|
f.write <<-'RUBY'
require 'mkmf'
@@ -227,6 +210,8 @@ install:
end
def test_build_extensions_extconf_bad
+ cwd = Dir.pwd
+
@spec.extensions << 'extconf.rb'
FileUtils.mkdir_p @spec.gem_dir
@@ -239,7 +224,7 @@ install:
assert_match(/\AERROR: Failed to build gem native extension.$/, e.message)
- assert_equal "Building native extensions. This could take a while...\n",
+ assert_equal "Building native extensions. This could take a while...\n",
@ui.output
assert_equal '', @ui.error
@@ -257,6 +242,8 @@ install:
assert_match %r%#{Regexp.escape Gem.ruby}: No such file%,
File.read(gem_make_out)
+
+ assert_equal cwd, Dir.pwd
end
def test_build_extensions_unsupported
@@ -272,7 +259,7 @@ install:
assert_match(/^\s*No builder for extension ''$/, e.message)
- assert_equal "Building native extensions. This could take a while...\n",
+ assert_equal "Building native extensions. This could take a while...\n",
@ui.output
assert_equal '', @ui.error
@@ -290,7 +277,7 @@ install:
FileUtils.mkdir_p @spec.gem_dir
- open File.join(@spec.gem_dir, "extconf.rb"), "w" do |f|
+ File.open File.join(@spec.gem_dir, "extconf.rb"), "w" do |f|
f.write <<-'RUBY'
puts "IN EXTCONF"
extconf_args = File.join File.dirname(__FILE__), 'extconf_args'
@@ -323,7 +310,7 @@ install:
build_info_file = File.join build_info_dir, "#{@spec.full_name}.info"
- open build_info_file, 'w' do |io|
+ File.open build_info_file, 'w' do |io|
io.puts '--with-foo-dir=/nonexistent'
end
diff --git a/test/rubygems/test_gem_ext_cmake_builder.rb b/test/rubygems/test_gem_ext_cmake_builder.rb
index 2093c7da55..6e62908090 100644
--- a/test/rubygems/test_gem_ext_cmake_builder.rb
+++ b/test/rubygems/test_gem_ext_cmake_builder.rb
@@ -10,7 +10,7 @@ class TestGemExtCmakeBuilder < Gem::TestCase
# Details: https://github.com/rubygems/rubygems/issues/1270#issuecomment-177368340
skip "CmakeBuilder doesn't work on Windows." if Gem.win_platform?
- `cmake #{Gem::Ext::Builder.redirector}`
+ system('cmake', out: IO::NULL, err: [:child, :out])
skip 'cmake not present' unless $?.success?
@@ -25,6 +25,7 @@ class TestGemExtCmakeBuilder < Gem::TestCase
File.open File.join(@ext, 'CMakeLists.txt'), 'w' do |cmakelists|
cmakelists.write <<-eo_cmake
cmake_minimum_required(VERSION 2.6)
+project(self_build LANGUAGES NONE)
install (FILES test.txt DESTINATION bin)
eo_cmake
end
@@ -34,7 +35,7 @@ install (FILES test.txt DESTINATION bin)
output = []
Dir.chdir @ext do
- Gem::Ext::CmakeBuilder.build nil, nil, @dest_path, output
+ Gem::Ext::CmakeBuilder.build nil, @dest_path, output
end
output = output.join "\n"
@@ -52,7 +53,7 @@ install (FILES test.txt DESTINATION bin)
error = assert_raises Gem::InstallError do
Dir.chdir @ext do
- Gem::Ext::CmakeBuilder.build nil, nil, @dest_path, output
+ Gem::Ext::CmakeBuilder.build nil, @dest_path, output
end
end
@@ -75,7 +76,7 @@ install (FILES test.txt DESTINATION bin)
output = []
Dir.chdir @ext do
- Gem::Ext::CmakeBuilder.build nil, nil, @dest_path, output
+ Gem::Ext::CmakeBuilder.build nil, @dest_path, output
end
output = output.join "\n"
diff --git a/test/rubygems/test_gem_ext_configure_builder.rb b/test/rubygems/test_gem_ext_configure_builder.rb
index 6fd28770d3..967458170b 100644
--- a/test/rubygems/test_gem_ext_configure_builder.rb
+++ b/test/rubygems/test_gem_ext_configure_builder.rb
@@ -27,7 +27,7 @@ class TestGemExtConfigureBuilder < Gem::TestCase
output = []
Dir.chdir @ext do
- Gem::Ext::ConfigureBuilder.build nil, nil, @dest_path, output
+ Gem::Ext::ConfigureBuilder.build nil, @dest_path, output
end
assert_match(/^current directory:/, output.shift)
@@ -50,11 +50,11 @@ class TestGemExtConfigureBuilder < Gem::TestCase
error = assert_raises Gem::InstallError do
Dir.chdir @ext do
- Gem::Ext::ConfigureBuilder.build nil, nil, @dest_path, output
+ Gem::Ext::ConfigureBuilder.build nil, @dest_path, output
end
end
- shell_error_msg = %r{(\./configure: .*)|((?:Can't|cannot) open \./configure(?:: No such file or directory)?)}
+ shell_error_msg = %r{(\./configure: .*)|((?:[Cc]an't|cannot) open '?\./configure'?(?:: No such file or directory)?)}
sh_prefix_configure = "sh ./configure --prefix="
assert_match 'configure failed', error.message
@@ -76,7 +76,7 @@ class TestGemExtConfigureBuilder < Gem::TestCase
output = []
Dir.chdir @ext do
- Gem::Ext::ConfigureBuilder.build nil, nil, @dest_path, output
+ Gem::Ext::ConfigureBuilder.build nil, @dest_path, output
end
assert_contains_make_command 'clean', output[1]
diff --git a/test/rubygems/test_gem_ext_ext_conf_builder.rb b/test/rubygems/test_gem_ext_ext_conf_builder.rb
index f5d10fae50..6decb29a99 100644
--- a/test/rubygems/test_gem_ext_ext_conf_builder.rb
+++ b/test/rubygems/test_gem_ext_ext_conf_builder.rb
@@ -29,7 +29,7 @@ class TestGemExtExtConfBuilder < Gem::TestCase
Dir.chdir @ext do
result =
- Gem::Ext::ExtConfBuilder.build 'extconf.rb', nil, @dest_path, output
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output
assert_same result, output
end
@@ -54,7 +54,7 @@ class TestGemExtExtConfBuilder < Gem::TestCase
output = []
Dir.chdir @ext do
- Gem::Ext::ExtConfBuilder.build 'extconf.rb', nil, @dest_path, output
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output
end
assert_equal "creating Makefile\n", output[2]
@@ -77,7 +77,7 @@ class TestGemExtExtConfBuilder < Gem::TestCase
assert_raises Gem::InstallError do
Dir.chdir @ext do
- Gem::Ext::ExtConfBuilder.build 'extconf.rb', nil, @dest_path, output
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output
end
end
@@ -103,7 +103,7 @@ class TestGemExtExtConfBuilder < Gem::TestCase
error = assert_raises Gem::InstallError do
Dir.chdir @ext do
- Gem::Ext::ExtConfBuilder.build 'extconf.rb', nil, @dest_path, output
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output
end
end
@@ -111,6 +111,29 @@ class TestGemExtExtConfBuilder < Gem::TestCase
assert_match(/^#{Gem.ruby}.* extconf.rb/, output[1])
assert_match(File.join(@dest_path, 'mkmf.log'), output[4])
+ assert_includes(output, "To see why this extension failed to compile, please check the mkmf.log which can be found here:\n")
+
+ assert_path_exists File.join @dest_path, 'mkmf.log'
+ end
+
+ def test_class_build_extconf_success_without_warning
+ if vc_windows? && !nmake_found?
+ skip("test_class_build_extconf_fail skipped - nmake not found")
+ end
+
+ File.open File.join(@ext, 'extconf.rb'), 'w' do |extconf|
+ extconf.puts "require 'mkmf'"
+ extconf.puts "File.open('mkmf.log', 'w'){|f| f.write('a')}"
+ extconf.puts "create_makefile 'foo'"
+ end
+
+ output = []
+
+ Dir.chdir @ext do
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output
+ end
+
+ refute_includes(output, "To see why this extension failed to compile, please check the mkmf.log which can be found here:\n")
assert_path_exists File.join @dest_path, 'mkmf.log'
end
@@ -149,7 +172,7 @@ end
output = []
Dir.chdir @ext do
- Gem::Ext::ExtConfBuilder.build 'extconf.rb', nil, @dest_path, output
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output
end
assert_contains_make_command 'clean', output[4]
@@ -193,14 +216,14 @@ end
assert_equal 'Makefile not found', error.message
end
- def configure_args args = nil
+ def configure_args(args = nil)
configure_args = RbConfig::CONFIG['configure_args']
RbConfig::CONFIG['configure_args'] = args if args
yield
ensure
- if configure_args then
+ if configure_args
RbConfig::CONFIG['configure_args'] = configure_args
else
RbConfig::CONFIG.delete 'configure_args'
@@ -208,4 +231,3 @@ end
end
end
-
diff --git a/test/rubygems/test_gem_ext_rake_builder.rb b/test/rubygems/test_gem_ext_rake_builder.rb
index b728f8f30a..6b647293a8 100644
--- a/test/rubygems/test_gem_ext_rake_builder.rb
+++ b/test/rubygems/test_gem_ext_rake_builder.rb
@@ -14,46 +14,66 @@ class TestGemExtRakeBuilder < Gem::TestCase
end
def test_class_build
- File.open File.join(@ext, 'mkrf_conf.rb'), 'w' do |mkrf_conf|
- mkrf_conf.puts <<-EO_MKRF
- File.open("Rakefile","w") do |f|
- f.puts "task :default"
- end
- EO_MKRF
+ create_temp_mkrf_file('task :default')
+ output = []
+
+ build_rake_in do |rake|
+ Dir.chdir @ext do
+ Gem::Ext::RakeBuilder.build 'mkrf_conf.rb', @dest_path, output
+ end
+
+ output = output.join "\n"
+
+ refute_match %r%^rake failed:%, output
+ assert_match %r%^#{Regexp.escape @@ruby} mkrf_conf\.rb%, output
+ assert_match %r%^#{Regexp.escape rake} RUBYARCHDIR\\=#{Regexp.escape @dest_path} RUBYLIBDIR\\=#{Regexp.escape @dest_path}%, output
end
+ end
+ # https://github.com/rubygems/rubygems/pull/1819
+ #
+ # It should not fail with a non-empty args list either
+ def test_class_build_with_args
+ create_temp_mkrf_file('task :default')
output = []
- realdir = nil # HACK /tmp vs. /private/tmp
build_rake_in do |rake|
Dir.chdir @ext do
- realdir = Dir.pwd
- Gem::Ext::RakeBuilder.build 'mkrf_conf.rb', nil, @dest_path, output
+ non_empty_args_list = ['']
+ Gem::Ext::RakeBuilder.build 'mkrf_conf.rb', @dest_path, output, non_empty_args_list
end
output = output.join "\n"
refute_match %r%^rake failed:%, output
assert_match %r%^#{Regexp.escape @@ruby} mkrf_conf\.rb%, output
- assert_match %r%^#{Regexp.escape rake} RUBYARCHDIR=#{Regexp.escape @dest_path} RUBYLIBDIR=#{Regexp.escape @dest_path}%, output
+ assert_match %r%^#{Regexp.escape rake} RUBYARCHDIR\\=#{Regexp.escape @dest_path} RUBYLIBDIR\\=#{Regexp.escape @dest_path}%, output
end
end
- def test_class_build_fail
- File.open File.join(@ext, 'mkrf_conf.rb'), 'w' do |mkrf_conf|
- mkrf_conf.puts <<-EO_MKRF
- File.open("Rakefile","w") do |f|
- f.puts "task :default do abort 'fail' end"
- end
- EO_MKRF
+ def test_class_build_no_mkrf_passes_args
+ output = []
+
+ build_rake_in do |rake|
+ Dir.chdir @ext do
+ Gem::Ext::RakeBuilder.build "ext/Rakefile", @dest_path, output, ["test1", "test2"]
+ end
+
+ output = output.join "\n"
+
+ refute_match %r%^rake failed:%, output
+ assert_match %r%^#{Regexp.escape rake} RUBYARCHDIR\\=#{Regexp.escape @dest_path} RUBYLIBDIR\\=#{Regexp.escape @dest_path} test1 test2%, output
end
+ end
+ def test_class_build_fail
+ create_temp_mkrf_file("task :default do abort 'fail' end")
output = []
build_rake_in(false) do |rake|
error = assert_raises Gem::InstallError do
Dir.chdir @ext do
- Gem::Ext::RakeBuilder.build "mkrf_conf.rb", nil, @dest_path, output
+ Gem::Ext::RakeBuilder.build "mkrf_conf.rb", @dest_path, output
end
end
@@ -61,5 +81,13 @@ class TestGemExtRakeBuilder < Gem::TestCase
end
end
+ def create_temp_mkrf_file(rakefile_content)
+ File.open File.join(@ext, 'mkrf_conf.rb'), 'w' do |mkrf_conf|
+ mkrf_conf.puts <<-EO_MKRF
+ File.open("Rakefile","w") do |f|
+ f.puts "#{rakefile_content}"
+ end
+ EO_MKRF
+ end
+ end
end
-
diff --git a/test/rubygems/test_gem_gem_runner.rb b/test/rubygems/test_gem_gem_runner.rb
index d68ac4da81..7c771de9e5 100644
--- a/test/rubygems/test_gem_gem_runner.rb
+++ b/test/rubygems/test_gem_gem_runner.rb
@@ -66,4 +66,3 @@ class TestGemGemRunner < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_gemcutter_utilities.rb b/test/rubygems/test_gem_gemcutter_utilities.rb
index c3f9a7ea18..4315090a3d 100644
--- a/test/rubygems/test_gem_gemcutter_utilities.rb
+++ b/test/rubygems/test_gem_gemcutter_utilities.rb
@@ -8,6 +8,8 @@ class TestGemGemcutterUtilities < Gem::TestCase
def setup
super
+ # below needed for random testing, class property
+ Gem.configuration.disable_default_gem_server = nil
ENV['RUBYGEMS_HOST'] = nil
Gem.configuration.rubygems_api_key = nil
@@ -31,7 +33,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
- open Gem.configuration.credentials_path, 'w' do |f|
+ File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
end
@@ -46,7 +48,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
keys = { :rubygems_api_key => 'KEY' }
FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
- open Gem.configuration.credentials_path, 'w' do |f|
+ File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
end
@@ -59,7 +61,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
keys = { :rubygems_api_key => 'KEY', :other => 'OTHER' }
FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
- open Gem.configuration.credentials_path, 'w' do |f|
+ File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
end
@@ -163,7 +165,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
other_api_key = 'f46dbb18bb6a9c97cdc61b5b85c186a17403cdcbf'
FileUtils.mkdir_p File.dirname(Gem.configuration.credentials_path)
- open Gem.configuration.credentials_path, 'w' do |f|
+ File.open Gem.configuration.credentials_path, 'w' do |f|
f.write Hash[:other_api_key, other_api_key].to_yaml
end
util_sign_in [api_key, 200, 'OK']
@@ -177,8 +179,6 @@ class TestGemGemcutterUtilities < Gem::TestCase
end
def test_sign_in_with_bad_credentials
- skip 'Always uses $stdin on windows' if Gem.win_platform?
-
assert_raises Gem::MockGemUi::TermError do
util_sign_in ['Access Denied.', 403, 'Forbidden']
end
@@ -187,9 +187,35 @@ class TestGemGemcutterUtilities < Gem::TestCase
assert_match %r{Access Denied.}, @sign_in_ui.output
end
- def util_sign_in response, host = nil, args = []
- skip 'Always uses $stdin on windows' if Gem.win_platform?
+ def test_sign_in_with_correct_otp_code
+ api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
+ response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
+
+ util_sign_in(proc do
+ @call_count ||= 0
+ (@call_count += 1).odd? ? [response_fail, 401, 'Unauthorized'] : [api_key, 200, 'OK']
+ end, nil, [], "111111\n")
+
+ assert_match 'You have enabled multi-factor authentication. Please enter OTP code.', @sign_in_ui.output
+ assert_match 'Code: ', @sign_in_ui.output
+ assert_match 'Signed in.', @sign_in_ui.output
+ assert_equal '111111', @fetcher.last_request['OTP']
+ end
+
+ def test_sign_in_with_incorrect_otp_code
+ response = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
+
+ assert_raises Gem::MockGemUi::TermError do
+ util_sign_in [response, 401, 'Unauthorized'], nil, [], "111111\n"
+ end
+
+ assert_match 'You have enabled multi-factor authentication. Please enter OTP code.', @sign_in_ui.output
+ assert_match 'Code: ', @sign_in_ui.output
+ assert_match response, @sign_in_ui.output
+ assert_equal '111111', @fetcher.last_request['OTP']
+ end
+ def util_sign_in(response, host = nil, args = [], extra_input = '')
email = 'you@example.com'
password = 'secret'
@@ -203,10 +229,10 @@ class TestGemGemcutterUtilities < Gem::TestCase
@fetcher.data["#{host}/api/v1/api_key"] = response
Gem::RemoteFetcher.fetcher = @fetcher
- @sign_in_ui = Gem::MockGemUi.new "#{email}\n#{password}\n"
+ @sign_in_ui = Gem::MockGemUi.new("#{email}\n#{password}\n" + extra_input)
use_ui @sign_in_ui do
- if args.length > 0 then
+ if args.length > 0
@cmd.sign_in(*args)
else
@cmd.sign_in
diff --git a/test/rubygems/test_gem_impossible_dependencies_error.rb b/test/rubygems/test_gem_impossible_dependencies_error.rb
index 027c99a9e5..8a0f8d6196 100644
--- a/test/rubygems/test_gem_impossible_dependencies_error.rb
+++ b/test/rubygems/test_gem_impossible_dependencies_error.rb
@@ -59,4 +59,3 @@ rye-0.9.8 requires net-ssh (>= 2.0.13) but it conflicted:
end
end
-
diff --git a/test/rubygems/test_gem_indexer.rb b/test/rubygems/test_gem_indexer.rb
index a4a966e8de..9f27744d48 100644
--- a/test/rubygems/test_gem_indexer.rb
+++ b/test/rubygems/test_gem_indexer.rb
@@ -2,7 +2,7 @@
require 'rubygems/test_case'
require 'rubygems/indexer'
-unless defined?(Builder::XChar) then
+unless defined?(Builder::XChar)
warn "Gem::Indexer tests are being skipped. Install builder gem." if $VERBOSE
end
@@ -39,8 +39,7 @@ class TestGemIndexer < Gem::TestCase
def test_initialize
assert_equal @tempdir, @indexer.dest_directory
- assert_equal File.join(Dir.tmpdir, "gem_generate_index_#{$$}"),
- @indexer.directory
+ assert_match %r{#{Dir.mktmpdir('gem_generate_index').match(/.*-/)}}, @indexer.directory
indexer = Gem::Indexer.new @tempdir
assert indexer.build_modern
@@ -364,4 +363,3 @@ class TestGemIndexer < Gem::TestCase
end
end if defined?(Builder::XChar)
-
diff --git a/test/rubygems/test_gem_install_update_options.rb b/test/rubygems/test_gem_install_update_options.rb
index e25259f4d1..65eaf7275f 100644
--- a/test/rubygems/test_gem_install_update_options.rb
+++ b/test/rubygems/test_gem_install_update_options.rb
@@ -21,8 +21,7 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
--build-root build_root
--format-exec
--ignore-dependencies
- --rdoc
- --ri
+ --document
-E
-f
-i /install_to
@@ -92,24 +91,6 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
assert_equal %w[ri], @cmd.options[:document]
end
- def test_rdoc
- @cmd.handle_options %w[--rdoc]
-
- assert_equal %w[rdoc ri], @cmd.options[:document].sort
- end
-
- def test_rdoc_no
- @cmd.handle_options %w[--no-rdoc]
-
- assert_equal %w[ri], @cmd.options[:document]
- end
-
- def test_ri
- @cmd.handle_options %w[--no-ri]
-
- assert_equal %w[], @cmd.options[:document]
- end
-
def test_security_policy
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
@@ -119,11 +100,14 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
end
def test_security_policy_unknown
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
@cmd.add_install_update_options
- assert_raises OptionParser::InvalidArgument do
+ e = assert_raises OptionParser::InvalidArgument do
@cmd.handle_options %w[-P UnknownSecurity]
end
+ assert_includes e.message, "UnknownSecurity"
end
def test_user_install_enabled
@@ -140,6 +124,8 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
def test_user_install_disabled_read_only
if win_platform?
skip('test_user_install_disabled_read_only test skipped on MS Windows')
+ elsif Process.uid.zero?
+ skip('test_user_install_disabled_read_only test skipped in root privilege')
else
@cmd.handle_options %w[--no-user-install]
diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb
index 6ceb2c6dfc..e32cccaf6d 100644
--- a/test/rubygems/test_gem_installer.rb
+++ b/test/rubygems/test_gem_installer.rb
@@ -21,7 +21,7 @@ class TestGemInstaller < Gem::InstallerTestCase
super
common_installer_setup
- if __name__ =~ /^test_install(_|$)/ then
+ if (self.class.method_defined?(:__name__) ? __name__ : name) =~ /\Atest_install(_|\Z)/
FileUtils.rm_r @spec.gem_dir
FileUtils.rm_r @user_spec.gem_dir
end
@@ -34,7 +34,7 @@ class TestGemInstaller < Gem::InstallerTestCase
super
- Gem.configuration = @config
+ Gem.configuration = instance_variable_defined?(:@config) ? @config : nil
end
def test_app_script_text
@@ -53,16 +53,21 @@ require 'rubygems'
version = \">= 0.a\"
-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
+str = ARGV.first
+if str
+ str = str.b[/\\A_(.*)_\\z/, 1]
+ if str and Gem::Version.correct?(str)
+ version = str
ARGV.shift
end
end
+if Gem.respond_to?(:activate_bin_path)
load Gem.activate_bin_path('a', 'executable', version)
+else
+gem "a", version
+load Gem.bin_path("a", "executable", version)
+end
EOF
wrapper = @installer.app_script_text 'executable'
@@ -82,7 +87,7 @@ load Gem.activate_bin_path('a', 'executable', version)
end
util_make_exec
- @installer.gem_dir = util_gem_dir @spec
+ @installer.gem_dir = @spec.gem_dir
@installer.wrappers = true
@installer.generate_bin
@@ -116,7 +121,7 @@ load Gem.activate_bin_path('a', 'executable', version)
ensure
Object.const_set :RUBY_FRAMEWORK_VERSION, orig_RUBY_FRAMEWORK_VERSION if
orig_RUBY_FRAMEWORK_VERSION
- if orig_bindir then
+ if orig_bindir
RbConfig::CONFIG['bindir'] = orig_bindir
else
RbConfig::CONFIG.delete 'bindir'
@@ -135,8 +140,8 @@ load Gem.activate_bin_path('a', 'executable', version)
s.require_path = 'lib'
end
- open File.join(util_inst_bindir, 'executable'), 'w' do |io|
- io.write <<-EXEC
+ File.open File.join(util_inst_bindir, 'executable'), 'w' do |io|
+ io.write <<-EXEC
#!/usr/local/bin/ruby
#
# This file was generated by RubyGems
@@ -245,7 +250,7 @@ gem 'other', version
expected = @installer.bin_dir
- if Gem.win_platform? then
+ if Gem.win_platform?
expected = expected.downcase.gsub(File::SEPARATOR, File::ALT_SEPARATOR)
end
@@ -299,7 +304,7 @@ gem 'other', version
def test_extract_files
@installer.extract_files
- assert_path_exists File.join util_gem_dir, 'bin/executable'
+ assert_path_exists File.join @spec.gem_dir, 'bin/executable'
end
def test_generate_bin_bindir
@@ -309,12 +314,12 @@ gem 'other', version
@spec.bindir = '.'
exec_file = @installer.formatted_program_filename 'executable'
- exec_path = File.join util_gem_dir(@spec), exec_file
+ exec_path = File.join @spec.gem_dir, exec_file
File.open exec_path, 'w' do |f|
f.puts '#!/usr/bin/ruby'
end
- @installer.gem_dir = util_gem_dir
+ @installer.gem_dir = @spec.gem_dir
@installer.generate_bin
@@ -331,6 +336,9 @@ gem 'other', version
bin_dir = Gem.win_platform? ? File.expand_path(ENV["WINDIR"]).upcase :
"/usr/bin"
+ old_path = ENV["PATH"]
+ ENV["PATH"] = [ENV["PATH"], bin_dir].compact.join(File::PATH_SEPARATOR)
+
options = {
:bin_dir => bin_dir,
:install_dir => "/non/existent"
@@ -345,12 +353,15 @@ gem 'other', version
end
assert_equal "", @ui.error
+
+ ensure
+ ENV["PATH"] = old_path
end
def test_generate_bin_script
@installer.wrappers = true
util_make_exec
- @installer.gem_dir = util_gem_dir
+ @installer.gem_dir = @spec.gem_dir
@installer.generate_bin
assert File.directory? util_inst_bindir
@@ -366,7 +377,7 @@ gem 'other', version
@installer.format_executable = true
@installer.wrappers = true
util_make_exec
- @installer.gem_dir = util_gem_dir
+ @installer.gem_dir = @spec.gem_dir
Gem::Installer.exec_format = 'foo-%s-bar'
@installer.generate_bin
@@ -380,7 +391,7 @@ gem 'other', version
def test_generate_bin_script_format_disabled
@installer.wrappers = true
util_make_exec
- @installer.gem_dir = util_gem_dir
+ @installer.gem_dir = @spec.gem_dir
Gem::Installer.exec_format = 'foo-%s-bar'
@installer.generate_bin
@@ -432,6 +443,8 @@ gem 'other', version
if win_platform?
skip('test_generate_bin_script_no_perms skipped on MS Windows')
+ elsif Process.uid.zero?
+ skip('test_generate_bin_script_no_perms skipped in root privilege')
else
FileUtils.chmod 0000, util_inst_bindir
@@ -469,13 +482,13 @@ gem 'other', version
def test_generate_bin_script_wrappers
@installer.wrappers = true
util_make_exec
- @installer.gem_dir = util_gem_dir
+ @installer.gem_dir = @spec.gem_dir
installed_exec = File.join(util_inst_bindir, 'executable')
- real_exec = File.join util_gem_dir, 'bin', 'executable'
+ real_exec = File.join @spec.gem_dir, 'bin', 'executable'
# fake --no-wrappers for previous install
- unless Gem.win_platform? then
+ unless Gem.win_platform?
FileUtils.mkdir_p File.dirname(installed_exec)
FileUtils.ln_s real_exec, installed_exec
end
@@ -496,13 +509,13 @@ gem 'other', version
@installer.wrappers = false
util_make_exec
- @installer.gem_dir = util_gem_dir
+ @installer.gem_dir = @spec.gem_dir
@installer.generate_bin
assert_equal true, File.directory?(util_inst_bindir)
installed_exec = File.join util_inst_bindir, 'executable'
assert_equal true, File.symlink?(installed_exec)
- assert_equal(File.join(util_gem_dir, 'bin', 'executable'),
+ assert_equal(File.join(@spec.gem_dir, 'bin', 'executable'),
File.readlink(installed_exec))
end
@@ -518,12 +531,14 @@ gem 'other', version
def test_generate_bin_symlink_no_perms
@installer.wrappers = false
util_make_exec
- @installer.gem_dir = util_gem_dir
+ @installer.gem_dir = @spec.gem_dir
Dir.mkdir util_inst_bindir
if win_platform?
skip('test_generate_bin_symlink_no_perms skipped on MS Windows')
+ elsif Process.uid.zero?
+ skip('test_user_install_disabled_read_only test skipped in root privilege')
else
FileUtils.chmod 0000, util_inst_bindir
@@ -540,11 +555,11 @@ gem 'other', version
@installer.wrappers = false
util_make_exec
- @installer.gem_dir = util_gem_dir
+ @installer.gem_dir = @spec.gem_dir
@installer.generate_bin
installed_exec = File.join(util_inst_bindir, 'executable')
- assert_equal(File.join(util_gem_dir, 'bin', 'executable'),
+ assert_equal(File.join(@spec.gem_dir, 'bin', 'executable'),
File.readlink(installed_exec))
@spec = Gem::Specification.new do |s|
@@ -557,7 +572,7 @@ gem 'other', version
end
util_make_exec
- @installer.gem_dir = util_gem_dir @spec
+ @installer.gem_dir = @spec.gem_dir
@installer.generate_bin
installed_exec = File.join(util_inst_bindir, 'executable')
assert_equal(@spec.bin_file('executable'),
@@ -570,11 +585,11 @@ gem 'other', version
@installer.wrappers = false
util_make_exec
- @installer.gem_dir = util_gem_dir
+ @installer.gem_dir = @spec.gem_dir
@installer.generate_bin
installed_exec = File.join(util_inst_bindir, 'executable')
- assert_equal(File.join(util_gem_dir, 'bin', 'executable'),
+ assert_equal(File.join(@spec.gem_dir, 'bin', 'executable'),
File.readlink(installed_exec))
spec = Gem::Specification.new do |s|
@@ -590,12 +605,12 @@ gem 'other', version
one = @spec.dup
one.version = 1
@installer = Gem::Installer.for_spec spec
- @installer.gem_dir = util_gem_dir one
+ @installer.gem_dir = one.gem_dir
@installer.generate_bin
installed_exec = File.join util_inst_bindir, 'executable'
- expected = File.join util_gem_dir, 'bin', 'executable'
+ expected = File.join @spec.gem_dir, 'bin', 'executable'
assert_equal(expected,
File.readlink(installed_exec),
"Ensure symlink not moved")
@@ -606,7 +621,7 @@ gem 'other', version
@installer.wrappers = true
util_make_exec
- @installer.gem_dir = util_gem_dir
+ @installer.gem_dir = @spec.gem_dir
@installer.generate_bin
@@ -625,7 +640,7 @@ gem 'other', version
util_installer @spec, @gemhome
@installer.wrappers = false
- @installer.gem_dir = util_gem_dir
+ @installer.gem_dir = @spec.gem_dir
@installer.generate_bin
@@ -643,7 +658,7 @@ gem 'other', version
File.const_set(:ALT_SEPARATOR, '\\')
@installer.wrappers = false
util_make_exec
- @installer.gem_dir = util_gem_dir
+ @installer.gem_dir = @spec.gem_dir
use_ui @ui do
@installer.generate_bin
@@ -654,7 +669,7 @@ gem 'other', version
assert_path_exists installed_exec
if symlink_supported?
- assert_send([File, :symlink?, installed_exec])
+ assert File.symlink?(installed_exec)
return
end
@@ -881,8 +896,6 @@ gem 'other', version
end
def test_install_creates_binstub_that_dont_trust_encoding
- skip unless "".respond_to?(:force_encoding)
-
Dir.mkdir util_inst_bindir
util_setup_gem
util_clear_gems
@@ -952,7 +965,7 @@ gem 'other', version
def test_install_force
use_ui @ui do
- installer = Gem::Installer.at old_ruby_required, :force => true
+ installer = Gem::Installer.at old_ruby_required('= 1.4.6'), :force => true
installer.install
end
@@ -1170,88 +1183,47 @@ gem 'other', version
refute_path_exists File.join expected_extension_dir, 'gem_make.out'
end
- # ruby core repository needs to `depend` file for extension build.
- # but 1.9.2 and earlier mkmf.rb does not create TOUCH file like depend.
- if RUBY_VERSION < '1.9.3'
- def test_find_lib_file_after_install
-
- @spec.extensions << "extconf.rb"
- write_file File.join(@tempdir, "extconf.rb") do |io|
- io.write <<-RUBY
- require "mkmf"
- create_makefile("#{@spec.name}")
- RUBY
- end
-
- write_file File.join(@tempdir, "a.c") do |io|
- io.write <<-C
- #include <ruby.h>
- void Init_a() { }
- C
- end
-
- Dir.mkdir File.join(@tempdir, "lib")
- write_file File.join(@tempdir, 'lib', "b.rb") do |io|
- io.write "# b.rb"
- end
-
- @spec.files += %w[extconf.rb lib/b.rb a.c]
-
- use_ui @ui do
- path = Gem::Package.build @spec
+ def test_find_lib_file_after_install
+ @spec.extensions << "extconf.rb"
+ write_file File.join(@tempdir, "extconf.rb") do |io|
+ io.write <<-RUBY
+ require "mkmf"
- installer = Gem::Installer.at path
- installer.install
- end
+ CONFIG['CC'] = '$(TOUCH) $@ ||'
+ CONFIG['LDSHARED'] = '$(TOUCH) $@ ||'
+ $ruby = '#{Gem.ruby}'
- expected = File.join @spec.full_require_paths.find { |path|
- File.exist? File.join path, 'b.rb'
- }, 'b.rb'
- assert_equal expected, @spec.matches_for_glob('b.rb').first
+ create_makefile("#{@spec.name}")
+ RUBY
end
- else
- def test_find_lib_file_after_install
- @spec.extensions << "extconf.rb"
- write_file File.join(@tempdir, "extconf.rb") do |io|
- io.write <<-RUBY
- require "mkmf"
-
- CONFIG['CC'] = '$(TOUCH) $@ ||'
- CONFIG['LDSHARED'] = '$(TOUCH) $@ ||'
- $ruby = '#{Gem.ruby}'
-
- create_makefile("#{@spec.name}")
- RUBY
- end
-
- write_file File.join(@tempdir, "depend")
- write_file File.join(@tempdir, "a.c") do |io|
- io.write <<-C
- #include <ruby.h>
- void Init_a() { }
- C
- end
+ write_file File.join(@tempdir, "depend")
- Dir.mkdir File.join(@tempdir, "lib")
- write_file File.join(@tempdir, 'lib', "b.rb") do |io|
- io.write "# b.rb"
- end
+ write_file File.join(@tempdir, "a.c") do |io|
+ io.write <<-C
+ #include <ruby.h>
+ void Init_a() { }
+ C
+ end
- @spec.files += %w[extconf.rb lib/b.rb depend a.c]
+ Dir.mkdir File.join(@tempdir, "lib")
+ write_file File.join(@tempdir, 'lib', "b.rb") do |io|
+ io.write "# b.rb"
+ end
- use_ui @ui do
- path = Gem::Package.build @spec
+ @spec.files += %w[extconf.rb lib/b.rb depend a.c]
- installer = Gem::Installer.at path
- installer.install
- end
+ use_ui @ui do
+ path = Gem::Package.build @spec
- expected = File.join @spec.full_require_paths.find { |path|
- File.exist? File.join path, 'b.rb'
- }, 'b.rb'
- assert_equal expected, @spec.matches_for_glob('b.rb').first
+ installer = Gem::Installer.at path
+ installer.install
end
+
+ expected = File.join @spec.full_require_paths.find { |path|
+ File.exist? File.join path, 'b.rb'
+ }, 'b.rb'
+ assert_equal expected, @spec.matches_for_glob('b.rb').first
end
def test_install_extension_and_script
@@ -1293,13 +1265,6 @@ gem 'other', version
end
def test_install_extension_flat
- skip '1.9.2 and earlier mkmf.rb does not create TOUCH' if
- RUBY_VERSION < '1.9.3'
-
- if RUBY_VERSION == "1.9.3" and RUBY_PATCHLEVEL <= 194
- skip "TOUCH was introduced into 1.9.3 after p194"
- end
-
@spec.require_paths = ["."]
@spec.extensions << "extconf.rb"
@@ -1415,15 +1380,32 @@ gem 'other', version
def test_pre_install_checks_ruby_version
use_ui @ui do
- installer = Gem::Installer.at old_ruby_required
- e = assert_raises Gem::InstallError do
+ installer = Gem::Installer.at old_ruby_required('= 1.4.6')
+ e = assert_raises Gem::RuntimeRequirementNotMetError do
installer.pre_install_checks
end
- assert_equal 'old_ruby_required requires Ruby version = 1.4.6.',
+ rv = Gem.ruby_version
+ assert_equal "old_ruby_required requires Ruby version = 1.4.6. The current ruby version is #{rv}.",
e.message
end
end
+ def test_pre_install_checks_ruby_version_with_prereleases
+ util_set_RUBY_VERSION '2.6.0', -1, '63539', 'ruby 2.6.0preview2 (2018-05-31 trunk 63539) [x86_64-linux]'
+
+ installer = Gem::Installer.at old_ruby_required('>= 2.6.0.preview2')
+ assert installer.pre_install_checks
+
+ installer = Gem::Installer.at old_ruby_required('> 2.6.0.preview2')
+ e = assert_raises Gem::RuntimeRequirementNotMetError do
+ assert installer.pre_install_checks
+ end
+ assert_equal "old_ruby_required requires Ruby version > 2.6.0.preview2. The current ruby version is 2.6.0.preview2.",
+ e.message
+ ensure
+ util_restore_RUBY_VERSION
+ end
+
def test_pre_install_checks_wrong_rubygems_version
spec = util_spec 'old_rubygems_required', '1' do |s|
s.required_rubygems_version = '< 0'
@@ -1435,14 +1417,143 @@ gem 'other', version
use_ui @ui do
@installer = Gem::Installer.at gem
- e = assert_raises Gem::InstallError do
+ e = assert_raises Gem::RuntimeRequirementNotMetError do
@installer.pre_install_checks
end
- assert_equal 'old_rubygems_required requires RubyGems version < 0. ' +
+ rgv = Gem::VERSION
+ assert_equal "old_rubygems_required requires RubyGems version < 0. The current RubyGems version is #{rgv}. " +
"Try 'gem update --system' to update RubyGems itself.", e.message
end
end
+ def test_pre_install_checks_malicious_name
+ spec = util_spec '../malicious', '1'
+ def spec.full_name # so the spec is buildable
+ "malicious-1"
+ end
+ def spec.validate(packaging, strict); end
+
+ util_build_gem spec
+
+ gem = File.join(@gemhome, 'cache', spec.file_name)
+
+ use_ui @ui do
+ @installer = Gem::Installer.at gem
+ e = assert_raises Gem::InstallError do
+ @installer.pre_install_checks
+ end
+ assert_equal '#<Gem::Specification name=../malicious version=1> has an invalid name', e.message
+ end
+ end
+
+ def test_pre_install_checks_malicious_name_before_eval
+ spec = util_spec "malicious\n::Object.const_set(:FROM_EVAL, true)#", '1'
+ def spec.full_name # so the spec is buildable
+ "malicious-1"
+ end
+ def spec.validate(*args); end
+
+ util_build_gem spec
+
+ gem = File.join(@gemhome, 'cache', spec.file_name)
+
+ use_ui @ui do
+ @installer = Gem::Installer.at gem
+ e = assert_raises Gem::InstallError do
+ @installer.pre_install_checks
+ end
+ assert_equal "#<Gem::Specification name=malicious\n::Object.const_set(:FROM_EVAL, true)# version=1> has an invalid name", e.message
+ end
+ refute defined?(::Object::FROM_EVAL)
+ end
+
+ def test_pre_install_checks_malicious_require_paths_before_eval
+ spec = util_spec "malicious", '1'
+ def spec.full_name # so the spec is buildable
+ "malicious-1"
+ end
+ def spec.validate(*args); end
+ spec.require_paths = ["malicious\n``"]
+
+ util_build_gem spec
+
+ gem = File.join(@gemhome, 'cache', spec.file_name)
+
+ use_ui @ui do
+ @installer = Gem::Installer.at gem
+ e = assert_raises Gem::InstallError do
+ @installer.pre_install_checks
+ end
+ assert_equal "#<Gem::Specification name=malicious version=1> has an invalid require_paths", e.message
+ end
+ end
+
+ def test_pre_install_checks_malicious_extensions_before_eval
+ skip "mswin environment disallow to create file contained the carriage return code." if Gem.win_platform?
+
+ spec = util_spec "malicious", '1'
+ def spec.full_name # so the spec is buildable
+ "malicious-1"
+ end
+ def spec.validate(*args); end
+ spec.extensions = ["malicious\n``"]
+
+ util_build_gem spec
+
+ gem = File.join(@gemhome, 'cache', spec.file_name)
+
+ use_ui @ui do
+ @installer = Gem::Installer.at gem
+ e = assert_raises Gem::InstallError do
+ @installer.pre_install_checks
+ end
+ assert_equal "#<Gem::Specification name=malicious version=1> has an invalid extensions", e.message
+ end
+ end
+
+ def test_pre_install_checks_malicious_specification_version_before_eval
+ spec = util_spec "malicious", '1'
+ def spec.full_name # so the spec is buildable
+ "malicious-1"
+ end
+ def spec.validate(*args); end
+ spec.specification_version = "malicious\n``"
+
+ util_build_gem spec
+
+ gem = File.join(@gemhome, 'cache', spec.file_name)
+
+ use_ui @ui do
+ @installer = Gem::Installer.at gem
+ e = assert_raises Gem::InstallError do
+ @installer.pre_install_checks
+ end
+ assert_equal "#<Gem::Specification name=malicious version=1> has an invalid specification_version", e.message
+ end
+ end
+
+ def test_pre_install_checks_malicious_dependencies_before_eval
+ spec = util_spec "malicious", '1'
+ def spec.full_name # so the spec is buildable
+ "malicious-1"
+ end
+ def spec.validate(*args); end
+ spec.add_dependency "b\nfoo", '> 5'
+
+ util_build_gem spec
+
+ gem = File.join(@gemhome, 'cache', spec.file_name)
+
+ use_ui @ui do
+ @installer = Gem::Installer.at gem
+ @installer.ignore_dependencies = true
+ e = assert_raises Gem::InstallError do
+ @installer.pre_install_checks
+ end
+ assert_equal "#<Gem::Specification name=malicious version=1> has an invalid dependencies", e.message
+ end
+ end
+
def test_shebang
util_make_exec @spec, "#!/usr/bin/ruby"
@@ -1714,7 +1825,7 @@ gem 'other', version
@installer.wrappers = true
@installer.options[:install_as_default] = true
- @installer.gem_dir = util_gem_dir @spec
+ @installer.gem_dir = @spec.gem_dir
@installer.generate_bin
use_ui @ui do
@@ -1733,9 +1844,9 @@ gem 'other', version
assert_equal ['bin/executable'], default_spec.files
end
- def old_ruby_required
+ def old_ruby_required(requirement)
spec = util_spec 'old_ruby_required', '1' do |s|
- s.required_ruby_version = '= 1.4.6'
+ s.required_ruby_version = requirement
end
util_build_gem spec
@@ -1750,7 +1861,7 @@ gem 'other', version
@installer = util_installer @spec, @gemhome
end
- def util_conflict_executable wrappers
+ def util_conflict_executable(wrappers)
conflict = quick_gem 'conflict' do |spec|
util_make_exec spec
end
diff --git a/test/rubygems/test_gem_local_remote_options.rb b/test/rubygems/test_gem_local_remote_options.rb
index 272623be74..6c21300c2a 100644
--- a/test/rubygems/test_gem_local_remote_options.rb
+++ b/test/rubygems/test_gem_local_remote_options.rb
@@ -44,8 +44,9 @@ class TestGemLocalRemoteOptions < Gem::TestCase
spec_fetcher
@cmd.add_local_remote_options
+ Gem.configuration.sources = nil
@cmd.handle_options %W[--clear-sources]
- assert_equal Gem.default_sources, Gem.sources
+ assert_equal Gem.default_sources, Gem.sources.to_a
end
def test_local_eh
@@ -123,7 +124,7 @@ class TestGemLocalRemoteOptions < Gem::TestCase
s1 = 'htp://more-gems.example.com'
- assert_raises OptionParser::InvalidArgument do
+ assert_raises ArgumentError do
@cmd.handle_options %W[--source #{s1}]
end
@@ -131,4 +132,3 @@ class TestGemLocalRemoteOptions < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_name_tuple.rb b/test/rubygems/test_gem_name_tuple.rb
index 9bc2924cf6..5331e1cdbb 100644
--- a/test/rubygems/test_gem_name_tuple.rb
+++ b/test/rubygems/test_gem_name_tuple.rb
@@ -42,4 +42,3 @@ class TestGemNameTuple < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_package.rb b/test/rubygems/test_gem_package.rb
index 9d47f0dea4..495c7fd644 100644
--- a/test/rubygems/test_gem_package.rb
+++ b/test/rubygems/test_gem_package.rb
@@ -1,4 +1,4 @@
-# coding: UTF-8
+# coding: utf-8
# frozen_string_literal: true
require 'rubygems/package/tar_test_case'
@@ -24,7 +24,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_class_new_old_format
- open 'old_format.gem', 'wb' do |io|
+ File.open 'old_format.gem', 'wb' do |io|
io.write SIMPLE_GEM
end
@@ -45,7 +45,7 @@ class TestGemPackage < Gem::Package::TarTestCase
FileUtils.mkdir 'lib'
- open 'lib/code.rb', 'w' do |io|
+ File.open 'lib/code.rb', 'w' do |io|
io.write '# lib/code.rb'
end
@@ -84,7 +84,7 @@ class TestGemPackage < Gem::Package::TarTestCase
io.write spec.to_yaml
end
- metadata_sha1 = Digest::SHA1.hexdigest s.string
+ metadata_sha256 = Digest::SHA256.hexdigest s.string
metadata_sha512 = Digest::SHA512.hexdigest s.string
expected = {
@@ -94,24 +94,43 @@ class TestGemPackage < Gem::Package::TarTestCase
}
}
- if defined?(OpenSSL::Digest) then
- expected['SHA1'] = {
- 'metadata.gz' => metadata_sha1,
- 'data.tar.gz' => Digest::SHA1.hexdigest(tar),
+ if defined?(OpenSSL::Digest)
+ expected['SHA256'] = {
+ 'metadata.gz' => metadata_sha256,
+ 'data.tar.gz' => Digest::SHA256.hexdigest(tar),
}
end
assert_equal expected, YAML.load(checksums)
end
+ def test_build_time_source_date_epoch
+ epoch = ENV["SOURCE_DATE_EPOCH"]
+ ENV["SOURCE_DATE_EPOCH"] = "123456789"
+
+ spec = Gem::Specification.new 'build', '1'
+ spec.summary = 'build'
+ spec.authors = 'build'
+ spec.files = ['lib/code.rb']
+ spec.date = Time.at 0
+ spec.rubygems_version = Gem::Version.new '0'
+
+
+ package = Gem::Package.new spec.file_name
+
+ assert_equal Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc, package.build_time
+ ensure
+ ENV["SOURCE_DATE_EPOCH"] = epoch
+ end
+
def test_add_files
spec = Gem::Specification.new
spec.files = %w[lib/code.rb lib/empty]
FileUtils.mkdir_p 'lib/empty'
- open 'lib/code.rb', 'w' do |io| io.write '# lib/code.rb' end
- open 'lib/extra.rb', 'w' do |io| io.write '# lib/extra.rb' end
+ File.open 'lib/code.rb', 'w' do |io| io.write '# lib/code.rb' end
+ File.open 'lib/extra.rb', 'w' do |io| io.write '# lib/extra.rb' end
package = Gem::Package.new 'bogus.gem'
package.spec = spec
@@ -134,16 +153,23 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_add_files_symlink
- skip 'symlink not supported' if Gem.win_platform?
-
spec = Gem::Specification.new
- spec.files = %w[lib/code.rb lib/code_sym.rb]
+ spec.files = %w[lib/code.rb lib/code_sym.rb lib/code_sym2.rb]
FileUtils.mkdir_p 'lib'
- open 'lib/code.rb', 'w' do |io| io.write '# lib/code.rb' end
+ File.open 'lib/code.rb', 'w' do |io| io.write '# lib/code.rb' end
# NOTE: 'code.rb' is correct, because it's relative to lib/code_sym.rb
- File.symlink('code.rb', 'lib/code_sym.rb')
+ begin
+ File.symlink('code.rb', 'lib/code_sym.rb')
+ File.symlink('../lib/code.rb', 'lib/code_sym2.rb')
+ rescue Errno::EACCES => e
+ if win_platform?
+ skip "symlink - must be admin with no UAC on Windows"
+ else
+ raise e
+ end
+ end
package = Gem::Package.new 'bogus.gem'
package.spec = spec
@@ -167,7 +193,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
assert_equal %w[lib/code.rb], files
- assert_equal [{'lib/code_sym.rb' => 'lib/code.rb'}], symlinks
+ assert_equal [{'lib/code_sym.rb' => 'lib/code.rb'}, {'lib/code_sym2.rb' => '../lib/code.rb'}], symlinks
end
def test_build
@@ -179,7 +205,7 @@ class TestGemPackage < Gem::Package::TarTestCase
FileUtils.mkdir 'lib'
- open 'lib/code.rb', 'w' do |io|
+ File.open 'lib/code.rb', 'w' do |io|
io.write '# lib/code.rb'
end
@@ -218,7 +244,7 @@ class TestGemPackage < Gem::Package::TarTestCase
FileUtils.mkdir 'lib'
- open 'lib/code.rb', 'w' do |io|
+ File.open 'lib/code.rb', 'w' do |io|
io.write '# lib/code.rb'
end
@@ -261,7 +287,7 @@ class TestGemPackage < Gem::Package::TarTestCase
FileUtils.mkdir 'lib'
- open 'lib/code.rb', 'w' do |io|
+ File.open 'lib/code.rb', 'w' do |io|
io.write '# lib/code.rb'
end
@@ -299,6 +325,19 @@ class TestGemPackage < Gem::Package::TarTestCase
assert_equal 'missing value for attribute summary', e.message
end
+ def test_build_invalid_arguments
+ spec = Gem::Specification.new 'build', '1'
+
+ package = Gem::Package.new spec.file_name
+ package.spec = spec
+
+ e = assert_raises ArgumentError do
+ package.build true, true
+ end
+
+ assert_equal "skip_validation = true and strict_validation = true are incompatible", e.message
+ end
+
def test_build_signed
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
@@ -311,7 +350,7 @@ class TestGemPackage < Gem::Package::TarTestCase
FileUtils.mkdir 'lib'
- open 'lib/code.rb', 'w' do |io|
+ File.open 'lib/code.rb', 'w' do |io|
io.write '# lib/code.rb'
end
@@ -348,7 +387,7 @@ class TestGemPackage < Gem::Package::TarTestCase
FileUtils.mkdir 'lib'
- open 'lib/code.rb', 'w' do |io|
+ File.open 'lib/code.rb', 'w' do |io|
io.write '# lib/code.rb'
end
@@ -408,7 +447,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
end
- open 'empty.gem', 'wb' do |io|
+ File.open 'empty.gem', 'wb' do |io|
io.write gem.string
end
@@ -435,8 +474,6 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_extract_tar_gz_symlink_relative_path
- skip 'symlink not supported' if Gem.win_platform?
-
package = Gem::Package.new @gem
tgz_io = util_tar_gz do |tar|
@@ -445,7 +482,15 @@ class TestGemPackage < Gem::Package::TarTestCase
tar.add_symlink 'lib/foo.rb', '../relative.rb', 0644
end
- package.extract_tar_gz tgz_io, @destination
+ begin
+ package.extract_tar_gz tgz_io, @destination
+ rescue Errno::EACCES => e
+ if win_platform?
+ skip "symlink - must be admin with no UAC on Windows"
+ else
+ raise e
+ end
+ end
extracted = File.join @destination, 'lib/foo.rb'
assert_path_exists extracted
@@ -455,6 +500,69 @@ class TestGemPackage < Gem::Package::TarTestCase
File.read(extracted)
end
+ def test_extract_symlink_parent
+ package = Gem::Package.new @gem
+
+ tgz_io = util_tar_gz do |tar|
+ tar.mkdir 'lib', 0755
+ tar.add_symlink 'lib/link', '../..', 0644
+ tar.add_file 'lib/link/outside.txt', 0644 do |io| io.write 'hi' end
+ end
+
+ # Extract into a subdirectory of @destination; if this test fails it writes
+ # a file outside destination_subdir, but we want the file to remain inside
+ # @destination so it will be cleaned up.
+ destination_subdir = File.join @destination, 'subdir'
+ FileUtils.mkdir_p destination_subdir
+
+ e = assert_raises(Gem::Package::PathError, Errno::EACCES) do
+ package.extract_tar_gz tgz_io, destination_subdir
+ end
+
+ if Gem::Package::PathError === e
+ assert_equal("installing into parent path lib/link/outside.txt of " +
+ "#{destination_subdir} is not allowed", e.message)
+ elsif win_platform?
+ skip "symlink - must be admin with no UAC on Windows"
+ else
+ raise e
+ end
+ end
+
+ def test_extract_symlink_parent_doesnt_delete_user_dir
+ package = Gem::Package.new @gem
+
+ # Extract into a subdirectory of @destination; if this test fails it writes
+ # a file outside destination_subdir, but we want the file to remain inside
+ # @destination so it will be cleaned up.
+ destination_subdir = File.join @destination, 'subdir'
+ FileUtils.mkdir_p destination_subdir
+
+ destination_user_dir = File.join @destination, 'user'
+ destination_user_subdir = File.join destination_user_dir, 'dir'
+ FileUtils.mkdir_p destination_user_subdir
+
+ tgz_io = util_tar_gz do |tar|
+ tar.add_symlink 'link', destination_user_dir, 16877
+ tar.add_symlink 'link/dir', '.', 16877
+ end
+
+ e = assert_raises(Gem::Package::PathError, Errno::EACCES) do
+ package.extract_tar_gz tgz_io, destination_subdir
+ end
+
+ assert_path_exists destination_user_subdir
+
+ if Gem::Package::PathError === e
+ assert_equal("installing into parent path #{destination_user_subdir} of " +
+ "#{destination_subdir} is not allowed", e.message)
+ elsif win_platform?
+ skip "symlink - must be admin with no UAC on Windows"
+ else
+ raise e
+ end
+ end
+
def test_extract_tar_gz_directory
package = Gem::Package.new @gem
@@ -499,6 +607,21 @@ class TestGemPackage < Gem::Package::TarTestCase
assert_path_exists extracted
end
+ if Gem.win_platform?
+ def test_extract_tar_gz_case_insensitive
+ package = Gem::Package.new @gem
+
+ tgz_io = util_tar_gz do |tar|
+ tar.add_file 'foo/file.rb', 0644 do |io| io.write 'hi' end
+ end
+
+ package.extract_tar_gz tgz_io, @destination.upcase
+
+ extracted = File.join @destination, 'foo/file.rb'
+ assert_path_exists extracted
+ end
+ end
+
def test_install_location
package = Gem::Package.new @gem
@@ -539,7 +662,6 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_install_location_extra_slash
- skip 'no File.realpath on 1.8' if RUBY_VERSION < '1.9'
package = Gem::Package.new @gem
file = 'foo//file.rb'.dup
@@ -566,8 +688,23 @@ class TestGemPackage < Gem::Package::TarTestCase
"#{@destination} is not allowed", e.message)
end
+ def test_install_location_suffix
+ package = Gem::Package.new @gem
+
+ filename = "../#{File.basename(@destination)}suffix.rb"
+
+ e = assert_raises Gem::Package::PathError do
+ package.install_location filename, @destination
+ end
+
+ parent = File.expand_path File.join @destination, filename
+
+ assert_equal("installing into parent path #{parent} of " +
+ "#{@destination} is not allowed", e.message)
+ end
+
def test_load_spec
- entry = StringIO.new Gem.gzip @spec.to_yaml
+ entry = StringIO.new Gem::Util.gzip @spec.to_yaml
def entry.full_name() 'metadata.gz' end
package = Gem::Package.new 'nonexistent.gem'
@@ -597,7 +734,7 @@ class TestGemPackage < Gem::Package::TarTestCase
data_tgz = data_tgz.string
gem = util_tar do |tar|
- metadata_gz = Gem.gzip @spec.to_yaml
+ metadata_gz = Gem::Util.gzip @spec.to_yaml
tar.add_file 'metadata.gz', 0444 do |io|
io.write metadata_gz
@@ -620,7 +757,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
end
- open 'mismatch.gem', 'wb' do |io|
+ File.open 'mismatch.gem', 'wb' do |io|
io.write gem.string
end
@@ -644,7 +781,7 @@ class TestGemPackage < Gem::Package::TarTestCase
data_tgz = data_tgz.string
gem = util_tar do |tar|
- metadata_gz = Gem.gzip @spec.to_yaml
+ metadata_gz = Gem::Util.gzip @spec.to_yaml
tar.add_file 'metadata.gz', 0444 do |io|
io.write metadata_gz
@@ -670,7 +807,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
end
- open 'data_checksum_missing.gem', 'wb' do |io|
+ File.open 'data_checksum_missing.gem', 'wb' do |io|
io.write gem.string
end
@@ -681,7 +818,7 @@ class TestGemPackage < Gem::Package::TarTestCase
def test_verify_corrupt
tf = Tempfile.open 'corrupt' do |io|
- data = Gem.gzip 'a' * 10
+ data = Gem::Util.gzip 'a' * 10
io.write \
tar_file_header('metadata.gz', "\000x", 0644, data.length, Time.now)
io.write data
@@ -697,7 +834,7 @@ class TestGemPackage < Gem::Package::TarTestCase
e.message
io
end
- tf.close! if tf.respond_to? :close!
+ tf.close!
end
def test_verify_empty
@@ -723,6 +860,32 @@ class TestGemPackage < Gem::Package::TarTestCase
assert_match %r%nonexistent.gem$%, e.message
end
+ def test_verify_duplicate_file
+ FileUtils.mkdir_p 'lib'
+ FileUtils.touch 'lib/code.rb'
+
+ build = Gem::Package.new @gem
+ build.spec = @spec
+ build.setup_signer
+ open @gem, 'wb' do |gem_io|
+ Gem::Package::TarWriter.new gem_io do |gem|
+ build.add_metadata gem
+ build.add_contents gem
+
+ gem.add_file_simple 'a.sig', 0444, 0
+ gem.add_file_simple 'a.sig', 0444, 0
+ end
+ end
+
+ package = Gem::Package.new @gem
+
+ e = assert_raises Gem::Security::Exception do
+ package.verify
+ end
+
+ assert_equal 'duplicate files in the package: ("a.sig")', e.message
+ end
+
def test_verify_security_policy
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
@@ -773,14 +936,20 @@ class TestGemPackage < Gem::Package::TarTestCase
FileUtils.mkdir 'lib'
FileUtils.touch 'lib/code.rb'
- open @gem, 'wb' do |gem_io|
+ File.open @gem, 'wb' do |gem_io|
Gem::Package::TarWriter.new gem_io do |gem|
build.add_metadata gem
build.add_contents gem
# write bogus data.tar.gz to foil signature
- bogus_data = Gem.gzip 'hello'
- gem.add_file_simple 'data.tar.gz', 0444, bogus_data.length do |io|
+ bogus_data = Gem::Util.gzip 'hello'
+ fake_signer = Class.new do
+ def digest_name; 'SHA512'; end
+ def digest_algorithm; Digest(:SHA512); end
+ def key; 'key'; end
+ def sign(*); 'fake_sig'; end
+ end
+ gem.add_file_signed 'data2.tar.gz', 0444, fake_signer.new do |io|
io.write bogus_data
end
@@ -804,7 +973,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_verify_truncate
- open 'bad.gem', 'wb' do |io|
+ File.open 'bad.gem', 'wb' do |io|
io.write File.read(@gem, 1024) # don't care about newlines
end
@@ -831,6 +1000,40 @@ class TestGemPackage < Gem::Package::TarTestCase
end
assert_equal "package is corrupt, exception while verifying: whatever (ArgumentError) in #{@gem}", e.message
+
+ valid_metadata = ["metadata", "metadata.gz"]
+ valid_metadata.each do |vm|
+ $spec_loaded = false
+ $good_name = vm
+
+ entry = Object.new
+ def entry.full_name() $good_name end
+
+ package = Gem::Package.new(@gem)
+ package.instance_variable_set(:@files, [])
+ def package.load_spec(entry) $spec_loaded = true end
+
+ package.verify_entry(entry)
+
+ assert $spec_loaded
+ end
+
+ invalid_metadata = ["metadataxgz", "foobar\nmetadata", "metadata\nfoobar"]
+ invalid_metadata.each do |vm|
+ $spec_loaded = false
+ $bad_name = vm
+
+ entry = Object.new
+ def entry.full_name() $bad_name end
+
+ package = Gem::Package.new(@gem)
+ package.instance_variable_set(:@files, [])
+ def package.load_spec(entry) $spec_loaded = true end
+
+ package.verify_entry(entry)
+
+ refute $spec_loaded
+ end
end
def test_spec
diff --git a/test/rubygems/test_gem_package_old.rb b/test/rubygems/test_gem_package_old.rb
index c15475b0c7..ab7934dde5 100644
--- a/test/rubygems/test_gem_package_old.rb
+++ b/test/rubygems/test_gem_package_old.rb
@@ -7,7 +7,7 @@ class TestGemPackageOld < Gem::TestCase
def setup
super
- open 'old_format.gem', 'wb' do |io|
+ File.open 'old_format.gem', 'wb' do |io|
io.write SIMPLE_GEM
end
@@ -87,4 +87,3 @@ class TestGemPackageOld < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_package_tar_header.rb b/test/rubygems/test_gem_package_tar_header.rb
index d33877057d..5e804bb418 100644
--- a/test/rubygems/test_gem_package_tar_header.rb
+++ b/test/rubygems/test_gem_package_tar_header.rb
@@ -143,5 +143,25 @@ group\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
assert_equal '012467', @tar_header.checksum
end
-end
+ def test_from_bad_octal
+ test_cases = [
+ "00000006,44\000", # bogus character
+ "00000006789\000", # non-octal digit
+ "+0000001234\000", # positive sign
+ "-0000001000\000", # negative sign
+ "0x000123abc\000", # radix prefix
+ ]
+
+ test_cases.each do |val|
+ header_s = @tar_header.to_s
+ # overwrite the size field
+ header_s[124, 12] = val
+ io = TempIO.new header_s
+ assert_raises ArgumentError do
+ Gem::Package::TarHeader.from io
+ end
+ io.close!
+ end
+ end
+end
diff --git a/test/rubygems/test_gem_package_tar_reader.rb b/test/rubygems/test_gem_package_tar_reader.rb
index e8a902c311..489685d09d 100644
--- a/test/rubygems/test_gem_package_tar_reader.rb
+++ b/test/rubygems/test_gem_package_tar_reader.rb
@@ -87,4 +87,3 @@ class TestGemPackageTarReader < Gem::Package::TarTestCase
end
end
-
diff --git a/test/rubygems/test_gem_package_tar_reader_entry.rb b/test/rubygems/test_gem_package_tar_reader_entry.rb
index dba1987d4d..ef088e32be 100644
--- a/test/rubygems/test_gem_package_tar_reader_entry.rb
+++ b/test/rubygems/test_gem_package_tar_reader_entry.rb
@@ -34,6 +34,10 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
assert_equal 1, @entry.bytes_read
end
+ def test_size
+ assert_equal @contents.size, @entry.size
+ end
+
def test_close
@entry.close
@@ -129,6 +133,13 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
assert_equal @contents[0...100], @entry.read(100)
end
+ def test_readpartial
+ assert_raises(EOFError) do
+ @entry.read(@contents.size)
+ @entry.readpartial(1)
+ end
+ end
+
def test_rewind
char = @entry.getc
diff --git a/test/rubygems/test_gem_package_tar_writer.rb b/test/rubygems/test_gem_package_tar_writer.rb
index bed1e3b221..517320c011 100644
--- a/test/rubygems/test_gem_package_tar_writer.rb
+++ b/test/rubygems/test_gem_package_tar_writer.rb
@@ -11,9 +11,12 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
@data = 'abcde12345'
@io = TempIO.new
@tar_writer = Gem::Package::TarWriter.new @io
+ @epoch = ENV["SOURCE_DATE_EPOCH"]
+ ENV["SOURCE_DATE_EPOCH"] = nil
end
def teardown
+ ENV["SOURCE_DATE_EPOCH"] = @epoch
@tar_writer.close unless @tar_writer.closed?
@io.close!
@@ -31,6 +34,16 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
assert_equal 1024, @io.pos
end
+ def test_add_file_source_date_epoch
+ ENV["SOURCE_DATE_EPOCH"] = "123456789"
+ Time.stub :now, Time.at(1458518157) do
+ @tar_writer.mkdir 'foo', 0644
+
+ assert_headers_equal tar_dir_header('foo', '', 0644, Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc),
+ @io.string[0, 512]
+ end
+ end
+
def test_add_symlink
Time.stub :now, Time.at(1458518157) do
@tar_writer.add_symlink 'x', 'y', 0644
@@ -41,6 +54,16 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
assert_equal 512, @io.pos
end
+ def test_add_symlink_source_date_epoch
+ ENV["SOURCE_DATE_EPOCH"] = "123456789"
+ Time.stub :now, Time.at(1458518157) do
+ @tar_writer.add_symlink 'x', 'y', 0644
+
+ assert_headers_equal(tar_symlink_header('x', '', 0644, Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc, 'y'),
+ @io.string[0, 512])
+ end
+ end
+
def test_add_file_digest
digest_algorithms = Digest::SHA1, Digest::SHA512
@@ -116,7 +139,6 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
assert_equal 2048, @io.pos
end
-
end
def test_add_file_signer_empty
@@ -148,6 +170,16 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
assert_equal 1024, @io.pos
end
+ def test_add_file_simple_source_date_epoch
+ ENV["SOURCE_DATE_EPOCH"] = "123456789"
+ Time.stub :now, Time.at(1458518157) do
+ @tar_writer.add_file_simple 'x', 0644, 10 do |io| io.write "a" * 10 end
+
+ assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc),
+ @io.string[0, 512])
+ end
+ end
+
def test_add_file_simple_padding
Time.stub :now, Time.at(1458518157) do
@tar_writer.add_file_simple 'x', 0, 100
@@ -175,12 +207,6 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
end
end
- def test_add_file_unseekable
- assert_raises Gem::Package::NonSeekableIO do
- Gem::Package::TarWriter.new(Object.new).add_file 'x', 0
- end
- end
-
def test_close
@tar_writer.close
@@ -223,6 +249,16 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
end
end
+ def test_mkdir_source_date_epoch
+ ENV["SOURCE_DATE_EPOCH"] = "123456789"
+ Time.stub :now, Time.at(1458518157) do
+ @tar_writer.mkdir 'foo', 0644
+
+ assert_headers_equal tar_dir_header('foo', '', 0644, Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc),
+ @io.string[0, 512]
+ end
+ end
+
def test_split_name
assert_equal ['b' * 100, 'a' * 155],
@tar_writer.split_name("#{'a' * 155}/#{'b' * 100}")
diff --git a/test/rubygems/test_gem_package_task.rb b/test/rubygems/test_gem_package_task.rb
index f69ee36bd3..e01f4a4043 100644
--- a/test/rubygems/test_gem_package_task.rb
+++ b/test/rubygems/test_gem_package_task.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
require 'rubygems/test_case'
require 'rubygems'
+require 'bundler/errors'
begin
require 'rubygems/package_task'
-rescue LoadError
+rescue LoadError, Bundler::GemfileNotFound
end
class TestGemPackageTask < Gem::TestCase
@@ -81,4 +82,3 @@ class TestGemPackageTask < Gem::TestCase
end
end if defined?(Rake::PackageTask)
-
diff --git a/test/rubygems/test_gem_path_support.rb b/test/rubygems/test_gem_path_support.rb
index 754c43e893..551ec28781 100644
--- a/test/rubygems/test_gem_path_support.rb
+++ b/test/rubygems/test_gem_path_support.rb
@@ -29,7 +29,7 @@ class TestGemPathSupport < Gem::TestCase
assert_equal expected, ps.path
end
- if defined?(File::ALT_SEPARATOR) and File::ALT_SEPARATOR
+ if File::ALT_SEPARATOR
def test_initialize_home_normalize
alternate = @tempdir.gsub(File::SEPARATOR, File::ALT_SEPARATOR)
ps = Gem::PathSupport.new "GEM_HOME" => alternate
@@ -44,10 +44,10 @@ class TestGemPathSupport < Gem::TestCase
assert_equal ENV["GEM_HOME"], ps.home
expected = [
- File.join(@tempdir, 'foo'),
- File.join(@tempdir, 'bar'),
- ENV["GEM_HOME"],
- ]
+ File.join(@tempdir, 'foo'),
+ File.join(@tempdir, 'bar'),
+ ENV["GEM_HOME"],
+ ]
assert_equal expected, ps.path
end
@@ -63,9 +63,9 @@ class TestGemPathSupport < Gem::TestCase
assert_equal ENV["GEM_HOME"], ps.home
expected = [
- File.join(@tempdir, 'foo'),
- File.join(@tempdir, 'bar'),
- ] + Gem.default_path << ENV["GEM_HOME"]
+ File.join(@tempdir, 'foo'),
+ File.join(@tempdir, 'bar'),
+ ] + Gem.default_path << ENV["GEM_HOME"]
assert_equal expected, ps.path
end
@@ -81,9 +81,9 @@ class TestGemPathSupport < Gem::TestCase
assert_equal ENV["GEM_HOME"], ps.home
expected = [
- File.join(@tempdir, 'foo'),
- File.join(@tempdir, 'bar'),
- ] + Gem.default_path << ENV["GEM_HOME"]
+ File.join(@tempdir, 'foo'),
+ File.join(@tempdir, 'bar'),
+ ] + Gem.default_path << ENV["GEM_HOME"]
assert_equal expected, ps.path
end
@@ -118,4 +118,21 @@ class TestGemPathSupport < Gem::TestCase
ps = Gem::PathSupport.new "GEM_SPEC_CACHE" => "foo"
assert_equal "foo", ps.spec_cache_dir
end
+
+ def test_gem_paths_do_not_contain_symlinks
+ dir = "#{@tempdir}/realgemdir"
+ symlink = "#{@tempdir}/symdir"
+ Dir.mkdir dir
+ begin
+ File.symlink(dir, symlink)
+ rescue NotImplementedError, SystemCallError
+ skip 'symlinks not supported'
+ end
+ not_existing = "#{@tempdir}/does_not_exist"
+ path = "#{symlink}#{File::PATH_SEPARATOR}#{not_existing}"
+
+ ps = Gem::PathSupport.new "GEM_PATH" => path, "GEM_HOME" => symlink
+ assert_equal dir, ps.home
+ assert_equal [dir, not_existing], ps.path
+ end
end
diff --git a/test/rubygems/test_gem_platform.rb b/test/rubygems/test_gem_platform.rb
index c9abd08868..fc8b7030fb 100644
--- a/test/rubygems/test_gem_platform.rb
+++ b/test/rubygems/test_gem_platform.rb
@@ -117,7 +117,7 @@ class TestGemPlatform < Gem::TestCase
assert_equal expected, platform.to_a, 'i386-mswin32 VC6'
ensure
- if orig_RUBY_SO_NAME then
+ if orig_RUBY_SO_NAME
RbConfig::CONFIG['RUBY_SO_NAME'] = orig_RUBY_SO_NAME
else
RbConfig::CONFIG.delete 'RUBY_SO_NAME'
@@ -145,7 +145,7 @@ class TestGemPlatform < Gem::TestCase
end
def test_to_s
- if win_platform? then
+ if win_platform?
assert_equal 'x86-mswin32-60', Gem::Platform.local.to_s
else
assert_equal 'x86-darwin-8', Gem::Platform.local.to_s
@@ -297,12 +297,11 @@ class TestGemPlatform < Gem::TestCase
assert_local_match 'sparc-solaris2.8-mq5.3'
end
- def assert_local_match name
+ def assert_local_match(name)
assert_match Gem::Platform.local, name
end
- def refute_local_match name
+ def refute_local_match(name)
refute_match Gem::Platform.local, name
end
end
-
diff --git a/test/rubygems/test_gem_rdoc.rb b/test/rubygems/test_gem_rdoc.rb
index 76ca8c45a9..073578527d 100644
--- a/test/rubygems/test_gem_rdoc.rb
+++ b/test/rubygems/test_gem_rdoc.rb
@@ -49,7 +49,7 @@ class TestGemRDoc < Gem::TestCase
end
def test_initialize
- if rdoc_4? then
+ if rdoc_4?
refute @hook.generate_rdoc
else
assert @hook.generate_rdoc
@@ -223,6 +223,7 @@ class TestGemRDoc < Gem::TestCase
def test_remove_unwritable
skip 'chmod not supported' if Gem.win_platform?
+ skip 'skipped in root privilege' if Process.uid.zero?
FileUtils.mkdir_p @a.base_dir
FileUtils.chmod 0, @a.base_dir
@@ -251,6 +252,7 @@ class TestGemRDoc < Gem::TestCase
def test_setup_unwritable
skip 'chmod not supported' if Gem.win_platform?
+ skip 'skipped in root privilege' if Process.uid.zero?
FileUtils.mkdir_p @a.doc_dir
FileUtils.chmod 0, @a.doc_dir
@@ -267,4 +269,3 @@ class TestGemRDoc < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb
index ca62a8342e..a725fe205c 100644
--- a/test/rubygems/test_gem_remote_fetcher.rb
+++ b/test/rubygems/test_gem_remote_fetcher.rb
@@ -9,6 +9,10 @@ rescue LoadError => e
e.message =~ / -- openssl$/
end
+unless defined?(OpenSSL::SSL)
+ warn 'Skipping Gem::Request tests. openssl not found.'
+end
+
require 'rubygems/remote_fetcher'
require 'rubygems/package'
require 'minitest/mock'
@@ -31,7 +35,7 @@ class TestGemRemoteFetcher < Gem::TestCase
include Gem::DefaultUserInteraction
- SERVER_DATA = <<-EOY
+ SERVER_DATA = <<-EOY.freeze
--- !ruby/object:Gem::Cache
gems:
rake-0.4.11: !ruby/object:Gem::Specification
@@ -47,7 +51,6 @@ gems:
author: Jim Weirich
email: jim@weirichhouse.org
homepage: http://rake.rubyforge.org
- rubyforge_project: rake
description: Rake is a Make-like program implemented in Ruby. Tasks and dependencies are specified in standard Ruby syntax.
autorequire:
default_executable: rake
@@ -118,7 +121,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
@a1.loaded_from = File.join(@gemhome, 'specifications', @a1.full_name)
Gem::RemoteFetcher.fetcher = nil
-
+ @stub_ui = Gem::MockGemUi.new
@fetcher = Gem::RemoteFetcher.fetcher
end
@@ -163,7 +166,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
def fetcher.request(uri, request_class, last_modified = nil)
- raise SocketError, "tarded"
+ raise SocketError, "oops"
end
uri = 'http://gems.example.com/yaml'
@@ -171,101 +174,16 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
fetcher.fetch_size uri
end
- assert_equal "SocketError: tarded (#{uri})", e.message
+ assert_equal "SocketError: oops (#{uri})", e.message
end
def test_no_proxy
- use_ui @ui do
+ use_ui @stub_ui do
assert_data_from_server @fetcher.fetch_path(@server_uri)
assert_equal SERVER_DATA.size, @fetcher.fetch_size(@server_uri)
end
end
- def test_api_endpoint
- uri = URI.parse "http://example.com/foo"
- target = MiniTest::Mock.new
- target.expect :target, "gems.example.com"
-
- dns = MiniTest::Mock.new
- dns.expect :getresource, target, [String, Object]
-
- fetch = Gem::RemoteFetcher.new nil, dns
- assert_equal URI.parse("http://gems.example.com/foo"), fetch.api_endpoint(uri)
-
- target.verify
- dns.verify
- end
-
- def test_api_endpoint_ignores_trans_domain_values
- uri = URI.parse "http://gems.example.com/foo"
- target = MiniTest::Mock.new
- target.expect :target, "blah.com"
-
- dns = MiniTest::Mock.new
- dns.expect :getresource, target, [String, Object]
-
- fetch = Gem::RemoteFetcher.new nil, dns
- assert_equal URI.parse("http://gems.example.com/foo"), fetch.api_endpoint(uri)
-
- target.verify
- dns.verify
- end
-
- def test_api_endpoint_ignores_trans_domain_values_that_starts_with_original
- uri = URI.parse "http://example.com/foo"
- target = MiniTest::Mock.new
- target.expect :target, "example.combadguy.com"
-
- dns = MiniTest::Mock.new
- dns.expect :getresource, target, [String, Object]
-
- fetch = Gem::RemoteFetcher.new nil, dns
- assert_equal URI.parse("http://example.com/foo"), fetch.api_endpoint(uri)
-
- target.verify
- dns.verify
- end
-
- def test_api_endpoint_ignores_trans_domain_values_that_end_with_original
- uri = URI.parse "http://example.com/foo"
- target = MiniTest::Mock.new
- target.expect :target, "badexample.com"
-
- dns = MiniTest::Mock.new
- dns.expect :getresource, target, [String, Object]
-
- fetch = Gem::RemoteFetcher.new nil, dns
- assert_equal URI.parse("http://example.com/foo"), fetch.api_endpoint(uri)
-
- target.verify
- dns.verify
- end
-
- def test_api_endpoint_timeout_warning
- uri = URI.parse "http://gems.example.com/foo"
-
- dns = MiniTest::Mock.new
- def dns.getresource arg, *rest
- raise Resolv::ResolvError.new('timeout!')
- end
-
- fetch = Gem::RemoteFetcher.new nil, dns
- begin
- old_verbose, Gem.configuration.verbose = Gem.configuration.verbose, 1
- endpoint = use_ui @ui do
- fetch.api_endpoint(uri)
- end
- ensure
- Gem.configuration.verbose = old_verbose
- end
-
- assert_equal uri, endpoint
-
- assert_equal "Getting SRV record failed: timeout!\n", @ui.output
-
- dns.verify
- end
-
def test_cache_update_path
uri = URI 'http://example/file'
path = File.join @tempdir, 'file'
@@ -292,20 +210,20 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
refute_path_exists path
end
- def util_fuck_with_fetcher data, blow = false
+ def util_fuck_with_fetcher(data, blow = false)
fetcher = Gem::RemoteFetcher.fetcher
fetcher.instance_variable_set :@test_data, data
- unless blow then
- def fetcher.fetch_path arg, *rest
+ unless blow
+ def fetcher.fetch_path(arg, *rest)
@test_arg = arg
@test_data
end
else
- def fetcher.fetch_path arg, *rest
+ def fetcher.fetch_path(arg, *rest)
# OMG I'm such an ass
class << self; remove_method :fetch_path; end
- def self.fetch_path arg, *rest
+ def self.fetch_path(arg, *rest)
@test_arg = arg
@test_data
end
@@ -416,7 +334,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
assert File.exist?(a1_cache_gem)
end
- unless win_platform? # File.chmod doesn't work
+ unless win_platform? || Process.uid.zero? # File.chmod doesn't work
def test_download_local_read_only
FileUtils.mv @a1_gem, @tempdir
local_path = File.join @tempdir, @a1.file_name
@@ -505,7 +423,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
util_setup_spec_fetcher @a1, @a2
@fetcher.instance_variable_set :@a1, @a1
@fetcher.instance_variable_set :@a2, @a2
- def @fetcher.fetch_path uri, mtime = nil, head = false
+ def @fetcher.fetch_path(uri, mtime = nil, head = false)
case uri.request_uri
when /#{@a1.spec_name}/ then
Gem.deflate Marshal.dump @a1
@@ -526,7 +444,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
@fetcher = fetcher
def fetcher.fetch_http(uri, mtime, head = nil)
- Gem.gzip 'foo'
+ Gem::Util.gzip 'foo'
end
assert_equal 'foo', fetcher.fetch_path(@uri + 'foo.gz')
@@ -540,7 +458,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
nil
end
- assert_equal nil, fetcher.fetch_path(@uri + 'foo.gz', Time.at(0))
+ assert_nil fetcher.fetch_path(@uri + 'foo.gz', Time.at(0))
end
def test_fetch_path_io_error
@@ -598,6 +516,24 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
assert_equal url, e.uri
end
+ def test_fetch_path_openssl_ssl_sslerror
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+
+ def fetcher.fetch_http(uri, mtime = nil, head = nil)
+ raise OpenSSL::SSL::SSLError
+ end
+
+ url = 'http://example.com/uri'
+
+ e = assert_raises Gem::RemoteFetcher::FetchError do
+ fetcher.fetch_path url
+ end
+
+ assert_equal "OpenSSL::SSL::SSLError: OpenSSL::SSL::SSLError (#{url})", e.message
+ assert_equal url, e.uri
+ end
+
def test_fetch_path_unmodified
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
@@ -606,11 +542,11 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
nil
end
- assert_equal nil, fetcher.fetch_path(URI.parse(@gem_repo), Time.at(0))
+ assert_nil fetcher.fetch_path(URI.parse(@gem_repo), Time.at(0))
end
def test_implicit_no_proxy
- use_ui @ui do
+ use_ui @stub_ui do
ENV['http_proxy'] = 'http://fakeurl:12345'
fetcher = Gem::RemoteFetcher.new :no_proxy
@fetcher = fetcher
@@ -619,7 +555,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
end
def test_implicit_proxy
- use_ui @ui do
+ use_ui @stub_ui do
ENV['http_proxy'] = @proxy_uri
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
@@ -628,7 +564,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
end
def test_implicit_upper_case_proxy
- use_ui @ui do
+ use_ui @stub_ui do
ENV['HTTP_PROXY'] = @proxy_uri
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
@@ -637,7 +573,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
end
def test_implicit_proxy_no_env
- use_ui @ui do
+ use_ui @stub_ui do
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
assert_data_from_server fetcher.fetch_path(@server_uri)
@@ -651,7 +587,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def fetcher.request(uri, request_class, last_modified = nil)
url = 'http://gems.example.com/redirect'
- unless defined? @requested then
+ unless defined? @requested
@requested = true
res = Net::HTTPMovedPermanently.new nil, 301, nil
res.add_field 'Location', url
@@ -712,10 +648,9 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
assert_equal "murphy", fetcher.fetch_path(@server_uri)
end
- def test_fetch_s3
+ def assert_fetch_s3(url)
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
- url = 's3://testuser:testpass@my-bucket/gems/specs.4.8.gz'
$fetched_uri = nil
def fetcher.request(uri, request_class, last_modified = nil)
@@ -737,19 +672,68 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
$fetched_uri = nil
end
- def test_fetch_s3_no_creds
+ def test_fetch_s3_config_creds
+ Gem.configuration[:s3_source] = {
+ 'my-bucket' => {:id => 'testuser', :secret => 'testpass'}
+ }
+ url = 's3://my-bucket/gems/specs.4.8.gz'
+ assert_fetch_s3 url
+ ensure
+ Gem.configuration[:s3_source] = nil
+ end
+
+ def test_fetch_s3_url_creds
+ url = 's3://testuser:testpass@my-bucket/gems/specs.4.8.gz'
+ assert_fetch_s3 url
+ end
+
+ def refute_fetch_s3(url, expected_message)
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
- url = 's3://my-bucket/gems/specs.4.8.gz'
+
e = assert_raises Gem::RemoteFetcher::FetchError do
fetcher.fetch_s3 URI.parse(url)
end
- assert_match "credentials needed", e.message
+ assert_match expected_message, e.message
+ end
+
+ def test_fetch_s3_no_source_key
+ url = 's3://my-bucket/gems/specs.4.8.gz'
+ refute_fetch_s3 url, 'no s3_source key exists in .gemrc'
+ end
+
+ def test_fetch_s3_no_host
+ Gem.configuration[:s3_source] = {
+ 'my-bucket' => {:id => 'testuser', :secret => 'testpass'}
+ }
+
+ url = 's3://other-bucket/gems/specs.4.8.gz'
+ refute_fetch_s3 url, 'no key for host other-bucket in s3_source in .gemrc'
+ ensure
+ Gem.configuration[:s3_source] = nil
+ end
+
+ def test_fetch_s3_no_id
+ Gem.configuration[:s3_source] = { 'my-bucket' => {:secret => 'testpass'} }
+
+ url = 's3://my-bucket/gems/specs.4.8.gz'
+ refute_fetch_s3 url, 's3_source for my-bucket missing id or secret'
+ ensure
+ Gem.configuration[:s3_source] = nil
+ end
+
+ def test_fetch_s3_no_secret
+ Gem.configuration[:s3_source] = { 'my-bucket' => {:id => 'testuser'} }
+
+ url = 's3://my-bucket/gems/specs.4.8.gz'
+ refute_fetch_s3 url, 's3_source for my-bucket missing id or secret'
+ ensure
+ Gem.configuration[:s3_source] = nil
end
def test_observe_no_proxy_env_single_host
- use_ui @ui do
+ use_ui @stub_ui do
ENV["http_proxy"] = @proxy_uri
ENV["no_proxy"] = URI::parse(@server_uri).host
fetcher = Gem::RemoteFetcher.new nil
@@ -759,7 +743,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
end
def test_observe_no_proxy_env_list
- use_ui @ui do
+ use_ui @stub_ui do
ENV["http_proxy"] = @proxy_uri
ENV["no_proxy"] = "fakeurl.com, #{URI::parse(@server_uri).host}"
fetcher = Gem::RemoteFetcher.new nil
@@ -781,7 +765,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
end
def test_yaml_error_on_size
- use_ui @ui do
+ use_ui @stub_ui do
self.class.enable_yaml = false
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
@@ -827,9 +811,9 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
with_configured_fetcher(
":ssl_ca_cert: #{temp_ca_cert}\n" +
":ssl_client_cert: #{temp_client_cert}\n") do |fetcher|
- assert_raises Gem::RemoteFetcher::FetchError do
- fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml")
- end
+ assert_raises Gem::RemoteFetcher::FetchError do
+ fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml")
+ end
end
end
@@ -851,11 +835,27 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_do_not_follow_insecure_redirect
ssl_server = self.class.start_ssl_server
- temp_ca_cert = File.join(DIR, 'ca_cert.pem'),
+ temp_ca_cert = File.join(DIR, 'ca_cert.pem')
+ expected_error_message =
+ "redirecting to non-https resource: #{@server_uri} (https://localhost:#{ssl_server.config[:Port]}/insecure_redirect?to=#{@server_uri})"
+
with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher|
- assert_raises Gem::RemoteFetcher::FetchError do
+ err = assert_raises Gem::RemoteFetcher::FetchError do
fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/insecure_redirect?to=#{@server_uri}")
end
+
+ assert_equal(err.message, expected_error_message)
+ end
+ end
+
+ def test_nil_ca_cert
+ ssl_server = self.class.start_ssl_server
+ temp_ca_cert = nil
+
+ with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher|
+ assert_raises Gem::RemoteFetcher::FetchError do
+ fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}")
+ end
end
end
@@ -997,7 +997,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
:DocumentRoot => nil,
:Logger => null_logger,
:AccessLog => null_logger
- )
+ )
s.mount_proc("/kill") { |req, res| s.shutdown }
s.mount_proc("/yaml") { |req, res|
if req["X-Captain"]
@@ -1052,4 +1052,4 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
assert_equal "/home/skillet", @fetcher.correct_for_windows_path(path)
end
-end
+end if defined?(OpenSSL::SSL)
diff --git a/test/rubygems/test_gem_request.rb b/test/rubygems/test_gem_request.rb
index 46a49a6943..8b475fae42 100644
--- a/test/rubygems/test_gem_request.rb
+++ b/test/rubygems/test_gem_request.rb
@@ -4,6 +4,10 @@ require 'rubygems/request'
require 'ostruct'
require 'base64'
+unless defined?(OpenSSL::SSL)
+ warn 'Skipping Gem::Request tests. openssl not found.'
+end
+
class TestGemRequest < Gem::TestCase
CA_CERT_FILE = cert_path 'ca'
@@ -13,7 +17,7 @@ class TestGemRequest < Gem::TestCase
PUBLIC_CERT_FILE = cert_path 'public'
SSL_CERT = load_cert 'ssl'
- def make_request uri, request_class, last_modified, proxy
+ def make_request(uri, request_class, last_modified, proxy)
Gem::Request.create_with_proxy uri, request_class, last_modified, proxy
end
@@ -438,7 +442,7 @@ ERROR: Certificate is an invalid CA certificate
message =
Gem::Request.verify_certificate_message error_number, EXPIRED_CERT
- assert_equal "You must add #{EXPIRED_CERT.issuer} to your local trusted store",
+ assert_equal "Cannot verify certificate issued by #{EXPIRED_CERT.issuer}",
message
end
@@ -461,7 +465,7 @@ ERROR: Certificate is an invalid CA certificate
@orig_RUBY_REVISION = RUBY_REVISION if defined? RUBY_REVISION
end
- def util_stub_net_http hash
+ def util_stub_net_http(hash)
old_client = Gem::Request::ConnectionPools.client
conn = Conn.new OpenStruct.new(hash)
Gem::Request::ConnectionPools.client = conn
@@ -473,7 +477,7 @@ ERROR: Certificate is an invalid CA certificate
class Conn
attr_accessor :payload
- def new *args; self; end
+ def new(*args); self; end
def use_ssl=(bool); end
def verify_callback=(setting); end
def verify_mode=(setting); end
@@ -491,5 +495,4 @@ ERROR: Certificate is an invalid CA certificate
end
end
-end
-
+end if defined?(OpenSSL::SSL)
diff --git a/test/rubygems/test_gem_request_connection_pools.rb b/test/rubygems/test_gem_request_connection_pools.rb
index a9bddb900e..eb48ba45d2 100644
--- a/test/rubygems/test_gem_request_connection_pools.rb
+++ b/test/rubygems/test_gem_request_connection_pools.rb
@@ -5,7 +5,7 @@ require 'timeout'
class TestGemRequestConnectionPool < Gem::TestCase
class FakeHttp
- def initialize *args
+ def initialize(*args)
end
def start
@@ -25,6 +25,28 @@ class TestGemRequestConnectionPool < Gem::TestCase
super
end
+ def test_to_proxy_substring
+ pools = Gem::Request::ConnectionPools.new nil, []
+
+ env_no_proxy = %w[
+ ems.example
+ ]
+
+ no_proxy = pools.send :no_proxy?, 'rubygems.example', env_no_proxy
+
+ refute no_proxy, 'mismatch'
+ end
+
+ def test_to_proxy_empty_string
+ pools = Gem::Request::ConnectionPools.new nil, []
+
+ env_no_proxy = [""]
+
+ no_proxy = pools.send :no_proxy?, 'ems.example', env_no_proxy
+
+ refute no_proxy, 'mismatch'
+ end
+
def test_checkout_same_connection
uri = URI.parse('http://example/some_endpoint')
@@ -86,8 +108,7 @@ class TestGemRequestConnectionPool < Gem::TestCase
net_http_args = pools.send :net_http_args, URI('http://[::1]'), nil
- expected_host = RUBY_VERSION >= "1.9.3" ? "::1" : "[::1]"
- assert_equal [expected_host, 80], net_http_args
+ assert_equal ["::1", 80], net_http_args
end
def test_net_http_args_proxy
@@ -118,13 +139,12 @@ class TestGemRequestConnectionPool < Gem::TestCase
pool.checkout
- t1 = Thread.new {
- Timeout.timeout(1) do
- pool.checkout
+ Thread.new {
+ assert_raises(Timeout::Error) do
+ Timeout.timeout(1) do
+ pool.checkout
+ end
end
- }
- assert_raises(Timeout::Error) do
- t1.join
- end
+ }.join
end
end
diff --git a/test/rubygems/test_gem_request_set.rb b/test/rubygems/test_gem_request_set.rb
index 3a48827481..86986f2287 100644
--- a/test/rubygems/test_gem_request_set.rb
+++ b/test/rubygems/test_gem_request_set.rb
@@ -52,7 +52,7 @@ class TestGemRequestSet < Gem::TestCase
rs = Gem::RequestSet.new
installed = []
- open 'gem.deps.rb', 'w' do |io|
+ File.open 'gem.deps.rb', 'w' do |io|
io.puts 'gem "a"'
io.flush
@@ -78,7 +78,7 @@ class TestGemRequestSet < Gem::TestCase
rs = Gem::RequestSet.new
- open 'gem.deps.rb', 'w' do |io|
+ File.open 'gem.deps.rb', 'w' do |io|
io.puts 'gem "a"'
io.flush
@@ -104,7 +104,7 @@ Gems to install:
rs = Gem::RequestSet.new
installed = []
- open 'gem.deps.rb', 'w' do |io|
+ File.open 'gem.deps.rb', 'w' do |io|
io.puts 'gem "a"'
end
@@ -128,7 +128,7 @@ Gems to install:
rs = Gem::RequestSet.new
- open 'gem.deps.rb', 'w' do |io|
+ File.open 'gem.deps.rb', 'w' do |io|
io.puts 'gem "a"'
io.flush
@@ -150,7 +150,7 @@ Gems to install:
rs = Gem::RequestSet.new
installed = []
- open 'gem.deps.rb.lock', 'w' do |io|
+ File.open 'gem.deps.rb.lock', 'w' do |io|
io.puts <<-LOCKFILE
GEM
remote: #{@gem_repo}
@@ -167,7 +167,7 @@ DEPENDENCIES
LOCKFILE
end
- open 'gem.deps.rb', 'w' do |io|
+ File.open 'gem.deps.rb', 'w' do |io|
io.puts 'gem "b"'
end
@@ -190,7 +190,7 @@ DEPENDENCIES
rs = Gem::RequestSet.new
installed = []
- open 'gem.deps.rb', 'w' do |io|
+ File.open 'gem.deps.rb', 'w' do |io|
io.puts <<-GEM_DEPS
gem "a"
ruby "0"
@@ -218,7 +218,7 @@ ruby "0"
assert_kind_of Gem::RequestSet::GemDependencyAPI, gem_deps
io
end
- tf.close! if tf.respond_to? :close!
+ tf.close!
assert_equal [dep('a')], rs.dependencies
@@ -239,7 +239,7 @@ ruby "0"
assert_kind_of Gem::RequestSet::GemDependencyAPI, gem_deps
io
end
- tf.close! if tf.respond_to? :close!
+ tf.close!
assert_equal [dep('a')], rs.dependencies
end
@@ -254,7 +254,7 @@ ruby "0"
rs.load_gemdeps io.path, [:test]
io
end
- tf.close! if tf.respond_to? :close!
+ tf.close!
assert_empty rs.dependencies
end
@@ -346,7 +346,7 @@ ruby "0"
rs.load_gemdeps io.path
io
end
- tf.close! if tf.respond_to? :close!
+ tf.close!
res = rs.resolve
assert_equal 1, res.size
@@ -410,7 +410,7 @@ ruby "0"
rs.load_gemdeps io.path
io
end
- tf.close! if tf.respond_to? :close!
+ tf.close!
res = rs.resolve
assert_equal 2, res.size
diff --git a/test/rubygems/test_gem_request_set_gem_dependency_api.rb b/test/rubygems/test_gem_request_set_gem_dependency_api.rb
index 509d49890d..320fdcb604 100644
--- a/test/rubygems/test_gem_request_set_gem_dependency_api.rb
+++ b/test/rubygems/test_gem_request_set_gem_dependency_api.rb
@@ -19,7 +19,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
@gda.instance_variable_set :@vendor_set, @vendor_set
end
- def with_engine_version name, version
+ def with_engine_version(name, version)
engine = RUBY_ENGINE if Object.const_defined? :RUBY_ENGINE
engine_version_const = "#{Gem.ruby_engine.upcase}_VERSION"
engine_version = Object.const_get engine_version_const
@@ -627,11 +627,7 @@ end
assert_equal [dep('a'), dep('b')], @set.dependencies
io
end
- tf.close! if tf.respond_to? :close!
- end
-
- def test_name_typo
- assert_same @GDA, Gem::RequestSet::GemDepedencyAPI
+ tf.close!
end
def test_pin_gem_source
@@ -753,7 +749,7 @@ end
@gda.ruby RUBY_VERSION, :engine => 'jruby', :engine_version => '1.7.4'
end
- assert_equal 'Your ruby engine is ruby, but your gem.deps.rb requires jruby',
+ assert_equal 'Your Ruby engine is ruby, but your gem.deps.rb requires jruby',
e.message
end
end
@@ -764,7 +760,7 @@ end
@gda.ruby RUBY_VERSION, :engine => 'jruby', :engine_version => '1.7.4'
end
- assert_equal 'Your ruby engine version is jruby 1.7.6, but your gem.deps.rb requires jruby 1.7.4',
+ assert_equal 'Your Ruby engine version is jruby 1.7.6, but your gem.deps.rb requires jruby 1.7.4',
e.message
end
end
@@ -774,7 +770,7 @@ end
@gda.ruby RUBY_VERSION, :engine => 'jruby'
end
- assert_equal 'you must specify engine_version along with the ruby engine',
+ assert_equal 'You must specify engine_version along with the Ruby engine',
e.message
end
@@ -828,4 +824,3 @@ end
end
end
-
diff --git a/test/rubygems/test_gem_request_set_lockfile.rb b/test/rubygems/test_gem_request_set_lockfile.rb
index 908f97303e..9ac691ebdd 100644
--- a/test/rubygems/test_gem_request_set_lockfile.rb
+++ b/test/rubygems/test_gem_request_set_lockfile.rb
@@ -21,17 +21,16 @@ class TestGemRequestSetLockfile < Gem::TestCase
@set.instance_variable_set :@vendor_set, @vendor_set
@gem_deps_file = 'gem.deps.rb'
-
end
def lockfile
Gem::RequestSet::Lockfile.build @set, @gem_deps_file
end
- def write_lockfile lockfile
+ def write_lockfile(lockfile)
@lock_file = File.expand_path "#{@gem_deps_file}.lock"
- open @lock_file, 'w' do |io|
+ File.open @lock_file, 'w' do |io|
io.write lockfile
end
end
@@ -387,7 +386,7 @@ DEPENDENCIES
s.add_dependency 'c', '~> 1.0'
end
- open 'b.gemspec', 'w' do |io|
+ File.open 'b.gemspec', 'w' do |io|
io.write b.to_ruby
end
@@ -400,7 +399,7 @@ DEPENDENCIES
Dir.chdir 'c' do
c = Gem::Specification.new 'c', 1
- open 'c.gemspec', 'w' do |io|
+ File.open 'c.gemspec', 'w' do |io|
io.write c.to_ruby
end
@@ -455,7 +454,7 @@ DEPENDENCIES
gem_deps_lock_file = "#{@gem_deps_file}.lock"
- open gem_deps_lock_file, 'w' do |io|
+ File.open gem_deps_lock_file, 'w' do |io|
io.write 'hello'
end
diff --git a/test/rubygems/test_gem_request_set_lockfile_parser.rb b/test/rubygems/test_gem_request_set_lockfile_parser.rb
index 9946c522d9..27ee519ea7 100644
--- a/test/rubygems/test_gem_request_set_lockfile_parser.rb
+++ b/test/rubygems/test_gem_request_set_lockfile_parser.rb
@@ -248,13 +248,8 @@ DEPENDENCIES
assert_equal %w[a-2], lockfile_set.specs.map { |s| s.full_name }
- if [].respond_to? :flat_map
- assert_equal %w[https://gems.example/ https://other.example/],
- lockfile_set.specs.flat_map { |s| s.sources.map{ |src| src.uri.to_s } }
- else # FIXME: remove when 1.8 is dropped
- assert_equal %w[https://gems.example/ https://other.example/],
- lockfile_set.specs.map { |s| s.sources.map{ |src| src.uri.to_s } }.flatten(1)
- end
+ assert_equal %w[https://gems.example/ https://other.example/],
+ lockfile_set.specs.flat_map { |s| s.sources.map{ |src| src.uri.to_s } }
end
def test_parse_GIT
@@ -535,13 +530,13 @@ DEPENDENCIES
refute lockfile_set
end
- def write_lockfile lockfile
- open @lock_file, 'w' do |io|
+ def write_lockfile(lockfile)
+ File.open @lock_file, 'w' do |io|
io.write lockfile
end
end
- def parse_lockfile set, platforms
+ def parse_lockfile(set, platforms)
tokenizer = Gem::RequestSet::Lockfile::Tokenizer.from_file @lock_file
parser = tokenizer.make_parser set, platforms
parser.parse
diff --git a/test/rubygems/test_gem_request_set_lockfile_tokenizer.rb b/test/rubygems/test_gem_request_set_lockfile_tokenizer.rb
index ab506a14e6..48e66cf56a 100644
--- a/test/rubygems/test_gem_request_set_lockfile_tokenizer.rb
+++ b/test/rubygems/test_gem_request_set_lockfile_tokenizer.rb
@@ -294,8 +294,8 @@ GEM
assert_equal :token, parser.get
end
- def write_lockfile lockfile
- open @lock_file, 'w' do |io|
+ def write_lockfile(lockfile)
+ File.open @lock_file, 'w' do |io|
io.write lockfile
end
end
diff --git a/test/rubygems/test_gem_requirement.rb b/test/rubygems/test_gem_requirement.rb
index c110009840..7a59243b6a 100644
--- a/test/rubygems/test_gem_requirement.rb
+++ b/test/rubygems/test_gem_requirement.rb
@@ -20,6 +20,12 @@ class TestGemRequirement < Gem::TestCase
refute_requirement_equal "= 1.2", "= 1.3"
refute_requirement_equal "= 1.3", "= 1.2"
+ refute_requirement_equal "~> 1.3", "~> 1.3.0"
+ refute_requirement_equal "~> 1.3.0", "~> 1.3"
+
+ assert_requirement_equal ["> 2", "~> 1.3"], ["> 2.0", "~> 1.3"]
+ assert_requirement_equal ["> 2.0", "~> 1.3"], ["> 2", "~> 1.3"]
+
refute_equal Object.new, req("= 1.2")
refute_equal req("= 1.2"), Object.new
end
@@ -28,6 +34,14 @@ class TestGemRequirement < Gem::TestCase
assert_requirement_equal "= 2", "2"
assert_requirement_equal "= 2", ["2"]
assert_requirement_equal "= 2", v(2)
+ assert_requirement_equal "2.0", "2"
+ assert_requirement_equal ["= 2", ">= 2"], [">= 2", "= 2"]
+ end
+
+ def test_create
+ assert_equal req("= 1"), Gem::Requirement.create("= 1")
+ assert_equal req(">= 1.2", "<= 1.3"), Gem::Requirement.create([">= 1.2", "<= 1.3"])
+ assert_equal req(">= 1.2", "<= 1.3"), Gem::Requirement.create(">= 1.2", "<= 1.3")
end
def test_empty_requirements_is_none
@@ -63,6 +77,7 @@ class TestGemRequirement < Gem::TestCase
assert_equal ['=', Gem::Version.new(1)], Gem::Requirement.parse('= 1')
assert_equal ['>', Gem::Version.new(1)], Gem::Requirement.parse('> 1')
assert_equal ['=', Gem::Version.new(1)], Gem::Requirement.parse("=\n1")
+ assert_equal ['=', Gem::Version.new(1)], Gem::Requirement.parse('1.0')
assert_equal ['=', Gem::Version.new(2)],
Gem::Requirement.parse(Gem::Version.new('2'))
@@ -220,6 +235,8 @@ class TestGemRequirement < Gem::TestCase
assert_satisfied_by "0.2.33", "= 0.2.33"
assert_satisfied_by "0.2.34", "> 0.2.33"
assert_satisfied_by "1.0", "= 1.0"
+ assert_satisfied_by "1.0.0", "= 1.0"
+ assert_satisfied_by "1.0", "= 1.0.0"
assert_satisfied_by "1.0", "1.0"
assert_satisfied_by "1.8.2", "> 1.8.0"
assert_satisfied_by "1.112", "> 1.111"
@@ -307,6 +324,7 @@ class TestGemRequirement < Gem::TestCase
def test_satisfied_by_boxed
refute_satisfied_by "1.3", "~> 1.4"
assert_satisfied_by "1.4", "~> 1.4"
+ assert_satisfied_by "1.4.0", "~> 1.4"
assert_satisfied_by "1.5", "~> 1.4"
refute_satisfied_by "2.0", "~> 1.4"
@@ -318,6 +336,20 @@ class TestGemRequirement < Gem::TestCase
refute_satisfied_by "2.0", "~> 1.4.4"
end
+ def test_satisfied_by_explicitly_bounded
+ req = [">= 1.4.4", "< 1.5"]
+
+ assert_satisfied_by "1.4.5", req
+ assert_satisfied_by "1.5.0.rc1", req
+ refute_satisfied_by "1.5.0", req
+
+ req = [">= 1.4.4", "< 1.5.a"]
+
+ assert_satisfied_by "1.4.5", req
+ refute_satisfied_by "1.5.0.rc1", req
+ refute_satisfied_by "1.5.0", req
+ end
+
def test_specific
refute req('> 1') .specific?
refute req('>= 1').specific?
@@ -361,26 +393,26 @@ class TestGemRequirement < Gem::TestCase
# Assert that two requirements are equal. Handles Gem::Requirements,
# strings, arrays, numbers, and versions.
- def assert_requirement_equal expected, actual
+ def assert_requirement_equal(expected, actual)
assert_equal req(expected), req(actual)
end
# Assert that +version+ satisfies +requirement+.
- def assert_satisfied_by version, requirement
+ def assert_satisfied_by(version, requirement)
assert req(requirement).satisfied_by?(v(version)),
"#{requirement} is satisfied by #{version}"
end
# Refute the assumption that two requirements are equal.
- def refute_requirement_equal unexpected, actual
+ def refute_requirement_equal(unexpected, actual)
refute_equal req(unexpected), req(actual)
end
# Refute the assumption that +version+ satisfies +requirement+.
- def refute_satisfied_by version, requirement
+ def refute_satisfied_by(version, requirement)
refute req(requirement).satisfied_by?(v(version)),
"#{requirement} is not satisfied by #{version}"
end
diff --git a/test/rubygems/test_gem_resolver.rb b/test/rubygems/test_gem_resolver.rb
index cf457db198..fa1c595afd 100644
--- a/test/rubygems/test_gem_resolver.rb
+++ b/test/rubygems/test_gem_resolver.rb
@@ -23,7 +23,7 @@ class TestGemResolver < Gem::TestCase
StaticSet.new(specs)
end
- def assert_resolves_to expected, resolver
+ def assert_resolves_to(expected, resolver)
actual = resolver.resolve
exp = expected.sort_by { |s| s.full_name }
@@ -36,10 +36,6 @@ class TestGemResolver < Gem::TestCase
flunk e.message
end
- def test_self_compatibility
- assert_same Gem::Resolver, Gem::DependencyResolver
- end
-
def test_self_compose_sets_best_set
best_set = @DR::BestSet.new
@@ -303,7 +299,7 @@ class TestGemResolver < Gem::TestCase
a2_p1 = a3_p2 = nil
spec_fetcher do |fetcher|
- fetcher.spec 'a', 2
+ fetcher.spec 'a', 2
a2_p1 = fetcher.spec 'a', 2 do |s| s.platform = Gem::Platform.local end
a3_p2 = fetcher.spec 'a', 3 do |s| s.platform = unknown end
end
@@ -521,11 +517,11 @@ class TestGemResolver < Gem::TestCase
assert_equal req('>= 0'), dependency.requirement
activated = e.conflict.activated
- assert_equal 'c-2', activated.full_name
+ assert_equal 'c-1', activated.full_name
- assert_equal dep('c', '>= 2'), activated.request.dependency
+ assert_equal dep('c', '= 1'), activated.request.dependency
- assert_equal [dep('c', '= 1'), dep('c', '>= 2')],
+ assert_equal [dep('c', '>= 2'), dep('c', '= 1')],
e.conflict.conflicting_dependencies
end
@@ -666,12 +662,12 @@ class TestGemResolver < Gem::TestCase
end
def test_second_level_backout
- b1 = new_spec "b", "1", { "c" => ">= 1" }, "lib/b.rb"
- b2 = new_spec "b", "2", { "c" => ">= 2" }, "lib/b.rb"
- c1 = new_spec "c", "1"
- c2 = new_spec "c", "2"
- d1 = new_spec "d", "1", { "c" => "< 2" }, "lib/d.rb"
- d2 = new_spec "d", "2", { "c" => "< 2" }, "lib/d.rb"
+ b1 = util_spec "b", "1", { "c" => ">= 1" }, "lib/b.rb"
+ b2 = util_spec "b", "2", { "c" => ">= 2" }, "lib/b.rb"
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2"
+ d1 = util_spec "d", "1", { "c" => "< 2" }, "lib/d.rb"
+ d2 = util_spec "d", "2", { "c" => "< 2" }, "lib/d.rb"
s = set(b1, b2, c1, c2, d1, d2)
@@ -683,6 +679,32 @@ class TestGemResolver < Gem::TestCase
assert_resolves_to [b1, c1, d2], r
end
+ def test_sorts_by_source_then_version
+ sourceA = Gem::Source.new 'http://example.com/a'
+ sourceB = Gem::Source.new 'http://example.com/b'
+ sourceC = Gem::Source.new 'http://example.com/c'
+
+ spec_A_1 = util_spec 'some-dep', '0.0.1'
+ spec_A_2 = util_spec 'some-dep', '1.0.0'
+ spec_B_1 = util_spec 'some-dep', '0.0.1'
+ spec_B_2 = util_spec 'some-dep', '0.0.2'
+ spec_C_1 = util_spec 'some-dep', '0.1.0'
+
+ set = StaticSet.new [
+ Gem::Resolver::SpecSpecification.new(nil, spec_B_1, sourceB),
+ Gem::Resolver::SpecSpecification.new(nil, spec_B_2, sourceB),
+ Gem::Resolver::SpecSpecification.new(nil, spec_C_1, sourceC),
+ Gem::Resolver::SpecSpecification.new(nil, spec_A_2, sourceA),
+ Gem::Resolver::SpecSpecification.new(nil, spec_A_1, sourceA),
+ ]
+
+ dependency = make_dep 'some-dep', '> 0'
+
+ resolver = Gem::Resolver.new [dependency], set
+
+ assert_resolves_to [spec_B_2], resolver
+ end
+
def test_select_local_platforms
r = Gem::Resolver.new nil, nil
diff --git a/test/rubygems/test_gem_resolver_activation_request.rb b/test/rubygems/test_gem_resolver_activation_request.rb
index ee1b38887a..5d5de1a2d5 100644
--- a/test/rubygems/test_gem_resolver_activation_request.rb
+++ b/test/rubygems/test_gem_resolver_activation_request.rb
@@ -71,4 +71,3 @@ class TestGemResolverActivationRequest < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_api_set.rb b/test/rubygems/test_gem_resolver_api_set.rb
index 5746614039..f6b3e49c74 100644
--- a/test/rubygems/test_gem_resolver_api_set.rb
+++ b/test/rubygems/test_gem_resolver_api_set.rb
@@ -206,4 +206,3 @@ class TestGemResolverAPISet < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_api_specification.rb b/test/rubygems/test_gem_resolver_api_specification.rb
index 4f198ea395..87ff901320 100644
--- a/test/rubygems/test_gem_resolver_api_specification.rb
+++ b/test/rubygems/test_gem_resolver_api_specification.rb
@@ -141,5 +141,28 @@ class TestGemResolverAPISpecification < Gem::TestCase
assert_equal 'a-1', spec.full_name
end
-end
+ def test_spec_jruby_platform
+ spec_fetcher do |fetcher|
+ fetcher.gem 'j', 1 do |spec|
+ spec.platform = 'jruby'
+ end
+ end
+ dep_uri = URI(@gem_repo) + 'api/v1/dependencies'
+ set = Gem::Resolver::APISet.new dep_uri
+ data = {
+ :name => 'j',
+ :number => '1',
+ :platform => 'jruby',
+ :dependencies => [],
+ }
+
+ api_spec = Gem::Resolver::APISpecification.new set, data
+
+ spec = api_spec.spec
+
+ assert_kind_of Gem::Specification, spec
+ assert_equal 'j-1-java', spec.full_name
+ end
+
+end
diff --git a/test/rubygems/test_gem_resolver_best_set.rb b/test/rubygems/test_gem_resolver_best_set.rb
index 556f0e8349..dc6c9b4c44 100644
--- a/test/rubygems/test_gem_resolver_best_set.rb
+++ b/test/rubygems/test_gem_resolver_best_set.rb
@@ -135,4 +135,3 @@ class TestGemResolverBestSet < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_composed_set.rb b/test/rubygems/test_gem_resolver_composed_set.rb
index f1c2da0454..0e745433a9 100644
--- a/test/rubygems/test_gem_resolver_composed_set.rb
+++ b/test/rubygems/test_gem_resolver_composed_set.rb
@@ -43,4 +43,3 @@ class TestGemResolverComposedSet < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_conflict.rb b/test/rubygems/test_gem_resolver_conflict.rb
index 56f048d91b..a078136510 100644
--- a/test/rubygems/test_gem_resolver_conflict.rb
+++ b/test/rubygems/test_gem_resolver_conflict.rb
@@ -15,7 +15,7 @@ class TestGemResolverConflict < Gem::TestCase
dep = Gem::Resolver::DependencyRequest.new dep('net-ssh', '>= 2.0.13'), nil
- spec = quick_spec 'net-ssh', '2.2.2'
+ spec = util_spec 'net-ssh', '2.2.2'
active =
Gem::Resolver::ActivationRequest.new spec, dep
@@ -85,4 +85,3 @@ class TestGemResolverConflict < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_dependency_request.rb b/test/rubygems/test_gem_resolver_dependency_request.rb
index ac8f48a526..51f0be9dcd 100644
--- a/test/rubygems/test_gem_resolver_dependency_request.rb
+++ b/test/rubygems/test_gem_resolver_dependency_request.rb
@@ -82,4 +82,3 @@ class TestGemResolverDependencyRequest < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_git_set.rb b/test/rubygems/test_gem_resolver_git_set.rb
index b87a80d44e..f38859c8b4 100644
--- a/test/rubygems/test_gem_resolver_git_set.rb
+++ b/test/rubygems/test_gem_resolver_git_set.rb
@@ -187,4 +187,3 @@ class TestGemResolverGitSet < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_git_specification.rb b/test/rubygems/test_gem_resolver_git_specification.rb
index 9e8e2c5715..3a9bb6a802 100644
--- a/test/rubygems/test_gem_resolver_git_specification.rb
+++ b/test/rubygems/test_gem_resolver_git_specification.rb
@@ -70,7 +70,7 @@ class TestGemResolverGitSpecification < Gem::TestCase
Dir.chdir 'git/a' do
FileUtils.mkdir_p 'ext/lib'
- open 'ext/extconf.rb', 'w' do |io|
+ File.open 'ext/extconf.rb', 'w' do |io|
io.puts 'require "mkmf"'
io.puts 'create_makefile "a"'
end
@@ -111,4 +111,3 @@ class TestGemResolverGitSpecification < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_index_set.rb b/test/rubygems/test_gem_resolver_index_set.rb
index 0f18572904..d9a30d0f58 100644
--- a/test/rubygems/test_gem_resolver_index_set.rb
+++ b/test/rubygems/test_gem_resolver_index_set.rb
@@ -87,4 +87,3 @@ class TestGemResolverIndexSet < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_index_specification.rb b/test/rubygems/test_gem_resolver_index_specification.rb
index 6565892750..3768dd9bae 100644
--- a/test/rubygems/test_gem_resolver_index_specification.rb
+++ b/test/rubygems/test_gem_resolver_index_specification.rb
@@ -87,4 +87,3 @@ class TestGemResolverIndexSpecification < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_installed_specification.rb b/test/rubygems/test_gem_resolver_installed_specification.rb
index 1dfbb054f3..2b54fbc629 100644
--- a/test/rubygems/test_gem_resolver_installed_specification.rb
+++ b/test/rubygems/test_gem_resolver_installed_specification.rb
@@ -47,4 +47,3 @@ class TestGemResolverInstalledSpecification < Gem::TestCase
end
-
diff --git a/test/rubygems/test_gem_resolver_installer_set.rb b/test/rubygems/test_gem_resolver_installer_set.rb
index e8d82d93b7..50350e2d3d 100644
--- a/test/rubygems/test_gem_resolver_installer_set.rb
+++ b/test/rubygems/test_gem_resolver_installer_set.rb
@@ -25,8 +25,8 @@ class TestGemResolverInstallerSet < Gem::TestCase
end
def test_add_always_install_errors
- @fetcher = Gem::FakeFetcher.new
- Gem::RemoteFetcher.fetcher = @fetcher
+ @stub_fetcher = Gem::FakeFetcher.new
+ Gem::RemoteFetcher.fetcher = @stub_fetcher
set = Gem::Resolver::InstallerSet.new :both
@@ -197,7 +197,7 @@ class TestGemResolverInstallerSet < Gem::TestCase
def (set.remote_set).prefetch(_)
raise "called"
end
- assert_equal nil, set.prefetch(nil)
+ assert_nil set.prefetch(nil)
end
def test_prerelease_equals
@@ -255,4 +255,3 @@ class TestGemResolverInstallerSet < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_local_specification.rb b/test/rubygems/test_gem_resolver_local_specification.rb
index d04d1ec8b3..82598f5188 100644
--- a/test/rubygems/test_gem_resolver_local_specification.rb
+++ b/test/rubygems/test_gem_resolver_local_specification.rb
@@ -43,4 +43,3 @@ class TestGemResolverLocalSpecification < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_lock_set.rb b/test/rubygems/test_gem_resolver_lock_set.rb
index 969f13fa8b..d5258477d7 100644
--- a/test/rubygems/test_gem_resolver_lock_set.rb
+++ b/test/rubygems/test_gem_resolver_lock_set.rb
@@ -61,4 +61,3 @@ class TestGemResolverLockSet < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_lock_specification.rb b/test/rubygems/test_gem_resolver_lock_specification.rb
index 3105ad65b3..7b9b0ac8f7 100644
--- a/test/rubygems/test_gem_resolver_lock_specification.rb
+++ b/test/rubygems/test_gem_resolver_lock_specification.rb
@@ -97,4 +97,3 @@ class TestGemResolverLockSpecification < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_requirement_list.rb b/test/rubygems/test_gem_resolver_requirement_list.rb
index 6361dc32e6..4cbb939199 100644
--- a/test/rubygems/test_gem_resolver_requirement_list.rb
+++ b/test/rubygems/test_gem_resolver_requirement_list.rb
@@ -18,4 +18,3 @@ class TestGemResolverRequirementList < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_specification.rb b/test/rubygems/test_gem_resolver_specification.rb
index 50ac7ec3a1..c184cea352 100644
--- a/test/rubygems/test_gem_resolver_specification.rb
+++ b/test/rubygems/test_gem_resolver_specification.rb
@@ -7,7 +7,7 @@ class TestGemResolverSpecification < Gem::TestCase
attr_writer :source
attr_reader :spec
- def initialize spec
+ def initialize(spec)
super()
@spec = spec
@@ -62,4 +62,3 @@ class TestGemResolverSpecification < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_vendor_set.rb b/test/rubygems/test_gem_resolver_vendor_set.rb
index c5b9ad019f..3fc79fec16 100644
--- a/test/rubygems/test_gem_resolver_vendor_set.rb
+++ b/test/rubygems/test_gem_resolver_vendor_set.rb
@@ -81,4 +81,3 @@ class TestGemResolverVendorSet < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_resolver_vendor_specification.rb b/test/rubygems/test_gem_resolver_vendor_specification.rb
index 1f9337c3fa..315ce05539 100644
--- a/test/rubygems/test_gem_resolver_vendor_specification.rb
+++ b/test/rubygems/test_gem_resolver_vendor_specification.rb
@@ -81,4 +81,3 @@ class TestGemResolverVendorSpecification < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_security.rb b/test/rubygems/test_gem_security.rb
index acd83f4dca..b5a887abb8 100644
--- a/test/rubygems/test_gem_security.rb
+++ b/test/rubygems/test_gem_security.rb
@@ -1,9 +1,8 @@
# frozen_string_literal: true
require 'rubygems/test_case'
require 'rubygems/security'
-require 'rubygems/fix_openssl_warnings' if RUBY_VERSION < "1.9"
-unless defined?(OpenSSL::SSL) then
+unless defined?(OpenSSL::SSL)
warn 'Skipping Gem::Security tests. openssl not found.'
end
@@ -61,6 +60,7 @@ class TestGemSecurity < Gem::TestCase
cert = @SEC.create_cert_self_signed subject, PRIVATE_KEY, 60
assert_equal '/CN=nobody/DC=example', cert.issuer.to_s
+ assert_equal "sha256WithRSAEncryption", cert.signature_algorithm
end
def test_class_create_cert_email
@@ -120,6 +120,7 @@ class TestGemSecurity < Gem::TestCase
end
def test_class_re_sign
+ assert_equal "sha1WithRSAEncryption", EXPIRED_CERT.signature_algorithm
re_signed = Gem::Security.re_sign EXPIRED_CERT, PRIVATE_KEY, 60
assert_in_delta Time.now, re_signed.not_before, 10
@@ -127,6 +128,7 @@ class TestGemSecurity < Gem::TestCase
assert_equal EXPIRED_CERT.serial + 1, re_signed.serial
assert re_signed.verify PUBLIC_KEY
+ assert_equal "sha256WithRSAEncryption", re_signed.signature_algorithm
end
def test_class_re_sign_not_self_signed
@@ -218,6 +220,8 @@ class TestGemSecurity < Gem::TestCase
assert_in_delta Time.now, signed.not_before, 10
assert_in_delta Time.now + 60, signed.not_after, 10
+ assert_equal "sha256WithRSAEncryption", signed.signature_algorithm
+
assert_equal 5, signed.extensions.length,
signed.extensions.map { |e| e.to_a.first }
@@ -304,4 +308,3 @@ class TestGemSecurity < Gem::TestCase
end
end if defined?(OpenSSL::SSL)
-
diff --git a/test/rubygems/test_gem_security_policy.rb b/test/rubygems/test_gem_security_policy.rb
index bee0973f64..8a1676cd3c 100644
--- a/test/rubygems/test_gem_security_policy.rb
+++ b/test/rubygems/test_gem_security_policy.rb
@@ -1,9 +1,9 @@
-# coding: UTF-8
+# coding: utf-8
# frozen_string_literal: true
require 'rubygems/test_case'
-unless defined?(OpenSSL::SSL) then
+unless defined?(OpenSSL::SSL)
warn 'Skipping Gem::Security::Policy tests. openssl not found.'
end
@@ -34,7 +34,7 @@ class TestGemSecurityPolicy < Gem::TestCase
s.files = %w[lib/code.rb]
end
- @sha1 = OpenSSL::Digest::SHA1
+ @digest = Gem::Security::DIGEST_ALGORITHM
@trust_dir = Gem::Security.trust_dir.dir # HACK use the object
@no = Gem::Security::NoSecurity
@@ -69,7 +69,7 @@ class TestGemSecurityPolicy < Gem::TestCase
signature = sign data
- assert @almost_no.check_data(PUBLIC_KEY, @sha1, signature, data)
+ assert @almost_no.check_data(PUBLIC_KEY, @digest, signature, data)
end
def test_check_data_invalid
@@ -80,7 +80,7 @@ class TestGemSecurityPolicy < Gem::TestCase
invalid = digest 'hello!'
e = assert_raises Gem::Security::Exception do
- @almost_no.check_data PUBLIC_KEY, @sha1, signature, invalid
+ @almost_no.check_data PUBLIC_KEY, @digest, signature, invalid
end
assert_equal 'invalid signature', e.message
@@ -238,18 +238,18 @@ class TestGemSecurityPolicy < Gem::TestCase
def test_check_trust
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
- assert @high.check_trust [PUBLIC_CERT], @sha1, @trust_dir
+ assert @high.check_trust [PUBLIC_CERT], @digest, @trust_dir
end
def test_check_trust_child
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
- assert @high.check_trust [PUBLIC_CERT, CHILD_CERT], @sha1, @trust_dir
+ assert @high.check_trust [PUBLIC_CERT, CHILD_CERT], @digest, @trust_dir
end
def test_check_trust_empty_chain
e = assert_raises Gem::Security::Exception do
- @chain.check_trust [], @sha1, @trust_dir
+ @chain.check_trust [], @digest, @trust_dir
end
assert_equal 'missing root certificate', e.message
@@ -259,7 +259,7 @@ class TestGemSecurityPolicy < Gem::TestCase
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
e = assert_raises Gem::Security::Exception do
- @high.check_trust [WRONG_KEY_CERT], @sha1, @trust_dir
+ @high.check_trust [WRONG_KEY_CERT], @digest, @trust_dir
end
assert_equal "trusted root certificate #{PUBLIC_CERT.subject} checksum " +
@@ -268,7 +268,7 @@ class TestGemSecurityPolicy < Gem::TestCase
def test_check_trust_no_chain
e = assert_raises Gem::Security::Exception do
- @chain.check_trust nil, @sha1, @trust_dir
+ @chain.check_trust nil, @digest, @trust_dir
end
assert_equal 'missing signing chain', e.message
@@ -276,7 +276,7 @@ class TestGemSecurityPolicy < Gem::TestCase
def test_check_trust_no_trust
e = assert_raises Gem::Security::Exception do
- @high.check_trust [PUBLIC_CERT], @sha1, @trust_dir
+ @high.check_trust [PUBLIC_CERT], @digest, @trust_dir
end
assert_equal "root cert #{PUBLIC_CERT.subject} is not trusted", e.message
@@ -284,7 +284,7 @@ class TestGemSecurityPolicy < Gem::TestCase
def test_check_trust_no_trust_child
e = assert_raises Gem::Security::Exception do
- @high.check_trust [PUBLIC_CERT, CHILD_CERT], @sha1, @trust_dir
+ @high.check_trust [PUBLIC_CERT, CHILD_CERT], @digest, @trust_dir
end
assert_equal "root cert #{PUBLIC_CERT.subject} is not trusted " +
@@ -293,7 +293,7 @@ class TestGemSecurityPolicy < Gem::TestCase
def test_subject
assert_equal 'email:nobody@example', @no.subject(PUBLIC_CERT)
- assert_equal '/C=JP/O=JIN.GR.JP/OU=RRR/CN=CA', @no.subject(CA_CERT)
+ assert_equal '/C=JP/ST=Tokyo/O=RubyGemsTest/CN=CA', @no.subject(CA_CERT)
end
def test_verify
@@ -370,7 +370,7 @@ class TestGemSecurityPolicy < Gem::TestCase
data = digest 'goodbye'
- signatures[1] = PRIVATE_KEY.sign @sha1.new, data.digest
+ signatures[1] = PRIVATE_KEY.sign @digest.new, data.digest
e = assert_raises Gem::Security::Exception do
@almost_no.verify [PUBLIC_CERT], nil, digests, signatures
@@ -450,20 +450,20 @@ class TestGemSecurityPolicy < Gem::TestCase
@spec.cert_chain = [PUBLIC_CERT.to_s]
- metadata_gz = Gem.gzip @spec.to_yaml
+ metadata_gz = Gem::Util.gzip @spec.to_yaml
package = Gem::Package.new 'nonexistent.gem'
- package.checksums['SHA1'] = {}
+ package.checksums[Gem::Security::DIGEST_NAME] = {}
s = StringIO.new metadata_gz
def s.full_name() 'metadata.gz' end
digests = package.digest s
- metadata_gz_digest = digests['SHA1']['metadata.gz']
+ metadata_gz_digest = digests[Gem::Security::DIGEST_NAME]['metadata.gz']
signatures = {}
signatures['metadata.gz'] =
- PRIVATE_KEY.sign @sha1.new, metadata_gz_digest.digest
+ PRIVATE_KEY.sign @digest.new, metadata_gz_digest.digest
assert @high.verify_signatures @spec, digests, signatures
end
@@ -473,22 +473,22 @@ class TestGemSecurityPolicy < Gem::TestCase
@spec.cert_chain = [PUBLIC_CERT.to_s]
- metadata_gz = Gem.gzip @spec.to_yaml
+ metadata_gz = Gem::Util.gzip @spec.to_yaml
package = Gem::Package.new 'nonexistent.gem'
- package.checksums['SHA1'] = {}
+ package.checksums[Gem::Security::DIGEST_NAME] = {}
s = StringIO.new metadata_gz
def s.full_name() 'metadata.gz' end
digests = package.digest s
- digests['SHA1']['data.tar.gz'] = OpenSSL::Digest.new 'SHA1', 'hello'
+ digests[Gem::Security::DIGEST_NAME]['data.tar.gz'] = @digest.new 'hello'
- metadata_gz_digest = digests['SHA1']['metadata.gz']
+ metadata_gz_digest = digests[Gem::Security::DIGEST_NAME]['metadata.gz']
signatures = {}
signatures['metadata.gz'] =
- PRIVATE_KEY.sign @sha1.new, metadata_gz_digest.digest
+ PRIVATE_KEY.sign @digest.new, metadata_gz_digest.digest
e = assert_raises Gem::Security::Exception do
@high.verify_signatures @spec, digests, signatures
@@ -502,40 +502,39 @@ class TestGemSecurityPolicy < Gem::TestCase
@spec.cert_chain = [PUBLIC_CERT.to_s]
- metadata_gz = Gem.gzip @spec.to_yaml
+ metadata_gz = Gem::Util.gzip @spec.to_yaml
package = Gem::Package.new 'nonexistent.gem'
- package.checksums['SHA1'] = {}
+ package.checksums[Gem::Security::DIGEST_NAME] = {}
s = StringIO.new metadata_gz
def s.full_name() 'metadata.gz' end
digests = package.digest s
- digests['SHA1']['data.tar.gz'] = OpenSSL::Digest.new 'SHA1', 'hello'
+ digests[Gem::Security::DIGEST_NAME]['data.tar.gz'] = @digest.new 'hello'
assert_raises Gem::Security::Exception do
@high.verify_signatures @spec, digests, {}
end
end
- def digest data
- digester = @sha1.new
+ def digest(data)
+ digester = @digest.new
digester << data
digester
end
- def sign data, key = PRIVATE_KEY
- key.sign @sha1.new, data.digest
+ def sign(data, key = PRIVATE_KEY)
+ key.sign @digest.new, data.digest
end
- def dummy_signatures key = PRIVATE_KEY
+ def dummy_signatures(key = PRIVATE_KEY)
data = digest 'hello'
- digests = { 'SHA1' => { 0 => data } }
+ digests = { Gem::Security::DIGEST_NAME => { 0 => data } }
signatures = { 0 => sign(data, key) }
return digests, signatures
end
end if defined?(OpenSSL::SSL)
-
diff --git a/test/rubygems/test_gem_security_signer.rb b/test/rubygems/test_gem_security_signer.rb
index 685a13949e..631afa87c4 100644
--- a/test/rubygems/test_gem_security_signer.rb
+++ b/test/rubygems/test_gem_security_signer.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'rubygems/test_case'
-unless defined?(OpenSSL::SSL) then
+unless defined?(OpenSSL::SSL)
warn 'Skipping Gem::Security::Signer tests. openssl not found.'
end
@@ -121,12 +121,12 @@ class TestGemSecuritySigner < Gem::TestCase
signature = signer.sign 'hello'
expected = <<-EXPECTED
-pxSf9ScaghbMNmNp8fqSJj7BiIGpbuoOVYCOM3TJNH9STLILA5z3xKp3gM6w
-VJ7aGsh9KCP485ftS3J9Kb/lKJsyoSkkRSQ5QG+LnyZwMuWlThPDR5o7q6al
-0oxE7vvbbqxFqcT4ojWIkwxJxOluFWmt2D8I6QTX2vLAn09y+Kl66AOrT7R5
-UinbXkz04VwcNvkBqJyko3yWxFKiGNpntZQg4jIw4L+h97EOaZp8H96udzQH
-Da3K0YZ6FsqLDFNnWAFhve3kmpE3CludpvDqH0piq0zKqnOiqAcvICIpPaJP
-c7NM7KZZjj7G++SXjYTEI1PHSA7aFQ/i/+qSUvx+Pg==
+cHze2sEfRysoUMCfGVAx/7o8jxj5liJJ2ptNxe2jf3l+EZvyjdqpXo9Ndzxx
+6xLp2rxLG4K2//ip4aCH5Sh7hnia+F5u6iuLBETPlklPrmw5dnuKZxolz+vM
+0O1aOZsQHcVzQoESTGjkms3KZk+gn3lg0sSBbAV5/LyDYoHCEjxlcA5D+Olb
+rDmRyBMOnMS+q489OZ5Hr6B2YJJ3QbUwIZNhUeNmOxIBEYTrrKkZ92qkxbRN
+qhlqFP4jR6zXFeyBCOr0KpTiWBNuxBFXDsxmhGyt2BOIjD6qmKn7RSIfYg/U
+toqvglr0kdbknSRRjBVLK6tsgr07aLT9gNP7mTW2PA==
EXPECTED
assert_equal expected, [signature].pack('m')
@@ -135,9 +135,11 @@ c7NM7KZZjj7G++SXjYTEI1PHSA7aFQ/i/+qSUvx+Pg==
def test_sign_expired
signer = Gem::Security::Signer.new PRIVATE_KEY, [EXPIRED_CERT]
- assert_raises Gem::Security::Exception do
+ e = assert_raises Gem::Security::Exception do
signer.sign 'hello'
end
+
+ assert_match "certificate /CN=nobody/DC=example not valid after 1970-01-01 00:00:00 UTC", e.message
end
def test_sign_expired_auto_update
@@ -214,4 +216,3 @@ c7NM7KZZjj7G++SXjYTEI1PHSA7aFQ/i/+qSUvx+Pg==
end
end if defined?(OpenSSL::SSL)
-
diff --git a/test/rubygems/test_gem_security_trust_dir.rb b/test/rubygems/test_gem_security_trust_dir.rb
index a0fbef809a..f2b7b006dd 100644
--- a/test/rubygems/test_gem_security_trust_dir.rb
+++ b/test/rubygems/test_gem_security_trust_dir.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'rubygems/test_case'
-unless defined?(OpenSSL::SSL) then
+unless defined?(OpenSSL::SSL)
warn 'Skipping Gem::Security::TrustDir tests. openssl not found.'
end
@@ -18,7 +18,7 @@ class TestGemSecurityTrustDir < Gem::TestCase
end
def test_cert_path
- digest = OpenSSL::Digest::SHA1.hexdigest PUBLIC_CERT.subject.to_s
+ digest = Gem::Security::DIGEST_ALGORITHM.hexdigest PUBLIC_CERT.subject.to_s
expected = File.join @dest_dir, "cert-#{digest}.pem"
@@ -42,7 +42,7 @@ class TestGemSecurityTrustDir < Gem::TestCase
end
def test_name_path
- digest = OpenSSL::Digest::SHA1.hexdigest PUBLIC_CERT.subject.to_s
+ digest = Gem::Security::DIGEST_ALGORITHM.hexdigest PUBLIC_CERT.subject.to_s
expected = File.join @dest_dir, "cert-#{digest}.pem"
@@ -98,4 +98,3 @@ class TestGemSecurityTrustDir < Gem::TestCase
end
end if defined?(OpenSSL::SSL)
-
diff --git a/test/rubygems/test_gem_server.rb b/test/rubygems/test_gem_server.rb
index 0ece6d67ef..8a3e6410ae 100644
--- a/test/rubygems/test_gem_server.rb
+++ b/test/rubygems/test_gem_server.rb
@@ -100,7 +100,7 @@ class TestGemServer < Gem::TestCase
specs_dir = File.join dir, 'specifications'
FileUtils.mkdir_p specs_dir
- open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ File.open File.join(specs_dir, spec.spec_name), 'w' do |io|
io.write spec.to_ruby
end
@@ -127,7 +127,7 @@ class TestGemServer < Gem::TestCase
assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'application/x-gzip', @res['content-type']
assert_equal [['a', Gem::Version.new(2), Gem::Platform::RUBY]],
- Marshal.load(Gem.gunzip(@res.body))
+ Marshal.load(Gem::Util.gunzip(@res.body))
end
def test_listen
@@ -177,7 +177,7 @@ class TestGemServer < Gem::TestCase
assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'application/x-gzip', @res['content-type']
assert_equal [['a', v('3.a'), Gem::Platform::RUBY]],
- Marshal.load(Gem.gunzip(@res.body))
+ Marshal.load(Gem::Util.gunzip(@res.body))
end
def test_quick_gemdirs
@@ -198,7 +198,7 @@ class TestGemServer < Gem::TestCase
FileUtils.mkdir_p specs_dir
- open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ File.open File.join(specs_dir, spec.spec_name), 'w' do |io|
io.write spec.to_ruby
end
@@ -222,7 +222,7 @@ class TestGemServer < Gem::TestCase
assert_equal 404, @res.status, @res.body
assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'text/plain', @res['content-type']
- assert_equal 'No gems found matching "z" "9" nil', @res.body
+ assert_equal 'No gems found matching "z-9"', @res.body
assert_equal 404, @res.status
end
@@ -236,7 +236,7 @@ class TestGemServer < Gem::TestCase
assert @res['date']
assert_equal 'application/x-deflate', @res['content-type']
- spec = Marshal.load Gem.inflate(@res.body)
+ spec = Marshal.load Gem::Util.inflate(@res.body)
assert_equal 'a', spec.name
assert_equal Gem::Version.new(1), spec.version
end
@@ -253,7 +253,7 @@ class TestGemServer < Gem::TestCase
assert @res['date']
assert_equal 'application/x-deflate', @res['content-type']
- spec = Marshal.load Gem.inflate(@res.body)
+ spec = Marshal.load Gem::Util.inflate(@res.body)
assert_equal 'a', spec.name
assert_equal Gem::Version.new(1), spec.version
assert_equal Gem::Platform.local, spec.platform
@@ -269,7 +269,7 @@ class TestGemServer < Gem::TestCase
assert @res['date']
assert_equal 'application/x-deflate', @res['content-type']
- spec = Marshal.load Gem.inflate(@res.body)
+ spec = Marshal.load Gem::Util.inflate(@res.body)
assert_equal 'a', spec.name
assert_equal v('3.a'), spec.version
end
@@ -286,11 +286,28 @@ class TestGemServer < Gem::TestCase
assert @res['date']
assert_equal 'application/x-deflate', @res['content-type']
- spec = Marshal.load Gem.inflate(@res.body)
+ spec = Marshal.load Gem::Util.inflate(@res.body)
assert_equal 'a-b', spec.name
assert_equal v('3.a'), spec.version
end
+ def test_quick_marshal_a_b_1_3_a_gemspec_rz
+ quick_gem 'a-b-1', '3.a'
+
+ data = StringIO.new "GET /quick/Marshal.#{Gem.marshal_version}/a-b-1-3.a.gemspec.rz HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.quick @req, @res
+
+ assert_equal 200, @res.status, @res.body
+ assert @res['date']
+ assert_equal 'application/x-deflate', @res['content-type']
+
+ spec = Marshal.load Gem::Util.inflate(@res.body)
+ assert_equal 'a-b-1', spec.name
+ assert_equal v('3.a'), spec.version
+ end
+
def test_rdoc
data = StringIO.new "GET /rdoc?q=a HTTP/1.0\r\n\r\n"
@req.parse data
@@ -322,7 +339,7 @@ class TestGemServer < Gem::TestCase
specs_dir = File.join dir, 'specifications'
FileUtils.mkdir_p specs_dir
- open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ File.open File.join(specs_dir, spec.spec_name), 'w' do |io|
io.write spec.to_ruby
end
@@ -336,6 +353,171 @@ class TestGemServer < Gem::TestCase
assert_match 'z 9', @res.body
end
+
+ def test_xss_homepage_fix_289313
+ data = StringIO.new "GET / HTTP/1.0\r\n\r\n"
+ dir = "#{@gemhome}2"
+
+ spec = util_spec 'xsshomepagegem', 1
+ spec.homepage = "javascript:confirm(document.domain)"
+
+ specs_dir = File.join dir, 'specifications'
+ FileUtils.mkdir_p specs_dir
+
+ open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ io.write spec.to_ruby
+ end
+
+ server = Gem::Server.new dir, process_based_port, false
+
+ @req.parse data
+
+ server.root @req, @res
+
+ assert_equal 200, @res.status
+ assert_match 'xsshomepagegem 1', @res.body
+
+ # This verifies that the homepage for this spec is not displayed and is set to ".", because it's not a
+ # valid HTTP/HTTPS URL and could be unsafe in an HTML context. We would prefer to throw an exception here,
+ # but spec.homepage is currently free form and not currently required to be a URL, this behavior may be
+ # validated in future versions of Gem::Specification.
+ #
+ # There are two variant we're checking here, one where rdoc is not present, and one where rdoc is present in the same regex:
+ #
+ # Variant #1 - rdoc not installed
+ #
+ # <b>xsshomepagegem 1</b>
+ #
+ #
+ # <span title="rdoc not installed">[rdoc]</span>
+ #
+ #
+ #
+ # <a href="." title=".">[www]</a>
+ #
+ # Variant #2 - rdoc installed
+ #
+ # <b>xsshomepagegem 1</b>
+ #
+ #
+ # <a href="\/doc_root\/xsshomepagegem-1\/">\[rdoc\]<\/a>
+ #
+ #
+ #
+ # <a href="." title=".">[www]</a>
+ regex_match = /xsshomepagegem 1<\/b>\s+(<span title="rdoc not installed">\[rdoc\]<\/span>|<a href="\/doc_root\/xsshomepagegem-1\/">\[rdoc\]<\/a>)\s+<a href="\." title="\.">\[www\]<\/a>/
+ assert_match regex_match, @res.body
+ end
+
+ def test_invalid_homepage
+ data = StringIO.new "GET / HTTP/1.0\r\n\r\n"
+ dir = "#{@gemhome}2"
+
+ spec = util_spec 'invalidhomepagegem', 1
+ spec.homepage = "notavalidhomepageurl"
+
+ specs_dir = File.join dir, 'specifications'
+ FileUtils.mkdir_p specs_dir
+
+ open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ io.write spec.to_ruby
+ end
+
+ server = Gem::Server.new dir, process_based_port, false
+
+ @req.parse data
+
+ server.root @req, @res
+
+ assert_equal 200, @res.status
+ assert_match 'invalidhomepagegem 1', @res.body
+
+ # This verifies that the homepage for this spec is not displayed and is set to ".", because it's not a
+ # valid HTTP/HTTPS URL and could be unsafe in an HTML context. We would prefer to throw an exception here,
+ # but spec.homepage is currently free form and not currently required to be a URL, this behavior may be
+ # validated in future versions of Gem::Specification.
+ #
+ # There are two variant we're checking here, one where rdoc is not present, and one where rdoc is present in the same regex:
+ #
+ # Variant #1 - rdoc not installed
+ #
+ # <b>invalidhomepagegem 1</b>
+ #
+ #
+ # <span title="rdoc not installed">[rdoc]</span>
+ #
+ #
+ #
+ # <a href="." title=".">[www]</a>
+ #
+ # Variant #2 - rdoc installed
+ #
+ # <b>invalidhomepagegem 1</b>
+ #
+ #
+ # <a href="\/doc_root\/invalidhomepagegem-1\/">\[rdoc\]<\/a>
+ #
+ #
+ #
+ # <a href="." title=".">[www]</a>
+ regex_match = /invalidhomepagegem 1<\/b>\s+(<span title="rdoc not installed">\[rdoc\]<\/span>|<a href="\/doc_root\/invalidhomepagegem-1\/">\[rdoc\]<\/a>)\s+<a href="\." title="\.">\[www\]<\/a>/
+ assert_match regex_match, @res.body
+ end
+
+ def test_valid_homepage_http
+ data = StringIO.new "GET / HTTP/1.0\r\n\r\n"
+ dir = "#{@gemhome}2"
+
+ spec = util_spec 'validhomepagegemhttp', 1
+ spec.homepage = "http://rubygems.org"
+
+ specs_dir = File.join dir, 'specifications'
+ FileUtils.mkdir_p specs_dir
+
+ open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ io.write spec.to_ruby
+ end
+
+ server = Gem::Server.new dir, process_based_port, false
+
+ @req.parse data
+
+ server.root @req, @res
+
+ assert_equal 200, @res.status
+ assert_match 'validhomepagegemhttp 1', @res.body
+
+ regex_match = /validhomepagegemhttp 1<\/b>\s+(<span title="rdoc not installed">\[rdoc\]<\/span>|<a href="\/doc_root\/validhomepagegemhttp-1\/">\[rdoc\]<\/a>)\s+<a href="http:\/\/rubygems\.org" title="http:\/\/rubygems\.org">\[www\]<\/a>/
+ assert_match regex_match, @res.body
+ end
+
+ def test_valid_homepage_https
+ data = StringIO.new "GET / HTTP/1.0\r\n\r\n"
+ dir = "#{@gemhome}2"
+
+ spec = util_spec 'validhomepagegemhttps', 1
+ spec.homepage = "https://rubygems.org"
+
+ specs_dir = File.join dir, 'specifications'
+ FileUtils.mkdir_p specs_dir
+
+ open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ io.write spec.to_ruby
+ end
+
+ server = Gem::Server.new dir, process_based_port, false
+
+ @req.parse data
+
+ server.root @req, @res
+
+ assert_equal 200, @res.status
+ assert_match 'validhomepagegemhttps 1', @res.body
+
+ regex_match = /validhomepagegemhttps 1<\/b>\s+(<span title="rdoc not installed">\[rdoc\]<\/span>|<a href="\/doc_root\/validhomepagegemhttps-1\/">\[rdoc\]<\/a>)\s+<a href="https:\/\/rubygems\.org" title="https:\/\/rubygems\.org">\[www\]<\/a>/
+ assert_match regex_match, @res.body
+ end
+
def test_specs
data = StringIO.new "GET /specs.#{Gem.marshal_version} HTTP/1.0\r\n\r\n"
@req.parse data
@@ -361,7 +543,7 @@ class TestGemServer < Gem::TestCase
specs_dir = File.join dir, 'specifications'
FileUtils.mkdir_p specs_dir
- open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ File.open File.join(specs_dir, spec.spec_name), 'w' do |io|
io.write spec.to_ruby
end
@@ -389,7 +571,23 @@ class TestGemServer < Gem::TestCase
assert_equal [['a', Gem::Version.new(1), Gem::Platform::RUBY],
['a', Gem::Version.new(2), Gem::Platform::RUBY],
['a', v('3.a'), Gem::Platform::RUBY]],
- Marshal.load(Gem.gunzip(@res.body))
+ Marshal.load(Gem::Util.gunzip(@res.body))
+ end
+
+ def test_uri_encode
+ url_safe = @server.uri_encode 'http://rubyonrails.org/">malicious_content</a>'
+ assert_equal url_safe, 'http://rubyonrails.org/%22%3Emalicious_content%3C/a%3E'
+ end
+
+ # Regression test for issue #1793: incorrect URL encoding.
+ # Checking that no URLs have had '://' incorrectly encoded
+ def test_regression_1793
+ data = StringIO.new "GET / HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.root @req, @res
+
+ refute_match %r|%3A%2F%2F|, @res.body
end
def util_listen
diff --git a/test/rubygems/test_gem_source.rb b/test/rubygems/test_gem_source.rb
index 64dfa42468..f8cd031c72 100644
--- a/test/rubygems/test_gem_source.rb
+++ b/test/rubygems/test_gem_source.rb
@@ -36,18 +36,6 @@ class TestGemSource < Gem::TestCase
assert_equal repository, source.uri
end
- def test_api_uri
- assert_equal @source.api_uri, @source.uri
- end
-
- def test_api_uri_resolved_from_remote_fetcher
- uri = URI.parse "http://gem.example/foo"
- @fetcher.api_endpoints[uri] = URI.parse "http://api.blah"
-
- src = Gem::Source.new uri
- assert_equal URI.parse("http://api.blah"), src.api_uri
- end
-
def test_cache_dir_escapes_windows_paths
uri = URI.parse("file:///C:/WINDOWS/Temp/gem_repo")
root = Gem.spec_cache_dir
@@ -110,7 +98,7 @@ class TestGemSource < Gem::TestCase
cache_file = File.join cache_dir, a1.spec_name
- open cache_file, 'wb' do |io|
+ File.open cache_file, 'wb' do |io|
Marshal.dump a1, io
end
@@ -163,7 +151,7 @@ class TestGemSource < Gem::TestCase
cache_file = File.join cache_dir, "latest_specs.#{Gem.marshal_version}"
- open cache_file, 'wb' do |io|
+ File.open cache_file, 'wb' do |io|
Marshal.dump latest_specs, io
end
@@ -187,7 +175,7 @@ class TestGemSource < Gem::TestCase
cache_file = File.join cache_dir, "latest_specs.#{Gem.marshal_version}"
- open cache_file, 'wb' do |io|
+ File.open cache_file, 'wb' do |io|
# Setup invalid data in the cache:
io.write Marshal.dump(latest_specs)[0, 10]
end
@@ -228,6 +216,15 @@ class TestGemSource < Gem::TestCase
assert_equal(-1, remote. <=>(no_uri), 'remote <=> no_uri')
end
+ def test_spaceship_order_is_preserved_when_uri_differs
+ sourceA = Gem::Source.new "http://example.com/a"
+ sourceB = Gem::Source.new "http://example.com/b"
+
+ assert_equal( 0, sourceA. <=>(sourceA), 'sourceA <=> sourceA')
+ assert_equal( 1, sourceA. <=>(sourceB), 'sourceA <=> sourceB')
+ assert_equal( 1, sourceB. <=>(sourceA), 'sourceB <=> sourceA')
+ end
+
def test_update_cache_eh
assert @source.update_cache?
end
@@ -239,4 +236,3 @@ class TestGemSource < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_source_fetch_problem.rb b/test/rubygems/test_gem_source_fetch_problem.rb
index 4a245f25df..f3b57e8384 100644
--- a/test/rubygems/test_gem_source_fetch_problem.rb
+++ b/test/rubygems/test_gem_source_fetch_problem.rb
@@ -25,4 +25,3 @@ class TestGemSourceFetchProblem < Gem::TestCase
refute_match sf.wordy, 'secret'
end
end
-
diff --git a/test/rubygems/test_gem_source_git.rb b/test/rubygems/test_gem_source_git.rb
index 0e13a11e7e..586e71bae6 100644
--- a/test/rubygems/test_gem_source_git.rb
+++ b/test/rubygems/test_gem_source_git.rb
@@ -229,7 +229,7 @@ class TestGemSourceGit < Gem::TestCase
Dir.chdir 'b' do
b = Gem::Specification.new 'b', 1
- open 'b.gemspec', 'w' do |io|
+ File.open 'b.gemspec', 'w' do |io|
io.write b.to_ruby
end
@@ -306,4 +306,3 @@ class TestGemSourceGit < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_source_installed.rb b/test/rubygems/test_gem_source_installed.rb
index 6c7974831f..b469f842fc 100644
--- a/test/rubygems/test_gem_source_installed.rb
+++ b/test/rubygems/test_gem_source_installed.rb
@@ -34,4 +34,3 @@ class TestGemSourceInstalled < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_source_lock.rb b/test/rubygems/test_gem_source_lock.rb
index 6bd4002b6c..6edfb00e1a 100644
--- a/test/rubygems/test_gem_source_lock.rb
+++ b/test/rubygems/test_gem_source_lock.rb
@@ -112,4 +112,3 @@ class TestGemSourceLock < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_source_vendor.rb b/test/rubygems/test_gem_source_vendor.rb
index 01b20bdd51..3082bb7e68 100644
--- a/test/rubygems/test_gem_source_vendor.rb
+++ b/test/rubygems/test_gem_source_vendor.rb
@@ -29,4 +29,3 @@ class TestGemSourceVendor < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_spec_fetcher.rb b/test/rubygems/test_gem_spec_fetcher.rb
index 53bb31a910..99862ce871 100644
--- a/test/rubygems/test_gem_spec_fetcher.rb
+++ b/test/rubygems/test_gem_spec_fetcher.rb
@@ -169,6 +169,26 @@ class TestGemSpecFetcher < Gem::TestCase
assert_equal "bad news from the internet (#{@gem_repo})", sfp.error.message
end
+ def test_suggest_gems_from_name_latest
+ spec_fetcher do|fetcher|
+ fetcher.spec 'example', 1
+ fetcher.spec 'other-example', 1
+ end
+
+ suggestions = @sf.suggest_gems_from_name('examplw')
+ assert_equal ['example'], suggestions
+ end
+
+ def test_suggest_gems_from_name_prerelease
+ spec_fetcher do|fetcher|
+ fetcher.spec 'example', '1.a'
+ fetcher.spec 'other-example', 1
+ end
+
+ suggestions = @sf.suggest_gems_from_name('examplw')
+ assert_equal ['example'], suggestions
+ end
+
def test_available_specs_latest
spec_fetcher do |fetcher|
fetcher.spec 'a', 1
@@ -308,4 +328,3 @@ class TestGemSpecFetcher < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb
index 87f0f360d7..d2ecbf4434 100644
--- a/test/rubygems/test_gem_specification.rb
+++ b/test/rubygems/test_gem_specification.rb
@@ -9,7 +9,7 @@ require 'rubygems/installer'
class TestGemSpecification < Gem::TestCase
- LEGACY_YAML_SPEC = <<-EOF
+ LEGACY_YAML_SPEC = <<-EOF.freeze
--- !ruby/object:Gem::Specification
rubygems_version: "1.0"
name: keyedlist
@@ -28,7 +28,7 @@ email: flgr@ccan.de
has_rdoc: true
EOF
- LEGACY_RUBY_SPEC = <<-EOF
+ LEGACY_RUBY_SPEC = <<-EOF.freeze
Gem::Specification.new do |s|
s.name = %q{keyedlist}
s.version = %q{0.4.0}
@@ -48,7 +48,6 @@ end
s.extensions << 'ext/a/extconf.rb'
s.test_file = 'test/suite.rb'
s.requirements << 'A working computer'
- s.rubyforge_project = 'example'
s.license = 'MIT'
s.add_dependency 'rake', '> 0.4'
@@ -80,7 +79,6 @@ end
s.executable = 'exec'
s.test_file = 'test/suite.rb'
s.requirements << 'A working computer'
- s.rubyforge_project = 'example'
s.license = 'MIT'
s.mark_version
@@ -101,14 +99,14 @@ end
end
def test_self_find_active_stub_by_path
- spec = new_spec('a', '1', nil, 'lib/foo.rb')
+ spec = util_spec('a', '1', nil, 'lib/foo.rb')
spec.activated = true
# There used to be a bug (introduced in a9c1aaf) when Gem::Specification
# objects are present in the @stubs collection. This test verifies that
# this scenario works correctly.
Gem::Specification.all = [spec]
- Gem::Specification.find_active_stub_by_path('foo')
+ assert_equal spec, Gem::Specification.find_active_stub_by_path('foo')
end
def test_self_activate
@@ -119,11 +117,11 @@ end
def test_self_activate_ambiguous_direct
save_loaded_features do
- a1 = new_spec "a", "1", "b" => "> 0"
- b1 = new_spec("b", "1", { "c" => ">= 1" }, "lib/d.rb")
- b2 = new_spec("b", "2", { "c" => ">= 2" }, "lib/d.rb")
- c1 = new_spec "c", "1"
- c2 = new_spec "c", "2"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec("b", "1", { "c" => ">= 1" }, "lib/d.rb")
+ b2 = util_spec("b", "2", { "c" => ">= 2" }, "lib/d.rb")
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2"
Gem::Specification.reset
install_specs c1, c2, b1, b2, a1
@@ -148,10 +146,10 @@ end
deps = Hash[((pkgi + 1)..num_of_pkg).map { |deppkgi|
["pkg#{deppkgi}", ">= 0"]
}]
- new_spec "pkg#{pkgi}", pkg_version.to_s, deps
+ util_spec "pkg#{pkgi}", pkg_version.to_s, deps
end
end
- base = new_spec "pkg_base", "1", {"pkg0" => ">= 0"}
+ base = util_spec "pkg_base", "1", {"pkg0" => ">= 0"}
Gem::Specification.reset
install_specs(*packages.flatten.reverse)
@@ -167,11 +165,11 @@ end
def test_self_activate_ambiguous_indirect
save_loaded_features do
- a1 = new_spec "a", "1", "b" => "> 0"
- b1 = new_spec "b", "1", "c" => ">= 1"
- b2 = new_spec "b", "2", "c" => ">= 2"
- c1 = new_spec "c", "1", nil, "lib/d.rb"
- c2 = new_spec "c", "2", nil, "lib/d.rb"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 1"
+ b2 = util_spec "b", "2", "c" => ">= 2"
+ c1 = util_spec "c", "1", nil, "lib/d.rb"
+ c2 = util_spec "c", "2", nil, "lib/d.rb"
install_specs c1, c2, b1, b2, a1
@@ -188,12 +186,12 @@ end
def test_self_activate_ambiguous_indirect_conflict
save_loaded_features do
- a1 = new_spec "a", "1", "b" => "> 0"
- a2 = new_spec "a", "2", "b" => "> 0"
- b1 = new_spec "b", "1", "c" => ">= 1"
- b2 = new_spec "b", "2", "c" => ">= 2"
- c1 = new_spec "c", "1", nil, "lib/d.rb"
- c2 = new_spec("c", "2", { "a" => "1" }, "lib/d.rb") # conflicts with a-2
+ a1 = util_spec "a", "1", "b" => "> 0"
+ a2 = util_spec "a", "2", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 1"
+ b2 = util_spec "b", "2", "c" => ">= 2"
+ c1 = util_spec "c", "1", nil, "lib/d.rb"
+ c2 = util_spec("c", "2", { "a" => "1" }, "lib/d.rb") # conflicts with a-2
install_specs c1, b1, a1, a2, c2, b2
@@ -210,12 +208,12 @@ end
def test_self_activate_ambiguous_unrelated
save_loaded_features do
- a1 = new_spec "a", "1", "b" => "> 0"
- b1 = new_spec "b", "1", "c" => ">= 1"
- b2 = new_spec "b", "2", "c" => ">= 2"
- c1 = new_spec "c", "1"
- c2 = new_spec "c", "2"
- d1 = new_spec "d", "1", nil, "lib/d.rb"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 1"
+ b2 = util_spec "b", "2", "c" => ">= 2"
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2"
+ d1 = util_spec "d", "1", nil, "lib/d.rb"
install_specs d1, c1, c2, b1, b2, a1
@@ -232,11 +230,11 @@ end
def test_require_should_prefer_latest_gem_level1
save_loaded_features do
- a1 = new_spec "a", "1", "b" => "> 0"
- b1 = new_spec "b", "1", "c" => ">= 0" # unresolved
- b2 = new_spec "b", "2", "c" => ">= 0"
- c1 = new_spec "c", "1", nil, "lib/c.rb" # 1st level
- c2 = new_spec "c", "2", nil, "lib/c.rb"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 0" # unresolved
+ b2 = util_spec "b", "2", "c" => ">= 0"
+ c1 = util_spec "c", "1", nil, "lib/c.rb" # 1st level
+ c2 = util_spec "c", "2", nil, "lib/c.rb"
install_specs c1, c2, b1, b2, a1
@@ -250,13 +248,13 @@ end
def test_require_should_prefer_latest_gem_level2
save_loaded_features do
- a1 = new_spec "a", "1", "b" => "> 0"
- b1 = new_spec "b", "1", "c" => ">= 0" # unresolved
- b2 = new_spec "b", "2", "c" => ">= 0"
- c1 = new_spec "c", "1", "d" => ">= 0" # 1st level
- c2 = new_spec "c", "2", "d" => ">= 0"
- d1 = new_spec "d", "1", nil, "lib/d.rb" # 2nd level
- d2 = new_spec "d", "2", nil, "lib/d.rb"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 0" # unresolved
+ b2 = util_spec "b", "2", "c" => ">= 0"
+ c1 = util_spec "c", "1", "d" => ">= 0" # 1st level
+ c2 = util_spec "c", "2", "d" => ">= 0"
+ d1 = util_spec "d", "1", nil, "lib/d.rb" # 2nd level
+ d2 = util_spec "d", "2", nil, "lib/d.rb"
install_specs d1, d2, c1, c2, b1, b2, a1
@@ -270,14 +268,14 @@ end
def test_require_finds_in_2nd_level_indirect
save_loaded_features do
- a1 = new_spec "a", "1", "b" => "> 0"
- b1 = new_spec "b", "1", "c" => ">= 0" # unresolved
- b2 = new_spec "b", "2", "c" => ">= 0"
- c1 = new_spec "c", "1", "d" => "<= 2" # 1st level
- c2 = new_spec "c", "2", "d" => "<= 2"
- d1 = new_spec "d", "1", nil, "lib/d.rb" # 2nd level
- d2 = new_spec "d", "2", nil, "lib/d.rb"
- d3 = new_spec "d", "3", nil, "lib/d.rb"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 0" # unresolved
+ b2 = util_spec "b", "2", "c" => ">= 0"
+ c1 = util_spec "c", "1", "d" => "<= 2" # 1st level
+ c2 = util_spec "c", "2", "d" => "<= 2"
+ d1 = util_spec "d", "1", nil, "lib/d.rb" # 2nd level
+ d2 = util_spec "d", "2", nil, "lib/d.rb"
+ d3 = util_spec "d", "3", nil, "lib/d.rb"
install_specs d1, d2, d3, c1, c2, b1, b2, a1
@@ -291,15 +289,15 @@ end
def test_require_should_prefer_reachable_gems
save_loaded_features do
- a1 = new_spec "a", "1", "b" => "> 0"
- b1 = new_spec "b", "1", "c" => ">= 0" # unresolved
- b2 = new_spec "b", "2", "c" => ">= 0"
- c1 = new_spec "c", "1", "d" => "<= 2" # 1st level
- c2 = new_spec "c", "2", "d" => "<= 2"
- d1 = new_spec "d", "1", nil, "lib/d.rb" # 2nd level
- d2 = new_spec "d", "2", nil, "lib/d.rb"
- d3 = new_spec "d", "3", nil, "lib/d.rb"
- e = new_spec "anti_d", "1", nil, "lib/d.rb"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 0" # unresolved
+ b2 = util_spec "b", "2", "c" => ">= 0"
+ c1 = util_spec "c", "1", "d" => "<= 2" # 1st level
+ c2 = util_spec "c", "2", "d" => "<= 2"
+ d1 = util_spec "d", "1", nil, "lib/d.rb" # 2nd level
+ d2 = util_spec "d", "2", nil, "lib/d.rb"
+ d3 = util_spec "d", "3", nil, "lib/d.rb"
+ e = util_spec "anti_d", "1", nil, "lib/d.rb"
install_specs d1, d2, d3, e, c1, c2, b1, b2, a1
@@ -313,14 +311,14 @@ end
def test_require_should_not_conflict
save_loaded_features do
- base = new_spec "0", "1", "A" => ">= 1"
- a1 = new_spec "A", "1", {"c" => ">= 2", "b" => "> 0"}, "lib/a.rb"
- a2 = new_spec "A", "2", {"c" => ">= 2", "b" => "> 0"}, "lib/a.rb"
- b1 = new_spec "b", "1", {"c" => "= 1"}, "lib/d.rb"
- b2 = new_spec "b", "2", {"c" => "= 2"}, "lib/d.rb"
- c1 = new_spec "c", "1", {}, "lib/c.rb"
- c2 = new_spec "c", "2", {}, "lib/c.rb"
- c3 = new_spec "c", "3", {}, "lib/c.rb"
+ base = util_spec "0", "1", "A" => ">= 1"
+ a1 = util_spec "A", "1", {"c" => ">= 2", "b" => "> 0"}, "lib/a.rb"
+ a2 = util_spec "A", "2", {"c" => ">= 2", "b" => "> 0"}, "lib/a.rb"
+ b1 = util_spec "b", "1", {"c" => "= 1"}, "lib/d.rb"
+ b2 = util_spec "b", "2", {"c" => "= 2"}, "lib/d.rb"
+ c1 = util_spec "c", "1", {}, "lib/c.rb"
+ c2 = util_spec "c", "2", {}, "lib/c.rb"
+ c3 = util_spec "c", "3", {}, "lib/c.rb"
install_specs c1, c2, c3, b1, b2, a1, a2, base
@@ -337,15 +335,15 @@ end
def test_inner_clonflict_in_indirect_gems
save_loaded_features do
- a1 = new_spec "a", "1", "b" => "> 0"
- b1 = new_spec "b", "1", "c" => ">= 1" # unresolved
- b2 = new_spec "b", "2", "c" => ">= 1", "d" => "< 3"
- c1 = new_spec "c", "1", "d" => "<= 2" # 1st level
- c2 = new_spec "c", "2", "d" => "<= 2"
- c3 = new_spec "c", "3", "d" => "<= 3"
- d1 = new_spec "d", "1", nil, "lib/d.rb" # 2nd level
- d2 = new_spec "d", "2", nil, "lib/d.rb"
- d3 = new_spec "d", "3", nil, "lib/d.rb"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 1" # unresolved
+ b2 = util_spec "b", "2", "c" => ">= 1", "d" => "< 3"
+ c1 = util_spec "c", "1", "d" => "<= 2" # 1st level
+ c2 = util_spec "c", "2", "d" => "<= 2"
+ c3 = util_spec "c", "3", "d" => "<= 3"
+ d1 = util_spec "d", "1", nil, "lib/d.rb" # 2nd level
+ d2 = util_spec "d", "2", nil, "lib/d.rb"
+ d3 = util_spec "d", "3", nil, "lib/d.rb"
install_specs d1, d2, d3, c1, c2, c3, b1, b2, a1
@@ -359,15 +357,15 @@ end
def test_inner_clonflict_in_indirect_gems_reversed
save_loaded_features do
- a1 = new_spec "a", "1", "b" => "> 0"
- b1 = new_spec "b", "1", "xc" => ">= 1" # unresolved
- b2 = new_spec "b", "2", "xc" => ">= 1", "d" => "< 3"
- c1 = new_spec "xc", "1", "d" => "<= 3" # 1st level
- c2 = new_spec "xc", "2", "d" => "<= 2"
- c3 = new_spec "xc", "3", "d" => "<= 3"
- d1 = new_spec "d", "1", nil, "lib/d.rb" # 2nd level
- d2 = new_spec "d", "2", nil, "lib/d.rb"
- d3 = new_spec "d", "3", nil, "lib/d.rb"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "xc" => ">= 1" # unresolved
+ b2 = util_spec "b", "2", "xc" => ">= 1", "d" => "< 3"
+ c1 = util_spec "xc", "1", "d" => "<= 3" # 1st level
+ c2 = util_spec "xc", "2", "d" => "<= 2"
+ c3 = util_spec "xc", "3", "d" => "<= 3"
+ d1 = util_spec "d", "1", nil, "lib/d.rb" # 2nd level
+ d2 = util_spec "d", "2", nil, "lib/d.rb"
+ d3 = util_spec "d", "3", nil, "lib/d.rb"
install_specs d1, d2, d3, c1, c2, c3, b1, b2, a1
@@ -387,8 +385,8 @@ end
def test_self_activate_checks_dependencies
a = util_spec 'a', '1.0'
- a.add_dependency 'c', '= 1.0'
- a.add_dependency 'b', '~> 1.0'
+ a.add_dependency 'c', '= 1.0'
+ a.add_dependency 'b', '~> 1.0'
b1 = util_spec 'b', '1.0'
b2 = util_spec 'b', '2.0'
@@ -493,9 +491,9 @@ end
end
def test_self_activate_via_require
- a1 = new_spec "a", "1", "b" => "= 1"
- b1 = new_spec "b", "1", nil, "lib/b/c.rb"
- b2 = new_spec "b", "2", nil, "lib/b/c.rb"
+ a1 = util_spec "a", "1", "b" => "= 1"
+ b1 = util_spec "b", "1", nil, "lib/b/c.rb"
+ b2 = util_spec "b", "2", nil, "lib/b/c.rb"
install_specs b1, b2, a1
@@ -509,13 +507,13 @@ end
def test_self_activate_via_require_wtf
save_loaded_features do
- a1 = new_spec "a", "1", "b" => "> 0", "d" => "> 0" # this
- b1 = new_spec "b", "1", { "c" => ">= 1" }, "lib/b.rb"
- b2 = new_spec "b", "2", { "c" => ">= 2" }, "lib/b.rb" # this
- c1 = new_spec "c", "1"
- c2 = new_spec "c", "2" # this
- d1 = new_spec "d", "1", { "c" => "< 2" }, "lib/d.rb"
- d2 = new_spec "d", "2", { "c" => "< 2" }, "lib/d.rb" # this
+ a1 = util_spec "a", "1", "b" => "> 0", "d" => "> 0" # this
+ b1 = util_spec "b", "1", { "c" => ">= 1" }, "lib/b.rb"
+ b2 = util_spec "b", "2", { "c" => ">= 2" }, "lib/b.rb" # this
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2" # this
+ d1 = util_spec "d", "1", { "c" => "< 2" }, "lib/d.rb"
+ d2 = util_spec "d", "2", { "c" => "< 2" }, "lib/d.rb" # this
install_specs c1, c2, b1, b2, d1, d2, a1
@@ -538,11 +536,11 @@ end
end
def test_self_activate_deep_unambiguous
- a1 = new_spec "a", "1", "b" => "= 1"
- b1 = new_spec "b", "1", "c" => "= 1"
- b2 = new_spec "b", "2", "c" => "= 2"
- c1 = new_spec "c", "1"
- c2 = new_spec "c", "2"
+ a1 = util_spec "a", "1", "b" => "= 1"
+ b1 = util_spec "b", "1", "c" => "= 1"
+ b2 = util_spec "b", "2", "c" => "= 2"
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2"
install_specs c1, c2, b1, b2, a1
@@ -668,7 +666,7 @@ end
end
def test_self_all_equals
- a = new_spec "foo", "1", nil, "lib/foo.rb"
+ a = util_spec "foo", "1", nil, "lib/foo.rb"
install_specs a
Gem::Specification.all = [a]
@@ -701,7 +699,6 @@ end
required_ruby_version
required_rubygems_version
requirements
- rubyforge_project
rubygems_version
signing_key
specification_version
@@ -721,11 +718,11 @@ end
spec.version = '1'
spec.specification_version = @current_version + 1
- new_spec = Marshal.load Marshal.dump(spec)
+ load_spec = Marshal.load Marshal.dump(spec)
- assert_equal 'a', new_spec.name
- assert_equal Gem::Version.new(1), new_spec.version
- assert_equal @current_version, new_spec.specification_version
+ assert_equal 'a', load_spec.name
+ assert_equal Gem::Version.new(1), load_spec.version
+ assert_equal @current_version, load_spec.specification_version
end
def test_self_from_yaml
@@ -743,12 +740,12 @@ end
yaml = @a1.to_yaml
yaml.sub!(/^date:.*/, "date: 2011-04-26 00:00:00.000000000Z")
- new_spec = with_syck do
+ spec = with_syck do
Gem::Specification.from_yaml yaml
end
assert_kind_of Time, @a1.date
- assert_kind_of Time, new_spec.date
+ assert_kind_of Time, spec.date
end
def test_self_from_yaml_syck_default_key_bug
@@ -778,14 +775,14 @@ test_files: []
bindir:
YAML
- new_spec = with_syck do
+ spec = with_syck do
Gem::Specification.from_yaml yaml
end
- op = new_spec.dependencies.first.requirement.requirements.first.first
+ op = spec.dependencies.first.requirement.requirements.first.first
refute_kind_of YAML::Syck::DefaultKey, op
- refute_match %r%DefaultKey%, new_spec.to_ruby
+ refute_match %r%DefaultKey%, spec.to_ruby
end
def test_self_from_yaml_cleans_up_defaultkey
@@ -814,12 +811,12 @@ test_files: []
bindir:
YAML
- new_spec = Gem::Specification.from_yaml yaml
+ spec = Gem::Specification.from_yaml yaml
- op = new_spec.dependencies.first.requirement.requirements.first.first
+ op = spec.dependencies.first.requirement.requirements.first.first
refute_kind_of YAML::Syck::DefaultKey, op
- refute_match %r%DefaultKey%, new_spec.to_ruby
+ refute_match %r%DefaultKey%, spec.to_ruby
end
def test_self_from_yaml_cleans_up_defaultkey_from_newer_192
@@ -848,12 +845,12 @@ test_files: []
bindir:
YAML
- new_spec = Gem::Specification.from_yaml yaml
+ spec = Gem::Specification.from_yaml yaml
- op = new_spec.dependencies.first.requirement.requirements.first.first
+ op = spec.dependencies.first.requirement.requirements.first.first
refute_kind_of YAML::Syck::DefaultKey, op
- refute_match %r%DefaultKey%, new_spec.to_ruby
+ refute_match %r%DefaultKey%, spec.to_ruby
end
def test_self_from_yaml_cleans_up_Date_objects
@@ -871,7 +868,6 @@ require_paths:
author: Austin Ziegler
email: diff-lcs@halostatue.ca
homepage: http://rubyforge.org/projects/ruwiki/
-rubyforge_project: ruwiki
description: "Test"
bindir: bin
has_rdoc: true
@@ -903,9 +899,9 @@ requirements: []
dependencies: []
YAML
- new_spec = Gem::Specification.from_yaml yaml
+ spec = Gem::Specification.from_yaml yaml
- assert_kind_of Time, new_spec.date
+ assert_kind_of Time, spec.date
end
def test_self_load
@@ -922,7 +918,7 @@ dependencies: []
end
def test_self_load_relative
- open 'a-2.gemspec', 'w' do |io|
+ File.open 'a-2.gemspec', 'w' do |io|
io.write @a2.to_ruby_for_cache
end
@@ -948,6 +944,9 @@ dependencies: []
@a2.files.clear
assert_equal @a2, spec
+
+ ensure
+ $SAFE = 0
end
def test_self_load_escape_curly
@@ -1007,7 +1006,6 @@ dependencies: []
assert_equal @a2, spec
end
- if defined?(Encoding)
def test_self_load_utf8_with_ascii_encoding
int_enc = Encoding.default_internal
silence_warnings { Encoding.default_internal = 'US-ASCII' }
@@ -1028,7 +1026,6 @@ dependencies: []
ensure
silence_warnings { Encoding.default_internal = int_enc }
end
- end
def test_self_load_legacy_ruby
spec = Gem::Deprecate.skip_during do
@@ -1111,7 +1108,7 @@ dependencies: []
end
def test_self_remove_spec_removed
- open @a1.spec_file, 'w' do |io|
+ File.open @a1.spec_file, 'w' do |io|
io.write @a1.to_ruby
end
@@ -1125,24 +1122,97 @@ dependencies: []
refute_includes Gem::Specification.stubs.map { |s| s.full_name }, 'a-1'
end
- DATA_PATH = File.expand_path "../data", __FILE__
+ def test_self_stubs
+ Gem.loaded_specs.clear
+ Gem::Specification.class_variable_set(:@@stubs, nil)
- def test_handles_private_null_type
- path = File.join DATA_PATH, "null-type.gemspec.rz"
+ dir_standard_specs = File.join Gem.dir, 'specifications'
+ dir_default_specs = Gem::BasicSpecification.default_specifications_dir
+
+ # Create gemspecs in three locations used in stubs
+ loaded_spec = Gem::Specification.new 'a', '3'
+ Gem.loaded_specs['a'] = loaded_spec
+ save_gemspec 'a', '2', dir_default_specs
+ save_gemspec 'a', '1', dir_standard_specs
+
+ full_names = ['a-3', 'a-2', 'a-1']
+ assert_equal full_names, Gem::Specification.stubs.map { |s| s.full_name }
+
+ Gem.loaded_specs.delete 'a'
+ Gem::Specification.class_variable_set(:@@stubs, nil)
+ end
+
+ def test_self_stubs_for
+ Gem.loaded_specs.clear
+ Gem::Specification.class_variable_set(:@@stubs, nil)
+
+ dir_standard_specs = File.join Gem.dir, 'specifications'
+ dir_default_specs = Gem::BasicSpecification.default_specifications_dir
+
+ # Create gemspecs in three locations used in stubs
+ loaded_spec = Gem::Specification.new 'a', '3'
+ Gem.loaded_specs['a'] = loaded_spec
+ save_gemspec 'a', '2', dir_default_specs
+ save_gemspec 'a', '1', dir_standard_specs
+
+ full_names = ['a-3', 'a-2', 'a-1']
+
+ full_names = Gem::Specification.stubs_for('a').map { |s| s.full_name }
+ assert_equal full_names, Gem::Specification.stubs_for('a').map { |s| s.full_name }
+ assert_equal 1, Gem::Specification.class_variable_get(:@@stubs_by_name).length
+
+ Gem.loaded_specs.delete 'a'
+ Gem::Specification.class_variable_set(:@@stubs, nil)
+ end
+
+ def test_self_stubs_for_mult_platforms
+ # gems for two different platforms are installed with --user-install
+ # the correct one should be returned in the array
+
+ orig_platform = Gem.platforms.dup
+
+ # create user spec
+ user_spec_dir = File.join Gem.user_dir, 'specifications'
+ FileUtils.mkdir_p(user_spec_dir) unless Dir.exist? user_spec_dir
+ # dirs doesn't include user ?
+ Gem::Specification.dirs << user_spec_dir
- data = Marshal.load Gem.inflate(Gem.read_binary(path))
+ gem = 'mingw'
+ v = '1.1.1'
+ platforms = ['x86-mingw32', 'x64-mingw32']
- assert_equal nil, data.rubyforge_project
+ #create specs
+ platforms.each do |plat|
+ spec = Gem::Specification.new(gem, v) { |s| s.platform = plat }
+ File.open File.join(user_spec_dir, "#{gem}-#{v}-#{plat}.gemspec"), 'w' do |io|
+ io.write spec.to_ruby
+ end
+ end
+
+ platforms.each do |plat|
+ cur_plat = Gem::Platform.new plat
+ Gem.platforms = ['ruby', cur_plat]
+
+ Gem::Specification.class_variable_set :@@stubs, nil
+ Gem::Specification.stubs if plat == platforms.last # test loading via stubs
+ t = Gem::Specification.stubs_for 'mingw'
+
+ assert_equal 1, t.length
+ assert_equal cur_plat, t.first.platform
+ end
+
+ Gem.platforms = orig_platform
end
- def test_emits_zulu_timestamps_properly
- t = Time.utc(2012, 3, 12)
- @a2.date = t
+ DATA_PATH = File.expand_path "../data", __FILE__
+
+ def test_handles_private_null_type
+ path = File.join DATA_PATH, "null-type.gemspec.rz"
- yaml = with_psych { @a2.to_yaml }
+ data = Marshal.load Gem::Util.inflate(Gem.read_binary(path))
- assert_match %r!date: 2012-03-12 00:00:00\.000000000 Z!, yaml
- end if RUBY_VERSION =~ /1\.9\.2/
+ assert_nil data.signing_key
+ end
def test_initialize
spec = Gem::Specification.new do |s|
@@ -1153,7 +1223,7 @@ dependencies: []
assert_equal "blah", spec.name
assert_equal "1.3.5", spec.version.to_s
assert_equal Gem::Platform::RUBY, spec.platform
- assert_equal nil, spec.summary
+ assert_nil spec.summary
assert_equal [], spec.files
assert_equal [], spec.test_files
@@ -1201,57 +1271,57 @@ dependencies: []
s.add_dependency 'some_gem'
end
- new_spec = spec.dup
+ dup_spec = spec.dup
assert_equal "blah", spec.name
- assert_same spec.name, new_spec.name
+ assert_same spec.name, dup_spec.name
assert_equal "1.3.5", spec.version.to_s
- assert_same spec.version, new_spec.version
+ assert_same spec.version, dup_spec.version
assert_equal Gem::Platform::RUBY, spec.platform
- assert_same spec.platform, new_spec.platform
+ assert_same spec.platform, dup_spec.platform
assert_equal 'summary', spec.summary
- assert_same spec.summary, new_spec.summary
+ assert_same spec.summary, dup_spec.summary
assert_equal %w[README.txt bin/exec ext/extconf.rb lib/file.rb
test/file.rb].sort,
spec.files
- refute_same spec.files, new_spec.files, 'files'
+ refute_same spec.files, dup_spec.files, 'files'
assert_equal %w[test/file.rb], spec.test_files
- refute_same spec.test_files, new_spec.test_files, 'test_files'
+ refute_same spec.test_files, dup_spec.test_files, 'test_files'
assert_equal %w[--foo], spec.rdoc_options
- refute_same spec.rdoc_options, new_spec.rdoc_options, 'rdoc_options'
+ refute_same spec.rdoc_options, dup_spec.rdoc_options, 'rdoc_options'
assert_equal %w[README.txt], spec.extra_rdoc_files
- refute_same spec.extra_rdoc_files, new_spec.extra_rdoc_files,
+ refute_same spec.extra_rdoc_files, dup_spec.extra_rdoc_files,
'extra_rdoc_files'
assert_equal %w[exec], spec.executables
- refute_same spec.executables, new_spec.executables, 'executables'
+ refute_same spec.executables, dup_spec.executables, 'executables'
assert_equal %w[ext/extconf.rb], spec.extensions
- refute_same spec.extensions, new_spec.extensions, 'extensions'
+ refute_same spec.extensions, dup_spec.extensions, 'extensions'
assert_equal %w[requirement], spec.requirements
- refute_same spec.requirements, new_spec.requirements, 'requirements'
+ refute_same spec.requirements, dup_spec.requirements, 'requirements'
assert_equal [Gem::Dependency.new('some_gem', Gem::Requirement.default)],
spec.dependencies
- refute_same spec.dependencies, new_spec.dependencies, 'dependencies'
+ refute_same spec.dependencies, dup_spec.dependencies, 'dependencies'
assert_equal 'bin', spec.bindir
- assert_same spec.bindir, new_spec.bindir
+ assert_same spec.bindir, dup_spec.bindir
assert_equal '>= 0', spec.required_ruby_version.to_s
- assert_same spec.required_ruby_version, new_spec.required_ruby_version
+ assert_same spec.required_ruby_version, dup_spec.required_ruby_version
assert_equal '>= 0', spec.required_rubygems_version.to_s
assert_same spec.required_rubygems_version,
- new_spec.required_rubygems_version
+ dup_spec.required_rubygems_version
end
def test_initialize_copy_broken
@@ -1260,7 +1330,7 @@ dependencies: []
s.version = '1'
end
- spec.instance_variable_set :@licenses, :blah
+ spec.instance_variable_set :@licenses, (class << (Object.new);self;end)
spec.loaded_from = '/path/to/file'
e = assert_raises Gem::FormatException do
@@ -1271,6 +1341,16 @@ dependencies: []
assert_equal '/path/to/file', e.file_path
end
+ def test_initialize_prerelease_version_before_name
+ spec = Gem::Specification.new do |s|
+ s.version = '1.0.0.dev'
+ s.name = 'a'
+ end
+
+ assert_equal "a", spec.name
+ assert_equal "1.0.0.dev", spec.version.to_s
+ end
+
def test__dump
@a2.platform = Gem::Platform.local
@a2.instance_variable_set :@original_platform, 'old_platform'
@@ -1330,31 +1410,31 @@ dependencies: []
@a2.bindir = nil
@a2.executable = 'app'
- assert_equal nil, @a2.bindir
+ assert_nil @a2.bindir
assert_equal %w[app lib/code.rb].sort, @a2.files
end
def test_extensions_equals_nil
@a2.instance_variable_set(:@extensions, nil)
- assert_equal nil, @a2.instance_variable_get(:@extensions)
+ assert_nil @a2.instance_variable_get(:@extensions)
assert_equal %w[lib/code.rb], @a2.files
end
def test_test_files_equals_nil
@a2.instance_variable_set(:@test_files, nil)
- assert_equal nil, @a2.instance_variable_get(:@test_files)
+ assert_nil @a2.instance_variable_get(:@test_files)
assert_equal %w[lib/code.rb], @a2.files
end
def test_executables_equals_nil
@a2.instance_variable_set(:@executables, nil)
- assert_equal nil, @a2.instance_variable_get(:@executables)
+ assert_nil @a2.instance_variable_get(:@executables)
assert_equal %w[lib/code.rb], @a2.files
end
def test_extra_rdoc_files_equals_nil
@a2.instance_variable_set(:@extra_rdoc_files, nil)
- assert_equal nil, @a2.instance_variable_get(:@extra_rdoc_files)
+ assert_nil @a2.instance_variable_get(:@extra_rdoc_files)
assert_equal %w[lib/code.rb], @a2.files
end
@@ -1363,13 +1443,13 @@ dependencies: []
assert_empty @ext.build_args
- open @ext.build_info_file, 'w' do |io|
+ File.open @ext.build_info_file, 'w' do |io|
io.puts
end
assert_empty @ext.build_args
- open @ext.build_info_file, 'w' do |io|
+ File.open @ext.build_info_file, 'w' do |io|
io.puts '--with-foo-dir=wherever'
end
@@ -1385,9 +1465,9 @@ dependencies: []
extconf_rb = File.join @ext.gem_dir, @ext.extensions.first
FileUtils.mkdir_p File.dirname extconf_rb
- open extconf_rb, 'w' do |f|
+ File.open extconf_rb, 'w' do |f|
f.write <<-'RUBY'
- open 'Makefile', 'w' do |f|
+ File.open 'Makefile', 'w' do |f|
f.puts "clean:\n\techo clean"
f.puts "default:\n\techo built"
f.puts "install:\n\techo installed"
@@ -1435,9 +1515,9 @@ dependencies: []
extconf_rb = File.join spec.gem_dir, spec.extensions.first
FileUtils.mkdir_p File.dirname extconf_rb
- open extconf_rb, 'w' do |f|
+ File.open extconf_rb, 'w' do |f|
f.write <<-'RUBY'
- open 'Makefile', 'w' do |f|
+ File.open 'Makefile', 'w' do |f|
f.puts "default:\n\techo built"
f.puts "install:\n\techo installed"
end
@@ -1461,6 +1541,7 @@ dependencies: []
def test_build_extensions_extensions_dir_unwritable
skip 'chmod not supported' if Gem.win_platform?
+ skip 'skipped in root privilege' if Process.uid.zero?
ext_spec
@@ -1469,9 +1550,9 @@ dependencies: []
extconf_rb = File.join @ext.gem_dir, @ext.extensions.first
FileUtils.mkdir_p File.dirname extconf_rb
- open extconf_rb, 'w' do |f|
+ File.open extconf_rb, 'w' do |f|
f.write <<-'RUBY'
- open 'Makefile', 'w' do |f|
+ File.open 'Makefile', 'w' do |f|
f.puts "clean:\n\techo clean"
f.puts "default:\n\techo built"
f.puts "install:\n\techo installed"
@@ -1486,7 +1567,7 @@ dependencies: []
@ext.build_extensions
refute_path_exists @ext.extension_dir
ensure
- unless ($DEBUG or win_platform?) then
+ unless ($DEBUG or win_platform? or Process.uid.zero?)
FileUtils.chmod 0755, File.join(@ext.base_dir, 'extensions')
FileUtils.chmod 0755, @ext.base_dir
end
@@ -1502,9 +1583,9 @@ dependencies: []
extconf_rb = File.join @ext.gem_dir, @ext.extensions.first
FileUtils.mkdir_p File.dirname extconf_rb
- open extconf_rb, 'w' do |f|
+ File.open extconf_rb, 'w' do |f|
f.write <<-'RUBY'
- open 'Makefile', 'w' do |f|
+ File.open 'Makefile', 'w' do |f|
f.puts "clean:\n\techo clean"
f.puts "default:\n\techo built"
f.puts "install:\n\techo installed"
@@ -1551,9 +1632,9 @@ dependencies: []
extconf_rb = File.join @ext.gem_dir, @ext.extensions.first
FileUtils.mkdir_p File.dirname extconf_rb
- open extconf_rb, 'w' do |f|
+ File.open extconf_rb, 'w' do |f|
f.write <<-'RUBY'
- open 'Makefile', 'w' do |f|
+ File.open 'Makefile', 'w' do |f|
f.puts "clean:\n\techo clean"
f.puts "default:\n\techo built"
f.puts "install:\n\techo installed"
@@ -1586,7 +1667,7 @@ dependencies: []
refute @ext.contains_requirable_file? 'nonexistent'
end
- expected = "Ignoring ext-1 because its extensions are not built. " +
+ expected = "Ignoring ext-1 because its extensions are not built. " +
"Try: gem pristine ext --version 1\n"
assert_equal expected, err
@@ -1637,6 +1718,14 @@ dependencies: []
assert_equal Time.utc(2012,01,12,0,0,0), @a1.date
end
+ def test_date_use_env_source_date_epoch
+ epoch = ENV["SOURCE_DATE_EPOCH"]
+ ENV["SOURCE_DATE_EPOCH"] = "123456789"
+ assert_equal Time.utc(1973,11,29,0,0,0), @a1.date
+ ensure
+ ENV["SOURCE_DATE_EPOCH"] = epoch
+ end
+
def test_dependencies
util_setup_deps
assert_equal [@bonobo, @monkey], @gem.dependencies
@@ -1681,8 +1770,8 @@ dependencies: []
end
def test_eql_eh
- g1 = new_spec 'gem', 1
- g2 = new_spec 'gem', 1
+ g1 = util_spec 'gem', 1
+ g2 = util_spec 'gem', 1
assert_equal g1, g2
assert_equal g1.hash, g2.hash
@@ -2051,7 +2140,7 @@ dependencies: []
remove_method :default_ext_dir_for
end
- def Gem.default_ext_dir_for base_dir
+ def Gem.default_ext_dir_for(base_dir)
'/foo'
end
@@ -2163,7 +2252,7 @@ dependencies: []
def test_require_already_activated
save_loaded_features do
- a1 = new_spec "a", "1", nil, "lib/d.rb"
+ a1 = util_spec "a", "1", nil, "lib/d.rb"
install_specs a1 # , a2, b1, b2, c1, c2
@@ -2180,12 +2269,12 @@ dependencies: []
def test_require_already_activated_indirect_conflict
save_loaded_features do
- a1 = new_spec "a", "1", "b" => "> 0"
- a2 = new_spec "a", "2", "b" => "> 0"
- b1 = new_spec "b", "1", "c" => ">= 1"
- b2 = new_spec "b", "2", "c" => ">= 2"
- c1 = new_spec "c", "1", nil, "lib/d.rb"
- c2 = new_spec("c", "2", { "a" => "1" }, "lib/d.rb") # conflicts with a-2
+ a1 = util_spec "a", "1", "b" => "> 0"
+ a2 = util_spec "a", "2", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 1"
+ b2 = util_spec "b", "2", "c" => ">= 2"
+ c1 = util_spec "c", "1", nil, "lib/d.rb"
+ c2 = util_spec("c", "2", { "a" => "1" }, "lib/d.rb") # conflicts with a-2
install_specs c1, b1, a1, a2, c2, b2
@@ -2206,7 +2295,7 @@ dependencies: []
end
def test_allowed_push_host
- assert_equal nil, @a1.metadata['allowed_push_host']
+ assert_nil @a1.metadata['allowed_push_host']
assert_equal 'https://privategemserver.com', @a3.metadata['allowed_push_host']
end
@@ -2223,8 +2312,8 @@ dependencies: []
end
def test_spaceship_name
- s1 = new_spec 'a', '1'
- s2 = new_spec 'b', '1'
+ s1 = util_spec 'a', '1'
+ s2 = util_spec 'b', '1'
assert_equal(-1, (s1 <=> s2))
assert_equal( 0, (s1 <=> s1))
@@ -2232,8 +2321,8 @@ dependencies: []
end
def test_spaceship_platform
- s1 = new_spec 'a', '1'
- s2 = new_spec 'a', '1' do |s|
+ s1 = util_spec 'a', '1'
+ s2 = util_spec 'a', '1' do |s|
s.platform = Gem::Platform.new 'x86-my_platform1'
end
@@ -2243,8 +2332,8 @@ dependencies: []
end
def test_spaceship_version
- s1 = new_spec 'a', '1'
- s2 = new_spec 'a', '2'
+ s1 = util_spec 'a', '1'
+ s2 = util_spec 'a', '2'
assert_equal( -1, (s1 <=> s2))
assert_equal( 0, (s1 <=> s1))
@@ -2416,7 +2505,6 @@ Gem::Specification.new do |s|
s.homepage = "http://example.com".freeze
s.licenses = ["MIT".freeze]
s.requirements = ["A working computer".freeze]
- s.rubyforge_project = "example".freeze
s.rubygems_version = "#{Gem::VERSION}".freeze
s.summary = "this is a summary".freeze
s.test_files = ["test/suite.rb".freeze]
@@ -2427,16 +2515,16 @@ Gem::Specification.new do |s|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<rake>.freeze, [\"> 0.4\"])
s.add_runtime_dependency(%q<jabber4r>.freeze, [\"> 0.0.0\"])
- s.add_runtime_dependency(%q<pqa>.freeze, [\"<= 0.6\", \"> 0.4\"])
+ s.add_runtime_dependency(%q<pqa>.freeze, [\"> 0.4\", \"<= 0.6\"])
else
s.add_dependency(%q<rake>.freeze, [\"> 0.4\"])
s.add_dependency(%q<jabber4r>.freeze, [\"> 0.0.0\"])
- s.add_dependency(%q<pqa>.freeze, [\"<= 0.6\", \"> 0.4\"])
+ s.add_dependency(%q<pqa>.freeze, [\"> 0.4\", \"<= 0.6\"])
end
else
s.add_dependency(%q<rake>.freeze, [\"> 0.4\"])
s.add_dependency(%q<jabber4r>.freeze, [\"> 0.0.0\"])
- s.add_dependency(%q<pqa>.freeze, [\"<= 0.6\", \"> 0.4\"])
+ s.add_dependency(%q<pqa>.freeze, [\"> 0.4\", \"<= 0.6\"])
end
end
SPEC
@@ -2533,7 +2621,7 @@ end
end
end
- def x s; s.gsub(/xxx/, ''); end
+ def x(s); s.gsub(/xxx/, ''); end
def w; x "WARxxxNING"; end
def t; x "TOxxxDO"; end
def f; x "FxxxIXME"; end
@@ -2609,6 +2697,7 @@ end
@a1.add_runtime_dependency 'l', '> 1.2.3'
@a1.add_runtime_dependency 'm', '~> 2.1.0'
@a1.add_runtime_dependency 'n', '~> 0.1.0'
+ @a1.add_runtime_dependency 'o'
use_ui @ui do
@a1.validate
@@ -2617,12 +2706,6 @@ end
expected = <<-EXPECTED
#{w}: prerelease dependency on b (>= 1.0.rc1) is not recommended
#{w}: prerelease dependency on c (>= 2.0.rc2, development) is not recommended
-#{w}: pessimistic dependency on d (~> 1.2.3) may be overly strict
- if d is semantically versioned, use:
- add_runtime_dependency 'd', '~> 1.2', '>= 1.2.3'
-#{w}: pessimistic dependency on e (~> 1.2.3.4) may be overly strict
- if e is semantically versioned, use:
- add_runtime_dependency 'e', '~> 1.2', '>= 1.2.3.4'
#{w}: open-ended dependency on i (>= 1.2) is not recommended
if i is semantically versioned, use:
add_runtime_dependency 'i', '~> 1.2'
@@ -2635,9 +2718,8 @@ end
#{w}: open-ended dependency on l (> 1.2.3) is not recommended
if l is semantically versioned, use:
add_runtime_dependency 'l', '~> 1.2', '> 1.2.3'
-#{w}: pessimistic dependency on m (~> 2.1.0) may be overly strict
- if m is semantically versioned, use:
- add_runtime_dependency 'm', '~> 2.1', '>= 2.1.0'
+#{w}: open-ended dependency on o (>= 0) is not recommended
+ use a bounded requirement, such as '~> x.y'
#{w}: See http://guides.rubygems.org/specification-reference/ for help
EXPECTED
@@ -2748,14 +2830,6 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
util_setup_validate
Dir.chdir @tempdir do
- @a1.email = ""
-
- use_ui @ui do
- @a1.validate
- end
-
- assert_match "#{w}: no email specified\n", @ui.error, "error"
-
@a1.email = "FIxxxXME (your e-mail)".sub(/xxx/, "")
e = assert_raises Gem::InvalidSpecificationException do
@@ -2812,7 +2886,7 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
end
def test_validate_empty_require_paths
- if win_platform? then
+ if win_platform?
skip 'test_validate_empty_require_paths skipped on MS Windows (symlink)'
else
util_setup_validate
@@ -2848,6 +2922,68 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
@a1.files
end
+ def test_unresolved_specs
+ specification = Gem::Specification.clone
+
+ set_orig specification
+
+ specification.define_singleton_method(:unresolved_deps) do
+ { b: Gem::Dependency.new("x","1") }
+ end
+
+ specification.define_singleton_method(:find_all_by_name) do |dep_name|
+ []
+ end
+
+ expected = <<-EXPECTED
+WARN: Unresolved or ambigious specs during Gem::Specification.reset:
+ x (= 1)
+WARN: Clearing out unresolved specs. Try 'gem cleanup <gem>'
+Please report a bug if this causes problems.
+ EXPECTED
+
+ assert_output nil, expected do
+ specification.reset
+ end
+ end
+
+ def test_unresolved_specs_with_versions
+ specification = Gem::Specification.clone
+
+ set_orig specification
+
+ specification.define_singleton_method(:unresolved_deps) do
+ { b: Gem::Dependency.new("x","1") }
+ end
+
+ specification.define_singleton_method(:find_all_by_name) do |dep_name|
+ [
+ specification.new { |s| s.name = "z", s.version = Gem::Version.new("1") },
+ specification.new { |s| s.name = "z", s.version = Gem::Version.new("2") }
+ ]
+ end
+
+ expected = <<-EXPECTED
+WARN: Unresolved or ambigious specs during Gem::Specification.reset:
+ x (= 1)
+ Available/installed versions of this gem:
+ - 1
+ - 2
+WARN: Clearing out unresolved specs. Try 'gem cleanup <gem>'
+Please report a bug if this causes problems.
+ EXPECTED
+
+ assert_output nil, expected do
+ specification.reset
+ end
+ end
+
+ def set_orig(cls)
+ s_cls = cls.singleton_class
+ s_cls.send :alias_method, :orig_unresolved_deps , :unresolved_deps
+ s_cls.send :alias_method, :orig_find_all_by_name, :find_all_by_name
+ end
+
def test_validate_files_recursive
util_setup_validate
FileUtils.touch @a1.file_name
@@ -2890,7 +3026,22 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
@a1.validate
end
- assert_equal '"over at my cool site" is not a URI', e.message
+ assert_equal '"over at my cool site" is not a valid HTTP URI', e.message
+
+ @a1.homepage = 'ftp://rubygems.org'
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ assert_equal '"ftp://rubygems.org" is not a valid HTTP URI', e.message
+
+ @a1.homepage = 'http://rubygems.org'
+ assert_equal true, @a1.validate
+
+ @a1.homepage = 'https://rubygems.org'
+ assert_equal true, @a1.validate
+
end
end
@@ -2933,6 +3084,17 @@ http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
assert_empty @ui.error
end
+ def test_validate_license_values_or_later
+ util_setup_validate
+
+ use_ui @ui do
+ @a1.licenses = ['GPL-2.0-or-later']
+ @a1.validate
+ end
+
+ assert_empty @ui.error
+ end
+
def test_validate_license_values_with
util_setup_validate
@@ -2962,6 +3124,20 @@ http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
warning
end
+ def test_validate_license_with_invalid_exception
+ util_setup_validate
+
+ use_ui @ui do
+ @a1.licenses = ['GPL-2.0+ WITH Autocofn-exception-2.0']
+ @a1.validate
+ end
+
+ assert_match <<-warning, @ui.error
+WARNING: license value 'GPL-2.0+ WITH Autocofn-exception-2.0' is invalid. Use a license identifier from
+http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
+ warning
+ end
+
def test_validate_license_gives_suggestions
util_setup_validate
@@ -2977,6 +3153,43 @@ Did you mean 'Ruby'?
warning
end
+ def test_validate_empty_files
+ util_setup_validate
+
+ use_ui @ui do
+ # we have to set all of these for #files to be empty
+ @a1.files = []
+ @a1.test_files = []
+ @a1.executables = []
+
+ @a1.validate
+ end
+
+ assert_match "no files specified", @ui.error
+ end
+
+ def test_validate_empty_homepage
+ util_setup_validate
+
+ use_ui @ui do
+ @a1.homepage = nil
+ @a1.validate
+ end
+
+ assert_match "no homepage specified", @ui.error
+ end
+
+ def test_validate_empty_summary
+ util_setup_validate
+
+ use_ui @ui do
+ @a1.summary = nil
+ @a1.validate
+ end
+
+ assert_match "no summary specified", @ui.error
+ end
+
def test_validate_name
util_setup_validate
@@ -2985,7 +3198,37 @@ Did you mean 'Ruby'?
@a1.validate
end
- assert_equal 'invalid value for attribute name: ":json"', e.message
+ assert_equal 'invalid value for attribute name: ":json" must be a string', e.message
+
+ @a1.name = []
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+ assert_equal "invalid value for attribute name: \"[]\" must be a string", e.message
+
+ @a1.name = ""
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+ assert_equal "invalid value for attribute name: \"\" must include at least one letter", e.message
+
+ @a1.name = "12345"
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+ assert_equal "invalid value for attribute name: \"12345\" must include at least one letter", e.message
+
+ @a1.name = "../malicious"
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+ assert_equal "invalid value for attribute name: \"../malicious\" can only include letters, numbers, dashes, and underscores", e.message
+
+ @a1.name = "\ba\t"
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+ assert_equal "invalid value for attribute name: \"\\ba\\t\" can only include letters, numbers, dashes, and underscores", e.message
end
def test_validate_non_nil
@@ -3147,7 +3390,7 @@ Did you mean 'Ruby'?
end
def test__load_fixes_Date_objects
- spec = new_spec "a", 1
+ spec = util_spec "a", 1
spec.instance_variable_set :@date, Date.today
spec = Marshal.load Marshal.dump(spec)
@@ -3206,10 +3449,10 @@ Did you mean 'Ruby'?
end
expected = %W[
- a-2
- a-2-x86-my_platform-1
- a-3-x86-other_platform-1
- ]
+ a-2
+ a-2-x86-my_platform-1
+ a-3-x86-other_platform-1
+ ]
latest_specs = Gem::Specification.latest_specs.map(&:full_name).sort
@@ -3222,7 +3465,11 @@ Did you mean 'Ruby'?
Dir.chdir @tempdir do
@m1 = quick_gem 'm', '1' do |s|
s.files = %w[lib/code.rb]
- s.metadata = { 'one' => "two", 'two' => "three" }
+ s.metadata = {
+ "one" => "two",
+ "home" => "three",
+ "homepage_uri" => "https://example.com/user/repo"
+ }
end
use_ui @ui do
@@ -3299,6 +3546,23 @@ Did you mean 'Ruby'?
end
end
+ def test_metadata_link_validation_fails
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @m2 = quick_gem 'm', '2' do |s|
+ s.files = %w[lib/code.rb]
+ s.metadata = { 'homepage_uri' => 'http:/example.com' }
+ end
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @m2.validate
+ end
+
+ assert_equal "metadata['homepage_uri'] has invalid link: \"http:/example.com\"", e.message
+ end
+ end
+
def test_metadata_specs
valid_ruby_spec = <<-EOF
# -*- encoding: utf-8 -*-
@@ -3338,9 +3602,9 @@ end
extconf_rb = File.join @ext.gem_dir, @ext.extensions.first
FileUtils.mkdir_p File.dirname extconf_rb
- open extconf_rb, 'w' do |f|
+ File.open extconf_rb, 'w' do |f|
f.write <<-'RUBY'
- open 'Makefile', 'w' do |f|
+ File.open 'Makefile', 'w' do |f|
f.puts "clean:\n\techo clean"
f.puts "default:\n\techo built"
f.puts "install:\n\techo installed"
@@ -3376,6 +3640,31 @@ end
refute @a1.missing_extensions?
end
+ def test_find_all_by_full_name
+ pl = Gem::Platform.new 'i386-linux'
+
+ a1 = util_spec "a", "1"
+ a1_pre = util_spec "a", "1.0.0.pre.1"
+ a_1_platform = util_spec("a", "1") {|s| s.platform = pl }
+ a_b_1 = util_spec "a-b", "1"
+ a_b_1_platform = util_spec("a-b", "1") {|s| s.platform = pl }
+
+ a_b_1_1 = util_spec "a-b-1", "1"
+ a_b_1_1_platform = util_spec("a-b-1", "1") {|s| s.platform = pl }
+
+ install_specs(a1, a1_pre, a_1_platform, a_b_1, a_b_1_platform,
+ a_b_1_1, a_b_1_1_platform)
+
+ assert_equal [a1], Gem::Specification.find_all_by_full_name("a-1")
+ assert_equal [a1_pre], Gem::Specification.find_all_by_full_name("a-1.0.0.pre.1")
+ assert_equal [a_1_platform], Gem::Specification.find_all_by_full_name("a-1-x86-linux")
+ assert_equal [a_b_1_1], Gem::Specification.find_all_by_full_name("a-b-1-1")
+ assert_equal [a_b_1_1_platform], Gem::Specification.find_all_by_full_name("a-b-1-1-x86-linux")
+
+ assert_equal [], Gem::Specification.find_all_by_full_name("monkeys")
+ assert_equal [], Gem::Specification.find_all_by_full_name("a-1-foo")
+ end
+
def test_find_by_name
install_specs util_spec "a"
install_specs util_spec "a", 1
@@ -3413,7 +3702,7 @@ end
end
def test_find_by_path
- a = new_spec "foo", "1", nil, "lib/foo.rb"
+ a = util_spec "foo", "1", nil, "lib/foo.rb"
install_specs a
@@ -3423,13 +3712,13 @@ end
end
def test_find_inactive_by_path
- a = new_spec "foo", "1", nil, "lib/foo.rb"
+ a = util_spec "foo", "1", nil, "lib/foo.rb"
install_specs a
assert_equal a, Gem::Specification.find_inactive_by_path('foo')
a.activate
- assert_equal nil, Gem::Specification.find_inactive_by_path('foo')
+ assert_nil Gem::Specification.find_inactive_by_path('foo')
end
def test_load_default_gem
@@ -3446,18 +3735,6 @@ end
assert_equal ["default-2.0.0.0"], Gem::Specification.map(&:full_name)
end
- def test_detect_bundled_gem_in_old_ruby
- util_set_RUBY_VERSION '1.9.3', 551
-
- spec = new_spec 'bigdecimal', '1.1.0' do |s|
- s.summary = "This bigdecimal is bundled with Ruby"
- end
-
- assert spec.bundled_gem_in_old_ruby?
- ensure
- util_restore_RUBY_VERSION
- end
-
def util_setup_deps
@gem = util_spec "awesome", "1.0" do |awesome|
awesome.add_runtime_dependency "bonobo", []
diff --git a/test/rubygems/test_gem_stream_ui.rb b/test/rubygems/test_gem_stream_ui.rb
index 9518ebc88e..342b7071a6 100644
--- a/test/rubygems/test_gem_stream_ui.rb
+++ b/test/rubygems/test_gem_stream_ui.rb
@@ -4,6 +4,7 @@ require 'rubygems/user_interaction'
require 'timeout'
class TestGemStreamUI < Gem::TestCase
+ SHORT_TIMEOUT = (defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?) ? 1.0 : 0.1 # increase timeout with MJIT for --jit-wait testing
module IsTty
attr_accessor :tty
@@ -36,9 +37,6 @@ class TestGemStreamUI < Gem::TestCase
end
def test_ask
- skip 'TTY detection broken on windows' if
- Gem.win_platform? unless RUBY_VERSION > '1.9.2'
-
Timeout.timeout(1) do
expected_answer = "Arthur, King of the Britons"
@in.string = "#{expected_answer}\n"
@@ -48,21 +46,15 @@ class TestGemStreamUI < Gem::TestCase
end
def test_ask_no_tty
- skip 'TTY detection broken on windows' if
- Gem.win_platform? unless RUBY_VERSION > '1.9.2'
-
@in.tty = false
- Timeout.timeout(0.1) do
+ Timeout.timeout(SHORT_TIMEOUT) do
answer = @sui.ask("what is your favorite color?")
- assert_equal nil, answer
+ assert_nil answer
end
end
def test_ask_for_password
- skip 'Always uses $stdin on windows' if
- Gem.win_platform? unless RUBY_VERSION > '1.9.2'
-
Timeout.timeout(1) do
expected_answer = "Arthur, King of the Britons"
@in.string = "#{expected_answer}\n"
@@ -72,24 +64,18 @@ class TestGemStreamUI < Gem::TestCase
end
def test_ask_for_password_no_tty
- skip 'TTY handling is broken on windows' if
- Gem.win_platform? unless RUBY_VERSION > '1.9.2'
-
@in.tty = false
- Timeout.timeout(0.1) do
+ Timeout.timeout(SHORT_TIMEOUT) do
answer = @sui.ask_for_password("what is the airspeed velocity of an unladen swallow?")
- assert_equal nil, answer
+ assert_nil answer
end
end
def test_ask_yes_no_no_tty_with_default
- skip 'TTY handling is broken on windows' if
- Gem.win_platform? unless RUBY_VERSION > '1.9.2'
-
@in.tty = false
- Timeout.timeout(0.1) do
+ Timeout.timeout(SHORT_TIMEOUT) do
answer = @sui.ask_yes_no("do coconuts migrate?", false)
assert_equal false, answer
@@ -99,12 +85,9 @@ class TestGemStreamUI < Gem::TestCase
end
def test_ask_yes_no_no_tty_without_default
- skip 'TTY handling is broken on windows' if
- Gem.win_platform? unless RUBY_VERSION > '1.9.2'
-
@in.tty = false
- Timeout.timeout(0.1) do
+ Timeout.timeout(SHORT_TIMEOUT) do
assert_raises(Gem::OperationNotSupportedError) do
@sui.ask_yes_no("do coconuts migrate?")
end
@@ -174,14 +157,14 @@ class TestGemStreamUI < Gem::TestCase
def test_download_reporter_anything
@cfg.verbose = 0
reporter = @sui.download_reporter
- assert_kind_of Gem::StreamUI::VerboseDownloadReporter, reporter
+ assert_kind_of Gem::StreamUI::ThreadedDownloadReporter, reporter
end
- def test_verbose_download_reporter
+ def test_threaded_download_reporter
@cfg.verbose = true
reporter = @sui.download_reporter
reporter.fetch 'a.gem', 1024
- assert_equal "Fetching: a.gem", @out.string
+ assert_equal "Fetching a.gem\n", @out.string
end
def test_verbose_download_reporter_progress
@@ -189,7 +172,7 @@ class TestGemStreamUI < Gem::TestCase
reporter = @sui.download_reporter
reporter.fetch 'a.gem', 1024
reporter.update 512
- assert_equal "Fetching: a.gem\rFetching: a.gem ( 50%)", @out.string
+ assert_equal "Fetching a.gem\n", @out.string
end
def test_verbose_download_reporter_progress_once
@@ -198,7 +181,7 @@ class TestGemStreamUI < Gem::TestCase
reporter.fetch 'a.gem', 1024
reporter.update 510
reporter.update 512
- assert_equal "Fetching: a.gem\rFetching: a.gem ( 50%)", @out.string
+ assert_equal "Fetching a.gem\n", @out.string
end
def test_verbose_download_reporter_progress_complete
@@ -207,7 +190,7 @@ class TestGemStreamUI < Gem::TestCase
reporter.fetch 'a.gem', 1024
reporter.update 510
reporter.done
- assert_equal "Fetching: a.gem\rFetching: a.gem ( 50%)\rFetching: a.gem (100%)\n", @out.string
+ assert_equal "Fetching a.gem\n", @out.string
end
def test_verbose_download_reporter_progress_nil_length
@@ -216,7 +199,7 @@ class TestGemStreamUI < Gem::TestCase
reporter.fetch 'a.gem', nil
reporter.update 1024
reporter.done
- assert_equal "Fetching: a.gem\rFetching: a.gem (1024B)\rFetching: a.gem (1024B)\n", @out.string
+ assert_equal "Fetching a.gem\n", @out.string
end
def test_verbose_download_reporter_progress_zero_length
@@ -225,7 +208,7 @@ class TestGemStreamUI < Gem::TestCase
reporter.fetch 'a.gem', 0
reporter.update 1024
reporter.done
- assert_equal "Fetching: a.gem\rFetching: a.gem (1024B)\rFetching: a.gem (1024B)\n", @out.string
+ assert_equal "Fetching a.gem\n", @out.string
end
def test_verbose_download_reporter_no_tty
diff --git a/test/rubygems/test_gem_stub_specification.rb b/test/rubygems/test_gem_stub_specification.rb
index 4cba13e139..3988206944 100644
--- a/test/rubygems/test_gem_stub_specification.rb
+++ b/test/rubygems/test_gem_stub_specification.rb
@@ -33,6 +33,20 @@ class TestStubSpecification < Gem::TestCase
assert_equal %w[ext/stub_e/extconf.rb], stub.extensions
end
+ def test_initialize_version
+ stub = stub_with_version
+
+ assert_equal 'stub_v', stub.name
+ assert_equal v(2), stub.version
+ end
+
+ def test_initialize_with_empty_version
+ stub = stub_without_version
+
+ assert_equal 'stub_v', stub.name
+ assert_equal v(0), stub.version
+ end
+
def test_initialize_missing_stubline
stub = Gem::StubSpecification.gemspec_stub(BAR, @base_dir, @gems_dir)
assert_equal "bar", stub.name
@@ -57,7 +71,7 @@ class TestStubSpecification < Gem::TestCase
refute stub.contains_requirable_file? 'nonexistent'
end
- expected = "Ignoring stub_e-2 because its extensions are not built. " +
+ expected = "Ignoring stub_e-2 because its extensions are not built. " +
"Try: gem pristine stub_e --version 2\n"
assert_equal expected, err
@@ -81,6 +95,12 @@ class TestStubSpecification < Gem::TestCase
assert_equal File.join(stub.full_gem_path, 'lib'), stub.lib_dirs_glob
end
+ def test_lib_dirs_glob_with_extension
+ stub = stub_with_extension
+
+ assert_equal File.join(stub.full_gem_path, 'lib'), stub.lib_dirs_glob
+ end
+
def test_matches_for_glob
stub = stub_without_extension
code_rb = File.join stub.gem_dir, 'lib', 'code.rb'
@@ -90,14 +110,26 @@ class TestStubSpecification < Gem::TestCase
assert_equal code_rb, stub.matches_for_glob('code*').first
end
+ def test_matches_for_glob_with_bundler_inline
+ stub = stub_with_extension
+ code_rb = File.join stub.gem_dir, 'lib', 'code.rb'
+ FileUtils.mkdir_p File.dirname code_rb
+ FileUtils.touch code_rb
+
+ stub.stub(:raw_require_paths, nil) do
+ assert_equal code_rb, stub.matches_for_glob('code*').first
+ end
+ end
+
+
def test_missing_extensions_eh
stub = stub_with_extension do |s|
extconf_rb = File.join s.gem_dir, s.extensions.first
FileUtils.mkdir_p File.dirname extconf_rb
- open extconf_rb, 'w' do |f|
+ File.open extconf_rb, 'w' do |f|
f.write <<-'RUBY'
- open 'Makefile', 'w' do |f|
+ File.open 'Makefile', 'w' do |f|
f.puts "clean:\n\techo clean"
f.puts "default:\n\techo built"
f.puts "install:\n\techo installed"
@@ -117,7 +149,7 @@ class TestStubSpecification < Gem::TestCase
spec = new_default_spec 'default', 1
spec.extensions << 'extconf.rb'
- open spec.loaded_from, 'w' do |io|
+ File.open spec.loaded_from, 'w' do |io|
io.write spec.to_ruby_for_cache
end
@@ -164,9 +196,55 @@ class TestStubSpecification < Gem::TestCase
assert stub.to_spec.instance_variable_get :@ignored
end
+ def stub_with_version
+ spec = File.join @gemhome, 'specifications', 'stub_e-2.gemspec'
+ File.open spec, 'w' do |io|
+ io.write <<-STUB
+# -*- encoding: utf-8 -*-
+# stub: stub_v 2 ruby lib
+
+Gem::Specification.new do |s|
+ s.name = 'stub_v'
+ s.version = Gem::Version.new '2'
+end
+ STUB
+
+ io.flush
+
+ stub = Gem::StubSpecification.gemspec_stub io.path, @gemhome, File.join(@gemhome, 'gems')
+
+ yield stub if block_given?
+
+ return stub
+ end
+ end
+
+ def stub_without_version
+ spec = File.join @gemhome, 'specifications', 'stub-2.gemspec'
+ File.open spec, 'w' do |io|
+ io.write <<-STUB
+# -*- encoding: utf-8 -*-
+# stub: stub_v ruby lib
+
+Gem::Specification.new do |s|
+ s.name = 'stub_v'
+ s.version = ""
+end
+ STUB
+
+ io.flush
+
+ stub = Gem::StubSpecification.gemspec_stub io.path, @gemhome, File.join(@gemhome, 'gems')
+
+ yield stub if block_given?
+
+ return stub
+ end
+ end
+
def stub_with_extension
spec = File.join @gemhome, 'specifications', 'stub_e-2.gemspec'
- open spec, 'w' do |io|
+ File.open spec, 'w' do |io|
io.write <<-STUB
# -*- encoding: utf-8 -*-
# stub: stub_e 2 ruby lib
@@ -192,7 +270,7 @@ end
def stub_without_extension
spec = File.join @gemhome, 'specifications', 'stub-2.gemspec'
- open spec, 'w' do |io|
+ File.open spec, 'w' do |io|
io.write <<-STUB
# -*- encoding: utf-8 -*-
# stub: stub 2 ruby lib
@@ -214,4 +292,3 @@ end
end
end
-
diff --git a/test/rubygems/test_gem_text.rb b/test/rubygems/test_gem_text.rb
index a6e22e04da..c50d0bdb7a 100644
--- a/test/rubygems/test_gem_text.rb
+++ b/test/rubygems/test_gem_text.rb
@@ -21,6 +21,10 @@ class TestGemText < Gem::TestCase
assert_equal " text to wrap", format_text("text to wrap", 40, 2)
end
+ def test_format_text_no_space
+ assert_equal "texttowr\nap", format_text("texttowrap", 8)
+ end
+
def test_format_text_trailing # for two spaces after .
text = <<-TEXT
This line is really, really long. So long, in fact, that it is more than eighty characters long! The purpose of this line is for testing wrapping behavior because sometimes people don't wrap their text to eighty characters. Without the wrapping, the text might not look good in the RSS feed.
@@ -36,6 +40,10 @@ Without the wrapping, the text might not look good in the RSS feed.
assert_equal expected, format_text(text, 78)
end
+ def test_format_removes_nonprintable_characters
+ assert_equal "text with weird .. stuff .", format_text("text with weird \x1b\x02 stuff \x7f", 40)
+ end
+
def test_min3
assert_equal 1, min3(1, 1, 1)
assert_equal 1, min3(1, 1, 2)
@@ -74,4 +82,16 @@ Without the wrapping, the text might not look good in the RSS feed.
assert_equal 7, levenshtein_distance("xxxxxxx", "ZenTest")
assert_equal 7, levenshtein_distance("zentest", "xxxxxxx")
end
+
+ def test_truncate_text
+ assert_equal "abc", truncate_text("abc", "desc")
+ assert_equal "Truncating desc to 2 characters:\nab", truncate_text("abc", "desc", 2)
+ s = "ab" * 500_001
+ assert_equal "Truncating desc to 1,000,000 characters:\n#{s[0, 1_000_000]}", truncate_text(s, "desc", 1_000_000)
+ end
+
+ def test_clean_text
+ assert_equal ".]2;nyan.", clean_text("\e]2;nyan\a")
+ end
+
end
diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb
index d005130895..a53526c0bc 100644
--- a/test/rubygems/test_gem_uninstaller.rb
+++ b/test/rubygems/test_gem_uninstaller.rb
@@ -177,10 +177,12 @@ class TestGemUninstaller < Gem::InstallerTestCase
gem_dir = File.join @gemhome, 'gems', @spec.full_name
Gem.pre_uninstall do
+ sleep(0.1) if win_platform?
assert File.exist?(gem_dir), 'gem_dir should exist'
end
Gem.post_uninstall do
+ sleep(0.1) if win_platform?
refute File.exist?(gem_dir), 'gem_dir should not exist'
end
@@ -212,7 +214,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
default_spec = new_default_spec 'default', '2'
install_default_gems default_spec
- spec = new_spec 'default', '2'
+ spec = util_spec 'default', '2'
install_gem spec
Gem::Specification.reset
@@ -482,4 +484,22 @@ create_makefile '#{@spec.name}'
assert_match %r!r-1 depends on q \(= 1, development\)!, lines.shift
assert_match %r!Successfully uninstalled q-1!, lines.last
end
+
+ def test_uninstall_no_permission
+ uninstaller = Gem::Uninstaller.new @spec.name, :executables => true
+
+ stub_rm_r = lambda do |*args|
+ _path = args.shift
+ options = args.shift || Hash.new
+ # Uninstaller calls a method in RDoc which also calls FileUtils.rm_rf which
+ # is an alias for FileUtils#rm_r, so skip if we're using the force option
+ raise Errno::EPERM unless options[:force]
+ end
+
+ FileUtils.stub :rm_r, stub_rm_r do
+ assert_raises Gem::UninstallError do
+ uninstaller.uninstall
+ end
+ end
+ end
end
diff --git a/test/rubygems/test_gem_unsatisfiable_dependency_error.rb b/test/rubygems/test_gem_unsatisfiable_dependency_error.rb
index 8b9c5604c9..e68185ce25 100644
--- a/test/rubygems/test_gem_unsatisfiable_dependency_error.rb
+++ b/test/rubygems/test_gem_unsatisfiable_dependency_error.rb
@@ -30,4 +30,3 @@ class TestGemUnsatisfiableDependencyError < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_uri_formatter.rb b/test/rubygems/test_gem_uri_formatter.rb
index c16ce98d23..b19bae9939 100644
--- a/test/rubygems/test_gem_uri_formatter.rb
+++ b/test/rubygems/test_gem_uri_formatter.rb
@@ -26,4 +26,3 @@ class TestGemUriFormatter < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_util.rb b/test/rubygems/test_gem_util.rb
index eb974427aa..88afc62976 100644
--- a/test/rubygems/test_gem_util.rb
+++ b/test/rubygems/test_gem_util.rb
@@ -5,7 +5,7 @@ require 'rubygems/util'
class TestGemUtil < Gem::TestCase
def test_class_popen
- assert_equal "0\n", Gem::Util.popen(Gem.ruby, '-e', 'p 0')
+ assert_equal "0\n", Gem::Util.popen(Gem.ruby, '-I', File.expand_path('../../../lib', __FILE__), '-e', 'p 0')
assert_raises Errno::ECHILD do
Process.wait(-1)
@@ -14,7 +14,7 @@ class TestGemUtil < Gem::TestCase
def test_silent_system
assert_silent do
- Gem::Util.silent_system Gem.ruby, '-e', 'puts "hello"; warn "hello"'
+ Gem::Util.silent_system Gem.ruby, '-I', File.expand_path('../../../lib', __FILE__), '-e', 'puts "hello"; warn "hello"'
end
end
@@ -26,6 +26,28 @@ class TestGemUtil < Gem::TestCase
assert_equal File.join(@tempdir, 'a/b/c'), enum.next
assert_equal File.join(@tempdir, 'a/b'), enum.next
assert_equal File.join(@tempdir, 'a'), enum.next
+ loop { break if enum.next.nil? } # exhaust the enumerator
+ end
+
+ def test_traverse_parents_does_not_crash_on_permissions_error
+ skip 'skipped on MS Windows (chmod has no effect)' if win_platform?
+
+ FileUtils.mkdir_p 'd/e/f'
+ # remove 'execute' permission from "e" directory and make it
+ # impossible to cd into it and its children
+ FileUtils.chmod(0666, 'd/e')
+
+ skip 'skipped in root privilege' if Process.uid.zero?
+
+ paths = Gem::Util.traverse_parents('d/e/f').to_a
+
+ assert_equal File.join(@tempdir, 'd'), paths[0]
+ assert_equal @tempdir, paths[1]
+ assert_equal File.realpath(Dir.tmpdir), paths[2]
+ assert_equal File.realpath("..", Dir.tmpdir), paths[3]
+ ensure
+ # restore default permissions, allow the directory to be removed
+ FileUtils.chmod(0775, 'd/e') unless win_platform?
end
def test_linked_list_find
@@ -36,5 +58,21 @@ class TestGemUtil < Gem::TestCase
assert_equal 4, list.find { |x| x == 4 }
end
-end
+ def test_glob_files_in_dir
+ FileUtils.mkdir_p 'g'
+ FileUtils.touch File.join('g', 'h.rb')
+ FileUtils.touch File.join('g', 'i.rb')
+
+ expected_paths = [
+ File.join(@tempdir, 'g/h.rb'),
+ File.join(@tempdir, 'g/i.rb'),
+ ]
+
+ files_with_absolute_base = Gem::Util.glob_files_in_dir('*.rb', File.join(@tempdir, 'g'))
+ assert_equal expected_paths.to_set, files_with_absolute_base.to_set
+ files_with_relative_base = Gem::Util.glob_files_in_dir('*.rb', 'g')
+ assert_equal expected_paths.to_set, files_with_relative_base.to_set
+ end
+
+end
diff --git a/test/rubygems/test_gem_validator.rb b/test/rubygems/test_gem_validator.rb
index 5365a1dabb..b9f597b7d3 100644
--- a/test/rubygems/test_gem_validator.rb
+++ b/test/rubygems/test_gem_validator.rb
@@ -43,4 +43,3 @@ class TestGemValidator < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_gem_version.rb b/test/rubygems/test_gem_version.rb
index 9898669ce6..939360c7a2 100644
--- a/test/rubygems/test_gem_version.rb
+++ b/test/rubygems/test_gem_version.rb
@@ -2,6 +2,8 @@
require 'rubygems/test_case'
require "rubygems/version"
+require "minitest/benchmark"
+
class TestGemVersion < Gem::TestCase
class V < ::Gem::Version
@@ -41,6 +43,16 @@ class TestGemVersion < Gem::TestCase
assert_equal v('1.1'), Gem::Version.create(ver)
end
+ def test_class_correct
+ assert_equal true, Gem::Version.correct?("5.1")
+ assert_equal false, Gem::Version.correct?("an incorrect version")
+
+ expected = "nil versions are discouraged and will be deprecated in Rubygems 4\n"
+ assert_output nil, expected do
+ Gem::Version.correct?(nil)
+ end
+ end
+
def test_class_new_subclass
v1 = Gem::Version.new '1'
v2 = V.new '1'
@@ -65,7 +77,8 @@ class TestGemVersion < Gem::TestCase
def test_hash
assert_equal v("1.2").hash, v("1.2").hash
refute_equal v("1.2").hash, v("1.3").hash
- refute_equal v("1.2").hash, v("1.2.0").hash
+ assert_equal v("1.2").hash, v("1.2.0").hash
+ assert_equal v("1.2.pre.1").hash, v("1.2.0.pre.1.0").hash
end
def test_initialize
@@ -76,18 +89,38 @@ class TestGemVersion < Gem::TestCase
assert_version_equal "1", 1
end
- def test_initialize_bad
- %W[
+ def test_initialize_invalid
+ invalid_versions = %W[
junk
1.0\n2.0
1..2
1.2\ 3.4
- ].each do |bad|
- e = assert_raises ArgumentError, bad do
- Gem::Version.new bad
+ ]
+
+ # DON'T TOUCH THIS WITHOUT CHECKING CVE-2013-4287
+ invalid_versions << "2.3422222.222.222222222.22222.ads0as.dasd0.ddd2222.2.qd3e."
+
+ invalid_versions.each do |invalid|
+ e = assert_raises ArgumentError, invalid do
+ Gem::Version.new invalid
end
- assert_equal "Malformed version number string #{bad}", e.message, bad
+ assert_equal "Malformed version number string #{invalid}", e.message, invalid
+ end
+ end
+
+ def bench_anchored_version_pattern
+ assert_performance_linear 0.5 do |count|
+ version_string = count.times.map {|i| "0" * i.succ }.join(".") << "."
+ version_string =~ Gem::Version::ANCHORED_VERSION_PATTERN
+ end
+ rescue RegexpError
+ skip "It fails to allocate the memory for regex pattern of Gem::Version::ANCHORED_VERSION_PATTERN"
+ end
+
+ def test_empty_version
+ ["", " ", " "].each do |empty|
+ assert_equal "0", Gem::Version.new(empty).version
end
end
@@ -99,6 +132,9 @@ class TestGemVersion < Gem::TestCase
assert_prerelease '1.A'
+ assert_prerelease '1-1'
+ assert_prerelease '1-a'
+
refute_prerelease "1.2.0"
refute_prerelease "2.9"
refute_prerelease "22.1.50.0"
@@ -126,11 +162,25 @@ class TestGemVersion < Gem::TestCase
def test_approximate_recommendation
assert_approximate_equal "~> 1.0", "1"
+ assert_approximate_satisfies_itself "1"
+
assert_approximate_equal "~> 1.0", "1.0"
+ assert_approximate_satisfies_itself "1.0"
+
assert_approximate_equal "~> 1.2", "1.2"
+ assert_approximate_satisfies_itself "1.2"
+
assert_approximate_equal "~> 1.2", "1.2.0"
+ assert_approximate_satisfies_itself "1.2.0"
+
assert_approximate_equal "~> 1.2", "1.2.3"
- assert_approximate_equal "~> 1.2", "1.2.3.a.4"
+ assert_approximate_satisfies_itself "1.2.3"
+
+ assert_approximate_equal "~> 1.2.a", "1.2.3.a.4"
+ assert_approximate_satisfies_itself "1.2.3.a.4"
+
+ assert_approximate_equal "~> 1.9.a", "1.9.0.dev"
+ assert_approximate_satisfies_itself "1.9.0.dev"
end
def test_to_s
@@ -154,46 +204,61 @@ class TestGemVersion < Gem::TestCase
assert_equal [9,8,7], v("9.8.7").segments
end
+ def test_canonical_segments
+ assert_equal [1], v("1.0.0").canonical_segments
+ assert_equal [1, "a", 1], v("1.0.0.a.1.0").canonical_segments
+ assert_equal [1, 2, 3, "pre", 1], v("1.2.3-1").canonical_segments
+ end
+
# Asserts that +version+ is a prerelease.
- def assert_prerelease version
+ def assert_prerelease(version)
assert v(version).prerelease?, "#{version} is a prerelease"
end
- # Assert that +expected+ is the "approximate" recommendation for +version".
+ # Assert that +expected+ is the "approximate" recommendation for +version+.
- def assert_approximate_equal expected, version
+ def assert_approximate_equal(expected, version)
assert_equal expected, v(version).approximate_recommendation
end
+ # Assert that the "approximate" recommendation for +version+ satifies +version+.
+
+ def assert_approximate_satisfies_itself(version)
+ gem_version = v(version)
+
+ assert Gem::Requirement.new(gem_version.approximate_recommendation).satisfied_by?(gem_version)
+ end
+
# Assert that bumping the +unbumped+ version yields the +expected+.
- def assert_bumped_version_equal expected, unbumped
+ def assert_bumped_version_equal(expected, unbumped)
assert_version_equal expected, v(unbumped).bump
end
# Assert that +release+ is the correct non-prerelease +version+.
- def assert_release_equal release, version
+ def assert_release_equal(release, version)
assert_version_equal release, v(version).release
end
# Assert that two versions are equal. Handles strings or
# Gem::Version instances.
- def assert_version_equal expected, actual
+ def assert_version_equal(expected, actual)
assert_equal v(expected), v(actual)
+ assert_equal v(expected).hash, v(actual).hash, "since #{actual} == #{expected}, they must have the same hash"
end
# Assert that two versions are eql?. Checks both directions.
- def assert_version_eql first, second
+ def assert_version_eql(first, second)
first, second = v(first), v(second)
assert first.eql?(second), "#{first} is eql? #{second}"
assert second.eql?(first), "#{second} is eql? #{first}"
end
- def assert_less_than left, right
+ def assert_less_than(left, right)
l = v(left)
r = v(right)
assert l < r, "#{left} not less than #{right}"
@@ -201,14 +266,14 @@ class TestGemVersion < Gem::TestCase
# Refute the assumption that +version+ is a prerelease.
- def refute_prerelease version
+ def refute_prerelease(version)
refute v(version).prerelease?, "#{version} is NOT a prerelease"
end
# Refute the assumption that two versions are eql?. Checks both
# directions.
- def refute_version_eql first, second
+ def refute_version_eql(first, second)
first, second = v(first), v(second)
refute first.eql?(second), "#{first} is NOT eql? #{second}"
refute second.eql?(first), "#{second} is NOT eql? #{first}"
@@ -216,7 +281,7 @@ class TestGemVersion < Gem::TestCase
# Refute the assumption that the two versions are equal?.
- def refute_version_equal unexpected, actual
+ def refute_version_equal(unexpected, actual)
refute_equal v(unexpected), v(actual)
end
end
diff --git a/test/rubygems/test_gem_version_option.rb b/test/rubygems/test_gem_version_option.rb
index d4699313c2..a680c5154e 100644
--- a/test/rubygems/test_gem_version_option.rb
+++ b/test/rubygems/test_gem_version_option.rb
@@ -106,6 +106,21 @@ class TestGemVersionOption < Gem::TestCase
assert_equal expected, @cmd.options
end
+ def test_multiple_version_operator_option_compound
+ @cmd.add_version_option
+
+ @cmd.handle_options ['--version', '< 1', '--version', '> 0.9']
+
+ expected = {
+ :args => [],
+ :explicit_prerelease => false,
+ :prerelease => false,
+ :version => Gem::Requirement.new('< 1', '> 0.9'),
+ }
+
+ assert_equal expected, @cmd.options
+ end
+
def test_version_option_explicit_prerelease
@cmd.add_prerelease_option
@cmd.add_version_option
@@ -149,4 +164,3 @@ class TestGemVersionOption < Gem::TestCase
end
end
-
diff --git a/test/rubygems/test_kernel.rb b/test/rubygems/test_kernel.rb
index b565c62f4a..f7d3988ce3 100644
--- a/test/rubygems/test_kernel.rb
+++ b/test/rubygems/test_kernel.rb
@@ -90,4 +90,34 @@ class TestKernel < Gem::TestCase
assert gem('a', '= 1'), "Should load"
refute $:.any? { |p| %r{a-1/bin} =~ p }
end
+
+ def test_gem_bundler
+ quick_gem 'bundler', '1'
+ quick_gem 'bundler', '2.a'
+
+ assert gem('bundler')
+ assert $:.any? { |p| %r{bundler-1/lib} =~ p }
+ end
+
+ def test_gem_bundler_missing_bundler_version
+ Gem::BundlerVersionFinder.stub(:bundler_version_with_reason, ["55", "reason"]) do
+ quick_gem 'bundler', '1'
+ quick_gem 'bundler', '2.a'
+
+ e = assert_raises Gem::MissingSpecVersionError do
+ gem('bundler')
+ end
+ assert_match "Could not find 'bundler' (55) required by reason.", e.message
+ end
+ end
+
+ def test_gem_bundler_inferred_bundler_version
+ Gem::BundlerVersionFinder.stub(:bundler_version_with_reason, ["1", "reason"]) do
+ quick_gem 'bundler', '1'
+ quick_gem 'bundler', '2.a'
+
+ assert gem('bundler', '>= 0.a')
+ assert $:.any? { |p| %r{bundler-1/lib} =~ p }
+ end
+ end
end
diff --git a/test/rubygems/test_remote_fetch_error.rb b/test/rubygems/test_remote_fetch_error.rb
index 6b0f5477d6..432c48b878 100644
--- a/test/rubygems/test_remote_fetch_error.rb
+++ b/test/rubygems/test_remote_fetch_error.rb
@@ -18,4 +18,3 @@ class TestRemoteFetchError < Gem::TestCase
assert_equal error.to_s, 'There was an error fetching (https://gemsource.org)'
end
end
-
diff --git a/test/rubygems/test_require.rb b/test/rubygems/test_require.rb
index 04cbc094d8..e3bdc218f7 100644
--- a/test/rubygems/test_require.rb
+++ b/test/rubygems/test_require.rb
@@ -4,7 +4,7 @@ require 'rubygems'
class TestGemRequire < Gem::TestCase
class Latch
- def initialize count = 1
+ def initialize(count = 1)
@count = count
@lock = Monitor.new
@cv = @lock.new_cond
@@ -38,24 +38,12 @@ class TestGemRequire < Gem::TestCase
assert require(path), "'#{path}' was already required"
end
- def append_latch spec
- dir = spec.gem_dir
- Dir.chdir dir do
- spec.files.each do |file|
- File.open file, 'a' do |fp|
- fp.puts "FILE_ENTERED_LATCH.release"
- fp.puts "FILE_EXIT_LATCH.await"
- end
- end
- end
- end
-
# Providing -I on the commandline should always beat gems
def test_dash_i_beats_gems
- a1 = new_spec "a", "1", {"b" => "= 1"}, "lib/test_gem_require_a.rb"
- b1 = new_spec "b", "1", {"c" => "> 0"}, "lib/b/c.rb"
- c1 = new_spec "c", "1", nil, "lib/c/c.rb"
- c2 = new_spec "c", "2", nil, "lib/c/c.rb"
+ a1 = util_spec "a", "1", {"b" => "= 1"}, "lib/test_gem_require_a.rb"
+ b1 = util_spec "b", "1", {"c" => "> 0"}, "lib/b/c.rb"
+ c1 = util_spec "c", "1", nil, "lib/c/c.rb"
+ c2 = util_spec "c", "2", nil, "lib/c/c.rb"
install_specs c1, c2, b1, a1
@@ -80,22 +68,28 @@ class TestGemRequire < Gem::TestCase
Object.send :remove_const, :HELLO if Object.const_defined? :HELLO
end
- def test_concurrent_require
- skip 'deadlock' if /^1\.8\./ =~ RUBY_VERSION
+ def create_sync_thread
+ Thread.new do
+ begin
+ yield
+ ensure
+ FILE_ENTERED_LATCH.release
+ FILE_EXIT_LATCH.await
+ end
+ end
+ end
+ def test_concurrent_require
Object.const_set :FILE_ENTERED_LATCH, Latch.new(2)
Object.const_set :FILE_EXIT_LATCH, Latch.new(1)
- a1 = new_spec "a", "1", nil, "lib/a.rb"
- b1 = new_spec "b", "1", nil, "lib/b.rb"
+ a1 = util_spec "a", "1", nil, "lib/a.rb"
+ b1 = util_spec "b", "1", nil, "lib/b.rb"
install_specs a1, b1
- append_latch a1
- append_latch b1
-
- t1 = Thread.new { assert_require 'a' }
- t2 = Thread.new { assert_require 'b' }
+ t1 = create_sync_thread{ assert_require 'a' }
+ t2 = create_sync_thread{ assert_require 'b' }
# wait until both files are waiting on the exit latch
FILE_ENTERED_LATCH.await
@@ -106,16 +100,14 @@ class TestGemRequire < Gem::TestCase
assert t1.join, "thread 1 should exit"
assert t2.join, "thread 2 should exit"
ensure
- return if $! # skipping
-
- Object.send :remove_const, :FILE_ENTERED_LATCH
- Object.send :remove_const, :FILE_EXIT_LATCH
+ Object.send :remove_const, :FILE_ENTERED_LATCH if Object.const_defined? :FILE_ENTERED_LATCH
+ Object.send :remove_const, :FILE_EXIT_LATCH if Object.const_defined? :FILE_EXIT_LATCH
end
def test_require_is_not_lazy_with_exact_req
- a1 = new_spec "a", "1", {"b" => "= 1"}, "lib/test_gem_require_a.rb"
- b1 = new_spec "b", "1", nil, "lib/b/c.rb"
- b2 = new_spec "b", "2", nil, "lib/b/c.rb"
+ a1 = util_spec "a", "1", {"b" => "= 1"}, "lib/test_gem_require_a.rb"
+ b1 = util_spec "b", "1", nil, "lib/b/c.rb"
+ b2 = util_spec "b", "2", nil, "lib/b/c.rb"
install_specs b1, b2, a1
@@ -128,9 +120,9 @@ class TestGemRequire < Gem::TestCase
end
def test_require_is_lazy_with_inexact_req
- a1 = new_spec "a", "1", {"b" => ">= 1"}, "lib/test_gem_require_a.rb"
- b1 = new_spec "b", "1", nil, "lib/b/c.rb"
- b2 = new_spec "b", "2", nil, "lib/b/c.rb"
+ a1 = util_spec "a", "1", {"b" => ">= 1"}, "lib/test_gem_require_a.rb"
+ b1 = util_spec "b", "1", nil, "lib/b/c.rb"
+ b2 = util_spec "b", "2", nil, "lib/b/c.rb"
install_specs b1, b2, a1
@@ -143,8 +135,8 @@ class TestGemRequire < Gem::TestCase
end
def test_require_is_not_lazy_with_one_possible
- a1 = new_spec "a", "1", {"b" => ">= 1"}, "lib/test_gem_require_a.rb"
- b1 = new_spec "b", "1", nil, "lib/b/c.rb"
+ a1 = util_spec "a", "1", {"b" => ">= 1"}, "lib/test_gem_require_a.rb"
+ b1 = util_spec "b", "1", nil, "lib/b/c.rb"
install_specs b1, a1
@@ -157,7 +149,7 @@ class TestGemRequire < Gem::TestCase
end
def test_require_can_use_a_pathname_object
- a1 = new_spec "a", "1", nil, "lib/test_gem_require_a.rb"
+ a1 = util_spec "a", "1", nil, "lib/test_gem_require_a.rb"
install_specs a1
@@ -167,9 +159,9 @@ class TestGemRequire < Gem::TestCase
end
def test_activate_via_require_respects_loaded_files
- a1 = new_spec "a", "1", {"b" => ">= 1"}, "lib/test_gem_require_a.rb"
- b1 = new_spec "b", "1", nil, "lib/benchmark.rb"
- b2 = new_spec "b", "2", nil, "lib/benchmark.rb"
+ a1 = util_spec "a", "1", {"b" => ">= 1"}, "lib/test_gem_require_a.rb"
+ b1 = util_spec "b", "1", nil, "lib/benchmark.rb"
+ b2 = util_spec "b", "2", nil, "lib/benchmark.rb"
install_specs b1, b2, a1
@@ -187,11 +179,11 @@ class TestGemRequire < Gem::TestCase
end
def test_already_activated_direct_conflict
- a1 = new_spec "a", "1", { "b" => "> 0" }
- b1 = new_spec "b", "1", { "c" => ">= 1" }, "lib/ib.rb"
- b2 = new_spec "b", "2", { "c" => ">= 2" }, "lib/ib.rb"
- c1 = new_spec "c", "1", nil, "lib/d.rb"
- c2 = new_spec("c", "2", nil, "lib/d.rb")
+ a1 = util_spec "a", "1", { "b" => "> 0" }
+ b1 = util_spec "b", "1", { "c" => ">= 1" }, "lib/ib.rb"
+ b2 = util_spec "b", "2", { "c" => ">= 2" }, "lib/ib.rb"
+ c1 = util_spec "c", "1", nil, "lib/d.rb"
+ c2 = util_spec("c", "2", nil, "lib/d.rb")
install_specs c1, c2, b1, b2, a1
@@ -207,13 +199,13 @@ class TestGemRequire < Gem::TestCase
end
def test_multiple_gems_with_the_same_path
- a1 = new_spec "a", "1", { "b" => "> 0", "x" => "> 0" }
- b1 = new_spec "b", "1", { "c" => ">= 1" }, "lib/ib.rb"
- b2 = new_spec "b", "2", { "c" => ">= 2" }, "lib/ib.rb"
- x1 = new_spec "x", "1", nil, "lib/ib.rb"
- x2 = new_spec "x", "2", nil, "lib/ib.rb"
- c1 = new_spec "c", "1", nil, "lib/d.rb"
- c2 = new_spec("c", "2", nil, "lib/d.rb")
+ a1 = util_spec "a", "1", { "b" => "> 0", "x" => "> 0" }
+ b1 = util_spec "b", "1", { "c" => ">= 1" }, "lib/ib.rb"
+ b2 = util_spec "b", "2", { "c" => ">= 2" }, "lib/ib.rb"
+ x1 = util_spec "x", "1", nil, "lib/ib.rb"
+ x2 = util_spec "x", "2", nil, "lib/ib.rb"
+ c1 = util_spec "c", "1", nil, "lib/d.rb"
+ c2 = util_spec("c", "2", nil, "lib/d.rb")
install_specs c1, c2, x1, x2, b1, b2, a1
@@ -230,13 +222,13 @@ class TestGemRequire < Gem::TestCase
end
def test_unable_to_find_good_unresolved_version
- a1 = new_spec "a", "1", { "b" => "> 0" }
- b1 = new_spec "b", "1", { "c" => ">= 2" }, "lib/ib.rb"
- b2 = new_spec "b", "2", { "c" => ">= 3" }, "lib/ib.rb"
+ a1 = util_spec "a", "1", { "b" => "> 0" }
+ b1 = util_spec "b", "1", { "c" => ">= 2" }, "lib/ib.rb"
+ b2 = util_spec "b", "2", { "c" => ">= 3" }, "lib/ib.rb"
- c1 = new_spec "c", "1", nil, "lib/d.rb"
- c2 = new_spec "c", "2", nil, "lib/d.rb"
- c3 = new_spec "c", "3", nil, "lib/d.rb"
+ c1 = util_spec "c", "1", nil, "lib/d.rb"
+ c2 = util_spec "c", "2", nil, "lib/d.rb"
+ c3 = util_spec "c", "3", nil, "lib/d.rb"
install_specs c1, c2, c3, b1, b2, a1
@@ -279,10 +271,10 @@ class TestGemRequire < Gem::TestCase
end
def test_require_doesnt_traverse_development_dependencies
- a = new_spec("a", "1", nil, "lib/a.rb")
- z = new_spec("z", "1", "w" => "> 0")
- w1 = new_spec("w", "1") { |s| s.add_development_dependency "non-existent" }
- w2 = new_spec("w", "2") { |s| s.add_development_dependency "non-existent" }
+ a = util_spec("a", "1", nil, "lib/a.rb")
+ z = util_spec("z", "1", "w" => "> 0")
+ w1 = util_spec("w", "1") { |s| s.add_development_dependency "non-existent" }
+ w2 = util_spec("w", "2") { |s| s.add_development_dependency "non-existent" }
install_specs a, w1, w2, z
@@ -301,11 +293,27 @@ class TestGemRequire < Gem::TestCase
assert_equal %w(default-2.0.0.0), loaded_spec_names
end
+ def test_realworld_default_gem
+ begin
+ gem 'json'
+ rescue Gem::MissingSpecError
+ skip "default gems are only available after ruby installation"
+ end
+
+ cmd = <<-RUBY
+ $stderr = $stdout
+ require "json"
+ puts Gem.loaded_specs["json"].default_gem?
+ RUBY
+ output = Gem::Util.popen(Gem.ruby, "-e", cmd).strip
+ assert_equal "true", output
+ end
+
def test_default_gem_and_normal_gem
default_gem_spec = new_default_spec("default", "2.0.0.0",
nil, "default/gem.rb")
install_default_specs(default_gem_spec)
- normal_gem_spec = new_spec("default", "3.0", nil,
+ normal_gem_spec = util_spec("default", "3.0", nil,
"lib/default/gem.rb")
install_specs(normal_gem_spec)
assert_require "default/gem"
@@ -341,6 +349,87 @@ class TestGemRequire < Gem::TestCase
Kernel::RUBYGEMS_ACTIVATION_MONITOR.exit
end
+ def test_require_when_gem_defined
+ default_gem_spec = new_default_spec("default", "2.0.0.0",
+ nil, "default/gem.rb")
+ install_default_specs(default_gem_spec)
+ c = Class.new do
+ def self.gem(*args)
+ raise "received #gem with #{args.inspect}"
+ end
+ end
+ assert c.send(:require, "default/gem")
+ assert_equal %w(default-2.0.0.0), loaded_spec_names
+ end
+
+ def test_require_default_when_gem_defined
+ a = util_spec("a", "1", nil, "lib/a.rb")
+ install_specs a
+ c = Class.new do
+ def self.gem(*args)
+ raise "received #gem with #{args.inspect}"
+ end
+ end
+ assert c.send(:require, "a")
+ assert_equal %w(a-1), loaded_spec_names
+ end
+
+
+ def test_require_bundler
+ b1 = util_spec('bundler', '1', nil, "lib/bundler/setup.rb")
+ b2a = util_spec('bundler', '2.a', nil, "lib/bundler/setup.rb")
+ install_specs b1, b2a
+
+ require "rubygems/bundler_version_finder"
+ $:.clear
+ assert_require 'bundler/setup'
+ assert_equal %w[bundler-2.a], loaded_spec_names
+ assert_empty unresolved_names
+ end
+
+ def test_require_bundler_missing_bundler_version
+ Gem::BundlerVersionFinder.stub(:bundler_version_with_reason, ["55", "reason"]) do
+ b1 = util_spec('bundler', '1.999999999', nil, "lib/bundler/setup.rb")
+ b2a = util_spec('bundler', '2.a', nil, "lib/bundler/setup.rb")
+ install_specs b1, b2a
+
+ e = assert_raises Gem::MissingSpecVersionError do
+ gem('bundler')
+ end
+ assert_match "Could not find 'bundler' (55) required by reason.", e.message
+ end
+ end
+
+ def test_require_bundler_with_bundler_version
+ Gem::BundlerVersionFinder.stub(:bundler_version_with_reason, ["1", "reason"]) do
+ b1 = util_spec('bundler', '1.999999999', nil, "lib/bundler/setup.rb")
+ b2 = util_spec('bundler', '2', nil, "lib/bundler/setup.rb")
+ install_specs b1, b2
+
+ $:.clear
+ assert_require 'bundler/setup'
+ assert_equal %w[bundler-1.999999999], loaded_spec_names
+ end
+ end
+
+ if RUBY_VERSION >= "2.5"
+ def test_no_kernel_require_in_warn_with_uplevel
+ lib = File.realpath("../../../lib", __FILE__)
+ Dir.mktmpdir("warn_test") do |dir|
+ File.write(dir + "/sub.rb", "warn 'uplevel', 'test', uplevel: 1\n")
+ File.write(dir + "/main.rb", "require 'sub'\n")
+ _, err = capture_subprocess_io do
+ system(@@ruby, "-w", "-rpp", "--disable=gems", "-I", lib, "-C", dir, "-I.", "main.rb")
+ end
+ assert_equal "main.rb:1: warning: uplevel\ntest\n", err
+ _, err = capture_subprocess_io do
+ system(@@ruby, "-w", "-rpp", "--enable=gems", "-I", lib, "-C", dir, "-I.", "main.rb")
+ end
+ assert_equal "main.rb:1: warning: uplevel\ntest\n", err
+ end
+ end
+ end
+
def silence_warnings
old_verbose, $VERBOSE = $VERBOSE, false
yield
diff --git a/test/runner.rb b/test/runner.rb
index 1fe32fcfc8..d8b44644f7 100644
--- a/test/runner.rb
+++ b/test/runner.rb
@@ -26,14 +26,7 @@ require_relative 'lib/zombie_hunter'
require_relative 'lib/iseq_loader_checker'
if ENV['COVERAGE']
- %w[doclie simplecov-html simplecov].each do |f|
- $LOAD_PATH.unshift "#{src_testdir}/../coverage/#{f}/lib"
- end
-
- require 'simplecov'
- SimpleCov.start do
- add_filter "/test/"
- end
+ require_relative "../tool/test-coverage.rb"
end
begin
diff --git a/test/scanf/test_scanf.rb b/test/scanf/test_scanf.rb
index 827eb2fc3b..988ff99adc 100644
--- a/test/scanf/test_scanf.rb
+++ b/test/scanf/test_scanf.rb
@@ -258,6 +258,7 @@ module ScanfTests
[ "%G", "+3.25e2", [325.0] ],
[ "%f", "3.z", [3.0] ],
[ "%a", "0X1P+10", [1024.0] ],
+ [ "%a", "0X1P10", [1024.0] ],
[ "%A", "0x1.deadbeefp+99", [1.1851510441583988e+30] ],
# Testing embedded matches including literal '[' behavior
diff --git a/test/sdbm/test_sdbm.rb b/test/sdbm/test_sdbm.rb
index 7b2d39c9a1..6cea36d3a5 100644
--- a/test/sdbm/test_sdbm.rb
+++ b/test/sdbm/test_sdbm.rb
@@ -108,6 +108,7 @@ class TestSDBM < Test::Unit::TestCase
def test_s_open_error
skip "doesn't support to avoid read access by owner on Windows" if /mswin|mingw/ =~ RUBY_PLATFORM
+ skip "skipped because root can open anything" if Process.uid == 0
assert_instance_of(SDBM, sdbm = SDBM.open("#{@tmpdir}/#{@prefix}", 0))
assert_raise(Errno::EACCES) {
SDBM.open("#{@tmpdir}/#{@prefix}", 0)
@@ -519,6 +520,7 @@ class TestSDBM < Test::Unit::TestCase
end
def test_readonly
+ skip "skipped because root can read anything" if /mswin|mingw/ !~ RUBY_PLATFORM && Process.uid == 0
@sdbm["bar"] = "baz"
@sdbm.close
File.chmod(0444, @path + ".dir")
diff --git a/test/shell/test_command_processor.rb b/test/shell/test_command_processor.rb
index 99fe1b222a..7e1aa5bce7 100644
--- a/test/shell/test_command_processor.rb
+++ b/test/shell/test_command_processor.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: false
+require 'test/unit'
require 'shell'
require 'tmpdir'
@@ -66,4 +67,35 @@ class TestShell::CommandProcessor < Test::Unit::TestCase
Process.waitall
Dir.rmdir(path)
end
+
+ def test_test
+ name = "foo#{exeext}"
+ path = File.join(@tmpdir, name)
+ open(path, "w", 0644) {}
+
+ assert_equal(true, @shell[?e, path])
+ assert_equal(true, @shell[:e, path])
+ assert_equal(true, @shell["e", path])
+ assert_equal(true, @shell[:exist?, path])
+ assert_equal(true, @shell["exist?", path])
+ assert_raise_with_message(RuntimeError, /unsupported command/) do
+ assert_equal(true, @shell[:instance_eval, path])
+ end
+ ensure
+ Process.waitall
+ File.unlink(path)
+ end
+
+ def test_option_type
+ name = 'foo.cmd'
+ path = File.join(@tmpdir, name)
+
+ open(path, 'w', 0755) {}
+ assert_raise(TypeError) {
+ catch(catch_command_start) {@shell.system(name, 42)}
+ }
+ ensure
+ Process.waitall
+ File.unlink(path)
+ end
end
diff --git a/test/socket/test_addrinfo.rb b/test/socket/test_addrinfo.rb
index 132d172380..a06f3eb451 100644
--- a/test/socket/test_addrinfo.rb
+++ b/test/socket/test_addrinfo.rb
@@ -102,6 +102,14 @@ class TestSocketAddrinfo < Test::Unit::TestCase
assert(!ipv4_ai.unix?)
end
+ def test_error_message
+ e = assert_raise_with_message(SocketError, /getaddrinfo:/) do
+ Addrinfo.ip("...")
+ end
+ m = e.message
+ assert_not_equal([false, Encoding::ASCII_8BIT], [m.ascii_only?, m.encoding], proc {m.inspect})
+ end
+
def test_ipv4_address_predicates
list = [
[:ipv4_private?, "10.0.0.0", "10.255.255.255",
diff --git a/test/socket/test_basicsocket.rb b/test/socket/test_basicsocket.rb
index e17a675d8a..c8e9b23f83 100644
--- a/test/socket/test_basicsocket.rb
+++ b/test/socket/test_basicsocket.rb
@@ -3,6 +3,7 @@
begin
require "socket"
require "test/unit"
+ require "io/nonblock"
rescue LoadError
end
@@ -97,7 +98,7 @@ class TestSocket_BasicSocket < Test::Unit::TestCase
end
def socks
- sserv = TCPServer.new(0)
+ sserv = TCPServer.new('localhost', 0)
ssock = nil
t = Thread.new { ssock = sserv.accept }
csock = TCPSocket.new('localhost', sserv.addr[1])
@@ -152,4 +153,78 @@ class TestSocket_BasicSocket < Test::Unit::TestCase
sock.close
end
end
+
+ def test_read_write_nonblock
+ socks do |sserv, ssock, csock|
+ set_nb = true
+ buf = String.new
+ if ssock.respond_to?(:nonblock?)
+ assert_not_predicate(ssock, :nonblock?)
+ assert_not_predicate(csock, :nonblock?)
+ csock.nonblock = ssock.nonblock = false
+
+ # Linux may use MSG_DONTWAIT to avoid setting O_NONBLOCK
+ if RUBY_PLATFORM.match?(/linux/) && Socket.const_defined?(:MSG_DONTWAIT)
+ set_nb = false
+ end
+ end
+ assert_equal :wait_readable, ssock.read_nonblock(1, buf, exception: false)
+ assert_equal 5, csock.write_nonblock('hello')
+ IO.select([ssock])
+ assert_same buf, ssock.read_nonblock(5, buf, exception: false)
+ assert_equal 'hello', buf
+ buf = '*' * 16384
+ n = 0
+
+ case w = csock.write_nonblock(buf, exception: false)
+ when Integer
+ n += w
+ when :wait_writable
+ break
+ end while true
+
+ assert_equal :wait_writable, w
+ assert_raise(IO::WaitWritable) { loop { csock.write_nonblock(buf) } }
+ assert_operator n, :>, 0
+ assert_not_predicate(csock, :nonblock?, '[Feature #13362]') unless set_nb
+ csock.close
+
+ case r = ssock.read_nonblock(16384, buf, exception: false)
+ when String
+ next
+ when nil
+ break
+ when :wait_readable
+ IO.select([ssock], nil, nil, 10) or
+ flunk 'socket did not become readable'
+ else
+ flunk "unexpected read_nonblock return: #{r.inspect}"
+ end while true
+
+ assert_raise(EOFError) { ssock.read_nonblock(1) }
+
+ assert_not_predicate(ssock, :nonblock?) unless set_nb
+ end
+ end
+
+ def test_read_nonblock_mix_buffered
+ socks do |sserv, ssock, csock|
+ ssock.write("hello\nworld\n")
+ assert_equal "hello\n", csock.gets
+ IO.select([csock], nil, nil, 10) or
+ flunk 'socket did not become readable'
+ assert_equal "world\n", csock.read_nonblock(8)
+ end
+ end
+
+ def test_write_nonblock_buffered
+ socks do |sserv, ssock, csock|
+ ssock.sync = false
+ ssock.write("h")
+ assert_equal :wait_readable, csock.read_nonblock(1, exception: false)
+ assert_equal 4, ssock.write_nonblock("ello")
+ ssock.close
+ assert_equal "hello", csock.read(5)
+ end
+ end
end if defined?(BasicSocket)
diff --git a/test/socket/test_socket.rb b/test/socket/test_socket.rb
index 7e75837e8f..a84569d9f9 100644
--- a/test/socket/test_socket.rb
+++ b/test/socket/test_socket.rb
@@ -105,6 +105,8 @@ class TestSocket < Test::Unit::TestCase
def test_getnameinfo
assert_raise(SocketError) { Socket.getnameinfo(["AF_UNIX", 80, "0.0.0.0"]) }
+ assert_raise(ArgumentError) {Socket.getnameinfo(["AF_INET", "http\0", "example.net"])}
+ assert_raise(ArgumentError) {Socket.getnameinfo(["AF_INET", "http", "example.net\0"])}
end
def test_ip_address_list
@@ -378,11 +380,10 @@ class TestSocket < Test::Unit::TestCase
in6_ifreq = [ifr_name,ai.to_sockaddr].pack('a16A*')
s.ioctl(ulSIOCGIFFLAGS, in6_ifreq)
next true if in6_ifreq.unpack('A16L1').last & ulIFF_POINTOPOINT != 0
- else
- ifconfig ||= `/sbin/ifconfig`
- next true if ifconfig.scan(/^(\w+):(.*(?:\n\t.*)*)/).find do|ifname, value|
- value.include?(ai.ip_address) && value.include?('POINTOPOINT')
- end
+ end
+ ifconfig ||= `/sbin/ifconfig`
+ next true if ifconfig.scan(/^(\w+):(.*(?:\n\t.*)*)/).find do |_ifname, value|
+ value.include?(ai.ip_address) && value.include?('POINTOPOINT')
end
end
false
@@ -454,6 +455,34 @@ class TestSocket < Test::Unit::TestCase
}
end
+ def timestamp_retry_rw(s1, s2, t1, type)
+ IO.pipe do |r,w|
+ # UDP may not be reliable, keep sending until recvmsg returns:
+ th = Thread.new do
+ n = 0
+ begin
+ s2.send("a", 0, s1.local_address)
+ n += 1
+ end while IO.select([r], nil, nil, 0.1).nil?
+ n
+ end
+ assert_equal([[s1],[],[]], IO.select([s1], nil, nil, 30))
+ msg, _, _, stamp = s1.recvmsg
+ assert_equal("a", msg)
+ assert(stamp.cmsg_is?(:SOCKET, type))
+ w.close # stop th
+ n = th.value
+ n > 1 and
+ warn "UDP packet loss for #{type} over loopback, #{n} tries needed"
+ t2 = Time.now.strftime("%Y-%m-%d")
+ pat = Regexp.union([t1, t2].uniq)
+ assert_match(pat, stamp.inspect)
+ t = stamp.timestamp
+ assert_match(pat, t.strftime("%Y-%m-%d"))
+ stamp
+ end
+ end
+
def test_timestamp
return if /linux|freebsd|netbsd|openbsd|solaris|darwin/ !~ RUBY_PLATFORM
return if !defined?(Socket::AncillaryData) || !defined?(Socket::SO_TIMESTAMP)
@@ -462,17 +491,10 @@ class TestSocket < Test::Unit::TestCase
Addrinfo.udp("127.0.0.1", 0).bind {|s1|
Addrinfo.udp("127.0.0.1", 0).bind {|s2|
s1.setsockopt(:SOCKET, :TIMESTAMP, true)
- s2.send "a", 0, s1.local_address
- msg, _, _, stamp = s1.recvmsg
- assert_equal("a", msg)
- assert(stamp.cmsg_is?(:SOCKET, :TIMESTAMP))
+ stamp = timestamp_retry_rw(s1, s2, t1, :TIMESTAMP)
}
}
- t2 = Time.now.strftime("%Y-%m-%d")
- pat = Regexp.union([t1, t2].uniq)
- assert_match(pat, stamp.inspect)
t = stamp.timestamp
- assert_match(pat, t.strftime("%Y-%m-%d"))
pat = /\.#{"%06d" % t.usec}/
assert_match(pat, stamp.inspect)
end
@@ -489,17 +511,10 @@ class TestSocket < Test::Unit::TestCase
# SO_TIMESTAMPNS is available since Linux 2.6.22
return
end
- s2.send "a", 0, s1.local_address
- msg, _, _, stamp = s1.recvmsg
- assert_equal("a", msg)
- assert(stamp.cmsg_is?(:SOCKET, :TIMESTAMPNS))
+ stamp = timestamp_retry_rw(s1, s2, t1, :TIMESTAMPNS)
}
}
- t2 = Time.now.strftime("%Y-%m-%d")
- pat = Regexp.union([t1, t2].uniq)
- assert_match(pat, stamp.inspect)
t = stamp.timestamp
- assert_match(pat, t.strftime("%Y-%m-%d"))
pat = /\.#{"%09d" % t.nsec}/
assert_match(pat, stamp.inspect)
end
@@ -534,13 +549,15 @@ class TestSocket < Test::Unit::TestCase
begin sleep(0.1) end until serv_thread.stop?
sock = TCPSocket.new("localhost", server.addr[1])
client_thread = Thread.new do
- sock.readline
+ assert_raise(IOError, bug4390) {
+ sock.readline
+ }
end
begin sleep(0.1) end until client_thread.stop?
Timeout.timeout(1) do
sock.close
sock = nil
- assert_raise(IOError, bug4390) {client_thread.join}
+ client_thread.join
end
ensure
serv_thread.value.close
diff --git a/test/socket/test_tcp.rb b/test/socket/test_tcp.rb
index 450d1b35b5..11325fdedb 100644
--- a/test/socket/test_tcp.rb
+++ b/test/socket/test_tcp.rb
@@ -8,6 +8,15 @@ end
class TestSocket_TCPSocket < Test::Unit::TestCase
+ def test_inspect
+ TCPServer.open("localhost", 0) {|server|
+ assert_match(/AF_INET/, server.inspect)
+ TCPSocket.open("localhost", server.addr[1]) {|client|
+ assert_match(/AF_INET/, client.inspect)
+ }
+ }
+ end
+
def test_initialize_failure
# These addresses are chosen from TEST-NET-1, TEST-NET-2, and TEST-NET-3.
# [RFC 5737]
diff --git a/test/socket/test_udp.rb b/test/socket/test_udp.rb
index 5b23cce288..f060b65f2c 100644
--- a/test/socket/test_udp.rb
+++ b/test/socket/test_udp.rb
@@ -15,6 +15,21 @@ class TestSocket_UDPSocket < Test::Unit::TestCase
assert_nothing_raised { UDPSocket.open(:AF_INET) {} }
end
+ def test_inspect
+ UDPSocket.open() {|sock|
+ assert_match(/AF_INET\b/, sock.inspect)
+ }
+ if Socket.const_defined?(:AF_INET6)
+ begin
+ UDPSocket.open(Socket::AF_INET6) {|sock|
+ assert_match(/AF_INET6\b/, sock.inspect)
+ }
+ rescue Errno::EAFNOSUPPORT
+ skip 'AF_INET6 not supported by kernel'
+ end
+ end
+ end
+
def test_connect
s = UDPSocket.new
host = Object.new
diff --git a/test/socket/test_unix.rb b/test/socket/test_unix.rb
index 7edb5e5d4f..6efb1d60ee 100644
--- a/test/socket/test_unix.rb
+++ b/test/socket/test_unix.rb
@@ -9,7 +9,6 @@ require "test/unit"
require "tempfile"
require "timeout"
require "tmpdir"
-require "thread"
require "io/nonblock"
class TestSocket_UNIXSocket < Test::Unit::TestCase
@@ -285,6 +284,16 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase
File.unlink path if path && File.socket?(path)
end
+ def test_open_nul_byte
+ tmpfile = Tempfile.new("s")
+ path = tmpfile.path
+ tmpfile.close(true)
+ assert_raise(ArgumentError) {UNIXServer.open(path+"\0")}
+ assert_raise(ArgumentError) {UNIXSocket.open(path+"\0")}
+ ensure
+ File.unlink path if path && File.socket?(path)
+ end
+
def test_addr
bound_unix_socket(UNIXServer) {|serv, path|
UNIXSocket.open(path) {|c|
diff --git a/test/stringio/test_stringio.rb b/test/stringio/test_stringio.rb
index a900362157..6d1a6ea8d5 100644
--- a/test/stringio/test_stringio.rb
+++ b/test/stringio/test_stringio.rb
@@ -74,8 +74,45 @@ class TestStringIO < Test::Unit::TestCase
assert_equal("abc\n", StringIO.new("abc\n\ndef\n").gets)
assert_equal("abc\n\ndef\n", StringIO.new("abc\n\ndef\n").gets(nil))
assert_equal("abc\n\n", StringIO.new("abc\n\ndef\n").gets(""))
+ stringio = StringIO.new("abc\n\ndef\n")
+ assert_equal("abc\n\n", stringio.gets(""))
+ assert_equal("def\n", stringio.gets(""))
assert_raise(TypeError){StringIO.new("").gets(1, 1)}
assert_nothing_raised {StringIO.new("").gets(nil, nil)}
+
+ assert_string("", Encoding::UTF_8, StringIO.new("foo").gets(0))
+ end
+
+ def test_gets_chomp
+ assert_equal(nil, StringIO.new("").gets(chomp: true))
+ assert_equal("", StringIO.new("\n").gets(chomp: true))
+ assert_equal("a", StringIO.new("a\n").gets(chomp: true))
+ assert_equal("a", StringIO.new("a\nb\n").gets(chomp: true))
+ assert_equal("a", StringIO.new("a").gets(chomp: true))
+ assert_equal("a", StringIO.new("a\nb").gets(chomp: true))
+ assert_equal("abc", StringIO.new("abc\n\ndef\n").gets(chomp: true))
+ assert_equal("abc\n\ndef", StringIO.new("abc\n\ndef\n").gets(nil, chomp: true))
+ assert_equal("abc\n", StringIO.new("abc\n\ndef\n").gets("", chomp: true))
+ stringio = StringIO.new("abc\n\ndef\n")
+ assert_equal("abc\n", stringio.gets("", chomp: true))
+ assert_equal("def", stringio.gets("", chomp: true))
+
+ assert_string("", Encoding::UTF_8, StringIO.new("\n").gets(chomp: true))
+ end
+
+ def test_gets_chomp_eol
+ assert_equal(nil, StringIO.new("").gets(chomp: true))
+ assert_equal("", StringIO.new("\r\n").gets(chomp: true))
+ assert_equal("a", StringIO.new("a\r\n").gets(chomp: true))
+ assert_equal("a", StringIO.new("a\r\nb\r\n").gets(chomp: true))
+ assert_equal("a", StringIO.new("a").gets(chomp: true))
+ assert_equal("a", StringIO.new("a\r\nb").gets(chomp: true))
+ assert_equal("abc", StringIO.new("abc\r\n\r\ndef\r\n").gets(chomp: true))
+ assert_equal("abc\r\n\r\ndef", StringIO.new("abc\r\n\r\ndef\r\n").gets(nil, chomp: true))
+ assert_equal("abc\r\n", StringIO.new("abc\r\n\r\ndef\r\n").gets("", chomp: true))
+ stringio = StringIO.new("abc\r\n\r\ndef\r\n")
+ assert_equal("abc\r\n", stringio.gets("", chomp: true))
+ assert_equal("def", stringio.gets("", chomp: true))
end
def test_readlines
@@ -169,6 +206,16 @@ class TestStringIO < Test::Unit::TestCase
}
end
+ def test_write_with_multiple_arguments
+ s = ""
+ f = StringIO.new(s, "w")
+ f.write("foo", "bar")
+ f.close
+ assert_equal("foobar", s)
+ ensure
+ f.close unless f.closed?
+ end
+
def test_set_encoding
bug10285 = '[ruby-core:65240] [Bug #10285]'
f = StringIO.new()
@@ -405,6 +452,10 @@ class TestStringIO < Test::Unit::TestCase
t.ungetbyte("\u{30eb 30d3 30fc}")
assert_equal(0, t.pos)
assert_equal("\u{30eb 30d3 30fc}\u7d05\u7389bar\n", s)
+
+ assert_nothing_raised {t.ungetbyte(-1)}
+ assert_nothing_raised {t.ungetbyte(256)}
+ assert_nothing_raised {t.ungetbyte(1<<64)}
end
def test_ungetc
@@ -420,6 +471,9 @@ class TestStringIO < Test::Unit::TestCase
f.ungetc("y".ord)
assert_equal("y", f.getc)
assert_equal("2", f.getc)
+
+ assert_raise(RangeError) {f.ungetc(0x1ffffff)}
+ assert_raise(RangeError) {f.ungetc(0xffffffffffffff)}
ensure
f.close unless f.closed?
end
@@ -473,6 +527,17 @@ class TestStringIO < Test::Unit::TestCase
def test_each
f = StringIO.new("foo\nbar\nbaz\n")
assert_equal(["foo\n", "bar\n", "baz\n"], f.each.to_a)
+ f.rewind
+ assert_equal(["foo", "bar", "baz"], f.each(chomp: true).to_a)
+ f = StringIO.new("foo\nbar\n\nbaz\n")
+ assert_equal(["foo\nbar\n\n", "baz\n"], f.each("").to_a)
+ f.rewind
+ assert_equal(["foo\nbar\n", "baz"], f.each("", chomp: true).to_a)
+
+ f = StringIO.new("foo\r\nbar\r\n\r\nbaz\r\n")
+ assert_equal(["foo\r\nbar\r\n\r\n", "baz\r\n"], f.each("").to_a)
+ f.rewind
+ assert_equal(["foo\r\nbar\r\n", "baz"], f.each("", chomp: true).to_a)
end
def test_putc
@@ -523,13 +588,20 @@ class TestStringIO < Test::Unit::TestCase
assert_equal("\u3042\u3044", f.read(nil, nil), bug5207)
f.rewind
s = ""
- f.read(nil, s)
+ assert_same(s, f.read(nil, s))
assert_equal("\u3042\u3044", s, bug5207)
f.rewind
# not empty buffer
s = "0123456789"
- f.read(nil, s)
+ assert_same(s, f.read(nil, s))
assert_equal("\u3042\u3044", s)
+
+ bug13806 = '[ruby-core:82349] [Bug #13806]'
+ assert_string("", Encoding::UTF_8, f.read, bug13806)
+ assert_string("", Encoding::UTF_8, f.read(nil, nil), bug13806)
+ s.force_encoding(Encoding::US_ASCII)
+ assert_same(s, f.read(nil, s))
+ assert_string("", Encoding::UTF_8, s, bug13806)
end
def test_readpartial
@@ -654,9 +726,10 @@ class TestStringIO < Test::Unit::TestCase
s = StringIO.new
s.freeze
bug = '[ruby-core:33648]'
- assert_raise(RuntimeError, bug) {s.puts("foo")}
- assert_raise(RuntimeError, bug) {s.string = "foo"}
- assert_raise(RuntimeError, bug) {s.reopen("")}
+ exception_class = defined?(FrozenError) ? FrozenError : RuntimeError
+ assert_raise(exception_class, bug) {s.puts("foo")}
+ assert_raise(exception_class, bug) {s.string = "foo"}
+ assert_raise(exception_class, bug) {s.reopen("")}
end
def test_frozen_string
@@ -709,4 +782,8 @@ class TestStringIO < Test::Unit::TestCase
assert_equal(0x100000, s.pos)
end;
end
+
+ def assert_string(content, encoding, str, mesg = nil)
+ assert_equal([content, encoding], [str, str.encoding], mesg)
+ end
end
diff --git a/test/strscan/test_stringscanner.rb b/test/strscan/test_stringscanner.rb
index ee97e454fe..3423f9cfed 100644
--- a/test/strscan/test_stringscanner.rb
+++ b/test/strscan/test_stringscanner.rb
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# frozen_string_literal: false
+# frozen_string_literal: true
#
# test/strscan/test_stringscanner.rb
#
@@ -14,7 +14,7 @@ class TestStringScanner < Test::Unit::TestCase
assert_equal false, s.eos?
assert_equal false, s.tainted?
- str = 'test string'
+ str = 'test string'.dup
str.taint
s = StringScanner.new(str, false)
assert_instance_of StringScanner, s
@@ -22,7 +22,7 @@ class TestStringScanner < Test::Unit::TestCase
assert_same str, s.string
assert_equal true, s.string.tainted?
- str = 'test string'
+ str = 'test string'.dup
str.taint
s = StringScanner.new(str)
assert_equal true, s.string.tainted?
@@ -96,7 +96,7 @@ class TestStringScanner < Test::Unit::TestCase
end
def test_inspect
- str = 'test string'
+ str = 'test string'.dup
str.taint
s = StringScanner.new(str, false)
assert_instance_of String, s.inspect
@@ -124,7 +124,7 @@ class TestStringScanner < Test::Unit::TestCase
s.scan(/\w+/)
assert_equal true, s.eos?
- s = StringScanner.new('test')
+ s = StringScanner.new('test'.dup)
s.scan(/te/)
s.string.replace ''
assert_equal true, s.eos?
@@ -181,11 +181,11 @@ class TestStringScanner < Test::Unit::TestCase
end
def test_string_append
- s = StringScanner.new('tender')
+ s = StringScanner.new('tender'.dup)
s << 'love'
assert_equal 'tenderlove', s.string
- s.string = 'tender'
+ s.string = 'tender'.dup
s << 'love'
assert_equal 'tenderlove', s.string
end
@@ -213,7 +213,7 @@ class TestStringScanner < Test::Unit::TestCase
end
def test_concat
- s = StringScanner.new('a')
+ s = StringScanner.new('a'.dup)
s.scan(/a/)
s.concat 'b'
assert_equal false, s.eos?
@@ -246,7 +246,7 @@ class TestStringScanner < Test::Unit::TestCase
assert_nil s.scan(/\w+/)
- str = 'stra strb strc'
+ str = 'stra strb strc'.dup
str.taint
s = StringScanner.new(str, false)
tmp = s.scan(/\w+/)
@@ -267,7 +267,7 @@ class TestStringScanner < Test::Unit::TestCase
assert_nil s.scan(/\w+/)
assert_nil s.scan(/\w+/)
- s = StringScanner.new('test')
+ s = StringScanner.new('test'.dup)
s.scan(/te/)
# This assumes #string does not duplicate string,
# but it is implementation specific issue.
@@ -293,7 +293,7 @@ class TestStringScanner < Test::Unit::TestCase
assert_nil s.skip(/\s+/)
assert_equal true, s.eos?
- s = StringScanner.new('test')
+ s = StringScanner.new('test'.dup)
s.scan(/te/)
s.string.replace ''
assert_equal nil, s.skip(/./)
@@ -313,7 +313,7 @@ class TestStringScanner < Test::Unit::TestCase
assert_equal 'e', s.getch
assert_nil s.getch
- str = 'abc'
+ str = 'abc'.dup
str.taint
s = StringScanner.new(str)
assert_equal true, s.getch.tainted?
@@ -321,11 +321,11 @@ class TestStringScanner < Test::Unit::TestCase
assert_equal true, s.getch.tainted?
assert_nil s.getch
- s = StringScanner.new("\244\242".force_encoding("euc-jp"))
- assert_equal "\244\242".force_encoding("euc-jp"), s.getch
+ s = StringScanner.new("\244\242".dup.force_encoding("euc-jp"))
+ assert_equal "\244\242".dup.force_encoding("euc-jp"), s.getch
assert_nil s.getch
- s = StringScanner.new('test')
+ s = StringScanner.new('test'.dup)
s.scan(/te/)
s.string.replace ''
assert_equal nil, s.getch
@@ -341,7 +341,7 @@ class TestStringScanner < Test::Unit::TestCase
assert_nil s.get_byte
assert_nil s.get_byte
- str = 'abc'
+ str = 'abc'.dup
str.taint
s = StringScanner.new(str)
assert_equal true, s.get_byte.tainted?
@@ -349,12 +349,12 @@ class TestStringScanner < Test::Unit::TestCase
assert_equal true, s.get_byte.tainted?
assert_nil s.get_byte
- s = StringScanner.new("\244\242".force_encoding("euc-jp"))
- assert_equal "\244".force_encoding("euc-jp"), s.get_byte
- assert_equal "\242".force_encoding("euc-jp"), s.get_byte
+ s = StringScanner.new("\244\242".dup.force_encoding("euc-jp"))
+ assert_equal "\244".dup.force_encoding("euc-jp"), s.get_byte
+ assert_equal "\242".dup.force_encoding("euc-jp"), s.get_byte
assert_nil s.get_byte
- s = StringScanner.new('test')
+ s = StringScanner.new('test'.dup)
s.scan(/te/)
s.string.replace ''
assert_equal nil, s.get_byte
@@ -387,7 +387,7 @@ class TestStringScanner < Test::Unit::TestCase
assert_equal 't', s.matched
assert_equal false, s.matched.tainted?
- str = 'test'
+ str = 'test'.dup
str.taint
s = StringScanner.new(str)
s.scan(/\w+/)
@@ -447,11 +447,11 @@ class TestStringScanner < Test::Unit::TestCase
assert_nil s[0]
- s = StringScanner.new("\244\242".force_encoding("euc-jp"))
+ s = StringScanner.new("\244\242".dup.force_encoding("euc-jp"))
s.getch
- assert_equal "\244\242".force_encoding("euc-jp"), s[0]
+ assert_equal "\244\242".dup.force_encoding("euc-jp"), s[0]
- str = 'test'
+ str = 'test'.dup
str.taint
s = StringScanner.new(str)
s.scan(/(t)(e)(s)(t)/)
@@ -496,7 +496,7 @@ class TestStringScanner < Test::Unit::TestCase
s.scan(/never match/)
assert_nil s.pre_match
- str = 'test string'
+ str = 'test string'.dup
str.taint
s = StringScanner.new(str)
s.scan(/\w+/)
@@ -530,7 +530,7 @@ class TestStringScanner < Test::Unit::TestCase
s.scan(/./)
assert_nil s.post_match
- str = 'test string'
+ str = 'test string'.dup
str.taint
s = StringScanner.new(str)
s.scan(/\w+/)
@@ -585,14 +585,14 @@ class TestStringScanner < Test::Unit::TestCase
end
def test_encoding
- ss = StringScanner.new("\xA1\xA2".force_encoding("euc-jp"))
+ ss = StringScanner.new("\xA1\xA2".dup.force_encoding("euc-jp"))
assert_equal(Encoding::EUC_JP, ss.scan(/./e).encoding)
end
def test_generic_regexp
- ss = StringScanner.new("\xA1\xA2".force_encoding("euc-jp"))
+ ss = StringScanner.new("\xA1\xA2".dup.force_encoding("euc-jp"))
t = ss.scan(/./)
- assert_equal("\xa1\xa2".force_encoding("euc-jp"), t)
+ assert_equal("\xa1\xa2".dup.force_encoding("euc-jp"), t)
end
def test_set_pos
@@ -718,4 +718,36 @@ class TestStringScanner < Test::Unit::TestCase
s.scan(/test strin/)
assert_equal('#<StringScanner 10/16 "...strin" @ "g tes...">', s.inspect)
end
+
+ def test_aref_without_regex
+ s = StringScanner.new('abc')
+ s.get_byte
+ assert_nil(s[:c])
+ assert_nil(s["c"])
+ s.getch
+ assert_nil(s[:c])
+ assert_nil(s["c"])
+ end
+
+ def test_size
+ s = StringScanner.new("Fri Dec 12 1975 14:39")
+ s.scan(/(\w+) (\w+) (\d+) /)
+ assert_equal(4, s.size)
+ end
+
+ def test_captures
+ s = StringScanner.new("Fri Dec 12 1975 14:39")
+ s.scan(/(\w+) (\w+) (\d+) /)
+ assert_equal(["Fri", "Dec", "12"], s.captures)
+ s.scan(/(\w+) (\w+) (\d+) /)
+ assert_nil(s.captures)
+ end
+
+ def test_values_at
+ s = StringScanner.new("Fri Dec 12 1975 14:39")
+ s.scan(/(\w+) (\w+) (\d+) /)
+ assert_equal(["Fri Dec 12 ", "12", nil, "Dec"], s.values_at(0, -1, 5, 2))
+ s.scan(/(\w+) (\w+) (\d+) /)
+ assert_nil(s.values_at(0, -1, 5, 2))
+ end
end
diff --git a/test/test_abbrev.rb b/test/test_abbrev.rb
index be9b7d5005..67287138aa 100644
--- a/test/test_abbrev.rb
+++ b/test/test_abbrev.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'abbrev'
diff --git a/test/test_cmath.rb b/test/test_cmath.rb
index 2752ce782c..b3692bc7cf 100644
--- a/test/test_cmath.rb
+++ b/test/test_cmath.rb
@@ -1,9 +1,20 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'cmath'
class TestCMath < Test::Unit::TestCase
+ def test_deprecated_method
+ orig = $VERBOSE
+ $VERBOSE = true
+ assert_warning(/CMath#sqrt! is deprecated; use CMath#sqrt or Math#sqrt/) do
+ CMath.sqrt!(1)
+ end
+ assert_equal CMath.sqrt(1), CMath.sqrt!(1)
+ $VERBOSE = orig
+ end
+
def test_sqrt
+ assert_equal 1, CMath.sqrt(1)
assert_equal CMath.sqrt(1i), CMath.sqrt(1.0i), '[ruby-core:31672]'
assert_equal Complex(0,2), CMath.sqrt(-4.0)
assert_equal Complex(0,2), CMath.sqrt(-4)
@@ -13,6 +24,7 @@ class TestCMath < Test::Unit::TestCase
assert_equal Complex(0,3), CMath.sqrt(Rational(-9))
assert_in_delta (1.272019649514069+0.7861513777574233i), CMath.sqrt(1+2i)
assert_in_delta 3.0i, CMath.sqrt(-9)
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.sqrt("1") }
end
def test_log
@@ -24,6 +36,13 @@ class TestCMath < Test::Unit::TestCase
end
def test_trigonometric_functions
+ assert_equal 0, CMath.asin(0)
+ assert_equal 0, CMath.acos(1)
+ assert_equal 0, CMath.atan(0)
+ assert_equal 0, CMath.asinh(0)
+ assert_equal 0, CMath.acosh(1)
+ assert_equal 0, CMath.atanh(0)
+
assert_in_delta CMath.sinh(2).i, CMath.sin(2i)
assert_in_delta CMath.cosh(2), CMath.cos(2i)
assert_in_delta CMath.tanh(2).i, CMath.tan(2i)
@@ -52,14 +71,37 @@ class TestCMath < Test::Unit::TestCase
assert_in_delta (1.4693517443681852+1.0634400235777521i), CMath.asinh(1+2i)
assert_in_delta (1.528570919480998+1.1437177404024204i), CMath.acosh(1+2i)
assert_in_delta (0.17328679513998635+1.1780972450961724i), CMath.atanh(1+2i)
+
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.sin("0") }
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.cos("0") }
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.tan("0") }
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.sinh("0") }
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.cosh("0") }
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.tanh("0") }
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.asin("0") }
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.atan("0") }
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.asinh("0") }
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.acosh("0") }
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.atanh("0") }
end
def test_functions
+ assert_equal (1), CMath.exp(0)
assert_in_delta (-1.1312043837568135+2.4717266720048188i), CMath.exp(1+2i)
assert_in_delta (-1), CMath.exp(Math::PI.i)
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.exp("0") }
+
+ assert_equal (0), CMath.log2(1)
assert_in_delta (1.1609640474436813+1.5972779646881088i), CMath.log2(1+2i)
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.log2("1") }
+
+ assert_equal (0), CMath.log10(1)
assert_in_delta (0.3494850021680094+0.480828578784234i), CMath.log10(1+2i)
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.log10("1") }
+
+ assert_equal (0), CMath.atan2(0,1)
assert_in_delta (1.3389725222944935+0.4023594781085251i), CMath.atan2(1+2i,1)
+ assert_raise_with_message(TypeError, "Numeric Number required") { CMath.atan2("0", 1) }
end
def test_error_handling
diff --git a/test/test_delegate.rb b/test/test_delegate.rb
index 629d191fad..5320520de3 100644
--- a/test/test_delegate.rb
+++ b/test/test_delegate.rb
@@ -1,8 +1,16 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'delegate'
class TestDelegateClass < Test::Unit::TestCase
+ module PP
+ def mu_pp(obj)
+ str = super
+ str = "#<#{obj.class}: #{str}>" if Delegator === obj
+ str
+ end
+ end
+
module M
attr_reader :m
end
@@ -92,7 +100,7 @@ class TestDelegateClass < Test::Unit::TestCase
a = [42, :hello].freeze
d = SimpleDelegator.new(a)
assert_nothing_raised(bug2679) {d.dup[0] += 1}
- assert_raise(RuntimeError) {d.clone[0] += 1}
+ assert_raise(FrozenError) {d.clone[0] += 1}
d.freeze
assert(d.clone.frozen?)
assert(!d.dup.frozen?)
@@ -101,7 +109,7 @@ class TestDelegateClass < Test::Unit::TestCase
def test_frozen
d = SimpleDelegator.new([1, :foo])
d.freeze
- assert_raise(RuntimeError, '[ruby-dev:40314]#1') {d.__setobj__("foo")}
+ assert_raise(FrozenError, '[ruby-dev:40314]#1') {d.__setobj__("foo")}
assert_equal([1, :foo], d)
end
@@ -119,6 +127,18 @@ class TestDelegateClass < Test::Unit::TestCase
assert_equal([:bar], s.methods(false))
end
+ def test_eql?
+ extend PP
+ s0 = SimpleDelegator.new("foo")
+ s1 = SimpleDelegator.new("bar")
+ s2 = SimpleDelegator.new("foo")
+ assert_operator(s0, :eql?, s0)
+ assert_operator(s0, :eql?, "foo")
+ assert_operator(s0, :eql?, s2)
+ assert_not_operator(s0, :eql?, s1)
+ assert_not_operator(s0, :eql?, "bar")
+ end
+
class Foo
private
def delegate_test_private
diff --git a/test/test_extlibs.rb b/test/test_extlibs.rb
new file mode 100644
index 0000000000..7dc22ee8a3
--- /dev/null
+++ b/test/test_extlibs.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: false
+require "envutil"
+require "shellwords"
+
+class TestExtLibs < Test::Unit::TestCase
+ @extdir = $".grep(/\/rbconfig\.rb\z/) {break "#$`/ext"}
+
+ def self.check_existence(ext, add_msg = nil)
+ return if @excluded.any? {|i| File.fnmatch?(i, ext, File::FNM_CASEFOLD)}
+ add_msg = ". #{add_msg}" if add_msg
+ log = "#{@extdir}/#{ext}/mkmf.log"
+ define_method("test_existence_of_#{ext}") do
+ assert_separately([], <<-"end;", ignore_stderr: true) # do
+ log = #{log.dump}
+ msg = proc {
+ "extension library `#{ext}' is not found#{add_msg}\n" <<
+ (File.exist?(log) ? File.binread(log) : "\#{log} not found")
+ }
+ assert_nothing_raised(msg) do
+ require "#{ext}"
+ end
+ end;
+ end
+ end
+
+ def windows?
+ /mswin|mingw/ =~ RUBY_PLATFORM
+ end
+
+ excluded = [RbConfig::CONFIG, ENV].map do |conf|
+ if args = conf['configure_args']
+ args.shellsplit.grep(/\A--without-ext=/) {$'.split(/,/)}
+ end
+ end.flatten.compact
+ excluded << '+' if excluded.empty?
+ if windows?
+ excluded.map! {|i| i == '+' ? ['pty', 'syslog'] : i}
+ excluded.flatten!
+ else
+ excluded.map! {|i| i == '+' ? '*win32*' : i}
+ end
+ @excluded = excluded
+
+ check_existence "bigdecimal"
+ check_existence "continuation"
+ check_existence "coverage"
+ check_existence "date"
+ #check_existence "dbm" # depend on libdbm
+ check_existence "digest"
+ check_existence "digest/bubblebabble"
+ check_existence "digest/md5"
+ check_existence "digest/rmd160"
+ check_existence "digest/sha1"
+ check_existence "digest/sha2"
+ check_existence "etc"
+ check_existence "fcntl"
+ check_existence "fiber"
+ check_existence "fiddle"
+ #check_existence "gdbm" # depend on libgdbm
+ check_existence "io/console"
+ check_existence "io/nonblock"
+ check_existence "io/wait"
+ check_existence "json"
+ check_existence "nkf"
+ check_existence "objspace"
+ check_existence "openssl", "this may be false positive, but should assert because rubygems requires this"
+ check_existence "pathname"
+ check_existence "psych"
+ check_existence "pty"
+ check_existence "racc/cparse"
+ check_existence "rbconfig/sizeof"
+ #check_existence "readline" # depend on libreadline
+ check_existence "ripper"
+ check_existence "sdbm"
+ check_existence "socket"
+ check_existence "stringio"
+ check_existence "strscan"
+ check_existence "syslog"
+ check_existence "thread"
+ check_existence "Win32API"
+ check_existence "win32ole"
+ check_existence "zlib", "this may be false positive, but should assert because rubygems requires this"
+end
diff --git a/test/test_find.rb b/test/test_find.rb
index e3663e47b2..0e691214c5 100644
--- a/test/test_find.rb
+++ b/test/test_find.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'find'
require 'tmpdir'
@@ -104,6 +104,8 @@ class TestFind < Test::Unit::TestCase
def test_unreadable_dir
skip "no meaning test on Windows" if /mswin|mingw/ =~ RUBY_PLATFORM
+ skip "because root can read anything" if Process.uid == 0
+
Dir.mktmpdir {|d|
Dir.mkdir(dir = "#{d}/dir")
File.open("#{dir}/foo", "w"){}
@@ -157,6 +159,8 @@ class TestFind < Test::Unit::TestCase
assert_equal([d, dir, file], a)
skip "no meaning test on Windows" if /mswin|mingw/ =~ RUBY_PLATFORM
+ skip "skipped because root can read anything" if Process.uid == 0
+
a = []
assert_raise_with_message(Errno::EACCES, /#{Regexp.quote(file)}/) do
Find.find(d, ignore_error: false) {|f| a << f }
@@ -297,6 +301,23 @@ class TestFind < Test::Unit::TestCase
}
end
+ def test_to_path
+ c = Class.new {
+ def initialize(path)
+ @path = path
+ end
+
+ def to_path
+ @path
+ end
+ }
+ Dir.mktmpdir {|d|
+ a = []
+ Find.find(c.new(d)) {|f| a << f }
+ assert_equal([d], a)
+ }
+ end
+
class TestInclude < Test::Unit::TestCase
include Find
diff --git a/test/test_forwardable.rb b/test/test_forwardable.rb
index e48dbfe18f..b3f8467c5c 100644
--- a/test/test_forwardable.rb
+++ b/test/test_forwardable.rb
@@ -296,6 +296,14 @@ class TestForwardable < Test::Unit::TestCase
end
end
+ def test_non_module
+ str = String.new
+ str.extend Forwardable
+ str.instance_variable_set("@h", 42)
+ str.def_delegator("@h", :to_s, :forty_two)
+ assert_equal("42", str.forty_two)
+ end
+
private
def forwardable_class(
diff --git a/test/test_ipaddr.rb b/test/test_ipaddr.rb
index 86482a08bd..1aa24ebc54 100644
--- a/test/test_ipaddr.rb
+++ b/test/test_ipaddr.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'ipaddr'
@@ -21,11 +21,13 @@ class TC_IPAddr < Test::Unit::TestCase
assert_equal("::", a.to_s)
assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", a.to_string)
assert_equal(Socket::AF_INET6, a.family)
+ assert_equal(128, a.prefix)
a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678")
assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678", a.to_s)
assert_equal("0123:4567:89ab:cdef:0abc:def0:1234:5678", a.to_string)
assert_equal(Socket::AF_INET6, a.family)
+ assert_equal(128, a.prefix)
a = IPAddr.new("3ffe:505:2::/48")
assert_equal("3ffe:505:2::", a.to_s)
@@ -34,16 +36,19 @@ class TC_IPAddr < Test::Unit::TestCase
assert_equal(false, a.ipv4?)
assert_equal(true, a.ipv6?)
assert_equal("#<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>", a.inspect)
+ assert_equal(48, a.prefix)
a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::")
assert_equal("3ffe:505:2::", a.to_s)
assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
assert_equal(Socket::AF_INET6, a.family)
+ assert_equal(48, a.prefix)
a = IPAddr.new("0.0.0.0")
assert_equal("0.0.0.0", a.to_s)
assert_equal("0.0.0.0", a.to_string)
assert_equal(Socket::AF_INET, a.family)
+ assert_equal(32, a.prefix)
a = IPAddr.new("192.168.1.2")
assert_equal("192.168.1.2", a.to_s)
@@ -51,17 +56,27 @@ class TC_IPAddr < Test::Unit::TestCase
assert_equal(Socket::AF_INET, a.family)
assert_equal(true, a.ipv4?)
assert_equal(false, a.ipv6?)
+ assert_equal(32, a.prefix)
- a = IPAddr.new("192.168.1.2/24")
+ a = IPAddr.new("192.168.1.2/26")
assert_equal("192.168.1.0", a.to_s)
assert_equal("192.168.1.0", a.to_string)
assert_equal(Socket::AF_INET, a.family)
- assert_equal("#<IPAddr: IPv4:192.168.1.0/255.255.255.0>", a.inspect)
+ assert_equal("#<IPAddr: IPv4:192.168.1.0/255.255.255.192>", a.inspect)
+ assert_equal(26, a.prefix)
a = IPAddr.new("192.168.1.2/255.255.255.0")
assert_equal("192.168.1.0", a.to_s)
assert_equal("192.168.1.0", a.to_string)
assert_equal(Socket::AF_INET, a.family)
+ assert_equal(24, a.prefix)
+
+ (0..32).each do |prefix|
+ assert_equal(prefix, IPAddr.new("10.20.30.40/#{prefix}").prefix)
+ end
+ (0..128).each do |prefix|
+ assert_equal(prefix, IPAddr.new("1:2:3:4:5:6:7:8/#{prefix}").prefix)
+ end
assert_equal("0:0:0:1::", IPAddr.new("0:0:0:1::").to_s)
assert_equal("2001:200:300::", IPAddr.new("2001:200:300::/48").to_s)
@@ -79,6 +94,7 @@ class TC_IPAddr < Test::Unit::TestCase
assert_raise(IPAddr::InvalidPrefixError) { IPAddr.new("::1/255.255.255.0") }
assert_raise(IPAddr::InvalidPrefixError) { IPAddr.new("::1/129") }
assert_raise(IPAddr::InvalidPrefixError) { IPAddr.new("192.168.0.1/33") }
+ assert_raise(IPAddr::InvalidPrefixError) { IPAddr.new("192.168.0.1/255.255.255.1") }
assert_raise(IPAddr::AddressFamilyError) { IPAddr.new(1) }
assert_raise(IPAddr::AddressFamilyError) { IPAddr.new("::ffff:192.168.1.2/120", Socket::AF_INET) }
end
@@ -153,6 +169,27 @@ class TC_IPAddr < Test::Unit::TestCase
}
end
+ def test_prefix_writer
+ a = IPAddr.new("192.168.1.2")
+ ["1", "255.255.255.0", "ffff:ffff:ffff:ffff::", nil, 1.0, -1, 33].each { |x|
+ assert_raise(IPAddr::InvalidPrefixError) { a.prefix = x }
+ }
+ a = IPAddr.new("1:2:3:4:5:6:7:8")
+ ["1", "255.255.255.0", "ffff:ffff:ffff:ffff::", nil, 1.0, -1, 129].each { |x|
+ assert_raise(IPAddr::InvalidPrefixError) { a.prefix = x }
+ }
+
+ a = IPAddr.new("192.168.1.2")
+ a.prefix = 26
+ assert_equal(26, a.prefix)
+ assert_equal("192.168.1.0", a.to_s)
+
+ a = IPAddr.new("1:2:3:4:5:6:7:8")
+ a.prefix = 52
+ assert_equal(52, a.prefix)
+ assert_equal("1:2:3::", a.to_s)
+ end
+
def test_to_s
assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0001", IPAddr.new("3ffe:505:2::1").to_string)
assert_equal("3ffe:505:2::1", IPAddr.new("3ffe:505:2::1").to_s)
@@ -234,7 +271,14 @@ class TC_Operator < Test::Unit::TestCase
def test_mask
a = @a.mask(32)
assert_equal("3ffe:505::", a.to_s)
+ assert_equal("3ffe:505::", @a.mask("ffff:ffff::").to_s)
assert_equal("3ffe:505:2::", @a.to_s)
+ a = IPAddr.new("192.168.2.0/24")
+ assert_equal("192.168.0.0", a.mask(16).to_s)
+ assert_equal("192.168.0.0", a.mask("255.255.0.0").to_s)
+ assert_equal("192.168.2.0", a.to_s)
+ assert_raise(IPAddr::InvalidPrefixError) {a.mask("255.255.0.255")}
+ assert_raise(IPAddr::InvalidPrefixError) {@a.mask("ffff:1::")}
end
def test_include?
@@ -253,6 +297,64 @@ class TC_Operator < Test::Unit::TestCase
end
+ def test_loopback?
+ assert_equal(true, IPAddr.new('127.0.0.1').loopback?)
+ assert_equal(true, IPAddr.new('127.127.1.1').loopback?)
+ assert_equal(false, IPAddr.new('0.0.0.0').loopback?)
+ assert_equal(false, IPAddr.new('192.168.2.0').loopback?)
+ assert_equal(false, IPAddr.new('255.0.0.0').loopback?)
+ assert_equal(true, IPAddr.new('::1').loopback?)
+ assert_equal(false, IPAddr.new('::').loopback?)
+ assert_equal(false, IPAddr.new('3ffe:505:2::1').loopback?)
+ end
+
+ def test_private?
+ assert_equal(false, IPAddr.new('0.0.0.0').private?)
+ assert_equal(false, IPAddr.new('127.0.0.1').private?)
+
+ assert_equal(false, IPAddr.new('8.8.8.8').private?)
+ assert_equal(true, IPAddr.new('10.0.0.0').private?)
+ assert_equal(true, IPAddr.new('10.255.255.255').private?)
+ assert_equal(false, IPAddr.new('11.255.1.1').private?)
+
+ assert_equal(false, IPAddr.new('172.15.255.255').private?)
+ assert_equal(true, IPAddr.new('172.16.0.0').private?)
+ assert_equal(true, IPAddr.new('172.31.255.255').private?)
+ assert_equal(false, IPAddr.new('172.32.0.0').private?)
+
+ assert_equal(false, IPAddr.new('190.168.0.0').private?)
+ assert_equal(true, IPAddr.new('192.168.0.0').private?)
+ assert_equal(true, IPAddr.new('192.168.255.255').private?)
+ assert_equal(false, IPAddr.new('192.169.0.0').private?)
+
+ assert_equal(false, IPAddr.new('169.254.0.1').private?)
+
+ assert_equal(false, IPAddr.new('::1').private?)
+ assert_equal(false, IPAddr.new('::').private?)
+
+ assert_equal(false, IPAddr.new('fb84:8bf7:e905::1').private?)
+ assert_equal(true, IPAddr.new('fc84:8bf7:e905::1').private?)
+ assert_equal(true, IPAddr.new('fd84:8bf7:e905::1').private?)
+ assert_equal(false, IPAddr.new('fe84:8bf7:e905::1').private?)
+ end
+
+ def test_link_local?
+ assert_equal(false, IPAddr.new('0.0.0.0').link_local?)
+ assert_equal(false, IPAddr.new('127.0.0.1').link_local?)
+ assert_equal(false, IPAddr.new('10.0.0.0').link_local?)
+ assert_equal(false, IPAddr.new('172.16.0.0').link_local?)
+ assert_equal(false, IPAddr.new('192.168.0.0').link_local?)
+
+ assert_equal(true, IPAddr.new('169.254.1.1').link_local?)
+ assert_equal(true, IPAddr.new('169.254.254.255').link_local?)
+
+ assert_equal(false, IPAddr.new('::1').link_local?)
+ assert_equal(false, IPAddr.new('::').link_local?)
+ assert_equal(false, IPAddr.new('fb84:8bf7:e905::1').link_local?)
+
+ assert_equal(true, IPAddr.new('fe80::dead:beef:cafe:1234').link_local?)
+ end
+
def test_hash
a1 = IPAddr.new('192.168.2.0')
a2 = IPAddr.new('192.168.2.0')
diff --git a/test/test_mathn.rb b/test/test_mathn.rb
deleted file mode 100644
index 2ea049502c..0000000000
--- a/test/test_mathn.rb
+++ /dev/null
@@ -1,162 +0,0 @@
-# frozen_string_literal: false
-require 'test/unit'
-
-# mathn redefines too much. It must be isolated to child processes.
-class TestMathn < Test::Unit::TestCase
- def test_power
- stderr = $VERBOSE ? ["lib/mathn.rb is deprecated"] : []
- assert_in_out_err ['-r', 'mathn', '-e', 'a=1**2;!a'], "", [], stderr, '[ruby-core:25740]'
- assert_in_out_err ['-r', 'mathn', '-e', 'a=(1 << 126)**2;!a'], "", [], stderr, '[ruby-core:25740]'
- assert_in_out_err ['-r', 'mathn/complex', '-e', 'a=Complex(0,1)**4;!a'], "", [], [], '[ruby-core:44170]'
- assert_in_out_err ['-r', 'mathn/complex', '-e', 'a=Complex(0,1)**5;!a'], "", [], [], '[ruby-core:44170]'
- end
-
- def test_quo
- stderr = $VERBOSE ? ["lib/mathn.rb is deprecated"] : []
- assert_in_out_err ['-r', 'mathn'], <<-EOS, %w(OK), stderr, '[ruby-core:41575]'
- 1.quo(2); puts :OK
- EOS
- end
-
- def test_floor
- assert_separately(%w[-rmathn], <<-EOS, ignore_stderr: true)
- assert_equal( 2, ( 13/5).floor)
- assert_equal( 2, ( 5/2).floor)
- assert_equal( 2, ( 12/5).floor)
- assert_equal(-3, (-12/5).floor)
- assert_equal(-3, ( -5/2).floor)
- assert_equal(-3, (-13/5).floor)
-
- assert_equal( 2, ( 13/5).floor(0))
- assert_equal( 2, ( 5/2).floor(0))
- assert_equal( 2, ( 12/5).floor(0))
- assert_equal(-3, (-12/5).floor(0))
- assert_equal(-3, ( -5/2).floor(0))
- assert_equal(-3, (-13/5).floor(0))
-
- assert_equal(( 13/5), ( 13/5).floor(2))
- assert_equal(( 5/2), ( 5/2).floor(2))
- assert_equal(( 12/5), ( 12/5).floor(2))
- assert_equal((-12/5), (-12/5).floor(2))
- assert_equal(( -5/2), ( -5/2).floor(2))
- assert_equal((-13/5), (-13/5).floor(2))
- EOS
- end
-
- def test_ceil
- assert_separately(%w[-rmathn], <<-EOS, ignore_stderr: true)
- assert_equal( 3, ( 13/5).ceil)
- assert_equal( 3, ( 5/2).ceil)
- assert_equal( 3, ( 12/5).ceil)
- assert_equal(-2, (-12/5).ceil)
- assert_equal(-2, ( -5/2).ceil)
- assert_equal(-2, (-13/5).ceil)
-
- assert_equal( 3, ( 13/5).ceil(0))
- assert_equal( 3, ( 5/2).ceil(0))
- assert_equal( 3, ( 12/5).ceil(0))
- assert_equal(-2, (-12/5).ceil(0))
- assert_equal(-2, ( -5/2).ceil(0))
- assert_equal(-2, (-13/5).ceil(0))
-
- assert_equal(( 13/5), ( 13/5).ceil(2))
- assert_equal(( 5/2), ( 5/2).ceil(2))
- assert_equal(( 12/5), ( 12/5).ceil(2))
- assert_equal((-12/5), (-12/5).ceil(2))
- assert_equal(( -5/2), ( -5/2).ceil(2))
- assert_equal((-13/5), (-13/5).ceil(2))
- EOS
- end
-
- def test_truncate
- assert_separately(%w[-rmathn], <<-EOS, ignore_stderr: true)
- assert_equal( 2, ( 13/5).truncate)
- assert_equal( 2, ( 5/2).truncate)
- assert_equal( 2, ( 12/5).truncate)
- assert_equal(-2, (-12/5).truncate)
- assert_equal(-2, ( -5/2).truncate)
- assert_equal(-2, (-13/5).truncate)
-
- assert_equal( 2, ( 13/5).truncate(0))
- assert_equal( 2, ( 5/2).truncate(0))
- assert_equal( 2, ( 12/5).truncate(0))
- assert_equal(-2, (-12/5).truncate(0))
- assert_equal(-2, ( -5/2).truncate(0))
- assert_equal(-2, (-13/5).truncate(0))
-
- assert_equal(( 13/5), ( 13/5).truncate(2))
- assert_equal(( 5/2), ( 5/2).truncate(2))
- assert_equal(( 12/5), ( 12/5).truncate(2))
- assert_equal((-12/5), (-12/5).truncate(2))
- assert_equal(( -5/2), ( -5/2).truncate(2))
- assert_equal((-13/5), (-13/5).truncate(2))
- EOS
- end
-
- def test_round
- assert_separately(%w[-rmathn], <<-EOS, ignore_stderr: true)
- assert_equal( 3, ( 13/5).round)
- assert_equal( 2, ( 5/2).round)
- assert_equal( 2, ( 12/5).round)
- assert_equal(-2, (-12/5).round)
- assert_equal(-2, ( -5/2).round)
- assert_equal(-3, (-13/5).round)
-
- assert_equal( 3, ( 13/5).round(0))
- assert_equal( 2, ( 5/2).round(0))
- assert_equal( 2, ( 12/5).round(0))
- assert_equal(-2, (-12/5).round(0))
- assert_equal(-2, ( -5/2).round(0))
- assert_equal(-3, (-13/5).round(0))
-
- assert_equal(( 13/5), ( 13/5).round(2))
- assert_equal(( 5/2), ( 5/2).round(2))
- assert_equal(( 12/5), ( 12/5).round(2))
- assert_equal((-12/5), (-12/5).round(2))
- assert_equal(( -5/2), ( -5/2).round(2))
- assert_equal((-13/5), (-13/5).round(2))
-
- assert_equal( 3, ( 13/5).round(half: :even))
- assert_equal( 2, ( 5/2).round(half: :even))
- assert_equal( 2, ( 12/5).round(half: :even))
- assert_equal(-2, (-12/5).round(half: :even))
- assert_equal(-2, ( -5/2).round(half: :even))
- assert_equal(-3, (-13/5).round(half: :even))
-
- assert_equal( 3, ( 13/5).round(0, half: :even))
- assert_equal( 2, ( 5/2).round(0, half: :even))
- assert_equal( 2, ( 12/5).round(0, half: :even))
- assert_equal(-2, (-12/5).round(0, half: :even))
- assert_equal(-2, ( -5/2).round(0, half: :even))
- assert_equal(-3, (-13/5).round(0, half: :even))
-
- assert_equal(( 13/5), ( 13/5).round(2, half: :even))
- assert_equal(( 5/2), ( 5/2).round(2, half: :even))
- assert_equal(( 12/5), ( 12/5).round(2, half: :even))
- assert_equal((-12/5), (-12/5).round(2, half: :even))
- assert_equal(( -5/2), ( -5/2).round(2, half: :even))
- assert_equal((-13/5), (-13/5).round(2, half: :even))
-
- assert_equal( 3, ( 13/5).round(half: :up))
- assert_equal( 3, ( 5/2).round(half: :up))
- assert_equal( 2, ( 12/5).round(half: :up))
- assert_equal(-2, (-12/5).round(half: :up))
- assert_equal(-3, ( -5/2).round(half: :up))
- assert_equal(-3, (-13/5).round(half: :up))
-
- assert_equal( 3, ( 13/5).round(0, half: :up))
- assert_equal( 3, ( 5/2).round(0, half: :up))
- assert_equal( 2, ( 12/5).round(0, half: :up))
- assert_equal(-2, (-12/5).round(0, half: :up))
- assert_equal(-3, ( -5/2).round(0, half: :up))
- assert_equal(-3, (-13/5).round(0, half: :up))
-
- assert_equal(( 13/5), ( 13/5).round(2, half: :up))
- assert_equal(( 5/2), ( 5/2).round(2, half: :up))
- assert_equal(( 12/5), ( 12/5).round(2, half: :up))
- assert_equal((-12/5), (-12/5).round(2, half: :up))
- assert_equal(( -5/2), ( -5/2).round(2, half: :up))
- assert_equal((-13/5), (-13/5).round(2, half: :up))
- EOS
- end
-end
diff --git a/test/test_mutex_m.rb b/test/test_mutex_m.rb
index 0365265b8c..30971dd352 100644
--- a/test/test_mutex_m.rb
+++ b/test/test_mutex_m.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: false
require 'test/unit'
-require 'thread'
require 'mutex_m'
class TestMutexM < Test::Unit::TestCase
diff --git a/test/test_observer.rb b/test/test_observer.rb
index c2fa728004..8f8f24b3c5 100644
--- a/test/test_observer.rb
+++ b/test/test_observer.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'observer'
diff --git a/test/test_open3.rb b/test/test_open3.rb
index d52ab6a9bf..6842ac8d8c 100644
--- a/test/test_open3.rb
+++ b/test/test_open3.rb
@@ -2,6 +2,7 @@
require 'test/unit'
require 'open3'
+require_relative 'lib/jit_support'
class TestOpen3 < Test::Unit::TestCase
RUBY = EnvUtil.rubybin
@@ -54,9 +55,9 @@ class TestOpen3 < Test::Unit::TestCase
i.close
assert_equal("baz", o.read)
ensure
- i.close if !i.closed?
- o.close if !o.closed?
- e.close if !e.closed?
+ i.close
+ o.close
+ e.close
t.join
end
@@ -103,8 +104,8 @@ class TestOpen3 < Test::Unit::TestCase
r, w = IO.pipe
yield r, w
ensure
- r.close if !r.closed?
- w.close if !w.closed?
+ r.close
+ w.close
end
def with_reopen(io, arg)
@@ -113,7 +114,7 @@ class TestOpen3 < Test::Unit::TestCase
yield old
ensure
io.reopen(old)
- old.close if old && !old.closed?
+ old.close
end
def test_popen2
@@ -126,7 +127,7 @@ class TestOpen3 < Test::Unit::TestCase
i.close
STDERR.reopen(old)
assert_equal("zo", o.read)
- assert_equal("ze", r.read)
+ assert_equal("ze", JITSupport.remove_mjit_logs(r.read))
}
}
}
@@ -155,6 +156,17 @@ class TestOpen3 < Test::Unit::TestCase
assert(s.success?)
end
+ def test_capture3_stdin_data_io
+ IO.pipe {|r, w|
+ w.write "i"
+ w.close
+ o, e, s = Open3.capture3(RUBY, '-e', 'i=STDIN.read; print i+"o"; STDOUT.flush; STDERR.print i+"e"', :stdin_data=>r)
+ assert_equal("io", o)
+ assert_equal("ie", e)
+ assert(s.success?)
+ }
+ end
+
def test_capture3_flip
o, e, s = Open3.capture3(RUBY, '-e', 'STDOUT.sync=true; 1000.times { print "o"*1000; STDERR.print "e"*1000 }')
assert_equal("o"*1000000, o)
@@ -168,12 +180,32 @@ class TestOpen3 < Test::Unit::TestCase
assert(s.success?)
end
+ def test_capture2_stdin_data_io
+ IO.pipe {|r, w|
+ w.write "i"
+ w.close
+ o, s = Open3.capture2(RUBY, '-e', 'i=STDIN.read; print i+"o"', :stdin_data=>r)
+ assert_equal("io", o)
+ assert(s.success?)
+ }
+ end
+
def test_capture2e
oe, s = Open3.capture2e(RUBY, '-e', 'i=STDIN.read; print i+"o"; STDOUT.flush; STDERR.print i+"e"', :stdin_data=>"i")
assert_equal("ioie", oe)
assert(s.success?)
end
+ def test_capture2e_stdin_data_io
+ IO.pipe {|r, w|
+ w.write "i"
+ w.close
+ oe, s = Open3.capture2e(RUBY, '-e', 'i=STDIN.read; print i+"o"; STDOUT.flush; STDERR.print i+"e"', :stdin_data=>r)
+ assert_equal("ioie", oe)
+ assert(s.success?)
+ }
+ end
+
def test_capture3_stdin_data
o, e, s = Open3.capture3(RUBY, '-e', '', :stdin_data=>"z"*(1024*1024))
assert_equal("", o)
@@ -284,4 +316,9 @@ class TestOpen3 < Test::Unit::TestCase
}
end
+ def test_integer_and_symbol_key
+ command = [RUBY, '-e', 'puts "test_integer_and_symbol_key"']
+ out, status = Open3.capture2(*command, :chdir => '.', 2 => IO::NULL)
+ assert_equal("test_integer_and_symbol_key\n", out)
+ end
end
diff --git a/test/test_pp.rb b/test/test_pp.rb
index 5f8216dcce..d0104651ea 100644
--- a/test/test_pp.rb
+++ b/test/test_pp.rb
@@ -194,4 +194,13 @@ class PPFileStatTest < Test::Unit::TestCase
end
end
+class PPAbstractSyntaxTree < Test::Unit::TestCase
+ AST = RubyVM::AbstractSyntaxTree
+ def test_literal
+ ast = AST.parse("1")
+ expected = "(SCOPE@1:0-1:1 tbl: [] args: nil body: (LIT@1:0-1:1 1))"
+ assert_equal(expected, PP.singleline_pp(ast, ''.dup), ast)
+ end
+end
+
end
diff --git a/test/test_prime.rb b/test/test_prime.rb
index 7f15894abf..9c793daca0 100644
--- a/test/test_prime.rb
+++ b/test/test_prime.rb
@@ -27,6 +27,15 @@ class TestPrime < Test::Unit::TestCase
assert_equal PRIMES, primes
end
+ def test_integer_each_prime
+ primes = []
+ Integer.each_prime(1000) do |p|
+ break if p > 541
+ primes << p
+ end
+ assert_equal PRIMES, primes
+ end
+
def test_each_by_prime_number_theorem
3.upto(15) do |i|
max = 2**i
@@ -124,6 +133,78 @@ class TestPrime < Test::Unit::TestCase
assert_raise(ArgumentError) { Prime.prime?(1.2) }
end
+ def test_prime?
+ assert_equal Prime.prime?(1), false
+ assert_equal Prime.prime?(2), true
+ assert_equal Prime.prime?(4), false
+ end
+
+ class TestPseudoPrimeGenerator < Test::Unit::TestCase
+ def test_upper_bound
+ pseudo_prime_generator = Prime::PseudoPrimeGenerator.new(42)
+ assert_equal pseudo_prime_generator.upper_bound, 42
+ end
+
+ def test_succ
+ pseudo_prime_generator = Prime::PseudoPrimeGenerator.new(42)
+ assert_raise(NotImplementedError) { pseudo_prime_generator.succ }
+ end
+
+ def test_next
+ pseudo_prime_generator = Prime::PseudoPrimeGenerator.new(42)
+ assert_raise(NotImplementedError) { pseudo_prime_generator.next }
+ end
+
+ def test_rewind
+ pseudo_prime_generator = Prime::PseudoPrimeGenerator.new(42)
+ assert_raise(NotImplementedError) { pseudo_prime_generator.rewind }
+ end
+ end
+
+ class TestTrialDivisionGenerator < Test::Unit::TestCase
+ # The first 100 prime numbers
+ PRIMES = [
+ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37,
+ 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83,
+ 89, 97, 101, 103, 107, 109, 113, 127, 131,
+ 137, 139, 149, 151, 157, 163, 167, 173, 179,
+ 181, 191, 193, 197, 199, 211, 223, 227, 229,
+ 233, 239, 241, 251, 257, 263, 269, 271, 277,
+ 281, 283, 293, 307, 311, 313, 317, 331, 337,
+ 347, 349, 353, 359, 367, 373, 379, 383, 389,
+ 397, 401, 409, 419, 421, 431, 433, 439, 443,
+ 449, 457, 461, 463, 467, 479, 487, 491, 499,
+ 503, 509, 521, 523, 541,
+ ]
+
+ def test_each
+ primes = []
+ Prime.each(nil, Prime::TrialDivisionGenerator.new) do |p|
+ break if p > 541
+ primes << p
+ end
+ assert_equal PRIMES, primes
+ end
+
+ def test_rewind
+ generator = Prime::TrialDivisionGenerator.new
+ assert_equal generator.next, 2
+ assert_equal generator.next, 3
+ generator.rewind
+ assert_equal generator.next, 2
+ end
+ end
+
+ class TestGenerator23 < Test::Unit::TestCase
+ def test_rewind
+ generator = Prime::Generator23.new
+ assert_equal generator.next, 2
+ assert_equal generator.next, 3
+ generator.rewind
+ assert_equal generator.next, 2
+ end
+ end
+
class TestInteger < Test::Unit::TestCase
def test_prime_division
pd = PRIMES.inject(&:*).prime_division
@@ -174,20 +255,22 @@ class TestPrime < Test::Unit::TestCase
def test_eratosthenes_works_fine_after_timeout
sieve = Prime::EratosthenesSieve.instance
sieve.send(:initialize)
+ # simulates that Timeout.timeout interrupts Prime::EratosthenesSieve#compute_primes
+ class << Integer
+ alias_method :org_sqrt, :sqrt
+ end
begin
- # simulates that Timeout.timeout interrupts Prime::EratosthenesSieve#extend_table
- def sieve.Integer(n)
- n = super(n)
+ def Integer.sqrt(n)
sleep 10 if /compute_primes/ =~ caller.first
- return n
+ org_sqrt(n)
end
-
assert_raise(Timeout::Error) do
Timeout.timeout(0.5) { Prime.each(7*37){} }
end
ensure
- class << sieve
- remove_method :Integer
+ class << Integer
+ alias_method :sqrt, :org_sqrt
+ remove_method :org_sqrt
end
end
diff --git a/test/test_pstore.rb b/test/test_pstore.rb
index e584e2f045..52b74f3775 100644
--- a/test/test_pstore.rb
+++ b/test/test_pstore.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'pstore'
require 'tmpdir'
@@ -133,7 +133,7 @@ class PStoreTest < Test::Unit::TestCase
def test_pstore_files_are_accessed_as_binary_files
bug5311 = '[ruby-core:39503]'
n = 128
- assert_in_out_err(["-Eutf-8:utf-8", "-rpstore", "-", @pstore_file], <<-SRC, [bug5311], [], bug5311, timeout: 15)
+ assert_in_out_err(["-Eutf-8:utf-8", "-rpstore", "-", @pstore_file], <<-SRC, [bug5311], [], bug5311, timeout: 30)
@pstore = PStore.new(ARGV[0])
(1..#{n}).each do |i|
@pstore.transaction {@pstore["Key\#{i}"] = "value \#{i}"}
diff --git a/test/test_pty.rb b/test/test_pty.rb
index cedf00f918..9e40a3d2fb 100644
--- a/test/test_pty.rb
+++ b/test/test_pty.rb
@@ -18,8 +18,8 @@ class TestPTY < Test::Unit::TestCase
else
assert_equal("a\r\n", r.gets)
ensure
- r.close if r
- w.close if w
+ r&.close
+ w&.close
Process.wait pid if pid
end
@@ -196,7 +196,6 @@ class TestPTY < Test::Unit::TestCase
rescue RuntimeError
skip $!
else
- assert_equal(pid, st1.pid) if st1
assert_nil(st1)
assert_equal(pid, st2.pid)
end
@@ -215,7 +214,6 @@ class TestPTY < Test::Unit::TestCase
rescue RuntimeError
skip $!
else
- assert_equal(pid, st1.pid) if st1
assert_nil(st1)
assert_equal(pid, st2.pid)
end
@@ -239,4 +237,3 @@ class TestPTY < Test::Unit::TestCase
skip $!
end
end if defined? PTY
-
diff --git a/test/test_rbconfig.rb b/test/test_rbconfig.rb
index 1bbf01b9a6..fcbbbd8500 100644
--- a/test/test_rbconfig.rb
+++ b/test/test_rbconfig.rb
@@ -51,4 +51,13 @@ class TestRbConfig < Test::Unit::TestCase
assert_match(/\$\(sitearch|\$\(rubysitearchprefix\)/, val, "#{key} #{bug7823}")
end
end
+
+ if /darwin/ =~ RUBY_PLATFORM
+ def test_sdkroot
+ assert_separately([{"SDKROOT" => "$(prefix)/SDKRoot"}], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_equal RbConfig::CONFIG["prefix"]+"/SDKRoot", RbConfig::CONFIG["SDKROOT"]
+ end;
+ end
+ end
end
diff --git a/test/test_securerandom.rb b/test/test_securerandom.rb
index cd5cb1dee2..1bc35f91f8 100644
--- a/test/test_securerandom.rb
+++ b/test/test_securerandom.rb
@@ -70,40 +70,6 @@ if false
end
end
- def test_s_random_bytes_without_openssl
- begin
- require 'openssl'
- rescue LoadError
- return
- end
- begin
- load_path = $LOAD_PATH.dup
- loaded_features = $LOADED_FEATURES.dup
- openssl = Object.instance_eval { remove_const(:OpenSSL) }
-
- remove_feature('securerandom.rb')
- remove_feature('openssl.rb')
- Dir.mktmpdir do |dir|
- open(File.join(dir, 'openssl.rb'), 'w') { |f|
- f << 'raise LoadError'
- }
- $LOAD_PATH.unshift(dir)
- v = $VERBOSE
- begin
- $VERBOSE = false
- require 'securerandom'
- ensure
- $VERBOSE = v
- end
- test_s_random_bytes
- end
- ensure
- $LOADED_FEATURES.replace(loaded_features)
- $LOAD_PATH.replace(load_path)
- Object.const_set(:OpenSSL, openssl)
- end
- end
-
def test_s_hex
s = @it.hex
assert_equal(16 * 2, s.size)
@@ -177,6 +143,14 @@ end
assert_match(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/, uuid)
end
+ def test_alphanumeric
+ 65.times do |n|
+ an = @it.alphanumeric(n)
+ assert_match(/\A[0-9a-zA-Z]*\z/, an)
+ assert_equal(n, an.length)
+ end
+ end
+
def protect
begin
yield
@@ -198,4 +172,23 @@ end
def assert_in_range(range, result, mesg = nil)
assert(range.cover?(result), message(mesg) {"Expected #{result} to be in #{range}"})
end
+
+ def test_with_openssl
+ begin
+ require 'openssl'
+ rescue LoadError
+ return
+ end
+ assert_equal(Encoding::ASCII_8BIT, @it.send(:gen_random_openssl, 16).encoding)
+ 65.times do |idx|
+ assert_equal(idx, @it.send(:gen_random_openssl, idx).size)
+ end
+ end
+
+ def test_repeated_gen_random
+ assert_nothing_raised NoMethodError, '[ruby-core:92633] [Bug #15847]' do
+ @it.gen_random(1)
+ @it.gen_random(1)
+ end
+ end
end
diff --git a/test/test_set.rb b/test/test_set.rb
index 07c404ab79..b20920e63e 100644
--- a/test/test_set.rb
+++ b/test/test_set.rb
@@ -200,6 +200,23 @@ class TC_Set < Test::Unit::TestCase
assert_equal(false, set.include?(true))
end
+ def test_eqq
+ set = Set[1,2,3]
+
+ assert_equal(true, set === 1)
+ assert_equal(true, set === 2)
+ assert_equal(true, set === 3)
+ assert_equal(false, set === 0)
+ assert_equal(false, set === nil)
+
+ set = Set["1",nil,"2",nil,"0","1",false]
+ assert_equal(true, set === nil)
+ assert_equal(true, set === false)
+ assert_equal(true, set === "1")
+ assert_equal(false, set === 0)
+ assert_equal(false, set === true)
+ end
+
def test_superset?
set = Set[1,2,3]
@@ -528,6 +545,24 @@ class TC_Set < Test::Unit::TestCase
assert_equal(Set.new(1..10), set)
end
+ def test_filter!
+ set = Set.new(1..10)
+ ret = set.filter! { |i| i <= 10 }
+ assert_equal(nil, ret)
+ assert_equal(Set.new(1..10), set)
+
+ set = Set.new(1..10)
+ ret = set.filter! { |i| i % 3 != 0 }
+ assert_same(set, ret)
+ assert_equal(Set[1,2,4,5,7,8,10], set)
+
+ set = Set.new(1..10)
+ enum = set.filter!
+ assert_equal(set.size, enum.size)
+ assert_equal(nil, enum.each { |i| i <= 10 })
+ assert_equal(Set.new(1..10), set)
+ end
+
def test_merge
set = Set[1,2,3]
@@ -676,7 +711,7 @@ class TC_Set < Test::Unit::TestCase
set << 4
assert_same orig, set.freeze
assert_equal true, set.frozen?
- assert_raise(RuntimeError) {
+ assert_raise(FrozenError) {
set << 5
}
assert_equal 4, set.size
@@ -699,21 +734,31 @@ class TC_Set < Test::Unit::TestCase
set2 = set1.clone
assert_predicate set2, :frozen?
- assert_raise(RuntimeError) {
+ assert_raise(FrozenError) {
set2.add 5
}
end
def test_inspect
- set1 = Set[1]
+ set1 = Set[1, 2]
+ assert_equal('#<Set: {1, 2}>', set1.inspect)
+
+ set2 = Set[Set[0], 1, 2, set1]
+ assert_equal('#<Set: {#<Set: {0}>, 1, 2, #<Set: {1, 2}>}>', set2.inspect)
+
+ set1.add(set2)
+ assert_equal('#<Set: {#<Set: {0}>, 1, 2, #<Set: {1, 2, #<Set: {...}>}>}>', set2.inspect)
+ end
- assert_equal('#<Set: {1}>', set1.inspect)
+ def test_to_s
+ set1 = Set[1, 2]
+ assert_equal('#<Set: {1, 2}>', set1.to_s)
set2 = Set[Set[0], 1, 2, set1]
- assert_equal(false, set2.inspect.include?('#<Set: {...}>'))
+ assert_equal('#<Set: {#<Set: {0}>, 1, 2, #<Set: {1, 2}>}>', set2.to_s)
set1.add(set2)
- assert_equal(true, set1.inspect.include?('#<Set: {...}>'))
+ assert_equal('#<Set: {#<Set: {0}>, 1, 2, #<Set: {1, 2, #<Set: {...}>}>}>', set2.to_s)
end
def test_compare_by_identity
@@ -734,6 +779,19 @@ class TC_Set < Test::Unit::TestCase
assert_equal(3, set.size)
assert_equal(array.uniq.sort, set.sort)
end
+
+ def test_reset
+ [Set, Class.new(Set)].each { |klass|
+ a = [1, 2]
+ b = [1]
+ set = klass.new([a, b])
+
+ b << 2
+ set.reset
+
+ assert_equal(klass.new([a]), set, klass.name)
+ }
+ end
end
class TC_SortedSet < Test::Unit::TestCase
@@ -802,6 +860,45 @@ class TC_SortedSet < Test::Unit::TestCase
set << 42
assert_equal(7, e.size)
end
+
+ def test_freeze
+ orig = set = SortedSet[3,2,1]
+ assert_equal false, set.frozen?
+ set << 4
+ assert_same orig, set.freeze
+ assert_equal true, set.frozen?
+ assert_raise(FrozenError) {
+ set << 5
+ }
+ assert_equal 4, set.size
+
+ # https://bugs.ruby-lang.org/issues/12091
+ assert_nothing_raised {
+ assert_equal [1,2,3,4], set.to_a
+ }
+ end
+
+ def test_freeze_dup
+ set1 = SortedSet[1,2,3]
+ set1.freeze
+ set2 = set1.dup
+
+ assert_not_predicate set2, :frozen?
+ assert_nothing_raised {
+ set2.add 4
+ }
+ end
+
+ def test_freeze_clone
+ set1 = SortedSet[1,2,3]
+ set1.freeze
+ set2 = set1.clone
+
+ assert_predicate set2, :frozen?
+ assert_raise(FrozenError) {
+ set2.add 5
+ }
+ end
end
class TC_Enumerable < Test::Unit::TestCase
diff --git a/test/test_sync.rb b/test/test_sync.rb
new file mode 100644
index 0000000000..e3294ff824
--- /dev/null
+++ b/test/test_sync.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'sync'
+require 'timeout'
+
+class SyncTest < Test::Unit::TestCase
+ class Tester
+ include Sync_m
+ end
+
+ def test_sync_lock_and_wakeup
+ tester = Tester.new
+
+ tester.sync_lock(:EX)
+
+ t = Thread.new { tester.sync_lock(:EX) }
+
+ sleep 0.1 until t.stop?
+ t.wakeup
+ sleep 0.1 until t.stop?
+
+ assert_equal(tester.sync_waiting.uniq, tester.sync_waiting)
+ ensure
+ t.kill
+ t.join
+ end
+
+ def test_sync_upgrade_and_wakeup
+ tester = Tester.new
+ tester.sync_lock(:SH)
+
+ t = Thread.new do
+ tester.sync_lock(:SH)
+ tester.sync_lock(:EX)
+ end
+
+ sleep 0.1 until t.stop?
+ t.wakeup
+ sleep 0.1 until t.stop?
+
+ tester.sync_upgrade_waiting.each { |ary|
+ assert(!tester.sync_waiting.include?(ary[0]))
+ }
+ assert_equal(tester.sync_waiting.uniq, tester.sync_waiting)
+ assert_equal(tester.sync_waiting, [])
+ ensure
+ t.kill
+ t.join
+ end
+
+ def test_sync_lock_and_raise
+ tester= Tester.new
+ tester.sync_lock(:EX)
+
+ t = Thread.new {
+ assert_raise(RuntimeError) {
+ tester.sync_lock(:EX)
+ }
+ }
+
+ sleep 0.1 until t.stop?
+ sleep 1 if RubyVM::MJIT.enabled? # t.stop? behaves unexpectedly with --jit-wait
+ t.raise
+ t.join
+
+ assert_equal(tester.sync_waiting.uniq, tester.sync_waiting)
+ assert_equal(tester.sync_waiting, [])
+ end
+end
diff --git a/test/test_syslog.rb b/test/test_syslog.rb
index c66e5f5fb2..2a7476191e 100644
--- a/test/test_syslog.rb
+++ b/test/test_syslog.rb
@@ -113,7 +113,7 @@ class TestSyslog < Test::Unit::TestCase
end
def syslog_line_regex(ident, message)
- /(?:^| )#{Regexp.quote(ident)}(?:\[([1-9][0-9]*)\])?(?: |[: ].* )#{Regexp.quote(message)}$/
+ /(?:^| )#{Regexp.quote(ident)}(?:\[([1-9][0-9]*)\])?(?: | ([1-9][0-9]*) - - ||[: ].* )#{Regexp.quote(message)}$/
end
def test_log
@@ -168,8 +168,9 @@ class TestSyslog < Test::Unit::TestCase
end
m = re.match(line)
assert_not_nil(m)
- assert_not_nil(m[1])
- assert_equal(pid, m[1].to_i)
+ output_pid = m[1] || m[2]
+ assert_not_nil(output_pid)
+ assert_equal(pid, output_pid.to_i)
}
}
end
diff --git a/test/test_tempfile.rb b/test/test_tempfile.rb
index cd2828df37..203059e41c 100644
--- a/test/test_tempfile.rb
+++ b/test/test_tempfile.rb
@@ -1,7 +1,6 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'tempfile'
-require 'thread'
class TestTempfile < Test::Unit::TestCase
def initialize(*)
@@ -20,6 +19,10 @@ class TestTempfile < Test::Unit::TestCase
end
end
+ def test_leackchecker
+ assert_instance_of(Tempfile, Tempfile.allocate)
+ end
+
def test_basic
t = tempfile("foo")
path = t.path
@@ -35,6 +38,8 @@ class TestTempfile < Test::Unit::TestCase
assert_nothing_raised(SecurityError, bug3733) {
proc {$SAFE = 1; File.expand_path(Dir.tmpdir)}.call
}
+ ensure
+ $SAFE = 0
end
def test_saves_in_given_directory
@@ -243,6 +248,13 @@ puts Tempfile.new('foo').path
assert_equal 5, t.size
end
+ def test_size_on_empty_file
+ t = tempfile("foo")
+ t.write("")
+ t.close
+ assert_equal 0, t.size
+ end
+
def test_concurrency
threads = []
tempfiles = []
@@ -332,6 +344,13 @@ puts Tempfile.new('foo').path
assert_file.exist?(path)
}
assert_file.not_exist?(path)
+
+ Tempfile.create("tempfile-create") {|f|
+ path = f.path
+ f.close
+ File.unlink(f.path)
+ }
+ assert_file.not_exist?(path)
end
def test_create_without_block
@@ -342,7 +361,7 @@ puts Tempfile.new('foo').path
f.close
assert_file.exist?(path)
ensure
- f.close if f && !f.closed?
+ f&.close
File.unlink path if path
end
@@ -354,5 +373,54 @@ puts Tempfile.new('foo').path
}
assert_file.not_exist?(path)
end
-end
+ TRAVERSAL_PATH = Array.new(Dir.pwd.split('/').count, '..').join('/') + Dir.pwd + '/'
+
+ def test_open_traversal_dir
+ expect = Dir.glob(TRAVERSAL_PATH + '*').count
+ t = Tempfile.open([TRAVERSAL_PATH, 'foo'])
+ actual = Dir.glob(TRAVERSAL_PATH + '*').count
+ assert_equal expect, actual
+ rescue Errno::EINVAL
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ assert "ok"
+ else
+ raise $!
+ end
+ ensure
+ t&.close!
+ end
+
+ def test_new_traversal_dir
+ expect = Dir.glob(TRAVERSAL_PATH + '*').count
+ t = Tempfile.new(TRAVERSAL_PATH + 'foo')
+ actual = Dir.glob(TRAVERSAL_PATH + '*').count
+ assert_equal expect, actual
+ rescue Errno::EINVAL
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ assert "ok"
+ else
+ raise $!
+ end
+ ensure
+ t&.close!
+ end
+
+ def test_create_traversal_dir
+ expect = Dir.glob(TRAVERSAL_PATH + '*').count
+ t = Tempfile.create(TRAVERSAL_PATH + 'foo')
+ actual = Dir.glob(TRAVERSAL_PATH + '*').count
+ assert_equal expect, actual
+ rescue Errno::EINVAL
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ assert "ok"
+ else
+ raise $!
+ end
+ ensure
+ if t
+ t.close
+ File.unlink(t.path)
+ end
+ end
+end
diff --git a/test/test_time.rb b/test/test_time.rb
index 398ab7279b..fb30bea723 100644
--- a/test/test_time.rb
+++ b/test/test_time.rb
@@ -105,6 +105,8 @@ class TestTimeExtension < Test::Unit::TestCase # :nodoc:
t = Time.utc(1996, 12, 20, 0, 39, 57)
s = "1996-12-19T16:39:57-08:00"
assert_equal(t, Time.__send__(method, s))
+ assert_equal(t, Time.__send__(method, s.sub(/:(?=00\z)/, '')))
+ assert_equal(t, Time.__send__(method, s.sub(/:00\z/, '')))
# There is no way to generate time string with arbitrary timezone.
s = "1996-12-20T00:39:57Z"
assert_equal(t, Time.__send__(method, s))
@@ -343,6 +345,13 @@ class TestTimeExtension < Test::Unit::TestCase # :nodoc:
Time.parse("2000-01-01T00:00:00+11:00", nil))
end
+ def test_parse_offset_hour_minute_second
+ t = Time.at(-100000000000).utc
+ assert_equal(t, Time.parse("1200-02-15 BC 14:13:20-00"))
+ assert_equal(t, Time.parse("1200-02-15 BC 14:13:20-00:00"))
+ assert_equal(t, Time.parse("1200-02-15 BC 14:13:20-00:00:00"))
+ end
+
def test_parse_leap_second
t = Time.utc(1998,12,31,23,59,59)
assert_equal(t, Time.parse("Thu Dec 31 23:59:59 UTC 1998"))
@@ -486,6 +495,20 @@ class TestTimeExtension < Test::Unit::TestCase # :nodoc:
assert_equal(true, t.utc?)
end
+ def test_strptime_j
+ t = Time.strptime("2018-365", "%Y-%j")
+ assert_equal(2018, t.year)
+ assert_equal(12, t.mon)
+ assert_equal(31, t.day)
+ assert_equal(0, t.hour)
+ assert_equal(0, t.min)
+ assert_equal(0, t.sec)
+ t = Time.strptime("2018-091", "%Y-%j")
+ assert_equal(2018, t.year)
+ assert_equal(4, t.mon)
+ assert_equal(1, t.day)
+ end
+
def test_nsec
assert_equal(123456789, Time.parse("2000-01-01T00:00:00.123456789+00:00").tv_nsec)
end
@@ -504,4 +527,23 @@ class TestTimeExtension < Test::Unit::TestCase # :nodoc:
define_method(test) {__send__(sub, :xmlschema)}
define_method(test.sub(/xmlschema/, 'iso8601')) {__send__(sub, :iso8601)}
end
+
+ def test_parse_with_various_object
+ d = Date.new(2010, 10, 28)
+ dt = DateTime.new(2010, 10, 28)
+ md = MyDate.new(10, 28, 2010)
+
+ t = Time.local(2010, 10, 28, 21, 26, 00)
+ assert_equal(t, Time.parse("21:26", d))
+ assert_equal(t, Time.parse("21:26", dt))
+ assert_equal(t, Time.parse("21:26", md))
+ end
+
+ class MyDate
+ attr_reader :mon, :day, :year
+
+ def initialize(mon, day, year)
+ @mon, @day, @year = mon, day, year
+ end
+ end
end
diff --git a/test/test_timeout.rb b/test/test_timeout.rb
index 09073e96cd..c57d90c063 100644
--- a/test/test_timeout.rb
+++ b/test/test_timeout.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: false
require 'test/unit'
require 'timeout'
-require 'thread'
class TestTimeout < Test::Unit::TestCase
def test_queue
diff --git a/test/test_tmpdir.rb b/test/test_tmpdir.rb
index 4fc4bca4d5..bede8c0ab4 100644
--- a/test/test_tmpdir.rb
+++ b/test/test_tmpdir.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'tmpdir'
@@ -20,6 +20,8 @@ class TestTmpdir < Test::Unit::TestCase
tmpdir << "foo"
assert_equal(tmpdir_org, Dir.tmpdir)
}.join
+ ensure
+ $SAFE = 0
end
def test_world_writable
@@ -31,6 +33,12 @@ class TestTmpdir < Test::Unit::TestCase
assert_equal(tmpdir, Dir.tmpdir)
File.chmod(0777, tmpdir)
assert_not_equal(tmpdir, Dir.tmpdir)
+ newdir = Dir.mktmpdir("d", tmpdir) do |dir|
+ assert_file.directory? dir
+ assert_equal(tmpdir, File.dirname(dir))
+ dir
+ end
+ assert_file.not_exist?(newdir)
File.chmod(01777, tmpdir)
assert_equal(tmpdir, Dir.tmpdir)
ensure
@@ -56,4 +64,41 @@ class TestTmpdir < Test::Unit::TestCase
assert_kind_of(String, d)
}
end
+
+ def test_mktmpdir_mutate
+ bug16918 = '[ruby-core:98563]'
+ assert_nothing_raised(bug16918) do
+ assert_mktmpdir_traversal do |traversal_path|
+ Dir.mktmpdir(traversal_path + 'foo') do |actual|
+ actual << "foo"
+ end
+ end
+ end
+ end
+
+ def test_mktmpdir_traversal
+ assert_mktmpdir_traversal do |traversal_path|
+ Dir.mktmpdir(traversal_path + 'foo') do |actual|
+ actual
+ end
+ end
+ end
+
+ def test_mktmpdir_traversal_array
+ assert_mktmpdir_traversal do |traversal_path|
+ Dir.mktmpdir([traversal_path, 'foo']) do |actual|
+ actual
+ end
+ end
+ end
+
+ def assert_mktmpdir_traversal
+ Dir.mktmpdir do |target|
+ target = target.chomp('/') + '/'
+ traversal_path = target.sub(/\A\w:/, '') # for DOSISH
+ traversal_path = Array.new(target.count('/')-2, '..').join('/') + traversal_path
+ actual = yield traversal_path
+ assert_not_send([File.absolute_path(actual), :start_with?, target])
+ end
+ end
end
diff --git a/test/test_unicode_normalize.rb b/test/test_unicode_normalize.rb
index 347741dcb2..0346b4c629 100644
--- a/test/test_unicode_normalize.rb
+++ b/test/test_unicode_normalize.rb
@@ -9,10 +9,11 @@ require 'unicode_normalize/normalize'
class TestUnicodeNormalize < Test::Unit::TestCase
UNICODE_VERSION = RbConfig::CONFIG['UNICODE_VERSION']
- UNICODE_DATA_PATH = "../enc/unicode/data/#{UNICODE_VERSION}"
+ path = File.expand_path("../enc/unicode/data/#{UNICODE_VERSION}", __dir__)
+ UNICODE_DATA_PATH = File.directory?("#{path}/ucd") ? "#{path}/ucd" : path
def self.expand_filename(basename)
- File.expand_path("#{UNICODE_DATA_PATH}/#{basename}.txt", __dir__)
+ File.expand_path("#{basename}.txt", UNICODE_DATA_PATH)
end
end
@@ -145,7 +146,9 @@ class TestUnicodeNormalize
generate_test_check_false :NFC, :NFKC, :nfkc
generate_test_check_false :NFD, :NFKC, :nfkc
generate_test_check_false :NFKD, :NFKC, :nfkc
+end
+class TestUnicodeNormalize
def test_non_UTF_8
assert_equal "\u1E0A".encode('UTF-16BE'), "D\u0307".encode('UTF-16BE').unicode_normalize(:nfc)
assert_equal true, "\u1E0A".encode('UTF-16BE').unicode_normalized?(:nfc)
@@ -164,6 +167,13 @@ class TestUnicodeNormalize
assert_equal "\u1100\u1161\u11A8", "\uAC00\u11A8".unicode_normalize(:nfd)
end
+ # preventive tests for (non-)bug #14934
+ def test_no_trailing_jamo
+ assert_equal "\u1100\u1176\u11a8", "\u1100\u1176\u11a8".unicode_normalize(:nfc)
+ assert_equal "\uae30\u11a7", "\u1100\u1175\u11a7".unicode_normalize(:nfc)
+ assert_equal "\uae30\u11c3", "\u1100\u1175\u11c3".unicode_normalize(:nfc)
+ end
+
def test_hangul_plus_accents
assert_equal "\uAC00\u0323\u0300", "\uAC00\u0300\u0323".unicode_normalize(:nfc)
assert_equal "\uAC00\u0323\u0300", "\u1100\u1161\u0300\u0323".unicode_normalize(:nfc)
@@ -177,6 +187,10 @@ class TestUnicodeNormalize
assert_raise(Encoding::CompatibilityError) { "abc".force_encoding('ISO-8859-1').unicode_normalized? }
end
+ def test_reiwa
+ assert_equal "\u4EE4\u548C", "\u32FF".unicode_normalize(:nfkc)
+ end
+
def test_us_ascii
ascii_string = 'abc'.encode('US-ASCII')
diff --git a/test/test_weakref.rb b/test/test_weakref.rb
index 7520e6bc83..f2308dbb5a 100644
--- a/test/test_weakref.rb
+++ b/test/test_weakref.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'weakref'
diff --git a/test/test_win32api.rb b/test/test_win32api.rb
index 6e39ca3360..f135964a27 100644
--- a/test/test_win32api.rb
+++ b/test/test_win32api.rb
@@ -1,8 +1,11 @@
# frozen_string_literal: false
require "test/unit"
begin
+ $VERBOSE, verbose = nil, $VERBOSE
require "Win32API"
rescue LoadError
+ensure
+ $VERBOSE = verbose
end
class TestWin32API < Test::Unit::TestCase
diff --git a/test/testunit/test_assertion.rb b/test/testunit/test_assertion.rb
index 7e772cc7b9..0c7c4882b8 100644
--- a/test/testunit/test_assertion.rb
+++ b/test/testunit/test_assertion.rb
@@ -14,4 +14,16 @@ class TestAssertion < Test::Unit::TestCase
end;
end
end
+
+ def return_in_assert_raise
+ assert_raise(RuntimeError) do
+ return
+ end
+ end
+
+ def test_assert_raise
+ assert_raise(MiniTest::Assertion) do
+ return_in_assert_raise
+ end
+ end
end
diff --git a/test/testunit/test_parallel.rb b/test/testunit/test_parallel.rb
index 3d85f9c211..2e5ad86733 100644
--- a/test/testunit/test_parallel.rb
+++ b/test/testunit/test_parallel.rb
@@ -5,6 +5,8 @@ require 'timeout'
module TestParallel
PARALLEL_RB = "#{File.dirname(__FILE__)}/../lib/test/unit/parallel.rb"
TESTS = "#{File.dirname(__FILE__)}/tests_for_parallel"
+ # use large timeout for --jit-wait
+ TIMEOUT = EnvUtil.apply_timeout_scale(RubyVM::MJIT.enabled? ? 100 : 10)
class TestParallelWorker < Test::Unit::TestCase
def setup
@@ -34,15 +36,20 @@ module TestParallel
end
end
ensure
- @worker_in.close
- @worker_out.close
+ begin
+ @worker_in.close
+ @worker_out.close
+ rescue Errno::EPIPE
+ # may already broken and rescue'ed in above code
+ end
end
def test_run
- Timeout.timeout(10) do
+ Timeout.timeout(TIMEOUT) do
assert_match(/^ready/,@worker_out.gets)
@worker_in.puts "run #{TESTS}/ptest_first.rb test"
assert_match(/^okay/,@worker_out.gets)
+ assert_match(/^record/,@worker_out.gets)
assert_match(/^p/,@worker_out.gets)
assert_match(/^done/,@worker_out.gets)
assert_match(/^ready/,@worker_out.gets)
@@ -50,12 +57,14 @@ module TestParallel
end
def test_run_multiple_testcase_in_one_file
- Timeout.timeout(10) do
+ Timeout.timeout(TIMEOUT) do
assert_match(/^ready/,@worker_out.gets)
@worker_in.puts "run #{TESTS}/ptest_second.rb test"
assert_match(/^okay/,@worker_out.gets)
+ assert_match(/^record/,@worker_out.gets)
assert_match(/^p/,@worker_out.gets)
assert_match(/^done/,@worker_out.gets)
+ assert_match(/^record/,@worker_out.gets)
assert_match(/^p/,@worker_out.gets)
assert_match(/^done/,@worker_out.gets)
assert_match(/^ready/,@worker_out.gets)
@@ -63,17 +72,20 @@ module TestParallel
end
def test_accept_run_command_multiple_times
- Timeout.timeout(10) do
+ Timeout.timeout(TIMEOUT) do
assert_match(/^ready/,@worker_out.gets)
@worker_in.puts "run #{TESTS}/ptest_first.rb test"
assert_match(/^okay/,@worker_out.gets)
+ assert_match(/^record/,@worker_out.gets)
assert_match(/^p/,@worker_out.gets)
assert_match(/^done/,@worker_out.gets)
assert_match(/^ready/,@worker_out.gets)
@worker_in.puts "run #{TESTS}/ptest_second.rb test"
assert_match(/^okay/,@worker_out.gets)
+ assert_match(/^record/,@worker_out.gets)
assert_match(/^p/,@worker_out.gets)
assert_match(/^done/,@worker_out.gets)
+ assert_match(/^record/,@worker_out.gets)
assert_match(/^p/,@worker_out.gets)
assert_match(/^done/,@worker_out.gets)
assert_match(/^ready/,@worker_out.gets)
@@ -81,7 +93,7 @@ module TestParallel
end
def test_p
- Timeout.timeout(10) do
+ Timeout.timeout(TIMEOUT) do
@worker_in.puts "run #{TESTS}/ptest_first.rb test"
while buf = @worker_out.gets
break if /^p (.+?)$/ =~ buf
@@ -91,13 +103,11 @@ module TestParallel
end
def test_done
- Timeout.timeout(10) do
+ Timeout.timeout(TIMEOUT) do
@worker_in.puts "run #{TESTS}/ptest_forth.rb test"
- 7.times { @worker_out.gets }
- buf = @worker_out.gets
- assert_match(/^done (.+?)$/, buf)
-
- /^done (.+?)$/ =~ buf
+ while buf = @worker_out.gets
+ break if /^done (.+?)$/ =~ buf
+ end
result = Marshal.load($1.chomp.unpack("m")[0])
@@ -116,7 +126,7 @@ module TestParallel
end
def test_quit
- Timeout.timeout(10) do
+ Timeout.timeout(TIMEOUT) do
@worker_in.puts "quit"
assert_match(/^bye$/m,@worker_out.read)
end
@@ -142,7 +152,7 @@ module TestParallel
rescue Timeout::Error
Process.kill(:KILL, @test_pid) if @test_pid
ensure
- @test_out.close if @test_out
+ @test_out&.close
end
end
@@ -152,41 +162,42 @@ module TestParallel
"--ruby", @options[:ruby].join(" "),
"-j","0", out: File::NULL, err: o)
o.close
- Timeout.timeout(10) {
+ Timeout.timeout(TIMEOUT) {
assert_match(/Error: parameter of -j option should be greater than 0/,@test_out.read)
}
end
def test_should_run_all_without_any_leaks
spawn_runner
- buf = Timeout.timeout(10) {@test_out.read}
- assert_match(/^[SFE\.]{9}$/,buf)
+ buf = Timeout.timeout(TIMEOUT) {@test_out.read}
+ assert_match(/^9 tests/,buf)
end
def test_should_retry_failed_on_workers
spawn_runner
- buf = Timeout.timeout(10) {@test_out.read}
+ buf = Timeout.timeout(TIMEOUT) {@test_out.read}
assert_match(/^Retrying\.+$/,buf)
end
def test_no_retry_option
spawn_runner "--no-retry"
- buf = Timeout.timeout(10) {@test_out.read}
+ buf = Timeout.timeout(TIMEOUT) {@test_out.read}
refute_match(/^Retrying\.+$/,buf)
assert_match(/^ +\d+\) Failure:\nTestD#test_fail_at_worker/,buf)
end
def test_jobs_status
spawn_runner "--jobs-status"
- buf = Timeout.timeout(10) {@test_out.read}
+ buf = Timeout.timeout(TIMEOUT) {@test_out.read}
assert_match(/\d+=ptest_(first|second|third|forth) */,buf)
end
def test_separate
# this test depends to --jobs-status
spawn_runner "--jobs-status", "--separate"
- buf = Timeout.timeout(10) {@test_out.read}
- assert(buf.scan(/(\d+?)[:=]/).flatten.uniq.size > 1)
+ buf = Timeout.timeout(TIMEOUT) {@test_out.read}
+ assert(buf.scan(/^\[\s*\d+\/\d+\]\s*(\d+?)=/).flatten.uniq.size > 1,
+ message("retried tests should run in different processes") {buf})
end
end
end
diff --git a/test/thread/test_cv.rb b/test/thread/test_cv.rb
deleted file mode 100644
index 6779cb37ef..0000000000
--- a/test/thread/test_cv.rb
+++ /dev/null
@@ -1,225 +0,0 @@
-# frozen_string_literal: false
-require 'test/unit'
-require 'thread'
-require 'tmpdir'
-
-class TestConditionVariable < Test::Unit::TestCase
- ConditionVariable = Thread::ConditionVariable
- Mutex = Thread::Mutex
-
- def test_initialized
- assert_raise(TypeError) {
- ConditionVariable.allocate.wait(nil)
- }
- end
-
- def test_condvar_signal_and_wait
- mutex = Mutex.new
- condvar = ConditionVariable.new
- result = []
- mutex.synchronize do
- t = Thread.new do
- mutex.synchronize do
- result << 1
- condvar.signal
- end
- end
-
- result << 0
- condvar.wait(mutex)
- result << 2
- t.join
- end
- assert_equal([0, 1, 2], result)
- end
-
- def test_condvar_wait_exception_handling
- # Calling wait in the only thread running should raise a ThreadError of
- # 'stopping only thread'
- mutex = Mutex.new
- condvar = ConditionVariable.new
-
- locked = false
- thread = Thread.new do
- Thread.current.abort_on_exception = false
- mutex.synchronize do
- begin
- condvar.wait(mutex)
- rescue Exception
- locked = mutex.locked?
- raise
- end
- end
- end
-
- until thread.stop?
- sleep(0.1)
- end
-
- thread.raise Interrupt, "interrupt a dead condition variable"
- assert_raise(Interrupt) { thread.value }
- assert(locked)
- end
-
- def test_condvar_wait_and_broadcast
- nr_threads = 3
- threads = Array.new
- mutex = Mutex.new
- condvar = ConditionVariable.new
- result = []
-
- nr_threads.times do |i|
- threads[i] = Thread.new do
- mutex.synchronize do
- result << "C1"
- condvar.wait mutex
- result << "C2"
- end
- end
- end
- sleep 0.1
- mutex.synchronize do
- result << "P1"
- condvar.broadcast
- result << "P2"
- end
- Timeout.timeout(5) do
- nr_threads.times do |i|
- threads[i].join
- end
- end
-
- assert_equal ["C1", "C1", "C1", "P1", "P2", "C2", "C2", "C2"], result
- end
-
- def test_condvar_wait_deadlock
- assert_in_out_err([], <<-INPUT, /\Afatal\nNo live threads left\. Deadlock/, [])
- require "thread"
-
- mutex = Mutex.new
- cv = ConditionVariable.new
-
- klass = nil
- mesg = nil
- begin
- mutex.lock
- cv.wait mutex
- mutex.unlock
- rescue Exception => e
- klass = e.class
- mesg = e.message
- end
- puts klass
- print mesg
-INPUT
- end
-
- def test_condvar_wait_deadlock_2
- nr_threads = 3
- threads = Array.new
- mutex = Mutex.new
- condvar = ConditionVariable.new
-
- nr_threads.times do |i|
- if (i != 0)
- mutex.unlock
- end
- threads[i] = Thread.new do
- mutex.synchronize do
- condvar.wait mutex
- end
- end
- mutex.lock
- end
-
- assert_raise(Timeout::Error) do
- Timeout.timeout(0.1) { condvar.wait mutex }
- end
- mutex.unlock
- threads.each(&:kill)
- threads.each(&:join)
- end
-
- def test_condvar_timed_wait
- mutex = Mutex.new
- condvar = ConditionVariable.new
- timeout = 0.3
- locked = false
-
- t0 = Time.now
- mutex.synchronize do
- begin
- condvar.wait(mutex, timeout)
- ensure
- locked = mutex.locked?
- end
- end
- t1 = Time.now
- t = t1-t0
-
- assert_operator(timeout*0.9, :<, t)
- assert(locked)
- end
-
- def test_condvar_nolock
- mutex = Mutex.new
- condvar = ConditionVariable.new
-
- assert_raise(ThreadError) {condvar.wait(mutex)}
- end
-
- def test_condvar_nolock_2
- mutex = Mutex.new
- condvar = ConditionVariable.new
-
- Thread.new do
- assert_raise(ThreadError) {condvar.wait(mutex)}
- end.join
- end
-
- def test_condvar_nolock_3
- mutex = Mutex.new
- condvar = ConditionVariable.new
-
- Thread.new do
- assert_raise(ThreadError) {condvar.wait(mutex, 0.1)}
- end.join
- end
-
- def test_condvar_empty_signal
- mutex = Mutex.new
- condvar = ConditionVariable.new
-
- assert_nothing_raised(Exception) { mutex.synchronize {condvar.signal} }
- end
-
- def test_condvar_empty_broadcast
- mutex = Mutex.new
- condvar = ConditionVariable.new
-
- assert_nothing_raised(Exception) { mutex.synchronize {condvar.broadcast} }
- end
-
- def test_dup
- bug9440 = '[ruby-core:59961] [Bug #9440]'
- condvar = ConditionVariable.new
- assert_raise(NoMethodError, bug9440) do
- condvar.dup
- end
- end
-
- (DumpableCV = ConditionVariable.dup).class_eval {remove_method :marshal_dump}
-
- def test_dump
- bug9674 = '[ruby-core:61677] [Bug #9674]'
- condvar = ConditionVariable.new
- assert_raise_with_message(TypeError, /#{ConditionVariable}/, bug9674) do
- Marshal.dump(condvar)
- end
-
- condvar = DumpableCV.new
- assert_raise_with_message(TypeError, /internal Array/, bug9674) do
- Marshal.dump(condvar)
- end
- end
-end
diff --git a/test/thread/test_queue.rb b/test/thread/test_queue.rb
deleted file mode 100644
index f441f19d17..0000000000
--- a/test/thread/test_queue.rb
+++ /dev/null
@@ -1,547 +0,0 @@
-# frozen_string_literal: false
-require 'test/unit'
-require 'thread'
-require 'tmpdir'
-require 'timeout'
-
-class TestQueue < Test::Unit::TestCase
- Queue = Thread::Queue
- SizedQueue = Thread::SizedQueue
-
- def test_queue_initialized
- assert_raise(TypeError) {
- Queue.allocate.push(nil)
- }
- end
-
- def test_sized_queue_initialized
- assert_raise(TypeError) {
- SizedQueue.allocate.push(nil)
- }
- end
-
- def test_queue
- grind(5, 1000, 15, Queue)
- end
-
- def test_sized_queue
- grind(5, 1000, 15, SizedQueue, 1000)
- end
-
- def grind(num_threads, num_objects, num_iterations, klass, *args)
- from_workers = klass.new(*args)
- to_workers = klass.new(*args)
-
- workers = (1..num_threads).map {
- Thread.new {
- while object = to_workers.pop
- from_workers.push object
- end
- }
- }
-
- Thread.new {
- num_iterations.times {
- num_objects.times { to_workers.push 99 }
- num_objects.times { from_workers.pop }
- }
- }.join
-
- # close the queue the old way to test for backwards-compatibility
- num_threads.times { to_workers.push nil }
- workers.each { |t| t.join }
-
- assert_equal 0, from_workers.size
- assert_equal 0, to_workers.size
- end
-
- def test_sized_queue_initialize
- q = SizedQueue.new(1)
- assert_equal 1, q.max
- assert_raise(ArgumentError) { SizedQueue.new(0) }
- assert_raise(ArgumentError) { SizedQueue.new(-1) }
- end
-
- def test_sized_queue_assign_max
- q = SizedQueue.new(2)
- assert_equal(2, q.max)
- q.max = 1
- assert_equal(1, q.max)
- assert_raise(ArgumentError) { q.max = 0 }
- assert_equal(1, q.max)
- assert_raise(ArgumentError) { q.max = -1 }
- assert_equal(1, q.max)
-
- before = q.max
- q.max.times { q << 1 }
- t1 = Thread.new { q << 1 }
- sleep 0.01 until t1.stop?
- q.max = q.max + 1
- assert_equal before + 1, q.max
- ensure
- t1.join if t1
- end
-
- def test_queue_pop_interrupt
- q = Queue.new
- t1 = Thread.new { q.pop }
- sleep 0.01 until t1.stop?
- t1.kill.join
- assert_equal(0, q.num_waiting)
- end
-
- def test_queue_pop_non_block
- q = Queue.new
- assert_raise_with_message(ThreadError, /empty/) do
- q.pop(true)
- end
- end
-
- def test_sized_queue_pop_interrupt
- q = SizedQueue.new(1)
- t1 = Thread.new { q.pop }
- sleep 0.01 until t1.stop?
- t1.kill.join
- assert_equal(0, q.num_waiting)
- end
-
- def test_sized_queue_pop_non_block
- q = SizedQueue.new(1)
- assert_raise_with_message(ThreadError, /empty/) do
- q.pop(true)
- end
- end
-
- def test_sized_queue_push_interrupt
- q = SizedQueue.new(1)
- q.push(1)
- assert_raise_with_message(ThreadError, /full/) do
- q.push(2, true)
- end
- end
-
- def test_sized_queue_push_non_block
- q = SizedQueue.new(1)
- q.push(1)
- t1 = Thread.new { q.push(2) }
- sleep 0.01 until t1.stop?
- t1.kill.join
- assert_equal(0, q.num_waiting)
- end
-
- def test_thr_kill
- bug5343 = '[ruby-core:39634]'
- Dir.mktmpdir {|d|
- timeout = 60
- total_count = 250
- begin
- assert_normal_exit(<<-"_eom", bug5343, {:timeout => timeout, :chdir=>d})
- require "thread"
- #{total_count}.times do |i|
- open("test_thr_kill_count", "w") {|f| f.puts i }
- queue = Queue.new
- r, w = IO.pipe
- th = Thread.start {
- queue.push(nil)
- r.read 1
- }
- queue.pop
- th.kill
- th.join
- end
- _eom
- rescue Timeout::Error
- count = File.read("#{d}/test_thr_kill_count").to_i
- flunk "only #{count}/#{total_count} done in #{timeout} seconds."
- end
- }
- end
-
- def test_queue_push_return_value
- q = Queue.new
- retval = q.push(1)
- assert_same q, retval
- end
-
- def test_queue_clear_return_value
- q = Queue.new
- retval = q.clear
- assert_same q, retval
- end
-
- def test_sized_queue_clear
- # Fill queue, then test that SizedQueue#clear wakes up all waiting threads
- sq = SizedQueue.new(2)
- 2.times { sq << 1 }
-
- t1 = Thread.new do
- sq << 1
- end
-
- t2 = Thread.new do
- sq << 1
- end
-
- t3 = Thread.new do
- Thread.pass
- sq.clear
- end
-
- [t3, t2, t1].each(&:join)
- assert_equal sq.length, 2
- end
-
- def test_sized_queue_push_return_value
- q = SizedQueue.new(1)
- retval = q.push(1)
- assert_same q, retval
- end
-
- def test_sized_queue_clear_return_value
- q = SizedQueue.new(1)
- retval = q.clear
- assert_same q, retval
- end
-
- def test_sized_queue_throttle
- q = SizedQueue.new(1)
- i = 0
- consumer = Thread.new do
- while q.pop
- i += 1
- Thread.pass
- end
- end
- nprod = 4
- npush = 100
-
- producer = nprod.times.map do
- Thread.new do
- npush.times { q.push(true) }
- end
- end
- producer.each(&:join)
- q.push(nil)
- consumer.join
- assert_equal(nprod * npush, i)
- end
-
- def test_queue_thread_raise
- q = Queue.new
- th1 = Thread.new do
- begin
- q.pop
- rescue RuntimeError
- sleep
- end
- end
- th2 = Thread.new do
- sleep 0.1
- q.pop
- end
- sleep 0.1
- th1.raise
- sleep 0.1
- q << :s
- assert_nothing_raised(Timeout::Error) do
- Timeout.timeout(1) { th2.join }
- end
- ensure
- [th1, th2].each do |th|
- if th and th.alive?
- th.wakeup
- th.join
- end
- end
- end
-
- def test_dup
- bug9440 = '[ruby-core:59961] [Bug #9440]'
- q = Queue.new
- assert_raise(NoMethodError, bug9440) do
- q.dup
- end
- end
-
- (DumpableQueue = Queue.dup).class_eval {remove_method :marshal_dump}
-
- def test_dump
- bug9674 = '[ruby-core:61677] [Bug #9674]'
- q = Queue.new
- assert_raise_with_message(TypeError, /#{Queue}/, bug9674) do
- Marshal.dump(q)
- end
-
- sq = SizedQueue.new(1)
- assert_raise_with_message(TypeError, /#{SizedQueue}/, bug9674) do
- Marshal.dump(sq)
- end
-
- q = DumpableQueue.new
- assert_raise_with_message(TypeError, /internal Array/, bug9674) do
- Marshal.dump(q)
- end
- end
-
- def test_close
- [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
- q = qcreate.call
- assert_equal false, q.closed?
- q << :something
- assert_equal q, q.close
- assert q.closed?
- assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
- assert_equal q.pop, :something
- assert_nil q.pop
- assert_nil q.pop
- # non-blocking
- assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
- end
- end
-
- # test that waiting producers are woken up on close
- def close_wakeup( num_items, num_threads, &qcreate )
- raise "This test won't work with num_items(#{num_items}) >= num_threads(#{num_threads})" if num_items >= num_threads
-
- # create the Queue
- q = yield
- threads = num_threads.times.map{Thread.new{q.pop}}
- num_items.times{|i| q << i}
-
- # wait until queue empty
- (Thread.pass; sleep 0.01) until q.size == 0
-
- # close the queue so remaining threads will wake up
- q.close
-
- # wait for them to go away
- Thread.pass until threads.all?{|thr| thr.status == false}
-
- # check that they've gone away. Convert nil to -1 so we can sort and do the comparison
- expected_values = [-1] * (num_threads - num_items) + num_items.times.to_a
- assert_equal expected_values, threads.map{|thr| thr.value || -1 }.sort
- end
-
- def test_queue_close_wakeup
- close_wakeup(15, 18){Queue.new}
- end
-
- def test_size_queue_close_wakeup
- close_wakeup(5, 8){SizedQueue.new 9}
- end
-
- def test_sized_queue_one_closed_interrupt
- q = SizedQueue.new 1
- q << :one
- t1 = Thread.new { q << :two }
- sleep 0.01 until t1.stop?
- q.close
-
- t1.kill.join
- assert_equal 1, q.size
- assert_equal :one, q.pop
- assert q.empty?, "queue not empty"
- end
-
- # make sure that shutdown state is handled properly by empty? for the non-blocking case
- def test_empty_non_blocking
- return
- q = SizedQueue.new 3
- 3.times{|i| q << i}
-
- # these all block cos the queue is full
- prod_threads = 4.times.map{|i| Thread.new{q << 3+i}}
- sleep 0.01 until prod_threads.all?{|thr| thr.status == 'sleep'}
- q.close
-
- items = []
- # sometimes empty? is false but pop will raise ThreadError('empty'),
- # meaning a value is not immediately available but will be soon.
- until q.empty?
- items << q.pop(non_block=true) rescue nil
- end
- items.compact!
-
- assert_equal 7.times.to_a, items.sort
- assert q.empty?
- end
-
- def test_sized_queue_closed_push_non_blocking
- q = SizedQueue.new 7
- q.close
- assert_raise_with_message(ClosedQueueError, /queue closed/){q.push(non_block=true)}
- end
-
- def test_blocked_pushers
- q = SizedQueue.new 3
- prod_threads = 6.times.map do |i|
- thr = Thread.new{q << i}; thr[:pc] = i; thr
- end
-
- # wait until some producer threads have finished, and the other 3 are blocked
- sleep 0.01 while prod_threads.reject{|t| t.status}.count < 3
- # this would ensure that all producer threads call push before close
- # sleep 0.01 while prod_threads.select{|t| t.status == 'sleep'}.count < 3
- q.close
-
- # more than prod_threads
- cons_threads = 10.times.map do |i|
- thr = Thread.new{q.pop}; thr[:pc] = i; thr
- end
-
- # values that came from the queue
- popped_values = cons_threads.map &:value
-
- # wait untl all threads have finished
- sleep 0.01 until prod_threads.find_all{|t| t.status}.count == 0
-
- # pick only the producer threads that got in before close
- successful_prod_threads = prod_threads.reject{|thr| thr.status == nil}
- assert_nothing_raised{ successful_prod_threads.map(&:value) }
-
- # the producer threads that tried to push after q.close should all fail
- unsuccessful_prod_threads = prod_threads - successful_prod_threads
- unsuccessful_prod_threads.each do |thr|
- assert_raise(ClosedQueueError){ thr.value }
- end
-
- assert_equal cons_threads.size, popped_values.size
- assert_equal 0, q.size
-
- # check that consumer threads with values match producers that called push before close
- assert_equal successful_prod_threads.map{|thr| thr[:pc]}, popped_values.compact.sort
- assert_nil q.pop
- end
-
- def test_deny_pushers
- [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
- prod_threads = nil
- q = qcreate[]
- synq = Queue.new
- producers_start = Thread.new do
- prod_threads = 20.times.map do |i|
- Thread.new{ synq.pop; q << i }
- end
- end
- q.close
- synq.close # start producer threads
-
- # wait for all threads to be finished, because of exceptions
- # NOTE: thr.status will be nil (raised) or false (terminated)
- sleep 0.01 until prod_threads&.all?{|thr| !thr.status}
-
- # check that all threads failed to call push
- prod_threads.each do |thr|
- assert_kind_of ClosedQueueError, (thr.value rescue $!)
- end
- end
- end
-
- # size should account for waiting pushers during shutdown
- def sized_queue_size_close
- q = SizedQueue.new 4
- 4.times{|i| q << i}
- Thread.new{ q << 5 }
- Thread.new{ q << 6 }
- assert_equal 4, q.size
- assert_equal 4, q.items
- q.close
- assert_equal 6, q.size
- assert_equal 4, q.items
- end
-
- def test_blocked_pushers_empty
- q = SizedQueue.new 3
- prod_threads = 6.times.map do |i|
- Thread.new{ q << i}
- end
-
- # this ensures that all producer threads call push before close
- sleep 0.01 while prod_threads.select{|t| t.status == 'sleep'}.count < 3
- q.close
-
- ary = []
- until q.empty?
- ary << q.pop
- end
- assert_equal 0, q.size
-
- assert_equal 3, ary.size
- ary.each{|e| assert [0,1,2,3,4,5].include?(e)}
- assert_nil q.pop
-
- prod_threads.each{|t|
- begin
- t.join
- rescue => e
- end
- }
- end
-
- # test thread wakeup on one-element SizedQueue with close
- def test_one_element_sized_queue
- q = SizedQueue.new 1
- t = Thread.new{ q.pop }
- q.close
- assert_nil t.value
- end
-
- def test_close_twice
- [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
- q = qcreate[]
- q.close
- assert_nothing_raised(ClosedQueueError){q.close}
- end
- end
-
- def test_queue_close_multi_multi
- q = SizedQueue.new rand(800..1200)
-
- count_items = rand(3000..5000)
- count_producers = rand(10..20)
-
- producers = count_producers.times.map do
- Thread.new do
- sleep(rand / 100)
- count_items.times{|i| q << [i,"#{i} for #{Thread.current.inspect}"]}
- end
- end
-
- consumers = rand(7..12).times.map do
- Thread.new do
- count = 0
- while e = q.pop
- i, st = e
- count += 1 if i.is_a?(Integer) && st.is_a?(String)
- end
- count
- end
- end
-
- # No dead or finished threads
- assert (consumers + producers).all?{|thr| thr.status =~ /\Arun|sleep\Z/}, 'no threads runnning'
-
- # just exercising the concurrency of the support methods.
- counter = Thread.new do
- until q.closed? && q.empty?
- raise if q.size > q.max
- # otherwise this exercise causes too much contention on the lock
- sleep 0.01
- end
- end
-
- producers.each &:join
- q.close
-
- # results not randomly distributed. Not sure why.
- # consumers.map{|thr| thr.value}.each do |x|
- # assert_not_equal 0, x
- # end
-
- all_items_count = consumers.map{|thr| thr.value}.inject(:+)
- assert_equal count_items * count_producers, all_items_count
-
- # don't leak this thread
- assert_nothing_raised{counter.join}
- end
-end
diff --git a/test/thread/test_sync.rb b/test/thread/test_sync.rb
deleted file mode 100644
index e576fc1d32..0000000000
--- a/test/thread/test_sync.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-# frozen_string_literal: false
-require 'test/unit'
-require 'sync'
-require 'timeout'
-
-class SyncTest < Test::Unit::TestCase
- class Tester
- include Sync_m
- end
-
- def test_sync_lock_and_wakeup
- tester = Tester.new
-
- tester.sync_lock(:EX)
-
- t = Thread.new { tester.sync_lock(:EX) }
-
- sleep 0.1 until t.stop?
- t.wakeup
- sleep 0.1 until t.stop?
-
- assert_equal(tester.sync_waiting.uniq, tester.sync_waiting)
- ensure
- t.kill
- t.join
- end
-
- def test_sync_upgrade_and_wakeup
- tester = Tester.new
- tester.sync_lock(:SH)
-
- t = Thread.new do
- tester.sync_lock(:SH)
- tester.sync_lock(:EX)
- end
-
- sleep 0.1 until t.stop?
- t.wakeup
- sleep 0.1 until t.stop?
-
- tester.sync_upgrade_waiting.each { |ary|
- assert(!tester.sync_waiting.include?(ary[0]))
- }
- assert_equal(tester.sync_waiting.uniq, tester.sync_waiting)
- assert_equal(tester.sync_waiting, [])
- ensure
- t.kill
- t.join
- end
-
- def test_sync_lock_and_raise
- tester= Tester.new
- tester.sync_lock(:EX)
-
- t = Thread.new { tester.sync_lock(:EX) }
-
- sleep 0.1 until t.stop?
- t.raise
- sleep 0.1 while t.alive?
-
- assert_equal(tester.sync_waiting.uniq, tester.sync_waiting)
- assert_equal(tester.sync_waiting, [])
- end
-end
diff --git a/test/uri/test_file.rb b/test/uri/test_file.rb
new file mode 100644
index 0000000000..7e542b652d
--- /dev/null
+++ b/test/uri/test_file.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'uri/file'
+
+class URI::TestFile < Test::Unit::TestCase
+ def test_parse
+ u = URI("file://example.com/file")
+ assert_equal "/file", u.path
+
+ u = URI("file://localhost/file")
+ assert_equal "/file", u.path
+ assert_equal "file:///file", u.to_s
+
+ u = URI("file://localhost:30/file")
+ assert_equal "", u.host
+ assert_equal nil, u.port
+ assert_equal "/file", u.path
+ assert_equal "file:///file", u.to_s
+
+ u = URI("file:///file")
+ assert_equal "/file", u.path
+ assert_equal "file:///file", u.to_s
+
+ u = URI("file:/file")
+ assert_equal "/file", u.path
+ assert_equal "file:///file", u.to_s
+
+ u = URI("file://foo:pass@example.com/file")
+ assert_equal "/file", u.path
+ assert_equal nil, u.user
+ assert_equal nil, u.password
+
+ u = URI("file:///c:/path/to/file")
+ assert_equal "/c:/path/to/file", u.path
+
+ # this form is not supported
+ u = URI("file:c:/path/to/file")
+ assert_equal "c:/path/to/file", u.opaque
+
+ end
+
+ def test_build
+ u = URI::File.build(scheme: "file", host: "example.com", path:"/file")
+ assert_equal "/file", u.path
+ assert_equal "file://example.com/file", u.to_s
+ assert_raise(URI::InvalidURIError){ u.user = "foo" }
+ assert_raise(URI::InvalidURIError){ u.password = "foo" }
+ assert_raise(URI::InvalidURIError){ u.userinfo = "foo" }
+ assert_raise(URI::InvalidURIError){ URI::File.build(scheme: "file", userinfo: "foo", host: "example.com", path:"/file") }
+
+ u = URI::File.build(scheme: "file", path:"/file")
+ assert_equal "", u.host
+ assert_equal "/file", u.path
+ assert_equal "file:///file", u.to_s
+
+ u = URI::File.build(scheme: "file", host: "localhost", path:"/file")
+ assert_equal "", u.host
+ assert_equal "/file", u.path
+ assert_equal "file:///file", u.to_s
+
+ u = URI::File.build(scheme: "file", path:"/file", port: 30)
+ assert_equal "", u.host
+ assert_equal nil, u.port
+ assert_equal "/file", u.path
+ assert_equal "file:///file", u.to_s
+ end
+end
diff --git a/test/uri/test_generic.rb b/test/uri/test_generic.rb
index b226b2278b..c850eb02d1 100644
--- a/test/uri/test_generic.rb
+++ b/test/uri/test_generic.rb
@@ -20,6 +20,10 @@ class URI::TestGeneric < Test::Unit::TestCase
str = URI(exp).to_s
assert_equal exp, str
assert_not_predicate str, :frozen?, '[ruby-core:71785] [Bug #11759]'
+
+ assert_equal "file:///foo", URI("file:///foo").to_s
+ assert_equal "postgres:///foo", URI("postgres:///foo").to_s
+ assert_equal "http:/foo", URI("http:///foo").to_s
end
def test_parse
@@ -204,6 +208,9 @@ class URI::TestGeneric < Test::Unit::TestCase
assert(nil != u.merge!("../baz"))
assert_equal('http://foo/baz', u.to_s)
+ url = URI.parse('http://a/b//c') + 'd//e'
+ assert_equal('http://a/b//d//e', url.to_s)
+
u0 = URI.parse('mailto:foo@example.com')
u1 = URI.parse('mailto:foo@example.com#bar')
assert_equal(uri_to_ary(u0 + '#bar'), uri_to_ary(u1), "[ruby-dev:23628]")
@@ -261,6 +268,9 @@ class URI::TestGeneric < Test::Unit::TestCase
url = URI.parse('http://hoge/b').route_to('http://hoge/b:c')
assert_equal('./b:c', url.to_s)
+ url = URI.parse('http://hoge/b//c').route_to('http://hoge/b/c')
+ assert_equal('../c', url.to_s)
+
url = URI.parse('file:///a/b/').route_to('file:///a/b/')
assert_equal('', url.to_s)
url = URI.parse('file:///a/b/').route_to('file:///a/b')
@@ -764,6 +774,24 @@ class URI::TestGeneric < Test::Unit::TestCase
assert_equal 'http://example', uri.to_s
end
+ def test_hierarchical
+ hierarchical = URI.parse('http://a.b.c/example')
+ opaque = URI.parse('mailto:mduerst@ifi.unizh.ch')
+
+ assert hierarchical.hierarchical?
+ refute opaque.hierarchical?
+ end
+
+ def test_absolute
+ abs_uri = URI.parse('http://a.b.c/')
+ not_abs = URI.parse('a.b.c')
+
+ refute not_abs.absolute?
+
+ assert abs_uri.absolute
+ assert abs_uri.absolute?
+ end
+
def test_ipv6
assert_equal("[::1]", URI("http://[::1]/bar/baz").host)
assert_equal("::1", URI("http://[::1]/bar/baz").hostname)
@@ -839,13 +867,38 @@ class URI::TestGeneric < Test::Unit::TestCase
with_proxy_env('http_proxy'=>'http://127.0.0.1:8080', 'no_proxy'=>'192.0.2.2') {|env|
assert_equal(URI('http://127.0.0.1:8080'), URI("http://192.0.2.1/").find_proxy(env))
assert_nil(URI("http://192.0.2.2/").find_proxy(env))
+
+ getaddress = IPSocket.method(:getaddress)
+ begin
+ class << IPSocket
+ undef getaddress
+ def getaddress(host)
+ host == "example.org" or raise
+ "192.0.2.1"
+ end
+ end
+ assert_equal(URI('http://127.0.0.1:8080'), URI.parse("http://example.org").find_proxy(env))
+ class << IPSocket
+ undef getaddress
+ def getaddress(host)
+ host == "example.org" or raise
+ "192.0.2.2"
+ end
+ end
+ assert_nil(URI.parse("http://example.org").find_proxy(env))
+ ensure
+ IPSocket.singleton_class.class_eval do
+ undef getaddress
+ define_method(:getaddress, getaddress)
+ end
+ end
}
with_proxy_env('http_proxy'=>'http://127.0.0.1:8080', 'no_proxy'=>'example.org') {|env|
assert_nil(URI("http://example.org/").find_proxy(env))
assert_nil(URI("http://www.example.org/").find_proxy(env))
}
with_proxy_env('http_proxy'=>'http://127.0.0.1:8080', 'no_proxy'=>'.example.org') {|env|
- assert_nil(URI("http://example.org/").find_proxy(env))
+ assert_equal(URI('http://127.0.0.1:8080'), URI("http://example.org/").find_proxy(env))
assert_nil(URI("http://www.example.org/").find_proxy(env))
}
end
@@ -881,6 +934,29 @@ class URI::TestGeneric < Test::Unit::TestCase
}
end
+ def test_use_proxy_p
+ [
+ ['example.com', nil, 80, '', true],
+ ['example.com', nil, 80, 'example.com:80', false],
+ ['example.com', nil, 80, 'example.org,example.com:80,example.net', false],
+ ['foo.example.com', nil, 80, 'example.com', false],
+ ['foo.example.com', nil, 80, '.example.com', false],
+ ['example.com', nil, 80, '.example.com', true],
+ ['xample.com', nil, 80, '.example.com', true],
+ ['fooexample.com', nil, 80, '.example.com', true],
+ ['foo.example.com', nil, 80, 'example.com:80', false],
+ ['foo.eXample.com', nil, 80, 'example.com:80', false],
+ ['foo.example.com', nil, 80, 'eXample.com:80', false],
+ ['foo.example.com', nil, 80, 'example.com:443', true],
+ ['127.0.0.1', '127.0.0.1', 80, '10.224.0.0/22', true],
+ ['10.224.1.1', '10.224.1.1', 80, '10.224.1.1', false],
+ ['10.224.1.1', '10.224.1.1', 80, '10.224.0.0/22', false],
+ ].each do |hostname, addr, port, no_proxy, expected|
+ assert_equal expected, URI::Generic.use_proxy?(hostname, addr, port, no_proxy),
+ "use_proxy?('#{hostname}', '#{addr}', #{port}, '#{no_proxy}')"
+ end
+ end
+
class CaseInsensitiveEnv
def initialize(h={})
@h = {}
diff --git a/test/uri/test_ldap.rb b/test/uri/test_ldap.rb
index adad4454b5..64845e487a 100644
--- a/test/uri/test_ldap.rb
+++ b/test/uri/test_ldap.rb
@@ -95,6 +95,10 @@ class TestLDAP < Test::Unit::TestCase
u.select(:scheme, :host, :not_exist, :port)
end
end
+
+ def test_parse_invalid_uri
+ assert_raise(URI::InvalidURIError) {URI.parse("ldap:https://example.com")}
+ end
end
diff --git a/test/uri/test_mailto.rb b/test/uri/test_mailto.rb
index 39a0f9cdeb..e7d3142198 100644
--- a/test/uri/test_mailto.rb
+++ b/test/uri/test_mailto.rb
@@ -2,10 +2,7 @@
require 'test/unit'
require 'uri/mailto'
-module URI
-
-
-class TestMailTo < Test::Unit::TestCase
+class URI::TestMailTo < Test::Unit::TestCase
def setup
@u = URI::MailTo
end
@@ -135,7 +132,7 @@ class TestMailTo < Test::Unit::TestCase
end
def test_initializer
- assert_raise(InvalidComponentError) do
+ assert_raise(URI::InvalidComponentError) do
URI::MailTo.new('mailto', 'sdmitry:bla', 'localhost', '2000', nil,
'joe@example.com', nil, nil, 'subject=Ruby')
end
@@ -144,11 +141,11 @@ class TestMailTo < Test::Unit::TestCase
def test_check_to
u = URI::MailTo.build(['joe@example.com', 'subject=Ruby'])
- assert_raise(InvalidComponentError) do
+ assert_raise(URI::InvalidComponentError) do
u.to = '#1@mail.com'
end
- assert_raise(InvalidComponentError) do
+ assert_raise(URI::InvalidComponentError) do
u.to = '@invalid.email'
end
end
@@ -178,6 +175,10 @@ class TestMailTo < Test::Unit::TestCase
u = URI::MailTo.build(params)
assert_equal(expected, u.to_mailtext)
end
+
+ u = URI.parse('mailto:ruby-list@ruby-lang.org?Subject=subscribe&cc=myaddr')
+ assert_equal "To: ruby-list@ruby-lang.org\nSubject: subscribe\nCc: myaddr\n\n\n",
+ u.to_mailtext
end
def test_select
@@ -188,6 +189,3 @@ class TestMailTo < Test::Unit::TestCase
end
end
end
-
-
-end
diff --git a/test/uri/test_parser.rb b/test/uri/test_parser.rb
index 757ac86c74..088628a3fb 100644
--- a/test/uri/test_parser.rb
+++ b/test/uri/test_parser.rb
@@ -45,4 +45,12 @@ class URI::TestParser < Test::Unit::TestCase
URI.parse(1)
end
end
+
+ def test_unescape
+ p1 = URI::Parser.new
+ assert_equal("\xe3\x83\x90", p1.unescape("\xe3\x83\x90"))
+ assert_equal("\xe3\x83\x90", p1.unescape('%e3%83%90'))
+ assert_equal("\u3042", p1.unescape('%e3%81%82'.force_encoding(Encoding::US_ASCII)))
+ assert_equal("\xe3\x83\x90\xe3\x83\x90", p1.unescape("\xe3\x83\x90%e3%83%90"))
+ end
end
diff --git a/test/webrick/test_config.rb b/test/webrick/test_config.rb
new file mode 100644
index 0000000000..a54a667452
--- /dev/null
+++ b/test/webrick/test_config.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: false
+require "test/unit"
+require "webrick/config"
+
+class TestWEBrickConfig < Test::Unit::TestCase
+ def test_server_name_default
+ config = WEBrick::Config::General.dup
+ assert_equal(false, config.key?(:ServerName))
+ assert_equal(WEBrick::Utils.getservername, config[:ServerName])
+ assert_equal(true, config.key?(:ServerName))
+ end
+
+ def test_server_name_set_nil
+ config = WEBrick::Config::General.dup.update(ServerName: nil)
+ assert_equal(nil, config[:ServerName])
+ end
+end
diff --git a/test/webrick/test_filehandler.rb b/test/webrick/test_filehandler.rb
index 0e05c6824c..7095038189 100644
--- a/test/webrick/test_filehandler.rb
+++ b/test/webrick/test_filehandler.rb
@@ -20,16 +20,10 @@ class WEBrick::TestFileHandler < Test::Unit::TestCase
end
def get_res_body(res)
- body = res.body
- if defined? body.read
- begin
- body.read
- ensure
- body.close
- end
- else
- body
- end
+ sio = StringIO.new
+ sio.binmode
+ res.send_body(sio)
+ sio.string
end
def make_range_request(range_spec)
@@ -81,13 +75,30 @@ class WEBrick::TestFileHandler < Test::Unit::TestCase
res = make_range_response(filename, "bytes=0-0, -2")
assert_match(%r{^multipart/byteranges}, res["content-type"])
+ body = get_res_body(res)
+ boundary = /; boundary=(.+)/.match(res['content-type'])[1]
+ off = filesize - 2
+ last = filesize - 1
+
+ exp = "--#{boundary}\r\n" \
+ "Content-Type: text/plain\r\n" \
+ "Content-Range: bytes 0-0/#{filesize}\r\n" \
+ "\r\n" \
+ "#{IO.read(__FILE__, 1)}\r\n" \
+ "--#{boundary}\r\n" \
+ "Content-Type: text/plain\r\n" \
+ "Content-Range: bytes #{off}-#{last}/#{filesize}\r\n" \
+ "\r\n" \
+ "#{IO.read(__FILE__, 2, off)}\r\n" \
+ "--#{boundary}--\r\n"
+ assert_equal exp, body
end
def test_filehandler
config = { :DocumentRoot => File.dirname(__FILE__), }
this_file = File.basename(__FILE__)
filesize = File.size(__FILE__)
- this_data = File.open(__FILE__, "rb") {|f| f.read}
+ this_data = File.binread(__FILE__)
range = nil
bug2593 = '[ruby-dev:40030]'
@@ -103,7 +114,7 @@ class WEBrick::TestFileHandler < Test::Unit::TestCase
http.request(req){|res|
assert_equal("200", res.code, log.call)
assert_equal("text/plain", res.content_type, log.call)
- assert_equal(File.read(__FILE__), res.body, log.call)
+ assert_equal(this_data, res.body, log.call)
}
req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=#{filesize-100}-")
@@ -321,4 +332,20 @@ class WEBrick::TestFileHandler < Test::Unit::TestCase
http.request(req, &response_assertion)
end
end
+
+ def test_erbhandler
+ config = { :DocumentRoot => File.dirname(__FILE__) }
+ log_tester = lambda {|log, access_log|
+ log = log.reject {|s| /ERROR `.*\' not found\./ =~ s }
+ assert_equal([], log)
+ }
+ TestWEBrick.start_httpserver(config, log_tester) do |server, addr, port, log|
+ http = Net::HTTP.new(addr, port)
+ req = Net::HTTP::Get.new("/webrick.rhtml")
+ http.request(req) do |res|
+ assert_equal("200", res.code, log.call)
+ assert_match %r!\Areq to http://[^/]+/webrick\.rhtml {}\n!, res.body
+ end
+ end
+ end
end
diff --git a/test/webrick/test_htgroup.rb b/test/webrick/test_htgroup.rb
new file mode 100644
index 0000000000..8749711df5
--- /dev/null
+++ b/test/webrick/test_htgroup.rb
@@ -0,0 +1,19 @@
+require "tempfile"
+require "test/unit"
+require "webrick/httpauth/htgroup"
+
+class TestHtgroup < Test::Unit::TestCase
+ def test_htgroup
+ Tempfile.create('test_htgroup') do |tmpfile|
+ tmpfile.close
+ tmp_group = WEBrick::HTTPAuth::Htgroup.new(tmpfile.path)
+ tmp_group.add 'superheroes', %w[spiderman batman]
+ tmp_group.add 'supervillains', %w[joker]
+ tmp_group.flush
+
+ htgroup = WEBrick::HTTPAuth::Htgroup.new(tmpfile.path)
+ assert_equal(htgroup.members('superheroes'), %w[spiderman batman])
+ assert_equal(htgroup.members('supervillains'), %w[joker])
+ end
+ end
+end
diff --git a/test/webrick/test_httpauth.rb b/test/webrick/test_httpauth.rb
index 126bc6ee54..3bdba625c1 100644
--- a/test/webrick/test_httpauth.rb
+++ b/test/webrick/test_httpauth.rb
@@ -4,6 +4,7 @@ require "net/http"
require "tempfile"
require "webrick"
require "webrick/httpauth/basicauth"
+require "stringio"
require_relative "utils"
class TestWEBrickHTTPAuth < Test::Unit::TestCase
@@ -36,56 +37,7 @@ class TestWEBrickHTTPAuth < Test::Unit::TestCase
}
end
- def test_basic_auth2
- log_tester = lambda {|log, access_log|
- log.reject! {|line| /\A\s*\z/ =~ line }
- pats = [
- /ERROR Basic WEBrick's realm: webrick: password unmatch\./,
- /ERROR WEBrick::HTTPStatus::Unauthorized/
- ]
- pats.each {|pat|
- assert(!log.grep(pat).empty?, "webrick log doesn't have expected error: #{pat.inspect}")
- log.reject! {|line| pat =~ line }
- }
- assert_equal([], log)
- }
- TestWEBrick.start_httpserver({}, log_tester) {|server, addr, port, log|
- realm = "WEBrick's realm"
- path = "/basic_auth2"
-
- Tempfile.create("test_webrick_auth") {|tmpfile|
- tmpfile.close
- tmp_pass = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path)
- tmp_pass.set_passwd(realm, "webrick", "supersecretpassword")
- tmp_pass.set_passwd(realm, "foo", "supersecretpassword")
- tmp_pass.flush
-
- htpasswd = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path)
- users = []
- htpasswd.each{|user, pass| users << user }
- assert_equal(2, users.size, log.call)
- assert(users.member?("webrick"), log.call)
- assert(users.member?("foo"), log.call)
-
- server.mount_proc(path){|req, res|
- auth = WEBrick::HTTPAuth::BasicAuth.new(
- :Realm => realm, :UserDB => htpasswd,
- :Logger => server.logger
- )
- auth.authenticate(req, res)
- res.body = "hoge"
- }
- http = Net::HTTP.new(addr, port)
- g = Net::HTTP::Get.new(path)
- g.basic_auth("webrick", "supersecretpassword")
- http.request(g){|res| assert_equal("hoge", res.body, log.call)}
- g.basic_auth("webrick", "not super")
- http.request(g){|res| assert_not_equal("hoge", res.body, log.call)}
- }
- }
- end
-
- def test_basic_auth3
+ def test_basic_auth_sha
Tempfile.create("test_webrick_auth") {|tmpfile|
tmpfile.puts("webrick:{SHA}GJYFRpBbdchp595jlh3Bhfmgp8k=")
tmpfile.flush
@@ -93,7 +45,9 @@ class TestWEBrickHTTPAuth < Test::Unit::TestCase
WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path)
}
}
+ end
+ def test_basic_auth_md5
Tempfile.create("test_webrick_auth") {|tmpfile|
tmpfile.puts("webrick:$apr1$IOVMD/..$rmnOSPXr0.wwrLPZHBQZy0")
tmpfile.flush
@@ -103,6 +57,104 @@ class TestWEBrickHTTPAuth < Test::Unit::TestCase
}
end
+ [nil, :crypt, :bcrypt].each do |hash_algo|
+ begin
+ case hash_algo
+ when :crypt
+ # require 'string/crypt'
+ when :bcrypt
+ require 'bcrypt'
+ end
+ rescue LoadError
+ next
+ end
+
+ define_method(:"test_basic_auth_htpasswd_#{hash_algo}") do
+ log_tester = lambda {|log, access_log|
+ log.reject! {|line| /\A\s*\z/ =~ line }
+ pats = [
+ /ERROR Basic WEBrick's realm: webrick: password unmatch\./,
+ /ERROR WEBrick::HTTPStatus::Unauthorized/
+ ]
+ pats.each {|pat|
+ assert(!log.grep(pat).empty?, "webrick log doesn't have expected error: #{pat.inspect}")
+ log.reject! {|line| pat =~ line }
+ }
+ assert_equal([], log)
+ }
+ TestWEBrick.start_httpserver({}, log_tester) {|server, addr, port, log|
+ realm = "WEBrick's realm"
+ path = "/basic_auth2"
+
+ Tempfile.create("test_webrick_auth") {|tmpfile|
+ tmpfile.close
+ tmp_pass = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path, password_hash: hash_algo)
+ tmp_pass.set_passwd(realm, "webrick", "supersecretpassword")
+ tmp_pass.set_passwd(realm, "foo", "supersecretpassword")
+ tmp_pass.flush
+
+ htpasswd = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path, password_hash: hash_algo)
+ users = []
+ htpasswd.each{|user, pass| users << user }
+ assert_equal(2, users.size, log.call)
+ assert(users.member?("webrick"), log.call)
+ assert(users.member?("foo"), log.call)
+
+ server.mount_proc(path){|req, res|
+ auth = WEBrick::HTTPAuth::BasicAuth.new(
+ :Realm => realm, :UserDB => htpasswd,
+ :Logger => server.logger
+ )
+ auth.authenticate(req, res)
+ res.body = "hoge"
+ }
+ http = Net::HTTP.new(addr, port)
+ g = Net::HTTP::Get.new(path)
+ g.basic_auth("webrick", "supersecretpassword")
+ http.request(g){|res| assert_equal("hoge", res.body, log.call)}
+ g.basic_auth("webrick", "not super")
+ http.request(g){|res| assert_not_equal("hoge", res.body, log.call)}
+ }
+ }
+ end
+
+ define_method(:"test_basic_auth_bad_username_htpasswd_#{hash_algo}") do
+ log_tester = lambda {|log, access_log|
+ assert_equal(2, log.length)
+ assert_match(/ERROR Basic WEBrick's realm: foo\\ebar: the user is not allowed\./, log[0])
+ assert_match(/ERROR WEBrick::HTTPStatus::Unauthorized/, log[1])
+ }
+ TestWEBrick.start_httpserver({}, log_tester) {|server, addr, port, log|
+ realm = "WEBrick's realm"
+ path = "/basic_auth"
+
+ Tempfile.create("test_webrick_auth") {|tmpfile|
+ tmpfile.close
+ tmp_pass = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path, password_hash: hash_algo)
+ tmp_pass.set_passwd(realm, "webrick", "supersecretpassword")
+ tmp_pass.set_passwd(realm, "foo", "supersecretpassword")
+ tmp_pass.flush
+
+ htpasswd = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path, password_hash: hash_algo)
+ users = []
+ htpasswd.each{|user, pass| users << user }
+ server.mount_proc(path){|req, res|
+ auth = WEBrick::HTTPAuth::BasicAuth.new(
+ :Realm => realm, :UserDB => htpasswd,
+ :Logger => server.logger
+ )
+ auth.authenticate(req, res)
+ res.body = "hoge"
+ }
+ http = Net::HTTP.new(addr, port)
+ g = Net::HTTP::Get.new(path)
+ g.basic_auth("foo\ebar", "passwd")
+ http.request(g){|res| assert_not_equal("hoge", res.body, log.call) }
+ }
+ }
+ end
+ end
+
DIGESTRES_ = /
([a-zA-Z\-]+)
[ \t]*(?:\r\n[ \t]*)*
@@ -180,12 +232,119 @@ class TestWEBrickHTTPAuth < Test::Unit::TestCase
}
end
+ def test_digest_auth_int
+ log_tester = lambda {|log, access_log|
+ log.reject! {|line| /\A\s*\z/ =~ line }
+ pats = [
+ /ERROR Digest wb auth-int realm: no credentials in the request\./,
+ /ERROR WEBrick::HTTPStatus::Unauthorized/,
+ /ERROR Digest wb auth-int realm: foo: digest unmatch\./
+ ]
+ pats.each {|pat|
+ assert(!log.grep(pat).empty?, "webrick log doesn't have expected error: #{pat.inspect}")
+ log.reject! {|line| pat =~ line }
+ }
+ assert_equal([], log)
+ }
+ TestWEBrick.start_httpserver({}, log_tester) {|server, addr, port, log|
+ realm = "wb auth-int realm"
+ path = "/digest_auth_int"
+
+ Tempfile.create("test_webrick_auth_int") {|tmpfile|
+ tmpfile.close
+ tmp_pass = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path)
+ tmp_pass.set_passwd(realm, "foo", "Hunter2")
+ tmp_pass.flush
+
+ htdigest = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path)
+ users = []
+ htdigest.each{|user, pass| users << user }
+ assert_equal %w(foo), users
+
+ auth = WEBrick::HTTPAuth::DigestAuth.new(
+ :Realm => realm, :UserDB => htdigest,
+ :Algorithm => 'MD5',
+ :Logger => server.logger,
+ :Qop => %w(auth-int),
+ )
+ server.mount_proc(path){|req, res|
+ auth.authenticate(req, res)
+ res.body = "bbb"
+ }
+ Net::HTTP.start(addr, port) do |http|
+ post = Net::HTTP::Post.new(path)
+ params = {}
+ data = 'hello=world'
+ body = StringIO.new(data)
+ post.content_length = data.bytesize
+ post['Content-Type'] = 'application/x-www-form-urlencoded'
+ post.body_stream = body
+
+ http.request(post) do |res|
+ assert_equal('401', res.code, log.call)
+ res["www-authenticate"].scan(DIGESTRES_) do |key, quoted, token|
+ params[key.downcase] = token || quoted.delete('\\')
+ end
+ params['uri'] = "http://#{addr}:#{port}#{path}"
+ end
+
+ body.rewind
+ cred = credentials_for_request('foo', 'Hunter3', params, body)
+ post['Authorization'] = cred
+ post.body_stream = body
+ http.request(post){|res|
+ assert_equal('401', res.code, log.call)
+ assert_not_equal("bbb", res.body, log.call)
+ }
+
+ body.rewind
+ cred = credentials_for_request('foo', 'Hunter2', params, body)
+ post['Authorization'] = cred
+ post.body_stream = body
+ http.request(post){|res| assert_equal("bbb", res.body, log.call)}
+ end
+ }
+ }
+ end
+
+ def test_digest_auth_invalid
+ digest_auth = WEBrick::HTTPAuth::DigestAuth.new(Realm: 'realm', UserDB: '')
+
+ def digest_auth.error(fmt, *)
+ end
+
+ def digest_auth.try_bad_request(len)
+ request = {"Authorization" => %[Digest a="#{'\b'*len}]}
+ authenticate request, nil
+ end
+
+ bad_request = WEBrick::HTTPStatus::BadRequest
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ assert_raise(bad_request) {digest_auth.try_bad_request(10)}
+ limit = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0)
+ [20, 50, 100, 200].each do |len|
+ assert_raise(bad_request) do
+ Timeout.timeout(len*limit) {digest_auth.try_bad_request(len)}
+ end
+ end
+ end
+
private
- def credentials_for_request(user, password, params)
+ def credentials_for_request(user, password, params, body = nil)
cnonce = "hoge"
nonce_count = 1
ha1 = "#{user}:#{params['realm']}:#{password}"
- ha2 = "GET:#{params['uri']}"
+ if body
+ dig = Digest::MD5.new
+ while buf = body.read(16384)
+ dig.update(buf)
+ end
+ body.rewind
+ ha2 = "POST:#{params['uri']}:#{dig.hexdigest}"
+ else
+ ha2 = "GET:#{params['uri']}"
+ end
+
request_digest =
"#{Digest::MD5.hexdigest(ha1)}:" \
"#{params['nonce']}:#{'%08x' % nonce_count}:#{cnonce}:#{params['qop']}:" \
diff --git a/test/webrick/test_httpproxy.rb b/test/webrick/test_httpproxy.rb
index 335d442fbb..504eb1f915 100644
--- a/test/webrick/test_httpproxy.rb
+++ b/test/webrick/test_httpproxy.rb
@@ -6,7 +6,6 @@ require "webrick/httpproxy"
begin
require "webrick/ssl"
require "net/https"
- require File.expand_path("../openssl/utils.rb", File.dirname(__FILE__))
rescue LoadError
# test_connect will be skipped
end
@@ -65,6 +64,7 @@ class TestWEBrickHTTPProxy < Test::Unit::TestCase
req = Net::HTTP::Post.new("/")
req.body = "post-data"
+ req.content_type = "application/x-www-form-urlencoded"
http.request(req){|res|
assert_equal("1.1 localhost.localdomain:#{port}", res["via"], log.call)
assert_equal("POST / post-data", res.body, log.call)
@@ -109,6 +109,7 @@ class TestWEBrickHTTPProxy < Test::Unit::TestCase
assert_equal(2, request_handler_called, log.call)
req = Net::HTTP::Post.new("/")
+ req.content_type = "application/x-www-form-urlencoded"
req.body = "post-data"
http.request(req){|res|
assert_nil(res["via"], log.call)
@@ -119,17 +120,119 @@ class TestWEBrickHTTPProxy < Test::Unit::TestCase
}
end
+ def test_big_bodies
+ require 'digest/md5'
+ rand_str = File.read(__FILE__)
+ rand_str.freeze
+ nr = 1024 ** 2 / rand_str.size # bigger works, too
+ exp = Digest::MD5.new
+ nr.times { exp.update(rand_str) }
+ exp = exp.hexdigest
+ TestWEBrick.start_httpserver do |o_server, o_addr, o_port, o_log|
+ o_server.mount_proc('/') do |req, res|
+ case req.request_method
+ when 'GET'
+ res['content-type'] = 'application/octet-stream'
+ if req.path == '/length'
+ res['content-length'] = (nr * rand_str.size).to_s
+ else
+ res.chunked = true
+ end
+ res.body = ->(socket) { nr.times { socket.write(rand_str) } }
+ when 'POST'
+ dig = Digest::MD5.new
+ req.body { |buf| dig.update(buf); buf.clear }
+ res['content-type'] = 'text/plain'
+ res['content-length'] = '32'
+ res.body = dig.hexdigest
+ end
+ end
+
+ http = Net::HTTP.new(o_addr, o_port)
+ IO.pipe do |rd, wr|
+ headers = {
+ 'Content-Type' => 'application/octet-stream',
+ 'Transfer-Encoding' => 'chunked',
+ }
+ post = Net::HTTP::Post.new('/', headers)
+ th = Thread.new { nr.times { wr.write(rand_str) }; wr.close }
+ post.body_stream = rd
+ http.request(post) do |res|
+ assert_equal 'text/plain', res['content-type']
+ assert_equal 32, res.content_length
+ assert_equal exp, res.body
+ end
+ assert_nil th.value
+ end
+
+ TestWEBrick.start_httpproxy do |p_server, p_addr, p_port, p_log|
+ http = Net::HTTP.new(o_addr, o_port, p_addr, p_port)
+ http.request_get('/length') do |res|
+ assert_equal(nr * rand_str.size, res.content_length)
+ dig = Digest::MD5.new
+ res.read_body { |buf| dig.update(buf); buf.clear }
+ assert_equal exp, dig.hexdigest
+ end
+ http.request_get('/') do |res|
+ assert_predicate res, :chunked?
+ dig = Digest::MD5.new
+ res.read_body { |buf| dig.update(buf); buf.clear }
+ assert_equal exp, dig.hexdigest
+ end
+
+ IO.pipe do |rd, wr|
+ headers = {
+ 'Content-Type' => 'application/octet-stream',
+ 'Content-Length' => (nr * rand_str.size).to_s,
+ }
+ post = Net::HTTP::Post.new('/', headers)
+ th = Thread.new { nr.times { wr.write(rand_str) }; wr.close }
+ post.body_stream = rd
+ http.request(post) do |res|
+ assert_equal 'text/plain', res['content-type']
+ assert_equal 32, res.content_length
+ assert_equal exp, res.body
+ end
+ assert_nil th.value
+ end
+
+ IO.pipe do |rd, wr|
+ headers = {
+ 'Content-Type' => 'application/octet-stream',
+ 'Transfer-Encoding' => 'chunked',
+ }
+ post = Net::HTTP::Post.new('/', headers)
+ th = Thread.new { nr.times { wr.write(rand_str) }; wr.close }
+ post.body_stream = rd
+ http.request(post) do |res|
+ assert_equal 'text/plain', res['content-type']
+ assert_equal 32, res.content_length
+ assert_equal exp, res.body
+ end
+ assert_nil th.value
+ end
+ end
+ end
+ end if RUBY_VERSION >= '2.5'
+
def make_certificate(key, cn)
subject = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=#{cn}")
exts = [
["keyUsage", "keyEncipherment,digitalSignature", true],
]
- cert = OpenSSL::TestUtils.issue_cert(
- subject, key, 1, Time.now, Time.now + 3600, exts,
- nil, nil, OpenSSL::Digest::SHA1.new
- )
+ cert = OpenSSL::X509::Certificate.new
+ cert.version = 2
+ cert.serial = 1
+ cert.subject = subject
+ cert.issuer = subject
+ cert.public_key = key
+ cert.not_before = Time.now - 3600
+ cert.not_after = Time.now + 3600
+ ef = OpenSSL::X509::ExtensionFactory.new(cert, cert)
+ exts.each {|args| cert.add_extension(ef.create_extension(*args)) }
+ cert.sign(key, "sha256")
return cert
- end if defined?(OpenSSL::TestUtils)
+ end if defined?(OpenSSL::SSL)
def test_connect
# Testing CONNECT to proxy server
@@ -139,7 +242,7 @@ class TestWEBrickHTTPProxy < Test::Unit::TestCase
# 2. ---- establish SSL session --->
# 3. ------- GET or POST ---------->
#
- key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ key = TEST_KEY_RSA2048
cert = make_certificate(key, "127.0.0.1")
s_config = {
:SSLEnable =>true,
@@ -178,7 +281,7 @@ class TestWEBrickHTTPProxy < Test::Unit::TestCase
}
}
}
- end if defined?(OpenSSL::TestUtils)
+ end if defined?(OpenSSL::SSL)
def test_upstream_proxy
# Testing GET or POST through the upstream proxy server
@@ -235,6 +338,7 @@ class TestWEBrickHTTPProxy < Test::Unit::TestCase
req = Net::HTTP::Post.new("/")
req.body = "post-data"
+ req.content_type = "application/x-www-form-urlencoded"
http.request(req){|res|
via = res["via"].split(/,\s+/)
assert(via.include?("1.1 localhost.localdomain:#{up_port}"), up_log.call + log.call)
@@ -246,7 +350,7 @@ class TestWEBrickHTTPProxy < Test::Unit::TestCase
assert_equal(3, proxy_handler_called, up_log.call + log.call)
assert_equal(3, request_handler_called, up_log.call + log.call)
- if defined?(OpenSSL::TestUtils)
+ if defined?(OpenSSL::SSL)
# Testing CONNECT to the upstream proxy server
#
# client -------> proxy -------> proxy -------> https
@@ -254,7 +358,7 @@ class TestWEBrickHTTPProxy < Test::Unit::TestCase
# 2. -------- establish SSL session ------>
# 3. ---------- GET or POST -------------->
#
- key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ key = TEST_KEY_RSA2048
cert = make_certificate(key, "127.0.0.1")
s_config = {
:SSLEnable =>true,
@@ -279,6 +383,7 @@ class TestWEBrickHTTPProxy < Test::Unit::TestCase
req2 = Net::HTTP::Post.new("/")
req2.body = "post-data"
+ req2.content_type = "application/x-www-form-urlencoded"
http.request(req2){|res|
assert_equal("SSL POST / post-data", res.body, up_log.call + log.call + s_log.call)
}
@@ -287,4 +392,36 @@ class TestWEBrickHTTPProxy < Test::Unit::TestCase
}
}
end
+
+ if defined?(OpenSSL::SSL)
+ TEST_KEY_RSA2048 = OpenSSL::PKey.read <<-_end_of_pem_
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAuV9ht9J7k4NBs38jOXvvTKY9gW8nLICSno5EETR1cuF7i4pN
+s9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enenfzq/t/e/1IRW0wkJUJUFQign
+4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWmqbjs07JbuS4QQGGXLc+Su96D
+kYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v68JkRFIhdGlb6JL8fllf/A/bl
+NwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX9KZYcU00mOX+fdxOSnGqS/8J
+DRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wIDAQABAoIBAAzsamqfYQAqwXTb
+I0CJtGg6msUgU7HVkOM+9d3hM2L791oGHV6xBAdpXW2H8LgvZHJ8eOeSghR8+dgq
+PIqAffo4x1Oma+FOg3A0fb0evyiACyrOk+EcBdbBeLo/LcvahBtqnDfiUMQTpy6V
+seSoFCwuN91TSCeGIsDpRjbG1vxZgtx+uI+oH5+ytqJOmfCksRDCkMglGkzyfcl0
+Xc5CUhIJ0my53xijEUQl19rtWdMnNnnkdbG8PT3LZlOta5Do86BElzUYka0C6dUc
+VsBDQ0Nup0P6rEQgy7tephHoRlUGTYamsajGJaAo1F3IQVIrRSuagi7+YpSpCqsW
+wORqorkCgYEA7RdX6MDVrbw7LePnhyuaqTiMK+055/R1TqhB1JvvxJ1CXk2rDL6G
+0TLHQ7oGofd5LYiemg4ZVtWdJe43BPZlVgT6lvL/iGo8JnrncB9Da6L7nrq/+Rvj
+XGjf1qODCK+LmreZWEsaLPURIoR/Ewwxb9J2zd0CaMjeTwafJo1CZvcCgYEAyCgb
+aqoWvUecX8VvARfuA593Lsi50t4MEArnOXXcd1RnXoZWhbx5rgO8/ATKfXr0BK/n
+h2GF9PfKzHFm/4V6e82OL7gu/kLy2u9bXN74vOvWFL5NOrOKPM7Kg+9I131kNYOw
+Ivnr/VtHE5s0dY7JChYWE1F3vArrOw3T00a4CXUCgYEA0SqY+dS2LvIzW4cHCe9k
+IQqsT0yYm5TFsUEr4sA3xcPfe4cV8sZb9k/QEGYb1+SWWZ+AHPV3UW5fl8kTbSNb
+v4ng8i8rVVQ0ANbJO9e5CUrepein2MPL0AkOATR8M7t7dGGpvYV0cFk8ZrFx0oId
+U0PgYDotF/iueBWlbsOM430CgYEAqYI95dFyPI5/AiSkY5queeb8+mQH62sdcCCr
+vd/w/CZA/K5sbAo4SoTj8dLk4evU6HtIa0DOP63y071eaxvRpTNqLUOgmLh+D6gS
+Cc7TfLuFrD+WDBatBd5jZ+SoHccVrLR/4L8jeodo5FPW05A+9gnKXEXsTxY4LOUC
+9bS4e1kCgYAqVXZh63JsMwoaxCYmQ66eJojKa47VNrOeIZDZvd2BPVf30glBOT41
+gBoDG3WMPZoQj9pb7uMcrnvs4APj2FIhMU8U15LcPAj59cD6S6rWnAxO8NFK7HQG
+4Jxg3JNNf8ErQoCHb1B3oVdXJkmbJkARoDpBKmTCgKtP8ADYLmVPQw==
+-----END RSA PRIVATE KEY-----
+ _end_of_pem_
+ end
end
diff --git a/test/webrick/test_httprequest.rb b/test/webrick/test_httprequest.rb
index 855ff9d4a7..592f857f65 100644
--- a/test/webrick/test_httprequest.rb
+++ b/test/webrick/test_httprequest.rb
@@ -237,6 +237,7 @@ GET /
def test_chunked
crlf = "\x0d\x0a"
+ expect = File.binread(__FILE__).freeze
msg = <<-_end_of_message_
POST /path HTTP/1.1
Host: test.ruby-lang.org:8080
@@ -253,7 +254,14 @@ GET /
msg << "0" << crlf
req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
req.parse(StringIO.new(msg))
- assert_equal(File.read(__FILE__), req.body)
+ assert_equal(expect, req.body)
+
+ # chunked req.body_reader
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg))
+ dst = StringIO.new
+ IO.copy_stream(req.body_reader, dst)
+ assert_equal(expect, dst.string)
end
def test_forwarded
@@ -414,4 +422,11 @@ GET /
req.body
}
end
+
+ def test_eof_raised_when_line_is_nil
+ assert_raise(WEBrick::HTTPStatus::EOFError) {
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(""))
+ }
+ end
end
diff --git a/test/webrick/test_httpresponse.rb b/test/webrick/test_httpresponse.rb
index d97a65b5db..cde8efc8ed 100644
--- a/test/webrick/test_httpresponse.rb
+++ b/test/webrick/test_httpresponse.rb
@@ -2,6 +2,7 @@
require "webrick"
require "minitest/autorun"
require "stringio"
+require "net/http"
module WEBrick
class TestHTTPResponse < MiniTest::Unit::TestCase
@@ -28,6 +29,90 @@ module WEBrick
@res.keep_alive = true
end
+ def test_prevent_response_splitting_headers_crlf
+ res['X-header'] = "malicious\r\nCookie: hack"
+ io = StringIO.new
+ res.send_response io
+ io.rewind
+ res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
+ assert_equal '500', res.code
+ refute_match 'hack', io.string
+ end
+
+ def test_prevent_response_splitting_cookie_headers_crlf
+ user_input = "malicious\r\nCookie: hack"
+ res.cookies << WEBrick::Cookie.new('author', user_input)
+ io = StringIO.new
+ res.send_response io
+ io.rewind
+ res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
+ assert_equal '500', res.code
+ refute_match 'hack', io.string
+ end
+
+ def test_prevent_response_splitting_headers_cr
+ res['X-header'] = "malicious\rCookie: hack"
+ io = StringIO.new
+ res.send_response io
+ io.rewind
+ res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
+ assert_equal '500', res.code
+ refute_match 'hack', io.string
+ end
+
+ def test_prevent_response_splitting_cookie_headers_cr
+ user_input = "malicious\rCookie: hack"
+ res.cookies << WEBrick::Cookie.new('author', user_input)
+ io = StringIO.new
+ res.send_response io
+ io.rewind
+ res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
+ assert_equal '500', res.code
+ refute_match 'hack', io.string
+ end
+
+ def test_prevent_response_splitting_headers_lf
+ res['X-header'] = "malicious\nCookie: hack"
+ io = StringIO.new
+ res.send_response io
+ io.rewind
+ res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
+ assert_equal '500', res.code
+ refute_match 'hack', io.string
+ end
+
+ def test_prevent_response_splitting_cookie_headers_lf
+ user_input = "malicious\nCookie: hack"
+ res.cookies << WEBrick::Cookie.new('author', user_input)
+ io = StringIO.new
+ res.send_response io
+ io.rewind
+ res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
+ assert_equal '500', res.code
+ refute_match 'hack', io.string
+ end
+
+ def test_set_redirect_response_splitting
+ url = "malicious\r\nCookie: hack"
+ assert_raises(URI::InvalidURIError) do
+ res.set_redirect(WEBrick::HTTPStatus::MultipleChoices, url)
+ end
+ end
+
+ def test_set_redirect_html_injection
+ url = 'http://example.com////?a</a><head></head><body><img src=1></body>'
+ assert_raises(WEBrick::HTTPStatus::MultipleChoices) do
+ res.set_redirect(WEBrick::HTTPStatus::MultipleChoices, url)
+ end
+ res.status = 300
+ io = StringIO.new
+ res.send_response(io)
+ io.rewind
+ res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io))
+ assert_equal '300', res.code
+ refute_match /<img/, io.string
+ end
+
def test_304_does_not_log_warning
res.status = 304
res.setup_header
@@ -147,6 +232,29 @@ module WEBrick
assert_equal 0, logger.messages.length
end
+ def test_send_body_proc
+ @res.body = Proc.new { |out| out.write('hello') }
+ IO.pipe do |r, w|
+ @res.send_body(w)
+ w.close
+ r.binmode
+ assert_equal 'hello', r.read
+ end
+ assert_equal 0, logger.messages.length
+ end
+
+ def test_send_body_proc_chunked
+ @res.body = Proc.new { |out| out.write('hello') }
+ @res.chunked = true
+ IO.pipe do |r, w|
+ @res.send_body(w)
+ w.close
+ r.binmode
+ assert_equal "5\r\nhello\r\n0\r\n\r\n", r.read
+ end
+ assert_equal 0, logger.messages.length
+ end
+
def test_set_error
status = 400
message = 'missing attribute'
diff --git a/test/webrick/test_https.rb b/test/webrick/test_https.rb
new file mode 100644
index 0000000000..ec0aac354a
--- /dev/null
+++ b/test/webrick/test_https.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: false
+require "test/unit"
+require "net/http"
+require "webrick"
+require "webrick/https"
+require "webrick/utils"
+require_relative "utils"
+
+class TestWEBrickHTTPS < Test::Unit::TestCase
+ empty_log = Object.new
+ def empty_log.<<(str)
+ assert_equal('', str)
+ self
+ end
+ NoLog = WEBrick::Log.new(empty_log, WEBrick::BasicLog::WARN)
+
+ class HTTPSNITest < ::Net::HTTP
+ attr_accessor :sni_hostname
+
+ def ssl_socket_connect(s, timeout)
+ s.hostname = sni_hostname
+ super
+ end
+ end
+
+ def teardown
+ WEBrick::Utils::TimeoutHandler.terminate
+ super
+ end
+
+ def https_get(addr, port, hostname, path, verifyname = nil)
+ subject = nil
+ http = HTTPSNITest.new(addr, port)
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ http.verify_callback = proc { |x, store| subject = store.chain[0].subject.to_s; x }
+ http.sni_hostname = hostname
+ req = Net::HTTP::Get.new(path)
+ req["Host"] = "#{hostname}:#{port}"
+ response = http.start { http.request(req).body }
+ assert_equal("/CN=#{verifyname || hostname}", subject)
+ response
+ end
+
+ def test_sni
+ config = {
+ :ServerName => "localhost",
+ :SSLEnable => true,
+ :SSLCertName => "/CN=localhost",
+ }
+ TestWEBrick.start_httpserver(config){|server, addr, port, log|
+ server.mount_proc("/") {|req, res| res.body = "master" }
+
+ # catch stderr in create_self_signed_cert
+ stderr_buffer = StringIO.new
+ old_stderr, $stderr = $stderr, stderr_buffer
+
+ begin
+ vhost_config1 = {
+ :ServerName => "vhost1",
+ :Port => port,
+ :DoNotListen => true,
+ :Logger => NoLog,
+ :AccessLog => [],
+ :SSLEnable => true,
+ :SSLCertName => "/CN=vhost1",
+ }
+ vhost1 = WEBrick::HTTPServer.new(vhost_config1)
+ vhost1.mount_proc("/") {|req, res| res.body = "vhost1" }
+ server.virtual_host(vhost1)
+
+ vhost_config2 = {
+ :ServerName => "vhost2",
+ :ServerAlias => ["vhost2alias"],
+ :Port => port,
+ :DoNotListen => true,
+ :Logger => NoLog,
+ :AccessLog => [],
+ :SSLEnable => true,
+ :SSLCertName => "/CN=vhost2",
+ }
+ vhost2 = WEBrick::HTTPServer.new(vhost_config2)
+ vhost2.mount_proc("/") {|req, res| res.body = "vhost2" }
+ server.virtual_host(vhost2)
+ ensure
+ # restore stderr
+ $stderr = old_stderr
+ end
+
+ assert_match(/\A([.+*]+\n)+\z/, stderr_buffer.string)
+ assert_equal("master", https_get(addr, port, "localhost", "/localhost"))
+ assert_equal("master", https_get(addr, port, "unknown", "/unknown", "localhost"))
+ assert_equal("vhost1", https_get(addr, port, "vhost1", "/vhost1"))
+ assert_equal("vhost2", https_get(addr, port, "vhost2", "/vhost2"))
+ assert_equal("vhost2", https_get(addr, port, "vhost2alias", "/vhost2alias", "vhost2"))
+ }
+ end
+
+ def test_check_ssl_virtual
+ config = {
+ :ServerName => "localhost",
+ :SSLEnable => true,
+ :SSLCertName => "/CN=localhost",
+ }
+ TestWEBrick.start_httpserver(config){|server, addr, port, log|
+ assert_raise ArgumentError do
+ vhost = WEBrick::HTTPServer.new({:DoNotListen => true, :Logger => NoLog})
+ server.virtual_host(vhost)
+ end
+ }
+ end
+end
diff --git a/test/webrick/test_httpserver.rb b/test/webrick/test_httpserver.rb
index 5aa94637ba..2e5d44940c 100644
--- a/test/webrick/test_httpserver.rb
+++ b/test/webrick/test_httpserver.rb
@@ -253,6 +253,7 @@ class TestWEBrickHTTPServer < Test::Unit::TestCase
server.virtual_host(WEBrick::HTTPServer.new(vhost_config))
Thread.pass while server.status != :Running
+ sleep 1 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # server.status behaves unexpectedly with --jit-wait
assert_equal(1, started, log.call)
assert_equal(0, stopped, log.call)
assert_equal(0, accepted, log.call)
@@ -275,6 +276,38 @@ class TestWEBrickHTTPServer < Test::Unit::TestCase
assert_equal(stopped, 1)
end
+ class CustomRequest < ::WEBrick::HTTPRequest; end
+ class CustomResponse < ::WEBrick::HTTPResponse; end
+ class CustomServer < ::WEBrick::HTTPServer
+ def create_request(config)
+ CustomRequest.new(config)
+ end
+
+ def create_response(config)
+ CustomResponse.new(config)
+ end
+ end
+
+ def test_custom_server_request_and_response
+ config = { :ServerName => "localhost" }
+ TestWEBrick.start_server(CustomServer, config){|server, addr, port, log|
+ server.mount_proc("/", lambda {|req, res|
+ assert_kind_of(CustomRequest, req)
+ assert_kind_of(CustomResponse, res)
+ res.body = "via custom response"
+ })
+ Thread.pass while server.status != :Running
+
+ Net::HTTP.start(addr, port) do |http|
+ req = Net::HTTP::Get.new("/")
+ http.request(req){|res|
+ assert_equal("via custom response", res.body)
+ }
+ server.shutdown
+ end
+ }
+ end
+
# This class is needed by test_response_io_with_chunked_set method
class EventManagerForChunkedResponseTest
def initialize
@@ -415,4 +448,96 @@ class TestWEBrickHTTPServer < Test::Unit::TestCase
}
assert_equal(0, requested, "Server responded to #{requested} requests after shutdown")
end
+
+ def test_cntrl_in_path
+ log_ary = []
+ access_log_ary = []
+ config = {
+ :Port => 0,
+ :BindAddress => '127.0.0.1',
+ :Logger => WEBrick::Log.new(log_ary, WEBrick::BasicLog::WARN),
+ :AccessLog => [[access_log_ary, '']],
+ }
+ s = WEBrick::HTTPServer.new(config)
+ s.mount('/foo', WEBrick::HTTPServlet::FileHandler, __FILE__)
+ th = Thread.new { s.start }
+ addr = s.listeners[0].addr
+
+ http = Net::HTTP.new(addr[3], addr[1])
+ req = Net::HTTP::Get.new('/notexist%0a/foo')
+ http.request(req) { |res| assert_equal('404', res.code) }
+ exp = %Q(ERROR `/notexist\\n/foo' not found.\n)
+ assert_equal 1, log_ary.size
+ assert_include log_ary[0], exp
+ ensure
+ s&.shutdown
+ th&.join
+ end
+
+ def test_gigantic_request_header
+ log_tester = lambda {|log, access_log|
+ assert_equal 1, log.size
+ assert_include log[0], 'ERROR headers too large'
+ }
+ TestWEBrick.start_httpserver({}, log_tester){|server, addr, port, log|
+ server.mount('/', WEBrick::HTTPServlet::FileHandler, __FILE__)
+ TCPSocket.open(addr, port) do |c|
+ c.write("GET / HTTP/1.0\r\n")
+ junk = -"X-Junk: #{' ' * 1024}\r\n"
+ assert_raise(Errno::ECONNRESET, Errno::EPIPE) do
+ loop { c.write(junk) }
+ end
+ end
+ }
+ end
+
+ def test_eof_in_chunk
+ log_tester = lambda do |log, access_log|
+ assert_equal 1, log.size
+ assert_include log[0], 'ERROR bad chunk data size'
+ end
+ TestWEBrick.start_httpserver({}, log_tester){|server, addr, port, log|
+ server.mount_proc('/', ->(req, res) { res.body = req.body })
+ TCPSocket.open(addr, port) do |c|
+ c.write("POST / HTTP/1.1\r\nHost: example.com\r\n" \
+ "Transfer-Encoding: chunked\r\n\r\n5\r\na")
+ c.shutdown(Socket::SHUT_WR) # trigger EOF in server
+ res = c.read
+ assert_match %r{\AHTTP/1\.1 400 }, res
+ end
+ }
+ end
+
+ def test_big_chunks
+ nr_out = 3
+ buf = 'big' # 3 bytes is bigger than 2!
+ config = { :InputBufferSize => 2 }.freeze
+ total = 0
+ all = ''
+ TestWEBrick.start_httpserver(config){|server, addr, port, log|
+ server.mount_proc('/', ->(req, res) {
+ err = []
+ ret = req.body do |chunk|
+ n = chunk.bytesize
+ n > config[:InputBufferSize] and err << "#{n} > :InputBufferSize"
+ total += n
+ all << chunk
+ end
+ ret.nil? or err << 'req.body should return nil'
+ (buf * nr_out) == all or err << 'input body does not match expected'
+ res.header['connection'] = 'close'
+ res.body = err.join("\n")
+ })
+ TCPSocket.open(addr, port) do |c|
+ c.write("POST / HTTP/1.1\r\nHost: example.com\r\n" \
+ "Transfer-Encoding: chunked\r\n\r\n")
+ chunk = "#{buf.bytesize.to_s(16)}\r\n#{buf}\r\n"
+ nr_out.times { c.write(chunk) }
+ c.write("0\r\n\r\n")
+ head, body = c.read.split("\r\n\r\n")
+ assert_match %r{\AHTTP/1\.1 200 OK}, head
+ assert_nil body
+ end
+ }
+ end
end
diff --git a/test/webrick/test_server.rb b/test/webrick/test_server.rb
index 4d539d0368..8162a186db 100644
--- a/test/webrick/test_server.rb
+++ b/test/webrick/test_server.rb
@@ -65,6 +65,7 @@ class TestWEBrickServer < Test::Unit::TestCase
}
TestWEBrick.start_server(Echo, config){|server, addr, port, log|
true while server.status != :Running
+ sleep 1 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # server.status behaves unexpectedly with --jit-wait
assert_equal(1, started, log.call)
assert_equal(0, stopped, log.call)
assert_equal(0, accepted, log.call)
diff --git a/test/webrick/test_ssl_server.rb b/test/webrick/test_ssl_server.rb
index f6d5250365..64cf25f78b 100644
--- a/test/webrick/test_ssl_server.rb
+++ b/test/webrick/test_ssl_server.rb
@@ -2,6 +2,7 @@ require "test/unit"
require "webrick"
require "webrick/ssl"
require_relative "utils"
+require 'timeout'
class TestWEBrickSSLServer < Test::Unit::TestCase
class Echo < WEBrick::GenericServer
@@ -37,4 +38,30 @@ class TestWEBrickSSLServer < Test::Unit::TestCase
io.close
}
end
+
+ def test_slow_connect
+ poke = lambda do |io, msg|
+ begin
+ sock = OpenSSL::SSL::SSLSocket.new(io)
+ sock.connect
+ sock.puts(msg)
+ assert_equal "#{msg}\n", sock.gets, msg
+ ensure
+ sock&.close
+ io.close
+ end
+ end
+ config = {
+ :SSLEnable => true,
+ :SSLCertName => "/C=JP/O=www.ruby-lang.org/CN=Ruby",
+ }
+ Timeout.timeout(10) do
+ TestWEBrick.start_server(Echo, config) do |server, addr, port, log|
+ outer = TCPSocket.new(addr, port)
+ inner = TCPSocket.new(addr, port)
+ poke.call(inner, 'fast TLS negotiation')
+ poke.call(outer, 'slow TLS negotiation')
+ end
+ end
+ end
end
diff --git a/test/webrick/test_utils.rb b/test/webrick/test_utils.rb
index c34ba8bb73..c2b7a36e8a 100644
--- a/test/webrick/test_utils.rb
+++ b/test/webrick/test_utils.rb
@@ -32,18 +32,18 @@ class TestWEBrickUtils < Test::Unit::TestCase
m = WEBrick::Utils
i = 0
assert_raise(Timeout::Error){
- m.timeout(0.2){
- assert_raise(Timeout::Error){ m.timeout(0.1){ i += 1; sleep } }
+ m.timeout(1){
+ assert_raise(Timeout::Error){ m.timeout(0.1){ i += 1; sleep(1) } }
assert_not_expired(m)
i += 1
- sleep
+ sleep(2)
}
}
assert_equal(2, i)
assert_expired(m)
end
- def test_timeout_default_execption
+ def test_timeout_default_exception
m = WEBrick::Utils
assert_raise(Timeout::Error){ m.timeout(0.01){ sleep } }
assert_expired(m)
diff --git a/test/webrick/utils.rb b/test/webrick/utils.rb
index 92353908be..cc231f07e8 100644
--- a/test/webrick/utils.rb
+++ b/test/webrick/utils.rb
@@ -15,6 +15,7 @@ module TestWEBrick
class WEBrick::HTTPServlet::CGIHandler
remove_const :Ruby
+ require "envutil" unless defined?(EnvUtil)
Ruby = EnvUtil.rubybin
remove_const :CGIRunner
CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\"" # :nodoc:
@@ -26,6 +27,7 @@ module TestWEBrick
RubyBin << " \"-I#{File.dirname(EnvUtil.rubybin)}/.ext/common\""
RubyBin << " \"-I#{File.dirname(EnvUtil.rubybin)}/.ext/#{RUBY_PLATFORM}\""
+ require "test/unit" unless defined?(Test::Unit)
include Test::Unit::Assertions
extend Test::Unit::Assertions
diff --git a/test/webrick/webrick.rhtml b/test/webrick/webrick.rhtml
new file mode 100644
index 0000000000..a7bbe43fb5
--- /dev/null
+++ b/test/webrick/webrick.rhtml
@@ -0,0 +1,4 @@
+req to <%=
+servlet_request.request_uri
+%> <%=
+servlet_request.query.inspect %>
diff --git a/test/win32ole/available_ole.rb b/test/win32ole/available_ole.rb
new file mode 100644
index 0000000000..ebc9baae66
--- /dev/null
+++ b/test/win32ole/available_ole.rb
@@ -0,0 +1,41 @@
+begin
+ require 'win32ole'
+rescue LoadError
+end
+
+if defined?(WIN32OLE)
+ module AvailableOLE
+ module_function
+
+ def sysmon_available?
+ WIN32OLE_TYPE.new('System Monitor Control', 'SystemMonitor')
+ true
+ rescue
+ false
+ end
+
+ def ado_available?
+ WIN32OLE.new('ADODB.Connection')
+ true
+ rescue
+ false
+ end
+
+ def msxml_available?
+ !WIN32OLE_TYPELIB.typelibs.find { |t| t.name.start_with?('Microsoft XML') }.nil?
+ end
+
+ def event_param
+ method = if msxml_available?
+ typelib = WIN32OLE_TYPELIB.typelibs.find { |t| t.name.start_with?('Microsoft XML') }
+ ole_type = WIN32OLE_TYPE.new(typelib.name, 'IVBSAXContentHandler')
+ WIN32OLE_METHOD.new(ole_type, 'startElement')
+ elsif ado_available?
+ typelib = WIN32OLE.new('ADODB.Connection').ole_typelib
+ ole_type = WIN32OLE_TYPE.new(typelib.name, 'Connection')
+ WIN32OLE_METHOD.new(ole_type, 'WillConnect')
+ end
+ method && method.params[0]
+ end
+ end
+end
diff --git a/test/win32ole/test_ole_methods.rb b/test/win32ole/test_ole_methods.rb
index cb903566b9..fbade04e48 100644
--- a/test/win32ole/test_ole_methods.rb
+++ b/test/win32ole/test_ole_methods.rb
@@ -19,7 +19,6 @@ if defined?(WIN32OLE)
x = @obj.ole_methods.select {|m|
m.invoke_kind == 'PROPERTYPUTREF'
}
- assert(x.size > 0)
assert_equal(1, x.size)
assert_equal('Item', x[0].name)
end
@@ -28,7 +27,6 @@ if defined?(WIN32OLE)
x = @obj.ole_put_methods.select {|m|
m.invoke_kind == 'PROPERTYPUTREF'
}
- assert(x.size > 0)
assert_equal(1, x.size)
assert_equal('Item', x[0].name)
end
diff --git a/test/win32ole/test_thread.rb b/test/win32ole/test_thread.rb
index f1cdfe32b0..cb34693064 100644
--- a/test/win32ole/test_thread.rb
+++ b/test/win32ole/test_thread.rb
@@ -6,7 +6,7 @@ end
require 'test/unit'
if defined?(WIN32OLE)
- class TestThread < Test::Unit::TestCase
+ class TestWIN32OLE_THREAD < Test::Unit::TestCase
#
# test for Bug #2618(ruby-core:27634)
#
diff --git a/test/win32ole/test_win32ole.rb b/test/win32ole/test_win32ole.rb
index c095e6d7ce..7dda36ce96 100644
--- a/test/win32ole/test_win32ole.rb
+++ b/test/win32ole/test_win32ole.rb
@@ -58,9 +58,10 @@ if defined?(WIN32OLE)
def test_no_method_error
exc = assert_raise(NoMethodError) {
- @dict1.non_exist_method
+ @dict1.non_exist_method
}
assert_match(/non_exist_method/, exc.message)
+ assert_kind_of(WIN32OLE, exc.receiver)
end
def test_ole_methods
@@ -68,7 +69,12 @@ if defined?(WIN32OLE)
mnames = methods.collect {|m|
m.name
}
- assert(mnames.include?("Add"))
+ assert_include(mnames, 'Add')
+ end
+
+ def test_methods
+ methods = @dict1.methods
+ assert_include(methods, :Add)
end
def test_ole_func_methods
@@ -76,7 +82,7 @@ if defined?(WIN32OLE)
mnames = methods.collect {|m|
m.name
}
- assert(mnames.include?("Add"))
+ assert_include(mnames, 'Add')
end
def test_ole_put_methods
@@ -84,7 +90,7 @@ if defined?(WIN32OLE)
mnames = methods.collect {|m|
m.name
}
- assert(mnames.include?("CompareMode"))
+ assert_include(mnames, 'CompareMode')
end
def test_ole_get_methods
@@ -92,7 +98,7 @@ if defined?(WIN32OLE)
mnames = methods.collect {|m|
m.name
}
- assert(mnames.include?("Count"))
+ assert_include(mnames, 'Count')
end
def test_ole_mehtod_help
@@ -175,12 +181,15 @@ if defined?(WIN32OLE)
$SAFE = 1
svr = "Scripting.Dictionary"
svr.taint
+ Thread.current.report_on_exception = false
WIN32OLE.new(svr)
}
exc = assert_raise(SecurityError) {
th.join
}
assert_match(/insecure object creation - `Scripting.Dictionary'/, exc.message)
+ ensure
+ $SAFE = 0
end
def test_s_new_exc_host_tainted
@@ -189,12 +198,15 @@ if defined?(WIN32OLE)
svr = "Scripting.Dictionary"
host = "localhost"
host.taint
+ Thread.current.report_on_exception = false
WIN32OLE.new(svr, host)
}
exc = assert_raise(SecurityError) {
th.join
}
assert_match(/insecure object creation - `localhost'/, exc.message)
+ ensure
+ $SAFE = 0
end
def test_s_new_DCOM
@@ -227,12 +239,15 @@ if defined?(WIN32OLE)
$SAFE = 1
svr = "winmgmts:"
svr.taint
+ Thread.current.report_on_exception = false
WIN32OLE.connect(svr)
}
exc = assert_raise(SecurityError) {
th.join
}
assert_match(/insecure connection - `winmgmts:'/, exc.message)
+ ensure
+ $SAFE = 0
end
def test_invoke_accept_symbol_hash_key
diff --git a/test/win32ole/test_win32ole_event.rb b/test/win32ole/test_win32ole_event.rb
index 02bbeee321..79fe9ddc72 100644
--- a/test/win32ole/test_win32ole_event.rb
+++ b/test/win32ole/test_win32ole_event.rb
@@ -63,11 +63,27 @@ if defined?(WIN32OLE_EVENT)
@sql = "SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_LocalTime'"
end
- def message_loop
+ def message_loop(watch_ivar = nil)
+ if watch_ivar
+ orig_ivar = instance_variable_get(watch_ivar)
+ end
+
2.times do
WIN32OLE_EVENT.message_loop
sleep 1
end
+
+ if watch_ivar
+ # wait until event is proceeded
+ tries = 0
+ while tries < 5 && instance_variable_get(watch_ivar) == orig_ivar
+ seconds = 2 ** tries # sleep at most 31s in total
+ $stderr.puts "test_win32ole_event.rb: retrying and sleeping #{seconds}s until #{watch_ivar} is changed from #{orig_ivar.inspect}..."
+ WIN32OLE_EVENT.message_loop
+ sleep(seconds)
+ tries += 1
+ end
+ end
end
def default_handler(event, *args)
@@ -101,14 +117,17 @@ if defined?(WIN32OLE_EVENT)
message_loop
GC.start
end
- assert_match(/OnObjectReady/, @event)
+
+ # @event randomly becomes "OnCompleted" here. Try to wait until it matches.
+ # https://ci.appveyor.com/project/ruby/ruby/builds/19963142/job/8gaxepksa0i3b998
+ assert_match_with_retries(/OnObjectReady/, :@event)
end
def test_on_event
exec_notification_query_async
ev = WIN32OLE_EVENT.new(@sws, 'ISWbemSinkEvents')
ev.on_event {|*args| default_handler(*args)}
- message_loop
+ message_loop(:@event)
assert_match(/OnObjectReady/, @event)
end
@@ -118,7 +137,7 @@ if defined?(WIN32OLE_EVENT)
ev.on_event(:OnObjectReady) {|*args|
handler1
}
- message_loop
+ message_loop(:@event1)
assert_equal("handler1", @event1)
end
@@ -131,6 +150,20 @@ if defined?(WIN32OLE_EVENT)
end
raise
end
+
+ def assert_match_with_retries(regexp, ivarname)
+ ivar = instance_variable_get(ivarname)
+
+ tries = 0
+ while tries < 5 && !ivar.match(regexp)
+ $stderr.puts "test_win32ole_event.rb: retrying until #{ivarname} matches #{regexp} (tries: #{tries})..."
+ sleep(2 ** tries) # sleep at most 31s in total
+ ivar = instance_variable_get(ivarname)
+ tries += 1
+ end
+
+ assert_match(regexp, ivar)
+ end
end
end
@@ -395,6 +428,8 @@ if defined?(WIN32OLE_EVENT)
th.join
}
assert_match(/insecure event creation - `ConnectionEvents'/, exc.message)
+ ensure
+ $SAFE = 0
end
end
end
diff --git a/test/win32ole/test_win32ole_method.rb b/test/win32ole/test_win32ole_method.rb
index 7fb43fd542..a0e113e7f0 100644
--- a/test/win32ole/test_win32ole_method.rb
+++ b/test/win32ole/test_win32ole_method.rb
@@ -18,9 +18,6 @@ if defined?(WIN32OLE_METHOD)
ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File")
@m_file_name = WIN32OLE_METHOD.new(ole_type, "name")
-
- ole_type = WIN32OLE_TYPE.new("Microsoft Internet Controls", "WebBrowser")
- @m_navigate_complete = WIN32OLE_METHOD.new(ole_type, "NavigateComplete")
end
def test_initialize
@@ -81,16 +78,6 @@ if defined?(WIN32OLE_METHOD)
assert(!@m_invoke.visible?)
end
- def test_event?
- assert(@m_navigate_complete.event?)
- assert(!@m_namespace.event?)
- end
-
- def test_event_interface
- assert_equal("DWebBrowserEvents", @m_navigate_complete.event_interface)
- assert_equal(nil, @m_namespace.event_interface)
- end
-
def test_helpstring
assert_equal("Get special folder from ShellSpecialFolderConstants", @m_namespace.helpstring)
end
diff --git a/test/win32ole/test_win32ole_method_event.rb b/test/win32ole/test_win32ole_method_event.rb
new file mode 100644
index 0000000000..6dad6ff2b4
--- /dev/null
+++ b/test/win32ole/test_win32ole_method_event.rb
@@ -0,0 +1,36 @@
+begin
+ require 'win32ole'
+rescue LoadError
+end
+
+require 'test/unit'
+
+if defined?(WIN32OLE_METHOD)
+ require_relative 'available_ole'
+ class TestWIN32OLE_METHOD_EVENT < Test::Unit::TestCase
+ unless AvailableOLE.sysmon_available?
+ def test_dummy_for_skip_message
+ skip 'System Monitor Control is not available'
+ end
+ else
+ def setup
+ ole_type = WIN32OLE_TYPE.new('System Monitor Control', 'SystemMonitor')
+ @on_dbl_click = WIN32OLE_METHOD.new(ole_type, 'OnDblClick')
+ ole_type = WIN32OLE_TYPE.new('Microsoft Shell Controls And Automation', 'Shell')
+ @namespace = WIN32OLE_METHOD.new(ole_type, 'namespace')
+ end
+
+ def test_event?
+ assert(@on_dbl_click.event?)
+ end
+
+ def test_event_interface
+ assert('DISystemMonitorEvents', @on_dbl_click.event_interface)
+ end
+
+ def test_event_interface_is_nil
+ assert_equal(nil, @namespace.event_interface)
+ end
+ end
+ end
+end
diff --git a/test/win32ole/test_win32ole_param.rb b/test/win32ole/test_win32ole_param.rb
index 4f8104b1d1..09452d1927 100644
--- a/test/win32ole/test_win32ole_param.rb
+++ b/test/win32ole/test_win32ole_param.rb
@@ -9,14 +9,6 @@ if defined?(WIN32OLE_PARAM)
class TestWIN32OLE_PARAM < Test::Unit::TestCase
def setup
- ole_type = WIN32OLE_TYPE.new("Microsoft Internet Controls", "WebBrowser")
- m_navigate = WIN32OLE_METHOD.new(ole_type, "Navigate")
- m_before_navigate = WIN32OLE_METHOD.new(ole_type, "BeforeNavigate")
- params = m_navigate.params
- @param_url = params[0]
- @param_flags = params[1]
- @param_cancel = m_before_navigate.params[5]
-
ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellLinkObject")
m_geticonlocation = WIN32OLE_METHOD.new(ole_type, "GetIconLocation")
@param_pbs = m_geticonlocation.params[0]
@@ -27,7 +19,12 @@ if defined?(WIN32OLE_PARAM)
ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject")
m_copyfile = WIN32OLE_METHOD.new(ole_type, "CopyFile")
+ @param_source = m_copyfile.params[0]
@param_overwritefiles = m_copyfile.params[2]
+
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "Dictionary")
+ m_add = WIN32OLE_METHOD.new(ole_type, "Add")
+ @param_key = m_add.params[0]
end
def test_s_new
@@ -42,9 +39,6 @@ if defined?(WIN32OLE_PARAM)
assert_raise(IndexError) {
WIN32OLE_PARAM.new(m_copyfile, 0);
}
- assert_raise(IndexError) {
- WIN32OLE_PARAM.new(m_copyfile, 0);
- }
param = WIN32OLE_PARAM.new(m_copyfile, 3)
assert_equal("OverWriteFiles", param.name)
assert_equal(WIN32OLE_PARAM, param.class)
@@ -53,54 +47,51 @@ if defined?(WIN32OLE_PARAM)
end
def test_name
- assert_equal('URL', @param_url.name)
- assert_equal('Flags', @param_flags.name)
- assert_equal('Cancel', @param_cancel.name)
+ assert_equal('Source', @param_source.name)
+ assert_equal('Key', @param_key.name)
end
def test_ole_type
- assert_equal('BSTR', @param_url.ole_type)
- assert_equal('VARIANT', @param_flags.ole_type)
+ assert_equal('BSTR', @param_source.ole_type)
+ assert_equal('VARIANT', @param_key.ole_type)
end
def test_ole_type_detail
- assert_equal(['BSTR'], @param_url.ole_type_detail)
- assert_equal(['PTR', 'VARIANT'], @param_flags.ole_type_detail)
+ assert_equal(['BSTR'], @param_source.ole_type_detail)
+ assert_equal(['PTR', 'VARIANT'], @param_key.ole_type_detail)
end
def test_input?
- assert(@param_url.input?)
- assert(@param_cancel.input?)
- assert(!@param_pbs.input?)
+ assert_equal(true, @param_source.input?)
+ assert_equal(false, @param_pbs.input?)
end
def test_output?
- assert(!@param_url.output?)
- assert(@param_cancel.output?)
- assert(@param_pbs.output?)
+ assert_equal(false, @param_source.output?)
+ assert_equal(true, @param_pbs.output?)
end
def test_optional?
- assert(!@param_url.optional?)
- assert(@param_flags.optional?)
+ assert_equal(false, @param_source.optional?)
+ assert_equal(true, @param_overwritefiles.optional?)
end
def test_retval?
- assert(!@param_url.retval?)
- assert(@param_p.retval?)
+ assert_equal(false, @param_source.retval?)
+ assert_equal(true, @param_p.retval?)
end
def test_default
- assert_equal(nil, @param_url.default)
+ assert_equal(nil, @param_source.default)
assert_equal(true, @param_overwritefiles.default)
end
def test_to_s
- assert_equal(@param_url.name, @param_url.to_s)
+ assert_equal(@param_source.name, @param_source.to_s)
end
def test_inspect
- assert_equal("#<WIN32OLE_PARAM:URL>", @param_url.inspect)
+ assert_equal("#<WIN32OLE_PARAM:Source>", @param_source.inspect)
assert_equal("#<WIN32OLE_PARAM:OverWriteFiles=true>", @param_overwritefiles.inspect)
end
end
diff --git a/test/win32ole/test_win32ole_param_event.rb b/test/win32ole/test_win32ole_param_event.rb
new file mode 100644
index 0000000000..64812e567d
--- /dev/null
+++ b/test/win32ole/test_win32ole_param_event.rb
@@ -0,0 +1,30 @@
+begin
+ require 'win32ole'
+rescue LoadError
+end
+
+require 'test/unit'
+
+if defined?(WIN32OLE_PARAM)
+ require_relative 'available_ole'
+
+ class TestWIN32OLE_PARAM_EVENT < Test::Unit::TestCase
+ if AvailableOLE.msxml_available? || AvailableOLE.ado_available?
+ def setup
+ @param = AvailableOLE.event_param
+ end
+
+ def test_input?
+ assert_equal(true, @param.input?)
+ end
+
+ def test_output?
+ assert_equal(true, @param.output?)
+ end
+ else
+ def test_dummy_for_skip_message
+ skip 'ActiveX Data Object Library and MS XML not found'
+ end
+ end
+ end
+end
diff --git a/test/win32ole/test_win32ole_record.rb b/test/win32ole/test_win32ole_record.rb
index 654c747711..7d6c3fb4af 100644
--- a/test/win32ole/test_win32ole_record.rb
+++ b/test/win32ole/test_win32ole_record.rb
@@ -67,14 +67,10 @@ End Class
if defined?(WIN32OLE_RECORD)
def rbcomtest_exist?
- exist = false
- begin
- obj = WIN32OLE.new(PROGID_RBCOMTEST)
- exist = true
- rescue WIN32OLERuntimeError
- exist = false
- end
- exist
+ WIN32OLE.new(PROGID_RBCOMTEST)
+ true
+ rescue WIN32OLERuntimeError
+ false
end
class TestWIN32OLE_RECORD_BY_RBCOMTEST < Test::Unit::TestCase
@@ -102,13 +98,13 @@ if defined?(WIN32OLE_RECORD)
def test_s_new_raise
assert_raise(WIN32OLERuntimeError) {
- rec = WIN32OLE_RECORD.new('NonExistRecordName', @obj)
+ WIN32OLE_RECORD.new('NonExistRecordName', @obj)
}
assert_raise(ArgumentError) {
- rec = WIN32OLE_RECORD.new
+ WIN32OLE_RECORD.new
}
assert_raise(ArgumentError) {
- rec = WIN32OLE_RECORD.new('NonExistRecordName')
+ WIN32OLE_RECORD.new('NonExistRecordName')
}
end
diff --git a/test/win32ole/test_win32ole_type.rb b/test/win32ole/test_win32ole_type.rb
index ce11e0235f..485b390d5c 100644
--- a/test/win32ole/test_win32ole_type.rb
+++ b/test/win32ole/test_win32ole_type.rb
@@ -175,62 +175,12 @@ if defined?(WIN32OLE_TYPE)
assert_instance_of(Array, ole_types)
assert_equal(1, ole_types.size)
assert_match(/^IShellDispatch\d{0,1}$/, ole_types[0].name)
-
- ie_otype = WIN32OLE_TYPE.new("Microsoft Internet Controls", "InternetExplorer")
- ole_types = ie_otype.implemented_ole_types
- assert_equal(4, ole_types.size)
- otype = ole_types.select{|t| t.name == "IWebBrowser2"}
- assert_equal(1, otype.size)
- otype = ole_types.select{|t| t.name == "IWebBrowserApp"}
- assert_equal(1, otype.size)
- otype = ole_types.select{|t| t.name == "DWebBrowserEvents2"}
- assert_equal(1, otype.size)
- otype = ole_types.select{|t| t.name == "DWebBrowserEvents"}
- assert_equal(1, otype.size)
- end
-
- def test_default_ole_types
- ie_otype = WIN32OLE_TYPE.new("Microsoft Internet Controls", "InternetExplorer")
- ole_types = ie_otype.default_ole_types
- otype = ole_types.select{|t| t.name == "IWebBrowser2"}
- assert_equal(1, otype.size)
- otype = ole_types.select{|t| t.name == "IWebBrowserApp"}
- assert_equal(0, otype.size)
- otype = ole_types.select{|t| t.name == "DWebBrowserEvents2"}
- assert_equal(1, otype.size)
- otype = ole_types.select{|t| t.name == "DWebBrowserEvents"}
- assert_equal(0, otype.size)
- end
-
- def test_source_ole_types
- ie_otype = WIN32OLE_TYPE.new("Microsoft Internet Controls", "InternetExplorer")
- ole_types = ie_otype.source_ole_types
- otype = ole_types.select{|t| t.name == "IWebBrowser2"}
- assert_equal(0, otype.size)
- otype = ole_types.select{|t| t.name == "IWebBrowserApp"}
- assert_equal(0, otype.size)
- otype = ole_types.select{|t| t.name == "DWebBrowserEvents2"}
- assert_equal(1, otype.size)
- otype = ole_types.select{|t| t.name == "DWebBrowserEvents"}
- assert_equal(1, otype.size)
- end
-
- def test_default_event_sources
- ie_otype = WIN32OLE_TYPE.new("Microsoft Internet Controls", "InternetExplorer")
- ole_types = ie_otype.default_event_sources
- otype = ole_types.select{|t| t.name == "IWebBrowser2"}
- assert_equal(0, otype.size)
- otype = ole_types.select{|t| t.name == "IWebBrowserApp"}
- assert_equal(0, otype.size)
- otype = ole_types.select{|t| t.name == "DWebBrowserEvents2"}
- assert_equal(1, otype.size)
- otype = ole_types.select{|t| t.name == "DWebBrowserEvents"}
- assert_equal(0, otype.size)
end
def test_inspect
assert_equal("#<WIN32OLE_TYPE:Shell>", @ole_type.inspect)
end
+
# WIN32OLE_TYPE.typelibs will be obsoleted.
def test_s_typelibs
tlibs = WIN32OLE_TYPE.typelibs.sort
diff --git a/test/win32ole/test_win32ole_type_event.rb b/test/win32ole/test_win32ole_type_event.rb
new file mode 100644
index 0000000000..ad2de54e59
--- /dev/null
+++ b/test/win32ole/test_win32ole_type_event.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: false
+begin
+ require 'win32ole'
+rescue LoadError
+end
+
+require 'test/unit'
+
+if defined?(WIN32OLE_TYPE)
+ require_relative 'available_ole'
+
+ class TestWIN32OLE_TYPE_EVENT < Test::Unit::TestCase
+ unless AvailableOLE.sysmon_available?
+ def test_dummy_for_skip_message
+ skip 'System Monitor Control is not available'
+ end
+ else
+
+ def setup
+ @ole_type = WIN32OLE_TYPE.new('System Monitor Control', 'SystemMonitor')
+ end
+
+ def test_implemented_ole_types
+ ole_types = @ole_type.implemented_ole_types.map(&:name).sort
+ assert_equal(['DISystemMonitor', 'DISystemMonitorEvents', 'ISystemMonitor'], ole_types)
+ end
+
+ def test_default_ole_types
+ ole_types = @ole_type.default_ole_types.map(&:name).sort
+ assert_equal(['DISystemMonitor', 'DISystemMonitorEvents'], ole_types)
+ end
+
+ def test_source_ole_types
+ ole_types = @ole_type.source_ole_types.map(&:name)
+ assert_equal(['DISystemMonitorEvents'], ole_types)
+ end
+
+ def test_default_event_sources
+ event_sources = @ole_type.default_event_sources.map(&:name)
+ assert_equal(['DISystemMonitorEvents'], event_sources)
+ end
+ end
+ end
+end
diff --git a/test/win32ole/test_word.rb b/test/win32ole/test_word.rb
index 03b0bcbdde..b1cdb273cc 100644
--- a/test/win32ole/test_word.rb
+++ b/test/win32ole/test_word.rb
@@ -8,17 +8,22 @@ rescue LoadError
end
require "test/unit"
+if defined?(WIN32OLE)
+ module Word; end
+end
+
def word_installed?
installed = false
w = nil
if defined?(WIN32OLE)
begin
w = WIN32OLE.new('Word.Application')
+ WIN32OLE.const_load(w, Word)
installed = true
rescue
ensure
if w
- w.quit
+ w.quit(Word::WdDoNotSaveChanges)
w = nil
end
end
@@ -59,7 +64,7 @@ if defined?(WIN32OLE)
def teardown
if @obj
- @obj.quit
+ @obj.quit(Word::WdDoNotSaveChanges)
@obj = nil
end
end
diff --git a/test/yaml/test_store.rb b/test/yaml/test_store.rb
index e55780533f..9112c569a3 100644
--- a/test/yaml/test_store.rb
+++ b/test/yaml/test_store.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
require 'test/unit'
require 'yaml/store'
require 'tmpdir'
diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb
index 0092daf0d9..fc2ab5073f 100644
--- a/test/zlib/test_zlib.rb
+++ b/test/zlib/test_zlib.rb
@@ -1,4 +1,5 @@
-# frozen_string_literal: false
+# coding: us-ascii
+# frozen_string_literal: true
require 'test/unit'
require 'stringio'
require 'tempfile'
@@ -41,7 +42,7 @@ if defined? Zlib
end
def test_deflate_chunked
- original = ''
+ original = ''.dup
chunks = []
r = Random.new 0
@@ -126,6 +127,16 @@ if defined? Zlib
assert_equal("foobar", Zlib::Inflate.inflate(s))
end
+ def test_expand_buffer;
+ z = Zlib::Deflate.new
+ src = "baz" * 1000
+ z.avail_out = 1
+ GC.stress = true
+ s = z.deflate(src, Zlib::FINISH)
+ GC.stress = false
+ assert_equal(src, Zlib::Inflate.inflate(s))
+ end
+
def test_total
z = Zlib::Deflate.new
1000.times { z << "foo" }
@@ -212,7 +223,9 @@ if defined? Zlib
z = Zlib::Deflate.new
z << "foo"
assert_raise(Zlib::StreamError) { z.set_dictionary("foo") }
- z.close # without this, outputs `zlib(finalizer): the stream was freed prematurely.'
+ EnvUtil.suppress_warning do
+ z.close # without this, outputs `zlib(finalizer): the stream was freed prematurely.'
+ end
end
def test_reset
@@ -312,7 +325,7 @@ if defined? Zlib
z = Zlib::Inflate.new
- inflated = ""
+ inflated = "".dup
deflated.each_char do |byte|
inflated << z.inflate(byte)
@@ -600,7 +613,7 @@ if defined? Zlib
assert_equal(t.path, f.path)
end
- s = ""
+ s = "".dup
sio = StringIO.new(s)
gz = Zlib::GzipWriter.new(sio)
gz.print("foo")
@@ -608,9 +621,9 @@ if defined? Zlib
gz.close
sio = StringIO.new(s)
- Zlib::GzipReader.new(sio) do |f|
- assert_raise(NoMethodError) { f.path }
- end
+ gz = Zlib::GzipReader.new(sio)
+ assert_raise(NoMethodError) { gz.path }
+ gz.close
}
end
end
@@ -622,7 +635,7 @@ if defined? Zlib
end
def test_ungetc
- s = ""
+ s = "".dup
w = Zlib::GzipWriter.new(StringIO.new(s))
w << (1...1000).to_a.inspect
w.close
@@ -637,7 +650,7 @@ if defined? Zlib
end
def test_ungetc_paragraph
- s = ""
+ s = "".dup
w = Zlib::GzipWriter.new(StringIO.new(s))
w << "abc"
w.close
@@ -650,6 +663,18 @@ if defined? Zlib
}
end
+ def test_ungetc_at_start_of_file
+ s = "".dup
+ w = Zlib::GzipWriter.new(StringIO.new(s))
+ w << "abc"
+ w.close
+ r = Zlib::GzipReader.new(StringIO.new(s))
+
+ r.ungetc ?!
+
+ assert_equal(-1, r.pos, "[ruby-core:81488][Bug #13616]")
+ end
+
def test_open
Tempfile.create("test_zlib_gzip_reader_open") {|t|
t.close
@@ -775,7 +800,7 @@ if defined? Zlib
end
Zlib::GzipReader.open(t.path) do |f|
- s = ""
+ s = "".dup
f.readpartial(3, s)
assert("foo".start_with?(s))
@@ -935,7 +960,7 @@ if defined? Zlib
end
def test_corrupted_header
- gz = Zlib::GzipWriter.new(StringIO.new(s = ""))
+ gz = Zlib::GzipWriter.new(StringIO.new(s = "".dup))
gz.orig_name = "X"
gz.comment = "Y"
gz.print("foo")
@@ -1032,6 +1057,14 @@ if defined? Zlib
}
end
+ def test_puts
+ Tempfile.create("test_zlib_gzip_writer_puts") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.puts("foo") }
+ assert_equal("foo\n", Zlib::GzipReader.open(t.path) {|gz| gz.read })
+ }
+ end
+
def test_writer_wrap
Tempfile.create("test_zlib_gzip_writer_wrap") {|t|
t.binmode
@@ -1048,6 +1081,23 @@ if defined? Zlib
assert_nothing_raised { w.close }
}
end
+
+ def test_zlib_writer_buffered_write
+ bug15356 = '[ruby-core:90346] [Bug #15356]'.freeze
+ fixes = 'r61631 (commit a55abcc0ca6f628fc05304f81e5a044d65ab4a68)'.freeze
+ ary = []
+ def ary.write(*args)
+ self.concat(args)
+ end
+ gz = Zlib::GzipWriter.new(ary)
+ gz.write(bug15356)
+ gz.write("\n")
+ gz.write(fixes)
+ gz.close
+ assert_not_predicate ary, :empty?
+ exp = [ bug15356, fixes ]
+ assert_equal exp, Zlib.gunzip(ary.join('')).split("\n")
+ end
end
class TestZlib < Test::Unit::TestCase
@@ -1117,7 +1167,7 @@ if defined? Zlib
def test_deflate_stream
r = Random.new 0
- deflated = ''
+ deflated = ''.dup
Zlib.deflate(r.bytes(20000)) do |chunk|
deflated << chunk
@@ -1126,5 +1176,58 @@ if defined? Zlib
assert_equal 20016, deflated.length
end
+ def test_gzip
+ actual = Zlib.gzip("foo".freeze)
+ actual[4, 4] = "\x00\x00\x00\x00" # replace mtime
+ actual[9] = "\xff" # replace OS
+ expected = %w[1f8b08000000000000ff4bcbcf07002165738c03000000].pack("H*")
+ assert_equal expected, actual
+
+ actual = Zlib.gzip("foo".freeze, level: 0)
+ actual[4, 4] = "\x00\x00\x00\x00" # replace mtime
+ actual[9] = "\xff" # replace OS
+ expected = %w[1f8b08000000000000ff010300fcff666f6f2165738c03000000].pack("H*")
+ assert_equal expected, actual
+
+ actual = Zlib.gzip("foo".freeze, level: 9)
+ actual[4, 4] = "\x00\x00\x00\x00" # replace mtime
+ actual[9] = "\xff" # replace OS
+ expected = %w[1f8b08000000000002ff4bcbcf07002165738c03000000].pack("H*")
+ assert_equal expected, actual
+
+ actual = Zlib.gzip("foo".freeze, level: 9, strategy: Zlib::FILTERED)
+ actual[4, 4] = "\x00\x00\x00\x00" # replace mtime
+ actual[9] = "\xff" # replace OS
+ expected = %w[1f8b08000000000002ff4bcbcf07002165738c03000000].pack("H*")
+ assert_equal expected, actual
+ end
+
+ def test_gunzip
+ src = %w[1f8b08000000000000034bcbcf07002165738c03000000].pack("H*")
+ assert_equal 'foo', Zlib.gunzip(src.freeze)
+
+ src = %w[1f8b08000000000000034bcbcf07002165738c03000001].pack("H*")
+ assert_raise(Zlib::GzipFile::LengthError){ Zlib.gunzip(src) }
+
+ src = %w[1f8b08000000000000034bcbcf07002165738d03000000].pack("H*")
+ assert_raise(Zlib::GzipFile::CRCError){ Zlib.gunzip(src) }
+
+ src = %w[1f8b08000000000000034bcbcf07002165738d030000].pack("H*")
+ assert_raise(Zlib::GzipFile::Error){ Zlib.gunzip(src) }
+
+ src = %w[1f8b08000000000000034bcbcf0700].pack("H*")
+ assert_raise(Zlib::GzipFile::NoFooter){ Zlib.gunzip(src) }
+
+ src = %w[1f8b080000000000000].pack("H*")
+ assert_raise(Zlib::GzipFile::Error){ Zlib.gunzip(src) }
+ end
+
+ def test_gunzip_no_memory_leak
+ assert_no_memory_leak(%[-rzlib], "#{<<~"{#"}", "#{<<~'};'}")
+ d = Zlib.gzip("data")
+ {#
+ 10_000.times {Zlib.gunzip(d)}
+ };
+ end
end
end
diff --git a/thread.c b/thread.c
index c596b5bc12..eca14b4b4c 100644
--- a/thread.c
+++ b/thread.c
@@ -20,6 +20,12 @@
model 3: Native Thread with fine grain lock
Using pthread and Ruby threads run concurrent or parallel.
+ model 4: M:N User:Native threads with Global VM lock
+ Combination of model 1 and 2
+
+ model 5: M:N User:Native thread with fine grain lock
+ Combination of model 1 and 3
+
------------------------------------------------------------------------
model 2:
@@ -57,13 +63,19 @@
/* for model 2 */
+#include "ruby/config.h"
+#include "ruby/io.h"
#include "eval_intern.h"
#include "gc.h"
#include "timev.h"
-#include "ruby/io.h"
#include "ruby/thread.h"
#include "ruby/thread_native.h"
+#include "ruby/debug.h"
#include "internal.h"
+#include "iseq.h"
+#include "vm_core.h"
+#include "mjit.h"
+#include "hrtime.h"
#ifndef USE_NATIVE_THREAD_PRIORITY
#define USE_NATIVE_THREAD_PRIORITY 0
@@ -82,19 +94,33 @@ static VALUE sym_on_blocking;
static VALUE sym_never;
static ID id_locals;
-static void sleep_timeval(rb_thread_t *th, struct timeval time, int spurious_check);
-static void sleep_wait_for_interrupt(rb_thread_t *th, double sleepsec, int spurious_check);
-static void sleep_forever(rb_thread_t *th, int nodeadlock, int spurious_check);
-static double timeofday(void);
+enum SLEEP_FLAGS {
+ SLEEP_DEADLOCKABLE = 0x1,
+ SLEEP_SPURIOUS_CHECK = 0x2
+};
+
+static void sleep_hrtime(rb_thread_t *, rb_hrtime_t, unsigned int fl);
+static void sleep_forever(rb_thread_t *th, unsigned int fl);
+static void rb_thread_sleep_deadly_allow_spurious_wakeup(void);
static int rb_threadptr_dead(rb_thread_t *th);
static void rb_check_deadlock(rb_vm_t *vm);
-static int rb_threadptr_pending_interrupt_empty_p(rb_thread_t *th);
+static int rb_threadptr_pending_interrupt_empty_p(const rb_thread_t *th);
+static const char *thread_status_name(rb_thread_t *th, int detail);
+static int hrtime_update_expire(rb_hrtime_t *, const rb_hrtime_t);
+NORETURN(static void async_bug_fd(const char *mesg, int errno_arg, int fd));
+static int consume_communication_pipe(int fd);
+static int check_signals_nogvl(rb_thread_t *, int sigwait_fd);
+void rb_sigwait_fd_migrate(rb_vm_t *); /* process.c */
#define eKillSignal INT2FIX(0)
#define eTerminateSignal INT2FIX(1)
static volatile int system_working = 1;
-#define closed_stream_error GET_VM()->special_exceptions[ruby_error_closed_stream]
+struct waiting_fd {
+ struct list_node wfd_node; /* <=> vm.waiting_fds */
+ rb_thread_t *th;
+ int fd;
+};
inline static void
st_delete_wrap(st_table *table, st_data_t key)
@@ -108,12 +134,10 @@ st_delete_wrap(st_table *table, st_data_t key)
struct rb_blocking_region_buffer {
enum rb_thread_status prev_status;
- struct rb_unblock_callback oldubf;
};
-static int set_unblock_function(rb_thread_t *th, rb_unblock_function_t *func, void *arg,
- struct rb_unblock_callback *old, int fail_if_interrupted);
-static void reset_unblock_function(rb_thread_t *th, const struct rb_unblock_callback *old);
+static int unblock_function_set(rb_thread_t *th, rb_unblock_function_t *func, void *arg, int fail_if_interrupted);
+static void unblock_function_clear(rb_thread_t *th);
static inline int blocking_region_begin(rb_thread_t *th, struct rb_blocking_region_buffer *region,
rb_unblock_function_t *ubf, void *arg, int fail_if_interrupted);
@@ -121,7 +145,7 @@ static inline void blocking_region_end(rb_thread_t *th, struct rb_blocking_regio
#ifdef __ia64
#define RB_GC_SAVE_MACHINE_REGISTER_STACK(th) \
- do{(th)->machine.register_stack_end = rb_ia64_bsp();}while(0)
+ do{(th)->ec->machine.register_stack_end = rb_ia64_bsp();}while(0)
#else
#define RB_GC_SAVE_MACHINE_REGISTER_STACK(th)
#endif
@@ -129,18 +153,17 @@ static inline void blocking_region_end(rb_thread_t *th, struct rb_blocking_regio
do { \
FLUSH_REGISTER_WINDOWS; \
RB_GC_SAVE_MACHINE_REGISTER_STACK(th); \
- setjmp((th)->machine.regs); \
- SET_MACHINE_STACK_END(&(th)->machine.stack_end); \
+ setjmp((th)->ec->machine.regs); \
+ SET_MACHINE_STACK_END(&(th)->ec->machine.stack_end); \
} while (0)
-#define GVL_UNLOCK_BEGIN() do { \
- rb_thread_t *_th_stored = GET_THREAD(); \
- RB_GC_SAVE_MACHINE_CONTEXT(_th_stored); \
- gvl_release(_th_stored->vm);
+#define GVL_UNLOCK_BEGIN(th) do { \
+ RB_GC_SAVE_MACHINE_CONTEXT(th); \
+ gvl_release(th->vm);
-#define GVL_UNLOCK_END() \
- gvl_acquire(_th_stored->vm, _th_stored); \
- rb_thread_set_current(_th_stored); \
+#define GVL_UNLOCK_END(th) \
+ gvl_acquire(th->vm, th); \
+ rb_thread_set_current(th); \
} while(0)
#ifdef __GNUC__
@@ -152,42 +175,76 @@ static inline void blocking_region_end(rb_thread_t *th, struct rb_blocking_regio
#else
#define only_if_constant(expr, notconst) notconst
#endif
-#define BLOCKING_REGION(exec, ubf, ubfarg, fail_if_interrupted) do { \
- rb_thread_t *__th = GET_THREAD(); \
+#define BLOCKING_REGION(th, exec, ubf, ubfarg, fail_if_interrupted) do { \
struct rb_blocking_region_buffer __region; \
- if (blocking_region_begin(__th, &__region, (ubf), (ubfarg), fail_if_interrupted) || \
+ if (blocking_region_begin(th, &__region, (ubf), (ubfarg), fail_if_interrupted) || \
/* always return true unless fail_if_interrupted */ \
!only_if_constant(fail_if_interrupted, TRUE)) { \
exec; \
- blocking_region_end(__th, &__region); \
+ blocking_region_end(th, &__region); \
}; \
} while(0)
-#define RUBY_VM_CHECK_INTS_BLOCKING(th) vm_check_ints_blocking(th)
-static inline void
-vm_check_ints_blocking(rb_thread_t *th)
+/*
+ * returns true if this thread was spuriously interrupted, false otherwise
+ * (e.g. hit by Thread#run or ran a Ruby-level Signal.trap handler)
+ */
+#define RUBY_VM_CHECK_INTS_BLOCKING(ec) vm_check_ints_blocking(ec)
+static inline int
+vm_check_ints_blocking(rb_execution_context_t *ec)
{
+ rb_thread_t *th = rb_ec_thread_ptr(ec);
+
if (LIKELY(rb_threadptr_pending_interrupt_empty_p(th))) {
- if (LIKELY(!RUBY_VM_INTERRUPTED_ANY(th))) return;
+ if (LIKELY(!RUBY_VM_INTERRUPTED_ANY(ec))) return FALSE;
}
else {
th->pending_interrupt_queue_checked = 0;
-
- RUBY_VM_SET_INTERRUPT(th);
+ RUBY_VM_SET_INTERRUPT(ec);
}
- rb_threadptr_execute_interrupts(th, 1);
+ return rb_threadptr_execute_interrupts(th, 1);
}
static int
-vm_living_thread_num(rb_vm_t *vm)
+vm_living_thread_num(const rb_vm_t *vm)
+{
+ return vm->living_thread_num;
+}
+
+/*
+ * poll() is supported by many OSes, but so far Linux is the only
+ * one we know of that supports using poll() in all places select()
+ * would work.
+ */
+#if defined(HAVE_POLL)
+# if defined(__linux__)
+# define USE_POLL
+# endif
+# if defined(__FreeBSD_version) && __FreeBSD_version >= 1100000
+# define USE_POLL
+ /* FreeBSD does not set POLLOUT when POLLHUP happens */
+# define POLLERR_SET (POLLHUP | POLLERR)
+# endif
+#endif
+
+static void
+timeout_prepare(rb_hrtime_t **to, rb_hrtime_t *rel, rb_hrtime_t *end,
+ const struct timeval *timeout)
{
- return (int)vm->living_thread_num;
+ if (timeout) {
+ *rel = rb_timeval2hrtime(timeout);
+ *end = rb_hrtime_add(rb_hrtime_now(), *rel);
+ *to = rel;
+ }
+ else {
+ *to = 0;
+ }
}
#if THREAD_DEBUG
#ifdef HAVE_VA_ARGS_MACRO
void rb_thread_debug(const char *file, int line, const char *fmt, ...);
-#define thread_debug(fmt, ...) rb_thread_debug(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
+#define thread_debug(...) rb_thread_debug(__FILE__, __LINE__, __VA_ARGS__)
#define POSITION_FORMAT "%s:%d:"
#define POSITION_ARGS ,file, line
#else
@@ -198,8 +255,9 @@ void rb_thread_debug(const char *fmt, ...);
#endif
# ifdef NON_SCALAR_THREAD_ID
-static const char *
-fill_thread_id_string(rb_nativethread_id_t thid, rb_thread_id_string_t buf)
+#define fill_thread_id_string ruby_fill_thread_id_string
+const char *
+ruby_fill_thread_id_string(rb_nativethread_id_t thid, rb_thread_id_string_t buf)
{
extern const char ruby_digitmap[];
size_t i;
@@ -263,9 +321,9 @@ rb_thread_s_debug_set(VALUE self, VALUE val)
#endif
#ifndef fill_thread_id_str
-# define fill_thread_id_string(thid, buf) (thid)
+# define fill_thread_id_string(thid, buf) ((void *)(uintptr_t)(thid))
# define fill_thread_id_str(th) (void)0
-# define thread_id_str(th) ((void *)(th)->thread_id)
+# define thread_id_str(th) ((void *)(uintptr_t)(th)->thread_id)
# define PRI_THREAD_ID "p"
#endif
@@ -274,7 +332,14 @@ rb_thread_s_debug_set(VALUE self, VALUE val)
#endif
NOINLINE(static int thread_start_func_2(rb_thread_t *th, VALUE *stack_start,
VALUE *register_stack_start));
-static void timer_thread_function(void *);
+static void timer_thread_function(void);
+void ruby_sigchld_handler(rb_vm_t *); /* signal.c */
+
+static void
+ubf_sigwait(void *ignore)
+{
+ rb_thread_wakeup_timer_thread(0);
+}
#if defined(_WIN32)
#include "thread_win32.c"
@@ -299,6 +364,19 @@ static void timer_thread_function(void *);
#error "unsupported thread type"
#endif
+/*
+ * TODO: somebody with win32 knowledge should be able to get rid of
+ * timer-thread by busy-waiting on signals. And it should be possible
+ * to make the GVL in thread_pthread.c be platform-independent.
+ */
+#ifndef BUSY_WAIT_SIGNALS
+# define BUSY_WAIT_SIGNALS (0)
+#endif
+
+#ifndef USE_EVENTFD
+# define USE_EVENTFD (0)
+#endif
+
#if THREAD_DEBUG
static int debug_mutex_initialized = 1;
static rb_nativethread_lock_t debug_mutex;
@@ -320,7 +398,7 @@ rb_thread_debug(
if (debug_mutex_initialized == 1) {
debug_mutex_initialized = 0;
- native_mutex_initialize(&debug_mutex);
+ rb_native_mutex_initialize(&debug_mutex);
}
va_start(args, fmt);
@@ -338,83 +416,88 @@ rb_vm_gvl_destroy(rb_vm_t *vm)
{
gvl_release(vm);
gvl_destroy(vm);
- native_mutex_destroy(&vm->thread_destruct_lock);
+ if (0) {
+ /* may be held by running threads */
+ rb_native_mutex_destroy(&vm->waitpid_lock);
+ rb_native_mutex_destroy(&vm->workqueue_lock);
+ }
}
void
rb_nativethread_lock_initialize(rb_nativethread_lock_t *lock)
{
- native_mutex_initialize(lock);
+ rb_native_mutex_initialize(lock);
}
void
rb_nativethread_lock_destroy(rb_nativethread_lock_t *lock)
{
- native_mutex_destroy(lock);
+ rb_native_mutex_destroy(lock);
}
void
rb_nativethread_lock_lock(rb_nativethread_lock_t *lock)
{
- native_mutex_lock(lock);
+ rb_native_mutex_lock(lock);
}
void
rb_nativethread_lock_unlock(rb_nativethread_lock_t *lock)
{
- native_mutex_unlock(lock);
+ rb_native_mutex_unlock(lock);
}
static int
-set_unblock_function(rb_thread_t *th, rb_unblock_function_t *func, void *arg,
- struct rb_unblock_callback *old, int fail_if_interrupted)
+unblock_function_set(rb_thread_t *th, rb_unblock_function_t *func, void *arg, int fail_if_interrupted)
{
do {
if (fail_if_interrupted) {
- if (RUBY_VM_INTERRUPTED_ANY(th)) {
+ if (RUBY_VM_INTERRUPTED_ANY(th->ec)) {
return FALSE;
}
}
else {
- RUBY_VM_CHECK_INTS(th);
+ RUBY_VM_CHECK_INTS(th->ec);
}
- native_mutex_lock(&th->interrupt_lock);
- } while (RUBY_VM_INTERRUPTED_ANY(th) &&
- (native_mutex_unlock(&th->interrupt_lock), TRUE));
+ rb_native_mutex_lock(&th->interrupt_lock);
+ } while (!th->ec->raised_flag && RUBY_VM_INTERRUPTED_ANY(th->ec) &&
+ (rb_native_mutex_unlock(&th->interrupt_lock), TRUE));
+
+ VM_ASSERT(th->unblock.func == NULL);
- if (old) *old = th->unblock;
th->unblock.func = func;
th->unblock.arg = arg;
- native_mutex_unlock(&th->interrupt_lock);
+ rb_native_mutex_unlock(&th->interrupt_lock);
return TRUE;
}
static void
-reset_unblock_function(rb_thread_t *th, const struct rb_unblock_callback *old)
+unblock_function_clear(rb_thread_t *th)
{
- native_mutex_lock(&th->interrupt_lock);
- th->unblock = *old;
- native_mutex_unlock(&th->interrupt_lock);
+ rb_native_mutex_lock(&th->interrupt_lock);
+ th->unblock.func = NULL;
+ rb_native_mutex_unlock(&th->interrupt_lock);
}
static void
rb_threadptr_interrupt_common(rb_thread_t *th, int trap)
{
- native_mutex_lock(&th->interrupt_lock);
- if (trap)
- RUBY_VM_SET_TRAP_INTERRUPT(th);
- else
- RUBY_VM_SET_INTERRUPT(th);
- if (th->unblock.func) {
+ rb_native_mutex_lock(&th->interrupt_lock);
+ if (trap) {
+ RUBY_VM_SET_TRAP_INTERRUPT(th->ec);
+ }
+ else {
+ RUBY_VM_SET_INTERRUPT(th->ec);
+ }
+ if (th->unblock.func != NULL) {
(th->unblock.func)(th->unblock.arg);
}
else {
/* none */
}
- native_cond_signal(&th->interrupt_cond);
- native_mutex_unlock(&th->interrupt_lock);
+ rb_native_mutex_unlock(&th->interrupt_lock);
}
void
@@ -423,8 +506,8 @@ rb_threadptr_interrupt(rb_thread_t *th)
rb_threadptr_interrupt_common(th, 0);
}
-void
-rb_threadptr_trap_interrupt(rb_thread_t *th)
+static void
+threadptr_trap_interrupt(rb_thread_t *th)
{
rb_threadptr_interrupt_common(th, 1);
}
@@ -436,12 +519,15 @@ terminate_all(rb_vm_t *vm, const rb_thread_t *main_thread)
list_for_each(&vm->living_threads, th, vmlt_node) {
if (th != main_thread) {
- thread_debug("terminate_i: %p\n", (void *)th);
+ thread_debug("terminate_all: begin (thid: %"PRI_THREAD_ID", status: %s)\n",
+ thread_id_str(th), thread_status_name(th, TRUE));
rb_threadptr_pending_interrupt_enque(th, eTerminateSignal);
rb_threadptr_interrupt(th);
+ thread_debug("terminate_all: end (thid: %"PRI_THREAD_ID", status: %s)\n",
+ thread_id_str(th), thread_status_name(th, TRUE));
}
else {
- thread_debug("terminate_i: main thread (%p)\n", (void *)th);
+ thread_debug("terminate_all: main thread (%p)\n", (void *)th);
}
}
}
@@ -456,7 +542,7 @@ rb_threadptr_unlock_all_locking_mutexes(rb_thread_t *th)
while (mutexes) {
mutex = mutexes;
/* rb_warn("mutex #<%p> remains to be locked by terminated thread",
- mutexes); */
+ (void *)mutexes); */
mutexes = mutex->next_mutex;
err = rb_mutex_unlock_th(mutex, th);
if (err) rb_bug("invalid keeping_mutexes: %s", err);
@@ -466,8 +552,9 @@ rb_threadptr_unlock_all_locking_mutexes(rb_thread_t *th)
void
rb_thread_terminate_all(void)
{
- rb_thread_t *th = GET_THREAD(); /* main thread */
- rb_vm_t *vm = th->vm;
+ rb_thread_t *volatile th = GET_THREAD(); /* main thread */
+ rb_execution_context_t * volatile ec = th->ec;
+ rb_vm_t *volatile vm = th->vm;
volatile int sleeping = 0;
if (vm->main_thread != th) {
@@ -478,20 +565,21 @@ rb_thread_terminate_all(void)
/* unlock all locking mutexes */
rb_threadptr_unlock_all_locking_mutexes(th);
- TH_PUSH_TAG(th);
- if (TH_EXEC_TAG() == 0) {
+ EC_PUSH_TAG(ec);
+ if (EC_EXEC_TAG() == TAG_NONE) {
retry:
thread_debug("rb_thread_terminate_all (main thread: %p)\n", (void *)th);
terminate_all(vm, th);
while (vm_living_thread_num(vm) > 1) {
+ rb_hrtime_t rel = RB_HRTIME_PER_SEC;
/*
* Thread exiting routine in thread_start_func_2 notify
* me when the last sub-thread exit.
*/
sleeping = 1;
- native_sleep(th, 0);
- RUBY_VM_CHECK_INTS_BLOCKING(th);
+ native_sleep(th, &rel);
+ RUBY_VM_CHECK_INTS_BLOCKING(ec);
sleeping = 0;
}
}
@@ -506,7 +594,7 @@ rb_thread_terminate_all(void)
goto retry;
}
}
- TH_POP_TAG();
+ EC_POP_TAG();
}
static void
@@ -514,9 +602,9 @@ thread_cleanup_func_before_exec(void *th_ptr)
{
rb_thread_t *th = th_ptr;
th->status = THREAD_KILLED;
- th->machine.stack_start = th->machine.stack_end = 0;
+ th->ec->machine.stack_start = th->ec->machine.stack_end = NULL;
#ifdef __ia64
- th->machine.register_stack_start = th->machine.register_stack_end = 0;
+ th->ec->machine.register_stack_start = th->ec->machine.register_stack_end = NULL;
#endif
}
@@ -532,16 +620,20 @@ thread_cleanup_func(void *th_ptr, int atfork)
* Unfortunately, we can't release native threading resource at fork
* because libc may have unstable locking state therefore touching
* a threading resource may cause a deadlock.
+ *
+ * FIXME: Skipping native_mutex_destroy(pthread_mutex_destroy) is safe
+ * with NPTL, but native_thread_destroy calls pthread_cond_destroy
+ * which calls free(3), so there is a small memory leak atfork, here.
*/
if (atfork)
return;
- native_mutex_destroy(&th->interrupt_lock);
+ rb_native_mutex_destroy(&th->interrupt_lock);
native_thread_destroy(th);
}
static VALUE rb_threadptr_raise(rb_thread_t *, int, VALUE *);
-static VALUE rb_thread_inspect(VALUE thread);
+static VALUE rb_thread_to_s(VALUE thread);
void
ruby_thread_init_stack(rb_thread_t *th)
@@ -549,29 +641,77 @@ ruby_thread_init_stack(rb_thread_t *th)
native_thread_init_stack(th);
}
+const VALUE *
+rb_vm_proc_local_ep(VALUE proc)
+{
+ const VALUE *ep = vm_proc_ep(proc);
+
+ if (ep) {
+ return rb_vm_ep_local_ep(ep);
+ }
+ else {
+ return NULL;
+ }
+}
+
+static void
+thread_do_start(rb_thread_t *th)
+{
+ native_set_thread_name(th);
+
+ if (th->invoke_type == thread_invoke_type_proc) {
+ VALUE args = th->invoke_arg.proc.args;
+ long args_len = RARRAY_LEN(args);
+ const VALUE *args_ptr;
+ VALUE procval = th->invoke_arg.proc.proc;
+ rb_proc_t *proc;
+ GetProcPtr(procval, proc);
+
+ th->ec->errinfo = Qnil;
+ th->ec->root_lep = rb_vm_proc_local_ep(procval);
+ th->ec->root_svar = Qfalse;
+
+ EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_BEGIN, th->self, 0, 0, 0, Qundef);
+
+ if (args_len < 8) {
+ /* free proc.args if the length is enough small */
+ args_ptr = ALLOCA_N(VALUE, args_len);
+ MEMCPY((VALUE *)args_ptr, RARRAY_CONST_PTR_TRANSIENT(args), VALUE, args_len);
+ th->invoke_arg.proc.args = Qnil;
+ }
+ else {
+ args_ptr = RARRAY_CONST_PTR(args);
+ }
+
+ th->value = rb_vm_invoke_proc(th->ec, proc,
+ (int)args_len, args_ptr,
+ VM_BLOCK_HANDLER_NONE);
+
+ EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_END, th->self, 0, 0, 0, Qundef);
+ }
+ else {
+ th->value = (*th->invoke_arg.func.func)(th->invoke_arg.func.arg);
+ }
+}
+
+void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec);
+
static int
thread_start_func_2(rb_thread_t *th, VALUE *stack_start, VALUE *register_stack_start)
{
- int state;
- VALUE args = th->first_args;
- rb_proc_t *proc;
+ enum ruby_tag_type state;
rb_thread_list_t *join_list;
rb_thread_t *main_th;
VALUE errinfo = Qnil;
-# ifdef USE_SIGALTSTACK
- void rb_register_sigaltstack(rb_thread_t *th);
-
- rb_register_sigaltstack(th);
-# endif
if (th == th->vm->main_thread)
rb_bug("thread_start_func_2 must not be used for main thread");
ruby_thread_set_native(th);
- th->machine.stack_start = stack_start;
+ th->ec->machine.stack_start = stack_start;
#ifdef __ia64
- th->machine.register_stack_start = register_stack_start;
+ th->ec->machine.register_stack_start = register_stack_start;
#endif
thread_debug("thread start: %p\n", (void *)th);
@@ -580,47 +720,32 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start, VALUE *register_stack_s
thread_debug("thread start (get lock): %p\n", (void *)th);
rb_thread_set_current(th);
- TH_PUSH_TAG(th);
- if ((state = EXEC_TAG()) == 0) {
- SAVE_ROOT_JMPBUF(th, {
- native_set_thread_name(th);
- if (!th->first_func) {
- GetProcPtr(th->first_proc, proc);
- th->errinfo = Qnil;
- th->root_lep = rb_vm_ep_local_ep(vm_proc_ep(th->first_proc));
- th->root_svar = Qfalse;
- EXEC_EVENT_HOOK(th, RUBY_EVENT_THREAD_BEGIN, th->self, 0, 0, 0, Qundef);
- th->value = rb_vm_invoke_proc(th, proc,
- (int)RARRAY_LEN(args), RARRAY_CONST_PTR(args),
- VM_BLOCK_HANDLER_NONE);
- EXEC_EVENT_HOOK(th, RUBY_EVENT_THREAD_END, th->self, 0, 0, 0, Qundef);
- }
- else {
- th->value = (*th->first_func)((void *)args);
- }
- });
+ EC_PUSH_TAG(th->ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ SAVE_ROOT_JMPBUF(th, thread_do_start(th));
}
else {
- errinfo = th->errinfo;
+ errinfo = th->ec->errinfo;
if (state == TAG_FATAL) {
/* fatal error within this thread, need to stop whole script */
}
else if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
/* exit on main_thread. */
}
- else if (th->vm->thread_abort_on_exception ||
- th->abort_on_exception || RTEST(ruby_debug)) {
- /* exit on main_thread */
- }
- else if (th->report_on_exception) {
- VALUE mesg = rb_thread_inspect(th->self);
- rb_str_cat_cstr(mesg, " terminated with exception:\n");
- rb_write_error_str(mesg);
- rb_threadptr_error_print(th, errinfo);
- errinfo = Qnil;
- }
else {
- errinfo = Qnil;
+ if (th->report_on_exception) {
+ VALUE mesg = rb_thread_to_s(th->self);
+ rb_str_cat_cstr(mesg, " terminated with exception (report_on_exception is true):\n");
+ rb_write_error_str(mesg);
+ rb_ec_error_print(th->ec, errinfo);
+ }
+ if (th->vm->thread_abort_on_exception ||
+ th->abort_on_exception || RTEST(ruby_debug)) {
+ /* exit on main_thread */
+ }
+ else {
+ errinfo = Qnil;
+ }
}
th->value = Qnil;
}
@@ -636,7 +761,9 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start, VALUE *register_stack_s
/* treat with normal error object */
rb_threadptr_raise(main_th, 1, &errinfo);
}
- TH_POP_TAG();
+ EC_POP_TAG();
+
+ rb_ec_clear_current_thread_trace_func(th->ec);
/* locking_mutex must be Qfalse */
if (th->locking_mutex != Qfalse) {
@@ -666,15 +793,8 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start, VALUE *register_stack_s
rb_threadptr_unlock_all_locking_mutexes(th);
rb_check_deadlock(th->vm);
- if (!th->root_fiber) {
- rb_thread_recycle_stack_release(th->stack);
- th->stack = 0;
- }
+ rb_fiber_close(th->ec->fiber_ptr);
}
- native_mutex_lock(&th->vm->thread_destruct_lock);
- /* make sure vm->running_thread never point me after this point.*/
- th->vm->running_thread = NULL;
- native_mutex_unlock(&th->vm->thread_destruct_lock);
thread_cleanup_func(th, FALSE);
gvl_release(th->vm);
@@ -684,19 +804,24 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start, VALUE *register_stack_s
static VALUE
thread_create_core(VALUE thval, VALUE args, VALUE (*fn)(ANYARGS))
{
- rb_thread_t *th, *current_th = GET_THREAD();
+ rb_thread_t *th = rb_thread_ptr(thval), *current_th = GET_THREAD();
int err;
if (OBJ_FROZEN(current_th->thgroup)) {
rb_raise(rb_eThreadError,
"can't start a new thread (frozen ThreadGroup)");
}
- GetThreadPtr(thval, th);
- /* setup thread environment */
- th->first_func = fn;
- th->first_proc = fn ? Qfalse : rb_block_proc();
- th->first_args = args; /* GC: shouldn't put before above line */
+ if (fn) {
+ th->invoke_type = thread_invoke_type_func;
+ th->invoke_arg.func.func = fn;
+ th->invoke_arg.func.arg = (void *)args;
+ }
+ else {
+ th->invoke_type = thread_invoke_type_proc;
+ th->invoke_arg.proc.proc = rb_block_proc();
+ th->invoke_arg.proc.args = args;
+ }
th->priority = current_th->priority;
th->thgroup = current_th->thgroup;
@@ -706,11 +831,7 @@ thread_create_core(VALUE thval, VALUE args, VALUE (*fn)(ANYARGS))
th->pending_interrupt_mask_stack = rb_ary_dup(current_th->pending_interrupt_mask_stack);
RBASIC_CLEAR_CLASS(th->pending_interrupt_mask_stack);
- th->interrupt_mask = 0;
-
- native_mutex_initialize(&th->interrupt_lock);
- native_cond_initialize(&th->interrupt_cond, RB_CONDATTR_CLOCK_MONOTONIC);
- th->report_on_exception = th->vm->thread_report_on_exception;
+ rb_native_mutex_initialize(&th->interrupt_lock);
/* kick thread */
err = native_thread_create(th);
@@ -722,7 +843,7 @@ thread_create_core(VALUE thval, VALUE args, VALUE (*fn)(ANYARGS))
return thval;
}
-#define threadptr_initialized(th) ((th)->first_args != 0)
+#define threadptr_initialized(th) ((th)->invoke_type != thread_invoke_type_none)
/*
* call-seq:
@@ -754,7 +875,7 @@ thread_s_new(int argc, VALUE *argv, VALUE klass)
rb_raise(rb_eThreadError, "can't alloc thread");
rb_obj_call_init(thread, argc, argv);
- GetThreadPtr(thread, th);
+ th = rb_thread_ptr(thread);
if (!threadptr_initialized(th)) {
rb_raise(rb_eThreadError, "uninitialized thread - check `%"PRIsVALUE"#initialize'",
klass);
@@ -778,31 +899,40 @@ thread_start(VALUE klass, VALUE args)
return thread_create_core(rb_thread_alloc(klass), args, 0);
}
+static VALUE
+threadptr_invoke_proc_location(rb_thread_t *th)
+{
+ if (th->invoke_type == thread_invoke_type_proc) {
+ return rb_proc_location(th->invoke_arg.proc.proc);
+ }
+ else {
+ return Qnil;
+ }
+}
+
/* :nodoc: */
static VALUE
thread_initialize(VALUE thread, VALUE args)
{
- rb_thread_t *th;
+ rb_thread_t *th = rb_thread_ptr(thread);
+
if (!rb_block_given_p()) {
- rb_raise(rb_eThreadError, "must be called with a block");
- }
- GetThreadPtr(thread, th);
- if (th->first_args) {
- VALUE proc = th->first_proc, line, loc;
- VALUE file;
- if (!proc || !RTEST(loc = rb_proc_location(proc))) {
+ rb_raise(rb_eThreadError, "must be called with a block");
+ }
+ else if (th->invoke_type != thread_invoke_type_none) {
+ VALUE loc = threadptr_invoke_proc_location(th);
+ if (!NIL_P(loc)) {
+ rb_raise(rb_eThreadError,
+ "already initialized thread - %"PRIsVALUE":%"PRIsVALUE,
+ RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
+ }
+ else {
rb_raise(rb_eThreadError, "already initialized thread");
}
- file = RARRAY_AREF(loc, 0);
- if (NIL_P(line = RARRAY_AREF(loc, 1))) {
- rb_raise(rb_eThreadError,
- "already initialized thread - %"PRIsVALUE, file);
- }
- rb_raise(rb_eThreadError,
- "already initialized thread - %"PRIsVALUE":%"PRIsVALUE,
- file, line);
}
- return thread_create_core(thread, args, 0);
+ else {
+ return thread_create_core(thread, args, NULL);
+ }
}
VALUE
@@ -812,12 +942,9 @@ rb_thread_create(VALUE (*fn)(ANYARGS), void *arg)
}
-/* +infty, for this purpose */
-#define DELAY_INFTY 1E30
-
struct join_arg {
rb_thread_t *target, *waiting;
- double delay;
+ rb_hrtime_t *limit;
};
static VALUE
@@ -846,30 +973,39 @@ thread_join_sleep(VALUE arg)
{
struct join_arg *p = (struct join_arg *)arg;
rb_thread_t *target_th = p->target, *th = p->waiting;
- const int forever = p->delay == DELAY_INFTY;
- const double limit = forever ? 0 : timeofday() + p->delay;
+ rb_hrtime_t end = 0;
+
+ if (p->limit) {
+ end = rb_hrtime_add(*p->limit, rb_hrtime_now());
+ }
while (target_th->status != THREAD_KILLED) {
- if (forever) {
- sleep_forever(th, 1, 0);
+ if (!p->limit) {
+ th->status = THREAD_STOPPED_FOREVER;
+ th->vm->sleeper++;
+ rb_check_deadlock(th->vm);
+ native_sleep(th, 0);
+ th->vm->sleeper--;
}
else {
- double now = timeofday();
- if (now > limit) {
+ if (hrtime_update_expire(p->limit, end)) {
thread_debug("thread_join: timeout (thid: %"PRI_THREAD_ID")\n",
thread_id_str(target_th));
return Qfalse;
}
- sleep_wait_for_interrupt(th, limit - now, 0);
+ th->status = THREAD_STOPPED;
+ native_sleep(th, p->limit);
}
- thread_debug("thread_join: interrupted (thid: %"PRI_THREAD_ID")\n",
- thread_id_str(target_th));
+ RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
+ th->status = THREAD_RUNNABLE;
+ thread_debug("thread_join: interrupted (thid: %"PRI_THREAD_ID", status: %s)\n",
+ thread_id_str(target_th), thread_status_name(target_th, TRUE));
}
return Qtrue;
}
static VALUE
-thread_join(rb_thread_t *target_th, double delay)
+thread_join(rb_thread_t *target_th, rb_hrtime_t *rel)
{
rb_thread_t *th = GET_THREAD();
struct join_arg arg;
@@ -883,9 +1019,10 @@ thread_join(rb_thread_t *target_th, double delay)
arg.target = target_th;
arg.waiting = th;
- arg.delay = delay;
+ arg.limit = rel;
- thread_debug("thread_join (thid: %"PRI_THREAD_ID")\n", thread_id_str(target_th));
+ thread_debug("thread_join (thid: %"PRI_THREAD_ID", status: %s)\n",
+ thread_id_str(target_th), thread_status_name(target_th, TRUE));
if (target_th->status != THREAD_KILLED) {
rb_thread_list_t list;
@@ -898,22 +1035,25 @@ thread_join(rb_thread_t *target_th, double delay)
}
}
- thread_debug("thread_join: success (thid: %"PRI_THREAD_ID")\n",
- thread_id_str(target_th));
+ thread_debug("thread_join: success (thid: %"PRI_THREAD_ID", status: %s)\n",
+ thread_id_str(target_th), thread_status_name(target_th, TRUE));
- if (target_th->errinfo != Qnil) {
- VALUE err = target_th->errinfo;
+ if (target_th->ec->errinfo != Qnil) {
+ VALUE err = target_th->ec->errinfo;
if (FIXNUM_P(err)) {
switch (err) {
case INT2FIX(TAG_FATAL):
+ thread_debug("thread_join: terminated (thid: %"PRI_THREAD_ID", status: %s)\n",
+ thread_id_str(target_th), thread_status_name(target_th, TRUE));
+
/* OK. killed. */
break;
default:
rb_bug("thread_join: Fixnum (%d) should not reach here.", FIX2INT(err));
}
}
- else if (THROW_DATA_P(target_th->errinfo)) {
+ else if (THROW_DATA_P(target_th->ec->errinfo)) {
rb_bug("thread_join: THROW_DATA should not reach here.");
}
else {
@@ -924,6 +1064,8 @@ thread_join(rb_thread_t *target_th, double delay)
return target_th->self;
}
+static rb_hrtime_t *double2hrtime(rb_hrtime_t *, double);
+
/*
* call-seq:
* thr.join -> thr
@@ -966,18 +1108,25 @@ thread_join(rb_thread_t *target_th, double delay)
static VALUE
thread_join_m(int argc, VALUE *argv, VALUE self)
{
- rb_thread_t *target_th;
- double delay = DELAY_INFTY;
VALUE limit;
+ rb_hrtime_t rel, *to = 0;
- GetThreadPtr(self, target_th);
-
- rb_scan_args(argc, argv, "01", &limit);
- if (!NIL_P(limit)) {
- delay = rb_num2dbl(limit);
+ /*
+ * This supports INFINITY and negative values, so we can't use
+ * rb_time_interval right now...
+ */
+ if (!rb_check_arity(argc, 0, 1) || NIL_P(argv[0])) {
+ /* unlimited */
+ }
+ else if (FIXNUM_P(limit = argv[0])) {
+ rel = rb_sec2hrtime(NUM2TIMET(limit));
+ to = &rel;
+ }
+ else {
+ to = double2hrtime(&rel, rb_num2dbl(limit));
}
- return thread_join(target_th, delay);
+ return thread_join(rb_thread_ptr(self), to);
}
/*
@@ -997,9 +1146,8 @@ thread_join_m(int argc, VALUE *argv, VALUE self)
static VALUE
thread_value(VALUE self)
{
- rb_thread_t *th;
- GetThreadPtr(self, th);
- thread_join(th, DELAY_INFTY);
+ rb_thread_t *th = rb_thread_ptr(self);
+ thread_join(th, 0);
return th->value;
}
@@ -1008,131 +1156,128 @@ thread_value(VALUE self)
*/
/*
- * The type of tv_sec in struct timeval is time_t in POSIX.
- * But several systems violate POSIX.
- *
- * OpenBSD 5.2 (amd64):
- * time_t: int (signed 32bit integer)
- * tv_sec: long (signed 64bit integer)
- *
- * MinGW-w64 (x64):
- * time_t: long long (signed 64bit integer)
- * tv_sec: long (signed 32bit integer)
+ * Back when we used "struct timeval", not all platforms implemented
+ * tv_sec as time_t. Nowadays we use "struct timespec" and tv_sec
+ * seems to be implemented more consistently across platforms.
+ * At least other parts of our code hasn't had to deal with non-time_t
+ * tv_sec in timespec...
*/
+#define TIMESPEC_SEC_MAX TIMET_MAX
+#define TIMESPEC_SEC_MIN TIMET_MIN
-#if SIGNEDNESS_OF_TIME_T < 0 /* signed */
-# define TIMEVAL_SEC_MAX SIGNED_INTEGER_MAX(TYPEOF_TIMEVAL_TV_SEC)
-# define TIMEVAL_SEC_MIN SIGNED_INTEGER_MIN(TYPEOF_TIMEVAL_TV_SEC)
-#elif SIGNEDNESS_OF_TIME_T > 0 /* unsigned */
-# define TIMEVAL_SEC_MAX ((TYPEOF_TIMEVAL_TV_SEC)(~(unsigned_time_t)0))
-# define TIMEVAL_SEC_MIN ((TYPEOF_TIMEVAL_TV_SEC)0)
-#endif
-
-static struct timeval
-double2timeval(double d)
+static rb_hrtime_t *
+double2hrtime(rb_hrtime_t *hrt, double d)
{
- /* assume timeval.tv_sec has same signedness as time_t */
- const double TIMEVAL_SEC_MAX_PLUS_ONE = (2*(double)(TIMEVAL_SEC_MAX/2+1));
-
- struct timeval time;
+ /* assume timespec.tv_sec has same signedness as time_t */
+ const double TIMESPEC_SEC_MAX_PLUS_ONE = TIMET_MAX_PLUS_ONE;
- if (TIMEVAL_SEC_MAX_PLUS_ONE <= d) {
- time.tv_sec = TIMEVAL_SEC_MAX;
- time.tv_usec = 999999;
+ if (TIMESPEC_SEC_MAX_PLUS_ONE <= d) {
+ return NULL;
}
- else if (d <= TIMEVAL_SEC_MIN) {
- time.tv_sec = TIMEVAL_SEC_MIN;
- time.tv_usec = 0;
+ else if (d <= 0) {
+ *hrt = 0;
}
else {
- time.tv_sec = (TYPEOF_TIMEVAL_TV_SEC)d;
- time.tv_usec = (int)((d - (time_t)d) * 1e6);
- if (time.tv_usec < 0) {
- time.tv_usec += (int)1e6;
- time.tv_sec -= 1;
- }
+ *hrt = (rb_hrtime_t)(d * (double)RB_HRTIME_PER_SEC);
}
- return time;
+ return hrt;
+}
+
+static void
+getclockofday(struct timespec *ts)
+{
+#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
+ if (clock_gettime(CLOCK_MONOTONIC, ts) == 0)
+ return;
+#endif
+ rb_timespec_now(ts);
+}
+
+/*
+ * Don't inline this, since library call is already time consuming
+ * and we don't want "struct timespec" on stack too long for GC
+ */
+NOINLINE(rb_hrtime_t rb_hrtime_now(void));
+rb_hrtime_t
+rb_hrtime_now(void)
+{
+ struct timespec ts;
+
+ getclockofday(&ts);
+ return rb_timespec2hrtime(&ts);
}
static void
-sleep_forever(rb_thread_t *th, int deadlockable, int spurious_check)
+sleep_forever(rb_thread_t *th, unsigned int fl)
{
enum rb_thread_status prev_status = th->status;
- enum rb_thread_status status = deadlockable ? THREAD_STOPPED_FOREVER : THREAD_STOPPED;
+ enum rb_thread_status status;
+ int woke;
+ status = fl & SLEEP_DEADLOCKABLE ? THREAD_STOPPED_FOREVER : THREAD_STOPPED;
th->status = status;
- RUBY_VM_CHECK_INTS_BLOCKING(th);
+ RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
while (th->status == status) {
- if (deadlockable) {
+ if (fl & SLEEP_DEADLOCKABLE) {
th->vm->sleeper++;
rb_check_deadlock(th->vm);
}
native_sleep(th, 0);
- if (deadlockable) {
+ if (fl & SLEEP_DEADLOCKABLE) {
th->vm->sleeper--;
}
- RUBY_VM_CHECK_INTS_BLOCKING(th);
- if (!spurious_check)
+ woke = vm_check_ints_blocking(th->ec);
+ if (woke && !(fl & SLEEP_SPURIOUS_CHECK))
break;
}
th->status = prev_status;
}
-static void
-getclockofday(struct timeval *tp)
+/*
+ * at least gcc 7.2 and 7.3 complains about "rb_hrtime_t end"
+ * being uninitialized, maybe other versions, too.
+ */
+COMPILER_WARNING_PUSH
+#if defined(__GNUC__) && __GNUC__ == 7 && __GNUC_MINOR__ <= 3
+COMPILER_WARNING_IGNORED(-Wmaybe-uninitialized)
+#endif
+#ifndef PRIu64
+#define PRIu64 PRI_64_PREFIX "u"
+#endif
+/*
+ * @end is the absolute time when @ts is set to expire
+ * Returns true if @end has past
+ * Updates @ts and returns false otherwise
+ */
+static int
+hrtime_update_expire(rb_hrtime_t *timeout, const rb_hrtime_t end)
{
-#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
- struct timespec ts;
+ rb_hrtime_t now = rb_hrtime_now();
- if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
- tp->tv_sec = ts.tv_sec;
- tp->tv_usec = (int)(ts.tv_nsec / 1000);
- }
- else
-#endif
- {
- gettimeofday(tp, NULL);
- }
+ if (now > end) return 1;
+ thread_debug("hrtime_update_expire: "
+ "%"PRIu64" > %"PRIu64"\n",
+ (uint64_t)end, (uint64_t)now);
+ *timeout = end - now;
+ return 0;
}
+COMPILER_WARNING_POP
static void
-sleep_timeval(rb_thread_t *th, struct timeval tv, int spurious_check)
+sleep_hrtime(rb_thread_t *th, rb_hrtime_t rel, unsigned int fl)
{
- struct timeval to, tvn;
enum rb_thread_status prev_status = th->status;
-
- getclockofday(&to);
- if (TIMEVAL_SEC_MAX - tv.tv_sec < to.tv_sec)
- to.tv_sec = TIMEVAL_SEC_MAX;
- else
- to.tv_sec += tv.tv_sec;
- if ((to.tv_usec += tv.tv_usec) >= 1000000) {
- if (to.tv_sec == TIMEVAL_SEC_MAX)
- to.tv_usec = 999999;
- else {
- to.tv_sec++;
- to.tv_usec -= 1000000;
- }
- }
+ int woke;
+ rb_hrtime_t end = rb_hrtime_add(rb_hrtime_now(), rel);
th->status = THREAD_STOPPED;
- RUBY_VM_CHECK_INTS_BLOCKING(th);
+ RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
while (th->status == THREAD_STOPPED) {
- native_sleep(th, &tv);
- RUBY_VM_CHECK_INTS_BLOCKING(th);
- getclockofday(&tvn);
- if (to.tv_sec < tvn.tv_sec) break;
- if (to.tv_sec == tvn.tv_sec && to.tv_usec <= tvn.tv_usec) break;
- thread_debug("sleep_timeval: %"PRI_TIMET_PREFIX"d.%.6ld > %"PRI_TIMET_PREFIX"d.%.6ld\n",
- (time_t)to.tv_sec, (long)to.tv_usec,
- (time_t)tvn.tv_sec, (long)tvn.tv_usec);
- tv.tv_sec = to.tv_sec - tvn.tv_sec;
- if ((tv.tv_usec = to.tv_usec - tvn.tv_usec) < 0) {
- --tv.tv_sec;
- tv.tv_usec += 1000000;
- }
- if (!spurious_check)
+ native_sleep(th, &rel);
+ woke = vm_check_ints_blocking(th->ec);
+ if (woke && !(fl & SLEEP_SPURIOUS_CHECK))
+ break;
+ if (hrtime_update_expire(&rel, end))
break;
}
th->status = prev_status;
@@ -1142,45 +1287,41 @@ void
rb_thread_sleep_forever(void)
{
thread_debug("rb_thread_sleep_forever\n");
- sleep_forever(GET_THREAD(), 0, 1);
+ sleep_forever(GET_THREAD(), SLEEP_SPURIOUS_CHECK);
}
void
rb_thread_sleep_deadly(void)
{
thread_debug("rb_thread_sleep_deadly\n");
- sleep_forever(GET_THREAD(), 1, 1);
+ sleep_forever(GET_THREAD(), SLEEP_DEADLOCKABLE|SLEEP_SPURIOUS_CHECK);
}
-static double
-timeofday(void)
+void
+rb_thread_sleep_interruptible(void)
{
-#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
- struct timespec tp;
+ rb_thread_t *th = GET_THREAD();
+ enum rb_thread_status prev_status = th->status;
- if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
- return (double)tp.tv_sec + (double)tp.tv_nsec * 1e-9;
- }
- else
-#endif
- {
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return (double)tv.tv_sec + (double)tv.tv_usec * 1e-6;
- }
+ th->status = THREAD_STOPPED;
+ native_sleep(th, 0);
+ RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
+ th->status = prev_status;
}
static void
-sleep_wait_for_interrupt(rb_thread_t *th, double sleepsec, int spurious_check)
+rb_thread_sleep_deadly_allow_spurious_wakeup(void)
{
- sleep_timeval(th, double2timeval(sleepsec), spurious_check);
+ thread_debug("rb_thread_sleep_deadly_allow_spurious_wakeup\n");
+ sleep_forever(GET_THREAD(), SLEEP_DEADLOCKABLE);
}
void
rb_thread_wait_for(struct timeval time)
{
rb_thread_t *th = GET_THREAD();
- sleep_timeval(th, time, 1);
+
+ sleep_hrtime(th, rb_timeval2hrtime(&time), SLEEP_SPURIOUS_CHECK);
}
/*
@@ -1193,8 +1334,7 @@ rb_thread_wait_for(struct timeval time)
void
rb_thread_check_ints(void)
{
- rb_thread_t *th = GET_THREAD();
- RUBY_VM_CHECK_INTS_BLOCKING(th);
+ RUBY_VM_CHECK_INTS_BLOCKING(GET_EC());
}
/*
@@ -1211,9 +1351,7 @@ rb_thread_check_trap_pending(void)
int
rb_thread_interrupted(VALUE thval)
{
- rb_thread_t *th;
- GetThreadPtr(thval, th);
- return (int)RUBY_VM_INTERRUPTED(th);
+ return (int)RUBY_VM_INTERRUPTED(rb_thread_ptr(thval)->ec);
}
void
@@ -1223,7 +1361,7 @@ rb_thread_sleep(int sec)
}
static void
-rb_thread_schedule_limits(unsigned long limits_us)
+rb_thread_schedule_limits(uint32_t limits_us)
{
thread_debug("rb_thread_schedule\n");
if (!rb_thread_alone()) {
@@ -1242,10 +1380,8 @@ rb_thread_schedule_limits(unsigned long limits_us)
void
rb_thread_schedule(void)
{
- rb_thread_t *cur_th = GET_THREAD();
rb_thread_schedule_limits(0);
-
- RUBY_VM_CHECK_INTS(cur_th);
+ RUBY_VM_CHECK_INTS(GET_EC());
}
/* blocking region */
@@ -1255,7 +1391,7 @@ blocking_region_begin(rb_thread_t *th, struct rb_blocking_region_buffer *region,
rb_unblock_function_t *ubf, void *arg, int fail_if_interrupted)
{
region->prev_status = th->status;
- if (set_unblock_function(th, ubf, arg, &region->oldubf, fail_if_interrupted)) {
+ if (unblock_function_set(th, ubf, arg, fail_if_interrupted)) {
th->blocking_region_buffer = region;
th->status = THREAD_STOPPED;
thread_debug("enter blocking region (%p)\n", (void *)th);
@@ -1271,12 +1407,15 @@ blocking_region_begin(rb_thread_t *th, struct rb_blocking_region_buffer *region,
static inline void
blocking_region_end(rb_thread_t *th, struct rb_blocking_region_buffer *region)
{
+ /* entry to ubf_list still permitted at this point, make it impossible: */
+ unblock_function_clear(th);
+ /* entry to ubf_list impossible at this point, so unregister is safe: */
+ unregister_ubf_list(th);
+
gvl_acquire(th->vm, th);
rb_thread_set_current(th);
thread_debug("leave blocking region (%p)\n", (void *)th);
- unregister_ubf_list(th);
th->blocking_region_buffer = 0;
- reset_unblock_function(th, &region->oldubf);
if (th->status == THREAD_STOPPED) {
th->status = region->prev_status;
}
@@ -1287,23 +1426,30 @@ call_without_gvl(void *(*func)(void *), void *data1,
rb_unblock_function_t *ubf, void *data2, int fail_if_interrupted)
{
void *val = 0;
-
- rb_thread_t *th = GET_THREAD();
+ rb_execution_context_t *ec = GET_EC();
+ rb_thread_t *th = rb_ec_thread_ptr(ec);
int saved_errno = 0;
+ VALUE ubf_th = Qfalse;
- th->waiting_fd = -1;
if (ubf == RUBY_UBF_IO || ubf == RUBY_UBF_PROCESS) {
ubf = ubf_select;
data2 = th;
}
+ else if (ubf && vm_living_thread_num(th->vm) == 1) {
+ ubf_th = rb_thread_start_unblock_thread();
+ }
- BLOCKING_REGION({
+ BLOCKING_REGION(th, {
val = func(data1);
saved_errno = errno;
}, ubf, data2, fail_if_interrupted);
if (!fail_if_interrupted) {
- RUBY_VM_CHECK_INTS_BLOCKING(th);
+ RUBY_VM_CHECK_INTS_BLOCKING(ec);
+ }
+
+ if (ubf_th != Qfalse) {
+ thread_value(rb_thread_kill(ubf_th));
}
errno = saved_errno;
@@ -1414,29 +1560,35 @@ VALUE
rb_thread_io_blocking_region(rb_blocking_function_t *func, void *data1, int fd)
{
volatile VALUE val = Qundef; /* shouldn't be used */
- rb_thread_t *th = GET_THREAD();
+ rb_execution_context_t * volatile ec = GET_EC();
volatile int saved_errno = 0;
- int state;
+ enum ruby_tag_type state;
+ struct waiting_fd wfd;
- th->waiting_fd = fd;
+ wfd.fd = fd;
+ wfd.th = rb_ec_thread_ptr(ec);
+ list_add(&rb_ec_vm_ptr(ec)->waiting_fds, &wfd.wfd_node);
- TH_PUSH_TAG(th);
- if ((state = EXEC_TAG()) == 0) {
- BLOCKING_REGION({
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ BLOCKING_REGION(wfd.th, {
val = func(data1);
saved_errno = errno;
- }, ubf_select, th, FALSE);
+ }, ubf_select, wfd.th, FALSE);
}
- TH_POP_TAG();
+ EC_POP_TAG();
- /* clear waiting_fd anytime */
- th->waiting_fd = -1;
+ /*
+ * must be deleted before jump
+ * this will delete either from waiting_fds or on-stack LIST_HEAD(busy)
+ */
+ list_del(&wfd.wfd_node);
if (state) {
- TH_JUMP_TAG(th, state);
+ EC_JUMP_TAG(ec, state);
}
/* TODO: check func() */
- RUBY_VM_CHECK_INTS_BLOCKING(th);
+ RUBY_VM_CHECK_INTS_BLOCKING(ec);
errno = saved_errno;
@@ -1593,19 +1745,23 @@ rb_threadptr_pending_interrupt_check_mask(rb_thread_t *th, VALUE err)
VALUE mask;
long mask_stack_len = RARRAY_LEN(th->pending_interrupt_mask_stack);
const VALUE *mask_stack = RARRAY_CONST_PTR(th->pending_interrupt_mask_stack);
- VALUE ancestors = rb_mod_ancestors(err); /* TODO: GC guard */
- long ancestors_len = RARRAY_LEN(ancestors);
- const VALUE *ancestors_ptr = RARRAY_CONST_PTR(ancestors);
- int i, j;
+ VALUE mod;
+ long i;
for (i=0; i<mask_stack_len; i++) {
mask = mask_stack[mask_stack_len-(i+1)];
- for (j=0; j<ancestors_len; j++) {
- VALUE klass = ancestors_ptr[j];
+ for (mod = err; mod; mod = RCLASS_SUPER(mod)) {
+ VALUE klass = mod;
VALUE sym;
- /* TODO: remove rb_intern() */
+ if (BUILTIN_TYPE(mod) == T_ICLASS) {
+ klass = RBASIC(mod)->klass;
+ }
+ else if (mod != RCLASS_ORIGIN(mod)) {
+ continue;
+ }
+
if ((sym = rb_hash_aref(mask, klass)) != Qnil) {
if (sym == sym_immediate) {
return INTERRUPT_IMMEDIATE;
@@ -1627,7 +1783,7 @@ rb_threadptr_pending_interrupt_check_mask(rb_thread_t *th, VALUE err)
}
static int
-rb_threadptr_pending_interrupt_empty_p(rb_thread_t *th)
+rb_threadptr_pending_interrupt_empty_p(const rb_thread_t *th)
{
return RARRAY_LEN(th->pending_interrupt_queue) == 0;
}
@@ -1682,8 +1838,8 @@ rb_threadptr_pending_interrupt_deque(rb_thread_t *th, enum handle_interrupt_timi
#endif
}
-int
-rb_threadptr_pending_interrupt_active_p(rb_thread_t *th)
+static int
+threadptr_pending_interrupt_active_p(rb_thread_t *th)
{
/*
* For optimization, we don't check async errinfo queue
@@ -1828,16 +1984,17 @@ static VALUE
rb_thread_s_handle_interrupt(VALUE self, VALUE mask_arg)
{
VALUE mask;
- rb_thread_t *th = GET_THREAD();
+ rb_execution_context_t * volatile ec = GET_EC();
+ rb_thread_t * volatile th = rb_ec_thread_ptr(ec);
volatile VALUE r = Qnil;
- int state;
+ enum ruby_tag_type state;
if (!rb_block_given_p()) {
rb_raise(rb_eArgError, "block is needed.");
}
mask = 0;
- mask_arg = rb_convert_type(mask_arg, T_HASH, "Hash", "to_hash");
+ mask_arg = rb_to_hash_type(mask_arg);
rb_hash_foreach(mask_arg, handle_interrupt_arg_check_i, (VALUE)&mask);
if (!mask) {
return rb_yield(Qnil);
@@ -1846,25 +2003,25 @@ rb_thread_s_handle_interrupt(VALUE self, VALUE mask_arg)
rb_ary_push(th->pending_interrupt_mask_stack, mask);
if (!rb_threadptr_pending_interrupt_empty_p(th)) {
th->pending_interrupt_queue_checked = 0;
- RUBY_VM_SET_INTERRUPT(th);
+ RUBY_VM_SET_INTERRUPT(th->ec);
}
- TH_PUSH_TAG(th);
- if ((state = EXEC_TAG()) == 0) {
+ EC_PUSH_TAG(th->ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
r = rb_yield(Qnil);
}
- TH_POP_TAG();
+ EC_POP_TAG();
rb_ary_pop(th->pending_interrupt_mask_stack);
if (!rb_threadptr_pending_interrupt_empty_p(th)) {
th->pending_interrupt_queue_checked = 0;
- RUBY_VM_SET_INTERRUPT(th);
+ RUBY_VM_SET_INTERRUPT(th->ec);
}
- RUBY_VM_CHECK_INTS(th);
+ RUBY_VM_CHECK_INTS(th->ec);
if (state) {
- TH_JUMP_TAG(th, state);
+ EC_JUMP_TAG(th->ec, state);
}
return r;
@@ -1883,9 +2040,7 @@ rb_thread_s_handle_interrupt(VALUE self, VALUE mask_arg)
static VALUE
rb_thread_pending_interrupt_p(int argc, VALUE *argv, VALUE target_thread)
{
- rb_thread_t *target_th;
-
- GetThreadPtr(target_thread, target_th);
+ rb_thread_t *target_th = rb_thread_ptr(target_thread);
if (!target_th->pending_interrupt_queue) {
return Qfalse;
@@ -1893,20 +2048,19 @@ rb_thread_pending_interrupt_p(int argc, VALUE *argv, VALUE target_thread)
if (rb_threadptr_pending_interrupt_empty_p(target_th)) {
return Qfalse;
}
+ if (rb_check_arity(argc, 0, 1)) {
+ VALUE err = argv[0];
+ if (!rb_obj_is_kind_of(err, rb_cModule)) {
+ rb_raise(rb_eTypeError, "class or module required for rescue clause");
+ }
+ if (rb_threadptr_pending_interrupt_include_p(target_th, err)) {
+ return Qtrue;
+ }
+ else {
+ return Qfalse;
+ }
+ }
else {
- if (argc == 1) {
- VALUE err;
- rb_scan_args(argc, argv, "01", &err);
- if (!rb_obj_is_kind_of(err, rb_cModule)) {
- rb_raise(rb_eTypeError, "class or module required for rescue clause");
- }
- if (rb_threadptr_pending_interrupt_include_p(target_th, err)) {
- return Qtrue;
- }
- else {
- return Qfalse;
- }
- }
return Qtrue;
}
}
@@ -1974,36 +2128,40 @@ rb_thread_s_pending_interrupt_p(int argc, VALUE *argv, VALUE self)
return rb_thread_pending_interrupt_p(argc, argv, GET_THREAD()->self);
}
+NORETURN(static void rb_threadptr_to_kill(rb_thread_t *th));
+
static void
rb_threadptr_to_kill(rb_thread_t *th)
{
rb_threadptr_pending_interrupt_clear(th);
th->status = THREAD_RUNNABLE;
th->to_kill = 1;
- th->errinfo = INT2FIX(TAG_FATAL);
- TH_JUMP_TAG(th, TAG_FATAL);
+ th->ec->errinfo = INT2FIX(TAG_FATAL);
+ EC_JUMP_TAG(th->ec, TAG_FATAL);
}
static inline rb_atomic_t
threadptr_get_interrupts(rb_thread_t *th)
{
+ rb_execution_context_t *ec = th->ec;
rb_atomic_t interrupt;
rb_atomic_t old;
do {
- interrupt = th->interrupt_flag;
- old = ATOMIC_CAS(th->interrupt_flag, interrupt, interrupt & th->interrupt_mask);
+ interrupt = ec->interrupt_flag;
+ old = ATOMIC_CAS(ec->interrupt_flag, interrupt, interrupt & ec->interrupt_mask);
} while (old != interrupt);
- return interrupt & (rb_atomic_t)~th->interrupt_mask;
+ return interrupt & (rb_atomic_t)~ec->interrupt_mask;
}
-void
+MJIT_FUNC_EXPORTED int
rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
{
rb_atomic_t interrupt;
int postponed_job_interrupt = 0;
+ int ret = FALSE;
- if (th->raised_flag) return;
+ if (th->ec->raised_flag) return ret;
while ((interrupt = threadptr_get_interrupts(th)) != 0) {
int sig;
@@ -2023,17 +2181,26 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
/* signal handling */
if (trap_interrupt && (th == th->vm->main_thread)) {
enum rb_thread_status prev_status = th->status;
+ int sigwait_fd = rb_sigwait_fd_get(th);
+
+ if (sigwait_fd >= 0) {
+ (void)consume_communication_pipe(sigwait_fd);
+ ruby_sigchld_handler(th->vm);
+ rb_sigwait_fd_put(th, sigwait_fd);
+ rb_sigwait_fd_migrate(th->vm);
+ }
th->status = THREAD_RUNNABLE;
while ((sig = rb_get_next_signal()) != 0) {
- rb_signal_exec(th, sig);
+ ret |= rb_signal_exec(th, sig);
}
th->status = prev_status;
}
/* exception from another thread */
- if (pending_interrupt && rb_threadptr_pending_interrupt_active_p(th)) {
+ if (pending_interrupt && threadptr_pending_interrupt_active_p(th)) {
VALUE err = rb_threadptr_pending_interrupt_deque(th, blocking_timing ? INTERRUPT_ON_BLOCKING : INTERRUPT_NONE);
thread_debug("rb_thread_execute_interrupts: %"PRIdVALUE"\n", err);
+ ret = TRUE;
if (err == Qundef) {
/* no error */
@@ -2044,6 +2211,10 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
rb_threadptr_to_kill(th);
}
else {
+ if (err == th->vm->special_exceptions[ruby_error_stream_closed]) {
+ /* the only special exception to be queued across thread */
+ err = ruby_vm_special_exception_copy(err);
+ }
/* set runnable if th was slept. */
if (th->status == THREAD_STOPPED ||
th->status == THREAD_STOPPED_FOREVER)
@@ -2053,7 +2224,7 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
}
if (timer_interrupt) {
- unsigned long limits_us = TIME_QUANTUM_USEC;
+ uint32_t limits_us = TIME_QUANTUM_USEC;
if (th->priority > 0)
limits_us <<= th->priority;
@@ -2063,19 +2234,19 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
if (th->status == THREAD_RUNNABLE)
th->running_time_us += TIME_QUANTUM_USEC;
- EXEC_EVENT_HOOK(th, RUBY_INTERNAL_EVENT_SWITCH, th->cfp->self, 0, 0, 0, Qundef);
+ EXEC_EVENT_HOOK(th->ec, RUBY_INTERNAL_EVENT_SWITCH, th->ec->cfp->self,
+ 0, 0, 0, Qundef);
rb_thread_schedule_limits(limits_us);
}
}
+ return ret;
}
void
rb_thread_execute_interrupts(VALUE thval)
{
- rb_thread_t *th;
- GetThreadPtr(thval, th);
- rb_threadptr_execute_interrupts(th, 1);
+ rb_threadptr_execute_interrupts(rb_thread_ptr(thval), 1);
}
static void
@@ -2084,14 +2255,12 @@ rb_threadptr_ready(rb_thread_t *th)
rb_threadptr_interrupt(th);
}
-void rb_threadptr_setup_exception(rb_thread_t *th, VALUE mesg, VALUE cause);
-
static VALUE
-rb_threadptr_raise(rb_thread_t *th, int argc, VALUE *argv)
+rb_threadptr_raise(rb_thread_t *target_th, int argc, VALUE *argv)
{
VALUE exc;
- if (rb_threadptr_dead(th)) {
+ if (rb_threadptr_dead(target_th)) {
return Qnil;
}
@@ -2101,9 +2270,16 @@ rb_threadptr_raise(rb_thread_t *th, int argc, VALUE *argv)
else {
exc = rb_make_exception(argc, argv);
}
- rb_threadptr_setup_exception(GET_THREAD(), exc, Qundef);
- rb_threadptr_pending_interrupt_enque(th, exc);
- rb_threadptr_interrupt(th);
+
+ /* making an exception object can switch thread,
+ so we need to check thread deadness again */
+ if (rb_threadptr_dead(target_th)) {
+ return Qnil;
+ }
+
+ rb_ec_setup_exception(GET_EC(), exc, Qundef);
+ rb_threadptr_pending_interrupt_enque(target_th, exc);
+ rb_threadptr_interrupt(target_th);
return Qnil;
}
@@ -2127,57 +2303,57 @@ rb_threadptr_signal_exit(rb_thread_t *th)
rb_threadptr_raise(th->vm->main_thread, 2, argv);
}
-#if defined(POSIX_SIGNAL) && defined(SIGSEGV) && defined(HAVE_SIGALTSTACK)
-#define USE_SIGALTSTACK
-#endif
-
-NORETURN(void ruby_thread_stack_overflow(rb_thread_t *th));
-void
-ruby_thread_stack_overflow(rb_thread_t *th)
-{
- th->raised_flag = 0;
-#ifdef USE_SIGALTSTACK
- if (!rb_during_gc()) {
- rb_exc_raise(sysstack_error);
- }
-#endif
- th->errinfo = sysstack_error;
- TH_JUMP_TAG(th, TAG_RAISE);
-}
-
int
-rb_threadptr_set_raised(rb_thread_t *th)
+rb_ec_set_raised(rb_execution_context_t *ec)
{
- if (th->raised_flag & RAISED_EXCEPTION) {
+ if (ec->raised_flag & RAISED_EXCEPTION) {
return 1;
}
- th->raised_flag |= RAISED_EXCEPTION;
+ ec->raised_flag |= RAISED_EXCEPTION;
return 0;
}
int
-rb_threadptr_reset_raised(rb_thread_t *th)
+rb_ec_reset_raised(rb_execution_context_t *ec)
{
- if (!(th->raised_flag & RAISED_EXCEPTION)) {
+ if (!(ec->raised_flag & RAISED_EXCEPTION)) {
return 0;
}
- th->raised_flag &= ~RAISED_EXCEPTION;
+ ec->raised_flag &= ~RAISED_EXCEPTION;
return 1;
}
-void
-rb_thread_fd_close(int fd)
+int
+rb_notify_fd_close(int fd, struct list_head *busy)
{
rb_vm_t *vm = GET_THREAD()->vm;
- rb_thread_t *th = 0;
+ struct waiting_fd *wfd = 0, *next;
- list_for_each(&vm->living_threads, th, vmlt_node) {
- if (th->waiting_fd == fd) {
- VALUE err = th->vm->special_exceptions[ruby_error_closed_stream];
+ list_for_each_safe(&vm->waiting_fds, wfd, next, wfd_node) {
+ if (wfd->fd == fd) {
+ rb_thread_t *th = wfd->th;
+ VALUE err;
+
+ list_del(&wfd->wfd_node);
+ list_add(busy, &wfd->wfd_node);
+
+ err = th->vm->special_exceptions[ruby_error_stream_closed];
rb_threadptr_pending_interrupt_enque(th, err);
rb_threadptr_interrupt(th);
}
}
+ return !list_empty(busy);
+}
+
+void
+rb_thread_fd_close(int fd)
+{
+ struct list_head busy;
+
+ list_head_init(&busy);
+ if (rb_notify_fd_close(fd, &busy)) {
+ do rb_thread_schedule(); while (!list_empty(&busy));
+ }
}
/*
@@ -2204,15 +2380,15 @@ rb_thread_fd_close(int fd)
static VALUE
thread_raise_m(int argc, VALUE *argv, VALUE self)
{
- rb_thread_t *target_th;
- rb_thread_t *th = GET_THREAD();
- GetThreadPtr(self, target_th);
+ rb_thread_t *target_th = rb_thread_ptr(self);
+ const rb_thread_t *current_th = GET_THREAD();
+
threadptr_check_pending_interrupt_queue(target_th);
rb_threadptr_raise(target_th, argc, argv);
/* To perform Thread.current.raise as Kernel.raise */
- if (th == target_th) {
- RUBY_VM_CHECK_INTS(th);
+ if (current_th == target_th) {
+ RUBY_VM_CHECK_INTS(target_th->ec);
}
return Qnil;
}
@@ -2234,9 +2410,7 @@ thread_raise_m(int argc, VALUE *argv, VALUE self)
VALUE
rb_thread_kill(VALUE thread)
{
- rb_thread_t *th;
-
- GetThreadPtr(thread, th);
+ rb_thread_t *th = rb_thread_ptr(thread);
if (th->to_kill || th->status == THREAD_KILLED) {
return thread;
@@ -2262,9 +2436,7 @@ rb_thread_kill(VALUE thread)
int
rb_thread_to_be_killed(VALUE thread)
{
- rb_thread_t *th;
-
- GetThreadPtr(thread, th);
+ rb_thread_t *th = rb_thread_ptr(thread);
if (th->to_kill || th->status == THREAD_KILLED) {
return TRUE;
@@ -2341,15 +2513,16 @@ rb_thread_wakeup(VALUE thread)
VALUE
rb_thread_wakeup_alive(VALUE thread)
{
- rb_thread_t *th;
- GetThreadPtr(thread, th);
+ rb_thread_t *target_th = rb_thread_ptr(thread);
+ if (target_th->status == THREAD_KILLED) return Qnil;
- if (th->status == THREAD_KILLED) {
- return Qnil;
+ rb_threadptr_ready(target_th);
+
+ if (target_th->status == THREAD_STOPPED ||
+ target_th->status == THREAD_STOPPED_FOREVER) {
+ target_th->status = THREAD_RUNNABLE;
}
- rb_threadptr_ready(th);
- if (th->status == THREAD_STOPPED || th->status == THREAD_STOPPED_FOREVER)
- th->status = THREAD_RUNNABLE;
+
return thread;
}
@@ -2501,8 +2674,8 @@ rb_thread_s_main(VALUE klass)
*
* The default is +false+.
*
- * When set to +true+, all threads will abort (the process will
- * <code>exit(0)</code>) if an exception is raised in any thread.
+ * When set to +true+, if any thread is aborted by an exception, the
+ * raised exception will be re-raised in the main thread.
*
* Can also be specified by the global $DEBUG flag or command line option
* +-d+.
@@ -2524,7 +2697,8 @@ rb_thread_s_abort_exc(void)
* call-seq:
* Thread.abort_on_exception= boolean -> true or false
*
- * When set to +true+, all threads will abort if an exception is raised.
+ * When set to +true+, if any thread is aborted by an exception, the
+ * raised exception will be re-raised in the main thread.
* Returns the new state.
*
* Thread.abort_on_exception = true
@@ -2575,9 +2749,7 @@ rb_thread_s_abort_exc_set(VALUE self, VALUE val)
static VALUE
rb_thread_abort_exc(VALUE thread)
{
- rb_thread_t *th;
- GetThreadPtr(thread, th);
- return th->abort_on_exception ? Qtrue : Qfalse;
+ return rb_thread_ptr(thread)->abort_on_exception ? Qtrue : Qfalse;
}
@@ -2585,10 +2757,8 @@ rb_thread_abort_exc(VALUE thread)
* call-seq:
* thr.abort_on_exception= boolean -> true or false
*
- * When set to +true+, all threads (including the main program) will abort if
- * an exception is raised in this +thr+.
- *
- * The process will effectively <code>exit(0)</code>.
+ * When set to +true+, if this +thr+ is aborted by an exception, the
+ * raised exception will be re-raised in the main thread.
*
* See also #abort_on_exception.
*
@@ -2599,10 +2769,7 @@ rb_thread_abort_exc(VALUE thread)
static VALUE
rb_thread_abort_exc_set(VALUE thread, VALUE val)
{
- rb_thread_t *th;
-
- GetThreadPtr(thread, th);
- th->abort_on_exception = RTEST(val);
+ rb_thread_ptr(thread)->abort_on_exception = RTEST(val);
return val;
}
@@ -2613,15 +2780,40 @@ rb_thread_abort_exc_set(VALUE thread, VALUE val)
*
* Returns the status of the global ``report on exception'' condition.
*
- * The default is +false+.
+ * The default is +true+ since Ruby 2.5.
+ *
+ * All threads created when this flag is true will report
+ * a message on $stderr if an exception kills the thread.
+ *
+ * Thread.new { 1.times { raise } }
+ *
+ * will produce this output on $stderr:
*
- * When set to +true+, all threads will report the exception if an
- * exception is raised in any thread.
+ * #<Thread:...> terminated with exception (report_on_exception is true):
+ * Traceback (most recent call last):
+ * 2: from -e:1:in `block in <main>'
+ * 1: from -e:1:in `times'
+ *
+ * This is done to catch errors in threads early.
+ * In some cases, you might not want this output.
+ * There are multiple ways to avoid the extra output:
+ *
+ * * If the exception is not intended, the best is to fix the cause of
+ * the exception so it does not happen anymore.
+ * * If the exception is intended, it might be better to rescue it closer to
+ * where it is raised rather then let it kill the Thread.
+ * * If it is guaranteed the Thread will be joined with Thread#join or
+ * Thread#value, then it is safe to disable this report with
+ * <code>Thread.current.report_on_exception = false</code>
+ * when starting the Thread.
+ * However, this might handle the exception much later, or not at all
+ * if the Thread is never joined due to the parent thread being blocked, etc.
*
* See also ::report_on_exception=.
*
* There is also an instance level method to set this for a specific thread,
- * see #report_on_exception.
+ * see #report_on_exception=.
+ *
*/
static VALUE
@@ -2635,8 +2827,9 @@ rb_thread_s_report_exc(void)
* call-seq:
* Thread.report_on_exception= boolean -> true or false
*
- * When set to +true+, all threads will report the exception if an
- * exception is raised. Returns the new state.
+ * Returns the new state.
+ * When set to +true+, all threads created afterwards will inherit the
+ * condition and report a message on $stderr if an exception kills a thread:
*
* Thread.report_on_exception = true
* t1 = Thread.new do
@@ -2649,10 +2842,9 @@ rb_thread_s_report_exc(void)
* This will produce:
*
* In new thread
- * prog.rb:4: Exception from thread (RuntimeError)
- * from prog.rb:2:in `initialize'
- * from prog.rb:2:in `new'
- * from prog.rb:2
+ * #<Thread:...prog.rb:2> terminated with exception (report_on_exception is true):
+ * Traceback (most recent call last):
+ * prog.rb:4:in `block in <main>': Exception from thread (RuntimeError)
* In the main thread
*
* See also ::report_on_exception.
@@ -2676,20 +2868,19 @@ rb_thread_s_report_exc_set(VALUE self, VALUE val)
* Returns the status of the thread-local ``report on exception'' condition for
* this +thr+.
*
- * The default is +false+.
+ * The default value when creating a Thread is the value of
+ * the global flag Thread.report_on_exception.
*
* See also #report_on_exception=.
*
- * There is also a class level method to set this for all threads, see
- * ::report_on_exception.
+ * There is also a class level method to set this for all new threads, see
+ * ::report_on_exception=.
*/
static VALUE
rb_thread_report_exc(VALUE thread)
{
- rb_thread_t *th;
- GetThreadPtr(thread, th);
- return th->report_on_exception ? Qtrue : Qfalse;
+ return rb_thread_ptr(thread)->report_on_exception ? Qtrue : Qfalse;
}
@@ -2697,22 +2888,19 @@ rb_thread_report_exc(VALUE thread)
* call-seq:
* thr.report_on_exception= boolean -> true or false
*
- * When set to +true+, all threads (including the main program) will
- * report the exception if an exception is raised in this +thr+.
+ * When set to +true+, a message is printed on $stderr if an exception
+ * kills this +thr+. See ::report_on_exception for details.
*
* See also #report_on_exception.
*
- * There is also a class level method to set this for all threads, see
+ * There is also a class level method to set this for all new threads, see
* ::report_on_exception=.
*/
static VALUE
rb_thread_report_exc_set(VALUE thread, VALUE val)
{
- rb_thread_t *th;
-
- GetThreadPtr(thread, th);
- th->report_on_exception = RTEST(val);
+ rb_thread_ptr(thread)->report_on_exception = RTEST(val);
return val;
}
@@ -2730,15 +2918,8 @@ rb_thread_report_exc_set(VALUE thread, VALUE val)
VALUE
rb_thread_group(VALUE thread)
{
- rb_thread_t *th;
- VALUE group;
- GetThreadPtr(thread, th);
- group = th->thgroup;
-
- if (!group) {
- group = Qnil;
- }
- return group;
+ VALUE group = rb_thread_ptr(thread)->thgroup;
+ return group == 0 ? Qnil : group;
}
static const char *
@@ -2746,10 +2927,7 @@ thread_status_name(rb_thread_t *th, int detail)
{
switch (th->status) {
case THREAD_RUNNABLE:
- if (th->to_kill)
- return "aborting";
- else
- return "run";
+ return th->to_kill ? "aborting" : "run";
case THREAD_STOPPED_FOREVER:
if (detail) return "sleep_forever";
case THREAD_STOPPED:
@@ -2802,17 +2980,20 @@ rb_threadptr_dead(rb_thread_t *th)
static VALUE
rb_thread_status(VALUE thread)
{
- rb_thread_t *th;
- GetThreadPtr(thread, th);
+ rb_thread_t *target_th = rb_thread_ptr(thread);
- if (rb_threadptr_dead(th)) {
- if (!NIL_P(th->errinfo) && !FIXNUM_P(th->errinfo)
- /* TODO */ ) {
+ if (rb_threadptr_dead(target_th)) {
+ if (!NIL_P(target_th->ec->errinfo) &&
+ !FIXNUM_P(target_th->ec->errinfo)) {
return Qnil;
}
- return Qfalse;
+ else {
+ return Qfalse;
+ }
+ }
+ else {
+ return rb_str_new2(thread_status_name(target_th, FALSE));
}
- return rb_str_new2(thread_status_name(th, FALSE));
}
@@ -2833,12 +3014,12 @@ rb_thread_status(VALUE thread)
static VALUE
rb_thread_alive_p(VALUE thread)
{
- rb_thread_t *th;
- GetThreadPtr(thread, th);
-
- if (rb_threadptr_dead(th))
+ if (rb_threadptr_dead(rb_thread_ptr(thread))) {
return Qfalse;
- return Qtrue;
+ }
+ else {
+ return Qtrue;
+ }
}
/*
@@ -2858,35 +3039,34 @@ rb_thread_alive_p(VALUE thread)
static VALUE
rb_thread_stop_p(VALUE thread)
{
- rb_thread_t *th;
- GetThreadPtr(thread, th);
+ rb_thread_t *th = rb_thread_ptr(thread);
- if (rb_threadptr_dead(th))
+ if (rb_threadptr_dead(th)) {
return Qtrue;
- if (th->status == THREAD_STOPPED || th->status == THREAD_STOPPED_FOREVER)
+ }
+ else if (th->status == THREAD_STOPPED ||
+ th->status == THREAD_STOPPED_FOREVER) {
return Qtrue;
- return Qfalse;
+ }
+ else {
+ return Qfalse;
+ }
}
/*
* call-seq:
* thr.safe_level -> integer
*
- * Returns the safe level in effect for <i>thr</i>. Setting thread-local safe
- * levels can help when implementing sandboxes which run insecure code.
+ * Returns the safe level.
*
- * thr = Thread.new { $SAFE = 1; sleep }
- * Thread.current.safe_level #=> 0
- * thr.safe_level #=> 1
+ * This method is obsolete because $SAFE is a process global state.
+ * Simply check $SAFE.
*/
static VALUE
rb_thread_safe_level(VALUE thread)
{
- rb_thread_t *th;
- GetThreadPtr(thread, th);
-
- return INT2NUM(th->safe_level);
+ return UINT2NUM(rb_safe_level());
}
/*
@@ -2899,9 +3079,7 @@ rb_thread_safe_level(VALUE thread)
static VALUE
rb_thread_getname(VALUE thread)
{
- rb_thread_t *th;
- GetThreadPtr(thread, th);
- return th->name;
+ return rb_thread_ptr(thread)->name;
}
/*
@@ -2915,11 +3093,8 @@ rb_thread_getname(VALUE thread)
static VALUE
rb_thread_setname(VALUE thread, VALUE name)
{
-#ifdef SET_ANOTHER_THREAD_NAME
- const char *s = "";
-#endif
- rb_thread_t *th;
- GetThreadPtr(thread, th);
+ rb_thread_t *target_th = rb_thread_ptr(thread);
+
if (!NIL_P(name)) {
rb_encoding *enc;
StringValueCStr(name);
@@ -2929,47 +3104,38 @@ rb_thread_setname(VALUE thread, VALUE name)
rb_enc_name(enc));
}
name = rb_str_new_frozen(name);
-#ifdef SET_ANOTHER_THREAD_NAME
- s = RSTRING_PTR(name);
-#endif
}
- th->name = name;
-#if defined(SET_ANOTHER_THREAD_NAME)
- if (threadptr_initialized(th)) {
- SET_ANOTHER_THREAD_NAME(th->thread_id, s);
+ target_th->name = name;
+ if (threadptr_initialized(target_th)) {
+ native_set_another_thread_name(target_th->thread_id, name);
}
-#endif
return name;
}
/*
* call-seq:
- * thr.inspect -> string
+ * thr.to_s -> string
*
* Dump the name, id, and status of _thr_ to a string.
*/
static VALUE
-rb_thread_inspect(VALUE thread)
+rb_thread_to_s(VALUE thread)
{
VALUE cname = rb_class_path(rb_obj_class(thread));
- rb_thread_t *th;
+ rb_thread_t *target_th = rb_thread_ptr(thread);
const char *status;
- VALUE str;
+ VALUE str, loc;
- GetThreadPtr(thread, th);
- status = thread_status_name(th, TRUE);
+ status = thread_status_name(target_th, TRUE);
str = rb_sprintf("#<%"PRIsVALUE":%p", cname, (void *)thread);
- if (!NIL_P(th->name)) {
- rb_str_catf(str, "@%"PRIsVALUE, th->name);
- }
- if (!th->first_func && th->first_proc) {
- VALUE loc = rb_proc_location(th->first_proc);
- if (!NIL_P(loc)) {
- const VALUE *ptr = RARRAY_CONST_PTR(loc);
- rb_str_catf(str, "@%"PRIsVALUE":%"PRIsVALUE, ptr[0], ptr[1]);
- rb_gc_force_recycle(loc);
- }
+ if (!NIL_P(target_th->name)) {
+ rb_str_catf(str, "@%"PRIsVALUE, target_th->name);
+ }
+ if ((loc = threadptr_invoke_proc_location(target_th)) != Qnil) {
+ rb_str_catf(str, "@%"PRIsVALUE":%"PRIsVALUE,
+ RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
+ rb_gc_force_recycle(loc);
}
rb_str_catf(str, " %s>", status);
OBJ_INFECT(str, thread);
@@ -2984,12 +3150,13 @@ static VALUE
threadptr_local_aref(rb_thread_t *th, ID id)
{
if (id == recursive_key) {
- return th->local_storage_recursive_hash;
+ return th->ec->local_storage_recursive_hash;
}
else {
st_data_t val;
+ st_table *local_storage = th->ec->local_storage;
- if (th->local_storage && st_lookup(th->local_storage, id, &val)) {
+ if (local_storage != NULL && st_lookup(local_storage, id, &val)) {
return (VALUE)val;
}
else {
@@ -3001,9 +3168,7 @@ threadptr_local_aref(rb_thread_t *th, ID id)
VALUE
rb_thread_local_aref(VALUE thread, ID id)
{
- rb_thread_t *th;
- GetThreadPtr(thread, th);
- return threadptr_local_aref(th, id);
+ return threadptr_local_aref(rb_thread_ptr(thread), id);
}
/*
@@ -3074,38 +3239,88 @@ rb_thread_aref(VALUE thread, VALUE key)
return rb_thread_local_aref(thread, id);
}
+/*
+ * call-seq:
+ * thr.fetch(sym) -> obj
+ * thr.fetch(sym) { } -> obj
+ * thr.fetch(sym, default) -> obj
+ *
+ * Returns a fiber-local for the given key. If the key can't be
+ * found, there are several options: With no other arguments, it will
+ * raise a <code>KeyError</code> exception; if <i>default</i> is
+ * given, then that will be returned; if the optional code block is
+ * specified, then that will be run and its result returned.
+ * See Thread#[] and Hash#fetch.
+ */
static VALUE
-threadptr_local_aset(rb_thread_t *th, ID id, VALUE val)
+rb_thread_fetch(int argc, VALUE *argv, VALUE self)
{
+ VALUE key, val;
+ ID id;
+ rb_thread_t *target_th = rb_thread_ptr(self);
+ int block_given;
+
+ rb_check_arity(argc, 1, 2);
+ key = argv[0];
+
+ block_given = rb_block_given_p();
+ if (block_given && argc == 2) {
+ rb_warn("block supersedes default value argument");
+ }
+
+ id = rb_check_id(&key);
+
if (id == recursive_key) {
- th->local_storage_recursive_hash = val;
+ return target_th->ec->local_storage_recursive_hash;
+ }
+ else if (id && target_th->ec->local_storage &&
+ st_lookup(target_th->ec->local_storage, id, &val)) {
return val;
}
- else if (NIL_P(val)) {
- if (!th->local_storage) return Qnil;
- st_delete_wrap(th->local_storage, id);
- return Qnil;
+ else if (block_given) {
+ return rb_yield(key);
+ }
+ else if (argc == 1) {
+ rb_key_err_raise(rb_sprintf("key not found: %+"PRIsVALUE, key), self, key);
}
else {
- if (!th->local_storage) {
- th->local_storage = st_init_numtable();
- }
- st_insert(th->local_storage, id, val);
+ return argv[1];
+ }
+}
+
+static VALUE
+threadptr_local_aset(rb_thread_t *th, ID id, VALUE val)
+{
+ if (id == recursive_key) {
+ th->ec->local_storage_recursive_hash = val;
return val;
}
+ else {
+ st_table *local_storage = th->ec->local_storage;
+
+ if (NIL_P(val)) {
+ if (!local_storage) return Qnil;
+ st_delete_wrap(local_storage, id);
+ return Qnil;
+ }
+ else {
+ if (local_storage == NULL) {
+ th->ec->local_storage = local_storage = st_init_numtable();
+ }
+ st_insert(local_storage, id, val);
+ return val;
+ }
+ }
}
VALUE
rb_thread_local_aset(VALUE thread, ID id, VALUE val)
{
- rb_thread_t *th;
- GetThreadPtr(thread, th);
-
if (OBJ_FROZEN(thread)) {
rb_error_frozen("thread locals");
}
- return threadptr_local_aset(th, id, val);
+ return threadptr_local_aset(rb_thread_ptr(thread), id, val);
}
/*
@@ -3202,18 +3417,18 @@ rb_thread_variable_set(VALUE thread, VALUE id, VALUE val)
static VALUE
rb_thread_key_p(VALUE self, VALUE key)
{
- rb_thread_t *th;
ID id = rb_check_id(&key);
+ st_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
- GetThreadPtr(self, th);
-
- if (!id || !th->local_storage) {
+ if (!id || local_storage == NULL) {
return Qfalse;
}
- if (st_lookup(th->local_storage, id, 0)) {
+ else if (st_lookup(local_storage, id, 0)) {
return Qtrue;
}
- return Qfalse;
+ else {
+ return Qfalse;
+ }
}
static int
@@ -3246,12 +3461,11 @@ rb_thread_alone(void)
static VALUE
rb_thread_keys(VALUE self)
{
- rb_thread_t *th;
+ st_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
VALUE ary = rb_ary_new();
- GetThreadPtr(self, th);
- if (th->local_storage) {
- st_foreach(th->local_storage, thread_keys_i, ary);
+ if (local_storage) {
+ st_foreach(local_storage, thread_keys_i, ary);
}
return ary;
}
@@ -3319,11 +3533,11 @@ rb_thread_variable_p(VALUE thread, VALUE key)
locals = rb_ivar_get(thread, id_locals);
- if (!RHASH(locals)->ntbl)
+ if (rb_hash_lookup(locals, ID2SYM(id)) != Qnil) {
+ return Qtrue;
+ }
+ else {
return Qfalse;
-
- if (st_lookup(RHASH(locals)->ntbl, ID2SYM(id), 0)) {
- return Qtrue;
}
return Qfalse;
@@ -3347,9 +3561,7 @@ rb_thread_variable_p(VALUE thread, VALUE key)
static VALUE
rb_thread_priority(VALUE thread)
{
- rb_thread_t *th;
- GetThreadPtr(thread, th);
- return INT2NUM(th->priority);
+ return INT2NUM(rb_thread_ptr(thread)->priority);
}
@@ -3382,13 +3594,11 @@ rb_thread_priority(VALUE thread)
static VALUE
rb_thread_priority_set(VALUE thread, VALUE prio)
{
- rb_thread_t *th;
+ rb_thread_t *target_th = rb_thread_ptr(thread);
int priority;
- GetThreadPtr(thread, th);
-
#if USE_NATIVE_THREAD_PRIORITY
- th->priority = NUM2INT(prio);
+ target_th->priority = NUM2INT(prio);
native_thread_apply_priority(th);
#else
priority = NUM2INT(prio);
@@ -3398,9 +3608,9 @@ rb_thread_priority_set(VALUE thread, VALUE prio)
else if (priority < RUBY_THREAD_PRIORITY_MIN) {
priority = RUBY_THREAD_PRIORITY_MIN;
}
- th->priority = priority;
+ target_th->priority = (int8_t)priority;
#endif
- return INT2NUM(th->priority);
+ return INT2NUM(target_th->priority);
}
/* for IO */
@@ -3533,11 +3743,6 @@ rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
memcpy(dst->fdset, src->fdset, size);
}
-#ifdef __native_client__
-int select(int nfds, fd_set *readfds, fd_set *writefds,
- fd_set *exceptfds, struct timeval *timeout);
-#endif
-
int
rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, struct timeval *timeout)
{
@@ -3557,9 +3762,7 @@ rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *excep
return select(n, r, w, e, timeout);
}
-#if defined __GNUC__ && __GNUC__ >= 6
-#define rb_fd_no_init(fds) ASSUME(!(fds)->maxfd)
-#endif
+#define rb_fd_no_init(fds) ((void)((fds)->fdset = 0), (void)((fds)->maxfd = 0))
#undef FD_ZERO
#undef FD_SET
@@ -3624,91 +3827,134 @@ rb_fd_set(int fd, rb_fdset_t *set)
#define FD_CLR(i, f) rb_fd_clr((i), (f))
#define FD_ISSET(i, f) rb_fd_isset((i), (f))
+#define rb_fd_no_init(fds) (void)((fds)->fdset = 0)
+
#endif
#ifndef rb_fd_no_init
#define rb_fd_no_init(fds) (void)(fds)
#endif
-static inline int
-retryable(int e)
+static int
+wait_retryable(int *result, int errnum, rb_hrtime_t *rel, rb_hrtime_t end)
{
- if (e == EINTR) return TRUE;
+ if (*result < 0) {
+ switch (errnum) {
+ case EINTR:
#ifdef ERESTART
- if (e == ERESTART) return TRUE;
+ case ERESTART:
#endif
+ *result = 0;
+ if (rel && hrtime_update_expire(rel, end)) {
+ *rel = 0;
+ }
+ return TRUE;
+ }
+ return FALSE;
+ }
+ else if (*result == 0) {
+ /* check for spurious wakeup */
+ if (rel) {
+ return !hrtime_update_expire(rel, end);
+ }
+ return TRUE;
+ }
return FALSE;
}
-#define restore_fdset(fds1, fds2) \
- ((fds1) ? rb_fd_dup(fds1, fds2) : (void)0)
+struct select_set {
+ int max;
+ int sigwait_fd;
+ rb_thread_t *th;
+ rb_fdset_t *rset;
+ rb_fdset_t *wset;
+ rb_fdset_t *eset;
+ rb_fdset_t orig_rset;
+ rb_fdset_t orig_wset;
+ rb_fdset_t orig_eset;
+ struct timeval *timeout;
+};
-static inline void
-update_timeval(struct timeval *timeout, double limit)
+static VALUE
+select_set_free(VALUE p)
{
- if (timeout) {
- double d = limit - timeofday();
+ struct select_set *set = (struct select_set *)p;
- timeout->tv_sec = (time_t)d;
- timeout->tv_usec = (int)((d-(double)timeout->tv_sec)*1e6);
- if (timeout->tv_sec < 0) timeout->tv_sec = 0;
- if (timeout->tv_usec < 0) timeout->tv_usec = 0;
+ if (set->sigwait_fd >= 0) {
+ rb_sigwait_fd_put(set->th, set->sigwait_fd);
+ rb_sigwait_fd_migrate(set->th->vm);
}
+
+ rb_fd_term(&set->orig_rset);
+ rb_fd_term(&set->orig_wset);
+ rb_fd_term(&set->orig_eset);
+
+ return Qfalse;
}
-static int
-do_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds,
- rb_fdset_t *exceptfds, struct timeval *timeout)
+static const rb_hrtime_t *
+sigwait_timeout(rb_thread_t *th, int sigwait_fd, const rb_hrtime_t *orig,
+ int *drained_p)
+{
+ static const rb_hrtime_t quantum = TIME_QUANTUM_USEC * 1000;
+
+ if (sigwait_fd >= 0 && (!ubf_threads_empty() || BUSY_WAIT_SIGNALS)) {
+ *drained_p = check_signals_nogvl(th, sigwait_fd);
+ if (!orig || *orig > quantum)
+ return &quantum;
+ }
+
+ return orig;
+}
+
+static VALUE
+do_select(VALUE p)
{
- int MAYBE_UNUSED(result);
+ struct select_set *set = (struct select_set *)p;
+ int result = 0;
int lerrno;
- rb_fdset_t MAYBE_UNUSED(orig_read);
- rb_fdset_t MAYBE_UNUSED(orig_write);
- rb_fdset_t MAYBE_UNUSED(orig_except);
- double limit = 0;
- struct timeval wait_rest;
- rb_thread_t *th = GET_THREAD();
+ rb_hrtime_t *to, rel, end = 0;
+ timeout_prepare(&to, &rel, &end, set->timeout);
+#define restore_fdset(dst, src) \
+ ((dst) ? rb_fd_dup(dst, src) : (void)0)
#define do_select_update() \
- (restore_fdset(readfds, &orig_read), \
- restore_fdset(writefds, &orig_write), \
- restore_fdset(exceptfds, &orig_except), \
- update_timeval(timeout, limit), \
+ (restore_fdset(set->rset, &set->orig_rset), \
+ restore_fdset(set->wset, &set->orig_wset), \
+ restore_fdset(set->eset, &set->orig_eset), \
TRUE)
- if (timeout) {
- limit = timeofday();
- limit += (double)timeout->tv_sec+(double)timeout->tv_usec*1e-6;
- wait_rest = *timeout;
- timeout = &wait_rest;
- }
-
-#define fd_init_copy(f) \
- (f##fds) ? rb_fd_init_copy(&orig_##f, f##fds) : rb_fd_no_init(&orig_##f)
- fd_init_copy(read);
- fd_init_copy(write);
- fd_init_copy(except);
-#undef fd_init_copy
-
do {
+ int drained;
lerrno = 0;
- BLOCKING_REGION({
- result = native_fd_select(n, readfds, writefds, exceptfds,
- timeout, th);
- if (result < 0) lerrno = errno;
- }, ubf_select, th, FALSE);
+ BLOCKING_REGION(set->th, {
+ const rb_hrtime_t *sto;
+ struct timeval tv;
+
+ sto = sigwait_timeout(set->th, set->sigwait_fd, to, &drained);
+ if (!RUBY_VM_INTERRUPTED(set->th->ec)) {
+ result = native_fd_select(set->max, set->rset, set->wset,
+ set->eset,
+ rb_hrtime2timeval(&tv, sto), set->th);
+ if (result < 0) lerrno = errno;
+ }
+ }, set->sigwait_fd >= 0 ? ubf_sigwait : ubf_select, set->th, TRUE);
+
+ if (set->sigwait_fd >= 0) {
+ if (result > 0 && rb_fd_isset(set->sigwait_fd, set->rset))
+ result--;
+ (void)check_signals_nogvl(set->th, set->sigwait_fd);
+ }
- RUBY_VM_CHECK_INTS_BLOCKING(th);
- } while (result < 0 && retryable(errno = lerrno) && do_select_update());
+ RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec); /* may raise */
+ } while (wait_retryable(&result, lerrno, to, end) && do_select_update());
-#define fd_term(f) if (f##fds) rb_fd_term(&orig_##f)
- fd_term(read);
- fd_term(write);
- fd_term(except);
-#undef fd_term
+ if (result < 0) {
+ errno = lerrno;
+ }
- return result;
+ return (VALUE)result;
}
static void
@@ -3744,39 +3990,69 @@ rb_thread_fd_writable(int fd)
return TRUE;
}
+static rb_fdset_t *
+init_set_fd(int fd, rb_fdset_t *fds)
+{
+ if (fd < 0) {
+ return 0;
+ }
+ rb_fd_init(fds);
+ rb_fd_set(fd, fds);
+
+ return fds;
+}
+
int
rb_thread_fd_select(int max, rb_fdset_t * read, rb_fdset_t * write, rb_fdset_t * except,
struct timeval *timeout)
{
- if (!read && !write && !except) {
- if (!timeout) {
- rb_thread_sleep_forever();
- return 0;
- }
- rb_thread_wait_for(*timeout);
- return 0;
- }
+ struct select_set set;
- if (read) {
- rb_fd_resize(max - 1, read);
- }
- if (write) {
- rb_fd_resize(max - 1, write);
- }
- if (except) {
- rb_fd_resize(max - 1, except);
+ set.th = GET_THREAD();
+ RUBY_VM_CHECK_INTS_BLOCKING(set.th->ec);
+ set.max = max;
+ set.rset = read;
+ set.wset = write;
+ set.eset = except;
+ set.timeout = timeout;
+
+ if (!set.rset && !set.wset && !set.eset) {
+ if (!timeout) {
+ rb_thread_sleep_forever();
+ return 0;
+ }
+ rb_thread_wait_for(*timeout);
+ return 0;
+ }
+
+ set.sigwait_fd = rb_sigwait_fd_get(set.th);
+ if (set.sigwait_fd >= 0) {
+ if (set.rset)
+ rb_fd_set(set.sigwait_fd, set.rset);
+ else
+ set.rset = init_set_fd(set.sigwait_fd, &set.orig_rset);
+ if (set.sigwait_fd >= set.max) {
+ set.max = set.sigwait_fd + 1;
+ }
}
- return do_select(max, read, write, except, timeout);
-}
+#define fd_init_copy(f) do { \
+ if (set.f) { \
+ rb_fd_resize(set.max - 1, set.f); \
+ if (&set.orig_##f != set.f) { /* sigwait_fd */ \
+ rb_fd_init_copy(&set.orig_##f, set.f); \
+ } \
+ } \
+ else { \
+ rb_fd_no_init(&set.orig_##f); \
+ } \
+ } while (0)
+ fd_init_copy(rset);
+ fd_init_copy(wset);
+ fd_init_copy(eset);
+#undef fd_init_copy
-/*
- * poll() is supported by many OSes, but so far Linux is the only
- * one we know of that supports using poll() in all places select()
- * would work.
- */
-#if defined(HAVE_POLL) && defined(__linux__)
-# define USE_POLL
-#endif
+ return (int)rb_ensure(do_select, (VALUE)&set, select_set_free, (VALUE)&set);
+}
#ifdef USE_POLL
@@ -3785,89 +4061,84 @@ rb_thread_fd_select(int max, rb_fdset_t * read, rb_fdset_t * write, rb_fdset_t *
#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
#define POLLEX_SET (POLLPRI)
-#ifndef HAVE_PPOLL
-/* TODO: don't ignore sigmask */
-int
-ppoll(struct pollfd *fds, nfds_t nfds,
- const struct timespec *ts, const sigset_t *sigmask)
-{
- int timeout_ms;
-
- if (ts) {
- int tmp, tmp2;
-
- if (ts->tv_sec > INT_MAX/1000)
- timeout_ms = -1;
- else {
- tmp = (int)(ts->tv_sec * 1000);
- tmp2 = (int)(ts->tv_nsec / (1000 * 1000));
- if (INT_MAX - tmp < tmp2)
- timeout_ms = -1;
- else
- timeout_ms = (int)(tmp + tmp2);
- }
- }
- else
- timeout_ms = -1;
-
- return poll(fds, nfds, timeout_ms);
-}
+#ifndef POLLERR_SET /* defined for FreeBSD for now */
+# define POLLERR_SET (0)
#endif
-static inline void
-update_timespec(struct timespec *timeout, double limit)
-{
- if (timeout) {
- double d = limit - timeofday();
-
- timeout->tv_sec = (long)d;
- timeout->tv_nsec = (long)((d-(double)timeout->tv_sec)*1e9);
- if (timeout->tv_sec < 0) timeout->tv_sec = 0;
- if (timeout->tv_nsec < 0) timeout->tv_nsec = 0;
- }
-}
-
/*
* returns a mask of events
*/
int
-rb_wait_for_single_fd(int fd, int events, struct timeval *tv)
+rb_wait_for_single_fd(int fd, int events, struct timeval *timeout)
{
- struct pollfd fds;
+ struct pollfd fds[2];
int result = 0, lerrno;
- double limit = 0;
- struct timespec ts;
- struct timespec *timeout = NULL;
- rb_thread_t *th = GET_THREAD();
-
-#define poll_update() \
- (update_timespec(timeout, limit), \
- TRUE)
+ rb_hrtime_t *to, rel, end = 0;
+ int drained;
+ nfds_t nfds;
+ rb_unblock_function_t *ubf;
+ struct waiting_fd wfd;
+ int state;
- if (tv) {
- ts.tv_sec = tv->tv_sec;
- ts.tv_nsec = tv->tv_usec * 1000;
- limit = timeofday();
- limit += (double)tv->tv_sec + (double)tv->tv_usec * 1e-6;
- timeout = &ts;
+ wfd.th = GET_THREAD();
+ wfd.fd = fd;
+ list_add(&wfd.th->vm->waiting_fds, &wfd.wfd_node);
+ EC_PUSH_TAG(wfd.th->ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ RUBY_VM_CHECK_INTS_BLOCKING(wfd.th->ec);
+ timeout_prepare(&to, &rel, &end, timeout);
+ fds[0].fd = fd;
+ fds[0].events = (short)events;
+ fds[0].revents = 0;
+ do {
+ fds[1].fd = rb_sigwait_fd_get(wfd.th);
+
+ if (fds[1].fd >= 0) {
+ fds[1].events = POLLIN;
+ fds[1].revents = 0;
+ nfds = 2;
+ ubf = ubf_sigwait;
+ }
+ else {
+ nfds = 1;
+ ubf = ubf_select;
+ }
+
+ lerrno = 0;
+ BLOCKING_REGION(wfd.th, {
+ const rb_hrtime_t *sto;
+ struct timespec ts;
+
+ sto = sigwait_timeout(wfd.th, fds[1].fd, to, &drained);
+ if (!RUBY_VM_INTERRUPTED(wfd.th->ec)) {
+ result = ppoll(fds, nfds, rb_hrtime2timespec(&ts, sto), 0);
+ if (result < 0) lerrno = errno;
+ }
+ }, ubf, wfd.th, TRUE);
+
+ if (fds[1].fd >= 0) {
+ if (result > 0 && fds[1].revents) {
+ result--;
+ }
+ (void)check_signals_nogvl(wfd.th, fds[1].fd);
+ rb_sigwait_fd_put(wfd.th, fds[1].fd);
+ rb_sigwait_fd_migrate(wfd.th->vm);
+ }
+ RUBY_VM_CHECK_INTS_BLOCKING(wfd.th->ec);
+ } while (wait_retryable(&result, lerrno, to, end));
+ }
+ EC_POP_TAG();
+ list_del(&wfd.wfd_node);
+ if (state) {
+ EC_JUMP_TAG(wfd.th->ec, state);
}
- fds.fd = fd;
- fds.events = (short)events;
-
- do {
- fds.revents = 0;
- lerrno = 0;
- BLOCKING_REGION({
- result = ppoll(&fds, 1, timeout, NULL);
- if (result < 0) lerrno = errno;
- }, ubf_select, th, FALSE);
-
- RUBY_VM_CHECK_INTS_BLOCKING(th);
- } while (result < 0 && retryable(errno = lerrno) && poll_update());
- if (result < 0) return -1;
+ if (result < 0) {
+ errno = lerrno;
+ return -1;
+ }
- if (fds.revents & POLLNVAL) {
+ if (fds[0].revents & POLLNVAL) {
errno = EBADF;
return -1;
}
@@ -3877,25 +4148,20 @@ rb_wait_for_single_fd(int fd, int events, struct timeval *tv)
* Therefore we need to fix it up.
*/
result = 0;
- if (fds.revents & POLLIN_SET)
+ if (fds[0].revents & POLLIN_SET)
result |= RB_WAITFD_IN;
- if (fds.revents & POLLOUT_SET)
+ if (fds[0].revents & POLLOUT_SET)
result |= RB_WAITFD_OUT;
- if (fds.revents & POLLEX_SET)
+ if (fds[0].revents & POLLEX_SET)
result |= RB_WAITFD_PRI;
+ /* all requested events are ready if there is an error */
+ if (fds[0].revents & POLLERR_SET)
+ result |= events;
+
return result;
}
#else /* ! USE_POLL - implement rb_io_poll_fd() using select() */
-static rb_fdset_t *
-init_set_fd(int fd, rb_fdset_t *fds)
-{
- rb_fd_init(fds);
- rb_fd_set(fd, fds);
-
- return fds;
-}
-
struct select_args {
union {
int fd;
@@ -3904,6 +4170,7 @@ struct select_args {
rb_fdset_t *read;
rb_fdset_t *write;
rb_fdset_t *except;
+ struct waiting_fd wfd;
struct timeval *tv;
};
@@ -3934,6 +4201,7 @@ select_single_cleanup(VALUE ptr)
{
struct select_args *args = (struct select_args *)ptr;
+ list_del(&args->wfd.wfd_node);
if (args->read) rb_fd_term(args->read);
if (args->write) rb_fd_term(args->write);
if (args->except) rb_fd_term(args->except);
@@ -3954,7 +4222,10 @@ rb_wait_for_single_fd(int fd, int events, struct timeval *tv)
args.write = (events & RB_WAITFD_OUT) ? init_set_fd(fd, &wfds) : NULL;
args.except = (events & RB_WAITFD_PRI) ? init_set_fd(fd, &efds) : NULL;
args.tv = tv;
+ args.wfd.fd = fd;
+ args.wfd.th = GET_THREAD();
+ list_add(&args.wfd.th->vm->waiting_fds, &args.wfd.wfd_node);
r = (int)rb_ensure(select_single, ptr, select_single_cleanup, ptr);
if (r == -1)
errno = args.as.error;
@@ -3976,7 +4247,6 @@ rb_gc_set_stack_end(VALUE **stack_end_p)
}
#endif
-
/*
*
*/
@@ -3987,39 +4257,97 @@ rb_threadptr_check_signal(rb_thread_t *mth)
/* mth must be main_thread */
if (rb_signal_buff_size() > 0) {
/* wakeup main thread */
- rb_threadptr_trap_interrupt(mth);
+ threadptr_trap_interrupt(mth);
}
}
static void
-timer_thread_function(void *arg)
+timer_thread_function(void)
{
- rb_vm_t *vm = GET_VM(); /* TODO: fix me for Multi-VM */
+ volatile rb_execution_context_t *ec;
- /*
- * Tricky: thread_destruct_lock doesn't close a race against
- * vm->running_thread switch. however it guarantees th->running_thread
- * point to valid pointer or NULL.
- */
- native_mutex_lock(&vm->thread_destruct_lock);
/* for time slice */
- if (vm->running_thread)
- RUBY_VM_SET_TIMER_INTERRUPT(vm->running_thread);
- native_mutex_unlock(&vm->thread_destruct_lock);
+ ec = ACCESS_ONCE(rb_execution_context_t *,
+ ruby_current_execution_context_ptr);
+ if (ec) RUBY_VM_SET_TIMER_INTERRUPT(ec);
+}
- /* check signal */
- rb_threadptr_check_signal(vm->main_thread);
+static void
+async_bug_fd(const char *mesg, int errno_arg, int fd)
+{
+ char buff[64];
+ size_t n = strlcpy(buff, mesg, sizeof(buff));
+ if (n < sizeof(buff)-3) {
+ ruby_snprintf(buff+n, sizeof(buff)-n, "(%d)", fd);
+ }
+ rb_async_bug_errno(buff, errno_arg);
+}
-#if 0
- /* prove profiler */
- if (vm->prove_profile.enable) {
- rb_thread_t *th = vm->running_thread;
+/* VM-dependent API is not available for this function */
+static int
+consume_communication_pipe(int fd)
+{
+#if USE_EVENTFD
+ uint64_t buff[1];
+#else
+ /* buffer can be shared because no one refers to them. */
+ static char buff[1024];
+#endif
+ ssize_t result;
+ int ret = FALSE; /* for rb_sigwait_sleep */
- if (vm->during_gc) {
- /* GC prove profiling */
+ /*
+ * disarm UBF_TIMER before we read, because it can become
+ * re-armed at any time via sighandler and the pipe will refill
+ * We can disarm it because this thread is now processing signals
+ * and we do not want unnecessary SIGVTALRM
+ */
+ ubf_timer_disarm();
+
+ while (1) {
+ result = read(fd, buff, sizeof(buff));
+ if (result > 0) {
+ ret = TRUE;
+ if (USE_EVENTFD || result < (ssize_t)sizeof(buff)) {
+ return ret;
+ }
}
- }
+ else if (result == 0) {
+ return ret;
+ }
+ else if (result < 0) {
+ int e = errno;
+ switch (e) {
+ case EINTR:
+ continue; /* retry */
+ case EAGAIN:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
#endif
+ return ret;
+ default:
+ async_bug_fd("consume_communication_pipe: read", e, fd);
+ }
+ }
+ }
+}
+
+static int
+check_signals_nogvl(rb_thread_t *th, int sigwait_fd)
+{
+ rb_vm_t *vm = GET_VM(); /* th may be 0 */
+ int ret = consume_communication_pipe(sigwait_fd);
+ ubf_wakeup_all_threads();
+ ruby_sigchld_handler(vm);
+ if (rb_signal_buff_size()) {
+ if (th == vm->main_thread)
+ /* no need to lock + wakeup if already in main thread */
+ RUBY_VM_SET_TRAP_INTERRUPT(th->ec);
+ else
+ threadptr_trap_interrupt(vm->main_thread);
+ ret = TRUE; /* for SIGCHLD_LOSSY && rb_sigwait_sleep */
+ }
+ return ret;
}
void
@@ -4043,30 +4371,46 @@ rb_thread_start_timer_thread(void)
rb_thread_create_timer_thread();
}
-#if defined(HAVE_WORKING_FORK)
static int
clear_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
{
int i;
- VALUE lines = (VALUE)val;
+ VALUE coverage = (VALUE)val;
+ VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
+ VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
- for (i = 0; i < RARRAY_LEN(lines); i++) {
- if (RARRAY_AREF(lines, i) != Qnil) {
- RARRAY_ASET(lines, i, INT2FIX(0));
+ if (lines) {
+ if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
+ rb_ary_clear(lines);
+ }
+ else {
+ int i;
+ for (i = 0; i < RARRAY_LEN(lines); i++) {
+ if (RARRAY_AREF(lines, i) != Qnil)
+ RARRAY_ASET(lines, i, INT2FIX(0));
+ }
+ }
+ }
+ if (branches) {
+ VALUE counters = RARRAY_AREF(branches, 1);
+ for (i = 0; i < RARRAY_LEN(counters); i++) {
+ RARRAY_ASET(counters, i, INT2FIX(0));
}
}
+
return ST_CONTINUE;
}
-static void
-clear_coverage(void)
+void
+rb_clear_coverages(void)
{
VALUE coverages = rb_get_coverages();
if (RTEST(coverages)) {
- st_foreach(rb_hash_tbl_raw(coverages), clear_coverage_i, 0);
+ rb_hash_foreach(coverages, clear_coverage_i, 0);
}
}
+#if defined(HAVE_WORKING_FORK)
static void
rb_thread_atfork_internal(rb_thread_t *th, void (*atfork)(rb_thread_t *, const rb_thread_t *))
{
@@ -4075,14 +4419,25 @@ rb_thread_atfork_internal(rb_thread_t *th, void (*atfork)(rb_thread_t *, const r
vm->main_thread = th;
gvl_atfork(th->vm);
+ ubf_list_atfork();
list_for_each(&vm->living_threads, i, vmlt_node) {
atfork(i, th);
}
rb_vm_living_threads_init(vm);
rb_vm_living_threads_insert(vm, th);
+
+ /* may be held by MJIT threads in parent */
+ rb_native_mutex_initialize(&vm->waitpid_lock);
+ rb_native_mutex_initialize(&vm->workqueue_lock);
+
+ /* may be held by any thread in parent */
+ rb_native_mutex_initialize(&th->interrupt_lock);
+
+ vm->fork_gen++;
+
vm->sleeper = 0;
- clear_coverage();
+ rb_clear_coverages();
}
static void
@@ -4095,15 +4450,20 @@ terminate_atfork_i(rb_thread_t *th, const rb_thread_t *current_th)
}
}
+void rb_fiber_atfork(rb_thread_t *);
void
rb_thread_atfork(void)
{
rb_thread_t *th = GET_THREAD();
rb_thread_atfork_internal(th, terminate_atfork_i);
th->join_list = NULL;
+ rb_fiber_atfork(th);
/* We don't want reproduce CVE-2003-0900. */
rb_reset_random_seed();
+
+ /* For child, starting MJIT worker thread in this place which is safer than immediately after `after_fork_ruby`. */
+ mjit_child_after_fork();
}
static void
@@ -4216,8 +4576,8 @@ thgroup_list(VALUE group)
* New threads can still be started in an enclosed ThreadGroup.
*
* ThreadGroup::Default.enclose #=> #<ThreadGroup:0x4029d914>
- * thr = Thread::new { Thread.stop } #=> #<Thread:0x402a7210 sleep>
- * tg = ThreadGroup::new #=> #<ThreadGroup:0x402752d4>
+ * thr = Thread.new { Thread.stop } #=> #<Thread:0x402a7210 sleep>
+ * tg = ThreadGroup.new #=> #<ThreadGroup:0x402752d4>
* tg.add thr
* #=> ThreadError: can't move from the enclosed thread group
*/
@@ -4282,11 +4642,9 @@ thgroup_enclosed_p(VALUE group)
static VALUE
thgroup_add(VALUE group, VALUE thread)
{
- rb_thread_t *th;
+ rb_thread_t *target_th = rb_thread_ptr(thread);
struct thgroup *data;
- GetThreadPtr(thread, th);
-
if (OBJ_FROZEN(group)) {
rb_raise(rb_eThreadError, "can't move to the frozen thread group");
}
@@ -4295,20 +4653,20 @@ thgroup_add(VALUE group, VALUE thread)
rb_raise(rb_eThreadError, "can't move to the enclosed thread group");
}
- if (!th->thgroup) {
+ if (!target_th->thgroup) {
return Qnil;
}
- if (OBJ_FROZEN(th->thgroup)) {
+ if (OBJ_FROZEN(target_th->thgroup)) {
rb_raise(rb_eThreadError, "can't move from the frozen thread group");
}
- TypedData_Get_Struct(th->thgroup, struct thgroup, &thgroup_data_type, data);
+ TypedData_Get_Struct(target_th->thgroup, struct thgroup, &thgroup_data_type, data);
if (data->enclosed) {
rb_raise(rb_eThreadError,
"can't move from the enclosed thread group");
}
- th->thgroup = group;
+ target_th->thgroup = group;
return group;
}
@@ -4382,7 +4740,7 @@ rb_thread_shield_wait(VALUE self)
rb_mutex_t *m;
if (!mutex) return Qfalse;
- GetMutexPtr(mutex, m);
+ m = mutex_ptr(mutex);
if (m->th == GET_THREAD()) return Qnil;
rb_thread_shield_waiting_inc(self);
rb_mutex_lock(mutex);
@@ -4427,13 +4785,13 @@ rb_thread_shield_destroy(VALUE self)
static VALUE
threadptr_recursive_hash(rb_thread_t *th)
{
- return th->local_storage_recursive_hash;
+ return th->ec->local_storage_recursive_hash;
}
static void
threadptr_recursive_hash_set(rb_thread_t *th, VALUE hash)
{
- th->local_storage_recursive_hash = hash;
+ th->ec->local_storage_recursive_hash = hash;
}
ID rb_frame_last_func(void);
@@ -4604,7 +4962,7 @@ exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE pairid, VALUE
return (*func)(obj, arg, TRUE);
}
else {
- int state;
+ enum ruby_tag_type state;
p.func = func;
@@ -4614,7 +4972,7 @@ exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE pairid, VALUE
result = rb_catch_protect(p.list, exec_recursive_i, (VALUE)&p, &state);
if (!recursive_pop(p.list, p.objid, p.pairid)) goto invalid;
if (!recursive_pop(p.list, ID2SYM(recursive_key), 0)) goto invalid;
- if (state) JUMP_TAG(state);
+ if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
if (result == p.list) {
result = (*func)(obj, arg, TRUE);
}
@@ -4622,18 +4980,18 @@ exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE pairid, VALUE
else {
volatile VALUE ret = Qundef;
recursive_push(p.list, p.objid, p.pairid);
- PUSH_TAG();
- if ((state = EXEC_TAG()) == 0) {
+ EC_PUSH_TAG(GET_EC());
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
ret = (*func)(obj, arg, FALSE);
}
- POP_TAG();
+ EC_POP_TAG();
if (!recursive_pop(p.list, p.objid, p.pairid)) {
invalid:
rb_raise(rb_eTypeError, "invalid inspect_tbl pair_list "
"for %+"PRIsVALUE" in %+"PRIsVALUE,
sym, rb_thread_current());
}
- if (state) JUMP_TAG(state);
+ if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
result = ret;
}
}
@@ -4780,6 +5138,7 @@ Init_Thread(void)
rb_define_method(rb_cThread, "wakeup", rb_thread_wakeup, 0);
rb_define_method(rb_cThread, "[]", rb_thread_aref, 1);
rb_define_method(rb_cThread, "[]=", rb_thread_aset, 2);
+ rb_define_method(rb_cThread, "fetch", rb_thread_fetch, -1);
rb_define_method(rb_cThread, "key?", rb_thread_key_p, 1);
rb_define_method(rb_cThread, "keys", rb_thread_keys, 0);
rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
@@ -4802,9 +5161,11 @@ Init_Thread(void)
rb_define_method(rb_cThread, "name", rb_thread_getname, 0);
rb_define_method(rb_cThread, "name=", rb_thread_setname, 1);
- rb_define_method(rb_cThread, "inspect", rb_thread_inspect, 0);
+ rb_define_method(rb_cThread, "to_s", rb_thread_to_s, 0);
+ rb_define_alias(rb_cThread, "inspect", "to_s");
- rb_vm_register_special_exception(ruby_error_closed_stream, rb_eIOError, "stream closed");
+ rb_vm_register_special_exception(ruby_error_stream_closed, rb_eIOError,
+ "stream closed in another thread");
cThGroup = rb_define_class("ThreadGroup", rb_cObject);
rb_define_alloc_func(cThGroup, thgroup_s_alloc);
@@ -4828,16 +5189,13 @@ Init_Thread(void)
/* acquire global vm lock */
gvl_init(th->vm);
gvl_acquire(th->vm, th);
- native_mutex_initialize(&th->vm->thread_destruct_lock);
- native_mutex_initialize(&th->interrupt_lock);
- native_cond_initialize(&th->interrupt_cond,
- RB_CONDATTR_CLOCK_MONOTONIC);
+ rb_native_mutex_initialize(&th->vm->waitpid_lock);
+ rb_native_mutex_initialize(&th->vm->workqueue_lock);
+ rb_native_mutex_initialize(&th->interrupt_lock);
th->pending_interrupt_queue = rb_ary_tmp_new(0);
th->pending_interrupt_queue_checked = 0;
th->pending_interrupt_mask_stack = rb_ary_tmp_new(0);
-
- th->interrupt_mask = 0;
}
}
@@ -4857,7 +5215,6 @@ ruby_native_thread_p(void)
return th != 0;
}
-VALUE rb_vm_backtrace_str_ary(rb_thread_t *th, long lev, long n);
static void
debug_deadlock_check(rb_vm_t *vm, VALUE msg)
{
@@ -4865,32 +5222,25 @@ debug_deadlock_check(rb_vm_t *vm, VALUE msg)
VALUE sep = rb_str_new_cstr("\n ");
rb_str_catf(msg, "\n%d threads, %d sleeps current:%p main thread:%p\n",
- vm_living_thread_num(vm), vm->sleeper, GET_THREAD(), vm->main_thread);
+ vm_living_thread_num(vm), vm->sleeper, (void *)GET_THREAD(), (void *)vm->main_thread);
list_for_each(&vm->living_threads, th, vmlt_node) {
rb_str_catf(msg, "* %+"PRIsVALUE"\n rb_thread_t:%p "
"native:%"PRI_THREAD_ID" int:%u",
- th->self, th, thread_id_str(th), th->interrupt_flag);
+ th->self, (void *)th, thread_id_str(th), th->ec->interrupt_flag);
if (th->locking_mutex) {
- rb_mutex_t *mutex;
- struct rb_thread_struct volatile *mth;
- int waiting;
- GetMutexPtr(th->locking_mutex, mutex);
-
- native_mutex_lock(&mutex->lock);
- mth = mutex->th;
- waiting = mutex->cond_waiting;
- native_mutex_unlock(&mutex->lock);
- rb_str_catf(msg, " mutex:%p cond:%d", mth, waiting);
+ rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
+ rb_str_catf(msg, " mutex:%p cond:%"PRIuSIZE,
+ (void *)mutex->th, rb_mutex_num_waiting(mutex));
}
{
rb_thread_list_t *list = th->join_list;
while (list) {
- rb_str_catf(msg, "\n depended by: tb_thread_id:%p", list->th);
+ rb_str_catf(msg, "\n depended by: tb_thread_id:%p", (void *)list->th);
list = list->next;
}
}
rb_str_catf(msg, "\n ");
- rb_str_concat(msg, rb_ary_join(rb_vm_backtrace_str_ary(th, 0, 0), sep));
+ rb_str_concat(msg, rb_ary_join(rb_ec_backtrace_str_ary(th->ec, 0, 0), sep));
rb_str_catf(msg, "\n");
}
}
@@ -4906,18 +5256,15 @@ rb_check_deadlock(rb_vm_t *vm)
if (patrol_thread && patrol_thread != GET_THREAD()) return;
list_for_each(&vm->living_threads, th, vmlt_node) {
- if (th->status != THREAD_STOPPED_FOREVER || RUBY_VM_INTERRUPTED(th)) {
+ if (th->status != THREAD_STOPPED_FOREVER || RUBY_VM_INTERRUPTED(th->ec)) {
found = 1;
}
else if (th->locking_mutex) {
- rb_mutex_t *mutex;
- GetMutexPtr(th->locking_mutex, mutex);
+ rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
- native_mutex_lock(&mutex->lock);
- if (mutex->th == th || (!mutex->th && mutex->cond_waiting)) {
+ if (mutex->th == th || (!mutex->th && !list_empty(&mutex->waitq))) {
found = 1;
}
- native_mutex_unlock(&mutex->lock);
}
if (found)
break;
@@ -4934,22 +5281,129 @@ rb_check_deadlock(rb_vm_t *vm)
}
static void
-update_coverage(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
+update_line_coverage(VALUE data, const rb_trace_arg_t *trace_arg)
+{
+ const rb_control_frame_t *cfp = GET_EC()->cfp;
+ VALUE coverage = rb_iseq_coverage(cfp->iseq);
+ if (RB_TYPE_P(coverage, T_ARRAY) && !RBASIC_CLASS(coverage)) {
+ VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
+ if (lines) {
+ long line = rb_sourceline() - 1;
+ long count;
+ VALUE num;
+ void rb_iseq_clear_event_flags(const rb_iseq_t *iseq, size_t pos, rb_event_flag_t reset);
+ if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
+ rb_iseq_clear_event_flags(cfp->iseq, cfp->pc - cfp->iseq->body->iseq_encoded - 1, RUBY_EVENT_COVERAGE_LINE);
+ rb_ary_push(lines, LONG2FIX(line + 1));
+ return;
+ }
+ if (line >= RARRAY_LEN(lines)) { /* no longer tracked */
+ return;
+ }
+ num = RARRAY_AREF(lines, line);
+ if (!FIXNUM_P(num)) return;
+ count = FIX2LONG(num) + 1;
+ if (POSFIXABLE(count)) {
+ RARRAY_ASET(lines, line, LONG2FIX(count));
+ }
+ }
+ }
+}
+
+static void
+update_branch_coverage(VALUE data, const rb_trace_arg_t *trace_arg)
{
- VALUE coverage = rb_iseq_coverage(GET_THREAD()->cfp->iseq);
+ const rb_control_frame_t *cfp = GET_EC()->cfp;
+ VALUE coverage = rb_iseq_coverage(cfp->iseq);
if (RB_TYPE_P(coverage, T_ARRAY) && !RBASIC_CLASS(coverage)) {
- long line = rb_sourceline() - 1;
- long count;
- VALUE num;
- if (line >= RARRAY_LEN(coverage)) { /* no longer tracked */
- return;
+ VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
+ if (branches) {
+ long pc = cfp->pc - cfp->iseq->body->iseq_encoded - 1;
+ long idx = FIX2INT(RARRAY_AREF(ISEQ_PC2BRANCHINDEX(cfp->iseq), pc)), count;
+ VALUE counters = RARRAY_AREF(branches, 1);
+ VALUE num = RARRAY_AREF(counters, idx);
+ count = FIX2LONG(num) + 1;
+ if (POSFIXABLE(count)) {
+ RARRAY_ASET(counters, idx, LONG2FIX(count));
+ }
}
- num = RARRAY_AREF(coverage, line);
- if (!FIXNUM_P(num)) return;
- count = FIX2LONG(num) + 1;
- if (POSFIXABLE(count)) {
- RARRAY_ASET(coverage, line, LONG2FIX(count));
+ }
+}
+
+const rb_method_entry_t *
+rb_resolve_me_location(const rb_method_entry_t *me, VALUE resolved_location[5])
+{
+ VALUE path, beg_pos_lineno, beg_pos_column, end_pos_lineno, end_pos_column;
+
+ retry:
+ switch (me->def->type) {
+ case VM_METHOD_TYPE_ISEQ: {
+ const rb_iseq_t *iseq = me->def->body.iseq.iseqptr;
+ rb_iseq_location_t *loc = &iseq->body->location;
+ path = rb_iseq_path(iseq);
+ beg_pos_lineno = INT2FIX(loc->code_location.beg_pos.lineno);
+ beg_pos_column = INT2FIX(loc->code_location.beg_pos.column);
+ end_pos_lineno = INT2FIX(loc->code_location.end_pos.lineno);
+ end_pos_column = INT2FIX(loc->code_location.end_pos.column);
+ break;
+ }
+ case VM_METHOD_TYPE_BMETHOD: {
+ const rb_iseq_t *iseq = rb_proc_get_iseq(me->def->body.bmethod.proc, 0);
+ if (iseq) {
+ rb_iseq_location_t *loc;
+ rb_iseq_check(iseq);
+ path = rb_iseq_path(iseq);
+ loc = &iseq->body->location;
+ beg_pos_lineno = INT2FIX(loc->code_location.beg_pos.lineno);
+ beg_pos_column = INT2FIX(loc->code_location.beg_pos.column);
+ end_pos_lineno = INT2FIX(loc->code_location.end_pos.lineno);
+ end_pos_column = INT2FIX(loc->code_location.end_pos.column);
+ break;
}
+ return NULL;
+ }
+ case VM_METHOD_TYPE_ALIAS:
+ me = me->def->body.alias.original_me;
+ goto retry;
+ case VM_METHOD_TYPE_REFINED:
+ me = me->def->body.refined.orig_me;
+ if (!me) return NULL;
+ goto retry;
+ default:
+ return NULL;
+ }
+
+ /* found */
+ if (RB_TYPE_P(path, T_ARRAY)) {
+ path = rb_ary_entry(path, 1);
+ if (!RB_TYPE_P(path, T_STRING)) return NULL; /* just for the case... */
+ }
+ if (resolved_location) {
+ resolved_location[0] = path;
+ resolved_location[1] = beg_pos_lineno;
+ resolved_location[2] = beg_pos_column;
+ resolved_location[3] = end_pos_lineno;
+ resolved_location[4] = end_pos_column;
+ }
+ return me;
+}
+
+static void
+update_method_coverage(VALUE me2counter, rb_trace_arg_t *trace_arg)
+{
+ const rb_control_frame_t *cfp = GET_EC()->cfp;
+ const rb_callable_method_entry_t *cme = rb_vm_frame_method_entry(cfp);
+ const rb_method_entry_t *me = (const rb_method_entry_t *)cme;
+ VALUE rcount;
+ long count;
+
+ me = rb_resolve_me_location(me, 0);
+ if (!me) return;
+
+ rcount = rb_hash_aref(me2counter, (VALUE) me);
+ count = FIXNUM_P(rcount) ? FIX2LONG(rcount) + 1 : 1;
+ if (POSFIXABLE(count)) {
+ rb_hash_aset(me2counter, (VALUE) me, LONG2FIX(count));
}
}
@@ -4959,29 +5413,75 @@ rb_get_coverages(void)
return GET_VM()->coverages;
}
-void
-rb_set_coverages(VALUE coverages)
+int
+rb_get_coverage_mode(void)
{
- GET_VM()->coverages = coverages;
- rb_add_event_hook(update_coverage, RUBY_EVENT_COVERAGE, Qnil);
+ return GET_VM()->coverage_mode;
}
-/* Make coverage arrays empty so old covered files are no longer tracked. */
-static int
-reset_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
+void
+rb_set_coverages(VALUE coverages, int mode, VALUE me2counter)
{
- VALUE coverage = (VALUE)val;
- rb_ary_clear(coverage);
- return ST_CONTINUE;
+ GET_VM()->coverages = coverages;
+ GET_VM()->coverage_mode = mode;
+ rb_add_event_hook2((rb_event_hook_func_t) update_line_coverage, RUBY_EVENT_COVERAGE_LINE, Qnil, RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
+ if (mode & COVERAGE_TARGET_BRANCHES) {
+ rb_add_event_hook2((rb_event_hook_func_t) update_branch_coverage, RUBY_EVENT_COVERAGE_BRANCH, Qnil, RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
+ }
+ if (mode & COVERAGE_TARGET_METHODS) {
+ rb_add_event_hook2((rb_event_hook_func_t) update_method_coverage, RUBY_EVENT_CALL, me2counter, RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
+ }
}
+/* Make coverage arrays empty so old covered files are no longer tracked. */
void
rb_reset_coverages(void)
{
- VALUE coverages = rb_get_coverages();
- st_foreach(rb_hash_tbl_raw(coverages), reset_coverage_i, 0);
+ rb_clear_coverages();
+ rb_iseq_remove_coverage_all();
GET_VM()->coverages = Qfalse;
- rb_remove_event_hook(update_coverage);
+ rb_remove_event_hook((rb_event_hook_func_t) update_line_coverage);
+ if (GET_VM()->coverage_mode & COVERAGE_TARGET_BRANCHES) {
+ rb_remove_event_hook((rb_event_hook_func_t) update_branch_coverage);
+ }
+ if (GET_VM()->coverage_mode & COVERAGE_TARGET_METHODS) {
+ rb_remove_event_hook((rb_event_hook_func_t) update_method_coverage);
+ }
+}
+
+VALUE
+rb_default_coverage(int n)
+{
+ VALUE coverage = rb_ary_tmp_new_fill(3);
+ VALUE lines = Qfalse, branches = Qfalse;
+ int mode = GET_VM()->coverage_mode;
+
+ if (mode & COVERAGE_TARGET_LINES) {
+ lines = n > 0 ? rb_ary_tmp_new_fill(n) : rb_ary_tmp_new(0);
+ }
+ RARRAY_ASET(coverage, COVERAGE_INDEX_LINES, lines);
+
+ if (mode & COVERAGE_TARGET_BRANCHES) {
+ branches = rb_ary_tmp_new_fill(2);
+ /* internal data structures for branch coverage:
+ *
+ * [[base_type, base_first_lineno, base_first_column, base_last_lineno, base_last_column,
+ * target_type_1, target_first_lineno_1, target_first_column_1, target_last_lineno_1, target_last_column_1, target_counter_index_1,
+ * target_type_2, target_first_lineno_2, target_first_column_2, target_last_lineno_2, target_last_column_2, target_counter_index_2, ...],
+ * ...]
+ *
+ * Example: [[:case, 1, 0, 4, 3,
+ * :when, 2, 8, 2, 9, 0,
+ * :when, 3, 8, 3, 9, 1, ...],
+ * ...]
+ */
+ RARRAY_ASET(branches, 0, rb_ary_tmp_new(0));
+ /* branch execution counters */
+ RARRAY_ASET(branches, 1, rb_ary_tmp_new(0));
+ }
+ RARRAY_ASET(coverage, COVERAGE_INDEX_BRANCHES, branches);
+
+ return coverage;
}
VALUE
@@ -4996,26 +5496,3 @@ rb_uninterruptible(VALUE (*b_proc)(ANYARGS), VALUE data)
return rb_ensure(b_proc, data, rb_ary_pop, cur_th->pending_interrupt_mask_stack);
}
-
-void
-ruby_kill(rb_pid_t pid, int sig)
-{
- int err;
- rb_thread_t *th = GET_THREAD();
-
- /*
- * When target pid is self, many caller assume signal will be
- * delivered immediately and synchronously.
- */
- {
- GVL_UNLOCK_BEGIN();
- native_mutex_lock(&th->interrupt_lock);
- err = kill(pid, sig);
- native_cond_wait(&th->interrupt_cond, &th->interrupt_lock);
- native_mutex_unlock(&th->interrupt_lock);
- GVL_UNLOCK_END();
- }
- if (err < 0) {
- rb_sys_fail(0);
- }
-}
diff --git a/thread_pthread.c b/thread_pthread.c
index 43644e7c06..96d2ca9cb3 100644
--- a/thread_pthread.c
+++ b/thread_pthread.c
@@ -12,6 +12,7 @@
#ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
#include "gc.h"
+#include "mjit.h"
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
@@ -27,147 +28,327 @@
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif
-#if defined(__native_client__) && defined(NACL_NEWLIB)
-# include "nacl/select.h"
-#endif
#if defined(HAVE_SYS_TIME_H)
#include <sys/time.h>
#endif
#if defined(__HAIKU__)
#include <kernel/OS.h>
#endif
+#include <time.h>
+#include <signal.h>
-static void native_mutex_lock(rb_nativethread_lock_t *lock);
-static void native_mutex_unlock(rb_nativethread_lock_t *lock);
-static int native_mutex_trylock(rb_nativethread_lock_t *lock);
-static void native_mutex_initialize(rb_nativethread_lock_t *lock);
-static void native_mutex_destroy(rb_nativethread_lock_t *lock);
-static void native_cond_signal(rb_nativethread_cond_t *cond);
-static void native_cond_broadcast(rb_nativethread_cond_t *cond);
-static void native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex);
-static void native_cond_initialize(rb_nativethread_cond_t *cond, int flags);
-static void native_cond_destroy(rb_nativethread_cond_t *cond);
-static void rb_thread_wakeup_timer_thread_low(void);
+#if defined(HAVE_SYS_EVENTFD_H) && defined(HAVE_EVENTFD)
+# define USE_EVENTFD (1)
+# include <sys/eventfd.h>
+#else
+# define USE_EVENTFD (0)
+#endif
+
+#if defined(SIGVTALRM) && !defined(__CYGWIN__)
+# define USE_UBF_LIST 1
+#endif
+
+/*
+ * UBF_TIMER and ubf_list both use SIGVTALRM.
+ *
+ * UBF_TIMER has NOTHING to do with thread timeslices (TIMER_INTERRUPT_MASK)
+ *
+ * UBF_TIMER is to close TOCTTOU signal race on programs where we
+ * cannot rely on GVL contention (vm->gvl.timer) to perform wakeups
+ * while a thread is doing blocking I/O on sockets or pipes. With
+ * rb_thread_call_without_gvl and similar functions:
+ *
+ * (1) Check interrupts.
+ * (2) release GVL.
+ * (2a) signal received
+ * (3) call func with data1 (blocks for a long time without ubf_timer)
+ * (4) acquire GVL.
+ * Other Ruby threads can not run in parallel any more.
+ * (5) Check interrupts.
+ *
+ * We need UBF_TIMER to break out of (3) if (2a) happens.
+ *
+ * ubf_list wakeups may be triggered on gvl_yield.
+ *
+ * If we have vm->gvl.timer (on GVL contention), we don't need UBF_TIMER
+ * as it can perform the same tasks while doing timeslices.
+ */
+#define UBF_TIMER_NONE 0
+#define UBF_TIMER_POSIX 1
+#define UBF_TIMER_PTHREAD 2
+
+#ifndef UBF_TIMER
+# if defined(HAVE_TIMER_SETTIME) && defined(HAVE_TIMER_CREATE) && \
+ defined(CLOCK_MONOTONIC) && defined(USE_UBF_LIST)
+ /* preferred */
+# define UBF_TIMER UBF_TIMER_POSIX
+# elif defined(USE_UBF_LIST)
+ /* safe, but inefficient */
+# define UBF_TIMER UBF_TIMER_PTHREAD
+# else
+ /* we'll be racy without SIGVTALRM for ubf_list */
+# define UBF_TIMER UBF_TIMER_NONE
+# endif
+#endif
+
+enum rtimer_state {
+ /* alive, after timer_create: */
+ RTIMER_DISARM,
+ RTIMER_ARMING,
+ RTIMER_ARMED,
+
+ RTIMER_DEAD
+};
+
+#if UBF_TIMER == UBF_TIMER_POSIX
+static const struct itimerspec zero;
static struct {
- pthread_t id;
- int created;
-} timer_thread;
-#define TIMER_THREAD_CREATED_P() (timer_thread.created != 0)
+ rb_atomic_t state; /* rtimer_state */
+ rb_pid_t owner;
+ timer_t timerid;
+} timer_posix = {
+ /* .state = */ RTIMER_DEAD,
+};
-#define RB_CONDATTR_CLOCK_MONOTONIC 1
+#elif UBF_TIMER == UBF_TIMER_PTHREAD
+static void *timer_pthread_fn(void *);
+static struct {
+ int low[2];
+ rb_atomic_t armed; /* boolean */
+ rb_pid_t owner;
+ pthread_t thid;
+} timer_pthread = {
+ { -1, -1 },
+};
+#endif
-#if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined(HAVE_CLOCKID_T) && \
- defined(CLOCK_REALTIME) && defined(CLOCK_MONOTONIC) && \
- defined(HAVE_CLOCK_GETTIME) && defined(HAVE_PTHREAD_CONDATTR_INIT)
-#define USE_MONOTONIC_COND 1
+void rb_native_mutex_lock(rb_nativethread_lock_t *lock);
+void rb_native_mutex_unlock(rb_nativethread_lock_t *lock);
+static int native_mutex_trylock(rb_nativethread_lock_t *lock);
+void rb_native_mutex_initialize(rb_nativethread_lock_t *lock);
+void rb_native_mutex_destroy(rb_nativethread_lock_t *lock);
+void rb_native_cond_signal(rb_nativethread_cond_t *cond);
+void rb_native_cond_broadcast(rb_nativethread_cond_t *cond);
+void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex);
+void rb_native_cond_initialize(rb_nativethread_cond_t *cond);
+void rb_native_cond_destroy(rb_nativethread_cond_t *cond);
+static void clear_thread_cache_altstack(void);
+static void ubf_wakeup_all_threads(void);
+static int ubf_threads_empty(void);
+static int native_cond_timedwait(rb_nativethread_cond_t *, pthread_mutex_t *,
+ const rb_hrtime_t *abs);
+static const rb_hrtime_t *sigwait_timeout(rb_thread_t *, int sigwait_fd,
+ const rb_hrtime_t *,
+ int *drained_p);
+static void ubf_timer_disarm(void);
+static void threadptr_trap_interrupt(rb_thread_t *);
+
+#define TIMER_THREAD_CREATED_P() (signal_self_pipe.owner_process == getpid())
+
+/* for testing, and in case we come across a platform w/o pipes: */
+#define BUSY_WAIT_SIGNALS (0)
+
+/*
+ * sigwait_th is the thread which owns sigwait_fd and sleeps on it
+ * (using ppoll). MJIT worker can be sigwait_th==0, so we initialize
+ * it to THREAD_INVALID at startup and fork time. It is the ONLY thread
+ * allowed to read from sigwait_fd, otherwise starvation can occur.
+ */
+#define THREAD_INVALID ((const rb_thread_t *)-1)
+static const rb_thread_t *sigwait_th;
+
+#ifdef HAVE_SCHED_YIELD
+#define native_thread_yield() (void)sched_yield()
#else
-#define USE_MONOTONIC_COND 0
+#define native_thread_yield() ((void)0)
#endif
-#if defined(HAVE_POLL) && defined(HAVE_FCNTL) && defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK) && !defined(__native_client__)
-/* The timer thread sleeps while only one Ruby thread is running. */
-# define USE_SLEEPY_TIMER_THREAD 1
+#if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && \
+ defined(CLOCK_REALTIME) && defined(CLOCK_MONOTONIC) && \
+ defined(HAVE_CLOCK_GETTIME)
+static pthread_condattr_t condattr_mono;
+static pthread_condattr_t *condattr_monotonic = &condattr_mono;
#else
-# define USE_SLEEPY_TIMER_THREAD 0
+static const void *const condattr_monotonic = NULL;
#endif
+/* 100ms. 10ms is too small for user level thread scheduling
+ * on recent Linux (tested on 2.6.35)
+ */
+#define TIME_QUANTUM_MSEC (100)
+#define TIME_QUANTUM_USEC (TIME_QUANTUM_MSEC * 1000)
+#define TIME_QUANTUM_NSEC (TIME_QUANTUM_USEC * 1000)
+
+static rb_hrtime_t native_cond_timeout(rb_nativethread_cond_t *, rb_hrtime_t);
+
+/*
+ * Designate the next gvl.timer thread, favor the last thread in
+ * the waitq since it will be in waitq longest
+ */
+static int
+designate_timer_thread(rb_vm_t *vm)
+{
+ native_thread_data_t *last;
+
+ last = list_tail(&vm->gvl.waitq, native_thread_data_t, node.ubf);
+ if (last) {
+ rb_native_cond_signal(&last->cond.gvlq);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * We become designated timer thread to kick vm->gvl.owner
+ * periodically. Continue on old timeout if it expired.
+ */
static void
-gvl_acquire_common(rb_vm_t *vm)
+do_gvl_timer(rb_vm_t *vm, rb_thread_t *th)
{
- if (vm->gvl.acquired) {
+ static rb_hrtime_t abs;
+ native_thread_data_t *nd = &th->native_thread_data;
- vm->gvl.waiting++;
- if (vm->gvl.waiting == 1) {
- /*
- * Wake up timer thread iff timer thread is slept.
- * When timer thread is polling mode, we don't want to
- * make confusing timer thread interval time.
- */
- rb_thread_wakeup_timer_thread_low();
- }
+ vm->gvl.timer = th;
- while (vm->gvl.acquired) {
- native_cond_wait(&vm->gvl.cond, &vm->gvl.lock);
- }
+ /* take over wakeups from UBF_TIMER */
+ ubf_timer_disarm();
- vm->gvl.waiting--;
+ if (vm->gvl.timer_err == ETIMEDOUT) {
+ abs = native_cond_timeout(&nd->cond.gvlq, TIME_QUANTUM_NSEC);
+ }
+ vm->gvl.timer_err = native_cond_timedwait(&nd->cond.gvlq, &vm->gvl.lock, &abs);
- if (vm->gvl.need_yield) {
- vm->gvl.need_yield = 0;
- native_cond_signal(&vm->gvl.switch_cond);
- }
+ ubf_wakeup_all_threads();
+ ruby_sigchld_handler(vm);
+ if (UNLIKELY(rb_signal_buff_size())) {
+ if (th == vm->main_thread) {
+ RUBY_VM_SET_TRAP_INTERRUPT(th->ec);
+ }
+ else {
+ threadptr_trap_interrupt(vm->main_thread);
+ }
}
- vm->gvl.acquired = 1;
+ /*
+ * Timeslice. Warning: the process may fork while this
+ * thread is contending for GVL:
+ */
+ if (vm->gvl.owner) timer_thread_function();
+ vm->gvl.timer = 0;
}
static void
-gvl_acquire(rb_vm_t *vm, rb_thread_t *th)
+gvl_acquire_common(rb_vm_t *vm, rb_thread_t *th)
{
- native_mutex_lock(&vm->gvl.lock);
- gvl_acquire_common(vm);
- native_mutex_unlock(&vm->gvl.lock);
+ if (vm->gvl.owner) {
+ native_thread_data_t *nd = &th->native_thread_data;
+
+ VM_ASSERT(th->unblock.func == 0 &&
+ "we must not be in ubf_list and GVL waitq at the same time");
+
+ list_add_tail(&vm->gvl.waitq, &nd->node.gvl);
+
+ do {
+ if (!vm->gvl.timer) {
+ do_gvl_timer(vm, th);
+ }
+ else {
+ rb_native_cond_wait(&nd->cond.gvlq, &vm->gvl.lock);
+ }
+ } while (vm->gvl.owner);
+
+ list_del_init(&nd->node.gvl);
+
+ if (vm->gvl.need_yield) {
+ vm->gvl.need_yield = 0;
+ rb_native_cond_signal(&vm->gvl.switch_cond);
+ }
+ }
+ else { /* reset timer if uncontended */
+ vm->gvl.timer_err = ETIMEDOUT;
+ }
+ vm->gvl.owner = th;
+ if (!vm->gvl.timer) {
+ if (!designate_timer_thread(vm) && !ubf_threads_empty()) {
+ rb_thread_wakeup_timer_thread(-1);
+ }
+ }
}
static void
+gvl_acquire(rb_vm_t *vm, rb_thread_t *th)
+{
+ rb_native_mutex_lock(&vm->gvl.lock);
+ gvl_acquire_common(vm, th);
+ rb_native_mutex_unlock(&vm->gvl.lock);
+}
+
+static const native_thread_data_t *
gvl_release_common(rb_vm_t *vm)
{
- vm->gvl.acquired = 0;
- if (vm->gvl.waiting > 0)
- native_cond_signal(&vm->gvl.cond);
+ native_thread_data_t *next;
+ vm->gvl.owner = 0;
+ next = list_top(&vm->gvl.waitq, native_thread_data_t, node.ubf);
+ if (next) rb_native_cond_signal(&next->cond.gvlq);
+
+ return next;
}
static void
gvl_release(rb_vm_t *vm)
{
- native_mutex_lock(&vm->gvl.lock);
+ rb_native_mutex_lock(&vm->gvl.lock);
gvl_release_common(vm);
- native_mutex_unlock(&vm->gvl.lock);
+ rb_native_mutex_unlock(&vm->gvl.lock);
}
static void
gvl_yield(rb_vm_t *vm, rb_thread_t *th)
{
- native_mutex_lock(&vm->gvl.lock);
+ const native_thread_data_t *next;
- gvl_release_common(vm);
+ /*
+ * Perhaps other threads are stuck in blocking region w/o GVL, too,
+ * (perhaps looping in io_close_fptr) so we kick them:
+ */
+ ubf_wakeup_all_threads();
+ rb_native_mutex_lock(&vm->gvl.lock);
+ next = gvl_release_common(vm);
/* An another thread is processing GVL yield. */
if (UNLIKELY(vm->gvl.wait_yield)) {
- while (vm->gvl.wait_yield)
- native_cond_wait(&vm->gvl.switch_wait_cond, &vm->gvl.lock);
- goto acquire;
- }
-
- if (vm->gvl.waiting > 0) {
- /* Wait until another thread task take GVL. */
- vm->gvl.need_yield = 1;
- vm->gvl.wait_yield = 1;
- while (vm->gvl.need_yield)
- native_cond_wait(&vm->gvl.switch_cond, &vm->gvl.lock);
- vm->gvl.wait_yield = 0;
+ while (vm->gvl.wait_yield)
+ rb_native_cond_wait(&vm->gvl.switch_wait_cond, &vm->gvl.lock);
+ }
+ else if (next) {
+ /* Wait until another thread task takes GVL. */
+ vm->gvl.need_yield = 1;
+ vm->gvl.wait_yield = 1;
+ while (vm->gvl.need_yield)
+ rb_native_cond_wait(&vm->gvl.switch_cond, &vm->gvl.lock);
+ vm->gvl.wait_yield = 0;
+ rb_native_cond_broadcast(&vm->gvl.switch_wait_cond);
}
else {
- native_mutex_unlock(&vm->gvl.lock);
- sched_yield();
- native_mutex_lock(&vm->gvl.lock);
+ rb_native_mutex_unlock(&vm->gvl.lock);
+ native_thread_yield();
+ rb_native_mutex_lock(&vm->gvl.lock);
+ rb_native_cond_broadcast(&vm->gvl.switch_wait_cond);
}
-
- native_cond_broadcast(&vm->gvl.switch_wait_cond);
- acquire:
- gvl_acquire_common(vm);
- native_mutex_unlock(&vm->gvl.lock);
+ gvl_acquire_common(vm, th);
+ rb_native_mutex_unlock(&vm->gvl.lock);
}
static void
gvl_init(rb_vm_t *vm)
{
- native_mutex_initialize(&vm->gvl.lock);
- native_cond_initialize(&vm->gvl.cond, RB_CONDATTR_CLOCK_MONOTONIC);
- native_cond_initialize(&vm->gvl.switch_cond, RB_CONDATTR_CLOCK_MONOTONIC);
- native_cond_initialize(&vm->gvl.switch_wait_cond, RB_CONDATTR_CLOCK_MONOTONIC);
- vm->gvl.acquired = 0;
- vm->gvl.waiting = 0;
+ rb_native_mutex_initialize(&vm->gvl.lock);
+ rb_native_cond_initialize(&vm->gvl.switch_cond);
+ rb_native_cond_initialize(&vm->gvl.switch_wait_cond);
+ list_head_init(&vm->gvl.waitq);
+ vm->gvl.owner = 0;
+ vm->gvl.timer = 0;
+ vm->gvl.timer_err = ETIMEDOUT;
vm->gvl.need_yield = 0;
vm->gvl.wait_yield = 0;
}
@@ -175,16 +356,25 @@ gvl_init(rb_vm_t *vm)
static void
gvl_destroy(rb_vm_t *vm)
{
- native_cond_destroy(&vm->gvl.switch_wait_cond);
- native_cond_destroy(&vm->gvl.switch_cond);
- native_cond_destroy(&vm->gvl.cond);
- native_mutex_destroy(&vm->gvl.lock);
+ /*
+ * only called once at VM shutdown (not atfork), another thread
+ * may still grab vm->gvl.lock when calling gvl_release at
+ * the end of thread_start_func_2
+ */
+ if (0) {
+ rb_native_cond_destroy(&vm->gvl.switch_wait_cond);
+ rb_native_cond_destroy(&vm->gvl.switch_cond);
+ rb_native_mutex_destroy(&vm->gvl.lock);
+ }
+ clear_thread_cache_altstack();
}
#if defined(HAVE_WORKING_FORK)
+static void thread_cache_reset(void);
static void
gvl_atfork(rb_vm_t *vm)
{
+ thread_cache_reset();
gvl_init(vm);
gvl_acquire(vm, GET_THREAD());
}
@@ -205,8 +395,8 @@ mutex_debug(const char *msg, void *lock)
}
}
-static void
-native_mutex_lock(pthread_mutex_t *lock)
+void
+rb_native_mutex_lock(pthread_mutex_t *lock)
{
int r;
mutex_debug("lock", lock);
@@ -215,8 +405,8 @@ native_mutex_lock(pthread_mutex_t *lock)
}
}
-static void
-native_mutex_unlock(pthread_mutex_t *lock)
+void
+rb_native_mutex_unlock(pthread_mutex_t *lock)
{
int r;
mutex_debug("unlock", lock);
@@ -241,8 +431,8 @@ native_mutex_trylock(pthread_mutex_t *lock)
return 0;
}
-static void
-native_mutex_initialize(pthread_mutex_t *lock)
+void
+rb_native_mutex_initialize(pthread_mutex_t *lock)
{
int r = pthread_mutex_init(lock, 0);
mutex_debug("init", lock);
@@ -251,8 +441,8 @@ native_mutex_initialize(pthread_mutex_t *lock)
}
}
-static void
-native_mutex_destroy(pthread_mutex_t *lock)
+void
+rb_native_mutex_destroy(pthread_mutex_t *lock)
{
int r = pthread_mutex_destroy(lock);
mutex_debug("destroy", lock);
@@ -261,46 +451,22 @@ native_mutex_destroy(pthread_mutex_t *lock)
}
}
-static void
-native_cond_initialize(rb_nativethread_cond_t *cond, int flags)
+void
+rb_native_cond_initialize(rb_nativethread_cond_t *cond)
{
-#ifdef HAVE_PTHREAD_COND_INIT
- int r;
-# if USE_MONOTONIC_COND
- pthread_condattr_t attr;
-
- pthread_condattr_init(&attr);
-
- cond->clockid = CLOCK_REALTIME;
- if (flags & RB_CONDATTR_CLOCK_MONOTONIC) {
- r = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
- if (r == 0) {
- cond->clockid = CLOCK_MONOTONIC;
- }
- }
-
- r = pthread_cond_init(&cond->cond, &attr);
- pthread_condattr_destroy(&attr);
-# else
- r = pthread_cond_init(&cond->cond, NULL);
-# endif
+ int r = pthread_cond_init(cond, condattr_monotonic);
if (r != 0) {
rb_bug_errno("pthread_cond_init", r);
}
-
- return;
-#endif
}
-static void
-native_cond_destroy(rb_nativethread_cond_t *cond)
+void
+rb_native_cond_destroy(rb_nativethread_cond_t *cond)
{
-#ifdef HAVE_PTHREAD_COND_INIT
- int r = pthread_cond_destroy(&cond->cond);
+ int r = pthread_cond_destroy(cond);
if (r != 0) {
rb_bug_errno("pthread_cond_destroy", r);
}
-#endif
}
/*
@@ -309,47 +475,49 @@ native_cond_destroy(rb_nativethread_cond_t *cond)
*
* http://www.opensource.apple.com/source/Libc/Libc-763.11/pthreads/pthread_cond.c
*
- * The following native_cond_signal and native_cond_broadcast functions
+ * The following rb_native_cond_signal and rb_native_cond_broadcast functions
* need to retrying until pthread functions don't return EAGAIN.
*/
-static void
-native_cond_signal(rb_nativethread_cond_t *cond)
+void
+rb_native_cond_signal(rb_nativethread_cond_t *cond)
{
int r;
do {
- r = pthread_cond_signal(&cond->cond);
+ r = pthread_cond_signal(cond);
} while (r == EAGAIN);
if (r != 0) {
rb_bug_errno("pthread_cond_signal", r);
}
}
-static void
-native_cond_broadcast(rb_nativethread_cond_t *cond)
+void
+rb_native_cond_broadcast(rb_nativethread_cond_t *cond)
{
int r;
do {
- r = pthread_cond_broadcast(&cond->cond);
+ r = pthread_cond_broadcast(cond);
} while (r == EAGAIN);
if (r != 0) {
- rb_bug_errno("native_cond_broadcast", r);
+ rb_bug_errno("rb_native_cond_broadcast", r);
}
}
-static void
-native_cond_wait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex)
+void
+rb_native_cond_wait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex)
{
- int r = pthread_cond_wait(&cond->cond, mutex);
+ int r = pthread_cond_wait(cond, mutex);
if (r != 0) {
rb_bug_errno("pthread_cond_wait", r);
}
}
static int
-native_cond_timedwait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *ts)
+native_cond_timedwait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex,
+ const rb_hrtime_t *abs)
{
int r;
+ struct timespec ts;
/*
* An old Linux may return EINTR. Even though POSIX says
@@ -358,7 +526,7 @@ native_cond_timedwait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex, cons
* Let's hide it from arch generic code.
*/
do {
- r = pthread_cond_timedwait(&cond->cond, mutex, ts);
+ r = pthread_cond_timedwait(cond, mutex, rb_hrtime2timespec(&ts, abs));
} while (r == EINTR);
if (r != 0 && r != ETIMEDOUT) {
@@ -368,63 +536,22 @@ native_cond_timedwait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex, cons
return r;
}
-static struct timespec
-native_cond_timeout(rb_nativethread_cond_t *cond, struct timespec timeout_rel)
+static rb_hrtime_t
+native_cond_timeout(rb_nativethread_cond_t *cond, const rb_hrtime_t rel)
{
- int ret;
- struct timeval tv;
- struct timespec timeout;
- struct timespec now;
-
-#if USE_MONOTONIC_COND
- if (cond->clockid == CLOCK_MONOTONIC) {
- ret = clock_gettime(cond->clockid, &now);
- if (ret != 0)
- rb_sys_fail("clock_gettime()");
- goto out;
+ if (condattr_monotonic) {
+ return rb_hrtime_add(rb_hrtime_now(), rel);
}
+ else {
+ struct timespec ts;
- if (cond->clockid != CLOCK_REALTIME)
- rb_bug("unsupported clockid %"PRIdVALUE, (SIGNED_VALUE)cond->clockid);
-#endif
-
- ret = gettimeofday(&tv, 0);
- if (ret != 0)
- rb_sys_fail(0);
- now.tv_sec = tv.tv_sec;
- now.tv_nsec = tv.tv_usec * 1000;
-
-#if USE_MONOTONIC_COND
- out:
-#endif
- timeout.tv_sec = now.tv_sec;
- timeout.tv_nsec = now.tv_nsec;
- timeout.tv_sec += timeout_rel.tv_sec;
- timeout.tv_nsec += timeout_rel.tv_nsec;
-
- if (timeout.tv_nsec >= 1000*1000*1000) {
- timeout.tv_sec++;
- timeout.tv_nsec -= 1000*1000*1000;
+ rb_timespec_now(&ts);
+ return rb_hrtime_add(rb_timespec2hrtime(&ts), rel);
}
-
- if (timeout.tv_sec < now.tv_sec)
- timeout.tv_sec = TIMET_MAX;
-
- return timeout;
}
#define native_cleanup_push pthread_cleanup_push
#define native_cleanup_pop pthread_cleanup_pop
-#ifdef HAVE_SCHED_YIELD
-#define native_thread_yield() (void)sched_yield()
-#else
-#define native_thread_yield() ((void)0)
-#endif
-
-#if defined(SIGVTALRM) && !defined(__CYGWIN__)
-#define USE_UBF_LIST 1
-static rb_nativethread_lock_t ubf_list_lock;
-#endif
static pthread_key_t ruby_native_thread_key;
@@ -449,20 +576,22 @@ ruby_thread_set_native(rb_thread_t *th)
static void native_thread_init(rb_thread_t *th);
void
-Init_native_thread(void)
+Init_native_thread(rb_thread_t *th)
{
- rb_thread_t *th = GET_THREAD();
-
+#if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK)
+ if (condattr_monotonic) {
+ int r = pthread_condattr_init(condattr_monotonic);
+ if (r == 0) {
+ r = pthread_condattr_setclock(condattr_monotonic, CLOCK_MONOTONIC);
+ }
+ if (r) condattr_monotonic = NULL;
+ }
+#endif
pthread_key_create(&ruby_native_thread_key, NULL);
th->thread_id = pthread_self();
fill_thread_id_str(th);
native_thread_init(th);
-#ifdef USE_UBF_LIST
- native_mutex_initialize(&ubf_list_lock);
-#endif
-#ifndef __native_client__
posix_signal(SIGVTALRM, null_func);
-#endif
}
static void
@@ -471,24 +600,37 @@ native_thread_init(rb_thread_t *th)
native_thread_data_t *nd = &th->native_thread_data;
#ifdef USE_UBF_LIST
- list_node_init(&nd->ubf_list);
+ list_node_init(&nd->node.ubf);
#endif
- native_cond_initialize(&nd->sleep_cond, RB_CONDATTR_CLOCK_MONOTONIC);
+ rb_native_cond_initialize(&nd->cond.gvlq);
+ if (&nd->cond.gvlq != &nd->cond.intr)
+ rb_native_cond_initialize(&nd->cond.intr);
ruby_thread_set_native(th);
}
+#ifndef USE_THREAD_CACHE
+#define USE_THREAD_CACHE 1
+#endif
+
static void
native_thread_destroy(rb_thread_t *th)
{
- native_cond_destroy(&th->native_thread_data.sleep_cond);
-}
+ native_thread_data_t *nd = &th->native_thread_data;
-#ifndef USE_THREAD_CACHE
-#define USE_THREAD_CACHE 0
-#endif
+ rb_native_cond_destroy(&nd->cond.gvlq);
+ if (&nd->cond.gvlq != &nd->cond.intr)
+ rb_native_cond_destroy(&nd->cond.intr);
+
+ /*
+ * prevent false positive from ruby_thread_has_gvl_p if that
+ * gets called from an interposing function wrapper
+ */
+ if (USE_THREAD_CACHE)
+ ruby_thread_set_native(0);
+}
#if USE_THREAD_CACHE
-static rb_thread_t *register_cached_thread_and_wait(void);
+static rb_thread_t *register_cached_thread_and_wait(void *);
#endif
#if defined HAVE_PTHREAD_GETATTR_NP || defined HAVE_PTHREAD_ATTR_GET_NP
@@ -584,8 +726,12 @@ get_stack(void **addr, size_t *size)
CHECK_ERR(pthread_attr_getstackaddr(&attr, addr));
CHECK_ERR(pthread_attr_getstacksize(&attr, size));
# endif
+# ifdef HAVE_PTHREAD_ATTR_GETGUARDSIZE
CHECK_ERR(pthread_attr_getguardsize(&attr, &guard));
*size -= guard;
+# else
+ *size -= getpagesize();
+# endif
pthread_attr_destroy(&attr);
#elif defined HAVE_PTHREAD_ATTR_GET_NP /* FreeBSD, DragonFly BSD, NetBSD */
pthread_attr_t attr;
@@ -740,6 +886,12 @@ ruby_init_stack(volatile VALUE *addr
)
{
native_main_thread.id = pthread_self();
+#ifdef __ia64
+ if (!native_main_thread.register_stack_start ||
+ (VALUE*)bsp < native_main_thread.register_stack_start) {
+ native_main_thread.register_stack_start = (VALUE*)bsp;
+ }
+#endif
#if MAINSTACKADDR_AVAILABLE
if (native_main_thread.stack_maxsize) return;
{
@@ -763,12 +915,6 @@ ruby_init_stack(volatile VALUE *addr
native_main_thread.stack_start = (VALUE *)addr;
}
#endif
-#ifdef __ia64
- if (!native_main_thread.register_stack_start ||
- (VALUE*)bsp < native_main_thread.register_stack_start) {
- native_main_thread.register_stack_start = (VALUE*)bsp;
- }
-#endif
{
#if defined(HAVE_GETRLIMIT)
#if defined(PTHREAD_STACK_DEFAULT)
@@ -832,8 +978,8 @@ native_thread_init_stack(rb_thread_t *th)
rb_nativethread_id_t curr = pthread_self();
if (pthread_equal(curr, native_main_thread.id)) {
- th->machine.stack_start = native_main_thread.stack_start;
- th->machine.stack_maxsize = native_main_thread.stack_maxsize;
+ th->ec->machine.stack_start = native_main_thread.stack_start;
+ th->ec->machine.stack_maxsize = native_main_thread.stack_maxsize;
}
else {
#ifdef STACKADDR_AVAILABLE
@@ -841,22 +987,18 @@ native_thread_init_stack(rb_thread_t *th)
size_t size;
if (get_stack(&start, &size) == 0) {
- th->machine.stack_start = start;
- th->machine.stack_maxsize = size;
- }
-#elif defined get_stack_of
- if (!th->machine.stack_maxsize) {
- native_mutex_lock(&th->interrupt_lock);
- native_mutex_unlock(&th->interrupt_lock);
+ uintptr_t diff = (uintptr_t)start - (uintptr_t)&curr;
+ th->ec->machine.stack_start = (VALUE *)&curr;
+ th->ec->machine.stack_maxsize = size - diff;
}
#else
rb_raise(rb_eNotImpError, "ruby engine can initialize only in the main thread");
#endif
}
#ifdef __ia64
- th->machine.register_stack_start = native_main_thread.register_stack_start;
- th->machine.stack_maxsize /= 2;
- th->machine.register_stack_maxsize = th->machine.stack_maxsize;
+ th->ec->machine.register_stack_start = native_main_thread.register_stack_start;
+ th->ec->machine.stack_maxsize /= 2;
+ th->ec->machine.register_stack_maxsize = th->ec->machine.stack_maxsize;
#endif
return 0;
}
@@ -868,11 +1010,12 @@ native_thread_init_stack(rb_thread_t *th)
static void *
thread_start_func_1(void *th_ptr)
{
+ rb_thread_t *th = th_ptr;
+ RB_ALTSTACK_INIT(void *altstack);
#if USE_THREAD_CACHE
thread_start:
#endif
{
- rb_thread_t *th = th_ptr;
#if !defined USE_NATIVE_THREAD_INIT
VALUE stack_start;
#endif
@@ -884,108 +1027,124 @@ thread_start_func_1(void *th_ptr)
native_thread_init(th);
/* run */
#if defined USE_NATIVE_THREAD_INIT
- thread_start_func_2(th, th->machine.stack_start, rb_ia64_bsp());
+ thread_start_func_2(th, th->ec->machine.stack_start, rb_ia64_bsp());
#else
thread_start_func_2(th, &stack_start, rb_ia64_bsp());
#endif
}
#if USE_THREAD_CACHE
- if (1) {
- /* cache thread */
- rb_thread_t *th;
- if ((th = register_cached_thread_and_wait()) != 0) {
- th_ptr = (void *)th;
- th->thread_id = pthread_self();
- goto thread_start;
- }
+ /* cache thread */
+ if ((th = register_cached_thread_and_wait(RB_ALTSTACK(altstack))) != 0) {
+ goto thread_start;
}
+#else
+ RB_ALTSTACK_FREE(altstack);
#endif
return 0;
}
struct cached_thread_entry {
- volatile rb_thread_t **th_area;
- rb_nativethread_cond_t *cond;
- struct cached_thread_entry *next;
+ rb_nativethread_cond_t cond;
+ rb_nativethread_id_t thread_id;
+ rb_thread_t *th;
+ void *altstack;
+ struct list_node node;
};
-
#if USE_THREAD_CACHE
static rb_nativethread_lock_t thread_cache_lock = RB_NATIVETHREAD_LOCK_INIT;
-struct cached_thread_entry *cached_thread_root;
+static LIST_HEAD(cached_thread_head);
-static rb_thread_t *
-register_cached_thread_and_wait(void)
+# if defined(HAVE_WORKING_FORK)
+static void
+thread_cache_reset(void)
{
- rb_nativethread_cond_t cond = RB_NATIVETHREAD_COND_INIT;
- volatile rb_thread_t *th_area = 0;
- struct timeval tv;
- struct timespec ts;
- struct cached_thread_entry *entry =
- (struct cached_thread_entry *)malloc(sizeof(struct cached_thread_entry));
+ rb_native_mutex_initialize(&thread_cache_lock);
+ list_head_init(&cached_thread_head);
+}
+# endif
- if (entry == 0) {
- return 0; /* failed -> terminate thread immediately */
- }
+/*
+ * number of seconds to cache for, I think 1-5s is sufficient to obviate
+ * the need for thread pool in many network programs (taking into account
+ * worst case network latency across the globe) without wasting memory
+ */
+#ifndef THREAD_CACHE_TIME
+# define THREAD_CACHE_TIME ((rb_hrtime_t)3 * RB_HRTIME_PER_SEC)
+#endif
- gettimeofday(&tv, 0);
- ts.tv_sec = tv.tv_sec + 60;
- ts.tv_nsec = tv.tv_usec * 1000;
+static rb_thread_t *
+register_cached_thread_and_wait(void *altstack)
+{
+ rb_hrtime_t end = THREAD_CACHE_TIME;
+ struct cached_thread_entry entry;
- native_mutex_lock(&thread_cache_lock);
- {
- entry->th_area = &th_area;
- entry->cond = &cond;
- entry->next = cached_thread_root;
- cached_thread_root = entry;
+ rb_native_cond_initialize(&entry.cond);
+ entry.altstack = altstack;
+ entry.th = NULL;
+ entry.thread_id = pthread_self();
+ end = native_cond_timeout(&entry.cond, end);
- native_cond_timedwait(&cond, &thread_cache_lock, &ts);
+ rb_native_mutex_lock(&thread_cache_lock);
+ {
+ list_add(&cached_thread_head, &entry.node);
- {
- struct cached_thread_entry *e, **prev = &cached_thread_root;
+ native_cond_timedwait(&entry.cond, &thread_cache_lock, &end);
- while ((e = *prev) != 0) {
- if (e == entry) {
- *prev = e->next;
- break;
- }
- prev = &e->next;
- }
- }
+ if (entry.th == NULL) { /* unused */
+ list_del(&entry.node);
+ }
+ }
+ rb_native_mutex_unlock(&thread_cache_lock);
- free(entry); /* ok */
- native_cond_destroy(&cond);
+ rb_native_cond_destroy(&entry.cond);
+ if (!entry.th) {
+ RB_ALTSTACK_FREE(entry.altstack);
}
- native_mutex_unlock(&thread_cache_lock);
- return (rb_thread_t *)th_area;
+ return entry.th;
}
+#else
+# if defined(HAVE_WORKING_FORK)
+static void thread_cache_reset(void) { }
+# endif
#endif
static int
use_cached_thread(rb_thread_t *th)
{
- int result = 0;
#if USE_THREAD_CACHE
struct cached_thread_entry *entry;
- if (cached_thread_root) {
- native_mutex_lock(&thread_cache_lock);
- entry = cached_thread_root;
- {
- if (cached_thread_root) {
- cached_thread_root = entry->next;
- *entry->th_area = th;
- result = 1;
- }
- }
- if (result) {
- native_cond_signal(entry->cond);
- }
- native_mutex_unlock(&thread_cache_lock);
+ rb_native_mutex_lock(&thread_cache_lock);
+ entry = list_pop(&cached_thread_head, struct cached_thread_entry, node);
+ if (entry) {
+ entry->th = th;
+ /* th->thread_id must be set before signal for Thread#name= */
+ th->thread_id = entry->thread_id;
+ fill_thread_id_str(th);
+ rb_native_cond_signal(&entry->cond);
}
+ rb_native_mutex_unlock(&thread_cache_lock);
+ return !!entry;
+#endif
+ return 0;
+}
+
+static void
+clear_thread_cache_altstack(void)
+{
+#if USE_THREAD_CACHE
+ struct cached_thread_entry *entry;
+
+ rb_native_mutex_lock(&thread_cache_lock);
+ list_for_each(&cached_thread_head, entry, node) {
+ void MAYBE_UNUSED(*altstack) = entry->altstack;
+ entry->altstack = 0;
+ RB_ALTSTACK_FREE(altstack);
+ }
+ rb_native_mutex_unlock(&thread_cache_lock);
#endif
- return result;
}
static int
@@ -997,22 +1156,16 @@ native_thread_create(rb_thread_t *th)
thread_debug("create (use cached thread): %p\n", (void *)th);
}
else {
-#ifdef HAVE_PTHREAD_ATTR_INIT
pthread_attr_t attr;
- pthread_attr_t *const attrp = &attr;
-#else
- pthread_attr_t *const attrp = NULL;
-#endif
const size_t stack_size = th->vm->default_params.thread_machine_stack_size;
const size_t space = space_size(stack_size);
- th->machine.stack_maxsize = stack_size - space;
+ th->ec->machine.stack_maxsize = stack_size - space;
#ifdef __ia64
- th->machine.stack_maxsize /= 2;
- th->machine.register_stack_maxsize = th->machine.stack_maxsize;
+ th->ec->machine.stack_maxsize /= 2;
+ th->ec->machine.register_stack_maxsize = th->ec->machine.stack_maxsize;
#endif
-#ifdef HAVE_PTHREAD_ATTR_INIT
CHECK_ERR(pthread_attr_init(&attr));
# ifdef PTHREAD_STACK_MIN
@@ -1024,41 +1177,16 @@ native_thread_create(rb_thread_t *th)
CHECK_ERR(pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED));
# endif
CHECK_ERR(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
-#endif
-#ifdef get_stack_of
- native_mutex_lock(&th->interrupt_lock);
-#endif
- err = pthread_create(&th->thread_id, attrp, thread_start_func_1, th);
-#ifdef get_stack_of
- if (!err) {
- get_stack_of(th->thread_id,
- &th->machine.stack_start,
- &th->machine.stack_maxsize);
- }
- native_mutex_unlock(&th->interrupt_lock);
-#endif
+
+ err = pthread_create(&th->thread_id, &attr, thread_start_func_1, th);
thread_debug("create: %p (%d)\n", (void *)th, err);
/* should be done in the created thread */
fill_thread_id_str(th);
-#ifdef HAVE_PTHREAD_ATTR_INIT
CHECK_ERR(pthread_attr_destroy(&attr));
-#endif
}
return err;
}
-#if USE_SLEEPY_TIMER_THREAD
-static void
-native_thread_join(pthread_t th)
-{
- int err = pthread_join(th, 0);
- if (err) {
- rb_raise(rb_eThreadError, "native_thread_join() failed (%d)", err);
- }
-}
-#endif
-
-
#if USE_NATIVE_THREAD_PRIORITY
static void
@@ -1100,77 +1228,80 @@ ubf_pthread_cond_signal(void *ptr)
{
rb_thread_t *th = (rb_thread_t *)ptr;
thread_debug("ubf_pthread_cond_signal (%p)\n", (void *)th);
- native_cond_signal(&th->native_thread_data.sleep_cond);
+ rb_native_cond_signal(&th->native_thread_data.cond.intr);
}
static void
-native_sleep(rb_thread_t *th, struct timeval *timeout_tv)
+native_cond_sleep(rb_thread_t *th, rb_hrtime_t *rel)
{
- struct timespec timeout;
rb_nativethread_lock_t *lock = &th->interrupt_lock;
- rb_nativethread_cond_t *cond = &th->native_thread_data.sleep_cond;
-
- if (timeout_tv) {
- struct timespec timeout_rel;
-
- timeout_rel.tv_sec = timeout_tv->tv_sec;
- timeout_rel.tv_nsec = timeout_tv->tv_usec * 1000;
-
- /* Solaris cond_timedwait() return EINVAL if an argument is greater than
- * current_time + 100,000,000. So cut up to 100,000,000. This is
- * considered as a kind of spurious wakeup. The caller to native_sleep
- * should care about spurious wakeup.
- *
- * See also [Bug #1341] [ruby-core:29702]
- * http://download.oracle.com/docs/cd/E19683-01/816-0216/6m6ngupgv/index.html
- */
- if (timeout_rel.tv_sec > 100000000) {
- timeout_rel.tv_sec = 100000000;
- timeout_rel.tv_nsec = 0;
- }
-
- timeout = native_cond_timeout(cond, timeout_rel);
- }
+ rb_nativethread_cond_t *cond = &th->native_thread_data.cond.intr;
+
+ /* Solaris cond_timedwait() return EINVAL if an argument is greater than
+ * current_time + 100,000,000. So cut up to 100,000,000. This is
+ * considered as a kind of spurious wakeup. The caller to native_sleep
+ * should care about spurious wakeup.
+ *
+ * See also [Bug #1341] [ruby-core:29702]
+ * http://download.oracle.com/docs/cd/E19683-01/816-0216/6m6ngupgv/index.html
+ */
+ const rb_hrtime_t max = (rb_hrtime_t)100000000 * RB_HRTIME_PER_SEC;
- GVL_UNLOCK_BEGIN();
+ GVL_UNLOCK_BEGIN(th);
{
- native_mutex_lock(lock);
+ rb_native_mutex_lock(lock);
th->unblock.func = ubf_pthread_cond_signal;
th->unblock.arg = th;
- if (RUBY_VM_INTERRUPTED(th)) {
+ if (RUBY_VM_INTERRUPTED(th->ec)) {
/* interrupted. return immediate */
thread_debug("native_sleep: interrupted before sleep\n");
}
else {
- if (!timeout_tv)
- native_cond_wait(cond, lock);
- else
- native_cond_timedwait(cond, lock, &timeout);
+ if (!rel) {
+ rb_native_cond_wait(cond, lock);
+ }
+ else {
+ rb_hrtime_t end;
+
+ if (*rel > max) {
+ *rel = max;
+ }
+
+ end = native_cond_timeout(cond, *rel);
+ native_cond_timedwait(cond, lock, &end);
+ }
}
th->unblock.func = 0;
- th->unblock.arg = 0;
- native_mutex_unlock(lock);
+ rb_native_mutex_unlock(lock);
}
- GVL_UNLOCK_END();
+ GVL_UNLOCK_END(th);
thread_debug("native_sleep done\n");
}
#ifdef USE_UBF_LIST
static LIST_HEAD(ubf_list_head);
+static rb_nativethread_lock_t ubf_list_lock = RB_NATIVETHREAD_LOCK_INIT;
+
+static void
+ubf_list_atfork(void)
+{
+ list_head_init(&ubf_list_head);
+ rb_native_mutex_initialize(&ubf_list_lock);
+}
/* The thread 'th' is registered to be trying unblock. */
static void
register_ubf_list(rb_thread_t *th)
{
- struct list_node *node = &th->native_thread_data.ubf_list;
+ struct list_node *node = &th->native_thread_data.node.ubf;
if (list_empty((struct list_head*)node)) {
- native_mutex_lock(&ubf_list_lock);
+ rb_native_mutex_lock(&ubf_list_lock);
list_add(&ubf_list_head, node);
- native_mutex_unlock(&ubf_list_lock);
+ rb_native_mutex_unlock(&ubf_list_lock);
}
}
@@ -1178,12 +1309,18 @@ register_ubf_list(rb_thread_t *th)
static void
unregister_ubf_list(rb_thread_t *th)
{
- struct list_node *node = &th->native_thread_data.ubf_list;
+ struct list_node *node = &th->native_thread_data.node.ubf;
+
+ /* we can't allow re-entry into ubf_list_head */
+ VM_ASSERT(th->unblock.func == 0);
if (!list_empty((struct list_head*)node)) {
- native_mutex_lock(&ubf_list_lock);
- list_del_init(node);
- native_mutex_unlock(&ubf_list_lock);
+ rb_native_mutex_lock(&ubf_list_lock);
+ list_del_init(node);
+ if (list_empty(&ubf_list_head) && !rb_signal_buff_size()) {
+ ubf_timer_disarm();
+ }
+ rb_native_mutex_unlock(&ubf_list_lock);
}
}
@@ -1195,25 +1332,42 @@ static void
ubf_wakeup_thread(rb_thread_t *th)
{
thread_debug("thread_wait_queue_wakeup (%"PRI_THREAD_ID")\n", thread_id_str(th));
- if (th)
- pthread_kill(th->thread_id, SIGVTALRM);
+ pthread_kill(th->thread_id, SIGVTALRM);
}
static void
ubf_select(void *ptr)
{
rb_thread_t *th = (rb_thread_t *)ptr;
+ rb_vm_t *vm = th->vm;
+ const rb_thread_t *cur = ruby_thread_from_native(); /* may be 0 */
+
register_ubf_list(th);
/*
* ubf_wakeup_thread() doesn't guarantee to wake up a target thread.
* Therefore, we repeatedly call ubf_wakeup_thread() until a target thread
- * exit from ubf function.
- * In the other hands, we shouldn't call rb_thread_wakeup_timer_thread()
- * if running on timer thread because it may make endless wakeups.
+ * exit from ubf function. We must have a timer to perform this operation.
+ * We use double-checked locking here because this function may be called
+ * while vm->gvl.lock is held in do_gvl_timer.
+ * There is also no need to start a timer if we're the designated
+ * sigwait_th thread, otherwise we can deadlock with a thread
+ * in unblock_function_clear.
*/
- if (!pthread_equal(pthread_self(), timer_thread.id))
- rb_thread_wakeup_timer_thread();
+ if (cur != vm->gvl.timer && cur != sigwait_th) {
+ /*
+ * Double-checked locking above was to prevent nested locking
+ * by the SAME thread. We use trylock here to prevent deadlocks
+ * between DIFFERENT threads
+ */
+ if (native_mutex_trylock(&vm->gvl.lock) == 0) {
+ if (!vm->gvl.timer) {
+ rb_thread_wakeup_timer_thread(-1);
+ }
+ rb_native_mutex_unlock(&vm->gvl.lock);
+ }
+ }
+
ubf_wakeup_thread(th);
}
@@ -1227,14 +1381,15 @@ static void
ubf_wakeup_all_threads(void)
{
rb_thread_t *th;
+ native_thread_data_t *dat;
if (!ubf_threads_empty()) {
- native_mutex_lock(&ubf_list_lock);
- list_for_each(&ubf_list_head, th,
- native_thread_data.ubf_list) {
+ rb_native_mutex_lock(&ubf_list_lock);
+ list_for_each(&ubf_list_head, dat, node.ubf) {
+ th = container_of(dat, rb_thread_t, native_thread_data);
ubf_wakeup_thread(th);
}
- native_mutex_unlock(&ubf_list_lock);
+ rb_native_mutex_unlock(&ubf_list_lock);
}
}
@@ -1244,57 +1399,39 @@ ubf_wakeup_all_threads(void)
#define ubf_select 0
static void ubf_wakeup_all_threads(void) { return; }
static int ubf_threads_empty(void) { return 1; }
+#define ubf_list_atfork() do {} while (0)
#endif /* USE_UBF_LIST */
#define TT_DEBUG 0
#define WRITE_CONST(fd, str) (void)(write((fd),(str),sizeof(str)-1)<0)
-/* 100ms. 10ms is too small for user level thread scheduling
- * on recent Linux (tested on 2.6.35)
- */
-#define TIME_QUANTUM_USEC (100 * 1000)
-
-#if USE_SLEEPY_TIMER_THREAD
static struct {
- /*
- * Read end of each pipe is closed inside timer thread for shutdown
- * Write ends are closed by a normal Ruby thread during shutdown
- */
- int normal[2];
- int low[2];
+ /* pipes are closed in forked children when owner_process does not match */
+ int normal[2]; /* [0] == sigwait_fd */
+ int ub_main[2]; /* unblock main thread from native_ppoll_sleep */
/* volatile for signal handler use: */
volatile rb_pid_t owner_process;
- rb_atomic_t writing;
-} timer_thread_pipe = {
+} signal_self_pipe = {
+ {-1, -1},
{-1, -1},
- {-1, -1}, /* low priority */
};
-NORETURN(static void async_bug_fd(const char *mesg, int errno_arg, int fd));
-static void
-async_bug_fd(const char *mesg, int errno_arg, int fd)
-{
- char buff[64];
- size_t n = strlcpy(buff, mesg, sizeof(buff));
- if (n < sizeof(buff)-3) {
- ruby_snprintf(buff, sizeof(buff)-n, "(%d)", fd);
- }
- rb_async_bug_errno(buff, errno_arg);
-}
-
/* only use signal-safe system calls here */
static void
-rb_thread_wakeup_timer_thread_fd(volatile int *fdp)
+rb_thread_wakeup_timer_thread_fd(int fd)
{
+#if USE_EVENTFD
+ const uint64_t buff = 1;
+#else
+ const char buff = '!';
+#endif
ssize_t result;
- int fd = *fdp; /* access fdp exactly once here and do not reread fdp */
/* already opened */
- if (fd >= 0 && timer_thread_pipe.owner_process == getpid()) {
- static const char buff[1] = {'!'};
+ if (fd >= 0) {
retry:
- if ((result = write(fd, buff, 1)) <= 0) {
+ if ((result = write(fd, &buff, sizeof(buff))) <= 0) {
int e = errno;
switch (e) {
case EINTR: goto retry;
@@ -1314,60 +1451,108 @@ rb_thread_wakeup_timer_thread_fd(volatile int *fdp)
}
}
-void
-rb_thread_wakeup_timer_thread(void)
-{
- /* must be safe inside sighandler, so no mutex */
- ATOMIC_INC(timer_thread_pipe.writing);
- rb_thread_wakeup_timer_thread_fd(&timer_thread_pipe.normal[1]);
- ATOMIC_DEC(timer_thread_pipe.writing);
-}
-
+/*
+ * This ensures we get a SIGVTALRM in TIME_QUANTUM_MSEC if our
+ * process could not react to the original signal in time.
+ */
static void
-rb_thread_wakeup_timer_thread_low(void)
-{
- ATOMIC_INC(timer_thread_pipe.writing);
- rb_thread_wakeup_timer_thread_fd(&timer_thread_pipe.low[1]);
- ATOMIC_DEC(timer_thread_pipe.writing);
+ubf_timer_arm(rb_pid_t current) /* async signal safe */
+{
+#if UBF_TIMER == UBF_TIMER_POSIX
+ if ((!current || timer_posix.owner == current) &&
+ !ATOMIC_CAS(timer_posix.state, RTIMER_DISARM, RTIMER_ARMING)) {
+ struct itimerspec it;
+
+ it.it_interval.tv_sec = it.it_value.tv_sec = 0;
+ it.it_interval.tv_nsec = it.it_value.tv_nsec = TIME_QUANTUM_NSEC;
+
+ if (timer_settime(timer_posix.timerid, 0, &it, 0))
+ rb_async_bug_errno("timer_settime (arm)", errno);
+
+ switch (ATOMIC_CAS(timer_posix.state, RTIMER_ARMING, RTIMER_ARMED)) {
+ case RTIMER_DISARM:
+ /* somebody requested a disarm while we were arming */
+ /* may race harmlessly with ubf_timer_destroy */
+ (void)timer_settime(timer_posix.timerid, 0, &zero, 0);
+
+ case RTIMER_ARMING: return; /* success */
+ case RTIMER_ARMED:
+ /*
+ * it is possible to have another thread disarm, and
+ * a third thread arm finish re-arming before we get
+ * here, so we wasted a syscall with timer_settime but
+ * probably unavoidable in a signal handler.
+ */
+ return;
+ case RTIMER_DEAD:
+ /* may race harmlessly with ubf_timer_destroy */
+ (void)timer_settime(timer_posix.timerid, 0, &zero, 0);
+ return;
+ default:
+ rb_async_bug_errno("UBF_TIMER_POSIX unknown state", ERANGE);
+ }
+ }
+#elif UBF_TIMER == UBF_TIMER_PTHREAD
+ if (!current || current == timer_pthread.owner) {
+ if (ATOMIC_EXCHANGE(timer_pthread.armed, 1) == 0)
+ rb_thread_wakeup_timer_thread_fd(timer_pthread.low[1]);
+ }
+#endif
}
-/* VM-dependent API is not available for this function */
-static void
-consume_communication_pipe(int fd)
+void
+rb_thread_wakeup_timer_thread(int sig)
{
-#define CCP_READ_BUFF_SIZE 1024
- /* buffer can be shared because no one refers to them. */
- static char buff[CCP_READ_BUFF_SIZE];
- ssize_t result;
+ rb_pid_t current;
- while (1) {
- result = read(fd, buff, sizeof(buff));
- if (result == 0) {
- return;
- }
- else if (result < 0) {
- int e = errno;
- switch (e) {
- case EINTR:
- continue; /* retry */
- case EAGAIN:
-#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
- case EWOULDBLOCK:
-#endif
- return;
- default:
- async_bug_fd("consume_communication_pipe: read", e, fd);
- }
- }
+ /* non-sighandler path */
+ if (sig <= 0) {
+ rb_thread_wakeup_timer_thread_fd(signal_self_pipe.normal[1]);
+ if (sig < 0) {
+ ubf_timer_arm(0);
+ }
+ return;
+ }
+
+ /* must be safe inside sighandler, so no mutex */
+ current = getpid();
+ if (signal_self_pipe.owner_process == current) {
+ rb_thread_wakeup_timer_thread_fd(signal_self_pipe.normal[1]);
+
+ /*
+ * system_working check is required because vm and main_thread are
+ * freed during shutdown
+ */
+ if (system_working > 0) {
+ volatile rb_execution_context_t *ec;
+ rb_vm_t *vm = GET_VM();
+ rb_thread_t *mth;
+
+ /*
+ * FIXME: root VM and main_thread should be static and not
+ * on heap for maximum safety (and startup/shutdown speed)
+ */
+ if (!vm) return;
+ mth = vm->main_thread;
+ if (!mth || system_working <= 0) return;
+
+ /* this relies on GC for grace period before cont_free */
+ ec = ACCESS_ONCE(rb_execution_context_t *, mth->ec);
+
+ if (ec) {
+ RUBY_VM_SET_TRAP_INTERRUPT(ec);
+ ubf_timer_arm(current);
+ }
+ }
}
}
-#define CLOSE_INVALIDATE(expr) \
- close_invalidate(&timer_thread_pipe.expr,"close_invalidate: "#expr)
+#define CLOSE_INVALIDATE_PAIR(expr) \
+ close_invalidate_pair(expr,"close_invalidate: "#expr)
static void
-close_invalidate(volatile int *fdp, const char *msg)
+close_invalidate(int *fdp, const char *msg)
{
- int fd = *fdp; /* access fdp exactly once here and do not reread fdp */
+ int fd = *fdp;
*fdp = -1;
if (close(fd) < 0) {
@@ -1376,6 +1561,19 @@ close_invalidate(volatile int *fdp, const char *msg)
}
static void
+close_invalidate_pair(int fds[2], const char *msg)
+{
+ if (USE_EVENTFD && fds[0] == fds[1]) {
+ close_invalidate(&fds[0], msg);
+ fds[1] = -1;
+ }
+ else {
+ close_invalidate(&fds[0], msg);
+ close_invalidate(&fds[1], msg);
+ }
+}
+
+static void
set_nonblock(int fd)
{
int oflags;
@@ -1390,14 +1588,33 @@ set_nonblock(int fd)
rb_sys_fail(0);
}
+/* communication pipe with timer thread and signal handler */
static int
setup_communication_pipe_internal(int pipes[2])
{
int err;
+ if (pipes[0] >= 0 || pipes[1] >= 0) {
+ VM_ASSERT(pipes[0] >= 0);
+ VM_ASSERT(pipes[1] >= 0);
+ return 0;
+ }
+
+ /*
+ * Don't bother with eventfd on ancient Linux 2.6.22..2.6.26 which were
+ * missing EFD_* flags, they can fall back to pipe
+ */
+#if USE_EVENTFD && defined(EFD_NONBLOCK) && defined(EFD_CLOEXEC)
+ pipes[0] = pipes[1] = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC);
+ if (pipes[0] >= 0) {
+ rb_update_max_fd(pipes[0]);
+ return 0;
+ }
+#endif
+
err = rb_cloexec_pipe(pipes);
if (err != 0) {
- rb_warn("Failed to create communication pipe for timer thread: %s",
+ rb_warn("pipe creation failed for timer: %s, scheduling broken",
strerror(errno));
return -1;
}
@@ -1408,242 +1625,222 @@ setup_communication_pipe_internal(int pipes[2])
return 0;
}
-/* communication pipe with timer thread and signal handler */
-static int
-setup_communication_pipe(void)
-{
- VM_ASSERT(timer_thread_pipe.owner_process == 0);
- VM_ASSERT(timer_thread_pipe.normal[0] == -1);
- VM_ASSERT(timer_thread_pipe.normal[1] == -1);
- VM_ASSERT(timer_thread_pipe.low[0] == -1);
- VM_ASSERT(timer_thread_pipe.low[1] == -1);
-
- if (setup_communication_pipe_internal(timer_thread_pipe.normal) < 0) {
- return errno;
- }
- if (setup_communication_pipe_internal(timer_thread_pipe.low) < 0) {
- int e = errno;
- CLOSE_INVALIDATE(normal[0]);
- CLOSE_INVALIDATE(normal[1]);
- return e;
- }
+#if !defined(SET_CURRENT_THREAD_NAME) && defined(__linux__) && defined(PR_SET_NAME)
+# define SET_CURRENT_THREAD_NAME(name) prctl(PR_SET_NAME, name)
+#endif
- return 0;
-}
+static VALUE threadptr_invoke_proc_location(rb_thread_t *th);
-/**
- * Let the timer thread sleep a while.
- *
- * The timer thread sleeps until woken up by rb_thread_wakeup_timer_thread() if only one Ruby thread is running.
- * @pre the calling context is in the timer thread.
- */
-static inline void
-timer_thread_sleep(rb_global_vm_lock_t* gvl)
+static void
+native_set_thread_name(rb_thread_t *th)
{
- int result;
- int need_polling;
- struct pollfd pollfds[2];
-
- pollfds[0].fd = timer_thread_pipe.normal[0];
- pollfds[0].events = POLLIN;
- pollfds[1].fd = timer_thread_pipe.low[0];
- pollfds[1].events = POLLIN;
-
- need_polling = !ubf_threads_empty();
-
- if (gvl->waiting > 0 || need_polling) {
- /* polling (TIME_QUANTUM_USEC usec) */
- result = poll(pollfds, 1, TIME_QUANTUM_USEC/1000);
- }
- else {
- /* wait (infinite) */
- result = poll(pollfds, numberof(pollfds), -1);
- }
-
- if (result == 0) {
- /* maybe timeout */
- }
- else if (result > 0) {
- consume_communication_pipe(timer_thread_pipe.normal[0]);
- consume_communication_pipe(timer_thread_pipe.low[0]);
- }
- else { /* result < 0 */
- int e = errno;
- switch (e) {
- case EBADF:
- case EINVAL:
- case ENOMEM: /* from Linux man */
- case EFAULT: /* from FreeBSD man */
- rb_async_bug_errno("thread_timer: select", e);
- default:
- /* ignore */;
- }
+#ifdef SET_CURRENT_THREAD_NAME
+ VALUE loc;
+ if (!NIL_P(loc = th->name)) {
+ SET_CURRENT_THREAD_NAME(RSTRING_PTR(loc));
+ }
+ else if ((loc = threadptr_invoke_proc_location(th)) != Qnil) {
+ char *name, *p;
+ char buf[16];
+ size_t len;
+ int n;
+
+ name = RSTRING_PTR(RARRAY_AREF(loc, 0));
+ p = strrchr(name, '/'); /* show only the basename of the path. */
+ if (p && p[1])
+ name = p + 1;
+
+ n = snprintf(buf, sizeof(buf), "%s:%d", name, NUM2INT(RARRAY_AREF(loc, 1)));
+ rb_gc_force_recycle(loc); /* acts as a GC guard, too */
+
+ len = (size_t)n;
+ if (len >= sizeof(buf)) {
+ buf[sizeof(buf)-2] = '*';
+ buf[sizeof(buf)-1] = '\0';
+ }
+ SET_CURRENT_THREAD_NAME(buf);
}
+#endif
}
-#else /* USE_SLEEPY_TIMER_THREAD */
-# define PER_NANO 1000000000
-void rb_thread_wakeup_timer_thread(void) {}
-static void rb_thread_wakeup_timer_thread_low(void) {}
-
-static rb_nativethread_lock_t timer_thread_lock;
-static rb_nativethread_cond_t timer_thread_cond;
-
-static inline void
-timer_thread_sleep(rb_global_vm_lock_t* unused)
+static VALUE
+native_set_another_thread_name(rb_nativethread_id_t thread_id, VALUE name)
{
- struct timespec ts;
- ts.tv_sec = 0;
- ts.tv_nsec = TIME_QUANTUM_USEC * 1000;
- ts = native_cond_timeout(&timer_thread_cond, ts);
-
- native_cond_timedwait(&timer_thread_cond, &timer_thread_lock, &ts);
-}
-#endif /* USE_SLEEPY_TIMER_THREAD */
-
-#if !defined(SET_CURRENT_THREAD_NAME) && defined(__linux__) && defined(PR_SET_NAME)
-# define SET_CURRENT_THREAD_NAME(name) prctl(PR_SET_NAME, name)
+#ifdef SET_ANOTHER_THREAD_NAME
+ const char *s = "";
+ if (!NIL_P(name)) s = RSTRING_PTR(name);
+ SET_ANOTHER_THREAD_NAME(thread_id, s);
#endif
+ return name;
+}
static void
-native_set_thread_name(rb_thread_t *th)
+ubf_timer_invalidate(void)
{
-#ifdef SET_CURRENT_THREAD_NAME
- if (!th->first_func && th->first_proc) {
- VALUE loc;
- if (!NIL_P(loc = th->name)) {
- SET_CURRENT_THREAD_NAME(RSTRING_PTR(loc));
- }
- else if (!NIL_P(loc = rb_proc_location(th->first_proc))) {
- const VALUE *ptr = RARRAY_CONST_PTR(loc); /* [ String, Integer ] */
- char *name, *p;
- char buf[16];
- size_t len;
- int n;
-
- name = RSTRING_PTR(ptr[0]);
- p = strrchr(name, '/'); /* show only the basename of the path. */
- if (p && p[1])
- name = p + 1;
-
- n = snprintf(buf, sizeof(buf), "%s:%d", name, NUM2INT(ptr[1]));
- rb_gc_force_recycle(loc); /* acts as a GC guard, too */
-
- len = (size_t)n;
- if (len >= sizeof(buf)) {
- buf[sizeof(buf)-2] = '*';
- buf[sizeof(buf)-1] = '\0';
- }
- SET_CURRENT_THREAD_NAME(buf);
- }
- }
+#if UBF_TIMER == UBF_TIMER_PTHREAD
+ CLOSE_INVALIDATE_PAIR(timer_pthread.low);
#endif
}
-static void *
-thread_timer(void *p)
+static void
+ubf_timer_pthread_create(rb_pid_t current)
{
- rb_global_vm_lock_t *gvl = (rb_global_vm_lock_t *)p;
+#if UBF_TIMER == UBF_TIMER_PTHREAD
+ int err;
+ if (timer_pthread.owner == current)
+ return;
- if (TT_DEBUG) WRITE_CONST(2, "start timer thread\n");
+ if (setup_communication_pipe_internal(timer_pthread.low) < 0)
+ return;
-#ifdef SET_CURRENT_THREAD_NAME
- SET_CURRENT_THREAD_NAME("ruby-timer-thr");
+ err = pthread_create(&timer_pthread.thid, 0, timer_pthread_fn, GET_VM());
+ if (!err)
+ timer_pthread.owner = current;
+ else
+ rb_warn("pthread_create failed for timer: %s, signals racy",
+ strerror(err));
#endif
+}
-#if !USE_SLEEPY_TIMER_THREAD
- native_mutex_initialize(&timer_thread_lock);
- native_cond_initialize(&timer_thread_cond, RB_CONDATTR_CLOCK_MONOTONIC);
- native_mutex_lock(&timer_thread_lock);
-#endif
- while (system_working > 0) {
+static void
+ubf_timer_create(rb_pid_t current)
+{
+#if UBF_TIMER == UBF_TIMER_POSIX
+# if defined(__sun)
+# define UBF_TIMER_CLOCK CLOCK_REALTIME
+# else /* Tested Linux and FreeBSD: */
+# define UBF_TIMER_CLOCK CLOCK_MONOTONIC
+# endif
+
+ struct sigevent sev;
- /* timer function */
- ubf_wakeup_all_threads();
- timer_thread_function(0);
+ sev.sigev_notify = SIGEV_SIGNAL;
+ sev.sigev_signo = SIGVTALRM;
+ sev.sigev_value.sival_ptr = &timer_posix;
- if (TT_DEBUG) WRITE_CONST(2, "tick\n");
+ if (!timer_create(UBF_TIMER_CLOCK, &sev, &timer_posix.timerid)) {
+ rb_atomic_t prev = ATOMIC_EXCHANGE(timer_posix.state, RTIMER_DISARM);
- /* wait */
- timer_thread_sleep(gvl);
+ if (prev != RTIMER_DEAD) {
+ rb_bug("timer_posix was not dead: %u\n", (unsigned)prev);
+ }
+ timer_posix.owner = current;
+ }
+ else {
+ rb_warn("timer_create failed: %s, signals racy", strerror(errno));
}
-#if USE_SLEEPY_TIMER_THREAD
- CLOSE_INVALIDATE(normal[0]);
- CLOSE_INVALIDATE(low[0]);
-#else
- native_mutex_unlock(&timer_thread_lock);
- native_cond_destroy(&timer_thread_cond);
- native_mutex_destroy(&timer_thread_lock);
#endif
-
- if (TT_DEBUG) WRITE_CONST(2, "finish timer thread\n");
- return NULL;
+ if (UBF_TIMER == UBF_TIMER_PTHREAD)
+ ubf_timer_pthread_create(current);
}
static void
rb_thread_create_timer_thread(void)
{
- if (!timer_thread.created) {
- int err;
-#ifdef HAVE_PTHREAD_ATTR_INIT
- pthread_attr_t attr;
+ /* we only create the pipe, and lazy-spawn */
+ rb_pid_t current = getpid();
+ rb_pid_t owner = signal_self_pipe.owner_process;
+
+ if (owner && owner != current) {
+ CLOSE_INVALIDATE_PAIR(signal_self_pipe.normal);
+ CLOSE_INVALIDATE_PAIR(signal_self_pipe.ub_main);
+ ubf_timer_invalidate();
+ }
+
+ if (setup_communication_pipe_internal(signal_self_pipe.normal) < 0) return;
+ if (setup_communication_pipe_internal(signal_self_pipe.ub_main) < 0) return;
+
+ ubf_timer_create(current);
+ if (owner != current) {
+ /* validate pipe on this process */
+ sigwait_th = THREAD_INVALID;
+ signal_self_pipe.owner_process = current;
+ }
+}
+
+static void
+ubf_timer_disarm(void)
+{
+#if UBF_TIMER == UBF_TIMER_POSIX
+ rb_atomic_t prev;
- err = pthread_attr_init(&attr);
- if (err != 0) {
- rb_warn("pthread_attr_init failed for timer: %s, scheduling broken",
- strerror(err));
- return;
+ prev = ATOMIC_CAS(timer_posix.state, RTIMER_ARMED, RTIMER_DISARM);
+ switch (prev) {
+ case RTIMER_DISARM: return; /* likely */
+ case RTIMER_ARMING: return; /* ubf_timer_arm will disarm itself */
+ case RTIMER_ARMED:
+ if (timer_settime(timer_posix.timerid, 0, &zero, 0)) {
+ int err = errno;
+
+ if (err == EINVAL) {
+ prev = ATOMIC_CAS(timer_posix.state, RTIMER_DISARM, RTIMER_DISARM);
+
+ /* main thread may have killed the timer */
+ if (prev == RTIMER_DEAD) return;
+
+ rb_bug_errno("timer_settime (disarm)", err);
+ }
}
-# ifdef PTHREAD_STACK_MIN
- {
- const size_t min_size = (4096 * 4);
- /* Allocate the machine stack for the timer thread
- * at least 16KB (4 pages). FreeBSD 8.2 AMD64 causes
- * machine stack overflow only with PTHREAD_STACK_MIN.
- */
- size_t stack_size = PTHREAD_STACK_MIN; /* may be dynamic, get only once */
- if (stack_size < min_size) stack_size = min_size;
- if (THREAD_DEBUG) stack_size += BUFSIZ;
- pthread_attr_setstacksize(&attr, stack_size);
- }
-# endif
+ return;
+ case RTIMER_DEAD: return; /* stay dead */
+ default:
+ rb_bug("UBF_TIMER_POSIX bad state: %u\n", (unsigned)prev);
+ }
+
+#elif UBF_TIMER == UBF_TIMER_PTHREAD
+ ATOMIC_SET(timer_pthread.armed, 0);
#endif
+}
-#if USE_SLEEPY_TIMER_THREAD
- err = setup_communication_pipe();
- if (err != 0) {
- rb_warn("pipe creation failed for timer: %s, scheduling broken",
- strerror(err));
- return;
- }
-#endif /* USE_SLEEPY_TIMER_THREAD */
+static void
+ubf_timer_destroy(void)
+{
+#if UBF_TIMER == UBF_TIMER_POSIX
+ if (timer_posix.owner == getpid()) {
+ rb_atomic_t expect = RTIMER_DISARM;
+ size_t i, max = 10000000;
+
+ /* prevent signal handler from arming: */
+ for (i = 0; i < max; i++) {
+ switch (ATOMIC_CAS(timer_posix.state, expect, RTIMER_DEAD)) {
+ case RTIMER_DISARM:
+ if (expect == RTIMER_DISARM) goto done;
+ expect = RTIMER_DISARM;
+ break;
+ case RTIMER_ARMING:
+ native_thread_yield(); /* let another thread finish arming */
+ expect = RTIMER_ARMED;
+ break;
+ case RTIMER_ARMED:
+ if (expect == RTIMER_ARMED) {
+ if (timer_settime(timer_posix.timerid, 0, &zero, 0))
+ rb_bug_errno("timer_settime (destroy)", errno);
+ goto done;
+ }
+ expect = RTIMER_ARMED;
+ break;
+ case RTIMER_DEAD:
+ rb_bug("RTIMER_DEAD unexpected");
+ }
+ }
+ rb_bug("timed out waiting for timer to arm");
+done:
+ if (timer_delete(timer_posix.timerid) < 0)
+ rb_sys_fail("timer_delete");
- /* create timer thread */
- if (timer_thread.created) {
- rb_bug("rb_thread_create_timer_thread: Timer thread was already created\n");
- }
-#ifdef HAVE_PTHREAD_ATTR_INIT
- err = pthread_create(&timer_thread.id, &attr, thread_timer, &GET_VM()->gvl);
- pthread_attr_destroy(&attr);
-#else
- err = pthread_create(&timer_thread.id, NULL, thread_timer, &GET_VM()->gvl);
-#endif
- if (err != 0) {
- rb_warn("pthread_create failed for timer: %s, scheduling broken",
- strerror(err));
-#if USE_SLEEPY_TIMER_THREAD
- CLOSE_INVALIDATE(normal[0]);
- CLOSE_INVALIDATE(normal[1]);
- CLOSE_INVALIDATE(low[0]);
- CLOSE_INVALIDATE(low[1]);
-#endif
- return;
- }
+ VM_ASSERT(ATOMIC_EXCHANGE(timer_posix.state, RTIMER_DEAD) == RTIMER_DEAD);
+ }
+#elif UBF_TIMER == UBF_TIMER_PTHREAD
+ int err;
- /* validate pipe on this process */
- timer_thread_pipe.owner_process = getpid();
- timer_thread.created = 1;
+ timer_pthread.owner = 0;
+ ubf_timer_disarm();
+ rb_thread_wakeup_timer_thread_fd(timer_pthread.low[1]);
+ err = pthread_join(timer_pthread.thid, 0);
+ if (err) {
+ rb_raise(rb_eThreadError, "native_thread_join() failed (%d)", err);
}
+#endif
}
static int
@@ -1651,37 +1848,10 @@ native_stop_timer_thread(void)
{
int stopped;
stopped = --system_working <= 0;
+ if (stopped)
+ ubf_timer_destroy();
if (TT_DEBUG) fprintf(stderr, "stop timer thread\n");
-#if USE_SLEEPY_TIMER_THREAD
- if (stopped) {
- /* prevent wakeups from signal handler ASAP */
- timer_thread_pipe.owner_process = 0;
-
- /*
- * however, the above was not enough: the FD may already be
- * captured and in the middle of a write while we are running,
- * so wait for that to finish:
- */
- while (ATOMIC_CAS(timer_thread_pipe.writing, (rb_atomic_t)0, 0)) {
- native_thread_yield();
- }
-
- /* stop writing ends of pipes so timer thread notices EOF */
- CLOSE_INVALIDATE(normal[1]);
- CLOSE_INVALIDATE(low[1]);
-
- /* timer thread will stop looping when system_working <= 0: */
- native_thread_join(timer_thread.id);
-
- /* timer thread will close the read end on exit: */
- VM_ASSERT(timer_thread_pipe.normal[0] == -1);
- VM_ASSERT(timer_thread_pipe.low[0] == -1);
-
- if (TT_DEBUG) fprintf(stderr, "joined timer thread\n");
- timer_thread.created = 0;
- }
-#endif
return stopped;
}
@@ -1715,8 +1885,8 @@ ruby_stack_overflowed_p(const rb_thread_t *th, const void *addr)
else
#endif
if (th) {
- size = th->machine.stack_maxsize;
- base = (char *)th->machine.stack_start - STACK_DIR_UPPER(0, size);
+ size = th->ec->machine.stack_maxsize;
+ base = (char *)th->ec->machine.stack_start - STACK_DIR_UPPER(0, size);
}
else {
return 0;
@@ -1738,20 +1908,23 @@ ruby_stack_overflowed_p(const rb_thread_t *th, const void *addr)
int
rb_reserved_fd_p(int fd)
{
-#if USE_SLEEPY_TIMER_THREAD
- if ((fd == timer_thread_pipe.normal[0] ||
- fd == timer_thread_pipe.normal[1] ||
- fd == timer_thread_pipe.low[0] ||
- fd == timer_thread_pipe.low[1]) &&
- timer_thread_pipe.owner_process == getpid()) { /* async-signal-safe */
+ /* no false-positive if out-of-FD at startup */
+ if (fd < 0)
+ return 0;
+
+#if UBF_TIMER == UBF_TIMER_PTHREAD
+ if (fd == timer_pthread.low[0] || fd == timer_pthread.low[1])
+ goto check_pid;
+#endif
+ if (fd == signal_self_pipe.normal[0] || fd == signal_self_pipe.normal[1])
+ goto check_pid;
+ if (fd == signal_self_pipe.ub_main[0] || fd == signal_self_pipe.ub_main[1])
+ goto check_pid;
+ return 0;
+check_pid:
+ if (signal_self_pipe.owner_process == getpid()) /* async-signal-safe */
return 1;
- }
- else {
- return 0;
- }
-#else
return 0;
-#endif
}
rb_nativethread_id_t
@@ -1760,4 +1933,304 @@ rb_nativethread_self(void)
return pthread_self();
}
+#if USE_MJIT
+/* A function that wraps actual worker function, for pthread abstraction. */
+static void *
+mjit_worker(void *arg)
+{
+ void (*worker_func)(void) = (void(*)(void))arg;
+
+#ifdef SET_CURRENT_THREAD_NAME
+ SET_CURRENT_THREAD_NAME("ruby-mjitworker"); /* 16 byte including NUL */
+#endif
+ worker_func();
+ return NULL;
+}
+
+/* Launch MJIT thread. Returns FALSE if it fails to create thread. */
+int
+rb_thread_create_mjit_thread(void (*worker_func)(void))
+{
+ pthread_attr_t attr;
+ pthread_t worker_pid;
+ int ret = FALSE;
+
+ if (pthread_attr_init(&attr) != 0) return ret;
+
+ /* jit_worker thread is not to be joined */
+ if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0
+ && pthread_create(&worker_pid, &attr, mjit_worker, (void *)worker_func) == 0) {
+ ret = TRUE;
+ }
+ pthread_attr_destroy(&attr);
+ return ret;
+}
+#endif
+
+int
+rb_sigwait_fd_get(const rb_thread_t *th)
+{
+ if (signal_self_pipe.normal[0] >= 0) {
+ VM_ASSERT(signal_self_pipe.owner_process == getpid());
+ /*
+ * no need to keep firing the timer if any thread is sleeping
+ * on the signal self-pipe
+ */
+ ubf_timer_disarm();
+
+ if (ATOMIC_PTR_CAS(sigwait_th, THREAD_INVALID, th) == THREAD_INVALID) {
+ return signal_self_pipe.normal[0];
+ }
+ }
+ return -1; /* avoid thundering herd and work stealing/starvation */
+}
+
+void
+rb_sigwait_fd_put(const rb_thread_t *th, int fd)
+{
+ const rb_thread_t *old;
+
+ VM_ASSERT(signal_self_pipe.normal[0] == fd);
+ old = ATOMIC_PTR_EXCHANGE(sigwait_th, THREAD_INVALID);
+ if (old != th) assert(old == th);
+}
+
+#ifndef HAVE_PPOLL
+/* TODO: don't ignore sigmask */
+static int
+ruby_ppoll(struct pollfd *fds, nfds_t nfds,
+ const struct timespec *ts, const sigset_t *sigmask)
+{
+ int timeout_ms;
+
+ if (ts) {
+ int tmp, tmp2;
+
+ if (ts->tv_sec > INT_MAX/1000)
+ timeout_ms = INT_MAX;
+ else {
+ tmp = (int)(ts->tv_sec * 1000);
+ /* round up 1ns to 1ms to avoid excessive wakeups for <1ms sleep */
+ tmp2 = (int)((ts->tv_nsec + 999999L) / (1000L * 1000L));
+ if (INT_MAX - tmp < tmp2)
+ timeout_ms = INT_MAX;
+ else
+ timeout_ms = (int)(tmp + tmp2);
+ }
+ }
+ else
+ timeout_ms = -1;
+
+ return poll(fds, nfds, timeout_ms);
+}
+# define ppoll(fds,nfds,ts,sigmask) ruby_ppoll((fds),(nfds),(ts),(sigmask))
+#endif
+
+void
+rb_sigwait_sleep(rb_thread_t *th, int sigwait_fd, const rb_hrtime_t *rel)
+{
+ struct pollfd pfd;
+ struct timespec ts;
+
+ pfd.fd = sigwait_fd;
+ pfd.events = POLLIN;
+
+ if (!BUSY_WAIT_SIGNALS && ubf_threads_empty()) {
+ (void)ppoll(&pfd, 1, rb_hrtime2timespec(&ts, rel), 0);
+ check_signals_nogvl(th, sigwait_fd);
+ }
+ else {
+ rb_hrtime_t to = RB_HRTIME_MAX, end;
+ int n = 0;
+
+ if (rel) {
+ to = *rel;
+ end = rb_hrtime_add(rb_hrtime_now(), to);
+ }
+ /*
+ * tricky: this needs to return on spurious wakeup (no auto-retry).
+ * But we also need to distinguish between periodic quantum
+ * wakeups, so we care about the result of consume_communication_pipe
+ *
+ * We want to avoid spurious wakeup for Mutex#sleep compatibility
+ * [ruby-core:88102]
+ */
+ for (;;) {
+ const rb_hrtime_t *sto = sigwait_timeout(th, sigwait_fd, &to, &n);
+
+ if (n) return;
+ n = ppoll(&pfd, 1, rb_hrtime2timespec(&ts, sto), 0);
+ if (check_signals_nogvl(th, sigwait_fd))
+ return;
+ if (n || (th && RUBY_VM_INTERRUPTED(th->ec)))
+ return;
+ if (rel && hrtime_update_expire(&to, end))
+ return;
+ }
+ }
+}
+
+/*
+ * we need to guarantee wakeups from native_ppoll_sleep because
+ * ubf_select may not be going through ubf_list if other threads
+ * are all sleeping.
+ */
+static void
+ubf_ppoll_sleep(void *ignore)
+{
+ rb_thread_wakeup_timer_thread_fd(signal_self_pipe.ub_main[1]);
+}
+
+/*
+ * Single CPU setups benefit from explicit sched_yield() before ppoll(),
+ * since threads may be too starved to enter the GVL waitqueue for
+ * us to detect contention. Instead, we want to kick other threads
+ * so they can run and possibly prevent us from entering slow paths
+ * in ppoll() or similar syscalls.
+ *
+ * Confirmed on FreeBSD 11.2 and Linux 4.19.
+ * [ruby-core:90417] [Bug #15398]
+ */
+#define GVL_UNLOCK_BEGIN_YIELD(th) do { \
+ const native_thread_data_t *next; \
+ rb_vm_t *vm = th->vm; \
+ RB_GC_SAVE_MACHINE_CONTEXT(th); \
+ rb_native_mutex_lock(&vm->gvl.lock); \
+ next = gvl_release_common(vm); \
+ rb_native_mutex_unlock(&vm->gvl.lock); \
+ if (!next && vm_living_thread_num(vm) > 1) { \
+ native_thread_yield(); \
+ }
+
+/*
+ * This function does not exclusively acquire sigwait_fd, so it
+ * cannot safely read from it. However, it can be woken up in
+ * 4 ways:
+ *
+ * 1) ubf_ppoll_sleep (from another thread)
+ * 2) rb_thread_wakeup_timer_thread (from signal handler)
+ * 3) any unmasked signal hitting the process
+ * 4) periodic ubf timer wakeups (after 3)
+ */
+static void
+native_ppoll_sleep(rb_thread_t *th, rb_hrtime_t *rel)
+{
+ rb_native_mutex_lock(&th->interrupt_lock);
+ th->unblock.func = ubf_ppoll_sleep;
+ rb_native_mutex_unlock(&th->interrupt_lock);
+
+ GVL_UNLOCK_BEGIN_YIELD(th);
+
+ if (!RUBY_VM_INTERRUPTED(th->ec)) {
+ struct pollfd pfd[2];
+ struct timespec ts;
+
+ pfd[0].fd = signal_self_pipe.normal[0]; /* sigwait_fd */
+ pfd[1].fd = signal_self_pipe.ub_main[0];
+ pfd[0].events = pfd[1].events = POLLIN;
+ if (ppoll(pfd, 2, rb_hrtime2timespec(&ts, rel), 0) > 0) {
+ if (pfd[1].revents & POLLIN) {
+ (void)consume_communication_pipe(pfd[1].fd);
+ }
+ }
+ /*
+ * do not read the sigwait_fd, here, let uplevel callers
+ * or other threads that, otherwise we may steal and starve
+ * other threads
+ */
+ }
+ unblock_function_clear(th);
+ GVL_UNLOCK_END(th);
+}
+
+static void
+native_sleep(rb_thread_t *th, rb_hrtime_t *rel)
+{
+ int sigwait_fd = rb_sigwait_fd_get(th);
+
+ if (sigwait_fd >= 0) {
+ rb_native_mutex_lock(&th->interrupt_lock);
+ th->unblock.func = ubf_sigwait;
+ rb_native_mutex_unlock(&th->interrupt_lock);
+
+ GVL_UNLOCK_BEGIN_YIELD(th);
+
+ if (!RUBY_VM_INTERRUPTED(th->ec)) {
+ rb_sigwait_sleep(th, sigwait_fd, rel);
+ }
+ else {
+ check_signals_nogvl(th, sigwait_fd);
+ }
+ unblock_function_clear(th);
+ GVL_UNLOCK_END(th);
+ rb_sigwait_fd_put(th, sigwait_fd);
+ rb_sigwait_fd_migrate(th->vm);
+ }
+ else if (th == th->vm->main_thread) { /* always able to handle signals */
+ native_ppoll_sleep(th, rel);
+ }
+ else {
+ native_cond_sleep(th, rel);
+ }
+}
+
+#if UBF_TIMER == UBF_TIMER_PTHREAD
+static void *
+timer_pthread_fn(void *p)
+{
+ rb_vm_t *vm = p;
+ pthread_t main_thread_id = vm->main_thread->thread_id;
+ struct pollfd pfd;
+ int timeout = -1;
+ int ccp;
+
+ pfd.fd = timer_pthread.low[0];
+ pfd.events = POLLIN;
+
+ while (system_working > 0) {
+ (void)poll(&pfd, 1, timeout);
+ ccp = consume_communication_pipe(pfd.fd);
+
+ if (system_working > 0) {
+ if (ATOMIC_CAS(timer_pthread.armed, 1, 1)) {
+ pthread_kill(main_thread_id, SIGVTALRM);
+
+ if (rb_signal_buff_size() || !ubf_threads_empty()) {
+ timeout = TIME_QUANTUM_MSEC;
+ }
+ else {
+ ATOMIC_SET(timer_pthread.armed, 0);
+ timeout = -1;
+ }
+ }
+ else if (ccp) {
+ pthread_kill(main_thread_id, SIGVTALRM);
+ ATOMIC_SET(timer_pthread.armed, 0);
+ timeout = -1;
+ }
+ }
+ }
+
+ return 0;
+}
+#endif /* UBF_TIMER_PTHREAD */
+
+static VALUE
+ubf_caller(const void *ignore)
+{
+ rb_thread_sleep_forever();
+
+ return Qfalse;
+}
+
+/*
+ * Called if and only if one thread is running, and
+ * the unblock function is NOT async-signal-safe
+ * This assumes USE_THREAD_CACHE is true for performance reasons
+ */
+static VALUE
+rb_thread_start_unblock_thread(void)
+{
+ return rb_thread_create(ubf_caller, 0);
+}
#endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */
diff --git a/thread_pthread.h b/thread_pthread.h
index 1c3782caf4..f2b7e598f7 100644
--- a/thread_pthread.h
+++ b/thread_pthread.h
@@ -16,18 +16,28 @@
#endif
#define RB_NATIVETHREAD_LOCK_INIT PTHREAD_MUTEX_INITIALIZER
-#define RB_NATIVETHREAD_COND_INIT { PTHREAD_COND_INITIALIZER, }
+#define RB_NATIVETHREAD_COND_INIT PTHREAD_COND_INITIALIZER
-typedef struct rb_thread_cond_struct {
- pthread_cond_t cond;
-#ifdef HAVE_CLOCKID_T
- clockid_t clockid;
-#endif
-} rb_nativethread_cond_t;
+typedef pthread_cond_t rb_nativethread_cond_t;
typedef struct native_thread_data_struct {
- struct list_node ubf_list;
- rb_nativethread_cond_t sleep_cond;
+ union {
+ struct list_node ubf;
+ struct list_node gvl;
+ } node;
+#if defined(__GLIBC__) || defined(__FreeBSD__)
+ union
+#else
+ /*
+ * assume the platform condvars are badly implemented and have a
+ * "memory" of which mutex they're associated with
+ */
+ struct
+#endif
+ {
+ rb_nativethread_cond_t intr; /* th->interrupt_lock */
+ rb_nativethread_cond_t gvlq; /* vm->gvl.lock */
+ } cond;
} native_thread_data_t;
#undef except
@@ -37,12 +47,23 @@ typedef struct native_thread_data_struct {
typedef struct rb_global_vm_lock_struct {
/* fast path */
- unsigned long acquired;
- rb_nativethread_lock_t lock;
+ const struct rb_thread_struct *owner;
+ rb_nativethread_lock_t lock; /* AKA vm->gvl.lock */
- /* slow path */
- volatile unsigned long waiting;
- rb_nativethread_cond_t cond;
+ /*
+ * slow path, protected by vm->gvl.lock
+ * - @waitq - FIFO queue of threads waiting for GVL
+ * - @timer - it handles timeslices for @owner. It is any one thread
+ * in @waitq, there is no @timer if @waitq is empty, but always
+ * a @timer if @waitq has entries
+ * - @timer_err tracks timeslice limit, the timeslice only resets
+ * when pthread_cond_timedwait returns ETIMEDOUT, so frequent
+ * switching between contended/uncontended GVL won't reset the
+ * timer.
+ */
+ struct list_head waitq; /* <=> native_thread_data_t.node.ubf */
+ const struct rb_thread_struct *timer;
+ int timer_err;
/* yield */
rb_nativethread_cond_t switch_cond;
diff --git a/thread_sync.c b/thread_sync.c
index 8869af2deb..077276b659 100644
--- a/thread_sync.c
+++ b/thread_sync.c
@@ -1,17 +1,50 @@
/* included by thread.c */
+#include "ccan/list/list.h"
static VALUE rb_cMutex, rb_cQueue, rb_cSizedQueue, rb_cConditionVariable;
static VALUE rb_eClosedQueueError;
+/* sync_waiter is always on-stack */
+struct sync_waiter {
+ rb_thread_t *th;
+ struct list_node node;
+};
+
+#define MUTEX_ALLOW_TRAP FL_USER1
+
+static void
+sync_wakeup(struct list_head *head, long max)
+{
+ struct sync_waiter *cur = 0, *next;
+
+ list_for_each_safe(head, cur, next, node) {
+ list_del_init(&cur->node);
+ if (cur->th->status != THREAD_KILLED) {
+ rb_threadptr_interrupt(cur->th);
+ cur->th->status = THREAD_RUNNABLE;
+ if (--max == 0) return;
+ }
+ }
+}
+
+static void
+wakeup_one(struct list_head *head)
+{
+ sync_wakeup(head, 1);
+}
+
+static void
+wakeup_all(struct list_head *head)
+{
+ sync_wakeup(head, LONG_MAX);
+}
+
/* Mutex */
typedef struct rb_mutex_struct {
- rb_nativethread_lock_t lock;
- rb_nativethread_cond_t cond;
- struct rb_thread_struct volatile *th;
+ rb_thread_t *th;
struct rb_mutex_struct *next_mutex;
- int cond_waiting;
- int allow_trap;
+ struct list_head waitq; /* protected by GVL */
} rb_mutex_t;
#if defined(HAVE_WORKING_FORK)
@@ -19,7 +52,7 @@ static void rb_mutex_abandon_all(rb_mutex_t *mutexes);
static void rb_mutex_abandon_keeping_mutexes(rb_thread_t *th);
static void rb_mutex_abandon_locking_mutex(rb_thread_t *th);
#endif
-static const char* rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t volatile *th);
+static const char* rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t *th);
/*
* Document-class: Mutex
@@ -29,7 +62,6 @@ static const char* rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t volatile *t
*
* Example:
*
- * require 'thread'
* semaphore = Mutex.new
*
* a = Thread.new {
@@ -46,23 +78,29 @@ static const char* rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t volatile *t
*
*/
-#define GetMutexPtr(obj, tobj) \
- TypedData_Get_Struct((obj), rb_mutex_t, &mutex_data_type, (tobj))
-
#define mutex_mark NULL
+static size_t
+rb_mutex_num_waiting(rb_mutex_t *mutex)
+{
+ struct sync_waiter *w = 0;
+ size_t n = 0;
+
+ list_for_each(&mutex->waitq, w, node) {
+ n++;
+ }
+
+ return n;
+}
+
static void
mutex_free(void *ptr)
{
- if (ptr) {
- rb_mutex_t *mutex = ptr;
- if (mutex->th) {
- /* rb_warn("free locked mutex"); */
- const char *err = rb_mutex_unlock_th(mutex, mutex->th);
- if (err) rb_bug("%s", err);
- }
- native_mutex_destroy(&mutex->lock);
- native_cond_destroy(&mutex->cond);
+ rb_mutex_t *mutex = ptr;
+ if (mutex->th) {
+ /* rb_warn("free locked mutex"); */
+ const char *err = rb_mutex_unlock_th(mutex, mutex->th);
+ if (err) rb_bug("%s", err);
}
ruby_xfree(ptr);
}
@@ -79,6 +117,16 @@ static const rb_data_type_t mutex_data_type = {
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
+static rb_mutex_t *
+mutex_ptr(VALUE obj)
+{
+ rb_mutex_t *mutex;
+
+ TypedData_Get_Struct(obj, rb_mutex_t, &mutex_data_type, mutex);
+
+ return mutex;
+}
+
VALUE
rb_obj_is_mutex(VALUE obj)
{
@@ -97,8 +145,7 @@ mutex_alloc(VALUE klass)
rb_mutex_t *mutex;
obj = TypedData_Make_Struct(klass, rb_mutex_t, &mutex_data_type, mutex);
- native_mutex_initialize(&mutex->lock);
- native_cond_initialize(&mutex->cond, RB_CONDATTR_CLOCK_MONOTONIC);
+ list_head_init(&mutex->waitq);
return obj;
}
@@ -129,16 +176,15 @@ rb_mutex_new(void)
VALUE
rb_mutex_locked_p(VALUE self)
{
- rb_mutex_t *mutex;
- GetMutexPtr(self, mutex);
+ rb_mutex_t *mutex = mutex_ptr(self);
+
return mutex->th ? Qtrue : Qfalse;
}
static void
mutex_locked(rb_thread_t *th, VALUE self)
{
- rb_mutex_t *mutex;
- GetMutexPtr(self, mutex);
+ rb_mutex_t *mutex = mutex_ptr(self);
if (th->keeping_mutexes) {
mutex->next_mutex = th->keeping_mutexes;
@@ -156,11 +202,9 @@ mutex_locked(rb_thread_t *th, VALUE self)
VALUE
rb_mutex_trylock(VALUE self)
{
- rb_mutex_t *mutex;
+ rb_mutex_t *mutex = mutex_ptr(self);
VALUE locked = Qfalse;
- GetMutexPtr(self, mutex);
- native_mutex_lock(&mutex->lock);
if (mutex->th == 0) {
rb_thread_t *th = GET_THREAD();
mutex->th = th;
@@ -168,61 +212,10 @@ rb_mutex_trylock(VALUE self)
mutex_locked(th, self);
}
- native_mutex_unlock(&mutex->lock);
return locked;
}
-static int
-lock_func(rb_thread_t *th, rb_mutex_t *mutex, int timeout_ms)
-{
- int interrupted = 0;
- int err = 0;
-
- mutex->cond_waiting++;
- for (;;) {
- if (!mutex->th) {
- mutex->th = th;
- break;
- }
- if (RUBY_VM_INTERRUPTED(th)) {
- interrupted = 1;
- break;
- }
- if (err == ETIMEDOUT) {
- interrupted = 2;
- break;
- }
-
- if (timeout_ms) {
- struct timespec timeout_rel;
- struct timespec timeout;
-
- timeout_rel.tv_sec = 0;
- timeout_rel.tv_nsec = timeout_ms * 1000 * 1000;
- timeout = native_cond_timeout(&mutex->cond, timeout_rel);
- err = native_cond_timedwait(&mutex->cond, &mutex->lock, &timeout);
- }
- else {
- native_cond_wait(&mutex->cond, &mutex->lock);
- err = 0;
- }
- }
- mutex->cond_waiting--;
-
- return interrupted;
-}
-
-static void
-lock_interrupt(void *ptr)
-{
- rb_mutex_t *mutex = (rb_mutex_t *)ptr;
- native_mutex_lock(&mutex->lock);
- if (mutex->cond_waiting > 0)
- native_cond_broadcast(&mutex->cond);
- native_mutex_unlock(&mutex->lock);
-}
-
/*
* At maximum, only one thread can use cond_timedwait and watch deadlock
* periodically. Multiple polling thread (i.e. concurrent deadlock check)
@@ -230,65 +223,59 @@ lock_interrupt(void *ptr)
*/
static const rb_thread_t *patrol_thread = NULL;
-/*
- * call-seq:
- * mutex.lock -> self
- *
- * Attempts to grab the lock and waits if it isn't available.
- * Raises +ThreadError+ if +mutex+ was locked by the current thread.
- */
-VALUE
-rb_mutex_lock(VALUE self)
+static VALUE
+do_mutex_lock(VALUE self, int interruptible_p)
{
rb_thread_t *th = GET_THREAD();
- rb_mutex_t *mutex;
- GetMutexPtr(self, mutex);
+ rb_mutex_t *mutex = mutex_ptr(self);
/* When running trap handler */
- if (!mutex->allow_trap && th->interrupt_mask & TRAP_INTERRUPT_MASK) {
+ if (!FL_TEST_RAW(self, MUTEX_ALLOW_TRAP) &&
+ th->ec->interrupt_mask & TRAP_INTERRUPT_MASK) {
rb_raise(rb_eThreadError, "can't be called from trap context");
}
if (rb_mutex_trylock(self) == Qfalse) {
+ struct sync_waiter w;
+
if (mutex->th == th) {
rb_raise(rb_eThreadError, "deadlock; recursive locking");
}
+ w.th = th;
+
while (mutex->th != th) {
- int interrupted;
enum rb_thread_status prev_status = th->status;
- volatile int timeout_ms = 0;
- struct rb_unblock_callback oldubf;
+ rb_hrtime_t *timeout = 0;
+ rb_hrtime_t rel = rb_msec2hrtime(100);
- set_unblock_function(th, lock_interrupt, mutex, &oldubf, FALSE);
th->status = THREAD_STOPPED_FOREVER;
th->locking_mutex = self;
-
- native_mutex_lock(&mutex->lock);
th->vm->sleeper++;
/*
- * Carefully! while some contended threads are in lock_func(),
- * vm->sleepr is unstable value. we have to avoid both deadlock
+ * Carefully! while some contended threads are in native_sleep(),
+ * vm->sleeper is unstable value. we have to avoid both deadlock
* and busy loop.
*/
if ((vm_living_thread_num(th->vm) == th->vm->sleeper) &&
!patrol_thread) {
- timeout_ms = 100;
+ timeout = &rel;
patrol_thread = th;
}
- GVL_UNLOCK_BEGIN();
- interrupted = lock_func(th, mutex, (int)timeout_ms);
- native_mutex_unlock(&mutex->lock);
- GVL_UNLOCK_END();
+ list_add_tail(&mutex->waitq, &w.node);
+ native_sleep(th, timeout); /* release GVL */
+ list_del(&w.node);
+
+ if (!mutex->th) {
+ mutex->th = th;
+ }
if (patrol_thread == th)
patrol_thread = NULL;
- reset_unblock_function(th, &oldubf);
-
th->locking_mutex = Qfalse;
- if (mutex->th && interrupted == 2) {
+ if (mutex->th && timeout && !RUBY_VM_INTERRUPTED(th->ec)) {
rb_check_deadlock(th->vm);
}
if (th->status == THREAD_STOPPED_FOREVER) {
@@ -296,16 +283,42 @@ rb_mutex_lock(VALUE self)
}
th->vm->sleeper--;
- if (mutex->th == th) mutex_locked(th, self);
-
- if (interrupted) {
- RUBY_VM_CHECK_INTS_BLOCKING(th);
- }
+ if (interruptible_p) {
+ /* release mutex before checking for interrupts...as interrupt checking
+ * code might call rb_raise() */
+ if (mutex->th == th) mutex->th = 0;
+ RUBY_VM_CHECK_INTS_BLOCKING(th->ec); /* may release mutex */
+ if (!mutex->th) {
+ mutex->th = th;
+ mutex_locked(th, self);
+ }
+ } else {
+ if (mutex->th == th) mutex_locked(th, self);
+ }
}
}
return self;
}
+static VALUE
+mutex_lock_uninterruptible(VALUE self)
+{
+ return do_mutex_lock(self, 0);
+}
+
+/*
+ * call-seq:
+ * mutex.lock -> self
+ *
+ * Attempts to grab the lock and waits if it isn't available.
+ * Raises +ThreadError+ if +mutex+ was locked by the current thread.
+ */
+VALUE
+rb_mutex_lock(VALUE self)
+{
+ return do_mutex_lock(self, 1);
+}
+
/*
* call-seq:
* mutex.owned? -> true or false
@@ -317,9 +330,7 @@ rb_mutex_owned_p(VALUE self)
{
VALUE owned = Qfalse;
rb_thread_t *th = GET_THREAD();
- rb_mutex_t *mutex;
-
- GetMutexPtr(self, mutex);
+ rb_mutex_t *mutex = mutex_ptr(self);
if (mutex->th == th)
owned = Qtrue;
@@ -328,12 +339,10 @@ rb_mutex_owned_p(VALUE self)
}
static const char *
-rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t volatile *th)
+rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t *th)
{
const char *err = NULL;
- native_mutex_lock(&mutex->lock);
-
if (mutex->th == 0) {
err = "Attempt to unlock a mutex which is not locked";
}
@@ -341,15 +350,26 @@ rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t volatile *th)
err = "Attempt to unlock a mutex which is locked by another thread";
}
else {
- mutex->th = 0;
- if (mutex->cond_waiting > 0)
- native_cond_signal(&mutex->cond);
- }
+ struct sync_waiter *cur = 0, *next;
+ rb_mutex_t **th_mutex = &th->keeping_mutexes;
- native_mutex_unlock(&mutex->lock);
-
- if (!err) {
- rb_mutex_t *volatile *th_mutex = &th->keeping_mutexes;
+ mutex->th = 0;
+ list_for_each_safe(&mutex->waitq, cur, next, node) {
+ list_del_init(&cur->node);
+ switch (cur->th->status) {
+ case THREAD_RUNNABLE: /* from someone else calling Thread#run */
+ case THREAD_STOPPED_FOREVER: /* likely (rb_mutex_lock) */
+ rb_threadptr_interrupt(cur->th);
+ goto found;
+ case THREAD_STOPPED: /* probably impossible */
+ rb_bug("unexpected THREAD_STOPPED");
+ case THREAD_KILLED:
+ /* not sure about this, possible in exit GC? */
+ rb_bug("unexpected THREAD_KILLED");
+ continue;
+ }
+ }
+ found:
while (*th_mutex != mutex) {
th_mutex = &(*th_mutex)->next_mutex;
}
@@ -371,8 +391,7 @@ VALUE
rb_mutex_unlock(VALUE self)
{
const char *err;
- rb_mutex_t *mutex;
- GetMutexPtr(self, mutex);
+ rb_mutex_t *mutex = mutex_ptr(self);
err = rb_mutex_unlock_th(mutex, GET_THREAD());
if (err) rb_raise(rb_eThreadError, "%s", err);
@@ -384,23 +403,19 @@ rb_mutex_unlock(VALUE self)
static void
rb_mutex_abandon_keeping_mutexes(rb_thread_t *th)
{
- if (th->keeping_mutexes) {
- rb_mutex_abandon_all(th->keeping_mutexes);
- }
+ rb_mutex_abandon_all(th->keeping_mutexes);
th->keeping_mutexes = NULL;
}
static void
rb_mutex_abandon_locking_mutex(rb_thread_t *th)
{
- rb_mutex_t *mutex;
-
- if (!th->locking_mutex) return;
+ if (th->locking_mutex) {
+ rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
- GetMutexPtr(th->locking_mutex, mutex);
- if (mutex->th == th)
- rb_mutex_abandon_all(mutex);
- th->locking_mutex = Qfalse;
+ list_head_init(&mutex->waitq);
+ th->locking_mutex = Qfalse;
+ }
}
static void
@@ -413,6 +428,7 @@ rb_mutex_abandon_all(rb_mutex_t *mutexes)
mutexes = mutex->next_mutex;
mutex->th = 0;
mutex->next_mutex = 0;
+ list_head_init(&mutex->waitq);
}
}
#endif
@@ -420,15 +436,16 @@ rb_mutex_abandon_all(rb_mutex_t *mutexes)
static VALUE
rb_mutex_sleep_forever(VALUE time)
{
- sleep_forever(GET_THREAD(), 1, 0); /* permit spurious check */
+ rb_thread_sleep_deadly_allow_spurious_wakeup();
return Qnil;
}
static VALUE
rb_mutex_wait_for(VALUE time)
{
- struct timeval *t = (struct timeval *)time;
- sleep_timeval(GET_THREAD(), *t, 0); /* permit spurious check */
+ rb_hrtime_t *rel = (rb_hrtime_t *)time;
+ /* permit spurious check */
+ sleep_hrtime(GET_THREAD(), *rel, 0);
return Qnil;
}
@@ -441,14 +458,19 @@ rb_mutex_sleep(VALUE self, VALUE timeout)
if (!NIL_P(timeout)) {
t = rb_time_interval(timeout);
}
+
rb_mutex_unlock(self);
beg = time(0);
if (NIL_P(timeout)) {
- rb_ensure(rb_mutex_sleep_forever, Qnil, rb_mutex_lock, self);
+ rb_ensure(rb_mutex_sleep_forever, Qnil, mutex_lock_uninterruptible, self);
}
else {
- rb_ensure(rb_mutex_wait_for, (VALUE)&t, rb_mutex_lock, self);
+ rb_hrtime_t rel = rb_timeval2hrtime(&t);
+
+ rb_ensure(rb_mutex_wait_for, (VALUE)&rel,
+ mutex_lock_uninterruptible, self);
}
+ RUBY_VM_CHECK_INTS_BLOCKING(GET_EC());
end = time(0) - beg;
return INT2FIX(end);
}
@@ -472,7 +494,7 @@ mutex_sleep(int argc, VALUE *argv, VALUE self)
{
VALUE timeout;
- rb_scan_args(argc, argv, "01", &timeout);
+ timeout = rb_check_arity(argc, 0, 1) ? argv[0] : Qnil;
return rb_mutex_sleep(self, timeout);
}
@@ -510,88 +532,157 @@ rb_mutex_synchronize_m(VALUE self, VALUE args)
void rb_mutex_allow_trap(VALUE self, int val)
{
- rb_mutex_t *m;
- GetMutexPtr(self, m);
+ Check_TypedStruct(self, &mutex_data_type);
- m->allow_trap = val;
+ if (val)
+ FL_SET_RAW(self, MUTEX_ALLOW_TRAP);
+ else
+ FL_UNSET_RAW(self, MUTEX_ALLOW_TRAP);
}
/* Queue */
-enum {
- QUEUE_QUE,
- QUEUE_WAITERS,
- SZQUEUE_WAITERS,
- SZQUEUE_MAX,
- END_QUEUE
-};
+#define queue_waitq(q) UNALIGNED_MEMBER_PTR(q, waitq)
+PACKED_STRUCT_UNALIGNED(struct rb_queue {
+ struct list_head waitq;
+ rb_serial_t fork_gen;
+ const VALUE que;
+ int num_waiting;
+});
+
+#define szqueue_waitq(sq) UNALIGNED_MEMBER_PTR(sq, q.waitq)
+#define szqueue_pushq(sq) UNALIGNED_MEMBER_PTR(sq, pushq)
+PACKED_STRUCT_UNALIGNED(struct rb_szqueue {
+ struct rb_queue q;
+ int num_waiting_push;
+ struct list_head pushq;
+ long max;
+});
-#define QUEUE_CLOSED FL_USER5
+static void
+queue_mark(void *ptr)
+{
+ struct rb_queue *q = ptr;
-#define GET_QUEUE_QUE(q) get_array((q), QUEUE_QUE)
-#define GET_QUEUE_WAITERS(q) get_array((q), QUEUE_WAITERS)
-#define GET_SZQUEUE_WAITERS(q) get_array((q), SZQUEUE_WAITERS)
-#define GET_SZQUEUE_MAX(q) RSTRUCT_GET((q), SZQUEUE_MAX)
-#define GET_SZQUEUE_ULONGMAX(q) NUM2ULONG(GET_SZQUEUE_MAX(q))
+ /* no need to mark threads in waitq, they are on stack */
+ rb_gc_mark(q->que);
+}
-static VALUE
-ary_buf_new(void)
+static size_t
+queue_memsize(const void *ptr)
{
- return rb_ary_tmp_new(1);
+ return sizeof(struct rb_queue);
}
+static const rb_data_type_t queue_data_type = {
+ "queue",
+ {queue_mark, RUBY_TYPED_DEFAULT_FREE, queue_memsize,},
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED
+};
+
static VALUE
-get_array(VALUE obj, int idx)
+queue_alloc(VALUE klass)
{
- VALUE ary = RSTRUCT_GET(obj, idx);
- if (!RB_TYPE_P(ary, T_ARRAY)) {
- rb_raise(rb_eTypeError, "%+"PRIsVALUE" not initialized", obj);
- }
- return ary;
+ VALUE obj;
+ struct rb_queue *q;
+
+ obj = TypedData_Make_Struct(klass, struct rb_queue, &queue_data_type, q);
+ list_head_init(queue_waitq(q));
+ return obj;
}
-static void
-wakeup_first_thread(VALUE list)
+static int
+queue_fork_check(struct rb_queue *q)
{
- VALUE thread;
+ rb_serial_t fork_gen = GET_VM()->fork_gen;
- while (!NIL_P(thread = rb_ary_shift(list))) {
- if (RTEST(rb_thread_wakeup_alive(thread))) break;
+ if (q->fork_gen == fork_gen) {
+ return 0;
}
+ /* forked children can't reach into parent thread stacks */
+ q->fork_gen = fork_gen;
+ list_head_init(queue_waitq(q));
+ q->num_waiting = 0;
+ return 1;
+}
+
+static struct rb_queue *
+queue_ptr(VALUE obj)
+{
+ struct rb_queue *q;
+
+ TypedData_Get_Struct(obj, struct rb_queue, &queue_data_type, q);
+ queue_fork_check(q);
+
+ return q;
}
+#define QUEUE_CLOSED FL_USER5
+
static void
-wakeup_all_threads(VALUE list)
+szqueue_mark(void *ptr)
+{
+ struct rb_szqueue *sq = ptr;
+
+ queue_mark(&sq->q);
+}
+
+static size_t
+szqueue_memsize(const void *ptr)
+{
+ return sizeof(struct rb_szqueue);
+}
+
+static const rb_data_type_t szqueue_data_type = {
+ "sized_queue",
+ {szqueue_mark, RUBY_TYPED_DEFAULT_FREE, szqueue_memsize,},
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED
+};
+
+static VALUE
+szqueue_alloc(VALUE klass)
+{
+ struct rb_szqueue *sq;
+ VALUE obj = TypedData_Make_Struct(klass, struct rb_szqueue,
+ &szqueue_data_type, sq);
+ list_head_init(szqueue_waitq(sq));
+ list_head_init(szqueue_pushq(sq));
+ return obj;
+}
+
+static struct rb_szqueue *
+szqueue_ptr(VALUE obj)
{
- VALUE thread;
- long i;
+ struct rb_szqueue *sq;
- for (i=0; i<RARRAY_LEN(list); i++) {
- thread = RARRAY_AREF(list, i);
- rb_thread_wakeup_alive(thread);
+ TypedData_Get_Struct(obj, struct rb_szqueue, &szqueue_data_type, sq);
+ if (queue_fork_check(&sq->q)) {
+ list_head_init(szqueue_pushq(sq));
+ sq->num_waiting_push = 0;
}
- rb_ary_clear(list);
+
+ return sq;
}
-static unsigned long
-queue_length(VALUE self)
+static VALUE
+ary_buf_new(void)
{
- VALUE que = GET_QUEUE_QUE(self);
- return RARRAY_LEN(que);
+ return rb_ary_tmp_new(1);
}
-static unsigned long
-queue_num_waiting(VALUE self)
+static VALUE
+check_array(VALUE obj, VALUE ary)
{
- VALUE waiters = GET_QUEUE_WAITERS(self);
- return RARRAY_LEN(waiters);
+ if (!RB_TYPE_P(ary, T_ARRAY)) {
+ rb_raise(rb_eTypeError, "%+"PRIsVALUE" not initialized", obj);
+ }
+ return ary;
}
-static unsigned long
-szqueue_num_waiting_producer(VALUE self)
+static long
+queue_length(VALUE self, struct rb_queue *q)
{
- VALUE waiters = GET_SZQUEUE_WAITERS(self);
- return RARRAY_LEN(waiters);
+ return RARRAY_LEN(check_array(self, q->que));
}
static int
@@ -600,6 +691,15 @@ queue_closed_p(VALUE self)
return FL_TEST_RAW(self, QUEUE_CLOSED) != 0;
}
+/*
+ * Document-class: ClosedQueueError
+ *
+ * The exception class which will be raised when pushing into a closed
+ * Queue. See Queue#close and SizedQueue#close.
+ */
+
+NORETURN(static void raise_closed_queue_error(VALUE self));
+
static void
raise_closed_queue_error(VALUE self)
{
@@ -607,32 +707,12 @@ raise_closed_queue_error(VALUE self)
}
static VALUE
-queue_closed_result(VALUE self)
+queue_closed_result(VALUE self, struct rb_queue *q)
{
- assert(queue_length(self) == 0);
+ assert(queue_length(self, q) == 0);
return Qnil;
}
-static VALUE
-queue_do_close(VALUE self, int is_szq)
-{
- if (!queue_closed_p(self)) {
- FL_SET(self, QUEUE_CLOSED);
-
- if (queue_num_waiting(self) > 0) {
- VALUE waiters = GET_QUEUE_WAITERS(self);
- wakeup_all_threads(waiters);
- }
-
- if (is_szq && szqueue_num_waiting_producer(self) > 0) {
- VALUE waiters = GET_SZQUEUE_WAITERS(self);
- wakeup_all_threads(waiters);
- }
- }
-
- return self;
-}
-
/*
* Document-class: Queue
*
@@ -646,8 +726,7 @@ queue_do_close(VALUE self, int is_szq)
*
* Example:
*
- * require 'thread'
- * queue = Queue.new
+ * queue = Queue.new
*
* producer = Thread.new do
* 5.times do |i|
@@ -665,6 +744,8 @@ queue_do_close(VALUE self, int is_szq)
* end
* end
*
+ * consumer.join
+ *
*/
/*
@@ -676,19 +757,20 @@ queue_do_close(VALUE self, int is_szq)
static VALUE
rb_queue_initialize(VALUE self)
{
- RSTRUCT_SET(self, QUEUE_QUE, ary_buf_new());
- RSTRUCT_SET(self, QUEUE_WAITERS, ary_buf_new());
+ struct rb_queue *q = queue_ptr(self);
+ RB_OBJ_WRITE(self, &q->que, ary_buf_new());
+ list_head_init(queue_waitq(q));
return self;
}
static VALUE
-queue_do_push(VALUE self, VALUE obj)
+queue_do_push(VALUE self, struct rb_queue *q, VALUE obj)
{
if (queue_closed_p(self)) {
raise_closed_queue_error(self);
}
- rb_ary_push(GET_QUEUE_QUE(self), obj);
- wakeup_first_thread(GET_QUEUE_WAITERS(self));
+ rb_ary_push(check_array(self, q->que), obj);
+ wakeup_one(queue_waitq(q));
return self;
}
@@ -705,10 +787,12 @@ queue_do_push(VALUE self, VALUE obj)
*
* - +close+ will be ignored.
*
- * - calling enq/push/<< will return nil.
+ * - calling enq/push/<< will raise a +ClosedQueueError+.
*
* - when +empty?+ is false, calling deq/pop/shift will return an object
* from the queue as usual.
+ * - when +empty?+ is true, deq(false) will not suspend the thread and will return nil.
+ * deq(true) will raise a +ThreadError+.
*
* ClosedQueueError is inherited from StopIteration, so that you can break loop block.
*
@@ -726,7 +810,15 @@ queue_do_push(VALUE self, VALUE obj)
static VALUE
rb_queue_close(VALUE self)
{
- return queue_do_close(self, FALSE);
+ struct rb_queue *q = queue_ptr(self);
+
+ if (!queue_closed_p(self)) {
+ FL_SET(self, QUEUE_CLOSED);
+
+ wakeup_all(queue_waitq(q));
+ }
+
+ return self;
}
/*
@@ -755,52 +847,74 @@ rb_queue_closed_p(VALUE self)
static VALUE
rb_queue_push(VALUE self, VALUE obj)
{
- return queue_do_push(self, obj);
+ return queue_do_push(self, queue_ptr(self), obj);
}
-struct waiting_delete {
- VALUE waiting;
- VALUE th;
+static VALUE
+queue_sleep(VALUE arg)
+{
+ rb_thread_sleep_deadly_allow_spurious_wakeup();
+ return Qnil;
+}
+
+struct queue_waiter {
+ struct sync_waiter w;
+ union {
+ struct rb_queue *q;
+ struct rb_szqueue *sq;
+ } as;
};
static VALUE
-queue_delete_from_waiting(struct waiting_delete *p)
+queue_sleep_done(VALUE p)
{
- rb_ary_delete(p->waiting, p->th);
- return Qnil;
+ struct queue_waiter *qw = (struct queue_waiter *)p;
+
+ list_del(&qw->w.node);
+ qw->as.q->num_waiting--;
+
+ return Qfalse;
}
static VALUE
-queue_sleep(VALUE arg)
+szqueue_sleep_done(VALUE p)
{
- rb_thread_sleep_deadly();
- return Qnil;
+ struct queue_waiter *qw = (struct queue_waiter *)p;
+
+ list_del(&qw->w.node);
+ qw->as.sq->num_waiting_push--;
+
+ return Qfalse;
}
static VALUE
-queue_do_pop(VALUE self, int should_block)
+queue_do_pop(VALUE self, struct rb_queue *q, int should_block)
{
- struct waiting_delete args;
- args.waiting = GET_QUEUE_WAITERS(self);
- args.th = rb_thread_current();
+ check_array(self, q->que);
- while (queue_length(self) == 0) {
+ while (RARRAY_LEN(q->que) == 0) {
if (!should_block) {
rb_raise(rb_eThreadError, "queue empty");
}
else if (queue_closed_p(self)) {
- return queue_closed_result(self);
+ return queue_closed_result(self, q);
}
else {
- assert(queue_length(self) == 0);
+ struct queue_waiter qw;
+
+ assert(RARRAY_LEN(q->que) == 0);
assert(queue_closed_p(self) == 0);
- rb_ary_push(args.waiting, args.th);
- rb_ensure(queue_sleep, Qfalse, queue_delete_from_waiting, (VALUE)&args);
+ qw.w.th = GET_THREAD();
+ qw.as.q = q;
+ list_add_tail(&qw.as.q->waitq, &qw.w.node);
+ qw.as.q->num_waiting++;
+
+ rb_ensure(queue_sleep, self, queue_sleep_done, (VALUE)&qw);
}
}
- return rb_ary_shift(GET_QUEUE_QUE(self));
+ return rb_ary_shift(q->que);
}
static int
@@ -832,7 +946,7 @@ static VALUE
rb_queue_pop(int argc, VALUE *argv, VALUE self)
{
int should_block = queue_pop_should_block(argc, argv);
- return queue_do_pop(self, should_block);
+ return queue_do_pop(self, queue_ptr(self), should_block);
}
/*
@@ -845,7 +959,7 @@ rb_queue_pop(int argc, VALUE *argv, VALUE self)
static VALUE
rb_queue_empty_p(VALUE self)
{
- return queue_length(self) == 0 ? Qtrue : Qfalse;
+ return queue_length(self, queue_ptr(self)) == 0 ? Qtrue : Qfalse;
}
/*
@@ -857,7 +971,9 @@ rb_queue_empty_p(VALUE self)
static VALUE
rb_queue_clear(VALUE self)
{
- rb_ary_clear(GET_QUEUE_QUE(self));
+ struct rb_queue *q = queue_ptr(self);
+
+ rb_ary_clear(check_array(self, q->que));
return self;
}
@@ -873,8 +989,7 @@ rb_queue_clear(VALUE self)
static VALUE
rb_queue_length(VALUE self)
{
- unsigned long len = queue_length(self);
- return ULONG2NUM(len);
+ return LONG2NUM(queue_length(self, queue_ptr(self)));
}
/*
@@ -886,8 +1001,9 @@ rb_queue_length(VALUE self)
static VALUE
rb_queue_num_waiting(VALUE self)
{
- unsigned long len = queue_num_waiting(self);
- return ULONG2NUM(len);
+ struct rb_queue *q = queue_ptr(self);
+
+ return INT2NUM(q->num_waiting);
}
/*
@@ -910,16 +1026,17 @@ static VALUE
rb_szqueue_initialize(VALUE self, VALUE vmax)
{
long max;
+ struct rb_szqueue *sq = szqueue_ptr(self);
max = NUM2LONG(vmax);
if (max <= 0) {
rb_raise(rb_eArgError, "queue size must be positive");
}
- RSTRUCT_SET(self, QUEUE_QUE, ary_buf_new());
- RSTRUCT_SET(self, QUEUE_WAITERS, ary_buf_new());
- RSTRUCT_SET(self, SZQUEUE_WAITERS, ary_buf_new());
- RSTRUCT_SET(self, SZQUEUE_MAX, vmax);
+ RB_OBJ_WRITE(self, &sq->q.que, ary_buf_new());
+ list_head_init(szqueue_waitq(sq));
+ list_head_init(szqueue_pushq(sq));
+ sq->max = max;
return self;
}
@@ -939,7 +1056,14 @@ rb_szqueue_initialize(VALUE self, VALUE vmax)
static VALUE
rb_szqueue_close(VALUE self)
{
- return queue_do_close(self, TRUE);
+ if (!queue_closed_p(self)) {
+ struct rb_szqueue *sq = szqueue_ptr(self);
+
+ FL_SET(self, QUEUE_CLOSED);
+ wakeup_all(szqueue_waitq(sq));
+ wakeup_all(szqueue_pushq(sq));
+ }
+ return self;
}
/*
@@ -951,7 +1075,7 @@ rb_szqueue_close(VALUE self)
static VALUE
rb_szqueue_max_get(VALUE self)
{
- return GET_SZQUEUE_MAX(self);
+ return LONG2NUM(szqueue_ptr(self)->max);
}
/*
@@ -964,19 +1088,18 @@ rb_szqueue_max_get(VALUE self)
static VALUE
rb_szqueue_max_set(VALUE self, VALUE vmax)
{
- long max = NUM2LONG(vmax), diff = 0;
- VALUE t;
+ long max = NUM2LONG(vmax);
+ long diff = 0;
+ struct rb_szqueue *sq = szqueue_ptr(self);
if (max <= 0) {
rb_raise(rb_eArgError, "queue size must be positive");
}
- if ((unsigned long)max > GET_SZQUEUE_ULONGMAX(self)) {
- diff = max - GET_SZQUEUE_ULONGMAX(self);
- }
- RSTRUCT_SET(self, SZQUEUE_MAX, vmax);
- while (diff-- > 0 && !NIL_P(t = rb_ary_shift(GET_SZQUEUE_WAITERS(self)))) {
- rb_thread_wakeup_alive(t);
+ if (max > sq->max) {
+ diff = max - sq->max;
}
+ sq->max = max;
+ sync_wakeup(szqueue_pushq(sq), diff);
return vmax;
}
@@ -1008,12 +1131,10 @@ szqueue_push_should_block(int argc, const VALUE *argv)
static VALUE
rb_szqueue_push(int argc, VALUE *argv, VALUE self)
{
- struct waiting_delete args;
+ struct rb_szqueue *sq = szqueue_ptr(self);
int should_block = szqueue_push_should_block(argc, argv);
- args.waiting = GET_SZQUEUE_WAITERS(self);
- args.th = rb_thread_current();
- while (queue_length(self) >= GET_SZQUEUE_ULONGMAX(self)) {
+ while (queue_length(self, &sq->q) >= sq->max) {
if (!should_block) {
rb_raise(rb_eThreadError, "queue full");
}
@@ -1021,8 +1142,15 @@ rb_szqueue_push(int argc, VALUE *argv, VALUE self)
goto closed;
}
else {
- rb_ary_push(args.waiting, args.th);
- rb_ensure(queue_sleep, Qfalse, queue_delete_from_waiting, (VALUE)&args);
+ struct queue_waiter qw;
+ struct list_head *pushq = szqueue_pushq(sq);
+
+ qw.w.th = GET_THREAD();
+ qw.as.sq = sq;
+ list_add_tail(pushq, &qw.w.node);
+ sq->num_waiting_push++;
+
+ rb_ensure(queue_sleep, self, szqueue_sleep_done, (VALUE)&qw);
}
}
@@ -1031,16 +1159,17 @@ rb_szqueue_push(int argc, VALUE *argv, VALUE self)
raise_closed_queue_error(self);
}
- return queue_do_push(self, argv[0]);
+ return queue_do_push(self, &sq->q, argv[0]);
}
static VALUE
szqueue_do_pop(VALUE self, int should_block)
{
- VALUE retval = queue_do_pop(self, should_block);
+ struct rb_szqueue *sq = szqueue_ptr(self);
+ VALUE retval = queue_do_pop(self, &sq->q, should_block);
- if (queue_length(self) < GET_SZQUEUE_ULONGMAX(self)) {
- wakeup_first_thread(GET_SZQUEUE_WAITERS(self));
+ if (queue_length(self, &sq->q) < sq->max) {
+ wakeup_one(szqueue_pushq(sq));
}
return retval;
@@ -1068,7 +1197,7 @@ rb_szqueue_pop(int argc, VALUE *argv, VALUE self)
}
/*
- * Document-method: Queue#clear
+ * Document-method: SizedQueue#clear
*
* Removes all objects from the queue.
*/
@@ -1076,12 +1205,31 @@ rb_szqueue_pop(int argc, VALUE *argv, VALUE self)
static VALUE
rb_szqueue_clear(VALUE self)
{
- rb_ary_clear(GET_QUEUE_QUE(self));
- wakeup_all_threads(GET_SZQUEUE_WAITERS(self));
+ struct rb_szqueue *sq = szqueue_ptr(self);
+
+ rb_ary_clear(check_array(self, sq->q.que));
+ wakeup_all(szqueue_pushq(sq));
return self;
}
/*
+ * Document-method: SizedQueue#length
+ * call-seq:
+ * length
+ * size
+ *
+ * Returns the length of the queue.
+ */
+
+static VALUE
+rb_szqueue_length(VALUE self)
+{
+ struct rb_szqueue *sq = szqueue_ptr(self);
+
+ return LONG2NUM(queue_length(self, &sq->q));
+}
+
+/*
* Document-method: SizedQueue#num_waiting
*
* Returns the number of threads waiting on the queue.
@@ -1090,18 +1238,32 @@ rb_szqueue_clear(VALUE self)
static VALUE
rb_szqueue_num_waiting(VALUE self)
{
- long len = queue_num_waiting(self) + szqueue_num_waiting_producer(self);
- return ULONG2NUM(len);
+ struct rb_szqueue *sq = szqueue_ptr(self);
+
+ return INT2NUM(sq->q.num_waiting + sq->num_waiting_push);
}
-/* ConditionalVariable */
+/*
+ * Document-method: SizedQueue#empty?
+ * call-seq: empty?
+ *
+ * Returns +true+ if the queue is empty.
+ */
-enum {
- CONDVAR_WAITERS,
- END_CONDVAR
-};
+static VALUE
+rb_szqueue_empty_p(VALUE self)
+{
+ struct rb_szqueue *sq = szqueue_ptr(self);
-#define GET_CONDVAR_WAITERS(cv) get_array((cv), CONDVAR_WAITERS)
+ return queue_length(self, &sq->q) == 0 ? Qtrue : Qfalse;
+}
+
+
+/* ConditionalVariable */
+struct rb_condvar {
+ struct list_head waitq;
+ rb_serial_t fork_gen;
+};
/*
* Document-class: ConditionVariable
@@ -1112,8 +1274,6 @@ enum {
*
* Example:
*
- * require 'thread'
- *
* mutex = Mutex.new
* resource = ConditionVariable.new
*
@@ -1133,6 +1293,47 @@ enum {
* }
*/
+static size_t
+condvar_memsize(const void *ptr)
+{
+ return sizeof(struct rb_condvar);
+}
+
+static const rb_data_type_t cv_data_type = {
+ "condvar",
+ {0, RUBY_TYPED_DEFAULT_FREE, condvar_memsize,},
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED
+};
+
+static struct rb_condvar *
+condvar_ptr(VALUE self)
+{
+ struct rb_condvar *cv;
+ rb_serial_t fork_gen = GET_VM()->fork_gen;
+
+ TypedData_Get_Struct(self, struct rb_condvar, &cv_data_type, cv);
+
+ /* forked children can't reach into parent thread stacks */
+ if (cv->fork_gen != fork_gen) {
+ cv->fork_gen = fork_gen;
+ list_head_init(&cv->waitq);
+ }
+
+ return cv;
+}
+
+static VALUE
+condvar_alloc(VALUE klass)
+{
+ struct rb_condvar *cv;
+ VALUE obj;
+
+ obj = TypedData_Make_Struct(klass, struct rb_condvar, &cv_data_type, cv);
+ list_head_init(&cv->waitq);
+
+ return obj;
+}
+
/*
* Document-method: ConditionVariable::new
*
@@ -1142,7 +1343,8 @@ enum {
static VALUE
rb_condvar_initialize(VALUE self)
{
- RSTRUCT_SET(self, CONDVAR_WAITERS, ary_buf_new());
+ struct rb_condvar *cv = condvar_ptr(self);
+ list_head_init(&cv->waitq);
return self;
}
@@ -1161,9 +1363,11 @@ do_sleep(VALUE args)
}
static VALUE
-delete_current_thread(VALUE ary)
+delete_from_waitq(struct sync_waiter *w)
{
- return rb_ary_delete(ary, rb_thread_current());
+ list_del(&w->node);
+
+ return Qnil;
}
/*
@@ -1179,16 +1383,15 @@ delete_current_thread(VALUE ary)
static VALUE
rb_condvar_wait(int argc, VALUE *argv, VALUE self)
{
- VALUE waiters = GET_CONDVAR_WAITERS(self);
- VALUE mutex, timeout;
+ struct rb_condvar *cv = condvar_ptr(self);
struct sleep_call args;
+ struct sync_waiter w;
- rb_scan_args(argc, argv, "11", &mutex, &timeout);
+ rb_scan_args(argc, argv, "11", &args.mutex, &args.timeout);
- args.mutex = mutex;
- args.timeout = timeout;
- rb_ary_push(waiters, rb_thread_current());
- rb_ensure(do_sleep, (VALUE)&args, delete_current_thread, waiters);
+ w.th = GET_THREAD();
+ list_add_tail(&cv->waitq, &w.node);
+ rb_ensure(do_sleep, (VALUE)&args, delete_from_waitq, (VALUE)&w);
return self;
}
@@ -1202,7 +1405,8 @@ rb_condvar_wait(int argc, VALUE *argv, VALUE self)
static VALUE
rb_condvar_signal(VALUE self)
{
- wakeup_first_thread(GET_CONDVAR_WAITERS(self));
+ struct rb_condvar *cv = condvar_ptr(self);
+ wakeup_one(&cv->waitq);
return self;
}
@@ -1215,7 +1419,8 @@ rb_condvar_signal(VALUE self)
static VALUE
rb_condvar_broadcast(VALUE self)
{
- wakeup_all_threads(GET_CONDVAR_WAITERS(self));
+ struct rb_condvar *cv = condvar_ptr(self);
+ wakeup_all(&cv->waitq);
return self;
}
@@ -1224,26 +1429,33 @@ static VALUE
undumpable(VALUE obj)
{
rb_raise(rb_eTypeError, "can't dump %"PRIsVALUE, rb_obj_class(obj));
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
-static void
-alias_global_const(const char *name, VALUE klass)
+static VALUE
+define_thread_class(VALUE outer, const char *name, VALUE super)
{
+ VALUE klass = rb_define_class_under(outer, name, super);
rb_define_const(rb_cObject, name, klass);
+ return klass;
}
static void
Init_thread_sync(void)
{
+#undef rb_intern
#if 0
+ rb_cMutex = rb_define_class("Mutex", rb_cObject); /* teach rdoc Mutex */
rb_cConditionVariable = rb_define_class("ConditionVariable", rb_cObject); /* teach rdoc ConditionVariable */
rb_cQueue = rb_define_class("Queue", rb_cObject); /* teach rdoc Queue */
rb_cSizedQueue = rb_define_class("SizedQueue", rb_cObject); /* teach rdoc SizedQueue */
#endif
+#define DEFINE_CLASS(name, super) \
+ rb_c##name = define_thread_class(rb_cThread, #name, rb_c##super)
+
/* Mutex */
- rb_cMutex = rb_define_class_under(rb_cThread, "Mutex", rb_cObject);
+ DEFINE_CLASS(Mutex, Object);
rb_define_alloc_func(rb_cMutex, mutex_alloc);
rb_define_method(rb_cMutex, "initialize", mutex_initialize, 0);
rb_define_method(rb_cMutex, "locked?", rb_mutex_locked_p, 0);
@@ -1255,10 +1467,8 @@ Init_thread_sync(void)
rb_define_method(rb_cMutex, "owned?", rb_mutex_owned_p, 0);
/* Queue */
- rb_cQueue = rb_struct_define_without_accessor_under(
- rb_cThread,
- "Queue", rb_cObject, rb_struct_alloc_noinit,
- "que", "waiters", NULL);
+ DEFINE_CLASS(Queue, Object);
+ rb_define_alloc_func(rb_cQueue, queue_alloc);
rb_eClosedQueueError = rb_define_class("ClosedQueueError", rb_eStopIteration);
@@ -1280,10 +1490,8 @@ Init_thread_sync(void)
rb_define_alias(rb_cQueue, "shift", "pop");
rb_define_alias(rb_cQueue, "size", "length");
- rb_cSizedQueue = rb_struct_define_without_accessor_under(
- rb_cThread,
- "SizedQueue", rb_cQueue, rb_struct_alloc_noinit,
- "que", "waiters", "queue_waiters", "size", NULL);
+ DEFINE_CLASS(SizedQueue, Queue);
+ rb_define_alloc_func(rb_cSizedQueue, szqueue_alloc);
rb_define_method(rb_cSizedQueue, "initialize", rb_szqueue_initialize, 1);
rb_define_method(rb_cSizedQueue, "close", rb_szqueue_close, 0);
@@ -1291,19 +1499,20 @@ Init_thread_sync(void)
rb_define_method(rb_cSizedQueue, "max=", rb_szqueue_max_set, 1);
rb_define_method(rb_cSizedQueue, "push", rb_szqueue_push, -1);
rb_define_method(rb_cSizedQueue, "pop", rb_szqueue_pop, -1);
+ rb_define_method(rb_cSizedQueue, "empty?", rb_szqueue_empty_p, 0);
rb_define_method(rb_cSizedQueue, "clear", rb_szqueue_clear, 0);
+ rb_define_method(rb_cSizedQueue, "length", rb_szqueue_length, 0);
rb_define_method(rb_cSizedQueue, "num_waiting", rb_szqueue_num_waiting, 0);
rb_define_alias(rb_cSizedQueue, "enq", "push");
rb_define_alias(rb_cSizedQueue, "<<", "push");
rb_define_alias(rb_cSizedQueue, "deq", "pop");
rb_define_alias(rb_cSizedQueue, "shift", "pop");
+ rb_define_alias(rb_cSizedQueue, "size", "length");
/* CVar */
- rb_cConditionVariable = rb_struct_define_without_accessor_under(
- rb_cThread,
- "ConditionVariable", rb_cObject, rb_struct_alloc_noinit,
- "waiters", NULL);
+ DEFINE_CLASS(ConditionVariable, Object);
+ rb_define_alloc_func(rb_cConditionVariable, condvar_alloc);
id_sleep = rb_intern("sleep");
@@ -1314,12 +1523,5 @@ Init_thread_sync(void)
rb_define_method(rb_cConditionVariable, "signal", rb_condvar_signal, 0);
rb_define_method(rb_cConditionVariable, "broadcast", rb_condvar_broadcast, 0);
-#define ALIAS_GLOBAL_CONST(name) \
- alias_global_const(#name, rb_c##name)
-
- ALIAS_GLOBAL_CONST(Mutex);
- ALIAS_GLOBAL_CONST(Queue);
- ALIAS_GLOBAL_CONST(SizedQueue);
- ALIAS_GLOBAL_CONST(ConditionVariable);
rb_provide("thread.rb");
}
diff --git a/thread_win32.c b/thread_win32.c
index 01d58a26d8..545d0dd35b 100644
--- a/thread_win32.c
+++ b/thread_win32.c
@@ -20,12 +20,16 @@
#define native_thread_yield() Sleep(0)
#define unregister_ubf_list(th)
+#define ubf_wakeup_all_threads() do {} while (0)
+#define ubf_threads_empty() (1)
+#define ubf_timer_disarm() do {} while (0)
+#define ubf_list_atfork() do {} while (0)
static volatile DWORD ruby_native_thread_key = TLS_OUT_OF_INDEXES;
static int w32_wait_events(HANDLE *events, int count, DWORD timeout, rb_thread_t *th);
-static int native_mutex_lock(rb_nativethread_lock_t *lock);
-static int native_mutex_unlock(rb_nativethread_lock_t *lock);
+void rb_native_mutex_lock(rb_nativethread_lock_t *lock);
+void rb_native_mutex_unlock(rb_nativethread_lock_t *lock);
static void
w32_error(const char *func)
@@ -54,7 +58,7 @@ w32_mutex_lock(HANDLE lock)
{
DWORD result;
while (1) {
- thread_debug("native_mutex_lock: %p\n", lock);
+ thread_debug("rb_native_mutex_lock: %p\n", lock);
result = w32_wait_events(&lock, 1, INFINITE, 0);
switch (result) {
case WAIT_OBJECT_0:
@@ -85,7 +89,7 @@ w32_mutex_create(void)
{
HANDLE lock = CreateMutex(NULL, FALSE, NULL);
if (lock == NULL) {
- w32_error("native_mutex_initialize");
+ w32_error("rb_native_mutex_initialize");
}
return lock;
}
@@ -140,10 +144,8 @@ ruby_thread_set_native(rb_thread_t *th)
}
void
-Init_native_thread(void)
+Init_native_thread(rb_thread_t *th)
{
- rb_thread_t *th = GET_THREAD();
-
ruby_native_thread_key = TlsAlloc();
ruby_thread_set_native(th);
DuplicateHandle(GetCurrentProcess(),
@@ -158,53 +160,34 @@ Init_native_thread(void)
th->native_thread_data.interrupt_event);
}
-static void
-w32_set_event(HANDLE handle)
-{
- if (SetEvent(handle) == 0) {
- w32_error("w32_set_event");
- }
-}
-
-static void
-w32_reset_event(HANDLE handle)
-{
- if (ResetEvent(handle) == 0) {
- w32_error("w32_reset_event");
- }
-}
-
static int
w32_wait_events(HANDLE *events, int count, DWORD timeout, rb_thread_t *th)
{
HANDLE *targets = events;
HANDLE intr;
+ const int initcount = count;
DWORD ret;
thread_debug(" w32_wait_events events:%p, count:%d, timeout:%ld, th:%p\n",
events, count, timeout, th);
if (th && (intr = th->native_thread_data.interrupt_event)) {
- gvl_acquire(th->vm, th);
- if (intr == th->native_thread_data.interrupt_event) {
- w32_reset_event(intr);
- if (RUBY_VM_INTERRUPTED(th)) {
- w32_set_event(intr);
- }
-
+ if (ResetEvent(intr) && (!RUBY_VM_INTERRUPTED(th->ec) || SetEvent(intr))) {
targets = ALLOCA_N(HANDLE, count + 1);
memcpy(targets, events, sizeof(HANDLE) * count);
targets[count++] = intr;
thread_debug(" * handle: %p (count: %d, intr)\n", intr, count);
}
- gvl_release(th->vm);
+ else if (intr == th->native_thread_data.interrupt_event) {
+ w32_error("w32_wait_events");
+ }
}
thread_debug(" WaitForMultipleObjects start (count: %d)\n", count);
ret = WaitForMultipleObjects(count, targets, FALSE, timeout);
thread_debug(" WaitForMultipleObjects end (ret: %lu)\n", ret);
- if (ret == (DWORD)(WAIT_OBJECT_0 + count - 1) && th) {
+ if (ret == (DWORD)(WAIT_OBJECT_0 + initcount) && th) {
errno = EINTR;
}
if (ret == WAIT_FAILED && THREAD_DEBUG) {
@@ -231,8 +214,9 @@ int
rb_w32_wait_events(HANDLE *events, int num, DWORD timeout)
{
int ret;
+ rb_thread_t *th = GET_THREAD();
- BLOCKING_REGION(ret = rb_w32_wait_events_blocking(events, num, timeout),
+ BLOCKING_REGION(th, ret = rb_w32_wait_events_blocking(events, num, timeout),
ubf_handle, ruby_thread_from_native(), FALSE);
return ret;
}
@@ -285,28 +269,34 @@ int WINAPI
rb_w32_Sleep(unsigned long msec)
{
int ret;
+ rb_thread_t *th = GET_THREAD();
- BLOCKING_REGION(ret = rb_w32_sleep(msec),
+ BLOCKING_REGION(th, ret = rb_w32_sleep(msec),
ubf_handle, ruby_thread_from_native(), FALSE);
return ret;
}
+static DWORD
+hrtime2msec(rb_hrtime_t hrt)
+{
+ return (DWORD)hrt / (DWORD)RB_HRTIME_PER_MSEC;
+}
+
static void
-native_sleep(rb_thread_t *th, struct timeval *tv)
+native_sleep(rb_thread_t *th, rb_hrtime_t *rel)
{
- const volatile DWORD msec = (tv) ?
- (DWORD)(tv->tv_sec * 1000 + tv->tv_usec / 1000) : INFINITE;
+ const volatile DWORD msec = rel ? hrtime2msec(*rel) : INFINITE;
- GVL_UNLOCK_BEGIN();
+ GVL_UNLOCK_BEGIN(th);
{
DWORD ret;
- native_mutex_lock(&th->interrupt_lock);
+ rb_native_mutex_lock(&th->interrupt_lock);
th->unblock.func = ubf_handle;
th->unblock.arg = th;
- native_mutex_unlock(&th->interrupt_lock);
+ rb_native_mutex_unlock(&th->interrupt_lock);
- if (RUBY_VM_INTERRUPTED(th)) {
+ if (RUBY_VM_INTERRUPTED(th->ec)) {
/* interrupted. return immediate */
}
else {
@@ -315,34 +305,32 @@ native_sleep(rb_thread_t *th, struct timeval *tv)
thread_debug("native_sleep done (%lu)\n", ret);
}
- native_mutex_lock(&th->interrupt_lock);
+ rb_native_mutex_lock(&th->interrupt_lock);
th->unblock.func = 0;
th->unblock.arg = 0;
- native_mutex_unlock(&th->interrupt_lock);
+ rb_native_mutex_unlock(&th->interrupt_lock);
}
- GVL_UNLOCK_END();
+ GVL_UNLOCK_END(th);
}
-static int
-native_mutex_lock(rb_nativethread_lock_t *lock)
+void
+rb_native_mutex_lock(rb_nativethread_lock_t *lock)
{
#if USE_WIN32_MUTEX
w32_mutex_lock(lock->mutex);
#else
EnterCriticalSection(&lock->crit);
#endif
- return 0;
}
-static int
-native_mutex_unlock(rb_nativethread_lock_t *lock)
+void
+rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
{
#if USE_WIN32_MUTEX
thread_debug("release mutex: %p\n", lock->mutex);
- return ReleaseMutex(lock->mutex);
+ ReleaseMutex(lock->mutex);
#else
LeaveCriticalSection(&lock->crit);
- return 0;
#endif
}
@@ -366,8 +354,8 @@ native_mutex_trylock(rb_nativethread_lock_t *lock)
#endif
}
-static void
-native_mutex_initialize(rb_nativethread_lock_t *lock)
+void
+rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
{
#if USE_WIN32_MUTEX
lock->mutex = w32_mutex_create();
@@ -377,8 +365,8 @@ native_mutex_initialize(rb_nativethread_lock_t *lock)
#endif
}
-static void
-native_mutex_destroy(rb_nativethread_lock_t *lock)
+void
+rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
{
#if USE_WIN32_MUTEX
w32_close_handle(lock->mutex);
@@ -393,8 +381,8 @@ struct cond_event_entry {
HANDLE event;
};
-static void
-native_cond_signal(rb_nativethread_cond_t *cond)
+void
+rb_native_cond_signal(rb_nativethread_cond_t *cond)
{
/* cond is guarded by mutex */
struct cond_event_entry *e = cond->next;
@@ -412,8 +400,8 @@ native_cond_signal(rb_nativethread_cond_t *cond)
}
}
-static void
-native_cond_broadcast(rb_nativethread_cond_t *cond)
+void
+rb_native_cond_broadcast(rb_nativethread_cond_t *cond)
{
/* cond is guarded by mutex */
struct cond_event_entry *e = cond->next;
@@ -433,7 +421,6 @@ native_cond_broadcast(rb_nativethread_cond_t *cond)
}
}
-
static int
native_cond_timedwait_ms(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex, unsigned long msec)
{
@@ -449,14 +436,14 @@ native_cond_timedwait_ms(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *m
head->prev->next = &entry;
head->prev = &entry;
- native_mutex_unlock(mutex);
+ rb_native_mutex_unlock(mutex);
{
r = WaitForSingleObject(entry.event, msec);
if ((r != WAIT_OBJECT_0) && (r != WAIT_TIMEOUT)) {
- rb_bug("native_cond_wait: WaitForSingleObject returns %lu", r);
+ rb_bug("rb_native_cond_wait: WaitForSingleObject returns %lu", r);
}
}
- native_mutex_lock(mutex);
+ rb_native_mutex_lock(mutex);
entry.prev->next = entry.next;
entry.next->prev = entry.prev;
@@ -465,12 +452,13 @@ native_cond_timedwait_ms(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *m
return (r == WAIT_OBJECT_0) ? 0 : ETIMEDOUT;
}
-static int
-native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex)
+void
+rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex)
{
- return native_cond_timedwait_ms(cond, mutex, INFINITE);
+ native_cond_timedwait_ms(cond, mutex, INFINITE);
}
+#if 0
static unsigned long
abs_timespec_to_timeout_ms(const struct timespec *ts)
{
@@ -528,16 +516,17 @@ native_cond_timeout(rb_nativethread_cond_t *cond, struct timespec timeout_rel)
return timeout;
}
+#endif
-static void
-native_cond_initialize(rb_nativethread_cond_t *cond, int flags)
+void
+rb_native_cond_initialize(rb_nativethread_cond_t *cond)
{
cond->next = (struct cond_event_entry *)cond;
cond->prev = (struct cond_event_entry *)cond;
}
-static void
-native_cond_destroy(rb_nativethread_cond_t *cond)
+void
+rb_native_cond_destroy(rb_nativethread_cond_t *cond)
{
/* */
}
@@ -564,8 +553,8 @@ native_thread_init_stack(rb_thread_t *th)
size = end - base;
space = size / 5;
if (space > 1024*1024) space = 1024*1024;
- th->machine.stack_start = (VALUE *)end - 1;
- th->machine.stack_maxsize = size - space;
+ th->ec->machine.stack_start = (VALUE *)end - 1;
+ th->ec->machine.stack_maxsize = size - space;
}
#ifndef InterlockedExchangePointer
@@ -593,7 +582,7 @@ thread_start_func_1(void *th_ptr)
thread_debug("thread created (th: %p, thid: %p, event: %p)\n", th,
th->thread_id, th->native_thread_data.interrupt_event);
- thread_start_func_2(th, th->machine.stack_start, rb_ia64_bsp());
+ thread_start_func_2(th, th->ec->machine.stack_start, rb_ia64_bsp());
w32_close_handle(thread_id);
thread_debug("thread deleted (th: %p)\n", th);
@@ -682,9 +671,15 @@ ubf_handle(void *ptr)
rb_thread_t *th = (rb_thread_t *)ptr;
thread_debug("ubf_handle: %p\n", th);
- w32_set_event(th->native_thread_data.interrupt_event);
+ if (!SetEvent(th->native_thread_data.interrupt_event)) {
+ w32_error("ubf_handle");
+ }
}
+int rb_w32_set_thread_description(HANDLE th, const WCHAR *name);
+int rb_w32_set_thread_description_str(HANDLE th, VALUE name);
+#define native_set_another_thread_name rb_w32_set_thread_description_str
+
static struct {
HANDLE id;
HANDLE lock;
@@ -694,21 +689,31 @@ static struct {
static unsigned long __stdcall
timer_thread_func(void *dummy)
{
+ rb_vm_t *vm = GET_VM();
thread_debug("timer_thread\n");
+ rb_w32_set_thread_description(GetCurrentThread(), L"ruby-timer-thread");
while (WaitForSingleObject(timer_thread.lock, TIME_QUANTUM_USEC/1000) ==
WAIT_TIMEOUT) {
- timer_thread_function(dummy);
+ timer_thread_function();
+ ruby_sigchld_handler(vm); /* probably no-op */
+ rb_threadptr_check_signal(vm->main_thread);
}
thread_debug("timer killed\n");
return 0;
}
void
-rb_thread_wakeup_timer_thread(void)
+rb_thread_wakeup_timer_thread(int sig)
{
/* do nothing */
}
+static VALUE
+rb_thread_start_unblock_thread(void)
+{
+ return Qfalse; /* no-op */
+}
+
static void
rb_thread_create_timer_thread(void)
{
@@ -747,7 +752,7 @@ native_reset_timer_thread(void)
int
ruby_stack_overflowed_p(const rb_thread_t *th, const void *addr)
{
- return rb_thread_raised_p(th, RAISED_STACKOVERFLOW);
+ return rb_ec_raised_p(th->ec, RAISED_STACKOVERFLOW);
}
#if defined(__MINGW32__)
@@ -755,7 +760,7 @@ LONG WINAPI
rb_w32_stack_overflow_handler(struct _EXCEPTION_POINTERS *exception)
{
if (exception->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
- rb_thread_raised_set(GET_THREAD(), RAISED_STACKOVERFLOW);
+ rb_ec_raised_set(GET_EC(), RAISED_STACKOVERFLOW);
raise(SIGSEGV);
}
return EXCEPTION_CONTINUE_SEARCH;
@@ -767,9 +772,9 @@ void
ruby_alloca_chkstk(size_t len, void *sp)
{
if (ruby_stack_length(NULL) * sizeof(VALUE) >= len) {
- rb_thread_t *th = GET_THREAD();
- if (!rb_thread_raised_p(th, RAISED_STACKOVERFLOW)) {
- rb_thread_raised_set(th, RAISED_STACKOVERFLOW);
+ rb_execution_context_t *ec = GET_EC();
+ if (!rb_ec_raised_p(ec, RAISED_STACKOVERFLOW)) {
+ rb_ec_raised_set(ec, RAISED_STACKOVERFLOW);
rb_exc_raise(sysstack_error);
}
}
@@ -781,6 +786,26 @@ rb_reserved_fd_p(int fd)
return 0;
}
+int
+rb_sigwait_fd_get(rb_thread_t *th)
+{
+ return -1; /* TODO */
+}
+
+NORETURN(void rb_sigwait_fd_put(rb_thread_t *, int));
+void
+rb_sigwait_fd_put(rb_thread_t *th, int fd)
+{
+ rb_bug("not implemented, should not be called");
+}
+
+NORETURN(void rb_sigwait_sleep(const rb_thread_t *, int, const rb_hrtime_t *));
+void
+rb_sigwait_sleep(const rb_thread_t *th, int fd, const rb_hrtime_t *rel)
+{
+ rb_bug("not implemented, should not be called");
+}
+
rb_nativethread_id_t
rb_nativethread_self(void)
{
@@ -792,4 +817,29 @@ native_set_thread_name(rb_thread_t *th)
{
}
+#if USE_MJIT
+static unsigned long __stdcall
+mjit_worker(void *arg)
+{
+ void (*worker_func)(void) = arg;
+ rb_w32_set_thread_description(GetCurrentThread(), L"ruby-mjitworker");
+ worker_func();
+ return 0;
+}
+
+/* Launch MJIT thread. Returns FALSE if it fails to create thread. */
+int
+rb_thread_create_mjit_thread(void (*worker_func)(void))
+{
+ size_t stack_size = 4 * 1024; /* 4KB is the minimum commit size */
+ HANDLE thread_id = w32_create_thread(stack_size, mjit_worker, worker_func);
+ if (thread_id == 0) {
+ return FALSE;
+ }
+
+ w32_resume_thread(thread_id);
+ return TRUE;
+}
+#endif
+
#endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */
diff --git a/time.c b/time.c
index 708d9a7d9f..228100a9d1 100644
--- a/time.c
+++ b/time.c
@@ -11,6 +11,7 @@
#define _DEFAULT_SOURCE
#define _BSD_SOURCE
+#include "ruby/encoding.h"
#include "internal.h"
#include <sys/types.h>
#include <time.h>
@@ -32,9 +33,17 @@
#endif
#include "timev.h"
+#include "id.h"
-static ID id_divmod, id_mul, id_submicro, id_nano_num, id_nano_den, id_offset, id_zone;
-static ID id_eq, id_ne, id_quo, id_div, id_cmp;
+static ID id_divmod, id_submicro, id_nano_num, id_nano_den, id_offset, id_zone;
+static ID id_quo, id_div;
+static ID id_nanosecond, id_microsecond, id_millisecond, id_nsec, id_usec;
+static ID id_local_to_utc, id_utc_to_local, id_find_timezone;
+static ID id_year, id_mon, id_mday, id_hour, id_min, id_sec, id_isdst, id_name;
+
+#ifndef TM_IS_TIME
+#define TM_IS_TIME 1
+#endif
#define NDIV(x,y) (-(-((x)+1)/(y))-1)
#define NMOD(x,y) ((y)-(-((x)+1)%(y))-1)
@@ -42,7 +51,6 @@ static ID id_eq, id_ne, id_quo, id_div, id_cmp;
#define MOD(n,d) ((n)<0 ? NMOD((n),(d)) : (n)%(d))
#define VTM_WDAY_INITVAL (7)
#define VTM_ISDST_INITVAL (3)
-#define TO_GMT_INITVAL (3)
static int
eq(VALUE x, VALUE y)
@@ -50,7 +58,7 @@ eq(VALUE x, VALUE y)
if (FIXNUM_P(x) && FIXNUM_P(y)) {
return x == y;
}
- return RTEST(rb_funcall(x, id_eq, 1, y));
+ return RTEST(rb_funcall(x, idEq, 1, y));
}
static int
@@ -63,7 +71,8 @@ cmp(VALUE x, VALUE y)
return 1;
return 0;
}
- return rb_cmpint(rb_funcall(x, id_cmp, 1, y), x, y);
+ if (RB_TYPE_P(x, T_BIGNUM)) return FIX2INT(rb_big_cmp(x, y));
+ return rb_cmpint(rb_funcall(x, idCmp, 1, y), x, y);
}
#define ne(x,y) (!eq((x),(y)))
@@ -73,7 +82,7 @@ cmp(VALUE x, VALUE y)
#define ge(x,y) (cmp((x),(y)) >= 0)
static VALUE
-add(VALUE x, VALUE y)
+addv(VALUE x, VALUE y)
{
if (FIXNUM_P(x) && FIXNUM_P(y)) {
return LONG2NUM(FIX2LONG(x) + FIX2LONG(y));
@@ -83,7 +92,7 @@ add(VALUE x, VALUE y)
}
static VALUE
-sub(VALUE x, VALUE y)
+subv(VALUE x, VALUE y)
{
if (FIXNUM_P(x) && FIXNUM_P(y)) {
return LONG2NUM(FIX2LONG(x) - FIX2LONG(y));
@@ -93,7 +102,7 @@ sub(VALUE x, VALUE y)
}
static VALUE
-mul(VALUE x, VALUE y)
+mulv(VALUE x, VALUE y)
{
if (FIXNUM_P(x) && FIXNUM_P(y)) {
return rb_fix_mul_fix(x, y);
@@ -103,10 +112,19 @@ mul(VALUE x, VALUE y)
return rb_funcall(x, '*', 1, y);
}
-#define div(x,y) (rb_funcall((x), id_div, 1, (y)))
+static VALUE
+divv(VALUE x, VALUE y)
+{
+ if (FIXNUM_P(x) && FIXNUM_P(y)) {
+ return rb_fix_div_fix(x, y);
+ }
+ if (RB_TYPE_P(x, T_BIGNUM))
+ return rb_big_div(x, y);
+ return rb_funcall(x, id_div, 1, y);
+}
static VALUE
-mod(VALUE x, VALUE y)
+modv(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
if (FIX2LONG(y) == 0) rb_num_zerodiv();
@@ -116,10 +134,10 @@ mod(VALUE x, VALUE y)
return rb_funcall(x, '%', 1, y);
}
-#define neg(x) (sub(INT2FIX(0), (x)))
+#define neg(x) (subv(INT2FIX(0), (x)))
static VALUE
-quo(VALUE x, VALUE y)
+quov(VALUE x, VALUE y)
{
VALUE ret;
if (FIXNUM_P(x) && FIXNUM_P(y)) {
@@ -133,7 +151,7 @@ quo(VALUE x, VALUE y)
return LONG2FIX(c);
}
}
- ret = rb_funcall(x, id_quo, 1, y);
+ ret = rb_numeric_quo(x, y);
if (RB_TYPE_P(ret, T_RATIONAL) &&
RRATIONAL(ret)->den == INT2FIX(1)) {
ret = RRATIONAL(ret)->num;
@@ -141,7 +159,7 @@ quo(VALUE x, VALUE y)
return ret;
}
-#define mulquo(x,y,z) (((y) == (z)) ? (x) : quo(mul((x),(y)),(z)))
+#define mulquov(x,y,z) (((y) == (z)) ? (x) : quov(mulv((x),(y)),(z)))
static void
divmodv(VALUE n, VALUE d, VALUE *q, VALUE *r)
@@ -298,7 +316,7 @@ weq(wideval_t wx, wideval_t wy)
if (FIXWV_P(wx) && FIXWV_P(wy)) {
return WIDEVAL_GET(wx) == WIDEVAL_GET(wy);
}
- return RTEST(rb_funcall(w2v(wx), id_eq, 1, w2v(wy)));
+ return RTEST(rb_funcall(w2v(wx), idEq, 1, w2v(wy)));
#else
return eq(WIDEVAL_GET(wx), WIDEVAL_GET(wy));
#endif
@@ -322,7 +340,7 @@ wcmp(wideval_t wx, wideval_t wy)
#endif
x = w2v(wx);
y = w2v(wy);
- return rb_cmpint(rb_funcall(x, id_cmp, 1, y), x, y);
+ return cmp(x, y);
}
#define wne(x,y) (!weq((x),(y)))
@@ -334,33 +352,25 @@ wcmp(wideval_t wx, wideval_t wy)
static wideval_t
wadd(wideval_t wx, wideval_t wy)
{
- VALUE x;
#if WIDEVALUE_IS_WIDER
if (FIXWV_P(wx) && FIXWV_P(wy)) {
wideint_t r = FIXWV2WINT(wx) + FIXWV2WINT(wy);
return WINT2WV(r);
}
- else
#endif
- x = w2v(wx);
- if (RB_TYPE_P(x, T_BIGNUM)) return v2w(rb_big_plus(x, w2v(wy)));
- return v2w(rb_funcall(x, '+', 1, w2v(wy)));
+ return v2w(addv(w2v(wx), w2v(wy)));
}
static wideval_t
wsub(wideval_t wx, wideval_t wy)
{
- VALUE x;
#if WIDEVALUE_IS_WIDER
if (FIXWV_P(wx) && FIXWV_P(wy)) {
wideint_t r = FIXWV2WINT(wx) - FIXWV2WINT(wy);
return WINT2WV(r);
}
- else
#endif
- x = w2v(wx);
- if (RB_TYPE_P(x, T_BIGNUM)) return v2w(rb_big_minus(x, w2v(wy)));
- return v2w(rb_funcall(x, '-', 1, w2v(wy)));
+ return v2w(subv(w2v(wx), w2v(wy)));
}
static wideval_t
@@ -372,7 +382,7 @@ wmul(wideval_t wx, wideval_t wy)
return WINT2WV(FIXWV2WINT(wx) * FIXWV2WINT(wy));
}
#endif
- return v2w(mul(w2v(wx), w2v(wy)));
+ return v2w(mulv(w2v(wx), w2v(wy)));
}
static wideval_t
@@ -390,7 +400,7 @@ wquo(wideval_t wx, wideval_t wy)
}
}
#endif
- return v2w(quo(w2v(wx), w2v(wy)));
+ return v2w(quov(w2v(wx), w2v(wy)));
}
#define wmulquo(x,y,z) ((WIDEVAL_GET(y) == WIDEVAL_GET(z)) ? (x) : wquo(wmul((x),(y)),(z)))
@@ -465,7 +475,7 @@ wdiv(wideval_t wx, wideval_t wy)
wideval_t q, dmy;
if (wdivmod0(wx, wy, &q, &dmy)) return q;
#endif
- return v2w(div(w2v(wx), w2v(wy)));
+ return v2w(divv(w2v(wx), w2v(wy)));
}
static wideval_t
@@ -475,58 +485,51 @@ wmod(wideval_t wx, wideval_t wy)
wideval_t r, dmy;
if (wdivmod0(wx, wy, &dmy, &r)) return r;
#endif
- return v2w(mod(w2v(wx), w2v(wy)));
+ return v2w(modv(w2v(wx), w2v(wy)));
}
static VALUE
num_exact(VALUE v)
{
VALUE tmp;
- int t;
- t = TYPE(v);
- switch (t) {
- case T_FIXNUM:
- case T_BIGNUM:
+ if (NIL_P(v)) {
+ rb_raise(rb_eTypeError, "can't convert nil into an exact number");
+ }
+ else if (RB_INTEGER_TYPE_P(v)) {
return v;
-
- case T_RATIONAL:
- break;
-
- case T_STRING:
- case T_NIL:
+ }
+ else if (RB_TYPE_P(v, T_RATIONAL)) {
+ goto rational;
+ }
+ else if (RB_TYPE_P(v, T_STRING)) {
goto typeerror;
-
- default:
- if ((tmp = rb_check_funcall(v, rb_intern("to_r"), 0, NULL)) != Qundef) {
+ }
+ else {
+ if ((tmp = rb_check_funcall(v, idTo_r, 0, NULL)) != Qundef) {
/* test to_int method availability to reject non-Numeric
* objects such as String, Time, etc which have to_r method. */
- if (!rb_respond_to(v, rb_intern("to_int"))) goto typeerror;
- v = tmp;
- break;
+ if (!rb_respond_to(v, idTo_int)) goto typeerror;
}
- if (!NIL_P(tmp = rb_check_to_integer(v, "to_int"))) {
- v = tmp;
- break;
+ else if (!NIL_P(tmp = rb_check_to_int(v))) {
+ return tmp;
+ }
+ else {
+ goto typeerror;
}
- goto typeerror;
}
- t = TYPE(v);
- switch (t) {
- case T_FIXNUM:
- case T_BIGNUM:
- return v;
-
- case T_RATIONAL:
+ if (RB_INTEGER_TYPE_P(tmp)) {
+ v = tmp;
+ }
+ else if (RB_TYPE_P(tmp, T_RATIONAL)) {
+ v = tmp;
+ rational:
if (RRATIONAL(v)->den == INT2FIX(1))
v = RRATIONAL(v)->num;
- break;
-
- default:
+ }
+ else {
typeerror:
- if (NIL_P(v))
- rb_raise(rb_eTypeError, "can't convert nil into an exact number");
rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into an exact number",
rb_obj_class(v));
}
@@ -561,14 +564,14 @@ rb_time_unmagnify_to_float(wideval_t w)
return DBL2NUM((double)c);
}
v = DBL2NUM((double)FIXWV2WINT(w));
- return quo(v, DBL2NUM(TIME_SCALE));
+ return quov(v, DBL2NUM(TIME_SCALE));
}
#endif
v = w2v(w);
if (RB_TYPE_P(v, T_RATIONAL))
- return rb_Float(quo(v, INT2FIX(TIME_SCALE)));
+ return rb_Float(quov(v, INT2FIX(TIME_SCALE)));
else
- return quo(v, DBL2NUM(TIME_SCALE));
+ return quov(v, DBL2NUM(TIME_SCALE));
}
static void
@@ -625,7 +628,7 @@ wv2timet(wideval_t w)
#define WV2TIMET(t) wv2timet(t)
VALUE rb_cTime;
-static VALUE time_utc_offset _((VALUE));
+static VALUE rb_cTimeTM;
static int obj2int(VALUE obj);
static uint32_t obj2ubits(VALUE obj, size_t bits);
@@ -639,6 +642,7 @@ static uint32_t obj2subsecx(VALUE obj, VALUE *subsecx);
static VALUE time_gmtime(VALUE);
static VALUE time_localtime(VALUE);
static VALUE time_fixoff(VALUE);
+static VALUE time_zonelocal(VALUE time, VALUE off);
static time_t timegm_noleapsecond(struct tm *tm);
static int tmcmp(struct tm *a, struct tm *b);
@@ -648,7 +652,11 @@ static const char *find_time_t(struct tm *tptr, int utc_p, time_t *tp);
static struct vtm *localtimew(wideval_t timew, struct vtm *result);
static int leap_year_p(long y);
-#define leap_year_v_p(y) leap_year_p(NUM2LONG(mod((y), INT2FIX(400))))
+#define leap_year_v_p(y) leap_year_p(NUM2LONG(modv((y), INT2FIX(400))))
+
+static VALUE tm_from_time(VALUE klass, VALUE time);
+
+bool ruby_tz_uptodate_p;
static struct tm *
rb_localtime_r(const time_t *t, struct tm *result)
@@ -656,6 +664,10 @@ rb_localtime_r(const time_t *t, struct tm *result)
#if defined __APPLE__ && defined __LP64__
if (*t != (time_t)(int)*t) return NULL;
#endif
+ if (!ruby_tz_uptodate_p) {
+ ruby_tz_uptodate_p = 1;
+ tzset();
+ }
#ifdef HAVE_GMTIME_R
result = localtime_r(t, result);
#else
@@ -681,7 +693,7 @@ rb_localtime_r(const time_t *t, struct tm *result)
#endif
return result;
}
-#define LOCALTIME(tm, result) (tzset(),rb_localtime_r((tm), &(result)))
+#define LOCALTIME(tm, result) rb_localtime_r((tm), &(result))
#ifndef HAVE_STRUCT_TM_TM_GMTOFF
static struct tm *
@@ -741,6 +753,67 @@ static const int leap_year_days_in_month[] = {
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
+#define M28(m) \
+ (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
+ (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
+ (m),(m),(m),(m),(m),(m),(m),(m)
+#define M29(m) \
+ (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
+ (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
+ (m),(m),(m),(m),(m),(m),(m),(m),(m)
+#define M30(m) \
+ (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
+ (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
+ (m),(m),(m),(m),(m),(m),(m),(m),(m),(m)
+#define M31(m) \
+ (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
+ (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
+ (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), (m)
+
+static const uint8_t common_year_mon_of_yday[] = {
+ M31(1), M28(2), M31(3), M30(4), M31(5), M30(6),
+ M31(7), M31(8), M30(9), M31(10), M30(11), M31(12)
+};
+static const uint8_t leap_year_mon_of_yday[] = {
+ M31(1), M29(2), M31(3), M30(4), M31(5), M30(6),
+ M31(7), M31(8), M30(9), M31(10), M30(11), M31(12)
+};
+
+#undef M28
+#undef M29
+#undef M30
+#undef M31
+
+#define D28 \
+ 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
+#define D29 \
+ 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
+#define D30 \
+ 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
+#define D31 \
+ 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
+
+static const uint8_t common_year_mday_of_yday[] = {
+ /* 1 2 3 4 5 6 7 8 9 10 11 12 */
+ D31, D28, D31, D30, D31, D30, D31, D31, D30, D31, D30, D31
+};
+static const uint8_t leap_year_mday_of_yday[] = {
+ D31, D29, D31, D30, D31, D30, D31, D31, D30, D31, D30, D31
+};
+
+#undef D28
+#undef D29
+#undef D30
+#undef D31
+
static int
calc_tm_yday(long tm_year, int tm_mon, int tm_mday)
{
@@ -766,7 +839,7 @@ timegmw_noleapsecond(struct vtm *vtm)
VALUE vdays, ret;
wideval_t wret;
- year1900 = sub(vtm->year, INT2FIX(1900));
+ year1900 = subv(vtm->year, INT2FIX(1900));
divmodv(year1900, INT2FIX(400), &q400, &r400);
year_mod400 = NUM2INT(r400);
@@ -788,60 +861,61 @@ timegmw_noleapsecond(struct vtm *vtm)
- DIV(year_mod400 - 1, 100)
+ (year_mod400 + 299) / 400;
vdays = LONG2NUM(days_in400);
- vdays = add(vdays, mul(q400, INT2FIX(97)));
- vdays = add(vdays, mul(year1900, INT2FIX(365)));
+ vdays = addv(vdays, mulv(q400, INT2FIX(97)));
+ vdays = addv(vdays, mulv(year1900, INT2FIX(365)));
wret = wadd(rb_time_magnify(v2w(ret)), wmul(rb_time_magnify(v2w(vdays)), WINT2FIXWV(86400)));
wret = wadd(wret, v2w(vtm->subsecx));
return wret;
}
-static st_table *zone_table;
-
-static int
-zone_str_update(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
+static VALUE
+zone_str(const char *zone)
{
- const char *s = (const char *)*key;
- const char **ret = (const char **)arg;
+ const char *p;
+ int ascii_only = 1;
+ VALUE str;
+ size_t len;
- if (existing) {
- *ret = (const char *)*value;
- return ST_STOP;
+ if (zone == NULL) {
+ return rb_fstring_lit("(NO-TIMEZONE-ABBREVIATION)");
}
- *ret = s = strdup(s);
- *key = *value = (st_data_t)s;
- return ST_CONTINUE;
-}
-
-static const char *
-zone_str(const char *s)
-{
- if (!zone_table)
- zone_table = st_init_strtable();
- st_update(zone_table, (st_data_t)s, zone_str_update, (st_data_t)&s);
- return s;
+ for (p = zone; *p; p++)
+ if (!ISASCII(*p)) {
+ ascii_only = 0;
+ break;
+ }
+ len = p - zone + strlen(p);
+ if (ascii_only) {
+ str = rb_usascii_str_new(zone, len);
+ }
+ else {
+ str = rb_enc_str_new(zone, len, rb_locale_encoding());
+ }
+ return rb_fstring(str);
}
static void
gmtimew_noleapsecond(wideval_t timew, struct vtm *vtm)
{
VALUE v;
- int i, n, x, y;
- const int *yday_offset;
+ int n, x, y;
int wday;
VALUE timev;
wideval_t timew2, w, w2;
+ VALUE subsecx;
vtm->isdst = 0;
- split_second(timew, &timew2, &vtm->subsecx);
+ split_second(timew, &timew2, &subsecx);
+ vtm->subsecx = subsecx;
wdivmod(timew2, WINT2FIXWV(86400), &w2, &w);
timev = w2v(w2);
v = w2v(w);
- wday = NUM2INT(mod(timev, INT2FIX(7)));
+ wday = NUM2INT(modv(timev, INT2FIX(7)));
vtm->wday = (wday + 4) % 7;
n = NUM2INT(v);
@@ -851,7 +925,7 @@ gmtimew_noleapsecond(wideval_t timew, struct vtm *vtm)
/* 97 leap days in the 400 year cycle */
divmodv(timev, INT2FIX(400*365 + 97), &timev, &v);
- vtm->year = mul(timev, INT2FIX(400));
+ vtm->year = mulv(timev, INT2FIX(400));
/* n is the days in the 400 year cycle.
* the start of the cycle is 1970-01-01. */
@@ -909,24 +983,19 @@ gmtimew_noleapsecond(wideval_t timew, struct vtm *vtm)
found:
vtm->yday = n+1;
- vtm->year = add(vtm->year, INT2NUM(y));
-
- if (leap_year_p(y))
- yday_offset = leap_year_yday_offset;
- else
- yday_offset = common_year_yday_offset;
+ vtm->year = addv(vtm->year, INT2NUM(y));
- for (i = 0; i < 12; i++) {
- if (yday_offset[i] < n) {
- vtm->mon = i+1;
- vtm->mday = n - yday_offset[i];
- }
- else
- break;
+ if (leap_year_p(y)) {
+ vtm->mon = leap_year_mon_of_yday[n];
+ vtm->mday = leap_year_mday_of_yday[n];
+ }
+ else {
+ vtm->mon = common_year_mon_of_yday[n];
+ vtm->mday = common_year_mday_of_yday[n];
}
vtm->utc_offset = INT2FIX(0);
- vtm->zone = "UTC";
+ vtm->zone = rb_fstring_lit("UTC");
}
static struct tm *
@@ -1099,6 +1168,13 @@ init_leap_second_info(void)
}
}
+/* Use this if you want to re-run init_leap_second_info() */
+void
+ruby_reset_leap_second_info(void)
+{
+ this_year = 0;
+}
+
static wideval_t
timegmw(struct vtm *vtm)
{
@@ -1116,7 +1192,14 @@ timegmw(struct vtm *vtm)
timew = timegmw_noleapsecond(vtm);
- if (wlt(rb_time_magnify(TIMET2WV(known_leap_seconds_limit)), timew)) {
+
+ if (number_of_leap_seconds_known == 0) {
+ /* When init_leap_second_info() is executed, the timezone doesn't have
+ * leap second information. Disable leap second for calculating gmtime.
+ */
+ return timew;
+ }
+ else if (wlt(rb_time_magnify(TIMET2WV(known_leap_seconds_limit)), timew)) {
return wadd(timew, rb_time_magnify(WINT2WV(number_of_leap_seconds_known)));
}
@@ -1149,7 +1232,14 @@ gmtimew(wideval_t timew, struct vtm *result)
init_leap_second_info();
- if (wlt(rb_time_magnify(TIMET2WV(known_leap_seconds_limit)), timew)) {
+ if (number_of_leap_seconds_known == 0) {
+ /* When init_leap_second_info() is executed, the timezone doesn't have
+ * leap second information. Disable leap second for calculating gmtime.
+ */
+ gmtimew_noleapsecond(timew, result);
+ return result;
+ }
+ else if (wlt(rb_time_magnify(TIMET2WV(known_leap_seconds_limit)), timew)) {
timew = wsub(timew, rb_time_magnify(WINT2WV(number_of_leap_seconds_known)));
gmtimew_noleapsecond(timew, result);
return result;
@@ -1172,12 +1262,17 @@ gmtimew(wideval_t timew, struct vtm *result)
result->wday = tm.tm_wday;
result->yday = tm.tm_yday+1;
result->isdst = tm.tm_isdst;
- result->zone = "UTC";
+#if 0
+ result->zone = rb_fstring_lit("UTC");
+#endif
return result;
}
-static struct tm *localtime_with_gmtoff_zone(const time_t *t, struct tm *result, long *gmtoff, const char **zone);
+#define GMTIMEW(w, v) \
+ (gmtimew(w, v) ? (void)0 : rb_raise(rb_eArgError, "gmtime error"))
+
+static struct tm *localtime_with_gmtoff_zone(const time_t *t, struct tm *result, long *gmtoff, VALUE *zone);
/*
* The idea is borrowed from Perl:
@@ -1212,7 +1307,7 @@ static struct tm *localtime_with_gmtoff_zone(const time_t *t, struct tm *result,
* }
*
*/
-static int compat_common_month_table[12][7] = {
+static const int compat_common_month_table[12][7] = {
/* Sun Mon Tue Wed Thu Fri Sat */
{ 2034, 2035, 2036, 2031, 2032, 2027, 2033 }, /* January */
{ 2026, 2027, 2033, 2034, 2035, 2030, 2031 }, /* February */
@@ -1253,19 +1348,19 @@ static int compat_common_month_table[12][7] = {
* puts
* }
*/
-static int compat_leap_month_table[7] = {
+static const int compat_leap_month_table[7] = {
/* Sun Mon Tue Wed Thu Fri Sat */
2032, 2016, 2028, 2012, 2024, 2036, 2020, /* February */
};
static int
-calc_wday(int year, int month, int day)
+calc_wday(int year_mod400, int month, int day)
{
int a, y, m;
int wday;
a = (14 - month) / 12;
- y = year + 4800 - a;
+ y = year_mod400 + 4800 - a;
m = month + 12 * a - 3;
wday = day + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 + 2;
wday = wday % 7;
@@ -1273,22 +1368,22 @@ calc_wday(int year, int month, int day)
}
static VALUE
-guess_local_offset(struct vtm *vtm_utc, int *isdst_ret, const char **zone_ret)
+guess_local_offset(struct vtm *vtm_utc, int *isdst_ret, VALUE *zone_ret)
{
struct tm tm;
long gmtoff;
- const char *zone;
+ VALUE zone;
time_t t;
struct vtm vtm2;
VALUE timev;
- int y, wday;
+ int year_mod400, wday;
/* Daylight Saving Time was introduced in 1916.
* So we don't need to care about DST before that. */
if (lt(vtm_utc->year, INT2FIX(1916))) {
VALUE off = INT2FIX(0);
int isdst = 0;
- zone = "UTC";
+ zone = rb_fstring_lit("UTC");
# if defined(NEGATIVE_TIME_T)
# if SIZEOF_TIME_T <= 4
@@ -1323,16 +1418,16 @@ guess_local_offset(struct vtm *vtm_utc, int *isdst_ret, const char **zone_ret)
vtm2 = *vtm_utc;
/* guess using a year before 2038. */
- y = NUM2INT(mod(vtm_utc->year, INT2FIX(400)));
- wday = calc_wday(y, vtm_utc->mon, 1);
- if (vtm_utc->mon == 2 && leap_year_p(y))
+ year_mod400 = NUM2INT(modv(vtm_utc->year, INT2FIX(400)));
+ wday = calc_wday(year_mod400, vtm_utc->mon, 1);
+ if (vtm_utc->mon == 2 && leap_year_p(year_mod400))
vtm2.year = INT2FIX(compat_leap_month_table[wday]);
else
vtm2.year = INT2FIX(compat_common_month_table[vtm_utc->mon-1][wday]);
timev = w2v(rb_time_unmagnify(timegmw(&vtm2)));
t = NUM2TIMET(timev);
- zone = "UTC";
+ zone = rb_fstring_lit("UTC");
if (localtime_with_gmtoff_zone(&t, &tm, &gmtoff, &zone)) {
if (isdst_ret)
*isdst_ret = tm.tm_isdst;
@@ -1345,13 +1440,19 @@ guess_local_offset(struct vtm *vtm_utc, int *isdst_ret, const char **zone_ret)
/* Use the current time offset as a last resort. */
static time_t now = 0;
static long now_gmtoff = 0;
- static const char *now_zone = "UTC";
+ static int now_isdst = 0;
+ static VALUE now_zone;
if (now == 0) {
+ VALUE zone;
now = time(NULL);
- localtime_with_gmtoff_zone(&now, &tm, &now_gmtoff, &now_zone);
+ localtime_with_gmtoff_zone(&now, &tm, &now_gmtoff, &zone);
+ now_isdst = tm.tm_isdst;
+ zone = rb_fstring(zone);
+ rb_gc_register_mark_object(zone);
+ now_zone = zone;
}
if (isdst_ret)
- *isdst_ret = tm.tm_isdst;
+ *isdst_ret = now_isdst;
if (zone_ret)
*zone_ret = now_zone;
return LONG2FIX(now_gmtoff);
@@ -1393,7 +1494,7 @@ timelocalw(struct vtm *vtm)
tm.tm_year = (int)l;
}
else {
- v = sub(vtm->year, INT2FIX(1900));
+ v = subv(vtm->year, INT2FIX(1900));
if (lt(v, INT2NUM(INT_MIN)) || lt(INT2NUM(INT_MAX), v))
goto no_localtime;
tm.tm_year = NUM2INT(v);
@@ -1459,7 +1560,7 @@ timelocalw(struct vtm *vtm)
}
static struct tm *
-localtime_with_gmtoff_zone(const time_t *t, struct tm *result, long *gmtoff, const char **zone)
+localtime_with_gmtoff_zone(const time_t *t, struct tm *result, long *gmtoff, VALUE *zone)
{
struct tm tm;
@@ -1490,10 +1591,7 @@ localtime_with_gmtoff_zone(const time_t *t, struct tm *result, long *gmtoff, con
if (zone) {
#if defined(HAVE_TM_ZONE)
- if (tm.tm_zone)
- *zone = zone_str(tm.tm_zone);
- else
- *zone = zone_str("(NO-TIMEZONE-ABBREVIATION)");
+ *zone = zone_str(tm.tm_zone);
#elif defined(HAVE_TZNAME) && defined(HAVE_DAYLIGHT)
# if RUBY_MSVCRT_VERSION >= 140
# define tzname _tzname
@@ -1543,8 +1641,8 @@ timew_out_of_timet_range(wideval_t timew)
}
#endif
timexv = w2v(timew);
- if (lt(timexv, mul(INT2FIX(TIME_SCALE), TIMET2NUM(TIMET_MIN))) ||
- le(mul(INT2FIX(TIME_SCALE), add(TIMET2NUM(TIMET_MAX), INT2FIX(1))), timexv))
+ if (lt(timexv, mulv(INT2FIX(TIME_SCALE), TIMET2NUM(TIMET_MIN))) ||
+ le(mulv(INT2FIX(TIME_SCALE), addv(TIMET2NUM(TIMET_MAX), INT2FIX(1))), timexv))
return 1;
return 0;
}
@@ -1553,7 +1651,7 @@ static struct vtm *
localtimew(wideval_t timew, struct vtm *result)
{
VALUE subsecx, offset;
- const char *zone;
+ VALUE zone;
int isdst;
if (!timew_out_of_timet_range(timew)) {
@@ -1598,33 +1696,37 @@ localtimew(wideval_t timew, struct vtm *result)
return result;
}
+#define TIME_TZMODE_LOCALTIME 0
+#define TIME_TZMODE_UTC 1
+#define TIME_TZMODE_FIXOFF 2
+#define TIME_TZMODE_UNINITIALIZED 3
+
PACKED_STRUCT_UNALIGNED(struct time_object {
wideval_t timew; /* time_t value * TIME_SCALE. possibly Rational. */
struct vtm vtm;
- uint8_t gmt:3; /* 0:localtime 1:utc 2:fixoff 3:init */
- uint8_t tm_got:1;
+ unsigned int tzmode:3; /* 0:localtime 1:utc 2:fixoff 3:uninitialized */
+ unsigned int tm_got:1;
});
#define GetTimeval(obj, tobj) ((tobj) = get_timeval(obj))
#define GetNewTimeval(obj, tobj) ((tobj) = get_new_timeval(obj))
#define IsTimeval(obj) rb_typeddata_is_kind_of((obj), &time_data_type)
-#define TIME_INIT_P(tobj) ((tobj)->gmt != TO_GMT_INITVAL)
+#define TIME_INIT_P(tobj) ((tobj)->tzmode != TIME_TZMODE_UNINITIALIZED)
-#define TIME_UTC_P(tobj) ((tobj)->gmt == 1)
-#define TIME_SET_UTC(tobj) ((tobj)->gmt = 1)
+#define TZMODE_UTC_P(tobj) ((tobj)->tzmode == TIME_TZMODE_UTC)
+#define TZMODE_SET_UTC(tobj) ((tobj)->tzmode = TIME_TZMODE_UTC)
-#define TIME_LOCALTIME_P(tobj) ((tobj)->gmt == 0)
-#define TIME_SET_LOCALTIME(tobj) ((tobj)->gmt = 0)
+#define TZMODE_LOCALTIME_P(tobj) ((tobj)->tzmode == TIME_TZMODE_LOCALTIME)
+#define TZMODE_SET_LOCALTIME(tobj) ((tobj)->tzmode = TIME_TZMODE_LOCALTIME)
-#define TIME_FIXOFF_P(tobj) ((tobj)->gmt == 2)
-#define TIME_SET_FIXOFF(tobj, off) \
- ((tobj)->gmt = 2, \
- (tobj)->vtm.utc_offset = (off), \
- (tobj)->vtm.zone = NULL)
+#define TZMODE_FIXOFF_P(tobj) ((tobj)->tzmode == TIME_TZMODE_FIXOFF)
+#define TZMODE_SET_FIXOFF(tobj, off) \
+ ((tobj)->tzmode = TIME_TZMODE_FIXOFF, \
+ (tobj)->vtm.utc_offset = (off))
-#define TIME_COPY_GMT(tobj1, tobj2) \
- ((tobj1)->gmt = (tobj2)->gmt, \
+#define TZMODE_COPY(tobj1, tobj2) \
+ ((tobj1)->tzmode = (tobj2)->tzmode, \
(tobj1)->vtm.utc_offset = (tobj2)->vtm.utc_offset, \
(tobj1)->vtm.zone = (tobj2)->vtm.zone)
@@ -1645,6 +1747,7 @@ time_mark(void *ptr)
rb_gc_mark(tobj->vtm.year);
rb_gc_mark(tobj->vtm.subsecx);
rb_gc_mark(tobj->vtm.utc_offset);
+ rb_gc_mark(tobj->vtm.zone);
}
static size_t
@@ -1666,9 +1769,10 @@ time_s_alloc(VALUE klass)
struct time_object *tobj;
obj = TypedData_Make_Struct(klass, struct time_object, &time_data_type, tobj);
- tobj->gmt = TO_GMT_INITVAL;
+ tobj->tzmode = TIME_TZMODE_UNINITIALIZED;
tobj->tm_got=0;
tobj->timew = WINT2FIXWV(0);
+ tobj->vtm.zone = Qnil;
return obj;
}
@@ -1724,7 +1828,7 @@ timew2timespec(wideval_t timew)
rb_raise(rb_eArgError, "time out of system range");
split_second(timew, &timew2, &subsecx);
ts.tv_sec = WV2TIMET(timew2);
- ts.tv_nsec = NUM2LONG(mulquo(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE)));
+ ts.tv_nsec = NUM2LONG(mulquov(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE)));
return ts;
}
@@ -1739,7 +1843,7 @@ timew2timespec_exact(wideval_t timew, struct timespec *ts)
return NULL;
split_second(timew, &timew2, &subsecx);
ts->tv_sec = WV2TIMET(timew2);
- nsecv = mulquo(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE));
+ nsecv = mulquov(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE));
if (!FIXNUM_P(nsecv))
return NULL;
ts->tv_nsec = NUM2LONG(nsecv);
@@ -1773,7 +1877,7 @@ time_init_0(VALUE time)
time_modify(time);
GetNewTimeval(time, tobj);
- tobj->gmt = 0;
+ tobj->tzmode = TIME_TZMODE_LOCALTIME;
tobj->tm_got=0;
tobj->timew = WINT2FIXWV(0);
rb_timespec_now(&ts);
@@ -1792,7 +1896,8 @@ time_set_utc_offset(VALUE time, VALUE off)
GetTimeval(time, tobj);
tobj->tm_got = 0;
- TIME_SET_FIXOFF(tobj, off);
+ tobj->vtm.zone = Qnil;
+ TZMODE_SET_FIXOFF(tobj, off);
return time;
}
@@ -1805,7 +1910,7 @@ vtm_add_offset(struct vtm *vtm, VALUE off)
int sec, min, hour;
int day;
- vtm->utc_offset = sub(vtm->utc_offset, off);
+ vtm->utc_offset = subv(vtm->utc_offset, off);
if (lt(off, INT2FIX(0))) {
sign = -1;
@@ -1832,13 +1937,13 @@ vtm_add_offset(struct vtm *vtm, VALUE off)
day = 0;
if (!rb_equal(subsec, INT2FIX(0))) {
- vtm->subsecx = add(vtm->subsecx, w2v(rb_time_magnify(v2w(subsec))));
+ vtm->subsecx = addv(vtm->subsecx, w2v(rb_time_magnify(v2w(subsec))));
if (lt(vtm->subsecx, INT2FIX(0))) {
- vtm->subsecx = add(vtm->subsecx, INT2FIX(TIME_SCALE));
+ vtm->subsecx = addv(vtm->subsecx, INT2FIX(TIME_SCALE));
sec -= 1;
}
if (le(INT2FIX(TIME_SCALE), vtm->subsecx)) {
- vtm->subsecx = sub(vtm->subsecx, INT2FIX(TIME_SCALE));
+ vtm->subsecx = subv(vtm->subsecx, INT2FIX(TIME_SCALE));
sec += 1;
}
goto not_zero_sec;
@@ -1888,7 +1993,7 @@ vtm_add_offset(struct vtm *vtm, VALUE off)
if (vtm->mon == 1 && vtm->mday == 1) {
vtm->mday = 31;
vtm->mon = 12; /* December */
- vtm->year = sub(vtm->year, INT2FIX(1));
+ vtm->year = subv(vtm->year, INT2FIX(1));
vtm->yday = leap_year_v_p(vtm->year) ? 366 : 365;
}
else if (vtm->mday == 1) {
@@ -1908,7 +2013,7 @@ vtm_add_offset(struct vtm *vtm, VALUE off)
else {
int leap = leap_year_v_p(vtm->year);
if (vtm->mon == 12 && vtm->mday == 31) {
- vtm->year = add(vtm->year, INT2FIX(1));
+ vtm->year = addv(vtm->year, INT2FIX(1));
vtm->mon = 1; /* January */
vtm->mday = 1;
vtm->yday = 1;
@@ -1928,6 +2033,22 @@ vtm_add_offset(struct vtm *vtm, VALUE off)
}
}
+static int
+maybe_tzobj_p(VALUE obj)
+{
+ if (NIL_P(obj)) return FALSE;
+ if (RB_INTEGER_TYPE_P(obj)) return FALSE;
+ if (RB_TYPE_P(obj, T_STRING)) return FALSE;
+ return TRUE;
+}
+
+NORETURN(static void invalid_utc_offset(void));
+static void
+invalid_utc_offset(void)
+{
+ rb_raise(rb_eArgError, "\"+HH:MM\" or \"-HH:MM\" expected for utc_offset");
+}
+
static VALUE
utc_offset_arg(VALUE arg)
{
@@ -1937,7 +2058,7 @@ utc_offset_arg(VALUE arg)
char *s = RSTRING_PTR(tmp);
if (!rb_enc_str_asciicompat_p(tmp)) {
invalid_utc_offset:
- rb_raise(rb_eArgError, "\"+HH:MM\" or \"-HH:MM\" expected for utc_offset");
+ return Qnil;
}
switch (RSTRING_LEN(tmp)) {
case 9:
@@ -1965,16 +2086,158 @@ utc_offset_arg(VALUE arg)
}
}
+static void
+zone_set_offset(VALUE zone, struct time_object *tobj,
+ wideval_t tlocal, wideval_t tutc)
+{
+ /* tlocal and tutc must be unmagnified and in seconds */
+ wideval_t w = wsub(tlocal, tutc);
+ VALUE off = w2v(w);
+ validate_utc_offset(off);
+ tobj->vtm.utc_offset = off;
+ tobj->vtm.zone = zone;
+ tobj->tzmode = TIME_TZMODE_LOCALTIME;
+}
+
+static wideval_t
+extract_time(VALUE time)
+{
+ wideval_t t;
+ const ID id_to_i = idTo_i;
+
+#define EXTRACT_TIME() do { \
+ t = v2w(rb_Integer(AREF(to_i))); \
+ } while (0)
+
+ if (rb_typeddata_is_kind_of(time, &time_data_type)) {
+ struct time_object *tobj = DATA_PTR(time);
+
+ time_gmtime(time); /* ensure tm got */
+ t = rb_time_unmagnify(tobj->timew);
+ }
+ else if (RB_TYPE_P(time, T_STRUCT)) {
+#define AREF(x) rb_struct_aref(time, ID2SYM(id_##x))
+ EXTRACT_TIME();
+#undef AREF
+ }
+ else {
+#define AREF(x) rb_funcallv(time, id_##x, 0, 0)
+ EXTRACT_TIME();
+#undef AREF
+ }
+#undef EXTRACT_TIME
+
+ return t;
+}
+
+static wideval_t
+extract_vtm(VALUE time, struct vtm *vtm, VALUE subsecx)
+{
+ wideval_t t;
+ const ID id_to_i = idTo_i;
+
+#define EXTRACT_VTM() do { \
+ VALUE subsecx; \
+ vtm->year = obj2vint(AREF(year)); \
+ vtm->mon = month_arg(AREF(mon)); \
+ vtm->mday = obj2ubits(AREF(mday), 5); \
+ vtm->hour = obj2ubits(AREF(hour), 5); \
+ vtm->min = obj2ubits(AREF(min), 6); \
+ vtm->sec = obj2subsecx(AREF(sec), &subsecx); \
+ vtm->isdst = RTEST(AREF(isdst)); \
+ vtm->utc_offset = Qnil; \
+ t = v2w(rb_Integer(AREF(to_i))); \
+ } while (0)
+
+ if (rb_typeddata_is_kind_of(time, &time_data_type)) {
+ struct time_object *tobj = DATA_PTR(time);
+
+ time_get_tm(time, tobj);
+ *vtm = tobj->vtm;
+ t = rb_time_unmagnify(tobj->timew);
+ if (TZMODE_FIXOFF_P(tobj) && vtm->utc_offset != INT2FIX(0))
+ t = wadd(t, vtm->utc_offset);
+ }
+ else if (RB_TYPE_P(time, T_STRUCT)) {
+#define AREF(x) rb_struct_aref(time, ID2SYM(id_##x))
+ EXTRACT_VTM();
+#undef AREF
+ }
+ else if (rb_integer_type_p(time)) {
+ t = v2w(time);
+ GMTIMEW(rb_time_magnify(t), vtm);
+ }
+ else {
+#define AREF(x) rb_funcallv(time, id_##x, 0, 0)
+ EXTRACT_VTM();
+#undef AREF
+ }
+#undef EXTRACT_VTM
+ vtm->subsecx = subsecx;
+ validate_vtm(vtm);
+ return t;
+}
+
+static int
+zone_timelocal(VALUE zone, VALUE time)
+{
+ VALUE utc, tm;
+ struct time_object *tobj = DATA_PTR(time);
+ wideval_t t, s;
+
+ t = rb_time_unmagnify(tobj->timew);
+ tm = tm_from_time(rb_cTimeTM, time);
+ utc = rb_check_funcall(zone, id_local_to_utc, 1, &tm);
+ if (utc == Qundef) return 0;
+
+ s = extract_time(utc);
+ zone_set_offset(zone, tobj, t, s);
+ s = rb_time_magnify(s);
+ if (tobj->vtm.subsecx != INT2FIX(0)) {
+ s = wadd(s, v2w(tobj->vtm.subsecx));
+ }
+ tobj->timew = s;
+ return 1;
+}
+
+static int
+zone_localtime(VALUE zone, VALUE time)
+{
+ VALUE local, tm, subsecx;
+ struct time_object *tobj = DATA_PTR(time);
+ wideval_t t, s;
+
+ split_second(tobj->timew, &t, &subsecx);
+ tm = tm_from_time(rb_cTimeTM, time);
+
+ local = rb_check_funcall(zone, id_utc_to_local, 1, &tm);
+ if (local == Qundef) return 0;
+
+ s = extract_vtm(local, &tobj->vtm, subsecx);
+ tobj->tm_got = 1;
+ zone_set_offset(zone, tobj, s, t);
+ return 1;
+}
+
+static VALUE
+find_timezone(VALUE time, VALUE zone)
+{
+ VALUE klass = CLASS_OF(time);
+
+ return rb_check_funcall_default(klass, id_find_timezone, 1, &zone, Qnil);
+}
+
static VALUE
time_init_1(int argc, VALUE *argv, VALUE time)
{
struct vtm vtm;
+ VALUE zone = Qnil;
VALUE v[7];
struct time_object *tobj;
vtm.wday = VTM_WDAY_INITVAL;
vtm.yday = 0;
- vtm.zone = "";
+ vtm.zone = rb_fstring_lit("");
/* year mon mday hour min sec off */
rb_scan_args(argc, argv, "16", &v[0],&v[1],&v[2],&v[3],&v[4],&v[5],&v[6]);
@@ -1989,8 +2252,15 @@ time_init_1(int argc, VALUE *argv, VALUE time)
vtm.min = NIL_P(v[4]) ? 0 : obj2ubits(v[4], 6);
- vtm.subsecx = INT2FIX(0);
- vtm.sec = NIL_P(v[5]) ? 0 : obj2subsecx(v[5], &vtm.subsecx);
+ if (NIL_P(v[5])) {
+ vtm.sec = 0;
+ vtm.subsecx = INT2FIX(0);
+ }
+ else {
+ VALUE subsecx;
+ vtm.sec = obj2subsecx(v[5], &subsecx);
+ vtm.subsecx = subsecx;
+ }
vtm.isdst = VTM_ISDST_INITVAL;
vtm.utc_offset = Qnil;
@@ -2000,15 +2270,33 @@ time_init_1(int argc, VALUE *argv, VALUE time)
vtm.isdst = 1;
else if (arg == ID2SYM(rb_intern("std")))
vtm.isdst = 0;
- else
- vtm.utc_offset = utc_offset_arg(arg);
+ else if (maybe_tzobj_p(arg))
+ zone = arg;
+ else if (NIL_P(vtm.utc_offset = utc_offset_arg(arg)))
+ if (NIL_P(zone = find_timezone(time, arg)))
+ invalid_utc_offset();
}
validate_vtm(&vtm);
time_modify(time);
GetNewTimeval(time, tobj);
- tobj->gmt = 0;
+
+ if (!NIL_P(zone)) {
+ tobj->timew = timegmw(&vtm);
+ tobj->vtm = vtm;
+ tobj->tm_got = 1;
+ TZMODE_SET_LOCALTIME(tobj);
+ if (zone_timelocal(zone, time)) {
+ return time;
+ }
+ else if (NIL_P(vtm.utc_offset = utc_offset_arg(zone))) {
+ if (NIL_P(zone = find_timezone(time, zone)) || !zone_timelocal(zone, time))
+ invalid_utc_offset();
+ }
+ }
+
+ tobj->tzmode = TIME_TZMODE_LOCALTIME;
tobj->tm_got=0;
tobj->timew = WINT2FIXWV(0);
@@ -2029,7 +2317,7 @@ time_init_1(int argc, VALUE *argv, VALUE time)
/*
* call-seq:
* Time.new -> time
- * Time.new(year, month=nil, day=nil, hour=nil, min=nil, sec=nil, utc_offset=nil) -> time
+ * Time.new(year, month=nil, day=nil, hour=nil, min=nil, sec=nil, tz=nil) -> time
*
* Returns a Time object.
*
@@ -2038,13 +2326,16 @@ time_init_1(int argc, VALUE *argv, VALUE time)
* *Note:* The new object will use the resolution available on your
* system clock, and may include fractional seconds.
*
- * If one or more arguments specified, the time is initialized to the specified
- * time.
+ * If one or more arguments are specified, the time is initialized to the
+ * specified time.
*
* +sec+ may have fraction if it is a rational.
*
- * +utc_offset+ is the offset from UTC.
- * It can be a string such as "+09:00" or a number of seconds such as 32400.
+ * +tz+ specifies the timezone.
+ * It can be an offset from UTC, given either as a string such as "+09:00"
+ * or as a number of seconds such as 32400.
+ * Or it can be a timezone object,
+ * see {Timezone argument}[#class-Time-label-Timezone+argument] for details.
*
* a = Time.new #=> 2007-11-19 07:50:02 -0600
* b = Time.new #=> 2007-11-19 07:50:02 -0600
@@ -2063,10 +2354,10 @@ time_init_1(int argc, VALUE *argv, VALUE time)
* t6 = Time.new(2007,11,5,11,21,0, "-05:00") # EST (Detroit)
* t7 = Time.new(2007,11,5,13,45,0, "-05:00") # EST (Detroit)
* t8 = Time.new(2007,11,6,17,10,0, "+09:00") # JST (Narita)
- * p((t2-t1)/3600.0) #=> 10.666666666666666
- * p((t4-t3)/3600.0) #=> 2.466666666666667
- * p((t6-t5)/3600.0) #=> 1.95
- * p((t8-t7)/3600.0) #=> 13.416666666666666
+ * (t2-t1)/3600.0 #=> 10.666666666666666
+ * (t4-t3)/3600.0 #=> 2.466666666666667
+ * (t6-t5)/3600.0 #=> 1.95
+ * (t8-t7)/3600.0 #=> 13.416666666666666
*
*/
@@ -2127,7 +2418,7 @@ time_new_timew(VALUE klass, wideval_t timew)
struct time_object *tobj;
tobj = DATA_PTR(time); /* skip type check */
- tobj->gmt = 0;
+ tobj->tzmode = TIME_TZMODE_LOCALTIME;
tobj->timew = timew;
return time;
@@ -2179,13 +2470,13 @@ rb_time_timespec_new(const struct timespec *ts, int offset)
if (-86400 < offset && offset < 86400) { /* fixoff */
GetTimeval(time, tobj);
- TIME_SET_FIXOFF(tobj, INT2FIX(offset));
+ TZMODE_SET_FIXOFF(tobj, INT2FIX(offset));
}
else if (offset == INT_MAX) { /* localtime */
}
else if (offset == INT_MAX-1) { /* UTC */
GetTimeval(time, tobj);
- TIME_SET_UTC(tobj);
+ TZMODE_SET_UTC(tobj);
}
else {
rb_raise(rb_eArgError, "utc_offset out of range");
@@ -2200,7 +2491,18 @@ rb_time_num_new(VALUE timev, VALUE off)
VALUE time = time_new_timew(rb_cTime, rb_time_magnify(v2w(timev)));
if (!NIL_P(off)) {
- off = utc_offset_arg(off);
+ VALUE zone = off;
+
+ if (maybe_tzobj_p(zone)) {
+ time_gmtime(time);
+ if (zone_timelocal(zone, time)) return time;
+ }
+ if (NIL_P(off = utc_offset_arg(off))) {
+ if (NIL_P(zone = find_timezone(time, zone))) invalid_utc_offset();
+ time_gmtime(time);
+ if (!zone_timelocal(zone, time)) invalid_utc_offset();
+ return time;
+ }
validate_utc_offset(off);
time_set_utc_offset(time, off);
return time;
@@ -2217,24 +2519,29 @@ time_timespec(VALUE num, int interval)
VALUE i, f, ary;
#ifndef NEGATIVE_TIME_T
- interval = 1;
+# define arg_range_check(v) \
+ (((v) < 0) ? \
+ rb_raise(rb_eArgError, "%s must not be negative", tstr) : \
+ (void)0)
+#else
+# define arg_range_check(v) \
+ ((interval && (v) < 0) ? \
+ rb_raise(rb_eArgError, "time interval must not be negative") : \
+ (void)0)
#endif
- switch (TYPE(num)) {
- case T_FIXNUM:
+ if (FIXNUM_P(num)) {
t.tv_sec = NUM2TIMET(num);
- if (interval && t.tv_sec < 0)
- rb_raise(rb_eArgError, "%s must be positive", tstr);
+ arg_range_check(t.tv_sec);
t.tv_nsec = 0;
- break;
-
- case T_FLOAT:
- if (interval && RFLOAT_VALUE(num) < 0.0)
- rb_raise(rb_eArgError, "%s must be positive", tstr);
- else {
+ }
+ else if (RB_FLOAT_TYPE_P(num)) {
+ double x = RFLOAT_VALUE(num);
+ arg_range_check(x);
+ {
double f, d;
- d = modf(RFLOAT_VALUE(num), &f);
+ d = modf(x, &f);
if (d >= 0) {
t.tv_nsec = (int)(d*1e9+0.5);
if (t.tv_nsec >= 1000000000) {
@@ -2248,37 +2555,33 @@ time_timespec(VALUE num, int interval)
}
t.tv_sec = (time_t)f;
if (f != t.tv_sec) {
- rb_raise(rb_eRangeError, "%f out of Time range", RFLOAT_VALUE(num));
+ rb_raise(rb_eRangeError, "%f out of Time range", x);
}
}
- break;
-
- case T_BIGNUM:
+ }
+ else if (RB_TYPE_P(num, T_BIGNUM)) {
t.tv_sec = NUM2TIMET(num);
- if (interval && t.tv_sec < 0)
- rb_raise(rb_eArgError, "%s must be positive", tstr);
+ arg_range_check(t.tv_sec);
t.tv_nsec = 0;
- break;
-
- default:
+ }
+ else {
i = INT2FIX(1);
ary = rb_check_funcall(num, id_divmod, 1, &i);
if (ary != Qundef && !NIL_P(ary = rb_check_array_type(ary))) {
i = rb_ary_entry(ary, 0);
f = rb_ary_entry(ary, 1);
t.tv_sec = NUM2TIMET(i);
- if (interval && t.tv_sec < 0)
- rb_raise(rb_eArgError, "%s must be positive", tstr);
- f = rb_funcall(f, id_mul, 1, INT2FIX(1000000000));
+ arg_range_check(t.tv_sec);
+ f = rb_funcall(f, '*', 1, INT2FIX(1000000000));
t.tv_nsec = NUM2LONG(f);
}
else {
rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into %s",
rb_obj_class(num), tstr);
}
- break;
}
return t;
+#undef arg_range_check
}
static struct timeval
@@ -2347,11 +2650,41 @@ time_s_now(VALUE klass)
return rb_class_new_instance(0, NULL, klass);
}
+static int
+get_scale(VALUE unit)
+{
+ if (unit == ID2SYM(id_nanosecond) || unit == ID2SYM(id_nsec)) {
+ return 1000000000;
+ }
+ else if (unit == ID2SYM(id_microsecond) || unit == ID2SYM(id_usec)) {
+ return 1000000;
+ }
+ else if (unit == ID2SYM(id_millisecond)) {
+ return 1000;
+ }
+ else {
+ rb_raise(rb_eArgError, "unexpected unit: %"PRIsVALUE, unit);
+ }
+}
+
/*
* call-seq:
* Time.at(time) -> time
* Time.at(seconds_with_frac) -> time
* Time.at(seconds, microseconds_with_frac) -> time
+ * Time.at(seconds, milliseconds, :millisecond) -> time
+ * Time.at(seconds, microseconds, :usec) -> time
+ * Time.at(seconds, microseconds, :microsecond) -> time
+ * Time.at(seconds, nanoseconds, :nsec) -> time
+ * Time.at(seconds, nanoseconds, :nanosecond) -> time
+ * Time.at(time, in: tz) -> time
+ * Time.at(seconds_with_frac, in: tz) -> time
+ * Time.at(seconds, microseconds_with_frac, in: tz) -> time
+ * Time.at(seconds, milliseconds, :millisecond, in: tz) -> time
+ * Time.at(seconds, microseconds, :usec, in: tz) -> time
+ * Time.at(seconds, microseconds, :microsecond, in: tz) -> time
+ * Time.at(seconds, nanoseconds, :nsec, in: tz) -> time
+ * Time.at(seconds, nanoseconds, :nanosecond, in: tz) -> time
*
* Creates a new Time object with the value given by +time+,
* the given number of +seconds_with_frac+, or
@@ -2360,26 +2693,38 @@ time_s_now(VALUE klass)
* can be an Integer, Float, Rational, or other Numeric.
* non-portable feature allows the offset to be negative on some systems.
*
- * If a numeric argument is given, the result is in local time.
+ * If +in+ argument is given, the result is in that timezone or UTC offset, or
+ * if a numeric argument is given, the result is in local time.
*
- * Time.at(0) #=> 1969-12-31 18:00:00 -0600
- * Time.at(Time.at(0)) #=> 1969-12-31 18:00:00 -0600
- * Time.at(946702800) #=> 1999-12-31 23:00:00 -0600
- * Time.at(-284061600) #=> 1960-12-31 00:00:00 -0600
- * Time.at(946684800.2).usec #=> 200000
- * Time.at(946684800, 123456.789).nsec #=> 123456789
+ * Time.at(0) #=> 1969-12-31 18:00:00 -0600
+ * Time.at(Time.at(0)) #=> 1969-12-31 18:00:00 -0600
+ * Time.at(946702800) #=> 1999-12-31 23:00:00 -0600
+ * Time.at(-284061600) #=> 1960-12-31 00:00:00 -0600
+ * Time.at(946684800.2).usec #=> 200000
+ * Time.at(946684800, 123456.789).nsec #=> 123456789
+ * Time.at(946684800, 123456789, :nsec).nsec #=> 123456789
*/
static VALUE
time_s_at(int argc, VALUE *argv, VALUE klass)
{
- VALUE time, t;
+ VALUE time, t, unit = Qundef, zone = Qundef, opts;
wideval_t timew;
- if (rb_scan_args(argc, argv, "11", &time, &t) == 2) {
+ argc = rb_scan_args(argc, argv, "12:", &time, &t, &unit, &opts);
+ if (!NIL_P(opts)) {
+ ID ids[1];
+ VALUE vals[numberof(ids)];
+
+ CONST_ID(ids[0], "in");
+ rb_get_kwargs(opts, ids, 0, 1, vals);
+ zone = vals[0];
+ }
+ if (argc >= 2) {
+ int scale = argc == 3 ? get_scale(unit) : 1000000;
time = num_exact(time);
t = num_exact(t);
- timew = wadd(rb_time_magnify(v2w(time)), wmulquoll(v2w(t), TIME_SCALE, 1000000));
+ timew = wadd(rb_time_magnify(v2w(time)), wmulquoll(v2w(t), TIME_SCALE, scale));
t = time_new_timew(klass, timew);
}
else if (IsTimeval(time)) {
@@ -2387,12 +2732,15 @@ time_s_at(int argc, VALUE *argv, VALUE klass)
GetTimeval(time, tobj);
t = time_new_timew(klass, tobj->timew);
GetTimeval(t, tobj2);
- TIME_COPY_GMT(tobj2, tobj);
+ TZMODE_COPY(tobj2, tobj);
}
else {
timew = rb_time_magnify(v2w(num_exact(time)));
t = time_new_timew(klass, timew);
}
+ if (zone != Qundef) {
+ time_zonelocal(t, zone);
+ }
return t;
}
@@ -2457,14 +2805,14 @@ obj2subsecx(VALUE obj, VALUE *subsecx)
return obj2ubits(obj, 6); /* vtm->sec */
}
-static long
+static VALUE
usec2subsecx(VALUE obj)
{
if (RB_TYPE_P(obj, T_STRING)) {
obj = rb_str_to_inum(obj, 10, FALSE);
}
- return mulquo(num_exact(obj), INT2FIX(TIME_SCALE), INT2FIX(1000000));
+ return mulquov(num_exact(obj), INT2FIX(TIME_SCALE), INT2FIX(1000000));
}
static uint32_t
@@ -2514,21 +2862,25 @@ validate_zone_name(VALUE zone_name)
static void
validate_vtm(struct vtm *vtm)
{
- if ( vtm->mon < 1 || vtm->mon > 12
- || vtm->mday < 1 || vtm->mday > 31
- || vtm->hour < 0 || vtm->hour > 24
- || (vtm->hour == 24 && (vtm->min > 0 || vtm->sec > 0))
- || vtm->min < 0 || vtm->min > 59
- || vtm->sec < 0 || vtm->sec > 60
- || lt(vtm->subsecx, INT2FIX(0)) || ge(vtm->subsecx, INT2FIX(TIME_SCALE))
- || (!NIL_P(vtm->utc_offset) && (validate_utc_offset(vtm->utc_offset), 0)))
- rb_raise(rb_eArgError, "argument out of range");
+#define validate_vtm_range(mem, b, e) \
+ ((vtm->mem < b || vtm->mem > e) ? \
+ rb_raise(rb_eArgError, #mem" out of range") : (void)0)
+ validate_vtm_range(mon, 1, 12);
+ validate_vtm_range(mday, 1, 31);
+ validate_vtm_range(hour, 0, 24);
+ validate_vtm_range(min, 0, (vtm->hour == 24 ? 0 : 59));
+ validate_vtm_range(sec, 0, (vtm->hour == 24 ? 0 : 60));
+ if (lt(vtm->subsecx, INT2FIX(0)) || ge(vtm->subsecx, INT2FIX(TIME_SCALE)))
+ rb_raise(rb_eArgError, "subsecx out of range");
+ if (!NIL_P(vtm->utc_offset)) validate_utc_offset(vtm->utc_offset);
+#undef validate_vtm_range
}
static void
-time_arg(int argc, VALUE *argv, struct vtm *vtm)
+time_arg(int argc, const VALUE *argv, struct vtm *vtm)
{
VALUE v[8];
+ VALUE subsecx = INT2FIX(0);
vtm->year = INT2FIX(0);
vtm->mon = 0;
@@ -2541,7 +2893,7 @@ time_arg(int argc, VALUE *argv, struct vtm *vtm)
vtm->wday = 0;
vtm->yday = 0;
vtm->isdst = 0;
- vtm->zone = "";
+ vtm->zone = rb_fstring_lit("");
if (argc == 10) {
v[0] = argv[5];
@@ -2577,21 +2929,50 @@ time_arg(int argc, VALUE *argv, struct vtm *vtm)
vtm->mday = obj2ubits(v[2], 5);
}
+ /* normalize month-mday */
+ switch (vtm->mon) {
+ case 2:
+ {
+ /* this drops higher bits but it's not a problem to calc leap year */
+ unsigned int mday2 = leap_year_v_p(vtm->year) ? 29 : 28;
+ if (vtm->mday > mday2) {
+ vtm->mday -= mday2;
+ vtm->mon++;
+ }
+ }
+ break;
+ case 4:
+ case 6:
+ case 9:
+ case 11:
+ if (vtm->mday == 31) {
+ vtm->mon++;
+ vtm->mday = 1;
+ }
+ break;
+ }
+
vtm->hour = NIL_P(v[3])?0:obj2ubits(v[3], 5);
vtm->min = NIL_P(v[4])?0:obj2ubits(v[4], 6);
if (!NIL_P(v[6]) && argc == 7) {
- vtm->sec = NIL_P(v[5])?0:obj2ubits(v[5],6);
- vtm->subsecx = usec2subsecx(v[6]);
+ vtm->sec = NIL_P(v[5])?0:obj2ubits(v[5],6);
+ subsecx = usec2subsecx(v[6]);
}
else {
/* when argc == 8, v[6] is timezone, but ignored */
- vtm->sec = NIL_P(v[5])?0:obj2subsecx(v[5], &vtm->subsecx);
+ if (NIL_P(v[5])) {
+ vtm->sec = 0;
+ }
+ else {
+ vtm->sec = obj2subsecx(v[5], &subsecx);
+ }
}
+ vtm->subsecx = subsecx;
validate_vtm(vtm);
- RB_GC_GUARD(vtm->subsecx);
+ RB_GC_GUARD(subsecx);
}
static int
@@ -2604,11 +2985,7 @@ static time_t
timegm_noleapsecond(struct tm *tm)
{
long tm_year = tm->tm_year;
- int tm_yday = tm->tm_mday;
- if (leap_year_p(tm_year + 1900))
- tm_yday += leap_year_yday_offset[tm->tm_mon];
- else
- tm_yday += common_year_yday_offset[tm->tm_mon];
+ int tm_yday = calc_tm_yday(tm->tm_year, tm->tm_mon, tm->tm_mday);
/*
* `Seconds Since the Epoch' in SUSv3:
@@ -2896,12 +3273,12 @@ find_time_t(struct tm *tptr, int utc_p, time_t *tp)
*tp = guess_lo +
((tptr->tm_year - tm_lo.tm_year) * 365 +
- ((tptr->tm_year-69)/4) -
- ((tptr->tm_year-1)/100) +
- ((tptr->tm_year+299)/400) -
- ((tm_lo.tm_year-69)/4) +
- ((tm_lo.tm_year-1)/100) -
- ((tm_lo.tm_year+299)/400) +
+ DIV((tptr->tm_year-69), 4) -
+ DIV((tptr->tm_year-1), 100) +
+ DIV((tptr->tm_year+299), 400) -
+ DIV((tm_lo.tm_year-69), 4) +
+ DIV((tm_lo.tm_year-1), 100) -
+ DIV((tm_lo.tm_year+299), 400) +
tptr_tm_yday -
tm_lo.tm_yday) * 86400 +
(tptr->tm_hour - tm_lo.tm_hour) * 3600 +
@@ -2957,21 +3334,6 @@ tmcmp(struct tm *a, struct tm *b)
return 0;
}
-static VALUE
-time_utc_or_local(int argc, VALUE *argv, int utc_p, VALUE klass)
-{
- struct vtm vtm;
- VALUE time;
-
- time_arg(argc, argv, &vtm);
- if (utc_p)
- time = time_new_timew(klass, timegmw(&vtm));
- else
- time = time_new_timew(klass, timelocalw(&vtm));
- if (utc_p) return time_gmtime(time);
- return time_localtime(time);
-}
-
/*
* call-seq:
* Time.utc(year) -> time
@@ -3007,7 +3369,10 @@ time_utc_or_local(int argc, VALUE *argv, int utc_p, VALUE klass)
static VALUE
time_s_mkutc(int argc, VALUE *argv, VALUE klass)
{
- return time_utc_or_local(argc, argv, TRUE, klass);
+ struct vtm vtm;
+
+ time_arg(argc, argv, &vtm);
+ return time_gmtime(time_new_timew(klass, timegmw(&vtm)));
}
/*
@@ -3038,7 +3403,10 @@ time_s_mkutc(int argc, VALUE *argv, VALUE klass)
static VALUE
time_s_mktime(int argc, VALUE *argv, VALUE klass)
{
- return time_utc_or_local(argc, argv, FALSE, klass);
+ struct vtm vtm;
+
+ time_arg(argc, argv, &vtm);
+ return time_localtime(time_new_timew(klass, timelocalw(&vtm)));
}
/*
@@ -3075,7 +3443,7 @@ time_to_i(VALUE time)
* t.to_i #=> 1270968744
*
* Note that IEEE 754 double is not accurate enough to represent
- * the number of nanoseconds since the Epoch.
+ * the exact number of nanoseconds since the Epoch.
*/
static VALUE
@@ -3095,7 +3463,7 @@ time_to_f(VALUE time)
* since the Epoch.
*
* t = Time.now
- * p t.to_r #=> (1270968792716287611/1000000000)
+ * t.to_r #=> (1270968792716287611/1000000000)
*
* This methods is intended to be used to get an accurate value
* representing the nanoseconds since the Epoch. You can use this method
@@ -3193,12 +3561,12 @@ time_subsec(VALUE time)
struct time_object *tobj;
GetTimeval(time, tobj);
- return quo(w2v(wmod(tobj->timew, WINT2FIXWV(TIME_SCALE))), INT2FIX(TIME_SCALE));
+ return quov(w2v(wmod(tobj->timew, WINT2FIXWV(TIME_SCALE))), INT2FIX(TIME_SCALE));
}
/*
* call-seq:
- * time <=> other_time -> -1, 0, +1 or nil
+ * time <=> other_time -> -1, 0, +1, or nil
*
* Comparison---Compares +time+ with +other_time+.
*
@@ -3285,7 +3653,7 @@ time_utc_p(VALUE time)
struct time_object *tobj;
GetTimeval(time, tobj);
- if (TIME_UTC_P(tobj)) return Qtrue;
+ if (TZMODE_UTC_P(tobj)) return Qtrue;
return Qfalse;
}
@@ -3334,9 +3702,10 @@ time_localtime(VALUE time)
{
struct time_object *tobj;
struct vtm vtm;
+ VALUE zone;
GetTimeval(time, tobj);
- if (TIME_LOCALTIME_P(tobj)) {
+ if (TZMODE_LOCALTIME_P(tobj)) {
if (tobj->tm_got)
return time;
}
@@ -3344,22 +3713,44 @@ time_localtime(VALUE time)
time_modify(time);
}
+ zone = tobj->vtm.zone;
+ if (maybe_tzobj_p(zone) && zone_localtime(zone, time)) {
+ return time;
+ }
+
if (!localtimew(tobj->timew, &vtm))
rb_raise(rb_eArgError, "localtime error");
tobj->vtm = vtm;
tobj->tm_got = 1;
- TIME_SET_LOCALTIME(tobj);
+ TZMODE_SET_LOCALTIME(tobj);
return time;
}
+static VALUE
+time_zonelocal(VALUE time, VALUE off)
+{
+ VALUE zone = off;
+ if (zone_localtime(zone, time)) return time;
+
+ if (NIL_P(off = utc_offset_arg(off))) {
+ if (NIL_P(zone = find_timezone(time, zone))) invalid_utc_offset();
+ if (!zone_localtime(zone, time)) invalid_utc_offset();
+ return time;
+ }
+ validate_utc_offset(off);
+
+ time_set_utc_offset(time, off);
+ return time_fixoff(time);
+}
+
/*
* call-seq:
* time.localtime -> time
* time.localtime(utc_offset) -> time
*
* Converts _time_ to local time (using the local time zone in
- * effect for this process) modifying the receiver.
+ * effect at the creation time of _time_) modifying the receiver.
*
* If +utc_offset+ is given, it is used instead of the local time.
*
@@ -3371,20 +3762,18 @@ time_localtime(VALUE time)
*
* t.localtime("+09:00") #=> 2000-01-02 05:15:01 +0900
* t.utc? #=> false
+ *
+ * If +utc_offset+ is not given and _time_ is local time, just returns
+ * the receiver.
*/
static VALUE
time_localtime_m(int argc, VALUE *argv, VALUE time)
{
VALUE off;
- rb_scan_args(argc, argv, "01", &off);
-
- if (!NIL_P(off)) {
- off = utc_offset_arg(off);
- validate_utc_offset(off);
- time_set_utc_offset(time, off);
- return time_fixoff(time);
+ if (rb_check_arity(argc, 0, 1) && !NIL_P(off = argv[0])) {
+ return time_zonelocal(time, off);
}
return time_localtime(time);
@@ -3415,7 +3804,7 @@ time_gmtime(VALUE time)
struct vtm vtm;
GetTimeval(time, tobj);
- if (TIME_UTC_P(tobj)) {
+ if (TZMODE_UTC_P(tobj)) {
if (tobj->tm_got)
return time;
}
@@ -3423,12 +3812,12 @@ time_gmtime(VALUE time)
time_modify(time);
}
- if (!gmtimew(tobj->timew, &vtm))
- rb_raise(rb_eArgError, "gmtime error");
+ vtm.zone = rb_fstring_lit("UTC");
+ GMTIMEW(tobj->timew, &vtm);
tobj->vtm = vtm;
tobj->tm_got = 1;
- TIME_SET_UTC(tobj);
+ TZMODE_SET_UTC(tobj);
return time;
}
@@ -3437,10 +3826,10 @@ time_fixoff(VALUE time)
{
struct time_object *tobj;
struct vtm vtm;
- VALUE off;
+ VALUE off, zone;
GetTimeval(time, tobj);
- if (TIME_FIXOFF_P(tobj)) {
+ if (TZMODE_FIXOFF_P(tobj)) {
if (tobj->tm_got)
return time;
}
@@ -3448,19 +3837,20 @@ time_fixoff(VALUE time)
time_modify(time);
}
- if (TIME_FIXOFF_P(tobj))
+ if (TZMODE_FIXOFF_P(tobj))
off = tobj->vtm.utc_offset;
else
off = INT2FIX(0);
- if (!gmtimew(tobj->timew, &vtm))
- rb_raise(rb_eArgError, "gmtime error");
+ GMTIMEW(tobj->timew, &vtm);
+ zone = tobj->vtm.zone;
tobj->vtm = vtm;
+ tobj->vtm.zone = zone;
vtm_add_offset(&tobj->vtm, off);
tobj->tm_got = 1;
- TIME_SET_FIXOFF(tobj, off);
+ TZMODE_SET_FIXOFF(tobj, off);
return time;
}
@@ -3468,6 +3858,7 @@ time_fixoff(VALUE time)
* call-seq:
* time.getlocal -> new_time
* time.getlocal(utc_offset) -> new_time
+ * time.getlocal(timezone) -> new_time
*
* Returns a new Time object representing _time_ in
* local time (using the local time zone in effect for this process).
@@ -3496,10 +3887,20 @@ static VALUE
time_getlocaltime(int argc, VALUE *argv, VALUE time)
{
VALUE off;
- rb_scan_args(argc, argv, "01", &off);
- if (!NIL_P(off)) {
- off = utc_offset_arg(off);
+ if (rb_check_arity(argc, 0, 1) && !NIL_P(off = argv[0])) {
+ VALUE zone = off;
+ if (maybe_tzobj_p(zone)) {
+ VALUE t = time_dup(time);
+ if (zone_localtime(off, t)) return t;
+ }
+
+ if (NIL_P(off = utc_offset_arg(off))) {
+ if (NIL_P(zone = find_timezone(time, zone))) invalid_utc_offset();
+ time = time_dup(time);
+ if (!zone_localtime(zone, time)) invalid_utc_offset();
+ return time;
+ }
validate_utc_offset(off);
time = time_dup(time);
@@ -3533,8 +3934,8 @@ time_getgmtime(VALUE time)
static VALUE
time_get_tm(VALUE time, struct time_object *tobj)
{
- if (TIME_UTC_P(tobj)) return time_gmtime(time);
- if (TIME_FIXOFF_P(tobj)) return time_fixoff(time);
+ if (TZMODE_UTC_P(tobj)) return time_gmtime(time);
+ if (TZMODE_FIXOFF_P(tobj)) return time_fixoff(time);
return time_localtime(time);
}
@@ -3567,11 +3968,11 @@ time_asctime(VALUE time)
* #strftime with the appropriate format string.
*
* t = Time.now
- * t.to_s => "2012-11-10 18:16:12 +0100"
- * t.strftime "%Y-%m-%d %H:%M:%S %z" => "2012-11-10 18:16:12 +0100"
+ * t.to_s #=> "2012-11-10 18:16:12 +0100"
+ * t.strftime "%Y-%m-%d %H:%M:%S %z" #=> "2012-11-10 18:16:12 +0100"
*
- * t.utc.to_s => "2012-11-10 17:16:12 UTC"
- * t.strftime "%Y-%m-%d %H:%M:%S UTC" => "2012-11-10 17:16:12 UTC"
+ * t.utc.to_s #=> "2012-11-10 17:16:12 UTC"
+ * t.strftime "%Y-%m-%d %H:%M:%S UTC" #=> "2012-11-10 17:16:12 UTC"
*/
static VALUE
@@ -3580,33 +3981,35 @@ time_to_s(VALUE time)
struct time_object *tobj;
GetTimeval(time, tobj);
- if (TIME_UTC_P(tobj))
+ if (TZMODE_UTC_P(tobj))
return strftimev("%Y-%m-%d %H:%M:%S UTC", time, rb_usascii_encoding());
else
return strftimev("%Y-%m-%d %H:%M:%S %z", time, rb_usascii_encoding());
}
static VALUE
-time_add(struct time_object *tobj, VALUE offset, int sign)
+time_add0(VALUE klass, const struct time_object *tobj, VALUE torig, VALUE offset, int sign)
{
VALUE result;
+ struct time_object *result_tobj;
+
offset = num_exact(offset);
if (sign < 0)
- result = time_new_timew(rb_cTime, wsub(tobj->timew, rb_time_magnify(v2w(offset))));
+ result = time_new_timew(klass, wsub(tobj->timew, rb_time_magnify(v2w(offset))));
else
- result = time_new_timew(rb_cTime, wadd(tobj->timew, rb_time_magnify(v2w(offset))));
- if (TIME_UTC_P(tobj)) {
- GetTimeval(result, tobj);
- TIME_SET_UTC(tobj);
- }
- else if (TIME_FIXOFF_P(tobj)) {
- VALUE off = tobj->vtm.utc_offset;
- GetTimeval(result, tobj);
- TIME_SET_FIXOFF(tobj, off);
- }
+ result = time_new_timew(klass, wadd(tobj->timew, rb_time_magnify(v2w(offset))));
+ GetTimeval(result, result_tobj);
+ TZMODE_COPY(result_tobj, tobj);
+
return result;
}
+static VALUE
+time_add(const struct time_object *tobj, VALUE torig, VALUE offset, int sign)
+{
+ return time_add0(rb_cTime, tobj, torig, offset, sign);
+}
+
/*
* call-seq:
* time + numeric -> time
@@ -3627,7 +4030,7 @@ time_plus(VALUE time1, VALUE time2)
if (IsTimeval(time2)) {
rb_raise(rb_eTypeError, "time + time?");
}
- return time_add(tobj, time2, 1);
+ return time_add(tobj, time1, time2, 1);
}
/*
@@ -3635,7 +4038,7 @@ time_plus(VALUE time1, VALUE time2)
* time - other_time -> float
* time - numeric -> time
*
- * Difference --- Returns a new Time object that represents the difference
+ * Difference --- Returns a difference in seconds as a Float
* between _time_ and +other_time+, or subtracts the given number
* of seconds in +numeric+ from _time_.
*
@@ -3657,7 +4060,7 @@ time_minus(VALUE time1, VALUE time2)
GetTimeval(time2, tobj2);
return rb_Float(rb_time_unmagnify_to_float(wsub(tobj->timew, tobj2->timew)));
}
- return time_add(tobj, time2, -1);
+ return time_add(tobj, time1, time2, -1);
}
/*
@@ -3685,7 +4088,10 @@ rb_time_succ(VALUE time)
GetTimeval(time, tobj);
time = time_new_timew(rb_cTime, wadd(tobj->timew, WINT2FIXWV(TIME_SCALE)));
GetTimeval(time, tobj2);
- TIME_COPY_GMT(tobj2, tobj);
+ TZMODE_COPY(tobj2, tobj);
+ if (TZMODE_LOCALTIME_P(tobj2) && maybe_tzobj_p(tobj2->vtm.zone)) {
+ zone_localtime(tobj2->vtm.zone, time);
+ }
return time;
}
@@ -3697,35 +4103,35 @@ rb_time_succ(VALUE time)
*
* Rounds sub seconds to a given precision in decimal digits (0 digits by default).
* It returns a new Time object.
- * +ndigits+ should be zero or positive integer.
+ * +ndigits+ should be zero or a positive integer.
*
* require 'time'
*
* t = Time.utc(2010,3,30, 5,43,"25.123456789".to_r)
- * p t.iso8601(10) #=> "2010-03-30T05:43:25.1234567890Z"
- * p t.round.iso8601(10) #=> "2010-03-30T05:43:25.0000000000Z"
- * p t.round(0).iso8601(10) #=> "2010-03-30T05:43:25.0000000000Z"
- * p t.round(1).iso8601(10) #=> "2010-03-30T05:43:25.1000000000Z"
- * p t.round(2).iso8601(10) #=> "2010-03-30T05:43:25.1200000000Z"
- * p t.round(3).iso8601(10) #=> "2010-03-30T05:43:25.1230000000Z"
- * p t.round(4).iso8601(10) #=> "2010-03-30T05:43:25.1235000000Z"
- * p t.round(5).iso8601(10) #=> "2010-03-30T05:43:25.1234600000Z"
- * p t.round(6).iso8601(10) #=> "2010-03-30T05:43:25.1234570000Z"
- * p t.round(7).iso8601(10) #=> "2010-03-30T05:43:25.1234568000Z"
- * p t.round(8).iso8601(10) #=> "2010-03-30T05:43:25.1234567900Z"
- * p t.round(9).iso8601(10) #=> "2010-03-30T05:43:25.1234567890Z"
- * p t.round(10).iso8601(10) #=> "2010-03-30T05:43:25.1234567890Z"
+ * t.iso8601(10) #=> "2010-03-30T05:43:25.1234567890Z"
+ * t.round.iso8601(10) #=> "2010-03-30T05:43:25.0000000000Z"
+ * t.round(0).iso8601(10) #=> "2010-03-30T05:43:25.0000000000Z"
+ * t.round(1).iso8601(10) #=> "2010-03-30T05:43:25.1000000000Z"
+ * t.round(2).iso8601(10) #=> "2010-03-30T05:43:25.1200000000Z"
+ * t.round(3).iso8601(10) #=> "2010-03-30T05:43:25.1230000000Z"
+ * t.round(4).iso8601(10) #=> "2010-03-30T05:43:25.1235000000Z"
+ * t.round(5).iso8601(10) #=> "2010-03-30T05:43:25.1234600000Z"
+ * t.round(6).iso8601(10) #=> "2010-03-30T05:43:25.1234570000Z"
+ * t.round(7).iso8601(10) #=> "2010-03-30T05:43:25.1234568000Z"
+ * t.round(8).iso8601(10) #=> "2010-03-30T05:43:25.1234567900Z"
+ * t.round(9).iso8601(10) #=> "2010-03-30T05:43:25.1234567890Z"
+ * t.round(10).iso8601(10) #=> "2010-03-30T05:43:25.1234567890Z"
*
* t = Time.utc(1999,12,31, 23,59,59)
- * p((t + 0.4).round.iso8601(3)) #=> "1999-12-31T23:59:59.000Z"
- * p((t + 0.49).round.iso8601(3)) #=> "1999-12-31T23:59:59.000Z"
- * p((t + 0.5).round.iso8601(3)) #=> "2000-01-01T00:00:00.000Z"
- * p((t + 1.4).round.iso8601(3)) #=> "2000-01-01T00:00:00.000Z"
- * p((t + 1.49).round.iso8601(3)) #=> "2000-01-01T00:00:00.000Z"
- * p((t + 1.5).round.iso8601(3)) #=> "2000-01-01T00:00:01.000Z"
+ * (t + 0.4).round.iso8601(3) #=> "1999-12-31T23:59:59.000Z"
+ * (t + 0.49).round.iso8601(3) #=> "1999-12-31T23:59:59.000Z"
+ * (t + 0.5).round.iso8601(3) #=> "2000-01-01T00:00:00.000Z"
+ * (t + 1.4).round.iso8601(3) #=> "2000-01-01T00:00:00.000Z"
+ * (t + 1.49).round.iso8601(3) #=> "2000-01-01T00:00:00.000Z"
+ * (t + 1.5).round.iso8601(3) #=> "2000-01-01T00:00:01.000Z"
*
* t = Time.utc(1999,12,31, 23,59,59)
- * p (t + 0.123456789).round(4).iso8601(6) #=> "1999-12-31T23:59:59.123500Z"
+ * (t + 0.123456789).round(4).iso8601(6) #=> "1999-12-31T23:59:59.123500Z"
*/
static VALUE
@@ -3735,9 +4141,7 @@ time_round(int argc, VALUE *argv, VALUE time)
long nd;
struct time_object *tobj;
- rb_scan_args(argc, argv, "01", &ndigits);
-
- if (NIL_P(ndigits))
+ if (!rb_check_arity(argc, 0, 1) || NIL_P(ndigits = argv[0]))
ndigits = INT2FIX(0);
else
ndigits = rb_to_int(ndigits);
@@ -3753,16 +4157,16 @@ time_round(int argc, VALUE *argv, VALUE time)
b = INT2FIX(10);
while (0 < nd) {
if (nd & 1)
- a = mul(a, b);
- b = mul(b, b);
+ a = mulv(a, b);
+ b = mulv(b, b);
nd = nd >> 1;
}
- den = quo(INT2FIX(1), a);
- v = mod(v, den);
- if (lt(v, quo(den, INT2FIX(2))))
- return time_add(tobj, v, -1);
+ den = quov(INT2FIX(1), a);
+ v = modv(v, den);
+ if (lt(v, quov(den, INT2FIX(2))))
+ return time_add(tobj, time, v, -1);
else
- return time_add(tobj, sub(den, v), 1);
+ return time_add(tobj, time, subv(den, v), 1);
}
/*
@@ -3918,14 +4322,15 @@ time_wday(VALUE time)
GetTimeval(time, tobj);
MAKE_TM(time, tobj);
+ if (tobj->vtm.wday == VTM_WDAY_INITVAL) {
+ VALUE zone = tobj->vtm.zone;
+ if (!NIL_P(zone)) zone_localtime(zone, time);
+ }
return INT2FIX((int)tobj->vtm.wday);
}
#define wday_p(n) {\
- struct time_object *tobj;\
- GetTimeval(time, tobj);\
- MAKE_TM(time, tobj);\
- return (tobj->vtm.wday == (n)) ? Qtrue : Qfalse;\
+ return (time_wday(time) == INT2FIX(n)) ? Qtrue : Qfalse; \
}
/*
@@ -3951,7 +4356,7 @@ time_sunday(VALUE time)
* Returns +true+ if _time_ represents Monday.
*
* t = Time.local(2003, 8, 4) #=> 2003-08-04 00:00:00 -0500
- * p t.monday? #=> true
+ * t.monday? #=> true
*/
static VALUE
@@ -3967,7 +4372,7 @@ time_monday(VALUE time)
* Returns +true+ if _time_ represents Tuesday.
*
* t = Time.local(1991, 2, 19) #=> 1991-02-19 00:00:00 -0600
- * p t.tuesday? #=> true
+ * t.tuesday? #=> true
*/
static VALUE
@@ -3983,7 +4388,7 @@ time_tuesday(VALUE time)
* Returns +true+ if _time_ represents Wednesday.
*
* t = Time.local(1993, 2, 24) #=> 1993-02-24 00:00:00 -0600
- * p t.wednesday? #=> true
+ * t.wednesday? #=> true
*/
static VALUE
@@ -3999,7 +4404,7 @@ time_wednesday(VALUE time)
* Returns +true+ if _time_ represents Thursday.
*
* t = Time.local(1995, 12, 21) #=> 1995-12-21 00:00:00 -0600
- * p t.thursday? #=> true
+ * t.thursday? #=> true
*/
static VALUE
@@ -4057,6 +4462,10 @@ time_yday(VALUE time)
GetTimeval(time, tobj);
MAKE_TM(time, tobj);
+ if (tobj->vtm.yday == 0) {
+ VALUE zone = tobj->vtm.zone;
+ if (!NIL_P(zone)) zone_localtime(zone, time);
+ }
return INT2FIX(tobj->vtm.yday);
}
@@ -4095,22 +4504,9 @@ time_isdst(VALUE time)
return tobj->vtm.isdst ? Qtrue : Qfalse;
}
-static VALUE
-time_zone_name(const char *zone)
-{
- VALUE name = rb_str_new_cstr(zone);
- if (!rb_enc_str_asciionly_p(name)) {
- name = rb_external_str_with_enc(name, rb_locale_encoding());
- }
- else {
- rb_enc_associate(name, rb_usascii_encoding());
- }
- return name;
-}
-
/*
* call-seq:
- * time.zone -> string
+ * time.zone -> string or timezone
*
* Returns the name of the time zone used for _time_. As of Ruby
* 1.8, returns ``UTC'' rather than ``GMT'' for UTC times.
@@ -4125,17 +4521,21 @@ static VALUE
time_zone(VALUE time)
{
struct time_object *tobj;
+ VALUE zone;
GetTimeval(time, tobj);
MAKE_TM(time, tobj);
- if (TIME_UTC_P(tobj)) {
+ if (TZMODE_UTC_P(tobj)) {
return rb_usascii_str_new_cstr("UTC");
}
- if (tobj->vtm.zone == NULL)
+ zone = tobj->vtm.zone;
+ if (NIL_P(zone))
return Qnil;
- return time_zone_name(tobj->vtm.zone);
+ if (RB_TYPE_P(zone, T_STRING))
+ zone = rb_str_dup(zone);
+ return zone;
}
/*
@@ -4153,18 +4553,18 @@ time_zone(VALUE time)
* l.gmt_offset #=> -21600
*/
-static VALUE
-time_utc_offset(VALUE time)
+VALUE
+rb_time_utc_offset(VALUE time)
{
struct time_object *tobj;
GetTimeval(time, tobj);
- MAKE_TM(time, tobj);
- if (TIME_UTC_P(tobj)) {
+ if (TZMODE_UTC_P(tobj)) {
return INT2FIX(0);
}
else {
+ MAKE_TM(time, tobj);
return tobj->vtm.utc_offset;
}
}
@@ -4208,7 +4608,7 @@ time_to_a(VALUE time)
static VALUE
rb_strftime_alloc(const char *format, size_t format_len, rb_encoding *enc,
- struct vtm *vtm, wideval_t timew, int gmt)
+ VALUE time, struct vtm *vtm, wideval_t timew, int gmt)
{
VALUE timev = Qnil;
struct timespec ts;
@@ -4217,10 +4617,10 @@ rb_strftime_alloc(const char *format, size_t format_len, rb_encoding *enc,
timev = w2v(rb_time_unmagnify(timew));
if (NIL_P(timev)) {
- return rb_strftime_timespec(format, format_len, enc, vtm, &ts, gmt);
+ return rb_strftime_timespec(format, format_len, enc, time, vtm, &ts, gmt);
}
else {
- return rb_strftime(format, format_len, enc, vtm, timev, gmt);
+ return rb_strftime(format, format_len, enc, time, vtm, timev, gmt);
}
}
@@ -4232,7 +4632,7 @@ strftime_cstr(const char *fmt, size_t len, VALUE time, rb_encoding *enc)
GetTimeval(time, tobj);
MAKE_TM(time, tobj);
- str = rb_strftime_alloc(fmt, len, enc, &tobj->vtm, tobj->timew, TIME_UTC_P(tobj));
+ str = rb_strftime_alloc(fmt, len, enc, time, &tobj->vtm, tobj->timew, TZMODE_UTC_P(tobj));
if (!str) rb_raise(rb_eArgError, "invalid format: %s", fmt);
return str;
}
@@ -4376,15 +4776,15 @@ strftime_cstr(const char *fmt, size_t len, VALUE time, rb_encoding *enc)
* America/Havana (-05:00), Asia/Harbin (+08:00), Australia/Darwin (+09:30)
* and Australia/Adelaide (+10:30).
* Also, %Z is highly dependent on the operating system.
- * For example, it may generate a non ASCII string on Japanese Windows.
+ * For example, it may generate a non ASCII string on Japanese Windows,
* i.e. the result can be different to "JST".
* So the numeric time zone offset, %z, is recommended.
*
* Examples:
*
* t = Time.new(2007,11,19,8,37,48,"-06:00") #=> 2007-11-19 08:37:48 -0600
- * t.strftime("Printed on %m/%d/%Y") #=> "Printed on 11/19/2007"
- * t.strftime("at %I:%M%p") #=> "at 08:37AM"
+ * t.strftime("Printed on %m/%d/%Y") #=> "Printed on 11/19/2007"
+ * t.strftime("at %I:%M %p") #=> "at 08:37 AM"
*
* Various ISO 8601 formats:
* %Y%m%d => 20071119 Calendar date (basic)
@@ -4431,6 +4831,7 @@ time_strftime(VALUE time, VALUE format)
const char *fmt;
long len;
rb_encoding *enc;
+ VALUE tmp;
GetTimeval(time, tobj);
MAKE_TM(time, tobj);
@@ -4438,17 +4839,18 @@ time_strftime(VALUE time, VALUE format)
if (!rb_enc_str_asciicompat_p(format)) {
rb_raise(rb_eArgError, "format should have ASCII compatible encoding");
}
- format = rb_str_new4(format);
- fmt = RSTRING_PTR(format);
- len = RSTRING_LEN(format);
+ tmp = rb_str_tmp_frozen_acquire(format);
+ fmt = RSTRING_PTR(tmp);
+ len = RSTRING_LEN(tmp);
enc = rb_enc_get(format);
if (len == 0) {
rb_warning("strftime called with empty format string");
return rb_enc_str_new(0, 0, enc);
}
else {
- VALUE str = rb_strftime_alloc(fmt, len, enc, &tobj->vtm, tobj->timew,
- TIME_UTC_P(tobj));
+ VALUE str = rb_strftime_alloc(fmt, len, enc, time, &tobj->vtm, tobj->timew,
+ TZMODE_UTC_P(tobj));
+ rb_str_tmp_frozen_release(format, tmp);
if (!str) rb_raise(rb_eArgError, "invalid format: %"PRIsVALUE, format);
return str;
}
@@ -4467,7 +4869,7 @@ time_mdump(VALUE time)
struct vtm vtm;
long year;
long usec, nsec;
- VALUE subsecx, nano, subnano, v;
+ VALUE subsecx, nano, subnano, v, zone;
GetTimeval(time, tobj);
@@ -4476,24 +4878,26 @@ time_mdump(VALUE time)
if (FIXNUM_P(vtm.year)) {
year = FIX2LONG(vtm.year);
if (year < 1900 || 1900+0xffff < year)
- rb_raise(rb_eArgError, "year too big to marshal: %ld UTC", year);
+ rb_raise(rb_eArgError, "year too %s to marshal: %ld UTC",
+ (year < 1900 ? "small" : "big"), year);
}
else {
- rb_raise(rb_eArgError, "year too big to marshal");
+ rb_raise(rb_eArgError, "year too %s to marshal: %"PRIsVALUE" UTC",
+ (le(vtm.year, INT2FIX(1900)) ? "small" : "big"), vtm.year);
}
subsecx = vtm.subsecx;
- nano = mulquo(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE));
+ nano = mulquov(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE));
divmodv(nano, INT2FIX(1), &v, &subnano);
nsec = FIX2LONG(v);
usec = nsec / 1000;
nsec = nsec % 1000;
- nano = add(LONG2FIX(nsec), subnano);
+ nano = addv(LONG2FIX(nsec), subnano);
p = 0x1UL << 31 | /* 1 */
- TIME_UTC_P(tobj) << 30 | /* 1 */
+ TZMODE_UTC_P(tobj) << 30 | /* 1 */
(year-1900) << 14 | /* 16 */
(vtm.mon-1) << 10 | /* 4 */
vtm.mday << 5 | /* 5 */
@@ -4542,16 +4946,18 @@ time_mdump(VALUE time)
len = 1;
rb_ivar_set(str, id_submicro, rb_str_new(buf, len));
}
- if (!TIME_UTC_P(tobj)) {
- VALUE off = time_utc_offset(time), div, mod;
+ if (!TZMODE_UTC_P(tobj)) {
+ VALUE off = rb_time_utc_offset(time), div, mod;
divmodv(off, INT2FIX(1), &div, &mod);
if (rb_equal(mod, INT2FIX(0)))
off = rb_Integer(div);
rb_ivar_set(str, id_offset, off);
}
- if (tobj->vtm.zone) {
- rb_ivar_set(str, id_zone, time_zone_name(tobj->vtm.zone));
+ zone = tobj->vtm.zone;
+ if (maybe_tzobj_p(zone)) {
+ zone = rb_funcallv(zone, id_name, 0, 0);
}
+ rb_ivar_set(str, id_zone, zone);
return str;
}
@@ -4561,12 +4967,32 @@ time_dump(int argc, VALUE *argv, VALUE time)
{
VALUE str;
- rb_scan_args(argc, argv, "01", 0);
+ rb_check_arity(argc, 0, 1);
str = time_mdump(time);
return str;
}
+static VALUE
+mload_findzone(VALUE arg)
+{
+ VALUE *argp = (VALUE *)arg;
+ VALUE time = argp[0], zone = argp[1];
+ return find_timezone(time, zone);
+}
+
+static VALUE
+mload_zone(VALUE time, VALUE zone)
+{
+ VALUE z, args[2];
+ args[0] = time;
+ args[1] = zone;
+ z = rb_rescue(mload_findzone, (VALUE)args, (VALUE (*)(ANYARGS))NULL, Qnil);
+ if (NIL_P(z)) return rb_fstring(zone);
+ if (RB_TYPE_P(z, T_STRING)) return rb_fstring(z);
+ return z;
+}
+
/* :nodoc: */
static VALUE
time_mload(VALUE time, VALUE str)
@@ -4635,16 +5061,16 @@ time_mload(VALUE time, VALUE str)
vtm.utc_offset = INT2FIX(0);
vtm.yday = vtm.wday = 0;
vtm.isdst = 0;
- vtm.zone = "";
+ vtm.zone = rb_fstring_lit("");
usec = (long)(s & 0xfffff);
nsec = usec * 1000;
- vtm.subsecx = mulquo(LONG2FIX(nsec), INT2FIX(TIME_SCALE), LONG2FIX(1000000000));
+ vtm.subsecx = mulquov(LONG2FIX(nsec), INT2FIX(TIME_SCALE), LONG2FIX(1000000000));
if (nano_num != Qnil) {
- VALUE nano = quo(num_exact(nano_num), num_exact(nano_den));
- vtm.subsecx = add(vtm.subsecx, mulquo(nano, INT2FIX(TIME_SCALE), LONG2FIX(1000000000)));
+ VALUE nano = quov(num_exact(nano_num), num_exact(nano_den));
+ vtm.subsecx = addv(vtm.subsecx, mulquov(nano, INT2FIX(TIME_SCALE), LONG2FIX(1000000000)));
}
else if (submicro != Qnil) { /* for Ruby 1.9.1 compatibility */
unsigned char *ptr;
@@ -4663,27 +5089,26 @@ time_mload(VALUE time, VALUE str)
if (10 <= (digit = ptr[1] >> 4)) goto end_submicro;
nsec += digit;
}
- vtm.subsecx = add(vtm.subsecx, mulquo(LONG2FIX(nsec), INT2FIX(TIME_SCALE), LONG2FIX(1000000000)));
+ vtm.subsecx = addv(vtm.subsecx, mulquov(LONG2FIX(nsec), INT2FIX(TIME_SCALE), LONG2FIX(1000000000)));
end_submicro: ;
}
timew = timegmw(&vtm);
}
GetNewTimeval(time, tobj);
- tobj->gmt = 0;
+ tobj->tzmode = TIME_TZMODE_LOCALTIME;
tobj->tm_got = 0;
tobj->timew = timew;
if (gmt) {
- TIME_SET_UTC(tobj);
+ TZMODE_SET_UTC(tobj);
}
else if (!NIL_P(offset)) {
time_set_utc_offset(time, offset);
time_fixoff(time);
}
if (!NIL_P(zone)) {
- zone = rb_str_new_frozen(zone);
- tobj->vtm.zone = StringValueCStr(zone);
- rb_ivar_set(time, id_zone, zone);
+ zone = mload_zone(time, zone);
+ tobj->vtm.zone = zone;
}
return time;
@@ -4699,6 +5124,261 @@ time_load(VALUE klass, VALUE str)
return time;
}
+/* :nodoc:*/
+/* Document-class: Time::tm
+ *
+ * A container class for timezone conversion.
+ */
+
+/*
+ * call-seq:
+ *
+ * Time::tm.from_time(t) -> tm
+ *
+ * Creates new Time::tm object from a Time object.
+ */
+
+static VALUE
+tm_from_time(VALUE klass, VALUE time)
+{
+ struct time_object *tobj;
+ struct vtm vtm, *v;
+#if TM_IS_TIME
+ VALUE tm;
+ struct time_object *ttm;
+
+ GetTimeval(time, tobj);
+ tm = time_s_alloc(klass);
+ ttm = DATA_PTR(tm);
+ v = &vtm;
+ GMTIMEW(ttm->timew = tobj->timew, v);
+ ttm->timew = wsub(ttm->timew, v->subsecx);
+ v->subsecx = INT2FIX(0);
+ v->zone = Qnil;
+ ttm->vtm = *v;
+ ttm->tm_got = 1;
+ TZMODE_SET_UTC(ttm);
+ return tm;
+#else
+ VALUE args[8];
+ int i = 0;
+
+ GetTimeval(time, tobj);
+ if (tobj->tm_got && TZMODE_UTC_P(tobj))
+ v = &tobj->vtm;
+ else
+ GMTIMEW(tobj->timew, v = &vtm);
+ args[i++] = v->year;
+ args[i++] = INT2FIX(v->mon);
+ args[i++] = INT2FIX(v->mday);
+ args[i++] = INT2FIX(v->hour);
+ args[i++] = INT2FIX(v->min);
+ args[i++] = INT2FIX(v->sec);
+ switch (v->isdst) {
+ case 0: args[i++] = Qfalse; break;
+ case 1: args[i++] = Qtrue; break;
+ default: args[i++] = Qnil; break;
+ }
+ args[i++] = w2v(rb_time_unmagnify(tobj->timew));
+ return rb_class_new_instance(i, args, klass);
+#endif
+}
+
+/*
+ * call-seq:
+ *
+ * Time::tm.new(year, month=nil, day=nil, hour=nil, min=nil, sec=nil, tz=nil) -> tm
+ *
+ * Creates new Time::tm object.
+ */
+
+static VALUE
+tm_initialize(int argc, VALUE *argv, VALUE tm)
+{
+ struct vtm vtm;
+ wideval_t t;
+
+ if (rb_check_arity(argc, 1, 7) > 6) argc = 6;
+ time_arg(argc, argv, &vtm);
+ t = timegmw(&vtm);
+ {
+#if TM_IS_TIME
+ struct time_object *tobj = DATA_PTR(tm);
+ tobj->tzmode = TIME_TZMODE_UTC;
+ tobj->timew = t;
+ tobj->vtm = vtm;
+#else
+ int i = 0;
+ RSTRUCT_SET(tm, i++, INT2FIX(vtm.sec));
+ RSTRUCT_SET(tm, i++, INT2FIX(vtm.min));
+ RSTRUCT_SET(tm, i++, INT2FIX(vtm.hour));
+ RSTRUCT_SET(tm, i++, INT2FIX(vtm.mday));
+ RSTRUCT_SET(tm, i++, INT2FIX(vtm.mon));
+ RSTRUCT_SET(tm, i++, vtm.year);
+ RSTRUCT_SET(tm, i++, w2v(rb_time_unmagnify(t)));
+#endif
+ }
+ return tm;
+}
+
+/* call-seq:
+ *
+ * tm.to_time -> time
+ *
+ * Returns a new Time object.
+ */
+
+static VALUE
+tm_to_time(VALUE tm)
+{
+#if TM_IS_TIME
+ struct time_object *torig = get_timeval(tm);
+ VALUE dup = time_s_alloc(rb_cTime);
+ struct time_object *tobj = DATA_PTR(dup);
+ *tobj = *torig;
+ return dup;
+#else
+ VALUE t[6];
+ const VALUE *p = RSTRUCT_CONST_PTR(tm);
+ int i;
+
+ for (i = 0; i < numberof(t); ++i) {
+ t[i] = p[numberof(t) - 1 - i];
+ }
+ return time_s_mkutc(numberof(t), t, rb_cTime);
+#endif
+}
+
+#if !TM_IS_TIME
+static VALUE
+tm_zero(VALUE tm)
+{
+ return INT2FIX(0);
+}
+
+#define tm_subsec tm_zero
+#define tm_utc_offset tm_zero
+
+static VALUE
+tm_isdst(VALUE tm)
+{
+ return Qfalse;
+}
+
+static VALUE
+tm_to_s(VALUE tm)
+{
+ const VALUE *p = RSTRUCT_CONST_PTR(tm);
+
+ return rb_sprintf("%.4"PRIsVALUE"-%.2"PRIsVALUE"-%.2"PRIsVALUE" "
+ "%.2"PRIsVALUE":%.2"PRIsVALUE":%.2"PRIsVALUE" "
+ "UTC",
+ p[5], p[4], p[3], p[2], p[1], p[0]);
+}
+#else
+static VALUE
+tm_plus(VALUE tm, VALUE offset)
+{
+ return time_add0(rb_obj_class(tm), get_timeval(tm), tm, offset, +1);
+}
+
+static VALUE
+tm_minus(VALUE tm, VALUE offset)
+{
+ return time_add0(rb_obj_class(tm), get_timeval(tm), tm, offset, -1);
+}
+#endif
+
+static VALUE
+Init_tm(VALUE outer, const char *name)
+{
+ /* :stopdoc:*/
+ VALUE tm;
+#if TM_IS_TIME
+ tm = rb_define_class_under(outer, name, rb_cObject);
+ rb_define_alloc_func(tm, time_s_alloc);
+ rb_define_method(tm, "sec", time_sec, 0);
+ rb_define_method(tm, "min", time_min, 0);
+ rb_define_method(tm, "hour", time_hour, 0);
+ rb_define_method(tm, "mday", time_mday, 0);
+ rb_define_method(tm, "day", time_mday, 0);
+ rb_define_method(tm, "mon", time_mon, 0);
+ rb_define_method(tm, "month", time_mon, 0);
+ rb_define_method(tm, "year", time_year, 0);
+ rb_define_method(tm, "isdst", time_isdst, 0);
+ rb_define_method(tm, "dst?", time_isdst, 0);
+ rb_define_method(tm, "zone", time_zone, 0);
+ rb_define_method(tm, "gmtoff", rb_time_utc_offset, 0);
+ rb_define_method(tm, "gmt_offset", rb_time_utc_offset, 0);
+ rb_define_method(tm, "utc_offset", rb_time_utc_offset, 0);
+ rb_define_method(tm, "utc?", time_utc_p, 0);
+ rb_define_method(tm, "gmt?", time_utc_p, 0);
+ rb_define_method(tm, "to_s", time_to_s, 0);
+ rb_define_method(tm, "inspect", time_to_s, 0);
+ rb_define_method(tm, "to_a", time_to_a, 0);
+ rb_define_method(tm, "tv_sec", time_to_i, 0);
+ rb_define_method(tm, "tv_usec", time_usec, 0);
+ rb_define_method(tm, "usec", time_usec, 0);
+ rb_define_method(tm, "tv_nsec", time_nsec, 0);
+ rb_define_method(tm, "nsec", time_nsec, 0);
+ rb_define_method(tm, "subsec", time_subsec, 0);
+ rb_define_method(tm, "to_i", time_to_i, 0);
+ rb_define_method(tm, "to_f", time_to_f, 0);
+ rb_define_method(tm, "to_r", time_to_r, 0);
+ rb_define_method(tm, "+", tm_plus, 1);
+ rb_define_method(tm, "-", tm_minus, 1);
+#else
+ tm = rb_struct_define_under(outer, "tm",
+ "sec", "min", "hour",
+ "mday", "mon", "year",
+ "to_i", NULL);
+ rb_define_method(tm, "subsec", tm_subsec, 0);
+ rb_define_method(tm, "utc_offset", tm_utc_offset, 0);
+ rb_define_method(tm, "to_s", tm_to_s, 0);
+ rb_define_method(tm, "inspect", tm_to_s, 0);
+ rb_define_method(tm, "isdst", tm_isdst, 0);
+ rb_define_method(tm, "dst?", tm_isdst, 0);
+#endif
+ rb_define_method(tm, "initialize", tm_initialize, -1);
+ rb_define_method(tm, "utc", tm_to_time, 0);
+ rb_alias(tm, rb_intern("to_time"), rb_intern("utc"));
+ rb_define_singleton_method(tm, "from_time", tm_from_time, 1);
+ /* :startdoc:*/
+
+ return tm;
+}
+
+VALUE
+rb_time_zone_abbreviation(VALUE zone, VALUE time)
+{
+ VALUE tm, abbr, strftime_args[2];
+
+ abbr = rb_check_string_type(zone);
+ if (!NIL_P(abbr)) return abbr;
+
+ tm = tm_from_time(rb_cTimeTM, time);
+ abbr = rb_check_funcall(zone, rb_intern("abbr"), 1, &tm);
+ if (abbr != Qundef) {
+ goto found;
+ }
+#ifdef SUPPORT_TZINFO_ZONE_ABBREVIATION
+ abbr = rb_check_funcall(zone, rb_intern("period_for_utc"), 1, &tm);
+ if (abbr != Qundef) {
+ abbr = rb_funcallv(abbr, rb_intern("abbreviation"), 0, 0);
+ goto found;
+ }
+#endif
+ strftime_args[0] = rb_fstring_lit("%Z");
+ strftime_args[1] = tm;
+ abbr = rb_check_funcall(zone, rb_intern("strftime"), 2, strftime_args);
+ if (abbr != Qundef) {
+ goto found;
+ }
+ abbr = rb_check_funcall_default(zone, rb_intern("name"), 0, 0, Qnil);
+ found:
+ return rb_obj_as_string(abbr);
+}
+
/*
* Time is an abstraction of dates and times. Time is stored internally as
* the number of seconds with fraction since the _Epoch_, January 1, 1970
@@ -4734,11 +5414,19 @@ time_load(VALUE klass, VALUE str)
* Time.new(2002) #=> 2002-01-01 00:00:00 -0500
* Time.new(2002, 10) #=> 2002-10-01 00:00:00 -0500
* Time.new(2002, 10, 31) #=> 2002-10-31 00:00:00 -0500
+ *
+ * You can pass a UTC offset:
+ *
* Time.new(2002, 10, 31, 2, 2, 2, "+02:00") #=> 2002-10-31 02:02:02 +0200
*
- * You can also use #gm, #local and
- * #utc to infer GMT, local and UTC timezones instead of using
- * the current system setting.
+ * Or a timezone object:
+ *
+ * tz = timezone("Europe/Athens") # Eastern European Time, UTC+2
+ * Time.new(2002, 10, 31, 2, 2, 2, tz) #=> 2002-10-31 02:02:02 +0200
+ *
+ * You can also use Time::gm, Time::local and Time::utc to infer GMT,
+ * local and UTC timezones instead of using the current system
+ * setting.
*
* You can also create a new time using Time::at which takes the number of
* seconds (or fraction of seconds) since the {Unix
@@ -4785,6 +5473,36 @@ time_load(VALUE klass, VALUE str)
* t1 > t2 #=> false
*
* Time.new(2010,10,31).between?(t1, t2) #=> true
+ *
+ * == Timezone argument
+ *
+ * A timezone argument must have +local_to_utc+ and +utc_to_local+
+ * methods, and may have +name+ and +abbr+ methods.
+ *
+ * The +local_to_utc+ method should convert a Time-like object from
+ * the timezone to UTC, and +utc_to_local+ is the opposite. The
+ * result also should be a Time or Time-like object (not necessary to
+ * be the same class). The #zone of the result is just ignored.
+ * Time-like argument to these methods is similar to a Time object in
+ * UTC without sub-second; it has attribute readers for the parts,
+ * e.g. #year, #month, and so on, and epoch time readers, #to_i. The
+ * sub-second attributes are fixed as 0, and #utc_offset, #zone,
+ * #isdst, and their aliases are same as a Time object in UTC.
+ * Also #to_time, #+, and #- methods are defined.
+ *
+ * The +name+ method is used for marshaling. If this method is not
+ * defined on a timezone object, Time objects using that timezone
+ * object can not be dumped by Marshal.
+ *
+ * The +abbr+ method is used by '%Z' in #strftime.
+ *
+ * === Auto conversion to Timezone
+ *
+ * At loading marshaled data, a timezone name will be converted to a timezone
+ * object by +find_timezone+ class method, if the method is defined.
+ *
+ * Similary, that class method will be called when a timezone argument does
+ * not have the necessary methods mentioned above.
*/
void
@@ -4793,18 +5511,30 @@ Init_Time(void)
#undef rb_intern
#define rb_intern(str) rb_intern_const(str)
- id_eq = rb_intern("==");
- id_ne = rb_intern("!=");
id_quo = rb_intern("quo");
id_div = rb_intern("div");
- id_cmp = rb_intern("<=>");
id_divmod = rb_intern("divmod");
- id_mul = rb_intern("*");
id_submicro = rb_intern("submicro");
id_nano_num = rb_intern("nano_num");
id_nano_den = rb_intern("nano_den");
id_offset = rb_intern("offset");
id_zone = rb_intern("zone");
+ id_nanosecond = rb_intern("nanosecond");
+ id_microsecond = rb_intern("microsecond");
+ id_millisecond = rb_intern("millisecond");
+ id_nsec = rb_intern("nsec");
+ id_usec = rb_intern("usec");
+ id_local_to_utc = rb_intern("local_to_utc");
+ id_utc_to_local = rb_intern("utc_to_local");
+ id_year = rb_intern("year");
+ id_mon = rb_intern("mon");
+ id_mday = rb_intern("mday");
+ id_hour = rb_intern("hour");
+ id_min = rb_intern("min");
+ id_sec = rb_intern("sec");
+ id_isdst = rb_intern("isdst");
+ id_name = rb_intern("name");
+ id_find_timezone = rb_intern("find_timezone");
rb_cTime = rb_define_class("Time", rb_cObject);
rb_include_module(rb_cTime, rb_mComparable);
@@ -4858,9 +5588,9 @@ Init_Time(void)
rb_define_method(rb_cTime, "isdst", time_isdst, 0);
rb_define_method(rb_cTime, "dst?", time_isdst, 0);
rb_define_method(rb_cTime, "zone", time_zone, 0);
- rb_define_method(rb_cTime, "gmtoff", time_utc_offset, 0);
- rb_define_method(rb_cTime, "gmt_offset", time_utc_offset, 0);
- rb_define_method(rb_cTime, "utc_offset", time_utc_offset, 0);
+ rb_define_method(rb_cTime, "gmtoff", rb_time_utc_offset, 0);
+ rb_define_method(rb_cTime, "gmt_offset", rb_time_utc_offset, 0);
+ rb_define_method(rb_cTime, "utc_offset", rb_time_utc_offset, 0);
rb_define_method(rb_cTime, "utc?", time_utc_p, 0);
rb_define_method(rb_cTime, "gmt?", time_utc_p, 0);
@@ -4894,4 +5624,6 @@ Init_Time(void)
#ifdef DEBUG_FIND_TIME_NUMGUESS
rb_define_virtual_variable("$find_time_numguess", find_time_numguess_getter, NULL);
#endif
+
+ rb_cTimeTM = Init_tm(rb_cTime, "tm");
}
diff --git a/timev.h b/timev.h
index 3947477630..d9d84b6d1c 100644
--- a/timev.h
+++ b/timev.h
@@ -5,15 +5,15 @@ PACKED_STRUCT_UNALIGNED(struct vtm {
VALUE year; /* 2000 for example. Integer. */
VALUE subsecx; /* 0 <= subsecx < TIME_SCALE. possibly Rational. */
VALUE utc_offset; /* -3600 as -01:00 for example. possibly Rational. */
- const char *zone; /* "JST", "EST", "EDT", etc. */
- uint16_t yday:9; /* 1..366 */
- uint8_t mon:4; /* 1..12 */
- uint8_t mday:5; /* 1..31 */
- uint8_t hour:5; /* 0..23 */
- uint8_t min:6; /* 0..59 */
- uint8_t sec:6; /* 0..60 */
- uint8_t wday:3; /* 0:Sunday, 1:Monday, ..., 6:Saturday 7:init */
- uint8_t isdst:2; /* 0:StandardTime 1:DayLightSavingTime 3:init */
+ VALUE zone; /* "JST", "EST", "EDT", etc. as String */
+ unsigned int yday:9; /* 1..366 */
+ unsigned int mon:4; /* 1..12 */
+ unsigned int mday:5; /* 1..31 */
+ unsigned int hour:5; /* 0..23 */
+ unsigned int min:6; /* 0..59 */
+ unsigned int sec:6; /* 0..60 */
+ unsigned int wday:3; /* 0:Sunday, 1:Monday, ..., 6:Saturday 7:init */
+ unsigned int isdst:2; /* 0:StandardTime 1:DayLightSavingTime 3:init */
});
#define TIME_SCALE 1000000000
@@ -39,4 +39,15 @@ typedef unsigned LONG_LONG unsigned_time_t;
# error cannot find integer type which size is same as time_t.
#endif
+/* strftime.c */
+#ifdef RUBY_ENCODING_H
+VALUE rb_strftime_timespec(const char *format, size_t format_len, rb_encoding *enc,
+ VALUE time, const struct vtm *vtm, struct timespec *ts, int gmt);
+VALUE rb_strftime(const char *format, size_t format_len, rb_encoding *enc,
+ VALUE time, const struct vtm *vtm, VALUE timev, int gmt);
+#endif
+
+/* time.c */
+VALUE rb_time_zone_abbreviation(VALUE zone, VALUE time);
+
#endif
diff --git a/tool/bisect.sh b/tool/bisect.sh
index d486c9cfc6..0a2d6b0ba9 100755
--- a/tool/bisect.sh
+++ b/tool/bisect.sh
@@ -21,25 +21,30 @@ case $1 in
exec git bisect run "$path" "run-$1"
;;
run-miniruby )
- $MAKE srcs || exit 125
- cd "${0%/*}" || exit 125 # assume a copy of this script is in builddir
- $MAKE Makefile || exit 125
- $MAKE mini || exit 125
- $MAKE run || exit 1
+ prep=mini
+ run=run
;;
run-ruby )
- $MAKE srcs || exit 125
- cd "${0%/*}" || exit 125 # assume a copy of this script is in builddir
- $MAKE Makefile || exit 125
- $MAKE program || exit 125
- $MAKE runruby || exit 1
+ prep=program
+ run=runruby
;;
"" )
- echo foo bar
+ echo missing command 1>&2
+ exit 1
;;
* )
echo unknown command "'$1'" 1>&2
exit 1
;;
esac
-exit 0
+
+case "$0" in
+*/*)
+ # assume a copy of this script is in builddir
+ cd `echo "$0" | sed 's:\(.*\)/.*:\1:'` || exit 125
+ ;;
+esac
+for target in srcs Makefile $prep; do
+ $MAKE $target || exit 125
+done
+exec $MAKE $run
diff --git a/tool/checksum.rb b/tool/checksum.rb
index 0de54a314d..3b41aedcfc 100755
--- a/tool/checksum.rb
+++ b/tool/checksum.rb
@@ -67,6 +67,6 @@ class Checksum
def self.update(argv)
k = new(VPath.new)
k.source, k.target, *argv = k.def_options.parse(*argv)
- k.update {|k| yield(k, *argv)}
+ k.update {|_| yield(_, *argv)}
end
end
diff --git a/tool/colorize.rb b/tool/colorize.rb
new file mode 100644
index 0000000000..1cbbeef543
--- /dev/null
+++ b/tool/colorize.rb
@@ -0,0 +1,41 @@
+# frozen-string-literal: true
+
+class Colorize
+ def initialize(color = nil)
+ @colors = @reset = nil
+ if color or (color == nil && STDOUT.tty?)
+ if (/\A\e\[.*m\z/ =~ IO.popen("tput smso", "r", err: IO::NULL, &:read) rescue nil)
+ @beg = "\e["
+ @colors = (colors = ENV['TEST_COLORS']) ? Hash[colors.scan(/(\w+)=([^:\n]*)/)] : {}
+ @reset = "#{@beg}m"
+ end
+ end
+ self
+ end
+
+ DEFAULTS = {
+ "pass"=>"32", "fail"=>"31;1", "skip"=>"33;1",
+ "black"=>"30", "red"=>"31", "green"=>"32", "yellow"=>"33",
+ "blue"=>"34", "magenta"=>"35", "cyan"=>"36", "white"=>"37",
+ }
+
+ def decorate(str, name)
+ if @colors and color = (@colors[name] || DEFAULTS[name])
+ "#{@beg}#{color}m#{str}#{@reset}"
+ else
+ str
+ end
+ end
+
+ DEFAULTS.each_key do |name|
+ define_method(name) {|str|
+ decorate(str, name)
+ }
+ end
+end
+
+if $0 == __FILE__
+ colorize = Colorize.new
+ col = ARGV.shift
+ ARGV.each {|str| puts colorize.decorate(str, col)}
+end
diff --git a/tool/darwin-cc b/tool/darwin-cc
new file mode 100755
index 0000000000..6eee96e435
--- /dev/null
+++ b/tool/darwin-cc
@@ -0,0 +1,6 @@
+#!/bin/bash
+exec 2> >(exec grep -v \
+ -e '^ld: warning: The [a-z0-9_][a-z0-9_]* architecture is deprecated for macOS' \
+ -e '^ld: warning: text-based stub file /System/Library/Frameworks/' \
+ >&2)
+exec "$@"
diff --git a/tool/downloader.rb b/tool/downloader.rb
index 6c34f73ca2..e884056d47 100644
--- a/tool/downloader.rb
+++ b/tool/downloader.rb
@@ -1,7 +1,9 @@
# Used by configure and make to download or update mirrored Ruby and GCC
# files. This will use HTTPS if possible, falling back to HTTP.
+require 'fileutils'
require 'open-uri'
+require 'pathname'
begin
require 'net/https'
rescue LoadError
@@ -9,8 +11,8 @@ rescue LoadError
else
https = 'https'
- # open-uri of ruby 2.2.0 accept an array of PEMs as ssl_ca_cert, but old
- # versions are not. so, patching OpenSSL::X509::Store#add_file instead.
+ # open-uri of ruby 2.2.0 accepts an array of PEMs as ssl_ca_cert, but old
+ # versions do not. so, patching OpenSSL::X509::Store#add_file instead.
class OpenSSL::X509::Store
alias orig_add_file add_file
def add_file(pems)
@@ -59,20 +61,44 @@ class Downloader
class RubyGems < self
def self.download(name, dir = nil, since = true, options = {})
require 'rubygems'
- verify = options.delete(:verify) {Gem::VERSION >= "2.4."}
+ options = options.dup
options[:ssl_ca_cert] = Dir.glob(File.expand_path("../lib/rubygems/ssl_certs/**/*.pem", File.dirname(__FILE__)))
- file = under(dir, name)
- super("https://rubygems.org/downloads/#{name}", file, nil, since, options) or
- return false
- return true unless verify
+ super("https://rubygems.org/downloads/#{name}", name, dir, since, options)
end
end
Gems = RubyGems
class Unicode < self
- def self.download(name, *rest)
- super("http://www.unicode.org/Public/#{name}", name, *rest)
+ INDEX = {} # cache index file information across files in the same directory
+ UNICODE_PUBLIC = "http://www.unicode.org/Public/"
+
+ def self.download(name, dir = nil, since = true, options = {})
+ options = options.dup
+ unicode_beta = options.delete(:unicode_beta)
+ name_dir_part = name.sub(/[^\/]+$/, '')
+ if unicode_beta == 'YES'
+ if INDEX.size == 0
+ index_options = options.dup
+ index_options[:cache_save] = false # TODO: make sure caching really doesn't work for index file
+ index_file = super(UNICODE_PUBLIC+name_dir_part, "#{name_dir_part}index.html", dir, true, index_options)
+ INDEX[:index] = IO.read index_file
+ end
+ file_base = File.basename(name, '.txt')
+ return if file_base == '.' # Use pre-generated headers and tables
+ beta_name = INDEX[:index][/#{Regexp.quote(file_base)}(-[0-9.]+d\d+)?\.txt/]
+ # make sure we always check for new versions of files,
+ # because they can easily change in the beta period
+ super(UNICODE_PUBLIC+name_dir_part+beta_name, name, dir, true, options)
+ else
+ index_file = Pathname.new(under(dir, name_dir_part+'index.html'))
+ if index_file.exist?
+ raise "Although Unicode is not in beta, file #{index_file} exists. " +
+ "Remove all files in this directory and in .downloaded-cache/ " +
+ "because they may be leftovers from the beta period."
+ end
+ super(UNICODE_PUBLIC+name, name, dir, since, options)
+ end
end
end
@@ -122,74 +148,177 @@ class Downloader
# download 'http://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt',
# 'UnicodeData.txt', 'enc/unicode/data'
def self.download(url, name, dir = nil, since = true, options = {})
- options.delete(:verify)
- file = under(dir, name)
- if since.nil? and File.exist?(file)
+ options = options.dup
+ url = URI(url)
+ dryrun = options.delete(:dryrun)
+ options.delete(:unicode_beta) # just to be on the safe side for gems and gcc
+
+ if name
+ file = Pathname.new(under(dir, name))
+ else
+ name = File.basename(url.path)
+ end
+ cache_save = options.delete(:cache_save) {
+ ENV["CACHE_SAVE"] != "no"
+ }
+ cache = cache_file(url, name, options.delete(:cache_dir))
+ file ||= cache
+ if since.nil? and file.exist?
if $VERBOSE
- $stdout.puts "#{name} already exists"
+ $stdout.puts "#{file} already exists"
$stdout.flush
end
- return true
+ if cache_save
+ save_cache(cache, file, name)
+ end
+ return file.to_path
+ end
+ if dryrun
+ puts "Download #{url} into #{file}"
+ return
+ end
+ if link_cache(cache, file, name, $VERBOSE)
+ return file.to_path
end
- if !https? and url.start_with?("https:")
+ if !https? and URI::HTTPS === url
warn "*** using http instead of https ***"
- url = url.sub(/\Ahttps/, 'http')
+ url.scheme = 'http'
+ url = URI(url.to_s)
end
- url = URI(url)
if $VERBOSE
$stdout.print "downloading #{name} ... "
$stdout.flush
end
begin
- data = url.read(options.merge(http_options(file, since.nil? ? true : since)))
+ data = with_retry(6) do
+ url.read(options.merge(http_options(file, since.nil? ? true : since)))
+ end
rescue OpenURI::HTTPError => http_error
if http_error.message =~ /^304 / # 304 Not Modified
if $VERBOSE
$stdout.puts "#{name} not modified"
$stdout.flush
end
- return true
+ return file.to_path
end
raise
rescue Timeout::Error
- if since.nil? and File.exist?(file)
+ if since.nil? and file.exist?
puts "Request for #{url} timed out, using old version."
- return true
+ return file.to_path
end
raise
rescue SocketError
- if since.nil? and File.exist?(file)
+ if since.nil? and file.exist?
puts "No network connection, unable to download #{url}, using old version."
- return true
+ return file.to_path
end
raise
end
mtime = nil
- open(file, "wb", 0600) do |f|
+ dest = (cache_save && cache && !cache.exist? ? cache : file)
+ dest.parent.mkpath
+ dest.open("wb", 0600) do |f|
f.write(data)
f.chmod(mode_for(data))
mtime = data.meta["last-modified"]
end
if mtime
mtime = Time.httpdate(mtime)
- File.utime(mtime, mtime, file)
+ dest.utime(mtime, mtime)
end
if $VERBOSE
$stdout.puts "done"
$stdout.flush
end
- true
+ if dest.eql?(cache)
+ link_cache(cache, file, name)
+ elsif cache_save
+ save_cache(cache, file, name)
+ end
+ return file.to_path
rescue => e
- raise "failed to download #{name}\n#{e.message}: #{url}"
- end
-
- def self.verify(file)
- true
+ raise "failed to download #{name}\n#{e.class}: #{e.message}: #{url}"
end
def self.under(dir, name)
dir ? File.join(dir, File.basename(name)) : name
end
+
+ def self.cache_file(url, name, cache_dir = nil)
+ case cache_dir
+ when false
+ return nil
+ when nil
+ cache_dir = ENV['CACHE_DIR']
+ if !cache_dir or cache_dir.empty?
+ cache_dir = ".downloaded-cache"
+ end
+ end
+ Pathname.new(cache_dir) + (name || File.basename(URI(url).path))
+ end
+
+ def self.link_cache(cache, file, name, verbose = false)
+ return false unless cache and cache.exist?
+ return true if cache.eql?(file)
+ if /cygwin/ !~ RUBY_PLATFORM or /winsymlink:nativestrict/ =~ ENV['CYGWIN']
+ begin
+ file.make_symlink(cache.relative_path_from(file.parent))
+ rescue SystemCallError
+ else
+ if verbose
+ $stdout.puts "made symlink #{name} to #{cache}"
+ $stdout.flush
+ end
+ return true
+ end
+ end
+ begin
+ file.make_link(cache)
+ rescue SystemCallError
+ else
+ if verbose
+ $stdout.puts "made link #{name} to #{cache}"
+ $stdout.flush
+ end
+ return true
+ end
+ end
+
+ def self.save_cache(cache, file, name)
+ return unless cache or cache.eql?(file)
+ begin
+ st = cache.stat
+ rescue
+ begin
+ file.rename(cache)
+ rescue
+ return
+ end
+ else
+ return unless st.mtime > file.lstat.mtime
+ file.unlink
+ end
+ link_cache(cache, file, name)
+ end
+
+ def self.with_retry(max_times, &block)
+ times = 0
+ begin
+ block.call
+ rescue Errno::ETIMEDOUT, SocketError, OpenURI::HTTPError, Net::ReadTimeout, Net::OpenTimeout => e
+ raise if e.is_a?(OpenURI::HTTPError) && e.message !~ /^50[023] / # retry only 500, 502, 503 for http error
+ times += 1
+ if times <= max_times
+ $stderr.puts "retrying #{e.class} (#{e.message}) after #{times ** 2} seconds..."
+ sleep(times ** 2)
+ retry
+ else
+ raise
+ end
+ end
+ end
+ private_class_method :with_retry
end
Downloader.https = https.freeze
@@ -211,8 +340,16 @@ if $0 == __FILE__
since = nil
when '-a'
since = false
- when '-V'
- options[:verify] = true
+ when '-n', '--dryrun'
+ options[:dryrun] = true
+ when '--cache-dir'
+ options[:cache_dir] = ARGV[1]
+ ARGV.shift
+ when '--unicode-beta'
+ options[:unicode_beta] = ARGV[1]
+ ARGV.shift
+ when /\A--cache-dir=(.*)/m
+ options[:cache_dir] = $1
when /\A-/
abort "#{$0}: unknown option #{ARGV[0]}"
else
@@ -228,8 +365,23 @@ if $0 == __FILE__
dl = Downloader.const_get(dl)
ARGV.shift
ARGV.each do |name|
- name = "#{prefix}/#{File.basename(name)}" if prefix
- dl.download(name, destdir, since, options)
+ dir = destdir
+ if prefix
+ name = name.sub(/\A\.\//, '')
+ destdir2 = destdir.sub(/\A\.\//, '')
+ if name.start_with?(destdir2+"/")
+ name = name[(destdir2.size+1)..-1]
+ if (dir = File.dirname(name)) == '.'
+ dir = destdir
+ else
+ dir = File.join(destdir, dir)
+ end
+ else
+ name = File.basename(name)
+ end
+ name = "#{prefix}/#{name}"
+ end
+ dl.download(name, dir, since, options)
end
else
abort "usage: #{$0} url name" unless ARGV.size == 2
diff --git a/tool/enc-emoji-citrus-gen.rb b/tool/enc-emoji-citrus-gen.rb
index 5037cbde1e..94864e5fa0 100644
--- a/tool/enc-emoji-citrus-gen.rb
+++ b/tool/enc-emoji-citrus-gen.rb
@@ -93,7 +93,7 @@ def generate_from_ucs(params, pairs)
end
def make_pairs(code_map)
- pairs = code_map.inject([]) {|acc, (range, ch)|
+ code_map.inject([]) {|acc, (range, ch)|
acc += range.map{|uni| pair = [uni, Integer(ch)]; ch = ch.succ; next pair }
}
end
diff --git a/tool/enc-unicode.rb b/tool/enc-unicode.rb
index 83bbe352e1..f64cecb881 100755
--- a/tool/enc-unicode.rb
+++ b/tool/enc-unicode.rb
@@ -14,13 +14,17 @@ if ARGV[0] == "--header"
header = true
ARGV.shift
end
-unless ARGV.size == 1
- abort "Usage: #{$0} data_directory"
+unless ARGV.size == 2
+ abort "Usage: #{$0} data_directory emoji_data_directory"
end
-$unicode_version = File.basename(ARGV[0])[/\A[.\d]+\z/]
+pat = /(?:\A|\/)([.\d]+)\z/
+$versions = {
+ :Unicode => ARGV[0][pat, 1],
+ :Emoji => ARGV[1][pat, 1],
+}
-POSIX_NAMES = %w[NEWLINE Alpha Blank Cntrl Digit Graph Lower Print Punct Space Upper XDigit Word Alnum ASCII]
+POSIX_NAMES = %w[NEWLINE Alpha Blank Cntrl Digit Graph Lower Print XPosixPunct Space Upper XDigit Word Alnum ASCII Punct]
def pair_codepoints(codepoints)
@@ -115,6 +119,7 @@ def define_posix_props(data)
data['Upper'] = data['Uppercase']
data['Lower'] = data['Lowercase']
data['Punct'] = data['Punctuation']
+ data['XPosixPunct'] = data['Punctuation'] + [0x24, 0x2b, 0x3c, 0x3d, 0x3e, 0x5e, 0x60, 0x7c, 0x7e]
data['Digit'] = data['Decimal_Number']
data['XDigit'] = (0x0030..0x0039).to_a + (0x0041..0x0046).to_a +
(0x0061..0x0066).to_a
@@ -132,19 +137,20 @@ def parse_scripts(data, categories)
files = [
{:fn => 'DerivedCoreProperties.txt', :title => 'Derived Property'},
{:fn => 'Scripts.txt', :title => 'Script'},
- {:fn => 'PropList.txt', :title => 'Binary Property'}
+ {:fn => 'PropList.txt', :title => 'Binary Property'},
+ {:fn => 'emoji-data.txt', :title => 'Emoji'}
]
current = nil
cps = []
names = {}
files.each do |file|
data_foreach(file[:fn]) do |line|
- if /^# Total code points: / =~ line
+ if /^# Total (?:code points|elements): / =~ line
data[current] = cps
categories[current] = file[:title]
(names[file[:title]] ||= []) << current
cps = []
- elsif /^([0-9a-fA-F]+)(?:..([0-9a-fA-F]+))?\s*;\s*(\w+)/ =~ line
+ elsif /^([0-9a-fA-F]+)(?:\.\.([0-9a-fA-F]+))?\s*;\s*(\w+)/ =~ line
current = $3
$2 ? cps.concat(($1.to_i(16)..$2.to_i(16)).to_a) : cps.push($1.to_i(16))
end
@@ -199,7 +205,7 @@ def parse_age(data)
ages << current
last_constname = constname
cps = []
- elsif /^([0-9a-fA-F]+)(?:..([0-9a-fA-F]+))?\s*;\s*(\d+\.\d+)/ =~ line
+ elsif /^([0-9a-fA-F]+)(?:\.\.([0-9a-fA-F]+))?\s*;\s*(\d+\.\d+)/ =~ line
current = $3
$2 ? cps.concat(($1.to_i(16)..$2.to_i(16)).to_a) : cps.push($1.to_i(16))
end
@@ -207,9 +213,26 @@ def parse_age(data)
ages
end
-def parse_block(data)
+def parse_GraphemeBreakProperty(data)
current = nil
- last_constname = nil
+ cps = []
+ ages = []
+ data_foreach('auxiliary/GraphemeBreakProperty.txt') do |line|
+ if /^# Total code points: / =~ line
+ constname = constantize_Grapheme_Cluster_Break(current)
+ data[constname] = cps
+ make_const(constname, cps, "Grapheme_Cluster_Break=#{current}")
+ ages << current
+ cps = []
+ elsif /^([0-9a-fA-F]+)(?:\.\.([0-9a-fA-F]+))?\s*;\s*(\w+)/ =~ line
+ current = $3
+ $2 ? cps.concat(($1.to_i(16)..$2.to_i(16)).to_a) : cps.push($1.to_i(16))
+ end
+ end
+ ages
+end
+
+def parse_block(data)
cps = []
blocks = []
data_foreach('Blocks.txt') do |line|
@@ -242,7 +265,11 @@ $const_cache = {}
# given property, group of paired codepoints, and a human-friendly name for
# the group
def make_const(prop, data, name)
- puts "\n/* '#{prop}': #{name} */"
+ if name.empty?
+ puts "\n/* '#{prop}' */"
+ else
+ puts "\n/* '#{prop}': #{name} */"
+ end
if origprop = $const_cache.key(data)
puts "#define CR_#{prop} CR_#{origprop}"
else
@@ -269,27 +296,45 @@ def constantize_agename(name)
"Age_#{name.sub(/\./, '_')}"
end
+def constantize_Grapheme_Cluster_Break(name)
+ "Grapheme_Cluster_Break_#{name}"
+end
+
def constantize_blockname(name)
"In_#{name.gsub(/\W/, '_')}"
end
def get_file(name)
- File.join(ARGV[0], name)
+ File.join(ARGV[name.start_with?("emoji-") ? 1 : 0], name)
end
def data_foreach(name, &block)
fn = get_file(name)
warn "Reading #{name}"
- pat = /^# #{name.sub(/\./, '-([\\d.]+)\\.')}/
+ if /^emoji-/ =~ name
+ sep = ""
+ pat = /^# #{Regexp.quote(File.basename(name))}.*^# Version: ([\d.]+)/m
+ type = :Emoji
+ else
+ sep = "\n"
+ pat = /^# #{File.basename(name).sub(/\./, '-([\\d.]+)\\.')}/
+ type = :Unicode
+ end
File.open(fn, 'rb') do |f|
- line = f.gets
- unless pat =~ line
- raise ArgumentError, "#{name}: no Unicode version"
+ line = f.gets(sep)
+ unless version = line[pat, 1]
+ raise ArgumentError, <<-ERROR
+#{name}: no #{type} version
+#{line.gsub(/^/, '> ')}
+ ERROR
end
- if !$unicode_version
- $unicode_version = $1
- elsif $unicode_version != $1
- raise ArgumentError, "#{name}: Unicode version mismatch: #$1"
+ if !(v = $versions[type])
+ $versions[type] = version
+ elsif v != version
+ raise ArgumentError, <<-ERROR
+#{name}: #{type} version mismatch: #{version} to #{v}
+#{line.gsub(/^/, '> ')}
+ ERROR
end
f.each(&block)
end
@@ -309,22 +354,29 @@ class Unifdef
def ifdef(sym)
if @kwdonly
@stdout.puts "#ifdef #{sym}"
- return
+ else
+ @stack << @top
+ @top << tmp = [sym]
+ @top = tmp
+ end
+ if block_given?
+ begin
+ return yield
+ ensure
+ endif(sym)
+ end
end
- @stack << @top
- @top << tmp = [sym]
- @top = tmp
end
def endif(sym)
if @kwdonly
@stdout.puts "#endif /* #{sym} */"
- return
- end
- unless sym == @top[0]
- restore
- raise ArgumentError, "#{sym} unmatch to #{@top[0]}"
+ else
+ unless sym == @top[0]
+ restore
+ raise ArgumentError, "#{sym} unmatch to #{@top[0]}"
+ end
+ @top = @stack.pop
end
- @top = @stack.pop
end
def show(dest, *syms)
_show(dest, @output, syms)
@@ -359,50 +411,64 @@ output = Unifdef.new($stdout)
output.kwdonly = !header
puts '%{'
-puts '#define long size_t'
props, data = parse_unicode_data(get_file('UnicodeData.txt'))
categories = {}
props.concat parse_scripts(data, categories)
aliases = parse_aliases(data)
+ages = blocks = graphemeBreaks = nil
define_posix_props(data)
POSIX_NAMES.each do |name|
- make_const(name, data[name], "[[:#{name}:]]")
+ if name == 'XPosixPunct'
+ make_const(name, data[name], "[[:Punct:]]")
+ elsif name == 'Punct'
+ make_const(name, data[name], "")
+ else
+ make_const(name, data[name], "[[:#{name}:]]")
+ end
end
-output.ifdef :USE_UNICODE_PROPERTIES
-props.each do |name|
- category = categories[name] ||
- case name.size
- when 1 then 'Major Category'
- when 2 then 'General Category'
- else '-'
- end
- make_const(name, data[name], category)
+output.ifdef :USE_UNICODE_PROPERTIES do
+ props.each do |name|
+ category = categories[name] ||
+ case name.size
+ when 1 then 'Major Category'
+ when 2 then 'General Category'
+ else '-'
+ end
+ make_const(name, data[name], category)
+ end
+ output.ifdef :USE_UNICODE_AGE_PROPERTIES do
+ ages = parse_age(data)
+ end
+ graphemeBreaks = parse_GraphemeBreakProperty(data)
+ blocks = parse_block(data)
end
-output.ifdef :USE_UNICODE_AGE_PROPERTIES
-ages = parse_age(data)
-output.endif :USE_UNICODE_AGE_PROPERTIES
-blocks = parse_block(data)
-output.endif :USE_UNICODE_PROPERTIES
puts(<<'__HEREDOC')
static const OnigCodePoint* const CodeRanges[] = {
__HEREDOC
POSIX_NAMES.each{|name|puts" CR_#{name},"}
-output.ifdef :USE_UNICODE_PROPERTIES
-props.each{|name| puts" CR_#{name},"}
-output.ifdef :USE_UNICODE_AGE_PROPERTIES
-ages.each{|name| puts" CR_#{constantize_agename(name)},"}
-output.endif :USE_UNICODE_AGE_PROPERTIES
-blocks.each{|name|puts" CR_#{name},"}
-output.endif :USE_UNICODE_PROPERTIES
+output.ifdef :USE_UNICODE_PROPERTIES do
+ props.each{|name| puts" CR_#{name},"}
+ output.ifdef :USE_UNICODE_AGE_PROPERTIES do
+ ages.each{|name| puts" CR_#{constantize_agename(name)},"}
+ end
+ graphemeBreaks.each{|name| puts" CR_#{constantize_Grapheme_Cluster_Break(name)},"}
+ blocks.each{|name|puts" CR_#{name},"}
+end
puts(<<'__HEREDOC')
};
struct uniname2ctype_struct {
- int name, ctype;
+ short name;
+ unsigned short ctype;
};
+#define uniname2ctype_offset(str) offsetof(struct uniname2ctype_pool_t, uniname2ctype_pool_##str)
-static const struct uniname2ctype_struct *uniname2ctype_p(const char *, unsigned int);
+static const struct uniname2ctype_struct *uniname2ctype_p(
+#if !(/*ANSI*/+0) /* if ANSI, old style not to conflict with generated prototype */
+ const char *, unsigned int
+#endif
+);
%}
struct uniname2ctype_struct;
%%
@@ -417,33 +483,39 @@ POSIX_NAMES.each do |name|
name_to_index[name] = i
puts"%-40s %3d" % [name + ',', i]
end
-output.ifdef :USE_UNICODE_PROPERTIES
-props.each do |name|
- i += 1
- name = normalize_propname(name)
- name_to_index[name] = i
- puts "%-40s %3d" % [name + ',', i]
-end
-aliases.each_pair do |k, v|
- next if name_to_index[k]
- next unless v = name_to_index[v]
- puts "%-40s %3d" % [k + ',', v]
-end
-output.ifdef :USE_UNICODE_AGE_PROPERTIES
-ages.each do |name|
- i += 1
- name = "age=#{name}"
- name_to_index[name] = i
- puts "%-40s %3d" % [name + ',', i]
-end
-output.endif :USE_UNICODE_AGE_PROPERTIES
-blocks.each do |name|
- i += 1
- name = normalize_propname(name)
- name_to_index[name] = i
- puts "%-40s %3d" % [name + ',', i]
+output.ifdef :USE_UNICODE_PROPERTIES do
+ props.each do |name|
+ i += 1
+ name = normalize_propname(name)
+ name_to_index[name] = i
+ puts "%-40s %3d" % [name + ',', i]
+ end
+ aliases.each_pair do |k, v|
+ next if name_to_index[k]
+ next unless v = name_to_index[v]
+ puts "%-40s %3d" % [k + ',', v]
+ end
+ output.ifdef :USE_UNICODE_AGE_PROPERTIES do
+ ages.each do |name|
+ i += 1
+ name = "age=#{name}"
+ name_to_index[name] = i
+ puts "%-40s %3d" % [name + ',', i]
+ end
+ end
+ graphemeBreaks.each do |name|
+ i += 1
+ name = "graphemeclusterbreak=#{name.delete('_').downcase}"
+ name_to_index[name] = i
+ puts "%-40s %3d" % [name + ',', i]
+ end
+ blocks.each do |name|
+ i += 1
+ name = normalize_propname(name)
+ name_to_index[name] = i
+ puts "%-40s %3d" % [name + ',', i]
+ end
end
-output.endif :USE_UNICODE_PROPERTIES
puts(<<'__HEREDOC')
%%
static int
@@ -454,17 +526,20 @@ uniname2ctype(const UChar *name, unsigned int len)
return -1;
}
__HEREDOC
-versions = $unicode_version.scan(/\d+/)
-print("#if defined ONIG_UNICODE_VERSION_STRING && !( \\\n")
-%w[MAJOR MINOR TEENY].zip(versions) do |n, v|
- print(" ONIG_UNICODE_VERSION_#{n} == #{v} && \\\n")
-end
-print(" 1)\n")
-print("# error ONIG_UNICODE_VERSION_STRING mismatch\n")
-print("#endif\n")
-print("#define ONIG_UNICODE_VERSION_STRING #{$unicode_version.dump}\n")
-%w[MAJOR MINOR TEENY].zip(versions) do |n, v|
- print("#define ONIG_UNICODE_VERSION_#{n} #{v}\n")
+$versions.each do |type, ver|
+ name = type == :Unicode ? "ONIG_UNICODE_VERSION" : "ONIG_UNICODE_EMOJI_VERSION"
+ versions = ver.scan(/\d+/)
+ print("#if defined #{name}_STRING && !( \\\n")
+ versions.zip(%w[MAJOR MINOR TEENY]) do |v, n|
+ print(" #{name}_#{n} == #{v} && \\\n")
+ end
+ print(" 1)\n")
+ print("# error #{name}_STRING mismatch\n")
+ print("#endif\n")
+ print("#define #{name}_STRING #{ver.dump}\n")
+ versions.zip(%w[MAJOR MINOR TEENY]) do |v, n|
+ print("#define #{name}_#{n} #{v}\n")
+ end
end
output.restore
@@ -481,7 +556,22 @@ if header
IO.popen([*NAME2CTYPE, out: tmp], "w") {|f| output.show(f, *syms)}
end while syms.pop
fds.each(&:close)
+ ff = nil
IO.popen(%W[diff -DUSE_UNICODE_AGE_PROPERTIES #{fds[1].path} #{fds[0].path}], "r") {|age|
- system(* %W[diff -DUSE_UNICODE_PROPERTIES #{fds[2].path} -], in: age)
+ IO.popen(%W[diff -DUSE_UNICODE_PROPERTIES #{fds[2].path} -], "r", in: age) {|f|
+ ansi = false
+ f.each {|line|
+ if /ANSI-C code produced by gperf/ =~ line
+ ansi = true
+ end
+ line.sub!(/\/\*ANSI\*\//, '1') if ansi
+ line.gsub!(/\(int\)\((?:long|size_t)\)&\(\(struct uniname2ctype_pool_t \*\)0\)->uniname2ctype_pool_(str\d+),\s+/,
+ 'uniname2ctype_offset(\1), ')
+ if ff = (!ff ? /^(uniname2ctype_hash) /=~line : /^\}/!~line) # no line can match both, exclusive flip-flop
+ line.sub!(/^( *(?:register\s+)?(.*\S)\s+hval\s*=\s*)(?=len;)/, '\1(\2)')
+ end
+ puts line
+ }
+ }
}
end
diff --git a/tool/eval.rb b/tool/eval.rb
index 981f72733b..9153573e6e 100644
--- a/tool/eval.rb
+++ b/tool/eval.rb
@@ -107,7 +107,6 @@ def calc_each data
end
def calc_stat stats
- stat = []
stats[0].each_with_index{|e, idx|
bm = e[0]
vals = stats.map{|st|
@@ -134,8 +133,7 @@ def stat
}
# pp total
total[0].each_with_index{|e, idx|
- bm = e[0]
- # print "#{bm}\t"
+ # print "#{e[0]}\t"
total.each{|st|
print st[idx][1], "\t"
}
diff --git a/tool/expand-config.rb b/tool/expand-config.rb
index d34f29f586..81ffa6cb98 100755
--- a/tool/expand-config.rb
+++ b/tool/expand-config.rb
@@ -15,8 +15,6 @@ while /\A(\w+)=(.*)/ =~ ARGV[0]
ARGV.shift
end
-re = /@(#{config.keys.map {|k| Regexp.quote(k)}.join('|')})@/
-
if $output
output = open($output, "wb", $mode &&= $mode.oct)
output.chmod($mode) if $mode
diff --git a/tool/extlibs.rb b/tool/extlibs.rb
index ee4fd72a16..a840724794 100755
--- a/tool/extlibs.rb
+++ b/tool/extlibs.rb
@@ -3,155 +3,184 @@
# Used to download, extract and patch extension libraries (extlibs)
# for Ruby. See common.mk for Ruby's usage.
-require 'fileutils'
require 'digest'
require_relative 'downloader'
-def do_download(url, base, cache_dir)
- Downloader.download(url, base, cache_dir, nil)
-end
+class ExtLibs
+ def cache_file(url, cache_dir)
+ Downloader.cache_file(url, nil, :cache_dir => cache_dir)
+ end
-def do_checksum(cache, chksums)
- chksums.each do |sum|
- name, sum = sum.split(/:/)
- if $VERBOSE
- $stdout.print "checking #{name} of #{cache} ..."
- $stdout.flush
- end
- hd = Digest(name.upcase).file(cache).hexdigest
- if hd == sum
+ def do_download(url, cache_dir)
+ Downloader.download(url, nil, nil, nil, :cache_dir => cache_dir)
+ end
+
+ def do_checksum(cache, chksums)
+ chksums.each do |sum|
+ name, sum = sum.split(/:/)
if $VERBOSE
- $stdout.puts " OK"
+ $stdout.print "checking #{name} of #{cache} ..."
$stdout.flush
end
- else
- if $VERBOSE
- $stdout.puts " NG"
- $stdout.flush
+ hd = Digest(name.upcase).file(cache).hexdigest
+ if hd == sum
+ if $VERBOSE
+ $stdout.puts " OK"
+ $stdout.flush
+ end
+ else
+ if $VERBOSE
+ $stdout.puts " NG"
+ $stdout.flush
+ end
+ raise "checksum mismatch: #{cache}, #{name}:#{hd}, expected #{sum}"
end
- raise "checksum mismatch: #{cache}, #{name}:#{hd}, expected #{sum}"
end
end
-end
-def do_extract(cache, dir)
- if $VERBOSE
- $stdout.puts "extracting #{cache} into #{dir}"
- $stdout.flush
- end
- ext = File.extname(cache)
- case ext
- when '.gz', '.tgz'
- f = IO.popen(["gzip", "-dc", cache])
- cache = cache.chomp('.gz')
- when '.bz2', '.tbz'
- f = IO.popen(["bzip2", "-dc", cache])
- cache = cache.chomp('.bz2')
- when '.xz', '.txz'
- f = IO.popen(["xz", "-dc", cache])
- cache = cache.chomp('.xz')
- else
- inp = cache
- end
- inp ||= f.binmode
- ext = File.extname(cache)
- case ext
- when '.tar', /\A\.t[gbx]z\z/
- pid = Process.spawn("tar", "xpf", "-", in: inp, chdir: dir)
- when '.zip'
- pid = Process.spawn("unzip", inp, "-d", dir)
+ def do_extract(cache, dir)
+ if $VERBOSE
+ $stdout.puts "extracting #{cache} into #{dir}"
+ $stdout.flush
+ end
+ ext = File.extname(cache)
+ case ext
+ when '.gz', '.tgz'
+ f = IO.popen(["gzip", "-dc", cache])
+ cache = cache.chomp('.gz')
+ when '.bz2', '.tbz'
+ f = IO.popen(["bzip2", "-dc", cache])
+ cache = cache.chomp('.bz2')
+ when '.xz', '.txz'
+ f = IO.popen(["xz", "-dc", cache])
+ cache = cache.chomp('.xz')
+ else
+ inp = cache
+ end
+ inp ||= f.binmode
+ ext = File.extname(cache)
+ case ext
+ when '.tar', /\A\.t[gbx]z\z/
+ pid = Process.spawn("tar", "xpf", "-", in: inp, chdir: dir)
+ when '.zip'
+ pid = Process.spawn("unzip", inp, "-d", dir)
+ end
+ f.close if f
+ Process.wait(pid)
+ $?.success? or raise "failed to extract #{cache}"
end
- f.close if f
- Process.wait(pid)
- $?.success? or raise "failed to extract #{cache}"
-end
-def do_patch(dest, patch, args)
- if $VERBOSE
- $stdout.puts "applying #{patch} under #{dest}"
- $stdout.flush
+ def do_patch(dest, patch, args)
+ if $VERBOSE
+ $stdout.puts "applying #{patch} under #{dest}"
+ $stdout.flush
+ end
+ Process.wait(Process.spawn("patch", "-d", dest, "-i", patch, *args))
+ $?.success? or raise "failed to patch #{patch}"
end
- Process.wait(Process.spawn("patch", "-d", dest, "-i", patch, *args))
- $?.success? or raise "failed to patch #{patch}"
-end
-cache_dir = ENV['CACHE_DIR'] || ".downloaded-cache"
-mode = :all
-until ARGV.empty?
- case ARGV[0]
- when '--download'
- mode = :download
- when '--extract'
- mode = :extract
- when '--patch'
- mode = :patch
- when '--all'
- mode = :all
- when '--cache'
- ARGV.shift
- cache_dir = ARGV[0]
- when /\A--cache=/
- cache_dir = $'
- when '--'
- ARGV.shift
- break
- when /\A-/
- abort "unknown option: #{ARGV[0]}"
- else
- break
- end
- ARGV.shift
-end
+ def do_command(mode, dest, url, cache_dir, chksums)
+ extracted = false
+ base = /.*(?=\.tar(?:\.\w+)?\z)/
-FileUtils.mkdir_p(cache_dir)
+ case mode
+ when :download
+ cache = do_download(url, cache_dir)
+ do_checksum(cache, chksums)
+ when :extract
+ cache = cache_file(url, cache_dir)
+ target = File.join(dest, File.basename(cache)[base])
+ unless File.directory?(target)
+ do_checksum(cache, chksums)
+ extracted = do_extract(cache, dest)
+ end
+ when :all
+ cache = do_download(url, cache_dir)
+ target = File.join(dest, File.basename(cache)[base])
+ unless File.directory?(target)
+ do_checksum(cache, chksums)
+ extracted = do_extract(cache, dest)
+ end
+ end
+ extracted
+ end
-success = true
-ARGV.each do |dir|
- Dir.glob("#{dir}/**/extlibs") do |list|
- if $VERBOSE
- $stdout.puts "downloading for #{list}"
- $stdout.flush
+ def run(argv)
+ cache_dir = nil
+ mode = :all
+ until argv.empty?
+ case argv[0]
+ when '--download'
+ mode = :download
+ when '--extract'
+ mode = :extract
+ when '--patch'
+ mode = :patch
+ when '--all'
+ mode = :all
+ when '--cache'
+ argv.shift
+ cache_dir = argv[0]
+ when /\A--cache=/
+ cache_dir = $'
+ when '--'
+ argv.shift
+ break
+ when /\A-/
+ warn "unknown option: #{argv[0]}"
+ return false
+ else
+ break
+ end
+ argv.shift
end
- extracted = false
- dest = File.dirname(list)
- IO.foreach(list) do |line|
- line.sub!(/\s*#.*/, '')
- if /^\t/ =~ line
- if extracted and (mode == :all or mode == :patch)
- patch, *args = line.split
- do_patch(dest, patch, args)
+
+ success = true
+ argv.each do |dir|
+ Dir.glob("#{dir}/**/extlibs") do |list|
+ if $VERBOSE
+ $stdout.puts "downloading for #{list}"
+ $stdout.flush
end
- next
- end
- url, *chksums = line.split(' ')
- next unless url
- extracted = false
- base = File.basename(url)
- cache = File.join(cache_dir, base)
- target = File.join(dest, base[/.*(?=\.tar(?:\.\w+)?\z)/])
- begin
- case mode
- when :download
- do_download(url, base, cache_dir)
- do_checksum(cache, chksums)
- when :extract
- unless File.directory?(target)
- do_checksum(cache, chksums)
- extracted = do_extract(cache, dest)
+ extracted = false
+ dest = File.dirname(list)
+ url = chksums = nil
+ IO.foreach(list) do |line|
+ line.sub!(/\s*#.*/, '')
+ if chksums
+ chksums.concat(line.split)
+ elsif /^\t/ =~ line
+ if extracted and (mode == :all or mode == :patch)
+ patch, *args = line.split
+ do_patch(dest, patch, args)
+ end
+ next
+ else
+ url, *chksums = line.split(' ')
+ end
+ if chksums.last == '\\'
+ chksums.pop
+ next
end
- when :all
- do_download(url, base, cache_dir)
- unless File.directory?(target)
- do_checksum(cache, chksums)
- extracted = do_extract(cache, dest)
+ next unless url
+ begin
+ extracted = do_command(mode, dest, url, cache_dir, chksums)
+ rescue => e
+ warn e.inspect
+ success = false
end
+ url = chksums = nil
end
- rescue => e
- warn e.inspect
- success = false
end
end
+ success
+ end
+
+ def self.run(argv)
+ self.new.run(argv)
end
end
-exit(success)
+if $0 == __FILE__
+ exit ExtLibs.run(ARGV)
+end
diff --git a/tool/fake.rb b/tool/fake.rb
index 99fc24e775..42174052e2 100644
--- a/tool/fake.rb
+++ b/tool/fake.rb
@@ -12,21 +12,9 @@ end
static = !!(defined?($static) && $static)
$:.unshift(builddir)
posthook = proc do
- config = RbConfig::CONFIG
- mkconfig = RbConfig::MAKEFILE_CONFIG
- extout = File.expand_path(mkconfig["EXTOUT"], builddir)
- [
- ["top_srcdir", $top_srcdir],
- ["topdir", $topdir],
- ].each do |var, val|
- next unless val
- mkconfig[var] = config[var] = val
- t = /\A#{Regexp.quote(val)}(?=\/)/
- $hdrdir.sub!(t) {"$(#{var})"}
- mkconfig.keys.grep(/dir\z/) do |k|
- mkconfig[k] = "$(#{var})#$'" if t =~ mkconfig[k]
- end
- end
+ RbConfig.fire_update!("top_srcdir", $top_srcdir)
+ RbConfig.fire_update!("topdir", $topdir)
+ $hdrdir.sub!(/\A#{Regexp.quote($top_srcdir)}(?=\/)/, "$(top_srcdir)")
if $extmk
$ruby = "$(topdir)/miniruby -I'$(topdir)' -I'$(top_srcdir)/lib' -I'$(extout)/$(arch)' -I'$(extout)/common'"
else
@@ -55,15 +43,14 @@ prehook = proc do |extmk|
$extout_prefix = '$(extout)$(target_prefix)/'
config = RbConfig::CONFIG
mkconfig = RbConfig::MAKEFILE_CONFIG
- mkconfig["builddir"] = config["builddir"] = builddir
- mkconfig["buildlibdir"] = config["buildlibdir"] = builddir
- mkconfig["top_srcdir"] = $top_srcdir if $top_srcdir
- mkconfig["extout"] ||= $extout
- config["top_srcdir"] = File.expand_path($top_srcdir ||= top_srcdir)
- config["rubyhdrdir"] = join[$top_srcdir, "include"]
- config["rubyarchhdrdir"] = join[builddir, config["EXTOUT"], "include", config["arch"]]
- config["extout"] ||= join[$topdir, ".ext"]
- mkconfig["libdirname"] = "buildlibdir"
+ RbConfig.fire_update!("builddir", builddir)
+ RbConfig.fire_update!("buildlibdir", builddir)
+ RbConfig.fire_update!("libdir", builddir)
+ RbConfig.fire_update!("top_srcdir", $top_srcdir ||= top_srcdir)
+ RbConfig.fire_update!("extout", $extout)
+ RbConfig.fire_update!("rubyhdrdir", "$(top_srcdir)/include")
+ RbConfig.fire_update!("rubyarchhdrdir", "$(extout)/include/$(arch)")
+ RbConfig.fire_update!("libdirname", "buildlibdir")
trace_var(:$ruby, posthook)
untrace_var(:$extmk, prehook)
end
diff --git a/tool/fetch-bundled_gems.rb b/tool/fetch-bundled_gems.rb
new file mode 100755
index 0000000000..ae3068d35c
--- /dev/null
+++ b/tool/fetch-bundled_gems.rb
@@ -0,0 +1,27 @@
+#!ruby -an
+BEGIN {
+ require 'fileutils'
+
+ dir = ARGV.shift
+ ARGF.eof?
+ FileUtils.mkdir_p(dir)
+ Dir.chdir(dir)
+}
+
+n, v, u = $F
+case n
+when "minitest"
+ v = "master"
+when "test-unit"
+else
+ v = "v" + v
+end
+
+if File.directory?(n)
+ puts "updating #{n} ..."
+ system(*%W"git fetch", chdir: n) or abort
+else
+ puts "retrieving #{n} ..."
+ system(*%W"git clone #{u} #{n}") or abort
+end
+system(*%W"git checkout #{v}", chdir: n) or abort
diff --git a/tool/file2lastrev.rb b/tool/file2lastrev.rb
index ee615a5060..8d68da9f88 100755
--- a/tool/file2lastrev.rb
+++ b/tool/file2lastrev.rb
@@ -89,7 +89,8 @@ else
begin
puts @output[*vcs.get_revisions(arg)]
rescue => e
- warn "#{File.basename(Program)}: #{e.message}" unless @suppress_not_found
+ next if @suppress_not_found and VCS::NotFoundError === e
+ warn "#{File.basename(Program)}: #{e.message}"
ok = false
end
end
diff --git a/tool/gen_ruby_tapset.rb b/tool/gen_ruby_tapset.rb
index ca4b09905a..ae3c1eccd2 100755
--- a/tool/gen_ruby_tapset.rb
+++ b/tool/gen_ruby_tapset.rb
@@ -5,7 +5,7 @@
require "optparse"
-def set_argument (argname, nth)
+def set_argument(argname, nth)
# remove C style type info
argname.gsub!(/.+ (.+)/, '\1') # e.g. char *hoge -> *hoge
argname.gsub!(/^\*/, '') # e.g. *filename -> filename
@@ -31,7 +31,7 @@ text.gsub!(/^\};/, "")
# probename()
text.gsub!(/probe (.+)\( *\);/) {
probe_name = $1
- probe = <<-End
+ <<-End
probe #{probe_name} = process("ruby").provider("ruby").mark("#{probe_name}")
{
}
@@ -43,7 +43,7 @@ text.gsub!(/ *probe (.+)\(([^,)]+)\);/) {
probe_name = $1
arg1 = $2
- probe = <<-End
+ <<-End
probe #{probe_name} = process("ruby").provider("ruby").mark("#{probe_name}")
{
#{set_argument(arg1, 1)}
@@ -57,7 +57,7 @@ text.gsub!(/ *probe (.+)\(([^,)]+),([^,)]+)\);/) {
arg1 = $2
arg2 = $3
- probe = <<-End
+ <<-End
probe #{probe_name} = process("#{ruby_path}").provider("ruby").mark("#{probe_name}")
{
#{set_argument(arg1, 1)}
@@ -73,7 +73,7 @@ text.gsub!(/ *probe (.+)\(([^,)]+),([^,)]+),([^,)]+)\);/) {
arg2 = $3
arg3 = $4
- probe = <<-End
+ <<-End
probe #{probe_name} = process("#{ruby_path}").provider("ruby").mark("#{probe_name}")
{
#{set_argument(arg1, 1)}
@@ -91,7 +91,7 @@ text.gsub!(/ *probe (.+)\(([^,)]+),([^,)]+),([^,)]+),([^,)]+)\);/) {
arg3 = $4
arg4 = $5
- probe = <<-End
+ <<-End
probe #{probe_name} = process("#{ruby_path}").provider("ruby").mark("#{probe_name}")
{
#{set_argument(arg1, 1)}
@@ -103,4 +103,3 @@ text.gsub!(/ *probe (.+)\(([^,)]+),([^,)]+),([^,)]+),([^,)]+)\);/) {
}
print text
-
diff --git a/tool/generate-backport-changelog.rb b/tool/generate-backport-changelog.rb
new file mode 100644
index 0000000000..0e8676a085
--- /dev/null
+++ b/tool/generate-backport-changelog.rb
@@ -0,0 +1,99 @@
+#!ruby
+require "time"
+
+def usage!
+ STDERR.puts <<-EOS
+Usage: #$0 [--trunk=<dir>] [--target=<dir>] <revision(s)>
+
+Generate ChangeLog entries for backporting.
+The entries are output to STDOUT, and the messages of this tool are output to
+STDERR. So you can simply redirect STDOUT to get the entries.
+
+You should specify the path of trunk by `--trunk`. If not, assumed cwd.
+You also should specify the path of the target branch by `--target`. If not,
+assumed cwd.
+This means that you have to specify at least one of `--trunk` or `--target`.
+
+revision(s) can be below or their combinations:
+ 12345 # means r12345
+ 12345,54321 # means r12345 and r54321
+ 12345-12347 # means r12345, r12346 and r12347 (of course, if available)
+
+Note that the revisions is backported branch's ones, not trunk's.
+
+The target of this tool is *not* to generate ChangeLog automatically, but to
+generate the draft of ChangeLog.
+You have to check and modify the output.
+ EOS
+ exit
+end
+
+Majors = {
+ "eregon" => "Benoit Daloze <eregontp@gmail.com>",
+ "kazu" => "Kazuhiro NISHIYAMA <zn@mbf.nifty.com>",
+ "ko1" => "Koichi Sasada <ko1@atdot.net>",
+ "marcandre" => "Marc-Andre Lafortune <ruby-core@marc-andre.ca>",
+ "naruse" => "NARUSE, Yui <naruse@ruby-lang.org>",
+ "nobu" => "Nobuyoshi Nakada <nobu@ruby-lang.org>",
+ "normal" => "Eric Wong <normalperson@yhbt.net>",
+ "rhe" => "Kazuki Yamaguchi <k@rhe.jp>",
+ "shugo" => "Shugo Maeda <shugo@ruby-lang.org>",
+ "stomar" => "Marcus Stollsteimer <sto.mar@web.de>",
+ "usa" => "NAKAMURA Usaku <usa@ruby-lang.org>",
+ "zzak" => "Zachary Scott <e@zzak.io>",
+}
+
+trunk = "."
+target = "."
+ARGV.delete_if{|e| /^--trunk=(.*)/ =~ e && trunk = $1}
+ARGV.delete_if{|e| /^--target=(.*)/ =~ e && target = $1}
+usage! if ARGV.size == 0 || trunk == target
+
+revisions = []
+ARGV.each do |a|
+ a.split(/,/).each do |b|
+ if /-/ =~ b
+ revisions += Range.new(*b.split(/-/, 2).map{|e| Integer(e)}).to_a
+ else
+ revisions << Integer(b)
+ end
+ end
+end
+revisions.sort!
+revisions.reverse!
+
+revisions.each do |rev|
+ if /^Index: ChangeLog$/ =~ `svn diff -c #{rev} #{target}`
+ STDERR.puts "#{rev} already has ChangeLog. Skip."
+ else
+ lines = `svn log -r #{rev} #{target}`.lines[1..-2]
+ if lines.empty?
+ STDERR.puts "#{rev} does not exist. Skip."
+ next
+ end
+ unless /^merge revision\(s\) (\d+)/ =~ lines[2]
+ STDERR.puts "#{rev} is not seems to be a merge commit. Skip."
+ next
+ end
+ original = $1
+ committer = `svn log -r #{original} #{trunk}`.lines[1].split(/\|/)[1].strip
+ if Majors[committer]
+ committer = Majors[committer]
+ else
+ committer = "#{committer} <#{committer}@ruby-lang.org>"
+ end
+ time = Time.parse(lines.shift.split(/\|/)[2]).getlocal("+09:00")
+ puts "#{time.asctime} #{committer}"
+ puts
+ lines.shift(2) # skip "merge" line
+ lines.shift while lines.first == "\n"
+ lines.pop while lines.last == "\n"
+ lines.each do |line|
+ line.chomp!
+ line = "\t#{line}" if line[0] != "\t" && line != ""
+ puts line
+ end
+ puts
+ STDERR.puts "#{rev} is processed."
+ end
+end
diff --git a/tool/generic_erb.rb b/tool/generic_erb.rb
index 0664056651..3904b570c4 100644
--- a/tool/generic_erb.rb
+++ b/tool/generic_erb.rb
@@ -5,9 +5,9 @@
require 'erb'
require 'optparse'
-require 'fileutils'
$:.unshift(File.dirname(__FILE__))
require 'vpath'
+require 'colorize'
vpath = VPath.new
timestamp = nil
@@ -15,31 +15,33 @@ output = nil
ifchange = nil
source = false
color = nil
+templates = []
-opt = OptionParser.new do |o|
+ARGV.options do |o|
o.on('-t', '--timestamp[=PATH]') {|v| timestamp = v || true}
+ o.on('-i', '--input=PATH') {|v| template << v}
o.on('-o', '--output=PATH') {|v| output = v}
o.on('-c', '--[no-]if-change') {|v| ifchange = v}
o.on('-x', '--source') {source = true}
o.on('--color') {color = true}
vpath.def_options(o)
o.order!(ARGV)
+ templates << (ARGV.shift or abort o.to_s) if templates.empty?
end
-unchanged = "unchanged"
-updated = "updated"
-if color or (color == nil && STDOUT.tty?)
- if (/\A\e\[.*m\z/ =~ IO.popen("tput smso", "r", err: IO::NULL, &:read) rescue nil)
- beg = "\e["
- colors = (colors = ENV['TEST_COLORS']) ? Hash[colors.scan(/(\w+)=([^:\n]*)/)] : {}
- reset = "#{beg}m"
- unchanged = "#{beg}#{colors["pass"] || "32;1"}m#{unchanged}#{reset}"
- updated = "#{beg}#{colors["fail"] || "31;1"}m#{updated}#{reset}"
+color = Colorize.new(color)
+unchanged = color.pass("unchanged")
+updated = color.fail("updated")
+
+result = templates.map do |template|
+ if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
+ erb = ERB.new(File.read(template), trim_mode: '%-')
+ else
+ erb = ERB.new(File.read(template), nil, '%-')
end
+ erb.filename = template
+ source ? erb.src : proc{erb.result(binding)}.call
end
-template = ARGV.shift or abort opt.to_s
-erb = ERB.new(File.read(template), nil, '%-')
-erb.filename = template
-result = source ? erb.src : erb.result
+result = result.size == 1 ? result[0] : result.join("")
if output
if ifchange and (vpath.open(output, "rb") {|f| f.read} rescue nil) == result
puts "#{output} #{unchanged}"
@@ -52,7 +54,8 @@ if output
dir, base = File.split(output)
timestamp = File.join(dir, ".time." + base)
end
- FileUtils.touch(timestamp)
+ File.open(timestamp, 'a') {}
+ File.utime(nil, nil, timestamp)
end
else
print result
diff --git a/tool/git-refresh b/tool/git-refresh
new file mode 100755
index 0000000000..9ed7d7c76e
--- /dev/null
+++ b/tool/git-refresh
@@ -0,0 +1,46 @@
+#!/bin/sh
+set -e
+
+if (cd -P .) 2>/dev/null; then
+ CHDIR='cd -P'
+else
+ CHDIR='cd'
+fi
+
+quiet=
+branch=
+
+until [ $# = 0 ]; do
+ case "$1" in
+ --) shift; break;;
+ -C|--directory) shift; $CHDIR "$1";;
+ -C*) $CHDIR `expr "$1" : '-C\(.*\)'`;;
+ --directory=*) $CHDIR `expr "$1" : '[^=]*=\(.*\)'`;;
+ -q) quiet=1;;
+ -b|--branch) shift; branch="$1";;
+ -b*) branch=`expr "$1" : '-b\(.*\)'`;;
+ --branch=*) branch=`expr "$1" : '[^=]*=\(.*\)'`;;
+ -*) echo "unknown option: $1" 1>&2; exit 1;;
+ *) break;;
+ esac
+ shift
+done
+
+url="$1"
+dir="$2"
+shift 2
+[ x"$branch" = x ] && unset branch || :
+if [ -d "$dir" ]; then
+ if [ x"$(git -C "$dir" describe --tags)" = x"$branch" ]; then
+ exit 0 # already up-to-date
+ fi
+ echo updating `expr "/$dir/" : '.*/\([^/][^/]*\)/'` ...
+ [ $quiet ] || set -x
+ $CHDIR "$dir"
+ ${branch+git} ${branch+fetch} ${branch+"$@"}
+ exec git ${branch+checkout} "${branch-pull}" "$@"
+else
+ echo retrieving `expr "/$dir/" : '.*/\([^/][^/]*\)/'` ...
+ [ $quiet ] || set -x
+ exec git clone ${branch+--branch} ${branch+"$branch"} "$url" "$dir" "$@"
+fi
diff --git a/tool/gperf.sed b/tool/gperf.sed
new file mode 100644
index 0000000000..6b3e1980be
--- /dev/null
+++ b/tool/gperf.sed
@@ -0,0 +1,22 @@
+/ANSI-C code/{
+ h
+ s/.*/ANSI:offset:/
+ x
+}
+/\/\*!ANSI{\*\//{
+ G
+ s/\/\*!ANSI{\*\/\(.*\)\/\*}!ANSI\*\/\(.*\)\nANSI:.*/\/\*\1\*\/\2/
+}
+s/(int)([a-z_]*)&((struct \([a-zA-Z_0-9][a-zA-Z_0-9]*\)_t *\*)0)->\1_str\([1-9][0-9]*\),/gperf_offsetof(\1, \2),/g
+/^#line/{
+ G
+ x
+ s/:offset:/:/
+ x
+ s/\(.*\)\(\n\).*:offset:.*/#define gperf_offsetof(s, n) (short)offsetof(struct s##_t, s##_str##n)\2\1/
+ s/\n[^#].*//
+}
+/^[a-zA-Z_0-9]*hash/,/^}/{
+ s/ hval = / hval = (unsigned int)/
+ s/ return / return (unsigned int)/
+}
diff --git a/tool/ifchange b/tool/ifchange
index 8571cf8049..050c8d8f26 100755
--- a/tool/ifchange
+++ b/tool/ifchange
@@ -31,6 +31,9 @@ until [ $# -eq 0 ]; do
--color=*)
color=`expr \( "$1" : '[^=]*=\(.*\)' \)`
;;
+ --debug)
+ set -x
+ ;;
*)
break
;;
@@ -55,7 +58,7 @@ if [ "$color" = always -o \( "$color" = auto -a -t 1 \) ]; then
msg_unchanged=`expr ":$TEST_COLORS:" : ".*:pass=\([^:]*\):"` || :
msg_updated=`expr ":$TEST_COLORS:" : ".*:fail=\([^:]*\):"` || :
fi
- msg_unchanged="${msg_begin}${msg_unchanged:-32;1}m"
+ msg_unchanged="${msg_begin}${msg_unchanged:-32}m"
msg_updated="${msg_begin}${msg_updated:-31;1}m"
msg_reset="${msg_begin}m"
;;
diff --git a/tool/insns2vm.rb b/tool/insns2vm.rb
index ecbbb52643..027dc4e380 100755
--- a/tool/insns2vm.rb
+++ b/tool/insns2vm.rb
@@ -3,16 +3,13 @@
# This is used by Makefile.in to generate .inc files.
# See Makefile.in for details.
-require 'optparse'
-
-Version = %w$Revision: 11626 $[1..-1]
-
-require "#{File.join(File.dirname(__FILE__), 'instruction')}"
+require_relative 'ruby_vm/scripts/insns2vm'
if $0 == __FILE__
- opts = ARGV.options
- maker = RubyVM::SourceCodeGenerator.def_options(opts)
- files = opts.parse!
- generator = maker.call
- generator.generate(files)
+ RubyVM::Insns2VM.router(ARGV).each do |(path, generator)|
+ str = generator.generate path
+ path.open 'wb:utf-8' do |fp|
+ fp.write str
+ end
+ end
end
diff --git a/tool/install-sh b/tool/install-sh
index af97fa6af1..11e502f56d 100644
--- a/tool/install-sh
+++ b/tool/install-sh
@@ -1,13 +1,13 @@
#!/bin/sh
-# Just only for using AC_PROG_INSTALL in configure.in.
+# Just only for using AC_PROG_INSTALL in configure.ac.
# See autoconf.info for more detail.
cat <<EOF >&2
-Ruby uses a BSD-compatible install(1) if possible. If not, Ruby
+Ruby uses a BSD-compatible install(1) if possible. If not, Ruby
provides its own install(1) alternative.
-This script a place holder for AC_PROG_INSTALL in configure.in.
+This script is a place holder for AC_PROG_INSTALL in configure.ac.
Please report a bug in Ruby to http://bugs.ruby-lang.org if you see
this message.
diff --git a/tool/instruction.rb b/tool/instruction.rb
deleted file mode 100755
index 57dc923005..0000000000
--- a/tool/instruction.rb
+++ /dev/null
@@ -1,1354 +0,0 @@
-#!./miniruby
-# -*- coding: us-ascii -*-
-#
-# This library is used by insns2vm.rb as part of autogenerating
-# instruction files with .inc extensions like insns.inc and vm.inc.
-
-require 'erb'
-$:.unshift(File.dirname(__FILE__))
-require 'vpath'
-
-class RubyVM
- class Instruction
- def initialize name, opes, pops, rets, comm, body, tvars, sp_inc,
- orig = self, defopes = [], type = nil,
- nsc = [], psc = [[], []]
-
- @name = name
- @opes = opes # [[type, name], ...]
- @pops = pops # [[type, name], ...]
- @rets = rets # [[type, name], ...]
- @comm = comm # {:c => category, :e => en desc, :j => ja desc}
- @body = body # '...'
-
- @orig = orig
- @defopes = defopes
- @type = type
- @tvars = tvars
-
- @nextsc = nsc
- @pushsc = psc
- @sc = []
- @unifs = []
- @optimized = []
- @is_sc = false
- @sp_inc = sp_inc
- end
-
- def add_sc sci
- @sc << sci
- sci.set_sc
- end
-
- attr_reader :name, :opes, :pops, :rets
- attr_reader :body, :comm
- attr_reader :nextsc, :pushsc
- attr_reader :orig, :defopes, :type
- attr_reader :sc
- attr_reader :unifs, :optimized
- attr_reader :is_sc
- attr_reader :tvars
- attr_reader :sp_inc
-
- def set_sc
- @is_sc = true
- end
-
- def add_unif insns
- @unifs << insns
- end
-
- def add_optimized insn
- @optimized << insn
- end
-
- def sp_increase_c_expr
- if(pops.any?{|t, v| v == '...'} ||
- rets.any?{|t, v| v == '...'})
- # user definition
- raise "no sp increase definition" if @sp_inc.nil?
- ret = "int inc = 0;\n"
-
- @opes.each_with_index{|(t, v), i|
- if (t == 'rb_num_t' && ((re = /\b#{v}\b/n) =~ @sp_inc)) ||
- (@defopes.any?{|t, val| re =~ val})
- ret << " int #{v} = FIX2INT(opes[#{i}]);\n"
- elsif (t == 'CALL_INFO' && ((re = /\b#{v}\b/n) =~ @sp_inc))
- ret << " CALL_INFO #{v} = (CALL_INFO)(opes[#{i}]);\n"
- end
- }
-
- @defopes.each_with_index{|((t, var), val), i|
- if t == 'rb_num_t' && val != '*' && /\b#{var}\b/ =~ @sp_inc
- ret << " #{t} #{var} = #{val};\n"
- end
- }
-
- ret << " #{@sp_inc};\n"
- ret << " return depth + inc;"
- ret
- else
- "return depth + #{rets.size - pops.size};"
- end
- end
-
- def inspect
- "#<Instruction:#{@name}>"
- end
- end
-
- class InstructionsLoader
- def initialize opts = {}
- @insns = []
- @insn_map = {}
-
- @vpath = opts[:VPATH] || File
- @use_const = opts[:use_const]
- @verbose = opts[:verbose]
- @destdir = opts[:destdir]
-
- (@vm_opts = load_vm_opts).each {|k, v|
- @vm_opts[k] = opts[k] if opts.key?(k)
- }
-
- load_insns_def opts[:"insns.def"] || 'insns.def'
-
- load_opt_operand_def opts[:"opope.def"] || 'defs/opt_operand.def'
- load_insn_unification_def opts[:"unif.def"] || 'defs/opt_insn_unif.def'
- make_stackcaching_insns if vm_opt?('STACK_CACHING')
- end
-
- attr_reader :vpath
- attr_reader :destdir
-
- %w[use_const verbose].each do |attr|
- attr_reader attr
- alias_method "#{attr}?", attr
- remove_method attr
- end
-
- def [](s)
- @insn_map[s.to_s]
- end
-
- def each
- @insns.each{|insn|
- yield insn
- }
- end
-
- def size
- @insns.size
- end
-
- ###
- private
-
- def vm_opt? name
- @vm_opts[name]
- end
-
- def load_vm_opts file = nil
- file ||= 'vm_opts.h'
- opts = {}
- vpath.open(file) do |f|
- f.grep(/^\#define\s+OPT_([A-Z_]+)\s+(\d+)/) do
- opts[$1] = !$2.to_i.zero?
- end
- end
- opts
- end
-
- SKIP_COMMENT_PATTERN = Regexp.compile(Regexp.escape('/** ##skip'))
-
- include Enumerable
-
- def add_insn insn
- @insns << insn
- @insn_map[insn.name] = insn
- end
-
- def make_insn name, opes, pops, rets, comm, body, sp_inc
- add_insn Instruction.new(name, opes, pops, rets, comm, body, [], sp_inc)
- end
-
- # str -> [[type, var], ...]
- def parse_vars line
- raise unless /\((.*?)\)/ =~ line
- vars = $1.split(',')
- vars.map!{|v|
- if /\s*(\S+)\s+(\S+)\s*/ =~ v
- type = $1
- var = $2
- elsif /\s*\.\.\.\s*/ =~ v
- type = var = '...'
- else
- raise
- end
- [type, var]
- }
- vars
- end
-
- def parse_comment comm
- c = 'others'
- j = ''
- e = ''
- comm.each_line{|line|
- case line
- when /@c (.+)/
- c = $1
- when /@e (.+)/
- e = $1
- when /@e\s*$/
- e = ''
- when /@j (.+)$/
- j = $1
- when /@j\s*$/
- j = ''
- end
- }
- { :c => c,
- :e => e,
- :j => j,
- }
- end
-
- def load_insns_def file
- body = insn = opes = pops = rets = nil
- comment = ''
-
- vpath.open(file) {|f|
- f.instance_variable_set(:@line_no, 0)
- class << f
- def line_no
- @line_no
- end
- def gets
- @line_no += 1
- super
- end
- end
-
- while line = f.gets
- line.chomp!
- case line
-
- when SKIP_COMMENT_PATTERN
- while line = f.gets.chomp
- if /\s+\*\/$/ =~ line
- break
- end
- end
-
- # collect instruction comment
- when /^\/\*\*$/
- while line = f.gets
- if /\s+\*\/\s*$/ =~ line
- break
- else
- comment << line
- end
- end
-
- # start instruction body
- when /^DEFINE_INSN$/
- insn = f.gets.chomp
- opes = parse_vars(f.gets.chomp)
- pops = parse_vars(f.gets.chomp).reverse
- rets_str = f.gets.chomp
- rets = parse_vars(rets_str).reverse
- comment = parse_comment(comment)
- insn_in = true
- body = ''
-
- sp_inc = rets_str[%r"//\s*(.+)", 1]
-
- raise unless /^\{$/ =~ f.gets.chomp
- line_no = f.line_no
-
- # end instruction body
- when /^\}/
- if insn_in
- body.instance_variable_set(:@line_no, line_no)
- body.instance_variable_set(:@file, f.path)
- insn = make_insn(insn, opes, pops, rets, comment, body, sp_inc)
- insn_in = false
- comment = ''
- end
-
- else
- if insn_in
- body << line + "\n"
- end
- end
- end
- }
- end
-
- ## opt op
- def load_opt_operand_def file
- vpath.foreach(file) {|line|
- line = line.gsub(/\#.*/, '').strip
- next if line.length == 0
- break if /__END__/ =~ line
- /(\S+)\s+(.+)/ =~ line
- insn = $1
- opts = $2
- add_opt_operand insn, opts.split(/,/).map{|e| e.strip}
- } if file
- end
-
- def label_escape label
- label.gsub(/\(/, '_O_').
- gsub(/\)/, '_C_').
- gsub(/\*/, '_WC_')
- end
-
- def add_opt_operand insn_name, opts
- insn = @insn_map[insn_name]
- opes = insn.opes
-
- if opes.size != opts.size
- raise "operand size mismatch for #{insn.name} (opes: #{opes.size}, opts: #{opts.size})"
- end
-
- ninsn = insn.name + '_OP_' + opts.map{|e| label_escape(e)}.join('_')
- nopes = []
- defv = []
-
- opts.each_with_index{|e, i|
- if e == '*'
- nopes << opes[i]
- end
- defv << [opes[i], e]
- }
-
- make_insn_operand_optimized(insn, ninsn, nopes, defv)
- end
-
- def make_insn_operand_optimized orig_insn, name, opes, defopes
- comm = orig_insn.comm.dup
- comm[:c] = 'optimize'
- add_insn insn = Instruction.new(
- name, opes, orig_insn.pops, orig_insn.rets, comm,
- orig_insn.body, orig_insn.tvars, orig_insn.sp_inc,
- orig_insn, defopes)
- orig_insn.add_optimized insn
- end
-
- ## insn unif
- def load_insn_unification_def file
- vpath.foreach(file) {|line|
- line = line.gsub(/\#.*/, '').strip
- next if line.length == 0
- break if /__END__/ =~ line
- make_unified_insns line.split.map{|e|
- raise "unknown insn: #{e}" unless @insn_map[e]
- @insn_map[e]
- }
- } if file
- end
-
- def all_combination sets
- ret = sets.shift.map{|e| [e]}
-
- sets.each{|set|
- prev = ret
- ret = []
- prev.each{|ary|
- set.each{|e|
- eary = ary.dup
- eary << e
- ret << eary
- }
- }
- }
- ret
- end
-
- def make_unified_insns insns
- if vm_opt?('UNIFY_ALL_COMBINATION')
- insn_sets = insns.map{|insn|
- [insn] + insn.optimized
- }
-
- all_combination(insn_sets).each{|insns_set|
- make_unified_insn_each insns_set
- }
- else
- make_unified_insn_each insns
- end
- end
-
- def mk_private_val vals, i, redef
- vals.dup.map{|v|
- # v[0] : type
- # v[1] : var name
-
- v = v.dup
- if v[0] != '...'
- redef[v[1]] = v[0]
- v[1] = "#{v[1]}_#{i}"
- end
- v
- }
- end
-
- def mk_private_val2 vals, i, redef
- vals.dup.map{|v|
- # v[0][0] : type
- # v[0][1] : var name
- # v[1] : default val
-
- pv = v.dup
- v = pv[0] = pv[0].dup
- if v[0] != '...'
- redef[v[1]] = v[0]
- v[1] = "#{v[1]}_#{i}"
- end
- pv
- }
- end
-
- def make_unified_insn_each insns
- names = []
- opes = []
- pops = []
- rets = []
- comm = {
- :c => 'optimize',
- :e => 'unified insn',
- :j => 'unified insn',
- }
- body = ''
- passed = []
- tvars = []
- defopes = []
- sp_inc = ''
-
- insns.each_with_index{|insn, i|
- names << insn.name
- redef_vars = {}
-
- e_opes = mk_private_val(insn.opes, i, redef_vars)
- e_pops = mk_private_val(insn.pops, i, redef_vars)
- e_rets = mk_private_val(insn.rets, i, redef_vars)
- # ToDo: fix it
- e_defs = mk_private_val2(insn.defopes, i, redef_vars)
-
- passed_vars = []
- while pvar = e_pops.pop
- rvar = rets.pop
- if rvar
- raise "unsupported unif insn: #{insns.inspect}" if rvar[0] == '...'
- passed_vars << [pvar, rvar]
- tvars << rvar
- else
- e_pops.push pvar
- break
- end
- end
-
- opes.concat e_opes
- pops.concat e_pops
- rets.concat e_rets
- defopes.concat e_defs
- sp_inc << "#{insn.sp_inc}"
-
- body << "{ /* unif: #{i} */\n" +
- passed_vars.map{|rpvars|
- pv = rpvars[0]
- rv = rpvars[1]
- "#define #{pv[1]} #{rv[1]}"
- }.join("\n") +
- "\n" +
- redef_vars.map{|v, type|
- "#{type} #{v} = #{v}_#{i};"
- }.join("\n") + "\n"
- if line = insn.body.instance_variable_get(:@line_no)
- file = insn.body.instance_variable_get(:@file)
- body << "#line #{line+1} \"#{file}\"\n"
- body << insn.body
- body << "\n#line __CURRENT_LINE__ \"__CURRENT_FILE__\"\n"
- else
- body << insn.body
- end
- body << redef_vars.keys.map{|v|
- "#{v}_#{i} = #{v};"
- }.join("\n") +
- "\n" +
- passed_vars.map{|rpvars|
- "#undef #{rpvars[0][1]}"
- }.join("\n") +
- "\n}\n"
- }
-
- tvars_ary = []
- tvars.each{|tvar|
- unless opes.any?{|var|
- var[1] == tvar[1]
- } || defopes.any?{|pvar|
- pvar[0][1] == tvar[1]
- }
- tvars_ary << tvar
- end
- }
- add_insn insn = Instruction.new("UNIFIED_" + names.join('_'),
- opes, pops, rets.reverse, comm, body,
- tvars_ary, sp_inc)
- insn.defopes.replace defopes
- insns[0].add_unif [insn, insns]
- end
-
- ## sc
- SPECIAL_INSN_FOR_SC_AFTER = {
- /\Asend/ => [:a],
- /\Aend/ => [:a],
- /\Ayield/ => [:a],
- /\Aclassdef/ => [:a],
- /\Amoduledef/ => [:a],
- }
- FROM_SC = [[], [:a], [:b], [:a, :b], [:b, :a]]
-
- def make_stackcaching_insns
- pops = rets = nil
-
- @insns.dup.each{|insn|
- opops = insn.pops
- orets = insn.rets
- oopes = insn.opes
- ocomm = insn.comm
- oname = insn.name
-
- after = SPECIAL_INSN_FOR_SC_AFTER.find {|k, v| k =~ oname}
-
- insns = []
- FROM_SC.each{|from|
- name, pops, rets, pushs1, pushs2, nextsc =
- *calc_stack(insn, from, after, opops, orets)
-
- make_insn_sc(insn, name, oopes, pops, rets, [pushs1, pushs2], nextsc)
- }
- }
- end
-
- def make_insn_sc orig_insn, name, opes, pops, rets, pushs, nextsc
- comm = orig_insn.comm.dup
- comm[:c] = 'optimize(sc)'
-
- scinsn = Instruction.new(
- name, opes, pops, rets, comm,
- orig_insn.body, orig_insn.tvars, orig_insn.sp_inc,
- orig_insn, orig_insn.defopes, :sc, nextsc, pushs)
-
- add_insn scinsn
- orig_insn.add_sc scinsn
- end
-
- def self.complement_name st
- "#{st[0] ? st[0] : 'x'}#{st[1] ? st[1] : 'x'}"
- end
-
- def add_stack_value st
- len = st.length
- if len == 0
- st[0] = :a
- [nil, :a]
- elsif len == 1
- if st[0] == :a
- st[1] = :b
- else
- st[1] = :a
- end
- [nil, st[1]]
- else
- st[0], st[1] = st[1], st[0]
- [st[1], st[1]]
- end
- end
-
- def calc_stack insn, ofrom, oafter, opops, orets
- from = ofrom.dup
- pops = opops.dup
- rets = orets.dup
- rest_scr = ofrom.dup
-
- pushs_before = []
- pushs= []
-
- pops.each_with_index{|e, i|
- if e[0] == '...'
- pushs_before = from
- from = []
- end
- r = from.pop
- break unless r
- pops[i] = pops[i].dup << r
- }
-
- if oafter
- from = oafter
- from.each_with_index{|r, i|
- rets[i] = rets[i].dup << r if rets[i]
- }
- else
- rets = rets.reverse
- rets.each_with_index{|e, i|
- break if e[0] == '...'
- pushed, r = add_stack_value from
- rets[i] = rets[i].dup << r
- if pushed
- if rest_scr.pop
- pushs << pushed
- end
-
- if i - 2 >= 0
- rets[i-2].pop
- end
- end
- }
- end
-
- if false #|| insn.name =~ /test3/
- p ofrom
- p pops
- p rets
- p pushs_before
- p pushs
- p from
- exit
- end
-
- ret = ["#{insn.name}_SC_#{InstructionsLoader.complement_name(ofrom)}_#{complement_name(from)}",
- pops, rets, pushs_before, pushs, from]
- end
- end
-
- class SourceCodeGenerator
- def initialize insns
- @insns = insns
- end
-
- attr_reader :insns
-
- def generate
- raise "should not reach here"
- end
-
- def vpath
- @insns.vpath
- end
-
- def verbose?
- @insns.verbose?
- end
-
- def use_const?
- @insns.use_const?
- end
-
- def build_string
- @lines = []
- yield
- @lines.join("\n")
- end
-
- EMPTY_STRING = ''.freeze
-
- def commit str = EMPTY_STRING
- @lines << str
- end
-
- def comment str
- @lines << str if verbose?
- end
-
- def output_path(fn)
- d = @insns.destdir
- fn = File.join(d, fn) if d
- fn
- end
- end
-
- ###################################################################
- # vm.inc
- class VmBodyGenerator < SourceCodeGenerator
- # vm.inc
- def generate
- vm_body = ''
- @insns.each{|insn|
- vm_body << "\n"
- vm_body << make_insn_def(insn)
- }
- src = vpath.read('template/vm.inc.tmpl')
- ERB.new(src).result(binding)
- end
-
- def generate_from_insnname insnname
- make_insn_def @insns[insnname.to_s]
- end
-
- #######
- private
-
- def make_header_prepare_stack insn
- comment " /* prepare stack status */"
-
- push_ba = insn.pushsc
- raise "unsupport" if push_ba[0].size > 0 && push_ba[1].size > 0
-
- n = 0
- push_ba.each {|pushs| n += pushs.length}
- commit " CHECK_VM_STACK_OVERFLOW_FOR_INSN(REG_CFP, #{n});" if n > 0
- push_ba.each{|pushs|
- pushs.each{|r|
- commit " PUSH(SCREG(#{r}));"
- }
- }
- end
-
- def make_header_operands insn
- comment " /* declare and get from iseq */"
-
- vars = insn.opes
- n = 0
- ops = []
-
- vars.each_with_index{|(type, var), i|
- if type == '...'
- break
- end
-
- # skip make operands when body has no reference to this operand
- # TODO: really needed?
- re = /\b#{var}\b/n
- if re =~ insn.body or re =~ insn.sp_inc or insn.rets.any?{|t, v| re =~ v} or re =~ 'ic' or re =~ 'ci' or re =~ 'cc'
- ops << " #{type} #{var} = (#{type})GET_OPERAND(#{i+1});"
- end
-
- n += 1
- }
- @opn = n
-
- # reverse or not?
- # ops.join
- commit ops.reverse
- end
-
- def make_header_default_operands insn
- vars = insn.defopes
-
- vars.each{|e|
- next if e[1] == '*'
- if use_const?
- commit " const #{e[0][0]} #{e[0][1]} = #{e[1]};"
- else
- commit " #define #{e[0][1]} #{e[1]}"
- end
- }
- end
-
- def make_footer_default_operands insn
- comment " /* declare and initialize default opes */"
- if use_const?
- commit
- else
- vars = insn.defopes
-
- vars.each{|e|
- next if e[1] == '*'
- commit "#undef #{e[0][1]}"
- }
- end
- end
-
- def make_header_stack_pops insn
- comment " /* declare and pop from stack */"
-
- n = 0
- pops = []
- vars = insn.pops
- vars.each_with_index{|iter, i|
- type, var, r = *iter
- if type == '...'
- break
- end
- if r
- pops << " #{type} #{var} = SCREG(#{r});"
- else
- pops << " #{type} #{var} = TOPN(#{n});"
- n += 1
- end
- }
- @popn = n
-
- # reverse or not?
- commit pops.reverse
- end
-
- def make_header_temporary_vars insn
- comment " /* declare temporary vars */"
-
- insn.tvars.each{|var|
- commit " #{var[0]} #{var[1]};"
- }
- end
-
- def make_header_stack_val insn
- comment "/* declare stack push val */"
-
- vars = insn.opes + insn.pops + insn.defopes.map{|e| e[0]}
-
- insn.rets.each{|var|
- if vars.all?{|e| e[1] != var[1]} && var[1] != '...'
- commit " #{var[0]} #{var[1]};"
- end
- }
- end
-
- def make_header_analysis insn
- commit " COLLECT_USAGE_INSN(BIN(#{insn.name}));"
- insn.opes.each_with_index{|op, i|
- commit " COLLECT_USAGE_OPERAND(BIN(#{insn.name}), #{i}, #{op[1]});"
- }
- end
-
- def make_header_pc insn
- commit " ADD_PC(1+#{@opn});"
- commit " PREFETCH(GET_PC());"
- end
-
- def make_header_popn insn
- comment " /* management */"
- commit " POPN(#{@popn});" if @popn > 0
- end
-
- def make_header_debug insn
- comment " /* for debug */"
- commit " DEBUG_ENTER_INSN(\"#{insn.name}\");"
- end
-
- def make_header_defines insn
- commit " #define CURRENT_INSN_#{insn.name} 1"
- commit " #define INSN_IS_SC() #{insn.sc ? 0 : 1}"
- commit " #define INSN_LABEL(lab) LABEL_#{insn.name}_##lab"
- commit " #define LABEL_IS_SC(lab) LABEL_##lab##_###{insn.sc.size == 0 ? 't' : 'f'}"
- end
-
- def each_footer_stack_val insn
- insn.rets.reverse_each{|v|
- break if v[1] == '...'
- yield v
- }
- end
-
- def make_footer_stack_val insn
- comment " /* push stack val */"
-
- n = 0
- each_footer_stack_val(insn){|v|
- n += 1 unless v[2]
- }
- commit " CHECK_VM_STACK_OVERFLOW_FOR_INSN(REG_CFP, #{n});" if n > 0
- each_footer_stack_val(insn){|v|
- if v[2]
- commit " SCREG(#{v[2]}) = #{v[1]};"
- else
- commit " PUSH(#{v[1]});"
- end
- }
- end
-
- def make_footer_undefs insn
- commit "#undef CURRENT_INSN_#{insn.name}"
- commit "#undef INSN_IS_SC"
- commit "#undef INSN_LABEL"
- commit "#undef LABEL_IS_SC"
- end
-
- def make_header insn
- commit "INSN_ENTRY(#{insn.name}){"
- make_header_prepare_stack insn
- commit "{"
- make_header_stack_val insn
- make_header_default_operands insn
- make_header_operands insn
- make_header_stack_pops insn
- make_header_temporary_vars insn
- #
- make_header_debug insn
- make_header_pc insn
- make_header_popn insn
- make_header_defines insn
- make_header_analysis insn
- commit "{"
- end
-
- def make_footer insn
- make_footer_stack_val insn
- make_footer_default_operands insn
- make_footer_undefs insn
- commit " END_INSN(#{insn.name});}}}"
- end
-
- def make_insn_def insn
- build_string do
- make_header insn
- if line = insn.body.instance_variable_get(:@line_no)
- file = insn.body.instance_variable_get(:@file)
- commit "#line #{line+1} \"#{file}\""
- commit insn.body
- commit '#line __CURRENT_LINE__ "__CURRENT_FILE__"'
- else
- commit insn.body
- end
- make_footer(insn)
- end
- end
- end
-
- ###################################################################
- # vmtc.inc
- class VmTCIncGenerator < SourceCodeGenerator
- def generate
-
- insns_table = build_string do
- @insns.each{|insn|
- commit " LABEL_PTR(#{insn.name}),"
- }
- end
-
- insn_end_table = build_string do
- @insns.each{|insn|
- commit " ELABEL_PTR(#{insn.name}),\n"
- }
- end
-
- ERB.new(vpath.read('template/vmtc.inc.tmpl')).result(binding)
- end
- end
-
- ###################################################################
- # insns_info.inc
- class InsnsInfoIncGenerator < SourceCodeGenerator
- def generate
- insns_info_inc
- end
-
- ###
- private
-
- def op2typesig op
- case op
- when /^OFFSET/
- "TS_OFFSET"
- when /^rb_num_t/
- "TS_NUM"
- when /^lindex_t/
- "TS_LINDEX"
- when /^VALUE/
- "TS_VALUE"
- when /^ID/
- "TS_ID"
- when /GENTRY/
- "TS_GENTRY"
- when /^IC/
- "TS_IC"
- when /^CALL_INFO/
- "TS_CALLINFO"
- when /^CALL_CACHE/
- "TS_CALLCACHE"
- when /^\.\.\./
- "TS_VARIABLE"
- when /^CDHASH/
- "TS_CDHASH"
- when /^ISEQ/
- "TS_ISEQ"
- when /rb_insn_func_t/
- "TS_FUNCPTR"
- else
- raise "unknown op type: #{op}"
- end
- end
-
- TYPE_CHARS = {
- 'TS_OFFSET' => 'O',
- 'TS_NUM' => 'N',
- 'TS_LINDEX' => 'L',
- 'TS_VALUE' => 'V',
- 'TS_ID' => 'I',
- 'TS_GENTRY' => 'G',
- 'TS_IC' => 'K',
- 'TS_CALLINFO' => 'C',
- 'TS_CALLCACHE' => 'E',
- 'TS_CDHASH' => 'H',
- 'TS_ISEQ' => 'S',
- 'TS_VARIABLE' => '.',
- 'TS_FUNCPTR' => 'F',
- }
-
- # insns_info.inc
- def insns_info_inc
- # insn_type_chars
- insn_type_chars = TYPE_CHARS.map{|t, c|
- "#define #{t} '#{c}'"
- }.join("\n")
-
- # insn_names
- insn_names = ''
- @insns.each{|insn|
- insn_names << " \"#{insn.name}\",\n"
- }
-
- # operands info
- operands_info = ''
- operands_num_info = ''
-
- @insns.each{|insn|
- opes = insn.opes
- operands_info << ' '
- ot = opes.map{|type, var|
- TYPE_CHARS.fetch(op2typesig(type))
- }
- operands_info << "\"#{ot.join}\"" << ",\n"
-
- num = opes.size + 1
- operands_num_info << " #{num},\n"
- }
-
- # stack num
- stack_num_info = ''
- @insns.each{|insn|
- num = insn.rets.size
- stack_num_info << " #{num},\n"
- }
-
- # stack increase
- stack_increase = ''
- @insns.each{|insn|
- stack_increase << <<-EOS
- case BIN(#{insn.name}):{
- #{insn.sp_increase_c_expr}
- }
- EOS
- }
- ERB.new(vpath.read('template/insns_info.inc.tmpl')).result(binding)
- end
- end
-
- ###################################################################
- # insns.inc
- class InsnsIncGenerator < SourceCodeGenerator
- def generate
- i=0
- insns = build_string do
- @insns.each{|insn|
- commit " %-30s = %d," % ["BIN(#{insn.name})", i]
- i+=1
- }
- end
-
- ERB.new(vpath.read('template/insns.inc.tmpl')).result(binding)
- end
- end
-
- ###################################################################
- # minsns.inc
- class MInsnsIncGenerator < SourceCodeGenerator
- def generate
- i=0
- defs = build_string do
- @insns.each{|insn|
- commit " rb_define_const(mYarvInsns, %-30s, INT2FIX(%d));" %
- ["\"I#{insn.name}\"", i]
- i+=1
- }
- end
- ERB.new(vpath.read('template/minsns.inc.tmpl')).result(binding)
- end
- end
-
- ###################################################################
- # optinsn.inc
- class OptInsnIncGenerator < SourceCodeGenerator
- def generate
- optinsn_inc
- end
-
- ###
- private
-
- def val_as_type op
- type = op[0][0]
- val = op[1]
-
- case type
- when /^long/, /^rb_num_t/, /^lindex_t/
- "INT2FIX(#{val})"
- when /^VALUE/
- val
- when /^ID/
- "INT2FIX(#{val})"
- when /^ISEQ/, /^rb_insn_func_t/
- val
- when /GENTRY/
- raise
- when /^\.\.\./
- raise
- else
- raise "type: #{type}"
- end
- end
-
- # optinsn.inc
- def optinsn_inc
- rule = ''
- opt_insns_map = Hash.new{|h, k| h[k] = []}
-
- @insns.each{|insn|
- next if insn.defopes.size == 0
- next if insn.type == :sc
- next if /^UNIFIED/ =~ insn.name.to_s
-
- originsn = insn.orig
- opt_insns_map[originsn] << insn
- }
-
- rule = build_string do
- opt_insns_map.each{|originsn, optinsns|
- commit "case BIN(#{originsn.name}):"
-
- optinsns.sort_by{|opti|
- opti.defopes.find_all{|e| e[1] == '*'}.size
- }.each{|opti|
- commit " if("
- i = 0
- commit " " + opti.defopes.map{|opinfo|
- i += 1
- next if opinfo[1] == '*'
- "insnobj->operands[#{i-1}] == #{val_as_type(opinfo)}"
- }.compact.join('&& ')
- commit " ){"
- idx = 0
- n = 0
- opti.defopes.each{|opinfo|
- if opinfo[1] == '*'
- if idx != n
- commit " insnobj->operands[#{idx}] = insnobj->operands[#{n}];"
- end
- idx += 1
- else
- # skip
- end
- n += 1
- }
- commit " insnobj->insn_id = BIN(#{opti.name});"
- commit " insnobj->operand_size = #{idx};"
- commit " break;\n }\n"
- }
- commit " break;";
- }
- end
-
- ERB.new(vpath.read('template/optinsn.inc.tmpl')).result(binding)
- end
- end
-
- ###################################################################
- # optunifs.inc
- class OptUnifsIncGenerator < SourceCodeGenerator
- def generate
- unif_insns_each = ''
- unif_insns = ''
- unif_insns_data = []
-
- insns = @insns.find_all{|insn| !insn.is_sc}
- insns.each{|insn|
- size = insn.unifs.size
- if size > 0
- insn.unifs.sort_by{|unif| -unif[1].size}.each_with_index{|unif, i|
-
- uni_insn, uni_insns = *unif
- uni_insns = uni_insns[1..-1]
- unif_insns_each << "static const int UNIFIED_#{insn.name}_#{i}[] = {" +
- " BIN(#{uni_insn.name}), #{uni_insns.size + 2},\n " +
- uni_insns.map{|e| "BIN(#{e.name})"}.join(", ") + "};\n"
- }
- else
-
- end
- if size > 0
- unif_insns << "static const int *const UNIFIED_#{insn.name}[] = {(int *)#{size+1},\n"
- unif_insns << (0...size).map{|e| " UNIFIED_#{insn.name}_#{e}"}.join(",\n") + "};\n"
- unif_insns_data << " UNIFIED_#{insn.name}"
- else
- unif_insns_data << " 0"
- end
- }
- unif_insns_data = "static const int *const *const unified_insns_data[] = {\n" +
- unif_insns_data.join(",\n") + "};\n"
- ERB.new(vpath.read('template/optunifs.inc.tmpl')).result(binding)
- end
- end
-
- ###################################################################
- # opt_sc.inc
- class OptSCIncGenerator < SourceCodeGenerator
- def generate
- sc_insn_info = []
- @insns.each{|insn|
- insns = insn.sc
- if insns.size > 0
- insns = ['SC_ERROR'] + insns.map{|e| " BIN(#{e.name})"}
- else
- insns = Array.new(6){'SC_ERROR'}
- end
- sc_insn_info << " {\n#{insns.join(",\n")}}"
- }
- sc_insn_info = sc_insn_info.join(",\n")
-
- sc_insn_next = @insns.map{|insn|
- " SCS_#{InstructionsLoader.complement_name(insn.nextsc).upcase}" +
- (verbose? ? " /* #{insn.name} */" : '')
- }.join(",\n")
- ERB.new(vpath.read('template/opt_sc.inc.tmpl')).result(binding)
- end
- end
-
- ###################################################################
- # yasmdata.rb
- class YASMDataRbGenerator < SourceCodeGenerator
- def generate
- insn_id2no = ''
- @insns.each_with_index{|insn, i|
- insn_id2no << " :#{insn.name} => #{i},\n"
- }
- ERB.new(vpath.read('template/yasmdata.rb.tmpl')).result(binding)
- end
- end
-
- ###################################################################
- # yarvarch.*
- class YARVDocGenerator < SourceCodeGenerator
- def generate
-
- end
-
- def desc lang
- d = ''
- i = 0
- cat = nil
- @insns.each{|insn|
- seq = insn.opes.map{|t,v| v}.join(' ')
- before = insn.pops.reverse.map{|t,v| v}.join(' ')
- after = insn.rets.reverse.map{|t,v| v}.join(' ')
-
- if cat != insn.comm[:c]
- d << "** #{insn.comm[:c]}\n\n"
- cat = insn.comm[:c]
- end
-
- d << "*** #{insn.name}\n"
- d << "\n"
- d << insn.comm[lang] + "\n\n"
- d << ":instruction sequence: 0x%02x #{seq}\n" % i
- d << ":stack: #{before} => #{after}\n\n"
- i+=1
- }
- d
- end
-
- def desc_ja
- d = desc :j
- ERB.new(vpath.read('template/yarvarch.ja')).result(binding)
- end
-
- def desc_en
- d = desc :e
- ERB.new(vpath.read('template/yarvarch.en')).result(binding)
- end
- end
-
- class SourceCodeGenerator
- Files = { # codes
- 'vm.inc' => VmBodyGenerator,
- 'vmtc.inc' => VmTCIncGenerator,
- 'insns.inc' => InsnsIncGenerator,
- 'insns_info.inc' => InsnsInfoIncGenerator,
- # 'minsns.inc' => MInsnsIncGenerator,
- 'optinsn.inc' => OptInsnIncGenerator,
- 'optunifs.inc' => OptUnifsIncGenerator,
- 'opt_sc.inc' => OptSCIncGenerator,
- 'yasmdata.rb' => YASMDataRbGenerator,
- }
-
- def generate args = []
- args = Files.keys if args.empty?
- args.each{|fn|
- s = Files[fn].new(@insns).generate
- open(output_path(fn), 'w') {|f| f.puts(s)}
- }
- end
-
- def self.def_options(opt)
- opts = {
- :"insns.def" => 'insns.def',
- :"opope.def" => 'defs/opt_operand.def',
- :"unif.def" => 'defs/opt_insn_unif.def',
- }
-
- opt.on("-Dname", /\AOPT_(\w+)\z/, "enable VM option") {|s, v|
- opts[v] = true
- }
- opt.on("--enable=name[,name...]", Array,
- "enable VM options (without OPT_ prefix)") {|*a|
- a.each {|v| opts[v] = true}
- }
- opt.on("-Uname", /\AOPT_(\w+)\z/, "disable VM option") {|s, v|
- opts[v] = false
- }
- opt.on("--disable=name[,name...]", Array,
- "disable VM options (without OPT_ prefix)") {|*a|
- a.each {|v| opts[v] = false}
- }
- opt.on("-i", "--insnsdef=FILE", "--instructions-def",
- "instructions definition file") {|n|
- opts[:insns_def] = n
- }
- opt.on("-o", "--opt-operanddef=FILE", "--opt-operand-def",
- "vm option: operand definition file") {|n|
- opts[:opope_def] = n
- }
- opt.on("-u", "--opt-insnunifdef=FILE", "--opt-insn-unif-def",
- "vm option: instruction unification file") {|n|
- opts[:unif_def] = n
- }
- opt.on("-C", "--[no-]use-const",
- "use consts for default operands instead of macros") {|v|
- opts[:use_const] = v
- }
- opt.on("-d", "--destdir", "--output-directory=DIR",
- "make output file underneath DIR") {|v|
- opts[:destdir] = v
- }
- opt.on("-V", "--[no-]verbose") {|v|
- opts[:verbose] = v
- }
-
- vpath = VPath.new
- vpath.def_options(opt)
-
- proc {
- opts[:VPATH] = vpath
- build opts
- }
- end
-
- def self.build opts, vpath = ['./']
- opts[:VPATH] ||= VPath.new(*vpath)
- self.new InstructionsLoader.new(opts)
- end
- end
-end
-
diff --git a/tool/m4/_colorize_result_prepare.m4 b/tool/m4/_colorize_result_prepare.m4
new file mode 100644
index 0000000000..cc2bbaa703
--- /dev/null
+++ b/tool/m4/_colorize_result_prepare.m4
@@ -0,0 +1,33 @@
+# -*- Autoconf -*-
+AC_DEFUN([_COLORIZE_RESULT_PREPARE], [
+ msg_checking= msg_result_yes= msg_result_no= msg_result_other= msg_reset=
+ AS_CASE(["x${CONFIGURE_TTY}"],
+ [xyes|xalways],[configure_tty=1],
+ [xno|xnever], [configure_tty=0],
+ [AS_IF([test -t 1],
+ [configure_tty=1],
+ [configure_tty=0])])
+ AS_IF([test $configure_tty -eq 1], [
+ msg_begin="`tput smso 2>/dev/null`"
+ AS_CASE(["$msg_begin"], ['@<:@'*m],
+ [msg_begin="`echo "$msg_begin" | sed ['s/[0-9]*m$//']`"
+ msg_checking="${msg_begin}33m"
+ AS_IF([test ${TEST_COLORS:+set}], [
+ msg_result_yes=[`expr ":$TEST_COLORS:" : ".*:pass=\([^:]*\):"`]
+ msg_result_no=[`expr ":$TEST_COLORS:" : ".*:fail=\([^:]*\):"`]
+ msg_result_other=[`expr ":$TEST_COLORS:" : ".*:skip=\([^:]*\):"`]
+ ])
+ msg_result_yes="${msg_begin}${msg_result_yes:-32;1}m"
+ msg_result_no="${msg_begin}${msg_result_no:-31;1}m"
+ msg_result_other="${msg_begin}${msg_result_other:-33;1}m"
+ msg_reset="${msg_begin}m"
+ ])
+ AS_UNSET(msg_begin)
+ ])
+ AS_REQUIRE_SHELL_FN([colorize_result],
+ [AS_FUNCTION_DESCRIBE([colorize_result], [MSG], [Colorize result])],
+ [AS_CASE(["$[]1"],
+ [yes], [_AS_ECHO([${msg_result_yes}$[]1${msg_reset}])],
+ [no], [_AS_ECHO([${msg_result_no}$[]1${msg_reset}])],
+ [_AS_ECHO([${msg_result_other}$[]1${msg_reset}])])])
+])dnl
diff --git a/tool/m4/ac_msg_result.m4 b/tool/m4/ac_msg_result.m4
new file mode 100644
index 0000000000..bcc7a63d7d
--- /dev/null
+++ b/tool/m4/ac_msg_result.m4
@@ -0,0 +1,5 @@
+# -*- Autoconf -*-
+AC_DEFUN([AC_MSG_RESULT], [dnl
+{ _AS_ECHO_LOG([result: $1])
+COLORIZE_RESULT([$1]); dnl
+}])dnl
diff --git a/tool/m4/colorize_result.m4 b/tool/m4/colorize_result.m4
new file mode 100644
index 0000000000..19ff6d49e8
--- /dev/null
+++ b/tool/m4/colorize_result.m4
@@ -0,0 +1,9 @@
+# -*- Autoconf -*-
+AC_DEFUN([COLORIZE_RESULT], [AC_REQUIRE([_COLORIZE_RESULT_PREPARE])dnl
+ AS_LITERAL_IF([$1],
+ [m4_case([$1],
+ [yes], [_AS_ECHO([${msg_result_yes}$1${msg_reset}])],
+ [no], [_AS_ECHO([${msg_result_no}$1${msg_reset}])],
+ [_AS_ECHO([${msg_result_other}$1${msg_reset}])])],
+ [colorize_result "$1"]) dnl
+])dnl
diff --git a/tool/m4/ruby_append_option.m4 b/tool/m4/ruby_append_option.m4
new file mode 100644
index 0000000000..07b6537cb7
--- /dev/null
+++ b/tool/m4/ruby_append_option.m4
@@ -0,0 +1,5 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_APPEND_OPTION],
+ [# RUBY_APPEND_OPTION($1)
+ AS_CASE([" [$]{$1-} "],
+ [*" $2 "*], [], [' '], [ $1="$2"], [ $1="[$]$1 $2"])])dnl
diff --git a/tool/m4/ruby_append_options.m4 b/tool/m4/ruby_append_options.m4
new file mode 100644
index 0000000000..164a4ab654
--- /dev/null
+++ b/tool/m4/ruby_append_options.m4
@@ -0,0 +1,7 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_APPEND_OPTIONS],
+ [# RUBY_APPEND_OPTIONS($1)
+ for rb_opt in $2; do
+ AS_CASE([" [$]{$1-} "],
+ [*" [$]{rb_opt} "*], [], [' '], [ $1="[$]{rb_opt}"], [ $1="[$]$1 [$]{rb_opt}"])
+ done])dnl
diff --git a/tool/m4/ruby_check_builtin_func.m4 b/tool/m4/ruby_check_builtin_func.m4
new file mode 100644
index 0000000000..3fe5caf429
--- /dev/null
+++ b/tool/m4/ruby_check_builtin_func.m4
@@ -0,0 +1,10 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_CHECK_BUILTIN_FUNC], [dnl
+AC_CACHE_CHECK([for $1], AS_TR_SH(rb_cv_builtin_$1),
+ [AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([int foo;], [$2;])],
+ [AS_TR_SH(rb_cv_builtin_$1)=yes],
+ [AS_TR_SH(rb_cv_builtin_$1)=no])])
+AS_IF([test "${AS_TR_SH(rb_cv_builtin_$1)}" != no], [
+ AC_DEFINE(AS_TR_CPP(HAVE_BUILTIN_$1))
+])])dnl
diff --git a/tool/m4/ruby_check_builtin_setjmp.m4 b/tool/m4/ruby_check_builtin_setjmp.m4
new file mode 100644
index 0000000000..a4289e2e9d
--- /dev/null
+++ b/tool/m4/ruby_check_builtin_setjmp.m4
@@ -0,0 +1,27 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_CHECK_BUILTIN_SETJMP], [
+AS_IF([test x"${ac_cv_func___builtin_setjmp}" = xyes], [
+ unset ac_cv_func___builtin_setjmp
+])
+AC_CACHE_CHECK(for __builtin_setjmp, ac_cv_func___builtin_setjmp,
+ [
+ ac_cv_func___builtin_setjmp=no
+ for cast in "" "(void **)"; do
+ RUBY_WERROR_FLAG(
+ [AC_TRY_LINK([@%:@include <setjmp.h>
+ @%:@include <stdio.h>
+ jmp_buf jb;
+ @%:@ifdef NORETURN
+ NORETURN(void t(void));
+ @%:@endif
+ void t(void) {__builtin_longjmp($cast jb, 1);}
+ int jump(void) {(void)(__builtin_setjmp($cast jb) ? 1 : 0); return 0;}],
+ [
+ void (*volatile f)(void) = t;
+ if (!jump()) printf("%d\n", f != 0);
+ ],
+ [ac_cv_func___builtin_setjmp="yes with cast ($cast)"])
+ ])
+ test "$ac_cv_func___builtin_setjmp" = no || break
+ done])
+])dnl
diff --git a/tool/m4/ruby_check_printf_prefix.m4 b/tool/m4/ruby_check_printf_prefix.m4
new file mode 100644
index 0000000000..9007c18c0a
--- /dev/null
+++ b/tool/m4/ruby_check_printf_prefix.m4
@@ -0,0 +1,30 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_CHECK_PRINTF_PREFIX], [
+AC_CACHE_CHECK([for printf prefix for $1], [rb_cv_pri_prefix_]AS_TR_SH($1),[
+ [rb_cv_pri_prefix_]AS_TR_SH($1)=[NONE]
+ RUBY_WERROR_FLAG(RUBY_APPEND_OPTIONS(CFLAGS, $rb_cv_wsuppress_flags)
+ for pri in $2; do
+ AC_TRY_COMPILE(
+ [@%:@include <stdio.h>
+ @%:@include <stddef.h>
+ @%:@ifdef __GNUC__
+ @%:@if defined __MINGW_PRINTF_FORMAT
+ @%:@define PRINTF_ARGS(decl, string_index, first_to_check) \
+ decl __attribute__((format(__MINGW_PRINTF_FORMAT, string_index, first_to_check)))
+ @%:@else
+ @%:@define PRINTF_ARGS(decl, string_index, first_to_check) \
+ decl __attribute__((format(printf, string_index, first_to_check)))
+ @%:@endif
+ @%:@else
+ @%:@define PRINTF_ARGS(decl, string_index, first_to_check) decl
+ @%:@endif
+ PRINTF_ARGS(void test_sprintf(const char*, ...), 1, 2);],
+ [printf("%]${pri}[d", (]$1[)42);
+ test_sprintf("%]${pri}[d", (]$1[)42);],
+ [rb_cv_pri_prefix_]AS_TR_SH($1)[=[$pri]; break])
+ done)])
+AS_IF([test "[$rb_cv_pri_prefix_]AS_TR_SH($1)" != NONE], [
+ AC_DEFINE_UNQUOTED([PRI_]m4_ifval($3,$3,AS_TR_CPP(m4_bpatsubst([$1],[_t$])))[_PREFIX],
+ "[$rb_cv_pri_prefix_]AS_TR_SH($1)")
+])
+])dnl
diff --git a/tool/m4/ruby_check_setjmp.m4 b/tool/m4/ruby_check_setjmp.m4
new file mode 100644
index 0000000000..59f38581b8
--- /dev/null
+++ b/tool/m4/ruby_check_setjmp.m4
@@ -0,0 +1,17 @@
+# -*- Autoconf -*-
+# used for AC_ARG_WITH(setjmp-type)
+AC_DEFUN([RUBY_CHECK_SETJMP], [
+AC_CACHE_CHECK([for ]$1[ as a macro or function], ac_cv_func_$1,
+ [AC_TRY_COMPILE([
+@%:@include <setjmp.h>
+]AC_INCLUDES_DEFAULT([$3])[
+@%:@define JMPARGS_1 env
+@%:@define JMPARGS_2 env,1
+@%:@define JMPARGS JMPARGS_]m4_ifval($2,2,1)[
+],
+ m4_ifval($2,$2,jmp_buf)[ env; $1(JMPARGS);],
+ ac_cv_func_$1=yes,
+ ac_cv_func_$1=no)]
+)
+AS_IF([test "$ac_cv_func_]$1[" = yes], [AC_DEFINE([HAVE_]AS_TR_CPP($1), 1)])
+])dnl
diff --git a/tool/m4/ruby_check_signedness.m4 b/tool/m4/ruby_check_signedness.m4
new file mode 100644
index 0000000000..5376efa208
--- /dev/null
+++ b/tool/m4/ruby_check_signedness.m4
@@ -0,0 +1,5 @@
+# -*- Autoconf -*-
+dnl RUBY_CHECK_SIGNEDNESS [typename] [if-signed] [if-unsigned] [included]
+AC_DEFUN([RUBY_CHECK_SIGNEDNESS], [dnl
+ AC_COMPILE_IFELSE([AC_LANG_BOOL_COMPILE_TRY([AC_INCLUDES_DEFAULT([$4])], [($1)-1 > 0])],
+ [$3], [$2])])dnl
diff --git a/tool/m4/ruby_check_sizeof.m4 b/tool/m4/ruby_check_sizeof.m4
new file mode 100644
index 0000000000..38d7918f2e
--- /dev/null
+++ b/tool/m4/ruby_check_sizeof.m4
@@ -0,0 +1,108 @@
+# -*- Autoconf -*-
+dnl RUBY_CHECK_SIZEOF [typename], [maybe same size types], [macros], [include]
+AC_DEFUN([RUBY_CHECK_SIZEOF],
+[dnl
+AS_VAR_PUSHDEF([rbcv_var], [rbcv_sizeof_var])dnl
+AS_VAR_PUSHDEF([cond], [rbcv_sizeof_cond])dnl
+AS_VAR_PUSHDEF([t], [rbcv_sizeof_type])dnl
+AS_VAR_PUSHDEF([s], [rbcv_sizeof_size])dnl
+]
+[m4_bmatch([$1], [\.], [], [if test "$universal_binary" = yes; then])
+AC_CACHE_CHECK([size of $1], [AS_TR_SH([ac_cv_sizeof_$1])], [
+ unset AS_TR_SH(ac_cv_sizeof_$1)
+ rbcv_var="
+typedef m4_bpatsubst([$1], [\..*]) ac__type_sizeof_;
+static ac__type_sizeof_ *rbcv_ptr;
+@%:@define AS_TR_CPP(SIZEOF_$1) sizeof((*rbcv_ptr)[]m4_bmatch([$1], [\.], .m4_bpatsubst([$1], [^[^.]*\.])))
+"
+ m4_ifval([$2], [test -z "${AS_TR_SH(ac_cv_sizeof_$1)+set}" && {
+ for t in $2; do
+ AC_COMPILE_IFELSE(
+ [AC_LANG_BOOL_COMPILE_TRY(AC_INCLUDES_DEFAULT([$4]
+ [$rbcv_var]),
+ [AS_TR_CPP(SIZEOF_$1) == sizeof($t)])], [
+ AS_TR_SH(ac_cv_sizeof_$1)=AS_TR_CPP([SIZEOF_]$t)
+ break])
+ done
+ }], [
+ AC_COMPUTE_INT([AS_TR_SH(ac_cv_sizeof_$1)], [AS_TR_CPP(SIZEOF_$1)],
+ [AC_INCLUDES_DEFAULT([$4])
+$rbcv_var],
+ [AS_TR_SH(ac_cv_sizeof_$1)=])
+ ])
+ unset cond
+ m4_ifval([$3], [test -z "${AS_TR_SH(ac_cv_sizeof_$1)+set}" && {
+ for s in 32 64 128; do
+ for t in $3; do
+ cond="${cond}
+@%:@${cond+el}if defined(__${t}${s}__) || defined(__${t}${s}) || defined(_${t}${s}) || defined(${t}${s})"
+ hdr="AC_INCLUDES_DEFAULT([$4
+@%:@if defined(__${t}${s}__) || defined(__${t}${s}) || defined(_${t}${s}) || defined(${t}${s})
+@%:@ define AS_TR_CPP(HAVE_$1) 1
+@%:@else
+@%:@ define AS_TR_CPP(HAVE_$1) 0
+@%:@endif])"
+ AC_COMPILE_IFELSE([AC_LANG_BOOL_COMPILE_TRY([$hdr], [!AS_TR_CPP(HAVE_$1)])], [continue])
+ AC_COMPILE_IFELSE([AC_LANG_BOOL_COMPILE_TRY([$hdr]
+ [$rbcv_var],
+ [AS_TR_CPP(HAVE_$1) == (AS_TR_CPP(SIZEOF_$1) == ($s / $rb_cv_char_bit))])],
+ [AS_TR_SH(ac_cv_sizeof_$1)="${AS_TR_SH(ac_cv_sizeof_$1)+${AS_TR_SH(ac_cv_sizeof_$1)-} }${t}${s}"; continue])
+ AC_COMPILE_IFELSE([AC_LANG_BOOL_COMPILE_TRY([$hdr]
+[
+@%:@if AS_TR_CPP(HAVE_$1)
+$rbcv_var
+@%:@else
+@%:@define AS_TR_CPP(SIZEOF_$1) 0
+@%:@endif
+],
+ [AS_TR_CPP(HAVE_$1) == (AS_TR_CPP(SIZEOF_$1) == (m4_bmatch([$2], [^[0-9][0-9]*$], [$2], [($s / $rb_cv_char_bit)])))])],
+ [AS_TR_SH(ac_cv_sizeof_$1)="${AS_TR_SH(ac_cv_sizeof_$1)+${AS_TR_SH(ac_cv_sizeof_$1)-} }${t}${s}m4_bmatch([$2], [^[0-9][0-9]*$], [:$2])"])
+ done
+ done
+ }])
+ test "${AS_TR_SH(ac_cv_sizeof_$1)@%:@@<:@1-9@:>@}" = "${AS_TR_SH(ac_cv_sizeof_$1)}" &&
+ m4_ifval([$2][$3],
+ [test "${AS_TR_SH(ac_cv_sizeof_$1)@%:@SIZEOF_}" = "${AS_TR_SH(ac_cv_sizeof_$1)}" && ]){
+ test "$universal_binary" = yes && cross_compiling=yes
+ AC_COMPUTE_INT([t], AS_TR_CPP(SIZEOF_$1), [AC_INCLUDES_DEFAULT([$4])]
+[${cond+$cond
+@%:@else}
+$rbcv_var
+${cond+@%:@endif}
+@%:@ifndef AS_TR_CPP(SIZEOF_$1)
+@%:@define AS_TR_CPP(SIZEOF_$1) 0
+@%:@endif], [t=0])
+ test "$universal_binary" = yes && cross_compiling=$real_cross_compiling
+ AS_IF([test ${t-0} != 0], [
+ AS_TR_SH(ac_cv_sizeof_$1)="${AS_TR_SH(ac_cv_sizeof_$1)+${AS_TR_SH(ac_cv_sizeof_$1)-} }${t}"
+ ])
+ }
+ : ${AS_TR_SH(ac_cv_sizeof_$1)=0}
+])
+{
+ unset cond
+ for t in ${AS_TR_SH(ac_cv_sizeof_$1)-}; do
+ AS_CASE(["$t"],
+ [[[0-9]*|SIZEOF_*]], [
+ ${cond+echo "@%:@else"}
+ echo "[@%:@define ]AS_TR_CPP(SIZEOF_$1) $t"
+ break
+ ],
+ [
+ s=`expr $t : ['.*[^0-9]\([0-9][0-9]*\)$']`
+ AS_CASE([$t], [*:*], [t="${t%:*}"], [s=`expr $s / $rb_cv_char_bit`])
+ echo "@%:@${cond+el}if defined(__${t}__) || defined(__${t}) || defined(_${t}) || defined($t)"
+ echo "@%:@define AS_TR_CPP(SIZEOF_$1) $s"
+ cond=1
+ ])
+ done
+ ${cond+echo "@%:@endif"}
+} >> confdefs.h
+m4_bmatch([$1], [\.], [], [else
+AC_CHECK_SIZEOF([$1], 0, [$4])
+fi])
+AS_VAR_POPDEF([rbcv_var])dnl
+AS_VAR_POPDEF([cond])dnl
+AS_VAR_POPDEF([t])dnl
+AS_VAR_POPDEF([s])dnl
+])dnl
diff --git a/tool/m4/ruby_check_sysconf.m4 b/tool/m4/ruby_check_sysconf.m4
new file mode 100644
index 0000000000..f6b247a16f
--- /dev/null
+++ b/tool/m4/ruby_check_sysconf.m4
@@ -0,0 +1,13 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_CHECK_SYSCONF], [dnl
+AC_CACHE_CHECK([whether _SC_$1 is supported], rb_cv_have_sc_[]m4_tolower($1),
+ [AC_TRY_COMPILE([#include <unistd.h>
+ ],
+ [_SC_$1 >= 0],
+ rb_cv_have_sc_[]m4_tolower($1)=yes,
+ rb_cv_have_sc_[]m4_tolower($1)=no)
+ ])
+AS_IF([test "$rb_cv_have_sc_[]m4_tolower($1)" = yes], [
+ AC_DEFINE(HAVE__SC_$1)
+])
+])dnl
diff --git a/tool/m4/ruby_cppoutfile.m4 b/tool/m4/ruby_cppoutfile.m4
new file mode 100644
index 0000000000..7c81c4f354
--- /dev/null
+++ b/tool/m4/ruby_cppoutfile.m4
@@ -0,0 +1,18 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_CPPOUTFILE],
+[AC_CACHE_CHECK(whether ${CPP} accepts -o, rb_cv_cppoutfile,
+[save_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS='-o conftest-1.i'
+rb_cv_cppoutfile=no
+AC_TRY_CPP([test-for-cppout],
+ [grep test-for-cppout conftest-1.i > /dev/null && rb_cv_cppoutfile=yes])
+CPPFLAGS="$save_CPPFLAGS"
+rm -f conftest*])
+AS_IF([test "$rb_cv_cppoutfile" = yes], [
+ CPPOUTFILE='-o conftest.i'
+], [test "$rb_cv_cppoutfile" = no], [
+ CPPOUTFILE='> conftest.i'
+], [test -n "$rb_cv_cppoutfile"], [
+ CPPOUTFILE="$rb_cv_cppoutfile"
+])
+AC_SUBST(CPPOUTFILE)])dnl
diff --git a/tool/m4/ruby_decl_attribute.m4 b/tool/m4/ruby_decl_attribute.m4
new file mode 100644
index 0000000000..3187b9be60
--- /dev/null
+++ b/tool/m4/ruby_decl_attribute.m4
@@ -0,0 +1,45 @@
+# -*- Autoconf -*-
+dnl RUBY_DECL_ATTRIBUTE(attrib, macroname, cachevar, condition, type, code)
+AC_DEFUN([RUBY_DECL_ATTRIBUTE], [dnl
+m4_ifval([$2], dnl
+ [AS_VAR_PUSHDEF([attrib], m4_bpatsubst([$2], [(.*)], []))], dnl
+ [AS_VAR_PUSHDEF([attrib], m4_toupper(m4_format(%.4s, [$5]))[_]AS_TR_CPP($1))] dnl
+)dnl
+m4_ifval([$3], dnl
+ [AS_VAR_PUSHDEF([rbcv],[$3])], dnl
+ [AS_VAR_PUSHDEF([rbcv],[rb_cv_]m4_format(%.4s, [$5])[_][$1])]dnl
+)dnl
+m4_pushdef([attrib_code],[m4_bpatsubst([$1],["],[\\"])])dnl
+m4_pushdef([attrib_params],[m4_bpatsubst([$2(x)],[^[^()]*(\([^()]*\)).*],[\1])])dnl
+m4_ifval([$4], [rbcv_cond=["$4"]; test "$rbcv_cond" || unset rbcv_cond])
+AC_CACHE_CHECK(for m4_ifval([$2],[m4_bpatsubst([$2], [(.*)], [])],[$1]) [$5] attribute, rbcv, dnl
+[rbcv=x
+RUBY_WERROR_FLAG([
+for mac in \
+ "__attribute__ ((attrib_code)) x" \
+ "x __attribute__ ((attrib_code))" \
+ "__declspec(attrib_code) x" \
+ x; do
+ m4_ifval([$4],mac="$mac"${rbcv_cond+" /* only if $rbcv_cond */"})
+ AC_TRY_COMPILE(
+ m4_ifval([$4],${rbcv_cond+[@%:@if ]$rbcv_cond})
+[@%:@define ]attrib[](attrib_params)[ $mac]
+m4_ifval([$4],${rbcv_cond+[@%:@else]}
+${rbcv_cond+[@%:@define ]attrib[](attrib_params)[ x]}
+${rbcv_cond+[@%:@endif]})
+$6
+@%:@define mesg ("")
+@%:@define san "address"
+ attrib[](attrib_params)[;], [],
+ [rbcv="$mac"; break])
+done
+])])
+AS_IF([test "$rbcv" != x], [
+ RUBY_DEFINE_IF(m4_ifval([$4],[${rbcv_cond}]), attrib[](attrib_params)[], $rbcv)
+])
+m4_ifval([$4], [unset rbcv_cond]) dnl
+m4_popdef([attrib_params])dnl
+m4_popdef([attrib_code])dnl
+AS_VAR_POPDEF([attrib])dnl
+AS_VAR_POPDEF([rbcv])dnl
+])dnl
diff --git a/tool/m4/ruby_default_arch.m4 b/tool/m4/ruby_default_arch.m4
new file mode 100644
index 0000000000..a53bb5fc41
--- /dev/null
+++ b/tool/m4/ruby_default_arch.m4
@@ -0,0 +1,11 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_DEFAULT_ARCH], [
+AC_MSG_CHECKING([arch option])
+AS_CASE([$1],
+ [*64], [ARCH_FLAG=-m64],
+ [[i[3-6]86]], [ARCH_FLAG=-m32],
+ [AC_MSG_ERROR(unknown target architecture: $target_archs)]
+ )
+AC_MSG_RESULT([$ARCH_FLAG])
+])
+dnl
diff --git a/tool/m4/ruby_define_if.m4 b/tool/m4/ruby_define_if.m4
new file mode 100644
index 0000000000..b18b104aad
--- /dev/null
+++ b/tool/m4/ruby_define_if.m4
@@ -0,0 +1,12 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_DEFINE_IF], [dnl
+ m4_ifval([$1], [AS_LITERAL_IF([$1], [], [test "X$1" = X || ])cat <<EOH >> confdefs.h
+@%:@if $1
+EOH
+])dnl
+AC_DEFINE_UNQUOTED($2, $3)dnl
+ m4_ifval([$1], [AS_LITERAL_IF([$1], [], [test "X$1" = X || ])cat <<EOH >> confdefs.h
+@%:@endif /* $1 */
+EOH
+])dnl
+])dnl
diff --git a/tool/m4/ruby_defint.m4 b/tool/m4/ruby_defint.m4
new file mode 100644
index 0000000000..e5b46a788f
--- /dev/null
+++ b/tool/m4/ruby_defint.m4
@@ -0,0 +1,40 @@
+# -*- Autoconf -*-
+dnl RUBY_DEFINT TYPENAME, SIZE, [UNSIGNED], [INCLUDES = DEFAULT-INCLUDES]
+AC_DEFUN([RUBY_DEFINT], [dnl
+AS_VAR_PUSHDEF([cond], [rb_defint_cond])dnl
+AS_VAR_PUSHDEF([type], [rb_defint_type])dnl
+AC_CACHE_CHECK([for $1], [rb_cv_type_$1],
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT([$4])
+typedef $1 t; int s = sizeof(t) == 42;])],
+ [rb_cv_type_$1=yes],
+ [AS_CASE([m4_bmatch([$2], [^[1-9][0-9]*$], $2, [$ac_cv_sizeof_]AS_TR_SH($2))],
+ ["1"], [ rb_cv_type_$1="m4_if([$3], [], [signed ], [$3 ])char"],
+ ["$ac_cv_sizeof_short"], [ rb_cv_type_$1="m4_if([$3], [], [], [$3 ])short"],
+ ["$ac_cv_sizeof_int"], [ rb_cv_type_$1="m4_if([$3], [], [], [$3 ])int"],
+ ["$ac_cv_sizeof_long"], [ rb_cv_type_$1="m4_if([$3], [], [], [$3 ])long"],
+ ["$ac_cv_sizeof_long_long"], [ rb_cv_type_$1="m4_if([$3], [], [], [$3 ])long long"],
+ ["${ac_cv_sizeof___int64@%:@*:}"], [ rb_cv_type_$1="m4_if([$3], [], [], [$3 ])__int64"],
+ ["${ac_cv_sizeof___int128@%:@*:}"], [ rb_cv_type_$1="m4_if([$3], [], [], [$3 ])__int128"],
+ [ rb_cv_type_$1=no])])])
+AS_IF([test "${rb_cv_type_$1}" != no], [
+ type="${rb_cv_type_$1@%:@@%:@unsigned }"
+ AS_IF([test "$type" != yes && eval 'test -n "${ac_cv_sizeof_'$type'+set}"'], [
+ eval cond='"${ac_cv_sizeof_'$type'}"'
+ AS_CASE([$cond], [*:*], [
+ cond=AS_TR_CPP($type)
+ echo "@%:@if defined SIZEOF_"$cond" && SIZEOF_"$cond" > 0" >> confdefs.h
+ ], [cond=])
+ ], [cond=])
+ AC_DEFINE([HAVE_]AS_TR_CPP($1), 1)
+ AS_IF([test "${rb_cv_type_$1}" = yes], [
+ m4_bmatch([$2], [^[1-9][0-9]*$], [AC_CHECK_SIZEOF([$1], 0, [AC_INCLUDES_DEFAULT([$4])])],
+ [RUBY_CHECK_SIZEOF([$1], [$2], [], [AC_INCLUDES_DEFAULT([$4])])])
+ ], [
+ AC_DEFINE_UNQUOTED($1, [$rb_cv_type_$1])
+ AC_DEFINE_UNQUOTED([SIZEOF_]AS_TR_CPP($1), [SIZEOF_]AS_TR_CPP([$type]))
+ ])
+ test -n "$cond" && echo "@%:@endif /* $cond */" >> confdefs.h
+])
+AS_VAR_POPDEF([cond])dnl
+AS_VAR_POPDEF([type])dnl
+])dnl
diff --git a/tool/m4/ruby_dtrace_available.m4 b/tool/m4/ruby_dtrace_available.m4
new file mode 100644
index 0000000000..79586d152c
--- /dev/null
+++ b/tool/m4/ruby_dtrace_available.m4
@@ -0,0 +1,20 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_DTRACE_AVAILABLE],
+[AC_CACHE_CHECK(whether dtrace USDT is available, rb_cv_dtrace_available,
+[
+ echo "provider conftest{ probe fire(); };" > conftest_provider.d
+ rb_cv_dtrace_available=no
+ AS_FOR(opt, rb_dtrace_opt, ["-xnolibs" ""], [dnl
+ AS_IF([$DTRACE opt -h -o conftest_provider.h -s conftest_provider.d >/dev/null 2>/dev/null],
+ [], [continue])
+ AC_TRY_COMPILE([@%:@include "conftest_provider.h"], [CONFTEST_FIRE();],
+ [], [continue])
+ # DTrace is available on the system
+ rb_cv_dtrace_available=yes${rb_dtrace_opt:+"(opt)"}
+ break
+ ])
+ rm -f conftest.[co] conftest_provider.[dho]
+])
+AS_CASE(["$rb_cv_dtrace_available"], ["yes("*")"],
+ [DTRACE_OPT=`expr "$rb_cv_dtrace_available" : "yes(\(.*\))"`])
+])dnl
diff --git a/tool/m4/ruby_dtrace_postprocess.m4 b/tool/m4/ruby_dtrace_postprocess.m4
new file mode 100644
index 0000000000..9ef088b3f8
--- /dev/null
+++ b/tool/m4/ruby_dtrace_postprocess.m4
@@ -0,0 +1,30 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_DTRACE_POSTPROCESS],
+[AC_CACHE_CHECK(whether $DTRACE needs post processing, rb_cv_prog_dtrace_g,
+[
+ rb_cv_prog_dtrace_g=no
+ AS_IF([{
+ cat >conftest_provider.d <<_PROBES &&
+ provider conftest {
+ probe fire();
+ };
+_PROBES
+ $DTRACE ${DTRACE_OPT} -h -o conftest_provider.h -s conftest_provider.d >/dev/null 2>/dev/null &&
+ :
+ }], [
+ AC_TRY_COMPILE([@%:@include "conftest_provider.h"], [CONFTEST_FIRE();], [
+ AS_IF([{
+ cp -p conftest.${ac_objext} conftest.${ac_objext}.save &&
+ $DTRACE ${DTRACE_OPT} -G -s conftest_provider.d conftest.${ac_objext} 2>/dev/null &&
+ :
+ }], [
+ AS_IF([cmp -s conftest.o conftest.${ac_objext}.save], [
+ rb_cv_prog_dtrace_g=yes
+ ], [
+ rb_cv_prog_dtrace_g=rebuild
+ ])
+ ])])
+ ])
+ rm -f conftest.[co] conftest_provider.[dho]
+])
+])dnl
diff --git a/tool/m4/ruby_func_attribute.m4 b/tool/m4/ruby_func_attribute.m4
new file mode 100644
index 0000000000..b265fd41da
--- /dev/null
+++ b/tool/m4/ruby_func_attribute.m4
@@ -0,0 +1,7 @@
+# -*- Autoconf -*-
+dnl RUBY_FUNC_ATTRIBUTE(attrib, macroname, cachevar, condition)
+AC_DEFUN([RUBY_FUNC_ATTRIBUTE], [dnl
+ RUBY_DECL_ATTRIBUTE([$1], [$2], [$3], [$4],
+ [function], [@%:@define x int conftest_attribute_check(void)]
+ )
+])dnl
diff --git a/tool/m4/ruby_mingw32.m4 b/tool/m4/ruby_mingw32.m4
new file mode 100644
index 0000000000..f44fe5575c
--- /dev/null
+++ b/tool/m4/ruby_mingw32.m4
@@ -0,0 +1,24 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_MINGW32],
+[AS_CASE(["$host_os"],
+[cygwin*], [
+AC_CACHE_CHECK(for mingw32 environment, rb_cv_mingw32,
+[AC_TRY_CPP([
+#ifndef __MINGW32__
+# error
+#endif
+], rb_cv_mingw32=yes,rb_cv_mingw32=no)
+rm -f conftest*])
+AS_IF([test "$rb_cv_mingw32" = yes], [
+ target_os="mingw32"
+ : ${ac_tool_prefix:="`expr "$CC" : ['\(.*-\)g\?cc[^/]*$']`"}
+ AC_DEFINE(__USE_MINGW_ANSI_STDIO, 1) dnl for gnu_printf
+])
+])
+AS_CASE(["$target_os"], [mingw*msvc], [
+target_os="`echo ${target_os} | sed 's/msvc$//'`"
+])
+AS_CASE(["$target_cpu-$target_os"], [x86_64-mingw*], [
+target_cpu=x64
+])
+])dnl
diff --git a/tool/m4/ruby_prepend_option.m4 b/tool/m4/ruby_prepend_option.m4
new file mode 100644
index 0000000000..3e868a6831
--- /dev/null
+++ b/tool/m4/ruby_prepend_option.m4
@@ -0,0 +1,5 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_PREPEND_OPTION],
+ [# RUBY_PREPEND_OPTION($1)
+ AS_CASE([" [$]{$1-} "],
+ [*" $2 "*], [], [' '], [ $1="$2"], [ $1="$2 [$]$1"])])dnl
diff --git a/tool/m4/ruby_prog_gnu_ld.m4 b/tool/m4/ruby_prog_gnu_ld.m4
new file mode 100644
index 0000000000..8d484f9440
--- /dev/null
+++ b/tool/m4/ruby_prog_gnu_ld.m4
@@ -0,0 +1,10 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_PROG_GNU_LD],
+[AC_CACHE_CHECK(whether the linker is GNU ld, rb_cv_prog_gnu_ld,
+[AS_IF([`$CC $CFLAGS $CPPFLAGS $LDFLAGS --print-prog-name=ld 2>&1` -v 2>&1 | grep "GNU ld" > /dev/null], [
+ rb_cv_prog_gnu_ld=yes
+], [
+ rb_cv_prog_gnu_ld=no
+])])
+GNU_LD=$rb_cv_prog_gnu_ld
+AC_SUBST(GNU_LD)])dnl
diff --git a/tool/m4/ruby_replace_type.m4 b/tool/m4/ruby_replace_type.m4
new file mode 100644
index 0000000000..a7d2ffe938
--- /dev/null
+++ b/tool/m4/ruby_replace_type.m4
@@ -0,0 +1,58 @@
+# -*- Autoconf -*-
+dnl RUBY_REPLACE_TYPE [typename] [default type] [macro type] [included]
+AC_DEFUN([RUBY_REPLACE_TYPE], [dnl
+ AC_CHECK_TYPES([$1],
+ [n="patsubst([$1],["],[\\"])"],
+ [n="patsubst([$2],["],[\\"])"],
+ [$4])
+ AC_CACHE_CHECK([for convertible type of [$1]], rb_cv_[$1]_convertible, [
+ u= t=
+ AS_CASE(["$n "],
+ [*" signed "*], [ ],
+ [*" unsigned "*], [
+ u=U],
+ [RUBY_CHECK_SIGNEDNESS($n, [], [u=U], [$4])])
+ AS_IF([test x"$t" = x], [
+ for t in "long long" long int short; do
+ test -n "$u" && t="unsigned $t"
+ AC_COMPILE_IFELSE(
+ [AC_LANG_BOOL_COMPILE_TRY([AC_INCLUDES_DEFAULT([$4])]
+ [typedef $n rbcv_conftest_target_type;
+ typedef $t rbcv_conftest_replace_type;
+ extern rbcv_conftest_target_type rbcv_conftest_var;
+ extern rbcv_conftest_replace_type rbcv_conftest_var;
+ extern rbcv_conftest_target_type rbcv_conftest_func(void);
+ extern rbcv_conftest_replace_type rbcv_conftest_func(void);
+ ], [sizeof(rbcv_conftest_target_type) == sizeof(rbcv_conftest_replace_type)])],
+ [n="$t"; break])
+ done
+ ])
+ AS_CASE([" $n "],
+ [*" long long "*], [
+ t=LL],
+ [*" long "*], [
+ t=LONG],
+ [*" short "*], [
+ t=SHORT],
+ [
+ t=INT])
+ rb_cv_[$1]_convertible=${u}${t}])
+ AS_IF([test "${AS_TR_SH(ac_cv_type_[$1])}" = "yes"], [
+ n="$1"
+ ], [
+ AS_CASE(["${rb_cv_[$1]_convertible}"],
+ [*LL], [n="long long"],
+ [*LONG], [n="long"],
+ [*SHORT], [n="short"],
+ [n="int"])
+ AS_CASE(["${rb_cv_[$1]_convertible}"],
+ [U*], [n="unsigned $n"])
+ ])
+ AS_CASE("${rb_cv_[$1]_convertible}", [U*], [u=+1], [u=-1])
+ AC_DEFINE_UNQUOTED(rb_[$1], $n)
+ AC_DEFINE_UNQUOTED([SIGNEDNESS_OF_]AS_TR_CPP($1), $u)
+ AC_DEFINE_UNQUOTED([$3]2NUM[(v)], [${rb_cv_[$1]_convertible}2NUM(v)])
+ AC_DEFINE_UNQUOTED(NUM2[$3][(v)], [NUM2${rb_cv_[$1]_convertible}(v)])
+ AC_DEFINE_UNQUOTED(PRI_[$3]_PREFIX,
+ [PRI_`echo ${rb_cv_[$1]_convertible} | sed ['s/^U//']`_PREFIX])
+])dnl
diff --git a/tool/m4/ruby_rm_recursive.m4 b/tool/m4/ruby_rm_recursive.m4
new file mode 100644
index 0000000000..e33ba1d97a
--- /dev/null
+++ b/tool/m4/ruby_rm_recursive.m4
@@ -0,0 +1,18 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_RM_RECURSIVE], [
+m4_version_prereq([2.70], [-1], [
+# suppress error messages, rm: cannot remove 'conftest.dSYM', from
+# AC_EGREP_CPP with CFLAGS=-g on Darwin.
+AS_CASE([$build_os], [darwin*], [
+rm() {
+ rm_recursive=''
+ for arg do
+ AS_CASE("$arg",
+ [--*], [],
+ [-*r*], [break],
+ [conftest.*], [AS_IF([test -d "$arg"], [rm_recursive=-r; break])],
+ [])
+ done
+ command rm $rm_recursive "[$]@"
+}
+])])])dnl
diff --git a/tool/m4/ruby_setjmp_type.m4 b/tool/m4/ruby_setjmp_type.m4
new file mode 100644
index 0000000000..e054a544c5
--- /dev/null
+++ b/tool/m4/ruby_setjmp_type.m4
@@ -0,0 +1,52 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_SETJMP_TYPE], [
+RUBY_CHECK_BUILTIN_SETJMP
+RUBY_CHECK_SETJMP(_setjmpex, [], [@%:@include <setjmpex.h>])
+RUBY_CHECK_SETJMP(_setjmp)
+RUBY_CHECK_SETJMP(sigsetjmp, [sigjmp_buf])
+AC_MSG_CHECKING(for setjmp type)
+setjmp_suffix=
+unset setjmp_sigmask
+AC_ARG_WITH(setjmp-type,
+ AS_HELP_STRING([--with-setjmp-type], [select setjmp type]),
+ [
+ AS_CASE([$withval],
+ [__builtin_setjmp], [setjmp=__builtin_setjmp],
+ [_setjmp], [ setjmp_prefix=_],
+ [sigsetjmp,*], [ setjmp_prefix=sig setjmp_sigmask=`expr "$withval" : 'sigsetjmp\(,.*\)'`],
+ [sigsetjmp], [ setjmp_prefix=sig],
+ [setjmp], [ setjmp_prefix=],
+ [setjmpex], [ setjmp_prefix= setjmp_suffix=ex],
+ [''], [ unset setjmp_prefix],
+ [ AC_MSG_ERROR(invalid setjmp type: $withval)])], [unset setjmp_prefix])
+setjmp_cast=
+AS_IF([test ${setjmp_prefix+set}], [
+ AS_IF([test "${setjmp_prefix}" && eval test '$ac_cv_func_'${setjmp_prefix}setjmp${setjmp_suffix} = no], [
+ AC_MSG_ERROR(${setjmp_prefix}setjmp${setjmp_suffix} is not available)
+ ])
+], [{ AS_CASE("$ac_cv_func___builtin_setjmp", [yes*], [true], [false]) }], [
+ setjmp_cast=`expr "$ac_cv_func___builtin_setjmp" : "yes with cast (\(.*\))"`
+ setjmp_prefix=__builtin_
+ setjmp_suffix=
+], [test "$ac_cv_header_setjmpex_h:$ac_cv_func__setjmpex" = yes:yes], [
+ setjmp_prefix=
+ setjmp_suffix=ex
+], [test "$ac_cv_func__setjmp" = yes], [
+ setjmp_prefix=_
+ setjmp_suffix=
+], [test "$ac_cv_func_sigsetjmp" = yes], [
+ AS_CASE([$target_os],[solaris*|cygwin*],[setjmp_prefix=],[setjmp_prefix=sig])
+ setjmp_suffix=
+], [
+ setjmp_prefix=
+ setjmp_suffix=
+])
+AS_IF([test x$setjmp_prefix:$setjmp_sigmask = xsig:], [
+ setjmp_sigmask=,0
+])
+AC_MSG_RESULT(${setjmp_prefix}setjmp${setjmp_suffix}${setjmp_cast:+\($setjmp_cast\)}${setjmp_sigmask})
+AC_DEFINE_UNQUOTED([RUBY_SETJMP(env)], [${setjmp_prefix}setjmp${setjmp_suffix}($setjmp_cast(env)${setjmp_sigmask})])
+AC_DEFINE_UNQUOTED([RUBY_LONGJMP(env,val)], [${setjmp_prefix}longjmp($setjmp_cast(env),val)])
+AC_DEFINE_UNQUOTED(RUBY_JMP_BUF, ${setjmp_sigmask+${setjmp_prefix}}jmp_buf)
+AS_IF([test x$setjmp_suffix = xex], [AC_DEFINE_UNQUOTED(RUBY_USE_SETJMPEX, 1)])
+])dnl
diff --git a/tool/m4/ruby_stack_grow_direction.m4 b/tool/m4/ruby_stack_grow_direction.m4
new file mode 100644
index 0000000000..e930bc62ec
--- /dev/null
+++ b/tool/m4/ruby_stack_grow_direction.m4
@@ -0,0 +1,30 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_STACK_GROW_DIRECTION], [
+ AS_VAR_PUSHDEF([stack_grow_dir], [rb_cv_stack_grow_dir_$1])
+ AC_CACHE_CHECK(stack growing direction on $1, stack_grow_dir, [
+AS_CASE(["$1"],
+[m68*|x86*|x64|i?86|ia64|ppc*|sparc*|alpha*], [ $2=-1],
+[hppa*], [ $2=+1],
+[
+ AC_TRY_RUN([
+/* recurse to get rid of inlining */
+static int
+stack_growup_p(addr, n)
+ volatile int *addr, n;
+{
+ volatile int end;
+ if (n > 0)
+ return *addr = stack_growup_p(addr, n - 1);
+ else
+ return (&end > addr);
+}
+int main()
+{
+ int x;
+ return stack_growup_p(&x, 10);
+}
+], $2=-1, $2=+1, $2=0)
+ ])
+eval stack_grow_dir=\$$2])
+eval $2=\$stack_grow_dir
+AS_VAR_POPDEF([stack_grow_dir])])dnl
diff --git a/tool/m4/ruby_try_cflags.m4 b/tool/m4/ruby_try_cflags.m4
new file mode 100644
index 0000000000..86ab80e1e6
--- /dev/null
+++ b/tool/m4/ruby_try_cflags.m4
@@ -0,0 +1,12 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_TRY_CFLAGS], [
+ AC_MSG_CHECKING([whether ]$1[ is accepted as CFLAGS])
+ RUBY_WERROR_FLAG([
+ CFLAGS="[$]CFLAGS $1"
+ AC_TRY_COMPILE([$4], [$5],
+ [$2
+ AC_MSG_RESULT(yes)],
+ [$3
+ AC_MSG_RESULT(no)])
+ ])
+])dnl
diff --git a/tool/m4/ruby_try_ldflags.m4 b/tool/m4/ruby_try_ldflags.m4
new file mode 100644
index 0000000000..b275107ed9
--- /dev/null
+++ b/tool/m4/ruby_try_ldflags.m4
@@ -0,0 +1,15 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_TRY_LDFLAGS], [
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="[$]LDFLAGS $1"
+ AC_MSG_CHECKING([whether $1 is accepted as LDFLAGS])
+ RUBY_WERROR_FLAG([
+ AC_TRY_LINK([$4], [$5],
+ [$2
+ AC_MSG_RESULT(yes)],
+ [$3
+ AC_MSG_RESULT(no)])
+ ])
+ LDFLAGS="$save_LDFLAGS"
+ save_LDFLAGS=
+])dnl
diff --git a/tool/m4/ruby_type_attribute.m4 b/tool/m4/ruby_type_attribute.m4
new file mode 100644
index 0000000000..6ddaa20c53
--- /dev/null
+++ b/tool/m4/ruby_type_attribute.m4
@@ -0,0 +1,8 @@
+# -*- Autoconf -*-
+dnl RUBY_TYPE_ATTRIBUTE(attrib, macroname, cachevar, condition)
+AC_DEFUN([RUBY_TYPE_ATTRIBUTE], [dnl
+ RUBY_DECL_ATTRIBUTE([$1], [$2], [$3], [$4],
+ [type], [
+@%:@define x struct conftest_attribute_check {int i;}
+])
+])dnl
diff --git a/tool/m4/ruby_universal_arch.m4 b/tool/m4/ruby_universal_arch.m4
new file mode 100644
index 0000000000..dd9ad7ee46
--- /dev/null
+++ b/tool/m4/ruby_universal_arch.m4
@@ -0,0 +1,90 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_UNIVERSAL_ARCH], [
+# RUBY_UNIVERSAL_ARCH begin
+ARCH_FLAG=`expr " $CXXFLAGS " : ['.* \(-m[0-9][0-9]*\) ']`
+test ${CXXFLAGS+set} && CXXFLAGS=`echo "$CXXFLAGS" | sed [-e 's/ *-arch *[^ ]*//g' -e 's/ *-m32//g' -e 's/ *-m64//g']`
+ARCH_FLAG=`expr " $CFLAGS " : ['.* \(-m[0-9][0-9]*\) ']`
+test ${CFLAGS+set} && CFLAGS=`echo "$CFLAGS" | sed [-e 's/ *-arch *[^ ]*//g' -e 's/ *-m32//g' -e 's/ *-m64//g']`
+test ${LDFLAGS+set} && LDFLAGS=`echo "$LDFLAGS" | sed [-e 's/ *-arch *[^ ]*//g' -e 's/ *-m32//g' -e 's/ *-m64//g']`
+unset universal_binary universal_archnames
+AS_IF([test ${target_archs+set}], [
+ AC_MSG_CHECKING([target architectures])
+ target_archs=`echo $target_archs | tr , ' '`
+ # /usr/lib/arch_tool -archify_list $TARGET_ARCHS
+ for archs in $target_archs
+ do
+ AS_CASE([",$universal_binary,"],[*",$archs,"*], [],[
+ cpu=$archs
+ cpu=`echo $cpu | sed 's/-.*-.*//'`
+ universal_binary="${universal_binary+$universal_binary,}$cpu"
+ universal_archnames="${universal_archnames} ${archs}=${cpu}"
+ ARCH_FLAG="${ARCH_FLAG+$ARCH_FLAG }-arch $archs"
+ ])
+ done
+ target_archs="$universal_binary"
+ unset universal_binary
+ AS_CASE(["$target_archs"],
+ [*,*], [universal_binary=yes],
+ [unset universal_archnames])
+ AC_MSG_RESULT([$target_archs])
+
+ target=`echo $target | sed "s/^$target_cpu-/-/"`
+ target_alias=`echo $target_alias | sed "s/^$target_cpu-/-/"`
+ AS_IF([test "${universal_binary-no}" = yes], [
+ AC_SUBST(try_header,try_compile)
+ target_cpu=universal
+ real_cross_compiling=$cross_compiling
+ ], [
+ AS_IF([test x"$target_cpu" != x"${target_archs}"], [
+ echo 'int main(){return 0;}' > conftest.c
+ AS_IF([$CC $CFLAGS $ARCH_FLAG -o conftest conftest.c > /dev/null 2>&1], [
+ rm -fr conftest.*
+ ], [
+ RUBY_DEFAULT_ARCH("$target_archs")
+ ])
+ ])
+ target_cpu=${target_archs}
+ ])
+ AS_CASE(["$target"], [-*], [ target="$target_cpu${target}"])
+ AS_CASE(["$target_alias"], [-*], [ target_alias="$target_cpu${target_alias}"])
+], [
+ AS_IF([test x"$target_alias" = x], [
+ AS_CASE(["$target_os"],
+ [darwin*], [
+ AC_MSG_CHECKING([for real target cpu])
+ target=`echo $target | sed "s/^$target_cpu-/-/"`
+ target_cpu=`$CC -E - 2>/dev/null <<EOF |
+#ifdef __x86_64__
+"processor-name=x86_64"
+#endif
+#ifdef __i386__
+"processor-name=i386"
+#endif
+#ifdef __ppc__
+"processor-name=powerpc"
+#endif
+#ifdef __ppc64__
+"processor-name=powerpc64"
+#endif
+#ifdef __arm64__
+"processor-name=arm64"
+#endif
+EOF
+ sed -n 's/^"processor-name=\(.*\)"/\1/p'`
+ target="$target_cpu${target}"
+ AC_MSG_RESULT([$target_cpu])
+ ])
+ ])
+ target_archs="$target_cpu"
+])
+AS_IF([test "${target_archs}" != "${rb_cv_target_archs-${target_archs}}"], [
+ AC_MSG_ERROR([target arch(s) has changed from ${rb_cv_target_archs-nothing} to ${target_archs}])
+], [
+ rb_cv_target_archs=${target_archs}
+])
+AS_IF([test "x${ARCH_FLAG}" != x], [
+ CFLAGS="${CFLAGS:+$CFLAGS }${ARCH_FLAG}"
+ LDFLAGS="${LDFLAGS:+$LDFLAGS }${ARCH_FLAG}"
+])
+# RUBY_UNIVERSAL_ARCH end
+])dnl
diff --git a/tool/m4/ruby_werror_flag.m4 b/tool/m4/ruby_werror_flag.m4
new file mode 100644
index 0000000000..e9155c8309
--- /dev/null
+++ b/tool/m4/ruby_werror_flag.m4
@@ -0,0 +1,18 @@
+# -*- Autoconf -*-
+AC_DEFUN([RUBY_WERROR_FLAG], [dnl
+save_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS $rb_cv_warnflags"
+AS_IF([test "${ac_c_werror_flag+set}"], [
+ rb_c_werror_flag="$ac_c_werror_flag"
+], [
+ unset rb_c_werror_flag
+])
+ac_c_werror_flag=yes
+$1
+CFLAGS="$save_CFLAGS"
+save_CFLAGS=
+AS_IF([test "${rb_c_werror_flag+set}"], [
+ ac_c_werror_flag="$rb_c_werror_flag"
+], [
+ unset ac_c_werror_flag
+])])dnl
diff --git a/tool/make-snapshot b/tool/make-snapshot
index 91c44bdf88..c8f4b0a628 100755
--- a/tool/make-snapshot
+++ b/tool/make-snapshot
@@ -6,7 +6,9 @@ require 'digest/sha2'
require 'fileutils'
require 'shellwords'
require 'tmpdir'
+require 'pathname'
require File.expand_path("../vcs", __FILE__)
+require File.expand_path("../colorize", __FILE__)
STDOUT.sync = true
$srcdir ||= nil
@@ -16,8 +18,10 @@ $keep_temp ||= nil
$patch_file ||= nil
$packages ||= nil
$digests ||= nil
+$s3 ||= nil
$tooldir = File.expand_path("..", __FILE__)
$unicode_version = nil if ($unicode_version ||= nil) == ""
+$colorize = Colorize.new
def usage
<<USAGE
@@ -30,6 +34,13 @@ options:
-patch_file=PATCH apply PATCH file after export
-packages=PKG[,...] make PKG packages (#{PACKAGES.keys.join(", ")})
-digests=ALG[,...] show ALG digests (#{DIGESTS.join(", ")})
+ -unicode_version=VER Unicode version to generate encodings
+ -svn[=URL] make snapshot from SVN repository
+ (#{SVNURL})
+ -git[=URL] make snapshot from GIT repository
+ (#{GITURL})
+ -s3=PATH s3 bucket path(default=tmp)
+ -help, --help show this message
version:
trunk, stable, branches/*, tags/*, X.Y, X.Y.Z, X.Y.Z-pL
each versions may be followed by optional @revision.
@@ -44,13 +55,22 @@ PACKAGES = {
"zip" => %w".zip zip -qr",
}
+if system("7z", out: IO::NULL)
+ PACKAGES["gzip"] = %w".tar.gz 7z a dummy -tgzip -mx -so"
+ PACKAGES["zip"] = %w".zip 7z a -tzip -mx" << {out: IO::NULL}
+end
if gzip = ENV.delete("GZIP")
PACKAGES["gzip"].concat(gzip.shellsplit)
end
+if mflags = ENV["GNUMAKEFLAGS"] and /\A-(\S*)j\d*/ =~ mflags
+ mflags = mflags.gsub(/(\A|\s)(-\S*)j\d*/, '\1\2')
+ mflags.strip!
+ ENV["GNUMAKEFLAGS"] = (mflags unless mflags.empty?)
+end
ENV["LC_ALL"] = ENV["LANG"] = "C"
-SVNURL = URI.parse("http://svn.ruby-lang.org/repos/ruby/")
-GITURL = URI.parse("git://github.com/ruby/ruby.git")
+SVNURL = URI.parse("https://svn.ruby-lang.org/repos/ruby/")
+GITURL = URI.parse("https://git.ruby-lang.org/ruby.git")
RUBY_VERSION_PATTERN = /^\#define\s+RUBY_VERSION\s+"([\d.]+)"/
ENV["VPATH"] ||= "include/ruby"
@@ -83,6 +103,9 @@ class Dir
end
$packages &&= $packages.split(/[, ]+/).tap {|pkg|
+ if all = pkg.index("all")
+ pkg[all, 1] = PACKAGES.keys - pkg
+ end
pkg -= PACKAGES.keys
pkg.empty? or abort "#{File.basename $0}: unknown packages - #{pkg.join(", ")}"
}
@@ -107,7 +130,7 @@ path = ENV["PATH"].split(File::PATH_SEPARATOR)
end
%w[BASERUBY RUBY MINIRUBY].each do |var|
- `#{ENV[var]} --disable-gem -e1 2>&1`
+ %x[#{ENV[var]} --disable-gem -e1 2>&1]
if $?.success?
ENV[var] += ' --disable-gem'
end
@@ -132,6 +155,46 @@ unless tmp = $exported
} unless $keep_temp
end
+def tar_create(tarball, dir)
+ require 'rubygems'
+ require 'rubygems/package'
+ require 'rubygems/package/tar_writer'
+ header = Gem::Package::TarHeader
+ dir_type = "5"
+ uname = gname = "ruby"
+ File.open(tarball, "wb") do |f|
+ w = Gem::Package::TarWriter.new(f)
+ Dir.glob("#{dir}/**/*", File::FNM_DOTMATCH) do |path|
+ next if File.basename(path) == "."
+ s = File.stat(path)
+ case
+ when s.file?
+ type = nil
+ size = s.size
+ when s.directory?
+ path += "/"
+ type = dir_type
+ size = 0
+ else
+ next
+ end
+ name, prefix = w.split_name(path)
+ h = header.new(name: name, prefix: prefix, typeflag: type,
+ mode: s.mode, size: size, mtime: s.mtime,
+ uname: uname, gname: gname)
+ f.write(h)
+ if size > 0
+ IO.copy_stream(path, f)
+ f.write("\0" * (-size % 512))
+ end
+ end
+ end
+ true
+rescue => e
+ warn e.message
+ false
+end
+
def package(vcs, rev, destdir, tmp = nil)
patchlevel = false
prerelease = false
@@ -199,13 +262,19 @@ def package(vcs, rev, destdir, tmp = nil)
FileUtils.mkpath(File.dirname(dest))
begin
FileUtils.ln(file, dest, force: true)
+ next unless File.symlink?(dest)
+ File.unlink(dest)
+ rescue SystemCallError
+ end
+ begin
+ FileUtils.cp_r(file, dest, preserve: true)
rescue SystemCallError
- FileUtils.cp(file, dest, preserve: true)
end
end
end
end
+ status = IO.read(File.dirname(__FILE__) + "/prereq.status")
Dir.chdir(tmp) if tmp
if !File.directory?(v)
@@ -214,16 +283,6 @@ def package(vcs, rev, destdir, tmp = nil)
v = v[0]
end
- # get last revision from previous ChangeLog archive
- last_ChangeLog = Dir["#{v}/doc/ChangeLog-*"].grep(/-(\d+)\z/) {|n| [$1.to_i, n]}.max[1]
- open(last_ChangeLog) do |f|
- f.readline
- unless /\Ar(\d+) / =~ f.readline
- abort "#{File.basename $0}: Cannot find revision from '#{last_ChangeLog}'"
- end
- vcs.export_changelog($1.to_i, revision.to_i, "#{v}/ChangeLog")
- end
-
open("#{v}/revision.h", "wb") {|f| f.puts "#define RUBY_REVISION #{revision}"}
version ||= (versionhdr = IO.read("#{v}/version.h"))[RUBY_VERSION_PATTERN, 1]
version or return
@@ -253,10 +312,32 @@ def package(vcs, rev, destdir, tmp = nil)
end
system(*%W"patch -d #{v} -p0 -i #{$patch_file}") if $patch_file
if !$exported or $patch_file
- "take a breath, and go ahead".scan(/./) {|c|print c; sleep(c == "," ? 0.7 : 0.05)}; puts
+ colors = %w[red yellow green cyan blue magenta]
+ "take a breath, and go ahead".scan(/./) do |c|
+ if c == ' '
+ print c
+ else
+ colors.push(color = colors.shift)
+ print $colorize.decorate(c, color)
+ end
+ sleep(c == "," ? 0.7 : 0.05)
+ end
+ puts
end
def (clean = []).add(n) push(n); n end
Dir.chdir(v) do
+ unless File.exist?("ChangeLog")
+ # get last revision from previous ChangeLog archive
+ last_ChangeLog = Dir["doc/ChangeLog-*"].grep(/-(\d+)\z/) {|n| [$1.to_i, n]}.max[1]
+ open(last_ChangeLog) do |f|
+ f.readline
+ unless /\Ar(\d+) / =~ f.readline
+ abort "#{File.basename $0}: Cannot find revision from '#{last_ChangeLog}'"
+ end
+ vcs.export_changelog(url, $1.to_i, revision.to_i, "ChangeLog")
+ end
+ end
+
File.open(clean.add("cross.rb"), "w") do |f|
f.puts "Object.__send__(:remove_const, :CROSS_COMPILING) if defined?(CROSS_COMPILING)"
f.puts "CROSS_COMPILING=true"
@@ -268,10 +349,10 @@ def package(vcs, rev, destdir, tmp = nil)
unless File.exist?("configure")
print "creating configure..."
unless system([ENV["AUTOCONF"]]*2)
- puts " failed"
+ puts $colorize.fail(" failed")
return
end
- puts " done"
+ puts $colorize.pass(" done")
end
clean.add("autom4te.cache")
clean.add("enc/unicode/data")
@@ -279,27 +360,13 @@ def package(vcs, rev, destdir, tmp = nil)
if File.file?("common.mk") && /^prereq/ =~ commonmk = IO.read("common.mk")
puts
extout = clean.add('tmp')
+ begin
+ status = IO.read("tool/prereq.status")
+ rescue Errno::ENOENT
+ # use fallback file
+ end
File.open(clean.add("config.status"), "w") {|f|
- f.puts "s,@configure_args@,|#_!!_#|,g"
- f.puts "s,@EXTOUT@,|#_!!_#|#{extout},g"
- f.puts "s,@bindir@,|#_!!_#|,g"
- f.puts "s,@ruby_install_name@,|#_!!_#|,g"
- f.puts "s,@ARCH_FLAG@,|#_!!_#|,g"
- f.puts "s,@CFLAGS@,|#_!!_#|,g"
- f.puts "s,@CPPFLAGS@,|#_!!_#|,g"
- f.puts "s,@CXXFLAGS@,|#_!!_#|,g"
- f.puts "s,@LDFLAGS@,|#_!!_#|,g"
- f.puts "s,@DLDFLAGS@,|#_!!_#|,g"
- f.puts "s,@LIBEXT@,|#_!!_#|a,g"
- f.puts "s,@OBJEXT@,|#_!!_#|o,g"
- f.puts "s,@EXEEXT@,|#_!!_#|,g"
- f.puts "s,@LIBRUBY@,|#_!!_#|libruby.a,g"
- f.puts "s,@LIBRUBY_A@,|#_!!_#|libruby.a,g"
- f.puts "s,@RM@,|#_!!_#|rm -f,g"
- f.puts "s,@CP@,|#_!!_#|cp,g"
- f.puts "s,@rubyarchdir@,|#_!!_#|,g"
- f.puts "s,@rubylibprefix@,|#_!!_#|,g"
- f.puts "s,@ruby_version@,|#_!!_#|#{version},g"
+ f.print status
}
FileUtils.mkpath(hdrdir = "#{extout}/include/ruby")
File.open("#{hdrdir}/config.h", "w") {}
@@ -310,40 +377,38 @@ def package(vcs, rev, destdir, tmp = nil)
baseruby = ENV["BASERUBY"]
mk = IO.read("Makefile.in").gsub(/^@.*\n/, '')
vars = {
- "srcdir"=>".",
- "CHDIR"=>"cd",
- "NULLCMD"=>":",
+ "EXTOUT"=>extout,
"PATH_SEPARATOR"=>File::PATH_SEPARATOR,
- "IFCHANGE"=>"tool/ifchange",
- "MKDIR_P"=>"mkdir -p",
- "RMALL"=>"rm -fr",
"MINIRUBY"=>miniruby,
- "RUNRUBY"=>miniruby,
"RUBY"=>ENV["RUBY"],
- "HAVE_BASERUBY"=>"yes",
"BASERUBY"=>baseruby,
- "BOOTSTRAPRUBY"=>baseruby,
"PWD"=>Dir.pwd,
- "CONFIGURE"=>"configure",
+ "ruby_version"=>version,
}
+ status.scan(/^s([%,])@([A-Za-z_][A-Za-z_0-9]*)@\1(.*?)\1g$/) do
+ vars[$2] ||= $3
+ end
vars["UNICODE_VERSION"] = $unicode_version if $unicode_version
args = vars.dup
mk.gsub!(/@([A-Za-z_]\w*)@/) {args.delete($1); vars[$1] || ENV[$1]}
mk << commonmk.gsub(/\{\$([^(){}]*)[^{}]*\}/, "")
mk << <<-'APPEND'
-prereq: clean-cache $(CLEAN_CACHE)
+prepare-package: touch-unicode-files:
+prepare-package: prereq update-download clean-cache $(CLEAN_CACHE)
clean-cache $(CLEAN_CACHE): after-update
update-download:: update-gems
after-update:: extract-gems
extract-gems: update-gems
update-gems:
+$(UNICODE_SRC_DATA_DIR)/.unicode-tables.time:
+touch-unicode-files:
APPEND
open(clean.add("Makefile"), "w") do |f|
- f.puts "prereq: update-download"
f.puts mk
end
- system("make", "prereq", *args.map {|arg| arg.join("=")})
+ ENV["CACHE_SAVE"] = "no"
+ system(ENV["MAKE"] || ENV["make"] || "make", "prepare-package", *args.map {|arg| arg.join("=")})
clean.push("rbconfig.rb", ".rbconfig.time", "enc.mk")
print "prerequisites"
else
@@ -355,17 +420,18 @@ update-gems:
if File.exist?("gems/bundled_gems")
gems = Dir.glob("gems/*.gem")
gems -= File.readlines("gems/bundled_gems").map {|line|
- 'gems/'+line.split(' ').join('-')+'.gem'
+ name, version, _ = line.split(' ')
+ "gems/#{name}-#{version}.gem"
}
FileUtils.rm_f(gems)
else
FileUtils.rm_rf("gems")
end
unless $?.success?
- puts " failed"
+ puts $colorize.fail(" failed")
return
end
- puts " done"
+ puts $colorize.pass(" done")
end
if v == "."
@@ -388,10 +454,10 @@ update-gems:
else
tarball = "#{$archname||v}.tar"
print "creating tarball... #{tarball}"
- if system("tar", "cf", tarball, v)
- puts " done"
+ if tar_create(tarball, v)
+ puts $colorize.pass(" done")
else
- puts " failed"
+ puts $colorize.fail(" failed")
tarball = ""
next
end
@@ -400,13 +466,19 @@ update-gems:
done = system(*cmd, tarball, out: file)
else
print "creating #{mesg} archive... #{file}"
- done = system(*cmd, file, v)
+ if Hash === cmd.last
+ *cmd, opt = *cmd
+ cmd << file << v << opt
+ else
+ (cmd = cmd.dup) << file << v
+ end
+ done = system(*cmd)
end
if done
- puts " done"
+ puts $colorize.pass(" done")
file
else
- puts " failed"
+ puts $colorize.fail(" failed")
nil
end
end.compact
@@ -434,7 +506,12 @@ revisions.collect {|rev| package(vcs, rev, destdir, tmp)}.flatten.each do |name|
next
end
str = open(name, "rb") {|f| f.read}
- puts "* #{name}"
+ if $s3
+ %x[aws s3 cp #{name} s3://ftp.r-l.o/pub/#{$s3}/#{Pathname(name).basename}]
+ FileUtils.rm_f name
+ name = "https://s3.amazonaws.com/ftp.r-l.o/pub/#{$s3}/#{Pathname(name).basename}"
+ end
+ puts "* #{$colorize.pass(name)}"
puts " SIZE: #{str.bytesize} bytes"
$digests.each do |alg|
printf " %-8s%s\n", "#{alg}:", Digest.const_get(alg).hexdigest(str)
diff --git a/tool/merger.rb b/tool/merger.rb
index 2faf86634f..6fedb0a936 100755
--- a/tool/merger.rb
+++ b/tool/merger.rb
@@ -38,6 +38,9 @@ def help
\e[1mtagging preview/RC\e[0m
ruby #$0 tag 2.2.0-preview1
+\e[1mremove tag\e[0m
+ ruby #$0 removetag 2.2.9
+
\e[33;1m* all operations shall be applied to the working directory.\e[0m
end
end
@@ -142,13 +145,43 @@ def tag intv_p = false, relname=nil
end
tagname = 'v' + x + (v[0] < "2" || (v[0] == "2" && v[1] < "1") || /^(?:preview|rc)/ =~ pl ? '_' + pl : '')
tag_url = $repos + 'tags/' + tagname
+ system(*%w'svn info', tag_url, out: IO::NULL, err: IO::NULL)
+ if $?.success?
+ abort "specfied tag already exists. check tag name and remove it if you want to force re-tagging"
+ end
if intv_p
interactive "OK? svn cp -m \"add tag #{tagname}\" #{branch_url} #{tag_url}" do
+ # nothing to do here
end
end
system(*%w'svn cp -m', "add tag #{tagname}", branch_url, tag_url)
- puts "run following command in git-svn working directory to push the tag into GitHub:"
- puts "git tag #{tagname} origin/tags/#{tagname} && git push ruby #{tagname}"
+end
+
+def remove_tag intv_p = false, relname
+ # relname:
+ # * 2.2.0-preview1
+ # * 2.2.0-rc1
+ # * 2.2.0
+ # * v2_2_0_preview1
+ # * v2_2_0_rc1
+ # * v2_2_0
+ if !relname && !intv_p.is_a?(String)
+ raise ArgumentError, "relname is not specified"
+ end
+ intv_p, relname = false, intv_p if !relname && intv_p.is_a?(String)
+
+ if /^v/ !~ relname
+ tagname = 'v' + relname.tr(".-", "_")
+ else
+ tagname = relname
+ end
+ tag_url = $repos + 'tags/' + tagname
+ if intv_p
+ interactive "OK? svn rm -m \"remove tag #{tagname}\" #{tag_url}" do
+ # nothing to do here
+ end
+ end
+ system(*%w'svn rm -m', "remove tag #{tagname}", tag_url)
end
def default_merge_branch
@@ -164,6 +197,8 @@ when "up", /\A(ver|version|rev|revision|lv|level|patch\s*level)\s*up/
system 'svn diff version.h'
when "tag"
tag :interactive, ARGV[1]
+when /\A(?:remove|rm|del)_?tag\z/
+ remove_tag :interactive, ARGV[1]
when nil, "-h", "--help"
help
exit
@@ -212,13 +247,14 @@ else
end
log << l
- log_svn << l.lines.grep(/^\+\t/).join.gsub(/^\+/, '').gsub(/^\t\*/, "\n\t\*")
+ l = l.lines.grep(/^\+\t/).join.gsub(/^\+/, '').gsub(/^\t\*/, "\n\t\*")
- if log_svn.empty?
- log_svn = IO.popen %w'svn log ' + r + [q] do |f|
+ if l.empty?
+ l = IO.popen %w'svn log ' + r + [q] do |f|
f.read
- end.sub(/\A-+\nr.*\n/, '').sub(/\n-+\n\z/, '').gsub(/^(?=\S)/, "\t")
+ end.sub(/\A-+\nr.*/, '').sub(/\n-+\n\z/, '').gsub(/^./, "\t\\&")
end
+ log_svn << l
a = %w'svn merge --accept=postpone' + r + [q]
STDERR.puts a.join(' ')
@@ -246,7 +282,7 @@ else
version_up
f = Tempfile.new 'merger.rb'
- f.printf "merge revision(s) %s:%s\n", revstr, tickets
+ f.printf "merge revision(s) %s:%s", revstr, tickets
f.write log_svn
f.flush
f.close
diff --git a/tool/mjit_archflag.sh b/tool/mjit_archflag.sh
new file mode 100644
index 0000000000..082fb4bcd0
--- /dev/null
+++ b/tool/mjit_archflag.sh
@@ -0,0 +1,40 @@
+# -*- sh -*-
+
+quote() {
+ printf "#${indent}define $1"
+ shift
+ ${1+printf} ${1+' "%s"'$sep} ${1+"$@"}
+ echo
+}
+
+archs=""
+arch_flag=""
+
+parse_arch_flags() {
+ for arch in $1; do
+ archs="${archs:+$archs }${arch%=*}"
+ done
+
+ while shift && [ "$#" -gt 0 ]; do
+ case "$1" in
+ -arch)
+ shift
+ archs="${archs:+$archs }$1"
+ ;;
+ *)
+ arch_flag="${arch_flag:+${arch_flag} }$1"
+ ;;
+ esac
+ done
+}
+
+define_arch_flags() {
+ ${archs:+echo} ${archs:+'#if 0'}
+ for arch in $archs; do
+ echo "#elif defined __${arch}__"
+ quote "MJIT_ARCHFLAG " -arch "${arch}"
+ done
+ ${archs:+echo} ${archs:+'#else'}
+ quote "MJIT_ARCHFLAG /* ${arch_flag:-no flag} */" ${arch_flag}
+ ${archs:+echo} ${archs:+'#endif'}
+}
diff --git a/tool/mk_call_iseq_optimized.rb b/tool/mk_call_iseq_optimized.rb
index f04917cceb..0978aa0bbc 100644
--- a/tool/mk_call_iseq_optimized.rb
+++ b/tool/mk_call_iseq_optimized.rb
@@ -1,10 +1,11 @@
puts <<EOS
+/* -*- c -*- */
#if 1 /* enable or disable this optimization */
/* DO NOT EDIT THIS FILE DIRECTLY
*
- * This file is generated by tool/mkcall_iseq.rb
+ * This file is generated by tool/mk_call_iseq_optimized.rb
*/
EOS
@@ -20,9 +21,9 @@ P.each{|param|
L.each{|local|
puts <<EOS
static VALUE
-#{fname(param, local)}(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+#{fname(param, local)}(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
{
- return vm_call_iseq_setup_normal(th, cfp, calling, ci, cc, 0, #{param}, #{local});
+ return vm_call_iseq_setup_normal(ec, cfp, calling, cc->me, 0, #{param}, #{local});
}
EOS
diff --git a/tool/mkconfig.rb b/tool/mkconfig.rb
index 7e8ff10dba..980bea6e2c 100755
--- a/tool/mkconfig.rb
+++ b/tool/mkconfig.rb
@@ -8,16 +8,14 @@
# avoid warnings with -d.
$install_name ||= nil
$so_name ||= nil
-$cross_compiling ||= nil
$unicode_version ||= nil
+$unicode_emoji_version ||= nil
arch = $arch or raise "missing -arch"
version = $version or raise "missing -version"
srcdir = File.expand_path('../..', __FILE__)
-$:.replace [srcdir+"/lib"] unless $cross_compiling == "yes"
$:.unshift(".")
-require "fileutils"
mkconfig = File.basename($0)
fast = {'prefix'=>true, 'ruby_install_name'=>true, 'INSTALL'=>true, 'EXEEXT'=>true}
@@ -64,6 +62,8 @@ File.foreach "config.status" do |line|
when /^(?:X|(?:MINI|RUN|(?:HAVE_)?BASE|BOOTSTRAP|BTEST)RUBY(?:_COMMAND)?$)/; next
when /^INSTALLDOC|TARGET$/; next
when /^DTRACE/; next
+ when /^MJIT_(CC|SUPPORT)/; # pass
+ when /^MJIT_/; next
when /^(?:MAJOR|MINOR|TEENY)$/; vars[name] = val; next
when /^LIBRUBY_D?LD/; next
when /^RUBY_INSTALL_NAME$/; next vars[name] = (install_name = val).dup if $install_name
@@ -71,6 +71,7 @@ File.foreach "config.status" do |line|
when /^arch$/; if val.empty? then val = arch else arch = val end
when /^sitearch$/; val = '$(arch)' if val.empty?
when /^DESTDIR$/; next
+ when /RUBYGEMS/; next
end
case val
when /^\$\(ac_\w+\)$/; next
@@ -163,6 +164,7 @@ prefix = vars.expand(vars["prefix"] ||= "")
rubyarchdir = vars.expand(vars["rubyarchdir"] ||= "")
relative_archdir = rubyarchdir.rindex(prefix, 0) ? rubyarchdir[prefix.size..-1] : rubyarchdir
puts %[\
+# encoding: ascii-8bit
# frozen-string-literal: false
#
# The module storing Ruby interpreter configurations on building.
@@ -245,8 +247,11 @@ print(*v_others)
print <<EOS if $unicode_version
CONFIG["UNICODE_VERSION"] = #{$unicode_version.dump}
EOS
+print <<EOS if $unicode_emoji_version
+ CONFIG["UNICODE_EMOJI_VERSION"] = #{$unicode_emoji_version.dump}
+EOS
print <<EOS if /darwin/ =~ arch
- CONFIG["SDKROOT"] = ENV["SDKROOT"] || "" # don't run xcrun everytime, usually useless.
+ CONFIG["SDKROOT"] = "\#{ENV['SDKROOT']}" # don't run xcrun every time, usually useless.
EOS
print <<EOS
CONFIG["archdir"] = "$(rubyarchdir)"
@@ -308,6 +313,38 @@ print <<EOS
RbConfig::expand(val)
end
+ # :nodoc:
+ # call-seq:
+ #
+ # RbConfig.fire_update!(key, val) -> string
+ # RbConfig.fire_update!(key, val, mkconf, conf) -> string
+ #
+ # updates +key+ in +mkconf+ with +val+, and all values depending on
+ # the +key+ in +mkconf+.
+ #
+ # RbConfig::MAKEFILE_CONFIG.values_at("CC", "LDSHARED") # => ["gcc", "$(CC) -shared"]
+ # RbConfig::CONFIG.values_at("CC", "LDSHARED") # => ["gcc", "gcc -shared"]
+ # RbConfig.fire_update!("CC", "gcc-8") # => ["CC", "LDSHARED"]
+ # RbConfig::MAKEFILE_CONFIG.values_at("CC", "LDSHARED") # => ["gcc-8", "$(CC) -shared"]
+ # RbConfig::CONFIG.values_at("CC", "LDSHARED") # => ["gcc-8", "gcc-8 -shared"]
+ #
+ # returns updated keys list, or +nil+ if nothing changed.
+ def RbConfig.fire_update!(key, val, mkconf = MAKEFILE_CONFIG, conf = CONFIG)
+ return if (old = mkconf[key]) == val
+ mkconf[key] = val
+ keys = [key]
+ deps = []
+ begin
+ re = Regexp.new("\\\\$\\\\((?:%1$s)\\\\)|\\\\$\\\\{(?:%1$s)\\\\}" % keys.join('|'))
+ deps |= keys
+ keys.clear
+ mkconf.each {|k,v| keys << k if re =~ v}
+ end until keys.empty?
+ deps.each {|k| conf[k] = mkconf[k].dup}
+ deps.each {|k| expand(conf[k])}
+ deps
+ end
+
# call-seq:
#
# RbConfig.ruby -> path
diff --git a/tool/mkrunnable.rb b/tool/mkrunnable.rb
index fbfc76047a..01756a3529 100755
--- a/tool/mkrunnable.rb
+++ b/tool/mkrunnable.rb
@@ -95,7 +95,6 @@ config.each_value {|s| RbConfig.expand(s, config)}
srcdir = config["srcdir"] ||= File.dirname(__FILE__)
top_srcdir = config["top_srcdir"] ||= File.dirname(srcdir)
extout = ARGV[0] || config["EXTOUT"]
-version = config["ruby_version"]
arch = config["arch"]
bindir = config["bindir"]
libdirname = config["libdirname"]
@@ -104,7 +103,6 @@ vendordir = config["vendordir"]
rubylibdir = config["rubylibdir"]
rubyarchdir = config["rubyarchdir"]
archdir = "#{extout}/#{arch}"
-rubylibs = [vendordir, rubylibdir, rubyarchdir]
[bindir, libdir, archdir].uniq.each do |dir|
File.directory?(dir) or mkdir_p(dir)
end
@@ -115,8 +113,8 @@ rubyw_install_name = config["rubyw_install_name"]
goruby_install_name = "go" + ruby_install_name
[ruby_install_name, rubyw_install_name, goruby_install_name].map do |ruby|
ruby += exeext
- if ruby and !ruby.empty?
- ln_relative(ruby, "#{bindir}/#{ruby}")
+ if ruby and !ruby.empty? and !File.file?(target = "#{bindir}/#{ruby}")
+ ln_relative(ruby, target)
end
end
so = config["LIBRUBY_SO"]
diff --git a/tool/node_name.rb b/tool/node_name.rb
index 5c67d7ccd5..dc0584e821 100755
--- a/tool/node_name.rb
+++ b/tool/node_name.rb
@@ -1,10 +1,8 @@
-#! ./miniruby
+#! ./miniruby -n
# Used when making Ruby to generate node_name.inc.
# See common.mk for details.
-while gets
- if ~/enum node_type \{/..~/^\};/
- ~/(NODE_.+),/ and puts(" case #{$1}:\n\treturn \"#{$1}\";")
- end
+if (t ||= /^enum node_type \{/ =~ $_) and (t = /^\};/ !~ $_)
+ /(NODE_.+),/ =~ $_ and puts(" case #{$1}:\n\treturn \"#{$1}\";")
end
diff --git a/tool/prereq.status b/tool/prereq.status
new file mode 100644
index 0000000000..7509b8cbd7
--- /dev/null
+++ b/tool/prereq.status
@@ -0,0 +1,43 @@
+s,@EXTOUT@,tmp,g
+s,@ruby_version@,0.0.0,g
+s,@NULLCMD@,:,g
+s,@ARCH_FLAG@,,g
+s,@BASERUBY@,ruby,g
+s,@BOOTSTRAPRUBY@,$(BASERUBY),g
+s,@CC@,false,g
+s,@CFLAGS@,,g
+s,@CHDIR@,cd,g
+s,@CONFIGURE@,configure,g
+s,@CP@,cp,g
+s,@CPPFLAGS@,,g
+s,@CXXFLAGS@,,g
+s,@DLDFLAGS@,,g
+s,@EXEEXT@,,g
+s,@HAVE_BASERUBY@,yes,g
+s,@IFCHANGE@,tool/ifchange,g
+s,@LDFLAGS@,,g
+s,@LIBEXT@,a,g
+s,@LIBRUBY@,libruby.a,g
+s,@LIBRUBY_A@,libruby.a,g
+s,@MINIRUBY@,$(BASERUBY),g
+s,@MKDIR_P@,mkdir -p,g
+s,@OBJEXT@,o,g
+s,@PATH_SEPARATOR@,:,g
+s,@PWD@,.,g
+s,@RM@,rm -f,g
+s,@RMALL@,rm -fr,g
+s,@RMDIR@,rmdir,g
+s,@RMDIRS@,$(RMDIR) -p,g
+s,@RUBY@,$(BASERUBY),g
+s,@RUNRUBY@,$(MINIRUBY),g
+s,@UNICODE_FILES@,,g
+s,@arch@,noarch,g
+s,@bindir@,,g
+s,@configure_args@,,g
+s,@ruby_install_name@,,g
+s,@rubyarchdir@,,g
+s,@rubylibprefix@,,g
+s,@srcdir@,.,g
+
+s/@[A-Za-z][A-Za-z0-9_]*@//g
+s/{\$([A-Za-z]*)}//g
diff --git a/tool/pure_parser.rb b/tool/pure_parser.rb
new file mode 100644
index 0000000000..aa00a2defa
--- /dev/null
+++ b/tool/pure_parser.rb
@@ -0,0 +1,20 @@
+#!/usr/bin/ruby -pi
+BEGIN {
+ require_relative 'colorize'
+
+ colorize = Colorize.new
+ file = ARGV.shift
+ begin
+ version = IO.popen(ARGV+%w[--version], &:read)
+ rescue Errno::ENOENT
+ abort "Failed to run `#{colorize.fail ARGV.join(' ')}'; You may have to install it."
+ end
+ unless /\Abison .* (\d+)\.\d+/ =~ version
+ puts colorize.fail("not bison")
+ exit
+ end
+ exit if $1.to_i >= 3
+ ARGV.clear
+ ARGV.push(file)
+}
+$_.sub!(/^%define\s+api\.pure/, '%pure-parser')
diff --git a/tool/rbinstall.rb b/tool/rbinstall.rb
index a80be8aa38..0b02025d96 100755
--- a/tool/rbinstall.rb
+++ b/tool/rbinstall.rb
@@ -28,8 +28,9 @@ rescue LoadError
$" << "zlib.rb"
end
+INDENT = " "*36
STDOUT.sync = true
-File.umask(077)
+File.umask(022)
def parse_args(argv = ARGV)
$mantype = 'doc'
@@ -41,6 +42,7 @@ def parse_args(argv = ARGV)
$installed_list = nil
$dryrun = false
$rdocdir = nil
+ $htmldir = nil
$data_mode = 0644
$prog_mode = 0755
$dir_mode = nil
@@ -79,6 +81,7 @@ def parse_args(argv = ARGV)
end
opt.on('--installed-list [FILENAME]') {|name| $installed_list = name}
opt.on('--rdoc-output [DIR]') {|dir| $rdocdir = dir}
+ opt.on('--html-output [DIR]') {|dir| $htmldir = dir}
opt.on('--cmd-type=TYPE', %w[cmd plain]) {|cmd| $cmdtype = (cmd unless cmd == 'plain')}
opt.on('--[no-]strip') {|strip| $strip = strip}
@@ -133,6 +136,7 @@ def parse_args(argv = ARGV)
end
$rdocdir ||= $mflags.defined?('RDOCOUT')
+ $htmldir ||= $mflags.defined?('HTMLOUT')
$dir_mode ||= $prog_mode | 0700
$script_mode ||= $prog_mode
@@ -301,7 +305,7 @@ def prepare(mesg, basedir, subdirs=nil)
else
dirs = [basedir, *subdirs.collect {|dir| File.join(basedir, dir)}]
end
- printf("installing %-18s %s%s\n", "#{mesg}:", basedir,
+ printf("%-*s%s%s\n", INDENT.size, "installing #{mesg}:", basedir,
(subdirs ? " (#{subdirs.join(', ')})" : ""))
makedirs(dirs)
end
@@ -326,19 +330,23 @@ rubyhdrdir = CONFIG["rubyhdrdir", true]
archhdrdir = CONFIG["rubyarchhdrdir"] || (rubyhdrdir + "/" + CONFIG['arch'])
rubylibdir = CONFIG["rubylibdir", true]
archlibdir = CONFIG["rubyarchdir", true]
-sitelibdir = CONFIG["sitelibdir"]
-sitearchlibdir = CONFIG["sitearchdir"]
-vendorlibdir = CONFIG["vendorlibdir"]
-vendorarchlibdir = CONFIG["vendorarchdir"]
+if CONFIG["sitedir"]
+ sitelibdir = CONFIG["sitelibdir"]
+ sitearchlibdir = CONFIG["sitearchdir"]
+end
+if CONFIG["vendordir"]
+ vendorlibdir = CONFIG["vendorlibdir"]
+ vendorarchlibdir = CONFIG["vendorarchdir"]
+end
mandir = CONFIG["mandir", true]
docdir = CONFIG["docdir", true]
-configure_args = Shellwords.shellwords(CONFIG["configure_args"])
enable_shared = CONFIG["ENABLE_SHARED"] == 'yes'
dll = CONFIG["LIBRUBY_SO", enable_shared]
lib = CONFIG["LIBRUBY", true]
arc = CONFIG["LIBRUBY_A", true]
-config_h = File.read(CONFIG["EXTOUT"]+"/include/"+CONFIG["arch"]+"/ruby/config.h")
-load_relative = config_h[/^\s*#\s*define\s+LOAD_RELATIVE\s+(\d+)/, 1].to_i.nonzero?
+load_relative = CONFIG["LIBRUBY_RELATIVE"] == 'yes'
+
+rdoc_noinst = %w[created.rid]
install?(:local, :arch, :bin, :'bin-arch') do
prepare "binary commands", bindir
@@ -355,13 +363,13 @@ install?(:local, :arch, :bin, :'bin-arch') do
end
end
-install?(:local, :arch, :lib) do
+install?(:local, :arch, :lib, :'lib-arch') do
prepare "base libraries", libdir
install lib, libdir, :mode => $prog_mode, :strip => $strip unless lib == arc
install arc, libdir, :mode => $data_mode unless CONFIG["INSTALL_STATIC_LIBRARY"] == "no"
if dll == lib and dll != arc
- for link in CONFIG["LIBRUBY_ALIASES"].split
+ for link in CONFIG["LIBRUBY_ALIASES"].split - [File.basename(dll)]
ln_sf(dll, File.join(libdir, link))
end
end
@@ -399,9 +407,12 @@ install?(:ext, :arch, :'ext-arch') do
end
end
end
-install?(:ext, :arch, :hdr, :'arch-hdr') do
+install?(:ext, :arch, :hdr, :'arch-hdr', :'hdr-arch') do
prepare "extension headers", archhdrdir
install_recursive("#{$extout}/include/#{CONFIG['arch']}", archhdrdir, :glob => "*.h", :mode => $data_mode)
+ install_recursive("#{$extout}/include/#{CONFIG['arch']}", archhdrdir, :glob => "rb_mjit_header-*.obj", :mode => $data_mode)
+ install_recursive("#{$extout}/include/#{CONFIG['arch']}", archhdrdir, :glob => "rb_mjit_header-*.pch", :mode => $data_mode)
+ install_recursive("#{$extout}/include/#{CONFIG['arch']}", archhdrdir, :glob => "rb_mjit_header-*.pdb", :mode => $data_mode)
end
install?(:ext, :comm, :'ext-comm') do
prepare "extension scripts", rubylibdir
@@ -409,7 +420,7 @@ install?(:ext, :comm, :'ext-comm') do
prepare "extension scripts", sitelibdir
prepare "extension scripts", vendorlibdir
end
-install?(:ext, :comm, :hdr, :'comm-hdr') do
+install?(:ext, :comm, :hdr, :'comm-hdr', :'hdr-comm') do
hdrdir = rubyhdrdir + "/ruby"
prepare "extension headers", hdrdir
install_recursive("#{$extout}/include/ruby", hdrdir, :glob => "*.h", :mode => $data_mode)
@@ -419,7 +430,13 @@ install?(:doc, :rdoc) do
if $rdocdir
ridatadir = File.join(CONFIG['ridir'], CONFIG['ruby_version'], "system")
prepare "rdoc", ridatadir
- install_recursive($rdocdir, ridatadir, :mode => $data_mode)
+ install_recursive($rdocdir, ridatadir, :no_install => rdoc_noinst, :mode => $data_mode)
+ end
+end
+install?(:doc, :html) do
+ if $htmldir
+ prepare "html-docs", docdir
+ install_recursive($htmldir, docdir+"/html", :no_install => rdoc_noinst, :mode => $data_mode)
end
end
install?(:doc, :capi) do
@@ -427,32 +444,39 @@ install?(:doc, :capi) do
install_recursive "doc/capi", docdir+"/capi", :mode => $data_mode
end
-if load_relative or /\s/ =~ bindir
- PROLOG_SCRIPT = <<EOS
-#!/bin/sh\n# -*- ruby -*-
+prolog_script = <<EOS
bindir="#{load_relative ? '${0%/*}' : bindir.gsub(/\"/, '\\\\"')}"
EOS
- if CONFIG["LIBRUBY_RELATIVE"] != 'yes' and libpathenv = CONFIG["LIBPATHENV"]
- pathsep = File::PATH_SEPARATOR
- PROLOG_SCRIPT << <<EOS
-libdir="#{load_relative ? '${bindir%/bin}/lib' : libdir.gsub(/\"/, '\\\\"')}"
+if CONFIG["LIBRUBY_RELATIVE"] != 'yes' and libpathenv = CONFIG["LIBPATHENV"]
+ pathsep = File::PATH_SEPARATOR
+ prolog_script << <<EOS
+libdir="#{load_relative ? '$\{bindir%/bin\}/lib' : libdir.gsub(/\"/, '\\\\"')}"
export #{libpathenv}="$libdir${#{libpathenv}:+#{pathsep}$#{libpathenv}}"
EOS
- end
- PROLOG_SCRIPT << %Q[exec "$bindir/#{ruby_install_name}" -x "$0" "$@"\n]
-else
- PROLOG_SCRIPT = nil
end
+prolog_script << %Q[exec "$bindir/#{ruby_install_name}" "-x" "$0" "$@"\n]
+PROLOG_SCRIPT = {}
+PROLOG_SCRIPT["exe"] = "#!#{bindir}/#{ruby_install_name}"
+PROLOG_SCRIPT["cmd"] = <<EOS
+:""||{ ""=> %q<-*- ruby -*-
+@"%~dp0#{ruby_install_name}" -x "%~f0" %*
+@exit /b %ERRORLEVEL%
+};{#\n#{prolog_script.gsub(/(?=\n)/, ' #')}>,\n}
+EOS
+PROLOG_SCRIPT.default = (load_relative || /\s/ =~ bindir) ?
+ <<EOS : PROLOG_SCRIPT["exe"]
+#!/bin/sh
+# -*- ruby -*-
+_=_\\
+=begin
+#{prolog_script}=end
+EOS
-install?(:local, :comm, :bin, :'bin-comm') do
- prepare "command scripts", bindir
-
+installer = Struct.new(:ruby_shebang, :ruby_bin, :ruby_install_name, :stub, :trans)
+$script_installer = Class.new(installer) do
ruby_shebang = File.join(bindir, ruby_install_name)
if File::ALT_SEPARATOR
ruby_bin = ruby_shebang.tr(File::SEPARATOR, File::ALT_SEPARATOR)
- if $cmdtype == 'exe'
- stub = File.open("rubystub.exe", "rb") {|f| f.read} << "\n" rescue nil
- end
end
if trans = CONFIG["program_transform_name"]
exp = []
@@ -478,23 +502,28 @@ install?(:local, :comm, :bin, :'bin-comm') do
else
trans = proc {|base| base}
end
- prebatch = ':""||{ ""=> %q<-*- ruby -*-'"\n"
- postbatch = PROLOG_SCRIPT ? "};{\n#{PROLOG_SCRIPT.sub(/\A(?:#.*\n)*/, '')}" : ''
- postbatch << ">,\n}\n"
- postbatch.gsub!(/(?=\n)/, ' #')
- install_recursive(File.join(srcdir, "bin"), bindir, :maxdepth => 1) do |src, cmd|
+
+ def prolog(shebang)
+ shebang.sub!(/\r$/, '')
+ script = PROLOG_SCRIPT[$cmdtype]
+ shebang.sub!(/\A(\#!.*?ruby\b)?/) do
+ if script.end_with?("\n")
+ script + ($1 || "#!ruby\n")
+ else
+ $1 ? script : "#{script}\n"
+ end
+ end
+ shebang
+ end
+
+ def install(src, cmd)
cmd = cmd.sub(/[^\/]*\z/m) {|n| RbConfig.expand(trans[n])}
shebang, body = open(src, "rb") do |f|
next f.gets, f.read
end
shebang or raise "empty file - #{src}"
- if PROLOG_SCRIPT and !$cmdtype
- shebang.sub!(/\A(\#!.*?ruby\b)?/) {PROLOG_SCRIPT + ($1 || "#!ruby\n")}
- else
- shebang.sub!(/\A(\#!.*?ruby\b)?/) {"#!" + ruby_shebang + ($1 ? "" : "\n")}
- end
- shebang.sub!(/\r$/, '')
+ shebang = prolog(shebang)
body.gsub!(/\r$/, '')
cmd << ".#{$cmdtype}" if $cmdtype
@@ -502,21 +531,39 @@ install?(:local, :comm, :bin, :'bin-comm') do
case $cmdtype
when "exe"
stub + shebang + body
- when "cmd"
- prebatch + <<"/EOH" << postbatch << shebang << body
-@"%~dp0#{ruby_install_name}" -x "%~f0" %*
-@exit /b %ERRORLEVEL%
-/EOH
else
shebang + body
end
end
end
+
+ def self.get_rubystub
+ stubfile = "rubystub.exe"
+ stub = File.open(stubfile, "rb") {|f| f.read} << "\n"
+ rescue => e
+ abort "No #{stubfile}: #{e}"
+ else
+ stub
+ end
+
+ def stub
+ super or self.stub = self.class.get_rubystub
+ end
+
+ break new(ruby_shebang, ruby_bin, ruby_install_name, nil, trans)
+end
+
+install?(:local, :comm, :bin, :'bin-comm') do
+ prepare "command scripts", bindir
+
+ install_recursive(File.join(srcdir, "bin"), bindir, :maxdepth => 1) do |src, cmd|
+ $script_installer.install(src, cmd)
+ end
end
install?(:local, :comm, :lib) do
prepare "library scripts", rubylibdir
- noinst = %w[README* *.txt *.rdoc *.gemspec]
+ noinst = %w[*.txt *.rdoc *.gemspec]
install_recursive(File.join(srcdir, "lib"), rubylibdir, :no_install => noinst, :mode => $data_mode)
end
@@ -561,7 +608,7 @@ install?(:local, :comm, :man) do
STDIN.reopen(f)
begin
destfile << suffix
- IO.popen(compress) {|f| f.read}
+ IO.popen(compress, &:read)
ensure
STDIN.reopen(stdin)
stdin.close
@@ -596,8 +643,9 @@ end
module RbInstall
module Specs
class FileCollector
- def initialize(base_dir)
- @base_dir = base_dir
+ def initialize(gemspec)
+ @gemspec = gemspec
+ @base_dir = File.dirname(gemspec)
end
def collect
@@ -620,8 +668,12 @@ module RbInstall
prefix = base.sub(/lib\/.*?\z/, "") + "lib/"
end
- Dir.glob("#{base}{.rb,/**/*.rb}").collect do |ruby_source|
- remove_prefix(prefix, ruby_source)
+ if base
+ Dir.glob("#{base}{.rb,/**/*.rb}").collect do |ruby_source|
+ remove_prefix(prefix, ruby_source)
+ end
+ else
+ [remove_prefix(File.dirname(@gemspec) + '/', @gemspec.gsub(/gemspec/, 'rb'))]
end
end
@@ -636,6 +688,8 @@ module RbInstall
end
when "lib"
[]
+ else
+ []
end
end
@@ -657,10 +711,12 @@ module RbInstall
return if path == destination_dir
File.chmod(0700, destination_dir)
mode = pattern == "bin/*" ? $script_mode : $data_mode
- install_recursive(path, without_destdir(destination_dir),
- :glob => pattern,
- :no_install => "*.gemspec",
- :mode => mode)
+ spec.files.each do |f|
+ src = File.join(path, f)
+ dest = File.join(without_destdir(destination_dir), f)
+ makedirs(dest[/.*(?=\/)/m])
+ install src, dest, :mode => mode
+ end
File.chmod($dir_mode, destination_dir)
end
end
@@ -675,6 +731,40 @@ module RbInstall
def build_extensions
end
+
+ def shebang(bin_file_name)
+ path = File.join(gem_dir, spec.bindir, bin_file_name)
+ first_line = File.open(path, "rb") {|file| file.gets}
+ $script_installer.prolog(first_line).chomp
+ end
+
+ def app_script_text(bin_file_name)
+ # move shell script part after comments generated by RubyGems.
+ super.sub(/\A
+ (\#!\/bin\/sh\n\#.*-\*-\s*ruby\s*-\*-.*\n)
+ ((?:.*\n)*?\#!.*ruby.*\n)
+ \#\n
+ ((?:\#.*\n)+)/x, '\1\3\2')
+ end
+
+ def check_executable_overwrite(filename)
+ return if @wrappers and same_bin_script?(filename, @bin_dir)
+ super
+ end
+
+ def generate_bin_script(filename, bindir)
+ return if same_bin_script?(filename, bindir)
+ super
+ end
+
+ def same_bin_script?(filename, bindir)
+ path = File.join(bindir, formatted_program_filename(filename))
+ begin
+ return true if File.binread(path) == app_script_text(filename)
+ rescue
+ end
+ false
+ end
end
end
@@ -682,7 +772,12 @@ class Gem::Installer
install = instance_method(:install)
define_method(:install) do
spec.post_install_message = nil
- install.bind(self).call
+ begin
+ u = File.umask(0022)
+ install.bind(self).call
+ ensure
+ File.umask(u)
+ end
end
generate_bin_script = instance_method(:generate_bin_script)
@@ -694,18 +789,38 @@ end
# :startdoc:
-install?(:ext, :comm, :gem) do
+install?(:ext, :comm, :gem, :'default-gems', :'default-gems-comm') do
+ install_default_gem('lib', srcdir)
+end
+install?(:ext, :arch, :gem, :'default-gems', :'default-gems-arch') do
+ install_default_gem('ext', srcdir)
+end
+
+def load_gemspec(file)
+ file = File.realpath(file)
+ code = File.read(file, encoding: "utf-8:-")
+ code.gsub!(/`git.*?`/m, '""')
+ code.gsub!(/%x\[git.*?\]/m, '""')
+ spec = eval(code, binding, file)
+ unless Gem::Specification === spec
+ raise TypeError, "[#{file}] isn't a Gem::Specification (#{spec.class} instead)."
+ end
+ spec.loaded_from = file
+ spec
+end
+
+def install_default_gem(dir, srcdir)
gem_dir = Gem.default_dir
directories = Gem.ensure_gem_subdirectories(gem_dir, :mode => $dir_mode)
- prepare "default gems", gem_dir, directories
+ prepare "default gems from #{dir}", gem_dir, directories
spec_dir = File.join(gem_dir, directories.grep(/^spec/)[0])
default_spec_dir = "#{spec_dir}/default"
makedirs(default_spec_dir)
- gems = Dir.glob(srcdir+"/{lib,ext}/**/*.gemspec").map {|src|
- spec = Gem::Specification.load(src) || raise("invalid spec in #{src}")
- file_collector = RbInstall::Specs::FileCollector.new(File.dirname(src))
+ gems = Dir.glob("#{srcdir}/#{dir}/**/*.gemspec").map {|src|
+ spec = load_gemspec(src)
+ file_collector = RbInstall::Specs::FileCollector.new(src)
files = file_collector.collect
next if files.empty?
spec.files = files
@@ -714,7 +829,7 @@ install?(:ext, :comm, :gem) do
gems.compact.sort_by(&:name).each do |gemspec|
full_name = "#{gemspec.name}-#{gemspec.version}"
- puts "#{" "*30}#{gemspec.name} #{gemspec.version}"
+ puts "#{INDENT}#{gemspec.name} #{gemspec.version}"
gemspec_path = File.join(default_spec_dir, "#{full_name}.gemspec")
open_for_install(gemspec_path, $data_mode) do
gemspec.to_ruby
@@ -724,16 +839,18 @@ install?(:ext, :comm, :gem) do
bin_dir = File.join(gem_dir, 'gems', full_name, gemspec.bindir)
makedirs(bin_dir)
- execs = gemspec.executables.map {|exec| File.join(srcdir, 'bin', exec)}
- install(execs, bin_dir, :mode => $script_mode)
+ gemspec.executables.map {|exec|
+ install File.join(srcdir, 'libexec', exec),
+ File.join(bin_dir, exec)
+ }
end
end
end
-install?(:ext, :comm, :gem) do
+install?(:ext, :comm, :gem, :'bundled-gems') do
gem_dir = Gem.default_dir
directories = Gem.ensure_gem_subdirectories(gem_dir, :mode => $dir_mode)
- prepare "bundle gems", gem_dir, directories
+ prepare "bundled gems", gem_dir, directories
install_dir = with_destdir(gem_dir)
installed_gems = {}
options = {
@@ -749,11 +866,9 @@ install?(:ext, :comm, :gem) do
}
gem_ext_dir = "#$extout/gems/#{CONFIG['arch']}"
extensions_dir = Gem::StubSpecification.gemspec_stub("", gem_dir, gem_dir).extensions_dir
- Gem::Specification.each_gemspec([srcdir+'/gems/*']) do |path|
- dir = File.dirname(path)
- spec = Dir.chdir(dir) {
- Gem::Specification.load(File.basename(path))
- }
+ dirs = Gem::Util.glob_files_in_dir "*/", "#{srcdir}/gems"
+ Gem::Specification.each_gemspec(dirs) do |path|
+ spec = load_gemspec(path)
next unless spec.platform == Gem::Platform::RUBY
next unless spec.full_name == path[srcdir.size..-1][/\A\/gems\/([^\/]+)/, 1]
spec.extension_dir = "#{extensions_dir}/#{spec.full_name}"
@@ -761,7 +876,7 @@ install?(:ext, :comm, :gem) do
spec.extensions[0] ||= "-"
end
ins = RbInstall::UnpackedInstaller.new(spec, options)
- puts "#{" "*30}#{spec.name} #{spec.version}"
+ puts "#{INDENT}#{spec.name} #{spec.version}"
ins.install
File.chmod($data_mode, File.join(install_dir, "specifications", "#{spec.full_name}.gemspec"))
unless spec.extensions.empty?
@@ -782,11 +897,11 @@ install?(:ext, :comm, :gem) do
inst.spec.extension_dir = with_destdir(inst.spec.extension_dir)
begin
Gem::DefaultUserInteraction.use_ui(silent) {inst.install}
- rescue Gem::InstallError => e
+ rescue Gem::InstallError
next
end
gemname = File.basename(gem)
- puts "#{" "*30}#{gemname}"
+ puts "#{INDENT}#{gemname}"
end
# fix directory permissions
# TODO: Gem.install should accept :dir_mode option or something
@@ -794,7 +909,7 @@ install?(:ext, :comm, :gem) do
# fix .gemspec permissions
File.chmod($data_mode, *Dir.glob(install_dir+"/specifications/*.gemspec"))
else
- puts "skip installing bundle gems because of lacking zlib"
+ puts "skip installing bundled gems because of lacking zlib"
end
end
diff --git a/tool/redmine-backporter.rb b/tool/redmine-backporter.rb
index 4be41b407a..00eafed96e 100755
--- a/tool/redmine-backporter.rb
+++ b/tool/redmine-backporter.rb
@@ -205,7 +205,7 @@ class << Readline
def readline(prompt = '')
console = IO.console
console.binmode
- ly, lx = console.winsize
+ _, lx = console.winsize
if /mswin|mingw/ =~ RUBY_PLATFORM or /^(?:vt\d\d\d|xterm)/i =~ ENV["TERM"]
cls = "\r\e[2K"
else
@@ -215,7 +215,7 @@ class << Readline
console.print prompt
console.flush
line = ''
- while 1
+ while true
case c = console.getch
when "\r", "\n"
puts
@@ -329,7 +329,7 @@ def status_char(obj)
end
console = IO.console
-row, col = console.winsize
+row, = console.winsize
@query['limit'] = row - 2
puts "Backporter #{VERSION}".color(bold: true) + " for #{TARGET_VERSION}"
@@ -368,8 +368,14 @@ commands = {
unless i["changesets"]
abort "You don't have view_changesets permission"
end
+ unless i["custom_fields"]
+ puts "The specified ticket \##{@issue} seems to be a feature ticket"
+ @issue = nil
+ next
+ end
id = "##{i["id"]}".color(*PRIORITIES[i["priority"]["name"]])
sio = StringIO.new
+ sio.set_encoding("utf-8")
sio.puts <<eom
#{i["subject"].color(bold: true, underscore: true)}
#{i["project"]["name"]} [#{i["tracker"]["name"]} #{id}] #{i["status"]["name"]} (#{i["created_on"]})
@@ -479,7 +485,7 @@ eom
res = http.get(uri.path)
data = JSON(res.body)
h = data["issue"]["custom_fields"].find{|x|x["id"]==5}
- if h and val = h["value"]
+ if h and val = h["value"] and val != ""
case val[/(?:\A|, )#{Regexp.quote TARGET_VERSION}: ([^,]+)/, 1]
when 'REQUIRED', 'UNKNOWN', 'DONTNEED', 'WONTFIX'
val[$~.offset(1)[0]...$~.offset(1)[1]] = 'DONE'
@@ -492,7 +498,7 @@ eom
raise "unknown status '#$1'"
end
else
- val = '#{TARGET_VERSION}: DONE'
+ val = "#{TARGET_VERSION}: DONE"
end
data = { "issue" => { "custom_fields" => [ {"id"=>5, "value" => val} ] } }
diff --git a/tool/ruby_vm/controllers/application_controller.rb b/tool/ruby_vm/controllers/application_controller.rb
new file mode 100644
index 0000000000..ba8f15a0c1
--- /dev/null
+++ b/tool/ruby_vm/controllers/application_controller.rb
@@ -0,0 +1,25 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require_relative '../helpers/dumper'
+require_relative '../models/instructions'
+require_relative '../models/typemap'
+require_relative '../loaders/vm_opts_h'
+
+class ApplicationController
+ def generate i, destdir
+ path = Pathname.new i
+ dst = destdir ? Pathname.new(destdir).join(i) : Pathname.new(i)
+ dumper = RubyVM::Dumper.new dst
+ return [path, dumper]
+ end
+end
diff --git a/tool/ruby_vm/helpers/c_escape.rb b/tool/ruby_vm/helpers/c_escape.rb
new file mode 100644
index 0000000000..2f73b8889f
--- /dev/null
+++ b/tool/ruby_vm/helpers/c_escape.rb
@@ -0,0 +1,128 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require 'securerandom'
+
+module RubyVM::CEscape
+ module_function
+
+ # generate comment, with escaps.
+ def commentify str
+ return "/* #{str.strip.b.gsub '*/', '*\\/'} */"
+ end
+
+ # Mimic gensym of CL.
+ def gensym prefix = 'gensym_'
+ return as_tr_cpp "#{prefix}#{SecureRandom.uuid}"
+ end
+
+ # Mimic AS_TR_CPP() of autoconf.
+ def as_tr_cpp name
+ q = name.b
+ q.gsub! %r/[^a-zA-Z0-9_]/m, '_'
+ q.gsub! %r/_+/, '_'
+ return q
+ end
+
+ # Section 6.10.4 of ISO/IEC 9899:1999 specifies that the file name used for
+ # #line directive shall be a "character string literal". So this is needed.
+ #
+ # I'm not sure how many chars are allowed here, though. The standard
+ # specifies 4095 chars at most, after string concatenation (section 5.2.4.1).
+ # But it is easy to have a path that is longer than that.
+ #
+ # Here we ignore the standard. Just create single string literal of any
+ # needed length.
+ def rstring2cstr str
+ # I believe this is the fastest implementation done in pure-ruby.
+ # Constants cached, gsub skips block evaluation, string literal optimized.
+ buf = str.b
+ buf.gsub! %r/./n, RString2CStr
+ return %'"#{buf}"'
+ end
+
+ RString2CStr = {
+ "\x00"=> "\\0", "\x01"=> "\\x1", "\x02"=> "\\x2", "\x03"=> "\\x3",
+ "\x04"=> "\\x4", "\x05"=> "\\x5", "\x06"=> "\\x6", "\a"=> "\\a",
+ "\b"=> "\\b", "\t"=> "\\t", "\n"=> "\\n", "\v"=> "\\v",
+ "\f"=> "\\f", "\r"=> "\\r", "\x0E"=> "\\xe", "\x0F"=> "\\xf",
+ "\x10"=>"\\x10", "\x11"=>"\\x11", "\x12"=>"\\x12", "\x13"=>"\\x13",
+ "\x14"=>"\\x14", "\x15"=>"\\x15", "\x16"=>"\\x16", "\x17"=>"\\x17",
+ "\x18"=>"\\x18", "\x19"=>"\\x19", "\x1A"=>"\\x1a", "\e"=>"\\x1b",
+ "\x1C"=>"\\x1c", "\x1D"=>"\\x1d", "\x1E"=>"\\x1e", "\x1F"=>"\\x1f",
+ " "=> " ", "!"=> "!", "\""=> "\\\"", "#"=> "#",
+ "$"=> "$", "%"=> "%", "&"=> "&", "'"=> "\\'",
+ "("=> "(", ")"=> ")", "*"=> "*", "+"=> "+",
+ ","=> ",", "-"=> "-", "."=> ".", "/"=> "/",
+ "0"=> "0", "1"=> "1", "2"=> "2", "3"=> "3",
+ "4"=> "4", "5"=> "5", "6"=> "6", "7"=> "7",
+ "8"=> "8", "9"=> "9", ":"=> ":", ";"=> ";",
+ "<"=> "<", "="=> "=", ">"=> ">", "?"=> "?",
+ "@"=> "@", "A"=> "A", "B"=> "B", "C"=> "C",
+ "D"=> "D", "E"=> "E", "F"=> "F", "G"=> "G",
+ "H"=> "H", "I"=> "I", "J"=> "J", "K"=> "K",
+ "L"=> "L", "M"=> "M", "N"=> "N", "O"=> "O",
+ "P"=> "P", "Q"=> "Q", "R"=> "R", "S"=> "S",
+ "T"=> "T", "U"=> "U", "V"=> "V", "W"=> "W",
+ "X"=> "X", "Y"=> "Y", "Z"=> "Z", "["=> "[",
+ "\\"=> "\\\\", "]"=> "]", "^"=> "^", "_"=> "_",
+ "`"=> "`", "a"=> "a", "b"=> "b", "c"=> "c",
+ "d"=> "d", "e"=> "e", "f"=> "f", "g"=> "g",
+ "h"=> "h", "i"=> "i", "j"=> "j", "k"=> "k",
+ "l"=> "l", "m"=> "m", "n"=> "n", "o"=> "o",
+ "p"=> "p", "q"=> "q", "r"=> "r", "s"=> "s",
+ "t"=> "t", "u"=> "u", "v"=> "v", "w"=> "w",
+ "x"=> "x", "y"=> "y", "z"=> "z", "{"=> "{",
+ "|"=> "|", "}"=> "}", "~"=> "~", "\x7F"=>"\\x7f",
+ "\x80"=>"\\x80", "\x81"=>"\\x81", "\x82"=>"\\x82", "\x83"=>"\\x83",
+ "\x84"=>"\\x84", "\x85"=>"\\x85", "\x86"=>"\\x86", "\x87"=>"\\x87",
+ "\x88"=>"\\x88", "\x89"=>"\\x89", "\x8A"=>"\\x8a", "\x8B"=>"\\x8b",
+ "\x8C"=>"\\x8c", "\x8D"=>"\\x8d", "\x8E"=>"\\x8e", "\x8F"=>"\\x8f",
+ "\x90"=>"\\x90", "\x91"=>"\\x91", "\x92"=>"\\x92", "\x93"=>"\\x93",
+ "\x94"=>"\\x94", "\x95"=>"\\x95", "\x96"=>"\\x96", "\x97"=>"\\x97",
+ "\x98"=>"\\x98", "\x99"=>"\\x99", "\x9A"=>"\\x9a", "\x9B"=>"\\x9b",
+ "\x9C"=>"\\x9c", "\x9D"=>"\\x9d", "\x9E"=>"\\x9e", "\x9F"=>"\\x9f",
+ "\xA0"=>"\\xa0", "\xA1"=>"\\xa1", "\xA2"=>"\\xa2", "\xA3"=>"\\xa3",
+ "\xA4"=>"\\xa4", "\xA5"=>"\\xa5", "\xA6"=>"\\xa6", "\xA7"=>"\\xa7",
+ "\xA8"=>"\\xa8", "\xA9"=>"\\xa9", "\xAA"=>"\\xaa", "\xAB"=>"\\xab",
+ "\xAC"=>"\\xac", "\xAD"=>"\\xad", "\xAE"=>"\\xae", "\xAF"=>"\\xaf",
+ "\xB0"=>"\\xb0", "\xB1"=>"\\xb1", "\xB2"=>"\\xb2", "\xB3"=>"\\xb3",
+ "\xB4"=>"\\xb4", "\xB5"=>"\\xb5", "\xB6"=>"\\xb6", "\xB7"=>"\\xb7",
+ "\xB8"=>"\\xb8", "\xB9"=>"\\xb9", "\xBA"=>"\\xba", "\xBB"=>"\\xbb",
+ "\xBC"=>"\\xbc", "\xBD"=>"\\xbd", "\xBE"=>"\\xbe", "\xBF"=>"\\xbf",
+ "\xC0"=>"\\xc0", "\xC1"=>"\\xc1", "\xC2"=>"\\xc2", "\xC3"=>"\\xc3",
+ "\xC4"=>"\\xc4", "\xC5"=>"\\xc5", "\xC6"=>"\\xc6", "\xC7"=>"\\xc7",
+ "\xC8"=>"\\xc8", "\xC9"=>"\\xc9", "\xCA"=>"\\xca", "\xCB"=>"\\xcb",
+ "\xCC"=>"\\xcc", "\xCD"=>"\\xcd", "\xCE"=>"\\xce", "\xCF"=>"\\xcf",
+ "\xD0"=>"\\xd0", "\xD1"=>"\\xd1", "\xD2"=>"\\xd2", "\xD3"=>"\\xd3",
+ "\xD4"=>"\\xd4", "\xD5"=>"\\xd5", "\xD6"=>"\\xd6", "\xD7"=>"\\xd7",
+ "\xD8"=>"\\xd8", "\xD9"=>"\\xd9", "\xDA"=>"\\xda", "\xDB"=>"\\xdb",
+ "\xDC"=>"\\xdc", "\xDD"=>"\\xdd", "\xDE"=>"\\xde", "\xDF"=>"\\xdf",
+ "\xE0"=>"\\xe0", "\xE1"=>"\\xe1", "\xE2"=>"\\xe2", "\xE3"=>"\\xe3",
+ "\xE4"=>"\\xe4", "\xE5"=>"\\xe5", "\xE6"=>"\\xe6", "\xE7"=>"\\xe7",
+ "\xE8"=>"\\xe8", "\xE9"=>"\\xe9", "\xEA"=>"\\xea", "\xEB"=>"\\xeb",
+ "\xEC"=>"\\xec", "\xED"=>"\\xed", "\xEE"=>"\\xee", "\xEF"=>"\\xef",
+ "\xF0"=>"\\xf0", "\xF1"=>"\\xf1", "\xF2"=>"\\xf2", "\xF3"=>"\\xf3",
+ "\xF4"=>"\\xf4", "\xF5"=>"\\xf5", "\xF6"=>"\\xf6", "\xF7"=>"\\xf7",
+ "\xF8"=>"\\xf8", "\xF9"=>"\\xf9", "\xFA"=>"\\xfa", "\xFB"=>"\\xfb",
+ "\xFC"=>"\\xfc", "\xFD"=>"\\xfd", "\xFE"=>"\\xfe", "\xFF"=>"\\xff",
+ }.freeze
+ private_constant :RString2CStr
+end
+
+unless defined? ''.b
+ class String
+ def b
+ return dup.force_encoding 'binary'
+ end
+ end
+end
diff --git a/tool/ruby_vm/helpers/dumper.rb b/tool/ruby_vm/helpers/dumper.rb
new file mode 100644
index 0000000000..ae2a0145cf
--- /dev/null
+++ b/tool/ruby_vm/helpers/dumper.rb
@@ -0,0 +1,112 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require 'pathname'
+require 'erb'
+require_relative 'c_escape'
+
+class RubyVM::Dumper
+ include RubyVM::CEscape
+ private
+
+ def new_binding
+ # This `eval 'binding'` does not return the current binding
+ # but creates one on top of it.
+ return eval 'binding'
+ end
+
+ def new_erb spec
+ path = Pathname.new(__FILE__).relative_path_from(Pathname.pwd).dirname
+ path += '../views'
+ path += spec
+ src = path.read mode: 'rt:utf-8:utf-8'
+ rescue Errno::ENOENT
+ raise "don't know how to generate #{path}"
+ else
+ if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
+ erb = ERB.new(src, trim_mode: '%-')
+ else
+ erb = ERB.new(src, nil, '%-')
+ end
+ erb.filename = path.to_path
+ return erb
+ end
+
+ def finderb spec
+ return @erb.fetch spec do |k|
+ erb = new_erb k
+ @erb[k] = erb
+ end
+ end
+
+ def replace_pragma_line str, lineno
+ if str == "#pragma RubyVM reset source\n" then
+ return "#line #{lineno + 2} #{@file}\n"
+ else
+ return str
+ end
+ end
+
+ def local_variable_set bnd, var, val
+ eval '__locals__ ||= {}', bnd
+ locals = eval '__locals__', bnd
+ locals[var] = val
+ eval "#{var} = __locals__[:#{var}]", bnd
+ test = eval "#{var}", bnd
+ raise unless test == val
+ end
+
+ public
+
+ def do_render source, locals
+ erb = finderb source
+ bnd = @empty.dup
+ locals.each_pair do |k, v|
+ local_variable_set bnd, k, v
+ end
+ return erb.result bnd
+ end
+
+ def replace_pragma str
+ return str \
+ . each_line \
+ . with_index \
+ . map {|i, j| replace_pragma_line i, j } \
+ . join
+ end
+
+ def initialize dst
+ @erb = {}
+ @empty = new_binding
+ @file = cstr dst.to_path
+ end
+
+ def render partial, opts = { :locals => {} }
+ return do_render "_#{partial}.erb", opts[:locals]
+ end
+
+ def generate template
+ str = do_render "#{template}.erb", {}
+ return replace_pragma str
+ end
+
+ private
+
+ # view helpers
+
+ alias cstr rstring2cstr
+ alias comm commentify
+
+ def render_c_expr expr
+ render 'c_expr', locals: { expr: expr, }
+ end
+end
diff --git a/tool/ruby_vm/helpers/scanner.rb b/tool/ruby_vm/helpers/scanner.rb
new file mode 100644
index 0000000000..410b02eff5
--- /dev/null
+++ b/tool/ruby_vm/helpers/scanner.rb
@@ -0,0 +1,52 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require 'pathname'
+
+# Poor man's StringScanner.
+# Sadly https://bugs.ruby-lang.org/issues/8343 is not backported to 2.0. We
+# have to do it by hand.
+class RubyVM::Scanner
+ attr_reader :__FILE__
+ attr_reader :__LINE__
+
+ def initialize path
+ src = Pathname.new(__FILE__).relative_path_from(Pathname.pwd).dirname
+ src += path
+ @__LINE__ = 1
+ @__FILE__ = src.to_path
+ @str = src.read mode: 'rt:utf-8:utf-8'
+ @pos = 0
+ end
+
+ def eos?
+ return @pos >= @str.length
+ end
+
+ def scan re
+ ret = @__LINE__
+ @last_match = @str.match re, @pos
+ return unless @last_match
+ @__LINE__ += @last_match.to_s.count "\n"
+ @pos = @last_match.end 0
+ return ret
+ end
+
+ def scan! re
+ scan re or raise sprintf "parse error at %s:%d near:\n %s...", \
+ @__FILE__, @__LINE__, @str[@pos, 32]
+ end
+
+ def [] key
+ return @last_match[key]
+ end
+end
diff --git a/tool/ruby_vm/loaders/insns_def.rb b/tool/ruby_vm/loaders/insns_def.rb
new file mode 100644
index 0000000000..f83cc77fa1
--- /dev/null
+++ b/tool/ruby_vm/loaders/insns_def.rb
@@ -0,0 +1,92 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require_relative '../helpers/scanner'
+
+json = []
+scanner = RubyVM::Scanner.new '../../../insns.def'
+path = scanner.__FILE__
+grammar = %r'
+ (?<comment> /[*] [^*]* [*]+ (?: [^*/] [^*]* [*]+ )* / ){0}
+ (?<keyword> typedef | extern | static | auto | register |
+ struct | union | enum ){0}
+ (?<C> (?: \g<block> | [^{}]+ )* ){0}
+ (?<block> \{ \g<ws>* ^ \g<C> $ \g<ws>* \} ){0}
+ (?<ws> \g<comment> | \s ){0}
+ (?<ident> [_a-zA-Z] [0-9_a-zA-Z]* ){0}
+ (?<type> (?: \g<keyword> \g<ws>+ )* \g<ident> ){0}
+ (?<arg> \g<type> \g<ws>+ \g<ident> | \.\.\. ){0}
+ (?<argv> (?# empty ) |
+ void |
+ \g<arg> (?: \g<ws>* , \g<ws>* \g<arg> \g<ws>* )* ){0}
+ (?<pragma> \g<ws>* // \s* attr \g<ws>+
+ (?<pragma:type> \g<type> ) \g<ws>+
+ (?<pragma:name> \g<ident> ) \g<ws>*
+ = \g<ws>*
+ (?<pragma:expr> .+?; ) \g<ws>* ){0}
+ (?<insn> DEFINE_INSN \g<ws>+
+ (?<insn:name> \g<ident> ) \g<ws>*
+ [(] \g<ws>* (?<insn:opes> \g<argv> ) \g<ws>* [)] \g<ws>*
+ [(] \g<ws>* (?<insn:pops> \g<argv> ) \g<ws>* [)] \g<ws>*
+ [(] \g<ws>* (?<insn:rets> \g<argv> ) \g<ws>* [)] \g<ws>* ){0}
+'x
+
+until scanner.eos? do
+ next if scanner.scan(/\G#{grammar}\g<ws>+/o)
+ split = lambda {|v|
+ case v when /\Avoid\z/ then
+ []
+ else
+ v.split(/, */)
+ end
+ }
+
+ l1 = scanner.scan!(/\G#{grammar}\g<insn>/o)
+ name = scanner["insn:name"]
+ ope = split.(scanner["insn:opes"])
+ pop = split.(scanner["insn:pops"])
+ ret = split.(scanner["insn:rets"])
+
+ attrs = []
+ while l2 = scanner.scan(/\G#{grammar}\g<pragma>/o) do
+ attrs << {
+ location: [path, l2],
+ name: scanner["pragma:name"],
+ type: scanner["pragma:type"],
+ expr: scanner["pragma:expr"],
+ }
+ end
+
+ l3 = scanner.scan!(/\G#{grammar}\g<block>/o)
+ json << {
+ name: name,
+ location: [path, l1],
+ signature: {
+ name: name,
+ ope: ope,
+ pop: pop,
+ ret: ret,
+ },
+ attributes: attrs,
+ expr: {
+ location: [path, l3],
+ expr: scanner["block"],
+ },
+ }
+end
+
+RubyVM::InsnsDef = json
+
+if __FILE__ == $0 then
+ require 'json'
+ JSON.dump RubyVM::InsnsDef, STDOUT
+end
diff --git a/tool/ruby_vm/loaders/opt_insn_unif_def.rb b/tool/ruby_vm/loaders/opt_insn_unif_def.rb
new file mode 100644
index 0000000000..0f5de69930
--- /dev/null
+++ b/tool/ruby_vm/loaders/opt_insn_unif_def.rb
@@ -0,0 +1,34 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require_relative '../helpers/scanner'
+
+json = []
+scanner = RubyVM::Scanner.new '../../../defs/opt_insn_unif.def'
+path = scanner.__FILE__
+until scanner.eos? do
+ next if scanner.scan(/\G ^ (?: \#.* )? \n /x)
+ break if scanner.scan(/\G ^ __END__ $ /x)
+
+ pos = scanner.scan!(/\G (?<series> (?: [\ \t]* \w+ )+ ) \n /mx)
+ json << {
+ location: [path, pos],
+ signature: scanner["series"].strip.split
+ }
+end
+
+RubyVM::OptInsnUnifDef = json
+
+if __FILE__ == $0 then
+ require 'json'
+ JSON.dump RubyVM::OptInsnUnifDef, STDOUT
+end
diff --git a/tool/ruby_vm/loaders/opt_operand_def.rb b/tool/ruby_vm/loaders/opt_operand_def.rb
new file mode 100644
index 0000000000..3022f9915d
--- /dev/null
+++ b/tool/ruby_vm/loaders/opt_operand_def.rb
@@ -0,0 +1,56 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require_relative '../helpers/scanner'
+
+json = []
+scanner = RubyVM::Scanner.new '../../../defs/opt_operand.def'
+path = scanner.__FILE__
+grammar = %r/
+ (?<comment> \# .+? \n ){0}
+ (?<ws> \g<comment> | \s ){0}
+ (?<insn> \w+ ){0}
+ (?<paren> \( (?: \g<paren> | [^()]+)* \) ){0}
+ (?<expr> (?: \g<paren> | [^(),\ \n] )+ ){0}
+ (?<remain> \g<expr> ){0}
+ (?<arg> \g<expr> ){0}
+ (?<extra> , \g<ws>* \g<remain> ){0}
+ (?<args> \g<arg> \g<extra>* ){0}
+ (?<decl> \g<insn> \g<ws>+ \g<args> \n ){0}
+/mx
+
+until scanner.eos? do
+ break if scanner.scan(/\G ^ __END__ $ /x)
+ next if scanner.scan(/\G#{grammar} \g<ws>+ /ox)
+
+ line = scanner.scan!(/\G#{grammar} \g<decl> /mox)
+ insn = scanner["insn"]
+ args = scanner["args"]
+ ary = []
+ until args.strip.empty? do
+ md = /\G#{grammar} \g<args> /mox.match(args)
+ ary << md["arg"]
+ args = md["remain"]
+ break unless args
+ end
+ json << {
+ location: [path, line],
+ signature: [insn, ary]
+ }
+end
+
+RubyVM::OptOperandDef = json
+
+if __FILE__ == $0 then
+ require 'json'
+ JSON.dump RubyVM::OptOperandDef, STDOUT
+end
diff --git a/tool/ruby_vm/loaders/vm_opts_h.rb b/tool/ruby_vm/loaders/vm_opts_h.rb
new file mode 100644
index 0000000000..d28db4eaa2
--- /dev/null
+++ b/tool/ruby_vm/loaders/vm_opts_h.rb
@@ -0,0 +1,37 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require_relative '../helpers/scanner'
+
+json = {}
+scanner = RubyVM::Scanner.new '../../../vm_opts.h'
+grammar = %r/
+ (?<ws> \u0020 ){0}
+ (?<key> \w+ ){0}
+ (?<value> 0|1 ){0}
+ (?<define> \G \#define \g<ws>+ OPT_\g<key> \g<ws>+ \g<value> \g<ws>*\n )
+/mx
+
+until scanner.eos? do
+ if scanner.scan grammar then
+ json[scanner['key']] = ! scanner['value'].to_i.zero? # not nonzero?
+ else
+ scanner.scan(/\G.*\n/)
+ end
+end
+
+RubyVM::VmOptsH = json
+
+if __FILE__ == $0 then
+ require 'json'
+ JSON.dump RubyVM::VmOptsH, STDOUT
+end
diff --git a/tool/ruby_vm/models/attribute.rb b/tool/ruby_vm/models/attribute.rb
new file mode 100644
index 0000000000..f9e158ac41
--- /dev/null
+++ b/tool/ruby_vm/models/attribute.rb
@@ -0,0 +1,54 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require_relative 'c_expr'
+
+class RubyVM::Attribute
+ include RubyVM::CEscape
+ attr_reader :insn, :key, :type, :expr
+
+ def initialize opts = {}
+ @insn = opts[:insn]
+ @key = opts[:name]
+ @expr = RubyVM::CExpr.new location: opts[:location], expr: opts[:expr]
+ @type = opts[:type]
+ end
+
+ def name
+ as_tr_cpp "attr #{@key} @ #{@insn.name}"
+ end
+
+ def pretty_name
+ "attr #{type} #{key} @ #{insn.pretty_name}"
+ end
+
+ def declaration
+ opes = @insn.opes
+ if opes.empty?
+ argv = "void"
+ else
+ argv = opes.map {|o| o[:decl] }.join(', ')
+ end
+ sprintf '%s %s(%s)', @type, name, argv
+ end
+
+ def definition
+ opes = @insn.opes
+ if opes.empty?
+ argv = "void"
+ else
+ argv = opes.map {|o| "MAYBE_UNUSED(#{o[:decl]})" }.join(",\n ")
+ argv = "\n #{argv}\n" if opes.size > 1
+ end
+ sprintf "%s\n%s(%s)", @type, name, argv
+ end
+end
diff --git a/tool/ruby_vm/models/bare_instructions.rb b/tool/ruby_vm/models/bare_instructions.rb
new file mode 100755
index 0000000000..2767f383e6
--- /dev/null
+++ b/tool/ruby_vm/models/bare_instructions.rb
@@ -0,0 +1,207 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require_relative '../loaders/insns_def'
+require_relative 'c_expr'
+require_relative 'typemap'
+require_relative 'attribute'
+
+class RubyVM::BareInstructions
+ attr_reader :template, :name, :opes, :pops, :rets, :decls, :expr
+
+ def initialize opts = {}
+ @template = opts[:template]
+ @name = opts[:name]
+ @loc = opts[:location]
+ @sig = opts[:signature]
+ @expr = RubyVM::CExpr.new opts[:expr]
+ @opes = typesplit @sig[:ope]
+ @pops = typesplit @sig[:pop].reject {|i| i == '...' }
+ @rets = typesplit @sig[:ret].reject {|i| i == '...' }
+ @attrs = opts[:attributes].map {|i|
+ RubyVM::Attribute.new i.merge(:insn => self)
+ }.each_with_object({}) {|a, h|
+ h[a.key] = a
+ }
+ @attrs_orig = @attrs.dup
+ predefine_attributes
+ end
+
+ def pretty_name
+ n = @sig[:name]
+ o = @sig[:ope].map{|i| i[/\S+$/] }.join ', '
+ p = @sig[:pop].map{|i| i[/\S+$/] }.join ', '
+ r = @sig[:ret].map{|i| i[/\S+$/] }.join ', '
+ return sprintf "%s(%s)(%s)(%s)", n, o, p, r
+ end
+
+ def bin
+ return "BIN(#{name})"
+ end
+
+ def call_attribute name
+ return sprintf 'attr_%s_%s(%s)', name, @name, \
+ @opes.map {|i| i[:name] }.compact.join(', ')
+ end
+
+ def has_attribute? k
+ @attrs_orig.has_key? k
+ end
+
+ def attributes
+ return @attrs \
+ . sort_by {|k, _| k } \
+ . map {|_, v| v }
+ end
+
+ def width
+ return 1 + opes.size
+ end
+
+ def declarations
+ return @variables \
+ . values \
+ . group_by {|h| h[:type] } \
+ . sort_by {|t, v| t } \
+ . map {|t, v| [t, v.map {|i| i[:name] }.sort ] } \
+ . map {|t, v|
+ sprintf("MAYBE_UNUSED(%s) %s", t, v.join(', '))
+ }
+ end
+
+ def preamble
+ # preamble makes sense for operand unifications
+ return []
+ end
+
+ def sc?
+ # sc stands for stack caching.
+ return false
+ end
+
+ def cast_to_VALUE var, expr = var[:name]
+ RubyVM::Typemap.typecast_to_VALUE var[:type], expr
+ end
+
+ def cast_from_VALUE var, expr = var[:name]
+ RubyVM::Typemap.typecast_from_VALUE var[:type], expr
+ end
+
+ def operands_info
+ opes.map {|o|
+ c, _ = RubyVM::Typemap.fetch o[:type]
+ next c
+ }.join
+ end
+
+ def handles_sp?
+ /\b(false|0)\b/ !~ @attrs.fetch('handles_sp').expr.expr
+ end
+
+ def always_leaf?
+ @attrs.fetch('leaf').expr.expr == 'true;'
+ end
+
+ def handle_canary stmt
+ # Stack canary is basically a good thing that we want to add, however:
+ #
+ # - When the instruction returns variadic number of return values,
+ # it is not easy to tell where is the stack top. We can't but
+ # skip it.
+ #
+ # - When the instruction body is empty (like putobject), we can
+ # say for 100% sure that canary is a waste of time.
+ #
+ # So we skip canary for those cases.
+ return '' if @sig[:ret].any? {|i| i == '...' }
+ return '' if @expr.blank?
+ return " #{stmt};\n"
+ end
+
+ def inspect
+ sprintf "#<%s %s@%s:%d>", self.class.name, @name, @loc[0], @loc[1]
+ end
+
+ private
+
+ def generate_attribute t, k, v
+ @attrs[k] ||= RubyVM::Attribute.new \
+ insn: self, \
+ name: k, \
+ type: t, \
+ location: [], \
+ expr: v.to_s + ';'
+ return @attrs[k] ||= attr
+ end
+
+ def predefine_attributes
+ # Beware: order matters here because some attribute depends another.
+ generate_attribute 'const char*', 'name', "insn_name(#{bin})"
+ generate_attribute 'enum ruby_vminsn_type', 'bin', bin
+ generate_attribute 'rb_num_t', 'open', opes.size
+ generate_attribute 'rb_num_t', 'popn', pops.size
+ generate_attribute 'rb_num_t', 'retn', rets.size
+ generate_attribute 'rb_num_t', 'width', width
+ generate_attribute 'rb_snum_t', 'sp_inc', rets.size - pops.size
+ generate_attribute 'bool', 'handles_sp', default_definition_of_handles_sp
+ generate_attribute 'bool', 'leaf', default_definition_of_leaf
+ end
+
+ def default_definition_of_handles_sp
+ # Insn with ISEQ should yield it; can handle sp.
+ return opes.any? {|o| o[:type] == 'ISEQ' }
+ end
+
+ def default_definition_of_leaf
+ # Insn that handles SP can never be a leaf.
+ if not has_attribute? 'handles_sp' then
+ return ! default_definition_of_handles_sp
+ elsif handles_sp? then
+ return "! #{call_attribute 'handles_sp'}"
+ else
+ return true
+ end
+ end
+
+ def typesplit a
+ @variables ||= {}
+ a.map do |decl|
+ md = %r'
+ (?<comment> /[*] [^*]* [*]+ (?: [^*/] [^*]* [*]+ )* / ){0}
+ (?<ws> \g<comment> | \s ){0}
+ (?<ident> [_a-zA-Z] [0-9_a-zA-Z]* ){0}
+ (?<type> (?: \g<ident> \g<ws>+ )* \g<ident> ){0}
+ (?<var> \g<ident> ){0}
+ \G \g<ws>* \g<type> \g<ws>+ \g<var>
+ 'x.match(decl)
+ @variables[md['var']] ||= {
+ decl: decl,
+ type: md['type'],
+ name: md['var'],
+ }
+ end
+ end
+
+ @instances = RubyVM::InsnsDef.map {|h|
+ new h.merge(:template => h)
+ }
+
+ def self.fetch name
+ @instances.find do |insn|
+ insn.name == name
+ end or raise IndexError, "instruction not found: #{name}"
+ end
+
+ def self.to_a
+ @instances
+ end
+end
diff --git a/tool/ruby_vm/models/c_expr.rb b/tool/ruby_vm/models/c_expr.rb
new file mode 100644
index 0000000000..923651a26b
--- /dev/null
+++ b/tool/ruby_vm/models/c_expr.rb
@@ -0,0 +1,41 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require_relative '../helpers/c_escape.rb'
+
+class RubyVM::CExpr
+ include RubyVM::CEscape
+
+ attr_reader :__FILE__, :__LINE__, :expr
+
+ def initialize opts = {}
+ @__FILE__ = opts[:location][0]
+ @__LINE__ = opts[:location][1]
+ @expr = opts[:expr]
+ end
+
+ # blank, in sense of C program.
+ RE = %r'\A{\g<s>*}\z|\A(?<s>\s|/[*][^*]*[*]+([^*/][^*]*[*]+)*/)*\z'
+ if RUBY_VERSION > '2.4' then
+ def blank?
+ RE.match? @expr
+ end
+ else
+ def blank?
+ RE =~ @expr
+ end
+ end
+
+ def inspect
+ sprintf "#<%s:%d %s>", @__FILE__, @__LINE__, @expr
+ end
+end
diff --git a/tool/ruby_vm/models/instructions.rb b/tool/ruby_vm/models/instructions.rb
new file mode 100644
index 0000000000..5a04190a19
--- /dev/null
+++ b/tool/ruby_vm/models/instructions.rb
@@ -0,0 +1,22 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require_relative 'bare_instructions'
+require_relative 'operands_unifications'
+require_relative 'instructions_unifications'
+
+RubyVM::Instructions = RubyVM::BareInstructions.to_a + \
+ RubyVM::OperandsUnifications.to_a + \
+ RubyVM::InstructionsUnifications.to_a
+
+require_relative 'trace_instructions'
+RubyVM::Instructions.freeze
diff --git a/tool/ruby_vm/models/instructions_unifications.rb b/tool/ruby_vm/models/instructions_unifications.rb
new file mode 100644
index 0000000000..aa8cef1bd5
--- /dev/null
+++ b/tool/ruby_vm/models/instructions_unifications.rb
@@ -0,0 +1,43 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require_relative '../helpers/c_escape'
+require_relative '../loaders/opt_insn_unif_def'
+require_relative 'bare_instructions'
+
+class RubyVM::InstructionsUnifications
+ include RubyVM::CEscape
+
+ attr_reader :name
+
+ def initialize opts = {}
+ @location = opts[:location]
+ @name = namegen opts[:signature]
+ @series = opts[:signature].map do |i|
+ RubyVM::BareInstructions.fetch i # Misshit is fatal
+ end
+ end
+
+ private
+
+ def namegen signature
+ as_tr_cpp ['UNIFIED', *signature].join('_')
+ end
+
+ @instances = RubyVM::OptInsnUnifDef.map do |h|
+ new h
+ end
+
+ def self.to_a
+ @instances
+ end
+end
diff --git a/tool/ruby_vm/models/operands_unifications.rb b/tool/ruby_vm/models/operands_unifications.rb
new file mode 100644
index 0000000000..184bb7d79c
--- /dev/null
+++ b/tool/ruby_vm/models/operands_unifications.rb
@@ -0,0 +1,137 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require_relative '../helpers/c_escape'
+require_relative '../loaders/opt_operand_def'
+require_relative 'bare_instructions'
+
+class RubyVM::OperandsUnifications < RubyVM::BareInstructions
+ include RubyVM::CEscape
+
+ attr_reader :preamble, :original, :spec
+
+ def initialize opts = {}
+ name = opts[:signature][0]
+ @original = RubyVM::BareInstructions.fetch name
+ template = @original.template
+ parts = compose opts[:location], opts[:signature], template[:signature]
+ json = template.dup
+ json[:location] = opts[:location]
+ json[:signature] = parts[:signature]
+ json[:name] = parts[:name]
+ @preamble = parts[:preamble]
+ @spec = parts[:spec]
+ super json.merge(:template => template)
+ parts[:vars].each do |v|
+ @variables[v[:name]] ||= v
+ end
+ end
+
+ def operand_shift_of var
+ before = @original.opes.find_index var
+ after = @opes.find_index var
+ raise "no #{var} for #{@name}" unless before and after
+ return before - after
+ end
+
+ def condition ptr
+ # :FIXME: I'm not sure if this method should be in model?
+ exprs = @spec.each_with_index.map do |(var, val), i|
+ case val when '*' then
+ next nil
+ else
+ type = @original.opes[i][:type]
+ expr = RubyVM::Typemap.typecast_to_VALUE type, val
+ next "#{ptr}[#{i}] == #{expr}"
+ end
+ end
+ exprs.compact!
+ if exprs.size == 1 then
+ return exprs[0]
+ else
+ exprs.map! {|i| "(#{i})" }
+ return exprs.join ' && '
+ end
+ end
+
+ private
+
+ def namegen signature
+ insn, argv = *signature
+ wcary = argv.map do |i|
+ case i when '*' then
+ 'WC'
+ else
+ i
+ end
+ end
+ as_tr_cpp [insn, *wcary].join(', ')
+ end
+
+ def compose location, spec, template
+ name = namegen spec
+ *, argv = *spec
+ opes = @original.opes
+ if opes.size != argv.size
+ raise sprintf("operand size mismatch for %s (%s's: %d, given: %d)",
+ name, template[:name], opes.size, argv.size)
+ else
+ src = []
+ mod = []
+ spec = []
+ vars = []
+ argv.each_index do |i|
+ j = argv[i]
+ k = opes[i]
+ spec[i] = [k, j]
+ case j when '*' then
+ # operand is from iseq
+ mod << k[:decl]
+ else
+ # operand is inside C
+ vars << k
+ src << {
+ location: location,
+ expr: " #{k[:name]} = #{j};"
+ }
+ end
+ end
+ src.map! {|i| RubyVM::CExpr.new i }
+ return {
+ name: name,
+ signature: {
+ name: name,
+ ope: mod,
+ pop: template[:pop],
+ ret: template[:ret],
+ },
+ preamble: src,
+ vars: vars,
+ spec: spec
+ }
+ end
+ end
+
+ @instances = RubyVM::OptOperandDef.map do |h|
+ new h
+ end
+
+ def self.to_a
+ @instances
+ end
+
+ def self.each_group
+ to_a.group_by(&:original).each_pair do |k, v|
+ yield k, v
+ end
+ end
+end
diff --git a/tool/ruby_vm/models/trace_instructions.rb b/tool/ruby_vm/models/trace_instructions.rb
new file mode 100644
index 0000000000..fc904a11b5
--- /dev/null
+++ b/tool/ruby_vm/models/trace_instructions.rb
@@ -0,0 +1,71 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require_relative '../helpers/c_escape'
+require_relative 'bare_instructions'
+
+class RubyVM::TraceInstructions
+ include RubyVM::CEscape
+
+ attr_reader :name
+
+ def initialize orig
+ @orig = orig
+ @name = as_tr_cpp "trace @ #{@orig.name}"
+ end
+
+ def pretty_name
+ return sprintf "%s(...)(...)(...)", @name
+ end
+
+ def jump_destination
+ return @orig.name
+ end
+
+ def bin
+ return sprintf "BIN(%s)", @name
+ end
+
+ def width
+ return @orig.width
+ end
+
+ def operands_info
+ return @orig.operands_info
+ end
+
+ def rets
+ return ['...']
+ end
+
+ def pops
+ return ['...']
+ end
+
+ def attributes
+ return []
+ end
+
+ def has_attribute? *;
+ return false
+ end
+
+ private
+
+ @instances = RubyVM::Instructions.map {|i| new i }
+
+ def self.to_a
+ @instances
+ end
+
+ RubyVM::Instructions.push(*to_a)
+end
diff --git a/tool/ruby_vm/models/typemap.rb b/tool/ruby_vm/models/typemap.rb
new file mode 100644
index 0000000000..7c5c872d7c
--- /dev/null
+++ b/tool/ruby_vm/models/typemap.rb
@@ -0,0 +1,62 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+RubyVM::Typemap = {
+ "..." => %w[. TS_VARIABLE],
+ "CALL_CACHE" => %w[E TS_CALLCACHE],
+ "CALL_INFO" => %w[C TS_CALLINFO],
+ "CDHASH" => %w[H TS_CDHASH],
+ "GENTRY" => %w[G TS_GENTRY],
+ "IC" => %w[K TS_IC],
+ "ID" => %w[I TS_ID],
+ "ISE" => %w[T TS_ISE],
+ "ISEQ" => %w[S TS_ISEQ],
+ "OFFSET" => %w[O TS_OFFSET],
+ "VALUE" => %w[V TS_VALUE],
+ "lindex_t" => %w[L TS_LINDEX],
+ "rb_insn_func_t" => %w[F TS_FUNCPTR],
+ "rb_num_t" => %w[N TS_NUM],
+}
+
+# :FIXME: should this method be here?
+class << RubyVM::Typemap
+ def typecast_from_VALUE type, val
+ # see also iseq_set_sequence()
+ case type
+ when '...'
+ raise "cast not possible: #{val}"
+ when 'VALUE' then
+ return val
+ when 'rb_num_t', 'lindex_t' then
+ return "NUM2LONG(#{val})"
+ when 'ID' then
+ return "SYM2ID(#{val})"
+ else
+ return "(#{type})(#{val})"
+ end
+ end
+
+ def typecast_to_VALUE type, val
+ case type
+ when 'VALUE' then
+ return val
+ when 'ISEQ', 'rb_insn_func_t' then
+ return "(VALUE)(#{val})"
+ when 'rb_num_t', 'lindex_t'
+ "LONG2NUM(#{val})"
+ when 'ID' then
+ return "ID2SYM(#{val})"
+ else
+ raise ":FIXME: TBW for #{type}"
+ end
+ end
+end
diff --git a/tool/ruby_vm/scripts/converter.rb b/tool/ruby_vm/scripts/converter.rb
new file mode 100644
index 0000000000..4e7c28d67b
--- /dev/null
+++ b/tool/ruby_vm/scripts/converter.rb
@@ -0,0 +1,29 @@
+# This script was needed only once when I converted the old insns.def.
+# Consider historical.
+#
+# ruby converter.rb insns.def | sponge insns.def
+
+BEGIN { $str = ARGF.read }
+END { puts $str }
+
+# deal with spaces
+$str.gsub! %r/\r\n|\r|\n|\z/, "\n"
+$str.gsub! %r/([^\t\n]*)\t/ do
+ x = $1
+ y = 8 - x.length % 8
+ next x + ' ' * y
+end
+$str.gsub! %r/\s+$/, "\n"
+
+# deal with comments
+$str.gsub! %r/@c.*?@e/m, ''
+$str.gsub! %r/@j.*?\*\//m, '*/'
+$str.gsub! %r/\n(\s*\n)+/, "\n\n"
+$str.gsub! %r/\/\*\*?\s*\n\s*/, "/* "
+$str.gsub! %r/\n\s+\*\//, " */"
+$str.gsub! %r/^(?!.*\/\*.+\*\/$)(.+?)\s*\*\//, "\\1\n */"
+
+# deal with sp_inc
+$str.gsub! %r/ \/\/ inc -= (.*)/, ' // inc += -\\1'
+$str.gsub! %r/\s+\/\/ inc \+= (.*)/, "\n// attr rb_snum_t sp_inc = \\1;"
+$str.gsub! %r/;;$/, ";"
diff --git a/tool/ruby_vm/scripts/insns2vm.rb b/tool/ruby_vm/scripts/insns2vm.rb
new file mode 100644
index 0000000000..564d91cd2d
--- /dev/null
+++ b/tool/ruby_vm/scripts/insns2vm.rb
@@ -0,0 +1,93 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# 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.
+
+require 'optparse'
+require_relative '../controllers/application_controller.rb'
+
+module RubyVM::Insns2VM
+ def self.router argv
+ options = { destdir: nil }
+ targets = generate_parser(options).parse argv
+ return targets.map do |i|
+ next ApplicationController.new.generate i, options[:destdir]
+ end
+ end
+
+ def self.generate_parser(options)
+ OptionParser.new do |this|
+ this.on "-I", "--srcdir=DIR", <<-'end'
+ Historically this option has been passed to the script. This is
+ supposedly because at the beginning the script was placed
+ outside of the ruby source tree. Decades passed since the merge
+ of YARV, now I can safely assume this feature is obsolescent.
+ Just ignore the passed value here.
+ end
+
+ this.on "-L", "--vpath=SPEC", <<-'end'
+ Likewise, this option is no longer supported.
+ end
+
+ this.on "--path-separator=SEP", /\A(?:\W\z|\.(\W).+)/, <<-'end'
+ Old script says this option is a "separator for vpath". I am
+ confident we no longer need this option.
+ end
+
+ this.on "-Dname", "--enable=name[,name...]", Array, <<-'end'
+ This option was used to override VM option that is defined in
+ vm_opts.h. Now it is officially unsupported because vm_opts.h to
+ remain mismatched with this option must break things. Just edit
+ vm_opts.h directly.
+ end
+
+ this.on "-Uname", "--disable=name[,name...]", Array, <<-'end'
+ This option was used to override VM option that is defined in
+ vm_opts.h. Now it is officially unsupported because vm_opts.h to
+ remain mismatched with this option must break things. Just edit
+ vm_opts.h directly.
+ end
+
+ this.on "-i", "--insnsdef=FILE", "--instructions-def", <<-'end'
+ This option was used to specify alternative path to insns.def. For
+ the same reason to ignore -I, we no longer support this.
+ end
+
+ this.on "-o", "--opt-operanddef=FILE", "--opt-operand-def", <<-'end'
+ This option was used to specify alternative path to opt_operand.def.
+ For the same reason to ignore -I, we no longer support this.
+ end
+
+ this.on "-u", "--opt-insnunifdef=FILE", "--opt-insn-unif-def", <<-'end'
+ This option was used to specify alternative path to
+ opt_insn_unif.def. For the same reason to ignore -I, we no
+ longer support this.
+ end
+
+ this.on "-C", "--[no-]use-const", <<-'end'
+ We use const whenever possible now so this option is ignored.
+ The author believes that C compilers can constant-fold.
+ end
+
+ this.on "-d", "--destdir", "--output-directory=DIR", <<-'begin' do |dir|
+ THIS IS THE ONLY OPTION THAT WORKS today. Change destination
+ directory from the current working directory to the given path.
+ begin
+ raise "directory was not found in '#{dir}'" unless Dir.exist?(dir)
+ options[:destdir] = dir
+ end
+
+ this.on "-V", "--[no-]verbose", <<-'end'
+ Please let us ignore this and be modest.
+ end
+ end
+ end
+ private_class_method :generate_parser
+end
diff --git a/tool/ruby_vm/tests/.gitkeep b/tool/ruby_vm/tests/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tool/ruby_vm/tests/.gitkeep
diff --git a/tool/ruby_vm/views/_attributes.erb b/tool/ruby_vm/views/_attributes.erb
new file mode 100644
index 0000000000..9229f1a5ce
--- /dev/null
+++ b/tool/ruby_vm/views/_attributes.erb
@@ -0,0 +1,35 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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
+%#
+#ifndef RUBY_VM_EXEC_H
+/* can't #include "vm_exec.h" here... */
+typedef long OFFSET;
+typedef unsigned long lindex_t;
+typedef VALUE GENTRY;
+typedef rb_iseq_t *ISEQ;
+#endif
+
+% attrs = RubyVM::Instructions.map(&:attributes).flatten
+%
+% attrs.each do |a|
+PUREFUNC(MAYBE_UNUSED(static <%= a.declaration %>));
+% end
+%
+% attrs.each do |a|
+
+/* <%= a.pretty_name %> */
+<%= a.definition %>
+{
+% str = render_c_expr a.expr
+% case str when /\A#/ then
+ return
+<%= str -%>
+% else
+ return <%= str -%>
+% end
+}
+% end
diff --git a/tool/ruby_vm/views/_c_expr.erb b/tool/ruby_vm/views/_c_expr.erb
new file mode 100644
index 0000000000..cebe4d7a5d
--- /dev/null
+++ b/tool/ruby_vm/views/_c_expr.erb
@@ -0,0 +1,17 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+%;
+% if expr.blank?
+% # empty
+% elsif ! expr.__LINE__
+<%= expr.expr %>
+% else
+#line <%= expr.__LINE__ %> <%=cstr expr.__FILE__ %>
+<%= expr.expr %>
+#pragma RubyVM reset source
+% end
diff --git a/tool/ruby_vm/views/_copyright.erb b/tool/ruby_vm/views/_copyright.erb
new file mode 100644
index 0000000000..a449b0b735
--- /dev/null
+++ b/tool/ruby_vm/views/_copyright.erb
@@ -0,0 +1,31 @@
+%# -*- mode: c; style: ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%;
+%# 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.
+%;
+%;
+%# Below is the licensing term for the generated output, not this erb file.
+/* This is an auto-generated file and is a part of the programming language
+ * Ruby. The person who created a program to generate this file (``I''
+ * hereafter) would like to refrain from defining licensing of this generated
+ * source code.
+ *
+ * This file consist of many small parts of codes copyrighted by each authors,
+ * not only the ``I'' person. Those original authors agree with some
+ * open-source license. I believe that the license we agree is the condition
+ * mentioned in the file COPYING. It states "4. You may modify and include
+ * the part of the software into any other software ...". But the problem is,
+ * the license never makes it clear if such modified parts still remain in the
+ * same license, or not. The fact that we agree with the source code's
+ * licensing terms do not automatically define that of generated ones. This is
+ * the reason why this file is under unclear situation. All that I know is
+ * that above provision guarantees this file to exist.
+ *
+ * Please let me hesitate to declare something about this nuanced contract. I
+ * am not in the position to take over other authors' license to merge into my
+ * one. Changing them to (say) GPLv3 is not doable by myself. Perhaps someday
+ * it might turn out to be okay to say this file is under a license. I wish the
+ * situation would become more clear in the future. */
diff --git a/tool/ruby_vm/views/_insn_entry.erb b/tool/ruby_vm/views/_insn_entry.erb
new file mode 100644
index 0000000000..05299d1006
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_entry.erb
@@ -0,0 +1,60 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+%;
+
+/* insn <%= insn.pretty_name %> */
+INSN_ENTRY(<%= insn.name %>)
+{
+%# NAME_OF_CURRENT_INSN is used in vm_exec.h
+# define NAME_OF_CURRENT_INSN <%= insn.name %>
+# define INSN_ATTR(x) <%= insn.call_attribute(' ## x ## ') %>
+ bool leaf;
+ MAYBE_UNUSED(VALUE *) canary;
+% unless insn.declarations.empty?
+ <%= insn.declarations.join(";\n ") %>;
+
+% end
+ START_OF_ORIGINAL_INSN(<%= insn.name %>);
+% insn.preamble.each do |konst|
+<%= render_c_expr konst -%>
+% end
+% insn.opes.each_with_index do |ope, i|
+ <%= ope[:name] %> = (<%= ope[:type] %>)GET_OPERAND(<%= i + 1 %>);
+% end
+%
+% insn.pops.reverse_each.with_index.reverse_each do |pop, i|
+ <%= pop[:name] %> = <%= insn.cast_from_VALUE pop, "TOPN(#{i})"%>;
+% end
+ DEBUG_ENTER_INSN(INSN_ATTR(name));
+ if (! (leaf = INSN_ATTR(leaf))) ADD_PC(INSN_ATTR(width));
+% if insn.handles_sp?
+ POPN(INSN_ATTR(popn));
+% end
+<%= insn.handle_canary "SETUP_CANARY()" -%>
+ COLLECT_USAGE_INSN(INSN_ATTR(bin));
+% insn.opes.each_with_index do |ope, i|
+ COLLECT_USAGE_OPERAND(INSN_ATTR(bin), <%= i %>, <%= ope[:name] %>);
+% end
+<%= render_c_expr insn.expr -%>
+ CHECK_VM_STACK_OVERFLOW_FOR_INSN(VM_REG_CFP, INSN_ATTR(retn));
+<%= insn.handle_canary "CHECK_CANARY()" -%>
+% if insn.handles_sp?
+% insn.rets.reverse_each do |ret|
+ PUSH(<%= insn.cast_to_VALUE ret %>);
+% end
+% else
+ INC_SP(INSN_ATTR(sp_inc));
+% insn.rets.reverse_each.with_index do |ret, i|
+ TOPN(<%= i %>) = <%= insn.cast_to_VALUE ret %>;
+% end
+% end
+ if (leaf) ADD_PC(INSN_ATTR(width));
+ END_INSN(<%= insn.name %>);
+# undef INSN_ATTR
+# undef NAME_OF_CURRENT_INSN
+}
diff --git a/tool/ruby_vm/views/_insn_len_info.erb b/tool/ruby_vm/views/_insn_len_info.erb
new file mode 100644
index 0000000000..abbdb2e92c
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_len_info.erb
@@ -0,0 +1,23 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+CONSTFUNC(MAYBE_UNUSED(static int insn_len(VALUE insn)));
+extern const char rb_vm_insn_len_info[];
+
+#ifdef RUBY_VM_INSNS_INFO
+const char rb_vm_insn_len_info[] = {
+% RubyVM::Instructions.each_slice 25 do |a|
+ <%= a.map(&:width).join(', ') -%>,
+% end
+};
+#endif
+
+int
+insn_len(VALUE i)
+{
+ return rb_vm_insn_len_info[i];
+}
diff --git a/tool/ruby_vm/views/_insn_name_info.erb b/tool/ruby_vm/views/_insn_name_info.erb
new file mode 100644
index 0000000000..f690a7b0c7
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_name_info.erb
@@ -0,0 +1,50 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+%
+% a = RubyVM::Instructions.map {|i| i.name }
+% b = (0...a.size)
+% c = a.inject([0]) {|r, i| r << (r[-1] + i.length + 1) }
+% c.pop
+%
+CONSTFUNC(MAYBE_UNUSED(static const char *insn_name(VALUE insn)));
+extern const char *rb_vm_insn_name_info;
+MJIT_SYMBOL_EXPORT_BEGIN
+extern const unsigned short rb_vm_insn_name_offset[];
+MJIT_SYMBOL_EXPORT_END
+
+#ifdef RUBY_VM_INSNS_INFO
+const unsigned short rb_vm_insn_name_offset[] = {
+% c.each_slice 12 do |d|
+ <%= d.map {|i| sprintf("%4d", i) }.join(', ') %>,
+% end
+};
+const int rb_vm_max_insn_name_size = <%= RubyVM::Instructions.map {|i| i.name.size }.max %>;
+ASSERT_VM_INSTRUCTION_SIZE(rb_vm_insn_name_offset);
+
+PACKED_STRUCT(struct rb_vm_insn_name_info_tag {
+% b.each_slice 3 do |d|
+ <%= d.map {|i|
+ sprintf("const char L%03d[%2d]", i, a[i].length + 1)
+ }.join('; ') %>;
+% end
+});
+
+static const struct rb_vm_insn_name_info_tag rb_vm_insn_name_base = {
+% a.each_slice 2 do |d|
+ <%= d.map {|i| sprintf("%-30s", cstr(i)) }.join(', ') %>,
+% end
+};
+
+const char *rb_vm_insn_name_info = (const char *)&rb_vm_insn_name_base;
+#endif
+
+const char *
+insn_name(VALUE i)
+{
+ return &rb_vm_insn_name_info[rb_vm_insn_name_offset[i]];
+}
diff --git a/tool/ruby_vm/views/_insn_operand_info.erb b/tool/ruby_vm/views/_insn_operand_info.erb
new file mode 100644
index 0000000000..f6b6565d59
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_operand_info.erb
@@ -0,0 +1,59 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+%
+% a = RubyVM::Instructions.map {|i| i.operands_info }
+% b = (0...a.size)
+% c = a.inject([0]) {|r, i| r << (r[-1] + i.length + 1) }
+% c.pop
+%
+CONSTFUNC(MAYBE_UNUSED(static const char *insn_op_types(VALUE insn)));
+CONSTFUNC(MAYBE_UNUSED(static int insn_op_type(VALUE insn, long pos)));
+extern const char *rb_vm_insn_op_info;
+extern const unsigned short rb_vm_insn_op_offset[];
+
+#ifdef RUBY_VM_INSNS_INFO
+const unsigned short rb_vm_insn_op_offset[] = {
+% c.each_slice 14 do |d|
+ <%= d.map {|i| sprintf("%3d", i) }.join(', ') %>,
+% end
+};
+ASSERT_VM_INSTRUCTION_SIZE(rb_vm_insn_op_offset);
+
+PACKED_STRUCT(struct rb_vm_insn_op_info_tag {
+% b.each_slice 3 do |d|
+ <%= d.map {|i|
+ sprintf("const char L%03d[%2d]", i, a[i].length + 1)
+ }.join('; ') %>;
+% end
+});
+
+static const struct rb_vm_insn_op_info_tag rb_vm_insn_op_base = {
+% a.each_slice 8 do |d|
+ <%= d.map {|i| sprintf("%-6s", cstr(i)) }.join(', ') %>,
+% end
+};
+
+const char *rb_vm_insn_op_info = (const char *)&rb_vm_insn_op_base;
+#endif
+
+const char *
+insn_op_types(VALUE i)
+{
+ return &rb_vm_insn_op_info[rb_vm_insn_op_offset[i]];
+}
+
+int
+insn_op_type(VALUE i, long j)
+{
+ if (j >= insn_len(i)) {
+ return 0;
+ }
+ else {
+ return insn_op_types(i)[j];
+ }
+}
diff --git a/tool/ruby_vm/views/_insn_stack_increase.erb b/tool/ruby_vm/views/_insn_stack_increase.erb
new file mode 100644
index 0000000000..84f5dbc935
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_stack_increase.erb
@@ -0,0 +1,52 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+%#
+PUREFUNC(MAYBE_UNUSED(static int insn_stack_increase(int depth, int insn, const VALUE *opes)));
+PUREFUNC(static rb_snum_t insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes));
+
+rb_snum_t
+insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes)
+{
+ static const signed char t[] = {
+% RubyVM::Instructions.each_slice 8 do |a|
+ <%= a.map { |i|
+ if i.has_attribute?('sp_inc')
+ '-127'
+ else
+ sprintf("%4d", i.rets.size - i.pops.size)
+ end
+ }.join(', ') -%>,
+% end
+ };
+ signed char c = t[insn];
+
+ ASSERT_VM_INSTRUCTION_SIZE(t);
+ if (c != -127) {
+ return c;
+ }
+ else switch(insn) {
+ default:
+ UNREACHABLE;
+% RubyVM::Instructions.each do |i|
+% next unless i.has_attribute?('sp_inc')
+ case <%= i.bin %>:
+ return attr_sp_inc_<%= i.name %>(<%=
+ i.opes.map.with_index do |v, j|
+ i.cast_from_VALUE v, "opes[#{j}]"
+ end.join(", ")
+ %>);
+% end
+ }
+}
+
+int
+insn_stack_increase(int depth, int insn, const VALUE *opes)
+{
+ enum ruby_vminsn_type itype = (enum ruby_vminsn_type)insn;
+ return depth + (int)insn_stack_increase_dispatch(itype, opes);
+}
diff --git a/tool/ruby_vm/views/_insn_type_chars.erb b/tool/ruby_vm/views/_insn_type_chars.erb
new file mode 100644
index 0000000000..b3eff5624f
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_type_chars.erb
@@ -0,0 +1,12 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+%
+% map = RubyVM::Typemap.each_pair.map {|k, (c, t)| sprintf "%s = '%s'", t, c }
+enum ruby_insn_type_chars {
+ <%= map.join(",\n ") %>
+};
diff --git a/tool/ruby_vm/views/_leaf_helpers.erb b/tool/ruby_vm/views/_leaf_helpers.erb
new file mode 100644
index 0000000000..618fbd78db
--- /dev/null
+++ b/tool/ruby_vm/views/_leaf_helpers.erb
@@ -0,0 +1,105 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# Copyright (c) 2018 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+%;
+
+static bool
+leafness_of_getglobal(VALUE gentry)
+{
+ const struct rb_global_entry *e = (void *)gentry;
+
+ if (UNLIKELY(rb_gvar_is_traced(e))) {
+ return false;
+ }
+ else {
+ /* We cannot write this function using a switch() because a
+ * case label cannot be a function pointer. */
+ static rb_gvar_getter_t *const allowlist[] = {
+ rb_gvar_val_getter,
+ rb_gvar_var_getter,
+ rb_gvar_undef_getter,
+ };
+ rb_gvar_getter_t *f = rb_gvar_getter_function_of(e);
+ int i;
+
+ for (i = 0; i < numberof(allowlist); i++) {
+ if (f == allowlist[i]) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+static bool
+leafness_of_setglobal(VALUE gentry)
+{
+ const struct rb_global_entry *e = (void *)gentry;
+
+ if (UNLIKELY(rb_gvar_is_traced(e))) {
+ return false;
+ }
+ else {
+ /* We cannot write this function using a switch() because a
+ * case label cannot be a function pointer. */
+ static rb_gvar_setter_t *const allowlist[] = {
+ rb_gvar_val_setter,
+ rb_gvar_readonly_setter,
+ rb_gvar_var_setter,
+ rb_gvar_undef_setter,
+ };
+ rb_gvar_setter_t *f = rb_gvar_setter_function_of(e);
+ int i;
+
+ for (i = 0; i < numberof(allowlist); i++) {
+ if (f == allowlist[i]) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+#include "iseq.h"
+
+static bool
+leafness_of_defined(rb_num_t op_type)
+{
+ /* see also: vm_insnhelper.c:vm_defined() */
+ switch (op_type) {
+ case DEFINED_IVAR:
+ case DEFINED_IVAR2:
+ case DEFINED_GVAR:
+ case DEFINED_CVAR:
+ case DEFINED_YIELD:
+ case DEFINED_REF:
+ case DEFINED_ZSUPER:
+ return false;
+ case DEFINED_CONST:
+ /* has rb_autoload_load(); */
+ return false;
+ case DEFINED_FUNC:
+ case DEFINED_METHOD:
+ /* calls #respond_to_missing? */
+ return false;
+ default:
+ rb_bug("unknown operand %ld: blame @shyouhei.", op_type);
+ }
+}
+
+static bool
+leafness_of_checkmatch(rb_num_t flag)
+{
+ /* see also: vm_insnhelper.c:check_match() */
+ if (flag == VM_CHECKMATCH_TYPE_WHEN) {
+ return true;
+ }
+ else {
+ /* has rb_funcallv() */
+ return false;
+ }
+}
diff --git a/tool/ruby_vm/views/_mjit_compile_insn.erb b/tool/ruby_vm/views/_mjit_compile_insn.erb
new file mode 100644
index 0000000000..d1b06bb398
--- /dev/null
+++ b/tool/ruby_vm/views/_mjit_compile_insn.erb
@@ -0,0 +1,91 @@
+% # -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+% # Copyright (c) 2018 Takashi Kokubun. All rights reserved.
+% #
+% # 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.
+ fprintf(f, "{\n");
+ {
+ MAYBE_UNUSED(int pc_moved_p) = FALSE;
+% # compiler: Prepare operands which may be used by `insn.call_attribute`
+% insn.opes.each_with_index do |ope, i|
+ MAYBE_UNUSED(<%= ope.fetch(:decl) %>) = (<%= ope.fetch(:type) %>)operands[<%= i %>];
+% end
+%
+% # JIT: Declare stack_size to be used in some macro of _mjit_compile_insn_body.erb
+ if (status->local_stack_p) {
+ fprintf(f, " MAYBE_UNUSED(unsigned int) stack_size = %u;\n", b->stack_size);
+ }
+%
+% # JIT: Declare variables for operands, popped values and return values
+% insn.declarations.each do |decl|
+ fprintf(f, " <%= decl %>;\n");
+% end
+
+% # JIT: Set const expressions for `RubyVM::OperandsUnifications` insn
+% insn.preamble.each do |amble|
+ fprintf(f, "<%= amble.expr %>\n");
+% end
+%
+% # JIT: Initialize operands
+% insn.opes.each_with_index do |ope, i|
+ fprintf(f, " <%= ope.fetch(:name) %> = (<%= ope.fetch(:type) %>)0x%"PRIxVALUE";", operands[<%= i %>]);
+% case ope.fetch(:type)
+% when 'ID'
+ comment_id(f, (ID)operands[<%= i %>]);
+% when 'CALL_INFO'
+ comment_id(f, ((CALL_INFO)operands[<%= i %>])->mid);
+% when 'VALUE'
+ if (SYMBOL_P((VALUE)operands[<%= i %>])) comment_id(f, SYM2ID((VALUE)operands[<%= i %>]));
+% end
+ fprintf(f, "\n");
+% end
+%
+% # JIT: Initialize popped values
+% insn.pops.reverse_each.with_index.reverse_each do |pop, i|
+ fprintf(f, " <%= pop.fetch(:name) %> = stack[%d];\n", b->stack_size - <%= i + 1 %>);
+% end
+%
+% # JIT: move sp and pc if necessary
+<%= render 'mjit_compile_pc_and_sp', locals: { insn: insn } -%>
+%
+% # JIT: Print insn body in insns.def
+<%= render 'mjit_compile_insn_body', locals: { insn: insn } -%>
+%
+% # JIT: Set return values
+% insn.rets.reverse_each.with_index do |ret, i|
+% # TOPN(n) = ...
+ fprintf(f, " stack[%d] = <%= ret.fetch(:name) %>;\n", b->stack_size + (int)<%= insn.call_attribute('sp_inc') %> - <%= i + 1 %>);
+% end
+%
+% # JIT: We should evaluate ISeq modified for TracePoint if it's enabled. Note: This is slow.
+% unless insn.always_leaf?
+ fprintf(f, " if (UNLIKELY(ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS)) {\n");
+ fprintf(f, " reg_cfp->sp = (VALUE *)reg_cfp->bp + %d;\n", b->stack_size + (int)<%= insn.call_attribute('sp_inc') %> + 1);
+ if (!pc_moved_p) {
+ fprintf(f, " reg_cfp->pc = original_body_iseq + %d;\n", next_pos);
+ }
+ fprintf(f, " goto cancel;\n");
+ fprintf(f, " }\n");
+% end
+%
+% # compiler: Move JIT compiler's internal stack pointer
+ b->stack_size += <%= insn.call_attribute('sp_inc') %>;
+ }
+ fprintf(f, "}\n");
+%
+% # compiler: If insn has conditional JUMP, the code should go to the branch not targeted by JUMP next.
+% if insn.expr.expr =~ /if\s+\([^{}]+\)\s+\{[^{}]+JUMP\([^)]+\);[^{}]+\}/
+ if (ALREADY_COMPILED_P(status, pos + insn_len(insn))) {
+ fprintf(f, "goto label_%d;\n", pos + insn_len(insn));
+ }
+ else {
+ compile_insns(f, body, b->stack_size, pos + insn_len(insn), status);
+ }
+% 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.expr =~ /\sTHROW_EXCEPTION\([^)]+\);/ || insn.expr.expr =~ /\sRESTORE_REGS\(\);/
+ b->finish_p = TRUE;
+% end
diff --git a/tool/ruby_vm/views/_mjit_compile_insn_body.erb b/tool/ruby_vm/views/_mjit_compile_insn_body.erb
new file mode 100644
index 0000000000..3b14c272b1
--- /dev/null
+++ b/tool/ruby_vm/views/_mjit_compile_insn_body.erb
@@ -0,0 +1,105 @@
+% # -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+% # Copyright (c) 2018 Takashi Kokubun. All rights reserved.
+% #
+% # 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.
+%
+% to_cstr = lambda do |line|
+% normalized = line.gsub(/\t/, ' ' * 8)
+% indented = normalized.sub(/\A(?!#)/, ' ') # avoid indenting preprocessor
+% rstring2cstr(indented.rstrip).sub(/"\z/, '\\n"')
+% end
+%
+% #
+% # Expand simple macro, which doesn't require dynamic C code.
+% #
+% expand_simple_macros = lambda do |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]
+% <<-RESTORE_REGS.gsub(/^ +/, '')
+% #if OPT_CALL_THREADED_CODE
+% #{indent}rb_ec_thread_ptr(ec)->retval = val;
+% #{indent}return 0;
+% #else
+% #{indent}return val;
+% #endif
+% RESTORE_REGS
+% end
+% end
+% end
+%
+% #
+% # Print a body of insn, but with macro expansion.
+% #
+% expand_simple_macros.call(insn.expr.expr).each_line do |line|
+% #
+% # Expand dynamic macro here (only JUMP for now)
+% #
+% # TODO: support combination of following macros in the same line
+% case line
+% 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
+ {
+ struct case_dispatch_var arg;
+ arg.f = f;
+ arg.base_pos = pos + insn_len(insn);
+ arg.last_value = Qundef;
+
+ fprintf(f, " switch (<%= dest %>) {\n");
+ st_foreach(RHASH_TBL_RAW(hash), compile_case_dispatch_each, (VALUE)&arg);
+ fprintf(f, " case %lu:\n", else_offset);
+ fprintf(f, " goto label_%lu;\n", arg.base_pos + else_offset);
+ fprintf(f, " }\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) = ...
+ fprintf(f, " stack[%d] = <%= ret.fetch(:name) %>;\n", b->stack_size + (int)<%= insn.call_attribute('sp_inc') %> - <%= i + 1 %>);
+% end
+%
+ next_pos = pos + insn_len(insn) + (unsigned int)<%= dest %>;
+ fprintf(f, " goto label_%d;\n", next_pos);
+% end
+% when /\A\s+CALL_SIMPLE_METHOD\(\);\s+\z/
+% # For `opt_xxx`'s fallbacks.
+ if (status->local_stack_p) {
+ fprintf(f, " reg_cfp->sp = (VALUE *)reg_cfp->bp + %d;\n", b->stack_size + 1);
+ }
+ fprintf(f, " reg_cfp->pc = original_body_iseq + %d;\n", pos);
+ fprintf(f, " goto cancel;\n");
+% 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.
+ fprintf(f, <%= to_cstr.call(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
+ fprintf(f, <%= to_cstr.call(line.sub(/\bGET_SP\(\)/, '%s')) %>, (status->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]
+ fprintf(f, <%= to_cstr.call(line.sub(/\bSTACK_ADDR_FROM_TOP\(([^)]+)\)/, '%s')) %>,
+ (status->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]
+ fprintf(f, <%= to_cstr.call(line.sub(/\bTOPN\(([^)]+)\)/, '%s')) %>,
+ (status->local_stack_p ? "*(stack + (stack_size - (<%= num %>) - 1))" : "TOPN(<%= num %>)"));
+% else
+ fprintf(f, <%= to_cstr.call(line) %>);
+% end
+% end
+% end
+% end
diff --git a/tool/ruby_vm/views/_mjit_compile_ivar.erb b/tool/ruby_vm/views/_mjit_compile_ivar.erb
new file mode 100644
index 0000000000..f3dd1351c1
--- /dev/null
+++ b/tool/ruby_vm/views/_mjit_compile_ivar.erb
@@ -0,0 +1,54 @@
+% # -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+% # Copyright (c) 2018 Takashi Kokubun. All rights reserved.
+% #
+% # 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.
+%
+% # Optimized case of get_instancevariable instruction.
+#if OPT_IC_FOR_IVAR
+{
+% # compiler: Prepare operands which may be used by `insn.call_attribute`
+% insn.opes.each_with_index do |ope, i|
+ MAYBE_UNUSED(<%= ope.fetch(:decl) %>) = (<%= ope.fetch(:type) %>)operands[<%= i %>];
+% end
+% # compiler: Use copied IC to avoid race condition
+ IC ic_copy = &(status->is_entries + ((union iseq_inline_storage_entry *)ic - body->is_entries))->cache;
+%
+% # compiler: Consider cfp->self as T_OBJECT if ic_copy->ic_serial is set
+ if (ic_copy->ic_serial) {
+% # 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`.
+% # <%= render 'mjit_compile_pc_and_sp', locals: { insn: insn } -%>
+%
+% # JIT: prepare vm_getivar's arguments and variables
+ fprintf(f, "{\n");
+ fprintf(f, " VALUE obj = GET_SELF();\n");
+ fprintf(f, " const rb_serial_t ic_serial = (rb_serial_t)%"PRI_SERIALT_PREFIX"u;\n", ic_copy->ic_serial);
+ fprintf(f, " const st_index_t index = %"PRIuSIZE";\n", ic_copy->ic_value.index);
+% # JIT: cache hit path of vm_getivar, or cancel JIT.
+% if insn.name == 'setinstancevariable'
+ fprintf(f, " VALUE val = stack[%d];\n", b->stack_size - 1);
+ fprintf(f, " if (LIKELY(RB_TYPE_P(obj, T_OBJECT) && ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass) && index < ROBJECT_NUMIV(obj) && !RB_OBJ_FROZEN(obj))) {\n");
+ fprintf(f, " VALUE *ptr = ROBJECT_IVPTR(obj);\n");
+ fprintf(f, " RB_OBJ_WRITE(obj, &ptr[index], val);\n");
+ fprintf(f, " }\n");
+% else
+ fprintf(f, " VALUE val;\n");
+ fprintf(f, " if (LIKELY(RB_TYPE_P(obj, T_OBJECT) && ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass) && index < ROBJECT_NUMIV(obj) && (val = ROBJECT_IVPTR(obj)[index]) != Qundef)) {\n");
+ fprintf(f, " stack[%d] = val;\n", b->stack_size);
+ fprintf(f, " }\n");
+% end
+ fprintf(f, " else {\n");
+ fprintf(f, " reg_cfp->pc = original_body_iseq + %d;\n", pos);
+ fprintf(f, " reg_cfp->sp = (VALUE *)reg_cfp->bp + %d;\n", b->stack_size + 1);
+ fprintf(f, " goto cancel;\n");
+ fprintf(f, " }\n");
+
+% # compiler: Move JIT compiler's internal stack pointer
+ b->stack_size += <%= insn.call_attribute('sp_inc') %>;
+ fprintf(f, "}\n");
+ break;
+ }
+}
+#endif /* OPT_IC_FOR_IVAR */
diff --git a/tool/ruby_vm/views/_mjit_compile_pc_and_sp.erb b/tool/ruby_vm/views/_mjit_compile_pc_and_sp.erb
new file mode 100644
index 0000000000..eff728f551
--- /dev/null
+++ b/tool/ruby_vm/views/_mjit_compile_pc_and_sp.erb
@@ -0,0 +1,36 @@
+% # Copyright (c) 2018 Takashi Kokubun. All rights reserved.
+% #
+% # 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.
+%
+% # JIT: Move pc for catch table on catch_except_p, and for #caller_locations and rb_profile_frames on !insn.always_leaf?
+ if (body->catch_except_p || <%= insn.always_leaf? ? 'FALSE' : 'TRUE' %>) {
+ fprintf(f, " reg_cfp->pc = original_body_iseq + %d;\n", next_pos); /* ADD_PC(INSN_ATTR(width)); */
+ pc_moved_p = TRUE;
+ }
+%
+% # JIT: move sp to use or preserve stack variables
+ if (status->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):
+ {
+ rb_snum_t i, push_size;
+ push_size = -<%= insn.call_attribute('sp_inc') %> + <%= insn.rets.size %> - <%= insn.pops.size %>;
+ fprintf(f, " reg_cfp->sp = (VALUE *)reg_cfp->bp + %ld + 1;\n", push_size); /* POPN(INSN_ATTR(popn)); */
+ for (i = 0; i < push_size; i++) {
+ fprintf(f, " *(reg_cfp->sp + %ld) = stack[%ld];\n", i - push_size, (rb_snum_t)b->stack_size - push_size + i);
+ }
+ }
+% end
+ }
+ else {
+% if insn.handles_sp?
+ fprintf(f, " reg_cfp->sp = (VALUE *)reg_cfp->bp + %d;\n", b->stack_size + 1 - <%= insn.pops.size %>); /* POPN(INSN_ATTR(popn)); */
+% else
+ fprintf(f, " reg_cfp->sp = (VALUE *)reg_cfp->bp + %d;\n", b->stack_size + 1);
+% end
+ }
diff --git a/tool/ruby_vm/views/_mjit_compile_send.erb b/tool/ruby_vm/views/_mjit_compile_send.erb
new file mode 100644
index 0000000000..40d803af7a
--- /dev/null
+++ b/tool/ruby_vm/views/_mjit_compile_send.erb
@@ -0,0 +1,94 @@
+% # -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+% # Copyright (c) 2018 Takashi Kokubun. All rights reserved.
+% #
+% # 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.
+%
+% # Optimized case of send / opt_send_without_block instructions.
+{
+ MAYBE_UNUSED(int pc_moved_p) = FALSE;
+% # compiler: Prepare operands which may be used by `insn.call_attribute`
+% insn.opes.each_with_index do |ope, i|
+ MAYBE_UNUSED(<%= ope.fetch(:decl) %>) = (<%= ope.fetch(:type) %>)operands[<%= i %>];
+% end
+% # compiler: Use copied cc to avoid race condition
+ CALL_CACHE cc_copy = status->cc_entries + (cc - body->cc_entries);
+%
+ if (has_valid_method_type(cc_copy)) {
+ const rb_iseq_t *iseq;
+ unsigned int argc = ci->orig_argc; /* unlike `ci->orig_argc`, `argc` may include blockarg */
+% if insn.name == 'send'
+ argc += ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0);
+% end
+
+ if (!(ci->flag & VM_CALL_TAILCALL) && /* inlining non-tailcall path */
+ cc_copy->me->def->type == VM_METHOD_TYPE_ISEQ && inlinable_iseq_p(ci, cc_copy, iseq = def_iseq_ptr(cc_copy->me->def)) /* CC_SET_FASTPATH in vm_callee_setup_arg */) {
+ int param_size = iseq->body->param.size; /* TODO: check calling->argc for argument_arity_error */
+
+ fprintf(f, "{\n");
+% # JIT: Declare stack_size to be used in some macro of _mjit_compile_insn_body.erb
+ if (status->local_stack_p) {
+ fprintf(f, " MAYBE_UNUSED(unsigned int) stack_size = %u;\n", b->stack_size);
+ }
+
+% # JIT: Invalidate call cache if it requires vm_search_method. This allows to inline some of following things.
+ fprintf(f, " if (UNLIKELY(GET_GLOBAL_METHOD_STATE() != %"PRI_SERIALT_PREFIX"u ||\n", cc_copy->method_state);
+ fprintf(f, " RCLASS_SERIAL(CLASS_OF(stack[%d])) != %"PRI_SERIALT_PREFIX"u)) {\n", b->stack_size - 1 - argc, cc_copy->class_serial);
+ fprintf(f, " reg_cfp->pc = original_body_iseq + %d;\n", pos);
+ fprintf(f, " reg_cfp->sp = (VALUE *)reg_cfp->bp + %d;\n", b->stack_size + 1);
+ fprintf(f, " goto cancel;\n");
+ fprintf(f, " }\n");
+
+% # JIT: move sp and pc if necessary
+<%= render 'mjit_compile_pc_and_sp', locals: { insn: insn } -%>
+
+% # JIT: Print insn body in insns.def
+ fprintf(f, " {\n");
+ fprintf(f, " struct rb_calling_info calling;\n");
+% if insn.name == 'send'
+ fprintf(f, " calling.block_handler = vm_caller_setup_arg_block(ec, reg_cfp, (CALL_INFO)0x%"PRIxVALUE", (rb_iseq_t *)0x%"PRIxVALUE", FALSE);\n", operands[0], operands[2]);
+% else
+ fprintf(f, " calling.block_handler = VM_BLOCK_HANDLER_NONE;\n");
+% end
+ fprintf(f, " calling.argc = %d;\n", ci->orig_argc);
+ fprintf(f, " calling.recv = stack[%d];\n", b->stack_size - 1 - argc);
+
+% # JIT: Special CALL_METHOD. Bypass cc_copy->call and inline vm_call_iseq_setup_normal for vm_call_iseq_setup_func FASTPATH.
+ fprintf(f, " {\n");
+ fprintf(f, " VALUE v;\n");
+ fprintf(f, " vm_call_iseq_setup_normal(ec, reg_cfp, &calling, (const rb_callable_method_entry_t *)0x%"PRIxVALUE", 0, %d, %d);\n",
+ (VALUE)cc_copy->me, param_size, iseq->body->local_table_size); /* rb_simple_iseq_p checks rb_simple_iseq_p, which ensures has_opt == FALSE */
+ if (iseq->body->catch_except_p) {
+ fprintf(f, " VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);\n");
+ fprintf(f, " v = vm_exec(ec, TRUE);\n");
+ }
+ else {
+ fprintf(f, " if ((v = mjit_exec(ec)) == Qundef) {\n");
+ fprintf(f, " VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);\n"); /* This is vm_call0_body's code after vm_call_iseq_setup */
+ fprintf(f, " v = vm_exec(ec, FALSE);\n");
+ fprintf(f, " }\n");
+ }
+ fprintf(f, " stack[%d] = v;\n", b->stack_size - argc - 1);
+ fprintf(f, " }\n");
+
+ fprintf(f, " }\n");
+
+% # JIT: We should evaluate ISeq modified for TracePoint if it's enabled. Note: This is slow.
+ fprintf(f, " if (UNLIKELY(ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS)) {\n");
+ fprintf(f, " reg_cfp->sp = (VALUE *)reg_cfp->bp + %d;\n", b->stack_size + (int)<%= insn.call_attribute('sp_inc') %> + 1);
+ if (!pc_moved_p) {
+ fprintf(f, " reg_cfp->pc = original_body_iseq + %d;\n", next_pos);
+ }
+ fprintf(f, " goto cancel;\n");
+ fprintf(f, " }\n");
+
+% # compiler: Move JIT compiler's internal stack pointer
+ b->stack_size += <%= insn.call_attribute('sp_inc') %>;
+
+ fprintf(f, "}\n");
+ break;
+ }
+ }
+}
diff --git a/tool/ruby_vm/views/_notice.erb b/tool/ruby_vm/views/_notice.erb
new file mode 100644
index 0000000000..8c6cdaf533
--- /dev/null
+++ b/tool/ruby_vm/views/_notice.erb
@@ -0,0 +1,22 @@
+%# -*- mode: c; style: ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%;
+%# 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.
+%;
+%;
+/*******************************************************************/
+/*******************************************************************/
+/*******************************************************************/
+/**
+ This file <%= this_file %>.
+
+ ----
+ This file is auto generated by insns2vm.rb
+ DO NOT TOUCH!
+
+ If you want to fix something, you must edit <%= cstr edit %>
+ or tool/insns2vm.rb
+ */
diff --git a/tool/ruby_vm/views/_trace_instruction.erb b/tool/ruby_vm/views/_trace_instruction.erb
new file mode 100644
index 0000000000..30933a6f5a
--- /dev/null
+++ b/tool/ruby_vm/views/_trace_instruction.erb
@@ -0,0 +1,16 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+%;
+
+/* insn <%= insn.pretty_name %> */
+INSN_ENTRY(<%= insn.name %>)
+{
+ vm_trace(ec, GET_CFP(), GET_PC());
+ DISPATCH_ORIGINAL_INSN(<%= insn.jump_destination %>);
+ END_INSN(<%= insn.name %>);
+}
diff --git a/tool/ruby_vm/views/insns.inc.erb b/tool/ruby_vm/views/insns.inc.erb
new file mode 100644
index 0000000000..78dddd69d1
--- /dev/null
+++ b/tool/ruby_vm/views/insns.inc.erb
@@ -0,0 +1,26 @@
+/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
+
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+<%= render 'copyright' %>
+<%= render 'notice', locals: {
+ this_file: 'contains YARV instruction list',
+ edit: __FILE__,
+} -%>
+
+/* BIN : Basic Instruction Name */
+#define BIN(n) YARVINSN_##n
+
+enum ruby_vminsn_type {
+% RubyVM::Instructions.each do |i|
+ <%= i.bin %>,
+% end
+ VM_INSTRUCTION_SIZE
+};
+
+#define ASSERT_VM_INSTRUCTION_SIZE(array) \
+ STATIC_ASSERT(numberof_##array, numberof(array) == VM_INSTRUCTION_SIZE)
diff --git a/tool/ruby_vm/views/insns_info.inc.erb b/tool/ruby_vm/views/insns_info.inc.erb
new file mode 100644
index 0000000000..67fcebfc42
--- /dev/null
+++ b/tool/ruby_vm/views/insns_info.inc.erb
@@ -0,0 +1,20 @@
+/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
+
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+<%= render 'copyright' %>
+<%= render 'notice', locals: {
+ this_file: 'contains instruction information for yarv instruction sequence.',
+ edit: __FILE__,
+} %>
+<%= render 'insn_type_chars' %>
+<%= render 'insn_name_info' %>
+<%= render 'insn_len_info' %>
+<%= render 'insn_operand_info' %>
+<%= render 'leaf_helpers' %>
+<%= render 'attributes' %>
+<%= render 'insn_stack_increase' %>
diff --git a/tool/ruby_vm/views/mjit_compile.inc.erb b/tool/ruby_vm/views/mjit_compile.inc.erb
new file mode 100644
index 0000000000..b2ba3ca240
--- /dev/null
+++ b/tool/ruby_vm/views/mjit_compile.inc.erb
@@ -0,0 +1,77 @@
+/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
+
+% # Copyright (c) 2018 Takashi Kokubun. All rights reserved.
+% #
+% # 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.
+<%= render 'copyright' %>
+%
+% # This is an ERB template that generates Ruby code that generates C code that
+% # generates JIT-ed C code.
+<%= render 'notice', locals: {
+ this_file: 'is the main part of compile_insn() in mjit_compile.c',
+ edit: __FILE__,
+} -%>
+%
+% unsupported_insns = [
+% 'getblockparamproxy', # TODO: support this
+% 'defineclass', # low priority
+% 'opt_call_c_function', # low priority
+% ]
+%
+% opt_send_without_block = RubyVM::Instructions.find { |i| i.name == 'opt_send_without_block' }
+% if opt_send_without_block.nil?
+% raise 'opt_send_without_block not found'
+% end
+%
+% # 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 `reg_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`
+
+switch (insn) {
+% (RubyVM::BareInstructions.to_a + RubyVM::OperandsUnifications.to_a).each do |insn|
+% next if unsupported_insns.include?(insn.name)
+ case BIN(<%= insn.name %>):
+% # Instruction-specific behavior in JIT
+% case insn.name
+% when 'opt_send_without_block', 'send'
+<%= render 'mjit_compile_send', locals: { insn: insn } -%>
+% when 'opt_aref' # experimental. TODO: increase insns and make the list automatically by finding DISPATCH_ORIGINAL_INSN
+<%= render 'mjit_compile_send', locals: { insn: opt_send_without_block } -%>
+% when 'getinstancevariable', 'setinstancevariable'
+<%= render 'mjit_compile_ivar', locals: { insn: insn } -%>
+% when 'leave'
+ if (b->stack_size != 1) {
+ if (mjit_opts.warnings || mjit_opts.verbose)
+ fprintf(stderr, "MJIT warning: Unexpected JIT stack_size on leave: %d\n", b->stack_size);
+ status->success = FALSE;
+ }
+% end
+%
+% # Main insn implementation generated by insns.def
+<%= render 'mjit_compile_insn', locals: { insn: insn } -%>
+ break;
+% end
+%
+% # We don't support InstructionsUnifications yet because it's not used for now.
+% # We don't support TraceInstructions yet. There is no blocker for it but it's just not implemented.
+ default:
+ if (mjit_opts.warnings || mjit_opts.verbose)
+ fprintf(stderr, "MJIT warning: Skipped to compile unsupported instruction: %s\n", insn_name(insn));
+ status->success = FALSE;
+ break;
+}
diff --git a/tool/ruby_vm/views/opt_sc.inc.erb b/tool/ruby_vm/views/opt_sc.inc.erb
new file mode 100644
index 0000000000..fdc9ee3d08
--- /dev/null
+++ b/tool/ruby_vm/views/opt_sc.inc.erb
@@ -0,0 +1,40 @@
+/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
+
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+% raise ':FIXME:TBW' if RubyVM::VmOptsH['STACK_CACHING']
+<%= render 'copyright' %>
+<%= render 'notice', locals: {
+ this_file: 'is for threaded code',
+ edit: __FILE__,
+} -%>
+
+#define SC_STATE_SIZE 6
+
+#define SCS_XX 1
+#define SCS_AX 2
+#define SCS_BX 3
+#define SCS_AB 4
+#define SCS_BA 5
+
+#define SC_ERROR 0xffffffff
+
+static const VALUE sc_insn_info[][SC_STATE_SIZE] = {
+#define NO_SC { SC_ERROR, SC_ERROR, SC_ERROR, SC_ERROR, SC_ERROR, SC_ERROR }
+% RubyVM::Instructions.each_slice 8 do |a|
+ <%= a.map{|i| 'NO_SC' }.join(', ') %>,
+% end
+#undef NO_SC
+};
+
+static const VALUE sc_insn_next[] = {
+% RubyVM::Instructions.each_slice 8 do |a|
+ <%= a.map{|i| 'SCS_XX' }.join(', ') %>,
+% end
+};
+
+ASSERT_VM_INSTRUCTION_SIZE(sc_insn_next);
diff --git a/tool/ruby_vm/views/optinsn.inc.erb b/tool/ruby_vm/views/optinsn.inc.erb
new file mode 100644
index 0000000000..1cb09d4809
--- /dev/null
+++ b/tool/ruby_vm/views/optinsn.inc.erb
@@ -0,0 +1,71 @@
+/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
+
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+<%= render 'copyright' -%>
+<%= render 'notice', locals: {
+ this_file: 'is for threaded code',
+ edit: __FILE__,
+} -%>
+
+static INSN *
+insn_operands_unification(INSN *iobj)
+{
+#ifdef OPT_OPERANDS_UNIFICATION
+ VALUE *op = iobj->operands;
+
+ switch (iobj->insn_id) {
+ default:
+ /* do nothing */;
+ break;
+
+% RubyVM::OperandsUnifications.each_group do |orig, unifs|
+ case <%= orig.bin %>:
+% unifs.each do |insn|
+
+ /* <%= insn.pretty_name %> */
+ if ( <%= insn.condition('op') %> ) {
+% insn.opes.each_with_index do |o, x|
+% n = insn.operand_shift_of(o)
+% if n != 0 then
+ op[<%= x %>] = op[<%= x + n %>];
+% end
+% end
+ iobj->insn_id = <%= insn.bin %>;
+ iobj->operand_size = <%= insn.opes.size %>;
+ break;
+ }
+% end
+
+ break;
+% end
+ }
+#endif
+ return iobj;
+}
+
+int
+rb_insn_unified_local_var_level(VALUE insn)
+{
+#ifdef OPT_OPERANDS_UNIFICATION
+ /* optimize rule */
+ switch (insn) {
+ default:
+ return -1; /* do nothing */;
+% RubyVM::OperandsUnifications.each_group do |orig, unifs|
+% unifs.each do|insn|
+ case <%= insn.bin %>:
+% insn.spec.map{|(var,val)|val}.reject{|i| i == '*' }.each do |val|
+ return <%= val %>;
+% break
+% end
+% end
+% end
+ }
+#endif
+ return -1;
+}
diff --git a/tool/ruby_vm/views/optunifs.inc.erb b/tool/ruby_vm/views/optunifs.inc.erb
new file mode 100644
index 0000000000..29d8ca2855
--- /dev/null
+++ b/tool/ruby_vm/views/optunifs.inc.erb
@@ -0,0 +1,21 @@
+/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
+
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+% raise ':FIXME:TBW' if RubyVM::VmOptsH['INSTRUCTIONS_UNIFICATION']
+% n = RubyVM::Instructions.size
+<%= render 'copyright' %>
+<%= render 'notice', locals: {
+ this_file: 'is for threaded code',
+ edit: __FILE__,
+} -%>
+
+/* Let .bss section automatically initialize this variable */
+/* cf. Section 6.7.8 of ISO/IEC 9899:1999 */
+static const int *const *const unified_insns_data[<%= n %>];
+
+ASSERT_VM_INSTRUCTION_SIZE(unified_insns_data);
diff --git a/tool/ruby_vm/views/vm.inc.erb b/tool/ruby_vm/views/vm.inc.erb
new file mode 100644
index 0000000000..24181fab95
--- /dev/null
+++ b/tool/ruby_vm/views/vm.inc.erb
@@ -0,0 +1,30 @@
+/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
+
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+<%= render 'copyright' %>
+<%= render 'notice', locals: {
+ this_file: 'is VM main loop',
+ edit: __FILE__,
+} -%>
+
+#include "vm_insnhelper.h"
+% RubyVM::BareInstructions.to_a.each do |insn|
+<%= render 'insn_entry', locals: { insn: insn } -%>
+% end
+%
+% RubyVM::OperandsUnifications.to_a.each do |insn|
+<%= render 'insn_entry', locals: { insn: insn } -%>
+% end
+%
+% RubyVM::InstructionsUnifications.to_a.each do |insn|
+<%= render 'insn_entry', locals: { insn: insn } -%>
+% end
+%
+% RubyVM::TraceInstructions.to_a.each do |insn|
+<%= render 'trace_instruction', locals: { insn: insn } -%>
+% end
diff --git a/tool/ruby_vm/views/vmtc.inc.erb b/tool/ruby_vm/views/vmtc.inc.erb
new file mode 100644
index 0000000000..16886a1ea6
--- /dev/null
+++ b/tool/ruby_vm/views/vmtc.inc.erb
@@ -0,0 +1,21 @@
+/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
+
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# 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.
+<%= render 'copyright' -%>
+<%= render 'notice', locals: {
+ this_file: 'is for threaded code',
+ edit: __FILE__,
+} -%>
+
+static const void *const insns_address_table[] = {
+% RubyVM::Instructions.each do |i|
+ LABEL_PTR(<%= i.name %>),
+% end
+};
+
+ASSERT_VM_INSTRUCTION_SIZE(insns_address_table);
diff --git a/tool/run-gcov.rb b/tool/run-gcov.rb
new file mode 100644
index 0000000000..5df7622aa3
--- /dev/null
+++ b/tool/run-gcov.rb
@@ -0,0 +1,54 @@
+#!ruby
+require "pathname"
+require "open3"
+
+Pathname.glob("**/*.gcda").sort.each do |gcda|
+ if gcda.fnmatch("ext/*")
+ cwd, gcda = gcda.split.map {|s| s.to_s }
+ objdir = "."
+ elsif gcda.fnmatch("rubyspec_temp/*")
+ next
+ else
+ cwd, objdir, gcda = ".", gcda.dirname.to_s, gcda.to_s
+ end
+ puts "$ gcov -lpbc -o #{ objdir } #{ gcda }"
+ out, err, _status = Open3.capture3("gcov", "-lpbc", "-o", objdir, gcda, chdir: cwd)
+ puts out
+ puts err
+
+ # a black list of source files that contains wrong #line directives
+ if err !~ %r(
+ \A(
+ Cannot\ open\ source\ file\ (
+ defs/keywords
+ |zonetab\.list
+ |enc/jis/props\.kwd
+ |parser\.c
+ |parser\.rl
+ )\n
+ )*\z
+ )x
+ raise "Unexpected gcov output"
+ end
+
+ if out !~ %r(
+ \A(
+ File\ .*\nLines\ executed:.*\n
+ (
+ Branches\ executed:.*\n
+ Taken\ at\ least\ once:.*\n
+ |
+ No\ branches\n
+ )?
+ (
+ Calls\ executed:.*\n
+ |
+ No\ calls\n
+ )?
+ Creating\ .*\n
+ \n
+ )+\z
+ )x
+ raise "Unexpected gcov output"
+ end
+end
diff --git a/tool/run-lcov.rb b/tool/run-lcov.rb
new file mode 100644
index 0000000000..f27578200a
--- /dev/null
+++ b/tool/run-lcov.rb
@@ -0,0 +1,164 @@
+#!ruby
+require "pathname"
+require "open3"
+require "tmpdir"
+
+def backup_gcda_files(gcda_files)
+ gcda_files = gcda_files.map do |gcda|
+ [gcda, gcda.sub_ext(".bak")]
+ end
+ begin
+ gcda_files.each do |before, after|
+ before.rename(after)
+ end
+ yield
+ ensure
+ gcda_files.each do |before, after|
+ after.rename(before)
+ end
+ end
+end
+
+def run_lcov(*args)
+ system("lcov", "--rc", "lcov_branch_coverage=1", *args)
+end
+
+$info_files = []
+def run_lcov_capture(dir, info)
+ $info_files << info
+ run_lcov("--capture", "-d", dir, "-o", info)
+end
+
+def run_lcov_merge(files, info)
+ run_lcov(*files.flat_map {|f| ["--add-tracefile", f] }, "-o", info)
+end
+
+def run_lcov_remove(info_src, info_out)
+ dirs = %w(/usr/*)
+ dirs << File.join(Dir.tmpdir, "*")
+ %w(
+ test/*
+ ext/-test-/*
+ ext/nkf/nkf-utf8/nkf.c
+ ).each {|f| dirs << File.join(File.dirname(__dir__), f) }
+ run_lcov("--remove", info_src, *dirs, "-o", info_out)
+end
+
+def run_genhtml(info, out)
+ system("genhtml", "--branch-coverage", "--ignore-errors", "source", info, "-o", out)
+end
+
+def gen_rb_lcov(file)
+ res = Marshal.load(File.binread(file))
+
+ open("lcov-rb-all.info", "w") do |f|
+ f.puts "TN:" # no test name
+ base_dir = File.dirname(__dir__)
+ res.each do |path, cov|
+ next unless path.start_with?(base_dir)
+ next if path.start_with?(File.join(base_dir, "test"))
+ f.puts "SF:#{ path }"
+
+ total = covered = 0
+ cov.each_with_index do |count, lineno|
+ next unless count
+ f.puts "DA:#{ lineno + 1 },#{ count }"
+ total += 1
+ covered += 1 if count > 0
+ end
+ f.puts "LF:#{ total }"
+ f.puts "LH:#{ covered }"
+
+ f.puts "end_of_record"
+ end
+ end
+end
+
+def gen_rb_lcov(file)
+ res = Marshal.load(File.binread(file))
+
+ open("lcov-rb-all.info", "w") do |f|
+ f.puts "TN:" # no test name
+ base_dir = File.dirname(File.dirname(__dir__))
+ res.each do |path, cov|
+ next unless path.start_with?(base_dir)
+ next if path.start_with?(File.join(base_dir, "test"))
+ f.puts "SF:#{ path }"
+
+ # function coverage
+ total = covered = 0
+ cov[:methods].each do |(klass, name, lineno), count|
+ f.puts "FN:#{ lineno },#{ klass }##{ name }"
+ total += 1
+ covered += 1 if count > 0
+ end
+ f.puts "FNF:#{ total }"
+ f.puts "FNF:#{ covered }"
+ cov[:methods].each do |(klass, name, _), count|
+ f.puts "FNDA:#{ count },#{ klass }##{ name }"
+ end
+
+ # line coverage
+ total = covered = 0
+ cov[:lines].each_with_index do |count, lineno|
+ next unless count
+ f.puts "DA:#{ lineno + 1 },#{ count }"
+ total += 1
+ covered += 1 if count > 0
+ end
+ f.puts "LF:#{ total }"
+ f.puts "LH:#{ covered }"
+
+ # branch coverage
+ total = covered = 0
+ id = 0
+ cov[:branches].each do |(_base_type, _, base_lineno), targets|
+ i = 0
+ targets.each do |(_target_type, _target_lineno), count|
+ f.puts "BRDA:#{ base_lineno },#{ id },#{ i },#{ count }"
+ total += 1
+ covered += 1 if count > 0
+ i += 1
+ end
+ id += 1
+ end
+ f.puts "BRF:#{ total }"
+ f.puts "BRH:#{ covered }"
+ f.puts "end_of_record"
+ end
+ end
+end
+
+gcda_files = Pathname.glob("**/*.gcda")
+ext_gcda_files = gcda_files.select {|f| f.fnmatch("ext/*") }
+rubyspec_temp_gcda_files = gcda_files.select {|f| f.fnmatch("rubyspec_temp/*") }
+
+backup_gcda_files(rubyspec_temp_gcda_files) do
+ if ext_gcda_files != []
+ backup_gcda_files(ext_gcda_files) do
+ info = "lcov-root.info"
+ run_lcov_capture(".", info)
+ end
+ end
+ ext_gcda_files.group_by {|f| f.descend.to_a[1] }.each do |key, files|
+ info = "lcov-#{ key.to_s.gsub(File::Separator, "-") }.info"
+ run_lcov_capture(key.to_s, info)
+ end
+end
+if $info_files != []
+ run_lcov_merge($info_files, "lcov-c-all.info")
+ run_lcov_remove("lcov-c-all.info", "lcov-c-all-filtered.info")
+ run_genhtml("lcov-c-all-filtered.info", "lcov-c-out")
+end
+
+if File.readable?("test-coverage.dat")
+ gen_rb_lcov("test-coverage.dat")
+ run_lcov_remove("lcov-rb-all.info", "lcov-rb-all-filtered.info")
+ run_genhtml("lcov-rb-all-filtered.info", "lcov-rb-out")
+end
+
+if File.readable?("lcov-c-all.info") && File.readable?("lcov-rb-all.info")
+ run_lcov_merge(%w(lcov-c-all.info lcov-rb-all.info), "lcov-all.info")
+ run_lcov_remove("lcov-all.info", "lcov-all-filtered.info")
+ run_genhtml("lcov-all-filtered.info", "lcov-out")
+end
diff --git a/tool/runruby.rb b/tool/runruby.rb
index 9329ef72b6..d240914f3c 100755
--- a/tool/runruby.rb
+++ b/tool/runruby.rb
@@ -5,8 +5,15 @@
show = false
precommand = []
+srcdir = File.realpath('..', File.dirname(__FILE__))
while arg = ARGV[0]
break ARGV.shift if arg == '--'
+ case arg
+ when '-C', /\A-C(.+)/m
+ ARGV.shift
+ Dir.chdir($1 || ARGV.shift)
+ next
+ end
/\A--([-\w]+)(?:=(.*))?\z/ =~ arg or break
arg, value = $1, $2
re = Regexp.new('\A'+arg.gsub(/\w+\b/, '\&\\w*')+'\z', "i")
@@ -23,12 +30,22 @@ while arg = ARGV[0]
# obsolete switch do nothing
when re =~ "debugger"
require 'shellwords'
- precommand.concat(value ? (Shellwords.shellwords(value) unless value == "no") : %w"gdb --args")
+ case value
+ when nil
+ debugger = :gdb
+ when "lldb"
+ debugger = :lldb
+ when "no"
+ else
+ debugger = Shellwords.shellwords(value)
+ end and precommand |= [:debugger]
when re =~ "precommand"
require 'shellwords'
precommand.concat(Shellwords.shellwords(value))
when re =~ "show"
show = true
+ when re =~ "chdir"
+ Dir.chdir(value)
else
break
end
@@ -37,19 +54,28 @@ end
unless defined?(File.realpath)
def File.realpath(*args)
- Dir.chdir(expand_path(*args)) do
- Dir.pwd
+ path = expand_path(*args)
+ if File.stat(path).directory?
+ Dir.chdir(path) {Dir.pwd}
+ else
+ dir, base = File.split(path)
+ File.join(Dir.chdir(dir) {Dir.pwd}, base)
end
end
end
-srcdir ||= File.realpath('..', File.dirname(__FILE__))
-archdir ||= '.'
+begin
+ conffile = File.realpath('rbconfig.rb', archdir)
+rescue Errno::ENOENT => e
+ # retry if !archdir and ARGV[0] and File.directory?(archdir = ARGV.shift)
+ abort "#$0: rbconfig.rb not found, use --archdir option"
+end
-abs_archdir = File.expand_path(archdir)
+abs_archdir = File.dirname(conffile)
+archdir ||= abs_archdir
$:.unshift(abs_archdir)
-config = File.read(conffile = File.join(abs_archdir, 'rbconfig.rb'))
+config = File.read(conffile)
config.sub!(/^(\s*)RUBY_VERSION\b.*(\sor\s*)\n.*\n/, '')
config = Module.new {module_eval(config, conffile)}::RbConfig::CONFIG
@@ -67,12 +93,21 @@ end
libs << File.expand_path("lib", srcdir)
config["bindir"] = abs_archdir
-env = {}
-
-runner = File.join(abs_archdir, "ruby-runner#{config['EXEEXT']}")
-runner = File.expand_path(ruby) unless File.exist?(runner)
-env["RUBY"] = runner
-env["PATH"] = [abs_archdir, ENV["PATH"]].compact.join(File::PATH_SEPARATOR)
+env = {
+ # Test with the smallest possible machine stack sizes.
+ # These values are clamped to machine-dependent minimum values in vm_core.h
+ 'RUBY_THREAD_MACHINE_STACK_SIZE' => '1',
+ 'RUBY_FIBER_MACHINE_STACK_SIZE' => '1',
+}
+
+runner = File.join(abs_archdir, "exe/ruby#{config['EXEEXT']}")
+runner = nil unless File.exist?(runner)
+abs_ruby = runner || File.expand_path(ruby)
+env["RUBY"] = abs_ruby
+env["GEM_PATH"] = env["GEM_HOME"] = File.expand_path(".bundle", srcdir)
+env["BUNDLE_RUBY"] = abs_ruby
+env["BUNDLE_GEM"] = "#{abs_ruby} -rrubygems #{srcdir}/bin/gem --backtrace"
+env["PATH"] = [File.dirname(abs_ruby), abs_archdir, ENV["PATH"]].compact.join(File::PATH_SEPARATOR)
if e = ENV["RUBYLIB"]
libs |= e.split(File::PATH_SEPARATOR)
@@ -84,21 +119,42 @@ if File.file?(libruby_so)
if e = config['LIBPATHENV'] and !e.empty?
env[e] = [abs_archdir, ENV[e]].compact.join(File::PATH_SEPARATOR)
end
- if e = config['PRELOADENV']
- e = nil if e.empty?
- e ||= "LD_PRELOAD" if /linux/ =~ RUBY_PLATFORM
- end
- if e
- env[e] = [libruby_so, ENV[e]].compact.join(File::PATH_SEPARATOR)
+ unless runner
+ if e = config['PRELOADENV']
+ e = nil if e.empty?
+ e ||= "LD_PRELOAD" if /linux/ =~ RUBY_PLATFORM
+ end
+ if e
+ env[e] = [libruby_so, ENV[e]].compact.join(File::PATH_SEPARATOR)
+ end
end
end
ENV.update env
-cmd = [ruby]
+if debugger or ENV['RUNRUBY_USE_GDB'] == 'true'
+ if debugger == :gdb or !debugger
+ debugger = %w'gdb'
+ if File.exist?(gdb = 'run.gdb') or
+ File.exist?(gdb = File.join(abs_archdir, 'run.gdb'))
+ debugger.push('-x', gdb)
+ end
+ debugger << '--args'
+ end
+ if debugger == :lldb
+ debugger = %w'lldb --'
+ end
+
+ if idx = precommand.index(:debugger)
+ precommand[idx, 1] = debugger
+ else
+ precommand.concat(debugger)
+ end
+end
+
+cmd = [runner || ruby]
cmd.concat(ARGV)
cmd.unshift(*precommand) unless precommand.empty?
-cmd.push(:close_others => false)
if show
require 'shellwords'
@@ -106,4 +162,4 @@ if show
puts Shellwords.join(cmd)
end
-exec(*cmd)
+exec(*cmd, close_others: false)
diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb
new file mode 100644
index 0000000000..bb4fcdd2cc
--- /dev/null
+++ b/tool/sync_default_gems.rb
@@ -0,0 +1,258 @@
+# sync following repositories to ruby repository
+#
+# * https://github.com/rubygems/rubygems
+# * https://github.com/bundler/bundler
+# * https://github.com/ruby/rdoc
+# * https://github.com/flori/json
+# * https://github.com/ruby/psych
+# * https://github.com/ruby/fileutils
+# * https://github.com/ruby/fiddle
+# * https://github.com/ruby/stringio
+# * https://github.com/ruby/io-console
+# * https://github.com/ruby/csv
+# * https://github.com/ruby/webrick
+# * https://github.com/ruby/dbm
+# * https://github.com/ruby/gdbm
+# * https://github.com/ruby/sdbm
+# * https://github.com/ruby/etc
+# * https://github.com/ruby/date
+# * https://github.com/ruby/zlib
+# * https://github.com/ruby/fcntl
+# * https://github.com/ruby/scanf
+# * https://github.com/ruby/cmath
+# * https://github.com/ruby/strscan
+# * https://github.com/ruby/ipaddr
+# * https://github.com/ruby/logger
+# * https://github.com/ruby/prime
+# * https://github.com/ruby/matrix
+# * https://github.com/ruby/ostruct
+# * https://github.com/ruby/rexml
+# * https://github.com/ruby/rss
+# * https://github.com/ruby/irb
+# * https://github.com/ruby/sync
+# * https://github.com/ruby/tracer
+# * https://github.com/ruby/shell
+# * https://github.com/ruby/forwardable
+# * https://github.com/ruby/thwait
+# * https://github.com/ruby/e2mmap
+# * https://github.com/ruby/mutex_m
+#
+
+$repositories = {
+ rubygems: 'rubygems/rubygems',
+ bundler: 'bundler/bundler',
+ rdoc: 'ruby/rdoc',
+ json: 'flori/json',
+ psych: 'ruby/psych',
+ fileutils: 'ruby/fileutils',
+ fiddle: 'ruby/fiddle',
+ stringio: 'ruby/stringio',
+ ioconsole: 'ruby/io-console',
+ csv: 'ruby/csv',
+ webrick: 'ruby/webrick',
+ dbm: 'ruby/dbm',
+ gdbm: 'ruby/gdbm',
+ sdbm: 'ruby/sdbm',
+ etc: 'ruby/etc',
+ date: 'ruby/date',
+ zlib: 'ruby/zlib',
+ fcntl: 'ruby/fcntl',
+ scanf: 'ruby/scanf',
+ cmath: 'ruby/cmath',
+ strscan: 'ruby/strscan',
+ ipaddr: 'ruby/ipaddr',
+ logger: 'ruby/logger',
+ prime: 'ruby/prime',
+ matrix: 'ruby/matrix',
+ ostruct: 'ruby/ostruct',
+ rexml: 'ruby/rexml',
+ rss: 'ruby/rss',
+ irb: 'ruby/irb',
+ sync: 'ruby/sync',
+ tracer: 'ruby/tracer',
+ shell: 'ruby/shell',
+ forwardable: "ruby/forwardable",
+ thwait: "ruby/thwait",
+ e2mmap: "ruby/e2mmap",
+ mutex_m: "ruby/mutex_m"
+}
+
+def sync_default_gems(gem)
+ puts "Sync #{$repositories[gem.to_sym]}"
+
+ case gem
+ when "rubygems"
+ `rm -rf lib/rubygems* test/rubygems`
+ `cp -r ../../rubygems/rubygems/lib/rubygems* ./lib`
+ `cp -r ../../rubygems/rubygems/test/rubygems ./test`
+ when "bundler"
+ `rm -rf lib/bundler* libexec/bundler libexec/bundle libexec/bundle_ruby spec/bundler man/bundle* man/gemfile*`
+ `cp -r ../../bundler/bundler/lib/bundler* ./lib`
+ `cp -r ../../bundler/bundler/exe/bundle* ./libexec`
+ `cp ../../bundler/bundler/bundler.gemspec ./lib`
+ `cp -r ../../bundler/bundler/spec spec/bundler`
+ `cp -r ../../bundler/bundler/man/*.{1,5,1\.txt,5\.txt,ronn} ./man`
+ `rm -rf spec/bundler/support/artifice/vcr_cassettes`
+ when "rdoc"
+ `rm -rf lib/rdoc* test/rdoc libexec/rdoc libexec/ri`
+ `cp -rf ../rdoc/lib/rdoc* ./lib`
+ `cp -rf ../rdoc/test test/rdoc`
+ `cp ../rdoc/rdoc.gemspec ./lib/rdoc`
+ `cp -rf ../rdoc/exe/rdoc ./libexec`
+ `cp -rf ../rdoc/exe/ri ./libexec`
+ `rm -f lib/rdoc/markdown.kpeg lib/rdoc/markdown/literals.kpeg lib/rdoc/rd/block_parser.ry lib/rdoc/rd/inline_parser.ry`
+ `git checkout lib/rdoc/.document`
+ when "json"
+ `rm -rf ext/json test/json`
+ `cp -rf ../../flori/json/ext/json/ext ext/json`
+ `cp -rf ../../flori/json/tests test/json`
+ `cp -rf ../../flori/json/lib ext/json`
+ `rm -rf ext/json/lib/json/pure*`
+ `cp ../../flori/json/json.gemspec ext/json`
+ `rm -r ext/json/lib/json/ext`
+ `git checkout ext/json/extconf.rb ext/json/parser/prereq.mk ext/json/generator/depend ext/json/parser/depend`
+ when "psych"
+ `rm -rf ext/psych test/psych`
+ `cp -rf ../psych/ext/psych ./ext`
+ `cp -rf ../psych/lib ./ext/psych`
+ `cp -rf ../psych/test/psych ./test`
+ `rm -rf ext/psych/lib/org ext/psych/lib/psych.jar ext/psych/lib/psych_jars.rb`
+ `rm -rf ext/psych/lib/psych.{bundle,so} ext/psych/lib/{2.0,2.1,2.2,2.3,2.4}`
+ `rm -f ext/psych/yaml/LICENSE`
+ `cp ../psych/psych.gemspec ext/psych/`
+ `git checkout ext/psych/depend`
+ when "fiddle"
+ `rm -rf ext/fiddle test/fiddle`
+ `cp -rf ../fiddle/ext/fiddle ext`
+ `cp -rf ../fiddle/lib ext/fiddle`
+ `cp -rf ../fiddle/test/fiddle test`
+ `cp -f ../fiddle/fiddle.gemspec ext/fiddle`
+ `git checkout ext/fiddle/depend`
+ `rm -rf ext/fiddle/lib/fiddle.{bundle,so}`
+ when "stringio"
+ `rm -rf ext/stringio test/stringio`
+ `cp -rf ../stringio/ext/stringio ext`
+ `cp -rf ../stringio/test/stringio test`
+ `cp -f ../stringio/stringio.gemspec ext/stringio`
+ `git checkout ext/stringio/depend ext/stringio/README.md`
+ when "ioconsole"
+ `rm -rf ext/io/console test/io/console`
+ `cp -rf ../io-console/ext/io/console ext/io`
+ `cp -rf ../io-console/test/io/console test/io`
+ `mkdir -p ext/io/console/lib`
+ `cp -rf ../io-console/lib/console ext/io/console/lib`
+ `cp -f ../io-console/io-console.gemspec ext/io/console`
+ `git checkout ext/io/console/depend`
+ when "dbm"
+ `rm -rf ext/dbm test/dbm`
+ `cp -rf ../dbm/ext/dbm ext`
+ `cp -rf ../dbm/test/dbm test`
+ `cp -f ../dbm/dbm.gemspec ext/dbm`
+ `git checkout ext/dbm/depend`
+ when "gdbm"
+ `rm -rf ext/gdbm test/gdbm`
+ `cp -rf ../gdbm/ext/gdbm ext`
+ `cp -rf ../gdbm/test/gdbm test`
+ `cp -f ../gdbm/gdbm.gemspec ext/gdbm`
+ `git checkout ext/gdbm/depend ext/gdbm/README`
+ when "sdbm"
+ `rm -rf ext/sdbm test/sdbm`
+ `cp -rf ../sdbm/ext/sdbm ext`
+ `cp -rf ../sdbm/test/sdbm test`
+ `cp -f ../sdbm/sdbm.gemspec ext/sdbm`
+ `git checkout ext/sdbm/depend`
+ when "etc"
+ `rm -rf ext/etc test/etc`
+ `cp -rf ../etc/ext/etc ext`
+ `cp -rf ../etc/test/etc test`
+ `cp -f ../etc/etc.gemspec ext/etc`
+ `git checkout ext/etc/depend`
+ when "date"
+ `rm -rf ext/date test/date`
+ `cp -rf ../date/ext/date ext`
+ `cp -rf ../date/lib ext/date`
+ `cp -rf ../date/test/date test`
+ `cp -f ../date/date.gemspec ext/date`
+ `git checkout ext/date/depend`
+ `rm -f ext/date/lib/date_core.bundle`
+ when "zlib"
+ `rm -rf ext/zlib test/zlib`
+ `cp -rf ../zlib/ext/zlib ext`
+ `cp -rf ../zlib/test/zlib test`
+ `cp -f ../zlib/zlib.gemspec ext/zlib`
+ `git checkout ext/zlib/depend`
+ when "fcntl"
+ `rm -rf ext/fcntl`
+ `cp -rf ../fcntl/ext/fcntl ext`
+ `cp -f ../fcntl/fcntl.gemspec ext/fcntl`
+ `git checkout ext/fcntl/depend`
+ when "thwait"
+ `rm -rf lib/thwait*`
+ `cp -rf ../thwait/lib/* lib`
+ `cp -rf ../thwait/thwait.gemspec lib/thwait`
+ when "e2mmap"
+ `rm -rf lib/e2mmap*`
+ `cp -rf ../e2mmap/lib/* lib`
+ `cp -rf ../e2mmap/e2mmap.gemspec lib`
+ when "strscan"
+ `rm -rf ext/strscan test/strscan`
+ `cp -rf ../strscan/ext/strscan ext`
+ `cp -rf ../strscan/test/strscan test`
+ `cp -f ../strscan/strscan.gemspec ext/strscan`
+ `rm -f ext/strscan/regenc.h ext/strscan/regint.h`
+ `git checkout ext/strscan/depend`
+ when "rexml", "rss", "matrix", "irb", "csv", "shell", "logger", "ostruct", "scanf", "webrick", "fileutils", "forwardable", "prime", "tracer", "ipaddr", "cmath", "mutex_m", "sync"
+ sync_lib gem
+ else
+ end
+end
+
+def sync_lib(repo)
+ `rm -rf lib/#{repo}.rb lib/#{repo}/* test/test_#{repo}.rb`
+ `cp -rf ../#{repo}/lib/* lib`
+ tests = if File.directory?("test/#{repo}")
+ "test/#{repo}"
+ else
+ "test/test_#{repo}.rb"
+ end
+ `cp -rf ../#{repo}/#{tests} test`
+ gemspec = if File.directory?("lib/#{repo}")
+ "lib/#{repo}/#{repo}.gemspec"
+ else
+ "lib/#{repo}.gemspec"
+ end
+ `cp -f ../#{repo}/#{repo}.gemspec #{gemspec}`
+end
+
+def update_default_gems(gem)
+ author, repository = $repositories[gem.to_sym].split('/')
+
+ unless File.exist?("../../#{author}/#{repository}")
+ `mkdir -p ../../#{author}`
+ `git clone git@github.com:#{author}/#{repository}.git ../../#{author}/#{repository}`
+ end
+
+ Dir.chdir("../../#{author}/#{repository}") do
+ unless `git remote`.match(/ruby\-core/)
+ `git remote add ruby-core git@github.com:ruby/ruby.git`
+ `git fetch ruby-core`
+ `git co ruby-core/trunk`
+ `git branch ruby-core`
+ end
+ `git co ruby-core`
+ `git fetch ruby-core trunk`
+ `git rebase ruby-core/trunk`
+ `git co master`
+ `git stash`
+ `git pull --rebase`
+ end
+end
+
+case ARGV[0]
+when "up"
+ $repositories.keys.each{|gem| update_default_gems(gem.to_s)}
+when "all"
+ $repositories.keys.each{|gem| sync_default_gems(gem.to_s)}
+else
+ sync_default_gems(ARGV[0])
+end
diff --git a/tool/test-coverage.rb b/tool/test-coverage.rb
new file mode 100644
index 0000000000..e0d7dd06e1
--- /dev/null
+++ b/tool/test-coverage.rb
@@ -0,0 +1,118 @@
+require "coverage"
+
+Coverage.start(lines: true, branches: true, methods: true)
+
+TEST_COVERAGE_DATA_FILE = "test-coverage.dat"
+
+def merge_coverage_data(res1, res2)
+ res1.each do |path, cov1|
+ cov2 = res2[path]
+ if cov2
+ cov1[:lines].each_with_index do |count1, i|
+ next unless count1
+ add_count(cov2[:lines], i, count1)
+ end
+ cov1[:branches].each do |base_key, targets1|
+ if cov2[:branches][base_key]
+ targets1.each do |target_key, count1|
+ add_count(cov2[:branches][base_key], target_key, count1)
+ end
+ else
+ cov2[:branches][base_key] = targets1
+ end
+ end
+ cov1[:methods].each do |key, count1|
+ add_count(cov2[:methods], key, count1)
+ end
+ else
+ res2[path] = cov1
+ end
+ end
+ res2
+end
+
+def add_count(h, key, count)
+ if h[key]
+ h[key] += count
+ else
+ h[key] = count
+ end
+end
+
+def save_coverage_data(res1)
+ res1.each do |_path, cov|
+ if cov[:methods]
+ h = {}
+ cov[:methods].each do |(klass, *key), count|
+ h[[klass.inspect, *key]] = count
+ end
+ cov[:methods].replace h
+ end
+ end
+ File.open(TEST_COVERAGE_DATA_FILE, File::RDWR | File::CREAT | File::BINARY) do |f|
+ f.flock(File::LOCK_EX)
+ s = f.read
+ res2 = s.size > 0 ? Marshal.load(s) : {}
+ res1 = merge_coverage_data(res1, res2)
+ f.rewind
+ f << Marshal.dump(res2)
+ f.flush
+ f.truncate(f.pos)
+ end
+end
+
+def invoke_simplecov_formatter
+ %w[doclie simplecov-html simplecov].each do |f|
+ $LOAD_PATH.unshift "#{__dir__}/../coverage/#{f}/lib"
+ end
+
+ require "simplecov"
+ res = Marshal.load(File.binread(TEST_COVERAGE_DATA_FILE))
+ simplecov_result = {}
+ base_dir = File.dirname(__dir__)
+ cur_dir = Dir.pwd
+
+ res.each do |path, cov|
+ next unless path.start_with?(base_dir) || path.start_with?(cur_dir)
+ next if path.start_with?(File.join(base_dir, "test"))
+ simplecov_result[path] = cov[:lines]
+ end
+
+ a, b = base_dir, cur_dir
+ until a == b
+ if a.size > b.size
+ a = File.dirname(a)
+ else
+ b = File.dirname(b)
+ end
+ end
+ root_dir = a
+
+ SimpleCov.configure do
+ root(root_dir)
+ coverage_dir(File.join(cur_dir, "coverage"))
+ end
+ res = SimpleCov::Result.new(simplecov_result)
+ res.command_name = "Ruby's `make test-all`"
+ SimpleCov::Formatter::HTMLFormatter.new.format(res)
+end
+
+pid = $$
+pwd = Dir.pwd
+
+at_exit do
+ exit_exc = $!
+
+ Dir.chdir(pwd) do
+ save_coverage_data(Coverage.result)
+ if pid == $$
+ begin
+ nil while Process.waitpid(-1)
+ rescue Errno::ECHILD
+ invoke_simplecov_formatter
+ end
+ end
+ end
+
+ raise exit_exc if exit_exc
+end
diff --git a/tool/transcode-tblgen.rb b/tool/transcode-tblgen.rb
index f43627b781..156b2de197 100644
--- a/tool/transcode-tblgen.rb
+++ b/tool/transcode-tblgen.rb
@@ -63,7 +63,7 @@ class ArrayCode
end
def insert_at_last(num, str)
- newnum = self.length + num
+ # newnum = self.length + num
@content << str
@len += num
end
@@ -145,7 +145,7 @@ class ActionMap
else
b = $1.to_i(16)
e = $2.to_i(16)
- b.upto(e) {|c| set[c] = true }
+ b.upto(e) {|_| set[_] = true }
end
}
i = nil
@@ -235,7 +235,7 @@ class ActionMap
all_rects = []
rects1.each {|rect|
- min, max, action = rect
+ _, _, action = rect
rect[2] = actions.length
actions << action
all_rects << rect
@@ -244,7 +244,7 @@ class ActionMap
boundary = actions.length
rects2.each {|rect|
- min, max, action = rect
+ _, _, action = rect
rect[2] = actions.length
actions << action
all_rects << rect
@@ -273,7 +273,7 @@ class ActionMap
singleton_rects = []
region_rects = []
rects.each {|rect|
- min, max, action = rect
+ min, max, = rect
if min == max
singleton_rects << rect
else
@@ -293,14 +293,14 @@ class ActionMap
if region_rects.empty? ? s_rect[0].length == prefix.length : region_rects[0][0].empty?
h = TMPHASH
while (s_rect = @singleton_rects.last) && s_rect[0].start_with?(prefix)
- min, max, action = @singleton_rects.pop
+ min, _, action = @singleton_rects.pop
raise ArgumentError, "ambiguous pattern: #{prefix}" if min.length != prefix.length
h[action] = true
end
- region_rects.each {|min, max, action|
+ for min, _, action in region_rects
raise ArgumentError, "ambiguous pattern: #{prefix}" if !min.empty?
h[action] = true
- }
+ end
tree = Action.new(block.call(prefix, h.keys))
h.clear
else
@@ -906,7 +906,7 @@ end
def transcode_generate_node(am, name_hint=nil)
STDERR.puts "converter for #{name_hint}" if VERBOSE_MODE
- name = am.gennode(TRANSCODE_GENERATED_BYTES_CODE, TRANSCODE_GENERATED_WORDS_CODE, name_hint)
+ am.gennode(TRANSCODE_GENERATED_BYTES_CODE, TRANSCODE_GENERATED_WORDS_CODE, name_hint)
''
end
@@ -1078,7 +1078,11 @@ if __FILE__ == $0
end
libs1 = $".dup
- erb = ERB.new(src, nil, '%')
+ if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
+ erb = ERB.new(src, trim_mode: '%')
+ else
+ erb = ERB.new(src, nil, '%')
+ end
erb.filename = arg
erb_result = erb.result(binding)
libs2 = $".dup
diff --git a/tool/transform_mjit_header.rb b/tool/transform_mjit_header.rb
new file mode 100644
index 0000000000..5f89446c81
--- /dev/null
+++ b/tool/transform_mjit_header.rb
@@ -0,0 +1,298 @@
+# Copyright (C) 2017 Vladimir Makarov, <vmakarov@redhat.com>
+# This is a script to transform functions to static inline.
+# Usage: transform_mjit_header.rb <c-compiler> <header file> <out>
+
+require 'fileutils'
+require 'tempfile'
+
+PROGRAM = File.basename($0, ".*")
+
+module MJITHeader
+ ATTR_VALUE_REGEXP = /[^()]|\([^()]*\)/
+ ATTR_REGEXP = /__attribute__\s*\(\(#{ATTR_VALUE_REGEXP}*\)\)/
+ # Example:
+ # VALUE foo(int bar)
+ # VALUE __attribute__ ((foo)) bar(int baz)
+ # __attribute__ ((foo)) VALUE bar(int baz)
+ FUNC_HEADER_REGEXP = /\A[^\[{(]*(\s*#{ATTR_REGEXP})*[^\[{(]*\((#{ATTR_REGEXP}|[^()])*\)(\s*#{ATTR_REGEXP})*\s*/
+ TARGET_NAME_REGEXP = /\A(rb|ruby|vm|insn|attr|Init)_/
+
+ # Predefined macros for compilers which are already supported by MJIT.
+ # We're going to support cl.exe too (WIP) but `cl.exe -E` can't produce macro.
+ SUPPORTED_CC_MACROS = [
+ '__GNUC__', # gcc
+ '__clang__', # clang
+ ]
+
+ # These macros are relied on this script's transformation
+ PREFIXED_MACROS = [
+ 'ALWAYS_INLINE',
+ 'inline',
+ ]
+
+ # For MinGW's ras.h. Those macros have its name in its definition and can't be preprocessed multiple times.
+ RECURSIVE_MACROS = %w[
+ RASCTRYINFO
+ RASIPADDR
+ ]
+
+ IGNORED_FUNCTIONS = [
+ 'rb_vm_search_method_slowpath', # This increases the time to compile when inlined. So we use it as external function.
+ 'rb_equal_opt', # Not used from VM and not compilable
+ ]
+
+ ALWAYS_INLINED_FUNCTIONS = [
+ 'vm_opt_plus',
+ 'vm_opt_minus',
+ 'vm_opt_mult',
+ 'vm_opt_div',
+ 'vm_opt_mod',
+ 'vm_opt_neq',
+ 'vm_opt_lt',
+ 'vm_opt_le',
+ 'vm_opt_gt',
+ 'vm_opt_ge',
+ 'vm_opt_ltlt',
+ 'vm_opt_and',
+ 'vm_opt_or',
+ 'vm_opt_aref',
+ 'vm_opt_aset',
+ 'vm_opt_aref_with',
+ 'vm_opt_aset_with',
+ 'vm_opt_not',
+ 'vm_getinstancevariable',
+ 'vm_setinstancevariable',
+ 'vm_setivar',
+ ]
+
+ # Return start..stop of last decl in CODE ending STOP
+ def self.find_decl(code, stop)
+ level = 0
+ i = stop
+ while i = code.rindex(/[;{}]/, i)
+ if level == 0 && stop != i && decl_found?($&, i)
+ return decl_start($&, i)..stop
+ end
+ case $&
+ when '}'
+ level += 1
+ when '{'
+ level -= 1
+ end
+ i -= 1
+ end
+ nil
+ end
+
+ def self.decl_found?(code, i)
+ i == 0 || code == ';' || code == '}'
+ end
+
+ def self.decl_start(code, i)
+ if i == 0 && code != ';' && code != '}'
+ 0
+ else
+ i + 1
+ end
+ end
+
+ # Given DECL return the name of it, nil if failed
+ def self.decl_name_of(decl)
+ ident_regex = /\w+/
+ decl = decl.gsub(/^#.+$/, '') # remove macros
+ reduced_decl = decl.gsub(ATTR_REGEXP, '') # remove attributes
+ su1_regex = /{[^{}]*}/
+ su2_regex = /{([^{}]|#{su1_regex})*}/
+ su3_regex = /{([^{}]|#{su2_regex})*}/ # 3 nested structs/unions is probably enough
+ reduced_decl.gsub!(su3_regex, '') # remove structs/unions in the header
+ id_seq_regex = /\s*(#{ident_regex}(\s+|\s*[*]+\s*))*/
+ # Process function header:
+ match = /\A#{id_seq_regex}(?<name>#{ident_regex})\s*\(/.match(reduced_decl)
+ return match[:name] if match
+ # Process non-function declaration:
+ reduced_decl.gsub!(/\s*=[^;]+(?=;)/, '') # remove initialization
+ match = /#{id_seq_regex}(?<name>#{ident_regex})/.match(reduced_decl);
+ return match[:name] if match
+ nil
+ end
+
+ # Return true if CC with CFLAGS compiles successfully the current code.
+ # Use STAGE in the message in case of a compilation failure
+ def self.check_code!(code, cc, cflags, stage)
+ with_code(code) do |path|
+ cmd = "#{cc} #{cflags} #{path}"
+ out = IO.popen(cmd, err: [:child, :out], &:read)
+ unless $?.success?
+ STDERR.puts "error in #{stage} header file:\n#{out}"
+ exit false
+ end
+ end
+ end
+
+ # Remove unpreprocessable macros
+ def self.remove_harmful_macros!(code)
+ code.gsub!(/^#define #{Regexp.union(RECURSIVE_MACROS)} .*$/, '')
+ end
+
+ # -dD outputs those macros, and it produces redefinition warnings or errors
+ # This assumes common.mk passes `-DMJIT_HEADER` first when it creates rb_mjit_header.h.
+ def self.remove_predefined_macros!(code)
+ code.sub!(/\A(#define [^\n]+|\n)*(#define MJIT_HEADER 1\n)/, '\2')
+ end
+
+ # Return [macro, others]. But others include PREFIXED_MACROS to be used in code.
+ def self.separate_macro_and_code(code)
+ code.lines.partition do |l|
+ l.start_with?('#') && PREFIXED_MACROS.all? { |m| !l.start_with?("#define #{m}") }
+ end.map! { |lines| lines.join('') }
+ end
+
+ def self.write(code, out)
+ # create with strict permission, then will install proper
+ # permission
+ FileUtils.mkdir_p(File.dirname(out), mode: 0700)
+ File.binwrite("#{out}.new", code, perm: 0600)
+ FileUtils.mv("#{out}.new", out)
+ end
+
+ # Note that this checks runruby. This conservatively covers platform names.
+ def self.windows?
+ RUBY_PLATFORM =~ /mswin|mingw|msys/
+ end
+
+ def self.cl_exe?(cc)
+ cc =~ /\Acl(\z| |\.exe)/
+ end
+
+ # If code has macro which only supported compilers predefine, return true.
+ def self.supported_header?(code)
+ SUPPORTED_CC_MACROS.any? { |macro| code =~ /^#\s*define\s+#{Regexp.escape(macro)}\b/ }
+ end
+
+ # This checks if syntax check outputs one of the following messages.
+ # "error: conflicting types for 'restrict'"
+ # "error: redefinition of parameter 'restrict'"
+ # If it's true, this script regards platform as AIX or Solaris and adds -std=c99 as workaround.
+ def self.conflicting_types?(code, cc, cflags)
+ with_code(code) do |path|
+ cmd = "#{cc} #{cflags} #{path}"
+ out = IO.popen(cmd, err: [:child, :out], &:read)
+ !$?.success? &&
+ (out.match?(/error: conflicting types for '[^']+'/) ||
+ out.match?(/error: redefinition of parameter '[^']+'/))
+ end
+ end
+
+ def self.with_code(code)
+ # for `system_header` pragma which can't be in the main file.
+ Tempfile.open(['', '.h'], mode: File::BINARY) do |f|
+ f.puts code
+ f.close
+ Tempfile.open(['', '.c'], mode: File::BINARY) do |c|
+ c.puts <<SRC
+#include "#{f.path}"
+SRC
+ c.close
+ return yield(c.path)
+ end
+ end
+ end
+ private_class_method :with_code
+end
+
+if ARGV.size != 3
+ abort "Usage: #{$0} <c-compiler> <header file> <out>"
+end
+
+cc = ARGV[0]
+code = File.binread(ARGV[1]) # Current version of the header file.
+outfile = ARGV[2]
+if MJITHeader.cl_exe?(cc)
+ cflags = '-DMJIT_HEADER -Zs'
+else
+ cflags = '-S -DMJIT_HEADER -fsyntax-only -Werror=implicit-function-declaration -Werror=implicit-int -Wfatal-errors'
+end
+
+if !MJITHeader.cl_exe?(cc) && !MJITHeader.supported_header?(code)
+ puts "This compiler (#{cc}) looks not supported for MJIT. Giving up to generate MJIT header."
+ MJITHeader.write("#error MJIT does not support '#{cc}' yet", outfile)
+ exit
+end
+
+MJITHeader.remove_predefined_macros!(code)
+
+if MJITHeader.windows? # transformation is broken with Windows headers for now
+ MJITHeader.remove_harmful_macros!(code)
+ MJITHeader.check_code!(code, cc, cflags, 'initial')
+ puts "\nSkipped transforming external functions to static on Windows."
+ MJITHeader.write(code, outfile)
+ exit
+end
+
+macro, code = MJITHeader.separate_macro_and_code(code) # note: this does not work on MinGW
+code = <<header + code
+#ifdef __GNUC__
+# pragma GCC system_header
+#endif
+header
+code_to_check = "#{code}#{macro}" # macro should not affect code again
+
+if MJITHeader.conflicting_types?(code_to_check, cc, cflags)
+ cflags = "#{cflags} -std=c99" # For AIX gcc
+end
+
+# Check initial file correctness in the manner of final output.
+MJITHeader.check_code!(code_to_check, cc, cflags, 'initial')
+puts "\nTransforming external functions to static:"
+
+stop_pos = -1
+extern_names = []
+
+# This loop changes function declarations to static inline.
+while (decl_range = MJITHeader.find_decl(code, stop_pos))
+ stop_pos = decl_range.begin - 1
+ decl = code[decl_range]
+ decl_name = MJITHeader.decl_name_of(decl)
+
+ if MJITHeader::IGNORED_FUNCTIONS.include?(decl_name) && /#{MJITHeader::FUNC_HEADER_REGEXP}{/.match(decl)
+ puts "#{PROGRAM}: changing definition of '#{decl_name}' to declaration"
+ code[decl_range] = decl.sub(/{.+}/m, ';')
+ elsif MJITHeader::ALWAYS_INLINED_FUNCTIONS.include?(decl_name) && match = /#{MJITHeader::FUNC_HEADER_REGEXP}{/.match(decl)
+ header = match[0].sub(/{\z/, '').strip
+ header = "static inline #{header.sub(/\A((static|inline) )+/, '')}"
+ decl[match.begin(0)...match.end(0)] = '{' # remove header
+ code[decl_range] = "\nALWAYS_INLINE(#{header});\n#{header} #{decl}"
+ elsif extern_names.include?(decl_name) && (decl =~ /#{MJITHeader::FUNC_HEADER_REGEXP};/)
+ decl.sub!(/(extern|static|inline) /, ' ')
+ unless decl_name =~ /\Aattr_\w+_\w+\z/ # skip too-many false-positive warnings in insns_info.inc.
+ puts "#{PROGRAM}: making declaration of '#{decl_name}' static inline"
+ end
+
+ code[decl_range] = "static inline #{decl}"
+ elsif (match = /#{MJITHeader::FUNC_HEADER_REGEXP}{/.match(decl)) && (header = match[0]) !~ /static/
+ unless decl_name.match(MJITHeader::TARGET_NAME_REGEXP)
+ puts "#{PROGRAM}: SKIPPED to transform #{decl_name}"
+ next
+ end
+
+ extern_names << decl_name
+ decl[match.begin(0)...match.end(0)] = ''
+
+ if decl =~ /\bstatic\b/
+ puts "warning: a static decl inside external definition of '#{decl_name}'"
+ end
+
+ header.sub!(/(extern|inline) /, ' ')
+ unless decl_name =~ /\Aattr_\w+_\w+\z/ # skip too-many false-positive warnings in insns_info.inc.
+ puts "#{PROGRAM}: making external definition of '#{decl_name}' static inline"
+ end
+ code[decl_range] = "static inline #{header}#{decl}"
+ end
+end
+
+code << macro
+
+# Check the final file correctness
+MJITHeader.check_code!(code, cc, cflags, 'final')
+
+MJITHeader.write(code, outfile)
diff --git a/tool/update-deps b/tool/update-deps
index c308f35195..2129bdba25 100755
--- a/tool/update-deps
+++ b/tool/update-deps
@@ -13,9 +13,9 @@
# 1. Compile ruby with -save-temps=obj option.
# Ex. ./configure debugflags='-save-temps=obj -g' && make all golf
# 2. Run tool/update-deps to show dependency problems.
-# Ex. ruby tool/update-deps
+# Ex. ./ruby tool/update-deps
# 3. Use --fix to fix makefiles.
-# Ex. ruby tool/update-deps --fix
+# Ex. ./ruby tool/update-deps --fix
#
# Other usages:
# * Fix makefiles using previously detected dependency problems
@@ -42,7 +42,7 @@ REV=48577
tar xf ruby-$VER-r$REV.tar.xz
cp -a ruby-$VER-r$REV tarball_source_dir_original
mv ruby-$VER-r$REV tarball_source_dir_after_build
-svn co -q -r$REV http://svn.ruby-lang.org/repos/ruby/trunk ruby
+svn co -q -r$REV https://svn.ruby-lang.org/repos/ruby/trunk ruby
(cd ruby; autoconf)
cp -a ruby repo_source_dir_original
mv ruby repo_source_dir_after_build
@@ -120,6 +120,7 @@ FILES_NEED_VPATH = %w[
known_errors.inc
lex.c
miniprelude.c
+ mjit_compile.inc
newline.c
node_name.inc
opt_sc.inc
@@ -175,7 +176,7 @@ def in_makefile(target, source)
target = target.to_s
source = source.to_s
case target
- when %r{\A[^/]*\z}
+ when %r{\A[^/]*\z}, %r{\Acoroutine/}
target2 = "#{target.sub(/\.o\z/, '.$(OBJEXT)')}"
case source
when *FILES_IN_SOURCE_DIRECTORY then source2 = "$(top_srcdir)/#{source}"
@@ -192,6 +193,7 @@ def in_makefile(target, source)
when %r{\Amissing/} then source2 = "{$(VPATH)}#{$'}"
when %r{\Accan/} then source2 = "$(CCAN_DIR)/#{$'}"
when %r{\Adefs/} then source2 = "{$(VPATH)}#{source}"
+ when %r{\Acoroutine/} then source2 = "{$(VPATH)}$(COROUTINE_H)"
else source2 = "$(top_srcdir)/#{source}"
end
["common.mk", target2, source2]
@@ -238,6 +240,10 @@ DEPENDENCIES_SECTION_END_MARK = "\# AUTOGENERATED DEPENDENCIES END\n"
def init_global
ENV['LC_ALL'] = 'C'
+ if mkflag0 = ENV['GNUMAKEFLAGS'] and (mkflag = mkflag0.sub(/(\A|\s+)-j\d*(?=\s+|\z)/, '')) != mkflag0
+ mkflag.strip!
+ ENV['GNUMAKEFLAGS'] = mkflag
+ end
$opt_fix = false
$opt_a = false
@@ -363,6 +369,7 @@ def read_cc_deps(cwd)
Pathname.glob('**/*.o').sort.each {|fn_o|
fn_i = fn_o.sub_ext('.i')
if !fn_i.exist?
+ next if fn_o.sub_ext('.S').exist?
warn "warning: not found: #{fn_i}"
$i_not_found = true
next
diff --git a/tool/vcs.rb b/tool/vcs.rb
index 95cb0da7a6..e53d0c7cc5 100644
--- a/tool/vcs.rb
+++ b/tool/vcs.rb
@@ -1,6 +1,5 @@
# vcs
require 'fileutils'
-require 'time'
# This library is used by several other tools/ scripts to detect the current
# VCS in use (e.g. SVN, Git) or to interact with that VCS.
@@ -15,7 +14,7 @@ unless File.respond_to? :realpath
end
def IO.pread(*args)
- STDERR.puts(*args.inspect) if $DEBUG
+ STDERR.puts(args.inspect) if $DEBUG
popen(*args) {|f|f.read}
end
@@ -91,9 +90,33 @@ else
$VERBOSE = verbose unless verbose.nil?
end
using DebugPOpen
+ module DebugSystem
+ def system(*args)
+ STDERR.puts args.inspect if $DEBUG
+ exception = false
+ opts = Hash.try_convert(args[-1])
+ if RUBY_VERSION >= "2.6"
+ unless opts
+ opts = {}
+ args << opts
+ end
+ exception = opts.fetch(:exception) {opts[:exception] = true}
+ elsif opts
+ exception = opts.delete(:exception) {true}
+ args.pop if opts.empty?
+ end
+ ret = super(*args)
+ raise "Command failed with status (#$?): #{args[0]}" if exception and !ret
+ ret
+ end
+ end
+ module Kernel
+ prepend(DebugSystem)
+ end
end
class VCS
+ prepend(DebugSystem) if defined?(DebugSystem)
class NotFoundError < RuntimeError; end
@@dirs = []
@@ -101,15 +124,18 @@ class VCS
@@dirs << [dir, self, pred]
end
- def self.detect(path)
- @@dirs.each do |dir, klass, pred|
- curr = path
- loop {
+ def self.detect(path, uplevel_limit: 0)
+ curr = path
+ begin
+ @@dirs.each do |dir, klass, pred|
return klass.new(curr) if pred ? pred[curr, dir] : File.directory?(File.join(curr, dir))
- prev, curr = curr, File.realpath(File.join(curr, '..'))
- break if curr == prev # stop at the root directory
- }
- end
+ end
+ if uplevel_limit
+ break if uplevel_limit.zero?
+ uplevel_limit -= 1
+ end
+ prev, curr = curr, File.realpath(File.join(curr, '..'))
+ end until curr == prev # stop at the root directory
raise VCS::NotFoundError, "does not seem to be under a vcs: #{path}"
end
@@ -140,6 +166,8 @@ class VCS
STDERR.reopen NullDevice, 'w'
end
self.class.get_revisions(path, @srcdir)
+ rescue Errno::ENOENT => e
+ raise VCS::NotFoundError, e.message
ensure
if save_stderr
STDERR.reopen save_stderr
@@ -194,16 +222,17 @@ class VCS
class SVN < self
register(".svn")
+ COMMAND = ENV['SVN'] || 'svn'
def self.get_revisions(path, srcdir = nil)
if srcdir and local_path?(path)
path = File.join(srcdir, path)
end
if srcdir
- info_xml = IO.pread(%W"svn info --xml #{srcdir}")
+ info_xml = IO.pread(%W"#{COMMAND} info --xml #{srcdir}")
info_xml = nil unless info_xml[/<url>(.*)<\/url>/, 1] == path.to_s
end
- info_xml ||= IO.pread(%W"svn info --xml #{path}")
+ info_xml ||= IO.pread(%W"#{COMMAND} info --xml #{path}")
_, last, _, changed, _ = info_xml.split(/revision="(\d+)"/)
modified = info_xml[/<date>([^<>]*)/, 1]
branch = info_xml[%r'<relative-url>\^/(?:branches/|tags/)?([^<>]+)', 1]
@@ -220,7 +249,7 @@ class VCS
end
def get_info
- @info ||= IO.pread(%W"svn info --xml #{@srcdir}")
+ @info ||= IO.pread(%W"#{COMMAND} info --xml #{@srcdir}")
end
def url
@@ -253,7 +282,7 @@ class VCS
end
def branch_list(pat)
- IO.popen(%W"svn ls #{branch('')}") do |f|
+ IO.popen(%W"#{COMMAND} ls #{branch('')}") do |f|
f.each do |line|
line.chomp!
line.chomp!('/')
@@ -263,7 +292,7 @@ class VCS
end
def grep(pat, tag, *files, &block)
- cmd = %W"svn cat"
+ cmd = %W"#{COMMAND} cat"
files.map! {|n| File.join(tag, n)} if tag
set = block.binding.eval("proc {|match| $~ = match}")
IO.popen([cmd, *files]) do |f|
@@ -283,7 +312,7 @@ class VCS
subdir = nil if subdir.empty?
FileUtils.mkdir_p(svndir = dir+"/.svn")
FileUtils.ln_s(Dir.glob(rootdir+"/.svn/*"), svndir)
- system("svn", "-q", "revert", "-R", subdir || ".", :chdir => dir) or return false
+ system(COMMAND, "-q", "revert", "-R", subdir || ".", :chdir => dir) or return false
FileUtils.rm_rf(svndir) unless keep_temp
if subdir
tmpdir = Dir.mktmpdir("tmp-co.", "#{dir}/#{subdir}")
@@ -298,7 +327,7 @@ class VCS
return true
end
end
- IO.popen(%W"svn export -r #{revision} #{url} #{dir}") do |pipe|
+ IO.popen(%W"#{COMMAND} export -r #{revision} #{url} #{dir}") do |pipe|
pipe.each {|line| /^A/ =~ line or yield line}
end
$?.success?
@@ -308,19 +337,24 @@ class VCS
FileUtils.rm_rf(dir+"/.svn")
end
- def export_changelog(from, to, path)
+ def export_changelog(url, from, to, path)
range = [to, (from+1 if from)].compact.join(':')
IO.popen({'TZ' => 'JST-9', 'LANG' => 'C', 'LC_ALL' => 'C'},
- %W"svn log -r#{range} #{url}") do |r|
+ %W"#{COMMAND} log -r#{range} #{url}") do |r|
open(path, 'w') do |w|
IO.copy_stream(r, w)
end
end
end
+
+ def commit
+ system(*%W"#{COMMAND} commit")
+ end
end
class GIT < self
register(".git") {|path, dir| File.exist?(File.join(path, dir))}
+ COMMAND = ENV["GIT"] || 'git'
def self.cmd_args(cmds, srcdir = nil)
if srcdir and local_path?(srcdir)
@@ -339,18 +373,22 @@ class VCS
end
def self.get_revisions(path, srcdir = nil)
- gitcmd = %W[git]
+ gitcmd = [COMMAND]
+ desc = cmd_read_at(srcdir, [gitcmd + %w[describe --tags --match REV_*]])
+ if /\AREV_(\d+)(?:-(\d+)-g\h+)?\Z/ =~ desc
+ last = ($1.to_i + $2.to_i).to_s
+ end
logcmd = gitcmd + %W[log -n1 --date=iso]
- logcmd << "--grep=^ *git-svn-id: .*@[0-9][0-9]*"
+ logcmd << "--grep=^ *git-svn-id: .*@[0-9][0-9]*" unless last
idpat = /git-svn-id: .*?@(\d+) \S+\Z/
log = cmd_read_at(srcdir, [logcmd])
commit = log[/\Acommit (\w+)/, 1]
- last = log[idpat, 1]
+ last ||= log[idpat, 1]
if path
cmd = logcmd
cmd += [path] unless path == '.'
log = cmd_read_at(srcdir, [cmd])
- changed = log[idpat, 1]
+ changed = log[idpat, 1] || last
else
changed = last
end
@@ -390,12 +428,12 @@ class VCS
end
def stable
- cmd = %W"git for-each-ref --format=\%(refname:short) refs/heads/ruby_[0-9]*"
+ cmd = %W"#{COMMAND} for-each-ref --format=\%(refname:short) refs/heads/ruby_[0-9]*"
branch(cmd_read(cmd)[/.*^(ruby_\d+_\d+)$/m, 1])
end
def branch_list(pat)
- cmd = %W"git for-each-ref --format=\%(refname:short) refs/heads/#{pat}"
+ cmd = %W"#{COMMAND} for-each-ref --format=\%(refname:short) refs/heads/#{pat}"
cmd_pipe(cmd) {|f|
f.each {|line|
line.chomp!
@@ -405,7 +443,7 @@ class VCS
end
def grep(pat, tag, *files, &block)
- cmd = %W[git grep -h --perl-regexp #{tag} --]
+ cmd = %W[#{COMMAND} grep -h --perl-regexp #{tag} --]
set = block.binding.eval("proc {|match| $~ = match}")
cmd_pipe(cmd+files) do |f|
f.grep(pat) do |s|
@@ -416,25 +454,24 @@ class VCS
end
def export(revision, url, dir, keep_temp = false)
- ret = system("git", "clone", "-s", (@srcdir || '.').to_s, "-b", url, dir)
- FileUtils.rm_rf("#{dir}/.git") if ret and !keep_temp
+ ret = system(COMMAND, "clone", "-s", (@srcdir || '.').to_s, "-b", url, dir)
ret
end
def after_export(dir)
- FileUtils.rm_rf("#{dir}/.git")
+ FileUtils.rm_rf(Dir.glob("#{dir}/.git*"))
end
- def export_changelog(from, to, path)
+ def export_changelog(url, from, to, path)
range = [from, to].map do |rev|
rev or next
rev = cmd_read({'LANG' => 'C', 'LC_ALL' => 'C'},
- %W"git log -n1 --format=format:%H" <<
+ %W"#{COMMAND} log -n1 --format=format:%H" <<
"--grep=^ *git-svn-id: .*@#{rev} ")
rev unless rev.empty?
- end.join('..')
+ end.join('^..')
cmd_pipe({'TZ' => 'JST-9', 'LANG' => 'C', 'LC_ALL' => 'C'},
- %W"git log --date=iso-local --topo-order #{range}") do |r|
+ %W"#{COMMAND} log --no-notes --date=iso-local --topo-order #{range}", "rb") do |r|
open(path, 'w') do |w|
sep = "-"*72
w.puts sep
@@ -446,12 +483,52 @@ class VCS
s.sub!(/^git-svn-id: .*@(\d+) .*\n+\z/, '')
rev = $1
s.gsub!(/^ {8}/, '') if /^(?! {8}|$)/ !~ s
- date = Time.strptime(time, "%Y-%m-%d %T %z").strftime("%a, %d %b %y")
- w.puts "r#{rev} | #{author} | #{time} (#{date}) | #{s.count("\n")} lines\n\n"
+ s.sub!(/\n\n\z/, "\n")
+ if /\A(\d+)-(\d+)-(\d+)/ =~ time
+ date = Time.new($1.to_i, $2.to_i, $3.to_i).strftime("%a, %d %b %Y")
+ end
+ lines = s.count("\n")
+ lines = "#{lines} line#{lines == 1 ? '' : 's'}"
+ w.puts "r#{rev} | #{author} | #{time} (#{date}) | #{lines}\n\n"
w.puts s, sep
end
end
end
end
+
+ def last_changed_revision
+ rev = cmd_read(%W"#{COMMAND} svn info"+[STDERR=>[:child, :out]])[/^Last Changed Rev: (\d+)/, 1]
+ com = cmd_read(%W"#{COMMAND} svn find-rev r#{rev}").chomp
+ return rev, com
+ end
+
+ def commit(opts = {})
+ dryrun = opts.fetch(:dryrun) {$DEBUG} if opts
+ rev, com = last_changed_revision
+ head = cmd_read(%W"#{COMMAND} symbolic-ref --short HEAD").chomp
+
+ commits = cmd_read([COMMAND, "log", "--reverse", "--format=%H %ae %ce", "#{com}..@"], "rb").split("\n")
+ commits.each_with_index do |l, i|
+ r, a, c = l.split
+ dcommit = [COMMAND, "svn", "dcommit"]
+ dcommit.insert(-2, "-n") if dryrun
+ dcommit << "--add-author-from" unless a == c
+ dcommit << r
+ system(*dcommit) or return false
+ system(COMMAND, "checkout", head) or return false
+ system(COMMAND, "rebase") or return false
+ end
+
+ if rev
+ old = [cmd_read(%W"#{COMMAND} log -1 --format=%H").chomp]
+ old << cmd_read(%W"#{COMMAND} svn reset -r#{rev}")[/^r#{rev} = (\h+)/, 1]
+ 3.times do
+ sleep 2
+ system(*%W"#{COMMAND} pull --no-edit --rebase")
+ break unless old.include?(cmd_read(%W"#{COMMAND} log -1 --format=%H").chomp)
+ end
+ end
+ true
+ end
end
end
diff --git a/tool/ytab.sed b/tool/ytab.sed
index a9599ce0ed..f7438077dc 100755
--- a/tool/ytab.sed
+++ b/tool/ytab.sed
@@ -12,48 +12,65 @@ i\
a\
#endif
}
-/^yydestruct.*yymsg/,/#endif/{
+/^yydestruct.*yymsg/,/{/{
/^yydestruct/{
- /parser/!{
+ /[, *]p)/!{
H
s/^/ruby_parser_&/
- s/)$/, parser)/
- /\*/s/parser)$/struct parser_params *&/
+ s/)$/, p)/
+ /\*/s/p)$/struct parser_params *&/
}
}
/^#endif/{
x
/yydestruct/{
i\
- struct parser_params *parser;
- a\
-#define yydestruct(m, t, v) ruby_parser_yydestruct(m, t, v, parser)
+\ struct parser_params *p;
}
x
}
-}
-/^yy_stack_print/{
- /parser/!{
- H
- s/)$/, parser)/
- /\*/s/parser)$/struct parser_params *&/
+ /^{/{
+ x
+ /yydestruct/{
+ i\
+#define yydestruct(m, t, v) ruby_parser_yydestruct(m, t, v, p)
+ }
+ x
}
}
-/yy_stack_print.*;/{
- x
- /yy_stack_print/{
+/^yy_stack_print /,/{/{
+ /^yy_stack_print/{
+ /[, *]p)/!{
+ H
+ s/^/ruby_parser_&/
+ s/)$/, p)/
+ /\*/s/p)$/struct parser_params *&/
+ }
+ }
+ /^#endif/{
+ x
+ /yy_stack_print/{
+ i\
+\ struct parser_params *p;
+ }
x
- s/\(yy_stack_print *\)(\(.*\));/\1(\2, parser);/
+ }
+ /^{/{
+ x
+ /yy_stack_print/{
+ i\
+#define yy_stack_print(b, t) ruby_parser_yy_stack_print(b, t, p)
+ }
x
}
- x
}
/^yy_reduce_print/,/^}/{
- s/fprintf *(stderr,/YYFPRINTF (parser,/g
+ s/fprintf *(stderr,/YYFPRINTF (p,/g
}
-s/\( YYFPRINTF *(\)yyoutput,/\1parser,/
-s/\( YYFPRINTF *(\)stderr,/\1parser,/
-s/\( YYDPRINTF *((\)stderr,/\1parser,/
+s/\( YYFPRINTF *(\)yyoutput,/\1p,/
+s/\( YYFPRINTF *(\)yyo,/\1p,/
+s/\( YYFPRINTF *(\)stderr,/\1p,/
+s/\( YYDPRINTF *((\)stderr,/\1p,/
s/^\([ ]*\)\(yyerror[ ]*([ ]*parser,\)/\1parser_\2/
s!^ *extern char \*getenv();!/* & */!
s/^\(#.*\)".*\.tab\.c"/\1"parse.c"/
diff --git a/transcode.c b/transcode.c
index 2c0b30ca0e..55ff30c934 100644
--- a/transcode.c
+++ b/transcode.c
@@ -9,20 +9,22 @@
**********************************************************************/
+#include "ruby/encoding.h"
#include "internal.h"
#include "transcode_data.h"
+#include "id.h"
#include <ctype.h>
#define ENABLE_ECONV_NEWLINE_OPTION 1
/* VALUE rb_cEncoding = rb_define_class("Encoding", rb_cObject); */
-VALUE rb_eUndefinedConversionError;
-VALUE rb_eInvalidByteSequenceError;
-VALUE rb_eConverterNotFoundError;
+static VALUE rb_eUndefinedConversionError;
+static VALUE rb_eInvalidByteSequenceError;
+static VALUE rb_eConverterNotFoundError;
VALUE rb_cEncodingConverter;
-static VALUE sym_invalid, sym_undef, sym_replace, sym_fallback, sym_aref;
+static VALUE sym_invalid, sym_undef, sym_replace, sym_fallback;
static VALUE sym_xml, sym_text, sym_attr;
static VALUE sym_universal_newline;
static VALUE sym_crlf_newline;
@@ -368,14 +370,13 @@ load_transcoder_entry(transcoder_entry_t *entry)
const size_t total_len = sizeof(transcoder_lib_prefix) - 1 + len;
const VALUE fn = rb_str_new(0, total_len);
char *const path = RSTRING_PTR(fn);
- const int safe = rb_safe_level();
memcpy(path, transcoder_lib_prefix, sizeof(transcoder_lib_prefix) - 1);
memcpy(path + sizeof(transcoder_lib_prefix) - 1, lib, len);
rb_str_set_len(fn, total_len);
FL_UNSET(fn, FL_TAINT);
OBJ_FREEZE(fn);
- rb_require_safe(fn, safe > 3 ? 3 : safe);
+ rb_require_safe(fn, rb_safe_level());
}
if (entry->transcoder)
@@ -2256,7 +2257,7 @@ method_fallback(VALUE fallback, VALUE c)
static VALUE
aref_fallback(VALUE fallback, VALUE c)
{
- return rb_funcall3(fallback, sym_aref, 1, &c);
+ return rb_funcallv_public(fallback, idAREF, 1, &c);
}
static void
@@ -2550,7 +2551,7 @@ rb_econv_prepare_options(VALUE opthash, VALUE *opts, int ecflags)
if (!NIL_P(v)) {
VALUE h = rb_check_hash_type(v);
if (NIL_P(h)
- ? (rb_obj_is_proc(v) || rb_obj_is_method(v) || rb_respond_to(v, sym_aref))
+ ? (rb_obj_is_proc(v) || rb_obj_is_method(v) || rb_respond_to(v, idAREF))
: (v = h, 1)) {
if (NIL_P(newhash))
newhash = rb_hash_new();
@@ -2905,6 +2906,11 @@ encoded_dup(VALUE newstr, VALUE str, int encidx)
return str_encode_associate(newstr, encidx);
}
+/*
+ * Document-class: Encoding::Converter
+ *
+ * Encoding conversion class.
+ */
static void
econv_free(void *ptr)
{
@@ -3150,8 +3156,12 @@ econv_s_search_convpath(int argc, VALUE *argv, VALUE klass)
convpath = Qnil;
transcode_search_path(sname, dname, search_convpath_i, &convpath);
- if (NIL_P(convpath))
- rb_exc_raise(rb_econv_open_exc(sname, dname, ecflags));
+ if (NIL_P(convpath)) {
+ VALUE exc = rb_econv_open_exc(sname, dname, ecflags);
+ RB_GC_GUARD(snamev);
+ RB_GC_GUARD(dnamev);
+ rb_exc_raise(exc);
+ }
if (decorate_convpath(convpath, ecflags) == -1) {
VALUE exc = rb_econv_open_exc(sname, dname, ecflags);
@@ -4075,7 +4085,7 @@ econv_insert_output(VALUE self, VALUE string)
}
/*
- * call-seq
+ * call-seq:
* ec.putback -> string
* ec.putback(max_numbytes) -> string
*
@@ -4106,10 +4116,9 @@ econv_putback(int argc, VALUE *argv, VALUE self)
int putbackable;
VALUE str, max;
- rb_scan_args(argc, argv, "01", &max);
-
- if (NIL_P(max))
+ if (!rb_check_arity(argc, 0, 1) || NIL_P(max = argv[0])) {
n = rb_econv_putbackable(ec);
+ }
else {
n = NUM2INT(max);
putbackable = rb_econv_putbackable(ec);
@@ -4416,7 +4425,6 @@ Init_transcode(void)
sym_undef = ID2SYM(rb_intern("undef"));
sym_replace = ID2SYM(rb_intern("replace"));
sym_fallback = ID2SYM(rb_intern("fallback"));
- sym_aref = ID2SYM(rb_intern("[]"));
sym_xml = ID2SYM(rb_intern("xml"));
sym_text = ID2SYM(rb_intern("text"));
sym_attr = ID2SYM(rb_intern("attr"));
diff --git a/transient_heap.c b/transient_heap.c
new file mode 100644
index 0000000000..55a796325f
--- /dev/null
+++ b/transient_heap.c
@@ -0,0 +1,872 @@
+/**********************************************************************
+
+ transient_heap.c - implement transient_heap.
+
+ Copyright (C) 2018 Koichi Sasada
+
+**********************************************************************/
+
+#include "ruby/ruby.h"
+#include "ruby/debug.h"
+#include "vm_debug.h"
+#include "gc.h"
+#include "internal.h"
+#include "ruby_assert.h"
+#include "transient_heap.h"
+#include "debug_counter.h"
+
+#if USE_TRANSIENT_HEAP /* USE_TRANSIENT_HEAP */
+/*
+ * 1: enable assertions
+ * 2: enable verify all transient heaps
+ */
+#ifndef TRANSIENT_HEAP_CHECK_MODE
+#define TRANSIENT_HEAP_CHECK_MODE 0
+#endif
+#define TH_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(TRANSIENT_HEAP_CHECK_MODE > 0, expr, #expr)
+
+/*
+ * 1: show events
+ * 2: show dump at events
+ * 3: show all operations
+ */
+#define TRANSIENT_HEAP_DEBUG 0
+
+/* For Debug: Provide blocks infinitely.
+ * This mode generates blocks unlimitedly
+ * and prohibit access free'ed blocks to check invalid access.
+ */
+#define TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK 0
+
+#if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
+#include <sys/mman.h>
+#include <errno.h>
+#endif
+
+/* For Debug: Prohibit promoting to malloc space.
+ */
+#define TRANSIENT_HEAP_DEBUG_DONT_PROMOTE 0
+
+/* size configuration */
+#define TRANSIENT_HEAP_PROMOTED_DEFAULT_SIZE 1024
+
+ /* K M */
+#define TRANSIENT_HEAP_BLOCK_SIZE (1024 * 32 ) /* 32KB int16_t */
+#define TRANSIENT_HEAP_TOTAL_SIZE (1024 * 1024 * 32) /* 32 MB */
+#define TRANSIENT_HEAP_ALLOC_MAX (1024 * 2 ) /* 2 KB */
+#define TRANSIENT_HEAP_BLOCK_NUM (TRANSIENT_HEAP_TOTAL_SIZE / TRANSIENT_HEAP_BLOCK_SIZE)
+
+#define TRANSIENT_HEAP_ALLOC_MAGIC 0xfeab
+#define TRANSIENT_HEAP_ALLOC_ALIGN RUBY_ALIGNOF(void *)
+
+#define TRANSIENT_HEAP_ALLOC_MARKING_LAST -1
+#define TRANSIENT_HEAP_ALLOC_MARKING_FREE -2
+
+enum transient_heap_status {
+ transient_heap_none,
+ transient_heap_marking,
+ transient_heap_escaping
+};
+
+struct transient_heap_block {
+ struct transient_heap_block_header {
+ int16_t size; /* sizeof(block) = TRANSIENT_HEAP_BLOCK_SIZE - sizeof(struct transient_heap_block_header) */
+ int16_t index;
+ int16_t last_marked_index;
+ int16_t objects;
+ struct transient_heap_block *next_block;
+ } info;
+ char buff[TRANSIENT_HEAP_BLOCK_SIZE - sizeof(struct transient_heap_block_header)];
+};
+
+struct transient_heap {
+ struct transient_heap_block *using_blocks;
+ struct transient_heap_block *marked_blocks;
+ struct transient_heap_block *free_blocks;
+ int total_objects;
+ int total_marked_objects;
+ int total_blocks;
+ enum transient_heap_status status;
+
+ VALUE *promoted_objects;
+ int promoted_objects_size;
+ int promoted_objects_index;
+
+ struct transient_heap_block *arena;
+ int arena_index; /* increment only */
+};
+
+struct transient_alloc_header {
+ uint16_t magic;
+ uint16_t size;
+ int16_t next_marked_index;
+ int16_t dummy;
+ VALUE obj;
+};
+
+static struct transient_heap global_transient_heap;
+
+static void transient_heap_promote_add(struct transient_heap* theap, VALUE obj);
+static const void *transient_heap_ptr(VALUE obj, int error);
+static int transient_header_managed_ptr_p(struct transient_heap* theap, const void *ptr);
+
+#define ROUND_UP(v, a) (((size_t)(v) + (a) - 1) & ~((a) - 1))
+
+static void
+transient_heap_block_dump(struct transient_heap* theap, struct transient_heap_block *block)
+{
+ int i=0, n=0;
+
+ while (i<block->info.index) {
+ void *ptr = &block->buff[i];
+ struct transient_alloc_header *header = ptr;
+ fprintf(stderr, "%4d %8d %p size:%4d next:%4d %s\n", n, i, ptr, header->size, header->next_marked_index, rb_obj_info(header->obj));
+ i += header->size;
+ n++;
+ }
+}
+
+static void
+transient_heap_blocks_dump(struct transient_heap* theap, struct transient_heap_block *block, const char *type_str)
+{
+ while (block) {
+ fprintf(stderr, "- transient_heap_dump: %s:%p index:%d objects:%d last_marked_index:%d next:%p\n",
+ type_str, (void *)block, block->info.index, block->info.objects, block->info.last_marked_index, (void *)block->info.next_block);
+
+ transient_heap_block_dump(theap, block);
+ block = block->info.next_block;
+ }
+}
+
+static void
+transient_heap_dump(struct transient_heap* theap)
+{
+ fprintf(stderr, "transient_heap_dump objects:%d marked_objects:%d blocks:%d\n", theap->total_objects, theap->total_marked_objects, theap->total_blocks);
+ transient_heap_blocks_dump(theap, theap->using_blocks, "using_blocks");
+ transient_heap_blocks_dump(theap, theap->marked_blocks, "marked_blocks");
+ transient_heap_blocks_dump(theap, theap->free_blocks, "free_blocks");
+}
+
+/* Debug: dump all tarnsient_heap blocks */
+void
+rb_transient_heap_dump(void)
+{
+ transient_heap_dump(&global_transient_heap);
+}
+
+#if TRANSIENT_HEAP_CHECK_MODE >= 2
+static void
+transient_heap_ptr_check(struct transient_heap *theap, VALUE obj)
+{
+ if (obj != Qundef) {
+ const void *ptr = transient_heap_ptr(obj, FALSE);
+ TH_ASSERT(ptr == NULL || transient_header_managed_ptr_p(theap, ptr));
+ }
+}
+
+static int
+transient_heap_block_verify(struct transient_heap *theap, struct transient_heap_block *block)
+{
+ int i=0, n=0;
+ struct transient_alloc_header *header;
+
+ while (i<block->info.index) {
+ header = (void *)&block->buff[i];
+ TH_ASSERT(header->magic == TRANSIENT_HEAP_ALLOC_MAGIC);
+ transient_heap_ptr_check(theap, header->obj);
+ n ++;
+ i += header->size;
+ }
+ TH_ASSERT(block->info.objects == n);
+
+ return n;
+}
+
+static int
+transient_heap_blocks_verify(struct transient_heap *theap, struct transient_heap_block *blocks, int *block_num_ptr)
+{
+ int n = 0;
+ struct transient_heap_block *block = blocks;
+ while (block) {
+ n += transient_heap_block_verify(theap, block);
+ *block_num_ptr += 1;
+ block = block->info.next_block;
+ }
+
+ return n;
+}
+#endif
+
+static void
+transient_heap_verify(struct transient_heap *theap)
+{
+#if TRANSIENT_HEAP_CHECK_MODE >= 2
+ int n=0, block_num=0;
+
+ n += transient_heap_blocks_verify(theap, theap->using_blocks, &block_num);
+ n += transient_heap_blocks_verify(theap, theap->marked_blocks, &block_num);
+
+ TH_ASSERT(n == theap->total_objects);
+ TH_ASSERT(n >= theap->total_marked_objects);
+ TH_ASSERT(block_num == theap->total_blocks);
+#endif
+}
+
+/* Debug: check assertions for all transient_heap blocks */
+void
+rb_transient_heap_verify(void)
+{
+ transient_heap_verify(&global_transient_heap);
+}
+
+static struct transient_heap*
+transient_heap_get(void)
+{
+ struct transient_heap* theap = &global_transient_heap;
+ transient_heap_verify(theap);
+ return theap;
+}
+
+static void
+reset_block(struct transient_heap_block *block)
+{
+ __msan_allocated_memory(block, sizeof block);
+ block->info.size = TRANSIENT_HEAP_BLOCK_SIZE - sizeof(struct transient_heap_block_header);
+ block->info.index = 0;
+ block->info.objects = 0;
+ block->info.last_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_LAST;
+ block->info.next_block = NULL;
+ __asan_poison_memory_region(&block->buff, sizeof block->buff);
+}
+
+static void
+connect_to_free_blocks(struct transient_heap *theap, struct transient_heap_block *block)
+{
+ block->info.next_block = theap->free_blocks;
+ theap->free_blocks = block;
+}
+
+static void
+connect_to_using_blocks(struct transient_heap *theap, struct transient_heap_block *block)
+{
+ block->info.next_block = theap->using_blocks;
+ theap->using_blocks = block;
+}
+
+#if 0
+static void
+connect_to_marked_blocks(struct transient_heap *theap, struct transient_heap_block *block)
+{
+ block->info.next_block = theap->marked_blocks;
+ theap->marked_blocks = block;
+}
+#endif
+
+static void
+append_to_marked_blocks(struct transient_heap *theap, struct transient_heap_block *append_blocks)
+{
+ if (theap->marked_blocks) {
+ struct transient_heap_block *block = theap->marked_blocks, *last_block = NULL;
+ while (block) {
+ last_block = block;
+ block = block->info.next_block;
+ }
+
+ TH_ASSERT(last_block->info.next_block == NULL);
+ last_block->info.next_block = append_blocks;
+ }
+ else {
+ theap->marked_blocks = append_blocks;
+ }
+}
+
+static struct transient_heap_block *
+transient_heap_block_alloc(struct transient_heap* theap)
+{
+ struct transient_heap_block *block;
+#if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
+ block = mmap(NULL, TRANSIENT_HEAP_BLOCK_SIZE, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+ if (block == MAP_FAILED) rb_bug("transient_heap_block_alloc: err:%d\n", errno);
+#else
+ if (theap->arena == NULL) {
+ theap->arena = rb_aligned_malloc(TRANSIENT_HEAP_BLOCK_SIZE, TRANSIENT_HEAP_TOTAL_SIZE);
+ }
+
+ TH_ASSERT(theap->arena_index < TRANSIENT_HEAP_BLOCK_NUM);
+ block = &theap->arena[theap->arena_index++];
+ TH_ASSERT(((intptr_t)block & (TRANSIENT_HEAP_BLOCK_SIZE - 1)) == 0);
+#endif
+ reset_block(block);
+
+ TH_ASSERT(((intptr_t)block->buff & (TRANSIENT_HEAP_ALLOC_ALIGN-1)) == 0);
+ if (0) fprintf(stderr, "transient_heap_block_alloc: %4d %p\n", theap->total_blocks, (void *)block);
+ return block;
+}
+
+
+static struct transient_heap_block *
+transient_heap_allocatable_block(struct transient_heap* theap)
+{
+ struct transient_heap_block *block;
+
+#if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
+ block = transient_heap_block_alloc(theap);
+ theap->total_blocks++;
+#else
+ /* get one block from free_blocks */
+ block = theap->free_blocks;
+ if (block) {
+ theap->free_blocks = block->info.next_block;
+ block->info.next_block = NULL;
+ theap->total_blocks++;
+ }
+#endif
+
+ return block;
+}
+
+static struct transient_alloc_header *
+transient_heap_allocatable_header(struct transient_heap* theap, size_t size)
+{
+ struct transient_heap_block *block = theap->using_blocks;
+
+ while (block) {
+ TH_ASSERT(block->info.size >= block->info.index);
+
+ if (block->info.size - block->info.index >= (int32_t)size) {
+ struct transient_alloc_header *header = (void *)&block->buff[block->info.index];
+ block->info.index += size;
+ block->info.objects++;
+ return header;
+ }
+ else {
+ block = transient_heap_allocatable_block(theap);
+ if (block) connect_to_using_blocks(theap, block);
+ }
+ }
+
+ return NULL;
+}
+
+void *
+rb_transient_heap_alloc(VALUE obj, size_t req_size)
+{
+ struct transient_heap* theap = transient_heap_get();
+ size_t size = ROUND_UP(req_size + sizeof(struct transient_alloc_header), TRANSIENT_HEAP_ALLOC_ALIGN);
+
+ TH_ASSERT(RB_TYPE_P(obj, T_ARRAY) ||
+ RB_TYPE_P(obj, T_OBJECT) ||
+ RB_TYPE_P(obj, T_STRUCT) ||
+ RB_TYPE_P(obj, T_HASH)); /* supported types */
+
+ if (size > TRANSIENT_HEAP_ALLOC_MAX) {
+ if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [too big: %ld] %s\n", (long)size, rb_obj_info(obj));
+ return NULL;
+ }
+#if TRANSIENT_HEAP_DEBUG_DONT_PROMOTE == 0
+ else if (RB_OBJ_PROMOTED_RAW(obj)) {
+ if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [promoted object] %s\n", rb_obj_info(obj));
+ return NULL;
+ }
+#else
+ else if (RBASIC_CLASS(obj) == 0) {
+ if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [hidden object] %s\n", rb_obj_info(obj));
+ return NULL;
+ }
+#endif
+ else {
+ struct transient_alloc_header *header = transient_heap_allocatable_header(theap, size);
+ if (header) {
+ void *ptr;
+
+ /* header is poisoned to prevent buffer overflow, should
+ * unpoison first... */
+ unpoison_memory_region(header, sizeof *header, true);
+
+ header->size = size;
+ header->magic = TRANSIENT_HEAP_ALLOC_MAGIC;
+ header->next_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_FREE;
+ header->obj = obj; /* TODO: can we eliminate it? */
+
+ /* header is fixed; shall poison again */
+ poison_memory_region(header, sizeof *header);
+ ptr = header + 1;
+
+ theap->total_objects++; /* statistics */
+
+#if TRANSIENT_HEAP_DEBUG_DONT_PROMOTE
+ if (RB_OBJ_PROMOTED_RAW(obj)) {
+ transient_heap_promote_add(theap, obj);
+ }
+#endif
+ if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: header:%p ptr:%p size:%d obj:%s\n", (void *)header, ptr, (int)size, rb_obj_info(obj));
+
+ RB_DEBUG_COUNTER_INC(theap_alloc);
+
+ /* ptr is set up; OK to unpoison. */
+ unpoison_memory_region(ptr, size, true);
+ return ptr;
+ }
+ else {
+ if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [no enough space: %ld] %s\n", (long)size, rb_obj_info(obj));
+ RB_DEBUG_COUNTER_INC(theap_alloc_fail);
+ return NULL;
+ }
+ }
+}
+
+void
+Init_TransientHeap(void)
+{
+ int i, block_num;
+ struct transient_heap* theap = transient_heap_get();
+
+#if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
+ block_num = 0;
+#else
+ TH_ASSERT(TRANSIENT_HEAP_BLOCK_SIZE * TRANSIENT_HEAP_BLOCK_NUM == TRANSIENT_HEAP_TOTAL_SIZE);
+ block_num = TRANSIENT_HEAP_BLOCK_NUM;
+#endif
+ for (i=0; i<block_num; i++) {
+ connect_to_free_blocks(theap, transient_heap_block_alloc(theap));
+ }
+ theap->using_blocks = transient_heap_allocatable_block(theap);
+
+ theap->promoted_objects_size = TRANSIENT_HEAP_PROMOTED_DEFAULT_SIZE;
+ theap->promoted_objects_index = 0;
+ /* should not use ALLOC_N to be free from GC */
+ theap->promoted_objects = malloc(sizeof(VALUE) * theap->promoted_objects_size);
+ if (theap->promoted_objects == NULL) rb_bug("Init_TransientHeap: malloc failed.");
+}
+
+static struct transient_heap_block *
+blocks_alloc_header_to_block(struct transient_heap *theap, struct transient_heap_block *blocks, struct transient_alloc_header *header)
+{
+ struct transient_heap_block *block = blocks;
+
+ while (block) {
+ if (block->buff <= (char *)header && (char *)header < block->buff + block->info.size) {
+ return block;
+ }
+ block = block->info.next_block;
+ }
+
+ return NULL;
+}
+
+static struct transient_heap_block *
+alloc_header_to_block_verbose(struct transient_heap *theap, struct transient_alloc_header *header)
+{
+ struct transient_heap_block *block;
+
+ if ((block = blocks_alloc_header_to_block(theap, theap->marked_blocks, header)) != NULL) {
+ if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "alloc_header_to_block: found in marked_blocks\n");
+ return block;
+ }
+ else if ((block = blocks_alloc_header_to_block(theap, theap->using_blocks, header)) != NULL) {
+ if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "alloc_header_to_block: found in using_blocks\n");
+ return block;
+ }
+ else {
+ return NULL;
+ }
+}
+
+static struct transient_alloc_header *
+ptr_to_alloc_header(const void *ptr)
+{
+ struct transient_alloc_header *header = (void *)ptr;
+ header -= 1;
+ return header;
+}
+
+static int
+transient_header_managed_ptr_p(struct transient_heap* theap, const void *ptr)
+{
+ if (alloc_header_to_block_verbose(theap, ptr_to_alloc_header(ptr))) {
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+
+int
+rb_transient_heap_managed_ptr_p(const void *ptr)
+{
+ return transient_header_managed_ptr_p(transient_heap_get(), ptr);
+}
+
+static struct transient_heap_block *
+alloc_header_to_block(struct transient_heap *theap, struct transient_alloc_header *header)
+{
+ struct transient_heap_block *block;
+#if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
+ block = alloc_header_to_block_verbose(theap, header);
+ if (block == NULL) {
+ transient_heap_dump(theap);
+ rb_bug("alloc_header_to_block: not found in mark_blocks (%p)\n", header);
+ }
+#else
+ block = (void *)((intptr_t)header & ~(TRANSIENT_HEAP_BLOCK_SIZE-1));
+ TH_ASSERT(block == alloc_header_to_block_verbose(theap, header));
+#endif
+ return block;
+}
+
+void
+rb_transient_heap_mark(VALUE obj, const void *ptr)
+{
+ struct transient_alloc_header *header = ptr_to_alloc_header(ptr);
+ unpoison_memory_region(header, sizeof *header, false);
+ if (header->magic != TRANSIENT_HEAP_ALLOC_MAGIC) rb_bug("rb_transient_heap_mark: wrong header, %s (%p)", rb_obj_info(obj), ptr);
+ if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_mark: %s (%p)\n", rb_obj_info(obj), ptr);
+
+#if TRANSIENT_HEAP_CHECK_MODE > 0
+ {
+ struct transient_heap* theap = transient_heap_get();
+ TH_ASSERT(theap->status == transient_heap_marking);
+ TH_ASSERT(transient_header_managed_ptr_p(theap, ptr));
+
+ if (header->magic != TRANSIENT_HEAP_ALLOC_MAGIC) {
+ transient_heap_dump(theap);
+ rb_bug("rb_transient_heap_mark: magic is broken");
+ }
+ else if (header->obj != obj) {
+ // transient_heap_dump(theap);
+ rb_bug("rb_transient_heap_mark: unmatch (%s is stored, but %s is given)\n",
+ rb_obj_info(header->obj), rb_obj_info(obj));
+ }
+ }
+#endif
+
+ if (header->next_marked_index != TRANSIENT_HEAP_ALLOC_MARKING_FREE) {
+ /* already marked */
+ return;
+ }
+ else {
+ struct transient_heap* theap = transient_heap_get();
+ struct transient_heap_block *block = alloc_header_to_block(theap, header);
+ __asan_unpoison_memory_region(&block->info, sizeof block->info);
+ header->next_marked_index = block->info.last_marked_index;
+ block->info.last_marked_index = (int)((char *)header - block->buff);
+ theap->total_marked_objects++;
+
+ transient_heap_verify(theap);
+ }
+}
+
+static const void *
+transient_heap_ptr(VALUE obj, int error)
+{
+ const void *ptr = NULL;
+
+ switch (BUILTIN_TYPE(obj)) {
+ case T_ARRAY:
+ if (RARRAY_TRANSIENT_P(obj)) {
+ TH_ASSERT(!FL_TEST_RAW(obj, RARRAY_EMBED_FLAG));
+ ptr = RARRAY(obj)->as.heap.ptr;
+ }
+ break;
+ case T_OBJECT:
+ if (ROBJ_TRANSIENT_P(obj)) {
+ ptr = ROBJECT_IVPTR(obj);
+ }
+ break;
+ case T_STRUCT:
+ if (RSTRUCT_TRANSIENT_P(obj)) {
+ ptr = rb_struct_const_heap_ptr(obj);
+ }
+ break;
+ case T_HASH:
+ if (RHASH_TRANSIENT_P(obj)) {
+ TH_ASSERT(RHASH_AR_TABLE_P(obj));
+ ptr = (VALUE *)(RHASH(obj)->as.ar);
+ }
+ else {
+ ptr = NULL;
+ }
+ break;
+ default:
+ if (error) {
+ rb_bug("transient_heap_ptr: unknown obj %s\n", rb_obj_info(obj));
+ }
+ }
+
+ return ptr;
+}
+
+static void
+transient_heap_promote_add(struct transient_heap* theap, VALUE obj)
+{
+ if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_promote: %s\n", rb_obj_info(obj));
+
+ if (TRANSIENT_HEAP_DEBUG_DONT_PROMOTE) {
+ /* duplicate check */
+ int i;
+ for (i=0; i<theap->promoted_objects_index; i++) {
+ if (theap->promoted_objects[i] == obj) return;
+ }
+ }
+
+ if (theap->promoted_objects_size <= theap->promoted_objects_index) {
+ theap->promoted_objects_size *= 2;
+ if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "rb_transient_heap_promote: expand table to %d\n", theap->promoted_objects_size);
+ theap->promoted_objects = realloc(theap->promoted_objects, theap->promoted_objects_size * sizeof(VALUE));
+ if (theap->promoted_objects == NULL) rb_bug("rb_transient_heap_promote: realloc failed");
+ }
+ theap->promoted_objects[theap->promoted_objects_index++] = obj;
+}
+
+void
+rb_transient_heap_promote(VALUE obj)
+{
+ if (transient_heap_ptr(obj, FALSE)) {
+ struct transient_heap* theap = transient_heap_get();
+ transient_heap_promote_add(theap, obj);
+ }
+ else {
+ /* ignore */
+ }
+}
+
+static struct transient_alloc_header *
+alloc_header(struct transient_heap_block* block, int index)
+{
+ return (void *)&block->buff[index];
+}
+
+static void
+transient_heap_reset(void)
+{
+ struct transient_heap* theap = transient_heap_get();
+ struct transient_heap_block* block;
+
+ if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! transient_heap_reset\n");
+
+ block = theap->marked_blocks;
+ while (block) {
+ struct transient_heap_block *next_block = block->info.next_block;
+ theap->total_objects -= block->info.objects;
+#if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
+ if (madvise(block, TRANSIENT_HEAP_BLOCK_SIZE, MADV_DONTNEED) != 0) {
+ rb_bug("madvise err:%d", errno);
+ }
+ if (mprotect(block, TRANSIENT_HEAP_BLOCK_SIZE, PROT_NONE) != 0) {
+ rb_bug("mprotect err:%d", errno);
+ }
+#else
+ reset_block(block);
+ connect_to_free_blocks(theap, block);
+#endif
+ theap->total_blocks--;
+ block = next_block;
+ }
+
+ if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! transient_heap_reset block_num:%d\n", theap->total_blocks);
+
+ theap->marked_blocks = NULL;
+ theap->total_marked_objects = 0;
+}
+
+static void
+transient_heap_block_evacuate(struct transient_heap* theap, struct transient_heap_block* block)
+{
+ int marked_index = block->info.last_marked_index;
+ block->info.last_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_LAST;
+
+ while (marked_index >= 0) {
+ struct transient_alloc_header *header = alloc_header(block, marked_index);
+ VALUE obj = header->obj;
+ TH_ASSERT(header->magic == TRANSIENT_HEAP_ALLOC_MAGIC);
+ if (header->magic != TRANSIENT_HEAP_ALLOC_MAGIC) rb_bug("rb_transient_heap_mark: wrong header %s\n", rb_obj_info(obj));
+
+ if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, " * transient_heap_block_evacuate %p %s\n", (void *)header, rb_obj_info(obj));
+
+ if (obj != Qnil) {
+ RB_DEBUG_COUNTER_INC(theap_evacuate);
+
+ switch (BUILTIN_TYPE(obj)) {
+ case T_ARRAY:
+ rb_ary_transient_heap_evacuate(obj, !TRANSIENT_HEAP_DEBUG_DONT_PROMOTE);
+ break;
+ case T_OBJECT:
+ rb_obj_transient_heap_evacuate(obj, !TRANSIENT_HEAP_DEBUG_DONT_PROMOTE);
+ break;
+ case T_STRUCT:
+ rb_struct_transient_heap_evacuate(obj, !TRANSIENT_HEAP_DEBUG_DONT_PROMOTE);
+ break;
+ case T_HASH:
+ rb_hash_transient_heap_evacuate(obj, !TRANSIENT_HEAP_DEBUG_DONT_PROMOTE);
+ break;
+ default:
+ rb_bug("unsupporeted: %s\n", rb_obj_info(obj));
+ }
+ header->obj = Qundef; /* for debug */
+ }
+ marked_index = header->next_marked_index;
+ }
+}
+
+static void
+transient_heap_update_status(struct transient_heap* theap, enum transient_heap_status status)
+{
+ TH_ASSERT(theap->status != status);
+ theap->status = status;
+}
+
+static void
+transient_heap_evacuate(void *dmy)
+{
+ struct transient_heap* theap = transient_heap_get();
+
+ if (theap->status == transient_heap_marking) {
+ if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! transient_heap_evacuate: skip while transient_heap_marking\n");
+ }
+ else {
+ VALUE gc_disabled = rb_gc_disable();
+ struct transient_heap_block* block;
+
+ if (TRANSIENT_HEAP_DEBUG >= 1) {
+ int i;
+ fprintf(stderr, "!! transient_heap_evacuate start total_blocks:%d\n", theap->total_blocks);
+ if (TRANSIENT_HEAP_DEBUG >= 4) {
+ for (i=0; i<theap->promoted_objects_index; i++) fprintf(stderr, "%4d %s\n", i, rb_obj_info(theap->promoted_objects[i]));
+ }
+ }
+ if (TRANSIENT_HEAP_DEBUG >= 2) transient_heap_dump(theap);
+
+ TH_ASSERT(theap->status == transient_heap_none);
+ transient_heap_update_status(theap, transient_heap_escaping);
+
+ /* evacuate from marked blocks */
+ block = theap->marked_blocks;
+ while (block) {
+ transient_heap_block_evacuate(theap, block);
+ block = block->info.next_block;
+ }
+
+ /* evacuate from using blocks
+ only affect incremental marking */
+ block = theap->using_blocks;
+ while (block) {
+ transient_heap_block_evacuate(theap, block);
+ block = block->info.next_block;
+ }
+
+ /* all objects in marked_objects are escaped. */
+ transient_heap_reset();
+
+ if (TRANSIENT_HEAP_DEBUG > 0) {
+ fprintf(stderr, "!! transient_heap_evacuate end total_blocks:%d\n", theap->total_blocks);
+ }
+
+ transient_heap_verify(theap);
+ transient_heap_update_status(theap, transient_heap_none);
+ if (gc_disabled != Qtrue) rb_gc_enable();
+ }
+}
+
+static void
+clear_marked_index(struct transient_heap_block* block)
+{
+ int marked_index = block->info.last_marked_index;
+
+ while (marked_index != TRANSIENT_HEAP_ALLOC_MARKING_LAST) {
+ struct transient_alloc_header *header = alloc_header(block, marked_index);
+ TH_ASSERT(marked_index != TRANSIENT_HEAP_ALLOC_MARKING_FREE);
+ if (0) fprintf(stderr, "clear_marked_index - block:%p mark_index:%d\n", (void *)block, marked_index);
+
+ marked_index = header->next_marked_index;
+ header->next_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_FREE;
+ }
+
+ block->info.last_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_LAST;
+}
+
+static void
+blocks_clear_marked_index(struct transient_heap_block* block)
+{
+ while (block) {
+ clear_marked_index(block);
+ block = block->info.next_block;
+ }
+}
+
+void
+rb_transient_heap_start_marking(int full_marking)
+{
+ struct transient_heap* theap = transient_heap_get();
+
+ if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! rb_transient_heap_start_marking objects:%d blocks:%d promtoed:%d full_marking:%d\n",
+ theap->total_objects, theap->total_blocks, theap->promoted_objects_index, full_marking);
+ if (TRANSIENT_HEAP_DEBUG >= 2) transient_heap_dump(theap);
+
+ blocks_clear_marked_index(theap->marked_blocks);
+ blocks_clear_marked_index(theap->using_blocks);
+
+ if (theap->using_blocks) {
+ if (theap->using_blocks->info.objects > 0) {
+ append_to_marked_blocks(theap, theap->using_blocks);
+ theap->using_blocks = NULL;
+ }
+ else {
+ append_to_marked_blocks(theap, theap->using_blocks->info.next_block);
+ theap->using_blocks->info.next_block = NULL;
+ }
+ }
+
+ if (theap->using_blocks == NULL) {
+ theap->using_blocks = transient_heap_allocatable_block(theap);
+ }
+
+ TH_ASSERT(theap->status == transient_heap_none);
+ transient_heap_update_status(theap, transient_heap_marking);
+ theap->total_marked_objects = 0;
+
+ if (full_marking) {
+ theap->promoted_objects_index = 0;
+ }
+ else { /* mark promoted objects */
+ int i;
+ for (i=0; i<theap->promoted_objects_index; i++) {
+ VALUE obj = theap->promoted_objects[i];
+ const void *ptr = transient_heap_ptr(obj, TRUE);
+ if (ptr) {
+ rb_transient_heap_mark(obj, ptr);
+ }
+ }
+ }
+
+ transient_heap_verify(theap);
+}
+
+void
+rb_transient_heap_finish_marking(void)
+{
+ struct transient_heap* theap = transient_heap_get();
+
+ if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! rb_transient_heap_finish_marking objects:%d, marked:%d\n",
+ theap->total_objects,
+ theap->total_marked_objects);
+ if (TRANSIENT_HEAP_DEBUG >= 2) transient_heap_dump(theap);
+
+ TH_ASSERT(theap->total_objects >= theap->total_marked_objects);
+
+ TH_ASSERT(theap->status == transient_heap_marking);
+ transient_heap_update_status(theap, transient_heap_none);
+
+ if (theap->total_marked_objects > 0) {
+ if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "-> rb_transient_heap_finish_marking register escape func.\n");
+ rb_postponed_job_register_one(0, transient_heap_evacuate, NULL);
+ }
+ else {
+ transient_heap_reset();
+ }
+
+ transient_heap_verify(theap);
+}
+#endif /* USE_TRANSIENT_HEAP */
diff --git a/transient_heap.h b/transient_heap.h
new file mode 100644
index 0000000000..a34aa1e4f8
--- /dev/null
+++ b/transient_heap.h
@@ -0,0 +1,60 @@
+/**********************************************************************
+
+ transient_heap.h - declarations of transient_heap related APIs.
+
+ Copyright (C) 2018 Koichi Sasada
+
+**********************************************************************/
+
+#ifndef RUBY_TRANSIENT_HEAP_H
+#define RUBY_TRANSIENT_HEAP_H
+
+#include "internal.h"
+
+#if USE_TRANSIENT_HEAP
+
+/* public API */
+
+/* Allocate req_size bytes from transient_heap.
+ Allocated memories are free-ed when next GC
+ if this memory is not marked by `rb_transient_heap_mark()`.
+ */
+void *rb_transient_heap_alloc(VALUE obj, size_t req_size);
+
+/* If `obj` uses a memory pointed by `ptr` from transient_heap,
+ you need to call `rb_transient_heap_mark(obj, ptr)`
+ to assert liveness of `obj` (and ptr). */
+void rb_transient_heap_mark(VALUE obj, const void *ptr);
+
+/* used by gc.c */
+void rb_transient_heap_promote(VALUE obj);
+void rb_transient_heap_start_marking(int full_marking);
+void rb_transient_heap_finish_marking(void);
+
+/* for debug API */
+void rb_transient_heap_dump(void);
+void rb_transient_heap_verify(void);
+int rb_transient_heap_managed_ptr_p(const void *ptr);
+
+/* evacuate functions for each type */
+void rb_ary_transient_heap_evacuate(VALUE ary, int promote);
+void rb_obj_transient_heap_evacuate(VALUE obj, int promote);
+void rb_hash_transient_heap_evacuate(VALUE hash, int promote);
+void rb_struct_transient_heap_evacuate(VALUE st, int promote);
+
+#else /* USE_TRANSIENT_HEAP */
+
+#define rb_transient_heap_alloc(o, s) NULL
+#define rb_transient_heap_verify() ((void)0)
+#define rb_transient_heap_promote(obj) ((void)0)
+#define rb_transient_heap_start_marking(full_marking) ((void)0)
+#define rb_transient_heap_finish_marking() ((void)0)
+#define rb_transient_heap_mark(obj, ptr) ((void)0)
+
+#define rb_ary_transient_heap_evacuate(x, y) ((void)0)
+#define rb_obj_transient_heap_evacuate(x, y) ((void)0)
+#define rb_hash_transient_heap_evacuate(x, y) ((void)0)
+#define rb_struct_transient_heap_evacuate(x, y) ((void)0)
+
+#endif /* USE_TRANSIENT_HEAP */
+#endif
diff --git a/util.c b/util.c
index b118bdf21a..814404fdf1 100644
--- a/util.c
+++ b/util.c
@@ -9,6 +9,10 @@
**********************************************************************/
+#if defined __MINGW32__ || defined __MINGW64__
+#define MINGW_HAS_SECURE_API 1
+#endif
+
#include "internal.h"
#include <ctype.h>
@@ -31,8 +35,12 @@ ruby_scan_oct(const char *start, size_t len, size_t *retlen)
{
register const char *s = start;
register unsigned long retval = 0;
+ size_t i;
- while (len-- && *s >= '0' && *s <= '7') {
+ for (i = 0; i < len; i++) {
+ if ((s[0] < '0') || ('7' < s[0])) {
+ break;
+ }
retval <<= 3;
retval |= *s++ - '0';
}
@@ -46,8 +54,16 @@ ruby_scan_hex(const char *start, size_t len, size_t *retlen)
register const char *s = start;
register unsigned long retval = 0;
const char *tmp;
+ size_t i = 0;
- while (len-- && *s && (tmp = strchr(hexdigit, *s))) {
+ for (i = 0; i < len; i++) {
+ if (! s[0]) {
+ break;
+ }
+ tmp = strchr(hexdigit, *s);
+ if (! tmp) {
+ break;
+ }
retval <<= 4;
retval |= (tmp - hexdigit) & 15;
s++;
@@ -76,6 +92,7 @@ const signed char ruby_digit36_to_number_table[] = {
/*f*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
};
+NO_SANITIZE("unsigned-integer-overflow", extern unsigned long ruby_scan_digits(const char *str, ssize_t len, int base, size_t *retlen, int *overflow));
unsigned long
ruby_scan_digits(const char *str, ssize_t len, int base, size_t *retlen, int *overflow)
{
@@ -191,14 +208,18 @@ ruby_strtoul(const char *str, char **endptr, int base)
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
-#if !defined HAVE_BSD_QSORT_R && defined HAVE_QSORT_S
+typedef int (cmpfunc_t)(const void*, const void*, void*);
+
+#if defined HAVE_QSORT_S && defined RUBY_MSVCRT_VERSION
+/* In contrast to its name, Visual Studio qsort_s is incompatible with
+ * C11 in the order of the comparison function's arguments, and same
+ * as BSD qsort_r rather. */
# define qsort_r(base, nel, size, arg, cmp) qsort_s(base, nel, size, cmp, arg)
# define cmp_bsd_qsort cmp_ms_qsort
# define HAVE_BSD_QSORT_R 1
#endif
-#if defined HAVE_BSD_QSORT_R
-typedef int (cmpfunc_t)(const void*, const void*, void*);
+#if defined HAVE_BSD_QSORT_R
struct bsd_qsort_r_args {
cmpfunc_t *cmp;
void *arg;
@@ -219,6 +240,21 @@ ruby_qsort(void* base, const size_t nel, const size_t size, cmpfunc_t *cmp, void
args.arg = d;
qsort_r(base, nel, size, &args, cmp_bsd_qsort);
}
+#elif defined HAVE_QSORT_S
+/* C11 qsort_s has the same arguments as GNU's, but uses
+ * runtime-constraints handler. */
+void
+ruby_qsort(void* base, const size_t nel, const size_t size, cmpfunc_t *cmp, void *d)
+{
+ if (!nel || !size) return; /* nothing to sort */
+
+ /* get rid of runtime-constraints handler for MT-safeness */
+ if (!base || !cmp) return;
+ if (nel > RSIZE_MAX || size > RSIZE_MAX) return;
+
+ qsort_s(base, nel, size, cmp, d);
+}
+# define HAVE_GNU_QSORT_R 1
#elif !defined HAVE_GNU_QSORT_R
/* mm.c */
@@ -335,7 +371,6 @@ typedef struct { char *LL, *RR; } stack_node; /* Stack structure for L,l,R,r */
((*cmp)((b),(c),d)<0 ? (b) : ((*cmp)((a),(c),d)<0 ? (c) : (a))) : \
((*cmp)((b),(c),d)>0 ? (b) : ((*cmp)((a),(c),d)<0 ? (a) : (c))))
-typedef int (cmpfunc_t)(const void*, const void*, void*);
void
ruby_qsort(void* base, const size_t nel, const size_t size, cmpfunc_t *cmp, void *d)
{
@@ -503,11 +538,11 @@ ruby_strdup(const char *str)
char *
ruby_getcwd(void)
{
-#if defined __native_client__
- char *buf = xmalloc(2);
- strcpy(buf, ".");
-#elif defined HAVE_GETCWD
+#if defined HAVE_GETCWD
+# undef RUBY_UNTYPED_DATA_WARNING
+# define RUBY_UNTYPED_DATA_WARNING 0
# if defined NO_GETCWD_MALLOC
+ VALUE guard = Data_Wrap_Struct((VALUE)0, NULL, RUBY_DEFAULT_FREE, NULL);
int size = 200;
char *buf = xmalloc(size);
@@ -515,17 +550,22 @@ ruby_getcwd(void)
int e = errno;
if (e != ERANGE) {
xfree(buf);
+ DATA_PTR(guard) = NULL;
rb_syserr_fail(e, "getcwd");
}
size *= 2;
+ DATA_PTR(guard) = buf;
buf = xrealloc(buf, size);
}
# else
+ VALUE guard = Data_Wrap_Struct((VALUE)0, NULL, free, NULL);
char *buf, *cwd = getcwd(NULL, 0);
+ DATA_PTR(guard) = cwd;
if (!cwd) rb_sys_fail("getcwd");
buf = ruby_strdup(cwd); /* allocate by xmalloc */
free(cwd);
# endif
+ DATA_PTR(RB_GC_GUARD(guard)) = NULL;
#else
# ifndef PATH_MAX
# define PATH_MAX 8192
@@ -736,6 +776,8 @@ ruby_getcwd(void)
#if HAVE_LONG_LONG
#define Llong LONG_LONG
+#else
+#define NO_LONG_LONG
#endif
#ifdef DEBUG
@@ -1313,7 +1355,7 @@ mult(Bigint *a, Bigint *b)
#else
#ifdef Pack_32
for (; xb < xbe; xb++, xc0++) {
- if (y = *xb & 0xffff) {
+ if ((y = *xb & 0xffff) != 0) {
x = xa;
xc = xc0;
carry = 0;
@@ -1326,7 +1368,7 @@ mult(Bigint *a, Bigint *b)
} while (x < xae);
*xc = (ULong)carry;
}
- if (y = *xb >> 16) {
+ if ((y = *xb >> 16) != 0) {
x = xa;
xc = xc0;
carry = 0;
@@ -1499,6 +1541,7 @@ cmp(Bigint *a, Bigint *b)
return 0;
}
+NO_SANITIZE("unsigned-integer-overflow", static Bigint * diff(Bigint *a, Bigint *b));
static Bigint *
diff(Bigint *a, Bigint *b)
{
@@ -1978,6 +2021,7 @@ hexnan(double *rvp, const char **sp)
#endif /*No_Hex_NaN*/
#endif /* INFNAN_CHECK */
+NO_SANITIZE("unsigned-integer-overflow", double ruby_strtod(const char *s00, char **se));
double
ruby_strtod(const char *s00, char **se)
{
@@ -2037,6 +2081,7 @@ break2:
if (!*++s || !(s1 = strchr(hexdigit, *s))) goto ret0;
if (*s == '0') {
while (*++s == '0');
+ if (!*s) goto ret;
s1 = strchr(hexdigit, *s);
}
if (s1 != NULL) {
@@ -2059,7 +2104,7 @@ break2:
for (; *s && (s1 = strchr(hexdigit, *s)); ++s) {
adj += aadj * ((s1 - hexdigit) & 15);
if ((aadj /= 16) == 0.0) {
- while (strchr(hexdigit, *++s));
+ while (*++s && strchr(hexdigit, *s));
break;
}
}
@@ -2105,7 +2150,7 @@ break2:
for (nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++)
if (nd < 9)
y = 10*y + c - '0';
- else if (nd < 16)
+ else if (nd < DBL_DIG + 2)
z = 10*z + c - '0';
nd0 = nd;
#ifdef USE_LOCALE
@@ -2145,17 +2190,19 @@ break2:
for (; c >= '0' && c <= '9'; c = *++s) {
have_dig:
nz++;
- if (nd > DBL_DIG * 4) continue;
+ if (nd > DBL_DIG * 4) {
+ continue;
+ }
if (c -= '0') {
nf += nz;
for (i = 1; i < nz; i++)
if (nd++ < 9)
y *= 10;
- else if (nd <= DBL_DIG + 1)
+ else if (nd <= DBL_DIG + 2)
z *= 10;
if (nd++ < 9)
y = 10*y + c;
- else if (nd <= DBL_DIG + 1)
+ else if (nd <= DBL_DIG + 2)
z = 10*z + c;
nz = 0;
}
@@ -2243,7 +2290,7 @@ ret0:
if (!nd0)
nd0 = nd;
- k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1;
+ k = nd < DBL_DIG + 2 ? nd : DBL_DIG + 2;
dval(rv) = y;
if (k > 9) {
#ifdef SET_INEXACT
@@ -2935,6 +2982,7 @@ ret:
return sign ? -dval(rv) : dval(rv);
}
+NO_SANITIZE("unsigned-integer-overflow", static int quorem(Bigint *b, Bigint *S));
static int
quorem(Bigint *b, Bigint *S)
{
diff --git a/variable.c b/variable.c
index e9245050d3..c3100f6e19 100644
--- a/variable.c
+++ b/variable.c
@@ -11,17 +11,22 @@
**********************************************************************/
-#include "internal.h"
+#include "ruby/encoding.h"
#include "ruby/st.h"
#include "ruby/util.h"
+#include "internal.h"
#include "id_table.h"
#include "constant.h"
#include "id.h"
#include "ccan/list/list.h"
#include "id_table.h"
+#include "debug_counter.h"
+#include "vm_core.h"
+#include "transient_heap.h"
-struct rb_id_table *rb_global_tbl;
+static struct rb_id_table *rb_global_tbl;
static ID autoload, classpath, tmp_classpath, classid;
+static VALUE autoload_featuremap; /* feature => autoload_i */
static void check_before_mod_set(VALUE, ID, VALUE, const char *);
static void setup_const_entry(rb_const_entry_t *, VALUE, VALUE, rb_const_flag_t);
@@ -32,7 +37,7 @@ static st_table *generic_iv_tbl_compat;
/* per-object */
struct gen_ivtbl {
uint32_t numiv;
- VALUE ivptr[1]; /* flexible array */
+ VALUE ivptr[FLEX_ARY_LEN];
};
struct ivar_update {
@@ -317,10 +322,11 @@ rb_class_path_no_cache(VALUE klass)
VALUE
rb_class_path_cached(VALUE klass)
{
- st_table *ivtbl = RCLASS_IV_TBL(klass);
+ st_table *ivtbl;
st_data_t n;
- if (!ivtbl) return Qnil;
+ if (!RCLASS_EXT(klass)) return Qnil;
+ if (!(ivtbl = RCLASS_IV_TBL(klass))) return Qnil;
if (st_lookup(ivtbl, (st_data_t)classpath, &n)) return (VALUE)n;
if (st_lookup(ivtbl, (st_data_t)tmp_classpath, &n)) return (VALUE)n;
return Qnil;
@@ -477,13 +483,25 @@ struct rb_global_variable {
struct trace_var *trace;
};
-struct rb_global_entry*
-rb_global_entry(ID id)
+static struct rb_global_entry*
+rb_find_global_entry(ID id)
{
struct rb_global_entry *entry;
VALUE data;
if (!rb_id_table_lookup(rb_global_tbl, id, &data)) {
+ return NULL;
+ }
+ entry = (struct rb_global_entry *)data;
+ ASSUME(entry != NULL);
+ return entry;
+}
+
+MJIT_FUNC_EXPORTED struct rb_global_entry*
+rb_global_entry(ID id)
+{
+ struct rb_global_entry *entry = rb_find_global_entry(id);
+ if (!entry) {
struct rb_global_variable *var;
entry = ALLOC(struct rb_global_entry);
var = ALLOC(struct rb_global_variable);
@@ -499,9 +517,6 @@ rb_global_entry(ID id)
var->trace = 0;
rb_id_table_insert(rb_global_tbl, id, (VALUE)entry);
}
- else {
- entry = (struct rb_global_entry *)data;
- }
return entry;
}
@@ -604,14 +619,37 @@ global_id(const char *name)
if (name[0] == '$') id = rb_intern(name);
else {
size_t len = strlen(name);
- char *buf = ALLOCA_N(char, len+1);
+ VALUE vbuf = 0;
+ char *buf = ALLOCV_N(char, vbuf, len+1);
buf[0] = '$';
memcpy(buf+1, name, len);
id = rb_intern2(buf, len+1);
+ ALLOCV_END(vbuf);
}
return id;
}
+static ID
+find_global_id(const char *name)
+{
+ ID id;
+ size_t len = strlen(name);
+
+ if (name[0] == '$') {
+ id = rb_check_id_cstr(name, len, NULL);
+ }
+ else {
+ VALUE vbuf = 0;
+ char *buf = ALLOCV_N(char, vbuf, len+1);
+ buf[0] = '$';
+ memcpy(buf+1, name, len);
+ id = rb_check_id_cstr(buf, len+1, NULL);
+ ALLOCV_END(vbuf);
+ }
+
+ return id;
+}
+
void
rb_define_hooked_variable(
const char *name,
@@ -787,7 +825,7 @@ rb_f_untrace_var(int argc, const VALUE *argv)
return Qnil;
}
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_gvar_get(struct rb_global_entry *entry)
{
struct rb_global_variable *var = entry->var;
@@ -820,7 +858,7 @@ trace_en(struct rb_global_variable *var)
return Qnil; /* not reached */
}
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_gvar_set(struct rb_global_entry *entry, VALUE val)
{
struct trace_data trace;
@@ -850,18 +888,42 @@ VALUE
rb_gv_get(const char *name)
{
struct rb_global_entry *entry;
+ ID id = find_global_id(name);
- entry = rb_global_entry(global_id(name));
+ if (!id) {
+ rb_warning("global variable `%s' not initialized", name);
+ return Qnil;
+ }
+
+ entry = rb_global_entry(id);
return rb_gvar_get(entry);
}
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_gvar_defined(struct rb_global_entry *entry)
{
if (entry->var->getter == rb_gvar_undef_getter) return Qfalse;
return Qtrue;
}
+rb_gvar_getter_t *
+rb_gvar_getter_function_of(const struct rb_global_entry *entry)
+{
+ return entry->var->getter;
+}
+
+rb_gvar_setter_t *
+rb_gvar_setter_function_of(const struct rb_global_entry *entry)
+{
+ return entry->var->setter;
+}
+
+bool
+rb_gvar_is_traced(const struct rb_global_entry *entry)
+{
+ return !!entry->var->trace;
+}
+
static enum rb_id_table_iterator_result
gvar_i(ID key, VALUE val, void *a)
{
@@ -942,25 +1004,6 @@ rb_alias_variable(ID name1, ID name2)
entry1->var = entry2->var;
}
-struct gen_ivar_compat_tbl {
- struct gen_ivtbl *ivtbl;
- st_table *tbl;
-};
-
-static int
-gen_ivar_compat_tbl_i(st_data_t id, st_data_t index, st_data_t arg)
-{
- struct gen_ivar_compat_tbl *a = (struct gen_ivar_compat_tbl *)arg;
-
- if (index < a->ivtbl->numiv) {
- VALUE val = a->ivtbl->ivptr[index];
- if (val != Qundef) {
- st_add_direct(a->tbl, id, (st_data_t)val);
- }
- }
- return ST_CONTINUE;
-}
-
static int
gen_ivtbl_get(VALUE obj, struct gen_ivtbl **ivtbl)
{
@@ -973,38 +1016,6 @@ gen_ivtbl_get(VALUE obj, struct gen_ivtbl **ivtbl)
return 0;
}
-/* for backwards compatibility only */
-st_table*
-rb_generic_ivar_table(VALUE obj)
-{
- st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj));
- struct gen_ivar_compat_tbl a;
- st_data_t d;
-
- if (!iv_index_tbl) return 0;
- if (!FL_TEST(obj, FL_EXIVAR)) return 0;
- if (!gen_ivtbl_get(obj, &a.ivtbl)) return 0;
-
- a.tbl = 0;
- if (!generic_iv_tbl_compat) {
- generic_iv_tbl_compat = st_init_numtable();
- }
- else {
- if (st_lookup(generic_iv_tbl_compat, (st_data_t)obj, &d)) {
- a.tbl = (st_table *)d;
- st_clear(a.tbl);
- }
- }
- if (!a.tbl) {
- a.tbl = st_init_numtable();
- d = (st_data_t)a.tbl;
- st_add_direct(generic_iv_tbl_compat, (st_data_t)obj, d);
- }
- st_foreach_safe(iv_index_tbl, gen_ivar_compat_tbl_i, (st_data_t)&a);
-
- return a.tbl;
-}
-
static VALUE
generic_ivar_delete(VALUE obj, ID id, VALUE undef)
{
@@ -1049,7 +1060,7 @@ generic_ivar_get(VALUE obj, ID id, VALUE undef)
static size_t
gen_ivtbl_bytes(size_t n)
{
- return sizeof(struct gen_ivtbl) + n * sizeof(VALUE) - sizeof(VALUE);
+ return offsetof(struct gen_ivtbl, ivptr) + n * sizeof(VALUE);
}
static struct gen_ivtbl *
@@ -1260,6 +1271,7 @@ VALUE
rb_ivar_get(VALUE obj, ID id)
{
VALUE iv = rb_ivar_lookup(obj, id, Qundef);
+ RB_DEBUG_COUNTER_INC(ivar_get_base);
if (iv == Qundef) {
if (RTEST(ruby_verbose))
@@ -1360,59 +1372,133 @@ generic_ivar_set(VALUE obj, ID id, VALUE val)
RB_OBJ_WRITTEN(obj, Qundef, val);
}
-VALUE
-rb_ivar_set(VALUE obj, ID id, VALUE val)
+static VALUE *
+obj_ivar_heap_alloc(VALUE obj, size_t newsize)
+{
+ VALUE *newptr = rb_transient_heap_alloc(obj, sizeof(VALUE) * newsize);
+
+ if (newptr != NULL) {
+ ROBJ_TRANSIENT_SET(obj);
+ }
+ else {
+ ROBJ_TRANSIENT_UNSET(obj);
+ newptr = ALLOC_N(VALUE, newsize);
+ }
+ return newptr;
+}
+
+static VALUE *
+obj_ivar_heap_realloc(VALUE obj, int32_t len, size_t newsize)
+{
+ VALUE *newptr;
+ int i;
+
+ if (ROBJ_TRANSIENT_P(obj)) {
+ const VALUE *orig_ptr = ROBJECT(obj)->as.heap.ivptr;
+ if ((newptr = obj_ivar_heap_alloc(obj, newsize)) != NULL) {
+ /* ok */
+ }
+ else {
+ newptr = ALLOC_N(VALUE, newsize);
+ ROBJ_TRANSIENT_UNSET(obj);
+ }
+ ROBJECT(obj)->as.heap.ivptr = newptr;
+ for (i=0; i<(int)len; i++) {
+ newptr[i] = orig_ptr[i];
+ }
+ }
+ else {
+ REALLOC_N(ROBJECT(obj)->as.heap.ivptr, VALUE, newsize);
+ newptr = ROBJECT(obj)->as.heap.ivptr;
+ }
+
+ return newptr;
+}
+
+#if USE_TRANSIENT_HEAP
+void
+rb_obj_transient_heap_evacuate(VALUE obj, int promote)
+{
+ if (ROBJ_TRANSIENT_P(obj)) {
+ uint32_t len = ROBJECT_NUMIV(obj);
+ const VALUE *old_ptr = ROBJECT_IVPTR(obj);
+ VALUE *new_ptr;
+
+ if (promote) {
+ new_ptr = ALLOC_N(VALUE, len);
+ ROBJ_TRANSIENT_UNSET(obj);
+ }
+ else {
+ new_ptr = obj_ivar_heap_alloc(obj, len);
+ }
+ MEMCPY(new_ptr, old_ptr, VALUE, len);
+ ROBJECT(obj)->as.heap.ivptr = new_ptr;
+ }
+}
+#endif
+
+static VALUE
+obj_ivar_set(VALUE obj, ID id, VALUE val)
{
struct ivar_update ivup;
uint32_t i, len;
- rb_check_frozen(obj);
-
- switch (BUILTIN_TYPE(obj)) {
- case T_OBJECT:
- ivup.iv_extended = 0;
- ivup.u.iv_index_tbl = iv_index_tbl_make(obj);
- iv_index_tbl_extend(&ivup, id);
- len = ROBJECT_NUMIV(obj);
- if (len <= ivup.index) {
- VALUE *ptr = ROBJECT_IVPTR(obj);
- if (ivup.index < ROBJECT_EMBED_LEN_MAX) {
- RBASIC(obj)->flags |= ROBJECT_EMBED;
- ptr = ROBJECT(obj)->as.ary;
- for (i = 0; i < ROBJECT_EMBED_LEN_MAX; i++) {
- ptr[i] = Qundef;
- }
+ ivup.iv_extended = 0;
+ ivup.u.iv_index_tbl = iv_index_tbl_make(obj);
+ iv_index_tbl_extend(&ivup, id);
+ len = ROBJECT_NUMIV(obj);
+ if (len <= ivup.index) {
+ VALUE *ptr = ROBJECT_IVPTR(obj);
+ if (ivup.index < ROBJECT_EMBED_LEN_MAX) {
+ RBASIC(obj)->flags |= ROBJECT_EMBED;
+ ptr = ROBJECT(obj)->as.ary;
+ for (i = 0; i < ROBJECT_EMBED_LEN_MAX; i++) {
+ ptr[i] = Qundef;
+ }
+ }
+ else {
+ VALUE *newptr;
+ uint32_t newsize = iv_index_tbl_newsize(&ivup);
+
+ if (RBASIC(obj)->flags & ROBJECT_EMBED) {
+ newptr = obj_ivar_heap_alloc(obj, newsize);
+ MEMCPY(newptr, ptr, VALUE, len);
+ RBASIC(obj)->flags &= ~ROBJECT_EMBED;
+ ROBJECT(obj)->as.heap.ivptr = newptr;
}
else {
- VALUE *newptr;
- uint32_t newsize = iv_index_tbl_newsize(&ivup);
-
- if (RBASIC(obj)->flags & ROBJECT_EMBED) {
- newptr = ALLOC_N(VALUE, newsize);
- MEMCPY(newptr, ptr, VALUE, len);
- RBASIC(obj)->flags &= ~ROBJECT_EMBED;
- ROBJECT(obj)->as.heap.ivptr = newptr;
- }
- else {
- REALLOC_N(ROBJECT(obj)->as.heap.ivptr, VALUE, newsize);
- newptr = ROBJECT(obj)->as.heap.ivptr;
- }
- for (; len < newsize; len++)
- newptr[len] = Qundef;
- ROBJECT(obj)->as.heap.numiv = newsize;
- ROBJECT(obj)->as.heap.iv_index_tbl = ivup.u.iv_index_tbl;
+ newptr = obj_ivar_heap_realloc(obj, len, newsize);
+ }
+ for (; len < newsize; len++) {
+ newptr[len] = Qundef;
}
+ ROBJECT(obj)->as.heap.numiv = newsize;
+ ROBJECT(obj)->as.heap.iv_index_tbl = ivup.u.iv_index_tbl;
}
- RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[ivup.index], val);
- break;
+ }
+ RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[ivup.index], val);
+
+ return val;
+}
+
+VALUE
+rb_ivar_set(VALUE obj, ID id, VALUE val)
+{
+ RB_DEBUG_COUNTER_INC(ivar_set_base);
+
+ rb_check_frozen(obj);
+
+ switch (BUILTIN_TYPE(obj)) {
+ case T_OBJECT:
+ return obj_ivar_set(obj, id, val);
case T_CLASS:
case T_MODULE:
- if (!RCLASS_IV_TBL(obj)) RCLASS_IV_TBL(obj) = st_init_numtable();
- rb_class_ivar_set(obj, id, val);
+ if (!RCLASS_IV_TBL(obj)) RCLASS_IV_TBL(obj) = st_init_numtable();
+ rb_class_ivar_set(obj, id, val);
break;
default:
- generic_ivar_set(obj, id, val);
- break;
+ generic_ivar_set(obj, id, val);
+ break;
}
return val;
}
@@ -1619,7 +1705,7 @@ rb_ivar_count(VALUE obj)
switch (BUILTIN_TYPE(obj)) {
case T_OBJECT:
if ((tbl = ROBJECT_IV_INDEX_TBL(obj)) != 0) {
- st_index_t i, count, num = tbl->num_entries;
+ st_index_t i, count, num = ROBJECT_NUMIV(obj);
const VALUE *const ivptr = ROBJECT_IVPTR(obj);
for (i = count = 0; i < num; ++i) {
if (ivptr[i] != Qundef) {
@@ -1711,9 +1797,11 @@ check_id_type(VALUE obj, VALUE *pname,
/*
* call-seq:
* obj.remove_instance_variable(symbol) -> obj
+ * obj.remove_instance_variable(string) -> obj
*
* Removes the named instance variable from <i>obj</i>, returning that
* variable's value.
+ * String arguments are converted to symbols.
*
* class Dummy
* attr_reader :var
@@ -1775,7 +1863,7 @@ rb_obj_remove_instance_variable(VALUE obj, VALUE name)
not_defined:
rb_name_err_raise("instance variable %1$s not defined",
obj, name);
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
NORETURN(static void uninitialized_constant(VALUE, VALUE));
@@ -1793,7 +1881,7 @@ uninitialized_constant(VALUE klass, VALUE name)
VALUE
rb_const_missing(VALUE klass, VALUE name)
{
- VALUE value = rb_funcallv(klass, rb_intern("const_missing"), 1, &name);
+ VALUE value = rb_funcallv(klass, idConst_missing, 1, &name);
rb_vm_inc_const_missing_count();
return value;
}
@@ -1838,10 +1926,15 @@ rb_const_missing(VALUE klass, VALUE name)
VALUE
rb_mod_const_missing(VALUE klass, VALUE name)
{
+ VALUE ref = GET_EC()->private_const_reference;
rb_vm_pop_cfunc_frame();
+ if (ref) {
+ rb_name_err_raise("private constant %2$s::%1$s referenced",
+ ref, name);
+ }
uninitialized_constant(klass, name);
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
static void
@@ -1886,32 +1979,55 @@ autoload_data(VALUE mod, ID id)
return (VALUE)val;
}
+struct autoload_const {
+ struct list_node cnode; /* <=> autoload_data_i.constants */
+ VALUE mod;
+ VALUE ad; /* autoload_data_i */
+ VALUE value;
+ VALUE file;
+ ID id;
+ int safe_level;
+ rb_const_flag_t flag;
+ int line;
+};
+
/* always on stack, no need to mark */
struct autoload_state {
- struct autoload_data_i *ele;
- VALUE mod;
+ struct autoload_const *ac;
VALUE result;
- ID id;
VALUE thread;
- union {
- struct list_node node;
- struct list_head head;
- } waitq;
+ struct list_node waitq;
};
struct autoload_data_i {
VALUE feature;
- int safe_level;
- VALUE value;
struct autoload_state *state; /* points to on-stack struct */
+ rb_serial_t fork_gen;
+ struct list_head constants; /* <=> autoload_const.cnode */
};
static void
autoload_i_mark(void *ptr)
{
struct autoload_data_i *p = ptr;
+
rb_gc_mark(p->feature);
- rb_gc_mark(p->value);
+
+ /* allow GC to free us if no modules refer to this via autoload_const.ad */
+ if (list_empty(&p->constants)) {
+ rb_hash_delete(autoload_featuremap, p->feature);
+ }
+}
+
+static void
+autoload_i_free(void *ptr)
+{
+ struct autoload_data_i *p = ptr;
+
+ /* we may leak some memory at VM shutdown time, no big deal */
+ if (list_empty(&p->constants)) {
+ xfree(p);
+ }
}
static size_t
@@ -1922,14 +2038,58 @@ autoload_i_memsize(const void *ptr)
static const rb_data_type_t autoload_data_i_type = {
"autoload_i",
- {autoload_i_mark, RUBY_TYPED_DEFAULT_FREE, autoload_i_memsize,},
+ {autoload_i_mark, autoload_i_free, autoload_i_memsize,},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
-#define check_autoload_data(av) \
- (struct autoload_data_i *)rb_check_typeddata((av), &autoload_data_i_type)
+static void
+autoload_c_mark(void *ptr)
+{
+ struct autoload_const *ac = ptr;
-void
+ rb_gc_mark(ac->mod);
+ rb_gc_mark(ac->ad);
+ rb_gc_mark(ac->value);
+ rb_gc_mark(ac->file);
+}
+
+static void
+autoload_c_free(void *ptr)
+{
+ struct autoload_const *ac = ptr;
+ list_del(&ac->cnode);
+ xfree(ac);
+}
+
+static size_t
+autoload_c_memsize(const void *ptr)
+{
+ return sizeof(struct autoload_const);
+}
+
+static const rb_data_type_t autoload_const_type = {
+ "autoload_const",
+ {autoload_c_mark, autoload_c_free, autoload_c_memsize,},
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+};
+
+static struct autoload_data_i *
+get_autoload_data(VALUE acv, struct autoload_const **acp)
+{
+ struct autoload_const *ac = rb_check_typeddata(acv, &autoload_const_type);
+ struct autoload_data_i *ele;
+
+ ele = rb_check_typeddata(ac->ad, &autoload_data_i_type);
+ /* do not reach across stack for ->state after forking: */
+ if (ele && ele->state && ele->fork_gen != GET_VM()->fork_gen) {
+ ele->state = 0;
+ ele->fork_gen = 0;
+ }
+ if (acp) *acp = ac;
+ return ele;
+}
+
+RUBY_FUNC_EXPORTED void
rb_autoload(VALUE mod, ID id, const char *file)
{
if (!file || !*file) {
@@ -1975,16 +2135,42 @@ rb_autoload_str(VALUE mod, ID id, VALUE file)
DATA_PTR(av) = tbl = st_init_numtable();
}
- ad = TypedData_Make_Struct(0, struct autoload_data_i, &autoload_data_i_type, ele);
if (OBJ_TAINTED(file)) {
file = rb_str_dup(file);
FL_UNSET(file, FL_TAINT);
}
- ele->feature = rb_fstring(file);
- ele->safe_level = rb_safe_level();
- ele->value = Qundef;
- ele->state = 0;
- st_insert(tbl, (st_data_t)id, (st_data_t)ad);
+ file = rb_fstring(file);
+ if (!autoload_featuremap) {
+ autoload_featuremap = rb_hash_new_compare_by_id();
+ rb_obj_hide(autoload_featuremap);
+ rb_gc_register_mark_object(autoload_featuremap);
+ }
+ ad = rb_hash_aref(autoload_featuremap, file);
+ if (NIL_P(ad)) {
+ ad = TypedData_Make_Struct(0, struct autoload_data_i,
+ &autoload_data_i_type, ele);
+ ele->feature = file;
+ ele->state = 0;
+ list_head_init(&ele->constants);
+ rb_hash_aset(autoload_featuremap, file, ad);
+ }
+ else {
+ ele = rb_check_typeddata(ad, &autoload_data_i_type);
+ }
+ {
+ VALUE acv;
+ struct autoload_const *ac;
+ acv = TypedData_Make_Struct(0, struct autoload_const,
+ &autoload_const_type, ac);
+ ac->mod = mod;
+ ac->id = id;
+ ac->value = Qundef;
+ ac->safe_level = rb_safe_level();
+ ac->flag = CONST_PUBLIC;
+ ac->ad = ad;
+ list_add_tail(&ele->constants, &ac->cnode);
+ st_insert(tbl, (st_data_t)id, (st_data_t)acv);
+ }
}
static void
@@ -1994,8 +2180,22 @@ autoload_delete(VALUE mod, ID id)
if (st_lookup(RCLASS_IV_TBL(mod), (st_data_t)autoload, &val)) {
struct st_table *tbl = check_autoload_table((VALUE)val);
+ struct autoload_data_i *ele;
+ struct autoload_const *ac;
st_delete(tbl, &n, &load);
+ ele = get_autoload_data((VALUE)load, &ac);
+ VM_ASSERT(ele);
+ if (ele) {
+ VM_ASSERT(!list_empty(&ele->constants));
+ }
+
+ /*
+ * we must delete here to avoid "already initialized" warnings
+ * with parallel autoload. Using list_del_init here so list_del
+ * works in autoload_c_free
+ */
+ list_del_init(&ac->cnode);
if (tbl->num_entries == 0) {
n = autoload;
@@ -2021,12 +2221,13 @@ reset_safe(VALUE safe)
static VALUE
check_autoload_required(VALUE mod, ID id, const char **loadingpath)
{
- VALUE file, load;
+ VALUE file;
+ VALUE load = autoload_data(mod, id);
struct autoload_data_i *ele;
const char *loading;
int safe;
- if (!(load = autoload_data(mod, id)) || !(ele = check_autoload_data(load))) {
+ if (!load || !(ele = get_autoload_data(load, 0))) {
return 0;
}
file = ele->feature;
@@ -2034,6 +2235,17 @@ check_autoload_required(VALUE mod, ID id, const char **loadingpath)
if (!RSTRING_LEN(file) || !*RSTRING_PTR(file)) {
rb_raise(rb_eArgError, "empty file name");
}
+
+ /*
+ * if somebody else is autoloading, we MUST wait for them, since
+ * rb_provide_feature can provide a feature before autoload_const_set
+ * completes. We must wait until autoload_const_set finishes in
+ * the other thread.
+ */
+ if (ele->state && ele->state->thread != rb_thread_current()) {
+ return load;
+ }
+
loading = RSTRING_PTR(file);
safe = rb_safe_level();
rb_set_safe_level_force(0);
@@ -2047,19 +2259,24 @@ check_autoload_required(VALUE mod, ID id, const char **loadingpath)
return 0;
}
-int
-rb_autoloading_value(VALUE mod, ID id, VALUE* value)
+MJIT_FUNC_EXPORTED int
+rb_autoloading_value(VALUE mod, ID id, VALUE* value, rb_const_flag_t *flag)
{
- VALUE load;
+ VALUE load = autoload_data(mod, id);
struct autoload_data_i *ele;
+ struct autoload_const *ac;
- if (!(load = autoload_data(mod, id)) || !(ele = check_autoload_data(load))) {
- return 0;
+ if (!load || !(ele = get_autoload_data(load, &ac))) {
+ return 0;
}
+
if (ele->state && ele->state->thread == rb_thread_current()) {
- if (ele->value != Qundef) {
+ if (ac->value != Qundef) {
if (value) {
- *value = ele->value;
+ *value = ac->value;
+ }
+ if (flag) {
+ *flag = ac->flag;
}
return 1;
}
@@ -2075,25 +2292,19 @@ autoload_defined_p(VALUE mod, ID id)
if (!ce || ce->value != Qundef) {
return 0;
}
- return !rb_autoloading_value(mod, id, NULL);
+ return !rb_autoloading_value(mod, id, NULL, NULL);
}
-struct autoload_const_set_args {
- VALUE mod;
- ID id;
- VALUE value;
-};
-
-static void const_tbl_update(struct autoload_const_set_args *);
+static void const_tbl_update(struct autoload_const *);
static VALUE
autoload_const_set(VALUE arg)
{
- struct autoload_const_set_args* args = (struct autoload_const_set_args *)arg;
- VALUE klass = args->mod;
- ID id = args->id;
- check_before_mod_set(klass, id, args->value, "constant");
- const_tbl_update(args);
+ struct autoload_const *ac = (struct autoload_const *)arg;
+ VALUE klass = ac->mod;
+ ID id = ac->id;
+ check_before_mod_set(klass, id, ac->value, "constant");
+ const_tbl_update(ac);
return 0; /* ignored */
}
@@ -2101,10 +2312,13 @@ static VALUE
autoload_require(VALUE arg)
{
struct autoload_state *state = (struct autoload_state *)arg;
+ struct autoload_const *ac = state->ac;
+ struct autoload_data_i *ele;
+ ele = rb_check_typeddata(ac->ad, &autoload_data_i_type);
/* this may release GVL and switch threads: */
state->result = rb_funcall(rb_vm_top_self(), rb_intern("require"), 1,
- state->ele->feature);
+ ele->feature);
return state->result;
}
@@ -2114,35 +2328,38 @@ autoload_reset(VALUE arg)
{
struct autoload_state *state = (struct autoload_state *)arg;
int need_wakeups = 0;
+ struct autoload_const *ac = state->ac;
+ struct autoload_data_i *ele;
- if (state->ele->state == state) {
- need_wakeups = 1;
- state->ele->state = 0;
+ ele = rb_check_typeddata(ac->ad, &autoload_data_i_type);
+ if (ele->state == state) {
+ need_wakeups = 1;
+ ele->state = 0;
+ ele->fork_gen = 0;
}
/* At the last, move a value defined in autoload to constant table */
- if (RTEST(state->result) && state->ele->value != Qundef) {
- int safe_backup;
- struct autoload_const_set_args args;
-
- args.mod = state->mod;
- args.id = state->id;
- args.value = state->ele->value;
- safe_backup = rb_safe_level();
- rb_set_safe_level_force(state->ele->safe_level);
- rb_ensure(autoload_const_set, (VALUE)&args,
- reset_safe, (VALUE)safe_backup);
+ if (RTEST(state->result)) {
+ struct autoload_const *next;
+ int safe_backup = rb_safe_level();
+
+ list_for_each_safe(&ele->constants, ac, next, cnode) {
+ if (ac->value != Qundef) {
+ rb_ensure(autoload_const_set, (VALUE)ac,
+ reset_safe, (VALUE)safe_backup);
+ }
+ }
}
/* wakeup any waiters we had */
if (need_wakeups) {
struct autoload_state *cur = 0, *nxt;
- list_for_each_safe(&state->waitq.head, cur, nxt, waitq.node) {
+ list_for_each_safe((struct list_head *)&state->waitq, cur, nxt, waitq) {
VALUE th = cur->thread;
cur->thread = Qfalse;
- list_del(&cur->waitq.node);
+ list_del_init(&cur->waitq); /* idempotent */
/*
* cur is stored on the stack of cur->waiting_th,
@@ -2155,12 +2372,41 @@ autoload_reset(VALUE arg)
return 0; /* ignored */
}
+static VALUE
+autoload_sleep(VALUE arg)
+{
+ struct autoload_state *state = (struct autoload_state *)arg;
+
+ /*
+ * autoload_reset in other thread will resume us and remove us
+ * from the waitq list
+ */
+ do {
+ rb_thread_sleep_deadly();
+ } while (state->thread != Qfalse);
+
+ return Qfalse;
+}
+
+static VALUE
+autoload_sleep_done(VALUE arg)
+{
+ struct autoload_state *state = (struct autoload_state *)arg;
+
+ if (state->thread != Qfalse && rb_thread_to_be_killed(state->thread)) {
+ list_del(&state->waitq); /* idempotent after list_del_init */
+ }
+
+ return Qfalse;
+}
+
VALUE
rb_autoload_load(VALUE mod, ID id)
{
VALUE load, result;
const char *loading = 0, *src;
struct autoload_data_i *ele;
+ struct autoload_const *ac;
struct autoload_state state;
if (!autoload_defined_p(mod, id)) return Qfalse;
@@ -2170,35 +2416,29 @@ rb_autoload_load(VALUE mod, ID id)
if (src && loading && strcmp(src, loading) == 0) return Qfalse;
/* set ele->state for a marker of autoloading thread */
- if (!(ele = check_autoload_data(load))) {
+ if (!(ele = get_autoload_data(load, &ac))) {
return Qfalse;
}
-
- state.ele = ele;
- state.mod = mod;
- state.id = id;
+ state.ac = ac;
state.thread = rb_thread_current();
if (!ele->state) {
ele->state = &state;
+ ele->fork_gen = GET_VM()->fork_gen;
/*
* autoload_reset will wake up any threads added to this
* iff the GVL is released during autoload_require
*/
- list_head_init(&state.waitq.head);
+ list_head_init((struct list_head *)&state.waitq);
}
else if (state.thread == ele->state->thread) {
return Qfalse;
}
else {
- list_add_tail(&ele->state->waitq.head, &state.waitq.node);
- /*
- * autoload_reset in other thread will resume us and remove us
- * from the waitq list
- */
- do {
- rb_thread_sleep_deadly();
- } while (state.thread != Qfalse);
+ list_add_tail((struct list_head *)&ele->state->waitq, &state.waitq);
+
+ rb_ensure(autoload_sleep, (VALUE)&state,
+ autoload_sleep_done, (VALUE)&state);
}
/* autoload_data_i can be deleted by another thread while require */
@@ -2221,10 +2461,10 @@ rb_autoload_p(VALUE mod, ID id)
}
load = check_autoload_required(mod, id, 0);
if (!load) return Qnil;
- return (ele = check_autoload_data(load)) ? ele->feature : Qnil;
+ return (ele = get_autoload_data(load, 0)) ? ele->feature : Qnil;
}
-void
+MJIT_FUNC_EXPORTED void
rb_const_warn_if_deprecated(const rb_const_entry_t *ce, VALUE klass, ID id)
{
if (RB_CONST_DEPRECATED_P(ce)) {
@@ -2250,6 +2490,7 @@ static VALUE
rb_const_search(VALUE klass, ID id, int exclude, int recurse, int visibility)
{
VALUE value, tmp, av;
+ rb_const_flag_t flag;
int mod_retry = 0;
tmp = klass;
@@ -2260,21 +2501,21 @@ rb_const_search(VALUE klass, ID id, int exclude, int recurse, int visibility)
while ((ce = rb_const_lookup(tmp, id))) {
if (visibility && RB_CONST_PRIVATE_P(ce)) {
- rb_name_err_raise("private constant %2$s::%1$s referenced",
- tmp, ID2SYM(id));
+ if (BUILTIN_TYPE(tmp) == T_ICLASS) tmp = RBASIC(tmp)->klass;
+ GET_EC()->private_const_reference = tmp;
+ return Qundef;
}
rb_const_warn_if_deprecated(ce, tmp, id);
value = ce->value;
if (value == Qundef) {
if (am == tmp) break;
am = tmp;
- if (rb_autoloading_value(tmp, id, &av)) return av;
+ if (rb_autoloading_value(tmp, id, &av, &flag)) return av;
rb_autoload_load(tmp, id);
continue;
}
if (exclude && tmp == rb_cObject && klass != rb_cObject) {
- rb_warn("toplevel constant %"PRIsVALUE" referenced by %"PRIsVALUE"::%"PRIsVALUE"",
- QUOTE_ID(id), rb_class_name(klass), QUOTE_ID(id));
+ goto not_found;
}
return value;
}
@@ -2287,6 +2528,8 @@ rb_const_search(VALUE klass, ID id, int exclude, int recurse, int visibility)
goto retry;
}
+ not_found:
+ GET_EC()->private_const_reference = 0;
return Qundef;
}
@@ -2308,7 +2551,7 @@ rb_const_get_at(VALUE klass, ID id)
return rb_const_get_0(klass, id, TRUE, FALSE, FALSE);
}
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_public_const_get_from(VALUE klass, ID id)
{
return rb_const_get_0(klass, id, TRUE, TRUE, TRUE);
@@ -2320,7 +2563,7 @@ rb_public_const_get(VALUE klass, ID id)
return rb_const_get_0(klass, id, FALSE, TRUE, TRUE);
}
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_public_const_get_at(VALUE klass, ID id)
{
return rb_const_get_0(klass, id, TRUE, FALSE, TRUE);
@@ -2488,16 +2731,11 @@ rb_const_list(void *data)
VALUE
rb_mod_constants(int argc, const VALUE *argv, VALUE mod)
{
- VALUE inherit;
+ bool inherit = TRUE;
- if (argc == 0) {
- inherit = Qtrue;
- }
- else {
- rb_scan_args(argc, argv, "01", &inherit);
- }
+ if (rb_check_arity(argc, 0, 1)) inherit = RTEST(argv[0]);
- if (RTEST(inherit)) {
+ if (inherit) {
return rb_const_list(rb_mod_const_of(mod, 0));
}
else {
@@ -2520,8 +2758,13 @@ rb_const_defined_0(VALUE klass, ID id, int exclude, int recurse, int visibility)
return (int)Qfalse;
}
if (ce->value == Qundef && !check_autoload_required(tmp, id, 0) &&
- !rb_autoloading_value(tmp, id, 0))
+ !rb_autoloading_value(tmp, id, NULL, NULL))
return (int)Qfalse;
+
+ if (exclude && tmp == rb_cObject && klass != rb_cObject) {
+ return (int)Qfalse;
+ }
+
return (int)Qtrue;
}
if (!recurse) break;
@@ -2553,7 +2796,7 @@ rb_const_defined_at(VALUE klass, ID id)
return rb_const_defined_0(klass, id, TRUE, FALSE, FALSE);
}
-int
+MJIT_FUNC_EXPORTED int
rb_public_const_defined_from(VALUE klass, ID id)
{
return rb_const_defined_0(klass, id, TRUE, TRUE, TRUE);
@@ -2597,49 +2840,89 @@ rb_const_set(VALUE klass, ID id, VALUE val)
setup_const_entry(ce, klass, val, CONST_PUBLIC);
}
else {
- struct autoload_const_set_args args;
- args.mod = klass;
- args.id = id;
- args.value = val;
- const_tbl_update(&args);
+ struct autoload_const ac;
+ memset(&ac, 0, sizeof(ac));
+ ac.mod = klass;
+ ac.id = id;
+ ac.value = val;
+ ac.flag = CONST_PUBLIC;
+ const_tbl_update(&ac);
}
/*
* Resolve and cache class name immediately to resolve ambiguity
* and avoid order-dependency on const_tbl
*/
if (rb_cObject && (RB_TYPE_P(val, T_MODULE) || RB_TYPE_P(val, T_CLASS))) {
- rb_class_name(val);
+ if (NIL_P(rb_class_path_cached(val))) {
+ if (klass == rb_cObject) {
+ rb_ivar_set(val, classpath, rb_id2str(id));
+ rb_name_class(val, id);
+ }
+ else {
+ VALUE path;
+ ID pathid;
+ st_data_t n;
+ st_table *ivtbl = RCLASS_IV_TBL(klass);
+ if (ivtbl &&
+ (st_lookup(ivtbl, (st_data_t)(pathid = classpath), &n) ||
+ st_lookup(ivtbl, (st_data_t)(pathid = tmp_classpath), &n))) {
+ path = rb_str_dup((VALUE)n);
+ rb_str_append(rb_str_cat2(path, "::"), rb_id2str(id));
+ OBJ_FREEZE(path);
+ rb_ivar_set(val, pathid, path);
+ rb_name_class(val, id);
+ }
+ }
+ }
+ }
+}
+
+static struct autoload_data_i *
+current_autoload_data(VALUE mod, ID id, struct autoload_const **acp)
+{
+ struct autoload_data_i *ele;
+ VALUE load = autoload_data(mod, id);
+ if (!load) return 0;
+ ele = get_autoload_data(load, acp);
+ if (!ele) return 0;
+ /* for autoloading thread, keep the defined value to autoloading storage */
+ if (ele->state && (ele->state->thread == rb_thread_current())) {
+ return ele;
}
+ return 0;
}
static void
-const_tbl_update(struct autoload_const_set_args *args)
+const_tbl_update(struct autoload_const *ac)
{
VALUE value;
- VALUE klass = args->mod;
- VALUE val = args->value;
- ID id = args->id;
+ VALUE klass = ac->mod;
+ VALUE val = ac->value;
+ ID id = ac->id;
struct rb_id_table *tbl = RCLASS_CONST_TBL(klass);
- rb_const_flag_t visibility = CONST_PUBLIC;
+ rb_const_flag_t visibility = ac->flag;
rb_const_entry_t *ce;
if (rb_id_table_lookup(tbl, id, &value)) {
ce = (rb_const_entry_t *)value;
if (ce->value == Qundef) {
- VALUE load;
- struct autoload_data_i *ele;
+ struct autoload_data_i *ele = current_autoload_data(klass, id, &ac);
- load = autoload_data(klass, id);
- /* for autoloading thread, keep the defined value to autoloading storage */
- if (load && (ele = check_autoload_data(load)) && ele->state &&
- (ele->state->thread == rb_thread_current())) {
+ if (ele) {
rb_clear_constant_cache();
- ele->value = val; /* autoload_i is non-WB-protected */
- return;
+ ac->value = val; /* autoload_i is non-WB-protected */
+ ac->file = rb_source_location(&ac->line);
}
- /* otherwise, allow to override */
- autoload_delete(klass, id);
+ else {
+ /* otherwise autoloaded constant, allow to override */
+ autoload_delete(klass, id);
+ ce->flag = visibility;
+ RB_OBJ_WRITE(klass, &ce->value, val);
+ RB_OBJ_WRITE(klass, &ce->file, ac->file);
+ ce->line = ac->line;
+ }
+ return;
}
else {
VALUE name = QUOTE_ID(id);
@@ -2656,7 +2939,8 @@ const_tbl_update(struct autoload_const_set_args *args)
}
rb_clear_constant_cache();
setup_const_entry(ce, klass, val, visibility);
- } else {
+ }
+ else {
rb_clear_constant_cache();
ce = ZALLOC(rb_const_entry_t);
@@ -2699,7 +2983,7 @@ set_const_visibility(VALUE mod, int argc, const VALUE *argv,
rb_const_entry_t *ce;
ID id;
- rb_frozen_class_p(mod);
+ rb_class_modify_check(mod);
if (argc == 0) {
rb_warning("%"PRIsVALUE" with no argument is just ignored",
QUOTE_ID(rb_frame_callee()));
@@ -2707,6 +2991,7 @@ set_const_visibility(VALUE mod, int argc, const VALUE *argv,
}
for (i = 0; i < argc; i++) {
+ struct autoload_const *ac;
VALUE val = argv[i];
id = rb_check_id(&val);
if (!id) {
@@ -2720,6 +3005,15 @@ set_const_visibility(VALUE mod, int argc, const VALUE *argv,
if ((ce = rb_const_lookup(mod, id))) {
ce->flag &= ~mask;
ce->flag |= flag;
+ if (ce->value == Qundef) {
+ struct autoload_data_i *ele;
+
+ ele = current_autoload_data(mod, id, &ac);
+ if (ele) {
+ ac->flag &= ~mask;
+ ac->flag |= flag;
+ }
+ }
}
else {
if (i > 0) {
@@ -2739,7 +3033,7 @@ rb_deprecate_constant(VALUE mod, const char *name)
ID id;
long len = strlen(name);
- rb_frozen_class_p(mod);
+ rb_class_modify_check(mod);
if (!(id = rb_check_id_cstr(name, len, NULL)) ||
!(ce = rb_const_lookup(mod, id))) {
rb_name_err_raise("constant %2$s::%1$s not defined",
@@ -2817,6 +3111,23 @@ cvar_front_klass(VALUE klass)
return RCLASS_SUPER(klass);
}
+static void
+cvar_overtaken(VALUE front, VALUE target, ID id)
+{
+ if (front && target != front) {
+ st_data_t did = (st_data_t)id;
+
+ if (RTEST(ruby_verbose)) {
+ rb_warning("class variable % "PRIsVALUE" of %"PRIsVALUE" is overtaken by %"PRIsVALUE"",
+ ID2SYM(id), rb_class_name(original_module(front)),
+ rb_class_name(original_module(target)));
+ }
+ if (BUILTIN_TYPE(front) == T_CLASS) {
+ st_delete(RCLASS_IV_TBL(front), &did, 0);
+ }
+ }
+}
+
#define CVAR_FOREACH_ANCESTORS(klass, v, r) \
for (klass = cvar_front_klass(klass); klass; klass = RCLASS_SUPER(klass)) { \
if (cvar_lookup_at(klass, id, (v))) { \
@@ -2837,18 +3148,7 @@ rb_cvar_set(VALUE klass, ID id, VALUE val)
tmp = klass;
CVAR_LOOKUP(0, {if (!front) front = klass; target = klass;});
if (target) {
- if (front && target != front) {
- st_data_t did = id;
-
- if (RTEST(ruby_verbose)) {
- rb_warning("class variable %"PRIsVALUE" of %"PRIsVALUE" is overtaken by %"PRIsVALUE"",
- QUOTE_ID(id), rb_class_name(original_module(front)),
- rb_class_name(original_module(target)));
- }
- if (BUILTIN_TYPE(front) == T_CLASS) {
- st_delete(RCLASS_IV_TBL(front),&did,0);
- }
- }
+ cvar_overtaken(front, target, id);
}
else {
target = tmp;
@@ -2874,18 +3174,7 @@ rb_cvar_get(VALUE klass, ID id)
rb_name_err_raise("uninitialized class variable %1$s in %2$s",
tmp, ID2SYM(id));
}
- if (front && target != front) {
- st_data_t did = id;
-
- if (RTEST(ruby_verbose)) {
- rb_warning("class variable %"PRIsVALUE" of %"PRIsVALUE" is overtaken by %"PRIsVALUE"",
- QUOTE_ID(id), rb_class_name(original_module(front)),
- rb_class_name(original_module(target)));
- }
- if (BUILTIN_TYPE(front) == T_CLASS) {
- st_delete(RCLASS_IV_TBL(front),&did,0);
- }
- }
+ cvar_overtaken(front, target, id);
return (VALUE)value;
}
@@ -3011,16 +3300,11 @@ cvar_list(void *data)
VALUE
rb_mod_class_variables(int argc, const VALUE *argv, VALUE mod)
{
- VALUE inherit;
+ bool inherit = TRUE;
st_table *tbl;
- if (argc == 0) {
- inherit = Qtrue;
- }
- else {
- rb_scan_args(argc, argv, "01", &inherit);
- }
- if (RTEST(inherit)) {
+ if (rb_check_arity(argc, 0, 1)) inherit = RTEST(argv[0]);
+ if (inherit) {
tbl = mod_cvar_of(mod, 0);
}
else {
@@ -3111,7 +3395,7 @@ rb_st_copy(VALUE obj, struct st_table *orig_tbl)
return new_tbl;
}
-rb_const_entry_t *
+MJIT_FUNC_EXPORTED rb_const_entry_t *
rb_const_lookup(VALUE klass, ID id)
{
struct rb_id_table *tbl = RCLASS_CONST_TBL(klass);
diff --git a/version.c b/version.c
index 4c7b1abb40..3215368e20 100644
--- a/version.c
+++ b/version.c
@@ -11,6 +11,8 @@
#include "ruby/ruby.h"
#include "version.h"
+#include "vm_core.h"
+#include "mjit.h"
#include <stdio.h>
#ifndef EXIT_SUCCESS
@@ -30,7 +32,8 @@ const char ruby_version[] = RUBY_VERSION;
const char ruby_release_date[] = RUBY_RELEASE_DATE;
const char ruby_platform[] = RUBY_PLATFORM;
const int ruby_patchlevel = RUBY_PATCHLEVEL;
-const char ruby_description[] = RUBY_DESCRIPTION;
+const char ruby_description[] = RUBY_DESCRIPTION_WITH("");
+static const char ruby_description_with_jit[] = RUBY_DESCRIPTION_WITH(" +JIT");
const char ruby_copyright[] = RUBY_COPYRIGHT;
const char ruby_engine[] = "ruby";
@@ -64,10 +67,6 @@ Init_version(void)
*/
rb_define_global_const("RUBY_REVISION", MKINT(revision));
/*
- * The full ruby version string, like <tt>ruby -v</tt> prints'
- */
- rb_define_global_const("RUBY_DESCRIPTION", MKSTR(description));
- /*
* The copyright string for ruby
*/
rb_define_global_const("RUBY_COPYRIGHT", MKSTR(copyright));
@@ -82,11 +81,40 @@ Init_version(void)
rb_define_global_const("RUBY_ENGINE_VERSION", (1 ? version : MKSTR(version)));
}
+#if USE_MJIT
+#define MJIT_OPTS_ON mjit_opts.on
+#else
+#define MJIT_OPTS_ON 0
+#endif
+
+void
+Init_ruby_description(void)
+{
+ VALUE description;
+
+ if (MJIT_OPTS_ON) {
+ description = MKSTR(description_with_jit);
+ }
+ else {
+ description = MKSTR(description);
+ }
+
+ /*
+ * The full ruby version string, like <tt>ruby -v</tt> prints
+ */
+ rb_define_global_const("RUBY_DESCRIPTION", /* MKSTR(description) */ description);
+}
+
/*! Prints the version information of the CRuby interpreter to stdout. */
void
ruby_show_version(void)
{
- PRINT(description);
+ if (MJIT_OPTS_ON) {
+ PRINT(description_with_jit);
+ }
+ else {
+ PRINT(description);
+ }
#ifdef RUBY_LAST_COMMIT_TITLE
fputs("last_commit=" RUBY_LAST_COMMIT_TITLE, stdout);
#endif
diff --git a/version.h b/version.h
index 7c3dd4f33f..e60fbb787f 100644
--- a/version.h
+++ b/version.h
@@ -1,10 +1,10 @@
-#define RUBY_VERSION "2.4.0"
-#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
-#define RUBY_PATCHLEVEL -1
+#define RUBY_VERSION "2.6.10"
+#define RUBY_RELEASE_DATE "2022-04-12"
+#define RUBY_PATCHLEVEL 210
-#define RUBY_RELEASE_YEAR 2016
-#define RUBY_RELEASE_MONTH 11
-#define RUBY_RELEASE_DAY 13
+#define RUBY_RELEASE_YEAR 2022
+#define RUBY_RELEASE_MONTH 4
+#define RUBY_RELEASE_DAY 12
#include "ruby/version.h"
@@ -60,11 +60,11 @@
# define RUBY_REVISION_STR ""
#endif
-# define RUBY_DESCRIPTION \
+# define RUBY_DESCRIPTION_WITH(opt) \
"ruby "RUBY_VERSION \
RUBY_PATCHLEVEL_STR \
" ("RUBY_RELEASE_DATE \
- RUBY_REVISION_STR") " \
+ RUBY_REVISION_STR")"opt" " \
"["RUBY_PLATFORM"]"
# define RUBY_COPYRIGHT \
"ruby - Copyright (C) " \
diff --git a/vm.c b/vm.c
index 7cededc1e1..04b8b96f53 100644
--- a/vm.c
+++ b/vm.c
@@ -12,11 +12,18 @@
#include "ruby/vm.h"
#include "ruby/st.h"
+#define vm_exec rb_vm_exec
+
#include "gc.h"
#include "vm_core.h"
+#include "vm_debug.h"
#include "iseq.h"
#include "eval_intern.h"
+#ifndef MJIT_HEADER
#include "probes.h"
+#else
+#include "probes.dmyh"
+#endif
#include "probes_helper.h"
VALUE rb_str_concat_literals(size_t, const VALUE*);
@@ -32,13 +39,13 @@ VM_EP_LEP(const VALUE *ep)
}
static inline const rb_control_frame_t *
-rb_vm_search_cf_from_ep(const rb_thread_t * const th, const rb_control_frame_t *cfp, const VALUE * const ep)
+rb_vm_search_cf_from_ep(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const VALUE * const ep)
{
if (!ep) {
return NULL;
}
else {
- const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(th); /* end of control frame pointer */
+ const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(ec); /* end of control frame pointer */
while (cfp < eocfp) {
if (cfp->ep == ep) {
@@ -86,10 +93,12 @@ rb_vm_frame_block_handler(const rb_control_frame_t *cfp)
#if VM_CHECK_MODE > 0
static int
-VM_CFP_IN_HEAP_P(const rb_thread_t *th, const rb_control_frame_t *cfp)
+VM_CFP_IN_HEAP_P(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
{
- const VALUE *start = th->stack;
- const VALUE *end = (VALUE *)th->stack + th->stack_size;
+ const VALUE *start = ec->vm_stack;
+ const VALUE *end = (VALUE *)ec->vm_stack + ec->vm_stack_size;
+ VM_ASSERT(start != NULL);
+
if (start <= (VALUE *)cfp && (VALUE *)cfp < end) {
return FALSE;
}
@@ -99,10 +108,12 @@ VM_CFP_IN_HEAP_P(const rb_thread_t *th, const rb_control_frame_t *cfp)
}
static int
-VM_EP_IN_HEAP_P(const rb_thread_t *th, const VALUE *ep)
+VM_EP_IN_HEAP_P(const rb_execution_context_t *ec, const VALUE *ep)
{
- const VALUE *start = th->stack;
- const VALUE *end = (VALUE *)th->cfp;
+ const VALUE *start = ec->vm_stack;
+ const VALUE *end = (VALUE *)ec->cfp;
+ VM_ASSERT(start != NULL);
+
if (start <= ep && ep < end) {
return FALSE;
}
@@ -112,9 +123,9 @@ VM_EP_IN_HEAP_P(const rb_thread_t *th, const VALUE *ep)
}
int
-vm_ep_in_heap_p_(const rb_thread_t *th, const VALUE *ep)
+vm_ep_in_heap_p_(const rb_execution_context_t *ec, const VALUE *ep)
{
- if (VM_EP_IN_HEAP_P(th, ep)) {
+ if (VM_EP_IN_HEAP_P(ec, ep)) {
VALUE envval = ep[VM_ENV_DATA_INDEX_ENV]; /* VM_ENV_ENVVAL(ep); */
if (envval != Qundef) {
@@ -134,14 +145,16 @@ vm_ep_in_heap_p_(const rb_thread_t *th, const VALUE *ep)
int
rb_vm_ep_in_heap_p(const VALUE *ep)
{
- return vm_ep_in_heap_p_(GET_THREAD(), ep);
+ const rb_execution_context_t *ec = GET_EC();
+ if (ec->vm_stack == NULL) return TRUE;
+ return vm_ep_in_heap_p_(ec, ep);
}
#endif
static struct rb_captured_block *
VM_CFP_TO_CAPTURED_BLOCK(const rb_control_frame_t *cfp)
{
- VM_ASSERT(!VM_CFP_IN_HEAP_P(GET_THREAD(), cfp));
+ VM_ASSERT(!VM_CFP_IN_HEAP_P(GET_EC(), cfp));
return (struct rb_captured_block *)&cfp->self;
}
@@ -149,8 +162,8 @@ static rb_control_frame_t *
VM_CAPTURED_BLOCK_TO_CFP(const struct rb_captured_block *captured)
{
rb_control_frame_t *cfp = ((rb_control_frame_t *)((VALUE *)(captured) - 3));
- VM_ASSERT(!VM_CFP_IN_HEAP_P(GET_THREAD(), cfp));
- VM_ASSERT(sizeof(rb_control_frame_t)/sizeof(VALUE) == 6 + VM_DEBUG_BP_CHECK ? 1 : 0);
+ VM_ASSERT(!VM_CFP_IN_HEAP_P(GET_EC(), cfp));
+ VM_ASSERT(sizeof(rb_control_frame_t)/sizeof(VALUE) == 7 + VM_DEBUG_BP_CHECK ? 1 : 0);
return cfp;
}
@@ -162,10 +175,11 @@ VM_BH_FROM_CFP_P(VALUE block_handler, const rb_control_frame_t *cfp)
}
static VALUE
-vm_passed_block_handler(rb_thread_t *th)
+vm_passed_block_handler(rb_execution_context_t *ec)
{
- VALUE block_handler = th->passed_block_handler;
- th->passed_block_handler = VM_BLOCK_HANDLER_NONE;
+ VALUE block_handler = ec->passed_block_handler;
+ ec->passed_block_handler = VM_BLOCK_HANDLER_NONE;
+ vm_block_handler_verify(block_handler);
return block_handler;
}
@@ -215,6 +229,12 @@ vm_cref_new_use_prev(VALUE klass, rb_method_visibility_t visi, int module_func,
return vm_cref_new0(klass, visi, module_func, prev_cref, pushed_by_eval, TRUE);
}
+static int
+ref_delete_symkey(VALUE key, VALUE value, VALUE unused)
+{
+ return SYMBOL_P(key) ? ST_DELETE : ST_CONTINUE;
+}
+
static rb_cref_t *
vm_cref_dup(const rb_cref_t *cref)
{
@@ -226,7 +246,9 @@ vm_cref_dup(const rb_cref_t *cref)
new_cref = vm_cref_new(klass, visi->method_visi, visi->module_func, next_cref, pushed_by_eval);
if (!NIL_P(CREF_REFINEMENTS(cref))) {
- CREF_REFINEMENTS_SET(new_cref, rb_hash_dup(CREF_REFINEMENTS(cref)));
+ VALUE ref = rb_hash_dup(CREF_REFINEMENTS(cref));
+ rb_hash_foreach(ref, ref_delete_symkey, Qnil);
+ CREF_REFINEMENTS_SET(new_cref, ref);
CREF_OMOD_SHARED_UNSET(new_cref);
}
@@ -234,12 +256,13 @@ vm_cref_dup(const rb_cref_t *cref)
}
static rb_cref_t *
-vm_cref_new_toplevel(rb_thread_t *th)
+vm_cref_new_toplevel(rb_execution_context_t *ec)
{
rb_cref_t *cref = vm_cref_new(rb_cObject, METHOD_VISI_PRIVATE /* toplevel visibility is private */, FALSE, NULL, FALSE);
+ VALUE top_wrapper = rb_ec_thread_ptr(ec)->top_wrapper;
- if (th->top_wrapper) {
- cref = vm_cref_new(th->top_wrapper, METHOD_VISI_PRIVATE, FALSE, cref, FALSE);
+ if (top_wrapper) {
+ cref = vm_cref_new(top_wrapper, METHOD_VISI_PRIVATE, FALSE, cref, FALSE);
}
return cref;
@@ -248,13 +271,13 @@ vm_cref_new_toplevel(rb_thread_t *th)
rb_cref_t *
rb_vm_cref_new_toplevel(void)
{
- return vm_cref_new_toplevel(GET_THREAD());
+ return vm_cref_new_toplevel(GET_EC());
}
static void
vm_cref_dump(const char *mesg, const rb_cref_t *cref)
{
- fprintf(stderr, "vm_cref_dump: %s (%p)\n", mesg, cref);
+ fprintf(stderr, "vm_cref_dump: %s (%p)\n", mesg, (void *)cref);
while (cref) {
fprintf(stderr, "= cref| klass: %s\n", RSTRING_PTR(rb_class_path(CREF_CLASS(cref))));
@@ -262,12 +285,19 @@ vm_cref_dump(const char *mesg, const rb_cref_t *cref)
}
}
+void
+rb_vm_block_ep_update(VALUE obj, const struct rb_block *dst, const VALUE *ep)
+{
+ *((const VALUE **)&dst->as.captured.ep) = ep;
+ RB_OBJ_WRITTEN(obj, Qundef, VM_ENV_ENVVAL(ep));
+}
+
static void
-vm_bind_update_env(rb_binding_t *bind, VALUE envval)
+vm_bind_update_env(VALUE bindval, rb_binding_t *bind, VALUE envval)
{
const rb_env_t *env = (rb_env_t *)envval;
- bind->block.as.captured.code.iseq = env->iseq;
- bind->block.as.captured.ep = env->ep;
+ RB_OBJ_WRITE(bindval, &bind->block.as.captured.code.iseq, env->iseq);
+ rb_vm_block_ep_update(bindval, &bind->block, env->ep);
}
#if VM_COLLECT_USAGE_DETAILS
@@ -276,31 +306,36 @@ static void vm_collect_usage_insn(int insn);
static void vm_collect_usage_register(int reg, int isset);
#endif
-static VALUE vm_make_env_object(rb_thread_t *th, rb_control_frame_t *cfp);
-
-static VALUE vm_invoke_bmethod(rb_thread_t *th, rb_proc_t *proc, VALUE self,
- int argc, const VALUE *argv, VALUE block_handler);
-static VALUE vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, VALUE self,
- int argc, const VALUE *argv, VALUE block_handler);
+static VALUE vm_make_env_object(const rb_execution_context_t *ec, rb_control_frame_t *cfp);
+extern VALUE rb_vm_invoke_bmethod(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self,
+ int argc, const VALUE *argv, VALUE block_handler,
+ const rb_callable_method_entry_t *me);
+static VALUE vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, VALUE block_handler);
-static rb_serial_t ruby_vm_global_method_state = 1;
-static rb_serial_t ruby_vm_global_constant_state = 1;
-static rb_serial_t ruby_vm_class_serial = 1;
+static VALUE rb_block_param_proxy;
+#include "mjit.h"
#include "vm_insnhelper.h"
#include "vm_exec.h"
#include "vm_insnhelper.c"
+
+#ifndef MJIT_HEADER
+
#include "vm_exec.c"
#include "vm_method.c"
+#endif /* #ifndef MJIT_HEADER */
#include "vm_eval.c"
+#ifndef MJIT_HEADER
#define PROCDEBUG 0
rb_serial_t
rb_next_class_serial(void)
{
- return NEXT_CLASS_SERIAL();
+ rb_serial_t class_serial = NEXT_CLASS_SERIAL();
+ mjit_add_class_serial(class_serial);
+ return class_serial;
}
VALUE rb_cRubyVM;
@@ -309,9 +344,16 @@ VALUE rb_mRubyVMFrozenCore;
#define ruby_vm_redefined_flag GET_VM()->redefined_flag
VALUE ruby_vm_const_missing_count = 0;
-rb_thread_t *ruby_current_thread = 0;
-rb_vm_t *ruby_current_vm = 0;
+rb_vm_t *ruby_current_vm_ptr = NULL;
+rb_execution_context_t *ruby_current_execution_context_ptr = NULL;
+
rb_event_flag_t ruby_vm_event_flags;
+rb_event_flag_t ruby_vm_event_enabled_global_flags;
+unsigned int ruby_vm_event_local_num;
+
+rb_serial_t ruby_vm_global_method_state = 1;
+rb_serial_t ruby_vm_global_constant_state = 1;
+rb_serial_t ruby_vm_class_serial = 1;
static void thread_free(void *ptr);
@@ -323,14 +365,14 @@ rb_vm_inc_const_missing_count(void)
VALUE rb_class_path_no_cache(VALUE _klass);
-int
-ruby_th_dtrace_setup(rb_thread_t *th, VALUE klass, ID id,
- struct ruby_dtrace_method_hook_args *args)
+MJIT_FUNC_EXPORTED int
+rb_dtrace_setup(rb_execution_context_t *ec, VALUE klass, ID id,
+ struct ruby_dtrace_method_hook_args *args)
{
enum ruby_value_type type;
if (!klass) {
- if (!th) th = GET_THREAD();
- if (!rb_thread_method_id_and_class(th, &id, 0, &klass) || !klass)
+ if (!ec) ec = GET_EC();
+ if (!rb_ec_frame_method_id_and_class(ec, &id, 0, &klass) || !klass)
return FALSE;
}
if (RB_TYPE_P(klass, T_ICLASS)) {
@@ -345,7 +387,7 @@ ruby_th_dtrace_setup(rb_thread_t *th, VALUE klass, ID id,
VALUE name = rb_class_path_no_cache(klass);
const char *classname, *filename;
const char *methodname = rb_id2name(id);
- if (methodname && (filename = rb_source_loc(&args->line_no)) != 0) {
+ if (methodname && (filename = rb_source_location_cstr(&args->line_no)) != 0) {
if (NIL_P(name) || !(classname = StringValuePtr(name)))
classname = "<unknown>";
args->classname = classname;
@@ -388,7 +430,8 @@ vm_stat(int argc, VALUE *argv, VALUE self)
VALUE arg = Qnil;
VALUE hash = Qnil, key = Qnil;
- if (rb_scan_args(argc, argv, "01", &arg) == 1) {
+ if (rb_check_arity(argc, 0, 1) == 1) {
+ arg = argv[0];
if (SYMBOL_P(arg))
key = arg;
else if (RB_TYPE_P(arg, T_HASH))
@@ -429,31 +472,33 @@ vm_stat(int argc, VALUE *argv, VALUE self)
/* control stack frame */
static void
-vm_set_top_stack(rb_thread_t *th, const rb_iseq_t *iseq)
+vm_set_top_stack(rb_execution_context_t *ec, const rb_iseq_t *iseq)
{
if (iseq->body->type != ISEQ_TYPE_TOP) {
rb_raise(rb_eTypeError, "Not a toplevel InstructionSequence");
}
/* for return */
- vm_push_frame(th, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH, th->top_self,
+ vm_push_frame(ec, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH, rb_ec_thread_ptr(ec)->top_self,
VM_BLOCK_HANDLER_NONE,
- (VALUE)vm_cref_new_toplevel(th), /* cref or me */
- iseq->body->iseq_encoded, th->cfp->sp, iseq->body->local_table_size, iseq->body->stack_max);
+ (VALUE)vm_cref_new_toplevel(ec), /* cref or me */
+ iseq->body->iseq_encoded, ec->cfp->sp,
+ iseq->body->local_table_size, iseq->body->stack_max);
}
static void
-vm_set_eval_stack(rb_thread_t * th, const rb_iseq_t *iseq, const rb_cref_t *cref, const struct rb_block *base_block)
+vm_set_eval_stack(rb_execution_context_t *ec, const rb_iseq_t *iseq, const rb_cref_t *cref, const struct rb_block *base_block)
{
- vm_push_frame(th, iseq, VM_FRAME_MAGIC_EVAL | VM_FRAME_FLAG_FINISH,
+ vm_push_frame(ec, iseq, VM_FRAME_MAGIC_EVAL | VM_FRAME_FLAG_FINISH,
vm_block_self(base_block), VM_GUARDED_PREV_EP(vm_block_ep(base_block)),
(VALUE)cref, /* cref or me */
iseq->body->iseq_encoded,
- th->cfp->sp, iseq->body->local_table_size, iseq->body->stack_max);
+ ec->cfp->sp, iseq->body->local_table_size,
+ iseq->body->stack_max);
}
static void
-vm_set_main_stack(rb_thread_t *th, const rb_iseq_t *iseq)
+vm_set_main_stack(rb_execution_context_t *ec, const rb_iseq_t *iseq)
{
VALUE toplevel_binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
rb_binding_t *bind;
@@ -461,18 +506,18 @@ vm_set_main_stack(rb_thread_t *th, const rb_iseq_t *iseq)
GetBindingPtr(toplevel_binding, bind);
RUBY_ASSERT_MESG(bind, "TOPLEVEL_BINDING is not built");
- vm_set_eval_stack(th, iseq, 0, &bind->block);
+ vm_set_eval_stack(ec, iseq, 0, &bind->block);
/* save binding */
if (iseq->body->local_table_size > 0) {
- vm_bind_update_env(bind, vm_make_env_object(th, th->cfp));
+ vm_bind_update_env(toplevel_binding, bind, vm_make_env_object(ec, ec->cfp));
}
}
rb_control_frame_t *
-rb_vm_get_binding_creatable_next_cfp(const rb_thread_t *th, const rb_control_frame_t *cfp)
+rb_vm_get_binding_creatable_next_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
{
- while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp)) {
+ while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) {
if (cfp->iseq) {
return (rb_control_frame_t *)cfp;
}
@@ -481,10 +526,11 @@ rb_vm_get_binding_creatable_next_cfp(const rb_thread_t *th, const rb_control_fra
return 0;
}
-rb_control_frame_t *
-rb_vm_get_ruby_level_next_cfp(const rb_thread_t *th, const rb_control_frame_t *cfp)
+MJIT_FUNC_EXPORTED rb_control_frame_t *
+rb_vm_get_ruby_level_next_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
{
- while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp)) {
+ if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) bp();
+ while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) {
if (VM_FRAME_RUBYFRAME_P(cfp)) {
return (rb_control_frame_t *)cfp;
}
@@ -493,8 +539,10 @@ rb_vm_get_ruby_level_next_cfp(const rb_thread_t *th, const rb_control_frame_t *c
return 0;
}
+#endif /* #ifndef MJIT_HEADER */
+
static rb_control_frame_t *
-vm_get_ruby_level_caller_cfp(const rb_thread_t *th, const rb_control_frame_t *cfp)
+vm_get_ruby_level_caller_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
{
if (VM_FRAME_RUBYFRAME_P(cfp)) {
return (rb_control_frame_t *)cfp;
@@ -502,7 +550,7 @@ vm_get_ruby_level_caller_cfp(const rb_thread_t *th, const rb_control_frame_t *cf
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
- while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp)) {
+ while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) {
if (VM_FRAME_RUBYFRAME_P(cfp)) {
return (rb_control_frame_t *)cfp;
}
@@ -515,28 +563,30 @@ vm_get_ruby_level_caller_cfp(const rb_thread_t *th, const rb_control_frame_t *cf
return 0;
}
-void
+MJIT_STATIC void
rb_vm_pop_cfunc_frame(void)
{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp;
+ rb_execution_context_t *ec = GET_EC();
+ rb_control_frame_t *cfp = ec->cfp;
const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
- EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, cfp->self, me->def->original_id, me->called_id, me->owner, Qnil);
- RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, me->owner, me->def->original_id);
- vm_pop_frame(th, cfp, cfp->ep);
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, cfp->self, me->def->original_id, me->called_id, me->owner, Qnil);
+ RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, me->owner, me->def->original_id);
+ vm_pop_frame(ec, cfp, cfp->ep);
}
+#ifndef MJIT_HEADER
+
void
-rb_vm_rewind_cfp(rb_thread_t *th, rb_control_frame_t *cfp)
+rb_vm_rewind_cfp(rb_execution_context_t *ec, rb_control_frame_t *cfp)
{
/* check skipped frame */
- while (th->cfp != cfp) {
+ while (ec->cfp != cfp) {
#if VMDEBUG
- printf("skipped frame: %s\n", vm_frametype_name(th->cfp));
+ printf("skipped frame: %s\n", vm_frametype_name(ec->cfp));
#endif
- if (VM_FRAME_TYPE(th->cfp) != VM_FRAME_MAGIC_CFUNC) {
- rb_vm_pop_frame(th);
+ if (VM_FRAME_TYPE(ec->cfp) != VM_FRAME_MAGIC_CFUNC) {
+ rb_vm_pop_frame(ec);
}
else { /* unlikely path */
rb_vm_pop_cfunc_frame();
@@ -544,13 +594,6 @@ rb_vm_rewind_cfp(rb_thread_t *th, rb_control_frame_t *cfp)
}
}
-/* obsolete */
-void
-rb_frame_pop(void)
-{
- ONLY_FOR_INTERNAL_USE("rb_frame_pop()");
-}
-
/* at exit */
void
@@ -571,7 +614,7 @@ ruby_vm_run_at_exit_hooks(rb_vm_t *vm)
while (l) {
rb_at_exit_list* t = l->next;
rb_vm_at_exit_func *func = l->func;
- free(l);
+ ruby_xfree(l);
l = t;
(*func)(vm);
}
@@ -607,28 +650,25 @@ check_env_value(const rb_env_t *env)
return Qnil; /* unreachable */
}
-static void
-vm_block_handler_escape(rb_thread_t *th, VALUE block_handler, VALUE *procvalptr)
+static VALUE
+vm_block_handler_escape(const rb_execution_context_t *ec, VALUE block_handler)
{
switch (vm_block_handler_type(block_handler)) {
case block_handler_type_ifunc:
case block_handler_type_iseq:
- *procvalptr = rb_vm_make_proc(th, VM_BH_TO_CAPT_BLOCK(block_handler), rb_cProc);
- return;
+ return rb_vm_make_proc(ec, VM_BH_TO_CAPT_BLOCK(block_handler), rb_cProc);
case block_handler_type_symbol:
case block_handler_type_proc:
- *procvalptr = block_handler;
- return;
+ return block_handler;
}
VM_UNREACHABLE(vm_block_handler_escape);
- return;
+ return Qnil;
}
static VALUE
-vm_make_env_each(rb_thread_t *const th, rb_control_frame_t *const cfp)
+vm_make_env_each(const rb_execution_context_t * const ec, rb_control_frame_t *const cfp)
{
- VALUE blockprocval = Qfalse;
const VALUE * const ep = cfp->ep;
const rb_env_t *env;
const rb_iseq_t *env_iseq;
@@ -650,7 +690,7 @@ vm_make_env_each(rb_thread_t *const th, rb_control_frame_t *const cfp)
VM_ASSERT(prev_cfp->ep != NULL);
}
- vm_make_env_each(th, prev_cfp);
+ vm_make_env_each(ec, prev_cfp);
VM_FORCE_WRITE_SPECIAL_CONST(&ep[VM_ENV_DATA_INDEX_SPECVAL], VM_GUARDED_PREV_EP(prev_cfp->ep));
}
}
@@ -658,7 +698,7 @@ vm_make_env_each(rb_thread_t *const th, rb_control_frame_t *const cfp)
VALUE block_handler = VM_ENV_BLOCK_HANDLER(ep);
if (block_handler != VM_BLOCK_HANDLER_NONE) {
- vm_block_handler_escape(th, block_handler, &blockprocval);
+ VALUE blockprocval = vm_block_handler_escape(ec, block_handler);
VM_STACK_ENV_WRITE(ep, VM_ENV_DATA_INDEX_SPECVAL, blockprocval);
}
}
@@ -683,8 +723,7 @@ vm_make_env_each(rb_thread_t *const th, rb_control_frame_t *const cfp)
*/
env_size = local_size +
- 1 /* envval */ +
- (blockprocval ? 1 : 0) /* blockprocval */;
+ 1 /* envval */;
env_body = ALLOC_N(VALUE, env_size);
MEMCPY(env_body, ep - (local_size - 1 /* specval */), VALUE, local_size);
@@ -702,7 +741,6 @@ vm_make_env_each(rb_thread_t *const th, rb_control_frame_t *const cfp)
env = vm_env_new(env_ep, env_body, env_size, env_iseq);
- if (blockprocval) RB_OBJ_WRITE(env, &env_ep[2], blockprocval);
cfp->ep = env_ep;
VM_ENV_FLAGS_SET(env_ep, VM_ENV_FLAG_ESCAPED | VM_ENV_FLAG_WB_REQUIRED);
VM_STACK_ENV_WRITE(ep, 0, (VALUE)env); /* GC mark */
@@ -710,9 +748,9 @@ vm_make_env_each(rb_thread_t *const th, rb_control_frame_t *const cfp)
}
static VALUE
-vm_make_env_object(rb_thread_t *th, rb_control_frame_t *cfp)
+vm_make_env_object(const rb_execution_context_t *ec, rb_control_frame_t *cfp)
{
- VALUE envval = vm_make_env_each(th, cfp);
+ VALUE envval = vm_make_env_each(ec, cfp);
if (PROCDEBUG) {
check_env_value((const rb_env_t *)envval);
@@ -722,11 +760,11 @@ vm_make_env_object(rb_thread_t *th, rb_control_frame_t *cfp)
}
void
-rb_vm_stack_to_heap(rb_thread_t *th)
+rb_vm_stack_to_heap(rb_execution_context_t *ec)
{
- rb_control_frame_t *cfp = th->cfp;
- while ((cfp = rb_vm_get_binding_creatable_next_cfp(th, cfp)) != 0) {
- vm_make_env_object(th, cfp);
+ rb_control_frame_t *cfp = ec->cfp;
+ while ((cfp = rb_vm_get_binding_creatable_next_cfp(ec, cfp)) != 0) {
+ vm_make_env_object(ec, cfp);
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
}
}
@@ -764,7 +802,7 @@ collect_local_variables_in_env(const rb_env_t *env, const struct local_var_list
}
static int
-vm_collect_local_variables_in_heap(rb_thread_t *th, const VALUE *ep, const struct local_var_list *vars)
+vm_collect_local_variables_in_heap(const VALUE *ep, const struct local_var_list *vars)
{
if (VM_ENV_ESCAPED_P(ep)) {
collect_local_variables_in_env(VM_ENV_ENVVAL_PTR(ep), vars);
@@ -797,58 +835,58 @@ rb_iseq_local_variables(const rb_iseq_t *iseq)
/* Proc */
-VALUE
-rb_proc_create_from_captured(VALUE klass,
+static VALUE
+vm_proc_create_from_captured(VALUE klass,
const struct rb_captured_block *captured,
enum rb_block_type block_type,
- int8_t safe_level, int8_t is_from_method, int8_t is_lambda)
+ int8_t is_from_method, int8_t is_lambda)
{
VALUE procval = rb_proc_alloc(klass);
rb_proc_t *proc = RTYPEDDATA_DATA(procval);
- VM_ASSERT(VM_EP_IN_HEAP_P(GET_THREAD(), captured->ep));
+ VM_ASSERT(VM_EP_IN_HEAP_P(GET_EC(), captured->ep));
/* copy block */
RB_OBJ_WRITE(procval, &proc->block.as.captured.self, captured->self);
RB_OBJ_WRITE(procval, &proc->block.as.captured.code.val, captured->code.val);
- *((const VALUE **)&proc->block.as.captured.ep) = captured->ep;
- RB_OBJ_WRITTEN(procval, Qundef, VM_ENV_ENVVAL(captured->ep));
+ rb_vm_block_ep_update(procval, &proc->block, captured->ep);
vm_block_type_set(&proc->block, block_type);
- proc->safe_level = safe_level;
proc->is_from_method = is_from_method;
proc->is_lambda = is_lambda;
return procval;
}
-VALUE
-rb_proc_create(VALUE klass, const struct rb_block *block,
- int8_t safe_level, int8_t is_from_method, int8_t is_lambda)
+void
+rb_vm_block_copy(VALUE obj, const struct rb_block *dst, const struct rb_block *src)
{
- VALUE procval = rb_proc_alloc(klass);
- rb_proc_t *proc = RTYPEDDATA_DATA(procval);
-
- VM_ASSERT(VM_EP_IN_HEAP_P(GET_THREAD(), vm_block_ep(block)));
-
/* copy block */
- switch (vm_block_type(block)) {
+ switch (vm_block_type(src)) {
case block_type_iseq:
case block_type_ifunc:
- RB_OBJ_WRITE(procval, &proc->block.as.captured.self, block->as.captured.self);
- RB_OBJ_WRITE(procval, &proc->block.as.captured.code.val, block->as.captured.code.val);
- *((const VALUE **)&proc->block.as.captured.ep) = block->as.captured.ep;
- RB_OBJ_WRITTEN(procval, Qundef, VM_ENV_ENVVAL(block->as.captured.ep));
+ RB_OBJ_WRITE(obj, &dst->as.captured.self, src->as.captured.self);
+ RB_OBJ_WRITE(obj, &dst->as.captured.code.val, src->as.captured.code.val);
+ rb_vm_block_ep_update(obj, dst, src->as.captured.ep);
break;
case block_type_symbol:
- RB_OBJ_WRITE(procval, &proc->block.as.symbol, block->as.symbol);
+ RB_OBJ_WRITE(obj, &dst->as.symbol, src->as.symbol);
break;
case block_type_proc:
- RB_OBJ_WRITE(procval, &proc->block.as.proc, block->as.proc);
+ RB_OBJ_WRITE(obj, &dst->as.proc, src->as.proc);
break;
}
+}
+
+static VALUE
+proc_create(VALUE klass, const struct rb_block *block, int8_t is_from_method, int8_t is_lambda)
+{
+ VALUE procval = rb_proc_alloc(klass);
+ rb_proc_t *proc = RTYPEDDATA_DATA(procval);
+
+ VM_ASSERT(VM_EP_IN_HEAP_P(GET_EC(), vm_block_ep(block)));
+ rb_vm_block_copy(procval, &proc->block, block);
vm_block_type_set(&proc->block, block->type);
- proc->safe_level = safe_level;
proc->is_from_method = is_from_method;
proc->is_lambda = is_lambda;
@@ -856,36 +894,43 @@ rb_proc_create(VALUE klass, const struct rb_block *block,
}
VALUE
-rb_vm_make_proc(rb_thread_t *th, const struct rb_captured_block *captured, VALUE klass)
+rb_proc_dup(VALUE self)
{
- return rb_vm_make_proc_lambda(th, captured, klass, FALSE);
+ VALUE procval;
+ rb_proc_t *src;
+
+ GetProcPtr(self, src);
+ procval = proc_create(rb_cProc, &src->block, src->is_from_method, src->is_lambda);
+ RB_GC_GUARD(self); /* for: body = rb_proc_dup(body) */
+ return procval;
}
-VALUE
-rb_vm_make_proc_lambda(rb_thread_t *th, const struct rb_captured_block *captured, VALUE klass, int8_t is_lambda)
+
+MJIT_FUNC_EXPORTED VALUE
+rb_vm_make_proc_lambda(const rb_execution_context_t *ec, const struct rb_captured_block *captured, VALUE klass, int8_t is_lambda)
{
VALUE procval;
if (!VM_ENV_ESCAPED_P(captured->ep)) {
rb_control_frame_t *cfp = VM_CAPTURED_BLOCK_TO_CFP(captured);
- vm_make_env_object(th, cfp);
+ vm_make_env_object(ec, cfp);
}
- VM_ASSERT(VM_EP_IN_HEAP_P(th, captured->ep));
- VM_ASSERT(RB_TYPE_P(captured->code.val, T_IMEMO));
+ VM_ASSERT(VM_EP_IN_HEAP_P(ec, captured->ep));
+ VM_ASSERT(imemo_type_p(captured->code.val, imemo_iseq) ||
+ imemo_type_p(captured->code.val, imemo_ifunc));
- procval = rb_proc_create_from_captured(klass, captured,
- imemo_type(captured->code.val) == imemo_iseq ? block_type_iseq : block_type_ifunc,
- (int8_t)th->safe_level, FALSE, is_lambda);
+ procval = vm_proc_create_from_captured(klass, captured,
+ imemo_type(captured->code.val) == imemo_iseq ? block_type_iseq : block_type_ifunc, FALSE, is_lambda);
return procval;
}
/* Binding */
VALUE
-rb_vm_make_binding(rb_thread_t *th, const rb_control_frame_t *src_cfp)
+rb_vm_make_binding(const rb_execution_context_t *ec, const rb_control_frame_t *src_cfp)
{
- rb_control_frame_t *cfp = rb_vm_get_binding_creatable_next_cfp(th, src_cfp);
- rb_control_frame_t *ruby_level_cfp = rb_vm_get_ruby_level_next_cfp(th, src_cfp);
+ rb_control_frame_t *cfp = rb_vm_get_binding_creatable_next_cfp(ec, src_cfp);
+ rb_control_frame_t *ruby_level_cfp = rb_vm_get_ruby_level_next_cfp(ec, src_cfp);
VALUE bindval, envval;
rb_binding_t *bind;
@@ -894,33 +939,36 @@ rb_vm_make_binding(rb_thread_t *th, const rb_control_frame_t *src_cfp)
}
while (1) {
- envval = vm_make_env_object(th, cfp);
+ envval = vm_make_env_object(ec, cfp);
if (cfp == ruby_level_cfp) {
break;
}
- cfp = rb_vm_get_binding_creatable_next_cfp(th, RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp));
+ cfp = rb_vm_get_binding_creatable_next_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp));
}
bindval = rb_binding_alloc(rb_cBinding);
GetBindingPtr(bindval, bind);
- vm_bind_update_env(bind, envval);
- bind->block.as.captured.self = cfp->self;
- bind->block.as.captured.code.iseq = cfp->iseq;
- bind->path = ruby_level_cfp->iseq->body->location.path;
+ vm_bind_update_env(bindval, bind, envval);
+ RB_OBJ_WRITE(bindval, &bind->block.as.captured.self, cfp->self);
+ RB_OBJ_WRITE(bindval, &bind->block.as.captured.code.iseq, cfp->iseq);
+ RB_OBJ_WRITE(bindval, &bind->pathobj, ruby_level_cfp->iseq->body->location.pathobj);
bind->first_lineno = rb_vm_get_sourceline(ruby_level_cfp);
return bindval;
}
const VALUE *
-rb_binding_add_dynavars(rb_binding_t *bind, int dyncount, const ID *dynvars)
+rb_binding_add_dynavars(VALUE bindval, rb_binding_t *bind, int dyncount, const ID *dynvars)
{
- VALUE envval, path = bind->path;
+ VALUE envval, pathobj = bind->pathobj;
+ VALUE path = pathobj_path(pathobj);
+ VALUE realpath = pathobj_realpath(pathobj);
const struct rb_block *base_block;
const rb_env_t *env;
- rb_thread_t *th = GET_THREAD();
+ rb_execution_context_t *ec = GET_EC();
const rb_iseq_t *base_iseq, *iseq;
- NODE *node = 0;
+ rb_ast_body_t ast;
+ NODE tmp_node;
ID minibuf[4], *dyns = minibuf;
VALUE idtmp = 0;
@@ -933,21 +981,24 @@ rb_binding_add_dynavars(rb_binding_t *bind, int dyncount, const ID *dynvars)
dyns[0] = dyncount;
MEMCPY(dyns + 1, dynvars, ID, dyncount);
- node = NEW_NODE(NODE_SCOPE, dyns, 0, 0);
+ rb_node_init(&tmp_node, NODE_SCOPE, (VALUE)dyns, 0, 0);
+ ast.root = &tmp_node;
+ ast.compile_option = 0;
+ ast.line_count = -1;
if (base_iseq) {
- iseq = rb_iseq_new(node, base_iseq->body->location.label, path, path, base_iseq, ISEQ_TYPE_EVAL);
+ iseq = rb_iseq_new(&ast, base_iseq->body->location.label, path, realpath, base_iseq, ISEQ_TYPE_EVAL);
}
else {
- VALUE tempstr = rb_fstring_cstr("<temp>");
- iseq = rb_iseq_new_top(node, tempstr, tempstr, tempstr, NULL);
+ VALUE tempstr = rb_fstring_lit("<temp>");
+ iseq = rb_iseq_new_top(&ast, tempstr, tempstr, tempstr, NULL);
}
- node->u1.tbl = 0; /* reset table */
+ tmp_node.nd_tbl = 0; /* reset table */
ALLOCV_END(idtmp);
- vm_set_eval_stack(th, iseq, 0, base_block);
- vm_bind_update_env(bind, envval = vm_make_env_object(th, th->cfp));
- rb_vm_pop_frame(th);
+ vm_set_eval_stack(ec, iseq, 0, base_block);
+ vm_bind_update_env(bindval, bind, envval = vm_make_env_object(ec, ec->cfp));
+ rb_vm_pop_frame(ec);
env = (const rb_env_t *)envval;
return env->env;
@@ -956,88 +1007,121 @@ rb_binding_add_dynavars(rb_binding_t *bind, int dyncount, const ID *dynvars)
/* C -> Ruby: block */
static inline VALUE
-invoke_block(rb_thread_t *th, const rb_iseq_t *iseq, VALUE self, const struct rb_captured_block *captured, const rb_cref_t *cref, VALUE type, int opt_pc)
+invoke_block(rb_execution_context_t *ec, const rb_iseq_t *iseq, VALUE self, const struct rb_captured_block *captured, const rb_cref_t *cref, VALUE type, int opt_pc)
{
int arg_size = iseq->body->param.size;
- vm_push_frame(th, iseq, type | VM_FRAME_FLAG_FINISH, self,
+ vm_push_frame(ec, iseq, type | VM_FRAME_FLAG_FINISH, self,
VM_GUARDED_PREV_EP(captured->ep),
(VALUE)cref, /* cref or method */
iseq->body->iseq_encoded + opt_pc,
- th->cfp->sp + arg_size, iseq->body->local_table_size - arg_size,
+ ec->cfp->sp + arg_size,
+ iseq->body->local_table_size - arg_size,
iseq->body->stack_max);
- return vm_exec(th);
+ return vm_exec(ec, TRUE);
}
static VALUE
-invoke_bmethod(rb_thread_t *th, const rb_iseq_t *iseq, VALUE self, const struct rb_captured_block *captured, const rb_callable_method_entry_t *me, VALUE type, int opt_pc)
+invoke_bmethod(rb_execution_context_t *ec, const rb_iseq_t *iseq, VALUE self, const struct rb_captured_block *captured, const rb_callable_method_entry_t *me, VALUE type, int opt_pc)
{
/* bmethod */
int arg_size = iseq->body->param.size;
VALUE ret;
+ rb_hook_list_t *hooks;
- vm_push_frame(th, iseq, type | VM_FRAME_FLAG_FINISH | VM_FRAME_FLAG_BMETHOD, self,
+ VM_ASSERT(me->def->type == VM_METHOD_TYPE_BMETHOD);
+
+ vm_push_frame(ec, iseq, type | VM_FRAME_FLAG_BMETHOD, self,
VM_GUARDED_PREV_EP(captured->ep),
(VALUE)me,
iseq->body->iseq_encoded + opt_pc,
- th->cfp->sp + arg_size, iseq->body->local_table_size - arg_size,
+ ec->cfp->sp + arg_size,
+ iseq->body->local_table_size - arg_size,
iseq->body->stack_max);
- RUBY_DTRACE_METHOD_ENTRY_HOOK(th, me->owner, me->def->original_id);
- EXEC_EVENT_HOOK(th, RUBY_EVENT_CALL, self, me->def->original_id, me->called_id, me->owner, Qnil);
- ret = vm_exec(th);
- EXEC_EVENT_HOOK(th, RUBY_EVENT_RETURN, self, me->def->original_id, me->called_id, me->owner, ret);
- RUBY_DTRACE_METHOD_RETURN_HOOK(th, me->owner, me->def->original_id);
+ RUBY_DTRACE_METHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id);
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_CALL, self, me->def->original_id, me->called_id, me->owner, Qnil);
+
+ if (UNLIKELY((hooks = me->def->body.bmethod.hooks) != NULL) &&
+ hooks->events & RUBY_EVENT_CALL) {
+ rb_exec_event_hook_orig(ec, hooks, RUBY_EVENT_CALL, self,
+ me->def->original_id, me->called_id, me->owner, Qnil, FALSE);
+ }
+ VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);
+ ret = vm_exec(ec, TRUE);
+
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_RETURN, self, me->def->original_id, me->called_id, me->owner, ret);
+ if ((hooks = me->def->body.bmethod.hooks) != NULL &&
+ hooks->events & RUBY_EVENT_RETURN) {
+ rb_exec_event_hook_orig(ec, hooks, RUBY_EVENT_RETURN, self,
+ me->def->original_id, me->called_id, me->owner, ret, FALSE);
+ }
+ RUBY_DTRACE_METHOD_RETURN_HOOK(ec, me->owner, me->def->original_id);
return ret;
}
+ALWAYS_INLINE(static VALUE
+ invoke_iseq_block_from_c(rb_execution_context_t *ec, const struct rb_captured_block *captured,
+ VALUE self, int argc, const VALUE *argv, VALUE passed_block_handler,
+ const rb_cref_t *cref, int is_lambda, const rb_callable_method_entry_t *me));
+
static inline VALUE
-invoke_iseq_block_from_c(rb_thread_t *th, const struct rb_captured_block *captured,
+invoke_iseq_block_from_c(rb_execution_context_t *ec, const struct rb_captured_block *captured,
VALUE self, int argc, const VALUE *argv, VALUE passed_block_handler,
- const rb_cref_t *cref, const int splattable, int is_lambda)
+ const rb_cref_t *cref, int is_lambda, const rb_callable_method_entry_t *me)
{
const rb_iseq_t *iseq = rb_iseq_check(captured->code.iseq);
int i, opt_pc;
- VALUE type = is_lambda ? VM_FRAME_MAGIC_LAMBDA : VM_FRAME_MAGIC_BLOCK;
- VALUE *sp = th->cfp->sp;
- const rb_callable_method_entry_t *me = th->passed_bmethod_me;
- th->passed_bmethod_me = NULL;
+ VALUE type = VM_FRAME_MAGIC_BLOCK | (is_lambda ? VM_FRAME_FLAG_LAMBDA : 0);
+ rb_control_frame_t *cfp = ec->cfp;
+ VALUE *sp = cfp->sp;
+
+ stack_check(ec);
+ CHECK_VM_STACK_OVERFLOW(cfp, argc);
+ cfp->sp = sp + argc;
for (i=0; i<argc; i++) {
sp[i] = argv[i];
}
- opt_pc = vm_yield_setup_args(th, iseq, argc, sp, passed_block_handler,
- (type == VM_FRAME_MAGIC_LAMBDA ? (splattable ? arg_setup_lambda : arg_setup_method) : arg_setup_block));
+ opt_pc = vm_yield_setup_args(ec, iseq, argc, sp, passed_block_handler,
+ (is_lambda ? arg_setup_method : arg_setup_block));
+ cfp->sp = sp;
if (me == NULL) {
- return invoke_block(th, iseq, self, captured, cref, type, opt_pc);
+ return invoke_block(ec, iseq, self, captured, cref, type, opt_pc);
}
else {
- return invoke_bmethod(th, iseq, self, captured, me, type, opt_pc);
+ return invoke_bmethod(ec, iseq, self, captured, me, type, opt_pc);
}
}
static inline VALUE
-invoke_block_from_c_splattable(rb_thread_t *th, VALUE block_handler,
- int argc, const VALUE *argv,
- VALUE passed_block_handler, const rb_cref_t *cref)
+invoke_block_from_c_bh(rb_execution_context_t *ec, VALUE block_handler,
+ int argc, const VALUE *argv,
+ VALUE passed_block_handler, const rb_cref_t *cref,
+ int is_lambda, int force_blockarg)
{
- int is_lambda = FALSE;
again:
switch (vm_block_handler_type(block_handler)) {
case block_handler_type_iseq:
{
const struct rb_captured_block *captured = VM_BH_TO_ISEQ_BLOCK(block_handler);
- return invoke_iseq_block_from_c(th, captured, captured->self, argc, argv, passed_block_handler, cref, TRUE, is_lambda);
+ return invoke_iseq_block_from_c(ec, captured, captured->self,
+ argc, argv, passed_block_handler,
+ cref, is_lambda, NULL);
}
case block_handler_type_ifunc:
- return vm_yield_with_cfunc(th, VM_BH_TO_IFUNC_BLOCK(block_handler), VM_BH_TO_IFUNC_BLOCK(block_handler)->self,
- argc, argv, passed_block_handler);
+ return vm_yield_with_cfunc(ec, VM_BH_TO_IFUNC_BLOCK(block_handler),
+ VM_BH_TO_IFUNC_BLOCK(block_handler)->self,
+ argc, argv, passed_block_handler, NULL);
case block_handler_type_symbol:
- return vm_yield_with_symbol(th, VM_BH_TO_SYMBOL(block_handler), argc, argv, passed_block_handler);
+ return vm_yield_with_symbol(ec, VM_BH_TO_SYMBOL(block_handler),
+ argc, argv, passed_block_handler);
case block_handler_type_proc:
- is_lambda = block_proc_is_lambda(VM_BH_TO_PROC(block_handler));
+ if (force_blockarg == FALSE) {
+ is_lambda = block_proc_is_lambda(VM_BH_TO_PROC(block_handler));
+ }
block_handler = vm_proc_to_block_handler(VM_BH_TO_PROC(block_handler));
goto again;
}
@@ -1046,10 +1130,10 @@ invoke_block_from_c_splattable(rb_thread_t *th, VALUE block_handler,
}
static inline VALUE
-check_block_handler(rb_thread_t *th)
+check_block_handler(rb_execution_context_t *ec)
{
- VALUE block_handler = VM_CF_BLOCK_HANDLER(th->cfp);
- VM_ASSERT(vm_block_handler_verify(block_handler));
+ VALUE block_handler = VM_CF_BLOCK_HANDLER(ec->cfp);
+ vm_block_handler_verify(block_handler);
if (UNLIKELY(block_handler == VM_BLOCK_HANDLER_NONE)) {
rb_vm_localjump_error("no block given", Qnil, 0);
}
@@ -1058,98 +1142,104 @@ check_block_handler(rb_thread_t *th)
}
static VALUE
-vm_yield_with_cref(rb_thread_t *th, int argc, const VALUE *argv, const rb_cref_t *cref)
+vm_yield_with_cref(rb_execution_context_t *ec, int argc, const VALUE *argv, const rb_cref_t *cref, int is_lambda)
{
- return invoke_block_from_c_splattable(th, check_block_handler(th), argc, argv, VM_BLOCK_HANDLER_NONE, cref);
+ return invoke_block_from_c_bh(ec, check_block_handler(ec),
+ argc, argv, VM_BLOCK_HANDLER_NONE,
+ cref, is_lambda, FALSE);
}
static VALUE
-vm_yield(rb_thread_t *th, int argc, const VALUE *argv)
+vm_yield(rb_execution_context_t *ec, int argc, const VALUE *argv)
{
- return invoke_block_from_c_splattable(th, check_block_handler(th), argc, argv, VM_BLOCK_HANDLER_NONE, NULL);
+ return invoke_block_from_c_bh(ec, check_block_handler(ec),
+ argc, argv, VM_BLOCK_HANDLER_NONE,
+ NULL, FALSE, FALSE);
}
static VALUE
-vm_yield_with_block(rb_thread_t *th, int argc, const VALUE *argv, VALUE block_handler)
+vm_yield_with_block(rb_execution_context_t *ec, int argc, const VALUE *argv, VALUE block_handler)
{
- return invoke_block_from_c_splattable(th, check_block_handler(th), argc, argv, block_handler, NULL);
+ return invoke_block_from_c_bh(ec, check_block_handler(ec),
+ argc, argv, block_handler,
+ NULL, FALSE, FALSE);
}
+static VALUE
+vm_yield_force_blockarg(rb_execution_context_t *ec, VALUE args)
+{
+ return invoke_block_from_c_bh(ec, check_block_handler(ec), 1, &args,
+ VM_BLOCK_HANDLER_NONE, NULL, FALSE, TRUE);
+}
+
+ALWAYS_INLINE(static VALUE
+ invoke_block_from_c_proc(rb_execution_context_t *ec, const rb_proc_t *proc,
+ VALUE self, int argc, const VALUE *argv,
+ VALUE passed_block_handler, int is_lambda,
+ const rb_callable_method_entry_t *me));
+
static inline VALUE
-invoke_block_from_c_unsplattable(rb_thread_t *th, const struct rb_block *block,
- VALUE self, int argc, const VALUE *argv,
- VALUE passed_block_handler, int is_lambda)
+invoke_block_from_c_proc(rb_execution_context_t *ec, const rb_proc_t *proc,
+ VALUE self, int argc, const VALUE *argv,
+ VALUE passed_block_handler, int is_lambda,
+ const rb_callable_method_entry_t *me)
{
+ const struct rb_block *block = &proc->block;
+
again:
switch (vm_block_type(block)) {
case block_type_iseq:
- return invoke_iseq_block_from_c(th, &block->as.captured, self, argc, argv, passed_block_handler, NULL, FALSE, is_lambda);
+ return invoke_iseq_block_from_c(ec, &block->as.captured, self, argc, argv, passed_block_handler, NULL, is_lambda, me);
case block_type_ifunc:
- return vm_yield_with_cfunc(th, &block->as.captured, self, argc, argv, passed_block_handler);
+ return vm_yield_with_cfunc(ec, &block->as.captured, self, argc, argv, passed_block_handler, me);
case block_type_symbol:
- return vm_yield_with_symbol(th, block->as.symbol, argc, argv, passed_block_handler);
+ return vm_yield_with_symbol(ec, block->as.symbol, argc, argv, passed_block_handler);
case block_type_proc:
is_lambda = block_proc_is_lambda(block->as.proc);
block = vm_proc_block(block->as.proc);
goto again;
}
- VM_UNREACHABLE(invoke_block_from_c_unsplattable);
+ VM_UNREACHABLE(invoke_block_from_c_proc);
return Qundef;
}
static VALUE
-vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, VALUE self,
+vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self,
int argc, const VALUE *argv, VALUE passed_block_handler)
{
- VALUE val = Qundef;
- int state;
- volatile int stored_safe = th->safe_level;
-
- TH_PUSH_TAG(th);
- if ((state = EXEC_TAG()) == 0) {
- th->safe_level = proc->safe_level;
- val = invoke_block_from_c_unsplattable(th, &proc->block, self, argc, argv, passed_block_handler, proc->is_lambda);
- }
- TH_POP_TAG();
-
- th->safe_level = stored_safe;
-
- if (state) {
- TH_JUMP_TAG(th, state);
- }
- return val;
+ return invoke_block_from_c_proc(ec, proc, self, argc, argv, passed_block_handler, proc->is_lambda, NULL);
}
-static VALUE
-vm_invoke_bmethod(rb_thread_t *th, rb_proc_t *proc, VALUE self,
- int argc, const VALUE *argv, VALUE block_handler)
+MJIT_FUNC_EXPORTED VALUE
+rb_vm_invoke_bmethod(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self,
+ int argc, const VALUE *argv, VALUE block_handler, const rb_callable_method_entry_t *me)
{
- return invoke_block_from_c_unsplattable(th, &proc->block, self, argc, argv, block_handler, TRUE);
+ return invoke_block_from_c_proc(ec, proc, self, argc, argv, block_handler, TRUE, me);
}
-VALUE
-rb_vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc,
+MJIT_FUNC_EXPORTED VALUE
+rb_vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc,
int argc, const VALUE *argv, VALUE passed_block_handler)
{
VALUE self = vm_block_self(&proc->block);
- VM_ASSERT(vm_block_handler_verify(passed_block_handler));
+ vm_block_handler_verify(passed_block_handler);
if (proc->is_from_method) {
- return vm_invoke_bmethod(th, proc, self, argc, argv, passed_block_handler);
+ return rb_vm_invoke_bmethod(ec, proc, self, argc, argv, passed_block_handler, NULL);
}
else {
- return vm_invoke_proc(th, proc, self, argc, argv, passed_block_handler);
+ return vm_invoke_proc(ec, proc, self, argc, argv, passed_block_handler);
}
}
/* special variable */
static rb_control_frame_t *
-vm_normal_frame(rb_thread_t *th, rb_control_frame_t *cfp)
+vm_normal_frame(const rb_execution_context_t *ec, rb_control_frame_t *cfp)
{
while (cfp->pc == 0) {
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
- if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp)) {
+ if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) {
return 0;
}
}
@@ -1157,92 +1247,78 @@ vm_normal_frame(rb_thread_t *th, rb_control_frame_t *cfp)
}
static VALUE
-vm_cfp_svar_get(rb_thread_t *th, rb_control_frame_t *cfp, VALUE key)
+vm_cfp_svar_get(const rb_execution_context_t *ec, rb_control_frame_t *cfp, VALUE key)
{
- cfp = vm_normal_frame(th, cfp);
- return lep_svar_get(th, cfp ? VM_CF_LEP(cfp) : 0, key);
+ cfp = vm_normal_frame(ec, cfp);
+ return lep_svar_get(ec, cfp ? VM_CF_LEP(cfp) : 0, key);
}
static void
-vm_cfp_svar_set(rb_thread_t *th, rb_control_frame_t *cfp, VALUE key, const VALUE val)
+vm_cfp_svar_set(const rb_execution_context_t *ec, rb_control_frame_t *cfp, VALUE key, const VALUE val)
{
- cfp = vm_normal_frame(th, cfp);
- lep_svar_set(th, cfp ? VM_CF_LEP(cfp) : 0, key, val);
+ cfp = vm_normal_frame(ec, cfp);
+ lep_svar_set(ec, cfp ? VM_CF_LEP(cfp) : 0, key, val);
}
static VALUE
-vm_svar_get(VALUE key)
+vm_svar_get(const rb_execution_context_t *ec, VALUE key)
{
- rb_thread_t *th = GET_THREAD();
- return vm_cfp_svar_get(th, th->cfp, key);
+ return vm_cfp_svar_get(ec, ec->cfp, key);
}
static void
-vm_svar_set(VALUE key, VALUE val)
+vm_svar_set(const rb_execution_context_t *ec, VALUE key, VALUE val)
{
- rb_thread_t *th = GET_THREAD();
- vm_cfp_svar_set(th, th->cfp, key, val);
+ vm_cfp_svar_set(ec, ec->cfp, key, val);
}
VALUE
rb_backref_get(void)
{
- return vm_svar_get(VM_SVAR_BACKREF);
+ return vm_svar_get(GET_EC(), VM_SVAR_BACKREF);
}
void
rb_backref_set(VALUE val)
{
- vm_svar_set(VM_SVAR_BACKREF, val);
+ vm_svar_set(GET_EC(), VM_SVAR_BACKREF, val);
}
VALUE
rb_lastline_get(void)
{
- return vm_svar_get(VM_SVAR_LASTLINE);
+ return vm_svar_get(GET_EC(), VM_SVAR_LASTLINE);
}
void
rb_lastline_set(VALUE val)
{
- vm_svar_set(VM_SVAR_LASTLINE, val);
+ vm_svar_set(GET_EC(), VM_SVAR_LASTLINE, val);
}
/* misc */
-VALUE
-rb_sourcefilename(void)
-{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
-
- if (cfp) {
- return cfp->iseq->body->location.path;
- }
- else {
- return Qnil;
- }
-}
-
+/* in intern.h */
const char *
rb_sourcefile(void)
{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
+ const rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
if (cfp) {
- return RSTRING_PTR(cfp->iseq->body->location.path);
+ return RSTRING_PTR(rb_iseq_path(cfp->iseq));
}
else {
return 0;
}
}
+/* in intern.h */
int
rb_sourceline(void)
{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
+ const rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
if (cfp) {
return rb_vm_get_sourceline(cfp);
@@ -1255,45 +1331,44 @@ rb_sourceline(void)
VALUE
rb_source_location(int *pline)
{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
+ const rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
- if (cfp) {
+ if (cfp && cfp->iseq) {
if (pline) *pline = rb_vm_get_sourceline(cfp);
- return cfp->iseq->body->location.path;
+ return rb_iseq_path(cfp->iseq);
}
else {
if (pline) *pline = 0;
- return 0;
+ return Qnil;
}
}
-const char *
-rb_source_loc(int *pline)
+MJIT_FUNC_EXPORTED const char *
+rb_source_location_cstr(int *pline)
{
VALUE path = rb_source_location(pline);
- if (!path) return 0;
+ if (NIL_P(path)) return NULL;
return RSTRING_PTR(path);
}
rb_cref_t *
rb_vm_cref(void)
{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
+ const rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
if (cfp == NULL) {
return NULL;
}
-
return rb_vm_get_cref(cfp->ep);
}
rb_cref_t *
rb_vm_cref_replace_with_duplicated_cref(void)
{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
+ const rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
rb_cref_t *cref = vm_cref_replace_with_duplicated_cref(cfp->ep);
return cref;
}
@@ -1301,8 +1376,8 @@ rb_vm_cref_replace_with_duplicated_cref(void)
const rb_cref_t *
rb_vm_cref_in_context(VALUE self, VALUE cbase)
{
- rb_thread_t *th = GET_THREAD();
- const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
+ const rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
const rb_cref_t *cref;
if (cfp->self != self) return NULL;
if (!vm_env_cref_by_cref(cfp->ep)) return NULL;
@@ -1326,8 +1401,8 @@ debug_cref(rb_cref_t *cref)
VALUE
rb_vm_cbase(void)
{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
+ const rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
if (cfp == 0) {
rb_raise(rb_eRuntimeError, "Can't call on top of Fiber or Thread");
@@ -1369,7 +1444,7 @@ make_localjump_error(const char *mesg, VALUE value, int reason)
return exc;
}
-void
+MJIT_FUNC_EXPORTED void
rb_vm_localjump_error(const char *mesg, VALUE value, int reason)
{
VALUE exc = make_localjump_error(mesg, value, reason);
@@ -1379,44 +1454,44 @@ rb_vm_localjump_error(const char *mesg, VALUE value, int reason)
VALUE
rb_vm_make_jump_tag_but_local_jump(int state, VALUE val)
{
- VALUE result = Qnil;
+ const char *mesg;
- if (val == Qundef) {
- val = GET_THREAD()->tag->retval;
- }
switch (state) {
- case 0:
- break;
case TAG_RETURN:
- result = make_localjump_error("unexpected return", val, state);
+ mesg = "unexpected return";
break;
case TAG_BREAK:
- result = make_localjump_error("unexpected break", val, state);
+ mesg = "unexpected break";
break;
case TAG_NEXT:
- result = make_localjump_error("unexpected next", val, state);
+ mesg = "unexpected next";
break;
case TAG_REDO:
- result = make_localjump_error("unexpected redo", Qnil, state);
+ mesg = "unexpected redo";
+ val = Qnil;
break;
case TAG_RETRY:
- result = make_localjump_error("retry outside of rescue clause", Qnil, state);
+ mesg = "retry outside of rescue clause";
+ val = Qnil;
break;
default:
- break;
+ return Qnil;
}
- return result;
+ if (val == Qundef) {
+ val = GET_EC()->tag->retval;
+ }
+ return make_localjump_error(mesg, val, state);
}
+#if 0
void
rb_vm_jump_tag_but_local_jump(int state)
{
VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef);
if (!NIL_P(exc)) rb_exc_raise(exc);
- JUMP_TAG(state);
+ EC_JUMP_TAG(GET_EC(), state);
}
-
-NORETURN(static void vm_iter_break(rb_thread_t *th, VALUE val));
+#endif
static rb_control_frame_t *
next_not_local_frame(rb_control_frame_t *cfp)
@@ -1427,12 +1502,14 @@ next_not_local_frame(rb_control_frame_t *cfp)
return cfp;
}
+NORETURN(static void vm_iter_break(rb_execution_context_t *ec, VALUE val));
+
static void
-vm_iter_break(rb_thread_t *th, VALUE val)
+vm_iter_break(rb_execution_context_t *ec, VALUE val)
{
- rb_control_frame_t *cfp = next_not_local_frame(th->cfp);
+ rb_control_frame_t *cfp = next_not_local_frame(ec->cfp);
const VALUE *ep = VM_CF_PREV_EP(cfp);
- const rb_control_frame_t *target_cfp = rb_vm_search_cf_from_ep(th, cfp, ep);
+ const rb_control_frame_t *target_cfp = rb_vm_search_cf_from_ep(ec, cfp, ep);
#if 0 /* raise LocalJumpError */
if (!target_cfp) {
@@ -1440,21 +1517,20 @@ vm_iter_break(rb_thread_t *th, VALUE val)
}
#endif
- th->state = TAG_BREAK;
- th->errinfo = (VALUE)THROW_DATA_NEW(val, target_cfp, TAG_BREAK);
- TH_JUMP_TAG(th, TAG_BREAK);
+ ec->errinfo = (VALUE)THROW_DATA_NEW(val, target_cfp, TAG_BREAK);
+ EC_JUMP_TAG(ec, TAG_BREAK);
}
void
rb_iter_break(void)
{
- vm_iter_break(GET_THREAD(), Qnil);
+ vm_iter_break(GET_EC(), Qnil);
}
void
rb_iter_break_value(VALUE val)
{
- vm_iter_break(GET_THREAD(), val);
+ vm_iter_break(GET_EC(), val);
}
/* optimization: redefine management */
@@ -1475,9 +1551,22 @@ vm_redefinition_check_flag(VALUE klass)
if (klass == rb_cNilClass) return NIL_REDEFINED_OP_FLAG;
if (klass == rb_cTrueClass) return TRUE_REDEFINED_OP_FLAG;
if (klass == rb_cFalseClass) return FALSE_REDEFINED_OP_FLAG;
+ if (klass == rb_cProc) return PROC_REDEFINED_OP_FLAG;
return 0;
}
+static int
+vm_redefinition_check_method_type(const rb_method_definition_t *def)
+{
+ switch (def->type) {
+ case VM_METHOD_TYPE_CFUNC:
+ case VM_METHOD_TYPE_OPTIMIZED:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
static void
rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me, VALUE klass)
{
@@ -1485,7 +1574,7 @@ rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me, VALUE klass)
if (RB_TYPE_P(klass, T_ICLASS) && FL_TEST(klass, RICLASS_IS_ORIGIN)) {
klass = RBASIC_CLASS(klass);
}
- if (me->def->type == VM_METHOD_TYPE_CFUNC) {
+ if (vm_redefinition_check_method_type(me->def)) {
if (st_lookup(vm_opt_method_table, (st_data_t)me, &bop)) {
int flag = vm_redefinition_check_flag(klass);
@@ -1518,7 +1607,7 @@ add_opt_method(VALUE klass, ID mid, VALUE bop)
{
const rb_method_entry_t *me = rb_method_entry_at(klass, mid);
- if (me && me->def->type == VM_METHOD_TYPE_CFUNC) {
+ if (me && vm_redefinition_check_method_type(me->def)) {
st_insert(vm_opt_method_table, (st_data_t)me, (st_data_t)bop);
}
else {
@@ -1541,7 +1630,7 @@ vm_init_redefined_flag(void)
OP(MULT, MULT), (C(Integer), C(Float));
OP(DIV, DIV), (C(Integer), C(Float));
OP(MOD, MOD), (C(Integer), C(Float));
- OP(Eq, EQ), (C(Integer), C(Float), C(String));
+ OP(Eq, EQ), (C(Integer), C(Float), C(String), C(Symbol));
OP(Eqq, EQQ), (C(Integer), C(Float), C(Symbol), C(String),
C(NilClass), C(TrueClass), C(FalseClass));
OP(LT, LT), (C(Integer), C(Float));
@@ -1557,8 +1646,12 @@ vm_init_redefined_flag(void)
OP(Succ, SUCC), (C(Integer), C(String), C(Time));
OP(EqTilde, MATCH), (C(Regexp), C(String));
OP(Freeze, FREEZE), (C(String));
+ OP(UMinus, UMINUS), (C(String));
OP(Max, MAX), (C(Array));
OP(Min, MIN), (C(Array));
+ OP(Call, CALL), (C(Proc));
+ OP(And, AND), (C(Integer));
+ OP(Or, OR), (C(Integer));
#undef C
#undef OP
}
@@ -1575,10 +1668,8 @@ vm_frametype_name(const rb_control_frame_t *cfp)
case VM_FRAME_MAGIC_CLASS: return "class";
case VM_FRAME_MAGIC_TOP: return "top";
case VM_FRAME_MAGIC_CFUNC: return "cfunc";
- case VM_FRAME_MAGIC_PROC: return "proc";
case VM_FRAME_MAGIC_IFUNC: return "ifunc";
case VM_FRAME_MAGIC_EVAL: return "eval";
- case VM_FRAME_MAGIC_LAMBDA: return "lambda";
case VM_FRAME_MAGIC_RESCUE: return "rescue";
default:
rb_bug("unknown frame");
@@ -1586,34 +1677,109 @@ vm_frametype_name(const rb_control_frame_t *cfp)
}
#endif
+static VALUE
+frame_return_value(const struct vm_throw_data *err)
+{
+ if (THROW_DATA_P(err) &&
+ THROW_DATA_STATE(err) == TAG_BREAK &&
+ THROW_DATA_CONSUMED_P(err) == FALSE) {
+ return THROW_DATA_VAL(err);
+ }
+ else {
+ return Qnil;
+ }
+}
+
+#if 0
+/* for debug */
+static const char *
+frame_name(const rb_control_frame_t *cfp)
+{
+ unsigned long type = VM_FRAME_TYPE(cfp);
+#define C(t) if (type == VM_FRAME_MAGIC_##t) return #t
+ C(METHOD);
+ C(BLOCK);
+ C(CLASS);
+ C(TOP);
+ C(CFUNC);
+ C(PROC);
+ C(IFUNC);
+ C(EVAL);
+ C(LAMBDA);
+ C(RESCUE);
+ C(DUMMY);
+#undef C
+ return "unknown";
+}
+#endif
+
static void
-hook_before_rewind(rb_thread_t *th, rb_control_frame_t *cfp, int will_finish_vm_exec)
+hook_before_rewind(rb_execution_context_t *ec, const rb_control_frame_t *cfp,
+ int will_finish_vm_exec, int state, struct vm_throw_data *err)
{
- switch (VM_FRAME_TYPE(th->cfp)) {
- case VM_FRAME_MAGIC_METHOD:
- RUBY_DTRACE_METHOD_RETURN_HOOK(th, 0, 0);
- EXEC_EVENT_HOOK_AND_POP_FRAME(th, RUBY_EVENT_RETURN, th->cfp->self, 0, 0, 0, Qnil);
- break;
- case VM_FRAME_MAGIC_BLOCK:
- case VM_FRAME_MAGIC_LAMBDA:
- if (VM_FRAME_BMETHOD_P(th->cfp)) {
- EXEC_EVENT_HOOK(th, RUBY_EVENT_B_RETURN, th->cfp->self, 0, 0, 0, Qnil);
-
- if (!will_finish_vm_exec) {
- /* kick RUBY_EVENT_RETURN at invoke_block_from_c() for bmethod */
- EXEC_EVENT_HOOK_AND_POP_FRAME(th, RUBY_EVENT_RETURN, th->cfp->self,
- rb_vm_frame_method_entry(th->cfp)->def->original_id,
- rb_vm_frame_method_entry(th->cfp)->called_id,
- rb_vm_frame_method_entry(th->cfp)->owner, Qnil);
- }
- }
- else {
- EXEC_EVENT_HOOK_AND_POP_FRAME(th, RUBY_EVENT_B_RETURN, th->cfp->self, 0, 0, 0, Qnil);
- }
- break;
- case VM_FRAME_MAGIC_CLASS:
- EXEC_EVENT_HOOK_AND_POP_FRAME(th, RUBY_EVENT_END, th->cfp->self, 0, 0, 0, Qnil);
- break;
+ if (state == TAG_RAISE && RBASIC_CLASS(err) == rb_eSysStackError) {
+ return;
+ }
+ else {
+ const rb_iseq_t *iseq = cfp->iseq;
+ rb_hook_list_t *local_hooks = iseq->aux.exec.local_hooks;
+
+ switch (VM_FRAME_TYPE(ec->cfp)) {
+ case VM_FRAME_MAGIC_METHOD:
+ RUBY_DTRACE_METHOD_RETURN_HOOK(ec, 0, 0);
+ EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_RETURN, ec->cfp->self, 0, 0, 0, frame_return_value(err));
+
+ if (UNLIKELY(local_hooks && local_hooks->events & RUBY_EVENT_RETURN)) {
+ rb_exec_event_hook_orig(ec, local_hooks, RUBY_EVENT_RETURN,
+ ec->cfp->self, 0, 0, 0, frame_return_value(err), TRUE);
+ }
+
+ THROW_DATA_CONSUMED_SET(err);
+ break;
+ case VM_FRAME_MAGIC_BLOCK:
+ if (VM_FRAME_BMETHOD_P(ec->cfp)) {
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_B_RETURN, ec->cfp->self, 0, 0, 0, frame_return_value(err));
+ if (UNLIKELY(local_hooks && local_hooks->events & RUBY_EVENT_B_RETURN)) {
+ rb_exec_event_hook_orig(ec, local_hooks, RUBY_EVENT_B_RETURN,
+ ec->cfp->self, 0, 0, 0, frame_return_value(err), FALSE);
+ }
+
+ if (!will_finish_vm_exec) {
+ const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(ec->cfp);
+
+ /* kick RUBY_EVENT_RETURN at invoke_block_from_c() for bmethod */
+ EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_RETURN, ec->cfp->self,
+ rb_vm_frame_method_entry(ec->cfp)->def->original_id,
+ rb_vm_frame_method_entry(ec->cfp)->called_id,
+ rb_vm_frame_method_entry(ec->cfp)->owner,
+ frame_return_value(err));
+
+ VM_ASSERT(me->def->type == VM_METHOD_TYPE_BMETHOD);
+ local_hooks = me->def->body.bmethod.hooks;
+
+ if (UNLIKELY(local_hooks && local_hooks->events & RUBY_EVENT_RETURN)) {
+ rb_exec_event_hook_orig(ec, local_hooks, RUBY_EVENT_RETURN, ec->cfp->self,
+ rb_vm_frame_method_entry(ec->cfp)->def->original_id,
+ rb_vm_frame_method_entry(ec->cfp)->called_id,
+ rb_vm_frame_method_entry(ec->cfp)->owner,
+ frame_return_value(err), TRUE);
+ }
+ }
+ THROW_DATA_CONSUMED_SET(err);
+ }
+ else {
+ EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_B_RETURN, ec->cfp->self, 0, 0, 0, frame_return_value(err));
+ if (UNLIKELY(local_hooks && local_hooks->events & RUBY_EVENT_B_RETURN)) {
+ rb_exec_event_hook_orig(ec, local_hooks, RUBY_EVENT_B_RETURN,
+ ec->cfp->self, 0, 0, 0, frame_return_value(err), TRUE);
+ }
+ THROW_DATA_CONSUMED_SET(err);
+ }
+ break;
+ case VM_FRAME_MAGIC_CLASS:
+ EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_END, ec->cfp->self, 0, 0, 0, Qnil);
+ break;
+ }
}
}
@@ -1636,10 +1802,10 @@ hook_before_rewind(rb_thread_t *th, rb_control_frame_t *cfp, int will_finish_vm_
rb_iseq_t *iseq; // cfp[2], iseq
VALUE self; // cfp[3], self
const VALUE *ep; // cfp[4], env pointer
- const void *block_code; // cfp[5], blcok code
+ const void *block_code; // cfp[5], block code
};
- struct rb_captured_blcok {
+ struct rb_captured_block {
VALUE self;
VALUE *ep;
union code;
@@ -1695,28 +1861,55 @@ hook_before_rewind(rb_thread_t *th, rb_control_frame_t *cfp, int will_finish_vm_
VALUE *ep; // ep
void *code; //
};
+
+ If mjit_exec is already called before calling vm_exec, `mjit_enable_p` should
+ be FALSE to avoid calling `mjit_exec` twice.
*/
-static VALUE
-vm_exec(rb_thread_t *th)
+static inline VALUE
+vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state,
+ VALUE errinfo, VALUE *initial);
+
+MJIT_FUNC_EXPORTED VALUE
+vm_exec(rb_execution_context_t *ec, int mjit_enable_p)
{
- int state;
- VALUE result;
+ enum ruby_tag_type state;
+ VALUE result = Qundef;
VALUE initial = 0;
- struct vm_throw_data *err;
- TH_PUSH_TAG(th);
+ EC_PUSH_TAG(ec);
+
_tag.retval = Qnil;
- if ((state = EXEC_TAG()) == 0) {
- vm_loop_start:
- result = vm_exec_core(th, initial);
- if ((state = th->state) != 0) {
- err = (struct vm_throw_data *)result;
- th->state = 0;
- goto exception_handler;
- }
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ if (!mjit_enable_p || (result = mjit_exec(ec)) == Qundef) {
+ result = vm_exec_core(ec, initial);
+ }
+ goto vm_loop_start; /* fallback to the VM */
}
else {
+ result = ec->errinfo;
+ rb_ec_raised_reset(ec, RAISED_STACKOVERFLOW);
+ while ((result = vm_exec_handle_exception(ec, state, result, &initial)) == Qundef) {
+ /* caught a jump, exec the handler */
+ result = vm_exec_core(ec, initial);
+ vm_loop_start:
+ VM_ASSERT(ec->tag == &_tag);
+ /* when caught `throw`, `tag.state` is set. */
+ if ((state = _tag.state) == TAG_NONE) break;
+ _tag.state = TAG_NONE;
+ }
+ }
+ EC_POP_TAG();
+ return result;
+}
+
+static inline VALUE
+vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state,
+ VALUE errinfo, VALUE *initial)
+{
+ struct vm_throw_data *err = (struct vm_throw_data *)errinfo;
+
+ for (;;) {
unsigned int i;
const struct iseq_catch_table_entry *entry;
const struct iseq_catch_table *ct;
@@ -1726,26 +1919,23 @@ vm_exec(rb_thread_t *th)
VALUE type;
const rb_control_frame_t *escape_cfp;
- err = (struct vm_throw_data *)th->errinfo;
-
- exception_handler:
cont_pc = cont_sp = 0;
catch_iseq = NULL;
- while (th->cfp->pc == 0 || th->cfp->iseq == 0) {
- if (UNLIKELY(VM_FRAME_TYPE(th->cfp) == VM_FRAME_MAGIC_CFUNC)) {
- EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, th->cfp->self,
- rb_vm_frame_method_entry(th->cfp)->def->original_id,
- rb_vm_frame_method_entry(th->cfp)->called_id,
- rb_vm_frame_method_entry(th->cfp)->owner, Qnil);
- RUBY_DTRACE_CMETHOD_RETURN_HOOK(th,
- rb_vm_frame_method_entry(th->cfp)->owner,
- rb_vm_frame_method_entry(th->cfp)->def->original_id);
+ while (ec->cfp->pc == 0 || ec->cfp->iseq == 0) {
+ if (UNLIKELY(VM_FRAME_TYPE(ec->cfp) == VM_FRAME_MAGIC_CFUNC)) {
+ EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_C_RETURN, ec->cfp->self,
+ rb_vm_frame_method_entry(ec->cfp)->def->original_id,
+ rb_vm_frame_method_entry(ec->cfp)->called_id,
+ rb_vm_frame_method_entry(ec->cfp)->owner, Qnil);
+ RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec,
+ rb_vm_frame_method_entry(ec->cfp)->owner,
+ rb_vm_frame_method_entry(ec->cfp)->def->original_id);
}
- rb_vm_pop_frame(th);
+ rb_vm_pop_frame(ec);
}
- cfp = th->cfp;
+ cfp = ec->cfp;
epc = cfp->pc - cfp->iseq->body->iseq_encoded;
escape_cfp = NULL;
@@ -1771,12 +1961,12 @@ vm_exec(rb_thread_t *th)
}
}
}
- if (!catch_iseq) {
- th->errinfo = Qnil;
- result = THROW_DATA_VAL(err);
- hook_before_rewind(th, th->cfp, TRUE);
- rb_vm_pop_frame(th);
- goto finish_vme;
+ if (catch_iseq == NULL) {
+ ec->errinfo = Qnil;
+ THROW_DATA_CATCH_FRAME_SET(err, cfp + 1);
+ hook_before_rewind(ec, ec->cfp, TRUE, state, err);
+ rb_vm_pop_frame(ec);
+ return THROW_DATA_VAL(err);
}
}
/* through */
@@ -1784,12 +1974,12 @@ vm_exec(rb_thread_t *th)
else {
/* TAG_BREAK */
#if OPT_STACK_CACHING
- initial = THROW_DATA_VAL(err);
+ *initial = THROW_DATA_VAL(err);
#else
- *th->cfp->sp++ = THROW_DATA_VAL(err);
+ *ec->cfp->sp++ = THROW_DATA_VAL(err);
#endif
- th->errinfo = Qnil;
- goto vm_loop_start;
+ ec->errinfo = Qnil;
+ return Qundef;
}
}
}
@@ -1827,8 +2017,8 @@ vm_exec(rb_thread_t *th)
escape_cfp = THROW_DATA_CATCH_FRAME(err);
if (cfp == escape_cfp) {
cfp->pc = cfp->iseq->body->iseq_encoded + entry->cont;
- th->errinfo = Qnil;
- goto vm_loop_start;
+ ec->errinfo = Qnil;
+ return Qundef;
}
}
}
@@ -1855,14 +2045,14 @@ vm_exec(rb_thread_t *th)
if (state != TAG_REDO) {
#if OPT_STACK_CACHING
- initial = THROW_DATA_VAL(err);
+ *initial = THROW_DATA_VAL(err);
#else
- *th->cfp->sp++ = THROW_DATA_VAL(err);
+ *ec->cfp->sp++ = THROW_DATA_VAL(err);
#endif
}
- th->errinfo = Qnil;
- th->state = 0;
- goto vm_loop_start;
+ ec->errinfo = Qnil;
+ VM_ASSERT(ec->tag->state == TAG_NONE);
+ return Qundef;
}
}
}
@@ -1901,7 +2091,7 @@ vm_exec(rb_thread_t *th)
/* push block frame */
cfp->sp[0] = (VALUE)err;
- vm_push_frame(th, catch_iseq, VM_FRAME_MAGIC_RESCUE,
+ vm_push_frame(ec, catch_iseq, VM_FRAME_MAGIC_RESCUE,
cfp->self,
VM_GUARDED_PREV_EP(cfp->ep),
0, /* cref or me */
@@ -1911,29 +2101,25 @@ vm_exec(rb_thread_t *th)
catch_iseq->body->stack_max);
state = 0;
- th->state = 0;
- th->errinfo = Qnil;
- goto vm_loop_start;
+ ec->tag->state = TAG_NONE;
+ ec->errinfo = Qnil;
+
+ return Qundef;
}
else {
- /* skip frame */
- hook_before_rewind(th, th->cfp, FALSE);
-
- if (VM_FRAME_FINISHED_P(th->cfp)) {
- rb_vm_pop_frame(th);
- th->errinfo = (VALUE)err;
- TH_TMPPOP_TAG();
- TH_JUMP_TAG(th, state);
+ hook_before_rewind(ec, ec->cfp, FALSE, state, err);
+
+ if (VM_FRAME_FINISHED_P(ec->cfp)) {
+ rb_vm_pop_frame(ec);
+ ec->errinfo = (VALUE)err;
+ ec->tag = ec->tag->prev;
+ EC_JUMP_TAG(ec, state);
}
else {
- rb_vm_pop_frame(th);
- goto exception_handler;
+ rb_vm_pop_frame(ec);
}
}
}
- finish_vme:
- TH_POP_TAG();
- return result;
}
/* misc */
@@ -1941,21 +2127,21 @@ vm_exec(rb_thread_t *th)
VALUE
rb_iseq_eval(const rb_iseq_t *iseq)
{
- rb_thread_t *th = GET_THREAD();
+ rb_execution_context_t *ec = GET_EC();
VALUE val;
- vm_set_top_stack(th, iseq);
- val = vm_exec(th);
+ vm_set_top_stack(ec, iseq);
+ val = vm_exec(ec, TRUE);
return val;
}
VALUE
rb_iseq_eval_main(const rb_iseq_t *iseq)
{
- rb_thread_t *th = GET_THREAD();
+ rb_execution_context_t *ec = GET_EC();
VALUE val;
- vm_set_main_stack(th, iseq);
- val = vm_exec(th);
+ vm_set_main_stack(ec, iseq);
+ val = vm_exec(ec, TRUE);
return val;
}
@@ -1976,70 +2162,42 @@ rb_vm_control_frame_id_and_class(const rb_control_frame_t *cfp, ID *idp, ID *cal
}
int
-rb_thread_method_id_and_class(rb_thread_t *th, ID *idp, ID *called_idp, VALUE *klassp)
+rb_ec_frame_method_id_and_class(const rb_execution_context_t *ec, ID *idp, ID *called_idp, VALUE *klassp)
{
- return rb_vm_control_frame_id_and_class(th->cfp, idp, called_idp, klassp);
+ return rb_vm_control_frame_id_and_class(ec->cfp, idp, called_idp, klassp);
}
int
-rb_frame_method_id_and_class(ID *idp, ID *called_idp, VALUE *klassp)
+rb_frame_method_id_and_class(ID *idp, VALUE *klassp)
{
- return rb_thread_method_id_and_class(GET_THREAD(), idp, called_idp, klassp);
-}
-
-VALUE
-rb_thread_current_status(const rb_thread_t *th)
-{
- const rb_control_frame_t *cfp = th->cfp;
- const rb_callable_method_entry_t *me;
- VALUE str = Qnil;
-
- if (cfp->iseq != 0) {
- if (cfp->pc != 0) {
- const rb_iseq_t *iseq = cfp->iseq;
- int line_no = rb_vm_get_sourceline(cfp);
- str = rb_sprintf("%"PRIsVALUE":%d:in `%"PRIsVALUE"'",
- iseq->body->location.path, line_no, iseq->body->location.label);
- }
- }
- else if ((me = rb_vm_frame_method_entry(cfp)) && me->def->original_id) {
- str = rb_sprintf("`%"PRIsVALUE"#%"PRIsVALUE"' (cfunc)",
- rb_class_path(me->owner),
- rb_id2str(me->def->original_id));
- }
-
- return str;
+ return rb_ec_frame_method_id_and_class(GET_EC(), idp, 0, klassp);
}
VALUE
rb_vm_call_cfunc(VALUE recv, VALUE (*func)(VALUE), VALUE arg,
VALUE block_handler, VALUE filename)
{
- rb_thread_t *th = GET_THREAD();
- const rb_control_frame_t *reg_cfp = th->cfp;
+ rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *reg_cfp = ec->cfp;
const rb_iseq_t *iseq = rb_iseq_new(0, filename, filename, Qnil, 0, ISEQ_TYPE_TOP);
VALUE val;
- vm_push_frame(th, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH,
+ vm_push_frame(ec, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH,
recv, block_handler,
- (VALUE)vm_cref_new_toplevel(th), /* cref or me */
+ (VALUE)vm_cref_new_toplevel(ec), /* cref or me */
0, reg_cfp->sp, 0, 0);
val = (*func)(arg);
- rb_vm_pop_frame(th);
+ rb_vm_pop_frame(ec);
return val;
}
/* vm */
-void rb_vm_trace_mark_event_hooks(rb_hook_list_t *hooks);
-
void
rb_vm_mark(void *ptr)
{
- int i;
-
RUBY_MARK_ENTER("vm");
RUBY_GC_INFO("-------------------------------------------------\n");
if (ptr) {
@@ -2058,6 +2216,7 @@ rb_vm_mark(void *ptr)
rb_gc_mark(vm->loaded_features);
rb_gc_mark(vm->loaded_features_snapshot);
rb_gc_mark(vm->top_self);
+ rb_gc_mark(vm->orig_progname);
RUBY_MARK_UNLESS_NULL(vm->coverages);
rb_gc_mark(vm->defined_module_hash);
@@ -2065,22 +2224,22 @@ rb_vm_mark(void *ptr)
rb_mark_tbl(vm->loading_table);
}
- rb_vm_trace_mark_event_hooks(&vm->event_hooks);
+ rb_hook_list_mark(&vm->global_hooks);
- for (i = 0; i < RUBY_NSIG; i++) {
- if (vm->trap_list[i].cmd)
- rb_gc_mark(vm->trap_list[i].cmd);
- }
+ rb_gc_mark_values(RUBY_NSIG, vm->trap_list.cmd);
+
+ mjit_mark();
}
RUBY_MARK_LEAVE("vm");
}
+#undef rb_vm_register_special_exception
void
-rb_vm_register_special_exception(enum ruby_special_exceptions sp, VALUE cls, const char *mesg)
+rb_vm_register_special_exception_str(enum ruby_special_exceptions sp, VALUE cls, VALUE mesg)
{
rb_vm_t *vm = GET_VM();
- VALUE exc = rb_exc_new3(cls, rb_obj_freeze(rb_str_new2(mesg)));
+ VALUE exc = rb_exc_new3(cls, rb_obj_freeze(mesg));
OBJ_TAINT(exc);
OBJ_FREEZE(exc);
((VALUE *)vm->special_exceptions)[sp] = exc;
@@ -2104,6 +2263,8 @@ free_loading_table_entry(st_data_t key, st_data_t value, st_data_t arg)
return ST_DELETE;
}
+extern void rb_native_mutex_destroy(rb_nativethread_lock_t *lock);
+
int
ruby_vm_destruct(rb_vm_t *vm)
{
@@ -2114,7 +2275,7 @@ ruby_vm_destruct(rb_vm_t *vm)
struct rb_objspace *objspace = vm->objspace;
vm->main_thread = 0;
if (th) {
- rb_fiber_reset_root_local_storage(th->self);
+ rb_fiber_reset_root_local_storage(th);
thread_free(th);
}
rb_vm_living_threads_init(vm);
@@ -2129,12 +2290,15 @@ ruby_vm_destruct(rb_vm_t *vm)
vm->frozen_strings = 0;
}
rb_vm_gvl_destroy(vm);
+ RB_ALTSTACK_FREE(vm->main_altstack);
if (objspace) {
rb_objspace_free(objspace);
}
+ rb_native_mutex_destroy(&vm->waitpid_lock);
+ rb_native_mutex_destroy(&vm->workqueue_lock);
/* after freeing objspace, you *can't* use ruby_xfree() */
ruby_mimfree(vm);
- ruby_current_vm = 0;
+ ruby_current_vm_ptr = NULL;
}
RUBY_FREE_LEAVE("vm");
return 0;
@@ -2240,6 +2404,7 @@ vm_init2(rb_vm_t *vm)
{
MEMZERO(vm, rb_vm_t, 1);
rb_vm_living_threads_init(vm);
+ vm->thread_report_on_exception = 1;
vm->src_encoding_index = -1;
vm_default_params_setup(vm);
@@ -2253,26 +2418,25 @@ vm_init2(rb_vm_t *vm)
#define RECYCLE_MAX 64
static VALUE *thread_recycle_stack_slot[RECYCLE_MAX];
static int thread_recycle_stack_count = 0;
+#endif /* USE_THREAD_DATA_RECYCLE */
-static VALUE *
-thread_recycle_stack(size_t size)
+VALUE *
+rb_thread_recycle_stack(size_t size)
{
- if (thread_recycle_stack_count) {
+#if USE_THREAD_DATA_RECYCLE
+ if (thread_recycle_stack_count > 0) {
/* TODO: check stack size if stack sizes are variable */
return thread_recycle_stack_slot[--thread_recycle_stack_count];
}
- else {
- return ALLOC_N(VALUE, size);
- }
+#endif /* USE_THREAD_DATA_RECYCLE */
+ return ALLOC_N(VALUE, size);
}
-#else
-#define thread_recycle_stack(size) ALLOC_N(VALUE, (size))
-#endif
-
void
rb_thread_recycle_stack_release(VALUE *stack)
{
+ VM_ASSERT(stack != NULL);
+
#if USE_THREAD_DATA_RECYCLE
if (thread_recycle_stack_count < RECYCLE_MAX) {
thread_recycle_stack_slot[thread_recycle_stack_count++] = stack;
@@ -2282,110 +2446,120 @@ rb_thread_recycle_stack_release(VALUE *stack)
ruby_xfree(stack);
}
-void rb_fiber_mark_self(rb_fiber_t *fib);
-
void
-rb_thread_mark(void *ptr)
+rb_execution_context_mark(const rb_execution_context_t *ec)
{
- rb_thread_t *th = ptr;
- RUBY_MARK_ENTER("thread");
+#if VM_CHECK_MODE > 0
+ void rb_ec_verify(const rb_execution_context_t *ec); /* cont.c */
+ rb_ec_verify(ec);
+#endif
- if (th->stack) {
- VALUE *p = th->stack;
- VALUE *sp = th->cfp->sp;
- rb_control_frame_t *cfp = th->cfp;
- rb_control_frame_t *limit_cfp = (void *)(th->stack + th->stack_size);
+ /* mark VM stack */
+ if (ec->vm_stack) {
+ VALUE *p = ec->vm_stack;
+ VALUE *sp = ec->cfp->sp;
+ rb_control_frame_t *cfp = ec->cfp;
+ rb_control_frame_t *limit_cfp = (void *)(ec->vm_stack + ec->vm_stack_size);
rb_gc_mark_values((long)(sp - p), p);
while (cfp != limit_cfp) {
-#if VM_CHECK_MODE > 0
const VALUE *ep = cfp->ep;
- VM_ASSERT(!!VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED) == vm_ep_in_heap_p_(th, ep));
-#endif
+ VM_ASSERT(!!VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED) == vm_ep_in_heap_p_(ec, ep));
rb_gc_mark(cfp->self);
rb_gc_mark((VALUE)cfp->iseq);
rb_gc_mark((VALUE)cfp->block_code);
+ if (!VM_ENV_LOCAL_P(ep)) {
+ const VALUE *prev_ep = VM_ENV_PREV_EP(ep);
+ if (VM_ENV_FLAGS(prev_ep, VM_ENV_FLAG_ESCAPED)) {
+ rb_gc_mark(prev_ep[VM_ENV_DATA_INDEX_ENV]);
+ }
+ }
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
}
}
+ /* mark machine stack */
+ if (ec->machine.stack_start && ec->machine.stack_end &&
+ ec != GET_EC() /* marked for current ec at the first stage of marking */
+ ) {
+ rb_gc_mark_machine_stack(ec);
+ rb_gc_mark_locations((VALUE *)&ec->machine.regs,
+ (VALUE *)(&ec->machine.regs) +
+ sizeof(ec->machine.regs) / (sizeof(VALUE)));
+ }
+
+ RUBY_MARK_UNLESS_NULL(ec->errinfo);
+ RUBY_MARK_UNLESS_NULL(ec->root_svar);
+ rb_mark_tbl(ec->local_storage);
+ RUBY_MARK_UNLESS_NULL(ec->local_storage_recursive_hash);
+ RUBY_MARK_UNLESS_NULL(ec->local_storage_recursive_hash_for_trace);
+ RUBY_MARK_UNLESS_NULL(ec->private_const_reference);
+}
+
+void rb_fiber_mark_self(rb_fiber_t *fib);
+void rb_threadptr_root_fiber_setup(rb_thread_t *th);
+void rb_threadptr_root_fiber_release(rb_thread_t *th);
+
+static void
+thread_mark(void *ptr)
+{
+ rb_thread_t *th = ptr;
+ RUBY_MARK_ENTER("thread");
+ rb_fiber_mark_self(th->ec->fiber_ptr);
+
/* mark ruby objects */
- RUBY_MARK_UNLESS_NULL(th->first_proc);
- if (th->first_proc) RUBY_MARK_UNLESS_NULL(th->first_args);
+ switch (th->invoke_type) {
+ case thread_invoke_type_proc:
+ RUBY_MARK_UNLESS_NULL(th->invoke_arg.proc.proc);
+ RUBY_MARK_UNLESS_NULL(th->invoke_arg.proc.args);
+ break;
+ case thread_invoke_type_func:
+ rb_gc_mark_maybe((VALUE)th->invoke_arg.func.arg);
+ break;
+ default:
+ break;
+ }
RUBY_MARK_UNLESS_NULL(th->thgroup);
RUBY_MARK_UNLESS_NULL(th->value);
- RUBY_MARK_UNLESS_NULL(th->errinfo);
RUBY_MARK_UNLESS_NULL(th->pending_interrupt_queue);
RUBY_MARK_UNLESS_NULL(th->pending_interrupt_mask_stack);
- RUBY_MARK_UNLESS_NULL(th->root_svar);
RUBY_MARK_UNLESS_NULL(th->top_self);
RUBY_MARK_UNLESS_NULL(th->top_wrapper);
- rb_fiber_mark_self(th->fiber);
- rb_fiber_mark_self(th->root_fiber);
+ if (th->root_fiber) rb_fiber_mark_self(th->root_fiber);
RUBY_MARK_UNLESS_NULL(th->stat_insn_usage);
RUBY_MARK_UNLESS_NULL(th->last_status);
-
RUBY_MARK_UNLESS_NULL(th->locking_mutex);
-
- rb_mark_tbl(th->local_storage);
- RUBY_MARK_UNLESS_NULL(th->local_storage_recursive_hash);
- RUBY_MARK_UNLESS_NULL(th->local_storage_recursive_hash_for_trace);
-
- if (GET_THREAD() != th && th->machine.stack_start && th->machine.stack_end) {
- rb_gc_mark_machine_stack(th);
- rb_gc_mark_locations((VALUE *)&th->machine.regs,
- (VALUE *)(&th->machine.regs) +
- sizeof(th->machine.regs) / sizeof(VALUE));
- }
-
RUBY_MARK_UNLESS_NULL(th->name);
- rb_vm_trace_mark_event_hooks(&th->event_hooks);
-
RUBY_MARK_LEAVE("thread");
}
static void
thread_free(void *ptr)
{
- rb_thread_t *th;
+ rb_thread_t *th = ptr;
RUBY_FREE_ENTER("thread");
- if (ptr) {
- th = ptr;
-
- if (!th->root_fiber) {
- RUBY_FREE_UNLESS_NULL(th->stack);
- }
-
- if (th->locking_mutex != Qfalse) {
- rb_bug("thread_free: locking_mutex must be NULL (%p:%p)", (void *)th, (void *)th->locking_mutex);
- }
- if (th->keeping_mutexes != NULL) {
- rb_bug("thread_free: keeping_mutexes must be NULL (%p:%p)", (void *)th, (void *)th->keeping_mutexes);
- }
+ if (th->locking_mutex != Qfalse) {
+ rb_bug("thread_free: locking_mutex must be NULL (%p:%p)", (void *)th, (void *)th->locking_mutex);
+ }
+ if (th->keeping_mutexes != NULL) {
+ rb_bug("thread_free: keeping_mutexes must be NULL (%p:%p)", (void *)th, (void *)th->keeping_mutexes);
+ }
- if (th->local_storage) {
- st_free_table(th->local_storage);
- }
+ rb_threadptr_root_fiber_release(th);
- if (th->vm && th->vm->main_thread == th) {
- RUBY_GC_INFO("main thread\n");
- }
- else {
-#ifdef USE_SIGALTSTACK
- if (th->altstack) {
- free(th->altstack);
- }
-#endif
- ruby_xfree(ptr);
- }
- if (ruby_current_thread == th)
- ruby_current_thread = NULL;
+ if (th->vm && th->vm->main_thread == th) {
+ RUBY_GC_INFO("main thread\n");
+ }
+ else {
+ ruby_xfree(ptr);
}
+
RUBY_FREE_LEAVE("thread");
}
@@ -2396,10 +2570,10 @@ thread_memsize(const void *ptr)
size_t size = sizeof(rb_thread_t);
if (!th->root_fiber) {
- size += th->stack_size * sizeof(VALUE);
+ size += th->ec->vm_stack_size * sizeof(VALUE);
}
- if (th->local_storage) {
- size += st_memsize(th->local_storage);
+ if (th->ec->local_storage) {
+ size += st_memsize(th->ec->local_storage);
}
return size;
}
@@ -2408,7 +2582,7 @@ thread_memsize(const void *ptr)
const rb_data_type_t ruby_threadptr_data_type = {
"VM/thread",
{
- rb_thread_mark,
+ thread_mark,
thread_free,
thread_memsize,
},
@@ -2440,32 +2614,28 @@ static void
th_init(rb_thread_t *th, VALUE self)
{
th->self = self;
+ rb_threadptr_root_fiber_setup(th);
- /* allocate thread stack */
-#ifdef USE_SIGALTSTACK
- /* altstack of main thread is reallocated in another place */
- th->altstack = malloc(rb_sigaltstack_size());
-#endif
- /* th->stack_size is word number.
- * th->vm->default_params.thread_vm_stack_size is byte size.
- */
- th->stack_size = th->vm->default_params.thread_vm_stack_size / sizeof(VALUE);
- th->stack = thread_recycle_stack(th->stack_size);
+ {
+ /* vm_stack_size is word number.
+ * th->vm->default_params.thread_vm_stack_size is byte size. */
+ size_t size = th->vm->default_params.thread_vm_stack_size / sizeof(VALUE);
+ rb_ec_set_vm_stack(th->ec, rb_thread_recycle_stack(size), size);
+ }
- th->cfp = (void *)(th->stack + th->stack_size);
+ th->ec->cfp = (void *)(th->ec->vm_stack + th->ec->vm_stack_size);
- vm_push_frame(th, 0 /* dummy iseq */, VM_FRAME_MAGIC_DUMMY | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH | VM_FRAME_FLAG_CFRAME /* dummy frame */,
+ vm_push_frame(th->ec, 0 /* dummy iseq */, VM_FRAME_MAGIC_DUMMY | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH | VM_FRAME_FLAG_CFRAME /* dummy frame */,
Qnil /* dummy self */, VM_BLOCK_HANDLER_NONE /* dummy block ptr */,
0 /* dummy cref/me */,
- 0 /* dummy pc */, th->stack, 0, 0);
+ 0 /* dummy pc */, th->ec->vm_stack, 0, 0);
th->status = THREAD_RUNNABLE;
- th->errinfo = Qnil;
th->last_status = Qnil;
- th->waiting_fd = -1;
- th->root_svar = Qfalse;
- th->local_storage_recursive_hash = Qnil;
- th->local_storage_recursive_hash_for_trace = Qnil;
+ th->ec->errinfo = Qnil;
+ th->ec->root_svar = Qfalse;
+ th->ec->local_storage_recursive_hash = Qnil;
+ th->ec->local_storage_recursive_hash_for_trace = Qnil;
#ifdef NON_SCALAR_THREAD_ID
th->thread_id_string[0] = '\0';
#endif
@@ -2474,14 +2644,14 @@ th_init(rb_thread_t *th, VALUE self)
th->retval = Qundef;
#endif
th->name = Qnil;
+ th->report_on_exception = th->vm->thread_report_on_exception;
}
static VALUE
ruby_thread_init(VALUE self)
{
- rb_thread_t *th;
+ rb_thread_t *th = rb_thread_ptr(self);
rb_vm_t *vm = GET_THREAD()->vm;
- GetThreadPtr(self, th);
th->vm = vm;
th_init(th, self);
@@ -2489,7 +2659,7 @@ ruby_thread_init(VALUE self)
th->top_wrapper = 0;
th->top_self = rb_vm_top_self();
- th->root_svar = Qfalse;
+ th->ec->root_svar = Qfalse;
return self;
}
@@ -2502,7 +2672,7 @@ rb_thread_alloc(VALUE klass)
}
static void
-vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval, int is_singleton)
+vm_define_method(VALUE obj, ID id, VALUE iseqval, int is_singleton)
{
VALUE klass;
rb_method_visibility_t visi;
@@ -2530,19 +2700,19 @@ vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval, int is_single
}
#define REWIND_CFP(expr) do { \
- rb_thread_t *th__ = GET_THREAD(); \
- VALUE *const curr_sp = (th__->cfp++)->sp; \
- VALUE *const saved_sp = th__->cfp->sp; \
- th__->cfp->sp = curr_sp; \
+ rb_execution_context_t *ec__ = GET_EC(); \
+ VALUE *const curr_sp = (ec__->cfp++)->sp; \
+ VALUE *const saved_sp = ec__->cfp->sp; \
+ ec__->cfp->sp = curr_sp; \
expr; \
- (th__->cfp--)->sp = saved_sp; \
+ (ec__->cfp--)->sp = saved_sp; \
} while (0)
static VALUE
m_core_define_method(VALUE self, VALUE sym, VALUE iseqval)
{
REWIND_CFP({
- vm_define_method(GET_THREAD(), Qnil, SYM2ID(sym), iseqval, FALSE);
+ vm_define_method(Qnil, SYM2ID(sym), iseqval, FALSE);
});
return sym;
}
@@ -2551,7 +2721,7 @@ static VALUE
m_core_define_singleton_method(VALUE self, VALUE cbase, VALUE sym, VALUE iseqval)
{
REWIND_CFP({
- vm_define_method(GET_THREAD(), cbase, SYM2ID(sym), iseqval, TRUE);
+ vm_define_method(cbase, SYM2ID(sym), iseqval, TRUE);
});
return sym;
}
@@ -2591,50 +2761,14 @@ m_core_set_postexe(VALUE self)
return Qnil;
}
-static VALUE core_hash_merge_ary(VALUE hash, VALUE ary);
-static VALUE core_hash_from_ary(VALUE ary);
-static VALUE core_hash_merge_kwd(int argc, VALUE *argv);
+static VALUE core_hash_merge_kwd(VALUE hash, VALUE kw);
static VALUE
core_hash_merge(VALUE hash, long argc, const VALUE *argv)
{
- long i;
-
+ Check_Type(hash, T_HASH);
VM_ASSERT(argc % 2 == 0);
- for (i=0; i<argc; i+=2) {
- rb_hash_aset(hash, argv[i], argv[i+1]);
- }
- return hash;
-}
-
-static VALUE
-m_core_hash_from_ary(VALUE self, VALUE ary)
-{
- VALUE hash;
- REWIND_CFP(hash = core_hash_from_ary(ary));
- return hash;
-}
-
-static VALUE
-core_hash_from_ary(VALUE ary)
-{
- VALUE hash = rb_hash_new();
-
- RUBY_DTRACE_CREATE_HOOK(HASH, RARRAY_LEN(ary));
- return core_hash_merge_ary(hash, ary);
-}
-
-static VALUE
-m_core_hash_merge_ary(VALUE self, VALUE hash, VALUE ary)
-{
- REWIND_CFP(core_hash_merge_ary(hash, ary));
- return hash;
-}
-
-static VALUE
-core_hash_merge_ary(VALUE hash, VALUE ary)
-{
- core_hash_merge(hash, RARRAY_LEN(ary), RARRAY_CONST_PTR(ary));
+ rb_hash_bulk_insert(argc, argv, hash);
return hash;
}
@@ -2648,40 +2782,58 @@ m_core_hash_merge_ptr(int argc, VALUE *argv, VALUE recv)
return hash;
}
+static void
+kw_check_symbol(VALUE key)
+{
+ if (!SYMBOL_P(key)) {
+ rb_raise(rb_eTypeError, "hash key %+"PRIsVALUE" is not a Symbol",
+ key);
+ }
+}
static int
kwmerge_i(VALUE key, VALUE value, VALUE hash)
{
- Check_Type(key, T_SYMBOL);
+ kw_check_symbol(key);
rb_hash_aset(hash, key, value);
return ST_CONTINUE;
}
-static int
-kwcheck_i(VALUE key, VALUE value, VALUE hash)
+static VALUE
+m_core_hash_merge_kwd(VALUE recv, VALUE hash, VALUE kw)
{
- Check_Type(key, T_SYMBOL);
- return ST_CONTINUE;
+ REWIND_CFP(hash = core_hash_merge_kwd(hash, kw));
+ return hash;
}
static VALUE
-m_core_hash_merge_kwd(int argc, VALUE *argv, VALUE recv)
+core_hash_merge_kwd(VALUE hash, VALUE kw)
{
- VALUE hash;
- REWIND_CFP(hash = core_hash_merge_kwd(argc, argv));
+ rb_hash_foreach(rb_to_hash_type(kw), kwmerge_i, hash);
return hash;
}
+/* Returns true if JIT is enabled */
static VALUE
-core_hash_merge_kwd(int argc, VALUE *argv)
-{
- VALUE hash, kw;
- rb_check_arity(argc, 1, 2);
- hash = argv[0];
- kw = argv[argc-1];
- kw = rb_convert_type(kw, T_HASH, "Hash", "to_hash");
- if (argc < 2) hash = kw;
- rb_hash_foreach(kw, argc < 2 ? kwcheck_i : kwmerge_i, hash);
- return hash;
+mjit_enabled_p(void)
+{
+ return mjit_enabled ? Qtrue : Qfalse;
+}
+
+static VALUE
+mjit_pause_m(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
+{
+ VALUE options = Qnil;
+ VALUE wait = Qtrue;
+ rb_scan_args(argc, argv, "0:", &options);
+
+ if (!NIL_P(options)) {
+ static ID keyword_ids[1];
+ if (!keyword_ids[0])
+ keyword_ids[0] = rb_intern("wait");
+ rb_get_kwargs(options, keyword_ids, 0, 1, &wait);
+ }
+
+ return mjit_pause(RTEST(wait));
}
extern VALUE *rb_gc_stack_start;
@@ -2731,14 +2883,23 @@ static VALUE usage_analysis_operand_stop(VALUE self);
static VALUE usage_analysis_register_stop(VALUE self);
#endif
+VALUE rb_resolve_feature_path(VALUE klass, VALUE fname);
+
void
Init_VM(void)
{
VALUE opts;
VALUE klass;
VALUE fcore;
+ VALUE mjit;
- /* ::RubyVM */
+ /*
+ * Document-class: RubyVM
+ *
+ * The RubyVM module provides some access to Ruby internals.
+ * This module is for very limited purposes, such as debugging,
+ * prototyping, and research. Normal users must not use it.
+ */
rb_cRubyVM = rb_define_class("RubyVM", rb_cObject);
rb_undef_alloc_func(rb_cRubyVM);
rb_undef_method(CLASS_OF(rb_cRubyVM), "new");
@@ -2754,10 +2915,8 @@ Init_VM(void)
rb_define_method_id(klass, id_core_define_method, m_core_define_method, 2);
rb_define_method_id(klass, id_core_define_singleton_method, m_core_define_singleton_method, 3);
rb_define_method_id(klass, id_core_set_postexe, m_core_set_postexe, 0);
- rb_define_method_id(klass, id_core_hash_from_ary, m_core_hash_from_ary, 1);
- rb_define_method_id(klass, id_core_hash_merge_ary, m_core_hash_merge_ary, 2);
rb_define_method_id(klass, id_core_hash_merge_ptr, m_core_hash_merge_ptr, -1);
- rb_define_method_id(klass, id_core_hash_merge_kwd, m_core_hash_merge_kwd, -1);
+ rb_define_method_id(klass, id_core_hash_merge_kwd, m_core_hash_merge_kwd, 2);
rb_define_method_id(klass, idProc, rb_block_proc, 0);
rb_define_method_id(klass, idLambda, rb_block_lambda, 0);
rb_obj_freeze(fcore);
@@ -2766,6 +2925,12 @@ Init_VM(void)
rb_gc_register_mark_object(fcore);
rb_mRubyVMFrozenCore = fcore;
+ /* RubyVM::MJIT */
+ mjit = rb_define_module_under(rb_cRubyVM, "MJIT");
+ rb_define_singleton_method(mjit, "enabled?", mjit_enabled_p, 0);
+ rb_define_singleton_method(mjit, "pause", mjit_pause_m, -1);
+ rb_define_singleton_method(mjit, "resume", mjit_resume, 0);
+
/*
* Document-class: Thread
*
@@ -2978,9 +3143,9 @@ Init_VM(void)
/* VM bootstrap: phase 2 */
{
- rb_vm_t *vm = ruby_current_vm;
+ rb_vm_t *vm = ruby_current_vm_ptr;
rb_thread_t *th = GET_THREAD();
- VALUE filename = rb_fstring_cstr("<main>");
+ VALUE filename = rb_fstring_lit("<main>");
const rb_iseq_t *iseq = rb_iseq_new(0, filename, filename, Qnil, 0, ISEQ_TYPE_TOP);
volatile VALUE th_self;
@@ -3000,12 +3165,12 @@ Init_VM(void)
rb_vm_living_threads_insert(vm, th);
rb_gc_register_mark_object((VALUE)iseq);
- th->cfp->iseq = iseq;
- th->cfp->pc = iseq->body->iseq_encoded;
- th->cfp->self = th->top_self;
+ th->ec->cfp->iseq = iseq;
+ th->ec->cfp->pc = iseq->body->iseq_encoded;
+ th->ec->cfp->self = th->top_self;
- VM_ENV_FLAGS_UNSET(th->cfp->ep, VM_FRAME_FLAG_CFRAME);
- VM_STACK_ENV_WRITE(th->cfp->ep, VM_ENV_DATA_INDEX_ME_CREF, (VALUE)vm_cref_new(rb_cObject, METHOD_VISI_PRIVATE, FALSE, NULL, FALSE));
+ VM_ENV_FLAGS_UNSET(th->ec->cfp->ep, VM_FRAME_FLAG_CFRAME);
+ VM_STACK_ENV_WRITE(th->ec->cfp->ep, VM_ENV_DATA_INDEX_ME_CREF, (VALUE)vm_cref_new(rb_cObject, METHOD_VISI_PRIVATE, FALSE, NULL, FALSE));
/*
* The Binding of the top level scope
@@ -3014,18 +3179,26 @@ Init_VM(void)
}
vm_init_redefined_flag();
+ rb_block_param_proxy = rb_obj_alloc(rb_cObject);
+ rb_add_method(rb_singleton_class(rb_block_param_proxy), idCall, VM_METHOD_TYPE_OPTIMIZED,
+ (void *)OPTIMIZED_METHOD_TYPE_BLOCK_CALL, METHOD_VISI_PUBLIC);
+ rb_obj_freeze(rb_block_param_proxy);
+ rb_gc_register_mark_object(rb_block_param_proxy);
+
/* vm_backtrace.c */
Init_vm_backtrace();
- VM_PROFILE_ATEXIT();
+
+ rb_define_singleton_method(rb_cRubyVM, "resolve_feature_path", rb_resolve_feature_path, 1);
}
void
rb_vm_set_progname(VALUE filename)
{
rb_thread_t *th = GET_VM()->main_thread;
- rb_control_frame_t *cfp = (void *)(th->stack + th->stack_size);
+ rb_control_frame_t *cfp = (void *)(th->ec->vm_stack + th->ec->vm_stack_size);
--cfp;
- RB_OBJ_WRITE(cfp->iseq, &cfp->iseq->body->location.path, filename);
+
+ rb_iseq_pathobj_set(cfp->iseq, rb_str_dup(filename), rb_iseq_realpath(cfp->iseq));
}
extern const struct st_hash_type rb_fstring_hash_type;
@@ -3041,15 +3214,15 @@ Init_BareVM(void)
exit(EXIT_FAILURE);
}
MEMZERO(th, rb_thread_t, 1);
- rb_thread_set_current_raw(th);
-
vm_init2(vm);
+
vm->objspace = rb_objspace_alloc();
- ruby_current_vm = vm;
+ ruby_current_vm_ptr = vm;
- Init_native_thread();
+ Init_native_thread(th);
th->vm = vm;
th_init(th, 0);
+ rb_thread_set_current_raw(th);
ruby_thread_init_stack(th);
}
@@ -3205,7 +3378,7 @@ vm_analysis_operand(int insn, int n, VALUE op)
HASH_ASET(ihash, INT2FIX(n), ophash);
}
/* intern */
- valstr = rb_insn_operand_intern(GET_THREAD()->cfp->iseq, insn, n, op, 0, 0, 0, 0);
+ valstr = rb_insn_operand_intern(GET_EC()->cfp->iseq, insn, n, op, 0, 0, 0, 0);
/* set count */
if ((cv = rb_hash_aref(ophash, valstr)) == Qnil) {
@@ -3260,9 +3433,9 @@ vm_analysis_register(int reg, int isset)
#undef HASH_ASET
-void (*ruby_vm_collect_usage_func_insn)(int insn) = vm_analysis_insn;
-void (*ruby_vm_collect_usage_func_operand)(int insn, int n, VALUE op) = vm_analysis_operand;
-void (*ruby_vm_collect_usage_func_register)(int reg, int isset) = vm_analysis_register;
+static void (*ruby_vm_collect_usage_func_insn)(int insn) = vm_analysis_insn;
+static void (*ruby_vm_collect_usage_func_operand)(int insn, int n, VALUE op) = vm_analysis_operand;
+static void (*ruby_vm_collect_usage_func_register)(int reg, int isset) = vm_analysis_register;
/* :nodoc: */
static VALUE
@@ -3290,9 +3463,9 @@ usage_analysis_register_stop(VALUE self)
#else
-void (*ruby_vm_collect_usage_func_insn)(int insn) = NULL;
-void (*ruby_vm_collect_usage_func_operand)(int insn, int n, VALUE op) = NULL;
-void (*ruby_vm_collect_usage_func_register)(int reg, int isset) = NULL;
+MAYBE_UNUSED(static void (*ruby_vm_collect_usage_func_insn)(int insn)) = NULL;
+MAYBE_UNUSED(static void (*ruby_vm_collect_usage_func_operand)(int insn, int n, VALUE op)) = NULL;
+MAYBE_UNUSED(static void (*ruby_vm_collect_usage_func_register)(int reg, int isset)) = NULL;
#endif
@@ -3318,7 +3491,7 @@ vm_collect_usage_operand(int insn, int n, VALUE op)
if (RUBY_DTRACE_INSN_OPERAND_ENABLED()) {
VALUE valstr;
- valstr = rb_insn_operand_intern(GET_THREAD()->cfp->iseq, insn, n, op, 0, 0, 0, 0);
+ valstr = rb_insn_operand_intern(GET_EC()->cfp->iseq, insn, n, op, 0, 0, 0, 0);
RUBY_DTRACE_INSN_OPERAND(RSTRING_PTR(valstr), rb_insns_name(insn));
RB_GC_GUARD(valstr);
@@ -3337,4 +3510,6 @@ vm_collect_usage_register(int reg, int isset)
}
#endif
+#endif /* #ifndef MJIT_HEADER */
+
#include "vm_call_iseq_optimized.inc" /* required from vm_insnhelper.c */
diff --git a/vm_args.c b/vm_args.c
index 6cded80924..2e70ed3fcf 100644
--- a/vm_args.c
+++ b/vm_args.c
@@ -8,29 +8,40 @@
**********************************************************************/
-NORETURN(static void raise_argument_error(rb_thread_t *th, const rb_iseq_t *iseq, const VALUE exc));
-NORETURN(static void argument_arity_error(rb_thread_t *th, const rb_iseq_t *iseq, const int miss_argc, const int min_argc, const int max_argc));
-NORETURN(static void argument_kw_error(rb_thread_t *th, const rb_iseq_t *iseq, const char *error, const VALUE keys));
+NORETURN(static void raise_argument_error(rb_execution_context_t *ec, const rb_iseq_t *iseq, const VALUE exc));
+NORETURN(static void argument_arity_error(rb_execution_context_t *ec, const rb_iseq_t *iseq, const int miss_argc, const int min_argc, const int max_argc));
+NORETURN(static void argument_kw_error(rb_execution_context_t *ec, const rb_iseq_t *iseq, const char *error, const VALUE keys));
VALUE rb_keyword_error_new(const char *error, VALUE keys); /* class.c */
+static VALUE method_missing(VALUE obj, ID id, int argc, const VALUE *argv,
+ enum method_missing_reason call_status);
struct args_info {
/* basic args info */
VALUE *argv;
int argc;
- const struct rb_call_info_kw_arg *kw_arg;
/* additional args info */
int rest_index;
+ int rest_dupped;
+ const struct rb_call_info_kw_arg *kw_arg;
VALUE *kw_argv;
VALUE rest;
};
enum arg_setup_type {
arg_setup_method,
- arg_setup_block,
- arg_setup_lambda
+ arg_setup_block
};
+static inline void
+arg_rest_dup(struct args_info *args)
+{
+ if(!args->rest_dupped) {
+ args->rest = rb_ary_dup(args->rest);
+ args->rest_dupped = TRUE;
+ }
+}
+
static inline int
args_argc(struct args_info *args)
{
@@ -48,7 +59,7 @@ args_extend(struct args_info *args, const int min_argc)
int i;
if (args->rest) {
- args->rest = rb_ary_dup(args->rest);
+ arg_rest_dup(args);
VM_ASSERT(args->rest_index == 0);
for (i=args->argc + RARRAY_LENINT(args->rest); i<min_argc; i++) {
rb_ary_push(args->rest, Qnil);
@@ -68,7 +79,7 @@ args_reduce(struct args_info *args, int over_argc)
const long len = RARRAY_LEN(args->rest);
if (len > over_argc) {
- args->rest = rb_ary_dup(args->rest);
+ arg_rest_dup(args);
rb_ary_resize(args->rest, len - over_argc);
return;
}
@@ -83,7 +94,7 @@ args_reduce(struct args_info *args, int over_argc)
}
static inline int
-args_check_block_arg0(struct args_info *args, rb_thread_t *th)
+args_check_block_arg0(struct args_info *args)
{
VALUE ary = Qnil;
@@ -113,7 +124,7 @@ args_copy(struct args_info *args)
if (args->rest != Qfalse) {
int argc = args->argc;
args->argc = 0;
- args->rest = rb_ary_dup(args->rest); /* make dup */
+ arg_rest_dup(args);
/*
* argv: [m0, m1, m2, m3]
@@ -145,6 +156,7 @@ args_copy(struct args_info *args)
else if (args->argc > 0) {
args->rest = rb_ary_new_from_values(args->argc, args->argv);
args->rest_index = 0;
+ args->rest_dupped = TRUE;
args->argc = 0;
}
}
@@ -152,7 +164,7 @@ args_copy(struct args_info *args)
static inline const VALUE *
args_rest_argv(struct args_info *args)
{
- return RARRAY_CONST_PTR(args->rest) + args->rest_index;
+ return RARRAY_CONST_PTR_TRANSIENT(args->rest) + args->rest_index;
}
static inline VALUE
@@ -161,7 +173,8 @@ args_rest_array(struct args_info *args)
VALUE ary;
if (args->rest) {
- ary = rb_ary_subseq(args->rest, args->rest_index, RARRAY_LEN(args->rest) - args->rest_index);
+ ary = rb_ary_behead(args->rest, args->rest_index);
+ args->rest_index = 0;
args->rest = 0;
}
else {
@@ -171,7 +184,7 @@ args_rest_array(struct args_info *args)
}
static int
-keyword_hash_p(VALUE *kw_hash_ptr, VALUE *rest_hash_ptr, rb_thread_t *th)
+keyword_hash_p(VALUE *kw_hash_ptr, VALUE *rest_hash_ptr)
{
*rest_hash_ptr = rb_check_hash_type(*kw_hash_ptr);
@@ -188,7 +201,7 @@ keyword_hash_p(VALUE *kw_hash_ptr, VALUE *rest_hash_ptr, rb_thread_t *th)
}
static VALUE
-args_pop_keyword_hash(struct args_info *args, VALUE *kw_hash_ptr, rb_thread_t *th)
+args_pop_keyword_hash(struct args_info *args, VALUE *kw_hash_ptr)
{
VALUE rest_hash;
@@ -197,7 +210,7 @@ args_pop_keyword_hash(struct args_info *args, VALUE *kw_hash_ptr, rb_thread_t *t
VM_ASSERT(args->argc > 0);
*kw_hash_ptr = args->argv[args->argc-1];
- if (keyword_hash_p(kw_hash_ptr, &rest_hash, th)) {
+ if (keyword_hash_p(kw_hash_ptr, &rest_hash)) {
if (rest_hash) {
args->argv[args->argc-1] = rest_hash;
}
@@ -213,12 +226,12 @@ args_pop_keyword_hash(struct args_info *args, VALUE *kw_hash_ptr, rb_thread_t *t
if (len > 0) {
*kw_hash_ptr = RARRAY_AREF(args->rest, len - 1);
- if (keyword_hash_p(kw_hash_ptr, &rest_hash, th)) {
+ if (keyword_hash_p(kw_hash_ptr, &rest_hash)) {
if (rest_hash) {
RARRAY_ASET(args->rest, len - 1, rest_hash);
}
else {
- args->rest = rb_ary_dup(args->rest);
+ arg_rest_dup(args);
rb_ary_pop(args->rest);
return TRUE;
}
@@ -238,7 +251,7 @@ args_kw_argv_to_hash(struct args_info *args)
const struct rb_call_info_kw_arg *kw_arg = args->kw_arg;
const VALUE *const passed_keywords = kw_arg->keywords;
const int kw_len = kw_arg->keyword_len;
- VALUE h = rb_hash_new();
+ VALUE h = rb_hash_new_with_size(kw_len);
const int kw_start = args->argc - kw_len;
const VALUE * const kw_argv = args->argv + kw_start;
int i;
@@ -256,11 +269,11 @@ args_kw_argv_to_hash(struct args_info *args)
static void
args_stored_kw_argv_to_hash(struct args_info *args)
{
- VALUE h = rb_hash_new();
int i;
const struct rb_call_info_kw_arg *kw_arg = args->kw_arg;
const VALUE *const passed_keywords = kw_arg->keywords;
const int passed_keyword_len = kw_arg->keyword_len;
+ VALUE h = rb_hash_new_with_size(passed_keyword_len);
for (i=0; i<passed_keyword_len; i++) {
rb_hash_aset(h, passed_keywords[i], args->kw_argv[i]);
@@ -268,7 +281,7 @@ args_stored_kw_argv_to_hash(struct args_info *args)
args->kw_argv = NULL;
if (args->rest) {
- args->rest = rb_ary_dup(args->rest);
+ arg_rest_dup(args);
rb_ary_push(args->rest, h);
}
else {
@@ -300,9 +313,8 @@ static inline void
args_setup_post_parameters(struct args_info *args, int argc, VALUE *locals)
{
long len;
- args_copy(args);
len = RARRAY_LEN(args->rest);
- MEMCPY(locals, RARRAY_CONST_PTR(args->rest) + len - argc, VALUE, argc);
+ MEMCPY(locals, RARRAY_CONST_PTR_TRANSIENT(args->rest) + len - argc, VALUE, argc);
rb_ary_resize(args->rest, len - argc);
}
@@ -322,13 +334,13 @@ args_setup_opt_parameters(struct args_info *args, int opt_max, VALUE *locals)
args->argc = 0;
if (args->rest) {
- int len = RARRAY_LENINT(args->rest);
- const VALUE *argv = RARRAY_CONST_PTR(args->rest);
+ int len = RARRAY_LENINT(args->rest);
+ const VALUE *argv = RARRAY_CONST_PTR_TRANSIENT(args->rest);
- for (; i<opt_max && args->rest_index < len; i++, args->rest_index++) {
- locals[i] = argv[args->rest_index];
- }
- }
+ for (; i<opt_max && args->rest_index < len; i++, args->rest_index++) {
+ locals[i] = argv[args->rest_index];
+ }
+ }
/* initialize by nil */
for (j=i; j<opt_max; j++) {
@@ -342,7 +354,6 @@ args_setup_opt_parameters(struct args_info *args, int opt_max, VALUE *locals)
static inline void
args_setup_rest_parameter(struct args_info *args, VALUE *locals)
{
- args_copy(args);
*locals = args_rest_array(args);
}
@@ -364,7 +375,7 @@ static VALUE
make_rest_kw_hash(const VALUE *passed_keywords, int passed_keyword_len, const VALUE *kw_argv)
{
int i;
- VALUE obj = rb_hash_new();
+ VALUE obj = rb_hash_new_with_size(passed_keyword_len);
for (i=0; i<passed_keyword_len; i++) {
if (kw_argv[i] != Qundef) {
@@ -391,9 +402,12 @@ args_setup_kw_parameters_lookup(const ID key, VALUE *ptr, const VALUE *const pas
return FALSE;
}
+#define KW_SPECIFIED_BITS_MAX (32-1) /* TODO: 32 -> Fixnum's max bits */
+
static void
-args_setup_kw_parameters(VALUE* const passed_values, const int passed_keyword_len, const VALUE *const passed_keywords,
- const rb_iseq_t * const iseq, VALUE * const locals)
+args_setup_kw_parameters(rb_execution_context_t *const ec, const rb_iseq_t *const iseq,
+ VALUE *const passed_values, const int passed_keyword_len, const VALUE *const passed_keywords,
+ VALUE *const locals)
{
const ID *acceptable_keywords = iseq->body->param.keyword->table;
const int req_key_num = iseq->body->param.keyword->required_num;
@@ -415,7 +429,7 @@ args_setup_kw_parameters(VALUE* const passed_values, const int passed_keyword_le
}
}
- if (missing) argument_kw_error(GET_THREAD(), iseq, "missing", missing);
+ if (missing) argument_kw_error(ec, iseq, "missing", missing);
for (di=0; i<key_num; i++, di++) {
if (args_setup_kw_parameters_lookup(acceptable_keywords[i], &locals[i], passed_keywords, passed_values, passed_keyword_len)) {
@@ -425,7 +439,7 @@ args_setup_kw_parameters(VALUE* const passed_values, const int passed_keyword_le
if (default_values[di] == Qundef) {
locals[i] = Qnil;
- if (LIKELY(i < 32)) { /* TODO: 32 -> Fixnum's max bits */
+ if (LIKELY(i < KW_SPECIFIED_BITS_MAX)) {
unspecified_bits |= 0x01 << di;
}
else {
@@ -434,7 +448,7 @@ args_setup_kw_parameters(VALUE* const passed_values, const int passed_keyword_le
int j;
unspecified_bits_value = rb_hash_new();
- for (j=0; j<32; j++) {
+ for (j=0; j<KW_SPECIFIED_BITS_MAX; j++) {
if (unspecified_bits & (0x01 << j)) {
rb_hash_aset(unspecified_bits_value, INT2FIX(j), Qtrue);
}
@@ -456,7 +470,7 @@ args_setup_kw_parameters(VALUE* const passed_values, const int passed_keyword_le
else {
if (found != passed_keyword_len) {
VALUE keys = make_unknown_kw_hash(passed_keywords, passed_keyword_len, passed_values);
- argument_kw_error(GET_THREAD(), iseq, "unknown", keys);
+ argument_kw_error(ec, iseq, "unknown", keys);
}
}
@@ -473,27 +487,10 @@ args_setup_kw_rest_parameter(VALUE keyword_hash, VALUE *locals)
}
static inline void
-args_setup_block_parameter(rb_thread_t *th, struct rb_calling_info *calling, VALUE *locals)
+args_setup_block_parameter(const rb_execution_context_t *ec, struct rb_calling_info *calling, VALUE *locals)
{
VALUE block_handler = calling->block_handler;
- VALUE blockval = Qnil;
-
- if (block_handler != VM_BLOCK_HANDLER_NONE) {
-
- switch (vm_block_handler_type(block_handler)) {
- case block_handler_type_iseq:
- case block_handler_type_ifunc:
- blockval = rb_vm_make_proc(th, VM_BH_TO_CAPT_BLOCK(block_handler), rb_cProc);
- break;
- case block_handler_type_symbol:
- blockval = rb_sym_to_proc(VM_BH_TO_SYMBOL(block_handler));
- break;
- case block_handler_type_proc:
- blockval = VM_BH_TO_PROC(block_handler);
- break;
- }
- }
- *locals = blockval;
+ *locals = rb_vm_bh_to_procval(ec, block_handler);
}
struct fill_values_arg {
@@ -513,7 +510,7 @@ fill_keys_values(st_data_t key, st_data_t val, st_data_t ptr)
}
static int
-setup_parameters_complex(rb_thread_t * const th, const rb_iseq_t * const iseq,
+setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * const iseq,
struct rb_calling_info *const calling,
const struct rb_call_info *ci,
VALUE * const locals, const enum arg_setup_type arg_setup_type)
@@ -522,9 +519,10 @@ setup_parameters_complex(rb_thread_t * const th, const rb_iseq_t * const iseq,
const int max_argc = (iseq->body->param.flags.has_rest == FALSE) ? min_argc + iseq->body->param.opt_num : UNLIMITED_ARGUMENTS;
int opt_pc = 0;
int given_argc;
+ int kw_splat = FALSE;
struct args_info args_body, *args;
VALUE keyword_hash = Qnil;
- VALUE * const orig_sp = th->cfp->sp;
+ VALUE * const orig_sp = ec->cfp->sp;
unsigned int i;
/*
@@ -544,12 +542,13 @@ setup_parameters_complex(rb_thread_t * const th, const rb_iseq_t * const iseq,
for (i=calling->argc; i<iseq->body->param.size; i++) {
locals[i] = Qnil;
}
- th->cfp->sp = &locals[i];
+ ec->cfp->sp = &locals[i];
/* setup args */
args = &args_body;
given_argc = args->argc = calling->argc;
args->argv = locals;
+ args->rest_dupped = FALSE;
if (ci->flag & VM_CALL_KWARG) {
args->kw_arg = ((struct rb_call_info_with_kwarg *)ci)->kw_arg;
@@ -589,18 +588,10 @@ setup_parameters_complex(rb_thread_t * const th, const rb_iseq_t * const iseq,
(min_argc > 0 || iseq->body->param.opt_num > 1 ||
iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest) &&
!iseq->body->param.flags.ambiguous_param0 &&
- args_check_block_arg0(args, th)) {
+ args_check_block_arg0(args)) {
given_argc = RARRAY_LENINT(args->rest);
}
break;
- case arg_setup_lambda:
- if (given_argc == 1 &&
- given_argc != iseq->body->param.lead_num &&
- !iseq->body->param.flags.has_opt &&
- !iseq->body->param.flags.has_rest &&
- args_check_block_arg0(args, th)) {
- given_argc = RARRAY_LENINT(args->rest);
- }
}
/* argc check */
@@ -611,20 +602,24 @@ setup_parameters_complex(rb_thread_t * const th, const rb_iseq_t * const iseq,
}
else {
if (arg_setup_type == arg_setup_block) {
- CHECK_VM_STACK_OVERFLOW(th->cfp, min_argc);
+ CHECK_VM_STACK_OVERFLOW(ec->cfp, min_argc);
given_argc = min_argc;
args_extend(args, min_argc);
}
else {
- argument_arity_error(th, iseq, given_argc, min_argc, max_argc);
+ argument_arity_error(ec, iseq, given_argc, min_argc, max_argc);
}
}
}
+ if (ci->flag & VM_CALL_KW_SPLAT) {
+ kw_splat = !iseq->body->param.flags.has_rest;
+ }
if (given_argc > min_argc &&
- (iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest) &&
+ (iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest ||
+ (kw_splat && given_argc > max_argc)) &&
args->kw_argv == NULL) {
- if (args_pop_keyword_hash(args, &keyword_hash, th)) {
+ if (args_pop_keyword_hash(args, &keyword_hash)) {
given_argc--;
}
}
@@ -636,7 +631,7 @@ setup_parameters_complex(rb_thread_t * const th, const rb_iseq_t * const iseq,
given_argc = max_argc;
}
else {
- argument_arity_error(th, iseq, given_argc, min_argc, max_argc);
+ argument_arity_error(ec, iseq, given_argc, min_argc, max_argc);
}
}
@@ -644,6 +639,10 @@ setup_parameters_complex(rb_thread_t * const th, const rb_iseq_t * const iseq,
args_setup_lead_parameters(args, iseq->body->param.lead_num, locals + 0);
}
+ if (iseq->body->param.flags.has_rest || iseq->body->param.flags.has_post){
+ args_copy(args);
+ }
+
if (iseq->body->param.flags.has_post) {
args_setup_post_parameters(args, iseq->body->param.post_num, locals + iseq->body->param.post_start);
}
@@ -662,7 +661,7 @@ setup_parameters_complex(rb_thread_t * const th, const rb_iseq_t * const iseq,
if (args->kw_argv != NULL) {
const struct rb_call_info_kw_arg *kw_arg = args->kw_arg;
- args_setup_kw_parameters(args->kw_argv, kw_arg->keyword_len, kw_arg->keywords, iseq, klocals);
+ args_setup_kw_parameters(ec, iseq, args->kw_argv, kw_arg->keyword_len, kw_arg->keywords, klocals);
}
else if (!NIL_P(keyword_hash)) {
int kw_len = rb_long2int(RHASH_SIZE(keyword_hash));
@@ -673,19 +672,27 @@ setup_parameters_complex(rb_thread_t * const th, const rb_iseq_t * const iseq,
arg.argc = 0;
rb_hash_foreach(keyword_hash, fill_keys_values, (VALUE)&arg);
VM_ASSERT(arg.argc == kw_len);
- args_setup_kw_parameters(arg.vals, kw_len, arg.keys, iseq, klocals);
+ args_setup_kw_parameters(ec, iseq, arg.vals, kw_len, arg.keys, klocals);
}
else {
VM_ASSERT(args_argc(args) == 0);
- args_setup_kw_parameters(NULL, 0, NULL, iseq, klocals);
+ args_setup_kw_parameters(ec, iseq, NULL, 0, NULL, klocals);
}
}
else if (iseq->body->param.flags.has_kwrest) {
args_setup_kw_rest_parameter(keyword_hash, locals + iseq->body->param.keyword->rest_start);
}
+ else if (!NIL_P(keyword_hash) && RHASH_SIZE(keyword_hash) > 0) {
+ argument_kw_error(ec, iseq, "unknown", rb_hash_keys(keyword_hash));
+ }
if (iseq->body->param.flags.has_block) {
- args_setup_block_parameter(th, calling, locals + iseq->body->param.block_start);
+ if (iseq->body->local_iseq == iseq) {
+ /* Do nothing */
+ }
+ else {
+ args_setup_block_parameter(ec, calling, locals + iseq->body->param.block_start);
+ }
}
#if 0
@@ -697,24 +704,28 @@ setup_parameters_complex(rb_thread_t * const th, const rb_iseq_t * const iseq,
}
#endif
- th->cfp->sp = orig_sp;
+ ec->cfp->sp = orig_sp;
return opt_pc;
}
+void rb_backtrace_use_iseq_first_lineno_for_last_location(VALUE self); /* vm_backtrace.c */
+
static void
-raise_argument_error(rb_thread_t *th, const rb_iseq_t *iseq, const VALUE exc)
+raise_argument_error(rb_execution_context_t *ec, const rb_iseq_t *iseq, const VALUE exc)
{
VALUE at;
if (iseq) {
- vm_push_frame(th, iseq, VM_FRAME_MAGIC_DUMMY | VM_ENV_FLAG_LOCAL, Qnil /* self */,
+ vm_push_frame(ec, iseq, VM_FRAME_MAGIC_DUMMY | VM_ENV_FLAG_LOCAL, Qnil /* self */,
VM_BLOCK_HANDLER_NONE /* specval*/, Qfalse /* me or cref */,
- iseq->body->iseq_encoded, th->cfp->sp, 0, 0 /* stack_max */);
- at = rb_vm_backtrace_object();
- rb_vm_pop_frame(th);
+ iseq->body->iseq_encoded,
+ ec->cfp->sp, 0, 0 /* stack_max */);
+ at = rb_ec_backtrace_object(ec);
+ rb_backtrace_use_iseq_first_lineno_for_last_location(at);
+ rb_vm_pop_frame(ec);
}
else {
- at = rb_vm_backtrace_object();
+ at = rb_ec_backtrace_object(ec);
}
rb_ivar_set(exc, idBt_locations, at);
@@ -723,15 +734,34 @@ raise_argument_error(rb_thread_t *th, const rb_iseq_t *iseq, const VALUE exc)
}
static void
-argument_arity_error(rb_thread_t *th, const rb_iseq_t *iseq, const int miss_argc, const int min_argc, const int max_argc)
+argument_arity_error(rb_execution_context_t *ec, const rb_iseq_t *iseq, const int miss_argc, const int min_argc, const int max_argc)
{
- raise_argument_error(th, iseq, rb_arity_error_new(miss_argc, min_argc, max_argc));
+ VALUE exc = rb_arity_error_new(miss_argc, min_argc, max_argc);
+ if (iseq->body->param.flags.has_kw) {
+ const struct rb_iseq_param_keyword *const kw = iseq->body->param.keyword;
+ const ID *keywords = kw->table;
+ int req_key_num = kw->required_num;
+ if (req_key_num > 0) {
+ static const char required[] = "; required keywords";
+ VALUE mesg = rb_attr_get(exc, idMesg);
+ rb_str_resize(mesg, RSTRING_LEN(mesg)-1);
+ rb_str_cat(mesg, required, sizeof(required) - 1 - (req_key_num == 1));
+ rb_str_cat_cstr(mesg, ":");
+ do {
+ rb_str_cat_cstr(mesg, " ");
+ rb_str_append(mesg, rb_id2str(*keywords++));
+ rb_str_cat_cstr(mesg, ",");
+ } while (--req_key_num);
+ RSTRING_PTR(mesg)[RSTRING_LEN(mesg)-1] = ')';
+ }
+ }
+ raise_argument_error(ec, iseq, exc);
}
static void
-argument_kw_error(rb_thread_t *th, const rb_iseq_t *iseq, const char *error, const VALUE keys)
+argument_kw_error(rb_execution_context_t *ec, const rb_iseq_t *iseq, const char *error, const VALUE keys)
{
- raise_argument_error(th, iseq, rb_keyword_error_new(error, keys));
+ raise_argument_error(ec, iseq, rb_keyword_error_new(error, keys));
}
static inline void
@@ -744,15 +774,15 @@ vm_caller_setup_arg_splat(rb_control_frame_t *cfp, struct rb_calling_info *calli
cfp->sp--;
if (!NIL_P(ary)) {
- const VALUE *ptr = RARRAY_CONST_PTR(ary);
- long len = RARRAY_LEN(ary), i;
+ const VALUE *ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
+ long len = RARRAY_LEN(ary), i;
- CHECK_VM_STACK_OVERFLOW(cfp, len);
+ CHECK_VM_STACK_OVERFLOW(cfp, len);
- for (i = 0; i < len; i++) {
- *cfp->sp++ = ptr[i];
- }
- calling->argc += i - 1;
+ for (i = 0; i < len; i++) {
+ *cfp->sp++ = ptr[i];
+ }
+ calling->argc += i - 1;
}
}
@@ -762,7 +792,7 @@ vm_caller_setup_arg_kw(rb_control_frame_t *cfp, struct rb_calling_info *calling,
struct rb_call_info_with_kwarg *ci_kw = (struct rb_call_info_with_kwarg *)ci;
const VALUE *const passed_keywords = ci_kw->kw_arg->keywords;
const int kw_len = ci_kw->kw_arg->keyword_len;
- const VALUE h = rb_hash_new();
+ const VALUE h = rb_hash_new_with_size(kw_len);
VALUE *sp = cfp->sp;
int i;
@@ -780,7 +810,16 @@ vm_to_proc(VALUE proc)
{
if (UNLIKELY(!rb_obj_is_proc(proc))) {
VALUE b;
- b = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc");
+ const rb_callable_method_entry_t *me =
+ rb_callable_method_entry_with_refinements(CLASS_OF(proc), idTo_proc, NULL);
+
+ if (me) {
+ b = rb_vm_call0(GET_EC(), proc, idTo_proc, 0, NULL, me);
+ }
+ else {
+ /* NOTE: calling method_missing */
+ 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,
@@ -800,69 +839,81 @@ refine_sym_proc_call(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg))
VALUE obj;
ID mid;
const rb_callable_method_entry_t *me;
+ rb_execution_context_t *ec;
+ const VALUE symbol = RARRAY_AREF(callback_arg, 0);
+ const VALUE refinements = RARRAY_AREF(callback_arg, 1);
+ VALUE klass;
if (argc-- < 1) {
rb_raise(rb_eArgError, "no receiver given");
}
obj = *argv++;
- mid = SYM2ID(callback_arg);
- me = rb_callable_method_entry_with_refinements(CLASS_OF(obj), mid);
+
+ mid = SYM2ID(symbol);
+ for (klass = CLASS_OF(obj); klass; klass = RCLASS_SUPER(klass)) {
+ me = rb_callable_method_entry(klass, mid);
+ if (me) {
+ me = rb_resolve_refined_method_callable(refinements, me);
+ if (me) break;
+ }
+ }
+
+ ec = GET_EC();
+ if (!NIL_P(blockarg)) {
+ vm_passed_block_handler_set(ec, blockarg);
+ }
if (!me) {
- /* fallback to funcall (e.g. method_missing) */
- return rb_funcall_with_block(obj, mid, argc, argv, blockarg);
+ return method_missing(obj, mid, argc, argv, MISSING_NOENTRY);
}
- return vm_call0(GET_THREAD(), obj, mid, argc, argv, me);
+ return rb_vm_call0(ec, obj, mid, argc, argv, me);
}
-static void
-vm_caller_setup_arg_block(const rb_thread_t *th, rb_control_frame_t *reg_cfp,
- struct rb_calling_info *calling, const struct rb_call_info *ci, rb_iseq_t *blockiseq, const int is_super)
+static VALUE
+vm_caller_setup_arg_block(const rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
+ const struct rb_call_info *ci, rb_iseq_t *blockiseq, const int is_super)
{
if (ci->flag & VM_CALL_ARGS_BLOCKARG) {
VALUE block_code = *(--reg_cfp->sp);
if (NIL_P(block_code)) {
- calling->block_handler = VM_BLOCK_HANDLER_NONE;
- }
- else {
- if (SYMBOL_P(block_code) && rb_method_basic_definition_p(rb_cSymbol, idTo_proc)) {
- const rb_cref_t *cref = vm_env_cref(reg_cfp->ep);
- if (cref && !NIL_P(cref->refinements)) {
- VALUE ref = cref->refinements;
- VALUE func = rb_hash_lookup(ref, block_code);
- if (NIL_P(func)) {
- /* TODO: limit cached funcs */
- func = rb_func_proc_new(refine_sym_proc_call, block_code);
- rb_hash_aset(ref, block_code, func);
- }
- block_code = func;
+ return VM_BLOCK_HANDLER_NONE;
+ }
+ else if (block_code == rb_block_param_proxy) {
+ return VM_CF_BLOCK_HANDLER(reg_cfp);
+ }
+ else if (SYMBOL_P(block_code) && rb_method_basic_definition_p(rb_cSymbol, idTo_proc)) {
+ const rb_cref_t *cref = vm_env_cref(reg_cfp->ep);
+ if (cref && !NIL_P(cref->refinements)) {
+ VALUE ref = cref->refinements;
+ VALUE func = rb_hash_lookup(ref, block_code);
+ if (NIL_P(func)) {
+ /* TODO: limit cached funcs */
+ VALUE callback_arg = rb_ary_tmp_new(2);
+ RARRAY_ASET(callback_arg, 0, block_code);
+ RARRAY_ASET(callback_arg, 1, ref);
+ OBJ_FREEZE_RAW(callback_arg);
+ func = rb_func_proc_new(refine_sym_proc_call, callback_arg);
+ rb_hash_aset(ref, block_code, func);
}
- calling->block_handler = block_code;
- }
- else {
- calling->block_handler = vm_to_proc(block_code);
+ block_code = func;
}
- }
+ return block_code;
+ }
+ else {
+ return vm_to_proc(block_code);
+ }
}
else if (blockiseq != NULL) { /* likely */
struct rb_captured_block *captured = VM_CFP_TO_CAPTURED_BLOCK(reg_cfp);
captured->code.iseq = blockiseq;
- calling->block_handler = VM_BH_FROM_ISEQ_BLOCK(captured);
+ return VM_BH_FROM_ISEQ_BLOCK(captured);
}
else {
if (is_super) {
- calling->block_handler = GET_BLOCK_HANDLER();
- }
- else {
- calling->block_handler = VM_BLOCK_HANDLER_NONE;
- }
+ return GET_BLOCK_HANDLER();
+ }
+ else {
+ return VM_BLOCK_HANDLER_NONE;
+ }
}
}
-
-#define IS_ARGS_SPLAT(ci) ((ci)->flag & VM_CALL_ARGS_SPLAT)
-#define IS_ARGS_KEYWORD(ci) ((ci)->flag & VM_CALL_KWARG)
-
-#define CALLER_SETUP_ARG(cfp, calling, ci) do { \
- if (UNLIKELY(IS_ARGS_SPLAT(ci))) vm_caller_setup_arg_splat((cfp), (calling)); \
- if (UNLIKELY(IS_ARGS_KEYWORD(ci))) vm_caller_setup_arg_kw((cfp), (calling), (ci)); \
-} while (0)
diff --git a/vm_backtrace.c b/vm_backtrace.c
index 1df7bb8b7f..2a6c5bde30 100644
--- a/vm_backtrace.c
+++ b/vm_backtrace.c
@@ -9,8 +9,9 @@
**********************************************************************/
-#include "internal.h"
+#include "ruby/encoding.h"
#include "ruby/debug.h"
+#include "internal.h"
#include "vm_core.h"
#include "eval_intern.h"
@@ -31,14 +32,33 @@ id2str(ID id)
inline static int
calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
{
- return rb_iseq_line_no(iseq, pc - iseq->body->iseq_encoded);
+ size_t pos = (size_t)(pc - iseq->body->iseq_encoded);
+ if (LIKELY(pos)) {
+ /* use pos-1 because PC points next instruction at the beginning of instruction */
+ pos--;
+ }
+#if VMDEBUG && defined(HAVE_BUILTIN___BUILTIN_TRAP)
+ else {
+ /* SDR() is not possible; that causes infinite loop. */
+ rb_print_backtrace();
+ __builtin_trap();
+ }
+#endif
+ return rb_iseq_line_no(iseq, pos);
}
int
rb_vm_get_sourceline(const rb_control_frame_t *cfp)
{
if (VM_FRAME_RUBYFRAME_P(cfp) && cfp->iseq) {
- return calc_lineno(cfp->iseq, cfp->pc);
+ const rb_iseq_t *iseq = cfp->iseq;
+ int line = calc_lineno(iseq, cfp->pc);
+ if (line != 0) {
+ return line;
+ }
+ else {
+ return FIX2INT(rb_iseq_first_lineno(iseq));
+ }
}
else {
return 0;
@@ -232,7 +252,7 @@ location_path(rb_backtrace_location_t *loc)
switch (loc->type) {
case LOCATION_TYPE_ISEQ:
case LOCATION_TYPE_ISEQ_CALCED:
- return loc->body.iseq.iseq->body->location.path;
+ return rb_iseq_path(loc->body.iseq.iseq);
case LOCATION_TYPE_CFUNC:
if (loc->body.cfunc.prev_loc) {
return location_path(loc->body.cfunc.prev_loc);
@@ -260,20 +280,20 @@ location_path_m(VALUE self)
}
static VALUE
-location_absolute_path(rb_backtrace_location_t *loc)
+location_realpath(rb_backtrace_location_t *loc)
{
switch (loc->type) {
case LOCATION_TYPE_ISEQ:
case LOCATION_TYPE_ISEQ_CALCED:
- return loc->body.iseq.iseq->body->location.absolute_path;
+ return rb_iseq_realpath(loc->body.iseq.iseq);
case LOCATION_TYPE_CFUNC:
if (loc->body.cfunc.prev_loc) {
- return location_absolute_path(loc->body.cfunc.prev_loc);
+ return location_realpath(loc->body.cfunc.prev_loc);
}
return Qnil;
case LOCATION_TYPE_IFUNC:
default:
- rb_bug("location_absolute_path: unreachable");
+ rb_bug("location_realpath: unreachable");
UNREACHABLE;
}
}
@@ -286,7 +306,7 @@ location_absolute_path(rb_backtrace_location_t *loc)
static VALUE
location_absolute_path_m(VALUE self)
{
- return location_absolute_path(location_ptr(self));
+ return location_realpath(location_ptr(self));
}
static VALUE
@@ -314,25 +334,24 @@ location_to_str(rb_backtrace_location_t *loc)
switch (loc->type) {
case LOCATION_TYPE_ISEQ:
- file = loc->body.iseq.iseq->body->location.path;
+ file = rb_iseq_path(loc->body.iseq.iseq);
name = loc->body.iseq.iseq->body->location.label;
lineno = loc->body.iseq.lineno.lineno = calc_lineno(loc->body.iseq.iseq, loc->body.iseq.lineno.pc);
loc->type = LOCATION_TYPE_ISEQ_CALCED;
break;
case LOCATION_TYPE_ISEQ_CALCED:
- file = loc->body.iseq.iseq->body->location.path;
+ file = rb_iseq_path(loc->body.iseq.iseq);
lineno = loc->body.iseq.lineno.lineno;
name = loc->body.iseq.iseq->body->location.label;
break;
case LOCATION_TYPE_CFUNC:
if (loc->body.cfunc.prev_loc) {
- file = loc->body.cfunc.prev_loc->body.iseq.iseq->body->location.path;
+ file = rb_iseq_path(loc->body.cfunc.prev_loc->body.iseq.iseq);
lineno = location_lineno(loc->body.cfunc.prev_loc);
}
else {
- rb_thread_t *th = GET_THREAD();
- file = th->vm->progname;
+ file = GET_VM()->progname;
lineno = INT2FIX(0);
}
name = rb_id2str(loc->body.cfunc.mid);
@@ -421,15 +440,15 @@ backtrace_alloc(VALUE klass)
}
static void
-backtrace_each(rb_thread_t *th,
+backtrace_each(const rb_execution_context_t *ec,
void (*init)(void *arg, size_t size),
void (*iter_iseq)(void *arg, const rb_control_frame_t *cfp),
void (*iter_cfunc)(void *arg, const rb_control_frame_t *cfp, ID mid),
void *arg)
{
- rb_control_frame_t *last_cfp = th->cfp;
- rb_control_frame_t *start_cfp = RUBY_VM_END_CONTROL_FRAME(th);
- rb_control_frame_t *cfp;
+ const rb_control_frame_t *last_cfp = ec->cfp;
+ const rb_control_frame_t *start_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
+ const rb_control_frame_t *cfp;
ptrdiff_t size, i;
/* <- start_cfp (end control frame)
@@ -439,7 +458,7 @@ backtrace_each(rb_thread_t *th,
* top frame
* ...
* 2nd frame <- lev:0
- * current frame <- th->cfp
+ * current frame <- ec->cfp
*/
start_cfp =
@@ -457,7 +476,7 @@ backtrace_each(rb_thread_t *th,
/* SDR(); */
for (i=0, cfp = start_cfp; i<size; i++, cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
- /* fprintf(stderr, "cfp: %d\n", (rb_control_frame_t *)(th->stack + th->stack_size) - cfp); */
+ /* fprintf(stderr, "cfp: %d\n", (rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size) - cfp); */
if (cfp->iseq) {
if (cfp->pc) {
iter_iseq(arg, cfp);
@@ -511,13 +530,13 @@ bt_iter_cfunc(void *ptr, const rb_control_frame_t *cfp, ID mid)
loc->body.cfunc.prev_loc = arg->prev_loc;
}
-static VALUE
-backtrace_object(rb_thread_t *th)
+MJIT_FUNC_EXPORTED VALUE
+rb_ec_backtrace_object(const rb_execution_context_t *ec)
{
struct bt_iter_arg arg;
arg.prev_loc = 0;
- backtrace_each(th,
+ backtrace_each(ec,
bt_init,
bt_iter_iseq,
bt_iter_cfunc,
@@ -526,12 +545,6 @@ backtrace_object(rb_thread_t *th)
return arg.btobj;
}
-VALUE
-rb_vm_backtrace_object(void)
-{
- return backtrace_object(GET_THREAD());
-}
-
static VALUE
backtrace_collect(rb_backtrace_t *bt, long lev, long n, VALUE (*func)(rb_backtrace_location_t *, void *arg), void *arg)
{
@@ -592,6 +605,25 @@ rb_backtrace_to_str_ary(VALUE self)
return bt->strary;
}
+MJIT_FUNC_EXPORTED void
+rb_backtrace_use_iseq_first_lineno_for_last_location(VALUE self)
+{
+ const rb_backtrace_t *bt;
+ const rb_iseq_t *iseq;
+ rb_backtrace_location_t *loc;
+
+ GetCoreDataFromValue(self, rb_backtrace_t, bt);
+ VM_ASSERT(bt->backtrace_size > 0);
+
+ loc = &bt->backtrace[bt->backtrace_size - 1];
+ iseq = loc->body.iseq.iseq;
+
+ VM_ASSERT(loc->type == LOCATION_TYPE_ISEQ);
+
+ loc->body.iseq.lineno.lineno = FIX2INT(iseq->body->location.first_lineno);
+ loc->type = LOCATION_TYPE_ISEQ_CALCED;
+}
+
static VALUE
location_create(rb_backtrace_location_t *srcloc, void *btobj)
{
@@ -656,15 +688,15 @@ backtrace_load_data(VALUE self, VALUE str)
}
VALUE
-rb_vm_backtrace_str_ary(rb_thread_t *th, long lev, long n)
+rb_ec_backtrace_str_ary(const rb_execution_context_t *ec, long lev, long n)
{
- return backtrace_to_str_ary(backtrace_object(th), lev, n);
+ return backtrace_to_str_ary(rb_ec_backtrace_object(ec), lev, n);
}
-VALUE
-rb_vm_backtrace_location_ary(rb_thread_t *th, long lev, long n)
+static VALUE
+ec_backtrace_location_ary(const rb_execution_context_t *ec, long lev, long n)
{
- return backtrace_to_location_ary(backtrace_object(th), lev, n);
+ return backtrace_to_location_ary(rb_ec_backtrace_object(ec), lev, n);
}
/* make old style backtrace directly */
@@ -680,9 +712,7 @@ static void
oldbt_init(void *ptr, size_t dmy)
{
struct oldbt_arg *arg = (struct oldbt_arg *)ptr;
- rb_thread_t *th = GET_THREAD();
-
- arg->filename = th->vm->progname;
+ arg->filename = GET_VM()->progname;
arg->lineno = 0;
}
@@ -692,7 +722,7 @@ oldbt_iter_iseq(void *ptr, const rb_control_frame_t *cfp)
const rb_iseq_t *iseq = cfp->iseq;
const VALUE *pc = cfp->pc;
struct oldbt_arg *arg = (struct oldbt_arg *)ptr;
- VALUE file = arg->filename = iseq->body->location.path;
+ VALUE file = arg->filename = rb_iseq_path(iseq);
VALUE name = iseq->body->location.label;
int lineno = arg->lineno = calc_lineno(iseq, pc);
@@ -732,7 +762,7 @@ vm_backtrace_print(FILE *fp)
arg.func = oldbt_print;
arg.data = (void *)fp;
- backtrace_each(GET_THREAD(),
+ backtrace_each(GET_EC(),
oldbt_init,
oldbt_iter_iseq,
oldbt_iter_cfunc,
@@ -765,7 +795,7 @@ rb_backtrace_print_as_bugreport(void)
arg.func = oldbt_bugreport;
arg.data = (int *)&i;
- backtrace_each(GET_THREAD(),
+ backtrace_each(GET_EC(),
oldbt_init,
oldbt_iter_iseq,
oldbt_iter_cfunc,
@@ -778,10 +808,15 @@ rb_backtrace(void)
vm_backtrace_print(stderr);
}
+struct print_to_arg {
+ VALUE (*iter)(VALUE recv, VALUE str);
+ VALUE output;
+};
+
static void
oldbt_print_to(void *data, VALUE file, int lineno, VALUE name)
{
- VALUE output = (VALUE)data;
+ const struct print_to_arg *arg = data;
VALUE str = rb_sprintf("\tfrom %"PRIsVALUE":%d:in ", file, lineno);
if (NIL_P(name)) {
@@ -790,17 +825,20 @@ oldbt_print_to(void *data, VALUE file, int lineno, VALUE name)
else {
rb_str_catf(str, " `%"PRIsVALUE"'\n", name);
}
- rb_io_write(output, str);
+ (*arg->iter)(arg->output, str);
}
void
-rb_backtrace_print_to(VALUE output)
+rb_backtrace_each(VALUE (*iter)(VALUE recv, VALUE str), VALUE output)
{
struct oldbt_arg arg;
+ struct print_to_arg parg;
+ parg.iter = iter;
+ parg.output = output;
arg.func = oldbt_print_to;
- arg.data = (void *)output;
- backtrace_each(GET_THREAD(),
+ arg.data = &parg;
+ backtrace_each(GET_EC(),
oldbt_init,
oldbt_iter_iseq,
oldbt_iter_cfunc,
@@ -810,15 +848,15 @@ rb_backtrace_print_to(VALUE output)
VALUE
rb_make_backtrace(void)
{
- return rb_vm_backtrace_str_ary(GET_THREAD(), 0, 0);
+ return rb_ec_backtrace_str_ary(GET_EC(), 0, 0);
}
static VALUE
-vm_backtrace_to_ary(rb_thread_t *th, int argc, const VALUE *argv, int lev_default, int lev_plus, int to_str)
+ec_backtrace_to_ary(const rb_execution_context_t *ec, int argc, const VALUE *argv, int lev_default, int lev_plus, int to_str)
{
VALUE level, vn;
long lev, n;
- VALUE btval = backtrace_object(th);
+ VALUE btval = rb_ec_backtrace_object(ec);
VALUE r;
rb_backtrace_t *bt;
@@ -887,13 +925,12 @@ vm_backtrace_to_ary(rb_thread_t *th, int argc, const VALUE *argv, int lev_defaul
static VALUE
thread_backtrace_to_ary(int argc, const VALUE *argv, VALUE thval, int to_str)
{
- rb_thread_t *th;
- GetThreadPtr(thval, th);
+ rb_thread_t *target_th = rb_thread_ptr(thval);
- if (th->to_kill || th->status == THREAD_KILLED)
- return Qnil;
+ if (target_th->to_kill || target_th->status == THREAD_KILLED)
+ return Qnil;
- return vm_backtrace_to_ary(th, argc, argv, 0, 0, to_str);
+ return ec_backtrace_to_ary(target_th->ec, argc, argv, 0, 0, to_str);
}
VALUE
@@ -949,7 +986,7 @@ rb_vm_thread_backtrace_locations(int argc, const VALUE *argv, VALUE thval)
static VALUE
rb_f_caller(int argc, VALUE *argv)
{
- return vm_backtrace_to_ary(GET_THREAD(), argc, argv, 1, 1, 1);
+ return ec_backtrace_to_ary(GET_EC(), argc, argv, 1, 1, 1);
}
/*
@@ -977,7 +1014,7 @@ rb_f_caller(int argc, VALUE *argv)
static VALUE
rb_f_caller_locations(int argc, VALUE *argv)
{
- return vm_backtrace_to_ary(GET_THREAD(), argc, argv, 1, 1, 0);
+ return ec_backtrace_to_ary(GET_EC(), argc, argv, 1, 1, 0);
}
/* called from Init_vm() in vm.c */
@@ -1059,7 +1096,7 @@ RUBY_SYMBOL_EXPORT_BEGIN
RUBY_SYMBOL_EXPORT_END
struct rb_debug_inspector_struct {
- rb_thread_t *th;
+ rb_execution_context_t *ec;
rb_control_frame_t *cfp;
VALUE backtrace;
VALUE contexts; /* [[klass, binding, iseq, cfp], ...] */
@@ -1132,7 +1169,7 @@ collect_caller_bindings_cfunc(void *arg, const rb_control_frame_t *cfp, ID mid)
}
static VALUE
-collect_caller_bindings(rb_thread_t *th)
+collect_caller_bindings(const rb_execution_context_t *ec)
{
struct collect_caller_bindings_data data;
VALUE result;
@@ -1140,7 +1177,7 @@ collect_caller_bindings(rb_thread_t *th)
data.ary = rb_ary_new();
- backtrace_each(th,
+ backtrace_each(ec,
collect_caller_bindings_init,
collect_caller_bindings_iseq,
collect_caller_bindings_cfunc,
@@ -1155,7 +1192,7 @@ collect_caller_bindings(rb_thread_t *th)
if (!NIL_P(cfp_val)) {
rb_control_frame_t *cfp = GC_GUARDED_PTR_REF(cfp_val);
- rb_ary_store(entry, CALLER_BINDING_BINDING, rb_vm_make_binding(th, cfp));
+ rb_ary_store(entry, CALLER_BINDING_BINDING, rb_vm_make_binding(ec, cfp));
}
}
@@ -1171,26 +1208,29 @@ VALUE
rb_debug_inspector_open(rb_debug_inspector_func_t func, void *data)
{
rb_debug_inspector_t dbg_context;
- rb_thread_t *th = GET_THREAD();
- int state;
+ rb_execution_context_t *ec = GET_EC();
+ enum ruby_tag_type state;
volatile VALUE MAYBE_UNUSED(result);
- dbg_context.th = th;
- dbg_context.cfp = dbg_context.th->cfp;
- dbg_context.backtrace = rb_vm_backtrace_location_ary(th, 0, 0);
+ /* escape all env to heap */
+ rb_vm_stack_to_heap(ec);
+
+ dbg_context.ec = ec;
+ dbg_context.cfp = dbg_context.ec->cfp;
+ dbg_context.backtrace = ec_backtrace_location_ary(ec, 0, 0);
dbg_context.backtrace_size = RARRAY_LEN(dbg_context.backtrace);
- dbg_context.contexts = collect_caller_bindings(th);
+ dbg_context.contexts = collect_caller_bindings(ec);
- TH_PUSH_TAG(th);
- if ((state = EXEC_TAG()) == 0) {
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
result = (*func)(&dbg_context, data);
}
- TH_POP_TAG();
+ EC_POP_TAG();
/* invalidate bindings? */
if (state) {
- TH_JUMP_TAG(th, state);
+ EC_JUMP_TAG(ec, state);
}
return result;
@@ -1245,8 +1285,8 @@ int
rb_profile_frames(int start, int limit, VALUE *buff, int *lines)
{
int i;
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp, *end_cfp = RUBY_VM_END_CONTROL_FRAME(th);
+ const rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = ec->cfp, *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
const rb_callable_method_entry_t *cme;
for (i=0; i<limit && cfp != end_cfp;) {
@@ -1312,7 +1352,7 @@ VALUE
rb_profile_frame_absolute_path(VALUE frame)
{
const rb_iseq_t *iseq = frame2iseq(frame);
- return iseq ? rb_iseq_absolute_path(iseq) : Qnil;
+ return iseq ? rb_iseq_realpath(iseq) : Qnil;
}
VALUE
@@ -1362,7 +1402,7 @@ rb_profile_frame_classpath(VALUE frame)
}
else if (FL_TEST(klass, FL_SINGLETON)) {
klass = rb_ivar_get(klass, id__attached__);
- if (!RB_TYPE_P(klass, T_CLASS))
+ if (!RB_TYPE_P(klass, T_CLASS) && !RB_TYPE_P(klass, T_MODULE))
return rb_sprintf("#<%s:%p>", rb_class2name(rb_obj_class(klass)), (void*)klass);
}
return rb_class_path(klass);
diff --git a/vm_core.h b/vm_core.h
index 93b1152aea..fcef0809af 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -45,23 +45,31 @@
#include "ruby_assert.h"
#if VM_CHECK_MODE > 0
-#define VM_ASSERT(expr) ( \
- RUBY_ASSERT_MESG_WHEN(VM_CHECK_MODE > 0, expr, #expr))
+#define VM_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(VM_CHECK_MODE > 0, expr, #expr)
#define VM_UNREACHABLE(func) rb_bug(#func ": unreachable")
#else
#define VM_ASSERT(expr) ((void)0)
-#define VM_UNREACHABLE(func) ((void)0)
+#define VM_UNREACHABLE(func) UNREACHABLE
#endif
#define RUBY_VM_THREAD_MODEL 2
+/*
+ * implementation selector of get_insn_info algorithm
+ * 0: linear search
+ * 1: binary search
+ * 2: succinct bitvector
+ */
+#ifndef VM_INSN_INFO_TABLE_IMPL
+# define VM_INSN_INFO_TABLE_IMPL 2
+#endif
+
#include "ruby/ruby.h"
#include "ruby/st.h"
#include "node.h"
-#include "vm_debug.h"
#include "vm_opts.h"
#include "id.h"
#include "method.h"
@@ -75,28 +83,43 @@
#include "thread_pthread.h"
#endif
-#ifndef ENABLE_VM_OBJSPACE
-#ifdef _WIN32
-/*
- * TODO: object space independent st_table.
- * socklist and conlist will be freed exit_handler(), after object
- * space destruction.
- */
-#define ENABLE_VM_OBJSPACE 0
-#else
-#define ENABLE_VM_OBJSPACE 1
-#endif
-#endif
-
#include <setjmp.h>
#include <signal.h>
-#ifndef NSIG
-# define NSIG (_SIGMAX + 1) /* For QNX */
+#if defined(NSIG_MAX) /* POSIX issue 8 */
+# undef NSIG
+# define NSIG NSIG_MAX
+#elif defined(_SIG_MAXSIG) /* FreeBSD */
+# undef NSIG
+# define NSIG _SIG_MAXSIG
+#elif defined(_SIGMAX) /* QNX */
+# define NSIG (_SIGMAX + 1)
+#elif defined(NSIG) /* 99% of everything else */
+# /* take it */
+#else /* Last resort */
+# define NSIG (sizeof(sigset_t) * CHAR_BIT + 1)
#endif
#define RUBY_NSIG NSIG
+#if defined(SIGCLD)
+# define RUBY_SIGCHLD (SIGCLD)
+#elif defined(SIGCHLD)
+# define RUBY_SIGCHLD (SIGCHLD)
+#else
+# define RUBY_SIGCHLD (0)
+#endif
+
+/* platforms with broken or non-existent SIGCHLD work by polling */
+#if defined(__APPLE__)
+# define SIGCHLD_LOSSY (1)
+#else
+# define SIGCHLD_LOSSY (0)
+#endif
+
+/* define to 0 to test old code path */
+#define WAITPID_USE_SIGCHLD (RUBY_SIGCHLD || SIGCHLD_LOSSY)
+
#ifdef HAVE_STDARG_PROTOTYPES
#include <stdarg.h>
#define va_init_list(a,b) va_start((a),(b))
@@ -106,7 +129,15 @@
#endif
#if defined(SIGSEGV) && defined(HAVE_SIGALTSTACK) && defined(SA_SIGINFO) && !defined(__NetBSD__)
-#define USE_SIGALTSTACK
+# define USE_SIGALTSTACK
+void *rb_register_sigaltstack(void);
+# define RB_ALTSTACK_INIT(var) var = rb_register_sigaltstack()
+# define RB_ALTSTACK_FREE(var) xfree(var)
+# define RB_ALTSTACK(var) var
+#else /* noop */
+# define RB_ALTSTACK_INIT(var)
+# define RB_ALTSTACK_FREE(var)
+# define RB_ALTSTACK(var) (0)
#endif
/*****************/
@@ -133,10 +164,6 @@
#endif
#endif
-#ifdef __native_client__
-#undef OPT_DIRECT_THREADED_CODE
-#endif
-
/* call threaded code */
#if OPT_CALL_THREADED_CODE
#if OPT_DIRECT_THREADED_CODE
@@ -147,9 +174,12 @@
#endif /* OPT_STACK_CACHING */
#endif /* OPT_CALL_THREADED_CODE */
+void rb_vm_encoded_insn_data_table_init(void);
typedef unsigned long rb_num_t;
+typedef signed long rb_snum_t;
enum ruby_tag_type {
+ RUBY_TAG_NONE = 0x0,
RUBY_TAG_RETURN = 0x1,
RUBY_TAG_BREAK = 0x2,
RUBY_TAG_NEXT = 0x3,
@@ -160,6 +190,8 @@ enum ruby_tag_type {
RUBY_TAG_FATAL = 0x8,
RUBY_TAG_MASK = 0xf
};
+
+#define TAG_NONE RUBY_TAG_NONE
#define TAG_RETURN RUBY_TAG_RETURN
#define TAG_BREAK RUBY_TAG_BREAK
#define TAG_NEXT RUBY_TAG_NEXT
@@ -172,7 +204,6 @@ enum ruby_tag_type {
enum ruby_vm_throw_flags {
VM_THROW_NO_ESCAPE_FLAG = 0x8000,
- VM_THROW_LEVEL_SHIFT = 16,
VM_THROW_STATE_MASK = 0xff
};
@@ -235,7 +266,8 @@ struct rb_calling_info {
};
struct rb_call_cache;
-typedef VALUE (*vm_call_handler)(struct rb_thread_struct *th, struct rb_control_frame_struct *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
+struct rb_execution_context_struct;
+typedef VALUE (*vm_call_handler)(struct rb_execution_context_struct *ec, struct rb_control_frame_struct *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
struct rb_call_cache {
/* inline cache: keys */
@@ -262,13 +294,44 @@ struct rb_call_cache {
#define GetCoreDataFromValue(obj, type, ptr) ((ptr) = CoreDataFromValue((obj), type))
typedef struct rb_iseq_location_struct {
- VALUE path;
- VALUE absolute_path;
- VALUE base_label;
- VALUE label;
+ VALUE pathobj; /* String (path) or Array [path, realpath]. Frozen. */
+ VALUE base_label; /* String */
+ VALUE label; /* String */
VALUE first_lineno; /* TODO: may be unsigned short */
+ int node_id;
+ rb_code_location_t code_location;
} rb_iseq_location_t;
+#define PATHOBJ_PATH 0
+#define PATHOBJ_REALPATH 1
+
+static inline VALUE
+pathobj_path(VALUE pathobj)
+{
+ if (RB_TYPE_P(pathobj, T_STRING)) {
+ return pathobj;
+ }
+ else {
+ VM_ASSERT(RB_TYPE_P(pathobj, T_ARRAY));
+ return RARRAY_AREF(pathobj, PATHOBJ_PATH);
+ }
+}
+
+static inline VALUE
+pathobj_realpath(VALUE pathobj)
+{
+ if (RB_TYPE_P(pathobj, T_STRING)) {
+ return pathobj;
+ }
+ else {
+ VM_ASSERT(RB_TYPE_P(pathobj, T_ARRAY));
+ return RARRAY_AREF(pathobj, PATHOBJ_REALPATH);
+ }
+}
+
+/* Forward declarations */
+struct rb_mjit_unit;
+
struct rb_iseq_constant_body {
enum iseq_type {
ISEQ_TYPE_TOP,
@@ -279,7 +342,7 @@ struct rb_iseq_constant_body {
ISEQ_TYPE_ENSURE,
ISEQ_TYPE_EVAL,
ISEQ_TYPE_MAIN,
- ISEQ_TYPE_DEFINED_GUARD
+ ISEQ_TYPE_PLAIN
} type; /* instruction sequence type */
unsigned int iseq_size;
@@ -358,7 +421,14 @@ struct rb_iseq_constant_body {
rb_iseq_location_t location;
/* insn info, must be freed */
- const struct iseq_line_info_entry *line_info_table;
+ struct iseq_insn_info {
+ const struct iseq_insn_info_entry *body;
+ unsigned int *positions;
+ unsigned int size;
+#if VM_INSN_INFO_TABLE_IMPL == 2
+ struct succ_index_table *succ_index_table;
+#endif
+ } insns_info;
const ID *local_table; /* must free */
@@ -375,24 +445,38 @@ struct rb_iseq_constant_body {
* So that:
* struct rb_call_info_with_kwarg *cikw_entries = &body->ci_entries[ci_size];
*/
- struct rb_call_cache *cc_entries; /* size is ci_size = ci_kw_size */
+ struct rb_call_cache *cc_entries; /* size is ci_size + ci_kw_size */
- VALUE mark_ary; /* Array: includes operands which should be GC marked */
+ struct {
+ rb_snum_t flip_count;
+ VALUE coverage;
+ VALUE pc2branchindex;
+ VALUE *original_iseq;
+ } variable;
unsigned int local_table_size;
unsigned int is_size;
unsigned int ci_size;
unsigned int ci_kw_size;
- unsigned int line_info_size;
unsigned int stack_max; /* for stack overflow check */
+
+#if USE_MJIT
+ /* The following fields are MJIT related info. */
+ VALUE (*jit_func)(struct rb_execution_context_struct *,
+ struct rb_control_frame_struct *); /* function pointer for loaded native code */
+ long unsigned total_calls; /* number of total calls with `mjit_exec()` */
+ struct rb_mjit_unit *jit_unit;
+#endif
+ char catch_except_p; /* If a frame of this ISeq may catch exception, set TRUE */
};
/* T_IMEMO/iseq */
/* typedef rb_iseq_t is in method.h */
struct rb_iseq_struct {
- VALUE flags;
- VALUE reserved1;
- struct rb_iseq_constant_body *body;
+ VALUE flags; /* 1 */
+ VALUE wrapper; /* 2 */
+
+ struct rb_iseq_constant_body *body; /* 3 */
union { /* 4, 5 words */
struct iseq_compile_data *compile_data; /* used at compile time */
@@ -401,6 +485,11 @@ struct rb_iseq_struct {
VALUE obj;
int index;
} loader;
+
+ struct {
+ struct rb_hook_list_struct *local_hooks;
+ rb_event_flag_t global_trace_events;
+ } exec;
} aux;
};
@@ -423,11 +512,21 @@ rb_iseq_check(const rb_iseq_t *iseq)
return iseq;
}
+static inline const rb_iseq_t *
+def_iseq_ptr(rb_method_definition_t *def)
+{
+#if VM_CHECK_MODE > 0
+ if (def->type != VM_METHOD_TYPE_ISEQ) rb_bug("def_iseq_ptr: not iseq (%d)", def->type);
+#endif
+ return rb_iseq_check(def->body.iseq.iseqptr);
+}
+
enum ruby_special_exceptions {
ruby_error_reenter,
ruby_error_nomemory,
ruby_error_sysstack,
- ruby_error_closed_stream,
+ ruby_error_stackfatal,
+ ruby_error_stream_closed,
ruby_special_error_count
};
@@ -454,8 +553,12 @@ enum ruby_basic_operators {
BOP_NEQ,
BOP_MATCH,
BOP_FREEZE,
+ BOP_UMINUS,
BOP_MAX,
BOP_MIN,
+ BOP_CALL,
+ BOP_AND,
+ BOP_OR,
BOP_LAST_
};
@@ -478,27 +581,39 @@ void rb_objspace_free(struct rb_objspace *);
typedef struct rb_hook_list_struct {
struct rb_event_hook_struct *hooks;
rb_event_flag_t events;
- int need_clean;
+ unsigned int need_clean;
+ unsigned int running;
} rb_hook_list_t;
typedef struct rb_vm_struct {
VALUE self;
rb_global_vm_lock_t gvl;
- rb_nativethread_lock_t thread_destruct_lock;
struct rb_thread_struct *main_thread;
- struct rb_thread_struct *running_thread;
+ /* persists across uncontended GVL release/acquire for time slice */
+ const struct rb_thread_struct *running_thread;
+
+#ifdef USE_SIGALTSTACK
+ void *main_altstack;
+#endif
+
+ rb_serial_t fork_gen;
+ rb_nativethread_lock_t waitpid_lock;
+ struct list_head waiting_pids; /* PID > 0: <=> struct waitpid_state */
+ struct list_head waiting_grps; /* PID <= 0: <=> struct waitpid_state */
+ struct list_head waiting_fds; /* <=> struct waiting_fd */
struct list_head living_threads;
- size_t living_thread_num;
VALUE thgroup_default;
+ int living_thread_num;
unsigned int running: 1;
unsigned int thread_abort_on_exception: 1;
unsigned int thread_report_on_exception: 1;
- unsigned int trace_running: 1;
- volatile int sleeper;
+
+ unsigned int safe_level_: 1;
+ int sleeper;
/* object management */
VALUE mark_object_ary;
@@ -517,24 +632,29 @@ typedef struct rb_vm_struct {
/* signal */
struct {
- VALUE cmd;
- int safe;
- } trap_list[RUBY_NSIG];
+ VALUE cmd[RUBY_NSIG];
+ unsigned char safe[RUBY_NSIG];
+ } trap_list;
/* hook */
- rb_hook_list_t event_hooks;
+ rb_hook_list_t global_hooks;
/* relation table of ensure - rollback for callcc */
struct st_table *ensure_rollback_table;
- /* postponed_job */
+ /* postponed_job (async-signal-safe, NOT thread-safe) */
struct rb_postponed_job_struct *postponed_job_buffer;
int postponed_job_index;
int src_encoding_index;
+ /* workqueue (thread-safe, NOT async-signal-safe) */
+ struct list_head workqueue; /* <=> rb_workqueue_job.jnode */
+ rb_nativethread_lock_t workqueue_lock;
+
VALUE verbose, debug, orig_progname, progname;
VALUE coverages;
+ int coverage_mode;
VALUE defined_module_hash;
@@ -568,7 +688,11 @@ typedef struct rb_vm_struct {
#define RUBY_VM_FIBER_VM_STACK_SIZE ( 16 * 1024 * sizeof(VALUE)) /* 64 KB or 128 KB */
#define RUBY_VM_FIBER_VM_STACK_SIZE_MIN ( 2 * 1024 * sizeof(VALUE)) /* 8 KB or 16 KB */
#define RUBY_VM_FIBER_MACHINE_STACK_SIZE ( 64 * 1024 * sizeof(VALUE)) /* 256 KB or 512 KB */
+#if defined(__powerpc64__)
+#define RUBY_VM_FIBER_MACHINE_STACK_SIZE_MIN ( 32 * 1024 * sizeof(VALUE)) /* 128 KB or 256 KB */
+#else
#define RUBY_VM_FIBER_MACHINE_STACK_SIZE_MIN ( 16 * 1024 * sizeof(VALUE)) /* 64 KB or 128 KB */
+#endif
/* optimize insn */
#define INTEGER_REDEFINED_OP_FLAG (1 << 0)
@@ -583,6 +707,7 @@ typedef struct rb_vm_struct {
#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((GET_VM()->redefined_flag[(op)]&(klass)) == 0))
@@ -634,16 +759,20 @@ typedef struct rb_control_frame_struct {
VALUE self; /* cfp[3] / block[0] */
const VALUE *ep; /* cfp[4] / block[1] */
const void *block_code; /* cfp[5] / block[2] */ /* iseq or ifunc */
+ const VALUE *bp; /* cfp[6] */
#if VM_DEBUG_BP_CHECK
- VALUE *bp_check; /* cfp[6] */
+ VALUE *bp_check; /* cfp[7] */
#endif
} rb_control_frame_t;
extern const rb_data_type_t ruby_threadptr_data_type;
-#define GetThreadPtr(obj, ptr) \
- TypedData_Get_Struct((obj), rb_thread_t, &ruby_threadptr_data_type, (ptr))
+static inline struct rb_thread_struct *
+rb_thread_ptr(VALUE thval)
+{
+ return (struct rb_thread_struct *)rb_check_typeddata(thval, &ruby_threadptr_data_type);
+}
enum rb_thread_status {
THREAD_RUNNABLE,
@@ -655,7 +784,7 @@ enum rb_thread_status {
typedef RUBY_JMP_BUF rb_jmpbuf_t;
/*
- the members which are written in TH_PUSH_TAG() should be placed at
+ the members which are written in EC_PUSH_TAG() should be placed at
the beginning and the end, so that entire region is accessible.
*/
struct rb_vm_tag {
@@ -663,8 +792,14 @@ struct rb_vm_tag {
VALUE retval;
rb_jmpbuf_t buf;
struct rb_vm_tag *prev;
+ enum ruby_tag_type state;
};
+STATIC_ASSERT(rb_vm_tag_buf_offset, offsetof(struct rb_vm_tag, buf) > 0);
+STATIC_ASSERT(rb_vm_tag_buf_end,
+ offsetof(struct rb_vm_tag, buf) + sizeof(rb_jmpbuf_t) <
+ sizeof(struct rb_vm_tag));
+
struct rb_vm_protect_tag {
struct rb_vm_protect_tag *prev;
};
@@ -696,29 +831,72 @@ typedef char rb_thread_id_string_t[sizeof(rb_nativethread_id_t) * 2 + 3];
typedef struct rb_fiber_struct rb_fiber_t;
-typedef struct rb_thread_struct {
- struct list_node vmlt_node;
- VALUE self;
- rb_vm_t *vm;
-
+typedef struct rb_execution_context_struct {
/* execution information */
- VALUE *stack; /* must free, must mark */
- size_t stack_size; /* size in word (byte size / sizeof(VALUE)) */
+ VALUE *vm_stack; /* must free, must mark */
+ size_t vm_stack_size; /* size in word (byte size / sizeof(VALUE)) */
rb_control_frame_t *cfp;
- int safe_level;
- int raised_flag;
- VALUE last_status; /* $? */
- /* passing state */
- int state;
+ struct rb_vm_tag *tag;
+ struct rb_vm_protect_tag *protect_tag;
+
+ /* interrupt flags */
+ rb_atomic_t interrupt_flag;
+ rb_atomic_t interrupt_mask; /* size should match flag */
+
+ rb_fiber_t *fiber_ptr;
+ struct rb_thread_struct *thread_ptr;
+
+ /* storage (ec (fiber) local) */
+ st_table *local_storage;
+ VALUE local_storage_recursive_hash;
+ VALUE local_storage_recursive_hash_for_trace;
+
+ /* eval env */
+ const VALUE *root_lep;
+ VALUE root_svar;
+
+ /* ensure & callcc */
+ rb_ensure_list_t *ensure_list;
+
+ /* trace information */
+ struct rb_trace_arg_struct *trace_arg;
+
+ /* temporary places */
+ VALUE errinfo;
+ VALUE passed_block_handler; /* for rb_iterate */
+
+ uint8_t raised_flag; /* only 3 bits needed */
- int waiting_fd;
+ /* n.b. only 7 bits needed, really: */
+ BITFIELD(enum method_missing_reason, method_missing_reason, 8);
- /* for rb_iterate */
- VALUE passed_block_handler;
+ VALUE private_const_reference;
- /* for bmethod */
- const rb_callable_method_entry_t *passed_bmethod_me;
+ /* for GC */
+ struct {
+ VALUE *stack_start;
+ VALUE *stack_end;
+ size_t stack_maxsize;
+#ifdef __ia64
+ VALUE *register_stack_start;
+ VALUE *register_stack_end;
+ size_t register_stack_maxsize;
+#endif
+ RUBY_ALIGNAS(SIZEOF_VALUE) jmp_buf regs;
+ } machine;
+} rb_execution_context_t;
+
+void rb_ec_set_vm_stack(rb_execution_context_t *ec, VALUE *stack, size_t size);
+
+typedef struct rb_thread_struct {
+ struct list_node vmlt_node;
+ VALUE self;
+ rb_vm_t *vm;
+
+ rb_execution_context_t *ec;
+
+ VALUE last_status; /* $? */
/* for cfunc */
struct rb_calling_info *calling;
@@ -727,18 +905,19 @@ typedef struct rb_thread_struct {
VALUE top_self;
VALUE top_wrapper;
- /* eval env */
- const VALUE *root_lep;
- VALUE root_svar;
-
/* thread control */
rb_nativethread_id_t thread_id;
#ifdef NON_SCALAR_THREAD_ID
rb_thread_id_string_t thread_id_string;
#endif
- enum rb_thread_status status;
- int to_kill;
- int priority;
+ BITFIELD(enum rb_thread_status, status, 2);
+ /* bit flags */
+ unsigned int to_kill : 1;
+ unsigned int abort_on_exception: 1;
+ unsigned int report_on_exception: 1;
+ unsigned int pending_interrupt_queue_checked: 1;
+ int8_t priority; /* -3 .. 3 (RUBY_THREAD_PRIORITY_{MIN,MAX}) */
+ uint32_t running_time_us; /* 12500..800000 */
native_thread_data_t native_thread_data;
void *blocking_region_buffer;
@@ -746,9 +925,6 @@ typedef struct rb_thread_struct {
VALUE thgroup;
VALUE value;
- /* temporary place of errinfo */
- VALUE errinfo;
-
/* temporary place of retval on OPT_CALL_THREADED_CODE */
#if OPT_CALL_THREADED_CODE
VALUE retval;
@@ -757,67 +933,42 @@ typedef struct rb_thread_struct {
/* async errinfo queue */
VALUE pending_interrupt_queue;
VALUE pending_interrupt_mask_stack;
- int pending_interrupt_queue_checked;
- rb_atomic_t interrupt_flag;
- unsigned long interrupt_mask;
+ /* interrupt management */
rb_nativethread_lock_t interrupt_lock;
- rb_nativethread_cond_t interrupt_cond;
struct rb_unblock_callback unblock;
VALUE locking_mutex;
struct rb_mutex_struct *keeping_mutexes;
- struct rb_vm_tag *tag;
- struct rb_vm_protect_tag *protect_tag;
-
- /* storage */
- st_table *local_storage;
- VALUE local_storage_recursive_hash;
- VALUE local_storage_recursive_hash_for_trace;
-
rb_thread_list_t *join_list;
- VALUE first_proc;
- VALUE first_args;
- VALUE (*first_func)(ANYARGS);
-
- /* for GC */
- struct {
- VALUE *stack_start;
- VALUE *stack_end;
- size_t stack_maxsize;
-#ifdef __ia64
- VALUE *register_stack_start;
- VALUE *register_stack_end;
- size_t register_stack_maxsize;
-#endif
- jmp_buf regs;
- } machine;
+ union {
+ struct {
+ VALUE proc;
+ VALUE args;
+ } proc;
+ struct {
+ VALUE (*func)(ANYARGS);
+ void *arg;
+ } func;
+ } invoke_arg;
+
+ enum {
+ thread_invoke_type_none = 0,
+ thread_invoke_type_proc,
+ thread_invoke_type_func
+ } invoke_type;
/* statistics data for profiler */
VALUE stat_insn_usage;
- /* tracer */
- rb_hook_list_t event_hooks;
- struct rb_trace_arg_struct *trace_arg; /* trace information */
-
/* fiber */
- rb_fiber_t *fiber;
rb_fiber_t *root_fiber;
rb_jmpbuf_t root_jmpbuf;
- /* ensure & callcc */
- rb_ensure_list_t *ensure_list;
-
/* misc */
- enum method_missing_reason method_missing_reason: 8;
- unsigned int abort_on_exception: 1;
- unsigned int report_on_exception: 1;
-#ifdef USE_SIGALTSTACK
- void *altstack;
-#endif
- unsigned long running_time_us;
VALUE name;
+
} rb_thread_t;
typedef enum {
@@ -839,16 +990,18 @@ typedef enum {
RUBY_SYMBOL_EXPORT_BEGIN
/* node -> iseq */
-rb_iseq_t *rb_iseq_new(NODE*, VALUE, VALUE, VALUE, const rb_iseq_t *parent, enum iseq_type);
-rb_iseq_t *rb_iseq_new_top(NODE *node, VALUE name, VALUE path, VALUE absolute_path, const rb_iseq_t *parent);
-rb_iseq_t *rb_iseq_new_main(NODE *node, VALUE path, VALUE absolute_path, const rb_iseq_t *parent);
-rb_iseq_t *rb_iseq_new_with_bopt(NODE*, VALUE, VALUE, VALUE, VALUE, VALUE, enum iseq_type, VALUE);
-rb_iseq_t *rb_iseq_new_with_opt(NODE*, VALUE, VALUE, VALUE, VALUE, const rb_iseq_t *parent, enum iseq_type, const rb_compile_option_t*);
+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);
+rb_iseq_t *rb_iseq_new_top (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent);
+rb_iseq_t *rb_iseq_new_main (const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_iseq_t *parent);
+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, enum iseq_type, const rb_compile_option_t*);
+rb_iseq_t *rb_iseq_new_ifunc(const struct vm_ifunc *ifunc, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno,
+ const rb_iseq_t *parent, enum iseq_type, const rb_compile_option_t*);
/* src -> iseq */
rb_iseq_t *rb_iseq_compile(VALUE src, VALUE file, VALUE line);
rb_iseq_t *rb_iseq_compile_on_base(VALUE src, VALUE file, VALUE line, const struct rb_block *base_block);
-rb_iseq_t *rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE absolute_path, VALUE line, const struct rb_block *base_block, VALUE opt);
+rb_iseq_t *rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, const struct rb_block *base_block, VALUE opt);
VALUE rb_iseq_disasm(const rb_iseq_t *iseq);
int rb_iseq_disasm_insn(VALUE str, const VALUE *iseqval, size_t pos, const rb_iseq_t *iseq, VALUE child);
@@ -866,9 +1019,8 @@ RUBY_SYMBOL_EXPORT_END
typedef struct {
const struct rb_block block;
- int8_t safe_level; /* 0..1 */
- int8_t is_from_method; /* bool */
- int8_t is_lambda; /* bool */
+ unsigned int is_from_method: 1; /* bool */
+ unsigned int is_lambda: 1; /* bool */
} rb_proc_t;
typedef struct {
@@ -885,8 +1037,8 @@ extern const rb_data_type_t ruby_binding_data_type;
GetCoreDataFromValue((obj), rb_binding_t, (ptr))
typedef struct {
- struct rb_block block;
- VALUE path;
+ const struct rb_block block;
+ const VALUE pathobj;
unsigned short first_lineno;
} rb_binding_t;
@@ -901,16 +1053,34 @@ enum vm_check_match_type {
#define VM_CHECKMATCH_TYPE_MASK 0x03
#define VM_CHECKMATCH_ARRAY 0x04
-#define VM_CALL_ARGS_SPLAT (0x01 << 0) /* m(*args) */
-#define VM_CALL_ARGS_BLOCKARG (0x01 << 1) /* m(&block) */
-#define VM_CALL_FCALL (0x01 << 2) /* m(...) */
-#define VM_CALL_VCALL (0x01 << 3) /* m */
-#define VM_CALL_ARGS_SIMPLE (0x01 << 4) /* (ci->flag & (SPLAT|BLOCKARG)) && blockiseq == NULL && ci->kw_arg == NULL */
-#define VM_CALL_BLOCKISEQ (0x01 << 5) /* has blockiseq */
-#define VM_CALL_KWARG (0x01 << 6) /* has kwarg */
-#define VM_CALL_TAILCALL (0x01 << 7) /* located at tail position */
-#define VM_CALL_SUPER (0x01 << 8) /* super */
-#define VM_CALL_OPT_SEND (0x01 << 9) /* internal flag */
+enum vm_call_flag_bits {
+ VM_CALL_ARGS_SPLAT_bit, /* m(*args) */
+ VM_CALL_ARGS_BLOCKARG_bit, /* m(&block) */
+ VM_CALL_FCALL_bit, /* m(...) */
+ VM_CALL_VCALL_bit, /* m */
+ VM_CALL_ARGS_SIMPLE_bit, /* (ci->flag & (SPLAT|BLOCKARG)) && blockiseq == NULL && ci->kw_arg == NULL */
+ VM_CALL_BLOCKISEQ_bit, /* has blockiseq */
+ VM_CALL_KWARG_bit, /* has kwarg */
+ VM_CALL_KW_SPLAT_bit, /* m(**opts) */
+ VM_CALL_TAILCALL_bit, /* located at tail position */
+ VM_CALL_SUPER_bit, /* super */
+ VM_CALL_ZSUPER_bit, /* zsuper */
+ VM_CALL_OPT_SEND_bit, /* internal flag */
+ VM_CALL__END
+};
+
+#define VM_CALL_ARGS_SPLAT (0x01 << VM_CALL_ARGS_SPLAT_bit)
+#define VM_CALL_ARGS_BLOCKARG (0x01 << VM_CALL_ARGS_BLOCKARG_bit)
+#define VM_CALL_FCALL (0x01 << VM_CALL_FCALL_bit)
+#define VM_CALL_VCALL (0x01 << VM_CALL_VCALL_bit)
+#define VM_CALL_ARGS_SIMPLE (0x01 << VM_CALL_ARGS_SIMPLE_bit)
+#define VM_CALL_BLOCKISEQ (0x01 << VM_CALL_BLOCKISEQ_bit)
+#define VM_CALL_KWARG (0x01 << VM_CALL_KWARG_bit)
+#define VM_CALL_KW_SPLAT (0x01 << VM_CALL_KW_SPLAT_bit)
+#define VM_CALL_TAILCALL (0x01 << VM_CALL_TAILCALL_bit)
+#define VM_CALL_SUPER (0x01 << VM_CALL_SUPER_bit)
+#define VM_CALL_ZSUPER (0x01 << VM_CALL_ZSUPER_bit)
+#define VM_CALL_OPT_SEND (0x01 << VM_CALL_OPT_SEND_bit)
enum vm_special_object_type {
VM_SPECIAL_OBJECT_VMCORE = 1,
@@ -928,6 +1098,7 @@ enum vm_svar_index {
/* inline cache */
typedef struct iseq_inline_cache_entry *IC;
+typedef union iseq_inline_storage_entry *ISE;
typedef struct rb_call_info *CALL_INFO;
typedef struct rb_call_cache *CALL_CACHE;
@@ -940,7 +1111,7 @@ typedef VALUE CDHASH;
#endif
typedef rb_control_frame_t *
- (FUNC_FASTCALL(*rb_insn_func_t))(rb_thread_t *, rb_control_frame_t *);
+ (FUNC_FASTCALL(*rb_insn_func_t))(rb_execution_context_t *, rb_control_frame_t *);
#define VM_TAGGED_PTR_SET(p, tag) ((VALUE)(p) | (tag))
#define VM_TAGGED_PTR_REF(v, mask) ((void *)((v) & ~mask))
@@ -951,12 +1122,12 @@ typedef rb_control_frame_t *
enum {
/* Frame/Environment flag bits:
- * MMMM MMMM MMMM MMMM ____ ____ FFFF EEEX (LSB)
+ * MMMM MMMM MMMM MMMM ____ __FF FFFF EEEX (LSB)
*
* X : tag for GC marking (It seems as Fixnum)
* EEE : 3 bits Env flags
- * FFFF: 4 bits Frame flags
- * MMMM: 16 bits frame magic (to check frame corruption)
+ * FF..: 6 bits Frame flags
+ * MM..: 15 bits frame magic (to check frame corruption)
*/
/* frame types */
@@ -965,20 +1136,20 @@ enum {
VM_FRAME_MAGIC_CLASS = 0x33330001,
VM_FRAME_MAGIC_TOP = 0x44440001,
VM_FRAME_MAGIC_CFUNC = 0x55550001,
- VM_FRAME_MAGIC_PROC = 0x66660001,
- VM_FRAME_MAGIC_IFUNC = 0x77770001,
- VM_FRAME_MAGIC_EVAL = 0x88880001,
- VM_FRAME_MAGIC_LAMBDA = 0x99990001,
- VM_FRAME_MAGIC_RESCUE = 0xaaaa0001,
- VM_FRAME_MAGIC_DUMMY = 0xbbbb0001,
+ VM_FRAME_MAGIC_IFUNC = 0x66660001,
+ VM_FRAME_MAGIC_EVAL = 0x77770001,
+ VM_FRAME_MAGIC_RESCUE = 0x78880001,
+ VM_FRAME_MAGIC_DUMMY = 0x79990001,
- VM_FRAME_MAGIC_MASK = 0xffff0001,
+ VM_FRAME_MAGIC_MASK = 0x7fff0001,
/* frame flag */
VM_FRAME_FLAG_PASSED = 0x0010,
VM_FRAME_FLAG_FINISH = 0x0020,
VM_FRAME_FLAG_BMETHOD = 0x0040,
VM_FRAME_FLAG_CFRAME = 0x0080,
+ VM_FRAME_FLAG_LAMBDA = 0x0100,
+ VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM = 0x0200,
/* env flag */
VM_ENV_FLAG_LOCAL = 0x0002,
@@ -992,7 +1163,6 @@ enum {
#define VM_ENV_DATA_INDEX_SPECVAL (-1) /* ep[-1] */
#define VM_ENV_DATA_INDEX_FLAGS ( 0) /* ep[ 0] */
#define VM_ENV_DATA_INDEX_ENV ( 1) /* ep[ 1] */
-#define VM_ENV_DATA_INDEX_ENV_PROC ( 2) /* ep[ 2] */
#define VM_ENV_INDEX_LAST_LVAR (-VM_ENV_DATA_SIZE)
@@ -1029,9 +1199,15 @@ VM_FRAME_TYPE(const rb_control_frame_t *cfp)
}
static inline int
+VM_FRAME_LAMBDA_P(const rb_control_frame_t *cfp)
+{
+ return VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_LAMBDA) != 0;
+}
+
+static inline int
VM_FRAME_FINISHED_P(const rb_control_frame_t *cfp)
{
- return VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_FINISH ) != 0;
+ return VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_FINISH) != 0;
}
static inline int
@@ -1043,7 +1219,7 @@ VM_FRAME_BMETHOD_P(const rb_control_frame_t *cfp)
static inline int
rb_obj_is_iseq(VALUE iseq)
{
- return RB_TYPE_P(iseq, T_IMEMO) && imemo_type(iseq) == imemo_iseq;
+ return imemo_type_p(iseq, imemo_iseq);
}
#if VM_CHECK_MODE > 0
@@ -1105,8 +1281,7 @@ VM_ENV_ESCAPED_P(const VALUE *ep)
static inline int
vm_assert_env(VALUE obj)
{
- VM_ASSERT(RB_TYPE_P(obj, T_IMEMO));
- VM_ASSERT(imemo_type(obj) == imemo_env);
+ VM_ASSERT(imemo_type_p(obj, imemo_env));
return 1;
}
#endif
@@ -1126,16 +1301,6 @@ VM_ENV_ENVVAL_PTR(const VALUE *ep)
return (const rb_env_t *)VM_ENV_ENVVAL(ep);
}
-static inline VALUE
-VM_ENV_PROCVAL(const VALUE *ep)
-{
- VM_ASSERT(VM_ENV_ESCAPED_P(ep));
- VM_ASSERT(VM_ENV_LOCAL_P(ep));
- VM_ASSERT(VM_ENV_BLOCK_HANDLER(ep) != VM_BLOCK_HANDLER_NONE);
-
- return ep[VM_ENV_DATA_INDEX_ENV_PROC];
-}
-
static inline const rb_env_t *
vm_env_new(VALUE *env_ep, VALUE *env_body, unsigned int env_size, const rb_iseq_t *iseq)
{
@@ -1166,16 +1331,29 @@ VM_STACK_ENV_WRITE(const VALUE *ep, int index, VALUE v)
}
const VALUE *rb_vm_ep_local_ep(const VALUE *ep);
+const VALUE *rb_vm_proc_local_ep(VALUE proc);
+void rb_vm_block_ep_update(VALUE obj, const struct rb_block *dst, const VALUE *ep);
+void rb_vm_block_copy(VALUE obj, const struct rb_block *dst, const struct rb_block *src);
+
VALUE rb_vm_frame_block_handler(const rb_control_frame_t *cfp);
#define RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp) ((cfp)+1)
#define RUBY_VM_NEXT_CONTROL_FRAME(cfp) ((cfp)-1)
-#define RUBY_VM_END_CONTROL_FRAME(th) \
- ((rb_control_frame_t *)((th)->stack + (th)->stack_size))
+
#define RUBY_VM_VALID_CONTROL_FRAME_P(cfp, ecfp) \
((void *)(ecfp) > (void *)(cfp))
-#define RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp) \
- (!RUBY_VM_VALID_CONTROL_FRAME_P((cfp), RUBY_VM_END_CONTROL_FRAME(th)))
+
+static inline const rb_control_frame_t *
+RUBY_VM_END_CONTROL_FRAME(const rb_execution_context_t *ec)
+{
+ return (rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size);
+}
+
+static inline int
+RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
+{
+ return !RUBY_VM_VALID_CONTROL_FRAME_P(cfp, RUBY_VM_END_CONTROL_FRAME(ec));
+}
static inline int
VM_BH_ISEQ_BLOCK_P(VALUE block_handler)
@@ -1183,8 +1361,7 @@ VM_BH_ISEQ_BLOCK_P(VALUE block_handler)
if ((block_handler & 0x03) == 0x01) {
#if VM_CHECK_MODE > 0
struct rb_captured_block *captured = VM_TAGGED_PTR_REF(block_handler, 0x03);
- VM_ASSERT(RB_TYPE_P(captured->code.val, T_IMEMO));
- VM_ASSERT(imemo_type(captured->code.val) == imemo_iseq);
+ VM_ASSERT(imemo_type_p(captured->code.val, imemo_iseq));
#endif
return 1;
}
@@ -1215,8 +1392,7 @@ VM_BH_IFUNC_P(VALUE block_handler)
if ((block_handler & 0x03) == 0x03) {
#if VM_CHECK_MODE > 0
struct rb_captured_block *captured = (void *)(block_handler & ~0x03);
- VM_ASSERT(RB_TYPE_P(captured->code.val, T_IMEMO));
- VM_ASSERT(imemo_type(captured->code.val) == imemo_ifunc);
+ VM_ASSERT(imemo_type_p(captured->code.val, imemo_ifunc));
#endif
return 1;
}
@@ -1267,12 +1443,11 @@ vm_block_handler_type(VALUE block_handler)
}
}
-static inline int
-vm_block_handler_verify(VALUE block_handler)
+static inline void
+vm_block_handler_verify(MAYBE_UNUSED(VALUE block_handler))
{
VM_ASSERT(block_handler == VM_BLOCK_HANDLER_NONE ||
- vm_block_handler_type(block_handler) >= 0);
- return 1;
+ (vm_block_handler_type(block_handler), 1));
}
static inline enum rb_block_type
@@ -1281,12 +1456,10 @@ vm_block_type(const struct rb_block *block)
#if VM_CHECK_MODE > 0
switch (block->type) {
case block_type_iseq:
- VM_ASSERT(RB_TYPE_P(block->as.captured.code.val, T_IMEMO));
- VM_ASSERT(imemo_type(block->as.captured.code.val) == imemo_iseq);
+ VM_ASSERT(imemo_type_p(block->as.captured.code.val, imemo_iseq));
break;
case block_type_ifunc:
- VM_ASSERT(RB_TYPE_P(block->as.captured.code.val, T_IMEMO));
- VM_ASSERT(imemo_type(block->as.captured.code.val) == imemo_ifunc);
+ VM_ASSERT(imemo_type_p(block->as.captured.code.val, imemo_ifunc));
break;
case block_type_symbol:
VM_ASSERT(SYMBOL_P(block->as.symbol));
@@ -1309,9 +1482,8 @@ vm_block_type_set(const struct rb_block *block, enum rb_block_type type)
static inline const struct rb_block *
vm_proc_block(VALUE procval)
{
- rb_proc_t *proc = RTYPEDDATA_DATA(procval);
VM_ASSERT(rb_obj_is_proc(procval));
- return &proc->block;
+ return &((rb_proc_t *)RTYPEDDATA_DATA(procval))->block;
}
static inline const rb_iseq_t *vm_block_iseq(const struct rb_block *block);
@@ -1320,7 +1492,6 @@ static inline const VALUE *vm_block_ep(const struct rb_block *block);
static inline const rb_iseq_t *
vm_proc_iseq(VALUE procval)
{
- VM_ASSERT(rb_obj_is_proc(procval));
return vm_block_iseq(vm_proc_block(procval));
}
@@ -1334,7 +1505,7 @@ static inline const rb_iseq_t *
vm_block_iseq(const struct rb_block *block)
{
switch (vm_block_type(block)) {
- case block_type_iseq: return block->as.captured.code.iseq;
+ case block_type_iseq: return rb_iseq_check(block->as.captured.code.iseq);
case block_type_proc: return vm_proc_iseq(block->as.proc);
case block_type_ifunc:
case block_type_symbol: return NULL;
@@ -1402,16 +1573,17 @@ VM_BH_FROM_PROC(VALUE procval)
/* VM related object allocate functions */
VALUE rb_thread_alloc(VALUE klass);
-VALUE rb_proc_alloc(VALUE klass);
VALUE rb_binding_alloc(VALUE klass);
+VALUE rb_proc_alloc(VALUE klass);
+VALUE rb_proc_dup(VALUE self);
/* for debug */
-extern void rb_vmdebug_stack_dump_raw(rb_thread_t *, rb_control_frame_t *);
-extern void rb_vmdebug_debug_print_pre(rb_thread_t *th, rb_control_frame_t *cfp, VALUE *_pc);
-extern void rb_vmdebug_debug_print_post(rb_thread_t *th, rb_control_frame_t *cfp);
+extern void rb_vmdebug_stack_dump_raw(const rb_execution_context_t *ec, const rb_control_frame_t *cfp);
+extern void rb_vmdebug_debug_print_pre(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const VALUE *_pc);
+extern void rb_vmdebug_debug_print_post(const rb_execution_context_t *ec, const rb_control_frame_t *cfp);
-#define SDR() rb_vmdebug_stack_dump_raw(GET_THREAD(), GET_THREAD()->cfp)
-#define SDR2(cfp) rb_vmdebug_stack_dump_raw(GET_THREAD(), (cfp))
+#define SDR() rb_vmdebug_stack_dump_raw(GET_EC(), GET_EC()->cfp)
+#define SDR2(cfp) rb_vmdebug_stack_dump_raw(GET_EC(), (cfp))
void rb_vm_bugreport(const void *);
NORETURN(void rb_bug_context(const void *, const char *fmt, ...));
@@ -1419,30 +1591,53 @@ NORETURN(void rb_bug_context(const void *, const char *fmt, ...));
RUBY_SYMBOL_EXPORT_BEGIN
VALUE rb_iseq_eval(const rb_iseq_t *iseq);
VALUE rb_iseq_eval_main(const rb_iseq_t *iseq);
+VALUE rb_iseq_path(const rb_iseq_t *iseq);
+VALUE rb_iseq_realpath(const rb_iseq_t *iseq);
RUBY_SYMBOL_EXPORT_END
-int rb_thread_method_id_and_class(rb_thread_t *th, ID *idp, ID *called_idp, VALUE *klassp);
-VALUE rb_vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, int argc, const VALUE *argv, VALUE block_handler);
-VALUE rb_vm_make_proc_lambda(rb_thread_t *th, const struct rb_captured_block *captured, VALUE klass, int8_t is_lambda);
-VALUE rb_vm_make_proc(rb_thread_t *th, const struct rb_captured_block *captured, VALUE klass);
-VALUE rb_vm_make_binding(rb_thread_t *th, const rb_control_frame_t *src_cfp);
+VALUE rb_iseq_pathobj_new(VALUE path, VALUE realpath);
+void rb_iseq_pathobj_set(const rb_iseq_t *iseq, VALUE path, VALUE realpath);
+
+int rb_ec_frame_method_id_and_class(const rb_execution_context_t *ec, ID *idp, ID *called_idp, VALUE *klassp);
+void rb_ec_setup_exception(const rb_execution_context_t *ec, VALUE mesg, VALUE cause);
+
+VALUE rb_vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, int argc, const VALUE *argv, VALUE block_handler);
+
+VALUE rb_vm_make_proc_lambda(const rb_execution_context_t *ec, const struct rb_captured_block *captured, VALUE klass, int8_t is_lambda);
+static inline VALUE
+rb_vm_make_proc(const rb_execution_context_t *ec, const struct rb_captured_block *captured, VALUE klass)
+{
+ return rb_vm_make_proc_lambda(ec, captured, klass, 0);
+}
+
+static inline VALUE
+rb_vm_make_lambda(const rb_execution_context_t *ec, const struct rb_captured_block *captured, VALUE klass)
+{
+ return rb_vm_make_proc_lambda(ec, captured, klass, 1);
+}
+
+VALUE rb_vm_make_binding(const rb_execution_context_t *ec, const rb_control_frame_t *src_cfp);
VALUE rb_vm_env_local_variables(const rb_env_t *env);
const rb_env_t *rb_vm_env_prev_env(const rb_env_t *env);
-const VALUE *rb_binding_add_dynavars(rb_binding_t *bind, int dyncount, const ID *dynvars);
+const VALUE *rb_binding_add_dynavars(VALUE bindval, rb_binding_t *bind, int dyncount, const ID *dynvars);
void rb_vm_inc_const_missing_count(void);
void rb_vm_gvl_destroy(rb_vm_t *vm);
-VALUE rb_vm_call(rb_thread_t *th, VALUE recv, VALUE id, int argc,
+VALUE rb_vm_call(rb_execution_context_t *ec, VALUE recv, VALUE id, int argc,
const VALUE *argv, const rb_callable_method_entry_t *me);
-void rb_vm_pop_frame(rb_thread_t *th);
+MJIT_STATIC void rb_vm_pop_frame(rb_execution_context_t *ec);
void rb_thread_start_timer_thread(void);
void rb_thread_stop_timer_thread(void);
void rb_thread_reset_timer_thread(void);
-void rb_thread_wakeup_timer_thread(void);
+void rb_thread_wakeup_timer_thread(int);
static inline void
rb_vm_living_threads_init(rb_vm_t *vm)
{
+ list_head_init(&vm->waiting_fds);
+ list_head_init(&vm->waiting_pids);
+ list_head_init(&vm->workqueue);
+ list_head_init(&vm->waiting_grps);
list_head_init(&vm->living_threads);
vm->living_thread_num = 0;
}
@@ -1462,24 +1657,26 @@ rb_vm_living_threads_remove(rb_vm_t *vm, rb_thread_t *th)
}
typedef int rb_backtrace_iter_func(void *, VALUE, int, VALUE);
-rb_control_frame_t *rb_vm_get_ruby_level_next_cfp(const rb_thread_t *th, const rb_control_frame_t *cfp);
-rb_control_frame_t *rb_vm_get_binding_creatable_next_cfp(const rb_thread_t *th, const rb_control_frame_t *cfp);
+rb_control_frame_t *rb_vm_get_ruby_level_next_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp);
+rb_control_frame_t *rb_vm_get_binding_creatable_next_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp);
int rb_vm_get_sourceline(const rb_control_frame_t *);
VALUE rb_name_err_mesg_new(VALUE mesg, VALUE recv, VALUE method);
-void rb_vm_stack_to_heap(rb_thread_t *th);
+void rb_vm_stack_to_heap(rb_execution_context_t *ec);
void ruby_thread_init_stack(rb_thread_t *th);
int rb_vm_control_frame_id_and_class(const rb_control_frame_t *cfp, ID *idp, ID *called_idp, VALUE *klassp);
-void rb_vm_rewind_cfp(rb_thread_t *th, rb_control_frame_t *cfp);
+void rb_vm_rewind_cfp(rb_execution_context_t *ec, rb_control_frame_t *cfp);
+MJIT_STATIC VALUE rb_vm_bh_to_procval(const rb_execution_context_t *ec, VALUE block_handler);
-void rb_vm_register_special_exception(enum ruby_special_exceptions sp, VALUE exception_class, const char *mesg);
+void rb_vm_register_special_exception_str(enum ruby_special_exceptions sp, VALUE exception_class, VALUE mesg);
-void rb_gc_mark_machine_stack(rb_thread_t *th);
+#define rb_vm_register_special_exception(sp, e, m) \
+ rb_vm_register_special_exception_str(sp, e, rb_usascii_str_new_static((m), (long)rb_strlen_lit(m)))
-int rb_autoloading_value(VALUE mod, ID id, VALUE* value);
+void rb_gc_mark_machine_stack(const rb_execution_context_t *ec);
void rb_vm_rewrite_cref(rb_cref_t *node, VALUE old_klass, VALUE new_klass, rb_cref_t **new_cref_ptr);
-const rb_callable_method_entry_t *rb_vm_frame_method_entry(const rb_control_frame_t *cfp);
+MJIT_STATIC const rb_callable_method_entry_t *rb_vm_frame_method_entry(const rb_control_frame_t *cfp);
#define sysstack_error GET_VM()->special_exceptions[ruby_error_sysstack]
@@ -1495,42 +1692,81 @@ const rb_callable_method_entry_t *rb_vm_frame_method_entry(const rb_control_fram
#define CHECK_VM_STACK_OVERFLOW(cfp, margin) \
WHEN_VM_STACK_OVERFLOWED(cfp, (cfp)->sp, margin) vm_stackoverflow()
+VALUE rb_catch_protect(VALUE t, rb_block_call_func *func, VALUE data, enum ruby_tag_type *stateptr);
+
/* for thread */
#if RUBY_VM_THREAD_MODEL == 2
-extern rb_thread_t *ruby_current_thread;
-extern rb_vm_t *ruby_current_vm;
-extern rb_event_flag_t ruby_vm_event_flags;
+RUBY_SYMBOL_EXPORT_BEGIN
-#define GET_VM() ruby_current_vm
+RUBY_EXTERN rb_vm_t *ruby_current_vm_ptr;
+RUBY_EXTERN rb_execution_context_t *ruby_current_execution_context_ptr;
+RUBY_EXTERN rb_event_flag_t ruby_vm_event_flags;
+RUBY_EXTERN rb_event_flag_t ruby_vm_event_enabled_global_flags;
+RUBY_EXTERN unsigned int ruby_vm_event_local_num;
-#ifndef OPT_CALL_CFUNC_WITHOUT_FRAME
-#define OPT_CALL_CFUNC_WITHOUT_FRAME 0
-#endif
+RUBY_SYMBOL_EXPORT_END
+
+#define GET_VM() rb_current_vm()
+#define GET_THREAD() rb_current_thread()
+#define GET_EC() rb_current_execution_context()
-#define GET_THREAD() vm_thread_with_frame(ruby_current_thread)
-#if OPT_CALL_CFUNC_WITHOUT_FRAME
static inline rb_thread_t *
-vm_thread_with_frame(rb_thread_t *th)
+rb_ec_thread_ptr(const rb_execution_context_t *ec)
{
- if (UNLIKELY(th->passed_ci != 0)) {
- void rb_vm_call_cfunc_push_frame(rb_thread_t *th);
- rb_vm_call_cfunc_push_frame(th);
+ return ec->thread_ptr;
+}
+
+static inline rb_vm_t *
+rb_ec_vm_ptr(const rb_execution_context_t *ec)
+{
+ const rb_thread_t *th = rb_ec_thread_ptr(ec);
+ if (th) {
+ return th->vm;
+ }
+ else {
+ return NULL;
}
- return th;
}
-#else
-#define vm_thread_with_frame(th) (th)
-#endif
-#define rb_thread_set_current_raw(th) (void)(ruby_current_thread = (th))
-#define rb_thread_set_current(th) do { \
- if ((th)->vm->running_thread != (th)) { \
- (th)->running_time_us = 0; \
- } \
- rb_thread_set_current_raw(th); \
- (th)->vm->running_thread = (th); \
-} while (0)
+static inline rb_execution_context_t *
+rb_current_execution_context(void)
+{
+ return ruby_current_execution_context_ptr;
+}
+
+static inline rb_thread_t *
+rb_current_thread(void)
+{
+ const rb_execution_context_t *ec = GET_EC();
+ return rb_ec_thread_ptr(ec);
+}
+
+static inline rb_vm_t *
+rb_current_vm(void)
+{
+ VM_ASSERT(ruby_current_vm_ptr == NULL ||
+ ruby_current_execution_context_ptr == NULL ||
+ rb_ec_thread_ptr(GET_EC()) == NULL ||
+ rb_ec_vm_ptr(GET_EC()) == ruby_current_vm_ptr);
+ return ruby_current_vm_ptr;
+}
+
+static inline void
+rb_thread_set_current_raw(const rb_thread_t *th)
+{
+ ruby_current_execution_context_ptr = th->ec;
+}
+
+static inline void
+rb_thread_set_current(rb_thread_t *th)
+{
+ if (th->vm->running_thread != th) {
+ th->running_time_us = 0;
+ }
+ rb_thread_set_current_raw(th);
+ th->vm->running_thread = th;
+}
#else
#error "unsupported thread model"
@@ -1543,41 +1779,46 @@ enum {
TRAP_INTERRUPT_MASK = 0x08
};
-#define RUBY_VM_SET_TIMER_INTERRUPT(th) ATOMIC_OR((th)->interrupt_flag, TIMER_INTERRUPT_MASK)
-#define RUBY_VM_SET_INTERRUPT(th) ATOMIC_OR((th)->interrupt_flag, PENDING_INTERRUPT_MASK)
-#define RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(th) ATOMIC_OR((th)->interrupt_flag, POSTPONED_JOB_INTERRUPT_MASK)
-#define RUBY_VM_SET_TRAP_INTERRUPT(th) ATOMIC_OR((th)->interrupt_flag, TRAP_INTERRUPT_MASK)
-#define RUBY_VM_INTERRUPTED(th) ((th)->interrupt_flag & ~(th)->interrupt_mask & (PENDING_INTERRUPT_MASK|TRAP_INTERRUPT_MASK))
-#define RUBY_VM_INTERRUPTED_ANY(th) ((th)->interrupt_flag & ~(th)->interrupt_mask)
+#define RUBY_VM_SET_TIMER_INTERRUPT(ec) ATOMIC_OR((ec)->interrupt_flag, TIMER_INTERRUPT_MASK)
+#define RUBY_VM_SET_INTERRUPT(ec) ATOMIC_OR((ec)->interrupt_flag, PENDING_INTERRUPT_MASK)
+#define RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(ec) ATOMIC_OR((ec)->interrupt_flag, POSTPONED_JOB_INTERRUPT_MASK)
+#define RUBY_VM_SET_TRAP_INTERRUPT(ec) ATOMIC_OR((ec)->interrupt_flag, TRAP_INTERRUPT_MASK)
+#define RUBY_VM_INTERRUPTED(ec) ((ec)->interrupt_flag & ~(ec)->interrupt_mask & \
+ (PENDING_INTERRUPT_MASK|TRAP_INTERRUPT_MASK))
+#define RUBY_VM_INTERRUPTED_ANY(ec) ((ec)->interrupt_flag & ~(ec)->interrupt_mask)
VALUE rb_exc_set_backtrace(VALUE exc, VALUE bt);
int rb_signal_buff_size(void);
-void rb_signal_exec(rb_thread_t *th, int sig);
+int rb_signal_exec(rb_thread_t *th, int sig);
void rb_threadptr_check_signal(rb_thread_t *mth);
void rb_threadptr_signal_raise(rb_thread_t *th, int sig);
void rb_threadptr_signal_exit(rb_thread_t *th);
-void rb_threadptr_execute_interrupts(rb_thread_t *, int);
+int rb_threadptr_execute_interrupts(rb_thread_t *, int);
void rb_threadptr_interrupt(rb_thread_t *th);
void rb_threadptr_unlock_all_locking_mutexes(rb_thread_t *th);
void rb_threadptr_pending_interrupt_clear(rb_thread_t *th);
void rb_threadptr_pending_interrupt_enque(rb_thread_t *th, VALUE v);
-int rb_threadptr_pending_interrupt_active_p(rb_thread_t *th);
-void rb_threadptr_error_print(rb_thread_t *th, VALUE errinfo);
+void rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo);
+void rb_execution_context_mark(const rb_execution_context_t *ec);
+void rb_fiber_close(rb_fiber_t *fib);
+void Init_native_thread(rb_thread_t *th);
-#define RUBY_VM_CHECK_INTS(th) ruby_vm_check_ints(th)
+#define RUBY_VM_CHECK_INTS(ec) rb_vm_check_ints(ec)
static inline void
-ruby_vm_check_ints(rb_thread_t *th)
+rb_vm_check_ints(rb_execution_context_t *ec)
{
- if (UNLIKELY(RUBY_VM_INTERRUPTED_ANY(th))) {
- rb_threadptr_execute_interrupts(th, 0);
+ VM_ASSERT(ec == GET_EC());
+ if (UNLIKELY(RUBY_VM_INTERRUPTED_ANY(ec))) {
+ rb_threadptr_execute_interrupts(rb_ec_thread_ptr(ec), 0);
}
}
/* tracer */
+
struct rb_trace_arg_struct {
rb_event_flag_t event;
- rb_thread_t *th;
- rb_control_frame_t *cfp;
+ rb_execution_context_t *ec;
+ const rb_control_frame_t *cfp;
VALUE self;
ID id;
ID called_id;
@@ -1591,50 +1832,75 @@ struct rb_trace_arg_struct {
VALUE path;
};
-void rb_threadptr_exec_event_hooks(struct rb_trace_arg_struct *trace_arg);
-void rb_threadptr_exec_event_hooks_and_pop_frame(struct rb_trace_arg_struct *trace_arg);
+void rb_hook_list_mark(rb_hook_list_t *hooks);
+void rb_hook_list_free(rb_hook_list_t *hooks);
+void rb_hook_list_connect_tracepoint(VALUE target, rb_hook_list_t *list, VALUE tpval, unsigned int target_line);
+void rb_hook_list_remove_tracepoint(rb_hook_list_t *list, VALUE tpval);
+
+void rb_exec_event_hooks(struct rb_trace_arg_struct *trace_arg, rb_hook_list_t *hooks, int pop_p);
-#define EXEC_EVENT_HOOK_ORIG(th_, flag_, self_, id_, called_id_, klass_, data_, pop_p_) do { \
+#define EXEC_EVENT_HOOK_ORIG(ec_, hooks_, flag_, self_, id_, called_id_, klass_, data_, pop_p_) do { \
const rb_event_flag_t flag_arg_ = (flag_); \
- if (UNLIKELY(ruby_vm_event_flags & (flag_arg_))) { \
- /* defer evaluating the other arguments */ \
- ruby_exec_event_hook_orig(th_, flag_arg_, self_, id_, called_id_, klass_, data_, pop_p_); \
+ rb_hook_list_t *hooks_arg_ = (hooks_); \
+ if (UNLIKELY((hooks_arg_)->events & (flag_arg_))) { \
+ /* defer evaluating the other arguments */ \
+ rb_exec_event_hook_orig(ec_, hooks_arg_, flag_arg_, self_, id_, called_id_, klass_, data_, pop_p_); \
} \
} while (0)
static inline void
-ruby_exec_event_hook_orig(rb_thread_t *const th, const rb_event_flag_t flag,
- VALUE self, ID id, ID called_id, VALUE klass, VALUE data, int pop_p)
-{
- if ((th->event_hooks.events | th->vm->event_hooks.events) & flag) {
- struct rb_trace_arg_struct trace_arg;
- trace_arg.event = flag;
- trace_arg.th = th;
- trace_arg.cfp = th->cfp;
- trace_arg.self = self;
- trace_arg.id = id;
- trace_arg.called_id = called_id;
- trace_arg.klass = klass;
- trace_arg.data = data;
- trace_arg.path = Qundef;
- trace_arg.klass_solved = 0;
- if (pop_p) rb_threadptr_exec_event_hooks_and_pop_frame(&trace_arg);
- else rb_threadptr_exec_event_hooks(&trace_arg);
- }
+rb_exec_event_hook_orig(rb_execution_context_t *ec, rb_hook_list_t *hooks, rb_event_flag_t flag,
+ VALUE self, ID id, ID called_id, VALUE klass, VALUE data, int pop_p)
+{
+ struct rb_trace_arg_struct trace_arg;
+
+ VM_ASSERT((hooks->events & flag) != 0);
+
+ trace_arg.event = flag;
+ trace_arg.ec = ec;
+ trace_arg.cfp = ec->cfp;
+ trace_arg.self = self;
+ trace_arg.id = id;
+ trace_arg.called_id = called_id;
+ trace_arg.klass = klass;
+ trace_arg.data = data;
+ trace_arg.path = Qundef;
+ trace_arg.klass_solved = 0;
+
+ rb_exec_event_hooks(&trace_arg, hooks, pop_p);
+}
+
+static inline rb_hook_list_t *
+rb_vm_global_hooks(const rb_execution_context_t *ec)
+{
+ return &rb_ec_vm_ptr(ec)->global_hooks;
}
-#define EXEC_EVENT_HOOK(th_, flag_, self_, id_, called_id_, klass_, data_) \
- EXEC_EVENT_HOOK_ORIG(th_, flag_, self_, id_, called_id_, klass_, data_, 0)
+#define EXEC_EVENT_HOOK(ec_, flag_, self_, id_, called_id_, klass_, data_) \
+ EXEC_EVENT_HOOK_ORIG(ec_, rb_vm_global_hooks(ec_), flag_, self_, id_, called_id_, klass_, data_, 0)
-#define EXEC_EVENT_HOOK_AND_POP_FRAME(th_, flag_, self_, id_, called_id_, klass_, data_) \
- EXEC_EVENT_HOOK_ORIG(th_, flag_, self_, id_, called_id_, klass_, data_, 1)
+#define EXEC_EVENT_HOOK_AND_POP_FRAME(ec_, flag_, self_, id_, called_id_, klass_, data_) \
+ EXEC_EVENT_HOOK_ORIG(ec_, rb_vm_global_hooks(ec_), flag_, self_, id_, called_id_, klass_, data_, 1)
+
+static inline void
+rb_exec_event_hook_script_compiled(rb_execution_context_t *ec, const rb_iseq_t *iseq, VALUE eval_script)
+{
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_SCRIPT_COMPILED, ec->cfp->self, 0, 0, 0,
+ NIL_P(eval_script) ? (VALUE)iseq :
+ rb_ary_new_from_args(2, eval_script, (VALUE)iseq));
+}
RUBY_SYMBOL_EXPORT_BEGIN
int rb_thread_check_trap_pending(void);
+/* #define RUBY_EVENT_RESERVED_FOR_INTERNAL_USE 0x030000 */ /* from vm_core.h */
+#define RUBY_EVENT_COVERAGE_LINE 0x010000
+#define RUBY_EVENT_COVERAGE_BRANCH 0x020000
+
extern VALUE rb_get_coverages(void);
-extern void rb_set_coverages(VALUE);
+extern void rb_set_coverages(VALUE, int, VALUE);
+extern void rb_clear_coverages(void);
extern void rb_reset_coverages(void);
void rb_postponed_job_flush(rb_vm_t *vm);
diff --git a/vm_dump.c b/vm_dump.c
index 179c132a74..1d36eb0690 100644
--- a/vm_dump.c
+++ b/vm_dump.c
@@ -13,6 +13,9 @@
#include "addr2line.h"
#include "vm_core.h"
#include "iseq.h"
+#ifdef HAVE_UCONTEXT_H
+#include "ucontext.h"
+#endif
/* see vm_insnhelper.h for the values */
#ifndef VMDEBUG
@@ -21,14 +24,15 @@
#define MAX_POSBUF 128
-#define VM_CFP_CNT(th, cfp) \
- ((rb_control_frame_t *)((th)->stack + (th)->stack_size) - (rb_control_frame_t *)(cfp))
+#define VM_CFP_CNT(ec, cfp) \
+ ((rb_control_frame_t *)((ec)->vm_stack + (ec)->vm_stack_size) - \
+ (rb_control_frame_t *)(cfp))
static void
-control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp)
+control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
{
ptrdiff_t pc = -1;
- ptrdiff_t ep = cfp->ep - th->stack;
+ ptrdiff_t ep = cfp->ep - ec->vm_stack;
char ep_in_heap = ' ';
char posbuf[MAX_POSBUF+1];
int line = 0;
@@ -38,7 +42,7 @@ control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp)
const rb_callable_method_entry_t *me;
- if (ep < 0 || (size_t)ep > th->stack_size) {
+ if (ep < 0 || (size_t)ep > ec->vm_stack_size) {
ep = (ptrdiff_t)cfp->ep;
ep_in_heap = 'p';
}
@@ -59,12 +63,6 @@ control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp)
case VM_FRAME_MAGIC_CFUNC:
magic = "CFUNC";
break;
- case VM_FRAME_MAGIC_PROC:
- magic = "PROC";
- break;
- case VM_FRAME_MAGIC_LAMBDA:
- magic = "LAMBDA";
- break;
case VM_FRAME_MAGIC_IFUNC:
magic = "IFUNC";
break;
@@ -91,7 +89,7 @@ control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp)
}
if (cfp->iseq != 0) {
-#define RUBY_VM_IFUNC_P(ptr) (RB_TYPE_P((VALUE)(ptr), T_IMEMO) && imemo_type((VALUE)ptr) == imemo_ifunc)
+#define RUBY_VM_IFUNC_P(ptr) imemo_type_p((VALUE)ptr, imemo_ifunc)
if (RUBY_VM_IFUNC_P(cfp->iseq)) {
iseq_name = "<ifunc>";
}
@@ -106,7 +104,7 @@ control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp)
iseq_name = RSTRING_PTR(cfp->iseq->body->location.label);
line = rb_vm_get_sourceline(cfp);
if (line) {
- snprintf(posbuf, MAX_POSBUF, "%s:%d", RSTRING_PTR(cfp->iseq->body->location.path), line);
+ snprintf(posbuf, MAX_POSBUF, "%s:%d", RSTRING_PTR(rb_iseq_path(cfp->iseq)), line);
}
}
}
@@ -117,14 +115,14 @@ control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp)
}
fprintf(stderr, "c:%04"PRIdPTRDIFF" ",
- ((rb_control_frame_t *)(th->stack + th->stack_size) - cfp));
+ ((rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size) - cfp));
if (pc == -1) {
fprintf(stderr, "p:---- ");
}
else {
fprintf(stderr, "p:%04"PRIdPTRDIFF" ", pc);
}
- fprintf(stderr, "s:%04"PRIdPTRDIFF" ", cfp->sp - th->stack);
+ fprintf(stderr, "s:%04"PRIdPTRDIFF" ", cfp->sp - ec->vm_stack);
fprintf(stderr, ep_in_heap == ' ' ? "e:%06"PRIdPTRDIFF" " : "E:%06"PRIxPTRDIFF" ", ep % 10000);
fprintf(stderr, "%-6s", magic);
if (line) {
@@ -143,19 +141,20 @@ control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp)
}
void
-rb_vmdebug_stack_dump_raw(rb_thread_t *th, rb_control_frame_t *cfp)
+rb_vmdebug_stack_dump_raw(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
{
#if 0
- VALUE *sp = cfp->sp, *ep = cfp->ep;
+ VALUE *sp = cfp->sp;
+ const VALUE *ep = cfp->ep;
VALUE *p, *st, *t;
fprintf(stderr, "-- stack frame ------------\n");
- for (p = st = th->stack; p < sp; p++) {
+ for (p = st = ec->vm_stack; p < sp; p++) {
fprintf(stderr, "%04ld (%p): %08"PRIxVALUE, (long)(p - st), p, *p);
t = (VALUE *)*p;
- if (th->stack <= t && t < sp) {
- fprintf(stderr, " (= %ld)", (long)((VALUE *)GC_GUARDED_PTR_REF(t) - th->stack));
+ if (ec->vm_stack <= t && t < sp) {
+ fprintf(stderr, " (= %ld)", (long)((VALUE *)GC_GUARDED_PTR_REF((VALUE)t) - ec->vm_stack));
}
if (p == ep)
@@ -167,8 +166,8 @@ rb_vmdebug_stack_dump_raw(rb_thread_t *th, rb_control_frame_t *cfp)
fprintf(stderr, "-- Control frame information "
"-----------------------------------------------\n");
- while ((void *)cfp < (void *)(th->stack + th->stack_size)) {
- control_frame_dump(th, cfp);
+ while ((void *)cfp < (void *)(ec->vm_stack + ec->vm_stack_size)) {
+ control_frame_dump(ec, cfp);
cfp++;
}
fprintf(stderr, "\n");
@@ -177,8 +176,8 @@ rb_vmdebug_stack_dump_raw(rb_thread_t *th, rb_control_frame_t *cfp)
void
rb_vmdebug_stack_dump_raw_current(void)
{
- rb_thread_t *th = GET_THREAD();
- rb_vmdebug_stack_dump_raw(th, th->cfp);
+ const rb_execution_context_t *ec = GET_EC();
+ rb_vmdebug_stack_dump_raw(ec, ec->cfp);
}
void
@@ -217,19 +216,18 @@ rb_vmdebug_proc_dump_raw(rb_proc_t *proc)
void
rb_vmdebug_stack_dump_th(VALUE thval)
{
- rb_thread_t *th;
- GetThreadPtr(thval, th);
- rb_vmdebug_stack_dump_raw(th, th->cfp);
+ rb_thread_t *target_th = rb_thread_ptr(thval);
+ rb_vmdebug_stack_dump_raw(target_th->ec, target_th->ec->cfp);
}
#if VMDEBUG > 2
/* copy from vm.c */
-static VALUE *
-vm_base_ptr(rb_control_frame_t *cfp)
+static const VALUE *
+vm_base_ptr(const rb_control_frame_t *cfp)
{
- rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
- VALUE *bp = prev_cfp->sp + iseq->body->local_table_size + VM_ENV_DATA_SIZE;
+ const rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
+ const VALUE *bp = prev_cfp->sp + cfp->iseq->body->local_table_size + VM_ENV_DATA_SIZE;
if (cfp->iseq->body->type == ISEQ_TYPE_METHOD) {
bp += 1;
@@ -238,17 +236,17 @@ vm_base_ptr(rb_control_frame_t *cfp)
}
static void
-vm_stack_dump_each(rb_thread_t *th, rb_control_frame_t *cfp)
+vm_stack_dump_each(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
{
- int i, argc = 0, local_size = 0;
+ int i, argc = 0, local_table_size = 0;
VALUE rstr;
VALUE *sp = cfp->sp;
- VALUE *ep = cfp->ep;
+ const VALUE *ep = cfp->ep;
if (VM_FRAME_RUBYFRAME_P(cfp)) {
- rb_iseq_t *iseq = cfp->iseq;
+ const rb_iseq_t *iseq = cfp->iseq;
argc = iseq->body->param.lead_num;
- local_size = iseq->body->local_size;
+ local_table_size = iseq->body->local_table_size;
}
/* stack trace header */
@@ -257,24 +255,21 @@ vm_stack_dump_each(rb_thread_t *th, rb_control_frame_t *cfp)
VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_TOP ||
VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_BLOCK ||
VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_CLASS ||
- VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_PROC ||
- VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_LAMBDA||
VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_CFUNC ||
VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_IFUNC ||
VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_EVAL ||
VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_RESCUE)
{
+ const VALUE *ptr = ep - local_table_size;
- VALUE *ptr = ep - local_size;
-
- control_frame_dump(th, cfp);
+ control_frame_dump(ec, cfp);
for (i = 0; i < argc; i++) {
rstr = rb_inspect(*ptr);
fprintf(stderr, " arg %2d: %8s (%p)\n", i, StringValueCStr(rstr),
(void *)ptr++);
}
- for (; i < local_size - 1; i++) {
+ for (; i < local_table_size - 1; i++) {
rstr = rb_inspect(*ptr);
fprintf(stderr, " local %2d: %8s (%p)\n", i, StringValueCStr(rstr),
(void *)ptr++);
@@ -282,19 +277,24 @@ vm_stack_dump_each(rb_thread_t *th, rb_control_frame_t *cfp)
ptr = vm_base_ptr(cfp);
for (; ptr < sp; ptr++, i++) {
- if (*ptr == Qundef) {
+ switch (TYPE(*ptr)) {
+ case T_UNDEF:
rstr = rb_str_new2("undef");
- }
- else {
+ break;
+ case T_IMEMO:
+ rstr = rb_str_new2("imemo"); /* TODO: can put mode detail information */
+ break;
+ default:
rstr = rb_inspect(*ptr);
+ break;
}
fprintf(stderr, " stack %2d: %8s (%"PRIdPTRDIFF")\n", i, StringValueCStr(rstr),
- (ptr - th->stack));
+ (ptr - ec->vm_stack));
}
}
else if (VM_FRAME_FINISHED_P(cfp)) {
- if ((th)->stack + (th)->stack_size > (VALUE *)(cfp + 1)) {
- vm_stack_dump_each(th, cfp + 1);
+ if (ec->vm_stack + ec->vm_stack_size > (VALUE *)(cfp + 1)) {
+ vm_stack_dump_each(ec, cfp + 1);
}
else {
/* SDR(); */
@@ -307,36 +307,34 @@ vm_stack_dump_each(rb_thread_t *th, rb_control_frame_t *cfp)
#endif
void
-rb_vmdebug_debug_print_register(rb_thread_t *th)
+rb_vmdebug_debug_print_register(const rb_execution_context_t *ec)
{
- rb_control_frame_t *cfp = th->cfp;
+ rb_control_frame_t *cfp = ec->cfp;
ptrdiff_t pc = -1;
- ptrdiff_t ep = cfp->ep - th->stack;
+ ptrdiff_t ep = cfp->ep - ec->vm_stack;
ptrdiff_t cfpi;
if (VM_FRAME_RUBYFRAME_P(cfp)) {
pc = cfp->pc - cfp->iseq->body->iseq_encoded;
}
- if (ep < 0 || (size_t)ep > th->stack_size) {
+ if (ep < 0 || (size_t)ep > ec->vm_stack_size) {
ep = -1;
}
- cfpi = ((rb_control_frame_t *)(th->stack + th->stack_size)) - cfp;
+ cfpi = ((rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size)) - cfp;
fprintf(stderr, " [PC] %04"PRIdPTRDIFF", [SP] %04"PRIdPTRDIFF", [EP] %04"PRIdPTRDIFF", [CFP] %04"PRIdPTRDIFF"\n",
- pc, (cfp->sp - th->stack), ep, cfpi);
+ pc, (cfp->sp - ec->vm_stack), ep, cfpi);
}
void
rb_vmdebug_thread_dump_regs(VALUE thval)
{
- rb_thread_t *th;
- GetThreadPtr(thval, th);
- rb_vmdebug_debug_print_register(th);
+ rb_vmdebug_debug_print_register(rb_thread_ptr(thval)->ec);
}
void
-rb_vmdebug_debug_print_pre(rb_thread_t *th, rb_control_frame_t *cfp,VALUE *_pc)
+rb_vmdebug_debug_print_pre(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const VALUE *_pc)
{
const rb_iseq_t *iseq = cfp->iseq;
@@ -344,13 +342,13 @@ rb_vmdebug_debug_print_pre(rb_thread_t *th, rb_control_frame_t *cfp,VALUE *_pc)
ptrdiff_t pc = _pc - iseq->body->iseq_encoded;
int i;
- for (i=0; i<(int)VM_CFP_CNT(th, cfp); i++) {
+ for (i=0; i<(int)VM_CFP_CNT(ec, cfp); i++) {
printf(" ");
}
printf("| ");
- if(0)printf("[%03ld] ", (long)(cfp->sp - th->stack));
+ if(0)printf("[%03ld] ", (long)(cfp->sp - ec->vm_stack));
- /* printf("%3"PRIdPTRDIFF" ", VM_CFP_CNT(th, cfp)); */
+ /* printf("%3"PRIdPTRDIFF" ", VM_CFP_CNT(ec, cfp)); */
if (pc >= 0) {
const VALUE *iseq_original = rb_iseq_original_iseq((rb_iseq_t *)iseq);
@@ -360,12 +358,12 @@ rb_vmdebug_debug_print_pre(rb_thread_t *th, rb_control_frame_t *cfp,VALUE *_pc)
#if VMDEBUG > 3
fprintf(stderr, " (1)");
- rb_vmdebug_debug_print_register(th);
+ rb_vmdebug_debug_print_register(ec);
#endif
}
void
-rb_vmdebug_debug_print_post(rb_thread_t *th, rb_control_frame_t *cfp
+rb_vmdebug_debug_print_post(const rb_execution_context_t *ec, const rb_control_frame_t *cfp
#if OPT_STACK_CACHING
, VALUE reg_a, VALUE reg_b
#endif
@@ -377,13 +375,13 @@ rb_vmdebug_debug_print_post(rb_thread_t *th, rb_control_frame_t *cfp
#if VMDEBUG > 3
fprintf(stderr, " (2)");
- rb_vmdebug_debug_print_register(th);
+ rb_vmdebug_debug_print_register(ec);
#endif
- /* stack_dump_raw(th, cfp); */
+ /* stack_dump_raw(ec, cfp); */
#if VMDEBUG > 2
- /* stack_dump_thobj(th); */
- vm_stack_dump_each(th, th->cfp);
+ /* stack_dump_thobj(ec); */
+ vm_stack_dump_each(ec, ec->cfp);
#if OPT_STACK_CACHING
{
@@ -402,10 +400,8 @@ rb_vmdebug_debug_print_post(rb_thread_t *th, rb_control_frame_t *cfp
VALUE
rb_vmdebug_thread_dump_state(VALUE self)
{
- rb_thread_t *th;
- rb_control_frame_t *cfp;
- GetThreadPtr(self, th);
- cfp = th->cfp;
+ rb_thread_t *th = rb_thread_ptr(self);
+ rb_control_frame_t *cfp = th->ec->cfp;
fprintf(stderr, "Thread state dump:\n");
fprintf(stderr, "pc : %p, sp : %p\n", (void *)cfp->pc, (void *)cfp->sp);
@@ -414,6 +410,14 @@ rb_vmdebug_thread_dump_state(VALUE self)
return Qnil;
}
+#if defined __APPLE__
+# if __DARWIN_UNIX03
+# define MCTX_SS_REG(reg) __ss.__##reg
+# else
+# define MCTX_SS_REG(reg) ss.reg
+# endif
+#endif
+
#if defined(HAVE_BACKTRACE)
# ifdef HAVE_LIBUNWIND
# undef backtrace
@@ -421,6 +425,7 @@ rb_vmdebug_thread_dump_state(VALUE self)
# elif defined(__APPLE__) && defined(__x86_64__) && defined(HAVE_LIBUNWIND_H)
# define UNW_LOCAL_ONLY
# include <libunwind.h>
+# include <sys/mman.h>
# undef backtrace
int
backtrace(void **trace, int size)
@@ -447,33 +452,64 @@ darwin_sigtramp:
/* darwin's bundled libunwind doesn't support signal trampoline */
{
ucontext_t *uctx;
- /* get _sigtramp's ucontext_t and set values to cursor
+ char vec[1];
+ int r;
+ /* get previous frame information from %rbx at _sigtramp and set values to cursor
* http://www.opensource.apple.com/source/Libc/Libc-825.25/i386/sys/_sigtramp.s
* http://www.opensource.apple.com/source/libunwind/libunwind-35.1/src/unw_getcontext.s
*/
unw_get_reg(&cursor, UNW_X86_64_RBX, &ip);
uctx = (ucontext_t *)ip;
- unw_set_reg(&cursor, UNW_X86_64_RAX, uctx->uc_mcontext->__ss.__rax);
- unw_set_reg(&cursor, UNW_X86_64_RBX, uctx->uc_mcontext->__ss.__rbx);
- unw_set_reg(&cursor, UNW_X86_64_RCX, uctx->uc_mcontext->__ss.__rcx);
- unw_set_reg(&cursor, UNW_X86_64_RDX, uctx->uc_mcontext->__ss.__rdx);
- unw_set_reg(&cursor, UNW_X86_64_RDI, uctx->uc_mcontext->__ss.__rdi);
- unw_set_reg(&cursor, UNW_X86_64_RSI, uctx->uc_mcontext->__ss.__rsi);
- unw_set_reg(&cursor, UNW_X86_64_RBP, uctx->uc_mcontext->__ss.__rbp);
- unw_set_reg(&cursor, UNW_X86_64_RSP, 8+(uctx->uc_mcontext->__ss.__rsp));
- unw_set_reg(&cursor, UNW_X86_64_R8, uctx->uc_mcontext->__ss.__r8);
- unw_set_reg(&cursor, UNW_X86_64_R9, uctx->uc_mcontext->__ss.__r9);
- unw_set_reg(&cursor, UNW_X86_64_R10, uctx->uc_mcontext->__ss.__r10);
- unw_set_reg(&cursor, UNW_X86_64_R11, uctx->uc_mcontext->__ss.__r11);
- unw_set_reg(&cursor, UNW_X86_64_R12, uctx->uc_mcontext->__ss.__r12);
- unw_set_reg(&cursor, UNW_X86_64_R13, uctx->uc_mcontext->__ss.__r13);
- unw_set_reg(&cursor, UNW_X86_64_R14, uctx->uc_mcontext->__ss.__r14);
- unw_set_reg(&cursor, UNW_X86_64_R15, uctx->uc_mcontext->__ss.__r15);
- ip = uctx->uc_mcontext->__ss.__rip;
- if (((char*)ip)[-2] == 0x0f && ((char*)ip)[-1] == 5) {
- /* signal received in syscall */
+ unw_set_reg(&cursor, UNW_X86_64_RAX, uctx->uc_mcontext->MCTX_SS_REG(rax));
+ unw_set_reg(&cursor, UNW_X86_64_RBX, uctx->uc_mcontext->MCTX_SS_REG(rbx));
+ unw_set_reg(&cursor, UNW_X86_64_RCX, uctx->uc_mcontext->MCTX_SS_REG(rcx));
+ unw_set_reg(&cursor, UNW_X86_64_RDX, uctx->uc_mcontext->MCTX_SS_REG(rdx));
+ unw_set_reg(&cursor, UNW_X86_64_RDI, uctx->uc_mcontext->MCTX_SS_REG(rdi));
+ unw_set_reg(&cursor, UNW_X86_64_RSI, uctx->uc_mcontext->MCTX_SS_REG(rsi));
+ unw_set_reg(&cursor, UNW_X86_64_RBP, uctx->uc_mcontext->MCTX_SS_REG(rbp));
+ unw_set_reg(&cursor, UNW_X86_64_RSP, 8+(uctx->uc_mcontext->MCTX_SS_REG(rsp)));
+ unw_set_reg(&cursor, UNW_X86_64_R8, uctx->uc_mcontext->MCTX_SS_REG(r8));
+ unw_set_reg(&cursor, UNW_X86_64_R9, uctx->uc_mcontext->MCTX_SS_REG(r9));
+ unw_set_reg(&cursor, UNW_X86_64_R10, uctx->uc_mcontext->MCTX_SS_REG(r10));
+ unw_set_reg(&cursor, UNW_X86_64_R11, uctx->uc_mcontext->MCTX_SS_REG(r11));
+ unw_set_reg(&cursor, UNW_X86_64_R12, uctx->uc_mcontext->MCTX_SS_REG(r12));
+ unw_set_reg(&cursor, UNW_X86_64_R13, uctx->uc_mcontext->MCTX_SS_REG(r13));
+ unw_set_reg(&cursor, UNW_X86_64_R14, uctx->uc_mcontext->MCTX_SS_REG(r14));
+ unw_set_reg(&cursor, UNW_X86_64_R15, uctx->uc_mcontext->MCTX_SS_REG(r15));
+ ip = uctx->uc_mcontext->MCTX_SS_REG(rip);
+
+ /* There are 4 cases for SEGV:
+ * (1) called invalid address
+ * (2) read or write invalid address
+ * (3) received signal
+ *
+ * Detail:
+ * (1) called invalid address
+ * In this case, saved ip is invalid address.
+ * It needs to just save the address for the information,
+ * skip the frame, and restore the frame calling the
+ * invalid address from %rsp.
+ * The problem is how to check whether the ip is valid or not.
+ * This code uses mincore(2) and assume the address's page is
+ * incore/referenced or not reflects the problem.
+ * Note that High Sierra's mincore(2) may return -128.
+ * (2) read or write invalid address
+ * saved ip is valid. just restart backtracing.
+ * (3) received signal in user space
+ * Same as (2).
+ * (4) received signal in kernel
+ * In this case saved ip points just after syscall, but registers are
+ * already overwritten by kernel. To fix register consistency,
+ * skip libc's kernel wrapper.
+ * To detect this case, just previous two bytes of ip is "\x0f\x05",
+ * syscall instruction of x86_64.
+ */
+ r = mincore((const void *)ip, 1, vec);
+ if (r || vec[0] <= 0 || memcmp((const char *)ip-2, "\x0f\x05", 2) == 0) {
+ /* if segv is caused by invalid call or signal received in syscall */
+ /* the frame is invalid; skip */
trace[n++] = (void *)ip;
- ip = *(unw_word_t*)uctx->uc_mcontext->__ss.__rsp;
+ ip = *(unw_word_t*)uctx->uc_mcontext->MCTX_SS_REG(rsp);
}
trace[n++] = (void *)ip;
unw_set_reg(&cursor, UNW_REG_IP, ip);
@@ -677,7 +713,7 @@ rb_print_backtrace(void)
#define MAX_NATIVE_TRACE 1024
static void *trace[MAX_NATIVE_TRACE];
int n = (int)backtrace(trace, MAX_NATIVE_TRACE);
-#if defined(USE_ELF) && defined(HAVE_DLADDR) && !defined(__sparc)
+#if (defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H)) && defined(HAVE_DLADDR) && !defined(__sparc)
rb_dump_backtrace_with_lines(n, trace);
#else
char **syms = backtrace_symbols(trace, n);
@@ -803,9 +839,9 @@ print_machine_register(size_t reg, const char *reg_name, int col_count, int max_
char buf[64];
#ifdef __LP64__
- ret = snprintf(buf, sizeof(buf), " %3.3s: 0x%016zx", reg_name, reg);
+ ret = snprintf(buf, sizeof(buf), " %3.3s: 0x%016" PRIxSIZE, reg_name, reg);
#else
- ret = snprintf(buf, sizeof(buf), " %3.3s: 0x%08zx", reg_name, reg);
+ ret = snprintf(buf, sizeof(buf), " %3.3s: 0x%08" PRIxSIZE, reg_name, reg);
#endif
if (col_count + ret > max_col) {
fputs("\n", stderr);
@@ -818,7 +854,7 @@ print_machine_register(size_t reg, const char *reg_name, int col_count, int max_
# ifdef __linux__
# define dump_machine_register(reg) (col_count = print_machine_register(mctx->gregs[REG_##reg], #reg, col_count, 80))
# elif defined __APPLE__
-# define dump_machine_register(reg) (col_count = print_machine_register(mctx->__ss.__##reg, #reg, col_count, 80))
+# define dump_machine_register(reg) (col_count = print_machine_register(mctx->MCTX_SS_REG(reg), #reg, col_count, 80))
# endif
static void
@@ -922,43 +958,6 @@ rb_dump_machine_register(const ucontext_t *ctx)
# define rb_dump_machine_register(ctx) ((void)0)
#endif /* HAVE_PRINT_MACHINE_REGISTERS */
-static void
-preface_dump(void)
-{
-#if defined __APPLE__
- static const char msg[] = ""
- "-- Crash Report log information "
- "--------------------------------------------\n"
- " See Crash Report log file under the one of following:\n"
- " * ~/Library/Logs/CrashReporter\n"
- " * /Library/Logs/CrashReporter\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 char *const endmsg = msg + sizeof(msg) - 1;
- const char *p = msg;
-#define RED "\033[;31;1;7m"
-#define GREEN "\033[;32;7m"
-#define RESET "\033[m"
-
- if (isatty(fileno(stderr))) {
- const char *e = strchr(p, '\n');
- const int w = (int)(e - p);
- do {
- int i = (int)(e - p);
- fputs(*p == ' ' ? GREEN : RED, stderr);
- fwrite(p, 1, e - p, stderr);
- for (; i < w; ++i) fputc(' ', stderr);
- fputs(RESET, stderr);
- fputc('\n', stderr);
- } while ((p = e + 1) < endmsg && (e = strchr(p, '\n')) != 0 && e > p + 1);
- }
- fwrite(p, 1, endmsg - p, stderr);
-#endif
-}
-
void
rb_vm_bugreport(const void *ctx)
{
@@ -972,8 +971,6 @@ rb_vm_bugreport(const void *ctx)
#endif
const rb_vm_t *const vm = GET_VM();
- preface_dump();
-
if (vm) {
SDR();
rb_backtrace_print_as_bugreport();
@@ -1081,3 +1078,25 @@ rb_vm_bugreport(const void *ctx)
#endif /* __FreeBSD__ */
}
}
+
+#ifdef NON_SCALAR_THREAD_ID
+const char *ruby_fill_thread_id_string(rb_nativethread_id_t thid, rb_thread_id_string_t buf);
+#endif
+
+void
+rb_vmdebug_stack_dump_all_threads(void)
+{
+ rb_vm_t *vm = GET_VM();
+ rb_thread_t *th = NULL;
+
+ list_for_each(&vm->living_threads, th, vmlt_node) {
+#ifdef NON_SCALAR_THREAD_ID
+ rb_thread_id_string_t buf;
+ ruby_fill_thread_id_string(th->thread_id, buf);
+ fprintf(stderr, "th: %p, native_id: %s\n", th, buf);
+#else
+ fprintf(stderr, "th: %p, native_id: %p\n", (void *)th, (void *)(uintptr_t)th->thread_id);
+#endif
+ rb_vmdebug_stack_dump_raw(th->ec, th->ec->cfp);
+ }
+}
diff --git a/vm_eval.c b/vm_eval.c
index ea398e04d2..e759197182 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -16,20 +16,18 @@ struct local_var_list {
};
static inline VALUE method_missing(VALUE obj, ID id, int argc, const VALUE *argv, enum method_missing_reason call_status);
-static inline VALUE vm_yield_with_cref(rb_thread_t *th, int argc, const VALUE *argv, const rb_cref_t *cref);
-static inline VALUE vm_yield(rb_thread_t *th, int argc, const VALUE *argv);
-static inline VALUE vm_yield_with_block(rb_thread_t *th, int argc, const VALUE *argv, VALUE block_handler);
-static VALUE vm_exec(rb_thread_t *th);
-static void vm_set_eval_stack(rb_thread_t * th, const rb_iseq_t *iseq, const rb_cref_t *cref, const struct rb_block *base_block);
-static int vm_collect_local_variables_in_heap(rb_thread_t *th, const VALUE *dfp, const struct local_var_list *vars);
+static inline VALUE vm_yield_with_cref(rb_execution_context_t *ec, int argc, const VALUE *argv, const rb_cref_t *cref, int is_lambda);
+static inline VALUE vm_yield(rb_execution_context_t *ec, int argc, const VALUE *argv);
+static inline VALUE vm_yield_with_block(rb_execution_context_t *ec, int argc, const VALUE *argv, VALUE block_handler);
+static inline VALUE vm_yield_force_blockarg(rb_execution_context_t *ec, VALUE args);
+VALUE vm_exec(rb_execution_context_t *ec, int mjit_enable_p);
+static void vm_set_eval_stack(rb_execution_context_t * th, const rb_iseq_t *iseq, const rb_cref_t *cref, const struct rb_block *base_block);
+static int vm_collect_local_variables_in_heap(const VALUE *dfp, const struct local_var_list *vars);
static VALUE rb_eUncaughtThrow;
static ID id_result, id_tag, id_value;
#define id_mesg idMesg
-/* vm_backtrace.c */
-VALUE rb_vm_backtrace_str_ary(rb_thread_t *th, int lev, int n);
-
typedef enum call_type {
CALL_PUBLIC,
CALL_FCALL,
@@ -38,11 +36,12 @@ typedef enum call_type {
} call_type;
static VALUE send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope);
+static VALUE vm_call0_body(rb_execution_context_t* ec, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, const VALUE *argv);
-static VALUE vm_call0_body(rb_thread_t* th, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, const VALUE *argv);
+#ifndef MJIT_HEADER
-static VALUE
-vm_call0(rb_thread_t* th, VALUE recv, ID id, int argc, const VALUE *argv, const rb_callable_method_entry_t *me)
+MJIT_FUNC_EXPORTED VALUE
+rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE *argv, const rb_callable_method_entry_t *me)
{
struct rb_calling_info calling_entry, *calling;
struct rb_call_info ci_entry;
@@ -58,54 +57,11 @@ vm_call0(rb_thread_t* th, VALUE recv, ID id, int argc, const VALUE *argv, const
calling->recv = recv;
calling->argc = argc;
- return vm_call0_body(th, calling, &ci_entry, &cc_entry, argv);
+ return vm_call0_body(ec, calling, &ci_entry, &cc_entry, argv);
}
-#if OPT_CALL_CFUNC_WITHOUT_FRAME
static VALUE
-vm_call0_cfunc(rb_thread_t* th, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, const VALUE *argv)
-{
- VALUE val;
-
- RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, cc->me->owner, ci->mid);
- EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, calling->recv, ci->mid, ci->mid, cc->me->owner, Qnil);
- {
- rb_control_frame_t *reg_cfp = th->cfp;
- const rb_callable_method_entry_t *me = cc->me;
- const rb_method_cfunc_t *cfunc = &me->def->body.cfunc;
- int len = cfunc->argc;
- VALUE recv = calling->recv;
- int argc = calling->argc;
-
- if (len >= 0) rb_check_arity(argc, len, len);
-
- th->passed_ci = ci;
- cc->aux.inc_sp = 0;
- VM_PROFILE_UP(C2C_CALL);
- val = (*cfunc->invoker)(cfunc->func, recv, argc, argv);
-
- if (reg_cfp == th->cfp) {
- if (UNLIKELY(th->passed_ci != ci)) {
- rb_bug("vm_call0_cfunc: passed_ci error (ci: %p, passed_ci: %p)", ci, th->passed_ci);
- }
- th->passed_ci = 0;
- }
- else {
- if (reg_cfp != th->cfp + 1) {
- rb_bug("vm_call0_cfunc: cfp consistency error");
- }
- VM_PROFILE_UP(C2C_POPF);
- rb_vm_pop_frame(th);
- }
- }
- EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, calling->recv, ci->mid, ci->mid, callnig->cc->me->owner, val);
- RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, cc->me->owner, ci->mid);
-
- return val;
-}
-#else
-static VALUE
-vm_call0_cfunc_with_frame(rb_thread_t* th, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, const VALUE *argv)
+vm_call0_cfunc_with_frame(rb_execution_context_t* ec, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, const VALUE *argv)
{
VALUE val;
const rb_callable_method_entry_t *me = cc->me;
@@ -116,52 +72,47 @@ vm_call0_cfunc_with_frame(rb_thread_t* th, struct rb_calling_info *calling, cons
ID mid = ci->mid;
VALUE block_handler = calling->block_handler;
- RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, me->owner, me->def->original_id);
- EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->def->original_id, mid, me->owner, Qnil);
+ RUBY_DTRACE_CMETHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id);
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, recv, me->def->original_id, mid, me->owner, Qnil);
{
- rb_control_frame_t *reg_cfp = th->cfp;
+ rb_control_frame_t *reg_cfp = ec->cfp;
- vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL, recv,
+ vm_push_frame(ec, 0, VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL, recv,
block_handler, (VALUE)me,
0, reg_cfp->sp, 0, 0);
if (len >= 0) rb_check_arity(argc, len, len);
- VM_PROFILE_UP(C2C_CALL);
val = (*cfunc->invoker)(cfunc->func, recv, argc, argv);
- if (UNLIKELY(reg_cfp != th->cfp + 1)) {
- rb_bug("vm_call0_cfunc_with_frame: cfp consistency error");
- }
- VM_PROFILE_UP(C2C_POPF);
- rb_vm_pop_frame(th);
+ CHECK_CFP_CONSISTENCY("vm_call0_cfunc_with_frame");
+ rb_vm_pop_frame(ec);
}
- EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->def->original_id, mid, me->owner, val);
- RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, me->owner, me->def->original_id);
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, recv, me->def->original_id, mid, me->owner, val);
+ RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, me->owner, me->def->original_id);
return val;
}
static VALUE
-vm_call0_cfunc(rb_thread_t* th, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, const VALUE *argv)
+vm_call0_cfunc(rb_execution_context_t *ec, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, const VALUE *argv)
{
- return vm_call0_cfunc_with_frame(th, calling, ci, cc, argv);
+ return vm_call0_cfunc_with_frame(ec, calling, ci, cc, argv);
}
-#endif
/* `ci' should point temporal value (on stack value) */
static VALUE
-vm_call0_body(rb_thread_t* th, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, const VALUE *argv)
+vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, const VALUE *argv)
{
VALUE ret;
- calling->block_handler = vm_passed_block_handler(th);
+ calling->block_handler = vm_passed_block_handler(ec);
again:
switch (cc->me->def->type) {
case VM_METHOD_TYPE_ISEQ:
{
- rb_control_frame_t *reg_cfp = th->cfp;
+ rb_control_frame_t *reg_cfp = ec->cfp;
int i;
CHECK_VM_STACK_OVERFLOW(reg_cfp, calling->argc + 1);
@@ -171,13 +122,13 @@ vm_call0_body(rb_thread_t* th, struct rb_calling_info *calling, const struct rb_
*reg_cfp->sp++ = argv[i];
}
- vm_call_iseq_setup(th, reg_cfp, calling, ci, cc);
- VM_ENV_FLAGS_SET(th->cfp->ep, VM_FRAME_FLAG_FINISH);
- return vm_exec(th); /* CHECK_INTS in this function */
+ vm_call_iseq_setup(ec, reg_cfp, calling, ci, cc);
+ VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);
+ return vm_exec(ec, TRUE); /* CHECK_INTS in this function */
}
case VM_METHOD_TYPE_NOTIMPLEMENTED:
case VM_METHOD_TYPE_CFUNC:
- ret = vm_call0_cfunc(th, calling, ci, cc, argv);
+ ret = vm_call0_cfunc(ec, calling, ci, cc, argv);
goto success;
case VM_METHOD_TYPE_ATTRSET:
rb_check_arity(calling->argc, 1, 1);
@@ -188,7 +139,7 @@ vm_call0_body(rb_thread_t* th, struct rb_calling_info *calling, const struct rb_
ret = rb_attr_get(calling->recv, cc->me->def->body.attr.id);
goto success;
case VM_METHOD_TYPE_BMETHOD:
- ret = vm_call_bmethod_body(th, calling, ci, cc, argv);
+ ret = vm_call_bmethod_body(ec, calling, ci, cc, argv);
goto success;
case VM_METHOD_TYPE_ZSUPER:
case VM_METHOD_TYPE_REFINED:
@@ -211,7 +162,7 @@ vm_call0_body(rb_thread_t* th, struct rb_calling_info *calling, const struct rb_
ret = method_missing(calling->recv, ci->mid, calling->argc, argv, ex);
goto success;
}
- RUBY_VM_CHECK_INTS(th);
+ RUBY_VM_CHECK_INTS(ec);
goto again;
}
case VM_METHOD_TYPE_ALIAS:
@@ -219,7 +170,7 @@ vm_call0_body(rb_thread_t* th, struct rb_calling_info *calling, const struct rb_
goto again;
case VM_METHOD_TYPE_MISSING:
{
- vm_passed_block_handler_set(th, calling->block_handler);
+ vm_passed_block_handler_set(ec, calling->block_handler);
return method_missing(calling->recv, ci->mid, calling->argc,
argv, MISSING_NOENTRY);
}
@@ -232,7 +183,7 @@ vm_call0_body(rb_thread_t* th, struct rb_calling_info *calling, const struct rb_
{
rb_proc_t *proc;
GetProcPtr(calling->recv, proc);
- ret = rb_vm_invoke_proc(th, proc, calling->argc, argv, calling->block_handler);
+ ret = rb_vm_invoke_proc(ec, proc, calling->argc, argv, calling->block_handler);
goto success;
}
default:
@@ -246,23 +197,23 @@ vm_call0_body(rb_thread_t* th, struct rb_calling_info *calling, const struct rb_
return Qundef;
success:
- RUBY_VM_CHECK_INTS(th);
+ RUBY_VM_CHECK_INTS(ec);
return ret;
}
VALUE
-rb_vm_call(rb_thread_t *th, VALUE recv, VALUE id, int argc, const VALUE *argv, const rb_callable_method_entry_t *me)
+rb_vm_call(rb_execution_context_t *ec, VALUE recv, VALUE id, int argc, const VALUE *argv, const rb_callable_method_entry_t *me)
{
- return vm_call0(th, recv, id, argc, argv, me);
+ return rb_vm_call0(ec, recv, id, argc, argv, me);
}
static inline VALUE
-vm_call_super(rb_thread_t *th, int argc, const VALUE *argv)
+vm_call_super(rb_execution_context_t *ec, int argc, const VALUE *argv)
{
- VALUE recv = th->cfp->self;
+ VALUE recv = ec->cfp->self;
VALUE klass;
ID id;
- rb_control_frame_t *cfp = th->cfp;
+ rb_control_frame_t *cfp = ec->cfp;
const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
if (VM_FRAME_RUBYFRAME_P(cfp)) {
@@ -278,45 +229,52 @@ vm_call_super(rb_thread_t *th, int argc, const VALUE *argv)
return method_missing(recv, id, argc, argv, MISSING_SUPER);
}
else {
- return vm_call0(th, recv, id, argc, argv, me);
+ return rb_vm_call0(ec, recv, id, argc, argv, me);
}
}
VALUE
rb_call_super(int argc, const VALUE *argv)
{
- rb_thread_t *th = GET_THREAD();
- PASS_PASSED_BLOCK_HANDLER_TH(th);
- return vm_call_super(th, argc, argv);
+ rb_execution_context_t *ec = GET_EC();
+ PASS_PASSED_BLOCK_HANDLER_EC(ec);
+ return vm_call_super(ec, argc, argv);
}
VALUE
rb_current_receiver(void)
{
- rb_thread_t *th = GET_THREAD();
+ const rb_execution_context_t *ec = GET_EC();
rb_control_frame_t *cfp;
- if (!th || !(cfp = th->cfp))
+ if (!ec || !(cfp = ec->cfp)) {
rb_raise(rb_eRuntimeError, "no self, no life");
+ }
return cfp->self;
}
+#endif /* #ifndef MJIT_HEADER */
+
static inline void
-stack_check(rb_thread_t *th)
+stack_check(rb_execution_context_t *ec)
{
- if (!rb_thread_raised_p(th, RAISED_STACKOVERFLOW) && ruby_stack_check()) {
- rb_thread_raised_set(th, RAISED_STACKOVERFLOW);
- rb_exc_raise(sysstack_error);
+ if (!rb_ec_raised_p(ec, RAISED_STACKOVERFLOW) &&
+ rb_ec_stack_check(ec)) {
+ rb_ec_raised_set(ec, RAISED_STACKOVERFLOW);
+ rb_ec_stack_overflow(ec, FALSE);
}
}
+#ifndef MJIT_HEADER
+
static inline const rb_callable_method_entry_t *rb_search_method_entry(VALUE recv, ID mid);
-static inline enum method_missing_reason rb_method_call_status(rb_thread_t *th, const rb_callable_method_entry_t *me, call_type scope, VALUE self);
+static inline enum method_missing_reason rb_method_call_status(rb_execution_context_t *ec, const rb_callable_method_entry_t *me, call_type scope, VALUE self);
/*!
* \internal
* calls the specified method.
*
* This function is called by functions in rb_call* family.
+ * \param ec current execution context
* \param recv receiver of the method
* \param mid an ID that represents the name of the method
* \param argc the number of method arguments
@@ -328,25 +286,33 @@ static inline enum method_missing_reason rb_method_call_status(rb_thread_t *th,
* \note \a self is used in order to controlling access to protected methods.
*/
static inline VALUE
-rb_call0(VALUE recv, ID mid, int argc, const VALUE *argv,
+rb_call0(rb_execution_context_t *ec,
+ VALUE recv, ID mid, int argc, const VALUE *argv,
call_type scope, VALUE self)
{
- const rb_callable_method_entry_t *me = rb_search_method_entry(recv, mid);
- rb_thread_t *th = GET_THREAD();
- enum method_missing_reason call_status = rb_method_call_status(th, me, scope, self);
+ const rb_callable_method_entry_t *me;
+ enum method_missing_reason call_status;
+
+ if (scope == CALL_PUBLIC) {
+ me = rb_callable_method_entry_with_refinements(CLASS_OF(recv), mid, NULL);
+ }
+ else {
+ me = rb_search_method_entry(recv, mid);
+ }
+ call_status = rb_method_call_status(ec, me, scope, self);
if (call_status != MISSING_NONE) {
return method_missing(recv, mid, argc, argv, call_status);
}
- stack_check(th);
- return vm_call0(th, recv, mid, argc, argv, me);
+ stack_check(ec);
+ return rb_vm_call0(ec, recv, mid, argc, argv, me);
}
struct rescue_funcall_args {
- rb_thread_t *th;
VALUE defined_class;
VALUE recv;
ID mid;
+ rb_execution_context_t *ec;
const rb_method_entry_t *me;
unsigned int respond: 1;
unsigned int respond_to_missing: 1;
@@ -357,13 +323,11 @@ struct rescue_funcall_args {
static VALUE
check_funcall_exec(struct rescue_funcall_args *args)
{
- return call_method_entry(args->th, args->defined_class,
+ return call_method_entry(args->ec, args->defined_class,
args->recv, idMethodMissing,
args->me, args->argc, args->argv);
}
-#define PRIV Qfalse /* TODO: for rubyspec now, should be Qtrue */
-
static VALUE
check_funcall_failed(struct rescue_funcall_args *args, VALUE e)
{
@@ -389,26 +353,26 @@ check_funcall_failed(struct rescue_funcall_args *args, VALUE e)
}
static int
-check_funcall_respond_to(rb_thread_t *th, VALUE klass, VALUE recv, ID mid)
+check_funcall_respond_to(rb_execution_context_t *ec, VALUE klass, VALUE recv, ID mid)
{
- return vm_respond_to(th, klass, recv, mid, TRUE);
+ return vm_respond_to(ec, klass, recv, mid, TRUE);
}
static int
-check_funcall_callable(rb_thread_t *th, const rb_callable_method_entry_t *me)
+check_funcall_callable(rb_execution_context_t *ec, const rb_callable_method_entry_t *me)
{
- return rb_method_call_status(th, me, CALL_FCALL, th->cfp->self) == MISSING_NONE;
+ return rb_method_call_status(ec, me, CALL_FCALL, ec->cfp->self) == MISSING_NONE;
}
static VALUE
-check_funcall_missing(rb_thread_t *th, VALUE klass, VALUE recv, ID mid, int argc, const VALUE *argv, int respond, VALUE def)
+check_funcall_missing(rb_execution_context_t *ec, VALUE klass, VALUE recv, ID mid, int argc, const VALUE *argv, int respond, VALUE def)
{
struct rescue_funcall_args args;
const rb_method_entry_t *me;
VALUE ret = Qundef;
- ret = basic_obj_respond_to_missing(th, klass, recv,
- ID2SYM(mid), PRIV);
+ ret = basic_obj_respond_to_missing(ec, klass, recv,
+ ID2SYM(mid), Qtrue);
if (!RTEST(ret)) return def;
args.respond = respond > 0;
args.respond_to_missing = (ret != Qundef);
@@ -419,8 +383,8 @@ check_funcall_missing(rb_thread_t *th, VALUE klass, VALUE recv, ID mid, int argc
new_args[0] = ID2SYM(mid);
MEMCPY(new_args+1, argv, VALUE, argc);
- th->method_missing_reason = MISSING_NOENTRY;
- args.th = th;
+ ec->method_missing_reason = MISSING_NOENTRY;
+ args.ec = ec;
args.recv = recv;
args.me = me;
args.mid = mid;
@@ -445,19 +409,21 @@ rb_check_funcall_default(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE
{
VALUE klass = CLASS_OF(recv);
const rb_callable_method_entry_t *me;
- rb_thread_t *th = GET_THREAD();
- int respond = check_funcall_respond_to(th, klass, recv, mid);
+ rb_execution_context_t *ec = GET_EC();
+ int respond = check_funcall_respond_to(ec, klass, recv, mid);
if (!respond)
return def;
me = rb_search_method_entry(recv, mid);
- if (!check_funcall_callable(th, me)) {
- return check_funcall_missing(th, klass, recv, mid, argc, argv,
- respond, def);
+ if (!check_funcall_callable(ec, me)) {
+ VALUE ret = check_funcall_missing(ec, klass, recv, mid, argc, argv,
+ respond, def);
+ if (ret == Qundef) ret = def;
+ return ret;
}
- stack_check(th);
- return vm_call0(th, recv, mid, argc, argv, me);
+ stack_check(ec);
+ return rb_vm_call0(ec, recv, mid, argc, argv, me);
}
VALUE
@@ -466,8 +432,8 @@ rb_check_funcall_with_hook(VALUE recv, ID mid, int argc, const VALUE *argv,
{
VALUE klass = CLASS_OF(recv);
const rb_callable_method_entry_t *me;
- rb_thread_t *th = GET_THREAD();
- int respond = check_funcall_respond_to(th, klass, recv, mid);
+ rb_execution_context_t *ec = GET_EC();
+ int respond = check_funcall_respond_to(ec, klass, recv, mid);
if (!respond) {
(*hook)(FALSE, recv, mid, argc, argv, arg);
@@ -475,51 +441,91 @@ rb_check_funcall_with_hook(VALUE recv, ID mid, int argc, const VALUE *argv,
}
me = rb_search_method_entry(recv, mid);
- if (!check_funcall_callable(th, me)) {
- VALUE ret = check_funcall_missing(th, klass, recv, mid, argc, argv,
+ if (!check_funcall_callable(ec, me)) {
+ VALUE ret = check_funcall_missing(ec, klass, recv, mid, argc, argv,
respond, Qundef);
(*hook)(ret != Qundef, recv, mid, argc, argv, arg);
return ret;
}
- stack_check(th);
+ stack_check(ec);
(*hook)(TRUE, recv, mid, argc, argv, arg);
- return vm_call0(th, recv, mid, argc, argv, me);
+ return rb_vm_call0(ec, recv, mid, argc, argv, me);
}
-static const char *
+const char *
rb_type_str(enum ruby_value_type type)
{
-#define type_case(t) case t: return #t;
+#define type_case(t) t: return #t
switch (type) {
- type_case(T_NONE)
- type_case(T_OBJECT)
- type_case(T_CLASS)
- type_case(T_MODULE)
- type_case(T_FLOAT)
- type_case(T_STRING)
- type_case(T_REGEXP)
- type_case(T_ARRAY)
- type_case(T_HASH)
- type_case(T_STRUCT)
- type_case(T_BIGNUM)
- type_case(T_FILE)
- type_case(T_DATA)
- type_case(T_MATCH)
- type_case(T_COMPLEX)
- type_case(T_RATIONAL)
- type_case(T_NIL)
- type_case(T_TRUE)
- type_case(T_FALSE)
- type_case(T_SYMBOL)
- type_case(T_FIXNUM)
- type_case(T_IMEMO)
- type_case(T_UNDEF)
- type_case(T_NODE)
- type_case(T_ICLASS)
- type_case(T_ZOMBIE)
- default: return NULL;
+ case type_case(T_NONE);
+ case type_case(T_OBJECT);
+ case type_case(T_CLASS);
+ case type_case(T_MODULE);
+ case type_case(T_FLOAT);
+ case type_case(T_STRING);
+ case type_case(T_REGEXP);
+ case type_case(T_ARRAY);
+ case type_case(T_HASH);
+ case type_case(T_STRUCT);
+ case type_case(T_BIGNUM);
+ case type_case(T_FILE);
+ case type_case(T_DATA);
+ case type_case(T_MATCH);
+ case type_case(T_COMPLEX);
+ case type_case(T_RATIONAL);
+ case type_case(T_NIL);
+ case type_case(T_TRUE);
+ case type_case(T_FALSE);
+ case type_case(T_SYMBOL);
+ case type_case(T_FIXNUM);
+ case type_case(T_IMEMO);
+ case type_case(T_UNDEF);
+ case type_case(T_NODE);
+ case type_case(T_ICLASS);
+ case type_case(T_ZOMBIE);
+ case T_MASK: break;
}
#undef type_case
+ return NULL;
+}
+
+NORETURN(static void uncallable_object(VALUE recv, ID mid));
+static void
+uncallable_object(VALUE recv, ID mid)
+{
+ VALUE flags;
+ int type;
+ const char *typestr;
+ VALUE mname = rb_id2str(mid);
+
+ if (SPECIAL_CONST_P(recv)) {
+ rb_raise(rb_eNotImpError,
+ "method `%"PRIsVALUE"' called on unexpected immediate object (%p)",
+ mname, (void *)recv);
+ }
+ else if ((flags = RBASIC(recv)->flags) == 0) {
+ rb_raise(rb_eNotImpError,
+ "method `%"PRIsVALUE"' called on terminated object (%p)",
+ mname, (void *)recv);
+ }
+ else if (!(typestr = rb_type_str(type = BUILTIN_TYPE(recv)))) {
+ rb_raise(rb_eNotImpError,
+ "method `%"PRIsVALUE"' called on broken T_?""?""?(0x%02x) object"
+ " (%p flags=0x%"PRIxVALUE")",
+ mname, type, (void *)recv, flags);
+ }
+ else if (T_OBJECT <= type && type < T_NIL) {
+ rb_raise(rb_eNotImpError,
+ "method `%"PRIsVALUE"' called on hidden %s object"
+ " (%p flags=0x%"PRIxVALUE")",
+ mname, typestr, (void *)recv, flags);
+ }
+ else {
+ rb_raise(rb_eNotImpError,
+ "method `%"PRIsVALUE"' called on unexpected %s object"
+ " (%p flags=0x%"PRIxVALUE")",
+ mname, typestr, (void *)recv, flags);
+ }
}
static inline const rb_callable_method_entry_t *
@@ -527,45 +533,12 @@ rb_search_method_entry(VALUE recv, ID mid)
{
VALUE klass = CLASS_OF(recv);
- if (!klass) {
- VALUE flags;
- if (SPECIAL_CONST_P(recv)) {
- rb_raise(rb_eNotImpError,
- "method `%"PRIsVALUE"' called on unexpected immediate object (%p)",
- rb_id2str(mid), (void *)recv);
- }
- flags = RBASIC(recv)->flags;
- if (flags == 0) {
- rb_raise(rb_eNotImpError,
- "method `%"PRIsVALUE"' called on terminated object"
- " (%p flags=0x%"PRIxVALUE")",
- rb_id2str(mid), (void *)recv, flags);
- }
- else {
- int type = BUILTIN_TYPE(recv);
- const char *typestr = rb_type_str(type);
- if (typestr && T_OBJECT <= type && type < T_NIL)
- rb_raise(rb_eNotImpError,
- "method `%"PRIsVALUE"' called on hidden %s object"
- " (%p flags=0x%"PRIxVALUE")",
- rb_id2str(mid), typestr, (void *)recv, flags);
- if (typestr)
- rb_raise(rb_eNotImpError,
- "method `%"PRIsVALUE"' called on unexpected %s object"
- " (%p flags=0x%"PRIxVALUE")",
- rb_id2str(mid), typestr, (void *)recv, flags);
- else
- rb_raise(rb_eNotImpError,
- "method `%"PRIsVALUE"' called on broken T_???" "(0x%02x) object"
- " (%p flags=0x%"PRIxVALUE")",
- rb_id2str(mid), type, (void *)recv, flags);
- }
- }
+ if (!klass) uncallable_object(recv, mid);
return rb_callable_method_entry(klass, mid);
}
static inline enum method_missing_reason
-rb_method_call_status(rb_thread_t *th, const rb_callable_method_entry_t *me, call_type scope, VALUE self)
+rb_method_call_status(rb_execution_context_t *ec, const rb_callable_method_entry_t *me, call_type scope, VALUE self)
{
VALUE klass;
ID oid;
@@ -624,11 +597,11 @@ rb_method_call_status(rb_thread_t *th, const rb_callable_method_entry_t *me, cal
static inline VALUE
rb_call(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope)
{
- rb_thread_t *th = GET_THREAD();
- return rb_call0(recv, mid, argc, argv, scope, th->cfp->self);
+ rb_execution_context_t *ec = GET_EC();
+ return rb_call0(ec, recv, mid, argc, argv, scope, ec->cfp->self);
}
-NORETURN(static void raise_method_missing(rb_thread_t *th, int argc, const VALUE *argv,
+NORETURN(static void raise_method_missing(rb_execution_context_t *ec, int argc, const VALUE *argv,
VALUE obj, enum method_missing_reason call_status));
/*
@@ -667,39 +640,33 @@ NORETURN(static void raise_method_missing(rb_thread_t *th, int argc, const VALUE
static VALUE
rb_method_missing(int argc, const VALUE *argv, VALUE obj)
{
- rb_thread_t *th = GET_THREAD();
- raise_method_missing(th, argc, argv, obj, th->method_missing_reason);
- UNREACHABLE;
+ rb_execution_context_t *ec = GET_EC();
+ raise_method_missing(ec, argc, argv, obj, ec->method_missing_reason);
+ UNREACHABLE_RETURN(Qnil);
}
-static VALUE
-make_no_method_exception(VALUE exc, VALUE format, VALUE obj,
- int argc, const VALUE *argv, int priv)
-{
- int n = 0;
- enum {
- arg_mesg,
- arg_name,
- arg_args,
- arg_priv,
- args_size
- };
- VALUE args[args_size];
+MJIT_FUNC_EXPORTED VALUE
+rb_make_no_method_exception(VALUE exc, VALUE format, VALUE obj,
+ int argc, const VALUE *argv, int priv)
+{
+ VALUE name = argv[0];
if (!format) {
- format = rb_fstring_cstr("undefined method `%s' for %s%s%s");
+ format = rb_fstring_lit("undefined method `%s' for %s%s%s");
}
- args[n++] = rb_name_err_mesg_new(format, obj, argv[0]);
- args[n++] = argv[0];
if (exc == rb_eNoMethodError) {
- args[n++] = rb_ary_new4(argc - 1, argv + 1);
- args[n++] = priv ? Qtrue : Qfalse;
+ VALUE args = rb_ary_new4(argc - 1, argv + 1);
+ return rb_nomethod_err_new(format, obj, name, args, priv);
+ }
+ else {
+ return rb_name_err_new(format, obj, name);
}
- return rb_class_new_instance(n, args, exc);
}
+#endif /* #ifndef MJIT_HEADER */
+
static void
-raise_method_missing(rb_thread_t *th, int argc, const VALUE *argv, VALUE obj,
+raise_method_missing(rb_execution_context_t *ec, int argc, const VALUE *argv, VALUE obj,
enum method_missing_reason last_call_status)
{
VALUE exc = rb_eNoMethodError;
@@ -714,25 +681,25 @@ raise_method_missing(rb_thread_t *th, int argc, const VALUE *argv, VALUE obj,
rb_obj_class(argv[0]));
}
- stack_check(th);
+ stack_check(ec);
if (last_call_status & MISSING_PRIVATE) {
- format = rb_fstring_cstr("private method `%s' called for %s%s%s");
+ format = rb_fstring_lit("private method `%s' called for %s%s%s");
}
else if (last_call_status & MISSING_PROTECTED) {
- format = rb_fstring_cstr("protected method `%s' called for %s%s%s");
+ format = rb_fstring_lit("protected method `%s' called for %s%s%s");
}
else if (last_call_status & MISSING_VCALL) {
- format = rb_fstring_cstr("undefined local variable or method `%s' for %s%s%s");
+ format = rb_fstring_lit("undefined local variable or method `%s' for %s%s%s");
exc = rb_eNameError;
}
else if (last_call_status & MISSING_SUPER) {
- format = rb_fstring_cstr("super: no superclass method `%s' for %s%s%s");
+ format = rb_fstring_lit("super: no superclass method `%s' for %s%s%s");
}
{
- exc = make_no_method_exception(exc, format, obj, argc, argv,
- last_call_status & (MISSING_FCALL|MISSING_VCALL));
+ exc = rb_make_no_method_exception(exc, format, obj, argc, argv,
+ last_call_status & (MISSING_FCALL|MISSING_VCALL));
if (!(last_call_status & MISSING_MISSING)) {
rb_vm_pop_cfunc_frame();
}
@@ -740,19 +707,27 @@ raise_method_missing(rb_thread_t *th, int argc, const VALUE *argv, VALUE obj,
}
}
+static void
+vm_raise_method_missing(rb_execution_context_t *ec, int argc, const VALUE *argv,
+ VALUE obj, int call_status)
+{
+ vm_passed_block_handler_set(ec, VM_BLOCK_HANDLER_NONE);
+ raise_method_missing(ec, argc, argv, obj, call_status | MISSING_MISSING);
+}
+
static inline VALUE
method_missing(VALUE obj, ID id, int argc, const VALUE *argv, enum method_missing_reason call_status)
{
VALUE *nargv, result, work, klass;
- rb_thread_t *th = GET_THREAD();
- VALUE block_handler = vm_passed_block_handler(th);
+ rb_execution_context_t *ec = GET_EC();
+ VALUE block_handler = vm_passed_block_handler(ec);
const rb_callable_method_entry_t *me;
- th->method_missing_reason = call_status;
+ ec->method_missing_reason = call_status;
if (id == idMethodMissing) {
missing:
- raise_method_missing(th, argc, argv, obj, call_status | MISSING_MISSING);
+ raise_method_missing(ec, argc, argv, obj, call_status | MISSING_MISSING);
}
nargv = ALLOCV_N(VALUE, work, argc + 1);
@@ -765,19 +740,13 @@ method_missing(VALUE obj, ID id, int argc, const VALUE *argv, enum method_missin
if (!klass) goto missing;
me = rb_callable_method_entry(klass, idMethodMissing);
if (!me || METHOD_ENTRY_BASIC(me)) goto missing;
- vm_passed_block_handler_set(th, block_handler);
- result = vm_call0(th, obj, idMethodMissing, argc, argv, me);
+ vm_passed_block_handler_set(ec, block_handler);
+ result = rb_vm_call0(ec, obj, idMethodMissing, argc, argv, me);
if (work) ALLOCV_END(work);
return result;
}
-void
-rb_raise_method_missing(rb_thread_t *th, int argc, const VALUE *argv,
- VALUE obj, int call_status)
-{
- vm_passed_block_handler_set(th, VM_BLOCK_HANDLER_NONE);
- raise_method_missing(th, argc, argv, obj, call_status | MISSING_MISSING);
-}
+#ifndef MJIT_HEADER
/*!
* Calls a method
@@ -803,10 +772,11 @@ rb_apply(VALUE recv, ID mid, VALUE args)
return ret;
}
argv = ALLOCA_N(VALUE, argc);
- MEMCPY(argv, RARRAY_CONST_PTR(args), VALUE, argc);
+ MEMCPY(argv, RARRAY_CONST_PTR_TRANSIENT(args), VALUE, argc);
return rb_call(recv, mid, argc, argv, CALL_FCALL);
}
+#undef rb_funcall
/*!
* Calls a method
* \param recv receiver of the method
@@ -879,18 +849,17 @@ VALUE
rb_funcall_with_block(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE passed_procval)
{
if (!NIL_P(passed_procval)) {
- rb_thread_t *th = GET_THREAD();
- vm_passed_block_handler_set(th, passed_procval);
+ vm_passed_block_handler_set(GET_EC(), passed_procval);
}
return rb_call(recv, mid, argc, argv, CALL_PUBLIC);
}
static VALUE *
-current_vm_stack_arg(rb_thread_t *th, const VALUE *argv)
+current_vm_stack_arg(const rb_execution_context_t *ec, const VALUE *argv)
{
- rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
- if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, prev_cfp)) return NULL;
+ rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(ec->cfp);
+ if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, prev_cfp)) return NULL;
if (prev_cfp->sp + 1 != argv) return NULL;
return prev_cfp->sp + 1;
}
@@ -902,13 +871,13 @@ send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope)
VALUE vid;
VALUE self;
VALUE ret, vargv = 0;
- rb_thread_t *th = GET_THREAD();
+ rb_execution_context_t *ec = GET_EC();
if (scope == CALL_PUBLIC) {
self = Qundef;
}
else {
- self = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp)->self;
+ self = RUBY_VM_PREVIOUS_CONTROL_FRAME(ec->cfp)->self;
}
if (argc == 0) {
@@ -920,13 +889,13 @@ send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope)
id = rb_check_id(&vid);
if (!id) {
if (rb_method_basic_definition_p(CLASS_OF(recv), idMethodMissing)) {
- VALUE exc = make_no_method_exception(rb_eNoMethodError, 0,
- recv, argc, argv,
- scope != CALL_PUBLIC);
+ VALUE exc = rb_make_no_method_exception(rb_eNoMethodError, 0,
+ recv, argc, argv,
+ scope != CALL_PUBLIC);
rb_exc_raise(exc);
}
if (!SYMBOL_P(*argv)) {
- VALUE *tmp_argv = current_vm_stack_arg(th, argv);
+ VALUE *tmp_argv = current_vm_stack_arg(ec, argv);
vid = rb_str_intern(vid);
if (tmp_argv) {
tmp_argv[0] = vid;
@@ -942,13 +911,13 @@ send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope)
}
}
id = idMethodMissing;
- th->method_missing_reason = MISSING_NOENTRY;
+ ec->method_missing_reason = MISSING_NOENTRY;
}
else {
argv++; argc--;
}
- PASS_PASSED_BLOCK_HANDLER_TH(th);
- ret = rb_call0(recv, id, argc, argv, scope, self);
+ PASS_PASSED_BLOCK_HANDLER_EC(ec);
+ ret = rb_call0(ec, recv, id, argc, argv, scope, self);
ALLOCV_END(vargv);
return ret;
}
@@ -995,7 +964,7 @@ rb_f_send(int argc, VALUE *argv, VALUE recv)
* 1.public_send(:puts, "hello") # causes NoMethodError
*/
-VALUE
+static VALUE
rb_f_public_send(int argc, VALUE *argv, VALUE recv)
{
return send_internal(argc, argv, recv, CALL_PUBLIC);
@@ -1006,7 +975,7 @@ rb_f_public_send(int argc, VALUE *argv, VALUE recv)
static inline VALUE
rb_yield_0(int argc, const VALUE * argv)
{
- return vm_yield(GET_THREAD(), argc, argv);
+ return vm_yield(GET_EC(), argc, argv);
}
VALUE
@@ -1026,6 +995,7 @@ rb_yield(VALUE val)
}
}
+#undef rb_yield_values
VALUE
rb_yield_values(int n, ...)
{
@@ -1068,9 +1038,15 @@ rb_yield_splat(VALUE values)
}
VALUE
+rb_yield_force_blockarg(VALUE values)
+{
+ return vm_yield_force_blockarg(GET_EC(), values);
+}
+
+VALUE
rb_yield_block(VALUE val, VALUE arg, int argc, const VALUE *argv, VALUE blockarg)
{
- return vm_yield_with_block(GET_THREAD(), argc, argv,
+ return vm_yield_with_block(GET_EC(), argc, argv,
NIL_P(blockarg) ? VM_BLOCK_HANDLER_NONE : blockarg);
}
@@ -1092,7 +1068,7 @@ loop_stop(VALUE dummy, VALUE exc)
static VALUE
rb_f_loop_size(VALUE self, VALUE args, VALUE eobj)
{
- return DBL2NUM(INFINITY);
+ return DBL2NUM(HUGE_VAL);
}
/*
@@ -1140,14 +1116,14 @@ vm_frametype_name(const rb_control_frame_t *cfp);
static VALUE
rb_iterate0(VALUE (* it_proc) (VALUE), VALUE data1,
const struct vm_ifunc *const ifunc,
- rb_thread_t *const th)
+ rb_execution_context_t *ec)
{
- int state;
+ enum ruby_tag_type state;
volatile VALUE retval = Qnil;
- rb_control_frame_t *const cfp = th->cfp;
+ rb_control_frame_t *const cfp = ec->cfp;
- TH_PUSH_TAG(th);
- state = TH_EXEC_TAG();
+ EC_PUSH_TAG(ec);
+ state = EC_EXEC_TAG();
if (state == 0) {
iter_retry:
{
@@ -1161,32 +1137,32 @@ rb_iterate0(VALUE (* it_proc) (VALUE), VALUE data1,
else {
block_handler = VM_CF_BLOCK_HANDLER(cfp);
}
- vm_passed_block_handler_set(th, block_handler);
+ vm_passed_block_handler_set(ec, block_handler);
}
retval = (*it_proc) (data1);
}
else if (state == TAG_BREAK || state == TAG_RETRY) {
- const struct vm_throw_data *const err = (struct vm_throw_data *)th->errinfo;
+ const struct vm_throw_data *const err = (struct vm_throw_data *)ec->errinfo;
const rb_control_frame_t *const escape_cfp = THROW_DATA_CATCH_FRAME(err);
if (cfp == escape_cfp) {
- rb_vm_rewind_cfp(th, cfp);
+ rb_vm_rewind_cfp(ec, cfp);
state = 0;
- th->state = 0;
- th->errinfo = Qnil;
+ ec->tag->state = TAG_NONE;
+ ec->errinfo = Qnil;
if (state == TAG_RETRY) goto iter_retry;
retval = THROW_DATA_VAL(err);
}
else if (0) {
- SDR(); fprintf(stderr, "%p, %p\n", cfp, escape_cfp);
+ SDR(); fprintf(stderr, "%p, %p\n", (void *)cfp, (void *)escape_cfp);
}
}
- TH_POP_TAG();
+ EC_POP_TAG();
if (state) {
- TH_JUMP_TAG(th, state);
+ EC_JUMP_TAG(ec, state);
}
return retval;
}
@@ -1196,8 +1172,8 @@ rb_iterate(VALUE (* it_proc)(VALUE), VALUE data1,
VALUE (* bl_proc)(ANYARGS), VALUE data2)
{
return rb_iterate0(it_proc, data1,
- bl_proc ? IFUNC_NEW(bl_proc, data2, rb_frame_this_func()) : 0,
- GET_THREAD());
+ bl_proc ? rb_vm_ifunc_proc_new(bl_proc, (void *)data2) : 0,
+ GET_EC());
}
struct iter_method_arg {
@@ -1229,6 +1205,23 @@ rb_block_call(VALUE obj, ID mid, int argc, const VALUE * argv,
return rb_iterate(iterate_method, (VALUE)&arg, bl_proc, data2);
}
+VALUE
+rb_lambda_call(VALUE obj, ID mid, int argc, const VALUE *argv,
+ rb_block_call_func_t bl_proc, int min_argc, int max_argc,
+ VALUE data2)
+{
+ struct iter_method_arg arg;
+ struct vm_ifunc *block;
+
+ if (!bl_proc) rb_raise(rb_eArgError, "NULL lambda function");
+ arg.obj = obj;
+ arg.mid = mid;
+ arg.argc = argc;
+ arg.argv = argv;
+ block = rb_vm_ifunc_new(bl_proc, (void *)data2, min_argc, max_argc);
+ return rb_iterate0(iterate_method, (VALUE)&arg, block, GET_EC());
+}
+
static VALUE
iterate_check_method(VALUE obj)
{
@@ -1257,137 +1250,106 @@ rb_each(VALUE obj)
return rb_call(obj, idEach, 0, 0, CALL_FCALL);
}
-static VALUE
-adjust_backtrace_in_eval(rb_thread_t *th, VALUE errinfo)
-{
- VALUE errat = rb_get_backtrace(errinfo);
- VALUE mesg = rb_attr_get(errinfo, id_mesg);
- if (RB_TYPE_P(errat, T_ARRAY)) {
- VALUE bt2 = rb_vm_backtrace_str_ary(th, 0, 0);
- if (RARRAY_LEN(bt2) > 0) {
- if (RB_TYPE_P(mesg, T_STRING) && !RSTRING_LEN(mesg)) {
- rb_ivar_set(errinfo, id_mesg, RARRAY_AREF(errat, 0));
- }
- RARRAY_ASET(errat, 0, RARRAY_AREF(bt2, 0));
- }
- }
- return errinfo;
-}
-
-static VALUE
-eval_string_with_cref(VALUE self, VALUE src, VALUE scope, rb_cref_t *const cref_arg,
- VALUE filename, int lineno)
+void rb_parser_warn_location(VALUE, int);
+static const rb_iseq_t *
+eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind,
+ const struct rb_block *base_block)
{
- int state;
- VALUE result = Qundef;
- rb_thread_t *th = GET_THREAD();
- struct rb_block block;
- const struct rb_block *base_block;
- volatile VALUE file;
- volatile int line;
-
- file = filename ? filename : rb_source_location(&lineno);
- line = lineno;
-
- {
- rb_cref_t *cref = cref_arg;
- rb_binding_t *bind = 0;
- const rb_iseq_t *iseq;
- VALUE absolute_path = Qnil;
- VALUE fname;
-
- if (file != Qundef) {
- absolute_path = file;
- }
+ const VALUE parser = rb_parser_new();
+ const rb_iseq_t *const parent = vm_block_iseq(base_block);
+ VALUE realpath = Qnil;
+ rb_iseq_t *iseq = 0;
+ rb_ast_t *ast;
- if (!NIL_P(scope)) {
- bind = Check_TypedStruct(scope, &ruby_binding_data_type);
-
- if (NIL_P(absolute_path) && !NIL_P(bind->path)) {
- file = bind->path;
- line = bind->first_lineno;
- absolute_path = rb_current_realfilepath();
- }
- base_block = &bind->block;
- }
- else {
- rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
-
- if (cfp != 0) {
- block.as.captured = *VM_CFP_TO_CAPTURED_BLOCK(cfp);
- block.as.captured.self = self;
- block.as.captured.code.iseq = cfp->iseq;
- block.type = block_type_iseq;
- base_block = &block;
- }
- else {
- rb_raise(rb_eRuntimeError, "Can't eval on top of Fiber or Thread");
- }
- }
-
- if ((fname = file) == Qundef) {
- fname = rb_usascii_str_new_cstr("(eval)");
- }
+ if (!fname) {
+ fname = rb_source_location(&line);
+ }
- if (RTEST(fname))
- fname = rb_fstring(fname);
- if (RTEST(absolute_path))
- absolute_path = rb_fstring(absolute_path);
+ if (fname != Qundef) {
+ if (!NIL_P(fname)) fname = rb_fstring(fname);
+ realpath = fname;
+ }
+ else if (bind) {
+ fname = pathobj_path(bind->pathobj);
+ realpath = pathobj_realpath(bind->pathobj);
+ line = bind->first_lineno;
+ rb_parser_warn_location(parser, TRUE);
+ }
+ else {
+ fname = rb_fstring_lit("(eval)");
+ }
- /* make eval iseq */
- iseq = rb_iseq_compile_with_option(src, fname, absolute_path, INT2FIX(line), base_block, Qnil);
+ rb_parser_set_context(parser, base_block, FALSE);
+ ast = rb_parser_compile_string_path(parser, fname, src, line);
+ if (ast->body.root) {
+ iseq = rb_iseq_new_with_opt(&ast->body,
+ parent->body->location.label,
+ fname, realpath, INT2FIX(line),
+ parent, ISEQ_TYPE_EVAL, NULL);
+ }
+ rb_ast_dispose(ast);
- if (!iseq) {
- rb_exc_raise(adjust_backtrace_in_eval(th, th->errinfo));
- }
+ if (0 && iseq) { /* for debug */
+ VALUE disasm = rb_iseq_disasm(iseq);
+ printf("%s\n", StringValuePtr(disasm));
+ }
- /* TODO: what the code checking? */
- if (!cref && base_block->as.captured.code.val) {
- if (NIL_P(scope)) {
- rb_cref_t *orig_cref = rb_vm_get_cref(vm_block_ep(base_block));
- cref = vm_cref_dup(orig_cref);
- }
- else {
- cref = NULL; /* use stacked CREF */
- }
- }
- vm_set_eval_stack(th, iseq, cref, base_block);
+ rb_exec_event_hook_script_compiled(GET_EC(), iseq, src);
- if (0) { /* for debug */
- VALUE disasm = rb_iseq_disasm(iseq);
- printf("%s\n", StringValuePtr(disasm));
- }
+ return iseq;
+}
- /* save new env */
- if (bind && iseq->body->local_table_size > 0) {
- vm_bind_update_env(bind, vm_make_env_object(th, th->cfp));
- }
+static VALUE
+eval_string_with_cref(VALUE self, VALUE src, rb_cref_t *cref, VALUE file, int line)
+{
+ rb_execution_context_t *ec = GET_EC();
+ struct rb_block block;
+ const rb_iseq_t *iseq;
+ rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
+ if (!cfp) {
+ rb_raise(rb_eRuntimeError, "Can't eval on top of Fiber or Thread");
}
- if (file != Qundef) {
- /* kick */
- return vm_exec(th);
- }
+ block.as.captured = *VM_CFP_TO_CAPTURED_BLOCK(cfp);
+ block.as.captured.self = self;
+ block.as.captured.code.iseq = cfp->iseq;
+ block.type = block_type_iseq;
- TH_PUSH_TAG(th);
- if ((state = TH_EXEC_TAG()) == 0) {
- result = vm_exec(th);
+ iseq = eval_make_iseq(src, file, line, NULL, &block);
+ if (!iseq) {
+ rb_exc_raise(ec->errinfo);
}
- TH_POP_TAG();
- if (state) {
- if (state == TAG_RAISE) {
- adjust_backtrace_in_eval(th, th->errinfo);
- }
- TH_JUMP_TAG(th, state);
+ /* TODO: what the code checking? */
+ if (!cref && block.as.captured.code.val) {
+ rb_cref_t *orig_cref = rb_vm_get_cref(vm_block_ep(&block));
+ cref = vm_cref_dup(orig_cref);
}
- return result;
+ vm_set_eval_stack(ec, iseq, cref, &block);
+
+ /* kick */
+ return vm_exec(ec, TRUE);
}
static VALUE
-eval_string(VALUE self, VALUE src, VALUE scope, VALUE file, int line)
+eval_string_with_scope(VALUE scope, VALUE src, VALUE file, int line)
{
- return eval_string_with_cref(self, src, scope, 0, file, line);
+ rb_execution_context_t *ec = GET_EC();
+ rb_binding_t *bind = Check_TypedStruct(scope, &ruby_binding_data_type);
+ const rb_iseq_t *iseq = eval_make_iseq(src, file, line, bind, &bind->block);
+ if (!iseq) {
+ rb_exc_raise(ec->errinfo);
+ }
+
+ vm_set_eval_stack(ec, iseq, NULL, &bind->block);
+
+ /* save new env */
+ if (iseq->body->local_table_size > 0) {
+ vm_bind_update_env(scope, bind, vm_make_env_object(ec, ec->cfp));
+ }
+
+ /* kick */
+ return vm_exec(ec, TRUE);
}
/*
@@ -1426,7 +1388,11 @@ rb_f_eval(int argc, const VALUE *argv, VALUE self)
if (!NIL_P(vfile))
file = vfile;
- return eval_string(self, src, scope, file, line);
+
+ if (NIL_P(scope))
+ return eval_string_with_cref(self, src, NULL, file, line);
+ else
+ return eval_string_with_scope(scope, src, file, line);
}
/** @note This function name is not stable. */
@@ -1434,7 +1400,7 @@ VALUE
ruby_eval_string_from_file(const char *str, const char *filename)
{
VALUE file = filename ? rb_str_new_cstr(filename) : 0;
- return eval_string(rb_vm_top_self(), rb_str_new2(str), Qnil, file, 1);
+ return eval_string_with_cref(rb_vm_top_self(), rb_str_new2(str), NULL, file, 1);
}
struct eval_string_from_file_arg {
@@ -1446,7 +1412,7 @@ static VALUE
eval_string_from_file_helper(VALUE data)
{
const struct eval_string_from_file_arg *const arg = (struct eval_string_from_file_arg*)data;
- return eval_string(rb_vm_top_self(), arg->str, Qnil, arg->filename, 1);
+ return eval_string_with_cref(rb_vm_top_self(), arg->str, NULL, arg->filename, 1);
}
VALUE
@@ -1476,6 +1442,12 @@ rb_eval_string(const char *str)
return ruby_eval_string_from_file(str, "eval");
}
+static VALUE
+eval_string_protect(VALUE str)
+{
+ return rb_eval_string((char *)str);
+}
+
/**
* Evaluates the given string in an isolated binding.
*
@@ -1487,9 +1459,9 @@ rb_eval_string(const char *str)
* @return The evaluated result if succeeded, an undefined value if otherwise.
*/
VALUE
-rb_eval_string_protect(const char *str, int *state)
+rb_eval_string_protect(const char *str, int *pstate)
{
- return rb_protect((VALUE (*)(VALUE))rb_eval_string, (VALUE)str, state);
+ return rb_protect(eval_string_protect, (VALUE)str, pstate);
}
/**
@@ -1504,9 +1476,9 @@ rb_eval_string_protect(const char *str, int *state)
* @return The evaluated result if succeeded, an undefined value if otherwise.
*/
VALUE
-rb_eval_string_wrap(const char *str, int *state)
+rb_eval_string_wrap(const char *str, int *pstate)
{
- int status;
+ int state;
rb_thread_t *th = GET_THREAD();
VALUE self = th->top_self;
VALUE wrapper = th->top_wrapper;
@@ -1516,16 +1488,16 @@ rb_eval_string_wrap(const char *str, int *state)
th->top_self = rb_obj_clone(rb_vm_top_self());
rb_extend_object(th->top_self, th->top_wrapper);
- val = rb_eval_string_protect(str, &status);
+ val = rb_eval_string_protect(str, &state);
th->top_self = self;
th->top_wrapper = wrapper;
- if (state) {
- *state = status;
+ if (pstate) {
+ *pstate = state;
}
- else if (status) {
- TH_JUMP_TAG(th, status);
+ else if (state != TAG_NONE) {
+ EC_JUMP_TAG(th->ec, state);
}
return val;
}
@@ -1533,30 +1505,30 @@ rb_eval_string_wrap(const char *str, int *state)
VALUE
rb_eval_cmd(VALUE cmd, VALUE arg, int level)
{
- int state;
+ enum ruby_tag_type state;
volatile VALUE val = Qnil; /* OK */
- const int VAR_NOCLOBBERED(safe) = rb_safe_level();
- rb_thread_t *const VAR_NOCLOBBERED(th) = GET_THREAD();
+ const int VAR_NOCLOBBERED(current_safe_level) = rb_safe_level();
+ rb_execution_context_t * volatile ec = GET_EC();
if (OBJ_TAINTED(cmd)) {
level = RUBY_SAFE_LEVEL_MAX;
}
- TH_PUSH_TAG(th);
+ EC_PUSH_TAG(ec);
rb_set_safe_level_force(level);
- if ((state = TH_EXEC_TAG()) == 0) {
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
if (!RB_TYPE_P(cmd, T_STRING)) {
val = rb_funcallv(cmd, idCall, RARRAY_LENINT(arg),
RARRAY_CONST_PTR(arg));
}
else {
- val = eval_string(rb_vm_top_self(), cmd, Qnil, 0, 0);
+ val = eval_string_with_cref(rb_vm_top_self(), cmd, NULL, 0, 0);
}
}
- TH_POP_TAG();
+ EC_POP_TAG();
- rb_set_safe_level_force(safe);
- if (state) TH_JUMP_TAG(th, state);
+ rb_set_safe_level_force(current_safe_level);
+ if (state) EC_JUMP_TAG(ec, state);
return val;
}
@@ -1565,14 +1537,15 @@ rb_eval_cmd(VALUE cmd, VALUE arg, int level)
static VALUE
yield_under(VALUE under, VALUE self, int argc, const VALUE *argv)
{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp;
+ rb_execution_context_t *ec = GET_EC();
+ rb_control_frame_t *cfp = ec->cfp;
VALUE block_handler = VM_CF_BLOCK_HANDLER(cfp);
VALUE new_block_handler = 0;
const struct rb_captured_block *captured = NULL;
struct rb_captured_block new_captured;
const VALUE *ep = NULL;
rb_cref_t *cref;
+ int is_lambda = FALSE;
if (block_handler != VM_BLOCK_HANDLER_NONE) {
again:
@@ -1588,27 +1561,29 @@ yield_under(VALUE under, VALUE self, int argc, const VALUE *argv)
new_block_handler = VM_BH_FROM_IFUNC_BLOCK(&new_captured);
break;
case block_handler_type_proc:
+ is_lambda = rb_proc_lambda_p(block_handler) != Qfalse;
block_handler = vm_proc_to_block_handler(VM_BH_TO_PROC(block_handler));
goto again;
case block_handler_type_symbol:
- return rb_sym_proc_call(SYM2ID(VM_BH_TO_SYMBOL(block_handler)), 1, &self, VM_BLOCK_HANDLER_NONE);
+ return rb_sym_proc_call(SYM2ID(VM_BH_TO_SYMBOL(block_handler)),
+ argc, argv, VM_BLOCK_HANDLER_NONE);
}
new_captured.self = self;
ep = captured->ep;
- VM_FORCE_WRITE_SPECIAL_CONST(&VM_CF_LEP(th->cfp)[VM_ENV_DATA_INDEX_SPECVAL], new_block_handler);
+ VM_FORCE_WRITE_SPECIAL_CONST(&VM_CF_LEP(ec->cfp)[VM_ENV_DATA_INDEX_SPECVAL], new_block_handler);
}
- cref = vm_cref_push(th, under, ep, TRUE);
- return vm_yield_with_cref(th, argc, argv, cref);
+ cref = vm_cref_push(ec, under, ep, TRUE);
+ return vm_yield_with_cref(ec, argc, argv, cref, is_lambda);
}
VALUE
rb_yield_refine_block(VALUE refinement, VALUE refinements)
{
- rb_thread_t *th = GET_THREAD();
- VALUE block_handler = VM_CF_BLOCK_HANDLER(th->cfp);
+ rb_execution_context_t *ec = GET_EC();
+ VALUE block_handler = VM_CF_BLOCK_HANDLER(ec->cfp);
if (vm_block_handler_type(block_handler) != block_handler_type_iseq) {
rb_bug("rb_yield_refine_block: an iseq block is required");
@@ -1618,11 +1593,11 @@ rb_yield_refine_block(VALUE refinement, VALUE refinements)
struct rb_captured_block new_captured = *captured;
VALUE new_block_handler = VM_BH_FROM_ISEQ_BLOCK(&new_captured);
const VALUE *ep = captured->ep;
- rb_cref_t *cref = vm_cref_push(th, refinement, ep, TRUE);
+ rb_cref_t *cref = vm_cref_push(ec, refinement, ep, TRUE);
CREF_REFINEMENTS_SET(cref, refinements);
- VM_FORCE_WRITE_SPECIAL_CONST(&VM_CF_LEP(th->cfp)[VM_ENV_DATA_INDEX_SPECVAL], new_block_handler);
+ VM_FORCE_WRITE_SPECIAL_CONST(&VM_CF_LEP(ec->cfp)[VM_ENV_DATA_INDEX_SPECVAL], new_block_handler);
new_captured.self = refinement;
- return vm_yield_with_cref(th, 0, NULL, cref);
+ return vm_yield_with_cref(ec, 0, NULL, cref, FALSE);
}
}
@@ -1630,9 +1605,9 @@ rb_yield_refine_block(VALUE refinement, VALUE refinements)
static VALUE
eval_under(VALUE under, VALUE self, VALUE src, VALUE file, int line)
{
- rb_cref_t *cref = vm_cref_push(GET_THREAD(), under, NULL, SPECIAL_CONST_P(self) && !NIL_P(under));
+ rb_cref_t *cref = vm_cref_push(GET_EC(), under, NULL, SPECIAL_CONST_P(self) && !NIL_P(under));
SafeStringValue(src);
- return eval_string_with_cref(self, src, Qnil, cref, file, line);
+ return eval_string_with_cref(self, src, cref, file, line);
}
static VALUE
@@ -1669,6 +1644,8 @@ singleton_class_for_eval(VALUE self)
switch (BUILTIN_TYPE(self)) {
case T_FLOAT: case T_BIGNUM: case T_SYMBOL:
return Qnil;
+ case T_STRING:
+ if (FL_TEST_RAW(self, RSTRING_FSTR)) return Qnil;
default:
return rb_singleton_class(self);
}
@@ -1883,14 +1860,14 @@ rb_f_throw(int argc, VALUE *argv)
rb_scan_args(argc, argv, "11", &tag, &value);
rb_throw_obj(tag, value);
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
void
rb_throw_obj(VALUE tag, VALUE value)
{
- rb_thread_t *th = GET_THREAD();
- struct rb_vm_tag *tt = th->tag;
+ rb_execution_context_t *ec = GET_EC();
+ struct rb_vm_tag *tt = ec->tag;
while (tt) {
if (tt->tag == tag) {
@@ -1907,8 +1884,8 @@ rb_throw_obj(VALUE tag, VALUE value)
rb_exc_raise(rb_class_new_instance(numberof(desc), desc, rb_eUncaughtThrow));
}
- th->errinfo = (VALUE)THROW_DATA_NEW(tag, NULL, TAG_THROW);
- TH_JUMP_TAG(th, TAG_THROW);
+ ec->errinfo = (VALUE)THROW_DATA_NEW(tag, NULL, TAG_THROW);
+ EC_JUMP_TAG(ec, TAG_THROW);
}
void
@@ -1975,16 +1952,9 @@ catch_i(VALUE tag, VALUE data)
*/
static VALUE
-rb_f_catch(int argc, VALUE *argv)
+rb_f_catch(int argc, VALUE *argv, VALUE self)
{
- VALUE tag;
-
- if (argc == 0) {
- tag = rb_obj_alloc(rb_cObject);
- }
- else {
- rb_scan_args(argc, argv, "01", &tag);
- }
+ VALUE tag = rb_check_arity(argc, 0, 1) ? argv[0] : rb_obj_alloc(rb_cObject);
return rb_catch_obj(tag, catch_i, 0);
}
@@ -1995,59 +1965,55 @@ rb_catch(const char *tag, VALUE (*func)(), VALUE data)
return rb_catch_obj(vtag, func, data);
}
-static VALUE vm_catch_protect(VALUE, rb_block_call_func *, VALUE, int *, rb_thread_t *);
-
-VALUE
-rb_catch_obj(VALUE t, VALUE (*func)(), VALUE data)
-{
- int state;
- rb_thread_t *th = GET_THREAD();
- VALUE val = vm_catch_protect(t, (rb_block_call_func *)func, data, &state, th);
- if (state)
- TH_JUMP_TAG(th, state);
- return val;
-}
-
-VALUE
-rb_catch_protect(VALUE t, rb_block_call_func *func, VALUE data, int *stateptr)
-{
- return vm_catch_protect(t, func, data, stateptr, GET_THREAD());
-}
-
static VALUE
vm_catch_protect(VALUE tag, rb_block_call_func *func, VALUE data,
- int *stateptr, rb_thread_t *th)
+ enum ruby_tag_type *stateptr, rb_execution_context_t *volatile ec)
{
- int state;
+ enum ruby_tag_type state;
VALUE val = Qnil; /* OK */
- rb_control_frame_t *saved_cfp = th->cfp;
+ rb_control_frame_t *volatile saved_cfp = ec->cfp;
- TH_PUSH_TAG(th);
+ EC_PUSH_TAG(ec);
_tag.tag = tag;
- if ((state = TH_EXEC_TAG()) == 0) {
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
/* call with argc=1, argv = [tag], block = Qnil to insure compatibility */
val = (*func)(tag, data, 1, (const VALUE *)&tag, Qnil);
}
- else if (state == TAG_THROW && THROW_DATA_VAL((struct vm_throw_data *)th->errinfo) == tag) {
- rb_vm_rewind_cfp(th, saved_cfp);
- val = th->tag->retval;
- th->errinfo = Qnil;
+ else if (state == TAG_THROW && THROW_DATA_VAL((struct vm_throw_data *)ec->errinfo) == tag) {
+ rb_vm_rewind_cfp(ec, saved_cfp);
+ val = ec->tag->retval;
+ ec->errinfo = Qnil;
state = 0;
}
- TH_POP_TAG();
+ EC_POP_TAG();
if (stateptr)
*stateptr = state;
return val;
}
+VALUE
+rb_catch_protect(VALUE t, rb_block_call_func *func, VALUE data, enum ruby_tag_type *stateptr)
+{
+ return vm_catch_protect(t, func, data, stateptr, GET_EC());
+}
+
+VALUE
+rb_catch_obj(VALUE t, VALUE (*func)(), VALUE data)
+{
+ enum ruby_tag_type state;
+ rb_execution_context_t *ec = GET_EC();
+ VALUE val = vm_catch_protect(t, (rb_block_call_func *)func, data, &state, ec);
+ if (state) EC_JUMP_TAG(ec, state);
+ return val;
+}
+
static void
local_var_list_init(struct local_var_list *vars)
{
- vars->tbl = rb_hash_new();
- RHASH(vars->tbl)->ntbl = st_init_numtable(); /* compare_by_identity */
+ vars->tbl = rb_hash_new_compare_by_id();
RBASIC_CLEAR_CLASS(vars->tbl);
}
@@ -2073,10 +2039,9 @@ static void
local_var_list_add(const struct local_var_list *vars, ID lid)
{
if (lid && rb_is_local_id(lid)) {
- /* should skip temporary variable */
- st_table *tbl = RHASH_TBL_RAW(vars->tbl);
- st_data_t idx = 0; /* tbl->num_entries */
- st_update(tbl, ID2SYM(lid), local_var_list_update, idx);
+ /* should skip temporary variable */
+ st_data_t idx = 0; /* tbl->num_entries */
+ rb_hash_stlike_update(vars->tbl, ID2SYM(lid), local_var_list_update, idx);
}
}
@@ -2097,9 +2062,8 @@ static VALUE
rb_f_local_variables(void)
{
struct local_var_list vars;
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp =
- vm_get_ruby_level_caller_cfp(th, RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp));
+ rb_execution_context_t *ec = GET_EC();
+ rb_control_frame_t *cfp = vm_get_ruby_level_caller_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(ec->cfp));
unsigned int i;
local_var_list_init(&vars);
@@ -2113,7 +2077,7 @@ rb_f_local_variables(void)
/* block */
const VALUE *ep = VM_CF_PREV_EP(cfp);
- if (vm_collect_local_variables_in_heap(th, ep, &vars)) {
+ if (vm_collect_local_variables_in_heap(ep, &vars)) {
break;
}
else {
@@ -2151,12 +2115,12 @@ rb_f_local_variables(void)
*/
-VALUE
+static VALUE
rb_f_block_given_p(void)
{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp;
- cfp = vm_get_ruby_level_caller_cfp(th, RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp));
+ rb_execution_context_t *ec = GET_EC();
+ rb_control_frame_t *cfp = ec->cfp;
+ cfp = vm_get_ruby_level_caller_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp));
if (cfp != NULL && VM_CF_BLOCK_HANDLER(cfp) != VM_BLOCK_HANDLER_NONE) {
return Qtrue;
@@ -2169,10 +2133,10 @@ rb_f_block_given_p(void)
VALUE
rb_current_realfilepath(void)
{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp;
- cfp = vm_get_ruby_level_caller_cfp(th, RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp));
- if (cfp != 0) return cfp->iseq->body->location.absolute_path;
+ const rb_execution_context_t *ec = GET_EC();
+ rb_control_frame_t *cfp = ec->cfp;
+ cfp = vm_get_ruby_level_caller_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp));
+ if (cfp != 0) return rb_iseq_realpath(cfp->iseq);
return Qnil;
}
@@ -2194,9 +2158,9 @@ Init_vm_eval(void)
rb_define_private_method(rb_cBasicObject, "method_missing", rb_method_missing, -1);
#if 1
- rb_add_method(rb_cBasicObject, rb_intern("__send__"),
+ rb_add_method(rb_cBasicObject, id__send__,
VM_METHOD_TYPE_OPTIMIZED, (void *)OPTIMIZED_METHOD_TYPE_SEND, METHOD_VISI_PUBLIC);
- rb_add_method(rb_mKernel, rb_intern("send"),
+ rb_add_method(rb_mKernel, idSend,
VM_METHOD_TYPE_OPTIMIZED, (void *)OPTIMIZED_METHOD_TYPE_SEND, METHOD_VISI_PUBLIC);
#else
rb_define_method(rb_cBasicObject, "__send__", rb_f_send, -1);
@@ -2219,3 +2183,5 @@ Init_vm_eval(void)
id_tag = rb_intern_const("tag");
id_value = rb_intern_const("value");
}
+
+#endif /* #ifndef MJIT_HEADER */
diff --git a/vm_exec.c b/vm_exec.c
index fcdc600b3a..0adaa7b721 100644
--- a/vm_exec.c
+++ b/vm_exec.c
@@ -45,12 +45,12 @@ vm_stack_overflow_for_insn(void)
#if !OPT_CALL_THREADED_CODE
static VALUE
-vm_exec_core(rb_thread_t *th, VALUE initial)
+vm_exec_core(rb_execution_context_t *ec, VALUE initial)
{
#if OPT_STACK_CACHING
#if 0
-#elif __GNUC__ && __x86_64__ && !defined(__native_client__)
+#elif __GNUC__ && __x86_64__
DECL_SC_REG(VALUE, a, "12");
DECL_SC_REG(VALUE, b, "13");
#else
@@ -66,11 +66,7 @@ vm_exec_core(rb_thread_t *th, VALUE initial)
#elif defined(__GNUC__) && defined(__x86_64__)
DECL_SC_REG(const VALUE *, pc, "14");
-# if defined(__native_client__)
- DECL_SC_REG(rb_control_frame_t *, cfp, "13");
-# else
DECL_SC_REG(rb_control_frame_t *, cfp, "15");
-# endif
#define USE_MACHINE_REGS 1
#elif defined(__GNUC__) && defined(__powerpc64__)
@@ -88,7 +84,7 @@ vm_exec_core(rb_thread_t *th, VALUE initial)
#undef RESTORE_REGS
#define RESTORE_REGS() \
{ \
- VM_REG_CFP = th->cfp; \
+ VM_REG_CFP = ec->cfp; \
reg_pc = reg_cfp->pc; \
}
@@ -102,11 +98,11 @@ vm_exec_core(rb_thread_t *th, VALUE initial)
#if OPT_TOKEN_THREADED_CODE || OPT_DIRECT_THREADED_CODE
#include "vmtc.inc"
- if (UNLIKELY(th == 0)) {
+ if (UNLIKELY(ec == 0)) {
return (VALUE)insns_address_table;
}
#endif
- reg_cfp = th->cfp;
+ reg_cfp = ec->cfp;
reg_pc = reg_cfp->pc;
#if OPT_STACK_CACHING
@@ -144,26 +140,27 @@ rb_vm_get_insns_address_table(void)
}
static VALUE
-vm_exec_core(rb_thread_t *th, VALUE initial)
+vm_exec_core(rb_execution_context_t *ec, VALUE initial)
{
- register rb_control_frame_t *reg_cfp = th->cfp;
+ register rb_control_frame_t *reg_cfp = ec->cfp;
+ rb_thread_t *th;
while (1) {
- reg_cfp = ((rb_insn_func_t) (*GET_PC()))(th, reg_cfp);
+ reg_cfp = ((rb_insn_func_t) (*GET_PC()))(ec, reg_cfp);
if (UNLIKELY(reg_cfp == 0)) {
break;
}
}
- if (th->retval != Qundef) {
+ if ((th = rb_ec_thread_ptr(ec))->retval != Qundef) {
VALUE ret = th->retval;
th->retval = Qundef;
return ret;
}
else {
- VALUE err = th->errinfo;
- th->errinfo = Qnil;
+ VALUE err = ec->errinfo;
+ ec->errinfo = Qnil;
return err;
}
}
diff --git a/vm_exec.h b/vm_exec.h
index 391eb6d151..f06399033d 100644
--- a/vm_exec.h
+++ b/vm_exec.h
@@ -27,7 +27,7 @@ typedef rb_iseq_t *ISEQ;
#if VMDEBUG > 0
#define debugs printf
#define DEBUG_ENTER_INSN(insn) \
- rb_vmdebug_debug_print_pre(th, GET_CFP(),GET_PC());
+ rb_vmdebug_debug_print_pre(ec, GET_CFP(), GET_PC());
#if OPT_STACK_CACHING
#define SC_REGS() , reg_a, reg_b
@@ -36,7 +36,7 @@ typedef rb_iseq_t *ISEQ;
#endif
#define DEBUG_END_INSN() \
- rb_vmdebug_debug_print_post(th, GET_CFP() SC_REGS());
+ rb_vmdebug_debug_print_post(ec, GET_CFP() SC_REGS());
#else
@@ -60,22 +60,29 @@ error !
#define INSN_ENTRY(insn) \
static rb_control_frame_t * \
- FUNC_FASTCALL(LABEL(insn))(rb_thread_t *th, rb_control_frame_t *reg_cfp) {
+ FUNC_FASTCALL(LABEL(insn))(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp) {
#define END_INSN(insn) return reg_cfp;}
#define NEXT_INSN() return reg_cfp;
+#define START_OF_ORIGINAL_INSN(x) /* ignore */
+#define DISPATCH_ORIGINAL_INSN(x) return LABEL(x)(ec, reg_cfp);
+
/************************************************/
#elif OPT_TOKEN_THREADED_CODE || OPT_DIRECT_THREADED_CODE
/* threaded code with gcc */
#define LABEL(x) INSN_LABEL_##x
#define ELABEL(x) INSN_ELABEL_##x
-#define LABEL_PTR(x) &&LABEL(x)
-
-#define INSN_ENTRY_SIG(insn)
+#define LABEL_PTR(x) RB_GNUC_EXTENSION(&&LABEL(x))
+#define INSN_ENTRY_SIG(insn) \
+ if (0) fprintf(stderr, "exec: %s@(%d, %d)@%s:%d\n", #insn, \
+ (int)(reg_pc - reg_cfp->iseq->body->iseq_encoded), \
+ (int)(reg_cfp->pc - reg_cfp->iseq->body->iseq_encoded), \
+ RSTRING_PTR(rb_iseq_path(reg_cfp->iseq)), \
+ (int)(rb_iseq_line_no(reg_cfp->iseq, reg_pc - reg_cfp->iseq->body->iseq_encoded)));
#define INSN_DISPATCH_SIG(insn)
@@ -91,17 +98,15 @@ error !
#else
#define DISPATCH_ARCH_DEPEND_WAY(addr) \
/* do nothing */
-
#endif
-
/**********************************/
#if OPT_DIRECT_THREADED_CODE
/* for GCC 3.4.x */
#define TC_DISPATCH(insn) \
INSN_DISPATCH_SIG(insn); \
- goto *(void const *)GET_CURRENT_INSN(); \
+ RB_GNUC_EXTENSION_BLOCK(goto *(void const *)GET_CURRENT_INSN()); \
;
#else
@@ -110,7 +115,7 @@ error !
#define TC_DISPATCH(insn) \
DISPATCH_ARCH_DEPEND_WAY(insns_address_table[GET_CURRENT_INSN()]); \
INSN_DISPATCH_SIG(insn); \
- goto *insns_address_table[GET_CURRENT_INSN()]; \
+ RB_GNUC_EXTENSION_BLOCK(goto *insns_address_table[GET_CURRENT_INSN()]); \
rb_bug("tc error");
@@ -130,6 +135,9 @@ error !
#define NEXT_INSN() TC_DISPATCH(__NEXT_INSN__)
+#define START_OF_ORIGINAL_INSN(x) start_of_##x:
+#define DISPATCH_ORIGINAL_INSN(x) goto start_of_##x;
+
/************************************************/
#else /* no threaded code */
/* most common method */
@@ -141,7 +149,6 @@ case BIN(insn):
DEBUG_END_INSN(); \
break;
-
#define INSN_DISPATCH() \
while (1) { \
switch (GET_CURRENT_INSN()) {
@@ -155,18 +162,28 @@ default: \
#define NEXT_INSN() goto first
+#define START_OF_ORIGINAL_INSN(x) start_of_##x:
+#define DISPATCH_ORIGINAL_INSN(x) goto start_of_##x;
+
#endif
-#define VM_SP_CNT(th, sp) ((sp) - (th)->stack)
+#define VM_SP_CNT(ec, sp) ((sp) - (ec)->vm_stack)
+#ifdef MJIT_HEADER
+#define THROW_EXCEPTION(exc) do { \
+ ec->errinfo = (VALUE)(exc); \
+ EC_JUMP_TAG(ec, ec->tag->state); \
+} while (0)
+#else
#if OPT_CALL_THREADED_CODE
#define THROW_EXCEPTION(exc) do { \
- th->errinfo = (VALUE)(exc); \
+ ec->errinfo = (VALUE)(exc); \
return 0; \
} while (0)
#else
#define THROW_EXCEPTION(exc) return (VALUE)(exc)
#endif
+#endif
#define SCREG(r) (reg_##r)
@@ -179,4 +196,7 @@ default: \
#define CHECK_VM_STACK_OVERFLOW_FOR_INSN(cfp, margin)
#endif
+#define INSN_LABEL2(insn, name) INSN_LABEL_ ## insn ## _ ## name
+#define INSN_LABEL(x) INSN_LABEL2(NAME_OF_CURRENT_INSN, x)
+
#endif /* RUBY_VM_EXEC_H */
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 43db728aef..0a018bdd54 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -10,31 +10,68 @@
/* finish iseq array */
#include "insns.inc"
+#ifndef MJIT_HEADER
+#include "insns_info.inc"
+#endif
#include <math.h>
#include "constant.h"
#include "internal.h"
-#include "probes.h"
-#include "probes_helper.h"
#include "ruby/config.h"
+#include "debug_counter.h"
/* control stack frame */
-static rb_control_frame_t *vm_get_ruby_level_caller_cfp(const rb_thread_t *th, const rb_control_frame_t *cfp);
+static rb_control_frame_t *vm_get_ruby_level_caller_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp);
-VALUE
-ruby_vm_sysstack_error_copy(void)
+MJIT_STATIC VALUE
+ruby_vm_special_exception_copy(VALUE exc)
{
- VALUE e = rb_obj_alloc(rb_eSysStackError);
- rb_obj_copy_ivar(e, sysstack_error);
+ VALUE e = rb_obj_alloc(rb_class_real(RBASIC_CLASS(exc)));
+ rb_obj_copy_ivar(e, exc);
return e;
}
+NORETURN(static void ec_stack_overflow(rb_execution_context_t *ec, int));
+static void
+ec_stack_overflow(rb_execution_context_t *ec, int setup)
+{
+ VALUE mesg = rb_ec_vm_ptr(ec)->special_exceptions[ruby_error_sysstack];
+ ec->raised_flag = RAISED_STACKOVERFLOW;
+ if (setup) {
+ VALUE at = rb_ec_backtrace_object(ec);
+ mesg = ruby_vm_special_exception_copy(mesg);
+ rb_ivar_set(mesg, idBt, at);
+ rb_ivar_set(mesg, idBt_locations, at);
+ }
+ ec->errinfo = mesg;
+ EC_JUMP_TAG(ec, TAG_RAISE);
+}
+
+NORETURN(static void vm_stackoverflow(void));
+
static void
vm_stackoverflow(void)
{
- rb_exc_raise(ruby_vm_sysstack_error_copy());
+ ec_stack_overflow(GET_EC(), TRUE);
}
+NORETURN(MJIT_STATIC void rb_ec_stack_overflow(rb_execution_context_t *ec, int crit));
+MJIT_STATIC void
+rb_ec_stack_overflow(rb_execution_context_t *ec, int crit)
+{
+ if (crit || rb_during_gc()) {
+ ec->raised_flag = RAISED_STACKOVERFLOW;
+ ec->errinfo = rb_ec_vm_ptr(ec)->special_exceptions[ruby_error_stackfatal];
+ EC_JUMP_TAG(ec, TAG_RAISE);
+ }
+#ifdef USE_SIGALTSTACK
+ ec_stack_overflow(ec, TRUE);
+#else
+ ec_stack_overflow(ec, FALSE);
+#endif
+}
+
+
#if VM_CHECK_MODE > 0
static int
callable_class_p(VALUE klass)
@@ -101,7 +138,7 @@ vm_check_frame_detail(VALUE type, int req_block, int req_me, int req_cref, VALUE
}
else { /* cref or Qfalse */
if (cref_or_me != Qfalse && cref_or_me_type != imemo_cref) {
- if ((magic == VM_FRAME_MAGIC_LAMBDA || magic == VM_FRAME_MAGIC_IFUNC) && (cref_or_me_type == imemo_ment)) {
+ if (((type & VM_FRAME_FLAG_LAMBDA) || magic == VM_FRAME_MAGIC_IFUNC) && (cref_or_me_type == imemo_ment)) {
/* ignore */
}
else {
@@ -149,10 +186,8 @@ vm_check_frame(VALUE type,
CHECK(VM_FRAME_MAGIC_TOP, TRUE, FALSE, TRUE, FALSE);
CHECK(VM_FRAME_MAGIC_CFUNC, TRUE, TRUE, FALSE, TRUE);
CHECK(VM_FRAME_MAGIC_BLOCK, FALSE, FALSE, FALSE, FALSE);
- CHECK(VM_FRAME_MAGIC_PROC, FALSE, FALSE, FALSE, FALSE);
CHECK(VM_FRAME_MAGIC_IFUNC, FALSE, FALSE, FALSE, TRUE);
CHECK(VM_FRAME_MAGIC_EVAL, FALSE, FALSE, FALSE, FALSE);
- CHECK(VM_FRAME_MAGIC_LAMBDA, FALSE, FALSE, FALSE, FALSE);
CHECK(VM_FRAME_MAGIC_RESCUE, FALSE, FALSE, FALSE, FALSE);
CHECK(VM_FRAME_MAGIC_DUMMY, TRUE, FALSE, FALSE, FALSE);
default:
@@ -165,7 +200,7 @@ vm_check_frame(VALUE type,
#endif /* VM_CHECK_MODE > 0 */
static inline rb_control_frame_t *
-vm_push_frame(rb_thread_t *th,
+vm_push_frame(rb_execution_context_t *ec,
const rb_iseq_t *iseq,
VALUE type,
VALUE self,
@@ -176,7 +211,7 @@ vm_push_frame(rb_thread_t *th,
int local_size,
int stack_max)
{
- rb_control_frame_t *const cfp = th->cfp - 1;
+ rb_control_frame_t *const cfp = ec->cfp - 1;
int i;
vm_check_frame(type, specval, cref_or_me, iseq);
@@ -185,7 +220,7 @@ vm_push_frame(rb_thread_t *th,
/* check stack overflow */
CHECK_VM_STACK_OVERFLOW0(cfp, sp, local_size + stack_max);
- th->cfp = cfp;
+ ec->cfp = cfp;
/* setup new frame */
cfp->pc = (VALUE *)pc;
@@ -208,7 +243,8 @@ vm_push_frame(rb_thread_t *th,
*sp++ = specval /* ep[-1] / block handler or prev env ptr */;
*sp = type; /* ep[-0] / ENV_FLAGS */
- cfp->ep = sp;
+ /* Store initial value of ep as bp to skip calculation cost of bp on JIT cancellation. */
+ cfp->ep = cfp->bp = sp;
cfp->sp = sp + 1;
#if VM_DEBUG_BP_CHECK
@@ -219,11 +255,39 @@ vm_push_frame(rb_thread_t *th,
SDR();
}
+#if USE_DEBUG_COUNTER
+ RB_DEBUG_COUNTER_INC(frame_push);
+ switch (type & VM_FRAME_MAGIC_MASK) {
+ case VM_FRAME_MAGIC_METHOD: RB_DEBUG_COUNTER_INC(frame_push_method); break;
+ case VM_FRAME_MAGIC_BLOCK: RB_DEBUG_COUNTER_INC(frame_push_block); break;
+ case VM_FRAME_MAGIC_CLASS: RB_DEBUG_COUNTER_INC(frame_push_class); break;
+ case VM_FRAME_MAGIC_TOP: RB_DEBUG_COUNTER_INC(frame_push_top); break;
+ case VM_FRAME_MAGIC_CFUNC: RB_DEBUG_COUNTER_INC(frame_push_cfunc); break;
+ case VM_FRAME_MAGIC_IFUNC: RB_DEBUG_COUNTER_INC(frame_push_ifunc); break;
+ case VM_FRAME_MAGIC_EVAL: RB_DEBUG_COUNTER_INC(frame_push_eval); break;
+ case VM_FRAME_MAGIC_RESCUE: RB_DEBUG_COUNTER_INC(frame_push_rescue); break;
+ case VM_FRAME_MAGIC_DUMMY: RB_DEBUG_COUNTER_INC(frame_push_dummy); break;
+ default: rb_bug("unreachable");
+ }
+ {
+ rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
+ if (RUBY_VM_END_CONTROL_FRAME(ec) != prev_cfp) {
+ int cur_ruby_frame = VM_FRAME_RUBYFRAME_P(cfp);
+ int pre_ruby_frame = VM_FRAME_RUBYFRAME_P(prev_cfp);
+
+ pre_ruby_frame ? (cur_ruby_frame ? RB_DEBUG_COUNTER_INC(frame_R2R) :
+ RB_DEBUG_COUNTER_INC(frame_R2C)):
+ (cur_ruby_frame ? RB_DEBUG_COUNTER_INC(frame_C2R) :
+ RB_DEBUG_COUNTER_INC(frame_C2C));
+ }
+ }
+#endif
+
return cfp;
}
rb_control_frame_t *
-rb_vm_push_frame(rb_thread_t *th,
+rb_vm_push_frame(rb_execution_context_t *ec,
const rb_iseq_t *iseq,
VALUE type,
VALUE self,
@@ -234,27 +298,27 @@ rb_vm_push_frame(rb_thread_t *th,
int local_size,
int stack_max)
{
- return vm_push_frame(th, iseq, type, self, specval, cref_or_me, pc, sp, local_size, stack_max);
+ return vm_push_frame(ec, iseq, type, self, specval, cref_or_me, pc, sp, local_size, stack_max);
}
/* return TRUE if the frame is finished */
static inline int
-vm_pop_frame(rb_thread_t *th, rb_control_frame_t *cfp, const VALUE *ep)
+vm_pop_frame(rb_execution_context_t *ec, rb_control_frame_t *cfp, const VALUE *ep)
{
VALUE flags = ep[VM_ENV_DATA_INDEX_FLAGS];
if (VM_CHECK_MODE >= 4) rb_gc_verify_internal_consistency();
if (VMDEBUG == 2) SDR();
- th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
+ ec->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
return flags & VM_FRAME_FLAG_FINISH;
}
-void
-rb_vm_pop_frame(rb_thread_t *th)
+MJIT_STATIC void
+rb_vm_pop_frame(rb_execution_context_t *ec)
{
- vm_pop_frame(th, th->cfp, th->cfp->ep);
+ vm_pop_frame(ec, ec->cfp, ec->cfp->ep);
}
/* method dispatch */
@@ -274,7 +338,7 @@ rb_arity_error_new(int argc, int min, int max)
return rb_exc_new3(rb_eArgError, err_mess);
}
-void
+MJIT_STATIC void
rb_error_arity(int argc, int min, int max)
{
rb_exc_raise(rb_arity_error_new(argc, min, max));
@@ -291,6 +355,7 @@ vm_env_write_slowpath(const VALUE *ep, int index, VALUE v)
rb_gc_writebarrier_remember(VM_ENV_ENVVAL(ep));
VM_FORCE_WRITE(&ep[index], v);
VM_ENV_FLAGS_UNSET(ep, VM_ENV_FLAG_WB_REQUIRED);
+ RB_DEBUG_COUNTER_INC(lvar_set_slowpath);
}
static inline void
@@ -305,13 +370,27 @@ vm_env_write(const VALUE *ep, int index, VALUE v)
}
}
-void
-rb_vm_env_write(const VALUE *ep, int index, VALUE v)
+MJIT_STATIC VALUE
+rb_vm_bh_to_procval(const rb_execution_context_t *ec, VALUE block_handler)
{
- vm_env_write(ep, index, v);
+ if (block_handler == VM_BLOCK_HANDLER_NONE) {
+ return Qnil;
+ }
+ else {
+ switch (vm_block_handler_type(block_handler)) {
+ case block_handler_type_iseq:
+ case block_handler_type_ifunc:
+ return rb_vm_make_proc(ec, VM_BH_TO_CAPT_BLOCK(block_handler), rb_cProc);
+ case block_handler_type_symbol:
+ return rb_sym_to_proc(VM_BH_TO_SYMBOL(block_handler));
+ case block_handler_type_proc:
+ return VM_BH_TO_PROC(block_handler);
+ default:
+ VM_UNREACHABLE(rb_vm_bh_to_procval);
+ }
+ }
}
-
/* svar */
#if VM_CHECK_MODE > 0
@@ -334,15 +413,15 @@ vm_svar_valid_p(VALUE svar)
#endif
static inline struct vm_svar *
-lep_svar(rb_thread_t *th, const VALUE *lep)
+lep_svar(const rb_execution_context_t *ec, const VALUE *lep)
{
VALUE svar;
- if (lep && (th == NULL || th->root_lep != lep)) {
+ if (lep && (ec == NULL || ec->root_lep != lep)) {
svar = lep[VM_ENV_DATA_INDEX_ME_CREF];
}
else {
- svar = th->root_svar;
+ svar = ec->root_svar;
}
VM_ASSERT(svar == Qfalse || vm_svar_valid_p(svar));
@@ -351,22 +430,22 @@ lep_svar(rb_thread_t *th, const VALUE *lep)
}
static inline void
-lep_svar_write(rb_thread_t *th, const VALUE *lep, const struct vm_svar *svar)
+lep_svar_write(const rb_execution_context_t *ec, const VALUE *lep, const struct vm_svar *svar)
{
VM_ASSERT(vm_svar_valid_p((VALUE)svar));
- if (lep && (th == NULL || th->root_lep != lep)) {
+ if (lep && (ec == NULL || ec->root_lep != lep)) {
vm_env_write(lep, VM_ENV_DATA_INDEX_ME_CREF, (VALUE)svar);
}
else {
- RB_OBJ_WRITE(th->self, &th->root_svar, svar);
+ RB_OBJ_WRITE(rb_ec_thread_ptr(ec)->self, &ec->root_svar, svar);
}
}
static VALUE
-lep_svar_get(rb_thread_t *th, const VALUE *lep, rb_num_t key)
+lep_svar_get(const rb_execution_context_t *ec, const VALUE *lep, rb_num_t key)
{
- const struct vm_svar *svar = lep_svar(th, lep);
+ const struct vm_svar *svar = lep_svar(ec, lep);
if ((VALUE)svar == Qfalse || imemo_type((VALUE)svar) != imemo_svar) return Qnil;
@@ -395,12 +474,12 @@ svar_new(VALUE obj)
}
static void
-lep_svar_set(rb_thread_t *th, const VALUE *lep, rb_num_t key, VALUE val)
+lep_svar_set(const rb_execution_context_t *ec, const VALUE *lep, rb_num_t key, VALUE val)
{
- struct vm_svar *svar = lep_svar(th, lep);
+ struct vm_svar *svar = lep_svar(ec, lep);
if ((VALUE)svar == Qfalse || imemo_type((VALUE)svar) != imemo_svar) {
- lep_svar_write(th, lep, svar = svar_new((VALUE)svar));
+ lep_svar_write(ec, lep, svar = svar_new((VALUE)svar));
}
switch (key) {
@@ -422,15 +501,15 @@ lep_svar_set(rb_thread_t *th, const VALUE *lep, rb_num_t key, VALUE val)
}
static inline VALUE
-vm_getspecial(rb_thread_t *th, const VALUE *lep, rb_num_t key, rb_num_t type)
+vm_getspecial(const rb_execution_context_t *ec, const VALUE *lep, rb_num_t key, rb_num_t type)
{
VALUE val;
if (type == 0) {
- val = lep_svar_get(th, lep, key);
+ val = lep_svar_get(ec, lep, key);
}
else {
- VALUE backref = lep_svar_get(th, lep, VM_SVAR_BACKREF);
+ VALUE backref = lep_svar_get(ec, lep, VM_SVAR_BACKREF);
if (type & 0x01) {
switch (type >> 1) {
@@ -484,7 +563,7 @@ check_method_entry(VALUE obj, int can_be_svar)
}
}
-const rb_callable_method_entry_t *
+MJIT_STATIC const rb_callable_method_entry_t *
rb_vm_frame_method_entry(const rb_control_frame_t *cfp)
{
const VALUE *ep = cfp->ep;
@@ -681,7 +760,7 @@ rb_vm_rewrite_cref(rb_cref_t *cref, VALUE old_klass, VALUE new_klass, rb_cref_t
}
static rb_cref_t *
-vm_cref_push(rb_thread_t *th, VALUE klass, const VALUE *ep, int pushed_by_eval)
+vm_cref_push(const rb_execution_context_t *ec, VALUE klass, const VALUE *ep, int pushed_by_eval)
{
rb_cref_t *prev_cref = NULL;
@@ -689,7 +768,7 @@ vm_cref_push(rb_thread_t *th, VALUE klass, const VALUE *ep, int pushed_by_eval)
prev_cref = vm_env_cref(ep);
}
else {
- rb_control_frame_t *cfp = vm_get_ruby_level_caller_cfp(th, th->cfp);
+ rb_control_frame_t *cfp = vm_get_ruby_level_caller_cfp(ec, ec->cfp);
if (cfp) {
prev_cref = vm_env_cref(cfp->ep);
@@ -755,14 +834,14 @@ vm_get_iclass(rb_control_frame_t *cfp, VALUE klass)
}
static inline VALUE
-vm_get_ev_const(rb_thread_t *th, VALUE orig_klass, ID id, int is_defined)
+vm_get_ev_const(rb_execution_context_t *ec, VALUE orig_klass, ID id, int is_defined)
{
void rb_const_warn_if_deprecated(const rb_const_entry_t *ce, VALUE klass, ID id);
VALUE val;
if (orig_klass == Qnil) {
/* in current lexical scope */
- const rb_cref_t *root_cref = rb_vm_get_cref(th->cfp->ep);
+ const rb_cref_t *root_cref = rb_vm_get_cref(ec->cfp->ep);
const rb_cref_t *cref;
VALUE klass = Qnil;
@@ -790,7 +869,7 @@ vm_get_ev_const(rb_thread_t *th, VALUE orig_klass, ID id, int is_defined)
if (am == klass) break;
am = klass;
if (is_defined) return 1;
- if (rb_autoloading_value(klass, id, &av)) return av;
+ if (rb_autoloading_value(klass, id, &av, NULL)) return av;
rb_autoload_load(klass, id);
goto search_continue;
}
@@ -808,10 +887,10 @@ vm_get_ev_const(rb_thread_t *th, VALUE orig_klass, ID id, int is_defined)
/* search self */
if (root_cref && !NIL_P(CREF_CLASS(root_cref))) {
- klass = vm_get_iclass(th->cfp, CREF_CLASS(root_cref));
+ klass = vm_get_iclass(ec->cfp, CREF_CLASS(root_cref));
}
else {
- klass = CLASS_OF(th->cfp->self);
+ klass = CLASS_OF(ec->cfp->self);
}
if (is_defined) {
@@ -872,32 +951,24 @@ vm_search_const_defined_class(const VALUE cbase, ID id)
return 0;
}
-#ifndef USE_IC_FOR_IVAR
-#define USE_IC_FOR_IVAR 1
-#endif
-
ALWAYS_INLINE(static VALUE vm_getivar(VALUE, ID, IC, struct rb_call_cache *, int));
static inline VALUE
vm_getivar(VALUE obj, ID id, IC ic, struct rb_call_cache *cc, int is_attr)
{
-#if USE_IC_FOR_IVAR
+#if OPT_IC_FOR_IVAR
if (LIKELY(RB_TYPE_P(obj, T_OBJECT))) {
VALUE val = Qundef;
- if (LIKELY(is_attr ? cc->aux.index > 0 : ic->ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass))) {
- st_index_t index = !is_attr ? ic->ic_value.index : (cc->aux.index - 1);
+ if (LIKELY(is_attr ?
+ RB_DEBUG_COUNTER_INC_UNLESS(ivar_get_ic_miss_unset, cc->aux.index > 0) :
+ RB_DEBUG_COUNTER_INC_UNLESS(ivar_get_ic_miss_serial,
+ ic->ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass)))) {
+ st_index_t index = !is_attr ? ic->ic_value.index : (cc->aux.index - 1);
if (LIKELY(index < ROBJECT_NUMIV(obj))) {
val = ROBJECT_IVPTR(obj)[index];
}
- undef_check:
- if (UNLIKELY(val == Qundef)) {
- if (!is_attr && RTEST(ruby_verbose))
- rb_warning("instance variable %"PRIsVALUE" not initialized", QUOTE_ID(id));
- val = Qnil;
- }
- return val;
}
else {
- st_data_t index;
+ st_data_t index;
struct st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj);
if (iv_index_tbl) {
@@ -905,19 +976,30 @@ vm_getivar(VALUE obj, ID id, IC ic, struct rb_call_cache *cc, int is_attr)
if (index < ROBJECT_NUMIV(obj)) {
val = ROBJECT_IVPTR(obj)[index];
}
- if (!is_attr) {
- ic->ic_value.index = index;
- ic->ic_serial = RCLASS_SERIAL(RBASIC(obj)->klass);
- }
- else { /* call_info */
- cc->aux.index = (int)index + 1;
- }
+ if (!is_attr) {
+ ic->ic_value.index = index;
+ ic->ic_serial = RCLASS_SERIAL(RBASIC(obj)->klass);
+ }
+ else { /* call_info */
+ cc->aux.index = (int)index + 1;
+ }
}
}
- goto undef_check;
}
+ if (UNLIKELY(val == Qundef)) {
+ if (!is_attr && RTEST(ruby_verbose))
+ rb_warning("instance variable %"PRIsVALUE" not initialized", QUOTE_ID(id));
+ val = Qnil;
+ }
+ RB_DEBUG_COUNTER_INC(ivar_get_ic_hit);
+ return val;
+ }
+ else {
+ RB_DEBUG_COUNTER_INC(ivar_get_ic_miss_noobject);
}
-#endif /* USE_IC_FOR_IVAR */
+#endif /* OPT_IC_FOR_IVAR */
+ RB_DEBUG_COUNTER_INC(ivar_get_ic_miss);
+
if (is_attr)
return rb_attr_get(obj, id);
return rb_ivar_get(obj, id);
@@ -926,21 +1008,22 @@ vm_getivar(VALUE obj, ID id, IC ic, struct rb_call_cache *cc, int is_attr)
static inline VALUE
vm_setivar(VALUE obj, ID id, VALUE val, IC ic, struct rb_call_cache *cc, int is_attr)
{
-#if USE_IC_FOR_IVAR
- rb_check_frozen(obj);
+#if OPT_IC_FOR_IVAR
+ rb_check_frozen_internal(obj);
if (LIKELY(RB_TYPE_P(obj, T_OBJECT))) {
VALUE klass = RBASIC(obj)->klass;
st_data_t index;
if (LIKELY(
- (!is_attr && ic->ic_serial == RCLASS_SERIAL(klass)) ||
- (is_attr && cc->aux.index > 0))) {
+ (!is_attr && RB_DEBUG_COUNTER_INC_UNLESS(ivar_set_ic_miss_serial, ic->ic_serial == RCLASS_SERIAL(klass))) ||
+ ( is_attr && RB_DEBUG_COUNTER_INC_UNLESS(ivar_set_ic_miss_unset, cc->aux.index > 0)))) {
VALUE *ptr = ROBJECT_IVPTR(obj);
index = !is_attr ? ic->ic_value.index : cc->aux.index-1;
- if (index < ROBJECT_NUMIV(obj)) {
+ if (RB_DEBUG_COUNTER_INC_UNLESS(ivar_set_ic_miss_oorange, index < ROBJECT_NUMIV(obj))) {
RB_OBJ_WRITE(obj, &ptr[index], val);
+ RB_DEBUG_COUNTER_INC(ivar_set_ic_hit);
return val; /* inline cache hit */
}
}
@@ -948,10 +1031,10 @@ vm_setivar(VALUE obj, ID id, VALUE val, IC ic, struct rb_call_cache *cc, int is_
struct st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj);
if (iv_index_tbl && st_lookup(iv_index_tbl, (st_data_t)id, &index)) {
- if (!is_attr) {
- ic->ic_value.index = index;
- ic->ic_serial = RCLASS_SERIAL(klass);
- }
+ if (!is_attr) {
+ ic->ic_value.index = index;
+ ic->ic_serial = RCLASS_SERIAL(klass);
+ }
else if (index >= INT_MAX) {
rb_raise(rb_eArgError, "too many instance variables");
}
@@ -962,14 +1045,18 @@ vm_setivar(VALUE obj, ID id, VALUE val, IC ic, struct rb_call_cache *cc, int is_
/* fall through */
}
}
-#endif /* USE_IC_FOR_IVAR */
+ else {
+ RB_DEBUG_COUNTER_INC(ivar_set_ic_miss_noobject);
+ }
+#endif /* OPT_IC_FOR_IVAR */
+ RB_DEBUG_COUNTER_INC(ivar_set_ic_miss);
return rb_ivar_set(obj, id, val);
}
static inline VALUE
vm_getinstancevariable(VALUE obj, ID id, IC ic)
{
- return vm_getivar(obj, id, ic, 0, 0);
+ return vm_getivar(obj, id, ic, NULL, FALSE);
}
static inline void
@@ -979,32 +1066,31 @@ vm_setinstancevariable(VALUE obj, ID id, VALUE val, IC ic)
}
static VALUE
-vm_throw_continue(rb_thread_t *th, VALUE err)
+vm_throw_continue(const rb_execution_context_t *ec, VALUE err)
{
/* continue throw */
if (FIXNUM_P(err)) {
- th->state = FIX2INT(err);
+ ec->tag->state = FIX2INT(err);
}
else if (SYMBOL_P(err)) {
- th->state = TAG_THROW;
+ ec->tag->state = TAG_THROW;
}
else if (THROW_DATA_P(err)) {
- th->state = THROW_DATA_STATE((struct vm_throw_data *)err);
+ ec->tag->state = THROW_DATA_STATE((struct vm_throw_data *)err);
}
else {
- th->state = TAG_RAISE;
- /*th->state = FIX2INT(rb_ivar_get(err, idThrowState));*/
+ ec->tag->state = TAG_RAISE;
}
return err;
}
static VALUE
-vm_throw_start(rb_thread_t *const th, rb_control_frame_t *const reg_cfp, enum ruby_tag_type state,
- const int flag, const rb_num_t level, const VALUE throwobj)
+vm_throw_start(const rb_execution_context_t *ec, rb_control_frame_t *const reg_cfp, enum ruby_tag_type state,
+ const int flag, const VALUE throwobj)
{
const rb_control_frame_t *escape_cfp = NULL;
- const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(th); /* end of control frame pointer */
+ const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(ec); /* end of control frame pointer */
if (flag != 0) {
/* do nothing */
@@ -1024,12 +1110,12 @@ vm_throw_start(rb_thread_t *const th, rb_control_frame_t *const reg_cfp, enum ru
else {
ep = VM_ENV_PREV_EP(ep);
base_iseq = base_iseq->body->parent_iseq;
- escape_cfp = rb_vm_search_cf_from_ep(th, escape_cfp, ep);
+ escape_cfp = rb_vm_search_cf_from_ep(ec, escape_cfp, ep);
VM_ASSERT(escape_cfp->iseq == base_iseq);
}
}
- if (VM_FRAME_TYPE(escape_cfp) == VM_FRAME_MAGIC_LAMBDA) {
+ if (VM_FRAME_LAMBDA_P(escape_cfp)) {
/* lambda{... break ...} */
is_orphan = 0;
state = TAG_RETURN;
@@ -1039,16 +1125,18 @@ vm_throw_start(rb_thread_t *const th, rb_control_frame_t *const reg_cfp, enum ru
while (escape_cfp < eocfp) {
if (escape_cfp->ep == ep) {
- const VALUE epc = escape_cfp->pc - escape_cfp->iseq->body->iseq_encoded;
- const rb_iseq_t * const iseq = escape_cfp->iseq;
- const struct iseq_catch_table * const ct = iseq->body->catch_table;
- const int ct_size = ct->size;
- int i;
+ const rb_iseq_t *const iseq = escape_cfp->iseq;
+ const VALUE epc = escape_cfp->pc - iseq->body->iseq_encoded;
+ const struct iseq_catch_table *const ct = iseq->body->catch_table;
+ unsigned int i;
- for (i=0; i<ct_size; i++) {
+ if (!ct) break;
+ for (i=0; i < ct->size; i++) {
const struct iseq_catch_table_entry * const entry = &ct->entries[i];
- if (entry->type == CATCH_TYPE_BREAK && entry->start < epc && entry->end >= epc) {
+ if (entry->type == CATCH_TYPE_BREAK &&
+ entry->iseq == base_iseq &&
+ entry->start < epc && entry->end >= epc) {
if (entry->cont == epc) { /* found! */
is_orphan = 0;
}
@@ -1067,19 +1155,15 @@ vm_throw_start(rb_thread_t *const th, rb_control_frame_t *const reg_cfp, enum ru
}
}
else if (state == TAG_RETRY) {
- rb_num_t i;
const VALUE *ep = VM_ENV_PREV_EP(GET_EP());
- for (i = 0; i < level; i++) {
- ep = VM_ENV_PREV_EP(ep);
- }
-
- escape_cfp = rb_vm_search_cf_from_ep(th, reg_cfp, ep);
+ escape_cfp = rb_vm_search_cf_from_ep(ec, reg_cfp, ep);
}
else if (state == TAG_RETURN) {
const VALUE *current_ep = GET_EP();
const VALUE *target_lep = VM_EP_LEP(current_ep);
int in_class_frame = 0;
+ int toplevel = 1;
escape_cfp = reg_cfp;
while (escape_cfp < eocfp) {
@@ -1097,7 +1181,8 @@ vm_throw_start(rb_thread_t *const th, rb_control_frame_t *const reg_cfp, enum ru
}
if (lep == target_lep) {
- if (VM_FRAME_TYPE(escape_cfp) == VM_FRAME_MAGIC_LAMBDA) {
+ if (VM_FRAME_LAMBDA_P(escape_cfp)) {
+ toplevel = 0;
if (in_class_frame) {
/* lambda {class A; ... return ...; end} */
goto valid_return;
@@ -1114,6 +1199,20 @@ vm_throw_start(rb_thread_t *const th, rb_control_frame_t *const reg_cfp, enum ru
}
}
}
+ else if (VM_FRAME_RUBYFRAME_P(escape_cfp)) {
+ switch (escape_cfp->iseq->body->type) {
+ case ISEQ_TYPE_TOP:
+ case ISEQ_TYPE_MAIN:
+ if (toplevel) goto valid_return;
+ break;
+ case ISEQ_TYPE_EVAL:
+ case ISEQ_TYPE_CLASS:
+ toplevel = 0;
+ break;
+ default:
+ break;
+ }
+ }
}
if (escape_cfp->ep == target_lep && escape_cfp->iseq->body->type == ISEQ_TYPE_METHOD) {
@@ -1131,45 +1230,49 @@ vm_throw_start(rb_thread_t *const th, rb_control_frame_t *const reg_cfp, enum ru
rb_bug("isns(throw): unsupport throw type");
}
- th->state = state;
+ ec->tag->state = state;
return (VALUE)THROW_DATA_NEW(throwobj, escape_cfp, state);
}
static VALUE
-vm_throw(rb_thread_t *th, rb_control_frame_t *reg_cfp,
+vm_throw(const rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
rb_num_t throw_state, VALUE throwobj)
{
const int state = (int)(throw_state & VM_THROW_STATE_MASK);
const int flag = (int)(throw_state & VM_THROW_NO_ESCAPE_FLAG);
- const rb_num_t level = throw_state >> VM_THROW_LEVEL_SHIFT;
if (state != 0) {
- return vm_throw_start(th, reg_cfp, state, flag, level, throwobj);
+ return vm_throw_start(ec, reg_cfp, state, flag, throwobj);
}
else {
- return vm_throw_continue(th, throwobj);
+ return vm_throw_continue(ec, throwobj);
}
}
static inline void
-vm_expandarray(rb_control_frame_t *cfp, VALUE ary, rb_num_t num, int flag)
+vm_expandarray(VALUE *sp, VALUE ary, rb_num_t num, int flag)
{
int is_splat = flag & 0x01;
rb_num_t space_size = num + is_splat;
- VALUE *base = cfp->sp;
+ VALUE *base = sp - 1;
const VALUE *ptr;
rb_num_t len;
+ const VALUE obj = ary;
- if (!RB_TYPE_P(ary, T_ARRAY)) {
- ary = rb_ary_to_ary(ary);
+ if (!RB_TYPE_P(ary, T_ARRAY) && NIL_P(ary = rb_check_array_type(ary))) {
+ ary = obj;
+ ptr = &ary;
+ len = 1;
+ }
+ else {
+ ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
+ len = (rb_num_t)RARRAY_LEN(ary);
}
- cfp->sp += space_size;
-
- ptr = RARRAY_CONST_PTR(ary);
- len = (rb_num_t)RARRAY_LEN(ary);
-
- if (flag & 0x02) {
+ if (space_size == 0) {
+ /* no space left on stack */
+ }
+ else if (flag & 0x02) {
/* post: ..., nil ,ary[-1], ..., ary[0..-num] # top */
rb_num_t i = 0, j;
@@ -1212,28 +1315,41 @@ vm_expandarray(rb_control_frame_t *cfp, VALUE ary, rb_num_t num, int flag)
RB_GC_GUARD(ary);
}
-static VALUE vm_call_general(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
+static VALUE vm_call_general(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
+
+MJIT_FUNC_EXPORTED void
+rb_vm_search_method_slowpath(const struct rb_call_info *ci, struct rb_call_cache *cc, VALUE klass)
+{
+ cc->me = rb_callable_method_entry(klass, ci->mid);
+ VM_ASSERT(callable_method_entry_p(cc->me));
+ cc->call = vm_call_general;
+#if OPT_INLINE_METHOD_CACHE
+ cc->method_state = GET_GLOBAL_METHOD_STATE();
+ cc->class_serial = RCLASS_SERIAL(klass);
+#endif
+}
static void
vm_search_method(const struct rb_call_info *ci, struct rb_call_cache *cc, VALUE recv)
{
VALUE klass = CLASS_OF(recv);
+ VM_ASSERT(klass != Qfalse);
+ VM_ASSERT(RBASIC_CLASS(klass) == 0 || rb_obj_is_kind_of(klass, rb_cClass));
+
#if OPT_INLINE_METHOD_CACHE
- if (LIKELY(GET_GLOBAL_METHOD_STATE() == cc->method_state && RCLASS_SERIAL(klass) == cc->class_serial)) {
+ if (LIKELY(RB_DEBUG_COUNTER_INC_UNLESS(mc_global_state_miss,
+ GET_GLOBAL_METHOD_STATE() == cc->method_state) &&
+ RB_DEBUG_COUNTER_INC_UNLESS(mc_class_serial_miss,
+ RCLASS_SERIAL(klass) == cc->class_serial))) {
/* cache hit! */
VM_ASSERT(cc->call != NULL);
+ RB_DEBUG_COUNTER_INC(mc_inline_hit);
return;
}
+ RB_DEBUG_COUNTER_INC(mc_inline_miss);
#endif
-
- cc->me = rb_callable_method_entry(klass, ci->mid);
- VM_ASSERT(callable_method_entry_p(cc->me));
- cc->call = vm_call_general;
-#if OPT_INLINE_METHOD_CACHE
- cc->method_state = GET_GLOBAL_METHOD_STATE();
- cc->class_serial = RCLASS_SERIAL(klass);
-#endif
+ rb_vm_search_method_slowpath(ci, cc, klass);
}
static inline int
@@ -1248,6 +1364,43 @@ check_cfunc(const rb_callable_method_entry_t *me, VALUE (*func)())
}
}
+static inline int
+vm_method_cfunc_is(CALL_INFO ci, CALL_CACHE cc,
+ VALUE recv, VALUE (*func)())
+{
+ vm_search_method(ci, cc, recv);
+ return check_cfunc(cc->me, func);
+}
+
+static VALUE
+opt_equal_fallback(VALUE recv, VALUE obj, CALL_INFO ci, CALL_CACHE cc)
+{
+ if (vm_method_cfunc_is(ci, cc, recv, rb_obj_equal)) {
+ return recv == obj ? Qtrue : Qfalse;
+ }
+
+ return Qundef;
+}
+
+#define BUILTIN_CLASS_P(x, k) (!SPECIAL_CONST_P(x) && RBASIC_CLASS(x) == k)
+#define EQ_UNREDEFINED_P(t) BASIC_OP_UNREDEFINED_P(BOP_EQ, t##_REDEFINED_OP_FLAG)
+
+/* 1: compare by identity, 0: not applicable, -1: redefined */
+static inline int
+comparable_by_identity(VALUE recv, VALUE obj)
+{
+ if (FIXNUM_2_P(recv, obj)) {
+ return (EQ_UNREDEFINED_P(INTEGER) != 0) * 2 - 1;
+ }
+ if (FLONUM_2_P(recv, obj)) {
+ return (EQ_UNREDEFINED_P(FLOAT) != 0) * 2 - 1;
+ }
+ if (SYMBOL_P(recv) && SYMBOL_P(obj)) {
+ return (EQ_UNREDEFINED_P(SYMBOL) != 0) * 2 - 1;
+ }
+ return 0;
+}
+
static
#ifndef NO_BIG_INLINE
inline
@@ -1255,43 +1408,60 @@ inline
VALUE
opt_eq_func(VALUE recv, VALUE obj, CALL_INFO ci, CALL_CACHE cc)
{
- if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_EQ, INTEGER_REDEFINED_OP_FLAG)) {
+ switch (comparable_by_identity(recv, obj)) {
+ case 1:
return (recv == obj) ? Qtrue : Qfalse;
+ case -1:
+ goto fallback;
}
- else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_EQ, FLOAT_REDEFINED_OP_FLAG)) {
- return (recv == obj) ? Qtrue : Qfalse;
+ if (0) {
}
- else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
- if (RBASIC_CLASS(recv) == rb_cFloat &&
- RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_EQ, FLOAT_REDEFINED_OP_FLAG)) {
- double a = RFLOAT_VALUE(recv);
- double b = RFLOAT_VALUE(obj);
-
- if (isnan(a) || isnan(b)) {
- return Qfalse;
- }
- return (a == b) ? Qtrue : Qfalse;
+ else if (BUILTIN_CLASS_P(recv, rb_cFloat)) {
+ if (EQ_UNREDEFINED_P(FLOAT)) {
+ return rb_float_equal(recv, obj);
}
- else if (RBASIC_CLASS(recv) == rb_cString &&
- RBASIC_CLASS(obj) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_EQ, STRING_REDEFINED_OP_FLAG)) {
+ }
+ else if (BUILTIN_CLASS_P(recv, rb_cString)) {
+ if (EQ_UNREDEFINED_P(STRING)) {
return rb_str_equal(recv, obj);
}
}
- {
- vm_search_method(ci, cc, recv);
+ fallback:
+ return opt_equal_fallback(recv, obj, ci, cc);
+}
- if (check_cfunc(cc->me, rb_obj_equal)) {
- return recv == obj ? Qtrue : Qfalse;
+static
+#ifndef NO_BIG_INLINE
+inline
+#endif
+VALUE
+opt_eql_func(VALUE recv, VALUE obj, CALL_INFO ci, CALL_CACHE cc)
+{
+ switch (comparable_by_identity(recv, obj)) {
+ case 1:
+ return (recv == obj) ? Qtrue : Qfalse;
+ case -1:
+ goto fallback;
+ }
+ if (0) {
+ }
+ else if (BUILTIN_CLASS_P(recv, rb_cFloat)) {
+ if (EQ_UNREDEFINED_P(FLOAT)) {
+ return rb_float_eql(recv, obj);
+ }
+ }
+ else if (BUILTIN_CLASS_P(recv, rb_cString)) {
+ if (EQ_UNREDEFINED_P(STRING)) {
+ return rb_str_eql(recv, obj);
}
}
- return Qundef;
+ fallback:
+ return opt_equal_fallback(recv, obj, ci, cc);
}
+#undef BUILTIN_CLASS_P
+#undef EQ_UNREDEFINED_P
VALUE
rb_equal_opt(VALUE obj1, VALUE obj2)
@@ -1306,10 +1476,23 @@ rb_equal_opt(VALUE obj1, VALUE obj2)
return opt_eq_func(obj1, obj2, &ci, &cc);
}
-static VALUE vm_call0(rb_thread_t*, VALUE, ID, int, const VALUE*, const rb_callable_method_entry_t *);
+VALUE
+rb_eql_opt(VALUE obj1, VALUE obj2)
+{
+ struct rb_call_info ci;
+ struct rb_call_cache cc;
+
+ ci.mid = idEqlP;
+ cc.method_state = 0;
+ cc.class_serial = 0;
+ cc.me = NULL;
+ return opt_eql_func(obj1, obj2, &ci, &cc);
+}
+
+extern VALUE rb_vm_call0(rb_execution_context_t *ec, VALUE, ID, int, const VALUE*, const rb_callable_method_entry_t *);
static VALUE
-check_match(VALUE pattern, VALUE target, enum vm_check_match_type type)
+check_match(rb_execution_context_t *ec, VALUE pattern, VALUE target, enum vm_check_match_type type)
{
switch (type) {
case VM_CHECKMATCH_TYPE_WHEN:
@@ -1320,9 +1503,10 @@ check_match(VALUE pattern, VALUE target, enum vm_check_match_type type)
}
/* fall through */
case VM_CHECKMATCH_TYPE_CASE: {
- const rb_callable_method_entry_t *me = rb_callable_method_entry_with_refinements(CLASS_OF(pattern), idEqq);
+ const rb_callable_method_entry_t *me =
+ rb_callable_method_entry_with_refinements(CLASS_OF(pattern), idEqq, NULL);
if (me) {
- return vm_call0(GET_THREAD(), pattern, idEqq, 1, &target, me);
+ return rb_vm_call0(ec, pattern, idEqq, 1, &target, me);
}
else {
/* fallback to funcall (e.g. method_missing) */
@@ -1383,8 +1567,8 @@ vm_base_ptr(const rb_control_frame_t *cfp)
#if VM_DEBUG_BP_CHECK
if (bp != cfp->bp_check) {
fprintf(stderr, "bp_check: %ld, bp: %ld\n",
- (long)(cfp->bp_check - GET_THREAD()->stack),
- (long)(bp - GET_THREAD()->stack));
+ (long)(cfp->bp_check - GET_EC()->vm_stack),
+ (long)(bp - GET_EC()->vm_stack));
rb_bug("vm_base_ptr: unreachable");
}
#endif
@@ -1399,46 +1583,33 @@ vm_base_ptr(const rb_control_frame_t *cfp)
#include "vm_args.c"
-static inline VALUE vm_call_iseq_setup_2(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc, int param_size, int local_size);
-static inline VALUE vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc, int param_size, int local_size);
-static inline VALUE vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc);
-static VALUE vm_call_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
-static VALUE vm_call_method_nome(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
-static VALUE vm_call_method_each_type(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
-static inline VALUE vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
+static inline VALUE vm_call_iseq_setup_2(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc, int param_size, int local_size);
+ALWAYS_INLINE(static VALUE vm_call_iseq_setup_normal(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const rb_callable_method_entry_t *me, int opt_pc, int param_size, int local_size));
+static inline VALUE vm_call_iseq_setup_tailcall(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc);
+static VALUE vm_call_super_method(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
+static VALUE vm_call_method_nome(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
+static VALUE vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
+static inline VALUE vm_call_method(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
static vm_call_handler vm_call_iseq_setup_func(const struct rb_call_info *ci, const int param_size, const int local_size);
-static rb_method_definition_t *method_definition_create(rb_method_type_t type, ID mid);
-static void method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def, void *opts);
-static int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2);
-
-static const rb_iseq_t *
-def_iseq_ptr(rb_method_definition_t *def)
-{
-#if VM_CHECK_MODE > 0
- if (def->type != VM_METHOD_TYPE_ISEQ) rb_bug("def_iseq_ptr: not iseq (%d)", def->type);
-#endif
- return rb_iseq_check(def->body.iseq.iseqptr);
-}
-
static VALUE
-vm_call_iseq_setup_tailcall_0start(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+vm_call_iseq_setup_tailcall_0start(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
{
- return vm_call_iseq_setup_tailcall(th, cfp, calling, ci, cc, 0);
+ return vm_call_iseq_setup_tailcall(ec, cfp, calling, ci, cc, 0);
}
static VALUE
-vm_call_iseq_setup_normal_0start(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+vm_call_iseq_setup_normal_0start(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
{
const rb_iseq_t *iseq = def_iseq_ptr(cc->me->def);
int param = iseq->body->param.size;
int local = iseq->body->local_table_size;
- return vm_call_iseq_setup_normal(th, cfp, calling, ci, cc, 0, param, local);
+ return vm_call_iseq_setup_normal(ec, cfp, calling, cc->me, 0, param, local);
}
-static inline int
-simple_iseq_p(const rb_iseq_t *iseq)
+MJIT_STATIC int
+rb_simple_iseq_p(const rb_iseq_t *iseq)
{
return iseq->body->param.flags.has_opt == FALSE &&
iseq->body->param.flags.has_rest == FALSE &&
@@ -1449,70 +1620,69 @@ simple_iseq_p(const rb_iseq_t *iseq)
}
static inline int
-vm_callee_setup_arg(rb_thread_t *th, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc,
+vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc,
const rb_iseq_t *iseq, VALUE *argv, int param_size, int local_size)
{
- if (LIKELY(simple_iseq_p(iseq))) {
- rb_control_frame_t *cfp = th->cfp;
+ if (LIKELY(rb_simple_iseq_p(iseq) && !(ci->flag & VM_CALL_KW_SPLAT))) {
+ rb_control_frame_t *cfp = ec->cfp;
CALLER_SETUP_ARG(cfp, calling, ci); /* splat arg */
if (calling->argc != iseq->body->param.lead_num) {
- argument_arity_error(th, iseq, calling->argc, iseq->body->param.lead_num, iseq->body->param.lead_num);
+ argument_arity_error(ec, iseq, calling->argc, iseq->body->param.lead_num, iseq->body->param.lead_num);
}
- CI_SET_FASTPATH(cc, vm_call_iseq_setup_func(ci, param_size, local_size),
+ CC_SET_FASTPATH(cc, vm_call_iseq_setup_func(ci, param_size, local_size),
(!IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) &&
!(METHOD_ENTRY_VISI(cc->me) == METHOD_VISI_PROTECTED)));
return 0;
}
else {
- return setup_parameters_complex(th, iseq, calling, ci, argv, arg_setup_method);
+ return setup_parameters_complex(ec, iseq, calling, ci, argv, arg_setup_method);
}
}
static VALUE
-vm_call_iseq_setup(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+vm_call_iseq_setup(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
{
const rb_iseq_t *iseq = def_iseq_ptr(cc->me->def);
const int param_size = iseq->body->param.size;
const int local_size = iseq->body->local_table_size;
- const int opt_pc = vm_callee_setup_arg(th, calling, ci, cc, def_iseq_ptr(cc->me->def), cfp->sp - calling->argc, param_size, local_size);
- return vm_call_iseq_setup_2(th, cfp, calling, ci, cc, opt_pc, param_size, local_size);
+ const int opt_pc = vm_callee_setup_arg(ec, calling, ci, cc, def_iseq_ptr(cc->me->def), cfp->sp - calling->argc, param_size, local_size);
+ return vm_call_iseq_setup_2(ec, cfp, calling, ci, cc, opt_pc, param_size, local_size);
}
static inline VALUE
-vm_call_iseq_setup_2(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc,
+vm_call_iseq_setup_2(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc,
int opt_pc, int param_size, int local_size)
{
if (LIKELY(!(ci->flag & VM_CALL_TAILCALL))) {
- return vm_call_iseq_setup_normal(th, cfp, calling, ci, cc, opt_pc, param_size, local_size);
+ return vm_call_iseq_setup_normal(ec, cfp, calling, cc->me, opt_pc, param_size, local_size);
}
else {
- return vm_call_iseq_setup_tailcall(th, cfp, calling, ci, cc, opt_pc);
+ return vm_call_iseq_setup_tailcall(ec, cfp, calling, ci, cc, opt_pc);
}
}
static inline VALUE
-vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc,
- int opt_pc, int param_size, int local_size)
+vm_call_iseq_setup_normal(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const rb_callable_method_entry_t *me,
+ int opt_pc, int param_size, int local_size)
{
- const rb_callable_method_entry_t *me = cc->me;
const rb_iseq_t *iseq = def_iseq_ptr(me->def);
VALUE *argv = cfp->sp - calling->argc;
VALUE *sp = argv + param_size;
cfp->sp = argv - 1 /* recv */;
- vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL, calling->recv,
- calling->block_handler, (VALUE)me,
- iseq->body->iseq_encoded + opt_pc, sp,
- local_size - param_size,
- iseq->body->stack_max);
+ vm_push_frame(ec, iseq, VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL, calling->recv,
+ calling->block_handler, (VALUE)me,
+ iseq->body->iseq_encoded + opt_pc, sp,
+ local_size - param_size,
+ iseq->body->stack_max);
return Qundef;
}
static inline VALUE
-vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc,
+vm_call_iseq_setup_tailcall(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc,
int opt_pc)
{
unsigned int i;
@@ -1535,10 +1705,8 @@ vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_
}
}
- vm_pop_frame(th, cfp, cfp->ep);
- cfp = th->cfp;
-
- RUBY_VM_CHECK_INTS(th);
+ vm_pop_frame(ec, cfp, cfp->ep);
+ cfp = ec->cfp;
sp_orig = sp = cfp->sp;
@@ -1551,13 +1719,15 @@ vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_
*sp++ = src_argv[i];
}
- vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL | finish_flag,
+ vm_push_frame(ec, iseq, VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL | finish_flag,
calling->recv, calling->block_handler, (VALUE)me,
iseq->body->iseq_encoded + opt_pc, sp,
iseq->body->local_table_size - iseq->body->param.size,
iseq->body->stack_max);
cfp->sp = sp_orig;
+ RUBY_VM_CHECK_INTS(ec);
+
return Qundef;
}
@@ -1669,34 +1839,21 @@ call_cfunc_15(VALUE (*func)(ANYARGS), VALUE recv, int argc, const VALUE *argv)
return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14]);
}
-#ifndef VM_PROFILE
-#define VM_PROFILE 0
-#endif
-
-#if VM_PROFILE
-enum {
- VM_PROFILE_R2C_CALL,
- VM_PROFILE_R2C_POPF,
- VM_PROFILE_C2C_CALL,
- VM_PROFILE_C2C_POPF,
- VM_PROFILE_COUNT
-};
-static int vm_profile_counter[VM_PROFILE_COUNT];
-#define VM_PROFILE_UP(x) (vm_profile_counter[VM_PROFILE_##x]++)
-#define VM_PROFILE_ATEXIT() atexit(vm_profile_show_result)
-static void
-vm_profile_show_result(void)
+static inline int
+vm_cfp_consistent_p(rb_execution_context_t *ec, const rb_control_frame_t *reg_cfp)
{
- fprintf(stderr, "VM Profile results: \n");
- fprintf(stderr, "r->c call: %d\n", vm_profile_counter[VM_PROFILE_R2C_CALL]);
- fprintf(stderr, "r->c popf: %d\n", vm_profile_counter[VM_PROFILE_R2C_POPF]);
- fprintf(stderr, "c->c call: %d\n", vm_profile_counter[VM_PROFILE_C2C_CALL]);
- fprintf(stderr, "c->c popf: %d\n", vm_profile_counter[VM_PROFILE_C2C_POPF]);
+ const int ov_flags = RAISED_STACKOVERFLOW;
+ if (LIKELY(reg_cfp == ec->cfp + 1)) return TRUE;
+ if (rb_ec_raised_p(ec, ov_flags)) {
+ rb_ec_raised_reset(ec, ov_flags);
+ return TRUE;
+ }
+ return FALSE;
}
-#else
-#define VM_PROFILE_UP(x)
-#define VM_PROFILE_ATEXIT()
-#endif
+
+#define CHECK_CFP_CONSISTENCY(func) \
+ (LIKELY(vm_cfp_consistent_p(ec, reg_cfp)) ? (void)0 : \
+ rb_bug(func ": cfp consistency error (%p, %p)", (void *)reg_cfp, (void *)(ec->cfp+1)))
static inline
const rb_method_cfunc_t *
@@ -1727,7 +1884,7 @@ vm_method_cfunc_entry(const rb_callable_method_entry_t *me)
}
static VALUE
-vm_call_cfunc_with_frame(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+vm_call_cfunc_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
{
VALUE val;
const rb_callable_method_entry_t *me = cc->me;
@@ -1738,125 +1895,44 @@ vm_call_cfunc_with_frame(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb
VALUE block_handler = calling->block_handler;
int argc = calling->argc;
- RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, me->owner, me->def->original_id);
- EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->def->original_id, ci->mid, me->owner, Qundef);
+ RUBY_DTRACE_CMETHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id);
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, recv, me->def->original_id, ci->mid, me->owner, Qundef);
- vm_push_frame(th, NULL, VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL, recv,
+ vm_push_frame(ec, NULL, VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL, recv,
block_handler, (VALUE)me,
- 0, th->cfp->sp, 0, 0);
+ 0, ec->cfp->sp, 0, 0);
if (len >= 0) rb_check_arity(argc, len, len);
reg_cfp->sp -= argc + 1;
- VM_PROFILE_UP(R2C_CALL);
val = (*cfunc->invoker)(cfunc->func, recv, argc, reg_cfp->sp + 1);
- if (reg_cfp != th->cfp + 1) {
- rb_bug("vm_call_cfunc - cfp consistency error");
- }
-
- rb_vm_pop_frame(th);
-
- EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->def->original_id, ci->mid, me->owner, val);
- RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, me->owner, me->def->original_id);
-
- return val;
-}
-
-#if OPT_CALL_CFUNC_WITHOUT_FRAME
-static VALUE
-vm_call_cfunc_latter(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling)
-{
- VALUE val;
- int argc = calling->argc;
- VALUE *argv = STACK_ADDR_FROM_TOP(argc);
- VALUE recv = calling->recv;
- const rb_method_cfunc_t *cfunc = vm_method_cfunc_entry(cc->me);
-
- th->passed_calling = calling;
- reg_cfp->sp -= argc + 1;
- ci->aux.inc_sp = argc + 1;
- VM_PROFILE_UP(R2C_CALL);
- val = (*cfunc->invoker)(cfunc->func, recv, argc, argv);
-
- /* check */
- if (reg_cfp == th->cfp) { /* no frame push */
- if (UNLIKELY(th->passed_ci != ci)) {
- rb_bug("vm_call_cfunc_latter: passed_ci error (ci: %p, passed_ci: %p)", ci, th->passed_ci);
- }
- th->passed_ci = 0;
- }
- else {
- if (UNLIKELY(reg_cfp != RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp))) {
- rb_bug("vm_call_cfunc_latter: cfp consistency error (%p, %p)", reg_cfp, th->cfp+1);
- }
- vm_pop_frame(th, reg_cfp, reg_cfp->ep);
- VM_PROFILE_UP(R2C_POPF);
- }
-
- return val;
-}
-
-static VALUE
-vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci)
-{
- VALUE val;
- const rb_callable_method_entry_t *me = cc->me;
- int len = vm_method_cfunc_entry(me)->argc;
- VALUE recv = calling->recv;
-
- CALLER_SETUP_ARG(reg_cfp, calling, ci);
- if (len >= 0) rb_check_arity(calling->argc, len, len);
-
- RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, me->owner, me->called_id);
- EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->owner, Qnil);
+ CHECK_CFP_CONSISTENCY("vm_call_cfunc");
- if (!(cc->me->def->flag & METHOD_VISI_PROTECTED) &&
- !(ci->flag & VM_CALL_ARGS_SPLAT) &&
- !(ci->kw_arg != NULL)) {
- CI_SET_FASTPATH(cc, vm_call_cfunc_latter, 1);
- }
- val = vm_call_cfunc_latter(th, reg_cfp, calling);
+ rb_vm_pop_frame(ec);
- EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->owner, val);
- RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, me->owner, me->called_id);
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, recv, me->def->original_id, ci->mid, me->owner, val);
+ RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, me->owner, me->def->original_id);
return val;
}
-void
-rb_vm_call_cfunc_push_frame(rb_thread_t *th)
-{
- struct rb_calling_info *calling = th->passed_calling;
- const rb_callable_method_entry_t *me = calling->me;
- th->passed_ci = 0;
-
- vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL,
- calling->recv, calling->block_handler, (VALUE)me /* cref */,
- 0, th->cfp->sp + cc->aux.inc_sp, 0, 0);
-
- if (calling->call != vm_call_general) {
- calling->call = vm_call_cfunc_with_frame;
- }
-}
-#else /* OPT_CALL_CFUNC_WITHOUT_FRAME */
static VALUE
-vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+vm_call_cfunc(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
{
CALLER_SETUP_ARG(reg_cfp, calling, ci);
- return vm_call_cfunc_with_frame(th, reg_cfp, calling, ci, cc);
+ return vm_call_cfunc_with_frame(ec, reg_cfp, calling, ci, cc);
}
-#endif
static VALUE
-vm_call_ivar(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+vm_call_ivar(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
{
cfp->sp -= 1;
- return vm_getivar(calling->recv, cc->me->def->body.attr.id, NULL, cc, 1);
+ return vm_getivar(calling->recv, cc->me->def->body.attr.id, NULL, cc, TRUE);
}
static VALUE
-vm_call_attrset(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+vm_call_attrset(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
{
VALUE val = *(cfp->sp - 1);
cfp->sp -= 2;
@@ -1864,21 +1940,20 @@ vm_call_attrset(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info
}
static inline VALUE
-vm_call_bmethod_body(rb_thread_t *th, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, const VALUE *argv)
+vm_call_bmethod_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, const VALUE *argv)
{
rb_proc_t *proc;
VALUE val;
/* control block frame */
- th->passed_bmethod_me = cc->me;
- GetProcPtr(cc->me->def->body.proc, proc);
- val = vm_invoke_bmethod(th, proc, calling->recv, calling->argc, argv, calling->block_handler);
+ GetProcPtr(cc->me->def->body.bmethod.proc, proc);
+ val = rb_vm_invoke_bmethod(ec, proc, calling->recv, calling->argc, argv, calling->block_handler, cc->me);
return val;
}
static VALUE
-vm_call_bmethod(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+vm_call_bmethod(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
{
VALUE *argv;
int argc;
@@ -1889,7 +1964,7 @@ vm_call_bmethod(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info
MEMCPY(argv, cfp->sp - argc, VALUE, argc);
cfp->sp += - argc - 1;
- return vm_call_bmethod_body(th, calling, ci, cc, argv);
+ return vm_call_bmethod_body(ec, calling, ci, cc, argv);
}
static enum method_missing_reason
@@ -1903,7 +1978,7 @@ ci_missing_reason(const struct rb_call_info *ci)
}
static VALUE
-vm_call_opt_send(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *orig_ci, struct rb_call_cache *orig_cc)
+vm_call_opt_send(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *orig_ci, struct rb_call_cache *orig_cc)
{
int i;
VALUE sym;
@@ -1938,14 +2013,15 @@ vm_call_opt_send(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling
if (!(ci->mid = rb_check_id(&sym))) {
if (rb_method_basic_definition_p(CLASS_OF(calling->recv), idMethodMissing)) {
- VALUE exc = make_no_method_exception(rb_eNoMethodError, 0, calling->recv,
- rb_long2int(calling->argc), &TOPN(i),
- ci->flag & (VM_CALL_FCALL|VM_CALL_VCALL));
+ VALUE exc =
+ rb_make_no_method_exception(rb_eNoMethodError, 0, calling->recv,
+ rb_long2int(calling->argc), &TOPN(i),
+ ci->flag & (VM_CALL_FCALL|VM_CALL_VCALL));
rb_exc_raise(exc);
}
TOPN(i) = rb_str_intern(sym);
ci->mid = idMethodMissing;
- th->method_missing_reason = cc->aux.method_missing_reason = ci_missing_reason(ci);
+ ec->method_missing_reason = cc->aux.method_missing_reason = ci_missing_reason(ci);
}
else {
/* shift arguments */
@@ -1956,31 +2032,54 @@ vm_call_opt_send(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling
DEC_SP(1);
}
- cc->me = rb_callable_method_entry_with_refinements(CLASS_OF(calling->recv), ci->mid);
+ cc->me = rb_callable_method_entry_with_refinements(CLASS_OF(calling->recv), ci->mid, NULL);
ci->flag = VM_CALL_FCALL | VM_CALL_OPT_SEND;
- return vm_call_method(th, reg_cfp, calling, ci, cc);
+ return vm_call_method(ec, reg_cfp, calling, ci, cc);
}
+static inline VALUE vm_invoke_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, VALUE block_handler);
+
+NOINLINE(static VALUE
+ vm_invoke_block_opt_call(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
+ struct rb_calling_info *calling, const struct rb_call_info *ci, VALUE block_handler));
+
static VALUE
-vm_call_opt_call(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+vm_invoke_block_opt_call(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
+ struct rb_calling_info *calling, const struct rb_call_info *ci, VALUE block_handler)
{
- rb_proc_t *proc;
- int argc;
- VALUE *argv;
+ int argc = calling->argc;
- CALLER_SETUP_ARG(cfp, calling, ci);
+ /* remove self */
+ if (argc > 0) MEMMOVE(&TOPN(argc), &TOPN(argc-1), VALUE, argc);
+ DEC_SP(1);
- argc = calling->argc;
- argv = ALLOCA_N(VALUE, argc);
- GetProcPtr(calling->recv, proc);
- MEMCPY(argv, cfp->sp - argc, VALUE, argc);
- cfp->sp -= argc + 1;
+ return vm_invoke_block(ec, reg_cfp, calling, ci, block_handler);
+}
+
+static VALUE
+vm_call_opt_call(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+{
+ VALUE procval = calling->recv;
+ return vm_invoke_block_opt_call(ec, reg_cfp, calling, ci, VM_BH_FROM_PROC(procval));
+}
- return rb_vm_invoke_proc(th, proc, argc, argv, calling->block_handler);
+static VALUE
+vm_call_opt_block_call(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+{
+ VALUE block_handler = VM_ENV_BLOCK_HANDLER(VM_CF_LEP(reg_cfp));
+
+ if (BASIC_OP_UNREDEFINED_P(BOP_CALL, PROC_REDEFINED_OP_FLAG)) {
+ return vm_invoke_block_opt_call(ec, reg_cfp, calling, ci, block_handler);
+ }
+ else {
+ calling->recv = rb_vm_bh_to_procval(ec, block_handler);
+ vm_search_method(ci, cc, calling->recv);
+ return vm_call_general(ec, reg_cfp, calling, ci, cc);
+ }
}
static VALUE
-vm_call_method_missing(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *orig_ci, struct rb_call_cache *orig_cc)
+vm_call_method_missing(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *orig_ci, struct rb_call_cache *orig_cc)
{
VALUE *argv = STACK_ADDR_FROM_TOP(calling->argc);
struct rb_call_info ci_entry;
@@ -1999,7 +2098,7 @@ vm_call_method_missing(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_c
cc_entry = *orig_cc;
cc_entry.me =
rb_callable_method_entry_without_refinements(CLASS_OF(calling->recv),
- idMethodMissing);
+ idMethodMissing, NULL);
cc = &cc_entry;
calling->argc = argc;
@@ -2012,22 +2111,25 @@ vm_call_method_missing(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_c
argv[0] = ID2SYM(orig_ci->mid);
INC_SP(1);
- th->method_missing_reason = orig_cc->aux.method_missing_reason;
- return vm_call_method(th, reg_cfp, calling, ci, cc);
+ ec->method_missing_reason = orig_cc->aux.method_missing_reason;
+ return vm_call_method(ec, reg_cfp, calling, ci, cc);
}
+static const rb_callable_method_entry_t *refined_method_callable_without_refinement(const rb_callable_method_entry_t *me);
static VALUE
-vm_call_zsuper(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, VALUE klass)
+vm_call_zsuper(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, VALUE klass)
{
klass = RCLASS_SUPER(klass);
cc->me = klass ? rb_callable_method_entry(klass, ci->mid) : NULL;
- if (cc->me != NULL) {
- return vm_call_method_each_type(th, cfp, calling, ci, cc);
+ if (!cc->me) {
+ return vm_call_method_nome(ec, cfp, calling, ci, cc);
}
- else {
- return vm_call_method_nome(th, cfp, calling, ci, cc);
+ if (cc->me->def->type == VM_METHOD_TYPE_REFINED &&
+ cc->me->def->body.refined.orig_me) {
+ cc->me = refined_method_callable_without_refinement(cc->me);
}
+ return vm_call_method_each_type(ec, cfp, calling, ci, cc);
}
static inline VALUE
@@ -2039,9 +2141,9 @@ find_refinement(VALUE refinements, VALUE klass)
return rb_hash_lookup(refinements, klass);
}
-PUREFUNC(static rb_control_frame_t * current_method_entry(rb_thread_t *th, rb_control_frame_t *cfp));
+PUREFUNC(static rb_control_frame_t * current_method_entry(const rb_execution_context_t *ec, rb_control_frame_t *cfp));
static rb_control_frame_t *
-current_method_entry(rb_thread_t *th, rb_control_frame_t *cfp)
+current_method_entry(const rb_execution_context_t *ec, rb_control_frame_t *cfp)
{
rb_control_frame_t *top_cfp = cfp;
@@ -2050,7 +2152,7 @@ current_method_entry(rb_thread_t *th, rb_control_frame_t *cfp)
do {
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
- if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp)) {
+ if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) {
/* TODO: orphan block */
return top_cfp;
}
@@ -2059,8 +2161,8 @@ current_method_entry(rb_thread_t *th, rb_control_frame_t *cfp)
return cfp;
}
-static VALUE
-find_defined_class_by_owner(VALUE current_class, VALUE target_owner)
+MJIT_FUNC_EXPORTED VALUE
+rb_find_defined_class_by_owner(VALUE current_class, VALUE target_owner)
{
VALUE klass = current_class;
@@ -2085,7 +2187,7 @@ aliased_callable_method_entry(const rb_callable_method_entry_t *me)
const rb_callable_method_entry_t *cme;
if (orig_me->defined_class == 0) {
- VALUE defined_class = find_defined_class_by_owner(me->defined_class, orig_me->owner);
+ VALUE defined_class = rb_find_defined_class_by_owner(me->defined_class, orig_me->owner);
VM_ASSERT(RB_TYPE_P(orig_me->owner, T_MODULE));
cme = rb_method_entry_complement_defined_class(orig_me, me->called_id, defined_class);
@@ -2093,9 +2195,9 @@ aliased_callable_method_entry(const rb_callable_method_entry_t *me)
RB_OBJ_WRITE(me, &me->def->body.alias.original_me, cme);
}
else {
- method_definition_set((rb_method_entry_t *)me,
- method_definition_create(VM_METHOD_TYPE_ALIAS, me->def->original_id),
- (void *)cme);
+ rb_method_definition_t *def =
+ rb_method_definition_create(VM_METHOD_TYPE_ALIAS, me->def->original_id);
+ rb_method_definition_set((rb_method_entry_t *)me, def, (void *)cme);
}
}
else {
@@ -2121,58 +2223,66 @@ refined_method_callable_without_refinement(const rb_callable_method_entry_t *me)
}
VM_ASSERT(callable_method_entry_p(cme));
+
+ if (UNDEFINED_METHOD_ENTRY_P(cme)) {
+ cme = NULL;
+ }
+
return cme;
}
static VALUE
-vm_call_method_each_type(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
{
switch (cc->me->def->type) {
case VM_METHOD_TYPE_ISEQ:
- CI_SET_FASTPATH(cc, vm_call_iseq_setup, TRUE);
- return vm_call_iseq_setup(th, cfp, calling, ci, cc);
+ CC_SET_FASTPATH(cc, vm_call_iseq_setup, TRUE);
+ return vm_call_iseq_setup(ec, cfp, calling, ci, cc);
case VM_METHOD_TYPE_NOTIMPLEMENTED:
case VM_METHOD_TYPE_CFUNC:
- CI_SET_FASTPATH(cc, vm_call_cfunc, TRUE);
- return vm_call_cfunc(th, cfp, calling, ci, cc);
+ CC_SET_FASTPATH(cc, vm_call_cfunc, TRUE);
+ return vm_call_cfunc(ec, cfp, calling, ci, cc);
case VM_METHOD_TYPE_ATTRSET:
CALLER_SETUP_ARG(cfp, calling, ci);
rb_check_arity(calling->argc, 1, 1);
cc->aux.index = 0;
- CI_SET_FASTPATH(cc, vm_call_attrset, !((ci->flag & VM_CALL_ARGS_SPLAT) || (ci->flag & VM_CALL_KWARG)));
- return vm_call_attrset(th, cfp, calling, ci, cc);
+ CC_SET_FASTPATH(cc, vm_call_attrset, !((ci->flag & VM_CALL_ARGS_SPLAT) || (ci->flag & VM_CALL_KWARG)));
+ return vm_call_attrset(ec, cfp, calling, ci, cc);
case VM_METHOD_TYPE_IVAR:
CALLER_SETUP_ARG(cfp, calling, ci);
rb_check_arity(calling->argc, 0, 0);
cc->aux.index = 0;
- CI_SET_FASTPATH(cc, vm_call_ivar, !(ci->flag & VM_CALL_ARGS_SPLAT));
- return vm_call_ivar(th, cfp, calling, ci, cc);
+ CC_SET_FASTPATH(cc, vm_call_ivar, !(ci->flag & VM_CALL_ARGS_SPLAT));
+ return vm_call_ivar(ec, cfp, calling, ci, cc);
case VM_METHOD_TYPE_MISSING:
cc->aux.method_missing_reason = 0;
- CI_SET_FASTPATH(cc, vm_call_method_missing, TRUE);
- return vm_call_method_missing(th, cfp, calling, ci, cc);
+ CC_SET_FASTPATH(cc, vm_call_method_missing, TRUE);
+ return vm_call_method_missing(ec, cfp, calling, ci, cc);
case VM_METHOD_TYPE_BMETHOD:
- CI_SET_FASTPATH(cc, vm_call_bmethod, TRUE);
- return vm_call_bmethod(th, cfp, calling, ci, cc);
+ CC_SET_FASTPATH(cc, vm_call_bmethod, TRUE);
+ return vm_call_bmethod(ec, cfp, calling, ci, cc);
case VM_METHOD_TYPE_ALIAS:
cc->me = aliased_callable_method_entry(cc->me);
VM_ASSERT(cc->me != NULL);
- return vm_call_method_each_type(th, cfp, calling, ci, cc);
+ return vm_call_method_each_type(ec, cfp, calling, ci, cc);
case VM_METHOD_TYPE_OPTIMIZED:
switch (cc->me->def->body.optimize_type) {
case OPTIMIZED_METHOD_TYPE_SEND:
- CI_SET_FASTPATH(cc, vm_call_opt_send, TRUE);
- return vm_call_opt_send(th, cfp, calling, ci, cc);
+ CC_SET_FASTPATH(cc, vm_call_opt_send, TRUE);
+ return vm_call_opt_send(ec, cfp, calling, ci, cc);
case OPTIMIZED_METHOD_TYPE_CALL:
- CI_SET_FASTPATH(cc, vm_call_opt_call, TRUE);
- return vm_call_opt_call(th, cfp, calling, ci, cc);
+ CC_SET_FASTPATH(cc, vm_call_opt_call, TRUE);
+ return vm_call_opt_call(ec, cfp, calling, ci, cc);
+ case OPTIMIZED_METHOD_TYPE_BLOCK_CALL:
+ CC_SET_FASTPATH(cc, vm_call_opt_block_call, TRUE);
+ return vm_call_opt_block_call(ec, cfp, calling, ci, cc);
default:
rb_bug("vm_call_method: unsupported optimized method type (%d)",
cc->me->def->body.optimize_type);
@@ -2182,59 +2292,61 @@ vm_call_method_each_type(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_cal
break;
case VM_METHOD_TYPE_ZSUPER:
- return vm_call_zsuper(th, cfp, calling, ci, cc, RCLASS_ORIGIN(cc->me->owner));
+ return vm_call_zsuper(ec, cfp, calling, ci, cc, RCLASS_ORIGIN(cc->me->defined_class));
case VM_METHOD_TYPE_REFINED: {
- const rb_cref_t *cref = rb_vm_get_cref(cfp->ep);
- VALUE refinements = cref ? CREF_REFINEMENTS(cref) : Qnil;
- VALUE refinement;
- const rb_callable_method_entry_t *ref_me;
-
- refinement = find_refinement(refinements, cc->me->owner);
-
- if (NIL_P(refinement)) {
- goto no_refinement_dispatch;
- }
- ref_me = rb_callable_method_entry(refinement, ci->mid);
-
- if (ref_me) {
- if (cc->call == vm_call_super_method) {
- const rb_control_frame_t *top_cfp = current_method_entry(th, cfp);
- const rb_callable_method_entry_t *top_me = rb_vm_frame_method_entry(top_cfp);
- if (top_me && rb_method_definition_eq(ref_me->def, top_me->def)) {
- goto no_refinement_dispatch;
- }
- }
- cc->me = ref_me;
- if (ref_me->def->type != VM_METHOD_TYPE_REFINED) {
- return vm_call_method(th, cfp, calling, ci, cc);
- }
- }
- else {
- cc->me = NULL;
- return vm_call_method_nome(th, cfp, calling, ci, cc);
- }
-
- no_refinement_dispatch:
- if (cc->me->def->body.refined.orig_me) {
- cc->me = refined_method_callable_without_refinement(cc->me);
-
- if (UNDEFINED_METHOD_ENTRY_P(cc->me)) {
- cc->me = NULL;
- }
- return vm_call_method(th, cfp, calling, ci, cc);
- }
- else {
- return vm_call_zsuper(th, cfp, calling, ci, cc, cc->me->owner);
- }
+ const rb_cref_t *cref = rb_vm_get_cref(cfp->ep);
+ VALUE refinements = cref ? CREF_REFINEMENTS(cref) : Qnil;
+ VALUE refinement;
+ const rb_callable_method_entry_t *ref_me;
+
+ refinement = find_refinement(refinements, cc->me->owner);
+
+ if (NIL_P(refinement)) {
+ goto no_refinement_dispatch;
+ }
+ ref_me = rb_callable_method_entry(refinement, ci->mid);
+
+ if (ref_me) {
+ if (cc->call == vm_call_super_method) {
+ const rb_control_frame_t *top_cfp = current_method_entry(ec, cfp);
+ const rb_callable_method_entry_t *top_me = rb_vm_frame_method_entry(top_cfp);
+ if (top_me && rb_method_definition_eq(ref_me->def, top_me->def)) {
+ goto no_refinement_dispatch;
+ }
+ }
+ if (cc->me->def->type != VM_METHOD_TYPE_REFINED ||
+ cc->me->def != ref_me->def) {
+ cc->me = ref_me;
+ }
+ if (ref_me->def->type != VM_METHOD_TYPE_REFINED) {
+ return vm_call_method(ec, cfp, calling, ci, cc);
+ }
+ }
+ else {
+ cc->me = NULL;
+ return vm_call_method_nome(ec, cfp, calling, ci, cc);
+ }
+
+ no_refinement_dispatch:
+ if (cc->me->def->body.refined.orig_me) {
+ cc->me = refined_method_callable_without_refinement(cc->me);
+ }
+ else {
+ VALUE klass = RCLASS_SUPER(cc->me->defined_class);
+ cc->me = klass ? rb_callable_method_entry(klass, ci->mid) : NULL;
+ }
+ return vm_call_method(ec, cfp, calling, ci, cc);
}
}
rb_bug("vm_call_method: unsupported method type (%d)", cc->me->def->type);
}
+NORETURN(static void vm_raise_method_missing(rb_execution_context_t *ec, int argc, const VALUE *argv, VALUE obj, int call_status));
+
static VALUE
-vm_call_method_nome(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+vm_call_method_nome(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
{
/* method missing */
const int stat = ci_missing_reason(ci);
@@ -2242,24 +2354,24 @@ vm_call_method_nome(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_
if (ci->mid == idMethodMissing) {
rb_control_frame_t *reg_cfp = cfp;
VALUE *argv = STACK_ADDR_FROM_TOP(calling->argc);
- rb_raise_method_missing(th, calling->argc, argv, calling->recv, stat);
+ vm_raise_method_missing(ec, calling->argc, argv, calling->recv, stat);
}
else {
cc->aux.method_missing_reason = stat;
- CI_SET_FASTPATH(cc, vm_call_method_missing, 1);
- return vm_call_method_missing(th, cfp, calling, ci, cc);
+ CC_SET_FASTPATH(cc, vm_call_method_missing, TRUE);
+ return vm_call_method_missing(ec, cfp, calling, ci, cc);
}
}
static inline VALUE
-vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+vm_call_method(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
{
VM_ASSERT(callable_method_entry_p(cc->me));
if (cc->me != NULL) {
switch (METHOD_ENTRY_VISI(cc->me)) {
case METHOD_VISI_PUBLIC: /* likely */
- return vm_call_method_each_type(th, cfp, calling, ci, cc);
+ return vm_call_method_each_type(ec, cfp, calling, ci, cc);
case METHOD_VISI_PRIVATE:
if (!(ci->flag & VM_CALL_FCALL)) {
@@ -2267,16 +2379,16 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info
if (ci->flag & VM_CALL_VCALL) stat |= MISSING_VCALL;
cc->aux.method_missing_reason = stat;
- CI_SET_FASTPATH(cc, vm_call_method_missing, 1);
- return vm_call_method_missing(th, cfp, calling, ci, cc);
+ CC_SET_FASTPATH(cc, vm_call_method_missing, TRUE);
+ return vm_call_method_missing(ec, cfp, calling, ci, cc);
}
- return vm_call_method_each_type(th, cfp, calling, ci, cc);
+ return vm_call_method_each_type(ec, cfp, calling, ci, cc);
case METHOD_VISI_PROTECTED:
if (!(ci->flag & VM_CALL_OPT_SEND)) {
if (!rb_obj_is_kind_of(cfp->self, cc->me->defined_class)) {
cc->aux.method_missing_reason = MISSING_PROTECTED;
- return vm_call_method_missing(th, cfp, calling, ci, cc);
+ return vm_call_method_missing(ec, cfp, calling, ci, cc);
}
else {
/* caching method info to dummy cc */
@@ -2285,32 +2397,32 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info
cc = &cc_entry;
VM_ASSERT(cc->me != NULL);
- return vm_call_method_each_type(th, cfp, calling, ci, cc);
+ return vm_call_method_each_type(ec, cfp, calling, ci, cc);
}
}
- return vm_call_method_each_type(th, cfp, calling, ci, cc);
+ return vm_call_method_each_type(ec, cfp, calling, ci, cc);
default:
rb_bug("unreachable");
}
}
else {
- return vm_call_method_nome(th, cfp, calling, ci, cc);
+ return vm_call_method_nome(ec, cfp, calling, ci, cc);
}
}
static VALUE
-vm_call_general(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+vm_call_general(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
{
- return vm_call_method(th, reg_cfp, calling, ci, cc);
+ return vm_call_method(ec, reg_cfp, calling, ci, cc);
}
static VALUE
-vm_call_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+vm_call_super_method(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
{
/* this check is required to distinguish with other functions. */
if (cc->call != vm_call_super_method) rb_bug("bug");
- return vm_call_method(th, reg_cfp, calling, ci, cc);
+ return vm_call_method(ec, reg_cfp, calling, ci, cc);
}
/* super */
@@ -2326,6 +2438,8 @@ vm_search_normal_superclass(VALUE klass)
return RCLASS_SUPER(klass);
}
+NORETURN(static void vm_super_outside(void));
+
static void
vm_super_outside(void)
{
@@ -2333,11 +2447,10 @@ vm_super_outside(void)
}
static void
-vm_search_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp,
+vm_search_super_method(const rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
struct rb_calling_info *calling, struct rb_call_info *ci, struct rb_call_cache *cc)
{
VALUE current_defined_class, klass;
- VALUE sigval = TOPN(calling->argc);
const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(reg_cfp);
if (!me) {
@@ -2363,7 +2476,7 @@ vm_search_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp,
rb_obj_class(calling->recv), m);
}
- if (me->def->type == VM_METHOD_TYPE_BMETHOD && !sigval) {
+ if (me->def->type == VM_METHOD_TYPE_BMETHOD && (ci->flag & VM_CALL_ZSUPER)) {
rb_raise(rb_eRuntimeError,
"implicit argument passing of super from method defined"
" by define_method() is not supported."
@@ -2376,12 +2489,12 @@ vm_search_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp,
if (!klass) {
/* bound instance method of module */
cc->aux.method_missing_reason = MISSING_SUPER;
- CI_SET_FASTPATH(cc, vm_call_method_missing, 1);
+ CC_SET_FASTPATH(cc, vm_call_method_missing, TRUE);
}
else {
/* TODO: use inline cache */
cc->me = rb_callable_method_entry(klass, ci->mid);
- CI_SET_FASTPATH(cc, vm_call_super_method, 1);
+ CC_SET_FASTPATH(cc, vm_call_super_method, TRUE);
}
}
@@ -2402,38 +2515,14 @@ block_proc_is_lambda(const VALUE procval)
}
static VALUE
-vm_block_handler_to_proc(rb_thread_t *th, VALUE block_handler)
-{
- VALUE blockarg = Qnil;
-
- if (block_handler != VM_BLOCK_HANDLER_NONE) {
- switch (vm_block_handler_type(block_handler)) {
- case block_handler_type_proc:
- blockarg = block_handler;
- break;
- case block_handler_type_symbol:
- blockarg = rb_sym_to_proc(block_handler);
- break;
- case block_handler_type_iseq:
- case block_handler_type_ifunc:
- blockarg = rb_vm_make_proc(th, VM_BH_TO_CAPT_BLOCK(block_handler), rb_cProc);
- break;
- }
- }
-
- return blockarg;
-}
-
-static VALUE
-vm_yield_with_cfunc(rb_thread_t *th,
+vm_yield_with_cfunc(rb_execution_context_t *ec,
const struct rb_captured_block *captured,
- VALUE self, int argc, const VALUE *argv, VALUE block_handler)
+ VALUE self, int argc, const VALUE *argv, VALUE block_handler,
+ const rb_callable_method_entry_t *me)
{
int is_lambda = FALSE; /* TODO */
VALUE val, arg, blockarg;
const struct vm_ifunc *ifunc = captured->code.ifunc;
- const rb_callable_method_entry_t *me = th->passed_bmethod_me;
- th->passed_bmethod_me = NULL;
if (is_lambda) {
arg = rb_ary_new4(argc, argv);
@@ -2445,24 +2534,25 @@ vm_yield_with_cfunc(rb_thread_t *th,
arg = argv[0];
}
- blockarg = vm_block_handler_to_proc(th, block_handler);
+ blockarg = rb_vm_bh_to_procval(ec, block_handler);
- vm_push_frame(th, (const rb_iseq_t *)captured->code.ifunc,
- VM_FRAME_MAGIC_IFUNC | VM_FRAME_FLAG_CFRAME,
+ vm_push_frame(ec, (const rb_iseq_t *)captured->code.ifunc,
+ VM_FRAME_MAGIC_IFUNC | VM_FRAME_FLAG_CFRAME |
+ (me ? VM_FRAME_FLAG_BMETHOD : 0),
self,
VM_GUARDED_PREV_EP(captured->ep),
- (VALUE)me,
- 0, th->cfp->sp, 0, 0);
+ (VALUE)me,
+ 0, ec->cfp->sp, 0, 0);
val = (*ifunc->func)(arg, ifunc->data, argc, argv, blockarg);
- rb_vm_pop_frame(th);
+ rb_vm_pop_frame(ec);
return val;
}
static VALUE
-vm_yield_with_symbol(rb_thread_t *th, VALUE symbol, int argc, const VALUE *argv, VALUE block_handler)
+vm_yield_with_symbol(rb_execution_context_t *ec, VALUE symbol, int argc, const VALUE *argv, VALUE block_handler)
{
- return rb_sym_proc_call(SYM2ID(symbol), argc, argv, vm_block_handler_to_proc(th, block_handler));
+ return rb_sym_proc_call(SYM2ID(symbol), argc, argv, rb_vm_bh_to_procval(ec, block_handler));
}
static inline int
@@ -2485,15 +2575,19 @@ vm_callee_setup_block_arg_arg0_check(VALUE *argv)
{
VALUE ary, arg0 = argv[0];
ary = rb_check_array_type(arg0);
+#if 0
argv[0] = arg0;
+#else
+ VM_ASSERT(argv[0] == arg0);
+#endif
return ary;
}
static int
-vm_callee_setup_block_arg(rb_thread_t *th, struct rb_calling_info *calling, const struct rb_call_info *ci, const rb_iseq_t *iseq, VALUE *argv, const enum arg_setup_type arg_setup_type)
+vm_callee_setup_block_arg(rb_execution_context_t *ec, struct rb_calling_info *calling, const struct rb_call_info *ci, const rb_iseq_t *iseq, VALUE *argv, const enum arg_setup_type arg_setup_type)
{
- if (simple_iseq_p(iseq)) {
- rb_control_frame_t *cfp = th->cfp;
+ if (rb_simple_iseq_p(iseq)) {
+ rb_control_frame_t *cfp = ec->cfp;
VALUE arg0;
CALLER_SETUP_ARG(cfp, calling, ci); /* splat arg */
@@ -2518,26 +2612,20 @@ vm_callee_setup_block_arg(rb_thread_t *th, struct rb_calling_info *calling, cons
calling->argc = iseq->body->param.lead_num; /* simply truncate arguments */
}
}
- else if (arg_setup_type == arg_setup_lambda &&
- calling->argc == 1 &&
- !NIL_P(arg0 = vm_callee_setup_block_arg_arg0_check(argv)) &&
- RARRAY_LEN(arg0) == iseq->body->param.lead_num) {
- calling->argc = vm_callee_setup_block_arg_arg0_splat(cfp, iseq, argv, arg0);
- }
else {
- argument_arity_error(th, iseq, calling->argc, iseq->body->param.lead_num, iseq->body->param.lead_num);
+ argument_arity_error(ec, iseq, calling->argc, iseq->body->param.lead_num, iseq->body->param.lead_num);
}
}
return 0;
}
else {
- return setup_parameters_complex(th, iseq, calling, ci, argv, arg_setup_type);
+ return setup_parameters_complex(ec, iseq, calling, ci, argv, arg_setup_type);
}
}
static int
-vm_yield_setup_args(rb_thread_t *th, const rb_iseq_t *iseq, const int argc, VALUE *argv, VALUE block_handler, enum arg_setup_type arg_setup_type)
+vm_yield_setup_args(rb_execution_context_t *ec, const rb_iseq_t *iseq, const int argc, VALUE *argv, VALUE block_handler, enum arg_setup_type arg_setup_type)
{
struct rb_calling_info calling_entry, *calling;
struct rb_call_info ci_entry, *ci;
@@ -2549,25 +2637,25 @@ vm_yield_setup_args(rb_thread_t *th, const rb_iseq_t *iseq, const int argc, VALU
ci_entry.flag = 0;
ci = &ci_entry;
- return vm_callee_setup_block_arg(th, calling, ci, iseq, argv, arg_setup_type);
+ return vm_callee_setup_block_arg(ec, calling, ci, iseq, argv, arg_setup_type);
}
/* ruby iseq -> ruby block */
static VALUE
-vm_invoke_iseq_block(rb_thread_t *th, rb_control_frame_t *reg_cfp,
+vm_invoke_iseq_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
struct rb_calling_info *calling, const struct rb_call_info *ci,
int is_lambda, const struct rb_captured_block *captured)
{
- const rb_iseq_t *iseq = captured->code.iseq;
+ const rb_iseq_t *iseq = rb_iseq_check(captured->code.iseq);
const int arg_size = iseq->body->param.size;
VALUE * const rsp = GET_SP() - calling->argc;
- int opt_pc = vm_callee_setup_block_arg(th, calling, ci, iseq, rsp, is_lambda ? arg_setup_lambda : arg_setup_block);
+ int opt_pc = vm_callee_setup_block_arg(ec, calling, ci, iseq, rsp, is_lambda ? arg_setup_method : arg_setup_block);
SET_SP(rsp);
- vm_push_frame(th, iseq,
- is_lambda ? VM_FRAME_MAGIC_LAMBDA : VM_FRAME_MAGIC_BLOCK,
+ vm_push_frame(ec, iseq,
+ VM_FRAME_MAGIC_BLOCK | (is_lambda ? VM_FRAME_FLAG_LAMBDA : 0),
captured->self,
VM_GUARDED_PREV_EP(captured->ep), 0,
iseq->body->iseq_encoded + opt_pc,
@@ -2578,29 +2666,29 @@ vm_invoke_iseq_block(rb_thread_t *th, rb_control_frame_t *reg_cfp,
}
static VALUE
-vm_invoke_symbol_block(rb_thread_t *th, rb_control_frame_t *reg_cfp,
+vm_invoke_symbol_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
struct rb_calling_info *calling, const struct rb_call_info *ci,
VALUE symbol)
{
VALUE val;
int argc;
- CALLER_SETUP_ARG(th->cfp, calling, ci);
+ CALLER_SETUP_ARG(ec->cfp, calling, ci);
argc = calling->argc;
- val = vm_yield_with_symbol(th, symbol, argc, STACK_ADDR_FROM_TOP(argc), VM_BLOCK_HANDLER_NONE);
+ val = vm_yield_with_symbol(ec, symbol, argc, STACK_ADDR_FROM_TOP(argc), calling->block_handler);
POPN(argc);
return val;
}
static VALUE
-vm_invoke_ifunc_block(rb_thread_t *th, rb_control_frame_t *reg_cfp,
+vm_invoke_ifunc_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
struct rb_calling_info *calling, const struct rb_call_info *ci,
const struct rb_captured_block *captured)
{
VALUE val;
int argc;
- CALLER_SETUP_ARG(th->cfp, calling, ci);
+ CALLER_SETUP_ARG(ec->cfp, calling, ci);
argc = calling->argc;
- val = vm_yield_with_cfunc(th, captured, captured->self, argc, STACK_ADDR_FROM_TOP(argc), VM_BLOCK_HANDLER_NONE);
+ val = vm_yield_with_cfunc(ec, captured, captured->self, argc, STACK_ADDR_FROM_TOP(argc), calling->block_handler, NULL);
POPN(argc); /* TODO: should put before C/yield? */
return val;
}
@@ -2624,36 +2712,30 @@ vm_proc_to_block_handler(VALUE procval)
return Qundef;
}
-static VALUE
-vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci)
+static inline VALUE
+vm_invoke_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
+ struct rb_calling_info *calling, const struct rb_call_info *ci, VALUE block_handler)
{
- VALUE block_handler = VM_CF_BLOCK_HANDLER(reg_cfp);
- VALUE type = GET_ISEQ()->body->local_iseq->body->type;
int is_lambda = FALSE;
- if ((type != ISEQ_TYPE_METHOD && type != ISEQ_TYPE_CLASS) ||
- block_handler == VM_BLOCK_HANDLER_NONE) {
- rb_vm_localjump_error("no block given (yield)", Qnil, 0);
- }
-
again:
switch (vm_block_handler_type(block_handler)) {
case block_handler_type_iseq:
{
const struct rb_captured_block *captured = VM_BH_TO_ISEQ_BLOCK(block_handler);
- return vm_invoke_iseq_block(th, reg_cfp, calling, ci, is_lambda, captured);
+ return vm_invoke_iseq_block(ec, reg_cfp, calling, ci, is_lambda, captured);
}
case block_handler_type_ifunc:
{
const struct rb_captured_block *captured = VM_BH_TO_IFUNC_BLOCK(block_handler);
- return vm_invoke_ifunc_block(th, reg_cfp, calling, ci, captured);
+ return vm_invoke_ifunc_block(ec, reg_cfp, calling, ci, captured);
}
case block_handler_type_proc:
is_lambda = block_proc_is_lambda(VM_BH_TO_PROC(block_handler));
block_handler = vm_proc_to_block_handler(VM_BH_TO_PROC(block_handler));
goto again;
case block_handler_type_symbol:
- return vm_invoke_symbol_block(th, reg_cfp, calling, ci, VM_BH_TO_SYMBOL(block_handler));
+ return vm_invoke_symbol_block(ec, reg_cfp, calling, ci, VM_BH_TO_SYMBOL(block_handler));
}
VM_UNREACHABLE(vm_invoke_block: unreachable);
return Qnil;
@@ -2662,8 +2744,8 @@ vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_
static VALUE
vm_make_proc_with_iseq(const rb_iseq_t *blockiseq)
{
- rb_thread_t *th = GET_THREAD();
- const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
+ const rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
struct rb_captured_block *captured;
if (cfp == 0) {
@@ -2673,7 +2755,7 @@ vm_make_proc_with_iseq(const rb_iseq_t *blockiseq)
captured = VM_CFP_TO_CAPTURED_BLOCK(cfp);
captured->code.iseq = blockiseq;
- return rb_vm_make_proc(th, captured, rb_cProc);
+ return rb_vm_make_proc(ec, captured, rb_cProc);
}
static VALUE
@@ -2692,14 +2774,14 @@ vm_once_clear(VALUE data)
}
rb_control_frame_t *
-FUNC_FASTCALL(rb_vm_opt_struct_aref)(rb_thread_t *th, rb_control_frame_t *reg_cfp)
+FUNC_FASTCALL(rb_vm_opt_struct_aref)(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp)
{
TOPN(0) = rb_struct_aref(GET_SELF(), TOPN(0));
return reg_cfp;
}
rb_control_frame_t *
-FUNC_FASTCALL(rb_vm_opt_struct_aset)(rb_thread_t *th, rb_control_frame_t *reg_cfp)
+FUNC_FASTCALL(rb_vm_opt_struct_aset)(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp)
{
rb_struct_aset(GET_SELF(), TOPN(0), TOPN(1));
return reg_cfp;
@@ -2719,15 +2801,15 @@ check_respond_to_missing(VALUE obj, VALUE v)
return DEFINED_METHOD;
}
else {
- return 0;
+ return DEFINED_NOT_DEFINED;
}
}
static VALUE
-vm_defined(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t op_type, VALUE obj, VALUE needstr, VALUE v)
+vm_defined(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, rb_num_t op_type, VALUE obj, VALUE needstr, VALUE v)
{
VALUE klass;
- enum defined_type expr_type = 0;
+ enum defined_type expr_type = DEFINED_NOT_DEFINED;
enum defined_type type = (enum defined_type)op_type;
switch (type) {
@@ -2754,7 +2836,7 @@ vm_defined(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t op_type, VALUE
}
case DEFINED_CONST:
klass = v;
- if (vm_get_ev_const(th, klass, SYM2ID(obj), 1)) {
+ if (vm_get_ev_const(ec, klass, SYM2ID(obj), 1)) {
expr_type = DEFINED_CONST;
}
break;
@@ -2776,7 +2858,7 @@ vm_defined(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t op_type, VALUE
case METHOD_VISI_PRIVATE:
break;
case METHOD_VISI_PROTECTED:
- if (!rb_obj_is_kind_of(GET_SELF(), rb_class_real(klass))) {
+ if (!rb_obj_is_kind_of(GET_SELF(), rb_class_real(me->defined_class))) {
break;
}
case METHOD_VISI_PUBLIC:
@@ -2811,7 +2893,7 @@ vm_defined(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t op_type, VALUE
}
break;
case DEFINED_REF:{
- if (vm_getspecial(th, GET_LEP(), Qfalse, FIX2INT(obj)) != Qnil) {
+ if (vm_getspecial(ec, GET_LEP(), Qfalse, FIX2INT(obj)) != Qnil) {
expr_type = DEFINED_GVAR;
}
break;
@@ -2833,3 +2915,1060 @@ vm_defined(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t op_type, VALUE
return Qnil;
}
}
+
+static const VALUE *
+vm_get_ep(const VALUE *const reg_ep, rb_num_t lv)
+{
+ rb_num_t i;
+ const VALUE *ep = reg_ep;
+ for (i = 0; i < lv; i++) {
+ ep = GET_PREV_EP(ep);
+ }
+ return ep;
+}
+
+static VALUE
+vm_get_special_object(const VALUE *const reg_ep,
+ enum vm_special_object_type type)
+{
+ switch (type) {
+ case VM_SPECIAL_OBJECT_VMCORE:
+ return rb_mRubyVMFrozenCore;
+ case VM_SPECIAL_OBJECT_CBASE:
+ return vm_get_cbase(reg_ep);
+ case VM_SPECIAL_OBJECT_CONST_BASE:
+ return vm_get_const_base(reg_ep);
+ default:
+ rb_bug("putspecialobject insn: unknown value_type %d", type);
+ }
+}
+
+static void
+vm_freezestring(VALUE str, VALUE debug)
+{
+ if (!NIL_P(debug)) {
+ rb_ivar_set(str, id_debug_created_info, debug);
+ }
+ rb_str_freeze(str);
+}
+
+static VALUE
+vm_concat_array(VALUE ary1, VALUE ary2st)
+{
+ const VALUE ary2 = ary2st;
+ VALUE tmp1 = rb_check_to_array(ary1);
+ VALUE tmp2 = rb_check_to_array(ary2);
+
+ if (NIL_P(tmp1)) {
+ tmp1 = rb_ary_new3(1, ary1);
+ }
+
+ if (NIL_P(tmp2)) {
+ tmp2 = rb_ary_new3(1, ary2);
+ }
+
+ if (tmp1 == ary1) {
+ tmp1 = rb_ary_dup(ary1);
+ }
+ return rb_ary_concat(tmp1, tmp2);
+}
+
+static VALUE
+vm_splat_array(VALUE flag, VALUE ary)
+{
+ VALUE tmp = rb_check_to_array(ary);
+ if (NIL_P(tmp)) {
+ return rb_ary_new3(1, ary);
+ }
+ else if (RTEST(flag)) {
+ return rb_ary_dup(tmp);
+ }
+ else {
+ return tmp;
+ }
+}
+
+static VALUE
+vm_check_match(rb_execution_context_t *ec, VALUE target, VALUE pattern, rb_num_t flag)
+{
+ enum vm_check_match_type type = ((int)flag) & VM_CHECKMATCH_TYPE_MASK;
+
+ if (flag & VM_CHECKMATCH_ARRAY) {
+ long i;
+ const long n = RARRAY_LEN(pattern);
+
+ for (i = 0; i < n; i++) {
+ VALUE v = RARRAY_AREF(pattern, i);
+ VALUE c = check_match(ec, v, target, type);
+
+ if (RTEST(c)) {
+ return c;
+ }
+ }
+ return Qfalse;
+ }
+ else {
+ return check_match(ec, pattern, target, type);
+ }
+}
+
+static VALUE
+vm_check_keyword(lindex_t bits, lindex_t idx, const VALUE *ep)
+{
+ const VALUE kw_bits = *(ep - bits);
+
+ if (FIXNUM_P(kw_bits)) {
+ unsigned int b = (unsigned int)FIX2ULONG(kw_bits);
+ if ((idx < KW_SPECIFIED_BITS_MAX) && (b & (0x01 << idx)))
+ return Qfalse;
+ }
+ else {
+ VM_ASSERT(RB_TYPE_P(kw_bits, T_HASH));
+ if (rb_hash_has_key(kw_bits, INT2FIX(idx))) return Qfalse;
+ }
+ return Qtrue;
+}
+
+static void
+vm_dtrace(rb_event_flag_t flag, rb_execution_context_t *ec)
+{
+ if (RUBY_DTRACE_METHOD_ENTRY_ENABLED() ||
+ RUBY_DTRACE_METHOD_RETURN_ENABLED() ||
+ RUBY_DTRACE_CMETHOD_ENTRY_ENABLED() ||
+ RUBY_DTRACE_CMETHOD_RETURN_ENABLED()) {
+
+ switch (flag) {
+ case RUBY_EVENT_CALL:
+ RUBY_DTRACE_METHOD_ENTRY_HOOK(ec, 0, 0);
+ return;
+ case RUBY_EVENT_C_CALL:
+ RUBY_DTRACE_CMETHOD_ENTRY_HOOK(ec, 0, 0);
+ return;
+ case RUBY_EVENT_RETURN:
+ RUBY_DTRACE_METHOD_RETURN_HOOK(ec, 0, 0);
+ return;
+ case RUBY_EVENT_C_RETURN:
+ RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, 0, 0);
+ return;
+ }
+ }
+}
+
+static VALUE
+vm_const_get_under(ID id, rb_num_t flags, VALUE cbase)
+{
+ VALUE ns;
+
+ if ((ns = vm_search_const_defined_class(cbase, id)) == 0) {
+ return ns;
+ }
+ else if (VM_DEFINECLASS_SCOPED_P(flags)) {
+ return rb_public_const_get_at(ns, id);
+ }
+ else {
+ return rb_const_get_at(ns, id);
+ }
+}
+
+static VALUE
+vm_check_if_class(ID id, rb_num_t flags, VALUE super, VALUE klass)
+{
+ if (!RB_TYPE_P(klass, T_CLASS)) {
+ rb_raise(rb_eTypeError, "%"PRIsVALUE" is not a class", rb_id2str(id));
+ }
+ else if (VM_DEFINECLASS_HAS_SUPERCLASS_P(flags)) {
+ VALUE tmp = rb_class_real(RCLASS_SUPER(klass));
+
+ if (tmp != super) {
+ rb_raise(rb_eTypeError,
+ "superclass mismatch for class %"PRIsVALUE"",
+ rb_id2str(id));
+ }
+ else {
+ return klass;
+ }
+ }
+ else {
+ return klass;
+ }
+}
+
+static VALUE
+vm_check_if_module(ID id, VALUE mod)
+{
+ if (!RB_TYPE_P(mod, T_MODULE)) {
+ rb_raise(rb_eTypeError, "%"PRIsVALUE" is not a module", rb_id2str(id));
+ }
+ else {
+ return mod;
+ }
+}
+
+static VALUE
+vm_declare_class(ID id, rb_num_t flags, VALUE cbase, VALUE super)
+{
+ /* new class declaration */
+ VALUE s = VM_DEFINECLASS_HAS_SUPERCLASS_P(flags) ? super : rb_cObject;
+ VALUE c = rb_define_class_id(id, s);
+
+ rb_set_class_path_string(c, cbase, rb_id2str(id));
+ rb_const_set(cbase, id, c);
+ rb_class_inherited(s, c);
+ return c;
+}
+
+static VALUE
+vm_declare_module(ID id, VALUE cbase)
+{
+ /* new module declaration */
+ VALUE mod = rb_define_module_id(id);
+ rb_set_class_path_string(mod, cbase, rb_id2str(id));
+ rb_const_set(cbase, id, mod);
+ return mod;
+}
+
+static VALUE
+vm_define_class(ID id, rb_num_t flags, VALUE cbase, VALUE super)
+{
+ VALUE klass;
+
+ if (VM_DEFINECLASS_HAS_SUPERCLASS_P(flags) && !RB_TYPE_P(super, T_CLASS)) {
+ rb_raise(rb_eTypeError,
+ "superclass must be a Class (%"PRIsVALUE" given)",
+ rb_obj_class(super));
+ }
+
+ vm_check_if_namespace(cbase);
+
+ /* find klass */
+ rb_autoload_load(cbase, id);
+ if ((klass = vm_const_get_under(id, flags, cbase)) != 0) {
+ return vm_check_if_class(id, flags, super, klass);
+ }
+ else {
+ return vm_declare_class(id, flags, cbase, super);
+ }
+}
+
+static VALUE
+vm_define_module(ID id, rb_num_t flags, VALUE cbase)
+{
+ VALUE mod;
+
+ vm_check_if_namespace(cbase);
+ if ((mod = vm_const_get_under(id, flags, cbase)) != 0) {
+ return vm_check_if_module(id, mod);
+ }
+ else {
+ return vm_declare_module(id, cbase);
+ }
+}
+
+static VALUE
+vm_find_or_create_class_by_id(ID id,
+ rb_num_t flags,
+ VALUE cbase,
+ VALUE super)
+{
+ rb_vm_defineclass_type_t type = VM_DEFINECLASS_TYPE(flags);
+
+ switch (type) {
+ case VM_DEFINECLASS_TYPE_CLASS:
+ /* classdef returns class scope value */
+ return vm_define_class(id, flags, cbase, super);
+
+ case VM_DEFINECLASS_TYPE_SINGLETON_CLASS:
+ /* classdef returns class scope value */
+ return rb_singleton_class(cbase);
+
+ case VM_DEFINECLASS_TYPE_MODULE:
+ /* classdef returns class scope value */
+ return vm_define_module(id, flags, cbase);
+
+ default:
+ rb_bug("unknown defineclass type: %d", (int)type);
+ }
+}
+
+static VALUE
+vm_opt_str_freeze(VALUE str, int bop, ID id)
+{
+ if (BASIC_OP_UNREDEFINED_P(bop, STRING_REDEFINED_OP_FLAG)) {
+ return str;
+ }
+ else {
+ return Qundef;
+ }
+}
+
+/* this macro is mandatory to use OPTIMIZED_CMP. What a design! */
+#define id_cmp idCmp
+
+static VALUE
+vm_opt_newarray_max(rb_num_t num, const VALUE *ptr)
+{
+ if (BASIC_OP_UNREDEFINED_P(BOP_MAX, ARRAY_REDEFINED_OP_FLAG)) {
+ if (num == 0) {
+ return Qnil;
+ }
+ else {
+ struct cmp_opt_data cmp_opt = { 0, 0 };
+ VALUE result = *ptr;
+ rb_snum_t i = num - 1;
+ while (i-- > 0) {
+ const VALUE v = *++ptr;
+ if (OPTIMIZED_CMP(v, result, cmp_opt) > 0) {
+ result = v;
+ }
+ }
+ return result;
+ }
+ }
+ else {
+ VALUE ary = rb_ary_new4(num, ptr);
+ return rb_funcall(ary, idMax, 0);
+ }
+}
+
+static VALUE
+vm_opt_newarray_min(rb_num_t num, const VALUE *ptr)
+{
+ if (BASIC_OP_UNREDEFINED_P(BOP_MIN, ARRAY_REDEFINED_OP_FLAG)) {
+ if (num == 0) {
+ return Qnil;
+ }
+ else {
+ struct cmp_opt_data cmp_opt = { 0, 0 };
+ VALUE result = *ptr;
+ rb_snum_t i = num - 1;
+ while (i-- > 0) {
+ const VALUE v = *++ptr;
+ if (OPTIMIZED_CMP(v, result, cmp_opt) < 0) {
+ result = v;
+ }
+ }
+ return result;
+ }
+ }
+ else {
+ VALUE ary = rb_ary_new4(num, ptr);
+ return rb_funcall(ary, idMin, 0);
+ }
+}
+
+#undef id_cmp
+
+static int
+vm_ic_hit_p(IC ic, const VALUE *reg_ep)
+{
+ if (ic->ic_serial == GET_GLOBAL_CONSTANT_STATE()) {
+ return (ic->ic_cref == NULL || ic->ic_cref == rb_vm_get_cref(reg_ep));
+ }
+ return FALSE;
+}
+
+static void
+vm_ic_update(IC ic, VALUE val, const VALUE *reg_ep)
+{
+ VM_ASSERT(ic->ic_value.value != Qundef);
+ ic->ic_value.value = val;
+ ic->ic_serial = GET_GLOBAL_CONSTANT_STATE() - ruby_vm_const_missing_count;
+ ic->ic_cref = vm_get_const_key_cref(reg_ep);
+ ruby_vm_const_missing_count = 0;
+}
+
+static VALUE
+vm_once_dispatch(rb_execution_context_t *ec, ISEQ iseq, ISE is)
+{
+ rb_thread_t *th = rb_ec_thread_ptr(ec);
+ rb_thread_t *const RUNNING_THREAD_ONCE_DONE = (rb_thread_t *)(0x1);
+
+ again:
+ if (is->once.running_thread == RUNNING_THREAD_ONCE_DONE) {
+ return is->once.value;
+ }
+ else if (is->once.running_thread == NULL) {
+ VALUE val;
+ is->once.running_thread = th;
+ val = rb_ensure(vm_once_exec, (VALUE)iseq, vm_once_clear, (VALUE)is);
+ RB_OBJ_WRITE(ec->cfp->iseq, &is->once.value, val);
+ /* is->once.running_thread is cleared by vm_once_clear() */
+ is->once.running_thread = RUNNING_THREAD_ONCE_DONE; /* success */
+ return val;
+ }
+ else if (is->once.running_thread == th) {
+ /* recursive once */
+ return vm_once_exec((VALUE)iseq);
+ }
+ else {
+ /* waiting for finish */
+ RUBY_VM_CHECK_INTS(ec);
+ rb_thread_schedule();
+ goto again;
+ }
+}
+
+static OFFSET
+vm_case_dispatch(CDHASH hash, OFFSET else_offset, VALUE key)
+{
+ switch (OBJ_BUILTIN_TYPE(key)) {
+ case -1:
+ case T_FLOAT:
+ case T_SYMBOL:
+ case T_BIGNUM:
+ case T_STRING:
+ if (BASIC_OP_UNREDEFINED_P(BOP_EQQ,
+ SYMBOL_REDEFINED_OP_FLAG |
+ INTEGER_REDEFINED_OP_FLAG |
+ FLOAT_REDEFINED_OP_FLAG |
+ NIL_REDEFINED_OP_FLAG |
+ TRUE_REDEFINED_OP_FLAG |
+ FALSE_REDEFINED_OP_FLAG |
+ STRING_REDEFINED_OP_FLAG)) {
+ st_data_t val;
+ if (RB_FLOAT_TYPE_P(key)) {
+ double kval = RFLOAT_VALUE(key);
+ if (!isinf(kval) && modf(kval, &kval) == 0.0) {
+ key = FIXABLE(kval) ? LONG2FIX((long)kval) : rb_dbl2big(kval);
+ }
+ }
+ if (rb_hash_stlike_lookup(hash, key, &val)) {
+ return FIX2LONG((VALUE)val);
+ }
+ else {
+ return else_offset;
+ }
+ }
+ }
+ return 0;
+}
+
+NORETURN(static void
+ vm_stack_consistency_error(const rb_execution_context_t *ec,
+ const rb_control_frame_t *,
+ const VALUE *));
+static void
+vm_stack_consistency_error(const rb_execution_context_t *ec,
+ const rb_control_frame_t *cfp,
+ const VALUE *bp)
+{
+ const ptrdiff_t nsp = VM_SP_CNT(ec, cfp->sp);
+ const ptrdiff_t nbp = VM_SP_CNT(ec, bp);
+ static const char stack_consistency_error[] =
+ "Stack consistency error (sp: %"PRIdPTRDIFF", bp: %"PRIdPTRDIFF")";
+#if defined RUBY_DEVEL
+ VALUE mesg = rb_sprintf(stack_consistency_error, nsp, nbp);
+ rb_str_cat_cstr(mesg, "\n");
+ rb_str_append(mesg, rb_iseq_disasm(cfp->iseq));
+ rb_exc_fatal(rb_exc_new3(rb_eFatal, mesg));
+#else
+ rb_bug(stack_consistency_error, nsp, nbp);
+#endif
+}
+
+static VALUE
+vm_opt_plus(VALUE recv, VALUE obj)
+{
+ if (FIXNUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_PLUS, INTEGER_REDEFINED_OP_FLAG)) {
+ return rb_fix_plus_fix(recv, obj);
+ }
+ else if (FLONUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) {
+ return DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj));
+ }
+ else if (SPECIAL_CONST_P(recv) || SPECIAL_CONST_P(obj)) {
+ return Qundef;
+ }
+ else if (RBASIC_CLASS(recv) == rb_cFloat &&
+ RBASIC_CLASS(obj) == rb_cFloat &&
+ BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) {
+ return DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj));
+ }
+ else if (RBASIC_CLASS(recv) == rb_cString &&
+ RBASIC_CLASS(obj) == rb_cString &&
+ BASIC_OP_UNREDEFINED_P(BOP_PLUS, STRING_REDEFINED_OP_FLAG)) {
+ return rb_str_plus(recv, obj);
+ }
+ else if (RBASIC_CLASS(recv) == rb_cArray &&
+ BASIC_OP_UNREDEFINED_P(BOP_PLUS, ARRAY_REDEFINED_OP_FLAG)) {
+ return rb_ary_plus(recv, obj);
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_minus(VALUE recv, VALUE obj)
+{
+ if (FIXNUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_MINUS, INTEGER_REDEFINED_OP_FLAG)) {
+ return rb_fix_minus_fix(recv, obj);
+ }
+ else if (FLONUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) {
+ return DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj));
+ }
+ else if (SPECIAL_CONST_P(recv) || SPECIAL_CONST_P(obj)) {
+ return Qundef;
+ }
+ else if (RBASIC_CLASS(recv) == rb_cFloat &&
+ RBASIC_CLASS(obj) == rb_cFloat &&
+ BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) {
+ return DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj));
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_mult(VALUE recv, VALUE obj)
+{
+ if (FIXNUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_MULT, INTEGER_REDEFINED_OP_FLAG)) {
+ return rb_fix_mul_fix(recv, obj);
+ }
+ else if (FLONUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) {
+ return DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj));
+ }
+ else if (SPECIAL_CONST_P(recv) || SPECIAL_CONST_P(obj)) {
+ return Qundef;
+ }
+ else if (RBASIC_CLASS(recv) == rb_cFloat &&
+ RBASIC_CLASS(obj) == rb_cFloat &&
+ BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) {
+ return DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj));
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_div(VALUE recv, VALUE obj)
+{
+ if (FIXNUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_DIV, INTEGER_REDEFINED_OP_FLAG)) {
+ return (FIX2LONG(obj) == 0) ? Qundef : rb_fix_div_fix(recv, obj);
+ }
+ else if (FLONUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_DIV, FLOAT_REDEFINED_OP_FLAG)) {
+ return rb_flo_div_flo(recv, obj);
+ }
+ else if (SPECIAL_CONST_P(recv) || SPECIAL_CONST_P(obj)) {
+ return Qundef;
+ }
+ else if (RBASIC_CLASS(recv) == rb_cFloat &&
+ RBASIC_CLASS(obj) == rb_cFloat &&
+ BASIC_OP_UNREDEFINED_P(BOP_DIV, FLOAT_REDEFINED_OP_FLAG)) {
+ return rb_flo_div_flo(recv, obj);
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_mod(VALUE recv, VALUE obj)
+{
+ if (FIXNUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_MOD, INTEGER_REDEFINED_OP_FLAG)) {
+ return (FIX2LONG(obj) == 0) ? Qundef : rb_fix_mod_fix(recv, obj);
+ }
+ else if (FLONUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) {
+ return DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)));
+ }
+ else if (SPECIAL_CONST_P(recv) || SPECIAL_CONST_P(obj)) {
+ return Qundef;
+ }
+ else if (RBASIC_CLASS(recv) == rb_cFloat &&
+ RBASIC_CLASS(obj) == rb_cFloat &&
+ BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) {
+ return DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)));
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_neq(CALL_INFO ci, CALL_CACHE cc,
+ CALL_INFO ci_eq, CALL_CACHE cc_eq,
+ VALUE recv, VALUE obj)
+{
+ if (vm_method_cfunc_is(ci, cc, recv, rb_obj_not_equal)) {
+ VALUE val = opt_eq_func(recv, obj, ci_eq, cc_eq);
+
+ if (val != Qundef) {
+ return RTEST(val) ? Qfalse : Qtrue;
+ }
+ }
+
+ return Qundef;
+}
+
+static VALUE
+vm_opt_lt(VALUE recv, VALUE obj)
+{
+ if (FIXNUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_LT, INTEGER_REDEFINED_OP_FLAG)) {
+ return (SIGNED_VALUE)recv < (SIGNED_VALUE)obj ? Qtrue : Qfalse;
+ }
+ else if (FLONUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) {
+ return RFLOAT_VALUE(recv) < RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
+ }
+ else if (SPECIAL_CONST_P(recv) || SPECIAL_CONST_P(obj)) {
+ return Qundef;
+ }
+ else if (RBASIC_CLASS(recv) == rb_cFloat &&
+ RBASIC_CLASS(obj) == rb_cFloat &&
+ BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) {
+ CHECK_CMP_NAN(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj));
+ return RFLOAT_VALUE(recv) < RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_le(VALUE recv, VALUE obj)
+{
+ if (FIXNUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_LE, INTEGER_REDEFINED_OP_FLAG)) {
+ return (SIGNED_VALUE)recv <= (SIGNED_VALUE)obj ? Qtrue : Qfalse;
+ }
+ else if (FLONUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_LE, FLOAT_REDEFINED_OP_FLAG)) {
+ return RFLOAT_VALUE(recv) <= RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
+ }
+ else if (SPECIAL_CONST_P(recv) || SPECIAL_CONST_P(obj)) {
+ return Qundef;
+ }
+ else if (RBASIC_CLASS(recv) == rb_cFloat &&
+ RBASIC_CLASS(obj) == rb_cFloat &&
+ BASIC_OP_UNREDEFINED_P(BOP_LE, FLOAT_REDEFINED_OP_FLAG)) {
+ CHECK_CMP_NAN(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj));
+ return RFLOAT_VALUE(recv) <= RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_gt(VALUE recv, VALUE obj)
+{
+ if (FIXNUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_GT, INTEGER_REDEFINED_OP_FLAG)) {
+ return (SIGNED_VALUE)recv > (SIGNED_VALUE)obj ? Qtrue : Qfalse;
+ }
+ else if (FLONUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) {
+ return RFLOAT_VALUE(recv) > RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
+ }
+ else if (SPECIAL_CONST_P(recv) || SPECIAL_CONST_P(obj)) {
+ return Qundef;
+ }
+ else if (RBASIC_CLASS(recv) == rb_cFloat &&
+ RBASIC_CLASS(obj) == rb_cFloat &&
+ BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) {
+ CHECK_CMP_NAN(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj));
+ return RFLOAT_VALUE(recv) > RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_ge(VALUE recv, VALUE obj)
+{
+ if (FIXNUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_GE, INTEGER_REDEFINED_OP_FLAG)) {
+ return (SIGNED_VALUE)recv >= (SIGNED_VALUE)obj ? Qtrue : Qfalse;
+ }
+ else if (FLONUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_GE, FLOAT_REDEFINED_OP_FLAG)) {
+ return RFLOAT_VALUE(recv) >= RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
+ }
+ else if (SPECIAL_CONST_P(recv) || SPECIAL_CONST_P(obj)) {
+ return Qundef;
+ }
+ else if (RBASIC_CLASS(recv) == rb_cFloat &&
+ RBASIC_CLASS(obj) == rb_cFloat &&
+ BASIC_OP_UNREDEFINED_P(BOP_GE, FLOAT_REDEFINED_OP_FLAG)) {
+ CHECK_CMP_NAN(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj));
+ return RFLOAT_VALUE(recv) >= RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
+ }
+ else {
+ return Qundef;
+ }
+}
+
+
+static VALUE
+vm_opt_ltlt(VALUE recv, VALUE obj)
+{
+ if (SPECIAL_CONST_P(recv)) {
+ return Qundef;
+ }
+ else if (RBASIC_CLASS(recv) == rb_cString &&
+ BASIC_OP_UNREDEFINED_P(BOP_LTLT, STRING_REDEFINED_OP_FLAG)) {
+ return rb_str_concat(recv, obj);
+ }
+ else if (RBASIC_CLASS(recv) == rb_cArray &&
+ BASIC_OP_UNREDEFINED_P(BOP_LTLT, ARRAY_REDEFINED_OP_FLAG)) {
+ return rb_ary_push(recv, obj);
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_and(VALUE recv, VALUE obj)
+{
+ if (FIXNUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_AND, INTEGER_REDEFINED_OP_FLAG)) {
+ return LONG2NUM(FIX2LONG(recv) & FIX2LONG(obj));
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_or(VALUE recv, VALUE obj)
+{
+ if (FIXNUM_2_P(recv, obj) &&
+ BASIC_OP_UNREDEFINED_P(BOP_OR, INTEGER_REDEFINED_OP_FLAG)) {
+ return LONG2NUM(FIX2LONG(recv) | FIX2LONG(obj));
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_aref(VALUE recv, VALUE obj)
+{
+ if (SPECIAL_CONST_P(recv)) {
+ return Qundef;
+ }
+ else if (RBASIC_CLASS(recv) == rb_cArray &&
+ BASIC_OP_UNREDEFINED_P(BOP_AREF, ARRAY_REDEFINED_OP_FLAG)) {
+ if (FIXNUM_P(obj)) {
+ return rb_ary_entry_internal(recv, FIX2LONG(obj));
+ }
+ else {
+ return rb_ary_aref1(recv, obj);
+ }
+ }
+ else if (RBASIC_CLASS(recv) == rb_cHash &&
+ BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG)) {
+ return rb_hash_aref(recv, obj);
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_aset(VALUE recv, VALUE obj, VALUE set)
+{
+ if (SPECIAL_CONST_P(recv)) {
+ return Qundef;
+ }
+ else if (RBASIC_CLASS(recv) == rb_cArray &&
+ BASIC_OP_UNREDEFINED_P(BOP_ASET, ARRAY_REDEFINED_OP_FLAG) &&
+ FIXNUM_P(obj)) {
+ rb_ary_store(recv, FIX2LONG(obj), set);
+ return set;
+ }
+ else if (RBASIC_CLASS(recv) == rb_cHash &&
+ BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG)) {
+ rb_hash_aset(recv, obj, set);
+ return set;
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_aref_with(VALUE recv, VALUE key)
+{
+ if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash &&
+ BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG) &&
+ rb_hash_compare_by_id_p(recv) == Qfalse) {
+ return rb_hash_aref(recv, key);
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_aset_with(VALUE recv, VALUE key, VALUE val)
+{
+ if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash &&
+ BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG) &&
+ rb_hash_compare_by_id_p(recv) == Qfalse) {
+ return rb_hash_aset(recv, key, val);
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_length(VALUE recv, int bop)
+{
+ if (SPECIAL_CONST_P(recv)) {
+ return Qundef;
+ }
+ else if (RBASIC_CLASS(recv) == rb_cString &&
+ BASIC_OP_UNREDEFINED_P(bop, STRING_REDEFINED_OP_FLAG)) {
+ if (bop == BOP_EMPTY_P) {
+ return LONG2NUM(RSTRING_LEN(recv));
+ }
+ else {
+ return rb_str_length(recv);
+ }
+ }
+ else if (RBASIC_CLASS(recv) == rb_cArray &&
+ BASIC_OP_UNREDEFINED_P(bop, ARRAY_REDEFINED_OP_FLAG)) {
+ return LONG2NUM(RARRAY_LEN(recv));
+ }
+ else if (RBASIC_CLASS(recv) == rb_cHash &&
+ BASIC_OP_UNREDEFINED_P(bop, HASH_REDEFINED_OP_FLAG)) {
+ return INT2FIX(RHASH_SIZE(recv));
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_empty_p(VALUE recv)
+{
+ switch (vm_opt_length(recv, BOP_EMPTY_P)) {
+ case Qundef: return Qundef;
+ case INT2FIX(0): return Qtrue;
+ default: return Qfalse;
+ }
+}
+
+static VALUE
+fix_succ(VALUE x)
+{
+ switch (x) {
+ case ~0UL:
+ /* 0xFFFF_FFFF == INT2FIX(-1)
+ * `-1.succ` is of course 0. */
+ return INT2FIX(0);
+ case RSHIFT(~0UL, 1):
+ /* 0x7FFF_FFFF == LONG2FIX(0x3FFF_FFFF)
+ * 0x3FFF_FFFF + 1 == 0x4000_0000, which is a Bignum. */
+ return rb_uint2big(1UL << (SIZEOF_LONG * CHAR_BIT - 2));
+ default:
+ /* LONG2FIX(FIX2LONG(x)+FIX2LONG(y))
+ * == ((lx*2+1)/2 + (ly*2+1)/2)*2+1
+ * == lx*2 + ly*2 + 1
+ * == (lx*2+1) + (ly*2+1) - 1
+ * == x + y - 1
+ *
+ * Here, if we put y := INT2FIX(1):
+ *
+ * == x + INT2FIX(1) - 1
+ * == x + 2 .
+ */
+ return x + 2;
+ }
+}
+
+static VALUE
+vm_opt_succ(VALUE recv)
+{
+ if (FIXNUM_P(recv) &&
+ BASIC_OP_UNREDEFINED_P(BOP_SUCC, INTEGER_REDEFINED_OP_FLAG)) {
+ return fix_succ(recv);
+ }
+ else if (SPECIAL_CONST_P(recv)) {
+ return Qundef;
+ }
+ else if (RBASIC_CLASS(recv) == rb_cString &&
+ BASIC_OP_UNREDEFINED_P(BOP_SUCC, STRING_REDEFINED_OP_FLAG)) {
+ return rb_str_succ(recv);
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_not(CALL_INFO ci, CALL_CACHE cc, VALUE recv)
+{
+ if (vm_method_cfunc_is(ci, cc, recv, rb_obj_not)) {
+ return RTEST(recv) ? Qfalse : Qtrue;
+ }
+ else {
+ return Qundef;
+ }
+}
+
+static VALUE
+vm_opt_regexpmatch1(VALUE recv, VALUE obj)
+{
+ if (BASIC_OP_UNREDEFINED_P(BOP_MATCH, REGEXP_REDEFINED_OP_FLAG)) {
+ return rb_reg_match(recv, obj);
+ }
+ else {
+ return rb_funcall(recv, idEqTilde, 1, obj);
+ }
+}
+
+static VALUE
+vm_opt_regexpmatch2(VALUE recv, VALUE obj)
+{
+ if (CLASS_OF(recv) == rb_cString &&
+ BASIC_OP_UNREDEFINED_P(BOP_MATCH, STRING_REDEFINED_OP_FLAG)) {
+ return rb_reg_match(obj, recv);
+ }
+ else {
+ return Qundef;
+ }
+}
+
+rb_event_flag_t rb_iseq_event_flags(const rb_iseq_t *iseq, size_t pos);
+
+NOINLINE(static void vm_trace(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE *pc));
+
+static inline void
+vm_trace_hook(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE *pc,
+ rb_event_flag_t pc_events, rb_event_flag_t target_event,
+ rb_hook_list_t *global_hooks, rb_hook_list_t *local_hooks, VALUE val)
+{
+ rb_event_flag_t event = pc_events & target_event;
+ VALUE self = GET_SELF();
+
+ VM_ASSERT(rb_popcount64((uint64_t)event) == 1);
+
+ if (event & global_hooks->events) {
+ /* increment PC because source line is calculated with PC-1 */
+ reg_cfp->pc++;
+ vm_dtrace(event, ec);
+ rb_exec_event_hook_orig(ec, global_hooks, event, self, 0, 0, 0 , val, 0);
+ reg_cfp->pc--;
+ }
+
+ if (local_hooks != NULL) {
+ if (event & local_hooks->events) {
+ /* increment PC because source line is calculated with PC-1 */
+ reg_cfp->pc++;
+ rb_exec_event_hook_orig(ec, local_hooks, event, self, 0, 0, 0 , val, 0);
+ reg_cfp->pc--;
+ }
+ }
+}
+
+#define VM_TRACE_HOOK(target_event, val) do { \
+ if ((pc_events & (target_event)) & enabled_flags) { \
+ vm_trace_hook(ec, reg_cfp, pc, pc_events, (target_event), global_hooks, local_hooks, (val)); \
+ } \
+} while (0)
+
+static void
+vm_trace(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE *pc)
+{
+ rb_event_flag_t enabled_flags = ruby_vm_event_flags & ISEQ_TRACE_EVENTS;
+
+ if (enabled_flags == 0 && ruby_vm_event_local_num == 0) {
+ return;
+ }
+ else {
+ const rb_iseq_t *iseq = reg_cfp->iseq;
+ size_t pos = pc - iseq->body->iseq_encoded;
+ rb_event_flag_t pc_events = rb_iseq_event_flags(iseq, pos);
+ rb_hook_list_t *local_hooks = iseq->aux.exec.local_hooks;
+ rb_event_flag_t local_hook_events = local_hooks != NULL ? local_hooks->events : 0;
+ enabled_flags |= local_hook_events;
+
+ VM_ASSERT((local_hook_events & ~ISEQ_TRACE_EVENTS) == 0);
+
+ if ((pc_events & enabled_flags) == 0) {
+#if 0
+ /* disable trace */
+ /* TODO: incomplete */
+ rb_iseq_trace_set(iseq, vm_event_flags & ISEQ_TRACE_EVENTS);
+#else
+ /* do not disable trace because of performance problem
+ * (re-enable overhead)
+ */
+#endif
+ return;
+ }
+ else if (ec->trace_arg != NULL) {
+ /* already tracing */
+ return;
+ }
+ else {
+ rb_hook_list_t *global_hooks = rb_vm_global_hooks(ec);
+
+ if (0) {
+ fprintf(stderr, "vm_trace>>%4d (%4x) - %s:%d %s\n",
+ (int)pos,
+ (int)pc_events,
+ RSTRING_PTR(rb_iseq_path(iseq)),
+ (int)rb_iseq_line_no(iseq, pos),
+ RSTRING_PTR(rb_iseq_label(iseq)));
+ }
+ VM_ASSERT(reg_cfp->pc == pc);
+ VM_ASSERT(pc_events != 0);
+ VM_ASSERT(enabled_flags & pc_events);
+
+ /* check traces */
+ VM_TRACE_HOOK(RUBY_EVENT_CLASS | RUBY_EVENT_CALL | RUBY_EVENT_B_CALL, Qundef);
+ VM_TRACE_HOOK(RUBY_EVENT_LINE, Qundef);
+ VM_TRACE_HOOK(RUBY_EVENT_COVERAGE_LINE, Qundef);
+ VM_TRACE_HOOK(RUBY_EVENT_COVERAGE_BRANCH, Qundef);
+ VM_TRACE_HOOK(RUBY_EVENT_END | RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN, TOPN(0));
+ }
+ }
+}
+
+#if VM_CHECK_MODE > 0
+static NORETURN( NOINLINE( COLDFUNC
+void vm_canary_is_found_dead(enum ruby_vminsn_type i, VALUE c)));
+static VALUE vm_stack_canary;
+
+void
+Init_vm_stack_canary(void)
+{
+ /* This has to be called _after_ our PRNG is properly set up. */
+ int n = ruby_fill_random_bytes(&vm_stack_canary, sizeof vm_stack_canary, false);
+
+ VM_ASSERT(n == 0);
+}
+
+static void
+vm_canary_is_found_dead(enum ruby_vminsn_type i, VALUE c)
+{
+ /* Because a method has already been called, why not call
+ * another one. */
+ const char *insn = rb_insns_name(i);
+ VALUE inspection = rb_inspect(c);
+ const char *str = StringValueCStr(inspection);
+
+ rb_bug("dead canary found at %s: %s", insn, str);
+}
+
+#else
+void Init_vm_stack_canary(void) { /* nothing to do */ }
+#endif
diff --git a/vm_insnhelper.h b/vm_insnhelper.h
index de2fc5bd39..e44d91ec34 100644
--- a/vm_insnhelper.h
+++ b/vm_insnhelper.h
@@ -12,7 +12,14 @@
#ifndef RUBY_INSNHELPER_H
#define RUBY_INSNHELPER_H
-extern VALUE ruby_vm_const_missing_count;
+RUBY_SYMBOL_EXPORT_BEGIN
+
+RUBY_EXTERN VALUE ruby_vm_const_missing_count;
+RUBY_EXTERN rb_serial_t ruby_vm_global_method_state;
+RUBY_EXTERN rb_serial_t ruby_vm_global_constant_state;
+RUBY_EXTERN rb_serial_t ruby_vm_class_serial;
+
+RUBY_SYMBOL_EXPORT_END
#if VM_COLLECT_USAGE_DETAILS
#define COLLECT_USAGE_INSN(insn) vm_collect_usage_insn(insn)
@@ -29,6 +36,17 @@ extern VALUE ruby_vm_const_missing_count;
/* deal with stack */
/**********************************************************/
+static inline int
+rb_obj_hidden_p(VALUE obj)
+{
+ if (SPECIAL_CONST_P(obj)) {
+ return FALSE;
+ }
+ else {
+ return RBASIC_CLASS(obj) ? FALSE : TRUE;
+ }
+}
+
#define PUSH(x) (SET_SV(x), INC_SP(1))
#define TOPN(n) (*(GET_SP()-(n)-1))
#define POPN(n) (DEC_SP(n))
@@ -47,7 +65,7 @@ extern VALUE ruby_vm_const_missing_count;
#define VM_REG_EP (VM_REG_CFP->ep)
#define RESTORE_REGS() do { \
- VM_REG_CFP = th->cfp; \
+ VM_REG_CFP = ec->cfp; \
} while (0)
#define REG_A reg_a
@@ -59,11 +77,11 @@ enum vm_regan_regtype {
VM_REGAN_EP = 2,
VM_REGAN_CFP = 3,
VM_REGAN_SELF = 4,
- VM_REGAN_ISEQ = 5,
+ VM_REGAN_ISEQ = 5
};
enum vm_regan_acttype {
VM_REGAN_ACT_GET = 0,
- VM_REGAN_ACT_SET = 1,
+ VM_REGAN_ACT_SET = 1
};
#if VM_COLLECT_USAGE_DETAILS
@@ -79,7 +97,7 @@ enum vm_regan_acttype {
#define GET_CURRENT_INSN() (*GET_PC())
#define GET_OPERAND(n) (GET_PC()[(n)])
#define ADD_PC(n) (SET_PC(VM_REG_PC + (n)))
-#define JUMP(dst) (VM_REG_PC += (dst))
+#define JUMP(dst) (SET_PC(VM_REG_PC + (dst)))
/* frame pointer, environment pointer */
#define GET_CFP() (COLLECT_USAGE_REGISTER_HELPER(CFP, GET, VM_REG_CFP))
@@ -95,8 +113,6 @@ enum vm_regan_acttype {
#define SET_SV(x) (*GET_SP() = (x))
/* set current stack value as x */
-#define GET_SP_COUNT() (VM_REG_SP - th->stack)
-
/* instruction sequence C struct */
#define GET_ISEQ() (GET_CFP()->iseq)
@@ -121,11 +137,36 @@ enum vm_regan_acttype {
/* deal with control flow 2: method/iterator */
/**********************************************************/
+#ifdef MJIT_HEADER
+/* When calling ISeq which may catch an exception from JIT-ed code, we should not call
+ mjit_exec directly to prevent the caller frame from being canceled. That's because
+ the caller frame may have stack values in the local variables and the cancelling
+ the caller frame will purge them. But directly calling mjit_exec is faster... */
+#define EXEC_EC_CFP(val) do { \
+ if (ec->cfp->iseq->body->catch_except_p) { \
+ VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH); \
+ val = vm_exec(ec, TRUE); \
+ } \
+ else if ((val = mjit_exec(ec)) == Qundef) { \
+ VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH); \
+ val = vm_exec(ec, FALSE); \
+ } \
+} while (0)
+#else
+/* When calling from VM, longjmp in the callee won't purge any JIT-ed caller frames.
+ So it's safe to directly call mjit_exec. */
+#define EXEC_EC_CFP(val) do { \
+ if ((val = mjit_exec(ec)) == Qundef) { \
+ RESTORE_REGS(); \
+ NEXT_INSN(); \
+ } \
+} while (0)
+#endif
+
#define CALL_METHOD(calling, ci, cc) do { \
- VALUE v = (*(cc)->call)(th, GET_CFP(), (calling), (ci), (cc)); \
+ VALUE v = (*(cc)->call)(ec, GET_CFP(), (calling), (ci), (cc)); \
if (v == Qundef) { \
- RESTORE_REGS(); \
- NEXT_INSN(); \
+ EXEC_EC_CFP(val); \
} \
else { \
val = v; \
@@ -136,17 +177,9 @@ enum vm_regan_acttype {
* because inline method cache does not care about receiver.
*/
-#ifndef OPT_CALL_FASTPATH
-#define OPT_CALL_FASTPATH 1
-#endif
-
-#if OPT_CALL_FASTPATH
-#define CI_SET_FASTPATH(cc, func, enabled) do { \
+#define CC_SET_FASTPATH(cc, func, enabled) do { \
if (LIKELY(enabled)) ((cc)->call = (func)); \
} while (0)
-#else
-#define CI_SET_FASTPATH(ci, func, enabled) /* do nothing */
-#endif
#define GET_BLOCK_HANDLER() (GET_LEP()[VM_ENV_DATA_INDEX_SPECVAL])
@@ -156,6 +189,25 @@ enum vm_regan_acttype {
/**********************************************************/
+/* deal with stack canary */
+/**********************************************************/
+
+#if VM_CHECK_MODE > 0
+#define SETUP_CANARY() \
+ if (leaf) { \
+ canary = GET_SP(); \
+ SET_SV(vm_stack_canary); \
+ }
+#define CHECK_CANARY() \
+ if (leaf && (*canary != vm_stack_canary)) { \
+ vm_canary_is_found_dead(INSN_ATTR(bin), *canary); \
+ }
+#else
+#define SETUP_CANARY() /* void */
+#define CHECK_CANARY() /* void */
+#endif
+
+/**********************************************************/
/* others */
/**********************************************************/
@@ -166,18 +218,22 @@ enum vm_regan_acttype {
#else
#define FLONUM_2_P(a, b) 0
#endif
+#define FLOAT_HEAP_P(x) (!SPECIAL_CONST_P(x) && RBASIC_CLASS(x) == rb_cFloat)
+#define FLOAT_INSTANCE_P(x) (FLONUM_P(x) || FLOAT_HEAP_P(x))
#ifndef USE_IC_FOR_SPECIALIZED_METHOD
#define USE_IC_FOR_SPECIALIZED_METHOD 1
#endif
-#define CALL_SIMPLE_METHOD(recv_) do { \
- struct rb_calling_info calling; \
- calling.block_handler = VM_BLOCK_HANDLER_NONE; \
- calling.argc = ci->orig_argc; \
- vm_search_method(ci, cc, calling.recv = (recv_)); \
- CALL_METHOD(&calling, ci, cc); \
+#ifndef MJIT_HEADER
+#define CALL_SIMPLE_METHOD() do { \
+ rb_snum_t x = leaf ? INSN_ATTR(width) : 0; \
+ rb_snum_t y = attr_width_opt_send_without_block(0, 0); \
+ rb_snum_t z = x - y; \
+ ADD_PC(z); \
+ DISPATCH_ORIGINAL_INSN(opt_send_without_block); \
} while (0)
+#endif
#define NEXT_CLASS_SERIAL() (++ruby_vm_class_serial)
#define GET_GLOBAL_METHOD_STATE() (ruby_vm_global_method_state)
@@ -185,8 +241,12 @@ enum vm_regan_acttype {
#define GET_GLOBAL_CONSTANT_STATE() (ruby_vm_global_constant_state)
#define INC_GLOBAL_CONSTANT_STATE() (++ruby_vm_global_constant_state)
-static VALUE make_no_method_exception(VALUE exc, VALUE format, VALUE obj,
- int argc, const VALUE *argv, int priv);
+extern rb_method_definition_t *rb_method_definition_create(rb_method_type_t type, ID mid);
+extern void rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def, void *opts);
+extern int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2);
+
+extern VALUE rb_make_no_method_exception(VALUE exc, VALUE format, VALUE obj,
+ int argc, const VALUE *argv, int priv);
static inline struct vm_throw_data *
THROW_DATA_NEW(VALUE val, const rb_control_frame_t *cf, VALUE st)
@@ -194,34 +254,63 @@ THROW_DATA_NEW(VALUE val, const rb_control_frame_t *cf, VALUE st)
return (struct vm_throw_data *)rb_imemo_new(imemo_throw_data, val, (VALUE)cf, st, 0);
}
-static inline void
-THROW_DATA_CATCH_FRAME_SET(struct vm_throw_data *obj, const rb_control_frame_t *cfp)
-{
- obj->catch_frame = cfp;
-}
-
-static inline void
-THROW_DATA_STATE_SET(struct vm_throw_data *obj, int st)
-{
- obj->throw_state = (VALUE)st;
-}
-
static inline VALUE
THROW_DATA_VAL(const struct vm_throw_data *obj)
{
+ VM_ASSERT(THROW_DATA_P(obj));
return obj->throw_obj;
}
static inline const rb_control_frame_t *
THROW_DATA_CATCH_FRAME(const struct vm_throw_data *obj)
{
+ VM_ASSERT(THROW_DATA_P(obj));
return obj->catch_frame;
}
-static int
+static inline int
THROW_DATA_STATE(const struct vm_throw_data *obj)
{
+ VM_ASSERT(THROW_DATA_P(obj));
return (int)obj->throw_state;
}
+static inline int
+THROW_DATA_CONSUMED_P(const struct vm_throw_data *obj)
+{
+ VM_ASSERT(THROW_DATA_P(obj));
+ return obj->flags & THROW_DATA_CONSUMED;
+}
+
+static inline void
+THROW_DATA_CATCH_FRAME_SET(struct vm_throw_data *obj, const rb_control_frame_t *cfp)
+{
+ VM_ASSERT(THROW_DATA_P(obj));
+ obj->catch_frame = cfp;
+}
+
+static inline void
+THROW_DATA_STATE_SET(struct vm_throw_data *obj, int st)
+{
+ VM_ASSERT(THROW_DATA_P(obj));
+ obj->throw_state = (VALUE)st;
+}
+
+static inline void
+THROW_DATA_CONSUMED_SET(struct vm_throw_data *obj)
+{
+ if (THROW_DATA_P(obj) &&
+ THROW_DATA_STATE(obj) == TAG_BREAK) {
+ obj->flags |= THROW_DATA_CONSUMED;
+ }
+}
+
+#define IS_ARGS_SPLAT(ci) ((ci)->flag & VM_CALL_ARGS_SPLAT)
+#define IS_ARGS_KEYWORD(ci) ((ci)->flag & VM_CALL_KWARG)
+
+#define CALLER_SETUP_ARG(cfp, calling, ci) do { \
+ if (UNLIKELY(IS_ARGS_SPLAT(ci))) vm_caller_setup_arg_splat((cfp), (calling)); \
+ if (UNLIKELY(IS_ARGS_KEYWORD(ci))) vm_caller_setup_arg_kw((cfp), (calling), (ci)); \
+} while (0)
+
#endif /* RUBY_INSNHELPER_H */
diff --git a/vm_method.c b/vm_method.c
index 33e90cf9f1..ba219e2d99 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -62,6 +62,7 @@ static struct {
static void
rb_class_clear_method_cache(VALUE klass, VALUE arg)
{
+ mjit_remove_class_serial(RCLASS_SERIAL(klass));
RCLASS_SERIAL(klass) = rb_next_class_serial();
if (RB_TYPE_P(klass, T_ICLASS)) {
@@ -81,12 +82,6 @@ rb_class_clear_method_cache(VALUE klass, VALUE arg)
}
void
-rb_clear_cache(void)
-{
- ONLY_FOR_INTERNAL_USE("rb_clear_cache()");
-}
-
-void
rb_clear_constant_cache(void)
{
INC_GLOBAL_CONSTANT_STATE();
@@ -123,7 +118,7 @@ rb_f_notimplement(int argc, const VALUE *argv, VALUE obj)
{
rb_notimplement();
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
static void
@@ -157,14 +152,16 @@ rb_method_definition_release(rb_method_definition_t *def, int complemented)
VM_ASSERT(complemented_count >= 0);
if (alias_count + complemented_count == 0) {
- if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:%d,%d (remove)\n", def, rb_id2name(def->original_id), alias_count, complemented_count);
+ if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:%d,%d (remove)\n", (void *)def,
+ rb_id2name(def->original_id), alias_count, complemented_count);
+ VM_ASSERT(def->type == VM_METHOD_TYPE_BMETHOD ? def->body.bmethod.hooks == NULL : TRUE);
xfree(def);
}
else {
if (complemented) def->complemented_count--;
else if (def->alias_count > 0) def->alias_count--;
- if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:%d->%d,%d->%d (dec)\n", def, rb_id2name(def->original_id),
+ if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:%d->%d,%d->%d (dec)\n", (void *)def, rb_id2name(def->original_id),
alias_count, def->alias_count, complemented_count, def->complemented_count);
}
}
@@ -177,7 +174,7 @@ rb_free_method_entry(const rb_method_entry_t *me)
}
static inline rb_method_entry_t *search_method(VALUE klass, ID id, VALUE *defined_class_ptr);
-static int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2);
+extern int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2);
static inline rb_method_entry_t *
lookup_method_table(VALUE klass, ID id)
@@ -228,8 +225,8 @@ setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(), int argc)
cfunc->invoker = call_cfunc_invoker_func(argc);
}
-static void
-method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def, void *opts)
+MJIT_FUNC_EXPORTED void
+rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def, void *opts)
{
*(rb_method_definition_t **)&me->def = def;
@@ -249,7 +246,7 @@ method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def,
method_cref = cref;
}
else {
- method_cref = vm_cref_new_toplevel(GET_THREAD()); /* TODO: can we reuse? */
+ method_cref = vm_cref_new_toplevel(GET_EC()); /* TODO: can we reuse? */
}
RB_OBJ_WRITE(me, &def->body.iseq.cref, method_cref);
@@ -258,22 +255,22 @@ method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def,
case VM_METHOD_TYPE_CFUNC:
{
rb_method_cfunc_t *cfunc = (rb_method_cfunc_t *)opts;
- setup_method_cfunc_struct(&def->body.cfunc, cfunc->func, cfunc->argc);
+ setup_method_cfunc_struct(UNALIGNED_MEMBER_PTR(def, body.cfunc), cfunc->func, cfunc->argc);
return;
}
case VM_METHOD_TYPE_ATTRSET:
case VM_METHOD_TYPE_IVAR:
{
- rb_thread_t *th = GET_THREAD();
+ const rb_execution_context_t *ec = GET_EC();
rb_control_frame_t *cfp;
int line;
def->body.attr.id = (ID)(VALUE)opts;
- cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
+ cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
if (cfp && (line = rb_vm_get_sourceline(cfp))) {
- VALUE location = rb_ary_new3(2, cfp->iseq->body->location.path, INT2FIX(line));
+ VALUE location = rb_ary_new3(2, rb_iseq_path(cfp->iseq), INT2FIX(line));
RB_OBJ_WRITE(me, &def->body.attr.location, rb_ary_freeze(location));
}
else {
@@ -282,10 +279,10 @@ method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def,
return;
}
case VM_METHOD_TYPE_BMETHOD:
- RB_OBJ_WRITE(me, &def->body.proc, (VALUE)opts);
+ RB_OBJ_WRITE(me, &def->body.bmethod.proc, (VALUE)opts);
return;
case VM_METHOD_TYPE_NOTIMPLEMENTED:
- setup_method_cfunc_struct(&def->body.cfunc, rb_f_notimplement, -1);
+ setup_method_cfunc_struct(UNALIGNED_MEMBER_PTR(def, body.cfunc), rb_f_notimplement, -1);
return;
case VM_METHOD_TYPE_OPTIMIZED:
def->body.optimize_type = (enum method_optimized_type)opts;
@@ -323,7 +320,9 @@ method_definition_reset(const rb_method_entry_t *me)
RB_OBJ_WRITTEN(me, Qundef, def->body.attr.location);
break;
case VM_METHOD_TYPE_BMETHOD:
- RB_OBJ_WRITTEN(me, Qundef, def->body.proc);
+ RB_OBJ_WRITTEN(me, Qundef, def->body.bmethod.proc);
+ /* give up to check all in a list */
+ if (def->body.bmethod.hooks) rb_gc_writebarrier_remember((VALUE)me);
break;
case VM_METHOD_TYPE_REFINED:
RB_OBJ_WRITTEN(me, Qundef, def->body.refined.orig_me);
@@ -342,8 +341,8 @@ method_definition_reset(const rb_method_entry_t *me)
}
}
-static rb_method_definition_t *
-method_definition_create(rb_method_type_t type, ID mid)
+MJIT_FUNC_EXPORTED rb_method_definition_t *
+rb_method_definition_create(rb_method_type_t type, ID mid)
{
rb_method_definition_t *def;
def = ZALLOC(rb_method_definition_t);
@@ -356,7 +355,7 @@ static rb_method_definition_t *
method_definition_addref(rb_method_definition_t *def)
{
def->alias_count++;
- if (METHOD_DEBUG) fprintf(stderr, "+%p-%s:%d\n", def, rb_id2name(def->original_id), def->alias_count);
+ if (METHOD_DEBUG) fprintf(stderr, "+%p-%s:%d\n", (void *)def, rb_id2name(def->original_id), def->alias_count);
return def;
}
@@ -364,7 +363,7 @@ static rb_method_definition_t *
method_definition_addref_complement(rb_method_definition_t *def)
{
def->complemented_count++;
- if (METHOD_DEBUG) fprintf(stderr, "+%p-%s:%d\n", def, rb_id2name(def->original_id), def->alias_count);
+ if (METHOD_DEBUG) fprintf(stderr, "+%p-%s:%d\n", (void *)def, rb_id2name(def->original_id), def->complemented_count);
return def;
}
@@ -407,13 +406,36 @@ rb_method_entry_clone(const rb_method_entry_t *src_me)
return me;
}
-const rb_callable_method_entry_t *
+MJIT_FUNC_EXPORTED const rb_callable_method_entry_t *
rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, ID called_id, VALUE defined_class)
{
- rb_method_entry_t *me = rb_method_entry_alloc(called_id, src_me->owner, defined_class,
- method_definition_addref_complement(src_me->def));
+ rb_method_definition_t *def = src_me->def;
+ rb_method_entry_t *me;
+ struct {
+ const struct rb_method_entry_struct *orig_me;
+ VALUE owner;
+ } refined = {0};
+
+ if (!src_me->defined_class &&
+ def->type == VM_METHOD_TYPE_REFINED &&
+ def->body.refined.orig_me) {
+ const rb_method_entry_t *orig_me =
+ rb_method_entry_clone(def->body.refined.orig_me);
+ RB_OBJ_WRITE((VALUE)orig_me, &orig_me->defined_class, defined_class);
+ refined.orig_me = orig_me;
+ refined.owner = orig_me->owner;
+ def = NULL;
+ }
+ else {
+ def = method_definition_addref_complement(def);
+ }
+ me = rb_method_entry_alloc(called_id, src_me->owner, defined_class, def);
METHOD_ENTRY_FLAGS_COPY(me, src_me);
METHOD_ENTRY_COMPLEMENTED_SET(me);
+ if (!def) {
+ def = rb_method_definition_create(VM_METHOD_TYPE_REFINED, called_id);
+ rb_method_definition_set(me, def, &refined);
+ }
VM_ASSERT(RB_TYPE_P(me->owner, T_MODULE));
@@ -442,6 +464,7 @@ make_method_entry_refined(VALUE owner, rb_method_entry_t *me)
struct rb_method_entry_struct *orig_me;
VALUE owner;
} refined;
+ rb_method_definition_t *def;
rb_vm_check_redefinition_opt_method(me, me->owner);
@@ -453,7 +476,8 @@ make_method_entry_refined(VALUE owner, rb_method_entry_t *me)
METHOD_ENTRY_FLAGS_COPY(refined.orig_me, me);
refined.owner = owner;
- method_definition_set(me, method_definition_create(VM_METHOD_TYPE_REFINED, me->called_id), (void *)&refined);
+ def = rb_method_definition_create(VM_METHOD_TYPE_REFINED, me->called_id);
+ rb_method_definition_set(me, def, (void *)&refined);
METHOD_ENTRY_VISI_SET(me, METHOD_VISI_PUBLIC);
}
}
@@ -519,7 +543,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
}
}
- rb_frozen_class_p(klass);
+ rb_class_modify_check(klass);
if (FL_TEST(klass, RMODULE_IS_REFINEMENT)) {
VALUE refined_class = rb_refinement_module_get_refined_class(klass);
@@ -547,6 +571,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
if (RTEST(ruby_verbose) &&
type != VM_METHOD_TYPE_UNDEF &&
(old_def->alias_count == 0) &&
+ !make_refined &&
old_def->type != VM_METHOD_TYPE_UNDEF &&
old_def->type != VM_METHOD_TYPE_ZSUPER &&
old_def->type != VM_METHOD_TYPE_ALIAS) {
@@ -558,14 +583,14 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
iseq = def_iseq_ptr(old_def);
break;
case VM_METHOD_TYPE_BMETHOD:
- iseq = rb_proc_get_iseq(old_def->body.proc, 0);
+ iseq = rb_proc_get_iseq(old_def->body.bmethod.proc, 0);
break;
default:
break;
}
- if (iseq && !NIL_P(iseq->body->location.path)) {
- int line = iseq->body->line_info_table ? FIX2INT(rb_iseq_first_lineno(iseq)) : 0;
- rb_compile_warning(RSTRING_PTR(iseq->body->location.path), line,
+ if (iseq) {
+ rb_compile_warning(RSTRING_PTR(rb_iseq_path(iseq)),
+ FIX2INT(iseq->body->location.first_lineno),
"previous definition of %"PRIsVALUE" was here",
rb_id2str(old_def->original_id));
}
@@ -574,14 +599,20 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
/* create method entry */
me = rb_method_entry_create(mid, defined_class, visi, NULL);
- if (def == NULL) def = method_definition_create(type, original_id);
- method_definition_set(me, def, opts);
+ if (def == NULL) def = rb_method_definition_create(type, original_id);
+ rb_method_definition_set(me, def, opts);
rb_clear_method_cache_by_class(klass);
/* check mid */
- if (klass == rb_cObject && mid == idInitialize) {
- rb_warn("redefining Object#initialize may cause infinite loop");
+ if (klass == rb_cObject) {
+ switch (mid) {
+ case idInitialize:
+ case idRespond_to_missing:
+ case idMethodMissing:
+ case idRespond_to:
+ rb_warn("redefining Object#%s may cause infinite loop", rb_id2name(mid));
+ }
}
/* check mid */
if (mid == object_id || mid == id__send__) {
@@ -700,7 +731,8 @@ search_method(VALUE klass, ID id, VALUE *defined_class_ptr)
{
rb_method_entry_t *me;
- for (me = 0; klass; klass = RCLASS_SUPER(klass)) {
+ for (; klass; klass = RCLASS_SUPER(klass)) {
+ RB_DEBUG_COUNTER_INC(mc_search_super);
if ((me = lookup_method_table(klass, id)) != 0) break;
}
@@ -784,14 +816,16 @@ method_entry_get(VALUE klass, ID id, VALUE *defined_class_ptr)
verify_method_cache(klass, id, ent->defined_class, ent->me);
#endif
if (defined_class_ptr) *defined_class_ptr = ent->defined_class;
+ RB_DEBUG_COUNTER_INC(mc_global_hit);
return ent->me;
}
#endif
+ RB_DEBUG_COUNTER_INC(mc_global_miss);
return method_entry_get_without_cache(klass, id, defined_class_ptr);
}
-const rb_method_entry_t *
+MJIT_FUNC_EXPORTED const rb_method_entry_t *
rb_method_entry(VALUE klass, ID id)
{
return method_entry_get(klass, id, NULL);
@@ -804,18 +838,21 @@ prepare_callable_method_entry(VALUE defined_class, ID id, const rb_method_entry_
const rb_callable_method_entry_t *cme;
if (me && me->defined_class == 0) {
+ RB_DEBUG_COUNTER_INC(mc_cme_complement);
VM_ASSERT(RB_TYPE_P(defined_class, T_ICLASS) || RB_TYPE_P(defined_class, T_MODULE));
VM_ASSERT(me->defined_class == 0);
- if ((mtbl = RCLASS_CALLABLE_M_TBL(defined_class)) == NULL) {
- mtbl = RCLASS_EXT(defined_class)->callable_m_tbl = rb_id_table_create(0);
- }
+ mtbl = RCLASS_CALLABLE_M_TBL(defined_class);
- if (rb_id_table_lookup(mtbl, id, (VALUE *)&me)) {
+ if (mtbl && rb_id_table_lookup(mtbl, id, (VALUE *)&me)) {
+ RB_DEBUG_COUNTER_INC(mc_cme_complement_hit);
cme = (rb_callable_method_entry_t *)me;
VM_ASSERT(callable_method_entry_p(cme));
}
else {
+ if (!mtbl) {
+ mtbl = RCLASS_EXT(defined_class)->callable_m_tbl = rb_id_table_create(0);
+ }
cme = rb_method_entry_complement_defined_class(me, me->called_id, defined_class);
rb_id_table_insert(mtbl, id, (VALUE)cme);
VM_ASSERT(callable_method_entry_p(cme));
@@ -829,7 +866,7 @@ prepare_callable_method_entry(VALUE defined_class, ID id, const rb_method_entry_
return cme;
}
-const rb_callable_method_entry_t *
+MJIT_FUNC_EXPORTED const rb_callable_method_entry_t *
rb_callable_method_entry(VALUE klass, ID id)
{
VALUE defined_class;
@@ -862,78 +899,59 @@ method_entry_resolve_refinement(VALUE klass, ID id, int with_refinement, VALUE *
return me;
}
-const rb_method_entry_t *
-rb_method_entry_with_refinements(VALUE klass, ID id)
-{
- return method_entry_resolve_refinement(klass, id, TRUE, NULL);
-}
-
-const rb_callable_method_entry_t *
-rb_callable_method_entry_with_refinements(VALUE klass, ID id)
+MJIT_FUNC_EXPORTED const rb_callable_method_entry_t *
+rb_callable_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class_ptr)
{
- VALUE defined_class;
- const rb_method_entry_t *me = method_entry_resolve_refinement(klass, id, TRUE, &defined_class);
- return prepare_callable_method_entry(defined_class, id, me);
+ VALUE defined_class, *dcp = defined_class_ptr ? defined_class_ptr : &defined_class;
+ const rb_method_entry_t *me = method_entry_resolve_refinement(klass, id, TRUE, dcp);
+ return prepare_callable_method_entry(*dcp, id, me);
}
const rb_method_entry_t *
-rb_method_entry_without_refinements(VALUE klass, ID id)
+rb_method_entry_without_refinements(VALUE klass, ID id, VALUE *defined_class_ptr)
{
- return method_entry_resolve_refinement(klass, id, FALSE, NULL);
+ return method_entry_resolve_refinement(klass, id, FALSE, defined_class_ptr);
}
-const rb_callable_method_entry_t *
-rb_callable_method_entry_without_refinements(VALUE klass, ID id)
+MJIT_FUNC_EXPORTED const rb_callable_method_entry_t *
+rb_callable_method_entry_without_refinements(VALUE klass, ID id, VALUE *defined_class_ptr)
{
- VALUE defined_class;
- const rb_method_entry_t *me = method_entry_resolve_refinement(klass, id, FALSE, &defined_class);
- return prepare_callable_method_entry(defined_class, id, me);
-}
-
-static const rb_method_entry_t *
-refined_method_original_method_entry(VALUE refinements, const rb_method_entry_t *me, VALUE *defined_class_ptr)
-{
- VALUE super;
-
- if (me->def->body.refined.orig_me) {
- if (defined_class_ptr) *defined_class_ptr = me->def->body.refined.orig_me->defined_class;
- return me->def->body.refined.orig_me;
- }
- else if (!(super = RCLASS_SUPER(me->owner))) {
- return 0;
- }
- else {
- rb_method_entry_t *tmp_me;
- tmp_me = method_entry_get(super, me->called_id, defined_class_ptr);
- return resolve_refined_method(refinements, tmp_me, defined_class_ptr);
- }
+ VALUE defined_class, *dcp = defined_class_ptr ? defined_class_ptr : &defined_class;
+ const rb_method_entry_t *me = method_entry_resolve_refinement(klass, id, FALSE, dcp);
+ return prepare_callable_method_entry(*dcp, id, me);
}
static const rb_method_entry_t *
resolve_refined_method(VALUE refinements, const rb_method_entry_t *me, VALUE *defined_class_ptr)
{
- if (me && me->def->type == VM_METHOD_TYPE_REFINED) {
+ while (me && me->def->type == VM_METHOD_TYPE_REFINED) {
VALUE refinement;
- rb_method_entry_t *tmp_me;
+ const rb_method_entry_t *tmp_me;
+ VALUE super;
refinement = find_refinement(refinements, me->owner);
- if (NIL_P(refinement)) {
- return refined_method_original_method_entry(refinements, me, defined_class_ptr);
- }
- else {
+ if (!NIL_P(refinement)) {
tmp_me = method_entry_get(refinement, me->called_id, defined_class_ptr);
if (tmp_me && tmp_me->def->type != VM_METHOD_TYPE_REFINED) {
return tmp_me;
}
- else {
- return refined_method_original_method_entry(refinements, me, defined_class_ptr);
- }
}
+
+ tmp_me = me->def->body.refined.orig_me;
+ if (tmp_me) {
+ if (defined_class_ptr) *defined_class_ptr = tmp_me->defined_class;
+ return tmp_me;
+ }
+
+ super = RCLASS_SUPER(me->owner);
+ if (!super) {
+ return 0;
+ }
+
+ me = method_entry_get(super, me->called_id, defined_class_ptr);
}
- else {
- return me;
- }
+ return me;
}
const rb_method_entry_t *
@@ -964,7 +982,7 @@ remove_method(VALUE klass, ID mid)
VALUE self = klass;
klass = RCLASS_ORIGIN(klass);
- rb_frozen_class_p(klass);
+ rb_class_modify_check(klass);
if (mid == object_id || mid == id__send__ || mid == idInitialize) {
rb_warn("removing `%s' may cause serious problems", rb_id2name(mid));
}
@@ -1033,8 +1051,9 @@ rb_export_method(VALUE klass, ID name, rb_method_visibility_t visi)
{
rb_method_entry_t *me;
VALUE defined_class;
+ VALUE origin_class = RCLASS_ORIGIN(klass);
- me = search_method(klass, name, &defined_class);
+ me = search_method(origin_class, name, &defined_class);
if (!me && RB_TYPE_P(klass, T_MODULE)) {
me = search_method(rb_cObject, name, &defined_class);
}
@@ -1047,7 +1066,7 @@ rb_export_method(VALUE klass, ID name, rb_method_visibility_t visi)
if (METHOD_ENTRY_VISI(me) != visi) {
rb_vm_check_redefinition_opt_method(me, klass);
- if (klass == defined_class || RCLASS_ORIGIN(klass) == defined_class) {
+ if (klass == defined_class || origin_class == defined_class) {
METHOD_ENTRY_VISI_SET(me, visi);
if (me->def->type == VM_METHOD_TYPE_REFINED && me->def->body.refined.orig_me) {
@@ -1067,7 +1086,14 @@ rb_export_method(VALUE klass, ID name, rb_method_visibility_t visi)
int
rb_method_boundp(VALUE klass, ID id, int ex)
{
- const rb_method_entry_t *me = rb_method_entry_without_refinements(klass, id);
+ const rb_method_entry_t *me;
+
+ if (ex & BOUND_RESPONDS) {
+ me = method_entry_resolve_refinement(klass, id, TRUE, NULL);
+ }
+ else {
+ me = rb_method_entry_without_refinements(klass, id, NULL);
+ }
if (me != 0) {
if ((ex & ~BOUND_RESPONDS) &&
@@ -1088,8 +1114,8 @@ rb_method_boundp(VALUE klass, ID id, int ex)
static rb_method_visibility_t
rb_scope_visibility_get(void)
{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
+ const rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
if (!vm_env_cref_by_cref(cfp->ep)) {
return METHOD_VISI_PUBLIC;
@@ -1102,8 +1128,8 @@ rb_scope_visibility_get(void)
static int
rb_scope_module_func_check(void)
{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
+ const rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
if (!vm_env_cref_by_cref(cfp->ep)) {
return FALSE;
@@ -1171,17 +1197,20 @@ rb_attr(VALUE klass, ID id, int read, int write, int ex)
void
rb_undef(VALUE klass, ID id)
{
- rb_method_entry_t *me;
+ const rb_method_entry_t *me;
if (NIL_P(klass)) {
rb_raise(rb_eTypeError, "no class to undef method");
}
- rb_frozen_class_p(klass);
+ rb_class_modify_check(klass);
if (id == object_id || id == id__send__ || id == idInitialize) {
rb_warn("undefining `%s' may cause serious problems", rb_id2name(id));
}
me = search_method(klass, id, 0);
+ if (me && me->def->type == VM_METHOD_TYPE_REFINED) {
+ me = rb_resolve_refined_method(Qnil, me);
+ }
if (UNDEFINED_METHOD_ENTRY_P(me) ||
UNDEFINED_REFINED_METHOD_P(me->def)) {
@@ -1253,14 +1282,44 @@ rb_mod_undef_method(int argc, VALUE *argv, VALUE mod)
return mod;
}
+static rb_method_visibility_t
+check_definition_visibility(VALUE mod, int argc, VALUE *argv)
+{
+ const rb_method_entry_t *me;
+ VALUE mid, include_super, lookup_mod = mod;
+ int inc_super;
+ ID id;
+
+ rb_scan_args(argc, argv, "11", &mid, &include_super);
+ id = rb_check_id(&mid);
+ if (!id) return METHOD_VISI_UNDEF;
+
+ if (argc == 1) {
+ inc_super = 1;
+ } else {
+ inc_super = RTEST(include_super);
+ if (!inc_super) {
+ lookup_mod = RCLASS_ORIGIN(mod);
+ }
+ }
+
+ me = rb_method_entry_without_refinements(lookup_mod, id, NULL);
+ if (me) {
+ if (me->def->type == VM_METHOD_TYPE_NOTIMPLEMENTED) return METHOD_VISI_UNDEF;
+ if (!inc_super && me->owner != mod) return METHOD_VISI_UNDEF;
+ return METHOD_ENTRY_VISI(me);
+ }
+ return METHOD_VISI_UNDEF;
+}
+
/*
* call-seq:
- * mod.method_defined?(symbol) -> true or false
- * mod.method_defined?(string) -> true or false
+ * mod.method_defined?(symbol, inherit=true) -> true or false
+ * mod.method_defined?(string, inherit=true) -> true or false
*
* Returns +true+ if the named method is defined by
- * _mod_ (or its included modules and, if _mod_ is a class,
- * its ancestors). Public and protected methods are matched.
+ * _mod_. If _inherit_ is set, the lookup will also search _mod_'s
+ * ancestors. Public and protected methods are matched.
* String arguments are converted to symbols.
*
* module A
@@ -1281,6 +1340,8 @@ rb_mod_undef_method(int argc, VALUE *argv, VALUE mod)
* A.method_defined? :method1 #=> true
* C.method_defined? "method1" #=> true
* C.method_defined? "method2" #=> true
+ * C.method_defined? "method2", true #=> true
+ * C.method_defined? "method2", false #=> false
* C.method_defined? "method3" #=> true
* C.method_defined? "protected_method1" #=> true
* C.method_defined? "method4" #=> false
@@ -1288,37 +1349,26 @@ rb_mod_undef_method(int argc, VALUE *argv, VALUE mod)
*/
static VALUE
-rb_mod_method_defined(VALUE mod, VALUE mid)
+rb_mod_method_defined(int argc, VALUE *argv, VALUE mod)
{
- ID id = rb_check_id(&mid);
- if (!id || !rb_method_boundp(mod, id, 1)) {
- return Qfalse;
- }
- return Qtrue;
-
+ rb_method_visibility_t visi = check_definition_visibility(mod, argc, argv);
+ return (visi == METHOD_VISI_PUBLIC || visi == METHOD_VISI_PROTECTED) ? Qtrue : Qfalse;
}
static VALUE
-check_definition(VALUE mod, VALUE mid, rb_method_visibility_t visi)
+check_definition(VALUE mod, int argc, VALUE *argv, rb_method_visibility_t visi)
{
- const rb_method_entry_t *me;
- ID id = rb_check_id(&mid);
- if (!id) return Qfalse;
- me = rb_method_entry_without_refinements(mod, id);
- if (me) {
- if (METHOD_ENTRY_VISI(me) == visi) return Qtrue;
- }
- return Qfalse;
+ return (check_definition_visibility(mod, argc, argv) == visi) ? Qtrue : Qfalse;
}
/*
* call-seq:
- * mod.public_method_defined?(symbol) -> true or false
- * mod.public_method_defined?(string) -> true or false
+ * mod.public_method_defined?(symbol, inherit=true) -> true or false
+ * mod.public_method_defined?(string, inherit=true) -> true or false
*
* Returns +true+ if the named public method is defined by
- * _mod_ (or its included modules and, if _mod_ is a class,
- * its ancestors).
+ * _mod_. If _inherit_ is set, the lookup will also search _mod_'s
+ * ancestors.
* String arguments are converted to symbols.
*
* module A
@@ -1333,26 +1383,28 @@ check_definition(VALUE mod, VALUE mid, rb_method_visibility_t visi)
* def method3() end
* end
*
- * A.method_defined? :method1 #=> true
- * C.public_method_defined? "method1" #=> true
- * C.public_method_defined? "method2" #=> false
- * C.method_defined? "method2" #=> true
+ * A.method_defined? :method1 #=> true
+ * C.public_method_defined? "method1" #=> true
+ * C.public_method_defined? "method1", true #=> true
+ * C.public_method_defined? "method1", false #=> true
+ * C.public_method_defined? "method2" #=> false
+ * C.method_defined? "method2" #=> true
*/
static VALUE
-rb_mod_public_method_defined(VALUE mod, VALUE mid)
+rb_mod_public_method_defined(int argc, VALUE *argv, VALUE mod)
{
- return check_definition(mod, mid, METHOD_VISI_PUBLIC);
+ return check_definition(mod, argc, argv, METHOD_VISI_PUBLIC);
}
/*
* call-seq:
- * mod.private_method_defined?(symbol) -> true or false
- * mod.private_method_defined?(string) -> true or false
+ * mod.private_method_defined?(symbol, inherit=true) -> true or false
+ * mod.private_method_defined?(string, inherit=true) -> true or false
*
* Returns +true+ if the named private method is defined by
- * _ mod_ (or its included modules and, if _mod_ is a class,
- * its ancestors).
+ * _mod_. If _inherit_ is set, the lookup will also search _mod_'s
+ * ancestors.
* String arguments are converted to symbols.
*
* module A
@@ -1367,26 +1419,28 @@ rb_mod_public_method_defined(VALUE mod, VALUE mid)
* def method3() end
* end
*
- * A.method_defined? :method1 #=> true
- * C.private_method_defined? "method1" #=> false
- * C.private_method_defined? "method2" #=> true
- * C.method_defined? "method2" #=> false
+ * A.method_defined? :method1 #=> true
+ * C.private_method_defined? "method1" #=> false
+ * C.private_method_defined? "method2" #=> true
+ * C.private_method_defined? "method2", true #=> true
+ * C.private_method_defined? "method2", false #=> false
+ * C.method_defined? "method2" #=> false
*/
static VALUE
-rb_mod_private_method_defined(VALUE mod, VALUE mid)
+rb_mod_private_method_defined(int argc, VALUE *argv, VALUE mod)
{
- return check_definition(mod, mid, METHOD_VISI_PRIVATE);
+ return check_definition(mod, argc, argv, METHOD_VISI_PRIVATE);
}
/*
* call-seq:
- * mod.protected_method_defined?(symbol) -> true or false
- * mod.protected_method_defined?(string) -> true or false
+ * mod.protected_method_defined?(symbol, inherit=true) -> true or false
+ * mod.protected_method_defined?(string, inherit=true) -> true or false
*
* Returns +true+ if the named protected method is defined
- * by _mod_ (or its included modules and, if _mod_ is a
- * class, its ancestors).
+ * _mod_. If _inherit_ is set, the lookup will also search _mod_'s
+ * ancestors.
* String arguments are converted to symbols.
*
* module A
@@ -1401,16 +1455,18 @@ rb_mod_private_method_defined(VALUE mod, VALUE mid)
* def method3() end
* end
*
- * A.method_defined? :method1 #=> true
- * C.protected_method_defined? "method1" #=> false
- * C.protected_method_defined? "method2" #=> true
- * C.method_defined? "method2" #=> true
+ * A.method_defined? :method1 #=> true
+ * C.protected_method_defined? "method1" #=> false
+ * C.protected_method_defined? "method2" #=> true
+ * C.protected_method_defined? "method2", true #=> true
+ * C.protected_method_defined? "method2", false #=> false
+ * C.method_defined? "method2" #=> true
*/
static VALUE
-rb_mod_protected_method_defined(VALUE mod, VALUE mid)
+rb_mod_protected_method_defined(int argc, VALUE *argv, VALUE mod)
{
- return check_definition(mod, mid, METHOD_VISI_PROTECTED);
+ return check_definition(mod, argc, argv, METHOD_VISI_PROTECTED);
}
int
@@ -1441,7 +1497,7 @@ original_method_definition(const rb_method_definition_t *def)
return def;
}
-static int
+MJIT_FUNC_EXPORTED int
rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2)
{
d1 = original_method_definition(d1);
@@ -1462,7 +1518,7 @@ rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_defini
case VM_METHOD_TYPE_IVAR:
return d1->body.attr.id == d2->body.attr.id;
case VM_METHOD_TYPE_BMETHOD:
- return RTEST(rb_equal(d1->body.proc, d2->body.proc));
+ return RTEST(rb_equal(d1->body.bmethod.proc, d2->body.bmethod.proc));
case VM_METHOD_TYPE_MISSING:
return d1->original_id == d2->original_id;
case VM_METHOD_TYPE_ZSUPER:
@@ -1496,7 +1552,7 @@ rb_hash_method_definition(st_index_t hash, const rb_method_definition_t *def)
case VM_METHOD_TYPE_IVAR:
return rb_hash_uint(hash, def->body.attr.id);
case VM_METHOD_TYPE_BMETHOD:
- return rb_hash_proc(hash, def->body.proc);
+ return rb_hash_proc(hash, def->body.bmethod.proc);
case VM_METHOD_TYPE_MISSING:
return rb_hash_uint(hash, def->original_id);
case VM_METHOD_TYPE_ZSUPER:
@@ -1530,7 +1586,7 @@ rb_alias(VALUE klass, ID alias_name, ID original_name)
rb_raise(rb_eTypeError, "no class to make alias");
}
- rb_frozen_class_p(klass);
+ rb_class_modify_check(klass);
again:
orig_me = search_method(klass, original_name, &defined_class);
@@ -1666,6 +1722,15 @@ rb_mod_public(int argc, VALUE *argv, VALUE module)
* defined methods to protected. With arguments, sets the named methods
* to have protected visibility.
* String arguments are converted to symbols.
+ *
+ * If a method has protected visibility, it is callable only where
+ * <code>self</code> of the context is the same as the method.
+ * (method definition or instance_eval). This behavior is different from
+ * Java's protected method. Usually <code>private</code> should be used.
+ *
+ * Note that a protected method is slow because it can't use inline cache.
+ *
+ * To show a private method on RDoc, use <code>:doc:</code> instead of this.
*/
static VALUE
@@ -1693,6 +1758,8 @@ rb_mod_protected(int argc, VALUE *argv, VALUE module)
* private :a
* end
* Mod.private_instance_methods #=> [:a, :c]
+ *
+ * Note that to show a private method on RDoc, use <code>:doc:</code>.
*/
static VALUE
@@ -1871,19 +1938,19 @@ rb_method_basic_definition_p(VALUE klass, ID id)
}
static VALUE
-call_method_entry(rb_thread_t *th, VALUE defined_class, VALUE obj, ID id,
+call_method_entry(rb_execution_context_t *ec, VALUE defined_class, VALUE obj, ID id,
const rb_method_entry_t *me, int argc, const VALUE *argv)
{
const rb_callable_method_entry_t *cme =
prepare_callable_method_entry(defined_class, id, me);
- VALUE passed_block_handler = vm_passed_block_handler(th);
- VALUE result = vm_call0(th, obj, id, argc, argv, cme);
- vm_passed_block_handler_set(th, passed_block_handler);
+ VALUE passed_block_handler = vm_passed_block_handler(ec);
+ VALUE result = rb_vm_call0(ec, obj, id, argc, argv, cme);
+ vm_passed_block_handler_set(ec, passed_block_handler);
return result;
}
static VALUE
-basic_obj_respond_to_missing(rb_thread_t *th, VALUE klass, VALUE obj,
+basic_obj_respond_to_missing(rb_execution_context_t *ec, VALUE klass, VALUE obj,
VALUE mid, VALUE priv)
{
VALUE defined_class, args[2];
@@ -1894,11 +1961,11 @@ basic_obj_respond_to_missing(rb_thread_t *th, VALUE klass, VALUE obj,
if (!me || METHOD_ENTRY_BASIC(me)) return Qundef;
args[0] = mid;
args[1] = priv;
- return call_method_entry(th, defined_class, obj, rtmid, me, 2, args);
+ return call_method_entry(ec, defined_class, obj, rtmid, me, 2, args);
}
static inline int
-basic_obj_respond_to(rb_thread_t *th, VALUE obj, ID id, int pub)
+basic_obj_respond_to(rb_execution_context_t *ec, VALUE obj, ID id, int pub)
{
VALUE klass = CLASS_OF(obj);
VALUE ret;
@@ -1907,7 +1974,7 @@ basic_obj_respond_to(rb_thread_t *th, VALUE obj, ID id, int pub)
case 2:
return FALSE;
case 0:
- ret = basic_obj_respond_to_missing(th, klass, obj, ID2SYM(id),
+ ret = basic_obj_respond_to_missing(ec, klass, obj, ID2SYM(id),
pub ? Qfalse : Qtrue);
return RTEST(ret) && ret != Qundef;
default:
@@ -1916,7 +1983,7 @@ basic_obj_respond_to(rb_thread_t *th, VALUE obj, ID id, int pub)
}
static int
-vm_respond_to(rb_thread_t *th, VALUE klass, VALUE obj, ID id, int priv)
+vm_respond_to(rb_execution_context_t *ec, VALUE klass, VALUE obj, ID id, int priv)
{
VALUE defined_class;
const ID resid = idRespond_to;
@@ -1961,7 +2028,7 @@ vm_respond_to(rb_thread_t *th, VALUE klass, VALUE obj, ID id, int priv)
}
}
}
- result = call_method_entry(th, defined_class, obj, resid, me, argc, args);
+ result = call_method_entry(ec, defined_class, obj, resid, me, argc, args);
return RTEST(result);
}
}
@@ -1969,10 +2036,10 @@ vm_respond_to(rb_thread_t *th, VALUE klass, VALUE obj, ID id, int priv)
int
rb_obj_respond_to(VALUE obj, ID id, int priv)
{
- rb_thread_t *th = GET_THREAD();
+ rb_execution_context_t *ec = GET_EC();
VALUE klass = CLASS_OF(obj);
- int ret = vm_respond_to(th, klass, obj, id, priv);
- if (ret == -1) ret = basic_obj_respond_to(th, obj, id, !priv);
+ int ret = vm_respond_to(ec, klass, obj, id, priv);
+ if (ret == -1) ret = basic_obj_respond_to(ec, obj, id, !priv);
return ret;
}
@@ -2008,16 +2075,16 @@ obj_respond_to(int argc, VALUE *argv, VALUE obj)
{
VALUE mid, priv;
ID id;
- rb_thread_t *th = GET_THREAD();
+ rb_execution_context_t *ec = GET_EC();
rb_scan_args(argc, argv, "11", &mid, &priv);
if (!(id = rb_check_id(&mid))) {
- VALUE ret = basic_obj_respond_to_missing(th, CLASS_OF(obj), obj,
+ VALUE ret = basic_obj_respond_to_missing(ec, CLASS_OF(obj), obj,
rb_to_symbol(mid), priv);
if (ret == Qundef) ret = Qfalse;
return ret;
}
- if (basic_obj_respond_to(th, obj, id, !RTEST(priv)))
+ if (basic_obj_respond_to(ec, obj, id, !RTEST(priv)))
return Qtrue;
return Qfalse;
}
@@ -2077,18 +2144,18 @@ Init_eval_method(void)
rb_define_method(rb_mKernel, "respond_to?", obj_respond_to, -1);
rb_define_method(rb_mKernel, "respond_to_missing?", obj_respond_to_missing, 2);
- rb_define_private_method(rb_cModule, "remove_method", rb_mod_remove_method, -1);
- rb_define_private_method(rb_cModule, "undef_method", rb_mod_undef_method, -1);
- rb_define_private_method(rb_cModule, "alias_method", rb_mod_alias_method, 2);
+ rb_define_method(rb_cModule, "remove_method", rb_mod_remove_method, -1);
+ rb_define_method(rb_cModule, "undef_method", rb_mod_undef_method, -1);
+ rb_define_method(rb_cModule, "alias_method", rb_mod_alias_method, 2);
rb_define_private_method(rb_cModule, "public", rb_mod_public, -1);
rb_define_private_method(rb_cModule, "protected", rb_mod_protected, -1);
rb_define_private_method(rb_cModule, "private", rb_mod_private, -1);
rb_define_private_method(rb_cModule, "module_function", rb_mod_modfunc, -1);
- rb_define_method(rb_cModule, "method_defined?", rb_mod_method_defined, 1);
- rb_define_method(rb_cModule, "public_method_defined?", rb_mod_public_method_defined, 1);
- rb_define_method(rb_cModule, "private_method_defined?", rb_mod_private_method_defined, 1);
- rb_define_method(rb_cModule, "protected_method_defined?", rb_mod_protected_method_defined, 1);
+ rb_define_method(rb_cModule, "method_defined?", rb_mod_method_defined, -1);
+ rb_define_method(rb_cModule, "public_method_defined?", rb_mod_public_method_defined, -1);
+ rb_define_method(rb_cModule, "private_method_defined?", rb_mod_private_method_defined, -1);
+ rb_define_method(rb_cModule, "protected_method_defined?", rb_mod_protected_method_defined, -1);
rb_define_method(rb_cModule, "public_class_method", rb_mod_public_method, -1);
rb_define_method(rb_cModule, "private_class_method", rb_mod_private_method, -1);
diff --git a/vm_opts.h b/vm_opts.h
index 3fedf1d6e2..6a7dd3d85d 100644
--- a/vm_opts.h
+++ b/vm_opts.h
@@ -18,7 +18,6 @@
* Following definitions are default values.
*/
-#define OPT_TRACE_INSTRUCTION 1
#define OPT_TAILCALL_OPTIMIZATION 0
#define OPT_PEEPHOLE_OPTIMIZATION 1
#define OPT_SPECIALISED_INSTRUCTION 1
@@ -31,9 +30,19 @@
*/
/* C compiler dependent */
-#define OPT_DIRECT_THREADED_CODE 1
-#define OPT_TOKEN_THREADED_CODE 0
-#define OPT_CALL_THREADED_CODE 0
+
+/*
+ * 0: direct (using labeled goto using GCC special)
+ * 1: token (switch/case)
+ * 2: call (function call for each insn dispatch)
+ */
+#ifndef OPT_THREADED_CODE
+#define OPT_THREADED_CODE 0
+#endif
+
+#define OPT_DIRECT_THREADED_CODE (OPT_THREADED_CODE == 0)
+#define OPT_TOKEN_THREADED_CODE (OPT_THREADED_CODE == 1)
+#define OPT_CALL_THREADED_CODE (OPT_THREADED_CODE == 2)
/* VM running option */
#define OPT_CHECKED_RUN 1
@@ -41,6 +50,10 @@
#define OPT_GLOBAL_METHOD_CACHE 1
#define OPT_BLOCKINLINING 0
+#ifndef OPT_IC_FOR_IVAR
+#define OPT_IC_FOR_IVAR 1
+#endif
+
/* architecture independent, affects generated code */
#define OPT_OPERANDS_UNIFICATION 1
#define OPT_INSTRUCTIONS_UNIFICATION 0
diff --git a/vm_trace.c b/vm_trace.c
index aca6367b08..f8205db4bb 100644
--- a/vm_trace.c
+++ b/vm_trace.c
@@ -25,6 +25,8 @@
#include "ruby/debug.h"
#include "vm_core.h"
+#include "mjit.h"
+#include "iseq.h"
#include "eval_intern.h"
/* (1) trace mechanisms */
@@ -35,18 +37,19 @@ typedef struct rb_event_hook_struct {
rb_event_hook_func_t func;
VALUE data;
struct rb_event_hook_struct *next;
+
+ struct {
+ rb_thread_t *th;
+ unsigned int target_line;
+ } filter;
} rb_event_hook_t;
typedef void (*rb_event_hook_raw_arg_func_t)(VALUE data, const rb_trace_arg_t *arg);
#define MAX_EVENT_NUM 32
-static int ruby_event_flag_count[MAX_EVENT_NUM] = {0};
-
-/* called from vm.c */
-
void
-rb_vm_trace_mark_event_hooks(rb_hook_list_t *hooks)
+rb_hook_list_mark(rb_hook_list_t *hooks)
{
rb_event_hook_t *hook = hooks->hooks;
@@ -56,50 +59,39 @@ rb_vm_trace_mark_event_hooks(rb_hook_list_t *hooks)
}
}
-/* ruby_vm_event_flags management */
+static void clean_hooks(const rb_execution_context_t *ec, rb_hook_list_t *list);
-static void
-recalc_add_ruby_vm_event_flags(rb_event_flag_t events)
+void
+rb_hook_list_free(rb_hook_list_t *hooks)
{
- int i;
- ruby_vm_event_flags = 0;
-
- for (i=0; i<MAX_EVENT_NUM; i++) {
- if (events & ((rb_event_flag_t)1 << i)) {
- ruby_event_flag_count[i]++;
- }
- ruby_vm_event_flags |= ruby_event_flag_count[i] ? (1<<i) : 0;
- }
-
- rb_objspace_set_event_hook(ruby_vm_event_flags);
+ clean_hooks(GET_EC(), hooks);
}
+/* ruby_vm_event_flags management */
+
static void
-recalc_remove_ruby_vm_event_flags(rb_event_flag_t events)
+update_global_event_hook(rb_event_flag_t vm_events)
{
- int i;
- ruby_vm_event_flags = 0;
+ rb_event_flag_t new_iseq_events = vm_events & ISEQ_TRACE_EVENTS;
+ rb_event_flag_t enabled_iseq_events = ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS;
- for (i=0; i<MAX_EVENT_NUM; i++) {
- if (events & ((rb_event_flag_t)1 << i)) {
- ruby_event_flag_count[i]--;
- }
- ruby_vm_event_flags |= ruby_event_flag_count[i] ? (1<<i) : 0;
+ if (new_iseq_events & ~enabled_iseq_events) {
+ /* Stop calling all JIT-ed code. Compiling trace insns is not supported for now. */
+#if USE_MJIT
+ mjit_call_p = FALSE;
+#endif
+
+ /* write all ISeqs iff new events are added */
+ rb_iseq_trace_set_all(new_iseq_events | enabled_iseq_events);
}
- rb_objspace_set_event_hook(ruby_vm_event_flags);
+ ruby_vm_event_flags = vm_events;
+ ruby_vm_event_enabled_global_flags |= vm_events;
+ rb_objspace_set_event_hook(vm_events);
}
/* add/remove hooks */
-static rb_thread_t *
-thval2thread_t(VALUE thval)
-{
- rb_thread_t *th;
- GetThreadPtr(thval, th);
- return th;
-}
-
static rb_event_hook_t *
alloc_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags)
{
@@ -114,144 +106,194 @@ alloc_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data,
hook->events = events;
hook->func = func;
hook->data = data;
+
+ /* no filters */
+ hook->filter.th = NULL;
+ hook->filter.target_line = 0;
+
return hook;
}
static void
-connect_event_hook(rb_hook_list_t *list, rb_event_hook_t *hook)
+hook_list_connect(VALUE list_owner, rb_hook_list_t *list, rb_event_hook_t *hook, int global_p)
{
hook->next = list->hooks;
list->hooks = hook;
- recalc_add_ruby_vm_event_flags(hook->events);
list->events |= hook->events;
+
+ if (global_p) {
+ /* global hooks are root objects at GC mark. */
+ update_global_event_hook(list->events);
+ }
+ else {
+ RB_OBJ_WRITTEN(list_owner, Qundef, hook->data);
+ }
}
static void
-rb_threadptr_add_event_hook(rb_thread_t *th, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags)
+connect_event_hook(const rb_execution_context_t *ec, rb_event_hook_t *hook)
+{
+ rb_hook_list_t *list = rb_vm_global_hooks(ec);
+ hook_list_connect(Qundef, list, hook, TRUE);
+}
+
+static void
+rb_threadptr_add_event_hook(const rb_execution_context_t *ec, rb_thread_t *th,
+ rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags)
{
rb_event_hook_t *hook = alloc_event_hook(func, events, data, hook_flags);
- connect_event_hook(&th->event_hooks, hook);
+ hook->filter.th = th;
+ connect_event_hook(ec, hook);
}
void
rb_thread_add_event_hook(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
{
- rb_threadptr_add_event_hook(thval2thread_t(thval), func, events, data, RUBY_EVENT_HOOK_FLAG_SAFE);
+ rb_threadptr_add_event_hook(GET_EC(), rb_thread_ptr(thval), func, events, data, RUBY_EVENT_HOOK_FLAG_SAFE);
}
void
rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
{
rb_event_hook_t *hook = alloc_event_hook(func, events, data, RUBY_EVENT_HOOK_FLAG_SAFE);
- connect_event_hook(&GET_VM()->event_hooks, hook);
+ connect_event_hook(GET_EC(), hook);
}
void
rb_thread_add_event_hook2(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags)
{
- rb_threadptr_add_event_hook(thval2thread_t(thval), func, events, data, hook_flags);
+ rb_threadptr_add_event_hook(GET_EC(), rb_thread_ptr(thval), func, events, data, hook_flags);
}
void
rb_add_event_hook2(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags)
{
rb_event_hook_t *hook = alloc_event_hook(func, events, data, hook_flags);
- connect_event_hook(&GET_VM()->event_hooks, hook);
+ connect_event_hook(GET_EC(), hook);
+}
+
+static void
+clean_hooks(const rb_execution_context_t *ec, rb_hook_list_t *list)
+{
+ rb_event_hook_t *hook, **nextp = &list->hooks;
+ VM_ASSERT(list->need_clean == TRUE);
+
+ list->events = 0;
+ list->need_clean = FALSE;
+
+ while ((hook = *nextp) != 0) {
+ if (hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) {
+ *nextp = hook->next;
+ xfree(hook);
+ }
+ else {
+ list->events |= hook->events; /* update active events */
+ nextp = &hook->next;
+ }
+ }
+
+ if (list == rb_vm_global_hooks(ec)) {
+ /* global events */
+ update_global_event_hook(list->events);
+ }
+ else {
+ /* local events */
+ }
}
+static void
+clean_hooks_check(const rb_execution_context_t *ec, rb_hook_list_t *list)
+{
+ if (UNLIKELY(list->need_clean != FALSE)) {
+ if (list->running == 0) {
+ clean_hooks(ec, list);
+ }
+ }
+}
+
+#define MATCH_ANY_FILTER_TH ((rb_thread_t *)1)
+
/* if func is 0, then clear all funcs */
static int
-remove_event_hook(rb_hook_list_t *list, rb_event_hook_func_t func, VALUE data)
+remove_event_hook(const rb_execution_context_t *ec, const rb_thread_t *filter_th, rb_event_hook_func_t func, VALUE data)
{
+ rb_vm_t *vm = rb_ec_vm_ptr(ec);
+ rb_hook_list_t *list = &vm->global_hooks;
int ret = 0;
rb_event_hook_t *hook = list->hooks;
while (hook) {
if (func == 0 || hook->func == func) {
- if (data == Qundef || hook->data == data) {
- hook->hook_flags |= RUBY_EVENT_HOOK_FLAG_DELETED;
- ret+=1;
- list->need_clean = TRUE;
+ if (hook->filter.th == filter_th || filter_th == MATCH_ANY_FILTER_TH) {
+ if (data == Qundef || hook->data == data) {
+ hook->hook_flags |= RUBY_EVENT_HOOK_FLAG_DELETED;
+ ret+=1;
+ list->need_clean = TRUE;
+ }
}
}
hook = hook->next;
}
+ clean_hooks_check(ec, list);
return ret;
}
static int
-rb_threadptr_remove_event_hook(rb_thread_t *th, rb_event_hook_func_t func, VALUE data)
+rb_threadptr_remove_event_hook(const rb_execution_context_t *ec, const rb_thread_t *filter_th, rb_event_hook_func_t func, VALUE data)
{
- return remove_event_hook(&th->event_hooks, func, data);
+ return remove_event_hook(ec, filter_th, func, data);
}
int
rb_thread_remove_event_hook(VALUE thval, rb_event_hook_func_t func)
{
- return rb_threadptr_remove_event_hook(thval2thread_t(thval), func, Qundef);
+ return rb_threadptr_remove_event_hook(GET_EC(), rb_thread_ptr(thval), func, Qundef);
}
int
rb_thread_remove_event_hook_with_data(VALUE thval, rb_event_hook_func_t func, VALUE data)
{
- return rb_threadptr_remove_event_hook(thval2thread_t(thval), func, data);
+ return rb_threadptr_remove_event_hook(GET_EC(), rb_thread_ptr(thval), func, data);
}
int
rb_remove_event_hook(rb_event_hook_func_t func)
{
- return remove_event_hook(&GET_VM()->event_hooks, func, Qundef);
+ return remove_event_hook(GET_EC(), NULL, func, Qundef);
}
int
rb_remove_event_hook_with_data(rb_event_hook_func_t func, VALUE data)
{
- return remove_event_hook(&GET_VM()->event_hooks, func, data);
+ return remove_event_hook(GET_EC(), NULL, func, data);
}
void
rb_clear_trace_func(void)
{
- rb_vm_t *vm = GET_VM();
- rb_thread_t *th = 0;
-
- list_for_each(&vm->living_threads, th, vmlt_node) {
- rb_threadptr_remove_event_hook(th, 0, Qundef);
- }
- rb_remove_event_hook(0);
+ rb_execution_context_t *ec = GET_EC();
+ rb_threadptr_remove_event_hook(ec, MATCH_ANY_FILTER_TH, 0, Qundef);
}
-/* invoke hooks */
-
-static void
-clean_hooks(rb_hook_list_t *list)
+void
+rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec)
{
- rb_event_hook_t *hook, **nextp = &list->hooks;
-
- list->events = 0;
- list->need_clean = FALSE;
-
- while ((hook = *nextp) != 0) {
- if (hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) {
- *nextp = hook->next;
- recalc_remove_ruby_vm_event_flags(hook->events);
- xfree(hook);
- }
- else {
- list->events |= hook->events; /* update active events */
- nextp = &hook->next;
- }
- }
+ rb_threadptr_remove_event_hook(ec, rb_ec_thread_ptr(ec), 0, Qundef);
}
+/* invoke hooks */
+
static void
-exec_hooks_body(rb_thread_t *th, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg)
+exec_hooks_body(const rb_execution_context_t *ec, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg)
{
rb_event_hook_t *hook;
for (hook = list->hooks; hook; hook = hook->next) {
- if (!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) && (trace_arg->event & hook->events)) {
+ if (!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) &&
+ (trace_arg->event & hook->events) &&
+ (LIKELY(hook->filter.th == 0) || hook->filter.th == rb_ec_thread_ptr(ec)) &&
+ (LIKELY(hook->filter.target_line == 0) || (hook->filter.target_line == (unsigned int)rb_vm_get_sourceline(ec->cfp)))) {
if (!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_RAW_ARG)) {
(*hook->func)(trace_arg->event, hook->data, trace_arg->self, trace_arg->id, trace_arg->klass);
}
@@ -263,163 +305,153 @@ exec_hooks_body(rb_thread_t *th, rb_hook_list_t *list, const rb_trace_arg_t *tra
}
static int
-exec_hooks_precheck(rb_thread_t *th, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg)
+exec_hooks_precheck(const rb_execution_context_t *ec, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg)
{
- if (UNLIKELY(list->need_clean != FALSE)) {
- if (th->vm->trace_running <= 1) { /* only running this hooks */
- clean_hooks(list);
- }
+ if (list->events & trace_arg->event) {
+ list->running++;
+ return TRUE;
+ }
+ else {
+ return FALSE;
}
+}
- return (list->events & trace_arg->event) != 0;
+static void
+exec_hooks_postcheck(const rb_execution_context_t *ec, rb_hook_list_t *list)
+{
+ list->running--;
+ clean_hooks_check(ec, list);
}
static void
-exec_hooks_unprotected(rb_thread_t *th, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg)
+exec_hooks_unprotected(const rb_execution_context_t *ec, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg)
{
- if (exec_hooks_precheck(th, list, trace_arg) == 0) return;
- exec_hooks_body(th, list, trace_arg);
+ if (exec_hooks_precheck(ec, list, trace_arg) == 0) return;
+ exec_hooks_body(ec, list, trace_arg);
+ exec_hooks_postcheck(ec, list);
}
static int
-exec_hooks_protected(rb_thread_t *th, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg)
+exec_hooks_protected(rb_execution_context_t *ec, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg)
{
- int state;
+ enum ruby_tag_type state;
volatile int raised;
- if (exec_hooks_precheck(th, list, trace_arg) == 0) return 0;
+ if (exec_hooks_precheck(ec, list, trace_arg) == 0) return 0;
- raised = rb_threadptr_reset_raised(th);
+ raised = rb_ec_reset_raised(ec);
/* TODO: Support !RUBY_EVENT_HOOK_FLAG_SAFE hooks */
- TH_PUSH_TAG(th);
- if ((state = TH_EXEC_TAG()) == 0) {
- exec_hooks_body(th, list, trace_arg);
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ exec_hooks_body(ec, list, trace_arg);
}
- TH_POP_TAG();
+ EC_POP_TAG();
+
+ exec_hooks_postcheck(ec, list);
if (raised) {
- rb_threadptr_set_raised(th);
+ rb_ec_set_raised(ec);
}
return state;
}
-static void
-rb_threadptr_exec_event_hooks_orig(rb_trace_arg_t *trace_arg, int pop_p)
+MJIT_FUNC_EXPORTED void
+rb_exec_event_hooks(rb_trace_arg_t *trace_arg, rb_hook_list_t *hooks, int pop_p)
{
- rb_thread_t *th = trace_arg->th;
+ rb_execution_context_t *ec = trace_arg->ec;
- if (trace_arg->event & RUBY_INTERNAL_EVENT_MASK) {
- if (th->trace_arg && (th->trace_arg->event & RUBY_INTERNAL_EVENT_MASK)) {
- /* skip hooks because this thread doing INTERNAL_EVENT */
+ if (UNLIKELY(trace_arg->event & RUBY_INTERNAL_EVENT_MASK)) {
+ if (ec->trace_arg && (ec->trace_arg->event & RUBY_INTERNAL_EVENT_MASK)) {
+ /* skip hooks because this thread doing INTERNAL_EVENT */
}
else {
- rb_trace_arg_t *prev_trace_arg = th->trace_arg;
- th->vm->trace_running++;
- th->trace_arg = trace_arg;
- exec_hooks_unprotected(th, &th->event_hooks, trace_arg);
- exec_hooks_unprotected(th, &th->vm->event_hooks, trace_arg);
- th->trace_arg = prev_trace_arg;
- th->vm->trace_running--;
+ rb_trace_arg_t *prev_trace_arg = ec->trace_arg;
+
+ ec->trace_arg = trace_arg;
+ /* only global hooks */
+ exec_hooks_unprotected(ec, rb_vm_global_hooks(ec), trace_arg);
+ ec->trace_arg = prev_trace_arg;
}
}
else {
- if (th->trace_arg == 0 && /* check reentrant */
+ if (ec->trace_arg == NULL && /* check reentrant */
trace_arg->self != rb_mRubyVMFrozenCore /* skip special methods. TODO: remove it. */) {
- const VALUE errinfo = th->errinfo;
- const int outer_state = th->state;
- const VALUE old_recursive = th->local_storage_recursive_hash;
+ const VALUE errinfo = ec->errinfo;
+ const VALUE old_recursive = ec->local_storage_recursive_hash;
int state = 0;
- th->local_storage_recursive_hash = th->local_storage_recursive_hash_for_trace;
- th->state = 0;
- th->errinfo = Qnil;
-
- th->vm->trace_running++;
- th->trace_arg = trace_arg;
- {
- /* thread local traces */
- state = exec_hooks_protected(th, &th->event_hooks, trace_arg);
- if (state) goto terminate;
+ /* setup */
+ ec->local_storage_recursive_hash = ec->local_storage_recursive_hash_for_trace;
+ ec->errinfo = Qnil;
+ ec->trace_arg = trace_arg;
- /* vm global traces */
- state = exec_hooks_protected(th, &th->vm->event_hooks, trace_arg);
- if (state) goto terminate;
+ /* kick hooks */
+ if ((state = exec_hooks_protected(ec, hooks, trace_arg)) == TAG_NONE) {
+ ec->errinfo = errinfo;
+ }
- th->errinfo = errinfo;
- }
- terminate:
- th->trace_arg = 0;
- th->vm->trace_running--;
-
- th->local_storage_recursive_hash_for_trace = th->local_storage_recursive_hash;
- th->local_storage_recursive_hash = old_recursive;
+ /* cleanup */
+ ec->trace_arg = NULL;
+ ec->local_storage_recursive_hash_for_trace = ec->local_storage_recursive_hash;
+ ec->local_storage_recursive_hash = old_recursive;
if (state) {
if (pop_p) {
- if (VM_FRAME_FINISHED_P(th->cfp)) {
- th->tag = th->tag->prev;
+ if (VM_FRAME_FINISHED_P(ec->cfp)) {
+ ec->tag = ec->tag->prev;
}
- rb_vm_pop_frame(th);
+ rb_vm_pop_frame(ec);
}
- TH_JUMP_TAG(th, state);
+ EC_JUMP_TAG(ec, state);
}
- th->state = outer_state;
}
}
}
-void
-rb_threadptr_exec_event_hooks_and_pop_frame(rb_trace_arg_t *trace_arg)
-{
- rb_threadptr_exec_event_hooks_orig(trace_arg, 1);
-}
-
-void
-rb_threadptr_exec_event_hooks(rb_trace_arg_t *trace_arg)
-{
- rb_threadptr_exec_event_hooks_orig(trace_arg, 0);
-}
-
VALUE
rb_suppress_tracing(VALUE (*func)(VALUE), VALUE arg)
{
volatile int raised;
- volatile int outer_state;
VALUE result = Qnil;
- rb_thread_t *th = GET_THREAD();
- int state;
- const int tracing = th->trace_arg ? 1 : 0;
+ rb_execution_context_t *const ec = GET_EC();
+ rb_vm_t *const vm = rb_ec_vm_ptr(ec);
+ enum ruby_tag_type state;
rb_trace_arg_t dummy_trace_arg;
dummy_trace_arg.event = 0;
- if (!tracing) th->vm->trace_running++;
- if (!th->trace_arg) th->trace_arg = &dummy_trace_arg;
+ if (!ec->trace_arg) {
+ ec->trace_arg = &dummy_trace_arg;
+ }
- raised = rb_threadptr_reset_raised(th);
- outer_state = th->state;
- th->state = 0;
+ raised = rb_ec_reset_raised(ec);
- TH_PUSH_TAG(th);
- if ((state = TH_EXEC_TAG()) == 0) {
+ EC_PUSH_TAG(ec);
+ if (LIKELY((state = EC_EXEC_TAG()) == TAG_NONE)) {
result = (*func)(arg);
}
- TH_POP_TAG();
+ else {
+ (void)*&vm; /* suppress "clobbered" warning */
+ }
+ EC_POP_TAG();
if (raised) {
- rb_threadptr_set_raised(th);
+ rb_ec_reset_raised(ec);
}
- if (th->trace_arg == &dummy_trace_arg) th->trace_arg = 0;
- if (!tracing) th->vm->trace_running--;
+ if (ec->trace_arg == &dummy_trace_arg) {
+ ec->trace_arg = NULL;
+ }
if (state) {
- TH_JUMP_TAG(th, state);
+#if defined RUBY_USE_SETJMPEX && RUBY_USE_SETJMPEX
+ RB_GC_GUARD(result);
+#endif
+ EC_JUMP_TAG(ec, state);
}
- th->state = outer_state;
return result;
}
@@ -489,7 +521,6 @@ static void call_trace_func(rb_event_flag_t, VALUE data, VALUE self, ID id, VALU
static VALUE
set_trace_func(VALUE obj, VALUE trace)
{
-
rb_remove_event_hook(call_trace_func);
if (NIL_P(trace)) {
@@ -505,13 +536,13 @@ set_trace_func(VALUE obj, VALUE trace)
}
static void
-thread_add_trace_func(rb_thread_t *th, VALUE trace)
+thread_add_trace_func(rb_execution_context_t *ec, rb_thread_t *filter_th, VALUE trace)
{
if (!rb_obj_is_proc(trace)) {
rb_raise(rb_eTypeError, "trace_func needs to be Proc");
}
- rb_threadptr_add_event_hook(th, call_trace_func, RUBY_EVENT_ALL, trace, RUBY_EVENT_HOOK_FLAG_SAFE);
+ rb_threadptr_add_event_hook(ec, filter_th, call_trace_func, RUBY_EVENT_ALL, trace, RUBY_EVENT_HOOK_FLAG_SAFE);
}
/*
@@ -526,10 +557,7 @@ thread_add_trace_func(rb_thread_t *th, VALUE trace)
static VALUE
thread_add_trace_func_m(VALUE obj, VALUE trace)
{
- rb_thread_t *th;
-
- GetThreadPtr(obj, th);
- thread_add_trace_func(th, trace);
+ thread_add_trace_func(GET_EC(), rb_thread_ptr(obj), trace);
return trace;
}
@@ -545,19 +573,20 @@ thread_add_trace_func_m(VALUE obj, VALUE trace)
*/
static VALUE
-thread_set_trace_func_m(VALUE obj, VALUE trace)
+thread_set_trace_func_m(VALUE target_thread, VALUE trace)
{
- rb_thread_t *th;
+ rb_execution_context_t *ec = GET_EC();
+ rb_thread_t *target_th = rb_thread_ptr(target_thread);
- GetThreadPtr(obj, th);
- rb_threadptr_remove_event_hook(th, call_trace_func, Qundef);
+ rb_threadptr_remove_event_hook(ec, target_th, call_trace_func, Qundef);
if (NIL_P(trace)) {
return Qnil;
}
-
- thread_add_trace_func(th, trace);
- return trace;
+ else {
+ thread_add_trace_func(ec, target_th, trace);
+ return trace;
+ }
}
static const char *
@@ -597,8 +626,7 @@ get_event_id(rb_event_flag_t event)
C(thread_begin, THREAD_BEGIN);
C(thread_end, THREAD_END);
C(fiber_switch, FIBER_SWITCH);
- C(specified_line, SPECIFIED_LINE);
- case RUBY_EVENT_LINE | RUBY_EVENT_SPECIFIED_LINE: CONST_ID(id, "line"); return id;
+ C(script_compiled, SCRIPT_COMPILED);
#undef C
default:
return 0;
@@ -606,17 +634,42 @@ get_event_id(rb_event_flag_t event)
}
static void
+get_path_and_lineno(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, rb_event_flag_t event, VALUE *pathp, int *linep)
+{
+ cfp = rb_vm_get_ruby_level_next_cfp(ec, cfp);
+
+ if (cfp) {
+ const rb_iseq_t *iseq = cfp->iseq;
+ *pathp = rb_iseq_path(iseq);
+
+ if (event & (RUBY_EVENT_CLASS |
+ RUBY_EVENT_CALL |
+ RUBY_EVENT_B_CALL)) {
+ *linep = FIX2INT(rb_iseq_first_lineno(iseq));
+ }
+ else {
+ *linep = rb_vm_get_sourceline(cfp);
+ }
+ }
+ else {
+ *pathp = Qnil;
+ *linep = 0;
+ }
+}
+
+static void
call_trace_func(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
{
int line;
- const char *srcfile = rb_source_loc(&line);
+ VALUE filename;
VALUE eventname = rb_str_new2(get_event_name(event));
- VALUE filename = srcfile ? rb_str_new2(srcfile) : Qnil;
VALUE argv[6];
- rb_thread_t *th = GET_THREAD();
+ const rb_execution_context_t *ec = GET_EC();
+
+ get_path_and_lineno(ec, ec->cfp, event, &filename, &line);
if (!klass) {
- rb_thread_method_id_and_class(th, &id, 0, &klass);
+ rb_ec_frame_method_id_and_class(ec, &id, 0, &klass);
}
if (klass) {
@@ -632,7 +685,7 @@ call_trace_func(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klas
argv[1] = filename;
argv[2] = INT2FIX(line);
argv[3] = id ? ID2SYM(id) : Qnil;
- argv[4] = (self && srcfile) ? rb_binding_new() : Qnil;
+ argv[4] = (self && (filename != Qnil)) ? rb_binding_new() : Qnil;
argv[5] = klass ? klass : Qnil;
rb_proc_call_with_block(proc, 6, argv, Qnil);
@@ -646,6 +699,10 @@ typedef struct rb_tp_struct {
rb_event_flag_t events;
int tracing; /* bool */
rb_thread_t *target_th;
+ VALUE local_target_set; /* Hash: target ->
+ * Qtrue (if target is iseq) or
+ * Qfalse (if target is bmethod)
+ */
void (*func)(VALUE tpval, void *data);
void *data;
VALUE proc;
@@ -657,6 +714,7 @@ tp_mark(void *ptr)
{
rb_tp_t *tp = ptr;
rb_gc_mark(tp->proc);
+ rb_gc_mark(tp->local_target_set);
if (tp->target_th) rb_gc_mark(tp->target_th->self);
}
@@ -683,7 +741,7 @@ static rb_event_flag_t
symbol2event_flag(VALUE v)
{
ID id;
- VALUE sym = rb_convert_type(v, T_SYMBOL, "Symbol", "to_sym");
+ VALUE sym = rb_to_symbol_type(v);
const rb_event_flag_t RUBY_EVENT_A_CALL =
RUBY_EVENT_CALL | RUBY_EVENT_B_CALL | RUBY_EVENT_C_CALL;
const rb_event_flag_t RUBY_EVENT_A_RETURN =
@@ -703,7 +761,9 @@ symbol2event_flag(VALUE v)
C(thread_begin, THREAD_BEGIN);
C(thread_end, THREAD_END);
C(fiber_switch, FIBER_SWITCH);
- C(specified_line, SPECIFIED_LINE);
+ C(script_compiled, SCRIPT_COMPILED);
+
+ /* joke */
C(a_call, A_CALL);
C(a_return, A_RETURN);
#undef C
@@ -721,7 +781,7 @@ tpptr(VALUE tpval)
static rb_trace_arg_t *
get_trace_arg(void)
{
- rb_trace_arg_t *trace_arg = GET_THREAD()->trace_arg;
+ rb_trace_arg_t *trace_arg = GET_EC()->trace_arg;
if (trace_arg == 0) {
rb_raise(rb_eRuntimeError, "access from outside");
}
@@ -750,16 +810,7 @@ static void
fill_path_and_lineno(rb_trace_arg_t *trace_arg)
{
if (trace_arg->path == Qundef) {
- rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(trace_arg->th, trace_arg->cfp);
-
- if (cfp) {
- trace_arg->path = cfp->iseq->body->location.path;
- trace_arg->lineno = rb_vm_get_sourceline(cfp);
- }
- else {
- trace_arg->path = Qnil;
- trace_arg->lineno = 0;
- }
+ get_path_and_lineno(trace_arg->ec, trace_arg->cfp, trace_arg->event, &trace_arg->path, &trace_arg->lineno);
}
}
@@ -798,6 +849,46 @@ fill_id_and_klass(rb_trace_arg_t *trace_arg)
}
VALUE
+rb_tracearg_parameters(rb_trace_arg_t *trace_arg)
+{
+ switch(trace_arg->event) {
+ case RUBY_EVENT_CALL:
+ case RUBY_EVENT_RETURN:
+ case RUBY_EVENT_B_CALL:
+ case RUBY_EVENT_B_RETURN: {
+ const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(trace_arg->ec, trace_arg->cfp);
+ if (cfp) {
+ int is_proc = 0;
+ if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_BLOCK && !VM_FRAME_LAMBDA_P(cfp)) {
+ is_proc = 1;
+ }
+ return rb_iseq_parameters(cfp->iseq, is_proc);
+ }
+ break;
+ }
+ case RUBY_EVENT_C_CALL:
+ case RUBY_EVENT_C_RETURN: {
+ fill_id_and_klass(trace_arg);
+ if (trace_arg->klass && trace_arg->id) {
+ const rb_method_entry_t *me;
+ VALUE iclass = Qnil;
+ me = rb_method_entry_without_refinements(trace_arg->klass, trace_arg->id, &iclass);
+ return rb_unnamed_parameters(rb_method_entry_arity(me));
+ }
+ break;
+ }
+ case RUBY_EVENT_RAISE:
+ case RUBY_EVENT_LINE:
+ case RUBY_EVENT_CLASS:
+ case RUBY_EVENT_END:
+ case RUBY_EVENT_SCRIPT_COMPILED:
+ rb_raise(rb_eRuntimeError, "not supported by this event");
+ break;
+ }
+ return Qnil;
+}
+
+VALUE
rb_tracearg_method_id(rb_trace_arg_t *trace_arg)
{
fill_id_and_klass(trace_arg);
@@ -822,10 +913,10 @@ VALUE
rb_tracearg_binding(rb_trace_arg_t *trace_arg)
{
rb_control_frame_t *cfp;
- cfp = rb_vm_get_binding_creatable_next_cfp(trace_arg->th, trace_arg->cfp);
+ cfp = rb_vm_get_binding_creatable_next_cfp(trace_arg->ec, trace_arg->cfp);
if (cfp) {
- return rb_vm_make_binding(trace_arg->th, cfp);
+ return rb_vm_make_binding(trace_arg->ec, cfp);
}
else {
return Qnil;
@@ -848,7 +939,7 @@ rb_tracearg_return_value(rb_trace_arg_t *trace_arg)
rb_raise(rb_eRuntimeError, "not supported by this event");
}
if (trace_arg->data == Qundef) {
- rb_bug("tp_attr_return_value_m: unreachable");
+ rb_bug("rb_tracearg_return_value: unreachable");
}
return trace_arg->data;
}
@@ -863,12 +954,63 @@ rb_tracearg_raised_exception(rb_trace_arg_t *trace_arg)
rb_raise(rb_eRuntimeError, "not supported by this event");
}
if (trace_arg->data == Qundef) {
- rb_bug("tp_attr_raised_exception_m: unreachable");
+ rb_bug("rb_tracearg_raised_exception: unreachable");
}
return trace_arg->data;
}
VALUE
+rb_tracearg_eval_script(rb_trace_arg_t *trace_arg)
+{
+ VALUE data = trace_arg->data;
+
+ if (trace_arg->event & (RUBY_EVENT_SCRIPT_COMPILED)) {
+ /* ok */
+ }
+ else {
+ rb_raise(rb_eRuntimeError, "not supported by this event");
+ }
+ if (data == Qundef) {
+ rb_bug("rb_tracearg_raised_exception: unreachable");
+ }
+ if (rb_obj_is_iseq(data)) {
+ return Qnil;
+ }
+ else {
+ VM_ASSERT(RB_TYPE_P(data, T_ARRAY));
+ /* [src, iseq] */
+ return RARRAY_AREF(data, 0);
+ }
+}
+
+VALUE
+rb_tracearg_instruction_sequence(rb_trace_arg_t *trace_arg)
+{
+ VALUE data = trace_arg->data;
+
+ if (trace_arg->event & (RUBY_EVENT_SCRIPT_COMPILED)) {
+ /* ok */
+ }
+ else {
+ rb_raise(rb_eRuntimeError, "not supported by this event");
+ }
+ if (data == Qundef) {
+ rb_bug("rb_tracearg_raised_exception: unreachable");
+ }
+
+ if (rb_obj_is_iseq(data)) {
+ return rb_iseqw_new((const rb_iseq_t *)data);
+ }
+ else {
+ VM_ASSERT(RB_TYPE_P(data, T_ARRAY));
+ VM_ASSERT(rb_obj_is_iseq(RARRAY_AREF(data, 1)));
+
+ /* [src, iseq] */
+ return rb_iseqw_new((const rb_iseq_t *)RARRAY_AREF(data, 1));
+ }
+}
+
+VALUE
rb_tracearg_object(rb_trace_arg_t *trace_arg)
{
if (trace_arg->event & (RUBY_INTERNAL_EVENT_NEWOBJ | RUBY_INTERNAL_EVENT_FREEOBJ)) {
@@ -878,7 +1020,7 @@ rb_tracearg_object(rb_trace_arg_t *trace_arg)
rb_raise(rb_eRuntimeError, "not supported by this event");
}
if (trace_arg->data == Qundef) {
- rb_bug("tp_attr_raised_exception_m: unreachable");
+ rb_bug("rb_tracearg_object: unreachable");
}
return trace_arg->data;
}
@@ -913,6 +1055,15 @@ tracepoint_attr_path(VALUE tpval)
}
/*
+ * Return the parameters of the method or block that the current hook belongs to
+ */
+static VALUE
+tracepoint_attr_parameters(VALUE tpval)
+{
+ return rb_tracearg_parameters(get_trace_arg());
+}
+
+/*
* Return the name at the definition of the method being called
*/
static VALUE
@@ -1009,6 +1160,28 @@ tracepoint_attr_raised_exception(VALUE tpval)
return rb_tracearg_raised_exception(get_trace_arg());
}
+/*
+ * Compiled source code (String) on *eval methods on the +:script_compiled+ event.
+ * If loaded from a file, it will return nil.
+ */
+static VALUE
+tracepoint_attr_eval_script(VALUE tpval)
+{
+ return rb_tracearg_eval_script(get_trace_arg());
+}
+
+/*
+ * Compiled instruction sequence represented by a RubyVM::InstructionSequence instance
+ * on the +:script_compiled+ event.
+ *
+ * Note that this method is MRI specific.
+ */
+static VALUE
+tracepoint_attr_instruction_sequence(VALUE tpval)
+{
+ return rb_tracearg_instruction_sequence(get_trace_arg());
+}
+
static void
tp_call_trace(VALUE tpval, rb_trace_arg_t *trace_arg)
{
@@ -1026,9 +1199,12 @@ VALUE
rb_tracepoint_enable(VALUE tpval)
{
rb_tp_t *tp;
-
tp = tpptr(tpval);
+ if (tp->local_target_set != Qfalse) {
+ rb_raise(rb_eArgError, "can't nest-enable a targetting TracePoint");
+ }
+
if (tp->target_th) {
rb_thread_add_event_hook2(tp->target_th->self, (rb_event_hook_func_t)tp_call_trace, tp->events, tpval,
RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
@@ -1041,6 +1217,92 @@ rb_tracepoint_enable(VALUE tpval)
return Qundef;
}
+static const rb_iseq_t *
+iseq_of(VALUE target)
+{
+ VALUE iseqv = rb_funcall(rb_cISeq, rb_intern("of"), 1, target);
+ if (NIL_P(iseqv)) {
+ rb_raise(rb_eArgError, "specified target is not supported");
+ }
+ else {
+ return rb_iseqw_to_iseq(iseqv);
+ }
+}
+
+const rb_method_definition_t *rb_method_def(VALUE method); /* proc.c */
+
+static VALUE
+rb_tracepoint_enable_for_target(VALUE tpval, VALUE target, VALUE target_line)
+{
+ rb_tp_t *tp = tpptr(tpval);
+ const rb_iseq_t *iseq = iseq_of(target);
+ int n;
+ unsigned int line = 0;
+
+ if (tp->tracing > 0) {
+ rb_raise(rb_eArgError, "can't nest-enable a targetting TracePoint");
+ }
+
+ if (!NIL_P(target_line)) {
+ if ((tp->events & RUBY_EVENT_LINE) == 0) {
+ rb_raise(rb_eArgError, "target_line is specified, but line event is not specified");
+ }
+ else {
+ line = NUM2UINT(target_line);
+ }
+ }
+
+ VM_ASSERT(tp->local_target_set == Qfalse);
+ tp->local_target_set = rb_obj_hide(rb_ident_hash_new());
+
+ /* iseq */
+ n = rb_iseq_add_local_tracepoint_recursively(iseq, tp->events, tpval, line);
+ rb_hash_aset(tp->local_target_set, (VALUE)iseq, Qtrue);
+
+ /* bmethod */
+ if (rb_obj_is_method(target)) {
+ rb_method_definition_t *def = (rb_method_definition_t *)rb_method_def(target);
+ if (def->type == VM_METHOD_TYPE_BMETHOD &&
+ (tp->events & (RUBY_EVENT_CALL | RUBY_EVENT_RETURN))) {
+ def->body.bmethod.hooks = ZALLOC(rb_hook_list_t);
+ rb_hook_list_connect_tracepoint(target, def->body.bmethod.hooks, tpval, 0);
+ rb_hash_aset(tp->local_target_set, target, Qfalse);
+
+ n++;
+ }
+ }
+
+ if (n == 0) {
+ rb_raise(rb_eArgError, "can not enable any hooks");
+ }
+
+ ruby_vm_event_local_num++;
+
+ tp->tracing = 1;
+
+ return Qnil;
+}
+
+static int
+disable_local_event_iseq_i(VALUE target, VALUE iseq_p, VALUE tpval)
+{
+ if (iseq_p) {
+ rb_iseq_remove_local_tracepoint_recursively((rb_iseq_t *)target, tpval);
+ }
+ else {
+ /* bmethod */
+ rb_method_definition_t *def = (rb_method_definition_t *)rb_method_def(target);
+ rb_hook_list_t *hooks = def->body.bmethod.hooks;
+ VM_ASSERT(hooks != NULL);
+ rb_hook_list_remove_tracepoint(hooks, tpval);
+ if (hooks->running == 0) {
+ rb_hook_list_free(def->body.bmethod.hooks);
+ }
+ def->body.bmethod.hooks = NULL;
+ }
+ return ST_CONTINUE;
+}
+
VALUE
rb_tracepoint_disable(VALUE tpval)
{
@@ -1048,16 +1310,53 @@ rb_tracepoint_disable(VALUE tpval)
tp = tpptr(tpval);
- if (tp->target_th) {
- rb_thread_remove_event_hook_with_data(tp->target_th->self, (rb_event_hook_func_t)tp_call_trace, tpval);
+ if (tp->local_target_set) {
+ rb_hash_foreach(tp->local_target_set, disable_local_event_iseq_i, tpval);
+ tp->local_target_set = Qfalse;
+ ruby_vm_event_local_num--;
}
else {
- rb_remove_event_hook_with_data((rb_event_hook_func_t)tp_call_trace, tpval);
+ if (tp->target_th) {
+ rb_thread_remove_event_hook_with_data(tp->target_th->self, (rb_event_hook_func_t)tp_call_trace, tpval);
+ }
+ else {
+ rb_remove_event_hook_with_data((rb_event_hook_func_t)tp_call_trace, tpval);
+ }
}
tp->tracing = 0;
return Qundef;
}
+void
+rb_hook_list_connect_tracepoint(VALUE target, rb_hook_list_t *list, VALUE tpval, unsigned int target_line)
+{
+ rb_tp_t *tp = tpptr(tpval);
+ rb_event_hook_t *hook = alloc_event_hook((rb_event_hook_func_t)tp_call_trace, tp->events, tpval,
+ RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
+ hook->filter.target_line = target_line;
+ hook_list_connect(target, list, hook, FALSE);
+}
+
+void
+rb_hook_list_remove_tracepoint(rb_hook_list_t *list, VALUE tpval)
+{
+ rb_event_hook_t *hook = list->hooks;
+ rb_event_flag_t events = 0;
+
+ while (hook) {
+ if (hook->data == tpval) {
+ hook->hook_flags |= RUBY_EVENT_HOOK_FLAG_DELETED;
+ list->need_clean = TRUE;
+ }
+ else {
+ events |= hook->events;
+ }
+ hook = hook->next;
+ }
+
+ list->events = events;
+}
+
/*
* call-seq:
* trace.enable -> true or false
@@ -1096,14 +1395,23 @@ rb_tracepoint_disable(VALUE tpval)
*
*/
static VALUE
-tracepoint_enable_m(VALUE tpval)
+tracepoint_enable_m(VALUE tpval, VALUE target, VALUE target_line)
{
rb_tp_t *tp = tpptr(tpval);
int previous_tracing = tp->tracing;
- rb_tracepoint_enable(tpval);
+
+ if (NIL_P(target)) {
+ if (!NIL_P(target_line)) {
+ rb_raise(rb_eArgError, "only target_line is specified");
+ }
+ rb_tracepoint_enable(tpval);
+ }
+ else {
+ rb_tracepoint_enable_for_target(tpval, target, target_line);
+ }
if (rb_block_given_p()) {
- return rb_ensure(rb_yield, Qnil,
+ return rb_ensure(rb_yield, Qundef,
previous_tracing ? rb_tracepoint_enable : rb_tracepoint_disable,
tpval);
}
@@ -1123,7 +1431,7 @@ tracepoint_enable_m(VALUE tpval)
* Return false if trace was disabled.
*
* trace.enabled? #=> true
- * trace.disable #=> false (previous status)
+ * trace.disable #=> true (previous status)
* trace.enabled? #=> false
* trace.disable #=> false
*
@@ -1146,19 +1454,25 @@ tracepoint_enable_m(VALUE tpval)
* trace.disable { p tp.lineno }
* #=> RuntimeError: access from outside
*/
+
static VALUE
tracepoint_disable_m(VALUE tpval)
{
rb_tp_t *tp = tpptr(tpval);
int previous_tracing = tp->tracing;
- rb_tracepoint_disable(tpval);
if (rb_block_given_p()) {
- return rb_ensure(rb_yield, Qnil,
+ if (tp->local_target_set != Qfalse) {
+ rb_raise(rb_eArgError, "can't disable a targetting TracePoint in a block");
+ }
+
+ rb_tracepoint_disable(tpval);
+ return rb_ensure(rb_yield, Qundef,
previous_tracing ? rb_tracepoint_enable : rb_tracepoint_disable,
tpval);
}
else {
+ rb_tracepoint_disable(tpval);
return previous_tracing ? Qtrue : Qfalse;
}
}
@@ -1225,9 +1539,10 @@ tracepoint_new(VALUE klass, rb_thread_t *target_th, rb_event_flag_t events, void
VALUE
rb_tracepoint_new(VALUE target_thval, rb_event_flag_t events, void (*func)(VALUE, void *), void *data)
{
- rb_thread_t *target_th = 0;
+ rb_thread_t *target_th = NULL;
+
if (RTEST(target_thval)) {
- GetThreadPtr(target_thval, target_th);
+ target_th = rb_thread_ptr(target_thval);
/* TODO: Test it!
* Warning: This function is not tested.
*/
@@ -1241,7 +1556,7 @@ rb_tracepoint_new(VALUE target_thval, rb_event_flag_t events, void (*func)(VALUE
*
* Returns a new TracePoint object, not enabled by default.
*
- * Next, in order to activate the trace, you must use TracePoint.enable
+ * Next, in order to activate the trace, you must use TracePoint#enable
*
* trace = TracePoint.new(:call) do |tp|
* p [tp.lineno, tp.defined_class, tp.method_id, tp.event]
@@ -1256,13 +1571,13 @@ rb_tracepoint_new(VALUE target_thval, rb_event_flag_t events, void (*func)(VALUE
* # [48, IRB::Notifier::AbstractNotifier, :printf, :call]
* # ...
*
- * When you want to deactivate the trace, you must use TracePoint.disable
+ * When you want to deactivate the trace, you must use TracePoint#disable
*
* trace.disable
*
* See TracePoint@Events for possible events and more information.
*
- * A block must be given, otherwise a ThreadError is raised.
+ * A block must be given, otherwise an ArgumentError is raised.
*
* If the trace method isn't included in the given events filter, a
* RuntimeError is raised.
@@ -1277,7 +1592,7 @@ rb_tracepoint_new(VALUE target_thval, rb_event_flag_t events, void (*func)(VALUE
* TracePoint.trace(:line) do |tp|
* $tp = tp
* end
- * $tp.line #=> access from outside (RuntimeError)
+ * $tp.lineno #=> access from outside (RuntimeError)
*
* Access from other threads is also forbidden.
*
@@ -1298,7 +1613,7 @@ tracepoint_new_s(int argc, VALUE *argv, VALUE self)
}
if (!rb_block_given_p()) {
- rb_raise(rb_eThreadError, "must be called with a block");
+ rb_raise(rb_eArgError, "must be called with a block");
}
return tracepoint_new(self, 0, events, 0, 0, rb_block_proc());
@@ -1324,12 +1639,11 @@ static VALUE
tracepoint_inspect(VALUE self)
{
rb_tp_t *tp = tpptr(self);
- rb_trace_arg_t *trace_arg = GET_THREAD()->trace_arg;
+ rb_trace_arg_t *trace_arg = GET_EC()->trace_arg;
if (trace_arg) {
switch (trace_arg->event) {
case RUBY_EVENT_LINE:
- case RUBY_EVENT_SPECIFIED_LINE:
{
VALUE sym = rb_tracearg_method_id(trace_arg);
if (NIL_P(sym))
@@ -1403,14 +1717,12 @@ tracepoint_stat_s(VALUE self)
rb_vm_t *vm = GET_VM();
VALUE stat = rb_hash_new();
- tracepoint_stat_event_hooks(stat, vm->self, vm->event_hooks.hooks);
+ tracepoint_stat_event_hooks(stat, vm->self, vm->global_hooks.hooks);
/* TODO: thread local hooks */
return stat;
}
-static void Init_postponed_job(void);
-
/* This function is called from inits.c */
void
Init_vm_trace(void)
@@ -1486,7 +1798,7 @@ Init_vm_trace(void)
*/
rb_define_singleton_method(rb_cTracePoint, "trace", tracepoint_trace_s, -1);
- rb_define_method(rb_cTracePoint, "enable", tracepoint_enable_m, 0);
+ rb_define_method(rb_cTracePoint, "__enable", tracepoint_enable_m, 2);
rb_define_method(rb_cTracePoint, "disable", tracepoint_disable_m, 0);
rb_define_method(rb_cTracePoint, "enabled?", rb_tracepoint_enabled_p, 0);
@@ -1495,6 +1807,7 @@ Init_vm_trace(void)
rb_define_method(rb_cTracePoint, "event", tracepoint_attr_event, 0);
rb_define_method(rb_cTracePoint, "lineno", tracepoint_attr_lineno, 0);
rb_define_method(rb_cTracePoint, "path", tracepoint_attr_path, 0);
+ rb_define_method(rb_cTracePoint, "parameters", tracepoint_attr_parameters, 0);
rb_define_method(rb_cTracePoint, "method_id", tracepoint_attr_method_id, 0);
rb_define_method(rb_cTracePoint, "callee_id", tracepoint_attr_callee_id, 0);
rb_define_method(rb_cTracePoint, "defined_class", tracepoint_attr_defined_class, 0);
@@ -1502,17 +1815,13 @@ Init_vm_trace(void)
rb_define_method(rb_cTracePoint, "self", tracepoint_attr_self, 0);
rb_define_method(rb_cTracePoint, "return_value", tracepoint_attr_return_value, 0);
rb_define_method(rb_cTracePoint, "raised_exception", tracepoint_attr_raised_exception, 0);
+ rb_define_method(rb_cTracePoint, "eval_script", tracepoint_attr_eval_script, 0);
+ rb_define_method(rb_cTracePoint, "instruction_sequence", tracepoint_attr_instruction_sequence, 0);
rb_define_singleton_method(rb_cTracePoint, "stat", tracepoint_stat_s, 0);
-
- /* initialized for postponed job */
-
- Init_postponed_job();
}
typedef struct rb_postponed_job_struct {
- unsigned long flags; /* reserved */
- struct rb_thread_struct *th; /* created thread, reserved */
rb_postponed_job_func_t func;
void *data;
} rb_postponed_job_t;
@@ -1520,114 +1829,171 @@ typedef struct rb_postponed_job_struct {
#define MAX_POSTPONED_JOB 1000
#define MAX_POSTPONED_JOB_SPECIAL_ADDITION 24
-static void
-Init_postponed_job(void)
+struct rb_workqueue_job {
+ struct list_node jnode; /* <=> vm->workqueue */
+ rb_postponed_job_t job;
+};
+
+void
+Init_vm_postponed_job(void)
{
rb_vm_t *vm = GET_VM();
vm->postponed_job_buffer = ALLOC_N(rb_postponed_job_t, MAX_POSTPONED_JOB);
vm->postponed_job_index = 0;
+ /* workqueue is initialized when VM locks are initialized */
}
enum postponed_job_register_result {
- PJRR_SUCESS = 0,
+ PJRR_SUCCESS = 0,
PJRR_FULL = 1,
PJRR_INTERRUPTED = 2
};
+/* Async-signal-safe */
static enum postponed_job_register_result
-postponed_job_register(rb_thread_t *th, rb_vm_t *vm,
- unsigned int flags, rb_postponed_job_func_t func, void *data, int max, int expected_index)
+postponed_job_register(rb_execution_context_t *ec, rb_vm_t *vm,
+ unsigned int flags, rb_postponed_job_func_t func, void *data, int max, int expected_index)
{
rb_postponed_job_t *pjob;
if (expected_index >= max) return PJRR_FULL; /* failed */
if (ATOMIC_CAS(vm->postponed_job_index, expected_index, expected_index+1) == expected_index) {
- pjob = &vm->postponed_job_buffer[expected_index];
+ pjob = &vm->postponed_job_buffer[expected_index];
}
else {
- return PJRR_INTERRUPTED;
+ return PJRR_INTERRUPTED;
}
- pjob->flags = flags;
- pjob->th = th;
+ /* unused: pjob->flags = flags; */
pjob->func = func;
pjob->data = data;
- RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(th);
+ RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(ec);
- return PJRR_SUCESS;
+ return PJRR_SUCCESS;
}
-
-/* return 0 if job buffer is full */
+/*
+ * return 0 if job buffer is full
+ * Async-signal-safe
+ */
int
rb_postponed_job_register(unsigned int flags, rb_postponed_job_func_t func, void *data)
{
- rb_thread_t *th = GET_THREAD();
- rb_vm_t *vm = th->vm;
+ rb_execution_context_t *ec = GET_EC();
+ rb_vm_t *vm = rb_ec_vm_ptr(ec);
begin:
- switch (postponed_job_register(th, vm, flags, func, data, MAX_POSTPONED_JOB, vm->postponed_job_index)) {
- case PJRR_SUCESS : return 1;
+ switch (postponed_job_register(ec, vm, flags, func, data, MAX_POSTPONED_JOB, vm->postponed_job_index)) {
+ case PJRR_SUCCESS : return 1;
case PJRR_FULL : return 0;
case PJRR_INTERRUPTED: goto begin;
default: rb_bug("unreachable\n");
}
}
-/* return 0 if job buffer is full */
+/*
+ * return 0 if job buffer is full
+ * Async-signal-safe
+ */
int
rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func, void *data)
{
- rb_thread_t *th = GET_THREAD();
- rb_vm_t *vm = th->vm;
+ rb_execution_context_t *ec = GET_EC();
+ rb_vm_t *vm = rb_ec_vm_ptr(ec);
rb_postponed_job_t *pjob;
int i, index;
begin:
index = vm->postponed_job_index;
for (i=0; i<index; i++) {
- pjob = &vm->postponed_job_buffer[i];
- if (pjob->func == func) {
- RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(th);
- return 2;
- }
+ pjob = &vm->postponed_job_buffer[i];
+ if (pjob->func == func) {
+ RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(ec);
+ return 2;
+ }
}
- switch (postponed_job_register(th, vm, flags, func, data, MAX_POSTPONED_JOB + MAX_POSTPONED_JOB_SPECIAL_ADDITION, index)) {
- case PJRR_SUCESS : return 1;
+ switch (postponed_job_register(ec, vm, flags, func, data, MAX_POSTPONED_JOB + MAX_POSTPONED_JOB_SPECIAL_ADDITION, index)) {
+ case PJRR_SUCCESS : return 1;
case PJRR_FULL : return 0;
case PJRR_INTERRUPTED: goto begin;
default: rb_bug("unreachable\n");
}
}
+/*
+ * thread-safe and called from non-Ruby thread
+ * returns FALSE on failure (ENOMEM), TRUE otherwise
+ */
+int
+rb_workqueue_register(unsigned flags, rb_postponed_job_func_t func, void *data)
+{
+ struct rb_workqueue_job *wq_job = malloc(sizeof(*wq_job));
+ rb_vm_t *vm = GET_VM();
+
+ if (!wq_job) return FALSE;
+ wq_job->job.func = func;
+ wq_job->job.data = data;
+
+ rb_nativethread_lock_lock(&vm->workqueue_lock);
+ list_add_tail(&vm->workqueue, &wq_job->jnode);
+ rb_nativethread_lock_unlock(&vm->workqueue_lock);
+
+ RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(GET_EC());
+
+ return TRUE;
+}
+
void
rb_postponed_job_flush(rb_vm_t *vm)
{
- rb_thread_t *th = GET_THREAD();
- const unsigned long block_mask = POSTPONED_JOB_INTERRUPT_MASK|TRAP_INTERRUPT_MASK;
- unsigned long saved_mask = th->interrupt_mask & block_mask;
- VALUE saved_errno = th->errinfo;
+ rb_execution_context_t *ec = GET_EC();
+ const rb_atomic_t block_mask = POSTPONED_JOB_INTERRUPT_MASK|TRAP_INTERRUPT_MASK;
+ volatile rb_atomic_t saved_mask = ec->interrupt_mask & block_mask;
+ VALUE volatile saved_errno = ec->errinfo;
+ struct list_head tmp;
+
+ list_head_init(&tmp);
- th->errinfo = Qnil;
+ rb_nativethread_lock_lock(&vm->workqueue_lock);
+ list_append_list(&tmp, &vm->workqueue);
+ rb_nativethread_lock_unlock(&vm->workqueue_lock);
+
+ ec->errinfo = Qnil;
/* mask POSTPONED_JOB dispatch */
- th->interrupt_mask |= block_mask;
+ ec->interrupt_mask |= block_mask;
{
- TH_PUSH_TAG(th);
- EXEC_TAG();
- {
- int index;
- while ((index = vm->postponed_job_index) > 0) {
- if (ATOMIC_CAS(vm->postponed_job_index, index, index-1) == index) {
- rb_postponed_job_t *pjob = &vm->postponed_job_buffer[index-1];
- (*pjob->func)(pjob->data);
- }
+ EC_PUSH_TAG(ec);
+ if (EC_EXEC_TAG() == TAG_NONE) {
+ int index;
+ struct rb_workqueue_job *wq_job;
+
+ while ((index = vm->postponed_job_index) > 0) {
+ if (ATOMIC_CAS(vm->postponed_job_index, index, index-1) == index) {
+ rb_postponed_job_t *pjob = &vm->postponed_job_buffer[index-1];
+ (*pjob->func)(pjob->data);
+ }
}
+ while ((wq_job = list_pop(&tmp, struct rb_workqueue_job, jnode))) {
+ rb_postponed_job_t pjob = wq_job->job;
+
+ free(wq_job);
+ (pjob.func)(pjob.data);
+ }
}
- TH_POP_TAG();
+ EC_POP_TAG();
}
/* restore POSTPONED_JOB mask */
- th->interrupt_mask &= ~(saved_mask ^ block_mask);
- th->errinfo = saved_errno;
+ ec->interrupt_mask &= ~(saved_mask ^ block_mask);
+ ec->errinfo = saved_errno;
+
+ /* don't leak memory if a job threw an exception */
+ if (!list_empty(&tmp)) {
+ rb_nativethread_lock_lock(&vm->workqueue_lock);
+ list_prepend_list(&vm->workqueue, &tmp);
+ rb_nativethread_lock_unlock(&vm->workqueue_lock);
+
+ RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(GET_EC());
+ }
}
diff --git a/vsnprintf.c b/vsnprintf.c
index 9a4f37abe0..04184d7a6a 100644
--- a/vsnprintf.c
+++ b/vsnprintf.c
@@ -175,7 +175,9 @@ typedef struct __sFILE {
short _flags; /* flags, below; this FILE is free if 0 */
short _file; /* fileno, if Unix descriptor, else -1 */
struct __sbuf _bf; /* the buffer (at least 1 byte, if !NULL) */
+#if 0
size_t _lbfsize; /* 0 or -_bf._size, for inline putc */
+#endif
int (*vwrite)(/* struct __sFILE*, struct __suio * */);
const char *(*vextra)(/* struct __sFILE*, size_t, void*, long*, int */);
} FILE;
@@ -1119,11 +1121,11 @@ number: if ((dprec = prec) >= 0)
*/
fieldsz = size;
long_len:
+ realsz = dprec > fieldsz ? dprec : fieldsz;
if (sign)
- fieldsz++;
+ realsz++;
if (flags & HEXPREFIX)
- fieldsz += 2;
- realsz = dprec > fieldsz ? dprec : fieldsz;
+ realsz += 2;
/* right-adjusting blank padding */
if ((flags & (LADJUST|ZEROPAD)) == 0)
@@ -1145,10 +1147,6 @@ long_len:
/* leading zeroes from decimal precision */
PAD_L(dprec - fieldsz, zeroes);
- if (sign)
- fieldsz--;
- if (flags & HEXPREFIX)
- fieldsz -= 2;
/* the string or number proper */
#ifdef FLOATING_POINT
@@ -1252,7 +1250,7 @@ cvt(double value, int ndigits, int flags, char *sign, int *decpt, int ch, int *l
if (value < 0) {
value = -value;
*sign = '-';
- } else if (value == 0.0 && 1.0/value < 0) {
+ } else if (value == 0.0 && signbit(value)) {
*sign = '-';
} else {
*sign = '\000';
diff --git a/wercker.yml b/wercker.yml
new file mode 100644
index 0000000000..af7c4ccb15
--- /dev/null
+++ b/wercker.yml
@@ -0,0 +1,282 @@
+#
+# Wercker is dedicated for testing MJIT. Please use Travis or AppVeyor for non-MJIT testing.
+# This runs all Ruby tests with --jit, or --jit-wait which synchronously JITs all methods.
+#
+box: ruby:2.5-stretch
+no-response-timeout: 30
+command-timeout: 60
+
+### Code to generate test-all definition with --jit-wait ###
+# This aims to relax no-output timeout, and to isolate TracePoint testing since JIT is not supporting trace_* insns for now.
+
+# allow_failures = []
+# dirs = Dir.glob('test/*/').sort
+# index = 1
+# puts <<-EOS
+# - script:
+# name: make test-all#{index} -- others (JIT wait)
+# code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTOPTS="#{dirs.map { |d| "--exclude #{d}" }.join(' ')} --color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+# EOS
+# dirs.each do |dir|
+# index += 1
+# puts <<-EOS
+# - script:
+# name: make test-all#{index} -- #{dir} (JIT wait#{(', allow failure' if allow_failures.include?(dir))})
+# code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="#{dir}" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"#{(' || true' if allow_failures.include?(dir))}
+# EOS
+# end
+
+# --jit + first half of --jit-wait.
+mjit-test1:
+ steps:
+ - install-packages:
+ packages: bison sudo
+ - script:
+ name: workaround ipv6 localhost
+ code: ruby -e "hosts = File.read('/etc/hosts').sub(/^::1\s*localhost.*$/, ''); File.write('/etc/hosts', hosts)"
+ - script:
+ name: create user # some file permission tests don't succeed with root.
+ code: useradd --shell /bin/bash --create-home test && chown -R test:test .
+ - script:
+ name: configure
+ code: /usr/bin/sudo -H -u test -- bash -c 'autoconf && ./configure --disable-install-doc --prefix=/tmp/ruby-prefix'
+ - script:
+ name: make all install
+ code: /usr/bin/sudo -H -u test -- make -j$(nproc) all install
+
+ # --jit
+ - script:
+ name: make test (JIT)
+ code: /usr/bin/sudo -H -u test -- make test RUN_OPTS="--disable-gems --jit --jit-warnings"
+ - script:
+ name: make test-spec (JIT)
+ code: /usr/bin/sudo -H -u test -- make test-spec RUN_OPTS="--disable-gems --jit --jit-warnings"
+ - script:
+ name: make test-all (JIT)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit --jit-warnings" TESTOPTS="-v --color=never --job-status=normal --longest 10 --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit"
+
+ # --jit-wait
+ - script:
+ name: make test (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test RUN_OPTS="--disable-gems --jit-wait --jit-warnings"
+ - script:
+ name: make test-spec (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-spec RUN_OPTS="--disable-gems --jit-wait --jit-warnings"
+
+ # -- AUTO GENERATED 1st half START (by above code) ---
+ - script:
+ name: make test-all1 -- others (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTOPTS="--exclude test/-ext-/ --exclude test/base64/ --exclude test/benchmark/ --exclude test/bigdecimal/ --exclude test/cgi/ --exclude test/coverage/ --exclude test/csv/ --exclude test/date/ --exclude test/dbm/ --exclude test/digest/ --exclude test/drb/ --exclude test/dtrace/ --exclude test/erb/ --exclude test/etc/ --exclude test/excludes/ --exclude test/fiddle/ --exclude test/fileutils/ --exclude test/gdbm/ --exclude test/io/ --exclude test/irb/ --exclude test/json/ --exclude test/lib/ --exclude test/logger/ --exclude test/matrix/ --exclude test/minitest/ --exclude test/misc/ --exclude test/mkmf/ --exclude test/monitor/ --exclude test/net/ --exclude test/nkf/ --exclude test/objspace/ --exclude test/open-uri/ --exclude test/openssl/ --exclude test/optparse/ --exclude test/ostruct/ --exclude test/pathname/ --exclude test/psych/ --exclude test/rdoc/ --exclude test/readline/ --exclude test/resolv/ --exclude test/rexml/ --exclude test/rinda/ --exclude test/ripper/ --exclude test/rss/ --exclude test/ruby/ --exclude test/rubygems/ --exclude test/scanf/ --exclude test/sdbm/ --exclude test/shell/ --exclude test/socket/ --exclude test/stringio/ --exclude test/strscan/ --exclude test/syslog/ --exclude test/testunit/ --exclude test/uri/ --exclude test/webrick/ --exclude test/win32ole/ --exclude test/yaml/ --exclude test/zlib/ --color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all2 -- test/-ext-/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/-ext-/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all3 -- test/base64/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/base64/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all4 -- test/benchmark/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/benchmark/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all5 -- test/bigdecimal/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/bigdecimal/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all6 -- test/cgi/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/cgi/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all7 -- test/coverage/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/coverage/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all8 -- test/csv/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/csv/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all9 -- test/date/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/date/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all10 -- test/dbm/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/dbm/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all11 -- test/digest/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/digest/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all12 -- test/drb/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/drb/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all13 -- test/dtrace/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/dtrace/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all14 -- test/erb/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/erb/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all15 -- test/etc/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/etc/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all16 -- test/excludes/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/excludes/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all17 -- test/fiddle/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/fiddle/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all18 -- test/fileutils/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/fileutils/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all19 -- test/gdbm/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/gdbm/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all20 -- test/io/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/io/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all21 -- test/irb/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/irb/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all22 -- test/json/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/json/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all23 -- test/lib/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/lib/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all24 -- test/logger/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/logger/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all25 -- test/matrix/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/matrix/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all26 -- test/minitest/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/minitest/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all27 -- test/misc/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/misc/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all28 -- test/mkmf/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/mkmf/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all29 -- test/monitor/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/monitor/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all30 -- test/net/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/net/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all31 -- test/nkf/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/nkf/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all32 -- test/objspace/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/objspace/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all33 -- test/open-uri/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/open-uri/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all34 -- test/openssl/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/openssl/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all35 -- test/optparse/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/optparse/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all36 -- test/ostruct/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/ostruct/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all37 -- test/pathname/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/pathname/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all38 -- test/psych/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/psych/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ # -- AUTO GENERATED 1st half END ---
+ after-steps:
+ - wantedly/pretty-slack-notify:
+ webhook_url: $SLACK_WEBHOOK_URL
+ username: Wercker mjit-test1
+ channel: alerts
+ notify_on: "failed"
+ branches: ^trunk$
+
+# second half of --jit-wait.
+mjit-test2:
+ steps:
+ - install-packages:
+ packages: bison sudo
+ - script:
+ name: workaround ipv6 localhost
+ code: ruby -e "hosts = File.read('/etc/hosts').sub(/^::1\s*localhost.*$/, ''); File.write('/etc/hosts', hosts)"
+ - script:
+ name: create user # some file permission tests don't succeed with root.
+ code: useradd --shell /bin/bash --create-home test && chown -R test:test .
+ - script:
+ name: configure
+ code: /usr/bin/sudo -H -u test -- bash -c 'autoconf && ./configure --disable-install-doc --prefix=/tmp/ruby-prefix'
+ - script:
+ name: make all install
+ code: /usr/bin/sudo -H -u test -- make -j$(nproc) all install
+
+ # -- AUTO GENERATED 2nd half START (by above code) ---
+ - script:
+ name: make test-all39 -- test/rdoc/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/rdoc/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all40 -- test/readline/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/readline/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all41 -- test/resolv/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/resolv/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all42 -- test/rexml/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/rexml/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all43 -- test/rinda/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/rinda/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all44 -- test/ripper/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/ripper/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all45 -- test/rss/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/rss/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all46 -- test/ruby/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/ruby/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all47 -- test/rubygems/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/rubygems/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all48 -- test/scanf/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/scanf/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all49 -- test/sdbm/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/sdbm/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all50 -- test/shell/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/shell/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all51 -- test/socket/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/socket/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all52 -- test/stringio/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/stringio/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all53 -- test/strscan/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/strscan/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all54 -- test/syslog/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/syslog/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all55 -- test/testunit/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/testunit/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all56 -- test/uri/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/uri/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all57 -- test/webrick/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/webrick/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all58 -- test/win32ole/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/win32ole/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all59 -- test/yaml/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/yaml/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ - script:
+ name: make test-all60 -- test/zlib/ (JIT wait)
+ code: /usr/bin/sudo -H -u test -- make test-all RUN_OPTS="--disable-gems --jit-wait --jit-warnings" TESTS="test/zlib/" TESTOPTS="--color=never --job-status=normal --subprocess-timeout-scale=3.0 --excludes=test/excludes/_wercker/jit-wait"
+ # -- AUTO GENERATED 2nd half END ---
+ after-steps:
+ - wantedly/pretty-slack-notify:
+ webhook_url: $SLACK_WEBHOOK_URL
+ username: Wercker mjit-test2
+ channel: alerts
+ notify_on: "failed"
+ branches: ^trunk$
diff --git a/win32/.document b/win32/.document
new file mode 100644
index 0000000000..8ef4a5dd56
--- /dev/null
+++ b/win32/.document
@@ -0,0 +1 @@
+README.win32
diff --git a/win32/Makefile.sub b/win32/Makefile.sub
index dc198f63d2..9ee5ddd5b5 100644
--- a/win32/Makefile.sub
+++ b/win32/Makefile.sub
@@ -14,6 +14,12 @@ PWD = $(MAKEDIR)
MFLAGS=-l
!endif
+!ifndef REVISION_FORCE
+!if "$(HAVE_BASERUBY)" == "yes"
+REVISION_FORCE = PHONY
+!endif
+!endif
+
!ifndef CROSS_COMPILING
CROSS_COMPILING = no
!endif
@@ -60,6 +66,9 @@ icondirs=$(ICONDIRS)
icondirs=$(icondirs:\=/)
iconinc=-I$(icondirs: = -I)
!endif
+!if !defined(MATHN)
+MATHN = yes
+!endif
###############
.SUFFIXES: .def .lib
@@ -113,6 +122,9 @@ ARCH = $(MACHINE)
!if !defined(DEBUGFLAGS)
DEBUGFLAGS = -Zi
!endif
+!if "$(RUBY_DEVEL)" == "yes"
+XCFLAGS = $(XCFLAGS) -DRUBY_DEVEL=1
+!endif
!if !defined(OPTFLAGS)
!if $(MSC_VER) < 1400
OPTFLAGS = -O2b2xg-
@@ -141,6 +153,9 @@ sitearch = $(ARCH)-$(RT)
!if !defined(ruby_version)
ruby_version = $(MAJOR).$(MINOR).0
!endif
+!if !defined(RUBY_VERSION_NAME)
+RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
+!endif
!ifndef RUBY_SO_NAME
RUBY_SO_NAME = $(RT)-$(RUBY_BASE_NAME)$(ruby_version:.=)
@@ -206,6 +221,13 @@ OUTFLAG = -Fe
!if !defined(COUTFLAG)
COUTFLAG = -Fo
!endif
+!if !defined(CPPOUTFLAG)
+! if $(MSC_VER) < 1600
+CPPOUTFLAG = >
+! else
+CPPOUTFLAG = -Fi
+! endif
+!endif
!if !defined(CSRCFLAG)
CSRCFLAG = -Tc
!endif
@@ -225,8 +247,14 @@ WARNFLAGS = -W2
!endif
!endif
WERRORFLAG = -WX
+!if !defined(CFLAGS_NO_ARCH)
+CFLAGS_NO_ARCH = $(RUNTIMEFLAG) $(DEBUGFLAGS) $(WARNFLAGS) $(OPTFLAGS) $(COMPILERFLAG)
+!endif
+!if !defined(ARCH_FLAG)
+ARCH_FLAG = $(PROCESSOR_FLAG)
+!endif
!if !defined(CFLAGS)
-CFLAGS = $(RUNTIMEFLAG) $(DEBUGFLAGS) $(WARNFLAGS) $(OPTFLAGS) $(PROCESSOR_FLAG) $(COMPILERFLAG)
+CFLAGS = $(CFLAGS_NO_ARCH) $(ARCH_FLAG)
!endif
!if !defined(CXXFLAGS)
CXXFLAGS = $(CFLAGS)
@@ -256,16 +284,32 @@ LIBS = $(LIBS) imagehlp.lib shlwapi.lib $(EXTLIBS)
!if !defined(MISSING)
MISSING = crypt.obj ffs.obj langinfo.obj lgamma_r.obj strlcat.obj strlcpy.obj win32/win32.obj win32/file.obj setproctitle.obj
!if $(RT_VER) < 120
-MISSING = $(MISSING) acosh.obj cbrt.obj erf.obj tgamma.obj
+MISSING = $(MISSING) acosh.obj cbrt.obj erf.obj nan.obj tgamma.obj
!endif
MISSING = $(MISSING) explicit_bzero.obj
!endif
DLNOBJ = dln.obj
+!if "$(ARCH)" == "x64"
+COROUTINE_OBJ = coroutine/Win64/Context.obj
+!elseif "$(ARCH)" == "i386"
+COROUTINE_OBJ = coroutine/Win32/Context.obj
+!else
+COROUTINE_OBJ =
+!endif
+!if "$(COROUTINE_OBJ)" == ""
+# get rid of empty value not to leave VPATH only in dependencies
+COROUTINE_H = coroutine/Win32/Context.h
+!else
+COROUTINE_H = $(COROUTINE_OBJ:.obj=.h)
+!endif
ARFLAGS = -machine:$(MACHINE) -out:
LD = $(CC)
LDSHARED = $(LD) -LD
XCFLAGS = -DRUBY_EXPORT $(INCFLAGS) $(XCFLAGS)
+!if "$(MATHN)" == "yes"
+XCFLAGS = $(XCFLAGS) -DCANONICALIZATION_FOR_MATHN
+!endif
!if $(MSC_VER) >= 1400
# Prevents VC++ 2005 (cl ver 14) warnings
MANIFESTTOOL = mt -nologo
@@ -274,8 +318,41 @@ LDSHARED_1 = @if exist $(@).manifest $(MANIFESTTOOL) -manifest $(@).manifest -ou
LDSHARED_2 = @if exist $(@).manifest @$(RM) $(@:/=\).manifest
!endif
CPPFLAGS = $(DEFS) $(ARCHDEFS) $(CPPFLAGS)
+!if "$(USE_RUBYGEMS)" == "no"
+CPPFLAGS = -DDISABLE_RUBYGEMS $(CPPFLAGS)
+!endif
+!ifndef MJIT_SUPPORT
+MJIT_SUPPORT = yes
+!endif
+!if "$(CPPOUTFLAG)" == ">"
+MJIT_HEADER_FLAGS =
+!else
+MJIT_HEADER_FLAGS = -P
+!endif
+MJIT_HEADER_SUFFIX =
+MJIT_HEADER_ARCH =
+MJIT_HEADER_INSTALL_DIR = include/$(RUBY_VERSION_NAME)/$(arch)
+MJIT_PRECOMPILED_HEADER_NAME = rb_mjit_header-$(RUBY_PROGRAM_VERSION).pch
+MJIT_PRECOMPILED_HEADER = $(MJIT_HEADER_INSTALL_DIR)/$(MJIT_PRECOMPILED_HEADER_NAME)
+!ifndef MJIT_CC
+MJIT_CC = $(CC)
+!endif
+!ifndef MJIT_OPTFLAGS
+# TODO: Use only $(OPTFLAGS) for performance. It requires to modify flags for precompiled header too.
+# For now, using flags used for building precompiled header to make JIT succeed.
+MJIT_OPTFLAGS = -DMJIT_HEADER $(CFLAGS) $(XCFLAGS) $(CPPFLAGS)
+!endif
+!ifndef MJIT_DEBUGFLAGS
+# TODO: Make this work... Another header for debug build needs to be installed first.
+MJIT_DEBUGFLAGS = $(DEBUGFLAGS)
+!endif
+!ifndef MJIT_LDSHARED
+MJIT_LDSHARED = $(MJIT_CC) -LD
+!endif
+POSTLINK =
DLDFLAGS = $(LDFLAGS) -dll
+MAINLIBS = $(LIBS)
SOLIBS =
RCFILES = $(RUBY_INSTALL_NAME).rc $(RUBYW_INSTALL_NAME).rc $(RUBY_SO_NAME).rc
!ifndef RCFLAGS
@@ -284,6 +361,7 @@ RCFLAGS=-nologo
!endif
!endif
+ENABLE_SHARED = yes
LIBRUBY_LDSHARED = $(LDSHARED)
LIBRUBY_DLDFLAGS = $(EXTLDFLAGS) -implib:dummy.lib -def:$(RUBYDEF)
@@ -335,8 +413,10 @@ ORGLIBPATH = $(LIB)
LIBRUBY_A = $(RUBY_SO_NAME)-static.lib
LIBRUBY_SO = $(RUBY_SO_NAME).dll
+LIBRUBY_SONAME= $(RUBY_SO_NAME).dll
LIBRUBY = $(RUBY_SO_NAME).lib
LIBRUBYARG = $(LIBRUBY)
+LIBRUBYARG_SHARED = $(LIBRUBY)
LIBRUBY_RELATIVE = yes
THREAD_MODEL = win32
@@ -364,6 +444,7 @@ OS_DEST_FILE = $(@:/=\)
!if !defined(WINMAINOBJ)
WINMAINOBJ = winmain.$(OBJEXT)
!endif
+MAINSRC = $(MAINOBJ:.obj=.c)
ARCHMINIOBJS = dmydln.$(OBJEXT) miniruby.res
LIBOBJS = $(MISSING) $(LIBOBJS)
@@ -393,22 +474,42 @@ top_srcdir = $(srcdir)
hdrdir = $(srcdir)/include
VPATH = $(arch_hdrdir)/ruby;$(hdrdir)/ruby;$(srcdir);$(srcdir)/missing;$(win_srcdir)
+!ifndef GIT
+GIT = git
+!endif
+!if "$(HAVE_GIT)" == "yes" || "$(HAVE_GIT)" == "no"
+!else if "$(GIT)" == ""
+HAVE_GIT = no
+!else if [for %I in ($(GIT)) do @if not "%~xI" == "" exit 1]
+! if [for %I in ($(GIT)) do @if not "%~$$PATH:I" == "" exit 1]
+HAVE_GIT = yes
+! else
+HAVE_GIT = no
+! endif
+!else
+! if [for %x in (%PATHEXT:;= %) do @for %I in ($(GIT)%x) do @if not "%~$$PATH:I" == "" exit 1]
+HAVE_GIT = yes
+! else
+HAVE_GIT = no
+! endif
+!endif
+
!if exist($(srcdir)/.svn)
VCS = svn
VCSUP = $(VCS) up $(SVNUPOPTIONS)
!else if exist($(srcdir)/.git/svn)
-VCS = git svn
+VCS = $(GIT) svn
VCSUP = $(VCS) rebase $(GITSVNREBASEOPTIONS)
!else if exist($(srcdir)/.git)
-VCS = git
+VCS = $(GIT)
VCSUP = $(VCS) pull $(GITPULLOPTIONS)
!else
VCSUP = rem
!endif
ruby_pc = $(RUBY_BASE_NAME)-$(MAJOR).$(MINOR).pc
-MESSAGE_BEGIN = @for %I in (
-MESSAGE_END = ) do @echo.%~I
+MESSAGE_BEGIN = @(for %I in (
+MESSAGE_END = ) do @echo.%~I)
ECHO_BEGIN = @echo.
ECHO_END =
@@ -435,6 +536,11 @@ PRELUDE_C = $(srcdir)/prelude.c
!else
PRELUDE_C = prelude.c
!endif
+!if !exist(golf_prelude.c) && exist($(srcdir)/golf_prelude.c)
+GOLF_PRELUDE_C = $(srcdir)/golf_prelude.c
+!else
+GOLF_PRELUDE_C = golf_prelude.c
+!endif
RBCONFIG = ./.rbconfig.time
!include $(srcdir)/common.mk
@@ -467,33 +573,8 @@ $(SCRIPTPROGRAMS): $(STUBPROGRAM)
update-src::
@cd "$(srcdir:/=\)" && set LC_TIME=C && $(VCSUP)
-update-mspec:
-!if exist($(srcdir)/spec/mspec)
- @echo updating mspec ...
- @cd $(srcdir:/=\)\spec\mspec && git pull
-!else
- @echo retrieving mspec ...
- @cd $(srcdir:/=\) && git clone $(MSPEC_GIT_URL) spec/mspec
-!endif
- @cd $(srcdir:/=\)\spec\mspec && git --no-pager log -1 --oneline
-
-update-rubyspec: update-mspec
-!if exist($(srcdir)/spec/rubyspec)
- @echo updating rubyspec ...
- @cd $(srcdir:/=\)\spec\rubyspec && git pull
-!else
- @echo retrieving rubyspec ...
- @cd $(srcdir:/=\) && git clone $(RUBYSPEC_GIT_URL) spec/rubyspec
-!endif
- @cd $(srcdir:/=\)\spec\rubyspec && git --no-pager log -1 --oneline
-
-test-rubyspec-precheck:
-!if !exist($(srcdir)/spec/rubyspec/.)
- @echo No rubyspec here. make update-rubyspec first.
- @exit 1
-!endif
-
-$(MKFILES): $(srcdir)/common.mk $(srcdir)/version.h \
+.PHONY: reconfig
+reconfig $(MKFILES): $(srcdir)/common.mk $(srcdir)/version.h \
$(win_srcdir)/Makefile.sub $(win_srcdir)/configure.bat \
$(win_srcdir)/setup.mak $(win_srcdir)/enc-setup.mak \
$(srcdir)/enc/Makefile.in
@@ -510,8 +591,15 @@ config.status: $(CONFIG_H)
BANG = !
-!if exist($(RUBY_CONFIG_H))
-!include $(RUBY_CONFIG_H)
+!if !exist(config.status)
+!else if [for /f "skip=1 delims=, tokens=2-3" %I in (config.status) do @ \
+ if "%I" == "@RUBY_SO_NAME@" ( \
+ if not "%J" == "$(RUBY_SO_NAME)" exit 1 \
+ ) else if "%I" == "@target_alias@" ( \
+ if not "%J" == "$(ARCH)-$(PLATFORM)" exit 1 \
+ ) \
+]
+config.status: nul
!endif
guard = INCLUDE_RUBY_CONFIG_H
@@ -534,12 +622,17 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
#define STDC_HEADERS 1
#define HAVE_SYS_TYPES_H 1
#define HAVE_SYS_STAT_H 1
+!if $(MSC_VER) >= 1800
+#define HAVE_STDBOOL_H 1
+!endif
#define HAVE_STDLIB_H 1
#define HAVE_STDDEF_H 1
#define HAVE_STRING_H 1
#define HAVE_MEMORY_H 1
!if $(MSC_VER) >= 1400
#define HAVE_LONG_LONG 1
+!else
+#define ULL_TO_DOUBLE(n) ((double)(unsigned long)((n)>>32) * (1I64 << 32) + (unsigned long)(n))
!endif
#define HAVE_OFF_T 1
#define SIZEOF_INT 4
@@ -565,6 +658,7 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
#define SIZEOF_FLOAT 4
#define SIZEOF_DOUBLE 8
#define SIGNEDNESS_OF_TIME_T -1
+#define NEGATIVE_TIME_T 1
!if $(RT_VER) >= 80
#define SIZEOF_TIME_T 8
#define TIMET2NUM(v) LL2NUM(v)
@@ -595,6 +689,11 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
#define HAVE_PROTOTYPES 1
#define TOKEN_PASTE(x,y) x##y
#define HAVE_STDARG_PROTOTYPES 1
+!if $(MSC_VER) >= 1800
+#define HAVE_VA_COPY 1
+!else
+#define HAVE_VA_COPY_VIA_STRUCT_ASSIGNMENT 1
+!endif
!if $(MSC_VER) > 1100
#define NORETURN(x) __declspec(noreturn) x
!endif
@@ -623,6 +722,8 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
#define PACKED_STRUCT_UNALIGNED(x) x
!endif
#define RUBY_EXTERN extern __declspec(dllimport)
+#define RUBY_ALIGNAS(n) __declspec(align(n))
+#define RUBY_ALIGNOF __alignof
#define HAVE_DECL_SYS_NERR 1
#define HAVE_LIMITS_H 1
#define HAVE_FCNTL_H 1
@@ -683,8 +784,10 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
#define HAVE_SSIZE_T 1
!if "$(ARCH)" == "x64" || "$(ARCH)" == "ia64"
#define ssize_t __int64
+#define PRI_PTR_PREFIX "I64"
!else
#define ssize_t int
+#define PRI_PTR_PREFIX PRI_INT_PREFIX
!endif
#define PRI_LL_PREFIX "I64"
#define PRI_PIDT_PREFIX PRI_INT_PREFIX
@@ -711,6 +814,7 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
#define HAVE_MKDIR 1
#define HAVE_CLOCK_GETTIME 1
#define HAVE_CLOCK_GETRES 1
+#define HAVE_GETTIMEOFDAY 1
#define HAVE_SPAWNV 1
#define HAVE_STRCASECMP 1
#define HAVE_STRNCASECMP 1
@@ -718,12 +822,12 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
#define HAVE_STRFTIME 1
#define HAVE_STRCHR 1
#define HAVE_STRSTR 1
-#define HAVE_STRTOD 1
-#define HAVE_STRTOL 1
-#define HAVE_STRTOUL 1
#define HAVE_FLOCK 1
#define HAVE_ISNAN 1
#define HAVE_FINITE 1
+!if $(RT_VER) >= 120
+#define HAVE_NAN 1
+!endif
#define HAVE_HYPOT 1
#define HAVE_FMOD 1
#define HAVE_FREXP 1
@@ -766,26 +870,27 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
!if "$(MACHINE)" == "x86" || "$(ARCH)" == "x64" || "$(ARCH)" == "ia64"
#define STACK_GROW_DIRECTION -1
!endif
-#define CANONICALIZATION_FOR_MATHN 1
-#define DEFAULT_KCODE KCODE_NONE
-!if "$(ENABLE_DEBUG_ENV)" == "yes"
-#define RUBY_DEBUG_ENV 1
+!if "$(COROUTINE_OBJ)" != ""
+#define FIBER_USE_COROUTINE "$(COROUTINE_H)"
!endif
+#define DEFAULT_KCODE KCODE_NONE
#define LOAD_RELATIVE 1
#define DLEXT ".so"
+!if "$(libdir_basename)" != "lib"
+#define LIBDIR_BASENAME "$(libdir_basename)"
+!endif
!if "$(EXTSTATIC)" == "static"
#define EXTSTATIC 1
!endif
#define EXECUTABLE_EXTS $(EXECUTABLE_EXTS)
#define RUBY_COREDLL "$(RT)"
-#define LIBRUBY_SO "$(LIBRUBY_SO)"
#define RUBY_PLATFORM "$(arch)"
#define RUBY_SITEARCH "$(sitearch)"
-#if 0
-$(BANG)if "$(RUBY_SO_NAME)"!="$$(RUBY_SO_NAME)" || "$(ARCH)-$(PLATFORM)"!="$$(ARCH)-$$(PLATFORM)"
-config.h: nul
-$(BANG)endif
-#endif
+!if "$(MJIT_SUPPORT)" == "yes"
+#define USE_MJIT 1
+!else
+#define USE_MJIT 0
+!endif
#endif /* $(guard) */
<<
@@ -820,7 +925,8 @@ s,@CPPFLAGS@,$(CPPFLAGS),;t t
s,@CXXFLAGS@,$(CXXFLAGS),;t t
s,@FFLAGS@,$(FFLAGS),;t t
s,@LDFLAGS@,$(LDFLAGS),;t t
-s,@LIBS@,$(LIBS),;t t
+s,@LIBS@,user32.lib,;t t
+s,@MAINLIBS@,$(MAINLIBS),;t t
s,@exec_prefix@,$${prefix},;t t
s,@prefix@,$(prefix),;t t
s,@program_transform_name@,s,.*,$(PROGRAM_PREFIX)&$(PROGRAM_SUFFIX),,;t t
@@ -828,9 +934,9 @@ s,@bindir@,$${exec_prefix}/bin,;t t
s,@sbindir@,$${exec_prefix}/sbin,;t t
s,@libexecdir@,$${exec_prefix}/libexec,;t t
s,@datadir@,$${prefix}/share,;t t
-s,@sysconfdir@,$${prefix}/etc,;t t
-s,@sharedstatedir@,/etc,;t t
-s,@localstatedir@,/var,;t t
+s,@sysconfdir@,,;t t
+s,@sharedstatedir@,$${prefix}/com,;t t
+s,@localstatedir@,$${prefix}/var,;t t
s,@libdir@,$${exec_prefix}/$(libdir_basename),;t t
s,@includedir@,$${prefix}/include,;t t
s,@oldincludedir@,/usr/include,;t t
@@ -884,6 +990,7 @@ s,@ARCH_FLAG@,$(ARCH_FLAG),;t t
s,@STATIC@,$(STATIC),;t t
s,@CCDLFLAGS@,,;t t
s,@LDSHARED@,$(LDSHARED),;t t
+s,@SOEXT@,dll,;t t
s,@DLEXT@,so,;t t
s,@LIBEXT@,lib,;t t
s,@STRIP@,$(STRIP),;t t
@@ -907,7 +1014,7 @@ s,@LIBRUBY_SO@,$$(RUBY_SO_NAME).dll,;t t
s,@LIBRUBY_ALIASES@,$(LIBRUBY_ALIASES),;t t
s,@LIBRUBY@,$$(RUBY_SO_NAME).lib,;t t
s,@LIBRUBYARG@,$$(LIBRUBYARG_SHARED),;t t
-s,@LIBRUBYARG_STATIC@,$$(LIBRUBY_A),;t t
+s,@LIBRUBYARG_STATIC@,$$(LIBRUBY_A) $$(MAINLIBS),;t t
s,@LIBRUBYARG_SHARED@,$$(LIBRUBY),;t t
s,@SOLIBS@,$(SOLIBS),;t t
s,@DLDLIBS@,$(DLDLIBS),;t t
@@ -917,6 +1024,8 @@ s,@OUTFLAG@,$(OUTFLAG),;t t
s,@COUTFLAG@,$(COUTFLAG),;t t
s,@CSRCFLAG@,$(CSRCFLAG),;t t
s,@CPPOUTFILE@,-P,;t t
+s,@PRELOADENV@,,;t t
+s,@LIBPATHENV@,PATH,;t t
s,@LIBPATHFLAG@, -libpath:%s,;t t
s,@RPATHFLAG@,,;t t
s,@LIBARG@,%s.lib,;t t
@@ -970,6 +1079,7 @@ s,@srcdir@,$(srcdir),;t t
s,@top_srcdir@,$(srcdir),;t t
s,@try_header@,try_compile,;t t
s,@ruby_pc@,$(ruby_pc),;t t
+s,@MJIT_SUPPORT@,$(MJIT_SUPPORT),;t t
<<KEEP
miniruby: miniruby$(EXEEXT)
@@ -989,7 +1099,7 @@ miniruby.rc:
!if "$(PROGRAM)" != ""
$(PROGRAM): $(MAINOBJ) $(LIBRUBY_SO) $(RUBY_INSTALL_NAME).res
$(ECHO) linking $(@:\=/)
- $(Q) $(PURIFY) $(CC) $(MAINOBJ) $(RUBY_INSTALL_NAME).res \
+ $(Q) $(PURIFY) $(CC) $(MAINOBJ) $(EXTOBJS) $(RUBY_INSTALL_NAME).res \
$(OUTFLAG)$@ $(LIBRUBYARG) -link $(LDFLAGS) $(XLDFLAGS)
$(Q) $(LDSHARED_0)
$(Q) $(LDSHARED_1)
@@ -1017,7 +1127,16 @@ $(STUBPROGRAM): rubystub.$(OBJEXT) $(LIBRUBY) $(LIBRUBY_SO) $(RUBY_INSTALL_NAME)
$(Q) $(LDSHARED_2)
!endif
+!if "$(LIBRUBY_SO_UPDATE)" == ""
+PRE_LIBRUBY_UPDATE = $(RM) $(LIBRUBY_EXTS)
+!else
+PRE_LIBRUBY_UPDATE =
+!endif
+
$(LIBRUBY_A): $(OBJS) $(INITOBJS)
+!if "$(EXTSTATIC)" != ""
+ @-$(PRE_LIBRUBY_UPDATE)
+!endif
$(ECHO) linking static-library $(@:\=/)
$(Q) $(AR) $(ARFLAGS)$@ $(OBJS) $(INITOBJS)
@@ -1027,9 +1146,11 @@ $(LIBRUBY): $(RUBYDEF)
$(LIBRUBY_SO): $(LIBRUBY_A) $(DLDOBJS) $(RUBYDEF) $(RUBY_SO_NAME).res
@echo $(DLDOBJS)
+!if "$(EXTSTATIC)" == ""
@-$(PRE_LIBRUBY_UPDATE)
+!endif
$(ECHO) linking shared-library $(@:\=/)
- $(Q) $(LDSHARED) $(MAINOBJ) $(DLDOBJS) $(LIBRUBY_A) \
+ $(Q) $(LDSHARED) $(DLDOBJS) $(LIBRUBY_A) \
$(RUBY_SO_NAME).res $(SOLIBS) $(EXTSOLIBS) $(LIBS) -Fe$@ -link $(LDFLAGS) \
$(LIBRUBY_DLDFLAGS)
@$(RM) dummy.lib dummy.exp
@@ -1038,7 +1159,7 @@ $(LIBRUBY_SO): $(LIBRUBY_A) $(DLDOBJS) $(RUBYDEF) $(RUBY_SO_NAME).res
$(Q) $(LDSHARED_2)
# | findstr -v -c:LNK4049 -c:LNK4217
-$(RUBYDEF): $(LIBRUBY_A) $(PREP)
+$(RUBYDEF): $(LIBRUBY_A) $(RBCONFIG)
$(ECHO) generating $(@:\=/)
$(Q) $(MINIRUBY) $(srcdir)/win32/mkexports.rb \
-output=$@ -arch=$(ARCH) $(LIBRUBY_A)
@@ -1057,28 +1178,31 @@ distclean-local::
-$(Q)$(RM) $(INSTALLED_LIST:/=\) $(arch_hdrdir:/=\)\ruby\config.h verconf.h
-$(Q)$(RMDIRS) $(arch_hdrdir:/=\)\ruby
-clean-ext distclean-ext realclean-ext::
- @cd ext && for /R $(EXTS) %I in (.) \
- do @if exist %I\Makefile ( \
+ext/clean.mk ext/distclean.mk ext/realclean.mk::
+ $(Q)if exist $(EXTS_MK) $(MAKE) -k -f $(EXTS_MK) top_srcdir=$(srcdir) $(*F)
+
+ext/clean gems/clean ext/distclean gems/distclean ext/realclean gems/realclean::
+ $(Q)cd $(@D) && for /R $(EXTS) %I in (.) \
+ do $(Q)if exist %I\Makefile ( \
cd %I && ( \
call set n=%I && \
- call set n=%n:%CD%\ext\=% && \
+ call set n=%n:%CD%\$(@D)\=% && \
call set n=%n:\.=% && \
- call echo $(@:-ext=)ing %n:\=/% & \
- $(MAKE) $(MFLAGS) $(@:-ext=) & \
+ call echo $(@F)ing %n:\=/% & \
+ $(MAKE) $(MFLAGS) $(@F) & \
cd %CD% & \
$(RMDIRS) %I \
) )
-distclean-ext realclean-ext::
- -$(Q)rmdir ext
-
-clean-extout:
+ext/distclean gems/distclean ext/realclean gems/realclean::
+ $(Q)cd $(@D) && for /R $(EXTS) %I in (exts.mk*) \
+ do $(Q)(del %I & rmdir %~dpI)
+ -$(Q)rmdir $(@D) 2> nul || @
clean-enc distclean-enc realclean-enc:
!if exist($(ENC_MK))
- @echo $(@:-enc=ing) encodings
- @-$(MAKE) $(MAKE_ENC) $(@:-enc=)
+ $(ECHO) $(@:-enc=ing) encodings
+ -$(Q)$(MAKE) $(MAKE_ENC) $(@:-enc=)
!endif
$(RBCONFIG): $(PREP)
@@ -1090,11 +1214,29 @@ $(RCFILES): $(RBCONFIG) $(srcdir)/revision.h $(srcdir)/win32/resource.rb
-so_name=$(RUBY_SO_NAME) \
. $(icondirs) $(win_srcdir)
+test-bundled-gems-run:
+ $(Q) for /f %G in ($(srcdir)/gems/bundled_gems) do @( \
+ echo testing %G gem & \
+ $(XRUBY) -C $(srcdir)/gems/src/%G -Ilib ../../../.bundle/bin/rake || \
+ exit /b %STATUS% \
+ )
+
+update-benchmark-driver:
+ $(GIT) clone https://github.com/benchmark-driver/benchmark-driver $(srcdir)/benchmark/benchmark-driver || \
+ $(GIT) -C $(srcdir)/benchmark/benchmark-driver pull origin master
+
$(ruby_pc): $(RBCONFIG)
@$(BOOTSTRAPRUBY) $(srcdir)/tool/expand-config.rb \
-output=$@ -mode=$(INSTALL_DATA_MODE) -config=rbconfig.rb \
$(srcdir)/template/ruby.pc.in
+{$(srcdir)/coroutine/Win32}.asm{coroutine/Win32}.obj:
+ $(ECHO) assembling $(<:\=/)
+ $(Q) $(AS) $(ASFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(<:\=/)
+{$(srcdir)/coroutine/Win64}.asm{coroutine/Win64}.obj:
+ $(ECHO) assembling $(<:\=/)
+ $(Q) $(AS) $(ASFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(<:\=/)
+
{$(srcdir)/enc/trans}.c.obj:
$(ECHO) compiling $(<:\=/)
$(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$(<:\=/)
@@ -1147,13 +1289,14 @@ $(ruby_pc): $(RBCONFIG)
lex.c: {$(srcdir)}lex.c.blt
copy $(?:/=\) $@
-enc/jis/props.h: {$(srcdir)}enc/jis/props.h.blt
+$(srcdir)/enc/jis/props.h: {$(srcdir)}enc/jis/props.h.blt
@if not exist $(@D:/=\) md $(@D:/=\)
$(ECHO) copying $@
$(Q) copy $(?:/=\) $(@:/=\) > nul
$(OBJS): {$(hdrdir)/ruby}win32.h
+win32/win32.$(OBJEXT): {$(VPATH)}id.h
dir.$(OBJEXT) win32/win32.$(OBJEXT): {$(srcdir)}win32/dir.h
file.$(OBJEXT) win32/win32.$(OBJEXT): {$(VPATH)}win32/file.h
@@ -1166,15 +1309,55 @@ probes.h: {$(VPATH)}probes.dmyh
#include "$(*F).dmyh"
<<KEEP
+main: mjit-headers
+yes-mjit-headers: $(MJIT_PRECOMPILED_HEADER)
+clean-local::
+ $(Q)$(RM) $(MJIT_PRECOMPILED_HEADER_NAME) $(MJIT_PRECOMPILED_HEADER_NAME:.pch=.)$(OBJEXT)
+ $(Q)$(RM) $(TIMESTAMPDIR)/$(MJIT_PRECOMPILED_HEADER_NAME:.pch=.time) mjit_config.h
+ $(Q)$(RM) $(MJIT_HEADER_INSTALL_DIR)/rb_mjit_header-*.pch
+ $(Q)$(RM) $(MJIT_HEADER_INSTALL_DIR)/rb_mjit_header-*.pdb
+ $(Q)$(RM) $(MJIT_HEADER_INSTALL_DIR)/rb_mjit_header-*.$(OBJEXT)
+ -$(Q) $(RMDIRS) $(MJIT_HEADER_INSTALL_DIR) 2> $(NULL) || exit 0
+ $(Q)$(RM) $(arch_hdrdir)/rb_mjit_header-*.pch
+ $(Q)$(RM) $(arch_hdrdir)/rb_mjit_header-*.pdb
+ $(Q)$(RM) $(arch_hdrdir)/rb_mjit_header-*.$(OBJEXT)
+
+# Non-mswin environment is not using prebuilt precompiled header because upgrading compiler
+# or changing compiler options may break MJIT so build (currently only by --jit-debug though).
+#
+# But mswin is building precompiled header because cl.exe cannot leave macro after preprocess.
+# As a workaround to use macro without installing many source files, it uses precompiled header
+# without sufficient guard for a broken build.
+#
+# TODO: Fix the above issue by including VC version in header name, and create another header
+# for --jit-debug as well.
+$(TIMESTAMPDIR)/$(MJIT_PRECOMPILED_HEADER_NAME:.pch=).time: probes.h vm.$(OBJEXT)
+ $(ECHO) building $(@F:.time=.pch)
+ $(Q) $(CC) -DMJIT_HEADER $(CFLAGS) $(XCFLAGS:-DRUBY_EXPORT =) -URUBY_EXPORT $(CPPFLAGS) $(srcdir)/vm.c -c -Yc \
+ $(COUTFLAG)$(@F:.time=.)$(OBJEXT) -Fd$(@F:.time=.pdb) -Fp$(@F:.time=.pch).new
+ $(Q) $(IFCHANGE) "--timestamp=$@" $(@F:.time=.pch) $(@F:.time=.pch).new
+
+$(MJIT_PRECOMPILED_HEADER_NAME): $(TIMESTAMPDIR)/$(MJIT_PRECOMPILED_HEADER_NAME:.pch=).time
+
+$(MJIT_PRECOMPILED_HEADER): $(MJIT_PRECOMPILED_HEADER_NAME)
+ $(Q) $(MAKEDIRS) $(MJIT_HEADER_INSTALL_DIR)
+ $(Q) $(MAKE_LINK) $(MJIT_PRECOMPILED_HEADER_NAME) $@
+ $(Q) $(MAKE_LINK) $(MJIT_PRECOMPILED_HEADER_NAME:.pch=.)$(OBJEXT) $(MJIT_HEADER_INSTALL_DIR)/$(MJIT_PRECOMPILED_HEADER_NAME:.pch=.)$(OBJEXT)
+ $(Q) $(MAKE_LINK) $(MJIT_PRECOMPILED_HEADER_NAME:.pch=.pdb) $(MJIT_HEADER_INSTALL_DIR)/$(MJIT_PRECOMPILED_HEADER_NAME:.pch=.pdb)
+ $(Q) $(MAKEDIRS) $(arch_hdrdir)
+ $(Q) $(MAKE_LINK) $(MJIT_PRECOMPILED_HEADER_NAME) $(arch_hdrdir)/$(MJIT_PRECOMPILED_HEADER_NAME)
+ $(Q) $(MAKE_LINK) $(MJIT_PRECOMPILED_HEADER_NAME:.pch=.)$(OBJEXT) $(arch_hdrdir)/$(MJIT_PRECOMPILED_HEADER_NAME:.pch=.)$(OBJEXT)
+ $(Q) $(MAKE_LINK) $(MJIT_PRECOMPILED_HEADER_NAME:.pch=.pdb) $(arch_hdrdir)/$(MJIT_PRECOMPILED_HEADER_NAME:.pch=.pdb)
+
INSNS = opt_sc.inc optinsn.inc optunifs.inc insns.inc insns_info.inc \
- vmtc.inc vm.inc
+ vmtc.inc vm.inc mjit_compile.inc
!if [exit > insns_rules.mk]
!else if [for %I in ($(INSNS)) do \
@for %J in (\
"%I: $$(srcdir)/insns.def {$$(VPATH)}vm_opts.h \" \
" $$(srcdir)/defs/opt_operand.def $$(srcdir)/defs/opt_insn_unif.def \" \
-" $$(srcdir)/tool/instruction.rb $$(srcdir)/tool/insns2vm.rb" \
+" $$(srcdir)/tool/insns2vm.rb" \
" @$$(RM) $$(PROGRAM)" \
" $$(BASERUBY) -Ku $$(srcdir)/tool/insns2vm.rb $$(INSNS2VMOPT) %I" \
"" \
@@ -1191,3 +1374,46 @@ loadpath: verconf.h
@$(CPP) $(XCFLAGS) $(CPPFLAGS) $(srcdir)/loadpath.c | \
sed -e '1,/^const char ruby_initial_load_paths/d;/;/,$$d' \
-e '/^^ /!d;s/ *"\\\\0"$$//;s/" *"//g'
+
+mjit_config.h: $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
+ @echo making <<$@
+#ifndef RUBY_MJIT_CONFIG_H
+#define RUBY_MJIT_CONFIG_H 1
+
+#define MJIT_CONFIG_ESCAPED_EQ "="
+#define MJIT_HEADER_INSTALL_DIR "/$(MJIT_HEADER_INSTALL_DIR)"
+#define MJIT_MIN_HEADER_NAME "$(MJIT_MIN_HEADER_NAME)"
+#define MJIT_PRECOMPILED_HEADER_NAME "$(MJIT_PRECOMPILED_HEADER_NAME)"
+<<KEEP
+ @
+ @(set sep=#define MJIT_CC_COMMON ) & \
+ for %I in ($(MJIT_CC)) do @(call echo.%%sep%%"%%~I", \& set sep= ) >> $@
+ @echo /* MJIT_CC_COMMON */>> $@
+ @
+ @(set sep=#define MJIT_CFLAGS ) & \
+ for %I in ($(RUNTIMEFLAG) $(ARCH_FLAG)) do @(call echo.%%sep%%"%%~I", \& set sep= ) >> $@
+ @echo /* MJIT_CFLAGS */>> $@
+ @
+ @(set sep=#define MJIT_OPTFLAGS ) & \
+ for %I in ($(MJIT_OPTFLAGS:^==" MJIT_CONFIG_ESCAPED_EQ ")) do @(call echo.%%sep%%"%%~I", \& set sep= ) >> $@
+ @echo /* MJIT_OPTFLAGS */>> $@
+ @
+ @(set sep=#define MJIT_DEBUGFLAGS ) & \
+ for %I in ($(MJIT_DEBUGFLAGS:^==" MJIT_CONFIG_ESCAPED_EQ ")) do @(call echo.%%sep%%"%%~I", \& set sep= ) >> $@
+ @echo /* MJIT_DEBUGFLAGS */>> $@
+ @
+ @(set sep=#define MJIT_LDSHARED ) & \
+ for %I in ($(MJIT_LDSHARED)) do @(call echo.%%sep%%"%%~I", \& set sep= ) >> $@
+ @echo /* MJIT_LDSHARED */>> $@
+ @
+ @(set sep=#define MJIT_DLDFLAGS ) & \
+ for %I in ($(DLDFLAGS)) do @(call echo.%%sep%%"%%~I", \& set sep= ) >> $@
+ @echo /* MJIT_DLDFLAGS */>> $@
+ @
+ @(set sep=#define MJIT_LIBS ) & \
+ for %I in ($(LIBRUBYARG_SHARED)) do @(call echo.%%sep%%"%%~I", \& set sep= ) >> $@
+ @echo /* MJIT_LIBS */>> $@
+ @
+ @echo.>> $@
+ @echo #endif /* RUBY_MJIT_CONFIG_H */>> $@
+ @$(Q:@=: :) type $@
diff --git a/win32/README.win32 b/win32/README.win32
index 314feb39cc..7c754c5718 100644
--- a/win32/README.win32
+++ b/win32/README.win32
@@ -22,6 +22,7 @@
(4) If you want to build from SVN source, following commands are required.
* bison
+ * patch
* sed
* ruby 1.8 or later
@@ -44,11 +45,13 @@
And add ((|RUBYW_INSTALL_NAME|)) to change the name of the
executable without console window if also you want.
-(3) Run `((%nmake%))'
+(3) Run `((%nmake up%))' if you are building from SVN source.
-(4) Run `((%nmake test%))'
+(4) Run `((%nmake%))'
-(5) Run `((%nmake install%))'
+(5) Run `((%nmake exam%))'
+
+(6) Run `((%nmake install%))'
== Icons
diff --git a/win32/configure.bat b/win32/configure.bat
index dca517e4f4..fa8966e0c9 100755
--- a/win32/configure.bat
+++ b/win32/configure.bat
@@ -11,7 +11,7 @@ echo> ~tmp~.mak ####
echo>> ~tmp~.mak conf = %0
echo>> ~tmp~.mak $(conf): nul
echo>> ~tmp~.mak @del ~setup~.mak
-echo>> ~tmp~.mak @-$(MAKE) -l$(MAKEFLAGS) -f $(@D)/setup.mak \
+echo>> ~tmp~.mak @$(MAKE) -l$(MAKEFLAGS) -f $(@D)/setup.mak \
if exist pathlist.tmp del pathlist.tmp
echo>confargs.tmp #define CONFIGURE_ARGS \
:loop
@@ -36,11 +36,16 @@ if "%1" == "--enable-debug-env" goto :enable-debug-env
if "%1" == "--disable-debug-env" goto :disable-debug-env
if "%1" == "--enable-rubygems" goto :enable-rubygems
if "%1" == "--disable-rubygems" goto :disable-rubygems
+if "%1" == "--enable-mjit-support" goto :enable-mjit-support
+if "%1" == "--disable-mjit-support" goto :disable-mjit-support
if "%1" == "--extout" goto :extout
if "%1" == "--path" goto :path
if "%1" == "--with-baseruby" goto :baseruby
+if "%1" == "--without-baseruby" goto :baseruby
if "%1" == "--with-ntver" goto :ntver
if "%1" == "--with-libdir" goto :libdir
+if "%1" == "--with-git" goto :git
+if "%1" == "--without-git" goto :nogit
if "%1" == "--without-ext" goto :witharg
if "%1" == "--without-extensions" goto :witharg
if "%opt:~0,10%" == "--without-" goto :withoutarg
@@ -140,12 +145,22 @@ goto :loop ;
shift
goto :loop ;
:enable-rubygems
- echo>> ~tmp~.mak "USE_RUBYGEMS=YES" \
+ echo>> ~tmp~.mak "USE_RUBYGEMS=yes" \
echo>>confargs.tmp %1 \
shift
goto :loop ;
:disable-rubygems
- echo>> ~tmp~.mak "USE_RUBYGEMS=NO" \
+ echo>> ~tmp~.mak "USE_RUBYGEMS=no" \
+ echo>>confargs.tmp %1 \
+ shift
+goto :loop ;
+:enable-mjit-support
+ echo>> ~tmp~.mak "MJIT_SUPPORT=yes" \
+ echo>>confargs.tmp %1 \
+ shift
+goto :loop ;
+:disable-mjit-support
+ echo>> ~tmp~.mak "MJIT_SUPPORT=no" \
echo>>confargs.tmp %1 \
shift
goto :loop ;
@@ -173,12 +188,29 @@ goto :loop ;
shift
shift
goto :loop ;
+:nobaseruby
+ echo>> ~tmp~.mak "HAVE_BASERUBY=no" \
+ echo>>confargs.tmp %1=%2 \
+ shift
+goto :loop ;
:libdir
echo>> ~tmp~.mak "libdir_basename=%~2" \
echo>>confargs.tmp %1=%2 \
shift
shift
goto :loop ;
+:git
+ echo>> ~tmp~.mak "GIT=%~2" \
+ echo>>confargs.tmp %1=%2 \
+ shift
+ shift
+goto :loop ;
+:nogit
+ echo>> ~tmp~.mak "GIT=never-use" \
+ echo>> ~tmp~.mak "HAVE_GIT=no" \
+ echo>>confargs.tmp %1 \
+ shift
+goto :loop ;
:witharg
echo>>confargs.tmp %1=%2\
set witharg=1
@@ -202,6 +234,7 @@ goto :loop ;
echo --with-static-linked-ext link external modules statically
echo --with-ext="a,b,..." use extensions a, b, ...
echo --without-ext="a,b,..." ignore extensions a, b, ...
+ echo --with-opt-dir=DIR-LIST add optional headers and libraries directories separated by `;'
echo --disable-install-doc do not install rdoc indexes during install
echo --with-ntver=0xXXXX target NT version (shouldn't use with old SDK)
del *.tmp
@@ -231,5 +264,6 @@ echo>>~setup~.mak @if exist Makefile.old del Makefile.old
echo>>~setup~.mak @if exist Makefile ren Makefile Makefile.old
echo>>~setup~.mak @ren Makefile.new Makefile
nmake -alf ~setup~.mak MAKEFILE=Makefile.new
+exit /b %ERRORLEVEL%
:exit
@endlocal
diff --git a/win32/dir.h b/win32/dir.h
index b1f981f257..29c4c1c6d5 100644
--- a/win32/dir.h
+++ b/win32/dir.h
@@ -33,6 +33,7 @@ long rb_w32_telldir(DIR *);
void rb_w32_seekdir(DIR *, long);
void rb_w32_rewinddir(DIR *);
void rb_w32_closedir(DIR *);
+char *rb_w32_ugetcwd(char *, int);
#define opendir(s) rb_w32_opendir((s))
#define readdir(d) rb_w32_readdir((d), 0)
diff --git a/win32/file.c b/win32/file.c
index 29bf3dfe95..446720c2f7 100644
--- a/win32/file.c
+++ b/win32/file.c
@@ -22,6 +22,15 @@ static struct code_page_table {
#define IS_DIR_SEPARATOR_P(c) (c == L'\\' || c == L'/')
#define IS_DIR_UNC_P(c) (IS_DIR_SEPARATOR_P(c[0]) && IS_DIR_SEPARATOR_P(c[1]))
+static int
+IS_ABSOLUTE_PATH_P(const WCHAR *path, size_t len)
+{
+ if (len < 2) return FALSE;
+ if (ISALPHA(path[0]))
+ return len > 2 && path[1] == L':' && IS_DIR_SEPARATOR_P(path[2]);
+ else
+ return IS_DIR_UNC_P(path);
+}
/* MultiByteToWideChar() doesn't work with code page 51932 */
#define INVALID_CODE_PAGE 51932
@@ -44,71 +53,6 @@ replace_wchar(wchar_t *s, int find, int replace)
}
}
-/*
- Return user's home directory using environment variables combinations.
- Memory allocated by this function should be manually freed afterwards.
-
- Try:
- HOME, HOMEDRIVE + HOMEPATH and USERPROFILE environment variables
- TODO: Special Folders - Profile and Personal
-*/
-static wchar_t *
-home_dir(void)
-{
- wchar_t *buffer = NULL;
- size_t buffer_len = 0, len = 0;
- enum {
- HOME_NONE, ENV_HOME, ENV_DRIVEPATH, ENV_USERPROFILE
- } home_type = HOME_NONE;
-
- /*
- GetEnvironmentVariableW when used with NULL will return the required
- buffer size and its terminating character.
- http://msdn.microsoft.com/en-us/library/windows/desktop/ms683188(v=vs.85).aspx
- */
-
- if ((len = GetEnvironmentVariableW(L"HOME", NULL, 0)) != 0) {
- buffer_len = len;
- home_type = ENV_HOME;
- }
- else if ((len = GetEnvironmentVariableW(L"HOMEDRIVE", NULL, 0)) != 0) {
- buffer_len = len;
- if ((len = GetEnvironmentVariableW(L"HOMEPATH", NULL, 0)) != 0) {
- buffer_len += len;
- home_type = ENV_DRIVEPATH;
- }
- }
- else if ((len = GetEnvironmentVariableW(L"USERPROFILE", NULL, 0)) != 0) {
- buffer_len = len;
- home_type = ENV_USERPROFILE;
- }
-
- if (!home_type) return NULL;
-
- /* allocate buffer */
- buffer = ALLOC_N(wchar_t, buffer_len);
-
- switch (home_type) {
- case ENV_HOME:
- GetEnvironmentVariableW(L"HOME", buffer, buffer_len);
- break;
- case ENV_DRIVEPATH:
- len = GetEnvironmentVariableW(L"HOMEDRIVE", buffer, buffer_len);
- GetEnvironmentVariableW(L"HOMEPATH", buffer + len, buffer_len - len);
- break;
- case ENV_USERPROFILE:
- GetEnvironmentVariableW(L"USERPROFILE", buffer, buffer_len);
- break;
- default:
- break;
- }
-
- /* sanitize backslashes with forwardslashes */
- replace_wchar(buffer, L'\\', L'/');
-
- return buffer;
-}
-
/* Remove trailing invalid ':$DATA' of the path. */
static inline size_t
remove_invalid_alternative_data(wchar_t *wfullpath, size_t size)
@@ -302,12 +246,24 @@ append_wstr(VALUE dst, const WCHAR *ws, ssize_t len, UINT cp, rb_encoding *enc)
}
VALUE
+rb_default_home_dir(VALUE result)
+{
+ WCHAR *dir = rb_w32_home_dir();
+ if (!dir) {
+ rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
+ }
+ append_wstr(result, dir, -1,
+ rb_w32_filecp(), rb_filesystem_encoding());
+ xfree(dir);
+ return result;
+}
+
+VALUE
rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_name, VALUE result)
{
size_t size = 0, whome_len = 0;
size_t buffer_len = 0;
long wpath_len = 0, wdir_len = 0;
- char *fullpath = NULL;
wchar_t *wfullpath = NULL, *wpath = NULL, *wpath_pos = NULL;
wchar_t *wdir = NULL, *wdir_pos = NULL;
wchar_t *whome = NULL, *buffer = NULL, *buffer_pos = NULL;
@@ -360,14 +316,14 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
/* tainted if expanding '~' */
tainted = 1;
- whome = home_dir();
+ whome = rb_w32_home_dir();
if (whome == NULL) {
free(wpath);
rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
}
whome_len = wcslen(whome);
- if (PathIsRelativeW(whome) && !(whome_len >= 2 && IS_DIR_UNC_P(whome))) {
+ if (!IS_ABSOLUTE_PATH_P(whome, whome_len)) {
free(wpath);
xfree(whome);
rb_raise(rb_eArgError, "non-absolute home");
@@ -441,7 +397,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
/* tainted if expanding '~' */
tainted = 1;
- whome = home_dir();
+ whome = rb_w32_home_dir();
if (whome == NULL) {
free(wpath);
free(wdir);
@@ -449,7 +405,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
}
whome_len = wcslen(whome);
- if (PathIsRelativeW(whome) && !(whome_len >= 2 && IS_DIR_UNC_P(whome))) {
+ if (!IS_ABSOLUTE_PATH_P(whome, whome_len)) {
free(wpath);
free(wdir);
xfree(whome);
@@ -575,7 +531,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
buffer_pos[0] = L'\0';
/* tainted if path is relative */
- if (!tainted && PathIsRelativeW(buffer) && !(buffer_len >= 2 && IS_DIR_UNC_P(buffer)))
+ if (!tainted && !IS_ABSOLUTE_PATH_P(buffer, buffer_len))
tainted = 1;
/* FIXME: Make this more robust */
@@ -640,9 +596,6 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
if (wfullpath != wfullpath_buffer)
xfree(wfullpath);
- if (fullpath)
- xfree(fullpath);
-
rb_enc_associate(result, path_encoding);
return result;
}
diff --git a/win32/file.h b/win32/file.h
index dd448269d4..f3ce244f5d 100644
--- a/win32/file.h
+++ b/win32/file.h
@@ -43,5 +43,6 @@ int fchmod(int fd, int mode);
#define HAVE_FCHMOD 0
UINT rb_w32_filecp(void);
+WCHAR *rb_w32_home_dir(void);
#endif /* RUBY_WIN32_FILE_H */
diff --git a/win32/ifchange.bat b/win32/ifchange.bat
index c95203181a..606d6ed66e 100755
--- a/win32/ifchange.bat
+++ b/win32/ifchange.bat
@@ -4,6 +4,7 @@
set timestamp=
set keepsuffix=
set empty=
+set color=auto
:optloop
for %%I in (%1) do set opt=%%~I
if "%opt%" == "--timestamp" (
@@ -26,6 +27,18 @@ if "%opt%" == "--timestamp" (
set empty=yes
shift
goto :optloop
+) else if "%opt%" == "--color" (
+ set color=always
+ shift
+ goto :optloop
+) else if "%opt:~0,8%" == "--color=" (
+ set color=%opt:~8%
+ shift
+ goto :optloop
+) else if "%opt%" == "--debug" (
+ shift
+ echo on
+ goto :optloop
)
if "%opt%" == "" goto :end
diff --git a/win32/mkexports.rb b/win32/mkexports.rb
index d26acbb107..3bb7594b3e 100755
--- a/win32/mkexports.rb
+++ b/win32/mkexports.rb
@@ -7,7 +7,7 @@ module RbConfig
end
class Exports
- PrivateNames = /(?:Init_|ruby_static_id_|.*_threadptr_|DllMain\b)/
+ PrivateNames = /(?:Init_|ruby_static_id_|DllMain\b)/
@@subclass = []
def self.inherited(klass)
@@ -109,12 +109,16 @@ class Exports::Mswin < Exports
objs = objs.collect {|s| s.tr('/', '\\')}
filetype = nil
objdump(objs) do |l|
- if (filetype = l[/^File Type: (.+)/, 1])..(/^\f/ =~ l)
+ if filetype
+ if /^\f/ =~ l
+ filetype = nil
+ next
+ end
case filetype
when /OBJECT/, /LIBRARY/
next if /^[[:xdigit:]]+ 0+ UNDEF / =~ l
next unless /External/ =~ l
- next if /(?:_local_stdio_printf_options|v(f|sn?)printf_l)\Z/ =~ l
+ next if /(?:_local_stdio_printf_options|v(f|sn?)printf(_s)?_l)\Z/ =~ l
next unless l.sub!(/.*?\s(\(\)\s+)?External\s+\|\s+/, '')
is_data = !$1
if noprefix or /^[@_]/ =~ l
@@ -130,6 +134,8 @@ class Exports::Mswin < Exports
next
end
yield l.strip, is_data
+ else
+ filetype = l[/^File Type: (.+)/, 1]
end
end
yield "strcasecmp", "msvcrt.stricmp"
diff --git a/win32/rtname.cmd b/win32/rtname.cmd
index 775e81681a..775e81681a 100644..100755
--- a/win32/rtname.cmd
+++ b/win32/rtname.cmd
diff --git a/win32/setup.mak b/win32/setup.mak
index 23bdadb839..eb99c6ed1d 100644
--- a/win32/setup.mak
+++ b/win32/setup.mak
@@ -20,23 +20,21 @@ MAKE = $(MAKE) -f $(MAKEFILE)
MAKEFILE = Makefile
!endif
CPU = PROCESSOR_LEVEL
-CC = cl -nologo
+CC = $(CC) -nologo
CPP = $(CC) -EP
+AS = $(AS) -nologo
all: -prologue- -generic- -epilogue-
-i386-mswin32: -prologue32- -i386- -epilogue-
-i486-mswin32: -prologue32- -i486- -epilogue-
-i586-mswin32: -prologue32- -i586- -epilogue-
-i686-mswin32: -prologue32- -i686- -epilogue-
-alpha-mswin32: -prologue32- -alpha- -epilogue-
-x64-mswin64: -prologue64- -x64- -epilogue-
-ia64-mswin64: -prologue64- -ia64- -epilogue-
+i386-mswin32: -prologue- -i386- -epilogue-
+i486-mswin32: -prologue- -i486- -epilogue-
+i586-mswin32: -prologue- -i586- -epilogue-
+i686-mswin32: -prologue- -i686- -epilogue-
+alpha-mswin32: -prologue- -alpha- -epilogue-
+x64-mswin64: -prologue- -x64- -epilogue-
+ia64-mswin64: -prologue- -ia64- -epilogue-
--prologue-: -basic-vars- -system-vars- -version- -program-name-
-
--prologue32-: -basic-vars- -system-vars32- -version- -program-name-
-
--prologue64-: -basic-vars- -system-vars64- -version- -program-name-
+-prologue-: -basic-vars-
+-generic-: -osname-
-basic-vars-: nul
@type << > $(MAKEFILE)
@@ -51,47 +49,57 @@ EXTSTATIC = $(EXTSTATIC)
!if defined(RDOCTARGET)
RDOCTARGET = $(RDOCTARGET)
!endif
-!if defined(EXTOUT)
+!if defined(EXTOUT) && "$(EXTOUT)" != ".ext"
EXTOUT = $(EXTOUT)
!endif
-!if defined(BASERUBY)
-BASERUBY = $(BASERUBY:/=\)
-!endif
!if defined(NTVER)
NTVER = $(NTVER)
!endif
!if defined(USE_RUBYGEMS)
USE_RUBYGEMS = $(USE_RUBYGEMS)
!endif
+!if defined(ENABLE_DEBUG_ENV)
+ENABLE_DEBUG_ENV = $(ENABLE_DEBUG_ENV)
+!endif
+!if defined(MJIT_SUPPORT)
+MJIT_SUPPORT = $(MJIT_SUPPORT)
+!endif
+# TOOLS
<<
-!if !defined(BASERUBY)
- @for %I in (ruby.exe) do @echo BASERUBY = %~s$$PATH:I>> $(MAKEFILE)
- @echo !if "$$(BASERUBY)" == "">> $(MAKEFILE)
- @echo BASERUBY = echo executable host ruby is required. use --with-baseruby option.^& exit 1 >> $(MAKEFILE)
- @echo HAVE_BASERUBY = no>> $(MAKEFILE)
- @echo !else>> $(MAKEFILE)
- @echo HAVE_BASERUBY = yes>> $(MAKEFILE)
- @echo !endif>> $(MAKEFILE)
-!elseif [$(BASERUBY) -eexit 2> nul] == 0
- @echo HAVE_BASERUBY = yes>> $(MAKEFILE)
+!if defined(BASERUBY)
+ @echo BASERUBY = $(BASERUBY:/=\)>> $(MAKEFILE)
!else
- @echo HAVE_BASERUBY = no>> $(MAKEFILE)
+ @for %I in (ruby.exe) do @echo BASERUBY = %~s$$PATH:I>> $(MAKEFILE)
+!endif
+ @type << >> $(MAKEFILE)
+$(BANG)if "$$(BASERUBY)" == ""
+BASERUBY = echo executable host ruby is required. use --with-baseruby option.^& exit 1
+HAVE_BASERUBY = no
+$(BANG)elseif [($$(BASERUBY) -eexit) > nul 2> nul] == 0
+HAVE_BASERUBY = yes
+$(BANG)else
+HAVE_BASERUBY = no
+$(BANG)endif
+<<
+!if "$(GIT)" != ""
+ @echo GIT = $(GIT)>> $(MAKEFILE)
+!endif
+!if "$(HAVE_GIT)" != ""
+ @echo HAVE_GIT = $(HAVE_GIT)>> $(MAKEFILE)
!endif
--system-vars-: -osname- -runtime- -headers-
-
--system-vars32-: -osname32- -runtime- -headers-
-
--system-vars64-: -osname64- -runtime- -headers-
+-osname-section-:
+ @$(APPEND)
+ @echo # TARGET>>$(MAKEFILE)
--osname32-: nul
+-osname32-: -osname-section-
@echo TARGET_OS = mswin32>>$(MAKEFILE)
--osname64-: nul
+-osname64-: -osname-section-
@echo TARGET_OS = mswin64>>$(MAKEFILE)
--osname-: nul
+-osname-: -osname-section-
@echo !ifndef TARGET_OS>>$(MAKEFILE)
@($(CC) -c <<conftest.c > nul && (echo TARGET_OS = mswin32) || (echo TARGET_OS = mswin64)) >>$(MAKEFILE)
#ifdef _WIN64
@@ -101,6 +109,12 @@ USE_RUBYGEMS = $(USE_RUBYGEMS)
@echo !endif>>$(MAKEFILE)
@$(WIN32DIR:/=\)\rm.bat conftest.*
+-compiler-: -compiler-section- -version- -runtime- -headers-
+
+-compiler-section-:
+ @$(APPEND)
+ @echo # COMPILER>>$(MAKEFILE)
+
-runtime-: nul
@$(CC) -MD <<conftest.c user32.lib -link > nul
#include <stdio.h>
@@ -119,7 +133,6 @@ int main(void) {return (EnumProcesses(NULL,0,NULL) ? 0 : 1);}
<<
-version-: nul verconf.mk
- @$(APPEND)
@$(CPP) -I$(srcdir) -I$(srcdir)/include <<"Creating $(MAKEFILE)" | findstr "=" >>$(MAKEFILE)
MSC_VER = _MSC_VER
<<
@@ -145,11 +158,16 @@ echo RUBY_PROGRAM_VERSION = %ruby_version:""=%
echo MAJOR = %major%
echo MINOR = %minor%
echo TEENY = %teeny%
+#if defined RUBY_PATCHLEVEL && RUBY_PATCHLEVEL < 0
+echo RUBY_DEVEL = yes
+#endif
del %0 & exit
<<
-program-name-:
@type << >>$(MAKEFILE)
+
+# PROGRAM-NAME
!ifdef PROGRAM_PREFIX
PROGRAM_PREFIX = $(PROGRAM_PREFIX)
!endif
@@ -177,15 +195,14 @@ MACHINE = x86
!if defined($(CPU))
@echo>>$(MAKEFILE) $(CPU) = $(PROCESSOR_LEVEL)
!endif
- @$(APPEND)
--alpha-: nul
+-alpha-: -osname32-
@echo MACHINE = alpha>>$(MAKEFILE)
--x64-: nul
+-x64-: -osname64-
@echo MACHINE = x64>>$(MAKEFILE)
--ia64-: nul
+-ia64-: -osname64-
@echo MACHINE = ia64>>$(MAKEFILE)
--ix86-: nul
+-ix86-: -osname32-
@echo MACHINE = x86>>$(MAKEFILE)
-i386-: -ix86-
@@ -197,9 +214,11 @@ MACHINE = x86
-i686-: -ix86-
@echo $(CPU) = 6>>$(MAKEFILE)
--epilogue-: -encs-
+-epilogue-: -compiler- -program-name- -encs-
-encs-: nul
+ @$(APPEND)
+ @echo # ENCODING>>$(MAKEFILE)
@$(MAKE) -l -f $(srcdir)/win32/enc-setup.mak srcdir="$(srcdir)" MAKEFILE=$(MAKEFILE)
-epilogue-: nul
@@ -219,7 +238,11 @@ MACHINE = x86
# XLDFLAGS =
# RFLAGS = -r
# EXTLIBS =
-CC = cl -nologo
+CC = $(CC)
+AS = $(AS)
+<<
+ @(for %I in (cl.exe) do @set MJIT_CC=%~$$PATH:I) && (call echo MJIT_CC = "%MJIT_CC:\=/%" -nologo>>$(MAKEFILE))
+ @type << >>$(MAKEFILE)
$(BANG)include $$(srcdir)/win32/Makefile.sub
<<
diff --git a/win32/win32.c b/win32/win32.c
index a0efcbcbd8..d28bd56452 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -23,6 +23,7 @@
#include "ruby/ruby.h"
#include "ruby/encoding.h"
+#include "ruby/io.h"
#include "ruby/util.h"
#include <fcntl.h>
#include <process.h>
@@ -49,8 +50,10 @@
#include <mswsock.h>
#endif
#include "ruby/win32.h"
+#include "ruby/vm.h"
#include "win32/dir.h"
#include "win32/file.h"
+#include "id.h"
#include "internal.h"
#include "encindex.h"
#define isdirsep(x) ((x) == '/' || (x) == '\\')
@@ -60,14 +63,13 @@
#endif
static int w32_wopen(const WCHAR *file, int oflag, int perm);
-static int w32_stati64(const char *path, struct stati64 *st, UINT cp);
-static int w32_lstati64(const char *path, struct stati64 *st, UINT cp);
+static int w32_stati128(const char *path, struct stati128 *st, UINT cp, BOOL lstat);
static char *w32_getenv(const char *name, UINT cp);
#undef getenv
#define DLN_FIND_EXTRA_ARG_DECL ,UINT cp
#define DLN_FIND_EXTRA_ARG ,cp
-#define rb_w32_stati64(path, st) w32_stati64(path, st, cp)
+#define rb_w32_stati128(path, st) w32_stati128(path, st, cp, FALSE)
#define getenv(name) w32_getenv(name, cp)
#undef CharNext
#define CharNext(p) CharNextExA(cp, (p), 0)
@@ -76,7 +78,7 @@ static char *w32_getenv(const char *name, UINT cp);
#include "dln.h"
#include "dln_find.c"
#undef MAXPATHLEN
-#undef rb_w32_stati64
+#undef rb_w32_stati128
#undef dln_find_exe_r
#undef dln_find_file_r
#define dln_find_exe_r(fname, path, buf, size) rb_w32_udln_find_exe_r(fname, path, buf, size, cp)
@@ -117,12 +119,10 @@ static char *w32_getenv(const char *name, UINT cp);
int rb_w32_reparse_symlink_p(const WCHAR *path);
-static struct ChildRecord *CreateChild(const WCHAR *, const WCHAR *, SECURITY_ATTRIBUTES *, HANDLE, HANDLE, HANDLE, DWORD);
static int has_redirection(const char *, UINT);
int rb_w32_wait_events(HANDLE *events, int num, DWORD timeout);
static int rb_w32_open_osfhandle(intptr_t osfhandle, int flags);
-static int wstati64(const WCHAR *path, struct stati64 *st);
-static int wlstati64(const WCHAR *path, struct stati64 *st);
+static int wstati128(const WCHAR *path, struct stati128 *st, BOOL lstat);
VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
int ruby_brace_glob_with_enc(const char *str, int flags, ruby_glob_func *func, VALUE arg, rb_encoding *enc);
static FARPROC get_proc_address(const char *module, const char *func, HANDLE *mh);
@@ -527,6 +527,70 @@ rb_w32_system_tmpdir(WCHAR *path, UINT len)
return (UINT)(p - path + numberof(temp) - 1);
}
+/*
+ Return user's home directory using environment variables combinations.
+ Memory allocated by this function should be manually freed
+ afterwards with xfree.
+
+ Try:
+ HOME, HOMEDRIVE + HOMEPATH and USERPROFILE environment variables
+ Special Folders - Profile and Personal
+*/
+WCHAR *
+rb_w32_home_dir(void)
+{
+ WCHAR *buffer = NULL;
+ size_t buffer_len = MAX_PATH, len = 0;
+ enum {
+ HOME_NONE, ENV_HOME, ENV_DRIVEPATH, ENV_USERPROFILE
+ } home_type = HOME_NONE;
+
+ if ((len = GetEnvironmentVariableW(L"HOME", NULL, 0)) != 0) {
+ buffer_len = len;
+ home_type = ENV_HOME;
+ }
+ else if ((len = GetEnvironmentVariableW(L"HOMEDRIVE", NULL, 0)) != 0) {
+ buffer_len = len;
+ if ((len = GetEnvironmentVariableW(L"HOMEPATH", NULL, 0)) != 0) {
+ buffer_len += len;
+ home_type = ENV_DRIVEPATH;
+ }
+ }
+ else if ((len = GetEnvironmentVariableW(L"USERPROFILE", NULL, 0)) != 0) {
+ buffer_len = len;
+ home_type = ENV_USERPROFILE;
+ }
+
+ /* allocate buffer */
+ buffer = ALLOC_N(WCHAR, buffer_len);
+
+ switch (home_type) {
+ case ENV_HOME:
+ GetEnvironmentVariableW(L"HOME", buffer, buffer_len);
+ break;
+ case ENV_DRIVEPATH:
+ len = GetEnvironmentVariableW(L"HOMEDRIVE", buffer, buffer_len);
+ GetEnvironmentVariableW(L"HOMEPATH", buffer + len, buffer_len - len);
+ break;
+ case ENV_USERPROFILE:
+ GetEnvironmentVariableW(L"USERPROFILE", buffer, buffer_len);
+ break;
+ default:
+ if (!get_special_folder(CSIDL_PROFILE, buffer, buffer_len) &&
+ !get_special_folder(CSIDL_PERSONAL, buffer, buffer_len)) {
+ xfree(buffer);
+ return NULL;
+ }
+ REALLOC_N(buffer, WCHAR, lstrlenW(buffer) + 1);
+ break;
+ }
+
+ /* sanitize backslashes with forwardslashes */
+ regulate_path(buffer);
+
+ return buffer;
+}
+
/* License: Ruby's */
static void
init_env(void)
@@ -625,7 +689,7 @@ rtc_error_handler(int e, const char *src, int line, const char *exe, const char
#endif
static CRITICAL_SECTION select_mutex;
-static int NtSocketsInitialized = 0;
+#define NtSocketsInitialized 1
static st_table *socklist = NULL;
static st_table *conlist = NULL;
#define conlist_disabled ((st_table *)-1)
@@ -666,21 +730,38 @@ exit_handler(void)
{
if (NtSocketsInitialized) {
WSACleanup();
- if (socklist) {
- st_free_table(socklist);
- socklist = NULL;
- }
DeleteCriticalSection(&select_mutex);
- NtSocketsInitialized = 0;
+ }
+ if (uenvarea) {
+ free(uenvarea);
+ uenvarea = NULL;
+ }
+}
+
+/* License: Ruby's */
+static void
+vm_exit_handler(ruby_vm_t *vm)
+{
+ if (socklist) {
+ st_free_table(socklist);
+ socklist = NULL;
}
if (conlist && conlist != conlist_disabled) {
st_foreach(conlist, free_conlist, 0);
st_free_table(conlist);
conlist = NULL;
}
- if (uenvarea) {
- free(uenvarea);
- uenvarea = NULL;
+}
+
+/* License: Ruby's */
+static void
+install_vm_exit_handler(void)
+{
+ static bool installed = 0;
+
+ if (!installed) {
+ ruby_vm_at_exit(vm_exit_handler);
+ installed = 1;
}
}
@@ -703,7 +784,7 @@ StartSockets(void)
InitializeCriticalSection(&select_mutex);
- NtSocketsInitialized = 1;
+ atexit(exit_handler);
}
#define MAKE_SOCKDATA(af, fl) ((int)((((int)af)<<4)|((fl)&0xFFFF)))
@@ -714,8 +795,10 @@ StartSockets(void)
static inline int
socklist_insert(SOCKET sock, int flag)
{
- if (!socklist)
+ if (!socklist) {
socklist = st_init_numtable();
+ install_vm_exit_handler();
+ }
return st_insert(socklist, (st_data_t)sock, (st_data_t)flag);
}
@@ -773,9 +856,8 @@ rb_w32_sysinit(int *argc, char ***argv)
_set_invalid_parameter_handler(invalid_parameter);
_RTC_SetErrorFunc(rtc_error_handler);
set_pioinfo_extra();
-#else
- SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
#endif
+ SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
get_version();
@@ -794,8 +876,6 @@ rb_w32_sysinit(int *argc, char ***argv)
init_stdhandle();
- atexit(exit_handler);
-
// Initialize Winsock
StartSockets();
}
@@ -1115,33 +1195,27 @@ child_result(struct ChildRecord *child, int mode)
}
/* License: Ruby's */
-static struct ChildRecord *
-CreateChild(const WCHAR *cmd, const WCHAR *prog, SECURITY_ATTRIBUTES *psa,
- HANDLE hInput, HANDLE hOutput, HANDLE hError, DWORD dwCreationFlags)
+static int
+CreateChild(struct ChildRecord *child, const WCHAR *cmd, const WCHAR *prog, HANDLE hInput, HANDLE hOutput, HANDLE hError, DWORD dwCreationFlags)
{
BOOL fRet;
STARTUPINFOW aStartupInfo;
PROCESS_INFORMATION aProcessInformation;
SECURITY_ATTRIBUTES sa;
- struct ChildRecord *child;
if (!cmd && !prog) {
errno = EFAULT;
- return NULL;
+ return FALSE;
}
- child = FindFreeChildSlot();
if (!child) {
- errno = EAGAIN;
- return NULL;
+ errno = EAGAIN;
+ return FALSE;
}
- if (!psa) {
- sa.nLength = sizeof (SECURITY_ATTRIBUTES);
- sa.lpSecurityDescriptor = NULL;
- sa.bInheritHandle = TRUE;
- psa = &sa;
- }
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
memset(&aStartupInfo, 0, sizeof(aStartupInfo));
memset(&aProcessInformation, 0, sizeof(aProcessInformation));
@@ -1171,19 +1245,19 @@ CreateChild(const WCHAR *cmd, const WCHAR *prog, SECURITY_ATTRIBUTES *psa,
if (lstrlenW(cmd) > 32767) {
child->pid = 0; /* release the slot */
errno = E2BIG;
- return NULL;
+ return FALSE;
}
RUBY_CRITICAL {
- fRet = CreateProcessW(prog, (WCHAR *)cmd, psa, psa,
- psa->bInheritHandle, dwCreationFlags, NULL, NULL,
+ fRet = CreateProcessW(prog, (WCHAR *)cmd, &sa, &sa,
+ sa.bInheritHandle, dwCreationFlags, NULL, NULL,
&aStartupInfo, &aProcessInformation);
errno = map_errno(GetLastError());
}
if (!fRet) {
child->pid = 0; /* release the slot */
- return NULL;
+ return FALSE;
}
CloseHandle(aProcessInformation.hThread);
@@ -1191,7 +1265,7 @@ CreateChild(const WCHAR *cmd, const WCHAR *prog, SECURITY_ATTRIBUTES *psa,
child->hProcess = aProcessInformation.hProcess;
child->pid = (rb_pid_t)aProcessInformation.dwProcessId;
- return child;
+ return TRUE;
}
/* License: Ruby's */
@@ -1217,6 +1291,46 @@ is_batch(const char *cmd)
#define utf8_to_wstr(str, plen) mbstr_to_wstr(CP_UTF8, str, -1, plen)
#define wstr_to_utf8(str, plen) wstr_to_mbstr(CP_UTF8, str, -1, plen)
+/* License: Ruby's */
+MJIT_FUNC_EXPORTED HANDLE
+rb_w32_start_process(const char *abspath, char *const *argv, int out_fd)
+{
+ /* NOTE: This function is used by MJIT worker, so it can be used parallelly with
+ Ruby's main thread. So functions touching things shared with main thread can't
+ be used, like `ALLOCV` that may trigger GC or `FindFreeChildSlot` that finds
+ a slot from shared memory without atomic locks. */
+ struct ChildRecord child;
+ char *cmd;
+ size_t len;
+ WCHAR *wcmd = NULL, *wprog = NULL;
+ HANDLE outHandle = NULL;
+
+ if (out_fd) {
+ outHandle = (HANDLE)rb_w32_get_osfhandle(out_fd);
+ }
+
+ len = join_argv(NULL, argv, FALSE, filecp(), 1);
+ cmd = alloca(sizeof(char) * len);
+ join_argv(cmd, argv, FALSE, filecp(), 1);
+
+ if (!(wcmd = mbstr_to_wstr(filecp(), cmd, -1, NULL))) {
+ errno = E2BIG;
+ return NULL;
+ }
+ if (!(wprog = mbstr_to_wstr(filecp(), abspath, -1, NULL))) {
+ errno = E2BIG;
+ return NULL;
+ }
+
+ if (!CreateChild(&child, wcmd, wprog, NULL, outHandle, outHandle, 0)) {
+ return NULL;
+ }
+
+ free(wcmd);
+ free(wprog);
+ return child.hProcess;
+}
+
/* License: Artistic or GPL */
static rb_pid_t
w32_spawn(int mode, const char *cmd, const char *prog, UINT cp)
@@ -1333,7 +1447,10 @@ w32_spawn(int mode, const char *cmd, const char *prog, UINT cp)
if (v) ALLOCV_END(v);
if (!e) {
- ret = child_result(CreateChild(wcmd, wshell, NULL, NULL, NULL, NULL, 0), mode);
+ struct ChildRecord *child = FindFreeChildSlot();
+ if (CreateChild(child, wcmd, wshell, NULL, NULL, NULL, 0)) {
+ ret = child_result(child, mode);
+ }
}
free(wshell);
free(wcmd);
@@ -1418,7 +1535,10 @@ w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags, UIN
if (!e && prog && !(wprog = mbstr_to_wstr(cp, prog, -1, NULL))) e = E2BIG;
if (!e) {
- ret = child_result(CreateChild(wcmd, wprog, NULL, NULL, NULL, NULL, flags), mode);
+ struct ChildRecord *child = FindFreeChildSlot();
+ if (CreateChild(child, wcmd, wprog, NULL, NULL, NULL, flags)) {
+ ret = child_result(child, mode);
+ }
}
free(wprog);
free(wcmd);
@@ -1502,7 +1622,7 @@ cmdglob(NtCmdLineElement *patt, NtCmdLineElement **tail, UINT cp, rb_encoding *e
if (patt->len >= PATH_MAX)
if (!(buf = malloc(patt->len + 1))) return 0;
- strlcpy(buf, patt->str, patt->len + 1);
+ memcpy(buf, patt->str, patt->len);
buf[patt->len] = '\0';
translate_char(buf, '\\', '/', cp);
status = ruby_brace_glob_with_enc(buf, 0, insert, (VALUE)&tail, enc);
@@ -1574,7 +1694,7 @@ has_redirection(const char *cmd, UINT cp)
static inline WCHAR *
skipspace(WCHAR *ptr)
{
- while (iswspace(*ptr))
+ while (ISSPACE(*ptr))
ptr++;
return ptr;
}
@@ -1596,7 +1716,7 @@ w32_cmdvector(const WCHAR *cmd, char ***vec, UINT cp, rb_encoding *enc)
//
// just return if we don't have a command line
//
- while (iswspace(*cmd))
+ while (ISSPACE(*cmd))
cmd++;
if (!*cmd) {
*vec = NULL;
@@ -1800,7 +1920,8 @@ w32_cmdvector(const WCHAR *cmd, char ***vec, UINT cp, rb_encoding *enc)
cptr = buffer + (elements+1) * sizeof(char *);
while ((curr = cmdhead) != 0) {
- strlcpy(cptr, curr->str, curr->len + 1);
+ memcpy(cptr, curr->str, curr->len);
+ cptr[curr->len] = '\0';
*vptr++ = cptr;
cptr += curr->len + 1;
cmdhead = curr->next;
@@ -1909,7 +2030,7 @@ open_dir_handle(const WCHAR *filename, WIN32_FIND_DATAW *fd)
static DIR *
w32_wopendir(const WCHAR *wpath)
{
- struct stati64 sbuf;
+ struct stati128 sbuf;
WIN32_FIND_DATAW fd;
HANDLE fh;
DIR *p;
@@ -1923,7 +2044,7 @@ w32_wopendir(const WCHAR *wpath)
//
// check to see if we've got a directory
//
- if (wstati64(wpath, &sbuf) < 0) {
+ if (wstati128(wpath, &sbuf, FALSE) < 0) {
return NULL;
}
if (!(sbuf.st_mode & S_IFDIR) &&
@@ -2030,6 +2151,7 @@ rb_w32_wstr_to_mbstr(UINT cp, const WCHAR *wstr, int clen, long *plen)
WCHAR *
rb_w32_mbstr_to_wstr(UINT cp, const char *str, int clen, long *plen)
{
+ /* This is used by MJIT worker. Do not trigger GC or call Ruby method here. */
WCHAR *ptr;
int len = MultiByteToWideChar(cp, 0, str, clen, NULL, 0);
if (!(ptr = malloc(sizeof(WCHAR) * len))) return 0;
@@ -2361,6 +2483,7 @@ EXTERN_C _CRTIMP ioinfo * __pioinfo[];
#endif
static inline ioinfo* _pioinfo(int);
+
#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
#define _osfhnd(i) (_pioinfo(i)->osfhnd)
#define _osfile(i) (_pioinfo(i)->osfile)
@@ -2435,7 +2558,7 @@ set_pioinfo_extra(void)
#else